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