1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#include <sys/types.h>
27#include <sys/param.h>
28#include <sys/promif.h>
29#include <sys/memlist.h>
30#include <sys/bootconf.h>
31#include <sys/salib.h>
32
33/*
34 * This file defines the interface from the prom and platform-dependent
35 * form of the memory lists, to boot's more generic form of the memory
36 * list.  For sun4u, the memory list properties are {hi, lo, size_hi, size_lo},
37 * which is similar to boot's format, except boot's format is a linked
38 * list, and the prom's is an array of these structures. Note that the
39 * native property on sparc machines is identical to the property encoded
40 * format, so no property decoding is required.
41 *
42 * Note that the format of the memory lists is really 4 encoded integers,
43 * but the encoding is the same as that given in the following structure
44 * on SPARC systems ...
45 */
46
47struct sun4u_prom_memlist {
48	u_longlong_t	addr;
49	u_longlong_t	size;
50};
51
52struct sun4u_prom_memlist scratch_memlist[200];
53
54struct memlist *fill_memlists(char *name, char *prop, struct memlist *);
55extern struct memlist *pfreelistp, *vfreelistp, *pinstalledp;
56
57static struct memlist *reg_to_list(struct sun4u_prom_memlist *a, size_t size,
58    struct memlist *old);
59static void sort_reglist(struct sun4u_prom_memlist *ar, size_t size);
60extern void kmem_init(void);
61
62void
63init_memlists(void)
64{
65	/* this list is a map of pmem actually installed */
66	pinstalledp = fill_memlists("memory", "reg", pinstalledp);
67
68	vfreelistp = fill_memlists("virtual-memory", "available", vfreelistp);
69	pfreelistp = fill_memlists("memory", "available", pfreelistp);
70
71	kmem_init();
72}
73
74struct memlist *
75fill_memlists(char *name, char *prop, struct memlist *old)
76{
77	static pnode_t pmem = 0;
78	static pnode_t pmmu = 0;
79	pnode_t node;
80	size_t links;
81	struct memlist *al;
82	struct sun4u_prom_memlist *pm = scratch_memlist;
83
84	if (pmem == (pnode_t)0)  {
85
86		/*
87		 * Figure out the interesting phandles, one time
88		 * only.
89		 */
90
91		ihandle_t ih;
92
93		if ((ih = prom_mmu_ihandle()) == (ihandle_t)-1)
94			prom_panic("Can't get mmu ihandle");
95		pmmu = prom_getphandle(ih);
96
97		if ((ih = prom_memory_ihandle()) == (ihandle_t)-1)
98			prom_panic("Can't get memory ihandle");
99		pmem = prom_getphandle(ih);
100	}
101
102	if (strcmp(name, "memory") == 0)
103		node = pmem;
104	else
105		node = pmmu;
106
107	/*
108	 * Read memory node and calculate the number of entries
109	 */
110	if ((links = prom_getproplen(node, prop)) == -1)
111		prom_panic("Cannot get list.\n");
112	if (links > sizeof (scratch_memlist)) {
113		prom_printf("%s list <%s> exceeds boot capabilities\n",
114		    name, prop);
115		prom_panic("fill_memlists - memlist size");
116	}
117	links = links / sizeof (struct sun4u_prom_memlist);
118
119
120	(void) prom_getprop(node, prop, (caddr_t)pm);
121	sort_reglist(pm, links);
122	al = reg_to_list(pm, links, old);
123	return (al);
124}
125
126/*
127 *  Simple selection sort routine.
128 *  Sorts platform dependent memory lists into ascending order
129 */
130
131static void
132sort_reglist(struct sun4u_prom_memlist *ar, size_t n)
133{
134	int i, j, min;
135	struct sun4u_prom_memlist temp;
136
137	for (i = 0; i < n; i++) {
138		min = i;
139
140		for (j = i+1; j < n; j++)  {
141			if (ar[j].addr < ar[min].addr)
142				min = j;
143		}
144
145		if (i != min)  {
146			/* Swap ar[i] and ar[min] */
147			temp = ar[min];
148			ar[min] = ar[i];
149			ar[i] = temp;
150		}
151	}
152}
153
154/*
155 *  This routine will convert our platform dependent memory list into
156 *  struct memlists's.  And it will also coalesce adjacent  nodes if
157 *  possible.
158 */
159static struct memlist *
160reg_to_list(struct sun4u_prom_memlist *ar, size_t n, struct memlist *old)
161{
162	struct memlist *ptr, *head, *last;
163	int i;
164	u_longlong_t size = 0;
165	u_longlong_t addr = 0;
166	u_longlong_t start1, start2;
167	int flag = 0;
168
169	if (n == 0)
170		return ((struct memlist *)0);
171
172	/*
173	 * if there was a memory list allocated before, free it first.
174	 */
175	if (old)
176		(void) add_to_freelist(old);
177
178	head = NULL;
179	last = NULL;
180
181	for (i = 0; i < n; i++) {
182		start1 = ar[i].addr;
183		start2 = ar[i+1].addr;
184		if (i < n-1 && (start1 + ar[i].size == start2)) {
185			size += ar[i].size;
186			if (!flag) {
187				addr = start1;
188				flag++;
189			}
190			continue;
191		} else if (flag) {
192			/*
193			 * catch the last one on the way out of
194			 * this iteration
195			 */
196			size += ar[i].size;
197		}
198
199		ptr = (struct memlist *)get_memlist_struct();
200		if (!head)
201			head = ptr;
202		if (last)
203			last->ml_next = ptr;
204		ptr->ml_address = flag ? addr : start1;
205		ptr->ml_size = size ? size : ar[i].size;
206		ptr->ml_prev = last;
207		last = ptr;
208
209		size = 0;
210		flag = 0;
211		addr = 0;
212	}
213
214	last->ml_next = NULL;
215	return (head);
216}
217