1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause |
2 | /* |
3 | * Copyright(c) 2015, 2016 Intel Corporation. |
4 | */ |
5 | |
6 | #include <linux/string.h> |
7 | #include <linux/string_helpers.h> |
8 | |
9 | #include "efivar.h" |
10 | |
11 | /* GUID for HFI1 variables in EFI */ |
12 | #define HFI1_EFIVAR_GUID EFI_GUID(0xc50a953e, 0xa8b2, 0x42a6, \ |
13 | 0xbf, 0x89, 0xd3, 0x33, 0xa6, 0xe9, 0xe6, 0xd4) |
14 | /* largest EFI data size we expect */ |
15 | #define EFI_DATA_SIZE 4096 |
16 | |
17 | /* |
18 | * Read the named EFI variable. Return the size of the actual data in *size |
19 | * and a kmalloc'ed buffer in *return_data. The caller must free the |
20 | * data. It is guaranteed that *return_data will be NULL and *size = 0 |
21 | * if this routine fails. |
22 | * |
23 | * Return 0 on success, -errno on failure. |
24 | */ |
25 | static int read_efi_var(const char *name, unsigned long *size, |
26 | void **return_data) |
27 | { |
28 | efi_status_t status; |
29 | efi_char16_t *uni_name; |
30 | efi_guid_t guid; |
31 | unsigned long temp_size; |
32 | void *temp_buffer; |
33 | void *data; |
34 | int i; |
35 | int ret; |
36 | |
37 | /* set failure return values */ |
38 | *size = 0; |
39 | *return_data = NULL; |
40 | |
41 | if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE)) |
42 | return -EOPNOTSUPP; |
43 | |
44 | uni_name = kcalloc(strlen(name) + 1, size: sizeof(efi_char16_t), GFP_KERNEL); |
45 | temp_buffer = kzalloc(EFI_DATA_SIZE, GFP_KERNEL); |
46 | |
47 | if (!uni_name || !temp_buffer) { |
48 | ret = -ENOMEM; |
49 | goto fail; |
50 | } |
51 | |
52 | /* input: the size of the buffer */ |
53 | temp_size = EFI_DATA_SIZE; |
54 | |
55 | /* convert ASCII to unicode - it is a 1:1 mapping */ |
56 | for (i = 0; name[i]; i++) |
57 | uni_name[i] = name[i]; |
58 | |
59 | /* need a variable for our GUID */ |
60 | guid = HFI1_EFIVAR_GUID; |
61 | |
62 | /* call into EFI runtime services */ |
63 | status = efi.get_variable( |
64 | uni_name, |
65 | &guid, |
66 | NULL, |
67 | &temp_size, |
68 | temp_buffer); |
69 | |
70 | /* |
71 | * It would be nice to call efi_status_to_err() here, but that |
72 | * is in the EFIVAR_FS code and may not be compiled in. |
73 | * However, even that is insufficient since it does not cover |
74 | * EFI_BUFFER_TOO_SMALL which could be an important return. |
75 | * For now, just split out success or not found. |
76 | */ |
77 | ret = status == EFI_SUCCESS ? 0 : |
78 | status == EFI_NOT_FOUND ? -ENOENT : |
79 | -EINVAL; |
80 | if (ret) |
81 | goto fail; |
82 | |
83 | /* |
84 | * We have successfully read the EFI variable into our |
85 | * temporary buffer. Now allocate a correctly sized |
86 | * buffer. |
87 | */ |
88 | data = kmemdup(p: temp_buffer, size: temp_size, GFP_KERNEL); |
89 | if (!data) { |
90 | ret = -ENOMEM; |
91 | goto fail; |
92 | } |
93 | |
94 | *size = temp_size; |
95 | *return_data = data; |
96 | |
97 | fail: |
98 | kfree(objp: uni_name); |
99 | kfree(objp: temp_buffer); |
100 | |
101 | return ret; |
102 | } |
103 | |
104 | /* |
105 | * Read an HFI1 EFI variable of the form: |
106 | * <PCIe address>-<kind> |
107 | * Return an kalloc'ed array and size of the data. |
108 | * |
109 | * Returns 0 on success, -errno on failure. |
110 | */ |
111 | int read_hfi1_efi_var(struct hfi1_devdata *dd, const char *kind, |
112 | unsigned long *size, void **return_data) |
113 | { |
114 | char prefix_name[64]; |
115 | char name[128]; |
116 | int result; |
117 | |
118 | /* create a common prefix */ |
119 | snprintf(buf: prefix_name, size: sizeof(prefix_name), fmt: "%04x:%02x:%02x.%x" , |
120 | pci_domain_nr(bus: dd->pcidev->bus), |
121 | dd->pcidev->bus->number, |
122 | PCI_SLOT(dd->pcidev->devfn), |
123 | PCI_FUNC(dd->pcidev->devfn)); |
124 | snprintf(buf: name, size: sizeof(name), fmt: "%s-%s" , prefix_name, kind); |
125 | result = read_efi_var(name, size, return_data); |
126 | |
127 | /* |
128 | * If reading the lowercase EFI variable fail, read the uppercase |
129 | * variable. |
130 | */ |
131 | if (result) { |
132 | string_upper(dst: prefix_name, src: prefix_name); |
133 | snprintf(buf: name, size: sizeof(name), fmt: "%s-%s" , prefix_name, kind); |
134 | result = read_efi_var(name, size, return_data); |
135 | } |
136 | |
137 | return result; |
138 | } |
139 | |