1 /*
2  * This file is subject to the terms and conditions of the GNU General Public
3  * License.  See the file "COPYING" in the main directory of this archive
4  * for more details.
5  *
6  * Copyright (C) 1999,2001-2003 Silicon Graphics, Inc.  All Rights Reserved.
7  *
8  * Module to export the system's Firmware Interface Tables, including
9  * PROM revision numbers, in /proc
10  */
11 #include <linux/config.h>
12 #include <linux/module.h>
13 #include <linux/slab.h>
14 #include <linux/proc_fs.h>
15 #include <asm/system.h>
16 #include <asm/io.h>
17 #include <asm/sn/simulator.h>
18 
19 /* to lookup nasids */
20 #include <asm/sn/sn_cpuid.h>
21 
22 MODULE_DESCRIPTION("PROM version reporting for /proc");
23 MODULE_AUTHOR("Chad Talbott");
24 MODULE_LICENSE("GPL");
25 
26 #undef DEBUG_PROMINFO
27 
28 #define TRACE_PROMINFO
29 
30 #if defined(DEBUG_PROMINFO)
31 #  define DPRINTK(x...) printk(KERN_DEBUG x)
32 #else
33 #  define DPRINTK(x...)
34 #endif
35 
36 #if defined(TRACE_PROMINFO) && defined(DEBUG_PROMINFO)
37 #  if defined(__GNUC__)
38 #    define TRACE()	printk(KERN_DEBUG "%s:%d:%s\n", \
39 			       __FILE__, __LINE__, __FUNCTION__)
40 #  else
41 #    define TRACE()	printk(KERN_DEBUG "%s:%d\n", __LINE__, __FILE__)
42 #  endif
43 #else
44 #  define TRACE()
45 #endif
46 
47 /* Sub-regions determined by bits in Node Offset */
48 #define	LB_PROM_SPACE		0x0000000700000000ul /* Local LB PROM */
49 
50 #define FIT_SIGNATURE		0x2020205f5449465ful
51 /* Standard Intel FIT entry types */
52 #define FIT_ENTRY_FIT_HEADER	0x00	/* FIT header entry */
53 #define FIT_ENTRY_PAL_B		0x01	/* PAL_B entry */
54 /* Entries 0x02 through 0x0D reserved by Intel */
55 #define FIT_ENTRY_PAL_A_PROC	0x0E	/* Processor-specific PAL_A entry */
56 #define FIT_ENTRY_PAL_A		0x0F	/* PAL_A entry, same as... */
57 #define FIT_ENTRY_PAL_A_GEN	0x0F	/* ...Generic PAL_A entry */
58 #define FIT_ENTRY_UNUSED	0x7F	/* Unused (reserved by Intel?) */
59 /* OEM-defined entries range from 0x10 to 0x7E. */
60 #define FIT_ENTRY_SAL_A		0x10	/* SAL_A entry */
61 #define FIT_ENTRY_SAL_B		0x11	/* SAL_B entry */
62 #define FIT_ENTRY_SALRUNTIME	0x12	/* SAL runtime entry */
63 #define FIT_ENTRY_EFI		0x1F	/* EFI entry */
64 #define FIT_ENTRY_FPSWA		0x20	/* embedded fpswa entry */
65 #define FIT_ENTRY_VMLINUX	0x21	/* embedded vmlinux entry */
66 
67 #define FIT_MAJOR_SHIFT	(32 + 8)
68 #define FIT_MAJOR_MASK	((1 << 8) - 1)
69 #define FIT_MINOR_SHIFT	32
70 #define FIT_MINOR_MASK	((1 << 8) - 1)
71 
72 #define FIT_MAJOR(q)	\
73 	((unsigned) ((q) >> FIT_MAJOR_SHIFT) & FIT_MAJOR_MASK)
74 #define FIT_MINOR(q)	\
75 	((unsigned) ((q) >> FIT_MINOR_SHIFT) & FIT_MINOR_MASK)
76 
77 #define FIT_TYPE_SHIFT	(32 + 16)
78 #define FIT_TYPE_MASK	((1 << 7) - 1)
79 
80 #define FIT_TYPE(q)	\
81 	((unsigned) ((q) >> FIT_TYPE_SHIFT) & FIT_TYPE_MASK)
82 
83 #define FIT_ENTRY(type, maj, min, size)					\
84 	((((unsigned long)(maj) & FIT_MAJOR_MASK) << FIT_MAJOR_SHIFT) |	\
85 	 (((unsigned long)(min) & FIT_MINOR_MASK) << FIT_MINOR_SHIFT) |	\
86 	 (((unsigned long)(type) & FIT_TYPE_MASK) << FIT_TYPE_SHIFT) |	\
87 	 (size))
88 
89 struct fit_type_map_t {
90 	unsigned char	type;
91 	const char	*name;
92 };
93 
94 static const struct fit_type_map_t fit_entry_types[] = {
95 	{ FIT_ENTRY_FIT_HEADER, "FIT Header" },
96 	{ FIT_ENTRY_PAL_A_GEN,  "Generic PAL_A" },
97 	{ FIT_ENTRY_PAL_A_PROC, "Processor-specific PAL_A" },
98 	{ FIT_ENTRY_PAL_A,      "PAL_A" },
99 	{ FIT_ENTRY_PAL_B,      "PAL_B" },
100 	{ FIT_ENTRY_SAL_A,      "SAL_A" },
101 	{ FIT_ENTRY_SAL_B,      "SAL_B" },
102 	{ FIT_ENTRY_SALRUNTIME, "SAL runtime" },
103 	{ FIT_ENTRY_EFI,	"EFI" },
104 	{ FIT_ENTRY_VMLINUX,    "Embedded Linux" },
105 	{ FIT_ENTRY_FPSWA,      "Embedded FPSWA" },
106 	{ FIT_ENTRY_UNUSED,     "Unused" },
107 	{ 0xff,                 "Error" },
108 };
109 
110 static const char *
fit_type_name(unsigned char type)111 fit_type_name(unsigned char type)
112 {
113 	struct fit_type_map_t const*mapp;
114 
115 	for (mapp = fit_entry_types; mapp->type != 0xff; mapp++)
116 		if (type == mapp->type)
117 			return mapp->name;
118 
119 	if ((type > FIT_ENTRY_PAL_A) && (type < FIT_ENTRY_UNUSED))
120 		return "OEM type";
121 	if ((type > FIT_ENTRY_PAL_B) && (type < FIT_ENTRY_PAL_A))
122 		return "Reserved";
123 
124 	return "Unknown type";
125 }
126 
127 /* These two routines read the FIT table directly from the FLASH PROM
128  * on a specific node.  The PROM can only be accessed using aligned 64
129  * bit reads, so we do that and then shift and mask the result to get
130  * at each field.
131  */
132 static int
dump_fit_entry(char * page,unsigned long * fentry)133 dump_fit_entry(char *page, unsigned long *fentry)
134 {
135 	unsigned long q1, q2;
136 	unsigned type;
137 
138 	TRACE();
139 
140 	q1 = readq(fentry);
141 	q2 = readq(fentry + 1);
142 	type = FIT_TYPE(q2);
143 	return sprintf(page, "%02x %-25s %x.%02x %016lx %u\n",
144 		       type,
145 		       fit_type_name(type),
146 		       FIT_MAJOR(q2), FIT_MINOR(q2),
147 		       q1,
148 		       /* mult by sixteen to get size in bytes */
149 		       (unsigned)q2 * 16);
150 }
151 
152 /* We assume that the fit table will be small enough that we can print
153  * the whole thing into one page.  (This is true for our default 16kB
154  * pages -- each entry is about 60 chars wide when printed.)  I read
155  * somewhere that the maximum size of the FIT is 128 entries, so we're
156  * OK except for 4kB pages (and no one is going to do that on SN
157  * anyway).
158  */
159 static int
dump_fit(char * page,unsigned long * fit)160 dump_fit(char *page, unsigned long *fit)
161 {
162 	unsigned long qw;
163 	int nentries;
164 	int fentry;
165 	char *p;
166 
167 	TRACE();
168 
169 	DPRINTK("dumping fit from %p\n", (void *)fit);
170 
171 	qw = readq(fit);
172 	DPRINTK("FIT signature: %016lx (%.8s)\n", qw, (char *)&qw);
173 	if (qw != FIT_SIGNATURE)
174 		printk(KERN_WARNING "Unrecognized FIT signature");
175 
176 	qw = readq(fit + 1);
177 	nentries = (unsigned)qw;
178 	DPRINTK("number of fit entries: %u\n", nentries);
179 	/* check that we won't overflow the page -- see comment above */
180 	BUG_ON(nentries * 60 > PAGE_SIZE);
181 
182 	p = page;
183 	for (fentry = 0; fentry < nentries; fentry++)
184 		/* each FIT entry is two 64 bit words */
185 		p += dump_fit_entry(p, fit + 2 * fentry);
186 
187 	return p - page;
188 }
189 
190 static int
dump_version(char * page,unsigned long * fit)191 dump_version(char *page, unsigned long *fit)
192 {
193 	int nentries;
194 	int fentry;
195 	unsigned long qw;
196 
197 	TRACE();
198 
199 	nentries = (unsigned)readq(fit + 1);
200 	BUG_ON(nentries * 60 > PAGE_SIZE);
201 
202 	for (fentry = 0; fentry < nentries; fentry++) {
203 		qw = readq(fit + 2 * fentry + 1);
204 		if (FIT_TYPE(qw) == FIT_ENTRY_SAL_A)
205 			return sprintf(page, "%x.%02x\n",
206 				       FIT_MAJOR(qw), FIT_MINOR(qw));
207 	}
208 	return 0;
209 }
210 
211 /* same as in proc_misc.c */
212 static int
proc_calc_metrics(char * page,char ** start,off_t off,int count,int * eof,int len)213 proc_calc_metrics(char *page, char **start, off_t off, int count, int *eof,
214 		  int len)
215 {
216 	if (len <= off+count) *eof = 1;
217 	*start = page + off;
218 	len -= off;
219 	if (len>count) len = count;
220 	if (len<0) len = 0;
221 	return len;
222 }
223 
224 static int
read_version_entry(char * page,char ** start,off_t off,int count,int * eof,void * data)225 read_version_entry(char *page, char **start, off_t off, int count, int *eof,
226 		   void *data)
227 {
228 	int len = 0;
229 
230 	MOD_INC_USE_COUNT;
231 	/* data holds the pointer to this node's FIT */
232 	len = dump_version(page, (unsigned long *)data);
233 	len = proc_calc_metrics(page, start, off, count, eof, len);
234 	MOD_DEC_USE_COUNT;
235 	return len;
236 }
237 
238 static int
read_fit_entry(char * page,char ** start,off_t off,int count,int * eof,void * data)239 read_fit_entry(char *page, char **start, off_t off, int count, int *eof,
240 	       void *data)
241 {
242 	int len = 0;
243 
244 	MOD_INC_USE_COUNT;
245 	/* data holds the pointer to this node's FIT */
246 	len = dump_fit(page, (unsigned long *)data);
247 	len = proc_calc_metrics(page, start, off, count, eof, len);
248 	MOD_DEC_USE_COUNT;
249 
250 	return len;
251 }
252 
253 /* this is a fake FIT that's used on the medusa simulator which
254  * doesn't usually run a complete PROM.
255  */
256 #ifdef CONFIG_IA64_SGI_SN_SIM
257 static unsigned long fakefit[] = {
258 	/* this is all we need to satisfy the code below */
259 	FIT_SIGNATURE,
260 	FIT_ENTRY(FIT_ENTRY_FIT_HEADER, 0x02, 0x60, 2),
261 	/* dump something arbitrary for
262 	 * /proc/sgi_prominfo/nodeX/version */
263 	0xbadbeef00fa3ef17ul,
264 	FIT_ENTRY(FIT_ENTRY_SAL_A, 0, 0x99, 0x100)
265 };
266 #endif
267 
268 static unsigned long *
lookup_fit(int nasid)269 lookup_fit(int nasid)
270 {
271 	unsigned long *fitp;
272 	unsigned long fit_paddr;
273 	unsigned long *fit_vaddr;
274 
275 #ifdef CONFIG_IA64_SGI_SN_SIM
276 	if (IS_RUNNING_ON_SIMULATOR())
277 		return fakefit;
278 #endif
279 
280 	fitp = (void *)GLOBAL_MMR_ADDR(nasid, LB_PROM_SPACE - 32);
281 	DPRINTK("pointer to fit at %p\n", (void *)fitp);
282 	fit_paddr = readq(fitp);
283 	DPRINTK("fit pointer contains %lx\n", fit_paddr);
284 	/* snag just the node-relative offset */
285 	fit_paddr &= ~0ul >> (63-35);
286 	/* the pointer to the FIT is relative to IA-64 compatibility
287 	 * space.  However, the PROM is mapped at a different offset
288 	 * in MMR space (both local and global)
289 	 */
290 	fit_paddr += 0x700000000;
291 	fit_vaddr = (void *)GLOBAL_MMR_ADDR(nasid, fit_paddr);
292 	DPRINTK("fit at %p\n", (void *)fit_vaddr);
293 	return fit_vaddr;
294 }
295 
296 /* module entry points */
297 int __init prominfo_init(void);
298 void __exit prominfo_exit(void);
299 
300 module_init(prominfo_init);
301 module_exit(prominfo_exit);
302 
303 static struct proc_dir_entry **proc_entries;
304 static struct proc_dir_entry *sgi_prominfo_entry;
305 
306 #define NODE_NAME_LEN 11
307 
308 int __init
prominfo_init(void)309 prominfo_init(void)
310 {
311 	struct proc_dir_entry **entp;
312 	cnodeid_t cnodeid;
313 	nasid_t nasid;
314 	char name[NODE_NAME_LEN];
315 
316 	if (!ia64_platform_is("sn2"))
317 		return 0;
318 
319 	TRACE();
320 
321 	DPRINTK("running on cpu %d\n", smp_processor_id());
322 	DPRINTK("numnodes %d\n", numnodes);
323 
324 	proc_entries = kmalloc(numnodes * sizeof(struct proc_dir_entry *),
325 			       GFP_KERNEL);
326 
327 	sgi_prominfo_entry = proc_mkdir("sgi_prominfo", NULL);
328 
329 	for (cnodeid = 0, entp = proc_entries;
330 	     cnodeid < numnodes;
331 	     cnodeid++, entp++) {
332 		sprintf(name, "node%d", cnodeid);
333 		*entp = proc_mkdir(name, sgi_prominfo_entry);
334 		nasid = cnodeid_to_nasid(cnodeid);
335 		create_proc_read_entry(
336 			"fit", 0, *entp, read_fit_entry,
337 			lookup_fit(nasid));
338 		create_proc_read_entry(
339 			"version", 0, *entp, read_version_entry,
340 			lookup_fit(nasid));
341 	}
342 
343 	return 0;
344 }
345 
346 void __exit
prominfo_exit(void)347 prominfo_exit(void)
348 {
349 	struct proc_dir_entry **entp;
350 	unsigned cnodeid;
351 	char name[NODE_NAME_LEN];
352 
353 	TRACE();
354 
355 	for (cnodeid = 0, entp = proc_entries;
356 	     cnodeid < numnodes;
357 	     cnodeid++, entp++) {
358 		remove_proc_entry("fit", *entp);
359 		remove_proc_entry("version", *entp);
360 		sprintf(name, "node%d", cnodeid);
361 		remove_proc_entry(name, sgi_prominfo_entry);
362 	}
363 	remove_proc_entry("sgi_prominfo", NULL);
364 	kfree(proc_entries);
365 }
366