[목차]
현재까지 사용한 코드&데이터 세그먼트 디스크립터는 보호 모드 커널 엔트리 포인트영역에서 어셈블리어로 작성한 것이다.
C코드로 GDT 테이블을 새로 작성한다.
세그먼트 디스크립터 & GDT 테이블 & TSS 세그먼트를 위한 자료구조와 매크로
// 조합에 사용할 기본 매크로
#define GDT_TYPE_CODE 0x0A
#define GDT_TYPE_DATA 0x02
#define GDT_TYPE_TSS 0x09
#define GDT_FLAGS_LOWER_S 0x10
#define GDT_FLAGS_LOWER_DPL0 0x00
#define GDT_FLAGS_LOWER_DPL1 0x20
#define GDT_FLAGS_LOWER_DPL2 0x40
#define GDT_FLAGS_LOWER_DPL3 0x60
#define GDT_FLAGS_LOWER_P 0x80
#define GDT_FLAGS_UPPER_L 0x20
#define GDT_FLAGS_UPPER_DB 0x40
#define GDT_FLAGS_UPPER_G 0x80
// 실제로 사용할 매크로
// Lower Flags는 Code/Data/TSS, DPL0, Present로 설정
#define GDT_FLAGS_LOWER_KERNELCODE ( GDT_TYPE_CODE | GDT_FLAGS_LOWER_S | \\
GDT_FLAGS_LOWER_DPL0 | GDT_FLAGS_LOWER_P )
#define GDT_FLAGS_LOWER_KERNELDATA ( GDT_TYPE_DATA | GDT_FLAGS_LOWER_S | \\
GDT_FLAGS_LOWER_DPL0 | GDT_FLAGS_LOWER_P )
#define GDT_FLAGS_LOWER_TSS ( GDT_FLAGS_LOWER_DPL0 | GDT_FLAGS_LOWER_P )
#define GDT_FLAGS_LOWER_USERCODE ( GDT_TYPE_CODE | GDT_FLAGS_LOWER_S | \\
GDT_FLAGS_LOWER_DPL3 | GDT_FLAGS_LOWER_P )
#define GDT_FLAGS_LOWER_USERDATA ( GDT_TYPE_DATA | GDT_FLAGS_LOWER_S | \\
GDT_FLAGS_LOWER_DPL3 | GDT_FLAGS_LOWER_P )
// Upper Flags는 Granulaty로 설정하고 코드 및 데이터는 64비트 추가
#define GDT_FLAGS_UPPER_CODE ( GDT_FLAGS_UPPER_G | GDT_FLAGS_UPPER_L )
#define GDT_FLAGS_UPPER_DATA ( GDT_FLAGS_UPPER_G | GDT_FLAGS_UPPER_L )
#define GDT_FLAGS_UPPER_TSS ( GDT_FLAGS_UPPER_G )
// 1바이트로 정렬
#pragma pack( push, 1 )
// GDTR 및 IDTR 구조체
typedef struct kGDTRStruct
{
WORD wLimit;
QWORD qwBaseAddress;
// 16바이트 어드레스 정렬을 위해 추가
WORD wPading;
DWORD dwPading;
} GDTR, IDTR;
// 8바이트 크기의 GDT 엔트리 구조
typedef struct kGDTEntry8Struct
{
WORD wLowerLimit;
WORD wLowerBaseAddress;
BYTE bUpperBaseAddress1;
// 4비트 Type, 1비트 S, 2비트 DPL, 1비트 P
BYTE bTypeAndLowerFlag;
// 4비트 Segment Limit, 1비트 AVL, L, D/B, G
BYTE bUpperLimitAndUpperFlag;
BYTE bUpperBaseAddress2;
} GDTENTRY8;
// 16바이트 크기의 GDT 엔트리 구조
typedef struct kGDTEntry16Struct
{
WORD wLowerLimit;
WORD wLowerBaseAddress;
BYTE bMiddleBaseAddress1;
// 4비트 Type, 1비트 0, 2비트 DPL, 1비트 P
BYTE bTypeAndLowerFlag;
// 4비트 Segment Limit, 1비트 AVL, 0, 0, G
BYTE bUpperLimitAndUpperFlag;
BYTE bMiddleBaseAddress2;
DWORD dwUpperBaseAddress;
DWORD dwReserved;
} GDTENTRY16;
// TSS Data 구조체
typedef struct kTSSDataStruct
{
DWORD dwReserved1;
QWORD qwRsp[ 3 ];
QWORD qwReserved2;
QWORD qwIST[ 7 ];
QWORD qwReserved3;
WORD wReserved;
WORD wIOMapBaseAddress;
} TSSSEGMENT;
#pragma pack ( pop )
세그먼트 디스크립터를 생성하는 함수 코드와 사용 예
디스크립터를 생성하는 함수는 파라미터로 넘어온 각 필드의 값을 세그먼트 디스크립터의 구조에 맞추어 삽입해주는 역할을 수행한다.
GDTR 자료구조의 시작 어드레스를 0x142000으로 설정하고, 그 이후 어드레스부터 각 자료구조를 위치시킨 이유
// GDT 테이블을 초기화
void kInitializeGDTTableAndTSS( void )
{
GDTR* pstGDTR;
GDTENTRY8* pstEntry;
TSSSEGMENT* pstTSS;
int i;
// GDTR 설정
pstGDTR = ( GDTR* ) 0x142000;
pstEntry = ( GDTENTRY8* ) ( 0x142000 + sizeof( GDTR ) );
pstGDTR->wLimit = ( sizeof( GDTENTRY8 ) * 3 ) + ( sizeof( GDTENTRY16 ) * 1 ) - 1;
pstGDTR->qwBaseAddress = ( QWORD ) pstEntry;
// TSS 영역 설정
pstTSS = ( TSSSEGMENT* ) ( ( QWORD ) pstEntry + GDT_TABLESIZE );
// NULL, 64비트 Code/Data, TSS를 위해 총 4개의 세그먼트를 생성한다.
kSetGDTEntry8( &( pstEntry[ 0 ] ), 0, 0, 0, 0, 0 );
kSetGDTEntry8( &( pstEntry[ 1 ] ), 0, 0xFFFFF, GDT_FLAGS_UPPER_CODE,
GDT_FLAGS_LOWER_KERNELCODE, GDT_TYPE_CODE );
kSetGDTEntry8( &( pstEntry[ 2 ] ), 0, 0xFFFFF, GDT_FLAGS_UPPER_DATA,
GDT_FLAGS_LOWER_KERNELDATA, GDT_TYPE_DATA );
kSetGDTEntry16( ( GDTENTRY16* ) &( pstEntry[ 3 ] ), ( QWORD ) pstTSS,
sizeof( TSSSEGMENT ) - 1, GDT_FLAGS_UPPER_TSS, GDT_FLAGS_LOWER_TSS,
GDT_TYPE_TSS );
// TSS 초기화 GDT 이하 영역을 사용함
kInitializeTSSSegment( pstTSS );
}
// 8바이트 크기의 GDT 엔트리에 값을 설정
// 코드와 데이터 세그먼트 디스크립터
void kSetGDTEntry8( GDTENTRY8* pstEntry, DWORD dwBaseAddress, DWORD dwLimit,
BYTE bUpperFlags, BYTE bLowerFlags, BYTE bType )
{
pstEntry->wLowerLimit = dwLimit & 0xFFFF;
pstEntry->wLowerBaseAddress = dwBaseAddress & 0xFFFF;
pstEntry->bUpperBaseAddress1 = ( dwBaseAddress >> 16 ) & 0xFF;
pstEntry->bTypeAndLowerFlag = bLowerFlags | bType;
pstEntry->bUpperLimitAndUpperFlag = ( ( dwLimit >> 16 ) & 0xFF ) |
bUpperFlags;
pstEntry->bUpperBaseAddress2 = ( dwBaseAddress >> 24 ) & 0xFF;
}
// 16바이트 크기의 GDT 엔트리에 값을 설정
// TSS 세그먼트 디스크립터를 설정하는데 사용
void kSetGDTEntry16( GDTENTRY16* pstEntry, QWORD qwBaseAddress, DWORD dwLimit,
BYTE bUpperFlags, BYTE bLowerFlags, BYTE bType )
{
pstEntry->wLowerLimit = dwLimit & 0xFFFF;
pstEntry->wLowerBaseAddress = qwBaseAddress & 0xFFFF;
pstEntry->bMiddleBaseAddress1 = ( qwBaseAddress >> 16 ) & 0xFF;
pstEntry->bTypeAndLowerFlag = bLowerFlags | bType;
pstEntry->bUpperLimitAndUpperFlag = ( ( dwLimit >> 16 ) & 0xFF ) | bUpperFlags;
pstEntry->bMiddleBaseAddress2 = ( qwBaseAddress >> 24 ) & 0xFF;
pstEntry->dwUpperBaseAddress = qwBaseAddress >> 32;
pstEntry->dwReserved = 0;
}
TSS 세그먼트는 스택에 대한 필드와 I/O 맵에 대한 필드로 구성되어 있다.
mint64 OS는 I/O 맵을 사용하지 않으며, IST방식을 사용하므로 이에 맞춰 TSS 세그먼트 필드를 설정한다.
I/O 맵을 사용하지 않게 설정하는 방법
IDT 게이트 디스크립터의 IST 필드를 0이 아닌 값으로 설정하고, TSS 세그먼트의 해당 IST영역에 핸들러가 사용할 스택 어드레스를 설정한다.
(단, IST의 스택 어드레스는 반드시 16byte로 정렬된 위치에서 시작해야 하는 것을 알아야 한다.)
I/O 맵을 사용하지 않고 7 ~ 8MB 영역을 IST 1로 사용하도록, TSS 세그먼트를 초기화하는 코드이다.
void kInitializeTSSSegment( TSSSEGMENT* pstTSS ){
kMemSet( pstTSS, 0, sizeof( TSSDATA ) );
pstTSS -> qwIST[ 0 ] = 0x800000;
pstTSS -> wIOMapBaseAddress = 0xFFFF;
}