| 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 | /* Stop the port so it doesn't start overwriting potential kernel text */ |
| 39 | static void ahci_port_quiesce(struct ahci_port *port) { |
| 40 | mmio_write_32(address: &port->cmd, value: mmio_read_32(address: &port->cmd) & ~AHCI_CMD_ST); |
| 41 | mmio_spin_wait(reg: &port->cmd, AHCI_CMD_CR, AHCI_CMD_TIMEOUT_MS); |
| 42 | |
| 43 | mmio_write_32(address: &port->cmd, value: mmio_read_32(address: &port->cmd) & ~AHCI_CMD_FRE); |
| 44 | mmio_spin_wait(reg: &port->cmd, AHCI_CMD_FR, AHCI_CMD_TIMEOUT_MS); |
| 45 | |
| 46 | mmio_write_32(address: &port->clb, value: 0); |
| 47 | mmio_write_32(address: &port->clbu, value: 0); |
| 48 | mmio_write_32(address: &port->fb, value: 0); |
| 49 | mmio_write_32(address: &port->fbu, value: 0); |
| 50 | } |
| 51 | |
| 52 | static void allocate_port(struct ahci_device *dev, struct ahci_port *port, |
| 53 | uint32_t port_num) { |
| 54 | uint64_t cmdlist_phys = pmm_alloc_page(); |
| 55 | uint64_t fis_phys = pmm_alloc_page(); |
| 56 | void *cmdlist = mmio_map(phys: cmdlist_phys, PAGE_SIZE); |
| 57 | void *fis = mmio_map(phys: fis_phys, PAGE_SIZE); |
| 58 | memset(cmdlist, 0, PAGE_SIZE); |
| 59 | memset(fis, 0, PAGE_SIZE); |
| 60 | |
| 61 | mmio_write_32(address: &port->clb, value: cmdlist_phys & 0xFFFFFFFFUL); |
| 62 | mmio_write_32(address: &port->clbu, value: cmdlist_phys >> 32); |
| 63 | mmio_write_32(address: &port->fb, value: fis_phys & 0xFFFFFFFFUL); |
| 64 | mmio_write_32(address: &port->fbu, value: fis_phys >> 32); |
| 65 | struct ahci_cmd_table **arr = |
| 66 | kmalloc(sizeof(struct ahci_cmd_table *) * 32, ALLOC_FLAGS_ZERO); |
| 67 | struct ahci_cmd_header **hdr = |
| 68 | kmalloc(sizeof(struct ahci_cmd_header *) * 32, ALLOC_FLAGS_ZERO); |
| 69 | |
| 70 | if (!arr || !hdr) |
| 71 | panic("Could not allocate space for AHCI commands" ); |
| 72 | |
| 73 | struct ahci_full_port p = {.port = port, |
| 74 | .fis = fis, |
| 75 | .cmd_list_base = cmdlist, |
| 76 | .cmd_tables = arr, |
| 77 | .cmd_hdrs = hdr}; |
| 78 | dev->regs[port_num] = p; |
| 79 | } |
| 80 | |
| 81 | static struct ahci_disk *device_setup(struct ahci_device *dev, |
| 82 | struct ahci_controller *ctrl, |
| 83 | uint32_t *disk_count) { |
| 84 | uint32_t pi = mmio_read_32(address: &ctrl->pi); |
| 85 | |
| 86 | mmio_write_32(address: &dev->ctrl->ghc, value: 1 << 1); |
| 87 | uint32_t total_disks = 0; |
| 88 | |
| 89 | for (uint32_t i = 0; i < 32; i++) { |
| 90 | if (!(pi & (1U << i))) |
| 91 | continue; |
| 92 | |
| 93 | struct ahci_port *port = ahci_get_port(dev, n: i); |
| 94 | |
| 95 | ahci_port_quiesce(port); |
| 96 | |
| 97 | uint32_t ssts = mmio_read_32(address: &port->ssts); |
| 98 | uint32_t det = ssts & 0x0F; |
| 99 | uint32_t ipm = (ssts >> 8) & 0x0F; |
| 100 | if (!(det == AHCI_DET_PRESENT && ipm == AHCI_IPM_ACTIVE)) |
| 101 | continue; |
| 102 | |
| 103 | uint32_t sig = mmio_read_32(address: &port->sig); |
| 104 | ahci_log(LOG_INFO, "Controller port %u has signature %p" , i, sig); |
| 105 | |
| 106 | if (sig == 0xFFFFFFFF) |
| 107 | continue; |
| 108 | |
| 109 | if (sig != 0x00000101) { |
| 110 | ahci_log(LOG_WARN, "Controller port %u is not an HDD, skipping..." , |
| 111 | i, sig); |
| 112 | continue; |
| 113 | } |
| 114 | |
| 115 | dev->port_count++; |
| 116 | total_disks++; |
| 117 | } |
| 118 | |
| 119 | if (!total_disks) |
| 120 | return NULL; |
| 121 | |
| 122 | *disk_count = total_disks; |
| 123 | |
| 124 | struct ahci_disk *disks = |
| 125 | kmalloc(sizeof(struct ahci_disk) * total_disks, ALLOC_FLAGS_ZERO); |
| 126 | if (!disks) |
| 127 | panic("Could not allocate space for AHCI disks" ); |
| 128 | |
| 129 | uint32_t disks_ind = 0; |
| 130 | |
| 131 | for (uint32_t i = 0; i < 32; i++) { |
| 132 | if (!(pi & (1U << i))) |
| 133 | continue; |
| 134 | |
| 135 | struct ahci_port *port = ahci_get_port(dev, n: i); |
| 136 | uint32_t ssts = mmio_read_32(address: &port->ssts); |
| 137 | |
| 138 | if ((ssts & 0x0F) == AHCI_DET_PRESENT && |
| 139 | ((ssts >> 8) & 0x0F) == AHCI_IPM_ACTIVE) { |
| 140 | uint32_t sig = mmio_read_32(address: &port->sig); |
| 141 | |
| 142 | if (sig != 0x00000101) |
| 143 | continue; |
| 144 | |
| 145 | disks[disks_ind].port = i; |
| 146 | disks[disks_ind].device = dev; |
| 147 | |
| 148 | mmio_write_32(address: &port->is, value: 0xFFFFFFFF); |
| 149 | mmio_write_32(address: &port->ie, value: 0xFFFFFFFF); |
| 150 | |
| 151 | ahci_port_quiesce(port); |
| 152 | |
| 153 | /* FRE may only be set once PxFB points at our memory */ |
| 154 | allocate_port(dev, port, port_num: i); |
| 155 | mmio_write_32(address: &port->cmd, value: mmio_read_32(address: &port->cmd) | AHCI_CMD_FRE); |
| 156 | |
| 157 | /* ST is set after the port slots are setup */ |
| 158 | setup_port_slots(dev, port_id: i); |
| 159 | mmio_write_32(address: &port->cmd, value: mmio_read_32(address: &port->cmd) | AHCI_CMD_ST); |
| 160 | |
| 161 | ahci_log(LOG_INFO, "Port %u slots set up" , i); |
| 162 | } |
| 163 | } |
| 164 | return disks; |
| 165 | } |
| 166 | |
| 167 | struct ahci_disk *ahci_setup_controller(struct ahci_controller *ctrl, |
| 168 | uint32_t *d_cnt) { |
| 169 | bool s64a = mmio_read_32(address: &ctrl->cap) & (1U << 31); |
| 170 | if (!s64a) { |
| 171 | ahci_log(LOG_WARN, "controller does not support 64-bit addressing\n" ); |
| 172 | return NULL; |
| 173 | } |
| 174 | |
| 175 | mmio_write_32(address: &ctrl->ghc, AHCI_GHC_HR); |
| 176 | |
| 177 | mmio_spin_wait(reg: &ctrl->ghc, AHCI_GHC_HR, AHCI_CMD_TIMEOUT_MS); |
| 178 | |
| 179 | mmio_write_32(address: &ctrl->ghc, value: mmio_read_32(address: &ctrl->ghc) | AHCI_GHC_AE); |
| 180 | |
| 181 | struct ahci_device *dev = |
| 182 | kmalloc(sizeof(struct ahci_device), ALLOC_FLAGS_ZERO); |
| 183 | if (!dev) |
| 184 | panic("Could not allocate space for AHCI device setup" ); |
| 185 | |
| 186 | dev->ctrl = ctrl; |
| 187 | dev->irq_num = irq_alloc_entry(); |
| 188 | |
| 189 | uint32_t disk_count = 0; |
| 190 | struct ahci_disk *d = device_setup(dev, ctrl, disk_count: &disk_count); |
| 191 | *d_cnt = disk_count; |
| 192 | ahci_log(LOG_INFO, |
| 193 | "Device initialized successfully, %u usable port(s) present" , |
| 194 | disk_count); |
| 195 | |
| 196 | return d; |
| 197 | } |
| 198 | |