Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 12 Mar 2001 14:01:54 -0800
From:      Chris Sears <cbsears@ix.netcom.com>
To:        freebsd-hackers@FreeBSD.org
Subject:   ecc kld for FreeBSD 4.2
Message-ID:  <3AAD4752.9C40CE34@ix.netcom.com>

next in thread | raw e-mail | index | archive | help
This is a multi-part message in MIME format.
--------------C829E1A7414D319B6C2A655A
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit


Linux has some support for ECC error detection:

        http://www.anime.net/~goemon/linux-ecc/

I've ported ECC 0.12 to a FreeBSD kld and it seems to work.

A  couple of minor changes:

   commented out probe_450gx because the compiler was
   giving some plausible warnings

   check if ecc_mode == ECC_NONE before installing the timer

I've attached it and would welcome any comments. I've also
posted it back to the Linux ECC people.

(sorry, the previous post didn't include the attachments)


Chris Sears
cbsears@ix.netcom.com
--------------C829E1A7414D319B6C2A655A
Content-Type: text/plain; charset=us-ascii;
 name="ecc.c"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="ecc.c"

/*
 * ECC kernel module (C) 1998, 1999 Dan Hollis <goemon at anime dot net>
 * Portions thanks to
 *	Michael O'Reilly <michael at metal dot iinet dot net dot au>
 *	Osma Ahvenlampi <oa at spray dot fi>
 *	Martin Maney <maney at pobox dot com>
 * Ported to FreeBSD 4.2
 *	Chris Sears
 */

#define	ECC_VER	"0.13 (Mar 8 2001)"

#ifdef linux
#define __KERNEL__
#define MODULE
#define DEBUG	0

#include <linux/mm.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/timer.h>
#include <asm/io.h>
#include <linux/proc_fs.h>

static struct timer_list ecctimer;
static struct pci_dev *bridge = NULL;

void		checkecc(void);
void		cleanup_module(void);
int		init_module(void);
#endif

#ifdef __FreeBSD__
#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/module.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/conf.h>
#include <sys/bus_private.h>

#include <pci/pcivar.h>
#include <pci/pcireg.h>
#include <i386/isa/pcibus.h>

#define u8  u_int8_t
#define u16 u_int16_t
#define u32 u_int32_t

static pcicfgregs		pci_cfg;
static struct callout_handle	ecc_ch = CALLOUT_HANDLE_INITIALIZER(&ecc_ch);

static int	ecc_modevent(module_t mod, int type, void* data);
static void	ecc_check(void*);
#endif

void		generic_check(void);
void		probe_via(void);
void		probe_450gx(void);
void		probe_440bx(void);
void		probe_440lx(void);
void		probe_440fx(void);
void		probe_430hx(void);
void		check_amd751(void);
void		probe_amd751(void);
void		probe_sis(void);
void		probe_aladdin4(void);
void		probe_aladdin5(void);
int		find_chipset(void);
unsigned int	pci_byte(int offset);
unsigned int	pci_word(int offset);
unsigned int	pci_dword(int offset);

static u16 vendor, device;
static int scrub_needed;
static int scrub_row;

#ifdef __FreeBSD__
#define pci_read_config_byte(bridge, offset, addr)	\
		*(addr) = pci_cfgread(&pci_cfg, (offset), 1)
#define pci_read_config_word(bridge, offset, addr)	\
		*(addr) = pci_cfgread(&pci_cfg, (offset), 2)
#define pci_read_config_dword(bridge, offset, addr)	\
		*(addr) = pci_cfgread(&pci_cfg, (offset), 4)

#define pci_write_config_byte(bridge, offset, value)	\
		pci_cfgwrite(&pci_cfg, (offset), (value), 1)
#define pci_write_config_word(bridge, offset, value)	\
		pci_cfgwrite(&pci_cfg, (offset), (value), 2)
#define pci_write_config_dword(bridge, offset, value)	\
		pci_cfgwrite(&pci_cfg, (offset), (value), 4)

#define printk printf
#endif

/* memory types */
#define BANK_EMPTY	0	/* Empty bank */
#define BANK_RESERVED	1	/* Reserved bank type */
#define BANK_FPM	2	/* Fast page mode */
#define BANK_EDO	3	/* Extended data out */
#define BANK_BEDO	4	/* Burst Extended data out */
#define BANK_SDR	5	/* Single data rate SDRAM */
#define BANK_DDR	6	/* Double data rate SDRAM */
#define BANK_RDR	7	/* Registered SDRAM */

/*
 * Memory bank info
 */
static struct bankstruct
{
	u32 endaddr;		/* bank ending address */
	u8 mbecount;		/* total number of MBE errors */
	u8 sbecount;		/* total number of SBE errors */
	u8 eccmode;		/* ECC enabled for this bank? */
	u8 mtype;		/* memory bank type */
} bank[8];			/* do any chipsets support more? */

/* chipset ECC capabilities and mode */
#define ECC_NONE	0	/* Doesnt support ECC (or is BIOS disabled) */
#define ECC_RESERVED	1	/* Reserved ECC type */
#define ECC_PARITY	2	/* Detects parity errors */
#define ECC_DETECT	3	/* Detects ECC errors */
#define ECC_CORRECT	4	/* Detects ECC errors and corrects SBE */
#define ECC_AUTO	5	/* Detects ECC errors and has hardware scrubber */

static struct ChipsetInfo
{
	int ecc_cap;		/* chipset ECC capabilities */
	int ecc_mode;		/* current ECC mode */
	void (*check)(void);	/* pointer to ecc checking routine */
#if 0
/*
 * I dont think we care about SERR at the moment.
 * We may if/when we hook into an NMI handler.
 */
	int SERR;		/* SERR enabled? */
	int SERR_MBE;		/* SERR on multi-bit error? */
	int SERR_SBE;		/* SERR on single-bit error? */
#endif
	int MBE_flag_address;	/* pci offset for mbe register */
	int MBE_flag_shift;	/* bits to shift for mbe flag */
	int MBE_flag_mask;	/* mask for mbe flag */
	int MBE_row_shift;	/* bits to shift for mbe row flag */
	int MBE_row_mask;	/* mask for mbe register (shifted) */
	int SBE_flag_address;	/* pci offset for sbe register */
	int SBE_flag_shift;	/* bits to shift for sbe flag */
	int SBE_flag_mask;	/* mask for sbe flag */
	int SBE_row_shift;	/* bits to shift for sbe row flag */	
	int SBE_row_mask;	/* mask for sbe register (shifted) */
	int MBE_err_address1;	/* pci offset for mbe address register */
	int MBE_err_shift1;	/* bits to shift for mbe address register */
	int MBE_err_address2;	/* pci offset for mbe address register */
	int MBE_err_shift2;	/* bits to shift for mbe address register */
	u32 MBE_err_mask;	/* mask for mbe address register */
	int MBE_err_flag;	/* MBE error flag */
	int MBE_err_row;	/* MBE row */
	u32 MBE_addr;		/* address of last MBE */
	int SBE_err_address1;	/* pci offset for mbe address register */
	int SBE_err_shift1;	/* bits to shift for mbe address register */
	int SBE_err_address2;	/* pci offset for mbe address register */
	int SBE_err_shift2;	/* bits to shift for mbe address register */
	u32 SBE_err_mask;	/* mask for mbe address register */
	int SBE_err_flag;	/* SBE error flag */
	int SBE_err_row;	/* SBE row */
	u32 SBE_addr;		/* address of last SBE */
} cs;

unsigned int pci_byte(int offset)
{
	u8 value;
	pci_read_config_byte(bridge, offset, &value);
	return value & 0xFF;
}

unsigned int pci_word(int offset)
{
	u16 value;
	pci_read_config_word(bridge, offset, &value);
	return value;
}

unsigned int pci_dword(int offset)
{
	u32 value;
	pci_read_config_dword(bridge, offset, &value);
	return value;
}

/*
 *	generic ECC check routine
 *
 *	This routine assumes that the MBE and SBE error status consist of:
 *	 * one or more bits in a status byte that are non-zero on error
 *	 * zero or more bits in a status byte that encode the row
 *	It accomodates both the case where both the MBE and SBE data are
 *	packed into a single byte (all chipsets currently known to me) as
 *	well as the case where the MBE and SBE information are contained in
 *	separate locations.  The status byte is read only once for the packed
 *	case in case the status value should be altered by being read.
 */
void generic_check(void)
{
	int status = pci_byte(cs.MBE_flag_address);
	if ((status >> cs.MBE_flag_shift) & cs.MBE_flag_mask)
	{
		int row = (status >> cs.MBE_row_shift) & cs.MBE_row_mask;
		printk("<1>ECC: MBE detected in DRAM row %d\n", row);
		if (cs.MBE_err_address1)
		{
			cs.MBE_addr =
			( pci_word(cs.MBE_err_address1 << cs.MBE_err_shift1) |
			  pci_word(cs.MBE_err_address2 << cs.MBE_err_shift2) ) &
			  cs.MBE_err_mask;
			printk("<1>ECC: MBE at memory address %lx\n", (long unsigned int)cs.MBE_addr);
		}
		scrub_needed = 2;
		scrub_row = row;
		bank[row].mbecount++;
	}
	if (cs.SBE_flag_address != cs.MBE_flag_address)
		status = pci_byte(cs.SBE_flag_address);
	if ((status >> cs.SBE_flag_shift) & cs.SBE_flag_mask)
	{
		int row = (status >> cs.SBE_row_shift) & cs.SBE_row_mask;
		printk("<1>ECC: SBE detected in DRAM row %d\n", row);
		if (cs.SBE_err_address1)
		{
			cs.SBE_addr =
			( pci_word(cs.SBE_err_address1 << cs.SBE_err_shift1) |
			  pci_word(cs.SBE_err_address2 << cs.SBE_err_shift2) ) &
			  cs.SBE_err_mask;
			printk("<1>ECC: SBE at memory address %lx\n", (long unsigned int)cs.SBE_addr);
		}
		scrub_needed = 1;
		scrub_row = row;
		bank[row].sbecount++;
	}
}

/* unified VIA probe */
void probe_via(void)
{
	int loop, ecc_ctrl, dimmslots = 3, bankshift = 23;
	int m_mem[] = { BANK_FPM, BANK_EDO, BANK_DDR, BANK_SDR };
	switch (device) {
		case 0x0305: /* VIA VT8363   - KT133		*/
		case 0x0391: /* VIA VT8371   - KX133		*/
			dimmslots = 4;
			bankshift = 24;
			bank[6].endaddr=(unsigned long)pci_byte(0x56)<<24;
			bank[7].endaddr=(unsigned long)pci_byte(0x57)<<24;
		case 0x0595: /* VIA VT82C595 - VP2,VP2/97	*/
			m_mem[2] = BANK_RESERVED;
			cs.ecc_cap = ECC_CORRECT;
			break;
		case 0x0501: /* VIA VT8501   - MVP4		*/
		case 0x0597: /* VIA VT82C597 - VP3		*/
		case 0x0598: /* VIA VT82C598 - MVP3		*/
		case 0x0691: /* VIA VT82C691 - Apollo PRO 	*/
		case 0x0693: /* VIA VT82C693 - Apollo PRO-Plus 	*/
			cs.ecc_cap = ECC_CORRECT;
			break;
		case 0x0585: /* VIA VT82C585 - VP,VPX,VPX/97	*/
		default:
			cs.ecc_cap = ECC_NONE;
			return;
	}
	ecc_ctrl = pci_byte(0x6E);
	cs.ecc_mode = (ecc_ctrl>>7)&1 ? ECC_CORRECT : ECC_NONE;

	cs.check = generic_check;
	cs.MBE_flag_address = 0x6F;
	cs.MBE_flag_shift = 7;
	cs.MBE_flag_mask = 1;
	cs.MBE_row_shift = 4;
	cs.MBE_row_mask = 7;
	cs.SBE_flag_address = 0x6F;
	cs.SBE_flag_shift = 3;
	cs.SBE_flag_mask = 1;
	cs.SBE_row_shift = 0;
	cs.SBE_row_mask = 7;

	for(loop=0;loop<6;loop++)
                bank[loop].endaddr = (unsigned long)pci_byte(0x5a+loop)<<bankshift;
	for(loop=0;loop<dimmslots;loop++)
	{
		bank[loop*2].mtype = m_mem[(pci_byte(0x60)>>(loop*2))&3];
		bank[(loop*2)+1].mtype = m_mem[(pci_byte(0x60)>>(loop*2))&3];
		bank[loop*2].eccmode = (ecc_ctrl>>(loop))&1;
		bank[(loop*2)+1].eccmode = (ecc_ctrl>>(loop))&1;
	}
}

/*
 * 450gx probing is buggered at the moment.
 */
void probe_450gx(void)
{
#if 0
	int loop, dramc, merrcmd;
	u32 nbxcfg;
	int m_mem[] = { BANK_EDO, BANK_SDR, BANK_RDR, BANK_RESERVED };
	int ddim[] = { ECC_NONE, ECC_DETECT, ECC_CORRECT, ECC_AUTO };
/*	nbxcfg = pci_word(0x50) | (pci_word(0x52)<<16); */
/*	dramc = pci_byte(0x57); */
	merrcmd = pci_word(0xC0);
/*	printk("<1>DRAM Data Integrity Mode : %s\n", ddim[(nbxcfg>>7)&3]); */
	for(loop=0;loop<8;loop++)
	{
		bank[loop].endaddr=(unsigned long)pci_byte(0x60+loop)<<23;
		/* 450gx doesnt allow mixing memory types. bleah. */
		bank[loop].mtype = m_mem[(dramc>>3)&3];
		/* yes, bit is _zero_ if ecc is _enabled_. */
		bank[loop].eccmode = !((nbxcfg>>(loop+24))&1);
	}
	cs.ecc_cap = ECC_AUTO;
/*	cs.ecc_mode = (merrcmd>>1)&1 ? ECC_AUTO : ECC_DETECT;
	printk("<1>Correction of SBE %s\n", (merrcmd>>1)&1 ? "Enabled" : "Disabled"); */

        cs.check = generic_check;
        cs.MBE_flag_address = 0xC2;
        cs.MBE_flag_shift = 0;
        cs.MBE_flag_mask = 1;
        cs.MBE_row_shift = 5;
        cs.MBE_row_mask = 7;
        cs.SBE_flag_address = 0xC2;
        cs.SBE_flag_shift = 1;
        cs.SBE_flag_mask = 1;
        cs.SBE_row_shift = 1;
        cs.SBE_row_mask = 7;

	cs.MBE_err_address1 = 0xA8;
	cs.MBE_err_shift1 = 0;
	cs.MBE_err_address2 = 0xAA;
	cs.MBE_err_shift2 = 16;
	cs.MBE_err_mask = 0xFFFFFFFC;

	cs.SBE_err_address1 = 0x74;
	cs.SBE_err_shift1 = 0;
	cs.SBE_err_address2 = 0x76;
	cs.SBE_err_shift2 = 16;
	cs.SBE_err_mask = 0xFFFFFFFC;
#endif
}

/* there seems to be NO WAY to distinguish 440zx from 440bx!! >B( */
void probe_440bx(void)
{
	int loop, dramc, errcmd;
	u32 nbxcfg;
	int m_mem[] = { BANK_EDO, BANK_SDR, BANK_RDR, BANK_RESERVED };
	int ddim[] = { ECC_NONE, ECC_DETECT, ECC_CORRECT, ECC_AUTO };
	nbxcfg = pci_word(0x50) | (pci_word(0x52)<<16);
	dramc = pci_byte(0x57);
	errcmd = pci_byte(0x90);
	cs.ecc_cap = ECC_AUTO;
	cs.ecc_mode = ddim[(nbxcfg>>7)&3];

        cs.check = generic_check;
        cs.MBE_flag_address = 0x91;
        cs.MBE_flag_shift = 4;
        cs.MBE_flag_mask = 1;
        cs.MBE_row_shift = 5;
        cs.MBE_row_mask = 7;
        cs.SBE_flag_address = 0x91;
        cs.SBE_flag_shift = 0;
        cs.SBE_flag_mask = 1;
        cs.SBE_row_shift = 1;
        cs.SBE_row_mask = 7;

	cs.MBE_err_address1 = 80;
	cs.MBE_err_shift1 = 0;
	cs.MBE_err_address2 = 82;
	cs.MBE_err_shift2 = 16;
	cs.MBE_err_mask = 0xFFFFF000;

	cs.SBE_err_address1 = 80;
	cs.SBE_err_shift1 = 0;
	cs.SBE_err_address2 = 82;
	cs.SBE_err_shift2 = 16;
	cs.SBE_err_mask = 0xFFFFF000;

	for(loop=0;loop<8;loop++)
	{
		bank[loop].endaddr=(unsigned long)pci_byte(0x60+loop)<<23;
		/* 440bx doesnt allow mixing memory types. bleah. */
		bank[loop].mtype = m_mem[(dramc>>3)&3];
		/* yes, bit is _zero_ if ecc is _enabled_. */
		bank[loop].eccmode = !((nbxcfg>>(loop+24))&1);
	}
}

/* no way to tell 440ex from 440lx!? grr. */
void probe_440lx(void)
{
	int loop, drt, paccfg, errcmd;
	int m_mem[] = { BANK_EDO, BANK_RESERVED, BANK_SDR, BANK_EMPTY };
	int ddim[] = { ECC_NONE, ECC_DETECT, ECC_RESERVED, ECC_CORRECT } ;
	paccfg = pci_word(0x50);
	drt = pci_byte(0x55) | (pci_byte(0x56)<<8);
	errcmd = pci_byte(0x90);
	/* 440ex doesnt support ecc, but no way to tell if its 440ex! */
	cs.ecc_cap = ECC_CORRECT;
	cs.ecc_mode = ddim[(paccfg>>7)&3];

        cs.check = generic_check;
        cs.MBE_flag_address = 0x91;
        cs.MBE_flag_shift = 4;
        cs.MBE_flag_mask = 1;
        cs.MBE_row_shift = 5;
        cs.MBE_row_mask = 7;
        cs.SBE_flag_address = 0x91;
        cs.SBE_flag_shift = 0;
        cs.SBE_flag_mask = 1;
        cs.SBE_row_shift = 1;
        cs.SBE_row_mask = 7;

	for(loop=0;loop<8;loop++)
	{
		bank[loop].endaddr = (unsigned long)pci_byte(0x60+loop)<<23;
		bank[loop].mtype = m_mem[(drt>>(loop*2))&3];
		bank[loop].eccmode = (cs.ecc_mode != 0);
	}
}

void probe_440fx(void)
{
	int loop, drt, pmccfg, errcmd;
	int m_mem[] = { BANK_FPM, BANK_EDO, BANK_BEDO, BANK_EMPTY };
	int ddim[] = { ECC_NONE, ECC_PARITY, ECC_DETECT, ECC_CORRECT };
	pmccfg = pci_word(0x50);
	drt = pci_byte(0x55) | (pci_byte(0x56)<<8);
	errcmd = pci_byte(0x90);
	for(loop=0;loop<8;loop++)
	{
		bank[loop].endaddr=(unsigned long)pci_byte(0x60+loop)<<23;
		bank[loop].mtype = m_mem[(drt>>(loop*2))&3];
	}
	cs.ecc_cap = ECC_CORRECT;
	cs.ecc_mode = ddim[(pmccfg>>4)&3];

        cs.check = generic_check;
        cs.MBE_flag_address = 0x91;
        cs.MBE_flag_shift = 4;
        cs.MBE_flag_mask = 1;
        cs.MBE_row_shift = 5;
        cs.MBE_row_mask = 7;
        cs.SBE_flag_address = 0x91;
        cs.SBE_flag_shift = 0;
        cs.SBE_flag_mask = 1;
        cs.SBE_row_shift = 1;
        cs.SBE_row_mask = 7;
}

void probe_430hx(void)
{
	int pcicmd, pcon, errcmd, drt, loop;
	pcicmd = pci_word(0x4);
	pcon = pci_byte(0x50);
	drt = pci_byte(0x68);
	errcmd = pci_byte(0x90);
	cs.ecc_cap = ECC_CORRECT;
	cs.ecc_mode = (pcon>>7)&1 ? ECC_CORRECT : ECC_PARITY;

        cs.check = generic_check;
        cs.MBE_flag_address = 0x91;
        cs.MBE_flag_shift = 4;
        cs.MBE_flag_mask = 1;
        cs.MBE_row_shift = 5;
        cs.MBE_row_mask = 7;
        cs.SBE_flag_address = 0x91;
        cs.SBE_flag_shift = 0;
        cs.SBE_flag_mask = 1;
        cs.SBE_row_shift = 1;
        cs.SBE_row_mask = 7;

	for(loop=0;loop<8;loop++)
	{
		bank[loop].endaddr=(unsigned long)pci_byte(0x60+loop)<<22;
		bank[loop].mtype = (drt>>loop)&1 ? BANK_EDO : BANK_FPM;
		bank[loop].eccmode = cs.ecc_mode;
	}
}

void check_amd751(void)
{
	int eccstat = pci_word(0x58);
	if(((eccstat>>8)&3) == 1)
	{
/* bits 5-0 of eccstat indicate the row the ecc error occurred.
   bit 0 = row 0, bit 1 = row 1 etc */
		int row = 0;
		printk("<1>ECC: MBE Detected in DRAM row %d\n", row);
		scrub_needed=2;
		bank[row].mbecount++;
	}
	if(((eccstat>>8)&3) == 2)
	{
		int row = 0;
		printk("<1>ECC: SBE Detected in DRAM row %d\n", row);
		scrub_needed=1;
		bank[row].sbecount++;
	}
	if(((eccstat>>8)&3) == 3)
	{
		int row = 0;
		printk("<1>ECC: SBE and MBE Detected in DRAM row %d\n", row);
		scrub_needed=1;
		bank[row].sbecount++;
	}
	if (scrub_needed)
	{
		/*
		 * clear error flag bits that were set by writing 0 to them
		 * we hope the error was a fluke or something :)
		 */
		int value = eccstat & 0xFCFF;
		pci_write_config_word(bridge, 0x58, value);
		scrub_needed = 0;
        }
}

/*
 * The 751 appears to be a bit of a rush job. It seems to only support
 * pc100 sdram, and no memory mixing is allowed eg its all ecc ram or none.
 */
void probe_amd751(void)
{
	int loop;
	cs.ecc_cap = ECC_CORRECT;
	cs.ecc_mode = (pci_byte(0x5a)>>2)&1 ? ECC_CORRECT : ECC_NONE;
	cs.check = check_amd751;
	for(loop=0;loop<6;loop++)
	{
		bank[loop].endaddr=(unsigned long)pci_byte(0x41+(loop*2))<<23;
		/* suprisingly, only pc100 sdram appears to be supported */
		bank[loop].mtype = pci_byte(0x40+(loop*2))&1 ? BANK_SDR : BANK_EMPTY;
		/* no per-bank register, assumed same for all banks? */
		bank[loop].eccmode = (pci_byte(0x5a)>>2)&1;
	}
}

/* SiS */
void probe_sis(void)
{
	int loop;
	u32 endaddr;
	int m_mem[] = { BANK_FPM, BANK_EDO, BANK_RESERVED, BANK_SDR };
	int dramsize[] = { 256, 1024, 4096, 16384, 1024, 2048, 4096,
		8192, 512, 1024, 2048, 0, 0, 0, 0, 0 };
	int sdramsize[] = { 1024, 4096, 4096, 8192, 2048, 8192,
		8192, 16384, 4096, 16384, 16384, 32768, 2048, 0, 0, 0 };

	cs.ecc_cap = ECC_CORRECT;

        cs.check = generic_check;

        cs.MBE_flag_address = 0x64;
        cs.MBE_flag_shift = 4;
        cs.MBE_flag_mask = 1;
        cs.MBE_row_shift = 5;
        cs.MBE_row_mask = 7;

        cs.SBE_flag_address = 0x64;
        cs.SBE_flag_shift = 3;
        cs.SBE_flag_mask = 1;
        cs.SBE_row_shift = 5;
        cs.SBE_row_mask = 7;

	cs.MBE_err_address1 = 0x64;
	cs.MBE_err_shift1 = 0;
	cs.MBE_err_address2 = 0x66;
	cs.MBE_err_shift2 = 16;
	cs.MBE_err_mask = 0xFFFFF000;

	cs.SBE_err_address1 = 0x64;
	cs.SBE_err_shift1 = 0;
	cs.SBE_err_address2 = 0x66;
	cs.SBE_err_shift2 = 16;
	cs.MBE_err_mask = 0xFFFFF000;

	endaddr = 0;
	for(loop=0;loop<3;loop++)
	{
		/* populated bank? */
		if ((pci_byte(0x63)>>loop)&1)
		{
			u32 banksize;
			int mtype = pci_byte(0x60+loop);

			bank[loop*2].mtype = m_mem[(mtype>>6)&3];
			if(bank[loop*2].mtype == BANK_SDR)
			{
				banksize = sdramsize[mtype&15]*1024;
			} else {
				banksize = dramsize[mtype&15]*1024;
			}
			endaddr += banksize;
			bank[loop*2].endaddr = endaddr;
			/* double sided dimm? */
			if ((mtype>>5)&1)
			{
				bank[(loop*2)+1].mtype = bank[loop*2].mtype;
				endaddr += banksize;
				bank[(loop*2)+1].endaddr = endaddr;
			}
		} else {
			bank[loop*2].mtype = BANK_EMPTY;
			bank[(loop*2)+1].mtype = BANK_EMPTY;
			bank[loop*2].endaddr = endaddr;
			bank[(loop*2)+1].endaddr = endaddr;
		}
	}
	cs.ecc_mode = ECC_NONE;
	for(loop=0;loop<6;loop++)
	{
		int eccmode = (pci_byte(0x74)>>loop)&1;
		bank[loop].eccmode = eccmode;
		if(eccmode)
			cs.ecc_mode = ECC_CORRECT;
	}
}

/* ALi */
void probe_aladdin4(void)
{
	int loop;
	int m_mem[] = { BANK_FPM, BANK_EDO, BANK_RESERVED, BANK_SDR };
	cs.ecc_cap = ECC_CORRECT;
	cs.ecc_mode = pci_byte(0x49)&1 ? ECC_CORRECT : ECC_PARITY;

        cs.check = generic_check;

        cs.MBE_flag_address = 0x4a;
        cs.MBE_flag_shift = 4;
        cs.MBE_flag_mask = 1;
        cs.MBE_row_shift = 5;
        cs.MBE_row_mask = 7;

        cs.SBE_flag_address = 0x4a;
        cs.SBE_flag_shift = 0;
        cs.SBE_flag_mask = 1;
        cs.SBE_row_shift = 1;
        cs.SBE_row_mask = 7;

	for(loop=0;loop<8;loop++)
	{
		bank[loop].endaddr = (unsigned long)(pci_byte(0x61+(loop*2))&15)<<27|(pci_byte(0x60+(loop*2))<<20);
		bank[loop].mtype = m_mem[(pci_byte(0x61+(loop*2))>>1)&3];
		if (cs.ecc_mode == ECC_CORRECT) {
			bank[loop].eccmode = 1;
		} else {
			bank[loop].eccmode = 0;
		}
	}
}

void probe_aladdin5(void)
{
	int loop;
	int m_mem[] = { BANK_FPM, BANK_EDO, BANK_RDR, BANK_SDR };
	cs.ecc_cap = ECC_CORRECT;
	cs.ecc_mode = pci_byte(0x50)&1 ? ECC_CORRECT : ECC_PARITY;

        cs.check = generic_check;

        cs.MBE_flag_address = 0x51;
        cs.MBE_flag_shift = 4;
        cs.MBE_flag_mask = 1;
        cs.MBE_row_shift = 5;
        cs.MBE_row_mask = 7;

        cs.SBE_flag_address = 0x51;
        cs.SBE_flag_shift = 0;
        cs.SBE_flag_mask = 1;
        cs.SBE_row_shift = 1;
        cs.SBE_row_mask = 7;

	for(loop=0;loop<8;loop++)
	{
		/* DBxCII not disabled address mapping? */
		if(pci_byte(0x61+(loop*2))&0xF0)
		{
			/* endaddr always 1 unit low, granularity 1mb */
			bank[loop].endaddr = (unsigned long)((pci_byte(0x61+(loop*2))&15)<<27|(pci_byte(0x60+(loop*2))<<20))+1048576;
			bank[loop].mtype = m_mem[(pci_byte(0x61+(loop*2))>>1)&3];
			if (cs.ecc_mode == ECC_CORRECT) {
				bank[loop].eccmode = 1;
			} else {
				bank[loop].eccmode = 0;
			}
		}
	}
}

#if 0
/*
 * memory scrubber routines, not ready to be used yet...
 */
/* start at 16mb */
unsigned long start = 4096;
unsigned long pages = 1;
/* other architectures have different page sizes... */
unsigned long step = 4096;

char buff[8192] = {0,};

/*
 * Michael's page scrubber routine
 */
void scrub_page(unsigned long volatile * p)
{
	int i;
	int len, err = 0;
	unsigned long *q;
	q = (unsigned long *) ((((int)buff)+4095) & ~4095);

	if (((int)p) >= 640 * 1024 && ((int)p) < 1024 * 1024)
		return;

	cli();	/* kill interrupts */
	err = pci_byte(0x91);
	outb(0x11, PCI_DATA + 1); /* clear the memory error indicator */
	
	for (i = 0; i < step / 4 ; ++i)
		q[i] = p[i];
	for (i = 0; i < step / 4 ; ++i)
		p[i] = q[i];
	err = inb(PCI_DATA + 1);
	sti();
	if (err & 0x11) {
		printk("<1>ECC: Memory error @ %08x (0x%02x)\n", p, err);
		return 1;
	}
	return 0;
}

void scrub(void)
{
	int i,j = 0;
	for (i = 0; i < pages; ++i) {
		j = scrub_page(start);
		start += step;
	}
	if (!j) {
		/*
		 * Hmm... This is probably a very bad situation.
		 */
		printk("<1>ECC: Scrubbed, no errors found?!\n");
		scrub_needed=0;
		return;
	}
	if (scrub_needed=2) {
		/*
		 * TODO: We should determine what process owns the memory
		 * and send a SIGBUS to it. We should also printk something
		 * along the lines of
		 * "ECC: Process (PID) killed with SIGBUS due to uncorrectable memory error at 0xDEADBEEF"
		 */
		scrub_needed=0;
	}
}
#endif

#ifdef linux
/*
 * Check ECC status every second.
 * SMP safe, doesn't use NMI, and auto-rate-limits.
 */
void checkecc(void) {
	if (!scrub_needed)
		if (cs.check)
			cs.check();
	/* if there is an ECC controller but no ECC memory ... */
	if (cs.ecc_mode == ECC_NONE)
		return;
	init_timer(&ecctimer);
	ecctimer.expires = jiffies + HZ;
	ecctimer.function = (void *)&checkecc;
	add_timer(&ecctimer);
}

#ifdef CONFIG_PROC_FS
int procfile_read(char *buffer, char **buffer_location, off_t offset,
	int buffer_length, int *eof, void *data)
{
	char *ecc[] = { "None", "Reserved", "Parity checking", "ECC detection",
		"ECC detection and correction", "ECC with hardware scrubber" };
	char *dram[] = { "Empty", "Reserved", "FPM", "EDO", "BEDO", "SDR",
		"DDR", "RDR" };
	static char memstat[120*4];
	unsigned long mem_end = 0;
	unsigned long last_mem = 0;
	int loop;
	int len = 0;

	if (offset)
		return 0;

	len += sprintf(memstat, "Chipset ECC capability : %s\n", ecc[cs.ecc_cap]);
	len += sprintf(memstat + len, "Current ECC mode : %s\n", ecc[cs.ecc_mode]);
	len += sprintf(memstat + len, "Bank\tSize\tType\tECC\tSBE\tMBE\n");
	for (loop=0;loop<8;loop++){
		last_mem=bank[loop].endaddr;
		if (last_mem>mem_end) {
			len += sprintf(memstat + len, "%d\t", loop);
			len += sprintf(memstat + len, "%dM\t", (int)(last_mem-mem_end)/1048576);
			len += sprintf(memstat + len, "%s\t", dram[bank[loop].mtype]);
			len += sprintf(memstat + len, "%s\t", bank[loop].eccmode ? "Y" : "N");
			len += sprintf(memstat + len, "%d\t", bank[loop].sbecount);
			len += sprintf(memstat + len, "%d\n", bank[loop].mbecount);
			mem_end=last_mem;
		}
	}
	len += sprintf(memstat + len, "Total\t%dM\n", (int)mem_end/1048576);

	*buffer_location = memstat;
	if (len <= buffer_length)
		*eof = 1;
	else
		len = buffer_length;
	return len;
}
#endif
#endif

struct pci_probe_matrix {
	int vendor;		/* pci vendor id */
	int device;		/* pci device id */
	void (*check)(void);	/* pointer to chipset probing routine */
};

static struct pci_probe_matrix probe_matrix[] = {
	/* AMD */
	{ 0x1022, 0x7006, probe_amd751 },
	/* Motorola */
	{ 0x1057, 0x4802, 0 }, /* falcon - not yet supported */
	/* Apple */
	{ 0x106b, 0x0001, 0 }, /* bandit - not yet supported */
	/* SiS */
	{ 0x1039, 0x0600, probe_sis }, /* 600 programatically same as 5600 */
	{ 0x1039, 0x0620, 0 }, /* 620 doesnt support ecc */
	{ 0x1039, 0x5600, probe_sis },
	/* ALi */
	{ 0x10b9, 0x1531, probe_aladdin4 },
	{ 0x10b9, 0x1541, probe_aladdin5 },
	/* VIA */
	{ 0x1106, 0x0391, probe_via },
	{ 0x1106, 0x0501, probe_via },
	{ 0x1106, 0x0585, probe_via },
	{ 0x1106, 0x0595, probe_via },
	{ 0x1106, 0x0597, probe_via },
	{ 0x1106, 0x0598, probe_via },
	{ 0x1106, 0x0691, probe_via },
	{ 0x1106, 0x0693, probe_via },
	/* Intel */
	{ 0x8086, 0x122d, 0 }, /* 430fx doesnt support ecc */
	{ 0x8086, 0x1237, probe_440fx },
	{ 0x8086, 0x1250, probe_430hx },
	{ 0x8086, 0x7030, 0 }, /* 430vx doesnt support ecc */
	{ 0x8086, 0x7120, 0 }, /* 810 doesnt support ecc */
	{ 0x8086, 0x7122, 0 },
	{ 0x8086, 0x7124, 0 }, /* 810e doesnt support ecc */
	{ 0x8086, 0x7180, probe_440lx }, /* also 440ex */
	{ 0x8086, 0x7190, probe_440bx }, /* also 440zx */
	{ 0x8086, 0x7192, probe_440bx }, /* also 440zx */
	{ 0x8086, 0x71A0, probe_440bx }, /* also 440gx */
	{ 0x8086, 0x71A2, probe_440bx }, /* also 440gx */
	{ 0x8086, 0x84C5, probe_450gx },
	{ 0, 0, 0 }
};

#ifdef linux
int find_chipset(void) {

	if (!pci_present()) {
		printk("<1>ECC: No PCI bus.\n");
		return 0;
	}

	while ((bridge = pci_find_class(PCI_CLASS_BRIDGE_HOST << 8, bridge)))
	{
		int loop = 0;
		pci_read_config_word(bridge, PCI_VENDOR_ID, &vendor);
		pci_read_config_word(bridge, PCI_DEVICE_ID, &device);
		while(probe_matrix[loop].vendor)
		{
			if( (vendor == probe_matrix[loop].vendor) &&
			    (device == probe_matrix[loop].device) )
			{
				if(probe_matrix[loop].check)
				{
					probe_matrix[loop].check();
					return 1;
				} else {
					printk("<1>ECC: Unsupported device %x:%x.\n", vendor, device);
					return 0;
				}
			}
			loop++;
		}
		printk("<1>ECC: Unknown device %x:%x.\n", vendor, device);
	}
	printk("<1>ECC: Can't find host bridge.\n");
	return 0;
}

void cleanup_module(void) {
	del_timer(&ecctimer);
#ifdef CONFIG_PROC_FS
	remove_proc_entry("ram", 0);
#endif
	printk("<1>ECC: unloaded.\n");
}

int init_module(void) {
	int loop;
#ifdef CONFIG_PROC_FS
	struct proc_dir_entry *ent;
#endif
	printk("<1>ECC: monitor version %s\n", ECC_VER);

	for (loop=0;loop<8;loop++) {
		bank[loop].endaddr = 0;
		bank[loop].sbecount = 0;
		bank[loop].mbecount = 0;
		bank[loop].eccmode = 0;
		bank[loop].mtype = ECC_RESERVED;
	}

	if (!find_chipset())
		return -ENODEV;

#ifdef CONFIG_PROC_FS
	ent = create_proc_entry("ram", S_IFREG | S_IRUGO, 0);
	if (ent) {
		ent->nlink = 1;
		ent->read_proc = procfile_read;
	}
#endif

	init_timer(&ecctimer);
	ecctimer.expires = jiffies + HZ;
	ecctimer.function = (void *)&checkecc;
	add_timer(&ecctimer);

	return 0; 
}
#endif

#ifdef __FreeBSD__
int find_chipset(void) {
	int		v, d, chip;

	if (pci_numdevs == 0) {
		printf("ECC: No PCI bus.\n");
		return 0;
	}

	bzero(&pci_cfg, sizeof(pci_cfg));

	for (pci_cfg.slot = 0; pci_cfg.slot <= PCI_SLOTMAX; pci_cfg.slot++) {
		for (pci_cfg.func = 0; pci_cfg.func <= PCI_FUNCMAX; pci_cfg.func++) {
			if (pci_cfgread(&pci_cfg, PCIR_SUBCLASS, 2)
				!= ((PCIC_BRIDGE << 8) | PCIS_BRIDGE_HOST))
				continue;
			if (((v = pci_cfgread(&pci_cfg, PCIR_VENDOR, 2)) < 0) ||
			    ((d = pci_cfgread(&pci_cfg, PCIR_DEVICE, 2)) < 0))
				continue;

			vendor = (u16) v; device = (u16) d;
			for (chip = 0; probe_matrix[chip].vendor; chip++) {
				if ((vendor == probe_matrix[chip].vendor) &&
			    	    (device == probe_matrix[chip].device)) {
					if (probe_matrix[chip].check) {
						probe_matrix[chip].check();
						return 1;
					} else {
						printf("ECC: Unsupported device %x:%x.\n", vendor, device);
						return 0;
					}
				}
			}
		}
		printf("ECC: Unknown device %x:%x.\n", vendor, device);
	}
	printf("ECC: Can't find host bridge.\n");
	return 0;
}

static void ecc_check(void* arg) {
	if (!scrub_needed)
		if (cs.check)
			cs.check();
	/* if there is an ECC controller but no ECC memory ... */
	if (cs.ecc_mode == ECC_NONE)
		return;
	ecc_ch = timeout(ecc_check, NULL, hz);
}

static int
ecc_modevent(
	module_t	mod,
	int		type,
	void*		data)
{
	static int	attached = 0;
	int		loop;

	switch (type) {
	case MOD_LOAD:
		printf("ECC: monitor version %s\n", ECC_VER);

		if (attached)
			return EEXIST;
		for (loop = 0; loop < 8; loop++) {
			bank[loop].endaddr = 0;
			bank[loop].sbecount = 0;
			bank[loop].mbecount = 0;
			bank[loop].eccmode = 0;
			bank[loop].mtype = ECC_RESERVED;
		}

		if (!find_chipset())
			return -ENODEV;

		printf("ECC: vendor 0x%x\n", vendor);
		printf("ECC: device 0x%x\n", device);

		/* if there is an ECC controller but no ECC memory ... */
		ecc_check(NULL);
		if (cs.ecc_mode == ECC_NONE) {
			printf("ECC: no ECC memory\n");
			return -ENODEV;
		}
		attached = 1;
		break;

	case MOD_UNLOAD:
		untimeout(ecc_check, NULL, ecc_ch);
		attached = 0;
		printf("ECC: unloaded.\n");
		break;

	case MOD_SHUTDOWN:
	default:
		return EOPNOTSUPP;
	}
	return 0;
}

DEV_MODULE(ecc, ecc_modevent, NULL);
#endif

--------------C829E1A7414D319B6C2A655A--


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-hackers" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?3AAD4752.9C40CE34>