coreboot
coreboot is an Open Source project aimed at replacing the proprietary BIOS found in most computers.
selfboot.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
4 #include <commonlib/endian.h>
5 #include <console/console.h>
6 #include <string.h>
7 #include <symbols.h>
8 #include <cbfs.h>
9 #include <lib.h>
10 #include <bootmem.h>
11 #include <program_loading.h>
12 #include <timestamp.h>
13 #include <cbmem.h>
14 #include <types.h>
15 
16 /* The type syntax for C is essentially unparsable. -- Rob Pike */
17 typedef int (*checker_t)(struct cbfs_payload_segment *cbfssegs, void *args);
18 
19 /* Decode a serialized cbfs payload segment
20  * from memory into native endianness.
21  */
23  const struct cbfs_payload_segment *src)
24 {
25  segment->type = read_be32(&src->type);
26  segment->compression = read_be32(&src->compression);
27  segment->offset = read_be32(&src->offset);
28  segment->load_addr = read_be64(&src->load_addr);
29  segment->len = read_be32(&src->len);
30  segment->mem_len = read_be32(&src->mem_len);
31 }
32 
33 static int segment_targets_type(void *dest, unsigned long memsz,
34  enum bootmem_type dest_type)
35 {
36  /* No bootmem to check in earlier stages, caller should not use
37  selfload_check(). */
38  if (!ENV_RAMSTAGE) {
40  "Callers not supposed to call selfload_check() in romstage");
41  return 0;
42  }
43 
44  uintptr_t d = (uintptr_t) dest;
45  if (bootmem_region_targets_type(d, memsz, dest_type))
46  return 1;
47 
48  if (payload_arch_usable_ram_quirk(d, memsz))
49  return 1;
50 
51  printk(BIOS_ERR, "SELF segment doesn't target RAM: %p, %lu bytes\n", dest, memsz);
53  return 0;
54 }
55 
56 static int load_one_segment(uint8_t *dest,
57  uint8_t *src,
58  size_t len,
59  size_t memsz,
61  int flags)
62 {
63  unsigned char *middle, *end;
64  printk(BIOS_DEBUG, "Loading Segment: addr: %p memsz: 0x%016zx filesz: 0x%016zx\n",
65  dest, memsz, len);
66 
67  /* Compute the boundaries of the segment */
68  end = dest + memsz;
69 
70  /* Copy data from the initial buffer */
71  switch (compression) {
72  case CBFS_COMPRESS_LZMA: {
73  printk(BIOS_DEBUG, "using LZMA\n");
75  len = ulzman(src, len, dest, memsz);
77  if (!len) /* Decompression Error. */
78  return 0;
79  break;
80  }
81  case CBFS_COMPRESS_LZ4: {
82  printk(BIOS_DEBUG, "using LZ4\n");
84  len = ulz4fn(src, len, dest, memsz);
86  if (!len) /* Decompression Error. */
87  return 0;
88  break;
89  }
90  case CBFS_COMPRESS_NONE: {
91  printk(BIOS_DEBUG, "it's not compressed!\n");
92  memcpy(dest, src, len);
93  break;
94  }
95  default:
96  printk(BIOS_INFO, "CBFS: Unknown compression type %d\n", compression);
97  return 0;
98  }
99  /* Calculate middle after any changes to len. */
100  middle = dest + len;
101  printk(BIOS_SPEW, "[ 0x%08lx, %08lx, 0x%08lx) <- %08lx\n",
102  (unsigned long)dest,
103  (unsigned long)middle,
104  (unsigned long)end,
105  (unsigned long)src);
106 
107  /* Zero the extra bytes between middle & end */
108  if (middle < end) {
110  "Clearing Segment: addr: 0x%016lx memsz: 0x%016lx\n",
111  (unsigned long)middle,
112  (unsigned long)(end - middle));
113 
114  /* Zero the extra bytes */
115  memset(middle, 0, end - middle);
116  }
117 
118  /*
119  * Each architecture can perform additional operations
120  * on the loaded segment
121  */
122  prog_segment_loaded((uintptr_t)dest, memsz, flags);
123 
124  return 1;
125 }
126 
127 /* Note: this function is a bit dangerous so is not exported.
128  * It assumes you're smart enough not to call it with the very
129  * last segment, since it uses seg + 1 */
131 {
132  return read_be32(&(seg + 1)->type) == PAYLOAD_SEGMENT_ENTRY;
133 }
134 
135 static int check_payload_segments(struct cbfs_payload_segment *cbfssegs,
136  enum bootmem_type dest_type)
137 {
138  uint8_t *dest;
139  size_t memsz;
140  struct cbfs_payload_segment *seg, segment;
141 
142  /* dest_type == INVALID means we're not supposed to check anything. */
143  if (dest_type == BM_MEM_INVALID)
144  return 0;
145 
146  for (seg = cbfssegs;; ++seg) {
147  printk(BIOS_DEBUG, "Checking segment from ROM address %p\n", seg);
148  cbfs_decode_payload_segment(&segment, seg);
149  dest = (uint8_t *)(uintptr_t)segment.load_addr;
150  memsz = segment.mem_len;
151  if (segment.type == PAYLOAD_SEGMENT_ENTRY)
152  break;
153  if (!segment_targets_type(dest, memsz, dest_type))
154  return -1;
155  }
156  return 0;
157 }
158 
159 static int load_payload_segments(struct cbfs_payload_segment *cbfssegs, uintptr_t *entry)
160 {
161  uint8_t *dest, *src;
162  size_t filesz, memsz;
164  struct cbfs_payload_segment *first_segment, *seg, segment;
165  int flags = 0;
166 
167  for (first_segment = seg = cbfssegs;; ++seg) {
168  printk(BIOS_DEBUG, "Loading segment from ROM address %p\n", seg);
169 
170  cbfs_decode_payload_segment(&segment, seg);
171  dest = (uint8_t *)(uintptr_t)segment.load_addr;
172  memsz = segment.mem_len;
173  compression = segment.compression;
174  filesz = segment.len;
175 
176  switch (segment.type) {
179  printk(BIOS_DEBUG, " %s (compression=%x)\n",
180  segment.type == PAYLOAD_SEGMENT_CODE
181  ? "code" : "data", segment.compression);
182  src = ((uint8_t *)first_segment) + segment.offset;
184  " New segment dstaddr %p memsize 0x%zx srcaddr %p filesize 0x%zx\n",
185  dest, memsz, src, filesz);
186 
187  /* Clean up the values */
188  if (filesz > memsz) {
189  filesz = memsz;
190  printk(BIOS_DEBUG, " cleaned up filesize 0x%zx\n", filesz);
191  }
192  break;
193 
194  case PAYLOAD_SEGMENT_BSS:
195  printk(BIOS_DEBUG, " BSS %p (%d byte)\n", (void *)
196  (intptr_t)segment.load_addr, segment.mem_len);
197  filesz = 0;
198  src = ((uint8_t *)first_segment) + segment.offset;
200  break;
201 
203  printk(BIOS_DEBUG, " Entry Point %p\n", (void *)
204  (intptr_t)segment.load_addr);
205 
206  *entry = segment.load_addr;
207  /* Per definition, a payload always has the entry point
208  * as last segment. Thus, we use the occurrence of the
209  * entry point as break condition for the loop.
210  */
211  return 0;
212 
213  default:
214  /* We found something that we don't know about. Throw
215  * hands into the sky and run away!
216  */
217  printk(BIOS_EMERG, "Bad segment type %x\n", segment.type);
218  return -1;
219  }
220  /* Note that the 'seg + 1' is safe as we only call this
221  * function on "not the last" * items, since entry
222  * is always last. */
223  if (last_loadable_segment(seg))
224  flags = SEG_FINAL;
225  if (!load_one_segment(dest, src, filesz, memsz, compression, flags))
226  return -1;
227  }
228 
229  return 1;
230 }
231 
233 {
234  return 0;
235 }
236 
237 bool selfload_mapped(struct prog *payload, void *mapping,
238  enum bootmem_type dest_type)
239 {
240  uintptr_t entry = 0;
241  struct cbfs_payload_segment *cbfssegs;
242 
243  cbfssegs = &((struct cbfs_payload *)mapping)->segments;
244 
245  if (check_payload_segments(cbfssegs, dest_type))
246  return false;
247 
248  if (load_payload_segments(cbfssegs, &entry))
249  return false;
250 
251  printk(BIOS_SPEW, "Loaded segments\n");
252 
253  /* Pass cbtables to payload if architecture desires it. */
254  prog_set_entry(payload, (void *)entry, cbmem_find(CBMEM_ID_CBTABLE));
255 
256  return true;
257 }
258 
259 bool selfload_check(struct prog *payload, enum bootmem_type dest_type)
260 {
261  if (prog_locate_hook(payload))
262  return false;
263 
264  payload->cbfs_type = CBFS_TYPE_SELF;
265  void *mapping = cbfs_type_map(prog_name(payload), NULL, &payload->cbfs_type);
266  if (!mapping)
267  return false;
268 
269  bool ret = selfload_mapped(payload, mapping, dest_type);
270 
271  cbfs_unmap(mapping);
272  return ret;
273 }
274 
275 bool selfload(struct prog *payload)
276 {
277  return selfload_check(payload, BM_MEM_INVALID);
278 }
void * memcpy(void *dest, const void *src, size_t n)
Definition: memcpy.c:7
void * memset(void *dstpp, int c, size_t len)
Definition: memset.c:12
void bootmem_dump_ranges(void)
Definition: bootmem.c:155
bootmem_type
Bootmem types match to LB_MEM tags, except for the following: BM_MEM_RAMSTAGE : Memory where any kind...
Definition: bootmem.h:19
@ BM_MEM_INVALID
Definition: bootmem.h:20
int bootmem_region_targets_type(uint64_t start, uint64_t size, enum bootmem_type dest_type)
Definition: bootmem.c:197
void cbfs_unmap(void *mapping)
Definition: cbfs.c:86
static void * cbfs_type_map(const char *name, size_t *size_out, enum cbfs_type *type)
Definition: cbfs.h:256
@ CBFS_COMPRESS_LZMA
@ CBFS_COMPRESS_NONE
@ CBFS_COMPRESS_LZ4
@ PAYLOAD_SEGMENT_ENTRY
@ PAYLOAD_SEGMENT_DATA
@ PAYLOAD_SEGMENT_CODE
@ PAYLOAD_SEGMENT_BSS
@ CBFS_TYPE_SELF
void * cbmem_find(u32 id)
Definition: imd_cbmem.c:166
#define CBMEM_ID_CBTABLE
Definition: cbmem_id.h:16
struct @413::@414 args
static uint32_t read_be32(const void *src)
Definition: endian.h:85
static uint64_t read_be64(const void *src)
Definition: endian.h:112
#define printk(level,...)
Definition: stdlib.h:16
size_t ulz4fn(const void *src, size_t srcn, void *dst, size_t dstn)
Definition: lz4_wrapper.c:96
unsigned int type
Definition: edid.c:57
void timestamp_add_now(enum timestamp_id id)
Definition: timestamp.c:141
size_t ulzman(const void *src, size_t srcn, void *dst, size_t dstn)
Definition: lzma.c:19
#define BIOS_INFO
BIOS_INFO - Expected events.
Definition: loglevel.h:113
#define BIOS_DEBUG
BIOS_DEBUG - Verbose output.
Definition: loglevel.h:128
#define BIOS_EMERG
BIOS_EMERG - Emergency / Fatal.
Definition: loglevel.h:25
#define BIOS_ERR
BIOS_ERR - System in incomplete state.
Definition: loglevel.h:72
#define BIOS_SPEW
BIOS_SPEW - Excessively verbose output.
Definition: loglevel.h:142
@ SEG_FINAL
void prog_segment_loaded(uintptr_t start, size_t size, int flags)
Definition: prog_ops.c:6
static void prog_set_entry(struct prog *prog, void *e, void *arg)
static const char * prog_name(const struct prog *prog)
int prog_locate_hook(struct prog *prog)
Definition: prog_loaders.c:48
#define ENV_RAMSTAGE
Definition: rules.h:150
const struct smm_save_state_ops *legacy_ops __weak
Definition: save_state.c:8
bool selfload_mapped(struct prog *payload, void *mapping, enum bootmem_type dest_type)
Definition: selfboot.c:237
__weak int payload_arch_usable_ram_quirk(uint64_t start, uint64_t size)
Definition: selfboot.c:232
bool selfload(struct prog *payload)
Definition: selfboot.c:275
static int load_payload_segments(struct cbfs_payload_segment *cbfssegs, uintptr_t *entry)
Definition: selfboot.c:159
static int segment_targets_type(void *dest, unsigned long memsz, enum bootmem_type dest_type)
Definition: selfboot.c:33
static int load_one_segment(uint8_t *dest, uint8_t *src, size_t len, size_t memsz, uint32_t compression, int flags)
Definition: selfboot.c:56
int(* checker_t)(struct cbfs_payload_segment *cbfssegs, void *args)
Definition: selfboot.c:17
static void cbfs_decode_payload_segment(struct cbfs_payload_segment *segment, const struct cbfs_payload_segment *src)
Definition: selfboot.c:22
bool selfload_check(struct prog *payload, enum bootmem_type dest_type)
Definition: selfboot.c:259
static int check_payload_segments(struct cbfs_payload_segment *cbfssegs, enum bootmem_type dest_type)
Definition: selfboot.c:135
static int last_loadable_segment(struct cbfs_payload_segment *seg)
Definition: selfboot.c:130
#define NULL
Definition: stddef.h:19
unsigned int uint32_t
Definition: stdint.h:14
signed long intptr_t
Definition: stdint.h:20
unsigned long uintptr_t
Definition: stdint.h:21
unsigned long long uint64_t
Definition: stdint.h:17
unsigned char uint8_t
Definition: stdint.h:8
this is the sub-header for payload components.
enum cbfs_type cbfs_type
@ TS_ULZ4F_END
@ TS_ULZMA_START
@ TS_ULZMA_END
@ TS_ULZ4F_START