@@ -97,6 +97,73 @@ struct irq_stack {
9797 2 . 软件保存被中断进程的通用目的寄存器在当前进程栈,然后清除 GPRs 的内容
9898 3 . 软件将栈切换到预设好的 per-CPU 的中断栈,栈的地址由 per-CPU 变量来记录,类似` moveq PER_CPU_VAR(irq_stack_ptr), %rsp `
9999
100+ ### 中断栈的分配和初始化
101+ ``` cpp
102+ start_kernel ()
103+ -> init_IRQ()
104+ -> irq_init_percpu_irqstack(smp_processor_id())
105+ -> map_irq_stack()
106+ // arch/x86/kernel/x86_init.c
107+ -> x86_init.irqs.intr_init()
108+ // arch/x86/kernel/irqinit.c
109+ => native_init_IRQ()
110+ -> idt_setup_apic_and_irq_gates()
111+ ```
112+ * arch/x86/kernel/irq_64.c
113+ ```c
114+ DEFINE_PER_CPU_PAGE_ALIGNED(struct irq_stack, irq_stack_backing_store) __visible;
115+ DECLARE_INIT_PER_CPU(irq_stack_backing_store);
116+ ...
117+ #ifdef CONFIG_VMAP_STACK
118+ ...
119+ #else
120+ static int map_irq_stack(unsigned int cpu)
121+ {
122+ void *va = per_cpu_ptr(&irq_stack_backing_store, cpu);
123+
124+ per_cpu(hardirq_stack_ptr, cpu) = va + IRQ_STACK_SIZE;
125+ return 0;
126+ }
127+ #endif
128+ ```
129+ * 因此` hardirq_stack_ptr ` 指向的是栈顶(高地址)
130+
131+ ### 切换中断栈
132+ * v5.4
133+ ``` cpp
134+ irq_entries_start
135+ jmp common_interrupt
136+ call interrupt_entry
137+ ENTER_IRQ_STACK old_rsp=%rdi save_ret=1
138+ movq \old_rsp, PER_CPU_VAR(irq_stack_backing_store + IRQ_STACK_SIZE - 8 )
139+ movq PER_CPU_VAR (hardirq_stack_ptr), %rsp
140+ ```
141+ * v6.0
142+ ```cpp
143+ irq_entries_start
144+ jmp asm_common_interrupt
145+ call common_interrupt
146+ -> irqentry_state_t state = irqentry_enter(regs);
147+ u32 vector = (u32)(u8)error_code;
148+ run_irq_on_irqstack_cond(__##func, regs, vector)
149+ call_on_irqstack_cond(func, regs, ASM_CALL_IRQ, IRQ_CONSTRAINTS, regs, vector);
150+ call_on_irqstack(func, asm_call, constr);
151+ call_on_stack(__this_cpu_read(hardirq_stack_ptr), func, asm_call, argconstr)
152+ register void *tos asm("r11");
153+ tos = ((void *)(stack));
154+ "movq %%rsp, (%[tos]) \n"
155+ "movq %[tos], %%rsp \n"
156+ asm_call
157+ => __common_interrupt()
158+ -> irqentry_exit(regs, state)
159+ ```
160+ * 在这个 commit 后中断栈的使用发生了一些变化,
161+ * [[ patch V2 00_13
] x86_irq_64 Inline irq stack switching
] ( https://lore.kernel.org/all/[email protected] / ) 162+ * [ x86/entry: Convert system vectors to irq stack macro] ( https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=569dd8b4eb7ef666b467c41b8e8e4f2820d07f67 )
163+ * 对于用户程序被中断或者已经有中断正在 per-CPU 的中断栈上被处理的情况,直接调用` __common_interrupt() `
164+ * 中断的是用户程序,用的应该是 trampoline stack
165+ * 已经有中断正在 per-CPU 中断栈上被处理,则使用 * 当前内核栈*
166+
100167## 异常栈
101168* 对于无需进行 privilege-level 变化的情况,比如异常发生时 CPU 运行在内核态
102169 1 . CPU 依然会将被中断进程的` ss ` 、` rsp ` 、` rflags ` 、` cs ` 、` rip ` 、` error code ` 等压入 * 当前内核栈*
@@ -239,7 +306,7 @@ start_kernel()
239306-> trap_init()
240307 -> cpu_init_exception_handling() /* Initialize TSS before setting up traps so ISTs work */
241308```
242- * 通过以下方式设定 PerCPU 的 `cpu_tss_rw` 的 `x86_tss.ist[]` 数组元素的值,指示的不同类型的 IST 异常的栈顶(高地址)
309+ * 通过以下方式设定 per-CPU 的 `cpu_tss_rw` 的 `x86_tss.ist[]` 数组元素的值,指示的不同类型的 IST 异常的栈顶(高地址)
243310```cpp
244311static inline void tss_setup_ist(struct tss_struct * tss)
245312{
@@ -340,7 +407,7 @@ struct entry_stack_page {
340407} __aligned(PAGE_SIZE);
341408```
342409### Trampoline stack 的设置
343- * Trampoline stack 的值由启动时` cpu_init() ` 调用` load_sp0() ` 设置 TSS 的` sp0 ` 为` entry_stack_storage ` 的地址
410+ * Trampoline stack 的值由启动时` cpu_init() ` 调用` load_sp0() ` 设置 TSS 的` sp0 ` 为` entry_stack_storage + PAGE_SIZE ` 的地址,也就是栈顶(高地址)
344411 * arch/x86/kernel/cpu/common.c
345412``` cpp
346413void cpu_init (void)
@@ -538,7 +605,7 @@ PAGE DIRECTORY: 4201067
538605ffd4000001fe8500 7fa14000 0 0 1 fffffc0001000 reserved
539606crash>
540607```
541- * ` 0xfffffe0000003000 ` 是 trampoline stack 的栈顶,故栈实为` 0xfffffe0000002000 - 0xfffffe0000002fff `
608+ * ` 0xfffffe0000003000 ` 是 trampoline stack 的栈顶(高地址) ,故栈实为` 0xfffffe0000002000 - 0xfffffe0000002fff `
542609* ` 0xff1100007fa14000 ` 为实际存储的 per-CPU 变量` entry_stack_storage ` 的地址
543610* ` vtop ` 的结果观察到它们都映射到了同一个物理页` 0x7fa14000 ` 上
544611
0 commit comments