diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c index fa1d0c7..16526d0 100644 --- a/grub-core/kern/efi/efi.c +++ b/grub-core/kern/efi/efi.c @@ -37,6 +37,10 @@ grub_efi_system_table_t *grub_efi_system_table; static grub_efi_guid_t console_control_guid = GRUB_EFI_CONSOLE_CONTROL_GUID; static grub_efi_guid_t loaded_image_guid = GRUB_EFI_LOADED_IMAGE_GUID; static grub_efi_guid_t device_path_guid = GRUB_EFI_DEVICE_PATH_GUID; +static grub_efi_guid_t driver_binding_guid = GRUB_EFI_DRIVER_BINDING_GUID; +static grub_efi_guid_t driver_configuration_guid = GRUB_EFI_DRIVER_CONFIGURATION_GUID; +static grub_efi_guid_t driver_diagnostics_guid = GRUB_EFI_DRIVER_DIAGNOSTICS_GUID; +static grub_efi_guid_t component_name_guid = GRUB_EFI_COMPONENT_NAME_GUID; void * grub_efi_locate_protocol (grub_efi_guid_t *protocol, void *registration) @@ -740,3 +744,303 @@ grub_efi_print_device_path (grub_efi_device_path_t *dp) dp = (grub_efi_device_path_t *) ((char *) dp + len); } } + + +grub_efi_status_t grub_efi_free_pool (void *ptr) +{ + grub_efi_boot_services_t *b; + b = grub_efi_system_table->boot_services; + + return efi_call_1 (b->free_pool, ptr); +} + +void * grub_efi_allocate_pool (grub_efi_memory_type_t type, grub_efi_uintn_t len) +{ + grub_efi_boot_services_t *b; + grub_efi_status_t status; + void *ret; + b = grub_efi_system_table->boot_services; + + status = efi_call_3 (b->allocate_pool, type, len, &ret); + + if (status != GRUB_EFI_SUCCESS) + return (void *) 0; + + return ret; +} + + +static int grub_efi_guid_cmp (grub_efi_guid_t * a, grub_efi_guid_t * b) +{ + return grub_memcmp (a, b, sizeof (grub_efi_guid_t)); +} + + + +grub_efi_status_t +grub_efi_scan_handle_database (grub_efi_handle_t driver_binding_handle, + grub_efi_uint32_t * + driver_binding_i, + grub_efi_handle_t controller_handle, + grub_efi_uint32_t * controller_i, + grub_efi_uintn_t * handle_count, + grub_efi_handle_t ** handle_buffer, + grub_efi_uint32_t ** handle_type) +{ + grub_efi_boot_services_t *b; + grub_efi_status_t status; + grub_efi_uintn_t i, j, k; + grub_efi_guid_t **protocol_guid_array; + grub_efi_uintn_t array_count; + grub_efi_uintn_t protocol_index; + grub_efi_open_protocol_information_entry_t *open_info; + grub_efi_uintn_t open_info_count; + grub_efi_boolean_t driver_binding_i_valid; + grub_efi_boolean_t controller_i_valid; + + b = grub_efi_system_table->boot_services; + + driver_binding_i_valid = 0; + if (driver_binding_i != NULL) + { + *driver_binding_i = 0xffffffff; + } + + controller_i_valid = 0; + if (controller_i != NULL) + { + *controller_i = 0xffffffff; + } + *handle_count = 0; + *handle_buffer = NULL; + *handle_type = NULL; + + // + // Retrieve the list of all handles from the handle database + // + status = efi_call_5 (b->locate_handle_buffer, + GRUB_EFI_ALL_HANDLES, + NULL, NULL, handle_count, handle_buffer); + + if (status != GRUB_EFI_SUCCESS) + goto error; + + *handle_type = grub_malloc (*handle_count * sizeof (grub_efi_uint32_t)); + if (!*handle_type) + goto error; + + for (i = 0; i < *handle_count; i++) + { + + // + // Assume that the handle type is unknown + // + (*handle_type)[i] = GRUB_EFI_HANDLE_TYPE_UNKNOWN; + + if (driver_binding_handle != NULL && driver_binding_i != NULL + && (*handle_buffer)[i] == driver_binding_handle) + { + *driver_binding_i = (grub_efi_uint32_t) i; + driver_binding_i_valid = 1; + } + + if (controller_handle != NULL && controller_i != NULL + && (*handle_buffer)[i] == controller_handle) + { + *controller_i = (grub_efi_uint32_t) i; + controller_i_valid = 1; + } + } + + for (i = 0; i < *handle_count; i++) + { + + // + // Retrieve the list of all the protocols on each handle + // + status = efi_call_3 (b->protocols_per_handle, + (*handle_buffer)[i], + &protocol_guid_array, &array_count); + + if (status != GRUB_EFI_SUCCESS) + continue; + + for (protocol_index = 0; protocol_index < array_count; protocol_index++) + { + + if (grub_efi_guid_cmp + (protocol_guid_array[protocol_index], &loaded_image_guid) == 0) + { + (*handle_type)[i] |= GRUB_EFI_HANDLE_TYPE_IMAGE_HANDLE; + } + + if (grub_efi_guid_cmp + (protocol_guid_array[protocol_index], + &driver_binding_guid) == 0) + { + (*handle_type)[i] |= GRUB_EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE; + } + + if (grub_efi_guid_cmp + (protocol_guid_array[protocol_index], + &driver_configuration_guid) == 0) + { + (*handle_type)[i] |= + GRUB_EFI_HANDLE_TYPE_DRIVER_CONFIGURATION_HANDLE; + } + + if (grub_efi_guid_cmp + (protocol_guid_array[protocol_index], + &driver_diagnostics_guid) == 0) + { + (*handle_type)[i] |= + GRUB_EFI_HANDLE_TYPE_DRIVER_DIAGNOSTICS_HANDLE; + } + + if (grub_efi_guid_cmp + (protocol_guid_array[protocol_index], + &component_name_guid) == 0) + { + (*handle_type)[i] |= GRUB_EFI_HANDLE_TYPE_COMPONENT_NAME_HANDLE; + } + + if (grub_efi_guid_cmp + (protocol_guid_array[protocol_index], &device_path_guid) == 0) + { + (*handle_type)[i] |= GRUB_EFI_HANDLE_TYPE_DEVICE_HANDLE; + } + + // + // Retrieve the list of agents that have opened each protocol + // + status = efi_call_4 (b->open_protocol_information, + (*handle_buffer)[i], + protocol_guid_array[protocol_index], + &open_info, &open_info_count); + + if (status != GRUB_EFI_SUCCESS) + continue; + + for (j = 0; j < open_info_count; j++) + { + + if (driver_binding_handle != NULL + && open_info[j].agent_handle == driver_binding_handle) + { + if ((open_info[j]. + attributes & GRUB_EFI_OPEN_PROTOCOL_BY_DRIVER) == + GRUB_EFI_OPEN_PROTOCOL_BY_DRIVER) + { + // + // Mark the device handle as being managed by the driver specified by driver_binding_handle + // + (*handle_type)[i] |= + (GRUB_EFI_HANDLE_TYPE_DEVICE_HANDLE | + GRUB_EFI_HANDLE_TYPE_CONTROLLER_HANDLE); + // + // Mark the driver_binding_handle as being a driver that is managing at least one controller + // + if (driver_binding_i_valid) + { + (*handle_type)[*driver_binding_i] |= + GRUB_EFI_HANDLE_TYPE_DEVICE_DRIVER; + } + } + if ((open_info[j]. + attributes & + GRUB_EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) == + GRUB_EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) + { + // + // Mark the driver_binding_handle as being a driver that is managing at least one child controller + // + if (driver_binding_i_valid) + { + (*handle_type)[*driver_binding_i] |= + GRUB_EFI_HANDLE_TYPE_BUS_DRIVER; + } + } + + if (controller_handle != NULL + && (*handle_buffer)[i] == controller_handle) + { + if ((open_info[j]. + attributes & + GRUB_EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) == + GRUB_EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) + { + for (k = 0; k < *handle_count; k++) + { + if ((*handle_buffer)[k] == + open_info[j].controller_handle) + { + (*handle_type)[k] |= + (GRUB_EFI_HANDLE_TYPE_DEVICE_HANDLE | + GRUB_EFI_HANDLE_TYPE_CHILD_HANDLE); + } + } + } + } + } + + + if (driver_binding_handle == NULL + && open_info[j].controller_handle == controller_handle) + { + if ((open_info[j]. + attributes & GRUB_EFI_OPEN_PROTOCOL_BY_DRIVER) == + GRUB_EFI_OPEN_PROTOCOL_BY_DRIVER) + { + for (k = 0; k < *handle_count; k++) + { + if ((*handle_buffer)[k] == + open_info[j].agent_handle) + { + (*handle_type)[k] |= + GRUB_EFI_HANDLE_TYPE_DEVICE_DRIVER; + } + } + } + if ((open_info[j]. + attributes & + GRUB_EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) == + GRUB_EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) + { + (*handle_type)[i] |= GRUB_EFI_HANDLE_TYPE_PARENT_HANDLE; + for (k = 0; k < *handle_count; k++) + { + if ((*handle_buffer)[k] == + open_info[j].agent_handle) + { + (*handle_type)[k] |= + GRUB_EFI_HANDLE_TYPE_BUS_DRIVER; + } + } + } + } + + } + grub_efi_free_pool (open_info); + + } + grub_efi_free_pool (protocol_guid_array); + } + + return GRUB_EFI_SUCCESS; + +error: + if (*handle_type != NULL) + grub_free (*handle_type); + + if (*handle_buffer != NULL) + grub_efi_free_pool (*handle_buffer); + *handle_count = 0; + *handle_buffer = NULL; + *handle_type = NULL; + + return status; +} + + + + diff --git a/grub-core/kern/efi/init.c b/grub-core/kern/efi/init.c index 1b0a872..a83bf2e 100644 --- a/grub-core/kern/efi/init.c +++ b/grub-core/kern/efi/init.c @@ -26,6 +26,66 @@ #include #include +static void bind_all_drivers (void) +{ + grub_efi_uintn_t n_all_handles; + grub_efi_handle_t *all_handles; + grub_efi_status_t status; + grub_efi_boot_services_t *b; + grub_efi_uintn_t handle_count; + grub_efi_handle_t *handle_buffer; + grub_efi_uint32_t *handle_type; + unsigned int i, j; + + b = grub_efi_system_table->boot_services; + + all_handles = + grub_efi_locate_handle (GRUB_EFI_ALL_HANDLES, NULL, NULL, &n_all_handles); + + if (!all_handles) + return; + + for (i = 0; i < n_all_handles; ++i) + { + int is_device = 1; + + status = + grub_efi_scan_handle_database (NULL, NULL, all_handles[i], NULL, + &handle_count, &handle_buffer, + &handle_type); + + if (status != GRUB_EFI_SUCCESS) + continue; + + if (handle_type[i] & GRUB_EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE) + is_device = 0; + if (handle_type[i] & GRUB_EFI_HANDLE_TYPE_IMAGE_HANDLE) + is_device = 0; + + if (is_device) + { + int is_parent = 0; + for (j = 0; j < handle_count; ++j) + { + if (handle_type[j] & GRUB_EFI_HANDLE_TYPE_PARENT_HANDLE) + is_parent = 1; + } + + if ((!is_parent) + && (handle_type[i] & GRUB_EFI_HANDLE_TYPE_DEVICE_HANDLE)) + { + efi_call_4 (b->connect_controller, all_handles[i], NULL, NULL, 1); + } + } + + grub_free (handle_type); + grub_efi_free_pool (handle_buffer); + } + + grub_free (all_handles); +} + + void grub_efi_init (void) { @@ -39,6 +99,8 @@ grub_efi_init (void) efi_call_4 (grub_efi_system_table->boot_services->set_watchdog_timer, 0, 0, 0, NULL); + bind_all_drivers(); + grub_efidisk_init (); } diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h index 535a3e3..d8bce06 100644 --- a/include/grub/efi/api.h +++ b/include/grub/efi/api.h @@ -119,6 +119,26 @@ { 0x82, 0x79, 0xa8, 0x4b, 0x79, 0x61, 0x78, 0x98 } \ } +#define GRUB_EFI_DRIVER_BINDING_GUID \ + { 0x18a031ab, 0xb443, 0x4d1a, \ + {0xa5, 0xc0, 0xc, 0x9, 0x26, 0x1e, 0x9f, 0x71 } \ + } + +#define GRUB_EFI_DRIVER_CONFIGURATION_GUID \ + { 0x107a772b, 0xd5e1, 0x11d4, \ + {0x9a, 0x46, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \ + } + +#define GRUB_EFI_DRIVER_DIAGNOSTICS_GUID \ + { 0x0784924f, 0xe296, 0x11d4, \ + { 0x9a, 0x49, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \ + } + +#define GRUB_EFI_COMPONENT_NAME_GUID \ + { 0x107a772c, 0xd5e1, 0x11d4, \ + {0x9a, 0x46, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \ + } + struct grub_efi_sal_system_table { grub_uint32_t signature; diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h index e9c57dd..a5b557b 100644 --- a/include/grub/efi/efi.h +++ b/include/grub/efi/efi.h @@ -68,6 +68,32 @@ void grub_efi_init (void); void grub_efi_fini (void); void grub_efi_set_prefix (void); +grub_efi_status_t EXPORT_FUNC(grub_efi_scan_handle_database) ( + grub_efi_handle_t driver_binding_handle, + grub_efi_uint32_t *driver_binding_handle_index, + grub_efi_handle_t controller_handle, + grub_efi_uint32_t *controller_handle_index, + grub_efi_uintn_t *handle_count, + grub_efi_handle_t **handle_buffer, + grub_efi_uint32_t **handle_type); + +#define GRUB_EFI_HANDLE_TYPE_UNKNOWN 0x000 +#define GRUB_EFI_HANDLE_TYPE_IMAGE_HANDLE 0x001 +#define GRUB_EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE 0x002 +#define GRUB_EFI_HANDLE_TYPE_DEVICE_DRIVER 0x004 +#define GRUB_EFI_HANDLE_TYPE_BUS_DRIVER 0x008 +#define GRUB_EFI_HANDLE_TYPE_DRIVER_CONFIGURATION_HANDLE 0x010 +#define GRUB_EFI_HANDLE_TYPE_DRIVER_DIAGNOSTICS_HANDLE 0x020 +#define GRUB_EFI_HANDLE_TYPE_COMPONENT_NAME_HANDLE 0x040 +#define GRUB_EFI_HANDLE_TYPE_DEVICE_HANDLE 0x080 +#define GRUB_EFI_HANDLE_TYPE_PARENT_HANDLE 0x100 +#define GRUB_EFI_HANDLE_TYPE_CONTROLLER_HANDLE 0x200 +#define GRUB_EFI_HANDLE_TYPE_CHILD_HANDLE 0x400 + +void *EXPORT_FUNC(grub_efi_allocate_pool) (grub_efi_memory_type_t type, grub_efi_uintn_t len); +grub_efi_status_t EXPORT_FUNC(grub_efi_free_pool) (void *); + + /* Variables. */ extern grub_efi_system_table_t *EXPORT_VAR(grub_efi_system_table); extern grub_efi_handle_t EXPORT_VAR(grub_efi_image_handle);