xref: /haiku/src/system/boot/platform/atari_m68k/devices.cpp (revision a1163de83ea633463a79de234b8742ee106531b2)
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 <KernelExport.h>
8 #include <boot/platform.h>
9 #include <boot/partitions.h>
10 #include <boot/stdio.h>
11 #include <boot/stage2.h>
12 
13 #include <string.h>
14 
15 #include "Handle.h"
16 #include "toscalls.h"
17 
18 //#define TRACE_DEVICES
19 #ifdef TRACE_DEVICES
20 #	define TRACE(x) dprintf x
21 #else
22 #	define TRACE(x) ;
23 #endif
24 
25 
26 // exported from shell.S
27 extern uint8 gBootedFromImage;
28 extern uint8 gBootDriveAPI; // ATARI_BOOT_DRIVE_API_*
29 extern uint8 gBootDriveID;
30 extern uint32 gBootPartitionOffset;
31 
32 #define SCRATCH_SIZE (2*4096)
33 static uint8 gScratchBuffer[SCRATCH_SIZE];
34 
35 static const uint16 kParametersSizeVersion1 = sizeof(struct tos_bpb);
36 static const uint16 kParametersSizeVersion2 = 0x1e;
37 static const uint16 kParametersSizeVersion3 = 0x42;
38 
39 static const uint16 kDevicePathSignature = 0xbedd;
40 
41 //XXX clean this up!
42 struct drive_parameters {
43 	struct tos_bpb bpb;
44 	uint16		parameters_size;
45 	uint16		flags;
46 	uint32		cylinders;
47 	uint32		heads;
48 	uint32		sectors_per_track;
49 	uint64		sectors;
50 	uint16		bytes_per_sector;
51 	/* edd 2.0 */
52 	//real_addr	device_table;
53 	/* edd 3.0 */
54 	uint16		device_path_signature;
55 	uint8		device_path_size;
56 	uint8		reserved1[3];
57 	char		host_bus[4];
58 	char		interface_type[8];
59 	union {
60 		struct {
61 			uint16	base_address;
62 		} legacy;
63 		struct {
64 			uint8	bus;
65 			uint8	slot;
66 			uint8	function;
67 		} pci;
68 		uint8		reserved[8];
69 	} interface;
70 	union {
71 		struct {
72 			uint8	slave;
73 		} ata;
74 		struct {
75 			uint8	slave;
76 			uint8	logical_unit;
77 		} atapi;
78 		struct {
79 			uint8	logical_unit;
80 		} scsi;
81 		struct {
82 			uint8	tbd;
83 		} usb;
84 		struct {
85 			uint64	guid;
86 		} firewire;
87 		struct {
88 			uint64	wwd;
89 		} fibre;
90 	} device;
91 	uint8		reserved2;
92 	uint8		checksum;
93 } _PACKED;
94 
95 struct device_table {
96 	uint16	base_address;
97 	uint16	control_port_address;
98 	uint8	_reserved1 : 4;
99 	uint8	is_slave : 1;
100 	uint8	_reserved2 : 1;
101 	uint8	lba_enabled : 1;
102 } _PACKED;
103 
104 struct specification_packet {
105 	uint8	size;
106 	uint8	media_type;
107 	uint8	drive_number;
108 	uint8	controller_index;
109 	uint32	start_emulation;
110 	uint16	device_specification;
111 	uint8	_more_[9];
112 } _PACKED;
113 
114 class BlockHandle : public Handle {
115 	public:
116 		BlockHandle(int handle);
117 		virtual ~BlockHandle();
118 
119 		virtual ssize_t ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize);
120 		virtual ssize_t WriteAt(void *cookie, off_t pos, const void *buffer, size_t bufferSize);
121 
122 		virtual off_t Size() const { return fSize; };
123 
124 		uint32 BlockSize() const { return fBlockSize; }
125 
126 		bool HasParameters() const { return fHasParameters; }
127 		const drive_parameters &Parameters() const { return fParameters; }
128 
129 		virtual status_t FillIdentifier();
130 
131 		disk_identifier &Identifier() { return fIdentifier; }
132 		uint8 DriveID() const { return fHandle; }
133 		status_t InitCheck() const { return fSize > 0 ? B_OK : B_ERROR; };
134 
135 
136 
137 		virtual ssize_t ReadBlocks(void *buffer, off_t first, int32 count);
138 
139 	protected:
140 		uint64	fSize;
141 		uint32	fBlockSize;
142 		bool	fHasParameters;
143 		drive_parameters fParameters;
144 		disk_identifier fIdentifier;
145 };
146 
147 
148 class FloppyDrive : public BlockHandle {
149 	public:
150 		FloppyDrive(int handle);
151 		virtual ~FloppyDrive();
152 
153 		status_t FillIdentifier();
154 
155 		virtual ssize_t ReadBlocks(void *buffer, off_t first, int32 count);
156 
157 	protected:
158 		status_t	ReadBPB(struct tos_bpb *bpb);
159 };
160 
161 
162 class BIOSDrive : public BlockHandle {
163 	public:
164 		BIOSDrive(int handle);
165 		virtual ~BIOSDrive();
166 
167 		status_t FillIdentifier();
168 
169 		virtual ssize_t ReadBlocks(void *buffer, off_t first, int32 count);
170 
171 	protected:
172 		status_t	ReadBPB(struct tos_bpb *bpb);
173 };
174 
175 
176 class XHDIDrive : public BlockHandle {
177 	public:
178 		XHDIDrive(int handle, uint16 major, uint16 minor);
179 		virtual ~XHDIDrive();
180 
181 		status_t FillIdentifier();
182 
183 		virtual ssize_t ReadBlocks(void *buffer, off_t first, int32 count);
184 
185 	protected:
186 		uint16 fMajor;
187 		uint16 fMinor;
188 };
189 
190 
191 static bool sBlockDevicesAdded = false;
192 
193 
194 static status_t
195 read_bpb(uint8 drive, struct tos_bpb *bpb)
196 {
197 	struct tos_bpb *p;
198 	p = Getbpb(drive);
199 	memcpy(bpb, p, sizeof(struct tos_bpb));
200 	/* Getbpb is buggy so we must force a media change */
201 	//XXX: docs seems to assume we should loop until it works
202 	Mediach(drive);
203 	return B_OK;
204 }
205 
206 static status_t
207 get_drive_parameters(uint8 drive, drive_parameters *parameters)
208 {
209 	status_t err;
210 	err = read_bpb(drive, &parameters->bpb);
211 	TRACE(("get_drive_parameters: get_bpb: 0x%08lx\n", err));
212 	TRACE(("get_drive_parameters: bpb: %04x %04x %04x %04x %04x %04x %04x %04x %04x \n",
213 			parameters->bpb.recsiz, parameters->bpb.clsiz,
214 			parameters->bpb.clsizb, parameters->bpb.rdlen,
215 			parameters->bpb.fsiz, parameters->bpb.fatrec,
216 			parameters->bpb.datrec, parameters->bpb.numcl,
217 			parameters->bpb.bflags));
218 
219 #if 0
220 	// fill drive_parameters structure with useful values
221 	parameters->parameters_size = kParametersSizeVersion1;
222 	parameters->flags = 0;
223 	parameters->cylinders = (((regs.ecx & 0xc0) << 2) | ((regs.ecx >> 8) & 0xff)) + 1;
224 	parameters->heads = ((regs.edx >> 8) & 0xff) + 1;
225 		// heads and cylinders start counting from 0
226 	parameters->sectors_per_track = regs.ecx & 0x3f;
227 	parameters->sectors = parameters->cylinders * parameters->heads
228 		* parameters->sectors_per_track;
229 	parameters->bytes_per_sector = 512;
230 #endif
231 	return B_OK;
232 }
233 
234 
235 #if 0
236 /** parse EDD 3.0 drive path information */
237 
238 static status_t
239 fill_disk_identifier_v3(disk_identifier &disk, const drive_parameters &parameters)
240 {
241 	if (parameters.parameters_size < kParametersSizeVersion3
242 		|| parameters.device_path_signature != kDevicePathSignature)
243 		return B_BAD_TYPE;
244 
245 	// parse host bus
246 
247 	if (!strncmp(parameters.host_bus, "PCI", 3)) {
248 		disk.bus_type = PCI_BUS;
249 
250 		disk.bus.pci.bus = parameters.interface.pci.bus;
251 		disk.bus.pci.slot = parameters.interface.pci.slot;
252 		disk.bus.pci.function = parameters.interface.pci.function;
253 	} else if (!strncmp(parameters.host_bus, "ISA", 3)) {
254 		disk.bus_type = LEGACY_BUS;
255 
256 		disk.bus.legacy.base_address = parameters.interface.legacy.base_address;
257 		dprintf("legacy base address %x\n", disk.bus.legacy.base_address);
258 	} else {
259 		dprintf("unknown host bus \"%s\"\n", parameters.host_bus);
260 		return B_BAD_DATA;
261 	}
262 
263 	// parse interface
264 
265 	if (!strncmp(parameters.interface_type, "ATA", 3)) {
266 		disk.device_type = ATA_DEVICE;
267 		disk.device.ata.master = !parameters.device.ata.slave;
268 		dprintf("ATA device, %s\n", disk.device.ata.master ? "master" : "slave");
269 	} else if (!strncmp(parameters.interface_type, "ATAPI", 3)) {
270 		disk.device_type = ATAPI_DEVICE;
271 		disk.device.atapi.master = !parameters.device.ata.slave;
272 		disk.device.atapi.logical_unit = parameters.device.atapi.logical_unit;
273 	} else if (!strncmp(parameters.interface_type, "SCSI", 3)) {
274 		disk.device_type = SCSI_DEVICE;
275 		disk.device.scsi.logical_unit = parameters.device.scsi.logical_unit;
276 	} else if (!strncmp(parameters.interface_type, "USB", 3)) {
277 		disk.device_type = USB_DEVICE;
278 		disk.device.usb.tbd = parameters.device.usb.tbd;
279 	} else if (!strncmp(parameters.interface_type, "1394", 3)) {
280 		disk.device_type = FIREWIRE_DEVICE;
281 		disk.device.firewire.guid = parameters.device.firewire.guid;
282 	} else if (!strncmp(parameters.interface_type, "FIBRE", 3)) {
283 		disk.device_type = FIBRE_DEVICE;
284 		disk.device.fibre.wwd = parameters.device.fibre.wwd;
285 	} else {
286 		dprintf("unknown interface type \"%s\"\n", parameters.interface_type);
287 		return B_BAD_DATA;
288 	}
289 
290 	return B_OK;
291 }
292 
293 
294 /** EDD 2.0 drive table information */
295 
296 static status_t
297 fill_disk_identifier_v2(disk_identifier &disk, const drive_parameters &parameters)
298 {
299 	if (parameters.device_table.segment == 0xffff
300 		&& parameters.device_table.offset == 0xffff)
301 		return B_BAD_TYPE;
302 
303 	device_table *table = (device_table *)LINEAR_ADDRESS(parameters.device_table.segment,
304 		parameters.device_table.offset);
305 
306 	disk.bus_type = LEGACY_BUS;
307 	disk.bus.legacy.base_address = table->base_address;
308 
309 	disk.device_type = ATA_DEVICE;
310 	disk.device.ata.master = !table->is_slave;
311 
312 	return B_OK;
313 }
314 #endif
315 
316 static off_t
317 get_next_check_sum_offset(int32 index, off_t maxSize)
318 {
319 	// The boot block often contains the disk super block, and should be
320 	// unique enough for most cases
321 	if (index < 2)
322 		return index * 512;
323 
324 	// Try some data in the first part of the drive
325 	if (index < 4)
326 		return (maxSize >> 10) + index * 2048;
327 
328 	// Some random value might do
329 	return ((system_time() + index) % (maxSize >> 9)) * 512;
330 }
331 
332 
333 /**	Computes a check sum for the specified block.
334  *	The check sum is the sum of all data in that block interpreted as an
335  *	array of uint32 values.
336  *	Note, this must use the same method as the one used in kernel/fs/vfs_boot.cpp.
337  */
338 
339 static uint32
340 compute_check_sum(BlockHandle *drive, off_t offset)
341 {
342 	char buffer[512];
343 	ssize_t bytesRead = drive->ReadAt(NULL, offset, buffer, sizeof(buffer));
344 	if (bytesRead < B_OK)
345 		return 0;
346 
347 	if (bytesRead < (ssize_t)sizeof(buffer))
348 		memset(buffer + bytesRead, 0, sizeof(buffer) - bytesRead);
349 
350 	uint32 *array = (uint32 *)buffer;
351 	uint32 sum = 0;
352 
353 	for (uint32 i = 0; i < (bytesRead + sizeof(uint32) - 1) / sizeof(uint32); i++) {
354 		sum += array[i];
355 	}
356 
357 	return sum;
358 }
359 
360 
361 static void
362 find_unique_check_sums(NodeList *devices)
363 {
364 	NodeIterator iterator = devices->GetIterator();
365 	Node *device;
366 	int32 index = 0;
367 	off_t minSize = 0;
368 	const int32 kMaxTries = 200;
369 
370 	while (index < kMaxTries) {
371 		bool clash = false;
372 
373 		iterator.Rewind();
374 
375 		while ((device = iterator.Next()) != NULL) {
376 			BlockHandle *drive = (BlockHandle *)device;
377 #if 0
378 			// there is no RTTI in the boot loader...
379 			BlockHandle *drive = dynamic_cast<BlockHandle *>(device);
380 			if (drive == NULL)
381 				continue;
382 #endif
383 
384 			// TODO: currently, we assume that the BIOS provided us with unique
385 			//	disk identifiers... hopefully this is a good idea
386 			if (drive->Identifier().device_type != UNKNOWN_DEVICE)
387 				continue;
388 
389 			if (minSize == 0 || drive->Size() < minSize)
390 				minSize = drive->Size();
391 
392 			// check for clashes
393 
394 			NodeIterator compareIterator = devices->GetIterator();
395 			while ((device = compareIterator.Next()) != NULL) {
396 				BlockHandle *compareDrive = (BlockHandle *)device;
397 
398 				if (compareDrive == drive
399 					|| compareDrive->Identifier().device_type != UNKNOWN_DEVICE)
400 					continue;
401 
402 				if (!memcmp(&drive->Identifier(), &compareDrive->Identifier(),
403 						sizeof(disk_identifier))) {
404 					clash = true;
405 					break;
406 				}
407 			}
408 
409 			if (clash)
410 				break;
411 		}
412 
413 		if (!clash) {
414 			// our work here is done.
415 			return;
416 		}
417 
418 		// add a new block to the check sums
419 
420 		off_t offset = get_next_check_sum_offset(index, minSize);
421 		int32 i = index % NUM_DISK_CHECK_SUMS;
422 		iterator.Rewind();
423 
424 		while ((device = iterator.Next()) != NULL) {
425 			BlockHandle *drive = (BlockHandle *)device;
426 
427 			disk_identifier& disk = drive->Identifier();
428 			disk.device.unknown.check_sums[i].offset = offset;
429 			disk.device.unknown.check_sums[i].sum = compute_check_sum(drive, offset);
430 
431 			TRACE(("disk %x, offset %Ld, sum %lu\n", drive->DriveID(), offset,
432 				disk.device.unknown.check_sums[i].sum));
433 		}
434 
435 		index++;
436 	}
437 
438 	// If we get here, we couldn't find a way to differentiate all disks from each other.
439 	// It's very likely that one disk is an exact copy of the other, so there is nothing
440 	// we could do, anyway.
441 
442 	dprintf("Could not make BIOS drives unique! Might boot from the wrong disk...\n");
443 }
444 
445 
446 static status_t
447 add_block_devices(NodeList *devicesList, bool identifierMissing)
448 {
449 	int32 map;
450 	uint8 driveID;
451 	uint8 driveCount = 0;
452 
453 	if (sBlockDevicesAdded)
454 		return B_OK;
455 
456 	if (init_xhdi() >= B_OK) {
457 		uint16 major;
458 		uint16 minor;
459 		uint32 blocksize;
460 		uint32 blocksize2;
461 		uint32 blocks;
462 		uint32 devflags;
463 		uint32 lastacc;
464 		char product[33];
465 		int32 err;
466 
467 		map = XHDrvMap();
468 		dprintf("XDrvMap() 0x%08lx\n", map);
469 		// sadly XDrvmap() has the same issues as XBIOS, it only lists known partitions.
470 		// so we just iterate on each major and try to see if there is something.
471 		for (driveID = 0; driveID < 32; driveID++) {
472 			uint32 startsect;
473 			err = XHInqDev(driveID, &major, &minor, &startsect, NULL);
474 			if (err < 0) {
475 				;//dprintf("XHInqDev(%d) error %d\n", driveID, err);
476 			} else {
477 				dprintf("XHInqDev(%d): (%d,%d):%d\n", driveID, major, minor, startsect);
478 			}
479 		}
480 
481 		product[32] = '\0';
482 		for (major = 0; major < 256; major++) {
483 			if (major == 64) // we don't want floppies
484 				continue;
485 			if (major > 23 && major != 64) // extensions and non-standard stuff... skip for speed.
486 				break;
487 
488 			for (minor = 0; minor < 255; minor++) {
489 				if (minor && (major < 8 || major >15))
490 					break; // minor only used for the SCSI LUN AFAIK.
491 				if (minor > 15) // are more used at all ?
492 					break;
493 
494 				product[0] = '\0';
495 				blocksize = 0;
496 				blocksize2 = 0;
497 #if 0
498 				err = XHLastAccess(major, minor, &lastacc);
499 				if (err < 0) {
500 					;//dprintf("XHLastAccess(%d,%d) error %d\n", major, minor, err);
501 				} else
502 					dprintf("XHLastAccess(%d,%d): %ld\n", major, minor, lastacc);
503 //continue;
504 #endif
505 				// we can pass NULL pointers but just to play safe we don't.
506 				err = XHInqTarget(major, minor, &blocksize, &devflags, product);
507 				if (err < 0) {
508 					dprintf("XHInqTarget(%d,%d) error %d\n", major, minor, err);
509 					continue;
510 				}
511 				err = XHGetCapacity(major, minor, &blocks, &blocksize2);
512 				if (err < 0) {
513 					//dprintf("XHGetCapacity(%d,%d) error %d\n", major, minor, err);
514 					continue;
515 				}
516 
517 				if (blocksize == 0) {
518 					dprintf("XHDI: blocksize for (%d,%d) is 0!\n", major, minor);
519 				}
520 				//dprintf("XHDI: (%d,%d) blocksize1 %ld blocksize2 %ld\n", major, minor, blocksize, blocksize2);
521 
522 				dprintf("XHDI(%d,%d): blksize %d, blocks %d, flags 0x%08lx, '%s'\n", major, minor, blocksize, blocks, devflags, product);
523 				driveID = (uint8)major;
524 
525 				//if (driveID == gBootDriveID)
526 				//	continue;
527 //continue;
528 
529 				BlockHandle *drive = new(nothrow) XHDIDrive(driveID, major, minor);
530 				if (drive->InitCheck() != B_OK) {
531 					dprintf("could not add drive (%d,%d)\n", major, minor);
532 					delete drive;
533 					continue;
534 				}
535 
536 				devicesList->Add(drive);
537 				driveCount++;
538 
539 				if (drive->FillIdentifier() != B_OK)
540 					identifierMissing = true;
541 			}
542 		}
543 	}
544 	if (!driveCount) { // try to fallback to BIOS XXX: use MetaDOS
545 		map = Drvmap();
546 		dprintf("Drvmap(): 0x%08lx\n", map);
547 		for (driveID = 0; driveID < 32; driveID++) {
548 			bool present = map & 0x1;
549 			map >>= 1;
550 			if (!present)
551 				continue;
552 
553 			if (driveID == gBootDriveID)
554 				continue;
555 
556 			BlockHandle *drive = new(nothrow) BlockHandle(driveID);
557 			if (drive->InitCheck() != B_OK) {
558 				dprintf("could not add drive %u\n", driveID);
559 				delete drive;
560 				continue;
561 			}
562 
563 			devicesList->Add(drive);
564 			driveCount++;
565 
566 			if (drive->FillIdentifier() != B_OK)
567 				identifierMissing = true;
568 		}
569 	}
570 	dprintf("number of drives: %d\n", driveCount);
571 
572 	if (identifierMissing) {
573 		// we cannot distinguish between all drives by identifier, we need
574 		// compute checksums for them
575 		find_unique_check_sums(devicesList);
576 	}
577 
578 	sBlockDevicesAdded = true;
579 	return B_OK;
580 }
581 
582 
583 //	#pragma mark -
584 
585 
586 BlockHandle::BlockHandle(int handle)
587 	: Handle(handle)
588 	, fSize(0LL)
589 	, fBlockSize(0)
590 	, fHasParameters(false)
591 
592 {
593 	TRACE(("BlockHandle::%s(): drive ID %u\n", __FUNCTION__, fHandle));
594 }
595 
596 
597 BlockHandle::~BlockHandle()
598 {
599 }
600 
601 
602 ssize_t
603 BlockHandle::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize)
604 {
605 	ssize_t ret;
606 	uint32 offset = pos % fBlockSize;
607 	pos /= fBlockSize;
608 	TRACE(("BlockHandle::%s: (%d) %Ld, %d\n", __FUNCTION__, fHandle, pos, bufferSize));
609 
610 	uint32 blocksLeft = (bufferSize + offset + fBlockSize - 1) / fBlockSize;
611 	int32 totalBytesRead = 0;
612 
613 	//TRACE(("BIOS reads %lu bytes from %Ld (offset = %lu)\n",
614 	//	blocksLeft * fBlockSize, pos * fBlockSize, offset));
615 
616 	// read partial block
617 	if (offset) {
618 		ret = ReadBlocks(gScratchBuffer, pos, 1);
619 		if (ret < 0)
620 			return ret;
621 		totalBytesRead += fBlockSize - offset;
622 		memcpy(buffer, gScratchBuffer + offset, totalBytesRead);
623 
624 	}
625 
626 	uint32 scratchSize = SCRATCH_SIZE / fBlockSize;
627 
628 	while (blocksLeft > 0) {
629 		uint32 blocksRead = blocksLeft;
630 		if (blocksRead > scratchSize)
631 			blocksRead = scratchSize;
632 
633 		ret = ReadBlocks(gScratchBuffer, pos, blocksRead);
634 		if (ret < 0)
635 			return ret;
636 
637 		uint32 bytesRead = fBlockSize * blocksRead - offset;
638 		// copy no more than bufferSize bytes
639 		if (bytesRead > bufferSize)
640 			bytesRead = bufferSize;
641 
642 		memcpy(buffer, (void *)(gScratchBuffer + offset), bytesRead);
643 		pos += blocksRead;
644 		offset = 0;
645 		blocksLeft -= blocksRead;
646 		bufferSize -= bytesRead;
647 		buffer = (void *)((addr_t)buffer + bytesRead);
648 		totalBytesRead += bytesRead;
649 	}
650 
651 	//TRACE(("BlockHandle::%s: %d bytes read\n", __FUNCTION__, totalBytesRead));
652 	return totalBytesRead;
653 }
654 
655 
656 ssize_t
657 BlockHandle::WriteAt(void *cookie, off_t pos, const void *buffer, size_t bufferSize)
658 {
659 	// we don't have to know how to write
660 	return B_NOT_ALLOWED;
661 }
662 
663 
664 ssize_t
665 BlockHandle::ReadBlocks(void *buffer, off_t first, int32 count)
666 {
667 	return B_NOT_ALLOWED;
668 }
669 
670 
671 status_t
672 BlockHandle::FillIdentifier()
673 {
674 	return B_NOT_ALLOWED;
675 }
676 
677 //	#pragma mark -
678 
679 /*
680  * BIOS based disk access.
681  * Only for fallback from missing XHDI.
682  * XXX: This is broken!
683  * XXX: check for physical drives in PUN_INFO
684  * XXX: at least try to use MetaDOS calls instead.
685  */
686 
687 
688 FloppyDrive::FloppyDrive(int handle)
689 	: BlockHandle(handle)
690 {
691 	TRACE(("FloppyDrive::%s(%d)\n", __FUNCTION__, fHandle));
692 
693 	/* first check if the drive exists */
694 	/* note floppy B can be reported present anyway... */
695 	uint32 map = Drvmap();
696 	if (!(map & (1 << fHandle))) {
697 		fSize = 0LL;
698 		return;
699 	}
700 	//XXX: check size
701 
702 	if (get_drive_parameters(fHandle, &fParameters) != B_OK) {
703 		dprintf("getting drive parameters for: %u failed!\n", fHandle);
704 		return;
705 	}
706 	// XXX: probe! this is a std 1.44kB floppy
707 	fBlockSize = 512;
708 	fParameters.sectors = 1440 * 1024 / 512;
709 	fParameters.sectors_per_track = 18;
710 	fParameters.heads = 2;
711 	fSize = fParameters.sectors * fBlockSize;
712 	fHasParameters = false;
713 /*
714 	parameters->sectors_per_track = 9;
715 	parameters->cylinders = (1440/2) / (9*2);
716 	parameters->heads = 2;
717 	parameters->sectors = parameters->cylinders * parameters->heads
718 	* parameters->sectors_per_track;
719 	*/
720 #if 0
721 	if (get_ext_drive_parameters(driveID, &fParameters) != B_OK) {
722 		// old style CHS support
723 
724 		if (get_drive_parameters(driveID, &fParameters) != B_OK) {
725 			dprintf("getting drive parameters for: %u failed!\n", fDriveID);
726 			return;
727 		}
728 
729 		TRACE(("  cylinders: %lu, heads: %lu, sectors: %lu, bytes_per_sector: %u\n",
730 			fParameters.cylinders, fParameters.heads, fParameters.sectors_per_track,
731 			fParameters.bytes_per_sector));
732 		TRACE(("  total sectors: %Ld\n", fParameters.sectors));
733 
734 		fBlockSize = 512;
735 		fSize = fParameters.sectors * fBlockSize;
736 		fLBA = false;
737 		fHasParameters = false;
738 	} else {
739 		TRACE(("size: %x\n", fParameters.parameters_size));
740 		TRACE(("drive_path_signature: %x\n", fParameters.device_path_signature));
741 		TRACE(("host bus: \"%s\", interface: \"%s\"\n", fParameters.host_bus,
742 			fParameters.interface_type));
743 		TRACE(("cylinders: %lu, heads: %lu, sectors: %lu, bytes_per_sector: %u\n",
744 			fParameters.cylinders, fParameters.heads, fParameters.sectors_per_track,
745 			fParameters.bytes_per_sector));
746 		TRACE(("total sectors: %Ld\n", fParameters.sectors));
747 
748 		fBlockSize = fParameters.bytes_per_sector;
749 		fSize = fParameters.sectors * fBlockSize;
750 		fLBA = true;
751 		fHasParameters = true;
752 	}
753 #endif
754 }
755 
756 
757 FloppyDrive::~FloppyDrive()
758 {
759 }
760 
761 
762 status_t
763 FloppyDrive::FillIdentifier()
764 {
765 	TRACE(("FloppyDrive::%s: (%d)\n", __FUNCTION__, fHandle));
766 #if 0
767 	if (HasParameters()) {
768 		// try all drive_parameters versions, beginning from the most informative
769 
770 #if 0
771 		if (fill_disk_identifier_v3(fIdentifier, fParameters) == B_OK)
772 			return B_OK;
773 
774 		if (fill_disk_identifier_v2(fIdentifier, fParameters) == B_OK)
775 			return B_OK;
776 #else
777 		// TODO: the above version is the correct one - it's currently
778 		//		disabled, as the kernel boot code only supports the
779 		//		UNKNOWN_BUS/UNKNOWN_DEVICE way to find the correct boot
780 		//		device.
781 		if (fill_disk_identifier_v3(fIdentifier, fParameters) != B_OK)
782 			fill_disk_identifier_v2(fIdentifier, fParameters);
783 
784 #endif
785 
786 		// no interesting information, we have to fall back to the default
787 		// unknown interface/device type identifier
788 	}
789 
790 	fIdentifier.bus_type = UNKNOWN_BUS;
791 	fIdentifier.device_type = UNKNOWN_DEVICE;
792 	fIdentifier.device.unknown.size = Size();
793 
794 	for (int32 i = 0; i < NUM_DISK_CHECK_SUMS; i++) {
795 		fIdentifier.device.unknown.check_sums[i].offset = -1;
796 		fIdentifier.device.unknown.check_sums[i].sum = 0;
797 	}
798 #endif
799 
800 	return B_ERROR;
801 }
802 
803 
804 static void
805 hexdump(uint8 *buf, uint32 offset)
806 {
807 // hexdump
808 
809 	for (int i = 0; i < 512; i++) {
810 		if ((i % 16) == 0)
811 			TRACE(("%08lx ", offset+i));
812 		if ((i % 16) == 8)
813 		TRACE((" "));
814 		TRACE((" %02x", buf[i]));
815 
816 		if ((i % 16) == 15)
817 			TRACE(("\n"));
818 	}
819 }
820 
821 
822 ssize_t
823 FloppyDrive::ReadBlocks(void *buffer, off_t first, int32 count)
824 {
825 	int sectorsPerBlocks = (fBlockSize / 512);
826 	int sectorsPerTrack = fParameters.sectors_per_track;
827 	int heads = fParameters.heads;
828 	int32 ret;
829 	//TRACE(("FloppyDrive::%s(%Ld,%ld) (%d)\n", __FUNCTION__, first, count, fHandle));
830 	// force single sector reads to avoid crossing track boundaries
831 	for (int i = 0; i < count; i++) {
832 		uint8 *buf = (uint8 *)buffer;
833 		buf += i * fBlockSize;
834 
835 		int16 track, side, sect;
836 		sect = (first + i) * sectorsPerBlocks;
837 		track = sect / (sectorsPerTrack * heads);
838 		side = (sect / sectorsPerTrack) % heads;
839 		sect %= sectorsPerTrack;
840 		sect++; // 1-based
841 
842 
843 		/*
844 		  TRACE(("FloppyDrive::%s: THS: %d %d %d\n",
845 		  __FUNCTION__, track, side, sect));
846 		*/
847 		ret = Floprd(buf, 0L, fHandle, sect, track, side, 1);
848 		if (ret < 0)
849 			return toserror(ret);
850 		//if (first >= 1440)
851 		//hexdump(buf, (uint32)(first+i)*512);
852 	}
853 
854 	return count;
855 }
856 
857 
858 //	#pragma mark -
859 
860 /*
861  * BIOS based disk access.
862  * Only for fallback from missing XHDI.
863  * XXX: This is broken!
864  * XXX: check for physical drives in PUN_INFO
865  * XXX: at least try to use MetaDOS calls instead.
866  */
867 
868 
869 BIOSDrive::BIOSDrive(int handle)
870 	: BlockHandle(handle)
871 {
872 	TRACE(("BIOSDrive::%s(%d)\n", __FUNCTION__, fHandle));
873 
874 	/* first check if the drive exists */
875 	/* note floppy B can be reported present anyway... */
876 	uint32 map = Drvmap();
877 	if (!(map & (1 << fHandle))) {
878 		fSize = 0LL;
879 		return;
880 	}
881 	//XXX: check size
882 
883 	if (get_drive_parameters(fHandle, &fParameters) != B_OK) {
884 		dprintf("getting drive parameters for: %u failed!\n", fHandle);
885 		return;
886 	}
887 	fBlockSize = 512;
888 	fSize = fParameters.sectors * fBlockSize;
889 	fHasParameters = false;
890 
891 #if 0
892 	if (get_ext_drive_parameters(driveID, &fParameters) != B_OK) {
893 		// old style CHS support
894 
895 		if (get_drive_parameters(driveID, &fParameters) != B_OK) {
896 			dprintf("getting drive parameters for: %u failed!\n", fDriveID);
897 			return;
898 		}
899 
900 		TRACE(("  cylinders: %lu, heads: %lu, sectors: %lu, bytes_per_sector: %u\n",
901 			fParameters.cylinders, fParameters.heads, fParameters.sectors_per_track,
902 			fParameters.bytes_per_sector));
903 		TRACE(("  total sectors: %Ld\n", fParameters.sectors));
904 
905 		fBlockSize = 512;
906 		fSize = fParameters.sectors * fBlockSize;
907 		fLBA = false;
908 		fHasParameters = false;
909 	} else {
910 		TRACE(("size: %x\n", fParameters.parameters_size));
911 		TRACE(("drive_path_signature: %x\n", fParameters.device_path_signature));
912 		TRACE(("host bus: \"%s\", interface: \"%s\"\n", fParameters.host_bus,
913 			fParameters.interface_type));
914 		TRACE(("cylinders: %lu, heads: %lu, sectors: %lu, bytes_per_sector: %u\n",
915 			fParameters.cylinders, fParameters.heads, fParameters.sectors_per_track,
916 			fParameters.bytes_per_sector));
917 		TRACE(("total sectors: %Ld\n", fParameters.sectors));
918 
919 		fBlockSize = fParameters.bytes_per_sector;
920 		fSize = fParameters.sectors * fBlockSize;
921 		fLBA = true;
922 		fHasParameters = true;
923 	}
924 #endif
925 }
926 
927 
928 BIOSDrive::~BIOSDrive()
929 {
930 }
931 
932 
933 status_t
934 BIOSDrive::FillIdentifier()
935 {
936 	TRACE(("BIOSDrive::%s: (%d)\n", __FUNCTION__, fHandle));
937 #if 0
938 	if (HasParameters()) {
939 		// try all drive_parameters versions, beginning from the most informative
940 
941 #if 0
942 		if (fill_disk_identifier_v3(fIdentifier, fParameters) == B_OK)
943 			return B_OK;
944 
945 		if (fill_disk_identifier_v2(fIdentifier, fParameters) == B_OK)
946 			return B_OK;
947 #else
948 		// TODO: the above version is the correct one - it's currently
949 		//		disabled, as the kernel boot code only supports the
950 		//		UNKNOWN_BUS/UNKNOWN_DEVICE way to find the correct boot
951 		//		device.
952 		if (fill_disk_identifier_v3(fIdentifier, fParameters) != B_OK)
953 			fill_disk_identifier_v2(fIdentifier, fParameters);
954 
955 #endif
956 
957 		// no interesting information, we have to fall back to the default
958 		// unknown interface/device type identifier
959 	}
960 
961 	fIdentifier.bus_type = UNKNOWN_BUS;
962 	fIdentifier.device_type = UNKNOWN_DEVICE;
963 	fIdentifier.device.unknown.size = Size();
964 
965 	for (int32 i = 0; i < NUM_DISK_CHECK_SUMS; i++) {
966 		fIdentifier.device.unknown.check_sums[i].offset = -1;
967 		fIdentifier.device.unknown.check_sums[i].sum = 0;
968 	}
969 #endif
970 
971 	return B_ERROR;
972 }
973 
974 
975 ssize_t
976 BIOSDrive::ReadBlocks(void *buffer, off_t first, int32 count)
977 {
978 	int sectorsPerBlocks = (fBlockSize / 256);
979 	int32 ret;
980 	TRACE(("BIOSDrive::%s(%Ld,%ld) (%d)\n", __FUNCTION__, first, count, fHandle));
981 	// XXX: check for AHDI 3.0 before using long recno!!!
982 	ret = Rwabs(RW_READ | RW_NOTRANSLATE, buffer, sectorsPerBlocks, -1, fHandle, first * sectorsPerBlocks);
983 	if (ret < 0)
984 		return toserror(ret);
985 	return ret;
986 }
987 
988 
989 //	#pragma mark -
990 
991 /*
992  * XHDI based devices
993  */
994 
995 
996 XHDIDrive::XHDIDrive(int handle, uint16 major, uint16 minor)
997 	: BlockHandle(handle)
998 {
999 	/* first check if the drive exists */
1000 	int32 err;
1001 	uint32 devflags;
1002 	uint32 blocks;
1003 	uint32 blocksize;
1004 	char product[33];
1005 
1006 	fMajor = major;
1007 	fMinor = minor;
1008 	TRACE(("XHDIDrive::%s(%d, %d, %d)\n", __FUNCTION__, handle, fMajor, fMinor));
1009 
1010 	product[32] = '\0';
1011 	err = XHInqTarget(major, minor, &fBlockSize, &devflags, product);
1012 	if (err < 0)
1013 		return;
1014 	//XXX: check size
1015 	err = XHGetCapacity(major, minor, &blocks, &blocksize);
1016 	if (err < 0)
1017 		return;
1018 
1019 	if (fBlockSize == 0)
1020 		fBlockSize = 512;
1021 
1022 	fSize = blocks * fBlockSize;
1023 	fHasParameters = false;
1024 #if 0
1025 	if (get_drive_parameters(fHandle, &fParameters) != B_OK) {
1026 		dprintf("getting drive parameters for: %u failed!\n", fHandle);
1027 		return;
1028 	}
1029 	fBlockSize = 512;
1030 	fSize = fParameters.sectors * fBlockSize;
1031 	fHasParameters = false;
1032 #endif
1033 #if 0
1034 	if (get_ext_drive_parameters(driveID, &fParameters) != B_OK) {
1035 		// old style CHS support
1036 
1037 		if (get_drive_parameters(driveID, &fParameters) != B_OK) {
1038 			dprintf("getting drive parameters for: %u failed!\n", fDriveID);
1039 			return;
1040 		}
1041 
1042 		TRACE(("  cylinders: %lu, heads: %lu, sectors: %lu, bytes_per_sector: %u\n",
1043 			fParameters.cylinders, fParameters.heads, fParameters.sectors_per_track,
1044 			fParameters.bytes_per_sector));
1045 		TRACE(("  total sectors: %Ld\n", fParameters.sectors));
1046 
1047 		fBlockSize = 512;
1048 		fSize = fParameters.sectors * fBlockSize;
1049 		fLBA = false;
1050 		fHasParameters = false;
1051 	} else {
1052 		TRACE(("size: %x\n", fParameters.parameters_size));
1053 		TRACE(("drive_path_signature: %x\n", fParameters.device_path_signature));
1054 		TRACE(("host bus: \"%s\", interface: \"%s\"\n", fParameters.host_bus,
1055 			fParameters.interface_type));
1056 		TRACE(("cylinders: %lu, heads: %lu, sectors: %lu, bytes_per_sector: %u\n",
1057 			fParameters.cylinders, fParameters.heads, fParameters.sectors_per_track,
1058 			fParameters.bytes_per_sector));
1059 		TRACE(("total sectors: %Ld\n", fParameters.sectors));
1060 
1061 		fBlockSize = fParameters.bytes_per_sector;
1062 		fSize = fParameters.sectors * fBlockSize;
1063 		fLBA = true;
1064 		fHasParameters = true;
1065 	}
1066 #endif
1067 }
1068 
1069 
1070 XHDIDrive::~XHDIDrive()
1071 {
1072 }
1073 
1074 
1075 status_t
1076 XHDIDrive::FillIdentifier()
1077 {
1078 	TRACE(("XHDIDrive::%s: (%d,%d)\n", __FUNCTION__, fMajor, fMinor));
1079 
1080 	fIdentifier.bus_type = UNKNOWN_BUS;
1081 	fIdentifier.device_type = UNKNOWN_DEVICE;
1082 	fIdentifier.device.unknown.size = Size();
1083 #if 0
1084 	// cf. http://toshyp.atari.org/010008.htm#XHDI-Terminologie
1085 	if (fMajor >= 8 && fMajor <= 15) { // scsi
1086 		fIdentifier.device_type = SCSI_DEVICE;
1087 		fIdentifier.device.scsi.logical_unit = fMinor;
1088 		//XXX: where am I supposed to put the ID ???
1089 	}
1090 #endif
1091 
1092 	for (int32 i = 0; i < NUM_DISK_CHECK_SUMS; i++) {
1093 		fIdentifier.device.unknown.check_sums[i].offset = -1;
1094 		fIdentifier.device.unknown.check_sums[i].sum = 0;
1095 	}
1096 
1097 	return B_ERROR;
1098 }
1099 
1100 
1101 ssize_t
1102 XHDIDrive::ReadBlocks(void *buffer, off_t first, int32 count)
1103 {
1104 	int sectorsPerBlock = (fBlockSize / 256);
1105 	int32 ret;
1106 	uint16 flags = RW_READ;
1107 	TRACE(("XHDIDrive::%s(%Ld, %d) (%d,%d)\n", __FUNCTION__, first, count, fMajor, fMinor));
1108 	ret = XHReadWrite(fMajor, fMinor, flags, (uint32)first, (uint16)count, buffer);
1109 	if (ret < 0)
1110 		return xhdierror(ret);
1111 	//TRACE(("XHReadWrite: %ld\n", ret));
1112 	/*
1113 	uint8 *b = (uint8 *)buffer;
1114 	int i = 0;
1115 	for (i = 0; i < 512; i+=16) {
1116 		TRACE(("[%8Ld+%3ld] %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
1117 			first, i, b[i], b[i+1], b[i+2], b[i+3], b[i+4], b[i+5], b[i+6], b[i+7],
1118 			b[i+8], b[i+9], b[i+10], b[i+11], b[i+12], b[i+13], b[i+14], b[i+15]));
1119 		//break;
1120 	}
1121 	*/
1122 	return ret;
1123 }
1124 
1125 
1126 
1127 
1128 //	#pragma mark -
1129 
1130 
1131 status_t
1132 platform_add_boot_device(struct stage2_args *args, NodeList *devicesList)
1133 {
1134 	TRACE(("boot drive ID: %x\n", gBootDriveID));
1135 	init_xhdi();
1136 
1137 	//XXX: FIXME
1138 	//BlockHandle *drive = new(nothrow) BlockHandle(gBootDriveID);
1139 	BlockHandle *drive;
1140 	switch (gBootDriveAPI) {
1141 		case ATARI_BOOT_DRVAPI_FLOPPY:
1142 			drive = new(nothrow) FloppyDrive(gBootDriveID);
1143 			break;
1144 			/*
1145 		case ATARI_BOOT_DRVAPI_XBIOS:
1146 			drive = new(nothrow) DMADrive(gBootDriveID);
1147 			break;
1148 			*/
1149 		case ATARI_BOOT_DRVAPI_XHDI:
1150 			drive = new(nothrow) XHDIDrive(gBootDriveID, gBootDriveID, 0);
1151 			break;
1152 		case ATARI_BOOT_DRVAPI_UNKNOWN:
1153 		default:
1154 			dprintf("unknown boot drive API %d\n", gBootDriveAPI);
1155 			return B_ERROR;
1156 			drive = new(nothrow) BIOSDrive(gBootDriveID);
1157 	}
1158 	if (drive->InitCheck() != B_OK) {
1159 		dprintf("no boot drive!\n");
1160 		return B_ERROR;
1161 	}
1162 
1163 	devicesList->Add(drive);
1164 
1165 	if (drive->FillIdentifier() != B_OK) {
1166 		// We need to add all block devices to give the kernel the possibility
1167 		// to find the right boot volume
1168 		add_block_devices(devicesList, true);
1169 	}
1170 
1171 	TRACE(("boot drive size: %Ld bytes\n", drive->Size()));
1172 	gKernelArgs.boot_volume.SetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE,
1173 		gBootedFromImage);
1174 
1175 	return B_OK;
1176 }
1177 
1178 
1179 status_t
1180 platform_get_boot_partition(struct stage2_args *args, Node *bootDevice,
1181 	NodeList *list, boot::Partition **_partition)
1182 {
1183 	BlockHandle *drive = static_cast<BlockHandle *>(bootDevice);
1184 	off_t offset = (off_t)gBootPartitionOffset * drive->BlockSize();
1185 
1186 	dprintf("boot partition offset: %Ld\n", offset);
1187 
1188 	NodeIterator iterator = list->GetIterator();
1189 	boot::Partition *partition = NULL;
1190 	while ((partition = (boot::Partition *)iterator.Next()) != NULL) {
1191 		TRACE(("partition offset = %Ld, size = %Ld\n", partition->offset, partition->size));
1192 		// search for the partition that contains the partition
1193 		// offset as reported by the BFS boot block
1194 		if (offset >= partition->offset
1195 			&& offset < partition->offset + partition->size) {
1196 			*_partition = partition;
1197 			return B_OK;
1198 		}
1199 	}
1200 
1201 	return B_ENTRY_NOT_FOUND;
1202 }
1203 
1204 
1205 status_t
1206 platform_add_block_devices(stage2_args *args, NodeList *devicesList)
1207 {
1208 	init_xhdi();
1209 	return add_block_devices(devicesList, false);
1210 }
1211 
1212 
1213 status_t
1214 platform_register_boot_device(Node *device)
1215 {
1216 	BlockHandle *drive = (BlockHandle *)device;
1217 
1218 #if 0
1219 	check_cd_boot(drive);
1220 #endif
1221 
1222 	gKernelArgs.boot_volume.SetInt64("boot drive number", drive->DriveID());
1223 	gKernelArgs.boot_volume.SetData(BOOT_VOLUME_DISK_IDENTIFIER, B_RAW_TYPE,
1224 		&drive->Identifier(), sizeof(disk_identifier));
1225 
1226 	return B_OK;
1227 }
1228 
1229