[PATCH 2 of 2] This patch moves x86 specific tests to there own directory

Jerone Young
2007-10-23T20:42:00Z

# HG changeset patch
# User Jerone Young <jyoung5-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>
# Date 1193171508 18000
# Node ID 5cd9f474ff2b1e7175278d4bcd3e885f1b294d98
# Parent 4f7e88d410070f9b49236fad68533ca4352981f6
This patch moves x86 specific tests to there own directory.

Signed-off-by: Jerone Young <jyoung5-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>

diff --git a/user/Makefile b/user/Makefile
--- a/user/Makefile
+++ b/user/Makefile
@@ -2,6 +2,8 @@ include config.mak
include config.mak

DESTDIR :=
+
+.PHONY: arch_clean clean

#make sure env CFLAGS variable is not used
CFLAGS =
@@ -15,13 +17,13 @@ cc-option = $(shell if $(CC) $(1) -S -o
cc-option = $(shell if $(CC) $(1) -S -o /dev/null -xc /dev/null \
> /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;)

-CFLAGS += $(autodepend-flags) -g -fomit-frame-pointer -Wall
+CFLAGS += $(autodepend-flags) -g -fomit-frame-pointer -Wall
CFLAGS += $(call cc-option, -fno-stack-protector, "")
CFLAGS += $(call cc-option, -fno-stack-protector-all, "")

LDFLAGS += $(CFLAGS)

-CXXFLAGS = $(autodepend-flags)
+CXXFLAGS = $(autodepend-flags)

autodepend-flags = -MMD -MF $(dir $*).$(notdir $*).d

@@ -36,29 +38,10 @@ install:
%.flat: %.o
$(CC) $(CFLAGS) -nostdlib -o $@ -Wl,-T,flat.lds $^

-test/bootstrap: test/bootstrap.o
- $(CC) -nostdlib -o $@ -Wl,-T,bootstrap.lds $^
-
%.o: %.S
$(CC) $(CFLAGS) -c -nostdlib -o $@ $^

-test/irq.flat: test/print.o
-
-test/access.flat: $(cstart.o) test/access.o test/printf.o test/print.o \
- test/smp.o
-
-test/sieve.flat: $(cstart.o) test/sieve.o test/print.o test/vm.o
-
-test/vmexit.flat: $(cstart.o) test/vmexit.o test/printf.o test/smp.o
-
-test/test32.flat: test/test32.o
-
-test/smp.flat: $(cstart.o) test/smp.o test/printf.o test/smptest.o
-
-test/%.o: CFLAGS += -std=gnu99 -ffreestanding
-
-include .*.d

-clean:
+clean: arch_clean
$(RM) kvmctl *.o *.a .*.d
- $(RM) test/bootstrap test/*.o test/*.flat test/.*.d
diff --git a/user/config-i386.mak b/user/config-i386.mak
--- a/user/config-i386.mak
+++ b/user/config-i386.mak
@@ -1,5 +1,6 @@ LIBDIR = /lib
LIBDIR = /lib
-cstart.o = test/cstart.o
+TEST_DIR=test/x86
+cstart.o = $(TEST_DIR)/cstart.o
bits = 32
ldarch = elf32-i386
CFLAGS += -m32
diff --git a/user/config-x86-common.mak b/user/config-x86-common.mak
--- a/user/config-x86-common.mak
+++ b/user/config-x86-common.mak
@@ -11,6 +11,33 @@ libkvm.a: kvmctl.o

balloon_ctl: balloon_ctl.o

-flatfiles_tests-common = test/bootstrap test/vmexit.flat test/smp.flat
+flatfiles_tests-common = $(TEST_DIR)/bootstrap \
+ $(TEST_DIR)/vmexit.flat \
+ $(TEST_DIR)/smp.flat

-flatfiles: $(flatfiles_tests-common) $(flatfile_tests))
+flatfiles: $(flatfiles_tests-common) $(flatfile_tests)
+
+$(TEST_DIR)/%.o: CFLAGS += -std=gnu99 -ffreestanding
+
+$(TEST_DIR)/bootstrap: $(TEST_DIR)/bootstrap.o
+ $(CC) -nostdlib -o $@ -Wl,-T,bootstrap.lds $^
+
+$(TEST_DIR)/irq.flat: $(TEST_DIR)/test/print.o
+
+$(TEST_DIR)/access.flat: $(cstart.o) $(TEST_DIR)/access.o \
+ $(TEST_DIR)/printf.o $(TEST_DIR)/print.o $(TEST_DIR)/smp.o
+
+$(TEST_DIR)/sieve.flat: $(cstart.o) $(TEST_DIR)/sieve.o \
+ $(TEST_DIR)/print.o $(TEST_DIR)/vm.o
+
+$(TEST_DIR)/vmexit.flat: $(cstart.o) $(TEST_DIR)/vmexit.o \
+ $(TEST_DIR)/printf.o $(TEST_DIR)/smp.o
+
+$(TEST_DIR)/test32.flat: $(TEST_DIR)/test32.o
+
+$(TEST_DIR)/smp.flat: $(cstart.o) $(TEST_DIR)/smp.o $(TEST_DIR)/printf.o \
+ $(TEST_DIR)/smptest.o
+
+arch_clean:
+ $(RM) $(TEST_DIR)/bootstrap $(TEST_DIR)/*.o $(TEST_DIR)/*.flat \
+ $(TEST_DIR)/.*.d
diff --git a/user/config-x86_64.mak b/user/config-x86_64.mak
--- a/user/config-x86_64.mak
+++ b/user/config-x86_64.mak
@@ -1,5 +1,6 @@ LIBDIR = /lib64
LIBDIR = /lib64
-cstart.o = test/cstart64.o
+TEST_DIR=test/x86
+cstart.o = $(TEST_DIR)/cstart64.o
bits = 64
ldarch = elf64-x86-64
CFLAGS += -m64
diff --git a/user/main.c b/user/main.c
--- a/user/main.c
+++ b/user/main.c
@@ -17,7 +17,7 @@
#define _GNU_SOURCE

#include "kvmctl.h"
-#include "test/apic.h"
+#include "test/x86/apic.h"

#include <stdio.h>
#include <unistd.h>
diff --git a/user/test/access.c b/user/test/access.c
deleted file mode 100644
--- a/user/test/access.c
+++ /dev/null
@@ -1,367 +0,0 @@
-
-#include "smp.h"
-#include "printf.h"
-
-typedef unsigned long pt_element_t;
-
-#define PAGE_SIZE ((pt_element_t)4096)
-#define PAGE_MASK (~(PAGE_SIZE-1))
-
-#define PT_BASE_ADDR_MASK ((pt_element_t)((((pt_element_t)1 << 40) - 1) & PAGE_MASK))
-
-#define PT_PRESENT_MASK ((pt_element_t)1 << 0)
-#define PT_WRITABLE_MASK ((pt_element_t)1 << 1)
-#define PT_USER_MASK ((pt_element_t)1 << 2)
-#define PT_ACCESSED_MASK ((pt_element_t)1 << 5)
-#define PT_DIRTY_MASK ((pt_element_t)1 << 6)
-
-#define CR0_WP_MASK (1UL << 16)
-
-#define PFERR_PRESENT_MASK (1U << 0)
-#define PFERR_WRITE_MASK (1U << 1)
-#define PFERR_USER_MASK (1U << 2)
-
-/*
- * page table access check tests
- */
-
-enum {
- AC_PTE_PRESENT,
- AC_PTE_WRITABLE,
- AC_PTE_USER,
- AC_PTE_ACCESSED,
- AC_PTE_DIRTY,
- // AC_PTE_NX,
-
- AC_ACCESS_USER,
- AC_ACCESS_WRITE,
- // AC_ACCESS_FETCH,
- // AC_ACCESS_PTE,
-
- // AC_CPU_EFER_NX,
- AC_CPU_CR0_WP,
-
- NR_AC_FLAGS
-};
-
-const char *ac_names[] = {
- [AC_PTE_PRESENT] = "pte.p",
- [AC_PTE_ACCESSED] = "pte.a",
- [AC_PTE_WRITABLE] = "pte.rw",
- [AC_PTE_USER] = "pte.user",
- [AC_PTE_DIRTY] = "pte.d",
- [AC_ACCESS_WRITE] = "write",
- [AC_ACCESS_USER] = "user",
- [AC_CPU_CR0_WP] = "cr0.wp",
-};
-
-static inline void *va(pt_element_t phys)
-{
- return (void *)phys;
-}
-
-static unsigned long read_cr0()
-{
- unsigned long cr0;
-
- asm volatile ("mov %%cr0, %0" : "=r"(cr0));
-
- return cr0;
-}
-
-static void write_cr0(unsigned long cr0)
-{
- asm volatile ("mov %0, %%cr0" : : "r"(cr0));
-}
-
-typedef struct {
- unsigned short offset0;
- unsigned short selector;
- unsigned short ist : 3;
- unsigned short : 5;
- unsigned short type : 4;
- unsigned short : 1;
- unsigned short dpl : 2;
- unsigned short p : 1;
- unsigned short offset1;
- unsigned offset2;
- unsigned reserved;
-} idt_entry_t;
-
-typedef struct {
- unsigned flags[NR_AC_FLAGS];
- void *virt;
- pt_element_t phys;
- pt_element_t pt_pool;
- pt_element_t *ptep;
- pt_element_t expected_pte;
- int expected_fault;
- unsigned expected_error;
- idt_entry_t idt[256];
-} ac_test_t;
-
-typedef struct {
- unsigned short limit;
- unsigned long linear_addr;
-} __attribute__((packed)) descriptor_table_t;
-
-void lidt(idt_entry_t *idt, int nentries)
-{
- descriptor_table_t dt;
-
- dt.limit = nentries * sizeof(*idt) - 1;
- dt.linear_addr = (unsigned long)idt;
- asm volatile ("lidt %0" : : "m"(dt));
-}
-
-void memset(void *a, unsigned char v, int n)
-{
- unsigned char *x = a;
-
- while (n--)
- *x++ = v;
-}
-
-unsigned short read_cs()
-{
- unsigned short r;
-
- asm volatile ("mov %%cs, %0" : "=r"(r));
-}
-
-void set_idt_entry(idt_entry_t *e, void *addr, int dpl)
-{
- memset(e, 0, sizeof *e);
- e->offset0 = (unsigned long)addr;
- e->selector = read_cs();
- e->ist = 0;
- e->type = 14;
- e->dpl = dpl;
- e->p = 1;
- e->offset1 = (unsigned long)addr >> 16;
- e->offset2 = (unsigned long)addr >> 32;
-}
-
-void set_cr0_wp(int wp)
-{
- unsigned long cr0 = read_cr0();
-
- cr0 &= ~CR0_WP_MASK;
- if (wp)
- cr0 |= CR0_WP_MASK;
- write_cr0(cr0);
-}
-
-void ac_test_init(ac_test_t *at)
-{
- set_cr0_wp(1);
- for (int i = 0; i < NR_AC_FLAGS; ++i)
- at->flags[i] = 0;
- at->virt = (void *)(0x123400000000 + 16 * smp_id());
- at->phys = 32 * 1024 * 1024;
- at->pt_pool = 33 * 1024 * 1024;
- memset(at->idt, 0, sizeof at->idt);
- lidt(at->idt, 256);
- extern char page_fault, kernel_entry;
- set_idt_entry(&at->idt[14], &page_fault, 0);
- set_idt_entry(&at->idt[0x20], &kernel_entry, 3);
-}
-
-int ac_test_bump(ac_test_t *at)
-{
- for (int i = 0; i < NR_AC_FLAGS; ++i)
- if (!at->flags[i]) {
- at->flags[i] = 1;
- return 1;
- } else
- at->flags[i] = 0;
- return 0;
-}
-
-unsigned long read_cr3()
-{
- unsigned long cr3;
-
- asm volatile ("mov %%cr3, %0" : "=r"(cr3));
- return cr3;
-}
-
-void invlpg(void *addr)
-{
- asm volatile ("invlpg (%0)" : : "r"(addr));
-}
-
-pt_element_t ac_test_alloc_pt(ac_test_t *at)
-{
- pt_element_t ret = at->pt_pool;
- at->pt_pool += PAGE_SIZE;
- return ret;
-}
-
-void ac_test_setup_pte(ac_test_t *at)
-{
- unsigned long root = read_cr3();
-
- for (int i = 4; i >= 1; --i) {
- pt_element_t *vroot = va(root & PT_BASE_ADDR_MASK);
- unsigned index = ((unsigned long)at->virt >> (12 + (i-1) * 9)) & 511;
- pt_element_t pte;
- if (i != 1) {
- pte = vroot[index];
- if (!(pte & PT_PRESENT_MASK))
- pte = ac_test_alloc_pt(at) | PT_PRESENT_MASK;
- pte |= PT_WRITABLE_MASK | PT_USER_MASK;
- } else {
- pte = at->phys & PT_BASE_ADDR_MASK;
- if (at->flags[AC_PTE_PRESENT])
- pte |= PT_PRESENT_MASK;
- if (at->flags[AC_PTE_WRITABLE])
- pte |= PT_WRITABLE_MASK;
- if (at->flags[AC_PTE_USER])
- pte |= PT_USER_MASK;
- if (at->flags[AC_PTE_ACCESSED])
- pte |= PT_ACCESSED_MASK;
- if (at->flags[AC_PTE_DIRTY])
- pte |= PT_DIRTY_MASK;
- at->ptep = &vroot[index];
- }
- vroot[index] = pte;
- root = vroot[index];
- }
- invlpg(at->virt);
- at->expected_pte = *at->ptep;
- at->expected_fault = 0;
- at->expected_error = 0;
- if (!at->flags[AC_PTE_PRESENT])
- at->expected_fault = 1;
- else
- at->expected_error |= PFERR_PRESENT_MASK;
-
- if (at->flags[AC_ACCESS_USER]) {
- at->expected_error |= PFERR_USER_MASK;
- if (!at->flags[AC_PTE_USER])
- at->expected_fault = 1;
- }
-
- if (at->flags[AC_ACCESS_WRITE]) {
- at->expected_error |= PFERR_WRITE_MASK;
- if (!at->flags[AC_PTE_WRITABLE]
- && (at->flags[AC_CPU_CR0_WP] || at->flags[AC_ACCESS_USER])) {
- at->expected_fault = 1;
- } else if (!at->expected_fault) {
- at->expected_pte |= PT_DIRTY_MASK;
- }
- }
-
- if (!at->expected_fault) {
- at->expected_pte |= PT_ACCESSED_MASK;
- }
-}
-
-int ac_test_do_access(ac_test_t *at)
-{
- static unsigned unique = 42;
- int fault = 0;
- unsigned e;
- static unsigned char user_stack[4096];
- unsigned long rsp;
-
- ++unique;
-
- unsigned r = unique;
- set_cr0_wp(at->flags[AC_CPU_CR0_WP]);
- asm volatile ("mov %%rsp, %%rdx \n\t"
- "cmp $0, %[user] \n\t"
- "jz do_access \n\t"
- "push %%rax; mov %[user_ds], %%ax; mov %%ax, %%ds; pop %%rax \n\t"
- "pushq %[user_ds] \n\t"
- "pushq %[user_stack_top] \n\t"
- "pushfq \n\t"
- "pushq %[user_cs] \n\t"
- "pushq $do_access \n\t"
- "iretq \n"
- "do_access: \n\t"
- "cmp $0, %[write] \n\t"
- "jnz 1f \n\t"
- "mov (%[addr]), %[reg] \n\t"
- "jmp done \n\t"
- "1: mov %[reg], (%[addr]) \n\t"
- "done: \n"
- "fixed1: \n"
- "int %[kernel_entry_vector] \n\t"
- "back_to_kernel:"
- : [reg]"+r"(r), "+a"(fault), "=b"(e), "=&d"(rsp)
- : [addr]"r"(at->virt),
- [write]"r"(at->flags[AC_ACCESS_WRITE]),
- [user]"r"(at->flags[AC_ACCESS_USER]),
- [user_ds]"i"(32+3),
- [user_cs]"i"(24+3),
- [user_stack_top]"r"(user_stack + sizeof user_stack),
- [kernel_entry_vector]"i"(0x20));
-
- asm volatile (".section .text.pf \n\t"
- "page_fault: \n\t"
- "pop %rbx \n\t"
- "movq $fixed1, (%rsp) \n\t"
- "movl $1, %eax \n\t"
- "iretq \n\t"
- ".section .text");
-
- asm volatile (".section .text.entry \n\t"
- "kernel_entry: \n\t"
- "mov %rdx, %rsp \n\t"
- "jmp back_to_kernel \n\t"
- ".section .text");
-
- if (fault && !at->expected_fault) {
- printf("unexpected fault\n");
- return 0;
- }
- if (!fault && at->expected_fault) {
- printf("unexpected access\n");
- return 0;
- }
- if (fault && e != at->expected_error) {
- printf("error code %x expected %x\n", e, at->expected_fault);
- return 0;
- }
- if (*at->ptep != at->expected_pte) {
- printf("pte %x expected %x\n", *at->ptep, at->expected_pte);
- return 0;
- }
-
- printf("OK\n");
- return 1;
-}
-
-void ac_test_exec(ac_test_t *at)
-{
- int r;
-
- printf("test");
- for (int i = 0; i < NR_AC_FLAGS; ++i)
- if (at->flags[i])
- printf(" %s", ac_names[i]);
- printf(": ");
- ac_test_setup_pte(at);
- r = ac_test_do_access(at);
-}
-
-void ac_test_run()
-{
- static ac_test_t at;
-
- printf("run\n");
- ac_test_init(&at);
- do {
- ac_test_exec(&at);
- } while (ac_test_bump(&at));
-}
-
-int main()
-{
- printf("starting test\n\n");
- smp_init(ac_test_run);
- ac_test_run();
- return 0;
-}
diff --git a/user/test/apic.h b/user/test/apic.h
deleted file mode 100644
--- a/user/test/apic.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef SILLY_APIC_H
-#define SILLY_APIC_H
-
-#define APIC_BASE 0x1000
-#define APIC_SIZE 0x100
-
-#define APIC_REG_NCPU 0x00
-#define APIC_REG_ID 0x04
-#define APIC_REG_SIPI_ADDR 0x08
-#define APIC_REG_SEND_SIPI 0x0c
-#define APIC_REG_IPI_VECTOR 0x10
-#define APIC_REG_SEND_IPI 0x14
-
-#endif
diff --git a/user/test/bootstrap.S b/user/test/bootstrap.S
deleted file mode 100644
--- a/user/test/bootstrap.S
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * minimal bootstrap to set up flat 32-bit protected mode
- */
-
-#include "apic.h"
-
-bstart = 0xf0000
-
-.code16
-
-stack_top = 0x1000
-cpu_up = 0x1000
-cpu_up_pmode = 0x1004
-
-pmode_stack_start = 0x10000
-pmode_stack_shift = 16
-pmode_stack_size = (1 << pmode_stack_shift)
-
-ipi_vec = 0xf0
-
-start:
- mov $stack_top, %sp
- call smp_init
-
- cs lidtl idt_desc
- cs lgdtl gdt_desc
- mov %cr0, %eax
- or $1, %eax
- mov %eax, %cr0
- ljmpl $8, $pmode + bstart
-
-smp_init:
- mov $ipi_vec, %eax
- mov $(APIC_BASE + APIC_REG_IPI_VECTOR), %dx
- out %eax, %dx
- movw $ap_switch_to_pmode, ipi_vec*4
- movw %cs, %ax
- mov %ax, ipi_vec*4+2
- mov $sipi, %eax
- mov $(APIC_BASE + APIC_REG_SIPI_ADDR), %dx
- outl %eax, %dx
- mov $(APIC_BASE + APIC_REG_NCPU), %dx
- inl %dx, %eax
- mov %eax, %ecx
- mov $1, %esi
-smp_loop:
- cmp %esi, %ecx
- jbe smp_done
- mov %esi, %eax
- mov $(APIC_BASE + APIC_REG_SEND_SIPI), %dx
- outl %eax, %dx
-wait_for_cpu:
- cmp cpu_up, %esi
- jne wait_for_cpu
- mov %esi, %eax
- mov $(APIC_BASE + APIC_REG_SEND_IPI), %dx
- out %eax, %dx
-wait_for_cpu_pmode:
- cmp cpu_up_pmode, %esi
- jne wait_for_cpu_pmode
-
- inc %esi
- jmp smp_loop
-smp_done:
- ret
-
-sipi:
- mov $(APIC_BASE + APIC_REG_ID), %dx
- inl %dx, %eax
- mov %eax, cpu_up
- shl $12, %eax
- addl $stack_top, %eax
- movl %eax, %esp
- sti
- nop
-1: hlt
- jmp 1b
-
-ap_switch_to_pmode:
- cs lidtl idt_desc
- cs lgdtl gdt_desc
- mov %cr0, %eax
- or $1, %eax
- mov %eax, %cr0
- ljmpl $8, $ap_pmode + bstart
-
-.code32
-ap_pmode:
- mov $0x10, %ax
- mov %ax, %ds
- mov %ax, %es
- mov %ax, %fs
- mov %ax, %gs
- mov %ax, %ss
- mov $(APIC_BASE + APIC_REG_ID), %dx
- in %dx, %eax
- mov %eax, cpu_up_pmode
- shl $pmode_stack_shift, %eax
- lea pmode_stack_start + pmode_stack_size(%eax), %esp
- sti
- nop
-ap_pmode_wait:
- hlt
- jmp ap_pmode_wait
-
-pmode:
- mov $0x10, %ax
- mov %ax, %ds
- mov %ax, %es
- mov %ax, %fs
- mov %ax, %gs
- mov %ax, %ss
- mov $pmode_stack_start + pmode_stack_size, %esp
- ljmp $8, $0x100000
-
-.align 16
-
-idt_desc:
- .word 8*256-1
- .long 0
-
-gdt_desc:
- .word gdt_end - gdt - 1
- .long gdt + bstart
-
-.align 16
-
-gdt:
- .quad 0
- .quad 0x00cf9b000000ffff // flat 32-bit code segment
- .quad 0x00cf93000000ffff // flat 32-bit data segment
-gdt_end:
-
-. = 0xfff0
- .code16
- ljmp $0xf000, $start
-.align 65536
diff --git a/user/test/cstart.S b/user/test/cstart.S
deleted file mode 100644
--- a/user/test/cstart.S
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-.bss
-
-.section .init
- call main
-1: hlt
- jmp 1b
-
-
diff --git a/user/test/cstart64.S b/user/test/cstart64.S
deleted file mode 100644
--- a/user/test/cstart64.S
+++ /dev/null
@@ -1,169 +0,0 @@
-
-#include "apic.h"
-
-boot_idt = 0
-
-ipi_vector = 0x20
-
-max_cpus = 4
-
-.bss
-
- . = . + 4096 * max_cpus
- .align 16
-stacktop:
-
- . = . + 4096
- .align 16
-ring0stacktop:
-
-.data
-
-.align 4096
-ptl2:
-i = 0
- .rept 512
- .quad 0x1e7 | (i << 21)
- i = i + 1
- .endr
-
-.align 4096
-ptl3:
- .quad ptl2 + 7
-
-.align 4096
-ptl4:
- .quad ptl3 + 7
-
-.align 4096
-
-gdt64_desc:
- .word gdt64_end - gdt64 - 1
- .quad gdt64
-
-gdt64:
- .quad 0
- .quad 0x00af9b000000ffff // 64-bit code segment
- .quad 0x00cf93000000ffff // 64-bit data segment
- .quad 0x00affb000000ffff // 64-bit code segment (user)
- .quad 0x00cff3000000ffff // 64-bit data segment (user)
-tss_descr:
- .rept max_cpus
- .quad 0x000089000000ffff // 64-bit avail tss
- .quad 0 // tss high addr
- .endr
-gdt64_end:
-
-i = 0
-tss:
- .rept max_cpus
- .long 0
- .quad ring0stacktop - i * 4096
- .quad 0, 0, 0
- .quad 0, 0, 0, 0, 0, 0, 0, 0
- .long 0, 0, 0
-i = i + 1
- .endr
-tss_end:
-
-.section .init
-
-.code32
- call prepare_64
- jmpl $8, $start64
-
-prepare_64:
- lgdt gdt64_desc
-
- mov %cr4, %eax
- bts $5, %eax // pae
- mov %eax, %cr4
-
- mov $ptl4, %eax
- mov %eax, %cr3
-
-efer = 0xc0000080
- mov $efer, %ecx
- rdmsr
- bts $8, %eax
- wrmsr
-
- mov %cr0, %eax
- bts $0, %eax
- bts $31, %eax
- mov %eax, %cr0
- ret
-
-
-smp_init_ipi:
- call prepare_64
- jmpl $8, $ap_start64
-
-.code64
-ap_start64:
- call load_tss
- sti
- nop
-
-1: hlt
- jmp 1b
-
-start64:
- call load_tss
- call smp_init
- call main
-
-1: hlt
- jmp 1b
-
-load_tss:
- mov $0, %eax
- mov %ax, %ss
- mov $(APIC_BASE + APIC_REG_ID), %dx
- in %dx, %eax
- mov %eax, %ebx
- shl $4, %ebx
- mov $((tss_end - tss) / max_cpus), %edx
- imul %edx
- add $tss, %rax
- mov %ax, tss_descr+2(%rbx)
- shr $16, %rax
- mov %al, tss_descr+4(%rbx)
- shr $8, %rax
- mov %al, tss_descr+7(%rbx)
- shr $8, %rax
- mov %eax, tss_descr+8(%rbx)
- lea tss_descr-gdt64(%rbx), %rax
- ltr %ax
- ret
-
-smp_init:
- lea boot_idt + ipi_vector * 8, %rdi
- mov $smp_init_ipi, %eax
- mov %ax, (%rdi)
- mov %cs, %ax
- mov %ax, 2(%rdi)
- movw $0x8e00, 4(%rdi)
- shr $16, %eax
- mov %ax, 6(%rdi)
-
- mov $(APIC_BASE + APIC_REG_IPI_VECTOR), %dx
- mov $ipi_vector, %eax
- out %eax, %dx
-
- mov $(APIC_BASE + APIC_REG_NCPU), %dx
- in %dx, %eax
- mov %eax, %ecx
- mov $1, %esi
-smp_loop:
- cmp %esi, %ecx
- je smp_init_done
-
- mov $(APIC_BASE + APIC_REG_SEND_IPI), %dx
- mov %esi, %eax
- out %eax, %dx
-
- inc %esi
- jmp smp_loop
-smp_init_done:
- ret
diff --git a/user/test/irq.S b/user/test/irq.S
deleted file mode 100644
--- a/user/test/irq.S
+++ /dev/null
@@ -1,118 +0,0 @@
-// irq test program. assumes outb $irq, $0xff generates an interrupt $irq.
-
-#include "print.h"
-
-.text
- PRINT "irq test"
- mov $stack_top, %rsp
-
- call setup_gdt
-
- mov %ds, %ax
- mov %ax, %ds // check ds descriptor is okay
-
- mov $irq_handler, %rdx
- mov $0x20, %eax
- call setup_idt_entry
-
- lidt idt_descriptor
-
- PRINT "software interrupt"
- int $0x20
-
- sti
- nop
-
- PRINT "injecting interrupt with interrupts enabled"
-
- mov $0x20, %al
- outb %al, $0xff // inject interrupt
-
- nop
- nop
- nop
- PRINT "after injection"
-
- cli
-
- PRINT "injecting interrupt with interrupts disabled"
-
- mov $0x20, %al
- outb %al, $0xff // inject interrupt
-
- // no interrupt here (disabled)
- nop
- nop
- PRINT "enabling interrupts"
- nop
- nop
- sti
- out %al, $0x80 // blocked by sti
- // interrupt here
- out %al, $0x80
-
- PRINT "after injection"
- nop
- nop
-
- hlt
-
-irq_handler:
- PRINT "interrupt handler"
- iretq
-
-setup_idt_entry: // %rax: irq %rdx: handler
- shl $4, %rax
- mov %dx, idt(%rax)
- shr $16, %rdx
- mov %cs, 2+idt(%rax)
- mov %dx, 6+idt(%rax)
- shr $16, %rdx
- mov %edx, 8+idt(%rax)
- movw $0x8e00, 4+idt(%rax)
- ret
-
-setup_gdt:
- mov $0, %eax
- mov %cs, %ax
- andl $~7, %eax
- movl $0xffff, gdt(%rax)
- movl $0xaf9b00, 4+gdt(%rax)
-
- mov $0, %eax
- mov %ds, %ax
- andl $~7, %eax
- movl $0xffff, gdt(%rax)
- movl $0x8f9300, 4+gdt(%rax)
-
- lgdt gdt_descriptor
- ret
-
-.data
-
-.align 16
-
-idt:
- . = . + 256 * 16
-
-idt_descriptor:
- .word . - idt - 1
- .quad idt
-
-.align 8
-
-gdt:
- . = . + 256 * 8
-
-gdt_descriptor:
- .word . - gdt - 1
- .quad gdt
-
-
-.align 4096
-stack_base:
- . = . + 4096
-stack_top:
-
-
-
diff --git a/user/test/memtest1.S b/user/test/memtest1.S
deleted file mode 100644
--- a/user/test/memtest1.S
+++ /dev/null
@@ -1,44 +0,0 @@
-.text
-
-start:
- mov $0x1000,%r8
- mov $0x0a,%ecx
-
-init_page:
- dec %ecx
- jne no_io
- mov $0x0,%al
- out %al,$0x80
- mov $0x0a,%ecx
-
-no_io:
- mov %r8,(%r8)
- add $0x1000,%r8
- cmp $0x8000000,%r8
- jne init_page
- mov $0x1000,%r8
- mov $0x0a,%ecx
-
-test_loop:
- dec %ecx
- jne no_io2
- mov $0x0,%al
- out %al,$0x80
- mov $0x0a,%ecx
-
-no_io2:
- mov (%r8),%r9
- cmp %r8,%r9
- jne err
- add $0x1000,%r8
- cmp $0x8000000,%r8
- jne test_loop
- mov $0x1000,%r8
- jmp test_loop
-
-err:
- mov $0xffffffffffffffff,%r12
- mov $0xffffffffffffffff,%r13
- mov $0x0,%al
- out %al,$0x80
- jmp err
diff --git a/user/test/print.S b/user/test/print.S
deleted file mode 100644
--- a/user/test/print.S
+++ /dev/null
@@ -1,31 +0,0 @@
-
-#include "print.h"
-
-#define PSEUDO_SERIAL_PORT 0xf1
-
-
-.text
- PRINT "boo"
- hlt
-1: jmp 1b
-
-.globl print
-print:
- push %rax
- push %rsi
- push %rdx
-
- mov %rdi, %rsi
- mov $(PSEUDO_SERIAL_PORT), %edx
-
-putchar:
- cmpb $0, (%rsi)
- jz done
- outsb
- jmp putchar
-done:
-
- pop %rdx
- pop %rsi
- pop %rax
- ret
diff --git a/user/test/print.h b/user/test/print.h
deleted file mode 100644
--- a/user/test/print.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef PRINT_H
-#define PRINT_H
-
-.macro PRINT text
-
-.data
-
-333: .asciz "\text\n"
-
-.previous
-
- push %rdi
- lea 333b, %rdi
- call print
- pop %rdi
-
-.endm
-
-#endif
diff --git a/user/test/printf.c b/user/test/printf.c
deleted file mode 100644
--- a/user/test/printf.c
+++ /dev/null
@@ -1,169 +0,0 @@
-#include "printf.h"
-#include "smp.h"
-#include <stdarg.h>
-
-static struct spinlock lock;
-
-void print(const char *s);
-
-typedef struct pstream {
- char *buffer;
- int remain;
- int added;
-} pstream_t;
-
-static void addchar(pstream_t *p, char c)
-{
- if (p->remain) {
- *p->buffer++ = c;
- --p->remain;
- }
- ++p->added;
-}
-
-void print_str(pstream_t *p, const char *s)
-{
- while (*s)
- addchar(p, *s++);
-}
-
-static char digits[16] = "0123456789abcdef";
-
-void print_int(pstream_t *ps, long n, int base)
-{
- char buf[sizeof(long) * 3 + 2], *p = buf;
- int s = 0, i;
-
- if (n < 0) {
- n = -n;
- s = 1;
- }
-
- while (n) {
- *p++ = digits[n % base];
- n /= base;
- }
-
- if (s)
- *p++ = '-';
-
- if (p == buf)
- *p++ = '0';
-
- for (i = 0; i < (p - buf) / 2; ++i) {
- char tmp;
-
- tmp = buf[i];
- buf[i] = p[-1-i];
- p[-1-i] = tmp;
- }
-
- *p = 0;
-
- print_str(ps, buf);
-}
-
-void print_unsigned(pstream_t *ps, unsigned long n, int base)
-{
- char buf[sizeof(long) * 3 + 1], *p = buf;
- int i;
-
- while (n) {
- *p++ = digits[n % base];
- n /= base;
- }
-
- if (p == buf)
- *p++ = '0';
-
- for (i = 0; i < (p - buf) / 2; ++i) {
- char tmp;
-
- tmp = buf[i];
- buf[i] = p[-1-i];
- p[-1-i] = tmp;
- }
-
- *p = 0;
-
- print_str(ps, buf);
-}
-
-int vsnprintf(char *buf, int size, const char *fmt, va_list va)
-{
- int n;
- pstream_t s;
-
- s.buffer = buf;
- s.remain = size - 1;
- s.added = 0;
- while (*fmt) {
- char f = *fmt++;
-
- if (f != '%') {
- addchar(&s, f);
- continue;
- }
- f = *fmt++;
- switch (f) {
- case '%':
- addchar(&s, '%');
- break;
- case '\0':
- --fmt;
- break;
- case 'd':
- print_int(&s, va_arg(va, int), 10);
- break;
- case 'x':
- print_unsigned(&s, va_arg(va, int), 16);
- break;
- case 'p':
- print_str(&s, "0x");
- print_unsigned(&s, (unsigned long)va_arg(va, void *), 16);
- break;
- case 's':
- print_str(&s, va_arg(va, const char *));
- break;
- default:
- addchar(&s, f);
- break;
- }
- }
- *s.buffer = 0;
- ++s.added;
- return s.added;
-}
-
-
-int snprintf(char *buf, int size, const char *fmt, ...)
-{
- va_list va;
- int r;
-
- va_start(va, fmt);
- r = vsnprintf(buf, size, fmt, va);
- va_end(va);
- return r;
-}
-
-void print_serial(const char *buf)
-{
- while (*buf)
- asm volatile ("out %%al, $0xf1" : : "a"(*buf++));
-}
-
-int printf(const char *fmt, ...)
-{
- va_list va;
- char buf[2000];
- int r;
-
- va_start(va, fmt);
- r = vsnprintf(buf, sizeof buf, fmt, va);
- va_end(va);
- spin_lock(&lock);
- print_serial(buf);
- spin_unlock(&lock);
- return r;
-}
diff --git a/user/test/printf.h b/user/test/printf.h
deleted file mode 100644
--- a/user/test/printf.h
+++ /dev/null
@@ -1,2 +0,0 @@
-
-int printf(const char *fmt, ...);
diff --git a/user/test/sieve.c b/user/test/sieve.c
deleted file mode 100644
--- a/user/test/sieve.c
+++ /dev/null
@@ -1,89 +0,0 @@
-#include "vm.h"
-
-void print(const char *text);
-
-void printi(int n)
-{
- char buf[10], *p = buf;
- int s = 0, i;
-
- if (n < 0) {
- n = -n;
- s = 1;
- }
-
- while (n) {
- *p++ = '0' + n % 10;
- n /= 10;
- }
-
- if (s)
- *p++ = '-';
-
- if (p == buf)
- *p++ = '0';
-
- for (i = 0; i < (p - buf) / 2; ++i) {
- char tmp;
-
- tmp = buf[i];
- buf[i] = p[-1-i];
- p[-1-i] = tmp;
- }
-
- *p = 0;
-
- print(buf);
-}
-
-int sieve(char* data, int size)
-{
- int i, j, r = 0;
-
- for (i = 0; i < size; ++i)
- data[i] = 1;
-
- data[0] = data[1] = 0;
-
- for (i = 2; i < size; ++i)
- if (data[i]) {
- ++r;
- for (j = i*2; j < size; j += i)
- data[j] = 0;
- }
- return r;
-}
-
-void test_sieve(const char *msg, char *data, int size)
-{
- int r;
-
- print(msg);
- print(": ");
- r = sieve(data, size);
- printi(r);
- print("\n");
-}
-
-#define STATIC_SIZE 1000000
-#define VSIZE 100000000
-char static_data[STATIC_SIZE];
-
-int main()
-{
- void *v;
- int i;
-
- print("starting sieve\n");
- test_sieve("static", static_data, STATIC_SIZE);
- setup_vm();
- print("mapped: ");
- test_sieve("mapped", static_data, STATIC_SIZE);
- for (i = 0; i < 30; ++i) {
- v = vmalloc(VSIZE);
- test_sieve("virtual", v, VSIZE);
- vfree(v);
- }
-
- return 0;
-}
diff --git a/user/test/simple.S b/user/test/simple.S
deleted file mode 100644
--- a/user/test/simple.S
+++ /dev/null
@@ -1,13 +0,0 @@
-
- .text
-
- mov $0, %al
- mov $10000, %ebx
-1:
- mov %rbx, %rcx
-2:
- loop 2b
- out %al, $0x80
- inc %al
- add $10000, %rbx
- jmp 1b
diff --git a/user/test/smp.c b/user/test/smp.c
deleted file mode 100644
--- a/user/test/smp.c
+++ /dev/null
@@ -1,151 +0,0 @@
-
-
-#include "smp.h"
-#include "apic.h"
-#include "printf.h"
-
-#define IPI_VECTOR 0x20
-
-static int apic_read(int reg)
-{
- unsigned short port = APIC_BASE + reg;
- unsigned v;
-
- asm volatile ("in %1, %0" : "=a"(v) : "d"(port));
- return v;
-}
-
-static void apic_write(int reg, unsigned v)
-{
- unsigned short port = APIC_BASE + reg;
-
- asm volatile ("out %0, %1" : : "a"(v), "d"(port));
-}
-
-static int apic_get_cpu_count()
-{
- return apic_read(APIC_REG_NCPU);
-}
-
-static int apic_get_id()
-{
- return apic_read(APIC_REG_ID);
-}
-
-static void apic_set_ipi_vector(int vector)
-{
- apic_write(APIC_REG_IPI_VECTOR, vector);
-}
-
-static void apic_send_ipi(int cpu)
-{
- apic_write(APIC_REG_SEND_IPI, cpu);
-}
-
-static struct spinlock ipi_lock;
-static void (*ipi_function)(void *data);
-static void *ipi_data;
-static volatile int ipi_done;
-
-static __attribute__((used)) void ipi()
-{
- ipi_function(ipi_data);
- ipi_done = 1;
-}
-
-asm (
- "ipi_entry: \n"
- " call ipi \n"
-#ifndef __x86_64__
- " iret"
-#else
- " iretq"
-#endif
- );
-
-
-static void set_ipi_descriptor(void (*ipi_entry)(void))
-{
- unsigned short *desc = (void *)(IPI_VECTOR * sizeof(long) * 2);
- unsigned short cs;
- unsigned long ipi = (unsigned long)ipi_entry;
-
- asm ("mov %%cs, %0" : "=r"(cs));
- desc[0] = ipi;
- desc[1] = cs;
- desc[2] = 0x8e00;
- desc[3] = ipi >> 16;
-#ifdef __x86_64__
- desc[4] = ipi >> 32;
- desc[5] = ipi >> 48;
- desc[6] = 0;
- desc[7] = 0;
-#endif
-}
-
-void spin_lock(struct spinlock *lock)
-{
- int v = 1;
-
- do {
- asm volatile ("xchg %1, %0" : "+m"(lock->v), "+r"(v));
- } while (v);
- asm volatile ("" : : : "memory");
-}
-
-void spin_unlock(struct spinlock *lock)
-{
- asm volatile ("" : : : "memory");
- lock->v = 0;
-}
-
-int cpu_count(void)
-{
- return apic_get_cpu_count();
-}
-
-int smp_id(void)
-{
- return apic_get_id();
-}
-
-void on_cpu(int cpu, void (*function)(void *data), void *data)
-{
- spin_lock(&ipi_lock);
- if (cpu == apic_get_id())
- function(data);
- else {
- ipi_function = function;
- ipi_data = data;
- apic_send_ipi(cpu);
- while (!ipi_done)
- ;
- ipi_done = 0;
- }
- spin_unlock(&ipi_lock);
-}
-
-static void (*smp_main_func)(void);
-static volatile int smp_main_running;
-
-asm ("smp_init_entry: \n"
- "incl smp_main_running \n"
- "sti \n"
- "call *smp_main_func");
-
-void smp_init(void (*smp_main)(void))
-{
- int i;
- void smp_init_entry(void);
- void ipi_entry(void);
-
- apic_set_ipi_vector(IPI_VECTOR);
- set_ipi_descriptor(smp_init_entry);
- smp_main_func = smp_main;
- for (i = 1; i < cpu_count(); ++i) {
- apic_send_ipi(i);
- while (smp_main_running < i)
- ;
- }
- set_ipi_descriptor(ipi_entry);
-}
diff --git a/user/test/smp.h b/user/test/smp.h
deleted file mode 100644
--- a/user/test/smp.h
+++ /dev/null
@@ -1,16 +0,0 @@
-#ifndef __SMP_H
-#define __SMP_H
-
-struct spinlock {
- int v;
-};
-
-void smp_init(void (*smp_main)(void));
-
-int cpu_count(void);
-int smp_id(void);
-void on_cpu(int cpu, void (*function)(void *data), void *data);
-void spin_lock(struct spinlock *lock);
-void spin_unlock(struct spinlock *lock);
-
-#endif
diff --git a/user/test/smptest.c b/user/test/smptest.c
deleted file mode 100644
--- a/user/test/smptest.c
+++ /dev/null
@@ -1,32 +0,0 @@
-
-#include "smp.h"
-#include "printf.h"
-
-static void ipi_test(void *data)
-{
- int n = (long)data;
-
- printf("ipi called, cpu %d\n", n);
- if (n != smp_id())
- printf("but wrong cpu %d\n", smp_id());
-}
-
-static void smp_main(void)
-{
- printf("smp main %d\n", smp_id());
- while (1)
- asm volatile ("hlt" : : : "memory");
-}
-
-int main()
-{
- int ncpus;
- int i;
-
- smp_init(smp_main);
- ncpus = cpu_count();
- printf("found %d cpus\n", ncpus);
- for (i = 0; i < ncpus; ++i)
- on_cpu(i, ipi_test, (void *)(long)i);
- return 0;
-}
diff --git a/user/test/stringio.S b/user/test/stringio.S
deleted file mode 100644
--- a/user/test/stringio.S
+++ /dev/null
@@ -1,31 +0,0 @@
-
-.data
-
-.macro str name, value
-
-\name : .long 1f-2f
-2: .ascii "\value"
-1:
-.endm
-
- str "forward", "forward"
- str "backward", "backward"
-
-.text
-
-
- cld
- movl forward, %ecx
- lea 4+forward, %rsi
- movw $1, %dx
- rep outsb
-
- std
- movl backward, %ecx
- lea 4+backward-1(%rcx), %rsi
- movw $2, %dx
- rep outsb
-
- hlt
-
-
diff --git a/user/test/test32.S b/user/test/test32.S
deleted file mode 100644
--- a/user/test/test32.S
+++ /dev/null
@@ -1,8 +0,0 @@
-.code32
-
-.text
-
-1:
- mov $0x12, %al
- out %al, $0x80
- jmp 1b
diff --git a/user/test/vm.c b/user/test/vm.c
deleted file mode 100644
--- a/user/test/vm.c
+++ /dev/null
@@ -1,249 +0,0 @@
-
-#include "vm.h"
-
-void print(const char *s);
-
-#define PAGE_SIZE 4096ul
-#define LARGE_PAGE_SIZE (512 * PAGE_SIZE)
-
-static void *free = 0;
-static void *vfree_top = 0;
-
-static unsigned long virt_to_phys(const void *virt)
-{
- return (unsigned long)virt;
-}
-
-static void *phys_to_virt(unsigned long phys)
-{
- return (void *)phys;
-}
-
-void *memset(void *data, int c, unsigned long len)
-{
- char *s = data;
-
- while (len--)
- *s++ = c;
-
- return data;
-}
-
-static void free_memory(void *mem, unsigned long size)
-{
- while (size >= PAGE_SIZE) {
- *(void **)mem = free;
- free = mem;
- mem += PAGE_SIZE;
- size -= PAGE_SIZE;
- }
-}
-
-void *alloc_page()
-{
- void *p;
-
- if (!free)
- return 0;
-
- p = free;
- free = *(void **)free;
-
- return p;
-}
-
-void free_page(void *page)
-{
- *(void **)page = free;
- free = page;
-}
-
-extern char edata;
-static unsigned long end_of_memory;
-
-#define PTE_PRESENT (1ull << 0)
-#define PTE_PSE (1ull << 7)
-#define PTE_WRITE (1ull << 1)
-#define PTE_ADDR (0xffffffffff000ull)
-
-static void install_pte(unsigned long *cr3,
- int pte_level,
- void *virt,
- unsigned long pte)
-{
- int level;
- unsigned long *pt = cr3;
- unsigned offset;
-
- for (level = 4; level > pte_level; --level) {
- offset = ((unsigned long)virt >> ((level-1) * 9 + 12)) & 511;
- if (!(pt[offset] & PTE_PRESENT)) {
- unsigned long *new_pt = alloc_page();
- memset(new_pt, 0, PAGE_SIZE);
- pt[offset] = virt_to_phys(new_pt) | PTE_PRESENT | PTE_WRITE;
- }
- pt = phys_to_virt(pt[offset] & 0xffffffffff000ull);
- }
- offset = ((unsigned long)virt >> (((level-1) * 9) + 12)) & 511;
- pt[offset] = pte;
-}
-
-static unsigned long get_pte(unsigned long *cr3, void *virt)
-{
- int level;
- unsigned long *pt = cr3, pte;
- unsigned offset;
-
- for (level = 4; level > 1; --level) {
- offset = ((unsigned long)virt >> (((level-1) * 9) + 12)) & 511;
- pte = pt[offset];
- if (!(pte & PTE_PRESENT))
- return 0;
- if (level == 2 && (pte & PTE_PSE))
- return pte;
- pt = phys_to_virt(pte & 0xffffffffff000ull);
- }
- offset = ((unsigned long)virt >> (((level-1) * 9) + 12)) & 511;
- pte = pt[offset];
- return pte;
-}
-
-static void install_large_page(unsigned long *cr3,
- unsigned long phys,
- void *virt)
-{
- install_pte(cr3, 2, virt, phys | PTE_PRESENT | PTE_WRITE | PTE_PSE);
-}
-
-static void install_page(unsigned long *cr3,
- unsigned long phys,
- void *virt)
-{
- install_pte(cr3, 1, virt, phys | PTE_PRESENT | PTE_WRITE);
-}
-
-static inline void load_cr3(unsigned long cr3)
-{
- asm ( "mov %0, %%cr3" : : "r"(cr3) );
-}
-
-static inline unsigned long read_cr3()
-{
- unsigned long cr3;
-
- asm volatile ( "mov %%cr3, %0" : "=r"(cr3) );
- return cr3;
-}
-
-static inline void load_cr0(unsigned long cr0)
-{
- asm volatile ( "mov %0, %%cr0" : : "r"(cr0) );
-}
-
-static inline unsigned long read_cr0()
-{
- unsigned long cr0;
-
- asm volatile ( "mov %%cr0, %0" : "=r"(cr0) );
- return cr0;
-}
-
-static inline void load_cr4(unsigned long cr4)
-{
- asm volatile ( "mov %0, %%cr4" : : "r"(cr4) );
-}
-
-static inline unsigned long read_cr4()
-{
- unsigned long cr4;
-
- asm volatile ( "mov %%cr4, %0" : "=r"(cr4) );
- return cr4;
-}
-
-struct gdt_table_descr
-{
- unsigned short len;
- unsigned long *table;
-} __attribute__((packed));
-
-static inline void load_gdt(unsigned long *table, int nent)
-{
- struct gdt_table_descr descr;
-
- descr.len = nent * 8 - 1;
- descr.table = table;
- asm volatile ( "lgdt %0" : : "m"(descr) );
-}
-
-#define SEG_CS_32 8
-#define SEG_CS_64 16
-
-struct ljmp {
- void *ofs;
- unsigned short seg;
-};
-
-static void setup_mmu(unsigned long len)
-{
- unsigned long *cr3 = alloc_page();
- unsigned long phys = 0;
-
- memset(cr3, 0, PAGE_SIZE);
- while (phys + LARGE_PAGE_SIZE <= len) {
- install_large_page(cr3, phys, (void *)phys);
- phys += LARGE_PAGE_SIZE;
- }
- while (phys + PAGE_SIZE <= len) {
- install_page(cr3, phys, (void *)phys);
- phys += PAGE_SIZE;
- }
-
- load_cr3(virt_to_phys(cr3));
- print("paging enabled\n");
-}
-
-static unsigned int inl(unsigned short port)
-{
- unsigned int val;
- asm volatile("inl %w1, %0" : "=a"(val) : "Nd"(port));
- return val;
-}
-
-void setup_vm()
-{
- end_of_memory = inl(0xd1);
- free_memory(&edata, end_of_memory - (unsigned long)&edata);
- setup_mmu(end_of_memory);
-}
-
-void *vmalloc(unsigned long size)
-{
- void *mem, *p;
- unsigned pages;
-
- size += sizeof(unsigned long);
-
- size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
- vfree_top -= size;
- mem = p = vfree_top;
- pages = size / PAGE_SIZE;
- while (pages--) {
- install_page(phys_to_virt(read_cr3()), virt_to_phys(alloc_page()), p);
- p += PAGE_SIZE;
- }
- *(unsigned long *)mem = size;
- mem += sizeof(unsigned long);
- return mem;
-}
-
-void vfree(void *mem)
-{
- unsigned long size = ((unsigned long *)mem)[-1];
-
- while (size) {
- free_page(phys_to_virt(get_pte(phys_to_virt(read_cr3()), mem) & PTE_ADDR));
- mem += PAGE_SIZE;
- size -= PAGE_SIZE;
- }
-}
diff --git a/user/test/vm.h b/user/test/vm.h
deleted file mode 100644
--- a/user/test/vm.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef VM_H
-#define VM_H
-
-void setup_vm();
-
-void *vmalloc(unsigned long size);
-void vfree(void *mem);
-
-#endif
diff --git a/user/test/vmexit.c b/user/test/vmexit.c
deleted file mode 100644
--- a/user/test/vmexit.c
+++ /dev/null
@@ -1,32 +0,0 @@
-
-#include "printf.h"
-
-static inline unsigned long long rdtsc()
-{
- long long r;
-
-#ifdef __x86_64__
- unsigned a, d;
-
- asm volatile ("rdtsc" : "=a"(a), "=d"(d));
- r = a | ((long long)d << 32);
-#else
- asm volatile ("rdtsc" : "=A"(r));
-#endif
- return r;
-}
-
-#define N (1 << 22)
-
-int main()
-{
- int i;
- unsigned long long t1, t2;
-
- t1 = rdtsc();
- for (i = 0; i < N; ++i)
- asm volatile ("cpuid" : : : "eax", "ebx", "ecx", "edx");
- t2 = rdtsc();
- printf("vmexit latency: %d\n", (int)((t2 - t1) / N));
- return 0;
-}
diff --git a/user/test/x86/access.c b/user/test/x86/access.c
new file mode 100644
--- /dev/null
+++ b/user/test/x86/access.c
@@ -0,0 +1,367 @@
+
+#include "smp.h"
+#include "printf.h"
+
+typedef unsigned long pt_element_t;
+
+#define PAGE_SIZE ((pt_element_t)4096)
+#define PAGE_MASK (~(PAGE_SIZE-1))
+
+#define PT_BASE_ADDR_MASK ((pt_element_t)((((pt_element_t)1 << 40) - 1) & PAGE_MASK))
+
+#define PT_PRESENT_MASK ((pt_element_t)1 << 0)
+#define PT_WRITABLE_MASK ((pt_element_t)1 << 1)
+#define PT_USER_MASK ((pt_element_t)1 << 2)
+#define PT_ACCESSED_MASK ((pt_element_t)1 << 5)
+#define PT_DIRTY_MASK ((pt_element_t)1 << 6)
+
+#define CR0_WP_MASK (1UL << 16)
+
+#define PFERR_PRESENT_MASK (1U << 0)
+#define PFERR_WRITE_MASK (1U << 1)
+#define PFERR_USER_MASK (1U << 2)
+
+/*
+ * page table access check tests
+ */
+
+enum {
+ AC_PTE_PRESENT,
+ AC_PTE_WRITABLE,
+ AC_PTE_USER,
+ AC_PTE_ACCESSED,
+ AC_PTE_DIRTY,
+ // AC_PTE_NX,
+
+ AC_ACCESS_USER,
+ AC_ACCESS_WRITE,
+ // AC_ACCESS_FETCH,
+ // AC_ACCESS_PTE,
+
+ // AC_CPU_EFER_NX,
+ AC_CPU_CR0_WP,
+
+ NR_AC_FLAGS
+};
+
+const char *ac_names[] = {
+ [AC_PTE_PRESENT] = "pte.p",
+ [AC_PTE_ACCESSED] = "pte.a",
+ [AC_PTE_WRITABLE] = "pte.rw",
+ [AC_PTE_USER] = "pte.user",
+ [AC_PTE_DIRTY] = "pte.d",
+ [AC_ACCESS_WRITE] = "write",
+ [AC_ACCESS_USER] = "user",
+ [AC_CPU_CR0_WP] = "cr0.wp",
+};
+
+static inline void *va(pt_element_t phys)
+{
+ return (void *)phys;
+}
+
+static unsigned long read_cr0()
+{
+ unsigned long cr0;
+
+ asm volatile ("mov %%cr0, %0" : "=r"(cr0));
+
+ return cr0;
+}
+
+static void write_cr0(unsigned long cr0)
+{
+ asm volatile ("mov %0, %%cr0" : : "r"(cr0));
+}
+
+typedef struct {
+ unsigned short offset0;
+ unsigned short selector;
+ unsigned short ist : 3;
+ unsigned short : 5;
+ unsigned short type : 4;
+ unsigned short : 1;
+ unsigned short dpl : 2;
+ unsigned short p : 1;
+ unsigned short offset1;
+ unsigned offset2;
+ unsigned reserved;
+} idt_entry_t;
+
+typedef struct {
+ unsigned flags[NR_AC_FLAGS];
+ void *virt;
+ pt_element_t phys;
+ pt_element_t pt_pool;
+ pt_element_t *ptep;
+ pt_element_t expected_pte;
+ int expected_fault;
+ unsigned expected_error;
+ idt_entry_t idt[256];
+} ac_test_t;
+
+typedef struct {
+ unsigned short limit;
+ unsigned long linear_addr;
+} __attribute__((packed)) descriptor_table_t;
+
+void lidt(idt_entry_t *idt, int nentries)
+{
+ descriptor_table_t dt;
+
+ dt.limit = nentries * sizeof(*idt) - 1;
+ dt.linear_addr = (unsigned long)idt;
+ asm volatile ("lidt %0" : : "m"(dt));
+}
+
+void memset(void *a, unsigned char v, int n)
+{
+ unsigned char *x = a;
+
+ while (n--)
+ *x++ = v;
+}
+
+unsigned short read_cs()
+{
+ unsigned short r;
+
+ asm volatile ("mov %%cs, %0" : "=r"(r));
+}
+
+void set_idt_entry(idt_entry_t *e, void *addr, int dpl)
+{
+ memset(e, 0, sizeof *e);
+ e->offset0 = (unsigned long)addr;
+ e->selector = read_cs();
+ e->ist = 0;
+ e->type = 14;
+ e->dpl = dpl;
+ e->p = 1;
+ e->offset1 = (unsigned long)addr >> 16;
+ e->offset2 = (unsigned long)addr >> 32;
+}
+
+void set_cr0_wp(int wp)
+{
+ unsigned long cr0 = read_cr0();
+
+ cr0 &= ~CR0_WP_MASK;
+ if (wp)
+ cr0 |= CR0_WP_MASK;
+ write_cr0(cr0);
+}
+
+void ac_test_init(ac_test_t *at)
+{
+ set_cr0_wp(1);
+ for (int i = 0; i < NR_AC_FLAGS; ++i)
+ at->flags[i] = 0;
+ at->virt = (void *)(0x123400000000 + 16 * smp_id());
+ at->phys = 32 * 1024 * 1024;
+ at->pt_pool = 33 * 1024 * 1024;
+ memset(at->idt, 0, sizeof at->idt);
+ lidt(at->idt, 256);
+ extern char page_fault, kernel_entry;
+ set_idt_entry(&at->idt[14], &page_fault, 0);
+ set_idt_entry(&at->idt[0x20], &kernel_entry, 3);
+}
+
+int ac_test_bump(ac_test_t *at)
+{
+ for (int i = 0; i < NR_AC_FLAGS; ++i)
+ if (!at->flags[i]) {
+ at->flags[i] = 1;
+ return 1;
+ } else
+ at->flags[i] = 0;
+ return 0;
+}
+
+unsigned long read_cr3()
+{
+ unsigned long cr3;
+
+ asm volatile ("mov %%cr3, %0" : "=r"(cr3));
+ return cr3;
+}
+
+void invlpg(void *addr)
+{
+ asm volatile ("invlpg (%0)" : : "r"(addr));
+}
+
+pt_element_t ac_test_alloc_pt(ac_test_t *at)
+{
+ pt_element_t ret = at->pt_pool;
+ at->pt_pool += PAGE_SIZE;
+ return ret;
+}
+
+void ac_test_setup_pte(ac_test_t *at)
+{
+ unsigned long root = read_cr3();
+
+ for (int i = 4; i >= 1; --i) {
+ pt_element_t *vroot = va(root & PT_BASE_ADDR_MASK);
+ unsigned index = ((unsigned long)at->virt >> (12 + (i-1) * 9)) & 511;
+ pt_element_t pte;
+ if (i != 1) {
+ pte = vroot[index];
+ if (!(pte & PT_PRESENT_MASK))
+ pte = ac_test_alloc_pt(at) | PT_PRESENT_MASK;
+ pte |= PT_WRITABLE_MASK | PT_USER_MASK;
+ } else {
+ pte = at->phys & PT_BASE_ADDR_MASK;
+ if (at->flags[AC_PTE_PRESENT])
+ pte |= PT_PRESENT_MASK;
+ if (at->flags[AC_PTE_WRITABLE])
+ pte |= PT_WRITABLE_MASK;
+ if (at->flags[AC_PTE_USER])
+ pte |= PT_USER_MASK;
+ if (at->flags[AC_PTE_ACCESSED])
+ pte |= PT_ACCESSED_MASK;
+ if (at->flags[AC_PTE_DIRTY])
+ pte |= PT_DIRTY_MASK;
+ at->ptep = &vroot[index];
+ }
+ vroot[index] = pte;
+ root = vroot[index];
+ }
+ invlpg(at->virt);
+ at->expected_pte = *at->ptep;
+ at->expected_fault = 0;
+ at->expected_error = 0;
+ if (!at->flags[AC_PTE_PRESENT])
+ at->expected_fault = 1;
+ else
+ at->expected_error |= PFERR_PRESENT_MASK;
+
+ if (at->flags[AC_ACCESS_USER]) {
+ at->expected_error |= PFERR_USER_MASK;
+ if (!at->flags[AC_PTE_USER])
+ at->expected_fault = 1;
+ }
+
+ if (at->flags[AC_ACCESS_WRITE]) {
+ at->expected_error |= PFERR_WRITE_MASK;
+ if (!at->flags[AC_PTE_WRITABLE]
+ && (at->flags[AC_CPU_CR0_WP] || at->flags[AC_ACCESS_USER])) {
+ at->expected_fault = 1;
+ } else if (!at->expected_fault) {
+ at->expected_pte |= PT_DIRTY_MASK;
+ }
+ }
+
+ if (!at->expected_fault) {
+ at->expected_pte |= PT_ACCESSED_MASK;
+ }
+}
+
+int ac_test_do_access(ac_test_t *at)
+{
+ static unsigned unique = 42;
+ int fault = 0;
+ unsigned e;
+ static unsigned char user_stack[4096];
+ unsigned long rsp;
+
+ ++unique;
+
+ unsigned r = unique;
+ set_cr0_wp(at->flags[AC_CPU_CR0_WP]);
+ asm volatile ("mov %%rsp, %%rdx \n\t"
+ "cmp $0, %[user] \n\t"
+ "jz do_access \n\t"
+ "push %%rax; mov %[user_ds], %%ax; mov %%ax, %%ds; pop %%rax \n\t"
+ "pushq %[user_ds] \n\t"
+ "pushq %[user_stack_top] \n\t"
+ "pushfq \n\t"
+ "pushq %[user_cs] \n\t"
+ "pushq $do_access \n\t"
+ "iretq \n"
+ "do_access: \n\t"
+ "cmp $0, %[write] \n\t"
+ "jnz 1f \n\t"
+ "mov (%[addr]), %[reg] \n\t"
+ "jmp done \n\t"
+ "1: mov %[reg], (%[addr]) \n\t"
+ "done: \n"
+ "fixed1: \n"
+ "int %[kernel_entry_vector] \n\t"
+ "back_to_kernel:"
+ : [reg]"+r"(r), "+a"(fault), "=b"(e), "=&d"(rsp)
+ : [addr]"r"(at->virt),
+ [write]"r"(at->flags[AC_ACCESS_WRITE]),
+ [user]"r"(at->flags[AC_ACCESS_USER]),
+ [user_ds]"i"(32+3),
+ [user_cs]"i"(24+3),
+ [user_stack_top]"r"(user_stack + sizeof user_stack),
+ [kernel_entry_vector]"i"(0x20));
+
+ asm volatile (".section .text.pf \n\t"
+ "page_fault: \n\t"
+ "pop %rbx \n\t"
+ "movq $fixed1, (%rsp) \n\t"
+ "movl $1, %eax \n\t"
+ "iretq \n\t"
+ ".section .text");
+
+ asm volatile (".section .text.entry \n\t"
+ "kernel_entry: \n\t"
+ "mov %rdx, %rsp \n\t"
+ "jmp back_to_kernel \n\t"
+ ".section .text");
+
+ if (fault && !at->expected_fault) {
+ printf("unexpected fault\n");
+ return 0;
+ }
+ if (!fault && at->expected_fault) {
+ printf("unexpected access\n");
+ return 0;
+ }
+ if (fault && e != at->expected_error) {
+ printf("error code %x expected %x\n", e, at->expected_fault);
+ return 0;
+ }
+ if (*at->ptep != at->expected_pte) {
+ printf("pte %x expected %x\n", *at->ptep, at->expected_pte);
+ return 0;
+ }
+
+ printf("OK\n");
+ return 1;
+}
+
+void ac_test_exec(ac_test_t *at)
+{
+ int r;
+
+ printf("test");
+ for (int i = 0; i < NR_AC_FLAGS; ++i)
+ if (at->flags[i])
+ printf(" %s", ac_names[i]);
+ printf(": ");
+ ac_test_setup_pte(at);
+ r = ac_test_do_access(at);
+}
+
+void ac_test_run()
+{
+ static ac_test_t at;
+
+ printf("run\n");
+ ac_test_init(&at);
+ do {
+ ac_test_exec(&at);
+ } while (ac_test_bump(&at));
+}
+
+int main()
+{
+ printf("starting test\n\n");
+ smp_init(ac_test_run);
+ ac_test_run();
+ return 0;
+}
diff --git a/user/test/x86/apic.h b/user/test/x86/apic.h
new file mode 100644
--- /dev/null
+++ b/user/test/x86/apic.h
@@ -0,0 +1,14 @@
+#ifndef SILLY_APIC_H
+#define SILLY_APIC_H
+
+#define APIC_BASE 0x1000
+#define APIC_SIZE 0x100
+
+#define APIC_REG_NCPU 0x00
+#define APIC_REG_ID 0x04
+#define APIC_REG_SIPI_ADDR 0x08
+#define APIC_REG_SEND_SIPI 0x0c
+#define APIC_REG_IPI_VECTOR 0x10
+#define APIC_REG_SEND_IPI 0x14
+
+#endif
diff --git a/user/test/x86/bootstrap.S b/user/test/x86/bootstrap.S
new file mode 100644
--- /dev/null
+++ b/user/test/x86/bootstrap.S
@@ -0,0 +1,137 @@
+/*
+ * minimal bootstrap to set up flat 32-bit protected mode
+ */
+
+#include "apic.h"
+
+bstart = 0xf0000
+
+.code16
+
+stack_top = 0x1000
+cpu_up = 0x1000
+cpu_up_pmode = 0x1004
+
+pmode_stack_start = 0x10000
+pmode_stack_shift = 16
+pmode_stack_size = (1 << pmode_stack_shift)
+
+ipi_vec = 0xf0
+
+start:
+ mov $stack_top, %sp
+ call smp_init
+
+ cs lidtl idt_desc
+ cs lgdtl gdt_desc
+ mov %cr0, %eax
+ or $1, %eax
+ mov %eax, %cr0
+ ljmpl $8, $pmode + bstart
+
+smp_init:
+ mov $ipi_vec, %eax
+ mov $(APIC_BASE + APIC_REG_IPI_VECTOR), %dx
+ out %eax, %dx
+ movw $ap_switch_to_pmode, ipi_vec*4
+ movw %cs, %ax
+ mov %ax, ipi_vec*4+2
+ mov $sipi, %eax
+ mov $(APIC_BASE + APIC_REG_SIPI_ADDR), %dx
+ outl %eax, %dx
+ mov $(APIC_BASE + APIC_REG_NCPU), %dx
+ inl %dx, %eax
+ mov %eax, %ecx
+ mov $1, %esi
+smp_loop:
+ cmp %esi, %ecx
+ jbe smp_done
+ mov %esi, %eax
+ mov $(APIC_BASE + APIC_REG_SEND_SIPI), %dx
+ outl %eax, %dx
+wait_for_cpu:
+ cmp cpu_up, %esi
+ jne wait_for_cpu
+ mov %esi, %eax
+ mov $(APIC_BASE + APIC_REG_SEND_IPI), %dx
+ out %eax, %dx
+wait_for_cpu_pmode:
+ cmp cpu_up_pmode, %esi
+ jne wait_for_cpu_pmode
+
+ inc %esi
+ jmp smp_loop
+smp_done:
+ ret
+
+sipi:
+ mov $(APIC_BASE + APIC_REG_ID), %dx
+ inl %dx, %eax
+ mov %eax, cpu_up
+ shl $12, %eax
+ addl $stack_top, %eax
+ movl %eax, %esp
+ sti
+ nop
+1: hlt
+ jmp 1b
+
+ap_switch_to_pmode:
+ cs lidtl idt_desc
+ cs lgdtl gdt_desc
+ mov %cr0, %eax
+ or $1, %eax
+ mov %eax, %cr0
+ ljmpl $8, $ap_pmode + bstart
+
+.code32
+ap_pmode:
+ mov $0x10, %ax
+ mov %ax, %ds
+ mov %ax, %es
+ mov %ax, %fs
+ mov %ax, %gs
+ mov %ax, %ss
+ mov $(APIC_BASE + APIC_REG_ID), %dx
+ in %dx, %eax
+ mov %eax, cpu_up_pmode
+ shl $pmode_stack_shift, %eax
+ lea pmode_stack_start + pmode_stack_size(%eax), %esp
+ sti
+ nop
+ap_pmode_wait:
+ hlt
+ jmp ap_pmode_wait
+
+pmode:
+ mov $0x10, %ax
+ mov %ax, %ds
+ mov %ax, %es
+ mov %ax, %fs
+ mov %ax, %gs
+ mov %ax, %ss
+ mov $pmode_stack_start + pmode_stack_size, %esp
+ ljmp $8, $0x100000
+
+.align 16
+
+idt_desc:
+ .word 8*256-1
+ .long 0
+
+gdt_desc:
+ .word gdt_end - gdt - 1
+ .long gdt + bstart
+
+.align 16
+
+gdt:
+ .quad 0
+ .quad 0x00cf9b000000ffff // flat 32-bit code segment
+ .quad 0x00cf93000000ffff // flat 32-bit data segment
+gdt_end:
+
+. = 0xfff0
+ .code16
+ ljmp $0xf000, $start
+.align 65536
diff --git a/user/test/x86/cstart.S b/user/test/x86/cstart.S
new file mode 100644
--- /dev/null
+++ b/user/test/x86/cstart.S
@@ -0,0 +1,10 @@
+
+
+.bss
+
+.section .init
+ call main
+1: hlt
+ jmp 1b
+
+
diff --git a/user/test/x86/cstart64.S b/user/test/x86/cstart64.S
new file mode 100644
--- /dev/null
+++ b/user/test/x86/cstart64.S
@@ -0,0 +1,169 @@
+
+#include "apic.h"
+
+boot_idt = 0
+
+ipi_vector = 0x20
+
+max_cpus = 4
+
+.bss
+
+ . = . + 4096 * max_cpus
+ .align 16
+stacktop:
+
+ . = . + 4096
+ .align 16
+ring0stacktop:
+
+.data
+
+.align 4096
+ptl2:
+i = 0
+ .rept 512
+ .quad 0x1e7 | (i << 21)
+ i = i + 1
+ .endr
+
+.align 4096
+ptl3:
+ .quad ptl2 + 7
+
+.align 4096
+ptl4:
+ .quad ptl3 + 7
+
+.align 4096
+
+gdt64_desc:
+ .word gdt64_end - gdt64 - 1
+ .quad gdt64
+
+gdt64:
+ .quad 0
+ .quad 0x00af9b000000ffff // 64-bit code segment
+ .quad 0x00cf93000000ffff // 64-bit data segment
+ .quad 0x00affb000000ffff // 64-bit code segment (user)
+ .quad 0x00cff3000000ffff // 64-bit data segment (user)
+tss_descr:
+ .rept max_cpus
+ .quad 0x000089000000ffff // 64-bit avail tss
+ .quad 0 // tss high addr
+ .endr
+gdt64_end:
+
+i = 0
+tss:
+ .rept max_cpus
+ .long 0
+ .quad ring0stacktop - i * 4096
+ .quad 0, 0, 0
+ .quad 0, 0, 0, 0, 0, 0, 0, 0
+ .long 0, 0, 0
+i = i + 1
+ .endr
+tss_end:
+
+.section .init
+
+.code32
+ call prepare_64
+ jmpl $8, $start64
+
+prepare_64:
+ lgdt gdt64_desc
+
+ mov %cr4, %eax
+ bts $5, %eax // pae
+ mov %eax, %cr4
+
+ mov $ptl4, %eax
+ mov %eax, %cr3
+
+efer = 0xc0000080
+ mov $efer, %ecx
+ rdmsr
+ bts $8, %eax
+ wrmsr
+
+ mov %cr0, %eax
+ bts $0, %eax
+ bts $31, %eax
+ mov %eax, %cr0
+ ret
+
+
+smp_init_ipi:
+ call prepare_64
+ jmpl $8, $ap_start64
+
+.code64
+ap_start64:
+ call load_tss
+ sti
+ nop
+
+1: hlt
+ jmp 1b
+
+start64:
+ call load_tss
+ call smp_init
+ call main
+
+1: hlt
+ jmp 1b
+
+load_tss:
+ mov $0, %eax
+ mov %ax, %ss
+ mov $(APIC_BASE + APIC_REG_ID), %dx
+ in %dx, %eax
+ mov %eax, %ebx
+ shl $4, %ebx
+ mov $((tss_end - tss) / max_cpus), %edx
+ imul %edx
+ add $tss, %rax
+ mov %ax, tss_descr+2(%rbx)
+ shr $16, %rax
+ mov %al, tss_descr+4(%rbx)
+ shr $8, %rax
+ mov %al, tss_descr+7(%rbx)
+ shr $8, %rax
+ mov %eax, tss_descr+8(%rbx)
+ lea tss_descr-gdt64(%rbx), %rax
+ ltr %ax
+ ret
+
+smp_init:
+ lea boot_idt + ipi_vector * 8, %rdi
+ mov $smp_init_ipi, %eax
+ mov %ax, (%rdi)
+ mov %cs, %ax
+ mov %ax, 2(%rdi)
+ movw $0x8e00, 4(%rdi)
+ shr $16, %eax
+ mov %ax, 6(%rdi)
+
+ mov $(APIC_BASE + APIC_REG_IPI_VECTOR), %dx
+ mov $ipi_vector, %eax
+ out %eax, %dx
+
+ mov $(APIC_BASE + APIC_REG_NCPU), %dx
+ in %dx, %eax
+ mov %eax, %ecx
+ mov $1, %esi
+smp_loop:
+ cmp %esi, %ecx
+ je smp_init_done
+
+ mov $(APIC_BASE + APIC_REG_SEND_IPI), %dx
+ mov %esi, %eax
+ out %eax, %dx
+
+ inc %esi
+ jmp smp_loop
+smp_init_done:
+ ret
diff --git a/user/test/x86/irq.S b/user/test/x86/irq.S
new file mode 100644
--- /dev/null
+++ b/user/test/x86/irq.S
@@ -0,0 +1,118 @@
+// irq test program. assumes outb $irq, $0xff generates an interrupt $irq.
+
+#include "print.h"
+
+.text
+ PRINT "irq test"
+ mov $stack_top, %rsp
+
+ call setup_gdt
+
+ mov %ds, %ax
+ mov %ax, %ds // check ds descriptor is okay
+
+ mov $irq_handler, %rdx
+ mov $0x20, %eax
+ call setup_idt_entry
+
+ lidt idt_descriptor
+
+ PRINT "software interrupt"
+ int $0x20
+
+ sti
+ nop
+
+ PRINT "injecting interrupt with interrupts enabled"
+
+ mov $0x20, %al
+ outb %al, $0xff // inject interrupt
+
+ nop
+ nop
+ nop
+ PRINT "after injection"
+
+ cli
+
+ PRINT "injecting interrupt with interrupts disabled"
+
+ mov $0x20, %al
+ outb %al, $0xff // inject interrupt
+
+ // no interrupt here (disabled)
+ nop
+ nop
+ PRINT "enabling interrupts"
+ nop
+ nop
+ sti
+ out %al, $0x80 // blocked by sti
+ // interrupt here
+ out %al, $0x80
+
+ PRINT "after injection"
+ nop
+ nop
+
+ hlt
+
+irq_handler:
+ PRINT "interrupt handler"
+ iretq
+
+setup_idt_entry: // %rax: irq %rdx: handler
+ shl $4, %rax
+ mov %dx, idt(%rax)
+ shr $16, %rdx
+ mov %cs, 2+idt(%rax)
+ mov %dx, 6+idt(%rax)
+ shr $16, %rdx
+ mov %edx, 8+idt(%rax)
+ movw $0x8e00, 4+idt(%rax)
+ ret
+
+setup_gdt:
+ mov $0, %eax
+ mov %cs, %ax
+ andl $~7, %eax
+ movl $0xffff, gdt(%rax)
+ movl $0xaf9b00, 4+gdt(%rax)
+
+ mov $0, %eax
+ mov %ds, %ax
+ andl $~7, %eax
+ movl $0xffff, gdt(%rax)
+ movl $0x8f9300, 4+gdt(%rax)
+
+ lgdt gdt_descriptor
+ ret
+
+.data
+
+.align 16
+
+idt:
+ . = . + 256 * 16
+
+idt_descriptor:
+ .word . - idt - 1
+ .quad idt
+
+.align 8
+
+gdt:
+ . = . + 256 * 8
+
+gdt_descriptor:
+ .word . - gdt - 1
+ .quad gdt
+
+
+.align 4096
+stack_base:
+ . = . + 4096
+stack_top:
+
+
+
diff --git a/user/test/x86/memtest1.S b/user/test/x86/memtest1.S
new file mode 100644
--- /dev/null
+++ b/user/test/x86/memtest1.S
@@ -0,0 +1,44 @@
+.text
+
+start:
+ mov $0x1000,%r8
+ mov $0x0a,%ecx
+
+init_page:
+ dec %ecx
+ jne no_io
+ mov $0x0,%al
+ out %al,$0x80
+ mov $0x0a,%ecx
+
+no_io:
+ mov %r8,(%r8)
+ add $0x1000,%r8
+ cmp $0x8000000,%r8
+ jne init_page
+ mov $0x1000,%r8
+ mov $0x0a,%ecx
+
+test_loop:
+ dec %ecx
+ jne no_io2
+ mov $0x0,%al
+ out %al,$0x80
+ mov $0x0a,%ecx
+
+no_io2:
+ mov (%r8),%r9
+ cmp %r8,%r9
+ jne err
+ add $0x1000,%r8
+ cmp $0x8000000,%r8
+ jne test_loop
+ mov $0x1000,%r8
+ jmp test_loop
+
+err:
+ mov $0xffffffffffffffff,%r12
+ mov $0xffffffffffffffff,%r13
+ mov $0x0,%al
+ out %al,$0x80
+ jmp err
diff --git a/user/test/x86/print.S b/user/test/x86/print.S
new file mode 100644
--- /dev/null
+++ b/user/test/x86/print.S
@@ -0,0 +1,31 @@
+
+#include "print.h"
+
+#define PSEUDO_SERIAL_PORT 0xf1
+
+
+.text
+ PRINT "boo"
+ hlt
+1: jmp 1b
+
+.globl print
+print:
+ push %rax
+ push %rsi
+ push %rdx
+
+ mov %rdi, %rsi
+ mov $(PSEUDO_SERIAL_PORT), %edx
+
+putchar:
+ cmpb $0, (%rsi)
+ jz done
+ outsb
+ jmp putchar
+done:
+
+ pop %rdx
+ pop %rsi
+ pop %rax
+ ret
diff --git a/user/test/x86/print.h b/user/test/x86/print.h
new file mode 100644
--- /dev/null
+++ b/user/test/x86/print.h
@@ -0,0 +1,19 @@
+#ifndef PRINT_H
+#define PRINT_H
+
+.macro PRINT text
+
+.data
+
+333: .asciz "\text\n"
+
+.previous
+
+ push %rdi
+ lea 333b, %rdi
+ call print
+ pop %rdi
+
+.endm
+
+#endif
diff --git a/user/test/x86/printf.c b/user/test/x86/printf.c
new file mode 100644
--- /dev/null
+++ b/user/test/x86/printf.c
@@ -0,0 +1,169 @@
+#include "printf.h"
+#include "smp.h"
+#include <stdarg.h>
+
+static struct spinlock lock;
+
+void print(const char *s);
+
+typedef struct pstream {
+ char *buffer;
+ int remain;
+ int added;
+} pstream_t;
+
+static void addchar(pstream_t *p, char c)
+{
+ if (p->remain) {
+ *p->buffer++ = c;
+ --p->remain;
+ }
+ ++p->added;
+}
+
+void print_str(pstream_t *p, const char *s)
+{
+ while (*s)
+ addchar(p, *s++);
+}
+
+static char digits[16] = "0123456789abcdef";
+
+void print_int(pstream_t *ps, long n, int base)
+{
+ char buf[sizeof(long) * 3 + 2], *p = buf;
+ int s = 0, i;
+
+ if (n < 0) {
+ n = -n;
+ s = 1;
+ }
+
+ while (n) {
+ *p++ = digits[n % base];
+ n /= base;
+ }
+
+ if (s)
+ *p++ = '-';
+
+ if (p == buf)
+ *p++ = '0';
+
+ for (i = 0; i < (p - buf) / 2; ++i) {
+ char tmp;
+
+ tmp = buf[i];
+ buf[i] = p[-1-i];
+ p[-1-i] = tmp;
+ }
+
+ *p = 0;
+
+ print_str(ps, buf);
+}
+
+void print_unsigned(pstream_t *ps, unsigned long n, int base)
+{
+ char buf[sizeof(long) * 3 + 1], *p = buf;
+ int i;
+
+ while (n) {
+ *p++ = digits[n % base];
+ n /= base;
+ }
+
+ if (p == buf)
+ *p++ = '0';
+
+ for (i = 0; i < (p - buf) / 2; ++i) {
+ char tmp;
+
+ tmp = buf[i];
+ buf[i] = p[-1-i];
+ p[-1-i] = tmp;
+ }
+
+ *p = 0;
+
+ print_str(ps, buf);
+}
+
+int vsnprintf(char *buf, int size, const char *fmt, va_list va)
+{
+ int n;
+ pstream_t s;
+
+ s.buffer = buf;
+ s.remain = size - 1;
+ s.added = 0;
+ while (*fmt) {
+ char f = *fmt++;
+
+ if (f != '%') {
+ addchar(&s, f);
+ continue;
+ }
+ f = *fmt++;
+ switch (f) {
+ case '%':
+ addchar(&s, '%');
+ break;
+ case '\0':
+ --fmt;
+ break;
+ case 'd':
+ print_int(&s, va_arg(va, int), 10);
+ break;
+ case 'x':
+ print_unsigned(&s, va_arg(va, int), 16);
+ break;
+ case 'p':
+ print_str(&s, "0x");
+ print_unsigned(&s, (unsigned long)va_arg(va, void *), 16);
+ break;
+ case 's':
+ print_str(&s, va_arg(va, const char *));
+ break;
+ default:
+ addchar(&s, f);
+ break;
+ }
+ }
+ *s.buffer = 0;
+ ++s.added;
+ return s.added;
+}
+
+
+int snprintf(char *buf, int size, const char *fmt, ...)
+{
+ va_list va;
+ int r;
+
+ va_start(va, fmt);
+ r = vsnprintf(buf, size, fmt, va);
+ va_end(va);
+ return r;
+}
+
+void print_serial(const char *buf)
+{
+ while (*buf)
+ asm volatile ("out %%al, $0xf1" : : "a"(*buf++));
+}
+
+int printf(const char *fmt, ...)
+{
+ va_list va;
+ char buf[2000];
+ int r;
+
+ va_start(va, fmt);
+ r = vsnprintf(buf, sizeof buf, fmt, va);
+ va_end(va);
+ spin_lock(&lock);
+ print_serial(buf);
+ spin_unlock(&lock);
+ return r;
+}
diff --git a/user/test/x86/printf.h b/user/test/x86/printf.h
new file mode 100644
--- /dev/null
+++ b/user/test/x86/printf.h
@@ -0,0 +1,2 @@
+
+int printf(const char *fmt, ...);
diff --git a/user/test/x86/sieve.c b/user/test/x86/sieve.c
new file mode 100644
--- /dev/null
+++ b/user/test/x86/sieve.c
@@ -0,0 +1,89 @@
+#include "vm.h"
+
+void print(const char *text);
+
+void printi(int n)
+{
+ char buf[10], *p = buf;
+ int s = 0, i;
+
+ if (n < 0) {
+ n = -n;
+ s = 1;
+ }
+
+ while (n) {
+ *p++ = '0' + n % 10;
+ n /= 10;
+ }
+
+ if (s)
+ *p++ = '-';
+
+ if (p == buf)
+ *p++ = '0';
+
+ for (i = 0; i < (p - buf) / 2; ++i) {
+ char tmp;
+
+ tmp = buf[i];
+ buf[i] = p[-1-i];
+ p[-1-i] = tmp;
+ }
+
+ *p = 0;
+
+ print(buf);
+}
+
+int sieve(char* data, int size)
+{
+ int i, j, r = 0;
+
+ for (i = 0; i < size; ++i)
+ data[i] = 1;
+
+ data[0] = data[1] = 0;
+
+ for (i = 2; i < size; ++i)
+ if (data[i]) {
+ ++r;
+ for (j = i*2; j < size; j += i)
+ data[j] = 0;
+ }
+ return r;
+}
+
+void test_sieve(const char *msg, char *data, int size)
+{
+ int r;
+
+ print(msg);
+ print(": ");
+ r = sieve(data, size);
+ printi(r);
+ print("\n");
+}
+
+#define STATIC_SIZE 1000000
+#define VSIZE 100000000
+char static_data[STATIC_SIZE];
+
+int main()
+{
+ void *v;
+ int i;
+
+ print("starting sieve\n");
+ test_sieve("static", static_data, STATIC_SIZE);
+ setup_vm();
+ print("mapped: ");
+ test_sieve("mapped", static_data, STATIC_SIZE);
+ for (i = 0; i < 30; ++i) {
+ v = vmalloc(VSIZE);
+ test_sieve("virtual", v, VSIZE);
+ vfree(v);
+ }
+
+ return 0;
+}
diff --git a/user/test/x86/simple.S b/user/test/x86/simple.S
new file mode 100644
--- /dev/null
+++ b/user/test/x86/simple.S
@@ -0,0 +1,13 @@
+
+ .text
+
+ mov $0, %al
+ mov $10000, %ebx
+1:
+ mov %rbx, %rcx
+2:
+ loop 2b
+ out %al, $0x80
+ inc %al
+ add $10000, %rbx
+ jmp 1b
diff --git a/user/test/x86/smp.c b/user/test/x86/smp.c
new file mode 100644
--- /dev/null
+++ b/user/test/x86/smp.c
@@ -0,0 +1,151 @@
+
+
+#include "smp.h"
+#include "apic.h"
+#include "printf.h"
+
+#define IPI_VECTOR 0x20
+
+static int apic_read(int reg)
+{
+ unsigned short port = APIC_BASE + reg;
+ unsigned v;
+
+ asm volatile ("in %1, %0" : "=a"(v) : "d"(port));
+ return v;
+}
+
+static void apic_write(int reg, unsigned v)
+{
+ unsigned short port = APIC_BASE + reg;
+
+ asm volatile ("out %0, %1" : : "a"(v), "d"(port));
+}
+
+static int apic_get_cpu_count()
+{
+ return apic_read(APIC_REG_NCPU);
+}
+
+static int apic_get_id()
+{
+ return apic_read(APIC_REG_ID);
+}
+
+static void apic_set_ipi_vector(int vector)
+{
+ apic_write(APIC_REG_IPI_VECTOR, vector);
+}
+
+static void apic_send_ipi(int cpu)
+{
+ apic_write(APIC_REG_SEND_IPI, cpu);
+}
+
+static struct spinlock ipi_lock;
+static void (*ipi_function)(void *data);
+static void *ipi_data;
+static volatile int ipi_done;
+
+static __attribute__((used)) void ipi()
+{
+ ipi_function(ipi_data);
+ ipi_done = 1;
+}
+
+asm (
+ "ipi_entry: \n"
+ " call ipi \n"
+#ifndef __x86_64__
+ " iret"
+#else
+ " iretq"
+#endif
+ );
+
+
+static void set_ipi_descriptor(void (*ipi_entry)(void))
+{
+ unsigned short *desc = (void *)(IPI_VECTOR * sizeof(long) * 2);
+ unsigned short cs;
+ unsigned long ipi = (unsigned long)ipi_entry;
+
+ asm ("mov %%cs, %0" : "=r"(cs));
+ desc[0] = ipi;
+ desc[1] = cs;
+ desc[2] = 0x8e00;
+ desc[3] = ipi >> 16;
+#ifdef __x86_64__
+ desc[4] = ipi >> 32;
+ desc[5] = ipi >> 48;
+ desc[6] = 0;
+ desc[7] = 0;
+#endif
+}
+
+void spin_lock(struct spinlock *lock)
+{
+ int v = 1;
+
+ do {
+ asm volatile ("xchg %1, %0" : "+m"(lock->v), "+r"(v));
+ } while (v);
+ asm volatile ("" : : : "memory");
+}
+
+void spin_unlock(struct spinlock *lock)
+{
+ asm volatile ("" : : : "memory");
+ lock->v = 0;
+}
+
+int cpu_count(void)
+{
+ return apic_get_cpu_count();
+}
+
+int smp_id(void)
+{
+ return apic_get_id();
+}
+
+void on_cpu(int cpu, void (*function)(void *data), void *data)
+{
+ spin_lock(&ipi_lock);
+ if (cpu == apic_get_id())
+ function(data);
+ else {
+ ipi_function = function;
+ ipi_data = data;
+ apic_send_ipi(cpu);
+ while (!ipi_done)
+ ;
+ ipi_done = 0;
+ }
+ spin_unlock(&ipi_lock);
+}
+
+static void (*smp_main_func)(void);
+static volatile int smp_main_running;
+
+asm ("smp_init_entry: \n"
+ "incl smp_main_running \n"
+ "sti \n"
+ "call *smp_main_func");
+
+void smp_init(void (*smp_main)(void))
+{
+ int i;
+ void smp_init_entry(void);
+ void ipi_entry(void);
+
+ apic_set_ipi_vector(IPI_VECTOR);
+ set_ipi_descriptor(smp_init_entry);
+ smp_main_func = smp_main;
+ for (i = 1; i < cpu_count(); ++i) {
+ apic_send_ipi(i);
+ while (smp_main_running < i)
+ ;
+ }
+ set_ipi_descriptor(ipi_entry);
+}
diff --git a/user/test/x86/smp.h b/user/test/x86/smp.h
new file mode 100644
--- /dev/null
+++ b/user/test/x86/smp.h
@@ -0,0 +1,16 @@
+#ifndef __SMP_H
+#define __SMP_H
+
+struct spinlock {
+ int v;
+};
+
+void smp_init(void (*smp_main)(void));
+
+int cpu_count(void);
+int smp_id(void);
+void on_cpu(int cpu, void (*function)(void *data), void *data);
+void spin_lock(struct spinlock *lock);
+void spin_unlock(struct spinlock *lock);
+
+#endif
diff --git a/user/test/x86/smptest.c b/user/test/x86/smptest.c
new file mode 100644
--- /dev/null
+++ b/user/test/x86/smptest.c
@@ -0,0 +1,32 @@
+
+#include "smp.h"
+#include "printf.h"
+
+static void ipi_test(void *data)
+{
+ int n = (long)data;
+
+ printf("ipi called, cpu %d\n", n);
+ if (n != smp_id())
+ printf("but wrong cpu %d\n", smp_id());
+}
+
+static void smp_main(void)
+{
+ printf("smp main %d\n", smp_id());
+ while (1)
+ asm volatile ("hlt" : : : "memory");
+}
+
+int main()
+{
+ int ncpus;
+ int i;
+
+ smp_init(smp_main);
+ ncpus = cpu_count();
+ printf("found %d cpus\n", ncpus);
+ for (i = 0; i < ncpus; ++i)
+ on_cpu(i, ipi_test, (void *)(long)i);
+ return 0;
+}
diff --git a/user/test/x86/stringio.S b/user/test/x86/stringio.S
new file mode 100644
--- /dev/null
+++ b/user/test/x86/stringio.S
@@ -0,0 +1,31 @@
+
+.data
+
+.macro str name, value
+
+\name : .long 1f-2f
+2: .ascii "\value"
+1:
+.endm
+
+ str "forward", "forward"
+ str "backward", "backward"
+
+.text
+
+
+ cld
+ movl forward, %ecx
+ lea 4+forward, %rsi
+ movw $1, %dx
+ rep outsb
+
+ std
+ movl backward, %ecx
+ lea 4+backward-1(%rcx), %rsi
+ movw $2, %dx
+ rep outsb
+
+ hlt
+
+
diff --git a/user/test/x86/test32.S b/user/test/x86/test32.S
new file mode 100644
--- /dev/null
+++ b/user/test/x86/test32.S
@@ -0,0 +1,8 @@
+.code32
+
+.text
+
+1:
+ mov $0x12, %al
+ out %al, $0x80
+ jmp 1b
diff --git a/user/test/x86/vm.c b/user/test/x86/vm.c
new file mode 100644
--- /dev/null
+++ b/user/test/x86/vm.c
@@ -0,0 +1,249 @@
+
+#include "vm.h"
+
+void print(const char *s);
+
+#define PAGE_SIZE 4096ul
+#define LARGE_PAGE_SIZE (512 * PAGE_SIZE)
+
+static void *free = 0;
+static void *vfree_top = 0;
+
+static unsigned long virt_to_phys(const void *virt)
+{
+ return (unsigned long)virt;
+}
+
+static void *phys_to_virt(unsigned long phys)
+{
+ return (void *)phys;
+}
+
+void *memset(void *data, int c, unsigned long len)
+{
+ char *s = data;
+
+ while (len--)
+ *s++ = c;
+
+ return data;
+}
+
+static void free_memory(void *mem, unsigned long size)
+{
+ while (size >= PAGE_SIZE) {
+ *(void **)mem = free;
+ free = mem;
+ mem += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+}
+
+void *alloc_page()
+{
+ void *p;
+
+ if (!free)
+ return 0;
+
+ p = free;
+ free = *(void **)free;
+
+ return p;
+}
+
+void free_page(void *page)
+{
+ *(void **)page = free;
+ free = page;
+}
+
+extern char edata;
+static unsigned long end_of_memory;
+
+#define PTE_PRESENT (1ull << 0)
+#define PTE_PSE (1ull << 7)
+#define PTE_WRITE (1ull << 1)
+#define PTE_ADDR (0xffffffffff000ull)
+
+static void install_pte(unsigned long *cr3,
+ int pte_level,
+ void *virt,
+ unsigned long pte)
+{
+ int level;
+ unsigned long *pt = cr3;
+ unsigned offset;
+
+ for (level = 4; level > pte_level; --level) {
+ offset = ((unsigned long)virt >> ((level-1) * 9 + 12)) & 511;
+ if (!(pt[offset] & PTE_PRESENT)) {
+ unsigned long *new_pt = alloc_page();
+ memset(new_pt, 0, PAGE_SIZE);
+ pt[offset] = virt_to_phys(new_pt) | PTE_PRESENT | PTE_WRITE;
+ }
+ pt = phys_to_virt(pt[offset] & 0xffffffffff000ull);
+ }
+ offset = ((unsigned long)virt >> (((level-1) * 9) + 12)) & 511;
+ pt[offset] = pte;
+}
+
+static unsigned long get_pte(unsigned long *cr3, void *virt)
+{
+ int level;
+ unsigned long *pt = cr3, pte;
+ unsigned offset;
+
+ for (level = 4; level > 1; --level) {
+ offset = ((unsigned long)virt >> (((level-1) * 9) + 12)) & 511;
+ pte = pt[offset];
+ if (!(pte & PTE_PRESENT))
+ return 0;
+ if (level == 2 && (pte & PTE_PSE))
+ return pte;
+ pt = phys_to_virt(pte & 0xffffffffff000ull);
+ }
+ offset = ((unsigned long)virt >> (((level-1) * 9) + 12)) & 511;
+ pte = pt[offset];
+ return pte;
+}
+
+static void install_large_page(unsigned long *cr3,
+ unsigned long phys,
+ void *virt)
+{
+ install_pte(cr3, 2, virt, phys | PTE_PRESENT | PTE_WRITE | PTE_PSE);
+}
+
+static void install_page(unsigned long *cr3,
+ unsigned long phys,
+ void *virt)
+{
+ install_pte(cr3, 1, virt, phys | PTE_PRESENT | PTE_WRITE);
+}
+
+static inline void load_cr3(unsigned long cr3)
+{
+ asm ( "mov %0, %%cr3" : : "r"(cr3) );
+}
+
+static inline unsigned long read_cr3()
+{
+ unsigned long cr3;
+
+ asm volatile ( "mov %%cr3, %0" : "=r"(cr3) );
+ return cr3;
+}
+
+static inline void load_cr0(unsigned long cr0)
+{
+ asm volatile ( "mov %0, %%cr0" : : "r"(cr0) );
+}
+
+static inline unsigned long read_cr0()
+{
+ unsigned long cr0;
+
+ asm volatile ( "mov %%cr0, %0" : "=r"(cr0) );
+ return cr0;
+}
+
+static inline void load_cr4(unsigned long cr4)
+{
+ asm volatile ( "mov %0, %%cr4" : : "r"(cr4) );
+}
+
+static inline unsigned long read_cr4()
+{
+ unsigned long cr4;
+
+ asm volatile ( "mov %%cr4, %0" : "=r"(cr4) );
+ return cr4;
+}
+
+struct gdt_table_descr
+{
+ unsigned short len;
+ unsigned long *table;
+} __attribute__((packed));
+
+static inline void load_gdt(unsigned long *table, int nent)
+{
+ struct gdt_table_descr descr;
+
+ descr.len = nent * 8 - 1;
+ descr.table = table;
+ asm volatile ( "lgdt %0" : : "m"(descr) );
+}
+
+#define SEG_CS_32 8
+#define SEG_CS_64 16
+
+struct ljmp {
+ void *ofs;
+ unsigned short seg;
+};
+
+static void setup_mmu(unsigned long len)
+{
+ unsigned long *cr3 = alloc_page();
+ unsigned long phys = 0;
+
+ memset(cr3, 0, PAGE_SIZE);
+ while (phys + LARGE_PAGE_SIZE <= len) {
+ install_large_page(cr3, phys, (void *)phys);
+ phys += LARGE_PAGE_SIZE;
+ }
+ while (phys + PAGE_SIZE <= len) {
+ install_page(cr3, phys, (void *)phys);
+ phys += PAGE_SIZE;
+ }
+
+ load_cr3(virt_to_phys(cr3));
+ print("paging enabled\n");
+}
+
+static unsigned int inl(unsigned short port)
+{
+ unsigned int val;
+ asm volatile("inl %w1, %0" : "=a"(val) : "Nd"(port));
+ return val;
+}
+
+void setup_vm()
+{
+ end_of_memory = inl(0xd1);
+ free_memory(&edata, end_of_memory - (unsigned long)&edata);
+ setup_mmu(end_of_memory);
+}
+
+void *vmalloc(unsigned long size)
+{
+ void *mem, *p;
+ unsigned pages;
+
+ size += sizeof(unsigned long);
+
+ size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
+ vfree_top -= size;
+ mem = p = vfree_top;
+ pages = size / PAGE_SIZE;
+ while (pages--) {
+ install_page(phys_to_virt(read_cr3()), virt_to_phys(alloc_page()), p);
+ p += PAGE_SIZE;
+ }
+ *(unsigned long *)mem = size;
+ mem += sizeof(unsigned long);
+ return mem;
+}
+
+void vfree(void *mem)
+{
+ unsigned long size = ((unsigned long *)mem)[-1];
+
+ while (size) {
+ free_page(phys_to_virt(get_pte(phys_to_virt(read_cr3()), mem) & PTE_ADDR));
+ mem += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+}
diff --git a/user/test/x86/vm.h b/user/test/x86/vm.h
new file mode 100644
--- /dev/null
+++ b/user/test/x86/vm.h
@@ -0,0 +1,9 @@
+#ifndef VM_H
+#define VM_H
+
+void setup_vm();
+
+void *vmalloc(unsigned long size);
+void vfree(void *mem);
+
+#endif
diff --git a/user/test/x86/vmexit.c b/user/test/x86/vmexit.c
new file mode 100644
--- /dev/null
+++ b/user/test/x86/vmexit.c
@@ -0,0 +1,32 @@
+
+#include "printf.h"
+
+static inline unsigned long long rdtsc()
+{
+ long long r;
+
+#ifdef __x86_64__
+ unsigned a, d;
+
+ asm volatile ("rdtsc" : "=a"(a), "=d"(d));
+ r = a | ((long long)d << 32);
+#else
+ asm volatile ("rdtsc" : "=A"(r));
+#endif
+ return r;
+}
+
+#define N (1 << 22)
+
+int main()
+{
+ int i;
+ unsigned long long t1, t2;
+
+ t1 = rdtsc();
+ for (i = 0; i < N; ++i)
+ asm volatile ("cpuid" : : : "eax", "ebx", "ecx", "edx");
+ t2 = rdtsc();
+ printf("vmexit latency: %d\n", (int)((t2 - t1) / N));
+ return 0;
+}