diff --git a/kernel/driver/disk/ahci/ahci.c b/kernel/driver/disk/ahci/ahci.c index a0977ece..10ad4200 100644 --- a/kernel/driver/disk/ahci/ahci.c +++ b/kernel/driver/disk/ahci/ahci.c @@ -1,19 +1,304 @@ #include "ahci.h" #include "../../../common/kprint.h" +#include "../../../mm/slab.h" + +struct pci_device_structure_header_t *ahci_devs[MAX_AHCI_DEVICES]; -struct pci_device_structure_header_t *ahci_devices[100]; uint32_t count_ahci_devices = 0; +uint64_t ahci_port_base_vaddr; // 端口映射base addr + +static void start_cmd(HBA_PORT *port); +static void stop_cmd(HBA_PORT *port); +static void port_rebase(HBA_PORT *port, int portno); + +// Find a free command list slot +static int ahci_find_cmdslot(HBA_PORT *port); + +// 计算HBA_MEM的虚拟内存地址 +#define cal_HBA_MEM_VIRT_ADDR(device_num) (AHCI_MAPPING_BASE + (ul)(((struct pci_device_structure_general_device_t *)(ahci_devs[device_num]))->BAR5 - ((((struct pci_device_structure_general_device_t *)(ahci_devs[0]))->BAR5) & PAGE_2M_MASK))) /** * @brief 初始化ahci模块 * */ void ahci_init() { - pci_get_device_structure(0x1, 0x6, ahci_devices, &count_ahci_devices); + pci_get_device_structure(0x1, 0x6, ahci_devs, &count_ahci_devices); - for(int i=0;iBAR5)); + // 映射ABAR + mm_map_phys_addr(AHCI_MAPPING_BASE, ((ul)(((struct pci_device_structure_general_device_t *)(ahci_devs[0]))->BAR5)) & PAGE_2M_MASK, PAGE_2M_SIZE, PAGE_KERNEL_PAGE | PAGE_PWT | PAGE_PCD); + kdebug("ABAR mapped!"); + + for (int i = 0; i < count_ahci_devices; ++i) { - kdebug("[%d] class_code=%d, sub_class=%d, progIF=%d", i, ahci_devices[i]->Class_code, ahci_devices[i]->SubClass, ahci_devices[i]->ProgIF); + kdebug("[%d] class_code=%d, sub_class=%d, progIF=%d, ABAR=%#010lx", i, ahci_devs[i]->Class_code, ahci_devs[i]->SubClass, ahci_devs[i]->ProgIF, ((struct pci_device_structure_general_device_t *)(ahci_devs[i]))->BAR5); + // 赋值HBA_MEM结构体 + ahci_devices[i].dev_struct = ahci_devs[i]; + ahci_devices[i].hba_mem = (HBA_MEM *)(cal_HBA_MEM_VIRT_ADDR(i)); } + ahci_port_base_vaddr = (uint64_t)kmalloc(1048576, 0); + ahci_probe_port(0); + port_rebase(&ahci_devices[0].hba_mem->ports[0], 0); + uint64_t buf[100]; + bool res = ahci_read(&(ahci_devices[0].hba_mem->ports[0]), 0, 0, 1, (uint64_t)&buf); + kdebug("res=%d, buf[0]=%#010lx", (uint)res, (uint32_t)buf[0]); + +} + +// 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; + } +} + +/** + * @brief 检测端口连接的设备的类型 + * + * @param device_num ahci设备号 + */ +void ahci_probe_port(const uint32_t device_num) +{ + HBA_MEM *abar = ahci_devices[device_num].hba_mem; + uint32_t pi = abar->pi; + + for (int i = 0; i < 32; ++i, (pi >>= 1)) + { + if (pi & 1) + { + uint dt = check_type(&abar->ports[i]); + ahci_devices[i].type = dt; + if (dt == AHCI_DEV_SATA) + { + kdebug("SATA drive found at port %d", i); + } + else if (dt == AHCI_DEV_SATAPI) + { + kdebug("SATAPI drive found at port %d", i); + } + else if (dt == AHCI_DEV_SEMB) + { + kdebug("SEMB drive found at port %d", i); + } + else if (dt == AHCI_DEV_PM) + { + kdebug("PM drive found at port %d", i); + } + else + { + kdebug("No drive found at port %d", i); + } + } + } +} + +// Start command engine +static 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 +static 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; + } +} + +static void port_rebase(HBA_PORT *port, int portno) +{ + + // Before rebasing Port memory space, OS must wait for current pending commands to finish + // and tell HBA to stop receiving FIS from the port. Otherwise an accidently incoming FIS may be + // written into a partially configured memory area. + + 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_port_base_vaddr + (portno << 10); + + memset((void *)(port->clb), 0, 1024); + + // FIS offset: 32K+256*portno + // FIS entry size = 256 bytes per port + port->fb = ahci_port_base_vaddr + (32 << 10) + (portno << 8); + + 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_port_base_vaddr + (40 << 10) + (portno << 13) + (i << 8); + + memset((void *)cmdheader[i].ctba, 0, 256); + } + + start_cmd(port); // Start command engine +} + +/** + * @brief read data from SATA device using 48bit LBA address + * + * @param port HBA PORT + * @param startl low 32bits of start addr + * @param starth high 32bits of start addr + * @param count total sectors to read + * @param buf buffer + * @return true done + * @return false failed + */ +bool ahci_read(HBA_PORT *port, uint32_t startl, uint32_t starth, uint32_t count, uint64_t buf) +{ + port->is = (uint32_t)-1; // Clear pending interrupt bits + int spin = 0; // Spin lock timeout counter + int slot = ahci_find_cmdslot(port); + + if (slot == -1) + return false; + + HBA_CMD_HEADER *cmdheader = (HBA_CMD_HEADER *)port->clb; + cmdheader += slot; + cmdheader->cfl = sizeof(FIS_REG_H2D) / sizeof(uint32_t); // Command FIS size + cmdheader->w = 0; // Read from device + cmdheader->prdtl = (uint16_t)((count - 1) >> 4) + 1; // PRDT entries count + + HBA_CMD_TBL *cmdtbl = (HBA_CMD_TBL *)(cmdheader->ctba); + memset(cmdtbl, 0, sizeof(HBA_CMD_TBL) + (cmdheader->prdtl - 1) * sizeof(HBA_PRDT_ENTRY)); + + + // 8K bytes (16 sectors) per PRDT + int i; + for (i = 0; i < cmdheader->prdtl - 1; ++i) + { + cmdtbl->prdt_entry[i].dba = buf; + cmdtbl->prdt_entry[i].dbc = 8 * 1024 - 1; // 8K bytes (this value should always be set to 1 less than the actual value) + cmdtbl->prdt_entry[i].i = 1; + buf += 4 * 1024; // 4K words + count -= 16; // 16 sectors + } + + // Last entry + cmdtbl->prdt_entry[i].dba = buf; + cmdtbl->prdt_entry[i].dbc = (count << 9) - 1; // 512 bytes per sector + cmdtbl->prdt_entry[i].i = 1; + + // Setup command + FIS_REG_H2D *cmdfis = (FIS_REG_H2D *)(&cmdtbl->cfis); + + cmdfis->fis_type = FIS_TYPE_REG_H2D; + cmdfis->c = 1; // Command + cmdfis->command = ATA_CMD_READ_DMA_EXT; + + cmdfis->lba0 = (uint8_t)startl; + cmdfis->lba1 = (uint8_t)(startl >> 8); + cmdfis->lba2 = (uint8_t)(startl >> 16); + cmdfis->device = 1 << 6; // LBA mode + + cmdfis->lba3 = (uint8_t)(startl >> 24); + cmdfis->lba4 = (uint8_t)starth; + cmdfis->lba5 = (uint8_t)(starth >> 8); + + cmdfis->countl = count & 0xFF; + cmdfis->counth = (count >> 8) & 0xFF; + + // The below loop waits until the port is no longer busy before issuing a new command + while ((port->tfd & (ATA_DEV_BUSY | ATA_DEV_DRQ)) && spin < 1000000) + { + spin++; + } + if (spin == 1000000) + { + kerror("Port is hung"); + return false; + } + + kdebug("slot=%d", slot); + port->ci = 1 << slot; // Issue command + + // Wait for completion + while (1) + { + // In some longer duration reads, it may be helpful to spin on the DPS bit + // in the PxIS port field as well (1 << 5) + if ((port->ci & (1 << slot)) == 0) + break; + if (port->is & HBA_PxIS_TFES) // Task file error + { + kerror("Read disk error"); + return false; + } + } + + // Check again + if (port->is & HBA_PxIS_TFES) + { + kerror("Read disk error"); + return false; + } + + return true; +} + +// Find a free command list slot +static int ahci_find_cmdslot(HBA_PORT *port) +{ + // If not set in SACT and CI, the slot is free + uint32_t slots = (port->sact | port->ci); + int num_of_cmd_clots = (ahci_devices[0].hba_mem->cap&0x0f00)>>8; // bit 12-8 + for (int i = 0; i < num_of_cmd_clots; i++) + { + if ((slots & 1) == 0) + return i; + slots >>= 1; + } + kerror("Cannot find free command list entry"); + return -1; } \ No newline at end of file diff --git a/kernel/driver/disk/ahci/ahci.h b/kernel/driver/disk/ahci/ahci.h index 335bd401..dea6aa57 100644 --- a/kernel/driver/disk/ahci/ahci.h +++ b/kernel/driver/disk/ahci/ahci.h @@ -2,6 +2,24 @@ #include "../block_device.h" #include "../../pci/pci.h" +#include "../../../mm/mm.h" + +#define AHCI_MAPPING_BASE SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE+AHCI_MAPPING_OFFSET + +#define MAX_AHCI_DEVICES 100 + +#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 + +#define ATA_CMD_READ_DMA_EXT 0x25 +#define ATA_CMD_WRITE_DMA_EXT 0x30 + +#define HBA_PxIS_TFES (1 << 30) /* TFES - Task File Error Status */ /** * @brief 在SATA3.0规范中定义的Frame Information Structure类型 @@ -180,10 +198,8 @@ typedef struct tagFIS_DMA_SETUP 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 + uint64_t clb; // 0x00, command list base address, 1K-byte aligned + uint64_t fb; // 0x08, FIS base address, 256-byte aligned uint32_t is; // 0x10, interrupt status uint32_t ie; // 0x14, interrupt enable uint32_t cmd; // 0x18, command and status @@ -222,7 +238,7 @@ typedef volatile struct tagHBA_MEM uint8_t vendor[0x100-0xA0]; // 0x100 - 0x10FF, Port control registers - HBA_PORT ports[1]; // 1 ~ 32 + HBA_PORT ports[32]; // 1 ~ 32 } HBA_MEM; @@ -273,8 +289,7 @@ typedef struct tagHBA_CMD_HEADER 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 + uint64_t ctba; // Command table descriptor base address // DW4 - 7 uint32_t rsv1[4]; // Reserved @@ -282,8 +297,7 @@ typedef struct tagHBA_CMD_HEADER typedef struct tagHBA_PRDT_ENTRY { - uint32_t dba; // Data base address - uint32_t dbau; // Data base address upper 32 bits + uint64_t dba; // Data base address uint32_t rsv0; // Reserved // DW3 @@ -307,7 +321,13 @@ typedef struct tagHBA_CMD_TBL // 0x80 HBA_PRDT_ENTRY prdt_entry[1]; // Physical region descriptor table entries, 0 ~ 65535 } HBA_CMD_TBL; - + +struct ahci_device_t +{ + uint32_t type; // 设备类型 + struct pci_device_structure_header_t * dev_struct; + HBA_MEM * hba_mem; +}ahci_devices[MAX_AHCI_DEVICES]; #define SATA_SIG_ATA 0x00000101 // SATA drive @@ -342,4 +362,24 @@ struct block_device_operation ahci_operation = * @brief 初始化ahci模块 * */ -void ahci_init(); \ No newline at end of file +void ahci_init(); + +/** + * @brief 检测端口连接的设备的类型 + * + * @param device_num ahci设备号 + */ +void ahci_probe_port(const uint32_t device_num); + +/** + * @brief read data from SATA device using 48bit LBA address + * + * @param port HBA PORT + * @param startl low 32bits of start addr + * @param starth high 32bits of start addr + * @param count total sectors to read + * @param buf buffer + * @return true done + * @return false failed + */ +bool ahci_read(HBA_PORT *port, uint32_t startl, uint32_t starth, uint32_t count, uint64_t buf); \ No newline at end of file diff --git a/kernel/driver/pci/pci.c b/kernel/driver/pci/pci.c index 75660902..be7eed1b 100644 --- a/kernel/driver/pci/pci.c +++ b/kernel/driver/pci/pci.c @@ -586,7 +586,6 @@ int pci_enable_msi(void *header, uint8_t vector, uint32_t processor, uint8_t edg void pci_get_device_structure(uint8_t class_code, uint8_t sub_class, struct pci_device_structure_header_t *res[], uint32_t *count_res) { - struct pci_device_structure_header_t *ptr_begin = container_of(pci_device_structure_list, struct pci_device_structure_header_t, list); struct pci_device_structure_header_t *ptr = container_of(pci_device_structure_list, struct pci_device_structure_header_t, list); *count_res = 0; diff --git a/kernel/mm/mm.h b/kernel/mm/mm.h index 4be0a17a..bbe4792e 100644 --- a/kernel/mm/mm.h +++ b/kernel/mm/mm.h @@ -42,6 +42,8 @@ #define ACPI_XSDT_MAPPING_OFFSET 0x9000000UL #define IO_APIC_MAPPING_OFFSET 0xfec00000UL #define LOCAL_APIC_MAPPING_OFFSET 0xfee00000UL +#define AHCI_MAPPING_OFFSET 0xff200000UL // AHCI 映射偏移量,之后使用了4M的地址 + // ===== 内存区域属性 ===== // DMA区域 #define ZONE_DMA (1 << 0)