The boot loader now does its job and computes drive check sums at arbitrary blocks
in order to differentiate boot disks. The kernel still ignores this wisdom, though, bug #241 still remains open. This currently only works correctly if the boot menu is opened, anyway. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@16891 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2003-2005, Axel Dörfler, [email protected].
|
* Copyright 2003-2006, Axel Dörfler, [email protected].
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
//#define TRACE_DEVICES
|
#define TRACE_DEVICES
|
||||||
#ifdef TRACE_DEVICES
|
#ifdef TRACE_DEVICES
|
||||||
# define TRACE(x) dprintf x
|
# define TRACE(x) dprintf x
|
||||||
#else
|
#else
|
||||||
@@ -141,8 +141,12 @@ class BIOSDrive : public Node {
|
|||||||
|
|
||||||
uint32 BlockSize() const { return fBlockSize; }
|
uint32 BlockSize() const { return fBlockSize; }
|
||||||
|
|
||||||
|
status_t FillIdentifier();
|
||||||
|
|
||||||
bool HasParameters() const { return fHasParameters; }
|
bool HasParameters() const { return fHasParameters; }
|
||||||
const drive_parameters &Parameters() const { return fParameters; }
|
const drive_parameters &Parameters() const { return fParameters; }
|
||||||
|
|
||||||
|
disk_identifier &Identifier() { return fIdentifier; }
|
||||||
uint8 DriveID() const { return fDriveID; }
|
uint8 DriveID() const { return fDriveID; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -152,6 +156,7 @@ class BIOSDrive : public Node {
|
|||||||
uint32 fBlockSize;
|
uint32 fBlockSize;
|
||||||
bool fHasParameters;
|
bool fHasParameters;
|
||||||
drive_parameters fParameters;
|
drive_parameters fParameters;
|
||||||
|
disk_identifier fIdentifier;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -280,6 +285,7 @@ fill_disk_identifier_v3(disk_identifier &disk, const drive_parameters ¶meter
|
|||||||
disk.bus_type = LEGACY_BUS;
|
disk.bus_type = LEGACY_BUS;
|
||||||
|
|
||||||
disk.bus.legacy.base_address = parameters.interface.legacy.base_address;
|
disk.bus.legacy.base_address = parameters.interface.legacy.base_address;
|
||||||
|
dprintf("legacy base address %x\n", disk.bus.legacy.base_address);
|
||||||
} else {
|
} else {
|
||||||
dprintf("unknown host bus \"%s\"\n", parameters.host_bus);
|
dprintf("unknown host bus \"%s\"\n", parameters.host_bus);
|
||||||
return B_BAD_DATA;
|
return B_BAD_DATA;
|
||||||
@@ -290,6 +296,7 @@ fill_disk_identifier_v3(disk_identifier &disk, const drive_parameters ¶meter
|
|||||||
if (!strncmp(parameters.interface_type, "ATA", 3)) {
|
if (!strncmp(parameters.interface_type, "ATA", 3)) {
|
||||||
disk.device_type = ATA_DEVICE;
|
disk.device_type = ATA_DEVICE;
|
||||||
disk.device.ata.master = !parameters.device.ata.slave;
|
disk.device.ata.master = !parameters.device.ata.slave;
|
||||||
|
dprintf("ATA device, %s\n", disk.device.ata.master ? "master" : "slave");
|
||||||
} else if (!strncmp(parameters.interface_type, "ATAPI", 3)) {
|
} else if (!strncmp(parameters.interface_type, "ATAPI", 3)) {
|
||||||
disk.device_type = ATAPI_DEVICE;
|
disk.device_type = ATAPI_DEVICE;
|
||||||
disk.device.atapi.master = !parameters.device.ata.slave;
|
disk.device.atapi.master = !parameters.device.ata.slave;
|
||||||
@@ -337,6 +344,135 @@ fill_disk_identifier_v2(disk_identifier &disk, const drive_parameters ¶meter
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static off_t
|
||||||
|
get_next_check_sum_offset(int32 index, off_t maxSize)
|
||||||
|
{
|
||||||
|
// The boot block often contains the disk super block, and should be
|
||||||
|
// unique enough for most cases
|
||||||
|
if (index < 2)
|
||||||
|
return index * 512;
|
||||||
|
|
||||||
|
// Try some data in the first part of the drive
|
||||||
|
if (index < 4)
|
||||||
|
return (maxSize >> 10) + index * 2048;
|
||||||
|
|
||||||
|
// Some random value might do
|
||||||
|
return ((system_time() + index) % (maxSize >> 9)) * 512;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Computes a check sum for the specified block.
|
||||||
|
* The check sum is the sum of all data in that block interpreted as an
|
||||||
|
* array of uint32 values.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static uint32
|
||||||
|
compute_check_sum(BIOSDrive *drive, off_t offset)
|
||||||
|
{
|
||||||
|
char buffer[512];
|
||||||
|
ssize_t bytesRead = drive->ReadAt(NULL, offset, buffer, sizeof(buffer));
|
||||||
|
if (bytesRead < B_OK)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (bytesRead < (ssize_t)sizeof(buffer))
|
||||||
|
memset(buffer + bytesRead, 0, sizeof(buffer) - bytesRead);
|
||||||
|
|
||||||
|
uint32 *array = (uint32 *)buffer;
|
||||||
|
uint32 sum = 0;
|
||||||
|
|
||||||
|
for (uint32 i = 0; i < (bytesRead + sizeof(uint32) - 1) / sizeof(uint32); i++) {
|
||||||
|
sum += array[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
find_unique_check_sums(NodeList *devices)
|
||||||
|
{
|
||||||
|
NodeIterator iterator = devices->GetIterator();
|
||||||
|
Node *device;
|
||||||
|
int32 index = 0;
|
||||||
|
off_t minSize = 0;
|
||||||
|
const int32 kMaxTries = 200;
|
||||||
|
|
||||||
|
while (index < kMaxTries) {
|
||||||
|
bool clash = false;
|
||||||
|
|
||||||
|
iterator.Rewind();
|
||||||
|
|
||||||
|
while ((device = iterator.Next()) != NULL) {
|
||||||
|
BIOSDrive *drive = (BIOSDrive *)device;
|
||||||
|
#if 0
|
||||||
|
// there is no RTTI in the boot loader...
|
||||||
|
BIOSDrive *drive = dynamic_cast<BIOSDrive *>(device);
|
||||||
|
if (drive == NULL)
|
||||||
|
continue;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// TODO: currently, we assume that the BIOS provided us with unique
|
||||||
|
// disk identifiers... hopefully this is a good idea
|
||||||
|
if (drive->Identifier().device_type != UNKNOWN_DEVICE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (minSize == 0 || drive->Size() < minSize)
|
||||||
|
minSize = drive->Size();
|
||||||
|
|
||||||
|
// check for clashes
|
||||||
|
|
||||||
|
NodeIterator compareIterator = devices->GetIterator();
|
||||||
|
while ((device = compareIterator.Next()) != NULL) {
|
||||||
|
BIOSDrive *compareDrive = (BIOSDrive *)device;
|
||||||
|
|
||||||
|
if (compareDrive == drive
|
||||||
|
|| compareDrive->Identifier().device_type != UNKNOWN_DEVICE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!memcmp(&drive->Identifier(), &compareDrive->Identifier(),
|
||||||
|
sizeof(disk_identifier))) {
|
||||||
|
clash = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clash)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!clash) {
|
||||||
|
// our work here is done.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add a new block to the check sums
|
||||||
|
|
||||||
|
off_t offset = get_next_check_sum_offset(index, minSize);
|
||||||
|
int32 i = index % NUM_DISK_CHECK_SUMS;
|
||||||
|
iterator.Rewind();
|
||||||
|
|
||||||
|
while ((device = iterator.Next()) != NULL) {
|
||||||
|
BIOSDrive *drive = (BIOSDrive *)device;
|
||||||
|
|
||||||
|
disk_identifier& disk = drive->Identifier();
|
||||||
|
disk.device.unknown.check_sums[i].offset = offset;
|
||||||
|
disk.device.unknown.check_sums[i].sum = compute_check_sum(drive, offset);
|
||||||
|
|
||||||
|
TRACE(("disk %x, offset %Ld, sum %lu\n", drive->DriveID(), offset,
|
||||||
|
disk.device.unknown.check_sums[i].sum));
|
||||||
|
}
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we get here, we couldn't find a way to differentiate all disks from each other.
|
||||||
|
// It's very likely that one disk is an exact copy of the other, so there is nothing
|
||||||
|
// we could do, anyway.
|
||||||
|
|
||||||
|
dprintf("Could not make BIOS drives unique! Might boot from the wrong disk...\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#define DUMPED_BLOCK_SIZE 16
|
#define DUMPED_BLOCK_SIZE 16
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -440,8 +576,8 @@ BIOSDrive::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize)
|
|||||||
uint32 blocksLeft = (bufferSize + offset + fBlockSize - 1) / fBlockSize;
|
uint32 blocksLeft = (bufferSize + offset + fBlockSize - 1) / fBlockSize;
|
||||||
int32 totalBytesRead = 0;
|
int32 totalBytesRead = 0;
|
||||||
|
|
||||||
TRACE(("BIOS reads %lu bytes from %Ld (offset = %lu), drive %u\n",
|
//TRACE(("BIOS reads %lu bytes from %Ld (offset = %lu), drive %u\n",
|
||||||
blocksLeft * fBlockSize, pos * fBlockSize, offset, fDriveID));
|
// blocksLeft * fBlockSize, pos * fBlockSize, offset, fDriveID));
|
||||||
|
|
||||||
uint32 scratchSize = 24 * 1024 / fBlockSize;
|
uint32 scratchSize = 24 * 1024 / fBlockSize;
|
||||||
// maximum value allowed by Phoenix BIOS is 0x7f
|
// maximum value allowed by Phoenix BIOS is 0x7f
|
||||||
@@ -551,6 +687,35 @@ BIOSDrive::Size() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
BIOSDrive::FillIdentifier()
|
||||||
|
{
|
||||||
|
if (HasParameters()) {
|
||||||
|
// try all drive_parameters versions, beginning from the most informative
|
||||||
|
|
||||||
|
if (fill_disk_identifier_v3(fIdentifier, fParameters) == B_OK)
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
if (fill_disk_identifier_v2(fIdentifier, fParameters) == B_OK)
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
// no interesting information, we have to fall back to the default
|
||||||
|
// unknown interface/device type identifier
|
||||||
|
}
|
||||||
|
|
||||||
|
fIdentifier.bus_type = UNKNOWN_BUS;
|
||||||
|
fIdentifier.device_type = UNKNOWN_DEVICE;
|
||||||
|
fIdentifier.device.unknown.size = Size();
|
||||||
|
|
||||||
|
for (int32 i = 0; i < NUM_DISK_CHECK_SUMS; i++) {
|
||||||
|
fIdentifier.device.unknown.check_sums[i].offset = -1;
|
||||||
|
fIdentifier.device.unknown.check_sums[i].sum = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
@@ -565,6 +730,10 @@ platform_get_boot_device(struct stage2_args *args, Node **_device)
|
|||||||
return B_ERROR;
|
return B_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (drive->FillIdentifier() != B_OK) {
|
||||||
|
// TODO: we need to add all block devices!
|
||||||
|
}
|
||||||
|
|
||||||
TRACE(("drive size: %Ld bytes\n", drive->Size()));
|
TRACE(("drive size: %Ld bytes\n", drive->Size()));
|
||||||
gKernelArgs.boot_disk.booted_from_image = gBootedFromImage;
|
gKernelArgs.boot_disk.booted_from_image = gBootedFromImage;
|
||||||
|
|
||||||
@@ -608,6 +777,8 @@ platform_add_block_devices(stage2_args *args, NodeList *devicesList)
|
|||||||
|
|
||||||
dprintf("number of drives: %d\n", driveCount);
|
dprintf("number of drives: %d\n", driveCount);
|
||||||
|
|
||||||
|
bool identifierMissing = false;
|
||||||
|
|
||||||
for (int32 i = 0; i < driveCount; i++) {
|
for (int32 i = 0; i < driveCount; i++) {
|
||||||
uint8 driveID = i + 0x80;
|
uint8 driveID = i + 0x80;
|
||||||
if (driveID == gBootDriveID)
|
if (driveID == gBootDriveID)
|
||||||
@@ -621,6 +792,15 @@ platform_add_block_devices(stage2_args *args, NodeList *devicesList)
|
|||||||
}
|
}
|
||||||
|
|
||||||
devicesList->Add(drive);
|
devicesList->Add(drive);
|
||||||
|
|
||||||
|
if (drive->FillIdentifier() != B_OK)
|
||||||
|
identifierMissing = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (identifierMissing) {
|
||||||
|
// we cannot distinguish between all drives by identifier, we need
|
||||||
|
// compute checksums for them
|
||||||
|
find_unique_check_sums(devicesList);
|
||||||
}
|
}
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
@@ -630,36 +810,12 @@ platform_add_block_devices(stage2_args *args, NodeList *devicesList)
|
|||||||
status_t
|
status_t
|
||||||
platform_register_boot_device(Node *device)
|
platform_register_boot_device(Node *device)
|
||||||
{
|
{
|
||||||
disk_identifier &disk = gKernelArgs.boot_disk.identifier;
|
|
||||||
BIOSDrive *drive = (BIOSDrive *)device;
|
BIOSDrive *drive = (BIOSDrive *)device;
|
||||||
|
|
||||||
gKernelArgs.platform_args.boot_drive_number = gBootDriveID;
|
|
||||||
check_cd_boot(drive);
|
check_cd_boot(drive);
|
||||||
|
|
||||||
if (drive->HasParameters()) {
|
gKernelArgs.platform_args.boot_drive_number = drive->DriveID();
|
||||||
const drive_parameters ¶meters = drive->Parameters();
|
gKernelArgs.boot_disk.identifier = drive->Identifier();
|
||||||
|
|
||||||
// try all drive_parameters versions, beginning from the most informative
|
|
||||||
|
|
||||||
if (fill_disk_identifier_v3(disk, parameters) == B_OK)
|
|
||||||
return B_OK;
|
|
||||||
|
|
||||||
if (fill_disk_identifier_v2(disk, parameters) == B_OK)
|
|
||||||
return B_OK;
|
|
||||||
|
|
||||||
// no interesting information, we have to fall back to the default
|
|
||||||
// unknown interface/device type identifier
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToDo: register all other BIOS drives as well!
|
|
||||||
|
|
||||||
// not yet implemented correctly!!!
|
|
||||||
|
|
||||||
dprintf("legacy drive identification\n");
|
|
||||||
|
|
||||||
disk.bus_type = UNKNOWN_BUS;
|
|
||||||
disk.device_type = UNKNOWN_DEVICE;
|
|
||||||
disk.device.unknown.size = drive->Size();
|
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user