From b4e9c266d656c6b595cc57920a34937776acc300 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Thu, 11 Dec 2008 21:03:36 -0800 Subject: [PATCH] usb.patch, with conflicts and some warnings fixed --- src/Makefile.build | 6 + src/devices/pci.c | 812 ++++++++++++++++++++++++ src/devices/pci.h | 112 ++++ src/devices/pci_lookup.h | 138 ++++ src/devices/usb.c | 776 +++++++++++++++++++++++ src/devices/usb.h | 197 ++++++ src/devices/usb_ehci.c | 52 ++ src/devices/usb_hub.c | 54 ++ src/devices/usb_hub.h | 7 + src/devices/usb_storage.c | 441 +++++++++++++ src/devices/usb_storage.h | 1 + src/devices/usb_uhci.c | 1263 +++++++++++++++++++++++++++++++++++++ src/lib/endian.h | 19 + src/threads/init.c | 30 +- src/threads/interrupt.c | 48 ++ src/threads/interrupt.h | 5 + src/threads/palloc.c | 1 + src/threads/palloc.h | 9 +- src/threads/pte.h | 24 +- src/userprog/pagedir.c | 2 +- 20 files changed, 3989 insertions(+), 8 deletions(-) create mode 100644 src/devices/pci.c create mode 100644 src/devices/pci.h create mode 100644 src/devices/pci_lookup.h create mode 100644 src/devices/usb.c create mode 100644 src/devices/usb.h create mode 100644 src/devices/usb_ehci.c create mode 100644 src/devices/usb_hub.c create mode 100644 src/devices/usb_hub.h create mode 100644 src/devices/usb_storage.c create mode 100644 src/devices/usb_storage.h create mode 100644 src/devices/usb_uhci.c create mode 100644 src/lib/endian.h diff --git a/src/Makefile.build b/src/Makefile.build index e997d27..763f0af 100644 --- a/src/Makefile.build +++ b/src/Makefile.build @@ -36,6 +36,12 @@ devices_SRC += devices/intq.c # Interrupt queue. devices_SRC += devices/rtc.c # Real-time clock. devices_SRC += devices/shutdown.c # Reboot and power off. devices_SRC += devices/speaker.c # PC speaker. +devices_SRC += devices/pci.c # PCI bus. +devices_SRC += devices/usb.c # USB layer. +devices_SRC += devices/usb_uhci.c # UHCI device. +devices_SRC += devices/usb_ehci.c # EHCI device. +devices_SRC += devices/usb_storage.c # USB mass storage class driver. +devices_SRC += devices/usb_hub.c # USB hub class driver. # Library code shared between kernel and user programs. lib_SRC = lib/debug.c # Debug helpers. diff --git a/src/devices/pci.c b/src/devices/pci.c new file mode 100644 index 0000000..2df8e2a --- /dev/null +++ b/src/devices/pci.c @@ -0,0 +1,812 @@ +#define PCI_TRANSLATION_ENABLE 1 + +#include "devices/pci.h" +#include "threads/init.h" +#include "threads/malloc.h" +#include "threads/interrupt.h" +#include "threads/pte.h" +#include "threads/io.h" +#include "pci_lookup.h" +#include +#include +#include +#include +#include + +extern uint32_t *base_page_dir; + +#define PCI_REG_ADDR 0xcf8 +#define PCI_REG_DATA 0xcfc +#define pci_config_offset(bus, dev, func, reg) (0x80000000 | ((bus) << 16) | ((dev) << 11) | ((func) << 8) | (reg & (~3))) + +#define PCI_MAX_DEV_PER_BUS 32 +#define PCI_MAX_FUNC_PER_DEV 8 + +#define PCI_HEADER_SZ 256 + +#define PCI_CMD_IO 0x001 /* enable io response */ +#define PCI_CMD_MEMORY 0x002 /* enable memory response */ +#define PCI_CMD_MASTER 0x004 /* enable bus mastering */ +#define PCI_CMD_SPECIAL 0x008 /* enable special cycles */ +#define PCI_CMD_INVALIDATE 0x010 /* memory write + invalidate */ +#define PCI_CMD_PALETTE 0x020 /* palette snooping */ +#define PCI_CMD_PARITY 0x040 /* parity checking */ +#define PCI_CMD_WAIT 0x080 /* address/data stepping */ +#define PCI_CMD_SERR 0x100 /* serr */ +#define PCI_CMD_FASTBACK 0x200 /* back-to-back writing */ +#define PCI_CMD_INTX_DISABLE 0x400 /* emulation disable */ + +#define PCI_STATUS_CAPLIST 0x10 /* capability list */ +#define PCI_STATUS_66MHZ 0x20 /* 66mhz pci 2.1 bus */ +#define PCI_STATUS_UDF 0x40 /* user definable features */ +#define PCI_STATUS_FASTBACK 0x80 /* fast back to back */ +#define PCI_STATUS_PARITY 0x100 /* parity error detected */ +#define PCI_STATUS_DEVSEL 0x600 /* devsel mask */ +#define PCI_STATUS_DEVSEL_FAST 0 +#define PCI_STATUS_DEVSEL_MED 0x200 +#define PCI_STATUS_DEVSEL_SLOW 0x400 +#define PCI_STATUS_SIG_ABORT 0x0800 /* set on target abort */ +#define PCI_STATUS_REC_ABORT 0x1000 /* master ack of abort */ +#define PCI_STATUS_REC_ABORT_M 0x2000 /* set on master abort */ +#define PCI_STATUS_SERR 0x4000 /* system error */ +#define PCI_STATUS_PARITY2 0x8000 /* set on parity err */ + +#define PCI_HEADER_NORMAL 0 +#define PCI_HEADER_BRIDGE 1 +#define PCI_HEADER_CARDBUS 2 + +#define PCI_HEADER_MASK 0x7f +#define PCI_HEADER_MULTIFUNC 0x80 + +#define pci_get_reg_offset(x) (&(((pci_config_header*)(NULL))->x)) + +#define PCI_VENDOR_INVALID 0xffff + +#define PCI_BASEADDR_IO 0x00000001 +#define PCI_BASEADDR_TYPEMASK 0x00000006 +#define PCI_BASEADDR_32BIT 0x00000000 +#define PCI_BASEADDR_64BIT 0x00000004 +#define PCI_BASEADDR_PREFETCH 0x00000008 +#define PCI_BASEADDR_ADDR32 0xfffffff0 +#define PCI_BASEADDR_ADDR64 0xfffffffffffffff0 +#define PCI_BASEADDR_IOPORT 0xfffffffc + +#pragma pack(1) + +struct pci_config_header +{ + uint16_t pci_vendor_id; + uint16_t pci_device_id; + + uint16_t pci_command; + uint16_t pci_status; + + uint8_t pci_revision; + + /* pci class */ + uint8_t pci_interface; + uint8_t pci_minor; + uint8_t pci_major; + + uint8_t pci_cachelinesz; + uint8_t pci_latency; + uint8_t pci_header; /* header type */ + uint8_t pci_bist; /* self test */ + + uint32_t pci_base_reg[6]; + uint32_t pci_cis; /* cardbus cis pointer */ + uint16_t pci_sub_vendor_id; + uint16_t pci_sub_id; + uint32_t pci_rom_addr; + uint8_t pci_capabilities; + uint8_t pci_resv[7]; + uint8_t pci_int_line; + uint8_t pci_int_pin; + uint8_t pci_min_gnt; + uint8_t pci_max_latency; +}; + +#pragma pack() + +#define PCI_BASE_COUNT 6 +struct pci_dev +{ + struct pci_config_header pch; + uint8_t bus, dev, func; + int base_reg_size[PCI_BASE_COUNT]; + + pci_handler_func *irq_handler; + void *irq_handler_aux; + + struct list io_ranges; + struct list_elem peer; + struct list_elem int_peer; +}; + +enum pci_io_type +{ PCI_IO_MEM, PCI_IO_PORT }; + +/* represents a PCI IO range */ +struct pci_io +{ + struct pci_dev *dev; + enum pci_io_type type; + size_t size; /* bytes in range */ + union + { + void *ptr; /* virtual memory address */ + int port; /* io port */ + } addr; + struct list_elem peer; /* linkage */ +}; + + +static void pci_write_config (int bus, int dev, int func, int reg, + int size, uint32_t data); +static uint32_t pci_read_config (int bus, int dev, int func, int reg, + int size); +static void pci_read_all_config (int bus, int dev, int func, + struct pci_config_header *pch); +static int pci_scan_bus (int bus); +static int pci_probe (int bus, int dev, int func, + struct pci_config_header *ph); +static int pci_pci_bridge (struct pci_dev *pd); +static void pci_power_on (struct pci_dev *pd); +static void pci_setup_io (struct pci_dev *pd); +static void pci_interrupt (struct intr_frame *); +static void pci_print_dev_info (struct pci_dev *pd); +static void *pci_alloc_mem (void *phys_ptr, int pages); + +static struct list devices; +static struct list int_devices; + +/* number of pages that have been allocated to pci devices in the pci zone */ +static int num_pci_pages; + +void +pci_init (void) +{ + list_init (&devices); + list_init (&int_devices); + + num_pci_pages = 0; + + pci_scan_bus (0); + pci_print_stats (); +} + +struct pci_dev * +pci_get_device (int vendor, int device, int func, int n) +{ + struct list_elem *e; + int count; + + count = 0; + e = list_begin (&devices); + while (e != list_end (&devices)) + { + struct pci_dev *pd; + + pd = list_entry (e, struct pci_dev, peer); + if (pd->pch.pci_vendor_id == vendor && pd->pch.pci_device_id == device) + if (pd->func == func) + { + if (count == n) + return pd; + count++; + } + + e = list_next (e); + } + + return NULL; +} + +struct pci_dev * +pci_get_dev_by_class (int major, int minor, int iface, int n) +{ + struct list_elem *e; + int count; + + count = 0; + e = list_begin (&devices); + while (e != list_end (&devices)) + { + struct pci_dev *pd; + + pd = list_entry (e, struct pci_dev, peer); + if (pd->pch.pci_major == major && pd->pch.pci_minor == minor && + pd->pch.pci_interface == iface) + { + if (count == n) + return pd; + count++; + } + + e = list_next (e); + } + + return NULL; + +} + +struct pci_io * +pci_io_enum (struct pci_dev *pio, struct pci_io *last) +{ + struct list_elem *e; + + if (last != NULL) + e = list_next (&last->peer); + else + e = list_begin (&pio->io_ranges); + + if (e == list_end (&pio->io_ranges)) + return NULL; + + return list_entry (e, struct pci_io, peer); +} + +void +pci_register_irq (struct pci_dev *pd, pci_handler_func * f, void *aux) +{ + int int_vec; + ASSERT (pd != NULL); + ASSERT (pd->irq_handler == NULL); + + pd->irq_handler_aux = aux; + pd->irq_handler = f; + int_vec = pd->pch.pci_int_line + 0x20; + + list_push_back (&int_devices, &pd->int_peer); + + /* ensure that pci interrupt is hooked */ + if (!intr_is_registered (int_vec)) + intr_register_ext (int_vec, pci_interrupt, "PCI"); +} + +void +pci_unregister_irq (struct pci_dev *pd) +{ + ASSERT (pd != NULL); + + intr_disable (); + list_remove (&pd->int_peer); + intr_enable (); + + pd->irq_handler = NULL; + pd->irq_handler_aux = NULL; +} + +size_t +pci_io_size (struct pci_io *pio) +{ + ASSERT (pio != NULL); + + return pio->size; +} + +void +pci_reg_write32 (struct pci_io *pio, int reg, uint32_t data) +{ + ASSERT (pio != NULL); + ASSERT ((unsigned) reg < pio->size); + + if (pio->type == PCI_IO_MEM) + { + *((uint32_t *) (pio->addr.ptr + reg)) = data; + } + else if (pio->type == PCI_IO_PORT) + { + outl (pio->addr.port + reg, data); + } + else + PANIC ("pci: Invalid IO type\n"); +} + +void +pci_reg_write16 (struct pci_io *pio, int reg, uint16_t data) +{ + ASSERT (pio != NULL); + ASSERT ((unsigned) reg < pio->size); + + if (pio->type == PCI_IO_MEM) + { + *((uint16_t *) (pio->addr.ptr + reg)) = data; + } + else if (pio->type == PCI_IO_PORT) + { + outw (pio->addr.port + reg, data); + } + else + PANIC ("pci: Invalid IO type\n"); +} + +void +pci_reg_write8 (struct pci_io *pio, int reg, uint8_t data) +{ + ASSERT (pio != NULL); + ASSERT ((unsigned) reg < pio->size); + + if (pio->type == PCI_IO_MEM) + { + ((uint8_t *) pio->addr.ptr)[reg] = data; + } + else if (pio->type == PCI_IO_PORT) + { + outb (pio->addr.port + reg, data); + } + else + PANIC ("pci: Invalid IO type\n"); +} + +uint32_t +pci_reg_read32 (struct pci_io *pio, int reg) +{ + uint32_t ret; + + ASSERT (pio != NULL); + ASSERT ((unsigned) reg < pio->size); + + if (pio->type == PCI_IO_MEM) + { + ret = ((uint32_t *) pio->addr.ptr)[reg]; + } + else if (pio->type == PCI_IO_PORT) + { + ret = inl (pio->addr.port + reg); + } + else + PANIC ("pci: Invalid IO type\n"); + + return ret; +} + +uint16_t +pci_reg_read16 (struct pci_io * pio, int reg) +{ + uint16_t ret; + + ASSERT (pio != NULL); + ASSERT ((unsigned) reg < pio->size); + + ret = 0; + if (pio->type == PCI_IO_MEM) + { + ret = ((uint16_t *) pio->addr.ptr)[reg]; + } + else if (pio->type == PCI_IO_PORT) + { + ret = inw (pio->addr.port + reg); + } + else + PANIC ("pci: Invalid IO type\n"); + + return ret; + +} + +uint8_t +pci_reg_read8 (struct pci_io * pio, int reg) +{ + uint8_t ret; + + ASSERT (pio != NULL); + ASSERT ((unsigned) reg < pio->size); + + if (pio->type == PCI_IO_MEM) + { + ret = ((uint8_t *) pio->addr.ptr)[reg]; + } + else if (pio->type == PCI_IO_PORT) + { + ret = inb (pio->addr.port + reg); + } + else + PANIC ("pci: Invalid IO type\n"); + + return ret; +} + +void +pci_read_in (struct pci_io *pio UNUSED, int off UNUSED, size_t size UNUSED, + void *buf UNUSED) +{ + PANIC ("pci_read_in: STUB"); +} + +void +pci_write_out (struct pci_io *pio UNUSED, int off UNUSED, size_t size UNUSED, + const void *buf UNUSED) +{ + PANIC ("pci_write_out: STUB"); +} + +static void +pci_write_config (int bus, int dev, int func, int reg, + int size, uint32_t data) +{ + int config_offset; + + config_offset = pci_config_offset (bus, dev, func, reg); + + outl (PCI_REG_ADDR, config_offset); + + switch (size) + { + case 1: + outb (PCI_REG_DATA + (reg & 3), data); + break; + + case 2: + outw (PCI_REG_DATA + (reg & 3), data); + break; + + case 4: + outl (PCI_REG_DATA, data); + break; + } +} + +static uint32_t +pci_read_config (int bus, int dev, int func, int reg, int size) +{ + uint32_t ret; + int config_offset; + + config_offset = pci_config_offset (bus, dev, func, reg); + + outl (PCI_REG_ADDR, config_offset); + + switch (size) + { + case 1: + ret = inb (PCI_REG_DATA); + break; + case 2: + ret = inw (PCI_REG_DATA); + break; + case 4: + ret = inl (PCI_REG_DATA); + break; + default: + PANIC ("pci: Strange config read size\n"); + } + + return ret; +} + +/* read entire configuration header into memory */ +static void +pci_read_all_config (int bus, int dev, int func, + struct pci_config_header *pch) +{ + unsigned int i; + for (i = 0; i < ((sizeof (struct pci_config_header) + 3) & ~3) / 4; i++) + { + ((uint32_t *) pch)[i] = pci_read_config (bus, dev, func, i * 4, 4); + } +} + + +/** scan PCI bus for all devices */ +static int +pci_scan_bus (int bus) +{ + int dev; + int max_bus; + + max_bus = 0; + + for (dev = 0; dev < PCI_MAX_DEV_PER_BUS; dev++) + { + struct pci_config_header pch; + int func_cnt, func; + + pci_read_all_config (bus, dev, 0, &pch); + + if (pch.pci_vendor_id == PCI_VENDOR_INVALID) + continue; + + func_cnt = 8; + if (!(pch.pci_header & PCI_HEADER_MULTIFUNC)) + { + func_cnt = 1; + } + + for (func = 0; func < func_cnt; func++) + { + int retbus; + retbus = pci_probe (bus, dev, func, &pch); + if (retbus > max_bus) + max_bus = retbus; + } + } + + return max_bus; +} + +/* get all information for a PCI device given bus/dev/func + add pci device to device list if applicable + return a new bus number if new bus is found + */ +static int +pci_probe (int bus, int dev, int func, struct pci_config_header *ph) +{ + struct pci_dev *pd; + + if (func != 0) + { + pci_read_all_config (bus, dev, func, ph); + if (ph->pci_vendor_id == PCI_VENDOR_INVALID) + return bus; + } + + pd = malloc (sizeof (struct pci_dev)); + memcpy (&pd->pch, ph, sizeof (struct pci_config_header)); + pd->irq_handler = NULL; + pd->irq_handler_aux = NULL; + pd->bus = bus; + pd->dev = dev; + pd->func = func; + + list_init (&pd->io_ranges); + list_push_back (&devices, &pd->peer); + + + if (ph->pci_major == PCI_MAJOR_BRIDGE) + { + if (ph->pci_minor == PCI_MINOR_PCI) + return pci_pci_bridge (pd); + } + + pci_setup_io (pd); + pci_power_on (pd); + + return bus; +} + +static void +pci_setup_io (struct pci_dev *pd) +{ + int i; + for (i = 0; i < PCI_BASE_COUNT; i++) + { + uint32_t tmp; + struct pci_io *pio; + + if (pd->pch.pci_base_reg[i] == 0) + { + continue; + } + + /* determine io granularity.. */ + pci_write_config (pd->bus, pd->dev, pd->func, + offsetof (struct pci_config_header, pci_base_reg[i]), + 4, ~0); + + tmp = + pci_read_config (pd->bus, pd->dev, pd->func, + offsetof (struct pci_config_header, pci_base_reg[i]), + 4); + + /* configure BAR to the default */ + pci_write_config (pd->bus, pd->dev, pd->func, + offsetof (struct pci_config_header, pci_base_reg[i]), + 4, pd->pch.pci_base_reg[i]); + + pio = malloc (sizeof (struct pci_io)); + pio->dev = pd; + + if (tmp & PCI_BASEADDR_IO) + { + pio->type = PCI_IO_PORT; + pio->size = (uint16_t) ((~tmp + 1) & 0xffff) + 1; + pio->addr.port = pd->pch.pci_base_reg[i] & ~1; + } + else + { + uint32_t ofs; + + pio->type = PCI_IO_MEM; + pio->size = ROUND_UP ((~tmp + 1), PGSIZE); + ofs = (pd->pch.pci_base_reg[i] & 0xfffffff0 & PGMASK); + pio->addr.ptr = pci_alloc_mem ((void *) pd->pch.pci_base_reg[i], + pio->size / PGSIZE); + if (pio->addr.ptr == NULL) + { + printf ("PCI: %d pages for %d:%d.%d failed - may not work\n", + pio->size / PGSIZE, pd->bus, pd->dev, pd->func); + free (pio); + pio = NULL; + } + else + { + pio->addr.ptr = (void *) ((uintptr_t) pio->addr.ptr + ofs); + } + } + + /* add IO struct to device, if valid */ + if (pio != NULL) + list_push_back (&pd->io_ranges, &pio->peer); + } + +} + +static void +pci_power_on (struct pci_dev *pd UNUSED) +{ + /* STUB */ +} + +static int +pci_pci_bridge (struct pci_dev *pd) +{ + int max_bus; + uint16_t command; + + /* put bus into offline mode */ + command = pd->pch.pci_command; + command &= ~0x3; + pci_write_config (pd->bus, pd->dev, pd->func, + offsetof (struct pci_config_header, pci_command), + 2, command); + pd->pch.pci_command = command; + + /* set up primary bus */ + pci_write_config (pd->bus, pd->dev, pd->func, 0x18, 1, pd->bus); + /* secondary bus */ + pci_write_config (pd->bus, pd->dev, pd->func, 0x19, 1, pd->bus + 1); + + /* disable subordinates */ + pci_write_config (pd->bus, pd->dev, pd->func, 0x1a, 1, 0xff); + + /* scan this new bus */ + max_bus = pci_scan_bus (pd->bus + 1); + + /* set subordinate to the actual last bus */ + pci_write_config (pd->bus, pd->dev, pd->func, 0x1a, 1, max_bus); + + /* put online */ + command |= 0x03; + pci_write_config (pd->bus, pd->dev, pd->func, + offsetof (struct pci_config_header, pci_command), + 2, command); + pd->pch.pci_command = command; + + return max_bus; +} + +/* alert all PCI devices waiting on interrupt line that IRQ fired */ +static void +pci_interrupt (struct intr_frame *frame) +{ + struct list_elem *e; + int int_line; + + int_line = frame->vec_no - 0x20; + e = list_begin (&int_devices); + while (e != list_end (&int_devices)) + { + struct pci_dev *pd; + + pd = list_entry (e, struct pci_dev, int_peer); + if (pd->pch.pci_int_line == int_line) + pd->irq_handler (pd->irq_handler_aux); + e = list_next (e); + } +} + +/* display information on all USB devices */ +void +pci_print_stats (void) +{ + struct list_elem *e; + + e = list_begin (&devices); + while (e != list_end (&devices)) + { + struct pci_dev *pd; + + pd = list_entry (e, struct pci_dev, peer); + pci_print_dev_info (pd); + + e = list_next (e); + } +} + +static void +pci_print_dev_info (struct pci_dev *pd) +{ + printf ("PCI Device %d:%d.%d (%x,%x): %s - %s (%s) IRQ %d\n", + pd->bus, pd->dev, pd->func, + pd->pch.pci_vendor_id, + pd->pch.pci_device_id, + pci_lookup_vendor (pd->pch.pci_vendor_id), + pci_lookup_device (pd->pch.pci_vendor_id, pd->pch.pci_device_id), + pci_lookup_class (pd->pch.pci_major, pd->pch.pci_minor, + pd->pch.pci_interface), pd->pch.pci_int_line); +} + +void +pci_mask_irq (struct pci_dev *pd) +{ + intr_irq_mask (pd->pch.pci_int_line); +} + +void +pci_unmask_irq (struct pci_dev *pd) +{ + intr_irq_unmask (pd->pch.pci_int_line); +} + +void +pci_write_config16 (struct pci_dev *pd, int off, uint16_t data) +{ + pci_write_config (pd->bus, pd->dev, pd->func, off, 2, data); +} + +void +pci_write_config32 (struct pci_dev *pd, int off, uint32_t data) +{ + pci_write_config (pd->bus, pd->dev, pd->func, off, 4, data); +} + +void +pci_write_config8 (struct pci_dev *pd, int off, uint8_t data) +{ + pci_write_config (pd->bus, pd->dev, pd->func, off, 1, data); +} + +uint8_t +pci_read_config8 (struct pci_dev *pd, int off) +{ + return pci_read_config (pd->bus, pd->dev, pd->func, off, 1); +} + +uint16_t +pci_read_config16 (struct pci_dev * pd, int off) +{ + return pci_read_config (pd->bus, pd->dev, pd->func, off, 2); +} + +uint32_t +pci_read_config32 (struct pci_dev * pd, int off) +{ + return pci_read_config (pd->bus, pd->dev, pd->func, off, 4); +} + + +/** allocate PCI memory pages for PCI devices */ +static void * +pci_alloc_mem (void *phys_ptr, int pages) +{ + void *vaddr; + int i; + + phys_ptr = (void *) ((uintptr_t) phys_ptr & ~PGMASK); + + /* not enough space to allocate? */ + if ((unsigned) (num_pci_pages + pages) >= (unsigned) PCI_ADDR_ZONE_PAGES) + { + return NULL; + } + + /* insert into PCI_ZONE */ + for (i = 0; i < pages; i++) + { + uint32_t pte_idx = (num_pci_pages + i) % 1024; + uint32_t pde_idx = (num_pci_pages + i) / 1024; + uint32_t *pt; + uint32_t pte; + + pde_idx += pd_no ((void *) PCI_ADDR_ZONE_BEGIN); + pte = ((uint32_t) phys_ptr + (i * PGSIZE)) | PTE_P | PTE_W | PTE_CD; + pt = (uint32_t *) (ptov (init_page_dir[pde_idx] & ~PGMASK)); + pt[pte_idx] = pte; + } + + vaddr = (void *) (PCI_ADDR_ZONE_BEGIN + (num_pci_pages * PGSIZE)); + num_pci_pages += pages; + + return vaddr; +} diff --git a/src/devices/pci.h b/src/devices/pci.h new file mode 100644 index 0000000..258c022 --- /dev/null +++ b/src/devices/pci.h @@ -0,0 +1,112 @@ +#ifndef DEVICES_PCI_H +#define DEVICES_PCI_H + +#include +#include + +/**************************/ +/* some major/minor pairs */ +/**************************/ +#define PCI_MAJOR_EARLY 0 +#define PCI_MINOR_VGA 1 + +#define PCI_MAJOR_MASS_STORAGE 1 /* mass storage controller */ +#define PCI_MINOR_SCSI 0 +#define PCI_MINOR_IDE 1 +#define PCI_MINOR_FLOPPY 2 +#define PCI_MINOR_IPI 3 +#define PCI_MINOR_RAID 4 +#define PCI_MINOR_MS_OTHER 0x80 + +#define PCI_MAJOR_NETWORK 2 /* network controller */ +#define PCI_MINOR_ETHERNET 0 +#define PCI_MINOR_TOKENRING 1 +#define PCI_MINOR_FDDI 2 +#define PCI_MINOR_ATM 3 +#define PCI_MINOR_ISDN 4 +#define PCI_MINOR_NET_OTHER 0x80 + +#define PCI_MAJOR_DISPLAY 3 /* display controller */ +#define PCI_MAJOR_MULTIMEDIA 4 /* multimedia device */ +#define PCI_MAJOR_MEMORY 5 /* memory controller */ + +#define PCI_MAJOR_BRIDGE 6 /* bus bridge controller */ +#define PCI_MINOR_HOST 0 +#define PCI_MINOR_ISA 1 +#define PCI_MINOR_EISA 2 +#define PCI_MINOR_MCA 3 +#define PCI_MINOR_PCI 4 +#define PCI_MINOR_PCMCIA 5 +#define PCI_MINOR_NUBUS 6 +#define PCI_MINOR_CARDBUS 7 +#define PCI_MINOR_RACEWAY 8 + +#define PCI_MAJOR_SIMPLE_COMM 7 + +#define PCI_MAJOR_BASE_PERIPHERAL 8 +#define PCI_MINOR_PIC 0 +#define PCI_MINOR_DMA 1 +#define PCI_MINOR_TIMER 2 +#define PCI_MINOR_RTC 3 +#define PCI_MINOR_HOTPLUG 4 + + +#define PCI_MAJOR_INPUT 9 +#define PCI_MAJOR_DOCK 10 +#define PCI_MAJOR_PROCESSOR 11 +#define PCI_MINOR_386 0 +#define PCI_MINOR_486 1 +#define PCI_MINOR_PENTIUM 2 +#define PCI_MINOR_ALPHA 0x10 +#define PCI_MINOR_PPC 0x20 +#define PCI_MINOR_MIPS 0x30 +#define PCI_MINOR_COPROC 0x40 + +#define PCI_MAJOR_SERIALBUS 12 +#define PCI_MINOR_FIREWIRE 0 +#define PCI_MINOR_ACCESS 1 +#define PCI_MINOR_SSA 2 +#define PCI_MINOR_USB 3 +#define PCI_USB_IFACE_UHCI 0 +#define PCI_USB_IFACE_OHCI 0x10 +#define PCI_USB_IFACE_EHCI 0x20 +#define PCI_MINOR_FIBRE 4 +#define PCI_MAJOR_UNDEF 0xff + +typedef void pci_handler_func (void *AUX); + +/* used for io range for a pci device */ +struct pci_io; + +/* structure representing a specific pci device/function */ +struct pci_dev; + +void pci_init (void); +struct pci_dev *pci_get_device (int vendor, int device, int func, int n); +struct pci_dev *pci_get_dev_by_class (int major, int minor, int iface, int n); +struct pci_io *pci_io_enum (struct pci_dev *, struct pci_io *last); +void pci_register_irq (struct pci_dev *, pci_handler_func *, void *AUX); +void pci_unregister_irq (struct pci_dev *); +size_t pci_io_size (struct pci_io *); + +void pci_write_config8 (struct pci_dev *, int reg, uint8_t); +void pci_write_config16 (struct pci_dev *, int reg, uint16_t); +void pci_write_config32 (struct pci_dev *, int reg, uint32_t); +uint8_t pci_read_config8 (struct pci_dev *, int reg); +uint16_t pci_read_config16 (struct pci_dev *, int reg); +uint32_t pci_read_config32 (struct pci_dev *, int reg); + +void pci_reg_write32 (struct pci_io *, int reg, uint32_t); +void pci_reg_write16 (struct pci_io *, int reg, uint16_t); +void pci_reg_write8 (struct pci_io *, int reg, uint8_t); +uint32_t pci_reg_read32 (struct pci_io *, int reg); +uint16_t pci_reg_read16 (struct pci_io *, int reg); +uint8_t pci_reg_read8 (struct pci_io *, int reg); + +void pci_read_in (struct pci_io *, int off, size_t sz, void *buf); +void pci_write_out (struct pci_io *, int off, size_t sz, const void *buf); +void pci_print_stats (void); +void pci_mask_irq (struct pci_dev *); +void pci_unmask_irq (struct pci_dev *); + +#endif diff --git a/src/devices/pci_lookup.h b/src/devices/pci_lookup.h new file mode 100644 index 0000000..355f24b --- /dev/null +++ b/src/devices/pci_lookup.h @@ -0,0 +1,138 @@ +#ifndef PCILOOKUP_H +#define PCILOOKUP_H + +#ifdef PCI_TRANSLATION_ENABLE + +struct pci_vendor +{ + uint16_t vendor_id; + char *vendor; +}; + +struct pci_vendor pci_vendor_table[] = { + {0x1013, "Cirrus Logic"}, + {0x10de, "nVidia"}, + {0x10EC, "Realtek"}, + {0x11c1, "Agere Systems"}, + {0x8086, "Intel"} +}; + +struct pci_device +{ + uint16_t vendor_id; + uint16_t device_id; + char *device; +}; + +struct pci_device pci_device_table[] = { + {0x1013, 0x00b8, "CL-GD5446"}, + {0x10ec, 0x8029, "RTL8029 - NE2K Compatible"}, + {0x10ec, 0x8139, "RTL8139"}, + {0x8086, 0x1237, "82441FX"}, + {0x8086, 0x7000, "82371SB_ISA"}, + {0x8086, 0x7010, "82371SB_IDE"}, + {0x8086, 0x7020, "82371SB_USB"}, + {0x8086, 0x7113, "82371AB/EB/MB_ACPI"} +}; + + +#define PCI_DEVICE_TABLE_SZ (sizeof (pci_device_table) / sizeof (struct pci_device )) +#define PCI_VENDOR_TABLE_SZ (sizeof (pci_vendor_table) / sizeof (struct pci_vendor)) + +/** Too lazy to to a binary search... */ + +const char *pci_lookup_vendor (uint16_t vendor); +const char *pci_lookup_device (uint16_t vendor, uint16_t device); + +const char * +pci_lookup_vendor (uint16_t vendor) +{ + unsigned int i; + for (i = 0; i < PCI_VENDOR_TABLE_SZ; i++) + { + if (pci_vendor_table[i].vendor_id > vendor) + break; + if (pci_vendor_table[i].vendor_id == vendor) + return pci_vendor_table[i].vendor; + } + + return "Unknown Vendor"; +} + +const char * +pci_lookup_device (uint16_t vendor, uint16_t device) +{ + unsigned int i; + for (i = 0; i < PCI_DEVICE_TABLE_SZ; i++) + { + if (pci_device_table[i].vendor_id > vendor) + break; + if (pci_device_table[i].vendor_id == vendor && + pci_device_table[i].device_id == device) + return pci_device_table[i].device; + } + + return "Unknown Device"; +} + +#else + +#define pci_lookup_vendor(x) "Unknown Vendor" +#define pci_lookup_device(x,y) "Unknown Device" + +#endif + +struct pci_class +{ + uint8_t major; + uint8_t minor; + uint8_t iface; + char *name; +}; + +struct pci_class pci_class_table[] = { + {0, 0, 0, "Pre-PCI 2.0 Non-VGA Device"}, + {0, 1, 0, "Pre-PCI 2.0 VGA Device"}, + {1, 0, 0, "SCSI Controller"}, + {1, 1, 0, "IDE Controller"}, + {1, 2, 0, "Floppy Disk Controller"}, + {2, 0, 0, "Ethernet"}, + {3, 0, 0, "VGA Controller"}, + {3, 1, 0, "XGA Controller"}, + {5, 0, 0, "Memory Controller - RAM"}, + {5, 1, 0, "Memory Controller - Flash"}, + {6, 0, 0, "PCI Host"}, + {6, 1, 0, "PCI-ISA Bridge"}, + {6, 2, 0, "PCI-EISA Bridge"}, + {6, 4, 0, "PCI-PCI Bridge"}, + {6, 5, 0, "PCI-PCMCIA Bridge"}, + {12, 0, 0, "Firewire Adapter"}, + {12, 3, 0, "USB 1.1 Controller (UHCI)"}, + {12, 3, 0x10, "USB 1.1 Controller (OHCI)"}, + {12, 3, 0x20, "USB 2.0 Controller (EHCI)"} +}; + +#define PCI_CLASS_TABLE_SZ (sizeof(pci_class_table) / sizeof(struct pci_class)) + +const char *pci_lookup_class (uint8_t major, uint8_t minor, uint8_t iface); +const char * +pci_lookup_class (uint8_t major, uint8_t minor, uint8_t iface) +{ + unsigned int i; + for (i = 0; i < PCI_CLASS_TABLE_SZ; i++) + { + if (pci_class_table[i].major > major) + break; + if (pci_class_table[i].major != major) + continue; + if (pci_class_table[i].minor != minor) + continue; + if (pci_class_table[i].iface != iface) + continue; + return pci_class_table[i].name; + } + + return "Unknown Type"; +} + +#endif diff --git a/src/devices/usb.c b/src/devices/usb.c new file mode 100644 index 0000000..7cdb2f0 --- /dev/null +++ b/src/devices/usb.c @@ -0,0 +1,776 @@ +#include "devices/usb.h" +#include "devices/usb_hub.h" +#include "threads/synch.h" +#include "threads/malloc.h" +#include "devices/timer.h" +#include +#include +#include + +#define ADDRS_PER_HOST 127 /* number of configurable addresses */ +#define ADDR_DEFAULT 0 /* default configuration address */ +#define ADDR_FIRST 1 /* first configurable address */ + +/** sometimes used for ''value' */ +/* used in desc_header */ +#define SETUP_DESC_DEVICE 1 +#define SETUP_DESC_CONFIG 2 +#define SETUP_DESC_STRING 3 +#define SETUP_DESC_IFACE 4 +#define SETUP_DESC_ENDPOINT 5 +#define SETUP_DESC_DEVQUAL 6 +#define SETUP_DESC_SPDCONFIG 7 +#define SETUP_DESC_IFACEPWR 8 + +#pragma pack(1) +struct desc_header +{ + uint8_t length; + uint8_t type; +}; + +/** + * More details can be found in chapter 9 of the usb1.1 spec + */ + +struct device_descriptor +{ + struct desc_header hdr; + uint16_t usb_spec; + uint8_t dev_class; + uint8_t dev_subclass; + uint8_t dev_proto; + uint8_t max_pktsz; + uint16_t vendor_id; + uint16_t product_id; + uint16_t device_id; + uint8_t manufacturer; + uint8_t product; + uint8_t serial; + uint8_t num_configs; +}; + +struct device_qualifier +{ + uint16_t usb_spec; + uint8_t dev_class; + uint8_t dev_subclass; + uint8_t dev_proto; + uint8_t max_pktsz; + uint8_t num_configs; + uint8_t resv; +}; + +struct config_descriptor +{ + struct desc_header hdr; + uint16_t total_length; + uint8_t num_ifaces; + uint8_t config_val; + uint8_t config_desc; + uint8_t attributes; + uint8_t max_power; +}; + +struct interface_descriptor +{ + struct desc_header hdr; + uint8_t iface_num; + uint8_t alt_setting; + uint8_t num_endpoints; + uint8_t iface_class; + uint8_t iface_subclass; + uint8_t iface_proto; + uint8_t iface_desc; +}; + +struct endpoint_descriptor +{ + struct desc_header hdr; + /* end point address */ + uint8_t endpoint_num:4; + uint8_t resv1:3; + uint8_t direction:1; + + /* attributes */ + uint8_t transfer:2; + uint8_t synch:2; + uint8_t usage:2; + uint8_t resv2:2; + + uint16_t max_pktsz; + uint8_t interval; + +}; + +#pragma pack() + +#define USB_CLASS_HUB 0x09 +struct host +{ + struct usb_host *dev; /* host driver */ + host_info info; /* private to host */ + struct bitmap *usb_dev_addrs; /* addrs used on device */ + struct list host_usb_devs; /* usb devices on host */ + struct list_elem peers; /* other hosts on system */ +}; + +struct class +{ + struct usb_class *dev; /* class driver */ + struct list usb_ifaces; /* usb devices on class */ + struct list_elem peers; +}; + +static struct list usb_dev_list; /* list of all usb devices */ +static struct list class_list; /* list of all classes */ +static struct list host_list; /* list of all hosts on system */ +static struct lock usb_sys_lock; /* big usb lock */ + +#define usb_lock() lock_acquire(&usb_sys_lock) +#define usb_unlock() lock_release(&usb_sys_lock) + +static void usb_scan_devices (struct host *h); +static struct usb_dev *usb_configure_default (struct host *h); +static char *usb_get_string (struct usb_dev *d, int ndx); +static struct class *usb_get_class_by_id (int id); +static void usb_setup_dev_addr (struct usb_dev *dev); +static void usb_config_dev (struct usb_dev *dev, int config_val); +static int usb_load_config (struct usb_dev *dev, int idx, void *data, + int dsz); +static int usb_tx_all (struct usb_endpoint *eop, void *buf, + int max_bytes, int bailout, bool in); +static void usb_attach_interfaces (struct usb_dev *dev); +static void usb_apply_class_to_interfaces (struct class *c); +static size_t wchar_to_ascii (char *dst, const char *src); + +extern void uhci_init (void); +extern void ehci_init (void); + +void +usb_init (void) +{ + list_init (&host_list); + list_init (&class_list); + list_init (&usb_dev_list); + lock_init (&usb_sys_lock); + + usb_hub_init (); + + /* add usb hosts */ + printf ("Initializing EHCI\n"); + ehci_init (); + printf ("Initializing UHCI\n"); + uhci_init (); +} + +/* add host controller device to usb layer */ +void +usb_register_host (struct usb_host *uh, host_info info) +{ + struct host *h; + + h = malloc (sizeof (struct host)); + h->dev = uh; + h->info = info; + h->usb_dev_addrs = bitmap_create (ADDRS_PER_HOST); + list_init (&h->host_usb_devs); + + usb_lock (); + list_push_back (&host_list, &h->peers); + usb_unlock (); + + usb_scan_devices (h); +} + +int +usb_register_class (struct usb_class *uc) +{ + struct class *c; + + usb_lock (); + + /* check to make sure class is not in list */ + if (usb_get_class_by_id (uc->class_id) != NULL) + { + usb_unlock (); + return -1; + } + + + /* add class to class list */ + c = malloc (sizeof (struct class)); + c->dev = uc; + list_init (&c->usb_ifaces); + list_push_back (&class_list, &c->peers); + + usb_apply_class_to_interfaces (c); + + usb_unlock (); + + return 0; +} + +static void +usb_scan_devices (struct host *h) +{ + /* scan until there all devices using default pipe are configured */ + + printf ("USB: scanning devices...\n"); + while (1) + { + struct usb_dev *dev; + + dev = usb_configure_default (h); + if (dev == NULL) + break; + + if (dev->ignore_device == false) + printf ("USB Device %d: %s (%s)\n", + dev->addr, dev->product, dev->manufacturer); + + list_push_back (&h->host_usb_devs, &dev->host_peers); + list_push_back (&usb_dev_list, &dev->sys_peers); + usb_attach_interfaces (dev); + } +} + + +static struct usb_dev * +usb_configure_default (struct host *h) +{ + struct usb_dev *dev; + struct usb_setup_pkt sp; + char data[256]; + struct device_descriptor *dd; + host_dev_info hi; + host_eop_info cfg_eop; + bool ignore_device = false; + int err, sz, txed; + int config_val; + + hi = h->dev->create_dev_channel (h->info, ADDR_DEFAULT, USB_VERSION_1_1); + cfg_eop = h->dev->create_eop (hi, 0, 64); + + /* determine device descriptor */ + sp.recipient = USB_SETUP_RECIP_DEV; + sp.type = USB_SETUP_TYPE_STD; + sp.direction = 1; + sp.request = REQ_STD_GET_DESC; + sp.value = SETUP_DESC_DEVICE << 8; + sp.index = 0; + sp.length = sizeof (struct device_descriptor); + + err = + h->dev->tx_pkt (cfg_eop, USB_TOKEN_SETUP, &sp, 0, sizeof (sp), NULL, + true); + /* did it timeout? - no device */ + if (err != USB_HOST_ERR_NONE) + { + h->dev->remove_eop (cfg_eop); + h->dev->remove_dev_channel (hi); + return NULL; + } + + dd = (void *) &data; + memset (dd, 0, sizeof (data)); + txed = 0; + h->dev->tx_pkt (cfg_eop, USB_TOKEN_IN, data, 0, sizeof (data) - txed, &sz, + true); + + /* some devices only send 16 bytes for the device descriptor - + * they wind up with a NAK, waiting for another command to be issued + * + * if it's a 16 byte max packet device, we just take the first 16 bytes */ + + if (dd->usb_spec == USB_VERSION_1_0) + { + /* USB 1.0 devices have strict schedule requirements not yet supported */ + printf ("USB 1.0 device detected - skipping\n"); + } + else if (sz == 8) + { + /* read in the full descriptor */ + txed = sz; + while (txed <= 16) + { + if (h->dev-> + tx_pkt (cfg_eop, USB_TOKEN_IN, data + txed, 0, + sizeof (data) - txed, &sz, true) != USB_HOST_ERR_NONE) + break; + txed += sz; + } + } + if (dd->num_configs == 0) + ignore_device = true; + + h->dev->remove_eop (cfg_eop); + cfg_eop = h->dev->create_eop (hi, 0, dd->max_pktsz); + + /* device exists - create device structure */ + dev = malloc (sizeof (struct usb_dev)); + memset (dev, 0, sizeof (struct usb_dev)); + list_init (&dev->interfaces); + + dev->ignore_device = ignore_device; + dev->h_dev = hi; + dev->h_cfg_eop = cfg_eop; + + dev->cfg_eop.h_eop = cfg_eop; + + dev->usb_version = dd->usb_spec; + dev->default_iface.class_id = dd->dev_class; + dev->default_iface.subclass_id = dd->dev_subclass; + dev->default_iface.iface_num = 0; + dev->default_iface.proto = dd->dev_proto; + dev->default_iface.class = NULL; + dev->default_iface.c_info = NULL; + dev->default_iface.dev = dev; + + dev->cfg_eop.iface = &dev->default_iface; + dev->cfg_eop.max_pkt = dd->max_pktsz; + dev->cfg_eop.eop = 0; + + dev->host = h; + + dev->vendor_id = dd->vendor_id; + dev->product_id = dd->product_id; + dev->device_id = dd->device_id; + + if (ignore_device == false) + { + dev->product = usb_get_string (dev, dd->product); + dev->manufacturer = usb_get_string (dev, dd->manufacturer); + } + + config_val = -123; + /* read in configuration data if there are configurations available */ + /* (which there should be...) */ + if (dd->num_configs > 0 && ignore_device == false) + { + config_val = usb_load_config (dev, 0, data, sizeof (data)); + if (config_val < 0) + { + printf + ("USB: Invalid configuration value %d on '%s (%s)' (%x,%x,%x)\n", + config_val, dev->product, dev->manufacturer, dev->vendor_id, + dev->product_id, dev->device_id); + } + } + + usb_setup_dev_addr (dev); + + usb_config_dev (dev, (config_val < 0) ? 1 : config_val); + + return dev; +} + +/** + * Load in data for 'idx' configuration into device structure + * XXX support multiple configurations + */ +static int +usb_load_config (struct usb_dev *dev, int idx, void *data, int dsz) +{ + struct usb_setup_pkt sp; + struct config_descriptor *cd; + struct host *h; + host_eop_info cfg; + void *ptr; + int config_val, err, sz; + int i; + + h = dev->host; + cfg = dev->h_cfg_eop; + + sp.recipient = USB_SETUP_RECIP_DEV; + sp.type = USB_SETUP_TYPE_STD; + sp.direction = 1; + sp.request = REQ_STD_GET_DESC; + sp.value = SETUP_DESC_CONFIG << 8 | idx; + sp.index = 0; + sp.length = dsz; + cd = data; + + err = h->dev->tx_pkt (cfg, USB_TOKEN_SETUP, &sp, 0, sizeof (sp), &sz, true); + if (err != USB_HOST_ERR_NONE) + { + printf ("USB: Could not setup GET descriptor\n"); + return -err; + } + + sz = usb_tx_all (&dev->cfg_eop, cd, dsz, + sizeof (struct config_descriptor), true); + if (sz < sizeof (struct config_descriptor)) + { + printf ("USB: Did not rx GET descriptor (%d bytes, expected %d)\n", sz, + sizeof (struct config_descriptor)); + return -err; + } + + if (sz == 0 || cd->hdr.type != SETUP_DESC_CONFIG) + { + printf ("USB: Invalid descriptor\n"); + return -1; + } + + if (sz < cd->total_length) + sz += usb_tx_all (&dev->cfg_eop, data+sz, dsz, cd->total_length - sz, true); + + dev->pwr = cd->max_power; + + /* interface information comes right after config data */ + /* scan interfaces */ + ptr = data + sizeof (struct config_descriptor); + + for (i = 0; i < cd->num_ifaces; i++) + { + struct interface_descriptor *iface; + struct usb_iface *ui; + int j; + + iface = ptr; + if (iface->hdr.type != SETUP_DESC_IFACE) + { + hex_dump (0, iface, 64, false); + PANIC ("Expected %d, found %d\n", SETUP_DESC_IFACE, + iface->hdr.type); + } + + ui = malloc (sizeof (struct usb_iface)); + ui->class_id = iface->iface_class; + ui->subclass_id = iface->iface_subclass; + ui->iface_num = iface->iface_num; + ui->proto = iface->iface_proto; + ui->class = NULL; + ui->c_info = NULL; + ui->dev = dev; + list_init (&ui->endpoints); + + /* endpoint data comes after interfaces */ + /* scan endpoints */ + ptr += sizeof (struct interface_descriptor); + for (j = 0; j < iface->num_endpoints; + j++, ptr += sizeof (struct endpoint_descriptor)) + { + struct usb_endpoint *ue; + struct endpoint_descriptor *ed; + + ed = ptr; + + ue = malloc (sizeof (struct usb_endpoint)); + ue->eop = ed->endpoint_num; + ue->direction = ed->direction; + ue->attr = ed->transfer; + ue->max_pkt = ed->max_pktsz; + ue->interval = ed->interval; + ue->iface = ui; + ue->h_eop = h->dev->create_eop (dev->h_dev, ue->eop, ue->max_pkt); + + list_push_back (&ui->endpoints, &ue->peers); + } + + list_push_back (&dev->interfaces, &ui->peers); + } + + config_val = cd->config_val; + + return config_val; +} + +/** + * Set USB device configuration to desired configuration value + */ +static void +usb_config_dev (struct usb_dev *dev, int config_val) +{ + struct usb_setup_pkt sp; + struct host *h; + host_eop_info cfg; + int err; + + h = dev->host; + cfg = dev->h_cfg_eop; + + sp.recipient = USB_SETUP_RECIP_DEV; + sp.type = USB_SETUP_TYPE_STD; + sp.direction = 0; + sp.request = REQ_STD_SET_CONFIG; + sp.value = config_val; + sp.index = 0; + sp.length = 0; + err = h->dev->tx_pkt (cfg, USB_TOKEN_SETUP, &sp, 0, sizeof (sp), NULL, true); + if (err != USB_HOST_ERR_NONE) + PANIC ("USB: Config setup packet did not tx\n"); + + err = h->dev->tx_pkt (cfg, USB_TOKEN_IN, NULL, 0, 0, NULL, true); + if (err != USB_HOST_ERR_NONE) + PANIC ("USB: Could not configure device!\n"); +} + +/** + * Set a device address to something other than the default pipe + */ +static void +usb_setup_dev_addr (struct usb_dev *dev) +{ + struct usb_setup_pkt sp; + struct host *h; + host_eop_info cfg; + int err; + + ASSERT (dev->addr == 0); + + h = dev->host; + cfg = dev->h_cfg_eop; + + dev->addr = bitmap_scan_and_flip (h->usb_dev_addrs, 1, 1, false); + + sp.recipient = USB_SETUP_RECIP_DEV; + sp.type = USB_SETUP_TYPE_STD; + sp.direction = 0; + sp.request = REQ_STD_SET_ADDRESS; + sp.value = dev->addr; + sp.index = 0; + sp.length = 0; + err = + h->dev->tx_pkt (cfg, USB_TOKEN_SETUP, &sp, 0, sizeof (sp), NULL, true); + if (err != USB_HOST_ERR_NONE) + { + PANIC ("USB: WHOOPS!!!!!!!\n"); + } + err = h->dev->tx_pkt (cfg, USB_TOKEN_IN, NULL, 0, 0, NULL, true); + if (err != USB_HOST_ERR_NONE) + { + PANIC ("USB: Error on setting device address (err = %d)\n", err); + } + + h->dev->modify_dev_channel (dev->h_dev, dev->addr, dev->usb_version); +} + +#define MAX_USB_STR 128 +/* read string by string descriptor index from usb device */ +static char * +usb_get_string (struct usb_dev *udev, int ndx) +{ + struct usb_setup_pkt sp; + char str[MAX_USB_STR]; + char *ret; + int sz; + + sp.recipient = USB_SETUP_RECIP_DEV; + sp.type = USB_SETUP_TYPE_STD; + sp.direction = 1; + sp.request = REQ_STD_GET_DESC; + sp.value = (SETUP_DESC_STRING << 8) | ndx; + sp.index = 0; + sp.length = MAX_USB_STR; + udev->host->dev->tx_pkt (udev->h_cfg_eop, USB_TOKEN_SETUP, + &sp, 0, sizeof (sp), NULL, false); + sz = usb_tx_all (&udev->cfg_eop, &str, MAX_USB_STR, 2, true); + sz += + usb_tx_all (&udev->cfg_eop, str + sz, (uint8_t) (str[0]) - sz, 0, true); + + /* string failed to tx? */ + if (sz == 0) + return NULL; + + /* some devices don't respect the string descriptor length value (str[0]) + * and just send any old value they want, so we can't use it */ + + str[(sz < (MAX_USB_STR - 1)) ? (sz) : (MAX_USB_STR - 1)] = '\0'; + + /* usb uses wchars for strings, convert to ASCII */ + wchar_to_ascii (str, str + 2); + ret = malloc (strlen (str) + 1); + strlcpy (ret, str, MAX_USB_STR); + return ret; +} + +static struct class * +usb_get_class_by_id (int id) +{ + struct list_elem *li; + + li = list_begin (&class_list); + while (li != list_end (&class_list)) + { + struct class *c; + c = list_entry (li, struct class, peers); + if (c->dev->class_id == id) + return c; + li = list_next (li); + } + + return NULL; +} + +/** + * Asssociate interfaces on a device with their respective device classes + */ +static void +usb_attach_interfaces (struct usb_dev *dev) +{ + + struct list_elem *li; + li = list_begin (&dev->interfaces); + while (li != list_end (&dev->interfaces)) + { + struct class *cl; + struct usb_iface *ui; + + ui = list_entry (li, struct usb_iface, peers); + li = list_next (li); + cl = usb_get_class_by_id (ui->class_id); + + /* no matching class? try next interface */ + if (cl == NULL) + continue; + + ui->c_info = cl->dev->attached (ui); + /* did class driver initialize it OK? */ + if (ui->c_info != NULL) + { + /* yes, add to list of usable interfaces */ + list_push_back (&cl->usb_ifaces, &ui->class_peers); + ui->class = cl; + } + } +} + +/** + * Scan all interfaces for interfaces that match class's class id + * Attach interfaces that match + */ +static void +usb_apply_class_to_interfaces (struct class *c) +{ + struct list_elem *li; + + li = list_begin (&usb_dev_list); + while (li != list_end (&usb_dev_list)) + { + struct usb_dev *ud; + struct list_elem *lii; + + ud = list_entry (li, struct usb_dev, sys_peers); + + lii = list_begin (&ud->interfaces); + while (lii != list_end (&ud->interfaces)) + { + struct usb_iface *ui; + + ui = list_entry (lii, struct usb_iface, peers); + + lii = list_next (lii); + if (ui->class_id != c->dev->class_id) + continue; + + ui->c_info = c->dev->attached (ui); + if (ui->c_info == NULL) + continue; + + list_push_back (&c->usb_ifaces, &ui->class_peers); + ui->class = c; + } + + li = list_next (li); + } +} + +int +usb_dev_bulk (struct usb_endpoint *eop, void *buf, int sz, int *tx) +{ + struct host *h; + int err; + int token; + + ASSERT (eop != NULL); + h = eop->iface->dev->host; + + if (eop->direction == 0) + token = USB_TOKEN_OUT; + else + token = USB_TOKEN_IN; + + err = h->dev->tx_pkt (eop->h_eop, token, buf, sz, sz, tx, true); + + return err; +} + +int +usb_dev_setup (struct usb_endpoint *eop, bool in, + struct usb_setup_pkt *s, void *buf, int sz) +{ + struct host *h; + int err; + + ASSERT (eop != NULL); + h = eop->iface->dev->host; + + err = h->dev->tx_pkt (eop->h_eop, USB_TOKEN_SETUP, s, + 0, sizeof (struct usb_setup_pkt), NULL, true); + if (err != USB_HOST_ERR_NONE) + { + printf ("usb_dev_setup: failed\n"); + return 0; + } + + err = h->dev->tx_pkt (eop->h_eop, (in) ? USB_TOKEN_IN : USB_TOKEN_OUT, + buf, 0, sz, &sz, true); + + return sz; +} + +/** convert a wchar string to ascii in place */ +static size_t +wchar_to_ascii (char *dst, const char *src) +{ + size_t sz = 0; + for (sz = 0; src[sz * 2] != '\0'; sz++) + { + dst[sz] = src[sz * 2]; + } + dst[sz] = '\0'; + return sz; +} + +/* this is used for variable sized transfers where a normal bulk transfer would + probably fail, since it expects some minimum size - we just want to + read/write as much to the pipe as we can + */ +static int +usb_tx_all (struct usb_endpoint *eop, void *buf, + int max_bytes, int bailout, bool in) +{ + int txed; + int token; + int prev_sz = 0; + struct host *h; + + if (max_bytes <= 0) + return 0; + + if (bailout == 0) + bailout = 512; + + txed = 0; + token = (in) ? USB_TOKEN_IN : USB_TOKEN_OUT; + h = eop->iface->dev->host; + while (txed < max_bytes && txed < bailout) + { + int sz, err; + sz = 0; + err = h->dev->tx_pkt (eop->h_eop, token, + buf + txed, 0, max_bytes - txed, &sz, true); + if (prev_sz == 0) + prev_sz = sz; + txed += sz; + /* this should probably be using short packet detection */ + if (err != USB_HOST_ERR_NONE || sz != prev_sz || sz == 0) + { + return txed; + } + } + return txed; +} diff --git a/src/devices/usb.h b/src/devices/usb.h new file mode 100644 index 0000000..b15978e --- /dev/null +++ b/src/devices/usb.h @@ -0,0 +1,197 @@ +#ifndef USB_H +#define USB_H + +#include +#include +#include + +#define USB_HOST_ERR_NONE 0 +#define USB_HOST_ERR_BITSTUFF 1 +#define USB_HOST_ERR_TIMEOUT 2 +#define USB_HOST_ERR_NAK 3 +#define USB_HOST_ERR_BABBLE 4 +#define USB_HOST_ERR_BUFFER 5 +#define USB_HOST_ERR_STALL 6 +#define USB_HOST_ERR_NODEV 7 + +#define make_usb_pid(x) ((x) | ((~(x)) << 4)) + +/* token packets... */ +#define USB_PID_OUT make_usb_pid(1) +#define USB_PID_IN make_usb_pid(9) +#define USB_PID_SOF make_usb_pid(5) +#define USB_PID_SETUP make_usb_pid(13) +/* data packets... */ +#define USB_PID_DATA0 make_usb_pid(3) +#define USB_PID_DATA1 make_usb_pid(11) +#define USB_PID_DATA2 make_usb_pid(7) +#define USB_PID_MDATA make_usb_pid(15) +/* handshake packets.. */ +#define USB_PID_ACK make_usb_pid(2) +#define USB_PID_NAK make_usb_pid(10) +#define USB_PID_STALL make_usb_pid(14) +#define USB_PID_NYET make_usb_pid(6) +/* special */ +#define USB_PID_PRE make_usb_pid(12) +#define USB_PID_ERR make_usb_pid(12) +#define USB_PID_SPLIT make_usb_pid(8) +#define USB_PID_PING make_usb_pid(4) + +/* the standard setup requests */ +#define REQ_STD_GET_STATUS 0 +#define REQ_STD_CLR_FEAT 1 +#define REQ_STD_SET_FEAT 3 +#define REQ_STD_SET_ADDRESS 5 +#define REQ_STD_GET_DESC 6 +#define REQ_STD_SET_DESC 7 +#define REQ_STD_GET_CONFIG 8 +#define REQ_STD_SET_CONFIG 9 +#define REQ_STD_GET_IFACE 10 +#define REQ_STD_SET_IFACE 11 +#define REQ_STD_SYNCH_FRAME 12 + + +#define USB_TOKEN_SETUP 0x00 +#define USB_TOKEN_IN 0x80 +#define USB_TOKEN_OUT 0x90 + +struct class; +struct host; +typedef void *host_info; +typedef void *host_eop_info; +typedef void *host_dev_info; +typedef void *class_info; + + +struct usb_iface +{ + int iface_num; + int class_id, subclass_id; + int proto; + + struct usb_dev *dev; + + struct class *class; + class_info c_info; + + struct list_elem class_peers; /* peers on class */ + struct list endpoints; + + struct list_elem peers; /* peers on device */ +}; + +#define USB_SPEED_1 0 +#define USB_SPEED_1_1 1 +#define USB_SPEED_2 2 + +struct usb_host +{ + const char *name; + int (*detect_change) (host_info); + int (*tx_pkt) (host_eop_info, int pid, void *pkt, + int min_sz, int max_sz, int *in_sz, bool wait); + + host_eop_info (*create_eop)(host_dev_info, int eop, int maxpkt); + void (*remove_eop)(host_eop_info); + + host_dev_info (*create_dev_channel) (host_info, int dev_addr, int ver); + void (*modify_dev_channel) (host_dev_info, int dev_addr, int ver); + void (*remove_dev_channel) (host_dev_info); +}; + +struct usb_dev; +struct usb_class +{ + const int class_id; + const char *name; + + /* when a device of this class is attached, the device is passed in */ + /* returns private info on device */ + void *(*attached) (struct usb_iface *); + + /* device is detached -> detached(dev_info) */ + void (*detached) (class_info info); +}; + +#define USB_VERSION_1_0 0x100 +#define USB_VERSION_1_1 0x110 +#define USB_VERSION_2 0x200 + +#define USB_EOP_ATTR_CTL 0 /* control */ +#define USB_EOP_ATTR_ISO 1 /* isochronous */ +#define USB_EOP_ATTR_BULK 2 /* bulk */ +#define USB_EOP_ATTR_INT 3 /* interrupt */ +struct usb_endpoint +{ + int eop; /* end point address */ + int attr; + int direction; /* 0 = host->dev, 1=dev->host */ + int max_pkt; + int interval; + struct usb_iface *iface; + host_eop_info h_eop; + struct list_elem peers; +}; + +struct usb_dev +{ + uint8_t addr; + int usb_version; + int class_id, subclass_id; + uint16_t product_id, vendor_id, device_id; + int interface; + int max_pkt_len; + int int_period; + char *manufacturer; + char *product; + int pwr; /* power draw for this config */ + bool ignore_device; + struct list interfaces; + + struct list_elem host_peers; /* peers on host */ + struct list_elem sys_peers; /* list for all devices */ + + struct usb_iface default_iface; + struct usb_endpoint cfg_eop; + + host_dev_info h_dev; + host_eop_info h_cfg_eop; /* configuration EOP */ + struct host *host; +}; + +/* pg276 usb_20.pdf */ +#define USB_SETUP_TYPE_STD 0 +#define USB_SETUP_TYPE_CLASS 1 +#define USB_SETUP_TYPE_VENDOR 2 + +#define USB_SETUP_RECIP_DEV 0 +#define USB_SETUP_RECIP_IFACE 1 +#define USB_SETUP_RECIP_ENDPT 2 +#define USB_SETUP_RECIP_OTHER 3 + +#pragma pack(1) +struct usb_setup_pkt +{ + uint8_t recipient:5; + uint8_t type:2; + uint8_t direction:1; /* 0 = host->dev, 1 = dev->host */ + uint8_t request; + uint16_t value; + uint16_t index; + uint16_t length; +}; +#pragma pack() + +void usb_init (void); + +void usb_register_host (struct usb_host *, host_info info); +int usb_unregister_host (struct usb_host *, host_info info); +int usb_register_class (struct usb_class *); +int usb_unregister_class (struct usb_class *); + +int usb_dev_bulk (struct usb_endpoint *eop, void *buf, int sz, int *tx); +int usb_dev_setup (struct usb_endpoint *eop, bool in, + struct usb_setup_pkt *s, void *buf, int sz); +int usb_dev_wait_int (struct usb_dev *); + +#endif diff --git a/src/devices/usb_ehci.c b/src/devices/usb_ehci.c new file mode 100644 index 0000000..34f328b --- /dev/null +++ b/src/devices/usb_ehci.c @@ -0,0 +1,52 @@ +/** + * + * Actual EHCI will be implemented later + * + * For now, we just deactivate the EHCI controller and routing circuitry + * so that any USB2.0 devices activated by the BIOS will show up on the + * USB1.1 controllers instead of being routed to EHCI and therefore "invisible" + * to the system. + * + */ + +#include "devices/pci.h" +#include "devices/usb.h" +#include +#include + +/* capability registers */ +#define EHCI_REG_CAPLENGTH 0x00 + +/* operational regisers - must be offset by op_base */ +#define EHCI_REG_CONFIGFLAG 0x40 + +void ehci_init (void); + + +void +ehci_init (void) +{ + struct pci_dev *pd; + int dev_num; + + dev_num = 0; + while ((pd = pci_get_dev_by_class (PCI_MAJOR_SERIALBUS, PCI_MINOR_USB, + PCI_USB_IFACE_EHCI, dev_num)) != NULL) + { + struct pci_io *io; + uint8_t op_base; + + dev_num++; + io = pci_io_enum (pd, NULL); + if (io == NULL) + { + printf ("IO not found on EHCI device?\n"); + continue; + } + printf ("Disabling the EHCI controller #%d\n", dev_num - 1); + op_base = pci_reg_read8 (io, EHCI_REG_CAPLENGTH); + + /* turn off EHCI routing */ + pci_reg_write32 (io, EHCI_REG_CONFIGFLAG + op_base, 0); + } +} diff --git a/src/devices/usb_hub.c b/src/devices/usb_hub.c new file mode 100644 index 0000000..01bbfb9 --- /dev/null +++ b/src/devices/usb_hub.c @@ -0,0 +1,54 @@ +#include "devices/usb.h" +#include "devices/usb_hub.h" +#include + +#define USB_CLASS_HUB 0x09 + +#define REQ_HUB_GET_STATUS 0 +#define REQ_HUB_CLEAR_FEATURE 1 +#define REQ_HUB_SET_FEATURE 2 +#define REQ_HUB_GET_DESC 6 +#define REQ_HUB_SET_DESC 7 +#define REQ_HUB_CLEAR_TT_BUF 8 +#define REQ_HUB_RESET_TT 9 +#define REQ_HUB_GET_TT_STATE 10 +#define REQ_HUB_STOP_TT 11 + +#define HUB_SEL_HUB_POWER 0 +#define HUB_SEL_OVER_CURRENT 1 +#define HUB_SEL_PORT_CONN 0 +#define HUB_SEL_PORT_ENABLE 1 +#define HUB_SEL_PORT_SUSPEND 2 +#define HUB_SEL_PORT_OVER_CURRENT 3 +#define HUB_SEL_PORT_RESET 4 +#define HUB_SEL_PORT_POWER 8 +#define HUB_SEL_PORT_LOW_SPEED 9 + +#define SETUP_DESC_HUB 0x29 + +static void* hub_attached(struct usb_iface*); +static void hub_detached(class_info); + +static struct usb_class hub_class = { + .attached = hub_attached, + .detached = hub_detached, + .class_id = USB_CLASS_HUB, + .name = "hub" + }; + + +void usb_hub_init(void) +{ + usb_register_class(&hub_class); +} + + +static void* hub_attached(struct usb_iface* ui UNUSED) +{ + return NULL; +} + +static void hub_detached(class_info info UNUSED) +{ +} + diff --git a/src/devices/usb_hub.h b/src/devices/usb_hub.h new file mode 100644 index 0000000..d4399d7 --- /dev/null +++ b/src/devices/usb_hub.h @@ -0,0 +1,7 @@ +#ifndef HUB_H +#define HUB_H + +void usb_hub_init(void); + + +#endif diff --git a/src/devices/usb_storage.c b/src/devices/usb_storage.c new file mode 100644 index 0000000..a5e3ad1 --- /dev/null +++ b/src/devices/usb_storage.c @@ -0,0 +1,441 @@ +/** + * USB mass storage driver - just like the one Elvis used! + */ + +#include +#include +#include +#include +#include +#include +#include "threads/malloc.h" +#include "threads/palloc.h" +#include "threads/thread.h" +#include "devices/timer.h" +#include "threads/synch.h" +#include "threads/pte.h" +#include "devices/usb.h" +#include "devices/block.h" +#include "devices/partition.h" +#include "devices/usb_storage.h" + +//http://www.usb.org/developers/defined_class +#define USB_CLASS_MASS_STORAGE 0x08 + +#define USB_SUBCLASS_RBC 0x01 /* reduced block commands */ +#define USB_SUBCLASS_ATAPI 0x02 /* ATAPI - CD's */ +#define USB_SUBCLASS_TAPE 0x03 +#define USB_SUBCLASS_UFI 0x04 /* floppy disk */ +#define USB_SUBCLASS_SFF8070 0x05 +#define USB_SUBCLASS_SCSI 0x06 /* scsi transparent command set */ + +#define USB_PROTO_COMPLETE 0x00 +#define USB_PROTO_NO_COMPLETE 0x01 +#define USB_PROTO_BULK 0x50 + +#pragma pack(1) + +#define CBW_SIG_MAGIC 0x43425355 + +#define CBW_FL_IN (1 << 7) +#define CBW_FL_OUT 0 + +/** usbmassbulk_10.pdf - pg 13 */ +/* command block wrapper */ +struct msc_cbw +{ + uint32_t sig; + uint32_t tag; + uint32_t tx_len; + uint8_t flags; + uint8_t lun; + uint8_t cb_len; /* command block length */ + uint8_t cb[16]; /* command block */ +}; + +#define CSW_SIG_MAGIC 0x53425355 +#define CSW_STATUS_PASSED 0 +#define CSW_STATUS_FAILED 1 +#define CSW_STATUS_PHASE 2 /* phase error */ +/* command status wrapper */ +struct msc_csw +{ + uint32_t sig; + uint32_t tag; + uint32_t residue; + uint8_t status; +}; + +struct scsi_cdb6 +{ + uint8_t op; + uint8_t lba[3]; + uint8_t len; + uint8_t control; +}; + +struct scsi_cdb10 +{ + uint8_t op; + uint8_t action; + uint32_t lba; + uint8_t resv; + uint16_t len; + uint8_t control; +}; + +struct scsi_cdb12 +{ + uint8_t op; + uint8_t action; + uint32_t lba; + uint32_t len; + uint8_t resv; + uint8_t control; +}; + +struct scsi_cdb16 +{ + uint8_t op; + uint8_t action; + uint32_t lba; + uint32_t data; + uint32_t len; + uint8_t resv; + uint8_t control; +}; + +struct scsi_capacity10 +{ + uint32_t blocks; + uint32_t block_len; +}; + +#define SCSI_OP_WRITE6 0xa +#define SCSI_OP_READ6 0x8 +#define SCSI_OP_TEST_READY 0x00 +#define SCSI_OP_SEEK8 0x0b +#define SCSI_OP_MODE_SENSE8 0x1a +#define SCSI_OP_MODE_SELECT8 0x15 +#define SCSI_OP_READ_CAPACITY10 0x25 +#define SCSI_OP_READ_CAPACITY16 0x9e +#define SCSI_OP_WRITE10 0x2a +#define SCSI_OP_READ10 0x28 +#define SCSI_OP_SEEK10 0x2b +#define SCSI_OP_MODE_SENSE10 0x5a +#define SCSI_OP_MODE_SELECT10 0x55 + + +#pragma pack() + +static void *msc_attached (struct usb_iface *); +static void msc_detached (class_info); + +static void msc_reset_endpoint (struct usb_endpoint *eop); + +static struct usb_class storage_class = { + .attached = msc_attached, + .detached = msc_detached, + .name = "Mass Storage", + .class_id = USB_CLASS_MASS_STORAGE +}; + +#define mci_lock(x) lock_acquire(&(x)->lock) +#define mci_unlock(x) lock_release(&(x)->lock) + +struct msc_class_info +{ + int refs; + struct lock lock; + struct usb_iface *ui; + int blk_size; + int blk_count; + bool retrying; + uint32_t tag; + void *bounce_buffer; + + struct usb_endpoint *eop_in; + struct usb_endpoint *eop_out; + struct list_elem peers; +}; +static void msc_get_geometry (struct msc_class_info *); +static void msc_io (struct msc_class_info *, block_sector_t, void *buf, bool wr); +static void msc_reset_recovery(struct msc_class_info* mci); +static void msc_bulk_reset(struct msc_class_info* mci); + +struct msc_blk_info +{ + struct msc_class_info *mci; +}; + +static struct list device_list; +static struct block_operations msc_operations; + +void usb_storage_init (void); + +void +usb_storage_init (void) +{ + list_init (&device_list); + usb_register_class (&storage_class); +} + + +static class_info +msc_attached (struct usb_iface *ui) +{ + static int dev_no; + + struct msc_class_info *mci; + struct msc_blk_info *mbi; + struct list_elem *li; + struct block *block; + char name[16]; + + if (ui->subclass_id != USB_SUBCLASS_SCSI) + { + printf ("usb_storage: Only support SCSI-type devices\n"); + return NULL; + } + + mci = malloc (sizeof (struct msc_class_info)); + + mci->refs = 0; + mci->retrying = false; + lock_init (&mci->lock); + mci->ui = ui; + list_push_back (&device_list, &mci->peers); + + /* grab endpoints */ + li = list_begin (&ui->endpoints); + mci->eop_in = NULL; + mci->eop_out = NULL; + while (li != list_end (&ui->endpoints)) + { + struct usb_endpoint *ue; + + ue = list_entry (li, struct usb_endpoint, peers); + li = list_next (li); + + if (ue->attr == USB_EOP_ATTR_BULK) + { + if (ue->direction == 0) + mci->eop_out = ue; + else + mci->eop_in = ue; + } + } + + ASSERT (mci->eop_in != NULL && mci->eop_out != NULL); + + msc_get_geometry (mci); + + if (mci->blk_size != 512) + { + printf ("ignoring device with %d-byte sectors\n", mci->blk_size); + return NULL; + } + + mci->bounce_buffer = palloc_get_multiple (PAL_ASSERT, + DIV_ROUND_UP (mci->blk_size, + PGSIZE)); + mbi = malloc (sizeof (struct msc_blk_info)); + mbi->mci = mci; + snprintf (name, sizeof name, "ud%c", 'a' + dev_no++); + block = block_register (name, BLOCK_RAW, "USB", mci->blk_count, + &msc_operations, mbi); + partition_scan (block); + + return mci; +} + +static void +msc_detached (class_info ci UNUSED) +{ + PANIC ("msc_detached: STUB"); +} + +static void +msc_read (void *mbi_, block_sector_t sector, void *buffer) +{ + struct msc_blk_info *mbi = mbi_; + + mci_lock (mbi->mci); + msc_io (mbi->mci, sector, buffer, false); + mci_unlock (mbi->mci); +} + +static void +msc_write (void *mbi_, block_sector_t sector, const void *buffer) +{ + struct msc_blk_info *mbi = mbi_; + + mci_lock (mbi->mci); + msc_io (mbi->mci, sector, (void *) buffer, true); + mci_unlock (mbi->mci); +} + +static struct block_operations msc_operations = + { + msc_read, + msc_write, + }; + +static void +msc_get_geometry (struct msc_class_info *mci) +{ + struct msc_cbw cbw; + struct scsi_capacity10 *cap; + uint8_t buf[sizeof (struct msc_csw) + sizeof (struct scsi_capacity10)]; + struct scsi_cdb10 *cdb; + struct msc_csw *csw; + int tx; + + /* cap + csw must be read in one shot, combine into a single buffer */ + cap = (struct scsi_capacity10 *) (buf); + csw = (struct msc_csw *) (&buf[sizeof (struct scsi_capacity10)]); + + cbw.sig = CBW_SIG_MAGIC; + cbw.tag = mci->tag++; + cbw.tx_len = sizeof (struct scsi_capacity10); + cbw.flags = CBW_FL_IN; + cbw.lun = 0; + cbw.cb_len = sizeof (struct scsi_cdb10); + + cdb = (void *) (&cbw.cb); + memset (cdb, 0, sizeof (struct scsi_cdb10)); + cdb->op = SCSI_OP_READ_CAPACITY10; + + usb_dev_bulk (mci->eop_out, &cbw, sizeof (cbw), &tx); + usb_dev_bulk (mci->eop_in, &buf, sizeof (buf), &tx); + + mci->blk_count = be32_to_machine (cap->blocks); + mci->blk_size = be32_to_machine (cap->block_len); + + /* did CSW stall? */ + if (tx == sizeof (struct scsi_capacity10)) + { + msc_reset_endpoint (mci->eop_in); + usb_dev_bulk (mci->eop_in, csw, sizeof (*csw), &tx); + } + + ASSERT (csw->sig == CSW_SIG_MAGIC); + + if (csw->status != CSW_STATUS_PASSED) + { + PANIC ("USB storage geometry read failure!\n"); + } + +} + +static void +msc_io (struct msc_class_info *mci, block_sector_t bn, void *buf, bool wr) +{ + struct msc_cbw cbw; + struct msc_csw csw; + struct scsi_cdb10 *cdb; + int tx; + int err; + + if (wr) + memcpy (mci->bounce_buffer, buf, mci->blk_size); + + memset (&cbw, 0, sizeof (cbw)); + cbw.sig = CBW_SIG_MAGIC; + cbw.tag = mci->tag++; + cbw.tx_len = mci->blk_size; + cbw.flags = (wr) ? CBW_FL_OUT : CBW_FL_IN; + cbw.lun = 0; + cbw.cb_len = sizeof (struct scsi_cdb10); + + cdb = (void *) (&cbw.cb); + cdb->op = (wr) ? SCSI_OP_WRITE10 : SCSI_OP_READ10; + *((uint32_t *) ((uint8_t *) (&cdb->lba) + 1)) = machine_to_be24 (bn); + cdb->len = machine_to_be16 (1); + +// msc_reset_endpoint (mci->eop_in); +// msc_reset_endpoint (mci->eop_out); + + /* set it up */ + err = usb_dev_bulk (mci->eop_out, &cbw, sizeof (cbw), &tx); + if (err != 0) + { + msc_reset_endpoint (mci->eop_out); + err = usb_dev_bulk (mci->eop_out, &cbw, sizeof (cbw), &tx); + } + + /* do storage io */ + err = usb_dev_bulk ((wr) ? mci->eop_out : mci->eop_in, + mci->bounce_buffer, mci->blk_size, &tx); + memset (&csw, 0, sizeof (csw)); + ASSERT (tx == mci->blk_size); + + + /* get command status */ + err = usb_dev_bulk (mci->eop_in, &csw, sizeof (csw), &tx); + if (err != 0) + { + msc_reset_endpoint (mci->eop_in); + msc_reset_endpoint (mci->eop_out); + err = usb_dev_bulk (mci->eop_in, &csw, sizeof (csw), &tx); + if (err != 0) + PANIC ("msc_io: error %d\n", err); + } + + if (csw.sig != CSW_SIG_MAGIC) + { + if (mci->retrying == true) + PANIC ("usb_msd: CSW still missing. Bail out\n"); + printf ("usb_msd: no command status, resetting. Buggy device?\n"); + msc_reset_recovery(mci); + printf ("reset complete\n"); + mci->retrying = true; + msc_io (mci, bn, buf, wr); + return; + } + mci->retrying = false; + + if (csw.status != CSW_STATUS_PASSED) + { + PANIC ("USB storage IO failure! - error %d\n", csw.status); + } + if (!wr) + memcpy (buf, mci->bounce_buffer, mci->blk_size); +} + +static void +msc_reset_endpoint (struct usb_endpoint *eop) +{ + struct usb_setup_pkt sp; + + sp.recipient = USB_SETUP_RECIP_ENDPT; + sp.type = USB_SETUP_TYPE_STD; + sp.direction = 0; + sp.request = REQ_STD_CLR_FEAT; + sp.value = 0; /* 0 is ENDPOINT_HALT */ + sp.index = eop->eop; + sp.length = 0; + usb_dev_setup (eop, true, &sp, NULL, 0); +} + +static void +msc_reset_recovery(struct msc_class_info* mci) +{ + msc_bulk_reset (mci); + msc_reset_endpoint (mci->eop_in); + msc_reset_endpoint (mci->eop_out); +} + +static void msc_bulk_reset(struct msc_class_info* mci) +{ + struct usb_setup_pkt sp; + + sp.recipient = USB_SETUP_RECIP_DEV; + sp.type = USB_SETUP_TYPE_CLASS; + sp.direction = 0; + sp.request = 0xff; + sp.value = 0; + sp.index = mci->ui->iface_num; + sp.length = 0; + usb_dev_setup (&mci->ui->dev->cfg_eop, true, &sp, NULL, 0); +} diff --git a/src/devices/usb_storage.h b/src/devices/usb_storage.h new file mode 100644 index 0000000..ed0a493 --- /dev/null +++ b/src/devices/usb_storage.h @@ -0,0 +1 @@ +void usb_storage_init (void); diff --git a/src/devices/usb_uhci.c b/src/devices/usb_uhci.c new file mode 100644 index 0000000..1e5a64a --- /dev/null +++ b/src/devices/usb_uhci.c @@ -0,0 +1,1263 @@ +/** + * Universal Host Controller Interface driver + * TODO: + * Stall timeouts + * Better (any) root hub handling + */ + +#include +#include +#include +#include +#include "threads/pte.h" +#include "threads/malloc.h" +#include "threads/palloc.h" +#include "threads/synch.h" +#include "threads/interrupt.h" +#include "threads/thread.h" +#include "devices/pci.h" +#include "devices/usb.h" +#include "devices/timer.h" + +#define UHCI_MAX_PORTS 8 + +#define FRAME_LIST_ENTRIES 1024 +#define TD_ENTRIES (4096/32) /* number of entries allocated */ +#define QH_ENTRIES (4096/16) + +/* uhci pci registers */ +#define UHCI_REG_USBCMD 0x00 /* Command */ +#define UHCI_REG_USBSTS 0x02 /* Status */ +#define UHCI_REG_USBINTR 0x04 /* interrupt enable */ +#define UHCI_REG_FRNUM 0x06 /* frame number */ +#define UHCI_REG_FLBASEADD 0x08 /* frame list base address */ +#define UHCI_REG_SOFMOD 0x0c /* start of frame modify */ +#define UHCI_REG_PORTSC1 0x10 /* port 1 status/control */ +#define UHCI_REG_PORTSC2 0x12 /* port 2 status/control */ +#define UHCI_REGSZ 0x20 /* register iospace size */ + +/* in PCI config space for some reason */ +#define UHCI_REG_LEGSUP 0xC0 + + +/* command register */ +#define USB_CMD_MAX_PACKET (1 << 7) +#define USB_CMD_CONFIGURE (1 << 6) +#define USB_CMD_FGR (1 << 4) /* force global resume */ +#define USB_CMD_EGSM (1 << 3) /* global suspend mode */ +#define USB_CMD_GRESET (1 << 2) /* global reset */ +#define USB_CMD_HCRESET (1 << 1) /* host controller reset */ +#define USB_CMD_RS (1 << 0) /* run/stop */ + +/* status register */ +#define USB_STATUS_HALTED (1 << 5) +#define USB_STATUS_PROCESSERR (1 << 4) +#define USB_STATUS_HOSTERR (1 << 3) +#define USB_STATUS_RESUME (1 << 2) +#define USB_STATUS_INTERR (1 << 1) +#define USB_STATUS_USBINT (1 << 0) + +/* interrupt enable register */ +#define USB_INTR_SHORT (1 << 3) /* enable short packets */ +#define USB_INTR_IOC (1 << 2) /* interrupt on complete */ +#define USB_INTR_RESUME (1 << 1) /* resume interrupt enable */ +#define USB_INTR_TIMEOUT (1 << 0) /* timeout int enable */ + +/* port control register */ +#define USB_PORT_SUSPEND (1 << 12) +#define USB_PORT_RESET (1 << 9) +#define USB_PORT_LOWSPEED (1 << 8) +#define USB_PORT_RESUMED (1 << 6) /* resume detected */ +#define USB_PORT_CHANGE (1 << 3) /* enable change */ +#define USB_PORT_ENABLE (1 << 2) /* enable the port */ +#define USB_PORT_CONNECTCHG (1 << 1) /* connect status changed */ +#define USB_PORT_CONNECTSTATUS (1 << 0) /* device is connected */ + +#define ptr_to_flp(x) (((uintptr_t)x) >> 4) +#define flp_to_ptr(x) (uintptr_t)(((uintptr_t)x) << 4) + +/* frame structures */ +#pragma pack(1) +struct frame_list_ptr +{ + uint32_t terminate:1; + uint32_t qh_select:1; + uint32_t depth_select:1; /* only for TD */ + uint32_t resv:1; /* zero */ + uint32_t flp:28; /* frame list pointer */ +}; + +struct td_token +{ + uint32_t pid:8; /* packet id */ + uint32_t dev_addr:7; /* device address */ + uint32_t end_point:4; + uint32_t data_toggle:1; + uint32_t resv:1; + uint32_t maxlen:11; /* maximum pkt length */ +}; + +struct td_control +{ + uint32_t actual_len:11; + uint32_t resv1:5; + + /* status information */ + uint32_t resv2:1; + uint32_t bitstuff:1; + uint32_t timeout:1; + uint32_t nak:1; + uint32_t babble:1; + uint32_t buffer_error:1; + uint32_t stalled:1; + uint32_t active:1; + + /* config data */ + uint32_t ioc:1; /* issue int on complete */ + uint32_t ios:1; /* isochronous select */ + uint32_t ls:1; /* low speed device */ + uint32_t error_limit:2; /* errors before interrupt */ + uint32_t spd:1; /* short packet detect */ + uint32_t resv3:2; +}; + +#define TD_FL_ASYNC 1 +#define TD_FL_USED 0x80000000 + +struct tx_descriptor +{ + struct frame_list_ptr flp; + struct td_control control; + struct td_token token; + uint32_t buf_ptr; + + + struct frame_list_ptr *head; /* for fast removal */ + uint32_t flags; +}; + +struct queue_head +{ + struct frame_list_ptr qhlp; /* queue head link pointer */ + struct frame_list_ptr qelp; /* queue elem link pointer */ +}; +#pragma pack() + +struct uhci_info +{ + struct pci_dev *dev; + struct pci_io *io; /* pci io space */ + struct lock lock; + struct frame_list_ptr *frame_list; /* page aligned frame list */ + + struct tx_descriptor *td_pool; + struct bitmap *td_used; + struct queue_head *qh_pool; + struct bitmap *qh_used; + + uint8_t num_ports; + uint8_t attached_ports; + + int timeouts; /* number of timeouts */ + + struct semaphore td_sem; + struct list devices; /* devices on host */ + struct list waiting; /* threads waiting */ +}; + +struct usb_wait +{ + struct tx_descriptor *td; + struct uhci_dev_info *ud; + struct semaphore sem; + struct list_elem peers; +}; + +struct uhci_dev_info +{ + struct uhci_info *ui; /* owner */ + bool low_speed; /* whether device is low speed */ + int dev_addr; /* device address */ + int errors; /* aggregate errors */ + struct list_elem peers; /* next dev on host */ + struct lock lock; + struct queue_head *qh; +}; + +struct uhci_eop_info +{ + struct uhci_dev_info *ud; + int eop; + int maxpkt; /* max packet size */ + int toggle; /* data toggle bit for bulk transfers */ +}; + +#define uhci_lock(x) lock_acquire(&(x)->lock) +#define uhci_unlock(x) lock_release(&(x)->lock) +#define dev_lock(x) lock_acquire(&(x)->lock) +#define dev_unlock(x) lock_release(&(x)->lock) + + +static int token_to_pid (int token); + +static int uhci_tx_pkt (host_eop_info eop, int token, void *pkt, + int min_sz, int max_sz, int *in_sz, bool wait); + +static int uhci_detect_change (host_info); + + +static int uhci_tx_pkt_now (struct uhci_eop_info *ue, int token, void *pkt, + int sz); +static int uhci_tx_pkt_wait (struct uhci_eop_info *ue, int token, void *pkt, + int max_sz, int *in_sz); +static int uhci_tx_pkt_bulk (struct uhci_eop_info *ue, int token, void *buf, + int sz, int *tx); + + +static int uhci_process_completed (struct uhci_info *ui); + +static struct tx_descriptor *uhci_acquire_td (struct uhci_info *); +static void uhci_release_td (struct uhci_info *, struct tx_descriptor *); +static void uhci_remove_qh (struct uhci_info *ui, struct queue_head *qh); + + +static void qh_free (struct uhci_info *ui, struct queue_head *qh); +static struct queue_head *qh_alloc (struct uhci_info *ui); + +static struct uhci_info *uhci_create_info (struct pci_io *io); +static void uhci_destroy_info (struct uhci_info *ui); +static host_eop_info uhci_create_eop (host_dev_info hd, int eop, int maxpkt); +static void uhci_remove_eop (host_eop_info hei); +static host_dev_info uhci_create_chan (host_info hi, int dev_addr, int ver); +static void uhci_destroy_chan (host_dev_info); +static void uhci_modify_chan (host_dev_info, int dev_addr, int ver); + +static struct tx_descriptor *td_from_pool (struct uhci_info *ui, int idx); + +static int check_and_flip_change (struct uhci_info *ui, int reg); +static void uhci_stop (struct uhci_info *ui); +static void uhci_run (struct uhci_info *ui); +static void uhci_stop_unlocked (struct uhci_info *ui); +static void uhci_run_unlocked (struct uhci_info *ui); +#define uhci_is_stopped(x) (pci_reg_read16((x)->io, UHCI_REG_USBSTS) \ + & USB_STATUS_HALTED) +#define uhci_port_enabled(x, y) (pci_reg_read16((x)->io, (y)) & USB_PORT_ENABLE) +static void uhci_add_td_to_qh (struct queue_head *qh, + struct tx_descriptor *td); +static void uhci_remove_error_td (struct tx_descriptor *td); +static void uhci_setup_td (struct tx_descriptor *td, int dev_addr, int token, + int eop, void *pkt, int sz, int toggle, bool ls); +static int uhci_enable_port (struct uhci_info *ui, int port); +static void uhci_irq (void *uhci_data); +static void uhci_detect_ports (struct uhci_info *ui); + +static int uhci_remove_stalled (struct uhci_info *ui); +static void uhci_stall_watchdog (struct uhci_info *ui); + +static void dump_all_qh (struct uhci_info *ui); +static void dump_qh (struct queue_head *qh); + +static void dump_regs (struct uhci_info *ui); + +void uhci_init (void); + + +static struct usb_host uhci_host = { + .name = "UHCI", + .tx_pkt = uhci_tx_pkt, + .detect_change = uhci_detect_change, + .create_dev_channel = uhci_create_chan, + .remove_dev_channel = uhci_destroy_chan, + .modify_dev_channel = uhci_modify_chan, + .create_eop = uhci_create_eop, + .remove_eop = uhci_remove_eop +}; + +void +uhci_init (void) +{ + struct pci_dev *pd; + int dev_num; + + dev_num = 0; + while ((pd = pci_get_dev_by_class (PCI_MAJOR_SERIALBUS, PCI_MINOR_USB, + PCI_USB_IFACE_UHCI, dev_num)) != NULL) + { + struct pci_io *io; + struct uhci_info *ui; + uint8_t sof; + int i; + + dev_num++; + + /* find IO space */ + io = NULL; + while ((io = pci_io_enum (pd, io)) != NULL) + { + if (pci_io_size (io) == UHCI_REGSZ) + break; + } + + /* not found, next PCI */ + if (io == NULL) + continue; + + ui = uhci_create_info (io); + ui->dev = pd; + + sof = pci_reg_read8 (ui->io, UHCI_REG_SOFMOD); + uhci_detect_ports (ui); + + /* reset devices */ + pci_reg_write16 (ui->io, UHCI_REG_USBCMD, USB_CMD_GRESET); + timer_msleep (50); + pci_reg_write16 (ui->io, UHCI_REG_USBCMD, 0); + timer_msleep (1); + + /* reset controller */ + pci_reg_write16 (ui->io, UHCI_REG_USBCMD, USB_CMD_HCRESET); + timer_msleep (1); + if (pci_reg_read16 (ui->io, UHCI_REG_USBCMD) & USB_CMD_HCRESET) + { + printf ("UHCI: Reset timed out\n"); + uhci_destroy_info (ui); + continue; + } + pci_reg_write16 (ui->io, UHCI_REG_USBINTR, 0); + pci_reg_write16 (ui->io, UHCI_REG_USBCMD, 0); + + for (i = 0; i < ui->num_ports; i++) + pci_reg_write16 (ui->io, UHCI_REG_PORTSC1 + i * 2, 0); + + timer_msleep (100); + printf ("UHCI: Enabling %d root ports\n", ui->num_ports); + ui->attached_ports = 0; + for (i = 0; i < ui->num_ports; i++) + ui->attached_ports += uhci_enable_port (ui, i); + + pci_reg_write8 (ui->io, UHCI_REG_SOFMOD, sof); + pci_reg_write16 (ui->io, UHCI_REG_FRNUM, 0); + pci_reg_write32 (ui->io, UHCI_REG_FLBASEADD, vtop (ui->frame_list)); + pci_reg_write16 (ui->io, UHCI_REG_USBINTR, + USB_INTR_SHORT | USB_INTR_IOC | + USB_INTR_TIMEOUT | USB_INTR_RESUME); + + /* deactivate SMM junk, only enable IRQ */ + pci_write_config16 (ui->dev, UHCI_REG_LEGSUP, 0x2000); + + uhci_lock (ui); + uhci_run (ui); + uhci_unlock (ui); + + pci_register_irq (pd, uhci_irq, ui); + + usb_register_host (&uhci_host, ui); + } +} + +#define UHCI_PORT_TIMEOUT 1000 +static int +uhci_enable_port (struct uhci_info *ui, int port) +{ + uint16_t status; + int count; + + /* individual ports must be reset for QEMU to go into USB_STATE_DEFAULT */ + pci_reg_write16 (ui->io, UHCI_REG_PORTSC1 + port * 2, USB_PORT_RESET); + timer_msleep (50); + + /* ACK disconnect from reset so we can see if port is connected */ + pci_reg_write16 (ui->io, UHCI_REG_PORTSC1 + port * 2, + USB_PORT_ENABLE | USB_PORT_CONNECTCHG); + timer_msleep (10); + status = pci_reg_read16 (ui->io, UHCI_REG_PORTSC1 + port * 2); + if (!(status & USB_PORT_CONNECTSTATUS)) + { + return 0; + } + + /* ACK CONNECTCHG status so port can be enabled */ + pci_reg_write16 (ui->io, UHCI_REG_PORTSC1 + port * 2, + USB_PORT_ENABLE | USB_PORT_CONNECTCHG); + for (count = 0; count < UHCI_PORT_TIMEOUT; count++) + { + if (uhci_port_enabled (ui, UHCI_REG_PORTSC1 + port * 2)) + { + pci_reg_write16 (ui->io, UHCI_REG_PORTSC1 + port * 2, + USB_PORT_ENABLE | USB_PORT_CONNECTCHG); + return 1; + } + } + + printf ("UHCI: Port %d enable timed out\n", port); + dump_regs (ui); + + return 0; +} + + +static void +dump_regs (struct uhci_info *ui) +{ + int regs[] = { 0, 2, 4, 6, 8, 0xc, 0x10, 0x12 }; + int sz[] = { 2, 2, 2, 2, 4, 2, 2, 2 }; + char *name[] = + { "cmd", "sts", "intr", "frnum", "base", "sofmod", "portsc1", "portsc2" }; + int i; + printf ("UHCI registers:\n"); + for (i = 0; i < 8; i++) + { + printf ("%s: %x\n", name[i], (sz[i] == 2) ? + pci_reg_read16 (ui->io, regs[i]) : + pci_reg_read32 (ui->io, regs[i])); + + } +} + + +static void +uhci_destroy_info (struct uhci_info *ui) +{ + palloc_free_page (ui->frame_list); + palloc_free_page (ui->td_pool); + palloc_free_page (ui->qh_pool); + bitmap_destroy (ui->qh_used); + bitmap_destroy (ui->td_used); + free (ui); +} + +static struct uhci_info * +uhci_create_info (struct pci_io *io) +{ + struct uhci_info *ui; + int i; + + ui = malloc (sizeof (struct uhci_info)); + + ui->io = io; + lock_init (&ui->lock); + + /* create an empty schedule */ + ui->frame_list = palloc_get_page (PAL_ASSERT | PAL_NOCACHE); + memset (ui->frame_list, 0, PGSIZE); + for (i = 0; i < FRAME_LIST_ENTRIES; i++) + ui->frame_list[i].terminate = 1; + + /* permit 3 timeouts */ + ui->timeouts = 3; +// thread_create ("uhci watchdog", PRI_MIN, +// (thread_func *) uhci_stall_watchdog, ui); + ui->td_pool = palloc_get_page (PAL_ASSERT | PAL_NOCACHE); + ui->td_used = bitmap_create (TD_ENTRIES); + ui->qh_pool = palloc_get_page (PAL_ASSERT | PAL_NOCACHE); + ui->qh_used = bitmap_create (QH_ENTRIES); + sema_init (&ui->td_sem, TD_ENTRIES); + + list_init (&ui->devices); + list_init (&ui->waiting); + + return ui; +} + + +static int +uhci_tx_pkt (host_eop_info hei, int token, void *pkt, int min_sz, + int max_sz, int *in_sz, bool wait) +{ + struct uhci_eop_info *ue; + struct uhci_dev_info *ud; + + ASSERT (min_sz <= max_sz); + + /* can't have page overlap */ + if (pkt != NULL) + { + ASSERT (max_sz > 0); + ASSERT (pg_no (pkt + max_sz - 1) == pg_no (pkt)); + } + + ue = hei; + ud = ue->ud; + + /* don't bother if ports are down */ + if (ud->ui->attached_ports == 0) + { + return USB_HOST_ERR_NODEV; + } + + /* setup token acts to synchronize data toggle */ + if (token == USB_TOKEN_SETUP) + ue->toggle = 0; + + if (min_sz != 0) + { + return uhci_tx_pkt_bulk (ue, token, pkt, max_sz, in_sz); + } + else + { + if (wait == false) + { + if (in_sz != NULL) + *in_sz = max_sz; + return uhci_tx_pkt_now (ue, token, pkt, max_sz); + } + else + { + return uhci_tx_pkt_wait (ue, token, pkt, max_sz, in_sz); + } + } + + return 0; +} + +static int +uhci_tx_pkt_bulk (struct uhci_eop_info *ue, int token, void *buf, + int sz, int *tx) +{ + /* XXX this can be made to use async packets */ + int bytes = 0; + int txed = 0; + + /* send data in max_pkt sized chunks */ + while (bytes < sz) + { + int to_tx, pkt_txed; + int left; + int err; + bool wait_on_pkt; + + left = sz - bytes; + to_tx = (left > ue->maxpkt) ? ue->maxpkt : left; + wait_on_pkt = (left <= to_tx) ? true : false; + + pkt_txed = 0; + err = uhci_tx_pkt (ue, token, buf + bytes, 0, to_tx, &pkt_txed, + wait_on_pkt); + if (err) + { + if (tx != NULL) + *tx = txed; + return err; + } + txed += pkt_txed; + bytes += pkt_txed; + } + + if (tx != NULL) + *tx = txed; + + return USB_HOST_ERR_NONE; +} + +static int +token_to_pid (int token) +{ + switch (token) + { + case USB_TOKEN_SETUP: + return USB_PID_SETUP; + case USB_TOKEN_IN: + return USB_PID_IN; + case USB_TOKEN_OUT: + return USB_PID_OUT; + default: + PANIC ("Unknown USB token\n"); + } +} + +static void +uhci_setup_td (struct tx_descriptor *td, int dev_addr, int token, + int eop, void *pkt, int sz, int toggle, bool ls) +{ + td->buf_ptr = (sz == 0) ? 0 : vtop (pkt); + + td->token.pid = token_to_pid (token); + td->token.dev_addr = dev_addr; + td->token.end_point = eop; + td->token.data_toggle = toggle; + td->token.maxlen = sz - 1; +// td->control.ls = ls; + + td->control.actual_len = 0; + td->control.active = 1; + td->flp.qh_select = 0; + td->flp.depth_select = 0; + + /* kill packet if too many errors */ + td->control.error_limit = 3; +} + +static int +uhci_tx_pkt_now (struct uhci_eop_info *ue, int token, void *pkt, int sz) +{ + struct tx_descriptor *td; + struct uhci_dev_info *ud; + + ud = ue->ud; + + uhci_lock (ud->ui); + + td = uhci_acquire_td (ud->ui); + memset (td, 0, sizeof (struct tx_descriptor)); + uhci_setup_td (td, ud->dev_addr, token, ue->eop, pkt, sz, ue->toggle, + ud->low_speed); + td->control.ioc = 1; + + uhci_stop (ud->ui); + + uhci_add_td_to_qh (ud->qh, td); + td->flags = TD_FL_ASYNC | TD_FL_USED; + + uhci_run (ud->ui); + uhci_unlock (ud->ui); + + ue->toggle ^= 1; + return USB_HOST_ERR_NONE; +} + +static int +uhci_tx_pkt_wait (struct uhci_eop_info *ue, int token, void *pkt, + int max_sz, int *in_sz) +{ + enum intr_level old_lvl; + struct tx_descriptor *td; + struct usb_wait w; + int err; + struct uhci_dev_info *ud; + + ud = ue->ud; + + uhci_lock (ud->ui); + + td = uhci_acquire_td (ud->ui); + memset (td, 0, sizeof (struct tx_descriptor)); + + uhci_setup_td (td, ud->dev_addr, token, ue->eop, pkt, max_sz, ue->toggle, + ud->low_speed); + td->control.ioc = 1; + + w.td = td; + w.ud = ud; + sema_init (&w.sem, 0); + + uhci_stop (ud->ui); + + /* put into device's queue and add to waiting packet list */ + uhci_add_td_to_qh (ud->qh, td); + td->flags = TD_FL_USED; + + list_push_back (&ud->ui->waiting, &w.peers); + + /* reactivate controller and wait */ + old_lvl = intr_disable (); + uhci_run (ud->ui); + uhci_unlock (ud->ui); + sema_down (&w.sem); + intr_set_level (old_lvl); + + if (in_sz != NULL) + { + if (w.td->control.actual_len == 0x7ff) + *in_sz = 0; + else + *in_sz = w.td->control.actual_len + 1; + } + + if (w.td->control.bitstuff) + err = USB_HOST_ERR_BITSTUFF; + else if (w.td->control.timeout) + err = USB_HOST_ERR_TIMEOUT; + else if (w.td->control.nak) + err = USB_HOST_ERR_NAK; + else if (w.td->control.babble) + err = USB_HOST_ERR_BABBLE; + else if (w.td->control.buffer_error) + err = USB_HOST_ERR_BUFFER; + else if (w.td->control.stalled) + err = USB_HOST_ERR_STALL; + else + { + err = USB_HOST_ERR_NONE; + ue->toggle ^= 1; + } + + uhci_release_td (ud->ui, td); + + return err; +} + +static void +uhci_add_td_to_qh (struct queue_head *qh, struct tx_descriptor *td) +{ + struct frame_list_ptr *fp; + + ASSERT (td != NULL); + + td->head = &qh->qelp; + if (qh->qelp.terminate == 1) + { + /* queue is empty */ + td->flp.terminate = 1; + barrier (); + td->flp.flp = 0; + qh->qelp.flp = ptr_to_flp (vtop (td)); + qh->qelp.terminate = 0; + } + else + { + /* find the last element in the queue */ + fp = ptov (flp_to_ptr (qh->qelp.flp)); + ASSERT (qh->qelp.terminate == 0); + while (!fp->terminate) + { + fp = ptov (flp_to_ptr (fp->flp)); + } + + /* set TD to terminated ptr */ + td->flp = *fp; + + fp->qh_select = 0; + fp->depth_select = 0; + fp->flp = ptr_to_flp (vtop (td)); + barrier (); + fp->terminate = 0; + } +} + +static void +uhci_irq (void *uhci_data) +{ + struct uhci_info *ui; + uint16_t status; + + ui = uhci_data; + status = pci_reg_read16 (ui->io, UHCI_REG_USBSTS); + if (status & USB_STATUS_PROCESSERR) + { + dump_all_qh (ui); + dump_regs (ui); + PANIC ("UHCI: Malformed schedule"); + } + else if (status & USB_STATUS_HOSTERR) + { + dump_all_qh (ui); + dump_regs (ui); + PANIC ("UHCI: Host system error"); + } + else if (status & USB_STATUS_INTERR) + { + /* errors */ + pci_reg_write16 (ui->io, UHCI_REG_USBSTS, USB_STATUS_INTERR); + } + + if (status & USB_STATUS_USBINT) + { + /* turn off interrupt */ + uhci_stop_unlocked (ui); + pci_reg_write16 (ui->io, UHCI_REG_USBSTS, USB_STATUS_USBINT); + uhci_process_completed (ui); + uhci_run_unlocked (ui); + } +} + +static int +uhci_process_completed (struct uhci_info *ui) +{ + struct list_elem *li; + int completed = 0; + size_t start = 0; + + li = list_begin (&ui->waiting); + while (li != list_end (&ui->waiting)) + { + struct usb_wait *uw; + struct list_elem *next; + + next = list_next (li); + uw = list_entry (li, struct usb_wait, peers); + + if (!uw->td->control.active) + { + list_remove (li); + if (uw->td->control.error_limit == 0 || uw->td->control.stalled) + { + uhci_remove_error_td (uw->td); + } + uw->td->flags = 0; + sema_up (&uw->sem); + completed++; + } + li = next; + } + + /* must be a completed async TD.. */ + /* is this too time consuming? I hope not */ + if (completed != 0) + return completed; + + while (start < TD_ENTRIES) + { + struct tx_descriptor *td; + + start = bitmap_scan (ui->td_used, start, 1, true); + if (start == BITMAP_ERROR) + break; + + td = td_from_pool (ui, start); + + if (!td->control.active && (td->flags & TD_FL_ASYNC) && + (td->flags & TD_FL_USED)) + { + if (td->control.error_limit == 0 || td->control.stalled) + { + uhci_remove_error_td (td); + } + uhci_release_td (ui, td); + completed++; + } + start++; + } + + return completed; +} + +static void +uhci_remove_error_td (struct tx_descriptor *td) +{ + struct frame_list_ptr *fp; + uint32_t td_flp; + + ASSERT (td->head != NULL); + + fp = td->head; + td_flp = ptr_to_flp (vtop (td)); + while (fp->flp != td_flp) + { + ASSERT (fp->terminate == 0); + fp = ptov (flp_to_ptr (fp->flp)); + } + *fp = td->flp; +} + +static int +uhci_detect_change (host_info hi) +{ + struct uhci_info *ui; + int change; + int i; + + ui = hi; + change = 0; + uhci_lock (ui); + for (i = 0; i < ui->num_ports; i++) + { + change = check_and_flip_change (ui, i); + if (change != 0) + break; + } + uhci_unlock (ui); + + return change; +} + +static int +check_and_flip_change (struct uhci_info *ui, int port) +{ + int val; + int reg; + + reg = UHCI_REG_PORTSC1 + port * 2; + val = pci_reg_read16 (ui->io, reg); + if (val & USB_PORT_CHANGE) + { + pci_reg_write16 (ui->io, reg, val & ~(USB_PORT_CHANGE)); + return 1; + } + + return 0; +} + +static host_dev_info +uhci_create_chan (host_info hi, int dev_addr, int ver) +{ + struct uhci_info *ui; + struct uhci_dev_info *ud; + int i; + + ASSERT (dev_addr <= 127 && dev_addr >= 0); + + ui = hi; + + ud = malloc (sizeof (struct uhci_dev_info)); + ud->dev_addr = dev_addr; + ud->low_speed = (ver == USB_VERSION_1_0) ? true : false; + + ud->errors = 0; + ud->ui = ui; + lock_init (&ud->lock); + + uhci_lock (ui); + + ud->qh = qh_alloc (ud->ui); + + /* queue data */ + memset (ud->qh, 0, sizeof (*ud->qh)); + ud->qh->qelp.terminate = 1; + barrier (); + ud->qh->qelp.flp = 0; + ud->qh->qelp.qh_select = 0; + ud->qh->qhlp.qh_select = 1; + + uhci_stop (ui); + + /* add to queues in frame list */ + ud->qh->qhlp.flp = ui->frame_list[0].flp; + ud->qh->qhlp.terminate = ui->frame_list[0].terminate; + for (i = 0; i < FRAME_LIST_ENTRIES; i++) + { + ui->frame_list[i].flp = ptr_to_flp (vtop (ud->qh)); + ui->frame_list[i].qh_select = 1; + ui->frame_list[i].terminate = 0; + } + + /* add to device list */ + list_push_back (&ui->devices, &ud->peers); + + uhci_run (ui); + uhci_unlock (ui); + + return ud; +} + +static void +uhci_destroy_chan (host_dev_info hd) +{ + struct uhci_dev_info *ud; + struct list_elem *li; + + ud = hd; + uhci_lock (ud->ui); + + uhci_stop (ud->ui); + + uhci_remove_qh (ud->ui, ud->qh); + + /* wake up all waiting */ + li = list_begin (&ud->ui->waiting); + while (li != list_end (&ud->ui->waiting)) + { + struct usb_wait *w; + w = list_entry (li, struct usb_wait, peers); + if (w->ud == ud) + { + sema_up (&w->sem); + list_remove (li); + } + li = list_next (li); + } + + list_remove (&ud->peers); + + uhci_run (ud->ui); + + qh_free (ud->ui, ud->qh); + + uhci_unlock (ud->ui); + + free (ud); +} + +/** + * Remove a queue from the UHCI schedule + */ +static void +uhci_remove_qh (struct uhci_info *ui, struct queue_head *qh) +{ + uintptr_t qh_flp; + + ASSERT (lock_held_by_current_thread (&ui->lock)); + ASSERT (uhci_is_stopped (ui)); + ASSERT (qh != NULL); + + qh_flp = ptr_to_flp (vtop (qh)); + /* remove from host queue */ + if (ui->frame_list[0].flp == qh_flp) + { + int i; + /* up top */ + for (i = 0; i < FRAME_LIST_ENTRIES; i++) + { + ui->frame_list[i] = qh->qhlp; + } + } + else + { + /* in the middle */ + struct frame_list_ptr *fp; + struct frame_list_ptr *prev; + + fp = ptov (flp_to_ptr (ui->frame_list[0].flp)); + ASSERT (!fp->terminate); + do + { + prev = fp; + fp = ptov (flp_to_ptr (fp->flp)); + } + while (!fp->terminate && fp->flp != qh_flp); + *prev = qh->qhlp; + } +} + +/** + * Put UHCI into stop state + * Wait until status register reflects setting + */ +static void +uhci_stop (struct uhci_info *ui) +{ + ASSERT (intr_get_level () != INTR_OFF); + ASSERT (lock_held_by_current_thread (&ui->lock)); + + uhci_stop_unlocked (ui); +} + +static void +uhci_stop_unlocked (struct uhci_info *ui) +{ + uint16_t cmd; + int i; + + cmd = pci_reg_read16 (ui->io, UHCI_REG_USBCMD); + cmd = cmd & ~USB_CMD_RS; + + pci_reg_write16 (ui->io, UHCI_REG_USBCMD, cmd); + + /* wait for execution schedule to finish up */ + for (i = 0; i < 1000; i++) + { + if (uhci_is_stopped (ui)) + return; + } + + PANIC ("UHCI: Controller did not halt\n"); + +} + +static void +uhci_run_unlocked (struct uhci_info *ui) +{ + uint16_t cmd; + cmd = pci_reg_read16 (ui->io, UHCI_REG_USBCMD); + cmd = cmd | USB_CMD_RS | USB_CMD_MAX_PACKET; + pci_reg_write16 (ui->io, UHCI_REG_USBCMD, cmd); +} + +/** + * Put UHCI into 'Run' State + */ +static void +uhci_run (struct uhci_info *ui) +{ + ASSERT (lock_held_by_current_thread (&ui->lock)); + uhci_run_unlocked (ui); +} + +static struct tx_descriptor * +uhci_acquire_td (struct uhci_info *ui) +{ + size_t td_idx; + struct tx_descriptor *td; + + ASSERT (lock_held_by_current_thread (&ui->lock)); + ASSERT (!uhci_is_stopped (ui)); + + sema_down (&ui->td_sem); + td_idx = bitmap_scan_and_flip (ui->td_used, 0, 1, false); + ASSERT (td_idx != BITMAP_ERROR); + td = td_from_pool (ui, td_idx); + + return td; +} + +static void +uhci_modify_chan (host_dev_info hd, int dev_addr, int ver) +{ + struct uhci_dev_info *ud; + + ud = hd; + ud->dev_addr = dev_addr; + ud->low_speed = (ver == USB_VERSION_1_0) ? true : false; +} + +static void +dump_all_qh (struct uhci_info *ui) +{ + struct list_elem *li; + + printf ("schedule: %x...", vtop (ui->frame_list)); + printf ("%x", *((uint32_t *) ui->frame_list)); + li = list_begin (&ui->devices); + while (li != list_end (&ui->devices)) + { + struct uhci_dev_info *ud; + ud = list_entry (li, struct uhci_dev_info, peers); + dump_qh (ud->qh); + li = list_next (li); + } +} + +static void +dump_qh (struct queue_head *qh) +{ + struct frame_list_ptr *fp; + printf ("qh: %p %x\n", qh, vtop (qh)); + fp = &qh->qelp; + while (!fp->terminate) + { + printf ("%x %x\n", *((uint32_t *) fp), *(uint32_t *) (fp + 1)); + fp = ptov (flp_to_ptr (fp->flp)); + } + printf ("%x %x\n\n", *(uint32_t *) fp, *(uint32_t *) (fp + 1)); +} + +static struct tx_descriptor * +td_from_pool (struct uhci_info *ui, int idx) +{ + ASSERT (idx >= 0 && idx < TD_ENTRIES); + return (((void *) ui->td_pool) + idx * 32); +} + +static void +uhci_detect_ports (struct uhci_info *ui) +{ + int i; + ui->num_ports = 0; + for (i = 0; i < UHCI_MAX_PORTS; i++) + { + uint16_t status; + status = pci_reg_read16 (ui->io, UHCI_REG_PORTSC1 + i * 2); + if (!(status & 0x0080) || status == 0xffff) + return; + ui->num_ports++; + } +} + +static void +uhci_stall_watchdog (struct uhci_info *ui) +{ + while (1) + { + int rmved; + timer_msleep (1000); + printf ("watchdog\n"); + uhci_lock (ui); + uhci_stop (ui); + rmved = uhci_remove_stalled (ui); + if (rmved > 0) + printf ("removed stalled packet in watchdog\n"); + uhci_run (ui); + uhci_unlock (ui); + } +} + +static int +uhci_remove_stalled (struct uhci_info *ui) +{ + struct list_elem *li; + int rmved; + + rmved = 0; + li = list_begin (&ui->waiting); + + intr_disable (); + + while (li != list_end (&ui->waiting)) + { + struct usb_wait *uw; + struct list_elem *next; + uint32_t ctrl; + + next = list_next (li); + uw = list_entry (li, struct usb_wait, peers); + + if ((!uw->td->control.active && uw->td->control.stalled) || + (uw->td->control.nak)) + { + memcpy (&ctrl, &uw->td->control, 4); + printf ("CTRL: %x\n", ctrl); + list_remove (li); + uhci_remove_error_td (uw->td); + sema_up (&uw->sem); + rmved++; + } + li = next; + } + + intr_enable (); + + return rmved; +} + +static void +uhci_release_td (struct uhci_info *ui, struct tx_descriptor *td) +{ + int ofs = (uintptr_t) td - (uintptr_t) ui->td_pool; + int entry = ofs / 32; + + ASSERT (entry < TD_ENTRIES); + + td->flags = 0; + bitmap_reset (ui->td_used, entry); + sema_up (&ui->td_sem); +} + +static host_eop_info +uhci_create_eop (host_dev_info hd, int eop, int maxpkt) +{ + struct uhci_dev_info *ud; + struct uhci_eop_info *e; + + ud = hd; + + e = malloc (sizeof (struct uhci_eop_info)); + e->eop = eop; + e->ud = ud; + e->maxpkt = maxpkt; + e->toggle = 0; + + return e; +} + +static void +uhci_remove_eop (host_eop_info hei) +{ + free (hei); +} + +static struct queue_head * +qh_alloc (struct uhci_info *ui) +{ + size_t qh_idx; + struct queue_head *qh; + + ASSERT (lock_held_by_current_thread (&ui->lock)); + + qh_idx = bitmap_scan_and_flip (ui->qh_used, 0, 1, false); + if (qh_idx == BITMAP_ERROR) + { + PANIC ("UHCI: Too many queue heads in use-- runaway USB stack?\n"); + } + qh = (void *) (((intptr_t) ui->qh_pool) + qh_idx * 16); + + return qh; +} + +static void +qh_free (struct uhci_info *ui, struct queue_head *qh) +{ + size_t entry; + ASSERT (lock_held_by_current_thread (&ui->lock)); + + entry = ((intptr_t) qh - (intptr_t) ui->qh_pool) / 16; + bitmap_reset (ui->qh_used, entry); +} diff --git a/src/lib/endian.h b/src/lib/endian.h new file mode 100644 index 0000000..7e5fc32 --- /dev/null +++ b/src/lib/endian.h @@ -0,0 +1,19 @@ +#ifndef __LIB_ENDIAN_H +#define __LIB_ENDIAN_H + + +#define endian_swap32(x) ((((x) & 0xff000000) >> 24) | \ + (((x) & 0x00ff0000) >> 8) | \ + (((x) & 0x0000ff00) << 8) | \ + (((x) & 0x000000ff) << 24)) +#define endian_swap16(x) ((((x) & 0xff00)>> 8) | (((x) & 0x00ff) << 8)) +#define endian_swap24(x) (((x) >> 16) & 0xff) | (((x) & 0xff) << 16) | ((x) & 0xff00) + +#define be32_to_machine(x) endian_swap32(x) +#define be16_to_machine(x) endian_swap16(x) +#define be24_to_machine(x) endian_swap24(x) +#define machine_to_be32(x) endian_swap32(x) +#define machine_to_be16(x) endian_swap16(x) +#define machine_to_be24(x) endian_swap24(x) + +#endif diff --git a/src/threads/init.c b/src/threads/init.c index cebec2c..dbb41da 100644 --- a/src/threads/init.c +++ b/src/threads/init.c @@ -12,9 +12,11 @@ #include "devices/input.h" #include "devices/serial.h" #include "devices/shutdown.h" +#include "devices/pci.h" #include "devices/timer.h" #include "devices/vga.h" #include "devices/rtc.h" +#include "devices/usb.h" #include "threads/interrupt.h" #include "threads/io.h" #include "threads/loader.h" @@ -40,6 +42,7 @@ /* Page directory with kernel mappings only. */ uint32_t *init_page_dir; +bool init_page_dir_initialized; #ifdef FILESYS /* -f: Format the file system? */ @@ -59,6 +62,7 @@ static size_t user_page_limit = SIZE_MAX; static void bss_init (void); static void paging_init (void); +static void pci_zone_init (void); static char **read_command_line (void); static char **parse_options (char **argv); @@ -109,6 +113,7 @@ main (void) intr_init (); timer_init (); kbd_init (); + pci_init (); input_init (); #ifdef USERPROG exception_init (); @@ -119,9 +124,11 @@ main (void) thread_start (); serial_init_queue (); timer_calibrate (); + usb_init (); #ifdef FILESYS /* Initialize file system. */ + usb_storage_init (); ide_init (); locate_block_devices (); filesys_init (format_filesys); @@ -174,18 +181,39 @@ paging_init (void) if (pd[pde_idx] == 0) { pt = palloc_get_page (PAL_ASSERT | PAL_ZERO); - pd[pde_idx] = pde_create (pt); + pd[pde_idx] = pde_create_kernel (pt); } pt[pte_idx] = pte_create_kernel (vaddr, !in_kernel_text); } + pci_zone_init (); + /* Store the physical address of the page directory into CR3 aka PDBR (page directory base register). This activates our new page tables immediately. See [IA32-v2a] "MOV--Move to/from Control Registers" and [IA32-v3a] 3.7.5 "Base Address of the Page Directory". */ asm volatile ("movl %0, %%cr3" : : "r" (vtop (init_page_dir))); + + init_page_dir_initialized = true; +} + +/* initialize PCI zone at PCI_ADDR_ZONE_BEGIN - PCI_ADDR_ZONE_END*/ +static void +pci_zone_init (void) +{ + int i; + for (i = 0; i < PCI_ADDR_ZONE_PDES; i++) + { + size_t pde_idx = pd_no ((void *) PCI_ADDR_ZONE_BEGIN) + i; + uint32_t pde; + void *pt; + + pt = palloc_get_page (PAL_ASSERT | PAL_ZERO); + pde = pde_create_kernel (pt); + init_page_dir[pde_idx] = pde; + } } /* Breaks the kernel command line into words and returns them as diff --git a/src/threads/interrupt.c b/src/threads/interrupt.c index 9ffd873..27f4708 100644 --- a/src/threads/interrupt.c +++ b/src/threads/interrupt.c @@ -17,6 +17,8 @@ #define PIC0_DATA 0x21 /* Master PIC data register address. */ #define PIC1_CTRL 0xa0 /* Slave PIC control register address. */ #define PIC1_DATA 0xa1 /* Slave PIC data register address. */ +#define IRQ_CASCADE0 2 +#define IRQ_CASCADE1 9 /* Number of x86 interrupts. */ #define INTR_CNT 256 @@ -33,6 +35,9 @@ static intr_handler_func *intr_handlers[INTR_CNT]; /* Names for each interrupt, for debugging purposes. */ static const char *intr_names[INTR_CNT]; +/* cached values for PIC */ +static uint8_t pic_mask[2]; + /* External interrupts are those generated by devices outside the CPU, such as the timer. External interrupts run with interrupts turned off, so they never nest, nor are they ever @@ -251,6 +256,8 @@ pic_init (void) /* Unmask all interrupts. */ outb (PIC0_DATA, 0x00); outb (PIC1_DATA, 0x00); + pic_mask[0] = 0; + pic_mask[1] = 0; } /* Sends an end-of-interrupt signal to the PIC for the given IRQ. @@ -418,3 +425,44 @@ intr_name (uint8_t vec) { return intr_names[vec]; } + +/** masks a given IRQ */ +void intr_irq_mask(int irq) +{ + if(irq < 8){ + pic_mask[0] |= 1 << irq; + outb (PIC0_DATA, pic_mask[0]); + }else{ + pic_mask[1] |= 1 << (irq - 8); + outb (PIC1_DATA, pic_mask[1]); + } +} + +/** unmasks a given IRQ */ +void intr_irq_unmask(int irq) +{ + if(irq >= 8){ + /* enable cascade if not enabled for pic2 */ + if(pic_mask[1] & (1 << (IRQ_CASCADE1 - 8))) + pic_mask[1] &= ~(1 << (IRQ_CASCADE1 - 8)); + + pic_mask[1] &= ~(1 << (irq - 8)); + outb(PIC1_DATA, pic_mask[1]); + + /* enable cascade if not enabled for pic1 */ + if(pic_mask[0] & (1 << IRQ_CASCADE0)) + irq = IRQ_CASCADE0; + } + + if(irq < 8){ + pic_mask[0] &= ~(1 << irq); + outb (PIC0_DATA, pic_mask[0]); + } + +} + +/* return whether an interrupt vector is registered */ +bool intr_is_registered(uint8_t vec_no) +{ + return (intr_handlers[vec_no] != NULL); +} diff --git a/src/threads/interrupt.h b/src/threads/interrupt.h index d43e06d..79d63e7 100644 --- a/src/threads/interrupt.h +++ b/src/threads/interrupt.h @@ -67,4 +67,9 @@ void intr_yield_on_return (void); void intr_dump_frame (const struct intr_frame *); const char *intr_name (uint8_t vec); +void intr_irq_mask(int irq); +void intr_irq_unmask(int irq); + +bool intr_is_registered ( uint8_t vec ); + #endif /* threads/interrupt.h */ diff --git a/src/threads/palloc.c b/src/threads/palloc.c index 4fc8394..fbd74fc 100644 --- a/src/threads/palloc.c +++ b/src/threads/palloc.c @@ -39,6 +39,7 @@ static struct pool kernel_pool, user_pool; static void init_pool (struct pool *, void *base, size_t page_cnt, const char *name); static bool page_from_pool (const struct pool *, void *page); +static void page_set_cache(void* page, bool enable_cache); /* Initializes the page allocator. At most USER_PAGE_LIMIT pages are put into the user pool. */ diff --git a/src/threads/palloc.h b/src/threads/palloc.h index cb3b70e..adfb3ca 100644 --- a/src/threads/palloc.h +++ b/src/threads/palloc.h @@ -6,11 +6,14 @@ /* How to allocate pages. */ enum palloc_flags { - PAL_ASSERT = 001, /* Panic on failure. */ - PAL_ZERO = 002, /* Zero page contents. */ - PAL_USER = 004 /* User page. */ + PAL_ASSERT = 1 << 0, /* Panic on failure. */ + PAL_ZERO = 1 << 1, /* Zero page contents. */ + PAL_USER = 1 << 2, /* User page. */ + PAL_NOCACHE = 1 << 3 /* Disable memory caching for page. */ }; +extern void *zero_page; + void palloc_init (size_t user_page_limit); void *palloc_get_page (enum palloc_flags); void *palloc_get_multiple (enum palloc_flags, size_t page_cnt); diff --git a/src/threads/pte.h b/src/threads/pte.h index 1660727..9133c45 100644 --- a/src/threads/pte.h +++ b/src/threads/pte.h @@ -28,6 +28,14 @@ #define PDBITS 10 /* Number of page dir bits. */ #define PDMASK BITMASK(PDSHIFT, PDBITS) /* Page directory bits (22:31). */ +/* memory dedicated to PCI - make sure this is 4MB aligned */ +#define PCI_ADDR_ZONE_BEGIN 0xe0000000 +#define PCI_ADDR_ZONE_END 0xe0800000 +#define PCI_ADDR_ZONE_PDES 2 +#define PCI_ADDR_ZONE_PAGES (PCI_ADDR_ZONE_END-PCI_ADDR_ZONE_BEGIN)/PGSIZE + + + /* Obtains page table index from a virtual address. */ static inline unsigned pt_no (const void *va) { return ((uintptr_t) va & PTMASK) >> PTSHIFT; @@ -64,15 +72,24 @@ static inline uintptr_t pd_no (const void *va) { #define PTE_P 0x1 /* 1=present, 0=not present. */ #define PTE_W 0x2 /* 1=read/write, 0=read-only. */ #define PTE_U 0x4 /* 1=user/kernel, 0=kernel only. */ +#define PTE_WT (1 << 3) /* 1=write-through, 0=write-back */ +#define PTE_CD (1 << 4) /* 1=cache disabled, 0=cache enabled. */ #define PTE_A 0x20 /* 1=accessed, 0=not acccessed. */ #define PTE_D 0x40 /* 1=dirty, 0=not dirty (PTEs only). */ +#define PTE_G (1 << 8) /* 1=global page, do not flush */ /* Returns a PDE that points to page table PT. */ -static inline uint32_t pde_create (uint32_t *pt) { +static inline uint32_t pde_create_user (uint32_t *pt) { ASSERT (pg_ofs (pt) == 0); return vtop (pt) | PTE_U | PTE_P | PTE_W; } +/* Returns a PDE that points to page table PT. */ +static inline uint32_t pde_create_kernel (uint32_t *pt) { + ASSERT (pg_ofs (pt) == 0); + return vtop (pt) | PTE_P | PTE_W | PTE_G; +} + /* Returns a pointer to the page table that page directory entry PDE, which must "present", points to. */ static inline uint32_t *pde_get_pt (uint32_t pde) { @@ -86,7 +103,7 @@ static inline uint32_t *pde_get_pt (uint32_t pde) { The page will be usable only by ring 0 code (the kernel). */ static inline uint32_t pte_create_kernel (void *page, bool writable) { ASSERT (pg_ofs (page) == 0); - return vtop (page) | PTE_P | (writable ? PTE_W : 0); + return vtop (page) | PTE_P | (writable ? PTE_W : 0) | PTE_G; } /* Returns a PTE that points to PAGE. @@ -94,7 +111,8 @@ static inline uint32_t pte_create_kernel (void *page, bool writable) { If WRITABLE is true then it will be writable as well. The page will be usable by both user and kernel code. */ static inline uint32_t pte_create_user (void *page, bool writable) { - return pte_create_kernel (page, writable) | PTE_U; + ASSERT (pg_ofs (page) == 0); + return vtop (page) | PTE_P | (writable ? PTE_W : 0) | PTE_U; } /* Returns a pointer to the page that page table entry PTE points diff --git a/src/userprog/pagedir.c b/src/userprog/pagedir.c index a6a87b8..83af90d 100644 --- a/src/userprog/pagedir.c +++ b/src/userprog/pagedir.c @@ -74,7 +74,7 @@ lookup_page (uint32_t *pd, const void *vaddr, bool create) if (pt == NULL) return NULL; - *pde = pde_create (pt); + *pde = pde_create_user (pt); } else return NULL; -- 2.30.2