/* * This file is part of the coreboot project. * * Copyright (C) 2014 Google Inc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; version 2 of * the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301 USA * * secmon_loader.c: Responsible for loading the rmodule, providing entry point * and parameter location for the rmodule. */ #include #include #include #include #include #include #include /* SECMON entry point encoded as an rmodule */ extern unsigned char _binary_secmon_start[]; typedef void (*secmon_entry_t)(struct secmon_params *); void __attribute__((weak)) soc_get_secmon_base_size(uint64_t *secmon_base, size_t *secmon_size) { /* Default weak implementation initializes to 0 */ *secmon_base = 0; *secmon_size = 0; } static secmon_entry_t secmon_load_rmodule(void) { struct rmodule secmon_mod; uint64_t secmon_base; size_t secmon_size; /* Get base address and size of the area available for secure monitor * rmodule. */ soc_get_secmon_base_size(&secmon_base, &secmon_size); if ((secmon_base == 0) || (secmon_size == 0)) { printk(BIOS_ERR, "ARM64: secmon_base / secmon_size invalid\n"); return NULL; } printk(BIOS_DEBUG,"secmon_base:%lx,secmon_size:%lx\n", (unsigned long)secmon_base, (unsigned long)secmon_size); /* Fail if can't parse secmon module */ if (rmodule_parse(&_binary_secmon_start, &secmon_mod)) { printk(BIOS_ERR, "ARM64: secmon_mod not found\n"); return NULL; } /* Load rmodule at secmon_base */ if (rmodule_load((void *)secmon_base, &secmon_mod)) { printk(BIOS_ERR, "ARM64:secmon_mod cannot load\n"); return NULL; } /* Identify the entry point for secure monitor */ return rmodule_entry(&secmon_mod); } struct secmon_runit { secmon_entry_t entry; struct secmon_params bsp_params; struct secmon_params secondary_params; }; static void secmon_start(void *arg) { uint32_t scr; secmon_entry_t entry; struct secmon_params *p = NULL; struct secmon_runit *r = arg; entry = r->entry; if (cpu_is_bsp()) p = &r->bsp_params; else { entry = secondary_entry_point(entry); if (r->secondary_params.entry != NULL) p = &r->secondary_params; } printk(BIOS_DEBUG, "CPU%x entering secure monitor %p.\n", cpu_info()->id, entry); /* We want to enforce the following policies: * NS bit is set for lower EL */ scr = raw_read_scr_el3(); scr |= SCR_NS; raw_write_scr_el3(scr); entry(p); } void secmon_run(void (*entry)(void *), void *cb_tables) { const struct spintable_attributes *spin_attrs; static struct secmon_runit runit; struct cpu_action action = { .run = secmon_start, .arg = &runit, }; printk(BIOS_SPEW, "payload jump @ %p\n", entry); if (get_current_el() != EL3) { printk(BIOS_DEBUG, "Secmon Error: Can only be loaded in EL3\n"); return; } runit.entry = secmon_load_rmodule(); if (runit.entry == NULL) die("ARM64 Error: secmon load error"); printk(BIOS_DEBUG, "ARM64: Loaded the el3 monitor...jumping to %p\n", runit.entry); runit.bsp_params.entry = entry; runit.bsp_params.arg = cb_tables; spin_attrs = spintable_get_attributes(); if (spin_attrs != NULL) { runit.secondary_params.entry = spin_attrs->entry; runit.secondary_params.arg = spin_attrs->addr; } arch_run_on_all_cpus_but_self_async(&action); secmon_start(&runit); }