@@ -271,6 +271,79 @@ static void error(char *x)
271271 asm("hlt" );
272272}
273273
274+ #if CONFIG_X86_NEED_RELOCS
275+ static void handle_relocations (void * output , unsigned long output_len )
276+ {
277+ int * reloc ;
278+ unsigned long delta , map , ptr ;
279+ unsigned long min_addr = (unsigned long )output ;
280+ unsigned long max_addr = min_addr + output_len ;
281+
282+ /*
283+ * Calculate the delta between where vmlinux was linked to load
284+ * and where it was actually loaded.
285+ */
286+ delta = min_addr - LOAD_PHYSICAL_ADDR ;
287+ if (!delta ) {
288+ debug_putstr ("No relocation needed... " );
289+ return ;
290+ }
291+ debug_putstr ("Performing relocations... " );
292+
293+ /*
294+ * The kernel contains a table of relocation addresses. Those
295+ * addresses have the final load address of the kernel in virtual
296+ * memory. We are currently working in the self map. So we need to
297+ * create an adjustment for kernel memory addresses to the self map.
298+ * This will involve subtracting out the base address of the kernel.
299+ */
300+ map = delta - __START_KERNEL_map ;
301+
302+ /*
303+ * Process relocations: 32 bit relocations first then 64 bit after.
304+ * Two sets of binary relocations are added to the end of the kernel
305+ * before compression. Each relocation table entry is the kernel
306+ * address of the location which needs to be updated stored as a
307+ * 32-bit value which is sign extended to 64 bits.
308+ *
309+ * Format is:
310+ *
311+ * kernel bits...
312+ * 0 - zero terminator for 64 bit relocations
313+ * 64 bit relocation repeated
314+ * 0 - zero terminator for 32 bit relocations
315+ * 32 bit relocation repeated
316+ *
317+ * So we work backwards from the end of the decompressed image.
318+ */
319+ for (reloc = output + output_len - sizeof (* reloc ); * reloc ; reloc -- ) {
320+ int extended = * reloc ;
321+ extended += map ;
322+
323+ ptr = (unsigned long )extended ;
324+ if (ptr < min_addr || ptr > max_addr )
325+ error ("32-bit relocation outside of kernel!\n" );
326+
327+ * (uint32_t * )ptr += delta ;
328+ }
329+ #ifdef CONFIG_X86_64
330+ for (reloc -- ; * reloc ; reloc -- ) {
331+ long extended = * reloc ;
332+ extended += map ;
333+
334+ ptr = (unsigned long )extended ;
335+ if (ptr < min_addr || ptr > max_addr )
336+ error ("64-bit relocation outside of kernel!\n" );
337+
338+ * (uint64_t * )ptr += delta ;
339+ }
340+ #endif
341+ }
342+ #else
343+ static inline void handle_relocations (void * output , unsigned long output_len )
344+ { }
345+ #endif
346+
274347static void parse_elf (void * output )
275348{
276349#ifdef CONFIG_X86_64
@@ -325,7 +398,8 @@ static void parse_elf(void *output)
325398asmlinkage void decompress_kernel (void * rmode , memptr heap ,
326399 unsigned char * input_data ,
327400 unsigned long input_len ,
328- unsigned char * output )
401+ unsigned char * output ,
402+ unsigned long output_len )
329403{
330404 real_mode = rmode ;
331405
@@ -365,6 +439,7 @@ asmlinkage void decompress_kernel(void *rmode, memptr heap,
365439 debug_putstr ("\nDecompressing Linux... " );
366440 decompress (input_data , input_len , NULL , NULL , output , NULL , error );
367441 parse_elf (output );
442+ handle_relocations (output , output_len );
368443 debug_putstr ("done.\nBooting the kernel.\n" );
369444 return ;
370445}
0 commit comments