PRTOS的分区中断设计是指设计PRTOS处理和分发分区中断的方式,以确保每个分区都能够及时响应中断。PRTOS的分区中断设计包括以下4个方面。
1)中断触发:当一个虚拟机需要响应中断时,PRTOS需要将中断注入到虚拟机中。为此,PRTOS会将物理中断转换为虚拟中断,并将其投递给目标虚拟机。
2)中断分发:当多个虚拟机都需要响应中断时,PRTOS需要将中断分发给所有需要响应中断的虚拟机。PRTOS采用静态配置策略来实现中断分发。
3)中断处理程序:当虚拟机收到中断后,PRTOS需要调用相应的中断处理程序来处理中断。中断处理程序通常由分区应用程序提供,PRTOS需要将中断处理程序注入到虚拟机中,以确保虚拟机能够正确响应中断。
4)中断屏蔽:当虚拟机不希望响应中断时,PRTOS需要实现中断屏蔽功能,以防止虚拟机被不必要的中断打扰。中断屏蔽通常由虚拟机客户操作系统控制,PRTOS需要将中断屏蔽功能注入到虚拟机中。
如图4-1所示,PRTOS为分区中断管理提供了一个虚拟陷阱表,虚拟陷阱表和本地陷阱表相结合形成了一个虚拟中断模型,它虚拟化了硬件中可用的底层中断服务程序,并添加了一组与分区系统相关的新中断(虚拟分区间中断)。
在分区执行的过程中,当外部中断事件因为某种条件(如硬件错误、软件异常、I/O请求等)触发时,本地CPU会停止当前执行的程序(保存当前分区的vCPU上下文),将CPU的控制权转移给PRTOS Hypervisor。PRTOS会根据外部中断向量号从本地CPU中断陷阱表中取出对应的表项,并执行本地中断处理程序。本地中断处理程序的执行逻辑如下。
第1步:检测当前中断事件是否属于某个分区。如果属于该分区,则将中断投递到该分区(即将分区中断挂起寄存器中该中断对应的位置1);如果该中断不属于任何分区,则执行PRTOS预留的默认中断处理程序。
第2步:检测当前分区的中断挂起寄存器是否有处于挂起状态的中断。如果有中断处在挂起状态,则处理该中断。
分区中断处理流程如图4-4所示。
图4-4 分区中断处理流程
提示: 如果触发的中断属于当前运行分区,则该中断会立即得到处理;如果不属于当前分区,则PRTOS会将其投递到所属目标分区(前提是已经给该中断配置了所属分区)。由于切换到pCPU内核模式后,分区vCPU的上下文保存在Hypervisor的内核栈中,并且外部硬件中断向量号和分区虚拟中断向量号不一定一致,因此当PRTOS Hypervisor将中断投递到目标分区时,仍需要构造分区环境来处理这个中断的栈帧。
分区陷阱表vtrap_table在分区中断虚拟化中起着非常关键的作用,分区的虚拟ISR(Interrupt Service Routines,中断服务程序)需要通过vtrap_table来派发。vtrap_table的构建过程如代码清单4-1所示。
//源码路径:user/bail/x86/boot.S
01 TABLE_START vtrap_table
02 BUILD_TRAP_NOERRCODE 0x0
03 BUILD_TRAP_NOERRCODE 0x1
04 BUILD_TRAP_NOERRCODE 0x2
05 BUILD_TRAP_NOERRCODE 0x3
06 BUILD_TRAP_NOERRCODE 0x4
07 BUILD_TRAP_NOERRCODE 0x5
08 BUILD_TRAP_NOERRCODE 0x6
09 BUILD_TRAP_NOERRCODE 0x7
10 BUILD_TRAP_ERRCODE 0x8
11 BUILD_TRAP_NOERRCODE 0x9
12 BUILD_TRAP_ERRCODE 0xa
13 BUILD_TRAP_ERRCODE 0xb
14 BUILD_TRAP_ERRCODE 0xc
15 BUILD_TRAP_ERRCODE 0xd
16 BUILD_TRAP_ERRCODE 0xe
17 BUILD_TRAP_NOERRCODE 0xf
18 BUILD_TRAP_NOERRCODE 0x10
19 BUILD_TRAP_ERRCODE 0x11
20 BUILD_TRAP_NOERRCODE 0x12
21 BUILD_TRAP_NOERRCODE 0x13
22 BUILD_TRAP_ERRCODE 0x14
23 BUILD_TRAP_ERRCODE 0x15
24 BUILD_TRAP_ERRCODE 0x16
25 BUILD_TRAP_ERRCODE 0x17
26 BUILD_TRAP_ERRCODE 0x18
27 BUILD_TRAP_ERRCODE 0x19
28 BUILD_TRAP_ERRCODE 0x1a
29 BUILD_TRAP_ERRCODE 0x1b
30 BUILD_TRAP_ERRCODE 0x1c
31 BUILD_TRAP_ERRCODE 0x1d
32 BUILD_TRAP_ERRCODE 0x1e
33 BUILD_TRAP_ERRCODE 0x1f
34 BUILD_TRAP_BLOCK 0x20 NUM_OF_PART_IDT_ENTRIES
35 TABLE_END
36
37 .data
38 .word 0
39 ENTRY(part_idt_desc)
40 .word NUM_OF_PART_IDT_ENTRIES*8-1
41 .long part_idt_table
42
43 part_gdt_table:
44 .quad 0x0000000000000000
45 .quad 0x00cfba000000bfff
46 .quad 0x00cfb2000000bfff
47
48 .word 0
49 ENTRY(part_gdt_desc)
50 .word 3*8-1
51 .long part_gdt_table
52
53 debug_string:
54 .asciz "XAL Irq: Calling address 0x%08x\n"
55
56 .bss
57
58 .globl _stack
59 _stack:
60 .fill (STACK_SIZE/4)*CONFIG_MAX_NO_VCPUS,4,0
61
62 .globl part_idt_table
63 part_idt_table:
64 .zero (NUM_OF_PART_IDT_ENTRIES*8)
65
66 .previous
在上述代码中,vtrap_table的前32表项(01~31)是vCPU的预留表项,和X86处理器中前32个处理器预留表项相对应(第02~33行),其余的256表项为分区的所属外围设备中断和虚拟中断预留(第34行)。
在X86平台中,vtrap_table表项通过分区中断描述符表完成跳转,所以第62~64行定义了part_idt_table。
同时,分区应用也具有自己的代码段和数据空间,所以第43~46行为分区定义了虚拟全局描述符表part_gdt_table以及虚拟全局描述符part_gdt_desc(第49~51行)。
在X86平台上,vtrap_table表项是通过分区中断描述符表完成跳转的,所以分区中断描述符表part_idt_table的初始化也非常关键,如代码清单4-2所示。
//源码路径:user/bail/x86/arch.c
01 void init_arch(void) {
02 extern vtrap_table_t vtrap_table[];
03 long irq_nr;
04
05 hw_set_trap_gate(0, vtrap_table[0], 1);
06 hw_set_irq_gate(1, vtrap_table[1], 1);
07 hw_set_irq_gate(2, vtrap_table[2], 1);
08 hw_set_trap_gate(3, vtrap_table[3], 1);
09 hw_set_trap_gate(4, vtrap_table[4], 1);
10 hw_set_trap_gate(5, vtrap_table[5], 1);
11 hw_set_trap_gate(6, vtrap_table[6], 1);
12 hw_set_trap_gate(7, vtrap_table[7], 1);
13 hw_set_trap_gate(8, vtrap_table[8], 1);
14 hw_set_trap_gate(9, vtrap_table[9], 1);
15 hw_set_trap_gate(10, vtrap_table[10], 1);
16 hw_set_trap_gate(11, vtrap_table[11], 1);
17 hw_set_trap_gate(12, vtrap_table[12], 1);
18 hw_set_trap_gate(13, vtrap_table[13], 1);
19 hw_set_irq_gate(13, vtrap_table[13], 1);
20 hw_set_irq_gate(14, vtrap_table[14], 1);
21 hw_set_trap_gate(15, vtrap_table[15], 1);
22 hw_set_trap_gate(16, vtrap_table[16], 1);
23 hw_set_trap_gate(17, vtrap_table[17], 1);
24 hw_set_trap_gate(18, vtrap_table[18], 1);
25 hw_set_trap_gate(19, vtrap_table[19], 1);
26
27 for (irq_nr = 0x20; irq_nr < IDT_ENTRIES; irq_nr++)
28 hw_set_irq_gate(irq_nr, vtrap_table[irq_nr], 1);
29
30 prtos_x86_load_gdt(&part_gdt_desc);
31 prtos_x86_load_idtr(&part_idt_desc);
32 }
在上述代码中,第05~25行用于初始化分区中断描述符表的预留表项;第27~28行完成外围设备中断和虚拟中断预留表项。分区中断描述符表part_idt_table的大小和类型与pCPU中预留的原生中断描述符表的大小和类型(参见3.1.2小节)一致。