xref: /linux/arch/alpha/boot/compressed/misc.c (revision 7fa247f96315d00511615bd9a36dca3f3906c621)
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