/****************************************************************************** * Reserve the memory region delimited by 'addr' and 'size'. The extents of free * memory are passed in 'free_base' and 'free_size' and they will be updated to * reflect the memory usage. * The caller must ensure the memory to reserve is free. *****************************************************************************/ void reserve_mem(uint64_t *free_base, size_t *free_size, uint64_t addr, size_t size) { size_t discard_size; size_t reserved_size; unsigned int pos; assert(free_base != NULL); assert(free_size != NULL); assert(is_mem_free(*free_base, *free_size, addr, size)); pos = choose_mem_pos(*free_base, *free_base + *free_size, addr, addr + size, &discard_size); reserved_size = size + discard_size; *free_size -= reserved_size; if (pos == BOTTOM) *free_base = addr + size; VERBOSE("Reserved 0x%lx bytes (discarded 0x%lx bytes %s)\n", reserved_size, discard_size, pos == TOP ? "above" : "below"); }
/****************************************************************************** * Reserve the memory region delimited by 'addr' and 'size'. The extents of free * memory are passed in 'free_base' and 'free_size' and they will be updated to * reflect the memory usage. * The caller must ensure the memory to reserve is free and that the addresses * and sizes passed in arguments are sane. *****************************************************************************/ void reserve_mem(uintptr_t *free_base, size_t *free_size, uintptr_t addr, size_t size) { size_t discard_size; size_t reserved_size; unsigned int pos; assert(free_base != NULL); assert(free_size != NULL); assert(is_mem_free(*free_base, *free_size, addr, size)); if (size == 0) { WARN("Nothing to allocate, requested size is zero\n"); return; } pos = choose_mem_pos(*free_base, *free_base + (*free_size - 1), addr, addr + (size - 1), &discard_size); reserved_size = size + discard_size; *free_size -= reserved_size; if (pos == BOTTOM) *free_base = addr + size; VERBOSE("Reserved 0x%zx bytes (discarded 0x%zx bytes %s)\n", reserved_size, discard_size, pos == TOP ? "above" : "below"); }
/******************************************************************************* * OPTEE Dispatcher setup. The OPTEED finds out the OPTEE entrypoint and type * (aarch32/aarch64) if not already known and initialises the context for entry * into OPTEE for its initialization. ******************************************************************************/ int32_t opteed_setup(void) { entry_point_info_t *ep_info; struct optee_header *header; uint64_t mpidr = read_mpidr(); uint32_t linear_id; uintptr_t init_load_addr; size_t init_size; size_t init_mem_usage; uintptr_t payload_addr; uintptr_t mem_limit; uintptr_t paged_part; uintptr_t paged_size; linear_id = platform_get_core_pos(mpidr); /* * Get information about the Secure Payload (BL32) image. Its * absence is a critical failure. TODO: Add support to * conditionally include the SPD service */ ep_info = bl31_plat_get_next_image_ep_info(SECURE); if (!ep_info) { WARN("No OPTEE provided by BL2 boot loader.\n"); goto err; } header = (struct optee_header *)ep_info->pc; if (header->magic != OPTEE_MAGIC || header->version != OPTEE_VERSION) { WARN("Invalid OPTEE header.\n"); goto err; } if (header->arch == OPTEE_ARCH_ARM32) opteed_rw = OPTEE_AARCH32; else if (header->arch == OPTEE_ARCH_ARM64) opteed_rw = OPTEE_AARCH64; else { WARN("Invalid OPTEE architecture (%d)\n", header->arch); goto err; } init_load_addr = ((uint64_t)header->init_load_addr_hi << 32) | header->init_load_addr_lo; init_size = header->init_size; init_mem_usage = header->init_mem_usage; payload_addr = (uintptr_t)(header + 1); paged_size = header->paged_size; /* * Move OPTEE binary to the required location in memory. * * There's two ways OPTEE can be running in memory: * 1. A memory large enough to keep the entire OPTEE binary * (DRAM currently) * 2. A part of OPTEE in a smaller (and more secure) memory * (SRAM currently). This is achieved with demand paging * of read-only data/code against a backing store in some * larger memory (DRAM currently). * * In either case dictates init_load_addr in the OPTEE * header the address where what's after the header * (payload) should be residing when started. init_size in * the header tells how much of the payload that need to be * copied. init_mem_usage tells how much runtime memory in * total is needed by OPTEE. * * In alternative 2 there's additional data after * init_size, this is the rest of OPTEE which is demand * paged into memory. A pointer to that data is supplied * to OPTEE when initializing. * * Alternative 1 only uses DRAM when executing OPTEE while * alternative 2 uses both SRAM and DRAM to execute. * * All data written which is later read by OPTEE must be flushed * out to memory since OPTEE starts with MMU turned off and caches * disabled. */ if (is_mem_free(BL32_SRAM_BASE, BL32_SRAM_LIMIT - BL32_SRAM_BASE, init_load_addr, init_mem_usage)) { /* Running in SRAM, paging some code against DRAM */ memcpy((void *)init_load_addr, (void *)payload_addr, init_size); flush_dcache_range(init_load_addr, init_size); paged_part = payload_addr + init_size; mem_limit = BL32_SRAM_LIMIT; } else if (is_mem_free(BL32_DRAM_BASE, BL32_DRAM_LIMIT - BL32_DRAM_BASE, init_load_addr, init_mem_usage)) { /* * Running in DRAM. * * The paged part normally empty, but if it isn't, * move it to the end of DRAM before moving the * init part in place. */ paged_part = BL32_DRAM_LIMIT - paged_size; if (paged_size) { if (!is_mem_free(BL32_DRAM_BASE, BL32_DRAM_LIMIT - BL32_DRAM_BASE, init_load_addr, init_mem_usage + paged_size)) { WARN("Failed to reserve memory 0x%lx - 0x%lx\n", init_load_addr, init_load_addr + init_mem_usage + paged_size); goto err; } memcpy((void *)paged_part, (void *)(payload_addr + init_size), paged_size); flush_dcache_range(paged_part, paged_size); } memmove((void *)init_load_addr, (void *)payload_addr, init_size); flush_dcache_range(init_load_addr, init_size); mem_limit = BL32_DRAM_LIMIT; } else { WARN("Failed to reserve memory 0x%lx - 0x%lx\n", init_load_addr, init_load_addr + init_mem_usage); goto err; } opteed_init_optee_ep_state(ep_info, opteed_rw, init_load_addr, paged_part, mem_limit, &opteed_sp_context[linear_id]); /* * All OPTEED initialization done. Now register our init function with * BL31 for deferred invocation */ bl31_register_bl32_init(&opteed_init); return 0; err: WARN("Booting device without OPTEE initialization.\n"); WARN("SMC`s destined for OPTEE will return SMC_UNK\n"); return 1; }
/******************************************************************************* * Generic function to load an image at a specific address given a name and * extents of free memory. It updates the memory layout if the load is * successful, as well as the image information and the entry point information. * The caller might pass a NULL pointer for the entry point if it is not * interested in this information, e.g. because the image just needs to be * loaded in memory but won't ever be executed. * Returns 0 on success, a negative error code otherwise. ******************************************************************************/ int load_image(meminfo_t *mem_layout, unsigned int image_id, uintptr_t image_base, image_info_t *image_data, entry_point_info_t *entry_point_info) { uintptr_t dev_handle; uintptr_t image_handle; uintptr_t image_spec; size_t image_size; size_t bytes_read; int io_result = IO_FAIL; assert(mem_layout != NULL); assert(image_data != NULL); assert(image_data->h.version >= VERSION_1); /* 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 != IO_SUCCESS) { 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 != IO_SUCCESS) { 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 != IO_SUCCESS) || (image_size == 0)) { WARN("Failed to determine the size of the image id=%u (%i)\n", image_id, io_result); goto exit; } /* Check that the memory where the image will be loaded is free */ if (!is_mem_free(mem_layout->free_base, mem_layout->free_size, image_base, image_size)) { WARN("Failed to reserve memory: 0x%lx - 0x%lx\n", image_base, image_base + image_size); dump_load_info(image_base, image_size, mem_layout); io_result = -ENOMEM; goto exit; } /* 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 != IO_SUCCESS) || (bytes_read < image_size)) { WARN("Failed to load image id=%u (%i)\n", image_id, io_result); goto exit; } /* * Update the memory usage info. * This is done after the actual loading so that it is not updated when * the load is unsuccessful. * If the caller does not provide an entry point, bypass the memory * reservation. */ if (entry_point_info != NULL) { reserve_mem(&mem_layout->free_base, &mem_layout->free_size, image_base, image_size); } else { INFO("Skip reserving memory: 0x%lx - 0x%lx\n", image_base, image_base + image_size); } image_data->image_base = image_base; image_data->image_size = image_size; if (entry_point_info != NULL) entry_point_info->pc = image_base; /* * File has been successfully loaded. * Flush the image in TZRAM so that the next EL can see it. */ flush_dcache_range(image_base, image_size); INFO("Image id=%u loaded: 0x%lx - 0x%lx\n", image_id, image_base, image_base + image_size); exit: io_close(image_handle); /* Ignore improbable/unrecoverable error in 'close' */ /* TODO: Consider maintaining open device connection from this bootloader stage */ io_dev_close(dev_handle); /* Ignore improbable/unrecoverable error in 'dev_close' */ return io_result; }
/******************************************************************************* * Generic function to load an image at a specific address given an image ID and * extents of free memory. * * If the load is successful then the image information is updated. * * If the entry_point_info argument is not NULL then this function also updates: * - the memory layout to mark the memory as reserved; * - the entry point information. * * The caller might pass a NULL pointer for the entry point if they are not * interested in this information. This is typically the case for non-executable * images (e.g. certificates) and executable images that won't ever be executed * on the application processor (e.g. additional microcontroller firmware). * * Returns 0 on success, a negative error code otherwise. ******************************************************************************/ int load_image(meminfo_t *mem_layout, unsigned int image_id, uintptr_t image_base, image_info_t *image_data, entry_point_info_t *entry_point_info) { uintptr_t dev_handle; uintptr_t image_handle; uintptr_t image_spec; size_t image_size; size_t bytes_read; int io_result; assert(mem_layout != NULL); assert(image_data != NULL); assert(image_data->h.version == VERSION_1); /* 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 %p\n", image_id, (void *) image_base); /* Find the size of the image */ io_result = io_size(image_handle, &image_size); if ((io_result != 0) || (image_size == 0)) { WARN("Failed to determine the size of the image id=%u (%i)\n", image_id, io_result); goto exit; } /* Check that the memory where the image will be loaded is free */ if (!is_mem_free(mem_layout->free_base, mem_layout->free_size, image_base, image_size)) { WARN("Failed to reserve region [base = %p, size = 0x%zx]\n", (void *) image_base, image_size); dump_load_info(image_base, image_size, mem_layout); io_result = -ENOMEM; goto exit; } /* 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; } image_data->image_base = image_base; image_data->image_size = image_size; /* * Update the memory usage info. * This is done after the actual loading so that it is not updated when * the load is unsuccessful. * If the caller does not provide an entry point, bypass the memory * reservation. */ if (entry_point_info != NULL) { reserve_mem(&mem_layout->free_base, &mem_layout->free_size, image_base, image_size); entry_point_info->pc = image_base; } else { INFO("Skip reserving region [base = %p, size = 0x%zx]\n", (void *) image_base, image_size); } #if !TRUSTED_BOARD_BOOT /* * File has been successfully loaded. * Flush the image to main memory so that it can be executed later by * any CPU, regardless of cache and MMU state. * When TBB is enabled the image is flushed later, after image * authentication. */ flush_dcache_range(image_base, image_size); #endif /* TRUSTED_BOARD_BOOT */ INFO("Image id=%u loaded at address %p, size = 0x%zx\n", image_id, (void *) image_base, image_size); exit: io_close(image_handle); /* Ignore improbable/unrecoverable error in 'close' */ /* TODO: Consider maintaining open device connection from this bootloader stage */ io_dev_close(dev_handle); /* Ignore improbable/unrecoverable error in 'dev_close' */ return io_result; }