ide: first commit

This commit is contained in:
Jack Halford 2020-01-05 20:35:21 +01:00
parent 0307afe365
commit 88279f3f0d
6 changed files with 288 additions and 5 deletions

1
.gitignore vendored
View file

@ -1 +1,2 @@
zig-cache zig-cache
disk.img

View file

@ -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 \

View file

@ -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]"
: :

View file

@ -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
View 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,
);
}
}
}

View file

@ -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 {