IT, 소프트웨어

10장. 초기화(Initialization)

꿈있는 내일 2011. 5. 9. 12:31

Chapter 10        초기화(Initialization)

 

 

리셋(RESET) 핀에 신호를 주면, 80386의 특정 레지스터들은 사전 지정한 값으로 셋팅된다. 이러한 값들은 부트스트랩(bootstrap) 프로그램을 실행하는데 적합하지만, 추가적인 초기화는 프로세서의 모든 특성들이 사용되기 전에 소프트웨어적으로 수행되어야 한다.

 

 

10.1  리셋 후의 프로세서 상태

 

레지스터 EAX의 내용은 전원을 킨 이후에 자가 진단 상태에 따라 다르다. 자가 진단은 리셋 과정의 마지막인 ‘BUSY#’ 선언으로 외부에서 요청될 것이다. 80386이 테스트를 통과하면 EAX 레지스터에는 0이 있을 것이다. 자가 진단 후 )이 아닌 값이 EAX에 있다면 80386의 한 부분에 문제가 있다는 것을 나타낸다. 만일 자가 진단이 수행되지 않는다면 리셋 이후 EAX에 어떤 값이 있을지는 알 수 없다.DX에는 라셋 후에 그림 10-1 처럼 컴포넌트 식별자와 리비전(revision) 숫자가 저장된다. DH에는 3이 저장되는데, 이는 80386 컴포넌트를 나타낸다. DL에는 리비전 수준의 고유한 식별자가 있다.

 

CR0(Control register zero)에는 그림 10-2와 같은 값들이 저장된다. CR0 ET 비트는 80387이 설정에서 존재가 확인되면(리셋 후에 ERROR# 핀의 상태에 따라) 셋팅된다. ET를 리셋하면 설정에 80287이 포함되었을 수도 있고 보조연산장치가 없을 수도 있다. 소프트웨어적인 테스팅으로 이러한 두 가지 가능성을 구분할 필요가 있다.

 

남은 레지스터들과 플래그는 아래와 같이 셋팅된다:

 

EFLAGS                       =00000002H

IP                              =0000FFF0H

CS selector                  =000H

DS selector                  =0000H

ES selector                   =0000H

SS selector                   =0000H

FS selector                   =0000H

IDTR:                          =0000H

           Base                =0

           Limit                =03FFH

 

위에서 언급하지 않은 모든 레지스터는 정의되지 않았다.

 

이러한 셋팅들은 프로세서가 실주소모드(real-address mode)로 인터럽트는 비활성 상태로 시작한다는 것을 의미한다.

 

그림 10-1. 리셋 후 EDX의 내용

 

 

그림 10-2 CR0의 초기 값들

 

 

 

 

 

10.2 실주소(Real-Address) 모드용 소프트웨어적 초기화

 

실주소모드에서는 프로그램이 이 모드에서 가능한 모든 특성들을 사용할 수 있도록 몇몇 구조체가 초기화되어야 한다.

 

10.2.1 스택(Stack)

 

스택 세그먼트 레지스터(SS)가 적재되기 전까지 스택을 사용할 수 있는 인스트럭션은 없다. SS 는 램(RAM) 영역을 카리켜야 한다.

 

10.2.2 인터럽트 테이블(Interrupt Table)

 

80386 초기 상태는 인터럽트가 비활성된 상태이다; 하지만 프로세서는 예외나 NMI(nonmaskable interrupt)가 발생할 때 계속해서 인터럽트 테이블을 접근하려 할 것이다. 초기화 소프트웨어는 다음 중 하나의 동작을 수행해야 한다.

 

l        IDTR의 한계 값을 0으로 변경. 이렇게 하면 예외나 NMI가 발생했을 때 다운되도록 한다(80386 이 외부에서 종료되는 방식에 관한 HW 매뉴얼 참조)l        예외나 인터럽트가 사용할 인터럽트 테이블에 유효한 인터럽트 처리기(handler)를 가리키는 포인터 입력l        IDTR 이 유효한 인터럽트 테이블을 가리키도록 변경

 

10.2.3 최초 인스트럭션

 

RESET 후, A{31-20} 주소 줄들은 인스트럭션이 읽어지도록 자동으로 확정된다. 이러한 사실은 CS:IP의 초기값을 사용해서 인스트럭션이 물리 주소 FFFFFFF0H에서 시작한다는 것을 의미한다. Near(intrasegment) 형태의 제어 이동 인스트럭션 형태들이 주소 공간 상위 64K에 있는 다른 주소로 이동하는 데 사용된다. 최초 far(intersemgment) 형태의 JMP나 CALL 인스트럭션은 A{31-20}이 낮은 주소가 되도록 하고 80386은 물리 메모리의 하위 1 메가바이트에 있는 인스트럭션을 계속해서 수행한다. 이러한 A{31-20} 주소 줄의 자동 확정으로 시스템 디자이너는 시스템을 초기화하기 위해 상위 주소 공간에 있는 ROM을 사용할 수 있다.

 

10.3 보호 모드로의 전환

 

CR0의 MSW PE비트를 셋팅하면 80386은 보호 모드로 동작한다. 현 특권레벨(CPL)은 0에서 시작한다. 세그먼트 레지스터들은 계속해서 실 주소 모드에서와 동일한 선형 주소를 가리킨다(실 주소모드에서 선형 주소들은 물리 주소와 동일하다).

 

PE 플래그를 셋팅한 직후, 초기화 코드는 JMP 인스트럭션을 수행해서 인스트럭션 프리패치 큐(prefetch queue)를 비워야 한다. 80386은 인스트럭션들과 주소가 사용되기 전에 이들을 읽어 들이고 해석한다; 하지만 보호모드로 전환한 후에, 이미 읽어 들인 인스트럭션 정보(실주소 모드 해당하는 부부분)는 더 이상 유효하지 않다. JMP는 프로세서로 하여금 유효하지 않은 정보를 버리도록 한다.

 

10.4 보호 모드 소프트웨어 초기화

 

보호 모드에 필요한 대부분의 초기화는 보호 모드로 전환 이전이나 이후 모두에서 가능하다. 하지만 보호 모드에서 행해진다면 초기화 프로시져는 아직 초기화되지 않은 보호모드 특성들을 사용해서는 안된다.

 

10.4.1 인터럽트 기술자 테이블(Interrupt Descriptor Table)

 

IDTR 은 실주소모드나 보호모드에서 적재될 수 있다. 하지만 보호모드에서 인터럽트 테이블 형식은 실주소모드에서와 다르다. 동시에 모호모드로 전환하고 인터럽트 테이블 형식을 바꾸는 것은 불가능하다.; 그래서 IDTR이 인터럽트 테이블을 선택했다면 어느 순간에는 형식이 맞지 않을 것이라는 것은 피할 수 없다. 이 시점에 발생하는 인터럽트와 예외는 예측 불가능한 결과를 가져올 것이다. 이러한 예측 불가능한 상태를 피하기 인터럽트 핸들러가 동작이 가능하고 유효한 IDT가 보호모드에서 생성될 때까지 인터럽트는 비활성 상태로 유지되어야 한다.

 

10.4.2 스택

 

SS 레지스터는 실주소 모드나 보호 모드에서 적재가 가능하다. 실주소모드에서 적재된다면, SS 는 보호 모드로 전환된 이후에도 계속해서 동일한 선형 베이스 주소를 가리키게 된다.

 

10.4.3 전역 기술자 테이블(Global Descriptor Table)

 

임의의 세그먼트 레지스터가 보호 모드에서 변경되기 전에, GDT 레지스터는 유효한 GDT를 카리켜야 한다. GDT와 GDTR 초기화는 실주소 모드에서 수행될 것이다. 프로세서가 기술자 접근 비트(accessed bit)를 변경하기 때문에 GDT(LDT뿐만 아니라)는 RAM 영역에 있어야 한다.

 

10.4.4 페이지 테이블

 

페이지 테이블과 CR3에 있는 PDBR은 실주소모드나 보호모드에서 초기화가 가능하다; 하지만 CR0의 페이지 활성비트(PG)는 프로세서가 보호모드가 될기 전에는 셋팅이 불가능하다. PG는 PE와 동시에  혹은 나중에 셋팅될 것이다. PG가 셋팅되었을 때, CR3의 PDBR은 이미 유효한 페이지 디렉토리를 가리키는 물리주소로 초기화되있어야 한다. 초기화 과정은 페이징 활성을 전후해서 주소화가 되도록 하기 위해 다음과 같은 과정 중 하나를 사용해야 한다.

 

l        현재 실행 중인 페이지는 PG 셋팅을 전후해서 물리 주소로 매핑되어야 한다.l        JMP 인스트럭션은 PG 셋팅 바로 다음에 있어야 한다.

 

10.4.5 최초 태스크

 

초기화 과정은 태스크 레지스터 초기화가 없이 보호모드에서 잠깐 실행될 수 있다; 하지만 최초 태스크로 전환하기 전에 다음 상황이 전체되어야 한다.

 

l        새로운 태스크에 대한 유효한 태스크 상태 세그먼트(TSS)가 있어야 한다.l        태스크 레지스터는 현재 태스크 상태를 저장할 영역을 가리켜야 한다. 첫 태스크 전환 이후에 이영역에 덥핑된 정보는 필요하지 않고 그 영역은 다른 목적으로 사용될 수 있다.

 

10.5 초기화 예제

 

$TITLE ('Initial Task')

 

NAME       INIT

 

init_stack       SEGMENT     RW

DW 20          DUP(?)

Tos           LABEL          WORD

init_stack ENDS

 

init_data       SEGMENT     RW PUBLIC

DW 20          DUP(?)

init_data ENDS

 

init_code       SEGMENT     ER PUBLIC

 

ASSUME        DS:init_data

 

nop

nop

nop

init_start:

; set up stack

mov ax, init_stack

mov ss, ax

mov esp, offset tos

mov a1,1

blink:

xor a1,1

out 0e4h,a1

mov cx,3FFFh

here:

dec cx

jnz here

 

jmp SHORT blink

hlt

init_code ends

 

END init_start, SS:init_stack, DS:init_data

 

$TITLE('Protected Mode Transition -- 386 initialization')

NAME RESET

;*****************************************************************

; Upon reset the 386 starts executing at address 0FFFFFFF0H. The

; upper 12 address bits remain high until a FAR call or jump is

; executed.

;

; Assume the following:

;

;

; - a short jump at address 0FFFFFFF0H (placed there by the

; system builder) causes execution to begin at START in segment

; RESET_CODE.

;

;

; - segment RESET_CODE is based at physical address 0FFFF0000H,

; i.e. at the start of the last 64K in the 4G address space.

; Note that this is the base of the CS register at reset. If

; you locate ROMcode above this address, you will need to

; figure out an adjustment factor to address things within this

; segment.

;

;*****************************************************************

$EJECT ;

; Define addresses to locate GDT and IDT in RAM.

; These addresses are also used in the BLD386 file that defines

; the GDT and IDT. If you change these addresses, make sure you

; change the base addresses specified in the build file.

 

GDTbase       EQU       00001000H   ; physical address for GDT base

IDTbase       EQU       00000400H   ; physical address for IDT base

 

PUBLIC        GDT_EPROM

PUBLIC        IDT_EPROM

PUBLIC        START

 

DUMMY         segment rw  ; onLY for ASM386 main module stack init

DW 0

DUMMY ends

 

;*****************************************************************

;

; Note: RESET CODE must be USEl6 because the 386 initally executes

; in real mode.

;

 

RESET_CODE       segment er PUBLIC       USE16

ASSUME DS:nothing, ES:nothing

 

;

; 386 Descriptor template

 

DESC          STRUC

lim_0_15    DW 0                     ; limit bits (0..15)

bas_0_15     DW 0               ; base bits (0..15)

bas_16_23    DB 0               ; base bits (16..23)

access       DB 0                     ; access byte

gran         DB 0                     ; granularity byte

bas_24_31   DB 0                     ; base bits (24..31)

DESC ENDS

 

; The following is the layout of the real GDT created by BLD386.

; It is located in EPROM and will be copied to RAM.

;

; GDT[O] ... NULL

; GDT[1] ... Alias for RAM GDT

; GDT[2] ... Alias for RAM IDT

; GDT[2] ... initial task TSS

; GDT[3] ... initial task TSS alias

; GDT[4] ... initial task LDT

; GDT[5] ... initial task LDT alias

;

; define entries in GDT and IDT.

 

GDT_ENTRIES              EQU       8

IDT_ENTRIES              EQU       32

 

; define some constants to index into the real GDT

 

GDT_ALIAS              EQU       1*SIZE DESC

IDT_ALIAS              EQU       2*SIZE DESC

INIT_TSS              EQU       3*SIZE DESC

INIT_TSS_A              EQU       4*SIZE DESC

INIT_LDT              EQU       5*SIZE DESC

INIT_LDT_A              EQU       6*SIZE DESC

;

; location of alias in INIT_LDT

 

INIT_LDT_ALIAS     EQU       1*SIZE DESC

;

; access rights byte for DATA and TSS descriptors

 

DS_ACCESS              EQU       010010010B

TSS_ACCESS              EQU       010001001B

;

; This temporary GDT will be used to set up the real GDT in RAM.

 

TEMP_GDT              LABEL       BYTE              ; tag for begin of scratch GDT

NULL_DES              DESC <>            ; NULL descriptor

 

FLAT_DES              DESC <0FFFFH,0,0,92h,0CFh,0>

GDT_eprom              DP ?       ; Builder places GDT address and limit

       ; in this 6 byte area.

IDT_eprom              DP ?       ; Builder places IDT address and limit

       ; in this 6 byte area.

;

; Prepare operand for loadings GDTR and LDTR.

 

TGDT_pword       LABEL PWORD         ; for temp GDT

DW       end_Temp_GDT_Temp_GDT -1

DD       0

 

GDT_pword       LABEL PWORD         ; for GDT in RAM

DW       GDT_ENTRIES * SIZE DESC -1

DD       GDTbase

 

IDT_pword       LABEL PWORD         ; for IDT in RAM

DW       IDT_ENTRIES * SIZE DESC -1

DD       IDTbase

 

end_Temp_GDT LABEL BYTE

;

; Define equates for addressing convenience.

 

GDT_DES_FLAT              EQU DS:GDT_ALIAS +GDTbase

IDT_DES_FLAT              EQU DS:IDT_ALIAS +GDTbase

 

INIT_TSS_A_OFFSET  EQU DS:INIT_TSS_A

INIT_TSS_OFFSET    EQU DS:INIT_TSS

INIT_LDT_A_OFFSET  EQU DS:INIT_LDT_A

INIT_LDT_OFFSET    EQU DS:INIT_LDT

 

; define pointer for first task switch

 

ENTRY POINTER LABEL DWORD

DW 0, INIT_TSS

 

;******************************************************************

;

; Jump from reset vector to here.

 

START:

 

CLI                     ;disable interrupts

CLD                     ;clear direction flag

 

LIDT       NULL_des       ;force shutdown on errors

 

;

; move scratch GDT to RAM at physical 0

 

XOR DI,DI

MOV ES,DI                  ;point ES:DI to physical location 0

MOV SI,OFFSET Temp_GDT

MOV CX,end_Temp_GDT-Temp_GDT       ;set byte count

INC CX

;

; move table

 

REP MOVS BYTE PTR ES:[DI],BYTE PTR CS:[SI]

 

LGDT tGDT_pword              ;load GDTR for Temp. GDT

;(located at 0)

; switch to protected mode

 

MOV EAX,CR0                     ;get current CRO

MOV EAX,1                ;set PE bit

MOV CRO,EAX                     ;begin protected mode

;

; clear prefetch queue

 

JMP SHORT flush

flush:

 

; set DS,ES,SS to address flat linear space (0 ... 4GB)

 

MOV BX,FLAT_DES-Temp_GDT

MOV US,BX

MOV ES,BX

MOV SS,BX

;

; initialize stack pointer to some (arbitrary) RAM location

 

MOV ESP, OFFSET end_Temp_GDT

;

; copy eprom GDT to RAM

 

MOV ESI,DWORD PTR GDT_eprom +2       ; get base of eprom GDT

; (put here by builder).

MOV EDI,GDTbase               ; point ES:EDI to GDT base in RAM.

MOV CX,WORD PTR gdt_eprom +0       ; limit of eprom GDT

INC CX

SHR CX,1                        ; easier to move words

CLD

REP MOVS WORD PTR ES:[EDI],WORD PTR DS:[ESI]

;

; copy eprom IDT to RAM

;

 

MOV ESI,DWORD PTR IDT_eprom +2       ; get base of eprom IDT

; (put here by builder)

MOV EDI,IDTbase              ; point ES:EDI to IDT base in RAM.

MOV CX,WORD PTR idt_eprom +0       ; limit of eprom IDT

INC CX

SHR CX,1

CLD

REP MOVS WORD PTR ES:[EDI],WORD PTR DS:[ESI]

 

; switch to RAM GDT and IDT

;

 

LIDT IDT_pword

LGDT GDT_pword

;

 

MOV BX,GDT_ALIAS              ; point DS to GDT alias

MOV DS,BX

;

; copy eprom TSS to RAM

;

 

MOV BX,INIT_TSS_A             ; INIT TSS A descriptor base

; has RAM location of INIT TSS.

MOV ES,BX                ; ES points to TSS in RAM

MOV BX,INIT_TSS              ; get inital task selector

LAR DX,BX                ; save access byte

MOV [BX].access,DS_ACCESS       ; set access as data segment

MOV FS,BX                ; FS points to eprom TSS

XOR si,si                ; FS:si points to eprom TSS

XOR di,di                ; ES:di points to RAM TSS

MOV CX,[BX].lim_0_15   ; get count to move

INC CX

 

;

; move INIT_TSS to RAM.

 

REP MOVS BYTE PTR ES:[di],BYTE PTR FS:[si]

MOV [BX].access,DH            ; restore access byte

;

; change base of INIT TSS descriptor to point to RAM.

 

MOV AX,INIT_TSS_A_OFFSET.bas_0_15

MOV INIT_TSS_OFFSET.bas_0_15,AX

MOV AL,INIT_TSS_A_OFFSET.bas_16_23

MOV INIT_TSS_OFFSET.bas_16_23,AL

MOV AL,INIT_TSS_A_OFFSET.bas_24_31

MOV INIT_TSS_OFFSET.bas_24_31,AL

;

; change INIT TSS A to form a save area for TSS on first task

 

; switch. Use RAM at location 0.

 

MOV BX,INIT_TSS_A

MOV WORD PTR [BX].bas_0_15,0

MOV [BX].bas_16_23,0

MOV [BX].bas_24_31,0

MOV [BX].access,TSS_ACCESS

MOV [BX].gran,O

LTR BX                          ; defines save area for TSS

;

; copy eprom LDT to RAM

 

MOV BX,INIT_LDT_A             ; INIT_LDT_A descriptor has

; base address in RAM for INIT_LDT.

MOV ES,BX                ; ES points LDT location in RAM.

MOV AH,[BX].bas_24_31

MOV AL,[BX].bas_16_23

SHL EAX,16

MOV AX,[BX].bas_0_15   ; save INIT_LDT base (ram) in EAX

MOV BX,INIT_LDT              ; get inital LDT selector

LAR DX,BX                ; save access rights

MOV [BX].access,DS_ACCESS       ; set access as data segment

MOV FS,BX                ; FS points to eprom LDT

XOR si,si                ; FS:SI points to eprom LDT

XOR di,di                ; ES:DI points to RAM LDT

MOV CX,[BX].lim_0_15   ; get count to move

INC CX

;

; move initial LDT to RAM

 

REP MOVS BYTE PTR ES:[di],BYTE PTR FS:[si]

MOV [BX].access,DH            ; restore access rights in

; INIT_LDT descriptor

;

; change base of alias (of INIT_LDT) to point to location in RAM.

 

MOV ES:[INIT_LDT_ALIAS].bas_0_15,AX

SHR EAX,16

MOV ES:[INIT_LDT_ALIAS].bas_16_23,AL

MOV ES:[INIT_LDT_ALIAS].bas_24_31,AH

;

; now set the base value in INIT_LDT descriptor

 

MOV AX,INIT_LDT_A_OFFSET.bas_0_15

MOV INIT_LDT_OFFSET.bas_0_15,AX

MOV AL,INIT_LDT_A_OFFSET.bas_16_23

MOV INIT_LDT_OFFSET.bas_16_23,AL

MOV AL,INIT_LDT_A_OFFSET.bas_24_31

MOV INIT_LDT_OFFSET.bas_24_31,AL

;

; Now GDT, IDT, initial TSS and initial LDT are all set up.

;

; Start the first task!

'

JMP ENTRY_POINTER

 

RESET_CODE ends

END START, SS:DUMMY,DS:DUMMY