Message ID | 833d193b8a18b0afe168c515e9e56a857ece4bd1.1469616641.git.lukas@wunner.de (mailing list archive) |
---|---|
State | Not Applicable, archived |
Headers | show |
On Thu, 28 Jul, at 02:25:41AM, Lukas Wunner wrote: > > diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c > index ff574da..7262ee4 100644 > --- a/arch/x86/boot/compressed/eboot.c > +++ b/arch/x86/boot/compressed/eboot.c > @@ -571,6 +571,55 @@ free_handle: > efi_call_early(free_pool, pci_handle); > } > > +static void retrieve_apple_device_properties(struct boot_params *params) > +{ > + efi_guid_t guid = APPLE_PROPERTIES_PROTOCOL_GUID; > + struct setup_data *data, *new; > + efi_status_t status; > + void *properties; > + u32 size = 0; > + > + status = efi_early->call( > + (unsigned long)sys_table->boottime->locate_protocol, > + &guid, NULL, &properties); > + if (status != EFI_SUCCESS) > + return; > + > + do { > + status = efi_call_early(allocate_pool, EFI_LOADER_DATA, > + size + sizeof(struct setup_data), &new); > + if (status != EFI_SUCCESS) { > + efi_printk(sys_table, > + "Failed to alloc mem for properties\n"); > + return; > + } > + status = efi_early->call(efi_early->is64 ? > + ((apple_properties_protocol_64 *)properties)->get_all : > + ((apple_properties_protocol_32 *)properties)->get_all, > + properties, new->data, &size); > + if (status == EFI_BUFFER_TOO_SMALL) > + efi_call_early(free_pool, new); > + } while (status == EFI_BUFFER_TOO_SMALL); Is this looping really required? Do we not know ahead of time what we expect the size to be? Writing this as a potentially infinite loop (if broken firmware always returns EFI_BUFFER_TOO_SMALL) is a bad idea. -- To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Thu, Aug 04, 2016 at 04:13:45PM +0100, Matt Fleming wrote: > On Thu, 28 Jul, at 02:25:41AM, Lukas Wunner wrote: > > diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c > > index ff574da..7262ee4 100644 > > --- a/arch/x86/boot/compressed/eboot.c > > +++ b/arch/x86/boot/compressed/eboot.c > > @@ -571,6 +571,55 @@ free_handle: > > efi_call_early(free_pool, pci_handle); > > } > > > > +static void retrieve_apple_device_properties(struct boot_params *params) > > +{ > > + efi_guid_t guid = APPLE_PROPERTIES_PROTOCOL_GUID; > > + struct setup_data *data, *new; > > + efi_status_t status; > > + void *properties; > > + u32 size = 0; > > + > > + status = efi_early->call( > > + (unsigned long)sys_table->boottime->locate_protocol, > > + &guid, NULL, &properties); > > + if (status != EFI_SUCCESS) > > + return; > > + > > + do { > > + status = efi_call_early(allocate_pool, EFI_LOADER_DATA, > > + size + sizeof(struct setup_data), &new); > > + if (status != EFI_SUCCESS) { > > + efi_printk(sys_table, > > + "Failed to alloc mem for properties\n"); > > + return; > > + } > > + status = efi_early->call(efi_early->is64 ? > > + ((apple_properties_protocol_64 *)properties)->get_all : > > + ((apple_properties_protocol_32 *)properties)->get_all, > > + properties, new->data, &size); > > + if (status == EFI_BUFFER_TOO_SMALL) > > + efi_call_early(free_pool, new); > > + } while (status == EFI_BUFFER_TOO_SMALL); > > Is this looping really required? Do we not know ahead of time what we > expect the size to be? Writing this as a potentially infinite loop (if > broken firmware always returns EFI_BUFFER_TOO_SMALL) is a bad idea. macOS' bootloader does exactly the same. So if the firmware was broken in this way, macOS wouldn't boot and it's unlikely that Apple would ship it. The code is not executed on non-Macs (due to the memcmp for sys_table->fw_vendor[] == u"Apple" in efi_main()). Looks like this in /usr/standalone/i386/boot.efi: 58b9 mov rbx, 0x8000000000000005 ; EFI_BUFFER_TOO_SMALL ... 58e6 mov rcx, qword [ss:rbp+var_38] ; properties protocol 58ea mov rdx, rdi ; properties buffer 58ed mov r8, rsi ; buffer len 58f0 call qword [ds:rcx+0x20] ; properties->get_all 58f3 cmp rax, rbx 58f6 je 0x58c5 ; infinite loop And the code in the corresponding ->get_all function in the EFI driver is such that it only returns either EFI_SUCCESS or EFI_BUFFER_TOO_SMALL. So I could cap the number of loop iterations but it would be pointless. I also checked the bootloader they shipped with OS X 10.6 (2009), they used Universal EFI binaries back then (x86_64 + i386) in order to support the very first Intel Macs of 2006. Found the same infinite loop there. The reason for the loop is that the number of device properties is dynamic. E.g. each attached Thunderbolt device is assigned 3 properties. If a Thunderbolt device is plugged in between a first loop iteration (to obtain the size) and a second loop iteration (to fill the buffer), EFI_BUFFER_TOO_SMALL is returned and a third loop iteration is needed. Thanks, Lukas -- To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Fri, 05 Aug, at 01:42:19PM, Lukas Wunner wrote: > > So I could cap the number of loop iterations but it would be pointless. OK, thanks for the explanation. -- To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index ff574da..7262ee4 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -571,6 +571,55 @@ free_handle: efi_call_early(free_pool, pci_handle); } +static void retrieve_apple_device_properties(struct boot_params *params) +{ + efi_guid_t guid = APPLE_PROPERTIES_PROTOCOL_GUID; + struct setup_data *data, *new; + efi_status_t status; + void *properties; + u32 size = 0; + + status = efi_early->call( + (unsigned long)sys_table->boottime->locate_protocol, + &guid, NULL, &properties); + if (status != EFI_SUCCESS) + return; + + do { + status = efi_call_early(allocate_pool, EFI_LOADER_DATA, + size + sizeof(struct setup_data), &new); + if (status != EFI_SUCCESS) { + efi_printk(sys_table, + "Failed to alloc mem for properties\n"); + return; + } + status = efi_early->call(efi_early->is64 ? + ((apple_properties_protocol_64 *)properties)->get_all : + ((apple_properties_protocol_32 *)properties)->get_all, + properties, new->data, &size); + if (status == EFI_BUFFER_TOO_SMALL) + efi_call_early(free_pool, new); + } while (status == EFI_BUFFER_TOO_SMALL); + + if (!size) { + efi_call_early(free_pool, new); + return; + } + + new->type = SETUP_APPLE_PROPERTIES; + new->len = size; + new->next = 0; + + data = (struct setup_data *)(unsigned long)params->hdr.setup_data; + if (!data) + params->hdr.setup_data = (unsigned long)new; + else { + while (data->next) + data = (struct setup_data *)(unsigned long)data->next; + data->next = (unsigned long)new; + } +} + static efi_status_t setup_uga32(void **uga_handle, unsigned long size, u32 *width, u32 *height) { @@ -1098,6 +1147,7 @@ free_mem_map: struct boot_params *efi_main(struct efi_config *c, struct boot_params *boot_params) { + efi_char16_t const apple[] = { 'A', 'p', 'p', 'l', 'e', 0 }; struct desc_ptr *gdt = NULL; efi_loaded_image_t *image; struct setup_header *hdr = &boot_params->hdr; @@ -1128,6 +1178,11 @@ struct boot_params *efi_main(struct efi_config *c, setup_efi_pci(boot_params); + if (!memcmp((void *)sys_table->fw_vendor, apple, sizeof(apple))) { + if (IS_ENABLED(CONFIG_APPLE_PROPERTIES)) + retrieve_apple_device_properties(boot_params); + } + status = efi_call_early(allocate_pool, EFI_LOADER_DATA, sizeof(*gdt), (void **)&gdt); if (status != EFI_SUCCESS) { diff --git a/arch/x86/include/uapi/asm/bootparam.h b/arch/x86/include/uapi/asm/bootparam.h index c18ce67..b10bf31 100644 --- a/arch/x86/include/uapi/asm/bootparam.h +++ b/arch/x86/include/uapi/asm/bootparam.h @@ -7,6 +7,7 @@ #define SETUP_DTB 2 #define SETUP_PCI 3 #define SETUP_EFI 4 +#define SETUP_APPLE_PROPERTIES 5 /* ram_size flags */ #define RAMDISK_IMAGE_START_MASK 0x07FF diff --git a/include/linux/efi.h b/include/linux/efi.h index 7f80a75..e53b4b2 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -432,6 +432,22 @@ typedef struct { #define EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO_16 0x20000 #define EFI_PCI_IO_ATTRIBUTE_VGA_IO_16 0x40000 +typedef struct { + u32 signature; + u32 get; + u32 set; + u32 del; + u32 get_all; +} apple_properties_protocol_32; + +typedef struct { + u64 signature; + u64 get; + u64 set; + u64 del; + u64 get_all; +} apple_properties_protocol_64; + /* * Types and defines for EFI ResetSystem */ @@ -580,6 +596,7 @@ void efi_native_runtime_setup(void); #define EFI_RNG_PROTOCOL_GUID EFI_GUID(0x3152bca5, 0xeade, 0x433d, 0x86, 0x2e, 0xc0, 0x1c, 0xdc, 0x29, 0x1f, 0x44) #define EFI_MEMORY_ATTRIBUTES_TABLE_GUID EFI_GUID(0xdcfa911d, 0x26eb, 0x469f, 0xa2, 0x20, 0x38, 0xb7, 0xdc, 0x46, 0x12, 0x20) #define EFI_CONSOLE_OUT_DEVICE_GUID EFI_GUID(0xd3b36f2c, 0xd551, 0x11d4, 0x9a, 0x46, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d) +#define APPLE_PROPERTIES_PROTOCOL_GUID EFI_GUID(0x91bd12fe, 0xf6c3, 0x44fb, 0xa5, 0xb7, 0x51, 0x22, 0xab, 0x30, 0x3a, 0xe0) /* * This GUID is used to pass to the kernel proper the struct screen_info