CMOS access question

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
Candamir

CMOS access question

Post by Candamir »

If I want to read from the CMOS map, I do it like this:

Code: Select all

outportb(0x70,0x10); // for the floppy drives, only an example
char x = inportb(0x71);
This works perfectly, but as I was browsing through some kernels and tutorials, I found that some of them disable interrupts (asm("cli");) before accessing the CMOS, and I don't think it is necessary (actually, I set up my floppy driver just after enabling interrupts...). Could you help me with this doubt please?

Candamir

BTW, there's a really great site out there containing the entire CMOS map and lots of useful information regarding the BIOS: http://www.addict3d.org/index.php?page=viewarticle&type=security&ID=5612&title=Bios%20Information%20Leakage
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re:CMOS access question

Post by Brendan »

Hi,
Candamir wrote:This works perfectly, but as I was browsing through some kernels and tutorials, I found that some of them disable interrupts (asm("cli");) before accessing the CMOS, and I don't think it is necessary (actually, I set up my floppy driver just after enabling interrupts...). Could you help me with this doubt please?
The reason they do this is because an IRQ (and/or task switch) might happen after you've set the CMOS location but before you've read or written the data. In this case, the IRQ handler (or another task) could change the CMOS location, so that when your code gets the CPU back you end up reading/writing the wrong CMOS location.

If you can guarantee that no IRQ handler ever touches the CMOS, and that no task switches are possible, then disabling IRQs isn't necessary....


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Xardfir

Re:CMOS access question

Post by Xardfir »

Hiya,

Further to this I'd like to add that "CLI" does not disable the NMI interrupt. I'm not sure why an NMI would occur when you are accessing CMOS but some documents recommend switching it off just in case it does.

Which comes to a problem I had with accessing CMOS - the CMOS register (0x70) also controls the NMI and has to be preserved.
Since only the last 5 bits are supposed to be accessed I'd recommend something like -

Code: Select all

unsigned char read_cmos(unsigned char cmos_register)
{
   unsigned char cmos_data;
   
   disable_interrupts();   
   outb(0x70, ((inb(0x70) & 0xe0) | (cmos_register & 0x31))); // keep the top 3 bits, alter the last 5
   cmos_data = inb (0x71);
   enable_interrupts();
   return(cmos_data);   // returns only after we have the data in custody
}
Note that the return data is saved BEFORE enabling interrupts so someone else can't screw the data up before the data is returned!

Also I wouldn't rely on any information above the 32nd place in CMOS. Even if there is more memory there every BIOS version is different and what works on one probably won't work on another even within the same version (lotsa subversions and BIOS updates). A lot of this stuff was defined for MCA and EISA computers back in the 1980's. PCI made all the extensions irrelevant with the PCI config space which is a lot bigger and more specific to each device.
Candamir

Re:CMOS access question

Post by Candamir »

Why would I want to keep the three top bits? If only the last five are taken into account, why should I preserve them?
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re:CMOS access question

Post by Brendan »

Hi,
Candamir wrote:Why would I want to keep the three top bits? If only the last five are taken into account, why should I preserve them?
The highest bit of I/O port 0x70 usually enables/disables the NMI.

This means if you've disabled the NMI for some reason by setting the highest bit (out 0x70,0x80) then you'd want to preserve this bit when you're accessing CMOS locations.

Bits 0 to 6 are normally used to select the CMOS location (for up to 128 bytes of CMOS) and shouldn't be preserved.

This implies something like:

Code: Select all

  outb(0x70, ( (inb(0x70) & 0x80) | (cmos_register & 0x7F) ) );
The problem here is that I/O port accesses to "legacy" devices (anything on the "PCI to LPC bridge") is slow - it doesn't make sense to read the previous value just to find out if NMI is enabled or disabled when you should already know.

Basically, if you disable IRQs when you disable NMI and if you don't access CMOS when NMI is disabled, then you know the NMI bit will always be enabled for normal code and it'd be faster to do something like this:

Code: Select all

  outb(0x70, (cmos_register & 0x7F) );
This should work fine as there's normally no reason to disable the NMI (it's actually not meant to be maskable in the first place).

I guess I should also point out that the NMI enable/disable bit only controls the motherboard's "hardware failure" NMI. It's possible to do some unusual things (like setting a timer IRQ as an NMI in the I/O APIC) where I/O port 0x70 has no effect on your "timer NMI".

This might sound insane, but there's actually good reasons for doing this. One reason is using it as a watchdog timer to find out if the computer has locked up. For e.g. if someone does "cli; hlt" then the timer IRQ will still work and can recover (terminate the malicious driver??). The other reason is for profiling - keep track of what EIP was when the timer interrupted to see where the CPU is spending it's time. Using an NMI in this case means you get correct "profiling statistics" regardless of whether IRQs are enabled or not.


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Post Reply