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