diff options
author | Lizzy Fleckenstein <lizzy@vlhl.dev> | 2023-12-15 16:10:22 +0100 |
---|---|---|
committer | Lizzy Fleckenstein <lizzy@vlhl.dev> | 2023-12-15 16:11:38 +0100 |
commit | 2298d17186cb0e58a96d285384de431902da9b1e (patch) | |
tree | 635d642931d9f55e701639ee3b3707e0e28a805e /stage3/ata.c | |
parent | 8a25a2935a60e65fcb3e2b715bada858f5fcd6a2 (diff) | |
download | cuddles-2298d17186cb0e58a96d285384de431902da9b1e.tar.xz |
big chungus
* fix a heap corruption bug
* add qemu support
* add an ATA driver
* add an USTAR read-only file system
* boot from disk instead of floppy
* font rendering
* image rendering
* PCI enumeration
* init script
Diffstat (limited to 'stage3/ata.c')
-rw-r--r-- | stage3/ata.c | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/stage3/ata.c b/stage3/ata.c new file mode 100644 index 0000000..ccfd695 --- /dev/null +++ b/stage3/ata.c @@ -0,0 +1,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); + + const char *errors[] = { + "address mark not found\n", + "track zero not found\n", + "aborted command\n", + "media change request\n", + "id not found\n", + "media changed\n", + "uncorrectable data error\n", + "bad block detected\n", + }; + + for (int i = 0; i < 8; i++) + if (err & (1 << i)) + print(errors[i]); + + panic("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("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("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("ata0-witch is not ATA\n"); + + u16 *idvec = malloc(256 * sizeof *idvec); + ata_recv(idvec); + + if (!(idvec[83] & (1 << 10))) + panic("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; +} |