1/*
2 * Copyright 2003-2006, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "bios.h"
8
9#include <KernelExport.h>
10#include <boot/platform.h>
11#include <boot/partitions.h>
12#include <boot/stdio.h>
13#include <boot/stage2.h>
14
15#include <string.h>
16
17//#define TRACE_DEVICES
18#ifdef TRACE_DEVICES
19#	define TRACE(x) dprintf x
20#else
21#	define TRACE(x) ;
22#endif
23
24
25// exported from shell.S
26extern uint8 gBootedFromImage;
27extern uint8 gBootDriveID;
28extern uint32 gBootPartitionOffset;
29
30// int 0x13 definitions
31#define BIOS_RESET_DISK_SYSTEM			0x0000
32#define BIOS_READ						0x0200
33#define BIOS_GET_DRIVE_PARAMETERS		0x0800
34#define BIOS_IS_EXT_PRESENT				0x4100
35#define BIOS_EXT_READ					0x4200
36#define BIOS_EXT_WRITE					0x4300
37#define BIOS_GET_EXT_DRIVE_PARAMETERS	0x4800
38#define BIOS_BOOT_CD_GET_STATUS			0x4b01
39
40struct real_addr {
41	uint16	offset;
42	uint16	segment;
43};
44
45struct disk_address_packet {
46	uint8		size;
47	uint8		reserved;
48	uint16		number_of_blocks;
49	uint32		buffer;
50	uint64		lba;
51	uint64		flat_buffer;
52};
53
54static const uint16 kParametersSizeVersion1 = 0x1a;
55static const uint16 kParametersSizeVersion2 = 0x1e;
56static const uint16 kParametersSizeVersion3 = 0x42;
57
58static const uint16 kDevicePathSignature = 0xbedd;
59
60struct drive_parameters {
61	uint16		parameters_size;
62	uint16		flags;
63	uint32		cylinders;
64	uint32		heads;
65	uint32		sectors_per_track;
66	uint64		sectors;
67	uint16		bytes_per_sector;
68	/* edd 2.0 */
69	real_addr	device_table;
70	/* edd 3.0 */
71	uint16		device_path_signature;
72	uint8		device_path_size;
73	uint8		reserved1[3];
74	char		host_bus[4];
75	char		interface_type[8];
76	union {
77		struct {
78			uint16	base_address;
79		} legacy;
80		struct {
81			uint8	bus;
82			uint8	slot;
83			uint8	function;
84		} pci;
85		uint8		reserved[8];
86	} interface;
87	union {
88		struct {
89			uint8	slave;
90		} ata;
91		struct {
92			uint8	slave;
93			uint8	logical_unit;
94		} atapi;
95		struct {
96			uint8	logical_unit;
97		} scsi;
98		struct {
99			uint8	tbd;
100		} usb;
101		struct {
102			uint64	guid;
103		} firewire;
104		struct {
105			uint64	wwd;
106		} fibre;
107	} device;
108	uint8		reserved2;
109	uint8		checksum;
110} _PACKED;
111
112struct device_table {
113	uint16	base_address;
114	uint16	control_port_address;
115	uint8	_reserved1 : 4;
116	uint8	is_slave : 1;
117	uint8	_reserved2 : 1;
118	uint8	lba_enabled : 1;
119} _PACKED;
120
121struct specification_packet {
122	uint8	size;
123	uint8	media_type;
124	uint8	drive_number;
125	uint8	controller_index;
126	uint32	start_emulation;
127	uint16	device_specification;
128	uint8	_more_[9];
129} _PACKED;
130
131class BIOSDrive : public Node {
132	public:
133		BIOSDrive(uint8 driveID);
134		virtual ~BIOSDrive();
135
136		status_t InitCheck() const;
137
138		virtual ssize_t ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize);
139		virtual ssize_t WriteAt(void *cookie, off_t pos, const void *buffer, size_t bufferSize);
140
141		virtual off_t Size() const;
142
143		uint32 BlockSize() const { return fBlockSize; }
144
145		status_t FillIdentifier();
146
147		bool HasParameters() const { return fHasParameters; }
148		const drive_parameters &Parameters() const { return fParameters; }
149
150		disk_identifier &Identifier() { return fIdentifier; }
151		uint8 DriveID() const { return fDriveID; }
152
153	protected:
154		uint8	fDriveID;
155		bool	fLBA;
156		uint64	fSize;
157		uint32	fBlockSize;
158		bool	fHasParameters;
159		drive_parameters fParameters;
160		disk_identifier fIdentifier;
161};
162
163
164static bool sBlockDevicesAdded = false;
165
166
167static void
168check_cd_boot(BIOSDrive *drive)
169{
170	gBootVolume.SetInt32(BOOT_METHOD, BOOT_METHOD_HARD_DISK);
171
172	if (drive->DriveID() != 0)
173		return;
174
175	struct bios_regs regs;
176	regs.eax = BIOS_BOOT_CD_GET_STATUS;
177	regs.edx = 0;
178	regs.esi = kDataSegmentScratch;
179	call_bios(0x13, &regs);
180
181	if ((regs.flags & CARRY_FLAG) != 0)
182		return;
183
184	// we obviously were booted from CD!
185
186	specification_packet *packet = (specification_packet *)kDataSegmentScratch;
187	if (packet->media_type != 0)
188		gBootVolume.SetInt32(BOOT_METHOD, BOOT_METHOD_CD);
189
190#if 0
191	dprintf("got CD boot spec:\n");
192	dprintf("  size: %#x\n", packet->size);
193	dprintf("  media type: %u\n", packet->media_type);
194	dprintf("  drive_number: %u\n", packet->drive_number);
195	dprintf("  controller index: %u\n", packet->controller_index);
196	dprintf("  start emulation: %lu\n", packet->start_emulation);
197	dprintf("  device_specification: %u\n", packet->device_specification);
198#endif
199}
200
201
202static bool
203are_extensions_available(uint8 drive)
204{
205	struct bios_regs regs;
206	regs.eax = BIOS_IS_EXT_PRESENT;
207	regs.ebx = 0x55aa;
208	regs.edx = drive;
209	call_bios(0x13, &regs);
210
211	TRACE(("checking extensions: carry: %u; ebx: 0x%08lx; ecx: 0x%08lx\n",
212		regs.flags & CARRY_FLAG, regs.ebx, regs.ecx));
213	return (regs.flags & CARRY_FLAG) == 0 && regs.ebx == 0xaa55
214		&& (regs.ecx & 0x01 /* supports device access using packet */) != 0;
215}
216
217
218static status_t
219get_ext_drive_parameters(uint8 drive, drive_parameters *targetParameters)
220{
221	drive_parameters *parameter = (drive_parameters *)kDataSegmentScratch;
222
223	memset(parameter, 0, sizeof(drive_parameters));
224	parameter->parameters_size = sizeof(drive_parameters);
225
226	struct bios_regs regs;
227	regs.eax = BIOS_GET_EXT_DRIVE_PARAMETERS;
228	regs.edx = drive;
229	regs.esi = (addr_t)parameter - kDataSegmentBase;
230	call_bios(0x13, &regs);
231
232	// filter out faulty BIOS return codes
233	if ((regs.flags & CARRY_FLAG) != 0
234		|| parameter->sectors == 0)
235		return B_ERROR;
236
237	memcpy(targetParameters, parameter, sizeof(drive_parameters));
238	return B_OK;
239}
240
241
242static status_t
243get_drive_parameters(uint8 drive, drive_parameters *parameters)
244{
245	struct bios_regs regs;
246	regs.eax = BIOS_GET_DRIVE_PARAMETERS;
247	regs.edx = drive;
248	regs.es = 0;
249	regs.edi = 0;	// guard against faulty BIOS, see Ralf Brown's interrupt list
250	call_bios(0x13, &regs);
251
252	if ((regs.flags & CARRY_FLAG) != 0 || (regs.ecx & 0x3f) == 0)
253		return B_ERROR;
254
255	// fill drive_parameters structure with useful values
256	parameters->parameters_size = kParametersSizeVersion1;
257	parameters->flags = 0;
258	parameters->cylinders = (((regs.ecx & 0xc0) << 2) | ((regs.ecx >> 8) & 0xff)) + 1;
259	parameters->heads = ((regs.edx >> 8) & 0xff) + 1;
260		// heads and cylinders start counting from 0
261	parameters->sectors_per_track = regs.ecx & 0x3f;
262	parameters->sectors = parameters->cylinders * parameters->heads
263		* parameters->sectors_per_track;
264	parameters->bytes_per_sector = 512;
265
266	return B_OK;
267}
268
269
270static status_t
271get_number_of_drives(uint8 *_count)
272{
273	struct bios_regs regs;
274	regs.eax = BIOS_GET_DRIVE_PARAMETERS;
275	regs.edx = 0x80;
276	regs.es = 0;
277	regs.edi = 0;
278	call_bios(0x13, &regs);
279
280	if (regs.flags & CARRY_FLAG)
281		return B_ERROR;
282
283	*_count = regs.edx & 0xff;
284	return B_OK;
285}
286
287
288/** parse EDD 3.0 drive path information */
289
290static status_t
291fill_disk_identifier_v3(disk_identifier &disk, const drive_parameters &parameters)
292{
293	if (parameters.parameters_size < kParametersSizeVersion3
294		|| parameters.device_path_signature != kDevicePathSignature)
295		return B_BAD_TYPE;
296
297	// parse host bus
298
299	if (!strncmp(parameters.host_bus, "PCI", 3)) {
300		disk.bus_type = PCI_BUS;
301
302		disk.bus.pci.bus = parameters.interface.pci.bus;
303		disk.bus.pci.slot = parameters.interface.pci.slot;
304		disk.bus.pci.function = parameters.interface.pci.function;
305	} else if (!strncmp(parameters.host_bus, "ISA", 3)) {
306		disk.bus_type = LEGACY_BUS;
307
308		disk.bus.legacy.base_address = parameters.interface.legacy.base_address;
309		dprintf("legacy base address %x\n", disk.bus.legacy.base_address);
310	} else {
311		dprintf("unknown host bus \"%s\"\n", parameters.host_bus);
312		return B_BAD_DATA;
313	}
314
315	// parse interface
316
317	if (!strncmp(parameters.interface_type, "ATA", 3)) {
318		disk.device_type = ATA_DEVICE;
319		disk.device.ata.master = !parameters.device.ata.slave;
320		dprintf("ATA device, %s\n", disk.device.ata.master ? "master" : "slave");
321	} else if (!strncmp(parameters.interface_type, "ATAPI", 3)) {
322		disk.device_type = ATAPI_DEVICE;
323		disk.device.atapi.master = !parameters.device.ata.slave;
324		disk.device.atapi.logical_unit = parameters.device.atapi.logical_unit;
325	} else if (!strncmp(parameters.interface_type, "SCSI", 3)) {
326		disk.device_type = SCSI_DEVICE;
327		disk.device.scsi.logical_unit = parameters.device.scsi.logical_unit;
328	} else if (!strncmp(parameters.interface_type, "USB", 3)) {
329		disk.device_type = USB_DEVICE;
330		disk.device.usb.tbd = parameters.device.usb.tbd;
331	} else if (!strncmp(parameters.interface_type, "1394", 3)) {
332		disk.device_type = FIREWIRE_DEVICE;
333		disk.device.firewire.guid = parameters.device.firewire.guid;
334	} else if (!strncmp(parameters.interface_type, "FIBRE", 3)) {
335		disk.device_type = FIBRE_DEVICE;
336		disk.device.fibre.wwd = parameters.device.fibre.wwd;
337	} else {
338		dprintf("unknown interface type \"%s\"\n", parameters.interface_type);
339		return B_BAD_DATA;
340	}
341
342	return B_OK;
343}
344
345
346/** EDD 2.0 drive table information */
347
348static status_t
349fill_disk_identifier_v2(disk_identifier &disk, const drive_parameters &parameters)
350{
351	if (parameters.device_table.segment == 0xffff
352		&& parameters.device_table.offset == 0xffff)
353		return B_BAD_TYPE;
354
355	device_table *table = (device_table *)LINEAR_ADDRESS(parameters.device_table.segment,
356		parameters.device_table.offset);
357
358	disk.bus_type = LEGACY_BUS;
359	disk.bus.legacy.base_address = table->base_address;
360
361	disk.device_type = ATA_DEVICE;
362	disk.device.ata.master = !table->is_slave;
363
364	return B_OK;
365}
366
367
368static off_t
369get_next_check_sum_offset(int32 index, off_t maxSize)
370{
371	// The boot block often contains the disk superblock, and should be
372	// unique enough for most cases
373	if (index < 2)
374		return index * 512;
375
376	// Try some data in the first part of the drive
377	if (index < 4)
378		return (maxSize >> 10) + index * 2048;
379
380	// Some random value might do
381	return ((system_time() + index) % (maxSize >> 9)) * 512;
382}
383
384
385/**	Computes a check sum for the specified block.
386 *	The check sum is the sum of all data in that block interpreted as an
387 *	array of uint32 values.
388 *	Note, this must use the same method as the one used in kernel/fs/vfs_boot.cpp.
389 */
390
391static uint32
392compute_check_sum(BIOSDrive *drive, off_t offset)
393{
394	char buffer[512];
395	ssize_t bytesRead = drive->ReadAt(NULL, offset, buffer, sizeof(buffer));
396	if (bytesRead < B_OK)
397		return 0;
398
399	if (bytesRead < (ssize_t)sizeof(buffer))
400		memset(buffer + bytesRead, 0, sizeof(buffer) - bytesRead);
401
402	uint32 *array = (uint32 *)buffer;
403	uint32 sum = 0;
404
405	for (uint32 i = 0; i < (bytesRead + sizeof(uint32) - 1) / sizeof(uint32); i++) {
406		sum += array[i];
407	}
408
409	return sum;
410}
411
412
413static void
414find_unique_check_sums(NodeList *devices)
415{
416	NodeIterator iterator = devices->GetIterator();
417	Node *device;
418	int32 index = 0;
419	off_t minSize = 0;
420	const int32 kMaxTries = 200;
421
422	while (index < kMaxTries) {
423		bool clash = false;
424
425		iterator.Rewind();
426
427		while ((device = iterator.Next()) != NULL) {
428			BIOSDrive *drive = (BIOSDrive *)device;
429#if 0
430			// there is no RTTI in the boot loader...
431			BIOSDrive *drive = dynamic_cast<BIOSDrive *>(device);
432			if (drive == NULL)
433				continue;
434#endif
435
436			// TODO: currently, we assume that the BIOS provided us with unique
437			//	disk identifiers... hopefully this is a good idea
438			if (drive->Identifier().device_type != UNKNOWN_DEVICE)
439				continue;
440
441			if (minSize == 0 || drive->Size() < minSize)
442				minSize = drive->Size();
443
444			// check for clashes
445
446			NodeIterator compareIterator = devices->GetIterator();
447			while ((device = compareIterator.Next()) != NULL) {
448				BIOSDrive *compareDrive = (BIOSDrive *)device;
449
450				if (compareDrive == drive
451					|| compareDrive->Identifier().device_type != UNKNOWN_DEVICE)
452					continue;
453
454				if (!memcmp(&drive->Identifier(), &compareDrive->Identifier(),
455						sizeof(disk_identifier))) {
456					clash = true;
457					break;
458				}
459			}
460
461			if (clash)
462				break;
463		}
464
465		if (!clash) {
466			// our work here is done.
467			return;
468		}
469
470		// add a new block to the check sums
471
472		off_t offset = get_next_check_sum_offset(index, minSize);
473		int32 i = index % NUM_DISK_CHECK_SUMS;
474		iterator.Rewind();
475
476		while ((device = iterator.Next()) != NULL) {
477			BIOSDrive *drive = (BIOSDrive *)device;
478
479			disk_identifier& disk = drive->Identifier();
480			disk.device.unknown.check_sums[i].offset = offset;
481			disk.device.unknown.check_sums[i].sum = compute_check_sum(drive, offset);
482
483			TRACE(("disk %x, offset %Ld, sum %lu\n", drive->DriveID(), offset,
484				disk.device.unknown.check_sums[i].sum));
485		}
486
487		index++;
488	}
489
490	// If we get here, we couldn't find a way to differentiate all disks from each other.
491	// It's very likely that one disk is an exact copy of the other, so there is nothing
492	// we could do, anyway.
493
494	dprintf("Could not make BIOS drives unique! Might boot from the wrong disk...\n");
495}
496
497
498static status_t
499add_block_devices(NodeList *devicesList, bool identifierMissing)
500{
501	if (sBlockDevicesAdded)
502		return B_OK;
503
504	uint8 driveCount;
505	if (get_number_of_drives(&driveCount) != B_OK)
506		return B_ERROR;
507
508	dprintf("number of drives: %d\n", driveCount);
509
510	for (int32 i = 0; i < driveCount; i++) {
511		uint8 driveID = i + 0x80;
512		if (driveID == gBootDriveID)
513			continue;
514
515		BIOSDrive *drive = new(nothrow) BIOSDrive(driveID);
516		if (drive->InitCheck() != B_OK) {
517			dprintf("could not add drive %u\n", driveID);
518			delete drive;
519			continue;
520		}
521
522		devicesList->Add(drive);
523
524		if (drive->FillIdentifier() != B_OK)
525			identifierMissing = true;
526	}
527
528	if (identifierMissing) {
529		// we cannot distinguish between all drives by identifier, we need
530		// compute checksums for them
531		find_unique_check_sums(devicesList);
532	}
533
534	sBlockDevicesAdded = true;
535	return B_OK;
536}
537
538
539//	#pragma mark -
540
541
542BIOSDrive::BIOSDrive(uint8 driveID)
543	:
544	fDriveID(driveID),
545	fSize(0)
546{
547	TRACE(("drive ID %u\n", driveID));
548
549	if (driveID < 0x80 || !are_extensions_available(driveID)
550		|| get_ext_drive_parameters(driveID, &fParameters) != B_OK) {
551		// old style CHS support
552
553		if (get_drive_parameters(driveID, &fParameters) != B_OK) {
554			dprintf("getting drive parameters for: %u failed!\n", fDriveID);
555			return;
556		}
557
558		TRACE(("  cylinders: %lu, heads: %lu, sectors: %lu, bytes_per_sector: %u\n",
559			fParameters.cylinders, fParameters.heads, fParameters.sectors_per_track,
560			fParameters.bytes_per_sector));
561		TRACE(("  total sectors: %Ld\n", fParameters.sectors));
562
563		fBlockSize = 512;
564		fSize = fParameters.sectors * fBlockSize;
565		fLBA = false;
566		fHasParameters = false;
567	} else {
568		TRACE(("size: %x\n", fParameters.parameters_size));
569		TRACE(("drive_path_signature: %x\n", fParameters.device_path_signature));
570		TRACE(("host bus: \"%s\", interface: \"%s\"\n", fParameters.host_bus,
571			fParameters.interface_type));
572		TRACE(("cylinders: %lu, heads: %lu, sectors: %lu, bytes_per_sector: %u\n",
573			fParameters.cylinders, fParameters.heads, fParameters.sectors_per_track,
574			fParameters.bytes_per_sector));
575		TRACE(("total sectors: %Ld\n", fParameters.sectors));
576
577		fBlockSize = fParameters.bytes_per_sector;
578		fSize = fParameters.sectors * fBlockSize;
579		fLBA = true;
580		fHasParameters = true;
581	}
582}
583
584
585BIOSDrive::~BIOSDrive()
586{
587}
588
589
590status_t
591BIOSDrive::InitCheck() const
592{
593	return fSize > 0 ? B_OK : B_ERROR;
594}
595
596
597ssize_t
598BIOSDrive::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize)
599{
600	uint32 offset = pos % fBlockSize;
601	pos /= fBlockSize;
602
603	uint32 blocksLeft = (bufferSize + offset + fBlockSize - 1) / fBlockSize;
604	int32 totalBytesRead = 0;
605
606	//TRACE(("BIOS reads %lu bytes from %Ld (offset = %lu), drive %u\n",
607	//	blocksLeft * fBlockSize, pos * fBlockSize, offset, fDriveID));
608
609	uint32 scratchSize = 24 * 1024 / fBlockSize;
610		// maximum value allowed by Phoenix BIOS is 0x7f
611
612	while (blocksLeft > 0) {
613		uint32 blocksRead = blocksLeft;
614		if (blocksRead > scratchSize)
615			blocksRead = scratchSize;
616
617		if (fLBA) {
618			struct disk_address_packet *packet = (disk_address_packet *)kDataSegmentScratch;
619			memset(packet, 0, sizeof(disk_address_packet));
620
621			packet->size = sizeof(disk_address_packet);
622			packet->number_of_blocks = blocksRead;
623			packet->buffer = kExtraSegmentScratch;
624			packet->lba = pos;
625
626			struct bios_regs regs;
627			regs.eax = BIOS_EXT_READ;
628			regs.edx = fDriveID;
629			regs.esi = (addr_t)packet - kDataSegmentBase;
630			call_bios(0x13, &regs);
631
632			if (regs.flags & CARRY_FLAG)
633				goto chs_read;
634		} else {
635	chs_read:
636			// Old style CHS read routine
637
638			// We can only read up to 64 kB this way, but since scratchSize
639			// is actually lower than this value, we don't have to take care
640			// of this here.
641
642			uint32 sector = pos % fParameters.sectors_per_track + 1;
643				// sectors start countint at 1 (unlike head and cylinder)
644			uint32 head = pos / fParameters.sectors_per_track;
645			uint32 cylinder = head / fParameters.heads;
646			head %= fParameters.heads;
647
648			if (cylinder >= fParameters.cylinders) {
649				TRACE(("cylinder value %lu bigger than available %lu\n",
650					cylinder, fParameters.cylinders));
651				return B_BAD_VALUE;
652			}
653
654			// try to read from the device more than once, just to make sure it'll work
655			struct bios_regs regs;
656			int32 tries = 3;
657			bool readWorked = false;
658
659			while (tries-- > 0) {
660				regs.eax = BIOS_READ | blocksRead;
661				regs.edx = fDriveID | (head << 8);
662				regs.ecx = sector | ((cylinder >> 2) & 0xc0) | ((cylinder & 0xff) << 8);
663				regs.es = 0;
664				regs.ebx = kExtraSegmentScratch;
665				call_bios(0x13, &regs);
666
667				if ((regs.flags & CARRY_FLAG) == 0) {
668					readWorked = true;
669					break;
670				}
671
672				TRACE(("read failed\n"));
673
674				if (tries < 2) {
675					// reset disk system
676					TRACE(("reset disk system\n"));
677					regs.eax = BIOS_RESET_DISK_SYSTEM;
678					regs.edx = fDriveID;
679					call_bios(0x13, &regs);
680				}
681
682				// wait a bit between the retries (1/20 sec)
683				spin(50000);
684			}
685
686			if (!readWorked) {
687				dprintf("reading %ld bytes from drive %u failed at %Ld\n",
688					blocksRead, fDriveID, pos);
689				return B_ERROR;
690			}
691		}
692
693		uint32 bytesRead = fBlockSize * blocksRead - offset;
694		// copy no more than bufferSize bytes
695		if (bytesRead > bufferSize)
696			bytesRead = bufferSize;
697
698		memcpy(buffer, (void *)(kExtraSegmentScratch + offset), bytesRead);
699		pos += blocksRead;
700		offset = 0;
701		blocksLeft -= blocksRead;
702		bufferSize -= bytesRead;
703		buffer = (void *)((addr_t)buffer + bytesRead);
704		totalBytesRead += bytesRead;
705	}
706
707	return totalBytesRead;
708}
709
710
711ssize_t
712BIOSDrive::WriteAt(void* cookie, off_t pos, const void* buffer,
713	size_t bufferSize)
714{
715	// we support only LBA addressing
716	if (!fLBA) {
717		dprintf("BIOSDrive::WriteAt(): CHS addressing not supported\n");
718		return B_UNSUPPORTED;
719	}
720
721	// we support only block-aligned writes
722	if (pos % fBlockSize != 0 || bufferSize % fBlockSize != 0) {
723		dprintf("BIOSDrive::WriteAt(pos: %" B_PRIdOFF ", size: %" B_PRIuSIZE
724			"): Block-unaligned write not supported.\n", pos, bufferSize);
725		return B_UNSUPPORTED;
726	}
727
728	pos /= fBlockSize;
729
730	uint32 blocksLeft = bufferSize / fBlockSize;
731	int32 totalBytesWritten = 0;
732
733	uint32 scratchSize = 24 * 1024 / fBlockSize;
734		// maximum value allowed by Phoenix BIOS is 0x7f
735
736	while (blocksLeft > 0) {
737		uint32 blocksToWrite = blocksLeft;
738		if (blocksToWrite > scratchSize)
739			blocksToWrite = scratchSize;
740
741		uint32 bytesToWrite = blocksToWrite * fBlockSize;
742
743		memcpy((void*)kExtraSegmentScratch, buffer, bytesToWrite);
744
745		struct disk_address_packet* packet
746			= (disk_address_packet*)kDataSegmentScratch;
747		memset(packet, 0, sizeof(disk_address_packet));
748
749		packet->size = sizeof(disk_address_packet);
750		packet->number_of_blocks = blocksToWrite;
751		packet->buffer = kExtraSegmentScratch;
752		packet->lba = pos;
753
754		struct bios_regs regs;
755		regs.eax = BIOS_EXT_WRITE;	// al = 0x00 -- no write verify
756		regs.edx = fDriveID;
757		regs.esi = (addr_t)packet - kDataSegmentBase;
758		call_bios(0x13, &regs);
759
760		if (regs.flags & CARRY_FLAG)
761			return B_ERROR;
762
763		pos += blocksToWrite;
764		blocksLeft -= blocksToWrite;
765		bufferSize -= bytesToWrite;
766		buffer = (void*)((addr_t)buffer + bytesToWrite);
767		totalBytesWritten += bytesToWrite;
768	}
769
770	return totalBytesWritten;
771}
772
773
774off_t
775BIOSDrive::Size() const
776{
777	return fSize;
778}
779
780
781status_t
782BIOSDrive::FillIdentifier()
783{
784	if (HasParameters()) {
785		// try all drive_parameters versions, beginning from the most informative
786
787#if 0
788		if (fill_disk_identifier_v3(fIdentifier, fParameters) == B_OK)
789			return B_OK;
790
791		if (fill_disk_identifier_v2(fIdentifier, fParameters) == B_OK)
792			return B_OK;
793#else
794		// TODO: the above version is the correct one - it's currently
795		//		disabled, as the kernel boot code only supports the
796		//		UNKNOWN_BUS/UNKNOWN_DEVICE way to find the correct boot
797		//		device.
798		if (fill_disk_identifier_v3(fIdentifier, fParameters) != B_OK)
799			fill_disk_identifier_v2(fIdentifier, fParameters);
800
801#endif
802
803		// no interesting information, we have to fall back to the default
804		// unknown interface/device type identifier
805	}
806
807	fIdentifier.bus_type = UNKNOWN_BUS;
808	fIdentifier.device_type = UNKNOWN_DEVICE;
809	fIdentifier.device.unknown.size = Size();
810
811	for (int32 i = 0; i < NUM_DISK_CHECK_SUMS; i++) {
812		fIdentifier.device.unknown.check_sums[i].offset = -1;
813		fIdentifier.device.unknown.check_sums[i].sum = 0;
814	}
815
816	return B_ERROR;
817}
818
819
820//	#pragma mark -
821
822
823status_t
824platform_add_boot_device(struct stage2_args *args, NodeList *devicesList)
825{
826	TRACE(("boot drive ID: %x\n", gBootDriveID));
827
828	BIOSDrive *drive = new(nothrow) BIOSDrive(gBootDriveID);
829	if (drive->InitCheck() != B_OK) {
830		dprintf("no boot drive!\n");
831		delete drive;
832		return B_ERROR;
833	}
834
835	devicesList->Add(drive);
836
837	if (drive->FillIdentifier() != B_OK) {
838		// We need to add all block devices to give the kernel the possibility
839		// to find the right boot volume
840		add_block_devices(devicesList, true);
841	}
842
843	TRACE(("boot drive size: %Ld bytes\n", drive->Size()));
844	gBootVolume.SetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, gBootedFromImage);
845
846	return B_OK;
847}
848
849
850status_t
851platform_get_boot_partition(struct stage2_args *args, Node *bootDevice,
852	NodeList *list, boot::Partition **_partition)
853{
854	BIOSDrive *drive = static_cast<BIOSDrive *>(bootDevice);
855	off_t offset = (off_t)gBootPartitionOffset * drive->BlockSize();
856
857	dprintf("boot partition offset: %Ld\n", offset);
858
859	NodeIterator iterator = list->GetIterator();
860	boot::Partition *partition = NULL;
861	while ((partition = (boot::Partition *)iterator.Next()) != NULL) {
862		TRACE(("partition offset = %Ld, size = %Ld\n", partition->offset, partition->size));
863		// search for the partition that contains the partition
864		// offset as reported by the BFS boot block
865		if (offset >= partition->offset
866			&& offset < partition->offset + partition->size) {
867			*_partition = partition;
868			return B_OK;
869		}
870	}
871
872	return B_ENTRY_NOT_FOUND;
873}
874
875
876status_t
877platform_add_block_devices(stage2_args *args, NodeList *devicesList)
878{
879	return add_block_devices(devicesList, false);
880}
881
882
883status_t
884platform_register_boot_device(Node *device)
885{
886	BIOSDrive *drive = (BIOSDrive *)device;
887
888	check_cd_boot(drive);
889
890	gBootVolume.SetInt64("boot drive number", drive->DriveID());
891	gBootVolume.SetData(BOOT_VOLUME_DISK_IDENTIFIER, B_RAW_TYPE,
892		&drive->Identifier(), sizeof(disk_identifier));
893
894	return B_OK;
895}
896
897