-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathata.c
More file actions
124 lines (103 loc) · 3.64 KB
/
Copy pathata.c
File metadata and controls
124 lines (103 loc) · 3.64 KB
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
#include "ata.h"
/* Low-level x86_64 inline Assembly I/O Helpers */
static inline void outb(uint16_t port, uint8_t val) {
__asm__ volatile ("outb %0, %1" : : "a"(val), "Nd"(port));
}
static inline uint8_t inb(uint16_t port) {
uint8_t ret;
__asm__ volatile ("inb %1, %0" : "=a"(ret) : "Nd"(port));
return ret;
}
static inline uint16_t inw(uint16_t port) {
uint16_t ret;
__asm__ volatile ("inw %1, %0" : "=a"(ret) : "Nd"(port));
return ret;
}
static inline void outw(uint16_t port, uint16_t val) {
__asm__ volatile ("outw %0, %1" : : "a"(val), "Nd"(port));
}
/* 400ns Delay required by ATA spec after changing drive selection */
static void ata_400ns_delay(void) {
inb(ATA_PRIMARY_CONTROL);
inb(ATA_PRIMARY_CONTROL);
inb(ATA_PRIMARY_CONTROL);
inb(ATA_PRIMARY_CONTROL);
}
/* Wait for BSY bit to clear */
static void ata_wait_bsy(void) {
while (inb(ATA_PRIMARY_STATUS) & ATA_SR_BSY);
}
/* Wait for DRQ bit to set (ready to transfer data) */
static int ata_wait_drq(void) {
while (1) {
uint8_t status = inb(ATA_PRIMARY_STATUS);
if (status & ATA_SR_ERR) return -1; // Disk Error
if (status & ATA_SR_DF) return -2; // Drive Fault
if (status & ATA_SR_DRQ) return 0; // Ready!
}
}
void ata_init(void) {
// Select Master Drive on Primary Bus
outb(ATA_PRIMARY_DRIVE_HEAD, 0xA0);
ata_400ns_delay();
}
/* Read single 512-byte sector using 28-bit LBA */
int ata_read_sector(uint32_t lba, uint8_t *buffer) {
ata_wait_bsy();
// Send 0xE0 | (Master Drive bit) | High 4 bits of LBA
outb(ATA_PRIMARY_DRIVE_HEAD, 0xE0 | ((lba >> 24) & 0x0F));
outb(ATA_PRIMARY_SECCOUNT, 1); // Read 1 sector
outb(ATA_PRIMARY_LBA_LO, (uint8_t)(lba)); // LBA 0-7
outb(ATA_PRIMARY_LBA_MID, (uint8_t)(lba >> 8)); // LBA 8-15
outb(ATA_PRIMARY_LBA_HI, (uint8_t)(lba >> 16)); // LBA 16-23
outb(ATA_PRIMARY_COMMAND, ATA_CMD_READ_SECTORS);
if (ata_wait_drq() != 0) {
return -1; // Drive error
}
// Read 256 16-bit words (512 bytes) from Data Port
uint16_t *buf16 = (uint16_t *)buffer;
for (size_t i = 0; i < 256; i++) {
buf16[i] = inw(ATA_PRIMARY_DATA);
}
return 0;
}
/* Write single 512-byte sector using 28-bit LBA */
int ata_write_sector(uint32_t lba, const uint8_t *buffer) {
ata_wait_bsy();
outb(ATA_PRIMARY_DRIVE_HEAD, 0xE0 | ((lba >> 24) & 0x0F));
outb(ATA_PRIMARY_SECCOUNT, 1); // Write 1 sector
outb(ATA_PRIMARY_LBA_LO, (uint8_t)(lba));
outb(ATA_PRIMARY_LBA_MID, (uint8_t)(lba >> 8));
outb(ATA_PRIMARY_LBA_HI, (uint8_t)(lba >> 16));
outb(ATA_PRIMARY_COMMAND, ATA_CMD_WRITE_SECTORS);
if (ata_wait_drq() != 0) {
return -1;
}
// Write 256 16-bit words (512 bytes) to Data Port
const uint16_t *buf16 = (const uint16_t *)buffer;
for (size_t i = 0; i < 256; i++) {
outw(ATA_PRIMARY_DATA, buf16[i]);
}
// Flush cache after write to ensure physical commit to disk
outb(ATA_PRIMARY_COMMAND, ATA_CMD_CACHE_FLUSH);
ata_wait_bsy();
return 0;
}
/* Read multiple sectors sequentially */
int ata_read_sectors(uint32_t lba, uint8_t sector_count, uint8_t *buffer) {
for (uint8_t i = 0; i < sector_count; i++) {
if (ata_read_sector(lba + i, buffer + (i * ATA_SECTOR_SIZE)) != 0) {
return -1;
}
}
return 0;
}
/* Write multiple sectors sequentially */
int ata_write_sectors(uint32_t lba, uint8_t sector_count, const uint8_t *buffer) {
for (uint8_t i = 0; i < sector_count; i++) {
if (ata_write_sector(lba + i, buffer + (i * ATA_SECTOR_SIZE)) != 0) {
return -1;
}
}
return 0;
}