summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@linux.intel.com>2014-01-22 20:55:40 -0800
committerH. Peter Anvin <hpa@linux.intel.com>2014-01-22 20:56:24 -0800
commit729b46714bbdfaed7adf46c54988d3ae58c6b67f (patch)
treec977313fb4e80df7a92b05218775849363d1cda1
downloadtest16-729b46714bbdfaed7adf46c54988d3ae58c6b67f.tar.gz
test16-729b46714bbdfaed7adf46c54988d3ae58c6b67f.tar.xz
test16-729b46714bbdfaed7adf46c54988d3ae58c6b67f.zip
Initial checkin
-rw-r--r--.gitignore6
-rw-r--r--Makefile54
-rw-r--r--code16gcc.h1
-rw-r--r--lib16/conio.c14
-rw-r--r--lib16/crt0.S7
-rw-r--r--lib16/strlen.c9
-rw-r--r--run16.c141
-rw-r--r--test16/hello.c7
8 files changed, 239 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..93d6f6c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+*~
+*.o
+*.a
+*.elf
+*.bin
+run16
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..e1eb9dc
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,54 @@
+CC = gcc
+LD = ld
+AR = ar
+OBJCOPY = objcopy
+CFLAGS = -g -m32 -O2
+S16FLAGS = -g -m32 -mregparm=3 -D__ASSEMBLY__ -I include16
+C16FLAGS = -g -m32 -mregparm=3 -O2 -ffreestanding \
+ -include code16gcc.h -I include16
+LD16FLAGS = -m elf_i386 --section-start=.init=0x1000
+
+LIB16S = $(wildcard lib16/*.S)
+LIB16C = $(wildcard lib16/*.c)
+LIB16O = $(LIB16C:.c=.o) $(LIB16S:.S=.o)
+
+TEST16S = $(wildcard test16/*.S)
+TEST16C = $(wildcard test16/*.c)
+TEST16O = $(TEST16C:.c=.o) $(TEST16S:.S=.o)
+TEST16ELF = $(TEST16O:.o=.elf)
+TEST16BIN = $(TEST16O:.o=.bin)
+
+all : run16 $(TEST16BIN)
+
+lib16/%.o: lib16/%.S
+ $(CC) $(S16FLAGS) -c -o $@ $<
+
+lib16/%.o: lib16/%.c
+ $(CC) $(C16FLAGS) -c -o $@ $<
+
+lib16/lib16.a: $(LIB16O)
+ rm -f $@
+ $(AR) cq $@ $^
+
+test16/%.o: test16/%.S
+ $(CC) $(S16FLAGS) -c -o $@ $<
+
+test16/%.o: test16/%.c
+ $(CC) $(C16FLAGS) -c -o $@ $<
+
+.PRECIOUS: test16/%.elf
+test16/%.elf: test16/%.o lib16/lib16.a
+ $(LD) $(LD16FLAGS) -o $@ $^
+
+test16/%.bin: test16/%.elf
+ $(OBJCOPY) -O binary $< $@
+
+run16: run16.c
+ $(CC) $(CFLAGS) -o $@ $<
+
+clean:
+ rm -f run16 *.o lib16/*.o lib16/*.a
+ rm -f test16/*.o test16/*.elf test16/*.bin
+
+spotless: clean
+ rm -f *~ */*~
diff --git a/code16gcc.h b/code16gcc.h
new file mode 100644
index 0000000..0daca66
--- /dev/null
+++ b/code16gcc.h
@@ -0,0 +1 @@
+asm(".code16gcc");
diff --git a/lib16/conio.c b/lib16/conio.c
new file mode 100644
index 0000000..96b03ca
--- /dev/null
+++ b/lib16/conio.c
@@ -0,0 +1,14 @@
+#define SEG_BASE (*(const unsigned int *)0xfffc)
+
+void puts(const char *s)
+{
+ int rv;
+
+ /* XXX: should loop over this */
+ asm volatile("int $0x80"
+ : "=a" (rv)
+ : "a" (4), /* __NR_write */
+ "b" (1),
+ "c" ((unsigned int)s + SEG_BASE),
+ "d" (strlen(s)));
+}
diff --git a/lib16/crt0.S b/lib16/crt0.S
new file mode 100644
index 0000000..447e002
--- /dev/null
+++ b/lib16/crt0.S
@@ -0,0 +1,7 @@
+ .code16
+ .section ".init","ax"
+ .globl _start
+_start:
+ jmp main
+ .type _start,@function
+ .size _start,.-_start
diff --git a/lib16/strlen.c b/lib16/strlen.c
new file mode 100644
index 0000000..cb7cdb4
--- /dev/null
+++ b/lib16/strlen.c
@@ -0,0 +1,9 @@
+int strlen(const char *c)
+{
+ const char *c0 = c;
+
+ while (*c)
+ c++;
+
+ return c - c0;
+}
diff --git a/run16.c b/run16.c
new file mode 100644
index 0000000..a814ba4
--- /dev/null
+++ b/run16.c
@@ -0,0 +1,141 @@
+/*
+ * Wrapper to run a 16-bit raw binary on Linux/i386
+ */
+
+#define _GNU_SOURCE
+#include <syscall.h>
+#include <stdio.h>
+#include <sys/syscall.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <asm/ldt.h>
+#include <string.h>
+#include <asm/unistd.h>
+#include <sys/mman.h>
+
+static char toybox[65536] __attribute__((aligned(4096)));
+
+#define LOAD_ADDR 0x1000
+
+static void setup_ldt(void)
+{
+ const struct user_desc code16_desc = {
+ .entry_number = 0,
+ .base_addr = (unsigned long)toybox,
+ .limit = 0xffff,
+ .seg_32bit = 0,
+ .contents = 2, /* Code, R/X */
+ .read_exec_only = 0,
+ .limit_in_pages = 0,
+ .seg_not_present = 0,
+ .useable = 0
+ };
+ const struct user_desc data16_desc = {
+ .entry_number = 1,
+ .base_addr = (unsigned long)toybox,
+ .limit = 0xffff,
+ .seg_32bit = 0,
+ .contents = 0, /* Data, R/W */
+ .read_exec_only = 0,
+ .limit_in_pages = 0,
+ .seg_not_present = 0,
+ .useable = 0
+ };
+
+ syscall(SYS_modify_ldt, 1, &code16_desc, sizeof code16_desc);
+ syscall(SYS_modify_ldt, 1, &data16_desc, sizeof data16_desc);
+}
+
+static void load_file(const char *file, unsigned int trampoline_addr)
+{
+ FILE *f = fopen(file, "rb");
+
+ if (!f) {
+ fprintf(stderr, "%s: could not open: %s\n", file);
+ exit(127);
+ }
+
+ fread(toybox + LOAD_ADDR, 1, trampoline_addr - LOAD_ADDR, f);
+ fclose(f);
+}
+
+static void jump16(uint32_t offset)
+{
+ struct far_ptr {
+ uint32_t offset;
+ uint16_t segment;
+ } target;
+
+ target.offset = offset;
+ target.segment = 7;
+
+ asm volatile("ljmpl *%0" : : "m" (target),
+ "a" (LOAD_ADDR), "d" (offset));
+}
+
+static void run(const char *file)
+{
+ unsigned int trampoline_len;
+ const void *trampoline;
+ unsigned char *entrypoint;
+ uint32_t offset;
+
+ setup_ldt();
+
+ /* No points for style */
+ asm volatile(
+ " .pushsection \".rodata\",\"a\"\n"
+ " .code16\n"
+ "1:\n"
+ " movw $15,%%cx\n"
+ " movw %%cx,%%ss\n"
+ " movl %%edx,%%esp\n"
+ " movw %%cx,%%ds\n"
+ " movw %%cx,%%es\n"
+ " movw %%cx,%%fs\n"
+ " movw %%cx,%%gs\n"
+ " calll *%%eax\n"
+ " movzbl %%al,%%ebx\n"
+ " movl %2,%%eax\n"
+ " int $0x80\n"
+ "2:\n"
+ " .code32\n"
+ " .popsection\n"
+ " movl $1b,%0\n"
+ " movl $(2b - 1b),%1\n"
+ : "=r" (trampoline), "=r" (trampoline_len)
+ : "i" (__NR_exit));
+
+ offset = (sizeof toybox - trampoline_len - 4) & ~15;
+ entrypoint = toybox + offset;
+ memcpy(entrypoint, trampoline, trampoline_len);
+
+ /* Save the segment base address in the highest dword */
+ *(uint32_t *)(toybox + sizeof toybox - 4) = (uint32_t)toybox;
+
+ load_file(file, offset);
+
+ /* Catch null pointer references */
+ mprotect(toybox, LOAD_ADDR, PROT_NONE);
+
+ /* The rest is fully permissive */
+ mprotect(toybox + LOAD_ADDR, sizeof toybox - LOAD_ADDR,
+ PROT_READ|PROT_WRITE|PROT_EXEC);
+
+ jump16(offset);
+ abort(); /* Does not return */
+}
+
+int main(int argc, char *argv[])
+{
+ int i;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s filename\n", argv[0]);
+ return 127;
+ }
+
+ run(argv[1]);
+ return 127;
+}
diff --git a/test16/hello.c b/test16/hello.c
new file mode 100644
index 0000000..5792920
--- /dev/null
+++ b/test16/hello.c
@@ -0,0 +1,7 @@
+extern void puts(const char *);
+
+int main(void)
+{
+ puts("Hello, World!\r\n");
+ return 0;
+}