177943Sdfr/*- 2164010Smarcel * Copyright (c) 2006 Marcel Moolenaar 377943Sdfr * All rights reserved. 477943Sdfr * 577943Sdfr * Redistribution and use in source and binary forms, with or without 677943Sdfr * modification, are permitted provided that the following conditions 777943Sdfr * are met: 8164010Smarcel * 977943Sdfr * 1. Redistributions of source code must retain the above copyright 1077943Sdfr * notice, this list of conditions and the following disclaimer. 1177943Sdfr * 2. Redistributions in binary form must reproduce the above copyright 1277943Sdfr * notice, this list of conditions and the following disclaimer in the 1377943Sdfr * documentation and/or other materials provided with the distribution. 1477943Sdfr * 15164010Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16164010Smarcel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17164010Smarcel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18164010Smarcel * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19164010Smarcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20164010Smarcel * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21164010Smarcel * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22164010Smarcel * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23164010Smarcel * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24164010Smarcel * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2577943Sdfr */ 2678320Sobrien 27113038Sobrien#include <sys/cdefs.h> 28113038Sobrien__FBSDID("$FreeBSD: releng/10.3/sys/boot/ia64/common/copy.c 221269 2011-04-30 20:16:49Z marcel $"); 2978320Sobrien 30138141Smarcel#include <stand.h> 31220313Smarcel#include <machine/param.h> 32221269Smarcel#include <machine/pte.h> 3377943Sdfr 34164010Smarcel#include "libia64.h" 35164010Smarcel 36220313Smarcelu_int ia64_legacy_kernel; 37220313Smarcel 38219691Smarceluint64_t *ia64_pgtbl; 39219691Smarceluint32_t ia64_pgtblsz; 40219691Smarcel 41219691Smarcelstatic int 42219691Smarcelpgtbl_extend(u_int idx) 43219691Smarcel{ 44221269Smarcel vm_paddr_t pa; 45219691Smarcel uint64_t *pgtbl; 46219691Smarcel uint32_t pgtblsz; 47219691Smarcel u_int pot; 48219691Smarcel 49219691Smarcel pgtblsz = (idx + 1) << 3; 50219691Smarcel 51219691Smarcel /* The minimum size is 4KB. */ 52219691Smarcel if (pgtblsz < 4096) 53219691Smarcel pgtblsz = 4096; 54219691Smarcel 55219691Smarcel /* Find the next higher power of 2. */ 56219691Smarcel pgtblsz--; 57219691Smarcel for (pot = 1; pot < 32; pot <<= 1) 58219691Smarcel pgtblsz = pgtblsz | (pgtblsz >> pot); 59219691Smarcel pgtblsz++; 60219691Smarcel 61219691Smarcel /* The maximum size is 1MB. */ 62219691Smarcel if (pgtblsz > 1048576) 63219691Smarcel return (ENOMEM); 64219691Smarcel 65219691Smarcel /* Make sure the size is a valid (mappable) page size. */ 66219691Smarcel if (pgtblsz == 32*1024 || pgtblsz == 128*1024 || pgtblsz == 512*1024) 67219691Smarcel pgtblsz <<= 1; 68219691Smarcel 69219691Smarcel /* Allocate naturally aligned memory. */ 70221269Smarcel pa = ia64_platform_alloc(0, pgtblsz); 71221269Smarcel if (pa == ~0UL) 72219691Smarcel return (ENOMEM); 73221269Smarcel pgtbl = (void *)pa; 74219691Smarcel 75219691Smarcel /* Initialize new page table. */ 76219691Smarcel if (ia64_pgtbl != NULL && ia64_pgtbl != pgtbl) 77219691Smarcel bcopy(ia64_pgtbl, pgtbl, ia64_pgtblsz); 78219691Smarcel bzero(pgtbl + (ia64_pgtblsz >> 3), pgtblsz - ia64_pgtblsz); 79219691Smarcel 80219691Smarcel if (ia64_pgtbl != NULL && ia64_pgtbl != pgtbl) 81219691Smarcel ia64_platform_free(0, (uintptr_t)ia64_pgtbl, ia64_pgtblsz); 82219691Smarcel 83219691Smarcel ia64_pgtbl = pgtbl; 84219691Smarcel ia64_pgtblsz = pgtblsz; 85219691Smarcel return (0); 86219691Smarcel} 87219691Smarcel 88220313Smarcelvoid * 89220313Smarcelia64_va2pa(vm_offset_t va, size_t *len) 9077943Sdfr{ 91221269Smarcel uint64_t pa, pte; 92219691Smarcel u_int idx, ofs; 93219691Smarcel int error; 94138141Smarcel 95219691Smarcel /* Backward compatibility. */ 96164010Smarcel if (va >= IA64_RR_BASE(7)) { 97220313Smarcel ia64_legacy_kernel = 1; 98164010Smarcel pa = IA64_RR_MASK(va); 99164010Smarcel return ((void *)pa); 100164010Smarcel } 101164010Smarcel 102219691Smarcel if (va < IA64_PBVM_BASE) { 103219691Smarcel error = EINVAL; 104219691Smarcel goto fail; 105219691Smarcel } 106219691Smarcel 107220313Smarcel ia64_legacy_kernel = 0; 108220313Smarcel 109219691Smarcel idx = (va - IA64_PBVM_BASE) >> IA64_PBVM_PAGE_SHIFT; 110219691Smarcel if (idx >= (ia64_pgtblsz >> 3)) { 111219691Smarcel error = pgtbl_extend(idx); 112219691Smarcel if (error) 113219691Smarcel goto fail; 114219691Smarcel } 115219691Smarcel 116219691Smarcel ofs = va & IA64_PBVM_PAGE_MASK; 117221269Smarcel pte = ia64_pgtbl[idx]; 118221269Smarcel if ((pte & PTE_PRESENT) == 0) { 119219691Smarcel pa = ia64_platform_alloc(va - ofs, IA64_PBVM_PAGE_SIZE); 120221269Smarcel if (pa == ~0UL) { 121219691Smarcel error = ENOMEM; 122219691Smarcel goto fail; 123219691Smarcel } 124221269Smarcel pte = PTE_AR_RWX | PTE_DIRTY | PTE_ACCESSED | PTE_PRESENT; 125221269Smarcel pte |= (pa & PTE_PPN_MASK); 126221269Smarcel ia64_pgtbl[idx] = pte; 127219691Smarcel } 128221269Smarcel pa = (pte & PTE_PPN_MASK) + ofs; 129219691Smarcel 130219691Smarcel /* We can not cross page boundaries (in general). */ 131219691Smarcel if (*len + ofs > IA64_PBVM_PAGE_SIZE) 132219691Smarcel *len = IA64_PBVM_PAGE_SIZE - ofs; 133219691Smarcel 134219691Smarcel return ((void *)pa); 135219691Smarcel 136219691Smarcel fail: 137164010Smarcel *len = 0; 138164010Smarcel return (NULL); 13977943Sdfr} 14077943Sdfr 141164010Smarcelssize_t 142164010Smarcelia64_copyin(const void *src, vm_offset_t va, size_t len) 14377943Sdfr{ 144164010Smarcel void *pa; 145164010Smarcel ssize_t res; 146164010Smarcel size_t sz; 147138141Smarcel 148164010Smarcel res = 0; 149164010Smarcel while (len > 0) { 150164010Smarcel sz = len; 151220313Smarcel pa = ia64_va2pa(va, &sz); 152164010Smarcel if (sz == 0) 153164010Smarcel break; 154164010Smarcel bcopy(src, pa, sz); 155164010Smarcel len -= sz; 156164010Smarcel res += sz; 157164010Smarcel va += sz; 158164010Smarcel } 159164010Smarcel return (res); 16077943Sdfr} 16177943Sdfr 162164010Smarcelssize_t 163164010Smarcelia64_copyout(vm_offset_t va, void *dst, size_t len) 16477943Sdfr{ 165164010Smarcel void *pa; 166164010Smarcel ssize_t res; 167164010Smarcel size_t sz; 168138141Smarcel 169164010Smarcel res = 0; 170164010Smarcel while (len > 0) { 171164010Smarcel sz = len; 172220313Smarcel pa = ia64_va2pa(va, &sz); 173164010Smarcel if (sz == 0) 174164010Smarcel break; 175164010Smarcel bcopy(pa, dst, sz); 176164010Smarcel len -= sz; 177164010Smarcel res += sz; 178164010Smarcel va += sz; 179164010Smarcel } 180164010Smarcel return (res); 18177943Sdfr} 182164010Smarcel 183220313Smarceluint64_t 184220313Smarcelia64_loadaddr(u_int type, void *data, uint64_t addr) 185220313Smarcel{ 186220313Smarcel uint64_t align; 187220313Smarcel 188220313Smarcel /* 189220313Smarcel * Align ELF objects at PBVM page boundaries. Align all other 190220313Smarcel * objects at cache line boundaries for good measure. 191220313Smarcel */ 192220313Smarcel align = (type == LOAD_ELF) ? IA64_PBVM_PAGE_SIZE : CACHE_LINE_SIZE; 193220313Smarcel return ((addr + align - 1) & ~(align - 1)); 194220313Smarcel} 195220313Smarcel 196164010Smarcelssize_t 197164010Smarcelia64_readin(int fd, vm_offset_t va, size_t len) 198164010Smarcel{ 199164010Smarcel void *pa; 200164010Smarcel ssize_t res, s; 201164010Smarcel size_t sz; 202164010Smarcel 203164010Smarcel res = 0; 204164010Smarcel while (len > 0) { 205164010Smarcel sz = len; 206220313Smarcel pa = ia64_va2pa(va, &sz); 207164010Smarcel if (sz == 0) 208164010Smarcel break; 209164010Smarcel s = read(fd, pa, sz); 210164010Smarcel if (s <= 0) 211164010Smarcel break; 212164010Smarcel len -= s; 213164010Smarcel res += s; 214164010Smarcel va += s; 215164010Smarcel } 216164010Smarcel return (res); 217164010Smarcel} 218