xref: /haiku/src/system/boot/platform/bios_ia32/devices.cpp (revision 1214ef1b2100f2b3299fc9d8d6142e46f70a4c3f)
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_GET_EXT_DRIVE_PARAMETERS	0x4800
37 #define BIOS_BOOT_CD_GET_STATUS			0x4b01
38 
39 struct real_addr {
40 	uint16	offset;
41 	uint16	segment;
42 };
43 
44 struct disk_address_packet {
45 	uint8		size;
46 	uint8		reserved;
47 	uint16		number_of_blocks;
48 	uint32		buffer;
49 	uint64		lba;
50 	uint64		flat_buffer;
51 };
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 	gKernelArgs.boot_volume.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 		gKernelArgs.boot_volume.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 status_t
202 get_ext_drive_parameters(uint8 drive, drive_parameters *targetParameters)
203 {
204 	drive_parameters *parameter = (drive_parameters *)kDataSegmentScratch;
205 
206 	memset(parameter, 0, sizeof(drive_parameters));
207 	parameter->parameters_size = sizeof(drive_parameters);
208 
209 	struct bios_regs regs;
210 	regs.eax = BIOS_GET_EXT_DRIVE_PARAMETERS;
211 	regs.edx = drive;
212 	regs.esi = (addr_t)parameter - kDataSegmentBase;
213 	call_bios(0x13, &regs);
214 
215 	// filter out faulty BIOS return codes
216 	if ((regs.flags & CARRY_FLAG) != 0
217 		|| parameter->sectors == 0)
218 		return B_ERROR;
219 
220 	memcpy(targetParameters, parameter, sizeof(drive_parameters));
221 	return B_OK;
222 }
223 
224 
225 static status_t
226 get_drive_parameters(uint8 drive, drive_parameters *parameters)
227 {
228 	struct bios_regs regs;
229 	regs.eax = BIOS_GET_DRIVE_PARAMETERS;
230 	regs.edx = drive;
231 	regs.es = 0;
232 	regs.edi = 0;	// guard against faulty BIOS, see Ralf Brown's interrupt list
233 	call_bios(0x13, &regs);
234 
235 	if ((regs.flags & CARRY_FLAG) != 0 || (regs.ecx & 0x3f) == 0)
236 		return B_ERROR;
237 
238 	// fill drive_parameters structure with useful values
239 	parameters->parameters_size = kParametersSizeVersion1;
240 	parameters->flags = 0;
241 	parameters->cylinders = (((regs.ecx & 0xc0) << 2) | ((regs.ecx >> 8) & 0xff)) + 1;
242 	parameters->heads = ((regs.edx >> 8) & 0xff) + 1;
243 		// heads and cylinders start counting from 0
244 	parameters->sectors_per_track = regs.ecx & 0x3f;
245 	parameters->sectors = parameters->cylinders * parameters->heads
246 		* parameters->sectors_per_track;
247 	parameters->bytes_per_sector = 512;
248 
249 	return B_OK;
250 }
251 
252 
253 static status_t
254 get_number_of_drives(uint8 *_count)
255 {
256 	struct bios_regs regs;
257 	regs.eax = BIOS_GET_DRIVE_PARAMETERS;
258 	regs.edx = 0x80;
259 	regs.es = 0;
260 	regs.edi = 0;
261 	call_bios(0x13, &regs);
262 
263 	if (regs.flags & CARRY_FLAG)
264 		return B_ERROR;
265 
266 	*_count = regs.edx & 0xff;
267 	return B_OK;
268 }
269 
270 
271 /** parse EDD 3.0 drive path information */
272 
273 static status_t
274 fill_disk_identifier_v3(disk_identifier &disk, const drive_parameters &parameters)
275 {
276 	if (parameters.parameters_size < kParametersSizeVersion3
277 		|| parameters.device_path_signature != kDevicePathSignature)
278 		return B_BAD_TYPE;
279 
280 	// parse host bus
281 
282 	if (!strncmp(parameters.host_bus, "PCI", 3)) {
283 		disk.bus_type = PCI_BUS;
284 
285 		disk.bus.pci.bus = parameters.interface.pci.bus;
286 		disk.bus.pci.slot = parameters.interface.pci.slot;
287 		disk.bus.pci.function = parameters.interface.pci.function;
288 	} else if (!strncmp(parameters.host_bus, "ISA", 3)) {
289 		disk.bus_type = LEGACY_BUS;
290 
291 		disk.bus.legacy.base_address = parameters.interface.legacy.base_address;
292 		dprintf("legacy base address %x\n", disk.bus.legacy.base_address);
293 	} else {
294 		dprintf("unknown host bus \"%s\"\n", parameters.host_bus);
295 		return B_BAD_DATA;
296 	}
297 
298 	// parse interface
299 
300 	if (!strncmp(parameters.interface_type, "ATA", 3)) {
301 		disk.device_type = ATA_DEVICE;
302 		disk.device.ata.master = !parameters.device.ata.slave;
303 		dprintf("ATA device, %s\n", disk.device.ata.master ? "master" : "slave");
304 	} else if (!strncmp(parameters.interface_type, "ATAPI", 3)) {
305 		disk.device_type = ATAPI_DEVICE;
306 		disk.device.atapi.master = !parameters.device.ata.slave;
307 		disk.device.atapi.logical_unit = parameters.device.atapi.logical_unit;
308 	} else if (!strncmp(parameters.interface_type, "SCSI", 3)) {
309 		disk.device_type = SCSI_DEVICE;
310 		disk.device.scsi.logical_unit = parameters.device.scsi.logical_unit;
311 	} else if (!strncmp(parameters.interface_type, "USB", 3)) {
312 		disk.device_type = USB_DEVICE;
313 		disk.device.usb.tbd = parameters.device.usb.tbd;
314 	} else if (!strncmp(parameters.interface_type, "1394", 3)) {
315 		disk.device_type = FIREWIRE_DEVICE;
316 		disk.device.firewire.guid = parameters.device.firewire.guid;
317 	} else if (!strncmp(parameters.interface_type, "FIBRE", 3)) {
318 		disk.device_type = FIBRE_DEVICE;
319 		disk.device.fibre.wwd = parameters.device.fibre.wwd;
320 	} else {
321 		dprintf("unknown interface type \"%s\"\n", parameters.interface_type);
322 		return B_BAD_DATA;
323 	}
324 
325 	return B_OK;
326 }
327 
328 
329 /** EDD 2.0 drive table information */
330 
331 static status_t
332 fill_disk_identifier_v2(disk_identifier &disk, const drive_parameters &parameters)
333 {
334 	if (parameters.device_table.segment == 0xffff
335 		&& parameters.device_table.offset == 0xffff)
336 		return B_BAD_TYPE;
337 
338 	device_table *table = (device_table *)LINEAR_ADDRESS(parameters.device_table.segment,
339 		parameters.device_table.offset);
340 
341 	disk.bus_type = LEGACY_BUS;
342 	disk.bus.legacy.base_address = table->base_address;
343 
344 	disk.device_type = ATA_DEVICE;
345 	disk.device.ata.master = !table->is_slave;
346 
347 	return B_OK;
348 }
349 
350 
351 static off_t
352 get_next_check_sum_offset(int32 index, off_t maxSize)
353 {
354 	// The boot block often contains the disk super block, and should be
355 	// unique enough for most cases
356 	if (index < 2)
357 		return index * 512;
358 
359 	// Try some data in the first part of the drive
360 	if (index < 4)
361 		return (maxSize >> 10) + index * 2048;
362 
363 	// Some random value might do
364 	return ((system_time() + index) % (maxSize >> 9)) * 512;
365 }
366 
367 
368 /**	Computes a check sum for the specified block.
369  *	The check sum is the sum of all data in that block interpreted as an
370  *	array of uint32 values.
371  *	Note, this must use the same method as the one used in kernel/fs/vfs_boot.cpp.
372  */
373 
374 static uint32
375 compute_check_sum(BIOSDrive *drive, off_t offset)
376 {
377 	char buffer[512];
378 	ssize_t bytesRead = drive->ReadAt(NULL, offset, buffer, sizeof(buffer));
379 	if (bytesRead < B_OK)
380 		return 0;
381 
382 	if (bytesRead < (ssize_t)sizeof(buffer))
383 		memset(buffer + bytesRead, 0, sizeof(buffer) - bytesRead);
384 
385 	uint32 *array = (uint32 *)buffer;
386 	uint32 sum = 0;
387 
388 	for (uint32 i = 0; i < (bytesRead + sizeof(uint32) - 1) / sizeof(uint32); i++) {
389 		sum += array[i];
390 	}
391 
392 	return sum;
393 }
394 
395 
396 static void
397 find_unique_check_sums(NodeList *devices)
398 {
399 	NodeIterator iterator = devices->GetIterator();
400 	Node *device;
401 	int32 index = 0;
402 	off_t minSize = 0;
403 	const int32 kMaxTries = 200;
404 
405 	while (index < kMaxTries) {
406 		bool clash = false;
407 
408 		iterator.Rewind();
409 
410 		while ((device = iterator.Next()) != NULL) {
411 			BIOSDrive *drive = (BIOSDrive *)device;
412 #if 0
413 			// there is no RTTI in the boot loader...
414 			BIOSDrive *drive = dynamic_cast<BIOSDrive *>(device);
415 			if (drive == NULL)
416 				continue;
417 #endif
418 
419 			// TODO: currently, we assume that the BIOS provided us with unique
420 			//	disk identifiers... hopefully this is a good idea
421 			if (drive->Identifier().device_type != UNKNOWN_DEVICE)
422 				continue;
423 
424 			if (minSize == 0 || drive->Size() < minSize)
425 				minSize = drive->Size();
426 
427 			// check for clashes
428 
429 			NodeIterator compareIterator = devices->GetIterator();
430 			while ((device = compareIterator.Next()) != NULL) {
431 				BIOSDrive *compareDrive = (BIOSDrive *)device;
432 
433 				if (compareDrive == drive
434 					|| compareDrive->Identifier().device_type != UNKNOWN_DEVICE)
435 					continue;
436 
437 				if (!memcmp(&drive->Identifier(), &compareDrive->Identifier(),
438 						sizeof(disk_identifier))) {
439 					clash = true;
440 					break;
441 				}
442 			}
443 
444 			if (clash)
445 				break;
446 		}
447 
448 		if (!clash) {
449 			// our work here is done.
450 			return;
451 		}
452 
453 		// add a new block to the check sums
454 
455 		off_t offset = get_next_check_sum_offset(index, minSize);
456 		int32 i = index % NUM_DISK_CHECK_SUMS;
457 		iterator.Rewind();
458 
459 		while ((device = iterator.Next()) != NULL) {
460 			BIOSDrive *drive = (BIOSDrive *)device;
461 
462 			disk_identifier& disk = drive->Identifier();
463 			disk.device.unknown.check_sums[i].offset = offset;
464 			disk.device.unknown.check_sums[i].sum = compute_check_sum(drive, offset);
465 
466 			TRACE(("disk %x, offset %Ld, sum %lu\n", drive->DriveID(), offset,
467 				disk.device.unknown.check_sums[i].sum));
468 		}
469 
470 		index++;
471 	}
472 
473 	// If we get here, we couldn't find a way to differentiate all disks from each other.
474 	// It's very likely that one disk is an exact copy of the other, so there is nothing
475 	// we could do, anyway.
476 
477 	dprintf("Could not make BIOS drives unique! Might boot from the wrong disk...\n");
478 }
479 
480 
481 static status_t
482 add_block_devices(NodeList *devicesList, bool identifierMissing)
483 {
484 	if (sBlockDevicesAdded)
485 		return B_OK;
486 
487 	uint8 driveCount;
488 	if (get_number_of_drives(&driveCount) != B_OK)
489 		return B_ERROR;
490 
491 	dprintf("number of drives: %d\n", driveCount);
492 
493 	for (int32 i = 0; i < driveCount; i++) {
494 		uint8 driveID = i + 0x80;
495 		if (driveID == gBootDriveID)
496 			continue;
497 
498 		BIOSDrive *drive = new(nothrow) BIOSDrive(driveID);
499 		if (drive->InitCheck() != B_OK) {
500 			dprintf("could not add drive %u\n", driveID);
501 			delete drive;
502 			continue;
503 		}
504 
505 		devicesList->Add(drive);
506 
507 		if (drive->FillIdentifier() != B_OK)
508 			identifierMissing = true;
509 	}
510 
511 	if (identifierMissing) {
512 		// we cannot distinguish between all drives by identifier, we need
513 		// compute checksums for them
514 		find_unique_check_sums(devicesList);
515 	}
516 
517 	sBlockDevicesAdded = true;
518 	return B_OK;
519 }
520 
521 
522 //	#pragma mark -
523 
524 
525 BIOSDrive::BIOSDrive(uint8 driveID)
526 	:
527 	fDriveID(driveID)
528 {
529 	TRACE(("drive ID %u\n", driveID));
530 
531 	if (get_ext_drive_parameters(driveID, &fParameters) != B_OK) {
532 		// old style CHS support
533 
534 		if (get_drive_parameters(driveID, &fParameters) != B_OK) {
535 			dprintf("getting drive parameters for: %u failed!\n", fDriveID);
536 			return;
537 		}
538 
539 		TRACE(("  cylinders: %lu, heads: %lu, sectors: %lu, bytes_per_sector: %u\n",
540 			fParameters.cylinders, fParameters.heads, fParameters.sectors_per_track,
541 			fParameters.bytes_per_sector));
542 		TRACE(("  total sectors: %Ld\n", fParameters.sectors));
543 
544 		fBlockSize = 512;
545 		fSize = fParameters.sectors * fBlockSize;
546 		fLBA = false;
547 		fHasParameters = false;
548 	} else {
549 		TRACE(("size: %x\n", fParameters.parameters_size));
550 		TRACE(("drive_path_signature: %x\n", fParameters.device_path_signature));
551 		TRACE(("host bus: \"%s\", interface: \"%s\"\n", fParameters.host_bus,
552 			fParameters.interface_type));
553 		TRACE(("cylinders: %lu, heads: %lu, sectors: %lu, bytes_per_sector: %u\n",
554 			fParameters.cylinders, fParameters.heads, fParameters.sectors_per_track,
555 			fParameters.bytes_per_sector));
556 		TRACE(("total sectors: %Ld\n", fParameters.sectors));
557 
558 		fBlockSize = fParameters.bytes_per_sector;
559 		fSize = fParameters.sectors * fBlockSize;
560 		fLBA = true;
561 		fHasParameters = true;
562 	}
563 }
564 
565 
566 BIOSDrive::~BIOSDrive()
567 {
568 }
569 
570 
571 status_t
572 BIOSDrive::InitCheck() const
573 {
574 	return fSize > 0 ? B_OK : B_ERROR;
575 }
576 
577 
578 ssize_t
579 BIOSDrive::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize)
580 {
581 	uint32 offset = pos % fBlockSize;
582 	pos /= fBlockSize;
583 
584 	uint32 blocksLeft = (bufferSize + offset + fBlockSize - 1) / fBlockSize;
585 	int32 totalBytesRead = 0;
586 
587 	//TRACE(("BIOS reads %lu bytes from %Ld (offset = %lu), drive %u\n",
588 	//	blocksLeft * fBlockSize, pos * fBlockSize, offset, fDriveID));
589 
590 	uint32 scratchSize = 24 * 1024 / fBlockSize;
591 		// maximum value allowed by Phoenix BIOS is 0x7f
592 
593 	while (blocksLeft > 0) {
594 		uint32 blocksRead = blocksLeft;
595 		if (blocksRead > scratchSize)
596 			blocksRead = scratchSize;
597 
598 		if (fLBA) {
599 			struct disk_address_packet *packet = (disk_address_packet *)kDataSegmentScratch;
600 			memset(packet, 0, sizeof(disk_address_packet));
601 
602 			packet->size = sizeof(disk_address_packet);
603 			packet->number_of_blocks = blocksRead;
604 			packet->buffer = kExtraSegmentScratch;
605 			packet->lba = pos;
606 
607 			struct bios_regs regs;
608 			regs.eax = BIOS_EXT_READ;
609 			regs.edx = fDriveID;
610 			regs.esi = (addr_t)packet - kDataSegmentBase;
611 			call_bios(0x13, &regs);
612 
613 			if (regs.flags & CARRY_FLAG)
614 				goto chs_read;
615 		} else {
616 	chs_read:
617 			// Old style CHS read routine
618 
619 			// We can only read up to 64 kB this way, but since scratchSize
620 			// is actually lower than this value, we don't have to take care
621 			// of this here.
622 
623 			uint32 sector = pos % fParameters.sectors_per_track + 1;
624 				// sectors start countint at 1 (unlike head and cylinder)
625 			uint32 head = pos / fParameters.sectors_per_track;
626 			uint32 cylinder = head / fParameters.heads;
627 			head %= fParameters.heads;
628 
629 			if (cylinder >= fParameters.cylinders)
630 				return B_BAD_VALUE;
631 
632 			// try to read from the device more than once, just to make sure it'll work
633 			struct bios_regs regs;
634 			int32 tries = 3;
635 
636 			while (tries-- > 0) {
637 				regs.eax = BIOS_READ | blocksRead;
638 				regs.edx = fDriveID | (head << 8);
639 				regs.ecx = sector | ((cylinder >> 2) & 0xc0) | ((cylinder & 0xff) << 8);
640 				regs.es = 0;
641 				regs.ebx = kExtraSegmentScratch;
642 				call_bios(0x13, &regs);
643 
644 				if ((regs.flags & CARRY_FLAG) == 0)
645 					break;
646 
647 				if (tries < 2) {
648 					// reset disk system
649 					regs.eax = BIOS_RESET_DISK_SYSTEM;
650 					regs.edx = fDriveID;
651 					call_bios(0x13, &regs);
652 				}
653 
654 				// wait a bit between the retries (1/20 sec)
655 				spin(50000);
656 			}
657 
658 			if (regs.flags & CARRY_FLAG) {
659 				dprintf("reading %ld bytes from drive %u failed at %Ld\n",
660 					blocksRead, fDriveID, pos);
661 				return B_ERROR;
662 			}
663 		}
664 
665 		uint32 bytesRead = fBlockSize * blocksRead - offset;
666 		// copy no more than bufferSize bytes
667 		if (bytesRead > bufferSize)
668 			bytesRead = bufferSize;
669 
670 		memcpy(buffer, (void *)(kExtraSegmentScratch + offset), bytesRead);
671 		pos += blocksRead;
672 		offset = 0;
673 		blocksLeft -= blocksRead;
674 		bufferSize -= bytesRead;
675 		buffer = (void *)((addr_t)buffer + bytesRead);
676 		totalBytesRead += bytesRead;
677 	}
678 
679 	return totalBytesRead;
680 }
681 
682 
683 ssize_t
684 BIOSDrive::WriteAt(void *cookie, off_t pos, const void *buffer, size_t bufferSize)
685 {
686 	// we don't have to know how to write
687 	return B_NOT_ALLOWED;
688 }
689 
690 
691 off_t
692 BIOSDrive::Size() const
693 {
694 	return fSize;
695 }
696 
697 
698 status_t
699 BIOSDrive::FillIdentifier()
700 {
701 	if (HasParameters()) {
702 		// try all drive_parameters versions, beginning from the most informative
703 
704 #if 0
705 		if (fill_disk_identifier_v3(fIdentifier, fParameters) == B_OK)
706 			return B_OK;
707 
708 		if (fill_disk_identifier_v2(fIdentifier, fParameters) == B_OK)
709 			return B_OK;
710 #else
711 		// TODO: the above version is the correct one - it's currently
712 		//		disabled, as the kernel boot code only supports the
713 		//		UNKNOWN_BUS/UNKNOWN_DEVICE way to find the correct boot
714 		//		device.
715 		if (fill_disk_identifier_v3(fIdentifier, fParameters) != B_OK)
716 			fill_disk_identifier_v2(fIdentifier, fParameters);
717 
718 #endif
719 
720 		// no interesting information, we have to fall back to the default
721 		// unknown interface/device type identifier
722 	}
723 
724 	fIdentifier.bus_type = UNKNOWN_BUS;
725 	fIdentifier.device_type = UNKNOWN_DEVICE;
726 	fIdentifier.device.unknown.size = Size();
727 
728 	for (int32 i = 0; i < NUM_DISK_CHECK_SUMS; i++) {
729 		fIdentifier.device.unknown.check_sums[i].offset = -1;
730 		fIdentifier.device.unknown.check_sums[i].sum = 0;
731 	}
732 
733 	return B_ERROR;
734 }
735 
736 
737 //	#pragma mark -
738 
739 
740 status_t
741 platform_add_boot_device(struct stage2_args *args, NodeList *devicesList)
742 {
743 	TRACE(("boot drive ID: %x\n", gBootDriveID));
744 
745 	BIOSDrive *drive = new(nothrow) BIOSDrive(gBootDriveID);
746 	if (drive->InitCheck() != B_OK) {
747 		dprintf("no boot drive!\n");
748 		return B_ERROR;
749 	}
750 
751 	devicesList->Add(drive);
752 
753 	if (drive->FillIdentifier() != B_OK) {
754 		// We need to add all block devices to give the kernel the possibility
755 		// to find the right boot volume
756 		add_block_devices(devicesList, true);
757 	}
758 
759 	TRACE(("boot drive size: %Ld bytes\n", drive->Size()));
760 	gKernelArgs.boot_volume.SetInt32(BOOT_VOLUME_BOOTED_FROM_IMAGE,
761 		gBootedFromImage);
762 
763 	return B_OK;
764 }
765 
766 
767 status_t
768 platform_get_boot_partition(struct stage2_args *args, Node *bootDevice,
769 	NodeList *list, boot::Partition **_partition)
770 {
771 	BIOSDrive *drive = static_cast<BIOSDrive *>(bootDevice);
772 	off_t offset = (off_t)gBootPartitionOffset * drive->BlockSize();
773 
774 	dprintf("boot partition offset: %Ld\n", offset);
775 
776 	NodeIterator iterator = list->GetIterator();
777 	boot::Partition *partition = NULL;
778 	while ((partition = (boot::Partition *)iterator.Next()) != NULL) {
779 		TRACE(("partition offset = %Ld, size = %Ld\n", partition->offset, partition->size));
780 		// search for the partition that contains the partition
781 		// offset as reported by the BFS boot block
782 		if (offset >= partition->offset
783 			&& offset < partition->offset + partition->size) {
784 			*_partition = partition;
785 			return B_OK;
786 		}
787 	}
788 
789 	return B_ENTRY_NOT_FOUND;
790 }
791 
792 
793 status_t
794 platform_add_block_devices(stage2_args *args, NodeList *devicesList)
795 {
796 	return add_block_devices(devicesList, false);
797 }
798 
799 
800 status_t
801 platform_register_boot_device(Node *device)
802 {
803 	BIOSDrive *drive = (BIOSDrive *)device;
804 
805 	check_cd_boot(drive);
806 
807 	gKernelArgs.boot_volume.SetInt64("boot drive number", drive->DriveID());
808 	gKernelArgs.boot_volume.SetData(BOOT_VOLUME_DISK_IDENTIFIER, B_RAW_TYPE,
809 		&drive->Identifier(), sizeof(disk_identifier));
810 
811 	return B_OK;
812 }
813 
814