iPXE
comboot.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2008 Daniel Verkamp <daniel@drv.nu>.
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., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  */
19 
20 /**
21  * @file
22  *
23  * SYSLINUX COMBOOT (16-bit) image format
24  *
25  */
26 
27 FILE_LICENCE ( GPL2_OR_LATER );
28 
29 #include <stdint.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <strings.h>
33 #include <errno.h>
34 #include <assert.h>
35 #include <realmode.h>
36 #include <basemem.h>
37 #include <comboot.h>
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>
43 #include <ipxe/console.h>
44 
46 
47 /**
48  * COMBOOT PSP, copied to offset 0 of code segment
49  */
50 struct comboot_psp {
51  /** INT 20 instruction, executed if COMBOOT image returns with RET */
53  /** Segment of first non-free paragraph of memory */
55 };
56 
57 /** Offset in PSP of command line */
58 #define COMBOOT_PSP_CMDLINE_OFFSET 0x81
59 
60 /** Maximum length of command line in PSP
61  * (127 bytes minus space and CR) */
62 #define COMBOOT_MAX_CMDLINE_LEN 125
63 
64 
65 /**
66  * Copy command line to PSP
67  *
68  * @v image COMBOOT image
69  */
70 static void comboot_copy_cmdline ( struct image * image, userptr_t seg_userptr ) {
71  const char *cmdline = ( image->cmdline ? image->cmdline : "" );
72  int cmdline_len = strlen ( cmdline );
73  if( cmdline_len > COMBOOT_MAX_CMDLINE_LEN )
74  cmdline_len = COMBOOT_MAX_CMDLINE_LEN;
75  uint8_t len_byte = cmdline_len;
76  char spc = ' ', cr = '\r';
77 
78  /* Copy length to byte before command line */
79  copy_to_user ( seg_userptr, COMBOOT_PSP_CMDLINE_OFFSET - 1,
80  &len_byte, 1 );
81 
82  /* Command line starts with space */
83  copy_to_user ( seg_userptr,
85  &spc, 1 );
86 
87  /* Copy command line */
88  copy_to_user ( seg_userptr,
90  cmdline, cmdline_len );
91 
92  /* Command line ends with CR */
93  copy_to_user ( seg_userptr,
94  COMBOOT_PSP_CMDLINE_OFFSET + cmdline_len + 1,
95  &cr, 1 );
96 }
97 
98 /**
99  * Initialize PSP
100  *
101  * @v image COMBOOT image
102  * @v seg_userptr segment to initialize
103  */
104 static void comboot_init_psp ( struct image * image, userptr_t seg_userptr ) {
105  struct comboot_psp psp;
106 
107  /* Fill PSP */
108 
109  /* INT 20h instruction, byte order reversed */
110  psp.int20 = 0x20CD;
111 
112  /* get_fbms() returns BIOS free base memory counter, which is in
113  * kilobytes; x * 1024 / 16 == x * 64 == x << 6 */
114  psp.first_non_free_para = get_fbms() << 6;
115 
116  DBGC ( image, "COMBOOT %p: first non-free paragraph = 0x%x\n",
117  image, psp.first_non_free_para );
118 
119  /* Copy the PSP to offset 0 of segment.
120  * The rest of the PSP was already zeroed by
121  * comboot_prepare_segment. */
122  copy_to_user ( seg_userptr, 0, &psp, sizeof( psp ) );
123 
124  /* Copy the command line to the PSP */
125  comboot_copy_cmdline ( image, seg_userptr );
126 }
127 
128 /**
129  * Execute COMBOOT image
130  *
131  * @v image COMBOOT image
132  * @ret rc Return status code
133  */
134 static int comboot_exec_loop ( struct image *image ) {
135  userptr_t seg_userptr = real_to_user ( COMBOOT_PSP_SEG, 0 );
136  int state;
137 
139 
140  switch ( state ) {
141  case 0: /* First time through; invoke COMBOOT program */
142 
143  /* Initialize PSP */
144  comboot_init_psp ( image, seg_userptr );
145 
146  /* Hook COMBOOT API interrupts */
148 
149  DBGC ( image, "executing 16-bit COMBOOT image at %4x:0100\n",
150  COMBOOT_PSP_SEG );
151 
152  /* Unregister image, so that a "boot" command doesn't
153  * throw us into an execution loop. We never
154  * reregister ourselves; COMBOOT images expect to be
155  * removed on exit.
156  */
158 
159  /* Store stack segment at 0x38 and stack pointer at 0x3A
160  * in the PSP and jump to the image */
162  REAL_CODE ( /* Save return address with segment on old stack */
163  "popw %%ax\n\t"
164  "pushw %%cs\n\t"
165  "pushw %%ax\n\t"
166  /* Set DS=ES=segment with image */
167  "movw %w0, %%ds\n\t"
168  "movw %w0, %%es\n\t"
169  /* Set SS:SP to new stack (end of image segment) */
170  "movw %w0, %%ss\n\t"
171  "xor %%sp, %%sp\n\t"
172  "pushw $0\n\t"
173  "pushw %w0\n\t"
174  "pushw $0x100\n\t"
175  /* Zero registers (some COM files assume GP regs are 0) */
176  "xorw %%ax, %%ax\n\t"
177  "xorw %%bx, %%bx\n\t"
178  "xorw %%cx, %%cx\n\t"
179  "xorw %%dx, %%dx\n\t"
180  "xorw %%si, %%si\n\t"
181  "xorw %%di, %%di\n\t"
182  "xorw %%bp, %%bp\n\t"
183  "lret\n\t" )
184  : : "r" ( COMBOOT_PSP_SEG ) : "eax" );
185  DBGC ( image, "COMBOOT %p: returned\n", image );
186  break;
187 
188  case COMBOOT_EXIT:
189  DBGC ( image, "COMBOOT %p: exited\n", image );
190  break;
191 
193  assert ( image->replacement );
194  DBGC ( image, "COMBOOT %p: exited to run kernel %s\n",
196  break;
197 
199  DBGC ( image, "COMBOOT %p: exited after executing command\n",
200  image );
201  break;
202 
203  default:
204  assert ( 0 );
205  break;
206  }
207 
210 
211  return 0;
212 }
213 
214 /**
215  * Check image name extension
216  *
217  * @v image COMBOOT image
218  * @ret rc Return status code
219  */
220 static int comboot_identify ( struct image *image ) {
221  const char *ext;
222 
223  ext = strrchr( image->name, '.' );
224 
225  if ( ! ext ) {
226  DBGC ( image, "COMBOOT %p: no extension\n",
227  image );
228  return -ENOEXEC;
229  }
230 
231  ++ext;
232 
233  if ( strcasecmp( ext, "cbt" ) ) {
234  DBGC ( image, "COMBOOT %p: unrecognized extension %s\n",
235  image, ext );
236  return -ENOEXEC;
237  }
238 
239  return 0;
240 }
241 
242 /**
243  * Load COMBOOT image into memory, preparing a segment and returning it
244  * @v image COMBOOT image
245  * @ret rc Return status code
246  */
247 static int comboot_prepare_segment ( struct image *image )
248 {
249  userptr_t seg_userptr;
250  size_t filesz, memsz;
251  int rc;
252 
253  /* Load image in segment */
254  seg_userptr = real_to_user ( COMBOOT_PSP_SEG, 0 );
255 
256  /* Allow etra 0x100 bytes before image for PSP */
257  filesz = image->len + 0x100;
258 
259  /* Ensure the entire 64k segment is free */
260  memsz = 0xFFFF;
261 
262  /* Prepare, verify, and load the real-mode segment */
263  if ( ( rc = prep_segment ( seg_userptr, filesz, memsz ) ) != 0 ) {
264  DBGC ( image, "COMBOOT %p: could not prepare segment: %s\n",
265  image, strerror ( rc ) );
266  return rc;
267  }
268 
269  /* Zero PSP */
270  memset_user ( seg_userptr, 0, 0, 0x100 );
271 
272  /* Copy image to segment:0100 */
273  memcpy_user ( seg_userptr, 0x100, image->data, 0, image->len );
274 
275  return 0;
276 }
277 
278 /**
279  * Probe COMBOOT image
280  *
281  * @v image COMBOOT image
282  * @ret rc Return status code
283  */
284 static int comboot_probe ( struct image *image ) {
285  int rc;
286 
287  DBGC ( image, "COMBOOT %p: name '%s'\n",
288  image, image->name );
289 
290  /* Check if this is a COMBOOT image */
291  if ( ( rc = comboot_identify ( image ) ) != 0 ) {
292 
293  return rc;
294  }
295 
296  return 0;
297 }
298 
299 /**
300  * Execute COMBOOT image
301  *
302  * @v image COMBOOT image
303  * @ret rc Return status code
304  */
305 static int comboot_exec ( struct image *image ) {
306  int rc;
307 
308  /* Sanity check for filesize */
309  if( image->len >= 0xFF00 ) {
310  DBGC( image, "COMBOOT %p: image too large\n",
311  image );
312  return -ENOEXEC;
313  }
314 
315  /* Prepare segment and load image */
316  if ( ( rc = comboot_prepare_segment ( image ) ) != 0 ) {
317  return rc;
318  }
319 
320  /* Reset console */
321  console_reset();
322 
323  return comboot_exec_loop ( image );
324 }
325 
326 /** SYSLINUX COMBOOT (16-bit) image type */
327 struct image_type comboot_image_type __image_type ( PROBE_NORMAL ) = {
328  .name = "COMBOOT",
329  .probe = comboot_probe,
330  .exec = comboot_exec,
331 };
uint16_t int20
INT 20 instruction, executed if COMBOOT image returns with RET.
Definition: comboot.c:52
static int comboot_identify(struct image *image)
Check image name extension.
Definition: comboot.c:220
#define COMBOOT_PSP_CMDLINE_OFFSET
Offset in PSP of command line.
Definition: comboot.c:58
struct arbelprm_rc_send_wqe rc
Definition: arbel.h:14
unsigned short uint16_t
Definition: stdint.h:11
userptr_t data
Raw file image.
Definition: image.h:41
static unsigned int get_fbms(void)
Read the BIOS free base memory counter.
Definition: basemem.h:21
uint8_t state
State.
Definition: eth_slow.h:47
char * strrchr(const char *src, int character)
Find rightmost character within a string.
Definition: string.c:289
Error codes.
#define COMBOOT_PSP_SEG
Segment used for COMBOOT PSP and image.
Definition: comboot.h:17
#define ENOEXEC
Exec format error.
Definition: errno.h:519
Base memory allocation.
#define DBGC(...)
Definition: compiler.h:505
static void comboot_init_psp(struct image *image, userptr_t seg_userptr)
Initialize PSP.
Definition: comboot.c:104
An executable image type.
Definition: image.h:76
#define COMBOOT_MAX_CMDLINE_LEN
Maximum length of command line in PSP (127 bytes minus space and CR)
Definition: comboot.c:62
#define PROBE_NORMAL
Normal image probe priority.
Definition: image.h:137
int strcasecmp(const char *first, const char *second)
Compare case-insensitive strings.
Definition: string.c:208
SYSLINUX COMBOOT.
An executable image.
Definition: image.h:24
#define FEATURE_IMAGE
Image formats.
Definition: features.h:22
static int comboot_probe(struct image *image)
Probe COMBOOT image.
Definition: comboot.c:284
Access to external ("user") memory.
char * name
Name of this image type.
Definition: image.h:78
char * cmdline
Command line to pass to image.
Definition: image.h:39
static int comboot_exec_loop(struct image *image)
Execute COMBOOT image.
Definition: comboot.c:134
Executable image segments.
void memset_user(userptr_t userptr, off_t offset, int c, size_t len)
Fill user buffer with a constant byte.
FILE_LICENCE(GPL2_OR_LATER)
#define DHCP_EB_FEATURE_COMBOOT
COMBOOT format.
Definition: features.h:51
#define COMBOOT_EXIT
Definition: comboot.h:119
Assertions.
assert((readw(&hdr->flags) &(GTF_reading|GTF_writing))==0)
int prep_segment(userptr_t segment, size_t filesz, size_t memsz)
Prepare segment for loading.
Definition: segment.c:60
rmjmp_buf comboot_return
Definition: comboot_call.c:82
Executable images.
struct image_type comboot_image_type __image_type(PROBE_NORMAL)
SYSLINUX COMBOOT (16-bit) image type.
void comboot_force_text_mode(void)
Set default text mode.
Definition: comboot_call.c:140
Feature list.
User interaction.
static void comboot_copy_cmdline(struct image *image, userptr_t seg_userptr)
Copy command line to PSP.
Definition: comboot.c:70
char * strerror(int errno)
Retrieve string representation of error number.
Definition: strerror.c:78
uint16_t first_non_free_para
Segment of first non-free paragraph of memory.
Definition: comboot.c:54
void unhook_comboot_interrupts()
Unhook BIOS interrupts related to COMBOOT API (INT 20h, 21h, 22h)
Definition: comboot_call.c:692
size_t len
Length of raw file image.
Definition: image.h:43
static __always_inline void copy_to_user(userptr_t dest, off_t dest_off, const void *src, size_t len)
Copy data to user buffer.
Definition: uaccess.h:324
size_t strlen(const char *src)
Get length of string.
Definition: string.c:243
unsigned char uint8_t
Definition: stdint.h:10
static int comboot_prepare_segment(struct image *image)
Load COMBOOT image into memory, preparing a segment and returning it.
Definition: comboot.c:247
static void console_reset(void)
Reset console.
Definition: console.h:214
__asm__ __volatile__("\n1:\n\t" "movb -1(%3,%1), %%al\n\t" "stosb\n\t" "loop 1b\n\t" "xorl %%eax, %%eax\n\t" "mov %4, %1\n\t" "rep stosb\n\t" :"=&D"(discard_D), "=&c"(discard_c), "+m"(*value) :"r"(data), "g"(pad_len), "0"(value0), "1"(len) :"eax")
uint16_t ext
Extended status.
Definition: ena.h:20
#define COMBOOT_EXIT_RUN_KERNEL
Definition: comboot.h:120
COMBOOT PSP, copied to offset 0 of code segment.
Definition: comboot.c:50
Definition: sis900.h:22
void unregister_image(struct image *image)
Unregister executable image.
Definition: image.c:300
__asm__(".section \".rodata\", \"a\", " PROGBITS "\n\t" "\nprivate_key_data:\n\t" ".size private_key_data, ( . - private_key_data )\n\t" ".equ private_key_len, ( . - private_key_data )\n\t" ".previous\n\t")
void hook_comboot_interrupts()
Hook BIOS interrupts related to COMBOOT API (INT 20h, 21h, 22h)
Definition: comboot_call.c:659
FEATURE(FEATURE_IMAGE, "COMBOOT", DHCP_EB_FEATURE_COMBOOT, 1)
#define COMBOOT_EXIT_COMMAND
Definition: comboot.h:121
#define rmsetjmp(_env)
Definition: rmsetjmp.h:17
static int comboot_exec(struct image *image)
Execute COMBOOT image.
Definition: comboot.c:305
struct image * replacement
Replacement image.
Definition: image.h:60
static __always_inline userptr_t real_to_user(unsigned int segment, unsigned int offset)
Convert segment:offset address to user buffer.
Definition: realmode.h:75
uint32_t cmdline
Definition: multiboot.h:16
#define REAL_CODE(asm_code_str)
Definition: libkir.h:226
char * name
Name.
Definition: image.h:34
String functions.
void memcpy_user(userptr_t dest, off_t dest_off, userptr_t src, off_t src_off, size_t len)
Copy data between user buffers.
unsigned long userptr_t
A pointer to a user buffer.
Definition: uaccess.h:33
String functions.