[PATCH v2 3/4] x86/efi: Retrieve and assign Apple device properties

2016-09-07 Thread Lukas Wunner
 It seems like this approach
would be more complicated and require more code. That doesn't seem
justified without a specific use case.

For comparison, the strategy on macOS is to assign properties to objects
in the ACPI namespace (AppleACPIPlatformExpert::mergeEFIProperties()).
That approach is definitely wrong as it fails for devices not present in
the namespace: The NHI EFI driver supplies properties for attached
Thunderbolt devices, yet on Macs with Thunderbolt 1 only one device
level behind the host controller is described in the namespace.
Consequently macOS cannot assign properties for chained devices. With
Thunderbolt 2 they started to describe three device levels behind host
controllers in the namespace but this grossly inflates the SSDT and
still fails if the user daisy-chained more than three devices.

We copy the property names and values from the setup_data payload to
swappable virtual memory and afterwards make the payload available to
the page allocator. This is just for the sake of good housekeeping, it
wouldn't occupy a meaningful amount of physical memory ( bytes on my
machine). Only the payload is freed, not the setup_data header since
otherwise we'd break the list linkage and we cannot safely update the
predecessor's ->next link because there's no locking for the list.

The payload is currently not passed on to kexec'ed kernels, same for PCI
ROMs retrieved by setup_efi_pci(). This can be added later if there is
demand by amending setup_efi_state(). The payload can then no longer be
made available to the page allocator of course.

Cc: rever...@put.as
Cc: grub-devel@gnu.org
Cc: Matt Fleming <m...@codeblueprint.co.uk>
Cc: Andreas Noever <andreas.noe...@gmail.com>
Tested-by: Lukas Wunner <lu...@wunner.de> [MacBookPro9,1]
Tested-by: Pierre Moreau <pierre.mor...@free.fr> [MacBookPro11,3]
Signed-off-by: Lukas Wunner <lu...@wunner.de>
---
 Documentation/kernel-parameters.txt |   5 +
 arch/x86/boot/compressed/eboot.c|  63 +
 arch/x86/include/uapi/asm/bootparam.h   |   1 +
 drivers/firmware/efi/Kconfig|  13 ++
 drivers/firmware/efi/Makefile   |   1 +
 drivers/firmware/efi/apple-properties.c | 230 
 include/linux/efi.h |  17 +++
 7 files changed, 330 insertions(+)
 create mode 100644 drivers/firmware/efi/apple-properties.c

diff --git a/Documentation/kernel-parameters.txt 
b/Documentation/kernel-parameters.txt
index 82b42c9..d693fc3d 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -993,6 +993,11 @@ bytes respectively. Such letter suffixes can also be 
entirely omitted.
 
dscc4.setup=[NET]
 
+   dump_apple_properties   [X86]
+   Dump name and content of EFI device properties on
+   x86 Macs.  Useful for driver authors to determine
+   what data is available or for reverse-engineering.
+
dyndbg[="val"]  [KNL,DYNAMIC_DEBUG]
module.dyndbg[="val"]
Enable debug messages at boot time.  See
diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index 447a6a2..1687770 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -537,6 +537,63 @@ 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_call_early(locate_protocol, , NULL, );
+   if (status != EFI_SUCCESS)
+   return;
+
+   if ((efi_is_64bit() ?
+   ((apple_properties_protocol_64 *)properties)->version :
+   ((apple_properties_protocol_32 *)properties)->version)
+  != 0x1) {
+   efi_printk(sys_table, "Unsupported properties proto version\n");
+   return;
+   }
+
+   efi_early->call(efi_is_64bit() ?
+   ((apple_properties_protocol_64 *)properties)->get_all :
+   ((apple_properties_protocol_32 *)properties)->get_all,
+   properties, NULL, );
+   if (!size)
+   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_is_64bit() ?
+   ((apple_properties_protocol_64

[PATCH v2 0/4] Apple device properties

2016-09-07 Thread Lukas Wunner
Retrieve device properties from EFI on Macs before ExitBootServices is
called and assign them to devices (patch [3/4]). The devices that
properties pertain to are encoded as EFI Device Paths, so add a parser
for these (patch [2/4]). As a first use case, amend the Thunderbolt driver
to take advantage of the Device ROM supplied by EFI (patch [4/4]).

Patch [1/4] is already queued in Rafael J. Wysocki's tree for 4.9 and is
included here only because patch [2/4] wouldn't compile without it.

The series also depends on these two patches which are already queued
in Matt's tree for 4.9:

- x86/efi: Optimize away setup_gop32/64 if unused
  https://patchwork.kernel.org/patch/9315763/

- x86/efi: Allow invocation of arbitrary boot services
  https://patchwork.kernel.org/patch/9293371/


Changes since v1:

- Previously there were two separate patches for retrieving properties
  and assigning them to devices. These are now squashed together in
  patch [3/4]. (Requested by Matt Fleming.)

- The version of the EFI properties protocol as well as the properties
  payload is now checked.

- Applied a bit of polish all over.


Link to v1:
https://lkml.org/lkml/2016/7/27/218

Browseable on GitHub:
https://github.com/l1k/linux/commits/apple_properties_v2

Thanks,

Lukas


Lukas Wunner (4):
  ACPI / bus: Make acpi_get_first_physical_node() public
  efi: Add device path parser
  x86/efi: Retrieve and assign Apple device properties
  thunderbolt: Use Device ROM retrieved from EFI

 Documentation/kernel-parameters.txt |   5 +
 arch/x86/boot/compressed/eboot.c|  63 +
 arch/x86/include/uapi/asm/bootparam.h   |   1 +
 drivers/acpi/internal.h |   1 -
 drivers/firmware/efi/Kconfig|  18 +++
 drivers/firmware/efi/Makefile   |   2 +
 drivers/firmware/efi/apple-properties.c | 230 
 drivers/firmware/efi/dev-path-parser.c  | 186 ++
 drivers/thunderbolt/Kconfig |   1 +
 drivers/thunderbolt/eeprom.c|  42 ++
 drivers/thunderbolt/switch.c|   2 +-
 include/linux/acpi.h|   7 +
 include/linux/efi.h |  38 ++
 13 files changed, 594 insertions(+), 2 deletions(-)
 create mode 100644 drivers/firmware/efi/apple-properties.c
 create mode 100644 drivers/firmware/efi/dev-path-parser.c

-- 
2.9.3


___
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

2016-08-05 Thread Lukas Wunner
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


[PATCH 1/6] efi: Retrieve Apple device properties

2016-07-27 Thread 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.)

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 <m...@codeblueprint.co.uk>
Cc: Andreas Noever <andreas.noe...@gmail.com>
Tested-by: Lukas Wunner <lu...@wunner.de> [MacBookPro9,1]
Tested-by: Pierre Moreau <pierre.mor...@free.fr> [MacBookPro11,3]
Signed-off-by: Lukas Wunner <lu...@wunner.de>
---
 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,
+