From 90e2de72ef2688986c206fd08605aec81b5890d6 Mon Sep 17 00:00:00 2001 From: Aleksa Vuckovic Date: Wed, 7 Jun 2023 01:23:03 +0200 Subject: sata & ahci --- kernel/Makefile | 1 + kernel/include/ahci.h | 143 +++++++++++++++++++++++++++++++++++++++ kernel/include/pci.h | 21 +++++- kernel/include/sata.h | 167 ++++++++++++++++++++++++++++++++++++++++++++++ kernel/src/devices/pci.c | 67 ++++++++++--------- kernel/src/devices/sata.c | 73 ++++++++++++++++++++ kernel/src/main.c | 2 + 7 files changed, 442 insertions(+), 32 deletions(-) create mode 100644 kernel/include/ahci.h create mode 100644 kernel/include/sata.h create mode 100644 kernel/src/devices/sata.c diff --git a/kernel/Makefile b/kernel/Makefile index b168ee0..3726508 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -26,6 +26,7 @@ OBJS = \ src/devices/keyboard.o \ src/devices/pci_info.o \ src/devices/pci.o \ + src/devices/sata.o \ src/devices/serial.o \ src/devices/timer.o \ src/fs/ext2.o \ diff --git a/kernel/include/ahci.h b/kernel/include/ahci.h new file mode 100644 index 0000000..2758c37 --- /dev/null +++ b/kernel/include/ahci.h @@ -0,0 +1,143 @@ +#ifndef AHCI_H +#define AHCI_H + +#include +#include + +#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 + +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; + +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 + uint16_t sdbfis; // Set Device Bit FIS + + // 0x60 + uint8_t ufis[64]; + + // 0xA0 + uint8_t rsv[0x100 - 0xA0]; +} HBA_FIS; + +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; + +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; + +#endif diff --git a/kernel/include/pci.h b/kernel/include/pci.h index 779d75a..d9475ff 100644 --- a/kernel/include/pci.h +++ b/kernel/include/pci.h @@ -2,8 +2,9 @@ #define PCI_H #include +#include -struct pci_dev { +struct pci_dev_t { uint16_t vendor_id; uint16_t device_id; uint16_t command; @@ -17,6 +18,22 @@ struct pci_dev { uint8_t header_type; uint8_t bist; }; -typedef struct pci_dev pci_dev; +typedef struct pci_dev_t pci_dev_t; + +struct pci_dev0_t { + pci_dev_t pci_dev; + uint32_t bar[6]; +}; +typedef struct pci_dev0_t pci_dev0_t; + +struct pci_dev_list_t { + pci_dev0_t *dev; + list_t list; +}; +typedef struct pci_dev_list_t pci_dev_list_t; + +extern pci_dev_list_t pci_dev_list; + +void pci_print_all(void); #endif diff --git a/kernel/include/sata.h b/kernel/include/sata.h new file mode 100644 index 0000000..3705218 --- /dev/null +++ b/kernel/include/sata.h @@ -0,0 +1,167 @@ +#ifndef SATA_H +#define SATA_H + +#include + +#define ATA_CMD_IDENTIFY 0xEC + +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; + +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; + +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; + +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; + +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; + +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; + +void ahci(void); + +#endif diff --git a/kernel/src/devices/pci.c b/kernel/src/devices/pci.c index 9862dee..ea6c80e 100644 --- a/kernel/src/devices/pci.c +++ b/kernel/src/devices/pci.c @@ -9,7 +9,9 @@ #include #include -inline void pci_print_dev(pci_dev *pci_func) +pci_dev_list_t pci_dev_list; + +inline void pci_print_dev(pci_dev_t *pci_func) { const char *vendor_str = get_vendor(pci_func->vendor_id); const char *class_str = get_class(pci_func->class_); @@ -54,18 +56,20 @@ void enumerate_function(uint64_t dev_addr, uint64_t function) uint64_t func_addr = dev_addr + (function << 12); map_addr(func_addr, func_addr, FLAG_PRESENT); - pci_dev *pci_func = (pci_dev *)kalloc(sizeof(pci_dev)); - memcpy(pci_func, (uint64_t *)func_addr, sizeof(pci_dev)); + pci_dev0_t *pci_func = (pci_dev0_t *)kalloc(sizeof(pci_dev0_t)); + memcpy(pci_func, (uint64_t *)func_addr, sizeof(pci_dev0_t)); - if (pci_func->device_id == 0) - goto error; - if (pci_func->device_id == 0xFFFF) - goto error; + int id = pci_func->pci_dev.device_id; + if (id == 0 || id == 0xFFFF) { + kfree(pci_func); + return; + } - pci_print_dev(pci_func); + pci_dev_list_t *dev_list = kalloc(sizeof(pci_dev_list_t)); + dev_list->dev = pci_func; -error: - kfree(pci_func); + add_to_list(&dev_list->list, &pci_dev_list.list, + pci_dev_list.list.next); } void enumerate_device(uint64_t bus_addr, uint64_t device) @@ -73,21 +77,18 @@ void enumerate_device(uint64_t bus_addr, uint64_t device) uint64_t dev_addr = bus_addr + (device << 15); map_addr(dev_addr, dev_addr, FLAG_PRESENT); - pci_dev *pci_device = (pci_dev *)kalloc(sizeof(pci_dev)); - memcpy(pci_device, (uint64_t *)bus_addr, sizeof(pci_dev)); + pci_dev_t *pci_device = (pci_dev_t *)kalloc(sizeof(pci_dev_t)); + memcpy(pci_device, (uint64_t *)bus_addr, sizeof(pci_dev_t)); - if (pci_device->device_id == 0) - goto error; - if (pci_device->device_id == 0xFFFF) - goto error; + if (pci_device->device_id == 0 || pci_device->device_id == 0xFFFF) { + kfree(pci_device); + return; + } size_t func; for (func = 0; func < 8; func++) { enumerate_function(dev_addr, func); } - -error: - kfree(pci_device); } void enumerate_bus(uint64_t base_addr, uint64_t bus) @@ -95,31 +96,39 @@ void enumerate_bus(uint64_t base_addr, uint64_t bus) uint64_t bus_addr = base_addr + (bus << 20); map_addr(bus_addr, bus_addr, FLAG_PRESENT); - pci_dev *pci_device = (pci_dev *)kalloc(sizeof(pci_dev)); - memcpy(pci_device, (uint64_t *)bus_addr, sizeof(pci_dev)); + pci_dev_t *pci_device = (pci_dev_t *)kalloc(sizeof(pci_dev_t)); + memcpy(pci_device, (uint64_t *)bus_addr, sizeof(pci_dev_t)); - if (pci_device->device_id == 0) - goto error; - if (pci_device->device_id == 0xFFFF) - goto error; + if (pci_device->device_id == 0 || pci_device->device_id == 0xFFFF) { + kfree(pci_device); + return; + } size_t dev; for (dev = 0; dev < 32; dev++) { enumerate_device(bus_addr, dev); } - -error: - kfree(pci_device); } void enumerate_cfg_space(config_space_mcfgt *cfg_space) { + INIT_LIST(pci_dev_list.list); + size_t i; for (i = cfg_space->start_bus; i < cfg_space->end_bus; i++) { enumerate_bus(cfg_space->base_addr, i); } } +void pci_print_all() +{ + pci_dev_list_t *curr; + list_for_each_entry(curr, (&pci_dev_list.list), list) { + pci_print_dev(&curr->dev->pci_dev); + } + printf("\n"); +} + void read_mcfgt() { uint64_t *mcfgt_addr = find_sys_table_addr("MCFG"); @@ -145,9 +154,7 @@ void read_mcfgt() for (i = 0; i < len; i++) { memcpy(cfg_space, (uint64_t *)mcfgt_cfg_addr, sizeof(config_space_mcfgt)); - /* printf("addr: 0x%x, group: %d, start: %d, stop: %d\n", cfg_space->base_addr, cfg_space->pci_seg_group, cfg_space->start_bus, cfg_space->end_bus); */ enumerate_cfg_space(cfg_space); } - printf("\n"); kfree(cfg_space); } diff --git a/kernel/src/devices/sata.c b/kernel/src/devices/sata.c new file mode 100644 index 0000000..d091941 --- /dev/null +++ b/kernel/src/devices/sata.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include +#include +#include +#include + +// 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) { + printf("SATA drive found at port %d\n", i); + } else if (dt == AHCI_DEV_SATAPI) { + printf("SATAPI drive found at port %d\n", i); + } else if (dt == AHCI_DEV_SEMB) { + printf("SEMB drive found at port %d\n", i); + } else if (dt == AHCI_DEV_PM) { + printf("PM drive found at port %d\n", i); + } + } + + pi >>= 1; + i++; + } +} + +void ahci() +{ + pci_dev_list_t *pos; + list_for_each_entry(pos, (&pci_dev_list.list), list) { + pci_dev_t *dev = &pos->dev->pci_dev; + if (dev->class_ != 0x1 || dev->subclass != 0x6) + continue; + + HBA_MEM *abar = (HBA_MEM *)(uint64_t)pos->dev->bar[5]; + printf("AHCI: %x\n", abar); + map_addr((uint64_t)abar, (uint64_t)abar, + FLAG_PRESENT | FLAG_WRITABLE | FLAG_HUGE); + + probe_port(abar); + } +} diff --git a/kernel/src/main.c b/kernel/src/main.c index ceba990..465c4b3 100644 --- a/kernel/src/main.c +++ b/kernel/src/main.c @@ -26,6 +26,7 @@ #include #include #include +#include int kernel_main(mb2_tag_header *multiboot_bootinfo, uint32_t multiboot_magic); int kernel_main(mb2_tag_header *multiboot_bootinfo, uint32_t multiboot_magic) @@ -52,6 +53,7 @@ int kernel_main(mb2_tag_header *multiboot_bootinfo, uint32_t multiboot_magic) enable_interrupts(); init_userspace(); read_mcfgt(); + ahci(); init_scheduler(); -- cgit v1.2.3