xref: /optee_os/core/drivers/cbmem_console.c (revision 6a0116edfb65dc6c4096e8ad2b94989699c7c7a6)
1426790bdSJeffrey Kardatzke // SPDX-License-Identifier: BSD-2-Clause
2426790bdSJeffrey Kardatzke /*
3426790bdSJeffrey Kardatzke  * Copyright (c) 2023, Google Limited
4426790bdSJeffrey Kardatzke  */
5426790bdSJeffrey Kardatzke 
6426790bdSJeffrey Kardatzke #include <compiler.h>
7426790bdSJeffrey Kardatzke #include <console.h>
8426790bdSJeffrey Kardatzke #include <drivers/cbmem_console.h>
9426790bdSJeffrey Kardatzke #include <drivers/serial.h>
10426790bdSJeffrey Kardatzke #include <io.h>
11426790bdSJeffrey Kardatzke #include <keep.h>
12426790bdSJeffrey Kardatzke #include <kernel/dt.h>
13426790bdSJeffrey Kardatzke #include <libfdt.h>
14426790bdSJeffrey Kardatzke #include <mm/core_mmu.h>
15426790bdSJeffrey Kardatzke #include <types_ext.h>
16426790bdSJeffrey Kardatzke #include <util.h>
17426790bdSJeffrey Kardatzke 
18426790bdSJeffrey Kardatzke #define CURSOR_MASK (BIT(28) - 1)
19426790bdSJeffrey Kardatzke #define OVERFLOW BIT(31)
20426790bdSJeffrey Kardatzke 
21426790bdSJeffrey Kardatzke struct cbmem_console {
22426790bdSJeffrey Kardatzke 	uint32_t size;
23426790bdSJeffrey Kardatzke 	uint32_t cursor;
24426790bdSJeffrey Kardatzke 	uint8_t body[0];
25426790bdSJeffrey Kardatzke };
26426790bdSJeffrey Kardatzke 
27426790bdSJeffrey Kardatzke struct cbmem_console_data {
28426790bdSJeffrey Kardatzke 	paddr_t base;
29426790bdSJeffrey Kardatzke 	struct cbmem_console *console;
30426790bdSJeffrey Kardatzke 	struct serial_chip chip;
31426790bdSJeffrey Kardatzke 	uint32_t size;
32426790bdSJeffrey Kardatzke };
33426790bdSJeffrey Kardatzke 
34426790bdSJeffrey Kardatzke /*
35426790bdSJeffrey Kardatzke  * Structures describing coreboot's in-memory descriptor tables. See
36426790bdSJeffrey Kardatzke  * https://github.com/coreboot/coreboot/blob/ea2a38be323173075db3b13729a4006ea1fef72d/src/commonlib/include/commonlib/coreboot_tables.h
37426790bdSJeffrey Kardatzke  * for canonical implementation.
38426790bdSJeffrey Kardatzke  */
39426790bdSJeffrey Kardatzke 
40426790bdSJeffrey Kardatzke struct cb_header {
41426790bdSJeffrey Kardatzke 	uint8_t signature[4];
42426790bdSJeffrey Kardatzke 	uint32_t header_bytes;
43426790bdSJeffrey Kardatzke 	uint32_t header_checksum;
44426790bdSJeffrey Kardatzke 	uint32_t table_bytes;
45426790bdSJeffrey Kardatzke 	uint32_t table_checksum;
46426790bdSJeffrey Kardatzke 	uint32_t table_entries;
47426790bdSJeffrey Kardatzke };
48426790bdSJeffrey Kardatzke 
49426790bdSJeffrey Kardatzke #define CB_TAG_CBMEM_CONSOLE 0x17
50426790bdSJeffrey Kardatzke 
51426790bdSJeffrey Kardatzke struct cb_entry {
52426790bdSJeffrey Kardatzke 	uint32_t tag;
53426790bdSJeffrey Kardatzke 	uint32_t size;
54426790bdSJeffrey Kardatzke 	uint64_t value;
55426790bdSJeffrey Kardatzke };
56426790bdSJeffrey Kardatzke 
57426790bdSJeffrey Kardatzke static struct cbmem_console_data cbmem_console;
58426790bdSJeffrey Kardatzke 
cbmem_console_flush(struct serial_chip * chip __unused)59426790bdSJeffrey Kardatzke static void cbmem_console_flush(struct serial_chip *chip __unused)
60426790bdSJeffrey Kardatzke {
61426790bdSJeffrey Kardatzke }
62426790bdSJeffrey Kardatzke 
cbmem_console_getchar(struct serial_chip * chip __unused)63426790bdSJeffrey Kardatzke static int cbmem_console_getchar(struct serial_chip *chip __unused)
64426790bdSJeffrey Kardatzke {
65426790bdSJeffrey Kardatzke 	return 0;
66426790bdSJeffrey Kardatzke }
67426790bdSJeffrey Kardatzke 
cbmem_console_have_rx_data(struct serial_chip * chip __unused)68426790bdSJeffrey Kardatzke static bool cbmem_console_have_rx_data(struct serial_chip *chip __unused)
69426790bdSJeffrey Kardatzke {
70426790bdSJeffrey Kardatzke 	return false;
71426790bdSJeffrey Kardatzke }
72426790bdSJeffrey Kardatzke 
cbmem_console_putc(struct serial_chip * chip,int ch)73426790bdSJeffrey Kardatzke static void cbmem_console_putc(struct serial_chip *chip, int ch)
74426790bdSJeffrey Kardatzke {
75426790bdSJeffrey Kardatzke 	struct cbmem_console_data *pd =
76426790bdSJeffrey Kardatzke 		container_of(chip, struct cbmem_console_data, chip);
77426790bdSJeffrey Kardatzke 	struct cbmem_console *c = pd->console;
78426790bdSJeffrey Kardatzke 
79426790bdSJeffrey Kardatzke 	if (!pd->size)
80426790bdSJeffrey Kardatzke 		return;
81426790bdSJeffrey Kardatzke 
82426790bdSJeffrey Kardatzke 	if ((c->cursor & CURSOR_MASK) + 1 >= pd->size) {
83426790bdSJeffrey Kardatzke 		c->cursor &= ~CURSOR_MASK;
84426790bdSJeffrey Kardatzke 		c->cursor |= OVERFLOW;
85426790bdSJeffrey Kardatzke 		c->body[0] = (uint8_t)(ch & 0xFF);
86426790bdSJeffrey Kardatzke 	} else {
87426790bdSJeffrey Kardatzke 		c->body[c->cursor & CURSOR_MASK] = (uint8_t)(ch & 0xFF);
88426790bdSJeffrey Kardatzke 		c->cursor++;
89426790bdSJeffrey Kardatzke 	}
90426790bdSJeffrey Kardatzke }
91426790bdSJeffrey Kardatzke 
92426790bdSJeffrey Kardatzke static const struct serial_ops cbmem_console_ops = {
93426790bdSJeffrey Kardatzke 	.flush = cbmem_console_flush,
94426790bdSJeffrey Kardatzke 	.getchar = cbmem_console_getchar,
95426790bdSJeffrey Kardatzke 	.have_rx_data = cbmem_console_have_rx_data,
96426790bdSJeffrey Kardatzke 	.putc = cbmem_console_putc,
97426790bdSJeffrey Kardatzke };
98426790bdSJeffrey Kardatzke DECLARE_KEEP_PAGER(cbmem_console_ops);
99426790bdSJeffrey Kardatzke 
get_cbmem_console_from_coreboot_table(paddr_t table_addr,size_t table_size)100426790bdSJeffrey Kardatzke static paddr_t get_cbmem_console_from_coreboot_table(paddr_t table_addr,
101426790bdSJeffrey Kardatzke 						     size_t table_size)
102426790bdSJeffrey Kardatzke {
103426790bdSJeffrey Kardatzke 	struct cb_header *header = NULL;
104426790bdSJeffrey Kardatzke 	void *ptr = NULL;
105426790bdSJeffrey Kardatzke 	uint32_t i = 0;
106426790bdSJeffrey Kardatzke 	struct cb_entry *entry = NULL;
107426790bdSJeffrey Kardatzke 	paddr_t cbmem_console_base = 0;
108426790bdSJeffrey Kardatzke 	void *base = NULL;
109426790bdSJeffrey Kardatzke 
110426790bdSJeffrey Kardatzke 	base = core_mmu_add_mapping(MEM_AREA_RAM_NSEC, table_addr, table_size);
111426790bdSJeffrey Kardatzke 	if (!base)
112426790bdSJeffrey Kardatzke 		return 0;
113426790bdSJeffrey Kardatzke 
114426790bdSJeffrey Kardatzke 	header = (struct cb_header *)base;
115426790bdSJeffrey Kardatzke 	if (memcmp(header->signature, "LBIO", 4))
116426790bdSJeffrey Kardatzke 		goto done;
117426790bdSJeffrey Kardatzke 
118426790bdSJeffrey Kardatzke 	if (header->header_bytes + header->table_bytes > table_size)
119426790bdSJeffrey Kardatzke 		goto done;
120426790bdSJeffrey Kardatzke 
121426790bdSJeffrey Kardatzke 	ptr = (uint8_t *)base + header->header_bytes;
122426790bdSJeffrey Kardatzke 	for (i = 0; i < header->table_entries; ++i) {
123426790bdSJeffrey Kardatzke 		entry = (struct cb_entry *)ptr;
124426790bdSJeffrey Kardatzke 		if ((uint8_t *)ptr >= (uint8_t *)base + table_size -
125426790bdSJeffrey Kardatzke 				sizeof(struct cb_entry)) {
126426790bdSJeffrey Kardatzke 			goto done;
127426790bdSJeffrey Kardatzke 		}
128426790bdSJeffrey Kardatzke 
129426790bdSJeffrey Kardatzke 		switch (get_le32(&entry->tag)) {
130426790bdSJeffrey Kardatzke 		case CB_TAG_CBMEM_CONSOLE:
131426790bdSJeffrey Kardatzke 			cbmem_console_base = get_le64(&entry->value);
132426790bdSJeffrey Kardatzke 			goto done;
133426790bdSJeffrey Kardatzke 		default:
134426790bdSJeffrey Kardatzke 			/* We skip all but one tag type. */
135426790bdSJeffrey Kardatzke 			break;
136426790bdSJeffrey Kardatzke 		}
137426790bdSJeffrey Kardatzke 
138426790bdSJeffrey Kardatzke 		ptr = (uint8_t *)ptr + get_le32(&entry->size);
139426790bdSJeffrey Kardatzke 	}
140426790bdSJeffrey Kardatzke 
141426790bdSJeffrey Kardatzke done:
142426790bdSJeffrey Kardatzke 	core_mmu_remove_mapping(MEM_AREA_RAM_NSEC, base, table_size);
143426790bdSJeffrey Kardatzke 	return cbmem_console_base;
144426790bdSJeffrey Kardatzke }
145426790bdSJeffrey Kardatzke 
cbmem_console_init_from_dt(void * fdt)146426790bdSJeffrey Kardatzke bool cbmem_console_init_from_dt(void *fdt)
147426790bdSJeffrey Kardatzke {
148426790bdSJeffrey Kardatzke 	int offset = 0;
149426790bdSJeffrey Kardatzke 	paddr_t cb_addr = 0;
150426790bdSJeffrey Kardatzke 	size_t cb_size = 0;
151426790bdSJeffrey Kardatzke 	paddr_t cbmem_console_base = 0;
152426790bdSJeffrey Kardatzke 
153426790bdSJeffrey Kardatzke 	if (!fdt)
154426790bdSJeffrey Kardatzke 		return false;
155426790bdSJeffrey Kardatzke 
156426790bdSJeffrey Kardatzke 	offset = fdt_path_offset(fdt, "/firmware/coreboot");
157426790bdSJeffrey Kardatzke 	if (offset < 0)
158426790bdSJeffrey Kardatzke 		return false;
159426790bdSJeffrey Kardatzke 
160*6a0116edSEtienne Carriere 	if (fdt_reg_info(fdt, offset, &cb_addr, &cb_size))
161*6a0116edSEtienne Carriere 		return false;
162426790bdSJeffrey Kardatzke 
163426790bdSJeffrey Kardatzke 	cbmem_console_base = get_cbmem_console_from_coreboot_table(cb_addr,
164426790bdSJeffrey Kardatzke 								   cb_size);
165426790bdSJeffrey Kardatzke 	if (!cbmem_console_base)
166426790bdSJeffrey Kardatzke 		return false;
167426790bdSJeffrey Kardatzke 
168426790bdSJeffrey Kardatzke 	cbmem_console.base = cbmem_console_base;
169426790bdSJeffrey Kardatzke 	cbmem_console.console = (struct cbmem_console *)
170426790bdSJeffrey Kardatzke 		core_mmu_add_mapping(MEM_AREA_RAM_NSEC, cbmem_console_base,
171426790bdSJeffrey Kardatzke 				     sizeof(struct cbmem_console));
172426790bdSJeffrey Kardatzke 	if (!cbmem_console.console)
173426790bdSJeffrey Kardatzke 		return false;
174426790bdSJeffrey Kardatzke 
175426790bdSJeffrey Kardatzke 	/*
176426790bdSJeffrey Kardatzke 	 * Copy the size now to prevent non-secure world from spoofing
177426790bdSJeffrey Kardatzke 	 * it later.
178426790bdSJeffrey Kardatzke 	 */
179426790bdSJeffrey Kardatzke 	cbmem_console.size = cbmem_console.console->size;
180426790bdSJeffrey Kardatzke 	cbmem_console.chip.ops = &cbmem_console_ops;
181426790bdSJeffrey Kardatzke 
182426790bdSJeffrey Kardatzke 	register_serial_console(&cbmem_console.chip);
183426790bdSJeffrey Kardatzke 	return true;
184426790bdSJeffrey Kardatzke }
185