1 /* SPDX-License-Identifier: GPL-2.0-only */
12 extern unsigned char trampoline
[];
13 extern unsigned int trampoline_len
;
16 * Current max number of segments include:
23 * 6. terminating entry segment
25 #define MAX_NUM_SEGMENTS 6
28 /* Input variables. */
30 struct cbfs_payload_segment segs
[MAX_NUM_SEGMENTS
];
31 struct buffer parameters
;
33 struct buffer trampoline
;
34 struct buffer cmdline
;
36 /* Output variables. */
37 enum cbfs_compression algo
;
38 comp_func_ptr compress
;
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;
54 bzp
->compress
= compression_function(algo
);
55 if (bzp
->compress
== NULL
) {
56 ERROR("Invalid compression algorithm specified.\n");
63 static int bzp_add_initrd(struct bzpayload
*bzp
, const char *fname
)
68 if (buffer_from_file(&bzp
->initrd
, fname
)) {
69 ERROR("could not open initrd.\n");
78 static void bzp_add_segment(struct bzpayload
*bzp
, struct buffer
*b
, void *data
,
81 buffer_init(b
, NULL
, data
, size
);
85 static int bzp_add_trampoline(struct bzpayload
*bzp
)
87 bzp_add_segment(bzp
, &bzp
->trampoline
, trampoline
,
92 static int bzp_add_cmdline(struct bzpayload
*bzp
, char *cmdline
)
97 bzp_add_segment(bzp
, &bzp
->cmdline
, cmdline
, strlen(cmdline
) + 1);
102 static int bzp_add_params(struct bzpayload
*bzp
, struct linux_params
*params
)
104 bzp_add_segment(bzp
, &bzp
->parameters
, params
, sizeof(*params
));
109 static int bzp_add_kernel(struct bzpayload
*bzp
, const struct buffer
*in
,
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
);
120 static int bzp_init_output(struct bzpayload
*bzp
, const char *name
)
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
);
133 if (buffer_create(&bzp
->output
, sz
, name
) != 0)
136 bzp
->out_seg
= &bzp
->segs
[0];
141 static void bzp_output_segment(struct bzpayload
*bzp
, struct buffer
*b
,
142 uint32_t type
, uint64_t load_addr
)
145 struct cbfs_payload_segment
*seg
;
148 /* Don't process empty buffers. */
149 if (b
!= NULL
&& buffer_size(b
) == 0)
154 seg
->load_addr
= load_addr
;
157 /* No buffer associated with segment. */
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
;
170 /* Update output offset. */
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
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)
199 if (bzp_add_trampoline(&bzp
) != 0)
202 if (bzp_add_initrd(&bzp
, initrd_name
) != 0)
205 if (bzp_add_cmdline(&bzp
, cmdline
) != 0)
208 if (hdr
->setup_sects
!= 0) {
209 setup_size
= (hdr
->setup_sects
+ 1) * 512;
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(¶ms
, 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
;
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");
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)
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
, ¶ms
) != 0)
291 if (bzp_init_output(&bzp
, input
->name
) != 0)
294 /* parameter block */
295 bzp_output_segment(&bzp
, &bzp
.parameters
,
296 PAYLOAD_SEGMENT_DATA
, LINUX_PARAM_LOC
);
299 bzp_output_segment(&bzp
, &bzp
.kernel
,
300 PAYLOAD_SEGMENT_CODE
, kernel_base
);
303 bzp_output_segment(&bzp
, &bzp
.trampoline
,
304 PAYLOAD_SEGMENT_CODE
, TRAMPOLINE_ENTRY_LOC
);
307 bzp_output_segment(&bzp
, &bzp
.cmdline
,
308 PAYLOAD_SEGMENT_DATA
, COMMAND_LINE_LOC
);
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
);