Re: [PATCH 1/6] efi: Retrieve Apple device properties
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. ___ Grub-devel mailing list Grub-devel@gnu.org https://lists.gnu.org/mailman/listinfo/grub-devel
Re: [PATCH 1/6] efi: Retrieve Apple device properties
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, > > + , NULL, ); > > + if (status != EFI_SUCCESS) > > + return; > > + > > + do { > > + status = efi_call_early(allocate_pool, EFI_LOADER_DATA, > > + size + sizeof(struct setup_data), ); > > + 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, ); > > + 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 movrbx, 0x8005 ; EFI_BUFFER_TOO_SMALL ... 58e6 movrcx, qword [ss:rbp+var_38] ; properties protocol 58ea movrdx, rdi; properties buffer 58ed movr8, rsi ; buffer len 58f0 call qword [ds:rcx+0x20] ; properties->get_all 58f3 cmprax, 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 ___ Grub-devel mailing list Grub-devel@gnu.org https://lists.gnu.org/mailman/listinfo/grub-devel
Re: [PATCH 1/6] efi: Retrieve Apple device properties
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, > + , NULL, ); > + if (status != EFI_SUCCESS) > + return; > + > + do { > + status = efi_call_early(allocate_pool, EFI_LOADER_DATA, > + size + sizeof(struct setup_data), ); > + 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, ); > + 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. ___ Grub-devel mailing list Grub-devel@gnu.org https://lists.gnu.org/mailman/listinfo/grub-devel
Re: [PATCH 1/6] efi: Retrieve Apple device properties
27.07.2016 14:20, Lukas Wunner пишет: > Retrieve device properties from EFI using Apple's proprietary protocol > with GUID 91BD12FE-F6C3-44FB-A5B7-5122AB303AE0, which looks like this: > > typedef struct { > unsigned long signature; /* 0x1 */ > efi_status_t (*get) ( > IN struct apple_properties_protocol *this, > IN struct efi_generic_dev_path *device, > IN efi_char16_t *property_name, > OUT void *buffer, > IN OUT u32 *buffer_size); > /* EFI_SUCCESS, EFI_NOT_FOUND, EFI_BUFFER_TOO_SMALL */ > efi_status_t (*set) ( > IN struct apple_properties_protocol *this, > IN struct efi_generic_dev_path *device, > IN efi_char16_t *property_name, > IN void *property_value, > IN u32 property_value_len); > /* allocates copies of property name and value */ > /* EFI_SUCCESS, EFI_OUT_OF_RESOURCES */ > efi_status_t (*del) ( > IN struct apple_properties_protocol *this, > IN struct efi_generic_dev_path *device, > IN efi_char16_t *property_name); > /* EFI_SUCCESS, EFI_NOT_FOUND */ > efi_status_t (*get_all) ( > IN struct apple_properties_protocol *this, > OUT void *buffer, > IN OUT u32 *buffer_size); > /* EFI_SUCCESS, EFI_BUFFER_TOO_SMALL */ > } apple_properties_protocol; > > Apple's EFI driver implementing this protocol, "AAPL,PathProperties", > is a per-device key/value store which is populated by other EFI drivers. > On macOS, these device properties are retrieved by the bootloader > /usr/standalone/i386/boot.efi. The extension AppleACPIPlatform.kext > subsequently merges them into the I/O Kit registry (see ioreg(8)) where > they can be queried by other kernel extensions and user space. > > These device properties contain vital information which cannot be > obtained any other way (e.g. Thunderbolt Device ROM). EFI drivers also > use them to communicate the current device state so that OS drivers can > pick up where EFI drivers left (e.g. GPU mode setting). > > The get_all() function conveniently fills a buffer with all properties > in marshalled form which can be passed to the kernel as a setup_data > payload. Note that the device properties will only be available if the > kernel is booted with the efistub. Distros should adjust their > installers to always use the efistub on Macs. grub with the "linux" > directive will not work unless the functionality of this commit is > duplicated in grub. (The "linuxefi" directive should work but is not > included upstream as of this writing.) > Intention was to partially merge linuxefi functionality into "linux" by autodetecting and preferring EFI stub support. Peter, any progress on this? > Thanks to osxreverser for this blog posting which was helpful in reverse > engineering Apple's EFI drivers and bootloader: > https://reverse.put.as/2016/06/25/apple-efi-firmware-passwords-and-the-scbo-myth/ > > If someone at Apple is reading this, please note there's a memory leak > in your implementation of the del() function as the property struct is > freed but the name and value allocations are not. > > Cc: rever...@put.as > Cc: grub-devel@gnu.org > Cc: Matt Fleming> Cc: Andreas Noever > Tested-by: Lukas Wunner [MacBookPro9,1] > Tested-by: Pierre Moreau [MacBookPro11,3] > Signed-off-by: Lukas Wunner > --- > arch/x86/boot/compressed/eboot.c | 55 > +++ > arch/x86/include/uapi/asm/bootparam.h | 1 + > include/linux/efi.h | 17 +++ > 3 files changed, 73 insertions(+) > > 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, > + , NULL, ); > + if (status != EFI_SUCCESS) > + return; > + > + do { > + status = efi_call_early(allocate_pool, EFI_LOADER_DATA, > + size + sizeof(struct setup_data), ); > + if (status != EFI_SUCCESS) { > + efi_printk(sys_table, > +"Failed to alloc mem for properties\n"); > + return; > + } > + status =
[PATCH 1/6] efi: Retrieve Apple device properties
Retrieve device properties from EFI using Apple's proprietary protocol with GUID 91BD12FE-F6C3-44FB-A5B7-5122AB303AE0, which looks like this: typedef struct { unsigned long signature; /* 0x1 */ efi_status_t (*get) ( IN struct apple_properties_protocol *this, IN struct efi_generic_dev_path *device, IN efi_char16_t *property_name, OUT void *buffer, IN OUT u32 *buffer_size); /* EFI_SUCCESS, EFI_NOT_FOUND, EFI_BUFFER_TOO_SMALL */ efi_status_t (*set) ( IN struct apple_properties_protocol *this, IN struct efi_generic_dev_path *device, IN efi_char16_t *property_name, IN void *property_value, IN u32 property_value_len); /* allocates copies of property name and value */ /* EFI_SUCCESS, EFI_OUT_OF_RESOURCES */ efi_status_t (*del) ( IN struct apple_properties_protocol *this, IN struct efi_generic_dev_path *device, IN efi_char16_t *property_name); /* EFI_SUCCESS, EFI_NOT_FOUND */ efi_status_t (*get_all) ( IN struct apple_properties_protocol *this, OUT void *buffer, IN OUT u32 *buffer_size); /* EFI_SUCCESS, EFI_BUFFER_TOO_SMALL */ } apple_properties_protocol; Apple's EFI driver implementing this protocol, "AAPL,PathProperties", is a per-device key/value store which is populated by other EFI drivers. On macOS, these device properties are retrieved by the bootloader /usr/standalone/i386/boot.efi. The extension AppleACPIPlatform.kext subsequently merges them into the I/O Kit registry (see ioreg(8)) where they can be queried by other kernel extensions and user space. These device properties contain vital information which cannot be obtained any other way (e.g. Thunderbolt Device ROM). EFI drivers also use them to communicate the current device state so that OS drivers can pick up where EFI drivers left (e.g. GPU mode setting). The get_all() function conveniently fills a buffer with all properties in marshalled form which can be passed to the kernel as a setup_data payload. Note that the device properties will only be available if the kernel is booted with the efistub. Distros should adjust their installers to always use the efistub on Macs. grub with the "linux" directive will not work unless the functionality of this commit is duplicated in grub. (The "linuxefi" directive should work but is not included upstream as of this writing.) Thanks to osxreverser for this blog posting which was helpful in reverse engineering Apple's EFI drivers and bootloader: https://reverse.put.as/2016/06/25/apple-efi-firmware-passwords-and-the-scbo-myth/ If someone at Apple is reading this, please note there's a memory leak in your implementation of the del() function as the property struct is freed but the name and value allocations are not. Cc: rever...@put.as Cc: grub-devel@gnu.org Cc: Matt FlemingCc: Andreas Noever Tested-by: Lukas Wunner [MacBookPro9,1] Tested-by: Pierre Moreau [MacBookPro11,3] Signed-off-by: Lukas Wunner --- arch/x86/boot/compressed/eboot.c | 55 +++ arch/x86/include/uapi/asm/bootparam.h | 1 + include/linux/efi.h | 17 +++ 3 files changed, 73 insertions(+) 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, + , NULL, ); + if (status != EFI_SUCCESS) + return; + + do { + status = efi_call_early(allocate_pool, EFI_LOADER_DATA, + size + sizeof(struct setup_data), ); + 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, ); + if (status == EFI_BUFFER_TOO_SMALL) +