acpi: Add IORT helper functions
[coreboot2.git] / util / cbfstool / cbfs-payload-linux.c
blobd0f24fec6cb615a22e9e28f56a5f30555c1a94db
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 struct buffer output;
38 size_t offset;
39 struct cbfs_payload_segment *out_seg;
42 static int bzp_init(struct bzpayload *bzp)
44 memset(bzp, 0, sizeof(*bzp));
47 * Need at least the terminating entry segment.
49 bzp->num_segments = 1;
51 return 0;
54 static int bzp_add_initrd(struct bzpayload *bzp, const char *fname)
56 if (fname == NULL)
57 return 0;
59 if (buffer_from_file(&bzp->initrd, fname)) {
60 ERROR("could not open initrd.\n");
61 return -1;
64 bzp->num_segments++;
66 return 0;
69 static void bzp_add_segment(struct bzpayload *bzp, struct buffer *b, void *data,
70 size_t size)
72 buffer_init(b, NULL, data, size);
73 bzp->num_segments++;
76 static int bzp_add_trampoline(struct bzpayload *bzp)
78 bzp_add_segment(bzp, &bzp->trampoline, trampoline,
79 trampoline_len);
80 return 0;
83 static int bzp_add_cmdline(struct bzpayload *bzp, char *cmdline)
85 if (cmdline == NULL)
86 return 0;
88 bzp_add_segment(bzp, &bzp->cmdline, cmdline, strlen(cmdline) + 1);
90 return 0;
93 static int bzp_add_params(struct bzpayload *bzp, struct linux_params *params)
95 bzp_add_segment(bzp, &bzp->parameters, params, sizeof(*params));
97 return 0;
100 static int bzp_add_kernel(struct bzpayload *bzp, const struct buffer *in,
101 size_t setup_size)
103 char *input = buffer_get(in);
104 size_t kern_sz = buffer_size(in) - setup_size;
106 bzp_add_segment(bzp, &bzp->kernel, &input[setup_size], kern_sz);
108 return 0;
111 static int bzp_init_output(struct bzpayload *bzp, const char *name)
113 size_t sz = 0;
115 sz += buffer_size(&bzp->parameters);
116 sz += buffer_size(&bzp->kernel);
117 sz += buffer_size(&bzp->trampoline);
118 sz += buffer_size(&bzp->cmdline);
119 sz += buffer_size(&bzp->initrd);
121 bzp->offset = bzp->num_segments * sizeof(struct cbfs_payload_segment);
122 sz += bzp->offset;
124 if (buffer_create(&bzp->output, sz, name) != 0)
125 return -1;
127 bzp->out_seg = &bzp->segs[0];
129 return 0;
132 static int bzp_output_segment(struct bzpayload *bzp, struct buffer *b,
133 uint32_t type, uint64_t load_addr,
134 enum cbfs_compression algo)
136 struct buffer out;
137 struct cbfs_payload_segment *seg;
138 int len = 0;
140 /* Don't process empty buffers. */
141 if (b != NULL && buffer_size(b) == 0) {
142 ERROR("%s(): Input buffer is empty\n", __func__);
143 return -1;
146 seg = bzp->out_seg;
147 seg->type = type;
148 seg->load_addr = load_addr;
149 bzp->out_seg++;
151 /* No buffer associated with segment. */
152 if (b == NULL) {
153 if (type == PAYLOAD_SEGMENT_ENTRY)
154 return 0; // entry segments don't have a buffer attached
155 else
156 return -1;
159 /* Use a temp buffer for easier management. */
160 buffer_splice(&out, &bzp->output, bzp->offset, buffer_size(b));
162 seg->mem_len = buffer_size(b);
163 seg->offset = bzp->offset;
165 comp_func_ptr compress_func = compression_function(algo);
166 int ret = compress_func(buffer_get(b), buffer_size(b), buffer_get(&out), &len);
167 if (ret) {
168 ERROR("%s(): Compression failed\n", __func__);
169 return ret;
172 seg->compression = algo;
173 seg->len = len;
175 /* Update output offset. */
176 bzp->offset += len;
178 return 0; // success
181 /* TODO:
182 * handle special arguments
183 * mem= argument - only affects loading decisions (kernel + initrd), not e820 -> build time
184 * vga= argument (FILO ignores this)
185 * add support for more parameters to trampoline:
186 * alt_mem_k, ext_mem_k (not strictly necessary since e820 takes precedence)
187 * framebuffer/console values
189 * larger work:
190 * is compress() safe to use in a size constrained buffer? ie. do(es) the
191 * compression algorithm(s) stop once the compression result reaches input
192 * size (ie. incompressible data)?
194 int parse_bzImage_to_payload(const struct buffer *input,
195 struct buffer *output, const char *initrd_name,
196 char *cmdline, enum cbfs_compression algo)
198 struct bzpayload bzp;
199 unsigned int initrd_base = 64*1024*1024;
200 struct linux_header *hdr = (struct linux_header *)input->data;
201 unsigned int setup_size = 4 * 512;
203 if (compression_function(algo) == NULL) {
204 ERROR("Invalid compression algorithm specified.\n");
205 return -1;
208 if (bzp_init(&bzp) != 0)
209 return -1;
211 if (bzp_add_trampoline(&bzp) != 0)
212 return -1;
214 if (bzp_add_initrd(&bzp, initrd_name) != 0)
215 return -1;
217 if (bzp_add_cmdline(&bzp, cmdline) != 0)
218 return -1;
220 if (hdr->setup_sects != 0) {
221 setup_size = (hdr->setup_sects + 1) * 512;
222 } else {
223 WARN("hdr->setup_sects is 0, which could cause boot problems.\n");
226 /* Setup parameter block. Imitate FILO. */
227 struct linux_params params;
229 memset(&params, 0, sizeof(struct linux_params));
231 params.mount_root_rdonly = hdr->root_flags;
232 params.orig_root_dev = hdr->root_dev;
233 params.init_size = hdr->init_size;
235 /* Sensible video defaults. Might be overridden on runtime by coreboot tables. */
236 params.orig_video_mode = 3;
237 params.orig_video_cols = 80;
238 params.orig_video_lines = 25;
239 params.orig_video_isVGA = 1;
240 params.orig_video_points = 16;
242 params.loader_type = 0xff; /* Unregistered Linux loader */
244 if (cmdline != NULL) {
245 if (hdr->protocol_version < 0x202) {
246 params.cl_magic = CL_MAGIC_VALUE;
247 params.cl_offset = COMMAND_LINE_LOC - LINUX_PARAM_LOC;
248 } else {
249 params.cmd_line_ptr = COMMAND_LINE_LOC;
253 unsigned long kernel_base = 0x100000;
254 if ((hdr->protocol_version < 0x200) || !(hdr->loadflags & 1)) {
255 kernel_base = 0x1000; /* zImage kernel */
257 /* kernel prefers an address, so listen */
258 if ((hdr->protocol_version >= 0x20a) && (!(hdr->pref_address >> 32))) {
259 kernel_base = hdr->pref_address;
261 if (hdr->protocol_version >= 0x205) {
262 params.relocatable_kernel = hdr->relocatable_kernel;
263 params.kernel_alignment = hdr->kernel_alignment;
264 if (hdr->relocatable_kernel != 0) {
265 /* 16 MB should be way outside coreboot's playground,
266 * so if possible (relocatable kernel) use that to
267 * avoid a trampoline copy. */
268 kernel_base = ALIGN_UP(16*1024*1024, params.kernel_alignment);
269 if (hdr->init_size == 0) {
270 ERROR("init_size 0 for relocatable kernel\n");
271 return -1;
276 /* We have a trampoline and use that, but it can simply use
277 * this information for its jump to real Linux. */
278 params.kernel_start = kernel_base;
280 /* To make decisions based on the protocol version,
281 copy that as well. */
282 params.param_block_version = hdr->protocol_version;
284 if (bzp_add_kernel(&bzp, input, setup_size) != 0)
285 return -1;
287 if (buffer_size(&bzp.initrd) != 0) {
288 /* TODO: this is a bit of a hack. Linux recommends to store
289 * initrd near to end-of-mem, but that's hard to do on build
290 * time. It definitely fails to read the image if it's too
291 * close to the kernel, so give it some room.
293 initrd_base = kernel_base + buffer_size(&bzp.kernel);
294 initrd_base = ALIGN_UP(initrd_base, 64*1024*1024);
296 params.initrd_start = initrd_base;
297 params.initrd_size = buffer_size(&bzp.initrd);
300 if (bzp_add_params(&bzp, &params) != 0)
301 return -1;
303 if (bzp_init_output(&bzp, input->name) != 0)
304 return -1;
306 int ret;
308 /* parameter block */
309 ret = bzp_output_segment(&bzp, &bzp.parameters,
310 PAYLOAD_SEGMENT_DATA, LINUX_PARAM_LOC, algo);
311 if (ret) {
312 ERROR("%s(): Failed to write Linux zero page into segment\n", __func__);
313 return ret;
316 /* code block: There is no point in compressing the bzImage (it is already compressed)*/
317 ret = bzp_output_segment(&bzp, &bzp.kernel,
318 PAYLOAD_SEGMENT_CODE, kernel_base, CBFS_COMPRESS_NONE);
319 if (ret) {
320 ERROR("%s(): Failed to write Linux kernel into segment\n", __func__);
321 return ret;
324 /* trampoline */
325 ret = bzp_output_segment(&bzp, &bzp.trampoline,
326 PAYLOAD_SEGMENT_CODE, TRAMPOLINE_ENTRY_LOC, algo);
327 if (ret) {
328 ERROR("%s(): Failed to write Linux trampoline into segment\n", __func__);
329 return ret;
332 /* cmdline */
333 if (buffer_size(&bzp.cmdline) != 0) {
334 ret = bzp_output_segment(&bzp, &bzp.cmdline,
335 PAYLOAD_SEGMENT_DATA, COMMAND_LINE_LOC, algo);
336 if (ret) {
337 ERROR("%s(): Failed to write Linux cmdline into segment\n", __func__);
338 return ret;
342 if (buffer_size(&bzp.initrd) != 0) {
343 /* initrd */
344 ret = bzp_output_segment(&bzp, &bzp.initrd,
345 PAYLOAD_SEGMENT_DATA, initrd_base, algo);
346 if (ret) {
347 ERROR("%s(): Failed to write Linux initrd into segment\n", __func__);
348 return ret;
352 /* Terminating entry segment. */
353 ret = bzp_output_segment(&bzp, NULL, PAYLOAD_SEGMENT_ENTRY, TRAMPOLINE_ENTRY_LOC, algo);
354 if (ret) {
355 ERROR("%s(): Failed to write entry segment\n", __func__);
356 return ret;
359 /* Set size of buffer taking into account potential compression. */
360 buffer_set_size(&bzp.output, bzp.offset);
361 /* Make passed-in output buffer be valid. */
362 buffer_clone(output, &bzp.output);
364 /* Serialize the segments with the correct encoding. */
365 xdr_segs(output, bzp.segs, bzp.num_segments);
366 return 0;