/* * Copyright (c) 2017-2020, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include #include /* * Structures describing coreboot's in-memory descriptor tables. See * /src/commonlib/include/commonlib/coreboot_tables.h for * canonical implementation. */ typedef struct { char signature[4]; uint32_t header_bytes; uint32_t header_checksum; uint32_t table_bytes; uint32_t table_checksum; uint32_t table_entries; } cb_header_t; typedef enum { CB_TAG_MEMORY = 0x1, CB_TAG_SERIAL = 0xf, CB_TAG_CBMEM_CONSOLE = 0x17, } cb_tag_t; typedef struct { uint32_t tag; uint32_t size; union { coreboot_memrange_t memranges[COREBOOT_MAX_MEMRANGES]; coreboot_serial_t serial; uint64_t uint64; }; } cb_entry_t; coreboot_memrange_t coreboot_memranges[COREBOOT_MAX_MEMRANGES]; coreboot_serial_t coreboot_serial; uint64_t coreboot_table_addr; uint32_t coreboot_table_size; /* * The coreboot table is parsed before the MMU is enabled (i.e. with strongly * ordered memory), so we cannot make unaligned accesses. The table entries * immediately follow one another without padding, so nothing after the header * is guaranteed to be naturally aligned. Therefore, we need to define safety * functions that can read unaligned integers. */ static uint32_t read_le32(uint32_t *p) { uintptr_t addr = (uintptr_t)p; return mmio_read_8(addr) | mmio_read_8(addr + 1) << 8 | mmio_read_8(addr + 2) << 16 | mmio_read_8(addr + 3) << 24; } static uint64_t read_le64(uint64_t *p) { return read_le32((void *)p) | (uint64_t)read_le32((void *)p + 4) << 32; } static void expand_and_mmap(uintptr_t baseaddr, size_t size) { uintptr_t pageaddr = round_down(baseaddr, PAGE_SIZE); size_t expanded = round_up(baseaddr - pageaddr + size, PAGE_SIZE); mmap_add_region(pageaddr, pageaddr, expanded, MT_MEMORY | MT_RW | MT_NS | MT_EXECUTE_NEVER); } static void setup_cbmem_console(uintptr_t baseaddr) { static console_cbmc_t console; assert(!console.console.base); /* should only have one CBMEM console */ /* CBMEM console structure stores its size in first header field. */ uint32_t size = *(uint32_t *)baseaddr; expand_and_mmap(baseaddr, size); console_cbmc_register(baseaddr, &console); console_set_scope(&console.console, CONSOLE_FLAG_BOOT | CONSOLE_FLAG_RUNTIME | CONSOLE_FLAG_CRASH); } coreboot_memory_t coreboot_get_memory_type(uintptr_t start, size_t size) { int i; for (i = 0; i < COREBOOT_MAX_MEMRANGES; i++) { coreboot_memrange_t *range = &coreboot_memranges[i]; if (range->type == CB_MEM_NONE) break; /* end of table reached */ if ((start >= range->start) && (start - range->start < range->size) && (size <= range->size - (start - range->start))) { return range->type; } } return CB_MEM_NONE; } void coreboot_get_table_location(uint64_t *address, uint32_t *size) { *address = coreboot_table_addr; *size = coreboot_table_size; } void coreboot_table_setup(void *base) { cb_header_t *header = base; void *ptr; int i; if (strncmp(header->signature, "LBIO", 4)) { ERROR("coreboot table signature corrupt!\n"); return; } coreboot_table_addr = (uint64_t) base; coreboot_table_size = header->header_bytes + header->table_bytes; ptr = base + header->header_bytes; for (i = 0; i < header->table_entries; i++) { size_t size; cb_entry_t *entry = ptr; if (ptr - base >= header->header_bytes + header->table_bytes) { ERROR("coreboot table exceeds its bounds!\n"); break; } switch (read_le32(&entry->tag)) { case CB_TAG_MEMORY: size = read_le32(&entry->size) - offsetof(cb_entry_t, memranges); if (size > sizeof(coreboot_memranges)) { ERROR("Need to truncate coreboot memranges!\n"); size = sizeof(coreboot_memranges); } memcpy(&coreboot_memranges, &entry->memranges, size); break; case CB_TAG_SERIAL: memcpy(&coreboot_serial, &entry->serial, sizeof(coreboot_serial)); break; case CB_TAG_CBMEM_CONSOLE: setup_cbmem_console(read_le64(&entry->uint64)); break; default: /* There are many tags TF doesn't need to care about. */ break; } ptr += read_le32(&entry->size); } }