Changeset View
Changeset View
Standalone View
Standalone View
stand/efi/libefi/efipart.c
Show First 20 Lines • Show All 94 Lines • ▼ Show 20 Lines | struct devsw efipart_hddev = { | ||||
.dv_strategy = efipart_strategy, | .dv_strategy = efipart_strategy, | ||||
.dv_open = efipart_open, | .dv_open = efipart_open, | ||||
.dv_close = efipart_close, | .dv_close = efipart_close, | ||||
.dv_ioctl = efipart_ioctl, | .dv_ioctl = efipart_ioctl, | ||||
.dv_print = efipart_printhd, | .dv_print = efipart_printhd, | ||||
.dv_cleanup = NULL | .dv_cleanup = NULL | ||||
}; | }; | ||||
static pdinfo_list_t fdinfo; | static pdinfo_list_t fdinfo = STAILQ_HEAD_INITIALIZER(fdinfo); | ||||
static pdinfo_list_t cdinfo; | static pdinfo_list_t cdinfo = STAILQ_HEAD_INITIALIZER(cdinfo); | ||||
static pdinfo_list_t hdinfo; | static pdinfo_list_t hdinfo = STAILQ_HEAD_INITIALIZER(hdinfo); | ||||
static EFI_HANDLE *efipart_handles = NULL; | /* | ||||
static UINTN efipart_nhandles = 0; | * efipart_inithandles() is used to build up the pdinfo list from | ||||
* block device handles. Then each devsw init callback is used to | |||||
* pick items from pdinfo and move to proper device list. | |||||
* In ideal world, we should end up with empty pdinfo once all | |||||
* devsw initializers are called. | |||||
*/ | |||||
static pdinfo_list_t pdinfo = STAILQ_HEAD_INITIALIZER(pdinfo); | |||||
pdinfo_list_t * | pdinfo_list_t * | ||||
efiblk_get_pdinfo_list(struct devsw *dev) | efiblk_get_pdinfo_list(struct devsw *dev) | ||||
{ | { | ||||
if (dev->dv_type == DEVT_DISK) | if (dev->dv_type == DEVT_DISK) | ||||
return (&hdinfo); | return (&hdinfo); | ||||
if (dev->dv_type == DEVT_CD) | if (dev->dv_type == DEVT_CD) | ||||
return (&cdinfo); | return (&cdinfo); | ||||
Show All 19 Lines | efiblk_get_pdinfo(struct devdesc *dev) | ||||
} | } | ||||
return (pd); | return (pd); | ||||
} | } | ||||
pdinfo_t * | pdinfo_t * | ||||
efiblk_get_pdinfo_by_device_path(EFI_DEVICE_PATH *path) | efiblk_get_pdinfo_by_device_path(EFI_DEVICE_PATH *path) | ||||
{ | { | ||||
EFI_HANDLE h; | EFI_HANDLE h; | ||||
EFI_STATUS status; | |||||
EFI_DEVICE_PATH *devp = path; | |||||
h = efi_devpath_to_handle(path, efipart_handles, efipart_nhandles); | status = BS->LocateDevicePath(&blkio_guid, &devp, &h); | ||||
if (h == NULL) | if (EFI_ERROR(status)) | ||||
return (NULL); | return (NULL); | ||||
return (efiblk_get_pdinfo_by_handle(h)); | return (efiblk_get_pdinfo_by_handle(h)); | ||||
} | } | ||||
static bool | static bool | ||||
same_handle(pdinfo_t *pd, EFI_HANDLE h) | same_handle(pdinfo_t *pd, EFI_HANDLE h) | ||||
{ | { | ||||
Show All 14 Lines | STAILQ_FOREACH(dp, &hdinfo, pd_link) { | ||||
STAILQ_FOREACH(pp, &dp->pd_part, pd_link) { | STAILQ_FOREACH(pp, &dp->pd_part, pd_link) { | ||||
if (same_handle(pp, h)) | if (same_handle(pp, h)) | ||||
return (pp); | return (pp); | ||||
} | } | ||||
} | } | ||||
STAILQ_FOREACH(dp, &cdinfo, pd_link) { | STAILQ_FOREACH(dp, &cdinfo, pd_link) { | ||||
if (same_handle(dp, h)) | if (same_handle(dp, h)) | ||||
return (dp); | return (dp); | ||||
STAILQ_FOREACH(pp, &dp->pd_part, pd_link) { | |||||
if (same_handle(pp, h)) | |||||
return (pp); | |||||
} | } | ||||
} | |||||
STAILQ_FOREACH(dp, &fdinfo, pd_link) { | STAILQ_FOREACH(dp, &fdinfo, pd_link) { | ||||
if (same_handle(dp, h)) | if (same_handle(dp, h)) | ||||
return (dp); | return (dp); | ||||
} | } | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
static int | static int | ||||
efiblk_pdinfo_count(pdinfo_list_t *pdi) | efiblk_pdinfo_count(pdinfo_list_t *pdi) | ||||
{ | { | ||||
pdinfo_t *pd; | pdinfo_t *pd; | ||||
int i = 0; | int i = 0; | ||||
STAILQ_FOREACH(pd, pdi, pd_link) { | STAILQ_FOREACH(pd, pdi, pd_link) { | ||||
i++; | i++; | ||||
} | } | ||||
return (i); | return (i); | ||||
} | } | ||||
int | int | ||||
efipart_inithandles(void) | efipart_inithandles(void) | ||||
{ | { | ||||
unsigned i, nin; | |||||
UINTN sz; | UINTN sz; | ||||
EFI_HANDLE *hin; | EFI_HANDLE *hin; | ||||
EFI_DEVICE_PATH *devpath; | |||||
EFI_BLOCK_IO *blkio; | |||||
EFI_STATUS status; | EFI_STATUS status; | ||||
pdinfo_t *pd; | |||||
if (efipart_nhandles != 0) { | if (!STAILQ_EMPTY(&pdinfo)) | ||||
free(efipart_handles); | return (0); | ||||
efipart_handles = NULL; | |||||
efipart_nhandles = 0; | |||||
} | |||||
sz = 0; | sz = 0; | ||||
hin = NULL; | hin = NULL; | ||||
status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, hin); | status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, hin); | ||||
if (status == EFI_BUFFER_TOO_SMALL) { | if (status == EFI_BUFFER_TOO_SMALL) { | ||||
hin = malloc(sz); | hin = malloc(sz); | ||||
status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, | status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, | ||||
hin); | hin); | ||||
if (EFI_ERROR(status)) | if (EFI_ERROR(status)) | ||||
free(hin); | free(hin); | ||||
} | } | ||||
if (EFI_ERROR(status)) | if (EFI_ERROR(status)) | ||||
return (efi_status_to_errno(status)); | return (efi_status_to_errno(status)); | ||||
efipart_handles = hin; | nin = sz / sizeof(*hin); | ||||
efipart_nhandles = sz / sizeof(*hin); | |||||
#ifdef EFIPART_DEBUG | #ifdef EFIPART_DEBUG | ||||
printf("%s: Got %d BLOCK IO MEDIA handle(s)\n", __func__, | printf("%s: Got %d BLOCK IO MEDIA handle(s)\n", __func__, nin); | ||||
efipart_nhandles); | |||||
#endif | #endif | ||||
return (0); | |||||
} | |||||
static ACPI_HID_DEVICE_PATH * | for (i = 0; i < nin; i++) { | ||||
efipart_floppy(EFI_DEVICE_PATH *node) | |||||
{ | |||||
ACPI_HID_DEVICE_PATH *acpi; | |||||
if (DevicePathType(node) == ACPI_DEVICE_PATH && | |||||
DevicePathSubType(node) == ACPI_DP) { | |||||
acpi = (ACPI_HID_DEVICE_PATH *) node; | |||||
if (acpi->HID == EISA_PNP_ID(PNP0604) || | |||||
acpi->HID == EISA_PNP_ID(PNP0700) || | |||||
acpi->HID == EISA_PNP_ID(PNP0701)) { | |||||
return (acpi); | |||||
} | |||||
} | |||||
return (NULL); | |||||
} | |||||
/* | /* | ||||
* Determine if the provided device path is hdd. | * Get devpath and open protocol. | ||||
* | * We should not get errors here | ||||
* There really is no simple fool proof way to classify the devices. | |||||
* Since we do build three lists of devices - floppy, cd and hdd, we | |||||
* will try to see if the device is floppy or cd, and list anything else | |||||
* as hdd. | |||||
*/ | */ | ||||
static bool | if ((devpath = efi_lookup_devpath(hin[i])) == NULL) | ||||
efipart_hdd(EFI_DEVICE_PATH *dp) | |||||
{ | |||||
unsigned i; | |||||
EFI_DEVICE_PATH *devpath, *node; | |||||
EFI_BLOCK_IO *blkio; | |||||
EFI_STATUS status; | |||||
if (dp == NULL) | |||||
return (false); | |||||
if ((node = efi_devpath_last_node(dp)) == NULL) | |||||
return (false); | |||||
if (efipart_floppy(node) != NULL) | |||||
return (false); | |||||
/* | |||||
* Test every EFI BLOCK IO handle to make sure dp is not device path | |||||
* for CD/DVD. | |||||
*/ | |||||
for (i = 0; i < efipart_nhandles; i++) { | |||||
devpath = efi_lookup_devpath(efipart_handles[i]); | |||||
if (devpath == NULL) | |||||
return (false); | |||||
/* Only continue testing when dp is prefix in devpath. */ | |||||
if (!efi_devpath_is_prefix(dp, devpath)) | |||||
continue; | continue; | ||||
/* | status = OpenProtocolByHandle(hin[i], &blkio_guid, | ||||
* The device path has to have last node describing the | |||||
* device, or we can not test the type. | |||||
*/ | |||||
if ((node = efi_devpath_last_node(devpath)) == NULL) | |||||
return (false); | |||||
if (DevicePathType(node) == MEDIA_DEVICE_PATH && | |||||
DevicePathSubType(node) == MEDIA_CDROM_DP) { | |||||
return (false); | |||||
} | |||||
/* Make sure we do have the media. */ | |||||
status = OpenProtocolByHandle(efipart_handles[i], &blkio_guid, | |||||
(void **)&blkio); | (void **)&blkio); | ||||
if (EFI_ERROR(status)) | if (EFI_ERROR(status)) { | ||||
return (false); | printf("error %lu\n", EFI_ERROR_CODE(status)); | ||||
continue; | |||||
/* USB or SATA cd without the media. */ | |||||
if (blkio->Media->RemovableMedia && | |||||
!blkio->Media->MediaPresent) { | |||||
return (false); | |||||
} | } | ||||
/* | /* | ||||
* 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 | * iPXE is known to insert stub BLOCK IO device with | ||||
* BlockSize 1. | * BlockSize 1. | ||||
*/ | */ | ||||
if (blkio->Media->BlockSize < 512 || | if (blkio->Media->BlockSize < 512 || | ||||
!powerof2(blkio->Media->BlockSize)) { | !powerof2(blkio->Media->BlockSize)) { | ||||
return (false); | continue; | ||||
} | } | ||||
} | |||||
return (true); | |||||
} | |||||
/* | /* This is bad. */ | ||||
* Add or update entries with new handle data. | if ((pd = calloc(1, sizeof(*pd))) == NULL) { | ||||
*/ | printf("efipart_inithandles: Out of memory.\n"); | ||||
static int | free(hin); | ||||
efipart_fdinfo_add(EFI_HANDLE handle, uint32_t uid, EFI_DEVICE_PATH *devpath) | |||||
{ | |||||
pdinfo_t *fd; | |||||
fd = calloc(1, sizeof(pdinfo_t)); | |||||
if (fd == NULL) { | |||||
printf("Failed to register floppy %d, out of memory\n", uid); | |||||
return (ENOMEM); | return (ENOMEM); | ||||
} | } | ||||
STAILQ_INIT(&fd->pd_part); | STAILQ_INIT(&pd->pd_part); | ||||
fd->pd_unit = uid; | pd->pd_handle = hin[i]; | ||||
fd->pd_handle = handle; | pd->pd_devpath = devpath; | ||||
fd->pd_devpath = devpath; | pd->pd_blkio = blkio; | ||||
fd->pd_parent = NULL; | STAILQ_INSERT_TAIL(&pdinfo, pd, pd_link); | ||||
fd->pd_devsw = &efipart_fddev; | } | ||||
STAILQ_INSERT_TAIL(&fdinfo, fd, pd_link); | |||||
free(hin); | |||||
return (0); | return (0); | ||||
} | } | ||||
static void | static ACPI_HID_DEVICE_PATH * | ||||
efipart_updatefd(void) | efipart_floppy(EFI_DEVICE_PATH *node) | ||||
{ | { | ||||
EFI_DEVICE_PATH *devpath, *node; | |||||
ACPI_HID_DEVICE_PATH *acpi; | ACPI_HID_DEVICE_PATH *acpi; | ||||
int i; | |||||
for (i = 0; i < efipart_nhandles; i++) { | if (DevicePathType(node) == ACPI_DEVICE_PATH && | ||||
devpath = efi_lookup_devpath(efipart_handles[i]); | DevicePathSubType(node) == ACPI_DP) { | ||||
if (devpath == NULL) | acpi = (ACPI_HID_DEVICE_PATH *) node; | ||||
continue; | if (acpi->HID == EISA_PNP_ID(PNP0604) || | ||||
acpi->HID == EISA_PNP_ID(PNP0700) || | |||||
if ((node = efi_devpath_last_node(devpath)) == NULL) | acpi->HID == EISA_PNP_ID(PNP0701)) { | ||||
continue; | return (acpi); | ||||
if ((acpi = efipart_floppy(node)) != NULL) { | |||||
efipart_fdinfo_add(efipart_handles[i], acpi->UID, | |||||
devpath); | |||||
} | } | ||||
} | } | ||||
return (NULL); | |||||
} | } | ||||
static pdinfo_t * | |||||
efipart_find_parent(pdinfo_list_t *pdi, EFI_DEVICE_PATH *devpath) | |||||
{ | |||||
pdinfo_t *pd; | |||||
STAILQ_FOREACH(pd, pdi, pd_link) { | |||||
if (efi_devpath_is_prefix(pd->pd_devpath, devpath)) | |||||
return (pd); | |||||
} | |||||
return (NULL); | |||||
} | |||||
static int | static int | ||||
efipart_initfd(void) | efipart_initfd(void) | ||||
{ | { | ||||
EFI_DEVICE_PATH *node; | |||||
ACPI_HID_DEVICE_PATH *acpi; | |||||
pdinfo_t *parent, *fd; | |||||
STAILQ_INIT(&fdinfo); | restart: | ||||
STAILQ_FOREACH(fd, &pdinfo, pd_link) { | |||||
if ((node = efi_devpath_last_node(fd->pd_devpath)) == NULL) | |||||
continue; | |||||
efipart_updatefd(); | if ((acpi = efipart_floppy(node)) == NULL) | ||||
continue; | |||||
STAILQ_REMOVE(&pdinfo, fd, pdinfo, pd_link); | |||||
parent = efipart_find_parent(&pdinfo, fd->pd_devpath); | |||||
if (parent != NULL) { | |||||
STAILQ_REMOVE(&pdinfo, parent, pdinfo, pd_link); | |||||
parent->pd_alias = fd->pd_handle; | |||||
parent->pd_unit = acpi->UID; | |||||
free(fd); | |||||
fd = parent; | |||||
} else { | |||||
fd->pd_unit = acpi->UID; | |||||
} | |||||
fd->pd_devsw = &efipart_fddev; | |||||
STAILQ_INSERT_TAIL(&fdinfo, fd, pd_link); | |||||
goto restart; | |||||
} | |||||
bcache_add_dev(efiblk_pdinfo_count(&fdinfo)); | bcache_add_dev(efiblk_pdinfo_count(&fdinfo)); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* | /* | ||||
* Add or update entries with new handle data. | * Add or update entries with new handle data. | ||||
*/ | */ | ||||
static int | static void | ||||
efipart_cdinfo_add(EFI_HANDLE handle, EFI_HANDLE alias, | efipart_cdinfo_add(pdinfo_t *cd) | ||||
EFI_DEVICE_PATH *devpath) | |||||
{ | { | ||||
int unit; | pdinfo_t *pd, *last; | ||||
pdinfo_t *cd; | |||||
pdinfo_t *pd; | |||||
unit = 0; | |||||
STAILQ_FOREACH(pd, &cdinfo, pd_link) { | STAILQ_FOREACH(pd, &cdinfo, pd_link) { | ||||
if (efi_devpath_match(pd->pd_devpath, devpath) == true) { | if (efi_devpath_is_prefix(pd->pd_devpath, cd->pd_devpath)) { | ||||
pd->pd_handle = handle; | last = STAILQ_LAST(&pd->pd_part, pdinfo, pd_link); | ||||
pd->pd_alias = alias; | if (last != NULL) | ||||
return (0); | cd->pd_unit = last->pd_unit + 1; | ||||
else | |||||
cd->pd_unit = 0; | |||||
cd->pd_parent = pd; | |||||
cd->pd_devsw = &efipart_cddev; | |||||
STAILQ_INSERT_TAIL(&pd->pd_part, cd, pd_link); | |||||
return; | |||||
} | } | ||||
unit++; | |||||
} | } | ||||
cd = calloc(1, sizeof(pdinfo_t)); | last = STAILQ_LAST(&cdinfo, pdinfo, pd_link); | ||||
if (cd == NULL) { | if (last != NULL) | ||||
printf("Failed to add cd %d, out of memory\n", unit); | cd->pd_unit = last->pd_unit + 1; | ||||
return (ENOMEM); | else | ||||
} | cd->pd_unit = 0; | ||||
STAILQ_INIT(&cd->pd_part); | |||||
cd->pd_handle = handle; | |||||
cd->pd_unit = unit; | |||||
cd->pd_alias = alias; | |||||
cd->pd_devpath = devpath; | |||||
cd->pd_parent = NULL; | cd->pd_parent = NULL; | ||||
cd->pd_devsw = &efipart_cddev; | cd->pd_devsw = &efipart_cddev; | ||||
STAILQ_INSERT_TAIL(&cdinfo, cd, pd_link); | STAILQ_INSERT_TAIL(&cdinfo, cd, pd_link); | ||||
return (0); | |||||
} | } | ||||
static bool | |||||
efipart_testcd(EFI_DEVICE_PATH *node, EFI_BLOCK_IO *blkio) | |||||
{ | |||||
if (DevicePathType(node) == MEDIA_DEVICE_PATH && | |||||
DevicePathSubType(node) == MEDIA_CDROM_DP) { | |||||
return (true); | |||||
} | |||||
/* cd drive without the media. */ | |||||
if (blkio->Media->RemovableMedia && | |||||
!blkio->Media->MediaPresent) { | |||||
return (true); | |||||
} | |||||
return (false); | |||||
} | |||||
static void | static void | ||||
efipart_updatecd(void) | efipart_updatecd(void) | ||||
{ | { | ||||
int i; | EFI_DEVICE_PATH *devpath, *node; | ||||
EFI_DEVICE_PATH *devpath, *devpathcpy, *tmpdevpath, *node; | |||||
EFI_HANDLE handle; | |||||
EFI_BLOCK_IO *blkio; | |||||
EFI_STATUS status; | EFI_STATUS status; | ||||
pdinfo_t *parent, *cd; | |||||
for (i = 0; i < efipart_nhandles; i++) { | restart: | ||||
devpath = efi_lookup_devpath(efipart_handles[i]); | STAILQ_FOREACH(cd, &pdinfo, pd_link) { | ||||
if (devpath == NULL) | if ((node = efi_devpath_last_node(cd->pd_devpath)) == NULL) | ||||
continue; | continue; | ||||
if ((node = efi_devpath_last_node(devpath)) == NULL) | |||||
continue; | |||||
if (efipart_floppy(node) != NULL) | if (efipart_floppy(node) != NULL) | ||||
continue; | continue; | ||||
if (efipart_hdd(devpath)) | /* Is parent of this device already registered? */ | ||||
continue; | parent = efipart_find_parent(&cdinfo, cd->pd_devpath); | ||||
if (parent != NULL) { | |||||
STAILQ_REMOVE(&pdinfo, cd, pdinfo, pd_link); | |||||
efipart_cdinfo_add(cd); | |||||
goto restart; | |||||
} | |||||
status = OpenProtocolByHandle(efipart_handles[i], &blkio_guid, | if (!efipart_testcd(node, cd->pd_blkio)) | ||||
(void **)&blkio); | |||||
if (EFI_ERROR(status)) | |||||
continue; | continue; | ||||
/* Find parent and unlink both parent and cd from pdinfo */ | |||||
STAILQ_REMOVE(&pdinfo, cd, pdinfo, pd_link); | |||||
parent = efipart_find_parent(&pdinfo, cd->pd_devpath); | |||||
if (parent != NULL) { | |||||
STAILQ_REMOVE(&pdinfo, parent, pdinfo, pd_link); | |||||
efipart_cdinfo_add(parent); | |||||
} | |||||
if (parent == NULL) | |||||
parent = efipart_find_parent(&cdinfo, cd->pd_devpath); | |||||
/* | /* | ||||
* If we come across a logical partition of subtype CDROM | * If we come across a logical partition of subtype CDROM | ||||
* it doesn't refer to the CD filesystem itself, but rather | * it doesn't refer to the CD filesystem itself, but rather | ||||
* to any usable El Torito boot image on it. In this case | * to any usable El Torito boot image on it. In this case | ||||
* we try to find the parent device and add that instead as | * we try to find the parent device and add that instead as | ||||
* that will be the CD filesystem. | * that will be the CD filesystem. | ||||
*/ | */ | ||||
if (DevicePathType(node) == MEDIA_DEVICE_PATH && | if (DevicePathType(node) == MEDIA_DEVICE_PATH && | ||||
DevicePathSubType(node) == MEDIA_CDROM_DP) { | DevicePathSubType(node) == MEDIA_CDROM_DP && | ||||
devpathcpy = efi_devpath_trim(devpath); | parent == NULL) { | ||||
if (devpathcpy == NULL) | parent = calloc(1, sizeof(*parent)); | ||||
continue; | if (parent == NULL) { | ||||
tmpdevpath = devpathcpy; | printf("efipart_updatecd: out of memory\n"); | ||||
status = BS->LocateDevicePath(&blkio_guid, &tmpdevpath, | /* this device is lost but try again. */ | ||||
&handle); | free(cd); | ||||
free(devpathcpy); | goto restart; | ||||
if (EFI_ERROR(status)) | |||||
continue; | |||||
devpath = efi_lookup_devpath(handle); | |||||
efipart_cdinfo_add(handle, efipart_handles[i], | |||||
devpath); | |||||
continue; | |||||
} | } | ||||
if (DevicePathType(node) == MESSAGING_DEVICE_PATH && | devpath = efi_devpath_trim(cd->pd_devpath); | ||||
DevicePathSubType(node) == MSG_ATAPI_DP) { | if (devpath == NULL) { | ||||
efipart_cdinfo_add(efipart_handles[i], NULL, | printf("efipart_updatecd: out of memory\n"); | ||||
devpath); | /* this device is lost but try again. */ | ||||
continue; | free(parent); | ||||
free(cd); | |||||
goto restart; | |||||
} | } | ||||
parent->pd_devpath = devpath; | |||||
/* USB or SATA cd without the media. */ | status = BS->LocateDevicePath(&blkio_guid, | ||||
if (blkio->Media->RemovableMedia && | &parent->pd_devpath, &parent->pd_handle); | ||||
!blkio->Media->MediaPresent) { | free(devpath); | ||||
efipart_cdinfo_add(efipart_handles[i], NULL, | if (EFI_ERROR(status)) { | ||||
devpath); | printf("efipart_updatecd: error %lu\n", | ||||
EFI_ERROR_CODE(status)); | |||||
free(parent); | |||||
free(cd); | |||||
goto restart; | |||||
} | } | ||||
parent->pd_devpath = | |||||
efi_lookup_devpath(parent->pd_handle); | |||||
efipart_cdinfo_add(parent); | |||||
} | } | ||||
efipart_cdinfo_add(cd); | |||||
goto restart; | |||||
} | } | ||||
} | |||||
static int | static int | ||||
efipart_initcd(void) | efipart_initcd(void) | ||||
{ | { | ||||
STAILQ_INIT(&cdinfo); | |||||
efipart_updatecd(); | efipart_updatecd(); | ||||
bcache_add_dev(efiblk_pdinfo_count(&cdinfo)); | bcache_add_dev(efiblk_pdinfo_count(&cdinfo)); | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static void | ||||
efipart_hdinfo_add(EFI_HANDLE disk_handle, EFI_HANDLE part_handle) | efipart_hdinfo_add(pdinfo_t *hd, HARDDRIVE_DEVICE_PATH *node) | ||||
{ | { | ||||
EFI_DEVICE_PATH *disk_devpath, *part_devpath; | pdinfo_t *pd, *last; | ||||
HARDDRIVE_DEVICE_PATH *node; | |||||
int unit; | |||||
pdinfo_t *hd, *pd, *last; | |||||
disk_devpath = efi_lookup_devpath(disk_handle); | STAILQ_FOREACH(pd, &hdinfo, pd_link) { | ||||
if (disk_devpath == NULL) | if (efi_devpath_is_prefix(pd->pd_devpath, hd->pd_devpath)) { | ||||
return (ENOENT); | |||||
if (part_handle != NULL) { | |||||
part_devpath = efi_lookup_devpath(part_handle); | |||||
if (part_devpath == NULL) | |||||
return (ENOENT); | |||||
node = (HARDDRIVE_DEVICE_PATH *) | |||||
efi_devpath_last_node(part_devpath); | |||||
if (node == NULL) | |||||
return (ENOENT); /* This should not happen. */ | |||||
} else { | |||||
part_devpath = NULL; | |||||
node = NULL; | |||||
} | |||||
pd = calloc(1, sizeof(pdinfo_t)); | |||||
if (pd == NULL) { | |||||
printf("Failed to add disk, out of memory\n"); | |||||
return (ENOMEM); | |||||
} | |||||
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. */ | /* Add the partition. */ | ||||
pd->pd_handle = part_handle; | hd->pd_unit = node->PartitionNumber; | ||||
pd->pd_unit = node->PartitionNumber; | hd->pd_parent = pd; | ||||
pd->pd_devpath = part_devpath; | hd->pd_devsw = &efipart_hddev; | ||||
pd->pd_parent = hd; | STAILQ_INSERT_TAIL(&pd->pd_part, hd, pd_link); | ||||
pd->pd_devsw = &efipart_hddev; | return; | ||||
STAILQ_INSERT_TAIL(&hd->pd_part, pd, pd_link); | |||||
return (0); | |||||
} | } | ||||
} | } | ||||
last = STAILQ_LAST(&hdinfo, pdinfo, pd_link); | last = STAILQ_LAST(&hdinfo, pdinfo, pd_link); | ||||
if (last != NULL) | if (last != NULL) | ||||
unit = last->pd_unit + 1; | hd->pd_unit = last->pd_unit + 1; | ||||
else | else | ||||
unit = 0; | hd->pd_unit = 0; | ||||
/* Add the disk. */ | /* Add the disk. */ | ||||
hd = pd; | |||||
hd->pd_handle = disk_handle; | |||||
hd->pd_unit = unit; | |||||
hd->pd_devpath = disk_devpath; | |||||
hd->pd_parent = NULL; | |||||
hd->pd_devsw = &efipart_hddev; | hd->pd_devsw = &efipart_hddev; | ||||
STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link); | STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link); | ||||
if (part_devpath == NULL) | |||||
return (0); | |||||
pd = calloc(1, sizeof(pdinfo_t)); | |||||
if (pd == NULL) { | |||||
printf("Failed to add partition, out of memory\n"); | |||||
return (ENOMEM); | |||||
} | } | ||||
STAILQ_INIT(&pd->pd_part); | |||||
/* Add the partition. */ | |||||
pd->pd_handle = part_handle; | |||||
pd->pd_unit = node->PartitionNumber; | |||||
pd->pd_devpath = part_devpath; | |||||
pd->pd_parent = hd; | |||||
pd->pd_devsw = &efipart_hddev; | |||||
STAILQ_INSERT_TAIL(&hd->pd_part, pd, pd_link); | |||||
return (0); | |||||
} | |||||
/* | /* | ||||
* The MEDIA_FILEPATH_DP has device name. | * The MEDIA_FILEPATH_DP has device name. | ||||
* From U-Boot sources it looks like names are in the form | * From U-Boot sources it looks like names are in the form | ||||
* of typeN:M, where type is interface type, N is disk id | * of typeN:M, where type is interface type, N is disk id | ||||
* and M is partition id. | * and M is partition id. | ||||
*/ | */ | ||||
static int | static void | ||||
efipart_hdinfo_add_filepath(EFI_HANDLE disk_handle) | efipart_hdinfo_add_filepath(pdinfo_t *hd, FILEPATH_DEVICE_PATH *node) | ||||
{ | { | ||||
EFI_DEVICE_PATH *devpath; | |||||
FILEPATH_DEVICE_PATH *node; | |||||
char *pathname, *p; | char *pathname, *p; | ||||
int unit, len; | int len; | ||||
pdinfo_t *pd, *last; | pdinfo_t *last; | ||||
/* First collect and verify all the data */ | |||||
if ((devpath = efi_lookup_devpath(disk_handle)) == NULL) | |||||
return (ENOENT); | |||||
node = (FILEPATH_DEVICE_PATH *)efi_devpath_last_node(devpath); | |||||
if (node == NULL) | |||||
return (ENOENT); /* This should not happen. */ | |||||
pd = calloc(1, sizeof(pdinfo_t)); | |||||
if (pd == NULL) { | |||||
printf("Failed to add disk, out of memory\n"); | |||||
return (ENOMEM); | |||||
} | |||||
STAILQ_INIT(&pd->pd_part); | |||||
last = STAILQ_LAST(&hdinfo, pdinfo, pd_link); | last = STAILQ_LAST(&hdinfo, pdinfo, pd_link); | ||||
if (last != NULL) | if (last != NULL) | ||||
unit = last->pd_unit + 1; | hd->pd_unit = last->pd_unit + 1; | ||||
else | else | ||||
unit = 0; | hd->pd_unit = 0; | ||||
/* FILEPATH_DEVICE_PATH has 0 terminated string */ | /* FILEPATH_DEVICE_PATH has 0 terminated string */ | ||||
len = ucs2len(node->PathName); | len = ucs2len(node->PathName); | ||||
if ((pathname = malloc(len + 1)) == NULL) { | if ((pathname = malloc(len + 1)) == NULL) { | ||||
printf("Failed to add disk, out of memory\n"); | printf("Failed to add disk, out of memory\n"); | ||||
free(pd); | free(hd); | ||||
return (ENOMEM); | return; | ||||
} | } | ||||
cpy16to8(node->PathName, pathname, len + 1); | cpy16to8(node->PathName, pathname, len + 1); | ||||
p = strchr(pathname, ':'); | p = strchr(pathname, ':'); | ||||
/* | /* | ||||
* Assume we are receiving handles in order, first disk handle, | * Assume we are receiving handles in order, first disk handle, | ||||
* then partitions for this disk. If this assumption proves | * then partitions for this disk. If this assumption proves | ||||
* false, this code would need update. | * false, this code would need update. | ||||
*/ | */ | ||||
if (p == NULL) { /* no colon, add the disk */ | if (p == NULL) { /* no colon, add the disk */ | ||||
pd->pd_handle = disk_handle; | hd->pd_devsw = &efipart_hddev; | ||||
pd->pd_unit = unit; | STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link); | ||||
pd->pd_devpath = devpath; | |||||
pd->pd_parent = NULL; | |||||
pd->pd_devsw = &efipart_hddev; | |||||
STAILQ_INSERT_TAIL(&hdinfo, pd, pd_link); | |||||
free(pathname); | free(pathname); | ||||
return (0); | return; | ||||
} | } | ||||
p++; /* skip the colon */ | p++; /* skip the colon */ | ||||
errno = 0; | errno = 0; | ||||
unit = (int)strtol(p, NULL, 0); | hd->pd_unit = (int)strtol(p, NULL, 0); | ||||
if (errno != 0) { | if (errno != 0) { | ||||
printf("Bad unit number for partition \"%s\"\n", pathname); | printf("Bad unit number for partition \"%s\"\n", pathname); | ||||
free(pathname); | free(pathname); | ||||
free(pd); | free(hd); | ||||
return (EUNIT); | return; | ||||
} | } | ||||
/* | /* | ||||
* We should have disk registered, if not, we are receiving | * We should have disk registered, if not, we are receiving | ||||
* handles out of order, and this code should be reworked | * handles out of order, and this code should be reworked | ||||
* to create "blank" disk for partition, and to find the | * to create "blank" disk for partition, and to find the | ||||
* disk based on PathName compares. | * disk based on PathName compares. | ||||
*/ | */ | ||||
if (last == NULL) { | if (last == NULL) { | ||||
printf("BUG: No disk for partition \"%s\"\n", pathname); | printf("BUG: No disk for partition \"%s\"\n", pathname); | ||||
free(pathname); | free(pathname); | ||||
free(pd); | free(hd); | ||||
return (EINVAL); | return; | ||||
} | } | ||||
/* Add the partition. */ | /* Add the partition. */ | ||||
pd->pd_handle = disk_handle; | hd->pd_parent = last; | ||||
pd->pd_unit = unit; | hd->pd_devsw = &efipart_hddev; | ||||
pd->pd_devpath = devpath; | STAILQ_INSERT_TAIL(&last->pd_part, hd, pd_link); | ||||
pd->pd_parent = last; | |||||
pd->pd_devsw = &efipart_hddev; | |||||
STAILQ_INSERT_TAIL(&last->pd_part, pd, pd_link); | |||||
free(pathname); | free(pathname); | ||||
return (0); | |||||
} | } | ||||
static void | static void | ||||
efipart_updatehd(void) | efipart_updatehd(void) | ||||
{ | { | ||||
int i; | EFI_DEVICE_PATH *devpath, *node; | ||||
EFI_DEVICE_PATH *devpath, *devpathcpy, *tmpdevpath, *node; | |||||
EFI_HANDLE handle; | |||||
EFI_BLOCK_IO *blkio; | |||||
EFI_STATUS status; | EFI_STATUS status; | ||||
pdinfo_t *parent, *hd; | |||||
for (i = 0; i < efipart_nhandles; i++) { | restart: | ||||
devpath = efi_lookup_devpath(efipart_handles[i]); | STAILQ_FOREACH(hd, &pdinfo, pd_link) { | ||||
if (devpath == NULL) | if ((node = efi_devpath_last_node(hd->pd_devpath)) == NULL) | ||||
continue; | continue; | ||||
if ((node = efi_devpath_last_node(devpath)) == NULL) | if (efipart_floppy(node) != NULL) | ||||
continue; | continue; | ||||
if (!efipart_hdd(devpath)) | if (efipart_testcd(node, hd->pd_blkio)) | ||||
continue; | continue; | ||||
status = OpenProtocolByHandle(efipart_handles[i], &blkio_guid, | if (DevicePathType(node) == HARDWARE_DEVICE_PATH && | ||||
(void **)&blkio); | DevicePathSubType(node) == HW_PCI_DP) { | ||||
if (EFI_ERROR(status)) | STAILQ_REMOVE(&pdinfo, hd, pdinfo, pd_link); | ||||
continue; | efipart_hdinfo_add(hd, NULL); | ||||
goto restart; | |||||
} | |||||
if (DevicePathType(node) == MEDIA_DEVICE_PATH && | if (DevicePathType(node) == MEDIA_DEVICE_PATH && | ||||
DevicePathSubType(node) == MEDIA_FILEPATH_DP) { | DevicePathSubType(node) == MEDIA_FILEPATH_DP) { | ||||
efipart_hdinfo_add_filepath(efipart_handles[i]); | STAILQ_REMOVE(&pdinfo, hd, pdinfo, pd_link); | ||||
continue; | efipart_hdinfo_add_filepath(hd, | ||||
(FILEPATH_DEVICE_PATH *)node); | |||||
goto restart; | |||||
} | } | ||||
if (DevicePathType(node) == MEDIA_DEVICE_PATH && | STAILQ_REMOVE(&pdinfo, hd, pdinfo, pd_link); | ||||
DevicePathSubType(node) == MEDIA_HARDDRIVE_DP) { | parent = efipart_find_parent(&pdinfo, hd->pd_devpath); | ||||
devpathcpy = efi_devpath_trim(devpath); | if (parent != NULL) { | ||||
if (devpathcpy == NULL) | STAILQ_REMOVE(&pdinfo, parent, pdinfo, pd_link); | ||||
continue; | efipart_hdinfo_add(parent, NULL); | ||||
tmpdevpath = devpathcpy; | } else { | ||||
status = BS->LocateDevicePath(&blkio_guid, &tmpdevpath, | parent = efipart_find_parent(&hdinfo, hd->pd_devpath); | ||||
&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 && | if (DevicePathType(node) == MEDIA_DEVICE_PATH && | ||||
DevicePathSubType(node) == MEDIA_HARDDRIVE_DP) | DevicePathSubType(node) == MEDIA_HARDDRIVE_DP && | ||||
continue; | parent == NULL) { | ||||
parent = calloc(1, sizeof(*parent)); | |||||
if (parent == NULL) { | |||||
printf("efipart_updatehd: out of memory\n"); | |||||
/* this device is lost but try again. */ | |||||
free(hd); | |||||
goto restart; | |||||
} | |||||
efipart_hdinfo_add(handle, efipart_handles[i]); | devpath = efi_devpath_trim(hd->pd_devpath); | ||||
continue; | if (devpath == NULL) { | ||||
printf("efipart_updatehd: out of memory\n"); | |||||
/* this device is lost but try again. */ | |||||
free(parent); | |||||
free(hd); | |||||
goto restart; | |||||
} | } | ||||
efipart_hdinfo_add(efipart_handles[i], NULL); | parent->pd_devpath = devpath; | ||||
status = BS->LocateDevicePath(&blkio_guid, | |||||
&parent->pd_devpath, &parent->pd_handle); | |||||
free(devpath); | |||||
if (EFI_ERROR(status)) { | |||||
printf("efipart_updatehd: error %lu\n", | |||||
EFI_ERROR_CODE(status)); | |||||
free(parent); | |||||
free(hd); | |||||
goto restart; | |||||
} | } | ||||
parent->pd_devpath = | |||||
efi_lookup_devpath(&parent->pd_handle); | |||||
efipart_hdinfo_add(parent, NULL); | |||||
} | } | ||||
efipart_hdinfo_add(hd, (HARDDRIVE_DEVICE_PATH *)node); | |||||
goto restart; | |||||
} | |||||
} | |||||
static int | static int | ||||
efipart_inithd(void) | efipart_inithd(void) | ||||
{ | { | ||||
STAILQ_INIT(&hdinfo); | |||||
efipart_updatehd(); | efipart_updatehd(); | ||||
bcache_add_dev(efiblk_pdinfo_count(&hdinfo)); | bcache_add_dev(efiblk_pdinfo_count(&hdinfo)); | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
efipart_print_common(struct devsw *dev, pdinfo_list_t *pdlist, int verbose) | efipart_print_common(struct devsw *dev, pdinfo_list_t *pdlist, int verbose) | ||||
▲ Show 20 Lines • Show All 96 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
va_list args; | va_list args; | ||||
struct disk_devdesc *dev; | struct disk_devdesc *dev; | ||||
pdinfo_t *pd; | pdinfo_t *pd; | ||||
EFI_BLOCK_IO *blkio; | EFI_BLOCK_IO *blkio; | ||||
EFI_STATUS status; | EFI_STATUS status; | ||||
va_start(args, f); | va_start(args, f); | ||||
dev = va_arg(args, struct disk_devdesc*); | dev = va_arg(args, struct disk_devdesc *); | ||||
va_end(args); | va_end(args); | ||||
if (dev == NULL) | if (dev == NULL) | ||||
return (EINVAL); | return (EINVAL); | ||||
pd = efiblk_get_pdinfo((struct devdesc *)dev); | pd = efiblk_get_pdinfo((struct devdesc *)dev); | ||||
if (pd == NULL) | if (pd == NULL) | ||||
return (EIO); | return (EIO); | ||||
▲ Show 20 Lines • Show All 175 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
struct disk_devdesc *dev = (struct disk_devdesc *)devdata; | struct disk_devdesc *dev = (struct disk_devdesc *)devdata; | ||||
pdinfo_t *pd; | pdinfo_t *pd; | ||||
EFI_BLOCK_IO *blkio; | EFI_BLOCK_IO *blkio; | ||||
uint64_t off, disk_blocks, d_offset = 0; | uint64_t off, disk_blocks, d_offset = 0; | ||||
char *blkbuf; | char *blkbuf; | ||||
size_t blkoff, blksz; | size_t blkoff, blksz; | ||||
int error; | int error; | ||||
size_t diskend, readstart; | uint64_t diskend, readstart; | ||||
if (dev == NULL || blk < 0) | if (dev == NULL || blk < 0) | ||||
return (EINVAL); | return (EINVAL); | ||||
pd = efiblk_get_pdinfo((struct devdesc *)dev); | pd = efiblk_get_pdinfo((struct devdesc *)dev); | ||||
if (pd == NULL) | if (pd == NULL) | ||||
return (EINVAL); | return (EINVAL); | ||||
Show All 40 Lines | efipart_realstrategy(void *devdata, int rw, daddr_t blk, size_t size, | ||||
if ((size % blkio->Media->BlockSize == 0) && | if ((size % blkio->Media->BlockSize == 0) && | ||||
(off % blkio->Media->BlockSize == 0)) | (off % blkio->Media->BlockSize == 0)) | ||||
return (efipart_readwrite(blkio, rw, | return (efipart_readwrite(blkio, rw, | ||||
off / blkio->Media->BlockSize, | off / blkio->Media->BlockSize, | ||||
size / blkio->Media->BlockSize, buf)); | size / blkio->Media->BlockSize, buf)); | ||||
/* | /* | ||||
* The block size of the media is not a multiple of I/O. | * The buffer size is not a multiple of the media block size. | ||||
*/ | */ | ||||
blkbuf = malloc(blkio->Media->BlockSize); | blkbuf = malloc(blkio->Media->BlockSize); | ||||
if (blkbuf == NULL) | if (blkbuf == NULL) | ||||
return (ENOMEM); | return (ENOMEM); | ||||
error = 0; | error = 0; | ||||
blk = off / blkio->Media->BlockSize; | blk = off / blkio->Media->BlockSize; | ||||
blkoff = off % blkio->Media->BlockSize; | blkoff = off % blkio->Media->BlockSize; | ||||
Show All 18 Lines |