CS350 COS
COS
Loading...
Searching...
No Matches
ide.c
Go to the documentation of this file.
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
57typedef struct IDE
58{
59 uint16_t base; // Base Port
60 uint16_t devctl; // Device Control
61 uint8_t lastDriveCode; // Last Drive Code
64
65typedef 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
72
73bool IDE_HasController(IDE *ide);
74void IDE_Reset(IDE *ide);
75void IDE_Identify(IDE *ide, int drive);
76int IDE_Read(Disk *disk, void *buf, SGArray *sga, DiskCB, void *arg);
77int IDE_Write(Disk *disk, void *buf, SGArray *sga, DiskCB, void *arg);
78int IDE_Flush(Disk *disk, void *buf, SGArray *sga, DiskCB, void *arg);
79int IDE_ReadOne(IDEDrive *drive, void *buf, uint64_t off, uint64_t len);
80int IDE_WriteOne(IDEDrive *drive, void *buf, uint64_t off, uint64_t len);
81
84
85void
87{
88 ASSERT(sizeof(ATAIdentifyDevice) == 512);
89
92 Spinlock_Init(&primary.lock, "IDE Primary Controller Lock",
94
96 kprintf("IDE: No controller detected\n");
97 return;
98 }
99
105}
106
107int
109{
110 uint8_t status;
111
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
130bool
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
144void
146{
147 uint8_t status;
148
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
159void
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
181void
182IDE_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);
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;
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
270int
271IDE_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
292int
293IDE_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
314int
315IDE_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);
332
333 IDEWaitForBusy(ide, false);
334 Spinlock_Unlock(&ide->lock);
335
336 return 0;
337}
338
339int
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)
385 else
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
413int
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)
459 else
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
static INLINE uint8_t inb(uint16_t port)
Definition: amd64op.h:452
static INLINE void outb(uint16_t port, uint8_t data)
Definition: amd64op.h:431
uint64_t lbaSectors
Definition: ata.h:26
uint8_t model[40]
Definition: ata.h:14
uint8_t serial[20]
Definition: ata.h:11
void Disk_AddDisk(Disk *disk)
void(* DiskCB)(int, void *)
Definition: disk.h:8
static char buf[4096]
Definition: ethdump.c:10
Spinlock lock
Definition: ide.c:62
#define IDE_STATUS_RDY
Definition: ide.c:50
int IDE_Write(Disk *disk, void *buf, SGArray *sga, DiskCB, void *arg)
Definition: ide.c:293
#define IDE_STATUS
Definition: ide.c:35
#define IDE_STATUS_ERR
Definition: ide.c:46
#define IDE_LBAMID
Definition: ide.c:31
#define IDE_DATAPORT
Definition: ide.c:27
uint16_t devctl
Definition: ide.c:60
#define IDE_SECTORCOUNT
Definition: ide.c:29
bool lba48
Definition: ide.c:69
#define IDE_STATUS_BSY
Definition: ide.c:51
void IDE_Init()
Definition: ide.c:86
#define IDE_CMD_IDENTIFY
Definition: ide.c:43
IDEDrive primaryDrives[2]
Definition: ide.c:83
int drive
Definition: ide.c:68
void IDE_Identify(IDE *ide, int drive)
Definition: ide.c:182
int IDE_ReadOne(IDEDrive *drive, void *buf, uint64_t off, uint64_t len)
Definition: ide.c:340
#define IDE_LBAHIGH
Definition: ide.c:32
IDE primary
Definition: ide.c:82
#define IDE_CMD_WRITE_EXT
Definition: ide.c:41
#define IDE_CONTROL_SRST
Definition: ide.c:53
IDE * ide
Definition: ide.c:67
int IDEWaitForBusy(IDE *ide, bool wait)
Definition: ide.c:108
int IDE_Flush(Disk *disk, void *buf, SGArray *sga, DiskCB, void *arg)
Definition: ide.c:315
bool IDE_HasController(IDE *ide)
Definition: ide.c:131
#define IDE_COMMAND
Definition: ide.c:34
uint64_t size
Definition: ide.c:70
#define IDE_CMD_FLUSH
Definition: ide.c:42
int IDE_Read(Disk *disk, void *buf, SGArray *sga, DiskCB, void *arg)
Definition: ide.c:271
int IDE_WriteOne(IDEDrive *drive, void *buf, uint64_t off, uint64_t len)
Definition: ide.c:414
#define IDE_LBALOW
Definition: ide.c:30
void IDE_SwapAndTruncateString(char *str, int len)
Definition: ide.c:160
void IDE_Reset(IDE *ide)
Definition: ide.c:145
#define IDE_SECTOR_SIZE
Definition: ide.c:55
#define IDE_CMD_READ
Definition: ide.c:38
#define IDE_PRIMARY_BASE
Definition: ide.c:18
#define IDE_CMD_WRITE
Definition: ide.c:40
#define IDE_CMD_READ_EXT
Definition: ide.c:39
#define IDE_PRIMARY_DEVCTL
Definition: ide.c:19
#define IDE_DRIVE
Definition: ide.c:33
uint8_t lastDriveCode
Definition: ide.c:61
uint16_t base
Definition: ide.c:59
Definition: ide.c:58
Definition: ide.c:66
static __inline__ void outsw(int port, const void *buf, int cnt)
Definition: ioport.h:87
static __inline__ void insw(int port, void *buf, int cnt)
Definition: ioport.h:66
#define Log(_module, _format,...)
Definition: kassert.h:32
#define DLOG(_module, _format,...)
Definition: kassert.h:37
#define ASSERT(_x)
Definition: kassert.h:8
int kprintf(const char *fmt,...)
Definition: printf.c:210
void * PAlloc_AllocPage()
Definition: palloc.c:188
pid_t wait(int *status)
Definition: process.c:49
uint64_t len
Definition: multiboot.h:2
uint64_t length
Definition: sga.h:10
SGEntry entries[SGARRAY_MAX_ENTRIES]
Definition: sga.h:16
uint32_t len
Definition: sga.h:15
uint64_t offset
Definition: sga.h:9
Definition: sga.h:14
void Spinlock_Unlock(Spinlock *lock) __UNLOCK_EX(*lock)
Definition: spinlock.c:109
#define SPINLOCK_TYPE_NORMAL
Definition: spinlock.h:12
bool Spinlock_IsHeld(Spinlock *lock) __LOCK_EX_ASSERT(*lock)
Definition: spinlock.c:126
void Spinlock_Lock(Spinlock *lock) __LOCK_EX(*lock)
Definition: spinlock.c:75
void Spinlock_Init(Spinlock *lock, const char *name, uint64_t type)
Definition: spinlock.c:43
void * memcpy(void *dst, const void *src, size_t len)
Definition: string.c:177
Definition: disk.h:11
uint64_t ctrlNo
Definition: disk.h:13
void * handle
Definition: disk.h:12
uint64_t sectorCount
Definition: disk.h:16
uint64_t diskNo
Definition: disk.h:14
int(* write)(Disk *, void *, SGArray *, DiskCB, void *)
Definition: disk.h:19
int(* flush)(Disk *, void *, SGArray *, DiskCB, void *)
Definition: disk.h:20
uint64_t diskSize
Definition: disk.h:17
uint64_t sectorSize
Definition: disk.h:15
int(* read)(Disk *, void *, SGArray *, DiskCB, void *)
Definition: disk.h:18
unsigned short uint16_t
Definition: types.h:11
unsigned long uint64_t
Definition: types.h:13
unsigned char uint8_t
Definition: types.h:10
void Panic(const char *str)
Definition: vgacons.c:164