2012-10-12 55 views
7

我正在开发一个具有严格启动时间要求的项目。目标架构是一个基于IA-32的处理器,运行在32位保护模式下。确定可以改进的一个领域是当前系统动态初始化处理器的IDT(中断描述符表)。由于我们没有任何即插即用设备,并且系统相对静态,因此我希望能够使用静态构建的IDT。静态定义的IDT

但是,由于8字节的中断门描述符拆分了ISR地址,这对IA-32拱门来说很麻烦。 ISR的低16位出现在描述符的前2个字节中,其他一些位填充接下来的4个字节,最后ISR的最后16位出现在最后2个字节中。

我想用一个常量数组定义IDT,然后简单地在这点IDT寄存器像这样:

typedef struct s_myIdt { 
    unsigned short isrLobits; 
    unsigned short segSelector; 
    unsigned short otherBits; 
    unsigned short isrHibits; 
} myIdtStruct; 

myIdtStruct myIdt[256] = { 
    { (unsigned short)myIsr0, 1, 2, (unsigned short)(myIsr0 >> 16)}, 
    { (unsigned short)myIsr1, 1, 2, (unsigned short)(myIsr1 >> 16)}, 

显然,这是行不通的,因为它是非法的在C中这样做,因为myIsr不是常量。它的值由链接器(只能进行有限的数学运算)而不是由编译器解决。

任何建议或其他想法如何做到这一点?

谢谢,

+0

我的建议是确保你的IDT和ISR在同一个模块中(显然ISR是在一个固定位置加载的)然后使用标签。我试着用GCC做这件事,但它不喜欢在函数外部使用'&& myIsr0'语法,而且我没有使用'__asm__'语法来声明IDT的内联汇编技巧。我可能只是使用NASM(个人偏好)编译这个模块,ISR是C函数的存根调用。这将是我的建议,但我绝对不能声称是一个专家:) – Justin

+0

这是一种讨厌的建议,我会给:使用程序集。当使用非常特定的处理器功能或极低级别的构造时,请调用一些程序集。它使它更容易。 – Linuxios

+0

这不是我的特长;有人可以简单地解释或指出我为什么这是非法的吗? – taz

回答

2

你遇到了一个众所周知的x86疣。我不相信链接程序可以用IDT条目预期的混合形式填充你的isr例程的地址。

如果您感觉雄心勃勃,可以创建一个类似于此(基于Linux)的方法的IDT构建器脚本。我还没有测试过这个方案,无论如何它可能是一个讨厌的黑客,所以谨慎行事。

第1步:编写脚本运行'nm'并捕获stdout。

第2步:在脚本中,解析nm输出以获取所有中断服务例程的内存地址。

步骤3:输出一个二进制文件'idt.bin',其IDT字节全部设置并准备好用于LIDT指令。您的脚本显然以正确的混合形式输出isr地址。

步骤4:将他的原始二进制为ELF段与objcopy命令:

objcopy -I binary -O elf32-i386 idt.bin idt.elf

第5步:现在idt.elf文件中有您的IDT的二进制,符号是这样的:

> nm idt.elf 
000000000000000a D _binary_idt_bin_end 
000000000000000a A _binary_idt_bin_size 
0000000000000000 D _binary_idt_bin_start 

第6步:重新链接你的二进制包括idt.elf。在装配存根和链接器脚本中,可以将符号_binary_idt_bin_start引用为IDT的基础。例如,您的链接器脚本可以将符号_binary_idt_bin_start放在您喜欢的任何地址。

请注意,使用IDT部分重新链接不会在您的二进制文件中移动任何其他内容,例如,你的isr例程。通过将IDT放入它自己的专用部分,在链接描述文件(.ld文件)中进行管理。

---编辑--- 从评论,似乎有关于这个问题的困惑。的32位x86 IDT预计中断服务程序的地址被划分成两个不同的16位字,像这样:

 
31   16 15   0 
+---------------+---------------+ 
| Address 31-16 |    | 
+---------------+---------------+ 
|    | Address 15-0 | 
+---------------+---------------+ 

连接体,从而无法插件的ISR地址作为一个正常的搬迁。因此,在启动时,软件必须构建这种分割格式,这会缩短启动时间。

+0

这是结论I已经到了。脚本或其他程序将不得不解析ELF图像的符号表并构造混搭的IDT。二进制输出文件是一个好主意。我正在考虑生成一个实际的C文件,但是使用二进制方法,我可以避免编译步骤,考虑到构建系统的工作原理,这将是一场噩梦。 – jkayca

+0

二进制方法还可以避免将代码注入已链接的固件映像。 – srking

0

你可以做这样的事情:

的main.c:

#include <stdint.h> 
#include <stdio.h> 

void isr0(); 

struct idt_entry 
{ 
    uint32_t idt_a; 
    uint32_t idt_b; 
}; 

extern char idt_a_0; 
extern char idt_b_0; 

struct idt_entry idt0 = { 
    (uint32_t)&idt_a_0, 
    (uint32_t)&idt_b_0 
}; 

int main() 
{ 
    printf ("isr0: %08x\n", &isr0); 
    printf ("%08x\n", ((uint16_t*)&idt0)[0]); 
    printf ("%08x\n", ((uint16_t*)&idt0)[1]); 
    printf ("%08x\n", ((uint16_t*)&idt0)[2]); 
    printf ("%08x\n", ((uint16_t*)&idt0)[3]); 

    return 0; 
} 

link.ld:

seg_selector = 1; 
other_bits = 2; 
isr0_lo = isr0 & 0xFFFF; 
isr0_hi = isr0 >> 16; 

idt_a_0 = (seg_selector << 16) | isr0_lo; 
idt_b_0 = (isr0_hi << 16) | other_bits; 

isr0.c:

void isr0() 
{ 
} 

的Makefile:

CFLAGS=-m32 
main: main.o isr0.o link.ld 
    gcc -m32 -Wl,link.ld -o [email protected] $^ 
main.o: main.c 
isr0.o: isr0.c