| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566 | #include "ahci.h"#include <common/kprint.h>#include <mm/slab.h>#include <syscall/syscall.h>#include <syscall/syscall_num.h>#include <sched/sched.h>struct pci_device_structure_header_t *ahci_devs[MAX_AHCI_DEVICES];struct block_device_request_queue ahci_req_queue;uint32_t count_ahci_devices = 0;static uint64_t ahci_port_base_vaddr;     // 端口映射base addrstatic uint64_t ahci_port_base_phys_addr; // 端口映射的物理基地址(ahci控制器的参数的地址都是物理地址)static void start_cmd(HBA_PORT *port);static void stop_cmd(HBA_PORT *port);static void port_rebase(HBA_PORT *port, int portno);static long ahci_query_disk();// Find a free command list slotstatic 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(){    kinfo("Initializing AHCI...");    pci_get_device_structure(0x1, 0x6, ahci_devs, &count_ahci_devices);    if (count_ahci_devices == 0)    {        kwarn("There is no AHCI device found on this computer!");        return;    }    // 映射ABAR    kdebug("phys_2_virt(ahci_devs[0])= %#018lx", (ahci_devs[0]));    kdebug("((struct pci_device_structure_general_device_t *)phys_2_virt(ahci_devs[0])))->BAR5= %#018lx", ((struct pci_device_structure_general_device_t *)(ahci_devs[0]))->BAR5);    uint32_t bar5 = ((struct pci_device_structure_general_device_t *)(ahci_devs[0]))->BAR5;    mm_map_phys_addr(AHCI_MAPPING_BASE, (ul)(bar5)&PAGE_2M_MASK, PAGE_2M_SIZE, PAGE_KERNEL_PAGE | PAGE_PWT | PAGE_PCD, false);    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));        kdebug("ahci_devices[i].hba_mem = %#018lx", (ul)ahci_devices[i].hba_mem);    }    // todo: 支持多个ahci控制器。    ahci_port_base_vaddr = (uint64_t)kmalloc(1048576, 0);    kdebug("ahci_port_base_vaddr=%#018lx", ahci_port_base_vaddr);    ahci_probe_port(0);    port_rebase(&ahci_devices[0].hba_mem->ports[0], 0);    // 初始化请求队列    ahci_req_queue.in_service = NULL;    wait_queue_init(&ahci_req_queue.wait_queue_list, NULL);    ahci_req_queue.request_count = 0;    kinfo("AHCI initialized.");}// Check device typestatic 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控制器号 */static 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 enginestatic 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 enginestatic 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 = virt_2_phys(ahci_port_base_vaddr + (portno << 10));    memset((void *)(phys_2_virt(port->clb)), 0, 1024);    // FIS offset: 32K+256*portno    // FIS entry size = 256 bytes per port    port->fb = virt_2_phys(ahci_port_base_vaddr + (32 << 10) + (portno << 8));    memset((void *)(phys_2_virt(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 *)(phys_2_virt(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 = virt_2_phys((ahci_port_base_vaddr + (40 << 10) + (portno << 13) + (i << 8)));        memset((void *)phys_2_virt(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 */static 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 E_NOEMPTYSLOT;    HBA_CMD_HEADER *cmdheader = (HBA_CMD_HEADER *)phys_2_virt(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 *)phys_2_virt(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 = virt_2_phys(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 uint16_ts        count -= 16;     // 16 sectors    }    // Last entry    cmdtbl->prdt_entry[i].dba = virt_2_phys(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 = AHCI_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 & (AHCI_DEV_BUSY | AHCI_DEV_DRQ)) && spin < 1000000)    {        spin++;    }    if (spin == 1000000)    {        kerror("Port is hung");        return E_PORT_HUNG;    }    port->ci = 1 << slot; // Issue command    current_pcb->flags |= PF_NEED_SCHED;    sched();    int retval = AHCI_SUCCESS;    // 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");            retval = E_TASK_FILE_ERROR;            break;        }    }    // Check again    if (port->is & HBA_PxIS_TFES)    {        kerror("Read disk error");        retval = E_TASK_FILE_ERROR;    }    enter_syscall_int(SYS_AHCI_END_REQ, 0, 0, 0, 0, 0, 0, 0, 0);    return retval;}static bool ahci_write(HBA_PORT *port, uint32_t startl, uint32_t starth, uint32_t count,                       uint64_t buf){    // kdebug("ahci write");    port->is = 0xffff; // Clear pending interrupt bits    int slot = ahci_find_cmdslot(port);    if (slot == -1)        return E_NOEMPTYSLOT;    HBA_CMD_HEADER *cmdheader = (HBA_CMD_HEADER *)phys_2_virt(port->clb);    cmdheader += slot;    cmdheader->cfl = sizeof(FIS_REG_H2D) / sizeof(uint32_t); // Command FIS size    cmdheader->w = 1;    cmdheader->c = 1;    cmdheader->p = 1;    cmdheader->prdtl = (uint16_t)((count - 1) >> 4) + 1; // PRDT entries count    HBA_CMD_TBL *cmdtbl = (HBA_CMD_TBL *)phys_2_virt(cmdheader->ctba);    memset(cmdtbl, 0, sizeof(HBA_CMD_TBL) + (cmdheader->prdtl - 1) * sizeof(HBA_PRDT_ENTRY));    int i = 0;    for (i = 0; i < cmdheader->prdtl - 1; ++i)    {        cmdtbl->prdt_entry[i].dba = virt_2_phys(buf);        cmdtbl->prdt_entry[i].dbc = 8 * 1024 - 1; // 8K bytes        cmdtbl->prdt_entry[i].i = 0;        buf += 4 * 1024; // 4K words        count -= 16;     // 16 sectors    }    cmdtbl->prdt_entry[i].dba = virt_2_phys(buf);    cmdtbl->prdt_entry[i].dbc = count << 9; // 512 bytes per sector    cmdtbl->prdt_entry[i].i = 0;    FIS_REG_H2D *cmdfis = (FIS_REG_H2D *)(&cmdtbl->cfis);    cmdfis->fis_type = FIS_TYPE_REG_H2D;    cmdfis->c = 1; // Command    cmdfis->command = AHCI_CMD_WRITE_DMA_EXT;    cmdfis->lba0 = (uint8_t)startl;    cmdfis->lba1 = (uint8_t)(startl >> 8);    cmdfis->lba2 = (uint8_t)(startl >> 16);    cmdfis->lba3 = (uint8_t)(startl >> 24);    cmdfis->lba4 = (uint8_t)starth;    cmdfis->lba5 = (uint8_t)(starth >> 8);    cmdfis->device = 1 << 6; // LBA mode    cmdfis->countl = count & 0xff;    cmdfis->counth = count >> 8;    //    printk("[slot]{%d}", slot);    port->ci = 1; // Issue command        current_pcb->flags |= PF_NEED_SCHED;    sched();    int retval = AHCI_SUCCESS;    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("Write disk error");            retval = E_TASK_FILE_ERROR;            break;        }    }    if (port->is & HBA_PxIS_TFES)    {        kerror("Write disk error");        retval = E_TASK_FILE_ERROR;    }    // kdebug("ahci write retval=%d", retval);    enter_syscall_int(SYS_AHCI_END_REQ, 0, 0, 0, 0, 0, 0, 0, 0);    return retval;}// Find a free command list slotstatic 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;}long ahci_open(){    return 0;}long ahci_close(){    return 0;}/** * @brief 创建ahci磁盘请求包 * * @param cmd 控制命令 * @param base_addr 48位LBA地址 * @param count total sectors to read * @param buf 缓冲区线性地址 * @param ahci_ctrl_num ahci控制器号 * @param port_num ahci控制器端口号 * @return struct block_device_request_packet* */static struct ahci_request_packet_t *ahci_make_request(long cmd, uint64_t base_addr, uint64_t count, uint64_t buffer, uint8_t ahci_ctrl_num, uint8_t port_num){    struct ahci_request_packet_t *pack = (struct ahci_request_packet_t *)kmalloc(sizeof(struct ahci_request_packet_t), 0);    wait_queue_init(&pack->blk_pak.wait_queue, current_pcb);    pack->blk_pak.device_type = BLK_TYPE_AHCI;    // 由于ahci不需要中断即可读取磁盘,因此end handler为空    switch (cmd)    {    case AHCI_CMD_READ_DMA_EXT:        pack->blk_pak.end_handler = NULL;        pack->blk_pak.cmd = AHCI_CMD_READ_DMA_EXT;        break;    case AHCI_CMD_WRITE_DMA_EXT:        pack->blk_pak.end_handler = NULL;        pack->blk_pak.cmd = AHCI_CMD_WRITE_DMA_EXT;        break;    default:        pack->blk_pak.end_handler = NULL;        pack->blk_pak.cmd = cmd;        break;    }    pack->blk_pak.LBA_start = base_addr;    pack->blk_pak.count = count;    pack->blk_pak.buffer_vaddr = buffer;    pack->ahci_ctrl_num = ahci_ctrl_num;    pack->port_num = port_num;    return pack;}/** * @brief 结束磁盘请求 * */void ahci_end_request(){    ahci_req_queue.in_service->wait_queue.pcb->state = PROC_RUNNING;    // ahci_req_queue.in_service->wait_queue.pcb->flags |= PF_NEED_SCHED;    // current_pcb->flags |= PF_NEED_SCHED;    kfree((uint64_t *)ahci_req_queue.in_service);    ahci_req_queue.in_service = NULL;    // 进行下一轮的磁盘请求 (由于未实现单独的io调度器,这里会造成长时间的io等待)    if (ahci_req_queue.request_count > 0)        ahci_query_disk();}static long ahci_query_disk(){    wait_queue_node_t *wait_queue_tmp = container_of(list_next(&ahci_req_queue.wait_queue_list.wait_list), wait_queue_node_t, wait_list);    struct ahci_request_packet_t *pack = (struct ahci_request_packet_t *)container_of(wait_queue_tmp, struct block_device_request_packet, wait_queue);    ahci_req_queue.in_service = (struct block_device_request_packet *)pack;    list_del(&(ahci_req_queue.in_service->wait_queue.wait_list));    --ahci_req_queue.request_count;    long ret_val;        switch (pack->blk_pak.cmd)    {    case AHCI_CMD_READ_DMA_EXT:        ret_val = ahci_read(&(ahci_devices[pack->ahci_ctrl_num].hba_mem->ports[pack->port_num]), pack->blk_pak.LBA_start & 0xFFFFFFFF, ((pack->blk_pak.LBA_start) >> 32) & 0xFFFFFFFF, pack->blk_pak.count, pack->blk_pak.buffer_vaddr);        break;    case AHCI_CMD_WRITE_DMA_EXT:        ret_val = ahci_write(&(ahci_devices[pack->ahci_ctrl_num].hba_mem->ports[pack->port_num]), pack->blk_pak.LBA_start & 0xFFFFFFFF, ((pack->blk_pak.LBA_start) >> 32) & 0xFFFFFFFF, pack->blk_pak.count, pack->blk_pak.buffer_vaddr);        break;    default:        kerror("Unsupport ahci command: %#05lx", pack->blk_pak.cmd);        ret_val = E_UNSUPPORTED_CMD;        break;    }    // ahci_end_request();    return ret_val;}/** * @brief 将请求包提交到io队列 * * @param pack */static void ahci_submit(struct ahci_request_packet_t *pack){    list_append(&(ahci_req_queue.wait_queue_list.wait_list), &(pack->blk_pak.wait_queue.wait_list));    ++ahci_req_queue.request_count;    if (ahci_req_queue.in_service == NULL) // 当前没有正在请求的io包,立即执行磁盘请求        ahci_query_disk();}/** * @brief ahci驱动程序的传输函数 * * @param cmd 控制命令 * @param base_addr 48位LBA地址 * @param count total sectors to read * @param buf 缓冲区线性地址 * @param ahci_ctrl_num ahci控制器号 * @param port_num ahci控制器端口号 * @return long */static long ahci_transfer(long cmd, uint64_t base_addr, uint64_t count, uint64_t buf, uint8_t ahci_ctrl_num, uint8_t port_num){    struct ahci_request_packet_t *pack = NULL;    if (cmd == AHCI_CMD_READ_DMA_EXT || cmd == AHCI_CMD_WRITE_DMA_EXT)    {        pack = ahci_make_request(cmd, base_addr, count, buf, ahci_ctrl_num, port_num);        ahci_submit(pack);    }    else        return E_UNSUPPORTED_CMD;    return AHCI_SUCCESS;}/** * @brief todo: io控制器函数 * * @param cmd 命令 * @param arg 参数 * @return long */static long ahci_ioctl(long cmd, long arg){}struct block_device_operation ahci_operation =    {        .open = ahci_open,        .close = ahci_close,        .ioctl = ahci_ioctl,        .transfer = ahci_transfer,};
 |