loader: Replace EFI part devices.

Rewrite EFI part device interface to present disk devices in more
user friendly way.

We keep list of three types of devices: floppy, cd and disk, the
visible names: fdX: cdX: and diskX:

Use common/disk.c and common/part.c interfaces to manage the
partitioning.

The lsdev -l will additionally list the device path.

Reviewed by:	imp, allanjude
Approved by:	imp (mentor), allanjude (mentor)
Differential Revision:	https://reviews.freebsd.org/D8581


git-svn-id: svn+ssh://svn.freebsd.org/base/head@313333 ccf9f872-aa2e-dd11-9fc8-001c23d0bc1f
This commit is contained in:
tsoome 2017-02-06 09:18:47 +00:00
parent 3fd6269950
commit 23bf6bc1ed
8 changed files with 869 additions and 197 deletions

View File

@ -168,6 +168,7 @@ struct devdesc
#define DEVT_NET 2
#define DEVT_CD 3
#define DEVT_ZFS 4
#define DEVT_FD 5
int d_unit;
void *d_opendata;
};

View File

@ -31,16 +31,37 @@
#define _LOADER_EFILIB_H
#include <stand.h>
#include <sys/queue.h>
extern EFI_HANDLE IH;
extern EFI_SYSTEM_TABLE *ST;
extern EFI_BOOT_SERVICES *BS;
extern EFI_RUNTIME_SERVICES *RS;
extern struct devsw efipart_dev;
extern struct devsw efipart_fddev;
extern struct devsw efipart_cddev;
extern struct devsw efipart_hddev;
extern struct devsw efinet_dev;
extern struct netif_driver efinetif;
/* EFI block device data, included here to help efi_zfs_probe() */
typedef STAILQ_HEAD(pdinfo_list, pdinfo) pdinfo_list_t;
typedef struct pdinfo
{
STAILQ_ENTRY(pdinfo) pd_link; /* link in device list */
pdinfo_list_t pd_part; /* link of partitions */
EFI_HANDLE pd_handle;
EFI_HANDLE pd_alias;
EFI_DEVICE_PATH *pd_devpath;
EFI_BLOCK_IO *pd_blkio;
int pd_unit; /* unit number */
int pd_open; /* reference counter */
void *pd_bcache; /* buffer cache data */
} pdinfo_t;
pdinfo_list_t *efiblk_get_pdinfo_list(struct devsw *dev);
void *efi_get_table(EFI_GUID *tbl);
int efi_register_handles(struct devsw *, EFI_HANDLE *, EFI_HANDLE *, int);
@ -53,6 +74,7 @@ EFI_DEVICE_PATH *efi_lookup_devpath(EFI_HANDLE);
EFI_HANDLE efi_devpath_handle(EFI_DEVICE_PATH *);
EFI_DEVICE_PATH *efi_devpath_last_node(EFI_DEVICE_PATH *);
EFI_DEVICE_PATH *efi_devpath_trim(EFI_DEVICE_PATH *);
int efi_devpath_match(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *);
CHAR16 *efi_devpath_name(EFI_DEVICE_PATH *);
void efi_free_devpath_name(CHAR16 *);

View File

@ -138,3 +138,31 @@ efi_devpath_handle(EFI_DEVICE_PATH *devpath)
return (NULL);
return (h);
}
int
efi_devpath_match(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2)
{
int len;
if (devpath1 == NULL || devpath2 == NULL)
return (0);
while (1) {
if (DevicePathType(devpath1) != DevicePathType(devpath2) ||
DevicePathSubType(devpath1) != DevicePathSubType(devpath2))
return (0);
len = DevicePathNodeLength(devpath1);
if (len != DevicePathNodeLength(devpath2))
return (0);
if (memcmp(devpath1, devpath2, (size_t)len) != 0)
return (0);
if (IsDevicePathEnd(devpath1))
break;
devpath1 = NextDevicePathNode(devpath1);
devpath2 = NextDevicePathNode(devpath2);
}
return (1);
}

View File

@ -27,8 +27,10 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/disk.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/queue.h>
#include <stddef.h>
#include <stdarg.h>
@ -37,57 +39,110 @@ __FBSDID("$FreeBSD$");
#include <efi.h>
#include <efilib.h>
#include <efiprot.h>
#include <disk.h>
static EFI_GUID blkio_guid = BLOCK_IO_PROTOCOL;
static int efipart_init(void);
static int efipart_initfd(void);
static int efipart_initcd(void);
static int efipart_inithd(void);
static int efipart_strategy(void *, int, daddr_t, size_t, char *, size_t *);
static int efipart_realstrategy(void *, int, daddr_t, size_t, char *, size_t *);
static int efipart_open(struct open_file *, ...);
static int efipart_close(struct open_file *);
static int efipart_print(int);
static int efipart_ioctl(struct open_file *, u_long, void *);
struct devsw efipart_dev = {
.dv_name = "part",
.dv_type = DEVT_DISK,
.dv_init = efipart_init,
static int efipart_printfd(int);
static int efipart_printcd(int);
static int efipart_printhd(int);
struct devsw efipart_fddev = {
.dv_name = "fd",
.dv_type = DEVT_FD,
.dv_init = efipart_initfd,
.dv_strategy = efipart_strategy,
.dv_open = efipart_open,
.dv_close = efipart_close,
.dv_ioctl = noioctl,
.dv_print = efipart_print,
.dv_ioctl = efipart_ioctl,
.dv_print = efipart_printfd,
.dv_cleanup = NULL
};
/*
* info structure to support bcache
*/
struct pdinfo {
int pd_unit; /* unit number */
int pd_open; /* reference counter */
void *pd_bcache; /* buffer cache data */
struct devsw efipart_cddev = {
.dv_name = "cd",
.dv_type = DEVT_CD,
.dv_init = efipart_initcd,
.dv_strategy = efipart_strategy,
.dv_open = efipart_open,
.dv_close = efipart_close,
.dv_ioctl = efipart_ioctl,
.dv_print = efipart_printcd,
.dv_cleanup = NULL
};
static struct pdinfo *pdinfo;
static int npdinfo = 0;
#define PD(dev) (pdinfo[(dev)->d_unit])
struct devsw efipart_hddev = {
.dv_name = "disk",
.dv_type = DEVT_DISK,
.dv_init = efipart_inithd,
.dv_strategy = efipart_strategy,
.dv_open = efipart_open,
.dv_close = efipart_close,
.dv_ioctl = efipart_ioctl,
.dv_print = efipart_printhd,
.dv_cleanup = NULL
};
static pdinfo_list_t fdinfo;
static pdinfo_list_t cdinfo;
static pdinfo_list_t hdinfo;
static EFI_HANDLE *efipart_handles = NULL;
static UINTN efipart_nhandles = 0;
static pdinfo_t *
efiblk_get_pdinfo(pdinfo_list_t *pdi, int unit)
{
pdinfo_t *pd;
STAILQ_FOREACH(pd, pdi, pd_link) {
if (pd->pd_unit == unit)
return (pd);
}
return (NULL);
}
static int
efipart_init(void)
efiblk_pdinfo_count(pdinfo_list_t *pdi)
{
pdinfo_t *pd;
int i = 0;
STAILQ_FOREACH(pd, pdi, pd_link) {
i++;
}
return (i);
}
static int
efipart_inithandles(void)
{
EFI_BLOCK_IO *blkio;
EFI_DEVICE_PATH *devpath, *devpathcpy, *tmpdevpath, *node;
EFI_HANDLE *hin, *hout, *aliases, handle;
EFI_STATUS status;
UINTN sz;
u_int n, nin, nout, nrdisk;
int err;
EFI_HANDLE *hin;
EFI_STATUS status;
if (efipart_nhandles != 0) {
free(efipart_handles);
efipart_handles = NULL;
efipart_nhandles = 0;
}
sz = 0;
hin = NULL;
status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, 0);
status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, hin);
if (status == EFI_BUFFER_TOO_SMALL) {
hin = (EFI_HANDLE *)malloc(sz * 3);
hin = malloc(sz);
status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz,
hin);
if (EFI_ERROR(status))
@ -96,33 +151,150 @@ efipart_init(void)
if (EFI_ERROR(status))
return (efi_status_to_errno(status));
/* Filter handles to only include FreeBSD partitions. */
nin = sz / sizeof(EFI_HANDLE);
hout = hin + nin;
aliases = hout + nin;
nout = 0;
nrdisk = 0;
efipart_handles = hin;
efipart_nhandles = sz;
return (0);
}
bzero(aliases, nin * sizeof(EFI_HANDLE));
pdinfo = malloc(nin * sizeof(*pdinfo));
if (pdinfo == NULL)
return (ENOMEM);
static ACPI_HID_DEVICE_PATH *
efipart_floppy(EFI_DEVICE_PATH *node)
{
ACPI_HID_DEVICE_PATH *acpi = NULL;
for (n = 0; n < nin; n++) {
devpath = efi_lookup_devpath(hin[n]);
if (devpath == NULL) {
continue;
if (DevicePathType(node) == ACPI_DEVICE_PATH &&
DevicePathSubType(node) == ACPI_DP) {
acpi = (ACPI_HID_DEVICE_PATH *) node;
if (acpi->HID == EISA_PNP_ID(0x604) ||
acpi->HID == EISA_PNP_ID(0x700) ||
acpi->HID == EISA_ID(0x41d1, 0x701)) {
return (acpi);
}
}
return (acpi);
}
status = BS->HandleProtocol(hin[n], &blkio_guid,
(void**)&blkio);
/*
* Add or update entries with new handle data.
*/
static int
efipart_fdinfo_add(EFI_HANDLE handle, uint32_t uid, EFI_DEVICE_PATH *devpath)
{
pdinfo_t *fd;
fd = malloc(sizeof(pdinfo_t));
if (fd == NULL) {
printf("Failed to register floppy %d, out of memory\n", uid);
return (ENOMEM);
}
memset(fd, 0, sizeof(pdinfo_t));
STAILQ_INIT(&fd->pd_part);
fd->pd_unit = uid;
fd->pd_handle = handle;
fd->pd_devpath = devpath;
STAILQ_INSERT_TAIL(&fdinfo, fd, pd_link);
return (0);
}
static void
efipart_updatefd(void)
{
EFI_DEVICE_PATH *devpath, *node;
ACPI_HID_DEVICE_PATH *acpi;
int i, nin;
nin = efipart_nhandles / sizeof (*efipart_handles);
for (i = 0; i < nin; i++) {
devpath = efi_lookup_devpath(efipart_handles[i]);
if (devpath == NULL)
continue;
if ((node = efi_devpath_last_node(devpath)) == NULL)
continue;
if ((acpi = efipart_floppy(node)) != NULL) {
efipart_fdinfo_add(efipart_handles[i], acpi->UID,
devpath);
}
}
}
static int
efipart_initfd(void)
{
int rv;
rv = efipart_inithandles();
if (rv != 0)
return (rv);
STAILQ_INIT(&fdinfo);
efipart_updatefd();
bcache_add_dev(efiblk_pdinfo_count(&fdinfo));
return (0);
}
/*
* Add or update entries with new handle data.
*/
static int
efipart_cdinfo_add(EFI_HANDLE handle, EFI_HANDLE alias,
EFI_DEVICE_PATH *devpath)
{
int unit;
pdinfo_t *cd;
pdinfo_t *pd;
unit = 0;
STAILQ_FOREACH(pd, &cdinfo, pd_link) {
if (efi_devpath_match(pd->pd_devpath, devpath) != 0) {
pd->pd_handle = handle;
pd->pd_alias = alias;
return (0);
}
unit++;
}
cd = malloc(sizeof(pdinfo_t));
if (cd == NULL) {
printf("Failed to add cd %d, out of memory\n", unit);
return (ENOMEM);
}
memset(cd, 0, sizeof(pdinfo_t));
STAILQ_INIT(&cd->pd_part);
cd->pd_handle = handle;
cd->pd_unit = unit;
cd->pd_alias = alias;
cd->pd_devpath = devpath;
STAILQ_INSERT_TAIL(&cdinfo, cd, pd_link);
return (0);
}
static void
efipart_updatecd(void)
{
int i, nin;
EFI_DEVICE_PATH *devpath, *devpathcpy, *tmpdevpath, *node;
EFI_HANDLE handle;
EFI_BLOCK_IO *blkio;
EFI_STATUS status;
nin = efipart_nhandles / sizeof (*efipart_handles);
for (i = 0; i < nin; i++) {
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)
continue;
status = BS->HandleProtocol(efipart_handles[i],
&blkio_guid, (void **)&blkio);
if (EFI_ERROR(status))
continue;
if (!blkio->Media->LogicalPartition) {
nrdisk++;
continue;
}
/*
* If we come across a logical partition of subtype CDROM
* it doesn't refer to the CD filesystem itself, but rather
@ -130,8 +302,6 @@ efipart_init(void)
* we try to find the parent device and add that instead as
* that will be the CD filesystem.
*/
if ((node = efi_devpath_last_node(devpath)) == NULL)
continue;
if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
DevicePathSubType(node) == MEDIA_CDROM_DP) {
devpathcpy = efi_devpath_trim(devpath);
@ -143,109 +313,400 @@ efipart_init(void)
free(devpathcpy);
if (EFI_ERROR(status))
continue;
hout[nout] = handle;
aliases[nout] = hin[n];
} else
hout[nout] = hin[n];
nout++;
pdinfo[npdinfo].pd_open = 0;
pdinfo[npdinfo].pd_bcache = NULL;
pdinfo[npdinfo].pd_unit = npdinfo;
npdinfo++;
devpath = efi_lookup_devpath(handle);
efipart_cdinfo_add(handle, efipart_handles[i],
devpath);
continue;
}
if (DevicePathType(node) == MESSAGING_DEVICE_PATH &&
DevicePathSubType(node) == MSG_ATAPI_DP) {
efipart_cdinfo_add(efipart_handles[i], NULL,
devpath);
continue;
}
/* USB or SATA cd without the media. */
if (blkio->Media->RemovableMedia &&
!blkio->Media->MediaPresent) {
efipart_cdinfo_add(efipart_handles[i], NULL,
devpath);
}
}
bcache_add_dev(npdinfo);
err = efi_register_handles(&efipart_dev, hout, aliases, nout);
free(hin);
if (nout == 0 && nrdisk > 0)
printf("Found %d disk(s) but no logical partition\n", nrdisk);
return (err);
}
static int
efipart_print(int verbose)
efipart_initcd(void)
{
char line[80];
EFI_BLOCK_IO *blkio;
EFI_HANDLE h;
EFI_STATUS status;
u_int unit;
int ret = 0;
int rv;
printf("%s devices:", efipart_dev.dv_name);
rv = efipart_inithandles();
if (rv != 0)
return (rv);
STAILQ_INIT(&cdinfo);
efipart_updatecd();
bcache_add_dev(efiblk_pdinfo_count(&cdinfo));
return (0);
}
static int
efipart_hdinfo_add(EFI_HANDLE disk_handle, EFI_HANDLE part_handle)
{
EFI_DEVICE_PATH *disk_devpath, *part_devpath;
HARDDRIVE_DEVICE_PATH *node;
int unit;
pdinfo_t *hd, *pd, *last;
disk_devpath = efi_lookup_devpath(disk_handle);
part_devpath = efi_lookup_devpath(part_handle);
if (disk_devpath == NULL || part_devpath == NULL) {
return (ENOENT);
}
pd = malloc(sizeof(pdinfo_t));
if (pd == NULL) {
printf("Failed to add disk, out of memory\n");
return (ENOMEM);
}
memset(pd, 0, sizeof(pdinfo_t));
STAILQ_INIT(&pd->pd_part);
node = (HARDDRIVE_DEVICE_PATH *)efi_devpath_last_node(part_devpath);
STAILQ_FOREACH(hd, &hdinfo, pd_link) {
if (efi_devpath_match(hd->pd_devpath, disk_devpath) != 0) {
/* Add the partition. */
pd->pd_handle = part_handle;
pd->pd_unit = node->PartitionNumber;
pd->pd_devpath = part_devpath;
STAILQ_INSERT_TAIL(&hd->pd_part, pd, pd_link);
return (0);
}
}
last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
if (last != NULL)
unit = last->pd_unit + 1;
else
unit = 0;
/* Add the disk. */
hd = pd;
hd->pd_handle = disk_handle;
hd->pd_unit = unit;
hd->pd_devpath = disk_devpath;
STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link);
pd = malloc(sizeof(pdinfo_t));
if (pd == NULL) {
printf("Failed to add partition, out of memory\n");
return (ENOMEM);
}
memset(pd, 0, sizeof(pdinfo_t));
STAILQ_INIT(&pd->pd_part);
/* Add the partition. */
pd->pd_handle = part_handle;
pd->pd_unit = node->PartitionNumber;
pd->pd_devpath = part_devpath;
STAILQ_INSERT_TAIL(&hd->pd_part, pd, pd_link);
return (0);
}
static void
efipart_updatehd(void)
{
int i, nin;
EFI_DEVICE_PATH *devpath, *devpathcpy, *tmpdevpath, *node;
EFI_HANDLE handle;
EFI_BLOCK_IO *blkio;
EFI_STATUS status;
nin = efipart_nhandles / sizeof (*efipart_handles);
for (i = 0; i < nin; i++) {
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)
continue;
status = BS->HandleProtocol(efipart_handles[i],
&blkio_guid, (void **)&blkio);
if (EFI_ERROR(status))
continue;
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)
continue;
if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
DevicePathSubType(node) == MEDIA_HARDDRIVE_DP)
continue;
efipart_hdinfo_add(handle, efipart_handles[i]);
continue;
}
}
}
static int
efipart_inithd(void)
{
int rv;
rv = efipart_inithandles();
if (rv != 0)
return (rv);
STAILQ_INIT(&hdinfo);
efipart_updatehd();
bcache_add_dev(efiblk_pdinfo_count(&hdinfo));
return (0);
}
static int
efipart_print_common(struct devsw *dev, pdinfo_list_t *pdlist, int verbose)
{
int ret = 0;
EFI_BLOCK_IO *blkio;
EFI_STATUS status;
EFI_HANDLE h;
pdinfo_t *pd;
CHAR16 *text;
struct disk_devdesc pd_dev;
char line[80];
if (STAILQ_EMPTY(pdlist))
return (0);
printf("%s devices:", dev->dv_name);
if ((ret = pager_output("\n")) != 0)
return (ret);
for (unit = 0, h = efi_find_handle(&efipart_dev, 0);
h != NULL; h = efi_find_handle(&efipart_dev, ++unit)) {
snprintf(line, sizeof(line), " %s%d:",
efipart_dev.dv_name, unit);
if ((ret = pager_output(line)) != 0)
break;
STAILQ_FOREACH(pd, pdlist, pd_link) {
h = pd->pd_handle;
if (verbose) { /* Output the device path. */
text = efi_devpath_name(efi_lookup_devpath(h));
if (text != NULL) {
printf(" %S", text);
efi_free_devpath_name(text);
if ((ret = pager_output("\n")) != 0)
break;
}
}
snprintf(line, sizeof(line),
" %s%d", dev->dv_name, pd->pd_unit);
printf("%s:", line);
status = BS->HandleProtocol(h, &blkio_guid, (void **)&blkio);
if (!EFI_ERROR(status)) {
snprintf(line, sizeof(line), " %llu blocks",
(unsigned long long)(blkio->Media->LastBlock + 1));
if ((ret = pager_output(line)) != 0)
printf(" %llu",
blkio->Media->LastBlock == 0? 0:
(unsigned long long) (blkio->Media->LastBlock + 1));
if (blkio->Media->LastBlock != 0) {
printf(" X %u", blkio->Media->BlockSize);
}
printf(" blocks");
if (blkio->Media->MediaPresent) {
if (blkio->Media->RemovableMedia)
printf(" (removable)");
} else
printf(" (no media)");
if ((ret = pager_output("\n")) != 0)
break;
if (!blkio->Media->MediaPresent)
continue;
pd->pd_blkio = blkio;
pd_dev.d_dev = dev;
pd_dev.d_unit = pd->pd_unit;
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,
blkio->Media->RemovableMedia? DISK_F_NOCACHE: 0);
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;
if (blkio->Media->RemovableMedia)
if ((ret = pager_output(" (removable)")) != 0)
break;
}
if ((ret = pager_output("\n")) != 0)
break;
}
return (ret);
}
static int
efipart_printfd(int verbose)
{
return (efipart_print_common(&efipart_fddev, &fdinfo, verbose));
}
static int
efipart_printcd(int verbose)
{
return (efipart_print_common(&efipart_cddev, &cdinfo, verbose));
}
static int
efipart_printhd(int verbose)
{
return (efipart_print_common(&efipart_hddev, &hdinfo, verbose));
}
pdinfo_list_t *
efiblk_get_pdinfo_list(struct devsw *dev)
{
if (dev->dv_type == DEVT_DISK)
return (&hdinfo);
if (dev->dv_type == DEVT_CD)
return (&cdinfo);
if (dev->dv_type == DEVT_FD)
return (&fdinfo);
return (NULL);
}
static int
efipart_open(struct open_file *f, ...)
{
va_list args;
struct devdesc *dev;
struct disk_devdesc *dev;
pdinfo_list_t *pdi;
pdinfo_t *pd;
EFI_BLOCK_IO *blkio;
EFI_HANDLE h;
EFI_STATUS status;
va_start(args, f);
dev = va_arg(args, struct devdesc*);
dev = va_arg(args, struct disk_devdesc*);
va_end(args);
h = efi_find_handle(&efipart_dev, dev->d_unit);
if (h == NULL)
if (dev == NULL)
return (EINVAL);
status = BS->HandleProtocol(h, &blkio_guid, (void **)&blkio);
if (EFI_ERROR(status))
return (efi_status_to_errno(status));
pdi = efiblk_get_pdinfo_list(dev->d_dev);
if (pdi == NULL)
return (EINVAL);
pd = efiblk_get_pdinfo(pdi, dev->d_unit);
if (pd == NULL)
return (EIO);
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));
}
blkio = pd->pd_blkio;
if (!blkio->Media->MediaPresent)
return (EAGAIN);
dev->d_opendata = blkio;
PD(dev).pd_open++;
if (PD(dev).pd_bcache == NULL)
PD(dev).pd_bcache = bcache_allocate();
pd->pd_open++;
if (pd->pd_bcache == NULL)
pd->pd_bcache = bcache_allocate();
if (dev->d_dev->dv_type == DEVT_DISK) {
return (disk_open(dev,
blkio->Media->BlockSize * (blkio->Media->LastBlock + 1),
blkio->Media->BlockSize,
blkio->Media->RemovableMedia? DISK_F_NOCACHE: 0));
}
return (0);
}
static int
efipart_close(struct open_file *f)
{
struct devdesc *dev;
struct disk_devdesc *dev;
pdinfo_list_t *pdi;
pdinfo_t *pd;
dev = (struct devdesc *)(f->f_devdata);
if (dev->d_opendata == NULL)
dev = (struct disk_devdesc *)(f->f_devdata);
if (dev == NULL)
return (EINVAL);
pdi = efiblk_get_pdinfo_list(dev->d_dev);
if (pdi == NULL)
return (EINVAL);
dev->d_opendata = NULL;
PD(dev).pd_open--;
if (PD(dev).pd_open == 0) {
bcache_free(PD(dev).pd_bcache);
PD(dev).pd_bcache = NULL;
pd = efiblk_get_pdinfo(pdi, dev->d_unit);
if (pd == NULL)
return (EINVAL);
pd->pd_open--;
if (pd->pd_open == 0) {
pd->pd_blkio = NULL;
bcache_free(pd->pd_bcache);
pd->pd_bcache = NULL;
}
if (dev->d_dev->dv_type == DEVT_DISK)
return (disk_close(dev));
return (0);
}
static int
efipart_ioctl(struct open_file *f, u_long cmd, void *data)
{
struct disk_devdesc *dev;
pdinfo_list_t *pdi;
pdinfo_t *pd;
int rc;
dev = (struct disk_devdesc *)(f->f_devdata);
if (dev == NULL)
return (EINVAL);
pdi = efiblk_get_pdinfo_list(dev->d_dev);
if (pdi == NULL)
return (EINVAL);
pd = efiblk_get_pdinfo(pdi, dev->d_unit);
if (pd == NULL)
return (EINVAL);
if (dev->d_dev->dv_type == DEVT_DISK) {
rc = disk_ioctl(dev, cmd, data);
if (rc != ENOTTY)
return (rc);
}
switch (cmd) {
case DIOCGSECTORSIZE:
*(u_int *)data = pd->pd_blkio->Media->BlockSize;
break;
case DIOCGMEDIASIZE:
*(off_t *)data = pd->pd_blkio->Media->BlockSize *
(pd->pd_blkio->Media->LastBlock + 1);
break;
default:
return (ENOTTY);
}
return (0);
}
@ -294,12 +755,29 @@ efipart_strategy(void *devdata, int rw, daddr_t blk, size_t size,
char *buf, size_t *rsize)
{
struct bcache_devdata bcd;
struct devdesc *dev;
struct disk_devdesc *dev;
pdinfo_list_t *pdi;
pdinfo_t *pd;
dev = (struct disk_devdesc *)devdata;
if (dev == NULL)
return (EINVAL);
pdi = efiblk_get_pdinfo_list(dev->d_dev);
if (pdi == NULL)
return (EINVAL);
pd = efiblk_get_pdinfo(pdi, dev->d_unit);
if (pd == NULL)
return (EINVAL);
dev = (struct devdesc *)devdata;
bcd.dv_strategy = efipart_realstrategy;
bcd.dv_devdata = devdata;
bcd.dv_cache = PD(dev).pd_bcache;
bcd.dv_cache = pd->pd_bcache;
if (dev->d_dev->dv_type == DEVT_DISK) {
return (bcache_strategy(&bcd, rw, blk + dev->d_offset,
size, buf, rsize));
}
return (bcache_strategy(&bcd, rw, blk, size, buf, rsize));
}
@ -307,7 +785,9 @@ static int
efipart_realstrategy(void *devdata, int rw, daddr_t blk, size_t size,
char *buf, size_t *rsize)
{
struct devdesc *dev = (struct devdesc *)devdata;
struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
pdinfo_list_t *pdi;
pdinfo_t *pd;
EFI_BLOCK_IO *blkio;
off_t off;
char *blkbuf;
@ -317,7 +797,15 @@ efipart_realstrategy(void *devdata, int rw, daddr_t blk, size_t size,
if (dev == NULL || blk < 0)
return (EINVAL);
blkio = dev->d_opendata;
pdi = efiblk_get_pdinfo_list(dev->d_dev);
if (pdi == NULL)
return (EINVAL);
pd = efiblk_get_pdinfo(pdi, dev->d_unit);
if (pd == NULL)
return (EINVAL);
blkio = pd->pd_blkio;
if (blkio == NULL)
return (ENXIO);

View File

@ -36,7 +36,9 @@ __FBSDID("$FreeBSD$");
#endif
struct devsw *devsw[] = {
&efipart_dev,
&efipart_fddev,
&efipart_cddev,
&efipart_hddev,
&efinet_dev,
#ifdef EFI_ZFS_BOOT
&zfs_dev,

View File

@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$");
#include <sys/disklabel.h>
#include <sys/param.h>
#include <bootstrap.h>
#include <disk.h>
#ifdef EFI_ZFS_BOOT
#include <libzfs.h>
#endif
@ -90,7 +91,7 @@ efi_parsedev(struct devdesc **dev, const char *devspec, const char **path)
struct devsw *dv;
char *cp;
const char *np;
int i;
int i, err;
/* minimum length check */
if (strlen(devspec) < 2)
@ -106,11 +107,26 @@ efi_parsedev(struct devdesc **dev, const char *devspec, const char **path)
return (ENOENT);
np = devspec + strlen(dv->dv_name);
err = 0;
switch (dv->dv_type) {
case DEVT_NONE:
break;
case DEVT_DISK:
idev = malloc(sizeof(struct disk_devdesc));
if (idev == NULL)
return (ENOMEM);
err = disk_parsedev((struct disk_devdesc *)idev, np, path);
if (err != 0) {
free(idev);
return (err);
}
break;
#ifdef EFI_ZFS_BOOT
if (dv->dv_type == DEVT_ZFS) {
int err;
case DEVT_ZFS:
idev = malloc(sizeof(struct zfs_devdesc));
if (idev == NULL)
return (ENOMEM);
@ -120,34 +136,35 @@ efi_parsedev(struct devdesc **dev, const char *devspec, const char **path)
free(idev);
return (err);
}
cp = strchr(np + 1, ':');
} else
break;
#endif
{
default:
idev = malloc(sizeof(struct devdesc));
if (idev == NULL)
return (ENOMEM);
idev->d_dev = dv;
idev->d_type = dv->dv_type;
idev->d_unit = -1;
cp = (char *)np;
if (*np != '\0' && *np != ':') {
idev->d_unit = strtol(np, &cp, 0);
if (cp == np) {
idev->d_unit = -1;
free(idev);
return (EUNIT);
}
}
if (*cp != '\0' && *cp != ':') {
free(idev);
return (EINVAL);
}
if (path != NULL)
*path = (*cp == 0) ? cp : cp + 1;
break;
}
if (*cp != '\0' && *cp != ':') {
free(idev);
return (EINVAL);
}
idev->d_dev = dv;
idev->d_type = dv->dv_type;
if (path != NULL)
*path = (*cp == 0) ? cp : cp + 1;
if (dev != NULL)
*dev = idev;
else
@ -162,14 +179,17 @@ efi_fmtdev(void *vdev)
static char buf[SPECNAMELEN + 1];
switch(dev->d_type) {
#ifdef EFI_ZFS_BOOT
case DEVT_ZFS:
return (zfs_fmtdev(dev));
#endif
case DEVT_NONE:
strcpy(buf, "(no device)");
break;
case DEVT_DISK:
return (disk_fmtdev(vdev));
#ifdef EFI_ZFS_BOOT
case DEVT_ZFS:
return (zfs_fmtdev(dev));
#endif
default:
sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit);
break;

View File

@ -35,6 +35,7 @@ __FBSDID("$FreeBSD$");
#include <stand.h>
#include <string.h>
#include <setjmp.h>
#include <disk.h>
#include <efi.h>
#include <efilib.h>
@ -70,6 +71,7 @@ EFI_GUID inputid = SIMPLE_TEXT_INPUT_PROTOCOL;
#ifdef EFI_ZFS_BOOT
static void efi_zfs_probe(void);
static uint64_t pool_guid;
#endif
static int
@ -154,12 +156,107 @@ has_keyboard(void)
return retval;
}
static int
find_currdev(EFI_LOADED_IMAGE *img, struct devsw **dev, int *unit,
uint64_t *extra)
static void
set_devdesc_currdev(struct devsw *dev, int unit)
{
struct devdesc currdev;
char *devname;
currdev.d_dev = dev;
currdev.d_type = currdev.d_dev->dv_type;
currdev.d_unit = unit;
currdev.d_opendata = NULL;
devname = efi_fmtdev(&currdev);
env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev,
env_nounset);
env_setenv("loaddev", EV_VOLATILE, devname, env_noset, env_nounset);
}
static int
find_currdev(EFI_LOADED_IMAGE *img)
{
pdinfo_list_t *pdi_list;
pdinfo_t *dp, *pp;
EFI_DEVICE_PATH *devpath, *copy;
EFI_HANDLE h;
char *devname;
struct devsw *dev;
int unit;
uint64_t extra;
/* Did efi_zfs_probe() detect the boot pool? */
if (pool_guid != 0) {
struct zfs_devdesc currdev;
currdev.d_dev = &zfs_dev;
currdev.d_unit = 0;
currdev.d_type = currdev.d_dev->dv_type;
currdev.d_opendata = NULL;
currdev.pool_guid = pool_guid;
currdev.root_guid = 0;
devname = efi_fmtdev(&currdev);
env_setenv("currdev", EV_VOLATILE, devname, efi_setcurrdev,
env_nounset);
env_setenv("loaddev", EV_VOLATILE, devname, env_noset,
env_nounset);
return (0);
}
/* We have device lists for hd, cd, fd, walk them all. */
pdi_list = efiblk_get_pdinfo_list(&efipart_hddev);
STAILQ_FOREACH(dp, pdi_list, pd_link) {
struct disk_devdesc currdev;
currdev.d_dev = &efipart_hddev;
currdev.d_type = currdev.d_dev->dv_type;
currdev.d_unit = dp->pd_unit;
currdev.d_opendata = NULL;
currdev.d_slice = -1;
currdev.d_partition = -1;
if (dp->pd_handle == img->DeviceHandle) {
devname = efi_fmtdev(&currdev);
env_setenv("currdev", EV_VOLATILE, devname,
efi_setcurrdev, env_nounset);
env_setenv("loaddev", EV_VOLATILE, devname,
env_noset, env_nounset);
return (0);
}
/* Assuming GPT partitioning. */
STAILQ_FOREACH(pp, &dp->pd_part, pd_link) {
if (pp->pd_handle == img->DeviceHandle) {
currdev.d_slice = pp->pd_unit;
currdev.d_partition = 255;
devname = efi_fmtdev(&currdev);
env_setenv("currdev", EV_VOLATILE, devname,
efi_setcurrdev, env_nounset);
env_setenv("loaddev", EV_VOLATILE, devname,
env_noset, env_nounset);
return (0);
}
}
}
pdi_list = efiblk_get_pdinfo_list(&efipart_cddev);
STAILQ_FOREACH(dp, pdi_list, pd_link) {
if (dp->pd_handle == img->DeviceHandle ||
dp->pd_alias == img->DeviceHandle) {
set_devdesc_currdev(&efipart_cddev, dp->pd_unit);
return (0);
}
}
pdi_list = efiblk_get_pdinfo_list(&efipart_fddev);
STAILQ_FOREACH(dp, pdi_list, pd_link) {
if (dp->pd_handle == img->DeviceHandle) {
set_devdesc_currdev(&efipart_fddev, dp->pd_unit);
return (0);
}
}
/*
* Try the device handle from our loaded image first. If that
@ -167,8 +264,10 @@ find_currdev(EFI_LOADED_IMAGE *img, struct devsw **dev, int *unit,
* any of the nodes in that path match one of the enumerated
* handles.
*/
if (efi_handle_lookup(img->DeviceHandle, dev, unit, extra) == 0)
if (efi_handle_lookup(img->DeviceHandle, &dev, &unit, &extra) == 0) {
set_devdesc_currdev(dev, unit);
return (0);
}
copy = NULL;
devpath = efi_lookup_image_devpath(IH);
@ -180,8 +279,10 @@ find_currdev(EFI_LOADED_IMAGE *img, struct devsw **dev, int *unit,
free(copy);
copy = NULL;
if (efi_handle_lookup(h, dev, unit, extra) == 0)
if (efi_handle_lookup(h, &dev, &unit, &extra) == 0) {
set_devdesc_currdev(dev, unit);
return (0);
}
devpath = efi_lookup_devpath(h);
if (devpath != NULL) {
@ -191,11 +292,6 @@ find_currdev(EFI_LOADED_IMAGE *img, struct devsw **dev, int *unit,
}
free(copy);
/* Try to fallback on first device */
if (devsw[0] != NULL) {
*dev = devsw[0];
return (0);
}
return (ENOENT);
}
@ -205,9 +301,7 @@ main(int argc, CHAR16 *argv[])
char var[128];
EFI_LOADED_IMAGE *img;
EFI_GUID *guid;
int i, j, vargood, unit, howto;
struct devsw *dev;
uint64_t pool_guid;
int i, j, vargood, howto;
UINTN k;
int has_kbd;
char buf[40];
@ -376,43 +470,9 @@ main(int argc, CHAR16 *argv[])
*/
BS->SetWatchdogTimer(0, 0, 0, NULL);
if (find_currdev(img, &dev, &unit, &pool_guid) != 0)
if (find_currdev(img) != 0)
return (EFI_NOT_FOUND);
switch (dev->dv_type) {
#ifdef EFI_ZFS_BOOT
case DEVT_ZFS: {
struct zfs_devdesc currdev;
currdev.d_dev = dev;
currdev.d_unit = unit;
currdev.d_type = currdev.d_dev->dv_type;
currdev.d_opendata = NULL;
currdev.pool_guid = pool_guid;
currdev.root_guid = 0;
env_setenv("currdev", EV_VOLATILE, efi_fmtdev(&currdev),
efi_setcurrdev, env_nounset);
env_setenv("loaddev", EV_VOLATILE, efi_fmtdev(&currdev), env_noset,
env_nounset);
init_zfs_bootenv(zfs_fmtdev(&currdev));
break;
}
#endif
default: {
struct devdesc currdev;
currdev.d_dev = dev;
currdev.d_unit = unit;
currdev.d_opendata = NULL;
currdev.d_type = currdev.d_dev->dv_type;
env_setenv("currdev", EV_VOLATILE, efi_fmtdev(&currdev),
efi_setcurrdev, env_nounset);
env_setenv("loaddev", EV_VOLATILE, efi_fmtdev(&currdev), env_noset,
env_nounset);
break;
}
}
efi_init_environment();
setenv("LINES", "24", 1); /* optional */
@ -733,21 +793,67 @@ COMMAND_SET(fdt, "fdt", "flattened device tree handling", command_fdt);
#endif
#ifdef EFI_ZFS_BOOT
static void
efipart_probe_img(pdinfo_list_t *hdi)
{
EFI_GUID imgid = LOADED_IMAGE_PROTOCOL;
EFI_LOADED_IMAGE *img;
pdinfo_t *hd, *pd = NULL;
char devname[SPECNAMELEN + 1];
BS->HandleProtocol(IH, &imgid, (VOID**)&img);
/*
* Search for the booted image device handle from hard disk list.
* Note, this does also include usb sticks, and we assume there is no
* ZFS on floppies nor cd.
* However, we might have booted from floppy (unlikely) or CD,
* so we should not surprised if we can not find the handle.
*/
STAILQ_FOREACH(hd, hdi, pd_link) {
if (hd->pd_handle == img->DeviceHandle)
break;
STAILQ_FOREACH(pd, &hd->pd_part, pd_link) {
if (pd->pd_handle == img->DeviceHandle)
break;
}
if (pd != NULL)
break;
}
if (hd != NULL) {
if (pd != NULL) {
snprintf(devname, sizeof(devname), "%s%dp%d:",
efipart_hddev.dv_name, hd->pd_unit, pd->pd_unit);
} else {
snprintf(devname, sizeof(devname), "%s%d:",
efipart_hddev.dv_name, hd->pd_unit);
}
(void) zfs_probe_dev(devname, &pool_guid);
}
}
static void
efi_zfs_probe(void)
{
EFI_HANDLE h;
u_int unit;
int i;
char dname[SPECNAMELEN + 1];
uint64_t guid;
pdinfo_list_t *hdi;
pdinfo_t *hd;
char devname[SPECNAMELEN + 1];
unit = 0;
h = efi_find_handle(&efipart_dev, 0);
for (i = 0; h != NULL; h = efi_find_handle(&efipart_dev, ++i)) {
snprintf(dname, sizeof(dname), "%s%d:", efipart_dev.dv_name, i);
if (zfs_probe_dev(dname, &guid) == 0)
(void)efi_handle_update_dev(h, &zfs_dev, unit++, guid);
hdi = efiblk_get_pdinfo_list(&efipart_hddev);
/*
* First probe the boot device (from where loader.efi was read),
* and set pool_guid global variable if we are booting from zfs.
* Since loader is running, we do have an access to the device,
* however, it might not be zfs.
*/
if (pool_guid == 0)
efipart_probe_img(hdi);
STAILQ_FOREACH(hd, hdi, pd_link) {
snprintf(devname, sizeof(devname), "%s%d:",
efipart_hddev.dv_name, hd->pd_unit);
(void) zfs_probe_dev(devname, NULL);
}
}
#endif

View File

@ -486,6 +486,8 @@ zfs_probe_dev(const char *devname, uint64_t *pool_guid)
uint64_t mediasz;
int ret;
if (pool_guid)
*pool_guid = 0;
pa.fd = open(devname, O_RDONLY);
if (pa.fd == -1)
return (ENXIO);
@ -493,6 +495,7 @@ zfs_probe_dev(const char *devname, uint64_t *pool_guid)
ret = zfs_probe(pa.fd, pool_guid);
if (ret == 0)
return (0);
/* Probe each partition */
ret = ioctl(pa.fd, DIOCGMEDIASIZE, &mediasz);
if (ret == 0)
@ -508,6 +511,8 @@ zfs_probe_dev(const char *devname, uint64_t *pool_guid)
}
}
close(pa.fd);
if (pool_guid && *pool_guid == 0)
ret = ENXIO;
return (ret);
}