Kernel: Add support for ATA CHS addressing and cleanup code

I thought that I had an PC without LBA support so I implement support
for CHS. Turns out that my ATA device detection was broken and was no
device on that port and initialize data was just garbage.

Now that I added CHS, I guess I should just keep it in :)

Both ATA read and write are now combined into a single function to avoid
code duplication.
This commit is contained in:
Bananymous 2024-11-19 00:28:43 +02:00
parent 1de50a2a94
commit c07fd265f0
6 changed files with 75 additions and 56 deletions

View File

@ -14,7 +14,6 @@ namespace Kernel
Ext2_NoInodes,
Storage_Boundaries,
Storage_GPTHeader,
ATA_NoLBA,
ATA_AMNF,
ATA_TKZNF,
ATA_ABRT,

View File

@ -35,6 +35,8 @@ namespace Kernel
{}
BAN::ErrorOr<void> initialize();
BAN::ErrorOr<void> send_command(ATADevice&, uint64_t lba, uint64_t sector_count, bool write);
void select_device(bool is_secondary);
BAN::ErrorOr<DeviceType> identify(bool is_secondary, BAN::Span<uint16_t> buffer);

View File

@ -26,11 +26,12 @@ namespace Kernel
uint32_t words_per_sector() const { return m_sector_words; }
uint64_t sector_count() const { return m_lba_count; }
bool has_lba() const { return m_has_lba; }
BAN::StringView model() const { return m_model; }
BAN::StringView name() const { return m_name; }
BAN::StringView name() const override { return m_name; }
virtual dev_t rdev() const override { return m_rdev; }
dev_t rdev() const override { return m_rdev; }
protected:
ATABaseDevice();
@ -42,6 +43,7 @@ namespace Kernel
uint32_t m_command_set;
uint32_t m_sector_words;
uint64_t m_lba_count;
bool m_has_lba;
char m_model[41];
char m_name[4] {};

View File

@ -17,7 +17,6 @@ namespace Kernel
"Ext2 filesystem out of inodes",
"Attempted to access outside of device boundaries",
"Device has invalid GPT header",
"Device does not support LBA addressing",
"Address mark not found",
"Track zero not found",
"Aborted command",

View File

@ -245,29 +245,12 @@ namespace Kernel
LockGuard _(m_mutex);
if (lba < (1 << 28))
{
// LBA28
io_write(ATA_PORT_DRIVE_SELECT, 0xE0 | ((uint8_t)device.is_secondary() << 4) | ((lba >> 24) & 0x0F));
select_delay();
io_write(ATA_PORT_CONTROL, 0);
TRY(send_command(device, lba, sector_count, false));
io_write(ATA_PORT_SECTOR_COUNT, sector_count);
io_write(ATA_PORT_LBA0, (uint8_t)(lba >> 0));
io_write(ATA_PORT_LBA1, (uint8_t)(lba >> 8));
io_write(ATA_PORT_LBA2, (uint8_t)(lba >> 16));
io_write(ATA_PORT_COMMAND, ATA_COMMAND_READ_SECTORS);
for (uint32_t sector = 0; sector < sector_count; sector++)
{
block_until_irq();
read_buffer(ATA_PORT_DATA, (uint16_t*)buffer.data() + sector * device.words_per_sector(), device.words_per_sector());
}
}
else
for (uint32_t sector = 0; sector < sector_count; sector++)
{
// LBA48
ASSERT(false);
TRY(block_until_irq());
read_buffer(ATA_PORT_DATA, (uint16_t*)buffer.data() + sector * device.words_per_sector(), device.words_per_sector());
}
return {};
@ -282,33 +265,60 @@ namespace Kernel
LockGuard _(m_mutex);
if (lba < (1 << 28))
{
// LBA28
io_write(ATA_PORT_DRIVE_SELECT, 0xE0 | ((uint8_t)device.is_secondary() << 4) | ((lba >> 24) & 0x0F));
select_delay();
io_write(ATA_PORT_CONTROL, 0);
TRY(send_command(device, lba, sector_count, true));
io_write(ATA_PORT_SECTOR_COUNT, sector_count);
io_write(ATA_PORT_LBA0, (uint8_t)(lba >> 0));
io_write(ATA_PORT_LBA1, (uint8_t)(lba >> 8));
io_write(ATA_PORT_LBA2, (uint8_t)(lba >> 16));
io_write(ATA_PORT_COMMAND, ATA_COMMAND_WRITE_SECTORS);
for (uint32_t sector = 0; sector < sector_count; sector++)
{
write_buffer(ATA_PORT_DATA, (uint16_t*)buffer.data() + sector * device.words_per_sector(), device.words_per_sector());
block_until_irq();
}
}
else
for (uint32_t sector = 0; sector < sector_count; sector++)
{
// LBA48
ASSERT(false);
write_buffer(ATA_PORT_DATA, (uint16_t*)buffer.data() + sector * device.words_per_sector(), device.words_per_sector());
TRY(block_until_irq());
}
io_write(ATA_PORT_COMMAND, ATA_COMMAND_CACHE_FLUSH);
block_until_irq();
TRY(block_until_irq());
return {};
}
BAN::ErrorOr<void> ATABus::send_command(ATADevice& device, uint64_t lba, uint64_t sector_count, bool write)
{
uint8_t io_select = 0;
uint8_t io_lba0 = 0;
uint8_t io_lba1 = 0;
uint8_t io_lba2 = 0;
if (lba >= (1 << 28))
{
dwarnln("LBA48 addressing not supported");
return BAN::Error::from_errno(ENOTSUP);
}
else if (device.has_lba())
{
io_select = 0xE0 | ((uint8_t)device.is_secondary() << 4) | ((lba >> 24) & 0x0F);
io_lba0 = (lba >> 0) & 0xFF;
io_lba1 = (lba >> 8) & 0xFF;
io_lba2 = (lba >> 16) & 0xFF;
}
else
{
const uint8_t sector = (lba % 63) + 1;
const uint8_t head = (lba + 1 - sector) % (16 * 63) / 63;
const uint16_t cylinder = (lba + 1 - sector) / (16 * 63);
io_select = 0xA0 | ((uint8_t)device.is_secondary() << 4) | head;
io_lba0 = sector;
io_lba1 = (cylinder >> 0) & 0xFF;
io_lba2 = (cylinder >> 8) & 0xFF;
}
io_write(ATA_PORT_DRIVE_SELECT, io_select);
select_delay();
io_write(ATA_PORT_CONTROL, 0);
io_write(ATA_PORT_SECTOR_COUNT, sector_count);
io_write(ATA_PORT_LBA0, io_lba0);
io_write(ATA_PORT_LBA1, io_lba1);
io_write(ATA_PORT_LBA2, io_lba2);
io_write(ATA_PORT_COMMAND, write ? ATA_COMMAND_WRITE_SECTORS : ATA_COMMAND_READ_SECTORS);
return {};
}

View File

@ -27,21 +27,20 @@ namespace Kernel
{
ASSERT(identify_data.size() >= 256);
m_signature = identify_data[ATA_IDENTIFY_SIGNATURE];
m_signature = identify_data[ATA_IDENTIFY_SIGNATURE];
m_capabilities = identify_data[ATA_IDENTIFY_CAPABILITIES];
m_command_set = 0;
m_command_set |= (uint32_t)(identify_data[ATA_IDENTIFY_COMMAND_SET + 0] << 0);
m_command_set |= (uint32_t)(identify_data[ATA_IDENTIFY_COMMAND_SET + 1] << 16);
m_command_set = static_cast<uint32_t>(identify_data[ATA_IDENTIFY_COMMAND_SET + 0]) << 0;
m_command_set |= static_cast<uint32_t>(identify_data[ATA_IDENTIFY_COMMAND_SET + 1]) << 16;
if (!(m_capabilities & ATA_CAPABILITIES_LBA))
return BAN::Error::from_error_code(ErrorCode::ATA_NoLBA);
m_has_lba = !!(m_capabilities & ATA_CAPABILITIES_LBA);
if ((identify_data[ATA_IDENTIFY_SECTOR_INFO] & (1 << 15)) == 0 &&
(identify_data[ATA_IDENTIFY_SECTOR_INFO] & (1 << 14)) != 0 &&
(identify_data[ATA_IDENTIFY_SECTOR_INFO] & (1 << 12)) != 0)
{
m_sector_words = *(uint32_t*)(identify_data.data() + ATA_IDENTIFY_SECTOR_WORDS);
m_sector_words = static_cast<uint32_t>(identify_data[ATA_IDENTIFY_SECTOR_WORDS + 0]) << 0;
m_sector_words |= static_cast<uint32_t>(identify_data[ATA_IDENTIFY_SECTOR_WORDS + 1]) << 16;
}
else
{
@ -50,9 +49,17 @@ namespace Kernel
m_lba_count = 0;
if (m_command_set & ATA_COMMANDSET_LBA48_SUPPORTED)
m_lba_count = *(uint64_t*)(identify_data.data() + ATA_IDENTIFY_LBA_COUNT_EXT);
{
m_lba_count = static_cast<uint64_t>(identify_data[ATA_IDENTIFY_LBA_COUNT_EXT + 0]) << 0;
m_lba_count |= static_cast<uint64_t>(identify_data[ATA_IDENTIFY_LBA_COUNT_EXT + 1]) << 16;
m_lba_count |= static_cast<uint64_t>(identify_data[ATA_IDENTIFY_LBA_COUNT_EXT + 2]) << 32;
m_lba_count |= static_cast<uint64_t>(identify_data[ATA_IDENTIFY_LBA_COUNT_EXT + 3]) << 48;
}
if (m_lba_count < (1 << 28))
m_lba_count = *(uint32_t*)(identify_data.data() + ATA_IDENTIFY_LBA_COUNT);
{
m_lba_count = static_cast<uint32_t>(identify_data[ATA_IDENTIFY_LBA_COUNT + 0]) << 0;
m_lba_count |= static_cast<uint32_t>(identify_data[ATA_IDENTIFY_LBA_COUNT + 1]) << 16;
}
for (int i = 0; i < 20; i++)
{