1/*-
2 * Copyright (c) 2012 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 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32
33#include <machine/md_var.h>
34#include <machine/vmparam.h>
35
36static u_int phys_avail_segs;
37
38vm_paddr_t phys_avail[2 * VM_PHYSSEG_MAX + 2];
39
40vm_paddr_t paddr_max;
41
42long realmem;
43
44static u_int
45ia64_physmem_find(vm_paddr_t base, vm_paddr_t lim)
46{
47	u_int idx;
48
49	for (idx = 0; phys_avail[idx + 1] != 0; idx += 2) {
50		if (phys_avail[idx] >= lim ||
51		    phys_avail[idx + 1] > base)
52			break;
53	}
54	return (idx);
55}
56
57static int
58ia64_physmem_insert(u_int idx, vm_paddr_t base, vm_paddr_t lim)
59{
60	u_int ridx;
61
62	if (phys_avail_segs == VM_PHYSSEG_MAX)
63		return (ENOMEM);
64
65	ridx = phys_avail_segs * 2;
66	while (idx < ridx) {
67		phys_avail[ridx + 1] = phys_avail[ridx - 1];
68		phys_avail[ridx] = phys_avail[ridx - 2];
69		ridx -= 2;
70	}
71	phys_avail[idx] = base;
72	phys_avail[idx + 1] = lim;
73	phys_avail_segs++;
74	return (0);
75}
76
77static int
78ia64_physmem_remove(u_int idx)
79{
80
81	if (phys_avail_segs == 0)
82		return (ENOENT);
83	do {
84		phys_avail[idx] = phys_avail[idx + 2];
85		phys_avail[idx + 1] = phys_avail[idx + 3];
86		idx += 2;
87	} while (phys_avail[idx + 1] != 0);
88	phys_avail_segs--;
89	return (0);
90}
91
92int
93ia64_physmem_add(vm_paddr_t base, vm_size_t len)
94{
95	vm_paddr_t lim;
96	u_int idx;
97
98	realmem += len;
99
100	lim = base + len;
101	idx = ia64_physmem_find(base, lim);
102	if (phys_avail[idx] == lim) {
103		phys_avail[idx] = base;
104		return (0);
105	}
106	if (idx > 0 && phys_avail[idx - 1] == base) {
107		phys_avail[idx - 1] = lim;
108		return (0);
109	}
110	return (ia64_physmem_insert(idx, base, lim));
111}
112
113int
114ia64_physmem_delete(vm_paddr_t base, vm_size_t len)
115{
116	vm_paddr_t lim;
117	u_int idx;
118
119	lim = base + len;
120	idx = ia64_physmem_find(base, lim);
121	if (phys_avail[idx] >= lim || phys_avail[idx + 1] == 0)
122		return (ENOENT);
123	if (phys_avail[idx] < base && phys_avail[idx + 1] > lim) {
124		len = phys_avail[idx + 1] - lim;
125		phys_avail[idx + 1] = base;
126		base = lim;
127		lim = base + len;
128		return (ia64_physmem_insert(idx + 2, base, lim));
129	} else {
130		if (phys_avail[idx] == base)
131			phys_avail[idx] = lim;
132		if (phys_avail[idx + 1] == lim)
133			phys_avail[idx + 1] = base;
134		if (phys_avail[idx] >= phys_avail[idx + 1])
135			return (ia64_physmem_remove(idx));
136	}
137	return (0);
138}
139
140int
141ia64_physmem_fini(void)
142{
143	vm_paddr_t base, lim, size;
144	u_int idx;
145
146	idx = 0;
147	while (phys_avail[idx + 1] != 0) {
148		base = round_page(phys_avail[idx]);
149		lim = trunc_page(phys_avail[idx + 1]);
150		if (base < lim) {
151			phys_avail[idx] = base;
152			phys_avail[idx + 1] = lim;
153			size = lim - base;
154			physmem += atop(size);
155			paddr_max = lim;
156			idx += 2;
157		} else
158			ia64_physmem_remove(idx);
159	}
160
161	/*
162	 * Round realmem to a multple of 128MB. Hopefully that compensates
163	 * for any loss of DRAM that isn't accounted for in the memory map.
164	 * I'm thinking legacy BIOS or VGA here. In any case, it's ok if
165	 * we got it wrong, because we don't actually use realmem. It's
166	 * just for show...
167	 */
168	size = 1U << 27;
169	realmem = (realmem + size - 1) & ~(size - 1);
170	realmem = atop(realmem);
171	return (0);
172}
173
174int
175ia64_physmem_init(void)
176{
177
178	/* Nothing to do just yet. */
179	return (0);
180}
181
182int
183ia64_physmem_track(vm_paddr_t base, vm_size_t len)
184{
185
186	realmem += len;
187	return (0);
188}
189
190void *
191ia64_physmem_alloc(vm_size_t len, vm_size_t align)
192{
193	vm_paddr_t base, lim, pa;
194	void *ptr;
195	u_int idx;
196
197	if (phys_avail_segs == 0)
198		return (NULL);
199
200	len = round_page(len);
201
202	/*
203	 * Try and allocate with least effort.
204	 */
205	idx = phys_avail_segs * 2;
206	while (idx > 0) {
207		idx -= 2;
208		base = phys_avail[idx];
209		lim = phys_avail[idx + 1];
210
211		if (lim - base < len)
212			continue;
213
214		/* First try from the end. */
215		pa = lim - len;
216		if ((pa & (align - 1)) == 0) {
217			if (pa == base)
218				ia64_physmem_remove(idx);
219			else
220				phys_avail[idx + 1] = pa;
221			goto gotit;
222		}
223
224		/* Try from the start next. */
225		pa = base;
226		if ((pa & (align - 1)) == 0) {
227			if (pa + len == lim)
228				ia64_physmem_remove(idx);
229			else
230				phys_avail[idx] += len;
231			goto gotit;
232		}
233	}
234
235	/*
236	 * Find a good segment and split it up.
237	 */
238	idx = phys_avail_segs * 2;
239	while (idx > 0) {
240		idx -= 2;
241		base = phys_avail[idx];
242		lim = phys_avail[idx + 1];
243
244		pa = (base + align - 1) & ~(align - 1);
245		if (pa + len <= lim) {
246			ia64_physmem_delete(pa, len);
247			goto gotit;
248		}
249	}
250
251	/* Out of luck. */
252	return (NULL);
253
254 gotit:
255	ptr = (void *)IA64_PHYS_TO_RR7(pa);
256	bzero(ptr, len);
257	return (ptr);
258}
259