swapgs命令

x64で追加された命令のひとつにswapgs(Swap GS Base Register)がある。この命令は、

現在の GS ベースレジスタ値を、MSR アドレス C0000102H(MSR_KERNELGSbase)に格納された値と交換する。
  ――インテル® エクステンデッド・メモリ 64 テクノロジ・ソフトウェア・デベロッパーズ・ガイド 第 2巻(全 2巻)

特権命令で、Windowsカーネルにおいてもよく利用されている。しかし、なぜこんなチープな命令が新設されたか疑問に思ったので調べてみると、どうやら、x86のsysenterに相当するx64のsyscall命令がRSPの値を更新しないため、なんらかの方法でRSPカーネル用に設定する必要にせまられ、その対応としてできた命令らしい。

SWAPGSは、KernelGSbase MSRのCPL 0データポインタをGSベースレジスタと交換する。するとカーネルは、通常のメモリ参照でGS プリフィックスを使用してカーネルデータ構造体にアクセスできるようになる。

ここでいうカーネル構造体は、Windowsでは KPCR 構造体である。以下は Win7 x64 における割り込み関連関数のひとつで、swapgsのあとに gs 経由で rsp の値を更新している。

nt!KiSystemCall64:
fffff800`0307df40 0f01f8                   swapgs
fffff800`0307df43 654889242510000000 mov   qword ptr gs:[10h],rsp
fffff800`0307df4c 65488b2425a8010000 mov   rsp,qword ptr gs:[1A8h]
...
1: kd> dt nt!_kpcr -r1
   +0x000 NtTib            : _NT_TIB
   +0x000 GdtBase          : Ptr64 _KGDTENTRY64
   +0x008 TssBase          : Ptr64 _KTSS64
   +0x010 UserRsp          : Uint8B
   +0x018 Self             : Ptr64 _KPCR
   +0x020 CurrentPrcb      : Ptr64 _KPRCB 
   ...
   +0x118 PcrAlign1        : [24] Uint4B
   +0x180 Prcb             : _KPRCB
      +0x000 MxCsr            : Uint4B
      +0x004 LegacyNumber     : UChar
      +0x005 ReservedMustBeZero : UChar
      +0x006 InterruptRequest : UChar
      +0x007 IdleHalt         : UChar
      +0x008 CurrentThread    : Ptr64 _KTHREAD
      +0x010 NextThread       : Ptr64 _KTHREAD
      +0x018 IdleThread       : Ptr64 _KTHREAD
      +0x020 NestingLevel     : UChar
      +0x021 PrcbPad00        : [3] UChar
      +0x024 Number           : Uint4B
      +0x028 RspBase          : Uint8B
      ...


いろいろ調べごとをしているが、x64は私にとってかなり未知な部分が多くて面白い。