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