initial fw_cfg support

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
diff --git a/Makefile b/Makefile
index 2e8a142..fa43371 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
 obj16-y = e820.o int10.o int15.o
-obj-y = $(obj16-y) entry.o main.o string.o printf.o cstart.o
+obj-y = $(obj16-y) entry.o main.o string.o printf.o cstart.o fw_cfg.o
 all-y = bios.bin
 
 CFLAGS := -O2 -Wall -g
diff --git a/fw_cfg.c b/fw_cfg.c
new file mode 100644
index 0000000..293e4be
--- /dev/null
+++ b/fw_cfg.c
@@ -0,0 +1,50 @@
+#include "bios.h"
+#include "ioport.h"
+#include "string.h"
+#include "fw_cfg.h"
+
+struct fw_cfg_file {
+	uint32_t size;
+	uint16_t select;
+	char name[57];
+};
+
+static struct fw_cfg_file files[32];
+
+void fw_cfg_setup(void)
+{
+	int i, n;
+
+	fw_cfg_select(FW_CFG_FILE_DIR);
+	n = fw_cfg_readl_be();
+	if (n > ARRAY_SIZE(files))
+		n = ARRAY_SIZE(files);
+
+	for (i = 0; i < n; i++) {
+		files[i].size = fw_cfg_readl_be();
+		files[i].select = fw_cfg_readw_be();
+		fw_cfg_readw_be();
+		fw_cfg_read(files[i].name, sizeof(files[i].name) - 1);
+	}
+}
+
+int fw_cfg_file_id(char *name)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(files); i++)
+		if (!strcmp(name, files[i].name))
+			return i;
+
+	return -1;
+}
+
+uint32_t fw_cfg_file_size(int id)
+{
+	return files[id].size;
+}
+
+void fw_cfg_file_select(int id)
+{
+	fw_cfg_select(files[id].select);
+}
diff --git a/include/bios.h b/include/bios.h
index 7232bef..bd7f77b 100644
--- a/include/bios.h
+++ b/include/bios.h
@@ -38,6 +38,8 @@
 
 extern struct e820map e820;
 
+#define ARRAY_SIZE(x)	(sizeof(x) / sizeof(x[0]))
+
 static inline void __attribute__((noreturn)) panic(void)
 {
 	asm volatile("cli; hlt");
diff --git a/include/fw_cfg.h b/include/fw_cfg.h
new file mode 100644
index 0000000..57afa14
--- /dev/null
+++ b/include/fw_cfg.h
@@ -0,0 +1,115 @@
+#ifndef _FW_CFG_H
+#define _FW_CFG_H 1
+
+// List of QEMU fw_cfg entries.  DO NOT ADD MORE.  (All new content
+// should be passed via the fw_cfg "file" interface.)
+#define FW_CFG_SIGNATURE              0x00
+#define FW_CFG_ID                     0x01
+#define FW_CFG_UUID                   0x02
+#define FW_CFG_RAM_SIZE               0x03
+#define FW_CFG_NOGRAPHIC              0x04
+#define FW_CFG_NB_CPUS                0x05
+#define FW_CFG_MACHINE_ID             0x06
+#define FW_CFG_KERNEL_ADDR            0x07
+#define FW_CFG_KERNEL_SIZE            0x08
+#define FW_CFG_KERNEL_CMDLINE         0x09
+#define FW_CFG_INITRD_ADDR            0x0a
+#define FW_CFG_INITRD_SIZE            0x0b
+#define FW_CFG_BOOT_DEVICE            0x0c
+#define FW_CFG_NUMA                   0x0d
+#define FW_CFG_BOOT_MENU              0x0e
+#define FW_CFG_MAX_CPUS               0x0f
+#define FW_CFG_KERNEL_ENTRY           0x10
+#define FW_CFG_KERNEL_DATA            0x11
+#define FW_CFG_INITRD_DATA            0x12
+#define FW_CFG_CMDLINE_ADDR           0x13
+#define FW_CFG_CMDLINE_SIZE           0x14
+#define FW_CFG_CMDLINE_DATA           0x15
+#define FW_CFG_SETUP_ADDR             0x16
+#define FW_CFG_SETUP_SIZE             0x17
+#define FW_CFG_SETUP_DATA             0x18
+#define FW_CFG_FILE_DIR               0x19
+#define FW_CFG_FILE_FIRST             0x20
+#define FW_CFG_ARCH_LOCAL             0x8000
+#define FW_CFG_ACPI_TABLES            (FW_CFG_ARCH_LOCAL + 0)
+#define FW_CFG_SMBIOS_ENTRIES         (FW_CFG_ARCH_LOCAL + 1)
+#define FW_CFG_IRQ0_OVERRIDE          (FW_CFG_ARCH_LOCAL + 2)
+#define FW_CFG_E820_TABLE             (FW_CFG_ARCH_LOCAL + 3)
+
+#define FW_CFG_CTL			0x510
+#define FW_CFG_DATA			0x511
+
+static inline void fw_cfg_select(uint16_t f)
+{
+	outw(FW_CFG_CTL, f);
+}
+
+static inline uint32_t fw_cfg_readb(void)
+{
+	return inb(FW_CFG_DATA);
+}
+
+static inline uint32_t fw_cfg_readw_be(void)
+{
+	uint32_t val;
+	
+	val = inb(FW_CFG_DATA);
+	val = (val << 8) | inb(FW_CFG_DATA);
+	return val;
+}
+
+static inline uint32_t fw_cfg_readw_le(void)
+{
+	uint32_t val;
+
+	val = inb(FW_CFG_DATA);
+	val = (inb(FW_CFG_DATA) << 8) | val;
+	return val;
+}
+
+static inline uint32_t fw_cfg_readl_be(void)
+{
+	uint32_t val;
+	
+	val = inb(FW_CFG_DATA);
+	val = (val << 8) | inb(FW_CFG_DATA);
+	val = (val << 8) | inb(FW_CFG_DATA);
+	val = (val << 8) | inb(FW_CFG_DATA);
+	return val;
+}
+
+static inline uint32_t fw_cfg_readl_le(void)
+{
+	uint32_t val;
+
+	val = inb(FW_CFG_DATA);
+	val = (inb(FW_CFG_DATA) << 8) | val;
+	val = (inb(FW_CFG_DATA) << 16) | val;
+	val = (inb(FW_CFG_DATA) << 24) | val;
+	return val;
+}
+
+static inline void fw_cfg_read(void *buf, int len)
+{
+	insb(buf, FW_CFG_DATA, len);
+}
+
+static inline void fw_cfg_skip(int len)
+{
+	while (len--)
+		inb(FW_CFG_DATA);
+}
+
+static inline void
+fw_cfg_read_entry(void *buf, int e, int len)
+{
+	fw_cfg_select(e);
+	fw_cfg_read(buf, len);
+}
+
+void fw_cfg_setup(void);
+int fw_cfg_file_id(char *name);
+uint32_t fw_cfg_file_size(int id);
+void fw_cfg_file_select(int id);
+
+#endif
diff --git a/main.c b/main.c
index 5ef38cf..2378abb 100644
--- a/main.c
+++ b/main.c
@@ -1,7 +1,9 @@
 #include "bios.h"
+#include "e820.h"
 #include "pci.h"
 #include "string.h"
 #include "segment.h"
+#include "fw_cfg.h"
 
 #define PCI_VENDOR_ID_INTEL		0x8086
 #define PCI_DEVICE_ID_INTEL_82441	0x1237
@@ -77,13 +79,28 @@
 	set_realmode_int(0x15, bios_int15);
 }
 
+static void extract_e820(void)
+{
+	int id = fw_cfg_file_id("etc/e820");
+	uint32_t size;
+
+	if (id == -1)
+		panic();
+
+	size = fw_cfg_file_size(id);
+	e820.nr_map = size / sizeof(e820.map[0]);
+	fw_cfg_file_select(id);
+	fw_cfg_read(&e820.map, size);
+}
+
 int main(void)
 {
 	make_bios_writable();
 	setup_pic();
 	setup_idt();
+	fw_cfg_setup();
 	// extract_acpi();
-	// extract_e820();
+	extract_e820();
 	// extract_smbios();
 	// extract_kernel();
 	// make_bios_readonly();