summaryrefslogtreecommitdiff
path: root/lib/coreboot/coreboot_table.c
blob: 43e9835903725427f5b1213b922f4b3cea9f0a48 (plain)
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
/*
 * Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <assert.h>
#include <string.h>

#include <drivers/coreboot/cbmem_console.h>
#include <common/debug.h>
#include <lib/coreboot.h>
#include <lib/mmio.h>
#include <lib/xlat_tables/xlat_tables_v2.h>

/*
 * Structures describing coreboot's in-memory descriptor tables. See
 * <coreboot>/src/commonlib/include/commonlib/coreboot_tables.h for
 * canonical implementation.
 */

typedef struct {
	char signature[4];
	uint32_t header_bytes;
	uint32_t header_checksum;
	uint32_t table_bytes;
	uint32_t table_checksum;
	uint32_t table_entries;
} cb_header_t;

typedef enum {
	CB_TAG_MEMORY = 0x1,
	CB_TAG_SERIAL = 0xf,
	CB_TAG_CBMEM_CONSOLE = 0x17,
} cb_tag_t;

typedef struct {
	uint32_t tag;
	uint32_t size;
	union {
		coreboot_memrange_t memranges[COREBOOT_MAX_MEMRANGES];
		coreboot_serial_t serial;
		uint64_t uint64;
	};
} cb_entry_t;

coreboot_memrange_t coreboot_memranges[COREBOOT_MAX_MEMRANGES];
coreboot_serial_t coreboot_serial;
uint64_t coreboot_table_addr;
uint32_t coreboot_table_size;

/*
 * The coreboot table is parsed before the MMU is enabled (i.e. with strongly
 * ordered memory), so we cannot make unaligned accesses. The table entries
 * immediately follow one another without padding, so nothing after the header
 * is guaranteed to be naturally aligned. Therefore, we need to define safety
 * functions that can read unaligned integers.
 */
static uint32_t read_le32(uint32_t *p)
{
	uintptr_t addr = (uintptr_t)p;
	return mmio_read_8(addr)		|
	       mmio_read_8(addr + 1) << 8	|
	       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.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);
}

coreboot_memory_t coreboot_get_memory_type(uintptr_t start, size_t size)
{
	int i;

	for (i = 0; i < COREBOOT_MAX_MEMRANGES; i++) {
		coreboot_memrange_t *range = &coreboot_memranges[i];

		if (range->type == CB_MEM_NONE)
			break;	/* end of table reached */
		if ((start >= range->start) &&
		    (start - range->start < range->size) &&
		    (size <= range->size - (start - range->start))) {
			return range->type;
		}
	}

	return CB_MEM_NONE;
}

void coreboot_get_table_location(uint64_t *address, uint32_t *size)
{
	*address = coreboot_table_addr;
	*size = coreboot_table_size;
}

void coreboot_table_setup(void *base)
{
	cb_header_t *header = base;
	void *ptr;
	int i;

	if (strncmp(header->signature, "LBIO", 4)) {
		ERROR("coreboot table signature corrupt!\n");
		return;
	}
	coreboot_table_addr = (uint64_t) base;
	coreboot_table_size = header->header_bytes + header->table_bytes;

	ptr = base + header->header_bytes;
	for (i = 0; i < header->table_entries; i++) {
		size_t size;
		cb_entry_t *entry = ptr;

		if (ptr - base >= header->header_bytes + header->table_bytes) {
			ERROR("coreboot table exceeds its bounds!\n");
			break;
		}

		switch (read_le32(&entry->tag)) {
		case CB_TAG_MEMORY:
			size = read_le32(&entry->size) -
			       offsetof(cb_entry_t, memranges);
			if (size > sizeof(coreboot_memranges)) {
				ERROR("Need to truncate coreboot memranges!\n");
				size = sizeof(coreboot_memranges);
			}
			memcpy(&coreboot_memranges, &entry->memranges, size);
			break;
		case CB_TAG_SERIAL:
			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;
		}

		ptr += read_le32(&entry->size);
	}
}