Skip to content

Commit

Permalink
+ | Started IDE driver
Browse files Browse the repository at this point in the history
  • Loading branch information
BolvicBolvicovic committed Sep 18, 2024
1 parent 162a895 commit d252da8
Show file tree
Hide file tree
Showing 3 changed files with 251 additions and 0 deletions.
9 changes: 9 additions & 0 deletions drivers/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,13 @@ inline void port_byte_out(unsigned short port, unsigned char data) {
asm volatile("outb %1, %0" : : "dN" (port), "a" (data));
}

inline void insl(unsigned short port, void *buffer, unsigned int count) {
asm volatile (
"rep insl" // Repeat the 'insl' instruction 'count' times
: "+D"(buffer), "+c"(count) // '+D' is the destination (EDI in x86), '+c' is the counter (ECX in x86)
: "d"(port) // 'd' is the source port (DX in x86)
: "memory" // Inform the compiler that memory is affected
);
}

#endif
110 changes: 110 additions & 0 deletions filesystem/ide/ide.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#include "ide.h"

channels chnls = {0};
ide_devices devices = {0};
uint8_t ide_buf[2048] = {0};
volatile static uint8_t ide_irq_invoked = 0;
static uint8_t atapi_packet[12] = {ATAPI_CMD_READ, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

uint8_t ide_read(uint8_t channel, uint8_t reg) {
uint8_t result;

if (reg > 0x07 && reg < 0x0C) // If secondary channel register, notice it's busy
ide_write(channel, ATA_REG_CONTROL, ATA_SR_BSY | chnls[channel].nIEN);
if (reg < 0x08)
result = port_byte_in(chnls[channel].base + reg);
else if (reg < 0x0C)
result = port_byte_in(chnls[channel].base + reg - 0x06);
else if (reg < 0x0E)
result = port_byte_in(chnls[channel].crtl + reg - 0x0A);
else if (reg < 0x16)
result = port_byte_in(chnls[channel].bmide + reg - 0x0E);
if (reg > 0x07 && reg < 0x0C) // If secondary channel register, not busy anymore
ide_write(channel, ATA_REG_CONTROL, chnls[channel].nIEN);

return result;
}

void ide_write(uint8_t channel, uint8_t reg, uint8_t data) {

if (reg > 0x07 && reg < 0x0C) // If secondary channel register, notice it's busy
ide_write(channel, ATA_REG_CONTROL, ATA_SR_BSY | chnls[channel].nIEN);
if (reg < 0x08)
port_byte_out(chnls[channel].base + reg, data);
else if (reg < 0x0C)
port_byte_out(chnls[channel].base + reg - 0x06, data);
else if (reg < 0x0E)
port_byte_out(chnls[channel].crtl + reg - 0x0A, data);
else if (reg < 0x16)
port_byte_out(chnls[channel].bmide + reg - 0x0E, data);
if (reg > 0x07 && reg < 0x0C) // If secondary channel register, not busy anymore
ide_write(channel, ATA_REG_CONTROL, chnls[channel].nIEN);
}

void ide_read_buffer(uint8_t channel, uint8_t reg, uint32_t buffer, uint32_t quads) {
if (reg > 0x07 && reg < 0x0C) // If secondary channel register, notice it's busy
ide_write(channel, ATA_REG_CONTROL, ATA_SR_BSY | chnls[channel].nIEN);
asm("pushw %es; pushw %ax; movw %ds, %ax; movw %ax, %es; popw %ax;");
if (reg < 0x08)
insl(chnls[channel].base + reg, (void*)buffer, quads);
else if (reg < 0x0C)
insl(chnls[channel].base + reg - 0x06, (void*)buffer, quads);
else if (reg < 0x0E)
insl(chnls[channel].crtl + reg - 0x0A, (void*)buffer, quads);
else if (reg < 0x16)
insl(chnls[channel].bmide + reg - 0x0E, (void*)buffer, quads);
asm("popw %es;");
if (reg > 0x07 && reg < 0x0C) // If secondary channel register, not busy anymore
ide_write(channel, ATA_REG_CONTROL, chnls[channel].nIEN);
}

uint8_t ide_polling(uint8_t channel, uint32_t advanced_check) {
// (Part I) Delay 400 nsec for BSY to be set
for (uint8_t i = 0; i < 4; i++) ide_read(channel, ATA_REG_ALTSTATUS);
// (Part II) Wait for BSY to be cleared
while (ide_read(channel, ATA_REG_STATUS) & ATA_SR_BSY);
if (advanced_check) {
uint8_t state = ide_read(channel, ATA_REG_STATUS);
if (state & ATA_SR_ERR) return 2; // Error
if (state & ATA_SR_DF) return 1; // Device Fault
if (!(state & ATA_SR_DRQ)) return 3; // Data RQ not set
}

return 0;
}

uint8_t ide_print_error(uint32_t drive, uint8_t err) {
if (!err) return err;
printf("IDE:");
switch (err) {
case 1:
printf("Device Fault\n");
err = 19;
break;
case 2: {
uint8_t st = ide_read(devices[drive].channel, ATA_REG_ERROR);
if (st & ATA_ER_AMNF) {printf("- No Address Mark Found\n "); err = 7;}
if (st & ATA_ER_TK0NF) {printf("- No Media or Media Error\n "); err = 3;}
if (st & ATA_ER_ABRT) {printf("- Command Aborted\n "); err = 20;}
if (st & ATA_ER_MCR) {printf("- No Media or Media Error\n "); err = 3;}
if (st & ATA_ER_IDNF) {printf("- ID mark not Found\n "); err = 21;}
if (st & ATA_ER_MC) {printf("- No Media or Media Error\n "); err = 3;}
if (st & ATA_ER_UNC) {printf("- Uncorrectable Data Error\n "); err = 22;}
if (st & ATA_ER_BBK) {printf("- Bad Sectors\n "); err = 13;}
}
case 3:
printf("- Reads Nothing\n");
err = 23;
break;
case 4:
printf("- [%s %s] %s\n",
(const char *[]){"Primary", "Secondary"}[devices[drive].channel], // Use the channel as an index into the array
(const char *[]){"Master", "Slave"}[devices[drive].drive], // Same as above, using the drive
devices[drive].model);
}
return err;
}

void ide_init(uint32_t BAR0, uint32_t BAR1, uint32_t BAR2, uint32_t BAR3, uint32_t BAR4) {
//TODO: https://wiki.osdev.org/PCI_IDE_Controller#Read/Write_From_ATA_Drive
}
132 changes: 132 additions & 0 deletions filesystem/ide/ide.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
#ifndef IDE_H
#define IDE_H

/* IDE (Integrated Drive Electronics) */

#include <stdint.h>
#include "../../drivers/utils.h"
#include "../../lib/stdio/stdio.h"

/* Status */
#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

/* Errors */
#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

/* Commands */
#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

// These are specific commands for ATAPI
#define ATAPI_CMD_READ 0xA8
#define ATAPI_CMD_EJECT 0x1B

/* Indentificators to read from identification space */
/* (after ATA_CMD_IDENTIFY or ATA_CMD_IDENTIFY_PACKET write in port Status/Commands) */
#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

/* Interface type */
#define IDE_ATA 0x00
#define IDE_ATAPI 0x01

#define ATA_MASTER 0x00
#define ATA_SLAVE 0x01

/* Task File (ports that are offsets from BAR0 and/or BAR2)*/
#define ATA_REG_DATA 0x00 // RW
#define ATA_REG_ERROR 0x01 // RO
#define ATA_REG_FEATURES 0x01 // WO
#define ATA_REG_SECCOUNT0 0x02 // RW
#define ATA_REG_LBA0 0x03 // RW (LBA == Logical Block Addressing)
#define ATA_REG_LBA1 0x04 // RW
#define ATA_REG_LBA2 0x05 // RW
#define ATA_REG_HDDEVSEL 0x06 // RW, used to select a drive in channel
#define ATA_REG_COMMAND 0x07 // WO
#define ATA_REG_STATUS 0x07 // RO
#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 // WO
#define ATA_REG_ALTSTATUS 0x0C // RO
#define ATA_REG_DEVADDRESS 0x0D // ?

// Channels:
#define ATA_PRIMARY 0x00
#define ATA_SECONDARY 0x01

// Directions:
#define ATA_READ 0x00
#define ATA_WRITE 0x01

typedef struct ide_channel_registers {
uint16_t base; // I/O Base
uint16_t crtl; // Control Base
uint16_t bmide;// Bus Master IDE
uint8_t nIEN; // (No Interrupt)

} channels[2];

typedef struct ide_device {
uint8_t reserved; // 0 (Empty) or 1 (Drive really exists)
uint8_t channel; // 0 (primary channel) or 1 (secondary channel)
uint8_t drive; // 0 (Master Drive) or 1 (Slave Drive)
uint16_t type; // 0 (ATA) or 1 (ATAPI)
uint16_t signature;// Drive signature
uint16_t capabilities; // features
uint32_t command_sets;
uint32_t size;
uint8_t model[41];// model of the drive in string
} ide_devices[4];

/*
BAR0: Base address of primary channel in PCI native mode (8 ports)
BAR1: Base address of primary channel control port in PCI native mode (4 ports)
BAR2: Base address of secondary channel in PCI native mode (8 ports)
BAR3: Base address of secondary channel control port in PCI native mode (4 ports)
BAR4: Bus master IDE (16 ports, 8 for each channel)
*/

uint8_t ide_read(uint8_t channel, uint8_t reg);
void ide_write(uint8_t channel, uint8_t reg, uint8_t data);
void ide_read_buffer(uint8_t channel, uint8_t reg, uint32_t buffer, uint32_t quads);
uint8_t ide_polling(uint8_t channel, uint32_t advanced_check);
uint8_t ide_print_error(uint32_t drive, uint8_t err);
void ide_init(uint32_t BAR0, uint32_t BAR1, uint32_t BAR2, uint32_t BAR3, uint32_t BAR4);

#endif

0 comments on commit d252da8

Please sign in to comment.