162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright(c) 2015, 2016 Intel Corporation.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/string.h>
762306a36Sopenharmony_ci#include <linux/string_helpers.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "efivar.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci/* GUID for HFI1 variables in EFI */
1262306a36Sopenharmony_ci#define HFI1_EFIVAR_GUID EFI_GUID(0xc50a953e, 0xa8b2, 0x42a6, \
1362306a36Sopenharmony_ci		0xbf, 0x89, 0xd3, 0x33, 0xa6, 0xe9, 0xe6, 0xd4)
1462306a36Sopenharmony_ci/* largest EFI data size we expect */
1562306a36Sopenharmony_ci#define EFI_DATA_SIZE 4096
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/*
1862306a36Sopenharmony_ci * Read the named EFI variable.  Return the size of the actual data in *size
1962306a36Sopenharmony_ci * and a kmalloc'ed buffer in *return_data.  The caller must free the
2062306a36Sopenharmony_ci * data.  It is guaranteed that *return_data will be NULL and *size = 0
2162306a36Sopenharmony_ci * if this routine fails.
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * Return 0 on success, -errno on failure.
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_cistatic int read_efi_var(const char *name, unsigned long *size,
2662306a36Sopenharmony_ci			void **return_data)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	efi_status_t status;
2962306a36Sopenharmony_ci	efi_char16_t *uni_name;
3062306a36Sopenharmony_ci	efi_guid_t guid;
3162306a36Sopenharmony_ci	unsigned long temp_size;
3262306a36Sopenharmony_ci	void *temp_buffer;
3362306a36Sopenharmony_ci	void *data;
3462306a36Sopenharmony_ci	int i;
3562306a36Sopenharmony_ci	int ret;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	/* set failure return values */
3862306a36Sopenharmony_ci	*size = 0;
3962306a36Sopenharmony_ci	*return_data = NULL;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
4262306a36Sopenharmony_ci		return -EOPNOTSUPP;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	uni_name = kcalloc(strlen(name) + 1, sizeof(efi_char16_t), GFP_KERNEL);
4562306a36Sopenharmony_ci	temp_buffer = kzalloc(EFI_DATA_SIZE, GFP_KERNEL);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	if (!uni_name || !temp_buffer) {
4862306a36Sopenharmony_ci		ret = -ENOMEM;
4962306a36Sopenharmony_ci		goto fail;
5062306a36Sopenharmony_ci	}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	/* input: the size of the buffer */
5362306a36Sopenharmony_ci	temp_size = EFI_DATA_SIZE;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	/* convert ASCII to unicode - it is a 1:1 mapping */
5662306a36Sopenharmony_ci	for (i = 0; name[i]; i++)
5762306a36Sopenharmony_ci		uni_name[i] = name[i];
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	/* need a variable for our GUID */
6062306a36Sopenharmony_ci	guid = HFI1_EFIVAR_GUID;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	/* call into EFI runtime services */
6362306a36Sopenharmony_ci	status = efi.get_variable(
6462306a36Sopenharmony_ci			uni_name,
6562306a36Sopenharmony_ci			&guid,
6662306a36Sopenharmony_ci			NULL,
6762306a36Sopenharmony_ci			&temp_size,
6862306a36Sopenharmony_ci			temp_buffer);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	/*
7162306a36Sopenharmony_ci	 * It would be nice to call efi_status_to_err() here, but that
7262306a36Sopenharmony_ci	 * is in the EFIVAR_FS code and may not be compiled in.
7362306a36Sopenharmony_ci	 * However, even that is insufficient since it does not cover
7462306a36Sopenharmony_ci	 * EFI_BUFFER_TOO_SMALL which could be an important return.
7562306a36Sopenharmony_ci	 * For now, just split out success or not found.
7662306a36Sopenharmony_ci	 */
7762306a36Sopenharmony_ci	ret = status == EFI_SUCCESS   ? 0 :
7862306a36Sopenharmony_ci	      status == EFI_NOT_FOUND ? -ENOENT :
7962306a36Sopenharmony_ci					-EINVAL;
8062306a36Sopenharmony_ci	if (ret)
8162306a36Sopenharmony_ci		goto fail;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	/*
8462306a36Sopenharmony_ci	 * We have successfully read the EFI variable into our
8562306a36Sopenharmony_ci	 * temporary buffer.  Now allocate a correctly sized
8662306a36Sopenharmony_ci	 * buffer.
8762306a36Sopenharmony_ci	 */
8862306a36Sopenharmony_ci	data = kmemdup(temp_buffer, temp_size, GFP_KERNEL);
8962306a36Sopenharmony_ci	if (!data) {
9062306a36Sopenharmony_ci		ret = -ENOMEM;
9162306a36Sopenharmony_ci		goto fail;
9262306a36Sopenharmony_ci	}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	*size = temp_size;
9562306a36Sopenharmony_ci	*return_data = data;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cifail:
9862306a36Sopenharmony_ci	kfree(uni_name);
9962306a36Sopenharmony_ci	kfree(temp_buffer);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	return ret;
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci/*
10562306a36Sopenharmony_ci * Read an HFI1 EFI variable of the form:
10662306a36Sopenharmony_ci *	<PCIe address>-<kind>
10762306a36Sopenharmony_ci * Return an kalloc'ed array and size of the data.
10862306a36Sopenharmony_ci *
10962306a36Sopenharmony_ci * Returns 0 on success, -errno on failure.
11062306a36Sopenharmony_ci */
11162306a36Sopenharmony_ciint read_hfi1_efi_var(struct hfi1_devdata *dd, const char *kind,
11262306a36Sopenharmony_ci		      unsigned long *size, void **return_data)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	char prefix_name[64];
11562306a36Sopenharmony_ci	char name[128];
11662306a36Sopenharmony_ci	int result;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	/* create a common prefix */
11962306a36Sopenharmony_ci	snprintf(prefix_name, sizeof(prefix_name), "%04x:%02x:%02x.%x",
12062306a36Sopenharmony_ci		 pci_domain_nr(dd->pcidev->bus),
12162306a36Sopenharmony_ci		 dd->pcidev->bus->number,
12262306a36Sopenharmony_ci		 PCI_SLOT(dd->pcidev->devfn),
12362306a36Sopenharmony_ci		 PCI_FUNC(dd->pcidev->devfn));
12462306a36Sopenharmony_ci	snprintf(name, sizeof(name), "%s-%s", prefix_name, kind);
12562306a36Sopenharmony_ci	result = read_efi_var(name, size, return_data);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	/*
12862306a36Sopenharmony_ci	 * If reading the lowercase EFI variable fail, read the uppercase
12962306a36Sopenharmony_ci	 * variable.
13062306a36Sopenharmony_ci	 */
13162306a36Sopenharmony_ci	if (result) {
13262306a36Sopenharmony_ci		string_upper(prefix_name, prefix_name);
13362306a36Sopenharmony_ci		snprintf(name, sizeof(name), "%s-%s", prefix_name, kind);
13462306a36Sopenharmony_ci		result = read_efi_var(name, size, return_data);
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	return result;
13862306a36Sopenharmony_ci}
139