mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-16 17:44:19 +00:00

coreboot supports an in-memory console to store firmware logs even when no serial console is available. It is widely supported by coreboot-compatible bootloaders (including SeaBIOS and GRUB) and can be read by the Linux kernel. This patch allows BL31 to add its own log messages to this console. The driver will be registered automatically if coreboot support is compiled in and detects the presence of a console buffer in the coreboot tables. Change-Id: I31254dfa0c2fdeb7454634134b5707b4b4154907 Signed-off-by: Julius Werner <jwerner@chromium.org>
123 lines
3 KiB
C
123 lines
3 KiB
C
/*
|
|
* Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <cbmem_console.h>
|
|
#include <coreboot.h>
|
|
#include <debug.h>
|
|
#include <mmio.h>
|
|
#include <string.h>
|
|
#include <xlat_tables_v2.h>
|
|
|
|
/*
|
|
* Structures describing coreboot's in-memory descriptor tables. See
|
|
* <coreboot>/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_SERIAL = 0xf,
|
|
CB_TAG_CBMEM_CONSOLE = 0x17,
|
|
} cb_tag_t;
|
|
|
|
typedef struct {
|
|
uint32_t tag;
|
|
uint32_t size;
|
|
union {
|
|
coreboot_serial_t serial;
|
|
uint64_t uint64;
|
|
};
|
|
} cb_entry_t;
|
|
|
|
coreboot_serial_t coreboot_serial;
|
|
|
|
/*
|
|
* 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.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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
ptr = base + header->header_bytes;
|
|
for (i = 0; i < header->table_entries; i++) {
|
|
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_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);
|
|
}
|
|
}
|