x86 Segment Addressing Revisited

Memory segmentation was first introduced to x86 family with 8086, to make it possible to access 1MB physical memory under 16bit addressing mode.

Real Mode

Logical address points directly into physical memory location.

Logical address consists of two parts: segment and offset.

Physical address is calculated as Segment * 16 + Offset, and if the A20 line is not enabled, the physical address is wrapped around into 1MiB range.

Protected Mode

Two stages of address translation would be used to arrive at a physical address: logical-address translation and linear address space paging.

Logical address consists of two parts: segment selector and offset.

Segment selector is used to determine the base address as well as the access rights. Segment selelctors are maintained in a table known as GDT (Global descriptor-table) or LDT (Local descriptor-table), which is referenced by GDTR or LDTR register.

When a thread is running in kernel mode, fs:[0] points to the PCR (Processor Control Region). Let's find it by manually translating the logical address:

 kd> r fs
fs=00000030

kd>  .formats @fs
Evaluate expression:
  Hex:     00000030
  Decimal: 48
  Octal:   00000000060
  Binary:  00000000 00000000 00000000 00110000
  Chars:   ...0
  Time:    Thu Jan 01 08:00:48 1970
  Float:   low 6.72623e-044 high 0
  Double:  2.37152e-322

According to the x86 architecture specification, we have Index = 6 (0000000000110), Table Indicator = GDT (0) and RPL (Requested Privilege Level) = 0.

 kd> dd @gdtr
8003f000  00000000 00000000 0000ffff 00cf9b00
8003f010  0000ffff 00cf9300 0000ffff 00cffb00
8003f020  0000ffff 00cff300 200020ab 80008b04
8003f030  f0000001 ffc093df 00000fff 0040f300
8003f040  0400ffff 0000f200 00000000 00000000
8003f050  a0000068 80008954 a0680068 80008954
8003f060  2f40ffff 00009302 80003fff 0000920b
8003f070  700003ff ff0092ff 0000ffff 80009a40

We can get the layout of segment descriptor from x86 architecture specification:

Now we can verify the address translation by looking at the memory contents:

 kd> dg @fs
                                  P Si Gr Pr Lo
Sel    Base     Limit     Type    l ze an es ng Flags
---- -------- -------- ---------- - -- -- -- -- --------
0030 ffdff000 00001fff Data RW Ac 0 Bg Pg P  Nl 00000c93
 kd> dd fs:[0] 
0030:00000000  805495d0 80549df0 80547000 00000000
0030:00000010  00000000 00000000 00000000 ffdff000
0030:00000020  ffdff120 0000001c 00000000 00000000
0030:00000030  ffff2050 80545bb8 8003f400 8003f000
0030:00000040  80042000 00010001 00000001 00000064
0030:00000050  00000000 00000000 00000000 00000000
0030:00000060  00000000 00000000 00000000 00000000
0030:00000070  00000000 00000000 00000000 00000000

kd> dd ffdff000
ffdff000  805495d0 80549df0 80547000 00000000
ffdff010  00000000 00000000 00000000 ffdff000
ffdff020  ffdff120 0000001c 00000000 00000000
ffdff030  ffff2050 80545bb8 8003f400 8003f000
ffdff040  80042000 00010001 00000001 00000064
ffdff050  00000000 00000000 00000000 00000000
ffdff060  00000000 00000000 00000000 00000000
ffdff070  00000000 00000000 00000000 00000000

It's time to dump the KPCR structure:

 kd> dt nt!_KPCR ffdff000
   +0x000 NtTib            : _NT_TIB
   +0x01c SelfPcr          : 0xffdff000 _KPCR
   +0x020 Prcb             : 0xffdff120 _KPRCB
   +0x024 Irql             : 0x1c ''
   +0x028 IRR              : 0
   +0x02c IrrActive        : 0
   +0x030 IDR              : 0xffff2050
   +0x034 KdVersionBlock   : 0x80545bb8 Void
   +0x038 IDT              : 0x8003f400 _KIDTENTRY
   +0x03c GDT              : 0x8003f000 _KGDTENTRY
   +0x040 TSS              : 0x80042000 _KTSS
   +0x044 MajorVersion     : 1
   +0x046 MinorVersion     : 1
   +0x048 SetMember        : 1
   +0x04c StallScaleFactor : 0x64
   +0x050 DebugActive      : 0 ''
   +0x051 Number           : 0 ''
   +0x052 Spare0           : 0 ''
   +0x053 SecondLevelCacheAssociativity : 0 ''
   +0x054 VdmAlert         : 0
   +0x058 KernelReserved   : [14] 0
   +0x090 SecondLevelCacheSize : 0
   +0x094 HalReserved      : [16] 0
   +0x0d4 InterruptMode    : 0
   +0x0d8 Spare1           : 0 ''
   +0x0dc KernelReserved2  : [17] 0
   +0x120 PrcbData         : _KPRCB

kd>  !pcr
KPCR for Processor 0 at ffdff000:
    Major 1 Minor 1
  NtTib.ExceptionList: 805495d0
       NtTib.StackBase: 80549df0
      NtTib.StackLimit: 80547000
    NtTib.SubSystemTib: 00000000
         NtTib.Version: 00000000
     NtTib.UserPointer: 00000000
         NtTib.SelfTib: 00000000

               SelfPcr: ffdff000
                  Prcb: ffdff120
                  Irql: 0000001c
                   IRR: 00000000
                   IDR: ffff2050
         InterruptMode: 00000000
                   IDT: 8003f400
                   GDT: 8003f000
                   TSS: 80042000

         CurrentThread: 80552840
            NextThread: 00000000
            IdleThread: 80552840

             DpcQueue: