1 /*
2 *
3 * This file is subject to the terms and conditions of the GNU General Public
4 * License. See the file "COPYING" in the main directory of this archive
5 * for more details.
6 *
7 * Copyright (C) 2000-2003 Silicon Graphics, Inc. All rights reserved.
8 */
9
10
11
12 /*
13 * FPROM EFI memory descriptor build routines
14 *
15 * - Routines to build the EFI memory descriptor map
16 * - Should also be usable by the SGI prom to convert
17 * klconfig to efi_memmap
18 */
19
20
21 /*
22 * Sometimes it is useful to build a fake prom with a memmap that matches another
23 * platform. There are restrictions on how successful you will be, but here
24 * are the instructions for what I have used in the past & had good luck:
25 *
26 * - compile a kernel for the target platform (like zx1) with the following
27 * set:
28 * #define EFI_DEBUG 1 # in arch/ia64/kernel/efi.c
29 * #define SRAT_DEBUG # in drivers/acpi/numa.c
30 *
31 * - this causes the kernel to dump the memmap & SRAT during boot.
32 *
33 * - copy the console output to a file.
34 *
35 * - run the following script with the file as input:
36 *
37 * #!/bin/sh
38 *
39 * awk '
40 * /^mem/ {
41 * printf "\t{%s, %sUL, %sUL, %sUL},\n", $4, $7, $11, $12
42 * }
43 * /SRAT Memory/ {
44 * if (srat != 1) print "SRAT"
45 * srat = 1
46 * for (i=1; i<=0;i++)
47 * printf "%4d |%s|\n", i, $i
48 * printf "\t{%s, %sUL, %sUL, %sUL},\n", $4, $6, $8, $13
49 * }
50 * BEGIN {
51 * FS="[ \[\)\(\t:=,-]"
52 * } '
53 *
54 * - this converts the memmap & SRAT info into C code array initilization statements.
55 *
56 * - copy & paste the initialization statements into the arrays below. Look for
57 * comments "PASTE xxx HERE". In general, on the MEMMAP is present on most other
58 * systems. If you have an SRAT, you may need to hack the number of nodes in fw-emu.
59 * Good luck...
60 *
61 * - set "#define FAKE_MEMMAP 1
62 *
63 * - When running medusa, make sure you set the node1_memory_config to cover the
64 * amount of memory that you are going to use. Also note that you can run the
65 * kernel in "alias" space (starts at phy adr 0). This is kinda tricky, though
66 */
67
68
69 #include <linux/config.h>
70 #include <linux/efi.h>
71 #include <linux/acpi.h>
72 #include "fpmem.h"
73
74 /*
75 * args points to a layout in memory like this
76 *
77 * 32 bit 32 bit
78 *
79 * numnodes numcpus
80 *
81 * 16 bit 16 bit 32 bit
82 * nasid0 cpuconf membankdesc0
83 * nasid1 cpuconf membankdesc1
84 * .
85 * .
86 * .
87 * .
88 * .
89 */
90
91 sn_memmap_t *sn_memmap ;
92 sn_config_t *sn_config ;
93
94 /*
95 * There is a hole in the node 0 address space. Dont put it
96 * in the memory map
97 */
98 #define NODE0_HOLE_SIZE (20*MB)
99 #define NODE0_HOLE_END (4UL*GB)
100
101 #define MB (1024*1024)
102 #define GB (1024*MB)
103 #define KERNEL_SIZE (4*MB)
104 #define PROMRESERVED_SIZE (1*MB)
105
106 #ifdef SGI_SN2
107 #define PHYS_ADDRESS(_n, _x) (((long)_n<<38) | (long)_x | 0x3000000000UL)
108 #define MD_BANK_SHFT 34
109 #endif
110
111 /*
112 * For SN, this may not take an arg and gets the numnodes from
113 * the prom variable or by traversing klcfg or promcfg
114 */
115 int
GetNumNodes(void)116 GetNumNodes(void)
117 {
118 return sn_config->nodes;
119 }
120
121 int
GetNumCpus(void)122 GetNumCpus(void)
123 {
124 return sn_config->cpus;
125 }
126
127 /* For SN, get the index th nasid */
128
129 int
GetNasid(int cnode)130 GetNasid(int cnode)
131 {
132 return sn_memmap[cnode].nasid ;
133 }
134
135 node_memmap_t
GetMemBankInfo(int index)136 GetMemBankInfo(int index)
137 {
138 return sn_memmap[index].node_memmap ;
139 }
140
141 int
IsCpuPresent(int cnode,int cpu)142 IsCpuPresent(int cnode, int cpu)
143 {
144 return sn_memmap[cnode].cpuconfig & (1UL<<cpu);
145 }
146
147 void
GetLogicalCpu(int bsp,int * nasidp,int * cpup)148 GetLogicalCpu(int bsp, int *nasidp, int *cpup)
149 {
150 int cnode, cpu;
151
152 for (cnode=0; cnode<GetNumNodes(); cnode++) {
153 for (cpu=0; cpu<MAX_CPUS_NODE; cpu++)
154 if (IsCpuPresent(cnode,cpu)) {
155 *nasidp = GetNasid(cnode);
156 *cpup = cpu;
157 if (bsp-- == 0)
158 return;
159 }
160 }
161 }
162
163 /*
164 * Made this into an explicit case statement so that
165 * we can assign specific properties to banks like bank0
166 * actually disabled etc.
167 */
168
169 #ifdef SGI_SN2
170 long
GetBankSize(int index,node_memmap_t nmemmap)171 GetBankSize(int index, node_memmap_t nmemmap)
172 {
173 int bsize, bdou, hack;
174 /*
175 * Add 2 because there are 4 dimms per bank.
176 */
177 switch (index) {
178 case 0:
179 bsize = nmemmap.b0size;
180 bdou = nmemmap.b0dou;
181 hack = nmemmap.hack0;
182 break;
183 case 1:
184 bsize = nmemmap.b1size;
185 bdou = nmemmap.b1dou;
186 hack = nmemmap.hack1;
187 break;
188 case 2:
189 bsize = nmemmap.b2size;
190 bdou = nmemmap.b2dou;
191 hack = nmemmap.hack2;
192 break;
193 case 3:
194 bsize = nmemmap.b3size;
195 bdou = nmemmap.b3dou;
196 hack = nmemmap.hack3;
197 break;
198 default:return -1 ;
199 }
200
201 if (bsize < 6 && hack == 0)
202 return (1UL<<((2+bsize+bdou)+SN2_BANK_SIZE_SHIFT))*31/32;
203 else
204 return (16*MB)*hack;
205 }
206
207 #endif
208
209 void
build_mem_desc(efi_memory_desc_t * md,int type,long paddr,long numbytes,long attr)210 build_mem_desc(efi_memory_desc_t *md, int type, long paddr, long numbytes, long attr)
211 {
212 md->type = type;
213 md->phys_addr = paddr;
214 md->virt_addr = 0;
215 md->num_pages = numbytes >> 12;
216 md->attribute = attr;
217 }
218
219
220 //#define FAKE_MEMMAP 1
221 #ifdef FAKE_MEMMAP
222
223 #define OFF 0x3000000000UL
224 struct {
225 unsigned long type;
226 unsigned long attr;
227 unsigned long start;
228 unsigned long end;
229 } mdx[] = {
230 /* PASTE SRAT HERE */
231 };
232
233 struct srat {
234 unsigned long start;
235 unsigned long len;
236 unsigned long type;
237 unsigned long pxm;
238 } srat[] = {
239
240 /* PASTE SRAT HERE */
241
242 };
243
244
245
246 void *
build_memory_srat(struct acpi_table_memory_affinity * ptr)247 build_memory_srat(struct acpi_table_memory_affinity *ptr)
248 {
249 int i;
250 int n = sizeof(srat)/sizeof(struct srat);
251
252 for (i=0; i<n; i++)
253 srat[i].start += OFF;
254 for (i=0; i<n; i++) {
255 ptr->header.type = ACPI_SRAT_MEMORY_AFFINITY;
256 ptr->header.length = sizeof(struct acpi_table_memory_affinity);
257 ptr->proximity_domain = srat[i].pxm;
258 ptr->base_addr_lo = srat[i].start & 0xffffffff;
259 ptr->length_lo = srat[i].len & 0xffffffff;
260 ptr->base_addr_hi = srat[i].start >> 32;
261 ptr->length_hi = srat[i].len >> 32;
262 ptr->memory_type = ACPI_ADDRESS_RANGE_MEMORY;
263 ptr->flags.enabled = 1;
264 ptr++;
265 }
266 return ptr;
267 }
268
269 int
build_efi_memmap(void * md,int mdsize)270 build_efi_memmap(void *md, int mdsize)
271 {
272 int i;
273
274 for (i=0; i<sizeof(mdx)/32; i++) {
275 mdx[i].start += OFF;
276 mdx[i].end += OFF;
277 }
278 for (i=0; i<sizeof(mdx)/32; i++) {
279 build_mem_desc(md, mdx[i].type, mdx[i].start, mdx[i].end-mdx[i].start, mdx[i].attr);
280 md += mdsize;
281 }
282 return i;
283
284 }
285
286 #else /* ! FAKE_MEMMAP */
287
288 void *
build_memory_srat(struct acpi_table_memory_affinity * ptr)289 build_memory_srat(struct acpi_table_memory_affinity *ptr)
290 {
291 int cnode, nasid;
292
293 for (cnode=0; cnode<GetNumNodes(); cnode++) {
294 nasid = GetNasid(cnode);
295 ptr->header.type = ACPI_SRAT_MEMORY_AFFINITY;
296 ptr->header.length = sizeof(struct acpi_table_memory_affinity);
297 ptr->proximity_domain = PROXIMITY_DOMAIN(nasid);
298 ptr->base_addr_lo = 0;
299 ptr->length_lo = 0;
300 #if defined(SGI_SN2)
301 ptr->base_addr_hi = (nasid<<6) | (3<<4);
302 ptr->length_hi = (MD_BANKSIZE*MD_BANKS_PER_NODE)>>32;
303 #endif
304 ptr->memory_type = ACPI_ADDRESS_RANGE_MEMORY;
305 ptr->flags.enabled = 1;
306 ptr++;
307 }
308 return ptr;
309 }
310
311
312 int
build_efi_memmap(void * md,int mdsize)313 build_efi_memmap(void *md, int mdsize)
314 {
315 int numnodes = GetNumNodes() ;
316 int cnode,bank ;
317 int nasid ;
318 node_memmap_t membank_info ;
319 int count = 0 ;
320 long paddr, hole, numbytes;
321
322
323 for (cnode=0;cnode<numnodes;cnode++) {
324 nasid = GetNasid(cnode) ;
325 membank_info = GetMemBankInfo(cnode) ;
326 for (bank=0;bank<MD_BANKS_PER_NODE;bank++) {
327 numbytes = GetBankSize(bank, membank_info);
328 if (numbytes) {
329 paddr = PHYS_ADDRESS(nasid, (long)bank<<MD_BANK_SHFT);
330 /*
331 * Only emulate the memory prom grabs
332 * if we have lots of memory, to allow
333 * us to simulate smaller memory configs than
334 * we can actually run on h/w. Otherwise,
335 * linux throws away a whole "granule".
336 */
337 if (cnode == 0 && bank == 0 &&
338 numbytes > 128*1024*1024) {
339 numbytes -= 1000;
340 }
341
342 /*
343 * Check for the node 0 hole. Since banks cant
344 * span the hole, we only need to check if the end of
345 * the range is the end of the hole.
346 */
347 if (paddr+numbytes == NODE0_HOLE_END)
348 numbytes -= NODE0_HOLE_SIZE;
349 /*
350 * UGLY hack - we must skip overr the kernel and
351 * PROM runtime services but we dont exactly where it is.
352 * So lets just reserve:
353 * node 0
354 * 0-1MB for PAL
355 * 1-4MB for SAL
356 * node 1-N
357 * 0-1 for SAL
358 */
359 if (bank == 0) {
360 if (cnode == 0) {
361 hole = 2*1024*1024;
362 build_mem_desc(md, EFI_PAL_CODE, paddr, hole, EFI_MEMORY_WB|EFI_MEMORY_WB);
363 numbytes -= hole;
364 paddr += hole;
365 count++ ;
366 md += mdsize;
367 hole = 1*1024*1024;
368 build_mem_desc(md, EFI_CONVENTIONAL_MEMORY, paddr, hole, EFI_MEMORY_UC);
369 numbytes -= hole;
370 paddr += hole;
371 count++ ;
372 md += mdsize;
373 hole = 1*1024*1024;
374 build_mem_desc(md, EFI_RUNTIME_SERVICES_DATA, paddr, hole, EFI_MEMORY_WB|EFI_MEMORY_WB);
375 numbytes -= hole;
376 paddr += hole;
377 count++ ;
378 md += mdsize;
379 } else {
380 hole = 2*1024*1024;
381 build_mem_desc(md, EFI_RUNTIME_SERVICES_DATA, paddr, hole, EFI_MEMORY_WB|EFI_MEMORY_WB);
382 numbytes -= hole;
383 paddr += hole;
384 count++ ;
385 md += mdsize;
386 hole = 2*1024*1024;
387 build_mem_desc(md, EFI_RUNTIME_SERVICES_DATA, paddr, hole, EFI_MEMORY_UC);
388 numbytes -= hole;
389 paddr += hole;
390 count++ ;
391 md += mdsize;
392 }
393 }
394 build_mem_desc(md, EFI_CONVENTIONAL_MEMORY, paddr, numbytes, EFI_MEMORY_WB|EFI_MEMORY_WB);
395
396 md += mdsize ;
397 count++ ;
398 }
399 }
400 }
401 return count ;
402 }
403 #endif /* FAKE_MEMMAP */
404
405 void
build_init(unsigned long args)406 build_init(unsigned long args)
407 {
408 sn_config = (sn_config_t *) (args);
409 sn_memmap = (sn_memmap_t *)(args + 8) ; /* SN equiv for this is */
410 /* init to klconfig start */
411 }
412