1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
#include "ata.h"
#include "halt.h"
#include "heap.h"
#include "io.h"
#include "font.h"
#define ATA_IO_DATA 0
#define ATA_IO_ERR 1
#define ATA_IO_FEATURES 1
#define ATA_IO_SECTORS 2
#define ATA_IO_LBA_LOW 3
#define ATA_IO_LBA_MID 4
#define ATA_IO_LBA_HIGH 5
#define ATA_IO_HEAD 6
#define ATA_IO_STATUS 7
#define ATA_IO_COMMAND 7
#define IO_ATA0_DATA 0x1F0
#define IO_ATA0_CTRL 0x3F6
#define ATA_CMD_IDENTIFY 0xEC
#define ATA_CMD_READ_SECTORS_EXT 0x24
typedef struct __attribute__((packed)) {
bool err : 1;
bool idx : 1;
bool corr : 1; // corrected data
bool drq : 1; // has data or ready to accept data
bool srv : 1; // overlapped
bool df : 1; // drive fault
bool rdy : 1; // clear if spun down or error
bool bsy : 1; // preparing/busy
} ata_status;
typedef struct {
unsigned int bits : 4;
bool doll : 1;
bool one0 : 1;
bool lba : 1;
bool one1 : 1;
} __attribute__((packed)) ata_head;
void ata_recv(u16 *buffer)
{
for (;;) {
ata_status status = BITCAST(inb(IO_ATA0_DATA + ATA_IO_STATUS), u8, ata_status);
if (status.err) {
u8 err = inb(IO_ATA0_DATA + ATA_IO_ERR);
str errors[] = {
S("address mark not found\n"),
S("track zero not found\n"),
S("aborted command\n"),
S("media change request\n"),
S("id not found\n"),
S("media changed\n"),
S("uncorrectable data error\n"),
S("bad block detected\n"),
};
for (int i = 0; i < 8; i++)
if (err & (1 << i))
print(errors[i]);
panic(S("ata0-witch error\n"));
} else if (status.drq)
break;
}
for (int i = 0; i < 256; i++)
buffer[i] = inw(IO_ATA0_DATA + ATA_IO_DATA);
}
void ata_delay()
{
for (int i = 0; i < 15; i++)
inb(IO_ATA0_DATA + ATA_IO_STATUS);
}
void ata_init()
{
u8 floating = inb(IO_ATA0_DATA + ATA_IO_STATUS);
if (floating == 0xFF)
panic(S("ata0 floating\n"));
outb(IO_ATA0_DATA + ATA_IO_HEAD, BITCAST(((ata_head) {
.bits = 0,
.doll = false,
.one0 = true,
.lba = false,
.one1 = true,
}), ata_head, u8));
ata_delay();
outb(IO_ATA0_DATA + ATA_IO_LBA_LOW, 0);
outb(IO_ATA0_DATA + ATA_IO_LBA_MID, 0);
outb(IO_ATA0_DATA + ATA_IO_LBA_HIGH, 0);
outb(IO_ATA0_DATA + ATA_IO_COMMAND, ATA_CMD_IDENTIFY);
u8 status_byte = inb(IO_ATA0_DATA + ATA_IO_STATUS);
if (status_byte == 0)
panic(S("no ata0-witch drive\n"));
while (BITCAST(status_byte, u8, ata_status).bsy)
status_byte = inb(IO_ATA0_DATA + ATA_IO_STATUS);
if (inb(IO_ATA0_DATA + ATA_IO_LBA_MID) != 0 || inb(IO_ATA0_DATA + ATA_IO_LBA_HIGH) != 0)
panic(S("ata0-witch is not ATA\n"));
u16 *idvec = malloc(256 * sizeof *idvec);
ata_recv(idvec);
if (!(idvec[83] & (1 << 10)))
panic(S("ata0-witch does not support LBA48 mode\n"));
// u64 lba48_sectors = *(u64 *) &idvec[100];
// print_num(lba48_sectors, 10, 0); print("\n");
free(idvec);
}
void ata_read(u64 lba, u16 sectors, void *buffer)
{
outb(IO_ATA0_DATA + ATA_IO_HEAD, BITCAST(((ata_head) {
.bits = 0,
.doll = false,
.one0 = true,
.lba = true,
.one1 = true,
}), ata_head, u8));
ata_delay();
outb(IO_ATA0_DATA + ATA_IO_SECTORS, sectors >> 8);
outb(IO_ATA0_DATA + ATA_IO_LBA_LOW, (lba >> 24) & 0xff);
outb(IO_ATA0_DATA + ATA_IO_LBA_MID, (lba >> 32) & 0xff);
outb(IO_ATA0_DATA + ATA_IO_LBA_HIGH, (lba >> 40) & 0xff);
outb(IO_ATA0_DATA + ATA_IO_SECTORS, sectors & 0xff);
outb(IO_ATA0_DATA + ATA_IO_LBA_LOW, lba & 0xff);
outb(IO_ATA0_DATA + ATA_IO_LBA_MID, (lba >> 8) & 0xff);
outb(IO_ATA0_DATA + ATA_IO_LBA_HIGH, (lba >> 16) & 0xff);
outb(IO_ATA0_DATA + ATA_IO_COMMAND, ATA_CMD_READ_SECTORS_EXT);
for (u16 i = 0; i < sectors; i++)
ata_recv(buffer + i*512);
}
void *ata_read_full(u64 lba, u64 sectors)
{
void *buffer = malloc(512 * sectors);
for (u64 off = 0; off < sectors; off += 0x10000) {
u64 sects = sectors - off;
if (sects >= 0x10000)
sects = 0;
ata_read(lba + off, (u16) sects, buffer + off * 512);
}
return buffer;
}
|