diff --git a/Makefile b/Makefile index 8ba1c362450b2550d5e1eac5bc9b0024dbad1e6e..38357508208d5f81a3cffb27dc6090f31911de01 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ linux.img : tools/build bootsect setup kernel/system tools/build : tools/build.c gcc -o $@ $< -kernel/system : kernel/head.S kernel/*.c mm/*.c +kernel/system : kernel/head.S kernel/*.c mm/*.c kernel/blk_drv/*.c cd kernel; make system; cd .. bootsect : bootsect.o diff --git a/fs/buffer.c b/fs/buffer.c index 17a9eb8dc4c385e60d6b4e9198d1e0fa48e8ed7a..f5c8deab461a2f4d0a777897d8f38405a4ce6e96 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -12,6 +12,61 @@ struct buffer_head * hash_table[NR_HASH]; static struct buffer_head * free_list; int NR_BUFFERS = 0; +inline void wait_on_buffer(struct buffer_head * bh) { + cli(); + while (bh->b_lock) { + sleep_on(&bh->b_wait); + } + sti(); +} + + +static inline void remove_from_queues(struct buffer_head * bh) { + if (bh->b_next) + bh->b_next->b_prev = bh->b_prev; + if (bh->b_prev) + bh->b_prev->b_next = bh->b_next; + + bh->b_prev_free->b_next_free = bh->b_next_free; + bh->b_next_free->b_prev_free = bh->b_prev_free; + if (free_list == bh) + free_list = bh->b_next_free; +} + + + +struct buffer_head * getblk(int dev,int block) { + struct buffer_head * tmp, * bh; + + tmp = free_list; + do { + if (tmp->b_count) + continue; + else + break; + } while ((tmp = tmp->b_next_free) != free_list); + + bh = tmp; + bh->b_count=1; + bh->b_dirt=0; + bh->b_uptodate=0; + remove_from_queues(bh); + bh->b_dev=dev; + bh->b_blocknr=block; + return bh; +} + +struct buffer_head * bread(int dev,int block) { + struct buffer_head * bh; + bh = getblk(dev,block); + ll_rw_block(READ,bh); + wait_on_buffer(bh); + if (bh->b_uptodate) + return bh; + + return NULL; +} + void buffer_init(long buffer_end) { struct buffer_head * h = start_buffer; void * b; diff --git a/include/linux/fs.h b/include/linux/fs.h index ec15c06a83423eed9ddb69e9fe314951b6cad970..0b8793f71d0174c86fa2bb588999c4b7074fa97a 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -3,8 +3,17 @@ #include +#define READ 0 +#define WRITE 1 +#define READA 2 +#define WRITEA 3 + void buffer_init(long buffer_end); +#define MAJOR(a) (((unsigned)(a))>>8) +#define MINOR(a) ((a)&0xff) + + #define NR_HASH 307 #define BLOCK_SIZE 1024 @@ -27,4 +36,11 @@ struct buffer_head { struct buffer_head * b_next_free; }; +extern inline void wait_on_buffer(struct buffer_head* bh); + +extern struct buffer_head * getblk(int dev, int block); +extern void ll_rw_block(int rw, struct buffer_head * bh); + +extern struct buffer_head * bread(int dev,int block); + #endif diff --git a/include/linux/hdreg.h b/include/linux/hdreg.h index 0c23feafbaa2e4eeee8480ceae88e7d3f41e6f36..5d2cf704b4d42d37efd933dbb5d97974856ca7ad 100644 --- a/include/linux/hdreg.h +++ b/include/linux/hdreg.h @@ -14,7 +14,24 @@ #define HD_CMD 0x3f6 +/* Bits of HD_STATUS */ +#define ERR_STAT 0x01 +#define INDEX_STAT 0x02 +#define ECC_STAT 0x04 +#define DRQ_STAT 0x08 +#define SEEK_STAT 0x10 +#define WRERR_STAT 0x20 +#define READY_STAT 0x40 +#define BUSY_STAT 0x80 + #define WIN_RESTORE 0x10 +#define WIN_READ 0x20 +#define WIN_WRITE 0x30 +#define WIN_VERIFY 0x40 +#define WIN_FORMAT 0x50 +#define WIN_INIT 0x60 +#define WIN_SEEK 0x70 +#define WIN_DIAGNOSE 0x90 #define WIN_SPECIFY 0x91 #define WIN_IDENTIFY 0xEC diff --git a/kernel/blk_drv/Makefile b/kernel/blk_drv/Makefile index 1d0e958d451bbbfb2becd30b68794b107f68a670..6e9fbcdb0f91f7da3d425416f4dae35f1e66f4ab 100644 --- a/kernel/blk_drv/Makefile +++ b/kernel/blk_drv/Makefile @@ -2,7 +2,7 @@ AR := ar LD := ld GCC := gcc CCFLAG := -m32 -I../../include -nostdinc -ffreestanding -fno-pic -Wall -fomit-frame-pointer -fno-stack-protector -c -OBJS := hd.o +OBJS := hd.o ll_rw_blk.o blk_drv.a : $(OBJS) $(AR) rcs $@ $^ @@ -11,6 +11,9 @@ blk_drv.a : $(OBJS) hd.o : hd.c $(GCC) $(CCFLAG) -o $@ $< +ll_rw_blk.o : ll_rw_blk.c + $(GCC) $(CCFLAG) -o $@ $< + clean : rm *.o rm blk_drv.a diff --git a/kernel/blk_drv/blk.h b/kernel/blk_drv/blk.h index 65937dd56cac774f59be7d63b5243746043f1f48..0b39c46f89b0b2fe83c1e29630d4ae795f4ec509 100644 --- a/kernel/blk_drv/blk.h +++ b/kernel/blk_drv/blk.h @@ -3,11 +3,29 @@ #define NR_BLK_DEV 7 +#define NR_REQUEST 32 + +struct request { + int dev; + int cmd; + int errors; + unsigned long sector; + unsigned long nr_sectors; + char * buffer; + struct task_struct * waiting; + struct buffer_head * bh; + struct request * next; +}; + struct blk_dev_struct { void (*request_fn)(void); + struct request * current_request; }; -struct blk_dev_struct blk_dev[NR_BLK_DEV]; +extern struct blk_dev_struct blk_dev[NR_BLK_DEV]; +extern struct request request[NR_REQUEST]; + +#ifdef MAJOR_NR #if (MAJOR_NR == 3) #define DEVICE_NAME "harddisk" @@ -19,10 +37,17 @@ struct blk_dev_struct blk_dev[NR_BLK_DEV]; #define DEVICE_OFF(device) #endif +#define CURRENT (blk_dev[MAJOR_NR].current_request) +#define CURRENT_DEV DEVICE_NR(CURRENT->dev) + #ifdef DEVICE_INTR void (*DEVICE_INTR)(void) = NULL; #endif +extern inline void unlock_buffer(struct buffer_head * bh); +extern inline void end_request(int uptodate); +extern inline void lock_buffer(struct buffer_head * bh); + #ifdef DEVICE_TIMEOUT int DEVICE_TIMEOUT = 0; #define SET_INTR(x) (DEVICE_INTR = (x),DEVICE_TIMEOUT = 200) @@ -32,4 +57,32 @@ int DEVICE_TIMEOUT = 0; static void (DEVICE_REQUEST)(void); +#ifdef DEVICE_TIMEOUT +#define CLEAR_DEVICE_TIMEOUT DEVICE_TIMEOUT = 0; +#else +#define CLEAR_DEVICE_TIMEOUT +#endif + +#ifdef DEVICE_INTR +#define CLEAR_DEVICE_INTR DEVICE_INTR = 0; +#else +#define CLEAR_DEVICE_INTR +#endif + +#define INIT_REQUEST \ +repeat: \ + if (!CURRENT) {\ + CLEAR_DEVICE_INTR \ + CLEAR_DEVICE_TIMEOUT \ + return; \ + }\ + if (MAJOR(CURRENT->dev) != MAJOR_NR) \ + printk(DEVICE_NAME ": request list destroyed"); \ + if (CURRENT->bh) { \ + if (!CURRENT->bh->b_lock) \ + printk(DEVICE_NAME ": block not locked"); \ + } + +#endif + #endif diff --git a/kernel/blk_drv/hd.c b/kernel/blk_drv/hd.c index fb8517a53b8e1cb32cc63a6489b774f1942ca7d0..fa8644dff4da5d7b0e74a0b876c14a40a45fcab7 100644 --- a/kernel/blk_drv/hd.c +++ b/kernel/blk_drv/hd.c @@ -19,16 +19,14 @@ inb_p(0x71); \ #define MAX_HD 2 -short identity_buf[512]; - struct blk_dev_struct blk_dev[NR_BLK_DEV] = { - { NULL }, - { NULL }, - { NULL }, - { NULL }, - { NULL }, - { NULL }, - { NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, + { NULL, NULL }, }; struct hd_i_struct { @@ -37,6 +35,8 @@ struct hd_i_struct { struct task_struct* waiting; +struct buffer_head* bh; + #ifdef HD_TYPE struct hd_i_struct hd_info[] = { HD_TYPE }; #define NR_HD ((sizeof (hd_info))/(sizeof (struct hd_i_struct))) @@ -78,15 +78,18 @@ static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect, void identity_callback() { printk("in identity callback. \n"); - port_read(HD_DATA,identity_buf,256); - wake_up(&waiting); + port_read(HD_DATA, bh->b_data, 256); + unlock_buffer(bh); } void hd_identity() { + bh = getblk(3, 0); + lock_buffer(bh); + hd_out(0, 0, 0, 0, 0, WIN_IDENTIFY, identity_callback); - printf("id request done\n"); - //sleep_on(&waiting); - int sectors = ((int)identity_buf[61] << 16) + identity_buf[60]; + wait_on_buffer(bh); + short* buf = (short*)bh->b_data; + int sectors = ((int)buf[61] << 16) + buf[60]; printf("HD size: %dMB\n", sectors * 512 / 1024 / 1024); } @@ -95,7 +98,7 @@ int sys_setup(void * BIOS) { static int callable = 1; int i,drive; unsigned char cmos_disks; - //struct partition *p; + struct partition *p; //struct buffer_head * bh; if (!callable) @@ -141,26 +144,111 @@ int sys_setup(void * BIOS) { } - for (i=0 ; ib_data[510] != 0x55 || (unsigned char) bh->b_data[511] != 0xAA) { + printk("Bad partition table on drive %d\n\r",drive); + } + + p = 0x1BE + (void *)bh->b_data; + for (i=1;i<5;i++,p++) { + hd[i+5*drive].start_sect = p->start_sect; + hd[i+5*drive].nr_sects = p->nr_sects; + + printk("table %d: start at %d, has %d sects\n", drive, + hd[i+5*drive].start_sect, + hd[i+5*drive].nr_sects); + } + } return 0; } +static int win_result(void) { + int i = inb_p(HD_STATUS); + if ((i & (BUSY_STAT | READY_STAT | WRERR_STAT | SEEK_STAT | ERR_STAT)) + == (READY_STAT | SEEK_STAT)) + return 0; + if (i&1) i = inb(HD_ERROR); + return (1); +} + void unexpected_hd_interrupt() { printk("Unexpected HD interrupt\n\r"); } +static void read_intr(void) { + printk("read callback\n"); + if (win_result()) { + printk("hd read error.\n"); + return; + } + port_read(HD_DATA,CURRENT->buffer,256); + CURRENT->errors = 0; + CURRENT->buffer += 512; + CURRENT->sector++; + if (--CURRENT->nr_sectors) { + printk("nr_sectors:%d\n", CURRENT->nr_sectors); + SET_INTR(&read_intr); + return; + } + end_request(1); +} + void do_hd_request() { + unsigned int block,dev; + unsigned int sec,head,cyl; + unsigned int nsect; + + INIT_REQUEST; + dev = MINOR(CURRENT->dev); + block = CURRENT->sector; + + block += hd[dev].start_sect; + dev /= 5; + + __asm__("divl %4":"=a" (block),"=d" (sec):"0" (block),"1" (0), + "r" (hd_info[dev].sect)); + __asm__("divl %4":"=a" (cyl),"=d" (head):"0" (block),"1" (0), + "r" (hd_info[dev].head)); + sec++; + nsect = CURRENT->nr_sectors; + + if (CURRENT->cmd == READ) { + hd_out(dev,nsect,sec,head,cyl,WIN_READ,&read_intr); + } } -void hd_init(void) { +void hd_init() { blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; set_intr_gate(0x2E,&hd_interrupt); outb_p(inb_p(0x21)&0xfb,0x21); outb(inb_p(0xA1)&0xbf,0xA1); } +inline void end_request(int uptodate) { + printk("end request\n"); + DEVICE_OFF(CURRENT->dev); + if (CURRENT->bh) { + CURRENT->bh->b_uptodate = uptodate; + unlock_buffer(CURRENT->bh); + } + if (!uptodate) { + printk(DEVICE_NAME " I/O error\n\r"); + printk("dev %04x, block %d\n\r",CURRENT->dev, + CURRENT->bh->b_blocknr); + } + CURRENT->dev = -1; + CURRENT = CURRENT->next; +} + diff --git a/kernel/blk_drv/ll_rw_blk.c b/kernel/blk_drv/ll_rw_blk.c new file mode 100644 index 0000000000000000000000000000000000000000..bb699da95ede75c9cb267ac6458827cf97e1b4da --- /dev/null +++ b/kernel/blk_drv/ll_rw_blk.c @@ -0,0 +1,68 @@ +#include +#include +#include +#include + +#include "blk.h" + +struct request request[NR_REQUEST]; + +inline void lock_buffer(struct buffer_head * bh) { + printk("lock buffer\n"); + cli(); + while (bh->b_lock) + sleep_on(&bh->b_wait); + bh->b_lock=1; + sti(); +} + +inline void unlock_buffer(struct buffer_head * bh) { + printk("unlock buffer\n"); + if (!bh->b_lock) + printk("ll_rw_block.c: buffer not locked\n\r"); + bh->b_lock = 0; + wake_up(&bh->b_wait); +} + +static void add_request(struct blk_dev_struct * dev, struct request * req) { + struct request * tmp; + req->next = NULL; + cli(); + + if (req->bh) + req->bh->b_dirt = 0; + + if (!(tmp = dev->current_request)) { + dev->current_request = req; + sti(); + (dev->request_fn)(); + return; + } +} + +static void make_request(int major,int rw, struct buffer_head * bh) { + struct request * req; + lock_buffer(bh); + + req = request+NR_REQUEST; + + req->dev = bh->b_dev; + req->cmd = rw; + req->errors=0; + req->sector = bh->b_blocknr<<1; + req->nr_sectors = 2; + req->buffer = bh->b_data; + req->waiting = NULL; + req->bh = bh; + req->next = NULL; + add_request(major+blk_dev,req); +} + +void ll_rw_block(int rw, struct buffer_head * bh) { + unsigned int major; + major = MAJOR(bh->b_dev); + + make_request(major,rw,bh); +} + + diff --git a/kernel/sys_call.S b/kernel/sys_call.S index 1da7fbde997e5005b652cf7a17de687a5787c7d1..ceee7d130b850a401ef2d05dc556a7cb6a01318d 100644 --- a/kernel/sys_call.S +++ b/kernel/sys_call.S @@ -104,13 +104,14 @@ hd_interrupt: jmp 1f 1: jmp 1f 1: xorl %edx, %edx - movl %eax, hd_timeout + movl %edx, hd_timeout xchgl do_hd, %edx testl %edx, %edx jne 1f movl $unexpected_hd_interrupt, %edx -1: outb %al, $0x20 - call *%edx +1: call *%edx + movb $0x20, %al + outb %al, $0x20 popl %fs popl %es popl %ds