mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-08-03 08:20:28 +00:00
[metal] Allow programs larger than 440 KiB to run in bare metal mode (#685)
* [metal] Copy program pages to extended memory at startup * [metal] Reclaim base memory pages for later app use * [metal] Load program pages beyond 1st 440 KiB to extended memory o//examples/hellolua.com now runs correctly under QEMU (in legacy BIOS mode). * [metal] Place GDT in read/write segment The CPU absolutely needs to alter the GDT when loading the task register (via ltr). To account for this, I move the GDT into a read/write data section. There is still a "rump" read-only GDT in the text section that is used by the real mode bootloader. We also delay the loading of the task register (ltr) until after the IDT and TSS are finally set up. * [metal] Get examples/vga2.c serial output working for UEFI boot * [metal] Get examples/vga2.c VGA output working for UEFI boot * [metal] Allow munmap() to reclaim dynamically allocated pages * Place TLS sections right after .text, not after embedded zip file Co-authored-by: tkchia <tkchia-cosmo@gmx.com>
This commit is contained in:
parent
120079b0a6
commit
0da47c51de
16 changed files with 725 additions and 191 deletions
|
@ -16,9 +16,12 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "ape/relocations.h"
|
||||
#include "ape/sections.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/newbie.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/efi.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
|
@ -37,6 +40,80 @@ struct EfiArgs {
|
|||
};
|
||||
|
||||
static const EFI_GUID kEfiLoadedImageProtocol = LOADED_IMAGE_PROTOCOL;
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* EFI Application Entrypoint.
|
||||
|
@ -64,27 +141,44 @@ static const EFI_GUID kEfiLoadedImageProtocol = LOADED_IMAGE_PROTOCOL;
|
|||
*/
|
||||
__msabi noasan EFI_STATUS EfiMain(EFI_HANDLE ImageHandle,
|
||||
EFI_SYSTEM_TABLE *SystemTable) {
|
||||
int type, x87cw;
|
||||
int type, x87cw = 0x037f;
|
||||
struct mman *mm;
|
||||
uint32_t DescVersion;
|
||||
uintptr_t i, j, MapSize;
|
||||
struct EfiArgs *ArgBlock;
|
||||
EFI_LOADED_IMAGE *ImgInfo;
|
||||
EFI_MEMORY_DESCRIPTOR *Map;
|
||||
EFI_MEMORY_DESCRIPTOR *Map, *Desc;
|
||||
uint64_t Address;
|
||||
uintptr_t Args, MapKey, DescSize;
|
||||
uint64_t p, pe, cr4, *m, *pd, *sp, *pml4t, *pdt1, *pdt2, *pdpt1, *pdpt2;
|
||||
extern char __os asm("__hostos");
|
||||
|
||||
__os = _HOSTMETAL;
|
||||
|
||||
/*
|
||||
* Allocates and clears PC-compatible memory and copies image.
|
||||
* 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.
|
||||
*/
|
||||
Address = 0;
|
||||
SystemTable->BootServices->AllocatePages(
|
||||
AllocateAddress, EfiConventionalMemory,
|
||||
MAX(2 * 1024 * 1024, 1024 * 1024 + (_end - _base)) / 4096, 0);
|
||||
SystemTable->BootServices->SetMem(0, 0x80000, 0);
|
||||
SystemTable->BootServices->CopyMem((void *)(1024 * 1024), _base,
|
||||
AllocateAddress, EfiRuntimeServicesData, 4096 / 4096, &Address);
|
||||
Address = 4096;
|
||||
SystemTable->BootServices->AllocatePages(
|
||||
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,
|
||||
_end - _base);
|
||||
|
||||
/*
|
||||
|
@ -97,6 +191,13 @@ __msabi noasan EFI_STATUS EfiMain(EFI_HANDLE ImageHandle,
|
|||
sizeof(ArgBlock->ArgBlock), ArgBlock->Args,
|
||||
ARRAYLEN(ArgBlock->Args));
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
/*
|
||||
* Asks UEFI which parts of our RAM we're allowed to use.
|
||||
*/
|
||||
|
@ -104,16 +205,26 @@ __msabi noasan EFI_STATUS EfiMain(EFI_HANDLE ImageHandle,
|
|||
MapSize = 0;
|
||||
SystemTable->BootServices->GetMemoryMap(&MapSize, Map, &MapKey, &DescSize,
|
||||
&DescVersion);
|
||||
SystemTable->BootServices->AllocatePool(EfiLoaderData, MapSize, Map);
|
||||
SystemTable->BootServices->AllocatePool(EfiLoaderData, MapSize, &Map);
|
||||
MapSize *= 2;
|
||||
SystemTable->BootServices->GetMemoryMap(&MapSize, Map, &MapKey, &DescSize,
|
||||
&DescVersion);
|
||||
asm("xor\t%0,%0" : "=r"(mm)); /* gcc assumes null isn't mapped */
|
||||
for (j = i = 0; i < MapSize / sizeof(EFI_MEMORY_DESCRIPTOR); ++i) {
|
||||
if (Map[i].Type != EfiConventionalMemory) continue;
|
||||
mm->e820[j].addr = Map[i].PhysicalStart;
|
||||
mm->e820[j].size = Map[i].NumberOfPages * 4096;
|
||||
mm->e820[j].type = kMemoryUsable;
|
||||
++j;
|
||||
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);
|
||||
}
|
||||
SystemTable->BootServices->FreePool(Map);
|
||||
|
||||
|
@ -135,7 +246,6 @@ __msabi noasan EFI_STATUS EfiMain(EFI_HANDLE ImageHandle,
|
|||
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;
|
||||
__map_phdrs(mm, pml4t, 1024 * 1024);
|
||||
|
||||
/*
|
||||
* Asks UEFI to handover control?
|
||||
|
@ -143,45 +253,8 @@ __msabi noasan EFI_STATUS EfiMain(EFI_HANDLE ImageHandle,
|
|||
SystemTable->BootServices->ExitBootServices(ImageHandle, MapKey);
|
||||
|
||||
/*
|
||||
* Launches program.
|
||||
* Switches to copied image and launches program.
|
||||
*/
|
||||
asm volatile("fldcw\t%3\n\t"
|
||||
"mov\t%4,%%cr3\n\t"
|
||||
".weak\tape_stack_vaddr\n\t"
|
||||
".weak\tape_stack_memsz\n\t"
|
||||
"movabs\t$ape_stack_vaddr,%%rsp\n\t"
|
||||
"add\t$ape_stack_memsz,%%rsp\n\t"
|
||||
"push\t$0\n\t" /* auxv[1][1] */
|
||||
"push\t$0\n\t" /* auxv[1][0] */
|
||||
"push\t(%1)\n\t" /* auxv[0][1] */
|
||||
"push\t$31\n\t" /* auxv[0][0] AT_EXECFN */
|
||||
"push\t$0\n\t" /* envp[0] */
|
||||
"sub\t%2,%%rsp\n\t"
|
||||
"mov\t%%rsp,%%rdi\n\t"
|
||||
"rep movsb\n\t" /* argv */
|
||||
"push\t%0\n\t" /* argc */
|
||||
"xor\t%%edi,%%edi\n\t"
|
||||
"xor\t%%eax,%%eax\n\t"
|
||||
"xor\t%%ebx,%%ebx\n\t"
|
||||
"xor\t%%ecx,%%ecx\n\t"
|
||||
"xor\t%%edx,%%edx\n\t"
|
||||
"xor\t%%edi,%%edi\n\t"
|
||||
"xor\t%%esi,%%esi\n\t"
|
||||
"xor\t%%ebp,%%ebp\n\t"
|
||||
"xor\t%%r8d,%%r8d\n\t"
|
||||
"xor\t%%r9d,%%r9d\n\t"
|
||||
"xor\t%%r10d,%%r10d\n\t"
|
||||
"xor\t%%r11d,%%r11d\n\t"
|
||||
"xor\t%%r12d,%%r12d\n\t"
|
||||
"xor\t%%r13d,%%r13d\n\t"
|
||||
"xor\t%%r14d,%%r14d\n\t"
|
||||
"xor\t%%r15d,%%r15d\n\t"
|
||||
".weak\t_start\n\t"
|
||||
"jmp\t_start"
|
||||
: /* no outputs */
|
||||
: "a"(Args), "S"(ArgBlock->Args), "c"((Args + 1) * 8),
|
||||
"m"(x87cw), "r"(pml4t)
|
||||
: "memory");
|
||||
|
||||
_EfiPostboot(mm, pml4t, Args, ArgBlock->Args);
|
||||
unreachable;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue