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
28extern uint8 gBootedFromImage;
29extern uint8 gBootDriveAPI; // ATARI_BOOT_DRIVE_API_*
30extern uint8 gBootDriveID;
31extern uint32 gBootPartitionOffset;
32
33#define SCRATCH_SIZE (2*4096)
34static uint8 gScratchBuffer[SCRATCH_SIZE];
35
36static const uint16 kParametersSizeVersion1 = sizeof(struct tos_bpb);
37static const uint16 kParametersSizeVersion2 = 0x1e;
38static const uint16 kParametersSizeVersion3 = 0x42;
39
40static const uint16 kDevicePathSignature = 0xbedd;
41
42//XXX clean this up!
43struct 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
96struct 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
105struct 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
115class 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
149class 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
163class 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
177class 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
192static bool sBlockDevicesAdded = false;
193
194
195static status_t
196read_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
207static status_t
208get_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
239static status_t
240fill_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
297static status_t
298fill_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
317static off_t
318get_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
340static uint32
341compute_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
362static void
363find_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
447static status_t
448add_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
587BlockHandle::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
598BlockHandle::~BlockHandle()
599{
600}
601
602
603ssize_t
604BlockHandle::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
657ssize_t
658BlockHandle::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
665ssize_t
666BlockHandle::ReadBlocks(void *buffer, off_t first, int32 count)
667{
668	return B_NOT_ALLOWED;
669}
670
671
672status_t
673BlockHandle::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
689FloppyDrive::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
758FloppyDrive::~FloppyDrive()
759{
760}
761
762
763status_t
764FloppyDrive::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
805static void
806hexdump(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
823ssize_t
824FloppyDrive::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
870BIOSDrive::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
929BIOSDrive::~BIOSDrive()
930{
931}
932
933
934status_t
935BIOSDrive::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
976ssize_t
977BIOSDrive::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
997XHDIDrive::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
1071XHDIDrive::~XHDIDrive()
1072{
1073}
1074
1075
1076status_t
1077XHDIDrive::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
1102ssize_t
1103XHDIDrive::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
1132status_t
1133platform_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
1191status_t
1192platform_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
1217status_t
1218platform_add_block_devices(stage2_args *args, NodeList *devicesList)
1219{
1220	init_xhdi();
1221	return add_block_devices(devicesList, false);
1222}
1223
1224
1225status_t
1226platform_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
1242void
1243platform_cleanup_devices()
1244{
1245}
1246