implement PCIBIOS specification

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
diff --git a/Makefile b/Makefile
index 1ac923d..b96dd09 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
 obj-y = code16.o entry.o main.o string.o printf.o cstart.o fw_cfg.o
-obj-y += linuxboot.o malloc.o pflash.o tables.o hwsetup.o pci.o
+obj-y += linuxboot.o malloc.o pflash.o tables.o hwsetup.o pci.o code32seg.o
 
 all-y = bios.bin
 all: $(all-y)
@@ -16,13 +16,15 @@
 BIOS_CFLAGS += -Iinclude
 BIOS_CFLAGS += -fno-pic
 
+code32seg.o-cflags = -fno-jump-tables
+
 dummy := $(shell mkdir -p .deps)
 autodepend-flags = -MMD -MF .deps/cc-$(patsubst %/,%,$(dir $*))-$(notdir $*).d
 -include .deps/*.d
 
 .PRECIOUS: %.o
 %.o: %.c
-	$(CC) $(CFLAGS) $(BIOS_CFLAGS) -c -s $< -o $@
+	$(CC) $(CFLAGS) $(BIOS_CFLAGS) $($@-cflags) -c -s $< -o $@
 %.o: %.S
 	$(CC) $(CFLAGS) $(BIOS_CFLAGS) -c -s $< -o $@
 
diff --git a/code32seg.c b/code32seg.c
new file mode 100644
index 0000000..213bf44
--- /dev/null
+++ b/code32seg.c
@@ -0,0 +1,73 @@
+#include <stddef.h>
+#include "bios.h"
+#include "pci.h"
+#include "processor-flags.h"
+
+#define PCI_FUNC_NOT_SUPPORTED 0x81
+#define PCI_BAD_VENDOR_ID      0x83
+#define PCI_DEVICE_NOT_FOUND   0x86
+#define PCI_BUFFER_TOO_SMALL   0x89
+
+/*
+ * The PCIBIOS handler must be position independent.  To read a flat pointer,
+ * we use the instruction pointer to retrieve the address corresponding to
+ * physical address 0 (i.e., what Linux calls PAGE_OFFSET).
+ */
+
+static inline void *from_flat_ptr(void *p)
+{
+	return p + pic_base();
+}
+
+#define FLAT_VAR(x)  (*(typeof(&(x))) from_flat_ptr(&(x)))
+
+bioscall void pcibios_handler(struct bios32regs *args)
+{
+	switch (args->eax) {
+		/* discovery */
+	case 0xb101:
+		args->eax = 0x01;
+		args->ebx = 0x210;
+		args->ecx = FLAT_VAR(max_bus);
+		args->edx = 0x20494350;
+		goto success;
+
+		/* config space access */
+	case 0xb108:
+		args->ecx = pci_config_readb(args->ebx, args->edi);
+		goto success;
+	case 0xb109:
+		args->ecx = pci_config_readw(args->ebx, args->edi);
+		goto success;
+	case 0xb10a:
+		args->ecx = pci_config_readl(args->ebx, args->edi);
+		goto success;
+	case 0xb10b:
+		pci_config_writeb(args->ebx, args->edi, args->ecx);
+		goto success;
+	case 0xb10c:
+		pci_config_writew(args->ebx, args->edi, args->ecx);
+		goto success;
+	case 0xb10d:
+		pci_config_writel(args->ebx, args->edi, args->ecx);
+		goto success;
+
+		/* find device id, find class code */
+	case 0xb102:
+	case 0xb103:
+		args->eax &= ~0xff00;
+		args->eax |= PCI_DEVICE_NOT_FOUND << 8;
+		break;
+
+	default:
+		args->eax &= ~0xff00;
+		args->eax |= PCI_FUNC_NOT_SUPPORTED << 8;
+		break;
+	}
+	args->eflags |= X86_EFLAGS_CF;
+	return;
+
+success:
+	/* On entry, CF=0 */
+	args->eax &= ~0xff00; /* clear ah */
+}
diff --git a/entry.S b/entry.S
index ecd5fae..31d840b 100644
--- a/entry.S
+++ b/entry.S
@@ -91,3 +91,48 @@
 
 	IRET
 ENTRY_END(bios_int15)
+
+	.code32
+ENTRY(pcibios_entry)
+	clc
+	pushfl
+	SAVE_BIOSREGS
+
+	movl	%esp, %eax
+	call	pcibios_handler
+
+	RESTORE_BIOSREGS
+	popfl
+	lretl
+ENTRY_END(pcibios_entry)
+
+ENTRY(bios32_entry)
+	pushfl
+	testl	%ebx, %ebx	   /* BIOS32 service directory? */
+	jnz	2f
+	cmp	$0x49435024, %eax  /* "$PCI"? */
+	movb	$0x80, %al	   /* service not present */
+	jne	1f
+	xorl	%ebx, %ebx	   /* fill in base/length/entry */
+	movl	$(1 << 20), %ecx
+	movl	$pcibios_entry, %edx
+	movb	$0x00, %al	   /* service present */
+1:
+	popfl
+	lretl
+2:
+	movb	$0x81, %al	   /* unimplemented function */
+	popfl
+	lretl
+ENTRY_END(bios32_entry)
+
+ENTRY(pic_base)
+	call 1f
+2:
+	ret
+1:
+	popl	%eax
+	pushl	%eax
+	subl	$2b, %eax
+	ret			   /* return to 2b */
+ENTRY_END(pic_base)
diff --git a/include/bios.h b/include/bios.h
index 12087f2..3e4cdee 100644
--- a/include/bios.h
+++ b/include/bios.h
@@ -25,18 +25,43 @@
 	uint32_t			ds;
 	uint32_t			es;
 	uint32_t			fs;
-	uint32_t			eip;
+	uint16_t			ip;
+	uint16_t			cs;
+	uint16_t			eflags;
+} __attribute__((packed));
+
+/*
+ * BIOS32 is called via a far call, so eflags is pushed by our
+ * entry point and lies below CS:EIP.  We do not include CS:EIP
+ * at all in this struct.
+ */
+struct bios32regs {
+	uint32_t			eax;
+	uint32_t			ebx;
+	uint32_t			ecx;
+	uint32_t			edx;
+	uint32_t			esp;
+	uint32_t			ebp;
+	uint32_t			esi;
+	uint32_t			edi;
+	uint32_t			ds;
+	uint32_t			es;
+	uint32_t			fs;
 	uint32_t			eflags;
-};
+} __attribute__((packed));
 
 extern bioscall void int10_handler(struct biosregs *regs);
 extern bioscall void int15_handler(struct biosregs *regs);
 extern bioscall void e820_query_map(struct biosregs *regs);
+extern bioscall void pcibios_handler(struct bios32regs *regs);
 
 extern void bios_intfake(void);
 extern void bios_irq(void);
 extern void bios_int10(void);
 extern void bios_int15(void);
+extern void bios32_entry(void);
+
+extern uint32_t pic_base(void);
 
 extern void setup_pci(void);
 extern void setup_hw(void);
@@ -44,6 +69,7 @@
 extern void extract_acpi(void);
 extern void boot_from_fwcfg(void);
 
+extern uint8_t max_bus;
 extern uint16_t e820_seg;
 extern uint32_t lowmem;
 
diff --git a/pci.c b/pci.c
index 1457d17..f03ff6d 100644
--- a/pci.c
+++ b/pci.c
@@ -1,11 +1,13 @@
 #include "bios.h"
 #include "ioport.h"
 #include "pci.h"
+#include <string.h>
 
 static uint16_t addend;
-static uint8_t bus, max_bus, bridge_head;
+static uint8_t bus, bridge_head;
 static bool use_i440fx_routing;
 static int bridge_count;
+uint8_t max_bus;
 
 static void do_setup_pci_bus(void);
 
@@ -143,6 +145,21 @@
 	}
 }
 
+void setup_bios32(void)
+{
+	char *bios32 = malloc_fseg_align(16, 16);
+	void *bios32_entry_ = &bios32_entry;
+	int i;
+
+	memcpy(bios32, "_32_", 4);
+	memcpy(bios32 + 4, &bios32_entry_, 4);
+	bios32[8] = 0;
+	bios32[9] = 1;
+	memset(bios32 + 10, 0, 6);
+	for (i = 0; i <= 9; i++)
+		bios32[10] -= bios32[i];
+}
+
 void setup_pci(void)
 {
 	const int bdf = 0;
@@ -156,4 +173,5 @@
 		panic();
 
 	do_setup_pci_bus();
+	setup_bios32();
 }