📄 monitor-host.c
字号:
/* * plex86: run multiple x86 operating systems concurrently * Copyright (C) 1999-2003 Kevin P. Lawton * * monitor-host.c: This file contains the top-level monitor code, * accessible from the host space. (kernel independent code) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include "plex86.h"#define IN_HOST_SPACE#include "monitor.h"/* ===================================================================== * Plex86 module global variables. This should be the _only_ place * where globals are declared. Since plex86 supports multiple VMs, almost * all data is stored per-VM. For the few variables which are global * to all VMs, we have to be careful to access them in SMP friendly ways. * The ones which are written upon kernel module initialization are fine, * since they are only written once. * ===================================================================== *//* Info regarding the physical pages that comprise the kernel module, * including physical page information. This is written (once) at * kernel module initialization time. Thus there are no SMP access issues. */kernelModulePages_t kernelModulePages;/* Information of the host processor as returned by the CPUID * instruction. This is written (once) at kernel module initialization time. * Thus there no are SMP access issues. */cpuid_info_t hostCpuIDInfo;/* Some constants used by the VM logic. Since they're "const", there are * no problems with SMP access. */static const selector_t nullSelector = { raw: 0 };static const descriptor_t nullDescriptor = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };static int hostInitIDTSlot(vm_t *vm, unsigned vec, int type);static void hostMapMonPages(vm_t *vm, Bit32u *, unsigned, Bit32u *, page_t *, unsigned user, unsigned writable, char *name);#if ANAL_CHECKSstatic void hostMapBlankPage(vm_t *vm, Bit32u *laddr_p, page_t *pageTable);#endif#define RW0 0#define RW1 1#define US0 0#define US1 1#define IDT_INTERRUPT 0#define IDT_EXCEPTION_ERROR 1#define IDT_EXCEPTION_NOERROR 2 unsignedhostModuleInit(void){ /* Kernel independent stuff to do at kernel module load time. */ if (!hostGetCpuCapabilities()) { hostOSPrint("getCpuCapabilities returned error\n"); return(0); /* Fail. */ } else {#if 0 hostOSPrint("ptype:%u, family:%u, model:%u stepping:%u\n", hostCpuIDInfo.procSignature.fields.procType, hostCpuIDInfo.procSignature.fields.family, hostCpuIDInfo.procSignature.fields.model, hostCpuIDInfo.procSignature.fields.stepping);#endif } /* xxx Should check that host CS.base is page aligned here. */#if 1 { Bit32u cr0; asm volatile ( "movl %%cr0, %0" : "=r" (cr0) ); hostOSPrint("host CR0=0x%x\n", cr0); }#endif return(1); /* Pass. */} voidhostDeviceOpen(vm_t *vm){ /* Kernel independent stuff to do at device open time. */ /* Zero out entire VM structure. */ mon_memzero( vm, sizeof(vm_t) ); vm->vmState = VMStateFDOpened;} inthostInitMonitor(vm_t *vm){ unsigned pdi, pti; unsigned int i; Bit32u nexus_size; page_t *pageTable; Bit32u laddr, base; int r; vm->kernel_offset = hostOSKernelOffset(); vm->system.a20Enable = 1; /* Start with A20 line enabled. */ vm->system.a20AddrMask = 0xffffffff; /* All address lines contribute. */ vm->system.a20IndexMask = 0x000fffff; /* All address lines contribute. */ /* Initialize nexus */ mon_memzero(vm->host.addr.nexus, 4096); /* Copy transition code (nexus) into code page allocated for this VM. */ nexus_size = ((Bit32u) &__nexus_end) - ((Bit32u) &__nexus_start); if (nexus_size > 4096) goto error; mon_memcpy(vm->host.addr.nexus, &__nexus_start, nexus_size); /* Init the convenience pointers. */ /* Pointer to host2mon routine inside nexus page */ vm->host.__host2mon = (void (*)(void)) HOST_NEXUS_OFFSET(vm, __host2mon); /* Pointer to guest context on monitor stack */ vm->host.addr.guest_context = (guest_context_t *) ( (Bit32u)vm->host.addr.nexus + PAGESIZE - sizeof(guest_context_t) ); /* Zero out various monitor data structures */ mon_memzero(vm->host.addr.log_buffer, 4096*LOG_BUFF_PAGES); mon_memzero(&vm->log_buffer_info, sizeof(vm->log_buffer_info)); mon_memzero(vm->host.addr.page_dir, 4096); mon_memzero(vm->host.addr.guest_cpu, 4096); mon_memzero(vm->host.addr.idt, MON_IDT_PAGES*4096); mon_memzero(vm->host.addr.gdt, MON_GDT_PAGES*4096); mon_memzero(vm->host.addr.ldt, MON_LDT_PAGES*4096); mon_memzero(vm->host.addr.tss, MON_TSS_PAGES*4096); mon_memzero(vm->host.addr.idt_stubs, MON_IDT_STUBS_PAGES*4096); vm->guestPhyPagePinQueue.nEntries = 0; vm->guestPhyPagePinQueue.tail = 0; /* * ================ * Nexus Page Table * ================ * * All structures needed by the monitor inside the guest environment * (code to perform the transition between host<-->guest, fault handler * code, various processor data structures like page directory, GDT, * IDT, TSS etc.) are mapped into a single Page Table. * * This allows us to migrate the complete nexus to anywhere in the * guest address space by just updating a single (unused) page directory * entry in the monitor/guest page directory to point to this nexus * page table. * * To simplify nexus migration, we try to avoid storing guest linear * addresses to nexus structures as far as possible. Instead, we use * offsets relative to the monitor code/data segments. As we update * the base of these segments whenever the monitor migrates, the net * effect is that those *offsets* remain valid across nexus migration. */ /* Fill in the PDE flags. The US bit is set to 1 (user access). * All of the US bits in the monitor PTEs are set to 0 (system access). */ vm->host.nexus_pde.fields.base = vm->pages.nexus_page_tbl; vm->host.nexus_pde.fields.avail = 0; vm->host.nexus_pde.fields.G = 0; /* not global */ vm->host.nexus_pde.fields.PS = 0; /* 4K pages */ vm->host.nexus_pde.fields.D = 0; /* (unused in pde) */ vm->host.nexus_pde.fields.A = 0; /* not accessed */ vm->host.nexus_pde.fields.PCD = 0; /* normal caching */ vm->host.nexus_pde.fields.PWT = 0; /* normal write-back */ vm->host.nexus_pde.fields.US = 1; /* user access (see above) */ vm->host.nexus_pde.fields.RW = 1; /* read or write */ vm->host.nexus_pde.fields.P = 1; /* present in memory */ /* Clear Page Table. */ pageTable = vm->host.addr.nexus_page_tbl; mon_memzero(pageTable, 4096); /* xxx Comment here */ laddr = 0; base = MON_BASE_FROM_LADDR(laddr); hostMapMonPages(vm, kernelModulePages.ppi, kernelModulePages.nPages, &laddr, pageTable, US0, RW1, "Monitor code/data pages");#if ANAL_CHECKS hostMapBlankPage(vm, &laddr, pageTable);#endif vm->guest.addr.nexus = (nexus_t *) (laddr - base); hostMapMonPages(vm, &vm->pages.nexus, 1, &laddr, pageTable, US0, RW1, "Nexus"); vm->guest.addr.guest_context = (guest_context_t *) ( (Bit32u)vm->guest.addr.nexus + PAGESIZE - sizeof(guest_context_t) );#if ANAL_CHECKS hostMapBlankPage(vm, &laddr, pageTable);#endif vm->host.addr.nexus->vm = (void *) (laddr - base); hostMapMonPages(vm, vm->pages.vm, BytesToPages(sizeof(*vm)), &laddr, pageTable, US0, RW1, "VM structure");#if ANAL_CHECKS hostMapBlankPage(vm, &laddr, pageTable);#endif vm->guest.addr.idt = (gate_t *) (laddr - base); hostMapMonPages(vm, vm->pages.idt, MON_IDT_PAGES, &laddr, pageTable, US0, RW1, "IDT");#if ANAL_CHECKS hostMapBlankPage(vm, &laddr, pageTable);#endif vm->guest.addr.gdt = (descriptor_t *) (laddr - base); hostMapMonPages(vm, vm->pages.gdt, MON_GDT_PAGES, &laddr, pageTable, US0, RW1, "GDT");#if ANAL_CHECKS hostMapBlankPage(vm, &laddr, pageTable);#endif vm->guest.addr.ldt = (descriptor_t *) (laddr - base); hostMapMonPages(vm, vm->pages.ldt, MON_LDT_PAGES, &laddr, pageTable, US0, RW1, "LDT");#if ANAL_CHECKS hostMapBlankPage(vm, &laddr, pageTable);#endif vm->guest.addr.tss = (tss_t *) (laddr - base); hostMapMonPages(vm, vm->pages.tss, MON_TSS_PAGES, &laddr, pageTable, US0, RW1, "TSS");#if ANAL_CHECKS hostMapBlankPage(vm, &laddr, pageTable);#endif vm->guest.addr.idt_stubs = (idt_stub_t *) (laddr - base); hostMapMonPages(vm, vm->pages.idt_stubs, MON_IDT_STUBS_PAGES, &laddr, pageTable, US0, RW1, "IDT stubs");#if ANAL_CHECKS hostMapBlankPage(vm, &laddr, pageTable);#endif /* Monitor Page Directory */ vm->guest.addr.page_dir = (pageEntry_t *) (laddr - base); hostMapMonPages(vm, &vm->pages.page_dir, 1, &laddr, pageTable, US0, RW1, "Monitor Page Directory");#if ANAL_CHECKS hostMapBlankPage(vm, &laddr, pageTable);#endif /* Nexus Page Table */ vm->guest.addr.nexus_page_tbl = (page_t *) (laddr - base); hostMapMonPages(vm, &vm->pages.nexus_page_tbl, 1, &laddr, pageTable, US0, RW1, "Nexus Page Table");#if ANAL_CHECKS hostMapBlankPage(vm, &laddr, pageTable);#endif /* Map virtualized guest page tables into monitor. */ vm->guest.addr.page_tbl = (page_t *) (laddr - base); hostMapMonPages(vm, vm->pages.page_tbl, MON_PAGE_TABLES, &laddr, pageTable, US0, RW1, "Guest Page Tables");#if ANAL_CHECKS hostMapBlankPage(vm, &laddr, pageTable);#endif /* Map of linear addresses of page tables mapped into monitor */ vm->guest.addr.page_tbl_laddr_map = (unsigned *) (laddr - base); hostMapMonPages(vm, &vm->pages.page_tbl_laddr_map, 1, &laddr, pageTable, US0, RW1, "Page Table Laddr Map");#if ANAL_CHECKS hostMapBlankPage(vm, &laddr, pageTable);#endif /* Guest CPU state (mapped RW into user space also). */ vm->guest.addr.guest_cpu = (guest_cpu_t *) (laddr - base); hostMapMonPages(vm, &vm->pages.guest_cpu, 1, &laddr, pageTable, US0, RW1, "Guest CPU State");#if ANAL_CHECKS hostMapBlankPage(vm, &laddr, pageTable);#endif /* * We need a buffer to implement a debug print facility which * can work in either host or monitor space. Map the buffer * into monitor/guest space. */ vm->guest.addr.log_buffer = (unsigned char *) (laddr - base); hostMapMonPages(vm, vm->pages.log_buffer, LOG_BUFF_PAGES, &laddr, pageTable, US0, RW1, "Log Buffer"); { /* The physical addresses of the following pages are not */ /* yet established. Pass dummy info until they are mapped. */ Bit32u tmp[1]; tmp[0] = 0;#if ANAL_CHECKS hostMapBlankPage(vm, &laddr, pageTable);#endif /* Window into the guest's current physical code page */ vm->guest.addr.code_phy_page = (unsigned char *) (laddr - base); hostMapMonPages(vm, tmp, 1, &laddr, pageTable, US0, RW1, "Code Phy Page");#if ANAL_CHECKS hostMapBlankPage(vm, &laddr, pageTable);#endif /* Temporary window into a guest physical page, for accessing */ /* guest GDT, IDT, etc info. */ vm->guest.addr.tmp_phy_page0 = (unsigned char *) (laddr - base); hostMapMonPages(vm, tmp, 1, &laddr, pageTable, US0, RW1, "Tmp Phy Page0"); vm->guest.addr.tmp_phy_page1 = (unsigned char *) (laddr - base); hostMapMonPages(vm, tmp, 1, &laddr, pageTable, US0, RW1, "Tmp Phy Page1"); }#if ANAL_CHECKS hostMapBlankPage(vm, &laddr, pageTable);#endif hostOSPrint("Using %u/1024 PTE slots in 4Meg monitor range.\n", (laddr >> 12) & 0x3ff); /* Pointer to mon2host routine inside nexus page */ vm->guest.__mon2host = (void (*)(void)) MON_NEXUS_OFFSET(vm, __mon2host); /* * ===================== * Transition Page Table * ===================== * * To aid in the transition between host<-->monitor/guest spaces, * we need to have an address identity map situation for at least * one page; the page containing the transition code. As we do * not know in advance whether this linear address range is in use * by the guest as well, we set aside a complete additional Page * Table, which contains only a single PTE pointing to the nexus page. * * To create the identity map, we simply change the corresponding * monitor page directory entry to point to this transition Page Table. * This happens transparently inside the host<-->guest transition code; * both the guest/monitor code and the host side code never see this * transition page table entered into the page directory! * * NOTE: We need to ensure that the nexus page table never spans the * same 4Meg linear address space region as this page table! * As we are free to choose the nexus linear address, this is * not a problem. */ /* Get full linear address of nexus code page, as seen in host space. */ laddr = (Bit32u)vm->host.addr.nexus + vm->kernel_offset; pdi = laddr >> 22; pti = (laddr >> 12) & 0x3ff; /* * We need to be able to access the PDE in the monitor page directory * that corresponds to this linear address from both host and monitor * address spaces. */ vm->host.addr.nexus->transition_pde_p_host = vm->host.addr.page_dir + pdi; vm->host.addr.nexus->transition_pde_p_mon = (pageEntry_t *) (((Bit32u)vm->guest.addr.page_dir) + (pdi << 2)); vm->host.addr.nexus->transition_laddr = laddr; /* Fill in the PDE flags */ vm->host.addr.nexus->transition_pde.fields.base = vm->pages.transition_PT; vm->host.addr.nexus->transition_pde.fields.avail = 0; vm->host.addr.nexus->transition_pde.fields.G = 0; /* not global */ vm->host.addr.nexus->transition_pde.fields.PS = 0; /* 4K pages */ vm->host.addr.nexus->transition_pde.fields.D = 0; /* (unused in pde) */ vm->host.addr.nexus->transition_pde.fields.A = 0; /* not accessed */ vm->host.addr.nexus->transition_pde.fields.PCD = 0; /* normal caching */ vm->host.addr.nexus->transition_pde.fields.PWT = 0; /* normal write-back*/ vm->host.addr.nexus->transition_pde.fields.US = 0; /* no user access */ vm->host.addr.nexus->transition_pde.fields.RW = 1; /* read or write */ vm->host.addr.nexus->transition_pde.fields.P = 1; /* present in memory*/ /* Clear Page Table; only one PTE is used. */ pageTable = vm->host.addr.transition_PT; mon_memzero(pageTable, 4096); /* Fill in the PTE for identity mapping the code page */ pageTable->pte[pti].fields.base = vm->pages.nexus; pageTable->pte[pti].fields.avail = 0; pageTable->pte[pti].fields.G = 0; /* not global */ pageTable->pte[pti].fields.PS = 0; /* (unused in pte) */ pageTable->pte[pti].fields.D = 0; /* clean */ pageTable->pte[pti].fields.A = 0; /* not accessed */ pageTable->pte[pti].fields.PCD = 0; /* normal caching */ pageTable->pte[pti].fields.PWT = 0; /* normal write-back */ pageTable->pte[pti].fields.US = 0; /* user can not access */ pageTable->pte[pti].fields.RW = 1; /* read or write */ pageTable->pte[pti].fields.P = 1; /* present in memory */ /* * Setup the TSS for the monitor/guest environment. * * We don't need to set the pagedir in the TSS, because we don't * actually jump to it anyway. The TSS is just used to set the kernel * stack and in a later stage, perhaps the I/O permission bitmap. */ /* No task chain. */ vm->host.addr.tss->back = 0; /* No debugging or I/O, for now. */ vm->host.addr.tss->trap = 0; vm->host.addr.tss->io = sizeof(tss_t); /* Monitor stack offset. */ vm->host.addr.tss->esp0 = ((Bit32u)vm->guest.addr.nexus) + PAGESIZE; /* * Set up initial monitor code and stack offset. */ vm->host.addr.nexus->mon_jmp_info.offset = MON_NEXUS_OFFSET(vm, __mon_cs); vm->host.addr.nexus->mon_stack_info.offset = vm->host.addr.tss->esp0 - (sizeof(guest_context_t) + 48);/* xxx 48 above should be calculated from code below which winds * xxx up monitor stack. */ /* * Setup the IDT for the monitor/guest environment */ r = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -