singrdk/base/boot/Singldr/singldr.cpp

1717 lines
58 KiB
C++
Raw Normal View History

2008-03-05 09:52:00 -05:00
//////////////////////////////////////////////////////////////////////////////
//
// 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);
}