xref: /haiku/src/system/boot/platform/bios_ia32/devices.cpp (revision 93aeb8c3bc3f13cb1f282e3e749258a23790d947)
1 /*
2  * Copyright 2003-2005, 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 		bool HasParameters() const { return fHasParameters; }
145 		const drive_parameters &Parameters() const { return fParameters; }
146 		uint8 DriveID() const { return fDriveID; }
147 
148 	protected:
149 		uint8	fDriveID;
150 		bool	fLBA;
151 		uint64	fSize;
152 		uint32	fBlockSize;
153 		bool	fHasParameters;
154 		drive_parameters fParameters;
155 };
156 
157 
158 static void
159 check_cd_boot(BIOSDrive *drive)
160 {
161 	gKernelArgs.boot_disk.cd = false;
162 
163 	if (drive->DriveID() != 0)
164 		return;
165 
166 	struct bios_regs regs;
167 	regs.eax = BIOS_BOOT_CD_GET_STATUS;
168 	regs.edx = 0;
169 	regs.esi = kDataSegmentScratch;
170 	call_bios(0x13, &regs);
171 
172 	if ((regs.flags & CARRY_FLAG) != 0)
173 		return;
174 
175 	// we obviously were booted from CD!
176 
177 	specification_packet *packet = (specification_packet *)kDataSegmentScratch;
178 	gKernelArgs.boot_disk.cd = packet->media_type != 0;
179 
180 #if 0
181 	dprintf("got CD boot spec:\n");
182 	dprintf("  size: %#x\n", packet->size);
183 	dprintf("  media type: %u\n", packet->media_type);
184 	dprintf("  drive_number: %u\n", packet->drive_number);
185 	dprintf("  controller index: %u\n", packet->controller_index);
186 	dprintf("  start emulation: %lu\n", packet->start_emulation);
187 	dprintf("  device_specification: %u\n", packet->device_specification);
188 #endif
189 }
190 
191 
192 static status_t
193 get_ext_drive_parameters(uint8 drive, drive_parameters *targetParameters)
194 {
195 	drive_parameters *parameter = (drive_parameters *)kDataSegmentScratch;
196 
197 	memset(parameter, 0, sizeof(drive_parameters));
198 	parameter->parameters_size = sizeof(drive_parameters);
199 
200 	struct bios_regs regs;
201 	regs.eax = BIOS_GET_EXT_DRIVE_PARAMETERS;
202 	regs.edx = drive;
203 	regs.esi = (addr_t)parameter - kDataSegmentBase;
204 	call_bios(0x13, &regs);
205 
206 	// filter out faulty BIOS return codes
207 	if ((regs.flags & CARRY_FLAG) != 0
208 		|| parameter->sectors == 0)
209 		return B_ERROR;
210 
211 	memcpy(targetParameters, parameter, sizeof(drive_parameters));
212 	return B_OK;
213 }
214 
215 
216 static status_t
217 get_drive_parameters(uint8 drive, drive_parameters *parameters)
218 {
219 	struct bios_regs regs;
220 	regs.eax = BIOS_GET_DRIVE_PARAMETERS;
221 	regs.edx = drive;
222 	regs.es = 0;
223 	regs.edi = 0;	// guard against faulty BIOS, see Ralf Brown's interrupt list
224 	call_bios(0x13, &regs);
225 
226 	if ((regs.flags & CARRY_FLAG) != 0 || (regs.ecx & 0x3f) == 0)
227 		return B_ERROR;
228 
229 	// fill drive_parameters structure with useful values
230 	parameters->parameters_size = kParametersSizeVersion1;
231 	parameters->flags = 0;
232 	parameters->cylinders = (((regs.ecx & 0xc0) << 2) | ((regs.ecx >> 8) & 0xff)) + 1;
233 	parameters->heads = ((regs.edx >> 8) & 0xff) + 1;
234 		// heads and cylinders start counting from 0
235 	parameters->sectors_per_track = regs.ecx & 0x3f;
236 	parameters->sectors = parameters->cylinders * parameters->heads
237 		* parameters->sectors_per_track;
238 	parameters->bytes_per_sector = 512;
239 
240 	return B_OK;
241 }
242 
243 
244 static status_t
245 get_number_of_drives(uint8 *_count)
246 {
247 	struct bios_regs regs;
248 	regs.eax = BIOS_GET_DRIVE_PARAMETERS;
249 	regs.edx = 0x80;
250 	regs.es = 0;
251 	regs.edi = 0;
252 	call_bios(0x13, &regs);
253 
254 	if (regs.flags & CARRY_FLAG)
255 		return B_ERROR;
256 
257 	*_count = regs.edx & 0xff;
258 	return B_OK;
259 }
260 
261 
262 /** parse EDD 3.0 drive path information */
263 
264 static status_t
265 fill_disk_identifier_v3(disk_identifier &disk, const drive_parameters &parameters)
266 {
267 	if (parameters.parameters_size < kParametersSizeVersion3
268 		|| parameters.device_path_signature != kDevicePathSignature)
269 		return B_BAD_TYPE;
270 
271 	// parse host bus
272 
273 	if (!strncmp(parameters.host_bus, "PCI", 3)) {
274 		disk.bus_type = PCI_BUS;
275 
276 		disk.bus.pci.bus = parameters.interface.pci.bus;
277 		disk.bus.pci.slot = parameters.interface.pci.slot;
278 		disk.bus.pci.function = parameters.interface.pci.function;
279 	} else if (!strncmp(parameters.host_bus, "ISA", 3)) {
280 		disk.bus_type = LEGACY_BUS;
281 
282 		disk.bus.legacy.base_address = parameters.interface.legacy.base_address;
283 	} else {
284 		dprintf("unknown host bus \"%s\"\n", parameters.host_bus);
285 		return B_BAD_DATA;
286 	}
287 
288 	// parse interface
289 
290 	if (!strncmp(parameters.interface_type, "ATA", 3)) {
291 		disk.device_type = ATA_DEVICE;
292 		disk.device.ata.master = !parameters.device.ata.slave;
293 	} else if (!strncmp(parameters.interface_type, "ATAPI", 3)) {
294 		disk.device_type = ATAPI_DEVICE;
295 		disk.device.atapi.master = !parameters.device.ata.slave;
296 		disk.device.atapi.logical_unit = parameters.device.atapi.logical_unit;
297 	} else if (!strncmp(parameters.interface_type, "SCSI", 3)) {
298 		disk.device_type = SCSI_DEVICE;
299 		disk.device.scsi.logical_unit = parameters.device.scsi.logical_unit;
300 	} else if (!strncmp(parameters.interface_type, "USB", 3)) {
301 		disk.device_type = USB_DEVICE;
302 		disk.device.usb.tbd = parameters.device.usb.tbd;
303 	} else if (!strncmp(parameters.interface_type, "1394", 3)) {
304 		disk.device_type = FIREWIRE_DEVICE;
305 		disk.device.firewire.guid = parameters.device.firewire.guid;
306 	} else if (!strncmp(parameters.interface_type, "FIBRE", 3)) {
307 		disk.device_type = FIBRE_DEVICE;
308 		disk.device.fibre.wwd = parameters.device.fibre.wwd;
309 	} else {
310 		dprintf("unknown interface type \"%s\"\n", parameters.interface_type);
311 		return B_BAD_DATA;
312 	}
313 
314 	return B_OK;
315 }
316 
317 
318 /** EDD 2.0 drive table information */
319 
320 static status_t
321 fill_disk_identifier_v2(disk_identifier &disk, const drive_parameters &parameters)
322 {
323 	if (parameters.device_table.segment == 0xffff
324 		&& parameters.device_table.offset == 0xffff)
325 		return B_BAD_TYPE;
326 
327 	device_table *table = (device_table *)LINEAR_ADDRESS(parameters.device_table.segment,
328 		parameters.device_table.offset);
329 
330 	disk.bus_type = LEGACY_BUS;
331 	disk.bus.legacy.base_address = table->base_address;
332 
333 	disk.device_type = ATA_DEVICE;
334 	disk.device.ata.master = !table->is_slave;
335 
336 	return B_OK;
337 }
338 
339 
340 #define DUMPED_BLOCK_SIZE 16
341 
342 void
343 dumpBlock(const char *buffer, int size, const char *prefix)
344 {
345 	int i;
346 
347 	for (i = 0; i < size;) {
348 		int start = i;
349 
350 		dprintf(prefix);
351 		for (; i < start+DUMPED_BLOCK_SIZE; i++) {
352 			if (!(i % 4))
353 				dprintf(" ");
354 
355 			if (i >= size)
356 				dprintf("  ");
357 			else
358 				dprintf("%02x", *(unsigned char *)(buffer + i));
359 		}
360 		dprintf("  ");
361 
362 		for (i = start; i < start + DUMPED_BLOCK_SIZE; i++) {
363 			if (i < size) {
364 				char c = buffer[i];
365 
366 				if (c < 30)
367 					dprintf(".");
368 				else
369 					dprintf("%c", c);
370 			} else
371 				break;
372 		}
373 		dprintf("\n");
374 	}
375 }
376 
377 
378 //	#pragma mark -
379 
380 
381 BIOSDrive::BIOSDrive(uint8 driveID)
382 	:
383 	fDriveID(driveID)
384 {
385 	TRACE(("drive ID %u\n", driveID));
386 
387 	if (get_ext_drive_parameters(driveID, &fParameters) != B_OK) {
388 		// old style CHS support
389 
390 		if (get_drive_parameters(driveID, &fParameters) != B_OK) {
391 			dprintf("getting drive parameters for: %u failed!\n", fDriveID);
392 			return;
393 		}
394 
395 		TRACE(("  cylinders: %lu, heads: %lu, sectors: %lu, bytes_per_sector: %u\n",
396 			fParameters.cylinders, fParameters.heads, fParameters.sectors_per_track,
397 			fParameters.bytes_per_sector));
398 		TRACE(("  total sectors: %Ld\n", fParameters.sectors));
399 
400 		fBlockSize = 512;
401 		fSize = fParameters.sectors * fBlockSize;
402 		fLBA = false;
403 		fHasParameters = false;
404 	} else {
405 		TRACE(("size: %x\n", fParameters.parameters_size));
406 		TRACE(("drive_path_signature: %x\n", fParameters.device_path_signature));
407 		TRACE(("host bus: \"%s\", interface: \"%s\"\n", fParameters.host_bus,
408 			fParameters.interface_type));
409 		TRACE(("cylinders: %lu, heads: %lu, sectors: %lu, bytes_per_sector: %u\n",
410 			fParameters.cylinders, fParameters.heads, fParameters.sectors_per_track,
411 			fParameters.bytes_per_sector));
412 		TRACE(("total sectors: %Ld\n", fParameters.sectors));
413 
414 		fBlockSize = fParameters.bytes_per_sector;
415 		fSize = fParameters.sectors * fBlockSize;
416 		fLBA = true;
417 		fHasParameters = true;
418 	}
419 }
420 
421 
422 BIOSDrive::~BIOSDrive()
423 {
424 }
425 
426 
427 status_t
428 BIOSDrive::InitCheck() const
429 {
430 	return fSize > 0 ? B_OK : B_ERROR;
431 }
432 
433 
434 ssize_t
435 BIOSDrive::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize)
436 {
437 	uint32 offset = pos % fBlockSize;
438 	pos /= fBlockSize;
439 
440 	uint32 blocksLeft = (bufferSize + offset + fBlockSize - 1) / fBlockSize;
441 	int32 totalBytesRead = 0;
442 
443 	TRACE(("BIOS reads %lu bytes from %Ld (offset = %lu), drive %u\n",
444 		blocksLeft * fBlockSize, pos * fBlockSize, offset, fDriveID));
445 
446 	uint32 scratchSize = 24 * 1024 / fBlockSize;
447 		// maximum value allowed by Phoenix BIOS is 0x7f
448 
449 	while (blocksLeft > 0) {
450 		uint32 blocksRead = blocksLeft;
451 		if (blocksRead > scratchSize)
452 			blocksRead = scratchSize;
453 
454 		if (fLBA) {
455 			struct disk_address_packet *packet = (disk_address_packet *)kDataSegmentScratch;
456 			memset(packet, 0, sizeof(disk_address_packet));
457 
458 			packet->size = sizeof(disk_address_packet);
459 			packet->number_of_blocks = blocksRead;
460 			packet->buffer = kExtraSegmentScratch;
461 			packet->lba = pos;
462 
463 			struct bios_regs regs;
464 			regs.eax = BIOS_EXT_READ;
465 			regs.edx = fDriveID;
466 			regs.esi = (addr_t)packet - kDataSegmentBase;
467 			call_bios(0x13, &regs);
468 
469 			if (regs.flags & CARRY_FLAG)
470 				goto chs_read;
471 		} else {
472 	chs_read:
473 			// Old style CHS read routine
474 
475 			// We can only read up to 64 kB this way, but since scratchSize
476 			// is actually lower than this value, we don't have to take care
477 			// of this here.
478 
479 			uint32 sector = pos % fParameters.sectors_per_track + 1;
480 				// sectors start countint at 1 (unlike head and cylinder)
481 			uint32 head = pos / fParameters.sectors_per_track;
482 			uint32 cylinder = head / fParameters.heads;
483 			head %= fParameters.heads;
484 
485 			if (cylinder >= fParameters.cylinders)
486 				return B_BAD_VALUE;
487 
488 			// try to read from the device more than once, just to make sure it'll work
489 			struct bios_regs regs;
490 			int32 tries = 3;
491 
492 			while (tries-- > 0) {
493 				regs.eax = BIOS_READ | blocksRead;
494 				regs.edx = fDriveID | (head << 8);
495 				regs.ecx = sector | ((cylinder >> 2) & 0xc0) | ((cylinder & 0xff) << 8);
496 				regs.es = 0;
497 				regs.ebx = kExtraSegmentScratch;
498 				call_bios(0x13, &regs);
499 
500 				if ((regs.flags & CARRY_FLAG) == 0)
501 					break;
502 
503 				if (tries < 2) {
504 					// reset disk system
505 					regs.eax = BIOS_RESET_DISK_SYSTEM;
506 					regs.edx = fDriveID;
507 					call_bios(0x13, &regs);
508 				}
509 
510 				// wait a bit between the retries (1/20 sec)
511 				spin(50000);
512 			}
513 
514 			if (regs.flags & CARRY_FLAG) {
515 				dprintf("reading %ld bytes from drive %u failed at %Ld\n",
516 					blocksRead, fDriveID, pos);
517 				return B_ERROR;
518 			}
519 		}
520 
521 		uint32 bytesRead = fBlockSize * blocksRead - offset;
522 		// copy no more than bufferSize bytes
523 		if (bytesRead > bufferSize)
524 			bytesRead = bufferSize;
525 
526 		memcpy(buffer, (void *)(kExtraSegmentScratch + offset), bytesRead);
527 		pos += blocksRead;
528 		offset = 0;
529 		blocksLeft -= blocksRead;
530 		bufferSize -= bytesRead;
531 		buffer = (void *)((addr_t)buffer + bytesRead);
532 		totalBytesRead += bytesRead;
533 	}
534 
535 	return totalBytesRead;
536 }
537 
538 
539 ssize_t
540 BIOSDrive::WriteAt(void *cookie, off_t pos, const void *buffer, size_t bufferSize)
541 {
542 	// we don't have to know how to write
543 	return B_NOT_ALLOWED;
544 }
545 
546 
547 off_t
548 BIOSDrive::Size() const
549 {
550 	return fSize;
551 }
552 
553 
554 //	#pragma mark -
555 
556 
557 status_t
558 platform_get_boot_device(struct stage2_args *args, Node **_device)
559 {
560 	TRACE(("boot drive ID: %x\n", gBootDriveID));
561 
562 	BIOSDrive *drive = new BIOSDrive(gBootDriveID);
563 	if (drive->InitCheck() != B_OK) {
564 		dprintf("no boot drive!\n");
565 		return B_ERROR;
566 	}
567 
568 	TRACE(("drive size: %Ld bytes\n", drive->Size()));
569 	gKernelArgs.boot_disk.booted_from_image = gBootedFromImage;
570 
571 	*_device = drive;
572 	return B_OK;
573 }
574 
575 
576 status_t
577 platform_get_boot_partition(struct stage2_args *args, Node *bootDevice,
578 	NodeList *list, boot::Partition **_partition)
579 {
580 	BIOSDrive *drive = static_cast<BIOSDrive *>(bootDevice);
581 	off_t offset = (off_t)gBootPartitionOffset * drive->BlockSize();
582 
583 	dprintf("boot partition offset: %Ld\n", offset);
584 
585 	NodeIterator iterator = list->GetIterator();
586 	boot::Partition *partition = NULL;
587 	while ((partition = (boot::Partition *)iterator.Next()) != NULL) {
588 		TRACE(("partition offset = %Ld, size = %Ld\n", partition->offset, partition->size));
589 		// search for the partition that contains the partition
590 		// offset as reported by the BFS boot block
591 		if (offset >= partition->offset
592 			&& offset < partition->offset + partition->size) {
593 			*_partition = partition;
594 			return B_OK;
595 		}
596 	}
597 
598 	return B_ENTRY_NOT_FOUND;
599 }
600 
601 
602 status_t
603 platform_add_block_devices(stage2_args *args, NodeList *devicesList)
604 {
605 	uint8 driveCount;
606 	if (get_number_of_drives(&driveCount) != B_OK)
607 		return B_ERROR;
608 
609 	dprintf("number of drives: %d\n", driveCount);
610 
611 	for (int32 i = 0; i < driveCount; i++) {
612 		uint8 driveID = i + 0x80;
613 		if (driveID == gBootDriveID)
614 			continue;
615 
616 		BIOSDrive *drive = new BIOSDrive(driveID);
617 		if (drive->InitCheck() != B_OK) {
618 			dprintf("could not add drive %u\n", driveID);
619 			delete drive;
620 			continue;
621 		}
622 
623 		devicesList->Add(drive);
624 	}
625 
626 	return B_OK;
627 }
628 
629 
630 status_t
631 platform_register_boot_device(Node *device)
632 {
633 	disk_identifier &disk = gKernelArgs.boot_disk.identifier;
634 	BIOSDrive *drive = (BIOSDrive *)device;
635 
636 	gKernelArgs.platform_args.boot_drive_number = gBootDriveID;
637 	check_cd_boot(drive);
638 
639 	if (drive->HasParameters()) {
640 		const drive_parameters &parameters = drive->Parameters();
641 
642 		// try all drive_parameters versions, beginning from the most informative
643 
644 		if (fill_disk_identifier_v3(disk, parameters) == B_OK)
645 			return B_OK;
646 
647 		if (fill_disk_identifier_v2(disk, parameters) == B_OK)
648 			return B_OK;
649 
650 		// no interesting information, we have to fall back to the default
651 		// unknown interface/device type identifier
652 	}
653 
654 	// ToDo: register all other BIOS drives as well!
655 
656 	// not yet implemented correctly!!!
657 
658 	dprintf("legacy drive identification\n");
659 
660 	disk.bus_type = UNKNOWN_BUS;
661 	disk.device_type = UNKNOWN_DEVICE;
662 	disk.device.unknown.size = drive->Size();
663 
664 	return B_OK;
665 }
666 
667