blob: 1446440e5a136b91050d32a0b64e410673f08b99 [file] [log] [blame]
Angel Pons118a9c72020-04-02 23:48:34 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Elyes HAOUAS93a195c2021-12-31 18:46:13 +01002
Aaron Durbindd4a6d22013-02-27 22:50:12 -06003#include <assert.h>
Kyösti Mälkkiae98e832014-11-28 11:24:19 +02004#include <cbmem.h>
Aaron Durbin67514a72015-03-26 21:04:18 -05005#include <cbfs.h>
Aaron Durbinad935522012-12-24 14:28:37 -06006#include <string.h>
7#include <console/console.h>
Ionela Voinescu00903e52015-01-09 13:14:20 +00008#include <program_loading.h>
Aaron Durbinad935522012-12-24 14:28:37 -06009#include <rmodule.h>
Elyes HAOUAS93a195c2021-12-31 18:46:13 +010010#include <types.h>
Aaron Durbinad935522012-12-24 14:28:37 -060011
12/* Change this define to get more verbose debugging for module loading. */
13#define PK_ADJ_LEVEL BIOS_NEVER
14
Julius Werner81dc20e2020-10-15 17:37:57 -070015const size_t region_alignment = MIN_UNSAFE(DYN_CBMEM_ALIGN_SIZE, 4096);
16
Aaron Durbinad935522012-12-24 14:28:37 -060017static 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. */
23static inline void *rmodule_load_addr(const struct rmodule *module,
Lee Leahye20a3192017-03-09 16:21:34 -080024 uintptr_t blob_addr)
Aaron Durbinad935522012-12-24 14:28:37 -060025{
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. */
31int 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 -
Lee Leahye20a3192017-03-09 16:21:34 -080057 rhdr->payload_begin_offset;
Aaron Durbinad935522012-12-24 14:28:37 -060058 module->relocations = &base[rhdr->relocations_begin_offset];
59
60 return 0;
61}
62
63int rmodule_memory_size(const struct rmodule *module)
64{
65 return module->header->module_program_size;
66}
67
68void *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
80int rmodule_entry_offset(const struct rmodule *module)
81{
82 return module->header->module_entry_point -
83 module->header->module_link_start_address;
84}
85
86void *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
94static 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
Aaron Durbin3eb8eb72014-03-10 16:13:58 -0500104static inline size_t rmodule_number_relocations(const struct rmodule *module)
Aaron Durbinad935522012-12-24 14:28:37 -0600105{
Aaron Durbin3eb8eb72014-03-10 16:13:58 -0500106 size_t r;
Aaron Durbinad935522012-12-24 14:28:37 -0600107
108 r = module->header->relocations_end_offset;
109 r -= module->header->relocations_begin_offset;
Aaron Durbin3eb8eb72014-03-10 16:13:58 -0500110 r /= sizeof(uintptr_t);
Aaron Durbinad935522012-12-24 14:28:37 -0600111 return r;
112}
113
114static 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));
Aaron Durbine8c866a2013-02-08 17:05:36 -0600120
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
Aaron Durbinad935522012-12-24 14:28:37 -0600126 memcpy(module->location, module->payload, module->payload_size);
127}
128
Aaron Durbinad935522012-12-24 14:28:37 -0600129static int rmodule_relocate(const struct rmodule *module)
130{
Aaron Durbin3eb8eb72014-03-10 16:13:58 -0500131 size_t num_relocations;
132 const uintptr_t *reloc;
133 uintptr_t adjustment;
Aaron Durbinad935522012-12-24 14:28:37 -0600134
135 /* Each relocation needs to be adjusted relative to the beginning of
136 * the loaded program. */
Aaron Durbin3eb8eb72014-03-10 16:13:58 -0500137 adjustment = (uintptr_t)rmodule_load_addr(module, 0);
Aaron Durbinad935522012-12-24 14:28:37 -0600138
139 reloc = module->relocations;
140 num_relocations = rmodule_number_relocations(module);
141
Furquan Shaikh7a3c3492014-07-22 10:59:28 -0700142 printk(BIOS_DEBUG, "Processing %zu relocs. Offset value of 0x%08lx\n",
143 num_relocations, (unsigned long)adjustment);
Aaron Durbinad935522012-12-24 14:28:37 -0600144
145 while (num_relocations > 0) {
Aaron Durbin3eb8eb72014-03-10 16:13:58 -0500146 uintptr_t *adjust_loc;
Aaron Durbinad935522012-12-24 14:28:37 -0600147
148 /* If the adjustment location is non-NULL adjust it. */
Aaron Durbin3eb8eb72014-03-10 16:13:58 -0500149 adjust_loc = rmodule_load_addr(module, *reloc);
Furquan Shaikh7a3c3492014-07-22 10:59:28 -0700150 printk(PK_ADJ_LEVEL, "Adjusting %p: 0x%08lx -> 0x%08lx\n",
151 adjust_loc, (unsigned long) *adjust_loc,
Furquan Shaikhae9cd012014-07-23 04:44:09 -0700152 (unsigned long) (*adjust_loc + adjustment));
Kyösti Mälkkic3bc6cb2018-06-25 18:51:05 +0300153 *adjust_loc += adjustment;
Aaron Durbinad935522012-12-24 14:28:37 -0600154
Aaron Durbin3eb8eb72014-03-10 16:13:58 -0500155 reloc++;
Aaron Durbinad935522012-12-24 14:28:37 -0600156 num_relocations--;
157 }
158
159 return 0;
160}
161
162int 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
Aaron Durbin3eb8eb72014-03-10 16:13:58 -0500167 * in the program is preserved. Default to 4KiB. */
168 return 4096;
Aaron Durbinad935522012-12-24 14:28:37 -0600169}
170
171int 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.
Aaron Durbin55ed3102013-03-01 17:00:39 -0600177 * 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.
Aaron Durbinad935522012-12-24 14:28:37 -0600181 */
182 module->location = base;
183 rmodule_copy_payload(module);
Aaron Durbin55ed3102013-03-01 17:00:39 -0600184 if (rmodule_relocate(module))
185 return -1;
Aaron Durbinad935522012-12-24 14:28:37 -0600186 rmodule_clear_bss(module);
Aaron Durbinf72f9e72014-03-25 15:31:00 -0500187
Aaron Durbin096f4572016-03-31 13:49:00 -0500188 prog_segment_loaded((uintptr_t)module->location,
Aaron Durbin6e76fff2015-03-20 09:42:05 -0500189 rmodule_memory_size(module), SEG_FINAL);
Aaron Durbinf72f9e72014-03-25 15:31:00 -0500190
Aaron Durbin55ed3102013-03-01 17:00:39 -0600191 return 0;
Aaron Durbinad935522012-12-24 14:28:37 -0600192}
193
Julius Werner81dc20e2020-10-15 17:37:57 -0700194static void *rmodule_cbfs_allocator(void *rsl_arg, size_t unused,
Julius Werner4676ec52021-03-10 16:52:14 -0800195 const union cbfs_mdata *mdata)
Aaron Durbine8c866a2013-02-08 17:05:36 -0600196{
Julius Werner81dc20e2020-10-15 17:37:57 -0700197 struct rmod_stage_load *rsl = rsl_arg;
Aaron Durbine8c866a2013-02-08 17:05:36 -0600198
Julius Werner81dc20e2020-10-15 17:37:57 -0700199 assert(IS_POWER_OF_2(region_alignment) &&
200 region_alignment >= sizeof(struct rmodule_header));
Aaron Durbindd4a6d22013-02-27 22:50:12 -0600201
Julius Werner81dc20e2020-10-15 17:37:57 -0700202 /* 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);
Aaron Durbindd4a6d22013-02-27 22:50:12 -0600214
215 /* Place the rmodule according to alignment. The rmodule files
Aaron Durbine8c866a2013-02-08 17:05:36 -0600216 * 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. */
Elyes Haouasd6b6b222022-10-10 12:34:21 +0200225 size_t region_size = ALIGN_UP(memlen + region_alignment, 4096);
Aaron Durbine8c866a2013-02-08 17:05:36 -0600226 /* 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 *
Aaron Durbindd4a6d22013-02-27 22:50:12 -0600231 * +--------------------------------+ region_alignment + region_size
Aaron Durbine8c866a2013-02-08 17:05:36 -0600232 * | >= 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 |
Aaron Durbindd4a6d22013-02-27 22:50:12 -0600239 * +--------------------------------+ region_alignment
Aaron Durbine8c866a2013-02-08 17:05:36 -0600240 */
Aaron Durbine8c866a2013-02-08 17:05:36 -0600241
Julius Werner81dc20e2020-10-15 17:37:57 -0700242 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);
Aaron Durbine8c866a2013-02-08 17:05:36 -0600247}
Aaron Durbinf545abf2013-10-24 10:14:06 -0500248
Aaron Durbin899d13d2015-05-15 23:39:23 -0500249int rmodule_stage_load(struct rmod_stage_load *rsl)
Aaron Durbinf545abf2013-10-24 10:14:06 -0500250{
251 struct rmodule rmod_stage;
Aaron Durbinf545abf2013-10-24 10:14:06 -0500252
Aaron Durbinac12c66c2015-05-20 12:08:55 -0500253 if (rsl->prog == NULL || prog_name(rsl->prog) == NULL)
Aaron Durbin899d13d2015-05-15 23:39:23 -0500254 return -1;
255
Julius Werner1de87082020-12-23 17:38:11 -0800256 if (prog_locate_hook(rsl->prog))
257 return -1;
Aaron Durbin899d13d2015-05-15 23:39:23 -0500258
Julius Werner81dc20e2020-10-15 17:37:57 -0700259 void *rmod_loc = cbfs_alloc(prog_name(rsl->prog),
260 rmodule_cbfs_allocator, rsl, NULL);
261 if (!rmod_loc)
Aaron Durbinf545abf2013-10-24 10:14:06 -0500262 return -1;
263
Aaron Durbin899d13d2015-05-15 23:39:23 -0500264 if (rmodule_parse(rmod_loc, &rmod_stage))
Aaron Durbinf545abf2013-10-24 10:14:06 -0500265 return -1;
266
Julius Werner81dc20e2020-10-15 17:37:57 -0700267 if (rmodule_load(rmod_loc + sizeof(struct rmodule_header), &rmod_stage))
Aaron Durbinf545abf2013-10-24 10:14:06 -0500268 return -1;
269
Aaron Durbin7ca65222015-04-06 17:18:18 -0500270 prog_set_area(rsl->prog, rmod_stage.location,
271 rmodule_memory_size(&rmod_stage));
Aaron Durbinf545abf2013-10-24 10:14:06 -0500272
Aaron Durbin94271b42016-03-17 23:13:34 -0500273 /* Allow caller to pick up parameters, if available. */
274 rsl->params = rmodule_parameters(&rmod_stage);
275
Kyösti Mälkkid87e4b32017-09-05 22:43:05 +0300276 prog_set_entry(rsl->prog, rmodule_entry(&rmod_stage), rsl->params);
277
Aaron Durbinf545abf2013-10-24 10:14:06 -0500278 return 0;
279}