////////////////////////////////////////////////////////////////////////////// // // singldr.cpp - Singularity PXE Boot Loader. // // Copyright (c) Microsoft Corporation. All rights reserved. // #pragma data_seg("_TEXT") ////////////////////////////////////////////////////////////////////////////// // #pragma warning(disable: 4103) #include "singldr.h" #include "fnames.h" #include "inifile.h" #include "pxe.h" #include "fatdevice.h" #include "jolietdevice.h" #include "usbdevice.h" #include "printf.cpp" #include "debug.cpp" #include "pci.h" ////////////////////////////////////////////////////////////////////////////// // extern "C" void __cdecl BootGetBiosInfo(Struct_Microsoft_Singularity_BootInfo __far *); extern "C" void __cdecl BootPhase2(Struct_Microsoft_Singularity_BootInfo __far *); extern "C" void __cdecl StopPhase0(void); extern "C" void __cdecl StopPhase3(); extern "C" int _cdecl BootGetSMAP(Struct_Microsoft_Singularity_SMAPINFO __far * pSmap, uint32 __far * pNext); extern "C" void __cdecl BootHalt(void); extern "C" void __cdecl Reset(void); extern "C" void __cdecl MpEnter(void); extern "C" void __cdecl MpBootPhase2(Struct_Microsoft_Singularity_BootInfo __far *, Struct_Microsoft_Singularity_CpuInfo __far *); extern "C" uint16 MpStartupLock; extern "C" void __cdecl IdtEnter0(void); extern "C" void __cdecl IdtEnter1(void); extern "C" void __cdecl IdtEnterN(void); extern "C" uint32 IdtTarget; extern "C" uint8 undump_dat[];; extern "C" void IoSpaceWrite8(uint16 port, uint8 value); extern "C" void IoSpaceWrite32(uint16 port, uint32 value); extern "C" uint8 IoSpaceRead8(uint16 port); extern "C" uint32 IoSpaceRead32(uint16 port); ////////////////////////////////////////////////////////////////////////////// // #pragma warning(disable: 4704) /////////////////////////////////////////////////////////////////////// Video. // static uint16 __far * pwScreen = (uint16 __far *)_MK_FP(0xb800, 0); static uint16 nCursor = 0; static uint16 s_wAttr = 0x1f00; static BOOL fDebuggerInitialized = FALSE; void Cls() { for (uint16 n = 0; n < 4000; n++) { pwScreen[n] = s_wAttr | ' '; } nCursor = 0; } void VPutChar(char cOut) { if (cOut == '\r') { nCursor -= nCursor % 80; } else if (cOut == '\n') { do { pwScreen[nCursor++] = s_wAttr | ' '; } while ((nCursor % 80) != 0); } else { pwScreen[nCursor++] = (uint16)(s_wAttr | cOut); } while (nCursor >= 4000) { for (uint16 n = 0; n < 4000 - 80; n++) { pwScreen[n] = pwScreen[n + 80]; } for (; n < 4000; n++) { pwScreen[n] = s_wAttr | ' '; } nCursor -= 80; } IoSpaceWrite8(0x3d4, 0xe); IoSpaceWrite8(0x3d5, (uint8)(nCursor >> 8)); IoSpaceWrite8(0x3d4, 0xf); IoSpaceWrite8(0x3d5, (uint8)(nCursor & 0xff)); } void PutChar(char cOut) { VPutChar(cOut); // If the debugger is not initialized, don't attempt // to output over the connection if (!fDebuggerInitialized) { return; } static CHAR szBuffer[128]; static int nBuffer = 0; szBuffer[nBuffer++] = cOut; if (cOut == '\n' || nBuffer >= sizeof(szBuffer) - 1) { BdPrintString(szBuffer, nBuffer); nBuffer = 0; } } void VideoInit() { __asm { mov ax, 1202h; // LINES_400_CONFIGURATION mov bx, 0301h; // SELECT_SCAN_LINE int 10h; mov ax, 3h; // SET_80X25_16_COLOR_MODE mov bx, 0h; // PAGE0 int 10h; mov ax, 1112h; // LOAD_8X8_CHARACTER_SET mov bx, 0h; int 10h; mov ax, 1003h; // Disable BLINK mode, enable background intensity. mov bx, 0h; int 10h; mov ax, 0200h; // Set Cursor position to 0, 0 mov bx, 0h; mov dx, 0h; int 10h; } Cls(); } ////////////////////////////////////////////////////////////////////// Memory. // void strcpy(LPCHAR dst, LPCHAR src) { while (*src) { *dst++ = *src++; } *dst = '\0'; } void memzero(LPVOID vp, uint32 cb) { uint8 __far * bp = (uint8 __far *)vp; while (cb-- > 0) { *bp++ = 0; } } void memset(LPVOID vp, uint8 value, uint32 cb) { uint8 __far * bp = (uint8 __far *)vp; while (cb-- > 0) { *bp++ = value; } } void memcopy(LPVOID dvp, LPVOID svp, int cb) { uint8 __far * dp = (uint8 __far *)dvp; uint8 __far * sp = (uint8 __far *)svp; while (cb-- > 0) { *dp++ = *sp++; } } uint8 Sum(uint8 __far * pbData, int cbData) { uint8 sum = 0; while (cbData-- > 0) { sum = (UINT8) (sum + *pbData++); } return sum; } int Checksum(uint8 __far * pbData, int cbData) { return (Sum(pbData, cbData) == 0); } uint32 PointerToUint32(LPVOID vp) { return (((uint32)_FP_SEG(vp)) << 4) + ((uint32)_FP_OFF(vp)); } // warning: alloc() behaves improperly if you // request a segment of size 0xFFFF static uint16 npHeapTop = 0x1800; static const uint16 npHeapMax = 0x6000; LPVOID alloc(uint16 cbSize, uint16 cbPad) { cbPad = (cbPad != 0) ? (cbPad + 0xf) >> 4 : 0x10; npHeapTop = (npHeapTop + (cbPad - 1)) & ~(cbPad - 1); uint16 pbData = npHeapTop; cbSize = (cbSize != 0) ? (cbSize + 0xf) >> 4 : 1; npHeapTop += cbSize; if (npHeapTop > npHeapMax) { npHeapTop = pbData; return NULL; } LPVOID pvData = _MK_FP(pbData, 0); memzero(pvData, cbSize << 4); return pvData; } LPVOID allocpages(uint16 cPages) { return alloc(Struct_Microsoft_Singularity_BootInfo_PAGE_SIZE * cPages, Struct_Microsoft_Singularity_BootInfo_PAGE_SIZE); } void __far * operator new(uint cbSize) { return alloc((uint16)cbSize, (uint16)0x10); } /////////////////////////////////////////////////////////////////////// Debug. // void dump(uint8 __far * pbData, uint cbData) { for (uint n = 0; n < cbData; n += 16) { printf(" %08lx ", PointerToUint32(pbData) + n); for (uint s = n; s < n + 16; s++) { if (s % 4 == 0) { printf(" "); } if (s < cbData) { printf("%02x", pbData[s]); } else { printf(" "); } } printf(" "); for (s = n; s < n + 16; s++) { if (s % 4 == 0) { printf(" "); } if (s < cbData) { if (pbData[s] >= ' ' && pbData[s] < 127) { printf("%c", pbData[s]); } else { printf("."); } } else { printf(" "); } } printf("\n"); } } const char * ToString(LPCHAR pszIn) { char szBuffer[128]; char *psz = szBuffer; while (*psz) { *psz++ = *pszIn++; } return szBuffer; } ///////////////////////////////////////////////////////////////////////// APM. // void ApmPowerOff() { uint16 version = 0; uint16 signature = 0; uint16 flags = 0; uint16 connection = 0; uint8 error = 0; uint16 which = 0; uint16 good = 0; __asm { mov ax, 0x5300; mov which, ax; mov bx, 0; int 0x15; // APM Check. jc ifailed; mov version, ax; mov signature, bx; mov flags, cx; mov good, 1; jmp idone; ifailed: mov error, ah; mov good, 0; idone: } if (!good) { goto exit; } printf(" APM: %c%c %x.%02x, fl=%04x\n", signature >> 8, signature & 0xff, version >> 8, version & 0xff, flags); __asm { mov ax, 0x5301; mov which, ax; mov bx, 0; int 0x15; // Real Mode Connect. jc failed; mov ax, 0x530e; mov which, ax; mov bx, 0; mov cx, 0x0102; int 0x15; // Set APM Driver (i.e. our code) Version. jc failed; mov connection, ax; } printf(" APM: Connect %x.%02x\n", connection >> 8, connection & 0xff); __asm { mov ax, 0x530d; mov which, ax; mov bx, 1; mov cx, 1; int 0x15; // Enable Power Management jc failed; mov ax, 0x530f; mov which, ax; mov bx, 1; mov cx, 1; int 0x15; // Engage Power Management jc failed; mov ax, 0x5307; mov which, ax; mov bx, 1; mov cx, 3; int 0x15; // Power Off jc failed; mov good, 1; jmp done; failed: mov error, ah; mov good, 0; done: } exit: if (!good || signature != 'PM') { printf("APM Call (%04x) failed: %02x\n", which, error); return; } printf(" APM: Power Off\n"); BootHalt(); } ///////////////////////////////////////////////////////////////////// Globals. // static Struct_Microsoft_Singularity_BootInfo __far *g_bi; static Struct_Microsoft_Singularity_MpBootInfo __far *g_mbi; static uint16 __far *g_CmdLine; //////////////////////////////////////////////////////////// PCI Mechanism #1. // UINT PciReadConfig(UINT8 nBus, UINT8 nDev, UINT8 nFun, void * pvData, UINT cbData) { PCI_CONFIG_BITS sel; sel.bits.enable = 1; sel.bits.bus = nBus; sel.bits.device = nDev; sel.bits.function = nFun; for (UINT cbDone = 0; cbDone < cbData; cbDone += sizeof(UINT32)) { sel.bits.offset = cbDone; IoSpaceWrite32(PCI_ADDR_PORT, sel.value); *((UINT32*&)pvData)++ = IoSpaceRead32(PCI_DATA_PORT); } return cbDone; } UINT32 PciGetSize(UINT8 nBus, UINT8 nDev, UINT8 nFun, UINT8 nIgnore, UINT32 nOffset) { PCI_CONFIG_BITS sel; UINT32 saved, size; sel.bits.enable = 1; sel.bits.bus = nBus; sel.bits.device = nDev; sel.bits.function = nFun; sel.bits.offset = nOffset; // Save existing address IoSpaceWrite32(PCI_ADDR_PORT, sel.value); saved = IoSpaceRead32(PCI_DATA_PORT); // Write size IoSpaceWrite32(PCI_ADDR_PORT, sel.value); IoSpaceWrite32(PCI_DATA_PORT, 0xffffffff); // Read size (masking off reserved lower bits) IoSpaceWrite32(PCI_ADDR_PORT, sel.value); size = IoSpaceRead32(PCI_DATA_PORT) & ~((1 << nIgnore) - 1); size = ~size + 1; // Restore saved address IoSpaceWrite32(PCI_ADDR_PORT, sel.value); IoSpaceWrite32(PCI_DATA_PORT, saved); return size; } UINT32 PciGetBarSize(UINT8 nBus, UINT8 nDev, UINT8 nFun, UINT32 nOffset) { return PciGetSize(nBus, nDev, nFun, 4, nOffset); } UINT32 PciGetRomSize(UINT8 nBus, UINT8 nDev, UINT8 nFun, UINT32 nOffset) { return PciGetSize(nBus, nDev, nFun, 10, nOffset); } #if DEBUG_PCI #define pciprintf(...) printf(__VA_ARGS__) #else #define pciprintf(...) #endif struct PCI_MEMORY_RANGE32 { // We could store cacheability state // here, e.g. ROM ranges can be cached. UINT32 base; UINT32 length; }; UINT16 ScanPci(const Struct_Microsoft_Singularity_BootInfo _far *bi, struct PCI_MEMORY_RANGE32 _far *lpPciMemRanges, UINT16 nMaxPciMemRangesCount) { UINT nFound = 0; UINT16 nMemCount = 0; ///////////////////////////////////////////////// Check for Compatibility. // pciprintf("Searching for PCI devices [%d busses].\n", bi->PciBiosCX + 1); if ((bi->PciBiosAX >> 8) != 0 || (bi->PciBiosEDX != 0x20494350)) { pciprintf("Hardware does not support PCI V2.x.\n"); pciprintf("PCI V2.x: AX:%04x, BX:%04x, CX:%04x, EDX:%08x\n", bi->PciBiosAX, bi->PciBiosBX, bi->PciBiosCX, bi->PciBiosEDX); return 0; } if (!(bi->PciBiosAX & 0x01)) { pciprintf("Hardware does not support multiple PCI buses.\n"); pciprintf("PCI V2.x: AX:%04x, BX:%04x, CX:%04x, EDX:%08x\n", bi->PciBiosAX, bi->PciBiosBX, bi->PciBiosCX, bi->PciBiosEDX); return 0; } for (UINT8 nBus = 0; nBus <= bi->PciBiosCX; nBus++) { for (UINT8 nDev = 0; nDev < PCI_MAX_DEVICES; nDev++) { BOOL bIsMultiFunction; PCI_COMMON_CONFIG config; config.VendorID = PCI_INVALID_VENDORID; PciReadConfig(nBus, nDev, 0, &config, PCI_FIXED_HDR_LENGTH); bIsMultiFunction = config.HeaderType & PCI_MULTIFUNCTION; for (UINT8 nFun = 0; nFun < PCI_MAX_FUNCTION; nFun++) { if (nFun > 0 && !bIsMultiFunction) { break; } if (nMemCount == nMaxPciMemRangesCount) { printf("PCI address ranges saturated storage."); return nMaxPciMemRangesCount; } // Read configuration header. // config.VendorID = PCI_INVALID_VENDORID; PciReadConfig(nBus, nDev, nFun, &config, sizeof(config)); if (config.VendorID == PCI_INVALID_VENDORID) { continue; } pciprintf("%2d.%2d.%1d: class %02x-%02x device %04x-%04x-%04x-%04x-%02x %02x il=%02x ip=%02x\n", nBus, nDev, nFun, config.BaseClass, config.SubClass, config.VendorID, config.DeviceID, config.type0.SubVendorID, config.type0.SubSystemID, config.RevisionID, config.HeaderType, config.type0.InterruptLine, config.type0.InterruptPin); switch (config.HeaderType & ~PCI_MULTIFUNCTION) { case PCI_DEVICE_TYPE: if (config.type0.BaseAddresses[0] || config.type0.BaseAddresses[1] || config.type0.BaseAddresses[2] || config.type0.BaseAddresses[3] || config.type0.BaseAddresses[4] || config.type0.BaseAddresses[5] || config.type0.ROMBaseAddress) { pciprintf(" A0=%08lx A1=%08lx A2=%08lx A3=%08lx A4=%08lx " "A5=%08lx RM=%08lx\n", config.type0.BaseAddresses[0], config.type0.BaseAddresses[1], config.type0.BaseAddresses[2], config.type0.BaseAddresses[3], config.type0.BaseAddresses[4], config.type0.BaseAddresses[5], config.type0.ROMBaseAddress); for (int i = 0; i < PCI_TYPE0_ADDRESSES; i++) { if (config.type0.BaseAddresses[i] & PCI_BAR_TYPE_IO_SPACE) { continue; } else if (config.type0.BaseAddresses[i] & PCI_BAR_MEMORY_TYPE_64BIT) { i++; continue; } else if ((config.type0.BaseAddresses[i] & PCI_BAR_ADDRESS_MASK) == 0) { continue; } lpPciMemRanges[nMemCount].base = config.type0.BaseAddresses[i] & PCI_BAR_ADDRESS_MASK; lpPciMemRanges[nMemCount].length = PciGetBarSize(nBus, nDev, nFun, 0x10 + i * 4); nMemCount++; } if (config.type0.ROMBaseAddress & PCI_ROMADDRESS_MASK) { lpPciMemRanges[nMemCount].base = config.type0.ROMBaseAddress & PCI_ROMADDRESS_MASK; lpPciMemRanges[nMemCount].length = PciGetRomSize(nBus, nDev, nFun, 0x30); nMemCount++; } } break; case PCI_BRIDGE_TYPE: pciprintf(" BUS=%02x/%02x/%02x IO=%02x/%02x " "A0=%08lx A1=%08lx RM=%08x\n", config.type1.PrimaryBus, config.type1.SecondaryBus, config.type1.SubordinateBus, config.type1.IOBase, config.type1.IOLimit, config.type1.BaseAddresses[0], config.type1.BaseAddresses[1], config.type1.ROMBaseAddress); for (int i = 0; i < PCI_TYPE1_ADDRESSES; i++) { if (config.type0.BaseAddresses[i] & PCI_BAR_TYPE_IO_SPACE) { continue; } else if (config.type0.BaseAddresses[i] & PCI_BAR_MEMORY_TYPE_64BIT) { i++; continue; } else if ((config.type0.BaseAddresses[i] & PCI_BAR_ADDRESS_MASK) == 0) { continue; } lpPciMemRanges[nMemCount].base = config.type0.BaseAddresses[i]; lpPciMemRanges[nMemCount].length = PciGetBarSize(nBus, nDev, nFun, 0x10 + i * 4); nMemCount++; } if (config.type0.ROMBaseAddress & PCI_ROMADDRESS_MASK) { lpPciMemRanges[nMemCount].base = config.type0.ROMBaseAddress; lpPciMemRanges[nMemCount].length = PciGetRomSize(nBus, nDev, nFun, 0x38); nMemCount++; } break; } nFound++; } } } pciprintf("\n"); return nMemCount; } /////////////////////////////////////////////// BootPhase1 - main entry point. // extern "C" int BootPhase1(PXE __far *pxe, PXENV __far * pxenv, uint32 diskid) { uint16 port = 0; // debug port BootDevice __far *bootDevice; // base class ptr // allocate 32KB for the ini file: uint8 __far * IniFileBuffer = (uint8 __far *) alloc(0x7FFF, 0); // constant, hard-coded filename LPCHAR initfile = "/Singularity/Singboot.ini"; VideoInit(); VPutChar('1'); // // Initialize the Debugger // if (BdInitDebugger(COM2_PORT)) { VPutChar('a'); port = COM2_PORT; fDebuggerInitialized = TRUE; } else { VPutChar('A'); if (BdInitDebugger(COM1_PORT)) { VPutChar('b'); port = COM1_PORT; fDebuggerInitialized = TRUE; } else { VPutChar('B'); if (BdInitDebugger(COM2_PORT)) { fDebuggerInitialized = TRUE; port = COM2_PORT; } } } VPutChar('2'); // // Get the BIOS info // Struct_Microsoft_Singularity_BootInfo __far *bi = (Struct_Microsoft_Singularity_BootInfo __far *) alloc(sizeof(Struct_Microsoft_Singularity_BootInfo), 0x10); g_bi = bi; VPutChar('3'); BootGetBiosInfo(bi); bi->Info16 = (uint32)bi; VPutChar('4'); const UINT16 nMaxPciRanges = 64; struct PCI_MEMORY_RANGE32 __far *lpPciRanges = (struct PCI_MEMORY_RANGE32 __far*) alloc(nMaxPciRanges * sizeof(struct PCI_MEMORY_RANGE32), 0); UINT16 nPciMemoryRanges = ScanPci(bi, lpPciRanges, nMaxPciRanges); // // Try to find a PCI 1394 connection for the debugger. // for (UINT8 nBus = 0; nBus <= bi->PciBiosCX; nBus++) { for (UINT8 nDev = 0; nDev < PCI_MAX_DEVICES; nDev++) { BOOL bIsMultiFunction; PCI_COMMON_CONFIG config; config.VendorID = PCI_INVALID_VENDORID; PciReadConfig(nBus, nDev, 0, &config, sizeof(config)); bIsMultiFunction = config.HeaderType & PCI_MULTIFUNCTION; for (UINT8 nFun = 0; nFun < PCI_MAX_FUNCTION; nFun++) { if (nFun > 0 && !bIsMultiFunction) { break; } if (config.BaseClass == 0x0c && config.SubClass == 0x00 && bi->Ohci1394Base.lo == 0) { // Found firewire. printf("%2d.%2d.%1d: 1394 device %04x-%04x-%04x-%04x-%02x\n", nBus, nDev, nFun, config.VendorID, config.DeviceID, config.type0.SubVendorID, config.type0.SubSystemID, config.RevisionID); printf(" %08lx %08lx %08lx %08lx %08lx %08lx\n", config.type0.BaseAddresses[0], config.type0.BaseAddresses[1], config.type0.BaseAddresses[2], config.type0.BaseAddresses[3], config.type0.BaseAddresses[4], config.type0.BaseAddresses[5]); bi->Ohci1394Base.lo = config.type0.BaseAddresses[0] & ~0xflu; bi->Ohci1394BufferAddr32.lo = PointerToUint32(allocpages(3)); bi->Ohci1394BufferSize32 = 3 * Struct_Microsoft_Singularity_BootInfo_PAGE_SIZE; break; } } } } // figure out the boot device if (pxe->Signature[0] == '!' && pxe->Signature[1] == 'P' && pxe->Signature[2] == 'X' && pxe->Signature[3] == 'E') { bootDevice = new __far PxeDevice(pxe, pxenv); } else if (pxenv->Signature[0] == 'P' && pxenv->Signature[1] == 'X' && pxenv->Signature[2] == 'E' && pxenv->Signature[3] == 'N' && pxenv->Signature[4] == 'V' && pxenv->Signature[5] == '+') { bootDevice = new __far PxeDevice(pxe, pxenv); } else if (PointerToUint32(pxe) == diskid && PointerToUint32(pxenv) == 0x4803) { bootDevice = new __far FatDevice((uint8)diskid, (uint8)32); } else if (PointerToUint32(pxe) == diskid && PointerToUint32(pxenv) == 0x4806) { bootDevice = new __far FatDevice((uint8)diskid, (uint8)16); } else if (PointerToUint32(pxe) == diskid && PointerToUint32(pxenv) == 0x4344) { bootDevice = new __far JolietDevice((uint8)diskid, (uint8)0); } else if (PointerToUint32(pxe) == diskid && PointerToUint32(pxenv) == 0x5544) { bootDevice = new __far UsbDevice((uint8)diskid, (uint8)0); } else { printf("Error: Invalid Boot Medium: diskid=%lx, pxe=%lx, pxenv=%lx", diskid, PointerToUint32(pxe), PointerToUint32(pxenv)); BootHalt(); } VPutChar('5'); BdPrintString("-------------------------------------------------------------------------------\n", 80); VPutChar('6'); // // print welcome message: // printf("16-bit Singularity Boot Loader [%s %s] (bi=%d) [com2]\n", __DATE__, __TIME__, sizeof(Struct_Microsoft_Singularity_BootInfo)); VPutChar('7'); VPutChar('.'); // // partial verify of the bios data // if (sizeof(Struct_Microsoft_Singularity_BootInfo) != bi->RecSize) { printf("sizeof(Struct_Microsoft_Singularity_BootInfo)=%d, bi->RecSize=%ld\n", sizeof(Struct_Microsoft_Singularity_BootInfo), bi->RecSize); BootHalt(); } // // Initialize the boot device // if (bootDevice->OpenDevice()==-1) { BootHalt(); } // read these globals from the boot device, to use in adjusting the // debug port g_CmdLine = bootDevice->CmdLine; bi->CmdLine32.lo = PointerToUint32(bootDevice->CmdLine); printf("\n"); // // Adjust Debug Port. // bi->DebugBasePort = port; for (uint16 __far * pwz = bootDevice->CmdLine; *pwz != '\0'; pwz++) { if ((pwz[0] == 'd' || pwz[0] == 'D') && (pwz[1] == 'b' || pwz[1] == 'B') && (pwz[2] == 'g' || pwz[2] == 'G') && pwz[3] == '=') { *pwz++ = ' '; // Remove 'd' *pwz++ = ' '; // Remove 'b' *pwz++ = ' '; // Remove 'g' *pwz++ = ' '; // Remove '=' bi->DebugBasePort = 0; while (*pwz != '\0' && *pwz != ' ') { if (*pwz >= 'a' && *pwz <= 'f') { bi->DebugBasePort = bi->DebugBasePort * 0x10 + (*pwz - 'a') + 10; *pwz++ = ' '; } else if (*pwz >= 'A' && *pwz <= 'F') { bi->DebugBasePort = bi->DebugBasePort * 0x10 + (*pwz - 'A') + 10; *pwz++ = ' '; } else if (*pwz >= '0' && *pwz <= '9') { bi->DebugBasePort = bi->DebugBasePort * 0x10 + (*pwz - '0'); *pwz++ = ' '; } else { break; } } } } if (bi->DebugBasePort < 0x100) { printf("Using default debug port until 1394 starts [%04x com1=%04x com2=%04x].\n", port, COM1_PORT, COM2_PORT); } else if (bi->DebugBasePort != port) { printf("Changing to debug port %x.\n", bi->DebugBasePort); BdInitDebugger(bi->DebugBasePort); BdPrintString("-------------------------------------------------------------------------------\n", 80); printf("16-bit Singularity Boot Loader [%s %s] (bi=%d) [dbg=%x]\n", __DATE__, __TIME__, sizeof(Struct_Microsoft_Singularity_BootInfo), bi->DebugBasePort); } else { printf("Default debug port [%04x com1=%04x com2=%04x].\n", port, COM1_PORT, COM2_PORT); bi->DebugBasePort = port; } // // Set up Memory Map // Struct_Microsoft_Singularity_SMAPINFO __far *sm = (Struct_Microsoft_Singularity_SMAPINFO __far *) alloc(sizeof(Struct_Microsoft_Singularity_SMAPINFO) * 128, 0x10); bi->SmapData32.lo = PointerToUint32(sm); // Read the system memory map. for (uint32 index = 0; bi->SmapCount < 128;) { uint32 next = index; int ax = BootGetSMAP(&sm[bi->SmapCount], &next); if (ax != 0x4150) { break; } bi->SmapCount++; if (next == 0) { break; } index = next; } // // Drop SMAP entries above supported limit // for (uint i = 0; i < bi->SmapCount; i++) { if (sm[i].type != Struct_Microsoft_Singularity_SMAPINFO_AddressTypeFree) { continue; } uint32 startKB = (sm[i].addr.lo >> 10) + (sm[i].addr.hi << 22); uint32 sizeKB = (sm[i].size.lo >> 10) + (sm[i].size.hi << 22); if ((sm[i].addr.hi != 0ul) || sm[i].addr.lo >= Struct_Microsoft_Singularity_BootInfo_MAX_VIRTUAL_ADDR) { sm[i] = sm[--bi->SmapCount]; } else if (startKB + sizeKB >= Struct_Microsoft_Singularity_BootInfo_MAX_VIRTUAL_ADDR / 1024ul) { sm[i].size.hi = 0; sm[i].size.lo = Struct_Microsoft_Singularity_BootInfo_MAX_VIRTUAL_ADDR - startKB * 1024; } } // Sort the system memory map. sortagain: for (i = 0; i < bi->SmapCount - 1; i++) { if ((sm[i].addr.hi > sm[i + 1].addr.hi) || (sm[i].addr.hi == sm[i + 1].addr.hi && sm[i].addr.lo > sm[i + 1].addr.lo)) { Struct_Microsoft_Singularity_SMAPINFO s = sm[i]; sm[i] = sm[i+1]; sm[i+1] = s; goto sortagain; } } // // Make a reasonable guess at the top of physical memory. // uint32 memoryKB = 0; for (i = 0; i < bi->SmapCount; i++) { uint32 limitKB = (sm[i].addr.lo >> 10) + (sm[i].size.lo >> 10); switch (sm[i].type) { case Struct_Microsoft_Singularity_SMAPINFO_AddressTypeFree: // case Struct_Microsoft_Singularity_SMAPINFO_AddressTypeACPI: // case Struct_Microsoft_Singularity_SMAPINFO_AddressTypeNVS: if (limitKB > memoryKB) { memoryKB = limitKB; } break; default: break; } } printf("Physical memory detected: %8luKB\n", memoryKB); // // Save the EBDA. // uint16 __far * pEbdaSegment = (uint16 __far *)_MK_FP(0x40, 0x0e); uint8 __far * pEbdaRegion = (uint8 __far *)_MK_FP(*pEbdaSegment, 0); bi->Ebda32 = PointerToUint32(pEbdaRegion); // // Scan for PNP. // LPPNP_ROOT pPnp = NULL; LPSMBIOS_ROOT pSmbios = NULL; LPDMIBIOS_ROOT pDmi = NULL; for (uint segment = 0xf000; segment < 0xffff; segment++) { LPPNP_ROOT ppr = (LPPNP_ROOT)_MK_FP(segment, 0); if (pPnp == NULL && ppr->Signature[0] == '$' && ppr->Signature[1] == 'P' && ppr->Signature[2] == 'n' && ppr->Signature[3] == 'P' && Checksum((uint8 __far *)ppr, ppr->Length)) { pPnp = ppr; } LPSMBIOS_ROOT psr = (LPSMBIOS_ROOT)ppr; if (pSmbios == NULL && psr->Signature[0] == '_' && psr->Signature[1] == 'S' && psr->Signature[2] == 'M' && psr->Signature[3] == '_' && psr->Signature2[0] == '_' && psr->Signature2[1] == 'D' && psr->Signature2[2] == 'M' && psr->Signature2[3] == 'I' && psr->Signature2[4] == '_' && Checksum((uint8 __far *)psr, psr->Length)) { pSmbios = psr; pDmi = (LPDMIBIOS_ROOT)&psr->Signature2[0]; } LPDMIBIOS_ROOT pdr = (LPDMIBIOS_ROOT)ppr; if (pDmi == NULL && pdr->Signature2[0] == '_' && pdr->Signature2[1] == 'D' && pdr->Signature2[2] == 'M' && pdr->Signature2[3] == 'I' && pdr->Signature2[4] == '_' && Checksum((uint8 __far *)pdr, sizeof(DMIBIOS_ROOT))) { pDmi = pdr; } } if (pPnp != NULL) { printf("Found PnP at %lp\n", pPnp); printf(" Rev=%02x, Len=%02x/%02x/%02x, Ctl=%04x Evt=%08lx\n", pPnp->Revision, pPnp->Length, sizeof(*pPnp), Sum((uint8 __far *)pPnp, pPnp->Length), pPnp->ControlField, pPnp->EventFlagAddress); printf(" RealMode: entry=%lp data=%04x [oem=%08lx]\n", pPnp->RealModeEntry, pPnp->RealModeDataSegment, pPnp->OemDeviceId); PNP_FN pfPnp = pPnp->RealModeEntry; int err; uint8 cNodes = 0; uint16 cbNode = 0; err = pfPnp(0, (uint8 __far *)&cNodes, (uint16 __far *)&cbNode, pPnp->RealModeDataSegment); if (err != 0) { printf("Get Number of System Device Nodes failed: %d\n", err); } LPPNP_NODE pNode = (LPPNP_NODE)alloc(cbNode, 4); uint16 cbNodes = 0; for (uint8 n = 0; n < 0xff;) { err = pfPnp(1, (uint8 __far *)&n, pNode, 1, pPnp->RealModeDataSegment); if (err != 0) { break; } cbNodes += pNode->Size; } uint8 __far * pNodes = (uint8 __far *)alloc(cbNodes, 4); bi->PnpNodesAddr32.lo = PointerToUint32(pNodes); bi->PnpNodesSize32 = cbNodes; for (n = 0; n < 0xff;) { pNode = (LPPNP_NODE)pNodes; err = pfPnp(1, (uint8 __far *)&n, pNode, 1, pPnp->RealModeDataSegment); if (err != 0) { printf("Get Number of System Device Node failed: %d\n", err); n = 0xff; continue; } pNodes += pNode->Size; } pNode = (LPPNP_NODE)pNodes; pNode->Size = 0; printf("\n"); PNP_ISACONFIG isaConfig; isaConfig.Revision = 1; err = pfPnp(0x40, (LPPNP_ISACONFIG)&isaConfig, pPnp->RealModeDataSegment); if (err != 0) { printf("Get ISA Config failed: %d\n", err); } else { bi->IsaCsns = isaConfig.TotalCSNs; bi->IsaReadPort = isaConfig.IsaReadDataPort; printf("ISA PnP Revision: %d, TotalCSNs: %d, IsaReadDatPort: %04x\n", isaConfig.Revision, isaConfig.TotalCSNs, isaConfig.IsaReadDataPort); } } if (pSmbios != NULL) { printf("Found SMBIOS at %lp\n", pSmbios); printf(" Version=%02x.%02x Len=%02x/%02x/%02x Max=%04x EPR=%02x\n", pSmbios->MajorVersion, pSmbios->MinorVersion, pSmbios->Length, sizeof(*pSmbios), Sum((uint8 __far *)pSmbios, pSmbios->Length), pSmbios->MaximumStructureSize, pSmbios->EntryPointRevision); printf("\n"); bi->SmbiosRoot32.lo = PointerToUint32(pSmbios); } if (pDmi != NULL) { printf("Found DMI at %lp\n", pDmi); printf(" Rev=%02x Len=%04x Num=%04x Addr=%08lx\n", pDmi->Revision, pDmi->StructureTableLength, pDmi->NumberStructures, pDmi->StructureTableAddress); printf("\n"); bi->DmiRoot32.lo = PointerToUint32(pDmi); } // // Load dump images // bi->DumpSize32 = 0; // Since we don't want to allocate much memory, we'll // recycle these 2 pointers to file structs for all filesystem ops FileData directory; FilePtr pDirectory = (FilePtr) &directory; FileData file; FilePtr pFile = (FilePtr) &file; pFile->Size = 0x7fff; pFile->FirstBlock = 0; // Zero the IniFileBuffer memzero((LPVOID)IniFileBuffer, 0x8000); // Find the .ini file and get file metadata for loaders that cache it if (bootDevice->GetFileProperties(initfile, pFile, pDirectory) == -1) { // the device-specific function already printed something, just halt BootHalt(); } if (pFile->Size > 0x8000) { printf(".ini file too big for SINGLDR.\n"); BootHalt(); } // Load the .ini file if (bootDevice->ReadFileLow(initfile, pFile, IniFileBuffer) == -1) { // the device-specific function already printed something, just halt BootHalt(); } IniFile iniFile(IniFileBuffer, pFile->Size); uint16 nMaxImages = iniFile.GetFileCount(); Struct_Microsoft_Singularity_Io_FileImage __far* fileImage = (Struct_Microsoft_Singularity_Io_FileImage __far*) alloc((uint16)(sizeof(Struct_Microsoft_Singularity_Io_FileImage) * nMaxImages), 0x10); iniFile.Rewind(); for (i = 0; i < nMaxImages; i++) { LPUINT8 nextFname = iniFile.GetCurrentFileName(); if (bootDevice->GetFileProperties((LPCHAR)nextFname, pFile, pDirectory) == -1) { BootHalt(); } fileImage[i].Size = iniFile.GetCurrentFileSize(); fileImage[i].Address = pFile->FirstBlock; bi->DumpSize32 += fileImage[i].Size; iniFile.MoveNext(); } // Pad size by one byte to work around // a bug in a popular pxeboot disk that has an off by one // buffer overflow check and wrongly refuses to copy last block // of file if provided buffer is exactly the right size. bi->DumpSize32 += 1; // // Find memory to load image. // uint32 dumpRegionSize = (bi->DumpSize32 + 0xffff) & 0xffff0000; bi->DumpAddr32.lo = 0; printf("System Memory Map:\n"); // Find a suitable region of memory. for (i = 0; i < bi->SmapCount; i++) { if (sm[i].type == 1 && sm[i].size.lo > dumpRegionSize) { // round down to nearest 2MB boundary (to start on super page boundary). uint32 target = (sm[i].addr.lo + sm[i].size.lo - dumpRegionSize) & 0xffe00000; if (target < sm[i].addr.lo) { continue; } if (bi->DumpAddr32.lo < target) { bi->DumpAddr32.lo = target; dumpRegionSize = sm[i].size.lo; } } printf(" %08lx..%08lx %ld.%ld\n", sm[i].addr.lo, sm[i].addr.lo + sm[i].size.lo, sm[i].type, sm[i].extendedAttributes ); } printf("\n"); printf("Editing the SMAP to protect the dump area...\n"); for (i = 0; i < bi->SmapCount; i++) { // Does this region straddle the start of the dump area? // if so, truncate it if (sm[i].type == 1 && sm[i].addr.lo < bi->DumpAddr32.lo && sm[i].addr.lo + sm[i].size.lo > bi->DumpAddr32.lo) { sm[i].size.lo = bi->DumpAddr32.lo - sm[i].addr.lo; printf(" %08lx..%08lx %d (truncated)\n", sm[i].addr.lo, sm[i].addr.lo + sm[i].size.lo, sm[i].type); } if (sm[i].type == 1 && sm[i].addr.lo >= bi->DumpAddr32.lo) { sm[i].type = 2; // Arbitrary non-free value printf(" %08lx..%08lx %d (marked unavailable)\n", sm[i].addr.lo, sm[i].addr.lo + sm[i].size.lo, sm[i].type); } } printf("\n"); printf("Loading image at %08lx..%08lx in %d files.\n", bi->DumpAddr32.lo, bi->DumpAddr32.lo + bi->DumpSize32, nMaxImages); // // Download image // bi->DumpSize32 = 0; iniFile.Rewind(); uint32 bytesread; // to ensure we read right amount for each file printf("Loading files"); // request each file, in order for (i = 0; i < nMaxImages; i++) { uint32 destination = bi->DumpAddr32.lo + bi->DumpSize32; LPUINT8 nextFname = iniFile.GetCurrentFileName(); // get the first block and size of the file pFile->FirstBlock = fileImage[i].Address; pFile->Size = fileImage[i].Size; // some media use the file name, others use the FilePtr object. We // send both to the function, and leave it up to the media to use // what it needs. bytesread = bootDevice->ReadFileHigh((LPCHAR) nextFname, pFile, destination, dumpRegionSize); if (bytesread != fileImage[i].Size) { printf("\n Read wrong # of bytes: %ld != %ld.\n", bytesread, fileImage[i].Size); BootHalt(); } else { printf("."); } // Change from address of first sector to physical memory address fileImage[i].Address = destination; bi->DumpSize32 += fileImage[i].Size; dumpRegionSize -= fileImage[i].Size; iniFile.MoveNext(); } printf("\n"); bi->FileImageTableBase32.lo = PointerToUint32(fileImage); bi->FileImageTableEntries = nMaxImages; // // Close the Boot Medium: // bootDevice->CloseDevice(); // // Allocate MpBootInfo structure // g_mbi = (Struct_Microsoft_Singularity_MpBootInfo __far *) alloc(sizeof(Struct_Microsoft_Singularity_MpBootInfo), 0x04); bi->MpBootInfo32.lo = PointerToUint32(g_mbi); bi->MpCpuCount = 0; bi->MpStartupLock32.lo = PointerToUint32(&MpStartupLock); // // Allocate pages for commonly referenced data // // 2 Pages (1 RW, 1 RO) for processor context LPVOID pfs = allocpages(2); // 2 Pages (1 RW, 1 RO) for thread LPVOID pgs = allocpages(2); uint32 pfsBase = PointerToUint32(pfs); uint32 pgsBase = PointerToUint32(pgs); // // Set up the GDT // bi->Cpu0.GdtPtr.addr = PointerToUint32(&bi->Cpu0.GdtNull); bi->Cpu0.GdtPtr.limit = (OFFSETOF(Struct_Microsoft_Singularity_CpuInfo, GdtEnd) - OFFSETOF(Struct_Microsoft_Singularity_CpuInfo, GdtNull)); bi->Cpu0.GdtRS.base0_15 = Struct_Microsoft_Singularity_BootInfo_REAL_CODE_BASE; bi->Cpu0.GdtRS.base16_23 = Struct_Microsoft_Singularity_BootInfo_REAL_CODE_BASE >> 16; bi->Cpu0.GdtRS.base24_31 = Struct_Microsoft_Singularity_BootInfo_REAL_CODE_BASE >> 24; bi->Cpu0.GdtRC.base0_15 = Struct_Microsoft_Singularity_BootInfo_REAL_CODE_BASE; bi->Cpu0.GdtRC.base16_23 = Struct_Microsoft_Singularity_BootInfo_REAL_CODE_BASE >> 16; bi->Cpu0.GdtRC.base24_31 = Struct_Microsoft_Singularity_BootInfo_REAL_CODE_BASE >> 24; bi->Cpu0.GdtPF.base0_15 = (uint16)pfsBase; bi->Cpu0.GdtPF.base16_23 = (uint8)(pfsBase >> 16); bi->Cpu0.GdtPF.base24_31 = (uint8)(pfsBase >> 24); bi->Cpu0.GdtPG.base0_15 = (uint16)pgsBase; bi->Cpu0.GdtPG.base16_23 = (uint8)(pgsBase >> 16); bi->Cpu0.GdtPG.base24_31 = (uint8)(pgsBase >> 24); bi->Cpu0.GdtRS.limit = 0xffff; bi->Cpu0.GdtRC.limit = 0xffff; bi->Cpu0.GdtPC.limit = 0xffff; bi->Cpu0.GdtPD.limit = 0xffff; bi->Cpu0.GdtUC.limit = 0xffff; bi->Cpu0.GdtUD.limit = 0xffff; bi->Cpu0.GdtPF.limit = 0x1; // 2 Pages (1 RW, 1 RO) bi->Cpu0.GdtPG.limit = 0x1; // 2 Pages (1 RW, 1 RO) bi->Cpu0.GdtRS.access = (Struct_Microsoft_Singularity_X86_GDTE_PRESENT | Struct_Microsoft_Singularity_X86_GDTE_RING0 | Struct_Microsoft_Singularity_X86_GDTE_USER | Struct_Microsoft_Singularity_X86_GDTE_WRITEABLE | Struct_Microsoft_Singularity_X86_GDTE_ACCESSED); bi->Cpu0.GdtRC.access = (Struct_Microsoft_Singularity_X86_GDTE_PRESENT | Struct_Microsoft_Singularity_X86_GDTE_RING0 | Struct_Microsoft_Singularity_X86_GDTE_USER | Struct_Microsoft_Singularity_X86_GDTE_READABLE | Struct_Microsoft_Singularity_X86_GDTE_CODE | Struct_Microsoft_Singularity_X86_GDTE_ACCESSED); bi->Cpu0.GdtPC.access = (Struct_Microsoft_Singularity_X86_GDTE_PRESENT | Struct_Microsoft_Singularity_X86_GDTE_RING0 | Struct_Microsoft_Singularity_X86_GDTE_USER | Struct_Microsoft_Singularity_X86_GDTE_READABLE | Struct_Microsoft_Singularity_X86_GDTE_CODE | Struct_Microsoft_Singularity_X86_GDTE_ACCESSED); bi->Cpu0.GdtPD.access = (Struct_Microsoft_Singularity_X86_GDTE_PRESENT | Struct_Microsoft_Singularity_X86_GDTE_RING0 | Struct_Microsoft_Singularity_X86_GDTE_USER | Struct_Microsoft_Singularity_X86_GDTE_WRITEABLE | Struct_Microsoft_Singularity_X86_GDTE_ACCESSED); bi->Cpu0.GdtUC.access = (Struct_Microsoft_Singularity_X86_GDTE_PRESENT | Struct_Microsoft_Singularity_X86_GDTE_RING3 | Struct_Microsoft_Singularity_X86_GDTE_USER | Struct_Microsoft_Singularity_X86_GDTE_READABLE | Struct_Microsoft_Singularity_X86_GDTE_CODE | Struct_Microsoft_Singularity_X86_GDTE_ACCESSED); bi->Cpu0.GdtUD.access = (Struct_Microsoft_Singularity_X86_GDTE_PRESENT | Struct_Microsoft_Singularity_X86_GDTE_RING3 | Struct_Microsoft_Singularity_X86_GDTE_USER | Struct_Microsoft_Singularity_X86_GDTE_WRITEABLE | Struct_Microsoft_Singularity_X86_GDTE_ACCESSED); bi->Cpu0.GdtPF.access = (Struct_Microsoft_Singularity_X86_GDTE_PRESENT | Struct_Microsoft_Singularity_X86_GDTE_RING3 | // for the moment, share UF and PF Struct_Microsoft_Singularity_X86_GDTE_USER | Struct_Microsoft_Singularity_X86_GDTE_WRITEABLE | Struct_Microsoft_Singularity_X86_GDTE_ACCESSED); bi->Cpu0.GdtPG.access = (Struct_Microsoft_Singularity_X86_GDTE_PRESENT | Struct_Microsoft_Singularity_X86_GDTE_RING0 | Struct_Microsoft_Singularity_X86_GDTE_USER | Struct_Microsoft_Singularity_X86_GDTE_WRITEABLE | Struct_Microsoft_Singularity_X86_GDTE_ACCESSED); bi->Cpu0.GdtRS.granularity = 0; bi->Cpu0.GdtRC.granularity = 0; bi->Cpu0.GdtPC.granularity = (Struct_Microsoft_Singularity_X86_GDTE_PAGES | Struct_Microsoft_Singularity_X86_GDTE_IS32BIT | Struct_Microsoft_Singularity_X86_GDTE_LIMIT20); bi->Cpu0.GdtPD.granularity = (Struct_Microsoft_Singularity_X86_GDTE_PAGES | Struct_Microsoft_Singularity_X86_GDTE_IS32BIT | Struct_Microsoft_Singularity_X86_GDTE_LIMIT20); bi->Cpu0.GdtUC.granularity = (Struct_Microsoft_Singularity_X86_GDTE_PAGES | Struct_Microsoft_Singularity_X86_GDTE_IS32BIT | Struct_Microsoft_Singularity_X86_GDTE_LIMIT20); bi->Cpu0.GdtUD.granularity = (Struct_Microsoft_Singularity_X86_GDTE_PAGES | Struct_Microsoft_Singularity_X86_GDTE_IS32BIT | Struct_Microsoft_Singularity_X86_GDTE_LIMIT20); bi->Cpu0.GdtPF.granularity = (Struct_Microsoft_Singularity_X86_GDTE_PAGES | Struct_Microsoft_Singularity_X86_GDTE_IS32BIT); bi->Cpu0.GdtPG.granularity = (Struct_Microsoft_Singularity_X86_GDTE_PAGES | Struct_Microsoft_Singularity_X86_GDTE_IS32BIT); Struct_Microsoft_Singularity_X86_TSS __far* ptss; ptss = (Struct_Microsoft_Singularity_X86_TSS __far*)allocpages(1); ptss->ss0 = (OFFSETOF(Struct_Microsoft_Singularity_CpuInfo, GdtPD) - OFFSETOF(Struct_Microsoft_Singularity_CpuInfo, GdtNull)); ptss->esp0 = PointerToUint32(ptss) + Struct_Microsoft_Singularity_BootInfo_PAGE_SIZE-0x10; ptss->io_bitmap_offset = sizeof(*ptss); uint32 tssaddr = PointerToUint32(ptss); bi->Cpu0.GdtTSS.base0_15 = (uint16)((tssaddr) & 0xffff); bi->Cpu0.GdtTSS.base16_23 = (uint8)((tssaddr >> 16) & 0xff); bi->Cpu0.GdtTSS.base24_31 = (uint8)((tssaddr >> 24) & 0xff); bi->Cpu0.GdtTSS.limit = sizeof(*ptss) - 1; bi->Cpu0.GdtTSS.access = (Struct_Microsoft_Singularity_X86_GDTE_PRESENT | Struct_Microsoft_Singularity_X86_GDTE_RING0 | Struct_Microsoft_Singularity_X86_GDTE_Tss32Free); #if !MAP_ZERO_PAGE // // Create a simple PAE page table for the double-fault handler. // uint64 __far * pdpe = (uint64 __far *) allocpages(1); // Page-directory-pointer table uint64 __far * pde = (uint64 __far *) allocpages(4); // Page-directory table uint64 __far * pte = (uint64 __far *) allocpages(1); // Page table uint32 pdptBase = PointerToUint32(pdpe); uint32 pdtsBase = PointerToUint32(pde); uint32 pteBase = PointerToUint32(pte); // Create page-directory-pointer entries for (i = 0; i < 4; i++) { pdpe[i].lo = (pdtsBase + (((uint32)i) << 12)) | (Struct_Microsoft_Singularity_X86_PE_VALID); } // Create PDE entries for full 4GB of addressable RAM. for (i = 0; i < 2048; i++) { pde[i].lo = (((uint32)i) << 21) | (Struct_Microsoft_Singularity_X86_PE_IS2MB | Struct_Microsoft_Singularity_X86_PE_ACCESSED | Struct_Microsoft_Singularity_X86_PE_WRITEABLE); } // Create a small-page entry for the low 2MB. pde[0].lo = (pteBase) | (Struct_Microsoft_Singularity_X86_PE_ACCESSED | Struct_Microsoft_Singularity_X86_PE_WRITEABLE); // Create the page table for the low 2MB page. for (i = 0; i < 512; i++) { pte[i].lo = (((uint32)i) << 12) | (Struct_Microsoft_Singularity_X86_PE_ACCESSED | Struct_Microsoft_Singularity_X86_PE_WRITEABLE); } // Map from 16KB to 2MB. for (i = 4; i < 512; i++) { pte[i].lo |= Struct_Microsoft_Singularity_X86_PE_VALID; } // Map physical memory pages (rounding up to 2MB) uint32 pages = (memoryKB + 0x7ff) >> 11; for (i = 0; i < pages; i++) { pde[i].lo |= Struct_Microsoft_Singularity_X86_PE_VALID; } // Make sure all SMAP addresses below 4GB are mapped. ACPI // data structures may be mapped higher than supported // physical memory (2GB), and we need to be able to read // these. for (i = 0; i < bi->SmapCount; i++) { if (sm[i].addr.hi == 0) { uint32 startKB = sm[i].addr.lo / 1024ul; uint32 sizeKB = sm[i].size.lo / 1024ul; uint32 currKB = startKB; while (currKB < startKB + sizeKB) { pde[currKB / 2048ul].lo |= Struct_Microsoft_Singularity_X86_PE_VALID; currKB += 2048ul; } } } // Mark the cached images as read-only. for (uint32 addr = bi->DumpAddr32.lo; addr < bi->DumpAddr32.lo + bi->DumpSize32; addr += 0x200000) { pde[addr >> 21].lo &= ~Struct_Microsoft_Singularity_X86_PE_WRITEABLE; printf(" %08lx..%08lx readonly\n", addr, addr + 0x1fffff); } // Map PCI memory based I/O ranges for (i = 0; i < nPciMemoryRanges; i++) { UINT32 start = lpPciRanges[i].base; UINT32 end = (start + lpPciRanges[i].length); printf(" %08lx..%08lx write-through, cache disabled (PCI memory)\n", start, end); start >>= 21; end >>= 21; do { pde[start].lo |= (Struct_Microsoft_Singularity_X86_PE_CACHEDISABLE | Struct_Microsoft_Singularity_X86_PE_WRITETHROUGH | Struct_Microsoft_Singularity_X86_PE_VALID); start ++; } while (start < end); } // Hack for I/O apics on NUMA box. We should be extracting // this from ACPI / MP Resources. (0xd0000000 >> 21) pde[0xd000 >> 5].lo |= (Struct_Microsoft_Singularity_X86_PE_CACHEDISABLE | Struct_Microsoft_Singularity_X86_PE_WRITETHROUGH | Struct_Microsoft_Singularity_X86_PE_VALID); // The last 64MB is mapped with caching disabled to allow // access to the APICs. for (i = 2016; i < 2048; i++) { pde[i].lo |= (Struct_Microsoft_Singularity_X86_PE_CACHEDISABLE | Struct_Microsoft_Singularity_X86_PE_WRITETHROUGH | Struct_Microsoft_Singularity_X86_PE_VALID); } bi->Pdpt32.lo = pdptBase; #endif // // Set up the IDT and some function vectors // bi->Info32.lo = PointerToUint32(bi); bi->Cpu0.Fs32 = pfsBase; bi->Cpu0.Gs32 = pgsBase; bi->Kill32.lo = PointerToUint32((LPVOID)StopPhase0); bi->KillAction = 0; bi->Undump.lo = PointerToUint32((LPVOID)undump_dat); bi->IdtEnter0.lo = PointerToUint32((LPVOID)IdtEnter0); bi->IdtEnter1.lo = PointerToUint32((LPVOID)IdtEnter1); bi->IdtEnterN.lo = PointerToUint32((LPVOID)IdtEnterN); bi->IdtTarget.lo = PointerToUint32((LPVOID)&IdtTarget); bi->MpEnter32.lo = PointerToUint32((LPVOID)MpEnter); uint32 __far * pWarmReset = (uint32 __far *)_MK_FP(0x40, 0x67); bi->BiosWarmResetVector = *pWarmReset; IoSpaceWrite8(CMOS_SELECT, 0xf); bi->BiosWarmResetCmos = IoSpaceRead8(CMOS_DATA); printf(" Warm Reset CMOS = %8x (before)\n", bi->BiosWarmResetCmos); printf(" Warm Reset Vector = %8lx %8lx (before)\n", bi->BiosWarmResetVector, (bi->BiosWarmResetVector >> 12) + (bi->BiosWarmResetVector & 0xffff)); *pWarmReset = (uint32)(LPVOID)MpEnter; IoSpaceWrite8(CMOS_SELECT, 0xf); IoSpaceWrite8(CMOS_DATA, 0xa); IoSpaceWrite8(CMOS_SELECT, 0xf); uint8 cmos = IoSpaceRead8(CMOS_DATA); printf(" Warm Reset CMOS = %8x (pre-boot)\n", cmos); printf(" Warm Reset Vector = %8lx %8lx (pre-boot)\n", *pWarmReset, (*pWarmReset >> 12) + (*pWarmReset & 0xffff)); // // Save the heap pointer. // bi->Heap32.lo = ((uint32)npHeapTop) << 4; // // Move to next phase // printf("Calling 32-bit code.\n"); BootPhase2(bi); return 0; } extern "C" void __cdecl StopPhase3() { Struct_Microsoft_Singularity_BootInfo __far *bi = g_bi; Cls(); BdInitDebugger(g_bi->DebugBasePort); printf("\n"); Cls(); printf("Singularity Boot Loader: Kernel Terminated w/ 0x%08x\n", bi->KillAction); uint32 __far * pWarmReset = (uint32 __far *)_MK_FP(0x40, 0x67); *pWarmReset = bi->BiosWarmResetVector; IoSpaceWrite8(CMOS_SELECT, 0xf); IoSpaceWrite8(CMOS_DATA, bi->BiosWarmResetCmos); IoSpaceWrite8(CMOS_SELECT, 0xf); uint8 cmos = IoSpaceRead8(CMOS_DATA); printf(" Warm Reset CMOS = %8x (restored)\n", cmos); printf(" Warm Reset Vector = %8lx %8lx (restored)\n", *pWarmReset, (*pWarmReset >> 12) + (*pWarmReset & 0xffff)); again: switch (bi->KillAction) { case Struct_Microsoft_Singularity_BootInfo_EXIT_AND_RESTART: printf("Reset via 8042.\n"); Reset(); BootHalt(); break; case Struct_Microsoft_Singularity_BootInfo_EXIT_AND_SHUTDOWN: printf("Power-off via APM. (idt=%08lx..%08lx)\n", bi->BiosIdtPtr.addr, bi->BiosIdtPtr.addr + bi->BiosIdtPtr.limit); ApmPowerOff(); BootHalt(); break; case Struct_Microsoft_Singularity_BootInfo_EXIT_AND_WARMBOOT: printf("Error: Warm boot requested, but wasn't handled by HAL.\n"); BootHalt(); break; case Struct_Microsoft_Singularity_BootInfo_EXIT_AND_HALT: printf("Halt requested.\n"); BootHalt(); break; default: printf("Unknown KillAction=%x\n", bi->KillAction); if (g_CmdLine[0] != '\0') { printf("Forcing to EXIT_AND_WARMBOOT\n"); bi->KillAction = Struct_Microsoft_Singularity_BootInfo_EXIT_AND_WARMBOOT; goto again; } BootHalt(); break; } } ////////////////////////////////////////////////////////////////////////////// // // MP Specific Routines // extern "C" void MpBootPhase1() { Struct_Microsoft_Singularity_MpBootInfo __far *p_mbi = g_mbi; g_bi->MpStatus32 = Struct_Microsoft_Singularity_MpBootStatus_Phase1Entry; if (p_mbi->signature != Struct_Microsoft_Singularity_MpBootInfo_Signature) { printf("Bad MP Signature"); g_bi->MpStatus32 = Struct_Microsoft_Singularity_MpBootStatus_BadSignature; BootHalt(); } int cpuId = (int)p_mbi->TargetCpu; if (cpuId >= Struct_Microsoft_Singularity_MpBootInfo_MAX_CPU) { printf("Stopping processor (%d >= %d)\n", cpuId, Struct_Microsoft_Singularity_MpBootInfo_MAX_CPU); g_bi->MpStatus32 = Struct_Microsoft_Singularity_MpBootStatus_ConfigLimit; BootHalt(); } // // Allocate pages for FS and GS data // // 2 Pages (1 RW, 1 RO) for processor context LPVOID pfs = (LPVOID) allocpages(2); if (pfs == 0) { g_bi->MpStatus32 = Struct_Microsoft_Singularity_MpBootStatus_AllocFailure; BootHalt; } // 2 Pages (1 RW, 1 RO) for processor context LPVOID pgs = (LPVOID) allocpages(2); if (pgs == 0) { g_bi->MpStatus32 = Struct_Microsoft_Singularity_MpBootStatus_AllocFailure; BootHalt(); } // // Copy CpuInfo of bootstrap processor and modify copy to suit // Struct_Microsoft_Singularity_CpuInfo __far* bsp = &g_bi->Cpu0; Struct_Microsoft_Singularity_CpuInfo __far* cpu = bsp + cpuId; memcopy(cpu, bsp, sizeof(Struct_Microsoft_Singularity_CpuInfo)); cpu->GdtPtr.addr = PointerToUint32(&cpu->GdtNull); cpu->Fs32 = PointerToUint32(pfs); cpu->GdtPF.base0_15 = (uint16)(cpu->Fs32); cpu->GdtPF.base16_23 = (uint8)(cpu->Fs32 >> 16); cpu->GdtPF.base24_31 = (uint8)(cpu->Fs32 >> 24); cpu->Gs32 = PointerToUint32(pgs); cpu->GdtPG.base0_15 = (uint16)cpu->Gs32; cpu->GdtPG.base16_23 = (uint8)(cpu->Gs32 >> 16); cpu->GdtPG.base24_31 = (uint8)(cpu->Gs32 >> 24); cpu->KernelStackBegin = p_mbi->KernelStackBegin; cpu->KernelStack = p_mbi->KernelStack; cpu->KernelStackLimit = p_mbi->KernelStackLimit; cpu->ProcessorId = cpuId; g_bi->MpCpuCount = cpuId; g_bi->MpStatus32 = Struct_Microsoft_Singularity_MpBootStatus_Phase2Entry; MpBootPhase2(g_bi, cpu); }