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., 675 Mass Ave, Cambridge, MA 02139, USA. 17 */ 18 19/** 20 * @file 21 * 22 * SYSLINUX COM32 image format 23 * 24 */ 25 26FILE_LICENCE ( GPL2_OR_LATER ); 27 28#include <stdint.h> 29#include <stdlib.h> 30#include <string.h> 31#include <strings.h> 32#include <errno.h> 33#include <assert.h> 34#include <realmode.h> 35#include <basemem.h> 36#include <comboot.h> 37#include <gpxe/uaccess.h> 38#include <gpxe/image.h> 39#include <gpxe/segment.h> 40#include <gpxe/init.h> 41#include <gpxe/memmap.h> 42 43struct image_type com32_image_type __image_type ( PROBE_NORMAL ); 44 45/** 46 * Execute COMBOOT image 47 * 48 * @v image COM32 image 49 * @ret rc Return status code 50 */ 51static int com32_exec ( struct image *image ) { 52 struct memory_map memmap; 53 unsigned int i; 54 int state; 55 uint32_t avail_mem_top; 56 57 state = rmsetjmp ( comboot_return ); 58 59 switch ( state ) { 60 case 0: /* First time through; invoke COM32 program */ 61 62 /* Get memory map */ 63 get_memmap ( &memmap ); 64 65 /* Find end of block covering COM32 image loading area */ 66 for ( i = 0, avail_mem_top = 0 ; i < memmap.count ; i++ ) { 67 if ( (memmap.regions[i].start <= COM32_START_PHYS) && 68 (memmap.regions[i].end > COM32_START_PHYS + image->len) ) { 69 avail_mem_top = memmap.regions[i].end; 70 break; 71 } 72 } 73 74 DBGC ( image, "COM32 %p: available memory top = 0x%x\n", 75 image, avail_mem_top ); 76 77 assert ( avail_mem_top != 0 ); 78 79 com32_external_esp = phys_to_virt ( avail_mem_top ); 80 81 /* Hook COMBOOT API interrupts */ 82 hook_comboot_interrupts(); 83 84 /* Unregister image, so that a "boot" command doesn't 85 * throw us into an execution loop. We never 86 * reregister ourselves; COMBOOT images expect to be 87 * removed on exit. 88 */ 89 unregister_image ( image ); 90 91 __asm__ __volatile__ ( 92 "movl %%esp, (com32_internal_esp)\n\t" /* Save internal virtual address space ESP */ 93 "movl (com32_external_esp), %%esp\n\t" /* Switch to COM32 ESP (top of available memory) */ 94 "call _virt_to_phys\n\t" /* Switch to flat physical address space */ 95 "pushl %0\n\t" /* Pointer to CDECL helper function */ 96 "pushl %1\n\t" /* Pointer to FAR call helper function */ 97 "pushl %2\n\t" /* Size of low memory bounce buffer */ 98 "pushl %3\n\t" /* Pointer to low memory bounce buffer */ 99 "pushl %4\n\t" /* Pointer to INT call helper function */ 100 "pushl %5\n\t" /* Pointer to the command line arguments */ 101 "pushl $6\n\t" /* Number of additional arguments */ 102 "call *%6\n\t" /* Execute image */ 103 "call _phys_to_virt\n\t" /* Switch back to internal virtual address space */ 104 "movl (com32_internal_esp), %%esp\n\t" /* Switch back to internal stack */ 105 : 106 : 107 /* %0 */ "r" ( virt_to_phys ( com32_cfarcall_wrapper ) ), 108 /* %1 */ "r" ( virt_to_phys ( com32_farcall_wrapper ) ), 109 /* %2 */ "r" ( get_fbms() * 1024 - (COM32_BOUNCE_SEG << 4) ), 110 /* %3 */ "i" ( COM32_BOUNCE_SEG << 4 ), 111 /* %4 */ "r" ( virt_to_phys ( com32_intcall_wrapper ) ), 112 /* %5 */ "r" ( virt_to_phys ( image->cmdline ) ), 113 /* %6 */ "r" ( COM32_START_PHYS ) 114 : 115 "memory" ); 116 DBGC ( image, "COM32 %p: returned\n", image ); 117 break; 118 119 case COMBOOT_EXIT: 120 DBGC ( image, "COM32 %p: exited\n", image ); 121 break; 122 123 case COMBOOT_EXIT_RUN_KERNEL: 124 DBGC ( image, "COM32 %p: exited to run kernel %p\n", 125 image, comboot_replacement_image ); 126 image->replacement = comboot_replacement_image; 127 comboot_replacement_image = NULL; 128 image_autoload ( image->replacement ); 129 break; 130 131 case COMBOOT_EXIT_COMMAND: 132 DBGC ( image, "COM32 %p: exited after executing command\n", 133 image ); 134 break; 135 136 default: 137 assert ( 0 ); 138 break; 139 } 140 141 unhook_comboot_interrupts(); 142 comboot_force_text_mode(); 143 144 return 0; 145} 146 147/** 148 * Check image name extension 149 * 150 * @v image COM32 image 151 * @ret rc Return status code 152 */ 153static int com32_identify ( struct image *image ) { 154 const char *ext; 155 static const uint8_t magic[] = { 0xB8, 0xFF, 0x4C, 0xCD, 0x21 }; 156 uint8_t buf[5]; 157 158 if ( image->len >= 5 ) { 159 /* Check for magic number 160 * mov eax,21cd4cffh 161 * B8 FF 4C CD 21 162 */ 163 copy_from_user ( buf, image->data, 0, sizeof(buf) ); 164 if ( ! memcmp ( buf, magic, sizeof(buf) ) ) { 165 DBGC ( image, "COM32 %p: found magic number\n", 166 image ); 167 return 0; 168 } 169 } 170 171 /* Magic number not found; check filename extension */ 172 173 ext = strrchr( image->name, '.' ); 174 175 if ( ! ext ) { 176 DBGC ( image, "COM32 %p: no extension\n", 177 image ); 178 return -ENOEXEC; 179 } 180 181 ++ext; 182 183 if ( strcasecmp( ext, "c32" ) ) { 184 DBGC ( image, "COM32 %p: unrecognized extension %s\n", 185 image, ext ); 186 return -ENOEXEC; 187 } 188 189 return 0; 190} 191 192 193/** 194 * Load COM32 image into memory 195 * @v image COM32 image 196 * @ret rc Return status code 197 */ 198static int comboot_load_image ( struct image *image ) { 199 size_t filesz, memsz; 200 userptr_t buffer; 201 int rc; 202 203 filesz = image->len; 204 memsz = filesz; 205 buffer = phys_to_user ( COM32_START_PHYS ); 206 if ( ( rc = prep_segment ( buffer, filesz, memsz ) ) != 0 ) { 207 DBGC ( image, "COM32 %p: could not prepare segment: %s\n", 208 image, strerror ( rc ) ); 209 return rc; 210 } 211 212 /* Copy image to segment */ 213 memcpy_user ( buffer, 0, image->data, 0, filesz ); 214 215 return 0; 216} 217 218/** 219 * Prepare COM32 low memory bounce buffer 220 * @v image COM32 image 221 * @ret rc Return status code 222 */ 223static int comboot_prepare_bounce_buffer ( struct image * image ) { 224 unsigned int seg; 225 userptr_t seg_userptr; 226 size_t filesz, memsz; 227 int rc; 228 229 seg = COM32_BOUNCE_SEG; 230 seg_userptr = real_to_user ( seg, 0 ); 231 232 /* Ensure the entire 64k segment is free */ 233 memsz = 0xFFFF; 234 filesz = 0; 235 236 /* Prepare, verify, and load the real-mode segment */ 237 if ( ( rc = prep_segment ( seg_userptr, filesz, memsz ) ) != 0 ) { 238 DBGC ( image, "COM32 %p: could not prepare bounce buffer segment: %s\n", 239 image, strerror ( rc ) ); 240 return rc; 241 } 242 243 return 0; 244} 245 246/** 247 * Load COM32 image into memory 248 * 249 * @v image COM32 image 250 * @ret rc Return status code 251 */ 252static int com32_load ( struct image *image ) { 253 int rc; 254 255 DBGC ( image, "COM32 %p: name '%s', cmdline '%s'\n", 256 image, image->name, image->cmdline ); 257 258 /* Check if this is a COMBOOT image */ 259 if ( ( rc = com32_identify ( image ) ) != 0 ) { 260 return rc; 261 } 262 263 /* This is a COM32 image, valid or otherwise */ 264 if ( ! image->type ) 265 image->type = &com32_image_type; 266 267 /* Load image */ 268 if ( ( rc = comboot_load_image ( image ) ) != 0 ) { 269 return rc; 270 } 271 272 /* Prepare bounce buffer segment */ 273 if ( ( rc = comboot_prepare_bounce_buffer ( image ) ) != 0 ) { 274 return rc; 275 } 276 277 return 0; 278} 279 280/** SYSLINUX COM32 image type */ 281struct image_type com32_image_type __image_type ( PROBE_NORMAL ) = { 282 .name = "COM32", 283 .load = com32_load, 284 .exec = com32_exec, 285}; 286