summaryrefslogtreecommitdiffstats
path: root/sys/boot/ia64/common/copy.c
diff options
context:
space:
mode:
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