On Wed, Mar 01, 2017 at 12:17:02AM +0900, YASUOKA Masahiko wrote:
> Hi,
> 
> On Tue, 28 Feb 2017 15:16:58 +0100
> Patrick Wildt <[email protected]> wrote:
> > On Tue, Feb 28, 2017 at 10:41:21PM +0900, YASUOKA Masahiko 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.
> > 
> > First of all, I've been having a look at supporting CDROMs in efiboot
> > and saw that FreeBSD has a nice "devpath.c" file which implements a few
> > good helpers.  I've been using efi_devpath_match(), efi_devpath_trim()
> > and efi_devpath_last_node().  Those have been particularly helpful and
> > simplify some code.  I wonder if it would make sense to pull that one
> > in instead of writing another completely new implementation of tha same
> > thing.
> > 
> >> diff --git a/sys/arch/amd64/stand/efiboot/efiboot.c 
> >> b/sys/arch/amd64/stand/efiboot/efiboot.c
> >> index 1903862d271..f82ea34f66e 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;
> > 
> > This part makes sense to me.
> > 
> >>                            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,14 @@ 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, 
> >> MESSAGING_DEVICE_PATH)
> >> +                      == 0)
> > 
> > This part makes sense to me as well.
> > 
> >>                            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 +212,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);
> >> +}
> > 
> > I don't like this.  Have a look at the FreeBSD implementation, it's
> > nicer sind you don't have to pass some device path type but instead
> > it does a full path comparison.
> 
> How will you fix by efi_devpath_match()?
> 
> AFAIK, we need to find the device path of the booted disk.  (not the
> booted parition)  So I belive "full path comparison" is not usable.
> 
> --yasuoka
> 

I see the problem.  The way people do it with hard drives and cdroms is
to trim/cut away the last node of the device path, because then you get
the device path to the entire device instead of just a subnode.  Then
you can compare the pathes.

Reply via email to