File: cbmem_console.c

package info (click to toggle)
optee-os 4.8.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 31,960 kB
  • sloc: ansic: 444,388; asm: 12,922; python: 3,719; makefile: 1,681; sh: 238
file content (184 lines) | stat: -rw-r--r-- 4,159 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
// SPDX-License-Identifier: BSD-2-Clause
/*
 * Copyright (c) 2023, Google Limited
 */

#include <compiler.h>
#include <console.h>
#include <drivers/cbmem_console.h>
#include <drivers/serial.h>
#include <io.h>
#include <keep.h>
#include <kernel/dt.h>
#include <libfdt.h>
#include <mm/core_mmu.h>
#include <types_ext.h>
#include <util.h>

#define CURSOR_MASK (BIT(28) - 1)
#define OVERFLOW BIT(31)

struct cbmem_console {
	uint32_t size;
	uint32_t cursor;
	uint8_t body[0];
};

struct cbmem_console_data {
	paddr_t base;
	struct cbmem_console *console;
	struct serial_chip chip;
	uint32_t size;
};

/*
 * Structures describing coreboot's in-memory descriptor tables. See
 * https://github.com/coreboot/coreboot/blob/ea2a38be323173075db3b13729a4006ea1fef72d/src/commonlib/include/commonlib/coreboot_tables.h
 * for canonical implementation.
 */

struct cb_header {
	uint8_t signature[4];
	uint32_t header_bytes;
	uint32_t header_checksum;
	uint32_t table_bytes;
	uint32_t table_checksum;
	uint32_t table_entries;
};

#define CB_TAG_CBMEM_CONSOLE 0x17

struct cb_entry {
	uint32_t tag;
	uint32_t size;
	uint64_t value;
};

static struct cbmem_console_data cbmem_console;

static void cbmem_console_flush(struct serial_chip *chip __unused)
{
}

static int cbmem_console_getchar(struct serial_chip *chip __unused)
{
	return 0;
}

static bool cbmem_console_have_rx_data(struct serial_chip *chip __unused)
{
	return false;
}

static void cbmem_console_putc(struct serial_chip *chip, int ch)
{
	struct cbmem_console_data *pd =
		container_of(chip, struct cbmem_console_data, chip);
	struct cbmem_console *c = pd->console;

	if (!pd->size)
		return;

	if ((c->cursor & CURSOR_MASK) + 1 >= pd->size) {
		c->cursor &= ~CURSOR_MASK;
		c->cursor |= OVERFLOW;
		c->body[0] = (uint8_t)(ch & 0xFF);
	} else {
		c->body[c->cursor & CURSOR_MASK] = (uint8_t)(ch & 0xFF);
		c->cursor++;
	}
}

static const struct serial_ops cbmem_console_ops = {
	.flush = cbmem_console_flush,
	.getchar = cbmem_console_getchar,
	.have_rx_data = cbmem_console_have_rx_data,
	.putc = cbmem_console_putc,
};
DECLARE_KEEP_PAGER(cbmem_console_ops);

static paddr_t get_cbmem_console_from_coreboot_table(paddr_t table_addr,
						     size_t table_size)
{
	struct cb_header *header = NULL;
	void *ptr = NULL;
	uint32_t i = 0;
	struct cb_entry *entry = NULL;
	paddr_t cbmem_console_base = 0;
	void *base = NULL;

	base = core_mmu_add_mapping(MEM_AREA_RAM_NSEC, table_addr, table_size);
	if (!base)
		return 0;

	header = (struct cb_header *)base;
	if (memcmp(header->signature, "LBIO", 4))
		goto done;

	if (header->header_bytes + header->table_bytes > table_size)
		goto done;

	ptr = (uint8_t *)base + header->header_bytes;
	for (i = 0; i < header->table_entries; ++i) {
		entry = (struct cb_entry *)ptr;
		if ((uint8_t *)ptr >= (uint8_t *)base + table_size -
				sizeof(struct cb_entry)) {
			goto done;
		}

		switch (get_le32(&entry->tag)) {
		case CB_TAG_CBMEM_CONSOLE:
			cbmem_console_base = get_le64(&entry->value);
			goto done;
		default:
			/* We skip all but one tag type. */
			break;
		}

		ptr = (uint8_t *)ptr + get_le32(&entry->size);
	}

done:
	core_mmu_remove_mapping(MEM_AREA_RAM_NSEC, base, table_size);
	return cbmem_console_base;
}

bool cbmem_console_init_from_dt(void *fdt)
{
	int offset = 0;
	paddr_t cb_addr = 0;
	size_t cb_size = 0;
	paddr_t cbmem_console_base = 0;

	if (!fdt)
		return false;

	offset = fdt_path_offset(fdt, "/firmware/coreboot");
	if (offset < 0)
		return false;

	if (fdt_reg_info(fdt, offset, &cb_addr, &cb_size))
		return false;

	cbmem_console_base = get_cbmem_console_from_coreboot_table(cb_addr,
								   cb_size);
	if (!cbmem_console_base)
		return false;

	cbmem_console.base = cbmem_console_base;
	cbmem_console.console = (struct cbmem_console *)
		core_mmu_add_mapping(MEM_AREA_RAM_NSEC, cbmem_console_base,
				     sizeof(struct cbmem_console));
	if (!cbmem_console.console)
		return false;

	/*
	 * Copy the size now to prevent non-secure world from spoofing
	 * it later.
	 */
	cbmem_console.size = cbmem_console.console->size;
	cbmem_console.chip.ops = &cbmem_console_ops;

	register_serial_console(&cbmem_console.chip);
	return true;
}