(09)ATF存储抽象层

存储抽象层

为了支持不同的存储设备,便于移植,TF-A设计统一的存储抽象层接口。

基本概念

存储

定义一些与设备IO操作的结构和函数。

  1. 设备类型

    定义可访问的设备类型。

    类型描述
    IO_TYPE_SEMIHOSTING半主机设备,如加载镜像通过文件名读取数据
    IO_TYPE_MEMMAP内存文件设备,如加载镜像从内存中读取数据
    IO_TYPE_BLOCK块设备,以块为单位读写设备,如SD、EMMC等
    IO_TYPE_MTDMTD设备,如NAND/NOR FLASH,通常基于扇区进行访问
    IO_TYPE_ENCRYPTED加密设备,TF-A把加密的镜像抽象层加密设备
  2. 描述规范

    定义代表文件、UUID和块的规范。这些规范可以用来访问存储设备上的不同类型的数据。

    /* File specification - used to refer to data on a device supporting file-like
     * entities */
    typedef struct io_file_spec {
    	const char *path;
    	unsigned int mode;
    } io_file_spec_t;
    
    /* UUID specification - used to refer to data accessed using UUIDs (i.e. FIP
     * images) */
    typedef struct io_uuid_spec {
    	uuid_t uuid;
    } io_uuid_spec_t;
    
    /* Block specification - used to refer to data on a device supporting
     * block-like entities */
    typedef struct io_block_spec {
    	size_t offset;
    	size_t length;
    } io_block_spec_t;
    

    io_file_spec_t用于访问类似文件的实体,比如闪存上的文件系统中的文件。io_uuid_spec_t用于访问使用 UUID 访问的数据,如FIP镜像。io_block_spec_t用于访问块设备,如SD卡。

  3. 设备操作

    /* Open a connection to a device */
    int io_dev_open(const struct io_dev_connector *dev_con,
    		const uintptr_t dev_spec,
    		uintptr_t *handle);
    
    /* Initialise a device explicitly - to permit lazy initialisation or
     * re-initialisation */
    int io_dev_init(uintptr_t dev_handle, const uintptr_t init_params);
    
    /* Close a connection to a device */
    int io_dev_close(uintptr_t dev_handle);
    
    操作描述
    io_dev_open打开对设备的连接
    io_dev_init显式初始化一个设备,允许延迟初始化或重新初始化
    io_dev_close关闭对设备的连接
  4. 同步操作

    /* Synchronous operations */
    int io_open(uintptr_t dev_handle, const uintptr_t spec, uintptr_t *handle);
    
    int io_seek(uintptr_t handle, io_seek_mode_t mode, signed long long offset);
    
    int io_size(uintptr_t handle, size_t *length);
    
    int io_read(uintptr_t handle, uintptr_t buffer, size_t length,
    		size_t *length_read);
    
    int io_write(uintptr_t handle, const uintptr_t buffer, size_t length,
    		size_t *length_written);
    
    int io_close(uintptr_t handle);
    
    操作描述
    io_open打开IO实体
    io_seek寻找IO实体中的特定位置
    io_size确定IO实体的长度
    io_read从IO实体中读取数据
    io_write向IO实体中写入数据
    io_close关闭IO实体
驱动

定义了一些用于IO存储框架的数据结构和API,用于创建设备连接和访问不同类型的存储设备。

  1. IO实体

    io_entity结构体表示一个可访问的IO构造,例如一个文件,它包含一个指向设备信息结构体的指针和一些设备相关的信息。

    /* Generic IO entity structure,representing an accessible IO construct on the
     * device, such as a file */
    typedef struct io_entity {
    	struct io_dev_info *dev_handle;
    	uintptr_t info;
    } io_entity_t;
    
  2. 设备信息

    io_dev_info结构体提供了设备特定的函数和添加驱动程序特定状态的方法。它包含一个指向设备函数结构体的指针和一些设备相关的信息。

    /* Device info structure, providing device-specific functions and a means of
     * adding driver-specific state */
    typedef struct io_dev_info {
    	const struct io_dev_funcs *funcs;
    	uintptr_t info;
    } io_dev_info_t;
    
  3. 设备连接

    io_dev_connector结构体用于创建到设备的连接,它包含一个指向设备驱动程序打开函数的指针,该函数接受一个设备规范参数和一个指向设备信息结构体指针的指针。

    /* Structure used to create a connection to a type of device */
    typedef struct io_dev_connector {
    	/* dev_open opens a connection to a particular device driver */
    	int (*dev_open)(const uintptr_t dev_spec, io_dev_info_t **dev_info);
    } io_dev_connector_t;
    
  4. 设备驱动操作

    io_dev_funcs结构体包含了一些设备驱动程序的函数指针,用于操作设备,例如打开、读取、写入、关闭等。

    /* Structure to hold device driver function pointers */
    typedef struct io_dev_funcs {
    	io_type_t (*type)(void);
    	int (*open)(io_dev_info_t *dev_info, const uintptr_t spec,
    			io_entity_t *entity);
    	int (*seek)(io_entity_t *entity, int mode, signed long long offset);
    	int (*size)(io_entity_t *entity, size_t *length);
    	int (*read)(io_entity_t *entity, uintptr_t buffer, size_t length,
    			size_t *length_read);
    	int (*write)(io_entity_t *entity, const uintptr_t buffer,
    			size_t length, size_t *length_written);
    	int (*close)(io_entity_t *entity);
    	int (*dev_init)(io_dev_info_t *dev_info, const uintptr_t init_params);
    	int (*dev_close)(io_dev_info_t *dev_info);
    } io_dev_funcs_t;
    
    操作描述
    type返回设备类型
    open打开一个实体并返回一个io_entity_t结构体
    seek改变实体的读写位置
    size返回实体的大小
    read从实体中读取数据
    write向实体中写入数据
    close关闭实体
    dev_init初始化设备信息结构体
    dev_close关闭设备
  5. 设备注册

    io_register_device函数用于注册一个IO设备,它接受一个指向设备信息结构体的指针作为参数,并返回注册是否成功。

    /* Register an IO device */
    int io_register_device(const io_dev_info_t *dev_info);
    

使用示例

以qemu平台从memmap设备加载镜像为例(其也支持半主机方式),看一下IO框架的使用方法和流程。

初始化

bl1_platform_setup中会调用plat_qemu_io_setup进行qemu平台的IO初始化。

void plat_qemu_io_setup(void)
{
	int io_result;

	io_result = register_io_dev_fip(&fip_dev_con);
	assert(io_result == 0);

	io_result = register_io_dev_memmap(&memmap_dev_con);
	assert(io_result == 0);

	/* Open connections to devices and cache the handles */
	io_result = io_dev_open(fip_dev_con, (uintptr_t)NULL,
				&fip_dev_handle);
	assert(io_result == 0);

	io_result = io_dev_open(memmap_dev_con, (uintptr_t)NULL,
				&memmap_dev_handle);
	assert(io_result == 0);

	/* Ignore improbable errors in release builds */
	(void)io_result;
}
  • register_io_dev_fip:注册IO层的FIP驱动,在其中调用io_register_device注册一个FIP IO设备,返回一个对fip设备的连接句柄fip_dev_connector。

    static const io_dev_connector_t fip_dev_connector = {
    	.dev_open = fip_dev_open
    };
    
  • io_dev_open(fip_dev_con):打开对FIP设备的连接,即会调用fip_dev_open函数,从fip设备池中分配设备信息dev_info并初始化,最终返回一个用io_dev_info_t定义的句柄fip_dev_handle。

    static fip_dev_state_t state_pool[MAX_FIP_DEVICES];
    static io_dev_info_t dev_info_pool[MAX_FIP_DEVICES];
    
    static int allocate_dev_info(io_dev_info_t **dev_info)
    {
    	int result = -ENOMEM;
    
    	assert(dev_info != NULL);
    
    	if (fip_dev_count < (unsigned int)MAX_FIP_DEVICES) {
    		unsigned int index = 0;
    
    		result = find_first_fip_state(0, &index);
    		assert(result == 0);
    		/* initialize dev_info */
    		dev_info_pool[index].funcs = &fip_dev_funcs;
    		dev_info_pool[index].info =
    				(uintptr_t)&state_pool[index];
    		*dev_info = &dev_info_pool[index];
    		++fip_dev_count;
    	}
    
    	return result;
    }
    
    static int fip_dev_open(const uintptr_t dev_spec,
    			 io_dev_info_t **dev_info)
    {
    	int result;
    	io_dev_info_t *info;
    	fip_dev_state_t *state;
    
    	assert(dev_info != NULL);
    #if MAX_FIP_DEVICES > 1
    	assert(dev_spec != (uintptr_t)NULL);
    #endif
    
    	result = allocate_dev_info(&info);
    	if (result != 0)
    		return -ENOMEM;
    
    	state = (fip_dev_state_t *)info->info;
    
    	state->dev_spec = dev_spec;
    
    	*dev_info = info;
    
    	return 0;
    }
    

    这个fip_dev_handle可以理解为对fip设备操作所需要的所有信息,展开类似如下。

    static const io_dev_funcs_t fip_dev_funcs = {
    	.type = device_type_fip,
    	.open = fip_file_open,
    	.seek = NULL,
    	.size = fip_file_len,
    	.read = fip_file_read,
    	.write = NULL,
    	.close = fip_file_close,
    	.dev_init = fip_dev_init,
    	.dev_close = fip_dev_close,
    };
    
    typedef struct {
    	uintptr_t dev_spec;
    	uint16_t plat_toc_flag;
    } fip_dev_state_t;
    
    (io_dev_info_t*)fip_dev_handle->funcs = &fip_dev_funcs;
    (io_dev_info_t*)fip_dev_handle->info.dev_spec = NULL;
    (io_dev_info_t*)fip_dev_handle->info.plat_toc_flag = 0;
    
  • register_io_dev_memmap:注册IO层的memmap驱动,同理在其中调用io_register_device(&memmap_dev_info)注册一个memmap IO设备,memmap_dev_info设备信息定义的设备驱动操作如下。

    static const io_dev_funcs_t memmap_dev_funcs = {
    	.type = device_type_memmap,
    	.open = memmap_block_open,
    	.seek = memmap_block_seek,
    	.size = memmap_block_len,
    	.read = memmap_block_read,
    	.write = memmap_block_write,
    	.close = memmap_block_close,
    	.dev_init = NULL,
    	.dev_close = memmap_dev_close,
    };
    
    /* No state associated with this device so structure can be const */
    static io_dev_info_t memmap_dev_info = {
    	.funcs = &memmap_dev_funcs,
    	.info = (uintptr_t)NULL
    };
    
    

    注册成功后返回一个对memmap设备的连接句柄,包含对memmap设备驱动的打开操作如下。

    static const io_dev_connector_t memmap_dev_connector = {
    	.dev_open = memmap_dev_open
    };
    
  • io_dev_open(memmap_dev_con):打开对memmap设备的连接,即会调用memmap_dev_open函数,最终返回一个用io_dev_info_t定义的句柄memmap_dev_handle。

    static io_dev_info_t memmap_dev_info = {
    	.funcs = &memmap_dev_funcs,
    	.info = (uintptr_t)NULL
    };
    
    static int memmap_dev_open(const uintptr_t dev_spec __unused,
    			   io_dev_info_t **dev_info)
    {
    	assert(dev_info != NULL);
    	*dev_info = &memmap_dev_info;
    	return 0;
    }
    
镜像加载

首先看一下qemu定义的io策略,为了便于描述,只考虑了BL2镜像,删除了其他镜像的定义。

struct plat_io_policy {
	uintptr_t *dev_handle;
	uintptr_t image_spec;
	int (*check)(const uintptr_t spec);
};

static const io_block_spec_t fip_block_spec = {
	.offset = PLAT_QEMU_FIP_BASE,
	.length = PLAT_QEMU_FIP_MAX_SIZE
};

static const io_uuid_spec_t bl2_uuid_spec = {
	.uuid = UUID_TRUSTED_BOOT_FIRMWARE_BL2,
};

static const struct plat_io_policy policies[] = {
	[FIP_IMAGE_ID] = {
		&memmap_dev_handle,
		(uintptr_t)&fip_block_spec,
		open_memmap
	},
	[BL2_IMAGE_ID] = {
		&fip_dev_handle,
		(uintptr_t)&bl2_uuid_spec,
		open_fip
	},
};

接着看下bl_common.c定义的load_image加载镜像函数,其通过标准的IO接口加载镜像。

plat_get_image_source
io_open
io_size
io_read
io_close
io_dev_close
static int load_image(unsigned int image_id, image_info_t *image_data)
{
	uintptr_t dev_handle;
	uintptr_t image_handle;
	uintptr_t image_spec;
	uintptr_t image_base;
	size_t image_size;
	size_t bytes_read;
	int io_result;

	assert(image_data != NULL);
	assert(image_data->h.version >= VERSION_2);

	image_base = image_data->image_base;

	/* Obtain a reference to the image by querying the platform layer */
	io_result = plat_get_image_source(image_id, &dev_handle, &image_spec);
	if (io_result != 0) {
		WARN("Failed to obtain reference to image id=%u (%i)\n",
			image_id, io_result);
		return io_result;
	}

	/* Attempt to access the image */
	io_result = io_open(dev_handle, image_spec, &image_handle);
	if (io_result != 0) {
		WARN("Failed to access image id=%u (%i)\n",
			image_id, io_result);
		return io_result;
	}

	INFO("Loading image id=%u at address 0x%lx\n", image_id, image_base);

	/* Find the size of the image */
	io_result = io_size(image_handle, &image_size);
	if ((io_result != 0) || (image_size == 0U)) {
		WARN("Failed to determine the size of the image id=%u (%i)\n",
			image_id, io_result);
		goto exit;
	}

	/* Check that the image size to load is within limit */
	if (image_size > image_data->image_max_size) {
		WARN("Image id=%u size out of bounds\n", image_id);
		io_result = -EFBIG;
		goto exit;
	}

	/*
	 * image_data->image_max_size is a uint32_t so image_size will always
	 * fit in image_data->image_size.
	 */
	image_data->image_size = (uint32_t)image_size;

	/* We have enough space so load the image now */
	/* TODO: Consider whether to try to recover/retry a partially successful read */
	io_result = io_read(image_handle, image_base, image_size, &bytes_read);
	if ((io_result != 0) || (bytes_read < image_size)) {
		WARN("Failed to load image id=%u (%i)\n", image_id, io_result);
		goto exit;
	}

	INFO("Image id=%u loaded: 0x%lx - 0x%lx\n", image_id, image_base,
	     (uintptr_t)(image_base + image_size));

exit:
	(void)io_close(image_handle);
	/* Ignore improbable/unrecoverable error in 'close' */

	/* TODO: Consider maintaining open device connection from this bootloader stage */
	(void)io_dev_close(dev_handle);
	/* Ignore improbable/unrecoverable error in 'dev_close' */

	return io_result;
}
  • 【1】 plat_get_image_source:获取镜像源,这里假设加载镜像BL2_IMAGE_ID,从policies查找该镜像的IO策略,即会调用open_fip。

    int plat_get_image_source(unsigned int image_id, uintptr_t *dev_handle,
    			  uintptr_t *image_spec)
    {
    	int result;
    	const struct plat_io_policy *policy;
    
    	assert(image_id < ARRAY_SIZE(policies));
    
    	policy = &policies[image_id];
    	result = policy->check(policy->image_spec);
    	if (result == 0) {
    		*image_spec = policy->image_spec;
    		*dev_handle = *(policy->dev_handle);
    	} else {
    		VERBOSE("Trying alternative IO\n");
    		result = get_alt_image_source(image_id, dev_handle, image_spec);
    	}
    
    	return result;
    }
    
  • 【1.1】open_fip:打开fip会调用io_dev_init和io_open函数。

    static int open_fip(const uintptr_t spec)
    {
    	int result;
    	uintptr_t local_image_handle;
    
    	/* See if a Firmware Image Package is available */
    	result = io_dev_init(fip_dev_handle, (uintptr_t)FIP_IMAGE_ID);
    	if (result == 0 && spec != (uintptr_t)NULL) {
    		result = io_open(fip_dev_handle, spec, &local_image_handle);
    		if (result == 0) {
    			VERBOSE("Using FIP\n");
    			io_close(local_image_handle);
    		}
    	}
    	return result;
    }
    
  • 【1.1.1】io_dev_init(fip_dev_handle, (uintptr_t)FIP_IMAGE_ID):初始化操作会调用上面存储初始化定义的fip_dev_init函数。首先获取镜像FIP_IMAGE_ID的镜像源,然后打开,读取header头,并校验头是否正确。这里plat_get_image_source又会像上面一样获取FIP_IMAGE_ID的IO策略,调用到open_memmap函数。

    static int fip_dev_init(io_dev_info_t *dev_info, const uintptr_t init_params)
    {
    	int result;
    	unsigned int image_id = (unsigned int)init_params;
    	uintptr_t backend_handle;
    	fip_toc_header_t header;
    	size_t bytes_read;
    	fip_dev_state_t *state;
    
    	assert(dev_info != NULL);
    
    	state = (fip_dev_state_t *)dev_info->info;
    
    	/* Obtain a reference to the image by querying the platform layer */
    	result = plat_get_image_source(image_id, &backend_dev_handle,
    				       &backend_image_spec);
    	if (result != 0) {
    		WARN("Failed to obtain reference to image id=%u (%i)\n",
    			image_id, result);
    		result = -ENOENT;
    		goto fip_dev_init_exit;
    	}
    
    	/* Attempt to access the FIP image */
    	result = io_open(backend_dev_handle, backend_image_spec,
    			 &backend_handle);
    	if (result != 0) {
    		WARN("Failed to access image id=%u (%i)\n", image_id, result);
    		result = -ENOENT;
    		goto fip_dev_init_exit;
    	}
    
    	result = io_read(backend_handle, (uintptr_t)&header, sizeof(header),
    			&bytes_read);
    	if (result == 0) {
    		if (!is_valid_header(&header)) {
    			WARN("Firmware Image Package header check failed.\n");
    			result = -ENOENT;
    		} else {
    			VERBOSE("FIP header looks OK.\n");
    			/*
    			 * Store 16-bit Platform ToC flags field which occupies
    			 * bits [32-47] in fip header.
    			 */
    			state->plat_toc_flag = (header.flags >> 32) & 0xffff;
    		}
    	}
    
    	io_close(backend_handle);
    
     fip_dev_init_exit:
    	return result;
    }
    
  • 【1.1.1.1】open_memmap:类似的也是初始化io_dev_init和io_open,这个io_dev_init会调用memmap中定义的,这里定义是空.dev_init = NULL,不会调用。

    static int open_memmap(const uintptr_t spec)
    {
    	int result;
    	uintptr_t local_image_handle;
    
    	result = io_dev_init(memmap_dev_handle, (uintptr_t)NULL);
    	if (result == 0) {
    		result = io_open(memmap_dev_handle, spec, &local_image_handle);
    		if (result == 0) {
    			VERBOSE("Using Memmap\n");
    			io_close(local_image_handle);
    		}
    	}
    	return result;
    }
    
  • 【1.1.1.2】io_open(memmap_dev_handle, spec, &local_image_handle):首先分配一个memmap类型的IO实体,然后调用memmap中定义的memmap_block_open函数,设置memmap文件的基地址及最大大小,最终设置handle句柄以跟踪到这个IO实体上。

    int io_open(uintptr_t dev_handle, const uintptr_t spec, uintptr_t *handle)
    {
    	int result;
    	assert((spec != (uintptr_t)NULL) && (handle != NULL));
    	assert(is_valid_dev(dev_handle));
    
    	io_dev_info_t *dev = (io_dev_info_t *)dev_handle;
    	io_entity_t *entity;
    
    	result = allocate_entity(&entity);
    
    	if (result == 0) {
    		assert(dev->funcs->open != NULL);
    		result = dev->funcs->open(dev, spec, entity);
    
    		if (result == 0) {
    			entity->dev_handle = dev;
    			set_handle(handle, entity);
    		} else
    			free_entity(entity);
    	}
    	return result;
    }
    
    static int memmap_block_open(io_dev_info_t *dev_info, const uintptr_t spec,
    			     io_entity_t *entity)
    {
    	int result = -ENOMEM;
    	const io_block_spec_t *block_spec = (io_block_spec_t *)spec;
    
    	/* Since we need to track open state for seek() we only allow one open
    	 * spec at a time. When we have dynamic memory we can malloc and set
    	 * entity->info.
    	 */
    	if (current_memmap_file.in_use == 0) {
    		assert(block_spec != NULL);
    		assert(entity != NULL);
    
    		current_memmap_file.in_use = 1;
    		current_memmap_file.base = block_spec->offset;
    		/* File cursor offset for seek and incremental reads etc. */
    		current_memmap_file.file_pos = 0;
    		current_memmap_file.size = block_spec->length;
    		entity->info = (uintptr_t)&current_memmap_file;
    		result = 0;
    	} else {
    		WARN("A Memmap device is already active. Close first.\n");
    	}
    
    	return result;
    }
    
  • 【1.1.1.3】io_close(local_image_handle):如果io_open打开成功,则关闭临时的local_image_handle,最终调用memmap_block_close函数,释放刚才打开的current_memmap_file。这里我觉得这里首先调用打开和关闭的目的应该是校验设备能否操作成功,我测试这些代码可以注释掉,以后后面读取镜像时还是会调用一遍

    int io_close(uintptr_t handle)
    {
    	int result = 0;
    	assert(is_valid_entity(handle));
    
    	io_entity_t *entity = (io_entity_t *)handle;
    
    	io_dev_info_t *dev = entity->dev_handle;
    
    	/* Absence of registered function implies NOP here */
    	if (dev->funcs->close != NULL)
    		result = dev->funcs->close(entity);
    
    	/* Ignore improbable free_entity failure */
    	(void)free_entity(entity);
    
    	return result;
    }
    
    static int memmap_block_close(io_entity_t *entity)
    {
    	assert(entity != NULL);
    
    	entity->info = 0;
    
    	/* This would be a mem free() if we had malloc.*/
    	zeromem((void *)&current_memmap_file, sizeof(current_memmap_file));
    
    	return 0;
    }
    

    至此open_memmap函数执行完毕,并执行plat_get_image_source剩下的代码,并返回两个重要的参数给到fip中定义的backend_dev_handlebackend_image_spec,然后回到fip_dev_init函数中继续执行io_open函数。

    backend_dev_handle = memmap_dev_handle;
    backend_image_spec = fip_block_spec;
    

    这两个参数句柄和描述即是FIP如何从memmap取数据的操作信息集合。

  • 【1.1.2】io_open(backend_dev_handle, backend_image_spec, &backend_handle):同理也是先分配一个fip类型的IO实体,因为这里backend_dev_handle关联到memmap设备上,因此会调用memmap中定义的memmap_block_open函数,即类似又重新执行一遍上面open_memmap函数中定义的io_open函数。

  • 【1.1.3】io_read(backend_handle, (uintptr_t)&header, sizeof(header), &bytes_read):调用read接口读取fip镜像头大小的数据,即调用memmap_block_read读取函数,由于是memmap是内存设备,这里即通过memcpy拷贝数据到buffer中。

    static int memmap_block_read(io_entity_t *entity, uintptr_t buffer,
    			     size_t length, size_t *length_read)
    {
    	memmap_file_state_t *fp;
    	unsigned long long pos_after;
    
    	assert(entity != NULL);
    	assert(length_read != NULL);
    
    	fp = (memmap_file_state_t *) entity->info;
    
    	/* Assert that file position is valid for this read operation */
    	pos_after = fp->file_pos + length;
    	assert((pos_after >= fp->file_pos) && (pos_after <= fp->size));
    
    	memcpy((void *)buffer,
    	       (void *)((uintptr_t)(fp->base + fp->file_pos)), length);
    
    	*length_read = length;
    
    	/* Set file position after read */
    	fp->file_pos = pos_after;
    
    	return 0;
    }
    
  • 【1.1.4】is_valid_header:校验fip头是否正确

  • io_close(backend_handle):关闭打开的backend_handle,至此fip_dev_init函数执行完毕,主要是判断memmap存储的镜像是否是FIP固件。然后回到open_fip函数继续执行

  • 【1.2】io_open(fip_dev_handle, spec, &local_image_handle):打开FIP IO实体,传入的参数spec为IO策略定义的bl2_uuid_spec,即通过UUID描述BL2镜像。

    static int fip_file_open(io_dev_info_t *dev_info, const uintptr_t spec,
    			 io_entity_t *entity)
    {
    	int result;
    	uintptr_t backend_handle;
    	const io_uuid_spec_t *uuid_spec = (io_uuid_spec_t *)spec;
    	static const uuid_t uuid_null = { {0} }; /* Double braces for clang */
    	size_t bytes_read;
    	int found_file = 0;
    
    	assert(uuid_spec != NULL);
    	assert(entity != NULL);
    
    	/* Can only have one file open at a time for the moment. We need to
    	 * track state like file cursor position. We know the header lives at
    	 * offset zero, so this entry should never be zero for an active file.
    	 * When the system supports dynamic memory allocation we can allow more
    	 * than one open file at a time if needed.
    	 */
    	if (current_fip_file.entry.offset_address != 0U) {
    		WARN("fip_file_open : Only one open file at a time.\n");
    		return -ENFILE;
    	}
    
    	/* Attempt to access the FIP image */
    	result = io_open(backend_dev_handle, backend_image_spec,
    			 &backend_handle);
    	if (result != 0) {
    		WARN("Failed to open Firmware Image Package (%i)\n", result);
    		result = -ENOENT;
    		goto fip_file_open_exit;
    	}
    
    	/* Seek past the FIP header into the Table of Contents */
    	result = io_seek(backend_handle, IO_SEEK_SET,
    			 (signed long long)sizeof(fip_toc_header_t));
    	if (result != 0) {
    		WARN("fip_file_open: failed to seek\n");
    		result = -ENOENT;
    		goto fip_file_open_close;
    	}
    
    	found_file = 0;
    	do {
    		result = io_read(backend_handle,
    				 (uintptr_t)&current_fip_file.entry,
    				 sizeof(current_fip_file.entry),
    				 &bytes_read);
    		if (result == 0) {
    			if (compare_uuids(&current_fip_file.entry.uuid,
    					  &uuid_spec->uuid) == 0) {
    				found_file = 1;
    			}
    		} else {
    			WARN("Failed to read FIP (%i)\n", result);
    			goto fip_file_open_close;
    		}
    	} while ((found_file == 0) &&
    			(compare_uuids(&current_fip_file.entry.uuid,
    				&uuid_null) != 0));
    
    	if (found_file == 1) {
    		/* All fine. Update entity info with file state and return. Set
    		 * the file position to 0. The 'current_fip_file.entry' holds
    		 * the base and size of the file.
    		 */
    		current_fip_file.file_pos = 0;
    		entity->info = (uintptr_t)&current_fip_file;
    	} else {
    		/* Did not find the file in the FIP. */
    		current_fip_file.entry.offset_address = 0;
    		result = -ENOENT;
    	}
    
     fip_file_open_close:
    	io_close(backend_handle);
    
     fip_file_open_exit:
    	return result;
    }
    

    fip_file_open主要是从FIP固件中寻找BL2 UUID是否存在。具体来说,FIP中的镜像是通过由toc_entry来标记,循环读取所有的toc_entry,并判断是否与BL2 UUID相等,如果相等,则表示FIP固件中有BL2镜像。

  • 【1.3】io_close(local_image_handle):关闭local_image_handle实体。至此open_fip函数执行完成,回到最顶层的load_image函数中继续执行。

  • 【2】 io_open(dev_handle, image_spec, &image_handle):打开BL2镜像的IO实体,这里又会执行fip_file_open函数去寻找BL2 UUID是否存在。

  • 【3】 io_size(image_handle, &image_size):获取BL2镜像的大小,调用fip_file_len函数,从entry中读取size得到。

    static int fip_file_len(io_entity_t *entity, size_t *length)
    {
    	assert(entity != NULL);
    	assert(length != NULL);
    
    	*length =  ((fip_file_state_t *)entity->info)->entry.size;
    
    	return 0;
    }
    
  • 【4】io_read(image_handle, image_base, image_size, &bytes_read):到这里才是真正的加载BL2负载像到内存地址上,底层调用fip_file_read函数,使用标准的io_seek,io_read等函数加载镜像。

    static int fip_file_read(io_entity_t *entity, uintptr_t buffer, size_t length,
    			  size_t *length_read)
    {
    	int result;
    	fip_file_state_t *fp;
    	size_t file_offset;
    	size_t bytes_read;
    	uintptr_t backend_handle;
    
    	assert(entity != NULL);
    	assert(length_read != NULL);
    	assert(entity->info != (uintptr_t)NULL);
    
    	/* Open the backend, attempt to access the blob image */
    	result = io_open(backend_dev_handle, backend_image_spec,
    			 &backend_handle);
    	if (result != 0) {
    		WARN("Failed to open FIP (%i)\n", result);
    		result = -ENOENT;
    		goto fip_file_read_exit;
    	}
    
    	fp = (fip_file_state_t *)entity->info;
    
    	/* Seek to the position in the FIP where the payload lives */
    	file_offset = fp->entry.offset_address + fp->file_pos;
    	result = io_seek(backend_handle, IO_SEEK_SET,
    			 (signed long long)file_offset);
    	if (result != 0) {
    		WARN("fip_file_read: failed to seek\n");
    		result = -ENOENT;
    		goto fip_file_read_close;
    	}
    
    	result = io_read(backend_handle, buffer, length, &bytes_read);
    	if (result != 0) {
    		/* We cannot read our data. Fail. */
    		WARN("Failed to read payload (%i)\n", result);
    		result = -ENOENT;
    		goto fip_file_read_close;
    	} else {
    		/* Set caller length and new file position. */
    		*length_read = bytes_read;
    		fp->file_pos += bytes_read;
    	}
    
    /* Close the backend. */
     fip_file_read_close:
    	io_close(backend_handle);
    
     fip_file_read_exit:
    	return result;
    }
    
  • 【4.1】io_open(backend_dev_handle, backend_image_spec, &backend_handle):打开memmap设备IO实体

  • 【4.2】io_seek(backend_handle, IO_SEEK_SET, (signed long long)file_offset):Seek到BL2镜像负载在FIP中的实际偏移位置,会调用到底层的memmap_block_seek函数。

    static int memmap_block_seek(io_entity_t *entity, int mode,
    			     signed long long offset)
    {
    	int result = -ENOENT;
    	memmap_file_state_t *fp;
    
    	/* We only support IO_SEEK_SET for the moment. */
    	if (mode == IO_SEEK_SET) {
    		assert(entity != NULL);
    
    		fp = (memmap_file_state_t *) entity->info;
    
    		/* Assert that new file position is valid */
    		assert((offset >= 0) &&
    		       ((unsigned long long)offset < fp->size));
    
    		/* Reset file position */
    		fp->file_pos = (unsigned long long)offset;
    		result = 0;
    	}
    
    	return result;
    }
    
  • 【4.3】io_read(backend_handle, buffer, length, &bytes_read):读取BL2镜像大小,调用底层的memmap_block_read函数。

    static int memmap_block_read(io_entity_t *entity, uintptr_t buffer,
    			     size_t length, size_t *length_read)
    {
    	memmap_file_state_t *fp;
    	unsigned long long pos_after;
    
    	assert(entity != NULL);
    	assert(length_read != NULL);
    
    	fp = (memmap_file_state_t *) entity->info;
    
    	/* Assert that file position is valid for this read operation */
    	pos_after = fp->file_pos + length;
    	assert((pos_after >= fp->file_pos) && (pos_after <= fp->size));
    
    	memcpy((void *)buffer,
    	       (void *)((uintptr_t)(fp->base + fp->file_pos)), length);
    
    	*length_read = length;
    
    	/* Set file position after read */
    	fp->file_pos = pos_after;
    
    	return 0;
    }
    
  • 【5】io_close(image_handle):关闭镜像句柄

  • 【6】io_dev_close(dev_handle):关闭设备句柄

欢迎关注“安全有理”微信公众号。

安全有理

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值