coreboot
coreboot is an Open Source project aimed at replacing the proprietary BIOS found in most computers.
rmodule.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <assert.h>
4 #include <cbmem.h>
5 #include <cbfs.h>
6 #include <string.h>
7 #include <console/console.h>
8 #include <program_loading.h>
9 #include <rmodule.h>
10 #include <types.h>
11 
12 /* Change this define to get more verbose debugging for module loading. */
13 #define PK_ADJ_LEVEL BIOS_NEVER
14 
16 
17 static inline int rmodule_is_loaded(const struct rmodule *module)
18 {
19  return module->location != NULL;
20 }
21 
22 /* Calculate a loaded program address based on the blob address. */
23 static inline void *rmodule_load_addr(const struct rmodule *module,
24  uintptr_t blob_addr)
25 {
26  char *loc = module->location;
27  return &loc[blob_addr - module->header->module_link_start_address];
28 }
29 
30 /* Initialize a rmodule structure based on raw data. */
31 int rmodule_parse(void *ptr, struct rmodule *module)
32 {
33  char *base;
34  struct rmodule_header *rhdr;
35 
36  base = ptr;
37  rhdr = ptr;
38 
39  if (rhdr == NULL)
40  return -1;
41 
42  /* Sanity check the raw data. */
43  if (rhdr->magic != RMODULE_MAGIC)
44  return -1;
45  if (rhdr->version != RMODULE_VERSION_1)
46  return -1;
47 
48  /* Indicate the module hasn't been loaded yet. */
49  module->location = NULL;
50 
51  /* The rmodule only needs a reference to the reloc_header. */
52  module->header = rhdr;
53 
54  /* The payload lives after the header. */
55  module->payload = &base[rhdr->payload_begin_offset];
56  module->payload_size = rhdr->payload_end_offset -
58  module->relocations = &base[rhdr->relocations_begin_offset];
59 
60  return 0;
61 }
62 
63 int rmodule_memory_size(const struct rmodule *module)
64 {
65  return module->header->module_program_size;
66 }
67 
68 void *rmodule_parameters(const struct rmodule *module)
69 {
70  if (!rmodule_is_loaded(module))
71  return NULL;
72 
73  /* Indicate if there are no parameters. */
74  if (module->header->parameters_begin == module->header->parameters_end)
75  return NULL;
76 
77  return rmodule_load_addr(module, module->header->parameters_begin);
78 }
79 
80 int rmodule_entry_offset(const struct rmodule *module)
81 {
82  return module->header->module_entry_point -
84 }
85 
86 void *rmodule_entry(const struct rmodule *module)
87 {
88  if (!rmodule_is_loaded(module))
89  return NULL;
90 
91  return rmodule_load_addr(module, module->header->module_entry_point);
92 }
93 
94 static void rmodule_clear_bss(struct rmodule *module)
95 {
96  char *begin;
97  int size;
98 
99  begin = rmodule_load_addr(module, module->header->bss_begin);
100  size = module->header->bss_end - module->header->bss_begin;
101  memset(begin, 0, size);
102 }
103 
104 static inline size_t rmodule_number_relocations(const struct rmodule *module)
105 {
106  size_t r;
107 
108  r = module->header->relocations_end_offset;
109  r -= module->header->relocations_begin_offset;
110  r /= sizeof(uintptr_t);
111  return r;
112 }
113 
114 static void rmodule_copy_payload(const struct rmodule *module)
115 {
116  printk(BIOS_DEBUG, "Loading module at %p with entry %p. "
117  "filesize: 0x%x memsize: 0x%x\n",
118  module->location, rmodule_entry(module),
119  module->payload_size, rmodule_memory_size(module));
120 
121  /* No need to copy the payload if the load location and the
122  * payload location are the same. */
123  if (module->location == module->payload)
124  return;
125 
126  memcpy(module->location, module->payload, module->payload_size);
127 }
128 
129 static int rmodule_relocate(const struct rmodule *module)
130 {
131  size_t num_relocations;
132  const uintptr_t *reloc;
133  uintptr_t adjustment;
134 
135  /* Each relocation needs to be adjusted relative to the beginning of
136  * the loaded program. */
137  adjustment = (uintptr_t)rmodule_load_addr(module, 0);
138 
139  reloc = module->relocations;
140  num_relocations = rmodule_number_relocations(module);
141 
142  printk(BIOS_DEBUG, "Processing %zu relocs. Offset value of 0x%08lx\n",
143  num_relocations, (unsigned long)adjustment);
144 
145  while (num_relocations > 0) {
146  uintptr_t *adjust_loc;
147 
148  /* If the adjustment location is non-NULL adjust it. */
149  adjust_loc = rmodule_load_addr(module, *reloc);
150  printk(PK_ADJ_LEVEL, "Adjusting %p: 0x%08lx -> 0x%08lx\n",
151  adjust_loc, (unsigned long) *adjust_loc,
152  (unsigned long) (*adjust_loc + adjustment));
153  *adjust_loc += adjustment;
154 
155  reloc++;
156  num_relocations--;
157  }
158 
159  return 0;
160 }
161 
162 int rmodule_load_alignment(const struct rmodule *module)
163 {
164  /* The load alignment is the start of the program's linked address.
165  * The base address where the program is loaded needs to be a multiple
166  * of the program's starting link address. That way all data alignment
167  * in the program is preserved. Default to 4KiB. */
168  return 4096;
169 }
170 
171 int rmodule_load(void *base, struct rmodule *module)
172 {
173  /*
174  * In order to load the module at a given address, the following steps
175  * take place:
176  * 1. Copy payload to base address.
177  * 2. Adjust relocations within the module to new base address.
178  * 3. Clear the bss segment last since the relocations live where
179  * the bss is. If an rmodule is being loaded from its load
180  * address the relocations need to be processed before the bss.
181  */
182  module->location = base;
183  rmodule_copy_payload(module);
184  if (rmodule_relocate(module))
185  return -1;
186  rmodule_clear_bss(module);
187 
189  rmodule_memory_size(module), SEG_FINAL);
190 
191  return 0;
192 }
193 
194 static void *rmodule_cbfs_allocator(void *rsl_arg, size_t unused,
195  const union cbfs_mdata *mdata)
196 {
197  struct rmod_stage_load *rsl = rsl_arg;
198 
200  region_alignment >= sizeof(struct rmodule_header));
201 
202  /* The CBFS core just passes us the decompressed size of the file, but
203  we need to know the memlen of the binary image. We need to find and
204  parse the stage header explicitly. */
205  const struct cbfs_file_attr_stageheader *sattr = cbfs_find_attr(mdata,
206  CBFS_FILE_ATTR_TAG_STAGEHEADER, sizeof(*sattr));
207  if (!sattr) {
208  printk(BIOS_ERR, "rmodule '%s' has no stage header!\n",
209  rsl->prog->name);
210  return NULL;
211  }
212 
213  const size_t memlen = be32toh(sattr->memlen);
214 
215  /* Place the rmodule according to alignment. The rmodule files
216  * themselves are packed as a header and a payload, however the rmodule
217  * itself is linked along with the header. The header starts at address
218  * 0. Immediately following the header in the file is the program,
219  * however its starting address is determined by the rmodule linker
220  * script. In short, sizeof(struct rmodule_header) can be less than
221  * or equal to the linked address of the program. Therefore we want
222  * to place the rmodule so that the program falls on the aligned
223  * address with the header just before it. Therefore, we need at least
224  * a page to account for the size of the header. */
225  size_t region_size = ALIGN(memlen + region_alignment, 4096);
226  /* The program starts immediately after the header. However,
227  * it needs to be aligned to a 4KiB boundary. Therefore, adjust the
228  * program location so that the program lands on a page boundary. The
229  * layout looks like the following:
230  *
231  * +--------------------------------+ region_alignment + region_size
232  * | >= 0 bytes from alignment |
233  * +--------------------------------+ program end (4KiB aligned)
234  * | program size |
235  * +--------------------------------+ program_begin (4KiB aligned)
236  * | sizeof(struct rmodule_header) |
237  * +--------------------------------+ rmodule header start
238  * | >= 0 bytes from alignment |
239  * +--------------------------------+ region_alignment
240  */
241 
242  uint8_t *stage_region = cbmem_add(rsl->cbmem_id, region_size);
243  if (stage_region == NULL)
244  return NULL;
245 
246  return stage_region + region_alignment - sizeof(struct rmodule_header);
247 }
248 
250 {
251  struct rmodule rmod_stage;
252 
253  if (rsl->prog == NULL || prog_name(rsl->prog) == NULL)
254  return -1;
255 
256  if (prog_locate_hook(rsl->prog))
257  return -1;
258 
259  void *rmod_loc = cbfs_alloc(prog_name(rsl->prog),
261  if (!rmod_loc)
262  return -1;
263 
264  if (rmodule_parse(rmod_loc, &rmod_stage))
265  return -1;
266 
267  if (rmodule_load(rmod_loc + sizeof(struct rmodule_header), &rmod_stage))
268  return -1;
269 
270  prog_set_area(rsl->prog, rmod_stage.location,
271  rmodule_memory_size(&rmod_stage));
272 
273  /* Allow caller to pick up parameters, if available. */
274  rsl->params = rmodule_parameters(&rmod_stage);
275 
276  prog_set_entry(rsl->prog, rmodule_entry(&rmod_stage), rsl->params);
277 
278  return 0;
279 }
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
#define assert(statement)
Definition: assert.h:74
#define MIN_UNSAFE(a, b)
Definition: helpers.h:23
#define IS_POWER_OF_2(x)
Definition: helpers.h:50
static void * cbfs_alloc(const char *name, cbfs_allocator_t allocator, void *arg, size_t *size_out)
Definition: cbfs.h:215
const void * cbfs_find_attr(const union cbfs_mdata *mdata, uint32_t attr_tag, size_t size_check)
Definition: cbfs_private.c:167
@ CBFS_FILE_ATTR_TAG_STAGEHEADER
#define DYN_CBMEM_ALIGN_SIZE
Definition: cbmem.h:26
void * cbmem_add(u32 id, u64 size)
Definition: imd_cbmem.c:144
#define printk(level,...)
Definition: stdlib.h:16
#define ALIGN
Definition: asm.h:22
static uint32_t be32toh(uint32_t big_endian_32bits)
Definition: endian.h:130
#define BIOS_DEBUG
BIOS_DEBUG - Verbose output.
Definition: loglevel.h:128
#define BIOS_ERR
BIOS_ERR - System in incomplete state.
Definition: loglevel.h:72
@ 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
static void prog_set_area(struct prog *prog, void *start, size_t size)
#define RMODULE_VERSION_1
Definition: rmodule-defs.h:9
#define RMODULE_MAGIC
Definition: rmodule-defs.h:8
#define PK_ADJ_LEVEL
Definition: rmodule.c:13
static size_t rmodule_number_relocations(const struct rmodule *module)
Definition: rmodule.c:104
int rmodule_memory_size(const struct rmodule *module)
Definition: rmodule.c:63
void * rmodule_entry(const struct rmodule *module)
Definition: rmodule.c:86
static void rmodule_clear_bss(struct rmodule *module)
Definition: rmodule.c:94
static int rmodule_is_loaded(const struct rmodule *module)
Definition: rmodule.c:17
void * rmodule_parameters(const struct rmodule *module)
Definition: rmodule.c:68
static void rmodule_copy_payload(const struct rmodule *module)
Definition: rmodule.c:114
int rmodule_parse(void *ptr, struct rmodule *module)
Definition: rmodule.c:31
int rmodule_load(void *base, struct rmodule *module)
Definition: rmodule.c:171
const size_t region_alignment
Definition: rmodule.c:15
int rmodule_load_alignment(const struct rmodule *module)
Definition: rmodule.c:162
int rmodule_stage_load(struct rmod_stage_load *rsl)
Definition: rmodule.c:249
static void * rmodule_load_addr(const struct rmodule *module, uintptr_t blob_addr)
Definition: rmodule.c:23
int rmodule_entry_offset(const struct rmodule *module)
Definition: rmodule.c:80
static void * rmodule_cbfs_allocator(void *rsl_arg, size_t unused, const union cbfs_mdata *mdata)
Definition: rmodule.c:194
static int rmodule_relocate(const struct rmodule *module)
Definition: rmodule.c:129
uintptr_t base
Definition: uart.c:17
#define NULL
Definition: stddef.h:19
unsigned long uintptr_t
Definition: stdint.h:21
unsigned char uint8_t
Definition: stdint.h:8
const char * name
struct prog * prog
Definition: rmodule.h:44
uint32_t cbmem_id
Definition: rmodule.h:43
void * params
Definition: rmodule.h:45
uint32_t module_link_start_address
Definition: rmodule-defs.h:28
uint16_t magic
Definition: rmodule-defs.h:14
uint32_t relocations_begin_offset
Definition: rmodule-defs.h:21
uint32_t parameters_begin
Definition: rmodule-defs.h:37
uint32_t bss_begin
Definition: rmodule-defs.h:40
uint32_t module_entry_point
Definition: rmodule-defs.h:34
uint32_t bss_end
Definition: rmodule-defs.h:41
uint32_t payload_end_offset
Definition: rmodule-defs.h:19
uint32_t module_program_size
Definition: rmodule-defs.h:32
uint32_t relocations_end_offset
Definition: rmodule-defs.h:22
uint8_t version
Definition: rmodule-defs.h:15
uint32_t parameters_end
Definition: rmodule-defs.h:38
uint32_t payload_begin_offset
Definition: rmodule-defs.h:18
void * location
Definition: rmodule.h:52
const void * payload
Definition: rmodule.h:54
struct rmodule_header * header
Definition: rmodule.h:53
int payload_size
Definition: rmodule.h:55
void * relocations
Definition: rmodule.h:56