176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/*
276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
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 HartmanFILE_LICENCE ( GPL2_OR_LATER );
2076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
2176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <stdint.h>
2276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <limits.h>
2376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <byteswap.h>
2476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <errno.h>
2576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <assert.h>
2676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/list.h>
2776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/blockdev.h>
2876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <gpxe/memmap.h>
2976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <realmode.h>
3076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <bios.h>
3176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <biosint.h>
3276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <bootsector.h>
3376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#include <int13.h>
3476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
3576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** @file
3676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
3776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * INT 13 emulation
3876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
3976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This module provides a mechanism for exporting block devices via
4076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * the BIOS INT 13 disk interrupt interface.
4176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
4276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
4376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
4476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** Vector for chaining to other INT 13 handlers */
4576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic struct segoff __text16 ( int13_vector );
4676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman#define int13_vector __use_text16 ( int13_vector )
4776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
4876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** Assembly wrapper */
4976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanextern void int13_wrapper ( void );
5076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
5176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/** List of registered emulated drives */
5276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic LIST_HEAD ( drives );
5376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
5476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
5576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Number of BIOS drives
5676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
5776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Note that this is the number of drives in the system as a whole
5876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * (i.e. a mirror of the counter at 40:75), rather than a count of the
5976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * number of emulated drives.
6076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
6176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic uint8_t num_drives;
6276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
6376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
6476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Update BIOS drive count
6576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
6676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void int13_set_num_drives ( void ) {
6776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct int13_drive *drive;
6876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
6976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Get current drive count */
7076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	get_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES );
7176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
7276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Ensure count is large enough to cover all of our emulated drives */
7376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	list_for_each_entry ( drive, &drives, list ) {
7476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if ( num_drives <= ( drive->drive & 0x7f ) )
7576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			num_drives = ( ( drive->drive & 0x7f ) + 1 );
7676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
7776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
7876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Update current drive count */
7976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	put_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES );
8076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
8176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
8276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
8376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Check number of drives
8476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
8576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void int13_check_num_drives ( void ) {
8676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	uint8_t check_num_drives;
8776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
8876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	get_real ( check_num_drives, BDA_SEG, BDA_NUM_DRIVES );
8976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( check_num_drives != num_drives ) {
9076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		int13_set_num_drives();
9176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBG ( "INT13 fixing up number of drives from %d to %d\n",
9276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		      check_num_drives, num_drives );
9376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
9476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
9576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
9676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
9776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * INT 13, 00 - Reset disk system
9876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
9976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v drive		Emulated drive
10076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret status		Status code
10176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
10276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int int13_reset ( struct int13_drive *drive __unused,
10376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			 struct i386_all_regs *ix86 __unused ) {
10476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBG ( "Reset drive\n" );
10576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return 0;
10676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
10776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
10876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
10976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * INT 13, 01 - Get status of last operation
11076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
11176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v drive		Emulated drive
11276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret status		Status code
11376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
11476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int int13_get_last_status ( struct int13_drive *drive,
11576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				   struct i386_all_regs *ix86 __unused ) {
11676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBG ( "Get status of last operation\n" );
11776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return drive->last_status;
11876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
11976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
12076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
12176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Read / write sectors
12276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
12376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v drive		Emulated drive
12476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v al		Number of sectors to read or write (must be nonzero)
12576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v ch		Low bits of cylinder number
12676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v cl (bits 7:6)	High bits of cylinder number
12776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v cl (bits 5:0)	Sector number
12876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v dh		Head number
12976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v es:bx		Data buffer
13076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v io		Read / write method
13176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret status		Status code
13276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret al		Number of sectors read or written
13376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
13476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int int13_rw_sectors ( struct int13_drive *drive,
13576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			      struct i386_all_regs *ix86,
13676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			      int ( * io ) ( struct block_device *blockdev,
13776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman					     uint64_t block,
13876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman					     unsigned long count,
13976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman					     userptr_t buffer ) ) {
14076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct block_device *blockdev = drive->blockdev;
14176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unsigned int cylinder, head, sector;
14276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unsigned long lba;
14376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unsigned int count;
14476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	userptr_t buffer;
14576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int rc;
14676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
14776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Validate blocksize */
14876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( blockdev->blksize != INT13_BLKSIZE ) {
14976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBG ( "Invalid blocksize (%zd) for non-extended read/write\n",
15076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		      blockdev->blksize );
15176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return -INT13_STATUS_INVALID;
15276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
15376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
15476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Calculate parameters */
15576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	cylinder = ( ( ( ix86->regs.cl & 0xc0 ) << 2 ) | ix86->regs.ch );
15676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	assert ( cylinder < drive->cylinders );
15776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	head = ix86->regs.dh;
15876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	assert ( head < drive->heads );
15976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	sector = ( ix86->regs.cl & 0x3f );
16076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	assert ( ( sector >= 1 ) && ( sector <= drive->sectors_per_track ) );
16176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	lba = ( ( ( ( cylinder * drive->heads ) + head )
16276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		  * drive->sectors_per_track ) + sector - 1 );
16376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	count = ix86->regs.al;
16476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	buffer = real_to_user ( ix86->segs.es, ix86->regs.bx );
16576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
16676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBG ( "C/H/S %d/%d/%d = LBA %#lx <-> %04x:%04x (count %d)\n", cylinder,
16776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	      head, sector, lba, ix86->segs.es, ix86->regs.bx, count );
16876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
16976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Read from / write to block device */
17076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( ( rc = io ( blockdev, lba, count, buffer ) ) != 0 ) {
17176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBG ( "INT 13 failed: %s\n", strerror ( rc ) );
17276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return -INT13_STATUS_READ_ERROR;
17376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
17476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
17576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return 0;
17676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
17776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
17876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
17976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * INT 13, 02 - Read sectors
18076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
18176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v drive		Emulated drive
18276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v al		Number of sectors to read (must be nonzero)
18376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v ch		Low bits of cylinder number
18476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v cl (bits 7:6)	High bits of cylinder number
18576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v cl (bits 5:0)	Sector number
18676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v dh		Head number
18776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v es:bx		Data buffer
18876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret status		Status code
18976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret al		Number of sectors read
19076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
19176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int int13_read_sectors ( struct int13_drive *drive,
19276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				struct i386_all_regs *ix86 ) {
19376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBG ( "Read: " );
19476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return int13_rw_sectors ( drive, ix86, drive->blockdev->op->read );
19576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
19676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
19776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
19876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * INT 13, 03 - Write sectors
19976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
20076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v drive		Emulated drive
20176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v al		Number of sectors to write (must be nonzero)
20276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v ch		Low bits of cylinder number
20376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v cl (bits 7:6)	High bits of cylinder number
20476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v cl (bits 5:0)	Sector number
20576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v dh		Head number
20676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v es:bx		Data buffer
20776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret status		Status code
20876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret al		Number of sectors written
20976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
21076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int int13_write_sectors ( struct int13_drive *drive,
21176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				 struct i386_all_regs *ix86 ) {
21276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBG ( "Write: " );
21376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return int13_rw_sectors ( drive, ix86, drive->blockdev->op->write );
21476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
21576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
21676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
21776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * INT 13, 08 - Get drive parameters
21876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
21976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v drive		Emulated drive
22076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret status		Status code
22176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret ch		Low bits of maximum cylinder number
22276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret cl (bits 7:6)	High bits of maximum cylinder number
22376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret cl (bits 5:0)	Maximum sector number
22476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret dh		Maximum head number
22576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret dl		Number of drives
22676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
22776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int int13_get_parameters ( struct int13_drive *drive,
22876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				  struct i386_all_regs *ix86 ) {
22976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unsigned int max_cylinder = drive->cylinders - 1;
23076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unsigned int max_head = drive->heads - 1;
23176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unsigned int max_sector = drive->sectors_per_track; /* sic */
23276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
23376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBG ( "Get drive parameters\n" );
23476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
23576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ix86->regs.ch = ( max_cylinder & 0xff );
23676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ix86->regs.cl = ( ( ( max_cylinder >> 8 ) << 6 ) | max_sector );
23776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ix86->regs.dh = max_head;
23876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	get_real ( ix86->regs.dl, BDA_SEG, BDA_NUM_DRIVES );
23976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return 0;
24076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
24176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
24276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
24376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * INT 13, 15 - Get disk type
24476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
24576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v drive		Emulated drive
24676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret ah		Type code
24776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret cx:dx		Sector count
24876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret status		Status code / disk type
24976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
25076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int int13_get_disk_type ( struct int13_drive *drive,
25176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				 struct i386_all_regs *ix86 ) {
25276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	uint32_t blocks;
25376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
25476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBG ( "Get disk type\n" );
25576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	blocks = ( ( drive->blockdev->blocks <= 0xffffffffUL ) ?
25676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		   drive->blockdev->blocks : 0xffffffffUL );
25776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ix86->regs.cx = ( blocks >> 16 );
25876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	ix86->regs.dx = ( blocks & 0xffff );
25976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return INT13_DISK_TYPE_HDD;
26076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
26176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
26276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
26376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * INT 13, 41 - Extensions installation check
26476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
26576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v drive		Emulated drive
26676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v bx		0x55aa
26776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret bx		0xaa55
26876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret cx		Extensions API support bitmap
26976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret status		Status code / API version
27076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
27176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int int13_extension_check ( struct int13_drive *drive __unused,
27276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				   struct i386_all_regs *ix86 ) {
27376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( ix86->regs.bx == 0x55aa ) {
27476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBG ( "INT 13 extensions installation check\n" );
27576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		ix86->regs.bx = 0xaa55;
27676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		ix86->regs.cx = INT13_EXTENSION_LINEAR;
27776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return INT13_EXTENSION_VER_1_X;
27876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	} else {
27976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return -INT13_STATUS_INVALID;
28076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
28176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
28276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
28376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
28476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Extended read / write
28576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
28676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v drive		Emulated drive
28776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v ds:si		Disk address packet
28876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v io		Read / write method
28976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret status		Status code
29076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
29176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int int13_extended_rw ( struct int13_drive *drive,
29276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			       struct i386_all_regs *ix86,
29376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			       int ( * io ) ( struct block_device *blockdev,
29476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman					      uint64_t block,
29576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman					      unsigned long count,
29676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman					      userptr_t buffer ) ) {
29776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct block_device *blockdev = drive->blockdev;
29876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct int13_disk_address addr;
29976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	uint64_t lba;
30076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unsigned long count;
30176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	userptr_t buffer;
30276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int rc;
30376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
30476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Read parameters from disk address structure */
30576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	copy_from_real ( &addr, ix86->segs.ds, ix86->regs.si, sizeof ( addr ));
30676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	lba = addr.lba;
30776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	count = addr.count;
30876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	buffer = real_to_user ( addr.buffer.segment, addr.buffer.offset );
30976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
31076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBG ( "LBA %#llx <-> %04x:%04x (count %ld)\n", (unsigned long long)lba,
31176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	      addr.buffer.segment, addr.buffer.offset, count );
31276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
31376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Read from / write to block device */
31476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( ( rc = io ( blockdev, lba, count, buffer ) ) != 0 ) {
31576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBG ( "INT 13 failed: %s\n", strerror ( rc ) );
31676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return -INT13_STATUS_READ_ERROR;
31776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
31876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
31976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return 0;
32076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
32176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
32276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
32376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * INT 13, 42 - Extended read
32476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
32576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v drive		Emulated drive
32676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v ds:si		Disk address packet
32776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret status		Status code
32876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
32976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int int13_extended_read ( struct int13_drive *drive,
33076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				 struct i386_all_regs *ix86 ) {
33176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBG ( "Extended read: " );
33276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return int13_extended_rw ( drive, ix86, drive->blockdev->op->read );
33376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
33476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
33576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
33676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * INT 13, 43 - Extended write
33776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
33876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v drive		Emulated drive
33976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v ds:si		Disk address packet
34076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret status		Status code
34176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
34276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int int13_extended_write ( struct int13_drive *drive,
34376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				  struct i386_all_regs *ix86 ) {
34476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBG ( "Extended write: " );
34576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return int13_extended_rw ( drive, ix86, drive->blockdev->op->write );
34676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
34776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
34876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
34976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * INT 13, 48 - Get extended parameters
35076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
35176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v drive		Emulated drive
35276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v ds:si		Drive parameter table
35376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret status		Status code
35476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
35576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic int int13_get_extended_parameters ( struct int13_drive *drive,
35676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman					   struct i386_all_regs *ix86 ) {
35776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct int13_disk_parameters params = {
35876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		.bufsize = sizeof ( params ),
35976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		.flags = INT13_FL_DMA_TRANSPARENT,
36076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		.cylinders = drive->cylinders,
36176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		.heads = drive->heads,
36276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		.sectors_per_track = drive->sectors_per_track,
36376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		.sectors = drive->blockdev->blocks,
36476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		.sector_size = drive->blockdev->blksize,
36576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	};
36676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
36776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBG ( "Get extended drive parameters to %04x:%04x\n",
36876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	      ix86->segs.ds, ix86->regs.si );
36976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
37076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	copy_to_real ( ix86->segs.ds, ix86->regs.si, &params,
37176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		       sizeof ( params ) );
37276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return 0;
37376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
37476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
37576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
37676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * INT 13 handler
37776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
37876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
37976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic __asmcall void int13 ( struct i386_all_regs *ix86 ) {
38076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int command = ix86->regs.ah;
38176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unsigned int bios_drive = ix86->regs.dl;
38276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct int13_drive *drive;
38376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int status;
38476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
38576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Check BIOS hasn't killed off our drive */
38676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int13_check_num_drives();
38776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
38876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	list_for_each_entry ( drive, &drives, list ) {
38976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
39076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if ( bios_drive != drive->drive ) {
39176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			/* Remap any accesses to this drive's natural number */
39276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			if ( bios_drive == drive->natural_drive ) {
39376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				DBG ( "INT 13,%04x (%02x) remapped to "
39476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				      "(%02x)\n", ix86->regs.ax,
39576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				      bios_drive, drive->drive );
39676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				ix86->regs.dl = drive->drive;
39776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				return;
39876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			}
39976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			continue;
40076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		}
40176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
40276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBG ( "INT 13,%04x (%02x): ", ix86->regs.ax, drive->drive );
40376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
40476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		switch ( command ) {
40576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		case INT13_RESET:
40676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			status = int13_reset ( drive, ix86 );
40776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			break;
40876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		case INT13_GET_LAST_STATUS:
40976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			status = int13_get_last_status ( drive, ix86 );
41076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			break;
41176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		case INT13_READ_SECTORS:
41276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			status = int13_read_sectors ( drive, ix86 );
41376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			break;
41476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		case INT13_WRITE_SECTORS:
41576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			status = int13_write_sectors ( drive, ix86 );
41676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			break;
41776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		case INT13_GET_PARAMETERS:
41876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			status = int13_get_parameters ( drive, ix86 );
41976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			break;
42076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		case INT13_GET_DISK_TYPE:
42176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			status = int13_get_disk_type ( drive, ix86 );
42276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			break;
42376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		case INT13_EXTENSION_CHECK:
42476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			status = int13_extension_check ( drive, ix86 );
42576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			break;
42676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		case INT13_EXTENDED_READ:
42776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			status = int13_extended_read ( drive, ix86 );
42876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			break;
42976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		case INT13_EXTENDED_WRITE:
43076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			status = int13_extended_write ( drive, ix86 );
43176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			break;
43276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		case INT13_GET_EXTENDED_PARAMETERS:
43376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			status = int13_get_extended_parameters ( drive, ix86 );
43476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			break;
43576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		default:
43676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			DBG ( "*** Unrecognised INT 13 ***\n" );
43776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			status = -INT13_STATUS_INVALID;
43876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			break;
43976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		}
44076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
44176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/* Store status for INT 13,01 */
44276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		drive->last_status = status;
44376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
44476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/* Negative status indicates an error */
44576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if ( status < 0 ) {
44676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			status = -status;
44776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			DBG ( "INT 13 returning failure status %x\n", status );
44876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		} else {
44976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			ix86->flags &= ~CF;
45076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		}
45176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		ix86->regs.ah = status;
45276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
45376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/* Set OF to indicate to wrapper not to chain this call */
45476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		ix86->flags |= OF;
45576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
45676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return;
45776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
45876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
45976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
46076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
46176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Hook INT 13 handler
46276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
46376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
46476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void hook_int13 ( void ) {
46576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Assembly wrapper to call int13().  int13() sets OF if we
46676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	 * should not chain to the previous handler.  (The wrapper
46776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	 * clears CF and OF before calling int13()).
46876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	 */
46976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	__asm__  __volatile__ (
47076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	       TEXT16_CODE ( "\nint13_wrapper:\n\t"
47176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     /* Preserve %ax and %dx for future reference */
47276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     "pushw %%bp\n\t"
47376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     "movw %%sp, %%bp\n\t"
47476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     "pushw %%ax\n\t"
47576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     "pushw %%dx\n\t"
47676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     /* Clear OF, set CF, call int13() */
47776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     "orb $0, %%al\n\t"
47876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     "stc\n\t"
47976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     "pushl %0\n\t"
48076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     "pushw %%cs\n\t"
48176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     "call prot_call\n\t"
48276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     /* Chain if OF not set */
48376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     "jo 1f\n\t"
48476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     "pushfw\n\t"
48576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     "lcall *%%cs:int13_vector\n\t"
48676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     "\n1:\n\t"
48776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     /* Overwrite flags for iret */
48876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     "pushfw\n\t"
48976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     "popw 6(%%bp)\n\t"
49076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     /* Fix up %dl:
49176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			      *
49276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			      * INT 13,15 : do nothing
49376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			      * INT 13,08 : load with number of drives
49476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			      * all others: restore original value
49576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			      */
49676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     "cmpb $0x15, -1(%%bp)\n\t"
49776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     "je 2f\n\t"
49876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     "movb -4(%%bp), %%dl\n\t"
49976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     "cmpb $0x08, -1(%%bp)\n\t"
50076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     "jne 2f\n\t"
50176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     "pushw %%ds\n\t"
50276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     "pushw %1\n\t"
50376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     "popw %%ds\n\t"
50476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     "movb %c2, %%dl\n\t"
50576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     "popw %%ds\n\t"
50676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     /* Return */
50776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     "\n2:\n\t"
50876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     "movw %%bp, %%sp\n\t"
50976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     "popw %%bp\n\t"
51076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			     "iret\n\t" )
51176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	       : : "i" ( int13 ), "i" ( BDA_SEG ), "i" ( BDA_NUM_DRIVES ) );
51276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
51376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	hook_bios_interrupt ( 0x13, ( unsigned int ) int13_wrapper,
51476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			      &int13_vector );
51576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
51676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
51776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
51876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Unhook INT 13 handler
51976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
52076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void unhook_int13 ( void ) {
52176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unhook_bios_interrupt ( 0x13, ( unsigned int ) int13_wrapper,
52276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				&int13_vector );
52376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
52476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
52576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
52676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Guess INT 13 drive geometry
52776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
52876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v drive		Emulated drive
52976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
53076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Guesses the drive geometry by inspecting the partition table.
53176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
53276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanstatic void guess_int13_geometry ( struct int13_drive *drive ) {
53376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct master_boot_record mbr;
53476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct partition_table_entry *partition;
53576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unsigned int guessed_heads = 255;
53676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unsigned int guessed_sectors_per_track = 63;
53776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unsigned long blocks;
53876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unsigned long blocks_per_cyl;
53976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	unsigned int i;
54076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
54176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Don't even try when the blksize is invalid for C/H/S access */
54276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( drive->blockdev->blksize != INT13_BLKSIZE )
54376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return;
54476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
54576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Scan through partition table and modify guesses for heads
54676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	 * and sectors_per_track if we find any used partitions.
54776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	 */
54876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( drive->blockdev->op->read ( drive->blockdev, 0, 1,
54976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				         virt_to_user ( &mbr ) ) == 0 ) {
55076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		for ( i = 0 ; i < 4 ; i++ ) {
55176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			partition = &mbr.partitions[i];
55276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			if ( ! partition->type )
55376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				continue;
55476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			guessed_heads =
55576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				( PART_HEAD ( partition->chs_end ) + 1 );
55676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			guessed_sectors_per_track =
55776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				PART_SECTOR ( partition->chs_end );
55876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			DBG ( "Guessing C/H/S xx/%d/%d based on partition "
55976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			      "%d\n", guessed_heads,
56076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			      guessed_sectors_per_track, ( i + 1 ) );
56176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		}
56276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	} else {
56376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBG ( "Could not read partition table to guess geometry\n" );
56476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
56576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
56676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Apply guesses if no geometry already specified */
56776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( ! drive->heads )
56876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		drive->heads = guessed_heads;
56976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( ! drive->sectors_per_track )
57076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		drive->sectors_per_track = guessed_sectors_per_track;
57176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( ! drive->cylinders ) {
57276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/* Avoid attempting a 64-bit divide on a 32-bit system */
57376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		blocks = ( ( drive->blockdev->blocks <= ULONG_MAX ) ?
57476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			   drive->blockdev->blocks : ULONG_MAX );
57576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		blocks_per_cyl = ( drive->heads * drive->sectors_per_track );
57676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		assert ( blocks_per_cyl != 0 );
57776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		drive->cylinders = ( blocks / blocks_per_cyl );
57876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		if ( drive->cylinders > 1024 )
57976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			drive->cylinders = 1024;
58076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
58176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
58276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
58376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
58476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Register INT 13 emulated drive
58576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
58676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v drive		Emulated drive
58776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
58876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Registers the drive with the INT 13 emulation subsystem, and hooks
58976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * the INT 13 interrupt vector (if not already hooked).
59076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
59176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * The underlying block device must be valid.  A drive number and
59276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * geometry will be assigned if left blank.
59376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
59476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanvoid register_int13_drive ( struct int13_drive *drive ) {
59576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	uint8_t num_drives;
59676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
59776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Give drive a default geometry if none specified */
59876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	guess_int13_geometry ( drive );
59976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
60076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Assign natural drive number */
60176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	get_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES );
60276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	drive->natural_drive = ( num_drives | 0x80 );
60376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
60476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Assign drive number */
60576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( ( drive->drive & 0xff ) == 0xff ) {
60676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/* Drive number == -1 => use natural drive number */
60776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		drive->drive = drive->natural_drive;
60876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	} else {
60976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		/* Use specified drive number (+0x80 if necessary) */
61076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		drive->drive |= 0x80;
61176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
61276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
61376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBG ( "Registered INT13 drive %02x (naturally %02x) with C/H/S "
61476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	      "geometry %d/%d/%d\n", drive->drive, drive->natural_drive,
61576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	      drive->cylinders, drive->heads, drive->sectors_per_track );
61676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
61776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Hook INT 13 vector if not already hooked */
61876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( list_empty ( &drives ) )
61976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		hook_int13();
62076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
62176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Add to list of emulated drives */
62276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	list_add ( &drive->list, &drives );
62376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
62476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Update BIOS drive count */
62576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int13_set_num_drives();
62676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
62776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
62876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
62976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Unregister INT 13 emulated drive
63076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
63176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v drive		Emulated drive
63276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
63376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Unregisters the drive from the INT 13 emulation subsystem.  If this
63476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * is the last emulated drive, the INT 13 vector is unhooked (if
63576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * possible).
63676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
63776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanvoid unregister_int13_drive ( struct int13_drive *drive ) {
63876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Remove from list of emulated drives */
63976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	list_del ( &drive->list );
64076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
64176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Should adjust BIOS drive count, but it's difficult to do so
64276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	 * reliably.
64376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	 */
64476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
64576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBG ( "Unregistered INT13 drive %02x\n", drive->drive );
64676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
64776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Unhook INT 13 vector if no more drives */
64876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( list_empty ( &drives ) )
64976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		unhook_int13();
65076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
65176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
65276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman/**
65376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Attempt to boot from an INT 13 drive
65476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
65576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @v drive		Drive number
65676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * @ret rc		Return status code
65776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
65876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * This boots from the specified INT 13 drive by loading the Master
65976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Boot Record to 0000:7c00 and jumping to it.  INT 18 is hooked to
66076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * capture an attempt by the MBR to boot the next device.  (This is
66176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * the closest thing to a return path from an MBR).
66276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman *
66376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman * Note that this function can never return success, by definition.
66476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman */
66576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartmanint int13_boot ( unsigned int drive ) {
66676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	struct memory_map memmap;
66776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int status, signature;
66876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int discard_c, discard_d;
66976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	int rc;
67076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
67176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	DBG ( "Booting from INT 13 drive %02x\n", drive );
67276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
67376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Use INT 13 to read the boot sector */
67476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	__asm__ __volatile__ ( REAL_CODE ( "pushw %%es\n\t"
67576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman					   "pushw $0\n\t"
67676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman					   "popw %%es\n\t"
67776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman					   "stc\n\t"
67876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman					   "sti\n\t"
67976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman					   "int $0x13\n\t"
68076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman					   "sti\n\t" /* BIOS bugs */
68176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman					   "jc 1f\n\t"
68276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman					   "xorl %%eax, %%eax\n\t"
68376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman					   "\n1:\n\t"
68476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman					   "movzwl %%es:0x7dfe, %%ebx\n\t"
68576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman					   "popw %%es\n\t" )
68676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			       : "=a" ( status ), "=b" ( signature ),
68776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				 "=c" ( discard_c ), "=d" ( discard_d )
68876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman			       : "a" ( 0x0201 ), "b" ( 0x7c00 ),
68976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman				 "c" ( 1 ), "d" ( drive ) );
69076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( status )
69176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return -EIO;
69276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
69376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Check signature is correct */
69476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( signature != be16_to_cpu ( 0x55aa ) ) {
69576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBG ( "Invalid disk signature %#04x (should be 0x55aa)\n",
69676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		      cpu_to_be16 ( signature ) );
69776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return -ENOEXEC;
69876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
69976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
70076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Dump out memory map prior to boot, if memmap debugging is
70176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	 * enabled.  Not required for program flow, but we have so
70276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	 * many problems that turn out to be memory-map related that
70376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	 * it's worth doing.
70476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	 */
70576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	get_memmap ( &memmap );
70676d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
70776d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	/* Jump to boot sector */
70876d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	if ( ( rc = call_bootsector ( 0x0, 0x7c00, drive ) ) != 0 ) {
70976d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		DBG ( "INT 13 drive %02x boot returned: %s\n",
71076d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		      drive, strerror ( rc ) );
71176d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman		return rc;
71276d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	}
71376d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman
71476d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman	return -ECANCELED; /* -EIMPOSSIBLE */
71576d05dc695b06c4e987bb8078f78032441e1430cGreg Hartman}
716