Dealing with some OpenAFS issues

November 14th, 2006

Some OpenAFS versions less than 1.4.1 have a bug in the fileserver that will cause tokens to be randomly discarded. The fix is to make sure the fileserver is at least 1.4.1.

Some previous OpenAFS versions allowed a principal with a ‘.’ (period) in the name. This no longer works. The symptom of this will be that you are able to obtain a token, but any operation on the filesystem that requires authentication will result in a short hang and the token is then discarded. The fix is to rename all such principals with a different character such as an underscore to replace the periods.

OpenAFS and Linux 2.6 have some issues with PAG support still. In my case, this manifested as an authenticated shell process that forks and calls another shell process will result in the child process having no tokens. Strangely, a shell process that runs other system programs that are not shells, such as ‘mv’, ‘rm’, etc, will succeed. So to work around this, convert your maintenance shell scripts that run authenticated to “source” each other with a . (dot) rather than to call each other. This will require some changes in error/exit handling but completely worked around the PAG issue here.

Sometimes you might notice your OpenAFS fileserver periodically restarting, with no cron job to explain it.  The culprit is the bos setrestart command.  By default, the fileserver restarts at 4:00am every Sunday, and at 5:00am every day if new binaries are detected.
To disable the periodic restart, issue the following command:
# bos setrestart server.domain.com -time never

Geforce2 passive cooling replacement

November 14th, 2006

If you have an older Geforce 2 GTS or MX card that came with a fan on it, chances are the fan is dead by now. The sleeve bearing fans they typically ship on video cards do not last. What to do?

If you can replace just the fan and avoid removing the heatsink, do that. Unfortunately, many chip coolers are sold exactly as that, so buying only the fan as a replacement is impossible. And the chip cooler itself is rarely sold at a reasonable price considering the age of the video card. Otherwise, a solution with more longevity is passive cooling. You can buy a part number HS325-ND heatsink from Digi-Key.

Remove the existing chip cooler by CAREFULLY prying between the chip and heatsink on the side where there is the least amount of glue. You have to be careful here for two reasons. There are small surface mount components and traces on the circuit board that could be damaged if you are not careful. Also, the chip package itself has tiny traces that lead from the chip core to the BGA contact points. If even one of these is damaged, the card is ruined.

Use a medium grit sandpaper and sand off the thermal glue that remains on the chip. You don't have to get rid of all of it, but you do need to get rid of as much as possible so that the chip with sanded glue is as flat as possible. Here again, avoid accidentally sanding the BGA traces on the outer diameter of the chip.

Wipe the chip down and attach the heatsink using the provided thermal tape, or glue a new chip cooler onto it. If you go with the thermal tape or pad, it would be advisable to secure the heat sink with a zip tie or something similar, because the tape or sticky pad has about a 50/50 chance of letting go at some point, which will then lead to a fried GPU.

Monitor the chip temperature for a while to make sure the cooling solution is effective. On a GF2, I have no problems with normal desktop use with passive cooling.

Check those capacitors for bulging and/or leaking while you are at it!

The Schwag on Google Video/YouTube

November 8th, 2006

http://video.google.com/videoplay?docid=7326301549273570499
http://www.youtube.com/watch?v=oqA7RjTo6nw
http://www.youtube.com/watch?v=iNv45lrKZuc




http://www.youtube.com/watch?v=_Ueh9RNxSuM











Parallel ports

November 6th, 2006

Parallel switchboxes
Don't use a parallel switchbox if you want to do high speed transfers. I have not yet found one, manual or automatic type, that is reliable at high byte rates.

PS/2
PS/2 (or Extended) mode takes the standard parallel port (SPP) and introduces an output latch and direction control for bidirectional port operation. Contrary to ECP/EPP mode, the behavior of the nACK interrupt is also changed so that the IRQ becomes active on the trailing edge instead of mirroring the pin; also, bit 2 of the status register reflects the status of the ACK interrupt (latched when IRQ generated, cleared on read) – a violation of ECP spec.

EPP
The difference between EPP 1.7 and EPP 1.9 as set in a system BIOS is one trivial difference in the EPP handshake. EPP 1.9 is equivalent to IEEE 1284. The only purpose for the EPP 1.7 setting is for any particular EPP devices built before IEEE 1284 that malfunction with the IEEE 1284/EPP 1.9 handshake. Note: IEEE 1284 defines the electrical characteristics and handshake protocols of an EPP port, not the register definition.

ECP
An ECP-capable port is a functional superset of IEEE 1284. An ECP-capable port in ECP mode is incompatible with non-ECP devices, however.

Many ECP ports do not implement the full ECP specification. Common elements to leave out are:
– nFault IRQ generation (full/empty FIFO can still generate IRQ though!)
– Hardware RLE compression (not required by spec)
– DMA (PCI cards cannot implement ECP DMA and generally do not need to since PCI write buffers provide sufficient speed)
– IRQ/DMA resource configuration (PCI cards cannot implement this)

Handling a parallel port interrupt
By definition, when sharing interrupts it is necessary for your device driver to be able to determine whether your device is the source of the interrupt or not (so you can pass the interrupt on unclaimed to other drivers if it is not). This is exceedingly difficult to do in a generic fashion for PCI parallel cards. Whether or not an interrupt is delivered in a particular operating mode, and where the status of that interrupt is reflected, is highly implementation dependent.

There are three places where an interrupt can be enabled:
– Control register bit 4 (~ACK interrupt)
– ECP Extended Control register (ECR) bit 4 (~ERR interrupt)
– ECP Extended Control register (ECR) bit 3 (DMA interrupt)
– ECP Extended Control register (ECR) bit 2 (FIFO interrupts)

There are five places where an interrupt can be generated:
– ~ACK transition
– ECP ~ERR transition
– ECP DMA completion
– ECP read FIFO filling
– ECP write FIFO emptying
– Some devices (NS) generate an interrupt on an unexpected EPP read

There are at least three places where an interrupt can be detected:
– Status register bit 2 (latched after ~ACK transition)
– ECP Config B register bit 6 (follows interrupt pin on bus)
– ECP Extended Control register (ECR) bit 2 (check for 0->1 transition)

PCI multifunction cards usually also have a global control register, which has some location outside of the usual parallel port register set that reflects the status of a parallel interrupt.

We don't really care what in particular caused the interrupt, but we do need to find some proof somewhere in the registers that this card was the one responsible for the interrupt, or things will go horribly wrong.

Problems:
– ~ACK transition is only latched to Status[2] in PS/2 mode by many cards. In SPP and other modes, it either reads 1 or follows the IRQ pin. Since a spec-conforming PCI card will use a level triggered interrupt, we can in theory use this to test for the interrupt (but only on PCI cards!)
– ECP Config B register can be used, but first the port has to be switched into Test mode to read it, which means the ECP FIFOs must be flushed and current ECP transaction terminated, possibly too high a cost for interrupt handling.
– There is no way to determine whether a ~ERR transition caused the interrupt or not. On an ISA card or one without a shared interrupt, it can be determined by a process of elimination (since a spec-conforming driver disables the ~ACK interrupt when in ECP mode), but on a shared interrupt it is impossible.
– ECR bit 2 is only useful if in ECP mode and FIFOs are being used.

Basically, the most useful parallel interrupts (those generated by external events) give us no reliable way to determine which card owns the interrupt. The ~ACK interrupt could be probed, had the PC parallel port's designers thought to put in a loop-back test, but they did not.

The best thing you can do to handle PCI parallel interrupt sharing in a generic fashion is to:
– Disable the ~ERR interrupt.
– The DMA interrupt is not an issue on PCI cards since they don't support it anyway.
– Keep track of the state of the ECR bit 2 when you set it to 0 (unmasks the ECP FIFO interrupt) so that you can check if it changed in your interrupt handler (meaning we generated an interrupt).
– Ensure that your card cannot both have the ~ACK interrupt enabled AND be in a mode that will not latch that interrupt in Status[2] (reflecting the pin state is not enough!). Then you can assume that a Status[2]==0 event means that we generated the interrupt. Note: On most/all PCI cards, the status register must be read in order to clear the level-triggered interrupt.
– Assure yourself to whatever degree of confidence required that your card will not produce ANY other type of interrupt (vendor's logic equation for IRQ event helps)!

If you are lucky enough to have a global interrupt flag for the parallel port on your PCI card, USE THAT INSTEAD! Then you can use ~ERR and ~ACK as external interrupt sources without worries, and you can also handle spurious interrupts with a high degree of confidence! Only use the above “generic” mechanism as a last resort. If someone would look into using the ECP Register B to check for the interrupt and see how well that works, that may be an even better “generic” solution for PCI parallel cards.

Simple PC parallel port detection in DOS


unsigned short lpt_base;
char lpt_irq;
unsigned char lpt_vector;
unsigned char lpt_pic; /* 0 = pic1, 1 = pic2 */
unsigned char lpt_mask; /* bit in PIC OCW to unmask/mask */
unsigned char received; /* The last byte received */
char is_ecp;
void interrupt(*old_lpt_irqhandler)(__CPPARGS);

// The following code should be inserted into a setup function, and allow
// user to override base address and IRQ
{
	// setup parallel port
	if (lpt_base == 0) {
		// Use BDA to find base address of system's first parallel port
		unsigned short far *bda_lpt = (unsigned short far*)MK_FP(0x40, 8);

		lpt_base = *bda_lpt;
		//printf("lpt_base %0.4x", *bda_lpt);
		assert(lpt_base == 0x3bc || lpt_base == 0x378 || lpt_base == 0x278);
	}

	if (lpt_base == 0x3bc) {
	  // We can assume a port at 0x3BC has IRQ 7 unless we find otherwise
	  lpt_irq = 7;
	}
	// Detect ECP port according to ECP spec p.31
	// ECR is at lpt_base + 0x402
	unsigned char test = inp(lpt_base+0x402);
	if ((test & 1) /* fifo empty */ && !(test & 2) /* fifo not full */) {
		// Attempt to write a read only bit (fifo empty) in ECR
		outp(lpt_base+0x402, 0x34);
		test = inp(lpt_base+0x402);
		if (test == 0x35)
			is_ecp = 1;
	}

	// If ECP port, read cnfgB to find parallel port IRQ number
	if (is_ecp) {
		// Put port into configuration mode
		test = inp(lpt_base+0x402);
		test |= 0xE0;
		outp(lpt_base+0x402, test);
		// Read cnfgB
		unsigned char irq = inp(lpt_base+0x401);
		irq &= 0x38;
		irq >>= 3;
		// irq0 means selected via jumper, user will have to hard code the irq
		if (irq != 0) {
			switch(irq){
				case 1: lpt_irq = 7; break;
				case 2: lpt_irq = 9; break;
				case 3: lpt_irq = 10; break;
				case 4: lpt_irq = 11; break;
				case 5: lpt_irq = 14; break;
				case 6: lpt_irq = 15; break;
				case 7: lpt_irq = 5; break;
				default: break;
			}
		}
		// Set ECP port mode to PS2
		test = inp(lpt_base+0x402);
		test &= ~0xE0;
		test |= 0x20;
		outp(lpt_base+0x402, test);
	}

	if (lpt_irq == -1) {
		fprintf(stderr, "Couldn't find interrupt for parallel port at 0x%x !\n", lpt_base);
		sleep(2);
		exit(EXIT_FAILURE);
	}

	// Convert IRQ number to interrupt vector
	switch(lpt_irq) {
		case 5: lpt_vector = 0x0d; lpt_mask = (1 << 5); break;
		case 7: lpt_vector = 0x0f; lpt_mask = (1 << 7); break;
		case 9: lpt_vector = 0x71; lpt_pic = 1; lpt_mask = (1 << 1); break;
		case 10: lpt_vector = 0x72; lpt_pic = 1; lpt_mask = (1 << 2); break;
		case 11: lpt_vector = 0x73; lpt_pic = 1; lpt_mask = (1 << 3); break;
		case 14: lpt_vector = 0x76; lpt_pic = 1; lpt_mask = (1 << 6); break;
		case 15: lpt_vector = 0x77; lpt_pic = 1; lpt_mask = (1 << 7); break;
		default: abort();
        }


        fprintf(stderr, "Parallel port at 0x%x, irq %d", lpt_base, lpt_irq);
        if (is_ecp)
                fprintf(stderr, ", ECP");
        fprintf(stderr, "\n");

        // set to data input mode using DCR
        outp(lpt_base+2, inp(lpt_base+2) | 0x20);

        // check that data lines are not driven by us
        int fail = 1;

        for (i = 0; i < 5; i++) {
                outp(lpt_base, 0x5a+i);
                if (inp(lpt_base) != 0x5a+i) {
                        fail = 0;
                        break;
                }
        }
        
        if (fail) {
                fprintf(stderr, "Parallel port does not appear to be bidirectional!\n");        
                sleep(2);
                exit(EXIT_FAILURE);
        }
        
        disable();  // cli()
        // grab IRQ vector
        old_lpt_irqhandler=getvect(lpt_vector);
        setvect(lpt_vector, lpt_irqhandler);
        
        if (lpt_pic > 0) {
                // unmask our IRQ
		outp(PICB_1, inp(PICB_1) & ~lpt_mask);
		// then unmask IRQ2
		outp(PICA_1, inp(PICA_1) & ~0x04);

	}
	else {
		// unmask our IRQ
		outp(PICA_1, inp(PICA_1) & ~lpt_mask);
	}
	// enable parallel port interrupt via ACK line
	outp(lpt_base+2, inp(lpt_base+2) | 0x10);

	enable(); // sti()
}

Simple bidirectional communication between two PCs with a standard parallel port cable
Swap STROBE and nACK pins on one end of the parallel cable. Ensure that the parallel port nACK interrupt is enabled on both ends (DCR[5] := 1). Then the communication looks like the following:


// Parallel port ISR, Turbo C++ 3.1 DOS code
void interrupt lpt_irqhandler(__CPPARGS)
{
  disable();

  received = inp(lpt_base);
  // Interrupt the sender, since STROBE on this end
  // is connected to ACK on the other end
  unsigned char tmp = inp(lpt_base+2);
  outp(lpt_base+2, tmp ^ LPT_STROBE);
  outp(lpt_base+2, tmp);

  old_lpt_irqhandler(); // chain old IRQ handler
  outp(PICA_0, EOI); // EOI
  if (lpt_pic > 0)
	outp(PICB_0, EOI); // also send EOI to PIC2

  enable();
}

I have found this to be a sufficient quick & dirty way of transferring bytes from one PC to another in interrupt driven fashion.

coLinux adventures

November 2nd, 2006

This is the best way to setup CoLinux networking, because it has a high speed TAP interface for the local X server traffic, and only uses WinPCap for external traffic. It also does not require Internet Connection Sharing to be enabled because WinPCap creates a virtual network adapter on top of the existing one.

Note that this will only work if your local X server can accept connections from 192.168.x.x, some commercial X servers have a license scheme limiting the IP range. If yours does, you can assign IP addresses within that rang Read the rest of this entry »

Automatic security updates with Debian

November 2nd, 2006

The following /etc/dpkg/dpkg.cfg settings are useful when you have scripted apt-get to do automatic security updates:

# Choose the default action regarding conffiles
force-confdef
# If no default, keep the old conffile
force-confold

This will prevent an update from aborting because the local version of the conf file differed from the package maintainer's version and no user input was available.

Debian archive GPG keys

October 29th, 2006

You might notice in new versions of apt:
W: GPG error: http://ftp.debian.org testing Release: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 010908312D230C5F

Go to this link to fix it: http://www.debian-administration.org/articles/174

Short version:
For NO_PUBKEY 8899AABBCCDDEEFF:

# gpg –keyserver keyring.debian.org –recv-keys 8899AABBCCDDEEFF
# gpg –armor –export 8899AABBCCDDEEFF | apt-key add –

Some more information about how to add a key e.g. for your private repository: https://drupal.gulic.org/node/367

ISA interrupt sharing

October 29th, 2006

ISA interrupt sharing

Arbitrary ISA boards cannot share interrupt lines safely (though several ISA devices integrated onto a single board can).

There are several reasons for this. First of all, the interrupt line may be driven at all times by some other board, causing the first board's interrupt request to vanish; extra hardware would be required on both cards to prevent this.

ISA devices employ edge triggered interrupts, so the interrupt may be missed because it is pulsed after that interrupt was already asserted by another board and latched by the PIC.

The card may lock up (stall) if its interrupt status register is read when no interrupt is pending, causing the host to hang. One requirement of a shared-interrupt device driver is that it has to check interrupt status on all cards assigned to that interrupt.

It is also possible to have spurious interrupts (like a parallel port interrupt caused by external event while no parallel port driver is loaded, or a fluctuation at power up); if something else is on that IRQ line, its interrupt handler will get called. The other interrupt handler will not know how to clear the asserted interrupt in the device, leading to an interrupt storm and lockup.

If you design ISA devices that account for all of the above scenarios, you may be able to get away with sharing interrupts between them (but not with an arbitrary ISA card!)

Under DOS, there is another caveat. If you have several device drivers sharing a software or hardware interrupt, the most recently installed interrupt handler will “chain” to the old handlers. If a device driver takes over a hardware IRQ vector, the other driver probably issued a PIC EOI at the end of its IRQ handling routine. If you then issue a PIC EOI at the end of your routine after calling the other driver, the result will be that an interrupt could be lost. So you would have to be quite careful to ensure that you issue EOI if and only if your board was the one responsible for the interrupt.

Some things you might not know about PCI

A PCI bus only has 4 possible interrupt lines: INTA-D. These are connected to PIC lines (or mapped by the I/O-APIC) and set to level-trigger mode in the ELCR (Edge/Level Control Register) by the system firmware. If you have more than 4 interrupt-driven PCI devices attached to a PCI bus (most systems only have one PCI bus and an AGP bus), at least two of those devices will be sharing a PCI interrupt line. This implies that they share the same CPU interrupt vector.

While PCI devices are required by the specification to have level triggered interrupts so that interrupt sharing is possible, PCI devices are NOT required to support sharing that interrupt line in any sane fashion. So when that CPU interrupt vector is called, it usually will call several interrupt handlers (one for each device on the shared IRQ), and the interrupt handler will attempt to determine whether its card was the one that generated the interrupt. Frequently, there are bugs in logic that result in lockups or unexpected behavior. For example, some cards will lock up if their interrupt reason register is read while an interrupt is not pending. Others will automatically clear the interrupt flag and continue operation when the status register is read during the scan, leading to problems if you needed to service interrupts in a particular order. Better yet, hardware bugs may cause a device to appear to its driver to be the source of an interrupt when in fact it was not, causing lost interrupts from the other device it shares the IRQ with.

ISA DMA
The FreeBSD developer's handbook has a good overview of ISA DMA and the 8237 DMA controller: http://www.freebsd.org/doc/en_US.ISO8859-1/books/developers-handbook/dma.html

Plug and Play (ISA)
Plug and Play is now a general PC industry term used to describe any hardware which should require no software installation or hardware configuration on the part of the user. ISA Plug and Play has a very specific meaning, however. The Plug and Play scheme consists of two special ports (0x279 and 0xA79) and a set of Plug and Play registers, implemented by system logic. 0x279 is ADDRESS and 0xA79 is WRITE_DATA. ADDRESS is the 8-bit address of an internal Plug and Play register, and WRITE_DATA is the value to be written.

Note: port 0x279 appears to conflict with the tertiary ISA parallel port; however, Plug & Play writes to this register, while parallel port driver software should only read from it.

The PnP card powers on in sleep mode; it only snoops the bus for writes to 0x279 and 0xA79. When software wants to configure a card, it disables interrupts, writes two zeroes, and then the 32-byte industry Plug and Play key (6A,B5,DA,ED,F6,FB,7D,BE,DF,6F,37,lB,0D,86,C3,61,B0,58,2C,16,8B,45,A2,D1,E8,74,3A,9D,CE,E7,73,39) to port 0x279. (This should be done twice, so that in case a previous key write failed to complete, the internal state machine in the card will be reset). Afterwards, a series of commands referred to as the isolation protocol attempts to isolate a particular PnP card for configuration by repeatedly relocating the PnP READ_DATA port and verifying the checksum of what is read from READ_DATA. If the checksum matches, the card is assigned a unique “Card Select Number” based on its serial number among other things, and the CSN is then used to configure the card. The process is repeated to isolate other PnP cards and configure them.

Most PnP cards also have a separate vendor-defined key that can be sent to place them into software configuration mode outside of the PnP scheme.

Because of the vendor key scheme, and due to the fact that most manufacturers of add-in boards do not bother to store a unique serial number in the board's EEPROM, it is rarely possible to have more than one identical ISA PnP card installed in the same system.

The PnP configuration can be performed by the BIOS, the operating system, or an operating system PnP driver (such as Intel's ICU for DOS).

If the PnP configuration is performed by the BIOS, the hardware resource configuration of the PnP device, as well as a list of all configurations it can support, is exported to the operating system via the 16-bit PnP BIOS interface. The operating system can use the configuration as-is, or it can change the configuration to any of several pre-set configurations provided by the card's PnP EEPROM. If the operating system changes the configuration via the PnP BIOS interface, the configuration will be stored in the ESCD, which is stored in non-volatile memory somewhere. When the system is reset, the BIOS will afterwards use the ESCD values to configure the card. Therefore, changes made to PnP cards in one operating system will persist to another operating system on the same computer via the PnP BIOS and ESCD.

Many systems will leave PnP cards unconfigured. In this case, the PnP BIOS is irrelevant and will show zero PnP cards. This is misleading, because there may very well be PnP cards in the system, but simply that the BIOS did not configure them! In this case, it is necessary for the operating system or driver software such as ICU, isapnptools, or the isapnp driver to use port 0x279 and 0xA79 to find and configure PnP devices.

This is the same case when 'PnP Operating System' or a similar function in the BIOS is set to 'Yes'. This simply means that the BIOS will not configure PnP cards, export cards through the PnPBIOS interface, or make use of the ESCD – thus leaving the configuration of the PnP cards wholly up to the operating system or drivers.

System design hazards

October 27th, 2006

There are several hazards you want to avoid if your system can have its power removed at any point.

– Writing to an EEPROM. If this is to be done, the user should be warned that power removal will corrupt the EEPROM. If this is to be done by firmware (as in IBM Thinkpads and their security serial EEPROM), there should always be a backup EEPROM that can be used in the case that power is removed during the write.

– Hardware that requires several discrete I/O cycles to complete a write to a non-volatile (preserved across boot, and perhaps required by system firmware) region. For example, the PC RTC (Real Time Clock) is notorious for becoming corrupted when a NMI or SMI arrives at the wrong moment due to its index/data register pair design. The 8042 NMI mask bit is there for a reason; use it. The SMM infrastructure allows for restarting I/O instructions, but it does not know anything about the RTC. Therefore, the SMI handler must scan the region around EIP to ensure that the SMI didn't arrive between a write to port 0x70 and a read from port 0x71.

Gateway's OEM MS-6330 v2.1 mainboard

October 20th, 2006

If you go motherboard hunting on EBay, you may run across a lot of these OEM boards. They have a Gateway AMI BIOS, MS-6330 v2.1 printed on the board, and a custom case front panel connector header. The reason you run across a lot of them is because they were manufactured with defective CPU filter capacitors; the aftermarket has been replacing the capacitors and reselling the boards.

The boards shipped with a BIOS that is full of bugs. The 0AAVWP02 BIOS (8/21/2000) most annoyingly has a bug that causes a corrupted IRQ routing table when a PS/2 mouse is not connected. Linux does not handle this corrupted IRQ routing table, and crashes at boot as in this bug report: http://lists.suse.com/archive/suse-linux-e/2002-Nov/0229.html

But, though this is a MSI board (similar to K7T Pro), you cannot use a MSI BIOS on it (MSI does not mention a MS-6330 v2.1 on their site). The key to knowing how to find this BIOS is to know that Gateway's code name for the board is “Oxnard”. Then you can go here: http://support.gateway.com/support/drivers/search.asp?param=oxnard&st=kw in order to download the BIOS (7511677.exe 2/17/2003) The latest BIOS is 0AAVWP13.

If you are trying to figure out how to hook up the front panel or other connectors on the motherboard, go here:
http://support.gateway.com/s/MOTHERBD/MSI/2514182/2514182tc.shtml
http://support.gateway.com/s/MOTHERBD/MSI/2510174/251017422.shtml

These are the OEM specs for the board:
MOTHERBOARD MODEL:
Microstar K7T Turbo MS-6330
CPU SUPPORT:
AMD Athlon up to 1.4 GHz with “A” prefix on CPU
AMD Duron up to 1000 MHz with “D” prefix on CPU
CHIPSET:
VIA KT133A Chipset
VIA VC82C686B Chipset
FSB:
200/266MHz FSB
SYSTEM MEMORY:
3 X 168-pin DIMM sockets for 3.3v SDR-SDRAM
Maximum: 1.5GB
EXPANSIONS SLOTS:
1 AGP slot
5 PCI slots
ON BOARD IDE:
An IDE controller on the VIA VT82C686B chipset provides IDE HDD/CD-ROM with PIO, Bus Master and Ultra DMA 33/66/100 operation modes.
I/O INTERFACE:
1 Serial port
1 Parallel port supports SPP/EPP/ECP mode.
4 USB ports. (2 rear connectors/2 front pin headers)
DIMENSIONS:
ATX Form Factor. 12″ x 8″
BIOS:
AMI BIOS
Plug & Play Flash BIOS
Desktop Management Interface(DMI) function