coreboot: Add support for CBMEM console

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>
This commit is contained in:
Julius Werner 2017-06-13 15:53:45 -07:00
parent 890abc33e4
commit 1c5f5031f3
4 changed files with 166 additions and 0 deletions

View File

@ -0,0 +1,101 @@
/*
* Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <asm_macros.S>
#include <cbmem_console.h>
#include <console_macros.S>
/*
* This driver implements access to coreboot's in-memory console
* (CBMEM console). For the original implementation, see
* <coreboot>/src/lib/cbmem_console.c.
*/
.globl console_cbmc_register
.globl console_cbmc_putc
.globl console_cbmc_flush
/* -----------------------------------------------
* int console_cbmc_register(console_cbmc_t *console,
* uintptr_t base)
* Registers a new CBMEM console instance. Reads
* the size field from the buffer header structure
* and stores it in our console_cbmc_t struct, so
* that we keep the size in secure memory where we
* can trust it. A malicious EL1 could manipulate
* the console buffer (including the header), so we
* must not trust its contents after boot.
* In: x0 - CBMEM console base address
* x1 - pointer to empty console_cbmc_t struct
* Out: x0 - 1 to indicate success
* Clobber list: x0, x1, x2, x7
* -----------------------------------------------
*/
func console_cbmc_register
str x0, [x1, #CONSOLE_T_CBMC_BASE]
ldr w2, [x0]
str w2, [x1, #CONSOLE_T_CBMC_SIZE]
mov x0, x1
finish_console_register cbmc
endfunc console_cbmc_register
/* -----------------------------------------------
* int console_cbmc_puts(int c, console_cbmc_t *console)
* Writes a character to the CBMEM console buffer,
* including overflow handling of the cursor field.
* The character must be preserved in x0.
* In: x0 - character to be stored
* x1 - pointer to console_cbmc_t struct
* Clobber list: x1, x2, x16, x17
* -----------------------------------------------
*/
func console_cbmc_putc
ldr w2, [x1, #CONSOLE_T_CBMC_SIZE]
ldr x1, [x1, #CONSOLE_T_CBMC_BASE]
add x1, x1, #8 /* keep address of body in x1 */
ldr w16, [x1, #-4] /* load cursor (one u32 before body) */
and w17, w16, #0xf0000000 /* keep flags part in w17 */
and w16, w16, #0x0fffffff /* keep actual cursor part in w16 */
cmp w16, w2 /* sanity check that cursor < size */
b.lo putc_within_bounds
mov w0, #-1 /* cursor >= size must be malicious */
ret /* so return error, don't write char */
putc_within_bounds:
strb w0, [x1, w16, uxtw] /* body[cursor] = character */
add w16, w16, #1 /* cursor++ */
cmp w16, w2 /* if cursor < size... */
b.lo putc_write_back /* ...skip overflow handling */
mov w16, #0 /* on overflow, set cursor back to 0 */
orr w17, w17, #(1 << 31) /* and set overflow flag */
putc_write_back:
orr w16, w16, w17 /* merge cursor and flags back */
str w16, [x1, #-4] /* write back cursor to memory */
ret
endfunc console_cbmc_putc
/* -----------------------------------------------
* int console_cbmc_flush(console_cbmc_t *console)
* Flushes the CBMEM console by flushing the
* console buffer from the CPU's data cache.
* In: x0 - pointer to console_cbmc_t struct
* Out: x0 - 0 for success
* Clobber list: x0, x1, x2, x3, x5
* -----------------------------------------------
*/
func console_cbmc_flush
mov x5, x30
ldr x1, [x0, #CONSOLE_T_CBMC_SIZE]
ldr x0, [x0, #CONSOLE_T_CBMC_BASE]
add x1, x1, #8 /* add size of console header */
bl clean_dcache_range /* (clobbers x2 and x3) */
mov x0, #0
ret x5
endfunc console_cbmc_flush

View File

@ -0,0 +1,27 @@
/*
* Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef __CBMEM_CONSOLE_H__
#define __CBMEM_CONSOLE_H__
#include <console.h>
#define CONSOLE_T_CBMC_BASE CONSOLE_T_DRVDATA
#define CONSOLE_T_CBMC_SIZE (CONSOLE_T_DRVDATA + REGSZ)
#ifndef __ASSEMBLER__
typedef struct {
console_t console;
uintptr_t base;
uint32_t size;
} console_cbmc_t;
int console_cbmc_register(uintptr_t base, console_cbmc_t *console);
#endif /* __ASSEMBLER__ */
#endif /* __CBMEM_CONSOLE_H__ */

View File

@ -17,4 +17,8 @@ endif
BL31_SOURCES += $(addprefix lib/coreboot/, \
coreboot_table.c)
BL31_SOURCES += drivers/coreboot/cbmem_console/${ARCH}/cbmem_console.S
INCLUDES += -Iinclude/drivers/coreboot
endif # COREBOOT

View File

@ -4,10 +4,13 @@
* 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
@ -26,6 +29,7 @@ typedef struct {
typedef enum {
CB_TAG_SERIAL = 0xf,
CB_TAG_CBMEM_CONSOLE = 0x17,
} cb_tag_t;
typedef struct {
@ -33,6 +37,7 @@ typedef struct {
uint32_t size;
union {
coreboot_serial_t serial;
uint64_t uint64;
};
} cb_entry_t;
@ -53,6 +58,32 @@ static uint32_t read_le32(uint32_t *p)
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)
{
@ -79,6 +110,9 @@ void coreboot_table_setup(void *base)
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;