copy.c revision 220313
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: head/sys/boot/ia64/common/copy.c 220313 2011-04-03 23:49:20Z marcel $");
2978320Sobrien
30138141Smarcel#include <stand.h>
31220313Smarcel#include <machine/param.h>
3277943Sdfr
33164010Smarcel#include "libia64.h"
34164010Smarcel
35220313Smarcelu_int ia64_legacy_kernel;
36220313Smarcel
37219691Smarceluint64_t *ia64_pgtbl;
38219691Smarceluint32_t ia64_pgtblsz;
39219691Smarcel
40219691Smarcelstatic int
41219691Smarcelpgtbl_extend(u_int idx)
42219691Smarcel{
43219691Smarcel	uint64_t *pgtbl;
44219691Smarcel	uint32_t pgtblsz;
45219691Smarcel	u_int pot;
46219691Smarcel
47219691Smarcel	pgtblsz = (idx + 1) << 3;
48219691Smarcel
49219691Smarcel	/* The minimum size is 4KB. */
50219691Smarcel	if (pgtblsz < 4096)
51219691Smarcel		pgtblsz = 4096;
52219691Smarcel
53219691Smarcel	/* Find the next higher power of 2. */
54219691Smarcel	pgtblsz--;
55219691Smarcel	for (pot = 1; pot < 32; pot <<= 1)
56219691Smarcel		pgtblsz = pgtblsz | (pgtblsz >> pot);
57219691Smarcel	pgtblsz++;
58219691Smarcel
59219691Smarcel	/* The maximum size is 1MB. */
60219691Smarcel	if (pgtblsz > 1048576)
61219691Smarcel		return (ENOMEM);
62219691Smarcel
63219691Smarcel	/* Make sure the size is a valid (mappable) page size. */
64219691Smarcel	if (pgtblsz == 32*1024 || pgtblsz == 128*1024 || pgtblsz == 512*1024)
65219691Smarcel		pgtblsz <<= 1;
66219691Smarcel
67219691Smarcel	/* Allocate naturally aligned memory. */
68219691Smarcel	pgtbl = (void *)ia64_platform_alloc(0, pgtblsz);
69219691Smarcel	if (pgtbl == NULL)
70219691Smarcel		return (ENOMEM);
71219691Smarcel
72219691Smarcel	/* Initialize new page table. */
73219691Smarcel	if (ia64_pgtbl != NULL && ia64_pgtbl != pgtbl)
74219691Smarcel		bcopy(ia64_pgtbl, pgtbl, ia64_pgtblsz);
75219691Smarcel	bzero(pgtbl + (ia64_pgtblsz >> 3), pgtblsz - ia64_pgtblsz);
76219691Smarcel
77219691Smarcel	if (ia64_pgtbl != NULL && ia64_pgtbl != pgtbl)
78219691Smarcel		ia64_platform_free(0, (uintptr_t)ia64_pgtbl, ia64_pgtblsz);
79219691Smarcel
80219691Smarcel	ia64_pgtbl = pgtbl;
81219691Smarcel	ia64_pgtblsz = pgtblsz;
82219691Smarcel	return (0);
83219691Smarcel}
84219691Smarcel
85220313Smarcelvoid *
86220313Smarcelia64_va2pa(vm_offset_t va, size_t *len)
8777943Sdfr{
88164010Smarcel	uint64_t pa;
89219691Smarcel	u_int idx, ofs;
90219691Smarcel	int error;
91138141Smarcel
92219691Smarcel	/* Backward compatibility. */
93164010Smarcel	if (va >= IA64_RR_BASE(7)) {
94220313Smarcel		ia64_legacy_kernel = 1;
95164010Smarcel		pa = IA64_RR_MASK(va);
96164010Smarcel		return ((void *)pa);
97164010Smarcel	}
98164010Smarcel
99219691Smarcel	if (va < IA64_PBVM_BASE) {
100219691Smarcel		error = EINVAL;
101219691Smarcel		goto fail;
102219691Smarcel	}
103219691Smarcel
104220313Smarcel	ia64_legacy_kernel = 0;
105220313Smarcel
106219691Smarcel	idx = (va - IA64_PBVM_BASE) >> IA64_PBVM_PAGE_SHIFT;
107219691Smarcel	if (idx >= (ia64_pgtblsz >> 3)) {
108219691Smarcel		error = pgtbl_extend(idx);
109219691Smarcel		if (error)
110219691Smarcel			goto fail;
111219691Smarcel	}
112219691Smarcel
113219691Smarcel	ofs = va & IA64_PBVM_PAGE_MASK;
114219691Smarcel	pa = ia64_pgtbl[idx];
115219691Smarcel	if (pa == 0) {
116219691Smarcel		pa = ia64_platform_alloc(va - ofs, IA64_PBVM_PAGE_SIZE);
117219691Smarcel		if (pa == 0) {
118219691Smarcel			error = ENOMEM;
119219691Smarcel			goto fail;
120219691Smarcel		}
121219691Smarcel		ia64_pgtbl[idx] = pa;
122219691Smarcel	}
123219691Smarcel	pa += ofs;
124219691Smarcel
125219691Smarcel	/* We can not cross page boundaries (in general). */
126219691Smarcel	if (*len + ofs > IA64_PBVM_PAGE_SIZE)
127219691Smarcel		*len = IA64_PBVM_PAGE_SIZE - ofs;
128219691Smarcel
129219691Smarcel	return ((void *)pa);
130219691Smarcel
131219691Smarcel fail:
132164010Smarcel	*len = 0;
133164010Smarcel	return (NULL);
13477943Sdfr}
13577943Sdfr
136164010Smarcelssize_t
137164010Smarcelia64_copyin(const void *src, vm_offset_t va, size_t len)
13877943Sdfr{
139164010Smarcel	void *pa;
140164010Smarcel	ssize_t res;
141164010Smarcel	size_t sz;
142138141Smarcel
143164010Smarcel	res = 0;
144164010Smarcel	while (len > 0) {
145164010Smarcel		sz = len;
146220313Smarcel		pa = ia64_va2pa(va, &sz);
147164010Smarcel		if (sz == 0)
148164010Smarcel			break;
149164010Smarcel		bcopy(src, pa, sz);
150164010Smarcel		len -= sz;
151164010Smarcel		res += sz;
152164010Smarcel		va += sz;
153164010Smarcel	}
154164010Smarcel	return (res);
15577943Sdfr}
15677943Sdfr
157164010Smarcelssize_t
158164010Smarcelia64_copyout(vm_offset_t va, void *dst, size_t len)
15977943Sdfr{
160164010Smarcel	void *pa;
161164010Smarcel	ssize_t res;
162164010Smarcel	size_t sz;
163138141Smarcel
164164010Smarcel	res = 0;
165164010Smarcel	while (len > 0) {
166164010Smarcel		sz = len;
167220313Smarcel		pa = ia64_va2pa(va, &sz);
168164010Smarcel		if (sz == 0)
169164010Smarcel			break;
170164010Smarcel		bcopy(pa, dst, sz);
171164010Smarcel		len -= sz;
172164010Smarcel		res += sz;
173164010Smarcel		va += sz;
174164010Smarcel	}
175164010Smarcel	return (res);
17677943Sdfr}
177164010Smarcel
178220313Smarceluint64_t
179220313Smarcelia64_loadaddr(u_int type, void *data, uint64_t addr)
180220313Smarcel{
181220313Smarcel	uint64_t align;
182220313Smarcel
183220313Smarcel	/*
184220313Smarcel	 * Align ELF objects at PBVM page boundaries.  Align all other
185220313Smarcel	 * objects at cache line boundaries for good measure.
186220313Smarcel	 */
187220313Smarcel	align = (type == LOAD_ELF) ? IA64_PBVM_PAGE_SIZE : CACHE_LINE_SIZE;
188220313Smarcel	return ((addr + align - 1) & ~(align - 1));
189220313Smarcel}
190220313Smarcel
191164010Smarcelssize_t
192164010Smarcelia64_readin(int fd, vm_offset_t va, size_t len)
193164010Smarcel{
194164010Smarcel	void *pa;
195164010Smarcel	ssize_t res, s;
196164010Smarcel	size_t sz;
197164010Smarcel
198164010Smarcel	res = 0;
199164010Smarcel	while (len > 0) {
200164010Smarcel		sz = len;
201220313Smarcel		pa = ia64_va2pa(va, &sz);
202164010Smarcel		if (sz == 0)
203164010Smarcel			break;
204164010Smarcel		s = read(fd, pa, sz);
205164010Smarcel		if (s <= 0)
206164010Smarcel			break;
207164010Smarcel		len -= s;
208164010Smarcel		res += s;
209164010Smarcel		va += s;
210164010Smarcel	}
211164010Smarcel	return (res);
212164010Smarcel}
213