Index: sys/boot/efi/boot1/boot1.c =================================================================== --- sys/boot/efi/boot1/boot1.c +++ sys/boot/efi/boot1/boot1.c @@ -51,7 +51,10 @@ EFI_STATUS efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE* Xsystab); static void try_load(const boot_module_t* mod); -static EFI_STATUS probe_handle(EFI_HANDLE h); +static void probe_handle_status(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath, + BOOLEAN match); +static EFI_STATUS probe_handle(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath, + BOOLEAN match); EFI_SYSTEM_TABLE *systab; EFI_BOOT_SERVICES *bs; @@ -84,11 +87,204 @@ (void)bs->FreePool(buf); } +static BOOLEAN +paths_match(EFI_DEVICE_PATH *imgpath, EFI_DEVICE_PATH *devpath) +{ + int len; + + if (imgpath == NULL || imgpath->Type != devpath->Type || + imgpath->SubType != devpath->SubType) + return (FALSE); + + len = DevicePathNodeLength(imgpath); + + if (len != DevicePathNodeLength(devpath)) + return (FALSE); + + return (memcmp(imgpath, devpath, (size_t)len) == 0); +} + +static EFI_DEVICE_PATH * +devpath_last(EFI_DEVICE_PATH *devpath) +{ + while (!IsDevicePathEnd(NextDevicePathNode(devpath))) + devpath = NextDevicePathNode(devpath); + + return (devpath); +} + +static EFI_DEVICE_PATH * +devpath_by_type(EFI_DEVICE_PATH *devpath, UINT8 type) +{ + while (!IsDevicePathType(devpath, type) && + !IsDevicePathEnd(NextDevicePathNode(devpath))) + devpath = NextDevicePathNode(devpath); + + if (!IsDevicePathType(devpath, type)) + return (NULL); + + return (devpath); +} + +/* + * devpath_print_node is a basic output method for a devpath. + * If we switch to UEFI 2.x then we should update it to use: + * EFI_DEVICE_PATH_TO_TEXT_PROTOCOL. + */ +static int +devpath_str_node(char *buf, size_t size, EFI_DEVICE_PATH *devpath) +{ + + switch (devpath->Type) { + case MESSAGING_DEVICE_PATH: + switch (devpath->SubType) { + case MSG_ATAPI_DP: { + ATAPI_DEVICE_PATH *atapi; + + atapi = (ATAPI_DEVICE_PATH *)(void *)devpath; + return snprintf(buf, size, "ata(%s,%s,0x%x)", + (atapi->PrimarySecondary == 1) ? "Sec" : "Pri", + (atapi->SlaveMaster == 1) ? "Slave" : "Master", + atapi->Lun); + } + case MSG_USB_DP: { + USB_DEVICE_PATH *usb; + + usb = (USB_DEVICE_PATH *)devpath; + return snprintf(buf, size, "usb(0x%02x,0x%02x)", + usb->ParentPortNumber, usb->InterfaceNumber); + } + case MSG_SCSI_DP: { + SCSI_DEVICE_PATH *scsi; + + scsi = (SCSI_DEVICE_PATH *)(void *)devpath; + return snprintf(buf, size, "scsi(0x%02x,0x%02x)", + scsi->Pun, scsi->Lun); + } + default: + return snprintf(buf, size, "msg(0x%02x)", + devpath->SubType); + } + break; + case HARDWARE_DEVICE_PATH: + switch (devpath->SubType) { + case HW_PCI_DP: { + PCI_DEVICE_PATH *pci; + + pci = (PCI_DEVICE_PATH *)devpath; + return snprintf(buf, size, "pci(0x%02x,0x%02x)", + pci->Device, pci->Function); + } + default: + return snprintf(buf, size, "hw(0x%02x)", + devpath->SubType); + } + break; + case ACPI_DEVICE_PATH: { + ACPI_HID_DEVICE_PATH *acpi; + + acpi = (ACPI_HID_DEVICE_PATH *)(void *)devpath; + if ((acpi->HID & PNP_EISA_ID_MASK) == PNP_EISA_ID_CONST) { + switch (EISA_ID_TO_NUM(acpi->HID)) { + case 0x0a03: + return snprintf(buf, size, "pciroot(0x%x)", + acpi->UID); + case 0x0a08: + return snprintf(buf, size, "pcieroot(0x%x)", + acpi->UID); + case 0x0604: + return snprintf(buf, size, "floppy(0x%x)", + acpi->UID); + case 0x0301: + return snprintf(buf, size, "keyboard(0x%x)", + acpi->UID); + case 0x0501: + return snprintf(buf, size, "serial(0x%x)", + acpi->UID); + case 0x0401: + return snprintf(buf, size, "parallelport(0x%x)", + acpi->UID); + default: + return snprintf(buf, size, "acpi(pnp%04x,0x%x)", + EISA_ID_TO_NUM(acpi->HID), acpi->UID); + } + } + + return snprintf(buf, size, "acpi(0x%08x,0x%x)", acpi->HID, + acpi->UID); + } + case MEDIA_DEVICE_PATH: + switch (devpath->SubType) { + case MEDIA_CDROM_DP: { + CDROM_DEVICE_PATH *cdrom; + + cdrom = (CDROM_DEVICE_PATH *)(void *)devpath; + return snprintf(buf, size, "cdrom(%x)", + cdrom->BootEntry); + } + case MEDIA_HARDDRIVE_DP: { + HARDDRIVE_DEVICE_PATH *hd; + + hd = (HARDDRIVE_DEVICE_PATH *)(void *)devpath; + return snprintf(buf, size, "hd(%x)", + hd->PartitionNumber); + } + default: + return snprintf(buf, size, "media(0x%02x)", + devpath->SubType); + } + case BBS_DEVICE_PATH: + return snprintf(buf, size, "bbs(0x%02x)", devpath->SubType); + case END_DEVICE_PATH_TYPE: + return (0); + } + + return snprintf(buf, size, "type(0x%02x, 0x%02x)", devpath->Type, + devpath->SubType); +} + +int +devpath_strncat(char *buf, size_t size, EFI_DEVICE_PATH *devpath) +{ + size_t len, used; + const char *sep; + + sep = ""; + used = 0; + while (!IsDevicePathEnd(devpath)) { + len = snprintf(buf, size - used, "%s", sep); + used += len; + if (used > size) + return (used); + buf += len; + + len = devpath_str_node(buf, size - used, devpath); + used += len; + if (used > size) + return (used); + buf += len; + devpath = NextDevicePathNode(devpath); + sep = ":"; + } + + return (used); +} + +char * +devpath_str(EFI_DEVICE_PATH *devpath) +{ + static char buf[128]; + + devpath_strncat(buf, sizeof(buf), devpath); + + return buf; +} + /* * This function only returns if it fails to load the kernel. If it * succeeds, it simply boots the kernel. */ -void +static void try_load(const boot_module_t *mod) { size_t bufsize, cmdsize; @@ -136,8 +332,8 @@ return; } - if ((status = bs->LoadImage(TRUE, image, dev->devpath, buf, bufsize, - &loaderhandle)) != EFI_SUCCESS) { + if ((status = bs->LoadImage(TRUE, image, devpath_last(dev->devpath), + buf, bufsize, &loaderhandle)) != EFI_SUCCESS) { printf("Failed to load image provided by %s, size: %zu, (%lu)\n", mod->name, bufsize, EFI_ERROR_CODE(status)); return; @@ -172,6 +368,8 @@ efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE *Xsystab) { EFI_HANDLE *handles; + EFI_LOADED_IMAGE *img; + EFI_DEVICE_PATH *imgpath; EFI_STATUS status; EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL; SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL; @@ -254,20 +452,31 @@ /* Scan all partitions, probing with all modules. */ nhandles = hsize / sizeof(*handles); printf(" Probing %zu block devices...", nhandles); - for (i = 0; i < nhandles; i++) { - status = probe_handle(handles[i]); - switch (status) { - case EFI_UNSUPPORTED: - printf("."); - break; - case EFI_SUCCESS: - printf("+"); - break; - default: - printf("x"); - break; + DPRINTF("\n"); + + /* + * We start by probing handles which match our images media path, so if + * possible we boot from a partition on our device. + */ + status = bs->HandleProtocol(image, &LoadedImageGUID, (VOID**)&img); + imgpath = NULL; + if (status == EFI_SUCCESS) { + status = bs->HandleProtocol(img->DeviceHandle, &DevicePathGUID, + (void **)&imgpath); + if (status == EFI_SUCCESS) { + imgpath = devpath_by_type(imgpath, + MESSAGING_DEVICE_PATH); + if (imgpath != NULL) { + for (i = 0; i < nhandles; i++) + probe_handle_status(handles[i], imgpath, + TRUE); + } } } + + /* Now check for other bootable partitions. */ + for (i = 0; i < nhandles; i++) + probe_handle_status(handles[i], imgpath, FALSE); printf(" done\n"); /* Status summary. */ @@ -287,12 +496,47 @@ panic("No bootable partitions found!"); } +static void +probe_handle_status(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath, BOOLEAN match) +{ + EFI_STATUS status; + + status = probe_handle(h, imgpath, match); + + DPRINTF("probe: match: %d => ", match); + switch (status) { + case EFI_UNSUPPORTED: + printf("."); + DPRINTF(" not supported\n"); + break; + case EFI_SUCCESS: + if (match) { + printf("%c", '*'); + DPRINTF(" supported (preferred)\n"); + } else { + printf("%c", '+'); + DPRINTF(" supported\n"); + } + break; + case EFI_NOT_FOUND: + if (match) + DPRINTF("- not matched\n"); + else + DPRINTF("- already tested\n"); + break; + default: + printf("x"); + DPRINTF(" error (%lu)\n", EFI_ERROR_CODE(status)); + break; + } +} + static EFI_STATUS -probe_handle(EFI_HANDLE h) +probe_handle(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath, BOOLEAN match) { dev_info_t *devinfo; EFI_BLOCK_IO *blkio; - EFI_DEVICE_PATH *devpath; + EFI_DEVICE_PATH *devpath, *msgpath; EFI_STATUS status; UINTN i; @@ -307,8 +551,20 @@ return (status); } - while (!IsDevicePathEnd(NextDevicePathNode(devpath))) - devpath = NextDevicePathNode(devpath); + DPRINTF("probing: %s\n", devpath_str(devpath)); + + /* Check this pass needs to process this partition. */ + msgpath = devpath_by_type(devpath, MESSAGING_DEVICE_PATH); + if (msgpath != NULL) { + if (match) { + if (!paths_match(imgpath, msgpath)) + return (EFI_NOT_FOUND); + } else if (paths_match(imgpath, msgpath)) { + /* Already tested. */ + return (EFI_NOT_FOUND); + } + } else if (match) + return (EFI_NOT_FOUND); status = bs->HandleProtocol(h, &BlockIoProtocolGUID, (void **)&blkio); if (status == EFI_UNSUPPORTED) Index: sys/boot/efi/boot1/boot_module.h =================================================================== --- sys/boot/efi/boot1/boot_module.h +++ sys/boot/efi/boot1/boot_module.h @@ -36,7 +36,7 @@ #include #ifdef EFI_DEBUG -#define DPRINTF(fmt, ...) printf(fmt, __VA_ARGS__) +#define DPRINTF(fmt, args...) printf(fmt, ##args) #else #define DPRINTF(fmt, ...) {} #endif @@ -75,14 +75,14 @@ /* * load should select the best out of a set of devices that probe - * indicated were loadable and load it. + * indicated were loadable and load the specified file. * * Return codes: * EFI_SUCCESS = The module can handle the device. * EFI_NOT_FOUND = The module can not handle the device. * Other = The module encountered an error. */ - EFI_STATUS (*load)(const char *loader_path, dev_info_t **devinfo, + EFI_STATUS (*load)(const char *filepath, dev_info_t **devinfo, void **buf, size_t *bufsize); /* status outputs information about the probed devices. */ @@ -107,4 +107,6 @@ extern EFI_SYSTEM_TABLE *systab; extern EFI_BOOT_SERVICES *bs; +extern int devpath_strncat(char *buf, size_t size, EFI_DEVICE_PATH *devpath); +extern char *devpath_str(EFI_DEVICE_PATH *devpath); #endif Index: sys/boot/efi/boot1/ufs_module.c =================================================================== --- sys/boot/efi/boot1/ufs_module.c +++ sys/boot/efi/boot1/ufs_module.c @@ -93,7 +93,7 @@ } static EFI_STATUS -try_load(dev_info_t *dev, const char *loader_path, void **bufp, size_t *bufsize) +try_load(dev_info_t *dev, const char *filepath, void **bufp, size_t *bufsize) { ufs_ino_t ino; EFI_STATUS status; @@ -101,28 +101,35 @@ ssize_t read; void *buf; - if (init_dev(dev) < 0) + DPRINTF("try_load: '%s' devpath: %s\n", filepath, + devpath_str(dev->devpath)); + + if (init_dev(dev) < 0) { + DPRINTF("Failed to device %p\n", dev); return (EFI_UNSUPPORTED); + } - if ((ino = lookup(loader_path)) == 0) + if ((ino = lookup(filepath)) == 0) { + DPRINTF("Failed to lookup '%s' on %p\n", filepath, dev); return (EFI_NOT_FOUND); + } if (fsread_size(ino, NULL, 0, &size) < 0 || size <= 0) { - printf("Failed to read size of '%s' ino: %d\n", loader_path, + printf("Failed to read size of '%s' ino: %d\n", filepath, ino); return (EFI_INVALID_PARAMETER); } if ((status = bs->AllocatePool(EfiLoaderData, size, &buf)) != EFI_SUCCESS) { - printf("Failed to allocate read buffer (%lu)\n", - EFI_ERROR_CODE(status)); + printf("Failed to allocate read buffer %zu for '%s' (%lu)\n", + size, filepath, EFI_ERROR_CODE(status)); return (status); } read = fsread(ino, buf, size); if ((size_t)read != size) { - printf("Failed to read '%s' (%zd != %zu)\n", loader_path, read, + printf("Failed to read '%s' (%zd != %zu)\n", filepath, read, size); (void)bs->FreePool(buf); return (EFI_INVALID_PARAMETER); @@ -135,19 +142,14 @@ } static EFI_STATUS -load(const char *loader_path, dev_info_t **devinfop, void **buf, - size_t *bufsize) +load(const char *filepath, dev_info_t **devinfop, void **buf, size_t *bufsize) { dev_info_t *dev; - EFI_STATUS status; for (dev = devices; dev != NULL; dev = dev->next) { - status = try_load(dev, loader_path, buf, bufsize); - if (status == EFI_SUCCESS) { + if (try_load(dev, filepath, buf, bufsize) == EFI_SUCCESS) { *devinfop = dev; return (EFI_SUCCESS); - } else if (status != EFI_NOT_FOUND) { - return (status); } } Index: sys/boot/efi/boot1/zfs_module.c =================================================================== --- sys/boot/efi/boot1/zfs_module.c +++ sys/boot/efi/boot1/zfs_module.c @@ -91,7 +91,8 @@ } static EFI_STATUS -try_load(dev_info_t *devinfo, const char *loader_path, void **bufp, size_t *bufsize) +try_load(dev_info_t *devinfo, const char *filepath, void **bufp, + size_t *bufsize) { spa_t *spa; struct zfsmount zfsmount; @@ -102,32 +103,41 @@ EFI_STATUS status; spa = devinfo->devdata; - if (zfs_spa_init(spa) != 0) { - /* Init failed, don't report this loudly. */ + + DPRINTF("try_load: '%s' spa: '%s', devpath: %s\n", + filepath, spa->spa_name, devpath_str(devinfo->devpath)); + + if ((err = zfs_spa_init(spa)) != 0) { + DPRINTF("Failed to load pool '%s' (%d)\n", spa->spa_name, err); return (EFI_NOT_FOUND); } - if (zfs_mount(spa, 0, &zfsmount) != 0) { - /* Mount failed, don't report this loudly. */ + if ((err = zfs_mount(spa, 0, &zfsmount)) != 0) { + DPRINTF("Failed to mount pool '%s' (%d)\n", spa->spa_name, err); return (EFI_NOT_FOUND); } - if ((err = zfs_lookup(&zfsmount, loader_path, &dn)) != 0) { - printf("Failed to lookup %s on pool %s (%d)\n", loader_path, + if ((err = zfs_lookup(&zfsmount, filepath, &dn)) != 0) { + if (err == ENOENT) { + DPRINTF("Failed to find '%s' on pool '%s' (%d)\n", + filepath, spa->spa_name, err); + return (EFI_NOT_FOUND); + } + printf("Failed to lookup '%s' on pool '%s' (%d)\n", filepath, spa->spa_name, err); return (EFI_INVALID_PARAMETER); } if ((err = zfs_dnode_stat(spa, &dn, &st)) != 0) { - printf("Failed to lookup %s on pool %s (%d)\n", loader_path, + printf("Failed to stat '%s' on pool '%s' (%d)\n", filepath, spa->spa_name, err); return (EFI_INVALID_PARAMETER); } if ((status = bs->AllocatePool(EfiLoaderData, (UINTN)st.st_size, &buf)) != EFI_SUCCESS) { - printf("Failed to allocate load buffer for pool %s (%lu)\n", - spa->spa_name, EFI_ERROR_CODE(status)); + printf("Failed to allocate load buffer %zu for pool '%s' for '%s' " + "(%lu)\n", st.st_size, spa->spa_name, filepath, EFI_ERROR_CODE(status)); return (EFI_INVALID_PARAMETER); } @@ -145,19 +155,14 @@ } static EFI_STATUS -load(const char *loader_path, dev_info_t **devinfop, void **bufp, - size_t *bufsize) +load(const char *filepath, dev_info_t **devinfop, void **bufp, size_t *bufsize) { - dev_info_t *devinfo; - EFI_STATUS status; + dev_info_t *dev; - for (devinfo = devices; devinfo != NULL; devinfo = devinfo->next) { - status = try_load(devinfo, loader_path, bufp, bufsize); - if (status == EFI_SUCCESS) { - *devinfop = devinfo; + for (dev = devices; dev != NULL; dev = dev->next) { + if (try_load(dev, filepath, bufp, bufsize) == EFI_SUCCESS) { + *devinfop = dev; return (EFI_SUCCESS); - } else if (status != EFI_NOT_FOUND) { - return (status); } } @@ -195,5 +200,5 @@ .init = init, .probe = probe, .load = load, - .status = status + .status = status, }; Index: sys/boot/efi/include/efidevp.h =================================================================== --- sys/boot/efi/include/efidevp.h +++ sys/boot/efi/include/efidevp.h @@ -40,9 +40,7 @@ #define EFI_DP_TYPE_MASK 0x7F #define EFI_DP_TYPE_UNPACKED 0x80 -//#define END_DEVICE_PATH_TYPE 0xff #define END_DEVICE_PATH_TYPE 0x7f -//#define END_DEVICE_PATH_TYPE_UNPACKED 0x7f #define END_ENTIRE_DEVICE_PATH_SUBTYPE 0xff #define END_INSTANCE_DEVICE_PATH_SUBTYPE 0x01 @@ -56,8 +54,9 @@ #define DevicePathSubType(a) ( (a)->SubType ) #define DevicePathNodeLength(a) ( ((a)->Length[0]) | ((a)->Length[1] << 8) ) #define NextDevicePathNode(a) ( (EFI_DEVICE_PATH *) ( ((UINT8 *) (a)) + DevicePathNodeLength(a))) -//#define IsDevicePathEndType(a) ( DevicePathType(a) == END_DEVICE_PATH_TYPE_UNPACKED ) -#define IsDevicePathEndType(a) ( DevicePathType(a) == END_DEVICE_PATH_TYPE ) +#define IsDevicePathType(a, t) ( DevicePathType(a) == t ) +#define IsDevicePathEndType(a) IsDevicePathType(a, END_DEVICE_PATH_TYPE) +#define IsDevicePathMessaging(a) IsDevicePathType(a, MESSAGING_DEVICE_PATH) #define IsDevicePathEndSubType(a) ( (a)->SubType == END_ENTIRE_DEVICE_PATH_SUBTYPE ) #define IsDevicePathEnd(a) ( IsDevicePathEndType(a) && IsDevicePathEndSubType(a) ) #define IsDevicePathUnpacked(a) ( (a)->Type & EFI_DP_TYPE_UNPACKED )