xref: /aosp_15_r20/external/coreboot/src/lib/rmodule.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
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 
15 const size_t region_alignment = MIN_UNSAFE(DYN_CBMEM_ALIGN_SIZE, 4096);
16 
rmodule_is_loaded(const struct rmodule * module)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. */
rmodule_load_addr(const struct rmodule * module,uintptr_t blob_addr)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. */
rmodule_parse(void * ptr,struct rmodule * module)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 -
57 				rhdr->payload_begin_offset;
58 	module->relocations = &base[rhdr->relocations_begin_offset];
59 
60 	return 0;
61 }
62 
rmodule_memory_size(const struct rmodule * module)63 int rmodule_memory_size(const struct rmodule *module)
64 {
65 	return module->header->module_program_size;
66 }
67 
rmodule_parameters(const struct rmodule * module)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 
rmodule_entry_offset(const struct rmodule * module)80 int rmodule_entry_offset(const struct rmodule *module)
81 {
82 	return module->header->module_entry_point -
83 	       module->header->module_link_start_address;
84 }
85 
rmodule_entry(const struct rmodule * module)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 
rmodule_clear_bss(struct rmodule * module)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 
rmodule_number_relocations(const struct rmodule * module)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 
rmodule_copy_payload(const struct rmodule * module)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 
rmodule_relocate(const struct rmodule * module)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 
rmodule_load_alignment(const struct rmodule * module)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 
rmodule_load(void * base,struct rmodule * module)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 
188 	prog_segment_loaded((uintptr_t)module->location,
189 				rmodule_memory_size(module), SEG_FINAL);
190 
191 	return 0;
192 }
193 
rmodule_cbfs_allocator(void * rsl_arg,size_t unused,const union cbfs_mdata * mdata)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 
199 	assert(IS_POWER_OF_2(region_alignment) &&
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_UP(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 
rmodule_stage_load(struct rmod_stage_load * rsl)249 int rmodule_stage_load(struct rmod_stage_load *rsl)
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),
260 				    rmodule_cbfs_allocator, rsl, NULL);
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 }
280