treewide: Move device_tree to commonlib
[coreboot2.git] / util / cbfstool / cbfs-payload-linux.c
blob53d455cfee53c0ca98e7381159e7fee2c125b54f
1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
7 #include "common.h"
8 #include "cbfs.h"
9 #include "linux.h"
11 /* trampoline */
12 extern unsigned char trampoline[];
13 extern unsigned int trampoline_len;
16 * Current max number of segments include:
18 * 1. parameters
19 * 2. kernel
20 * 3. trampoline
21 * 4. optional cmdline
22 * 5. optional initrd
23 * 6. terminating entry segment
25 #define MAX_NUM_SEGMENTS 6
27 struct bzpayload {
28 /* Input variables. */
29 int num_segments;
30 struct cbfs_payload_segment segs[MAX_NUM_SEGMENTS];
31 struct buffer parameters;
32 struct buffer kernel;
33 struct buffer trampoline;
34 struct buffer cmdline;
35 struct buffer initrd;
36 /* Output variables. */
37 enum cbfs_compression algo;
38 comp_func_ptr compress;
39 struct buffer output;
40 size_t offset;
41 struct cbfs_payload_segment *out_seg;
44 static int bzp_init(struct bzpayload *bzp, enum cbfs_compression algo)
46 memset(bzp, 0, sizeof(*bzp));
49 * Need at least the terminating entry segment.
51 bzp->num_segments = 1;
53 bzp->algo = algo;
54 bzp->compress = compression_function(algo);
55 if (bzp->compress == NULL) {
56 ERROR("Invalid compression algorithm specified.\n");
57 return -1;
60 return 0;
63 static int bzp_add_initrd(struct bzpayload *bzp, const char *fname)
65 if (fname == NULL)
66 return 0;
68 if (buffer_from_file(&bzp->initrd, fname)) {
69 ERROR("could not open initrd.\n");
70 return -1;
73 bzp->num_segments++;
75 return 0;
78 static void bzp_add_segment(struct bzpayload *bzp, struct buffer *b, void *data,
79 size_t size)
81 buffer_init(b, NULL, data, size);
82 bzp->num_segments++;
85 static int bzp_add_trampoline(struct bzpayload *bzp)
87 bzp_add_segment(bzp, &bzp->trampoline, trampoline,
88 trampoline_len);
89 return 0;
92 static int bzp_add_cmdline(struct bzpayload *bzp, char *cmdline)
94 if (cmdline == NULL)
95 return 0;
97 bzp_add_segment(bzp, &bzp->cmdline, cmdline, strlen(cmdline) + 1);
99 return 0;
102 static int bzp_add_params(struct bzpayload *bzp, struct linux_params *params)
104 bzp_add_segment(bzp, &bzp->parameters, params, sizeof(*params));
106 return 0;
109 static int bzp_add_kernel(struct bzpayload *bzp, const struct buffer *in,
110 size_t setup_size)
112 char *input = buffer_get(in);
113 size_t kern_sz = buffer_size(in) - setup_size;
115 bzp_add_segment(bzp, &bzp->kernel, &input[setup_size], kern_sz);
117 return 0;
120 static int bzp_init_output(struct bzpayload *bzp, const char *name)
122 size_t sz = 0;
124 sz += buffer_size(&bzp->parameters);
125 sz += buffer_size(&bzp->kernel);
126 sz += buffer_size(&bzp->trampoline);
127 sz += buffer_size(&bzp->cmdline);
128 sz += buffer_size(&bzp->initrd);
130 bzp->offset = bzp->num_segments * sizeof(struct cbfs_payload_segment);
131 sz += bzp->offset;
133 if (buffer_create(&bzp->output, sz, name) != 0)
134 return -1;
136 bzp->out_seg = &bzp->segs[0];
138 return 0;
141 static void bzp_output_segment(struct bzpayload *bzp, struct buffer *b,
142 uint32_t type, uint64_t load_addr)
144 struct buffer out;
145 struct cbfs_payload_segment *seg;
146 int len = 0;
148 /* Don't process empty buffers. */
149 if (b != NULL && buffer_size(b) == 0)
150 return;
152 seg = bzp->out_seg;
153 seg->type = type;
154 seg->load_addr = load_addr;
155 bzp->out_seg++;
157 /* No buffer associated with segment. */
158 if (b == NULL)
159 return;
161 /* Use a temp buffer for easier management. */
162 buffer_splice(&out, &bzp->output, bzp->offset, buffer_size(b));
164 seg->mem_len = buffer_size(b);
165 seg->offset = bzp->offset;
166 bzp->compress(buffer_get(b), buffer_size(b), buffer_get(&out), &len);
167 seg->compression = bzp->algo;
168 seg->len = len;
170 /* Update output offset. */
171 bzp->offset += len;
174 /* TODO:
175 * handle special arguments
176 * mem= argument - only affects loading decisions (kernel + initrd), not e820 -> build time
177 * vga= argument (FILO ignores this)
178 * add support for more parameters to trampoline:
179 * alt_mem_k, ext_mem_k (not strictly necessary since e820 takes precedence)
180 * framebuffer/console values
182 * larger work:
183 * is compress() safe to use in a size constrained buffer? ie. do(es) the
184 * compression algorithm(s) stop once the compression result reaches input
185 * size (ie. incompressible data)?
187 int parse_bzImage_to_payload(const struct buffer *input,
188 struct buffer *output, const char *initrd_name,
189 char *cmdline, enum cbfs_compression algo)
191 struct bzpayload bzp;
192 unsigned int initrd_base = 64*1024*1024;
193 struct linux_header *hdr = (struct linux_header *)input->data;
194 unsigned int setup_size = 4 * 512;
196 if (bzp_init(&bzp, algo) != 0)
197 return -1;
199 if (bzp_add_trampoline(&bzp) != 0)
200 return -1;
202 if (bzp_add_initrd(&bzp, initrd_name) != 0)
203 return -1;
205 if (bzp_add_cmdline(&bzp, cmdline) != 0)
206 return -1;
208 if (hdr->setup_sects != 0) {
209 setup_size = (hdr->setup_sects + 1) * 512;
210 } else {
211 WARN("hdr->setup_sects is 0, which could cause boot problems.\n");
214 /* Setup parameter block. Imitate FILO. */
215 struct linux_params params;
217 memset(&params, 0, sizeof(struct linux_params));
219 params.mount_root_rdonly = hdr->root_flags;
220 params.orig_root_dev = hdr->root_dev;
221 params.init_size = hdr->init_size;
223 /* Sensible video defaults. Might be overridden on runtime by coreboot tables. */
224 params.orig_video_mode = 3;
225 params.orig_video_cols = 80;
226 params.orig_video_lines = 25;
227 params.orig_video_isVGA = 1;
228 params.orig_video_points = 16;
230 params.loader_type = 0xff; /* Unregistered Linux loader */
232 if (cmdline != NULL) {
233 if (hdr->protocol_version < 0x202) {
234 params.cl_magic = CL_MAGIC_VALUE;
235 params.cl_offset = COMMAND_LINE_LOC - LINUX_PARAM_LOC;
236 } else {
237 params.cmd_line_ptr = COMMAND_LINE_LOC;
241 unsigned long kernel_base = 0x100000;
242 if ((hdr->protocol_version < 0x200) || !(hdr->loadflags & 1)) {
243 kernel_base = 0x1000; /* zImage kernel */
245 /* kernel prefers an address, so listen */
246 if ((hdr->protocol_version >= 0x20a) && (!(hdr->pref_address >> 32))) {
247 kernel_base = hdr->pref_address;
249 if (hdr->protocol_version >= 0x205) {
250 params.relocatable_kernel = hdr->relocatable_kernel;
251 params.kernel_alignment = hdr->kernel_alignment;
252 if (hdr->relocatable_kernel != 0) {
253 /* 16 MB should be way outside coreboot's playground,
254 * so if possible (relocatable kernel) use that to
255 * avoid a trampoline copy. */
256 kernel_base = ALIGN_UP(16*1024*1024, params.kernel_alignment);
257 if (hdr->init_size == 0) {
258 ERROR("init_size 0 for relocatable kernel\n");
259 return -1;
264 /* We have a trampoline and use that, but it can simply use
265 * this information for its jump to real Linux. */
266 params.kernel_start = kernel_base;
268 /* To make decisions based on the protocol version,
269 copy that as well. */
270 params.param_block_version = hdr->protocol_version;
272 if (bzp_add_kernel(&bzp, input, setup_size) != 0)
273 return -1;
275 if (buffer_size(&bzp.initrd) != 0) {
276 /* TODO: this is a bit of a hack. Linux recommends to store
277 * initrd near to end-of-mem, but that's hard to do on build
278 * time. It definitely fails to read the image if it's too
279 * close to the kernel, so give it some room.
281 initrd_base = kernel_base + buffer_size(&bzp.kernel);
282 initrd_base = ALIGN_UP(initrd_base, 64*1024*1024);
284 params.initrd_start = initrd_base;
285 params.initrd_size = buffer_size(&bzp.initrd);
288 if (bzp_add_params(&bzp, &params) != 0)
289 return -1;
291 if (bzp_init_output(&bzp, input->name) != 0)
292 return -1;
294 /* parameter block */
295 bzp_output_segment(&bzp, &bzp.parameters,
296 PAYLOAD_SEGMENT_DATA, LINUX_PARAM_LOC);
298 /* code block */
299 bzp_output_segment(&bzp, &bzp.kernel,
300 PAYLOAD_SEGMENT_CODE, kernel_base);
302 /* trampoline */
303 bzp_output_segment(&bzp, &bzp.trampoline,
304 PAYLOAD_SEGMENT_CODE, TRAMPOLINE_ENTRY_LOC);
306 /* cmdline */
307 bzp_output_segment(&bzp, &bzp.cmdline,
308 PAYLOAD_SEGMENT_DATA, COMMAND_LINE_LOC);
310 /* initrd */
311 bzp_output_segment(&bzp, &bzp.initrd,
312 PAYLOAD_SEGMENT_DATA, initrd_base);
314 /* Terminating entry segment. */
315 bzp_output_segment(&bzp, NULL, PAYLOAD_SEGMENT_ENTRY, TRAMPOLINE_ENTRY_LOC);
317 /* Set size of buffer taking into account potential compression. */
318 buffer_set_size(&bzp.output, bzp.offset);
319 /* Make passed-in output buffer be valid. */
320 buffer_clone(output, &bzp.output);
322 /* Serialize the segments with the correct encoding. */
323 xdr_segs(output, bzp.segs, bzp.num_segments);
324 return 0;