📄 machine.cxx
字号:
/* * Copyright (C) 1998, 1999, 2001, Jonathan S. Shapiro. * * This file is part of the EROS Operating System. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2, * or (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#include <kerninc/kernel.hxx>#include <kerninc/KernStream.hxx>#include <kerninc/Machine.hxx>#include <kerninc/IRQ.hxx>#include <kerninc/dma.h>#include <kerninc/Thread.hxx>#include <kerninc/util.h>#include <kerninc/PhysMem.hxx>#include <kerninc/SysTimer.hxx>#include <disk/DiskKey.hxx>#include <kerninc/BootInfo.h>#include <kerninc/PCI.hxx>#include <eros/TimeOfDay.h>#include <eros/memory.h>#include <eros/SysTraceKey.h>#include "Log386.hxx"#include <arch-kerninc/PTE.hxx>#include "CpuFeatures.hxx"/* #include <machine/RegLayout.hxx> */#include <kerninc/Process.hxx>#include <kerninc/Depend.hxx>#include "CMOS.hxx"#include "lostart.hxx"#include "GDT.hxx"#include "IDT.hxx"#include "TSS.hxx"#include <disk/LowVolume.hxx> /* for VolFlags */extern "C" { extern void etext(); extern void end(); extern void start();}kva_t Machine::frameBuffer = 0;kva_t Machine::mappedFrameBuffer = 0;static void MapKernel();/* Machine::BootInit() -- first routine called by main() if we * came into the kernel via the bootstrap code. * * On entry, we have a stack (created in lostart.S) and interrupts * are disabled. Static constructors have been run. * * Otherwise, the state of the machine is whatever environment * the bootstrap code gave us (typically not much). In particular, * controllers and devices may be in arbitrarily inconsistent states * unless the static constructors have ensured otherwise. * * On exit from this routine, the kernel should be running in * an appropriate virtual map, and should have performed enough * initialization to have a minimal interrupt table installed. * Interrupts are disabled on entry, and should be enabled on exit. * Note that on exit from this procedure the controller entry points * have not yet been established (that will happen next). * * The implicit assumption here is that interrupt handling happens * in two tiers, and that the procesor interrupts can be enabled * without enabling all device interrupts. * * It is customary for this code to hand-initialize the console, so * that boot-related diagnostics can be seen. */void Machine::BootInit(){ /* Set up the boot console by hand so that we can do kernel * diagnostics during startup. Note that the boot console is output * only unless a kernel debugger is present, and we will enable * debugger input later after interrupts have been initialized. */ KernStream::InitStreams(); /* On the 386, this can be done before enabling VM, which * is helpful. */ PhysMem::Init(); /* The kernel address space must, however, be constructed and * enabled before the GDT, IDT, and TSS descriptors are loaded, * because these descriptors reference linear addresses that change * when the mapping is updated. */ MapKernel(); /* DANGER! No console use permitted between the mapping enable and the GDT enable! */ (void) Machine::SetMappingTable(KERNPAGEDIR); /* Well known address! */ (void) Machine::EnableVirtualMapping(); printf("FB updated -- now virtual\n"); if (BootInfoPtr->useGraphicsFB) {#if defined(OPTION_CONSFB) && 0 extern void animate(); extern void redrawLogos(); animate(); redrawLogos();#endif /* Now try it at the linear address, before we load the GDT: */ Machine::frameBuffer = KVTOL(Machine::mappedFrameBuffer);#if defined(OPTION_CONSFB) && 0 printf("Redraw at KVTOL(fb-vp)\n"); animate(); redrawLogos();#endif } printf("About to load GDT\n"); GDT::Init(); if (BootInfoPtr->useGraphicsFB) {#if defined(OPTION_CONSFB) && 0 extern void animate(); extern void redrawLogos();#endif Machine::frameBuffer = Machine::mappedFrameBuffer;#if defined(OPTION_CONSFB) && 0 animate(); redrawLogos();#endif } if (BootInfoPtr->consInfo) printf("FB at 0x%08x\n", (unsigned long) BootInfoPtr->consInfo->frameBuffer); else printf("No FB console found.");#if defined(OPTION_CONSFB) && 0 if (BootInfoPtr->useGraphicsFB) pause();#endif /* printf("main(): loaded GDT\n"); * Commented out to avoid missing symbol complaints */ IDT::Init(); /* printf("main(): loaded IDT\n"); */ TSS::Init(); /* printf("main(): loaded TSS\n"); */#if 0 /* Enable the following when you dork the process structure to * recompute the important offsets. */ printf("PROCESS_FIXREGS_OFFSET = %d\n", offsetof(Process, fixRegs)); printf("PROCESS_MAPTABLE_OFFSET = %d\n", offsetof(Process, fixRegs.MappingTable)); printf("PROCESS_V86_FIXREGS_TOP = %d\n", offsetof(Process, fixRegs) + offsetof(fixregs_t, sndPtr)); fatal("PROCESS_FIXREGS_TOP = %d\n", offsetof(Process, fixRegs) + offsetof(fixregs_t, ES));#endif switch(BusArchitecture()) { case bt_Unknown: printf("Unknown bus type!\n"); halt('u'); case bt_ISA: printf("ISA bus\n"); break; case bt_MCA: printf("MCA bus -- get a real machine!\n"); break; case bt_PCI: printf("PCI bus\n"); break; } init_dma(); assert(sizeof(uint16_t) == 2); assert(sizeof(uint32_t) == 4); assert(sizeof(void *) == 4); /* assert(sizeof(Key) == 12); */ assert(sizeof(fixregs_t) == EROS_SAVE_AREA_SIZE); /* Verify the queue key representation pun: */ assert(sizeof(ThreadPile) == 2 * sizeof(uint32_t)); /* Verify that in shifting the interrupt vectors around we haven't * violated the assumptions in eros/i486/target.h */ assert(IRQ_FROM_EXCEPTION(IntVec::IRQ0) == 0); /* printf("Kernel is mapped. CR0 = 0x%x\n", flags); */ /* printf("Pre enable: intdepth=%d\n", IDT::intdepth); */ /* We enable interrupts on the processor here. Note that at this * point all of the hardware interrupts are disabled. We need to * enable processor interrupts before we start autoconfiguring, * since some of the IRQ detection code will proceed by inducing an * interrupt to detect the configured IRQ. * * The down side to enabling here is that we have not yet set up the * exception handlers for processor generated exceptions. Those * will get initialized very early in the autoconfiguration process. * The alternative would be a small redesign that would allow us to * initialize them by hand via a call to the AutoConfig logic here. * * In principle, the kernel ought to be completely free of * processor-generated exceptions, so the current design is probably * just fine. This note is here mostly as a guideline for other * processors that may require different solutions. */ #ifdef OPTION_KERN_PROFILE /* This must be done before the timer interrupt is enabled!! */ extern void InitKernelProfiler(); InitKernelProfiler();#endif#if 0 if (BootInfoPtr->ramdiskSz) { printf("Machine::init(): %d sector ramdisk found at 0x%x\n", SysConfig.ramdiskSz, SysConfig.ramdiskAddress); } else { printf("Machine::init(): no ramdisk\n", SysConfig.ramdiskSz, SysConfig.ramdiskAddress); }#endif#ifdef EROS_HAVE_FPU Machine::InitializeFPU();#endif IRQ::ENABLE(); Machine::InitHardClock(); #ifdef OPTION_DDB KernStream::dbg_stream->EnableDebuggerInput();#endif printf("Motherboard interrupts initialized\n");#if defined(OPTION_CONSFB) && 0 if (BootInfoPtr->useGraphicsFB) { int i; extern void animate(); extern void redrawLogos(); for (i = 0; i < 4; i++) { animate(); redrawLogos(); } }#endif}uint64_tMachine::GetIplSysId(){ return BootInfoPtr->iplSysId;}#ifdef OPTION_SMALL_SPACES#include <kerninc/Invocation.hxx>voidProcess::WriteDisableSmallSpaces(){ uint32_t nFrames = KTUNE_NCONTEXT / 32; PTE *pte = Process::smallSpaces; for (unsigned i = 0; i < nFrames * NPTE_PER_PAGE; i++) pte[i].WriteProtect();}static voidMakeSmallSpaces(){ assert (KTUNE_NCONTEXT % 32 == 0); /* Allocate the page *tables* for the small spaces. */ uint32_t nFrames = KTUNE_NCONTEXT / 32; Process::smallSpaces = KPAtoP(PTE *,PhysMem::Alloc(nFrames * EROS_PAGE_SIZE, &PhysMem::pages)); bzero(Process::smallSpaces, nFrames * EROS_PAGE_SIZE); assert (((uint32_t)Process::smallSpaces & EROS_PAGE_MASK) == 0); PTE *pageTab = Process::smallSpaces; /* Insert those page tables into the master kernel map, from which they will be copied to all other spaces. */ uint32_t vaddr = UMSGTOP; uint32_t dirndx = (vaddr >> 22); PTE *pageDir = (PTE*) PTOV(KERNPAGEDIR); /* kernel page directory */ for (uint32_t i = 0; i < nFrames; i++) { PTE_SET(pageDir[dirndx], (VTOP(pageTab) & PTE_FRAMEBITS) ); PTE_SET(pageDir[dirndx], PTE_W|PTE_V|PTE_ACC|PTE_USER); pageTab += NPTE_PER_PAGE; dirndx++; }}#endif static void MapPageAt(kva_t va, kpa_t pa, uint32_t mode){ PTE *pageDir = (PTE*) PTOV(KERNPAGEDIR); /* kernel page directory */ PTE *pageTab; uint32_t tabndx = (va >> 12) & 0x3ffu; uint32_t dirndx = (va >> 22); if (mode & PTE_PGSZ) { pageDir[dirndx].Invalidate(); PTE_SET(pageDir[dirndx], (VTOP(pa) & PTE_FRAMEBITS) ); PTE_SET(pageDir[dirndx], mode); return; } if ( PTE_IS(pageDir[dirndx], PTE_V) ) { pageTab = (PTE *) PTOV(pageDir[dirndx].PageFrame()); } else { /* Allocate a new page table. Allocating here also has the * property that all of the pages above those allocated for use * by the kernel will be contiguous in virtual space. */ pageTab = KPAtoP(PTE *,PhysMem::Alloc(EROS_PAGE_SIZE, &PhysMem::pages)); bzero(pageTab, EROS_PAGE_SIZE); assert (((uint32_t)pageTab & EROS_PAGE_MASK) == 0); /* pageTab = ::new PTE[NPTE_PER_PAGE]; */#if 0 printf("Allocated new page table at 0x%x, dirndx 0x%x\n", pageTab, dirndx);#endif PTE_SET(pageDir[dirndx], (VTOP(pageTab) & PTE_FRAMEBITS) ); PTE_SET(pageDir[dirndx], PTE_W | PTE_V | mode); } pageTab[tabndx].Invalidate(); PTE_SET(pageTab[tabndx], (pa & PTE_FRAMEBITS) ); PTE_SET(pageTab[tabndx], mode);}/* Build a kernel mapping table. In the initial construction, we create TWO mappings for the kernel. The first begins at VA=0x0, and is used until we get a chance to set up a new global descriptor table. The second begins at KVA, and goes into effect after we set up a global descriptor table for the kernel. Since the page table entries for the two mappings are identical, the duplication of mapping has essentially no cost. There is also no need to undo the duplication, as all of the mappings in question are supervisor-only. */static voidMapKernel(){ PTE *pageDir = (PTE*) PTOV(KERNPAGEDIR); /* kernel page directory */ bzero(pageDir, EROS_PAGE_SIZE); PTE *pageTab = 0; /* The kernel mapping table must include all of the physical pages, * including the ramdisk if any. * * FIX: This is broken, as the kernel will soon need to support * machines where there are more physical pages than there are * virtual pages. */ uint32_t physPages = PhysMem::TotalPhysicalPages(); /* printf("pageDir: 0x%x\n", pageDir); */ /* Computation of the virtual address must user paddr + KVA * because we are using segmentation tricks to make kernel segment * offsets be the same be the same as physical addresses */ uint32_t increment = 1; /* increment in pages by default */ uint32_t globalPage = 0;#ifndef NO_GLOBAL_PAGES if (CpuIdHi > 1 && CpuFeatures & CPUFEAT_PGE) globalPage = PTE_GLBL;#endif bool supports_large_pages = false;#ifndef NO_LARGE_PAGES if (CpuIdHi > 1 && CpuFeatures & CPUFEAT_PSE) { supports_large_pages = true; }#endif #ifndef NO_LARGE_PAGES if (supports_large_pages) { /* Enable the page size extensions: */ __asm__ __volatile__ ("\tmov %%cr4,%%eax\n" "\torl $0x10,%%eax\n" "\tmov %%eax,%%cr4\n" : /* no outputs */ : /* no inputs */ : "ax" /* smash any convenient register */); increment = 1024; /* increment in large pages */ }#endif#ifndef NO_GLOBAL_PAGES if (globalPage) { /* Enable the page size extensions: */ __asm__ __volatile__ ("\tmov %%cr4,%%eax\n" "\torl $0x80,%%eax\n" "\tmov %%eax,%%cr4\n" : /* no outputs */ : /* no inputs */ : "ax" /* smash any convenient register */); increment = 1024; /* increment in large pages */ }#endif /* FIX: This is no longer correct -- we should no longer be mapping * all of physical memory, just the kernel heap. I am temporarily * leaving this vestigial code here because when it is removed we * will need to deal with page tables in a different way * (preallocation), and I want to make one change at a time. As a * TEMPORARY measure I am simply placing the heap above the physical * page map and leaving the physical page map in place. */ for (uint32_t i = 0 ; i < physPages; i += increment) { uint32_t paddr = i * EROS_PAGE_SIZE; uint32_t vaddr = KVTOL(PTOV(paddr));#ifndef NDEBUG uint32_t tabndx = (vaddr >> 12) & 0x3ffu;#endif uint32_t dirndx = (vaddr >> 22); uint32_t pdirndx = (paddr >> 22); uint32_t mode = globalPage | PTE_V;#ifndef NO_LARGE_PAGES if (supports_large_pages) { mode |= PTE_W|PTE_PGSZ;#ifdef WRITE_THROUGH mode |= PTE_WT;#endif assert (tabndx == 0); MapPageAt(vaddr, paddr, mode); pageDir[pdirndx] = pageDir[dirndx]; continue; }#endif assert((paddr & EROS_PAGE_MASK) == 0); if (paddr < (uint32_t) start || paddr >= ((uint32_t)etext & PTE_FRAMEBITS)){ mode |= PTE_W;#ifdef WRITE_THROUGH mode |= PTE_WT;#endif } /* Hand-protect kernel page zero, which holds the kernel page * directory, but is never referenced by virtual addresses once * we load the new mapping table pointer: */ if (vaddr == 0) mode = 0; MapPageAt(vaddr, paddr, mode); /* This is a sleazy trick to avoid redundant page table allocation: */ pageDir[pdirndx] = pageDir[dirndx]; } unsigned heap_first_page = physPages; heap_first_page += (1024 - 1); heap_first_page -= (heap_first_page % 1024);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -