aarch64: be more flexible about where the kernel is physically

This commit is contained in:
K. Lange 2022-02-06 21:10:20 +09:00
parent 097662ef3d
commit e1e66bf6cd
7 changed files with 78 additions and 70 deletions

View File

@ -18,6 +18,9 @@
#include <kernel/printf.h>
#include <kernel/elf.h>
#define QEMU_DTB_BASE 0x40000000UL
#define KERNEL_PHYS_BASE 0x41000000UL
static uint32_t swizzle(uint32_t from) {
uint8_t a = from >> 24;
uint8_t b = from >> 16;
@ -153,7 +156,7 @@ static uint32_t * find_subnode(uint32_t * node, char * strings, const char * nam
}
static uint32_t * find_node_int(const char * name, int (*cmp)(const char*,const char*)) {
uintptr_t addr = 0x40000000;
uintptr_t addr = QEMU_DTB_BASE;
struct fdt_header * fdt = (struct fdt_header*)addr;
char * dtb_strings = (char *)(addr + swizzle(fdt->off_dt_strings));
uint32_t * dtb_struct = (uint32_t *)(addr + swizzle(fdt->off_dt_struct));
@ -202,7 +205,7 @@ static uint32_t * node_find_property_int(uint32_t * node, char * strings, const
}
static uint32_t * node_find_property(uint32_t * node, const char * property) {
uintptr_t addr = 0x40000000;
uintptr_t addr = QEMU_DTB_BASE;
struct fdt_header * fdt = (struct fdt_header*)addr;
char * dtb_strings = (char *)(addr + swizzle(fdt->off_dt_strings));
uint32_t * out = NULL;
@ -228,6 +231,7 @@ static struct BaseTables {
uintptr_t l0_base[512];
uintptr_t l1_high_gbs[512];
uintptr_t l1_low_gbs[512];
uintptr_t l2_kernel[512];
} _baseTables __attribute__((aligned(4096)));
#define PTE_VALID (1UL << 0)
@ -267,7 +271,7 @@ static void bootstub_mmu_init(void) {
_baseTables.l0_base[511] = (uintptr_t)&_baseTables.l1_high_gbs | PTE_VALID | PTE_TABLE | PTE_AF;
/* Mapping for us */
_baseTables.l1_low_gbs[1] = 0x40000000 | PTE_VALID | PTE_AF | PTE_SH_A | (1 << 2);
_baseTables.l1_low_gbs[1] = QEMU_DTB_BASE | PTE_VALID | PTE_AF | PTE_SH_A | (1 << 2);
/* -512GB is a map of 64GB of memory */
for (size_t i = 0; i < 64; ++i) {
@ -275,7 +279,12 @@ static void bootstub_mmu_init(void) {
}
/* -2GiB, map kernel here */
_baseTables.l1_high_gbs[510] = 0x80000000 | PTE_VALID | PTE_AF | PTE_SH_A | (1 << 2);
_baseTables.l1_high_gbs[510] = (uintptr_t)&_baseTables.l2_kernel | PTE_VALID | PTE_TABLE | PTE_AF;
for (size_t i = 0; i < 512; ++i) {
_baseTables.l2_kernel[i] = (KERNEL_PHYS_BASE + (i << 21)) | PTE_VALID | PTE_AF | PTE_SH_A | (1 << 2);
}
uint64_t sctlr = 0
| (1UL << 0) /* mmu enabled */
@ -428,8 +437,8 @@ static void bootstub_start_kernel(Elf64_Header * header) {
printf("bootstub: Jump to kernel entry point at %zx\n",
header->e_entry);
void (*entry)(void) = (void(*)(void))header->e_entry;
entry();
void (*entry)(uintptr_t,uintptr_t) = (void(*)(uintptr_t,uintptr_t))header->e_entry;
entry(QEMU_DTB_BASE, KERNEL_PHYS_BASE);
}
int kmain(void) {

View File

@ -15,6 +15,8 @@
#include <kernel/arch/aarch64/dtb.h>
uintptr_t aarch64_dtb_phys = 0;
static uint32_t swizzle(uint32_t from) {
uint8_t a = from >> 24;
uint8_t b = from >> 16;
@ -137,7 +139,8 @@ void dtb_callback_direct_children(uint32_t * node, void (*callback)(uint32_t * c
static uint32_t * find_node_int(const char * name, int (*cmp)(const char*,const char*)) {
uintptr_t addr = (uintptr_t)mmu_map_from_physical(0x40000000);
uintptr_t addr = (uintptr_t)mmu_map_from_physical(aarch64_dtb_phys);
dprintf("dtb: find '%s' from %#zx\n", name, addr);
struct fdt_header * fdt = (struct fdt_header*)addr;
char * dtb_strings = (char *)(addr + swizzle(fdt->off_dt_strings));
uint32_t * dtb_struct = (uint32_t *)(addr + swizzle(fdt->off_dt_struct));
@ -187,7 +190,7 @@ static uint32_t * node_find_property_int(uint32_t * node, char * strings, const
}
uint32_t * dtb_node_find_property(uint32_t * node, const char * property) {
uintptr_t addr = (uintptr_t)mmu_map_from_physical(0x40000000);
uintptr_t addr = (uintptr_t)mmu_map_from_physical(aarch64_dtb_phys);
struct fdt_header * fdt = (struct fdt_header*)addr;
char * dtb_strings = (char *)(addr + swizzle(fdt->off_dt_strings));
uint32_t * out = NULL;
@ -199,7 +202,7 @@ uint32_t * dtb_node_find_property(uint32_t * node, const char * property) {
* Figure out 1) how much actual memory we have and 2) what the last
* address of physical memory is.
*/
void dtb_memory_size(size_t * memsize, size_t * physsize) {
void dtb_memory_size(size_t * memaddr, size_t * physsize) {
uint32_t * memory = dtb_find_node_prefix("memory");
if (!memory) {
printf("dtb: Could not find memory node.\n");
@ -218,7 +221,7 @@ void dtb_memory_size(size_t * memsize, size_t * physsize) {
uint64_t mem_addr = (uint64_t)swizzle(regs[3]) | ((uint64_t)swizzle(regs[2]) << 32UL);
uint64_t mem_size = (uint64_t)swizzle(regs[5]) | ((uint64_t)swizzle(regs[4]) << 32UL);
*memsize = mem_size;
*physsize = mem_addr + mem_size;
*memaddr = mem_addr;
*physsize = mem_size;
}

View File

@ -54,7 +54,7 @@ void fwcfg_load_initrd(uintptr_t * ramdisk_phys_base, size_t * ramdisk_size) {
size_t uz_pages = 0;
extern char end[];
uintptr_t ramdisk_map_start = ((uintptr_t)&end - 0xffffffff80000000UL) + 0x80000000;
uintptr_t ramdisk_map_start = mmu_map_to_physical(NULL, (uintptr_t)&end);
/* See if we can find a qemu fw_cfg interface, we can use that for a ramdisk */
uint32_t * fw_cfg = dtb_find_node_prefix("fw-cfg");

View File

@ -442,9 +442,17 @@ void aarch64_processor_data(void) {
* at -2GiB, and there's some other mappings so that
* a bit of RAM is 1:1.
*/
int kmain(void) {
int kmain(uintptr_t dtb_base, uintptr_t phys_base) {
early_log_initialize();
console_set_output(_early_log_write);
extern uintptr_t aarch64_kernel_phys_base;
aarch64_kernel_phys_base = phys_base;
extern uintptr_t aarch64_dtb_phys;
aarch64_dtb_phys = dtb_base;
dprintf("%s %d.%d.%d-%s %s %s\n",
__kernel_name,
__kernel_version_major,
@ -454,6 +462,9 @@ int kmain(void) {
__kernel_version_codename,
__kernel_arch);
dprintf("boot: dtb @ %#zx kernel @ %#zx\n",
dtb_base, phys_base);
/* Initialize TPIDR_EL1 */
arch_set_core_base((uintptr_t)&processor_local_data[0]);
@ -470,12 +481,12 @@ int kmain(void) {
/* Probe DTB for memory layout. */
extern char end[];
size_t memsize, physsize;
dtb_memory_size(&memsize, &physsize);
size_t memaddr, memsize;
dtb_memory_size(&memaddr, &memsize);
/* Initialize the MMU based on the memory we got from dtb */
mmu_init(
memsize, physsize,
memaddr, memsize,
0x40100000 /* Should be end of DTB, but we're really just guessing */,
(uintptr_t)&end + ramdisk_size - 0xffffffff80000000UL);

View File

@ -20,6 +20,9 @@ static volatile uint32_t *frames;
static size_t nframes;
static size_t total_memory = 0;
static size_t unavailable_memory = 0;
static uint64_t ram_starts_at = 0;
uintptr_t aarch64_kernel_phys_base = 0;
/* TODO Used for CoW later. */
//static uint8_t * mem_refcounts = NULL;
@ -46,8 +49,6 @@ static size_t unavailable_memory = 0;
#define INDEX_FROM_BIT(b) ((b) >> 5)
#define OFFSET_FROM_BIT(b) ((b) & 0x1F)
#define QEMU_VIRT_KERNEL_BASE 0x80000000UL
#define _pagemap __attribute__((aligned(PAGE_SIZE))) = {0}
union PML init_page_region[512] _pagemap;
union PML high_base_pml[512] _pagemap;
@ -87,6 +88,8 @@ union PML kbase_pmls[65][512] _pagemap;
void mmu_frame_set(uintptr_t frame_addr) {
if (frame_addr < ram_starts_at) return;
frame_addr -= ram_starts_at;
if (frame_addr < nframes * PAGE_SIZE) {
uint64_t frame = frame_addr >> 12;
uint64_t index = INDEX_FROM_BIT(frame);
@ -99,6 +102,8 @@ void mmu_frame_set(uintptr_t frame_addr) {
static uintptr_t lowest_available = 0;
void mmu_frame_clear(uintptr_t frame_addr) {
if (frame_addr < ram_starts_at) return;
frame_addr -= ram_starts_at;
if (frame_addr < nframes * PAGE_SIZE) {
uint64_t frame = frame_addr >> PAGE_SHIFT;
uint64_t index = INDEX_FROM_BIT(frame);
@ -110,6 +115,8 @@ void mmu_frame_clear(uintptr_t frame_addr) {
}
int mmu_frame_test(uintptr_t frame_addr) {
if (frame_addr < ram_starts_at) return 1;
frame_addr -= ram_starts_at;
if (!(frame_addr < nframes * PAGE_SIZE)) return 1;
uint64_t frame = frame_addr >> PAGE_SHIFT;
uint64_t index = INDEX_FROM_BIT(frame);
@ -132,7 +139,7 @@ uintptr_t mmu_first_n_frames(int n) {
}
}
if (!bad) {
return i / PAGE_SIZE;
return (i + ram_starts_at) / PAGE_SIZE;
}
}
@ -152,7 +159,7 @@ uintptr_t mmu_first_frame(void) {
if (!(frames[i] & testFrame)) {
uintptr_t out = (i << 5) + j;
lowest_available = out + 1;
return out;
return out + (ram_starts_at >> 12);
}
}
}
@ -266,7 +273,7 @@ union PML * mmu_get_page_other(union PML * root, uintptr_t virtAddr) {
uintptr_t mmu_map_to_physical(union PML * root, uintptr_t virtAddr) {
if (!root) {
if (virtAddr >= MODULE_BASE_START) {
return (virtAddr - MODULE_BASE_START) + QEMU_VIRT_KERNEL_BASE;
return (virtAddr - MODULE_BASE_START) + aarch64_kernel_phys_base;
} else if (virtAddr >= HIGH_MAP_REGION) {
return (virtAddr - HIGH_MAP_REGION);
}
@ -612,7 +619,7 @@ void mmu_free(union PML * from) {
}
union PML * mmu_get_kernel_directory(void) {
return mmu_map_from_physical((uintptr_t)&init_page_region - MODULE_BASE_START + QEMU_VIRT_KERNEL_BASE);
return mmu_map_from_physical((uintptr_t)&init_page_region - MODULE_BASE_START + aarch64_kernel_phys_base);
}
void mmu_set_directory(union PML * new_pml) {
@ -733,50 +740,23 @@ int mmu_validate_user_pointer(void * addr, size_t size, int flags) {
}
static uintptr_t k2p(void * x) {
return ((uintptr_t)x - MODULE_BASE_START) + QEMU_VIRT_KERNEL_BASE;
return ((uintptr_t)x - MODULE_BASE_START) + aarch64_kernel_phys_base;
}
void mmu_init(uintptr_t memsize, size_t physsize, uintptr_t firstFreePage, uintptr_t endOfRamDisk) {
void mmu_init(uintptr_t memaddr, size_t memsize, uintptr_t firstFreePage, uintptr_t endOfRamDisk) {
this_core->current_pml = (union PML *)&init_page_region;
/* On this machine, there's 1GiB of unavailable memory. */
unavailable_memory = 1048576;
/* Convert from bytes to kibibytes */
total_memory = memsize / 1024;
/* MAIR setup? */
uint64_t mair;
asm volatile ("mrs %0,MAIR_EL1" : "=r"(mair));
dprintf("Current MAIR:\n"
" Attr0: 0x%02zx Attr1: 0x%02zx\n"
" Attr2: 0x%02zx Attr3: 0x%02zx\n"
" Attr4: 0x%02zx Attr5: 0x%02zx\n"
" Attr6: 0x%02zx Attr7: 0x%02zx\n",
((mair >> 0) & 0xFF),
((mair >> 8) & 0xFF),
((mair >> 16) & 0xFF),
((mair >> 24) & 0xFF),
((mair >> 32) & 0xFF),
((mair >> 40) & 0xFF),
((mair >> 48) & 0xFF),
((mair >> 52) & 0xFF));
/* We don't currently support gaps in this setup. */
unavailable_memory = 0;
//mair &= (0xFFffFFffFF000000);
mair = (0x000000000044ff00);
/* MAIR setup? */
uint64_t mair = (0x000000000044ff00);
asm volatile ("msr MAIR_EL1,%0" :: "r"(mair));
asm volatile ("mrs %0,MAIR_EL1" : "=r"(mair));
dprintf("Loaded MAIR:\n"
" Attr0: 0x%02zx Attr1: 0x%02zx\n"
" Attr2: 0x%02zx Attr3: 0x%02zx\n"
" Attr4: 0x%02zx Attr5: 0x%02zx\n"
" Attr6: 0x%02zx Attr7: 0x%02zx\n",
((mair >> 0) & 0xFF),
((mair >> 8) & 0xFF),
((mair >> 16) & 0xFF),
((mair >> 24) & 0xFF),
((mair >> 32) & 0xFF),
((mair >> 40) & 0xFF),
((mair >> 48) & 0xFF),
((mair >> 52) & 0xFF));
dprintf("mmu: MAIR_EL1=0x%016lx\n", mair);
asm volatile ("" ::: "memory");
@ -800,7 +780,7 @@ void mmu_init(uintptr_t memsize, size_t physsize, uintptr_t firstFreePage, uintp
for (size_t j = 0; j < twoms; ++j) {
kbase_pmls[0][j].raw = k2p(&kbase_pmls[1+j]) | PTE_VALID | PTE_TABLE | PTE_AF;
for (int i = 0; i < 512; ++i) {
kbase_pmls[1+j][i].raw = (uintptr_t)(QEMU_VIRT_KERNEL_BASE + LARGE_PAGE_SIZE * j + PAGE_SIZE * i) |
kbase_pmls[1+j][i].raw = (uintptr_t)(aarch64_kernel_phys_base + LARGE_PAGE_SIZE * j + PAGE_SIZE * i) |
PTE_VALID | PTE_AF | PTE_SH_A | PTE_TABLE | (1 << 2);
}
}
@ -818,7 +798,8 @@ void mmu_init(uintptr_t memsize, size_t physsize, uintptr_t firstFreePage, uintp
/* Physical frame allocator. We're gonna do this the same as the one we have x86-64, because
* I can't be bothered to think of anything better right now... */
nframes = (physsize) >> 12;
ram_starts_at = memaddr;
nframes = (memsize) >> 12;
size_t bytesOfFrames = INDEX_FROM_BIT(nframes * 8);
bytesOfFrames = (bytesOfFrames + PAGE_LOW_MASK) & PAGE_SIZE_MASK;
@ -837,18 +818,18 @@ void mmu_init(uintptr_t memsize, size_t physsize, uintptr_t firstFreePage, uintp
frames = (void*)((uintptr_t)KERNEL_HEAP_START);
memset((void*)frames, 0x00, bytesOfFrames);
/* Set frames as in use... this also marks all of the lower gigabyte, conveniently... */
for (uintptr_t i = 0; i < firstFreePage + bytesOfFrames; i+= PAGE_SIZE) {
/* Set frames as in use... */
for (uintptr_t i = memaddr; i < firstFreePage + bytesOfFrames; i+= PAGE_SIZE) {
mmu_frame_set(i);
}
/* Set kernel space as in use */
for (uintptr_t i = 0; i < twoms * LARGE_PAGE_SIZE; i += PAGE_SIZE) {
mmu_frame_set(QEMU_VIRT_KERNEL_BASE + i);
mmu_frame_set(aarch64_kernel_phys_base + i);
}
heapStart = (char*)KERNEL_HEAP_START + bytesOfFrames;
lowest_available = (firstFreePage + bytesOfFrames);
lowest_available = (firstFreePage + bytesOfFrames) - memaddr;
}

View File

@ -184,7 +184,9 @@ void aarch64_smp_start(void) {
asm volatile ("mrs %0, VBAR_EL1" : "=r"(aarch64_vbar));
startup_ttbr0[0][0].raw = mmu_map_to_physical(NULL, (uintptr_t)&startup_ttbr0[1]) | (0x3) | (1 << 10);
startup_ttbr0[1][2].raw = 0x80000000 | (1 << 2) | 1 | (1 << 10);
for (long i = 0; i < 512; ++i) {
startup_ttbr0[1][i].raw = (i << 30) | (1 << 2) | 1 | (1 << 10);
}
aarch64_ttbr0 = mmu_map_to_physical(NULL, (uintptr_t)&startup_ttbr0[0]);
aarch64_ttbr1 = mmu_map_to_physical(NULL, (uintptr_t)mmu_get_kernel_directory());

View File

@ -31,6 +31,8 @@ static uintptr_t pcie_addr(uint32_t device, int field) {
return (pci_extract_bus(device) << 20) | (pci_extract_slot(device) << 15) | (pci_extract_func(device) << 12) | (field);
}
uintptr_t pcie_ecam_phys = 0x3f000000;
/**
* @brief Write to a PCI device configuration space field.
*/
@ -42,13 +44,13 @@ void pci_write_field(uint32_t device, int field, int size, uint32_t value) {
/* ECAM space */
if (size == 4) {
*(volatile uint32_t*)mmu_map_from_physical(0x3f000000 + pcie_addr(device,field)) = value;
*(volatile uint32_t*)mmu_map_from_physical(pcie_ecam_phys + pcie_addr(device,field)) = value;
return;
} else if (size == 2) {
*(volatile uint16_t*)mmu_map_from_physical(0x3f000000 + pcie_addr(device,field)) = value;
*(volatile uint16_t*)mmu_map_from_physical(pcie_ecam_phys + pcie_addr(device,field)) = value;
return;
} else if (size == 1) {
*(volatile uint8_t*)mmu_map_from_physical(0x3f000000 + pcie_addr(device,field)) = value;
*(volatile uint8_t*)mmu_map_from_physical(pcie_ecam_phys + pcie_addr(device,field)) = value;
return;
}
@ -77,11 +79,11 @@ uint32_t pci_read_field(uint32_t device, int field, int size) {
#else
uintptr_t field_addr = pcie_addr(device,field);
if (size == 4) {
return *(volatile uint32_t*)mmu_map_from_physical(0x3f000000 + field_addr);
return *(volatile uint32_t*)mmu_map_from_physical(pcie_ecam_phys + field_addr);
} else if (size == 2) {
return *(volatile uint16_t*)mmu_map_from_physical(0x3f000000 + field_addr);
return *(volatile uint16_t*)mmu_map_from_physical(pcie_ecam_phys + field_addr);
} else if (size == 1) {
return *(volatile uint8_t*)mmu_map_from_physical(0x3f000000 + field_addr);
return *(volatile uint8_t*)mmu_map_from_physical(pcie_ecam_phys + field_addr);
}
#endif
return 0xFFFF;