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 
Основные определения ядра
void * memset(void *ptr, char value, size_t num)
Заполнение массива указанными символами
Definition: string.c:203
void * memcpy(void *restrict destination, const void *restrict source, size_t n)
Копирование непересекающихся массивов используя SSE.
Definition: string.c:173
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
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
uint16_t pci_get_device(uint8_t bus, uint8_t slot, uint8_t function)
[PCI] Получение ID-Устройства
Definition: pci.c:269
Эта структура определяет каждый ATA диск в системе
Definition: ata.h:102
uint16_t block_size
Адресация по CHS?
Definition: ata.h:111
Definition: ata_dma.h:8