xref: /haiku/src/system/boot/platform/bios_ia32/devices.cpp (revision 5ac9b506412b11afb993bb52d161efe7666958a5)
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 	uint64		flat_buffer;
52 };
53 
54 static const uint16 kParametersSizeVersion1 = 0x1a;
55 static const uint16 kParametersSizeVersion2 = 0x1e;
56 static const uint16 kParametersSizeVersion3 = 0x42;
57 
58 static const uint16 kDevicePathSignature = 0xbedd;
59 
60 struct 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 
112 struct 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 
121 struct 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 
131 class 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 
164 static bool sBlockDevicesAdded = false;
165 
166 
167 static void
168 check_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 
202 static bool
203 are_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 
218 static status_t
219 get_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 
242 static status_t
243 get_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 
270 static status_t
271 get_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 
290 static status_t
291 fill_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 
348 static status_t
349 fill_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 
368 static off_t
369 get_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 
391 static uint32
392 compute_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 /**	Checks if the specified drive is usable for reading.
413  */
414 
415 static bool
416 is_drive_readable(BIOSDrive *drive)
417 {
418 	char buffer;
419 	return drive->ReadAt(NULL, 0, &buffer, sizeof(buffer)) > 0;
420 }
421 
422 
423 static void
424 find_unique_check_sums(NodeList *devices)
425 {
426 	NodeIterator iterator = devices->GetIterator();
427 	Node *device;
428 	int32 index = 0;
429 	off_t minSize = 0;
430 	const int32 kMaxTries = 200;
431 
432 	while (index < kMaxTries) {
433 		bool clash = false;
434 
435 		iterator.Rewind();
436 
437 		while ((device = iterator.Next()) != NULL) {
438 			BIOSDrive *drive = (BIOSDrive *)device;
439 #if 0
440 			// there is no RTTI in the boot loader...
441 			BIOSDrive *drive = dynamic_cast<BIOSDrive *>(device);
442 			if (drive == NULL)
443 				continue;
444 #endif
445 
446 			// TODO: currently, we assume that the BIOS provided us with unique
447 			//	disk identifiers... hopefully this is a good idea
448 			if (drive->Identifier().device_type != UNKNOWN_DEVICE)
449 				continue;
450 
451 			if (minSize == 0 || drive->Size() < minSize)
452 				minSize = drive->Size();
453 
454 			// check for clashes
455 
456 			NodeIterator compareIterator = devices->GetIterator();
457 			while ((device = compareIterator.Next()) != NULL) {
458 				BIOSDrive *compareDrive = (BIOSDrive *)device;
459 
460 				if (compareDrive == drive
461 					|| compareDrive->Identifier().device_type != UNKNOWN_DEVICE)
462 					continue;
463 
464 // TODO: Until we can actually get and compare *all* fields of the disk
465 // identifier in the kernel, we cannot compare the whole structure (we also
466 // should be more careful zeroing the structure before we fill it).
467 #if 0
468 				if (!memcmp(&drive->Identifier(), &compareDrive->Identifier(),
469 						sizeof(disk_identifier))) {
470 					clash = true;
471 					break;
472 				}
473 #else
474 				const disk_identifier& ourId = drive->Identifier();
475 				const disk_identifier& otherId = compareDrive->Identifier();
476 				if (memcmp(&ourId.device.unknown.check_sums,
477 						&otherId.device.unknown.check_sums,
478 						sizeof(ourId.device.unknown.check_sums)) == 0) {
479 					clash = true;
480 				}
481 #endif
482 			}
483 
484 			if (clash)
485 				break;
486 		}
487 
488 		if (!clash) {
489 			// our work here is done.
490 			return;
491 		}
492 
493 		// add a new block to the check sums
494 
495 		off_t offset = get_next_check_sum_offset(index, minSize);
496 		int32 i = index % NUM_DISK_CHECK_SUMS;
497 		iterator.Rewind();
498 
499 		while ((device = iterator.Next()) != NULL) {
500 			BIOSDrive *drive = (BIOSDrive *)device;
501 
502 			disk_identifier& disk = drive->Identifier();
503 			disk.device.unknown.check_sums[i].offset = offset;
504 			disk.device.unknown.check_sums[i].sum = compute_check_sum(drive, offset);
505 
506 			TRACE(("disk %x, offset %Ld, sum %lu\n", drive->DriveID(), offset,
507 				disk.device.unknown.check_sums[i].sum));
508 		}
509 
510 		index++;
511 	}
512 
513 	// If we get here, we couldn't find a way to differentiate all disks from each other.
514 	// It's very likely that one disk is an exact copy of the other, so there is nothing
515 	// we could do, anyway.
516 
517 	dprintf("Could not make BIOS drives unique! Might boot from the wrong disk...\n");
518 }
519 
520 
521 static status_t
522 add_block_devices(NodeList *devicesList, bool identifierMissing)
523 {
524 	if (sBlockDevicesAdded)
525 		return B_OK;
526 
527 	uint8 driveCount;
528 	if (get_number_of_drives(&driveCount) != B_OK)
529 		return B_ERROR;
530 
531 	dprintf("number of drives: %d\n", driveCount);
532 
533 	for (int32 i = 0; i < driveCount; i++) {
534 		uint8 driveID = i + 0x80;
535 		if (driveID == gBootDriveID)
536 			continue;
537 
538 		BIOSDrive *drive = new(nothrow) BIOSDrive(driveID);
539 		if (drive->InitCheck() != B_OK) {
540 			dprintf("could not add drive %u\n", driveID);
541 			delete drive;
542 			continue;
543 		}
544 
545 		// Only add usable drives
546 		if (is_drive_readable(drive))
547 			devicesList->Add(drive);
548 		else {
549 			dprintf("could not read from drive %" B_PRIu8 ", not adding\n", driveID);
550 			delete drive;
551 			continue;
552 		}
553 
554 		if (drive->FillIdentifier() != B_OK)
555 			identifierMissing = true;
556 	}
557 
558 	if (identifierMissing) {
559 		// we cannot distinguish between all drives by identifier, we need
560 		// compute checksums for them
561 		find_unique_check_sums(devicesList);
562 	}
563 
564 	sBlockDevicesAdded = true;
565 	return B_OK;
566 }
567 
568 
569 //	#pragma mark -
570 
571 
572 BIOSDrive::BIOSDrive(uint8 driveID)
573 	:
574 	fDriveID(driveID),
575 	fSize(0)
576 {
577 	TRACE(("drive ID %u\n", driveID));
578 
579 	if (driveID < 0x80 || !are_extensions_available(driveID)
580 		|| get_ext_drive_parameters(driveID, &fParameters) != B_OK) {
581 		// old style CHS support
582 
583 		if (get_drive_parameters(driveID, &fParameters) != B_OK) {
584 			dprintf("getting drive parameters for: %u failed!\n", fDriveID);
585 			return;
586 		}
587 
588 		TRACE(("  cylinders: %lu, heads: %lu, sectors: %lu, bytes_per_sector: %u\n",
589 			fParameters.cylinders, fParameters.heads, fParameters.sectors_per_track,
590 			fParameters.bytes_per_sector));
591 		TRACE(("  total sectors: %Ld\n", fParameters.sectors));
592 
593 		fBlockSize = 512;
594 		fSize = fParameters.sectors * fBlockSize;
595 		fLBA = false;
596 		fHasParameters = false;
597 	} else {
598 		TRACE(("size: %x\n", fParameters.parameters_size));
599 		TRACE(("drive_path_signature: %x\n", fParameters.device_path_signature));
600 		TRACE(("host bus: \"%s\", interface: \"%s\"\n", fParameters.host_bus,
601 			fParameters.interface_type));
602 		TRACE(("cylinders: %lu, heads: %lu, sectors: %lu, bytes_per_sector: %u\n",
603 			fParameters.cylinders, fParameters.heads, fParameters.sectors_per_track,
604 			fParameters.bytes_per_sector));
605 		TRACE(("total sectors: %Ld\n", fParameters.sectors));
606 
607 		fBlockSize = fParameters.bytes_per_sector;
608 		fSize = fParameters.sectors * fBlockSize;
609 		fLBA = true;
610 		fHasParameters = true;
611 	}
612 }
613 
614 
615 BIOSDrive::~BIOSDrive()
616 {
617 }
618 
619 
620 status_t
621 BIOSDrive::InitCheck() const
622 {
623 	return fSize > 0 ? B_OK : B_ERROR;
624 }
625 
626 
627 ssize_t
628 BIOSDrive::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize)
629 {
630 	uint32 offset = pos % fBlockSize;
631 	pos /= fBlockSize;
632 
633 	uint32 blocksLeft = (bufferSize + offset + fBlockSize - 1) / fBlockSize;
634 	int32 totalBytesRead = 0;
635 
636 	//TRACE(("BIOS reads %lu bytes from %Ld (offset = %lu), drive %u\n",
637 	//	blocksLeft * fBlockSize, pos * fBlockSize, offset, fDriveID));
638 
639 	uint32 scratchSize = 24 * 1024 / fBlockSize;
640 		// maximum value allowed by Phoenix BIOS is 0x7f
641 
642 	while (blocksLeft > 0) {
643 		uint32 blocksRead = blocksLeft;
644 		if (blocksRead > scratchSize)
645 			blocksRead = scratchSize;
646 
647 		if (fLBA) {
648 			struct disk_address_packet *packet = (disk_address_packet *)kDataSegmentScratch;
649 			memset(packet, 0, sizeof(disk_address_packet));
650 
651 			packet->size = sizeof(disk_address_packet);
652 			packet->number_of_blocks = blocksRead;
653 			packet->buffer = kExtraSegmentScratch;
654 			packet->lba = pos;
655 
656 			struct bios_regs regs;
657 			regs.eax = BIOS_EXT_READ;
658 			regs.edx = fDriveID;
659 			regs.esi = (addr_t)packet - kDataSegmentBase;
660 			call_bios(0x13, &regs);
661 
662 			if (regs.flags & CARRY_FLAG)
663 				goto chs_read;
664 		} else {
665 	chs_read:
666 			// Old style CHS read routine
667 
668 			// We can only read up to 64 kB this way, but since scratchSize
669 			// is actually lower than this value, we don't have to take care
670 			// of this here.
671 
672 			uint32 sector = pos % fParameters.sectors_per_track + 1;
673 				// sectors start countint at 1 (unlike head and cylinder)
674 			uint32 head = pos / fParameters.sectors_per_track;
675 			uint32 cylinder = head / fParameters.heads;
676 			head %= fParameters.heads;
677 
678 			if (cylinder >= fParameters.cylinders) {
679 				TRACE(("cylinder value %lu bigger than available %lu\n",
680 					cylinder, fParameters.cylinders));
681 				return B_BAD_VALUE;
682 			}
683 
684 			// try to read from the device more than once, just to make sure it'll work
685 			struct bios_regs regs;
686 			int32 tries = 3;
687 			bool readWorked = false;
688 
689 			while (tries-- > 0) {
690 				regs.eax = BIOS_READ | blocksRead;
691 				regs.edx = fDriveID | (head << 8);
692 				regs.ecx = sector | ((cylinder >> 2) & 0xc0) | ((cylinder & 0xff) << 8);
693 				regs.es = 0;
694 				regs.ebx = kExtraSegmentScratch;
695 				call_bios(0x13, &regs);
696 
697 				if ((regs.flags & CARRY_FLAG) == 0) {
698 					readWorked = true;
699 					break;
700 				}
701 
702 				TRACE(("read failed\n"));
703 
704 				if (tries < 2) {
705 					// reset disk system
706 					TRACE(("reset disk system\n"));
707 					regs.eax = BIOS_RESET_DISK_SYSTEM;
708 					regs.edx = fDriveID;
709 					call_bios(0x13, &regs);
710 				}
711 
712 				// wait a bit between the retries (1/20 sec)
713 				spin(50000);
714 			}
715 
716 			if (!readWorked) {
717 				dprintf("reading %ld bytes from drive %u failed at %Ld\n",
718 					blocksRead, fDriveID, pos);
719 				return B_ERROR;
720 			}
721 		}
722 
723 		uint32 bytesRead = fBlockSize * blocksRead - offset;
724 		// copy no more than bufferSize bytes
725 		if (bytesRead > bufferSize)
726 			bytesRead = bufferSize;
727 
728 		memcpy(buffer, (void *)(kExtraSegmentScratch + offset), bytesRead);
729 		pos += blocksRead;
730 		offset = 0;
731 		blocksLeft -= blocksRead;
732 		bufferSize -= bytesRead;
733 		buffer = (void *)((addr_t)buffer + bytesRead);
734 		totalBytesRead += bytesRead;
735 	}
736 
737 	return totalBytesRead;
738 }
739 
740 
741 ssize_t
742 BIOSDrive::WriteAt(void* cookie, off_t pos, const void* buffer,
743 	size_t bufferSize)
744 {
745 	// we support only LBA addressing
746 	if (!fLBA) {
747 		dprintf("BIOSDrive::WriteAt(): CHS addressing not supported\n");
748 		return B_UNSUPPORTED;
749 	}
750 
751 	// we support only block-aligned writes
752 	if (pos % fBlockSize != 0 || bufferSize % fBlockSize != 0) {
753 		dprintf("BIOSDrive::WriteAt(pos: %" B_PRIdOFF ", size: %" B_PRIuSIZE
754 			"): Block-unaligned write not supported.\n", pos, bufferSize);
755 		return B_UNSUPPORTED;
756 	}
757 
758 	pos /= fBlockSize;
759 
760 	uint32 blocksLeft = bufferSize / fBlockSize;
761 	int32 totalBytesWritten = 0;
762 
763 	uint32 scratchSize = 24 * 1024 / fBlockSize;
764 		// maximum value allowed by Phoenix BIOS is 0x7f
765 
766 	while (blocksLeft > 0) {
767 		uint32 blocksToWrite = blocksLeft;
768 		if (blocksToWrite > scratchSize)
769 			blocksToWrite = scratchSize;
770 
771 		uint32 bytesToWrite = blocksToWrite * fBlockSize;
772 
773 		memcpy((void*)kExtraSegmentScratch, buffer, bytesToWrite);
774 
775 		struct disk_address_packet* packet
776 			= (disk_address_packet*)kDataSegmentScratch;
777 		memset(packet, 0, sizeof(disk_address_packet));
778 
779 		packet->size = sizeof(disk_address_packet);
780 		packet->number_of_blocks = blocksToWrite;
781 		packet->buffer = kExtraSegmentScratch;
782 		packet->lba = pos;
783 
784 		struct bios_regs regs;
785 		regs.eax = BIOS_EXT_WRITE;	// al = 0x00 -- no write verify
786 		regs.edx = fDriveID;
787 		regs.esi = (addr_t)packet - kDataSegmentBase;
788 		call_bios(0x13, &regs);
789 
790 		if (regs.flags & CARRY_FLAG)
791 			return B_ERROR;
792 
793 		pos += blocksToWrite;
794 		blocksLeft -= blocksToWrite;
795 		bufferSize -= bytesToWrite;
796 		buffer = (void*)((addr_t)buffer + bytesToWrite);
797 		totalBytesWritten += bytesToWrite;
798 	}
799 
800 	return totalBytesWritten;
801 }
802 
803 
804 off_t
805 BIOSDrive::Size() const
806 {
807 	return fSize;
808 }
809 
810 
811 status_t
812 BIOSDrive::FillIdentifier()
813 {
814 	if (HasParameters()) {
815 		// try all drive_parameters versions, beginning from the most informative
816 
817 #if 0
818 		if (fill_disk_identifier_v3(fIdentifier, fParameters) == B_OK)
819 			return B_OK;
820 
821 		if (fill_disk_identifier_v2(fIdentifier, fParameters) == B_OK)
822 			return B_OK;
823 #else
824 		// TODO: the above version is the correct one - it's currently
825 		//		disabled, as the kernel boot code only supports the
826 		//		UNKNOWN_BUS/UNKNOWN_DEVICE way to find the correct boot
827 		//		device.
828 		if (fill_disk_identifier_v3(fIdentifier, fParameters) != B_OK)
829 			fill_disk_identifier_v2(fIdentifier, fParameters);
830 
831 #endif
832 
833 		// no interesting information, we have to fall back to the default
834 		// unknown interface/device type identifier
835 	}
836 
837 	fIdentifier.bus_type = UNKNOWN_BUS;
838 	fIdentifier.device_type = UNKNOWN_DEVICE;
839 	fIdentifier.device.unknown.size = Size();
840 
841 	for (int32 i = 0; i < NUM_DISK_CHECK_SUMS; i++) {
842 		fIdentifier.device.unknown.check_sums[i].offset = -1;
843 		fIdentifier.device.unknown.check_sums[i].sum = 0;
844 	}
845 
846 	return B_ERROR;
847 }
848 
849 
850 //	#pragma mark -
851 
852 
853 status_t
854 platform_add_boot_device(struct stage2_args *args, NodeList *devicesList)
855 {
856 	TRACE(("boot drive ID: %x\n", gBootDriveID));
857 
858 	BIOSDrive *drive = new(nothrow) BIOSDrive(gBootDriveID);
859 	if (drive->InitCheck() != B_OK) {
860 		dprintf("no boot drive!\n");
861 		delete drive;
862 		return B_ERROR;
863 	}
864 
865 	devicesList->Add(drive);
866 
867 	if (drive->FillIdentifier() != B_OK) {
868 		// We need to add all block devices to give the kernel the possibility
869 		// to find the right boot volume
870 		add_block_devices(devicesList, true);
871 	}
872 
873 	TRACE(("boot drive size: %Ld bytes\n", drive->Size()));
874 	gBootVolume.SetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, gBootedFromImage);
875 
876 	return B_OK;
877 }
878 
879 
880 status_t
881 platform_get_boot_partitions(struct stage2_args *args, Node *bootDevice,
882 	NodeList *list, NodeList *bootList)
883 {
884 	BIOSDrive *drive = static_cast<BIOSDrive *>(bootDevice);
885 	off_t offset = (off_t)gBootPartitionOffset * drive->BlockSize();
886 
887 	dprintf("boot partition offset: %Ld\n", offset);
888 
889 	NodeIterator iterator = list->GetIterator();
890 	boot::Partition *partition = NULL;
891 	while ((partition = (boot::Partition *)iterator.Next()) != NULL) {
892 		TRACE(("partition offset = %Ld, size = %Ld\n", partition->offset, partition->size));
893 		// search for the partition that contains the partition
894 		// offset as reported by the BFS boot block
895 		if (offset >= partition->offset
896 			&& offset < partition->offset + partition->size) {
897 			bootList->Insert(partition);
898 			return B_OK;
899 		}
900 	}
901 
902 	return B_ENTRY_NOT_FOUND;
903 }
904 
905 
906 status_t
907 platform_add_block_devices(stage2_args *args, NodeList *devicesList)
908 {
909 	return add_block_devices(devicesList, false);
910 }
911 
912 
913 status_t
914 platform_register_boot_device(Node *device)
915 {
916 	BIOSDrive *drive = (BIOSDrive *)device;
917 
918 	check_cd_boot(drive);
919 
920 	gBootVolume.SetInt64("boot drive number", drive->DriveID());
921 	gBootVolume.SetData(BOOT_VOLUME_DISK_IDENTIFIER, B_RAW_TYPE,
922 		&drive->Identifier(), sizeof(disk_identifier));
923 
924 	return B_OK;
925 }
926 
927