Skip to content

Instantly share code, notes, and snippets.

@44670
Created March 31, 2019 11:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 44670/0d8c152df7c5b59d17d469aba4dda0e5 to your computer and use it in GitHub Desktop.
Save 44670/0d8c152df7c5b59d17d469aba4dda0e5 to your computer and use it in GitHub Desktop.
#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;
}
#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