SayoriOS 0.3.3

ata_dma.c
1 #include "common.h"
2 #include "drv/disk/ata_dma.h"
3 #include "drv/pci.h"
4 #include "io/ports.h"
5 #include "mem/vmm.h"
6 #include "mem/pmm.h"
7 #include "drv/disk/ata.h"
8 #include "debug/hexview.h"
9 #include "lib/math.h"
10
11 #define ATA_PCI_VEN 0x8086
12 #define ATA_PCI_DEV 0x7010
13
14 #define ATA_DMA_READ 0xC8
15 #define ATA_DMA_WRITE 0xCA
16
17 uint8_t ata_busnum;
18 uint8_t ata_slot;
19 uint8_t ata_func;
20
21 uint16_t ata_dma_bar4;
22
23 prdt_t* ata_dma_prdt = 0;
24 size_t ata_dma_phys_prdt = 0;
25 size_t prdt_entry_count = 16;
26
27 extern ata_drive_t drives[4];
28
29
30 void ata_dma_init() {
31 pci_find_device(ATA_PCI_VEN, ATA_PCI_DEV, &ata_busnum, &ata_slot, &ata_func);
32
33 uint16_t devnum = pci_get_device(ata_busnum, ata_slot, ata_func);
34
35 qemu_log("ATA DMA ID: %d (%x)", devnum, devnum);
36
37 if(devnum == PCI_VENDOR_NO_DEVICE) {
38 qemu_log("ATA DMA not found!");
39 return;
40 }else{
41 qemu_log("Detected ATA DMA");
42 }
43
44 qemu_log("Enabling Busmastering");
45
46 uint16_t command_register = pci_read_confspc_word(ata_busnum, ata_slot, ata_func, 4);
47 command_register |= 0x05;
48 pci_write(ata_busnum, ata_slot, ata_func, 4, command_register);
49
50 qemu_log("Enabled Busmastering!!!");
51
52 ata_dma_bar4 = pci_read_confspc_word(ata_busnum, ata_slot, ata_func, 0x20);
53
54 if(ata_dma_bar4 & 0x01) {
55 ata_dma_bar4 &= 0xfffffffc;
56 }
57
58 qemu_log("ATA DMA: BAR4: %x (%d)", ata_dma_bar4, ata_dma_bar4);
59
60 qemu_log("PRDT: %d bytes", sizeof(prdt_t));
61
62 // SETUP PRDT
63 ata_dma_prdt = kmalloc_common(sizeof(prdt_t) * prdt_entry_count, PAGE_SIZE);
64 memset(ata_dma_prdt, 0, sizeof(prdt_t) * prdt_entry_count);
65
66 ata_dma_phys_prdt = virt2phys(get_kernel_page_directory(), (virtual_addr_t) ata_dma_prdt);
67
68 qemu_log("PRDT ON: V%x; P%x", ata_dma_prdt, ata_dma_phys_prdt);
69 }
70
71 void ata_dma_clear_prdt() {
72 memset(ata_dma_prdt, 0, sizeof(prdt_t) * prdt_entry_count);
73 }
74
75 void ata_dma_set_prdt_entry(prdt_t* prdt, uint16_t index, uint32_t address, uint16_t byte_count, bool is_last) {
76 prdt[index].buffer_phys = address;
77 prdt[index].transfer_size = byte_count;
78 prdt[index].mark_end = is_last ? ATA_DMA_MARK_END : 0;
79 }
80
81 void dump_prdt(prdt_t* prdt) {
82 int i = 0;
83 size_t bytes = 0;
84 qemu_warn("Dumping PRDT:");
85 do {
86 size_t size;
87
88 if(prdt[i].transfer_size == 0)
89 size = 65536;
90 else
91 size = prdt[i].transfer_size;
92
93 qemu_log("[%d:%d] [Address: %x] -> %d", i, prdt[i].mark_end, prdt[i].buffer_phys, size);
94
95 bytes += size;
96 i++;
97 } while(prdt[i - 1].mark_end != ATA_DMA_MARK_END);
98
99 qemu_ok("Entries: %d; Bytes to process: %d", i, bytes);
100 }
101
102 status_t ata_dma_read_sector(uint8_t drive, uint8_t *buf, uint32_t lba) {
103 ON_NULLPTR(buf, {
104 qemu_err("Buffer is nullptr!");
105 return E_INVALID_BUFFER;
106 });
107
108 if (!drives[drive].online) {
109 qemu_err("Attempted read from drive that does not exist.");
110 return E_DEVICE_NOT_ONLINE;
111 }
112
113 // Clear our prdt
114 ata_dma_clear_prdt();
115
116 // Allocate a temporary buffer
117 void* temp_buf = kmalloc_common(PAGE_SIZE, PAGE_SIZE);
118 memset(temp_buf, 0, PAGE_SIZE);
119
120 // Make a physical address from a virtual to tell DMA where is our temp buffer
121 size_t phys_buf = virt2phys(get_kernel_page_directory(), (virtual_addr_t) temp_buf);
122
123 // Set only one PRDT entry to read only 512 bytes
124 ata_dma_set_prdt_entry(ata_dma_prdt, 0, phys_buf, 512, true);
125
126 // Only 28-bit LBA supported!
127 lba &= 0x00FFFFFF;
128
129 uint16_t io = 0;
130
131 // Set our port address and drive number
132 ata_set_params(drive, &io, &drive);
133
134 // Set our addresses according to IO
135 size_t status_offset = io == ATA_PRIMARY_IO ? ATA_DMA_PRIMARY_STATUS : ATA_DMA_SECONDARY_STATUS;
136 size_t prdt_offset = io == ATA_PRIMARY_IO ? ATA_DMA_PRIMARY_PRDT : ATA_DMA_SECONDARY_PRDT;
137 size_t cmd_offset = io == ATA_PRIMARY_IO ? ATA_DMA_PRIMARY_CMD : ATA_DMA_SECONDARY_CMD;
138
139 outb(ata_dma_bar4 + cmd_offset, 8);
140 outb(ata_dma_bar4 + status_offset, 6);
141
142 // Send our PRDT address
143 outl(ata_dma_bar4 + prdt_offset, ata_dma_phys_prdt);
144
145 // Select drive
146 outb(io + ATA_REG_HDDEVSEL, drive == ATA_MASTER ? 0xE0 : 0xF0);
147 outb(io + 1, 0x00);
148
149 // Write LBA
150 outb(io + ATA_REG_LBA0, (uint8_t)((lba) & 0xFF));
151 outb(io + ATA_REG_LBA1, (uint8_t)((lba >> 8) & 0xFF));
152 outb(io + ATA_REG_LBA2, (uint8_t)((lba >> 16) & 0xFF));
153
154 // Send command to read DMA!
155 outb(io + ATA_REG_COMMAND, ATA_CMD_READ_DMA);
156
157 // Start DMA transfer!
158 outb(ata_dma_bar4 + cmd_offset, 9);
159
160 while (1) {
161 int status = inb(ata_dma_bar4 + status_offset);
162 int dstatus = inb(io + ATA_REG_STATUS);
163
164 // qemu_log("Status: %x; Dstatus: %x; ERR: %d", status, dstatus, status & (1 << 1));
165
166 if (!(status & 0x04)) continue;
167 if (!(dstatus & 0x80)) break;
168 }
169
170 outb(ata_dma_bar4 + cmd_offset, 0);
171
172 memcpy(buf, temp_buf, 512);
173
174 kfree(temp_buf);
175
176 return OK;
177 }
178
179 // Internal function: do not use in production!
180 // WARNING: buffer is MUST be PAGE_SIZE aligned!
181 // WARNING: if numsects = 0 there's 256 sectors
182 status_t ata_dma_read_sectors(uint8_t drive, uint8_t *buf, uint32_t lba, uint8_t numsects) {
183 ON_NULLPTR(buf, {
184 qemu_err("Buffer is nullptr!");
185 return E_INVALID_BUFFER;
186 });
187
188 if (!drives[drive].online) {
189 qemu_err("Attempted read from drive that does not exist.");
190 return E_DEVICE_NOT_ONLINE;
191 }
192
193
194 // Clear our prdt
195 ata_dma_clear_prdt();
196
197 // Make a physical address from a virtual to tell DMA where is our temp buffer
198 size_t phys_buf = virt2phys(get_kernel_page_directory(), (virtual_addr_t)buf);
199 // qemu_log("Read: buffer at %x (P%x); LBA: %d; %d sectors", buf, phys_buf, lba, numsects);
200
201 // Fill the PRDT
202 int i = 0;
203 size_t byte_count;
204
205 if(numsects == 0)
206 byte_count = 256 * 512;
207 else
208 byte_count = numsects * 512;
209
210 // qemu_warn("Filling with: %d bytes", byte_count);
211
212 while(byte_count >= 65536) {
213 byte_count -= 65536;
214
215 ata_dma_set_prdt_entry(ata_dma_prdt, i, phys_buf + (i * 65536), 0, false);
216
217 i++;
218 }
219 // qemu_warn("Remaining bytes: %d", byte_count);
220
221 if(byte_count != 0) {
222 // qemu_ok("Zero!");
223 ata_dma_set_prdt_entry(ata_dma_prdt, i, phys_buf + (i * 65536), byte_count, true);
224 } else {
225 // qemu_ok("Not zero!");
226 ata_dma_prdt[i - 1].mark_end = ATA_DMA_MARK_END;
227 }
228
229 // dump_prdt(ata_dma_prdt);
230
231 // Only 28-bit LBA supported!
232 lba &= 0x00FFFFFF;
233
234 uint16_t io = 0;
235
236 // Set our port address and drive number
237 ata_set_params(drive, &io, &drive);
238
239 // Set our addresses according to IO
240 size_t status_offset = io == ATA_PRIMARY_IO ? ATA_DMA_PRIMARY_STATUS : ATA_DMA_SECONDARY_STATUS;
241 size_t prdt_offset = io == ATA_PRIMARY_IO ? ATA_DMA_PRIMARY_PRDT : ATA_DMA_SECONDARY_PRDT;
242 size_t cmd_offset = io == ATA_PRIMARY_IO ? ATA_DMA_PRIMARY_CMD : ATA_DMA_SECONDARY_CMD;
243
244 outb(ata_dma_bar4 + cmd_offset, 8);
245 outb(ata_dma_bar4 + status_offset, 6);
246
247 // Send our PRDT address
248 outl(ata_dma_bar4 + prdt_offset, ata_dma_phys_prdt);
249
250 // Select drive
251 outb(io + ATA_REG_HDDEVSEL, drive == ATA_MASTER ? 0xE0 : 0xF0);
252 outb(io + 1, 0x00);
253
254 // Write LBA
255 outb(io + ATA_REG_SECCOUNT0, numsects); // 0x00 is 128KB
256 outb(io + ATA_REG_LBA0, lba & 0xFF);
257 outb(io + ATA_REG_LBA1, (lba >> 8) & 0xFF);
258 outb(io + ATA_REG_LBA2, (lba >> 16) & 0xFF);
259
260 // Send command to read DMA!
261 outb(io + ATA_REG_COMMAND, ATA_CMD_READ_DMA);
262
263 // TODO: WAIT DRQ HERE
264
265 // Start DMA transfer!
266 outb(ata_dma_bar4 + cmd_offset, 9);
267
268 while (1) {
269 int status = inb(ata_dma_bar4 + status_offset);
270 int dstatus = inb(io + ATA_REG_STATUS);
271
272 // qemu_log("Status: %x; Dstatus: %x; ERR: %d", status, dstatus, status & (1 << 1));
273
274 if (!(status & 0x04)) continue;
275 if (!(dstatus & 0x80)) break;
276 }
277
278 outb(ata_dma_bar4 + cmd_offset, 0);
279
280 // int status = inb(ata_dma_bar4 + status_offset);
281 // int dstatus = inb(io + ATA_REG_STATUS);
282 //
283 // qemu_log("FINAL: Status: %x; Dstatus: %x; ERR: %d", status, dstatus, status & (1 << 1));
284
285 return OK;
286 }
287
288 // Internal function: do not use in production!
289 // WARNING: buffer is MUST be PAGE_SIZE aligned!
290 // WARNING: if numsects = 0 there's 256 sectors
291 status_t ata_dma_write_sectors(uint8_t drive, uint8_t *buf, uint32_t lba, uint8_t numsects) {
292 ON_NULLPTR(buf, {
293 qemu_err("Buffer is nullptr!");
294 return E_INVALID_BUFFER;
295 });
296
297 if (!drives[drive].online) {
298 qemu_err("Attempted read from drive that does not exist.");
299 return E_DEVICE_NOT_ONLINE;
300 }
301
302 // Clear our prdt
303 ata_dma_clear_prdt();
304
305 // Make a physical address from a virtual to tell DMA where is our temp buffer
306 size_t phys_buf = virt2phys(get_kernel_page_directory(), (virtual_addr_t)buf);
307 // qemu_log("Read: buffer at %x (P%x); LBA: %d; %d sectors", buf, phys_buf, lba, numsects);
308
309 // Fill the PRDT
310 int i = 0;
311 size_t byte_count;
312
313 if(numsects == 0)
314 byte_count = 256 * 512;
315 else
316 byte_count = numsects * 512;
317
318 // qemu_warn("Filling with: %d bytes", byte_count);
319
320 while(byte_count >= 65536) {
321 byte_count -= 65536;
322
323 ata_dma_set_prdt_entry(ata_dma_prdt, i, phys_buf + (i * 65536), 0, false);
324
325 i++;
326 }
327 // qemu_warn("Remaining bytes: %d", byte_count);
328
329 if(byte_count != 0) {
330 // qemu_ok("Zero!");
331 ata_dma_set_prdt_entry(ata_dma_prdt, i, phys_buf + (i * 65536), byte_count, true);
332 } else {
333 // qemu_ok("Not zero!");
334 ata_dma_prdt[i - 1].mark_end = ATA_DMA_MARK_END;
335 }
336
337 // dump_prdt(ata_dma_prdt);
338
339 // Only 28-bit LBA supported!
340 lba &= 0x00FFFFFF;
341
342 uint16_t io = 0;
343
344 // Set our port address and drive number
345 ata_set_params(drive, &io, &drive);
346
347 // Set our addresses according to IO
348 size_t status_offset = io == ATA_PRIMARY_IO ? ATA_DMA_PRIMARY_STATUS : ATA_DMA_SECONDARY_STATUS;
349 size_t prdt_offset = io == ATA_PRIMARY_IO ? ATA_DMA_PRIMARY_PRDT : ATA_DMA_SECONDARY_PRDT;
350 size_t cmd_offset = io == ATA_PRIMARY_IO ? ATA_DMA_PRIMARY_CMD : ATA_DMA_SECONDARY_CMD;
351
352 outb(ata_dma_bar4 + cmd_offset, 0);
353 outb(ata_dma_bar4 + status_offset, 6);
354
355 // Send our PRDT address
356 outl(ata_dma_bar4 + prdt_offset, ata_dma_phys_prdt);
357
358 // Select drive
359 outb(io + ATA_REG_HDDEVSEL, drive == ATA_MASTER ? 0xE0 : 0xF0);
360 outb(io + 1, 0x00);
361
362 // Write LBA
363 outb(io + ATA_REG_SECCOUNT0, numsects); // 0x00 is 128KB
364 outb(io + ATA_REG_LBA0, lba & 0xFF);
365 outb(io + ATA_REG_LBA1, (lba >> 8) & 0xFF);
366 outb(io + ATA_REG_LBA2, (lba >> 16) & 0xFF);
367
368 // Send command to write DMA!
369 outb(io + ATA_REG_COMMAND, ATA_CMD_WRITE_DMA);
370
371 // TODO: WAIT DRQ HERE
372
373 // Start DMA transfer!
374 outb(ata_dma_bar4 + cmd_offset, 1);
375
376 while (1) {
377 int status = inb(ata_dma_bar4 + status_offset);
378 int dstatus = inb(io + ATA_REG_STATUS);
379
380 qemu_log("Status: %x; Dstatus: %x; ERR: %d", status, dstatus, status & (1 << 1));
381
382 if (!(status & 0x04)) continue;
383 if (!(dstatus & 0x80)) break;
384 }
385
386 outb(ata_dma_bar4 + cmd_offset, 0);
387
388 return OK;
389 }
390
391 status_t ata_dma_read(uint8_t drive, char *buf, uint32_t location, uint32_t length) {
392 ON_NULLPTR(buf, {
393 qemu_err("Buffer is nullptr!");
394 return E_INVALID_BUFFER;
395 });
396
397 if(!drives[drive].online) {
398 qemu_err("Attempted read from drive that does not exist.");
399 return E_DEVICE_NOT_ONLINE;
400 }
401
402 qemu_log("DRIVE: %d; Buffer: %x, Location: %x, len: %d", drive, buf, location, length);
403
404 size_t start_sector = location / drives[drive].block_size;
405 size_t end_sector = (location + length - 1) / drives[drive].block_size;
406 size_t sector_count = end_sector - start_sector + 1;
407
408 size_t real_length = sector_count * drives[drive].block_size;
409
410 // TODO: Optimize to read without kmalloc
411 uint8_t* real_buf = kmalloc_common(real_length, PAGE_SIZE);
412
413 if(!drives[drive].is_packet) {
414 // Okay, ATA can only read 256 sectors (128 KB of memory) at one request, so subdivide our data to clusters to manage.
415 int i = 0;
416 size_t cluster_count = sector_count / 256;
417 size_t remaining_count = sector_count % 256;
418
419 for(; i < cluster_count; i++) {
420 ata_dma_read_sectors(drive, real_buf + (i * (65536 * 2)), start_sector + (i * 256), 0);
421 }
422
423 if(remaining_count != 0)
424 ata_dma_read_sectors(drive, real_buf + (i * (65536 * 2)), start_sector + (i * 256), remaining_count);
425 }
426
427 // hexview_advanced(real_buf, 512, 32, true, new_qemu_printf);
428
429 memcpy(buf, real_buf + (location % drives[drive].block_size), length);
430
431 kfree(real_buf);
432
433 return OK;
434 }
435
436 status_t ata_dma_write(uint8_t drive, const char *buf, uint32_t location, uint32_t length) {
437 ON_NULLPTR(buf, {
438 qemu_err("Buffer is nullptr!");
439 return E_INVALID_BUFFER;
440 });
441
442 if(!drives[drive].online) {
443 qemu_log("Attempted read from drive that does not exist.");
444 return E_DEVICE_NOT_ONLINE;
445 }
446
447 size_t start_sector = location / drives[drive].block_size;
448 size_t end_sector = (location + length - 1) / drives[drive].block_size;
449 size_t sector_count = end_sector - start_sector + 1;
450
451 size_t real_length = sector_count * drives[drive].block_size;
452
453 // TODO: Optimize to read without kmalloc
454 uint8_t* real_buf = kmalloc_common(real_length, PAGE_SIZE);
455
456 ata_dma_read_sectors(drive, real_buf, start_sector, sector_count);
457
458 size_t start_offset = location % drives[drive].block_size;
459 memcpy(real_buf + start_offset, buf, length);
460
461 if(!drives[drive].is_packet) {
462 // Okay, ATA can only operate with 256 sectors (128 KB of memory) at one request, so subdivide our data to clusters to manage.
463 int i = 0;
464 size_t cluster_count = sector_count / 256;
465 size_t remaining_count = sector_count % 256;
466
467 for(; i < cluster_count; i++) {
468 ata_dma_write_sectors(drive, real_buf + (i * (65536 * 2)), start_sector + (i * 256), 0);
469 }
470
471 if(remaining_count != 0)
472 ata_dma_write_sectors(drive, real_buf + (i * (65536 * 2)), start_sector + (i * 256), remaining_count);
473 }
474
475 kfree(real_buf);
476
477 return OK;
478 }
479
common.h
Основные определения ядра
memset
void * memset(void *ptr, char value, size_t num)
Заполнение массива указанными символами
Definition: string.c:203
memcpy
void * memcpy(void *restrict destination, const void *restrict source, size_t n)
Копирование непересекающихся массивов используя SSE.
Definition: string.c:173
pci_read_confspc_word
uint16_t pci_read_confspc_word(uint8_t bus, uint8_t slot, uint8_t function, uint8_t offset)
[PCI] Чтение 16-битных полей из пространства механизма конфигураций 1
Definition: pci.c:32
pci_find_device
void pci_find_device(uint16_t vendor, uint16_t device, uint8_t *bus_ret, uint8_t *slot_ret, uint8_t *func_ret)
[PCI] Поиск устройства по ID-поставшика и устройства
Definition: pci.c:362
pci_get_device
uint16_t pci_get_device(uint8_t bus, uint8_t slot, uint8_t function)
[PCI] Получение ID-Устройства
Definition: pci.c:269
ata_drive_t
Эта структура определяет каждый ATA диск в системе
Definition: ata.h:102
ata_drive_t::block_size
uint16_t block_size
Адресация по CHS?
Definition: ata.h:111
prdt
Definition: ata_dma.h:8