mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 19:43:32 +00:00
160 lines
9 KiB
C
160 lines
9 KiB
C
/*-*- 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 2020 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. │
|
||
╠──────────────────────────────────────────────────────────────────────────────╣
|
||
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
|
||
│░░░░░░░█▀█░█▀█░▀█▀░█░█░█▀█░█░░░█░░░█░█░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
|
||
│░░░░░░░█▀█░█░▄░░█░░█░█░█▀█░█░░░█░░░▀█▀░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
|
||
│░░░░░░░▀░▀░▀▀▀░░▀░░▀▀▀░▀░▀░▀▀▀░▀▀▀░░▀░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
|
||
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
|
||
│░░░░░░░█▀█░█▀█░█▀█░▀█▀░█▀█░█▀█░█░░░█▀▀░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
|
||
│░░░░░░░█▀▀░█ █░██▀░░█░░█▀█░█▀█░█░░░█▀▀░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
|
||
│░░░░░░░▀░░░▀▀▀░▀░▀░░▀░░▀░▀░▀▀▀░▀▀▀░▀▀▀░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
|
||
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
|
||
│░░░░░░░█▀▀░█░█░█▀▀░█▀█░█░█░▀█▀░█▀█░█▀█░█░░█▀▀░░░░░░░░░░░░░░░░░░░░░░░░▄▄░░░▐█░░│
|
||
│░░░░░░░█▀▀░▄▀▄░█▀▀░█░▄░█░█░░█░░█▀█░█▀█░█░░█▀▀░░░░░░░░░░░░▄▄▄░░░▄██▄░░█▀░░░█░▄░│
|
||
│░░░░░░░▀▀▀░▀░▀░▀▀▀░▀▀▀░▀▀▀░░▀░░▀░▀░▀▀▀░▀▀░▀▀▀░░░░░░░░░░▄██▀█▌░██▄▄░░▐█▀▄░▐█▀░░│
|
||
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▐█▀▀▌░░░▄▀▌░▌░█░▌░░▌░▌░░│
|
||
╠──────────────────────────────────────────────────────▌▀▄─▐──▀▄─▐▄─▐▄▐▄─▐▄─▐▄─│
|
||
│ αcτµαlly pδrταblε εxεcµταblε § no-frills virtual memory management │
|
||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||
#include "ape/relocations.h"
|
||
#include "libc/elf/def.h"
|
||
#include "libc/elf/struct/phdr.h"
|
||
#include "libc/macros.internal.h"
|
||
#include "libc/nexgen32e/uart.internal.h"
|
||
#include "libc/runtime/e820.internal.h"
|
||
#include "libc/runtime/metalprintf.internal.h"
|
||
#include "libc/runtime/pc.internal.h"
|
||
#include "libc/runtime/runtime.h"
|
||
|
||
/**
|
||
* Allocates new page of physical memory.
|
||
*/
|
||
noasan texthead uint64_t __new_page(struct mman *mm) {
|
||
uint64_t p;
|
||
if (mm->pdpi == mm->e820n) {
|
||
/* TODO: reclaim free pages */
|
||
return 0;
|
||
}
|
||
while (mm->pdp >= mm->e820[mm->pdpi].addr + mm->e820[mm->pdpi].size) {
|
||
if (++mm->pdpi == mm->e820n) return 0;
|
||
mm->pdp = mm->e820[mm->pdpi].addr;
|
||
}
|
||
p = mm->pdp;
|
||
mm->pdp += 4096;
|
||
return p;
|
||
}
|
||
|
||
/**
|
||
* Returns pointer to page table entry for page at virtual address.
|
||
* Additional page tables are allocated if needed as a side-effect.
|
||
*/
|
||
noasan texthead uint64_t *__get_virtual(struct mman *mm, uint64_t *t,
|
||
int64_t vaddr, bool maketables) {
|
||
uint64_t *e, p;
|
||
unsigned char h;
|
||
for (h = 39;; h -= 9) {
|
||
e = t + ((vaddr >> h) & 511);
|
||
if (h == 12) return e;
|
||
if (!(*e & PAGE_V)) {
|
||
if (!maketables) return NULL;
|
||
if (!(p = __new_page(mm))) return NULL;
|
||
*e = p | PAGE_V | PAGE_RW;
|
||
}
|
||
t = (uint64_t *)(BANE + (*e & PAGE_TA));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Sorts, rounds, and filters BIOS memory map.
|
||
*/
|
||
static noasan texthead void __normalize_e820(struct mman *mm) {
|
||
uint64_t a, b;
|
||
uint64_t x, y;
|
||
unsigned i, j, n;
|
||
for (n = i = 0; mm->e820[i].size; ++i) {
|
||
mm->e820[n] = mm->e820[i];
|
||
x = mm->e820[n].addr;
|
||
y = mm->e820[n].addr + mm->e820[n].size;
|
||
a = ROUNDUP(x, 4096);
|
||
b = ROUNDDOWN(y, 4096) - a;
|
||
if (b > 0 && mm->e820[i].type == kMemoryUsable) {
|
||
mm->e820[n].addr = a;
|
||
mm->e820[n].size = b;
|
||
++n;
|
||
}
|
||
}
|
||
for (i = 1; i < n; ++i) {
|
||
for (j = i; j > 0 && mm->e820[i].addr < mm->e820[j - 1].addr; --j) {
|
||
mm->e820[j] = mm->e820[j - 1];
|
||
}
|
||
mm->e820[j] = mm->e820[i];
|
||
}
|
||
mm->pdp = MAX(0x80000, mm->e820[0].addr);
|
||
mm->pdpi = 0;
|
||
mm->e820n = n;
|
||
}
|
||
|
||
/**
|
||
* Identity maps all usable physical memory to its negative address.
|
||
*/
|
||
static noasan texthead void __invert_memory(struct mman *mm, uint64_t *pml4t) {
|
||
uint64_t i, j, *m, p, pe;
|
||
for (i = 0; i < mm->e820n; ++i) {
|
||
for (p = mm->e820[i].addr, pe = mm->e820[i].addr + mm->e820[i].size;
|
||
p + 0x200000 < pe; p += 4096) {
|
||
m = __get_virtual(mm, pml4t, BANE + p, true);
|
||
if (m && !(*m & PAGE_V)) {
|
||
*m = p | PAGE_V | PAGE_RW;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
noasan texthead void __setup_mman(struct mman *mm, uint64_t *pml4t) {
|
||
__normalize_e820(mm);
|
||
__invert_memory(mm, pml4t);
|
||
}
|
||
|
||
/**
|
||
* Maps APE-defined ELF program headers into memory and clears BSS.
|
||
*/
|
||
noasan textreal void __map_phdrs(struct mman *mm, uint64_t *pml4t, uint64_t b) {
|
||
struct Elf64_Phdr *p;
|
||
uint64_t i, f, v, m, *e;
|
||
extern char ape_phdrs[] __attribute__((__weak__));
|
||
extern char ape_phdrs_end[] __attribute__((__weak__));
|
||
__setup_mman(mm, pml4t);
|
||
for (p = (struct Elf64_Phdr *)REAL(ape_phdrs), m = 0;
|
||
p < (struct Elf64_Phdr *)REAL(ape_phdrs_end); ++p) {
|
||
if (p->p_type == PT_LOAD || p->p_type == PT_GNU_STACK) {
|
||
f = PAGE_V | PAGE_U;
|
||
if (p->p_flags & PF_W) f |= PAGE_RW;
|
||
for (i = 0; i < p->p_memsz; i += 4096) {
|
||
if (i < p->p_filesz) {
|
||
v = b + p->p_offset + i;
|
||
m = MAX(m, v);
|
||
} else {
|
||
v = __clear_page(__new_page(mm));
|
||
}
|
||
*__get_virtual(mm, pml4t, p->p_vaddr + i, true) = (v & PAGE_TA) | f;
|
||
}
|
||
}
|
||
}
|
||
mm->pdp = MAX(mm->pdp, m);
|
||
}
|