xref: /optee_os/core/drivers/cbmem_console.c (revision 426790bd3afd47b180cab482e904d9d79f3ca221)
1*426790bdSJeffrey Kardatzke // SPDX-License-Identifier: BSD-2-Clause
2*426790bdSJeffrey Kardatzke /*
3*426790bdSJeffrey Kardatzke  * Copyright (c) 2023, Google Limited
4*426790bdSJeffrey Kardatzke  */
5*426790bdSJeffrey Kardatzke 
6*426790bdSJeffrey Kardatzke #include <compiler.h>
7*426790bdSJeffrey Kardatzke #include <console.h>
8*426790bdSJeffrey Kardatzke #include <drivers/cbmem_console.h>
9*426790bdSJeffrey Kardatzke #include <drivers/serial.h>
10*426790bdSJeffrey Kardatzke #include <io.h>
11*426790bdSJeffrey Kardatzke #include <keep.h>
12*426790bdSJeffrey Kardatzke #include <kernel/dt.h>
13*426790bdSJeffrey Kardatzke #include <libfdt.h>
14*426790bdSJeffrey Kardatzke #include <mm/core_mmu.h>
15*426790bdSJeffrey Kardatzke #include <types_ext.h>
16*426790bdSJeffrey Kardatzke #include <util.h>
17*426790bdSJeffrey Kardatzke 
18*426790bdSJeffrey Kardatzke #define CURSOR_MASK (BIT(28) - 1)
19*426790bdSJeffrey Kardatzke #define OVERFLOW BIT(31)
20*426790bdSJeffrey Kardatzke 
21*426790bdSJeffrey Kardatzke struct cbmem_console {
22*426790bdSJeffrey Kardatzke 	uint32_t size;
23*426790bdSJeffrey Kardatzke 	uint32_t cursor;
24*426790bdSJeffrey Kardatzke 	uint8_t body[0];
25*426790bdSJeffrey Kardatzke };
26*426790bdSJeffrey Kardatzke 
27*426790bdSJeffrey Kardatzke struct cbmem_console_data {
28*426790bdSJeffrey Kardatzke 	paddr_t base;
29*426790bdSJeffrey Kardatzke 	struct cbmem_console *console;
30*426790bdSJeffrey Kardatzke 	struct serial_chip chip;
31*426790bdSJeffrey Kardatzke 	uint32_t size;
32*426790bdSJeffrey Kardatzke };
33*426790bdSJeffrey Kardatzke 
34*426790bdSJeffrey Kardatzke /*
35*426790bdSJeffrey Kardatzke  * Structures describing coreboot's in-memory descriptor tables. See
36*426790bdSJeffrey Kardatzke  * https://github.com/coreboot/coreboot/blob/ea2a38be323173075db3b13729a4006ea1fef72d/src/commonlib/include/commonlib/coreboot_tables.h
37*426790bdSJeffrey Kardatzke  * for canonical implementation.
38*426790bdSJeffrey Kardatzke  */
39*426790bdSJeffrey Kardatzke 
40*426790bdSJeffrey Kardatzke struct cb_header {
41*426790bdSJeffrey Kardatzke 	uint8_t signature[4];
42*426790bdSJeffrey Kardatzke 	uint32_t header_bytes;
43*426790bdSJeffrey Kardatzke 	uint32_t header_checksum;
44*426790bdSJeffrey Kardatzke 	uint32_t table_bytes;
45*426790bdSJeffrey Kardatzke 	uint32_t table_checksum;
46*426790bdSJeffrey Kardatzke 	uint32_t table_entries;
47*426790bdSJeffrey Kardatzke };
48*426790bdSJeffrey Kardatzke 
49*426790bdSJeffrey Kardatzke #define CB_TAG_CBMEM_CONSOLE 0x17
50*426790bdSJeffrey Kardatzke 
51*426790bdSJeffrey Kardatzke struct cb_entry {
52*426790bdSJeffrey Kardatzke 	uint32_t tag;
53*426790bdSJeffrey Kardatzke 	uint32_t size;
54*426790bdSJeffrey Kardatzke 	uint64_t value;
55*426790bdSJeffrey Kardatzke };
56*426790bdSJeffrey Kardatzke 
57*426790bdSJeffrey Kardatzke static struct cbmem_console_data cbmem_console;
58*426790bdSJeffrey Kardatzke 
59*426790bdSJeffrey Kardatzke static void cbmem_console_flush(struct serial_chip *chip __unused)
60*426790bdSJeffrey Kardatzke {
61*426790bdSJeffrey Kardatzke }
62*426790bdSJeffrey Kardatzke 
63*426790bdSJeffrey Kardatzke static int cbmem_console_getchar(struct serial_chip *chip __unused)
64*426790bdSJeffrey Kardatzke {
65*426790bdSJeffrey Kardatzke 	return 0;
66*426790bdSJeffrey Kardatzke }
67*426790bdSJeffrey Kardatzke 
68*426790bdSJeffrey Kardatzke static bool cbmem_console_have_rx_data(struct serial_chip *chip __unused)
69*426790bdSJeffrey Kardatzke {
70*426790bdSJeffrey Kardatzke 	return false;
71*426790bdSJeffrey Kardatzke }
72*426790bdSJeffrey Kardatzke 
73*426790bdSJeffrey Kardatzke static void cbmem_console_putc(struct serial_chip *chip, int ch)
74*426790bdSJeffrey Kardatzke {
75*426790bdSJeffrey Kardatzke 	struct cbmem_console_data *pd =
76*426790bdSJeffrey Kardatzke 		container_of(chip, struct cbmem_console_data, chip);
77*426790bdSJeffrey Kardatzke 	struct cbmem_console *c = pd->console;
78*426790bdSJeffrey Kardatzke 
79*426790bdSJeffrey Kardatzke 	if (!pd->size)
80*426790bdSJeffrey Kardatzke 		return;
81*426790bdSJeffrey Kardatzke 
82*426790bdSJeffrey Kardatzke 	if ((c->cursor & CURSOR_MASK) + 1 >= pd->size) {
83*426790bdSJeffrey Kardatzke 		c->cursor &= ~CURSOR_MASK;
84*426790bdSJeffrey Kardatzke 		c->cursor |= OVERFLOW;
85*426790bdSJeffrey Kardatzke 		c->body[0] = (uint8_t)(ch & 0xFF);
86*426790bdSJeffrey Kardatzke 	} else {
87*426790bdSJeffrey Kardatzke 		c->body[c->cursor & CURSOR_MASK] = (uint8_t)(ch & 0xFF);
88*426790bdSJeffrey Kardatzke 		c->cursor++;
89*426790bdSJeffrey Kardatzke 	}
90*426790bdSJeffrey Kardatzke }
91*426790bdSJeffrey Kardatzke 
92*426790bdSJeffrey Kardatzke static const struct serial_ops cbmem_console_ops = {
93*426790bdSJeffrey Kardatzke 	.flush = cbmem_console_flush,
94*426790bdSJeffrey Kardatzke 	.getchar = cbmem_console_getchar,
95*426790bdSJeffrey Kardatzke 	.have_rx_data = cbmem_console_have_rx_data,
96*426790bdSJeffrey Kardatzke 	.putc = cbmem_console_putc,
97*426790bdSJeffrey Kardatzke };
98*426790bdSJeffrey Kardatzke DECLARE_KEEP_PAGER(cbmem_console_ops);
99*426790bdSJeffrey Kardatzke 
100*426790bdSJeffrey Kardatzke static paddr_t get_cbmem_console_from_coreboot_table(paddr_t table_addr,
101*426790bdSJeffrey Kardatzke 						     size_t table_size)
102*426790bdSJeffrey Kardatzke {
103*426790bdSJeffrey Kardatzke 	struct cb_header *header = NULL;
104*426790bdSJeffrey Kardatzke 	void *ptr = NULL;
105*426790bdSJeffrey Kardatzke 	uint32_t i = 0;
106*426790bdSJeffrey Kardatzke 	struct cb_entry *entry = NULL;
107*426790bdSJeffrey Kardatzke 	paddr_t cbmem_console_base = 0;
108*426790bdSJeffrey Kardatzke 	void *base = NULL;
109*426790bdSJeffrey Kardatzke 
110*426790bdSJeffrey Kardatzke 	base = core_mmu_add_mapping(MEM_AREA_RAM_NSEC, table_addr, table_size);
111*426790bdSJeffrey Kardatzke 	if (!base)
112*426790bdSJeffrey Kardatzke 		return 0;
113*426790bdSJeffrey Kardatzke 
114*426790bdSJeffrey Kardatzke 	header = (struct cb_header *)base;
115*426790bdSJeffrey Kardatzke 	if (memcmp(header->signature, "LBIO", 4))
116*426790bdSJeffrey Kardatzke 		goto done;
117*426790bdSJeffrey Kardatzke 
118*426790bdSJeffrey Kardatzke 	if (header->header_bytes + header->table_bytes > table_size)
119*426790bdSJeffrey Kardatzke 		goto done;
120*426790bdSJeffrey Kardatzke 
121*426790bdSJeffrey Kardatzke 	ptr = (uint8_t *)base + header->header_bytes;
122*426790bdSJeffrey Kardatzke 	for (i = 0; i < header->table_entries; ++i) {
123*426790bdSJeffrey Kardatzke 		entry = (struct cb_entry *)ptr;
124*426790bdSJeffrey Kardatzke 		if ((uint8_t *)ptr >= (uint8_t *)base + table_size -
125*426790bdSJeffrey Kardatzke 				sizeof(struct cb_entry)) {
126*426790bdSJeffrey Kardatzke 			goto done;
127*426790bdSJeffrey Kardatzke 		}
128*426790bdSJeffrey Kardatzke 
129*426790bdSJeffrey Kardatzke 		switch (get_le32(&entry->tag)) {
130*426790bdSJeffrey Kardatzke 		case CB_TAG_CBMEM_CONSOLE:
131*426790bdSJeffrey Kardatzke 			cbmem_console_base = get_le64(&entry->value);
132*426790bdSJeffrey Kardatzke 			goto done;
133*426790bdSJeffrey Kardatzke 		default:
134*426790bdSJeffrey Kardatzke 			/* We skip all but one tag type. */
135*426790bdSJeffrey Kardatzke 			break;
136*426790bdSJeffrey Kardatzke 		}
137*426790bdSJeffrey Kardatzke 
138*426790bdSJeffrey Kardatzke 		ptr = (uint8_t *)ptr + get_le32(&entry->size);
139*426790bdSJeffrey Kardatzke 	}
140*426790bdSJeffrey Kardatzke 
141*426790bdSJeffrey Kardatzke done:
142*426790bdSJeffrey Kardatzke 	core_mmu_remove_mapping(MEM_AREA_RAM_NSEC, base, table_size);
143*426790bdSJeffrey Kardatzke 	return cbmem_console_base;
144*426790bdSJeffrey Kardatzke }
145*426790bdSJeffrey Kardatzke 
146*426790bdSJeffrey Kardatzke bool cbmem_console_init_from_dt(void *fdt)
147*426790bdSJeffrey Kardatzke {
148*426790bdSJeffrey Kardatzke 	int offset = 0;
149*426790bdSJeffrey Kardatzke 	paddr_t cb_addr = 0;
150*426790bdSJeffrey Kardatzke 	size_t cb_size = 0;
151*426790bdSJeffrey Kardatzke 	paddr_t cbmem_console_base = 0;
152*426790bdSJeffrey Kardatzke 
153*426790bdSJeffrey Kardatzke 	if (!fdt)
154*426790bdSJeffrey Kardatzke 		return false;
155*426790bdSJeffrey Kardatzke 
156*426790bdSJeffrey Kardatzke 	offset = fdt_path_offset(fdt, "/firmware/coreboot");
157*426790bdSJeffrey Kardatzke 	if (offset < 0)
158*426790bdSJeffrey Kardatzke 		return false;
159*426790bdSJeffrey Kardatzke 
160*426790bdSJeffrey Kardatzke 	cb_addr = fdt_reg_base_address(fdt, offset);
161*426790bdSJeffrey Kardatzke 	cb_size = fdt_reg_size(fdt, offset);
162*426790bdSJeffrey Kardatzke 
163*426790bdSJeffrey Kardatzke 	cbmem_console_base = get_cbmem_console_from_coreboot_table(cb_addr,
164*426790bdSJeffrey Kardatzke 								   cb_size);
165*426790bdSJeffrey Kardatzke 	if (!cbmem_console_base)
166*426790bdSJeffrey Kardatzke 		return false;
167*426790bdSJeffrey Kardatzke 
168*426790bdSJeffrey Kardatzke 	cbmem_console.base = cbmem_console_base;
169*426790bdSJeffrey Kardatzke 	cbmem_console.console = (struct cbmem_console *)
170*426790bdSJeffrey Kardatzke 		core_mmu_add_mapping(MEM_AREA_RAM_NSEC, cbmem_console_base,
171*426790bdSJeffrey Kardatzke 				     sizeof(struct cbmem_console));
172*426790bdSJeffrey Kardatzke 	if (!cbmem_console.console)
173*426790bdSJeffrey Kardatzke 		return false;
174*426790bdSJeffrey Kardatzke 
175*426790bdSJeffrey Kardatzke 	/*
176*426790bdSJeffrey Kardatzke 	 * Copy the size now to prevent non-secure world from spoofing
177*426790bdSJeffrey Kardatzke 	 * it later.
178*426790bdSJeffrey Kardatzke 	 */
179*426790bdSJeffrey Kardatzke 	cbmem_console.size = cbmem_console.console->size;
180*426790bdSJeffrey Kardatzke 	cbmem_console.chip.ops = &cbmem_console_ops;
181*426790bdSJeffrey Kardatzke 
182*426790bdSJeffrey Kardatzke 	register_serial_console(&cbmem_console.chip);
183*426790bdSJeffrey Kardatzke 	return true;
184*426790bdSJeffrey Kardatzke }
185