Prechádzať zdrojové kódy

:new: 读取SATA磁盘

fslongjin 2 rokov pred
rodič
commit
8294e0d12b

+ 289 - 4
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);
+
+    kdebug("phys addr=%#018lx", (ul)(((struct pci_device_structure_general_device_t *)(ahci_devs[0]))->BAR5));
+    // 映射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, 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;
 
-    for(int i=0;i<count_ahci_devices;++i)
+    // 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++)
     {
-        kdebug("[%d]  class_code=%d, sub_class=%d, progIF=%d", i, ahci_devices[i]->Class_code, ahci_devices[i]->SubClass, ahci_devices[i]->ProgIF);
+        if ((slots & 1) == 0)
+            return i;
+        slots >>= 1;
     }
+    kerror("Cannot find free command list entry");
+    return -1;
 }

+ 51 - 11
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();
+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);

+ 0 - 1
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;
     

+ 2 - 0
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)