1 | /* |
---|
2 | * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>. |
---|
3 | * |
---|
4 | * This program is free software; you can redistribute it and/or |
---|
5 | * modify it under the terms of the GNU General Public License as |
---|
6 | * published by the Free Software Foundation; either version 2 of the |
---|
7 | * License, or any later version. |
---|
8 | * |
---|
9 | * This program is distributed in the hope that it will be useful, but |
---|
10 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
12 | * General Public License for more details. |
---|
13 | * |
---|
14 | * You should have received a copy of the GNU General Public License |
---|
15 | * along with this program; if not, write to the Free Software |
---|
16 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
---|
17 | */ |
---|
18 | |
---|
19 | FILE_LICENCE ( GPL2_OR_LATER ); |
---|
20 | |
---|
21 | /** |
---|
22 | * @file |
---|
23 | * |
---|
24 | * Linux bzImage image format |
---|
25 | * |
---|
26 | */ |
---|
27 | |
---|
28 | #include <stdint.h> |
---|
29 | #include <stdlib.h> |
---|
30 | #include <string.h> |
---|
31 | #include <errno.h> |
---|
32 | #include <assert.h> |
---|
33 | #include <realmode.h> |
---|
34 | #include <bzimage.h> |
---|
35 | #include <gpxe/uaccess.h> |
---|
36 | #include <gpxe/image.h> |
---|
37 | #include <gpxe/segment.h> |
---|
38 | #include <gpxe/init.h> |
---|
39 | #include <gpxe/cpio.h> |
---|
40 | #include <gpxe/features.h> |
---|
41 | |
---|
42 | FEATURE ( FEATURE_IMAGE, "bzImage", DHCP_EB_FEATURE_BZIMAGE, 1 ); |
---|
43 | |
---|
44 | struct image_type bzimage_image_type __image_type ( PROBE_NORMAL ); |
---|
45 | |
---|
46 | /** |
---|
47 | * bzImage context |
---|
48 | */ |
---|
49 | struct bzimage_context { |
---|
50 | /** Boot protocol version */ |
---|
51 | unsigned int version; |
---|
52 | /** Real-mode kernel portion load segment address */ |
---|
53 | unsigned int rm_kernel_seg; |
---|
54 | /** Real-mode kernel portion load address */ |
---|
55 | userptr_t rm_kernel; |
---|
56 | /** Real-mode kernel portion file size */ |
---|
57 | size_t rm_filesz; |
---|
58 | /** Real-mode heap top (offset from rm_kernel) */ |
---|
59 | size_t rm_heap; |
---|
60 | /** Command line (offset from rm_kernel) */ |
---|
61 | size_t rm_cmdline; |
---|
62 | /** Command line maximum length */ |
---|
63 | size_t cmdline_size; |
---|
64 | /** Real-mode kernel portion total memory size */ |
---|
65 | size_t rm_memsz; |
---|
66 | /** Non-real-mode kernel portion load address */ |
---|
67 | userptr_t pm_kernel; |
---|
68 | /** Non-real-mode kernel portion file and memory size */ |
---|
69 | size_t pm_sz; |
---|
70 | /** Video mode */ |
---|
71 | unsigned int vid_mode; |
---|
72 | /** Memory limit */ |
---|
73 | uint64_t mem_limit; |
---|
74 | /** Initrd address */ |
---|
75 | physaddr_t ramdisk_image; |
---|
76 | /** Initrd size */ |
---|
77 | physaddr_t ramdisk_size; |
---|
78 | |
---|
79 | /** Command line magic block */ |
---|
80 | struct bzimage_cmdline cmdline_magic; |
---|
81 | /** bzImage header */ |
---|
82 | struct bzimage_header bzhdr; |
---|
83 | }; |
---|
84 | |
---|
85 | /** |
---|
86 | * Parse bzImage header |
---|
87 | * |
---|
88 | * @v image bzImage file |
---|
89 | * @v bzimg bzImage context |
---|
90 | * @v src bzImage to parse |
---|
91 | * @ret rc Return status code |
---|
92 | */ |
---|
93 | static int bzimage_parse_header ( struct image *image, |
---|
94 | struct bzimage_context *bzimg, |
---|
95 | userptr_t src ) { |
---|
96 | unsigned int syssize; |
---|
97 | int is_bzimage; |
---|
98 | |
---|
99 | /* Sanity check */ |
---|
100 | if ( image->len < ( BZI_HDR_OFFSET + sizeof ( bzimg->bzhdr ) ) ) { |
---|
101 | DBGC ( image, "bzImage %p too short for kernel header\n", |
---|
102 | image ); |
---|
103 | return -ENOEXEC; |
---|
104 | } |
---|
105 | |
---|
106 | /* Read in header structures */ |
---|
107 | memset ( bzimg, 0, sizeof ( *bzimg ) ); |
---|
108 | copy_from_user ( &bzimg->cmdline_magic, src, BZI_CMDLINE_OFFSET, |
---|
109 | sizeof ( bzimg->cmdline_magic ) ); |
---|
110 | copy_from_user ( &bzimg->bzhdr, src, BZI_HDR_OFFSET, |
---|
111 | sizeof ( bzimg->bzhdr ) ); |
---|
112 | |
---|
113 | /* Calculate size of real-mode portion */ |
---|
114 | bzimg->rm_filesz = |
---|
115 | ( ( bzimg->bzhdr.setup_sects ? bzimg->bzhdr.setup_sects : 4 ) + 1 ) << 9; |
---|
116 | if ( bzimg->rm_filesz > image->len ) { |
---|
117 | DBGC ( image, "bzImage %p too short for %zd byte of setup\n", |
---|
118 | image, bzimg->rm_filesz ); |
---|
119 | return -ENOEXEC; |
---|
120 | } |
---|
121 | bzimg->rm_memsz = BZI_ASSUMED_RM_SIZE; |
---|
122 | |
---|
123 | /* Calculate size of protected-mode portion */ |
---|
124 | bzimg->pm_sz = ( image->len - bzimg->rm_filesz ); |
---|
125 | syssize = ( ( bzimg->pm_sz + 15 ) / 16 ); |
---|
126 | |
---|
127 | /* Check for signatures and determine version */ |
---|
128 | if ( bzimg->bzhdr.boot_flag != BZI_BOOT_FLAG ) { |
---|
129 | DBGC ( image, "bzImage %p missing 55AA signature\n", image ); |
---|
130 | return -ENOEXEC; |
---|
131 | } |
---|
132 | if ( bzimg->bzhdr.header == BZI_SIGNATURE ) { |
---|
133 | /* 2.00+ */ |
---|
134 | bzimg->version = bzimg->bzhdr.version; |
---|
135 | } else { |
---|
136 | /* Pre-2.00. Check that the syssize field is correct, |
---|
137 | * as a guard against accepting arbitrary binary data, |
---|
138 | * since the 55AA check is pretty lax. Note that the |
---|
139 | * syssize field is unreliable for protocols between |
---|
140 | * 2.00 and 2.03 inclusive, so we should not always |
---|
141 | * check this field. |
---|
142 | */ |
---|
143 | bzimg->version = 0x0100; |
---|
144 | if ( bzimg->bzhdr.syssize != syssize ) { |
---|
145 | DBGC ( image, "bzImage %p bad syssize %x (expected " |
---|
146 | "%x)\n", image, bzimg->bzhdr.syssize, syssize ); |
---|
147 | return -ENOEXEC; |
---|
148 | } |
---|
149 | } |
---|
150 | |
---|
151 | /* Determine image type */ |
---|
152 | is_bzimage = ( ( bzimg->version >= 0x0200 ) ? |
---|
153 | ( bzimg->bzhdr.loadflags & BZI_LOAD_HIGH ) : 0 ); |
---|
154 | |
---|
155 | /* Calculate load address of real-mode portion */ |
---|
156 | bzimg->rm_kernel_seg = ( is_bzimage ? 0x1000 : 0x9000 ); |
---|
157 | bzimg->rm_kernel = real_to_user ( bzimg->rm_kernel_seg, 0 ); |
---|
158 | |
---|
159 | /* Allow space for the stack and heap */ |
---|
160 | bzimg->rm_memsz += BZI_STACK_SIZE; |
---|
161 | bzimg->rm_heap = bzimg->rm_memsz; |
---|
162 | |
---|
163 | /* Allow space for the command line */ |
---|
164 | bzimg->rm_cmdline = bzimg->rm_memsz; |
---|
165 | bzimg->rm_memsz += BZI_CMDLINE_SIZE; |
---|
166 | |
---|
167 | /* Calculate load address of protected-mode portion */ |
---|
168 | bzimg->pm_kernel = phys_to_user ( is_bzimage ? BZI_LOAD_HIGH_ADDR |
---|
169 | : BZI_LOAD_LOW_ADDR ); |
---|
170 | |
---|
171 | /* Extract video mode */ |
---|
172 | bzimg->vid_mode = bzimg->bzhdr.vid_mode; |
---|
173 | |
---|
174 | /* Extract memory limit */ |
---|
175 | bzimg->mem_limit = ( ( bzimg->version >= 0x0203 ) ? |
---|
176 | bzimg->bzhdr.initrd_addr_max : BZI_INITRD_MAX ); |
---|
177 | |
---|
178 | /* Extract command line size */ |
---|
179 | bzimg->cmdline_size = ( ( bzimg->version >= 0x0206 ) ? |
---|
180 | bzimg->bzhdr.cmdline_size : BZI_CMDLINE_SIZE ); |
---|
181 | |
---|
182 | DBGC ( image, "bzImage %p version %04x RM %#lx+%#zx PM %#lx+%#zx " |
---|
183 | "cmdlen %zd\n", image, bzimg->version, |
---|
184 | user_to_phys ( bzimg->rm_kernel, 0 ), bzimg->rm_filesz, |
---|
185 | user_to_phys ( bzimg->pm_kernel, 0 ), bzimg->pm_sz, |
---|
186 | bzimg->cmdline_size ); |
---|
187 | |
---|
188 | return 0; |
---|
189 | } |
---|
190 | |
---|
191 | /** |
---|
192 | * Update bzImage header in loaded kernel |
---|
193 | * |
---|
194 | * @v image bzImage file |
---|
195 | * @v bzimg bzImage context |
---|
196 | * @v dst bzImage to update |
---|
197 | */ |
---|
198 | static void bzimage_update_header ( struct image *image, |
---|
199 | struct bzimage_context *bzimg, |
---|
200 | userptr_t dst ) { |
---|
201 | |
---|
202 | /* Set loader type */ |
---|
203 | if ( bzimg->version >= 0x0200 ) |
---|
204 | bzimg->bzhdr.type_of_loader = BZI_LOADER_TYPE_GPXE; |
---|
205 | |
---|
206 | /* Set heap end pointer */ |
---|
207 | if ( bzimg->version >= 0x0201 ) { |
---|
208 | bzimg->bzhdr.heap_end_ptr = ( bzimg->rm_heap - 0x200 ); |
---|
209 | bzimg->bzhdr.loadflags |= BZI_CAN_USE_HEAP; |
---|
210 | } |
---|
211 | |
---|
212 | /* Set command line */ |
---|
213 | if ( bzimg->version >= 0x0202 ) { |
---|
214 | bzimg->bzhdr.cmd_line_ptr = user_to_phys ( bzimg->rm_kernel, |
---|
215 | bzimg->rm_cmdline ); |
---|
216 | } else { |
---|
217 | bzimg->cmdline_magic.magic = BZI_CMDLINE_MAGIC; |
---|
218 | bzimg->cmdline_magic.offset = bzimg->rm_cmdline; |
---|
219 | bzimg->bzhdr.setup_move_size = bzimg->rm_memsz; |
---|
220 | } |
---|
221 | |
---|
222 | /* Set video mode */ |
---|
223 | bzimg->bzhdr.vid_mode = bzimg->vid_mode; |
---|
224 | |
---|
225 | /* Set initrd address */ |
---|
226 | if ( bzimg->version >= 0x0200 ) { |
---|
227 | bzimg->bzhdr.ramdisk_image = bzimg->ramdisk_image; |
---|
228 | bzimg->bzhdr.ramdisk_size = bzimg->ramdisk_size; |
---|
229 | } |
---|
230 | |
---|
231 | /* Write out header structures */ |
---|
232 | copy_to_user ( dst, BZI_CMDLINE_OFFSET, &bzimg->cmdline_magic, |
---|
233 | sizeof ( bzimg->cmdline_magic ) ); |
---|
234 | copy_to_user ( dst, BZI_HDR_OFFSET, &bzimg->bzhdr, |
---|
235 | sizeof ( bzimg->bzhdr ) ); |
---|
236 | |
---|
237 | DBGC ( image, "bzImage %p vidmode %d\n", image, bzimg->vid_mode ); |
---|
238 | } |
---|
239 | |
---|
240 | /** |
---|
241 | * Parse kernel command line for bootloader parameters |
---|
242 | * |
---|
243 | * @v image bzImage file |
---|
244 | * @v bzimg bzImage context |
---|
245 | * @v cmdline Kernel command line |
---|
246 | * @ret rc Return status code |
---|
247 | */ |
---|
248 | static int bzimage_parse_cmdline ( struct image *image, |
---|
249 | struct bzimage_context *bzimg, |
---|
250 | const char *cmdline ) { |
---|
251 | char *vga; |
---|
252 | char *mem; |
---|
253 | |
---|
254 | /* Look for "vga=" */ |
---|
255 | if ( ( vga = strstr ( cmdline, "vga=" ) ) ) { |
---|
256 | vga += 4; |
---|
257 | if ( strcmp ( vga, "normal" ) == 0 ) { |
---|
258 | bzimg->vid_mode = BZI_VID_MODE_NORMAL; |
---|
259 | } else if ( strcmp ( vga, "ext" ) == 0 ) { |
---|
260 | bzimg->vid_mode = BZI_VID_MODE_EXT; |
---|
261 | } else if ( strcmp ( vga, "ask" ) == 0 ) { |
---|
262 | bzimg->vid_mode = BZI_VID_MODE_ASK; |
---|
263 | } else { |
---|
264 | bzimg->vid_mode = strtoul ( vga, &vga, 0 ); |
---|
265 | if ( *vga && ( *vga != ' ' ) ) { |
---|
266 | DBGC ( image, "bzImage %p strange \"vga=\"" |
---|
267 | "terminator '%c'\n", image, *vga ); |
---|
268 | } |
---|
269 | } |
---|
270 | } |
---|
271 | |
---|
272 | /* Look for "mem=" */ |
---|
273 | if ( ( mem = strstr ( cmdline, "mem=" ) ) ) { |
---|
274 | mem += 4; |
---|
275 | bzimg->mem_limit = strtoul ( mem, &mem, 0 ); |
---|
276 | switch ( *mem ) { |
---|
277 | case 'G': |
---|
278 | case 'g': |
---|
279 | bzimg->mem_limit <<= 10; |
---|
280 | case 'M': |
---|
281 | case 'm': |
---|
282 | bzimg->mem_limit <<= 10; |
---|
283 | case 'K': |
---|
284 | case 'k': |
---|
285 | bzimg->mem_limit <<= 10; |
---|
286 | break; |
---|
287 | case '\0': |
---|
288 | case ' ': |
---|
289 | break; |
---|
290 | default: |
---|
291 | DBGC ( image, "bzImage %p strange \"mem=\" " |
---|
292 | "terminator '%c'\n", image, *mem ); |
---|
293 | break; |
---|
294 | } |
---|
295 | bzimg->mem_limit -= 1; |
---|
296 | } |
---|
297 | |
---|
298 | return 0; |
---|
299 | } |
---|
300 | |
---|
301 | /** |
---|
302 | * Set command line |
---|
303 | * |
---|
304 | * @v image bzImage image |
---|
305 | * @v bzimg bzImage context |
---|
306 | * @v cmdline Kernel command line |
---|
307 | * @ret rc Return status code |
---|
308 | */ |
---|
309 | static int bzimage_set_cmdline ( struct image *image, |
---|
310 | struct bzimage_context *bzimg, |
---|
311 | const char *cmdline ) { |
---|
312 | size_t cmdline_len; |
---|
313 | |
---|
314 | /* Copy command line down to real-mode portion */ |
---|
315 | cmdline_len = ( strlen ( cmdline ) + 1 ); |
---|
316 | if ( cmdline_len > bzimg->cmdline_size ) |
---|
317 | cmdline_len = bzimg->cmdline_size; |
---|
318 | copy_to_user ( bzimg->rm_kernel, bzimg->rm_cmdline, |
---|
319 | cmdline, cmdline_len ); |
---|
320 | DBGC ( image, "bzImage %p command line \"%s\"\n", image, cmdline ); |
---|
321 | |
---|
322 | return 0; |
---|
323 | } |
---|
324 | |
---|
325 | /** |
---|
326 | * Load initrd |
---|
327 | * |
---|
328 | * @v image bzImage image |
---|
329 | * @v initrd initrd image |
---|
330 | * @v address Address at which to load, or UNULL |
---|
331 | * @ret len Length of loaded image, rounded up to 4 bytes |
---|
332 | */ |
---|
333 | static size_t bzimage_load_initrd ( struct image *image, |
---|
334 | struct image *initrd, |
---|
335 | userptr_t address ) { |
---|
336 | char *filename = initrd->cmdline; |
---|
337 | struct cpio_header cpio; |
---|
338 | size_t offset = 0; |
---|
339 | |
---|
340 | /* Do not include kernel image itself as an initrd */ |
---|
341 | if ( initrd == image ) |
---|
342 | return 0; |
---|
343 | |
---|
344 | /* Create cpio header before non-prebuilt images */ |
---|
345 | if ( filename && filename[0] ) { |
---|
346 | size_t name_len = ( strlen ( filename ) + 1 ); |
---|
347 | |
---|
348 | DBGC ( image, "bzImage %p inserting initrd %p as %s\n", |
---|
349 | image, initrd, filename ); |
---|
350 | memset ( &cpio, '0', sizeof ( cpio ) ); |
---|
351 | memcpy ( cpio.c_magic, CPIO_MAGIC, sizeof ( cpio.c_magic ) ); |
---|
352 | cpio_set_field ( cpio.c_mode, 0100644 ); |
---|
353 | cpio_set_field ( cpio.c_nlink, 1 ); |
---|
354 | cpio_set_field ( cpio.c_filesize, initrd->len ); |
---|
355 | cpio_set_field ( cpio.c_namesize, name_len ); |
---|
356 | if ( address ) { |
---|
357 | copy_to_user ( address, offset, &cpio, |
---|
358 | sizeof ( cpio ) ); |
---|
359 | } |
---|
360 | offset += sizeof ( cpio ); |
---|
361 | if ( address ) { |
---|
362 | copy_to_user ( address, offset, filename, |
---|
363 | name_len ); |
---|
364 | } |
---|
365 | offset += name_len; |
---|
366 | offset = ( ( offset + 0x03 ) & ~0x03 ); |
---|
367 | } |
---|
368 | |
---|
369 | /* Copy in initrd image body */ |
---|
370 | if ( address ) |
---|
371 | memcpy_user ( address, offset, initrd->data, 0, initrd->len ); |
---|
372 | offset += initrd->len; |
---|
373 | if ( address ) { |
---|
374 | DBGC ( image, "bzImage %p has initrd %p at [%lx,%lx)\n", |
---|
375 | image, initrd, user_to_phys ( address, 0 ), |
---|
376 | user_to_phys ( address, offset ) ); |
---|
377 | } |
---|
378 | |
---|
379 | /* Round up to 4-byte boundary */ |
---|
380 | offset = ( ( offset + 0x03 ) & ~0x03 ); |
---|
381 | return offset; |
---|
382 | } |
---|
383 | |
---|
384 | /** |
---|
385 | * Load initrds, if any |
---|
386 | * |
---|
387 | * @v image bzImage image |
---|
388 | * @v bzimg bzImage context |
---|
389 | * @ret rc Return status code |
---|
390 | */ |
---|
391 | static int bzimage_load_initrds ( struct image *image, |
---|
392 | struct bzimage_context *bzimg ) { |
---|
393 | struct image *initrd; |
---|
394 | size_t total_len = 0; |
---|
395 | physaddr_t address; |
---|
396 | int rc; |
---|
397 | |
---|
398 | /* Add up length of all initrd images */ |
---|
399 | for_each_image ( initrd ) |
---|
400 | total_len += bzimage_load_initrd ( image, initrd, UNULL ); |
---|
401 | |
---|
402 | /* Give up if no initrd images found */ |
---|
403 | if ( ! total_len ) |
---|
404 | return 0; |
---|
405 | |
---|
406 | /* Find a suitable start address. Try 1MB boundaries, |
---|
407 | * starting from the downloaded kernel image itself and |
---|
408 | * working downwards until we hit an available region. |
---|
409 | */ |
---|
410 | for ( address = ( user_to_phys ( image->data, 0 ) & ~0xfffff ) ; ; |
---|
411 | address -= 0x100000 ) { |
---|
412 | /* Check that we're not going to overwrite the |
---|
413 | * kernel itself. This check isn't totally |
---|
414 | * accurate, but errs on the side of caution. |
---|
415 | */ |
---|
416 | if ( address <= ( BZI_LOAD_HIGH_ADDR + image->len ) ) { |
---|
417 | DBGC ( image, "bzImage %p could not find a location " |
---|
418 | "for initrd\n", image ); |
---|
419 | return -ENOBUFS; |
---|
420 | } |
---|
421 | /* Check that we are within the kernel's range */ |
---|
422 | if ( ( address + total_len - 1 ) > bzimg->mem_limit ) |
---|
423 | continue; |
---|
424 | /* Prepare and verify segment */ |
---|
425 | if ( ( rc = prep_segment ( phys_to_user ( address ), 0, |
---|
426 | total_len ) ) != 0 ) |
---|
427 | continue; |
---|
428 | /* Use this address */ |
---|
429 | break; |
---|
430 | } |
---|
431 | |
---|
432 | /* Record initrd location */ |
---|
433 | bzimg->ramdisk_image = address; |
---|
434 | bzimg->ramdisk_size = total_len; |
---|
435 | |
---|
436 | /* Construct initrd */ |
---|
437 | DBGC ( image, "bzImage %p constructing initrd at [%lx,%lx)\n", |
---|
438 | image, address, ( address + total_len ) ); |
---|
439 | for_each_image ( initrd ) { |
---|
440 | address += bzimage_load_initrd ( image, initrd, |
---|
441 | phys_to_user ( address ) ); |
---|
442 | } |
---|
443 | |
---|
444 | return 0; |
---|
445 | } |
---|
446 | |
---|
447 | /** |
---|
448 | * Execute bzImage image |
---|
449 | * |
---|
450 | * @v image bzImage image |
---|
451 | * @ret rc Return status code |
---|
452 | */ |
---|
453 | static int bzimage_exec ( struct image *image ) { |
---|
454 | struct bzimage_context bzimg; |
---|
455 | const char *cmdline = ( image->cmdline ? image->cmdline : "" ); |
---|
456 | int rc; |
---|
457 | |
---|
458 | /* Read and parse header from loaded kernel */ |
---|
459 | if ( ( rc = bzimage_parse_header ( image, &bzimg, |
---|
460 | image->priv.user ) ) != 0 ) |
---|
461 | return rc; |
---|
462 | assert ( bzimg.rm_kernel == image->priv.user ); |
---|
463 | |
---|
464 | /* Parse command line for bootloader parameters */ |
---|
465 | if ( ( rc = bzimage_parse_cmdline ( image, &bzimg, cmdline ) ) != 0) |
---|
466 | return rc; |
---|
467 | |
---|
468 | /* Store command line */ |
---|
469 | if ( ( rc = bzimage_set_cmdline ( image, &bzimg, cmdline ) ) != 0 ) |
---|
470 | return rc; |
---|
471 | |
---|
472 | /* Load any initrds */ |
---|
473 | if ( ( rc = bzimage_load_initrds ( image, &bzimg ) ) != 0 ) |
---|
474 | return rc; |
---|
475 | |
---|
476 | /* Update kernel header */ |
---|
477 | bzimage_update_header ( image, &bzimg, bzimg.rm_kernel ); |
---|
478 | |
---|
479 | /* Prepare for exiting */ |
---|
480 | shutdown ( SHUTDOWN_BOOT ); |
---|
481 | |
---|
482 | DBGC ( image, "bzImage %p jumping to RM kernel at %04x:0000 " |
---|
483 | "(stack %04x:%04zx)\n", image, ( bzimg.rm_kernel_seg + 0x20 ), |
---|
484 | bzimg.rm_kernel_seg, bzimg.rm_heap ); |
---|
485 | |
---|
486 | /* Jump to the kernel */ |
---|
487 | __asm__ __volatile__ ( REAL_CODE ( "movw %w0, %%ds\n\t" |
---|
488 | "movw %w0, %%es\n\t" |
---|
489 | "movw %w0, %%fs\n\t" |
---|
490 | "movw %w0, %%gs\n\t" |
---|
491 | "movw %w0, %%ss\n\t" |
---|
492 | "movw %w1, %%sp\n\t" |
---|
493 | "pushw %w2\n\t" |
---|
494 | "pushw $0\n\t" |
---|
495 | "lret\n\t" ) |
---|
496 | : : "r" ( bzimg.rm_kernel_seg ), |
---|
497 | "r" ( bzimg.rm_heap ), |
---|
498 | "r" ( bzimg.rm_kernel_seg + 0x20 ) ); |
---|
499 | |
---|
500 | /* There is no way for the image to return, since we provide |
---|
501 | * no return address. |
---|
502 | */ |
---|
503 | assert ( 0 ); |
---|
504 | |
---|
505 | return -ECANCELED; /* -EIMPOSSIBLE */ |
---|
506 | } |
---|
507 | |
---|
508 | /** |
---|
509 | * Load bzImage image into memory |
---|
510 | * |
---|
511 | * @v image bzImage file |
---|
512 | * @ret rc Return status code |
---|
513 | */ |
---|
514 | int bzimage_load ( struct image *image ) { |
---|
515 | struct bzimage_context bzimg; |
---|
516 | int rc; |
---|
517 | |
---|
518 | /* Read and parse header from image */ |
---|
519 | if ( ( rc = bzimage_parse_header ( image, &bzimg, |
---|
520 | image->data ) ) != 0 ) |
---|
521 | return rc; |
---|
522 | |
---|
523 | /* This is a bzImage image, valid or otherwise */ |
---|
524 | if ( ! image->type ) |
---|
525 | image->type = &bzimage_image_type; |
---|
526 | |
---|
527 | /* Prepare segments */ |
---|
528 | if ( ( rc = prep_segment ( bzimg.rm_kernel, bzimg.rm_filesz, |
---|
529 | bzimg.rm_memsz ) ) != 0 ) { |
---|
530 | DBGC ( image, "bzImage %p could not prepare RM segment: %s\n", |
---|
531 | image, strerror ( rc ) ); |
---|
532 | return rc; |
---|
533 | } |
---|
534 | if ( ( rc = prep_segment ( bzimg.pm_kernel, bzimg.pm_sz, |
---|
535 | bzimg.pm_sz ) ) != 0 ) { |
---|
536 | DBGC ( image, "bzImage %p could not prepare PM segment: %s\n", |
---|
537 | image, strerror ( rc ) ); |
---|
538 | return rc; |
---|
539 | } |
---|
540 | |
---|
541 | /* Load segments */ |
---|
542 | memcpy_user ( bzimg.rm_kernel, 0, image->data, |
---|
543 | 0, bzimg.rm_filesz ); |
---|
544 | memcpy_user ( bzimg.pm_kernel, 0, image->data, |
---|
545 | bzimg.rm_filesz, bzimg.pm_sz ); |
---|
546 | |
---|
547 | /* Update and write out header */ |
---|
548 | bzimage_update_header ( image, &bzimg, bzimg.rm_kernel ); |
---|
549 | |
---|
550 | /* Record real-mode segment in image private data field */ |
---|
551 | image->priv.user = bzimg.rm_kernel; |
---|
552 | |
---|
553 | return 0; |
---|
554 | } |
---|
555 | |
---|
556 | /** Linux bzImage image type */ |
---|
557 | struct image_type bzimage_image_type __image_type ( PROBE_NORMAL ) = { |
---|
558 | .name = "bzImage", |
---|
559 | .load = bzimage_load, |
---|
560 | .exec = bzimage_exec, |
---|
561 | }; |
---|