uio_usbdebug: User-space-i/o framework for usbdebug

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.

Currently, only Intel chipsets are supported.

Change-Id: Iaf0bcd4b4c01ae0b099d1206d553344054a62f31
Signed-off-by: Nico Huber <nico.h@gmx.de>
Reviewed-on: http://review.coreboot.org/4695
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
This commit is contained in:
Nico Huber 2014-01-01 20:47:55 +01:00 committed by Stefan Reinauer
parent 86f4ca5b4b
commit b0f8326ac2
10 changed files with 559 additions and 0 deletions

View File

@ -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

77
util/uio_usbdebug/README Normal file
View File

@ -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.

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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

View File

@ -0,0 +1,8 @@
#include <stdint.h>
#include <stddef.h>
void *cbmem_find(u32 id)
{
return NULL;
}

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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)
{
}