Commit dfde8785 authored by Jesse Barnes's avatar Jesse Barnes Committed by Tony Luck

[IA64-SGI] sn2: serialize access to PROM chips

 
If we read and write the PROM chips at the same time, as might happen at boot
when salinfo extracts MCA records and a user is checking the PROM revision
in /proc/sgi_prominfo, an MCA might occur, since the PROM chips can't be
accessed that way.  This patch fixes the problem for systems with new PROMs
(>= 3.50) by using the SAL to do PROM reads.
Signed-off-by: default avatarJesse Barnes <jbarnes@sgi.com>
Signed-off-by: default avatarTony Luck <tony.luck@intel.com>
parent 970c091b
...@@ -14,50 +14,14 @@ ...@@ -14,50 +14,14 @@
#include <linux/proc_fs.h> #include <linux/proc_fs.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/sn/sn2/addrs.h> #include <asm/sn/sn_sal.h>
#include <asm/sn/simulator.h>
/* to lookup nasids */
#include <asm/sn/sn_cpuid.h> #include <asm/sn/sn_cpuid.h>
#include <asm/sn/sn2/addrs.h>
MODULE_DESCRIPTION("PROM version reporting for /proc"); MODULE_DESCRIPTION("PROM version reporting for /proc");
MODULE_AUTHOR("Chad Talbott"); MODULE_AUTHOR("Chad Talbott");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
#undef DEBUG_PROMINFO
#define TRACE_PROMINFO
#if defined(DEBUG_PROMINFO)
# define DPRINTK(x...) printk(KERN_DEBUG x)
#else
# define DPRINTK(x...)
#endif
#if defined(TRACE_PROMINFO) && defined(DEBUG_PROMINFO)
# if defined(__GNUC__)
# define TRACE() printk(KERN_DEBUG "%s:%d:%s\n", \
__FILE__, __LINE__, __FUNCTION__)
# else
# define TRACE() printk(KERN_DEBUG "%s:%d\n", __LINE__, __FILE__)
# endif
#else
# define TRACE()
#endif
/* Architected IA64 firmware space */
#define FW_BASE 0x00000000FF000000
#define FW_TOP 0x0000000100000000
/* Sub-regions determined by bits in Node Offset */
#define LB_PROM_SPACE 0x0000000700000000ul /* Local LB PROM */
/* Offset of PROM banner pointers in SAL A and SAL B */
#define SAL_A_BANNER_OFFSET (1 * 16)
#define SAL_B_BANNER_OFFSET (3 * 16)
#define FIT_SIGNATURE 0x2020205f5449465ful
/* Standard Intel FIT entry types */ /* Standard Intel FIT entry types */
#define FIT_ENTRY_FIT_HEADER 0x00 /* FIT header entry */ #define FIT_ENTRY_FIT_HEADER 0x00 /* FIT header entry */
#define FIT_ENTRY_PAL_B 0x01 /* PAL_B entry */ #define FIT_ENTRY_PAL_B 0x01 /* PAL_B entry */
...@@ -90,12 +54,6 @@ MODULE_LICENSE("GPL"); ...@@ -90,12 +54,6 @@ MODULE_LICENSE("GPL");
#define FIT_TYPE(q) \ #define FIT_TYPE(q) \
((unsigned) ((q) >> FIT_TYPE_SHIFT) & FIT_TYPE_MASK) ((unsigned) ((q) >> FIT_TYPE_SHIFT) & FIT_TYPE_MASK)
#define FIT_ENTRY(type, maj, min, size) \
((((unsigned long)(maj) & FIT_MAJOR_MASK) << FIT_MAJOR_SHIFT) | \
(((unsigned long)(min) & FIT_MINOR_MASK) << FIT_MINOR_SHIFT) | \
(((unsigned long)(type) & FIT_TYPE_MASK) << FIT_TYPE_SHIFT) | \
(size))
struct fit_type_map_t { struct fit_type_map_t {
unsigned char type; unsigned char type;
const char *name; const char *name;
...@@ -134,6 +92,39 @@ fit_type_name(unsigned char type) ...@@ -134,6 +92,39 @@ fit_type_name(unsigned char type)
return "Unknown type"; return "Unknown type";
} }
/* ============ BEGIN temp til old PROMs are no longer supported =============
*
* The OS should not make direct access to the PROM flash memory. Access to
* this region must be serialized with a PROM lock. If SAL on one cpu is
* updating the FLASH error log at the same time another cpu is accessing the
* PROM, data corruption will occur.
*
* To solve the problem, all flash PROM access has been moved to SAL. Because
* not all systems will have instant PROM updates, we need to support a new OS
* running on a system with old PROMs.
*
* This code should be deleted after 1 OS/PROM release has occurred & the OS
* no longer supports downrev PROMs. (PROM support should be in the 3.50
* PROMs).
*/
#define SUPPORT_OLD_PROMS
#ifdef SUPPORT_OLD_PROMS
#define FIT_SIGNATURE 0x2020205f5449465ful
/* Sub-regions determined by bits in Node Offset */
#define LB_PROM_SPACE 0x0000000700000000ul /* Local LB PROM */
/* Offset of PROM banner pointers in SAL A and SAL B */
#define SAL_A_BANNER_OFFSET (1 * 16)
#define SAL_B_BANNER_OFFSET (3 * 16)
/* Architected IA64 firmware space */
#define FW_BASE 0x00000000FF000000
#define FW_TOP 0x0000000100000000
static unsigned long static unsigned long
convert_fw_addr(nasid_t nasid, unsigned long addr) convert_fw_addr(nasid_t nasid, unsigned long addr)
{ {
...@@ -154,32 +145,95 @@ valid_fw_addr(unsigned long addr) ...@@ -154,32 +145,95 @@ valid_fw_addr(unsigned long addr)
return (addr >= FW_BASE && addr < FW_TOP); return (addr >= FW_BASE && addr < FW_TOP);
} }
/* These two routines read the FIT table directly from the FLASH PROM static unsigned long *
* on a specific node. The PROM can only be accessed using aligned 64 lookup_fit(int nasid)
* bit reads, so we do that and then shift and mask the result to get {
* at each field. unsigned long *fitp;
unsigned long fit_paddr;
unsigned long *fit_vaddr;
fitp = (void *)GLOBAL_MMR_ADDR(nasid, LB_PROM_SPACE - 32);
fit_paddr = readq(fitp);
fit_vaddr = (unsigned long *) convert_fw_addr(nasid, fit_paddr);
return fit_vaddr;
}
#endif /* SUPPORT_OLD_PROMS */
/* ============ END temp til old PROMs are no longer supported ============= */
static int
get_fit_entry(unsigned long nasid, int index, unsigned long *fentry,
char *banner, int banlen)
{
int ret;
ret = ia64_sn_get_fit_compt(nasid, index, fentry, banner, banlen);
#ifdef SUPPORT_OLD_PROMS
/* The following is hack is temporary until PROMs are updated */
if (ret == SALRET_NOT_IMPLEMENTED) {
unsigned long *fitadr = lookup_fit(nasid);
int nentries;
if (readq(fitadr) != FIT_SIGNATURE) {
printk(KERN_WARNING "Unrecognized FIT signature");
return -2;
}
nentries = (unsigned int) (readq(fitadr + 1) & 0xffffff);
if (index >= nentries)
return -2;
fentry[0] = readq(fitadr + 2 * index);
fentry[1] = readq(fitadr + 2 * index + 1);
ret = 0;
if (banner && FIT_TYPE(fentry[1]) == FIT_ENTRY_SAL_A) {
unsigned long i, qw, *bwp, *qwp;
banner[0] = '\0';
qw = fentry[0]; /* Address of SAL A */
if (!valid_fw_addr(qw))
return 0;
qw += SAL_A_BANNER_OFFSET;
qw = convert_fw_addr(nasid, qw);
qw = readq(qw); /* Address of banner */
if (!valid_fw_addr(qw))
return 0;
qw = convert_fw_addr(nasid, qw);
qwp = (unsigned long *) qw;
bwp = (unsigned long *) banner;
for (i=0; i<banlen/8; i++)
bwp[i] = qwp[i];
}
}
#endif /* SUPPORT_OLD_PROMS */
return ret;
}
/*
* These two routines display the FIT table for each node.
*/ */
static int static int
dump_fit_entry(char *page, unsigned long *fentry) dump_fit_entry(char *page, unsigned long *fentry)
{ {
unsigned long q1, q2;
unsigned type; unsigned type;
TRACE(); type = FIT_TYPE(fentry[1]);
q1 = readq(fentry);
q2 = readq(fentry + 1);
type = FIT_TYPE(q2);
return sprintf(page, "%02x %-25s %x.%02x %016lx %u\n", return sprintf(page, "%02x %-25s %x.%02x %016lx %u\n",
type, type,
fit_type_name(type), fit_type_name(type),
FIT_MAJOR(q2), FIT_MINOR(q2), FIT_MAJOR(fentry[1]), FIT_MINOR(fentry[1]),
q1, fentry[0],
/* mult by sixteen to get size in bytes */ /* mult by sixteen to get size in bytes */
(unsigned)q2 * 16); (unsigned)(fentry[1] & 0xffffff) * 16);
} }
/* We assume that the fit table will be small enough that we can print
/*
* We assume that the fit table will be small enough that we can print
* the whole thing into one page. (This is true for our default 16kB * the whole thing into one page. (This is true for our default 16kB
* pages -- each entry is about 60 chars wide when printed.) I read * pages -- each entry is about 60 chars wide when printed.) I read
* somewhere that the maximum size of the FIT is 128 entries, so we're * somewhere that the maximum size of the FIT is 128 entries, so we're
...@@ -187,77 +241,46 @@ dump_fit_entry(char *page, unsigned long *fentry) ...@@ -187,77 +241,46 @@ dump_fit_entry(char *page, unsigned long *fentry)
* anyway). * anyway).
*/ */
static int static int
dump_fit(char *page, unsigned long *fit) dump_fit(char *page, unsigned long nasid)
{ {
unsigned long qw; unsigned long fentry[2];
int nentries; int index;
int fentry;
char *p; char *p;
TRACE();
DPRINTK("dumping fit from %p\n", (void *)fit);
qw = readq(fit);
DPRINTK("FIT signature: %016lx (%.8s)\n", qw, (char *)&qw);
if (qw != FIT_SIGNATURE)
printk(KERN_WARNING "Unrecognized FIT signature");
qw = readq(fit + 1);
nentries = (unsigned)qw;
DPRINTK("number of fit entries: %u\n", nentries);
/* check that we won't overflow the page -- see comment above */
BUG_ON(nentries * 60 > PAGE_SIZE);
p = page; p = page;
for (fentry = 0; fentry < nentries; fentry++) for (index=0;;index++) {
/* each FIT entry is two 64 bit words */ BUG_ON(index * 60 > PAGE_SIZE);
p += dump_fit_entry(p, fit + 2 * fentry); if (get_fit_entry(nasid, index, fentry, NULL, 0))
break;
p += dump_fit_entry(p, fentry);
}
return p - page; return p - page;
} }
static int static int
dump_version(char *page, unsigned long *fit) dump_version(char *page, unsigned long nasid)
{ {
int nentries; unsigned long fentry[2];
int fentry; char banner[128];
unsigned long qw = 0; int index;
int len; int len;
nasid_t nasid = NASID_GET(fit);
TRACE();
nentries = (unsigned)readq(fit + 1); for (index = 0; ; index++) {
BUG_ON(nentries * 60 > PAGE_SIZE); if (get_fit_entry(nasid, index, fentry, banner,
sizeof(banner)))
for (fentry = 0; fentry < nentries; fentry++) { return 0;
qw = readq(fit + 2 * fentry + 1); if (FIT_TYPE(fentry[1]) == FIT_ENTRY_SAL_A)
if (FIT_TYPE(qw) == FIT_ENTRY_SAL_A)
break; break;
} }
if (fentry >= nentries)
return 0;
len = sprintf(page, "%x.%02x\n", FIT_MAJOR(qw), FIT_MINOR(qw)); len = sprintf(page, "%x.%02x\n", FIT_MAJOR(fentry[1]),
FIT_MINOR(fentry[1]));
page += len; page += len;
qw = readq(fit + 2 * fentry); /* Address of SAL A */ if (banner[0])
DPRINTK("SAL A at %p\n", (void *)qw); len += snprintf(page, PAGE_SIZE-len, "%s\n", banner);
if (!valid_fw_addr(qw))
return len;
qw += SAL_A_BANNER_OFFSET;
qw = convert_fw_addr(nasid, qw);
DPRINTK("Banner ptr at %p\n", (void *)qw);
qw = readq(qw); /* Address of banner */
if (!valid_fw_addr(qw))
return len;
qw = convert_fw_addr(nasid, qw);
DPRINTK("Banner at %p\n", (void *)qw);
len += snprintf(page, PAGE_SIZE-len, "%s\n", (char *)qw);
return len; return len;
} }
...@@ -280,8 +303,8 @@ read_version_entry(char *page, char **start, off_t off, int count, int *eof, ...@@ -280,8 +303,8 @@ read_version_entry(char *page, char **start, off_t off, int count, int *eof,
{ {
int len = 0; int len = 0;
/* data holds the pointer to this node's FIT */ /* data holds the NASID of the node */
len = dump_version(page, (unsigned long *)data); len = dump_version(page, (unsigned long)data);
len = proc_calc_metrics(page, start, off, count, eof, len); len = proc_calc_metrics(page, start, off, count, eof, len);
return len; return len;
} }
...@@ -292,52 +315,13 @@ read_fit_entry(char *page, char **start, off_t off, int count, int *eof, ...@@ -292,52 +315,13 @@ read_fit_entry(char *page, char **start, off_t off, int count, int *eof,
{ {
int len = 0; int len = 0;
/* data holds the pointer to this node's FIT */ /* data holds the NASID of the node */
len = dump_fit(page, (unsigned long *)data); len = dump_fit(page, (unsigned long)data);
len = proc_calc_metrics(page, start, off, count, eof, len); len = proc_calc_metrics(page, start, off, count, eof, len);
return len; return len;
} }
/* this is a fake FIT that's used on the medusa simulator which
* doesn't usually run a complete PROM.
*/
#ifdef CONFIG_IA64_SGI_SN_SIM
static unsigned long fakefit[] = {
/* this is all we need to satisfy the code below */
FIT_SIGNATURE,
FIT_ENTRY(FIT_ENTRY_FIT_HEADER, 0x02, 0x60, 2),
/* dump something arbitrary for
* /proc/sgi_prominfo/nodeX/version */
0xbadbeef00fa3ef17ul,
FIT_ENTRY(FIT_ENTRY_SAL_A, 0, 0x99, 0x100)
};
#endif
static unsigned long *
lookup_fit(int nasid)
{
unsigned long *fitp;
unsigned long fit_paddr;
unsigned long *fit_vaddr;
#ifdef CONFIG_IA64_SGI_SN_SIM
if (IS_RUNNING_ON_SIMULATOR())
return fakefit;
#endif
fitp = (void *)GLOBAL_MMR_ADDR(nasid, LB_PROM_SPACE - 32);
DPRINTK("pointer to fit at %p\n", (void *)fitp);
fit_paddr = readq(fitp);
DPRINTK("fit pointer contains %lx\n", fit_paddr);
BUG_ON(!valid_fw_addr(fit_paddr));
fit_vaddr = (void *)convert_fw_addr(nasid, fit_paddr);
DPRINTK("fit at %p\n", (void *)fit_vaddr);
return fit_vaddr;
}
/* module entry points */ /* module entry points */
int __init prominfo_init(void); int __init prominfo_init(void);
void __exit prominfo_exit(void); void __exit prominfo_exit(void);
...@@ -356,17 +340,12 @@ prominfo_init(void) ...@@ -356,17 +340,12 @@ prominfo_init(void)
struct proc_dir_entry **entp; struct proc_dir_entry **entp;
struct proc_dir_entry *p; struct proc_dir_entry *p;
cnodeid_t cnodeid; cnodeid_t cnodeid;
nasid_t nasid; unsigned long nasid;
char name[NODE_NAME_LEN]; char name[NODE_NAME_LEN];
if (!ia64_platform_is("sn2")) if (!ia64_platform_is("sn2"))
return 0; return 0;
TRACE();
DPRINTK("running on cpu %d\n", smp_processor_id());
DPRINTK("numnodes %d\n", numnodes);
proc_entries = kmalloc(numnodes * sizeof(struct proc_dir_entry *), proc_entries = kmalloc(numnodes * sizeof(struct proc_dir_entry *),
GFP_KERNEL); GFP_KERNEL);
...@@ -380,12 +359,12 @@ prominfo_init(void) ...@@ -380,12 +359,12 @@ prominfo_init(void)
nasid = cnodeid_to_nasid(cnodeid); nasid = cnodeid_to_nasid(cnodeid);
p = create_proc_read_entry( p = create_proc_read_entry(
"fit", 0, *entp, read_fit_entry, "fit", 0, *entp, read_fit_entry,
lookup_fit(nasid)); (void *)nasid);
if (p) if (p)
p->owner = THIS_MODULE; p->owner = THIS_MODULE;
p = create_proc_read_entry( p = create_proc_read_entry(
"version", 0, *entp, read_version_entry, "version", 0, *entp, read_version_entry,
lookup_fit(nasid)); (void *)nasid);
if (p) if (p)
p->owner = THIS_MODULE; p->owner = THIS_MODULE;
} }
...@@ -400,8 +379,6 @@ prominfo_exit(void) ...@@ -400,8 +379,6 @@ prominfo_exit(void)
unsigned cnodeid; unsigned cnodeid;
char name[NODE_NAME_LEN]; char name[NODE_NAME_LEN];
TRACE();
for (cnodeid = 0, entp = proc_entries; for (cnodeid = 0, entp = proc_entries;
cnodeid < numnodes; cnodeid < numnodes;
cnodeid++, entp++) { cnodeid++, entp++) {
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#define SN_SAL_NO_FAULT_ZONE_PHYSICAL 0x02000011 #define SN_SAL_NO_FAULT_ZONE_PHYSICAL 0x02000011
#define SN_SAL_PRINT_ERROR 0x02000012 #define SN_SAL_PRINT_ERROR 0x02000012
#define SN_SAL_SET_ERROR_HANDLING_FEATURES 0x0200001a // reentrant #define SN_SAL_SET_ERROR_HANDLING_FEATURES 0x0200001a // reentrant
#define SN_SAL_GET_FIT_COMPT 0x0200001b // reentrant
#define SN_SAL_CONSOLE_PUTC 0x02000021 #define SN_SAL_CONSOLE_PUTC 0x02000021
#define SN_SAL_CONSOLE_GETC 0x02000022 #define SN_SAL_CONSOLE_GETC 0x02000022
#define SN_SAL_CONSOLE_PUTS 0x02000023 #define SN_SAL_CONSOLE_PUTS 0x02000023
...@@ -107,12 +108,13 @@ ...@@ -107,12 +108,13 @@
/* /*
* SN_SAL_GET_PARTITION_ADDR return constants * SAL Error Codes
*/ */
#define SALRET_MORE_PASSES 1 #define SALRET_MORE_PASSES 1
#define SALRET_OK 0 #define SALRET_OK 0
#define SALRET_INVALID_ARG -2 #define SALRET_NOT_IMPLEMENTED (-1)
#define SALRET_ERROR -3 #define SALRET_INVALID_ARG (-2)
#define SALRET_ERROR (-3)
/* /*
* SN_SAL_SET_ERROR_HANDLING_FEATURES bit settings * SN_SAL_SET_ERROR_HANDLING_FEATURES bit settings
...@@ -829,6 +831,34 @@ ia64_sn_irtr_intr_disable(nasid_t nasid, int subch, u64 intr) ...@@ -829,6 +831,34 @@ ia64_sn_irtr_intr_disable(nasid_t nasid, int subch, u64 intr)
return (int) rv.v0; return (int) rv.v0;
} }
/**
* ia64_sn_get_fit_compt - read a FIT entry from the PROM header
* @nasid: NASID of node to read
* @index: FIT entry index to be retrieved (0..n)
* @fitentry: 16 byte buffer where FIT entry will be stored.
* @banbuf: optional buffer for retrieving banner
* @banlen: length of banner buffer
*
* Access to the physical PROM chips needs to be serialized since reads and
* writes can't occur at the same time, so we need to call into the SAL when
* we want to look at the FIT entries on the chips.
*
* Returns:
* %SALRET_OK if ok
* %SALRET_INVALID_ARG if index too big
* %SALRET_NOT_IMPLEMENTED if running on older PROM
* ??? if nasid invalid OR banner buffer not large enough
*/
static inline int
ia64_sn_get_fit_compt(u64 nasid, u64 index, void *fitentry, void *banbuf,
u64 banlen)
{
struct ia64_sal_retval rv;
SAL_CALL_NOLOCK(rv, SN_SAL_GET_FIT_COMPT, nasid, index, fitentry,
banbuf, banlen, 0, 0);
return (int) rv.status;
}
/* /*
* Initialize the SAL components of the system controller * Initialize the SAL components of the system controller
* communication driver; specifically pass in a sizable buffer that * communication driver; specifically pass in a sizable buffer that
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment