1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Decompression and self-relocation for the compressed kernel boot stub, 4 * based on arch/parisc/boot/compressed/misc.c. 5 * 6 * aboot loads the stub as an ordinary ELF at START_ADDR and jumps to it 7 * with the parameter page and stack already set up; vmlinux.lds.S 8 * explains why the stub has to be linked there. We decompress above our 9 * own bss and never write below START_ADDR, so what aboot prepared stays 10 * valid for the real kernel. 11 */ 12 13 #include <linux/elf.h> 14 #include <linux/kernel.h> 15 #include <linux/string.h> 16 #include <linux/types.h> 17 18 #include <asm/hwrpb.h> 19 #include <asm/page.h> 20 #include <asm/pal.h> 21 #include <asm/setup.h> 22 23 #define STATIC static 24 #define memzero(s, n) memset((s), 0, (n)) 25 26 /* 27 * decompress_unxz.c supplies its own memmove() unless the identifier is 28 * already a macro. We link our own build of arch/alpha/lib/memmove.S, 29 * which is better, so take that instead. 30 */ 31 #define memmove memmove 32 33 extern char input_data[]; 34 extern int input_len; 35 /* output_len is inserted by the linker, possibly at an unaligned address */ 36 extern char output_len; 37 38 extern char _ebss[]; 39 extern char trampoline_start[], trampoline_end[]; 40 41 static unsigned long free_mem_ptr; 42 static unsigned long free_mem_end_ptr; 43 static unsigned long mem_end; 44 45 /* The stub has no console, so the message is only for debuggers. */ 46 void __noreturn error(char *m) 47 { 48 while (1) 49 __halt(); 50 } 51 52 #ifdef CONFIG_KERNEL_BZIP2 53 #include "../../../../lib/decompress_bunzip2.c" 54 #endif 55 56 #ifdef CONFIG_KERNEL_GZIP 57 #include "../../../../lib/decompress_inflate.c" 58 #endif 59 60 #ifdef CONFIG_KERNEL_LZ4 61 #include "../../../../lib/decompress_unlz4.c" 62 #endif 63 64 #ifdef CONFIG_KERNEL_LZMA 65 #include "../../../../lib/decompress_unlzma.c" 66 #endif 67 68 #ifdef CONFIG_KERNEL_LZO 69 #include "../../../../lib/decompress_unlzo.c" 70 #endif 71 72 #ifdef CONFIG_KERNEL_XZ 73 #include "../../../../lib/decompress_unxz.c" 74 #endif 75 76 #ifdef CONFIG_KERNEL_ZSTD 77 #include "../../../../lib/decompress_unzstd.c" 78 #endif 79 80 /* Scratch heap for the decompressors, larger than any of them needs. */ 81 #define STUB_HEAP_SIZE (4 * 1024 * 1024) 82 83 /* 84 * Type punning through a packed struct, to avoid the warnings the regular 85 * get_unaligned_le32() produces when reading a char-typed symbol as an 86 * integer. 87 */ 88 static u32 punned_get_unaligned_le32(const void *p) 89 { 90 const struct { __le32 x; } __packed *pp = p; 91 92 return le32_to_cpu(pp->x); 93 } 94 95 /* 96 * End of the usable memory cluster that holds the kernel, or 0 if the 97 * HWRPB does not describe one. Used to check that the decompressed image 98 * and the stub's scratch space fit before anything is written. 99 */ 100 static unsigned long usable_memory_end(void) 101 { 102 struct hwrpb_struct *hwrpb; 103 struct memdesc_struct *memdesc; 104 unsigned long pfn = (START_ADDR - PAGE_OFFSET) >> PAGE_SHIFT; 105 unsigned long i; 106 107 hwrpb = (struct hwrpb_struct *)(PAGE_OFFSET + INIT_HWRPB->phys_addr); 108 memdesc = (struct memdesc_struct *)((unsigned long)hwrpb + 109 hwrpb->mddt_offset); 110 111 for (i = 0; i < memdesc->numclusters; i++) { 112 struct memclust_struct *cluster = &memdesc->cluster[i]; 113 unsigned long end_pfn = cluster->start_pfn + cluster->numpages; 114 115 if (cluster->usage) 116 continue; 117 if (pfn >= cluster->start_pfn && pfn < end_pfn) 118 return PAGE_OFFSET + (end_pfn << PAGE_SHIFT); 119 } 120 121 return 0; 122 } 123 124 /* 125 * Move the decompressed kernel's single PT_LOAD segment down onto 126 * START_ADDR and jump to its entry point. The kernel is always linked 127 * with exactly one PT_LOAD whose p_vaddr equals its e_entry equals 128 * START_ADDR (see arch/alpha/kernel/vmlinux.lds.S); anything else we do 129 * not know how to boot. 130 * 131 * The copy cannot run from this stub, which is itself executing at 132 * START_ADDR; see trampoline.S. 133 */ 134 static void __noreturn relocate_and_jump(void *image) 135 { 136 void (*trampoline)(unsigned long dest, unsigned long src, 137 unsigned long filesz, unsigned long memsz, 138 unsigned long entry); 139 unsigned long tramp_len = trampoline_end - trampoline_start; 140 Elf64_Phdr phdr, load_phdr = {}; 141 Elf64_Ehdr ehdr; 142 int i, found_load = 0; 143 unsigned long tramp; 144 145 memcpy(&ehdr, image, sizeof(ehdr)); 146 147 if (ehdr.e_ident[EI_MAG0] != ELFMAG0 || 148 ehdr.e_ident[EI_MAG1] != ELFMAG1 || 149 ehdr.e_ident[EI_MAG2] != ELFMAG2 || 150 ehdr.e_ident[EI_MAG3] != ELFMAG3 || 151 ehdr.e_ident[EI_CLASS] != ELFCLASS64 || 152 ehdr.e_ident[EI_DATA] != ELFDATA2LSB || 153 ehdr.e_machine != EM_ALPHA || 154 ehdr.e_type != ET_EXEC || 155 ehdr.e_entry != START_ADDR) 156 error("Not a bootable Alpha kernel image"); 157 158 for (i = 0; i < ehdr.e_phnum; i++) { 159 memcpy(&phdr, image + ehdr.e_phoff + i * ehdr.e_phentsize, 160 sizeof(phdr)); 161 162 if (phdr.p_type != PT_LOAD) 163 continue; 164 if (found_load || phdr.p_vaddr != START_ADDR) 165 error("Unexpected kernel segment layout"); 166 found_load = 1; 167 load_phdr = phdr; 168 } 169 if (!found_load) 170 error("Kernel image has no PT_LOAD segment"); 171 172 /* 173 * The trampoline has to outlive both the scratch heap it is copied 174 * into and the region it is about to write, which extends past the 175 * kernel's file image to the end of its bss. 176 */ 177 tramp = ALIGN(max(free_mem_end_ptr, START_ADDR + load_phdr.p_memsz), 8); 178 if (mem_end && tramp + tramp_len > mem_end) 179 error("Not enough memory to relocate the kernel"); 180 181 memcpy((void *)tramp, trampoline_start, tramp_len); 182 imb(); 183 184 trampoline = (void *)tramp; 185 trampoline(load_phdr.p_vaddr, 186 (unsigned long)image + load_phdr.p_offset, 187 load_phdr.p_filesz, load_phdr.p_memsz, ehdr.e_entry); 188 unreachable(); 189 } 190 191 void __noreturn decompress_and_boot(void) 192 { 193 unsigned long image_len = punned_get_unaligned_le32(&output_len); 194 void *image = (void *)ALIGN((unsigned long)_ebss, PAGE_SIZE); 195 196 free_mem_ptr = ALIGN((unsigned long)image + image_len, PAGE_SIZE); 197 free_mem_end_ptr = free_mem_ptr + STUB_HEAP_SIZE; 198 199 mem_end = usable_memory_end(); 200 if (mem_end && free_mem_end_ptr > mem_end) 201 error("Not enough memory to decompress the kernel"); 202 203 /* Some decompressors report failure only through the return value. */ 204 if (__decompress(input_data, input_len, NULL, NULL, image, image_len, 205 NULL, error)) 206 error("Kernel decompression failed"); 207 208 relocate_and_jump(image); 209 } 210