1238184Smarcel/*-
2238184Smarcel * Copyright (c) 2012 Marcel Moolenaar
3238184Smarcel * All rights reserved.
4238184Smarcel *
5238184Smarcel * Redistribution and use in source and binary forms, with or without
6238184Smarcel * modification, are permitted provided that the following conditions
7238184Smarcel * are met:
8238184Smarcel * 1. Redistributions of source code must retain the above copyright
9238184Smarcel *    notice, this list of conditions and the following disclaimer.
10238184Smarcel * 2. Redistributions in binary form must reproduce the above copyright
11238184Smarcel *    notice, this list of conditions and the following disclaimer in the
12238184Smarcel *    documentation and/or other materials provided with the distribution.
13238184Smarcel *
14238184Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15238184Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16238184Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17238184Smarcel * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18238184Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19238184Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20238184Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21238184Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22238184Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23238184Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24238184Smarcel * SUCH DAMAGE.
25238184Smarcel */
26238184Smarcel
27238184Smarcel#include <sys/cdefs.h>
28238184Smarcel__FBSDID("$FreeBSD$");
29238184Smarcel
30238184Smarcel#include <sys/param.h>
31238184Smarcel#include <sys/systm.h>
32238184Smarcel
33238184Smarcel#include <machine/md_var.h>
34238184Smarcel#include <machine/vmparam.h>
35238184Smarcel
36238184Smarcelstatic u_int phys_avail_segs;
37238184Smarcel
38238184Smarcelvm_paddr_t phys_avail[2 * VM_PHYSSEG_MAX + 2];
39238184Smarcel
40238184Smarcelvm_paddr_t paddr_max;
41238184Smarcel
42238184Smarcellong realmem;
43238184Smarcel
44238184Smarcelstatic u_int
45238184Smarcelia64_physmem_find(vm_paddr_t base, vm_paddr_t lim)
46238184Smarcel{
47238184Smarcel	u_int idx;
48238184Smarcel
49238184Smarcel	for (idx = 0; phys_avail[idx + 1] != 0; idx += 2) {
50238184Smarcel		if (phys_avail[idx] >= lim ||
51238184Smarcel		    phys_avail[idx + 1] > base)
52238184Smarcel			break;
53238184Smarcel	}
54238184Smarcel	return (idx);
55238184Smarcel}
56238184Smarcel
57238184Smarcelstatic int
58238184Smarcelia64_physmem_insert(u_int idx, vm_paddr_t base, vm_paddr_t lim)
59238184Smarcel{
60238184Smarcel	u_int ridx;
61238184Smarcel
62238184Smarcel	if (phys_avail_segs == VM_PHYSSEG_MAX)
63238184Smarcel		return (ENOMEM);
64238184Smarcel
65238184Smarcel	ridx = phys_avail_segs * 2;
66238184Smarcel	while (idx < ridx) {
67238184Smarcel		phys_avail[ridx + 1] = phys_avail[ridx - 1];
68238184Smarcel		phys_avail[ridx] = phys_avail[ridx - 2];
69238184Smarcel		ridx -= 2;
70238184Smarcel	}
71238184Smarcel	phys_avail[idx] = base;
72238184Smarcel	phys_avail[idx + 1] = lim;
73238184Smarcel	phys_avail_segs++;
74238184Smarcel	return (0);
75238184Smarcel}
76238184Smarcel
77238184Smarcelstatic int
78238184Smarcelia64_physmem_remove(u_int idx)
79238184Smarcel{
80238184Smarcel
81238184Smarcel	if (phys_avail_segs == 0)
82238184Smarcel		return (ENOENT);
83238184Smarcel	do {
84238184Smarcel		phys_avail[idx] = phys_avail[idx + 2];
85238184Smarcel		phys_avail[idx + 1] = phys_avail[idx + 3];
86238184Smarcel		idx += 2;
87238184Smarcel	} while (phys_avail[idx + 1] != 0);
88238184Smarcel	phys_avail_segs--;
89238184Smarcel	return (0);
90238184Smarcel}
91238184Smarcel
92238184Smarcelint
93238184Smarcelia64_physmem_add(vm_paddr_t base, vm_size_t len)
94238184Smarcel{
95238184Smarcel	vm_paddr_t lim;
96238184Smarcel	u_int idx;
97238184Smarcel
98238184Smarcel	realmem += len;
99238184Smarcel
100238184Smarcel	lim = base + len;
101238184Smarcel	idx = ia64_physmem_find(base, lim);
102238184Smarcel	if (phys_avail[idx] == lim) {
103238184Smarcel		phys_avail[idx] = base;
104238184Smarcel		return (0);
105238184Smarcel	}
106238184Smarcel	if (idx > 0 && phys_avail[idx - 1] == base) {
107238184Smarcel		phys_avail[idx - 1] = lim;
108238184Smarcel		return (0);
109238184Smarcel	}
110238184Smarcel	return (ia64_physmem_insert(idx, base, lim));
111238184Smarcel}
112238184Smarcel
113238184Smarcelint
114238184Smarcelia64_physmem_delete(vm_paddr_t base, vm_size_t len)
115238184Smarcel{
116238184Smarcel	vm_paddr_t lim;
117238184Smarcel	u_int idx;
118238184Smarcel
119238184Smarcel	lim = base + len;
120238184Smarcel	idx = ia64_physmem_find(base, lim);
121238184Smarcel	if (phys_avail[idx] >= lim || phys_avail[idx + 1] == 0)
122238184Smarcel		return (ENOENT);
123238184Smarcel	if (phys_avail[idx] < base && phys_avail[idx + 1] > lim) {
124238184Smarcel		len = phys_avail[idx + 1] - lim;
125238184Smarcel		phys_avail[idx + 1] = base;
126238184Smarcel		base = lim;
127238184Smarcel		lim = base + len;
128238184Smarcel		return (ia64_physmem_insert(idx + 2, base, lim));
129238184Smarcel	} else {
130238184Smarcel		if (phys_avail[idx] == base)
131238184Smarcel			phys_avail[idx] = lim;
132238184Smarcel		if (phys_avail[idx + 1] == lim)
133238184Smarcel			phys_avail[idx + 1] = base;
134238184Smarcel		if (phys_avail[idx] >= phys_avail[idx + 1])
135238184Smarcel			return (ia64_physmem_remove(idx));
136238184Smarcel	}
137238184Smarcel	return (0);
138238184Smarcel}
139238184Smarcel
140238184Smarcelint
141238184Smarcelia64_physmem_fini(void)
142238184Smarcel{
143238184Smarcel	vm_paddr_t base, lim, size;
144238184Smarcel	u_int idx;
145238184Smarcel
146238184Smarcel	idx = 0;
147238184Smarcel	while (phys_avail[idx + 1] != 0) {
148238184Smarcel		base = round_page(phys_avail[idx]);
149238184Smarcel		lim = trunc_page(phys_avail[idx + 1]);
150238184Smarcel		if (base < lim) {
151238184Smarcel			phys_avail[idx] = base;
152238184Smarcel			phys_avail[idx + 1] = lim;
153238184Smarcel			size = lim - base;
154238184Smarcel			physmem += atop(size);
155238184Smarcel			paddr_max = lim;
156238184Smarcel			idx += 2;
157238184Smarcel		} else
158238184Smarcel			ia64_physmem_remove(idx);
159238184Smarcel	}
160238184Smarcel
161238184Smarcel	/*
162238184Smarcel	 * Round realmem to a multple of 128MB. Hopefully that compensates
163238184Smarcel	 * for any loss of DRAM that isn't accounted for in the memory map.
164238184Smarcel	 * I'm thinking legacy BIOS or VGA here. In any case, it's ok if
165238184Smarcel	 * we got it wrong, because we don't actually use realmem. It's
166238184Smarcel	 * just for show...
167238184Smarcel	 */
168238184Smarcel	size = 1U << 27;
169238184Smarcel	realmem = (realmem + size - 1) & ~(size - 1);
170238184Smarcel	realmem = atop(realmem);
171238184Smarcel	return (0);
172238184Smarcel}
173238184Smarcel
174238184Smarcelint
175238184Smarcelia64_physmem_init(void)
176238184Smarcel{
177238184Smarcel
178238184Smarcel	/* Nothing to do just yet. */
179238184Smarcel	return (0);
180238184Smarcel}
181238184Smarcel
182238184Smarcelint
183238184Smarcelia64_physmem_track(vm_paddr_t base, vm_size_t len)
184238184Smarcel{
185238184Smarcel
186238184Smarcel	realmem += len;
187238184Smarcel	return (0);
188238184Smarcel}
189238184Smarcel
190251963Smariusvoid *
191238184Smarcelia64_physmem_alloc(vm_size_t len, vm_size_t align)
192238184Smarcel{
193251963Smarius	vm_paddr_t base, lim, pa;
194251963Smarius	void *ptr;
195251963Smarius	u_int idx;
196238184Smarcel
197251963Smarius	if (phys_avail_segs == 0)
198251963Smarius		return (NULL);
199251963Smarius
200251963Smarius	len = round_page(len);
201251963Smarius
202251963Smarius	/*
203251963Smarius	 * Try and allocate with least effort.
204251963Smarius	 */
205251963Smarius	idx = phys_avail_segs * 2;
206251963Smarius	while (idx > 0) {
207251963Smarius		idx -= 2;
208251963Smarius		base = phys_avail[idx];
209251963Smarius		lim = phys_avail[idx + 1];
210251963Smarius
211251963Smarius		if (lim - base < len)
212251963Smarius			continue;
213251963Smarius
214251963Smarius		/* First try from the end. */
215251963Smarius		pa = lim - len;
216251963Smarius		if ((pa & (align - 1)) == 0) {
217251963Smarius			if (pa == base)
218251963Smarius				ia64_physmem_remove(idx);
219251963Smarius			else
220251963Smarius				phys_avail[idx + 1] = pa;
221251963Smarius			goto gotit;
222251963Smarius		}
223251963Smarius
224251963Smarius		/* Try from the start next. */
225251963Smarius		pa = base;
226251963Smarius		if ((pa & (align - 1)) == 0) {
227251963Smarius			if (pa + len == lim)
228251963Smarius				ia64_physmem_remove(idx);
229251963Smarius			else
230251963Smarius				phys_avail[idx] += len;
231251963Smarius			goto gotit;
232251963Smarius		}
233251963Smarius	}
234251963Smarius
235251963Smarius	/*
236251963Smarius	 * Find a good segment and split it up.
237251963Smarius	 */
238251963Smarius	idx = phys_avail_segs * 2;
239251963Smarius	while (idx > 0) {
240251963Smarius		idx -= 2;
241251963Smarius		base = phys_avail[idx];
242251963Smarius		lim = phys_avail[idx + 1];
243251963Smarius
244251963Smarius		pa = (base + align - 1) & ~(align - 1);
245251963Smarius		if (pa + len <= lim) {
246251963Smarius			ia64_physmem_delete(pa, len);
247251963Smarius			goto gotit;
248251963Smarius		}
249251963Smarius	}
250251963Smarius
251251963Smarius	/* Out of luck. */
252251963Smarius	return (NULL);
253251963Smarius
254251963Smarius gotit:
255251963Smarius	ptr = (void *)IA64_PHYS_TO_RR7(pa);
256251963Smarius	bzero(ptr, len);
257251963Smarius	return (ptr);
258238184Smarcel}
259