[작성한 이유]


 


 OS를 만들어보고 싶은 마음에 무작정 뛰어 들었는데 대부분 환경은 리눅스상이
gcc였습니다. 하지만 저는 windows 환경하의 vc 개발을 주로 해왔으므로 무척
불편(?)했습니다. Djgpp 라는 dos용 gcc 포팅 버전과 윈도우용 cygwin 패키지를
사용하면 역시 동일하게 gcc를 윈도우에서 컴파일 할 수 있습니다 그래도 저는 vc의
에디팅 환경이 맘에 들어서 디버깅까지는 힘들겠지만 (어차피 커널 디버깅하려면
스스로 자신의 커널 디버거를 만들어야 하는 거 같습니다. ) 에디팅과 컴파일
그리고 링커를 vc6를 사용하고 싶었고 그래서 이를 사용할 수 있는 방법을 소개하려
합니다.


 


 


[참고하면 좋은 사이트들]


 


 http://www.osx86.org/


 -
어려운 영문 OS개발방법을 한글화 해주는 고마운 사이트


 http://www.zap.pe.kr/


 -
Chobits os의 소스를 얻을 수 있습니다.


 http://www.execpc.com/~geezer/johnfine/index.htm


 -
무척 좋은 Protected mode에 대한 설명과 소스를 담고 있는 사이트


http://www.nondot.org/sabre/os/articles


 -
OS에 대한 전반적인 자료가 있습니다.


http://www.osdever.net/


 -
Bona Fide OS 개발 튜토리얼 사이트


http://mega-tokyo.com/osfaq2/


 -
OS FAQ


http://www.osdev.org/


 -
소스가 공개된 여러 os가 있습니다.


http://www.bellona2.com


 -
혼자서 GUI OS를 만드시고 책도 출판하신 분의 사이트


http://ksyspro.org


 -
한국 임베디드 개발자 모임


 


[Protected i386 OS를 위한 잛은 설명]


 


 Intel은 다른 cpu 벤더보다 역사가 오래되어서 4bit microprocessor인 4004에서
출발해서 8bit cpu 8008, 8080, 16비트 8086, 80186, 80286, 32비트 80386, 80486,
80586 (또는 P5 그러나 숫자는 저작권 보호를 받지 못한다길래 이후에 Pentium으로
바뀌었습니다 ). 이렇게 긴 역사를 갖고 있는 보편적인 cpu 8080 또는 8086 통틀어
intel x86 cpu에서 돌던 프로그램도 586에서도 수행되도록 하위호환을 갖게 됩니다.


 이 하위호환 때문에 아무리 최신 컴퓨터도 처음 부팅시에는 빠른 x86 처럼
수행되도록 설계되었습니다. 부트섹터에서 이 빠른 x86에서 최신의 기능을 사용하는
i386+ 환경으로 만들기 위해서 적어도 다음과 같은 일을 하게 됩니다.


 


 ORG 0x7C00 - PC가 리셋되면 부팅될 디바이스의 첫번째 섹터(512바이트)를
읽어 메모리 0x7C00에 올려 놓은 후 이 코드로 점프하게 됩니다. 따라서 부트섹터
코드는 그 메모리에서 시작한다고 지정해야 할것입니다.


 


 A20 enable - 예전에는 실제 메모리크기가 크지 않아 1M 이하만 접근하도록
설계되었는데 이젠 메모리가 1M이상 되는 것이 많아 1M 이상을 실제 접근 하기 위해서
해주어야 합니다.


 


 Protected mode - 처음 부팅할땐 x86 Real Mode라는 세그먼트:오프셋
지정 방식으로 수행되지만 80286이상에서는 보호모드가 지원되었고 (최대16M가능)
80386에서는 4G까지 접근 가능하도록 되었습니다.


 


 커널로드 - 부트섹터 코드는 C로된 바이너리 커널을 여러 섹터에서
읽어서 특정 메모리 번지에 로드한 후 그 시작 함수 (엔트리 포인트 함수)로 점프하게
됩니다.


 


[준비물]


 


 VC6,
- 커널의 컴파일 및 링킹을 담당합니다.


 VMWare
- 우리는 디스켓도 사용하지 않고 바로 컴파일해서 이미지로 뜬 후 VMWare로
로드할 것입니다. ( no 플로피 부팅디스켓, no 리붓, no test machine )


 Nasm
- Netwide Asm으로 at&t 계열 및 intel 계열 둘다 지원하고 target format도
많이 지원하는 어셈블러입니다. 설치과정없이 그냥 nasm.exe 만 있으면 됩니다.


 


 


[소스 파일들]


 


 bootsect.asm
- 간단한 보호모드(세그먼테이션방식) 진입 및 커널 로드 부트로더


 main.c
- VC로 컴파일될 작은 커널


  Makeboot.exe
- 부트섹터와 커널이미지를 merge하는 간단한 makeboot.c 컴파일해서 자신만의
부트 이미지 뜨는 프로그램 만들면된다.


 


[소스]


bootsect.asm

 


[BITS
16]       ; We need 16-bit intructions for Real
mode


 


[ORG
0x7C00]    ; The BIOS loads the boot sector into memory location
0x7C00


 


reset_drive:


        mov
ah, 0               ;
RESET-command


        int
13h                 ;
Call interrupt 13h


        or
ah, ah               ;
Check for error code


        jnz
reset_drive         ; Try again if ah
!= 0


 


        ;vc에서
/base 10000 로 헀기 때문에 여기서부터 메모리를 로드


        ;
해야 한다. ES:BX 로 1000:0000h 이기 때문에 아래처럼 한다.


        mov
ax, 1000h           ; es:bx
<-- 1000:0000h


        mov
es, ax


        mov
bx, 0h          ; Destination address
= 0000:1000


 


        mov
ah, 02h             ;
READ SECTOR-command


        ;
두번째 섹터부터 3칸섹터가 커널이미지 섹터다.


        mov
al, 3h             ;
Number of sectors to read = 1


        mov
ch, 0               ;
Cylinder = 0


        ;
현재 첫번째섹터512 이며 두번째 섹터부터 3개 섹터가 커널이미지다


        mov
cl, 02h             ;
Sector = 2


        mov
dh, 0               ;
Head = 0


        int
13h                 ;
Call interrupt 13h


        or
ah, ah               ;
Check for error code


        jnz
reset_drive         ; Try again if ah
!= 0


 


A20Address:


    ;
Set A20 Address line here


 


    CLI


    CALL
enableA20


    STI


    JMP
Continue


enableA20:


        call
enableA20o1


        jnz
short enableA20done


        mov
al,0d1h


        out
64h,al


        call
enableA20o1


        jnz
short enableA20done


        mov
al,0dfh


        out
60h,al


enableA20o1:


        mov
ecx,20000h


enableA20o1l:


        jmp
short $+2


        in
al,64h


        test
al,2


        loopnz
enableA20o1l


enableA20done:


        ret


Continue:   


 


        cli
                    ;
Disable interrupts, we want to be alone


 


        xor
ax, ax


        mov
ds, ax              ;
Set DS-register to 0 - used by lgdt


 


        lgdt
[gdt_desc]         ; Load the GDT descriptor


 


        mov
eax, cr0            ;
Copy the contents of CR0 into EAX


        or
eax, 1               ;
Set bit 0


        mov
cr0, eax            ;
Copy the contents of EAX into CR0


 


        jmp
08h:clear_pipe      ; Jump to code segment, offset
clear_pipe


 


[BITS
32]                       ;
We now need 32-bit instructions


clear_pipe:


        mov
ax, 10h             ;
Save data segment identifyer


        mov
ds, ax              ;
Move a valid data segment into the data segment


register


        mov
ss, ax              ;
Move a valid data segment into the stack segment


register


        mov
esp, 090000h        ; Move the stack pointer
to 090000h


 


        ;
10200h로 점프 200h는 vc로 링킹하면 기본 PE헤더(200h)를 붙여서 더했다.


        ;
10200h 즉 1000:0200h 로 점프


        ;jmp
1000:0200h


        jmp
10200h


        


gdt:
                   ;
Address for the GDT


 


gdt_null:
              ;
Null Segment


        dd
0


        dd
0


 


gdt_code:
              ;
Code segment, read/execute, nonconforming


        dw
0FFFFh


        dw
0


        db
0


        db
10011010b


        db
11001111b


        db
0


 


gdt_data:
              ;
Data segment, read/write, expand down


        dw
0FFFFh


        dw
0


        db
0


        db
10010010b


        db
11001111b


        db
0


 


gdt_end:
               ;
Used to calculate the size of the GDT


gdt_desc:
                      ;
The GDT descriptor


        dw
gdt_end - gdt - 1    ; Limit (size)


        dd
gdt                  ;
Address of the GDT


 


times
510-($-$$) db 0           ;
Fill up the file with zeros


 


        dw
0AA55h                ;
Boot sector identifyer


 




아래처럼 어셈블 하여 오브젝 부트로더 바이너리를 얻어내야
합니다.


nasm -f bin bootsect.asm -o bootsect.bin

 


 


다음은 부트로더와 커널 이미지를 합쳐서 하나의 부팅이미지로 만들어주는 makeboot.c
입니다. 따로 프로젝트를 만들어서 간단히 컴파일해서 makeboot.exe를 만들도록
합니다.


 


#include


 


int
main(int argnr, char *args[])


{


  FILE
*output, *input;


  int
i, bytes_read, sectors_read, bytes_from_file;


  char
buffer[512];


 


  if
(argnr < 4) {


    printf("Invalid
number of parameters.\n\n");


    printf("USAGE:
%s [output] [input 1] [input 2] ... [input n]\n", args[0]);


    printf("Example:
%s a.img bootsect.bin kernel.bin");


    exit(0);


  }


 


  output
= fopen(args[1], "r");


 


//
덮어 쓰기 할꺼냐고 물어보는 코든데 그냥 덮어쓰자. --


  if
(output != NULL) {


    buffer[0]
= 0;


  }


 


  fclose(output);


  output
= fopen(args[1], "wb");


 


  for
(i = 2; i < argnr; i++) {


    input
= fopen(args[i], "rb");


 


    if
(input == NULL) {


      printf("Missing
input file %s. Aborting operation...", args[i]);


      fclose(output);


      exit(1);


    }


 


    bytes_read
= 512;


    bytes_from_file
= 0;


    sectors_read
= 0;


 


    while(bytes_read
== 512 && !feof(input)) {


      bytes_read
= fread(buffer, 1, 512, input);


 


      if
(bytes_read == 0)


        break;


 


      if
(bytes_read != 512)


        memset(buffer+bytes_read,
0, 512-bytes_read);


 


      sectors_read++;


      fwrite(buffer,
1, 512, output);


      bytes_from_file
+= bytes_read;


    }


 


    printf("%d
sectors, %d bytes read from file %s...\n", sectors_read, bytes_from_file,


args[i]);


 


    fclose(input);


  }


 


  fclose(output);


  return
0;


}


 


다음은 커널 소스입니다.


//
bro (bro@shinbiro.com)2004-02-17 - initial ( vc dev envronment setting )


//
starting function


 


void
start()


{   


    char*
hello = "Hello OS!";


    unsigned
char* vidmem = (unsigned char*)0xB8000;


 


    while(
*hello != '\0' )        


    {


        *vidmem++
= *hello++;


        *vidmem++
= 7;        // 문자 기본 속성


    }   


    


    //
커널이 끝나면 안된다. 계속 돌아야한다.   


    for(;;);


}


 


 


[Visual C++ 컴파일 환경 구성하기]


 


위의 커널 소스를 컴파일 하기 위해서 VC 컴파일 환경을 구성해보도록 합니다.


먼저 Win32 Console Application으로 간단히 프로젝트를 생성합니다.


 



 



 


디버깅은 못하니 그냥 Release 모드로 선택한 후 C++ 세팅을 아래처럼 합니다.


 


/nologo
/G4 /Zp1 /ML /W3 /vmg /vd0 /GX /Od /Gf /FAc /Fa"Release/" /Fo"Release/"
/Fd"Release/" /FD /c


 


Link탭의 옵션은 아래처럼 합니다.


/nologo
/base:"0x10000" /entry:"start" /subsystem:console /incremental:no


/pdb:"Release/testos.pdb"
/map:"Release/testos.map" /machine:I386 /nodefaultlib /out:"testos.bin"
/DRIVER /align:512 /FIXED


 


또한 컴파일 작업에서 바로 부트섹터도 컴파일하고 컴파일 끝나면 바로 부트 이미지도
만들도록 Pre-Link와 Post-Build를 작성합니다.


 



 



 


 


컴파일을 해봅니다.


 


Deleting
intermediate files and output files for project 'testos - Win32 Release'.


--------------------Configuration:
testos - Win32 Release--------------------


Compiling...


main.c


Linking...


LINK
: warning LNK4096: /BASE value "10000" is invalid for Windows 95;
image may


not
run


1
sectors, 512 bytes read from file bootsect.bin...


3
sectors, 1536 bytes read from file testos.bin...


 


testos.bin
- 0 error(s), 1 warning(s)


 


 


[VMWare 테스트 환경 구성하기]


 


컴파일을 마치고 나면 아래와 같은 탐색기 모습이 될 것입니다. 마지막으로 셍성된
testos.img 가 우리의 커널 이미지가 됩니다.


 



 


Partcopy.exe 툴을 사용하여 부팅 디스켓에 놓을 수도 있지만 번거롭습니다. 따라서
VMWare에서 직접 이를 디스켓 이미지로 로드하도록 합니다.


 



 


무조건 그냥 기본으로 Next 합니다.


제일 중요한 것은 이렇게 만든 가상 testos 의 플로피 디스켓 부팅 설정입니다.


 



 


Use Floppy Image에 해당 img 패스 맞추도록 합니다.


 


 


이제 실행해봅니다.


 



 



 


 

'KB > tutorial' 카테고리의 다른 글

DCOM 로컬서버 하기  (0) 2005.08.04
Implementation FAT File System  (0) 2004.11.04
DDK in VC with SoftICE  (1) 2004.11.04
COM 형식 라이브러리 사용하기  (0) 2004.11.04
[winsock] 윈속 소켓 정리  (0) 2004.03.19

+ Recent posts