mirror of
https://github.com/limine-bootloader/limine
synced 2024-12-05 06:31:55 +03:00
9274ee656e
* initial riscv64 port * enable Paging Mode feature for all architectures * riscv: add missing protocol docs * riscv: fix tests * docs: clarify `LIMINE_PAGING_MODE_DEFAULT` macro * build: fix whitespace in common/GNUmakefile * riscv: default to Sv48 paging when supported * vmm: make `VMM_MAX_LEVEL` 1-indexed * limine: do not call `reported_addr()` before finaling paging mode smp/riscv: do not overwrite the argument passed to APs * limine/riscv: update default paging mode in limine.h * test/riscv: pad OVMF.fd when downloading it
317 lines
8.9 KiB
C
317 lines
8.9 KiB
C
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <stdarg.h>
|
|
#include <lib/libc.h>
|
|
#include <lib/misc.h>
|
|
#include <lib/print.h>
|
|
#include <lib/trace.h>
|
|
#include <lib/real.h>
|
|
#include <fs/file.h>
|
|
#include <mm/pmm.h>
|
|
|
|
#if defined (UEFI)
|
|
EFI_SYSTEM_TABLE *gST;
|
|
EFI_BOOT_SERVICES *gBS;
|
|
EFI_RUNTIME_SERVICES *gRT;
|
|
EFI_HANDLE efi_image_handle;
|
|
EFI_MEMORY_DESCRIPTOR *efi_mmap = NULL;
|
|
UINTN efi_mmap_size = 0, efi_desc_size = 0;
|
|
UINT32 efi_desc_ver = 0;
|
|
#endif
|
|
|
|
bool editor_enabled = true;
|
|
|
|
bool parse_resolution(size_t *width, size_t *height, size_t *bpp, const char *buf) {
|
|
size_t res[3] = {0};
|
|
|
|
const char *first = buf;
|
|
for (size_t i = 0; i < 3; i++) {
|
|
const char *last;
|
|
size_t x = strtoui(first, &last, 10);
|
|
if (first == last)
|
|
break;
|
|
res[i] = x;
|
|
if (*last == 0)
|
|
break;
|
|
first = last + 1;
|
|
}
|
|
|
|
if (res[0] == 0 || res[1] == 0)
|
|
return false;
|
|
|
|
if (res[2] == 0)
|
|
res[2] = 32;
|
|
|
|
*width = res[0], *height = res[1];
|
|
if (bpp != NULL)
|
|
*bpp = res[2];
|
|
|
|
return true;
|
|
}
|
|
|
|
// This integer sqrt implementation has been adapted from:
|
|
// https://stackoverflow.com/questions/1100090/looking-for-an-efficient-integer-square-root-algorithm-for-arm-thumb2
|
|
uint64_t sqrt(uint64_t a_nInput) {
|
|
uint64_t op = a_nInput;
|
|
uint64_t res = 0;
|
|
uint64_t one = (uint64_t)1 << 62;
|
|
|
|
// "one" starts at the highest power of four <= than the argument.
|
|
while (one > op) {
|
|
one >>= 2;
|
|
}
|
|
|
|
while (one != 0) {
|
|
if (op >= res + one) {
|
|
op = op - (res + one);
|
|
res = res + 2 * one;
|
|
}
|
|
res >>= 1;
|
|
one >>= 2;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
size_t get_trailing_zeros(uint64_t val) {
|
|
for (size_t i = 0; i < 64; i++) {
|
|
if ((val & 1) != 0) {
|
|
return i;
|
|
}
|
|
val >>= 1;
|
|
}
|
|
return 64;
|
|
}
|
|
|
|
uint32_t oct2bin(uint8_t *str, uint32_t max) {
|
|
uint32_t value = 0;
|
|
while (max-- > 0) {
|
|
value <<= 3;
|
|
value += *str++ - '0';
|
|
}
|
|
return value;
|
|
}
|
|
|
|
uint32_t hex2bin(uint8_t *str, uint32_t size) {
|
|
uint32_t value = 0;
|
|
while (size-- > 0) {
|
|
value <<= 4;
|
|
if (*str >= '0' && *str <= '9')
|
|
value += (uint32_t)((*str) - '0');
|
|
else if (*str >= 'A' && *str <= 'F')
|
|
value += (uint32_t)((*str) - 'A' + 10);
|
|
else if (*str >= 'a' && *str <= 'f')
|
|
value += (uint32_t)((*str) - 'a' + 10);
|
|
str++;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
#if defined (UEFI)
|
|
|
|
#if defined (__riscv)
|
|
|
|
RISCV_EFI_BOOT_PROTOCOL *get_riscv_boot_protocol(void) {
|
|
EFI_GUID boot_proto_guid = RISCV_EFI_BOOT_PROTOCOL_GUID;
|
|
RISCV_EFI_BOOT_PROTOCOL *proto;
|
|
|
|
// LocateProtocol() is available from EFI version 1.1
|
|
if (gBS->Hdr.Revision >= ((1 << 16) | 10)) {
|
|
if (gBS->LocateProtocol(&boot_proto_guid, NULL, (void **)&proto) == EFI_SUCCESS) {
|
|
return proto;
|
|
}
|
|
}
|
|
|
|
UINTN bufsz = 0;
|
|
if (gBS->LocateHandle(ByProtocol, &boot_proto_guid, NULL, &bufsz, NULL) != EFI_BUFFER_TOO_SMALL)
|
|
return NULL;
|
|
|
|
EFI_HANDLE *handles_buf = ext_mem_alloc(bufsz);
|
|
if (handles_buf == NULL)
|
|
return NULL;
|
|
|
|
if (bufsz < sizeof(EFI_HANDLE))
|
|
goto error;
|
|
|
|
if (gBS->LocateHandle(ByProtocol, &boot_proto_guid, NULL, &bufsz, handles_buf) != EFI_SUCCESS)
|
|
goto error;
|
|
|
|
if (gBS->HandleProtocol(handles_buf[0], &boot_proto_guid, (void **)&proto) != EFI_SUCCESS)
|
|
goto error;
|
|
|
|
pmm_free(handles_buf, bufsz);
|
|
return proto;
|
|
|
|
error:
|
|
pmm_free(handles_buf, bufsz);
|
|
return NULL;
|
|
}
|
|
|
|
#endif
|
|
|
|
no_unwind bool efi_boot_services_exited = false;
|
|
|
|
bool efi_exit_boot_services(void) {
|
|
EFI_STATUS status;
|
|
|
|
EFI_MEMORY_DESCRIPTOR tmp_mmap[1];
|
|
efi_mmap_size = sizeof(tmp_mmap);
|
|
UINTN mmap_key = 0;
|
|
|
|
gBS->GetMemoryMap(&efi_mmap_size, tmp_mmap, &mmap_key, &efi_desc_size, &efi_desc_ver);
|
|
|
|
efi_mmap_size += 4096;
|
|
|
|
status = gBS->FreePool(efi_mmap);
|
|
if (status) {
|
|
goto fail;
|
|
}
|
|
|
|
status = gBS->AllocatePool(EfiLoaderData, efi_mmap_size, (void **)&efi_mmap);
|
|
if (status) {
|
|
goto fail;
|
|
}
|
|
|
|
EFI_MEMORY_DESCRIPTOR *efi_copy;
|
|
status = gBS->AllocatePool(EfiLoaderData, efi_mmap_size * 2, (void **)&efi_copy);
|
|
if (status) {
|
|
goto fail;
|
|
}
|
|
|
|
const size_t EFI_COPY_MAX_ENTRIES = (efi_mmap_size * 2) / efi_desc_size;
|
|
|
|
size_t retries = 0;
|
|
|
|
retry:
|
|
status = gBS->GetMemoryMap(&efi_mmap_size, efi_mmap, &mmap_key, &efi_desc_size, &efi_desc_ver);
|
|
if (retries == 0 && status) {
|
|
goto fail;
|
|
}
|
|
|
|
// Be gone, UEFI!
|
|
status = gBS->ExitBootServices(efi_image_handle, mmap_key);
|
|
if (status) {
|
|
if (retries == 128) {
|
|
goto fail;
|
|
}
|
|
retries++;
|
|
goto retry;
|
|
}
|
|
|
|
#if defined(__x86_64__) || defined(__i386__)
|
|
asm volatile ("cli" ::: "memory");
|
|
#elif defined (__aarch64__)
|
|
asm volatile ("msr daifset, #15" ::: "memory");
|
|
#elif defined (__riscv64)
|
|
asm volatile ("csrci sstatus, 0x2" ::: "memory");
|
|
#else
|
|
#error Unknown architecture
|
|
#endif
|
|
|
|
// Go through new EFI memmap and free up bootloader entries
|
|
size_t entry_count = efi_mmap_size / efi_desc_size;
|
|
|
|
size_t efi_copy_i = 0;
|
|
|
|
for (size_t i = 0; i < entry_count; i++) {
|
|
EFI_MEMORY_DESCRIPTOR *orig_entry = (void *)efi_mmap + i * efi_desc_size;
|
|
EFI_MEMORY_DESCRIPTOR *new_entry = (void *)efi_copy + efi_copy_i * efi_desc_size;
|
|
|
|
memcpy(new_entry, orig_entry, efi_desc_size);
|
|
|
|
uint64_t base = orig_entry->PhysicalStart;
|
|
uint64_t length = orig_entry->NumberOfPages * 4096;
|
|
uint64_t top = base + length;
|
|
|
|
// Find for a match in the untouched memory map
|
|
for (size_t j = 0; j < untouched_memmap_entries; j++) {
|
|
if (untouched_memmap[j].type != MEMMAP_USABLE)
|
|
continue;
|
|
|
|
if (top > untouched_memmap[j].base && top <= untouched_memmap[j].base + untouched_memmap[j].length) {
|
|
if (untouched_memmap[j].base < base) {
|
|
new_entry->NumberOfPages = (base - untouched_memmap[j].base) / 4096;
|
|
|
|
efi_copy_i++;
|
|
if (efi_copy_i == EFI_COPY_MAX_ENTRIES) {
|
|
panic(false, "efi: New memory map exhausted");
|
|
}
|
|
new_entry = (void *)efi_copy + efi_copy_i * efi_desc_size;
|
|
memcpy(new_entry, orig_entry, efi_desc_size);
|
|
|
|
new_entry->NumberOfPages -= (base - untouched_memmap[j].base) / 4096;
|
|
new_entry->PhysicalStart = base;
|
|
new_entry->VirtualStart = new_entry->PhysicalStart;
|
|
|
|
length = new_entry->NumberOfPages * 4096;
|
|
top = base + length;
|
|
}
|
|
|
|
if (untouched_memmap[j].base > base) {
|
|
new_entry->NumberOfPages = (untouched_memmap[j].base - base) / 4096;
|
|
|
|
efi_copy_i++;
|
|
if (efi_copy_i == EFI_COPY_MAX_ENTRIES) {
|
|
panic(false, "efi: New memory map exhausted");
|
|
}
|
|
new_entry = (void *)efi_copy + efi_copy_i * efi_desc_size;
|
|
memcpy(new_entry, orig_entry, efi_desc_size);
|
|
|
|
new_entry->NumberOfPages -= (untouched_memmap[j].base - base) / 4096;
|
|
new_entry->PhysicalStart = untouched_memmap[j].base;
|
|
new_entry->VirtualStart = new_entry->PhysicalStart;
|
|
|
|
base = new_entry->PhysicalStart;
|
|
length = new_entry->NumberOfPages * 4096;
|
|
top = base + length;
|
|
}
|
|
|
|
if (length < untouched_memmap[j].length) {
|
|
panic(false, "efi: Memory map corruption");
|
|
}
|
|
|
|
new_entry->Type = EfiConventionalMemory;
|
|
|
|
if (length == untouched_memmap[j].length) {
|
|
// It's a perfect match!
|
|
break;
|
|
}
|
|
|
|
new_entry->NumberOfPages = untouched_memmap[j].length / 4096;
|
|
|
|
efi_copy_i++;
|
|
if (efi_copy_i == EFI_COPY_MAX_ENTRIES) {
|
|
panic(false, "efi: New memory map exhausted");
|
|
}
|
|
new_entry = (void *)efi_copy + efi_copy_i * efi_desc_size;
|
|
memcpy(new_entry, orig_entry, efi_desc_size);
|
|
|
|
new_entry->NumberOfPages = (length - untouched_memmap[j].length) / 4096;
|
|
new_entry->PhysicalStart = base + untouched_memmap[j].length;
|
|
new_entry->VirtualStart = new_entry->PhysicalStart;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
efi_copy_i++;
|
|
if (efi_copy_i == EFI_COPY_MAX_ENTRIES) {
|
|
panic(false, "efi: New memory map exhausted");
|
|
}
|
|
}
|
|
|
|
efi_mmap = efi_copy;
|
|
efi_mmap_size = efi_copy_i * efi_desc_size;
|
|
|
|
efi_boot_services_exited = true;
|
|
|
|
printv("efi: Exited boot services.\n");
|
|
|
|
return true;
|
|
|
|
fail:
|
|
panic(false, "efi: Failed to exit boot services");
|
|
}
|
|
|
|
#endif
|