176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Copyright (C) 2008 Daniel Verkamp <daniel@drv.nu>.
376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This program is free software; you can redistribute it and/or
576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * modify it under the terms of the GNU General Public License as
676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * published by the Free Software Foundation; either version 2 of the
776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * License, or any later version.
876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This program is distributed in the hope that it will be useful, but
1076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * WITHOUT ANY WARRANTY; without even the implied warranty of
1176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
1276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * General Public License for more details.
1376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
1476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * You should have received a copy of the GNU General Public License
1576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * along with this program; if not, write to the Free Software
1676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
1776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
1876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
1976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
2076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @file
2176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
2276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * SYSLINUX COMBOOT (16-bit) image format
2376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
2476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
2576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
2676d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanFILE_LICENCE ( GPL2_OR_LATER );
2776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
2876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <stdint.h>
2976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <stdlib.h>
3076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <string.h>
3176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <strings.h>
3276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <errno.h>
3376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <assert.h>
3476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <realmode.h>
3576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <basemem.h>
3676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <comboot.h>
3776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/uaccess.h>
3876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/image.h>
3976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/segment.h>
4076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/init.h>
4176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/features.h>
4276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
4376d05dc695b06c4e987bb8078f78032441e1430cGreg HartmanFEATURE ( FEATURE_IMAGE, "COMBOOT", DHCP_EB_FEATURE_COMBOOT, 1 );
4476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
4576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct image_type comboot_image_type __image_type ( PROBE_NORMAL );
4676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
4776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
4876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * COMBOOT PSP, copied to offset 0 of code segment
4976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
5076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct comboot_psp {
5176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/** INT 20 instruction, executed if COMBOOT image returns with RET */
5276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	uint16_t int20;
5376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/** Segment of first non-free paragraph of memory */
5476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	uint16_t first_non_free_para;
5576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman};
5676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
5776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** Offset in PSP of command line */
5876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#define COMBOOT_PSP_CMDLINE_OFFSET 0x81
5976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
6076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** Maximum length of command line in PSP
6176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * (127 bytes minus space and CR) */
6276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#define COMBOOT_MAX_CMDLINE_LEN    125
6376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
6476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
6576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
6676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Copy command line to PSP
6776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
6876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v image		COMBOOT image
6976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
7076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void comboot_copy_cmdline ( struct image * image, userptr_t seg_userptr ) {
7176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	const char *cmdline = ( image->cmdline ? image->cmdline : "" );
7276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int cmdline_len = strlen ( cmdline );
7376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if( cmdline_len > COMBOOT_MAX_CMDLINE_LEN )
7476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		cmdline_len = COMBOOT_MAX_CMDLINE_LEN;
7576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	uint8_t len_byte = cmdline_len;
7676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	char spc = ' ', cr = '\r';
7776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
7876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Copy length to byte before command line */
7976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	copy_to_user ( seg_userptr, COMBOOT_PSP_CMDLINE_OFFSET - 1,
8076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	               &len_byte, 1 );
8176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
8276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Command line starts with space */
8376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	copy_to_user ( seg_userptr,
8476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	               COMBOOT_PSP_CMDLINE_OFFSET,
8576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	               &spc, 1 );
8676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
8776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Copy command line */
8876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	copy_to_user ( seg_userptr,
8976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	               COMBOOT_PSP_CMDLINE_OFFSET + 1,
9076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	               cmdline, cmdline_len );
9176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
9276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Command line ends with CR */
9376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	copy_to_user ( seg_userptr,
9476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	               COMBOOT_PSP_CMDLINE_OFFSET + cmdline_len + 1,
9576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	               &cr, 1 );
9676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
9776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
9876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
9976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Initialize PSP
10076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
10176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v image		COMBOOT image
10276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v seg_userptr	segment to initialize
10376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
10476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void comboot_init_psp ( struct image * image, userptr_t seg_userptr ) {
10576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct comboot_psp psp;
10676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
10776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Fill PSP */
10876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
10976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* INT 20h instruction, byte order reversed */
11076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	psp.int20 = 0x20CD;
11176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
11276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* get_fbms() returns BIOS free base memory counter, which is in
11376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	 * kilobytes; x * 1024 / 16 == x * 64 == x << 6 */
11476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	psp.first_non_free_para = get_fbms() << 6;
11576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
11676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGC ( image, "COMBOOT %p: first non-free paragraph = 0x%x\n",
11776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	       image, psp.first_non_free_para );
11876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
11976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Copy the PSP to offset 0 of segment.
12076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	 * The rest of the PSP was already zeroed by
12176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	 * comboot_prepare_segment. */
12276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	copy_to_user ( seg_userptr, 0, &psp, sizeof( psp ) );
12376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
12476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Copy the command line to the PSP */
12576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	comboot_copy_cmdline ( image, seg_userptr );
12676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
12776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
12876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
12976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Execute COMBOOT image
13076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
13176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v image		COMBOOT image
13276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc		Return status code
13376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
13476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int comboot_exec ( struct image *image ) {
13576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	userptr_t seg_userptr = real_to_user ( COMBOOT_PSP_SEG, 0 );
13676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int state;
13776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
13876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	state = rmsetjmp ( comboot_return );
13976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
14076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	switch ( state ) {
14176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	case 0: /* First time through; invoke COMBOOT program */
14276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
14376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/* Initialize PSP */
14476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		comboot_init_psp ( image, seg_userptr );
14576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
14676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/* Hook COMBOOT API interrupts */
14776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		hook_comboot_interrupts();
14876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
14976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBGC ( image, "executing 16-bit COMBOOT image at %4x:0100\n",
15076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		       COMBOOT_PSP_SEG );
15176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
15276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/* Unregister image, so that a "boot" command doesn't
15376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		 * throw us into an execution loop.  We never
15476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		 * reregister ourselves; COMBOOT images expect to be
15576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		 * removed on exit.
15676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		 */
15776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		unregister_image ( image );
15876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
15976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/* Store stack segment at 0x38 and stack pointer at 0x3A
16076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		 * in the PSP and jump to the image */
16176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		__asm__ __volatile__ (
16276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		    REAL_CODE ( /* Save return address with segment on old stack */
16376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				    "popw %%ax\n\t"
16476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				    "pushw %%cs\n\t"
16576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				    "pushw %%ax\n\t"
16676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				    /* Set DS=ES=segment with image */
16776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				    "movw %w0, %%ds\n\t"
16876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				    "movw %w0, %%es\n\t"
16976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				    /* Set SS:SP to new stack (end of image segment) */
17076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				    "movw %w0, %%ss\n\t"
17176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				    "xor %%sp, %%sp\n\t"
17276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				    "pushw $0\n\t"
17376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				    "pushw %w0\n\t"
17476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				    "pushw $0x100\n\t"
17576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				    /* Zero registers (some COM files assume GP regs are 0) */
17676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				    "xorw %%ax, %%ax\n\t"
17776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				    "xorw %%bx, %%bx\n\t"
17876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				    "xorw %%cx, %%cx\n\t"
17976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				    "xorw %%dx, %%dx\n\t"
18076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				    "xorw %%si, %%si\n\t"
18176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				    "xorw %%di, %%di\n\t"
18276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				    "xorw %%bp, %%bp\n\t"
18376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				    "lret\n\t" )
18476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman					 : : "r" ( COMBOOT_PSP_SEG ) : "eax" );
18576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBGC ( image, "COMBOOT %p: returned\n", image );
18676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		break;
18776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
18876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	case COMBOOT_EXIT:
18976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBGC ( image, "COMBOOT %p: exited\n", image );
19076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		break;
19176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
19276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	case COMBOOT_EXIT_RUN_KERNEL:
19376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBGC ( image, "COMBOOT %p: exited to run kernel %p\n",
19476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		       image, comboot_replacement_image );
19576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		image->replacement = comboot_replacement_image;
19676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		comboot_replacement_image = NULL;
19776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		image_autoload ( image->replacement );
19876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		break;
19976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
20076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	case COMBOOT_EXIT_COMMAND:
20176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBGC ( image, "COMBOOT %p: exited after executing command\n",
20276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		       image );
20376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		break;
20476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
20576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	default:
20676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		assert ( 0 );
20776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		break;
20876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
20976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
21076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unhook_comboot_interrupts();
21176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	comboot_force_text_mode();
21276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
21376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return 0;
21476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
21576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
21676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
21776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Check image name extension
21876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
21976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v image		COMBOOT image
22076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc		Return status code
22176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
22276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int comboot_identify ( struct image *image ) {
22376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	const char *ext;
22476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
22576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ext = strrchr( image->name, '.' );
22676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
22776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( ! ext ) {
22876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBGC ( image, "COMBOOT %p: no extension\n",
22976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		       image );
23076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return -ENOEXEC;
23176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
23276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
23376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	++ext;
23476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
23576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( strcasecmp( ext, "com" ) && strcasecmp( ext, "cbt" ) ) {
23676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBGC ( image, "COMBOOT %p: unrecognized extension %s\n",
23776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		       image, ext );
23876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return -ENOEXEC;
23976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
24076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
24176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return 0;
24276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
24376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
24476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
24576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Load COMBOOT image into memory, preparing a segment and returning it
24676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v image		COMBOOT image
24776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc		Return status code
24876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
24976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int comboot_prepare_segment ( struct image *image )
25076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman{
25176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	userptr_t seg_userptr;
25276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	size_t filesz, memsz;
25376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int rc;
25476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
25576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Load image in segment */
25676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	seg_userptr = real_to_user ( COMBOOT_PSP_SEG, 0 );
25776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
25876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Allow etra 0x100 bytes before image for PSP */
25976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	filesz = image->len + 0x100;
26076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
26176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Ensure the entire 64k segment is free */
26276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	memsz = 0xFFFF;
26376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
26476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Prepare, verify, and load the real-mode segment */
26576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( ( rc = prep_segment ( seg_userptr, filesz, memsz ) ) != 0 ) {
26676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBGC ( image, "COMBOOT %p: could not prepare segment: %s\n",
26776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		       image, strerror ( rc ) );
26876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return rc;
26976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
27076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
27176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Zero PSP */
27276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	memset_user ( seg_userptr, 0, 0, 0x100 );
27376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
27476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Copy image to segment:0100 */
27576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	memcpy_user ( seg_userptr, 0x100, image->data, 0, image->len );
27676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
27776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return 0;
27876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
27976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
28076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
28176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Load COMBOOT image into memory
28276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
28376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v image		COMBOOT image
28476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc		Return status code
28576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
28676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int comboot_load ( struct image *image ) {
28776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int rc;
28876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
28976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBGC ( image, "COMBOOT %p: name '%s'\n",
29076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	       image, image->name );
29176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
29276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Check if this is a COMBOOT image */
29376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( ( rc = comboot_identify ( image ) ) != 0 ) {
29476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
29576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return rc;
29676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
29776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
29876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* This is a 16-bit COMBOOT image, valid or otherwise */
29976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( ! image->type )
30076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		image->type = &comboot_image_type;
30176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
30276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Sanity check for filesize */
30376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if( image->len >= 0xFF00 ) {
30476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBGC( image, "COMBOOT %p: image too large\n",
30576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		      image );
30676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return -ENOEXEC;
30776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
30876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
30976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Prepare segment and load image */
31076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( ( rc = comboot_prepare_segment ( image ) ) != 0 ) {
31176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return rc;
31276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
31376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
31476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return 0;
31576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
31676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
31776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** SYSLINUX COMBOOT (16-bit) image type */
31876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstruct image_type comboot_image_type __image_type ( PROBE_NORMAL ) = {
31976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	.name = "COMBOOT",
32076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	.load = comboot_load,
32176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	.exec = comboot_exec,
32276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman};
323