summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/fd.c360
-rw-r--r--kernel/fd_s.s27
-rw-r--r--kernel/fs/block.c22
-rw-r--r--kernel/fs/buffer.c102
-rw-r--r--kernel/fs/fs.c2
-rw-r--r--kernel/fs/mount.c12
-rw-r--r--kernel/hd.c18
-rw-r--r--kernel/sched.c7
-rw-r--r--kernel/tty.c4
9 files changed, 488 insertions, 66 deletions
diff --git a/kernel/fd.c b/kernel/fd.c
new file mode 100644
index 0000000..19068c4
--- /dev/null
+++ b/kernel/fd.c
@@ -0,0 +1,360 @@
+#include <asm/interrupt.h>
+#include <asm/io.h>
+#include <errno.h>
+#include <kernel/con.h>
+#include <kernel/fs.h>
+#include <kernel/kernel.h>
+#include <kernel/sched.h>
+#include <limits.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+
+#define FDBASE 0x3F0
+
+#define FDSRA FDBASE
+#define FDSRB (FDBASE + 1)
+#define FDDOR (FDBASE + 2)
+#define FDTDR (FDBASE + 3)
+#define FDMSR (FDBASE + 4)
+#define FDDRS (FDBASE + 4)
+#define FDDATA (FDBASE + 5)
+#define FDDIR (FDBASE + 7)
+#define FDCCR (FDBASE + 7)
+
+#define SECTS(n) ((n) * (BLOCK_SIZE / 512))
+#define BLOCKS(n) ((n) / (BLOCK_SIZE / 512))
+
+#define NTYPES (sizeof(ftypes) / sizeof(struct ftype))
+#define NFDS (sizeof(fd) / sizeof(struct fdrive))
+
+/* helper macros for converting LBA into CHS */
+#define C(lba, ft) \
+ (lba / (ft->h * ft->s))
+#define H(lba, ft) \
+ ((lba / ft->s) % ft->h)
+#define S(lba, ft) \
+ ((lba % ft->s) + 1)
+
+#define PRESENT(fd) ((fd.type) && (fd.type != ftypes))
+
+/* TODO: replace any delay loops with sleep_on() */
+/* TODO: implement a head scheduling algorithm */
+/* TODO: implement write functionality */
+/* TODO: implement access to multiple drives */
+/* TODO: seek before reading */
+/* TODO: check drive ready state in floppy driver (ensure the drive door is closed before any operation) */
+/* TODO: cut back on printk() usage */
+
+/* TODO: implement a timeout to prevent system hangs */
+#define WAITIRQ(x) ({ \
+ irq_fired = 0; \
+ (x); \
+ while(!irq_fired); \
+ irq_fired = 0; \
+ })
+
+extern void fd_isr(void);
+
+struct sense_result {
+ uint8_t st0;
+ uint8_t cyl;
+} __attribute__((packed));
+
+struct ftype {
+ uint16_t c;
+ uint8_t h;
+ uint8_t s;
+ uint8_t drate; /* 0: 500Kb/s, 1: 300Kb/s, 2: 250Kb/s, 3: 1Mb/s */
+ int delay; /* time to spin up (ms) */
+ char *name;
+};
+
+struct fdrive {
+ struct ftype *type;
+ struct task_struct *seek_wait;
+ uint8_t cyl;
+ uint8_t st0;
+};
+
+/* possible floppy disk/drive configurations */
+static struct ftype ftypes[] = {
+ { 0, 0, 0, 0, 0, "none" },
+ { 40, 2, 9, 1, 500, "360KB 5 1/4\"" },
+ { 80, 2, 15, 0, 500, "1.2MB 5 1/4\"" },
+ { 80, 2, 9, 2, 300, "720KB 3 1/2\"" },
+ { 80, 2, 18, 0, 300, "1.44MB 3 1/2\"" },
+ { 80, 2, 36, 3, 300, "2.88MB 3 1/2\"" }
+};
+
+static volatile int irq_fired;
+
+static struct fdrive fd[4];
+
+static char buffer[512] __attribute__((aligned (0x10000)));
+
+static struct task_struct *read_wait;
+
+static ssize_t read_data(void *buf, size_t len) {
+ ssize_t c = 0;
+ uint8_t *p = buf;
+
+ if(!buf)
+ return 0;
+
+ if(len > SSIZE_MAX)
+ len = SSIZE_MAX;
+
+ /*
+ * TODO: fix this.
+ * This entire routine really is garbage.
+ * It doesn't use a timeout (potentially
+ * hanging the entire system) and doesn't
+ * handle the DIO flag properly.
+ */
+ while(len--) {
+ while(!(inb(FDMSR) & 0x80));
+
+ if(!(inb(FDMSR) & 0x40))
+ break;
+
+ *p++ = inb(FDDATA);
+ c++;
+
+ /* delay */
+ inb(0x80);
+ }
+
+ /* empty out any remaining bytes the FDC may have for us */
+ while((inb(FDMSR) & 0xC0) == 0xC0)
+ inb(FDDATA);
+
+ if(!c)
+ return -1;
+ return c;
+}
+
+static void write_data(void *buf, size_t len) {
+ uint8_t *p = buf;
+
+ if(!buf)
+ return;
+
+ /* TODO: timeout waiting for RQM flag */
+ while(len--) {
+ while(!(inb(FDMSR) & 0x80));
+
+ if((inb(FDMSR) & 0x40)) {
+ printk("[fd] Expected DIO=0. Drive not ready for data!\n");
+ panic();
+ }
+
+ outb(FDDATA, *p++);
+
+ /* delay */
+ inb(0x80);
+ }
+}
+
+static int seek(uint8_t drive, int cyl) {
+ drive &= 3;
+
+ char cmd_seek[] = {
+ 0x0F,
+ drive,
+ cyl
+ };
+
+ if(!PRESENT(fd[drive]))
+ return -EINVAL;
+
+ WAITIRQ(write_data(cmd_seek, sizeof(cmd_seek)));
+
+ /* wait for the seek command to finish */
+ while((inb(FDMSR) & (1 << drive)));
+
+ return 0;
+}
+
+static int sense_intr(struct sense_result *sr) {
+ ssize_t in;
+ char cmd_sense[] = { 0x08 };
+
+ /* send the sense interrupt command */
+ write_data(cmd_sense, sizeof(cmd_sense));
+ in = read_data(sr, sizeof(struct sense_result));
+ if(in < sizeof(struct sense_result))
+ return -1;
+
+ printk("[fd] Sense command success (Drive: %c:, CYL: 0x%02x, ST0: 0x%02x)\n", 'A' + (sr->st0 & 3), sr->cyl, sr->st0);
+ return 0;
+}
+
+static void reset(void) {
+ int i;
+ int wait = 0;
+ ssize_t in;
+ struct sense_result sr;
+
+ /* TODO: set timings depending on the drive */
+ char cmd_specify[] = {
+ 0x03,
+ 0xDF, /* SRT: 3ms, HUT: 240ms */
+ 0x02 /* HLT: 16ms, NDMA: 0 */
+ };
+
+ char cmd_recalibrate[] = { 0x07, 0x00 };
+
+ /* disable interrupts and put the FDC into reset */
+ /* TODO: store FDDOR as static variable as the register is write-only */
+ printk("[fd] Resetting FDC...\n");
+ outb(FDDOR, inb(FDDOR) & 0xF3);
+
+ /* program the data-rate */
+ /* TODO: set this depending on floppy type */
+ outb(FDDRS, 0);
+ outb(FDCCR, 0);
+
+ /* bring the FDC out of reset and re-enable interrupts */
+ outb(FDDOR, inb(FDDOR) | 0x0C);
+
+ /* the IRQ handler will issue a sense-interrupt for us */
+ WAITIRQ(NULL);
+
+ /* send the specify command */
+ write_data(cmd_specify, sizeof(cmd_specify));
+
+ for(i = 0; i < NFDS; i++) {
+ if(!PRESENT(fd[i]))
+ continue;
+ printk("[fd] Recalibrating Drive %c:...\n", 'A' + i);
+ cmd_recalibrate[1] = i & 3;
+ write_data(cmd_recalibrate, sizeof(cmd_recalibrate));
+ wait |= 1 << i;
+ }
+
+ /* wait for all drives to finish recalibrating */
+ while((inb(FDMSR) & wait));
+}
+
+int fd_read_sect(int sect) {
+ ssize_t in;
+ struct {
+ uint8_t st0;
+ uint8_t st1;
+ uint8_t st2;
+ uint8_t c;
+ uint8_t h;
+ uint8_t r;
+ uint8_t n;
+ } __attribute__((packed)) res;
+
+ char cmd[] = {
+ 0x46,
+ H(sect, fd[0].type) << 2,
+ C(sect, fd[0].type),
+ H(sect, fd[0].type),
+ S(sect, fd[0].type),
+ 0x02,
+ S(sect, fd[0].type),
+ 0x1B,
+ 0xFF
+ };
+
+ cli();
+ if(read_wait)
+ sleep_on(&read_wait);
+ sti();
+
+ printk("[fd] Reading sector 0x%01x (C: 0x%01x, H: 0x%01x, S: 0x%01x)\n", sect, cmd[2], cmd[3], cmd[4]);
+
+ WAITIRQ(write_data(cmd, sizeof(cmd)));
+
+ in = read_data(&res, sizeof(res));
+ if(in == sizeof(res) && !(res.st0 & 0xC0)) {
+ printk("[fd] Successfully read a sector (ST0: 0x%02x, N: 0x%02x)!\n", res.st0, res.n);
+ printk(" DATA: %s\n", buffer);
+ return 0;
+ }
+
+ printk("[fd] Read command failed!\n");
+ return -1;
+}
+
+void fd_interrupt(void) {
+ struct sense_result sr;
+ irq_fired = 1;
+
+ wake_up(&read_wait);
+
+ if((inb(FDMSR) & 0xC0) != 0x80)
+ return;
+ if(sense_intr(&sr) < sizeof(sr))
+ return;
+ /* deposit result into current drive struct */
+ fd[sr.cyl & 3].cyl = sr.cyl;
+ fd[sr.st0 & 3].st0 = sr.st0;
+}
+
+void fd_block_read(struct buffer *b) {
+ int i;
+ void *p = b->b_data;
+
+ for(i = SECTS(b->b_block); i < SECTS(b->b_block + 1); i++) {
+ if(fd_read_sect(i)) {
+ b->b_device = 0;
+ return;
+ }
+ memcpy(p, buffer, sizeof(buffer));
+ p += 512;
+ }
+
+ b->b_present = 1;
+}
+
+void fd_init(void) {
+ int i;
+ int delay = 0;
+ uint8_t tmp;
+
+ /* put the controller into reset */
+ outb(FDDOR, 0);
+
+ outb(0x70, 0x10);
+ tmp = inb(0x71);
+
+ fd[0].type = &ftypes[(tmp >> 4) > NTYPES ? 0 : (tmp >> 4)];
+ fd[1].type = &ftypes[(tmp & 0xF) > NTYPES ? 0 : (tmp & 0xF)];
+ fd[2].type = ftypes;
+ fd[3].type = ftypes;
+
+ /* setup the drive (motors A and B on) */
+ /* TODO: motor timeout */
+ outb(FDDOR, 0x3C);
+
+ /* wait for all drives to spin up */
+ for(i = 0; i < NFDS; i++)
+ if(PRESENT(fd[i]))
+ delay = fd[i].type->delay > delay ? fd[i].type->delay : delay;
+ delay = (delay / 10) + ticks;
+ while(ticks < delay);
+
+ /* initialize the DMA controller */
+ outb(0x0F, 0x0F);
+ outb(0x0C, 0xFF);
+ outb(0x04, (uint32_t) buffer);
+ outb(0x04, (uint32_t) buffer >> 8);
+ outb(0x0C, 0xFF);
+ outb(0x05, sizeof(buffer));
+ outb(0x05, sizeof(buffer) >> 8);
+ outb(0x81, (uint32_t) buffer >> 16);
+ outb(0x0B, 0x56);
+ outb(0x0A, 0x02);
+
+ register_isr(0x26, 0, &fd_isr);
+ irq_enable(0x40);
+
+ reset();
+
+ printk("[fd] Floppy subsystem initialized (A: %s, B: %s)\n", fd[0].type->name, fd[1].type->name);
+}
diff --git a/kernel/fd_s.s b/kernel/fd_s.s
new file mode 100644
index 0000000..a8acb5b
--- /dev/null
+++ b/kernel/fd_s.s
@@ -0,0 +1,27 @@
+global fd_isr
+extern printk
+
+extern fd_interrupt
+
+fd_isr:
+ pusha
+ mov ax, ds
+ push ax
+ mov ax, 0x10
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+
+ call fd_interrupt
+
+ mov al, 0x20
+ out 0x20, al
+
+ pop ax
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+ popa
+ iret
diff --git a/kernel/fs/block.c b/kernel/fs/block.c
index caf97d2..8f9d907 100644
--- a/kernel/fs/block.c
+++ b/kernel/fs/block.c
@@ -1,9 +1,23 @@
+#include <kernel/fd.h>
+#include <kernel/fs.h>
#include <sys/types.h>
-int block_read(void *buf, size_t block) {
- return -1;
+/*
+ * Minix Major Numbers:
+ *
+ * [0] NULL
+ * [1] /dev/mem
+ * [2] /dev/fdX
+ * [3] /dev/hdX
+ * [4] /dev/ttyX
+ * [5] /dev/tty
+ * [6] /dev/lpX
+ */
+
+void block_read(struct buffer *b) {
+ fd_block_read(b);
}
-int block_write(void *buf, size_t block) {
- return -1;
+void block_write(struct buffer *b) {
+ b->b_device = 0;
}
diff --git a/kernel/fs/buffer.c b/kernel/fs/buffer.c
index 7405738..99c3161 100644
--- a/kernel/fs/buffer.c
+++ b/kernel/fs/buffer.c
@@ -1,16 +1,76 @@
+#include <asm/interrupt.h>
+#include <errno.h>
#include <kernel/con.h>
#include <kernel/fs.h>
#include <stdint.h>
#include <string.h>
/* memory dedicated to the block cache */
+/* TODO: start buffers at kernel end not 512K */
#define BSTART 0x80000
#define BEND 0xA0000
static struct buffer *lru;
static struct buffer *mru;
-struct buffer *buffer_get_block(uint16_t device, uint16_t block);
+struct buffer *buffer_get_block(uint16_t device, uint16_t block) {
+ struct buffer **p = &mru;
+ struct buffer *b;
+
+ if(!device)
+ return NULL;
+
+ /* check if the requested block is already in the cache */
+ while(b = *p) {
+ if(b->b_device != device || b->b_block != block)
+ goto next;
+ /* remove the buffer from the list */
+ if(lru == b && b->b_next) lru = b->b_next;
+ if(b->b_next) b->b_next->b_prev = b->b_prev;
+ if(b->b_prev) b->b_prev->b_next = b->b_next;
+ /* put the buffer back at the MRU end of the list */
+ b->b_next = NULL;
+ b->b_prev = mru;
+ if(mru != b)
+ mru->b_next = b;
+ mru = b;
+ cli();
+ /* if the buffer exists but isn't populated. wait on it */
+ if(!b->b_present) {
+ sleep_on(&b->b_wait);
+ printk("[buf] Waiting for buffer %04x:%04x to be populated...\n", b->b_device, b->b_block);
+ }
+ sti();
+ printk("[buf] Fetched block %04x:%04x from cache\n", b->b_device, b->b_block);
+ return b->b_device ? b : NULL;
+next:
+ p = &b->b_prev;
+ }
+
+ /*
+ * otherwise, iterate through the least recently used
+ * buffers, to find a buffer that isn't pending an
+ * I/O operation, and read into it.
+ */
+
+ p = &lru;
+ while(b = *p) {
+ if(b->b_present || !b->b_device)
+ break;
+ p = &(*p)->b_next;
+ }
+
+ if(!b)
+ return NULL;
+
+ b->b_device = device;
+ b->b_block = block;
+ b->b_present = 0;
+ block_read(b);
+ wake_up(&b->b_wait);
+ printk("[buf] Called driver to read block %04x:%04x\n", b->b_device, b->b_block);
+ return b->b_device ? b : NULL;
+}
void buffer_init(void) {
int c = 0;
@@ -40,43 +100,3 @@ void buffer_init(void) {
printk("[buf] Buffers initialized (0x%x buffers in pool)\n", c);
}
-
-struct buffer *buffer_get_block(uint16_t device, uint16_t block) {
- int ret;
- struct buffer **p = &mru;
- struct buffer *b;
-
- /* check if the requested block is already in the cache */
- while(*p) {
- if((*p)->b_device == device && (*p)->b_block == block) {
- /* put the buffer back at the MRU end of the list */
- (*p)->b_next = NULL;
- (*p)->b_prev = mru;
- mru = *p;
- return *p;
- }
- p = &(*p)->b_prev;
- }
-
- /* find a buffer at the end of the LRU list */
- b = lru;
- lru = lru->b_next;
- lru->b_prev = NULL;
- ret = block_read(b->b_data, block);
- /* abort if we failed to read in the block */
- if(ret < 0) {
- /* release the allocated buffer to the pool */
- lru->b_prev = b;
- b->b_next = lru;
- b->b_prev = NULL;
- lru = b;
- return NULL;
- }
- b->b_device = device;
- b->b_block = block;
- /* put the buffer back at the MRU end of the list */
- b->b_next = NULL;
- b->b_prev = mru;
- mru = b;
- return b;
-}
diff --git a/kernel/fs/fs.c b/kernel/fs/fs.c
index 381f20a..e460a4a 100644
--- a/kernel/fs/fs.c
+++ b/kernel/fs/fs.c
@@ -1,5 +1,6 @@
#include <errno.h>
#include <kernel/con.h>
+#include <kernel/fd.h>
#include <kernel/fs.h>
#include <kernel/kernel.h>
#include <kernel/sched.h>
@@ -32,6 +33,7 @@ void fs_init(void) {
buffer_init();
/* hd_init(); */
+ fd_init();
mount_root();
}
diff --git a/kernel/fs/mount.c b/kernel/fs/mount.c
index 9cdf49b..e4d3636 100644
--- a/kernel/fs/mount.c
+++ b/kernel/fs/mount.c
@@ -7,15 +7,17 @@ struct super_block sblocks[NRSUPER];
void mount_root(void) {
int ret;
- char buf[BLOCK_SIZE];
- struct super_block *s = (void*) buf;
+ struct buffer *b;
+ struct super_block *s;
- ret = block_read(buf, 1);
- if(ret < 0) {
+ b = buffer_get_block(1, 1);
+ if(!b) {
printk("[fs] Failed to read super block\n");
return;
}
+ s = b->b_data;
+
if(s->s_magic != SUPER_MAGIC) {
printk("[fs] Invalid magic number in super block!\n");
return;
@@ -31,5 +33,5 @@ void mount_root(void) {
printk(" Max file size: 0x%01x\n", s->s_max_size);
/* copy the super block into the table */
- memcpy(sblocks, buf, ((unsigned) &s->s_imap - (unsigned) s));
+ memcpy(sblocks, s, ((unsigned) &s->s_imap - (unsigned) s));
}
diff --git a/kernel/hd.c b/kernel/hd.c
index 2f680d1..83108dd 100644
--- a/kernel/hd.c
+++ b/kernel/hd.c
@@ -40,7 +40,7 @@ static int read_sect(void *buf, uint32_t lba) {
outb(0x1F7, 0x20);
/* wait for the drive */
- while(1) {
+ for(;;) {
/* poll the drive's status */
s = inb(0x1F7);
if(s & ST_DF) {
@@ -81,7 +81,7 @@ static int write_sect(void *buf, uint32_t lba) {
outb(0x1F7, 0x30);
/* wait for the drive */
- while(1) {
+ for(;;) {
/* poll the drive's status */
s = inb(0x1F7);
if(s & ST_DF) {
@@ -102,15 +102,13 @@ static int write_sect(void *buf, uint32_t lba) {
return 0;
}
-int hd_read_block(void *buf, size_t block) {
+int hd_read_block(void *buf, uint16_t block) {
int ret;
size_t i;
uint32_t s = SECTS(block);
uint32_t e = SECTS(block + 1);
- if(nblocks <= s)
- return -1;
- if(nblocks <= e)
+ if(nblocks <= s || nblocks <= e)
return -1;
for(i = 0; i < (e - s); i++) {
@@ -125,15 +123,13 @@ int hd_read_block(void *buf, size_t block) {
return 0;
}
-int hd_write_block(void *buf, size_t block) {
+int hd_write_block(void *buf, uint16_t block) {
int ret;
size_t i;
uint32_t s = SECTS(block);
uint32_t e = SECTS(block + 1);
- if(nblocks <= s)
- return -1;
- if(nblocks <= e)
+ if(nblocks <= s || nblocks <= e)
return -1;
for(i = 0; i < (e - s); i++) {
@@ -158,7 +154,7 @@ int hd_init(void) {
/* send the IDENTIFY command */
outb(0x1F7, 0xEC);
- while(1) {
+ for(;;) {
s = inb(0x1F7);
if(!s) {
printk("[hd] Master drive not detected on primary bus!\n");
diff --git a/kernel/sched.c b/kernel/sched.c
index da55fa3..ff2d036 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -85,7 +85,8 @@ struct task_struct *create_proc(void *bin, size_t len) {
}
}
- /* here we allocate pages to setup the process'
+ /*
+ * here we allocate pages to setup the process'
* address space. in addition to the pages that
* will hold the binary image, the following
* pages are allocated:
@@ -222,7 +223,7 @@ void reschedule(void) {
return;
n = ctaskn;
- while(1) {
+ for(;;) {
/* check alarms and signals */
for(i = 0; i < NRTASKS; i++) {
if(tasks[i].alarm && tasks[i].alarm < ticks) {
@@ -288,7 +289,7 @@ void interruptible_sleep_on(struct task_struct **waiting) {
t = *waiting;
*waiting = ctask;
- while(1) {
+ for(;;) {
ctask->state = TSTATE_INTERRUPTIBLE;
reschedule();
diff --git a/kernel/tty.c b/kernel/tty.c
index 529d955..a17429f 100644
--- a/kernel/tty.c
+++ b/kernel/tty.c
@@ -20,7 +20,7 @@ static struct tty_struct *ttys[] = {
/* check if a buffer's empty and if so, go to sleep */
static int sleep_while_empty(struct tty_queue *q) {
cli();
- while(1) {
+ for(;;) {
if(q->pread == q->pwrite)
interruptible_sleep_on(&q->waiting);
@@ -38,7 +38,7 @@ static int sleep_while_empty(struct tty_queue *q) {
/* check if a buffer's full and if so, go to sleep */
static int sleep_while_full(struct tty_queue *q, struct tty_struct *tty) {
cli();
- while(1) {
+ for(;;) {
/*
* if the buffer is full, first notify the
* driver so it can try to remove some