2021-02-22 00:26:36 +00:00
|
|
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
|
|
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
|
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
|
|
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
|
|
|
│ │
|
|
|
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
|
|
|
│ any purpose with or without fee is hereby granted, provided that the │
|
|
|
|
│ above copyright notice and this permission notice appear in all copies. │
|
|
|
|
│ │
|
|
|
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
|
|
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
|
|
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
|
|
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
|
|
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
|
|
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
|
|
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
|
|
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
|
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
2022-12-18 01:51:20 +00:00
|
|
|
#include "ape/relocations.h"
|
2022-10-14 16:52:35 +00:00
|
|
|
#include "ape/sections.internal.h"
|
2021-02-22 00:26:36 +00:00
|
|
|
#include "libc/dce.h"
|
2022-10-14 16:52:35 +00:00
|
|
|
#include "libc/intrin/bits.h"
|
2022-12-18 01:51:20 +00:00
|
|
|
#include "libc/intrin/newbie.h"
|
|
|
|
#include "libc/intrin/weaken.h"
|
2021-03-01 07:42:35 +00:00
|
|
|
#include "libc/macros.internal.h"
|
2021-02-22 00:26:36 +00:00
|
|
|
#include "libc/nt/efi.h"
|
|
|
|
#include "libc/nt/thunk/msabi.h"
|
2021-02-24 04:23:19 +00:00
|
|
|
#include "libc/runtime/e820.internal.h"
|
2021-02-22 07:33:44 +00:00
|
|
|
#include "libc/runtime/internal.h"
|
2021-02-24 04:23:19 +00:00
|
|
|
#include "libc/runtime/pc.internal.h"
|
2021-02-22 00:26:36 +00:00
|
|
|
#include "libc/runtime/runtime.h"
|
2021-02-24 04:23:19 +00:00
|
|
|
#include "libc/str/str.h"
|
|
|
|
|
2022-07-20 21:01:15 +00:00
|
|
|
/* TODO: Why can't we change CR3? Could it really need PML5T? */
|
|
|
|
/* TODO: Why does QEMU in UEFI mode take ten seconds to boot? */
|
2021-02-22 00:26:36 +00:00
|
|
|
|
2021-02-22 07:33:44 +00:00
|
|
|
struct EfiArgs {
|
2021-02-24 04:23:19 +00:00
|
|
|
char *Args[0x400 / sizeof(char *)];
|
|
|
|
char ArgBlock[0xC00];
|
2021-02-22 07:33:44 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static const EFI_GUID kEfiLoadedImageProtocol = LOADED_IMAGE_PROTOCOL;
|
2022-12-18 01:51:20 +00:00
|
|
|
static const EFI_GUID kEfiGraphicsOutputProtocol = GRAPHICS_OUTPUT_PROTOCOL;
|
|
|
|
|
|
|
|
extern const char vga_console[];
|
|
|
|
extern void _EfiPostboot(struct mman *, uint64_t *, uintptr_t, char **);
|
|
|
|
|
|
|
|
static void EfiInitVga(struct mman *mm, EFI_SYSTEM_TABLE *SystemTable) {
|
|
|
|
EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphInfo;
|
|
|
|
EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *GraphMode;
|
|
|
|
EFI_PIXEL_BITMASK *PixelInfo;
|
|
|
|
unsigned vid_typ = PC_VIDEO_TEXT;
|
|
|
|
size_t bytes_per_pix = 0;
|
|
|
|
|
|
|
|
SystemTable->BootServices->LocateProtocol(&kEfiGraphicsOutputProtocol, NULL,
|
|
|
|
&GraphInfo);
|
|
|
|
GraphMode = GraphInfo->Mode;
|
|
|
|
switch (GraphMode->Info->PixelFormat) {
|
|
|
|
case PixelRedGreenBlueReserved8BitPerColor:
|
|
|
|
vid_typ = PC_VIDEO_RGBX8888;
|
|
|
|
bytes_per_pix = 4;
|
|
|
|
break;
|
|
|
|
case PixelBlueGreenRedReserved8BitPerColor:
|
|
|
|
vid_typ = PC_VIDEO_BGRX8888;
|
|
|
|
bytes_per_pix = 4;
|
|
|
|
break;
|
|
|
|
case PixelBitMask:
|
|
|
|
PixelInfo = &GraphMode->Info->PixelInformation;
|
|
|
|
switch (le32toh(PixelInfo->RedMask)) {
|
|
|
|
case 0x00FF0000U:
|
|
|
|
if (le32toh(PixelInfo->ReservedMask) >= 0x01000000U &&
|
|
|
|
le32toh(PixelInfo->GreenMask) == 0x0000FF00U &&
|
|
|
|
le32toh(PixelInfo->BlueMask) == 0x000000FFU) {
|
|
|
|
vid_typ = PC_VIDEO_BGRX8888;
|
|
|
|
bytes_per_pix = 4;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x000000FFU:
|
|
|
|
if (le32toh(PixelInfo->ReservedMask) >= 0x01000000U &&
|
|
|
|
le32toh(PixelInfo->GreenMask) == 0x0000FF00U &&
|
|
|
|
le32toh(PixelInfo->BlueMask) == 0x00FF0000U) {
|
|
|
|
vid_typ = PC_VIDEO_RGBX8888;
|
|
|
|
bytes_per_pix = 4;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x0000F800U:
|
|
|
|
if (le32toh(PixelInfo->ReservedMask) <= 0x0000FFFFU &&
|
|
|
|
le32toh(PixelInfo->GreenMask) == 0x000007E0U &&
|
|
|
|
le32toh(PixelInfo->BlueMask) == 0x0000001FU) {
|
|
|
|
vid_typ = PC_VIDEO_BGR565;
|
|
|
|
bytes_per_pix = 2;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x00007C00U:
|
|
|
|
if (le32toh(PixelInfo->ReservedMask) <= 0x0000FFFFU &&
|
|
|
|
le32toh(PixelInfo->GreenMask) == 0x000003E0U &&
|
|
|
|
le32toh(PixelInfo->BlueMask) == 0x0000001FU) {
|
|
|
|
vid_typ = PC_VIDEO_BGR555;
|
|
|
|
bytes_per_pix = 2;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
notpossible;
|
|
|
|
}
|
|
|
|
if (!bytes_per_pix) notpossible;
|
|
|
|
mm->pc_video_type = vid_typ;
|
|
|
|
mm->pc_video_stride = GraphMode->Info->PixelsPerScanLine * bytes_per_pix;
|
|
|
|
mm->pc_video_width = GraphMode->Info->HorizontalResolution;
|
|
|
|
mm->pc_video_height = GraphMode->Info->VerticalResolution;
|
|
|
|
mm->pc_video_framebuffer = GraphMode->FrameBufferBase;
|
|
|
|
mm->pc_video_framebuffer_size = GraphMode->FrameBufferSize;
|
|
|
|
mm->pc_video_curs_info.y = mm->pc_video_curs_info.x = 0;
|
|
|
|
SystemTable->BootServices->SetMem((void *)GraphMode->FrameBufferBase,
|
|
|
|
GraphMode->FrameBufferSize, 0);
|
|
|
|
}
|
2021-02-22 07:33:44 +00:00
|
|
|
|
2021-02-22 00:26:36 +00:00
|
|
|
/**
|
|
|
|
* EFI Application Entrypoint.
|
|
|
|
*
|
|
|
|
* This entrypoint is mutually exclusive from WinMain since
|
|
|
|
* Windows apps and EFI apps use the same PE binary format.
|
2021-02-24 04:23:19 +00:00
|
|
|
* So if you want to trade away Windows so that you can use
|
|
|
|
* UEFI instead of the normal BIOS boot process, do this:
|
2021-02-22 00:26:36 +00:00
|
|
|
*
|
2021-02-24 04:23:19 +00:00
|
|
|
* STATIC_YOINK("EfiMain");
|
|
|
|
* int main() { ... }
|
2021-02-22 00:26:36 +00:00
|
|
|
*
|
2021-02-24 04:23:19 +00:00
|
|
|
* You can use QEMU to test this, but please note that UEFI
|
|
|
|
* goes thousands of times slower than the normal BIOS boot
|
2021-02-22 00:26:36 +00:00
|
|
|
*
|
|
|
|
* qemu-system-x86_64 \
|
|
|
|
* -bios OVMF.fd \
|
2021-02-22 07:33:44 +00:00
|
|
|
* -nographic \
|
2021-02-22 00:26:36 +00:00
|
|
|
* -net none \
|
|
|
|
* -drive format=raw,file=fat:rw:o/tool/viz
|
|
|
|
* FS0:
|
|
|
|
* deathstar.com
|
|
|
|
*
|
|
|
|
* @see libc/dce.h
|
|
|
|
*/
|
|
|
|
__msabi noasan EFI_STATUS EfiMain(EFI_HANDLE ImageHandle,
|
|
|
|
EFI_SYSTEM_TABLE *SystemTable) {
|
2022-12-18 01:51:20 +00:00
|
|
|
int type, x87cw = 0x037f;
|
2021-02-24 04:23:19 +00:00
|
|
|
struct mman *mm;
|
|
|
|
uint32_t DescVersion;
|
|
|
|
uintptr_t i, j, MapSize;
|
|
|
|
struct EfiArgs *ArgBlock;
|
|
|
|
EFI_LOADED_IMAGE *ImgInfo;
|
2022-12-18 01:51:20 +00:00
|
|
|
EFI_MEMORY_DESCRIPTOR *Map, *Desc;
|
|
|
|
uint64_t Address;
|
2021-02-24 04:23:19 +00:00
|
|
|
uintptr_t Args, MapKey, DescSize;
|
|
|
|
uint64_t p, pe, cr4, *m, *pd, *sp, *pml4t, *pdt1, *pdt2, *pdpt1, *pdpt2;
|
|
|
|
|
|
|
|
/*
|
2022-12-18 01:51:20 +00:00
|
|
|
* Allocates and clears PC-compatible memory and copies image. Marks the
|
|
|
|
* pages as EfiRuntimeServicesData, so that we can simply free up all
|
|
|
|
* EfiLoader... and EfiBootServices... pages later. The first page at
|
|
|
|
* address 0 is normally already allocated as EfiBootServicesData, so
|
|
|
|
* handle it separately.
|
2021-02-24 04:23:19 +00:00
|
|
|
*/
|
2022-12-18 01:51:20 +00:00
|
|
|
Address = 0;
|
|
|
|
SystemTable->BootServices->AllocatePages(
|
|
|
|
AllocateAddress, EfiRuntimeServicesData, 4096 / 4096, &Address);
|
|
|
|
Address = 4096;
|
2021-02-24 04:23:19 +00:00
|
|
|
SystemTable->BootServices->AllocatePages(
|
2022-12-18 01:51:20 +00:00
|
|
|
AllocateAddress, EfiRuntimeServicesData, (IMAGE_BASE_REAL - 4096) / 4096,
|
|
|
|
&Address);
|
|
|
|
Address = 0x79000;
|
|
|
|
SystemTable->BootServices->AllocatePages(
|
|
|
|
AllocateAddress, EfiRuntimeServicesData,
|
|
|
|
(0x7e000 - 0x79000 + sizeof(struct EfiArgs) + 4095) / 4096, &Address);
|
|
|
|
Address = IMAGE_BASE_PHYSICAL;
|
|
|
|
SystemTable->BootServices->AllocatePages(
|
|
|
|
AllocateAddress, EfiRuntimeServicesData,
|
|
|
|
((_end - _base) + 4095) / 4096, &Address);
|
|
|
|
mm = (struct mman *)0x0500;
|
|
|
|
SystemTable->BootServices->SetMem(mm, sizeof(*mm), 0);
|
|
|
|
SystemTable->BootServices->SetMem((void *)0x79000,
|
|
|
|
0x7e000 - 0x79000 + sizeof(struct EfiArgs), 0);
|
|
|
|
SystemTable->BootServices->CopyMem((void *)IMAGE_BASE_PHYSICAL, _base,
|
2021-02-24 04:23:19 +00:00
|
|
|
_end - _base);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Converts UEFI shell arguments to argv.
|
|
|
|
*/
|
|
|
|
ArgBlock = (struct EfiArgs *)0x7e000;
|
2021-02-22 07:33:44 +00:00
|
|
|
SystemTable->BootServices->HandleProtocol(ImageHandle,
|
2021-02-24 04:23:19 +00:00
|
|
|
&kEfiLoadedImageProtocol, &ImgInfo);
|
|
|
|
Args = GetDosArgv(ImgInfo->LoadOptions, ArgBlock->ArgBlock,
|
|
|
|
sizeof(ArgBlock->ArgBlock), ArgBlock->Args,
|
|
|
|
ARRAYLEN(ArgBlock->Args));
|
|
|
|
|
2022-12-18 01:51:20 +00:00
|
|
|
/*
|
|
|
|
* Gets information about our current video mode. Clears the screen.
|
|
|
|
* TODO: if needed, switch to a video mode that has a linear frame buffer
|
|
|
|
* type we support.
|
|
|
|
*/
|
|
|
|
if (_weaken(vga_console)) EfiInitVga(mm, SystemTable);
|
|
|
|
|
2021-02-24 04:23:19 +00:00
|
|
|
/*
|
|
|
|
* Asks UEFI which parts of our RAM we're allowed to use.
|
|
|
|
*/
|
|
|
|
Map = NULL;
|
|
|
|
MapSize = 0;
|
|
|
|
SystemTable->BootServices->GetMemoryMap(&MapSize, Map, &MapKey, &DescSize,
|
|
|
|
&DescVersion);
|
2022-12-18 01:51:20 +00:00
|
|
|
SystemTable->BootServices->AllocatePool(EfiLoaderData, MapSize, &Map);
|
|
|
|
MapSize *= 2;
|
2021-02-24 04:23:19 +00:00
|
|
|
SystemTable->BootServices->GetMemoryMap(&MapSize, Map, &MapKey, &DescSize,
|
|
|
|
&DescVersion);
|
2022-12-18 01:51:20 +00:00
|
|
|
for (j = i = 0, Desc = Map; i < MapSize / DescSize; ++i) {
|
|
|
|
switch (Desc->Type) {
|
|
|
|
case EfiLoaderCode:
|
|
|
|
case EfiLoaderData:
|
|
|
|
case EfiBootServicesCode:
|
|
|
|
case EfiBootServicesData:
|
|
|
|
if (Desc->PhysicalStart != 0)
|
|
|
|
break;
|
|
|
|
/* fallthrough */
|
|
|
|
case EfiConventionalMemory:
|
|
|
|
mm->e820[j].addr = Desc->PhysicalStart;
|
|
|
|
mm->e820[j].size = Desc->NumberOfPages * 4096;
|
|
|
|
mm->e820[j].type = kMemoryUsable;
|
|
|
|
++j;
|
|
|
|
}
|
|
|
|
Desc = (EFI_MEMORY_DESCRIPTOR *)((char *)Desc + DescSize);
|
2021-02-24 04:23:19 +00:00
|
|
|
}
|
|
|
|
SystemTable->BootServices->FreePool(Map);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sets up page tables.
|
|
|
|
*/
|
|
|
|
pd = (uint64_t *)0x79000;
|
|
|
|
pdt1 = (uint64_t *)0x7b000;
|
|
|
|
pdt2 = (uint64_t *)0x7a000;
|
|
|
|
pdpt1 = (uint64_t *)0x7d000;
|
|
|
|
pdpt2 = (uint64_t *)0x7c000;
|
|
|
|
pml4t = (uint64_t *)0x7e000;
|
|
|
|
for (i = 0; i < 512; ++i) {
|
2022-10-12 18:07:11 +00:00
|
|
|
pd[i] = 0x1000 * i + PAGE_V + PAGE_RSRV + PAGE_RW;
|
2021-02-24 04:23:19 +00:00
|
|
|
}
|
|
|
|
pdt1[0] = (intptr_t)pd + PAGE_V + PAGE_RW;
|
|
|
|
pdt2[0] = (intptr_t)pd + PAGE_V + PAGE_RW;
|
|
|
|
pdpt1[0] = (intptr_t)pdt1 + PAGE_V + PAGE_RW;
|
|
|
|
pdpt2[0] = (intptr_t)pdt2 + PAGE_V + PAGE_RW;
|
|
|
|
pml4t[0] = (intptr_t)pdpt1 + PAGE_V + PAGE_RW;
|
|
|
|
pml4t[256] = (intptr_t)pdpt2 + PAGE_V + PAGE_RW;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Asks UEFI to handover control?
|
|
|
|
*/
|
|
|
|
SystemTable->BootServices->ExitBootServices(ImageHandle, MapKey);
|
|
|
|
|
|
|
|
/*
|
2022-12-18 01:51:20 +00:00
|
|
|
* Switches to copied image and launches program.
|
2021-02-24 04:23:19 +00:00
|
|
|
*/
|
2022-12-18 01:51:20 +00:00
|
|
|
_EfiPostboot(mm, pml4t, Args, ArgBlock->Args);
|
2021-02-22 00:26:36 +00:00
|
|
|
unreachable;
|
|
|
|
}
|