1621540d1SAxel Dörfler/*
21bcbbd6aSAxel Dörfler * Copyright 2003-2006, Axel D��rfler, axeld@pinc-software.de.
317e0266fSAxel Dörfler * Distributed under the terms of the MIT License.
417e0266fSAxel Dörfler */
5621540d1SAxel Dörfler
6621540d1SAxel Dörfler
7502a70f8SAxel Dörfler#include "bios.h"
8502a70f8SAxel Dörfler
9597416d1SAxel Dörfler#include <KernelExport.h>
10621540d1SAxel Dörfler#include <boot/platform.h>
110f87a64bSAxel Dörfler#include <boot/partitions.h>
12502a70f8SAxel Dörfler#include <boot/stdio.h>
1317e0266fSAxel Dörfler#include <boot/stage2.h>
14502a70f8SAxel Dörfler
15502a70f8SAxel Dörfler#include <string.h>
16502a70f8SAxel Dörfler
17fabe8c62SAxel Dörfler//#define TRACE_DEVICES
188d156f1cSAxel Dörfler#ifdef TRACE_DEVICES
198d156f1cSAxel Dörfler#	define TRACE(x) dprintf x
208d156f1cSAxel Dörfler#else
218d156f1cSAxel Dörfler#	define TRACE(x) ;
228d156f1cSAxel Dörfler#endif
238d156f1cSAxel Dörfler
24502a70f8SAxel Dörfler
250f87a64bSAxel Dörfler// exported from shell.S
2651e081baSAxel Dörflerextern uint8 gBootedFromImage;
279fd75867SAxel Dörflerextern uint8 gBootDriveID;
280f87a64bSAxel Dörflerextern uint32 gBootPartitionOffset;
29502a70f8SAxel Dörfler
30502a70f8SAxel Dörfler// int 0x13 definitions
31597416d1SAxel Dörfler#define BIOS_RESET_DISK_SYSTEM			0x0000
328d156f1cSAxel Dörfler#define BIOS_READ						0x0200
33502a70f8SAxel Dörfler#define BIOS_GET_DRIVE_PARAMETERS		0x0800
34502a70f8SAxel Dörfler#define BIOS_IS_EXT_PRESENT				0x4100
35502a70f8SAxel Dörfler#define BIOS_EXT_READ					0x4200
36d1367a37SIngo Weinhold#define BIOS_EXT_WRITE					0x4300
37502a70f8SAxel Dörfler#define BIOS_GET_EXT_DRIVE_PARAMETERS	0x4800
382e2bd875SAxel Dörfler#define BIOS_BOOT_CD_GET_STATUS			0x4b01
39502a70f8SAxel Dörfler
40502a70f8SAxel Dörflerstruct real_addr {
41502a70f8SAxel Dörfler	uint16	offset;
42502a70f8SAxel Dörfler	uint16	segment;
43502a70f8SAxel Dörfler};
44502a70f8SAxel Dörfler
45502a70f8SAxel Dörflerstruct disk_address_packet {
46502a70f8SAxel Dörfler	uint8		size;
47502a70f8SAxel Dörfler	uint8		reserved;
48502a70f8SAxel Dörfler	uint16		number_of_blocks;
49502a70f8SAxel Dörfler	uint32		buffer;
50502a70f8SAxel Dörfler	uint64		lba;
518aa8345aSAugustin Cavalier} _PACKED;
52502a70f8SAxel Dörfler
5317e0266fSAxel Dörflerstatic const uint16 kParametersSizeVersion1 = 0x1a;
5417e0266fSAxel Dörflerstatic const uint16 kParametersSizeVersion2 = 0x1e;
5517e0266fSAxel Dörflerstatic const uint16 kParametersSizeVersion3 = 0x42;
5617e0266fSAxel Dörfler
5717e0266fSAxel Dörflerstatic const uint16 kDevicePathSignature = 0xbedd;
5817e0266fSAxel Dörfler
59502a70f8SAxel Dörflerstruct drive_parameters {
6017e0266fSAxel Dörfler	uint16		parameters_size;
61502a70f8SAxel Dörfler	uint16		flags;
62502a70f8SAxel Dörfler	uint32		cylinders;
63502a70f8SAxel Dörfler	uint32		heads;
64502a70f8SAxel Dörfler	uint32		sectors_per_track;
65502a70f8SAxel Dörfler	uint64		sectors;
66502a70f8SAxel Dörfler	uint16		bytes_per_sector;
67502a70f8SAxel Dörfler	/* edd 2.0 */
68502a70f8SAxel Dörfler	real_addr	device_table;
69502a70f8SAxel Dörfler	/* edd 3.0 */
70502a70f8SAxel Dörfler	uint16		device_path_signature;
71502a70f8SAxel Dörfler	uint8		device_path_size;
72502a70f8SAxel Dörfler	uint8		reserved1[3];
73502a70f8SAxel Dörfler	char		host_bus[4];
74502a70f8SAxel Dörfler	char		interface_type[8];
75502a70f8SAxel Dörfler	union {
76502a70f8SAxel Dörfler		struct {
77502a70f8SAxel Dörfler			uint16	base_address;
78502a70f8SAxel Dörfler		} legacy;
79502a70f8SAxel Dörfler		struct {
80502a70f8SAxel Dörfler			uint8	bus;
81502a70f8SAxel Dörfler			uint8	slot;
82502a70f8SAxel Dörfler			uint8	function;
83502a70f8SAxel Dörfler		} pci;
84502a70f8SAxel Dörfler		uint8		reserved[8];
85502a70f8SAxel Dörfler	} interface;
86502a70f8SAxel Dörfler	union {
87502a70f8SAxel Dörfler		struct {
88502a70f8SAxel Dörfler			uint8	slave;
89502a70f8SAxel Dörfler		} ata;
90502a70f8SAxel Dörfler		struct {
91502a70f8SAxel Dörfler			uint8	slave;
92502a70f8SAxel Dörfler			uint8	logical_unit;
93502a70f8SAxel Dörfler		} atapi;
94502a70f8SAxel Dörfler		struct {
95502a70f8SAxel Dörfler			uint8	logical_unit;
96502a70f8SAxel Dörfler		} scsi;
97502a70f8SAxel Dörfler		struct {
98502a70f8SAxel Dörfler			uint8	tbd;
99502a70f8SAxel Dörfler		} usb;
100502a70f8SAxel Dörfler		struct {
101502a70f8SAxel Dörfler			uint64	guid;
102502a70f8SAxel Dörfler		} firewire;
103502a70f8SAxel Dörfler		struct {
104502a70f8SAxel Dörfler			uint64	wwd;
105502a70f8SAxel Dörfler		} fibre;
106502a70f8SAxel Dörfler	} device;
107502a70f8SAxel Dörfler	uint8		reserved2;
108502a70f8SAxel Dörfler	uint8		checksum;
109502a70f8SAxel Dörfler} _PACKED;
110502a70f8SAxel Dörfler
11117e0266fSAxel Dörflerstruct device_table {
11217e0266fSAxel Dörfler	uint16	base_address;
11317e0266fSAxel Dörfler	uint16	control_port_address;
11417e0266fSAxel Dörfler	uint8	_reserved1 : 4;
11517e0266fSAxel Dörfler	uint8	is_slave : 1;
11617e0266fSAxel Dörfler	uint8	_reserved2 : 1;
11717e0266fSAxel Dörfler	uint8	lba_enabled : 1;
11817e0266fSAxel Dörfler} _PACKED;
119502a70f8SAxel Dörfler
12051e081baSAxel Dörflerstruct specification_packet {
12151e081baSAxel Dörfler	uint8	size;
12251e081baSAxel Dörfler	uint8	media_type;
12351e081baSAxel Dörfler	uint8	drive_number;
12451e081baSAxel Dörfler	uint8	controller_index;
12551e081baSAxel Dörfler	uint32	start_emulation;
12651e081baSAxel Dörfler	uint16	device_specification;
12751e081baSAxel Dörfler	uint8	_more_[9];
12851e081baSAxel Dörfler} _PACKED;
12951e081baSAxel Dörfler
130502a70f8SAxel Dörflerclass BIOSDrive : public Node {
131502a70f8SAxel Dörfler	public:
132502a70f8SAxel Dörfler		BIOSDrive(uint8 driveID);
133502a70f8SAxel Dörfler		virtual ~BIOSDrive();
134502a70f8SAxel Dörfler
135f1915280SAxel Dörfler		status_t InitCheck() const;
136f1915280SAxel Dörfler
137502a70f8SAxel Dörfler		virtual ssize_t ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize);
138502a70f8SAxel Dörfler		virtual ssize_t WriteAt(void *cookie, off_t pos, const void *buffer, size_t bufferSize);
139502a70f8SAxel Dörfler
140502a70f8SAxel Dörfler		virtual off_t Size() const;
141502a70f8SAxel Dörfler
1424c03f90fSAxel Dörfler		uint32 BlockSize() const { return fBlockSize; }
1434c03f90fSAxel Dörfler
1441bcbbd6aSAxel Dörfler		status_t FillIdentifier();
1451bcbbd6aSAxel Dörfler
14617e0266fSAxel Dörfler		bool HasParameters() const { return fHasParameters; }
14717e0266fSAxel Dörfler		const drive_parameters &Parameters() const { return fParameters; }
1481bcbbd6aSAxel Dörfler
1491bcbbd6aSAxel Dörfler		disk_identifier &Identifier() { return fIdentifier; }
150597416d1SAxel Dörfler		uint8 DriveID() const { return fDriveID; }
15117e0266fSAxel Dörfler
152502a70f8SAxel Dörfler	protected:
153502a70f8SAxel Dörfler		uint8	fDriveID;
154502a70f8SAxel Dörfler		bool	fLBA;
155502a70f8SAxel Dörfler		uint64	fSize;
156502a70f8SAxel Dörfler		uint32	fBlockSize;
15717e0266fSAxel Dörfler		bool	fHasParameters;
158502a70f8SAxel Dörfler		drive_parameters fParameters;
1591bcbbd6aSAxel Dörfler		disk_identifier fIdentifier;
160502a70f8SAxel Dörfler};
161502a70f8SAxel Dörfler
162502a70f8SAxel Dörfler
163ecdaf9deSAxel Dörflerstatic bool sBlockDevicesAdded = false;
164ecdaf9deSAxel Dörfler
165ecdaf9deSAxel Dörfler
1662e2bd875SAxel Dörflerstatic void
167597416d1SAxel Dörflercheck_cd_boot(BIOSDrive *drive)
1682e2bd875SAxel Dörfler{
16993cb9538SAlex Smith	gBootVolume.SetInt32(BOOT_METHOD, BOOT_METHOD_HARD_DISK);
1702e2bd875SAxel Dörfler
171597416d1SAxel Dörfler	if (drive->DriveID() != 0)
1722e2bd875SAxel Dörfler		return;
1732e2bd875SAxel Dörfler
1742e2bd875SAxel Dörfler	struct bios_regs regs;
1752e2bd875SAxel Dörfler	regs.eax = BIOS_BOOT_CD_GET_STATUS;
1762e2bd875SAxel Dörfler	regs.edx = 0;
1772e2bd875SAxel Dörfler	regs.esi = kDataSegmentScratch;
1782e2bd875SAxel Dörfler	call_bios(0x13, &regs);
1792e2bd875SAxel Dörfler
1802e2bd875SAxel Dörfler	if ((regs.flags & CARRY_FLAG) != 0)
1812e2bd875SAxel Dörfler		return;
1822e2bd875SAxel Dörfler
1832e2bd875SAxel Dörfler	// we obviously were booted from CD!
1842e2bd875SAxel Dörfler
1852e2bd875SAxel Dörfler	specification_packet *packet = (specification_packet *)kDataSegmentScratch;
1869e8dc2a9SIngo Weinhold	if (packet->media_type != 0)
18793cb9538SAlex Smith		gBootVolume.SetInt32(BOOT_METHOD, BOOT_METHOD_CD);
1882e2bd875SAxel Dörfler
1892e2bd875SAxel Dörfler#if 0
1902e2bd875SAxel Dörfler	dprintf("got CD boot spec:\n");
1912e2bd875SAxel Dörfler	dprintf("  size: %#x\n", packet->size);
1922e2bd875SAxel Dörfler	dprintf("  media type: %u\n", packet->media_type);
1932e2bd875SAxel Dörfler	dprintf("  drive_number: %u\n", packet->drive_number);
1942e2bd875SAxel Dörfler	dprintf("  controller index: %u\n", packet->controller_index);
1952e2bd875SAxel Dörfler	dprintf("  start emulation: %lu\n", packet->start_emulation);
1962e2bd875SAxel Dörfler	dprintf("  device_specification: %u\n", packet->device_specification);
1972e2bd875SAxel Dörfler#endif
1982e2bd875SAxel Dörfler}
1992e2bd875SAxel Dörfler
2002e2bd875SAxel Dörfler
20103a574d8SMichael Lotzstatic bool
20203a574d8SMichael Lotzare_extensions_available(uint8 drive)
20303a574d8SMichael Lotz{
20403a574d8SMichael Lotz	struct bios_regs regs;
20503a574d8SMichael Lotz	regs.eax = BIOS_IS_EXT_PRESENT;
20603a574d8SMichael Lotz	regs.ebx = 0x55aa;
20703a574d8SMichael Lotz	regs.edx = drive;
20803a574d8SMichael Lotz	call_bios(0x13, &regs);
20903a574d8SMichael Lotz
21003a574d8SMichael Lotz	TRACE(("checking extensions: carry: %u; ebx: 0x%08lx; ecx: 0x%08lx\n",
21103a574d8SMichael Lotz		regs.flags & CARRY_FLAG, regs.ebx, regs.ecx));
21203a574d8SMichael Lotz	return (regs.flags & CARRY_FLAG) == 0 && regs.ebx == 0xaa55
21303a574d8SMichael Lotz		&& (regs.ecx & 0x01 /* supports device access using packet */) != 0;
21403a574d8SMichael Lotz}
21503a574d8SMichael Lotz
21603a574d8SMichael Lotz
217502a70f8SAxel Dörflerstatic status_t
218502a70f8SAxel Dörflerget_ext_drive_parameters(uint8 drive, drive_parameters *targetParameters)
219502a70f8SAxel Dörfler{
220502a70f8SAxel Dörfler	drive_parameters *parameter = (drive_parameters *)kDataSegmentScratch;
221597416d1SAxel Dörfler
2225a54b401SAxel Dörfler	memset(parameter, 0, sizeof(drive_parameters));
22317e0266fSAxel Dörfler	parameter->parameters_size = sizeof(drive_parameters);
224502a70f8SAxel Dörfler
225502a70f8SAxel Dörfler	struct bios_regs regs;
226502a70f8SAxel Dörfler	regs.eax = BIOS_GET_EXT_DRIVE_PARAMETERS;
227502a70f8SAxel Dörfler	regs.edx = drive;
228502a70f8SAxel Dörfler	regs.esi = (addr_t)parameter - kDataSegmentBase;
229502a70f8SAxel Dörfler	call_bios(0x13, &regs);
230502a70f8SAxel Dörfler
231597416d1SAxel Dörfler	// filter out faulty BIOS return codes
232597416d1SAxel Dörfler	if ((regs.flags & CARRY_FLAG) != 0
233597416d1SAxel Dörfler		|| parameter->sectors == 0)
234502a70f8SAxel Dörfler		return B_ERROR;
235502a70f8SAxel Dörfler
236502a70f8SAxel Dörfler	memcpy(targetParameters, parameter, sizeof(drive_parameters));
237502a70f8SAxel Dörfler	return B_OK;
238502a70f8SAxel Dörfler}
239502a70f8SAxel Dörfler
240502a70f8SAxel Dörfler
2418d156f1cSAxel Dörflerstatic status_t
2428d156f1cSAxel Dörflerget_drive_parameters(uint8 drive, drive_parameters *parameters)
2438d156f1cSAxel Dörfler{
2448d156f1cSAxel Dörfler	struct bios_regs regs;
2458d156f1cSAxel Dörfler	regs.eax = BIOS_GET_DRIVE_PARAMETERS;
2468d156f1cSAxel Dörfler	regs.edx = drive;
2478d156f1cSAxel Dörfler	regs.es = 0;
2488d156f1cSAxel Dörfler	regs.edi = 0;	// guard against faulty BIOS, see Ralf Brown's interrupt list
2498d156f1cSAxel Dörfler	call_bios(0x13, &regs);
2508d156f1cSAxel Dörfler
2518d156f1cSAxel Dörfler	if ((regs.flags & CARRY_FLAG) != 0 || (regs.ecx & 0x3f) == 0)
2528d156f1cSAxel Dörfler		return B_ERROR;
2538d156f1cSAxel Dörfler
2548d156f1cSAxel Dörfler	// fill drive_parameters structure with useful values
2558d156f1cSAxel Dörfler	parameters->parameters_size = kParametersSizeVersion1;
2568d156f1cSAxel Dörfler	parameters->flags = 0;
2577416092bSAxel Dörfler	parameters->cylinders = (((regs.ecx & 0xc0) << 2) | ((regs.ecx >> 8) & 0xff)) + 1;
2588d156f1cSAxel Dörfler	parameters->heads = ((regs.edx >> 8) & 0xff) + 1;
2598d156f1cSAxel Dörfler		// heads and cylinders start counting from 0
2608d156f1cSAxel Dörfler	parameters->sectors_per_track = regs.ecx & 0x3f;
2618d156f1cSAxel Dörfler	parameters->sectors = parameters->cylinders * parameters->heads
2628d156f1cSAxel Dörfler		* parameters->sectors_per_track;
2638d156f1cSAxel Dörfler	parameters->bytes_per_sector = 512;
2648d156f1cSAxel Dörfler
2658d156f1cSAxel Dörfler	return B_OK;
2668d156f1cSAxel Dörfler}
2678d156f1cSAxel Dörfler
2688d156f1cSAxel Dörfler
269502a70f8SAxel Dörflerstatic status_t
270502a70f8SAxel Dörflerget_number_of_drives(uint8 *_count)
271502a70f8SAxel Dörfler{
272502a70f8SAxel Dörfler	struct bios_regs regs;
2735a54b401SAxel Dörfler	regs.eax = BIOS_GET_DRIVE_PARAMETERS;
274502a70f8SAxel Dörfler	regs.edx = 0x80;
275502a70f8SAxel Dörfler	regs.es = 0;
276502a70f8SAxel Dörfler	regs.edi = 0;
277502a70f8SAxel Dörfler	call_bios(0x13, &regs);
278502a70f8SAxel Dörfler
279502a70f8SAxel Dörfler	if (regs.flags & CARRY_FLAG)
280502a70f8SAxel Dörfler		return B_ERROR;
281502a70f8SAxel Dörfler
282502a70f8SAxel Dörfler	*_count = regs.edx & 0xff;
283502a70f8SAxel Dörfler	return B_OK;
284502a70f8SAxel Dörfler}
285502a70f8SAxel Dörfler
286502a70f8SAxel Dörfler
28717e0266fSAxel Dörfler/** parse EDD 3.0 drive path information */
28817e0266fSAxel Dörfler
28917e0266fSAxel Dörflerstatic status_t
29017e0266fSAxel Dörflerfill_disk_identifier_v3(disk_identifier &disk, const drive_parameters &parameters)
29117e0266fSAxel Dörfler{
29217e0266fSAxel Dörfler	if (parameters.parameters_size < kParametersSizeVersion3
29317e0266fSAxel Dörfler		|| parameters.device_path_signature != kDevicePathSignature)
29417e0266fSAxel Dörfler		return B_BAD_TYPE;
29517e0266fSAxel Dörfler
29617e0266fSAxel Dörfler	// parse host bus
29717e0266fSAxel Dörfler
29817e0266fSAxel Dörfler	if (!strncmp(parameters.host_bus, "PCI", 3)) {
29917e0266fSAxel Dörfler		disk.bus_type = PCI_BUS;
30017e0266fSAxel Dörfler
30117e0266fSAxel Dörfler		disk.bus.pci.bus = parameters.interface.pci.bus;
30217e0266fSAxel Dörfler		disk.bus.pci.slot = parameters.interface.pci.slot;
30317e0266fSAxel Dörfler		disk.bus.pci.function = parameters.interface.pci.function;
30417e0266fSAxel Dörfler	} else if (!strncmp(parameters.host_bus, "ISA", 3)) {
30517e0266fSAxel Dörfler		disk.bus_type = LEGACY_BUS;
30617e0266fSAxel Dörfler
30717e0266fSAxel Dörfler		disk.bus.legacy.base_address = parameters.interface.legacy.base_address;
3081bcbbd6aSAxel Dörfler		dprintf("legacy base address %x\n", disk.bus.legacy.base_address);
30917e0266fSAxel Dörfler	} else {
31017e0266fSAxel Dörfler		dprintf("unknown host bus \"%s\"\n", parameters.host_bus);
31117e0266fSAxel Dörfler		return B_BAD_DATA;
31217e0266fSAxel Dörfler	}
31317e0266fSAxel Dörfler
31417e0266fSAxel Dörfler	// parse interface
31517e0266fSAxel Dörfler
31617e0266fSAxel Dörfler	if (!strncmp(parameters.interface_type, "ATA", 3)) {
31717e0266fSAxel Dörfler		disk.device_type = ATA_DEVICE;
31817e0266fSAxel Dörfler		disk.device.ata.master = !parameters.device.ata.slave;
3191bcbbd6aSAxel Dörfler		dprintf("ATA device, %s\n", disk.device.ata.master ? "master" : "slave");
32017e0266fSAxel Dörfler	} else if (!strncmp(parameters.interface_type, "ATAPI", 3)) {
32117e0266fSAxel Dörfler		disk.device_type = ATAPI_DEVICE;
32217e0266fSAxel Dörfler		disk.device.atapi.master = !parameters.device.ata.slave;
32317e0266fSAxel Dörfler		disk.device.atapi.logical_unit = parameters.device.atapi.logical_unit;
32417e0266fSAxel Dörfler	} else if (!strncmp(parameters.interface_type, "SCSI", 3)) {
32517e0266fSAxel Dörfler		disk.device_type = SCSI_DEVICE;
32617e0266fSAxel Dörfler		disk.device.scsi.logical_unit = parameters.device.scsi.logical_unit;
32717e0266fSAxel Dörfler	} else if (!strncmp(parameters.interface_type, "USB", 3)) {
32817e0266fSAxel Dörfler		disk.device_type = USB_DEVICE;
32917e0266fSAxel Dörfler		disk.device.usb.tbd = parameters.device.usb.tbd;
33017e0266fSAxel Dörfler	} else if (!strncmp(parameters.interface_type, "1394", 3)) {
33117e0266fSAxel Dörfler		disk.device_type = FIREWIRE_DEVICE;
33217e0266fSAxel Dörfler		disk.device.firewire.guid = parameters.device.firewire.guid;
33317e0266fSAxel Dörfler	} else if (!strncmp(parameters.interface_type, "FIBRE", 3)) {
33417e0266fSAxel Dörfler		disk.device_type = FIBRE_DEVICE;
33517e0266fSAxel Dörfler		disk.device.fibre.wwd = parameters.device.fibre.wwd;
33617e0266fSAxel Dörfler	} else {
33717e0266fSAxel Dörfler		dprintf("unknown interface type \"%s\"\n", parameters.interface_type);
33817e0266fSAxel Dörfler		return B_BAD_DATA;
33917e0266fSAxel Dörfler	}
34017e0266fSAxel Dörfler
34117e0266fSAxel Dörfler	return B_OK;
34217e0266fSAxel Dörfler}
34317e0266fSAxel Dörfler
34417e0266fSAxel Dörfler
34517e0266fSAxel Dörfler/** EDD 2.0 drive table information */
34617e0266fSAxel Dörfler
34717e0266fSAxel Dörflerstatic status_t
34817e0266fSAxel Dörflerfill_disk_identifier_v2(disk_identifier &disk, const drive_parameters &parameters)
34917e0266fSAxel Dörfler{
35017e0266fSAxel Dörfler	if (parameters.device_table.segment == 0xffff
35117e0266fSAxel Dörfler		&& parameters.device_table.offset == 0xffff)
35217e0266fSAxel Dörfler		return B_BAD_TYPE;
35317e0266fSAxel Dörfler
35417e0266fSAxel Dörfler	device_table *table = (device_table *)LINEAR_ADDRESS(parameters.device_table.segment,
35517e0266fSAxel Dörfler		parameters.device_table.offset);
35617e0266fSAxel Dörfler
35717e0266fSAxel Dörfler	disk.bus_type = LEGACY_BUS;
35817e0266fSAxel Dörfler	disk.bus.legacy.base_address = table->base_address;
35917e0266fSAxel Dörfler
36017e0266fSAxel Dörfler	disk.device_type = ATA_DEVICE;
36117e0266fSAxel Dörfler	disk.device.ata.master = !table->is_slave;
36217e0266fSAxel Dörfler
36317e0266fSAxel Dörfler	return B_OK;
36417e0266fSAxel Dörfler}
36517e0266fSAxel Dörfler
36617e0266fSAxel Dörfler
3671bcbbd6aSAxel Dörflerstatic off_t
3681bcbbd6aSAxel Dörflerget_next_check_sum_offset(int32 index, off_t maxSize)
3691bcbbd6aSAxel Dörfler{
37046cf7a5aSPrzemysław Buczkowski	// The boot block often contains the disk superblock, and should be
3711bcbbd6aSAxel Dörfler	// unique enough for most cases
3721bcbbd6aSAxel Dörfler	if (index < 2)
3731bcbbd6aSAxel Dörfler		return index * 512;
3741bcbbd6aSAxel Dörfler
3751bcbbd6aSAxel Dörfler	// Try some data in the first part of the drive
3761bcbbd6aSAxel Dörfler	if (index < 4)
3771bcbbd6aSAxel Dörfler		return (maxSize >> 10) + index * 2048;
3781bcbbd6aSAxel Dörfler
3791bcbbd6aSAxel Dörfler	// Some random value might do
3801bcbbd6aSAxel Dörfler	return ((system_time() + index) % (maxSize >> 9)) * 512;
3811bcbbd6aSAxel Dörfler}
3821bcbbd6aSAxel Dörfler
3831bcbbd6aSAxel Dörfler
3841bcbbd6aSAxel Dörfler/**	Computes a check sum for the specified block.
3851bcbbd6aSAxel Dörfler *	The check sum is the sum of all data in that block interpreted as an
3861bcbbd6aSAxel Dörfler *	array of uint32 values.
387fabe8c62SAxel Dörfler *	Note, this must use the same method as the one used in kernel/fs/vfs_boot.cpp.
3881bcbbd6aSAxel Dörfler */
3891bcbbd6aSAxel Dörfler
3901bcbbd6aSAxel Dörflerstatic uint32
3911bcbbd6aSAxel Dörflercompute_check_sum(BIOSDrive *drive, off_t offset)
3921bcbbd6aSAxel Dörfler{
3931bcbbd6aSAxel Dörfler	char buffer[512];
3941bcbbd6aSAxel Dörfler	ssize_t bytesRead = drive->ReadAt(NULL, offset, buffer, sizeof(buffer));
3951bcbbd6aSAxel Dörfler	if (bytesRead < B_OK)
3961bcbbd6aSAxel Dörfler		return 0;
3971bcbbd6aSAxel Dörfler
3981bcbbd6aSAxel Dörfler	if (bytesRead < (ssize_t)sizeof(buffer))
3991bcbbd6aSAxel Dörfler		memset(buffer + bytesRead, 0, sizeof(buffer) - bytesRead);
4001bcbbd6aSAxel Dörfler
4011bcbbd6aSAxel Dörfler	uint32 *array = (uint32 *)buffer;
4021bcbbd6aSAxel Dörfler	uint32 sum = 0;
4031bcbbd6aSAxel Dörfler
4041bcbbd6aSAxel Dörfler	for (uint32 i = 0; i < (bytesRead + sizeof(uint32) - 1) / sizeof(uint32); i++) {
4051bcbbd6aSAxel Dörfler		sum += array[i];
4061bcbbd6aSAxel Dörfler	}
4071bcbbd6aSAxel Dörfler
4081bcbbd6aSAxel Dörfler	return sum;
4091bcbbd6aSAxel Dörfler}
4101bcbbd6aSAxel Dörfler
411679ad262SMichał Siejak/**	Checks if the specified drive is usable for reading.
412679ad262SMichał Siejak */
413679ad262SMichał Siejak
414679ad262SMichał Siejakstatic bool
415679ad262SMichał Siejakis_drive_readable(BIOSDrive *drive)
416679ad262SMichał Siejak{
417679ad262SMichał Siejak	char buffer;
418679ad262SMichał Siejak	return drive->ReadAt(NULL, 0, &buffer, sizeof(buffer)) > 0;
419679ad262SMichał Siejak}
420679ad262SMichał Siejak
4211bcbbd6aSAxel Dörfler
4221bcbbd6aSAxel Dörflerstatic void
4231bcbbd6aSAxel Dörflerfind_unique_check_sums(NodeList *devices)
4241bcbbd6aSAxel Dörfler{
4251bcbbd6aSAxel Dörfler	NodeIterator iterator = devices->GetIterator();
4261bcbbd6aSAxel Dörfler	Node *device;
4271bcbbd6aSAxel Dörfler	int32 index = 0;
4281bcbbd6aSAxel Dörfler	off_t minSize = 0;
4291bcbbd6aSAxel Dörfler	const int32 kMaxTries = 200;
4301bcbbd6aSAxel Dörfler
4311bcbbd6aSAxel Dörfler	while (index < kMaxTries) {
4321bcbbd6aSAxel Dörfler		bool clash = false;
4331bcbbd6aSAxel Dörfler
4341bcbbd6aSAxel Dörfler		iterator.Rewind();
4351bcbbd6aSAxel Dörfler
4361bcbbd6aSAxel Dörfler		while ((device = iterator.Next()) != NULL) {
4371bcbbd6aSAxel Dörfler			BIOSDrive *drive = (BIOSDrive *)device;
4381bcbbd6aSAxel Dörfler#if 0
4391bcbbd6aSAxel Dörfler			// there is no RTTI in the boot loader...
4401bcbbd6aSAxel Dörfler			BIOSDrive *drive = dynamic_cast<BIOSDrive *>(device);
4411bcbbd6aSAxel Dörfler			if (drive == NULL)
4421bcbbd6aSAxel Dörfler				continue;
4431bcbbd6aSAxel Dörfler#endif
4441bcbbd6aSAxel Dörfler
4451bcbbd6aSAxel Dörfler			// TODO: currently, we assume that the BIOS provided us with unique
4461bcbbd6aSAxel Dörfler			//	disk identifiers... hopefully this is a good idea
4471bcbbd6aSAxel Dörfler			if (drive->Identifier().device_type != UNKNOWN_DEVICE)
4481bcbbd6aSAxel Dörfler				continue;
4491bcbbd6aSAxel Dörfler
4501bcbbd6aSAxel Dörfler			if (minSize == 0 || drive->Size() < minSize)
4511bcbbd6aSAxel Dörfler				minSize = drive->Size();
4521bcbbd6aSAxel Dörfler
4531bcbbd6aSAxel Dörfler			// check for clashes
4541bcbbd6aSAxel Dörfler
4551bcbbd6aSAxel Dörfler			NodeIterator compareIterator = devices->GetIterator();
4561bcbbd6aSAxel Dörfler			while ((device = compareIterator.Next()) != NULL) {
4571bcbbd6aSAxel Dörfler				BIOSDrive *compareDrive = (BIOSDrive *)device;
4581bcbbd6aSAxel Dörfler
4591bcbbd6aSAxel Dörfler				if (compareDrive == drive
4601bcbbd6aSAxel Dörfler					|| compareDrive->Identifier().device_type != UNKNOWN_DEVICE)
4611bcbbd6aSAxel Dörfler					continue;
4621bcbbd6aSAxel Dörfler
4637b1dee39SIngo Weinhold// TODO: Until we can actually get and compare *all* fields of the disk
4647b1dee39SIngo Weinhold// identifier in the kernel, we cannot compare the whole structure (we also
4657b1dee39SIngo Weinhold// should be more careful zeroing the structure before we fill it).
4667b1dee39SIngo Weinhold#if 0
4671bcbbd6aSAxel Dörfler				if (!memcmp(&drive->Identifier(), &compareDrive->Identifier(),
4681bcbbd6aSAxel Dörfler						sizeof(disk_identifier))) {
4691bcbbd6aSAxel Dörfler					clash = true;
4701bcbbd6aSAxel Dörfler					break;
4711bcbbd6aSAxel Dörfler				}
4727b1dee39SIngo Weinhold#else
4737b1dee39SIngo Weinhold				const disk_identifier& ourId = drive->Identifier();
4747b1dee39SIngo Weinhold				const disk_identifier& otherId = compareDrive->Identifier();
4757b1dee39SIngo Weinhold				if (memcmp(&ourId.device.unknown.check_sums,
4767b1dee39SIngo Weinhold						&otherId.device.unknown.check_sums,
4777b1dee39SIngo Weinhold						sizeof(ourId.device.unknown.check_sums)) == 0) {
4787b1dee39SIngo Weinhold					clash = true;
4797b1dee39SIngo Weinhold				}
4807b1dee39SIngo Weinhold#endif
4811bcbbd6aSAxel Dörfler			}
4821bcbbd6aSAxel Dörfler
4831bcbbd6aSAxel Dörfler			if (clash)
4841bcbbd6aSAxel Dörfler				break;
4851bcbbd6aSAxel Dörfler		}
4861bcbbd6aSAxel Dörfler
4871bcbbd6aSAxel Dörfler		if (!clash) {
4881bcbbd6aSAxel Dörfler			// our work here is done.
4891bcbbd6aSAxel Dörfler			return;
4901bcbbd6aSAxel Dörfler		}
4911bcbbd6aSAxel Dörfler
4921bcbbd6aSAxel Dörfler		// add a new block to the check sums
4931bcbbd6aSAxel Dörfler
4941bcbbd6aSAxel Dörfler		off_t offset = get_next_check_sum_offset(index, minSize);
4951bcbbd6aSAxel Dörfler		int32 i = index % NUM_DISK_CHECK_SUMS;
4961bcbbd6aSAxel Dörfler		iterator.Rewind();
4971bcbbd6aSAxel Dörfler
4981bcbbd6aSAxel Dörfler		while ((device = iterator.Next()) != NULL) {
4991bcbbd6aSAxel Dörfler			BIOSDrive *drive = (BIOSDrive *)device;
5001bcbbd6aSAxel Dörfler
5011bcbbd6aSAxel Dörfler			disk_identifier& disk = drive->Identifier();
5021bcbbd6aSAxel Dörfler			disk.device.unknown.check_sums[i].offset = offset;
5031bcbbd6aSAxel Dörfler			disk.device.unknown.check_sums[i].sum = compute_check_sum(drive, offset);
5041bcbbd6aSAxel Dörfler
5051bcbbd6aSAxel Dörfler			TRACE(("disk %x, offset %Ld, sum %lu\n", drive->DriveID(), offset,
5061bcbbd6aSAxel Dörfler				disk.device.unknown.check_sums[i].sum));
5071bcbbd6aSAxel Dörfler		}
5081bcbbd6aSAxel Dörfler
5091bcbbd6aSAxel Dörfler		index++;
5101bcbbd6aSAxel Dörfler	}
5111bcbbd6aSAxel Dörfler
5121bcbbd6aSAxel Dörfler	// If we get here, we couldn't find a way to differentiate all disks from each other.
5131bcbbd6aSAxel Dörfler	// It's very likely that one disk is an exact copy of the other, so there is nothing
5141bcbbd6aSAxel Dörfler	// we could do, anyway.
5151bcbbd6aSAxel Dörfler
5161bcbbd6aSAxel Dörfler	dprintf("Could not make BIOS drives unique! Might boot from the wrong disk...\n");
5171bcbbd6aSAxel Dörfler}
5181bcbbd6aSAxel Dörfler
5191bcbbd6aSAxel Dörfler
520ecdaf9deSAxel Dörflerstatic status_t
521ecdaf9deSAxel Dörfleradd_block_devices(NodeList *devicesList, bool identifierMissing)
522502a70f8SAxel Dörfler{
523ecdaf9deSAxel Dörfler	if (sBlockDevicesAdded)
524ecdaf9deSAxel Dörfler		return B_OK;
525502a70f8SAxel Dörfler
526ecdaf9deSAxel Dörfler	uint8 driveCount;
527ecdaf9deSAxel Dörfler	if (get_number_of_drives(&driveCount) != B_OK)
528ecdaf9deSAxel Dörfler		return B_ERROR;
529502a70f8SAxel Dörfler
530ecdaf9deSAxel Dörfler	dprintf("number of drives: %d\n", driveCount);
531ecdaf9deSAxel Dörfler
532ecdaf9deSAxel Dörfler	for (int32 i = 0; i < driveCount; i++) {
533ecdaf9deSAxel Dörfler		uint8 driveID = i + 0x80;
534ecdaf9deSAxel Dörfler		if (driveID == gBootDriveID)
535ecdaf9deSAxel Dörfler			continue;
536ecdaf9deSAxel Dörfler
53782029bdaSMarcus Overhagen		BIOSDrive *drive = new(nothrow) BIOSDrive(driveID);
538ecdaf9deSAxel Dörfler		if (drive->InitCheck() != B_OK) {
539ecdaf9deSAxel Dörfler			dprintf("could not add drive %u\n", driveID);
540ecdaf9deSAxel Dörfler			delete drive;
541ecdaf9deSAxel Dörfler			continue;
542502a70f8SAxel Dörfler		}
543ecdaf9deSAxel Dörfler
544679ad262SMichał Siejak		// Only add usable drives
545679ad262SMichał Siejak		if (is_drive_readable(drive))
546679ad262SMichał Siejak			devicesList->Add(drive);
547679ad262SMichał Siejak		else {
548679ad262SMichał Siejak			dprintf("could not read from drive %" B_PRIu8 ", not adding\n", driveID);
549679ad262SMichał Siejak			delete drive;
550679ad262SMichał Siejak			continue;
551679ad262SMichał Siejak		}
552ecdaf9deSAxel Dörfler
553ecdaf9deSAxel Dörfler		if (drive->FillIdentifier() != B_OK)
554ecdaf9deSAxel Dörfler			identifierMissing = true;
555ecdaf9deSAxel Dörfler	}
556ecdaf9deSAxel Dörfler
557ecdaf9deSAxel Dörfler	if (identifierMissing) {
558ecdaf9deSAxel Dörfler		// we cannot distinguish between all drives by identifier, we need
559ecdaf9deSAxel Dörfler		// compute checksums for them
560ecdaf9deSAxel Dörfler		find_unique_check_sums(devicesList);
561502a70f8SAxel Dörfler	}
562ecdaf9deSAxel Dörfler
563ecdaf9deSAxel Dörfler	sBlockDevicesAdded = true;
564d1367a37SIngo Weinhold	return B_OK;
565502a70f8SAxel Dörfler}
566502a70f8SAxel Dörfler
567502a70f8SAxel Dörfler
568502a70f8SAxel Dörfler//	#pragma mark -
569502a70f8SAxel Dörfler
570502a70f8SAxel Dörfler
571502a70f8SAxel DörflerBIOSDrive::BIOSDrive(uint8 driveID)
572502a70f8SAxel Dörfler	:
573447e6190SIngo Weinhold	fDriveID(driveID),
574447e6190SIngo Weinhold	fSize(0)
575502a70f8SAxel Dörfler{
5768d156f1cSAxel Dörfler	TRACE(("drive ID %u\n", driveID));
5778d156f1cSAxel Dörfler
57803a574d8SMichael Lotz	if (driveID < 0x80 || !are_extensions_available(driveID)
5795c8e597eSMichael Lotz		|| get_ext_drive_parameters(driveID, &fParameters) != B_OK) {
5808d156f1cSAxel Dörfler		// old style CHS support
5818d156f1cSAxel Dörfler
582597416d1SAxel Dörfler		if (get_drive_parameters(driveID, &fParameters) != B_OK) {
583597416d1SAxel Dörfler			dprintf("getting drive parameters for: %u failed!\n", fDriveID);
5848d156f1cSAxel Dörfler			return;
585597416d1SAxel Dörfler		}
5868d156f1cSAxel Dörfler
5878d156f1cSAxel Dörfler		TRACE(("  cylinders: %lu, heads: %lu, sectors: %lu, bytes_per_sector: %u\n",
5888d156f1cSAxel Dörfler			fParameters.cylinders, fParameters.heads, fParameters.sectors_per_track,
5898d156f1cSAxel Dörfler			fParameters.bytes_per_sector));
5908d156f1cSAxel Dörfler		TRACE(("  total sectors: %Ld\n", fParameters.sectors));
5918d156f1cSAxel Dörfler
592502a70f8SAxel Dörfler		fBlockSize = 512;
5938d156f1cSAxel Dörfler		fSize = fParameters.sectors * fBlockSize;
594502a70f8SAxel Dörfler		fLBA = false;
59517e0266fSAxel Dörfler		fHasParameters = false;
596502a70f8SAxel Dörfler	} else {
5978d156f1cSAxel Dörfler		TRACE(("size: %x\n", fParameters.parameters_size));
5988d156f1cSAxel Dörfler		TRACE(("drive_path_signature: %x\n", fParameters.device_path_signature));
5998d156f1cSAxel Dörfler		TRACE(("host bus: \"%s\", interface: \"%s\"\n", fParameters.host_bus,
6008d156f1cSAxel Dörfler			fParameters.interface_type));
6018d156f1cSAxel Dörfler		TRACE(("cylinders: %lu, heads: %lu, sectors: %lu, bytes_per_sector: %u\n",
602502a70f8SAxel Dörfler			fParameters.cylinders, fParameters.heads, fParameters.sectors_per_track,
6038d156f1cSAxel Dörfler			fParameters.bytes_per_sector));
6048d156f1cSAxel Dörfler		TRACE(("total sectors: %Ld\n", fParameters.sectors));
605502a70f8SAxel Dörfler
606502a70f8SAxel Dörfler		fBlockSize = fParameters.bytes_per_sector;
607502a70f8SAxel Dörfler		fSize = fParameters.sectors * fBlockSize;
608502a70f8SAxel Dörfler		fLBA = true;
60917e0266fSAxel Dörfler		fHasParameters = true;
610502a70f8SAxel Dörfler	}
611502a70f8SAxel Dörfler}
612502a70f8SAxel Dörfler
613502a70f8SAxel Dörfler
614502a70f8SAxel DörflerBIOSDrive::~BIOSDrive()
615502a70f8SAxel Dörfler{
616502a70f8SAxel Dörfler}
617502a70f8SAxel Dörfler
618502a70f8SAxel Dörfler
619d1367a37SIngo Weinholdstatus_t
620f1915280SAxel DörflerBIOSDrive::InitCheck() const
621f1915280SAxel Dörfler{
6228d156f1cSAxel Dörfler	return fSize > 0 ? B_OK : B_ERROR;
623f1915280SAxel Dörfler}
624f1915280SAxel Dörfler
625f1915280SAxel Dörfler
626d1367a37SIngo Weinholdssize_t
627502a70f8SAxel DörflerBIOSDrive::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize)
628502a70f8SAxel Dörfler{
629502a70f8SAxel Dörfler	uint32 offset = pos % fBlockSize;
630502a70f8SAxel Dörfler	pos /= fBlockSize;
631502a70f8SAxel Dörfler
632502a70f8SAxel Dörfler	uint32 blocksLeft = (bufferSize + offset + fBlockSize - 1) / fBlockSize;
6338d156f1cSAxel Dörfler	int32 totalBytesRead = 0;
634502a70f8SAxel Dörfler
6351bcbbd6aSAxel Dörfler	//TRACE(("BIOS reads %lu bytes from %Ld (offset = %lu), drive %u\n",
6361bcbbd6aSAxel Dörfler	//	blocksLeft * fBlockSize, pos * fBlockSize, offset, fDriveID));
637502a70f8SAxel Dörfler
638502a70f8SAxel Dörfler	uint32 scratchSize = 24 * 1024 / fBlockSize;
639502a70f8SAxel Dörfler		// maximum value allowed by Phoenix BIOS is 0x7f
640502a70f8SAxel Dörfler
641502a70f8SAxel Dörfler	while (blocksLeft > 0) {
6428d156f1cSAxel Dörfler		uint32 blocksRead = blocksLeft;
6438d156f1cSAxel Dörfler		if (blocksRead > scratchSize)
6448d156f1cSAxel Dörfler			blocksRead = scratchSize;
645502a70f8SAxel Dörfler
646502a70f8SAxel Dörfler		if (fLBA) {
647502a70f8SAxel Dörfler			struct disk_address_packet *packet = (disk_address_packet *)kDataSegmentScratch;
648502a70f8SAxel Dörfler			memset(packet, 0, sizeof(disk_address_packet));
649502a70f8SAxel Dörfler
650502a70f8SAxel Dörfler			packet->size = sizeof(disk_address_packet);
651502a70f8SAxel Dörfler			packet->number_of_blocks = blocksRead;
652502a70f8SAxel Dörfler			packet->buffer = kExtraSegmentScratch;
653502a70f8SAxel Dörfler			packet->lba = pos;
654502a70f8SAxel Dörfler
655502a70f8SAxel Dörfler			struct bios_regs regs;
656502a70f8SAxel Dörfler			regs.eax = BIOS_EXT_READ;
657502a70f8SAxel Dörfler			regs.edx = fDriveID;
658502a70f8SAxel Dörfler			regs.esi = (addr_t)packet - kDataSegmentBase;
659502a70f8SAxel Dörfler			call_bios(0x13, &regs);
660502a70f8SAxel Dörfler
661502a70f8SAxel Dörfler			if (regs.flags & CARRY_FLAG)
662502a70f8SAxel Dörfler				goto chs_read;
663502a70f8SAxel Dörfler		} else {
664502a70f8SAxel Dörfler	chs_read:
6658d156f1cSAxel Dörfler			// Old style CHS read routine
6668d156f1cSAxel Dörfler
6678d156f1cSAxel Dörfler			// We can only read up to 64 kB this way, but since scratchSize
6688d156f1cSAxel Dörfler			// is actually lower than this value, we don't have to take care
6698d156f1cSAxel Dörfler			// of this here.
6708d156f1cSAxel Dörfler
6718d156f1cSAxel Dörfler			uint32 sector = pos % fParameters.sectors_per_track + 1;
6728d156f1cSAxel Dörfler				// sectors start countint at 1 (unlike head and cylinder)
6738d156f1cSAxel Dörfler			uint32 head = pos / fParameters.sectors_per_track;
6748d156f1cSAxel Dörfler			uint32 cylinder = head / fParameters.heads;
6758d156f1cSAxel Dörfler			head %= fParameters.heads;
6768d156f1cSAxel Dörfler
67703a574d8SMichael Lotz			if (cylinder >= fParameters.cylinders) {
67803a574d8SMichael Lotz				TRACE(("cylinder value %lu bigger than available %lu\n",
67903a574d8SMichael Lotz					cylinder, fParameters.cylinders));
6808d156f1cSAxel Dörfler				return B_BAD_VALUE;
68103a574d8SMichael Lotz			}
6828d156f1cSAxel Dörfler
683597416d1SAxel Dörfler			// try to read from the device more than once, just to make sure it'll work
6848d156f1cSAxel Dörfler			struct bios_regs regs;
685597416d1SAxel Dörfler			int32 tries = 3;
68603a574d8SMichael Lotz			bool readWorked = false;
687597416d1SAxel Dörfler
688597416d1SAxel Dörfler			while (tries-- > 0) {
689597416d1SAxel Dörfler				regs.eax = BIOS_READ | blocksRead;
690597416d1SAxel Dörfler				regs.edx = fDriveID | (head << 8);
691597416d1SAxel Dörfler				regs.ecx = sector | ((cylinder >> 2) & 0xc0) | ((cylinder & 0xff) << 8);
692597416d1SAxel Dörfler				regs.es = 0;
693597416d1SAxel Dörfler				regs.ebx = kExtraSegmentScratch;
694597416d1SAxel Dörfler				call_bios(0x13, &regs);
695597416d1SAxel Dörfler
69603a574d8SMichael Lotz				if ((regs.flags & CARRY_FLAG) == 0) {
69703a574d8SMichael Lotz					readWorked = true;
698597416d1SAxel Dörfler					break;
69903a574d8SMichael Lotz				}
70003a574d8SMichael Lotz
70103a574d8SMichael Lotz				TRACE(("read failed\n"));
702597416d1SAxel Dörfler
703597416d1SAxel Dörfler				if (tries < 2) {
704597416d1SAxel Dörfler					// reset disk system
70503a574d8SMichael Lotz					TRACE(("reset disk system\n"));
706597416d1SAxel Dörfler					regs.eax = BIOS_RESET_DISK_SYSTEM;
707597416d1SAxel Dörfler					regs.edx = fDriveID;
708597416d1SAxel Dörfler					call_bios(0x13, &regs);
709597416d1SAxel Dörfler				}
71003a574d8SMichael Lotz
711597416d1SAxel Dörfler				// wait a bit between the retries (1/20 sec)
712597416d1SAxel Dörfler				spin(50000);
713597416d1SAxel Dörfler			}
7148d156f1cSAxel Dörfler
71503a574d8SMichael Lotz			if (!readWorked) {
716597416d1SAxel Dörfler				dprintf("reading %ld bytes from drive %u failed at %Ld\n",
717597416d1SAxel Dörfler					blocksRead, fDriveID, pos);
7188d156f1cSAxel Dörfler				return B_ERROR;
719597416d1SAxel Dörfler			}
720502a70f8SAxel Dörfler		}
721502a70f8SAxel Dörfler
7228d156f1cSAxel Dörfler		uint32 bytesRead = fBlockSize * blocksRead - offset;
723502a70f8SAxel Dörfler		// copy no more than bufferSize bytes
724502a70f8SAxel Dörfler		if (bytesRead > bufferSize)
725502a70f8SAxel Dörfler			bytesRead = bufferSize;
726502a70f8SAxel Dörfler
727502a70f8SAxel Dörfler		memcpy(buffer, (void *)(kExtraSegmentScratch + offset), bytesRead);
728502a70f8SAxel Dörfler		pos += blocksRead;
7298d156f1cSAxel Dörfler		offset = 0;
730502a70f8SAxel Dörfler		blocksLeft -= blocksRead;
731502a70f8SAxel Dörfler		bufferSize -= bytesRead;
732502a70f8SAxel Dörfler		buffer = (void *)((addr_t)buffer + bytesRead);
733502a70f8SAxel Dörfler		totalBytesRead += bytesRead;
734502a70f8SAxel Dörfler	}
735502a70f8SAxel Dörfler
736502a70f8SAxel Dörfler	return totalBytesRead;
737502a70f8SAxel Dörfler}
738502a70f8SAxel Dörfler
739502a70f8SAxel Dörfler
740d1367a37SIngo Weinholdssize_t
741d1367a37SIngo WeinholdBIOSDrive::WriteAt(void* cookie, off_t pos, const void* buffer,
742d1367a37SIngo Weinhold	size_t bufferSize)
743502a70f8SAxel Dörfler{
744d1367a37SIngo Weinhold	// we support only LBA addressing
745d1367a37SIngo Weinhold	if (!fLBA) {
746d1367a37SIngo Weinhold		dprintf("BIOSDrive::WriteAt(): CHS addressing not supported\n");
747d1367a37SIngo Weinhold		return B_UNSUPPORTED;
748d1367a37SIngo Weinhold	}
749d1367a37SIngo Weinhold
750d1367a37SIngo Weinhold	// we support only block-aligned writes
751d1367a37SIngo Weinhold	if (pos % fBlockSize != 0 || bufferSize % fBlockSize != 0) {
752d1367a37SIngo Weinhold		dprintf("BIOSDrive::WriteAt(pos: %" B_PRIdOFF ", size: %" B_PRIuSIZE
753d1367a37SIngo Weinhold			"): Block-unaligned write not supported.\n", pos, bufferSize);
754d1367a37SIngo Weinhold		return B_UNSUPPORTED;
755d1367a37SIngo Weinhold	}
756d1367a37SIngo Weinhold
757d1367a37SIngo Weinhold	pos /= fBlockSize;
758d1367a37SIngo Weinhold
759d1367a37SIngo Weinhold	uint32 blocksLeft = bufferSize / fBlockSize;
760d1367a37SIngo Weinhold	int32 totalBytesWritten = 0;
761d1367a37SIngo Weinhold
762d1367a37SIngo Weinhold	uint32 scratchSize = 24 * 1024 / fBlockSize;
763d1367a37SIngo Weinhold		// maximum value allowed by Phoenix BIOS is 0x7f
764d1367a37SIngo Weinhold
765d1367a37SIngo Weinhold	while (blocksLeft > 0) {
766d1367a37SIngo Weinhold		uint32 blocksToWrite = blocksLeft;
767d1367a37SIngo Weinhold		if (blocksToWrite > scratchSize)
768d1367a37SIngo Weinhold			blocksToWrite = scratchSize;
769d1367a37SIngo Weinhold
770d1367a37SIngo Weinhold		uint32 bytesToWrite = blocksToWrite * fBlockSize;
771d1367a37SIngo Weinhold
772d1367a37SIngo Weinhold		memcpy((void*)kExtraSegmentScratch, buffer, bytesToWrite);
773d1367a37SIngo Weinhold
774d1367a37SIngo Weinhold		struct disk_address_packet* packet
775d1367a37SIngo Weinhold			= (disk_address_packet*)kDataSegmentScratch;
776d1367a37SIngo Weinhold		memset(packet, 0, sizeof(disk_address_packet));
777d1367a37SIngo Weinhold
778d1367a37SIngo Weinhold		packet->size = sizeof(disk_address_packet);
779d1367a37SIngo Weinhold		packet->number_of_blocks = blocksToWrite;
780d1367a37SIngo Weinhold		packet->buffer = kExtraSegmentScratch;
781d1367a37SIngo Weinhold		packet->lba = pos;
782d1367a37SIngo Weinhold
783d1367a37SIngo Weinhold		struct bios_regs regs;
784d1367a37SIngo Weinhold		regs.eax = BIOS_EXT_WRITE;	// al = 0x00 -- no write verify
785d1367a37SIngo Weinhold		regs.edx = fDriveID;
786d1367a37SIngo Weinhold		regs.esi = (addr_t)packet - kDataSegmentBase;
787d1367a37SIngo Weinhold		call_bios(0x13, &regs);
788d1367a37SIngo Weinhold
789d1367a37SIngo Weinhold		if (regs.flags & CARRY_FLAG)
790d1367a37SIngo Weinhold			return B_ERROR;
791d1367a37SIngo Weinhold
792d1367a37SIngo Weinhold		pos += blocksToWrite;
793d1367a37SIngo Weinhold		blocksLeft -= blocksToWrite;
794d1367a37SIngo Weinhold		bufferSize -= bytesToWrite;
795d1367a37SIngo Weinhold		buffer = (void*)((addr_t)buffer + bytesToWrite);
796d1367a37SIngo Weinhold		totalBytesWritten += bytesToWrite;
797d1367a37SIngo Weinhold	}
798d1367a37SIngo Weinhold
799d1367a37SIngo Weinhold	return totalBytesWritten;
800502a70f8SAxel Dörfler}
801502a70f8SAxel Dörfler
802502a70f8SAxel Dörfler
803d1367a37SIngo Weinholdoff_t
804502a70f8SAxel DörflerBIOSDrive::Size() const
805502a70f8SAxel Dörfler{
806502a70f8SAxel Dörfler	return fSize;
807502a70f8SAxel Dörfler}
808502a70f8SAxel Dörfler
809502a70f8SAxel Dörfler
8101bcbbd6aSAxel Dörflerstatus_t
8111bcbbd6aSAxel DörflerBIOSDrive::FillIdentifier()
8121bcbbd6aSAxel Dörfler{
8131bcbbd6aSAxel Dörfler	if (HasParameters()) {
8141bcbbd6aSAxel Dörfler		// try all drive_parameters versions, beginning from the most informative
8151bcbbd6aSAxel Dörfler
816fabe8c62SAxel Dörfler#if 0
8171bcbbd6aSAxel Dörfler		if (fill_disk_identifier_v3(fIdentifier, fParameters) == B_OK)
8181bcbbd6aSAxel Dörfler			return B_OK;
8191bcbbd6aSAxel Dörfler
8201bcbbd6aSAxel Dörfler		if (fill_disk_identifier_v2(fIdentifier, fParameters) == B_OK)
8211bcbbd6aSAxel Dörfler			return B_OK;
822fabe8c62SAxel Dörfler#else
823fabe8c62SAxel Dörfler		// TODO: the above version is the correct one - it's currently
824fabe8c62SAxel Dörfler		//		disabled, as the kernel boot code only supports the
825fabe8c62SAxel Dörfler		//		UNKNOWN_BUS/UNKNOWN_DEVICE way to find the correct boot
826fabe8c62SAxel Dörfler		//		device.
827fabe8c62SAxel Dörfler		if (fill_disk_identifier_v3(fIdentifier, fParameters) != B_OK)
828fabe8c62SAxel Dörfler			fill_disk_identifier_v2(fIdentifier, fParameters);
829fabe8c62SAxel Dörfler
830fabe8c62SAxel Dörfler#endif
8311bcbbd6aSAxel Dörfler
8321bcbbd6aSAxel Dörfler		// no interesting information, we have to fall back to the default
8331bcbbd6aSAxel Dörfler		// unknown interface/device type identifier
8341bcbbd6aSAxel Dörfler	}
8351bcbbd6aSAxel Dörfler
8361bcbbd6aSAxel Dörfler	fIdentifier.bus_type = UNKNOWN_BUS;
8371bcbbd6aSAxel Dörfler	fIdentifier.device_type = UNKNOWN_DEVICE;
8381bcbbd6aSAxel Dörfler	fIdentifier.device.unknown.size = Size();
8391bcbbd6aSAxel Dörfler
8401bcbbd6aSAxel Dörfler	for (int32 i = 0; i < NUM_DISK_CHECK_SUMS; i++) {
8411bcbbd6aSAxel Dörfler		fIdentifier.device.unknown.check_sums[i].offset = -1;
8421bcbbd6aSAxel Dörfler		fIdentifier.device.unknown.check_sums[i].sum = 0;
8431bcbbd6aSAxel Dörfler	}
8441bcbbd6aSAxel Dörfler
8451bcbbd6aSAxel Dörfler	return B_ERROR;
8461bcbbd6aSAxel Dörfler}
8471bcbbd6aSAxel Dörfler
8481bcbbd6aSAxel Dörfler
849502a70f8SAxel Dörfler//	#pragma mark -
850621540d1SAxel Dörfler
851621540d1SAxel Dörfler
852d1367a37SIngo Weinholdstatus_t
853ecdaf9deSAxel Dörflerplatform_add_boot_device(struct stage2_args *args, NodeList *devicesList)
854621540d1SAxel Dörfler{
8558d156f1cSAxel Dörfler	TRACE(("boot drive ID: %x\n", gBootDriveID));
856502a70f8SAxel Dörfler
85782029bdaSMarcus Overhagen	BIOSDrive *drive = new(nothrow) BIOSDrive(gBootDriveID);
858f1915280SAxel Dörfler	if (drive->InitCheck() != B_OK) {
8595a7a832fSAxel Dörfler		dprintf("no boot drive!\n");
860cee04e80SArtur Wyszynski		delete drive;
861f1915280SAxel Dörfler		return B_ERROR;
862f1915280SAxel Dörfler	}
863f1915280SAxel Dörfler
864ecdaf9deSAxel Dörfler	devicesList->Add(drive);
865ecdaf9deSAxel Dörfler
8661bcbbd6aSAxel Dörfler	if (drive->FillIdentifier() != B_OK) {
867ecdaf9deSAxel Dörfler		// We need to add all block devices to give the kernel the possibility
868ecdaf9deSAxel Dörfler		// to find the right boot volume
869ecdaf9deSAxel Dörfler		add_block_devices(devicesList, true);
8701bcbbd6aSAxel Dörfler	}
8711bcbbd6aSAxel Dörfler
872ecdaf9deSAxel Dörfler	TRACE(("boot drive size: %Ld bytes\n", drive->Size()));
87393cb9538SAlex Smith	gBootVolume.SetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, gBootedFromImage);
874502a70f8SAxel Dörfler
875502a70f8SAxel Dörfler	return B_OK;
876621540d1SAxel Dörfler}
877621540d1SAxel Dörfler
878621540d1SAxel Dörfler
879621540d1SAxel Dörflerstatus_t
880e8882171SJessica Hamiltonplatform_get_boot_partitions(struct stage2_args *args, Node *bootDevice,
881e8882171SJessica Hamilton	NodeList *list, NodeList *bootList)
882621540d1SAxel Dörfler{
8834c03f90fSAxel Dörfler	BIOSDrive *drive = static_cast<BIOSDrive *>(bootDevice);
8844c03f90fSAxel Dörfler	off_t offset = (off_t)gBootPartitionOffset * drive->BlockSize();
8854c03f90fSAxel Dörfler
8865a7a832fSAxel Dörfler	dprintf("boot partition offset: %Ld\n", offset);
8870f87a64bSAxel Dörfler
888164d84bbSIngo Weinhold	NodeIterator iterator = list->GetIterator();
889502a70f8SAxel Dörfler	boot::Partition *partition = NULL;
890502a70f8SAxel Dörfler	while ((partition = (boot::Partition *)iterator.Next()) != NULL) {
8918d156f1cSAxel Dörfler		TRACE(("partition offset = %Ld, size = %Ld\n", partition->offset, partition->size));
8920f87a64bSAxel Dörfler		// search for the partition that contains the partition
8930f87a64bSAxel Dörfler		// offset as reported by the BFS boot block
8944c03f90fSAxel Dörfler		if (offset >= partition->offset
8954c03f90fSAxel Dörfler			&& offset < partition->offset + partition->size) {
896e8882171SJessica Hamilton			bootList->Insert(partition);
8970f87a64bSAxel Dörfler			return B_OK;
8980f87a64bSAxel Dörfler		}
899502a70f8SAxel Dörfler	}
900502a70f8SAxel Dörfler
901502a70f8SAxel Dörfler	return B_ENTRY_NOT_FOUND;
902621540d1SAxel Dörfler}
903621540d1SAxel Dörfler
904621540d1SAxel Dörfler
905621540d1SAxel Dörflerstatus_t
906bc0096beSAxel Dörflerplatform_add_block_devices(stage2_args *args, NodeList *devicesList)
907621540d1SAxel Dörfler{
908ecdaf9deSAxel Dörfler	return add_block_devices(devicesList, false);
909621540d1SAxel Dörfler}
910621540d1SAxel Dörfler
911f1915280SAxel Dörfler
912d1367a37SIngo Weinholdstatus_t
91317e0266fSAxel Dörflerplatform_register_boot_device(Node *device)
91417e0266fSAxel Dörfler{
91517e0266fSAxel Dörfler	BIOSDrive *drive = (BIOSDrive *)device;
91617e0266fSAxel Dörfler
917597416d1SAxel Dörfler	check_cd_boot(drive);
91817e0266fSAxel Dörfler
91993cb9538SAlex Smith	gBootVolume.SetInt64("boot drive number", drive->DriveID());
92093cb9538SAlex Smith	gBootVolume.SetData(BOOT_VOLUME_DISK_IDENTIFIER, B_RAW_TYPE,
9219e8dc2a9SIngo Weinhold		&drive->Identifier(), sizeof(disk_identifier));
92217e0266fSAxel Dörfler
92317e0266fSAxel Dörfler	return B_OK;
92417e0266fSAxel Dörfler}
92517e0266fSAxel Dörfler
926e1b41d44SAndreas Faerber
927e1b41d44SAndreas Faerbervoid
928e1b41d44SAndreas Faerberplatform_cleanup_devices()
929e1b41d44SAndreas Faerber{
930e1b41d44SAndreas Faerber}
931