1#include <acpi/ioapic.h>
2#include <acpi/lapic.h>
3#include <asm.h>
4#include <block/bcache.h>
5#include <block/block.h>
6#include <block/sched.h>
7#include <compiler/core.h>
8#include <console/printf.h>
9#include <drivers/ata.h>
10#include <irq/idt.h>
11#include <math/bit.h>
12#include <mem/alloc.h>
13#include <mem/nofail.h>
14#include <smp/core.h>
15#include <stddef.h>
16#include <stdint.h>
17#include <time/spin_sleep.h>
18
19LOG_SITE_DEFINE_PRINT(ide);
20LOG_HANDLE_DEFINE_PRINT(ide);
21
22void ide_print_info(struct block_device *d) {
23 struct ata_drive *drive = (struct ata_drive *) d->driver_data;
24 if (!drive->actually_exists)
25 return;
26}
27
28static void swap_str(char *dst, const uint16_t *src, uint64_t word_len) {
29 for (uint64_t i = 0; i < word_len; i++) {
30 dst[2 * i] = (src[i] >> 8) & 0xFF;
31 dst[2 * i + 1] = src[i] & 0xFF;
32 }
33 dst[2 * word_len] = '\0';
34
35 for (int i = 2 * word_len - 1; i >= 0; --i) {
36 if (dst[i] == ' ' || dst[i] == '\0')
37 dst[i] = '\0';
38 else
39 break;
40 }
41}
42
43void ide_identify(struct ata_drive *drive) {
44 uint16_t *buf = nofail_vnews(buf, 256);
45
46 uint16_t io = drive->io_base;
47
48 outb(REG_DRIVE_HEAD(io), value: 0xA0 | (drive->slave ? 0x10 : 0x00));
49
50 outb(REG_COMMAND(io), ATA_CMD_IDENTIFY);
51
52 uint8_t status;
53
54 uint64_t timeout = IDE_IDENT_TIMEOUT_MS * 1000;
55 while ((status = inb(REG_STATUS(io))) & STATUS_BSY) {
56 sleep_spin_us(us: 10);
57 timeout--;
58 if (timeout == 0)
59 goto out;
60 }
61
62 if (status == 0 || (status & STATUS_ERR)) {
63 goto out;
64 }
65
66 timeout = IDE_IDENT_TIMEOUT_MS * 1000;
67 while (!((status = inb(REG_STATUS(io))) & STATUS_DRQ)) {
68 sleep_spin_us(us: 10);
69 timeout--;
70 if (timeout == 0)
71 goto out;
72 }
73
74 insw(REG_DATA(io), addr: buf, count: 256);
75
76 swap_str(dst: drive->serial, src: &buf[10], word_len: 10);
77 swap_str(dst: drive->firmware, src: &buf[23], word_len: 4);
78 swap_str(dst: drive->model, src: &buf[27], word_len: 20);
79
80 for (int i = 39; i >= 0 && drive->model[i] == ' '; i--)
81 drive->model[i] = '\0';
82
83 drive->supports_lba48 = BIT_TEST(buf[83], 10) ? 1 : 0;
84
85 if (drive->supports_lba48) {
86 drive->total_sectors =
87 ((uint64_t) buf[100]) | ((uint64_t) buf[101] << 16) |
88 ((uint64_t) buf[102] << 32) | ((uint64_t) buf[103] << 48);
89 } else {
90 drive->total_sectors =
91 ((uint32_t) buf[60]) | ((uint32_t) buf[61] << 16);
92 }
93
94 drive->actually_exists = drive->total_sectors != 0;
95
96 drive->supports_dma = BIT_TEST(buf[49], 8) ? 1 : 0;
97
98 drive->udma_mode = 0;
99 if (BIT_TEST(buf[88], 13)) {
100 for (int i = 0; i < 8; i++) {
101 if (BIT_TEST(buf[88], i)) {
102 drive->udma_mode = i;
103 }
104 }
105 }
106
107 drive->pio_mode = buf[64] & 0x03;
108out:
109 vfree(buf);
110}
111
112static struct bio_scheduler_ops ide_bio_ops = {
113 .should_coalesce = noop_should_coalesce,
114 /* .reorder = ide_reorder, */
115 .do_coalesce = noop_do_coalesce,
116
117 .max_wait_time =
118 {
119 [BIO_RQ_BACKGROUND] = 35,
120 [BIO_RQ_LOW] = 25,
121 [BIO_RQ_MEDIUM] = 10,
122 [BIO_RQ_HIGH] = 4,
123 [BIO_RQ_URGENT] = 0,
124 },
125
126 .dispatch_threshold = 96,
127
128 .boost_occupance_limit =
129 {
130 [BIO_RQ_BACKGROUND] = 64,
131 [BIO_RQ_LOW] = 56,
132 [BIO_RQ_MEDIUM] = 48,
133 [BIO_RQ_HIGH] = 40,
134 [BIO_RQ_URGENT] = 32,
135 },
136 .min_wait_ms = 1,
137 .tick_ms = 25,
138};
139
140struct block_device *ide_create_generic(struct ata_drive *ide) {
141 ide_identify(drive: ide);
142 if (!ide->actually_exists)
143 return NULL;
144
145 irq_t irq = irq_alloc_entry();
146 ide_log(LOG_INFO, "IDE drive IRQ on line %u, allocated entry %u", ide->irq,
147 irq);
148
149 ioapic_route_irq(irq: ide->irq, vector: irq, dest_apic_id: smp_apic_id(cpu: 0), false);
150 irq_register(name: "ata", vector: irq, handler: ide_irq_handler, ctx: &ide->channel, flags: IRQ_FLAG_NONE);
151 irq_set_chip(vector: irq, chip: lapic_get_chip(), NULL);
152 ide->channel.current_drive = ide;
153
154 struct block_device *d = nofail_vnew(d);
155
156 d->driver_data = ide;
157 d->sector_size = ide->sector_size;
158 d->read_sector = ide_read_sector_wrapper;
159 d->write_sector = ide_write_sector_wrapper;
160 d->submit_bio_async = ide_submit_bio_async;
161
162 d->flags = BDEV_FLAG_NO_COALESCE | BDEV_FLAG_NO_REORDER;
163
164 d->cache = vnew(d->cache, ALLOC_ZERO);
165 if (!d->cache)
166 panic(OUT_OF_MEMORY, "IDE drive block cache");
167
168 d->scheduler = bio_sched_create(disk: d, ops: &ide_bio_ops);
169
170 bcache_init(cache: d->cache, DEFAULT_BLOCK_CACHE_SIZE);
171
172 d->type = BDEV_IDE_DRIVE;
173 return d;
174}
175