Index: sys/boot/efi/include/efilib.h =================================================================== --- sys/boot/efi/include/efilib.h +++ sys/boot/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 Index: sys/boot/efi/libefi/efipart.c =================================================================== --- sys/boot/efi/libefi/efipart.c +++ sys/boot/efi/libefi/efipart.c @@ -42,6 +42,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); @@ -371,24 +372,82 @@ return (0); } +static size_t +wcslen(const CHAR16 *s) +{ + size_t len; + + for(len = 0; s[len] != '\0'; len++); + + return len; +} + +static void +efifs_dev_print(EFI_DEVICE_PATH *devpath) +{ + CHAR16 *name16; + + name16 = efi_devpath_name(devpath); + char buf[wcslen(name16) + 1]; + memset(buf, 0, sizeof buf); + cpy16to8(name16, buf, wcslen(name16)); + printf("%s\n", buf); +} + 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); part_devpath = efi_lookup_devpath(part_handle); + if (disk_devpath == NULL || part_devpath == NULL) { return (ENOENT); } - node = (HARDDRIVE_DEVICE_PATH *)efi_devpath_last_node(part_devpath); - if (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); @@ -396,8 +455,53 @@ STAILQ_INIT(&pd->pd_part); STAILQ_FOREACH(hd, &hdinfo, pd_link) { - if (efi_devpath_match(hd->pd_devpath, disk_devpath) == true) { - /* 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_devpath = part_devpath; + free(trimpath); + free(pd); + return (0); + } + free(trimpath); + + } + + /* Add the partition. */ pd->pd_handle = part_handle; pd->pd_unit = node->PartitionNumber; pd->pd_devpath = part_devpath; @@ -538,7 +642,6 @@ devpath = efi_lookup_devpath(efipart_handles[i]); if (devpath == NULL) continue; - if ((node = efi_devpath_last_node(devpath)) == NULL) continue; if (efipart_floppy(node) != NULL) @@ -549,21 +652,47 @@ 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_HARDDRIVE_DP) { 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) @@ -571,6 +700,7 @@ if (DevicePathType(node) == MEDIA_DEVICE_PATH && DevicePathSubType(node) == MEDIA_HARDDRIVE_DP) continue; + efipart_hdinfo_add(handle, efipart_handles[i]); continue; } @@ -658,18 +788,6 @@ pd_dev.d_slice = -1; pd_dev.d_partition = -1; pd_dev.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() */ - ret = 0; - } } else { if ((ret = pager_output("\n")) != 0) break; @@ -696,6 +814,49 @@ 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->d_dev); + if (pdi == NULL) { + return (EINVAL); + } + + STAILQ_FOREACH(curr, pdi, pd_link) { + if (curr->pd_unit == dev->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. + */ + STAILQ_FOREACH(curr, &pd->pd_part, pd_link) { + if (curr->pd_unit == dev->d_slice) { + *pp = curr; + break; + } + } + + if (*pp == NULL) + return (ENOENT); + + return (0); +} + static int efipart_open(struct open_file *f, ...) { @@ -704,48 +865,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->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); } @@ -754,14 +901,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) { @@ -769,8 +915,7 @@ bcache_free(pd->pd_bcache); pd->pd_bcache = NULL; } - if (dev->d_dev->dv_type == DEVT_DISK) - return (disk_close(dev)); + return (0); } @@ -779,21 +924,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->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: @@ -859,14 +996,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) @@ -896,12 +1032,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) @@ -911,20 +1047,7 @@ return (EIO); off = blk * 512; - /* - * Get disk blocks, this value is either for whole disk or for - * partition. - */ - disk_blocks = 0; - if (dev->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) {