Skip to content

Commit 2d8ae03

Browse files
committed
Added ATA support to Avery
1 parent f22c1c8 commit 2d8ae03

8 files changed

Lines changed: 328 additions & 3 deletions

File tree

include/drivers/ata.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* ata.h
3+
* As part of the Avery project
4+
* Created by Max Van den Eynde in 2026
5+
* --------------------------------------
6+
* Description: ATA device manager
7+
* Copyright (c) 2026 Max Van den Eynde
8+
*/
9+
10+
#ifndef AVERY_ATA_H
11+
#define AVERY_ATA_H
12+
#include "driver.h"
13+
#include "pci.h"
14+
15+
class ATADiskDevice final : public BlockDevice {
16+
public:
17+
ATADiskDevice(PCIDevice* controller, u16 ioBase, u16 ctrlBase, u8 drive, u64 sectors);
18+
bool readBlocks(u64 lba, u32 count, void* buffer) override;
19+
bool writeBlocks(u64 lba, u32 count, const void* buffer) override;
20+
21+
private:
22+
bool access(bool write, u64 lba, u32 count, void* buffer);
23+
24+
u16 ioBase;
25+
u8 drive;
26+
};
27+
28+
class ATADriver final : public Driver {
29+
public:
30+
string name() const override {
31+
return "ATA Driver - Avery";
32+
}
33+
34+
bool probe(Device& device) override;
35+
bool start(Device& device) override;
36+
bool stop(Device& device) override;
37+
};
38+
39+
namespace ata {
40+
static ATADriver* driver = nullptr;
41+
void registerDriver();
42+
}
43+
44+
#endif //AVERY_ATA_H

include/drivers/driver.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,6 @@ class DriverManager {
124124
static bool tryBind(Driver& driver);
125125
static void unbind(Device& device);
126126

127-
private:
128127
static constexpr usize MaxDrivers = 128;
129128
static Driver* drivers[MaxDrivers];
130129
static usize driverCount;

include/io/io.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ namespace io {
3232
return val;
3333
}
3434

35+
inline void wait() {
36+
outb(0x80, 0);
37+
}
38+
3539
inline void outl(u16 port, u32 value) {
3640
asm volatile(
3741
"outl %0, %1"
@@ -40,6 +44,14 @@ namespace io {
4044
);
4145
}
4246

47+
inline void outw(u16 port, u16 value) {
48+
asm volatile(
49+
"outw %0, %1"
50+
:
51+
: "a"(value), "Nd"(port)
52+
);
53+
}
54+
4355
inline u32 inl(u16 port) {
4456
u32 val;
4557

@@ -51,5 +63,17 @@ namespace io {
5163

5264
return val;
5365
}
66+
67+
inline u16 inw(u16 port) {
68+
u16 val;
69+
70+
asm volatile(
71+
"inw %1, %0"
72+
: "=a"(val)
73+
: "Nd"(port)
74+
);
75+
76+
return val;
77+
}
5478
}
5579
#endif //AVERY_IO_H

kernel/drivers/drivers.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99

1010
#include <drivers/driver.h>
1111

12+
#include "drivers/ata.h"
1213
#include "drivers/keyboard.h"
1314
#include "drivers/pit.h"
15+
#include "kernel/debug.h"
1416

1517
Device* DeviceManager::devices[MaxDevices] = {};
1618
usize DeviceManager::s_deviceCount = 0;
@@ -184,4 +186,10 @@ void drivers::init() {
184186
time::registerDevice();
185187
keyboard::registerDriver();
186188
keyboard::registerDevice();
189+
ata::registerDriver();
190+
191+
for (usize i = 0; i < DriverManager::driverCount; i++) {
192+
auto* driver = DriverManager::drivers[i];
193+
debug::log("Registered driver ", i, ": ", driver->name());
194+
}
187195
}

kernel/drivers/input/keyboard.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ namespace {
153153
class KeyboardDriver final : public Driver {
154154
public:
155155
string name() const override {
156-
return "ps2-keyboard";
156+
return "PS/2 Keyboard - Avery";
157157
}
158158

159159
bool probe(Device& device) override {

kernel/drivers/storage/ata.cpp

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
/*
2+
* ata.cpp
3+
* As part of the Avery project
4+
* Created by Max Van den Eynde in 2026
5+
* --------------------------------------
6+
* Description: ATA implementation
7+
* Copyright (c) 2026 Max Van den Eynde
8+
*/
9+
10+
#include "drivers/ata.h"
11+
12+
#include "types.h"
13+
#include "io/io.h"
14+
#include "kernel/debug.h"
15+
16+
namespace {
17+
constexpr u8 ATA_REG_DATA = 0x00;
18+
constexpr u8 ATA_REG_SECCOUNT0 = 0x02;
19+
constexpr u8 ATA_REG_LBA0 = 0x03;
20+
constexpr u8 ATA_REG_LBA1 = 0x04;
21+
constexpr u8 ATA_REG_LBA2 = 0x05;
22+
constexpr u8 ATA_REG_HDDEVSEL = 0x06;
23+
constexpr u8 ATA_REG_COMMAND = 0x07;
24+
constexpr u8 ATA_REG_STATUS = 0x07;
25+
26+
constexpr u8 ATA_SR_BSY = 0x80;
27+
constexpr u8 ATA_SR_DF = 0x20;
28+
constexpr u8 ATA_SR_DRQ = 0x08;
29+
constexpr u8 ATA_SR_ERR = 0x01;
30+
31+
constexpr u8 ATA_CMD_READ_PIO = 0x20;
32+
constexpr u8 ATA_CMD_WRITE_PIO = 0x30;
33+
constexpr u8 ATA_CMD_CACHE_FLUSH = 0xE7;
34+
constexpr u8 ATA_CMD_IDENTIFY = 0xEC;
35+
36+
bool waitBSY(u16 ioBase) {
37+
for (usize i = 0; i < 100000; i++) {
38+
if (!(io::inb(ioBase + ATA_REG_STATUS) & ATA_SR_BSY)) return true;
39+
}
40+
return false;
41+
}
42+
43+
bool waitDRQ(u16 ioBase) {
44+
for (usize i = 0; i < 100000; i++) {
45+
u8 status = io::inb(ioBase + ATA_REG_STATUS);
46+
47+
if (status & (ATA_SR_ERR | ATA_SR_DF)) {
48+
return false;
49+
}
50+
51+
if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRQ)) return true;
52+
}
53+
return false;
54+
}
55+
56+
void selectDrive(u16 ioBase, u8 drive, u64 lba) {
57+
io::outb(ioBase + ATA_REG_HDDEVSEL,
58+
static_cast<u8>(0xE0 | static_cast<u8>((drive & 1) << 4) | ((lba >> 24) & 0x0F)));
59+
io::wait();
60+
}
61+
62+
bool identifyDrive(u16 ioBase, u8 drive, u16* identify) {
63+
selectDrive(ioBase, drive, 0);
64+
65+
io::outb(ioBase + ATA_REG_SECCOUNT0, 0);
66+
io::outb(ioBase + ATA_REG_LBA0, 0);
67+
io::outb(ioBase + ATA_REG_LBA1, 0);
68+
io::outb(ioBase + ATA_REG_LBA2, 0);
69+
io::outb(ioBase + ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
70+
71+
io::wait();
72+
73+
if (io::inb(ioBase + ATA_REG_STATUS) == 0) {
74+
return false;
75+
}
76+
77+
if (!waitDRQ(ioBase)) {
78+
return false;
79+
}
80+
81+
for (usize i = 0; i < 256; i++) {
82+
identify[i] = io::inw(ioBase + ATA_REG_DATA);
83+
}
84+
85+
return true;
86+
}
87+
88+
u64 sectorCountFromIdentify(const u16* id) {
89+
return static_cast<u64>(id[60]) | (static_cast<u64>(id[61]) << 16);
90+
}
91+
}
92+
93+
94+
ATADiskDevice::ATADiskDevice([[maybe_unused]] PCIDevice* controller, u16 ioBase, [[maybe_unused]] u16 ctrlBase,
95+
u8 drive, u64 sectors) :
96+
BlockDevice("Ata Disk", sectors, 512),
97+
ioBase(ioBase),
98+
drive(drive) {
99+
}
100+
101+
bool ATADiskDevice::readBlocks(u64 lba, u32 count, void* buffer) {
102+
return access(false, lba, count, buffer);
103+
}
104+
105+
bool ATADiskDevice::writeBlocks(u64 lba, u32 count, const void* buffer) {
106+
return access(true, lba, count, const_cast<void*>(buffer));
107+
}
108+
109+
bool ATADiskDevice::access(bool write, u64 lba, u32 count, void* buffer) {
110+
if (count == 0) {
111+
return true;
112+
}
113+
114+
if (lba + count > blockCount()) {
115+
debug::error("Tried to access region ", lba, " + ", count, " which resulted in an overflow.");
116+
return false;
117+
}
118+
119+
if (lba > 0x0FFFFFFF) {
120+
debug::error("For now LBA28 is not supported");
121+
return false;
122+
}
123+
124+
auto* words = reinterpret_cast<u16*>(buffer);
125+
126+
for (u32 sector = 0; sector < count; sector++) {
127+
u64 currentLBA = lba + sector;
128+
129+
if (!waitBSY(ioBase)) {
130+
return false;
131+
}
132+
133+
selectDrive(ioBase, drive, currentLBA);
134+
135+
io::outb(ioBase + ATA_REG_SECCOUNT0, 1);
136+
io::outb(ioBase + ATA_REG_LBA0, currentLBA & 0xFF);
137+
io::outb(ioBase + ATA_REG_LBA1, (currentLBA >> 8) & 0xFF);
138+
io::outb(ioBase + ATA_REG_LBA2, (currentLBA >> 16) & 0xFF);
139+
140+
io::outb(ioBase + ATA_REG_COMMAND, write ? ATA_CMD_WRITE_PIO : ATA_CMD_READ_PIO);
141+
142+
if (write) {
143+
for (usize i = 0; i < 256; i++) {
144+
io::outw(ioBase + ATA_REG_DATA, words[sector * 256 + i]);
145+
}
146+
147+
io::outb(ioBase + ATA_REG_COMMAND, ATA_CMD_CACHE_FLUSH);
148+
149+
if (!waitBSY(ioBase)) {
150+
return false;
151+
}
152+
}
153+
else {
154+
for (usize i = 0; i < 256; i++) {
155+
words[sector * 256 + i] = io::inw(ioBase + ATA_REG_DATA);
156+
}
157+
}
158+
}
159+
160+
return true;
161+
}
162+
163+
bool ATADriver::probe(Device& device) {
164+
if (device.type() != DeviceType::PCI) {
165+
return false;
166+
}
167+
168+
auto& pciDevice = static_cast<PCIDevice&>(device);
169+
170+
return pciDevice.isClass(0x01, 0x01);
171+
}
172+
173+
bool ATADriver::start(Device& device) {
174+
debug::log("Starting ATA Driver");
175+
176+
auto& pciDevice = static_cast<PCIDevice&>(device);
177+
178+
pciDevice.enableIOSpace();
179+
pciDevice.enableBusMastering();
180+
181+
struct Channel {
182+
u16 ioBase;
183+
u16 ctrlBase;
184+
};
185+
186+
Channel channels[] = {
187+
{0x1F0, 0x3F6},
188+
{0x170, 0x376}
189+
};
190+
191+
bool foundAny = false;
192+
193+
for (auto& channel : channels) {
194+
for (u8 drive = 0; drive < 2; drive++) {
195+
u16 identify[256]{};
196+
197+
if (!identifyDrive(channel.ioBase, drive, identify)) continue;
198+
199+
u64 sectors = sectorCountFromIdentify(identify);
200+
201+
if (sectors == 0) {
202+
debug::warn("Found a drive with no sectors.");
203+
}
204+
205+
auto* disk = new ATADiskDevice(
206+
&pciDevice,
207+
channel.ioBase,
208+
channel.ctrlBase,
209+
drive,
210+
sectors
211+
);
212+
213+
disk->parent = &pciDevice;
214+
disk->driver = this;
215+
216+
DeviceManager::registerDevice(disk);
217+
foundAny = true;
218+
}
219+
}
220+
221+
if (!foundAny) {
222+
return false;
223+
}
224+
225+
device.driver = this;
226+
setState(DriverState::Active);
227+
return true;
228+
}
229+
230+
bool ATADriver::stop(Device& device) {
231+
if (device.driver == this) {
232+
device.driver = nullptr;
233+
}
234+
235+
setState(DriverState::Stopping);
236+
return true;
237+
}
238+
239+
void ata::registerDriver() {
240+
driver = new ATADriver();
241+
DriverManager::registerDriver(driver);
242+
}

kernel/drivers/time/pit.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ namespace {
2626
class PitDriver final : public Driver {
2727
public:
2828
string name() const override {
29-
return "pit";
29+
return "Programmable Interval Timer - Avery";
3030
}
3131

3232
bool probe(Device& device) override {

kernel/utils/types.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,4 +205,12 @@ extern "C" [[noreturn]] void __cxa_pure_virtual() {
205205
}
206206
}
207207

208+
extern "C" void* memset(void* dest, int value, usize count) {
209+
auto* p = static_cast<unsigned char*>(dest);
208210

211+
for (usize i = 0; i < count; i++) {
212+
p[i] = static_cast<unsigned char>(value);
213+
}
214+
215+
return dest;
216+
}

0 commit comments

Comments
 (0)