| 1 | #include <asm.h> |
| 2 | #include <console/printf.h> |
| 3 | #include <drivers/ahci.h> |
| 4 | #include <drivers/mmio.h> |
| 5 | #include <irq/idt.h> |
| 6 | #include <mem/alloc.h> |
| 7 | #include <mem/pmm.h> |
| 8 | #include <mem/vmm.h> |
| 9 | #include <stdbool.h> |
| 10 | #include <stdint.h> |
| 11 | #include <string.h> |
| 12 | #include <time/spin_sleep.h> |
| 13 | |
| 14 | // TODO: Hand this off to IDE if the GHC bit 31 is OFF |
| 15 | // It won't be AHCI - Sometimes we are in IDE emul mode |
| 16 | |
| 17 | /* TODO: file is very messy. clean this up you goober >:C */ |
| 18 | |
| 19 | static void setup_port_slots(struct ahci_device *dev, uint32_t port_id) { |
| 20 | struct ahci_full_port *port = &dev->regs[port_id]; |
| 21 | for (uint64_t slot = 0; slot < 32; slot++) { |
| 22 | uint64_t cmdtbl_phys = pmm_alloc_page(); |
| 23 | |
| 24 | void *cmdtbl_virt = mmio_map(phys: cmdtbl_phys, PAGE_SIZE); |
| 25 | memset(cmdtbl_virt, 0, PAGE_SIZE); |
| 26 | |
| 27 | struct ahci_cmd_header * = |
| 28 | (port->cmd_list_base + slot * sizeof(struct ahci_cmd_header)); |
| 29 | |
| 30 | cmd_header->ctba = (uint32_t) (cmdtbl_phys & 0xFFFFFFFF); |
| 31 | cmd_header->ctbau = (uint32_t) (cmdtbl_phys >> 32); |
| 32 | cmd_header->prdtl = 1; |
| 33 | port->cmd_tables[slot] = cmdtbl_virt; |
| 34 | port->cmd_hdrs[slot] = cmd_header; |
| 35 | } |
| 36 | } |
| 37 | |
| 38 | static void allocate_port(struct ahci_device *dev, struct ahci_port *port, |
| 39 | uint32_t port_num) { |
| 40 | uint64_t cmdlist_phys = pmm_alloc_page(); |
| 41 | uint64_t fis_phys = pmm_alloc_page(); |
| 42 | void *cmdlist = mmio_map(phys: cmdlist_phys, PAGE_SIZE); |
| 43 | void *fis = mmio_map(phys: fis_phys, PAGE_SIZE); |
| 44 | memset(cmdlist, 0, PAGE_SIZE); |
| 45 | memset(fis, 0, PAGE_SIZE); |
| 46 | |
| 47 | port->clb = cmdlist_phys & 0xFFFFFFFFUL; |
| 48 | port->clbu = cmdlist_phys >> 32; |
| 49 | port->fb = fis_phys & 0xFFFFFFFFUL; |
| 50 | port->fbu = fis_phys >> 32; |
| 51 | struct ahci_cmd_table **arr = |
| 52 | kmalloc(sizeof(struct ahci_cmd_table *) * 32, ALLOC_FLAGS_ZERO); |
| 53 | struct ahci_cmd_header **hdr = |
| 54 | kmalloc(sizeof(struct ahci_cmd_header *) * 32, ALLOC_FLAGS_ZERO); |
| 55 | |
| 56 | if (!arr || !hdr) |
| 57 | panic("Could not allocate space for AHCI commands" ); |
| 58 | |
| 59 | struct ahci_full_port p = {.port = port, |
| 60 | .fis = fis, |
| 61 | .cmd_list_base = cmdlist, |
| 62 | .cmd_tables = arr, |
| 63 | .cmd_hdrs = hdr}; |
| 64 | dev->regs[port_num] = p; |
| 65 | } |
| 66 | |
| 67 | static struct ahci_disk *device_setup(struct ahci_device *dev, |
| 68 | struct ahci_controller *ctrl, |
| 69 | uint32_t *disk_count) { |
| 70 | uint32_t pi = mmio_read_32(address: &ctrl->pi); |
| 71 | |
| 72 | mmio_write_32(address: &dev->ctrl->ghc, value: 1 << 1); |
| 73 | uint32_t total_disks = 0; |
| 74 | |
| 75 | for (uint32_t i = 0; i < 32; i++) { |
| 76 | if (!(pi & (1U << i))) |
| 77 | continue; |
| 78 | |
| 79 | struct ahci_port *port = ahci_get_port(dev, n: i); |
| 80 | |
| 81 | mmio_write_32(address: &port->cmd, value: mmio_read_32(address: &port->cmd) & ~AHCI_CMD_ST); |
| 82 | mmio_spin_wait(reg: &port->cmd, AHCI_CMD_CR, AHCI_CMD_TIMEOUT_MS); |
| 83 | |
| 84 | mmio_write_32(address: &port->cmd, value: mmio_read_32(address: &port->cmd) & ~AHCI_CMD_FRE); |
| 85 | mmio_spin_wait(reg: &port->cmd, AHCI_CMD_FR, AHCI_CMD_TIMEOUT_MS); |
| 86 | |
| 87 | uint32_t cmd = mmio_read_32(address: &port->cmd); |
| 88 | cmd |= AHCI_CMD_FRE | AHCI_CMD_ST; |
| 89 | mmio_write_32(address: &port->cmd, value: cmd); |
| 90 | |
| 91 | uint32_t ssts = mmio_read_32(address: &port->ssts); |
| 92 | uint32_t det = ssts & 0x0F; |
| 93 | uint32_t ipm = (ssts >> 8) & 0x0F; |
| 94 | if (!(det == AHCI_DET_PRESENT && ipm == AHCI_IPM_ACTIVE)) |
| 95 | continue; |
| 96 | |
| 97 | uint32_t sig = mmio_read_32(address: &port->sig); |
| 98 | ahci_log(LOG_INFO, "Controller port %u has signature %p" , i, sig); |
| 99 | |
| 100 | if (sig == 0xFFFFFFFF) |
| 101 | continue; |
| 102 | |
| 103 | if (sig != 0x00000101) { |
| 104 | ahci_log(LOG_WARN, "Controller port %u is not an HDD, skipping..." , |
| 105 | i, sig); |
| 106 | continue; |
| 107 | } |
| 108 | |
| 109 | dev->port_count++; |
| 110 | total_disks++; |
| 111 | } |
| 112 | |
| 113 | if (!total_disks) |
| 114 | return NULL; |
| 115 | |
| 116 | *disk_count = total_disks; |
| 117 | |
| 118 | struct ahci_disk *disks = |
| 119 | kmalloc(sizeof(struct ahci_disk) * total_disks, ALLOC_FLAGS_ZERO); |
| 120 | if (!disks) |
| 121 | panic("Could not allocate space for AHCI disks" ); |
| 122 | |
| 123 | uint32_t disks_ind = 0; |
| 124 | |
| 125 | for (uint32_t i = 0; i < 32; i++) { |
| 126 | if (!(pi & (1U << i))) |
| 127 | continue; |
| 128 | |
| 129 | struct ahci_port *port = ahci_get_port(dev, n: i); |
| 130 | uint32_t ssts = mmio_read_32(address: &port->ssts); |
| 131 | |
| 132 | if ((ssts & 0x0F) == AHCI_DET_PRESENT && |
| 133 | ((ssts >> 8) & 0x0F) == AHCI_IPM_ACTIVE) { |
| 134 | uint32_t sig = mmio_read_32(address: &port->sig); |
| 135 | |
| 136 | if (sig != 0x00000101) |
| 137 | continue; |
| 138 | |
| 139 | disks[disks_ind].port = i; |
| 140 | disks[disks_ind].device = dev; |
| 141 | |
| 142 | uint32_t cmd = mmio_read_32(address: &port->cmd); |
| 143 | mmio_write_32(address: &port->is, value: 0xFFFFFFFF); |
| 144 | mmio_write_32(address: &port->ie, value: 0xFFFFFFFF); |
| 145 | mmio_write_32(address: &port->cmd, value: cmd & ~(AHCI_CMD_ST | AHCI_CMD_FRE)); |
| 146 | |
| 147 | mmio_spin_wait(reg: &port->cmd, AHCI_CMD_CR | AHCI_CMD_FR, |
| 148 | AHCI_CMD_TIMEOUT_MS); |
| 149 | |
| 150 | allocate_port(dev, port, port_num: i); |
| 151 | |
| 152 | cmd = mmio_read_32(address: &port->cmd); |
| 153 | cmd |= AHCI_CMD_FRE; |
| 154 | cmd |= AHCI_CMD_ST; |
| 155 | mmio_write_32(address: &port->cmd, value: cmd); |
| 156 | |
| 157 | setup_port_slots(dev, port_id: i); |
| 158 | ahci_log(LOG_INFO, "Port %u slots set up" , i); |
| 159 | } |
| 160 | } |
| 161 | return disks; |
| 162 | } |
| 163 | |
| 164 | struct ahci_disk *ahci_setup_controller(struct ahci_controller *ctrl, |
| 165 | uint32_t *d_cnt) { |
| 166 | bool s64a = mmio_read_32(address: &ctrl->cap) & (1U << 31); |
| 167 | if (!s64a) { |
| 168 | ahci_log(LOG_WARN, "controller does not support 64-bit addressing\n" ); |
| 169 | return NULL; |
| 170 | } |
| 171 | |
| 172 | mmio_write_32(address: &ctrl->ghc, AHCI_GHC_HR); |
| 173 | |
| 174 | mmio_spin_wait(reg: &ctrl->ghc, AHCI_GHC_HR, AHCI_CMD_TIMEOUT_MS); |
| 175 | |
| 176 | mmio_write_32(address: &ctrl->ghc, value: mmio_read_32(address: &ctrl->ghc) | AHCI_GHC_AE); |
| 177 | |
| 178 | struct ahci_device *dev = |
| 179 | kmalloc(sizeof(struct ahci_device), ALLOC_FLAGS_ZERO); |
| 180 | if (!dev) |
| 181 | panic("Could not allocate space for AHCI device setup" ); |
| 182 | |
| 183 | dev->ctrl = ctrl; |
| 184 | dev->irq_num = irq_alloc_entry(); |
| 185 | |
| 186 | uint32_t disk_count = 0; |
| 187 | struct ahci_disk *d = device_setup(dev, ctrl, disk_count: &disk_count); |
| 188 | *d_cnt = disk_count; |
| 189 | ahci_log(LOG_INFO, |
| 190 | "Device initialized successfully, %u usable port(s) present" , |
| 191 | disk_count); |
| 192 | |
| 193 | return d; |
| 194 | } |
| 195 | |