There has been a lot of (heated) discussion in the past weeks concerning UEFI Secure Boot and how this can impact on the ability of a user to install their operating system of choice.
To address this, today has seen no less than two papers published to address this hot topic. Canonical along with Red Hat have published a white paper that describes how UEFI Secure Boot will impact users and manufactures. The paper also provides recommendations on the implementation of UEFI Secure Boot in way that allows users to be in control of their own PC hardware.
Meanwhile the Linux Foundation has also published a paper giving technical guidance on how to implement UEFI Secure Boot to allow operating systems other than Windows 8 to operate on new Windows 8 PCs.
So lots to read and good technical guidance all round. Let's hope that these constructive set of papers will push the argument into a positive outcome.
Friday, 28 October 2011
Sunday, 23 October 2011
Determining the number of arguments in a C vararg macro
C vararg macros are very useful and I've generally used them a lot for wrapping C vararg functions. However, at times it would be very useful to be able to determine the number of arguments being passed into the the vararg macro and this is not as straight forward as it first seems.
Anyhow, this problem has been asked many times on the usenet and internet, and I stumbled on a very creative solution by Laurent Deniau posted on comp.std.c back in 2006.
However, passing no arguments to this macro yields 1, which is not as we expect. So last night I tweaked the macro to fix this problem by checking the length of the stringified macro arguments and adjusting the return value for a empty __VA_ARGS__ - as follows:
The purists may point out that PP_NARG() only handles 64 arguments. For just integer arguments, a better solution for any number of arguments has been proposed by user qrdl on stackoverflow:
..which is appealing as it is more immediately understandable than the PP_NARG() macro, however it is less generic since it only works for ints.
Anyhow, it's great to find such novel solutions even if they may be at first a little bit non-intuitive.
Anyhow, this problem has been asked many times on the usenet and internet, and I stumbled on a very creative solution by Laurent Deniau posted on comp.std.c back in 2006.
#define PP_NARG(...) \
PP_NARG_(__VA_ARGS__,PP_RSEQ_N())
#define PP_NARG_(...) \
PP_ARG_N(__VA_ARGS__)
#define PP_ARG_N( \
_1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
_11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
_21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
_31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
_41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
_51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
_61,_62,_63,N,...) N
#define PP_RSEQ_N() \
63,62,61,60, \
59,58,57,56,55,54,53,52,51,50, \
49,48,47,46,45,44,43,42,41,40, \
39,38,37,36,35,34,33,32,31,30, \
29,28,27,26,25,24,23,22,21,20, \
19,18,17,16,15,14,13,12,11,10, \
9,8,7,6,5,4,3,2,1,0
/* Some test cases */
PP_NARG(A) -> 1
PP_NARG(A,B) -> 2
PP_NARG(A,B,C) -> 3
PP_NARG(A,B,C,D) -> 4
PP_NARG(A,B,C,D,E) -> 5
However, passing no arguments to this macro yields 1, which is not as we expect. So last night I tweaked the macro to fix this problem by checking the length of the stringified macro arguments and adjusting the return value for a empty __VA_ARGS__ - as follows:
#define PP_NARG(...) (PP_NARG_(__VA_ARGS__,PP_RSEQ_N()) - \
(sizeof(#__VA_ARGS__) == 1))
#define PP_NARG_(...) PP_ARG_N(__VA_ARGS__)
#define PP_ARG_N( \
_1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
_11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
_21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
_31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
_41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
_51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
_61,_62,_63, N, ...) N
#define PP_RSEQ_N() \
63,62,61,60, \
59,58,57,56,55,54,53,52,51,50, \
49,48,47,46,45,44,43,42,41,40, \
39,38,37,36,35,34,33,32,31,30, \
29,28,27,26,25,24,23,22,21,20, \
19,18,17,16,15,14,13,12,11,10, \
9,8,7,6,5,4,3,2,1,0
The purists may point out that PP_NARG() only handles 64 arguments. For just integer arguments, a better solution for any number of arguments has been proposed by user qrdl on stackoverflow:
#define NUMARGS(...) (int)(sizeof((int[]){0, ##__VA_ARGS__})/sizeof(int)-1)
..which is appealing as it is more immediately understandable than the PP_NARG() macro, however it is less generic since it only works for ints.
Anyhow, it's great to find such novel solutions even if they may be at first a little bit non-intuitive.
Forcing a CMOS reset from userspace
Resetting CMOS memory on x86 platforms is normally achieved by either removing the CMOS battery or by setting a CMOS clear motherboard jumper in the appropriate position. However, both these methods require access to the motherboard which is time consuming especially when dealing with a laptop or netbook.
An alternative method is to twiddle specific bits in the CMOS memory so that the checksum is no longer valid and on the next boot the BIOS detects this and this generally forces a complete CMOS reset.
I've read several ways to do this, however the CMOS memory layout varies from machine to machine so some suggested solutions may be unreliable across all platforms. Apart from the Real Time Clock (which writing to won't affect a CMOS reset), the only CMOS addresses to be consistently used across most machines are 0x10 (Floppy Drive Type), 0x2e (CMOS checksum high byte) and 0x2f (CMOS checksum low byte). With this in mind, it seems that the best way to force a CMOS reset is to corrupt the checksum bytes, so my suggested solution is to totally invert each bit of the checksum bytes.
To be able to read the contents of CMOS memory we need to write the address of the memory to port 0x70 then delay a small amount of time and then read the contents by reading port 0x71. To write to CMOS memory we again write the address to port 0x70, delay a little, and then write the value to port 0x71. A small delay of 1 microsecond (independent of CPU speed) can be achieved by writing to port 0x80 (the Power-On-Self-Test (POST) code debug port).
And hence inverting CMOS memory at a specified address is thus:
To ensure we are the only process accessing the CMOS memory we should also turn off interrupts, so we use iopl(3) and asm("cli") to do this and then asm("sti") and iopl(0) to undo this. We also need to use ioperm() to get access to ports 0x70, 0x71 and 0x80 for cmos_read() and cmos_write() to work and we need to run the program with root privileges. The final program is as follows:
You can find this source in by debug code git repo.
Before you run this program, make sure you know which key should be pressed to jump into the BIOS settings on reboot (such as F2, delete, backspace,ESC, etc.) as some machines may just display a warning message on reboot and need you to press this key to progress further.
So to reset, simple run the program with sudo and reboot. Easy. (Just don't complain to me if your machine isn't easily bootable after running this!)
An alternative method is to twiddle specific bits in the CMOS memory so that the checksum is no longer valid and on the next boot the BIOS detects this and this generally forces a complete CMOS reset.
I've read several ways to do this, however the CMOS memory layout varies from machine to machine so some suggested solutions may be unreliable across all platforms. Apart from the Real Time Clock (which writing to won't affect a CMOS reset), the only CMOS addresses to be consistently used across most machines are 0x10 (Floppy Drive Type), 0x2e (CMOS checksum high byte) and 0x2f (CMOS checksum low byte). With this in mind, it seems that the best way to force a CMOS reset is to corrupt the checksum bytes, so my suggested solution is to totally invert each bit of the checksum bytes.
To be able to read the contents of CMOS memory we need to write the address of the memory to port 0x70 then delay a small amount of time and then read the contents by reading port 0x71. To write to CMOS memory we again write the address to port 0x70, delay a little, and then write the value to port 0x71. A small delay of 1 microsecond (independent of CPU speed) can be achieved by writing to port 0x80 (the Power-On-Self-Test (POST) code debug port).
static inline uint8_t cmos_read(uint8_t addr)
{
outb(addr, 0x70); /* specify address to read */
outb(0, 0x80); /* tiny delay */
return inb(0x71); /* read value */
}
static inline void cmos_write(uint8_t addr, uint8_t val)
{
outb(addr, 0x70); /* specify address to write */
outb(0, 0x80); /* tiny delay */
outb(val, 0x71); /* write value */
}
And hence inverting CMOS memory at a specified address is thus:
static inline void cmos_invert(uint8_t addr)
{
cmos_write(addr, 255 ^ cmos_read(addr));
}
To ensure we are the only process accessing the CMOS memory we should also turn off interrupts, so we use iopl(3) and asm("cli") to do this and then asm("sti") and iopl(0) to undo this. We also need to use ioperm() to get access to ports 0x70, 0x71 and 0x80 for cmos_read() and cmos_write() to work and we need to run the program with root privileges. The final program is as follows:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/io.h>
#define CMOS_CHECKSUM_HI (0x2e)
#define CMOS_CHECKSUM_LO (0x2f)
static inline uint8_t cmos_read(uint8_t addr)
{
outb(addr, 0x70); /* specify address to read */
outb(0, 0x80); /* tiny delay */
return inb(0x71); /* read value */
}
static inline void cmos_write(uint8_t addr, uint8_t val)
{
outb(addr, 0x70); /* specify address to write */
outb(0, 0x80); /* tiny delay */
outb(val, 0x71); /* write value */
}
static inline void cmos_invert(uint8_t addr)
{
cmos_write(addr, 255 ^ cmos_read(addr));
}
int main(int argc, char **argv)
{
if (ioperm(0x70, 2, 1) < 0) {
fprintf(stderr, "ioperm failed on ports 0x70 and 0x71\n");
exit(1);
}
if (ioperm(0x80, 1, 1) < 0) {
fprintf(stderr, "ioperm failed on port 0x80\n");
exit(1);
}
if (iopl(3) < 0) {
fprintf(stderr, "iopl failed\n");
exit(1);
}
asm("cli");
/* Invert CMOS checksum, high and low bytes*/
cmos_invert(CMOS_CHECKSUM_HI);
cmos_invert(CMOS_CHECKSUM_LO);
asm("sti");
(void)iopl(0);
(void)ioperm(0x80, 1, 0);
(void)ioperm(0x70, 2, 0);
exit(0);
}
You can find this source in by debug code git repo.
Before you run this program, make sure you know which key should be pressed to jump into the BIOS settings on reboot (such as F2, delete, backspace,ESC, etc.) as some machines may just display a warning message on reboot and need you to press this key to progress further.
So to reset, simple run the program with sudo and reboot. Easy. (Just don't complain to me if your machine isn't easily bootable after running this!)
Wednesday, 19 October 2011
PCI Interrupt Routing
Understanding PCI Interrupt Routing on the x86 platform is a not entirely straight forward. The underlining principle is determining which interrupt is being asserted when a PCI interrupt signal occurs. Unfortunately this is generally platform specific and so firmware tables of various types have been used over the many years to describe the routing configuration.
While looking into the legacy PCI interrupt routing tables I found an excellent article by FreeBSD kernel hacker John Baldwin that explains PCI interrupt routing in a clear an succinct manner. Although written for FreeBSD, this article is contains a lot of Linux relevant information.
While looking into the legacy PCI interrupt routing tables I found an excellent article by FreeBSD kernel hacker John Baldwin that explains PCI interrupt routing in a clear an succinct manner. Although written for FreeBSD, this article is contains a lot of Linux relevant information.
Subscribe to:
Posts (Atom)