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