1#include <acpi/ioapic.h>
2#include <acpi/lapic.h>
3#include <drivers/ahci.h>
4#include <drivers/mmio.h>
5#include <drivers/pci.h>
6#include <irq/idt.h>
7#include <math/align.h>
8#include <mem/alloc.h>
9#include <mem/vmm.h>
10#include <registry.h>
11#include <smp/core.h>
12#include <stdbool.h>
13#include <stddef.h>
14#include <stdint.h>
15
16LOG_SITE_DEFINE_PRINT(ahci);
17LOG_HANDLE_DEFINE_PRINT(ahci);
18
19struct ahci_disk *ahci_discover_device(uint8_t bus, uint8_t device,
20 uint8_t function,
21 uint32_t *out_disk_count) {
22 ahci_log(LOG_INFO, "Found device at %02x:%02x.%x", bus, device, function);
23 uint32_t abar = pci_read(bus, slot: device, func: function, PCI_BAR5);
24 uint32_t abar_base = abar & ~0xFU;
25
26 pci_write(bus, slot: device, func: function, PCI_BAR5, value: 0xFFFFFFFF);
27 uint32_t size_mask = pci_read(bus, slot: device, func: function, PCI_BAR5);
28 pci_write(bus, slot: device, func: function, PCI_BAR5, value: abar);
29
30 if (size_mask == 0 || size_mask == 0xFFFFFFFF) {
31 ahci_log(LOG_WARN, "invalid BAR size");
32 return NULL;
33 }
34
35 uint64_t abar_size = ~(size_mask & ~0xFU) + 1;
36 uint64_t map_size = PAGE_ALIGN_UP(abar_size);
37
38 void cc_mem_io *abar_virt = mmio_map(phys: abar_base, size: map_size);
39 if (!abar_virt) {
40 ahci_log(LOG_ERROR, "failed to map BAR - likely OOM error");
41 return NULL;
42 }
43 irq_t irq_line = pci_read(bus, slot: device, func: function, PCI_INTERRUPT_LINE);
44
45 ahci_log(LOG_INFO, "AHCI device uses IRQ %u ", irq_line);
46
47 struct ahci_controller cc_mem_io *ctrl =
48 (struct ahci_controller cc_mem_io *) abar_virt;
49
50 struct ahci_disk *disk = ahci_setup_controller(ctrl, d_cnt: out_disk_count);
51 if (!disk) {
52 ahci_log(LOG_WARN, "AHCI device unsupported");
53 return NULL;
54 }
55
56 cpu_id_t core = 0; /* TODO: BSP only for now */
57 irq_register(name: "ahci", vector: disk->device->irq_num, handler: ahci_isr_handler, ctx: disk->device,
58 flags: IRQ_FLAG_NONE);
59 irq_set_chip(vector: disk->device->irq_num, chip: lapic_get_chip(), NULL);
60 ioapic_route_irq(irq: irq_line, vector: disk->device->irq_num, dest_apic_id: smp_apic_id(cpu: core), false);
61 return disk;
62}
63
64void ahci_print_wrapper(struct block_device *d) {
65 struct ahci_disk *a = d->driver_data;
66 ahci_identify(disk: a);
67}
68
69static struct bio_scheduler_ops ahci_sata_ssd_ops = {
70 .should_coalesce = noop_should_coalesce,
71 .reorder = noop_reorder,
72 .do_coalesce = noop_do_coalesce,
73 .max_wait_time =
74 {
75 [BIO_RQ_BACKGROUND] = 30,
76 [BIO_RQ_LOW] = 20,
77 [BIO_RQ_MEDIUM] = 15,
78 [BIO_RQ_HIGH] = 10,
79 [BIO_RQ_URGENT] = 0,
80 },
81 .dispatch_threshold = 96,
82 .boost_occupance_limit =
83 {
84 [BIO_RQ_BACKGROUND] = 50,
85 [BIO_RQ_LOW] = 40,
86 [BIO_RQ_MEDIUM] = 30,
87 [BIO_RQ_HIGH] = 20,
88 [BIO_RQ_URGENT] = 8,
89 },
90 .min_wait_ms = 2,
91 .tick_ms = 25,
92};
93
94struct block_device *ahci_create_generic(struct ahci_disk *disk) {
95 struct block_device *d = vnew(d, ALLOC_ZERO);
96 if (!d)
97 ahci_log(LOG_ERROR, "could not allocate space for device");
98
99 ahci_identify(disk);
100
101 d->flags = BDEV_FLAG_NO_COALESCE | BDEV_FLAG_NO_REORDER;
102 d->driver_data = disk;
103 d->sector_size = disk->sector_size;
104 d->read_sector = ahci_read_sector_wrapper;
105 d->write_sector = ahci_write_sector_wrapper;
106 d->submit_bio_async = ahci_submit_bio_request;
107 d->cache = vnew(d->cache, ALLOC_ZERO);
108 if (!d->cache)
109 panic(OUT_OF_MEMORY, "AHCI device block cache");
110
111 d->scheduler = bio_sched_create(disk: d, ops: &ahci_sata_ssd_ops);
112 bcache_init(cache: d->cache, DEFAULT_BLOCK_CACHE_SIZE);
113 d->type = BDEV_AHCI_DRIVE;
114 return d;
115}
116
117static uint64_t ahci_cnt = 1;
118
119static enum err ahci_pci_init(struct device *device) {
120 struct pci_device *dev = device->driver_data;
121 uint8_t bus = dev->bus, slot = dev->dev, func = dev->function;
122 uint32_t d_cnt = 0;
123 struct ahci_disk *disks = ahci_discover_device(bus, device: slot, function: func, out_disk_count: &d_cnt);
124 for (uint32_t i = 0; i < d_cnt; i++) {
125 struct block_device *disk = ahci_create_generic(disk: &disks[i]);
126 registry_mkname(disk, prefix: "sata", counter: ahci_cnt++);
127 registry_register(disk);
128 k_print_register(disk->name);
129 }
130
131 return ERR_OK;
132}
133
134PCI_DEV_REGISTER(ahci, 1, 6, 1, 0xFFFF, ahci_pci_init)
135