aarch64: be more flexible about where the kernel is physically
This commit is contained in:
parent
097662ef3d
commit
e1e66bf6cd
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user