@@ -764,6 +764,19 @@ struct efi_block_io {
efi_status_t (EFIAPI *flush_blocks)(struct efi_block_io *this);
};
+#define EFI_RAM_DISK_PROTOCOL_GUID \
+ EFI_GUID(0xab38a0df, 0x6873, 0x44a9, \
+ 0x87, 0xe6, 0xd4, 0xeb, 0x56, 0x14, 0x84, 0x49)
+
+struct efi_ram_disk_protocol {
+ /* "register" is a reserved keyword in C, use "disk_register" instead */
+ efi_status_t(EFIAPI *disk_register)(
+ u64 ram_disk_base, u64 ram_disk_size, efi_guid_t *ram_disk_type,
+ struct efi_device_path *parent_device_path,
+ struct efi_device_path **device_path);
+ efi_status_t (EFIAPI *unregister)(struct efi_device_path *device_path);
+};
+
struct simple_text_output_mode {
s32 max_mode;
s32 mode;
@@ -333,6 +333,8 @@ extern const efi_guid_t smbios_guid;
/*GUID of console */
extern const efi_guid_t efi_guid_text_input_protocol;
extern const efi_guid_t efi_guid_text_output_protocol;
+/* GUID of Ram Disk protocol */
+extern const efi_guid_t efi_guid_ram_disk_protocol;
extern char __efi_runtime_start[], __efi_runtime_stop[];
extern char __efi_runtime_rel_start[], __efi_runtime_rel_stop[];
@@ -1159,4 +1161,6 @@ efi_status_t efi_disk_get_device_name(const efi_handle_t handle, char *buf, int
*/
void efi_add_known_memory(void);
+efi_status_t efi_ram_disk_register(void);
+
#endif /* _EFI_LOADER_H */
@@ -44,8 +44,11 @@ static efi_status_t check_node_type(efi_handle_t handle)
/* Get the last node */
const struct efi_device_path *node = efi_dp_last_node(dp);
/* We do not support partitions as controller */
- if (!node || node->type == DEVICE_PATH_TYPE_MEDIA_DEVICE)
- ret = EFI_UNSUPPORTED;
+ if (!node || node->type == DEVICE_PATH_TYPE_MEDIA_DEVICE) {
+ /* We support RAM disk as controller */
+ if (node->sub_type != DEVICE_PATH_SUB_TYPE_RAM_DISK_PATH)
+ ret = EFI_UNSUPPORTED;
+ }
}
return ret;
}
@@ -433,4 +433,10 @@ config EFI_RISCV_BOOT_PROTOCOL
replace the transfer via the device-tree. The latter is not
possible on systems using ACPI.
+config EFI_RAM_DISK_PROTOCOL
+ bool "EFI_RAM_DISK_PROTOCOL support"
+ depends on PARTITIONS
+ help
+ Provide a EFI_RAM_DISK_PROTOCOL implementation to register the
+ RAM disk and create the block device.
endif
@@ -86,6 +86,7 @@ obj-$(CONFIG_EFI_RISCV_BOOT_PROTOCOL) += efi_riscv.o
obj-$(CONFIG_EFI_LOAD_FILE2_INITRD) += efi_load_initrd.o
obj-$(CONFIG_EFI_SIGNATURE_SUPPORT) += efi_signature.o
obj-$(CONFIG_EFI_ECPT) += efi_conformance.o
+obj-$(CONFIG_EFI_RAM_DISK_PROTOCOL) += efi_ram_disk.o
EFI_VAR_SEED_FILE := $(subst $\",,$(CONFIG_EFI_VAR_SEED_FILE))
$(obj)/efi_var_seed.o: $(srctree)/$(EFI_VAR_SEED_FILE)
new file mode 100644
@@ -0,0 +1,334 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * RAM Disk Protocol
+ *
+ * Copyright (c) 2023, Linaro Limited
+ */
+
+#include <efi_driver.h>
+#include <malloc.h>
+#include <linux/unaligned/le_byteshift.h>
+#include <linux/unaligned/generic.h>
+
+#define MEM_BLOCK_SIZE_SHIFT 9 /* 512 bytes */
+
+const efi_guid_t efi_guid_ram_disk_protocol = EFI_RAM_DISK_PROTOCOL_GUID;
+static struct list_head obj_list;
+
+/**
+ * struct efi_ram_disk_obj - ram disk protocol object
+ *
+ * @ops: EFI disk I/O protocol interface
+ * @media: block I/O media information
+ * @dp: device path
+ * @image: image base address
+ * @image_size: image size
+ * @list: list structure
+ */
+struct efi_ram_disk_obj {
+ struct efi_block_io ops;
+ struct efi_block_io_media media;
+ struct efi_device_path *dp;
+ u8 *image;
+ u64 image_size;
+ struct list_head list;
+};
+
+/*
+ * reset - Reset service of the block IO protocol
+ *
+ * @this: pointer to the BLOCK_IO_PROTOCOL
+ * @extended_verification: extended verification
+ * Return: status code
+ */
+static efi_status_t EFIAPI reset(struct efi_block_io *this,
+ char extended_verification)
+{
+ return EFI_SUCCESS;
+}
+
+/*
+ * read_blocks - Read service of the block IO protocol
+ *
+ * @this: pointer to the BLOCK_IO_PROTOCOL
+ * @media_id: id of the medium to be read from
+ * @lba: starting logical block for reading
+ * @buffer_size: size of the read buffer
+ * @buffer: pointer to the destination buffer
+ * Return: status code
+ */
+static efi_status_t EFIAPI read_blocks(struct efi_block_io *this, u32 media_id,
+ u64 lba, efi_uintn_t buffer_size,
+ void *buffer)
+{
+ u8 *start;
+ struct efi_ram_disk_obj *ram_disk_obj;
+
+ if (!this || !buffer)
+ return EFI_INVALID_PARAMETER;
+
+ if (!buffer_size)
+ return EFI_SUCCESS;
+
+ /* TODO: check for media changes */
+ if (media_id != this->media->media_id)
+ return EFI_MEDIA_CHANGED;
+
+ if (!this->media->media_present)
+ return EFI_NO_MEDIA;
+
+ EFI_ENTRY("%p, %x, %llx, %zx, %p", this, media_id, lba,
+ buffer_size, buffer);
+
+ ram_disk_obj = container_of(this, struct efi_ram_disk_obj, ops);
+
+ if ((lba << MEM_BLOCK_SIZE_SHIFT) + buffer_size >
+ ram_disk_obj->image_size)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ start = ram_disk_obj->image + (lba << MEM_BLOCK_SIZE_SHIFT);
+ memmove(buffer, start, buffer_size);
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/*
+ * write_blocks - Write service of the block IO protocol
+ *
+ * @this: pointer to the BLOCK_IO_PROTOCOL
+ * @media_id: id of the medium to be written to
+ * @lba: starting logical block for writing
+ * @buffer_size: size of the write buffer
+ * @buffer: pointer to the source buffer
+ * Return: status code
+ */
+static efi_status_t EFIAPI write_blocks(struct efi_block_io *this, u32 media_id,
+ u64 lba, efi_uintn_t buffer_size,
+ void *buffer)
+{
+ u8 *start;
+ struct efi_ram_disk_obj *ram_disk_obj;
+
+ if (!this || !buffer)
+ return EFI_INVALID_PARAMETER;
+
+ if (this->media->read_only)
+ return EFI_WRITE_PROTECTED;
+
+ if (!buffer_size)
+ return EFI_SUCCESS;
+
+ /* TODO: check for media changes */
+ if (media_id != this->media->media_id)
+ return EFI_MEDIA_CHANGED;
+
+ if (!this->media->media_present)
+ return EFI_NO_MEDIA;
+
+ EFI_ENTRY("%p, %x, %llx, %zx, %p", this, media_id, lba,
+ buffer_size, buffer);
+
+ ram_disk_obj = container_of(this, struct efi_ram_disk_obj, ops);
+
+ if ((lba << MEM_BLOCK_SIZE_SHIFT) + buffer_size >
+ ram_disk_obj->image_size)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ start = ram_disk_obj->image + (lba << MEM_BLOCK_SIZE_SHIFT);
+ memmove(start, buffer, buffer_size);
+
+ return EFI_EXIT(EFI_SUCCESS);
+}
+
+/*
+ * flush_blocks - Flush service of the block IO protocol
+ *
+ * @this: pointer to the BLOCK_IO_PROTOCOL
+ * Return: status code
+ */
+static efi_status_t EFIAPI flush_blocks(struct efi_block_io *this)
+{
+ return EFI_SUCCESS;
+}
+
+/*
+ * ram_disk_register - Register service of the RAM disk protocol
+ *
+ * @ram_disk_base: The base address of registered RAM disk
+ * @ram_disk_size: The size of registered RAM disk
+ * @ram_disk_type: The type of registered RAM disk
+ * @parent_device_path: Pointer to the parent device path
+ * @device_path: Pointer to the device path
+ * Return: status code
+ */
+static efi_status_t EFIAPI
+ram_disk_register(u64 ram_disk_base, u64 ram_disk_size,
+ efi_guid_t *ram_disk_type,
+ struct efi_device_path *parent_device_path,
+ struct efi_device_path **device_path)
+{
+ efi_status_t ret;
+ efi_handle_t disk_handle = NULL;
+ struct efi_device_path *dp;
+ struct efi_device_path end_node;
+ struct efi_ram_disk_obj *ram_disk_obj;
+ struct efi_device_path_ram_disk_path ram_disk_node;
+
+ EFI_ENTRY("%llu %llu %pUs %p, %p", ram_disk_base, ram_disk_size,
+ ram_disk_type, parent_device_path, device_path);
+
+ if (!ram_disk_size || !ram_disk_type || !device_path)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ *device_path = NULL;
+
+ ram_disk_obj = calloc(1, sizeof(*ram_disk_obj));
+ if (!ram_disk_obj)
+ return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+
+ ram_disk_obj->image = (u8 *)ram_disk_base;
+ ram_disk_obj->image_size = ram_disk_size;
+ ram_disk_obj->ops.media = &ram_disk_obj->media;
+
+ ram_disk_obj->ops.media->block_size = 1 << MEM_BLOCK_SIZE_SHIFT;
+ ram_disk_obj->ops.media->last_block =
+ (ram_disk_size >> MEM_BLOCK_SIZE_SHIFT) - 1;
+ ram_disk_obj->ops.media->media_present = 1;
+
+ ram_disk_obj->ops.reset = reset;
+ ram_disk_obj->ops.read_blocks = read_blocks;
+ ram_disk_obj->ops.write_blocks = write_blocks;
+ ram_disk_obj->ops.flush_blocks = flush_blocks;
+
+ dp = calloc(1, sizeof(struct efi_device_path_ram_disk_path) +
+ sizeof(struct efi_device_path));
+ if (!dp) {
+ free(ram_disk_obj);
+ return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+ }
+
+ ram_disk_node.dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE;
+ ram_disk_node.dp.sub_type = DEVICE_PATH_SUB_TYPE_RAM_DISK_PATH;
+ ram_disk_node.dp.length = sizeof(struct efi_device_path_ram_disk_path);
+ put_unaligned_le64(ram_disk_base, &ram_disk_node.starting_address);
+ put_unaligned_le64(ram_disk_base + ram_disk_size - 1, &ram_disk_node.ending_address);
+ guidcpy(&ram_disk_node.disk_type_guid, ram_disk_type);
+ ram_disk_node.disk_instance = 0;
+ memcpy(dp, &ram_disk_node, sizeof(struct efi_device_path_ram_disk_path));
+
+ end_node.type = DEVICE_PATH_TYPE_END;
+ end_node.sub_type = DEVICE_PATH_SUB_TYPE_END;
+ end_node.length = sizeof(struct efi_device_path);
+ memcpy((char *)dp + sizeof(struct efi_device_path_ram_disk_path),
+ &end_node, sizeof(struct efi_device_path));
+
+ ram_disk_obj->dp = efi_dp_append(parent_device_path, dp);
+ free(dp);
+ if (!ram_disk_obj->dp) {
+ free(ram_disk_obj);
+ return EFI_EXIT(EFI_OUT_OF_RESOURCES);
+ }
+
+ if (efi_dp_find_obj(ram_disk_obj->dp, NULL, NULL)) {
+ log_err("Already Started\n");
+ ret = EFI_ALREADY_STARTED;
+ goto err;
+ }
+
+ ret = efi_install_multiple_protocol_interfaces(
+ &disk_handle, &efi_guid_device_path, ram_disk_obj->dp,
+ &efi_block_io_guid, &ram_disk_obj->ops, NULL);
+ if (ret != EFI_SUCCESS) {
+ log_err("InstallProtocolInterface failed\n");
+ goto err;
+ }
+
+ ret = EFI_CALL(systab.boottime->connect_controller(disk_handle, NULL,
+ NULL, 1));
+ if (ret != EFI_SUCCESS) {
+ log_err("ConnectController failed\n");
+ goto err;
+ }
+
+ *device_path = ram_disk_obj->dp;
+ list_add(&ram_disk_obj->list, &obj_list);
+
+ return EFI_EXIT(ret);
+err:
+ efi_free_pool(ram_disk_obj->dp);
+ free(ram_disk_obj);
+
+ return EFI_EXIT(ret);
+}
+
+/*
+ * ram_disk_unregister - Unregister service of the RAM disk protocol
+ *
+ * @device_path: Pointer to the device path
+ * Return: status code
+ */
+static efi_status_t EFIAPI
+ram_disk_unregister(struct efi_device_path *device_path)
+{
+ int ret;
+ efi_handle_t disk_handle;
+ struct list_head *pos, *n;
+ struct efi_ram_disk_obj *entry;
+ struct efi_ram_disk_obj *ram_disk_obj = NULL;
+
+ EFI_ENTRY("%p", device_path);
+
+ if (!device_path)
+ return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+ list_for_each_safe(pos, n, &obj_list) {
+ entry = list_entry(pos, struct efi_ram_disk_obj, list);
+ if (!efi_dp_match(device_path, entry->dp)) {
+ ram_disk_obj = entry;
+ break;
+ }
+ }
+
+ if (!ram_disk_obj)
+ return EFI_EXIT(EFI_NOT_FOUND);
+
+ disk_handle = efi_dp_find_obj(device_path, &efi_block_io_guid, NULL);
+ if (!disk_handle)
+ return EFI_EXIT(EFI_NOT_FOUND);
+
+ ret = efi_uninstall_multiple_protocol_interfaces(
+ disk_handle, &efi_guid_device_path, ram_disk_obj->dp,
+ &efi_block_io_guid, &ram_disk_obj->ops, NULL);
+ if (ret != EFI_SUCCESS)
+ log_err("UninstallProtocolInterface failed\n");
+
+ list_del(&ram_disk_obj->list);
+ efi_free_pool(ram_disk_obj->dp);
+ free(ram_disk_obj);
+
+ return EFI_EXIT(ret);
+}
+
+static const struct efi_ram_disk_protocol efi_ram_disk_protocol = {
+ .disk_register = ram_disk_register,
+ .unregister = ram_disk_unregister,
+};
+
+/**
+ * efi_ram_disk_register() - register EFI_RAM_DISK_PROTOCOL
+ *
+ * Return: status code
+ */
+efi_status_t efi_ram_disk_register(void)
+{
+ efi_status_t ret;
+
+ ret = efi_add_protocol(efi_root, &efi_guid_ram_disk_protocol,
+ (void *)&efi_ram_disk_protocol);
+ if (ret != EFI_SUCCESS)
+ log_err("Cannot install EFI_RAM_DISK_PROTOCOL\n");
+
+ INIT_LIST_HEAD(&obj_list);
+
+ return ret;
+}
@@ -344,6 +344,12 @@ efi_status_t efi_init_obj_list(void)
if (ret != EFI_SUCCESS)
goto out;
+ if (IS_ENABLED(CONFIG_EFI_RAM_DISK_PROTOCOL)) {
+ ret = efi_ram_disk_register();
+ if (ret != EFI_SUCCESS)
+ goto out;
+ }
+
/* Initialize EFI runtime services */
ret = efi_reset_system_init();
if (ret != EFI_SUCCESS)
@@ -195,6 +195,10 @@ static const struct {
"Firmware Management",
EFI_FIRMWARE_MANAGEMENT_PROTOCOL_GUID
},
+ {
+ "Ram Disk",
+ EFI_RAM_DISK_PROTOCOL_GUID
+ },
/* Configuration table GUIDs */
{
"ACPI table",
This commit adds the EFI_RAM_DISK_PROTOCOL implementation. User can mount the distro installer by registering the memory mapped ISO image through EFI_RAM_DISK_PROTOCOL. Note that the installation process may not proceed after the distro installer calls ExitBootServices() since there is no hand-off process for the block device of memory mapped ISO image. Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org> --- include/efi_api.h | 13 ++ include/efi_loader.h | 4 + lib/efi_driver/efi_uclass.c | 7 +- lib/efi_loader/Kconfig | 6 + lib/efi_loader/Makefile | 1 + lib/efi_loader/efi_ram_disk.c | 334 ++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_setup.c | 6 + lib/uuid.c | 4 + 8 files changed, 373 insertions(+), 2 deletions(-) create mode 100644 lib/efi_loader/efi_ram_disk.c