summaryrefslogtreecommitdiff
path: root/src/lib/imd_cbmem.c
blob: a88cf98d6c94c1e05684ac815916e9f950d3c915 (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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
/* SPDX-License-Identifier: GPL-2.0-only */

#include <assert.h>
#include <boot/coreboot_tables.h>
#include <bootmem.h>
#include <console/console.h>
#include <cbmem.h>
#include <imd.h>
#include <lib.h>
#include <types.h>

/* The program loader passes on cbmem_top and the program entry point
   has to fill in the _cbmem_top_ptr symbol based on the calling arguments. */
uintptr_t _cbmem_top_ptr;

static struct imd imd;

uintptr_t cbmem_top(void)
{
	if (ENV_CREATES_CBMEM) {
		static uintptr_t top;
		if (top)
			return top;
		top = cbmem_top_chipset();
		return top;
	}
	if (ENV_POSTCAR || ENV_RAMSTAGE)
		return _cbmem_top_ptr;

	dead_code();
}

int cbmem_initialized;

static inline const struct cbmem_entry *imd_to_cbmem(const struct imd_entry *e)
{
	return (const struct cbmem_entry *)e;
}

static inline const struct imd_entry *cbmem_to_imd(const struct cbmem_entry *e)
{
	return (const struct imd_entry *)e;
}

void cbmem_initialize_empty(void)
{
	cbmem_initialize_empty_id_size(0, 0);
}

static void cbmem_top_init_once(void)
{
	/* Call one-time hook on expected cbmem init during boot. */
	if (!ENV_CREATES_CBMEM)
		return;

	/* The test is only effective on X86 and when address hits UC memory. */
	if (ENV_X86)
		quick_ram_check_or_die(cbmem_top() - sizeof(u32));
}

void cbmem_initialize_empty_id_size(u32 id, u64 size)
{
	const int no_recovery = 0;

	cbmem_top_init_once();

	imd_handle_init(&imd, (void *)cbmem_top());

	printk(BIOS_DEBUG, "CBMEM:\n");

	if (imd_create_tiered_empty(&imd, CBMEM_ROOT_MIN_SIZE, CBMEM_LG_ALIGN,
					CBMEM_SM_ROOT_SIZE, CBMEM_SM_ALIGN)) {
		printk(BIOS_DEBUG, "failed.\n");
		return;
	}

	/* Add the specified range first */
	if (size)
		cbmem_add(id, size);

	/* Complete migration to CBMEM. */
	cbmem_run_init_hooks(no_recovery);

	cbmem_initialized = 1;
}

int cbmem_initialize(void)
{
	return cbmem_initialize_id_size(0, 0);
}

int cbmem_initialize_id_size(u32 id, u64 size)
{
	const int recovery = 1;

	cbmem_top_init_once();

	imd_handle_init(&imd, (void *)cbmem_top());

	if (imd_recover(&imd))
		return 1;

	/*
	 * Lock the imd in romstage on a recovery. The assumption is that
	 * if the imd area was recovered in romstage then S3 resume path
	 * is being taken.
	 */
	if (ENV_CREATES_CBMEM)
		imd_lockdown(&imd);

	/* Add the specified range first */
	if (size)
		cbmem_add(id, size);

	/* Complete migration to CBMEM. */
	cbmem_run_init_hooks(recovery);

	cbmem_initialized = 1;

	/* Recovery successful. */
	return 0;
}

int cbmem_recovery(int is_wakeup)
{
	int rv = 0;
	if (!is_wakeup)
		cbmem_initialize_empty();
	else
		rv = cbmem_initialize();
	return rv;
}

const struct cbmem_entry *cbmem_entry_add(u32 id, u64 size64)
{
	const struct imd_entry *e;

	e = imd_entry_find_or_add(&imd, id, size64);

	return imd_to_cbmem(e);
}

void *cbmem_add(u32 id, u64 size)
{
	const struct imd_entry *e;

	e = imd_entry_find_or_add(&imd, id, size);

	if (e == NULL)
		return NULL;

	return imd_entry_at(&imd, e);
}

/* Retrieve a region provided a given id. */
const struct cbmem_entry *cbmem_entry_find(u32 id)
{
	const struct imd_entry *e;

	e = imd_entry_find(&imd, id);

	return imd_to_cbmem(e);
}

void *cbmem_find(u32 id)
{
	const struct imd_entry *e;

	e = imd_entry_find(&imd, id);

	if (e == NULL)
		return NULL;

	return imd_entry_at(&imd, e);
}

/* Remove a reserved region. Returns 0 on success, < 0 on error. Note: A region
 * cannot be removed unless it was the last one added. */
int cbmem_entry_remove(const struct cbmem_entry *entry)
{
	return imd_entry_remove(&imd, cbmem_to_imd(entry));
}

u64 cbmem_entry_size(const struct cbmem_entry *entry)
{
	return imd_entry_size(cbmem_to_imd(entry));
}

void *cbmem_entry_start(const struct cbmem_entry *entry)
{
	return imd_entry_at(&imd, cbmem_to_imd(entry));
}

void cbmem_add_bootmem(void)
{
	void *baseptr = NULL;
	size_t size = 0;

	cbmem_get_region(&baseptr, &size);
	bootmem_add_range((uintptr_t)baseptr, size, BM_MEM_TABLE);
}

void cbmem_get_region(void **baseptr, size_t *size)
{
	imd_region_used(&imd, baseptr, size);
}

#if ENV_PAYLOAD_LOADER || (CONFIG(EARLY_CBMEM_LIST) && ENV_HAS_CBMEM)
/*
 * -fdata-sections doesn't work so well on read only strings. They all
 * get put in the same section even though those strings may never be
 * referenced in the final binary.
 */
void cbmem_list(void)
{
	static const struct imd_lookup lookup[] = { CBMEM_ID_TO_NAME_TABLE };

	imd_print_entries(&imd, lookup, ARRAY_SIZE(lookup));
}
#endif

void cbmem_add_records_to_cbtable(struct lb_header *header)
{
	struct imd_cursor cursor;

	if (imd_cursor_init(&imd, &cursor))
		return;

	while (1) {
		const struct imd_entry *e;
		struct lb_cbmem_entry *lbe;
		uint32_t id;

		e = imd_cursor_next(&cursor);

		if (e == NULL)
			break;

		id = imd_entry_id(e);
		/* Don't add these metadata entries. */
		if (id == CBMEM_ID_IMD_ROOT || id == CBMEM_ID_IMD_SMALL)
			continue;

		lbe = (struct lb_cbmem_entry *)lb_new_record(header);
		lbe->tag = LB_TAG_CBMEM_ENTRY;
		lbe->size = sizeof(*lbe);
		lbe->address = (uintptr_t)imd_entry_at(&imd, e);
		lbe->entry_size = imd_entry_size(e);
		lbe->id = id;
	}
}