diff options
-rw-r--r-- | util/uio_usbdebug/Makefile | 54 | ||||
-rw-r--r-- | util/uio_usbdebug/README | 77 | ||||
-rw-r--r-- | util/uio_usbdebug/console/printk.c | 34 | ||||
-rw-r--r-- | util/uio_usbdebug/drivers/usb/pci_ehci.c | 98 | ||||
-rw-r--r-- | util/uio_usbdebug/include/device/device.h | 35 | ||||
-rw-r--r-- | util/uio_usbdebug/lib/cbmem.c | 8 | ||||
-rw-r--r-- | util/uio_usbdebug/linux/Makefile | 13 | ||||
-rw-r--r-- | util/uio_usbdebug/linux/uio_ehci_pci.c | 106 | ||||
-rw-r--r-- | util/uio_usbdebug/uio_usbdebug.c | 67 | ||||
-rw-r--r-- | util/uio_usbdebug/uio_usbdebug_intel.c | 67 |
10 files changed, 559 insertions, 0 deletions
diff --git a/util/uio_usbdebug/Makefile b/util/uio_usbdebug/Makefile new file mode 100644 index 0000000000..74bc80e2e6 --- /dev/null +++ b/util/uio_usbdebug/Makefile @@ -0,0 +1,54 @@ +include ../../.config + +ARCHDIR-$(CONFIG_ARCH_ARMV7) := armv7 +ARCHDIR-$(CONFIG_ARCH_X86) := x86 + +# Only Intel chipsets supported, currently. +OBJ-$(CONFIG_SOUTHBRIDGE_INTEL_COMMON) += uio_usbdebug_intel.o + +PROGRAM := uio_usbdebug + +CB_SRC := $(shell realpath ../../src) +CB_SOURCES := drivers/usb/ehci_debug.c +CB_INCLUDES := \ + drivers/usb/ehci.h \ + drivers/usb/ehci_debug.h \ + drivers/usb/usb_ch9.h +INCLUDES := \ + include/device/device.h +OBJECTS := \ + uio_usbdebug.o \ + drivers/usb/pci_ehci.o \ + console/printk.o \ + lib/cbmem.o \ + $(OBJ-y) \ + $(patsubst %.c,%.o,$(CB_SOURCES)) + +KCONFIG_H := ../../src/include/kconfig.h + +CFLAGS += \ + -m32 -g \ + -Wall -Wextra -Werror \ + -Wno-unused-parameter -Wno-error=sign-compare +CPPFLAGS += \ + -Iinclude/ \ + -I../../src/include/ -I../../src/arch/$(ARCHDIR-y)/include/ \ + -I../../build/ -include$(KCONFIG_H) + +LIBS := -lpci -lz + +all: $(PROGRAM) + +$(PROGRAM): $(OBJECTS) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJECTS) $(LIBS) + +$(CB_SOURCES) $(CB_INCLUDES): + @mkdir -p $(dir $@) + @ln -sf $(CB_SRC)/$@ $@ + +$(OBJECTS): $(CONFIG_H) $(CB_INCLUDES) $(INCLUDES) + +clean: + -@rm -rf $(CB_SOURCES) $(CB_INCLUDES) $(OBJECTS) $(PROGRAM) + +.PHONY: all clean diff --git a/util/uio_usbdebug/README b/util/uio_usbdebug/README new file mode 100644 index 0000000000..2d523386ba --- /dev/null +++ b/util/uio_usbdebug/README @@ -0,0 +1,77 @@ + +uio_usbdebug - Run coreboot's usbdebug driver in userspace +========================================================== + + +## Purpose + +uio_usbdebug enables you to debug coreboot's usbdebug driver inside a +running operating system (only Linux at this time). This comes very +handy if you're hacking the usbdebug driver and don't have any other +debug output from coreboot itself. + + +## State + +Currently only Intel chipsets are supported. Support for other chipsets +should be straightforward (normally just some port-enable code has to +be implemented). + +The Linux kernel driver (see linux/uio_ehci_pci.c) has only one PCI ID +hardcoded (for ICH7). The whole setup has been developed and tested on +a ThinkPad T60. + +### Files + +uio_usbdebug.c - The userspace part of the uio interface. + +uio_usbdebug_intel.c - Port enable code for Intel chipsets. + +linux/uio_ehci_pci.c - Kernel part of the uio interface. + +console/printk.c - A do_printk() implementation so you can see debug + output with CONFIG_DEBUG_USBDEBUG enabled. + +device/*.c lib/*.c - Some stubs for (hopefully) unneeded functions for + proper linking. + + +## Usage + +### Preparations + +The MMIO space has to be a whole 4K page in size and alignment to be +mapped into userspace. This is very uncommon, so you'll most probably +have to remap the MMIO space. The Linux kernel does that for you with +the `pci=resource_alignment=<pci address>` kernel parameter (e.g. +`pci=resource_alignment=0:1d.7` for ICH7). + +If your PCI device isn't listed in the kernel driver yet, you might want +to add it to the `ehci_pci_ids` table in `linux/uio_ehci_pci.c` (or do +some module alias magic if you know how to). + +### Build / Install + +Somehow like this: + +$ # Configure coreboot for your board and enable CONFIG_USBDEBUG +$ make menuconfig +$ cd util/uio_usbdebug/ +$ make -Clinux/ +$ sudo make -Clinux/ install +$ make + +### Run + +$ # Unload Linux' EHCI driver (high-speed devices will stop working) +$ sudo modprobe -r ehci-pci +$ # Load the uio driver +$ sudo modprobe uio-ehci-pci +$ # Find your uio device +$ ls /sys/module/uio_ehci_pci/drivers/*/*/uio/ +uio0 +$ # Run uio_usbdebug on this device +$ sudo ./uio_usbdebug /dev/uio0 + +Sadly, uio_usbdebug has to be run with root privileges since there are +port-80 writes in the usbdebug driver. diff --git a/util/uio_usbdebug/console/printk.c b/util/uio_usbdebug/console/printk.c new file mode 100644 index 0000000000..a73407cfe8 --- /dev/null +++ b/util/uio_usbdebug/console/printk.c @@ -0,0 +1,34 @@ +/* + * This file is part of uio_usbdebug + * + * Copyright (C) 2013 Nico Huber <nico.h@gmx.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdio.h> +#include <stdarg.h> +#include <console/console.h> + +int do_printk(int msg_level, const char *const fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i = vfprintf(stderr, fmt, args); + va_end(args); + + return i; +} diff --git a/util/uio_usbdebug/drivers/usb/pci_ehci.c b/util/uio_usbdebug/drivers/usb/pci_ehci.c new file mode 100644 index 0000000000..6a316a7983 --- /dev/null +++ b/util/uio_usbdebug/drivers/usb/pci_ehci.c @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2014 Nico Huber <nico.h@gmx.de> + * + * Code borrowed from pci_early.c: + * Copyright (C) 2011 Google Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA + */ + +#include <device/pci_ehci.h> + +static unsigned pci_find_next_capability(pci_devfn_t dev, unsigned cap, unsigned last) +{ + unsigned pos = 0; + u16 status; + unsigned reps = 48; + + status = pci_read_config16(dev, PCI_STATUS); + if (!(status & PCI_STATUS_CAP_LIST)) + return 0; + + u8 hdr_type = pci_read_config8(dev, PCI_HEADER_TYPE); + switch (hdr_type & 0x7f) { + case PCI_HEADER_TYPE_NORMAL: + case PCI_HEADER_TYPE_BRIDGE: + pos = PCI_CAPABILITY_LIST; + break; + case PCI_HEADER_TYPE_CARDBUS: + pos = PCI_CB_CAPABILITY_LIST; + break; + default: + return 0; + } + + pos = pci_read_config8(dev, pos); + while (reps-- && (pos >= 0x40)) { /* Loop through the linked list. */ + unsigned this_cap; + + pos &= ~3; + this_cap = pci_read_config8(dev, pos + PCI_CAP_LIST_ID); + if (this_cap == 0xff) + break; + + if (!last && (this_cap == cap)) + return pos; + + if (last == pos) + last = 0; + + pos = pci_read_config8(dev, pos + PCI_CAP_LIST_NEXT); + } + return 0; +} + +static unsigned pci_find_capability(pci_devfn_t dev, unsigned cap) +{ + return pci_find_next_capability(dev, cap, 0); +} + +extern void *ehci_bar; +int ehci_debug_hw_enable(unsigned int *base, unsigned int *dbg_offset) +{ + pci_devfn_t dbg_dev = pci_ehci_dbg_dev(CONFIG_USBDEBUG_HCD_INDEX); + pci_ehci_dbg_enable(dbg_dev, CONFIG_EHCI_BAR); + pci_devfn_t dev = dbg_dev; + + u8 pos = pci_find_capability(dev, PCI_CAP_ID_EHCI_DEBUG); + if (!pos) + return -1; + + u32 cap = pci_read_config32(dev, pos); + + /* FIXME: We should remove static EHCI_BAR_INDEX. */ + u8 dbg_bar = 0x10 + 4 * ((cap >> 29) - 1); + if (dbg_bar != EHCI_BAR_INDEX) + return -1; + + *base = (u32)ehci_bar; + *dbg_offset = (cap>>16) & 0x1ffc; + return 0; +} + +void ehci_debug_select_port(unsigned int port) +{ + pci_devfn_t dbg_dev = pci_ehci_dbg_dev(CONFIG_USBDEBUG_HCD_INDEX); + pci_ehci_dbg_set_port(dbg_dev, port); +} diff --git a/util/uio_usbdebug/include/device/device.h b/util/uio_usbdebug/include/device/device.h new file mode 100644 index 0000000000..1838f4d348 --- /dev/null +++ b/util/uio_usbdebug/include/device/device.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2014 Nico Huber <nico.h@gmx.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA + */ + + +#ifndef _DEVICE_DEVICE_H +#define _DEVICE_DEVICE_H + +#include <pci/pci.h> + +typedef struct pci_dev *pci_devfn_t; + +#define pci_read_config8 pci_read_byte +#define pci_read_config16 pci_read_word +#define pci_read_config32 pci_read_long + +#define PCI_CAP_ID_EHCI_DEBUG PCI_CAP_ID_DBG + +extern struct pci_access *pci_access; +#define PCI_DEV(b, d, f) pci_get_dev(pci_access, 0, b, d, f) + +#endif diff --git a/util/uio_usbdebug/lib/cbmem.c b/util/uio_usbdebug/lib/cbmem.c new file mode 100644 index 0000000000..6d87880acf --- /dev/null +++ b/util/uio_usbdebug/lib/cbmem.c @@ -0,0 +1,8 @@ + +#include <stdint.h> +#include <stddef.h> + +void *cbmem_find(u32 id) +{ + return NULL; +} diff --git a/util/uio_usbdebug/linux/Makefile b/util/uio_usbdebug/linux/Makefile new file mode 100644 index 0000000000..fd60b4f7b8 --- /dev/null +++ b/util/uio_usbdebug/linux/Makefile @@ -0,0 +1,13 @@ + +obj-m := uio_ehci_pci.o + +all: uio_ehci_pci.c + @$(MAKE) -C/lib/modules/`uname -r`/build M=$(CURDIR) modules + +install: + @$(MAKE) -C/lib/modules/`uname -r`/build M=$(CURDIR) modules_install + +clean: + -@$(MAKE) -C/lib/modules/`uname -r`/build M=$(CURDIR) clean + +.PHONY: all install clean diff --git a/util/uio_usbdebug/linux/uio_ehci_pci.c b/util/uio_usbdebug/linux/uio_ehci_pci.c new file mode 100644 index 0000000000..d5c33e3fef --- /dev/null +++ b/util/uio_usbdebug/linux/uio_ehci_pci.c @@ -0,0 +1,106 @@ +/* + * uio_ehci_pci - UIO driver for PCI EHCI devices + * + * Copyright (C) 2013 Nico Huber <nico.h@gmx.de> + * + * This only implements MMIO access (no interrupts). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA + */ + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/uio_driver.h> + +#define DRIVER_VERSION "0.0.1" +#define DRIVER_AUTHOR "Nico Huber <nico.h@gmx.de>" +#define DRIVER_DESC "UIO driver for PCI EHCI devices" +#define DRIVER_TAG "uio_ehci_pci" + +static int probe(struct pci_dev *const pci_dev, + const struct pci_device_id *const did) +{ + struct uio_info *info; + int ret; + + ret = pci_enable_device(pci_dev); + if (ret) + goto return_; + + ret = pci_request_regions(pci_dev, DRIVER_TAG); + if (ret) + goto return_disable; + + info = kzalloc(sizeof(struct uio_info), GFP_KERNEL); + if (!info) { + ret = -ENOMEM; + goto return_release; + } + + info->name = DRIVER_TAG; + info->version = DRIVER_VERSION; + + info->mem[0].name = "EHCI MMIO area"; + info->mem[0].addr = pci_resource_start(pci_dev, 0); + if (!info->mem[0].addr) { + ret = -ENODEV; + goto return_free; + } + info->mem[0].size = pci_resource_len(pci_dev, 0); + info->mem[0].memtype = UIO_MEM_PHYS; + + ret = uio_register_device(&pci_dev->dev, info); + if (ret) + goto return_free; + pci_set_drvdata(pci_dev, info); + + return 0; +return_free: + kfree(info); +return_release: + pci_release_regions(pci_dev); +return_disable: + pci_disable_device(pci_dev); +return_: + return ret; +} + +static void remove(struct pci_dev *const pci_dev) +{ + struct uio_info *const info = pci_get_drvdata(pci_dev); + + uio_unregister_device(info); + kfree(info); + pci_release_regions(pci_dev); + pci_disable_device(pci_dev); +} + +static DEFINE_PCI_DEVICE_TABLE(ehci_pci_ids) = { + { PCI_DEVICE(0x8086, 0x27cc) }, + { 0, } +}; + +static struct pci_driver uio_ehci_pci_driver = { + .name = DRIVER_TAG, + .id_table = ehci_pci_ids, + .probe = probe, + .remove = remove, +}; + +module_pci_driver(uio_ehci_pci_driver); +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/util/uio_usbdebug/uio_usbdebug.c b/util/uio_usbdebug/uio_usbdebug.c new file mode 100644 index 0000000000..e2836fc3b1 --- /dev/null +++ b/util/uio_usbdebug/uio_usbdebug.c @@ -0,0 +1,67 @@ +/* + * uio_usbdebug - Run coreboot's usbdebug driver in userspace + * + * Copyright (C) 2013 Nico Huber <nico.h@gmx.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdio.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/mman.h> + +#include <pci/pci.h> + +/* coreboot's arch/io.h conflicts with libc's sys/io.h, so declare this here: */ +int ioperm(unsigned long from, unsigned long num, int turn_on); + +#include <arch/io.h> +#include <console/usb.h> + +void *ehci_bar; +struct pci_access *pci_access; + +int main(int argc, char *argv[]) +{ + if (argc != 2) { + fprintf(stderr, "Usage: %s <uio-dev>\n", argv[0]); + return 1; + } + const int fd = open(argv[1], O_RDWR); + if (fd < 0) { + perror("Failed to open uio device"); + return 2; + } + ehci_bar = + mmap(NULL, 1 << 8, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (MAP_FAILED == ehci_bar) { + perror("Failed to map ehci bar"); + close(fd); + return 3; + } + + ioperm(0x80, 1, 1); + + pci_access = pci_alloc(); + pci_init(pci_access); + + usbdebug_init(); + + pci_cleanup(pci_access); + munmap(ehci_bar, 1 << 8); + close(fd); + return 0; +} diff --git a/util/uio_usbdebug/uio_usbdebug_intel.c b/util/uio_usbdebug/uio_usbdebug_intel.c new file mode 100644 index 0000000000..5e4d9264a3 --- /dev/null +++ b/util/uio_usbdebug/uio_usbdebug_intel.c @@ -0,0 +1,67 @@ +/* + * This file is part of uio_usbdebug + * + * Copyright (C) 2013 Nico Huber <nico.h@gmx.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <arch/io.h> +#include <device/device.h> +#include <device/pci_ehci.h> +#include <console/usb.h> + +extern void *ehci_bar; + +pci_devfn_t pci_ehci_dbg_dev(unsigned hcd_idx) +{ + u32 class; + pci_devfn_t dev; + +#if CONFIG_HAVE_USBDEBUG_OPTIONS + if (hcd_idx==2) + dev = PCI_DEV(0, 0x1a, 0); + else + dev = PCI_DEV(0, 0x1d, 0); +#else + dev = PCI_DEV(0, 0x1d, 7); +#endif + + class = pci_read_config32(dev, PCI_CLASS_REVISION) >> 8; +#if CONFIG_HAVE_USBDEBUG_OPTIONS + if (class != PCI_EHCI_CLASSCODE) { + /* If we enter here before RCBA programming, EHCI function may + * appear with the highest function number instead. + */ + dev |= PCI_DEV(0, 0, 7); + class = pci_read_config32(dev, PCI_CLASS_REVISION) >> 8; + } +#endif + if (class != PCI_EHCI_CLASSCODE) + return 0; + + return dev; +} + +void pci_ehci_dbg_set_port(pci_devfn_t dev, unsigned int port) +{ + /* claim usb debug port */ + const unsigned long dbgctl_addr = + ((unsigned long)ehci_bar) + CONFIG_EHCI_DEBUG_OFFSET; + write32(dbgctl_addr, read32(dbgctl_addr) | (1 << 30)); +} + +void pci_ehci_dbg_enable(pci_devfn_t dev, unsigned long base) +{ +} |