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