blob: f8ae16af5d5742718b8d4239c2fc46f2e3930abd [file] [log] [blame]
Patrick Georgi0a3d4e02020-03-04 14:39:09 +01001/* SPDX-License-Identifier: GPL-2.0-or-later */
Patrick Rudolpha892cde2018-04-19 14:39:07 +02002
Julius Werner98eeb962019-12-11 15:47:42 -08003#include <commonlib/bsd/compression.h>
Patrick Rudolpha892cde2018-04-19 14:39:07 +02004#include <console/console.h>
5#include <bootmem.h>
Patrick Rudolpha892cde2018-04-19 14:39:07 +02006#include <program_loading.h>
7#include <string.h>
Patrick Rudolpha892cde2018-04-19 14:39:07 +02008#include <lib.h>
9#include <fit.h>
10#include <endian.h>
11
12#define MAX_KERNEL_SIZE (64*MiB)
13
14struct 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
28static 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. */
38static 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:
Julius Wernere9665952022-01-21 17:06:20 -080055 printk(BIOS_ERR, "Unsupported compression algorithm!\n");
Patrick Rudolpha892cde2018-04-19 14:39:07 +020056 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) {
Julius Wernere9665952022-01-21 17:06:20 -080064 printk(BIOS_ERR, "Invalid kernel magic: %#.8x\n != %#.8x\n",
Patrick Rudolpha892cde2018-04-19 14:39:07 +020065 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
82static 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
101static 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 */
144static 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
163bool 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
Julius Wernerb379f192019-05-13 16:34:16 -0700171 if (!decompress_kernel_header(config->kernel)) {
Elyes Haouas51c31182022-11-13 08:02:41 +0100172 printk(BIOS_CRIT, "Payload doesn't look like an ARM64"
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200173 " kernel Image.\n");
174 return false;
175 }
176
177 /* Update kernel size from image header, if possible */
Julius Wernerb379f192019-05-13 16:34:16 -0700178 kernel->size = get_kernel_size(config->kernel);
Patrick Rudolpha892cde2018-04-19 14:39:07 +0200179 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}