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
19static 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 *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
38static 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
67static 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
164struct 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