Venturing into protected-mode

advertisement
Venturing into protected-mode
A first look at the CPU registers
and instructions which provide the
essential supporting infrastructure
A need for Diagnostics
• Upon entering protected-mode, the “rules”
change regarding the allowed CPU actions
• Memory-addresses are computed using a
different set of circuitry within the CPU
• Restrictions are enforced by generating a
variety of “exceptions” which interrupt the
CPU’s normal fetch-execute cycle
• We will need to “diagnose” their causes
Memory-addresses
• The first change programmers encounter
when the CPU is switched into Protected
Mode concerns the way in which the CPU
constructs its memory-addresses (i.e., the
segment registers play a different role)
• Some formerly “hidden” aspects of those
segment-registers will come to the fore!
• (Some terminology also gets revised)
Real-Mode Addresses
Logical Address:
segment
offset
x16
+
While in Real-Mode, the
memory-segments are
all 64-kilobytes in size
(and readable/writable)
Physical Address:
Operand’s effective address
Protected-Mode Addresses
Logical Address:
segment-selector
segment-offset
Segment Descriptor Table
descriptor
descriptor
Segment Base-address
descriptor
(also Segment-Limit
and Access Rights)
+
Validity is
checked
by CPU
descriptor
Physical Address:
Operand’s effective address
Segment-Descriptor Format
63
32
Base[31..24]
RA
D
CR
Limit
GDSV
P P SX / / A
[19..16]
VL
L
DW
Base[15..0]
31
Base[23..16]
Limit[15..0]
0
Several instances of this basic ‘segment-descriptor’ data-structure will occur in
the Global Descriptor Table (and maybe also in some Local Descriptor Tables)
“Hidden” part of segment-registers
selector
segment base
segment limit
access
rights
The “invisible” parts of a segment-register
The programmer-visible part of a segment-register
Segment-Register “cache”
• The “hidden” portions of any segmentregister will automatically be modified
whenever any instruction places a new
value in a segment-register’s visible part
• Examples (some obvious, some not):
mov
pop
lss
ljmp
int
lret
%ax, %ds
%es
tos, %esp
$0x07C0, $main
$0x13
# new value from a general register
# new value from a word in memory
# new value from a memory-pointer
# new value from “immediate” data
# new value from interrupt vector table
# new value from the stack’s memory
Illegal segment-values
• In Real-Mode, any 16-bit value was ‘legal’
to be loaded into any segment-register
• But in Protected-Mode, the CPU doesn’t
allow certain 16-bit values to be placed in
particular segment-registers
• For example: the selector for a descriptor
that isn’t ‘executable’ cannot go into CS,
and one that’s legal for CS can’t go in SS
Special ‘system’ registers
• In protected-mode the CPU needs quick
access to its important data-structures:
– Memory-Segment Descriptors
– Interrupt-Gate Descriptors
– Call-Gate Descriptors
– Task-State Descriptors
– Page-Directory and Page-Table Descriptors
• So special CPU registers exist which are
dedicated to locating these crucial items
GDT and IDT
• The two most vital system registers for
protected-mode execution are:
– GDTR (Global Descriptor Table Register)
– IDTR (Interrupt Descriptor Table Register)
• Each of these is 48-bits wide and contains
the base-address and segment-limit for an
array of descriptors (the GDT and the IDT)
• Special instructions allow access to these
registers: SGDT/LGDT and SIDT/LIDT
48-bit Register-Format
47
16 15
0
Segment Base-Address
Segment
Limit
32 bits
16 bits
System Relationships
GDTR
IDTR
Global Descriptor
Table
descriptor
descriptor
descriptor
descriptor
descriptor
descriptor
descriptor
Interrupt Descriptor
Table
descriptor
descriptor
descriptor
descriptor
descriptor
descriptor
descriptor
descriptor
descriptor
descriptor
descriptor
descriptor
descriptor
LDT and TSS
• For protected-mode multitasking, the CPU
needs to access two other data-structures:
– The current Local Descriptor Table (LDT)
– The current Task-State Segment (TSS)
• Again, special registers tell the CPU where
to find these data-structures in memory
(assuming protected-mode is enabled)
• And special instructions afford access to
them: SLDT/LLDT and STR/LTR
Indirection
• Registers LDTR and TR are like segmentregisters: they have a visible part (16-bits)
and a “hidden” descriptor-cache part
• The programmer-visible portion of these
two registers holds a “segment-selector”
(i.e., an array-index into the GDT array)
• The hidden portion is updated from the
GDT whenever these register get loaded
System Relationships
TR
LDTR
GDTR
Global Descriptor
Table
descriptor
descriptor
descriptor
descriptor
descriptor
descriptor
descriptor
descriptor
descriptor
descriptor
descriptor
descriptor
Task
State
Segment
Local Descriptor
Table
descriptor
descriptor
descriptor
descriptor
descriptor
descriptor
Reading LDTR and TR
• The LDTR and TR registers are not able to
be accessed while executing in real-mode
• An “Undefined Opcode” exception (INT-6)
will be generated if SLDT or STR opcodes
are encountered in a “real-mode” program
• So to obtain the values in these registers,
any bootsector program must temporarily
enable protected-mode
Control Register 0
• Register CR0 is the 32-bit version of the
MSW register (Machine Status Word)
• It contains the PE-bit (Protection Enabled)
– when PE=0 the CPU is in real-mode
– when PE=1 the CPU is in protected-mode
PCN
G DW
A
M
W
P
NE T EMP
E T SMP E
Machine Status Word
Using the LMSW instruction
• You can use the LMSW instruction to turn
on the PE-bit (enables protected-mode)
• But you cannot use LMSW to turn off PE
(i.e., PE was a “sticky bit” in the 80286)
• The Intel 80386 processor introduced a
new name and enlarged size for the MSW
• Special version of the ‘MOV’ instruction
can either enable or disable the PE-bit
How to enter protected-mode
This instruction-sequence turns on PE-bit:
mov
bts
mov
%cr0, %eax
$0, %eax
%eax, %cr0
# get current machine status
# set the image of its PE-bit
# now enter protected mode
Warning: you have to do this with interrupts
temporarily disabled -- since the real-mode
Interrupt Vector Table won’t work any more
How to leave protected-mode
This instruction-sequence turns off PE-bit
mov
btr
mov
%cr0, %eax
$0, %eax
%eax, %cr0
# get current machine status
# set the image of its PE-bit
# now leave protected mode
Warning: you need to make sure that all of
the segment-registers have proper accessrights and segment-limits in their caches to
function correctly once back in real-mode!
An observation
• If we can enter protected-mode, but NOT
do anything to alter any segment-register,
then we won’t need to construct Tables of
Segment-Descriptors
• The left-over ‘real-mode’ descriptor-values
will still be in any segment-register’s cache
• Let’s pursue this idea in a program ‘demo’
Hexadecimal Display
• To display values in registers or memory
locations, we need to convert from binary
numbers to character-strings that consist
of ascii-codes for hexadecimal numerals
• Why? Because hexadecimal values are
easier for human programmers to grasp
and to mentally convert into the actual bitpatterns represented in the computer -thereby giving us a way to “see” inside it
Conversion Algorithm
• The easiest algorithm to understand uses
a “lookup table” for converting ‘nybbles’ to
ascii numerals:
0000→ ‘0’ (=0x30) 1010→ ‘A’ (=0x41)
0001→ ‘1’ (=0x31) 1011→ ‘B’ (=0x42)
0010→ ‘2’ (=0x32) 1011→ ‘C’ (=0x43)
•••
•••
1001→ ‘9’ (=0x39) 1111→ ‘F’ (=0x46)
Our Lookup-Table algorithm
#-----------------------------------------------------------------------------------------hexlist: .ascii
“0123456789ABCDEF”
# array of hex numerals
#-----------------------------------------------------------------------------------------# Algorithm assumes DS was already is setup to address ‘hexlist’
mov
and
mov
%al, %bl
$0x0F, %bx
hex(%bx), %dl
# copy byte-value from AL into BL
# clear all but lowest 4-bits in BX
# use BX as array-index for lookup
This algorithm converts the lowest ‘nybble’
in AL to a hexadecimal numeral in DL, but
it ‘clobbers’ the contents of the BX register
Alternative avoids data-table
A clever machine-algorithm (by Tim Lopez) that
exploits subtle aspects of some x86 instructions:
and $0x0F, %al
cmp $10, %al
sbb $0x69, %al
das
# isolate lowest nybble in AL
# sets up the carry-flag for SBB
# sets up auxiliary-flag for DAS
# decimal adjustment to result
No lookup-table is required here – just some
“immediate data” within the instruction-stream
-- but it may take some effort to comprehend!
Unmodified segment-registers
• If you can arrange for your program not to
change any segment-registers while PE=1
then your code can safely enter and leave
protected-mode without creating GDT/IDT
• This means you will have to have to make
sure no interrupts or exceptions can occur
while the PE-bit is set for protected-mode
• Can use cli and sti to control interrupts
• Avoid exceptions by doing nothing illegal
In-class Exercise
• The ‘sysregs.s’ bootsector demo-program
displays the current value found in the two
48-bit system registers: GDTR and IDTR
• Your job is to add modifications that demo
• Modify the ‘sysregs.s’ bootsector program
so it will display registers LDTR and TR in
addition to displaying GDTR and IDTR
• Can you enter and leave protected-mode
without causing a system “crash”?
Download