
// vmm.c
// Author: Josh Holtrop
// Date: 09/30/03

PageDirectory	*vmm_PDBR = 0;
dword	vmm_first_virtual_address = 0;


void	vmm_init()
{
	if (!(vmm_PDBR = mm_palloc(1, PID_KERNEL)))
	{
		printf("ERROR! COULD NOT ALLOCATE PAGE FOR INITIAL PAGE DIRECTORY!!\n");
		halt();
	}
	vmm_init_pagetable((dword)vmm_PDBR);

	if (mm_totalmem % 4096)
		vmm_first_virtual_address = mm_totalmem + (4096 - (mm_totalmem % 4096));
	else
		vmm_first_virtual_address = mm_totalmem;

	if (vmm_mapn(0, 0, 0x03, mm_totalmem/4096+1))
	{
		printf("Could not page in all physical RAM!\n");
		halt();
	}
	//we also need to map in the video framebuffer memory:
	if (video_mode.PhysBasePtr > 0 && video_mode.BitsPerPixel > 0)
	{
		if (vmm_mapn(video_mode.PhysBasePtr, video_mode.PhysBasePtr, 0x03, ((video_mode.BitsPerPixel>>3) * video_mode.XResolution * video_mode.YResolution)/4096+1))
		{
			printf("Could not page in video memory at 0x%x!\n", video_mode.PhysBasePtr);
			halt();
		}
	}
}


void	vmm_enable_paging()
{
	write_cr3((dword)vmm_PDBR);
	write_cr0(0x80000000|read_cr0());
}


int	vmm_map1(dword virtual, dword physical, dword flags)
{
	if (virtual & 0x00000FFF)
		return 1;			// ERROR 1: address not page-aligned
	if (physical & 0x00000FFF)
		return 1;			// ERROR 1: address not page-aligned
	int pde = (virtual & 0xFFC00000) >> 22;
	int pte = (virtual & 0x003FF000) >> 12;
	if (!(vmm_PDBR->pageTables[pde] & 0x01))
	{
		vmm_PDBR->pageTables[pde] = (dword)mm_palloc(1, PID_KERNEL) | 0x7;	//user, r/w, present
		vmm_init_pagetable(vmm_PDBR->pageTables[pde] & 0xFFFFF000);
		if (vmm_PDBR->pageTables[pde] == 0x07)
			return 2;		// ERROR 2: page table could not be created
	}
	PageTable *ptp = (PageTable *)(vmm_PDBR->pageTables[pde] & 0xFFFFF000);
	if (ptp->page[pte] & 0x01)
		return 3;			// ERROR 3: page table entry already exists
	ptp->page[pte] = physical | flags;
	//invlpg();
	return 0;
}


int	vmm_mapn(dword virtual, dword physical, dword flags, dword n)
{
	int mapped;
	int result = 0;
	dword va = virtual;
	dword pa = physical;
	for (mapped = 0; mapped < n; mapped++)
	{
		if (result = vmm_map1(va, pa, flags))
		{
			if (mapped > 0)
				vmm_unmapn(virtual, mapped);
			return result;
		}
		va += 4096;
		pa += 4096;
	}
	//invlpg();
	return 0;
}

int	vmm_unmap1(dword virtual)
{
	if (virtual & 0x00000FFF)
		return 1;		// ERROR 1: address not page-aligned
	int pde = (virtual & 0xFFC00000) >> 22;
	int pte = (virtual & 0x003FF000) >> 12;
	if (!(vmm_PDBR->pageTables[pde] & 0x01))
		return 2;		// ERROR 2: page table not present
	PageTable *ptp = (PageTable *)(vmm_PDBR->pageTables[pde] & 0xFFFFF000);
	ptp->page[pte] = 0;
	//invlpg();
	return 0;
}


int	vmm_unmapn(dword virtual, dword n)
{
	int reslt;
	int i;
	for (i = 0; i < n; i++)
	{
		if (reslt = vmm_unmap1(virtual))
			return reslt;
		virtual += 4096;
	}
	vmm_walkPageTables();
	//invlpg();
	return 0;
}


void	vmm_init_pagetable(dword address)
{
	PageTable *ptp = (PageTable *)address;
	int n;
	for (n = 0; n < 1024; n++)
		ptp->page[n] = 0;
}


int	vmm_walkPageTables()
{
	int pde;
	int pte;
	int used_ptes;
	PageTable *ptp;
	for (pde = 0; pde < 1024; pde++)
	{
		if (vmm_PDBR->pageTables[pde] & 0x01)
		{
			used_ptes = 0;
			ptp = (PageTable *)(vmm_PDBR->pageTables[pde] & 0xFFFFF000);
			for (pte = 0; pte < 1024; pte++)
			{
				if (ptp->page[pte] & 0x01)	//page table entry present
					used_ptes++;
			}
			if (!(used_ptes))			//no used pte's -- remove page table & page directory entry
			{
				mm_pfree(ptp);
				vmm_PDBR->pageTables[pde] = 0;
			}
		}
	}
	return 0;
}


void	*malloc(dword bytes)
{


}



int	free(void *ptr)
{




}







