blob: 8554709f4f7d24fdbaf7ed55762961a1bb5b68d9 [file] [log] [blame]
/*
* Copyright (c) 2018-2024, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <cactus_message_loop.h>
#include <drivers/arm/pl011.h>
#include <drivers/console.h>
#include <lib/aarch64/arch_helpers.h>
#include <lib/hob/hob.h>
#include <lib/tftf_lib.h>
#include <lib/xlat_tables/xlat_mmu_helpers.h>
#include <lib/xlat_tables/xlat_tables_v2.h>
#include <ffa_helpers.h>
#include <plat_arm.h>
#include <plat/common/platform.h>
#include <platform_def.h>
#include <sp_debug.h>
#include <sp_helpers.h>
#include <spm_helpers.h>
#include <std_svc.h>
#include "sp_def.h"
#include "sp_tests.h"
#include "cactus.h"
/* Host machine information injected by the build system in the ELF file. */
extern const char build_message[];
extern const char version_string[];
extern void secondary_cold_entry(void);
/* Global ffa_id */
ffa_id_t g_ffa_id;
/*
*
* Message loop function
* Notice we cannot use regular print functions because this serves to both
* "primary" and "secondary" VMs. Secondary VM cannot access UART directly
* but rather through Hafnium print hypercall.
*
*/
static void __dead2 message_loop(ffa_id_t vm_id, struct mailbox_buffers *mb)
{
struct ffa_value ffa_ret;
ffa_id_t destination;
/*
* This initial wait call is necessary to inform SPMD that
* SP initialization has completed. It blocks until receiving
* a direct message request.
*/
ffa_ret = ffa_msg_wait();
for (;;) {
VERBOSE("Woke up with func:%s id: %x\n",
ffa_func_name(ffa_func_id(ffa_ret)),
ffa_func_id(ffa_ret));
if (ffa_func_id(ffa_ret) == FFA_ERROR) {
ERROR("Error: %s\n",
ffa_error_name(ffa_error_code(ffa_ret)));
break;
}
if (ffa_func_id(ffa_ret) != FFA_MSG_SEND_DIRECT_REQ_SMC32 &&
ffa_func_id(ffa_ret) != FFA_MSG_SEND_DIRECT_REQ_SMC64 &&
ffa_func_id(ffa_ret) != FFA_INTERRUPT &&
ffa_func_id(ffa_ret) != FFA_RUN) {
ERROR("%s(%x) unknown func id %s\n", __func__, vm_id,
ffa_func_name(ffa_func_id(ffa_ret)));
break;
}
if ((ffa_func_id(ffa_ret) == FFA_INTERRUPT) ||
(ffa_func_id(ffa_ret) == FFA_RUN)) {
/*
* Received FFA_INTERRUPT in waiting state.
* The interrupt id is passed although this is just
* informational as we're running with virtual
* interrupts unmasked and the interrupt is processed
* by the interrupt handler.
*/
if (ffa_func_id(ffa_ret) == FFA_RUN) {
/*
* Received FFA_RUN in waiting state, the
* endpoint simply returns by FFA_MSG_WAIT.
*/
VERBOSE("Nothing to do. Exit to NWd\n");
}
ffa_ret = ffa_msg_wait();
continue;
}
destination = ffa_dir_msg_dest(ffa_ret);
if (destination != vm_id) {
ERROR("%s(%u) invalid vm id 0x%x\n",
__func__, vm_id, destination);
break;
}
if (!cactus_handle_cmd(&ffa_ret, &ffa_ret, mb)) {
break;
}
}
panic();
}
static const mmap_region_t cactus_mmap[] __attribute__((used)) = {
MAP_REGION_FLAT(PLAT_CACTUS_DEVICE_BASE, PLAT_CACTUS_DEVICE_SIZE,
MT_DEVICE | MT_RW),
/* scratch memory allocated to be used for running SMMU tests */
MAP_REGION_FLAT(PLAT_CACTUS_MEMCPY_BASE, PLAT_CACTUS_MEMCPY_RANGE,
MT_MEMORY | MT_RW),
#if PLAT_fvp
MAP_REGION_FLAT(PLAT_CACTUS_NS_MEMCPY_BASE, PLAT_CACTUS_MEMCPY_RANGE,
MT_MEMORY | MT_RW | MT_NS),
#endif
{0}
};
static void cactus_print_memory_layout(unsigned int vm_id)
{
INFO("Secure Partition memory layout:\n");
INFO(" Text region : %p - %p\n",
(void *)CACTUS_TEXT_START, (void *)CACTUS_TEXT_END);
INFO(" Read-only data region : %p - %p\n",
(void *)CACTUS_RODATA_START, (void *)CACTUS_RODATA_END);
INFO(" Data region : %p - %p\n",
(void *)CACTUS_DATA_START, (void *)CACTUS_DATA_END);
INFO(" BSS region : %p - %p\n",
(void *)CACTUS_BSS_START, (void *)CACTUS_BSS_END);
INFO(" RX : %p - %p\n",
(void *)get_sp_rx_start(vm_id),
(void *)get_sp_rx_end(vm_id));
INFO(" TX : %p - %p\n",
(void *)get_sp_tx_start(vm_id),
(void *)get_sp_tx_end(vm_id));
}
static void cactus_print_hob_list(uint64_t hob_content_addr, size_t size)
{
INFO("SP Hob List Contents: 0x%llx, size 0x%lx\n", hob_content_addr, size);
struct efi_hob_handoff_info_table *hob_list = (struct
efi_hob_handoff_info_table *) hob_content_addr;
dump_hob_list(hob_list);
}
static void cactus_print_boot_info(struct ffa_boot_info_header *boot_info_header)
{
struct ffa_boot_info_desc *boot_info_desc;
if (boot_info_header == NULL) {
NOTICE("SP doesn't have boot information!\n");
return;
}
VERBOSE("SP boot info:\n");
VERBOSE(" Signature: %x\n", boot_info_header->signature);
VERBOSE(" Version: %x\n", boot_info_header->version);
VERBOSE(" Blob Size: %u\n", boot_info_header->info_blob_size);
VERBOSE(" Descriptor Size: %u\n", boot_info_header->desc_size);
VERBOSE(" Descriptor Count: %u\n", boot_info_header->desc_count);
boot_info_desc = boot_info_header->boot_info;
if (boot_info_desc == NULL) {
ERROR("Boot data arguments error...\n");
return;
}
for (uint32_t i = 0; i < boot_info_header->desc_count; i++) {
VERBOSE(" Boot Data:\n");
VERBOSE(" Type: %u\n",
ffa_boot_info_type(&boot_info_desc[i]));
VERBOSE(" Type ID: %u\n",
ffa_boot_info_type_id(&boot_info_desc[i]));
VERBOSE(" Flags:\n");
VERBOSE(" Name Format: %x\n",
ffa_boot_info_name_format(&boot_info_desc[i]));
VERBOSE(" Content Format: %x\n",
ffa_boot_info_content_format(&boot_info_desc[i]));
VERBOSE(" Size: %u\n", boot_info_desc[i].size);
VERBOSE(" Value: %llx\n", boot_info_desc[i].content);
if (ffa_boot_info_type_id(&boot_info_desc[i]) == 1) {
cactus_print_hob_list(boot_info_desc[i].content, boot_info_desc[i].size);
}
}
}
static void cactus_plat_configure_mmu(unsigned int vm_id)
{
mmap_add_region(CACTUS_TEXT_START,
CACTUS_TEXT_START,
CACTUS_TEXT_END - CACTUS_TEXT_START,
MT_CODE);
mmap_add_region(CACTUS_RODATA_START,
CACTUS_RODATA_START,
CACTUS_RODATA_END - CACTUS_RODATA_START,
MT_RO_DATA);
mmap_add_region(CACTUS_DATA_START,
CACTUS_DATA_START,
CACTUS_DATA_END - CACTUS_DATA_START,
MT_RW_DATA);
mmap_add_region(CACTUS_BSS_START,
CACTUS_BSS_START,
CACTUS_BSS_END - CACTUS_BSS_START,
MT_RW_DATA);
mmap_add_region(get_sp_rx_start(vm_id),
get_sp_rx_start(vm_id),
(SP_RX_TX_SIZE / 2),
MT_RO_DATA);
mmap_add_region(get_sp_tx_start(vm_id),
get_sp_tx_start(vm_id),
(SP_RX_TX_SIZE / 2),
MT_RW_DATA);
mmap_add(cactus_mmap);
init_xlat_tables();
}
static struct ffa_value register_secondary_entrypoint(void)
{
struct ffa_value args;
args.fid = FFA_SECONDARY_EP_REGISTER_SMC64;
args.arg1 = (u_register_t)&secondary_cold_entry;
return ffa_service_call(&args);
}
static void cactus_map_boot_info(struct ffa_boot_info_header *boot_info_header,
bool do_desc_access)
{
struct ffa_boot_info_desc *boot_info_desc;
assert(boot_info_header != NULL);
boot_info_desc = boot_info_header->boot_info;
/*
* TODO: Currently just validating that cactus can
* access the boot info descriptors. By default, allocate one page
* for boot info. In case we want to use the boot info contents, we should check the
* blob and remap if the size is bigger than one page.
* Only then access the contents.
*/
mmap_add_dynamic_region(
(unsigned long long)boot_info_header,
(uintptr_t)boot_info_header,
PAGE_SIZE, MT_RO_DATA);
if (do_desc_access) {
for (uint32_t i = 0; i < boot_info_header->desc_count; i++) {
mmap_add_dynamic_region(
(unsigned long long)boot_info_desc[i].content,
(uintptr_t)boot_info_desc[i].content,
PAGE_SIZE, MT_RO_DATA);
}
}
}
void __dead2 cactus_main(bool primary_cold_boot,
struct ffa_boot_info_header *boot_info_header)
{
assert(IS_IN_EL1() != 0);
struct mailbox_buffers mb;
struct ffa_value ret;
/* Get current FFA id */
struct ffa_value ffa_id_ret = ffa_id_get();
ffa_id_t ffa_id = ffa_endpoint_id(ffa_id_ret);
if (ffa_func_id(ffa_id_ret) != FFA_SUCCESS_SMC32) {
ERROR("FFA_ID_GET failed.\n");
panic();
}
if (primary_cold_boot == true) {
/* Clear BSS */
memset((void *)CACTUS_BSS_START,
0, CACTUS_BSS_END - CACTUS_BSS_START);
mb.send = (void *) get_sp_tx_start(ffa_id);
mb.recv = (void *) get_sp_rx_start(ffa_id);
/* Configure and enable Stage-1 MMU, enable D-Cache */
cactus_plat_configure_mmu(ffa_id);
/* Initialize locks for tail end interrupt handler */
sp_handler_spin_lock_init();
if (boot_info_header != NULL) {
cactus_map_boot_info(boot_info_header, ffa_id == SP_ID(3));
}
}
/*
* The local ffa_id value is held on the stack. The global g_ffa_id
* value is set after BSS is cleared.
*/
g_ffa_id = ffa_id;
enable_mmu_el1(0);
/* Enable IRQ/FIQ */
enable_irq();
enable_fiq();
if (primary_cold_boot == false) {
goto msg_loop;
}
set_putc_impl(FFA_SVC_SMC_CALL_AS_STDOUT);
/* Below string is monitored by CI expect script. */
NOTICE("Booting Secure Partition (ID: %x)\n%s\n%s\n",
ffa_id, build_message, version_string);
/*
* Print FF-A boot info if requested in manifest via FF-A boot info
* protocol.
*/
if (ffa_id == SP_ID(1) || ffa_id == SP_ID(3)) {
cactus_print_boot_info(boot_info_header);
}
cactus_print_memory_layout(ffa_id);
/*
* Cactus-tertiary makes use of FFA_RXTX_MAP API instead of specifying
* `rx_tx-info` in its manifest, as done by the primary and secondary
* cactus partitions.
*/
if (ffa_id == SP_ID(3)) {
VERBOSE("Mapping RXTX Region\n");
CONFIGURE_AND_MAP_MAILBOX(mb, PAGE_SIZE, ret);
if (ffa_func_id(ret) != FFA_SUCCESS_SMC32) {
ERROR("Failed to map RXTX buffers. Error: %s\n",
ffa_error_name(ffa_error_code(ret)));
panic();
}
}
ret = register_secondary_entrypoint();
/* FFA_SECONDARY_EP_REGISTER interface is not supported for UP SP. */
if (ffa_id == SP_ID(3)) {
EXPECT(ffa_func_id(ret), FFA_ERROR);
EXPECT(ffa_error_code(ret), FFA_ERROR_NOT_SUPPORTED);
} else {
EXPECT(ffa_func_id(ret), FFA_SUCCESS_SMC32);
}
#if !SPMC_AT_EL3
discover_managed_exit_interrupt_id();
register_maintenance_interrupt_handlers();
/* Invoking self tests */
ffa_tests(&mb, true);
cpu_feature_tests();
#endif
msg_loop:
/* End up to message loop */
message_loop(ffa_id, &mb);
/* Not reached */
}