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