Code: Select all
function BootstrapKernelPaging(): integer;
var
i: integer;
begin
{Set result to the default error code.}
BootstrapKernelPaging:= -1;
pPageDirectory:= nil;
{Get a page to be page directory.}
i:= AllocatePage();
if i > 0 then
pPageDirectory:= PLongword(Longword(i * 4096 - Longword(@SegmentationVirtualBase)))
else
begin
{Set the page directory pointer to nil. IMPORTANT.}
pPageDirectory:= nil;
Write(PChar('AllocatePage for the page directory failed with error '));
WriteIntLn(i);
{-2 means the page directory couldn't be allocated.}
BootstrapKernelPaging:= -2;
Exit;
end;
{This is equivalent to pPageDirectory[all]:= SetPageEntry(0,false,false,false).}
FillDWord(pPageDirectory^,1024,0);
{Get another page to be the first page table.}
i:= AllocatePage();
if i > 0 then
pPageTable:= PLongword(Longword(i * 4096 - Longword(@SegmentationVirtualBase)))
else
begin
{Free the page directory. VERY IMPORTANT.}
FreePage(Longword(pPageDirectory) div 4096);
{Set the page directory pointer to nil. IMPORTANT.}
pPageDirectory:= nil;
Write(PChar('AllocatePage for the first page table failed with error '));
WriteIntLn(i);
{-3 means the first page table couldn't be allocated.}
BootstrapKernelPaging:= -3;
Exit;
end;
{This is equivalent to pPageTable[all]:= SetPageEntry(0,false,false,false).}
FillDWord(pPageTable^,1024,0);
{Set every page in the first page table to supervisor, writable and present.}
{This will map the first 4 MB of memory to its place.}
{For now it is assumed that 4 MB is enough to map the entire kernel area.}
i:= KernelZoneEnd + Longword(@SegmentationVirtualBase);
{Map video memory.}
pPageTable[$B8]:= SetPageEntry(Pointer($B8000),false,true,true);
{Address $100 = $100000 div $1000 (page). The kernel's load point is the first page to get mapped.}
{BIOS and such aren't mapped because they break at the high addresses.}
for i:= $100 to (i div 4096) - 1 do
pPageTable[i]:= SetPageEntry(Pointer(i * 4096),false,true,true);
{Map the page table and page directory to new locations since they were allocated dynamically.}
{These new locations will allow for planning of page table/directory and kernel heap locations.}
i:= KernelZoneEnd + (4096 - (KernelZoneEnd mod 4096)) + Longword(@SegmentationVirtualBase);
pPageTable[i div 4096]:= SetPageEntry(Pointer(i),false,true,true);
i:= i + 4096;
pPageTable[i div 4096]:= SetPageEntry(Pointer(i),false,true,true);
{Map that page table to virtual address $C0000000.}
i:= Longword(pPageTable) + Longword(@SegmentationVirtualBase);
{Address $300 = $C0000000 div 4096 (page) div 1024 (superpage) and superpage = page directory entry.}
pPageDirectory[$300]:= SetPageEntry(Pointer(i),false,true,true);
{Identity map the page table. This keeps the kernel from crashing before the segment base can be changed.}
pPageDirectory[0]:= SetPageEntry(Pointer(i),false,true,true);
{Put the address of the page directory into the proper CPU register.}
i:= Longword(pPageDirectory) + Longword(@SegmentationVirtualBase);
SetPageDirectory(Pointer(i));
{If an IRQ fires while paging is on but before the IDT is refilled with exceptions and IRQ handlers there will be a triplefault, so disable IRQs.}
DisableInterrupts();
{Enable paging. DANGEROUS.}
SetPaging(true);
{With paging enabled set these variables to their new virtual addresses.}
pPageDirectory:= PLongword(KernelZoneEnd + Longword(4096 - (KernelZoneEnd mod 4096)));
pPageTable:= pPageDirectory + 1024;
{The exceptions and IRQs in the IDT need to be bootstrapped for paging.}
BootstrapIDT($20);
BootstrapIRQs($20);
{Now that everything knows what the new environment is, switch the segment registers.}
SetSegmentRegisters($18,$20);
{Now that everything knows the new environment, reenable IRQs.}
EnableInterrupts();
{Now that the segments are changed there is no more need for identity mapping.}
{The page directory and page table must be mapped in their higher half places correctly for this assignment to work.}
pPageDirectory[0]:= 0;
WriteLn(PChar('Kernel now paged to virtual address $C010000.'));
BootstrapKernelPaging:= 1;
end;