xref: /aosp_15_r20/external/coreboot/src/arch/arm64/fit_payload.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
3 #include <commonlib/bsd/compression.h>
4 #include <console/console.h>
5 #include <bootmem.h>
6 #include <program_loading.h>
7 #include <string.h>
8 #include <lib.h>
9 #include <fit.h>
10 #include <endian.h>
11 
12 #define MAX_KERNEL_SIZE (64*MiB)
13 
14 struct arm64_kernel_header {
15 	u32 code0;
16 	u32 code1;
17 	u64 text_offset;
18 	u64 image_size;
19 	u64 flags;
20 	u64 res2;
21 	u64 res3;
22 	u64 res4;
23 	u32 magic;
24 #define KERNEL_HEADER_MAGIC  0x644d5241
25 	u32 res5;
26 };
27 
28 static struct {
29 	union {
30 		struct arm64_kernel_header header;
31 		u8 raw[sizeof(struct arm64_kernel_header) + 0x100];
32 	};
33 #define SCRATCH_CANARY_VALUE 0xdeadbeef
34 	u32 canary;
35 } scratch;
36 
37 /* Returns true if decompressing was successful and it looks like a kernel. */
decompress_kernel_header(const struct fit_image_node * node)38 static bool decompress_kernel_header(const struct fit_image_node *node)
39 {
40 	/* Partially decompress to get text_offset. Can't check for errors. */
41 	scratch.canary = SCRATCH_CANARY_VALUE;
42 	switch (node->compression) {
43 	case CBFS_COMPRESS_NONE:
44 		memcpy(scratch.raw, node->data, sizeof(scratch.raw));
45 		break;
46 	case CBFS_COMPRESS_LZMA:
47 		ulzman(node->data, node->size,
48 		       scratch.raw, sizeof(scratch.raw));
49 		break;
50 	case CBFS_COMPRESS_LZ4:
51 		ulz4fn(node->data, node->size,
52 		       scratch.raw, sizeof(scratch.raw));
53 		break;
54 	default:
55 		printk(BIOS_ERR, "Unsupported compression algorithm!\n");
56 		return false;
57 	}
58 
59 	/* Should never happen, but if it does we'll want to know. */
60 	if (scratch.canary != SCRATCH_CANARY_VALUE)
61 		die("ERROR: Partial decompression ran over scratchbuf!\n");
62 
63 	if (scratch.header.magic != KERNEL_HEADER_MAGIC) {
64 		printk(BIOS_ERR, "Invalid kernel magic: %#.8x\n != %#.8x\n",
65 		       scratch.header.magic, KERNEL_HEADER_MAGIC);
66 		return false;
67 	}
68 
69 	/**
70 	 * Prior to v3.17, the endianness of text_offset was not specified.  In
71 	 * these cases image_size is zero and text_offset is 0x80000 in the
72 	 * endianness of the kernel.  Where image_size is non-zero image_size is
73 	 * little-endian and must be respected.  Where image_size is zero,
74 	 * text_offset can be assumed to be 0x80000.
75 	 */
76 	if (!scratch.header.image_size)
77 		scratch.header.text_offset = cpu_to_le64(0x80000);
78 
79 	return true;
80 }
81 
get_kernel_size(const struct fit_image_node * node)82 static size_t get_kernel_size(const struct fit_image_node *node)
83 {
84 	if (scratch.header.image_size)
85 		return le64_to_cpu(scratch.header.image_size);
86 
87 	/**
88 	 * When image_size is zero, a bootloader should attempt to keep as much
89 	 * memory as possible free for use by the kernel immediately after the
90 	 * end of the kernel image. The amount of space required will vary
91 	 * depending on selected features, and is effectively unbound.
92 	 */
93 
94 	printk(BIOS_WARNING, "FIT: image_size not set in kernel header.\n"
95 	       "Leaving additional %u MiB of free space after kernel.\n",
96 	       MAX_KERNEL_SIZE >> 20);
97 
98 	return node->size + MAX_KERNEL_SIZE;
99 }
100 
fit_place_kernel(const struct range_entry * r,void * arg)101 static bool fit_place_kernel(const struct range_entry *r, void *arg)
102 {
103 	struct region *region = arg;
104 	resource_t start;
105 
106 	if (range_entry_tag(r) != BM_MEM_RAM)
107 		return true;
108 
109 	/**
110 	 * The Image must be placed text_offset bytes from a 2MB aligned base
111 	 * address anywhere in usable system RAM and called there. The region
112 	 * between the 2 MB aligned base address and the start of the image has
113 	 * no special significance to the kernel, and may be used for other
114 	 * purposes.
115 	 *
116 	 * If the reserved memory (BL31 for example) is smaller than text_offset
117 	 * we can use the 2 MiB base address, otherwise use the next 2 MiB page.
118 	 * It's not mandatory, but wastes less memory below the kernel.
119 	 */
120 	start = ALIGN_DOWN(range_entry_base(r), 2 * MiB) +
121 		le64_to_cpu(scratch.header.text_offset);
122 
123 	if (start < range_entry_base(r))
124 		start += 2 * MiB;
125 	/**
126 	 * At least image_size bytes from the start of the image must be free
127 	 * for use by the kernel.
128 	 */
129 	if (start + region->size < range_entry_end(r)) {
130 		region->offset = (size_t)start;
131 		return false;
132 	}
133 
134 	return true;
135 }
136 
137 /**
138  * Place the region in free memory range.
139  *
140  * The caller has to set region->offset to the minimum allowed address.
141  * The region->offset is usually 0 on kernel >v4.6 and kernel_base + kernel_size
142  * on kernel <v4.6.
143  */
fit_place_mem(const struct range_entry * r,void * arg)144 static bool fit_place_mem(const struct range_entry *r, void *arg)
145 {
146 	struct region *region = arg;
147 	resource_t start;
148 
149 	if (range_entry_tag(r) != BM_MEM_RAM)
150 		return true;
151 
152 	/* Linux 4.15 doesn't like 4KiB alignment. Align to 1 MiB for now. */
153 	start = ALIGN_UP(MAX(region->offset, range_entry_base(r)), 1 * MiB);
154 
155 	if (start + region->size < range_entry_end(r)) {
156 		region->offset = (size_t)start;
157 		return false;
158 	}
159 
160 	return true;
161 }
162 
fit_payload_arch(struct prog * payload,struct fit_config_node * config,struct region * kernel,struct region * fdt,struct region * initrd)163 bool fit_payload_arch(struct prog *payload, struct fit_config_node *config,
164 		      struct region *kernel,
165 		      struct region *fdt,
166 		      struct region *initrd)
167 {
168 	bool place_anywhere;
169 	void *arg = NULL;
170 
171 	if (!decompress_kernel_header(config->kernel)) {
172 		printk(BIOS_CRIT, "Payload doesn't look like an ARM64"
173 		       " kernel Image.\n");
174 		return false;
175 	}
176 
177 	/* Update kernel size from image header, if possible */
178 	kernel->size = get_kernel_size(config->kernel);
179 	printk(BIOS_DEBUG, "FIT: Using kernel size of 0x%zx bytes\n",
180 	       kernel->size);
181 
182 	/**
183 	 * The code assumes that bootmem_walk provides a sorted list of memory
184 	 * regions, starting from the lowest address.
185 	 * The order of the calls here doesn't matter, as the placement is
186 	 * enforced in the called functions.
187 	 * For details check code on top.
188 	 */
189 
190 	if (!bootmem_walk(fit_place_kernel, kernel))
191 		return false;
192 
193 	/* Mark as reserved for future allocations. */
194 	bootmem_add_range(kernel->offset, kernel->size, BM_MEM_PAYLOAD);
195 
196 	/**
197 	 * NOTE: versions prior to v4.6 cannot make use of memory below the
198 	 * physical offset of the Image so it is recommended that the Image be
199 	 * placed as close as possible to the start of system RAM.
200 	 *
201 	 * For kernel <v4.6 the INITRD and FDT can't be placed below the kernel.
202 	 * In that case set region offset to an address on top of kernel.
203 	 */
204 	place_anywhere = !!(le64_to_cpu(scratch.header.flags) & (1 << 3));
205 	printk(BIOS_DEBUG, "FIT: Placing FDT and INITRD %s\n",
206 	       place_anywhere ? "anywhere" : "on top of kernel");
207 
208 	/* Place INITRD */
209 	if (config->ramdisk) {
210 		if (place_anywhere)
211 			initrd->offset = 0;
212 		else
213 			initrd->offset = kernel->offset + kernel->size;
214 
215 		if (!bootmem_walk(fit_place_mem, initrd))
216 			return false;
217 		/* Mark as reserved for future allocations. */
218 		bootmem_add_range(initrd->offset, initrd->size, BM_MEM_PAYLOAD);
219 	}
220 
221 	/* Place FDT */
222 	if (place_anywhere)
223 		fdt->offset = 0;
224 	else
225 		fdt->offset = kernel->offset + kernel->size;
226 
227 	if (!bootmem_walk(fit_place_mem, fdt))
228 		return false;
229 	/* Mark as reserved for future allocations. */
230 	bootmem_add_range(fdt->offset, fdt->size, BM_MEM_PAYLOAD);
231 
232 	/* Kernel expects FDT as argument */
233 	arg = (void *)fdt->offset;
234 
235 	prog_set_entry(payload, (void *)kernel->offset, arg);
236 
237 	bootmem_dump_ranges();
238 
239 	return true;
240 }
241