/ src / lib / rmodule.c
rmodule.c
  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  
 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 -
 57  				rhdr->payload_begin_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 -
 83  	       module->header->module_link_start_address;
 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  
188  	prog_segment_loaded((uintptr_t)module->location,
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  
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  
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  }