[not working] basic achi driver

This commit is contained in:
hippoz 2020-12-13 23:24:26 +02:00
parent 5303b1e59c
commit eec6b437ad
Signed by untrusted user who does not match committer: hippoz
GPG key ID: 7C52899193467641
4 changed files with 575 additions and 4 deletions

View file

@ -13,10 +13,10 @@ NASMFLAGS += -felf32
BIN = sysroot/boot/hhhos.bin
ISO = build/HhhOS.iso
DIRS = build build/boot build/std build/drivers build/drivers/terminal build/drivers/idt build/drivers/device build/drivers/keyboard build/drivers/pic
DIRS = build build/boot build/std build/drivers build/drivers/terminal build/drivers/idt build/drivers/device build/drivers/keyboard build/drivers/pic build/drivers/ahci
OBJS = build/boot/boot.o build/boot/kernel.o build/std/string.o build/drivers/terminal/terminal.o build/drivers/idt/idt.o \
build/drivers/idt/isr.o build/std/util.o build/drivers/device/device.o build/drivers/keyboard/keyboard.o build/drivers/pic/pic.o
build/drivers/idt/isr.o build/std/util.o build/drivers/device/device.o build/drivers/keyboard/keyboard.o build/drivers/pic/pic.o build/drivers/ahci/ahci.o
ARCHDIR = arch/$(ARCH)/

View file

@ -4,6 +4,7 @@
#include <drivers/keyboard/keyboard.h>
#include <drivers/pic/pic.h>
#include <drivers/device/device.h>
#include <drivers/ahci/ahci.h>
/* Check if the compiler thinks you are targeting the wrong operating system. */
#if defined(__linux__)
@ -28,6 +29,9 @@ void kmain() {
int keyboard_descriptor = keyboard_init();
terminal_writestring("done, descriptor: ");
terminal_writeline(itoa(keyboard_descriptor));
terminal_writestring("Preparing AHCI... ");
ahci_init();
terminal_writestring("Are interrupts enabled? ");
if (are_interrupts_enabled() == 1) {
@ -42,8 +46,8 @@ void kmain() {
terminal_writeline("no");
}
terminal_writestring("kmain memory address: ");
terminal_writeline(itoa(kmain));
//terminal_writestring("kmain memory address: ");
//terminal_writeline(itoa(kmain));
terminal_writeline("----------");

View file

@ -0,0 +1,213 @@
#include <stdint.h>
#include <drivers/ahci/ahci.h>
#include <drivers/terminal/terminal.h>
#include <std/util.h>
#include <std/string.h>
#include <std/inline.h>
// AHCI driver, source: https://wiki.osdev.org/AHCI
static void trace_ahci(const char* message, int i) {
terminal_writestring(message);
terminal_writeline(itoa(i));
}
// Check device type
static int check_type(HBA_PORT *port) {
uint32_t ssts = port->ssts;
uint8_t ipm = (ssts >> 8) & 0x0F;
uint8_t det = ssts & 0x0F;
if (det != HBA_PORT_DET_PRESENT) // Check drive status
return AHCI_DEV_NULL;
if (ipm != HBA_PORT_IPM_ACTIVE)
return AHCI_DEV_NULL;
switch (port->sig)
{
case SATA_SIG_ATAPI:
return AHCI_DEV_SATAPI;
case SATA_SIG_SEMB:
return AHCI_DEV_SEMB;
case SATA_SIG_PM:
return AHCI_DEV_PM;
default:
return AHCI_DEV_SATA;
}
}
void probe_port(HBA_MEM *abar) {
// Search disk in implemented ports
uint32_t pi = abar->pi;
int i = 0;
while (i<32)
{
if (pi & 1)
{
int dt = check_type(&abar->ports[i]);
if (dt == AHCI_DEV_SATA)
{
trace_ahci("SATA drive found at port: ", i);
}
else if (dt == AHCI_DEV_SATAPI)
{
trace_ahci("SATAPI drive found at port: ", i);
}
else if (dt == AHCI_DEV_SEMB)
{
trace_ahci("SEMB drive found at port: ", i);
}
else if (dt == AHCI_DEV_PM)
{
trace_ahci("PM drive found at port: ", i);
}
else
{
//trace_ahci("No drive found at port: ", i);
}
}
pi >>= 1;
i ++;
}
}
uint32_t pci_config_read_word(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset) {
uint32_t address;
uint32_t lbus = (uint32_t)bus;
uint32_t lslot = (uint32_t)slot;
uint32_t lfunc = (uint32_t)func;
uint64_t tmp = 0;
/* create configuration address as per Figure 1 */
address = (uint32_t)((lbus << 16) | (lslot << 11) |
(lfunc << 8) | (offset & 0xfc) | ((uint32_t)0x80000000));
/* write out the address */
outportl(0xCF8, address);
/* read in the data */
/* (offset & 2) * 8) = 0 will choose the first word of the 32 bits register */
if (offset == 0x24)
tmp = inportb(0xCFC) ;
else
tmp = (uint16_t)( (inportb(0xCFC) >> ((offset & 2) * 8) ) & 0xffff);
return (tmp);
}
uint16_t get_vendor_id(uint8_t bus, uint8_t slot)
{
return pci_config_read_word(bus, slot, 0, 0);
}
uint16_t get_device_id(uint8_t bus, uint8_t slot)
{
return pci_config_read_word(bus, slot, 0, 2);
}
uint64_t check_device(uint8_t bus, uint8_t device) {
//uint8_t function = 0;
uint16_t vendorID = get_vendor_id(bus, device);
if(vendorID == 0xFFFF) return 0; // Device doesn't exist
uint16_t deviceID = get_device_id(bus, device);
return pci_config_read_word(bus, device, 0, 0x24);
return 0;
}
uint64_t check_all_buses(void)
{
uint16_t bus;
uint8_t device;
uint64_t bar5;
for(bus = 0; bus < 256; bus++)
{
for(device = 0; device < 32; device++)
{
bar5 = check_device(bus, device);
if (bar5 == 0) return 0;
probe_port((HBA_MEM*)bar5);
return bar5;
}
}
return 0;
}
// Start command engine
void start_cmd(HBA_PORT *port)
{
// Wait until CR (bit15) is cleared
while (port->cmd & HBA_PxCMD_CR);
// Set FRE (bit4) and ST (bit0)
port->cmd |= HBA_PxCMD_FRE;
port->cmd |= HBA_PxCMD_ST;
}
// Stop command engine
void stop_cmd(HBA_PORT *port)
{
// Clear ST (bit0)
port->cmd &= ~HBA_PxCMD_ST;
// Clear FRE (bit4)
port->cmd &= ~HBA_PxCMD_FRE;
// Wait until FR (bit14), CR (bit15) are cleared
while(1)
{
if (port->cmd & HBA_PxCMD_FR)
continue;
if (port->cmd & HBA_PxCMD_CR)
continue;
break;
}
}
void port_rebase(HBA_PORT *port, int portno)
{
stop_cmd(port); // Stop command engine
// Command list offset: 1K*portno
// Command list entry size = 32
// Command list entry maxim count = 32
// Command list maxim size = 32*32 = 1K per port
port->clb = AHCI_BASE + (portno<<10);
port->clbu = 0;
memset((void*)(port->clb), 0, 1024);
// FIS offset: 32K+256*portno
// FIS entry size = 256 bytes per port
port->fb = AHCI_BASE + (32<<10) + (portno<<8);
port->fbu = 0;
memset((void*)(port->fb), 0, 256);
// Command table offset: 40K + 8K*portno
// Command table size = 256*32 = 8K per port
HBA_CMD_HEADER *cmdheader = (HBA_CMD_HEADER*)(port->clb);
for (int i=0; i<32; i++)
{
cmdheader[i].prdtl = 8; // 8 prdt entries per command table
// 256 bytes per command table, 64+16+48+16*8
// Command table offset: 40K + 8K*portno + cmdheader_index*256
cmdheader[i].ctba = AHCI_BASE + (40<<10) + (portno<<13) + (i<<8);
cmdheader[i].ctbau = 0;
memset((void*)cmdheader[i].ctba, 0, 256);
}
start_cmd(port); // Start command engine
}
void ahci_init(void) {
uint64_t bar5 = check_all_buses();
if (bar5 == 0) {
terminal_writeline("Error: Could not find AHCI device");
return;
}
terminal_writestring("Found AHCI device at: ");
terminal_writeline(itoa(bar5));
}

354
include/drivers/ahci/ahci.h Normal file
View file

@ -0,0 +1,354 @@
#ifndef _AHCI_H
#define _AHCI_H 1
#ifdef __cplusplus
extern "C" {
#endif
// AHCI driver, source: https://wiki.osdev.org/AHCI
// FIS = Frame Information Structure
// ---- FIS types ----
// Following code defines different kinds of FIS specified in Serial ATA Revision 3.0.
typedef enum {
FIS_TYPE_REG_H2D = 0x27, // Register FIS - host to device
FIS_TYPE_REG_D2H = 0x34, // Register FIS - device to host
FIS_TYPE_DMA_ACT = 0x39, // DMA activate FIS - device to host
FIS_TYPE_DMA_SETUP = 0x41, // DMA setup FIS - bidirectional
FIS_TYPE_DATA = 0x46, // Data FIS - bidirectional
FIS_TYPE_BIST = 0x58, // BIST activate FIS - bidirectional
FIS_TYPE_PIO_SETUP = 0x5F, // PIO setup FIS - device to host
FIS_TYPE_DEV_BITS = 0xA1, // Set device bits FIS - device to host
} FIS_TYPE;
// ---- Various other FIS things (this struct was not found in OSDev wiki)
typedef struct tagFIS_SET_DEVICE_BITS {
uint8_t fis_type;
uint8_t pmport:4;
uint8_t rsvd:2;
uint8_t i:1;
uint8_t n:1;
uint8_t statusl:3;
uint8_t rsvd2:1;
uint8_t statush:3;
uint8_t rsvd3:1;
uint8_t error;
} FIS_DEV_BITS;
// ---- Register FIS - Host to Device ----
typedef struct tagFIS_REG_H2D
{
// DWORD 0
uint8_t fis_type; // FIS_TYPE_REG_H2D
uint8_t pmport:4; // Port multiplier
uint8_t rsv0:3; // Reserved
uint8_t c:1; // 1: Command, 0: Control
uint8_t command; // Command register
uint8_t featurel; // Feature register, 7:0
// DWORD 1
uint8_t lba0; // LBA low register, 7:0
uint8_t lba1; // LBA mid register, 15:8
uint8_t lba2; // LBA high register, 23:16
uint8_t device; // Device register
// DWORD 2
uint8_t lba3; // LBA register, 31:24
uint8_t lba4; // LBA register, 39:32
uint8_t lba5; // LBA register, 47:40
uint8_t featureh; // Feature register, 15:8
// DWORD 3
uint8_t countl; // Count register, 7:0
uint8_t counth; // Count register, 15:8
uint8_t icc; // Isochronous command completion
uint8_t control; // Control register
// DWORD 4
uint8_t rsv1[4]; // Reserved
} FIS_REG_H2D;
// ---- Register FIS Device to Host ----
// A device to host register FIS is used by the device to notify the host that some ATA register has changed. It contains the updated task files such as status, error and other registers.
typedef struct tagFIS_REG_D2H
{
// DWORD 0
uint8_t fis_type; // FIS_TYPE_REG_D2H
uint8_t pmport:4; // Port multiplier
uint8_t rsv0:2; // Reserved
uint8_t i:1; // Interrupt bit
uint8_t rsv1:1; // Reserved
uint8_t status; // Status register
uint8_t error; // Error register
// DWORD 1
uint8_t lba0; // LBA low register, 7:0
uint8_t lba1; // LBA mid register, 15:8
uint8_t lba2; // LBA high register, 23:16
uint8_t device; // Device register
// DWORD 2
uint8_t lba3; // LBA register, 31:24
uint8_t lba4; // LBA register, 39:32
uint8_t lba5; // LBA register, 47:40
uint8_t rsv2; // Reserved
// DWORD 3
uint8_t countl; // Count register, 7:0
uint8_t counth; // Count register, 15:8
uint8_t rsv3[2]; // Reserved
// DWORD 4
uint8_t rsv4[4]; // Reserved
} FIS_REG_D2H;
// ---- Data FIS Bidirectional ----
// This FIS is used by the host or device to send data payload. The data size can be varied.
typedef struct tagFIS_DATA {
// DWORD 0
uint8_t fis_type; // FIS_TYPE_DATA
uint8_t pmport:4; // Port multiplier
uint8_t rsv0:4; // Reserved
uint8_t rsv1[2]; // Reserved
// DWORD 1 ~ N
uint32_t data[1]; // Payload
} FIS_DATA;
// ---- PIO Setup Device to Host ----
// This FIS is used by the device to tell the host that its about to send or ready to receive a PIO data payload.
typedef struct tagFIS_PIO_SETUP {
// DWORD 0
uint8_t fis_type; // FIS_TYPE_PIO_SETUP
uint8_t pmport:4; // Port multiplier
uint8_t rsv0:1; // Reserved
uint8_t d:1; // Data transfer direction, 1 - device to host
uint8_t i:1; // Interrupt bit
uint8_t rsv1:1;
uint8_t status; // Status register
uint8_t error; // Error register
// DWORD 1
uint8_t lba0; // LBA low register, 7:0
uint8_t lba1; // LBA mid register, 15:8
uint8_t lba2; // LBA high register, 23:16
uint8_t device; // Device register
// DWORD 2
uint8_t lba3; // LBA register, 31:24
uint8_t lba4; // LBA register, 39:32
uint8_t lba5; // LBA register, 47:40
uint8_t rsv2; // Reserved
// DWORD 3
uint8_t countl; // Count register, 7:0
uint8_t counth; // Count register, 15:8
uint8_t rsv3; // Reserved
uint8_t e_status; // New value of status register
// DWORD 4
uint16_t tc; // Transfer count
uint8_t rsv4[2]; // Reserved
} FIS_PIO_SETUP;
// ---- DMA Setup Device to Host ----
typedef struct tagFIS_DMA_SETUP {
// DWORD 0
uint8_t fis_type; // FIS_TYPE_DMA_SETUP
uint8_t pmport:4; // Port multiplier
uint8_t rsv0:1; // Reserved
uint8_t d:1; // Data transfer direction, 1 - device to host
uint8_t i:1; // Interrupt bit
uint8_t a:1; // Auto-activate. Specifies if DMA Activate FIS is needed
uint8_t rsved[2]; // Reserved
//DWORD 1&2
uint64_t DMAbufferID; // DMA Buffer Identifier. Used to Identify DMA buffer in host memory. SATA Spec says host specific and not in Spec. Trying AHCI spec might work.
//DWORD 3
uint32_t rsvd; //More reserved
//DWORD 4
uint32_t DMAbufOffset; //Byte offset into buffer. First 2 bits must be 0
//DWORD 5
uint32_t TransferCount; //Number of bytes to transfer. Bit 0 must be 0
//DWORD 6
uint32_t resvd; //Reserved
} FIS_DMA_SETUP;
// ---- Memory structs (HBA memory registers) ----
typedef volatile struct tagHBA_PORT {
uint32_t clb; // 0x00, command list base address, 1K-byte aligned
uint32_t clbu; // 0x04, command list base address upper 32 bits
uint32_t fb; // 0x08, FIS base address, 256-byte aligned
uint32_t fbu; // 0x0C, FIS base address upper 32 bits
uint32_t is; // 0x10, interrupt status
uint32_t ie; // 0x14, interrupt enable
uint32_t cmd; // 0x18, command and status
uint32_t rsv0; // 0x1C, Reserved
uint32_t tfd; // 0x20, task file data
uint32_t sig; // 0x24, signature
uint32_t ssts; // 0x28, SATA status (SCR0:SStatus)
uint32_t sctl; // 0x2C, SATA control (SCR2:SControl)
uint32_t serr; // 0x30, SATA error (SCR1:SError)
uint32_t sact; // 0x34, SATA active (SCR3:SActive)
uint32_t ci; // 0x38, command issue
uint32_t sntf; // 0x3C, SATA notification (SCR4:SNotification)
uint32_t fbs; // 0x40, FIS-based switch control
uint32_t rsv1[11]; // 0x44 ~ 0x6F, Reserved
uint32_t vendor[4]; // 0x70 ~ 0x7F, vendor specific
} HBA_PORT;
typedef volatile struct tagHBA_MEM {
// 0x00 - 0x2B, Generic Host Control
uint32_t cap; // 0x00, Host capability
uint32_t ghc; // 0x04, Global host control
uint32_t is; // 0x08, Interrupt status
uint32_t pi; // 0x0C, Port implemented
uint32_t vs; // 0x10, Version
uint32_t ccc_ctl; // 0x14, Command completion coalescing control
uint32_t ccc_pts; // 0x18, Command completion coalescing ports
uint32_t em_loc; // 0x1C, Enclosure management location
uint32_t em_ctl; // 0x20, Enclosure management control
uint32_t cap2; // 0x24, Host capabilities extended
uint32_t bohc; // 0x28, BIOS/OS handoff control and status
// 0x2C - 0x9F, Reserved
uint8_t rsv[0xA0-0x2C];
// 0xA0 - 0xFF, Vendor specific registers
uint8_t vendor[0x100-0xA0];
// 0x100 - 0x10FF, Port control registers
HBA_PORT ports[1]; // 1 ~ 32
} HBA_MEM;
// ---- Received FIS ----
typedef volatile struct tagHBA_FIS {
// 0x00
FIS_DMA_SETUP dsfis; // DMA Setup FIS
uint8_t pad0[4];
// 0x20
FIS_PIO_SETUP psfis; // PIO Setup FIS
uint8_t pad1[12];
// 0x40
FIS_REG_D2H rfis; // Register Device to Host FIS
uint8_t pad2[4];
// 0x58
FIS_DEV_BITS sdbfis; // Set Device Bit FIS
// 0x60
uint8_t ufis[64];
// 0xA0
uint8_t rsv[0x100-0xA0];
} HBA_FIS;
// ---- Command List ----
typedef struct tagHBA_CMD_HEADER {
// DW0
uint8_t cfl:5; // Command FIS length in DWORDS, 2 ~ 16
uint8_t a:1; // ATAPI
uint8_t w:1; // Write, 1: H2D, 0: D2H
uint8_t p:1; // Prefetchable
uint8_t r:1; // Reset
uint8_t b:1; // BIST
uint8_t c:1; // Clear busy upon R_OK
uint8_t rsv0:1; // Reserved
uint8_t pmp:4; // Port multiplier port
uint16_t prdtl; // Physical region descriptor table length in entries
// DW1
volatile
uint32_t prdbc; // Physical region descriptor byte count transferred
// DW2, 3
uint32_t ctba; // Command table descriptor base address
uint32_t ctbau; // Command table descriptor base address upper 32 bits
// DW4 - 7
uint32_t rsv1[4]; // Reserved
} HBA_CMD_HEADER;
// ---- Command Table and Physical Region Descriptor Table ----
typedef struct tagHBA_PRDT_ENTRY {
uint32_t dba; // Data base address
uint32_t dbau; // Data base address upper 32 bits
uint32_t rsv0; // Reserved
// DW3
uint32_t dbc:22; // Byte count, 4M max
uint32_t rsv1:9; // Reserved
uint32_t i:1; // Interrupt on completion
} HBA_PRDT_ENTRY;
typedef struct tagHBA_CMD_TBL {
// 0x00
uint8_t cfis[64]; // Command FIS
// 0x40
uint8_t acmd[16]; // ATAPI command, 12 or 16 bytes
// 0x50
uint8_t rsv[48]; // Reserved
// 0x80
HBA_PRDT_ENTRY prdt_entry[1]; // Physical region descriptor table entries, 0 ~ 65535
} HBA_CMD_TBL;
// ---- Signatures and values ----
#define SATA_SIG_ATA 0x00000101 // SATA drive
#define SATA_SIG_ATAPI 0xEB140101 // SATAPI drive
#define SATA_SIG_SEMB 0xC33C0101 // Enclosure management bridge
#define SATA_SIG_PM 0x96690101 // Port multiplier
#define AHCI_DEV_NULL 0
#define AHCI_DEV_SATA 1
#define AHCI_DEV_SEMB 2
#define AHCI_DEV_PM 3
#define AHCI_DEV_SATAPI 4
#define HBA_PORT_IPM_ACTIVE 1
#define HBA_PORT_DET_PRESENT 3
#define AHCI_BASE 0x400000 // 4M
#define HBA_PxCMD_ST 0x0001
#define HBA_PxCMD_FRE 0x0010
#define HBA_PxCMD_FR 0x4000
#define HBA_PxCMD_CR 0x8000
#define ATA_DEV_BUSY 0x80
#define ATA_DEV_DRQ 0x08
// ---- Functions ----
void ahci_init(void);
#ifdef __cplusplus
}
#endif
#endif /* _AHCI_H */