Index: stand/efi/include/efilib.h =================================================================== --- stand/efi/include/efilib.h +++ stand/efi/include/efilib.h @@ -30,6 +30,9 @@ #ifndef _LOADER_EFILIB_H #define _LOADER_EFILIB_H +#define FREEBSD_GELI_GUID { 0x516e7cbc, 0x6ecf, 0x11d6, \ + {0x8f, 0xf8, 0x00, 0x02, 0x2d, 0x09, 0x71, 0x2b } } + #include #include #include @@ -52,6 +55,7 @@ { STAILQ_ENTRY(pdinfo) pd_link; /* link in device list */ pdinfo_list_t pd_part; /* list of partitions */ + EFI_HANDLE pd_basehandle; EFI_HANDLE pd_handle; EFI_HANDLE pd_alias; EFI_DEVICE_PATH *pd_devpath; Index: stand/efi/libefi/efipart.c =================================================================== --- stand/efi/libefi/efipart.c +++ stand/efi/libefi/efipart.c @@ -43,6 +43,7 @@ #include static EFI_GUID blkio_guid = BLOCK_IO_PROTOCOL; +static EFI_GUID FreeBSDGELIGUID = FREEBSD_GELI_GUID; static int efipart_initfd(void); static int efipart_initcd(void); @@ -299,7 +300,7 @@ } /* - * We assume the block size 512 or greater power of 2. + * We assume the block size 512 or greater power of 2. * iPXE is known to insert stub BLOCK IO device with * BlockSize 1. */ @@ -490,13 +491,16 @@ static int efipart_hdinfo_add(EFI_HANDLE disk_handle, EFI_HANDLE part_handle) { - EFI_DEVICE_PATH *disk_devpath, *part_devpath; + EFI_DEVICE_PATH *disk_devpath, *part_devpath, *trimpath, *lastnode; + VENDOR_DEVICE_PATH *vendornode; HARDDRIVE_DEVICE_PATH *node; int unit; - pdinfo_t *hd, *pd, *last; + pdinfo_t *hd, *pd, *pp, *last; disk_devpath = efi_lookup_devpath(disk_handle); - if (disk_devpath == NULL) + part_devpath = efi_lookup_devpath(part_handle); + + if (disk_devpath == NULL || part_devpath == NULL) return (ENOENT); if (part_handle != NULL) { @@ -512,7 +516,45 @@ node = NULL; } + /* Get the disk partition node */ + lastnode = efi_devpath_last_node(part_devpath); + if (lastnode == NULL) + return (ENOENT); /* This should not happen. */ + + + if (DevicePathType(lastnode) == MEDIA_DEVICE_PATH) { + if (DevicePathSubType(lastnode) == MEDIA_VENDOR_DP) { + vendornode = (VENDOR_DEVICE_PATH *)lastnode; + + /* We only want GELI partitions */ + if (memcmp(&(vendornode->Guid), &FreeBSDGELIGUID, + sizeof(EFI_GUID))) { + return (EINVAL); + } + + /* Trim off the vendor node */ + trimpath = efi_devpath_trim(part_devpath); + + if (trimpath == NULL) + return (ENOENT); + + lastnode = efi_devpath_last_node(trimpath); + + if (lastnode == NULL) + return (ENOENT); + + node = (HARDDRIVE_DEVICE_PATH *)lastnode; + } else if (DevicePathSubType(lastnode) == MEDIA_HARDDRIVE_DP) { + node = (HARDDRIVE_DEVICE_PATH *)lastnode; + } + else + return (EINVAL); + } else { + return (EINVAL); + } + pd = calloc(1, sizeof(pdinfo_t)); + if (pd == NULL) { printf("Failed to add disk, out of memory\n"); return (ENOMEM); @@ -520,12 +562,56 @@ STAILQ_INIT(&pd->pd_part); STAILQ_FOREACH(hd, &hdinfo, pd_link) { - if (efi_devpath_match(hd->pd_devpath, disk_devpath) == true) { - if (part_devpath == NULL) - return (0); - - /* Add the partition. */ + if (efi_devpath_match(hd->pd_devpath, disk_devpath) != 0) { + /* Check if there's a related device entry + * already here. + */ + STAILQ_FOREACH(pp, &hd->pd_part, pd_link) { + /* If the new device is a subpath of + * an existing device, then the + * existing entry subsumes the new + * one. + */ + trimpath = efi_devpath_trim(pp->pd_devpath); + + if (trimpath == NULL) + return (ENOMEM); + + if (efi_devpath_match(trimpath, + part_devpath) != 0) { + free(trimpath); + free(pd); + return (EBUSY); + } + + /* If the existing device path is a + * subpath of the new one, then the + * new entry subsumes the existing + * one. + */ + free(trimpath); + + trimpath = efi_devpath_trim(part_devpath); + + if (trimpath == NULL) + return (ENOMEM); + + if (efi_devpath_match(trimpath, + pp->pd_devpath) != 0) { + pp->pd_handle = part_handle; + pp->pd_basehandle = disk_handle; + pp->pd_devpath = part_devpath; + free(trimpath); + free(pd); + return (0); + } + free(trimpath); + + } + + /* Add the partition. */ pd->pd_handle = part_handle; + pd->pd_basehandle = disk_handle; pd->pd_unit = node->PartitionNumber; pd->pd_devpath = part_devpath; pd->pd_parent = hd; @@ -544,6 +630,7 @@ /* Add the disk. */ hd = pd; hd->pd_handle = disk_handle; + hd->pd_basehandle = disk_handle; hd->pd_unit = unit; hd->pd_devpath = disk_devpath; hd->pd_parent = NULL; @@ -562,6 +649,7 @@ /* Add the partition. */ pd->pd_handle = part_handle; + pd->pd_basehandle = disk_handle; pd->pd_unit = node->PartitionNumber; pd->pd_devpath = part_devpath; pd->pd_parent = hd; @@ -622,6 +710,7 @@ */ if (p == NULL) { /* no colon, add the disk */ pd->pd_handle = disk_handle; + pd->pd_basehandle = disk_handle; pd->pd_unit = unit; pd->pd_devpath = devpath; pd->pd_parent = NULL; @@ -653,7 +742,8 @@ return (EINVAL); } /* Add the partition. */ - pd->pd_handle = disk_handle; + pd->pd_handle = disk_handle; + pd->pd_basehandle = disk_handle; pd->pd_unit = unit; pd->pd_devpath = devpath; pd->pd_parent = last; @@ -677,7 +767,6 @@ devpath = efi_lookup_devpath(efipart_handles[i]); if (devpath == NULL) continue; - if ((node = efi_devpath_last_node(devpath)) == NULL) continue; @@ -689,6 +778,29 @@ if (EFI_ERROR(status)) continue; + /* Handle GELI volumes */ + if (DevicePathType(node) == MEDIA_DEVICE_PATH && + DevicePathSubType(node) == MEDIA_VENDOR_DP) { + VENDOR_DEVICE_PATH *vendornode; + + vendornode = (VENDOR_DEVICE_PATH *)node; + + /* We only want GELI partitions */ + if (memcmp(&(vendornode->Guid), &FreeBSDGELIGUID, + sizeof(EFI_GUID))) { + continue; + } + + /* Trim off the vendor node */ + devpathcpy = efi_devpath_trim(devpath); + if (devpathcpy == NULL) + continue; + if ((node = efi_devpath_last_node(devpathcpy)) == NULL) + continue; + + devpath = devpathcpy; + } + if (DevicePathType(node) == MEDIA_DEVICE_PATH && DevicePathSubType(node) == MEDIA_FILEPATH_DP) { efipart_hdinfo_add_filepath(efipart_handles[i]); @@ -700,16 +812,19 @@ devpathcpy = efi_devpath_trim(devpath); if (devpathcpy == NULL) continue; + tmpdevpath = devpathcpy; status = BS->LocateDevicePath(&blkio_guid, &tmpdevpath, &handle); free(devpathcpy); if (EFI_ERROR(status)) continue; + /* * We do not support nested partitions. */ devpathcpy = efi_lookup_devpath(handle); + if (devpathcpy == NULL) continue; if ((node = efi_devpath_last_node(devpathcpy)) == NULL) @@ -745,7 +860,8 @@ int ret = 0; EFI_BLOCK_IO *blkio; EFI_STATUS status; - EFI_HANDLE h; + EFI_HANDLE handle; + EFI_HANDLE base_handle; pdinfo_t *pd; CHAR16 *text; struct disk_devdesc pd_dev; @@ -759,9 +875,10 @@ return (ret); STAILQ_FOREACH(pd, pdlist, pd_link) { - h = pd->pd_handle; + handle = pd->pd_handle; + base_handle = pd->pd_basehandle; if (verbose) { /* Output the device path. */ - text = efi_devpath_name(efi_lookup_devpath(h)); + text = efi_devpath_name(efi_lookup_devpath(base_handle)); if (text != NULL) { printf(" %S", text); efi_free_devpath_name(text); @@ -772,7 +889,8 @@ snprintf(line, sizeof(line), " %s%d", dev->dv_name, pd->pd_unit); printf("%s:", line); - status = BS->HandleProtocol(h, &blkio_guid, (void **)&blkio); + status = BS->HandleProtocol(base_handle, &blkio_guid, + (void **)&blkio); if (!EFI_ERROR(status)) { printf(" %llu", blkio->Media->LastBlock == 0? 0: @@ -797,19 +915,22 @@ pd_dev.dd.d_unit = pd->pd_unit; pd_dev.d_slice = -1; pd_dev.d_partition = -1; - ret = disk_open(&pd_dev, blkio->Media->BlockSize * - (blkio->Media->LastBlock + 1), - blkio->Media->BlockSize); - if (ret == 0) { - ret = disk_print(&pd_dev, line, verbose); - disk_close(&pd_dev); - if (ret != 0) - return (ret); - } else { - /* Do not fail from disk_open() */ - ret = 0; - } - } else { + pd_dev.dd.d_opendata = blkio; + + ret = disk_open(&pd_dev, blkio->Media->BlockSize * + (blkio->Media->LastBlock + 1), + blkio->Media->BlockSize); + if (ret == 0) { + ret = disk_print(&pd_dev, line, verbose); + disk_close(&pd_dev); + if (ret != 0) + return (ret); + } else { + /* Do not fail from disk_open() */ + printf("Open device failed %d\n", ret); + ret = 0; + } + } else { if ((ret = pager_output("\n")) != 0) break; } @@ -835,6 +956,53 @@ return (efipart_print_common(&efipart_hddev, &hdinfo, verbose)); } +static int +efipart_lookupdev(struct disk_devdesc *dev, pdinfo_t **pp) +{ + pdinfo_list_t *pdi; + pdinfo_t *pd = NULL, *curr; + + if (dev == NULL) { + return (EINVAL); + } + + pdi = efiblk_get_pdinfo_list(dev->dd.d_dev); + if (pdi == NULL) { + return (EINVAL); + } + + STAILQ_FOREACH(curr, pdi, pd_link) { + if (curr->pd_unit == dev->dd.d_unit) { + pd = curr; + break; + } + } + + if (pd == NULL) + return (ENOENT); + + *pp = NULL; + + /* If we're looking up a specific partition, get the + * IO interface from that devide handle. + */ + if (dev->d_slice != -1) { + STAILQ_FOREACH(curr, &pd->pd_part, pd_link) { + if (curr->pd_unit == dev->d_slice) { + *pp = curr; + break; + } + } + } else { + *pp = pd; + } + + if (*pp == NULL) + return (ENOENT); + + return (0); +} + static int efipart_open(struct open_file *f, ...) { @@ -843,48 +1011,34 @@ pdinfo_t *pd; EFI_BLOCK_IO *blkio; EFI_STATUS status; + int err; va_start(args, f); dev = va_arg(args, struct disk_devdesc*); va_end(args); - if (dev == NULL) - return (EINVAL); - pd = efiblk_get_pdinfo((struct devdesc *)dev); - if (pd == NULL) - return (EIO); + if ((err = efipart_lookupdev(dev, &pd)) != 0) { + return (err); + } if (pd->pd_blkio == NULL) { - status = BS->HandleProtocol(pd->pd_handle, &blkio_guid, - (void **)&pd->pd_blkio); - if (EFI_ERROR(status)) - return (efi_status_to_errno(status)); + status = BS->HandleProtocol(pd->pd_handle, &blkio_guid, + (void **)&pd->pd_blkio); + if (EFI_ERROR(status)) + return (efi_status_to_errno(status)); } blkio = pd->pd_blkio; - if (!blkio->Media->MediaPresent) + if (!blkio->Media->MediaPresent) { return (EAGAIN); + } pd->pd_open++; if (pd->pd_bcache == NULL) pd->pd_bcache = bcache_allocate(); - if (dev->dd.d_dev->dv_type == DEVT_DISK) { - int rc; - - rc = disk_open(dev, - blkio->Media->BlockSize * (blkio->Media->LastBlock + 1), - blkio->Media->BlockSize); - if (rc != 0) { - pd->pd_open--; - if (pd->pd_open == 0) { - pd->pd_blkio = NULL; - bcache_free(pd->pd_bcache); - pd->pd_bcache = NULL; - } - } - return (rc); - } + dev->d_offset = 0; + return (0); } @@ -893,14 +1047,13 @@ { struct disk_devdesc *dev; pdinfo_t *pd; + int err; dev = (struct disk_devdesc *)(f->f_devdata); - if (dev == NULL) - return (EINVAL); - pd = efiblk_get_pdinfo((struct devdesc *)dev); - if (pd == NULL) - return (EINVAL); + if ((err = efipart_lookupdev(dev, &pd)) != 0) { + return (err); + } pd->pd_open--; if (pd->pd_open == 0) { @@ -908,8 +1061,7 @@ bcache_free(pd->pd_bcache); pd->pd_bcache = NULL; } - if (dev->dd.d_dev->dv_type == DEVT_DISK) - return (disk_close(dev)); + return (0); } @@ -918,21 +1070,13 @@ { struct disk_devdesc *dev; pdinfo_t *pd; - int rc; + int err; dev = (struct disk_devdesc *)(f->f_devdata); - if (dev == NULL) - return (EINVAL); - - pd = efiblk_get_pdinfo((struct devdesc *)dev); - if (pd == NULL) - return (EINVAL); - if (dev->dd.d_dev->dv_type == DEVT_DISK) { - rc = disk_ioctl(dev, cmd, data); - if (rc != ENOTTY) - return (rc); - } + if ((err = efipart_lookupdev(dev, &pd)) != 0) { + return (err); + } switch (cmd) { case DIOCGSECTORSIZE: @@ -998,14 +1142,13 @@ struct bcache_devdata bcd; struct disk_devdesc *dev; pdinfo_t *pd; + int err; dev = (struct disk_devdesc *)devdata; - if (dev == NULL) - return (EINVAL); - pd = efiblk_get_pdinfo((struct devdesc *)dev); - if (pd == NULL) - return (EINVAL); + if ((err = efipart_lookupdev(dev, &pd)) != 0) { + return (err); + } if (pd->pd_blkio->Media->RemovableMedia && !pd->pd_blkio->Media->MediaPresent) @@ -1039,12 +1182,12 @@ int error; size_t diskend, readstart; - if (dev == NULL || blk < 0) + if (blk < 0) return (EINVAL); - pd = efiblk_get_pdinfo((struct devdesc *)dev); - if (pd == NULL) - return (EINVAL); + if ((error = efipart_lookupdev(dev, &pd)) != 0) { + return (error); + } blkio = pd->pd_blkio; if (blkio == NULL) @@ -1054,20 +1197,7 @@ return (EIO); off = blk * 512; - /* - * Get disk blocks, this value is either for whole disk or for - * partition. - */ - disk_blocks = 0; - if (dev->dd.d_dev->dv_type == DEVT_DISK) { - if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks) == 0) { - /* DIOCGMEDIASIZE does return bytes. */ - disk_blocks /= blkio->Media->BlockSize; - } - d_offset = dev->d_offset; - } - if (disk_blocks == 0) - disk_blocks = blkio->Media->LastBlock + 1 - d_offset; + disk_blocks = blkio->Media->LastBlock + 1 - d_offset; /* make sure we don't read past disk end */ if ((off + size) / blkio->Media->BlockSize > d_offset + disk_blocks) {