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); |