Added support for PCI config space read/write via PCI BIOS. Added some PCI commands to query/modify config space and enum devices.
diff --git a/app/pcitests/pci_tests.c b/app/pcitests/pci_tests.c
new file mode 100644
index 0000000..19c11dc
--- /dev/null
+++ b/app/pcitests/pci_tests.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <app.h>
+#include <debug.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <compiler.h>
+#include <platform.h>
+#include <dev/pci.h>
+
+#if defined(WITH_LIB_CONSOLE)
+#include <lib/console.h>
+
+/*
+ * enumerates pci devices
+ */
+static void pci_list(void)
+{
+	pci_location_t state;
+	uint16_t device_id, vendor_id;
+	uint8_t header_type;
+	int busses = 0, devices = 0, lines = 0, devfn, ret;
+	char c;
+	
+	printf("Scanning...\n");
+	
+	for (state.bus = 0; state.bus <= pci_get_last_bus(); state.bus++) {
+		busses++;
+		
+		for (devfn = 0; devfn < 256; devfn++) {
+			state.dev_fn = devfn;
+			
+			ret = pci_read_config_half(&state, PCI_CONFIG_VENDOR_ID, &vendor_id);
+			if (ret != _PCI_SUCCESSFUL) goto error;
+			
+			ret = pci_read_config_half(&state, PCI_CONFIG_DEVICE_ID, &device_id);
+			if (ret != _PCI_SUCCESSFUL) goto error;
+			
+			ret = pci_read_config_byte(&state, PCI_CONFIG_HEADER_TYPE, &header_type);
+			if (ret != _PCI_SUCCESSFUL) goto error;
+			
+			if (vendor_id != 0xffff) {
+				printf("%02x:%02x vendor_id=%04x device_id=%04x, header_type=%02x\n", state.bus, state.dev_fn,
+						vendor_id, device_id, header_type);
+				devices++;
+				lines++;
+			}
+			
+			if (~header_type & PCI_HEADER_TYPE_MULTI_FN) {
+				// this is not a multi-function device, so advance to the next device
+				devfn |= 7;
+			}
+			
+			if (lines == 23) {
+				printf("... press any key to continue, q to quit ...");
+				while(getc(&c) < 0);
+				printf("\n");
+				lines = 0;
+				
+				if (c == 'q' || c == 'Q') goto quit;
+			}
+		}
+	}
+
+	printf("... done. Scanned %d busses, %d device/functions\n", busses, devices);
+quit:
+	return;
+	
+error:
+	printf("Error while reading PCI config space: %02x\n", ret);
+}
+
+/*
+ * a somewhat fugly pci config space examin/modify command. this should probably
+ * be broken up a bit.
+ */
+static int pci_config(int argc, const cmd_args *argv)
+{
+	pci_location_t loc;
+	pci_config_t config;
+	uint32_t offset;
+	unsigned int i;
+	int ret;
+	
+	if (argc < 5) {
+		return -1;
+	}
+	
+	if (!strcmp(argv[2].str, "dump")) {
+		loc.bus = atoui(argv[3].str);
+		loc.dev_fn = atoui(argv[4].str);
+		
+		for (i=0; i < sizeof(pci_config_t); i++) {
+			ret = pci_read_config_byte(&loc, i, (uint8_t *) &config + i);
+			if (ret != _PCI_SUCCESSFUL) goto error;
+		}
+		
+		printf("Device at %02x:%02x vendor id=%04x device id=%04x\n", loc.bus,
+			loc.dev_fn, config.vendor_id, config.device_id);
+		printf("command=%04x status=%04x pi=%02x sub cls=%02x base cls=%02x\n",
+			config.command, config.status, config.program_interface,
+			config.sub_class, config.base_class);
+		
+		for (i=0; i < 6; i+=2) {
+			printf("bat%d=%08x  bat%d=%08x\n", i, config.base_addresses[i],
+				i+1, config.base_addresses[i+1]);
+		}
+	} else if (!strcmp(argv[2].str, "rb") || !strcmp(argv[2].str, "rh") || !strcmp(argv[2].str, "rw")) {
+		if (argc != 6) {
+			return -1;
+		}
+		
+		loc.bus = atoui(argv[3].str);
+		loc.dev_fn = atoui(argv[4].str);
+		offset = atoui(argv[5].str);
+		
+		switch (argv[2].str[1]) {
+			case 'b': {
+				uint8_t value;
+				ret = pci_read_config_byte(&loc, offset, &value);
+				if (ret != _PCI_SUCCESSFUL) goto error;
+				
+				printf("byte at device %02x:%02x config offset %04x: %02x\n", loc.bus, loc.dev_fn, offset, value);
+			}
+			break;
+			
+			case 'h': {
+				uint16_t value;
+				ret = pci_read_config_half(&loc, offset, &value);
+				if (ret != _PCI_SUCCESSFUL) goto error;
+				
+				printf("half at device %02x:%02x config offset %04x: %04x\n", loc.bus, loc.dev_fn, offset, value);
+			}
+			break;
+			
+			case 'w': {
+				uint32_t value;
+				ret = pci_read_config_word(&loc, offset, &value);
+				if (ret != _PCI_SUCCESSFUL) goto error;
+				
+				printf("word at device %02x:%02x config offset %04x: %08x\n", loc.bus, loc.dev_fn, offset, value);
+			}
+			break;
+		}
+	} else if (!strcmp(argv[2].str, "mb") || !strcmp(argv[2].str, "mh") || !strcmp(argv[2].str, "mw")) {
+		if (argc != 7) {
+			return -1;
+		}
+		
+		loc.bus = atoui(argv[3].str);
+		loc.dev_fn = atoui(argv[4].str);
+		offset = atoui(argv[5].str);
+		
+		switch (argv[2].str[1]) {
+			case 'b': {
+				uint8_t value = atoui(argv[6].str);
+				ret = pci_write_config_byte(&loc, offset, value);
+				if (ret != _PCI_SUCCESSFUL) goto error;
+				
+				printf("byte to device %02x:%02x config offset %04x: %02x\n", loc.bus, loc.dev_fn, offset, value);
+			}
+			break;
+			
+			case 'h': {
+				uint16_t value = atoui(argv[6].str);
+				ret = pci_write_config_half(&loc, offset, value);
+				if (ret != _PCI_SUCCESSFUL) goto error;
+				
+				printf("half to device %02x:%02x config offset %04x: %04x\n", loc.bus, loc.dev_fn, offset, value);
+			}
+			break;
+			
+			case 'w': {
+				uint32_t value = atoui(argv[6].str);
+				ret = pci_write_config_word(&loc, offset, value);
+				if (ret != _PCI_SUCCESSFUL) goto error;
+				
+				printf("word to device %02x:%02x config offset %04x: %08x\n", loc.bus, loc.dev_fn, offset, value);
+			}
+			break;
+		}
+	} else {
+		return -1;
+	}
+	
+	return 0;
+
+error:
+	printf("Error while reading PCI config space: %02x\n", ret);
+	return -2;
+}
+
+static int pci_cmd(int argc, const cmd_args *argv)
+{
+	if (argc < 2) {
+		printf("pci commands:\n");
+usage:
+		printf("%s list\n", argv[0].str);
+		printf("%s config dump <bus> <devfn>\n", argv[0].str);
+		printf("%s config <rb|rh|rw> <bus> <devfn> <offset>\n", argv[0].str);
+		printf("%s config <mb|mh|mw> <bus> <devfn> <offset> <value>\n", argv[0].str);
+		goto out;
+	}
+	
+	if (!strcmp(argv[1].str, "list")) {
+		pci_list();
+	} else if (!strcmp(argv[1].str, "config")) {
+		if (pci_config(argc, argv)) {
+			goto usage;
+		}
+	} else {
+		goto usage;
+	}
+
+out:
+	return 0;
+}
+
+STATIC_COMMAND_START
+{ "pci", "pci toolbox", &pci_cmd },
+STATIC_COMMAND_END(pcitests);
+
+#endif
+
+APP_START(pcitests)
+APP_END
+
diff --git a/app/pcitests/rules.mk b/app/pcitests/rules.mk
new file mode 100644
index 0000000..a72aa49
--- /dev/null
+++ b/app/pcitests/rules.mk
@@ -0,0 +1,6 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+#INCLUDES += -I$(LOCAL_DIR)/include
+
+OBJS += \
+	$(LOCAL_DIR)/pci_tests.o 
diff --git a/include/dev/pci.h b/include/dev/pci.h
new file mode 100644
index 0000000..4ba644e
--- /dev/null
+++ b/include/dev/pci.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef __PCI_H
+#define __PCI_H
+
+#include <sys/types.h>
+#include <compiler.h>
+
+/*
+ * PCI access return codes
+ */
+#define _PCI_SUCCESSFUL				0x00
+#define _PCI_FUNC_NOT_SUPPORTED		0x81
+#define _PCI_BAD_VENDOR_ID			0x83
+#define _PCI_DEVICE_NOT_FOUND		0x86
+#define _PCI_BAD_REGISTER_NUMBER	0x87
+#define _PCI_SET_FAILED				0x88
+#define _PCI_BUFFER_TOO_SMALL		0x89
+
+/*
+ * PCI configuration space offsets
+ */
+#define PCI_CONFIG_VENDOR_ID		0x00
+#define PCI_CONFIG_DEVICE_ID		0x02
+#define PCI_CONFIG_COMMAND			0x04
+#define PCI_CONFIG_STATUS			0x06
+#define PCI_CONFIG_REVISION_ID		0x08
+#define PCI_CONFIG_CLASS_CODE		0x09
+#define PCI_CONFIG_CACHE_LINE_SIZE	0x0c
+#define PCI_CONFIG_LATENCY_TIMER	0x0d
+#define PCI_CONFIG_HEADER_TYPE		0x0e
+#define PCI_CONFIG_BIST				0x0f
+#define PCI_CONFIG_BASE_ADDRESSES	0x10
+#define PCI_CONFIG_CARDBUS_CIS_PTR	0x28
+#define PCI_CONFIG_SUBSYS_VENDOR_ID	0x2c
+#define PCI_CONFIG_SUBSYS_ID		0x2e
+#define PCI_CONFIG_EXP_ROM_ADDRESS	0x30
+#define PCI_CONFIG_CAPABILITIES		0x34
+#define PCI_CONFIG_INTERRUPT_LINE	0x3c
+#define PCI_CONFIG_INTERRUPT_PIN	0x3d
+#define PCI_CONFIG_MIN_GRANT		0x3e
+#define PCI_CONFIG_MAX_LATENCY		0x3f
+
+/*
+ * PCI header type register bits
+ */
+#define PCI_HEADER_TYPE_MASK		0x7f
+#define PCI_HEADER_TYPE_MULTI_FN	0x80
+
+/*
+ * PCI header types
+ */
+#define PCI_HEADER_TYPE_STANDARD	0x00
+#define PCI_HEADER_TYPE_PCI_BRIDGE	0x01
+#define PCI_HEADER_TYPE_CARD_BUS	0x02
+
+/*
+ * PCI command register bits
+ */
+#define PCI_COMMAND_IO_EN			0x0001
+#define PCI_COMMAND_MEM_EN			0x0002
+#define PCI_COMMAND_BUS_MASTER_EN	0x0004
+#define PCI_COMMAND_SPECIAL_EN		0x0008
+#define PCI_COMMAND_MEM_WR_INV_EN	0x0010
+#define PCI_COMMAND_PAL_SNOOP_EN	0x0020
+#define PCI_COMMAND_PERR_RESP_EN	0x0040
+#define PCI_COMMAND_AD_STEP_EN		0x0080
+#define PCI_COMMAND_SERR_EN			0x0100
+#define PCI_COMMAND_FAST_B2B_EN		0x0200
+
+/*
+ * PCI status register bits
+ */
+#define PCI_STATUS_NEW_CAPS			0x0010
+#define PCI_STATUS_66_MHZ			0x0020
+#define PCI_STATUS_FAST_B2B			0x0080
+#define PCI_STATUS_MSTR_PERR		0x0100
+#define PCI_STATUS_DEVSEL_MASK		0x0600
+#define PCI_STATUS_TARG_ABORT_SIG	0x0800
+#define PCI_STATUS_TARG_ABORT_RCV	0x1000
+#define PCI_STATUS_MSTR_ABORT_RCV	0x2000
+#define PCI_STATUS_SERR_SIG			0x4000
+#define PCI_STATUS_PERR				0x8000
+
+typedef struct {
+	uint16_t vendor_id;
+	uint16_t device_id;
+	uint16_t command;
+	uint16_t status;
+	uint8_t revision_id_0;
+	uint8_t program_interface;
+	uint8_t sub_class;
+	uint8_t base_class;
+	uint8_t cache_line_size;
+	uint8_t latency_timer;
+	uint8_t header_type;
+	uint8_t bist;
+	uint32_t base_addresses[6];
+	uint32_t cardbus_cis_ptr;
+	uint16_t subsystem_vendor_id;
+	uint16_t subsystem_id;
+	uint32_t expansion_rom_address;
+	uint8_t capabilities_ptr;
+	uint8_t reserved_0[3];
+	uint32_t reserved_1;
+	uint8_t interrupt_line;
+	uint8_t interrupt_pin;
+	uint8_t min_grant;
+	uint8_t max_latency;
+} __PACKED pci_config_t;
+
+/*
+ * PCI address structure
+ */
+typedef struct
+{
+	uint8_t bus;
+	uint8_t dev_fn;
+} pci_location_t;
+
+typedef struct {
+	uint8_t id;
+	uint8_t next;
+} __PACKED pci_capability_t;
+
+typedef struct {
+	uint8_t bus;
+	uint8_t device;
+	uint8_t link_int_a;
+	uint16_t irq_int_a;
+	uint8_t link_int_b;
+	uint16_t irq_int_b;
+	uint8_t link_int_c;
+	uint16_t irq_int_c;
+	uint8_t link_int_d;
+	uint16_t irq_int_d;
+	uint8_t slot;
+	uint8_t reserved;
+} __PACKED irq_routing_entry;
+
+void pci_init(void);
+
+int pci_get_last_bus(void);
+
+int pci_find_pci_device(pci_location_t *state, uint16_t device_id, uint16_t vendor_id, uint16_t index);
+int pci_find_pci_class_code(pci_location_t *state, uint32_t class_code, uint16_t index);
+ 
+int pci_read_config_byte(const pci_location_t *state, uint32_t reg, uint8_t *value);
+int pci_read_config_half(const pci_location_t *state, uint32_t reg, uint16_t *value);
+int pci_read_config_word(const pci_location_t *state, uint32_t reg, uint32_t *value);
+
+int pci_write_config_byte(const pci_location_t *state, uint32_t reg, uint8_t value);
+int pci_write_config_half(const pci_location_t *state, uint32_t reg, uint16_t value);
+int pci_write_config_word(const pci_location_t *state, uint32_t reg, uint32_t value);
+	
+int pci_get_irq_routing_options(irq_routing_entry *entries, uint16_t *count, uint16_t *pci_irqs);
+int pci_set_irq_hw_int(const pci_location_t *state, uint8_t int_pin, uint8_t irq);
+
+#endif
diff --git a/platform/pc/pci.c b/platform/pc/pci.c
new file mode 100644
index 0000000..6175723
--- /dev/null
+++ b/platform/pc/pci.c
@@ -0,0 +1,576 @@
+/*
+ * Copyright (c) 2009 Corey Tabaka
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <debug.h>
+#include <err.h>
+#include <stdlib.h>
+#include <string.h>
+#include <kernel/thread.h>
+#include <arch/x86/descriptor.h>
+#include <dev/pci.h>
+
+static int last_bus = 0;
+
+typedef struct {
+	uint16_t size;
+	void *offset;
+	uint16_t selector;
+} __PACKED irq_routing_options_t;
+
+static int pci_type1_detect(void);
+static int pci_bios_detect(void);
+
+int pci_get_last_bus(void)
+{
+	return last_bus;
+}
+
+/*
+ * pointers to installed PCI routines
+ */
+int (*g_pci_find_pci_device)(pci_location_t *state, uint16_t device_id, uint16_t vendor_id, uint16_t index);
+int (*g_pci_find_pci_class_code)(pci_location_t *state, uint32_t class_code, uint16_t index);
+ 
+int (*g_pci_read_config_byte)(const pci_location_t *state, uint32_t reg, uint8_t *value);
+int (*g_pci_read_config_half)(const pci_location_t *state, uint32_t reg, uint16_t *value);
+int (*g_pci_read_config_word)(const pci_location_t *state, uint32_t reg, uint32_t *value);
+
+int (*g_pci_write_config_byte)(const pci_location_t *state, uint32_t reg, uint8_t value);
+int (*g_pci_write_config_half)(const pci_location_t *state, uint32_t reg, uint16_t value);
+int (*g_pci_write_config_word)(const pci_location_t *state, uint32_t reg, uint32_t value);
+	
+int (*g_pci_get_irq_routing_options)(irq_routing_options_t *options, uint16_t *pci_irqs);
+int (*g_pci_set_irq_hw_int)(const pci_location_t *state, uint8_t int_pin, uint8_t irq);
+
+
+int pci_find_pci_device(pci_location_t *state, uint16_t device_id, uint16_t vendor_id, uint16_t index)
+{
+	enter_critical_section();
+	
+	int res = g_pci_find_pci_device(state, device_id, vendor_id, index);
+	
+	exit_critical_section();
+	
+	return res;
+}
+
+int pci_find_pci_class_code(pci_location_t *state, uint32_t class_code, uint16_t index)
+{
+	enter_critical_section();
+	
+	int res = g_pci_find_pci_class_code(state, class_code, index);
+	
+	exit_critical_section();
+	
+	return res;
+}
+ 
+int pci_read_config_byte(const pci_location_t *state, uint32_t reg, uint8_t *value)
+{
+	enter_critical_section();
+	
+	int res = g_pci_read_config_byte(state, reg, value);
+	
+	exit_critical_section();
+	
+	return res;
+}
+int pci_read_config_half(const pci_location_t *state, uint32_t reg, uint16_t *value)
+{
+	enter_critical_section();
+	
+	int res = g_pci_read_config_half(state, reg, value);
+	
+	exit_critical_section();
+	
+	return res;
+}
+
+int pci_read_config_word(const pci_location_t *state, uint32_t reg, uint32_t *value)
+{
+	enter_critical_section();
+	
+	int res = g_pci_read_config_word(state, reg, value);
+	
+	exit_critical_section();
+	
+	return res;
+}
+
+int pci_write_config_byte(const pci_location_t *state, uint32_t reg, uint8_t value)
+{
+	enter_critical_section();
+	
+	int res = g_pci_write_config_byte(state, reg, value);
+	
+	exit_critical_section();
+	
+	return res;
+}
+
+int pci_write_config_half(const pci_location_t *state, uint32_t reg, uint16_t value)
+{
+	enter_critical_section();
+	
+	int res = g_pci_write_config_half(state, reg, value);
+	
+	exit_critical_section();
+	
+	return res;
+}
+
+int pci_write_config_word(const pci_location_t *state, uint32_t reg, uint32_t value)
+{
+	enter_critical_section();
+	
+	int res = g_pci_write_config_word(state, reg, value);
+	
+	exit_critical_section();
+	
+	return res;
+}
+
+	
+int pci_get_irq_routing_options(irq_routing_entry *entries, uint16_t *count, uint16_t *pci_irqs)
+{
+	enter_critical_section();
+	
+	irq_routing_options_t options;
+	options.size = sizeof(irq_routing_entry) * *count;
+	options.selector = DATA_SELECTOR;
+	options.offset = entries;
+	
+	int res = g_pci_get_irq_routing_options(&options, pci_irqs);
+	
+	*count = options.size / sizeof(irq_routing_entry);
+	
+	exit_critical_section();
+	
+	return res;
+}
+
+int pci_set_irq_hw_int(const pci_location_t *state, uint8_t int_pin, uint8_t irq)
+{
+	enter_critical_section();
+	
+	int res = g_pci_set_irq_hw_int(state, int_pin, irq);
+	
+	exit_critical_section();
+	
+	return res;
+}
+
+void pci_init(void)
+{
+	if (!pci_bios_detect()) {
+		dprintf(INFO, "pci bios functions installed\n");
+		dprintf(INFO, "last pci bus is %d\n", last_bus);
+	}
+}
+
+#define PCIBIOS_PRESENT					0xB101
+#define PCIBIOS_FIND_PCI_DEVICE			0xB102
+#define PCIBIOS_FIND_PCI_CLASS_CODE		0xB103
+#define PCIBIOS_GENERATE_SPECIAL_CYCLE	0xB106
+#define PCIBIOS_READ_CONFIG_BYTE		0xB108
+#define PCIBIOS_READ_CONFIG_WORD		0xB109
+#define PCIBIOS_READ_CONFIG_DWORD		0xB10A
+#define PCIBIOS_WRITE_CONFIG_BYTE		0xB10B
+#define PCIBIOS_WRITE_CONFIG_WORD		0xB10C
+#define PCIBIOS_WRITE_CONFIG_DWORD		0xB10D
+#define PCIBIOS_GET_IRQ_ROUTING_OPTIONS	0xB10E
+#define PCIBIOS_PCI_SET_IRQ_HW_INT		0xB10F
+
+#define PCIBIOS_SUCCESSFUL				0x00
+#define PCIBIOS_FUNC_NOT_SUPPORTED		0x81
+#define PCIBIOS_BAD_VENDOR_ID			0x83
+#define PCIBIOS_DEVICE_NOT_FOUND		0x86
+#define PCIBIOS_BAD_REGISTER_NUMBER		0x87
+#define PCIBIOS_SET_FAILED				0x88
+#define PCIBIOS_BUFFER_TOO_SMALL		0x89
+
+/*
+ * far call structure used by BIOS32 routines
+ */
+static struct {
+	uint32_t offset;
+	uint16_t selector;
+} __PACKED bios32_entry;
+
+/*
+ * BIOS32 entry header
+ */
+typedef struct {
+	uint8_t magic[4];	// "_32_"
+	void * entry;		// entry point
+	uint8_t revision;
+	uint8_t length;
+	uint8_t checksum;
+	uint8_t reserved[5];
+} __PACKED pci_bios_info;
+
+/*
+ * scan for pci bios
+ */
+static const char * pci_bios_magic = "_32_";
+static pci_bios_info *find_pci_bios_info(void) {
+	uint32_t *head = (uint32_t *) 0x000e0000;
+	int8_t sum, *b;
+	uint i;
+	
+	while (head < (uint32_t *) 0x000ffff0) {
+		if (*head == *(uint32_t *) pci_bios_magic) {
+			// perform the checksum
+			sum = 0;
+			b = (int8_t *) head;
+			for (i=0; i < sizeof(pci_bios_info); i++) {
+				sum += b[i];
+			}
+			
+			if (sum == 0) {
+				return (pci_bios_info *) head;
+			}
+		}
+		
+		head += 4;
+	}
+	
+	return NULL;
+}
+
+/*
+ * local BIOS32 PCI routines
+ */
+static int bios_find_pci_device(pci_location_t *state, uint16_t device_id, uint16_t vendor_id, uint16_t index)
+{
+	uint32_t bx, ret;
+
+	__asm__(
+		"lcall *(%%edi)		\n\t"
+		"jc 1f				\n\t"
+		"xor %%ah,%%ah		\n"
+		"1:"
+		: "=b"(bx),
+		  "=a"(ret)
+		: "1"(PCIBIOS_FIND_PCI_DEVICE),
+		  "c"(device_id),
+		  "d"(vendor_id),
+		  "S"(index),
+		  "D"(&bios32_entry));
+	
+	state->bus = bx >> 8;
+	state->dev_fn = bx & 0xFF;
+	
+	ret >>= 8;
+	return ret & 0xFF;
+}
+
+static int bios_find_pci_class_code(pci_location_t *state, uint32_t class_code, uint16_t index)
+{
+	uint32_t bx, ret;
+
+	__asm__(
+		"lcall *(%%edi)			\n\t"
+		"jc 1f					\n\t"
+		"xor %%ah,%%ah			\n"
+		"1:"
+		: "=b"(bx),
+		  "=a"(ret)
+		: "1"(PCIBIOS_FIND_PCI_CLASS_CODE),
+		  "c"(class_code),
+		  "S"(index),
+		  "D"(&bios32_entry));
+	
+	state->bus = bx >> 8;
+	state->dev_fn = bx & 0xFF;
+	
+	ret >>= 8;
+	return ret & 0xFF;
+}
+
+
+static int bios_read_config_byte(const pci_location_t *state, uint32_t reg, uint8_t *value)
+{
+	uint32_t bx, ret;
+
+	bx = state->bus;
+	bx <<= 8;
+	bx |= state->dev_fn;
+	__asm__(
+		"lcall *(%%esi)			\n\t"
+		"jc 1f					\n\t"
+		"xor %%ah,%%ah			\n"
+		"1:"
+		: "=c"(*value),
+		  "=a"(ret)
+		: "1"(PCIBIOS_READ_CONFIG_BYTE),
+		  "b"(bx),
+		  "D"(reg),
+		  "S"(&bios32_entry));
+	ret >>= 8;
+	return ret & 0xFF;
+}
+
+static int bios_read_config_half(const pci_location_t *state, uint32_t reg, uint16_t *value)
+{
+	uint32_t bx, ret;
+
+	bx = state->bus;
+	bx <<= 8;
+	bx |= state->dev_fn;
+	__asm__(
+		"lcall *(%%esi)			\n\t"
+		"jc 1f					\n\t"
+		"xor %%ah,%%ah			\n"
+		"1:"
+		: "=c"(*value),
+		  "=a"(ret)
+		: "1"(PCIBIOS_READ_CONFIG_WORD),
+		  "b"(bx),
+		  "D"(reg),
+		  "S"(&bios32_entry));
+	ret >>= 8;
+	return ret & 0xFF;
+}
+
+static int bios_read_config_word(const pci_location_t *state, uint32_t reg, uint32_t *value)
+{
+	uint32_t bx, ret;
+
+	bx = state->bus;
+	bx <<= 8;
+	bx |= state->dev_fn;
+	__asm__(
+		"lcall *(%%esi)			\n\t"
+		"jc 1f					\n\t"
+		"xor %%ah,%%ah			\n"
+		"1:"
+		: "=c"(*value),
+		  "=a"(ret)
+		: "1"(PCIBIOS_READ_CONFIG_DWORD),
+		  "b"(bx),
+		  "D"(reg),
+		  "S"(&bios32_entry));
+	ret >>= 8;
+	return ret & 0xFF;
+}
+
+static int bios_write_config_byte(const pci_location_t *state, uint32_t reg, uint8_t value)
+{
+	uint32_t bx, ret;
+
+	bx = state->bus;
+	bx <<= 8;
+	bx |= state->dev_fn;
+	 __asm__(
+	 	"lcall *(%%esi)			\n\t"
+		"jc 1f					\n\t"
+		"xor %%ah,%%ah			\n"
+		"1:"
+		: "=a"(ret)
+		: "0"(PCIBIOS_WRITE_CONFIG_BYTE),
+		  "c"(value),
+		  "b"(bx),
+		  "D"(reg),
+		  "S"(&bios32_entry));
+	ret >>= 8;
+	return ret & 0xFF;
+}
+
+static int bios_write_config_half(const pci_location_t *state, uint32_t reg, uint16_t value)
+{
+	uint32_t bx, ret;
+
+	bx = state->bus;
+	bx <<= 8;
+	bx |= state->dev_fn;
+	 __asm__(
+	 	"lcall *(%%esi)	\n\t"
+		"jc 1f					\n\t"
+		"xor %%ah,%%ah			\n"
+		"1:"
+		: "=a"(ret)
+		: "0"(PCIBIOS_WRITE_CONFIG_WORD),
+		  "c"(value),
+		  "b"(bx),
+		  "D"(reg),
+		  "S"(&bios32_entry));
+	ret >>= 8;
+	return ret & 0xFF;
+}
+
+static int bios_write_config_word(const pci_location_t *state, uint32_t reg, uint32_t value)
+{
+	uint32_t bx, ret;
+
+	bx = state->bus;
+	bx <<= 8;
+	bx |= state->dev_fn;
+	 __asm__(
+	 	"lcall *(%%esi)			\n\t"
+		"jc 1f					\n\t"
+		"xor %%ah,%%ah			\n"
+		"1:"
+		: "=a"(ret)
+		: "0"(PCIBIOS_WRITE_CONFIG_DWORD),
+		  "c"(value),
+		  "b"(bx),
+		  "D"(reg),
+		  "S"(&bios32_entry));
+	ret >>= 8;
+	return ret & 0xFF;
+}
+
+static int bios_get_irq_routing_options(irq_routing_options_t *route_buffer, uint16_t *pciIrqs)
+{
+	uint32_t ret;
+	
+	__asm__(
+		"lcall *(%%esi)			\n\t"
+		"jc 1f					\n\t"
+		"xor %%ah,%%ah			\n"
+		"1:"
+		: "=b"(*pciIrqs),
+		  "=a"(ret)
+		: "1"(PCIBIOS_GET_IRQ_ROUTING_OPTIONS),
+		  "b"(0),
+		  "D"(route_buffer),
+		  "S"(&bios32_entry));
+	ret >>= 8;
+	return ret & 0xff;
+}
+
+static int bios_set_irq_hw_int(const pci_location_t *state, uint8_t int_pin, uint8_t irq)
+{
+	uint32_t bx, cx, ret;
+
+	bx = state->bus;
+	bx <<= 8;
+	bx |= state->dev_fn;
+	cx = irq;
+	cx <<= 8;
+	cx |= int_pin;
+	__asm__(
+		"lcall *(%%esi)			\n\t"
+		"jc 1f					\n\t"
+		"xor %%ah,%%ah			\n"
+		"1:"
+		: "=a"(ret)
+		: "0"(PCIBIOS_PCI_SET_IRQ_HW_INT),
+		  "b"(bx),
+		  "c"(cx),
+		  "S"(&bios32_entry));
+	ret >>= 8;
+	return ret & 0xFF;
+}
+
+static const char *pci_signature = "PCI ";
+static int pci_bios_detect(void) {
+	pci_bios_info * pci = find_pci_bios_info();
+	
+	if (pci != NULL) {
+		/*printf("Found PCI structure at %08x\n", (uint32_t) pci);
+			
+		printf("\nPCI header info:\n");
+		printf("%c%c%c%c\n", pci->magic[0], pci->magic[1], pci->magic[2],
+			pci->magic[3]);
+		printf("%08x\n", (uint32_t) pci->entry);
+		printf("%d\n", pci->length * 16);
+		printf("%d\n", pci->checksum);*/
+		
+		uint32_t adr, temp, len;
+		uint8_t err;
+		
+		bios32_entry.offset = (uint32_t) pci->entry;
+		bios32_entry.selector = CODE_SELECTOR;
+		
+		__asm__(
+			"lcall *(%%edi)"
+			: "=a"(err),	/* AL out=status */
+			  "=b"(adr),	/* EBX out=code segment base adr */
+			  "=c"(len),	/* ECX out=code segment size */
+			  "=d"(temp)	/* EDX out=entry pt offset in code */
+			: "0"(0x49435024),/* EAX in=service="$PCI" */
+			  "1"(0),	/* EBX in=0=get service entry pt */
+			  "D"(&bios32_entry)
+		);
+		
+		if (err == 0x80) {
+			dprintf(INFO, "BIOS32 found, but no PCI BIOS\n");
+			return -1;
+		}
+		
+		if (err != 0) {
+			dprintf(INFO, "BIOS32 call to locate PCI BIOS returned %x\n", err);
+			return -1;
+		}
+		
+		bios32_entry.offset = adr + temp;
+		
+		// now call PCI_BIOS_PRESENT to get version, hw mechanism, and last bus
+		uint16_t present, version, busses;
+		uint32_t signature;
+		__asm__(
+			"lcall *(%%edi)		\n\t"
+			"jc 1f				\n\t"
+			"xor %%ah,%%ah		\n"
+			"1:"
+			: "=a"(present),
+			  "=b"(version),
+			  "=c"(busses),
+			  "=d"(signature)
+			: "0"(PCIBIOS_PRESENT),
+			  "S"(&bios32_entry)
+		);
+		
+		if (present & 0xff00) {
+			dprintf(INFO, "PCI_BIOS_PRESENT call returned ah=%02x\n", present >> 8);
+			return -1;
+		}
+		
+		if (signature != *(uint32_t *)pci_signature) {
+			dprintf(INFO, "PCI_BIOS_PRESENT call returned edx=%08x\n", signature);
+			return -1;
+		}
+		
+		//dprintf(DEBUG, "busses=%04x\n", busses);
+		last_bus = busses & 0xff;
+		
+		g_pci_find_pci_device = bios_find_pci_device;
+		g_pci_find_pci_class_code = bios_find_pci_class_code;
+		
+		g_pci_read_config_word = bios_read_config_word;
+		g_pci_read_config_half = bios_read_config_half;
+		g_pci_read_config_byte = bios_read_config_byte;
+	
+		g_pci_write_config_word = bios_write_config_word;
+		g_pci_write_config_half = bios_write_config_half;
+		g_pci_write_config_byte = bios_write_config_byte;
+		
+		g_pci_get_irq_routing_options = bios_get_irq_routing_options;
+		g_pci_set_irq_hw_int = bios_set_irq_hw_int;
+		
+		return 0;
+	}
+	
+	return -1;
+}
diff --git a/platform/pc/platform.c b/platform/pc/platform.c
index df0189f..ca5a946 100644
--- a/platform/pc/platform.c
+++ b/platform/pc/platform.c
@@ -29,6 +29,7 @@
 #include <platform/multiboot.h>
 #include <platform/console.h>
 #include <platform/keyboard.h>
+#include <dev/pci.h>
 
 extern multiboot_info_t *_multiboot_info;
 extern unsigned int _heap_end;
@@ -91,5 +92,6 @@
 {
 	platform_init_keyboard();
 	
+	pci_init();
 }
 
diff --git a/platform/pc/rules.mk b/platform/pc/rules.mk
index bedf6c2..e083a80 100644
--- a/platform/pc/rules.mk
+++ b/platform/pc/rules.mk
@@ -12,10 +12,8 @@
 	$(LOCAL_DIR)/timer.o \
 	$(LOCAL_DIR)/debug.o \
 	$(LOCAL_DIR)/console.o \
-	$(LOCAL_DIR)/keyboard.o
-
-MEMBASE := 0x0
-MEMSIZE := 0x400000	# 4MB
+	$(LOCAL_DIR)/keyboard.o \
+	$(LOCAL_DIR)/pci.o
 
 LINKER_SCRIPT += \
 	$(BUILDDIR)/kernel.ld
diff --git a/project/pc-x86-test.mk b/project/pc-x86-test.mk
index c5ff491..d0b1937 100644
--- a/project/pc-x86-test.mk
+++ b/project/pc-x86-test.mk
@@ -5,7 +5,8 @@
 TARGET := pc-x86
 MODULES += \
 	app/tests \
-	app/shell
+	app/shell \
+	app/pcitests
 
 # extra rules to copy the pc-x86.conf file to the build dir
 #$(BUILDDIR)/pc-x86.conf: $(LOCAL_DIR)/pc-x86.conf