X86: Implement the startupCPU function.
authorGabe Black <gblack@eecs.umich.edu>
Mon, 12 Nov 2007 22:38:10 +0000 (14:38 -0800)
committerGabe Black <gblack@eecs.umich.edu>
Mon, 12 Nov 2007 22:38:10 +0000 (14:38 -0800)
--HG--
extra : convert_revision : d2331a0e0bd14863e82004508558f657c5b900a2

src/arch/x86/utility.cc

index 0eee0c93e2251a2ca9b45544f688b44bc6a6404c..b2a6ea04029db51cd2187c20b5fc513da1ecf6a6 100644 (file)
@@ -60,6 +60,7 @@
 #include "arch/x86/segmentregs.hh"
 #include "arch/x86/utility.hh"
 #include "arch/x86/x86_traits.hh"
+#include "sim/system.hh"
 
 namespace X86ISA {
 
@@ -253,20 +254,177 @@ void initCPU(ThreadContext *tc, int cpuId)
 
 #endif
 
+#if FULL_SYSTEM
 void startupCPU(ThreadContext *tc, int cpuId)
 {
     if (cpuId == 0) {
         // This is the boot strap processor (BSP). Initialize it to look like
-        // the boot loader has just turned control over to the 64 bit OS.
-
-        // Enable paging, turn on long mode, etc.
+        // the boot loader has just turned control over to the 64 bit OS. We
+        // won't actually set up real mode or legacy protected mode descriptor
+        // tables because we aren't executing any code that would require
+        // them. We do, however toggle the control bits in the correct order
+        // while allowing consistency checks and the underlying mechansims
+        // just to be safe.
+
+        const int NumPDTs = 4;
+
+        const Addr PageMapLevel4 = 0x70000;
+        const Addr PageDirPtrTable = 0x71000;
+        const Addr PageDirTable[NumPDTs] =
+            {0x72000, 0x73000, 0x74000, 0x75000};
+        const Addr GDTBase = 0x76000;
+
+        const int PML4Bits = 9;
+        const int PDPTBits = 9;
+        const int PDTBits = 9;
+
+        // Get a port to write the page tables and descriptor tables.
+        FunctionalPort * physPort = tc->getPhysPort();
+
+        /*
+         * Set up the gdt.
+         */
+        // Place holder at selector 0
+        uint64_t nullDescriptor = 0;
+        physPort->writeBlob(GDTBase, (uint8_t *)(&nullDescriptor), 8);
+
+        //64 bit code segment
+        SegDescriptor csDesc = 0;
+        csDesc.type.c = 0; // Not conforming
+        csDesc.dpl = 0; // Privelege level 0
+        csDesc.p = 1; // Present
+        csDesc.l = 1; // 64 bit
+        csDesc.d = 0; // default operand size
+        //Because we're dealing with a pointer and I don't think it's
+        //guaranteed that there isn't anything in a nonvirtual class between
+        //it's beginning in memory and it's actual data, we'll use an
+        //intermediary.
+        uint64_t csDescVal = csDesc;
+        physPort->writeBlob(GDTBase, (uint8_t *)(&csDescVal), 8);
+
+        tc->setMiscReg(MISCREG_GDTR_BASE, GDTBase);
+        tc->setMiscReg(MISCREG_GDTR_LIMIT, 0xF);
+
+        /*
+         * Identity map the first 4GB of memory. In order to map this region
+         * of memory in long mode, there needs to be one actual page map level
+         * 4 entry which points to one page directory pointer table which
+         * points to 4 different page directory tables which are full of two
+         * megabyte pages. All of the other entries in valid tables are set
+         * to indicate that they don't pertain to anything valid and will
+         * cause a fault if used.
+         */
+
+        // Put valid values in all of the various table entries which indicate
+        // that those entries don't point to further tables or pages. Then
+        // set the values of those entries which are needed.
+
+        // Page Map Level 4
+
+        // read/write, user, not present
+        uint64_t pml4e = X86ISA::htog(0x6);
+        for (int offset = 0; offset < (1 << PML4Bits) * 8; offset += 8) {
+            physPort->writeBlob(PageMapLevel4 + offset, (uint8_t *)(&pml4e), 8);
+        }
+        // Point to the only PDPT
+        pml4e = X86ISA::htog(0x7 | PageDirPtrTable);
+        physPort->writeBlob(PageMapLevel4, (uint8_t *)(&pml4e), 8);
+
+        // Page Directory Pointer Table
+
+        // read/write, user, not present
+        uint64_t pdpe = X86ISA::htog(0x6);
+        for (int offset = 0; offset < (1 << PDPTBits) * 8; offset += 8) {
+            physPort->writeBlob(PageDirPtrTable + offset,
+                    (uint8_t *)(&pdpe), 8);
+        }
+        // Point to the PDTs
+        for (int table = 0; table < NumPDTs; table++) {
+            pdpe = X86ISA::htog(0x7 | PageDirTable[table]);
+            physPort->writeBlob(PageDirPtrTable + table * 8,
+                    (uint8_t *)(&pdpe), 8);
+        }
+
+        // Page Directory Tables
+
+        Addr base = 0;
+        const Addr pageSize = 2 << 20;
+        for (int table = 0; table < NumPDTs; table++) {
+            for (int offset = 0; offset < (1 << PDTBits) * 8; offset += 8) {
+                // read/write, user, present, 4MB
+                uint64_t pdte = X86ISA::htog(0x87 | base);
+                physPort->writeBlob(PageDirTable[table] + offset,
+                        (uint8_t *)(&pdte), 8);
+                base += pageSize;
+            }
+        }
+
+        /*
+         * Transition from real mode all the way up to Long mode
+         */
+        CR0 cr0 = tc->readMiscRegNoEffect(MISCREG_CR0);
+        //Turn off paging.
+        cr0.pg = 0;
+        tc->setMiscReg(MISCREG_CR0, cr0);
+        //Turn on protected mode.
+        cr0.pe = 1;
+        tc->setMiscReg(MISCREG_CR0, cr0);
+
+        CR4 cr4 = tc->readMiscRegNoEffect(MISCREG_CR4);
+        //Turn on pae.
+        cr4.pae = 1;
+        tc->setMiscReg(MISCREG_CR4, cr4);
+
+        //Point to the page tables.
+        tc->setMiscReg(MISCREG_CR3, PageMapLevel4);
+
+        Efer efer = tc->readMiscRegNoEffect(MISCREG_EFER);
+        //Enable long mode.
+        efer.lme = 1;
+        tc->setMiscReg(MISCREG_EFER, efer);
+
+        //Activate long mode.
+        cr0.pg = 1;
+        tc->setMiscReg(MISCREG_CR0, cr0);
+
+        /*
+         * Far jump into 64 bit mode.
+         */
+        // Set the selector
+        tc->setMiscReg(MISCREG_CS, 1);
+        // Manually set up the segment attributes. In the future when there's
+        // other existing functionality to do this, that could be used
+        // instead.
+        SegAttr csAttr = 0;
+        csAttr.writable = 0;
+        csAttr.readable = 1;
+        csAttr.expandDown = 0;
+        csAttr.dpl = 0;
+        csAttr.defaultSize = 0;
+        csAttr.longMode = 1;
+        tc->setMiscReg(MISCREG_CS_ATTR, csAttr);
+
+        tc->setPC(tc->getSystemPtr()->kernelEntry);
+        tc->setNextPC(tc->readPC());
+
+        // We should now be in long mode. Yay!
 
         tc->activate(0);
     } else {
         // This is an application processor (AP). It should be initialized to
         // look like only the BIOS POST has run on it and put then put it into
         // a halted state.
+        tc->suspend();
     }
 }
 
+#else
+
+void startupCPU(ThreadContext *tc, int cpuId)
+{
+    tc->activate(0);
+}
+
+#endif
+
 } //namespace X86_ISA