From: Ben Pfaff Date: Sun, 29 Aug 2004 19:18:25 +0000 (+0000) Subject: Basic disk detection stuff. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?p=pintos-anon;a=commitdiff_plain;h=8ed1dcfb47df08fa29070c8543453926cecdcd34 Basic disk detection stuff. --- diff --git a/src/Makefile.inc b/src/Makefile.inc index 6ffa350..a76c4a9 100644 --- a/src/Makefile.inc +++ b/src/Makefile.inc @@ -31,6 +31,7 @@ DEVICES_SRC = timer.c # Timer device. DEVICES_SRC += kbd.c # Keyboard device. DEVICES_SRC += vga.c # Video device. DEVICES_SRC += serial.c # Serial port device. +DEVICES_SRC += disk.c # IDE disk device. # Library code. LIB_SRC = debug.c # Debug helpers. diff --git a/src/devices/disk.c b/src/devices/disk.c index b40a8e8..a742ee4 100644 --- a/src/devices/disk.c +++ b/src/devices/disk.c @@ -1,4 +1,11 @@ #include "disk.h" +#include +#include "debug.h" +#include "io.h" +#include "interrupt.h" +#include "lib.h" +#include "synch.h" +#include "timer.h" #define reg_data(CHANNEL) ((CHANNEL)->reg_base + 0) #define reg_error(CHANNEL) ((CHANNEL)->reg_base + 1) @@ -10,17 +17,17 @@ #define reg_status(CHANNEL) ((CHANNEL)->reg_base + 7) #define reg_command(CHANNEL) reg_status (CHANNEL) #define reg_ctl(CHANNEL) ((CHANNEL)->reg_base + 0x206) -#define reg_alt(CHANNEL) reg_ctl (CHANNEL) +#define reg_alt_status(CHANNEL) reg_ctl (CHANNEL) /* Alternate Status Register bits. */ -#define ALT_BSY 0x80 -#define ALT_DRDY 0x40 -#define ALT_DF 0x20 -#define ALT_DSC 0x10 -#define ALT_DRQ 0x08 -#define ALT_CORR 0x04 -#define ALT_IDX 0x02 -#define ALT_ERR 0x01 +#define STA_BSY 0x80 +#define STA_DRDY 0x40 +#define STA_DF 0x20 +#define STA_DSC 0x10 +#define STA_DRQ 0x08 +#define STA_CORR 0x04 +#define STA_IDX 0x02 +#define STA_ERR 0x01 /* Control Register bits. */ #define CTL_SRST 0x04 @@ -36,7 +43,7 @@ struct disk { - const char *name; + char name[8]; struct channel *channel; int device; @@ -46,72 +53,75 @@ struct disk struct channel { - const char *name; + char name[8]; uint16_t reg_base; - int irq; + uint8_t irq; + + bool expecting_interrupt; + struct semaphore completion_wait; + struct disk dev[2]; }; #define CHANNEL_CNT (sizeof channels / sizeof *channels) -static struct channel channels[2] = { - {"ide0", 0x1f0, 14}, - {"ide1", 0x170, 15}, -}; +static struct channel channels[2]; static void -select_device (struct device *d) +select_device (const struct disk *d) { struct channel *c = d->channel; uint8_t dev = DEV_MBS; if (d->device == 1) dev |= DEV_DEV; outb (reg_device (c), dev); - inb (reg_alt (c)); - ndelay (400); + inb (reg_alt_status (c)); + timer_nsleep (400); } static void -wait_idle (struct device *d) +wait_idle (const struct disk *d) { int i; for(i = 0; i < 1000; i++) { - if ((inb (reg_status (d->channel)) & (STA_BUSY | STA_DRQ)) == 0) + if ((inb (reg_status (d->channel)) & (STA_BSY | STA_DRQ)) == 0) return; - udelay (10); + timer_usleep (10); } printk ("%s: idle timeout\n", d->name); } static void -select_device_wait (struct device *d) +select_device_wait (const struct disk *d) { wait_idle (d); select_device (d); wait_idle (d); } +/* Wait up to 30 seconds for disk D to clear BSY. */ static bool -busy_wait (struct channel *c) +busy_wait (const struct disk *d) { + struct channel *c = d->channel; int i; for (i = 0; i < 3000; i++) { if (i == 700) - printk ("%s: busy, waiting..."); - if (!(inb (reg_alt (c)) & ALT_BSY)) + printk ("%s: busy, waiting...", d->name); + if (!(inb (reg_alt_status (c)) & STA_BSY)) { if (i >= 700) printk ("ok\n"); return true; } - mdelay (10); + timer_msleep (10); } - printk ("failed\n", c->name); + printk ("failed\n"); return false; } @@ -119,13 +129,14 @@ static void reset_channel (struct channel *c) { bool present[2]; + int device; /* The ATA reset sequence depends on which devices are present, so we start by detecting device presence. */ for (device = 0; device < 2; device++) { struct disk *d = &c->dev[device]; - + select_device (d); outb (reg_nsect (c), 0x55); @@ -144,38 +155,40 @@ reset_channel (struct channel *c) /* Issue soft reset sequence, which selects device 0 as a side effect. Also enable interrupts. */ outb (reg_ctl (c), 0); - delay (1); + timer_usleep (10); outb (reg_ctl (c), CTL_SRST); - delay (1); + timer_usleep (10); outb (reg_ctl (c), 0); - delay (150); + + timer_msleep (150); /* Wait for device 0 to clear BSY. */ if (present[0]) { - select_device (c->dev[0]); - busy_wait (c); + select_device (&c->dev[0]); + busy_wait (&c->dev[0]); } /* Wait for device 1 to clear BSY. */ if (present[1]) { - select_device (c->dev[1]); + int i; + + select_device (&c->dev[1]); for (i = 0; i < 3000; i++) { if (inb (reg_nsect (c)) == 1 && inb (reg_lbal (c)) == 1) break; - delay (10); + timer_msleep (10); } - busy_wait (c); + busy_wait (&c->dev[1]); } } static bool -check_ata_device (const struct device *d) +check_ata_device (struct disk *d) { struct channel *c = d->channel; - bool maybe_slave; /* If D is a master, could a slave exist? */ uint8_t error, lbam, lbah; select_device (d); @@ -205,49 +218,121 @@ execute_command (struct disk *d, uint8_t command) up'd by the completion handler. */ ASSERT (intr_get_level () == IF_ON); + /* Atomically note that we expect an interrupt and send the + command to the device. */ intr_disable (); - sema_init (&completion_sema, 0, "disk"); - intr_enable (); - + c->expecting_interrupt = true; outb (reg_command (c), command); + intr_enable (); - sema_down (); + /* Wait for the command to complete. */ + sema_down (&c->completion_wait); } -void +static bool +input_sector (struct channel *c, void *sector) +{ + uint8_t status; + + ASSERT (sector != NULL); + + status = inb (reg_status (c)); + if (status & STA_DRQ) + { + /* Command was successful. Read data into SECTOR. */ + insw (reg_data (c), sector, DISK_SECTOR_SIZE / 2); + return true; + } + else + { + /* Command failed. */ + return false; + } +} + +static void identify_ata_device (struct disk *d) { - struct channel *c = d->channel; + uint16_t id[DISK_SECTOR_SIZE / 2]; ASSERT (d->is_ata); select_device_wait (d); - execute_command (CMD_IDENTIFY); - - + execute_command (d, CMD_IDENTIFY); + busy_wait (d); + + if (!input_sector (d->channel, id)) + { + d->is_ata = false; + return; + } + d->capacity = id[57] | ((uint32_t) id[58] << 16); + printk ("%s: detected %"PRDSNu" sector (%d MB) disk\n", + d->name, d->capacity, d->capacity * DISK_SECTOR_SIZE / 1024 / 1024); } -void -disk_init (void) +static void +interrupt_handler (struct intr_frame *f) { struct channel *c; for (c = channels; c < channels + CHANNEL_CNT; c++) + if (f->vec_no == c->irq) + { + if (c->expecting_interrupt) + sema_up (&c->completion_wait); + else + printk ("%s: unexpected interrupt\n", c->name); + return; + } + + NOT_REACHED (); +} + +void +disk_init (void) +{ + size_t channel; + + for (channel = 0; channel < CHANNEL_CNT; channel++) { + struct channel *c = &channels[channel]; int device; - /* Register interrupt handler. */ - register_irq (c); - - /* Initialize device state. */ + /* Initialize channel. */ + snprintf (c->name, sizeof c->name, "ide%d", channel); + switch (channel) + { + case 0: + c->reg_base = 0x1f0; + c->irq = 14 + 0x20; + break; + case 1: + c->reg_base = 0x170; + c->irq = 15 + 0x20; + break; + default: + NOT_REACHED (); + } + c->expecting_interrupt = false; + sema_init (&c->completion_wait, 0, c->name); + + /* Initialize devices. */ for (device = 0; device < 2; device++) { struct disk *d = &c->dev[device]; + snprintf (d->name, sizeof d->name, "%s:%d", c->name, device); d->channel = c; d->device = device; + + d->is_ata = false; + d->capacity = 0; } + /* Register interrupt handler. */ + intr_register (c->irq, 0, IF_OFF, interrupt_handler, c->name); + /* Reset hardware. */ reset_channel (c); @@ -266,7 +351,7 @@ struct disk * disk_get (int idx) { ASSERT (idx >= 0 && idx < 4); - return &channel[idx / 2].dev[idx % 2]; + return &channels[idx / 2].dev[idx % 2]; } disk_sector_no disk_size (struct disk *); diff --git a/src/devices/disk.h b/src/devices/disk.h index 9df00ff..5f89d1d 100644 --- a/src/devices/disk.h +++ b/src/devices/disk.h @@ -1,11 +1,13 @@ #ifndef HEADER_DISK_H #define HEADER_DISK_H 1 +#include #include #define DISK_SECTOR_SIZE 512 typedef uint32_t disk_sector_no; +#define PRDSNu PRId32 /* For use with printk(). */ void disk_init (void); struct disk *disk_get (int idx); diff --git a/src/threads/init.c b/src/threads/init.c index 6da714e..54a98d2 100644 --- a/src/threads/init.c +++ b/src/threads/init.c @@ -18,6 +18,7 @@ #include "vga.h" #ifdef FILESYS #include "filesys.h" +#include "disk.h" #endif /* Size of kernel static code and data, in 4 kB pages. */ @@ -32,6 +33,7 @@ void power_off (void); static void main_thread (void *aux UNUSED) { + disk_init (); thread_execute ("a.out"); }