On Tue, Feb 28, 2017 at 11:09:51PM +0900, YASUOKA Masahiko wrote:
> Let me update the diff,
>
> On Tue, 28 Feb 2017 22:41:21 +0900 (JST)
> YASUOKA Masahiko <[email protected]> wrote:
> > On Mon, 27 Feb 2017 11:10:31 +0100
> > Patrick Wildt <[email protected]> wrote:
> >> I'm surprised this didn't come up earlier, but I think the for-loop is a
> >> bit wrong. What the code is supposed to be doing is going over each
> >> device path node for the loaded image, which is supposed to be the path
> >> to the device that efiboot was loaded from, and check for a node that
> >> is a medium (as in hard drive, cdrom) and look for a hard drive.
> >
> > Yes, you are right it's wrong.
> >
> >> As it turns out, the code would actually skip all device paths that
> >> are mediums and instead match on all non-media types. It's surprising
> >> to me that this seems to work for people.
> >>
> >> On Qemu and VMware the first node in the device path is ACPI (2) with
> >> subtype (1). Interestingly, the hard drive subtype in the media type
> >> has the subtype (1) as well. So that might be a reason it did work.
> >
> > As far as my test on QEMU and VAIO, the first node is ACPI and subtype
> > is 1.
> >
> > But we had another bug,
> >
> > 87 if (status == EFI_SUCCESS) {
> > 88 for (dp = dp0; !IsDevicePathEnd(dp);
> > 89 dp = NextDevicePathNode(dp)) {
> > 90 if (DevicePathType(dp) == MEDIA_DEVICE_PATH)
> > 91 continue;
> > 92 if (DevicePathSubType(dp) ==
> > MEDIA_HARDDRIVE_DP) {
> > 93 bios_bootdev = 0x80;
> > 94 efi_bootdp = dp;
> > 95 break;
> > 96 }
> > 97 break;
> > 98 }
> > 99 }
> >
> > if we fix the bug at #90, at #94 efi_bootdp will not become the first
> > dp node(dp0). Then efi_diskprobe() will fail to reorder hd? properly
> > since the function assumes efi_bootdp is the first dp node.
> >
> > So I'd like to commit the diff below.
> >
> > diff --git a/sys/arch/amd64/stand/efiboot/efiboot.c
> > b/sys/arch/amd64/stand/efiboot/efiboot.c
>
> (snip)
>
> > + if (efi_device_path_cmp(efi_bootdp, dp,
> > MESSAGING_DEVICE_PATH)
> > + == 0)
> > bootdev = 1;
>
> Not only the messaging device path but hardware device path and acpi
> device path also must be compared.
>
> diff --git a/sys/arch/amd64/stand/efiboot/efiboot.c
> b/sys/arch/amd64/stand/efiboot/efiboot.c
> index 1903862d271..2087a4112e3 100644
> --- a/sys/arch/amd64/stand/efiboot/efiboot.c
> +++ b/sys/arch/amd64/stand/efiboot/efiboot.c
> @@ -41,7 +41,8 @@
> EFI_SYSTEM_TABLE *ST;
> EFI_BOOT_SERVICES *BS;
> EFI_RUNTIME_SERVICES *RS;
> -EFI_HANDLE IH, efi_bootdp = NULL;
> +EFI_HANDLE IH;
> +EFI_DEVICE_PATH *efi_bootdp = NULL;
> EFI_PHYSICAL_ADDRESS heap;
> EFI_LOADED_IMAGE *loadedImage;
> UINTN heapsiz = 1 * 1024 * 1024;
> @@ -51,6 +52,7 @@ static EFI_GUID blkio_guid = BLOCK_IO_PROTOCOL;
> static EFI_GUID devp_guid = DEVICE_PATH_PROTOCOL;
> u_long efi_loadaddr;
>
> +static int efi_device_path_cmp(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *, int);
> static void efi_heap_init(void);
> static void efi_memprobe_internal(void);
> static void efi_video_init(void);
> @@ -87,14 +89,12 @@ efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab)
> if (status == EFI_SUCCESS) {
> for (dp = dp0; !IsDevicePathEnd(dp);
> dp = NextDevicePathNode(dp)) {
> - if (DevicePathType(dp) == MEDIA_DEVICE_PATH)
> - continue;
> - if (DevicePathSubType(dp) == MEDIA_HARDDRIVE_DP) {
> + if (DevicePathType(dp) == MEDIA_DEVICE_PATH &&
> + DevicePathSubType(dp) == MEDIA_HARDDRIVE_DP) {
> bios_bootdev = 0x80;
> - efi_bootdp = dp;
> + efi_bootdp = dp0;
> break;
> }
> - break;
> }
> }
>
> @@ -166,7 +166,7 @@ efi_diskprobe(void)
> EFI_BLOCK_IO *blkio;
> EFI_BLOCK_IO_MEDIA *media;
> struct diskinfo *di;
> - EFI_DEVICE_PATH *dp, *bp;
> + EFI_DEVICE_PATH *dp;
>
> TAILQ_INIT(&efi_disklist);
>
> @@ -193,23 +193,18 @@ efi_diskprobe(void)
> di = alloc(sizeof(struct diskinfo));
> efid_init(di, blkio);
>
> - if (efi_bootdp == NULL)
> - goto next;
> - status = EFI_CALL(BS->HandleProtocol, handles[i], &devp_guid,
> - (void **)&dp);
> - if (EFI_ERROR(status))
> - goto next;
> - bp = efi_bootdp;
> - while (1) {
> - if (IsDevicePathEnd(dp)) {
> + if (efi_bootdp != NULL) {
> + status = EFI_CALL(BS->HandleProtocol, handles[i],
> &devp_guid,
> + (void **)&dp);
> + if (EFI_ERROR(status))
> + goto next;
> + if (efi_device_path_cmp(efi_bootdp, dp,
> HARDWARE_DEVICE_PATH)
> + == 0 &&
> + efi_device_path_cmp(efi_bootdp, dp,
> ACPI_DEVICE_PATH)
> + == 0 &&
> + efi_device_path_cmp(efi_bootdp, dp,
> MESSAGING_DEVICE_PATH)
> + == 0)
That solidifies my point that passing a specific type to a device path
comparison is ugly.
> bootdev = 1;
> - break;
> - }
> - if (memcmp(dp, bp, sizeof(EFI_DEVICE_PATH)) != 0 ||
> - memcmp(dp, bp, DevicePathNodeLength(dp)) != 0)
> - break;
> - dp = NextDevicePathNode(dp);
> - bp = NextDevicePathNode(bp);
> }
> next:
> if (bootdev)
> @@ -221,6 +216,35 @@ next:
> free(handles, sz);
> }
>
> +static int
> +efi_device_path_cmp(EFI_DEVICE_PATH *dpa, EFI_DEVICE_PATH *dpb, int dptype)
> +{
> + int cmp;
> + EFI_DEVICE_PATH *dp, *dpt_a = NULL, *dpt_b = NULL;
> +
> + for (dp = dpa; !IsDevicePathEnd(dp); dp = NextDevicePathNode(dp)) {
> + if (DevicePathType(dp) == dptype) {
> + dpt_a = dp;
> + break;
> + }
> + }
> + for (dp = dpb; !IsDevicePathEnd(dp); dp = NextDevicePathNode(dp)) {
> + if (DevicePathType(dp) == dptype) {
> + dpt_b = dp;
> + break;
> + }
> + }
> +
> + if (dpt_a && dpt_b) {
> + cmp = DevicePathNodeLength(dpt_a) - DevicePathNodeLength(dpt_b);
> + if (cmp)
> + return (cmp);
> + return (memcmp(dpt_a, dpt_b, DevicePathNodeLength(dpt_a)));
> + }
> +
> + return ((uintptr_t)dpt_a - (uintptr_t)dpt_b);
> +}
> +
> /***********************************************************************
> * Memory
> ***********************************************************************/
>