1
2 #include <stdbool.h>
3 #include <stdint.h>
4 #include <string.h>
5
6 #include <sys/kassert.h>
7 #include <sys/kmem.h>
8 #include <sys/spinlock.h>
9 #include <sys/disk.h>
10
11 #include "ioport.h"
12 #include "../ata.h"
13
14 /*
15 * IDE Definitions
16 */
17
18 #define IDE_PRIMARY_BASE 0x1F0
19 #define IDE_PRIMARY_DEVCTL 0x3F6
20 #define IDE_PRIMARY_IRQ 14
21
22 #define IDE_SECONDARY_BASE 0x170
23 #define IDE_SECONDARY_DEVCTL 0x376
24 #define IDE_SECONDARY_IRQ 15
25
26 // Port Offsets
27 #define IDE_DATAPORT 0
28 #define IDE_FEATURES 1
29 #define IDE_SECTORCOUNT 2
30 #define IDE_LBALOW 3
31 #define IDE_LBAMID 4
32 #define IDE_LBAHIGH 5
33 #define IDE_DRIVE 6
34 #define IDE_COMMAND 7 /* Write */
35 #define IDE_STATUS 7 /* Read */
36
37 // IDE Commands (PIO)
38 #define IDE_CMD_READ 0x20
39 #define IDE_CMD_READ_EXT 0x24
40 #define IDE_CMD_WRITE 0x30
41 #define IDE_CMD_WRITE_EXT 0x34
42 #define IDE_CMD_FLUSH 0xE7
43 #define IDE_CMD_IDENTIFY 0xEC
44
45 // Status
46 #define IDE_STATUS_ERR 0x01 /* Error */
47 #define IDE_STATUS_DRQ 0x08 /* Data Ready */
48 #define IDE_STATUS_SRV 0x10 /* Overlapped-mode Service Request */
49 #define IDE_STATUS_DF 0x20 /* Drive Fault Error */
50 #define IDE_STATUS_RDY 0x40 /* Ready */
51 #define IDE_STATUS_BSY 0x80 /* Busy */
52
53 #define IDE_CONTROL_SRST 0x04 /* Software Reset */
54
55 #define IDE_SECTOR_SIZE 512
56
57 typedef struct IDE
58 {
59 uint16_t base; // Base Port
60 uint16_t devctl; // Device Control
61 uint8_t lastDriveCode; // Last Drive Code
62 Spinlock lock;
63 } IDE;
64
65 typedef struct IDEDrive
66 {
67 IDE *ide; // IDE Controller
68 int drive; // Drive Number
69 bool lba48; // Supports 48-bit LBA
70 uint64_t size; // Size of Disk
71 } IDEDrive;
72
73 bool IDE_HasController(IDE *ide);
74 void IDE_Reset(IDE *ide);
75 void IDE_Identify(IDE *ide, int drive);
76 int IDE_Read(Disk *disk, void *buf, SGArray *sga, DiskCB, void *arg);
77 int IDE_Write(Disk *disk, void *buf, SGArray *sga, DiskCB, void *arg);
78 int IDE_Flush(Disk *disk, void *buf, SGArray *sga, DiskCB, void *arg);
79 int IDE_ReadOne(IDEDrive *drive, void *buf, uint64_t off, uint64_t len);
80 int IDE_WriteOne(IDEDrive *drive, void *buf, uint64_t off, uint64_t len);
81
82 IDE primary;
83 IDEDrive primaryDrives[2];
84
85 void
IDE_Init()86 IDE_Init()
87 {
88 ASSERT(sizeof(ATAIdentifyDevice) == 512);
89
90 primary.base = IDE_PRIMARY_BASE;
91 primary.devctl = IDE_PRIMARY_DEVCTL;
92 Spinlock_Init(&primary.lock, "IDE Primary Controller Lock",
93 SPINLOCK_TYPE_NORMAL);
94
95 if (!IDE_HasController(&primary)) {
96 kprintf("IDE: No controller detected\n");
97 return;
98 }
99
100 Spinlock_Lock(&primary.lock);
101 IDE_Reset(&primary);
102 IDE_Identify(&primary, 0);
103 IDE_Identify(&primary, 1);
104 Spinlock_Unlock(&primary.lock);
105 }
106
107 int
IDEWaitForBusy(IDE * ide,bool wait)108 IDEWaitForBusy(IDE *ide, bool wait)
109 {
110 uint8_t status;
111
112 ASSERT(Spinlock_IsHeld(&ide->lock));
113
114 if (wait) {
115 inb(ide->base + IDE_STATUS);
116 inb(ide->base + IDE_STATUS);
117 inb(ide->base + IDE_STATUS);
118 inb(ide->base + IDE_STATUS);
119 }
120
121 while (1) {
122 status = inb(ide->base + IDE_STATUS);
123 if ((status & IDE_STATUS_BSY) == 0)
124 return status;
125 }
126
127 return 0xFF;
128 }
129
130 bool
IDE_HasController(IDE * ide)131 IDE_HasController(IDE *ide)
132 {
133 outb(ide->base + IDE_LBALOW, 0x41);
134 outb(ide->base + IDE_LBAMID, 0x4D);
135
136 if (inb(ide->base + IDE_LBALOW) != 0x41)
137 return false;
138 if (inb(ide->base + IDE_LBAMID) != 0x4D)
139 return false;
140
141 return true;
142 }
143
144 void
IDE_Reset(IDE * ide)145 IDE_Reset(IDE *ide)
146 {
147 uint8_t status;
148
149 outb(ide->devctl, IDE_CONTROL_SRST);
150 outb(ide->devctl, 0);
151
152 status = IDEWaitForBusy(ide, true);
153 if ((status & IDE_STATUS_RDY) != IDE_STATUS_RDY) {
154 Log(ide, "Controller not ready!\n");
155 return;
156 }
157 }
158
159 void
IDE_SwapAndTruncateString(char * str,int len)160 IDE_SwapAndTruncateString(char *str, int len)
161 {
162 int i;
163
164 ASSERT(len % 2 == 0);
165
166 for (i = 0; i < len/2; i++)
167 {
168 char tmp = str[2*i];
169 str[2*i] = str[2*i+1];
170 str[2*i+1] = tmp;
171 }
172
173 for (i = len - 1; i > 0; i--) {
174 if (str[i] == ' ' || str[i] == '\0')
175 str[i] = '\0';
176 else
177 break;
178 }
179 }
180
181 void
IDE_Identify(IDE * ide,int drive)182 IDE_Identify(IDE *ide, int drive)
183 {
184 uint8_t driveCode;
185 uint8_t status;
186 ATAIdentifyDevice ident;
187
188 ASSERT(drive == 0 || drive == 1);
189
190 if (drive == 0)
191 driveCode = 0xA0;
192 else
193 driveCode = 0xB0;
194
195 outb(ide->base + IDE_DRIVE, driveCode);
196
197 status = IDEWaitForBusy(ide, true);
198 if ((status & IDE_STATUS_ERR) != 0) {
199 Log(ide, "Error selecting drive %d\n", drive);
200 return;
201 }
202 ide->lastDriveCode = driveCode;
203
204 outb(ide->base + IDE_SECTORCOUNT, 0x00);
205 outb(ide->base + IDE_LBALOW, 0x00);
206 outb(ide->base + IDE_LBAMID, 0x00);
207 outb(ide->base + IDE_LBAHIGH, 0x00);
208 outb(ide->base + IDE_COMMAND, IDE_CMD_IDENTIFY);
209
210 status = inb(ide->base + IDE_STATUS);
211 if (status == 0) {
212 Log(ide, "Drive %d not present\n", drive);
213 return;
214 }
215
216 // XXX: Need timeout
217 while (1) {
218 if ((status & IDE_STATUS_BSY) == 0)
219 break;
220 status = inb(ide->base + IDE_STATUS);
221 }
222
223 if ((status & IDE_STATUS_ERR) != 0) {
224 Log(ide, "Error trying to identify drive %d\n", drive);
225 return;
226 }
227
228 insw(ide->base, (void *)&ident, 256);
229
230 // Cleanup model and serial for printing
231 char model[41];
232 char serial[21];
233
234 memcpy(&model[0], &ident.model[0], 40);
235 model[40] = '\0';
236 IDE_SwapAndTruncateString(&model[0], 40);
237
238 memcpy(&serial[0], &ident.serial[0], 20);
239 serial[20] = '\0';
240 IDE_SwapAndTruncateString(&serial[0], 20);
241
242 Log(ide, "Drive %d Model: %s Serial: %s\n", drive, model, serial);
243 Log(ide, "Drive %d %llu Sectors (%llu MBs)\n",
244 drive, ident.lbaSectors, ident.lbaSectors / 2048ULL);
245
246 primaryDrives[drive].ide = &primary;
247 primaryDrives[drive].drive = drive;
248 primaryDrives[drive].lba48 = (ident.lbaSectors > (1 << 24));
249 primaryDrives[drive].size = ident.lbaSectors;
250
251 // Register Disk
252 Disk *disk = PAlloc_AllocPage();
253 if (!disk) {
254 Panic("IDE: No memory!\n");
255 }
256
257 disk->handle = &primaryDrives[drive];
258 disk->ctrlNo = 0;
259 disk->diskNo = drive;
260 disk->sectorSize = IDE_SECTOR_SIZE;
261 disk->sectorCount = ident.lbaSectors;
262 disk->diskSize = IDE_SECTOR_SIZE * ident.lbaSectors;
263 disk->read = IDE_Read;
264 disk->write = IDE_Write;
265 disk->flush = IDE_Flush;
266
267 Disk_AddDisk(disk);
268 }
269
270 int
IDE_Read(Disk * disk,void * buf,SGArray * sga,DiskCB cb,void * arg)271 IDE_Read(Disk *disk, void *buf, SGArray *sga, DiskCB cb, void *arg)
272 {
273 int i;
274 int status;
275 IDEDrive *idedrive;
276
277 idedrive = disk->handle;
278
279 for (i = 0; i < sga->len; i++) {
280 status = IDE_ReadOne(idedrive,
281 buf,
282 sga->entries[i].offset / 512,
283 sga->entries[i].length / 512);
284 buf += sga->entries[i].length;
285 if (status < 0)
286 return status;
287 }
288
289 return 0;
290 }
291
292 int
IDE_Write(Disk * disk,void * buf,SGArray * sga,DiskCB cb,void * arg)293 IDE_Write(Disk *disk, void *buf, SGArray *sga, DiskCB cb, void *arg)
294 {
295 int i;
296 int status;
297 IDEDrive *idedrive;
298
299 idedrive = disk->handle;
300
301 for (i = 0; i < sga->len; i++) {
302 status = IDE_WriteOne(idedrive,
303 buf,
304 sga->entries[i].offset / 512,
305 sga->entries[i].length / 512);
306 buf += sga->entries[i].length;
307 if (status < 0)
308 return status;
309 }
310
311 return 0;
312 }
313
314 int
IDE_Flush(Disk * disk,void * buf,SGArray * sga,DiskCB cb,void * arg)315 IDE_Flush(Disk *disk, void *buf, SGArray *sga, DiskCB cb, void *arg)
316 {
317 uint8_t driveCode;
318 IDE *ide;
319 IDEDrive *idedrive;
320
321 idedrive = disk->handle;
322 ide = idedrive->ide;
323
324 if (idedrive->drive == 0)
325 driveCode = 0xA0;
326 else
327 driveCode = 0xB0;
328
329 Spinlock_Lock(&ide->lock);
330 outb(ide->base + IDE_DRIVE, driveCode);
331 outb(ide->base + IDE_COMMAND, IDE_CMD_FLUSH);
332
333 IDEWaitForBusy(ide, false);
334 Spinlock_Unlock(&ide->lock);
335
336 return 0;
337 }
338
339 int
IDE_ReadOne(IDEDrive * drive,void * buf,uint64_t off,uint64_t len)340 IDE_ReadOne(IDEDrive *drive, void *buf, uint64_t off, uint64_t len)
341 {
342 bool lba48 = false;
343 uint8_t driveCode;
344 uint8_t status;
345 IDE *ide = drive->ide;
346
347 DLOG(ide, "read %llx %llx\n", off, len);
348
349 ASSERT(drive->drive == 0 || drive->drive == 1);
350
351 if (drive->drive == 0)
352 driveCode = lba48 ? 0x40 : 0xE0;
353 else
354 driveCode = lba48 ? 0x50 : 0xF0;
355
356 ASSERT(len < 0x10000);
357
358 Spinlock_Lock(&ide->lock);
359 if (driveCode != ide->lastDriveCode) {
360 outb(ide->base + IDE_DRIVE, driveCode);
361
362 // Need to wait for select to complete
363 status = IDEWaitForBusy(ide, true);
364 if ((status & IDE_STATUS_ERR) != 0) {
365 Spinlock_Unlock(&ide->lock);
366 Log(ide, "Error selecting drive %d\n", drive->drive);
367 return -1;
368 }
369 ide->lastDriveCode = driveCode;
370 }
371
372 if (lba48) {
373 outb(ide->base + IDE_SECTORCOUNT, len >> 8);
374 outb(ide->base + IDE_LBALOW, off >> 24);
375 outb(ide->base + IDE_LBAMID, off >> 32);
376 outb(ide->base + IDE_LBAHIGH, off >> 40);
377 }
378 outb(ide->base + IDE_SECTORCOUNT, len);
379 outb(ide->base + IDE_LBALOW, off & 0xff);
380 outb(ide->base + IDE_LBAMID, (off >> 8) & 0xff);
381 outb(ide->base + IDE_LBAHIGH, (off >> 16) & 0xff);
382
383 if (lba48)
384 outb(ide->base + IDE_COMMAND, IDE_CMD_READ_EXT);
385 else
386 outb(ide->base + IDE_COMMAND, IDE_CMD_READ);
387
388 status = IDEWaitForBusy(ide, false);
389 if ((status & IDE_STATUS_ERR) != 0) {
390 Spinlock_Unlock(&ide->lock);
391 Log(ide, "Error trying read from drive %d\n", drive->drive);
392 return -1;
393 }
394
395 int sectors;
396 for (sectors = 0; sectors < len; sectors++)
397 {
398 uint8_t *b = buf + sectors * IDE_SECTOR_SIZE;
399 insw(ide->base + IDE_DATAPORT, b, 256);
400
401 status = IDEWaitForBusy(ide, true);
402 if ((status & IDE_STATUS_ERR) != 0) {
403 Spinlock_Unlock(&ide->lock);
404 Log(ide, "Error reading from drive %d\n", drive->drive);
405 return -1;
406 }
407 }
408 Spinlock_Unlock(&ide->lock);
409
410 return 0;
411 }
412
413 int
IDE_WriteOne(IDEDrive * drive,void * buf,uint64_t off,uint64_t len)414 IDE_WriteOne(IDEDrive *drive, void *buf, uint64_t off, uint64_t len)
415 {
416 bool lba48 = false;
417 uint8_t driveCode;
418 uint8_t status;
419 IDE *ide = drive->ide;
420
421 DLOG(ide, "read %llx %llx\n", off, len);
422
423 ASSERT(drive->drive == 0 || drive->drive == 1);
424
425 if (drive->drive == 0)
426 driveCode = lba48 ? 0x40 : 0xE0;
427 else
428 driveCode = lba48 ? 0x50 : 0xF0;
429
430 ASSERT(len < 0x10000);
431
432 Spinlock_Lock(&ide->lock);
433 if (driveCode != ide->lastDriveCode) {
434 outb(ide->base + IDE_DRIVE, driveCode);
435
436 // Need to wait for select to complete
437 status = IDEWaitForBusy(ide, true);
438 if ((status & IDE_STATUS_ERR) != 0) {
439 Spinlock_Unlock(&ide->lock);
440 Log(ide, "Error selecting drive %d\n", drive->drive);
441 return -1;
442 }
443 ide->lastDriveCode = driveCode;
444 }
445
446 if (lba48) {
447 outb(ide->base + IDE_SECTORCOUNT, len >> 8);
448 outb(ide->base + IDE_LBALOW, off >> 24);
449 outb(ide->base + IDE_LBAMID, off >> 32);
450 outb(ide->base + IDE_LBAHIGH, off >> 40);
451 }
452 outb(ide->base + IDE_SECTORCOUNT, len);
453 outb(ide->base + IDE_LBALOW, off & 0xff);
454 outb(ide->base + IDE_LBAMID, (off >> 8) & 0xff);
455 outb(ide->base + IDE_LBAHIGH, (off >> 16) & 0xff);
456
457 if (lba48)
458 outb(ide->base + IDE_COMMAND, IDE_CMD_WRITE_EXT);
459 else
460 outb(ide->base + IDE_COMMAND, IDE_CMD_WRITE);
461
462 status = IDEWaitForBusy(ide, false);
463 if ((status & IDE_STATUS_ERR) != 0) {
464 Spinlock_Unlock(&ide->lock);
465 Log(ide, "Error trying read from drive %d\n", drive->drive);
466 return -1;
467 }
468
469 int sectors;
470 for (sectors = 0; sectors < len; sectors++)
471 {
472 uint8_t *b = buf + sectors * IDE_SECTOR_SIZE;
473 outsw(ide->base + IDE_DATAPORT, b, 256);
474
475 status = IDEWaitForBusy(ide, true);
476 if ((status & IDE_STATUS_ERR) != 0) {
477 Spinlock_Unlock(&ide->lock);
478 Log(ide, "Error reading from drive %d\n", drive->drive);
479 return -1;
480 }
481 }
482 Spinlock_Unlock(&ide->lock);
483
484 // XXX: Flush cache ...
485
486 return 0;
487 }
488
489