2 * Copyright (C) 2008 Daniel Verkamp <daniel@drv.nu>.
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA
23 * SYSLINUX COMBOOT (16-bit) image format
27 FILE_LICENCE ( GPL2_OR_LATER );
38 #include <ipxe/uaccess.h>
39 #include <ipxe/image.h>
40 #include <ipxe/segment.h>
41 #include <ipxe/init.h>
42 #include <ipxe/features.h>
44 FEATURE ( FEATURE_IMAGE, "COMBOOT", DHCP_EB_FEATURE_COMBOOT, 1 );
47 * COMBOOT PSP, copied to offset 0 of code segment
50 /** INT 20 instruction, executed if COMBOOT image returns with RET */
52 /** Segment of first non-free paragraph of memory */
53 uint16_t first_non_free_para;
56 /** Offset in PSP of command line */
57 #define COMBOOT_PSP_CMDLINE_OFFSET 0x81
59 /** Maximum length of command line in PSP
60 * (127 bytes minus space and CR) */
61 #define COMBOOT_MAX_CMDLINE_LEN 125
65 * Copy command line to PSP
67 * @v image COMBOOT image
69 static void comboot_copy_cmdline ( struct image * image, userptr_t seg_userptr ) {
70 const char *cmdline = ( image->cmdline ? image->cmdline : "" );
71 int cmdline_len = strlen ( cmdline );
72 if( cmdline_len > COMBOOT_MAX_CMDLINE_LEN )
73 cmdline_len = COMBOOT_MAX_CMDLINE_LEN;
74 uint8_t len_byte = cmdline_len;
75 char spc = ' ', cr = '\r';
77 /* Copy length to byte before command line */
78 copy_to_user ( seg_userptr, COMBOOT_PSP_CMDLINE_OFFSET - 1,
81 /* Command line starts with space */
82 copy_to_user ( seg_userptr,
83 COMBOOT_PSP_CMDLINE_OFFSET,
86 /* Copy command line */
87 copy_to_user ( seg_userptr,
88 COMBOOT_PSP_CMDLINE_OFFSET + 1,
89 cmdline, cmdline_len );
91 /* Command line ends with CR */
92 copy_to_user ( seg_userptr,
93 COMBOOT_PSP_CMDLINE_OFFSET + cmdline_len + 1,
100 * @v image COMBOOT image
101 * @v seg_userptr segment to initialize
103 static void comboot_init_psp ( struct image * image, userptr_t seg_userptr ) {
104 struct comboot_psp psp;
108 /* INT 20h instruction, byte order reversed */
111 /* get_fbms() returns BIOS free base memory counter, which is in
112 * kilobytes; x * 1024 / 16 == x * 64 == x << 6 */
113 psp.first_non_free_para = get_fbms() << 6;
115 DBGC ( image, "COMBOOT %p: first non-free paragraph = 0x%x\n",
116 image, psp.first_non_free_para );
118 /* Copy the PSP to offset 0 of segment.
119 * The rest of the PSP was already zeroed by
120 * comboot_prepare_segment. */
121 copy_to_user ( seg_userptr, 0, &psp, sizeof( psp ) );
123 /* Copy the command line to the PSP */
124 comboot_copy_cmdline ( image, seg_userptr );
128 * Execute COMBOOT image
130 * @v image COMBOOT image
131 * @ret rc Return status code
133 static int comboot_exec_loop ( struct image *image ) {
134 userptr_t seg_userptr = real_to_user ( COMBOOT_PSP_SEG, 0 );
137 state = rmsetjmp ( comboot_return );
140 case 0: /* First time through; invoke COMBOOT program */
143 comboot_init_psp ( image, seg_userptr );
145 /* Hook COMBOOT API interrupts */
146 hook_comboot_interrupts();
148 DBGC ( image, "executing 16-bit COMBOOT image at %4x:0100\n",
151 /* Unregister image, so that a "boot" command doesn't
152 * throw us into an execution loop. We never
153 * reregister ourselves; COMBOOT images expect to be
156 unregister_image ( image );
158 /* Store stack segment at 0x38 and stack pointer at 0x3A
159 * in the PSP and jump to the image */
160 __asm__ __volatile__ (
161 REAL_CODE ( /* Save return address with segment on old stack */
165 /* Set DS=ES=segment with image */
168 /* Set SS:SP to new stack (end of image segment) */
174 /* Zero registers (some COM files assume GP regs are 0) */
175 "xorw %%ax, %%ax\n\t"
176 "xorw %%bx, %%bx\n\t"
177 "xorw %%cx, %%cx\n\t"
178 "xorw %%dx, %%dx\n\t"
179 "xorw %%si, %%si\n\t"
180 "xorw %%di, %%di\n\t"
181 "xorw %%bp, %%bp\n\t"
183 : : "r" ( COMBOOT_PSP_SEG ) : "eax" );
184 DBGC ( image, "COMBOOT %p: returned\n", image );
188 DBGC ( image, "COMBOOT %p: exited\n", image );
191 case COMBOOT_EXIT_RUN_KERNEL:
192 assert ( image->replacement );
193 DBGC ( image, "COMBOOT %p: exited to run kernel %s\n",
194 image, image->replacement->name );
197 case COMBOOT_EXIT_COMMAND:
198 DBGC ( image, "COMBOOT %p: exited after executing command\n",
207 unhook_comboot_interrupts();
208 comboot_force_text_mode();
214 * Check image name extension
216 * @v image COMBOOT image
217 * @ret rc Return status code
219 static int comboot_identify ( struct image *image ) {
222 ext = strrchr( image->name, '.' );
225 DBGC ( image, "COMBOOT %p: no extension\n",
232 if ( strcasecmp( ext, "cbt" ) ) {
233 DBGC ( image, "COMBOOT %p: unrecognized extension %s\n",
242 * Load COMBOOT image into memory, preparing a segment and returning it
243 * @v image COMBOOT image
244 * @ret rc Return status code
246 static int comboot_prepare_segment ( struct image *image )
248 userptr_t seg_userptr;
249 size_t filesz, memsz;
252 /* Load image in segment */
253 seg_userptr = real_to_user ( COMBOOT_PSP_SEG, 0 );
255 /* Allow etra 0x100 bytes before image for PSP */
256 filesz = image->len + 0x100;
258 /* Ensure the entire 64k segment is free */
261 /* Prepare, verify, and load the real-mode segment */
262 if ( ( rc = prep_segment ( seg_userptr, filesz, memsz ) ) != 0 ) {
263 DBGC ( image, "COMBOOT %p: could not prepare segment: %s\n",
264 image, strerror ( rc ) );
269 memset_user ( seg_userptr, 0, 0, 0x100 );
271 /* Copy image to segment:0100 */
272 memcpy_user ( seg_userptr, 0x100, image->data, 0, image->len );
278 * Probe COMBOOT image
280 * @v image COMBOOT image
281 * @ret rc Return status code
283 static int comboot_probe ( struct image *image ) {
286 DBGC ( image, "COMBOOT %p: name '%s'\n",
287 image, image->name );
289 /* Check if this is a COMBOOT image */
290 if ( ( rc = comboot_identify ( image ) ) != 0 ) {
299 * Execute COMBOOT image
301 * @v image COMBOOT image
302 * @ret rc Return status code
304 static int comboot_exec ( struct image *image ) {
307 /* Sanity check for filesize */
308 if( image->len >= 0xFF00 ) {
309 DBGC( image, "COMBOOT %p: image too large\n",
314 /* Prepare segment and load image */
315 if ( ( rc = comboot_prepare_segment ( image ) ) != 0 ) {
319 return comboot_exec_loop ( image );
322 /** SYSLINUX COMBOOT (16-bit) image type */
323 struct image_type comboot_image_type __image_type ( PROBE_NORMAL ) = {
325 .probe = comboot_probe,
326 .exec = comboot_exec,