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/image.h>
39 #include <ipxe/segment.h>
40 #include <ipxe/init.h>
41 #include <ipxe/features.h>
42 #include <ipxe/console.h>
43 
45 
46 /**
47  * COMBOOT PSP, copied to offset 0 of code segment
48  */
49 struct comboot_psp {
50  /** INT 20 instruction, executed if COMBOOT image returns with RET */
52  /** Segment of first non-free paragraph of memory */
54 };
55 
56 /** Offset in PSP of command line */
57 #define COMBOOT_PSP_CMDLINE_OFFSET 0x81
58 
59 /** Maximum length of command line in PSP
60  * (127 bytes minus space and CR) */
61 #define COMBOOT_MAX_CMDLINE_LEN 125
62 
63 
64 /**
65  * Copy command line to PSP
66  *
67  * @v image COMBOOT image
68  */
69 static void comboot_copy_cmdline ( struct image * image, void *seg ) {
70  const char *cmdline = ( image->cmdline ? image->cmdline : "" );
71  int cmdline_len = strlen ( cmdline );
72  uint8_t *psp_cmdline;
73 
74  /* Limit length of command line */
75  if( cmdline_len > COMBOOT_MAX_CMDLINE_LEN )
76  cmdline_len = COMBOOT_MAX_CMDLINE_LEN;
77 
78  /* Copy length to byte before command line */
79  psp_cmdline = ( seg + COMBOOT_PSP_CMDLINE_OFFSET );
80  psp_cmdline[-1] = cmdline_len;
81 
82  /* Command line starts with space */
83  psp_cmdline[0] = ' ';
84 
85  /* Copy command line */
86  memcpy ( &psp_cmdline[1], cmdline, cmdline_len );
87 
88  /* Command line ends with CR */
89  psp_cmdline[ 1 + cmdline_len ] = '\r';
90 }
91 
92 /**
93  * Initialize PSP
94  *
95  * @v image COMBOOT image
96  * @v seg segment to initialize
97  */
98 static void comboot_init_psp ( struct image * image, void *seg ) {
99  struct comboot_psp *psp;
100 
101  /* Fill PSP */
102  psp = seg;
103 
104  /* INT 20h instruction, byte order reversed */
105  psp->int20 = 0x20CD;
106 
107  /* get_fbms() returns BIOS free base memory counter, which is in
108  * kilobytes; x * 1024 / 16 == x * 64 == x << 6 */
109  psp->first_non_free_para = get_fbms() << 6;
110 
111  DBGC ( image, "COMBOOT %s: first non-free paragraph = 0x%x\n",
112  image->name, psp->first_non_free_para );
113 
114  /* Copy the command line to the PSP */
116 }
117 
118 /**
119  * Execute COMBOOT image
120  *
121  * @v image COMBOOT image
122  * @ret rc Return status code
123  */
124 static int comboot_exec_loop ( struct image *image ) {
125  void *seg = real_to_virt ( COMBOOT_PSP_SEG, 0 );
126  int state;
127 
129 
130  switch ( state ) {
131  case 0: /* First time through; invoke COMBOOT program */
132 
133  /* Initialize PSP */
135 
136  /* Hook COMBOOT API interrupts */
138 
139  DBGC ( image, "executing 16-bit COMBOOT image at %4x:0100\n",
140  COMBOOT_PSP_SEG );
141 
142  /* Unregister image, so that a "boot" command doesn't
143  * throw us into an execution loop. We never
144  * reregister ourselves; COMBOOT images expect to be
145  * removed on exit.
146  */
148 
149  /* Store stack segment at 0x38 and stack pointer at 0x3A
150  * in the PSP and jump to the image */
152  REAL_CODE ( /* Save return address with segment on old stack */
153  "popw %%ax\n\t"
154  "pushw %%cs\n\t"
155  "pushw %%ax\n\t"
156  /* Set DS=ES=segment with image */
157  "movw %w0, %%ds\n\t"
158  "movw %w0, %%es\n\t"
159  /* Set SS:SP to new stack (end of image segment) */
160  "movw %w0, %%ss\n\t"
161  "xor %%sp, %%sp\n\t"
162  "pushw $0\n\t"
163  "pushw %w0\n\t"
164  "pushw $0x100\n\t"
165  /* Zero registers (some COM files assume GP regs are 0) */
166  "xorw %%ax, %%ax\n\t"
167  "xorw %%bx, %%bx\n\t"
168  "xorw %%cx, %%cx\n\t"
169  "xorw %%dx, %%dx\n\t"
170  "xorw %%si, %%si\n\t"
171  "xorw %%di, %%di\n\t"
172  "xorw %%bp, %%bp\n\t"
173  "lret\n\t" )
174  : : "R" ( COMBOOT_PSP_SEG ) : "eax" );
175  DBGC ( image, "COMBOOT %s: returned\n", image->name );
176  break;
177 
178  case COMBOOT_EXIT:
179  DBGC ( image, "COMBOOT %s: exited\n", image->name );
180  break;
181 
183  assert ( image->replacement );
184  DBGC ( image, "COMBOOT %s: exited to run kernel %s\n",
186  break;
187 
189  DBGC ( image, "COMBOOT %s: exited after executing command\n",
190  image->name );
191  break;
192 
193  default:
194  assert ( 0 );
195  break;
196  }
197 
200 
201  return 0;
202 }
203 
204 /**
205  * Check image name extension
206  *
207  * @v image COMBOOT image
208  * @ret rc Return status code
209  */
210 static int comboot_identify ( struct image *image ) {
211  const char *ext;
212 
213  ext = strrchr( image->name, '.' );
214 
215  if ( ! ext ) {
216  DBGC ( image, "COMBOOT %s: no extension\n",
217  image->name );
218  return -ENOEXEC;
219  }
220 
221  ++ext;
222 
223  if ( strcasecmp( ext, "cbt" ) ) {
224  DBGC ( image, "COMBOOT %s: unrecognized extension %s\n",
225  image->name, ext );
226  return -ENOEXEC;
227  }
228 
229  return 0;
230 }
231 
232 /**
233  * Load COMBOOT image into memory, preparing a segment and returning it
234  * @v image COMBOOT image
235  * @ret rc Return status code
236  */
237 static int comboot_prepare_segment ( struct image *image )
238 {
239  void *seg;
240  size_t filesz, memsz;
241  int rc;
242 
243  /* Load image in segment */
245 
246  /* Allow etra 0x100 bytes before image for PSP */
247  filesz = image->len + 0x100;
248 
249  /* Ensure the entire 64k segment is free */
250  memsz = 0xFFFF;
251 
252  /* Prepare, verify, and load the real-mode segment */
253  if ( ( rc = prep_segment ( seg, filesz, memsz ) ) != 0 ) {
254  DBGC ( image, "COMBOOT %s: could not prepare segment: %s\n",
255  image->name, strerror ( rc ) );
256  return rc;
257  }
258 
259  /* Zero PSP */
260  memset ( seg, 0, 0x100 );
261 
262  /* Copy image to segment:0100 */
263  memcpy ( ( seg + 0x100 ), image->data, image->len );
264 
265  return 0;
266 }
267 
268 /**
269  * Probe COMBOOT image
270  *
271  * @v image COMBOOT image
272  * @ret rc Return status code
273  */
274 static int comboot_probe ( struct image *image ) {
275  int rc;
276 
277  /* Check if this is a COMBOOT image */
278  if ( ( rc = comboot_identify ( image ) ) != 0 ) {
279 
280  return rc;
281  }
282 
283  return 0;
284 }
285 
286 /**
287  * Execute COMBOOT image
288  *
289  * @v image COMBOOT image
290  * @ret rc Return status code
291  */
292 static int comboot_exec ( struct image *image ) {
293  int rc;
294 
295  /* Sanity check for filesize */
296  if( image->len >= 0xFF00 ) {
297  DBGC( image, "COMBOOT %s: image too large\n",
298  image->name );
299  return -ENOEXEC;
300  }
301 
302  /* Prepare segment and load image */
303  if ( ( rc = comboot_prepare_segment ( image ) ) != 0 ) {
304  return rc;
305  }
306 
307  /* Reset console */
308  console_reset();
309 
310  return comboot_exec_loop ( image );
311 }
312 
313 /** SYSLINUX COMBOOT (16-bit) image type */
314 struct image_type comboot_image_type __image_type ( PROBE_NORMAL ) = {
315  .name = "COMBOOT",
316  .probe = comboot_probe,
317  .exec = comboot_exec,
318 };
uint16_t int20
INT 20 instruction, executed if COMBOOT image returns with RET.
Definition: comboot.c:51
static int comboot_identify(struct image *image)
Check image name extension.
Definition: comboot.c:210
#define COMBOOT_PSP_CMDLINE_OFFSET
Offset in PSP of command line.
Definition: comboot.c:57
struct arbelprm_rc_send_wqe rc
Definition: arbel.h:14
unsigned short uint16_t
Definition: stdint.h:11
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.
const void * data
Read-only data.
Definition: image.h:50
#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
An executable image type.
Definition: image.h:94
#define COMBOOT_MAX_CMDLINE_LEN
Maximum length of command line in PSP (127 bytes minus space and CR)
Definition: comboot.c:61
#define PROBE_NORMAL
Normal image probe priority.
Definition: image.h:155
int strcasecmp(const char *first, const char *second)
Compare case-insensitive strings.
Definition: string.c:208
SYSLINUX COMBOOT.
An executable image.
Definition: image.h:23
#define FEATURE_IMAGE
Image formats.
Definition: features.h:22
static int comboot_probe(struct image *image)
Probe COMBOOT image.
Definition: comboot.c:274
char * name
Name of this image type.
Definition: image.h:96
char * cmdline
Command line to pass to image.
Definition: image.h:42
static __always_inline void * real_to_virt(unsigned int segment, unsigned int offset)
Convert segment:offset address to virtual address.
Definition: realmode.h:77
static int comboot_exec_loop(struct image *image)
Execute COMBOOT image.
Definition: comboot.c:124
Executable image segments.
void * memcpy(void *dest, const void *src, size_t len) __nonnull
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)
rmjmp_buf comboot_return
Definition: comboot_call.c:83
Executable images.
struct image_type comboot_image_type __image_type(PROBE_NORMAL)
SYSLINUX COMBOOT (16-bit) image type.
static void comboot_init_psp(struct image *image, void *seg)
Initialize PSP.
Definition: comboot.c:98
void comboot_force_text_mode(void)
Set default text mode.
Definition: comboot_call.c:136
Feature list.
User interaction.
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:53
void unhook_comboot_interrupts()
Unhook BIOS interrupts related to COMBOOT API (INT 20h, 21h, 22h)
Definition: comboot_call.c:678
size_t len
Length of raw file image.
Definition: image.h:55
static void comboot_copy_cmdline(struct image *image, void *seg)
Copy command line to PSP.
Definition: comboot.c:69
__asm__ __volatile__("call *%9" :"=a"(result), "=c"(discard_ecx), "=d"(discard_edx) :"d"(0), "a"(code), "b"(0), "c"(in_phys), "D"(0), "S"(out_phys), "m"(hypercall))
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:237
static void console_reset(void)
Reset console.
Definition: console.h:214
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:49
void unregister_image(struct image *image)
Unregister executable image.
Definition: image.c:357
__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:645
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:292
struct image * replacement
Replacement image.
Definition: image.h:72
static size_t memsz
Definition: fdtmem.c:51
int prep_segment(void *segment, size_t filesz, size_t memsz)
Prepare segment for loading.
Definition: segment.c:61
struct golan_mkey_seg seg
Definition: CIB_PRM.h:28
uint32_t cmdline
Definition: multiboot.h:16
#define REAL_CODE(asm_code_str)
Definition: libkir.h:226
char * name
Name.
Definition: image.h:37
String functions.
String functions.
void * memset(void *dest, int character, size_t len) __nonnull