mirror of
https://github.com/grub4dos/ntloader.git
synced 2025-05-09 12:11:09 +08:00
265 lines
6.5 KiB
C
265 lines
6.5 KiB
C
/*
|
|
* Copyright (C) 2021 Michael Brown <mbrown@fensystems.co.uk>.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301, USA.
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
*
|
|
* Paging
|
|
*
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include "ntloader.h"
|
|
#include "e820.h"
|
|
#include "paging.h"
|
|
|
|
#if defined(__i386__) || defined(__x86_64__)
|
|
|
|
/** Virtual address used as a 2MB window during relocation */
|
|
#define COPY_WINDOW 0x200000
|
|
|
|
/** Paging is available */
|
|
int paging;
|
|
|
|
/** Page directory pointer table */
|
|
static uint64_t pdpt[4] __attribute__ ((aligned (PAGE_SIZE)));
|
|
|
|
/** Page directories */
|
|
static uint64_t pd[2048] __attribute__ ((aligned (PAGE_SIZE)));
|
|
|
|
/**
|
|
* Check that paging can be supported
|
|
*
|
|
* @ret supported Paging can be supported on this CPU
|
|
*/
|
|
static int paging_supported (void)
|
|
{
|
|
uint32_t eax;
|
|
uint32_t ebx;
|
|
uint32_t ecx;
|
|
uint32_t edx;
|
|
|
|
/* Get CPU features */
|
|
__asm__ ("cpuid"
|
|
: "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx)
|
|
: "0" (CPUID_FEATURES));
|
|
|
|
return (edx & CPUID_FEATURE_EDX_PAE);
|
|
}
|
|
|
|
/**
|
|
* Map 2MB page directory entry containing address
|
|
*
|
|
* @v vaddr Virtual address
|
|
* @v paddr Physical address
|
|
*/
|
|
static void map_page (uint32_t vaddr, uint64_t paddr)
|
|
{
|
|
char *byte = ((char *) (intptr_t) vaddr);
|
|
unsigned int index;
|
|
|
|
/* Sanity checks */
|
|
assert ((vaddr & (PAGE_SIZE_2MB - 1)) == 0);
|
|
assert ((paddr & (PAGE_SIZE_2MB - 1)) == 0);
|
|
|
|
/* Populate page directory entry */
|
|
index = (vaddr / PAGE_SIZE_2MB);
|
|
pd[index] = (paddr | PG_P | PG_RW | PG_US | PG_PS);
|
|
|
|
/* Invalidate TLB */
|
|
__asm__ __volatile__ ("invlpg %0" : : "m" (*byte));
|
|
}
|
|
|
|
/**
|
|
* Initialise paging
|
|
*
|
|
*/
|
|
void init_paging (void)
|
|
{
|
|
uint32_t addr;
|
|
unsigned int i;
|
|
|
|
/* Check for PAE */
|
|
if (! paging_supported())
|
|
{
|
|
DBG ("Paging not possible on this CPU\n");
|
|
return;
|
|
}
|
|
|
|
/* Initialise page directory entries */
|
|
addr = 0;
|
|
do
|
|
{
|
|
map_page (addr, addr);
|
|
addr += PAGE_SIZE_2MB;
|
|
} while (addr);
|
|
|
|
/* Initialise page directory pointer table */
|
|
for (i = 0 ; i < (sizeof (pdpt) / sizeof (pdpt[0])) ; i++)
|
|
{
|
|
addr = ((intptr_t) &pd[ i * PAGE_SIZE / sizeof (pd[0]) ]);
|
|
pdpt[i] = (addr | PG_P);
|
|
}
|
|
|
|
/* Mark paging as available */
|
|
paging = 1;
|
|
}
|
|
|
|
/**
|
|
* Enable paging
|
|
*
|
|
* @v state Saved paging state to fill in
|
|
*/
|
|
void enable_paging (struct paging_state *state)
|
|
{
|
|
unsigned long cr0;
|
|
unsigned long cr3;
|
|
unsigned long cr4;
|
|
|
|
/* Do nothing if paging is unavailable */
|
|
if (! paging)
|
|
return;
|
|
|
|
/* Save paging state */
|
|
__asm__ __volatile__ ("mov %%cr0, %0\n\t"
|
|
"mov %%cr3, %1\n\t"
|
|
"mov %%cr4, %2\n\t"
|
|
: "=r" (cr0), "=r" (cr3), "=r" (cr4));
|
|
state->cr0 = cr0;
|
|
state->cr3 = cr3;
|
|
state->cr4 = cr4;
|
|
|
|
/* Disable any existing paging */
|
|
__asm__ __volatile__ ("mov %0, %%cr0" : : "r" (cr0 & ~CR0_PG));
|
|
|
|
/* Enable PAE */
|
|
__asm__ __volatile__ ("mov %0, %%cr4" : : "r" (cr4 | CR4_PAE));
|
|
|
|
/* Load page directory pointer table */
|
|
__asm__ __volatile__ ("mov %0, %%cr3" : : "r" (pdpt));
|
|
|
|
/* Enable paging */
|
|
__asm__ __volatile__ ("mov %0, %%cr0" : : "r" (cr0 | CR0_PG));
|
|
}
|
|
|
|
/**
|
|
* Disable paging
|
|
*
|
|
* @v state Previously saved paging state
|
|
*/
|
|
void disable_paging (struct paging_state *state)
|
|
{
|
|
unsigned long cr0 = state->cr0;
|
|
unsigned long cr3 = state->cr3;
|
|
unsigned long cr4 = state->cr4;
|
|
|
|
/* Do nothing if paging is unavailable */
|
|
if (! paging)
|
|
return;
|
|
|
|
/* Disable paging */
|
|
__asm__ __volatile__ ("mov %0, %%cr0" : : "r" (cr0 & ~CR0_PG));
|
|
|
|
/* Restore saved paging state */
|
|
__asm__ __volatile__ ("mov %2, %%cr4\n\t"
|
|
"mov %1, %%cr3\n\t"
|
|
"mov %0, %%cr0\n\t"
|
|
: : "r" (cr0), "r" (cr3), "r" (cr4));
|
|
}
|
|
|
|
/**
|
|
* Relocate data out of 32-bit address space, if possible
|
|
*
|
|
* @v data Start of data
|
|
* @v len Length of data
|
|
* @ret start Physical start address
|
|
*/
|
|
uint64_t relocate_memory_high (void *data, size_t len)
|
|
{
|
|
intptr_t end = (((intptr_t) data) + len);
|
|
struct e820_entry *e820 = NULL;
|
|
uint64_t start;
|
|
uint64_t dest;
|
|
size_t offset;
|
|
size_t frag_len;
|
|
|
|
/* Do nothing if paging is unavailable */
|
|
if (! paging)
|
|
return ((intptr_t) data);
|
|
|
|
/* Read system memory map */
|
|
while ((e820 = memmap_next (e820)) != NULL)
|
|
{
|
|
|
|
/* Find highest compatible placement within this region */
|
|
start = (e820->start + e820->len);
|
|
if (start < ADDR_4GB)
|
|
continue;
|
|
start = (((start - end) & ~(PAGE_SIZE_2MB - 1)) + end);
|
|
start -= len;
|
|
if (start < e820->start)
|
|
continue;
|
|
if (start < ADDR_4GB)
|
|
continue;
|
|
|
|
/* Relocate to this region */
|
|
dest = start;
|
|
while (len)
|
|
{
|
|
|
|
/* Calculate length within this 2MB page */
|
|
offset = (((intptr_t) data) &
|
|
(PAGE_SIZE_2MB - 1));
|
|
frag_len = (PAGE_SIZE_2MB - offset);
|
|
if (frag_len > len)
|
|
frag_len = len;
|
|
|
|
/* Map copy window to destination */
|
|
map_page (COPY_WINDOW,
|
|
(dest & ~(PAGE_SIZE_2MB - 1)));
|
|
|
|
/* Copy data through copy window */
|
|
memcpy ((((void *) COPY_WINDOW) + offset),
|
|
data, frag_len);
|
|
|
|
/* Map original page to destination */
|
|
map_page ((((intptr_t) data) - offset),
|
|
(dest & ~(PAGE_SIZE_2MB - 1)));
|
|
|
|
/* Move to next 2MB page */
|
|
data += frag_len;
|
|
dest += frag_len;
|
|
len -= frag_len;
|
|
}
|
|
|
|
/* Remap copy window */
|
|
map_page (COPY_WINDOW, COPY_WINDOW);
|
|
|
|
return start;
|
|
}
|
|
|
|
/* Leave at original location */
|
|
return ((intptr_t) data);
|
|
}
|
|
|
|
#endif /* defined(__i386__) || defined(__x86_64__) */
|