ide: first commit
This commit is contained in:
parent
0307afe365
commit
88279f3f0d
6 changed files with 288 additions and 5 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1 +1,2 @@
|
||||||
zig-cache
|
zig-cache
|
||||||
|
disk.img
|
||||||
|
|
|
||||||
2
qemu.sh
2
qemu.sh
|
|
@ -4,6 +4,7 @@ QEMU_GDB_PORT=4242
|
||||||
KERNEL=build/kernel
|
KERNEL=build/kernel
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
|
touch disk.img
|
||||||
sudo pkill -9 qemu
|
sudo pkill -9 qemu
|
||||||
sudo qemu-system-i386 \
|
sudo qemu-system-i386 \
|
||||||
-gdb tcp::${QEMU_GDB_PORT} \
|
-gdb tcp::${QEMU_GDB_PORT} \
|
||||||
|
|
@ -11,6 +12,7 @@ start() {
|
||||||
-enable-kvm \
|
-enable-kvm \
|
||||||
-m 1341M \
|
-m 1341M \
|
||||||
-curses \
|
-curses \
|
||||||
|
-hda disk.img \
|
||||||
-kernel ${KERNEL}
|
-kernel ${KERNEL}
|
||||||
# -drive file=disk.img,if=virtio\
|
# -drive file=disk.img,if=virtio\
|
||||||
# -no-reboot \
|
# -no-reboot \
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,17 @@ pub inline fn inl(port: u16) u32 {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub inline fn insl(port: u16, addr: var, cnt: usize) void {
|
||||||
|
asm volatile ("cld; repne; insl;"
|
||||||
|
: [addr] "={edi}" (addr),
|
||||||
|
[cnt] "={ecx}" (cnt)
|
||||||
|
: [port] "{dx}" (port),
|
||||||
|
[addr] "0" (addr),
|
||||||
|
[cnt] "1" (cnt)
|
||||||
|
: "memory", "cc"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
pub inline fn outb(port: u16, value: u8) void {
|
pub inline fn outb(port: u16, value: u8) void {
|
||||||
asm volatile ("outb %[value], %[port]"
|
asm volatile ("outb %[value], %[port]"
|
||||||
:
|
:
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ fn execute(input: []u8) void {
|
||||||
if (eql(u8, input, "paging")) return x86.paging.format();
|
if (eql(u8, input, "paging")) return x86.paging.format();
|
||||||
if (eql(u8, input, "memory")) return x86.pmem.format();
|
if (eql(u8, input, "memory")) return x86.pmem.format();
|
||||||
if (eql(u8, input, "tasks")) return task.format();
|
if (eql(u8, input, "tasks")) return task.format();
|
||||||
if (eql(u8, input, "tasks")) return task.format_short();
|
|
||||||
if (eql(u8, input, "lspci")) return pci.lspci();
|
if (eql(u8, input, "lspci")) return pci.lspci();
|
||||||
if (eql(u8, input, "sleep2")) return sleep_for_2();
|
if (eql(u8, input, "sleep2")) return sleep_for_2();
|
||||||
if (eql(u8, input, "t-sleep2")) {
|
if (eql(u8, input, "t-sleep2")) {
|
||||||
|
|
|
||||||
249
src/pci/ide.zig
Normal file
249
src/pci/ide.zig
Normal file
|
|
@ -0,0 +1,249 @@
|
||||||
|
usingnamespace @import("pci.zig");
|
||||||
|
|
||||||
|
const IDE_ATA = 0x00;
|
||||||
|
const IDE_ATAPI = 0x01;
|
||||||
|
|
||||||
|
const ATA_MASTER = 0x00;
|
||||||
|
const ATA_SLAVE = 0x01;
|
||||||
|
|
||||||
|
// Channels:
|
||||||
|
const ATA_PRIMARY = 0x00;
|
||||||
|
const ATA_SECONDARY = 0x01;
|
||||||
|
|
||||||
|
// Directions:
|
||||||
|
const ATA_READ = 0x00;
|
||||||
|
const ATA_WRITE = 0x01;
|
||||||
|
|
||||||
|
// Commands
|
||||||
|
const ATA_CMD_READ_PIO = 0x20;
|
||||||
|
const ATA_CMD_READ_PIO_EXT = 0x24;
|
||||||
|
const ATA_CMD_READ_DMA = 0xC8;
|
||||||
|
const ATA_CMD_READ_DMA_EXT = 0x25;
|
||||||
|
const ATA_CMD_WRITE_PIO = 0x30;
|
||||||
|
const ATA_CMD_WRITE_PIO_EXT = 0x34;
|
||||||
|
const ATA_CMD_WRITE_DMA = 0xCA;
|
||||||
|
const ATA_CMD_WRITE_DMA_EXT = 0x35;
|
||||||
|
const ATA_CMD_CACHE_FLUSH = 0xE7;
|
||||||
|
const ATA_CMD_CACHE_FLUSH_EXT = 0xEA;
|
||||||
|
const ATA_CMD_PACKET = 0xA0;
|
||||||
|
const ATA_CMD_IDENTIFY_PACKET = 0xA1;
|
||||||
|
const ATA_CMD_IDENTIFY = 0xEC;
|
||||||
|
|
||||||
|
// Status:
|
||||||
|
const ATA_SR_BSY = 0x80; // Busy
|
||||||
|
const ATA_SR_DRDY = 0x40; // Drive ready
|
||||||
|
const ATA_SR_DF = 0x20; // Drive write fault
|
||||||
|
const ATA_SR_DSC = 0x10; // Drive seek complete
|
||||||
|
const ATA_SR_DRQ = 0x08; // Data request ready
|
||||||
|
const ATA_SR_CORR = 0x04; // Corrected data
|
||||||
|
const ATA_SR_IDX = 0x02; // Index
|
||||||
|
const ATA_SR_ERR = 0x01; // Error
|
||||||
|
|
||||||
|
// Registers:
|
||||||
|
const ATA_REG_DATA = 0x00;
|
||||||
|
const ATA_REG_ERROR = 0x01;
|
||||||
|
const ATA_REG_FEATURES = 0x01;
|
||||||
|
const ATA_REG_SECCOUNT0 = 0x02;
|
||||||
|
const ATA_REG_LBA0 = 0x03;
|
||||||
|
const ATA_REG_LBA1 = 0x04;
|
||||||
|
const ATA_REG_LBA2 = 0x05;
|
||||||
|
const ATA_REG_HDDEVSEL = 0x06;
|
||||||
|
const ATA_REG_COMMAND = 0x07;
|
||||||
|
const ATA_REG_STATUS = 0x07;
|
||||||
|
const ATA_REG_SECCOUNT1 = 0x08;
|
||||||
|
const ATA_REG_LBA3 = 0x09;
|
||||||
|
const ATA_REG_LBA4 = 0x0A;
|
||||||
|
const ATA_REG_LBA5 = 0x0B;
|
||||||
|
const ATA_REG_CONTROL = 0x0C;
|
||||||
|
const ATA_REG_ALTSTATUS = 0x0C;
|
||||||
|
const ATA_REG_DEVADDRESS = 0x0D;
|
||||||
|
|
||||||
|
// Identification space
|
||||||
|
const ATA_IDENT_DEVICETYPE = 0;
|
||||||
|
const ATA_IDENT_CYLINDERS = 2;
|
||||||
|
const ATA_IDENT_HEADS = 6;
|
||||||
|
const ATA_IDENT_SECTORS = 12;
|
||||||
|
const ATA_IDENT_SERIAL = 20;
|
||||||
|
const ATA_IDENT_MODEL = 54;
|
||||||
|
const ATA_IDENT_CAPABILITIES = 98;
|
||||||
|
const ATA_IDENT_FIELDVALID = 106;
|
||||||
|
const ATA_IDENT_MAX_LBA = 120;
|
||||||
|
const ATA_IDENT_COMMANDSETS = 164;
|
||||||
|
const ATA_IDENT_MAX_LBA_EXT = 200;
|
||||||
|
|
||||||
|
const ide_buf: [2048]u8 = [1]u8{0} ** 2048;
|
||||||
|
const atapi_packet: [12]u8 = [1]u8{0xA8} ++ [1]u8{0} ** 11;
|
||||||
|
const ide_irq_invoked = false;
|
||||||
|
|
||||||
|
const IDEDevice = struct {
|
||||||
|
reserved: u8, // 0 (Empty) or 1 (This Drive really exists).
|
||||||
|
channel: u8, // 0 (Primary Channel) or 1 (Secondary Channel).
|
||||||
|
drive: u8, // 0 (Master Drive) or 1 (Slave Drive).
|
||||||
|
idetype: u16, // 0: ATA, 1:ATAPI.
|
||||||
|
signature: u16, // Drive Signature
|
||||||
|
capabilities: u16, // Features.
|
||||||
|
commandsets: usize, // Command Sets Supported.
|
||||||
|
size: usize, // Size in Sectors.
|
||||||
|
model: [41]u8, // Model in string.
|
||||||
|
};
|
||||||
|
|
||||||
|
var ide_devices: [4]IDEDevice = undefined;
|
||||||
|
|
||||||
|
const IDEChannelRegister = struct {
|
||||||
|
base: u16, // I/O Base.
|
||||||
|
ctrl: u16, // Control Base
|
||||||
|
bmide: u16, // Bus Master IDE
|
||||||
|
nIEN: u8, // nIEN (No Interrupt);
|
||||||
|
};
|
||||||
|
|
||||||
|
var channels: [2]IDEChannelRegister = undefined;
|
||||||
|
|
||||||
|
pub inline fn ide_read(channel: u8, comptime reg: u8) u8 {
|
||||||
|
if (reg > 0x07 and reg < 0x0C) ide_write(channel, ATA_REG_CONTROL, 0x80 | channels[channel].nIEN);
|
||||||
|
defer if (reg > 0x07 and reg < 0x0C) ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN);
|
||||||
|
return switch (reg) {
|
||||||
|
0x0...0x7 => x86.inb(channels[channel].base + reg - 0x0),
|
||||||
|
0x8...0xb => x86.inb(channels[channel].base + reg - 0x6),
|
||||||
|
0xc...0xd => x86.inb(channels[channel].ctrl + reg - 0xa),
|
||||||
|
0xe...0x16 => x86.inb(channels[channel].bmide + reg - 0xe),
|
||||||
|
else => @compileError("bad IDE register."),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub inline fn ide_read_buffer(channel: u8, comptime reg: u8, buf: var, cnt: usize) void {
|
||||||
|
if (reg > 0x07 and reg < 0x0C) ide_write(channel, ATA_REG_CONTROL, 0x80 | channels[channel].nIEN);
|
||||||
|
defer if (reg > 0x07 and reg < 0x0C) ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN);
|
||||||
|
switch (reg) {
|
||||||
|
0x0...0x7 => x86.insl(channels[channel].base + reg - 0x0, buf, cnt),
|
||||||
|
0x8...0xb => x86.insl(channels[channel].base + reg - 0x6, buf, cnt),
|
||||||
|
0xc...0xd => x86.insl(channels[channel].ctrl + reg - 0xa, buf, cnt),
|
||||||
|
0xe...0x16 => x86.insl(channels[channel].bmide + reg - 0xe, buf, cnt),
|
||||||
|
else => @compileError("bad IDE register."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub inline fn ide_write(channel: u8, comptime reg: u8, data: u8) void {
|
||||||
|
if (reg > 0x07 and reg < 0x0C) ide_write(channel, ATA_REG_CONTROL, 0x80 | channels[channel].nIEN);
|
||||||
|
defer if (reg > 0x07 and reg < 0x0C) ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN);
|
||||||
|
switch (reg) {
|
||||||
|
0x0...0x7 => x86.outb(channels[channel].base + reg - 0x0, data),
|
||||||
|
0x8...0xb => x86.outb(channels[channel].base + reg - 0x6, data),
|
||||||
|
0xc...0xd => x86.outb(channels[channel].ctrl + reg - 0xa, data),
|
||||||
|
0xe...0x16 => x86.outb(channels[channel].bmide + reg - 0xe, data),
|
||||||
|
else => @compileError("bad IDE register."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(dev: PciDevice) void {
|
||||||
|
println("-- ide init --");
|
||||||
|
print("[ide] ");
|
||||||
|
dev.format();
|
||||||
|
assert(dev.header_type() == 0x0); // mass storage device
|
||||||
|
|
||||||
|
dev.config_write(@intCast(u8, 0xfe), 0x3c);
|
||||||
|
if (dev.intr_line() == 0xfe) {
|
||||||
|
println("[ide] needs IRQ assignement");
|
||||||
|
} else {
|
||||||
|
println("The device doesn't use IRQs");
|
||||||
|
}
|
||||||
|
|
||||||
|
const BAR0 = @intCast(u16, dev.bar(0));
|
||||||
|
const BAR1 = @intCast(u16, dev.bar(1));
|
||||||
|
const BAR2 = @intCast(u16, dev.bar(2));
|
||||||
|
const BAR3 = @intCast(u16, dev.bar(3));
|
||||||
|
const BAR4 = @intCast(u16, dev.bar(4));
|
||||||
|
channels[ATA_PRIMARY].base = if (BAR0 == 0) 0x1f0 else BAR0;
|
||||||
|
channels[ATA_PRIMARY].ctrl = if (BAR1 == 0) 0x3F6 else BAR1;
|
||||||
|
channels[ATA_SECONDARY].base = if (BAR2 == 0) 0x170 else BAR2;
|
||||||
|
channels[ATA_SECONDARY].ctrl = if (BAR3 == 0) 0x376 else BAR3;
|
||||||
|
channels[ATA_PRIMARY].bmide = (BAR4 & 0xFFFC); // Bus Master IDE
|
||||||
|
channels[ATA_SECONDARY].bmide = (BAR4 & 0xFFFC) + 8; // Bus Master IDE
|
||||||
|
|
||||||
|
// turn off irqs
|
||||||
|
ide_write(ATA_PRIMARY, ATA_REG_CONTROL, 2);
|
||||||
|
ide_write(ATA_SECONDARY, ATA_REG_CONTROL, 2);
|
||||||
|
|
||||||
|
// parse identification space
|
||||||
|
var count: usize = 0;
|
||||||
|
var err: u8 = 0;
|
||||||
|
var status: u8 = 0;
|
||||||
|
for ([_]u8{ 0, 1 }) |i| {
|
||||||
|
for ([_]u8{ 0, 1 }) |j| {
|
||||||
|
var idetype: u8 = IDE_ATA;
|
||||||
|
ide_devices[count].reserved = 0; // Assuming that no drive here.
|
||||||
|
|
||||||
|
// (I) Select Drive:
|
||||||
|
ide_write(i, ATA_REG_HDDEVSEL, 0xA0 | (j << 4)); // Select Drive.
|
||||||
|
task.usleep(1000) catch unreachable; // Wait 1ms for drive select to work.
|
||||||
|
|
||||||
|
// (II) Send ATA Identify Command:
|
||||||
|
ide_write(i, ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
|
||||||
|
task.usleep(1000) catch unreachable;
|
||||||
|
|
||||||
|
if (ide_read(i, ATA_REG_STATUS) == 0) continue; // If Status = 0, No Device.
|
||||||
|
while (true) {
|
||||||
|
status = ide_read(i, ATA_REG_STATUS);
|
||||||
|
if (status & ATA_SR_ERR != 0) {
|
||||||
|
err = 1;
|
||||||
|
break;
|
||||||
|
} // If Err, Device is not ATA.
|
||||||
|
if ((status & ATA_SR_BSY == 0) and (status & ATA_SR_DRQ != 0)) break; // Everything is right.
|
||||||
|
}
|
||||||
|
|
||||||
|
// (IV) Probe for ATAPI Devices:)
|
||||||
|
if (err != 0) {
|
||||||
|
// Device is not ATA
|
||||||
|
const cl = ide_read(i, ATA_REG_LBA1);
|
||||||
|
const ch = ide_read(i, ATA_REG_LBA2);
|
||||||
|
|
||||||
|
if (cl == 0x14 and ch == 0xEB) idetype = IDE_ATAPI;
|
||||||
|
if (cl == 0x69 and ch == 0x96) idetype = IDE_ATAPI;
|
||||||
|
if (idetype != IDE_ATAPI) continue; // Unknown Type (may not be a device).
|
||||||
|
|
||||||
|
ide_write(i, ATA_REG_COMMAND, ATA_CMD_IDENTIFY_PACKET);
|
||||||
|
task.usleep(1000) catch unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
// (V) Read Identification Space of the Device:
|
||||||
|
ide_read_buffer(i, ATA_REG_DATA, &ide_buf, 128);
|
||||||
|
|
||||||
|
ide_devices[count].reserved = 1;
|
||||||
|
ide_devices[count].idetype = idetype;
|
||||||
|
ide_devices[count].channel = i;
|
||||||
|
ide_devices[count].drive = j;
|
||||||
|
ide_devices[count].signature = @ptrCast(*const u8, &ide_buf[ATA_IDENT_DEVICETYPE]).*;
|
||||||
|
ide_devices[count].capabilities = @ptrCast(*const u8, &ide_buf[ATA_IDENT_CAPABILITIES]).*;
|
||||||
|
ide_devices[count].commandsets = @ptrCast(*const usize, &ide_buf[ATA_IDENT_COMMANDSETS]).*;
|
||||||
|
|
||||||
|
// (VII) Get Size:
|
||||||
|
if (ide_devices[count].commandsets & (1 << 26) != 0) {
|
||||||
|
// Device uses 48-Bit Addressing:
|
||||||
|
ide_devices[count].size = @ptrCast(*const usize, &ide_buf[ATA_IDENT_MAX_LBA_EXT]).*;
|
||||||
|
} else {
|
||||||
|
// Device uses CHS or 28-bit Addressing:
|
||||||
|
ide_devices[count].size = @ptrCast(*const usize, &ide_buf[ATA_IDENT_MAX_LBA]).*;
|
||||||
|
}
|
||||||
|
|
||||||
|
// (VIII) String indicates model of device (like Western Digital HDD and SONY DVD-RW...):
|
||||||
|
var k: u16 = 0;
|
||||||
|
while (k < 40) : (k = k + 2) {
|
||||||
|
ide_devices[count].model[k] = ide_buf[ATA_IDENT_MODEL + k + 1];
|
||||||
|
ide_devices[count].model[k + 1] = ide_buf[ATA_IDENT_MODEL + k];
|
||||||
|
}
|
||||||
|
ide_devices[count].model[40] = 0; // Terminate String.
|
||||||
|
|
||||||
|
count = count + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 4- Print Summary:
|
||||||
|
for ([_]u8{ 0, 1, 2, 3 }) |i| {
|
||||||
|
if (ide_devices[i].reserved == 1) {
|
||||||
|
println(
|
||||||
|
"[ide] found {} Drive {}GB - {}",
|
||||||
|
if (ide_devices[i].idetype == 0) "ATA" else "ATAPI",
|
||||||
|
ide_devices[i].size / 1024 / 1024 / 2,
|
||||||
|
ide_devices[i].model,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
pub usingnamespace @import("../index.zig");
|
pub usingnamespace @import("../index.zig");
|
||||||
pub const virtio = @import("virtio.zig");
|
pub const virtio = @import("virtio.zig");
|
||||||
|
pub const ide = @import("ide.zig");
|
||||||
|
|
||||||
const PCI_CONFIG_ADDRESS = 0xCF8;
|
const PCI_CONFIG_ADDRESS = 0xCF8;
|
||||||
const PCI_CONFIG_DATA = 0xCFC;
|
const PCI_CONFIG_DATA = 0xCFC;
|
||||||
|
|
@ -94,12 +95,30 @@ pub const PciDevice = struct {
|
||||||
pub fn header_type(self: PciDevice) u8 {
|
pub fn header_type(self: PciDevice) u8 {
|
||||||
return self.config_read(u8, 0xe);
|
return self.config_read(u8, 0xe);
|
||||||
}
|
}
|
||||||
|
pub fn intr_line(self: PciDevice) u8 {
|
||||||
|
return self.config_read(u8, 0x3c);
|
||||||
|
}
|
||||||
|
pub fn bar(self: PciDevice, comptime n: usize) u32 {
|
||||||
|
assert(n <= 5);
|
||||||
|
return self.config_read(u32, 0x10 + 4 * n);
|
||||||
|
}
|
||||||
// only for header_type == 0
|
// only for header_type == 0
|
||||||
pub fn subsystem(self: PciDevice) u16 {
|
pub fn subsystem(self: PciDevice) u16 {
|
||||||
return self.config_read(u8, 0x2e);
|
return self.config_read(u8, 0x2e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub inline fn config_write(self: PciDevice, value: var, comptime offset: u8) void {
|
||||||
|
// ask for access before writing config
|
||||||
|
x86.outl(PCI_CONFIG_ADDRESS, self.address(offset));
|
||||||
|
switch (@typeOf(value)) {
|
||||||
|
// read the correct size
|
||||||
|
u8 => return x86.outb(PCI_CONFIG_DATA, value),
|
||||||
|
u16 => return x86.outw(PCI_CONFIG_DATA, value),
|
||||||
|
u32 => return x86.outl(PCI_CONFIG_DATA, value),
|
||||||
|
else => @compileError("pci config space only supports writing u8, u16, u32."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub inline fn config_read(self: PciDevice, comptime size: type, comptime offset: u8) size {
|
pub inline fn config_read(self: PciDevice, comptime size: type, comptime offset: u8) size {
|
||||||
// ask for access before reading config
|
// ask for access before reading config
|
||||||
x86.outl(PCI_CONFIG_ADDRESS, self.address(offset));
|
x86.outl(PCI_CONFIG_ADDRESS, self.address(offset));
|
||||||
|
|
@ -114,7 +133,7 @@ pub const PciDevice = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
const Driver = struct {
|
const Driver = struct {
|
||||||
name: [*]u8,
|
name: []const u8,
|
||||||
class: u8,
|
class: u8,
|
||||||
subclass: u8,
|
subclass: u8,
|
||||||
vendor: ?u16 = null,
|
vendor: ?u16 = null,
|
||||||
|
|
@ -122,8 +141,10 @@ const Driver = struct {
|
||||||
init: fn (PciDevice) void,
|
init: fn (PciDevice) void,
|
||||||
};
|
};
|
||||||
|
|
||||||
const name = "virtio-blk";
|
pub var Drivers = [_]Driver{
|
||||||
pub var Drivers: [1]Driver = [_]Driver{Driver{ .name = &name, .class = 0x1, .subclass = 0x0, .vendor = 0x1af4, .subsystem = 0x2, .init = virtio.init }};
|
Driver{ .name = "virtio-blk"[0..], .class = 0x1, .subclass = 0x0, .vendor = 0x1af4, .subsystem = 0x2, .init = virtio.init },
|
||||||
|
Driver{ .name = "ide-ata", .class = 0x1, .subclass = 0x1, .init = ide.init },
|
||||||
|
};
|
||||||
|
|
||||||
// TODO: factor 2 functions when anonymous fn is released
|
// TODO: factor 2 functions when anonymous fn is released
|
||||||
pub fn scan() void {
|
pub fn scan() void {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue