aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile6
-rw-r--r--code32seg.c73
-rw-r--r--entry.S45
-rw-r--r--include/bios.h30
-rw-r--r--pci.c20
5 files changed, 169 insertions, 5 deletions
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 += -mstringop-strategy=rep_byte -minline-all-stringops
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 @@ ENTRY(bios_int15)
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 @@ struct biosregs {
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 bool setup_mmconfig(void);
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 @@ static void do_setup_pci_bus(void)
}
}
+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 @@ void setup_pci(void)
panic();
do_setup_pci_bus();
+ setup_bios32();
}