/ src / lib / imd_cbmem.c
imd_cbmem.c
  1  /* SPDX-License-Identifier: GPL-2.0-only */
  2  
  3  #include <assert.h>
  4  #include <boot/coreboot_tables.h>
  5  #include <bootmem.h>
  6  #include <console/console.h>
  7  #include <cbmem.h>
  8  #include <imd.h>
  9  #include <lib.h>
 10  #include <types.h>
 11  
 12  /* The program loader passes on cbmem_top and the program entry point
 13     has to fill in the _cbmem_top_ptr symbol based on the calling arguments. */
 14  uintptr_t _cbmem_top_ptr;
 15  
 16  static struct imd imd;
 17  
 18  uintptr_t cbmem_top(void)
 19  {
 20  	if (ENV_CREATES_CBMEM) {
 21  		static uintptr_t top;
 22  		if (top)
 23  			return top;
 24  		top = cbmem_top_chipset();
 25  		return top;
 26  	}
 27  	if (ENV_POSTCAR || ENV_RAMSTAGE)
 28  		return _cbmem_top_ptr;
 29  
 30  	dead_code();
 31  }
 32  
 33  int cbmem_initialized;
 34  
 35  static inline const struct cbmem_entry *imd_to_cbmem(const struct imd_entry *e)
 36  {
 37  	return (const struct cbmem_entry *)e;
 38  }
 39  
 40  static inline const struct imd_entry *cbmem_to_imd(const struct cbmem_entry *e)
 41  {
 42  	return (const struct imd_entry *)e;
 43  }
 44  
 45  void cbmem_initialize_empty(void)
 46  {
 47  	cbmem_initialize_empty_id_size(0, 0);
 48  }
 49  
 50  static void cbmem_top_init_once(void)
 51  {
 52  	/* Call one-time hook on expected cbmem init during boot. */
 53  	if (!ENV_CREATES_CBMEM)
 54  		return;
 55  
 56  	/* The test is only effective on X86 and when address hits UC memory. */
 57  	if (ENV_X86)
 58  		quick_ram_check_or_die(cbmem_top() - sizeof(u32));
 59  }
 60  
 61  void cbmem_initialize_empty_id_size(u32 id, u64 size)
 62  {
 63  	const int no_recovery = 0;
 64  
 65  	cbmem_top_init_once();
 66  
 67  	imd_handle_init(&imd, (void *)cbmem_top());
 68  
 69  	printk(BIOS_DEBUG, "CBMEM:\n");
 70  
 71  	if (imd_create_tiered_empty(&imd, CBMEM_ROOT_MIN_SIZE, CBMEM_LG_ALIGN,
 72  					CBMEM_SM_ROOT_SIZE, CBMEM_SM_ALIGN)) {
 73  		printk(BIOS_DEBUG, "failed.\n");
 74  		return;
 75  	}
 76  
 77  	/* Add the specified range first */
 78  	if (size)
 79  		cbmem_add(id, size);
 80  
 81  	/* Complete migration to CBMEM. */
 82  	cbmem_run_init_hooks(no_recovery);
 83  
 84  	cbmem_initialized = 1;
 85  }
 86  
 87  int cbmem_initialize(void)
 88  {
 89  	return cbmem_initialize_id_size(0, 0);
 90  }
 91  
 92  int cbmem_initialize_id_size(u32 id, u64 size)
 93  {
 94  	const int recovery = 1;
 95  
 96  	cbmem_top_init_once();
 97  
 98  	imd_handle_init(&imd, (void *)cbmem_top());
 99  
100  	if (imd_recover(&imd))
101  		return 1;
102  
103  	/*
104  	 * Lock the imd in romstage on a recovery. The assumption is that
105  	 * if the imd area was recovered in romstage then S3 resume path
106  	 * is being taken.
107  	 */
108  	if (ENV_CREATES_CBMEM)
109  		imd_lockdown(&imd);
110  
111  	/* Add the specified range first */
112  	if (size)
113  		cbmem_add(id, size);
114  
115  	/* Complete migration to CBMEM. */
116  	cbmem_run_init_hooks(recovery);
117  
118  	cbmem_initialized = 1;
119  
120  	/* Recovery successful. */
121  	return 0;
122  }
123  
124  int cbmem_recovery(int is_wakeup)
125  {
126  	int rv = 0;
127  	if (!is_wakeup)
128  		cbmem_initialize_empty();
129  	else
130  		rv = cbmem_initialize();
131  	return rv;
132  }
133  
134  const struct cbmem_entry *cbmem_entry_add(u32 id, u64 size64)
135  {
136  	const struct imd_entry *e;
137  
138  	e = imd_entry_find_or_add(&imd, id, size64);
139  
140  	return imd_to_cbmem(e);
141  }
142  
143  void *cbmem_add(u32 id, u64 size)
144  {
145  	const struct imd_entry *e;
146  
147  	e = imd_entry_find_or_add(&imd, id, size);
148  
149  	if (e == NULL)
150  		return NULL;
151  
152  	return imd_entry_at(&imd, e);
153  }
154  
155  /* Retrieve a region provided a given id. */
156  const struct cbmem_entry *cbmem_entry_find(u32 id)
157  {
158  	const struct imd_entry *e;
159  
160  	e = imd_entry_find(&imd, id);
161  
162  	return imd_to_cbmem(e);
163  }
164  
165  void *cbmem_find(u32 id)
166  {
167  	const struct imd_entry *e;
168  
169  	e = imd_entry_find(&imd, id);
170  
171  	if (e == NULL)
172  		return NULL;
173  
174  	return imd_entry_at(&imd, e);
175  }
176  
177  /* Remove a reserved region. Returns 0 on success, < 0 on error. Note: A region
178   * cannot be removed unless it was the last one added. */
179  int cbmem_entry_remove(const struct cbmem_entry *entry)
180  {
181  	return imd_entry_remove(&imd, cbmem_to_imd(entry));
182  }
183  
184  u64 cbmem_entry_size(const struct cbmem_entry *entry)
185  {
186  	return imd_entry_size(cbmem_to_imd(entry));
187  }
188  
189  void *cbmem_entry_start(const struct cbmem_entry *entry)
190  {
191  	return imd_entry_at(&imd, cbmem_to_imd(entry));
192  }
193  
194  void cbmem_add_bootmem(void)
195  {
196  	void *baseptr;
197  	size_t size;
198  
199  	if (cbmem_get_region(&baseptr, &size)) {
200  		printk(BIOS_ERR, "CBMEM pointer/size not found!\n");
201  		return;
202  	}
203  	bootmem_add_range((uintptr_t)baseptr, size, BM_MEM_TABLE);
204  }
205  
206  int cbmem_get_region(void **baseptr, size_t *size)
207  {
208  	return imd_region_used(&imd, baseptr, size);
209  }
210  
211  #if ENV_PAYLOAD_LOADER || (CONFIG(EARLY_CBMEM_LIST) && ENV_HAS_CBMEM)
212  /*
213   * -fdata-sections doesn't work so well on read only strings. They all
214   * get put in the same section even though those strings may never be
215   * referenced in the final binary.
216   */
217  void cbmem_list(void)
218  {
219  	static const struct imd_lookup lookup[] = { CBMEM_ID_TO_NAME_TABLE };
220  
221  	imd_print_entries(&imd, lookup, ARRAY_SIZE(lookup));
222  }
223  #endif
224  
225  void cbmem_add_records_to_cbtable(struct lb_header *header)
226  {
227  	struct imd_cursor cursor;
228  
229  	if (imd_cursor_init(&imd, &cursor))
230  		return;
231  
232  	while (1) {
233  		const struct imd_entry *e;
234  		struct lb_cbmem_entry *lbe;
235  		uint32_t id;
236  
237  		e = imd_cursor_next(&cursor);
238  
239  		if (e == NULL)
240  			break;
241  
242  		id = imd_entry_id(e);
243  		/* Don't add these metadata entries. */
244  		if (id == CBMEM_ID_IMD_ROOT || id == CBMEM_ID_IMD_SMALL)
245  			continue;
246  
247  		lbe = (struct lb_cbmem_entry *)lb_new_record(header);
248  		lbe->tag = LB_TAG_CBMEM_ENTRY;
249  		lbe->size = sizeof(*lbe);
250  		lbe->address = (uintptr_t)imd_entry_at(&imd, e);
251  		lbe->entry_size = imd_entry_size(e);
252  		lbe->id = id;
253  	}
254  }