141 lines
3.2 KiB
C
141 lines
3.2 KiB
C
#define _GNU_SOURCE
|
|
#include <search.h>
|
|
#include "platform.h"
|
|
|
|
#define USER_SPACE RANGE(0x40000000, 0xc0000000)
|
|
|
|
typedef struct PageMap {
|
|
void *va;
|
|
void *pa;
|
|
struct PageMap *next;
|
|
int prot;
|
|
int is_mapped;
|
|
char key[32]; // used for hsearch_r()
|
|
} PageMap;
|
|
|
|
typedef struct VMHead {
|
|
PageMap *head;
|
|
struct hsearch_data hash;
|
|
int nr_page;
|
|
} VMHead;
|
|
|
|
#define list_foreach(p, head) \
|
|
for (p = (PageMap *)(head); p != NULL; p = p->next)
|
|
|
|
extern int __am_pgsize;
|
|
static int vme_enable = 0;
|
|
static void* (*pgalloc)(int) = NULL;
|
|
static void (*pgfree)(void *) = NULL;
|
|
|
|
bool vme_init(void* (*pgalloc_f)(int), void (*pgfree_f)(void*)) {
|
|
pgalloc = pgalloc_f;
|
|
pgfree = pgfree_f;
|
|
vme_enable = 1;
|
|
return true;
|
|
}
|
|
|
|
void protect(AddrSpace *as) {
|
|
assert(as != NULL);
|
|
VMHead *h = pgalloc(__am_pgsize); // used as head of the list
|
|
assert(h != NULL);
|
|
memset(h, 0, sizeof(*h));
|
|
int max_pg = (USER_SPACE.end - USER_SPACE.start) / __am_pgsize;
|
|
int ret = hcreate_r(max_pg, &h->hash);
|
|
assert(ret != 0);
|
|
|
|
as->ptr = h;
|
|
as->pgsize = __am_pgsize;
|
|
as->area = USER_SPACE;
|
|
}
|
|
|
|
void unprotect(AddrSpace *as) {
|
|
}
|
|
|
|
void __am_switch(Context *c) {
|
|
if (!vme_enable) return;
|
|
|
|
VMHead *head = c->vm_head;
|
|
VMHead *now_head = thiscpu->vm_head;
|
|
if (head == now_head) goto end;
|
|
|
|
PageMap *pp;
|
|
if (now_head != NULL) {
|
|
// munmap all mappings
|
|
list_foreach(pp, now_head->head) {
|
|
if (pp->is_mapped) {
|
|
__am_pmem_unmap(pp->va);
|
|
pp->is_mapped = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (head != NULL) {
|
|
// mmap all mappings
|
|
list_foreach(pp, head->head) {
|
|
assert(IN_RANGE(pp->va, USER_SPACE));
|
|
__am_pmem_map(pp->va, pp->pa, pp->prot);
|
|
pp->is_mapped = true;
|
|
}
|
|
}
|
|
|
|
end:
|
|
thiscpu->vm_head = head;
|
|
}
|
|
|
|
void map(AddrSpace *as, void *va, void *pa, int prot) {
|
|
assert(IN_RANGE(va, USER_SPACE));
|
|
assert((uintptr_t)va % __am_pgsize == 0);
|
|
assert((uintptr_t)pa % __am_pgsize == 0);
|
|
assert(as != NULL);
|
|
PageMap *pp = NULL;
|
|
VMHead *vm_head = as->ptr;
|
|
assert(vm_head != NULL);
|
|
char buf[32];
|
|
snprintf(buf, 32, "%x", va);
|
|
ENTRY item = { .key = buf };
|
|
ENTRY *item_find;
|
|
hsearch_r(item, FIND, &item_find, &vm_head->hash);
|
|
if (item_find == NULL) {
|
|
pp = pgalloc(__am_pgsize); // this will waste memory, any better idea?
|
|
snprintf(pp->key, 32, "%x", va);
|
|
item.key = pp->key;
|
|
item.data = pp;
|
|
int ret = hsearch_r(item, ENTER, &item_find, &vm_head->hash);
|
|
assert(ret != 0);
|
|
vm_head->nr_page ++;
|
|
} else {
|
|
pp = item_find->data;
|
|
}
|
|
pp->va = va;
|
|
pp->pa = pa;
|
|
pp->prot = prot;
|
|
pp->is_mapped = false;
|
|
pp->next = vm_head->head;
|
|
vm_head->head = pp;
|
|
|
|
if (vm_head == thiscpu->vm_head) {
|
|
// enforce the map immediately
|
|
__am_pmem_map(pp->va, pp->pa, pp->prot);
|
|
pp->is_mapped = true;
|
|
}
|
|
}
|
|
|
|
Context* ucontext(AddrSpace *as, Area kstack, void *entry) {
|
|
Context *c = (Context*)kstack.end - 1;
|
|
|
|
__am_get_example_uc(c);
|
|
c->uc.uc_mcontext.gregs[REG_RIP] = (uintptr_t)entry;
|
|
c->uc.uc_mcontext.gregs[REG_RSP] = (uintptr_t)USER_SPACE.end;
|
|
|
|
int ret = sigemptyset(&(c->uc.uc_sigmask)); // enable interrupt
|
|
assert(ret == 0);
|
|
c->vm_head = as->ptr;
|
|
|
|
c->ksp = (uintptr_t)kstack.end;
|
|
|
|
return c;
|
|
}
|
|
|
|
int __am_in_userspace(void *addr) {
|
|
return vme_enable && thiscpu->vm_head != NULL && IN_RANGE(addr, USER_SPACE);
|
|
}
|