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