physmem.c revision 310508
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: stable/10/sys/ia64/ia64/physmem.c 310508 2016-12-24 13:28:39Z avg $");
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;
41long Maxmem;
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
172	/*
173	 * Maxmem isn't the "maximum memory", it's one larger than the
174	 * highest page of the physical address space.
175	 */
176	Maxmem = atop(paddr_max);
177	return (0);
178}
179
180int
181ia64_physmem_init(void)
182{
183
184	/* Nothing to do just yet. */
185	return (0);
186}
187
188int
189ia64_physmem_track(vm_paddr_t base, vm_size_t len)
190{
191
192	realmem += len;
193	return (0);
194}
195
196void *
197ia64_physmem_alloc(vm_size_t len, vm_size_t align)
198{
199	vm_paddr_t base, lim, pa;
200	void *ptr;
201	u_int idx;
202
203	if (phys_avail_segs == 0)
204		return (NULL);
205
206	len = round_page(len);
207
208	/*
209	 * Try and allocate with least effort.
210	 */
211	idx = phys_avail_segs * 2;
212	while (idx > 0) {
213		idx -= 2;
214		base = phys_avail[idx];
215		lim = phys_avail[idx + 1];
216
217		if (lim - base < len)
218			continue;
219
220		/* First try from the end. */
221		pa = lim - len;
222		if ((pa & (align - 1)) == 0) {
223			if (pa == base)
224				ia64_physmem_remove(idx);
225			else
226				phys_avail[idx + 1] = pa;
227			goto gotit;
228		}
229
230		/* Try from the start next. */
231		pa = base;
232		if ((pa & (align - 1)) == 0) {
233			if (pa + len == lim)
234				ia64_physmem_remove(idx);
235			else
236				phys_avail[idx] += len;
237			goto gotit;
238		}
239	}
240
241	/*
242	 * Find a good segment and split it up.
243	 */
244	idx = phys_avail_segs * 2;
245	while (idx > 0) {
246		idx -= 2;
247		base = phys_avail[idx];
248		lim = phys_avail[idx + 1];
249
250		pa = (base + align - 1) & ~(align - 1);
251		if (pa + len <= lim) {
252			ia64_physmem_delete(pa, len);
253			goto gotit;
254		}
255	}
256
257	/* Out of luck. */
258	return (NULL);
259
260 gotit:
261	ptr = (void *)IA64_PHYS_TO_RR7(pa);
262	bzero(ptr, len);
263	return (ptr);
264}
265