2011 Kernel/VM Advent Calendar 31 日目 @hdk_2: BitVisor を OS に仕立ててみる

カーネル/VM Advent Calendar : ATND 2011-12-31

去年に引き続き参加してみることにしました。今回は、BitVisor ネタです。

BitVisor とは、知ってる人は知ってると思いますが、筑波大学が中心となって開発されたソフトウェアで、PC のセキュリティ向上を目的とした仮想マシンモニターです。

今回は、その BitVisor から仮想マシンモニターなどのコードを削除して、OS として仕立ててしまおうというお話です。カーネルに必要そうな機能はなんとなく用意されているような感じがするので、手っ取り早く OS が作れるのではないかという試みです。

なお、以下の作業は GNU/Linux 環境を想定しています。Debian GNU/Linux で試していますが、他の GNU/Linux 環境でも似たようなものでしょう。

まずはダウンロード

BitVisor | Free Security & Utilities software downloads at SourceForge.net

ここからバージョン 1.2 をダウンロードしましょう。ダウンロードしたら tar コマンドで展開します。

$ tar xf bitvisor-1.2.tar.gz

$ cd bitvisor-1.2

コードをざっくり削る (1)

BitVisor には、セキュリティ向上を目的としたコードが多く含まれています。がっつり削りましょう。

$ rm -r crypto drivers idman storage vpn

$ rm core/iccard.* core/vmmcall_iccard.* core/vpn_ve.* core/builtin/*idman* core/builtin/*storage* core/builtin/*vpn*

これに合わせて Makefile 他、少し編集します。差分を以下に載せておきます。

diff --git a/Makefile b/Makefile --- a/Makefile +++ b/Makefile @@ -105,14 +105,9 @@ FORMAT = elf32-i386 TARGET = $(NAME).elf MAP = $(NAME).map LDS = $(NAME).lds -OBJS-1 = core/core.o drivers/drivers.o storage/storage.o -OBJS-$(CONFIG_CRYPTO_VPN) += vpn/vpn.o -OBJS-$(CONFIG_IDMAN) += idman/idman.o -OBJS-1 += crypto/crypto.a +OBJS-1 = core/core.o OBJS = $(OBJS-1) -CORE-1 = defconfig crypto/crypto.a storage/storage.o -CORE-$(CONFIG_IDMAN) += idman/idman.o -CORE-$(CONFIG_CRYPTO_VPN) += vpn/vpn.o +CORE-1 = defconfig BITS-0 = 32 BITS-1 = 64 @@ -140,10 +135,6 @@ all : $(TARGET) clean : rm -f $(TARGET) $(MAP) $(MAKE) -C core clean - $(MAKE) -C drivers clean - $(MAKE) -C crypto clean - $(MAKE) -C vpn clean - $(MAKE) -C idman clean rm -rf documents/* config : diff --git a/core/Makefile b/core/Makefile --- a/core/Makefile +++ b/core/Makefile @@ -24,7 +24,6 @@ CONSTANTS-$(CONFIG_FWDBG) += -DFWDBG CONSTANTS-$(CONFIG_ACPI_DSDT) += -DACPI_DSDT CONSTANTS-$(CONFIG_DISABLE_SLEEP) += -DDISABLE_SLEEP CONSTANTS-$(CONFIG_ENABLE_ASSERT) += -DENABLE_ASSERT -CONSTANTS-$(CONFIG_CARDSTATUS) += -DCARDSTATUS CONSTANTS-$(CONFIG_IDMAN) += -DIDMAN CONSTANTS-$(CONFIG_VPN_VE) += -DVPN_VE CONSTANTS-$(CONFIG_DISABLE_TCG_BIOS) += -DDISABLE_TCG_BIOS diff --git a/core/acpi.c b/core/acpi.c --- a/core/acpi.c +++ b/core/acpi.c @@ -509,6 +509,7 @@ acpi_init_global (void) memcpy (&rsdp_copy, p, sizeof *p); rsdp_found = true; +#if 0 r=find_entry(DMAR_SIGNATURE); if (!r) { printf ("ACPI DMAR not found.\n"); @@ -523,6 +524,7 @@ acpi_init_global (void) for (i=0 ; i<MAX_IO_DOM ; i++) dom_io[i]=create_dom(i) ; } +#endif q = find_facp (); if (!q) { diff --git a/core/builtin/Makefile b/core/builtin/Makefile --- a/core/builtin/Makefile +++ b/core/builtin/Makefile @@ -25,8 +25,7 @@ AS_2 = $(AS_2-$(CONFIG_64)) OUT_OBJ = process_builtin.o OUT_LIB = lib.a OUT_ASM = process_builtin.s -OTHER_LIBS = ../../storage/lib/libstorage.a ../../idman/libidman.a \ - ../../vpn/libvpn.a ../../crypto/crypto.a +OTHER_LIBS = .PHONY : all clean diff --git a/core/vmmcall_boot.c b/core/vmmcall_boot.c --- a/core/vmmcall_boot.c +++ b/core/vmmcall_boot.c @@ -40,7 +40,6 @@ #include "string.h" #include "vmmcall.h" #include "vmmcall_boot.h" -#include "../crypto/decryptcfg.h" struct loadcfg_data { u32 len; @@ -134,7 +133,7 @@ loadcfg (void) ASSERT (data); tmpbuf = alloc (d->datalen); ASSERT (tmpbuf); -#ifdef CRYPTO_VPN +#if 0 decryptcfg (pass, d->passlen, data, d->datalen, tmpbuf); #else

panic ("cannot decrypt");

ここまで消しても、何もしない VMM としてまだ動作するはずです。

コードをざっくり削る (2)

さて、今回は VMM の機能もいらないわけですから、さらに削ります。ヘッダーファイルもいらないものは消しましょう。

$ rm core/*pass*

$ rm core/vt* core/svm* core/vcpu.* core/vmctl.h core/vmm* core/cpu_* core/cpuid.* core/io_io* core/mmio.* core/msr.* core/xsetbv.* core/gmm* core/guest_bioshook.* core/current.* core/loadbootsector.*

$ rm core/wakeup* include/IDMan.h include/storage* include/core/mmio.h core/vpnsys.h include/core/vpnsys.h

$ rm -r include/passthrough/

このくらい消すとずいぶんすっきりしてきますが、このままではコンパイルが通りませんので、いろいろ直さなければなりません。差分を以下に載せます。

diff --git a/core/acpi.c b/core/acpi.c --- a/core/acpi.c +++ b/core/acpi.c @@ -29,20 +29,16 @@ #include "acpi.h" #include "acpi_dsdt.h" +#include "asm.h" #include "assert.h" #include "beep.h" #include "constants.h" -#include "current.h" #include "initfunc.h" -#include "io_io.h" #include "mm.h" #include "panic.h" #include "printf.h" #include "sleep.h" #include "string.h" -#include "wakeup.h" -#include "passthrough/vtd.h" -#include "passthrough/iodom.h" #define FIND_RSDP_NOT_FOUND 0xFFFFFFFFFFFFFFFFULL #define RSDP_SIGNATURE "RSD PTR" @@ -305,6 +301,7 @@ debug_dump (void *p, int len) } } +#if 0 static bool acpi_pm1_sleep (u32 v) { @@ -408,6 +405,7 @@ acpi_iohook (void) set_iofunc (smi_cmd, acpi_smi_monitor); } } +#endif static void get_pm1a_cnt_ioaddr (struct facp *q) @@ -493,10 +491,7 @@ acpi_init_global (void) u64 rsdp; struct rsdp *p; struct facp *q; - struct acpi_ent_dmar *r; - struct domain *create_dom() ; - wakeup_init (); rsdp_found = false; pm1a_cnt_found = false; diff --git a/core/acpi.h b/core/acpi.h --- a/core/acpi.h +++ b/core/acpi.h @@ -37,9 +37,7 @@ struct acpi_data { bool smi_hook_disabled; }; -void acpi_iohook (void); void acpi_poweroff (void); bool get_acpi_time_raw (u32 *r); -void acpi_smi_hook (void); #endif diff --git a/core/callrealmode.c b/core/callrealmode.c --- a/core/callrealmode.c +++ b/core/callrealmode.c @@ -31,7 +31,6 @@ #include "assert.h" #include "callrealmode.h" #include "callrealmode_asm.h" -#include "current.h" #include "entry.h" #include "initfunc.h" #include "mm.h" @@ -39,9 +38,6 @@ #include "savemsr.h" #include "seg.h" #include "string.h" -#include "vmmcall_boot.h" - -static struct vcpu *callrealmode_vcpu; u32 callrealmode_endofcodeaddr (void) @@ -49,6 +45,7 @@ callrealmode_endofcodeaddr (void) return CALLREALMODE_OFFSET + callrealmode_end - callrealmode_start; } +#if 0 static void callrealmode_clearvcpu (void) { @@ -72,6 +69,7 @@ callrealmode_usevcpu (struct vcpu *p) { callrealmode_vcpu = p; } +#endif static void callrealmode_copy (void) @@ -80,6 +78,7 @@ callrealmode_copy (void) callrealmode_end - callrealmode_start); } +#if 0 static void callrealmode_call_vcpu (struct vcpu *c, struct callrealmode_data *d) { @@ -131,6 +130,7 @@ callrealmode_call_vcpu (struct vcpu *c, memcpy (d, p, sizeof *d); unmapmem (p, size); } +#endif /* interrupts must be disabled */ static void @@ -237,10 +237,7 @@ callrealmode_call_directly (struct callr static void callrealmode_call (struct callrealmode_data *d) { - if (callrealmode_vcpu) - callrealmode_call_vcpu (callrealmode_vcpu, d); - else - callrealmode_call_directly (d); + callrealmode_call_directly (d); } void @@ -380,6 +377,3 @@ callrealmode_startkernel32 (u32 paramsad d.u.startkernel32.startaddr = startaddr; callrealmode_call (&d); } - -INITFUNC ("global0", callrealmode_init_global); -INITFUNC ("panic0", callrealmode_panic); diff --git a/core/debug.c b/core/debug.c --- a/core/debug.c +++ b/core/debug.c @@ -28,9 +28,7 @@ */ #include "constants.h" -#include "cpu_mmu.h" #include "debug.h" -#include "gmm_access.h" #include "i386-stub.h" #include "int.h" #include "mm.h" @@ -40,7 +38,6 @@ #include "serial.h" #include "string.h" #include "types.h" -#include "vmmerr.h" static int memdump, memfree; #ifdef FWDBG @@ -95,12 +92,6 @@ debug_addstr (char *str) static void memdump_gphys (void *data) { - struct memdump_gphys_data *d; - int i; - - d = data; - for (i = 0; i < d->sendlen; i++) - read_gphys_b (d->physaddr + i, &d->q[i], 0); } static void @@ -115,6 +106,7 @@ memdump_hvirt (void *data) static void memdump_gvirt (void *data) { +#if 0 struct memdump_gvirt_data *dd; struct memdump_data *d; int i; @@ -138,6 +130,7 @@ memdump_gvirt (void *data) } read_gphys_b (physaddr, &dd->q[i], 0); } +#endif } static int diff --git a/core/main.c b/core/main.c --- a/core/main.c +++ b/core/main.c @@ -32,12 +32,10 @@ #include "callrealmode.h" #include "config.h" #include "convert.h" -#include "current.h" #include "debug.h" #include "initfunc.h" #include "keyboard.h" #include "linkage.h" -#include "loadbootsector.h" #include "main.h" #include "mm.h" #include "multiboot.h" @@ -49,23 +47,11 @@ #include "regs.h" #include "sleep.h" #include "string.h" -#include "svm.h" -#include "svm_init.h" #include "types.h" -#include "vcpu.h" -#include "vmmcall.h" -#include "vmmcall_boot.h" #include "vramwrite.h" -#include "vt.h" -#include "vt_init.h" static struct multiboot_info mi; -static u32 minios_startaddr; -static u32 minios_paramsaddr; -static u8 minios_params[OSLOADER_BOOTPARAMS_SIZE]; -static void *bios_data_area; static int shiftkey; -static u8 imr_master, imr_slave; static void print_boot_msg (void) @@ -75,6 +61,7 @@ print_boot_msg (void) printf ("All rights reserved.\n"); } +#if 0 static void print_startvm_msg (void) { @@ -87,6 +74,7 @@ load_drivers (void) printf ("Loading drivers.\n"); call_initfunc ("driver"); } +#endif static u8 detect_bios_boot_device (struct multiboot_info *mi) @@ -99,6 +87,7 @@ detect_bios_boot_device (struct multiboo } } +#if 0 static void copy_minios (void) { @@ -229,6 +218,7 @@ initregs (void) current->vmctl.write_flags (RFLAGS_ALWAYS1_BIT); current->vmctl.write_idtr (0, 0x3FF); } +#endif static void sync_cursor_pos (void) @@ -239,6 +229,7 @@ sync_cursor_pos (void) callrealmode_setcursorpos (0, row, col); } +#if 0 static void save_bios_data_area (void) { @@ -399,6 +390,7 @@ resume_vm (u32 wake_addr) current->vmctl.start_vm (); panic ("VM stopped."); } +#endif static void get_shiftflags (void) @@ -425,6 +417,7 @@ ap_proc (void) { call_initfunc ("ap"); call_initfunc ("pcpu"); + asm_cli_and_hlt (); } static void @@ -432,6 +425,15 @@ bsp_proc (void) { call_initfunc ("bsp"); call_initfunc ("pcpu"); + for (;;) { + int d; + + d = newprocess ("init"); + debug_msgregister (); + msgsendint (d, 1); + debug_msgunregister (); + msgclose (d); + } } asmlinkage void @@ -444,9 +446,6 @@ vmm_main (struct multiboot_info *mi_arg) } INITFUNC ("pcpu1", sync_all_processors); -INITFUNC ("pcpu2", virtualization_init_pcpu); -INITFUNC ("pcpu5", create_pass_vm); INITFUNC ("bsp0", debug_on_shift_key); INITFUNC ("global1", print_boot_msg); -INITFUNC ("global3", copy_minios); INITFUNC ("global3", get_shiftflags); diff --git a/core/main.h b/core/main.h --- a/core/main.h +++ b/core/main.h @@ -30,6 +30,4 @@ #ifndef _CORE_MAIN_H #define _CORE_MAIN_H -void resume_vm (u32 wake_addr); - #endif diff --git a/core/mm.c b/core/mm.c --- a/core/mm.c +++ b/core/mm.c @@ -32,9 +32,7 @@ #include "callrealmode.h" #include "comphappy.h" #include "constants.h" -#include "current.h" #include "entry.h" -#include "gmm_access.h" #include "initfunc.h" #include "int.h" #include "linkage.h" @@ -46,7 +44,7 @@ #include "spinlock.h" #include "string.h" -#define VMMSIZE_ALL (128 * 1024 * 1024) +#define VMMSIZE_ALL (32 * 1024 * 1024) #define NUM_OF_PAGES (VMMSIZE_ALL >> PAGESIZE_SHIFT) #define NUM_OF_ALLOCSIZE 13 #define MAPMEM_ADDR_START 0xF0000000 @@ -1456,6 +1454,7 @@ pmap_open_vmm (pmap_t *m, ulong cr3, int m->type = PMAP_TYPE_VMM; } +#if 0 void pmap_open_guest (pmap_t *m, ulong cr3, int levels, bool atomic) { @@ -1473,6 +1472,7 @@ pmap_open_guest (pmap_t *m, ulong cr3, i else m->type = PMAP_TYPE_GUEST; } +#endif void pmap_close (pmap_t *m) @@ -1521,12 +1521,6 @@ pmap_rd32 (pmap_t *m, u64 phys, u32 attr case PMAP_TYPE_VMM: r = *(u32 *)phys_to_virt (phys); break; - case PMAP_TYPE_GUEST: - read_gphys_l (phys, &r, attr); - break; - case PMAP_TYPE_GUEST_ATOMIC: - cmpxchg_gphys_l (phys, &r, r, attr); - break; } return r; } @@ -1540,12 +1534,6 @@ pmap_rd64 (pmap_t *m, u64 phys, u32 attr case PMAP_TYPE_VMM: r = *(u64 *)phys_to_virt (phys); break; - case PMAP_TYPE_GUEST: - read_gphys_q (phys, &r, attr); - break; - case PMAP_TYPE_GUEST_ATOMIC: - cmpxchg_gphys_q (phys, &r, r, attr); - break; } return r; } @@ -1553,22 +1541,12 @@ pmap_rd64 (pmap_t *m, u64 phys, u32 attr static bool pmap_wr32 (pmap_t *m, u64 phys, u32 attr, u64 oldentry, u64 *entry) { - u32 tmp; bool r = false; switch (m->type) { case PMAP_TYPE_VMM: *(u32 *)phys_to_virt (phys) = *entry; break; - case PMAP_TYPE_GUEST: - write_gphys_l (phys, *entry, attr); - break; - case PMAP_TYPE_GUEST_ATOMIC: - tmp = oldentry; - r = cmpxchg_gphys_l (phys, &tmp, *entry, attr); - if (r) - *entry = tmp; - break; } return r; } @@ -1582,14 +1560,6 @@ pmap_wr64 (pmap_t *m, u64 phys, u32 attr case PMAP_TYPE_VMM: *(u64 *)phys_to_virt (phys) = *entry; break; - case PMAP_TYPE_GUEST: - write_gphys_q (phys, *entry, attr); - break; - case PMAP_TYPE_GUEST_ATOMIC: - r = cmpxchg_gphys_q (phys, &oldentry, *entry, attr); - if (r) - *entry = oldentry; - break; } return r; } @@ -1869,6 +1839,7 @@ mapped_hphys_addr (u64 hphys, uint len) return (void *)(virt_t)(HPHYS_ADDR + hphys); } +#if 0 static void * mapped_gphys_addr (u64 gphys, uint len, int flags) { @@ -1896,6 +1867,7 @@ mapped_gphys_addr (u64 gphys, uint len, } return mapped_hphys_addr (hphys, len); } +#endif static void * mapmem_alloc (pmap_t *m, uint offset, uint len) @@ -1949,12 +1921,6 @@ mapmem_domap (pmap_t *m, void *virt, int pmap_seek (m, v + (i << PAGESIZE_SHIFT), 1); if (flags & MAPMEM_HPHYS) { pte = (p + (i << PAGESIZE_SHIFT)) | PTE_P_BIT; - } else if (flags & MAPMEM_GPHYS) { - pte = current->gmm.gp2hp (p + (i << PAGESIZE_SHIFT), - &fakerom); - if (fakerom && (flags & MAPMEM_WRITE)) - return true; - pte = (pte & ~PAGESIZE_MASK) | PTE_P_BIT; } else { return true; } @@ -2015,11 +1981,6 @@ mapmem (int flags, u64 physaddr, uint le if (!r) goto skip; return r; - } else if (flags & MAPMEM_GPHYS) { - r = mapped_gphys_addr (physaddr, len, flags); - if (!r) - goto skip; - return r; } return NULL; skip: @@ -2043,12 +2004,6 @@ mapmem_hphys (u64 physaddr, uint len, in return mapmem (MAPMEM_HPHYS | flags, physaddr, len); } -void * -mapmem_gphys (u64 physaddr, uint len, int flags) -{ - return mapmem (MAPMEM_GPHYS | flags, physaddr, len); -} - /* Flush all write back caches including other processors */ void mm_flush_wb_cache (void) diff --git a/core/mm.h b/core/mm.h --- a/core/mm.h +++ b/core/mm.h @@ -48,8 +48,6 @@ enum pmap_type { PMAP_TYPE_VMM, - PMAP_TYPE_GUEST, - PMAP_TYPE_GUEST_ATOMIC, }; typedef struct { @@ -90,7 +88,6 @@ phys_t mm_process_switch (phys_t switcht /* accessing page tables */ void pmap_open_vmm (pmap_t *m, ulong cr3, int levels); -void pmap_open_guest (pmap_t *m, ulong cr3, int levels, bool atomic); void pmap_close (pmap_t *m); int pmap_getreadlevel (pmap_t *m); diff --git a/core/panic.c b/core/panic.c --- a/core/panic.c +++ b/core/panic.c @@ -31,7 +31,6 @@ #include "callrealmode.h" #include "config.h" #include "cpu.h" -#include "current.h" #include "debug.h" #include "initfunc.h" #include "keyboard.h" @@ -41,6 +40,7 @@ #include "printf.h" #include "process.h" #include "reboot.h" +#include "regs.h" #include "sleep.h" #include "spinlock.h" #include "stdarg.h" @@ -287,6 +287,7 @@ dump_vmm_other_regs (void) backtrace (); } +#if 0 static void dump_vm_general_regs (void) { @@ -406,6 +407,7 @@ dump_vm_other_regs (void) if (current->vmctl.panic_dump) current->vmctl.panic_dump (); } +#endif static void wait_for_other_cpu (void) @@ -435,15 +437,6 @@ panic_nomsg (bool w) dump_vmm_other_regs (); printf ("------------------------------------------------\n"); } - if (currentcpu_available () && current) { - printf ("Guest state and registers of cpu %d ------------\n", - get_cpu_id ()); - dump_vm_general_regs (); - dump_vm_control_regs (); - dump_vm_sregs (); - dump_vm_other_regs (); - printf ("------------------------------------------------\n"); - } if (!w && do_wakeup) { do_wakeup = false; sleep_set_timer_counter (); diff --git a/core/pcpu.h b/core/pcpu.h --- a/core/pcpu.h +++ b/core/pcpu.h @@ -34,10 +34,8 @@ #include "desc.h" #include "seg.h" #include "spinlock.h" -#include "svm.h" #include "thread.h" #include "types.h" -#include "vt.h" #define NUM_OF_SEGDESCTBL 32 #define PCPU_GS_ALIGN __attribute__ ((aligned (8))) @@ -57,8 +55,6 @@ struct pcpu { struct segdesc segdesctbl[NUM_OF_SEGDESCTBL]; struct tss32 tss32; struct tss64 tss64; - struct vt_pcpu_data vt; - struct svm_pcpu_data svm; enum fullvirtualize_type fullvirtualize; int cpunum; int pid; diff --git a/core/serial.c b/core/serial.c --- a/core/serial.c +++ b/core/serial.c @@ -29,7 +29,6 @@ #include "asm.h" #include "initfunc.h" -#include "io_io.h" #include "process.h" #include "serial.h" #include "types.h" @@ -171,7 +170,7 @@ serial_init_iohook (void) unsigned int i; for (i = PORT; i < PORT + NUM_OF_PORT; i++) - set_iofunc (i, do_io_nothing); + ; } static void diff --git a/core/serial.h b/core/serial.h --- a/core/serial.h +++ b/core/serial.h @@ -33,6 +33,5 @@ void serial_putDebugChar (int c); int serial_getDebugChar (void); void serial_putchar (unsigned char c); -void serial_init_iohook (void); #endif diff --git a/core/time.c b/core/time.c --- a/core/time.c +++ b/core/time.c @@ -42,7 +42,6 @@ #include "sleep.h" #include "spinlock.h" #include "time.h" -#include "vmmcall_status.h" static u64 volatile lasttime; static u64 lastacpitime; @@ -173,6 +172,7 @@ time_init_msg (void) msgregister ("time", time_msghandler); } +#if 0 static char * time_status (void) { @@ -198,6 +198,7 @@ time_init_global_status (void) { register_status_callback (time_status); } +#endif static void time_init_global (void) @@ -208,6 +209,5 @@ time_init_global (void) } INITFUNC ("global3", time_init_global); -INITFUNC ("global4", time_init_global_status); INITFUNC ("pcpu3", time_init_pcpu); INITFUNC ("msg0", time_init_msg); diff --git a/include/core/mm.h b/include/core/mm.h --- a/include/core/mm.h +++ b/include/core/mm.h @@ -33,7 +33,6 @@ #include <core/types.h> #define MAPMEM_HPHYS 0x1 -#define MAPMEM_GPHYS 0x2 #define MAPMEM_WRITE 0x4 #define MAPMEM_PWT 0x8 #define MAPMEM_PCD 0x10 @@ -59,6 +58,5 @@ void mempool_freemem (struct mempool *mp void unmapmem (void *virt, uint len); void *mapmem (int flags, u64 physaddr, uint len); void *mapmem_hphys (u64 physaddr, uint len, int flags); -void *mapmem_gphys (u64 physaddr, uint len, int flags); #endif

動かしてみる

さて make、といきたいところですが、その前に。

    • x86 以外のプロセッサーでは動作しません。

    • Pentium III 以前の古いプロセッサーでは、動かないかも知れません。

    • PAE に対応していないプロセッサー (最近では初代 Pentium M など) では、core/Makefile の中にある -DUSE_PAE オプションを削除しておく必要があります。あと、core/entry.s の中の test $CPUID_1_EDX_PAE_BIT,%edx という行の次の je 命令のところを消す必要があります。それで動くんじゃないかと思います。

    • 64 ビットに対応していないプロセッサー (最近では Atom Z520 など) では、make の前に make config コマンドを実行して、一番上の CONFIG_64 のところに * 印がついていたらスペースキーを押して消して、Enter キーを押してください。Debian/Ubuntu の場合、32 ビット環境でも 64 ビットのバイナリーの生成ができますので特に注意してください。

ついでに、去年書いたようにフロッピーイメージで試したい方もいると思いますので、以下の修正をしておくといいでしょう。これで、バイナリーの大きさをかなり小さくできます。

diff --git a/bitvisor.lds b/bitvisor.lds --- a/bitvisor.lds +++ b/bitvisor.lds @@ -22,8 +22,8 @@ SECTIONS { __initfunc_start = .; *(.initfunc) __initfunc_end = .; - /* } - .bss : AT (phys + (bss - head)) { */ + } + .bss : AT (phys + (bss - head)) { bss = .; *(.bss) *(COMMON)

これで、make すると bitvisor.elf というバイナリーが出来上がります。これは、GNU GRUB の Multiboot に対応していますので、GNU GRUB から読み込ませると起動できます。GNU GRUB Legacy の場合のコマンド例:

kernel /bitvisor.elf

GNU GRUB2 の場合のコマンド例:

multiboot /bitvisor.elf

ついでに、GNU GRUB2 で今回の OS を起動できる CD イメージを作成する方法を紹介しておきます。GNU GRUB2 と xorriso をあらかじめインストールしておきます。bitvisor.elf が出来上がったら、以下のようなコマンドを叩きます。すると、cd.img というファイルに CD イメージが出来上がります。

mkdir -p work/boot/grub

{ echo 'menuentry "a" {'; echo 'multiboot /bitvisor.elf'; echo '}'; } > work/boot/grub/grub.cfg

cp bitvisor.elf work

grub-mkrescue -o cd.img work

カーネルとプロセス

さて、実行すると謎のプロンプト > が現れ、簡単なコマンドを受け付けるようになっていると思います。

    • 注意: PC によっては、キーボード入力が正常にできない場合があります。キーを素早く操作すると、それ以降のキー入力ができなくなる PC があることが分かっています。次の節にある、キー入力を BIOS 経由にする変更を行えば、そのような PC でも入力ができるようになります。

実はこのシェル、すでにプロセスとして ring 3 で動いています。コマンド、例えば debug と入力して Enter を叩けば、debug というプロセスが生成・実行されます。

プロセスのソースコードは、core/builtin/ 以下にあります。bin_ で始まるファイル名が、コマンドのソースコードで、lib_ で始まるものが、ライブラリーとして各コマンドにリンクされるものです。

$ ls core/builtin/

Makefile bin_sendint.c lib_lineinput.c lib_spinlock.h

bin_debug.c bin_serialtest.c lib_lineinput.h lib_stdlib.c

bin_help.c bin_shell.c lib_mm.c lib_stdlib.h

bin_init.c lib_arith.s lib_mm.h lib_string.h

bin_log.c lib_assert.c lib_printf.c lib_string.s

bin_panic.c lib_assert.h lib_printf.h lib_syscalls.c

bin_recvexample.c lib_ctype.c lib_putchar.c lib_syscalls.h

bin_sendexample.c lib_ctype.h lib_putchar.h longmode.h

システムコールは独自仕様ですが、簡単なメッセージパッシング的なインターフェースが実装されており、すでに OS っぽく見えるかと思います。

じゃあ、OS として見た時に何が足りないのか? というと、いろいろ足りないのですが、例えば以下のようなものがあるでしょう。

    • 割り込み管理: BitVisor は、仮想マシンモニターではありますが、セキュリティ向上に必要な部分だけを仮想化する構造になっており、割り込み処理はすべてゲスト OS にまかせるしくみになっています。そのため、BitVisor は割り込みを禁止した状態で動作しており、割り込みを受けて割り込みコントローラーに EOI コマンドを出すような機能は入っていません。

    • プリエンプション: プロセスやカーネルスレッドの機能はありますが、割り込み処理をゲスト OS にまかせている関係で、プリエンプションができず、いわゆる擬似マルチタスクとなります。

    • カーネルのメモリー管理: BitVisor は、起動時に自分用の RAM を確保し、残りはゲスト OS にすべて譲る構造になっています。そのため、通常の OS のように、システムの RAM すべてを管理するようにはなっていません。

    • プロセスのメモリー管理: プロセスのメモリー管理は極めてシンプルなものです。必要になるメモリー使用量分の配列をあらかじめ確保しておき、そこから確保/解放を行う alloc()/free() 関数があります。動的に領域を拡張できるような機能は実装されていません。

    • 外部記憶装置の取り扱い: BIOS を呼び出してストレージからセクターを読み取る機能だけは、実装されていますが、プロセスへの機能提供はありません。

    • ファイルシステム: ファイルシステムっぽいものはいっさいありません。

    • デバイスドライバー: セキュリティ関連のデバイスドライバーがありますが、基本的にはゲスト OS が動作していることが前提の特殊なデバイスドライバーであるため、今回は最初の段階で削っています。デバッグ用に、シリアルポート入出力、PS/2 キーボード入力、および、CGA テキスト出力機能があります。

    • 浮動小数点演算命令: BitVisor は、自身が浮動小数点演算命令を使わないことによって、浮動小数点レジスターの保存・読み込みを省略しています。間違えて使ってしまうと誤動作の原因となるため、浮動小数点演算命令を使うと例外が発生するよう、CR0 の TS ビットを立てた状態にしています。浮動小数点演算命令を使いたければ、TS ビットをクリアし、浮動小数点レジスターの適切な取り扱いを実装する必要があります。

    • 外部からのプロセス読み込み: 現在のプロセスは、bitvisor.elf にすべて埋め込まれており、ストレージなど外部から読み込む機能がありません。

キーボード入力

キーボード入力を BIOS で行うようにしてみます。リアルモードに戻って BIOS を呼び出す機能はすでに実装されているので、そこに機能を追加してやればいいわけです。BSP (起動時のプロセッサー) 以外では BIOS 呼び出しができませんが、今のところキー入力を行っているのは BSP なので、大丈夫です。差分を以下に載せておきます。

diff --git a/core/callrealmode.c b/core/callrealmode.c --- a/core/callrealmode.c +++ b/core/callrealmode.c @@ -377,3 +377,13 @@ callrealmode_startkernel32 (u32 paramsad d.u.startkernel32.startaddr = startaddr; callrealmode_call (&d); } + +int +callrealmode_getkey (void) +{ + struct callrealmode_data d; + + d.func = CALLREALMODE_FUNC_GETKEY; + callrealmode_call (&d); + return d.u.getkey.ax_ret; +} diff --git a/core/callrealmode.h b/core/callrealmode.h --- a/core/callrealmode.h +++ b/core/callrealmode.h @@ -91,5 +91,6 @@ bool callrealmode_bootcd_getstatus (u8 d void callrealmode_setcursorpos (u8 page_num, u8 row, u8 column); void callrealmode_startkernel32 (u32 paramsaddr, u32 startaddr); void callrealmode_tcgbios (u32 al, struct tcgbios_args *args); +int callrealmode_getkey (void); #endif diff --git a/core/callrealmode_asm.h b/core/callrealmode_asm.h --- a/core/callrealmode_asm.h +++ b/core/callrealmode_asm.h @@ -48,6 +48,7 @@ enum callrealmode_func { CALLREALMODE_FUNC_SETCURSORPOS = 0x8, CALLREALMODE_FUNC_STARTKERNEL32 = 0x9, CALLREALMODE_FUNC_TCGBIOS = 0xA, + CALLREALMODE_FUNC_GETKEY = 0xB, }; struct callrealmode_printmsg { @@ -106,6 +107,10 @@ struct callrealmode_tcgbios { u32 al; } __attribute__ ((packed)); +struct callrealmode_getkey { + u16 ax_ret; +}; + struct callrealmode_data { enum callrealmode_func func : 32; union { @@ -119,6 +124,7 @@ struct callrealmode_data { struct callrealmode_setcursorpos setcursorpos; struct callrealmode_startkernel32 startkernel32; struct callrealmode_tcgbios tcgbios; + struct callrealmode_getkey getkey; } u; } __attribute__ ((packed)); diff --git a/core/callrealmode_asm.s b/core/callrealmode_asm.s --- a/core/callrealmode_asm.s +++ b/core/callrealmode_asm.s @@ -39,6 +39,7 @@ CALLREALMODE_FUNC_SETCURSORPOS = 0x8 CALLREALMODE_FUNC_STARTKERNEL32 = 0x9 CALLREALMODE_FUNC_TCGBIOS = 0xA + CALLREALMODE_FUNC_GETKEY = 0xB SEG_SEL_CODE_REAL = 0x0000 SEG_SEL_DATA_REAL = 0x0000 @@ -172,6 +173,7 @@ callrealmode_switch: OFF_TCGBIOS_IN_ES = 0x60 OFF_TCGBIOS_IN_DS = 0x64 OFF_TCGBIOS_AL = 0x68 + OFF_GETKEY_AX_RET = 0x30 # Which function? mov OFF_FUNC(%bp),%ax @@ -197,6 +199,8 @@ callrealmode_switch: je startkernel32 cmp $CALLREALMODE_FUNC_TCGBIOS,%ax je tcgbios + cmp $CALLREALMODE_FUNC_GETKEY,%ax + je getkey # Error! cld mov $(errormsg_data-1-callrealmode_start+CALLREALMODE_OFFSET),%di @@ -424,6 +428,17 @@ tcgbios: mov %ds,OFF_TCGBIOS_OUT_DS(%bp) ret +# getkey +# +getkey: + # Enable interrupts + sti + # Call int $0x16 + mov $0x00,%ah + int $0x16 + mov %ax,OFF_GETKEY_AX_RET(%bp) + ret + # Subroutines # paging_and_protection_off: diff --git a/core/keyboard.c b/core/keyboard.c --- a/core/keyboard.c +++ b/core/keyboard.c @@ -28,6 +28,7 @@ */ #include "asm.h" +#include "callrealmode.h" #include "initfunc.h" #include "keyboard.h" #include "process.h" @@ -186,8 +187,11 @@ keyboard_getchar (void) spinlock_lock (&keyboard_lock); retry: - key = keyboard_getkey (); - c = keycode_to_ascii (key); + c = callrealmode_getkey (); + if (c & 0xFF) + c &= 0xFF; + else + c = keycode_to_ascii (c >> 8); if (c < 0) goto retry; spinlock_unlock (&keyboard_lock);

BIOS で現在時刻を取得

BIOS で現在時刻を取得する機能を追加してみます。動作確認用に、メッセージパッシング機能を使用します。

diff --git a/core/callrealmode.c b/core/callrealmode.c --- a/core/callrealmode.c +++ b/core/callrealmode.c @@ -31,6 +31,7 @@ #include "assert.h" #include "callrealmode.h" #include "callrealmode_asm.h" +#include "convert.h" #include "entry.h" #include "initfunc.h" #include "mm.h" @@ -387,3 +388,14 @@ callrealmode_getkey (void) callrealmode_call (&d); return d.u.getkey.ax_ret; } + +int +callrealmode_getsystemtime (u32 *ticks) +{ + struct callrealmode_data d; + + d.func = CALLREALMODE_FUNC_GETSYSTEMTIME; + callrealmode_call (&d); + conv16to32 (d.u.getsystemtime.dx_ret, d.u.getsystemtime.cx_ret, ticks); + return d.u.getsystemtime.al_ret; +} diff --git a/core/callrealmode.h b/core/callrealmode.h --- a/core/callrealmode.h +++ b/core/callrealmode.h @@ -92,5 +92,6 @@ void callrealmode_setcursorpos (u8 page_ void callrealmode_startkernel32 (u32 paramsaddr, u32 startaddr); void callrealmode_tcgbios (u32 al, struct tcgbios_args *args); int callrealmode_getkey (void); +int callrealmode_getsystemtime (u32 *ticks); #endif diff --git a/core/callrealmode_asm.h b/core/callrealmode_asm.h --- a/core/callrealmode_asm.h +++ b/core/callrealmode_asm.h @@ -49,6 +49,7 @@ enum callrealmode_func { CALLREALMODE_FUNC_STARTKERNEL32 = 0x9, CALLREALMODE_FUNC_TCGBIOS = 0xA, CALLREALMODE_FUNC_GETKEY = 0xB, + CALLREALMODE_FUNC_GETSYSTEMTIME = 0xC, }; struct callrealmode_printmsg { @@ -111,6 +112,12 @@ struct callrealmode_getkey { u16 ax_ret; }; +struct callrealmode_getsystemtime { + u16 cx_ret; + u16 dx_ret; + u8 al_ret; +} __attribute__ ((packed)); + struct callrealmode_data { enum callrealmode_func func : 32; union { @@ -125,6 +132,7 @@ struct callrealmode_data { struct callrealmode_startkernel32 startkernel32; struct callrealmode_tcgbios tcgbios; struct callrealmode_getkey getkey; + struct callrealmode_getsystemtime getsystemtime; } u; } __attribute__ ((packed)); diff --git a/core/callrealmode_asm.s b/core/callrealmode_asm.s --- a/core/callrealmode_asm.s +++ b/core/callrealmode_asm.s @@ -40,6 +40,7 @@ CALLREALMODE_FUNC_STARTKERNEL32 = 0x9 CALLREALMODE_FUNC_TCGBIOS = 0xA CALLREALMODE_FUNC_GETKEY = 0xB + CALLREALMODE_FUNC_GETSYSTEMTIME = 0xC SEG_SEL_CODE_REAL = 0x0000 SEG_SEL_DATA_REAL = 0x0000 @@ -174,6 +175,9 @@ callrealmode_switch: OFF_TCGBIOS_IN_DS = 0x64 OFF_TCGBIOS_AL = 0x68 OFF_GETKEY_AX_RET = 0x30 + OFF_GETSYSTEMTIME_CX_RET = 0x30 + OFF_GETSYSTEMTIME_DX_RET = 0x32 + OFF_GETSYSTEMTIME_AL_RET = 0x34 # Which function? mov OFF_FUNC(%bp),%ax @@ -201,6 +205,8 @@ callrealmode_switch: je tcgbios cmp $CALLREALMODE_FUNC_GETKEY,%ax je getkey + cmp $CALLREALMODE_FUNC_GETSYSTEMTIME,%ax + je getsystemtime # Error! cld mov $(errormsg_data-1-callrealmode_start+CALLREALMODE_OFFSET),%di @@ -439,6 +445,19 @@ getkey: mov %ax,OFF_GETKEY_AX_RET(%bp) ret +# getsystemtime +# +getsystemtime: + # Enable interrupts + sti + # Call int $0x16 + mov $0x00,%ah + int $0x1A + mov %cx,OFF_GETSYSTEMTIME_CX_RET(%bp) + mov %dx,OFF_GETSYSTEMTIME_DX_RET(%bp) + mov %al,OFF_GETSYSTEMTIME_AL_RET(%bp) + ret + # Subroutines # paging_and_protection_off: diff --git a/core/systemtime.c b/core/systemtime.c new file mode 100644 --- /dev/null +++ b/core/systemtime.c @@ -0,0 +1,34 @@ +#include "callrealmode.h" +#include "initfunc.h" +#include "printf.h" +#include "process.h" + +static int +systemtime_msghandler (int m, int c) +{ + u32 ticks; + u32 timeh, timem, times, timems; + + if (m == MSG_INT) { + callrealmode_getsystemtime (&ticks); + timeh = ticks * 24 / 0x1800B0; + ticks = ticks * 24 % 0x1800B0; + timem = ticks * 60 / 0x1800B0; + ticks = ticks * 60 % 0x1800B0; + times = ticks * 60 / 0x1800B0; + ticks = ticks * 60 % 0x1800B0; + timems = ticks * 1000 / 0x1800B0; + printf ("Current time is %02u:%02u:%02u.%03u\n", + timeh, timem, times, timems); + return 0; + } + return -1; +} + +static void +systemtime_init_msg (void) +{ + msgregister ("systemtime", systemtime_msghandler); +} + +INITFUNC ("msg0", systemtime_init_msg);

動作確認は以下のようにします。

> sendint

sendint> systemtime

send 0 to systemtime

Current time is 16:42:16.715

表示される時刻は少しずれていると思います。usleep を使うとさらにずれます。

sendint> usleep 10000000

send 10000000 to usleep

sendint> systemtime

send 0 to systemtime

Current time is 16:42:19.727

このズレは、BitVisor が割り込み禁止で動作しており、usleep は割り込み禁止のままポーリングで時間経過を待つため、その間のタイマー割り込みが発生しないことによって起こります。

割り込み処理

ということで、割り込み処理を実装しましょう。割り込み処理も、基本的には BIOS にまかせることにします。例外と区別するため、割り込みコントローラーにアクセスして、割り込み番号をずらします。

diff --git a/core/callrealmode.c b/core/callrealmode.c --- a/core/callrealmode.c +++ b/core/callrealmode.c @@ -33,6 +33,7 @@ #include "callrealmode_asm.h" #include "convert.h" #include "entry.h" +#include "hardint.h" #include "initfunc.h" #include "mm.h" #include "pcpu.h" @@ -143,6 +144,7 @@ callrealmode_call_directly (struct callr ulong cr3; struct savemsr msr; + asm_cli (); savemsr_save (&msr); asm_rdcr3 (&cr3); asm_wrcr3 (vmm_base_cr3); @@ -233,6 +235,7 @@ callrealmode_call_directly (struct callr memcpy (d, (u8 *)sp16, sizeof *d); asm_wrcr3 (cr3); savemsr_load (&msr); + hardint_sti (); } static void @@ -399,3 +402,13 @@ callrealmode_getsystemtime (u32 *ticks) conv16to32 (d.u.getsystemtime.dx_ret, d.u.getsystemtime.cx_ret, ticks); return d.u.getsystemtime.al_ret; } + +void +callrealmode_callint (u32 addr) +{ + struct callrealmode_data d; + + d.func = CALLREALMODE_FUNC_CALLINT; + d.u.callint.addr = addr; + callrealmode_call (&d); +} diff --git a/core/callrealmode.h b/core/callrealmode.h --- a/core/callrealmode.h +++ b/core/callrealmode.h @@ -93,5 +93,6 @@ void callrealmode_startkernel32 (u32 par void callrealmode_tcgbios (u32 al, struct tcgbios_args *args); int callrealmode_getkey (void); int callrealmode_getsystemtime (u32 *ticks); +void callrealmode_callint (u32 addr); #endif diff --git a/core/callrealmode_asm.h b/core/callrealmode_asm.h --- a/core/callrealmode_asm.h +++ b/core/callrealmode_asm.h @@ -50,6 +50,7 @@ enum callrealmode_func { CALLREALMODE_FUNC_TCGBIOS = 0xA, CALLREALMODE_FUNC_GETKEY = 0xB, CALLREALMODE_FUNC_GETSYSTEMTIME = 0xC, + CALLREALMODE_FUNC_CALLINT = 0xD, }; struct callrealmode_printmsg { @@ -118,6 +119,10 @@ struct callrealmode_getsystemtime { u8 al_ret; } __attribute__ ((packed)); +struct callrealmode_callint { + u32 addr; +}; + struct callrealmode_data { enum callrealmode_func func : 32; union { @@ -133,6 +138,7 @@ struct callrealmode_data { struct callrealmode_tcgbios tcgbios; struct callrealmode_getkey getkey; struct callrealmode_getsystemtime getsystemtime; + struct callrealmode_callint callint; } u; } __attribute__ ((packed)); diff --git a/core/callrealmode_asm.s b/core/callrealmode_asm.s --- a/core/callrealmode_asm.s +++ b/core/callrealmode_asm.s @@ -41,6 +41,7 @@ CALLREALMODE_FUNC_TCGBIOS = 0xA CALLREALMODE_FUNC_GETKEY = 0xB CALLREALMODE_FUNC_GETSYSTEMTIME = 0xC + CALLREALMODE_FUNC_CALLINT = 0xD SEG_SEL_CODE_REAL = 0x0000 SEG_SEL_DATA_REAL = 0x0000 @@ -178,6 +179,7 @@ callrealmode_switch: OFF_GETSYSTEMTIME_CX_RET = 0x30 OFF_GETSYSTEMTIME_DX_RET = 0x32 OFF_GETSYSTEMTIME_AL_RET = 0x34 + OFF_CALLINT_ADDR = 0x30 # Which function? mov OFF_FUNC(%bp),%ax @@ -207,6 +209,8 @@ callrealmode_switch: je getkey cmp $CALLREALMODE_FUNC_GETSYSTEMTIME,%ax je getsystemtime + cmp $CALLREALMODE_FUNC_CALLINT,%ax + je callint # Error! cld mov $(errormsg_data-1-callrealmode_start+CALLREALMODE_OFFSET),%di @@ -458,6 +462,13 @@ getsystemtime: mov %al,OFF_GETSYSTEMTIME_AL_RET(%bp) ret +# callint +# +callint: + pushf + lcall *OFF_CALLINT_ADDR(%bp) + ret + # Subroutines # paging_and_protection_off: diff --git a/core/hardint.c b/core/hardint.c new file mode 100644 --- /dev/null +++ b/core/hardint.c @@ -0,0 +1,111 @@ +#include "asm.h" +#include "callrealmode.h" +#include "cpu.h" +#include "initfunc.h" +#include "int.h" +#include "mm.h" +#include "printf.h" +#include "string.h" + +#define M(N) \ + void irq##N (void); \ + asm ("irq" #N ": \n" \ + " push $" #N "\n" \ + " jmp irq_handler \n") +M(0); M(1); M(2); M(3); M(4); M(5); M(6); M(7); M(8); M(9); M(10); M(11); +M(12); M(13); M(14); M(15); +#undef M + +static bool ready = false; + +void +hardint_sti (void) +{ + if (ready && get_cpu_id () == 0) + asm_sti (); +} + +void +hardint_func (int irq) +{ + u32 *vector; + + ready = false; + vector = mapmem_hphys ((irq + 8) * 4, 4, MAPMEM_WRITE); + callrealmode_callint (*vector); + unmapmem (vector, 4); + ready = true; +} + +static void +setup_handlers (void) +{ + set_int_handler (0x20, irq0); + set_int_handler (0x21, irq1); + set_int_handler (0x22, irq2); + set_int_handler (0x23, irq3); + set_int_handler (0x24, irq4); + set_int_handler (0x25, irq5); + set_int_handler (0x26, irq6); + set_int_handler (0x27, irq7); + set_int_handler (0x70, irq8); + set_int_handler (0x71, irq9); + set_int_handler (0x72, irq10); + set_int_handler (0x73, irq11); + set_int_handler (0x74, irq12); + set_int_handler (0x75, irq13); + set_int_handler (0x76, irq14); + set_int_handler (0x77, irq15); +} + +static void +setup_realmode_handlers (void) +{ + u16 *p; + u32 addr; + int size, i; + extern u8 hardint_realmode[], hardint_realmode_end[]; + + size = hardint_realmode_end - hardint_realmode; + addr = alloc_realmodemem (size); + p = mapmem_hphys (addr, size, MAPMEM_WRITE); + memcpy (p, hardint_realmode, size); + unmapmem (p, size); + p = mapmem_hphys (0x20 * 4, 4 * 8, MAPMEM_WRITE); + for (i = 0; i < 16; i += 2) { + p[i] = (addr + i * 2) & 0xF; + p[i + 1] = (addr + i * 2) >> 4; + } + unmapmem (p, 4 * 8); +} + +static void +pic_init (void) +{ + u8 imr_master, imr_slave; + + asm_inb (0x21, &imr_master); + asm_inb (0xA1, &imr_slave); + asm_outb (0x20, 0x11); + asm_outb (0x21, 0x20); + asm_outb (0x21, 0x4); + asm_outb (0x21, 0x1); + asm_outb (0xA0, 0x11); + asm_outb (0xA1, 0x70); + asm_outb (0xA1, 0x2); + asm_outb (0xA1, 0x1); + asm_outb (0x21, imr_master); + asm_outb (0xA1, imr_slave); +} + +static void +hardint_init (void) +{ + setup_handlers (); + setup_realmode_handlers (); + pic_init (); + ready = true; + asm_sti (); +} + +INITFUNC ("global3", hardint_init); diff --git a/core/hardint.h b/core/hardint.h new file mode 100644 --- /dev/null +++ b/core/hardint.h @@ -0,0 +1,1 @@ +void hardint_sti (void); diff --git a/core/hardint_handler.s b/core/hardint_handler.s new file mode 100644 --- /dev/null +++ b/core/hardint_handler.s @@ -0,0 +1,111 @@ + SEG_SEL_PCPU32 = (8 * 8) + SEG_SEL_PCPU64 = (16 * 8) + gs_inthandling = 0 + + .include "longmode.h" + + .text + .globl irq_handler +irq_handler: +.if longmode + push %rax + push %rbx + push %rcx + push %rdx + push %rbp + push %rsi + push %rdi + push %r8 + push %r9 + push %r10 + push %r11 + push %r12 + push %r13 + push %r14 + push %r15 + push %fs + push %gs + mov 17*8(%rsp),%edi + mov %ss,%eax + mov %eax,%fs + mov $SEG_SEL_PCPU64,%ax + mov %eax,%gs + call hardint_func + pop %gs + pop %fs + pop %r15 + pop %r14 + pop %r13 + pop %r12 + pop %r11 + pop %r10 + pop %r9 + pop %r8 + pop %rdi + pop %rsi + pop %rbp + pop %rdx + pop %rcx + pop %rbx + pop %rax + add $8,%rsp + iretq +.else + pusha + push %ds + push %es + push %fs + push %gs + mov 12*4(%esp),%edi + mov %ss,%eax + mov %eax,%ds + mov %eax,%es + mov %eax,%fs + mov $SEG_SEL_PCPU32,%ax + mov %eax,%gs + push %edi + call hardint_func + add $4,%esp + pop %gs + pop %fs + pop %es + pop %ds + popa + add $4,%esp + iretl +.endif + .globl hardint_realmode +hardint_realmode: + .code16 + push $0x8*4 + jmp 1f + push $0x9*4 + jmp 1f + push $0xA*4 + jmp 1f + push $0xB*4 + jmp 1f + push $0xC*4 + jmp 1f + push $0xD*4 + jmp 1f + push $0xE*4 + jmp 1f + push $0xF*4 +1: + push %bp + mov %sp,%bp + push %ds + push %bx + mov $0,%bx + mov %bx,%ds + mov 2(%bp),%bx + lds (%bx),%bx + mov %ds,2(%bp) + xchg %bx,0(%bp) + mov %bx,%bp + pop %bx + pop %ds + lret + .globl hardint_realmode_end +hardint_realmode_end: diff --git a/core/process.c b/core/process.c --- a/core/process.c +++ b/core/process.c @@ -32,6 +32,7 @@ #include "constants.h" #include "elf.h" #include "entry.h" +#include "hardint.h" #include "initfunc.h" #include "mm.h" #include "msg.h" @@ -951,6 +952,7 @@ static syscall_func_t syscall_table[NUM_ __attribute__ ((regparm (1))) void process_syscall (struct syscall_regs *regs) { + hardint_sti (); if (regs->rbx < NUM_OF_SYSCALLS && syscall_table[regs->rbx]) { regs->rax = syscall_table[regs->rbx] (regs->rdx, regs->rcx, regs->rbx, regs->rsi, diff --git a/core/seg.c b/core/seg.c --- a/core/seg.c +++ b/core/seg.c @@ -232,8 +232,13 @@ fill_segdesctbl (struct segdesc *gdt, st SEGDESC_TYPE_RDWR_DATA, SEGDESC_S_CODE_OR_DATA_SEGMENT, 3, 1, 0, SEGDESC_L_64, SEGDESC_D_B_64); + /* SEG_SEL_CODE64U */ + set_segdesc (&gdt[14], 0xFFFFFFFF, 0x00000000, + SEGDESC_TYPE_EXECREAD_CODE, + SEGDESC_S_CODE_OR_DATA_SEGMENT, 3, 1, + 0, SEGDESC_L_64, SEGDESC_D_B_64); /* SEG_SEL_TSS64 */ - set_segdesc_sys64 (&gdt[14], sizeof *tss64 - 1, (ulong)tss64, + set_segdesc_sys64 (&gdt[17], sizeof *tss64 - 1, (ulong)tss64, SEGDESC_TYPE_64BIT_TSS_AVAILABLE, 0, 1, 0); @@ -243,7 +248,7 @@ fill_segdesctbl (struct segdesc *gdt, st SEGDESC_S_CODE_OR_DATA_SEGMENT, 0, 1, 0, SEGDESC_L_64, SEGDESC_D_B_64); /* Unused */ - i = 17; + i = 19; ASSERT (i <= NUM_OF_SEGDESCTBL); for (; i < NUM_OF_SEGDESCTBL; i++) set_segdesc (&gdt[i], 0x00000000, 0x00000000, @@ -291,7 +296,7 @@ segment_wakeup (bool bsp) resume_pcpu = p->next; gdt = p->segdesctbl; gdt[5].type = SEGDESC_TYPE_32BIT_TSS_AVAILABLE; /* SEG_SEL_TSS32 */ - gdt[14].type = SEGDESC_TYPE_64BIT_TSS_AVAILABLE; /* SEG_SEL_TSS64 */ + gdt[17].type = SEGDESC_TYPE_64BIT_TSS_AVAILABLE; /* SEG_SEL_TSS64 */ load_segment (gdt); } diff --git a/core/seg.h b/core/seg.h --- a/core/seg.h +++ b/core/seg.h @@ -46,8 +46,8 @@ #define SEG_SEL_DATA64 (11 * 8) #define SEG_SEL_CODE64U (12 * 8 + 3) #define SEG_SEL_DATA64U (13 * 8 + 3) -#define SEG_SEL_TSS64 (14 * 8) -#define SEG_SEL_TSS64_ (15 * 8) /* Reserved */ +#define SEG_SEL_TSS64 (17 * 8) +#define SEG_SEL_TSS64_ (18 * 8) /* Reserved */ #define SEG_SEL_PCPU64 (16 * 8) void get_seg_base (ulong gdtbase, u16 ldtr, u16 sel, ulong *segbase);

割り込みハンドラーの呼び出される状況をモニターすると、案外呼ばれてないことに気づくかも知れません。実はキー入力待ちは BIOS 内の処理なので、その間 32 ビットの割り込みハンドラーは呼ばれません。

えっ、なんでセグメントの設定をいじっているのかって? バグですよバグ... もともとの BitVisor は割り込みハンドラーからリターンすることがなかったから発覚していませんでしたが、sysret 命令の挙動を勘違いしていて、64 ビットのプロセスの CS セレクターの値が変な値になっていたようです (汗;

ユーザープロセスのプログラム

そういえば、ここまでユーザープロセスの実装例がありませんので、紹介します。以下に示すのは、指定された個数の素数を表示するだけの簡単なプログラムです。

#include <lib_lineinput.h> #include <lib_printf.h> #include <lib_stdlib.h> #include <lib_syscalls.h> int _start (int a1, int a2) { char buf[100]; long n, i, j; printf ("? "); lineinput (buf, 100); n = strtol (buf, NULL, 0); for (i = 1; n > 0; i++) { for (j = 2; j < i; j++) if (i % j == 0) break; if (i == j) printf ("%ld ", i), n--; } printf ("\n"); exitprocess (0); return 0; }

POSIX とは違いますが、なんとなく雰囲気は伝わると思います。これを、core/builtin/bin_primes.c などというファイル名で保存し、make すると、自動的に primes などというコマンドが増えます。動作確認は以下のようにします。

> primes ? 16 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 >

できた!

おわりに

しょぼいネタの割に長くなってしまいましたが、最後まで読んでくださってありがとうございました。次回の Kernel/VM Advent Calendar は、期待の @oza_x86 さんです。なんと、「去年の分も書く!!!」と高らかに宣言されています。正座して待ちましょう!

+ +

∧_∧ +

(0゚・∀・) ワクワクテカテカ

(0゚∪ ∪ +

と__)__) +