Index: head/stand/efi/include/efidevp.h =================================================================== --- head/stand/efi/include/efidevp.h (revision 355223) +++ head/stand/efi/include/efidevp.h (revision 355224) @@ -1,503 +1,511 @@ /* $FreeBSD$ */ #ifndef _DEVPATH_H #define _DEVPATH_H /*++ Copyright (c) 1999 - 2002 Intel Corporation. All rights reserved This software and associated documentation (if any) is furnished under a license and may only be used or copied in accordance with the terms of the license. Except as permitted by such license, no part of this software or documentation may be reproduced, stored in a retrieval system, or transmitted in any form or by any means without the express written consent of Intel Corporation. Module Name: devpath.h Abstract: Defines for parsing the EFI Device Path structures Revision History --*/ // // Device Path structures - Section C // #pragma pack(1) typedef struct _EFI_DEVICE_PATH { UINT8 Type; UINT8 SubType; UINT8 Length[2]; } EFI_DEVICE_PATH; #define EFI_DP_TYPE_MASK 0x7F #define EFI_DP_TYPE_UNPACKED 0x80 #define END_DEVICE_PATH_TYPE 0x7f #define END_ENTIRE_DEVICE_PATH_SUBTYPE 0xff #define END_INSTANCE_DEVICE_PATH_SUBTYPE 0x01 #define END_DEVICE_PATH_LENGTH (sizeof(EFI_DEVICE_PATH)) #define DP_IS_END_TYPE(a) #define DP_IS_END_SUBTYPE(a) ( ((a)->SubType == END_ENTIRE_DEVICE_PATH_SUBTYPE ) #define DevicePathType(a) ( ((a)->Type) & EFI_DP_TYPE_MASK ) #define DevicePathSubType(a) ( (a)->SubType ) #define DevicePathNodeLength(a) ((size_t)(((a)->Length[0]) | ((a)->Length[1] << 8))) #define NextDevicePathNode(a) ( (EFI_DEVICE_PATH *) ( ((UINT8 *) (a)) + DevicePathNodeLength(a))) #define IsDevicePathType(a, t) ( DevicePathType(a) == t ) #define IsDevicePathEndType(a) IsDevicePathType(a, END_DEVICE_PATH_TYPE) #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 ) #define SetDevicePathNodeLength(a,l) { \ (a)->Length[0] = (UINT8) (l); \ (a)->Length[1] = (UINT8) ((l) >> 8); \ } #define SetDevicePathEndNode(a) { \ (a)->Type = END_DEVICE_PATH_TYPE; \ (a)->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; \ (a)->Length[0] = sizeof(EFI_DEVICE_PATH); \ (a)->Length[1] = 0; \ } /* * */ #define HARDWARE_DEVICE_PATH 0x01 #define HW_PCI_DP 0x01 typedef struct _PCI_DEVICE_PATH { EFI_DEVICE_PATH Header; UINT8 Function; UINT8 Device; } PCI_DEVICE_PATH; #define HW_PCCARD_DP 0x02 typedef struct _PCCARD_DEVICE_PATH { EFI_DEVICE_PATH Header; UINT8 FunctionNumber; } PCCARD_DEVICE_PATH; #define HW_MEMMAP_DP 0x03 typedef struct _MEMMAP_DEVICE_PATH { EFI_DEVICE_PATH Header; UINT32 MemoryType; EFI_PHYSICAL_ADDRESS StartingAddress; EFI_PHYSICAL_ADDRESS EndingAddress; } MEMMAP_DEVICE_PATH; #define HW_VENDOR_DP 0x04 typedef struct _VENDOR_DEVICE_PATH { EFI_DEVICE_PATH Header; EFI_GUID Guid; } VENDOR_DEVICE_PATH; #define UNKNOWN_DEVICE_GUID \ { 0xcf31fac5, 0xc24e, 0x11d2, {0x85, 0xf3, 0x0, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b} } typedef struct _UKNOWN_DEVICE_VENDOR_DP { VENDOR_DEVICE_PATH DevicePath; UINT8 LegacyDriveLetter; } UNKNOWN_DEVICE_VENDOR_DEVICE_PATH; #define HW_CONTROLLER_DP 0x05 typedef struct _CONTROLLER_DEVICE_PATH { EFI_DEVICE_PATH Header; UINT32 Controller; } CONTROLLER_DEVICE_PATH; /* * */ #define ACPI_DEVICE_PATH 0x02 #define ACPI_DP 0x01 typedef struct _ACPI_HID_DEVICE_PATH { EFI_DEVICE_PATH Header; UINT32 HID; UINT32 UID; } ACPI_HID_DEVICE_PATH; #define ACPI_EXTENDED_DP 0x02 typedef struct _ACPI_EXTENDED_HID_DEVICE_PATH { EFI_DEVICE_PATH Header; UINT32 HID; UINT32 UID; UINT32 CID; } ACPI_EXTENDED_HID_DEVICE_PATH; #define ACPI_ADR_DP 0x03 /* ACPI_ADR_DEVICE_PATH not defined */ // // EISA ID Macro // EISA ID Definition 32-bits // bits[15:0] - three character compressed ASCII EISA ID. // bits[31:16] - binary number // Compressed ASCII is 5 bits per character 0b00001 = 'A' 0b11010 = 'Z' // #define PNP_EISA_ID_CONST 0x41d0 #define EISA_ID(_Name, _Num) ((UINT32) ((_Name) | (_Num) << 16)) #define EISA_PNP_ID(_PNPId) (EISA_ID(PNP_EISA_ID_CONST, (_PNPId))) #define EFI_PNP_ID(_PNPId) (EISA_ID(PNP_EISA_ID_CONST, (_PNPId))) #define PNP_EISA_ID_MASK 0xffff #define EISA_ID_TO_NUM(_Id) ((_Id) >> 16) /* * */ #define MESSAGING_DEVICE_PATH 0x03 #define MSG_ATAPI_DP 0x01 typedef struct _ATAPI_DEVICE_PATH { EFI_DEVICE_PATH Header; UINT8 PrimarySecondary; UINT8 SlaveMaster; UINT16 Lun; } ATAPI_DEVICE_PATH; #define MSG_SCSI_DP 0x02 typedef struct _SCSI_DEVICE_PATH { EFI_DEVICE_PATH Header; UINT16 Pun; UINT16 Lun; } SCSI_DEVICE_PATH; #define MSG_FIBRECHANNEL_DP 0x03 typedef struct _FIBRECHANNEL_DEVICE_PATH { EFI_DEVICE_PATH Header; UINT32 Reserved; UINT64 WWN; UINT64 Lun; } FIBRECHANNEL_DEVICE_PATH; #define MSG_1394_DP 0x04 typedef struct _F1394_DEVICE_PATH { EFI_DEVICE_PATH Header; UINT32 Reserved; UINT64 Guid; } F1394_DEVICE_PATH; #define MSG_USB_DP 0x05 typedef struct _USB_DEVICE_PATH { EFI_DEVICE_PATH Header; UINT8 ParentPortNumber; UINT8 InterfaceNumber; } USB_DEVICE_PATH; #define MSG_USB_CLASS_DP 0x0F typedef struct _USB_CLASS_DEVICE_PATH { EFI_DEVICE_PATH Header; UINT16 VendorId; UINT16 ProductId; UINT8 DeviceClass; UINT8 DeviceSubClass; UINT8 DeviceProtocol; } USB_CLASS_DEVICE_PATH; #define MSG_I2O_DP 0x06 typedef struct _I2O_DEVICE_PATH { EFI_DEVICE_PATH Header; UINT32 Tid; } I2O_DEVICE_PATH; #define MSG_MAC_ADDR_DP 0x0b typedef struct _MAC_ADDR_DEVICE_PATH { EFI_DEVICE_PATH Header; EFI_MAC_ADDRESS MacAddress; UINT8 IfType; } MAC_ADDR_DEVICE_PATH; #define MSG_IPv4_DP 0x0c typedef struct _IPv4_DEVICE_PATH { EFI_DEVICE_PATH Header; EFI_IPv4_ADDRESS LocalIpAddress; EFI_IPv4_ADDRESS RemoteIpAddress; UINT16 LocalPort; UINT16 RemotePort; UINT16 Protocol; BOOLEAN StaticIpAddress; EFI_IPv4_ADDRESS GatewayIpAddress; EFI_IPv4_ADDRESS SubnetMask; } IPv4_DEVICE_PATH; #define MSG_IPv6_DP 0x0d typedef struct _IPv6_DEVICE_PATH { EFI_DEVICE_PATH Header; EFI_IPv6_ADDRESS LocalIpAddress; EFI_IPv6_ADDRESS RemoteIpAddress; UINT16 LocalPort; UINT16 RemotePort; UINT16 Protocol; BOOLEAN StaticIpAddress; } IPv6_DEVICE_PATH; #define MSG_INFINIBAND_DP 0x09 typedef struct _INFINIBAND_DEVICE_PATH { EFI_DEVICE_PATH Header; UINT32 ResourceFlags; UINT8 PortGid[16]; UINT64 ServiceId; UINT64 TargetPortId; UINT64 DeviceId; } INFINIBAND_DEVICE_PATH; #define INFINIBAND_RESOURCE_FLAG_IOC_SERVICE 0x01 #define INFINIBAND_RESOURCE_FLAG_EXTENDED_BOOT_ENVIRONMENT 0x02 #define INFINIBAND_RESOURCE_FLAG_CONSOLE_PROTOCOL 0x04 #define INFINIBAND_RESOURCE_FLAG_STORAGE_PROTOCOL 0x08 #define INFINIBAND_RESOURCE_FLAG_NETWORK_PROTOCOL 0x10 #define MSG_UART_DP 0x0e typedef struct _UART_DEVICE_PATH { EFI_DEVICE_PATH Header; UINT32 Reserved; UINT64 BaudRate; UINT8 DataBits; UINT8 Parity; UINT8 StopBits; } UART_DEVICE_PATH; #define MSG_VENDOR_DP 0x0A /* Use VENDOR_DEVICE_PATH struct */ #define DEVICE_PATH_MESSAGING_PC_ANSI \ { 0xe0c14753, 0xf9be, 0x11d2, {0x9a, 0x0c, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d} } #define DEVICE_PATH_MESSAGING_VT_100 \ { 0xdfa66065, 0xb419, 0x11d3, {0x9a, 0x2d, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d} } #define DEVICE_PATH_MESSAGING_VT_100_PLUS \ { 0x7baec70b, 0x57e0, 0x4c76, {0x8e, 0x87, 0x2f, 0x9e, 0x28, 0x08, 0x83, 0x43} } #define DEVICE_PATH_MESSAGING_VT_UTF8 \ { 0xad15a0d6, 0x8bec, 0x4acf, {0xa0, 0x73, 0xd0, 0x1d, 0xe7, 0x7e, 0x2d, 0x88} } +/* Device Logical Unit SubType. */ +#define MSG_DEVICE_LOGICAL_UNIT_DP 0x11 +typedef struct { + EFI_DEVICE_PATH Header; + /* Logical Unit Number for the interface. */ + UINT8 Lun; +} DEVICE_LOGICAL_UNIT_DEVICE_PATH; + #define MSG_SATA_DP 0x12 typedef struct _SATA_DEVICE_PATH { EFI_DEVICE_PATH Header; UINT16 HBAPortNumber; UINT16 PortMultiplierPortNumber; UINT16 Lun; } SATA_DEVICE_PATH; /* DNS Device Path SubType */ #define MSG_DNS_DP 0x1F typedef struct { EFI_DEVICE_PATH Header; /* Indicates the DNS server address is IPv4 or IPv6 address. */ UINT8 IsIPv6; /* Instance of the DNS server address. */ /* XXX: actually EFI_IP_ADDRESS */ EFI_IPv4_ADDRESS DnsServerIp[]; } DNS_DEVICE_PATH; /* Uniform Resource Identifiers (URI) Device Path SubType */ #define MSG_URI_DP 0x18 typedef struct { EFI_DEVICE_PATH Header; /* Instance of the URI pursuant to RFC 3986. */ CHAR8 Uri[]; } URI_DEVICE_PATH; #define MEDIA_DEVICE_PATH 0x04 #define MEDIA_HARDDRIVE_DP 0x01 typedef struct _HARDDRIVE_DEVICE_PATH { EFI_DEVICE_PATH Header; UINT32 PartitionNumber; UINT64 PartitionStart; UINT64 PartitionSize; UINT8 Signature[16]; UINT8 MBRType; UINT8 SignatureType; } HARDDRIVE_DEVICE_PATH; #define MBR_TYPE_PCAT 0x01 #define MBR_TYPE_EFI_PARTITION_TABLE_HEADER 0x02 #define SIGNATURE_TYPE_MBR 0x01 #define SIGNATURE_TYPE_GUID 0x02 #define MEDIA_CDROM_DP 0x02 typedef struct _CDROM_DEVICE_PATH { EFI_DEVICE_PATH Header; UINT32 BootEntry; UINT64 PartitionStart; UINT64 PartitionSize; } CDROM_DEVICE_PATH; #define MEDIA_VENDOR_DP 0x03 /* Use VENDOR_DEVICE_PATH struct */ #define MEDIA_FILEPATH_DP 0x04 typedef struct _FILEPATH_DEVICE_PATH { EFI_DEVICE_PATH Header; CHAR16 PathName[1]; } FILEPATH_DEVICE_PATH; #define SIZE_OF_FILEPATH_DEVICE_PATH EFI_FIELD_OFFSET(FILEPATH_DEVICE_PATH,PathName) #define MEDIA_PROTOCOL_DP 0x05 typedef struct _MEDIA_PROTOCOL_DEVICE_PATH { EFI_DEVICE_PATH Header; EFI_GUID Protocol; } MEDIA_PROTOCOL_DEVICE_PATH; #define BBS_DEVICE_PATH 0x05 #define BBS_BBS_DP 0x01 typedef struct _BBS_BBS_DEVICE_PATH { EFI_DEVICE_PATH Header; UINT16 DeviceType; UINT16 StatusFlag; CHAR8 String[1]; } BBS_BBS_DEVICE_PATH; /* DeviceType definitions - from BBS specification */ #define BBS_TYPE_FLOPPY 0x01 #define BBS_TYPE_HARDDRIVE 0x02 #define BBS_TYPE_CDROM 0x03 #define BBS_TYPE_PCMCIA 0x04 #define BBS_TYPE_USB 0x05 #define BBS_TYPE_EMBEDDED_NETWORK 0x06 #define BBS_TYPE_DEV 0x80 #define BBS_TYPE_UNKNOWN 0xFF typedef union { EFI_DEVICE_PATH DevPath; PCI_DEVICE_PATH Pci; PCCARD_DEVICE_PATH PcCard; MEMMAP_DEVICE_PATH MemMap; VENDOR_DEVICE_PATH Vendor; UNKNOWN_DEVICE_VENDOR_DEVICE_PATH UnknownVendor; CONTROLLER_DEVICE_PATH Controller; ACPI_HID_DEVICE_PATH Acpi; ATAPI_DEVICE_PATH Atapi; SCSI_DEVICE_PATH Scsi; FIBRECHANNEL_DEVICE_PATH FibreChannel; F1394_DEVICE_PATH F1394; USB_DEVICE_PATH Usb; USB_CLASS_DEVICE_PATH UsbClass; I2O_DEVICE_PATH I2O; MAC_ADDR_DEVICE_PATH MacAddr; IPv4_DEVICE_PATH Ipv4; IPv6_DEVICE_PATH Ipv6; INFINIBAND_DEVICE_PATH InfiniBand; UART_DEVICE_PATH Uart; HARDDRIVE_DEVICE_PATH HardDrive; CDROM_DEVICE_PATH CD; FILEPATH_DEVICE_PATH FilePath; MEDIA_PROTOCOL_DEVICE_PATH MediaProtocol; BBS_BBS_DEVICE_PATH Bbs; } EFI_DEV_PATH; typedef union { EFI_DEVICE_PATH *DevPath; PCI_DEVICE_PATH *Pci; PCCARD_DEVICE_PATH *PcCard; MEMMAP_DEVICE_PATH *MemMap; VENDOR_DEVICE_PATH *Vendor; UNKNOWN_DEVICE_VENDOR_DEVICE_PATH *UnknownVendor; CONTROLLER_DEVICE_PATH *Controller; ACPI_HID_DEVICE_PATH *Acpi; ACPI_EXTENDED_HID_DEVICE_PATH *ExtendedAcpi; ATAPI_DEVICE_PATH *Atapi; SCSI_DEVICE_PATH *Scsi; FIBRECHANNEL_DEVICE_PATH *FibreChannel; F1394_DEVICE_PATH *F1394; USB_DEVICE_PATH *Usb; USB_CLASS_DEVICE_PATH *UsbClass; I2O_DEVICE_PATH *I2O; MAC_ADDR_DEVICE_PATH *MacAddr; IPv4_DEVICE_PATH *Ipv4; IPv6_DEVICE_PATH *Ipv6; INFINIBAND_DEVICE_PATH *InfiniBand; UART_DEVICE_PATH *Uart; HARDDRIVE_DEVICE_PATH *HardDrive; FILEPATH_DEVICE_PATH *FilePath; MEDIA_PROTOCOL_DEVICE_PATH *MediaProtocol; CDROM_DEVICE_PATH *CD; BBS_BBS_DEVICE_PATH *Bbs; } EFI_DEV_PATH_PTR; #define EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID \ { 0xbc62157e, 0x3e33, 0x4fec, { 0x99, 0x20, 0x2d, 0x3b, 0x36, 0xd7, 0x50, 0xdf } } #define EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID \ { 0x8b843e20, 0x8132, 0x4852, { 0x90, 0xcc, 0x55, 0x1a, 0x4e, 0x4a, 0x7f, 0x1c } } #define EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL_GUID \ { 0x05c99a21, 0xc70f, 0x4ad2, { 0x8a, 0x5f, 0x35, 0xdf, 0x33, 0x43, 0xf5, 0x1e } } INTERFACE_DECL(_EFI_DEVICE_PATH_PROTOCOL); typedef CHAR16* (EFIAPI *EFI_DEVICE_PATH_TO_TEXT_NODE) ( IN struct _EFI_DEVICE_PATH *This, IN BOOLEAN DisplayOnly, IN BOOLEAN AllowShortCuts ); typedef CHAR16* (EFIAPI *EFI_DEVICE_PATH_TO_TEXT_PATH) ( IN struct _EFI_DEVICE_PATH *This, IN BOOLEAN DisplayOnly, IN BOOLEAN AllowShortCuts ); typedef struct _EFI_DEVICE_PATH_TO_TEXT_PROTOCOL { EFI_DEVICE_PATH_TO_TEXT_NODE ConvertDeviceNodeToText; EFI_DEVICE_PATH_TO_TEXT_PATH ConvertDevicePathToText; } EFI_DEVICE_PATH_TO_TEXT_PROTOCOL; typedef struct _EFI_DEVICE_PATH* (EFIAPI *EFI_DEVICE_PATH_FROM_TEXT_NODE) ( IN CONST CHAR16* TextDeviceNode ); typedef struct _EFI_DEVICE_PATH* (EFIAPI *EFI_DEVICE_PATH_FROM_TEXT_PATH) ( IN CONST CHAR16* TextDevicePath ); typedef struct _EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL { EFI_DEVICE_PATH_FROM_TEXT_NODE ConvertTextToDeviceNode; EFI_DEVICE_PATH_FROM_TEXT_PATH ConvertTextToDevicePath; } EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL; #pragma pack() #endif Index: head/stand/efi/include/efilib.h =================================================================== --- head/stand/efi/include/efilib.h (revision 355223) +++ head/stand/efi/include/efilib.h (revision 355224) @@ -1,151 +1,152 @@ /*- * Copyright (c) 2000 Doug Rabson * Copyright (c) 2006 Marcel Moolenaar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _LOADER_EFILIB_H #define _LOADER_EFILIB_H #include #include #include extern EFI_HANDLE IH; extern EFI_SYSTEM_TABLE *ST; extern EFI_BOOT_SERVICES *BS; extern EFI_RUNTIME_SERVICES *RS; extern struct devsw efipart_fddev; extern struct devsw efipart_cddev; extern struct devsw efipart_hddev; extern struct devsw efihttp_dev; 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; /* list of partitions */ EFI_HANDLE pd_handle; EFI_HANDLE pd_alias; EFI_DEVICE_PATH *pd_devpath; EFI_BLOCK_IO *pd_blkio; uint32_t pd_unit; /* unit number */ uint32_t pd_open; /* reference counter */ void *pd_bcache; /* buffer cache data */ struct pdinfo *pd_parent; /* Linked items (eg partitions) */ struct devsw *pd_devsw; /* Back pointer to devsw */ } pdinfo_t; pdinfo_list_t *efiblk_get_pdinfo_list(struct devsw *dev); pdinfo_t *efiblk_get_pdinfo(struct devdesc *dev); pdinfo_t *efiblk_get_pdinfo_by_handle(EFI_HANDLE h); pdinfo_t *efiblk_get_pdinfo_by_device_path(EFI_DEVICE_PATH *path); void *efi_get_table(EFI_GUID *tbl); EFI_STATUS OpenProtocolByHandle(EFI_HANDLE, EFI_GUID *, void **); int efi_getdev(void **vdev, const char *devspec, const char **path); char *efi_fmtdev(void *vdev); int efi_setcurrdev(struct env_var *ev, int flags, const void *value); int efi_register_handles(struct devsw *, EFI_HANDLE *, EFI_HANDLE *, int); EFI_HANDLE efi_find_handle(struct devsw *, int); int efi_handle_lookup(EFI_HANDLE, struct devsw **, int *, uint64_t *); int efi_handle_update_dev(EFI_HANDLE, struct devsw *, int, uint64_t); EFI_DEVICE_PATH *efi_lookup_image_devpath(EFI_HANDLE); EFI_DEVICE_PATH *efi_lookup_devpath(EFI_HANDLE); +void efi_close_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 *); bool efi_devpath_match(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *); bool efi_devpath_match_node(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *); bool efi_devpath_is_prefix(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *); CHAR16 *efi_devpath_name(EFI_DEVICE_PATH *); void efi_free_devpath_name(CHAR16 *); bool efi_devpath_same_disk(EFI_DEVICE_PATH *, EFI_DEVICE_PATH *); EFI_DEVICE_PATH *efi_devpath_to_media_path(EFI_DEVICE_PATH *); UINTN efi_devpath_length(EFI_DEVICE_PATH *); EFI_DEVICE_PATH *efi_name_to_devpath(const char *path); EFI_DEVICE_PATH *efi_name_to_devpath16(CHAR16 *path); void efi_devpath_free(EFI_DEVICE_PATH *dp); EFI_HANDLE efi_devpath_to_handle(EFI_DEVICE_PATH *path, EFI_HANDLE *handles, unsigned nhandles); int efi_status_to_errno(EFI_STATUS); EFI_STATUS errno_to_efi_status(int errno); void efi_time_init(void); void efi_time_fini(void); int parse_uefi_con_out(void); bool efi_cons_update_mode(void); EFI_STATUS efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE* Xsystab); EFI_STATUS main(int argc, CHAR16 *argv[]); void efi_exit(EFI_STATUS status) __dead2; void delay(int usecs); /* EFI environment initialization. */ void efi_init_environment(void); /* EFI Memory type strings. */ const char *efi_memory_type(EFI_MEMORY_TYPE); /* CHAR16 utility functions. */ int wcscmp(CHAR16 *, CHAR16 *); void cpy8to16(const char *, CHAR16 *, size_t); void cpy16to8(const CHAR16 *, char *, size_t); /* * Routines for interacting with EFI's env vars in a more unix-like * way than the standard APIs. In addition, convenience routines for * the loader setting / getting FreeBSD specific variables. */ EFI_STATUS efi_delenv(EFI_GUID *guid, const char *varname); EFI_STATUS efi_freebsd_delenv(const char *varname); EFI_STATUS efi_freebsd_getenv(const char *v, void *data, __size_t *len); EFI_STATUS efi_getenv(EFI_GUID *g, const char *v, void *data, __size_t *len); EFI_STATUS efi_global_getenv(const char *v, void *data, __size_t *len); EFI_STATUS efi_setenv(EFI_GUID *guid, const char *varname, UINT32 attr, void *data, __size_t len); EFI_STATUS efi_setenv_freebsd_wcs(const char *varname, CHAR16 *valstr); /* guids and names */ bool efi_guid_to_str(const EFI_GUID *, char **); bool efi_str_to_guid(const char *, EFI_GUID *); bool efi_name_to_guid(const char *, EFI_GUID *); bool efi_guid_to_name(EFI_GUID *, char **); /* efipart.c */ int efipart_inithandles(void); #endif /* _LOADER_EFILIB_H */ Index: head/stand/efi/libefi/devpath.c =================================================================== --- head/stand/efi/libefi/devpath.c (revision 355223) +++ head/stand/efi/libefi/devpath.c (revision 355224) @@ -1,742 +1,752 @@ /*- * Copyright (c) 2016 John Baldwin * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include static EFI_GUID ImageDevicePathGUID = EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID; static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL; static EFI_GUID DevicePathToTextGUID = EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID; static EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *toTextProtocol; static EFI_GUID DevicePathFromTextGUID = EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL_GUID; static EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL *fromTextProtocol; EFI_DEVICE_PATH * efi_lookup_image_devpath(EFI_HANDLE handle) { EFI_DEVICE_PATH *devpath; EFI_STATUS status; status = OpenProtocolByHandle(handle, &ImageDevicePathGUID, (void **)&devpath); if (EFI_ERROR(status)) devpath = NULL; return (devpath); } EFI_DEVICE_PATH * efi_lookup_devpath(EFI_HANDLE handle) { EFI_DEVICE_PATH *devpath; EFI_STATUS status; status = OpenProtocolByHandle(handle, &DevicePathGUID, (void **)&devpath); if (EFI_ERROR(status)) devpath = NULL; return (devpath); } +void +efi_close_devpath(EFI_HANDLE handle) +{ + EFI_STATUS status; + + status = BS->CloseProtocol(handle, &DevicePathGUID, IH, NULL); + if (EFI_ERROR(status)) + printf("CloseProtocol error: %lu\n", EFI_ERROR_CODE(status)); +} + static char * efi_make_tail(char *suffix) { char *tail; tail = NULL; if (suffix != NULL) (void)asprintf(&tail, "/%s", suffix); else tail = strdup(""); return (tail); } typedef struct { EFI_DEVICE_PATH Header; EFI_GUID Guid; UINT8 VendorDefinedData[1]; } __packed VENDOR_DEVICE_PATH_WITH_DATA; static char * efi_vendor_path(const char *type, VENDOR_DEVICE_PATH *node, char *suffix) { uint32_t size = DevicePathNodeLength(&node->Header) - sizeof(*node); VENDOR_DEVICE_PATH_WITH_DATA *dp = (VENDOR_DEVICE_PATH_WITH_DATA *)node; char *name, *tail, *head; char *uuid; int rv; uuid_to_string((const uuid_t *)(void *)&node->Guid, &uuid, &rv); if (rv != uuid_s_ok) return (NULL); tail = efi_make_tail(suffix); rv = asprintf(&head, "%sVendor(%s)[%x:", type, uuid, size); free(uuid); if (rv < 0) return (NULL); if (DevicePathNodeLength(&node->Header) > sizeof(*node)) { for (uint32_t i = 0; i < size; i++) { rv = asprintf(&name, "%s%02x", head, dp->VendorDefinedData[i]); if (rv < 0) { free(tail); free(head); return (NULL); } free(head); head = name; } } if (asprintf(&name, "%s]%s", head, tail) < 0) name = NULL; free(head); free(tail); return (name); } static char * efi_hw_dev_path(EFI_DEVICE_PATH *node, char *suffix) { uint8_t subtype = DevicePathSubType(node); char *name, *tail; tail = efi_make_tail(suffix); switch (subtype) { case HW_PCI_DP: if (asprintf(&name, "Pci(%x,%x)%s", ((PCI_DEVICE_PATH *)node)->Function, ((PCI_DEVICE_PATH *)node)->Device, tail) < 0) name = NULL; break; case HW_PCCARD_DP: if (asprintf(&name, "PCCARD(%x)%s", ((PCCARD_DEVICE_PATH *)node)->FunctionNumber, tail) < 0) name = NULL; break; case HW_MEMMAP_DP: if (asprintf(&name, "MMap(%x,%" PRIx64 ",%" PRIx64 ")%s", ((MEMMAP_DEVICE_PATH *)node)->MemoryType, ((MEMMAP_DEVICE_PATH *)node)->StartingAddress, ((MEMMAP_DEVICE_PATH *)node)->EndingAddress, tail) < 0) name = NULL; break; case HW_VENDOR_DP: name = efi_vendor_path("Hardware", (VENDOR_DEVICE_PATH *)node, tail); break; case HW_CONTROLLER_DP: if (asprintf(&name, "Ctrl(%x)%s", ((CONTROLLER_DEVICE_PATH *)node)->Controller, tail) < 0) name = NULL; break; default: if (asprintf(&name, "UnknownHW(%x)%s", subtype, tail) < 0) name = NULL; break; } free(tail); return (name); } static char * efi_acpi_dev_path(EFI_DEVICE_PATH *node, char *suffix) { uint8_t subtype = DevicePathSubType(node); ACPI_HID_DEVICE_PATH *acpi = (ACPI_HID_DEVICE_PATH *)node; char *name, *tail; tail = efi_make_tail(suffix); switch (subtype) { case ACPI_DP: if ((acpi->HID & PNP_EISA_ID_MASK) == PNP_EISA_ID_CONST) { switch (EISA_ID_TO_NUM (acpi->HID)) { case 0x0a03: if (asprintf(&name, "PciRoot(%x)%s", acpi->UID, tail) < 0) name = NULL; break; case 0x0a08: if (asprintf(&name, "PcieRoot(%x)%s", acpi->UID, tail) < 0) name = NULL; break; case 0x0604: if (asprintf(&name, "Floppy(%x)%s", acpi->UID, tail) < 0) name = NULL; break; case 0x0301: if (asprintf(&name, "Keyboard(%x)%s", acpi->UID, tail) < 0) name = NULL; break; case 0x0501: if (asprintf(&name, "Serial(%x)%s", acpi->UID, tail) < 0) name = NULL; break; case 0x0401: if (asprintf(&name, "ParallelPort(%x)%s", acpi->UID, tail) < 0) name = NULL; break; default: if (asprintf(&name, "Acpi(PNP%04x,%x)%s", EISA_ID_TO_NUM(acpi->HID), acpi->UID, tail) < 0) name = NULL; break; } } else { if (asprintf(&name, "Acpi(%08x,%x)%s", acpi->HID, acpi->UID, tail) < 0) name = NULL; } break; case ACPI_EXTENDED_DP: default: if (asprintf(&name, "UnknownACPI(%x)%s", subtype, tail) < 0) name = NULL; break; } free(tail); return (name); } static char * efi_messaging_dev_path(EFI_DEVICE_PATH *node, char *suffix) { uint8_t subtype = DevicePathSubType(node); char *name; char *tail; tail = efi_make_tail(suffix); switch (subtype) { case MSG_ATAPI_DP: if (asprintf(&name, "ATA(%s,%s,%x)%s", ((ATAPI_DEVICE_PATH *)node)->PrimarySecondary == 1 ? "Secondary" : "Primary", ((ATAPI_DEVICE_PATH *)node)->SlaveMaster == 1 ? "Slave" : "Master", ((ATAPI_DEVICE_PATH *)node)->Lun, tail) < 0) name = NULL; break; case MSG_SCSI_DP: if (asprintf(&name, "SCSI(%x,%x)%s", ((SCSI_DEVICE_PATH *)node)->Pun, ((SCSI_DEVICE_PATH *)node)->Lun, tail) < 0) name = NULL; break; case MSG_FIBRECHANNEL_DP: if (asprintf(&name, "Fibre(%" PRIx64 ",%" PRIx64 ")%s", ((FIBRECHANNEL_DEVICE_PATH *)node)->WWN, ((FIBRECHANNEL_DEVICE_PATH *)node)->Lun, tail) < 0) name = NULL; break; case MSG_1394_DP: if (asprintf(&name, "I1394(%016" PRIx64 ")%s", ((F1394_DEVICE_PATH *)node)->Guid, tail) < 0) name = NULL; break; case MSG_USB_DP: if (asprintf(&name, "USB(%x,%x)%s", ((USB_DEVICE_PATH *)node)->ParentPortNumber, ((USB_DEVICE_PATH *)node)->InterfaceNumber, tail) < 0) name = NULL; break; case MSG_USB_CLASS_DP: if (asprintf(&name, "UsbClass(%x,%x,%x,%x,%x)%s", ((USB_CLASS_DEVICE_PATH *)node)->VendorId, ((USB_CLASS_DEVICE_PATH *)node)->ProductId, ((USB_CLASS_DEVICE_PATH *)node)->DeviceClass, ((USB_CLASS_DEVICE_PATH *)node)->DeviceSubClass, ((USB_CLASS_DEVICE_PATH *)node)->DeviceProtocol, tail) < 0) name = NULL; break; case MSG_MAC_ADDR_DP: if (asprintf(&name, "MAC(%02x:%02x:%02x:%02x:%02x:%02x,%x)%s", ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[0], ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[1], ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[2], ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[3], ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[4], ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[5], ((MAC_ADDR_DEVICE_PATH *)node)->IfType, tail) < 0) name = NULL; break; case MSG_VENDOR_DP: name = efi_vendor_path("Messaging", (VENDOR_DEVICE_PATH *)node, tail); break; case MSG_UART_DP: if (asprintf(&name, "UART(%" PRIu64 ",%u,%x,%x)%s", ((UART_DEVICE_PATH *)node)->BaudRate, ((UART_DEVICE_PATH *)node)->DataBits, ((UART_DEVICE_PATH *)node)->Parity, ((UART_DEVICE_PATH *)node)->StopBits, tail) < 0) name = NULL; break; case MSG_SATA_DP: if (asprintf(&name, "Sata(%x,%x,%x)%s", ((SATA_DEVICE_PATH *)node)->HBAPortNumber, ((SATA_DEVICE_PATH *)node)->PortMultiplierPortNumber, ((SATA_DEVICE_PATH *)node)->Lun, tail) < 0) name = NULL; break; default: if (asprintf(&name, "UnknownMessaging(%x)%s", subtype, tail) < 0) name = NULL; break; } free(tail); return (name); } static char * efi_media_dev_path(EFI_DEVICE_PATH *node, char *suffix) { uint8_t subtype = DevicePathSubType(node); HARDDRIVE_DEVICE_PATH *hd; char *name; char *str; char *tail; int rv; tail = efi_make_tail(suffix); name = NULL; switch (subtype) { case MEDIA_HARDDRIVE_DP: hd = (HARDDRIVE_DEVICE_PATH *)node; switch (hd->SignatureType) { case SIGNATURE_TYPE_MBR: if (asprintf(&name, "HD(%d,MBR,%08x,%" PRIx64 ",%" PRIx64 ")%s", hd->PartitionNumber, *((uint32_t *)(uintptr_t)&hd->Signature[0]), hd->PartitionStart, hd->PartitionSize, tail) < 0) name = NULL; break; case SIGNATURE_TYPE_GUID: name = NULL; uuid_to_string((const uuid_t *)(void *) &hd->Signature[0], &str, &rv); if (rv != uuid_s_ok) break; rv = asprintf(&name, "HD(%d,GPT,%s,%" PRIx64 ",%" PRIx64 ")%s", hd->PartitionNumber, str, hd->PartitionStart, hd->PartitionSize, tail); free(str); break; default: if (asprintf(&name, "HD(%d,%d,0)%s", hd->PartitionNumber, hd->SignatureType, tail) < 0) { name = NULL; } break; } break; case MEDIA_CDROM_DP: if (asprintf(&name, "CD(%x,%" PRIx64 ",%" PRIx64 ")%s", ((CDROM_DEVICE_PATH *)node)->BootEntry, ((CDROM_DEVICE_PATH *)node)->PartitionStart, ((CDROM_DEVICE_PATH *)node)->PartitionSize, tail) < 0) { name = NULL; } break; case MEDIA_VENDOR_DP: name = efi_vendor_path("Media", (VENDOR_DEVICE_PATH *)node, tail); break; case MEDIA_FILEPATH_DP: name = NULL; str = NULL; if (ucs2_to_utf8(((FILEPATH_DEVICE_PATH *)node)->PathName, &str) == 0) { (void)asprintf(&name, "%s%s", str, tail); free(str); } break; case MEDIA_PROTOCOL_DP: name = NULL; uuid_to_string((const uuid_t *)(void *) &((MEDIA_PROTOCOL_DEVICE_PATH *)node)->Protocol, &str, &rv); if (rv != uuid_s_ok) break; rv = asprintf(&name, "Protocol(%s)%s", str, tail); free(str); break; default: if (asprintf(&name, "UnknownMedia(%x)%s", subtype, tail) < 0) name = NULL; } free(tail); return (name); } static char * efi_translate_devpath(EFI_DEVICE_PATH *devpath) { EFI_DEVICE_PATH *dp = NextDevicePathNode(devpath); char *name, *ptr; uint8_t type; if (!IsDevicePathEnd(devpath)) name = efi_translate_devpath(dp); else return (NULL); ptr = NULL; type = DevicePathType(devpath); switch (type) { case HARDWARE_DEVICE_PATH: ptr = efi_hw_dev_path(devpath, name); break; case ACPI_DEVICE_PATH: ptr = efi_acpi_dev_path(devpath, name); break; case MESSAGING_DEVICE_PATH: ptr = efi_messaging_dev_path(devpath, name); break; case MEDIA_DEVICE_PATH: ptr = efi_media_dev_path(devpath, name); break; case BBS_DEVICE_PATH: default: if (asprintf(&ptr, "UnknownPath(%x)%s", type, name? name : "") < 0) ptr = NULL; break; } if (ptr != NULL) { free(name); name = ptr; } return (name); } static CHAR16 * efi_devpath_to_name(EFI_DEVICE_PATH *devpath) { char *name = NULL; CHAR16 *ptr = NULL; size_t len; int rv; name = efi_translate_devpath(devpath); if (name == NULL) return (NULL); /* * We need to return memory from AllocatePool, so it can be freed * with FreePool() in efi_free_devpath_name(). */ rv = utf8_to_ucs2(name, &ptr, &len); free(name); if (rv == 0) { CHAR16 *out = NULL; EFI_STATUS status; status = BS->AllocatePool(EfiLoaderData, len, (void **)&out); if (EFI_ERROR(status)) { free(ptr); return (out); } memcpy(out, ptr, len); free(ptr); ptr = out; } return (ptr); } CHAR16 * efi_devpath_name(EFI_DEVICE_PATH *devpath) { EFI_STATUS status; if (devpath == NULL) return (NULL); if (toTextProtocol == NULL) { status = BS->LocateProtocol(&DevicePathToTextGUID, NULL, (VOID **)&toTextProtocol); if (EFI_ERROR(status)) toTextProtocol = NULL; } if (toTextProtocol == NULL) return (efi_devpath_to_name(devpath)); return (toTextProtocol->ConvertDevicePathToText(devpath, TRUE, TRUE)); } void efi_free_devpath_name(CHAR16 *text) { if (text != NULL) BS->FreePool(text); } EFI_DEVICE_PATH * efi_name_to_devpath(const char *path) { EFI_DEVICE_PATH *devpath; CHAR16 *uv; size_t ul; uv = NULL; if (utf8_to_ucs2(path, &uv, &ul) != 0) return (NULL); devpath = efi_name_to_devpath16(uv); free(uv); return (devpath); } EFI_DEVICE_PATH * efi_name_to_devpath16(CHAR16 *path) { EFI_STATUS status; if (path == NULL) return (NULL); if (fromTextProtocol == NULL) { status = BS->LocateProtocol(&DevicePathFromTextGUID, NULL, (VOID **)&fromTextProtocol); if (EFI_ERROR(status)) fromTextProtocol = NULL; } if (fromTextProtocol == NULL) return (NULL); return (fromTextProtocol->ConvertTextToDevicePath(path)); } void efi_devpath_free(EFI_DEVICE_PATH *devpath) { BS->FreePool(devpath); } EFI_DEVICE_PATH * efi_devpath_last_node(EFI_DEVICE_PATH *devpath) { if (IsDevicePathEnd(devpath)) return (NULL); while (!IsDevicePathEnd(NextDevicePathNode(devpath))) devpath = NextDevicePathNode(devpath); return (devpath); } EFI_DEVICE_PATH * efi_devpath_trim(EFI_DEVICE_PATH *devpath) { EFI_DEVICE_PATH *node, *copy; size_t prefix, len; if ((node = efi_devpath_last_node(devpath)) == NULL) return (NULL); prefix = (UINT8 *)node - (UINT8 *)devpath; if (prefix == 0) return (NULL); len = prefix + DevicePathNodeLength(NextDevicePathNode(node)); copy = malloc(len); if (copy != NULL) { memcpy(copy, devpath, prefix); node = (EFI_DEVICE_PATH *)((UINT8 *)copy + prefix); SetDevicePathEndNode(node); } return (copy); } EFI_HANDLE efi_devpath_handle(EFI_DEVICE_PATH *devpath) { EFI_STATUS status; EFI_HANDLE h; /* * There isn't a standard way to locate a handle for a given * device path. However, querying the EFI_DEVICE_PATH protocol * for a given device path should give us a handle for the * closest node in the path to the end that is valid. */ status = BS->LocateDevicePath(&DevicePathGUID, &devpath, &h); if (EFI_ERROR(status)) return (NULL); return (h); } bool efi_devpath_match_node(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2) { size_t len; if (devpath1 == NULL || devpath2 == NULL) return (false); if (DevicePathType(devpath1) != DevicePathType(devpath2) || DevicePathSubType(devpath1) != DevicePathSubType(devpath2)) return (false); len = DevicePathNodeLength(devpath1); if (len != DevicePathNodeLength(devpath2)) return (false); if (memcmp(devpath1, devpath2, len) != 0) return (false); return (true); } static bool _efi_devpath_match(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2, bool ignore_media) { if (devpath1 == NULL || devpath2 == NULL) return (false); while (true) { if (ignore_media && IsDevicePathType(devpath1, MEDIA_DEVICE_PATH) && IsDevicePathType(devpath2, MEDIA_DEVICE_PATH)) return (true); if (!efi_devpath_match_node(devpath1, devpath2)) return false; if (IsDevicePathEnd(devpath1)) break; devpath1 = NextDevicePathNode(devpath1); devpath2 = NextDevicePathNode(devpath2); } return (true); } /* * Are two devpaths identical? */ bool efi_devpath_match(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2) { return _efi_devpath_match(devpath1, devpath2, false); } /* * Like efi_devpath_match, but stops at when we hit the media device * path node that specifies the partition information. If we match * up to that point, then we're on the same disk. */ bool efi_devpath_same_disk(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2) { return _efi_devpath_match(devpath1, devpath2, true); } bool efi_devpath_is_prefix(EFI_DEVICE_PATH *prefix, EFI_DEVICE_PATH *path) { size_t len; if (prefix == NULL || path == NULL) return (false); while (1) { if (IsDevicePathEnd(prefix)) break; if (DevicePathType(prefix) != DevicePathType(path) || DevicePathSubType(prefix) != DevicePathSubType(path)) return (false); len = DevicePathNodeLength(prefix); if (len != DevicePathNodeLength(path)) return (false); if (memcmp(prefix, path, len) != 0) return (false); prefix = NextDevicePathNode(prefix); path = NextDevicePathNode(path); } return (true); } /* * Skip over the 'prefix' part of path and return the part of the path * that starts with the first node that's a MEDIA_DEVICE_PATH. */ EFI_DEVICE_PATH * efi_devpath_to_media_path(EFI_DEVICE_PATH *path) { while (!IsDevicePathEnd(path)) { if (DevicePathType(path) == MEDIA_DEVICE_PATH) return (path); path = NextDevicePathNode(path); } return (NULL); } UINTN efi_devpath_length(EFI_DEVICE_PATH *path) { EFI_DEVICE_PATH *start = path; while (!IsDevicePathEnd(path)) path = NextDevicePathNode(path); return ((UINTN)path - (UINTN)start) + DevicePathNodeLength(path); } EFI_HANDLE efi_devpath_to_handle(EFI_DEVICE_PATH *path, EFI_HANDLE *handles, unsigned nhandles) { unsigned i; EFI_DEVICE_PATH *media, *devpath; EFI_HANDLE h; media = efi_devpath_to_media_path(path); if (media == NULL) return (NULL); for (i = 0; i < nhandles; i++) { h = handles[i]; devpath = efi_lookup_devpath(h); if (devpath == NULL) continue; if (!efi_devpath_match_node(media, efi_devpath_to_media_path(devpath))) continue; return (h); } return (NULL); } Index: head/stand/efi/libefi/efipart.c =================================================================== --- head/stand/efi/libefi/efipart.c (revision 355223) +++ head/stand/efi/libefi/efipart.c (revision 355224) @@ -1,1199 +1,1229 @@ /*- * Copyright (c) 2010 Marcel Moolenaar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include static EFI_GUID blkio_guid = BLOCK_IO_PROTOCOL; +typedef bool (*pd_test_cb_t)(pdinfo_t *, pdinfo_t *); static int efipart_initfd(void); static int efipart_initcd(void); static int efipart_inithd(void); +static void efipart_cdinfo_add(pdinfo_t *); 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_ioctl(struct open_file *, u_long, void *); static int efipart_printfd(int); static int efipart_printcd(int); static int efipart_printhd(int); /* EISA PNP ID's for floppy controllers */ #define PNP0604 0x604 #define PNP0700 0x700 #define PNP0701 0x701 /* Bounce buffer max size */ #define BIO_BUFFER_SIZE 0x4000 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 = efipart_ioctl, .dv_print = efipart_printfd, .dv_cleanup = NULL }; 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 }; 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 = STAILQ_HEAD_INITIALIZER(fdinfo); static pdinfo_list_t cdinfo = STAILQ_HEAD_INITIALIZER(cdinfo); static pdinfo_list_t hdinfo = STAILQ_HEAD_INITIALIZER(hdinfo); /* * 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 * 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); } /* XXX this gets called way way too often, investigate */ pdinfo_t * efiblk_get_pdinfo(struct devdesc *dev) { pdinfo_list_t *pdi; pdinfo_t *pd = NULL; pdi = efiblk_get_pdinfo_list(dev->d_dev); if (pdi == NULL) return (pd); STAILQ_FOREACH(pd, pdi, pd_link) { if (pd->pd_unit == dev->d_unit) return (pd); } return (pd); } pdinfo_t * efiblk_get_pdinfo_by_device_path(EFI_DEVICE_PATH *path) { EFI_HANDLE h; EFI_STATUS status; EFI_DEVICE_PATH *devp = path; status = BS->LocateDevicePath(&blkio_guid, &devp, &h); if (EFI_ERROR(status)) return (NULL); return (efiblk_get_pdinfo_by_handle(h)); } static bool same_handle(pdinfo_t *pd, EFI_HANDLE h) { return (pd->pd_handle == h || pd->pd_alias == h); } pdinfo_t * efiblk_get_pdinfo_by_handle(EFI_HANDLE h) { pdinfo_t *dp, *pp; /* * Check hard disks, then cd, then floppy */ STAILQ_FOREACH(dp, &hdinfo, pd_link) { if (same_handle(dp, h)) return (dp); STAILQ_FOREACH(pp, &dp->pd_part, pd_link) { if (same_handle(pp, h)) return (pp); } } STAILQ_FOREACH(dp, &cdinfo, pd_link) { if (same_handle(dp, h)) return (dp); STAILQ_FOREACH(pp, &dp->pd_part, pd_link) { if (same_handle(pp, h)) return (pp); } } STAILQ_FOREACH(dp, &fdinfo, pd_link) { if (same_handle(dp, h)) return (dp); } return (NULL); } static int efiblk_pdinfo_count(pdinfo_list_t *pdi) { pdinfo_t *pd; int i = 0; STAILQ_FOREACH(pd, pdi, pd_link) { i++; } return (i); } +static pdinfo_t * +efipart_find_parent(pdinfo_list_t *pdi, EFI_DEVICE_PATH *devpath) +{ + pdinfo_t *pd; + EFI_DEVICE_PATH *parent; + + /* We want to find direct parent */ + parent = efi_devpath_trim(devpath); + /* We should not get out of memory here but be careful. */ + if (parent == NULL) + return (NULL); + + STAILQ_FOREACH(pd, pdi, pd_link) { + /* We must have exact match. */ + if (efi_devpath_match(pd->pd_devpath, parent)) + break; + } + free(parent); + return (pd); +} + +/* + * Return true when we should ignore this device. + */ +static bool +efipart_ignore_device(EFI_HANDLE h, EFI_BLOCK_IO *blkio, + EFI_DEVICE_PATH *devpath) +{ + EFI_DEVICE_PATH *node, *parent; + + /* + * We assume the block size 512 or greater power of 2. + * Also skip devices with block size > 64k (16 is max + * ashift supported by zfs). + * iPXE is known to insert stub BLOCK IO device with + * BlockSize 1. + */ + if (blkio->Media->BlockSize < 512 || + blkio->Media->BlockSize > (1 << 16) || + !powerof2(blkio->Media->BlockSize)) { + efi_close_devpath(h); + return (true); + } + + /* Allowed values are 0, 1 and power of 2. */ + if (blkio->Media->IoAlign > 1 && + !powerof2(blkio->Media->IoAlign)) { + efi_close_devpath(h); + return (true); + } + + /* + * With device tree setup: + * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0) + * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x1) + * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x2) + * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x3) + * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x3)/CDROM.. + * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x3)/CDROM.. + * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x4) + * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x5) + * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x6) + * PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x7) + * + * In above exmple only Unit(0x3) has media, all other nodes are + * missing media and should not be used. + * + * No media does not always mean there is no device, but in above + * case, we can not really assume there is any device. + * Therefore, if this node is USB, or this node is Unit (LUN) and + * direct parent is USB and we have no media, we will ignore this + * device. + */ + + /* Do not ignore device with media. */ + if (blkio->Media->MediaPresent) + return (false); + + node = efi_devpath_last_node(devpath); + if (node == NULL) + return (false); + + /* USB without media present */ + if (DevicePathType(node) == MESSAGING_DEVICE_PATH && + DevicePathSubType(node) == MSG_USB_DP) { + efi_close_devpath(h); + return (true); + } + + parent = efi_devpath_trim(devpath); + if (parent != NULL) { + bool parent_is_usb = false; + + node = efi_devpath_last_node(parent); + if (node == NULL) { + free(parent); + return (false); + } + if (DevicePathType(node) == MESSAGING_DEVICE_PATH && + DevicePathSubType(node) == MSG_USB_DP) + parent_is_usb = true; + free(parent); + + /* no media, parent is USB and devicepath is lun. */ + node = efi_devpath_last_node(devpath); + if (node == NULL) + return (false); + if (parent_is_usb && + DevicePathType(node) == MESSAGING_DEVICE_PATH && + DevicePathSubType(node) == MSG_DEVICE_LOGICAL_UNIT_DP) { + efi_close_devpath(h); + return (true); + } + } + return (false); +} + int efipart_inithandles(void) { unsigned i, nin; UINTN sz; EFI_HANDLE *hin; EFI_DEVICE_PATH *devpath; EFI_BLOCK_IO *blkio; EFI_STATUS status; pdinfo_t *pd; if (!STAILQ_EMPTY(&pdinfo)) return (0); sz = 0; hin = NULL; status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, hin); if (status == EFI_BUFFER_TOO_SMALL) { hin = malloc(sz); status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, hin); if (EFI_ERROR(status)) free(hin); } if (EFI_ERROR(status)) return (efi_status_to_errno(status)); nin = sz / sizeof(*hin); #ifdef EFIPART_DEBUG printf("%s: Got %d BLOCK IO MEDIA handle(s)\n", __func__, nin); #endif for (i = 0; i < nin; i++) { /* * Get devpath and open protocol. * We should not get errors here */ if ((devpath = efi_lookup_devpath(hin[i])) == NULL) continue; status = OpenProtocolByHandle(hin[i], &blkio_guid, (void **)&blkio); if (EFI_ERROR(status)) { printf("error %lu\n", EFI_ERROR_CODE(status)); continue; } - /* - * We assume the block size 512 or greater power of 2. - * Also skip devices with block size > 64k (16 is max - * ashift supported by zfs). - * iPXE is known to insert stub BLOCK IO device with - * BlockSize 1. - */ - if (blkio->Media->BlockSize < 512 || - blkio->Media->BlockSize > (1 << 16) || - !powerof2(blkio->Media->BlockSize)) { + if (efipart_ignore_device(hin[i], blkio, devpath)) continue; - } - /* Allowed values are 0, 1 and power of 2. */ - if (blkio->Media->IoAlign > 1 && - !powerof2(blkio->Media->IoAlign)) { - continue; - } - /* This is bad. */ if ((pd = calloc(1, sizeof(*pd))) == NULL) { printf("efipart_inithandles: Out of memory.\n"); free(hin); return (ENOMEM); } STAILQ_INIT(&pd->pd_part); pd->pd_handle = hin[i]; pd->pd_devpath = devpath; pd->pd_blkio = blkio; STAILQ_INSERT_TAIL(&pdinfo, pd, pd_link); } + /* + * Walk pdinfo and set parents based on device path. + */ + STAILQ_FOREACH(pd, &pdinfo, pd_link) { + pd->pd_parent = efipart_find_parent(&pdinfo, pd->pd_devpath); + } free(hin); return (0); } +/* + * Get node identified by pd_test() from plist. + */ +static pdinfo_t * +efipart_get_pd(pdinfo_list_t *plist, pd_test_cb_t pd_test, pdinfo_t *data) +{ + pdinfo_t *pd; + + STAILQ_FOREACH(pd, plist, pd_link) { + if (pd_test(pd, data)) + break; + } + + return (pd); +} + static ACPI_HID_DEVICE_PATH * 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); } -static pdinfo_t * -efipart_find_parent(pdinfo_list_t *pdi, EFI_DEVICE_PATH *devpath) +static bool +efipart_testfd(pdinfo_t *fd, pdinfo_t *data __unused) { - pdinfo_t *pd, *part; + EFI_DEVICE_PATH *node; - STAILQ_FOREACH(pd, pdi, pd_link) { - if (efi_devpath_is_prefix(pd->pd_devpath, devpath)) - return (pd); - part = efipart_find_parent(&pd->pd_part, devpath); - if (part != NULL) - return (part); - } - return (NULL); + node = efi_devpath_last_node(fd->pd_devpath); + if (node == NULL) + return (false); + + if (efipart_floppy(node) != NULL) + return (true); + + return (false); } static int efipart_initfd(void) { EFI_DEVICE_PATH *node; ACPI_HID_DEVICE_PATH *acpi; pdinfo_t *parent, *fd; -restart: - STAILQ_FOREACH(fd, &pdinfo, pd_link) { + while ((fd = efipart_get_pd(&pdinfo, efipart_testfd, NULL)) != NULL) { if ((node = efi_devpath_last_node(fd->pd_devpath)) == NULL) continue; if ((acpi = efipart_floppy(node)) == NULL) continue; STAILQ_REMOVE(&pdinfo, fd, pdinfo, pd_link); - parent = efipart_find_parent(&pdinfo, fd->pd_devpath); + parent = fd->pd_parent; 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)); return (0); } /* * Add or update entries with new handle data. */ static void efipart_cdinfo_add(pdinfo_t *cd) { - pdinfo_t *pd, *last; + pdinfo_t *parent, *pd, *last; - STAILQ_FOREACH(pd, &cdinfo, pd_link) { - if (efi_devpath_is_prefix(pd->pd_devpath, cd->pd_devpath)) { - last = STAILQ_LAST(&pd->pd_part, pdinfo, pd_link); - if (last != NULL) - 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; + if (cd == NULL) + return; + + parent = cd->pd_parent; + /* Make sure we have parent added */ + efipart_cdinfo_add(parent); + + STAILQ_FOREACH(pd, &pdinfo, pd_link) { + if (efi_devpath_match(pd->pd_devpath, cd->pd_devpath)) { + STAILQ_REMOVE(&pdinfo, cd, pdinfo, pd_link); + break; } } + if (pd == NULL) { + /* This device is already added. */ + return; + } + if (parent != NULL) { + last = STAILQ_LAST(&parent->pd_part, pdinfo, pd_link); + if (last != NULL) + cd->pd_unit = last->pd_unit + 1; + else + cd->pd_unit = 0; + cd->pd_devsw = &efipart_cddev; + STAILQ_INSERT_TAIL(&parent->pd_part, cd, pd_link); + return; + } + last = STAILQ_LAST(&cdinfo, pdinfo, pd_link); if (last != NULL) cd->pd_unit = last->pd_unit + 1; else cd->pd_unit = 0; - cd->pd_parent = NULL; cd->pd_devsw = &efipart_cddev; STAILQ_INSERT_TAIL(&cdinfo, cd, pd_link); } static bool -efipart_testcd(EFI_DEVICE_PATH *node, EFI_BLOCK_IO *blkio) +efipart_testcd(pdinfo_t *cd, pdinfo_t *data __unused) { + EFI_DEVICE_PATH *node; + + node = efi_devpath_last_node(cd->pd_devpath); + if (node == NULL) + return (false); + + if (efipart_floppy(node) != NULL) + return (false); + 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) { + if (cd->pd_blkio->Media->RemovableMedia && + !cd->pd_blkio->Media->MediaPresent) { return (true); } return (false); } -static void -efipart_updatecd(void) +/* + * Test if pd is parent for device. + */ +static bool +efipart_testchild(pdinfo_t *dev, pdinfo_t *pd) { - EFI_DEVICE_PATH *devpath, *node; - EFI_STATUS status; - pdinfo_t *parent, *cd; + /* device with no parent. */ + if (dev->pd_parent == NULL) + return (false); -restart: - STAILQ_FOREACH(cd, &pdinfo, pd_link) { - if ((node = efi_devpath_last_node(cd->pd_devpath)) == NULL) - continue; - - if (efipart_floppy(node) != NULL) - continue; - - /* Is parent of this device already registered? */ - parent = efipart_find_parent(&cdinfo, cd->pd_devpath); - if (parent != NULL) { - STAILQ_REMOVE(&pdinfo, cd, pdinfo, pd_link); - efipart_cdinfo_add(cd); - goto restart; - } - - if (!efipart_testcd(node, cd->pd_blkio)) - 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 - * it doesn't refer to the CD filesystem itself, but rather - * to any usable El Torito boot image on it. In this case - * we try to find the parent device and add that instead as - * that will be the CD filesystem. - */ - if (DevicePathType(node) == MEDIA_DEVICE_PATH && - DevicePathSubType(node) == MEDIA_CDROM_DP && - parent == NULL) { - parent = calloc(1, sizeof(*parent)); - if (parent == NULL) { - printf("efipart_updatecd: out of memory\n"); - /* this device is lost but try again. */ - free(cd); - goto restart; - } - - devpath = efi_devpath_trim(cd->pd_devpath); - if (devpath == NULL) { - printf("efipart_updatecd: out of memory\n"); - /* this device is lost but try again. */ - free(parent); - free(cd); - goto restart; - } - parent->pd_devpath = devpath; - status = BS->LocateDevicePath(&blkio_guid, - &parent->pd_devpath, &parent->pd_handle); - free(devpath); - if (EFI_ERROR(status)) { - 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; + if (efi_devpath_match(dev->pd_parent->pd_devpath, pd->pd_devpath)) { + return (true); } + return (false); } static int efipart_initcd(void) { - efipart_updatecd(); + pdinfo_t *cd; + while ((cd = efipart_get_pd(&pdinfo, efipart_testcd, NULL)) != NULL) + efipart_cdinfo_add(cd); + + /* Find all children of CD devices we did add above. */ + STAILQ_FOREACH(cd, &cdinfo, pd_link) { + pdinfo_t *child; + + for (child = efipart_get_pd(&pdinfo, efipart_testchild, cd); + child != NULL; + child = efipart_get_pd(&pdinfo, efipart_testchild, cd)) + efipart_cdinfo_add(child); + } bcache_add_dev(efiblk_pdinfo_count(&cdinfo)); return (0); } -static bool +static void efipart_hdinfo_add_node(pdinfo_t *hd, EFI_DEVICE_PATH *node) { - pdinfo_t *pd, *ptr; + pdinfo_t *parent, *ptr; if (node == NULL) - return (false); + return; - /* Find our disk device. */ - STAILQ_FOREACH(pd, &hdinfo, pd_link) { - if (efi_devpath_is_prefix(pd->pd_devpath, hd->pd_devpath)) - break; - } - if (pd == NULL) - return (false); - - /* If the node is not MEDIA_HARDDRIVE_DP, it is sub-partition. */ + parent = hd->pd_parent; + /* + * If the node is not MEDIA_HARDDRIVE_DP, it is sub-partition. + * This can happen with Vendor nodes, and since we do not know + * the more about those nodes, we just count them. + */ if (DevicePathSubType(node) != MEDIA_HARDDRIVE_DP) { - STAILQ_FOREACH(ptr, &pd->pd_part, pd_link) { - if (efi_devpath_is_prefix(ptr->pd_devpath, - hd->pd_devpath)) - break; - } - /* - * ptr == NULL means we have handles in unexpected order - * and we would need to re-order the partitions later. - */ + ptr = STAILQ_LAST(&parent->pd_part, pdinfo, pd_link); if (ptr != NULL) - pd = ptr; - } - - /* Add the partition. */ - if (DevicePathSubType(node) == MEDIA_HARDDRIVE_DP) { - hd->pd_unit = ((HARDDRIVE_DEVICE_PATH *)node)->PartitionNumber; - } else { - ptr = STAILQ_LAST(&pd->pd_part, pdinfo, pd_link); - if (ptr != NULL) hd->pd_unit = ptr->pd_unit + 1; else hd->pd_unit = 0; + } else { + hd->pd_unit = ((HARDDRIVE_DEVICE_PATH *)node)->PartitionNumber; } - hd->pd_parent = pd; - hd->pd_devsw = &efipart_hddev; - STAILQ_INSERT_TAIL(&pd->pd_part, hd, pd_link); - return (true); -} - -static void -efipart_hdinfo_add(pdinfo_t *hd, EFI_DEVICE_PATH *node) -{ - pdinfo_t *last; - - if (efipart_hdinfo_add_node(hd, node)) - return; - - last = STAILQ_LAST(&hdinfo, pdinfo, pd_link); - if (last != NULL) - hd->pd_unit = last->pd_unit + 1; - else - hd->pd_unit = 0; - - /* Add the disk. */ hd->pd_devsw = &efipart_hddev; - STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link); + STAILQ_INSERT_TAIL(&parent->pd_part, hd, pd_link); } /* * The MEDIA_FILEPATH_DP has device name. * From U-Boot sources it looks like names are in the form * of typeN:M, where type is interface type, N is disk id * and M is partition id. */ static void efipart_hdinfo_add_filepath(pdinfo_t *hd, FILEPATH_DEVICE_PATH *node) { char *pathname, *p; int len; pdinfo_t *last; last = STAILQ_LAST(&hdinfo, pdinfo, pd_link); if (last != NULL) hd->pd_unit = last->pd_unit + 1; else hd->pd_unit = 0; /* FILEPATH_DEVICE_PATH has 0 terminated string */ len = ucs2len(node->PathName); if ((pathname = malloc(len + 1)) == NULL) { printf("Failed to add disk, out of memory\n"); free(hd); return; } cpy16to8(node->PathName, pathname, len + 1); p = strchr(pathname, ':'); /* * Assume we are receiving handles in order, first disk handle, * then partitions for this disk. If this assumption proves * false, this code would need update. */ if (p == NULL) { /* no colon, add the disk */ hd->pd_devsw = &efipart_hddev; STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link); free(pathname); return; } p++; /* skip the colon */ errno = 0; hd->pd_unit = (int)strtol(p, NULL, 0); if (errno != 0) { printf("Bad unit number for partition \"%s\"\n", pathname); free(pathname); free(hd); return; } /* * We should have disk registered, if not, we are receiving * handles out of order, and this code should be reworked * to create "blank" disk for partition, and to find the * disk based on PathName compares. */ if (last == NULL) { printf("BUG: No disk for partition \"%s\"\n", pathname); free(pathname); free(hd); return; } /* Add the partition. */ hd->pd_parent = last; hd->pd_devsw = &efipart_hddev; STAILQ_INSERT_TAIL(&last->pd_part, hd, pd_link); free(pathname); } static void -efipart_updatehd(void) +efipart_hdinfo_add(pdinfo_t *hd) { - EFI_DEVICE_PATH *devpath, *node; - EFI_STATUS status; - pdinfo_t *parent, *hd; + pdinfo_t *parent, *pd, *last; + EFI_DEVICE_PATH *node; -restart: - STAILQ_FOREACH(hd, &pdinfo, pd_link) { - if ((node = efi_devpath_last_node(hd->pd_devpath)) == NULL) - continue; + if (hd == NULL) + return; - if (efipart_floppy(node) != NULL) - continue; + parent = hd->pd_parent; + /* Make sure we have parent added */ + efipart_hdinfo_add(parent); - if (efipart_testcd(node, hd->pd_blkio)) - continue; - - if (DevicePathType(node) == HARDWARE_DEVICE_PATH && - (DevicePathSubType(node) == HW_PCI_DP || - DevicePathSubType(node) == HW_VENDOR_DP)) { + STAILQ_FOREACH(pd, &pdinfo, pd_link) { + if (efi_devpath_match(pd->pd_devpath, hd->pd_devpath)) { STAILQ_REMOVE(&pdinfo, hd, pdinfo, pd_link); - efipart_hdinfo_add(hd, NULL); - goto restart; + break; } + } + if (pd == NULL) { + /* This device is already added. */ + return; + } - if (DevicePathType(node) == MEDIA_DEVICE_PATH && - DevicePathSubType(node) == MEDIA_FILEPATH_DP) { - STAILQ_REMOVE(&pdinfo, hd, pdinfo, pd_link); - efipart_hdinfo_add_filepath(hd, - (FILEPATH_DEVICE_PATH *)node); - goto restart; - } + if ((node = efi_devpath_last_node(hd->pd_devpath)) == NULL) + return; - STAILQ_REMOVE(&pdinfo, hd, pdinfo, pd_link); - parent = efipart_find_parent(&pdinfo, hd->pd_devpath); - if (parent != NULL) { - STAILQ_REMOVE(&pdinfo, parent, pdinfo, pd_link); - efipart_hdinfo_add(parent, NULL); - } else { - parent = efipart_find_parent(&hdinfo, hd->pd_devpath); - } + if (DevicePathType(node) == MEDIA_DEVICE_PATH && + DevicePathSubType(node) == MEDIA_FILEPATH_DP) { + efipart_hdinfo_add_filepath(hd, + (FILEPATH_DEVICE_PATH *)node); + return; + } - if (DevicePathType(node) == MEDIA_DEVICE_PATH && - DevicePathSubType(node) == MEDIA_HARDDRIVE_DP && - 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; - } + if (parent != NULL) { + efipart_hdinfo_add_node(hd, node); + return; + } - devpath = efi_devpath_trim(hd->pd_devpath); - if (devpath == NULL) { - printf("efipart_updatehd: out of memory\n"); - /* this device is lost but try again. */ - free(parent); - free(hd); - goto restart; - } + last = STAILQ_LAST(&hdinfo, pdinfo, pd_link); + if (last != NULL) + hd->pd_unit = last->pd_unit + 1; + else + hd->pd_unit = 0; - 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; - } + /* Add the disk. */ + hd->pd_devsw = &efipart_hddev; + STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link); +} - parent->pd_devpath = - efi_lookup_devpath(&parent->pd_handle); +static bool +efipart_testhd(pdinfo_t *hd, pdinfo_t *data __unused) +{ + if (efipart_testfd(hd, NULL)) + return (false); - efipart_hdinfo_add(parent, NULL); - } + if (efipart_testcd(hd, NULL)) + return (false); - efipart_hdinfo_add(hd, node); - goto restart; - } + /* Anything else must be HD. */ + return (true); } static int efipart_inithd(void) { + pdinfo_t *hd; - efipart_updatehd(); + while ((hd = efipart_get_pd(&pdinfo, efipart_testhd, NULL)) != NULL) + efipart_hdinfo_add(hd); 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); 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 = OpenProtocolByHandle(h, &blkio_guid, (void **)&blkio); if (!EFI_ERROR(status)) { 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.dd.d_dev = dev; pd_dev.dd.d_unit = pd->pd_unit; pd_dev.d_slice = D_SLICENONE; pd_dev.d_partition = D_PARTNONE; 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; } } 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)); } static int efipart_open(struct open_file *f, ...) { va_list args; struct disk_devdesc *dev; pdinfo_t *pd; EFI_BLOCK_IO *blkio; EFI_STATUS status; 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 (pd->pd_blkio == NULL) { status = OpenProtocolByHandle(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); 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); } return (0); } static int efipart_close(struct open_file *f) { struct disk_devdesc *dev; pdinfo_t *pd; dev = (struct disk_devdesc *)(f->f_devdata); if (dev == NULL) return (EINVAL); pd = efiblk_get_pdinfo((struct devdesc *)dev); 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->dd.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_t *pd; int rc; 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); } switch (cmd) { case DIOCGSECTORSIZE: *(u_int *)data = pd->pd_blkio->Media->BlockSize; break; case DIOCGMEDIASIZE: *(uint64_t *)data = pd->pd_blkio->Media->BlockSize * (pd->pd_blkio->Media->LastBlock + 1); break; default: return (ENOTTY); } return (0); } /* * efipart_readwrite() * Internal equivalent of efipart_strategy(), which operates on the * media-native block size. This function expects all I/O requests * to be within the media size and returns an error if such is not * the case. */ static int efipart_readwrite(EFI_BLOCK_IO *blkio, int rw, daddr_t blk, daddr_t nblks, char *buf) { EFI_STATUS status; if (blkio == NULL) return (ENXIO); if (blk < 0 || blk > blkio->Media->LastBlock) return (EIO); if ((blk + nblks - 1) > blkio->Media->LastBlock) return (EIO); switch (rw & F_MASK) { case F_READ: status = blkio->ReadBlocks(blkio, blkio->Media->MediaId, blk, nblks * blkio->Media->BlockSize, buf); break; case F_WRITE: if (blkio->Media->ReadOnly) return (EROFS); status = blkio->WriteBlocks(blkio, blkio->Media->MediaId, blk, nblks * blkio->Media->BlockSize, buf); break; default: return (ENOSYS); } if (EFI_ERROR(status)) { printf("%s: rw=%d, blk=%ju size=%ju status=%lu\n", __func__, rw, blk, nblks, EFI_ERROR_CODE(status)); } return (efi_status_to_errno(status)); } static int efipart_strategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf, size_t *rsize) { struct bcache_devdata bcd; struct disk_devdesc *dev; pdinfo_t *pd; dev = (struct disk_devdesc *)devdata; if (dev == NULL) return (EINVAL); pd = efiblk_get_pdinfo((struct devdesc *)dev); if (pd == NULL) return (EINVAL); if (pd->pd_blkio->Media->RemovableMedia && !pd->pd_blkio->Media->MediaPresent) return (ENXIO); bcd.dv_strategy = efipart_realstrategy; bcd.dv_devdata = devdata; bcd.dv_cache = pd->pd_bcache; if (dev->dd.d_dev->dv_type == DEVT_DISK) { daddr_t offset; offset = dev->d_offset * pd->pd_blkio->Media->BlockSize; offset /= 512; return (bcache_strategy(&bcd, rw, blk + offset, size, buf, rsize)); } return (bcache_strategy(&bcd, rw, blk, size, buf, rsize)); } static int efipart_realstrategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf, size_t *rsize) { struct disk_devdesc *dev = (struct disk_devdesc *)devdata; pdinfo_t *pd; EFI_BLOCK_IO *blkio; uint64_t off, disk_blocks, d_offset = 0; char *blkbuf; size_t blkoff, blksz, bio_size; unsigned ioalign; bool need_buf; int rc; uint64_t diskend, readstart; if (dev == NULL || blk < 0) return (EINVAL); pd = efiblk_get_pdinfo((struct devdesc *)dev); if (pd == NULL) return (EINVAL); blkio = pd->pd_blkio; if (blkio == NULL) return (ENXIO); if (size == 0 || (size % 512) != 0) 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; /* make sure we don't read past disk end */ if ((off + size) / blkio->Media->BlockSize > d_offset + disk_blocks) { diskend = d_offset + disk_blocks; readstart = off / blkio->Media->BlockSize; if (diskend <= readstart) { if (rsize != NULL) *rsize = 0; return (EIO); } size = diskend - readstart; size = size * blkio->Media->BlockSize; } need_buf = true; /* Do we need bounce buffer? */ if ((size % blkio->Media->BlockSize == 0) && (off % blkio->Media->BlockSize == 0)) need_buf = false; /* Do we have IO alignment requirement? */ ioalign = blkio->Media->IoAlign; if (ioalign == 0) ioalign++; if (ioalign > 1 && (uintptr_t)buf != roundup2((uintptr_t)buf, ioalign)) need_buf = true; if (need_buf) { for (bio_size = BIO_BUFFER_SIZE; bio_size > 0; bio_size -= blkio->Media->BlockSize) { blkbuf = memalign(ioalign, bio_size); if (blkbuf != NULL) break; } } else { blkbuf = buf; bio_size = size; } if (blkbuf == NULL) return (ENOMEM); if (rsize != NULL) *rsize = size; rc = 0; blk = off / blkio->Media->BlockSize; blkoff = off % blkio->Media->BlockSize; while (size > 0) { size_t x = min(size, bio_size); if (x < blkio->Media->BlockSize) x = 1; else x /= blkio->Media->BlockSize; switch (rw & F_MASK) { case F_READ: blksz = blkio->Media->BlockSize * x - blkoff; if (size < blksz) blksz = size; rc = efipart_readwrite(blkio, rw, blk, x, blkbuf); if (rc != 0) goto error; if (need_buf) bcopy(blkbuf + blkoff, buf, blksz); break; case F_WRITE: rc = 0; if (blkoff != 0) { /* * We got offset to sector, read 1 sector to * blkbuf. */ x = 1; blksz = blkio->Media->BlockSize - blkoff; blksz = min(blksz, size); rc = efipart_readwrite(blkio, F_READ, blk, x, blkbuf); } else if (size < blkio->Media->BlockSize) { /* * The remaining block is not full * sector. Read 1 sector to blkbuf. */ x = 1; blksz = size; rc = efipart_readwrite(blkio, F_READ, blk, x, blkbuf); } else { /* We can write full sector(s). */ blksz = blkio->Media->BlockSize * x; } if (rc != 0) goto error; /* * Put your Data In, Put your Data out, * Put your Data In, and shake it all about */ if (need_buf) bcopy(buf, blkbuf + blkoff, blksz); rc = efipart_readwrite(blkio, F_WRITE, blk, x, blkbuf); if (rc != 0) goto error; break; default: /* DO NOTHING */ rc = EROFS; goto error; } blkoff = 0; buf += blksz; size -= blksz; blk += x; } error: if (rsize != NULL) *rsize -= size; if (need_buf) free(blkbuf); return (rc); }