#include <kernel/kernel.h>
#include <kernel/memory.h>
#include <stdlib.h>
#include <wchar.h>
#include <errno.h>
extern byte scode[], edata[], ebss[];
extern dword kernel_pagedir[];
void main();
page_pool_t pool_all, pool_low;
//! Marks a range of pages with the specified flags.
/*!
* \param start Page number of the start of the range
* \param end Page number of the end of the range
* \param flags Flags to assign to each of the pages
*/
/*void memMark(int start, int end, dword flags)
{
//if (flags == PAGE_RESERVED)
//wprintf(L"Reserved: %08x-%08x\n", start * PAGE_SIZE, end * PAGE_SIZE);
assert(end < num_pages);
for (; start < end; start++)
pages[start].flags = flags;
}*/
//! Allocates a contiguous block of physical memory.
/*!
* \param length The number of pages to allocate
* \return The physical address of the start of the block. If there was
* insufficient free memory, or if a contiguous block could not be found,
* returns NULL.
*/
/*addr_t memAlloc(int length)
{
int i, j;
for (i = 0; i < num_pages; i++)
{
for (j = 0; pages[i + j].flags == PAGE_FREE && j < length; j++)
;
if (j == length)
{
wprintf(L"allocated %d pages at %x\n", length, i * PAGE_SIZE);
memMark(i, i + length, PAGE_USED);
return i * PAGE_SIZE;
}
}
wprintf(L"memAlloc: no pages left!\n");
halt(0);
errno = ENOMEM;
return NULL;
}*/
addr_t memAlloc()
{
addr_t page;
do
{
assert(pool_all.free_pages > 0);
pool_all.free_pages--;
page = pool_all.pages[pool_all.free_pages];
} while (page == (addr_t) -1);
return page;
}
addr_t memAllocLow()
{
addr_t page;
do
{
assert(pool_low.free_pages > 0);
pool_low.free_pages--;
page = pool_low.pages[pool_low.free_pages];
} while (page == (addr_t) -1);
assert(page < NUM_LOW_PAGES * PAGE_SIZE);
return page;
}
addr_t memAllocLowSpan(size_t pages)
{
addr_t page, j, start;
bool found;
for (page = 0; page < pool_low.free_pages; page++)
{
if (pool_low.pages[page] != (addr_t) -1)
{
found = true;
for (j = 0; j < pages; j++)
if (pool_low.pages[page + j] !=
pool_low.pages[page] + j * PAGE_SIZE)
{
found = false;
break;
}
if (found)
{
start = pool_low.pages[page];
for (j = 0; j < pages; j++)
pool_low.pages[page + j] = (addr_t) -1;
return start;
}
}
}
return NULL;
}
//! Frees a block of memory allocated by memAlloc.
/*!
* \param block The physical address of the start of the block
* \param length The number of pages occupied by the block
*/
/*void memFree(addr_t block, int length)
{
if (block)
memMark(block / PAGE_SIZE, block / PAGE_SIZE + length, PAGE_FREE);
}*/
void memFree(addr_t block)
{
pool_all.pages[pool_all.free_pages] = block;
pool_all.free_pages++;
}
void memFreeLow(addr_t block)
{
assert(block < NUM_LOW_PAGES * PAGE_SIZE);
pool_low.pages[pool_low.free_pages] = block;
pool_low.free_pages++;
}
//! Creates a linear-to-physical address mapping in the specified page directory.
/*!
* \param PageDir The page directory to modify
* \param Virt The virtual address of the start of the range
* \param Phys The physical address of the start of the range
* \param pages The number of pages to map
* \param Privilge The IA32 privilege flags to assign to each of the
* pages. In addition, PRIV_PRES is assigned to each page.
* \return true if the pages could be mapped, false if there was insufficient
* free memory to allocate another page table.
*/
bool memMap(dword *PageDir, dword Virt, dword Phys, dword pages, byte Privilege)
{
dword PageTabOff, PageDirEnt, Temp;
/* the top 20 bits of the page table entries are for address translation,
the bottom 12 bits (on the 386) are:
x x x 0 0 Dirty Accessed 0 0 User Writable Present
let the User and Writable bits be set by Privilege */
Privilege = (Privilege & PRIV_ALL)/* | PRIV_PRES*/;
//wprintf(L"map %x => %x: ", Virt, Phys);
for (; pages > 0; pages--, Virt += PAGE_SIZE, Phys += PAGE_SIZE)
{
/* get top-level page offset (i.e. page directory entry on x86)
from virtual address */
PageDirEnt = PAGE_DIRENT(Virt);
if (PageDir[PageDirEnt] == 0)
/* the page directory entry is zero, which means there is no corresponding
page table. Get another 4K of memory and use it to create a new page table. */
{
Temp = memAlloc();
//wprintf(L"%new page = %p: ", Virt, Temp);
if (!Temp)
return false;
/* zero the new page table */
i386_lmemset32(Temp, 0, PAGE_SIZE);
/* point page directory entry to new page table */
/* xxx - Privilege of PageDir is not necessarily Privilege of PageTable
e.g. call this function while creating the code pages, and the
page table ends up read-only (so ALL Ring 3 pages will read-only!!!!) */
PageDir[PageDirEnt] = Temp | PRIV_PRES | PRIV_WR | PRIV_USER;
}
/* get bottom-level page offset (i.e. page table entry on x86)
from virtual address */
Temp = PageDir[PageDirEnt] & -PAGE_SIZE;
PageTabOff = PAGE_TABENT(Virt);
// point page table entry to physical address, and set privilege bits
i386_lpoke32(Temp + PageTabOff * 4, PAGE_NUM(Phys) | Privilege);
}
//wprintf(L"done\n");
return true;
}
//! Retrieves the linear-to-physical address mapping for the specified address.
/*!
* \param pPageDir The page directory to query
* \param pAddress The address to look up
* \return The page table entry associated with the page at pAddress,
* including the physical address and the privilege flags. Returns NULL
* if the page has not been mapped.
*/
dword memTranslate(const dword* pPageDir, const void* pAddress)
{
dword PageDirEnt, PageTable, PageTabOff;
PageDirEnt = PAGE_DIRENT((dword) pAddress);
PageTable = (pPageDir[PageDirEnt]) & -PAGE_SIZE;
if (!PageTable)
return NULL;
PageTabOff = PAGE_TABENT((dword) pAddress);
asm("movl %%gs:(%1),%0" : "=r" (PageTable): "r" (PageTable + PageTabOff * 4));
return PageTable;
}
#define PAGETABLE_MAP 0xdfc00000
#define ADDR_TO_PDE(v) (addr_t*)(PAGEDIRECTORY_MAP + \
(((addr_t)v / (1024 * 1024))&(~0x3)))
#define ADDR_TO_PTE(v) (addr_t*)(PAGETABLE_MAP + ((((addr_t)v / 1024))&(~0x3)))
addr_t* cur_page_dir = (addr_t*) PAGETABLE_MAP;
void memFreeRange(page_pool_t* pool, addr_t start, addr_t end)
{
int i;
//start = (start + PAGE_SIZE) & -PAGE_SIZE;
start &= -PAGE_SIZE;
end = (end + PAGE_SIZE / 2) & -PAGE_SIZE;
wprintf(L"memFreeRange: %x to %x", start, end);
start >>= PAGE_BITS;
end >>= PAGE_BITS;
wprintf(L" (%d pages)\n", end - start);
for (i = 0; i < end - start; i++)
pool->pages[i + pool->free_pages] = (i + start) * PAGE_SIZE;
pool->free_pages += end - start;
}
//! Initialises the physical memory manager and creates the initial kernel page
//! mappings.
/*!
* Called by main() during kernel startup.
* \return true if initialisation succeeded, false otherwise. If this function
* returns false then the system will be unstable.
*/
bool INIT_CODE memInit()
{
addr_t pagedir_phys;
int num_pages;
addr_t* pages;
num_pages = _sysinfo.memory_top / PAGE_SIZE;
_sysinfo.kernel_data = _sysinfo.kernel_size + ebss - edata + num_pages * sizeof(addr_t);
pages = (addr_t*) ebss;
i386_lmemset32((addr_t) pages, 0, num_pages * sizeof(addr_t));
pool_all.num_pages = num_pages - NUM_LOW_PAGES;
pool_all.pages = pages + NUM_LOW_PAGES;
pool_all.free_pages = 0;
pool_low.num_pages = NUM_LOW_PAGES;
pool_low.pages = pages;
pool_low.free_pages = 0;
/* Free memory below the BIOS */
memFreeRange(&pool_low, 0x5000, 0xA0000);
/*memFreeRange(&pool_all,
0x100000, _sysinfo.kernel_phys);
memFreeRange(&pool_all,
_sysinfo.kernel_phys + _sysinfo.kernel_data, _sysinfo.ramdisk_phys);
memFreeRange(&pool_all,
_sysinfo.ramdisk_phys + _sysinfo.ramdisk_size, _sysinfo.memory_top);*/
/* Free memory between 1MB and the ramdisk */
memFreeRange(&pool_all, 0x100000, _sysinfo.ramdisk_phys);
memFreeRange(&pool_all,
_sysinfo.ramdisk_phys + _sysinfo.ramdisk_size, _sysinfo.memory_top);
/*memMark(0, 5, PAGE_RESERVED);
memMark(0xA0, 0x100, PAGE_RESERVED);
memMark(_sysinfo.ramdisk_phys / PAGE_SIZE,
(_sysinfo.ramdisk_phys + _sysinfo.ramdisk_size) / PAGE_SIZE, PAGE_RESERVED);
memMark(_sysinfo.kernel_phys / PAGE_SIZE,
(_sysinfo.kernel_phys + _sysinfo.kernel_data) / PAGE_SIZE, PAGE_RESERVED);*/
/* Map physical memory except bottom 4KB */
memMap(kernel_pagedir, PAGE_SIZE, PAGE_SIZE,
_sysinfo.memory_top / PAGE_SIZE - 1, PRIV_KERN | PRIV_WR | PRIV_PRES);
/* Map graphics memory */
memMap(kernel_pagedir, 0xa0000, 0xa0000, 16, PRIV_USER | PRIV_WR | PRIV_PRES);
/* Map text memory */
memMap(kernel_pagedir, 0xb8000, 0xb8000, 1, PRIV_USER | PRIV_WR | PRIV_PRES);
/* Map kernel above 0x80000000 */
memMap(kernel_pagedir, (addr_t) scode, _sysinfo.kernel_phys,
_sysinfo.kernel_data / PAGE_SIZE, PRIV_KERN | PRIV_WR | PRIV_PRES);
//memMap(kernel_pagedir, 0xff000000, 0xff000000, 1048576 / 4096, PRIV_USER | PRIV_WR);
pagedir_phys = (addr_t) kernel_pagedir - (addr_t) scode + _sysinfo.kernel_phys;
//kernel_pagedir[PAGE_DIRENT((addr_t) cur_page_dir)] = pagedir_phys | PRIV_KERN | PRIV_WR | PRIV_PRES;
asm("mov %0, %%cr3" : : "r" (pagedir_phys));
//wprintf(L"main (%p) maps to %x\n", main, memTranslate(kernel_pagedir, main));
//wprintf(L"_con_x (%p) maps to %x\n", &_con_x, memTranslate(kernel_pagedir, &_con_x));
//wprintf(L"text video buffer maps to %x\n", memTranslate(kernel_pagedir, (void*) 0xb8000));
//wprintf(L"cur_page_dir = %x\n", kernel_pagedir[PAGE_DIRENT((addr_t) cur_page_dir)]);
//wprintf(L"addr 0x100000 = %p = %x\n",
//ADDR_TO_PTE(0x10000), *ADDR_TO_PTE(0x10000));
asm("mov $0x20,%%eax ;"
"mov %%eax,%%ds ;"
"mov %%eax,%%es ;"
"mov %%eax,%%fs ;"
"mov %%eax,%%gs ;"
"mov %%eax,%%ss ;" : : : "eax");
asm("mov %%cr0,%%eax ;"
"or $0x80000000,%%eax ;"
"mov %%eax,%%cr0" : : : "eax");
asm("ljmp $0x18,$_paging ;"
"_paging:");
return true;
}