1/*-
2 * Copyright (c) 2006 Marcel Moolenaar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include <stand.h>
31#include <machine/param.h>
32#include <machine/pte.h>
33
34#include "libia64.h"
35
36u_int ia64_legacy_kernel;
37
38uint64_t *ia64_pgtbl;
39uint32_t ia64_pgtblsz;
40
41static int
42pgtbl_extend(u_int idx)
43{
44	vm_paddr_t pa;
45	uint64_t *pgtbl;
46	uint32_t pgtblsz;
47	u_int pot;
48
49	pgtblsz = (idx + 1) << 3;
50
51	/* The minimum size is 4KB. */
52	if (pgtblsz < 4096)
53		pgtblsz = 4096;
54
55	/* Find the next higher power of 2. */
56	pgtblsz--;
57	for (pot = 1; pot < 32; pot <<= 1)
58		pgtblsz = pgtblsz | (pgtblsz >> pot);
59	pgtblsz++;
60
61	/* The maximum size is 1MB. */
62	if (pgtblsz > 1048576)
63		return (ENOMEM);
64
65	/* Make sure the size is a valid (mappable) page size. */
66	if (pgtblsz == 32*1024 || pgtblsz == 128*1024 || pgtblsz == 512*1024)
67		pgtblsz <<= 1;
68
69	/* Allocate naturally aligned memory. */
70	pa = ia64_platform_alloc(0, pgtblsz);
71	if (pa == ~0UL)
72		return (ENOMEM);
73	pgtbl = (void *)pa;
74
75	/* Initialize new page table. */
76	if (ia64_pgtbl != NULL && ia64_pgtbl != pgtbl)
77		bcopy(ia64_pgtbl, pgtbl, ia64_pgtblsz);
78	bzero(pgtbl + (ia64_pgtblsz >> 3), pgtblsz - ia64_pgtblsz);
79
80	if (ia64_pgtbl != NULL && ia64_pgtbl != pgtbl)
81		ia64_platform_free(0, (uintptr_t)ia64_pgtbl, ia64_pgtblsz);
82
83	ia64_pgtbl = pgtbl;
84	ia64_pgtblsz = pgtblsz;
85	return (0);
86}
87
88void *
89ia64_va2pa(vm_offset_t va, size_t *len)
90{
91	uint64_t pa, pte;
92	u_int idx, ofs;
93	int error;
94
95	/* Backward compatibility. */
96	if (va >= IA64_RR_BASE(7)) {
97		ia64_legacy_kernel = 1;
98		pa = IA64_RR_MASK(va);
99		return ((void *)pa);
100	}
101
102	if (va < IA64_PBVM_BASE) {
103		error = EINVAL;
104		goto fail;
105	}
106
107	ia64_legacy_kernel = 0;
108
109	idx = (va - IA64_PBVM_BASE) >> IA64_PBVM_PAGE_SHIFT;
110	if (idx >= (ia64_pgtblsz >> 3)) {
111		error = pgtbl_extend(idx);
112		if (error)
113			goto fail;
114	}
115
116	ofs = va & IA64_PBVM_PAGE_MASK;
117	pte = ia64_pgtbl[idx];
118	if ((pte & PTE_PRESENT) == 0) {
119		pa = ia64_platform_alloc(va - ofs, IA64_PBVM_PAGE_SIZE);
120		if (pa == ~0UL) {
121			error = ENOMEM;
122			goto fail;
123		}
124		pte = PTE_AR_RWX | PTE_DIRTY | PTE_ACCESSED | PTE_PRESENT;
125		pte |= (pa & PTE_PPN_MASK);
126		ia64_pgtbl[idx] = pte;
127	}
128	pa = (pte & PTE_PPN_MASK) + ofs;
129
130	/* We can not cross page boundaries (in general). */
131	if (*len + ofs > IA64_PBVM_PAGE_SIZE)
132		*len = IA64_PBVM_PAGE_SIZE - ofs;
133
134	return ((void *)pa);
135
136 fail:
137	*len = 0;
138	return (NULL);
139}
140
141ssize_t
142ia64_copyin(const void *src, vm_offset_t va, size_t len)
143{
144	void *pa;
145	ssize_t res;
146	size_t sz;
147
148	res = 0;
149	while (len > 0) {
150		sz = len;
151		pa = ia64_va2pa(va, &sz);
152		if (sz == 0)
153			break;
154		bcopy(src, pa, sz);
155		len -= sz;
156		res += sz;
157		va += sz;
158	}
159	return (res);
160}
161
162ssize_t
163ia64_copyout(vm_offset_t va, void *dst, size_t len)
164{
165	void *pa;
166	ssize_t res;
167	size_t sz;
168
169	res = 0;
170	while (len > 0) {
171		sz = len;
172		pa = ia64_va2pa(va, &sz);
173		if (sz == 0)
174			break;
175		bcopy(pa, dst, sz);
176		len -= sz;
177		res += sz;
178		va += sz;
179	}
180	return (res);
181}
182
183uint64_t
184ia64_loadaddr(u_int type, void *data, uint64_t addr)
185{
186	uint64_t align;
187
188	/*
189	 * Align ELF objects at PBVM page boundaries.  Align all other
190	 * objects at cache line boundaries for good measure.
191	 */
192	align = (type == LOAD_ELF) ? IA64_PBVM_PAGE_SIZE : CACHE_LINE_SIZE;
193	return ((addr + align - 1) & ~(align - 1));
194}
195
196ssize_t
197ia64_readin(int fd, vm_offset_t va, size_t len)
198{
199	void *pa;
200	ssize_t res, s;
201	size_t sz;
202
203	res = 0;
204	while (len > 0) {
205		sz = len;
206		pa = ia64_va2pa(va, &sz);
207		if (sz == 0)
208			break;
209		s = read(fd, pa, sz);
210		if (s <= 0)
211			break;
212		len -= s;
213		res += s;
214		va += s;
215	}
216	return (res);
217}
218