Created
April 13, 2014 21:34
-
-
Save winocm/10603326 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* asdfghjkl;' | |
*/ | |
#include <mach/machine/vm_types.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#pragma mark - MMU TLB lookup functionality | |
#define TB_BUCKET_NUM 32 | |
#define TB_BUCKET_SIZE 8 | |
#define tb_get_shadow_pa(tb_ent, va) ((tb_ent) ^ (va)) | |
#define tb_get_shadow_va(tb_ent, pa) ((tb_ent) ^ (pa)) | |
#define tb_set_shadow(tb_ent, va, pa) ((tb_ent) = (va) ^ (pa)) | |
/* | |
* The TLB entries stores in each 'tb_entry_t' contain a shadowed | |
* mapping. Effectively, the VA and PA is xored together to form one | |
* unified member in the array. See tb_get_shadow_va and tb_get_shadow_pa | |
* for how the address entries are retrieved. | |
* | |
* Did you know? The Java version of *this exact same code* is 20 times | |
* slower than its C counterpart! That's your fun fact for the day. | |
*/ | |
typedef struct __tb_entry { | |
uint32_t va_key; | |
uint32_t shadow; | |
uint32_t granularity; | |
uint32_t domain:4; | |
uint32_t ap:2; | |
} tb_entry_t; | |
typedef struct __tb_context { | |
uint8_t tlb_writepos[TB_BUCKET_NUM]; | |
uint8_t tlb_readpos[TB_BUCKET_NUM]; | |
tb_entry_t tlb_hashtbl[TB_BUCKET_NUM][TB_BUCKET_SIZE]; | |
} tb_context_t; | |
tb_context_t tb_context = { }; | |
void tb_insert(uint32_t va, uint32_t pa, uint32_t ap, uint32_t domain, | |
uint32_t granularity); | |
tb_entry_t *tb_probe(uint32_t va); | |
void tb_flush(void); | |
static inline uint32_t tb_get_pg_hash(uint32_t va, uint32_t pgshift, | |
uint32_t nmemb) | |
{ | |
uint32_t addr_hash = (va >> pgshift); | |
return (addr_hash ^ (addr_hash >> (pgshift >> 1)) ^ (addr_hash >> pgshift)) | |
% nmemb; | |
} | |
void tb_insert(uint32_t va, uint32_t pa, uint32_t ap, uint32_t domain, | |
uint32_t granularity) | |
{ | |
uint32_t bucket = tb_get_pg_hash(va, PAGE_SHIFT, TB_BUCKET_NUM); | |
tb_set_shadow(tb_context. | |
tlb_hashtbl[bucket][tb_context.tlb_writepos[bucket]].shadow, | |
va, pa); | |
tb_context.tlb_hashtbl[bucket][tb_context.tlb_writepos[bucket]].va_key = va; | |
tb_context.tlb_hashtbl[bucket][tb_context.tlb_writepos[bucket]].ap = ap; | |
tb_context.tlb_hashtbl[bucket][tb_context.tlb_writepos[bucket]].domain = | |
domain; | |
tb_context.tlb_hashtbl[bucket][tb_context.tlb_writepos[bucket]]. | |
granularity = granularity; | |
tb_context.tlb_readpos[bucket] = tb_context.tlb_writepos[bucket]; | |
if (++tb_context.tlb_writepos[bucket] == TB_BUCKET_SIZE) | |
tb_context.tlb_writepos[bucket] = 0; | |
#if DEBUG | |
printf | |
("tb_insert: 0x%08x -> 0x%08x, hash bucket %d, populated %d (%x,%x)\n", | |
va, pa, bucket, tb_context.tlb_writepos[bucket], ap, domain); | |
#endif | |
} | |
tb_entry_t *tb_probe(uint32_t va) | |
{ | |
uint32_t bucket = tb_get_pg_hash(va, PAGE_SHIFT, TB_BUCKET_NUM); | |
uint32_t i, j; | |
for (j = 0, i = tb_context.tlb_readpos[bucket]; j < TB_BUCKET_SIZE; | |
j++, i--) { | |
if (i == -1) | |
i = TB_BUCKET_SIZE - 1; | |
if ((tb_context.tlb_hashtbl[bucket][i].va_key <= va) | |
&& | |
((tb_context.tlb_hashtbl[bucket][i].va_key + | |
tb_context.tlb_hashtbl[bucket][i].granularity) > va)) { | |
#if DEBUG | |
uint32_t shadow = tb_context.tlb_hashtbl[bucket][i].shadow; | |
uint32_t pa = tb_get_shadow_pa(shadow, va); | |
tb_context.tlb_readpos[bucket] = i; | |
printf | |
("Victim found in TLB: Shadow 0x%08x, VA 0x%08x, PA 0x%08x, AP 0x%08x, Domain 0x%08x Size 0x%08x\n", | |
shadow, va, pa, tb_context.tlb_hashtbl[bucket][i].ap, | |
tb_context.tlb_hashtbl[bucket][i].domain, | |
tb_context.tlb_hashtbl[bucket][i].granularity); | |
#endif | |
return &tb_context.tlb_hashtbl[bucket][i]; | |
} | |
} | |
return (tb_entry_t *) NULL; | |
} | |
void tb_flush(void) | |
{ | |
uint32_t j; | |
bzero((void *) &tb_context.tlb_hashtbl, | |
sizeof(tb_entry_t) * TB_BUCKET_NUM * TB_BUCKET_SIZE); | |
for (j = 0; j < TB_BUCKET_NUM; j++) { | |
tb_context.tlb_readpos[j] = 0; | |
tb_context.tlb_writepos[j] = 0; | |
} | |
} | |
#pragma mark - Memory access routines | |
/* | |
* Each memory 'region' has its own Read and Write functions, much | |
* like any other emulator would. For regions that do not have a valid | |
* 'area', reads and writes can either be set to RAZ/WI, ... or we could | |
* raise a 'bus error' like condition and assert an external precise | |
* abort. Whatever. | |
* | |
* Note: | |
* The read function reads into an allocated buffer, write writes into | |
* an allocated buffer. | |
*/ | |
#define MEMREGION_NUM 32 | |
#define MEMORY_REGION_SUCCESS 0 | |
#define MEMORY_REGION_NO_SUCH_DEVICE 1 | |
#define MEMORY_REGION_WRITEFN_FAILED 2 | |
#define MEMORY_REGION_READFN_FAILED 3 | |
#define MEMORY_REGION_ABORTED 4 | |
typedef uint32_t memory_error_t; | |
typedef memory_error_t(*memory_write_fn_t) (uint32_t offset, void *read, | |
uint32_t size); | |
typedef memory_error_t(*memory_read_fn_t) (uint32_t offset, void *write, | |
uint32_t size); | |
typedef struct __memory_region { | |
uint32_t phys; | |
uint32_t size; | |
memory_write_fn_t write; | |
memory_read_fn_t read; | |
} memory_region_t; | |
typedef struct __memory_context { | |
memory_region_t region[MEMREGION_NUM]; | |
int reftrack; | |
} memory_context_t; | |
memory_context_t memory_context = { }; | |
void memory_region_register(uint32_t phys, uint32_t size, | |
memory_read_fn_t readfn, memory_write_fn_t writefn); | |
memory_error_t memory_read(uint32_t phys, void *readbuf, uint32_t size); | |
memory_error_t memory_write(uint32_t phys, void *readbuf, uint32_t size); | |
#define memory_write_guts(off, val, type) \ | |
do { \ | |
type __v = val; \ | |
memory_write(off, (void*)&__v, sizeof(type)); \ | |
} while(0); | |
#define memory_write_uint64(off, val) memory_write_guts(off, val, uint64_t) | |
#define memory_write_uint32(off, val) memory_write_guts(off, val, uint32_t) | |
#define memory_write_uint16(off, val) memory_write_guts(off, val, uint16_t) | |
#define memory_write_uint8(off, val) memory_write_guts(off, val, uint8_t) | |
#define memory_read_inline_guts(name, type) \ | |
type memory_read_ ##name (uint32_t off) { \ | |
type __v; \ | |
memory_read(off, (void*)&__v, sizeof(type)); \ | |
return __v; \ | |
} | |
uint64_t memory_read_uint64(uint32_t off); | |
uint32_t memory_read_uint32(uint32_t off); | |
uint16_t memory_read_uint16(uint32_t off); | |
uint8_t memory_read_uint8(uint32_t off); | |
memory_read_inline_guts(uint64, uint64_t); | |
memory_read_inline_guts(uint32, uint32_t); | |
memory_read_inline_guts(uint16, uint16_t); | |
memory_read_inline_guts(uint8, uint8_t); | |
void memory_region_register(uint32_t phys, uint32_t size, | |
memory_read_fn_t readfn, memory_write_fn_t writefn) | |
{ | |
memory_context.region[memory_context.reftrack].phys = phys; | |
memory_context.region[memory_context.reftrack].size = size; | |
memory_context.region[memory_context.reftrack].read = readfn; | |
memory_context.region[memory_context.reftrack].write = writefn; | |
memory_context.reftrack++; | |
#if DEBUG | |
printf("%s: registered physical region 0x%08x->0x%08x, size %d\n", | |
__FUNCTION__, phys, phys + size, size); | |
#endif | |
} | |
memory_error_t memory_read(uint32_t phys, void *readbuf, uint32_t size) | |
{ | |
int i; | |
for (i = 0; i < memory_context.reftrack; i++) { | |
if ((memory_context.region[i].phys <= phys) | |
&& (phys < | |
(memory_context.region[i].phys + | |
memory_context.region[i].size))) { | |
#if DEBUG | |
printf("%s: Memory READ of PA 0x%08x, region %d, size %d\n", | |
__FUNCTION__, phys, i, size); | |
#endif | |
return memory_context.region[i].read(phys, readbuf, size); | |
} | |
} | |
return MEMORY_REGION_NO_SUCH_DEVICE; | |
} | |
memory_error_t memory_write(uint32_t phys, void *readbuf, uint32_t size) | |
{ | |
int i; | |
for (i = 0; i < memory_context.reftrack; i++) { | |
if ((memory_context.region[i].phys <= phys) | |
&& (phys < | |
(memory_context.region[i].phys + | |
memory_context.region[i].size))) { | |
#if DEBUG | |
printf("%s: Memory WRITE of PA 0x%08x, region %d, size %d\n", | |
__FUNCTION__, phys, i, size); | |
#endif | |
return memory_context.region[i].write(phys, readbuf, size); | |
} | |
} | |
return MEMORY_REGION_NO_SUCH_DEVICE; | |
} | |
#pragma mark - Address translation routines | |
/* | |
* Address Translation | |
* | |
* Address translation is relatively simple on ARM platforms. The first | |
* level descriptor is retrieved from the TTB base, then we check the last | |
* bits to see if we need to fetch a L2 descriptor. If we need to, we should | |
* and then we consider the translation successful if said descriptor | |
* is not a fault one. | |
* | |
* Successful translations are stored in our Translation Lookaside Buffer | |
* (TLB). See the tb_xxx routines above. | |
*/ | |
static uint32_t ttbr_base = 0; | |
static uint32_t mmu_enabled = 1; | |
static uint32_t mmu_dacr = 0x01; /* temp */ | |
uint32_t mmu_translate_address(uint32_t mva, uint32_t priv, uint32_t w, | |
uint32_t * pa, uint32_t * fault); | |
void mmu_set_ttb(uint32_t ttbr); | |
void mmu_set_enabled(void); | |
void mmu_set_disabled(void); | |
void mmu_toggle(void); | |
#define mmu_tlb_flush tb_flush | |
#define L1_TTE_SECT_SHIFT 20 | |
#define L1_TTE_SECT_MASK 0xfff00000 | |
#define L1_TTE_SECT_SIZE (1 << L1_TTE_SECT_SHIFT) | |
#define L1_TTE_OFFSET(addr) (((addr & L1_TTE_SECT_MASK) >> L1_TTE_SECT_SHIFT) << 2) | |
#define L1_TTE_COARSE_MASK 0xfffffc00; | |
#define L1_TTB_DESCRIPTOR_TYPE_FAULT 0 | |
#define L1_TTB_DESCRIPTOR_TYPE_PAGE 1 | |
#define L1_TTB_DESCRIPTOR_TYPE_SECTION 2 | |
#define L1_TTB_DESCRIPTOR_MASK 3 | |
#define L2_PTE_OFFSET(addr) ((addr & 0xff000) >> 10) | |
#define L2_PTE_DESCRIPTOR_FAULT 0 | |
#define L2_PTE_DESCRIPTOR_64K 1 | |
#define L2_PTE_DESCRIPTOR_4K 2 | |
#define L2_PTE_DESCRIPTOR_1K 3 | |
#define L2_PTE_DESCRIPTOR_MASK 3 | |
#define L2_PTE_64K_MASK 0xffff0000 | |
#define L2_PTE_4K_MASK 0xfffff000 | |
#define L2_PTE_4K 4096 | |
#define L2_PTE_64K 65536 | |
#define FSR_ALIGNMENT 1 | |
#define FSR_DEBUG 2 | |
#define FSR_ACCESS_SECT 3 | |
#define FSR_CACHE 4 | |
#define FSR_TRANS_SECT 5 | |
#define FSR_ACCESS_PAGE 6 | |
#define FSR_TRANS_PAGE 7 | |
#define FSR_EXTABT 8 | |
#define FSR_DOMAIN 9 | |
#define FSR_RESV 10 | |
#define FSR_DOMAIN_PAGE 11 | |
#define FSR_EXT_L1 12 | |
#define FSR_PERM_SECT 13 | |
#define FSR_EXT_L2 14 | |
#define FSR_PERM_PAGE 15 | |
#define DACR_NO_ACCESS 0 | |
#define DACR_CLIENT 1 | |
#define DACR_RESERVED 2 | |
#define DACR_MANAGER 3 | |
uint32_t mmu_translate_address(uint32_t mva, uint32_t priv, uint32_t w, | |
uint32_t * pa, uint32_t * fault) | |
{ | |
uint32_t pa_offset, va_offset, l1_desc, l2_desc, l2_desc_addr, domain, | |
ap_bits; | |
int granularity, is_sect = 0; | |
memory_error_t err; | |
tb_entry_t *tlb_ent; | |
if (!mmu_enabled) { | |
pa_offset = va_offset = 0; | |
goto finalize; | |
} | |
/* | |
* Check the TLB for any address translations. | |
*/ | |
if ((tlb_ent = tb_probe(mva)) != (tb_entry_t *) NULL) { | |
va_offset = mva; | |
pa_offset = tb_get_shadow_pa(tlb_ent->shadow, mva); | |
ap_bits = tlb_ent->ap; | |
domain = tlb_ent->domain; | |
goto ap_verify; | |
} | |
/* | |
* Verify the table is word aligned. | |
*/ | |
if (ttbr_base & 3) { | |
*fault = FSR_ALIGNMENT | (w << 11);; | |
return 1; | |
} | |
err = | |
memory_read(L1_TTE_OFFSET(mva) + ttbr_base, &l1_desc, sizeof(uint32_t)); | |
if (err) { | |
#if DEBUG | |
printf("%s: external abort on L1 descriptor fetch!\n", __FUNCTION__); | |
#endif | |
*fault = FSR_EXT_L1 | (w << 11); | |
return 1; | |
} | |
#if DEBUG | |
printf("mmu_translate_address: l1_desc: 0x%08x\n", l1_desc); | |
#endif | |
/* | |
* Check memory domain against DACR. | |
*/ | |
domain = (l1_desc >> 5) & 0xF; | |
/* | |
* Cap the write bit to 1. | |
*/ | |
w &= 1; | |
/* | |
* Check the descriptor type. | |
*/ | |
switch (l1_desc & L1_TTB_DESCRIPTOR_MASK) { | |
case L1_TTB_DESCRIPTOR_TYPE_FAULT: | |
*fault = FSR_TRANS_SECT | (domain << 4) | (w << 11); | |
return 1; | |
case L1_TTB_DESCRIPTOR_TYPE_SECTION: | |
pa_offset = l1_desc & L1_TTE_SECT_MASK; | |
va_offset = mva & L1_TTE_SECT_MASK; | |
ap_bits = (l1_desc >> 10) & 3; | |
granularity = L1_TTE_SECT_SIZE; | |
is_sect = 1; | |
goto insert_tlb; | |
case L1_TTB_DESCRIPTOR_TYPE_PAGE: | |
is_sect = 0; | |
l2_desc_addr = l1_desc & L1_TTE_COARSE_MASK + L2_PTE_OFFSET(mva); | |
break; | |
default: | |
printf("???? type %d\n", l1_desc & L1_TTB_DESCRIPTOR_MASK); | |
*fault = FSR_EXT_L1 | (w << 11); | |
return 1; | |
} | |
/* | |
* Get second level table. | |
*/ | |
printf("%s: l2_desc_addr: 0x%08x\n", __FUNCTION__, l2_desc_addr); | |
err = memory_read(l2_desc_addr, &l2_desc, sizeof(uint32_t)); | |
if (err) { | |
#if DEBUG | |
printf("%s: external abort on L2 descriptor fetch!\n", __FUNCTION__); | |
#endif | |
*fault = FSR_EXT_L2 | (w << 11); | |
return 1; | |
} | |
/* | |
* Look at the descriptor bits | |
*/ | |
switch (l2_desc & L2_PTE_DESCRIPTOR_MASK) { | |
case L2_PTE_DESCRIPTOR_FAULT: | |
*fault = FSR_TRANS_PAGE | (domain << 4) | (w << 11); | |
return 1; | |
case L2_PTE_DESCRIPTOR_64K: | |
pa_offset = l2_desc & L2_PTE_64K_MASK; | |
va_offset = mva & L2_PTE_64K_MASK; | |
ap_bits = (l1_desc >> 14) & 3; | |
granularity = L2_PTE_64K; | |
is_sect = 0; | |
goto insert_tlb; | |
case L2_PTE_DESCRIPTOR_4K: | |
pa_offset = l2_desc & L2_PTE_4K_MASK; | |
va_offset = mva & L2_PTE_4K_MASK; | |
ap_bits = (l1_desc >> 10) & 3; | |
granularity = L2_PTE_4K; | |
is_sect = 0; | |
goto insert_tlb; | |
default: | |
printf("???? %d\n", l2_desc & L2_PTE_DESCRIPTOR_FAULT); | |
*fault = FSR_EXT_L2 | (w << 11); | |
return 1; | |
} | |
insert_tlb: | |
tb_insert(va_offset, pa_offset, ap_bits, domain, granularity); | |
ap_verify: | |
/* | |
* Verify against DACR. | |
*/ | |
#if DEBUG | |
printf("%s: verifying against DACR ... current DACR 0x%08x\n", __FUNCTION__, | |
mmu_dacr); | |
#endif | |
switch (mmu_dacr >> (domain * 2) & 3) { | |
case DACR_NO_ACCESS: | |
case DACR_RESERVED: | |
*fault = | |
(is_sect ? FSR_DOMAIN : FSR_DOMAIN_PAGE) | (domain << 4) | (w << | |
11); | |
return 1; | |
case DACR_CLIENT: | |
break; | |
case DACR_MANAGER: | |
goto finalize; | |
} | |
/* | |
* Verify AP bits. (kinda broken.. no S/R-bit in SCTLR to respect!) | |
*/ | |
#if DEBUG | |
printf("%s: verifying AP bits... bits %x\n", __FUNCTION__, ap_bits); | |
#endif | |
switch (ap_bits) { | |
case 0: | |
/* | |
* Read Only | |
*/ | |
if (w) { | |
break; | |
} | |
goto finalize; | |
case 1: | |
/* | |
* Privileged Read + Write, User None | |
*/ | |
if (!priv) { | |
break; | |
} | |
goto finalize; | |
case 2: | |
/* | |
* Privileged Read + Write, User Read Only | |
*/ | |
if (!priv && w) { | |
break; | |
} | |
goto finalize; | |
case 3: | |
default: | |
/* | |
* Privileged Read + Write, User Read + Write | |
*/ | |
goto finalize; | |
} | |
*fault = | |
(is_sect ? FSR_PERM_SECT : FSR_PERM_PAGE) | (domain << 4) | (w << 11);; | |
return 1; | |
finalize: | |
*pa = mva - va_offset + pa_offset; | |
*fault = 0; | |
return 0; | |
} | |
void mmu_set_ttb(uint32_t ttbr) | |
{ | |
#if DEBUG | |
printf("%s: TTBR set to 0x%08x\n", __FUNCTION__, ttbr); | |
#endif | |
ttbr_base = ttbr; | |
} | |
void mmu_set_enabled(void) | |
{ | |
#if DEBUG | |
printf("%s: enabling MMU status (%d)\n", __FUNCTION__, mmu_enabled); | |
#endif | |
mmu_enabled = 1; | |
tb_flush(); | |
} | |
void mmu_set_disabled(void) | |
{ | |
#if DEBUG | |
printf("%s: disabling MMU status (%d)\n", __FUNCTION__, mmu_enabled); | |
#endif | |
mmu_enabled = 0; | |
} | |
void mmu_toggle(void) | |
{ | |
#if DEBUG | |
printf("%s: toggling MMU status (%d)\n", __FUNCTION__, mmu_enabled); | |
#endif | |
mmu_enabled ^= 1; | |
} | |
/* Implement privileged check, hook with CPSR state & 0x1F != 0x10. */ | |
memory_error_t mmu_memory_read(uint32_t va, void *readbuf, uint32_t size, | |
uint32_t * fsr) | |
{ | |
uint32_t translated_addr; | |
memory_error_t err; | |
if (!mmu_enabled) | |
return memory_read(va, readbuf, size); | |
err = mmu_translate_address(va, 1, 0, &translated_addr, fsr); | |
if (err) | |
return MEMORY_REGION_ABORTED; | |
#if DEBUG | |
printf("%s: VA 0x%08x => PA 0x%08x, read of size %d\n", __FUNCTION__, va, | |
translated_addr, size); | |
#endif | |
return memory_read(translated_addr, readbuf, size); | |
} | |
memory_error_t mmu_memory_write(uint32_t va, void *readbuf, uint32_t size, | |
uint32_t * fsr) | |
{ | |
uint32_t translated_addr; | |
memory_error_t err; | |
if (!mmu_enabled) | |
return memory_write(va, readbuf, size); | |
err = mmu_translate_address(va, 1, 1, &translated_addr, fsr); | |
if (err) | |
return MEMORY_REGION_ABORTED; | |
#if DEBUG | |
printf("%s: VA 0x%08x => PA 0x%08x, write of size %d\n", __FUNCTION__, va, | |
translated_addr, size); | |
#endif | |
return memory_write(translated_addr, readbuf, size); | |
} | |
/* THESE DO NOT IMPLEMENT ABORT CHECKING NEVER USE */ | |
#define mmu_memory_write_guts(off, val, type) \ | |
do { \ | |
type __v = val; \ | |
uint32_t fsr; \ | |
mmu_memory_write(off, (void*)&__v, sizeof(type), &fsr); \ | |
} while(0); | |
#define mmu_memory_write_uint64(off, val) mmu_memory_write_guts(off, val, uint64_t) | |
#define mmu_memory_write_uint32(off, val) mmu_memory_write_guts(off, val, uint32_t) | |
#define mmu_memory_write_uint16(off, val) mmu_memory_write_guts(off, val, uint16_t) | |
#define mmu_memory_write_uint8(off, val) mmu_memory_write_guts(off, val, uint8_t) | |
#define mmu_memory_read_inline_guts(name, type) \ | |
type mmu_memory_read_ ##name (uint32_t off) { \ | |
type __v; \ | |
uint32_t fsr; \ | |
mmu_memory_read(off, (void*)&__v, sizeof(type), &fsr); \ | |
return __v; \ | |
} | |
uint64_t mmu_memory_read_uint64(uint32_t off); | |
uint32_t mmu_memory_read_uint32(uint32_t off); | |
uint16_t mmu_memory_read_uint16(uint32_t off); | |
uint8_t mmu_memory_read_uint8(uint32_t off); | |
mmu_memory_read_inline_guts(uint64, uint64_t); | |
mmu_memory_read_inline_guts(uint32, uint32_t); | |
mmu_memory_read_inline_guts(uint16, uint16_t); | |
mmu_memory_read_inline_guts(uint8, uint8_t); | |
#pragma mark - RAM emulation | |
/* | |
* RAM is dynamically allocated of size 32MB. We set all of the addresses | |
* to 0xAA55AA55 patterns.. much like real memory. The boot loader is responsible | |
* for zeroing out and initializing memory. | |
* | |
* The memory region routines are useful for reading and writing to physical | |
* addresses. | |
*/ | |
static void *ram_buffer = NULL; | |
static int ram_size = 32 * 1024 * 1024; | |
memory_error_t ram_read(uint32_t offset, void *read, uint32_t size) | |
{ | |
if (offset > ram_size) | |
return MEMORY_REGION_READFN_FAILED; | |
bcopy((void *) ((uintptr_t) ram_buffer + offset), read, size); | |
return MEMORY_REGION_SUCCESS; | |
} | |
memory_error_t ram_write(uint32_t offset, void *read, uint32_t size) | |
{ | |
if (offset > ram_size) | |
return MEMORY_REGION_READFN_FAILED; | |
bcopy(read, (void *) ((uintptr_t) ram_buffer + offset), size); | |
return MEMORY_REGION_SUCCESS; | |
} | |
void ram_initialize(void) | |
{ | |
int i; | |
ram_buffer = malloc(ram_size); | |
if (!ram_buffer) { | |
printf("malloc fail\n"); | |
abort(); | |
} | |
/* | |
* On Darwin, we would use memset_pattern4, that doesn't exist on Win32. | |
*/ | |
for (i = 0; i < ram_size; i += 4) { | |
uint32_t *ram_buf = (uint32_t *) ((uintptr_t) ram_buffer + i); | |
*ram_buf = 0xAA55AA55; | |
} | |
memory_region_register(0, ram_size, ram_read, ram_write); | |
} | |
#if 1 | |
int main(void) | |
{ | |
#if 0 | |
uint32_t buf; | |
ram_initialize(); | |
memory_read(0x00000000, (void *) &buf, 4); | |
printf("buf = 0x%08x\n", buf); | |
buf = 0xdeadbeef; | |
memory_write(0x00000000, (void *) &buf, 4); | |
memory_read(0x00000000, (void *) &buf, 4); | |
printf("buf = 0x%08x\n", buf); | |
#endif | |
uint32_t addr = 0, fsr; | |
ram_initialize(); | |
memory_write_uint32(0x00100020, 0xdeadbeef); | |
mmu_set_ttb(0); | |
mmu_set_enabled(); | |
memory_write_uint32(0, 0x0100000 | (3 << 10) | 2); | |
printf("first section 0x%08x\n", memory_read_uint32(0)); | |
memory_write_uint32(4, 0xfff00000 | 1); | |
printf("first pagetable 0x%08x\n", memory_read_uint32(4)); | |
printf("0x%08x\n", mmu_memory_read_uint32(0x20)); | |
printf("0x%08x\n", mmu_memory_read_uint32(0x00100020)); | |
mmu_set_disabled(); | |
printf("0x%08x\n", mmu_memory_read_uint32(0x20)); | |
printf("0x%08x\n", mmu_memory_read_uint32(0x00100020)); | |
#if 0 | |
mmu_translate_address(0x12345, 0, 1, &addr, &fsr); | |
printf("0x%08x FSR 0x%08x\n", addr, fsr); | |
mmu_translate_address(0xfff, 0, 1, &addr, &fsr); | |
printf("0x%08x FSR 0x%08x\n", addr, fsr); | |
mmu_translate_address(0x00112345, 0, 1, &addr, &fsr); | |
printf("0x%08x FSR 0x%08x\n", addr, fsr); | |
mmu_translate_address(0x00100fff, 0, 1, &addr, &fsr); | |
printf("0x%08x FSR 0x%08x\n", addr, fsr); | |
#endif | |
#if 0 | |
memory_region_register(0xfffe0000, 65535, NULL, NULL); | |
#endif | |
#if 0 | |
uint32_t pa = 0, va = 0xffff0000; | |
for (pa = 0; pa <= 0xffff0000; pa += PAGE_SIZE, va -= PAGE_SIZE) { | |
tb_insert(va, pa, 0, 0, PAGE_SIZE); | |
// tb_flush(); | |
tb_probe(0xfffe0000); | |
} | |
#endif | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment