297 lines
6.0 KiB
C
297 lines
6.0 KiB
C
/*
|
|
Copyright (C) 2015-2019 The University of Notre Dame
|
|
This software is distributed under the GNU General Public License.
|
|
See the file LICENSE for details.
|
|
*/
|
|
|
|
#include "pagetable.h"
|
|
#include "page.h"
|
|
#include "string.h"
|
|
#include "kernelcore.h"
|
|
|
|
#define ENTRIES_PER_TABLE (PAGE_SIZE/4)
|
|
|
|
struct pageentry {
|
|
unsigned present:1; // 1 = present
|
|
unsigned readwrite:1; // 1 = writable
|
|
unsigned user:1; // 1 = user mode
|
|
unsigned writethrough:1; // 1 = write through
|
|
|
|
unsigned nocache:1; // 1 = no caching
|
|
unsigned accessed:1; // 1 = accessed
|
|
unsigned dirty:1; // 1 = dirty
|
|
unsigned pagesize:1; // leave to zero
|
|
|
|
unsigned globalpage:1; // 1 if not to be flushed
|
|
unsigned avail:3;
|
|
|
|
unsigned addr:20;
|
|
};
|
|
|
|
struct pagetable {
|
|
struct pageentry entry[ENTRIES_PER_TABLE];
|
|
};
|
|
|
|
struct pagetable *pagetable_create()
|
|
{
|
|
return page_alloc(1);
|
|
}
|
|
|
|
void pagetable_init(struct pagetable *p)
|
|
{
|
|
unsigned i, stop;
|
|
stop = total_memory * 1024 * 1024;
|
|
for(i = 0; i < stop; i += PAGE_SIZE) {
|
|
pagetable_map(p, i, i, PAGE_FLAG_KERNEL | PAGE_FLAG_READWRITE);
|
|
}
|
|
stop = (unsigned) video_buffer + video_xres * video_yres * 3;
|
|
for(i = (unsigned) video_buffer; i <= stop; i += PAGE_SIZE) {
|
|
pagetable_map(p, i, i, PAGE_FLAG_KERNEL | PAGE_FLAG_READWRITE);
|
|
}
|
|
}
|
|
|
|
int pagetable_getmap(struct pagetable *p, unsigned vaddr, unsigned *paddr, int *flags)
|
|
{
|
|
struct pagetable *q;
|
|
struct pageentry *e;
|
|
|
|
unsigned a = vaddr >> 22;
|
|
unsigned b = (vaddr >> 12) & 0x3ff;
|
|
|
|
e = &p->entry[a];
|
|
if(!e->present)
|
|
return 0;
|
|
|
|
q = (struct pagetable *) (e->addr << 12);
|
|
|
|
e = &q->entry[b];
|
|
if(!e->present)
|
|
return 0;
|
|
|
|
*paddr = e->addr << 12;
|
|
|
|
if(flags) {
|
|
*flags = 0;
|
|
if(e->readwrite)
|
|
*flags |= PAGE_FLAG_READWRITE;
|
|
if(e->avail & 0x01)
|
|
*flags |= PAGE_FLAG_ALLOC;
|
|
if(!e->user)
|
|
*flags |= PAGE_FLAG_KERNEL;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int pagetable_map(struct pagetable *p, unsigned vaddr, unsigned paddr, int flags)
|
|
{
|
|
struct pagetable *q;
|
|
struct pageentry *e;
|
|
|
|
unsigned a = vaddr >> 22;
|
|
unsigned b = (vaddr >> 12) & 0x3ff;
|
|
|
|
if(flags & PAGE_FLAG_ALLOC) {
|
|
paddr = (unsigned) page_alloc(flags & PAGE_FLAG_CLEAR);
|
|
if(!paddr)
|
|
return 0;
|
|
}
|
|
|
|
e = &p->entry[a];
|
|
|
|
if(!e->present) {
|
|
q = pagetable_create();
|
|
if(!q)
|
|
return 0;
|
|
e->present = 1;
|
|
e->readwrite = 1;
|
|
e->user = (flags & PAGE_FLAG_KERNEL) ? 0 : 1;
|
|
e->writethrough = 0;
|
|
e->nocache = 0;
|
|
e->accessed = 0;
|
|
e->dirty = 0;
|
|
e->pagesize = 0;
|
|
e->globalpage = (flags & PAGE_FLAG_KERNEL) ? 1 : 0;
|
|
e->avail = 0;
|
|
e->addr = (((unsigned) q) >> 12);
|
|
} else {
|
|
q = (struct pagetable *) (((unsigned) e->addr) << 12);
|
|
}
|
|
|
|
|
|
e = &q->entry[b];
|
|
|
|
e->present = 1;
|
|
e->readwrite = (flags & PAGE_FLAG_READWRITE) ? 1 : 0;
|
|
e->user = (flags & PAGE_FLAG_KERNEL) ? 0 : 1;
|
|
e->writethrough = 0;
|
|
e->nocache = 0;
|
|
e->accessed = 0;
|
|
e->dirty = 0;
|
|
e->pagesize = 0;
|
|
e->globalpage = !e->user;
|
|
e->avail = (flags & PAGE_FLAG_ALLOC) ? 1 : 0;
|
|
e->addr = (paddr >> 12);
|
|
|
|
return 1;
|
|
}
|
|
|
|
void pagetable_unmap(struct pagetable *p, unsigned vaddr)
|
|
{
|
|
struct pagetable *q;
|
|
struct pageentry *e;
|
|
|
|
unsigned a = vaddr >> 22;
|
|
unsigned b = vaddr >> 12 & 0x3ff;
|
|
|
|
e = &p->entry[a];
|
|
if(e->present) {
|
|
q = (struct pagetable *) (e->addr << 12);
|
|
e = &q->entry[b];
|
|
e->present = 0;
|
|
}
|
|
}
|
|
|
|
void pagetable_delete(struct pagetable *p)
|
|
{
|
|
unsigned i, j;
|
|
|
|
struct pageentry *e;
|
|
struct pagetable *q;
|
|
|
|
for(i = 0; i < ENTRIES_PER_TABLE; i++) {
|
|
e = &p->entry[i];
|
|
if(e->present) {
|
|
q = (struct pagetable *) (e->addr << 12);
|
|
for(j = 0; j < ENTRIES_PER_TABLE; j++) {
|
|
e = &q->entry[j];
|
|
if(e->present && e->avail) {
|
|
void *paddr;
|
|
paddr = (void *) (e->addr << 12);
|
|
page_free(paddr);
|
|
}
|
|
}
|
|
page_free(q);
|
|
}
|
|
}
|
|
|
|
page_free(p);
|
|
}
|
|
|
|
void pagetable_alloc(struct pagetable *p, unsigned vaddr, unsigned length, int flags)
|
|
{
|
|
unsigned npages = length / PAGE_SIZE;
|
|
|
|
if(length % PAGE_SIZE)
|
|
npages++;
|
|
|
|
vaddr &= 0xfffff000;
|
|
|
|
while(npages > 0) {
|
|
unsigned paddr;
|
|
if(!pagetable_getmap(p, vaddr, &paddr, 0)) {
|
|
pagetable_map(p, vaddr, 0, flags | PAGE_FLAG_ALLOC);
|
|
}
|
|
vaddr += PAGE_SIZE;
|
|
npages--;
|
|
}
|
|
}
|
|
|
|
void pagetable_free(struct pagetable *p, unsigned vaddr, unsigned length)
|
|
{
|
|
unsigned npages = length / PAGE_SIZE;
|
|
|
|
if(length % PAGE_SIZE)
|
|
npages++;
|
|
|
|
vaddr &= 0xfffff000;
|
|
|
|
while(npages > 0) {
|
|
unsigned paddr;
|
|
int flags;
|
|
if(pagetable_getmap(p, vaddr, &paddr, &flags)) {
|
|
pagetable_unmap(p, vaddr);
|
|
if(flags & PAGE_FLAG_ALLOC)
|
|
page_free((void *) paddr);
|
|
}
|
|
vaddr += PAGE_SIZE;
|
|
npages--;
|
|
}
|
|
}
|
|
|
|
struct pagetable *pagetable_load(struct pagetable *p)
|
|
{
|
|
struct pagetable *oldp;
|
|
asm("mov %%cr3, %0":"=r"(oldp));
|
|
asm("mov %0, %%cr3"::"r"(p));
|
|
return oldp;
|
|
}
|
|
|
|
void pagetable_refresh()
|
|
{
|
|
asm("mov %cr3, %eax");
|
|
asm("mov %eax, %cr3");
|
|
}
|
|
|
|
void pagetable_enable()
|
|
{
|
|
asm("movl %cr0, %eax");
|
|
asm("orl $0x80000000, %eax");
|
|
asm("movl %eax, %cr0");
|
|
}
|
|
|
|
struct pagetable *pagetable_duplicate(struct pagetable *sp)
|
|
{
|
|
unsigned i, j;
|
|
|
|
struct pageentry *e;
|
|
struct pagetable *q;
|
|
|
|
struct pageentry *newe;
|
|
struct pagetable *newq;
|
|
struct pagetable *newp = pagetable_create();
|
|
if(!newp)
|
|
goto cleanup;
|
|
|
|
for(i = 0; i < ENTRIES_PER_TABLE; i++) {
|
|
e = &sp->entry[i];
|
|
newe = &newp->entry[i];
|
|
if(e->present) {
|
|
q = (struct pagetable *) (e->addr << 12);
|
|
newq = pagetable_create();
|
|
if(!newq)
|
|
goto cleanup;
|
|
memcpy(newe, e, sizeof(struct pageentry));
|
|
newe->addr = (((unsigned) newq) >> 12);
|
|
for(j = 0; j < ENTRIES_PER_TABLE; j++) {
|
|
e = &q->entry[j];
|
|
newe = &newq->entry[j];
|
|
memcpy(newe, e, sizeof(struct pageentry));
|
|
if(e->present) {
|
|
void *paddr;
|
|
paddr = (void *) (e->addr << 12);
|
|
void *new_paddr = 0;
|
|
if(e->avail) {
|
|
new_paddr = page_alloc(0);
|
|
if(!new_paddr)
|
|
goto cleanup;
|
|
memcpy(new_paddr, paddr, PAGE_SIZE);
|
|
} else {
|
|
new_paddr = paddr;
|
|
}
|
|
newe->addr = (((unsigned) new_paddr) >> 12);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return newp;
|
|
cleanup:
|
|
printf("Pagetable duplicate errors\n");
|
|
if(newp) {
|
|
pagetable_delete(newp);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void pagetable_copy(struct pagetable *sp, unsigned saddr, struct pagetable *tp, unsigned taddr, unsigned length);
|