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 }