diff options
| author | H. Peter Anvin <hpa@linux.intel.com> | 2013-04-19 20:09:03 -0400 |
|---|---|---|
| committer | H. Peter Anvin <hpa@linux.intel.com> | 2013-04-19 20:09:03 -0400 |
| commit | c0a9f451e4e7ecd2ad1a6c27ea5c31d0226bdddf (patch) | |
| tree | dcb22c46c33a994db115ec8925093f91f6463f15 | |
| parent | 74c3e3fcf350b2e7e3eaf9550528ee3f74e44b37 (diff) | |
| parent | 8c58bf3eec3b8fc8162fe557e9361891c20758f2 (diff) | |
Merge remote-tracking branch 'efi/urgent' into x86/urgent
Matt Fleming (1):
x86, efivars: firmware bug workarounds should be in platform
code
Matthew Garrett (3):
Move utf16 functions to kernel core and rename
efi: Pass boot services variable info to runtime code
efi: Distinguish between "remaining space" and actually used
space
Richard Weinberger (2):
x86,efi: Check max_size only if it is non-zero.
x86,efi: Implement efi_no_storage_paranoia parameter
Sergey Vlasov (2):
x86/Kconfig: Make EFI select UCS2_STRING
efi: Export efi_query_variable_store() for efivars.ko
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
| -rw-r--r-- | Documentation/kernel-parameters.txt | 6 | ||||
| -rw-r--r-- | arch/x86/Kconfig | 1 | ||||
| -rw-r--r-- | arch/x86/boot/compressed/eboot.c | 47 | ||||
| -rw-r--r-- | arch/x86/include/asm/efi.h | 7 | ||||
| -rw-r--r-- | arch/x86/include/uapi/asm/bootparam.h | 1 | ||||
| -rw-r--r-- | arch/x86/platform/efi/efi.c | 168 | ||||
| -rw-r--r-- | drivers/firmware/Kconfig | 1 | ||||
| -rw-r--r-- | drivers/firmware/efivars.c | 98 | ||||
| -rw-r--r-- | include/linux/efi.h | 9 | ||||
| -rw-r--r-- | include/linux/ucs2_string.h | 14 | ||||
| -rw-r--r-- | lib/Kconfig | 3 | ||||
| -rw-r--r-- | lib/Makefile | 2 | ||||
| -rw-r--r-- | lib/ucs2_string.c | 51 |
13 files changed, 325 insertions, 83 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 4609e81dbc37..d1cc3a9fa14f 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt | |||
| @@ -788,6 +788,12 @@ bytes respectively. Such letter suffixes can also be entirely omitted. | |||
| 788 | edd= [EDD] | 788 | edd= [EDD] |
| 789 | Format: {"off" | "on" | "skip[mbr]"} | 789 | Format: {"off" | "on" | "skip[mbr]"} |
| 790 | 790 | ||
| 791 | efi_no_storage_paranoia [EFI; X86] | ||
| 792 | Using this parameter you can use more than 50% of | ||
| 793 | your efi variable storage. Use this parameter only if | ||
| 794 | you are really sure that your UEFI does sane gc and | ||
| 795 | fulfills the spec otherwise your board may brick. | ||
| 796 | |||
| 791 | eisa_irq_edge= [PARISC,HW] | 797 | eisa_irq_edge= [PARISC,HW] |
| 792 | See header of drivers/parisc/eisa.c. | 798 | See header of drivers/parisc/eisa.c. |
| 793 | 799 | ||
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 70c0f3da0476..15b5cef4aa38 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig | |||
| @@ -1549,6 +1549,7 @@ config X86_SMAP | |||
| 1549 | config EFI | 1549 | config EFI |
| 1550 | bool "EFI runtime service support" | 1550 | bool "EFI runtime service support" |
| 1551 | depends on ACPI | 1551 | depends on ACPI |
| 1552 | select UCS2_STRING | ||
| 1552 | ---help--- | 1553 | ---help--- |
| 1553 | This enables the kernel to use EFI runtime services that are | 1554 | This enables the kernel to use EFI runtime services that are |
| 1554 | available (such as the EFI variable services). | 1555 | available (such as the EFI variable services). |
diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index c205035a6b96..8615f7581820 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c | |||
| @@ -251,6 +251,51 @@ static void find_bits(unsigned long mask, u8 *pos, u8 *size) | |||
| 251 | *size = len; | 251 | *size = len; |
| 252 | } | 252 | } |
| 253 | 253 | ||
| 254 | static efi_status_t setup_efi_vars(struct boot_params *params) | ||
| 255 | { | ||
| 256 | struct setup_data *data; | ||
| 257 | struct efi_var_bootdata *efidata; | ||
| 258 | u64 store_size, remaining_size, var_size; | ||
| 259 | efi_status_t status; | ||
| 260 | |||
| 261 | if (!sys_table->runtime->query_variable_info) | ||
| 262 | return EFI_UNSUPPORTED; | ||
| 263 | |||
| 264 | data = (struct setup_data *)(unsigned long)params->hdr.setup_data; | ||
| 265 | |||
| 266 | while (data && data->next) | ||
| 267 | data = (struct setup_data *)(unsigned long)data->next; | ||
| 268 | |||
| 269 | status = efi_call_phys4(sys_table->runtime->query_variable_info, | ||
| 270 | EFI_VARIABLE_NON_VOLATILE | | ||
| 271 | EFI_VARIABLE_BOOTSERVICE_ACCESS | | ||
| 272 | EFI_VARIABLE_RUNTIME_ACCESS, &store_size, | ||
| 273 | &remaining_size, &var_size); | ||
| 274 | |||
| 275 | if (status != EFI_SUCCESS) | ||
| 276 | return status; | ||
| 277 | |||
| 278 | status = efi_call_phys3(sys_table->boottime->allocate_pool, | ||
| 279 | EFI_LOADER_DATA, sizeof(*efidata), &efidata); | ||
| 280 | |||
| 281 | if (status != EFI_SUCCESS) | ||
| 282 | return status; | ||
| 283 | |||
| 284 | efidata->data.type = SETUP_EFI_VARS; | ||
| 285 | efidata->data.len = sizeof(struct efi_var_bootdata) - | ||
| 286 | sizeof(struct setup_data); | ||
| 287 | efidata->data.next = 0; | ||
| 288 | efidata->store_size = store_size; | ||
| 289 | efidata->remaining_size = remaining_size; | ||
| 290 | efidata->max_var_size = var_size; | ||
| 291 | |||
| 292 | if (data) | ||
| 293 | data->next = (unsigned long)efidata; | ||
| 294 | else | ||
| 295 | params->hdr.setup_data = (unsigned long)efidata; | ||
| 296 | |||
| 297 | } | ||
| 298 | |||
| 254 | static efi_status_t setup_efi_pci(struct boot_params *params) | 299 | static efi_status_t setup_efi_pci(struct boot_params *params) |
| 255 | { | 300 | { |
| 256 | efi_pci_io_protocol *pci; | 301 | efi_pci_io_protocol *pci; |
| @@ -1157,6 +1202,8 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table, | |||
| 1157 | 1202 | ||
| 1158 | setup_graphics(boot_params); | 1203 | setup_graphics(boot_params); |
| 1159 | 1204 | ||
| 1205 | setup_efi_vars(boot_params); | ||
| 1206 | |||
| 1160 | setup_efi_pci(boot_params); | 1207 | setup_efi_pci(boot_params); |
| 1161 | 1208 | ||
| 1162 | status = efi_call_phys3(sys_table->boottime->allocate_pool, | 1209 | status = efi_call_phys3(sys_table->boottime->allocate_pool, |
diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index 60c89f30c727..2fb5d5884e23 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h | |||
| @@ -102,6 +102,13 @@ extern void efi_call_phys_epilog(void); | |||
| 102 | extern void efi_unmap_memmap(void); | 102 | extern void efi_unmap_memmap(void); |
| 103 | extern void efi_memory_uc(u64 addr, unsigned long size); | 103 | extern void efi_memory_uc(u64 addr, unsigned long size); |
| 104 | 104 | ||
| 105 | struct efi_var_bootdata { | ||
| 106 | struct setup_data data; | ||
| 107 | u64 store_size; | ||
| 108 | u64 remaining_size; | ||
| 109 | u64 max_var_size; | ||
| 110 | }; | ||
| 111 | |||
| 105 | #ifdef CONFIG_EFI | 112 | #ifdef CONFIG_EFI |
| 106 | 113 | ||
| 107 | static inline bool efi_is_native(void) | 114 | static inline bool efi_is_native(void) |
diff --git a/arch/x86/include/uapi/asm/bootparam.h b/arch/x86/include/uapi/asm/bootparam.h index c15ddaf90710..08744242b8d2 100644 --- a/arch/x86/include/uapi/asm/bootparam.h +++ b/arch/x86/include/uapi/asm/bootparam.h | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #define SETUP_E820_EXT 1 | 6 | #define SETUP_E820_EXT 1 |
| 7 | #define SETUP_DTB 2 | 7 | #define SETUP_DTB 2 |
| 8 | #define SETUP_PCI 3 | 8 | #define SETUP_PCI 3 |
| 9 | #define SETUP_EFI_VARS 4 | ||
| 9 | 10 | ||
| 10 | /* ram_size flags */ | 11 | /* ram_size flags */ |
| 11 | #define RAMDISK_IMAGE_START_MASK 0x07FF | 12 | #define RAMDISK_IMAGE_START_MASK 0x07FF |
diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 5f2ecaf3f9d8..e4a86a677ce1 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c | |||
| @@ -41,6 +41,7 @@ | |||
| 41 | #include <linux/io.h> | 41 | #include <linux/io.h> |
| 42 | #include <linux/reboot.h> | 42 | #include <linux/reboot.h> |
| 43 | #include <linux/bcd.h> | 43 | #include <linux/bcd.h> |
| 44 | #include <linux/ucs2_string.h> | ||
| 44 | 45 | ||
| 45 | #include <asm/setup.h> | 46 | #include <asm/setup.h> |
| 46 | #include <asm/efi.h> | 47 | #include <asm/efi.h> |
| @@ -51,6 +52,13 @@ | |||
| 51 | 52 | ||
| 52 | #define EFI_DEBUG 1 | 53 | #define EFI_DEBUG 1 |
| 53 | 54 | ||
| 55 | /* | ||
| 56 | * There's some additional metadata associated with each | ||
| 57 | * variable. Intel's reference implementation is 60 bytes - bump that | ||
| 58 | * to account for potential alignment constraints | ||
| 59 | */ | ||
| 60 | #define VAR_METADATA_SIZE 64 | ||
| 61 | |||
| 54 | struct efi __read_mostly efi = { | 62 | struct efi __read_mostly efi = { |
| 55 | .mps = EFI_INVALID_TABLE_ADDR, | 63 | .mps = EFI_INVALID_TABLE_ADDR, |
| 56 | .acpi = EFI_INVALID_TABLE_ADDR, | 64 | .acpi = EFI_INVALID_TABLE_ADDR, |
| @@ -69,6 +77,13 @@ struct efi_memory_map memmap; | |||
| 69 | static struct efi efi_phys __initdata; | 77 | static struct efi efi_phys __initdata; |
| 70 | static efi_system_table_t efi_systab __initdata; | 78 | static efi_system_table_t efi_systab __initdata; |
| 71 | 79 | ||
| 80 | static u64 efi_var_store_size; | ||
| 81 | static u64 efi_var_remaining_size; | ||
| 82 | static u64 efi_var_max_var_size; | ||
| 83 | static u64 boot_used_size; | ||
| 84 | static u64 boot_var_size; | ||
| 85 | static u64 active_size; | ||
| 86 | |||
| 72 | unsigned long x86_efi_facility; | 87 | unsigned long x86_efi_facility; |
| 73 | 88 | ||
| 74 | /* | 89 | /* |
| @@ -98,6 +113,15 @@ static int __init setup_add_efi_memmap(char *arg) | |||
| 98 | } | 113 | } |
| 99 | early_param("add_efi_memmap", setup_add_efi_memmap); | 114 | early_param("add_efi_memmap", setup_add_efi_memmap); |
| 100 | 115 | ||
| 116 | static bool efi_no_storage_paranoia; | ||
| 117 | |||
| 118 | static int __init setup_storage_paranoia(char *arg) | ||
| 119 | { | ||
| 120 | efi_no_storage_paranoia = true; | ||
| 121 | return 0; | ||
| 122 | } | ||
| 123 | early_param("efi_no_storage_paranoia", setup_storage_paranoia); | ||
| 124 | |||
| 101 | 125 | ||
| 102 | static efi_status_t virt_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc) | 126 | static efi_status_t virt_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc) |
| 103 | { | 127 | { |
| @@ -162,8 +186,53 @@ static efi_status_t virt_efi_get_next_variable(unsigned long *name_size, | |||
| 162 | efi_char16_t *name, | 186 | efi_char16_t *name, |
| 163 | efi_guid_t *vendor) | 187 | efi_guid_t *vendor) |
| 164 | { | 188 | { |
| 165 | return efi_call_virt3(get_next_variable, | 189 | efi_status_t status; |
| 166 | name_size, name, vendor); | 190 | static bool finished = false; |
| 191 | static u64 var_size; | ||
| 192 | |||
| 193 | status = efi_call_virt3(get_next_variable, | ||
| 194 | name_size, name, vendor); | ||
| 195 | |||
| 196 | if (status == EFI_NOT_FOUND) { | ||
| 197 | finished = true; | ||
| 198 | if (var_size < boot_used_size) { | ||
| 199 | boot_var_size = boot_used_size - var_size; | ||
| 200 | active_size += boot_var_size; | ||
| 201 | } else { | ||
| 202 | printk(KERN_WARNING FW_BUG "efi: Inconsistent initial sizes\n"); | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | if (boot_used_size && !finished) { | ||
| 207 | unsigned long size; | ||
| 208 | u32 attr; | ||
| 209 | efi_status_t s; | ||
| 210 | void *tmp; | ||
| 211 | |||
| 212 | s = virt_efi_get_variable(name, vendor, &attr, &size, NULL); | ||
| 213 | |||
| 214 | if (s != EFI_BUFFER_TOO_SMALL || !size) | ||
| 215 | return status; | ||
| 216 | |||
| 217 | tmp = kmalloc(size, GFP_ATOMIC); | ||
| 218 | |||
| 219 | if (!tmp) | ||
| 220 | return status; | ||
| 221 | |||
| 222 | s = virt_efi_get_variable(name, vendor, &attr, &size, tmp); | ||
| 223 | |||
| 224 | if (s == EFI_SUCCESS && (attr & EFI_VARIABLE_NON_VOLATILE)) { | ||
| 225 | var_size += size; | ||
| 226 | var_size += ucs2_strsize(name, 1024); | ||
| 227 | active_size += size; | ||
| 228 | active_size += VAR_METADATA_SIZE; | ||
| 229 | active_size += ucs2_strsize(name, 1024); | ||
| 230 | } | ||
| 231 | |||
| 232 | kfree(tmp); | ||
| 233 | } | ||
| 234 | |||
| 235 | return status; | ||
| 167 | } | 236 | } |
| 168 | 237 | ||
| 169 | static efi_status_t virt_efi_set_variable(efi_char16_t *name, | 238 | static efi_status_t virt_efi_set_variable(efi_char16_t *name, |
| @@ -172,9 +241,34 @@ static efi_status_t virt_efi_set_variable(efi_char16_t *name, | |||
| 172 | unsigned long data_size, | 241 | unsigned long data_size, |
| 173 | void *data) | 242 | void *data) |
| 174 | { | 243 | { |
| 175 | return efi_call_virt5(set_variable, | 244 | efi_status_t status; |
| 176 | name, vendor, attr, | 245 | u32 orig_attr = 0; |
| 177 | data_size, data); | 246 | unsigned long orig_size = 0; |
| 247 | |||
| 248 | status = virt_efi_get_variable(name, vendor, &orig_attr, &orig_size, | ||
| 249 | NULL); | ||
| 250 | |||
| 251 | if (status != EFI_BUFFER_TOO_SMALL) | ||
| 252 | orig_size = 0; | ||
| 253 | |||
| 254 | status = efi_call_virt5(set_variable, | ||
| 255 | name, vendor, attr, | ||
| 256 | data_size, data); | ||
| 257 | |||
| 258 | if (status == EFI_SUCCESS) { | ||
| 259 | if (orig_size) { | ||
| 260 | active_size -= orig_size; | ||
| 261 | active_size -= ucs2_strsize(name, 1024); | ||
| 262 | active_size -= VAR_METADATA_SIZE; | ||
| 263 | } | ||
| 264 | if (data_size) { | ||
| 265 | active_size += data_size; | ||
| 266 | active_size += ucs2_strsize(name, 1024); | ||
| 267 | active_size += VAR_METADATA_SIZE; | ||
| 268 | } | ||
| 269 | } | ||
| 270 | |||
| 271 | return status; | ||
| 178 | } | 272 | } |
| 179 | 273 | ||
| 180 | static efi_status_t virt_efi_query_variable_info(u32 attr, | 274 | static efi_status_t virt_efi_query_variable_info(u32 attr, |
| @@ -682,6 +776,9 @@ void __init efi_init(void) | |||
| 682 | char vendor[100] = "unknown"; | 776 | char vendor[100] = "unknown"; |
| 683 | int i = 0; | 777 | int i = 0; |
| 684 | void *tmp; | 778 | void *tmp; |
| 779 | struct setup_data *data; | ||
| 780 | struct efi_var_bootdata *efi_var_data; | ||
| 781 | u64 pa_data; | ||
| 685 | 782 | ||
| 686 | #ifdef CONFIG_X86_32 | 783 | #ifdef CONFIG_X86_32 |
| 687 | if (boot_params.efi_info.efi_systab_hi || | 784 | if (boot_params.efi_info.efi_systab_hi || |
| @@ -699,6 +796,22 @@ void __init efi_init(void) | |||
| 699 | if (efi_systab_init(efi_phys.systab)) | 796 | if (efi_systab_init(efi_phys.systab)) |
| 700 | return; | 797 | return; |
| 701 | 798 | ||
| 799 | pa_data = boot_params.hdr.setup_data; | ||
| 800 | while (pa_data) { | ||
| 801 | data = early_ioremap(pa_data, sizeof(*efi_var_data)); | ||
| 802 | if (data->type == SETUP_EFI_VARS) { | ||
| 803 | efi_var_data = (struct efi_var_bootdata *)data; | ||
| 804 | |||
| 805 | efi_var_store_size = efi_var_data->store_size; | ||
| 806 | efi_var_remaining_size = efi_var_data->remaining_size; | ||
| 807 | efi_var_max_var_size = efi_var_data->max_var_size; | ||
| 808 | } | ||
| 809 | pa_data = data->next; | ||
| 810 | early_iounmap(data, sizeof(*efi_var_data)); | ||
| 811 | } | ||
| 812 | |||
| 813 | boot_used_size = efi_var_store_size - efi_var_remaining_size; | ||
| 814 | |||
| 702 | set_bit(EFI_SYSTEM_TABLES, &x86_efi_facility); | 815 | set_bit(EFI_SYSTEM_TABLES, &x86_efi_facility); |
| 703 | 816 | ||
| 704 | /* | 817 | /* |
| @@ -999,3 +1112,48 @@ u64 efi_mem_attributes(unsigned long phys_addr) | |||
| 999 | } | 1112 | } |
| 1000 | return 0; | 1113 | return 0; |
| 1001 | } | 1114 | } |
| 1115 | |||
| 1116 | /* | ||
| 1117 | * Some firmware has serious problems when using more than 50% of the EFI | ||
| 1118 | * variable store, i.e. it triggers bugs that can brick machines. Ensure that | ||
| 1119 | * we never use more than this safe limit. | ||
| 1120 | * | ||
| 1121 | * Return EFI_SUCCESS if it is safe to write 'size' bytes to the variable | ||
| 1122 | * store. | ||
| 1123 | */ | ||
| 1124 | efi_status_t efi_query_variable_store(u32 attributes, unsigned long size) | ||
| 1125 | { | ||
| 1126 | efi_status_t status; | ||
| 1127 | u64 storage_size, remaining_size, max_size; | ||
| 1128 | |||
| 1129 | status = efi.query_variable_info(attributes, &storage_size, | ||
| 1130 | &remaining_size, &max_size); | ||
| 1131 | if (status != EFI_SUCCESS) | ||
| 1132 | return status; | ||
| 1133 | |||
| 1134 | if (!max_size && remaining_size > size) | ||
| 1135 | printk_once(KERN_ERR FW_BUG "Broken EFI implementation" | ||
| 1136 | " is returning MaxVariableSize=0\n"); | ||
| 1137 | /* | ||
| 1138 | * Some firmware implementations refuse to boot if there's insufficient | ||
| 1139 | * space in the variable store. We account for that by refusing the | ||
| 1140 | * write if permitting it would reduce the available space to under | ||
| 1141 | * 50%. However, some firmware won't reclaim variable space until | ||
| 1142 | * after the used (not merely the actively used) space drops below | ||
| 1143 | * a threshold. We can approximate that case with the value calculated | ||
| 1144 | * above. If both the firmware and our calculations indicate that the | ||
| 1145 | * available space would drop below 50%, refuse the write. | ||
| 1146 | */ | ||
| 1147 | |||
| 1148 | if (!storage_size || size > remaining_size || | ||
| 1149 | (max_size && size > max_size)) | ||
| 1150 | return EFI_OUT_OF_RESOURCES; | ||
| 1151 | |||
| 1152 | if (!efi_no_storage_paranoia && | ||
| 1153 | ((active_size + size + VAR_METADATA_SIZE > storage_size / 2) && | ||
| 1154 | (remaining_size - size < storage_size / 2))) | ||
| 1155 | return EFI_OUT_OF_RESOURCES; | ||
| 1156 | |||
| 1157 | return EFI_SUCCESS; | ||
| 1158 | } | ||
| 1159 | EXPORT_SYMBOL_GPL(efi_query_variable_store); | ||
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 42c759a4d047..3e532002e4d1 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig | |||
| @@ -39,6 +39,7 @@ config FIRMWARE_MEMMAP | |||
| 39 | config EFI_VARS | 39 | config EFI_VARS |
| 40 | tristate "EFI Variable Support via sysfs" | 40 | tristate "EFI Variable Support via sysfs" |
| 41 | depends on EFI | 41 | depends on EFI |
| 42 | select UCS2_STRING | ||
| 42 | default n | 43 | default n |
| 43 | help | 44 | help |
| 44 | If you say Y here, you are able to get EFI (Extensible Firmware | 45 | If you say Y here, you are able to get EFI (Extensible Firmware |
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c index 7acafb80fd4c..182ce9471175 100644 --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c | |||
| @@ -80,6 +80,7 @@ | |||
| 80 | #include <linux/slab.h> | 80 | #include <linux/slab.h> |
| 81 | #include <linux/pstore.h> | 81 | #include <linux/pstore.h> |
| 82 | #include <linux/ctype.h> | 82 | #include <linux/ctype.h> |
| 83 | #include <linux/ucs2_string.h> | ||
| 83 | 84 | ||
| 84 | #include <linux/fs.h> | 85 | #include <linux/fs.h> |
| 85 | #include <linux/ramfs.h> | 86 | #include <linux/ramfs.h> |
| @@ -172,51 +173,6 @@ static void efivar_update_sysfs_entries(struct work_struct *); | |||
| 172 | static DECLARE_WORK(efivar_work, efivar_update_sysfs_entries); | 173 | static DECLARE_WORK(efivar_work, efivar_update_sysfs_entries); |
| 173 | static bool efivar_wq_enabled = true; | 174 | static bool efivar_wq_enabled = true; |
| 174 | 175 | ||
| 175 | /* Return the number of unicode characters in data */ | ||
| 176 | static unsigned long | ||
| 177 | utf16_strnlen(efi_char16_t *s, size_t maxlength) | ||
| 178 | { | ||
| 179 | unsigned long length = 0; | ||
| 180 | |||
| 181 | while (*s++ != 0 && length < maxlength) | ||
| 182 | length++; | ||
| 183 | return length; | ||
| 184 | } | ||
| 185 | |||
| 186 | static inline unsigned long | ||
| 187 | utf16_strlen(efi_char16_t *s) | ||
| 188 | { | ||
| 189 | return utf16_strnlen(s, ~0UL); | ||
| 190 | } | ||
| 191 | |||
| 192 | /* | ||
| 193 | * Return the number of bytes is the length of this string | ||
| 194 | * Note: this is NOT the same as the number of unicode characters | ||
| 195 | */ | ||
| 196 | static inline unsigned long | ||
| 197 | utf16_strsize(efi_char16_t *data, unsigned long maxlength) | ||
| 198 | { | ||
| 199 | return utf16_strnlen(data, maxlength/sizeof(efi_char16_t)) * sizeof(efi_char16_t); | ||
| 200 | } | ||
| 201 | |||
| 202 | static inline int | ||
| 203 | utf16_strncmp(const efi_char16_t *a, const efi_char16_t *b, size_t len) | ||
| 204 | { | ||
| 205 | while (1) { | ||
| 206 | if (len == 0) | ||
| 207 | return 0; | ||
| 208 | if (*a < *b) | ||
| 209 | return -1; | ||
| 210 | if (*a > *b) | ||
| 211 | return 1; | ||
| 212 | if (*a == 0) /* implies *b == 0 */ | ||
| 213 | return 0; | ||
| 214 | a++; | ||
| 215 | b++; | ||
| 216 | len--; | ||
| 217 | } | ||
| 218 | } | ||
| 219 | |||
| 220 | static bool | 176 | static bool |
| 221 | validate_device_path(struct efi_variable *var, int match, u8 *buffer, | 177 | validate_device_path(struct efi_variable *var, int match, u8 *buffer, |
| 222 | unsigned long len) | 178 | unsigned long len) |
| @@ -268,7 +224,7 @@ validate_load_option(struct efi_variable *var, int match, u8 *buffer, | |||
| 268 | u16 filepathlength; | 224 | u16 filepathlength; |
| 269 | int i, desclength = 0, namelen; | 225 | int i, desclength = 0, namelen; |
| 270 | 226 | ||
| 271 | namelen = utf16_strnlen(var->VariableName, sizeof(var->VariableName)); | 227 | namelen = ucs2_strnlen(var->VariableName, sizeof(var->VariableName)); |
| 272 | 228 | ||
| 273 | /* Either "Boot" or "Driver" followed by four digits of hex */ | 229 | /* Either "Boot" or "Driver" followed by four digits of hex */ |
| 274 | for (i = match; i < match+4; i++) { | 230 | for (i = match; i < match+4; i++) { |
| @@ -291,7 +247,7 @@ validate_load_option(struct efi_variable *var, int match, u8 *buffer, | |||
| 291 | * There's no stored length for the description, so it has to be | 247 | * There's no stored length for the description, so it has to be |
| 292 | * found by hand | 248 | * found by hand |
| 293 | */ | 249 | */ |
| 294 | desclength = utf16_strsize((efi_char16_t *)(buffer + 6), len - 6) + 2; | 250 | desclength = ucs2_strsize((efi_char16_t *)(buffer + 6), len - 6) + 2; |
| 295 | 251 | ||
| 296 | /* Each boot entry must have a descriptor */ | 252 | /* Each boot entry must have a descriptor */ |
| 297 | if (!desclength) | 253 | if (!desclength) |
| @@ -436,24 +392,12 @@ static efi_status_t | |||
| 436 | check_var_size_locked(struct efivars *efivars, u32 attributes, | 392 | check_var_size_locked(struct efivars *efivars, u32 attributes, |
| 437 | unsigned long size) | 393 | unsigned long size) |
| 438 | { | 394 | { |
| 439 | u64 storage_size, remaining_size, max_size; | ||
| 440 | efi_status_t status; | ||
| 441 | const struct efivar_operations *fops = efivars->ops; | 395 | const struct efivar_operations *fops = efivars->ops; |
| 442 | 396 | ||
| 443 | if (!efivars->ops->query_variable_info) | 397 | if (!efivars->ops->query_variable_store) |
| 444 | return EFI_UNSUPPORTED; | 398 | return EFI_UNSUPPORTED; |
| 445 | 399 | ||
| 446 | status = fops->query_variable_info(attributes, &storage_size, | 400 | return fops->query_variable_store(attributes, size); |
| 447 | &remaining_size, &max_size); | ||
| 448 | |||
| 449 | if (status != EFI_SUCCESS) | ||
| 450 | return status; | ||
| 451 | |||
| 452 | if (!storage_size || size > remaining_size || size > max_size || | ||
| 453 | (remaining_size - size) < (storage_size / 2)) | ||
| 454 | return EFI_OUT_OF_RESOURCES; | ||
| 455 | |||
| 456 | return status; | ||
| 457 | } | 401 | } |
| 458 | 402 | ||
| 459 | 403 | ||
| @@ -593,7 +537,7 @@ efivar_store_raw(struct efivar_entry *entry, const char *buf, size_t count) | |||
| 593 | spin_lock_irq(&efivars->lock); | 537 | spin_lock_irq(&efivars->lock); |
| 594 | 538 | ||
| 595 | status = check_var_size_locked(efivars, new_var->Attributes, | 539 | status = check_var_size_locked(efivars, new_var->Attributes, |
| 596 | new_var->DataSize + utf16_strsize(new_var->VariableName, 1024)); | 540 | new_var->DataSize + ucs2_strsize(new_var->VariableName, 1024)); |
| 597 | 541 | ||
| 598 | if (status == EFI_SUCCESS || status == EFI_UNSUPPORTED) | 542 | if (status == EFI_SUCCESS || status == EFI_UNSUPPORTED) |
| 599 | status = efivars->ops->set_variable(new_var->VariableName, | 543 | status = efivars->ops->set_variable(new_var->VariableName, |
| @@ -771,7 +715,7 @@ static ssize_t efivarfs_file_write(struct file *file, | |||
| 771 | * QueryVariableInfo() isn't supported by the firmware. | 715 | * QueryVariableInfo() isn't supported by the firmware. |
| 772 | */ | 716 | */ |
| 773 | 717 | ||
| 774 | varsize = datasize + utf16_strsize(var->var.VariableName, 1024); | 718 | varsize = datasize + ucs2_strsize(var->var.VariableName, 1024); |
| 775 | status = check_var_size(efivars, attributes, varsize); | 719 | status = check_var_size(efivars, attributes, varsize); |
| 776 | 720 | ||
| 777 | if (status != EFI_SUCCESS) { | 721 | if (status != EFI_SUCCESS) { |
| @@ -1223,7 +1167,7 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) | |||
| 1223 | 1167 | ||
| 1224 | inode = NULL; | 1168 | inode = NULL; |
| 1225 | 1169 | ||
| 1226 | len = utf16_strlen(entry->var.VariableName); | 1170 | len = ucs2_strlen(entry->var.VariableName); |
| 1227 | 1171 | ||
| 1228 | /* name, plus '-', plus GUID, plus NUL*/ | 1172 | /* name, plus '-', plus GUID, plus NUL*/ |
| 1229 | name = kmalloc(len + 1 + GUID_LEN + 1, GFP_ATOMIC); | 1173 | name = kmalloc(len + 1 + GUID_LEN + 1, GFP_ATOMIC); |
| @@ -1481,8 +1425,8 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count, | |||
| 1481 | 1425 | ||
| 1482 | if (efi_guidcmp(entry->var.VendorGuid, vendor)) | 1426 | if (efi_guidcmp(entry->var.VendorGuid, vendor)) |
| 1483 | continue; | 1427 | continue; |
| 1484 | if (utf16_strncmp(entry->var.VariableName, efi_name, | 1428 | if (ucs2_strncmp(entry->var.VariableName, efi_name, |
| 1485 | utf16_strlen(efi_name))) { | 1429 | ucs2_strlen(efi_name))) { |
| 1486 | /* | 1430 | /* |
| 1487 | * Check if an old format, | 1431 | * Check if an old format, |
| 1488 | * which doesn't support holding | 1432 | * which doesn't support holding |
| @@ -1494,8 +1438,8 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count, | |||
| 1494 | for (i = 0; i < DUMP_NAME_LEN; i++) | 1438 | for (i = 0; i < DUMP_NAME_LEN; i++) |
| 1495 | efi_name_old[i] = name_old[i]; | 1439 | efi_name_old[i] = name_old[i]; |
| 1496 | 1440 | ||
| 1497 | if (utf16_strncmp(entry->var.VariableName, efi_name_old, | 1441 | if (ucs2_strncmp(entry->var.VariableName, efi_name_old, |
| 1498 | utf16_strlen(efi_name_old))) | 1442 | ucs2_strlen(efi_name_old))) |
| 1499 | continue; | 1443 | continue; |
| 1500 | } | 1444 | } |
| 1501 | 1445 | ||
| @@ -1573,8 +1517,8 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj, | |||
| 1573 | * Does this variable already exist? | 1517 | * Does this variable already exist? |
| 1574 | */ | 1518 | */ |
| 1575 | list_for_each_entry_safe(search_efivar, n, &efivars->list, list) { | 1519 | list_for_each_entry_safe(search_efivar, n, &efivars->list, list) { |
| 1576 | strsize1 = utf16_strsize(search_efivar->var.VariableName, 1024); | 1520 | strsize1 = ucs2_strsize(search_efivar->var.VariableName, 1024); |
| 1577 | strsize2 = utf16_strsize(new_var->VariableName, 1024); | 1521 | strsize2 = ucs2_strsize(new_var->VariableName, 1024); |
| 1578 | if (strsize1 == strsize2 && | 1522 | if (strsize1 == strsize2 && |
| 1579 | !memcmp(&(search_efivar->var.VariableName), | 1523 | !memcmp(&(search_efivar->var.VariableName), |
| 1580 | new_var->VariableName, strsize1) && | 1524 | new_var->VariableName, strsize1) && |
| @@ -1590,7 +1534,7 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj, | |||
| 1590 | } | 1534 | } |
| 1591 | 1535 | ||
| 1592 | status = check_var_size_locked(efivars, new_var->Attributes, | 1536 | status = check_var_size_locked(efivars, new_var->Attributes, |
| 1593 | new_var->DataSize + utf16_strsize(new_var->VariableName, 1024)); | 1537 | new_var->DataSize + ucs2_strsize(new_var->VariableName, 1024)); |
| 1594 | 1538 | ||
| 1595 | if (status && status != EFI_UNSUPPORTED) { | 1539 | if (status && status != EFI_UNSUPPORTED) { |
| 1596 | spin_unlock_irq(&efivars->lock); | 1540 | spin_unlock_irq(&efivars->lock); |
| @@ -1614,7 +1558,7 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj, | |||
| 1614 | 1558 | ||
| 1615 | /* Create the entry in sysfs. Locking is not required here */ | 1559 | /* Create the entry in sysfs. Locking is not required here */ |
| 1616 | status = efivar_create_sysfs_entry(efivars, | 1560 | status = efivar_create_sysfs_entry(efivars, |
| 1617 | utf16_strsize(new_var->VariableName, | 1561 | ucs2_strsize(new_var->VariableName, |
| 1618 | 1024), | 1562 | 1024), |
| 1619 | new_var->VariableName, | 1563 | new_var->VariableName, |
| 1620 | &new_var->VendorGuid); | 1564 | &new_var->VendorGuid); |
| @@ -1644,8 +1588,8 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj, | |||
| 1644 | * Does this variable already exist? | 1588 | * Does this variable already exist? |
| 1645 | */ | 1589 | */ |
| 1646 | list_for_each_entry_safe(search_efivar, n, &efivars->list, list) { | 1590 | list_for_each_entry_safe(search_efivar, n, &efivars->list, list) { |
| 1647 | strsize1 = utf16_strsize(search_efivar->var.VariableName, 1024); | 1591 | strsize1 = ucs2_strsize(search_efivar->var.VariableName, 1024); |
| 1648 | strsize2 = utf16_strsize(del_var->VariableName, 1024); | 1592 | strsize2 = ucs2_strsize(del_var->VariableName, 1024); |
| 1649 | if (strsize1 == strsize2 && | 1593 | if (strsize1 == strsize2 && |
| 1650 | !memcmp(&(search_efivar->var.VariableName), | 1594 | !memcmp(&(search_efivar->var.VariableName), |
| 1651 | del_var->VariableName, strsize1) && | 1595 | del_var->VariableName, strsize1) && |
| @@ -1691,9 +1635,9 @@ static bool variable_is_present(efi_char16_t *variable_name, efi_guid_t *vendor) | |||
| 1691 | unsigned long strsize1, strsize2; | 1635 | unsigned long strsize1, strsize2; |
| 1692 | bool found = false; | 1636 | bool found = false; |
| 1693 | 1637 | ||
| 1694 | strsize1 = utf16_strsize(variable_name, 1024); | 1638 | strsize1 = ucs2_strsize(variable_name, 1024); |
| 1695 | list_for_each_entry_safe(entry, n, &efivars->list, list) { | 1639 | list_for_each_entry_safe(entry, n, &efivars->list, list) { |
| 1696 | strsize2 = utf16_strsize(entry->var.VariableName, 1024); | 1640 | strsize2 = ucs2_strsize(entry->var.VariableName, 1024); |
| 1697 | if (strsize1 == strsize2 && | 1641 | if (strsize1 == strsize2 && |
| 1698 | !memcmp(variable_name, &(entry->var.VariableName), | 1642 | !memcmp(variable_name, &(entry->var.VariableName), |
| 1699 | strsize2) && | 1643 | strsize2) && |
| @@ -2131,7 +2075,7 @@ efivars_init(void) | |||
| 2131 | ops.get_variable = efi.get_variable; | 2075 | ops.get_variable = efi.get_variable; |
| 2132 | ops.set_variable = efi.set_variable; | 2076 | ops.set_variable = efi.set_variable; |
| 2133 | ops.get_next_variable = efi.get_next_variable; | 2077 | ops.get_next_variable = efi.get_next_variable; |
| 2134 | ops.query_variable_info = efi.query_variable_info; | 2078 | ops.query_variable_store = efi_query_variable_store; |
| 2135 | 2079 | ||
| 2136 | error = register_efivars(&__efivars, &ops, efi_kobj); | 2080 | error = register_efivars(&__efivars, &ops, efi_kobj); |
| 2137 | if (error) | 2081 | if (error) |
diff --git a/include/linux/efi.h b/include/linux/efi.h index 9bf2f1fcae27..3d7df3d32c66 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h | |||
| @@ -333,6 +333,7 @@ typedef efi_status_t efi_query_capsule_caps_t(efi_capsule_header_t **capsules, | |||
| 333 | unsigned long count, | 333 | unsigned long count, |
| 334 | u64 *max_size, | 334 | u64 *max_size, |
| 335 | int *reset_type); | 335 | int *reset_type); |
| 336 | typedef efi_status_t efi_query_variable_store_t(u32 attributes, unsigned long size); | ||
| 336 | 337 | ||
| 337 | /* | 338 | /* |
| 338 | * EFI Configuration Table and GUID definitions | 339 | * EFI Configuration Table and GUID definitions |
| @@ -575,9 +576,15 @@ extern void efi_enter_virtual_mode (void); /* switch EFI to virtual mode, if pos | |||
| 575 | #ifdef CONFIG_X86 | 576 | #ifdef CONFIG_X86 |
| 576 | extern void efi_late_init(void); | 577 | extern void efi_late_init(void); |
| 577 | extern void efi_free_boot_services(void); | 578 | extern void efi_free_boot_services(void); |
| 579 | extern efi_status_t efi_query_variable_store(u32 attributes, unsigned long size); | ||
| 578 | #else | 580 | #else |
| 579 | static inline void efi_late_init(void) {} | 581 | static inline void efi_late_init(void) {} |
| 580 | static inline void efi_free_boot_services(void) {} | 582 | static inline void efi_free_boot_services(void) {} |
| 583 | |||
| 584 | static inline efi_status_t efi_query_variable_store(u32 attributes, unsigned long size) | ||
| 585 | { | ||
| 586 | return EFI_SUCCESS; | ||
| 587 | } | ||
| 581 | #endif | 588 | #endif |
| 582 | extern void __iomem *efi_lookup_mapped_addr(u64 phys_addr); | 589 | extern void __iomem *efi_lookup_mapped_addr(u64 phys_addr); |
| 583 | extern u64 efi_get_iobase (void); | 590 | extern u64 efi_get_iobase (void); |
| @@ -731,7 +738,7 @@ struct efivar_operations { | |||
| 731 | efi_get_variable_t *get_variable; | 738 | efi_get_variable_t *get_variable; |
| 732 | efi_get_next_variable_t *get_next_variable; | 739 | efi_get_next_variable_t *get_next_variable; |
| 733 | efi_set_variable_t *set_variable; | 740 | efi_set_variable_t *set_variable; |
| 734 | efi_query_variable_info_t *query_variable_info; | 741 | efi_query_variable_store_t *query_variable_store; |
| 735 | }; | 742 | }; |
| 736 | 743 | ||
| 737 | struct efivars { | 744 | struct efivars { |
diff --git a/include/linux/ucs2_string.h b/include/linux/ucs2_string.h new file mode 100644 index 000000000000..cbb20afdbc01 --- /dev/null +++ b/include/linux/ucs2_string.h | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | #ifndef _LINUX_UCS2_STRING_H_ | ||
| 2 | #define _LINUX_UCS2_STRING_H_ | ||
| 3 | |||
| 4 | #include <linux/types.h> /* for size_t */ | ||
| 5 | #include <linux/stddef.h> /* for NULL */ | ||
| 6 | |||
| 7 | typedef u16 ucs2_char_t; | ||
| 8 | |||
| 9 | unsigned long ucs2_strnlen(const ucs2_char_t *s, size_t maxlength); | ||
| 10 | unsigned long ucs2_strlen(const ucs2_char_t *s); | ||
| 11 | unsigned long ucs2_strsize(const ucs2_char_t *data, unsigned long maxlength); | ||
| 12 | int ucs2_strncmp(const ucs2_char_t *a, const ucs2_char_t *b, size_t len); | ||
| 13 | |||
| 14 | #endif /* _LINUX_UCS2_STRING_H_ */ | ||
diff --git a/lib/Kconfig b/lib/Kconfig index 3958dc4389f9..fe01d418b09a 100644 --- a/lib/Kconfig +++ b/lib/Kconfig | |||
| @@ -404,4 +404,7 @@ config OID_REGISTRY | |||
| 404 | help | 404 | help |
| 405 | Enable fast lookup object identifier registry. | 405 | Enable fast lookup object identifier registry. |
| 406 | 406 | ||
| 407 | config UCS2_STRING | ||
| 408 | tristate | ||
| 409 | |||
| 407 | endmenu | 410 | endmenu |
diff --git a/lib/Makefile b/lib/Makefile index d7946ff75b2e..6e2cc561f761 100644 --- a/lib/Makefile +++ b/lib/Makefile | |||
| @@ -174,3 +174,5 @@ quiet_cmd_build_OID_registry = GEN $@ | |||
| 174 | cmd_build_OID_registry = perl $(srctree)/$(src)/build_OID_registry $< $@ | 174 | cmd_build_OID_registry = perl $(srctree)/$(src)/build_OID_registry $< $@ |
| 175 | 175 | ||
| 176 | clean-files += oid_registry_data.c | 176 | clean-files += oid_registry_data.c |
| 177 | |||
| 178 | obj-$(CONFIG_UCS2_STRING) += ucs2_string.o | ||
diff --git a/lib/ucs2_string.c b/lib/ucs2_string.c new file mode 100644 index 000000000000..6f500ef2301d --- /dev/null +++ b/lib/ucs2_string.c | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | #include <linux/ucs2_string.h> | ||
| 2 | #include <linux/module.h> | ||
| 3 | |||
| 4 | /* Return the number of unicode characters in data */ | ||
| 5 | unsigned long | ||
| 6 | ucs2_strnlen(const ucs2_char_t *s, size_t maxlength) | ||
| 7 | { | ||
| 8 | unsigned long length = 0; | ||
| 9 | |||
| 10 | while (*s++ != 0 && length < maxlength) | ||
| 11 | length++; | ||
| 12 | return length; | ||
| 13 | } | ||
| 14 | EXPORT_SYMBOL(ucs2_strnlen); | ||
| 15 | |||
| 16 | unsigned long | ||
| 17 | ucs2_strlen(const ucs2_char_t *s) | ||
| 18 | { | ||
| 19 | return ucs2_strnlen(s, ~0UL); | ||
| 20 | } | ||
| 21 | EXPORT_SYMBOL(ucs2_strlen); | ||
| 22 | |||
| 23 | /* | ||
| 24 | * Return the number of bytes is the length of this string | ||
| 25 | * Note: this is NOT the same as the number of unicode characters | ||
| 26 | */ | ||
| 27 | unsigned long | ||
| 28 | ucs2_strsize(const ucs2_char_t *data, unsigned long maxlength) | ||
| 29 | { | ||
| 30 | return ucs2_strnlen(data, maxlength/sizeof(ucs2_char_t)) * sizeof(ucs2_char_t); | ||
| 31 | } | ||
| 32 | EXPORT_SYMBOL(ucs2_strsize); | ||
| 33 | |||
| 34 | int | ||
| 35 | ucs2_strncmp(const ucs2_char_t *a, const ucs2_char_t *b, size_t len) | ||
| 36 | { | ||
| 37 | while (1) { | ||
| 38 | if (len == 0) | ||
| 39 | return 0; | ||
| 40 | if (*a < *b) | ||
| 41 | return -1; | ||
| 42 | if (*a > *b) | ||
| 43 | return 1; | ||
| 44 | if (*a == 0) /* implies *b == 0 */ | ||
| 45 | return 0; | ||
| 46 | a++; | ||
| 47 | b++; | ||
| 48 | len--; | ||
| 49 | } | ||
| 50 | } | ||
| 51 | EXPORT_SYMBOL(ucs2_strncmp); | ||
