在中断或异常处理的entry代码处, 会执行SWAPGS切换到kernel GS, GS.base 是存储了中断stack 的地址。
1、关于swapgs指令,手册描述如下:
When FS and GS segment overrides are used in 64-bit mode, their respective base addresses are used in the linear
address calculation: (FS or GS).base + index + displacement. FS.base and GS.base are then expanded to the full
linear-address size supported by the implementation. The resulting effective address calculation can wrap across
positive and negative addresses; the resulting linear address must be canonical.
1)The SWAPGS instruction is available only in 64-bit mode. It swaps the contents of two specific MSRs (IA32_GS_BASE and IA32_KERNEL_GS_BASE).
2)The IA32_GS_BASE MSR shadows the base address portion of the GS descriptor register; the IA32_KERNEL_GS_BASE MSR holds the base address of the GS segment used by the kernel (typically it houses kernel structures).
3)SWAPGS is intended for use with fast system calls when in 64-bit mode to allow immediate access to kernel structures on transition to kernel mode.
由此可见,64位长模式下,FS和GS寄存器已经和GDT没有关系,其基址保存在MSR_FS_BASE和MSR_GS_BASE中。
2、Linux下GS的初始化过程
2.1 <arch/x86/kernel/head_64.S>
/* Set up %gs.
*
* The base of %gs always points to the bottom of the irqstack
* union. If the stack protector canary is enabled, it is
* located at %gs:40. Note that, on SMP, the boot cpu uses
* init data section till per cpu areas are set up.
*/
movl $MSR_GS_BASE,%ecx
movl initial_gs(%rip),%eax
movl initial_gs+4(%rip),%edx
wrmsr
......
GLOBAL(initial_gs)
.quad INIT_PER_CPU_VAR(irq_stack_union)
这是早期boot cpu对MSR_GS_BASE初始化,后续创建per cpu areas后会重新对MSR_GS_BASE进行赋值。
<arch/x86/include/asm/processor.h>
union irq_stack_union {
char irq_stack[IRQ_STACK_SIZE];
/*
* GCC hardcodes the stack canary as %gs:40. Since the
* irq_stack is the object at %gs:0, we reserve the bottom
* 48 bytes of the irq stack for the canary.
*/
struct {
char gs_base[40];
unsigned long stack_canary;
};
};
<arch/x86/include/asm/page_64_types.h>
#ifdef CONFIG_KASAN
#define KASAN_STACK_ORDER 1
#else
#define KASAN_STACK_ORDER 0
#endif
#define IRQ_STACK_ORDER (2 + KASAN_STACK_ORDER)
#define IRQ_STACK_SIZE (PAGE_SIZE << IRQ_STACK_ORDER)
由此可见,CONFIG_KASAN=n的情况下,IRQ_STACK_SIZE等于1M。
2.2 <arch/x86/kernel/cpu/common.c>
void load_percpu_segment(int cpu)
{
#ifdef CONFIG_X86_32
loadsegment(fs, __KERNEL_PERCPU);
#else
loadsegment(gs, 0); /* 将%gs段寄存器清零 */
/* 将per_cpu变量irq_stack_union.gs_base赋值给每个CPU的MSR_GS_BASE */
wrmsrl(MSR_GS_BASE, (unsigned long)per_cpu(irq_stack_union.gs_base, cpu));
#endif
load_stack_canary_segment();
}
问题1:irq_stack_union.gs_base是在何处初始化的?//没找到
问题2:MSR_KERNEL_GS_BASE中保存的基址用于什么?中断处理中为什么要swapgs?