summaryrefslogtreecommitdiffstats
path: root/sys/boot/ia64/common/copy.c
diff options
context:
space:
mode:
authormarcel <marcel@FreeBSD.org>2011-03-16 03:53:18 +0000
committermarcel <marcel@FreeBSD.org>2011-03-16 03:53:18 +0000
commit8e0b0a22844d3d1383011c024b1eb1a3b2ee51a8 (patch)
tree55215f77e655236a449e347f8f98d98dfb718eed /sys/boot/ia64/common/copy.c
parente79931d6175d1a1999786ee313a144784de8d6a9 (diff)
downloadFreeBSD-src-8e0b0a22844d3d1383011c024b1eb1a3b2ee51a8.zip
FreeBSD-src-8e0b0a22844d3d1383011c024b1eb1a3b2ee51a8.tar.gz
MFaltix:
Add support for Pre-Boot Virtual Memory (PBVM) to the loader. PBVM allows us to link the kernel at a fixed virtual address without having to make any assumptions about the physical memory layout. On the SGI Altix 350 for example, there's no usuable physical memory below 192GB. Also, the PBVM allows us to control better where we're going to physically load the kernel and its modules so that we can make sure we load the kernel in memory that's close to the BSP. The PBVM is managed by a simple page table. The minimum size of the page table is 4KB (EFI page size) and the maximum is currently set to 1MB. A page in the PBVM is 64KB, as that's the maximum alignment one can specify in a linker script. The bottom line is that PBVM is between 64KB and 8GB in size. The loader maps the PBVM page table at a fixed virtual address and using a single translations. The PBVM itself is also mapped using a single translation for a maximum of 32MB. While here, increase the heap in the EFI loader from 512KB to 2MB and set the stage for supporting relocatable modules.
Diffstat (limited to 'sys/boot/ia64/common/copy.c')
-rw-r--r--sys/boot/ia64/common/copy.c83
1 files changed, 82 insertions, 1 deletions
diff --git a/sys/boot/ia64/common/copy.c b/sys/boot/ia64/common/copy.c
index 8d8dab6..93ef77b 100644
--- a/sys/boot/ia64/common/copy.c
+++ b/sys/boot/ia64/common/copy.c
@@ -32,17 +32,98 @@ __FBSDID("$FreeBSD$");
#include "libia64.h"
+uint64_t *ia64_pgtbl;
+uint32_t ia64_pgtblsz;
+
+static int
+pgtbl_extend(u_int idx)
+{
+ uint64_t *pgtbl;
+ uint32_t pgtblsz;
+ u_int pot;
+
+ pgtblsz = (idx + 1) << 3;
+
+ /* The minimum size is 4KB. */
+ if (pgtblsz < 4096)
+ pgtblsz = 4096;
+
+ /* Find the next higher power of 2. */
+ pgtblsz--;
+ for (pot = 1; pot < 32; pot <<= 1)
+ pgtblsz = pgtblsz | (pgtblsz >> pot);
+ pgtblsz++;
+
+ /* The maximum size is 1MB. */
+ if (pgtblsz > 1048576)
+ return (ENOMEM);
+
+ /* Make sure the size is a valid (mappable) page size. */
+ if (pgtblsz == 32*1024 || pgtblsz == 128*1024 || pgtblsz == 512*1024)
+ pgtblsz <<= 1;
+
+ /* Allocate naturally aligned memory. */
+ pgtbl = (void *)ia64_platform_alloc(0, pgtblsz);
+ if (pgtbl == NULL)
+ return (ENOMEM);
+
+ /* Initialize new page table. */
+ if (ia64_pgtbl != NULL && ia64_pgtbl != pgtbl)
+ bcopy(ia64_pgtbl, pgtbl, ia64_pgtblsz);
+ bzero(pgtbl + (ia64_pgtblsz >> 3), pgtblsz - ia64_pgtblsz);
+
+ if (ia64_pgtbl != NULL && ia64_pgtbl != pgtbl)
+ ia64_platform_free(0, (uintptr_t)ia64_pgtbl, ia64_pgtblsz);
+
+ ia64_pgtbl = pgtbl;
+ ia64_pgtblsz = pgtblsz;
+ return (0);
+}
+
static void *
va2pa(vm_offset_t va, size_t *len)
{
uint64_t pa;
+ u_int idx, ofs;
+ int error;
+ /* Backward compatibility. */
if (va >= IA64_RR_BASE(7)) {
pa = IA64_RR_MASK(va);
return ((void *)pa);
}
- printf("\n%s: va=%lx, *len=%lx\n", __func__, va, *len);
+ if (va < IA64_PBVM_BASE) {
+ error = EINVAL;
+ goto fail;
+ }
+
+ idx = (va - IA64_PBVM_BASE) >> IA64_PBVM_PAGE_SHIFT;
+ if (idx >= (ia64_pgtblsz >> 3)) {
+ error = pgtbl_extend(idx);
+ if (error)
+ goto fail;
+ }
+
+ ofs = va & IA64_PBVM_PAGE_MASK;
+ pa = ia64_pgtbl[idx];
+ if (pa == 0) {
+ pa = ia64_platform_alloc(va - ofs, IA64_PBVM_PAGE_SIZE);
+ if (pa == 0) {
+ error = ENOMEM;
+ goto fail;
+ }
+ ia64_pgtbl[idx] = pa;
+ }
+ pa += ofs;
+
+ /* We can not cross page boundaries (in general). */
+ if (*len + ofs > IA64_PBVM_PAGE_SIZE)
+ *len = IA64_PBVM_PAGE_SIZE - ofs;
+
+ return ((void *)pa);
+
+ fail:
*len = 0;
return (NULL);
}
OpenPOWER on IntegriCloud