-
-
Save 44670/0d8c152df7c5b59d17d469aba4dda0e5 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
#include "CacheEngine.h" | |
uint64_t volatile __attribute__((aligned(CE_PAGE_SIZE))) ceLv1PageTable[(CE_PAGE_SIZE / 8) * (1)]; | |
uint64_t volatile __attribute__((aligned(CE_PAGE_SIZE))) ceLv2PageTables[(CE_PAGE_SIZE / 8) * (1)]; | |
uint64_t volatile __attribute__((aligned(CE_PAGE_SIZE))) ceLv3PageTables[(CE_PAGE_SIZE / 8) * (CE_LV3_PAGE_TABLE_COUNT)]; | |
uint64_t __attribute__((aligned(CE_PAGE_SIZE))) ceCacheMemory[(CE_PAGE_SIZE / 8) * CE_CACHE_SIZE_IN_PAGES]; | |
uint16_t ceCacheMemoryBlockAge[CE_CACHE_SIZE_IN_BLOCKS]; | |
uint16_t ceCacheMemoryBlockToVBlockId[CE_CACHE_SIZE_IN_BLOCKS]; | |
static const uint64_t ceVABase = 0x100000000ULL; | |
static int ceIsMapWritable; | |
#ifdef CE_USE_FATFS | |
static FIL* ceFatFsFp; | |
static DWORD ceFatFsFileClusterTable[1024]; | |
void* ceMapFileFatFs(FIL* fp) { | |
memset(ceFatFsFileClusterTable, 0, sizeof(ceFatFsFileClusterTable)); | |
ceFatFsFileClusterTable[0] = sizeof(ceFatFsFileClusterTable) / sizeof(DWORD); | |
// Cache the cluster table to make random access effecient, FF_USE_FASTSEEK must be set in ffconf.h | |
fp->cltbl = ceFatFsFileClusterTable; | |
FRESULT ret = f_lseek(fp, CREATE_LINKMAP); | |
if (ret != FR_OK) { | |
CE_ERROR_PRINT("ceMapFileFatFs: init cluster table failed: %d (maybe the file is too fragmented?)\n", ret); | |
return 0; | |
} | |
ceFatFsFp = fp; | |
return (void*)ceVABase; | |
} | |
int ceFileReadCallback(uint32_t fileOffset, uint64_t* buf, uint32_t len) { | |
CE_DEBUG_PRINT("ceFileReadCallback(fatfs): %d, %p, %d\n", fileOffset, buf, len); | |
UINT bytesRead = 0; | |
if (f_lseek(ceFatFsFp, fileOffset) != FR_OK) { | |
CE_DEBUG_PRINT("ceFileReadCallback(fatfs) f_lseek failed: %d, %p, %d\n", fileOffset, buf, len); | |
return -1; | |
} | |
if (f_read(ceFatFsFp, buf, len, &bytesRead) != FR_OK) { | |
CE_DEBUG_PRINT("ceFileReadCallback(fatfs) f_read failed: %d, %p, %d\n", fileOffset, buf, len); | |
return -1; | |
} | |
if (bytesRead != len) { | |
CE_DEBUG_PRINT("ceFileReadCallback(fatfs) bytesRead != len: %d, %p, %d\n", fileOffset, buf, len); | |
return -1; | |
} | |
return 0; | |
} | |
#endif | |
void ceResetCacheState() { | |
memset(ceCacheMemoryBlockAge, 0, sizeof(ceCacheMemoryBlockAge)); | |
memset(ceLv3PageTables, 0, sizeof(ceLv3PageTables)); | |
ceIsMapWritable = 0; | |
asm volatile ("sfence.vm"); | |
} | |
uint64_t ceEncodePTE(uint32_t physAddr, uint32_t flags) { | |
assert((physAddr % CE_PAGE_SIZE) == 0); | |
return (((uint64_t)physAddr >> 12) << 10) | flags; | |
} | |
void ceSetupMMU() { | |
const uint64_t stapModeSv39 = 9; | |
CE_DEBUG_PRINT("setup mmu...\n"); | |
//0 - 0xFFFFFFFF -> mirror to phys | |
for (uint32_t i = 0; i < 4; i++) { | |
ceLv1PageTable[i] = ceEncodePTE((0x40000000U) * i, PTE_V | PTE_R | PTE_W | PTE_X | PTE_G | PTE_U); | |
} | |
//0x100000000 (1GiB) -> lv2 | |
ceLv1PageTable[4] = ceEncodePTE((uint32_t)ceLv2PageTables, PTE_V | PTE_G | PTE_U ); | |
//0x100000000 (2MiB * CE_LV3_PAGE_TABLE_COUNT) -> lv3 | |
for (uint32_t i = 0; i < CE_LV3_PAGE_TABLE_COUNT; i++) { | |
ceLv2PageTables[i] = ceEncodePTE(((uint32_t)ceLv3PageTables) + i * CE_PAGE_SIZE, PTE_V | PTE_G | PTE_U); | |
} | |
write_csr(sptbr, (uint64_t)ceLv1PageTable >> 12); | |
uint64_t msValue = read_csr(mstatus); | |
msValue |= MSTATUS_MPRV | ((uint64_t)VM_SV39 << 24); | |
write_csr(mstatus, msValue); | |
ceResetCacheState(); | |
} | |
static inline uint32_t ceVAddrToVBlockId(uintptr_t vaddr) { | |
return (vaddr - ceVABase) / (CE_BLOCK_SIZE); | |
} | |
static void ceMapVBlockToPhysAddr(uint32_t vBlockId, uint32_t physAddr) { | |
uint32_t basePageId = vBlockId * CE_BLOCK_SIZE_IN_PAGES; | |
uintptr_t vaddr = 0; | |
for (uint32_t i = 0 ; i < CE_BLOCK_SIZE_IN_PAGES; i++) { | |
ceLv3PageTables[basePageId + i] = physAddr ? ceEncodePTE(physAddr + i * CE_PAGE_SIZE, PTE_V | PTE_R | PTE_X | PTE_G | PTE_U) : 0; | |
vaddr = ceVABase + ((uintptr_t)(basePageId + i) * CE_PAGE_SIZE); | |
asm volatile("sfence.vm %0" : "=r"(vaddr)); | |
} | |
} | |
static inline int ceCheckAndSetVBlockAccessFlag(uint32_t vBlockId) { | |
int hasAccessed = 0; | |
uint32_t basePageId = vBlockId * CE_BLOCK_SIZE_IN_PAGES; | |
for (uint32_t i = 0; i < CE_BLOCK_SIZE_IN_PAGES; i++) { | |
uint64_t pte = ceLv3PageTables[basePageId + i]; | |
assert(pte & PTE_V); | |
if (pte & PTE_A) { | |
// TODO: ensure this operation is atomic | |
ceLv3PageTables[basePageId + i] &= (~((uint64_t)PTE_A)); | |
hasAccessed = 1; | |
} | |
} | |
return hasAccessed; | |
} | |
static uint32_t ceFindBlockToRetire() { | |
uint16_t maxAge = 0; | |
uint32_t maxAgeAt = 0; | |
for (uint32_t i = 0; i < CE_CACHE_SIZE_IN_BLOCKS; i++) { | |
uint16_t age = ceCacheMemoryBlockAge[i]; | |
if (age == 0) { | |
// an empty block! | |
return i; | |
} | |
if (age >= maxAge) { | |
maxAge = age; | |
maxAgeAt = i; | |
} | |
} | |
return maxAgeAt; | |
} | |
int ceHandlePageFault(uintptr_t vaddr, int isWrite) { | |
if (isWrite) { | |
if (!ceIsMapWritable) { | |
return -1; | |
} | |
} | |
uint32_t cacheBlockId = ceFindBlockToRetire(); | |
CE_DEBUG_PRINT("ceHandlePageFault: %p, %d\n", (void*)vaddr, cacheBlockId); | |
if (ceCacheMemoryBlockAge[cacheBlockId]) { | |
// an used block, free it | |
ceMapVBlockToPhysAddr(ceCacheMemoryBlockToVBlockId[cacheBlockId], 0); | |
} | |
ceCacheMemoryBlockAge[cacheBlockId] = 0; | |
uint32_t vBlockId = ceVAddrToVBlockId(vaddr); | |
uint32_t physAddr = ((uint32_t) ceCacheMemory) + (CE_BLOCK_SIZE * cacheBlockId); | |
int ret = ceFileReadCallback(vBlockId * CE_BLOCK_SIZE, (uint64_t*)physAddr, CE_BLOCK_SIZE); | |
if (ret != 0) { | |
CE_ERROR_PRINT("ceHandlePageFault: file read failed, %p, %d\n", (void*)vaddr, ret); | |
return -1; | |
} | |
ceCacheMemoryBlockAge[cacheBlockId] = 1; | |
ceCacheMemoryBlockToVBlockId[cacheBlockId] = (uint16_t) vBlockId; | |
ceMapVBlockToPhysAddr(vBlockId, physAddr); | |
return 0; | |
} | |
void ceUpdateBlockAge() { | |
for (uint32_t i = 0; i < CE_CACHE_SIZE_IN_BLOCKS; i++) { | |
uint16_t age = ceCacheMemoryBlockAge[i]; | |
if (age == 0) { | |
// an empty block! | |
continue; | |
} | |
int hasAccessed = ceCheckAndSetVBlockAccessFlag(ceCacheMemoryBlockToVBlockId[i]); | |
if (!hasAccessed) { | |
if (age < UINT16_MAX) { | |
age ++; | |
ceCacheMemoryBlockAge[i] = age; | |
} | |
} else { | |
age = 1; | |
ceCacheMemoryBlockAge[i] = age; | |
} | |
//CE_DEBUG_PRINT("ceUpdateBlockAge: %d, %d\n", i, age); | |
} | |
} | |
uintptr_t handle_fault_load(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32]) { | |
uintptr_t badAddr = read_csr(mbadaddr); | |
if ((badAddr >= ceVABase) && (badAddr < (ceVABase + CE_CACHE_VA_SPACE_SIZE))) { | |
if (ceHandlePageFault(badAddr, 0) == 0) { | |
return epc; | |
} | |
} | |
CE_ERROR_PRINT("fault load could not be handled, badAddr: %p, epc: %p\n", (void*) badAddr, (void*) epc); | |
sys_exit(1337); | |
return epc; | |
} |
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
#pragma once | |
#define CE_USE_FATFS | |
#include <stdio.h> | |
#include <assert.h> | |
#include "bsp.h" | |
#include "printf.h" | |
#include "encoding.h" | |
#ifdef CE_USE_FATFS | |
#include "ff.h" | |
void* ceMapFileFatFs(FIL* fp); | |
#endif | |
#define CE_PAGE_SIZE (4096) | |
#define CE_BLOCK_SIZE_IN_PAGES (1) | |
#define CE_BLOCK_SIZE (CE_PAGE_SIZE * CE_BLOCK_SIZE_IN_PAGES) | |
#define CE_LV3_PAGE_TABLE_COUNT (32) | |
#define CE_CACHE_VA_SPACE_SIZE_IN_BLOCKS (512 * CE_LV3_PAGE_TABLE_COUNT) | |
#define CE_CACHE_VA_SPACE_SIZE_IN_PAGES (CE_CACHE_VA_SPACE_SIZE_IN_BLOCKS * CE_BLOCK_SIZE_IN_PAGES) | |
#define CE_CACHE_VA_SPACE_SIZE (CE_CACHE_VA_SPACE_SIZE_IN_PAGES * CE_PAGE_SIZE) | |
#define CE_CACHE_SIZE_IN_BLOCKS (1024 / CE_BLOCK_SIZE_IN_PAGES) | |
#define CE_CACHE_SIZE_IN_PAGES (CE_CACHE_SIZE_IN_BLOCKS * CE_BLOCK_SIZE_IN_PAGES) | |
void ceResetCacheState(); | |
void ceSetupMMU(); | |
void ceUpdateBlockAge(); | |
#define CE_DEBUG_PRINT printk | |
#define CE_ERROR_PRINT printk |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment