first version of ide controller
This commit is contained in:
@@ -1,17 +1,279 @@
|
||||
//
|
||||
// Created by rick on 03-02-21.
|
||||
//
|
||||
// https://wiki.osdev.org/PCI_IDE_Controller
|
||||
|
||||
#include "ide.h"
|
||||
#include "ports.h"
|
||||
#include <types.h>
|
||||
#include <kprint.h>
|
||||
#include <drivers/pci.h>
|
||||
#include <libc/stdbool.h>
|
||||
#include <libk.h>
|
||||
#include <libc/libc.h>
|
||||
#include <cpu/timer.h>
|
||||
|
||||
const char* ide_pci_driver_name = "pci-ide";
|
||||
#define ATA_SR_BSY 0x80 // Busy
|
||||
#define ATA_SR_DRDY 0x40 // Drive ready
|
||||
#define ATA_SR_DF 0x20 // Drive write fault
|
||||
#define ATA_SR_DSC 0x10 // Drive seek complete
|
||||
#define ATA_SR_DRQ 0x08 // Data request ready
|
||||
#define ATA_SR_CORR 0x04 // Corrected data
|
||||
#define ATA_SR_IDX 0x02 // Index
|
||||
#define ATA_SR_ERR 0x01 // Error
|
||||
|
||||
#define ATA_ER_BBK 0x80 // Bad block
|
||||
#define ATA_ER_UNC 0x40 // Uncorrectable data
|
||||
#define ATA_ER_MC 0x20 // Media changed
|
||||
#define ATA_ER_IDNF 0x10 // ID mark not found
|
||||
#define ATA_ER_MCR 0x08 // Media change request
|
||||
#define ATA_ER_ABRT 0x04 // Command aborted
|
||||
#define ATA_ER_TK0NF 0x02 // Track 0 not found
|
||||
#define ATA_ER_AMNF 0x01 // No address mark
|
||||
|
||||
#define ATA_CMD_READ_PIO 0x20
|
||||
#define ATA_CMD_READ_PIO_EXT 0x24
|
||||
#define ATA_CMD_READ_DMA 0xC8
|
||||
#define ATA_CMD_READ_DMA_EXT 0x25
|
||||
#define ATA_CMD_WRITE_PIO 0x30
|
||||
#define ATA_CMD_WRITE_PIO_EXT 0x34
|
||||
#define ATA_CMD_WRITE_DMA 0xCA
|
||||
#define ATA_CMD_WRITE_DMA_EXT 0x35
|
||||
#define ATA_CMD_CACHE_FLUSH 0xE7
|
||||
#define ATA_CMD_CACHE_FLUSH_EXT 0xEA
|
||||
#define ATA_CMD_PACKET 0xA0
|
||||
#define ATA_CMD_IDENTIFY_PACKET 0xA1
|
||||
#define ATA_CMD_IDENTIFY 0xEC
|
||||
|
||||
#define ATAPI_CMD_READ 0xA8
|
||||
#define ATAPI_CMD_EJECT 0x1B
|
||||
|
||||
#define ATA_IDENT_DEVICETYPE 0
|
||||
#define ATA_IDENT_CYLINDERS 2
|
||||
#define ATA_IDENT_HEADS 6
|
||||
#define ATA_IDENT_SECTORS 12
|
||||
#define ATA_IDENT_SERIAL 20
|
||||
#define ATA_IDENT_MODEL 54
|
||||
#define ATA_IDENT_CAPABILITIES 98
|
||||
#define ATA_IDENT_FIELDVALID 106
|
||||
#define ATA_IDENT_MAX_LBA 120
|
||||
#define ATA_IDENT_COMMANDSETS 164
|
||||
#define ATA_IDENT_MAX_LBA_EXT 200
|
||||
|
||||
#define IDE_ATA 0x00
|
||||
#define IDE_ATAPI 0x01
|
||||
|
||||
#define ATA_MASTER 0x00
|
||||
#define ATA_SLAVE 0x01
|
||||
|
||||
#define ATA_REG_DATA 0x00
|
||||
#define ATA_REG_ERROR 0x01
|
||||
#define ATA_REG_FEATURES 0x01
|
||||
#define ATA_REG_SECCOUNT0 0x02
|
||||
#define ATA_REG_LBA0 0x03
|
||||
#define ATA_REG_LBA1 0x04
|
||||
#define ATA_REG_LBA2 0x05
|
||||
#define ATA_REG_HDDEVSEL 0x06
|
||||
#define ATA_REG_COMMAND 0x07
|
||||
#define ATA_REG_STATUS 0x07
|
||||
#define ATA_REG_SECCOUNT1 0x08
|
||||
#define ATA_REG_LBA3 0x09
|
||||
#define ATA_REG_LBA4 0x0A
|
||||
#define ATA_REG_LBA5 0x0B
|
||||
#define ATA_REG_CONTROL 0x0C
|
||||
#define ATA_REG_ALTSTATUS 0x0C
|
||||
#define ATA_REG_DEVADDRESS 0x0D
|
||||
|
||||
// Channels:
|
||||
#define ATA_PRIMARY 0x00
|
||||
#define ATA_SECONDARY 0x01
|
||||
|
||||
// Directions:
|
||||
#define ATA_READ 0x00
|
||||
#define ATA_WRITE 0x01
|
||||
|
||||
struct ide_channel_registers {
|
||||
unsigned short base; // I/O Base.
|
||||
unsigned short ctrl; // Control Base
|
||||
unsigned short bmide; // Bus Master IDE
|
||||
unsigned char nIEN; // nIEN (No Interrupt);
|
||||
} channels[2];
|
||||
|
||||
unsigned char ide_buf[2048] = {0};
|
||||
unsigned static char ide_irq_invoked = 0;
|
||||
unsigned static char atapi_packet[12] = {0xA8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
struct ide_device {
|
||||
unsigned char reserved; // 0 (Empty) or 1 (This Drive really exists).
|
||||
unsigned char channel; // 0 (Primary Channel) or 1 (Secondary Channel).
|
||||
unsigned char drive; // 0 (Master Drive) or 1 (Slave Drive).
|
||||
unsigned short type; // 0: ATA, 1:ATAPI.
|
||||
unsigned short signature; // Drive Signature
|
||||
unsigned short capabilities;// Features.
|
||||
unsigned int commandSets; // Command Sets Supported.
|
||||
unsigned int size; // Size in Sectors.
|
||||
unsigned char model[41]; // Model in string.
|
||||
} ide_devices[4];
|
||||
|
||||
u8 ide_read(u8 channel, u8 reg);
|
||||
|
||||
void ide_write(u8 channel, u8 reg, u8 data);
|
||||
|
||||
u8 ide_read(u8 channel, u8 reg) {
|
||||
u8 result;
|
||||
if (reg > 0x07 && reg < 0x0C)
|
||||
ide_write(channel, ATA_REG_CONTROL, 0x80 | channels[channel].nIEN);
|
||||
if (reg < 0x08)
|
||||
result = port_byte_in(channels[channel].base + reg - 0x00);
|
||||
else if (reg < 0x0C)
|
||||
result = port_byte_in(channels[channel].base + reg - 0x06);
|
||||
else if (reg < 0x0E)
|
||||
result = port_byte_in(channels[channel].ctrl + reg - 0x0A);
|
||||
else if (reg < 0x16)
|
||||
result = port_byte_in(channels[channel].bmide + reg - 0x0E);
|
||||
if (reg > 0x07 && reg < 0x0C)
|
||||
ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN);
|
||||
return result;
|
||||
}
|
||||
|
||||
void ide_write(u8 channel, u8 reg, u8 data) {
|
||||
if (reg > 0x07 && reg < 0x0C)
|
||||
ide_write(channel, ATA_REG_CONTROL, 0x80 | channels[channel].nIEN);
|
||||
if (reg < 0x08)
|
||||
port_byte_out(channels[channel].base + reg - 0x00, data);
|
||||
else if (reg < 0x0C)
|
||||
port_byte_out(channels[channel].base + reg - 0x06, data);
|
||||
else if (reg < 0x0E)
|
||||
port_byte_out(channels[channel].ctrl + reg - 0x0A, data);
|
||||
else if (reg < 0x16)
|
||||
port_byte_out(channels[channel].bmide + reg - 0x0E, data);
|
||||
if (reg > 0x07 && reg < 0x0C)
|
||||
ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN);
|
||||
}
|
||||
|
||||
void ide_read_buffer(unsigned char channel, unsigned char reg, unsigned int* buffer, unsigned int quads) {
|
||||
/* WARNING: This code contains a serious bug. The inline assembly trashes ES and
|
||||
* ESP for all of the code the compiler generates between the inline
|
||||
* assembly blocks.
|
||||
*/
|
||||
if (reg > 0x07 && reg < 0x0C) {
|
||||
ide_write(channel, ATA_REG_CONTROL, 0x80 | channels[channel].nIEN);
|
||||
}
|
||||
// asm("pushw %es; movw %ds, %ax; movw %ax, %es" : : : "es", "esp");
|
||||
if (reg < 0x08) {
|
||||
port_double_word_in_repeat(channels[channel].base + reg - 0x00, buffer, quads);
|
||||
} else if (reg < 0x0C) {
|
||||
port_double_word_in_repeat(channels[channel].base + reg - 0x06, buffer, quads);
|
||||
} else if (reg < 0x0E) {
|
||||
port_double_word_in_repeat(channels[channel].ctrl + reg - 0x0A, buffer, quads);
|
||||
} else if (reg < 0x16) {
|
||||
port_double_word_in_repeat(channels[channel].bmide + reg - 0x0E, buffer, quads);
|
||||
}
|
||||
// asm("popw %es;");
|
||||
if (reg > 0x07 && reg < 0x0C) {
|
||||
ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char ide_polling(unsigned char channel, unsigned int advanced_check) {
|
||||
|
||||
// (I) Delay 400 nanosecond for BSY to be set:
|
||||
// -------------------------------------------------
|
||||
for (int i = 0; i < 4; i++)
|
||||
ide_read(channel, ATA_REG_ALTSTATUS); // Reading the Alternate Status port wastes 100ns; loop four times.
|
||||
|
||||
// (II) Wait for BSY to be cleared:
|
||||
// -------------------------------------------------
|
||||
while (ide_read(channel, ATA_REG_STATUS) & ATA_SR_BSY); // Wait for BSY to be zero.
|
||||
|
||||
if (advanced_check) {
|
||||
unsigned char state = ide_read(channel, ATA_REG_STATUS); // Read Status Register.
|
||||
|
||||
// (III) Check For Errors:
|
||||
// -------------------------------------------------
|
||||
if (state & ATA_SR_ERR)
|
||||
return 2; // Error.
|
||||
|
||||
// (IV) Check If Device fault:
|
||||
// -------------------------------------------------
|
||||
if (state & ATA_SR_DF)
|
||||
return 1; // Device Fault.
|
||||
|
||||
// (V) Check DRQ:
|
||||
// -------------------------------------------------
|
||||
// BSY = 0; DF = 0; ERR = 0 so we should check for DRQ now.
|
||||
if ((state & ATA_SR_DRQ) == 0)
|
||||
return 3; // DRQ should be set
|
||||
|
||||
}
|
||||
|
||||
return 0; // No Error.
|
||||
|
||||
}
|
||||
|
||||
unsigned char ide_print_error(unsigned int drive, unsigned char err) {
|
||||
if (err == 0)
|
||||
return err;
|
||||
|
||||
kprint("IDE:");
|
||||
if (err == 1) {
|
||||
kprint("- Device Fault\n ");
|
||||
err = 19;
|
||||
} else if (err == 2) {
|
||||
unsigned char st = ide_read(ide_devices[drive].channel, ATA_REG_ERROR);
|
||||
if (st & ATA_ER_AMNF) {
|
||||
kprint("- No Address Mark Found\n ");
|
||||
err = 7;
|
||||
}
|
||||
if (st & ATA_ER_TK0NF) {
|
||||
kprint("- No Media or Media Error\n ");
|
||||
err = 3;
|
||||
}
|
||||
if (st & ATA_ER_ABRT) {
|
||||
kprint("- Command Aborted\n ");
|
||||
err = 20;
|
||||
}
|
||||
if (st & ATA_ER_MCR) {
|
||||
kprint("- No Media or Media Error\n ");
|
||||
err = 3;
|
||||
}
|
||||
if (st & ATA_ER_IDNF) {
|
||||
kprint("- ID mark not Found\n ");
|
||||
err = 21;
|
||||
}
|
||||
if (st & ATA_ER_MC) {
|
||||
kprint("- No Media or Media Error\n ");
|
||||
err = 3;
|
||||
}
|
||||
if (st & ATA_ER_UNC) {
|
||||
kprint("- Uncorrectable Data Error\n ");
|
||||
err = 22;
|
||||
}
|
||||
if (st & ATA_ER_BBK) {
|
||||
kprint("- Bad Sectors\n ");
|
||||
err = 13;
|
||||
}
|
||||
} else if (err == 3) {
|
||||
kprint("- Reads Nothing\n ");
|
||||
err = 23;
|
||||
} else if (err == 4) {
|
||||
kprint("- Write Protected\n ");
|
||||
err = 8;
|
||||
}
|
||||
kprint(" - [");
|
||||
kprint((const char *[]) {"Primary", "Secondary"}[ide_devices[drive].channel]);
|
||||
kprint(" ");
|
||||
kprint((const char *[]) {"Master", "Slave"}[ide_devices[drive].drive]);
|
||||
kprint("] ");
|
||||
kprint(&ide_devices[drive].model);
|
||||
kprint("\n");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
u8 ide_pci_validate(const pci_device *device);
|
||||
u8 ide_pci_initialize(const pci_device *device);
|
||||
|
||||
u8 ide_pci_initialize(pci_device *device);
|
||||
|
||||
const pci_driver ide_pci_driver = {
|
||||
.name = "pci-ide",
|
||||
@@ -27,16 +289,139 @@ const pci_driver ide_pci_driver = {
|
||||
|
||||
u8 ide_pci_validate(const pci_device *device) {
|
||||
if (device->class != PCI_CLASS_MASS_STORAGE
|
||||
|| device->subclass != PCI_SUB_CLASS_IDE) {
|
||||
|| device->subclass != PCI_SUB_CLASS_IDE
|
||||
|| (device->programInterface != 0x8A && device->programInterface != 0x80)) {
|
||||
return PCI_VALIDATE_FAIL;
|
||||
}
|
||||
// todo other validations
|
||||
return PCI_VALIDATE_OK;
|
||||
}
|
||||
|
||||
u8 ide_pci_initialize(const pci_device *device) {
|
||||
kprint("IDE registered");
|
||||
return PCI_VALIDATE_OK;
|
||||
u8 ide_pci_initialize(pci_device *device) {
|
||||
pci_config_write_byte(device->bus, device->slot, device->func, PCI_CONFIG_INTERRUPT_LINE, 0xFE);
|
||||
if (pci_config_read_byte(device->bus, device->slot, device->func, PCI_CONFIG_INTERRUPT_LINE) == 0xFE) {
|
||||
// todo
|
||||
// k_panics("Interrupt line");
|
||||
// return PCI_INIT_FAIL;
|
||||
}
|
||||
|
||||
pci_init_bar(device, 0);
|
||||
pci_init_bar(device, 1);
|
||||
pci_init_bar(device, 2);
|
||||
pci_init_bar(device, 3);
|
||||
pci_init_bar(device, 4);
|
||||
|
||||
if (!device->bar0.present
|
||||
|| !device->bar1.present
|
||||
|| !device->bar2.present
|
||||
|| !device->bar3.present
|
||||
|| !device->bar4.present) {
|
||||
k_panics("IDE Missing bars");
|
||||
return PCI_INIT_FAIL;
|
||||
}
|
||||
|
||||
channels[ATA_PRIMARY].base = device->bar0.address + 0x1F0;
|
||||
channels[ATA_PRIMARY].ctrl = device->bar1.address + 0x3F6;
|
||||
channels[ATA_SECONDARY].base = device->bar2.address + 0x170;
|
||||
channels[ATA_SECONDARY].ctrl = device->bar3.address + 0x376;
|
||||
channels[ATA_PRIMARY].bmide = device->bar4.address + 0; // Bus Master IDE
|
||||
channels[ATA_SECONDARY].bmide = device->bar4.address + 8; // Bus Master IDE
|
||||
|
||||
// disable IRQ
|
||||
ide_write(ATA_PRIMARY, ATA_REG_CONTROL, 2);
|
||||
ide_write(ATA_SECONDARY, ATA_REG_CONTROL, 2);
|
||||
|
||||
int count = 0;
|
||||
// 3- Detect ATA-ATAPI Devices:
|
||||
for (int i = 0; i < 2; i++)
|
||||
for (int j = 0; j < 2; j++) {
|
||||
|
||||
unsigned char err = 0, type = IDE_ATA, status;
|
||||
ide_devices[count].reserved = 0; // Assuming that no drive here.
|
||||
|
||||
// (I) Select Drive:
|
||||
ide_write(i, ATA_REG_HDDEVSEL, 0xA0 | (j << 4)); // Select Drive.
|
||||
sleep(1); // Wait 1ms for drive select to work.
|
||||
|
||||
// (II) Send ATA Identify Command:
|
||||
ide_write(i, ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
|
||||
sleep(1); // This function should be implemented in your OS. which waits for 1 ms.
|
||||
// it is based on System Timer Device Driver.
|
||||
|
||||
// (III) Polling:
|
||||
if (ide_read(i, ATA_REG_STATUS) == 0) continue; // If Status = 0, No Device.
|
||||
|
||||
while (1) {
|
||||
status = ide_read(i, ATA_REG_STATUS);
|
||||
if ((status & ATA_SR_ERR)) {
|
||||
err = 1;
|
||||
break;
|
||||
} // If Err, Device is not ATA.
|
||||
if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRQ)) break; // Everything is right.
|
||||
}
|
||||
|
||||
// (IV) Probe for ATAPI Devices:
|
||||
|
||||
if (err != 0) {
|
||||
unsigned char cl = ide_read(i, ATA_REG_LBA1);
|
||||
unsigned char ch = ide_read(i, ATA_REG_LBA2);
|
||||
|
||||
if (cl == 0x14 && ch == 0xEB)
|
||||
type = IDE_ATAPI;
|
||||
else if (cl == 0x69 && ch == 0x96)
|
||||
type = IDE_ATAPI;
|
||||
else
|
||||
continue; // Unknown Type (may not be a device).
|
||||
|
||||
ide_write(i, ATA_REG_COMMAND, ATA_CMD_IDENTIFY_PACKET);
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
// (V) Read Identification Space of the Device:
|
||||
ide_read_buffer(i, ATA_REG_DATA, (unsigned int*) ide_buf, 128);
|
||||
|
||||
// (VI) Read Device Parameters:
|
||||
ide_devices[count].reserved = 1;
|
||||
ide_devices[count].type = type;
|
||||
ide_devices[count].channel = i;
|
||||
ide_devices[count].drive = j;
|
||||
ide_devices[count].signature = *((unsigned short *) (ide_buf + ATA_IDENT_DEVICETYPE));
|
||||
ide_devices[count].capabilities = *((unsigned short *) (ide_buf + ATA_IDENT_CAPABILITIES));
|
||||
ide_devices[count].commandSets = *((unsigned int *) (ide_buf + ATA_IDENT_COMMANDSETS));
|
||||
|
||||
// (VII) Get Size:
|
||||
if (ide_devices[count].commandSets & (1 << 26))
|
||||
// Device uses 48-Bit Addressing:
|
||||
ide_devices[count].size = *((unsigned int *) (ide_buf + ATA_IDENT_MAX_LBA_EXT));
|
||||
else
|
||||
// Device uses CHS or 28-bit Addressing:
|
||||
ide_devices[count].size = *((unsigned int *) (ide_buf + ATA_IDENT_MAX_LBA));
|
||||
|
||||
// (VIII) String indicates model of device (like Western Digital HDD and SONY DVD-RW...):
|
||||
for (int k = 0; k < 40; k += 2) {
|
||||
ide_devices[count].model[k] = ide_buf[ATA_IDENT_MODEL + k + 1];
|
||||
ide_devices[count].model[k + 1] = ide_buf[ATA_IDENT_MODEL + k];
|
||||
}
|
||||
ide_devices[count].model[40] = 0; // Terminate String.
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
// 4- Print Summary:
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (ide_devices[i].reserved == 1) {
|
||||
char tmp[64];
|
||||
itoa(ide_devices[i].size / 1024 / 1024 / 2, tmp, 10);
|
||||
kprint("Found ");
|
||||
kprint((const char *[]) {"ATA", "ATAPI"}[ide_devices[i].type]);
|
||||
kprint(" Drive ");
|
||||
kprint(tmp);
|
||||
kprint(" - ");
|
||||
kprint(ide_devices[i].model);
|
||||
kprint("\n");
|
||||
}
|
||||
}
|
||||
return PCI_INIT_OK;
|
||||
}
|
||||
|
||||
void ide_register() {
|
||||
|
||||
Reference in New Issue
Block a user