load ACPI tables from fw_cfg
diff --git a/Makefile b/Makefile
index 008dc6c..86050a6 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 cbfs.o
+obj-y += linuxboot.o malloc.o pflash.o cbfs.o tables.o
 
 all-y = bios.bin
 all: $(all-y)
diff --git a/README b/README
index 3d6a5a4..a4f4f0b 100644
--- a/README
+++ b/README
@@ -45,6 +45,5 @@
 TODO
 ====
 
-* ACPI tables
 * SMBIOS tables
 * Multiboot loading
diff --git a/fw_cfg.c b/fw_cfg.c
index d222da2..79aeb70 100644
--- a/fw_cfg.c
+++ b/fw_cfg.c
@@ -44,6 +44,8 @@
 
 uint32_t fw_cfg_file_size(int id)
 {
+	if (id == -1)
+		return 0;
 	return files[id].size;
 }
 
diff --git a/include/bios.h b/include/bios.h
index 4ed3a1a..ed2cab4 100644
--- a/include/bios.h
+++ b/include/bios.h
@@ -38,6 +38,7 @@
 extern void bios_int10(void);
 extern void bios_int15(void);
 
+extern void extract_acpi(void);
 extern void boot_from_fwcfg(void);
 extern bool boot_from_cbfs(void *base, size_t sz);
 
diff --git a/include/fw_cfg.h b/include/fw_cfg.h
index 57afa14..a846812 100644
--- a/include/fw_cfg.h
+++ b/include/fw_cfg.h
@@ -39,6 +39,8 @@
 #define FW_CFG_CTL			0x510
 #define FW_CFG_DATA			0x511
 
+#include "ioport.h"
+
 static inline void fw_cfg_select(uint16_t f)
 {
 	outw(FW_CFG_CTL, f);
diff --git a/main.c b/main.c
index 6c3d48d..756cc87 100644
--- a/main.c
+++ b/main.c
@@ -114,7 +114,7 @@
 	e820->map[1] = (struct e820entry)
 		{ .addr = 639 * 1024, .size = 1024, .type = E820_RESERVED }; /* EBDA */
 	e820->map[2] = (struct e820entry)
-		{ .addr = 0xe0000, .size = 64 * 1024, .type = E820_NVS }; /* ACPI tables */
+		{ .addr = 0xd0000, .size = 128 * 1024, .type = E820_NVS }; /* ACPI tables */
 	e820->map[3] = (struct e820entry)
 		{ .addr = 0xf0000, .size = 64 * 1024, .type = E820_RESERVED }; /* firmware */
 	fw_cfg_read(&e820->map[4], size);
@@ -146,7 +146,7 @@
 	setup_pic();
 	setup_idt();
 	fw_cfg_setup();
-	// extract_acpi();
+	extract_acpi();
 	extract_e820();
 	// extract_smbios();
 	if (!detect_cbfs_and_boot())
diff --git a/tables.c b/tables.c
new file mode 100644
index 0000000..3f59b3e
--- /dev/null
+++ b/tables.c
@@ -0,0 +1,160 @@
+#include "bios.h"
+#include "stdio.h"
+#include "fw_cfg.h"
+#include "string.h"
+
+struct loader_cmd {
+	uint32_t cmd;
+	union {
+#define CMD_QUIT 0
+#define CMD_ALLOC 1
+		struct {
+			char file[56];
+			uint32_t align;
+			uint8_t zone;
+		} alloc;
+#define CMD_PTR 2
+		struct {
+			char dest[56];
+			char src[56];
+			uint32_t offset;
+			uint8_t size;
+		} ptr;
+#define CMD_CHECKSUM 3
+		struct {
+			char file[56];
+			uint32_t offset;
+			uint32_t start;
+			uint32_t len;
+		} checksum;
+		uint8_t pad[124];
+	};
+} __attribute__((__packed__));
+
+enum {
+	ALLOC_HIGH = 1,
+	ALLOC_FSEG = 2
+};
+
+static uint8_t *file_address[20];
+
+static inline void *id_to_addr(int fw_cfg_id)
+{
+	return file_address[fw_cfg_id];
+}
+
+static inline void set_file_addr(int fw_cfg_id, void *p)
+{
+	file_address[fw_cfg_id] = p;
+}
+
+static void do_alloc(char *file, uint32_t align, uint8_t zone)
+{
+	int id = fw_cfg_file_id(file);
+	int n = fw_cfg_file_size(id);
+	char *p;
+
+	if (id == -1)
+		panic();
+
+	if (align > 16)
+		n += align - 16;
+
+	if (zone == ALLOC_FSEG)
+		p = malloc_fseg(n);
+	else
+		p = malloc(n);
+
+	p = (char *)((uintptr_t)(p + align - 1) & -align);
+
+	set_file_addr(id, p);
+	fw_cfg_file_select(id);
+	fw_cfg_read(p, n);
+}
+
+static void do_ptr(char *dest, char *src, uint32_t offset, uint8_t size)
+{
+	char *p, *q;
+	int id;
+	union {
+		long long ll;
+		char b[8];
+	} data;
+
+	id = fw_cfg_file_id(src);
+	p = id_to_addr(id);
+	if (!p)
+		panic();
+
+	id = fw_cfg_file_id(dest);
+	q = id_to_addr(id);
+	if (!q)
+		panic();
+
+	q += offset;
+
+	/* Assumes little endian */
+	data.ll = 0;
+	memcpy(&data.b, q, size);
+	data.ll += (uintptr_t) p;
+	memcpy(q, &data.b, size);
+}
+
+static inline uint8_t csum8(uint8_t *buf, uint32_t len)
+{
+	uint32_t s = 0;
+	while (len-- > 0)
+		s += *buf++;
+	return s;
+}
+static void do_checksum(char *file, uint32_t offset, uint32_t start, uint32_t len)
+{
+	uint8_t *p;
+	int id;
+	int n;
+
+	id = fw_cfg_file_id(file);
+	p = id_to_addr(id);
+	if (!p)
+		panic();
+
+	n = fw_cfg_file_size(id);
+	if (offset >= n || n < start || len > n - start)
+		panic();
+
+	p[offset] -= csum8(&p[start], len);
+}
+
+void extract_acpi(void)
+{
+	int id = fw_cfg_file_id("etc/table-loader");
+	int n = fw_cfg_file_size(id);
+	struct loader_cmd script[n / sizeof(struct loader_cmd)];
+	int i;
+
+	if (!n)
+		return;
+
+	fw_cfg_file_select(id);
+	fw_cfg_read(script, n);
+
+	for (i = 0; i < ARRAY_SIZE(script); i++) {
+		struct loader_cmd *s = &script[i];
+		switch(script[i].cmd) {
+		case CMD_ALLOC:
+			do_alloc(s->alloc.file, s->alloc.align, s->alloc.zone);
+			break;
+		case CMD_PTR:
+			do_ptr(s->ptr.dest, s->ptr.src, s->ptr.offset, s->ptr.size);
+			break;
+		case CMD_CHECKSUM:
+			do_checksum(s->checksum.file, s->checksum.offset,
+				    s->checksum.start, s->checksum.len);
+			break;
+		case CMD_QUIT:
+			return;
+		default:
+			panic();
+		}
+	}
+}