4 * Copyright (C) 2013 Patrick Georgi <patrick@georgi-clan.de>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
25 extern unsigned char trampoline
[];
26 extern unsigned int trampoline_len
;
29 * Current max number of segments include:
36 * 6. terminating entry segment
38 #define MAX_NUM_SEGMENTS 6
41 /* Input variables. */
43 struct cbfs_payload_segment segs
[MAX_NUM_SEGMENTS
];
44 struct buffer parameters
;
46 struct buffer trampoline
;
47 struct buffer cmdline
;
49 /* Output variables. */
51 comp_func_ptr compress
;
54 struct cbfs_payload_segment
*out_seg
;
57 static int bzp_init(struct bzpayload
*bzp
, enum comp_algo algo
)
59 memset(bzp
, 0, sizeof(*bzp
));
62 * Need at least the terminating entry segment.
64 bzp
->num_segments
= 1;
67 bzp
->compress
= compression_function(algo
);
68 if (bzp
->compress
== NULL
) {
69 ERROR("Invalid compression algorithm specified.\n");
76 static int bzp_add_initrd(struct bzpayload
*bzp
, const char *fname
)
81 if (buffer_from_file(&bzp
->initrd
, fname
)) {
82 ERROR("could not open initrd.\n");
91 static void bzp_add_segment(struct bzpayload
*bzp
, struct buffer
*b
, void *data
,
94 buffer_init(b
, NULL
, data
, size
);
98 static int bzp_add_trampoline(struct bzpayload
*bzp
)
100 bzp_add_segment(bzp
, &bzp
->trampoline
, trampoline
,
105 static int bzp_add_cmdline(struct bzpayload
*bzp
, char *cmdline
)
110 bzp_add_segment(bzp
, &bzp
->cmdline
, cmdline
, strlen(cmdline
) + 1);
115 static int bzp_add_params(struct bzpayload
*bzp
, struct linux_params
*params
)
117 bzp_add_segment(bzp
, &bzp
->parameters
, params
, sizeof(*params
));
122 static int bzp_add_kernel(struct bzpayload
*bzp
, const struct buffer
*in
,
125 char *input
= buffer_get(in
);
126 size_t kern_sz
= buffer_size(in
) - setup_size
;
128 bzp_add_segment(bzp
, &bzp
->kernel
, &input
[setup_size
], kern_sz
);
133 static int bzp_init_output(struct bzpayload
*bzp
, const char *name
)
137 sz
+= buffer_size(&bzp
->parameters
);
138 sz
+= buffer_size(&bzp
->kernel
);
139 sz
+= buffer_size(&bzp
->trampoline
);
140 sz
+= buffer_size(&bzp
->cmdline
);
141 sz
+= buffer_size(&bzp
->initrd
);
143 bzp
->offset
= bzp
->num_segments
* sizeof(struct cbfs_payload_segment
);
146 if (buffer_create(&bzp
->output
, sz
, name
) != 0)
149 bzp
->out_seg
= &bzp
->segs
[0];
154 static void bzp_output_segment(struct bzpayload
*bzp
, struct buffer
*b
,
155 uint32_t type
, uint64_t load_addr
)
158 struct cbfs_payload_segment
*seg
;
161 /* Don't process empty buffers. */
162 if (b
!= NULL
&& buffer_size(b
) == 0)
167 seg
->load_addr
= load_addr
;
170 /* No buffer associated with segment. */
174 /* Use a temp buffer for easier management. */
175 buffer_splice(&out
, &bzp
->output
, bzp
->offset
, buffer_size(b
));
177 seg
->mem_len
= buffer_size(b
);
178 seg
->offset
= bzp
->offset
;
179 bzp
->compress(buffer_get(b
), buffer_size(b
), buffer_get(&out
), &len
);
180 seg
->compression
= bzp
->algo
;
183 /* Update output offset. */
188 * handle special arguments
189 * mem= argument - only affects loading decisions (kernel + initrd), not e820 -> build time
190 * vga= argument (FILO ignores this)
191 * add support for more parameters to trampoline:
192 * alt_mem_k, ext_mem_k (not strictly necessary since e820 takes precedence)
193 * framebuffer/console values
196 * is compress() safe to use in a size constrained buffer? ie. do(es) the
197 * compression algorithm(s) stop once the compression result reaches input
198 * size (ie. incompressible data)?
200 int parse_bzImage_to_payload(const struct buffer
*input
,
201 struct buffer
*output
, const char *initrd_name
,
202 char *cmdline
, enum comp_algo algo
)
204 struct bzpayload bzp
;
205 unsigned int initrd_base
= 64*1024*1024;
206 struct linux_header
*hdr
= (struct linux_header
*)input
->data
;
207 unsigned int setup_size
= 4 * 512;
209 if (bzp_init(&bzp
, algo
) != 0)
212 if (bzp_add_trampoline(&bzp
) != 0)
215 if (bzp_add_initrd(&bzp
, initrd_name
) != 0)
218 if (bzp_add_cmdline(&bzp
, cmdline
) != 0)
221 if (hdr
->setup_sects
!= 0) {
222 setup_size
= (hdr
->setup_sects
+ 1) * 512;
224 WARN("hdr->setup_sects is 0, which could cause boot problems.\n");
227 /* Setup parameter block. Imitate FILO. */
228 struct linux_params params
;
230 memset(¶ms
, 0, sizeof(struct linux_params
));
232 params
.mount_root_rdonly
= hdr
->root_flags
;
233 params
.orig_root_dev
= hdr
->root_dev
;
234 params
.init_size
= hdr
->init_size
;
236 /* Sensible video defaults. Might be overridden on runtime by coreboot tables. */
237 params
.orig_video_mode
= 3;
238 params
.orig_video_cols
= 80;
239 params
.orig_video_lines
= 25;
240 params
.orig_video_isVGA
= 1;
241 params
.orig_video_points
= 16;
243 params
.loader_type
= 0xff; /* Unregistered Linux loader */
245 if (cmdline
!= NULL
) {
246 if (hdr
->protocol_version
< 0x202) {
247 params
.cl_magic
= CL_MAGIC_VALUE
;
248 params
.cl_offset
= COMMAND_LINE_LOC
- LINUX_PARAM_LOC
;
250 params
.cmd_line_ptr
= COMMAND_LINE_LOC
;
254 unsigned long kernel_base
= 0x100000;
255 if ((hdr
->protocol_version
< 0x200) || !(hdr
->loadflags
& 1)) {
256 kernel_base
= 0x1000; /* zImage kernel */
258 /* kernel prefers an address, so listen */
259 if ((hdr
->protocol_version
>= 0x20a) && (!(hdr
->pref_address
>> 32))) {
260 kernel_base
= hdr
->pref_address
;
262 if (hdr
->protocol_version
>= 0x205) {
263 params
.relocatable_kernel
= hdr
->relocatable_kernel
;
264 params
.kernel_alignment
= hdr
->kernel_alignment
;
265 if (hdr
->relocatable_kernel
!= 0) {
266 /* 16 MB should be way outside coreboot's playground,
267 * so if possible (relocatable kernel) use that to
268 * avoid a trampoline copy. */
269 kernel_base
= ALIGN(16*1024*1024, params
.kernel_alignment
);
270 if (hdr
->init_size
== 0) {
271 ERROR("init_size 0 for relocatable kernel\n");
277 /* We have a trampoline and use that, but it can simply use
278 * this information for its jump to real Linux. */
279 params
.kernel_start
= kernel_base
;
281 if (bzp_add_kernel(&bzp
, input
, setup_size
) != 0)
284 if (buffer_size(&bzp
.initrd
) != 0) {
285 /* TODO: this is a bit of a hack. Linux recommends to store
286 * initrd near to end-of-mem, but that's hard to do on build
287 * time. It definitely fails to read the image if it's too
288 * close to the kernel, so give it some room.
290 initrd_base
= kernel_base
+ buffer_size(&bzp
.kernel
);
291 initrd_base
= ALIGN(initrd_base
, 64*1024*1024);
293 params
.initrd_start
= initrd_base
;
294 params
.initrd_size
= buffer_size(&bzp
.initrd
);
297 if (bzp_add_params(&bzp
, ¶ms
) != 0)
300 if (bzp_init_output(&bzp
, input
->name
) != 0)
303 /* parameter block */
304 bzp_output_segment(&bzp
, &bzp
.parameters
,
305 PAYLOAD_SEGMENT_DATA
, LINUX_PARAM_LOC
);
308 bzp_output_segment(&bzp
, &bzp
.kernel
,
309 PAYLOAD_SEGMENT_CODE
, kernel_base
);
312 bzp_output_segment(&bzp
, &bzp
.trampoline
,
313 PAYLOAD_SEGMENT_CODE
, TRAMPOLINE_ENTRY_LOC
);
316 bzp_output_segment(&bzp
, &bzp
.cmdline
,
317 PAYLOAD_SEGMENT_DATA
, COMMAND_LINE_LOC
);
320 bzp_output_segment(&bzp
, &bzp
.initrd
,
321 PAYLOAD_SEGMENT_DATA
, initrd_base
);
323 /* Terminating entry segment. */
324 bzp_output_segment(&bzp
, NULL
, PAYLOAD_SEGMENT_ENTRY
, TRAMPOLINE_ENTRY_LOC
);
326 /* Set size of buffer taking into account potential compression. */
327 buffer_set_size(&bzp
.output
, bzp
.offset
);
328 /* Make passed-in output buffer be valid. */
329 buffer_clone(output
, &bzp
.output
);
331 /* Serialize the segments with the correct encoding. */
332 xdr_segs(output
, bzp
.segs
, bzp
.num_segments
);