You are on page 1of 94

Basics of X86 architecture

Abhijit A M
abhijit.comp@coep.ac.in

Credits: Notes by Prof. Sorav Bansal, https://www.felixcloutier.com/x86/, Intel Documentation


Any processor has

integer registers and their execution unit

floating-point/vector registers and execution
unit(s)

memory management unit (MMU)

multiprocessor/multicore: local interrupt
controller (APIC)

etc
Segmentation
Compiler’s view of the program
Segmentation
Address Translation
8086: 16 bit CPU
(precursor to x86 family)

https://jerry153fish.github.io/2016/01/01/8086-Registers.html
8086 Registers

16 bit ought to be enough ! (?)

General purpose registers

four 16-bit data registers: AX, BX, CX, DX

each in two 8-bit halves, e.g. AH and AL

Registers for memory addressing

SP, BP, SI, DI : 16 bit

SP stack pointer, BP base pointer, SI Source Index, DI
Destination Index

IP instruction Pointer

Addressible memory: 2^16 = 64 kb
8086 address extension

8086 has 20-bit physical addresses ==> 1 MB AS

the extra four bits usually come from a 16-bit "segment
register":

CS - code segment, for fetches via IP

SS - stack segment, for load/store via SP and BP

DS - data segment, for load/store via other registers

ES - another data segment, destination for string operations

%cs:%ip full address: %cs * 16 + %ip is actual address that
goes on bus. virtual to physical translation is

pa = va + seg*16

e.g. set CS = 4096 to execute starting at 65536
8086 address extension

Extending ‘address space’ with the help of
MMU

FLAGS register

FLAGS - various condition codes: whether last
arithmetic operation

overflowed

was positive/negative

was [not] zero

carry/borrow on add/subtract

etc.

whether interrupts are enabled

direction of data copy instructions

Uses in JP, JN, J[N]Z, J[N]C, J[N]O ...
32 bit 80386

boots in 16-bit mode, then on running particular
instructions switches to 32-bit mode (protected
mode)

registers are 32 bits wide, called EAX rather than AX

EAX EBX ECX EDX

ESP EBP ESI EDI

operands and addresses 32-bit in 32-bit mode,

e.g. ADD does 32-bit arithmetic

Segment registers are 16 bit: CS, SS, DS, etc.
32 bit 80386

Still possible to access 16 bit registers using AX or BX

Specifix coding of machine instructions to tell whether
operands are 16 or 32 bit

prefixes 0x66/0x67 toggle between 16-bit and 32-bit
operands/addresses respectively

in 32-bit mode, MOVW is expressed as 0x66 MOVW

the .code32 in bootasm.S tells assembler to generate 0x66
for e.g. MOVW

80386 also changed segments and added paged
memory...
Summary of registers in 80386
(32 bit)

General registers

32 bits : EAX EBX ECX EDX

16 bits : AX BX CX DX

8 bits : AH AL BH BL CH CL DH DL
Summary of registers in 80386

Segment Registers

CS: Holds the Code segment in which your
program runs.

DS : Holds the Data segment that your program
accesses.

ES,FS,GS : These are extra segment registers

SS : Holds the Stack segment your program
uses.

All 16 bit
Summary of registers in 80386

For a typical register, the 
SS:EBP
corresponding segment is 
Stack Segment: EBP ==>
used. Pairs of Indexes & mov (%ebp), %eax ==>
pointers (Segment & for accessing (%ebp) SS:
Registers) EBP address will be used

CS:EIP

DS:ESI , ES: EDI .

Code Segment: Index Pointer 
ESI: Extended Source

E.g. mov $32, %eax ==> The Index, EDI: Extended
code of move instruction Destination Index
uses CS: EIP

SS:ESP

The EFLAGS register

Stack Segment: ESP for flags

E.g. push $32 ==> The $32
will be pushed on stack.
Using SS: ESP address
X86 Assembly Code

Syntax

Intel syntax: op dst, src (Intel manuals!)

AT&T (gcc/gas) syntax: op src, dst (xv6)

uses b, w, l suffix on instructions to specify size
of operands

Operands are registers, constant, memory
via register, memory via constant
Examples of X86 instructions
AT&T syntax "C"-ish equivalent Operands
movl %eax, %edx edx = eax; register mode
movl $0x123, %edx edx = 0x123; immediate
movl 0x123, %edx edx = direct
*(int32_t*)0x123;
movl (%ebx), %edx edx = *(int32_t*)ebx; indirect
movl 4(%ebx), %edx edx = *(int32_t*) displaced
(ebx+4)
Instructions suffix/prefix

mov %eax, %ebx # 32 bit data

movw %ax, %bx # move 16 bit data

mov %ax, %bx # ax is 16 bit, so equivalent
to movw

mov $123, 0x123 # Ambigious

movw $123, 0x123 # correct, move 16 bit
data

Types of Instructions

data movement 
Control

MOV, PUSH, POP, ... 
JMP, JZ, JNZ, CALL,
RET

Arithmetic

TEST, SHL, ADD,

String
AND, ... 
REP MOVSB, ...

i/o 
System

IN, OUT, ... 
IRET, INT
Stack, given by %esp

Grows downwards

pushl %eax

Semantics are: substract 4 (32 bit value = 4 bytes) from esp
(grow downwards), and mov eax to memory pointed by esp, so
sub $4, %esp # esp = esp -4
mov %eax, (%esp) # *esp = eax

pop %eax

has opposite semantics, that is
movl (%esp), %eax
addl $4, %esp
Function calls using stack:
Approximate picture

main() { ... a(); ...} a(){ ... b();... c(); ...}

Each “frame’ contains the local variables for that function


GCC calling convention
int add(int x, int y) { 
Note: single set of
int z; registes for ENTIRE
z = x + y; program!
return z; 
For this code to work,
} questions in front of
compiler
int main() { 
Variables m and n have to
int m, n, sum, prod; be passed to add() - using
scanf("%d%d", &m, ‘pass by value’
&n); 
They are passed on stack –
sum = add(m, n); but how?

How to split the access of
general purpose registers?

add() should not modify
registers in access by main()
Picture of stack frame growth
GCC Calling 
after ret instruction:
%eip contains return
convention

address
Contract between caller 
%esp points at
(function) and callee arguments pushed by
(called function) caller

called function may have

at entry to a function trashed formal
(i.e. just after call): arguments

%eip points at first

%eax (and %edx, if
instruction of function return type is 64-bit)
contains return value (or

%esp+4 (upwards, trash if function is void)
reverse direction!) 
%eax, %edx (above), and
points at first %ecx may be trashed
argument 
%ebp, %ebx, %esi, %edi

%esp points at return must contain contents
address from time of call
“caller” save and “callee” save
registers

Terminology:

%eax, %ecx, %edx are "caller save" registers

%ebp, %ebx, %esi, %edi are "callee save" registers

Means, if main()->add() then code of add()
function (callee) can

freely use eax, ecx, edx and

If it uses, ebp, ebx, etc. then

it must save them before using and restore them before
returning
GCC does more than that !

function prologue: 
function epilogue

pushl %ebp 
movl %ebp, %esp

movl %esp, %ebp 
popl %ebp

(ret)

each function has a stack frame marked by %ebp, %esp

%esp can move to make stack frame bigger, smaller

%ebp points at saved %ebp from previous function,
chain to walk stack
Stack Frame
ebp, for debugging

The frame pointer (in ebp) is not strictly

Compiler can compute the address of its return address and
function arguments based on its knowledge of the current
depth of the stack pointer (in esp).

The frame pointer is useful for debugging purposes

Current function is based on the current value of eip.

Current value of *(ebp+4) provides the return address of the caller.

Current value of *((*ebp) + 4) (where *ebp contains the saved ebp
of the caller) provides the return address of the caller's caller

Current value of *(*(*ebp) + 4) provides the return address of the
caller's caller's caller, and so on . . .
Let’s see a demo of how the stack is built and destroyed during function calls, on a
Linux machine using GCC.

Consider this C code


int mult(int a, int b) { mult:
int c, d = 20, e = 30, f; pushl %ebp
f = add(d, e); movl %esp, %ebp
c = a * b + f; subl $24, %esp
return c; movl $20, -24(%ebp)
} movl $30, -20(%ebp)
int add(int x, int y) { subl $8, %esp
int z; pushl -20(%ebp)
z = x + y; pushl -24(%ebp)
return z; call add
} addl $16, %esp
movl %eax, -16(%ebp)
Translated to assembly as: movl 8(%ebp), %eax
add: imull 12(%ebp), %eax
pushl %ebp movl %eax, %edx
movl %esp, %ebp movl -16(%ebp), %eax
subl $16, %esp addl %edx, %eax
movl 8(%ebp), %edx movl %eax, -12(%ebp)
movl 12(%ebp), %eax movl -12(%ebp), %eax
addl %edx, %eax leave
movl %eax, -4(%ebp) ret
movl -4(%ebp), %eax
leave
ret
/* Control is here */
int mult(int a, int b) {
Stack int c, d = 20, e = 30, f;
f = add(d, e);
c = a * b + f;
return c;
}
X int add(int x, int y) {
b int z;
a ebp z = x + y;
RetAddr main()
return z;
}

esp * Control is here */


mult:
pushl %ebp
movl %esp, %ebp
subl $24, %esp
movl $20, -24(%ebp)
movl $30, -20(%ebp)
subl $8, %esp
pushl -20(%ebp)
pushl -24(%ebp)
call add
int mult(int a, int b) {
int c, d = 20, e = 30, f;
Stack f = add(d, e);
c = a * b + f;
return c;
}
int add(int x, int y) {
X int z;
b z = x + y;
a return z;
RetAddr main()
}

X (prev ebp)
Y
ebp
esp
mult:
pushl %ebp
movl %esp, %ebp
subl $24, %esp
movl $20, -24(%ebp)
movl $30, -20(%ebp)
subl $8, %esp
pushl -20(%ebp)
pushl -24(%ebp)
call add
int mult(int a, int b) {
int c, d = 20, e = 30, f;
Stack f = add(d, e);
c = a * b + f;
return c;
}
int add(int x, int y) {
X int z;
b z = x + y;
a return z;
RetAddr main()
}

X (prev ebp)
Y

ebp
mult:
esp pushl %ebp
movl %esp, %ebp
subl $24, %esp
movl $20, -24(%ebp)
movl $30, -20(%ebp)
subl $8, %esp
pushl -20(%ebp)
pushl -24(%ebp)
call add
int mult(int a, int b) {
int c, d = 20, e = 30, f;
Stack f = add(d, e);
c = a * b + f;
return c;
}
int add(int x, int y) {
X int z;
b z = x + y;
a return z;
RetAddr main()
}

X (prev ebp)
Y

ebp
mult:
pushl %ebp
movl %esp, %ebp
subl $24, %esp
movl $20, -24(%ebp)
movl $30, -20(%ebp)
esp subl $8, %esp
pushl -20(%ebp)
pushl -24(%ebp)
call add
int mult(int a, int b) {
int c, d = 20, e = 30, f;
Stack f = add(d, e);
c = a * b + f;
return c;
}
int add(int x, int y) {
X int z;
b z = x + y;
a return z;
RetAddr main()
}

X (prev ebp)
Y

ebp
mult:
pushl %ebp
movl %esp, %ebp
e = 30 subl $24, %esp
d = 20 movl $20, -24(%ebp)
movl $30, -20(%ebp)
subl $8, %esp
esp pushl -20(%ebp)
pushl -24(%ebp)
call add
int mult(int a, int b) {
int c, d = 20, e = 30, f;
Stack f = add(d, e);
c = a * b + f;
return c;
}
int add(int x, int y) {
X int z;
b z = x + y;
a return z;
RetAddr main()
}

X (prev ebp)
Y

ebp
mult:
pushl %ebp
movl %esp, %ebp
e = 30 subl $24, %esp
movl $20, -24(%ebp)
d = 20
movl $30, -20(%ebp)
subl $8, %esp
pushl -20(%ebp)
pushl -24(%ebp)
call add
esp
Stack
X int mult(int a, int b) {
b int c, d = 20, e = 30, f;
a f = add(d, e);
c = a * b + f;
RetAddr main()
return c;
X (prev ebp) }
Y int add(int x, int y) {
int z;
ebp z = x + y;
return z;
}

e = 30
d = 20

mult:
pushl %ebp
30 (y) movl %esp, %ebp
20 (x) subl $24, %esp
movl $20, -24(%ebp)
movl $30, -20(%ebp)
esp subl $8, %esp
pushl -20(%ebp)
pushl -24(%ebp)
call add
Stack
X int mult(int a, int b) {
b int c, d = 20, e = 30, f;
a f = add(d, e);
c = a * b + f;
RetAddr main()
return c;
X (prev ebp) }
Y int add(int x, int y) {
int z;
ebp z = x + y;
return z;
}

e = 30
d = 20

mult:
pushl %ebp
30 (y) movl %esp, %ebp
20 (x) subl $24, %esp
movl $20, -24(%ebp)
Retadd mult() movl $30, -20(%ebp)
subl $8, %esp
pushl -20(%ebp)
esp
pushl -24(%ebp)
call add
Stack X b
int mult(int a, int b) {
a int c, d = 20, e = 30, f;
RetAddr main() f = add(d, e);
c = a * b + f;
X (prev ebp) return c;
Y
}
int add(int x, int y) {
int z;
z = x + y;
return z;
}
e = 30
d = 20
add:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
30 (y) movl 8(%ebp), %edx
20 (x) movl 12(%ebp), %eax
addl %edx, %eax
Retadd mult()
movl %eax, -4(%ebp)
Y(prev ebp) movl -4(%ebp), %eax
leave
ret
ebp

esp
Stack X
b
int mult(int a, int b) {
a int c, d = 20, e = 30, f;
RetAddr main() f = add(d, e);
c = a * b + f;
X (prev ebp) return c;
Y
}
int add(int x, int y) {
int z;
z = x + y;
return z;
}
e = 30
d = 20
edx = 20 add:
eax = 30 pushl %ebp
eax = eax + edx = 50 movl %esp, %ebp
subl $16, %esp
30 (y) movl 8(%ebp), %edx
20 (x) movl 12(%ebp), %eax
addl %edx, %eax
Retadd mult()
movl %eax, -4(%ebp)
Y(prev ebp) movl -4(%ebp), %eax
leave
ret
ebp

esp
Stack X
b
int mult(int a, int b) {
a int c, d = 20, e = 30, f;
RetAddr main() f = add(d, e);
c = a * b + f;
X (prev ebp) return c;
Y
}
int add(int x, int y) {
int z;
z = x + y;
return z;
}
e = 30
d = 20
eax = 50 add:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
30 (y) movl 8(%ebp), %edx
20 (x) movl 12(%ebp), %eax
addl %edx, %eax
Retadd mult()
movl %eax, -4(%ebp)
Y(prev ebp) movl -4(%ebp), %eax
50 leave
ret
ebp Some redundant code
generated here. Before
esp “leave”. Result is in eax
Stack X
b
int mult(int a, int b) {
a int c, d = 20, e = 30, f;
RetAddr main() f = add(d, e);
c = a * b + f;
X (prev ebp) leave: step 1 return c;
Y
}
int add(int x, int y) {
int z;
z = x + y;
return z;
}
e = 30
d = 20
eax = 50 add:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
30 (y) movl 8(%ebp), %edx
20 (x) movl 12(%ebp), %eax
addl %edx, %eax
Retadd mult()
movl %eax, -4(%ebp)
Y(prev ebp) movl -4(%ebp), %eax
50 leave # # Set ESP to
EBP, then pop EBP.
ebp ret

esp
Stack X
b
int mult(int a, int b) {
a int c, d = 20, e = 30, f;
RetAddr main() f = add(d, e);
c = a * b + f;
X (prev ebp) leave: step 2 return c;
Y
}
int add(int x, int y) {
int z;
z = x + y;
return z;
}
e = 30
d = 20 eax = 50 add:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
30 (y) movl 8(%ebp), %edx
20 (x) movl 12(%ebp), %eax
addl %edx, %eax
Retadd mult()
movl %eax, -4(%ebp)
Y(prev ebp) movl -4(%ebp), %eax
50 leave # # Set ESP to
EBP, then pop EBP.
ebp ret

esp
Stack X
b
int mult(int a, int b) {
a int c, d = 20, e = 30, f;
RetAddr main() f = add(d, e); // here
c = a * b + f;
X (prev ebp) return c;
Y
}
int add(int x, int y) {
int z;
z = x + y;
return z;
}
e = 30
d = 20 eax = 50 add:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
30 (y) movl 8(%ebp), %edx
20 (x) movl 12(%ebp), %eax
addl %edx, %eax
Retadd mult()
movl %eax, -4(%ebp)
Y(prev ebp) movl -4(%ebp), %eax
50 leave # # Set ESP to
EBP, then pop EBP.
ebp ret

esp
Stack X
b
int mult(int a, int b) {
a int c, d = 20, e = 30, f;
RetAddr main() f = add(d, e); // here
c = a * b + f;
X (prev ebp) return c;
Y
}
int add(int x, int y) {
int z;
z = x + y;
return z;
}
e = 30
d = 20 eax = 50 Mult:
....
call add
addl $16, %esp
30 (y) movl %eax, -16(%ebp)
20 (x) movl 8(%ebp), %eax
imull 12(%ebp), %eax
Retadd mult()
movl %eax, %edx
Y(prev ebp) movl -16(%ebp), %eax
50 addl %edx, %eax
movl %eax, -12(%ebp)
ebp movl -12(%ebp), %eax
leave
esp ret
Stack X
b
int mult(int a, int b) {
a int c, d = 20, e = 30, f;
RetAddr main() f = add(d, e);
c = a * b + f;
X (prev ebp) return c;
Y
}
int add(int x, int y) {
int z;
z = x + y;
f = 50 (eax) return z;
}
e = 30
d = 20 eax = 50 Mult:
....
call add
addl $16, %esp
30 (y) movl %eax, -16(%ebp)
20 (x) movl 8(%ebp), %eax
imull 12(%ebp), %eax
Retadd mult()
movl %eax, %edx
Y(prev ebp) movl -16(%ebp), %eax
50 addl %edx, %eax
movl %eax, -12(%ebp)
ebp movl -12(%ebp), %eax
leave
esp ret
Stack X
b
int mult(int a, int b) {
a int c, d = 20, e = 30, f;
RetAddr main() f = add(d, e);
c = a * b + f;
X (prev ebp) return c;
Y
}
int add(int x, int y) {
int z;
z = x + y;
f = 50 return z;
}
e = 30
d = 20 eax = a Mult:
eax = eax * b ....
edx = eax call add
eax = f addl $16, %esp
30 (y) eax = edx + eax movl %eax, -16(%ebp)
20 (x) // eax = a*b + f movl 8(%ebp), %eax
imull 12(%ebp), %eax
Retadd mult()
movl %eax, %edx
Y(prev ebp) movl -16(%ebp), %eax
50 addl %edx, %eax
movl %eax, -12(%ebp)
ebp movl -12(%ebp), %eax
leave
esp ret
Stack X
b
int mult(int a, int b) {
a int c, d = 20, e = 30, f;
RetAddr main() f = add(d, e);
c = a * b + f;
X (prev ebp) return c;
Y
}
int add(int x, int y) {
int z;
c = eax z = x + y;
f = 50 return z;
}
e = 30
d = 20 // eax = a*b + f Mult:
....
call add
addl $16, %esp
30 (y) movl %eax, -16(%ebp)
Again
20 (x) some movl 8(%ebp), %eax
redundant imull 12(%ebp), %eax
Retadd mult()
code movl %eax, %edx
Y(prev ebp) movl -16(%ebp), %eax
50 addl %edx, %eax
movl %eax, -12(%ebp)
ebp movl -12(%ebp), %eax
leave
esp ret
Stack X
b
int mult(int a, int b) {
a int c, d = 20, e = 30, f;
RetAddr main() f = add(d, e);
c = a * b + f;
X (prev ebp) return c;
Y
}
int add(int x, int y) {
int z;
c = eax z = x + y;
f = 50 return z;
}
e = 30
d = 20 After leave Mult:
// eax = a*b + f ....
call add
addl $16, %esp
30 (y) movl %eax, -16(%ebp)
20 (x) movl 8(%ebp), %eax
imull 12(%ebp), %eax
Retadd mult()
movl %eax, %edx
Y(prev ebp) movl -16(%ebp), %eax
50 addl %edx, %eax
movl %eax, -12(%ebp)
ebp movl -12(%ebp), %eax
leave
esp ret
Lessons

Calling function (caller)

Pushes arguments on stack , copies values

On call

Return IP is pushed

Initially in called function (callee)

Old ebp is pushed

ebp = stack

Stack is decremented to make space for local
variables
Lessons

Before Return

Ensure that result is in ‘eax

On Return

stack = ebp

Pop ebp (ebp = old ebp)

On ‘ret’

Pop ‘return IP’ and go back in old function
Lessons

This was a demonstration for a

User program, compiled with GCC, On Linux

Followed the conventions we discussed earlier

Applicable to

C programs which work using LIFO function calls

Compiler can’t generate code using this
mechanism for

Functions like fork(), exec(), scheduler(), etc.

Boot code of OS
Memory Management
X86 address : protected mode
address translation
X86 paging
Segmentation + Paging

Selector value is
implicit based on
address being
accessed.
Instruction: CS
Stack Variable: SS
Data: DS
etc.
GDT Entry
Page Directory Entry (PDE)
Page Table Entry (PTE)
Segment selector

EFLAGS register
CR0

PG: Paging enabled or not WP: Write protecion on/off


PE: Protection Enabled --> protected mode.

CR2
CR3

CR4
mmu.h : paging related macros
#define PTXSHIFT 12 // offset of PTX in a linear address
#define PDXSHIFT 22 // offset of PDX in a linear address
#define PDX(va) (((uint)(va) >> PDXSHIFT) & 0x3FF)// page
directory index
#define PTX(va) (((uint)(va) >> PTXSHIFT) & 0x3FF)// page table
index
// construct virtual address from indexes and offset
#define PGADDR(d, t, o) ((uint)((d) << PDXSHIFT | (t) << PTXSHIFT |
(o)))
// +--------10------+-------10-------+---------12----------+
// | Page Directory | Page Table | Offset within Page |
// | Index | Index | |
// +----------------+----------------+---------------------+
// \--- PDX(va) --/ \--- PTX(va) --/
mmu.h : paging related macros
// Page directory and page table constants.
#define NPDENTRIES 1024 // #
directory entries per page directory
#define NPTENTRIES 1024 // # PTEs
per page table
#define PGSIZE 4096 // bytes
mapped by a page
#define PGROUNDUP(sz) (((sz)+PGSIZE-1) &
~(PGSIZE-1))
#define PGROUNDDOWN(a) (((a)) & ~(PGSIZE-1))
mmu.h : paging related macros
// Page table/directory entry flags.
#define PTE_P 0x001 // Present
#define PTE_W 0x002 // Writeable
#define PTE_U 0x004 // User
#define PTE_PS 0x080 // Page Size

// Address in page table or page directory entry


#define PTE_ADDR(pte) ((uint)(pte) & ~0xFFF) //
get all but last 12 bits
#define PTE_FLAGS(pte) ((uint)(pte) & 0xFFF) //
get last 12 bits
mmu.h : Segmentation related
macros
// various segment selectors.
#define SEG_KCODE 1 // kernel code
#define SEG_KDATA 2 // kernel data+stack
#define SEG_UCODE 3 // user code
#define SEG_UDATA 4 // user data+stack
#define SEG_TSS 5 // this process's task
state
mmu.h : Segmentation related
macros
// various segment selectors.
#define SEG_KCODE 1 // kernel code
#define SEG_KDATA 2 // kernel data+stack
#define SEG_UCODE 3 // user code
#define SEG_UDATA 4 // user data+stack
#define SEG_TSS 5 // this process's task state
#define NSEGS 6
mmu.h : Segmentation related
macros
struct segdesc { // 64 bit in size
uint lim_15_0 : 16; // Low bits of segment limit
uint base_15_0 : 16; // Low bits of segment base address
uint base_23_16 : 8; // Middle bits of segment base address
uint type : 4; // Segment type (see STS_ constants)
uint s : 1; // 0 = system, 1 = application
uint dpl : 2; // Descriptor Privilege Level
uint p : 1; // Present
uint lim_19_16 : 4; // High bits of segment limit
uint avl : 1; // Unused (available for software use)
uint rsv1 : 1; // Reserved
uint db : 1; // 0 = 16-bit segment, 1 = 32-bit segment
uint g : 1; // Granularity: limit scaled by 4K when set
uint base_31_24 : 8; // High bits of segment base address
};
mmu.h : Segmentation related
code
// Application segment type bits
#define STA_X 0x8 // Executable segment
#define STA_W 0x2 // Writeable (non-executable
segments)
#define STA_R 0x2 // Readable (executable
segments)

// System segment type bits


#define STS_T32A 0x9 // Available 32-bit TSS
#define STS_IG32 0xE // 32-bit Interrupt Gate
#define STS_TG32 0xF // 32-bit Trap Gate
Handling Traps
Handling traps

Transition from user mode to kernel mode

On a system call

On a hardware interrupt

User program doing illegal work (exception)

Actions needed, particularly w.r.t. to hardware
interrupts

Change to kernel mode

Kernel to work with devices, if needed

Kernel to understand interface of device
Handling traps

Actions needed on a trap

Save the processor’s registers (context) for future
use

Set up the system to run kernel code (kernel context)

Start kernel in appropriate place (sys call, intr
handler, etc)

Kernel to get all info related to event (which block I/O
done?, which sys call called, which process did
exception and what type, get arguments to system
call, etc)

Privilege level

The x86 has 4 protection levels, numbered 0
(most privilege) to 3 (least privilege).

In practice, most operating systems use
only 2 levels: 0 and 3, which are then called
kernel mode and user mode, respectively.

The current privilege level with which the
x86 executes instructions is stored in %cs
register, in the field CPL.
Privilege level

Changes automatically on
“int” instruction
hardware interrupt
exeception

Changes back on
iret

“int” 10 --> makes 10th hardware interrupt. S/w
interrupt can be used to create hardware interrupt’

Xv6 uses “int 64” for actual system calls
Interrupt Descriptor Table (IDT)

IDT defines intertupt handlers

Has 256 entries

each giving the %cs and %eip to be used when handling the
corresponding interrupt.

Interrupts 0-31 are defined for software exceptions,
like divide errors or attempts to access invalid
memory addresses.

Xv6 maps the 32 hardware interrupts to the range 32-
63

and uses interrupt 64 as the system call interrupt
Interrupt Descriptor Table (IDT)
entries
// Gate descriptors for interrupts and traps
struct gatedesc {
uint off_15_0 : 16; // low 16 bits of offset in
segment
uint cs : 16; // code segment selector
uint args : 5; // # args, 0 for interrupt/trap
gates
uint rsv1 : 3; // reserved(should be zero I
guess)
uint type : 4; // type(STS_{IG32,TG32})
uint s : 1; // must be 0 (system)
uint dpl : 2; // descriptor(meaning new)
privilege level
uint p : 1; // Present
uint off_31_16 : 16; // high bits of offset in segment
};
Setting IDT entries
void
tvinit(void)
{
int i;
for(i = 0; i < 256; i++)
SETGATE(idt[i], 0, SEG_KCODE<<3, vectors[i], 0);
SETGATE(idt[T_SYSCALL], 1, SEG_KCODE<<3,
vectors[T_SYSCALL], DPL_USER);
/* value 1 in second argument --> don't disable
interrupts
* DPL_USER means that processes can raise
this interrupt. */
initlock(&tickslock, "time");
}
Setting IDT entries
#define SETGATE(gate, istrap, sel, off, d) \
{ \
(gate).off_15_0 = (uint)(off) & 0xffff; \
(gate).cs = (sel); \
(gate).args = 0; \
(gate).rsv1 = 0; \
(gate).type = (istrap) ? STS_TG32 : STS_IG32; \
(gate).s = 0; \
(gate).dpl = (d); \
(gate).p = 1; \
(gate).off_31_16 = (uint)(off) >> 16; \
}
Setting IDT entries
Vectors.S trapasm.S
# generated by vectors.pl - #include "mmu.h"
do not edit
# vectors.S sends all traps
# handlers here.
.globl alltraps .globl alltraps
.globl vector0 alltraps:
vector0:
# Build trap frame.
pushl $0
pushl %ds
pushl $0
pushl %es
jmp alltraps
pushl %fs
.globl vector1
pushl %gs
vector1:
pushl $0 Pushal
pushl $1 ....
jmp alltraps
How will interrupts be handled?
On int instruction/interrupt
the CPU does this:

Fetch the n’th descriptor from 
Push %ss. // optional
the IDT, where n is the
argument of int. 
Push %esp. // optional

Check that CPL in %cs is <= 
Push %eflags.
DPL, where DPL is the privilege
level in the descriptor. 
Push %cs.

Save %esp and %ss in CPU- 
Push %eip.
internal registers, but only if the
target segment selector’s PL < 
Clear the IF bit in %eflags,
CPL. but only on an interrupt.

Switching from user mode to
kernel mode. Hence save user 
Set %cs and %eip to the
code’s SS and ESP
values in the descriptor.

Load %ss and %esp from a task
segment descriptor.

Stack changes to kernel stack
now
After “int” ‘s job is done

IDT was already set

Remember vectors.S

So jump to 64th entry in vector’s
vector64:
pushl $0
pushl $64
jmp alltraps

So now stack has ss, esp,eflags, cs, eip, 0 (for error code),
64

Next run alltraps from trapasm.S
# Build trap frame.
pushl %ds alltraps:
pushl %es
pushl %fs 
Now stack contains
pushl %gs 
ss, esp,eflags, cs, eip, 0
pushal // push all gen purpose (for error code), 64, ds,
regs es, fs, gs, eax, ecx, edx,
# Set up data segments. ebx, oesp, ebp, esi, edi
movw $(SEG_KDATA<<3), %ax 
This is the struct
trapframe !
movw %ax, %ds

So the kernel stack now
movw %ax, %es contains the trapframe
# Call trap(tf), where tf=%esp 
Trapframe is a part of
pushl %esp # first arg to trap() kernel stcak
call trap
addl $4, %esp
void
trap(struct trapframe *tf) trap()
{
if(tf->trapno == T_SYSCALL){ 
Argument is trapframe
if(myproc()->killed) 
In alltraps
exit(); 
Before “call trap”, there
myproc()->tf = tf; was “push %esp” and
syscall(); stack had the trapframe
if(myproc()->killed)

Remember calling
convention --> when a
exit(); function is called, the
return; stack contains the
} arguments in reverse
order (here only 1 arg)
switch(tf->trapno){
.....
trap()
Timer
Has a switch



wakeup(&ticks)

switch(tf->trapno) 
IDE: disk interrupt

Ideintr()

Q: who set this 
KBD
trapno? 
Kbdintr()
COM1
Depending on the



Uatrintr()
type of trap 
If Timer
Call yield() -- calls sched()
Call interrupt



If process was killed (how is that
handler done?

Call exit()!

Stack had (trapframe)
ss, esp,eflags, cs, eip, 0 (for
when trap() returns

error code), 64, ds, es, fs, gs,


eax, ecx, edx, ebx, oesp, ebp,
esi, edi, esp

#Back in alltraps
call trap 
add $4 %esp
addl $4, %esp 
esp

# Return falls through to trapret...



popal
.globl trapret 
eax, ecx, edx, ebx, oesp, ebp,
trapret: esi, edi
popal 
Then gs, fs, es, ds
popl %gs
popl %fs 
add $0x8, %esp
popl %es 
0 (for error code), 64
popl %ds
addl $0x8, %esp # trapno and errcode

iret
iret 
ss, esp,eflags, cs, eip,

Extra slides
To be Ignored
bootasm.S after “lgdt gdtdesc”
till jump to “entry”

Logical
Address =
offset Physical
Address

CS, SS, etc.


Selector Base Limit Permissions

3
2 0 4GB Write
1 0 4GB Read, Execute
DS 0 0 0 0
GDT
SS

CS
From entry: RAM
Till: inside main(), before kvmalloc()

Logical
Address =
offset Linear
Address

4MB
CS, SS, etc. 0
Selector Base Limit Permissions

3
2 0 4GB Write 512 0 P,W,PS
1 0 4GB Read, Execute .
DS 0 0 0 0 .
GDT .
SS
3
2
CS
1
CR3 0 0 P,W,PS
entrypgdir
From entry: RAM
Till: inside main(), before kvmalloc()
Physical
Addr
Logical
Address =
offset Dir Offset

4MB
CS, SS, etc. 0
Selector Base Limit Permissions

3
2 0 4GB Write 512 0 P,W,PS
1 0 4GB Read, Execute .
DS 0 0 0 0 .
GDT .
SS
3
2
CS
1
Even now, every
CR3 0 0 P,W,PS
Logical address =
Physical address, but entrypgdir
through Page dir
Free List in XV6 Obtained after main() -> kinit1()

Pages obtained Between


end = 801154a8 = 2049 MB to P2V(4MB) = 2052 MB
Remember
Right now Logical = Physical address.

lock
kmem
uselock Seen
Actually like
run *freelist independent
this in memory
ly

run * run * run *


kmap[] mappings done in kvmalloc(). This shows segmentwise,
entries are done in page directory and page table for corresponding
VA-> PA mappings
4GB

DEVSPACE=3.96GB
4GB
Un
mapped DEVSPACE=3.96GB
KERNBASE+PHYSTOP=
2.224GB= 2272MB Unused
Kernel
data +
memory PHYSTOP = 224MB
data= 2049.0.3125 MB Kernel
Kernel data +
code + memory
RO Data
KERNBASE+EXTMEM=2049
Kernel 1.03125 MB = 1MB +
MB I/O
code + data
Space
RO Data
KERNBASE=2048MB
I/O EXTMEM=1MB
Process Space
address
space 0x80108000 =data
0
= 2049.3125 MB
0 Is obtained from
kernel.sym
After kvmalloc() in main() RAM
Physical
Addr
Logical Linear
Address = Address
offset Dir pg Offset

4MB

CS, SS, etc. 0


Selector Base Limit Permissions

3
2 0 4GB Write
1 0 4GB Read, Execute
DS 0 0 0 0
GDT
SS
Page
Table
CS
Now Linear Address =
CR3
Logical Address !=
Physical Address kpgdir
After seginit() in main(). RAM
On the processor where we started booting Physical
Addr
Logical Linear
Address = Address
offset Dir pg Offset

4MB

CS, SS, etc. 0


Selector Base Limit Permissions DPL
4 0 4GB Write 3
3 0 4GB Read, Execute 3
2 0 4GB Write 0
1 0 4GB Read, Execute 0
DS 0 0 0 0
GDT
SS
Page
Table
CS
Now Linear Address =
CR3
Logical Address !=
Physical Address kpgdir
Segmentation + Paging

You might also like