#include "OSXP.h"
#include "thunk.h"
EFI_HANDLE *gIH;
/* Application entry point - this is where it all starts */
EFI_STATUS
efi_main(
EFI_HANDLE ImageHandle,
EFI_SYSTEM_TABLE *SystemTable
)
{
/* Initialize convenient global variables */
gIH = ImageHandle;
EfiInitializeDriverLib(ImageHandle, SystemTable);
/* Disable watchdog timer */
gBS->SetWatchdogTimer(0, 0, 0, NULL);
/* Switch to text mode */
InitScreen();
/* Install a master boot record on the disk and load it to 0000:7C00 */
if (InstallMBR() == EFI_SUCCESS) {
/* Read a boot loader from CD-ROM, if present, and load to 0000:7C00 */
ReadCD();
/* Install the interrupt vector table */
Debug(L"Installing real mode interrupt vector table.\n");
InstallIVT();
Debug(L"Switching to real mode, please hold on.\n");
gST->ConOut->ClearScreen(gST->ConOut);
ThunkMBR();
Debug(L"MBR returned. That shouldn't happen.\n");
}
Print(L"Unable to continue, aborting.\n");
return EFI_UNSUPPORTED;
}
UINT32 peek32(UINT32 address)
{
UINT32 *ptr = (UINT32 *) address;
return *ptr;
}
void poke32(UINT32 address, UINT32 value)
{
UINT32 *ptr = (UINT32 *) address;
*ptr = value;
}
UINT16 peek16(UINT32 address)
{
UINT16 *ptr = (UINT16 *) address;
return *ptr;
}
void poke16(UINT32 address, UINT16 value)
{
UINT16 *ptr = (UINT16 *) address;
*ptr = value;
}
UINT8 peek8(UINT32 address)
{
UINT8 *ptr = (UINT8 *) address;
return *ptr;
}
void poke8(UINT32 address, UINT8 value)
{
UINT8 *ptr = (UINT8 *) address;
*ptr = value;
}
static void InitScreen()
{
EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl;
EFI_STATUS rv;
UINT32 PCIEXBAR = 0xe0000000;
/* Set text mode */
if (gST->BootServices->LocateProtocol(
&gEfiConsoleControlProtocolGuid,
NULL,
(void **)&ConsoleControl) == EFI_SUCCESS)
{
ConsoleControl->SetMode(ConsoleControl, EfiConsoleControlScreenText);
}
/* Set full screen mode (on the iMac and MBP; should be 2 for the Mini) */
gST->ConOut->SetMode(gST->ConOut, 1);
/* Device 0 registers */
poke16(PCIEXBAR + PCI_GCC, PCI_GCC_DISABLE_IGD);
poke32(PCIEXBAR + PCI_DEVEN, PCI_DEVEN_DISABLE_IGD);
poke8 (PCIEXBAR + PCI_LAC, PCI_LAC_DISABLE_MDA);
/* Device 1 registers */
poke16(PCIEXBAR + 0x8000 + PCI_BCTRL1, PCI_BCTRL1_VGA_ENABLE);
Set80x50();
}
/* OSXP.efi is fully relocated at link time. The .data section gets mapped at
* 0x7000. __PADDING__ is the first variable stored in that section, and it
* reserves some space for the MBR and CD boot loader, which like to be loaded
* at 0000:7C00.
*/
char __PADDING__[8192] = {'S', 'h', 'i', 'b', 'b', 'y', '!', 0};
#include "Gpt.h"
#include "mbr.h"
#include "XP_MBR.h"
EFI_GUID Unused_GUID = {
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
};
EFI_GUID Windows_GUID = {
0xEBD0A0A2, 0xB9E5, 0x4433, 0x87, 0xC0, 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7
};
EFI_BLOCK_IO_PROTOCOL *BootDrive;
EFI_BLOCK_IO_PROTOCOL *HD;
static EFI_STATUS InstallMBR()
{
EFI_STATUS rv;
Debug(L"Installing MBR:\n");
/* Get loaded image device path */
EFI_LOADED_IMAGE_PROTOCOL *LIP;
if ((rv = gBS->HandleProtocol(gIH,
&gEfiLoadedImageProtocolGuid,
(VOID **)&LIP)) != EFI_SUCCESS) {
Debug(L" Unable to get LOADED_IMAGE_PROTOCOL: %r.\n", rv);
return EFI_NOT_FOUND;
}
EFI_DEVICE_PATH_PROTOCOL *DPP = EfiDuplicateDevicePath(
EfiDevicePathFromHandle(
LIP->DeviceHandle));
Debug(L" Image device path: %s\n", DevicePathToStr(DPP));
EFI_DEVICE_PATH_PROTOCOL *dpp = DPP;
/* Seek to ATAPI node in path */
while (!IsDevicePathEnd(dpp)) {
if ((DevicePathType(dpp) == MESSAGING_DEVICE_PATH) &&
(DevicePathSubType(dpp) == MSG_ATAPI_DP))
break;
dpp = NextDevicePathNode(dpp);
}
if ((DevicePathType(dpp) != MESSAGING_DEVICE_PATH) ||
(DevicePathSubType(dpp) != MSG_ATAPI_DP)) {
Debug(L" Unable to find ATAPI node in device path.\n");
return EFI_NOT_FOUND;
}
/* Terminate the device path, lopping off the HD node */
SetDevicePathEndNode(NextDevicePathNode(dpp));
Debug(L" Truncated device path: %s\n", DevicePathToStr(DPP));
EFI_HANDLE Handle;
if ((rv = gBS->LocateDevicePath(&gEfiBlockIoProtocolGuid,
&DPP,
&Handle)) != EFI_SUCCESS) {
Debug(L"Unable to get BLOCK_IO_PROTOCOL device handle: %r.\n", rv);
return EFI_NOT_FOUND;
}
/* Get EFI_BLOCK_IO_PROTOCOL interface */
if ((rv = gBS->HandleProtocol(Handle,
&gEfiBlockIoProtocolGuid,
(VOID **) &HD)) != EFI_SUCCESS) {
Debug(L" Unable to get EFI_BLOCK_IO_PROTOCOL interface: %r\n", rv);
return EFI_NOT_FOUND;
}
Debug(L"\n Interface to EFI_BLOCK_IO_PROTOCOL:\n");
Debug(L" Media ID = 0x%x\n", HD->Media->MediaId);
Debug(L" Removable = %d\n", HD->Media->RemovableMedia);
Debug(L" Present = %d\n", HD->Media->MediaPresent);
Debug(L" Logical = %d\n", HD->Media->LogicalPartition);
Debug(L" BlockSize = %d\n", HD->Media->BlockSize);
Debug(L" LastBlock = 0x%x\n", HD->Media->LastBlock);
/* Read GPT header */
CHAR8 Buffer[512];
EFI_PARTITION_TABLE_HEADER *GPTHdr = (EFI_PARTITION_TABLE_HEADER *) Buffer;
if ((rv = HD->ReadBlocks(HD,
HD->Media->MediaId,
1,
512,
(VOID *) Buffer)) != EFI_SUCCESS) {
Debug(L"Failed to read GPT header: %r\n", rv);
return EFI_NOT_FOUND;
}
Debug(L"\n GPT header:\n");
Debug(L" My LBA = %d\n", GPTHdr->MyLBA);
Debug(L" Alternate LBA = %d\n", GPTHdr->AlternateLBA);
Debug(L" First usable LBA = %d\n", GPTHdr->FirstUsableLBA);
Debug(L" Last usable LBA = %d\n", GPTHdr->LastUsableLBA);
Debug(L" Part entry LBA = %d\n", GPTHdr->PartitionEntryLBA);
Debug(L" # of part entries = %d\n", GPTHdr->NumberOfPartitionEntries);
Debug(L" Part entry size = %d\n", GPTHdr->SizeOfPartitionEntry);
/* Read GPT partitions and update MBR */
EFI_PARTITION_ENTRY GPTPart[4];
struct MBR_t *mbr = (struct MBT_t *) XP_MBR;
int i, mbr_parts = 0;
/* Add Protective MBR entry */
mbr->partition[mbr_parts].boot = 0x0;
mbr->partition[mbr_parts].start_chs[0] = 0xFF;
mbr->partition[mbr_parts].start_chs[1] = 0xFF;
mbr->partition[mbr_parts].start_chs[2] = 0xFF;
mbr->partition[mbr_parts].fstype = 0xEE; /* This is key! */
mbr->partition[mbr_parts].end_chs[0] = 0xFF;
mbr->partition[mbr_parts].end_chs[1] = 0xFF;
mbr->partition[mbr_parts].end_chs[2] = 0xFF;
mbr->partition[mbr_parts].start_lba = 0x1;
mbr->partition[mbr_parts].size = HD->Media->LastBlock - 1;
mbr_parts++;
for (i = 0; i < GPTHdr->NumberOfPartitionEntries; i++) {
if (i % 4 == 0)
if ((rv = HD->ReadBlocks(HD,
HD->Media->MediaId,
2 + i / 4,
512,
(VOID *)&GPTPart)) != EFI_SUCCESS) {
Debug(L"Failed to read GPT partition entries: %r\n", rv);
return EFI_NOT_FOUND;
}
if (EfiCompareGuid(&GPTPart[i % 4].PartitionTypeGUID, &Unused_GUID))
break;
Debug(L"\n GPT partition entry:\n");
Debug(L" GUID = %g\n", &GPTPart[i % 4].PartitionTypeGUID);
Debug(L" Start LBA = %d\n", GPTPart[i % 4].StartingLBA);
Debug(L" End LBA = %d\n", GPTPart[i % 4].EndingLBA);
Debug(L" Attributes = 0x%x\n", GPTPart[i % 4].Attributes);
Debug(L" Name = %s\n", GPTPart[i % 4].PartitionName);
/* Add Windows partitions to the MBR */
if (EfiCompareGuid(&GPTPart[i % 4].PartitionTypeGUID, &Windows_GUID)) {
/* MBR is limited to 4 partitions */
if (mbr_parts < 4) {
Debug(L" Adding this partition to the MBR.\n");
if (mbr_parts == 1) { /* First Windows partition */
mbr->partition[mbr_parts].boot = 0x80;
/* Fixup length of EFI "partition". This assumes that
* the Windows partitions all come after the EFI and
* HFS partitions.
*/
mbr->partition[0].size = GPTPart[i % 4].StartingLBA - 2;
} else
mbr->partition[mbr_parts].boot = 0x0;
mbr->partition[mbr_parts].start_chs[0] = 0xFF;
mbr->partition[mbr_parts].start_chs[1] = 0xFF;
mbr->partition[mbr_parts].start_chs[2] = 0xFF;
mbr->partition[mbr_parts].fstype = 0xC;
mbr->partition[mbr_parts].end_chs[0] = 0xFF;
mbr->partition[mbr_parts].end_chs[1] = 0xFF;
mbr->partition[mbr_parts].end_chs[2] = 0xFF;
mbr->partition[mbr_parts].start_lba =
GPTPart[i % 4].StartingLBA;
mbr->partition[mbr_parts].size =
GPTPart[i % 4].EndingLBA - GPTPart[i % 4].StartingLBA + 1;
mbr_parts++;
}
else
Debug(L"\n No room in the MBR for this partition.\n");
}
}
if (mbr_parts == 1) {
Print(L"No Windows partitions were found. To run OSXP, you need to\n");
Print(L"create at least one Windows partition on your startup disk\n");
Print(L"using the Disk Utility program on the Mac OS X installation\n");
Print(L"DVD. This usually means you will have to repartition your");
Print(L"disk and reinstall Mac OS X. More information is available\n");
Print(L"at the following web site:\n");
Print(L"\n");
Print(L"http://daemons.net/~clay/OSXP/\n");
Print(L"\n");
return EFI_NOT_FOUND;
}
/* Print MBR */
Debug(L"\n Master Boot Record:\n");
for (i = 0; i < 4; i++) {
Debug(L" Partition %d: act: %3s, id: %2x, start: %10d, size: %d\n",
i,
mbr->partition[i].boot & 0x80 ? L"Yes" : L"No",
mbr->partition[i].fstype,
mbr->partition[i].start_lba,
mbr->partition[i].size);
}
/* Write MBR */
if ((rv = HD->WriteBlocks(HD,
HD->Media->MediaId,
0,
512,
mbr)) == EFI_SUCCESS) {
Debug(L" Wrote MBR successfully.\n");
}
else {
Debug(L" Failed to write MBR: %r\n", rv);
return EFI_NOT_FOUND;
}
HD->FlushBlocks(HD);
/* MBR expects to be loaded at 0000:7C00 */
Debug(L" Copying MBR to 0000:7C00\n");
EfiCopyMem((VOID *) 0x7C00, XP_MBR, 512);
BootDrive = HD;
return EFI_SUCCESS;
}
EFI_BLOCK_IO_PROTOCOL *CD;
#include "ElTorito.h"
static void ReadCD()
{
EFI_HANDLE Handles[512];
EFI_STATUS rv;
int i, BufferSize = sizeof(Handles);
Debug(L"Looking for bootable CD-ROM:\n");
/* Get a list of all block IO devices */
if ((rv = gBS->LocateHandle(ByProtocol,
&gEfiBlockIoProtocolGuid,
NULL,
&BufferSize,
Handles)) != EFI_SUCCESS) {
Debug(L" LocateHandle() failed: %r\n", rv);
return;
}
/* Search for a raw device (whole disk, not a sliced device) */
for (i = 0; BufferSize > 0; BufferSize -= sizeof(EFI_HANDLE), i++) {
EFI_DEVICE_PATH_PROTOCOL *DPP = EfiDevicePathFromHandle(Handles[i]);
/* Seek to penultimate node in path */
while (!IsDevicePathEnd(DPP) &&
!IsDevicePathEnd(NextDevicePathNode(DPP)))
DPP = NextDevicePathNode(DPP);
if ((DevicePathType(DPP) == MESSAGING_DEVICE_PATH) &&
(DevicePathSubType(DPP) == MSG_ATAPI_DP)) {
Debug(L" Found an ATAPI block device: %s\n",
DevicePathToStr(EfiDevicePathFromHandle(Handles[i])));
/* Get EFI_BLOCK_IO_PROTOCOL interface */
if ((rv = gBS->HandleProtocol(Handles[i],
&gEfiBlockIoProtocolGuid,
(VOID **) &CD)) != EFI_SUCCESS) {
Debug(L" Unable to get EFI_BLOCK_IO_PROTOCOL: %r\n", rv);
continue;
}
Debug(L"\n Interface to EFI_BLOCK_IO_PROTOCOL:\n");
Debug(L" Media ID = 0x%x\n", CD->Media->MediaId);
Debug(L" Removable = %d\n", CD->Media->RemovableMedia);
Debug(L" Present = %d\n", CD->Media->MediaPresent);
Debug(L" Logical = %d\n", CD->Media->LogicalPartition);
Debug(L" BlockSize = %d\n", CD->Media->BlockSize);
Debug(L" LastBlock = 0x%x\n", CD->Media->LastBlock);
if ((CD->Media->RemovableMedia) &&
(CD->Media->BlockSize == 2048)) {
Debug(L" This looks like a CD-ROM drive.\n");
break;
}
else {
Debug(L" This does not look like a CD-ROM drive.\n");
CD = NULL;
}
Debug(L"\n");
}
}
if (CD == NULL) {
Debug(L" No CD-ROM block device was found.\n");
return;
}
CHAR8 Buffer[2048];
Debug(L" Reading boot record at sector 17.\n");
/* Validate CD */
if ((rv = CD->ReadBlocks(CD,
CD->Media->MediaId,
17,
2048,
(VOID *) Buffer)) != EFI_SUCCESS) {
Debug(L" Unable to read CD-ROM: %r\n", rv);
return;
}
EL_TORITO_BOOT_RECORD *ETBR = (EL_TORITO_BOOT_RECORD *) Buffer;
if ((ETBR->BootRecordIndicator != 0) ||
(ETBR->CD001[0] != 'C') ||
(ETBR->CD001[1] != 'D') ||
(ETBR->CD001[2] != '0') ||
(ETBR->CD001[3] != '0') ||
(ETBR->CD001[4] != '1') ||
(ETBR->Version != 1) ||
(ETBR->ElTorito[0] != 'E') ||
(ETBR->ElTorito[1] != 'L') ||
(ETBR->ElTorito[2] != ' ') ||
(ETBR->ElTorito[3] != 'T') ||
(ETBR->ElTorito[4] != 'O') ||
(ETBR->ElTorito[5] != 'R') ||
(ETBR->ElTorito[6] != 'I') ||
(ETBR->ElTorito[7] != 'T') ||
(ETBR->ElTorito[8] != 'O') ||
(ETBR->ElTorito[9] != ' ') ||
(ETBR->ElTorito[10] != 'S') ||
(ETBR->ElTorito[11] != 'P') ||
(ETBR->ElTorito[12] != 'E') ||
(ETBR->ElTorito[13] != 'C') ||
(ETBR->ElTorito[14] != 'I') ||
(ETBR->ElTorito[15] != 'F') ||
(ETBR->ElTorito[16] != 'I') ||
(ETBR->ElTorito[17] != 'C') ||
(ETBR->ElTorito[18] != 'A') ||
(ETBR->ElTorito[19] != 'T') ||
(ETBR->ElTorito[20] != 'I') ||
(ETBR->ElTorito[21] != 'O') ||
(ETBR->ElTorito[22] != 'N')) {
Debug(L" This does not appear to be a valid El Torito CD-ROM.\n");
return;
}
Debug(L" Reading boot catalog from sector %d.\n", ETBR->BootCatalog);
/* Read the boot catalog */
if ((rv = CD->ReadBlocks(CD,
CD->Media->MediaId,
ETBR->BootCatalog,
2048,
(VOID *) Buffer)) != EFI_SUCCESS) {
Debug(L" Unable to read CD-ROM: %r\n", rv);
return;
}
EL_TORITO_BOOT_CATALOG_HEADER *ETBCH =
(EL_TORITO_BOOT_CATALOG_HEADER *) Buffer;
/* Validate boot catalog */
if ((ETBCH->HeaderID != 1) ||
(ETBCH->Key[0] != 0x55) ||
(ETBCH->Key[1] != 0xAA)) {
Debug(L" CD does not contain a valid boot catalog.\n");
return;
}
EL_TORITO_BOOT_CATALOG_ENTRY *ETBCE =
(EL_TORITO_BOOT_CATALOG_ENTRY *)
(Buffer + sizeof(EL_TORITO_BOOT_CATALOG_HEADER));
Debug(L" Reading boot loader, %d bytes starting at sector %d\n",
ETBCE->SectorCount * 512,
ETBCE->StartAddress);
/* Load the CD boot loader at 0000:7C00 */
if ((rv = CD->ReadBlocks(CD,
CD->Media->MediaId,
ETBCE->StartAddress,
ETBCE->SectorCount * 512,
(VOID *) 0x7C00)) != EFI_SUCCESS) {
Debug(L" Unable to read boot loader from CD-ROM: %r\n", rv);
return;
}
BootDrive = CD;
}
extern int IVT;
void InstallIVT()
{
EfiCopyMem((VOID *) 0x0, (VOID *) &IVT, 256 * 4);
}