BLOG ARTICLE Operating Systems | 11 ARTICLE FOUND

  1. 2010.10.26 OS 만들기 -7- (Hardware Interrupt)
  2. 2010.09.14 OS 만들기 -6- (IDT)
  3. 2010.09.08 OS 만들기 -5- (GDT)
  4. 2010.09.08 OS 만들기 -4- (Protected Mode) 3
  5. 2010.09.08 OS 만들기 -3- (Kernel)

OS 만들기 -7- (Hardware Interrupt)

Operating Systems/OS 만들기 프로젝트 2010. 10. 26. 11:46 Posted by 알 수 없는 사용자

 
PC의 Hardware Interrupt는 8259A 라는 칩을 통하여 입력을 받는데 보통 PIC(Programmable Interrupt Controller)라고도 한다.



이 PIC를 제어하기 위해 PIC용 PORT 명령어 셋인 
ICW(Initialization Command Word, 8bit) ICW1, ICW2, ICW3, ICW4 

그리고 OCW(Operation Control Word) OCW1, OCW2, OCW3 에 대해서 알고 있어야 한다.






Master

Master PIC는 Interrupt가 발생하면 INT핀에 신호를 실어 CPU에 보낸다.

CPU는 신호를 받고 EFLAG Register IE비트를 1로 셋하고 Interrupt를 받을 수 있다면 /INTA를 통해 Master PIC에 보낸다.

Master PIC는 /INTA로 신호를 받으면 IRQ의 숫자를 DATA Bus를 통하여 CPU로 전달한다.

CPU는 Protected Mode라면 IDT에서 맞는 Descriptor를 찾아 Handler를 실행한다.

Slave

Slave PIC는 자신의 INT핀에 신호를 실어 Master PIC의 IRQ 2번 핀에 신호를 보낸다.

Master PIC는 INT핀에 신호를 CPU로 보낸다.

Slave PIC /INTA핀에 신호를 받으면 DATA Bus에 숫자(8~15)를 실어 CPU에 보낸다.




 각 IRQ핀은 여러가지 장치들과 위 표와 같이 연결이 되어 있다.
 


 위 두 개의 PIC가 제대로 동작하려면 초기화 과정을 거쳐야 한다. 
 ICW1 -> ICW2 -> ICW3 -> ICW4 순서대로 초기화 과정을 거치면 된다.


 이 PIC는 I/O 명령어인 out를 사용하며 Master PIC는 0x20, 0x21 Slave PIC는 0xA0, 0xA1 포트번호를 이용한다.



 ICW1 - PIC를 초기화


 4~7bit 는 정해져 있는데 보통 0으로 사용한다. LTIM은 Interrupt가 발생하였을때 엣지트리거(0), 레벨트리거(1)를 결정한다.

 SNGL은 PIC가 Master/Slave(1)로 되어있는지 Master(0)만 사용할 것인지 결정해준다.

 IC4는 ICW4 명령어가 추가적으로 필요하면 1로 필요하지 않으면 0으로 결정해준다.
 

 ICW2 - IRQ 리맵핑. CPU에 설정된 것과 구현하는 것의 충돌이 일어나기에 번호를 바꿔주는 것.

 0~2bit는 0으로 결정되어있다. 이것은 8단위로 기재해야 한다는 뜻이다.

 나머지 bit를 이용하여 Interrupt가 발생했을떄 CPU에게 숫자를 더하여 알려준다.

 예를 들어 00010000(0x10)을 넣으면 0번이 발생하였을때 CPU에게는 0x10의 숫자를 보낸다. 만약, 00100000(0x20)이라고 한다면 0번이 발생하였을때 CPU에게 0x20의 숫자를 보내어 Interrupt 번호를 결정해 준다.

 

 ICW3 - Master /Slave 연결 방법

 Master PIC. 각 비트에 0을 넣으면 하드웨어 장치에 연결이 되어 있다는 것을 나타내고 1을 넣으면 Slave PIC에 연결이 되어 있다는 것을 나타낸다.

 Slave PIC. Slave에서는 Master PIC의 몇번째 핀에 연결되어 있는지를 나타낸다. 3~7bit는 0으로 설정이 되고 나머지 bit를 이용하여 결정한다.

 예를 들면 000000102(210)이라고 설정을 해 주면 Master PIC의 2번 IRQ에 연결이 되어 있는 것이다.

 

 ICW4 - 추가 명령어.


 SFNM, BUF, M/S 기능은 현재는 구현하지 않아도 되므로 0으로 설정해준다.

 AEOIbit는 PIC를 자동으로 Reset 해줄 것인지 나타낸다. Interrupt가 발생했을 때 CPU에게 알린 후 Reset을 하지 않으면 다른 Interrupt를 받아 들이지 못한다. 

 mPMbit는 0을 넣으면 MCS-80/50 Mode로 움직이고 1을 넣으면 8086 Mode로 움직이는 것을 나타낸다.

 

port 0x20, 0xA0을 같이 사용하는 ICW1, OCW2, OCW3은 각 bit로 구별이 가능하다.

ICW1은 4bit가 반드시 1로 설정을 해야하고 OCW2는 3bit가 0 4bit가 1로 OCW3은 3bit가 1 4bit가 0으로 반드시 설정이 되기에 구별이 가능하다.

 

OCW2
 

OCW3
 
 

 

 이제 실제로 Code를 작성해 보자.




 위는 MBR Code 에서 PIC 초기화 부분이다.

 

 Interrupt Mask는 Slave PIC에서는 모든 IRQ를 막아 놓고 Master PIC에서는 2번 IRQ를 뺸 나머지를 막아 놓는다.

 Protected Mode를 넘어가면서 외적인 다른 Hardware interrupt가 걸리지 않도록 Mask 상태로 만드는 것이다.







Kernel Code 중 IDT Segment로 Descriptor를 복사하는 과정이다. 

앞서 ICW2로 ReMapping을 하였기 때문에 IRQ 0번에서 IRQ 0x20(16진수 32)로 바뀌었다.
 


Timer, Keyboard interrupt 를 위해 Mask를 해제하고 유효하게 만드는 것이다.


 



위는 각 Descriptor Pointer 부분이다.




 
Keyboard 루틴에서

in al, 0x60

명령은 Keyboard buffer에서 스캔코드를 가지고 온다. 만약 가져오지 않으면 다음 Interrupt가 발생하지 않는다.




그 이후

mov al, 0x20

out 0x20, al

명령은 PIC를 Reset 하는 명령어이다. ICW4 AEOI 비트에서 수동으로 설정을 하였기에 매번 Reset을 해주어야 한다.







 
 결과 화면이다. "This" 앞부분이 Timer Interrupt가 걸리기 때문에 매번 바뀌고 있고, Keyboard가 눌렸다 땔때마다 두 개의 문자가 바뀌는 것을 알 수 있다.

bochsrc에서 clock을 Slow로 지정해 주지 않으면 너무 빠르게 Timer Interrupt가 걸린다. 
 


 
 위 그림은 예외까지 추가하여 작성된 결과화면이다. Protected Mode에서는 Exception, Hardware Interrupt, Software Interrupt 모두 같은 IDT를 이용하기에 ICW2를 이용하여 Remapping을 해주는 것이다. 0~19번까지는 예외 처리를 위하여 할당되어 있고, 20~31까지는 Inter에서 예약해둔 Interrupt가 할당되어 있다. 32~255번까지 프로그래머가 지정할 수 있는 영역이기에 0x20을 이용하여 Remapping을 해 주었던 것이다. 

 위 결과는 Zero_devide가 예외가 발생하였을 때 나오는 화면이다. Zero_devide ISR을 보면 jmp $을 이용하여 무한루프에 빠지게 만들었기에 위 결과처럼 다른 인터럽트가 걸리지 않게 된다. 

 

예외 Kernel Code는 이제까지 한 내용으로 충분히 이해가 되리라 믿기에 따로 설명은 하지 않았습니다.

 
 

'Operating Systems > OS 만들기 프로젝트' 카테고리의 다른 글

OS 만들기 -6- (IDT)  (0) 2010.09.14
OS 만들기 -5- (GDT)  (0) 2010.09.08
OS 만들기 -4- (Protected Mode)  (3) 2010.09.08
OS 만들기 -3- (Kernel)  (0) 2010.09.08
OS 만들기 -2- (MBR)  (2) 2010.09.02
AND

OS 만들기 -6- (IDT)

Operating Systems/OS 만들기 프로젝트 2010. 9. 14. 05:32 Posted by 알 수 없는 사용자




Protected Mode에서의 Interrupt 동작은 Real Mode와는 다르다.

Real Mode에서는 Vios Interrupt를 이용하지만 Protected Mode에서는 IDT(Interrupt Descriptor Table)를 이용한다.

IDT는 256개로 GDT Descriptor와 비슷한 구조로 되어있다.

 


Interrupt Handler Offset - 인터럽트가 요청 되었을 때 그 인터럽트가 실행될 Routine의 Offset.
Interrupt Handler Segment -  인터럽트가 실행될 Routine Code가 존재하는 Segment

Type(기타 설정) - DPL, P, D bit

 


Real Mode일때 사용했던 SP(Stack Pointer)를 새로운 곳으로 지정해준다.

IDTSelector를 이용하여 IDT가 저장될 Segment를 지정해 준 후 256개의 Interrupt Descriptor를 만든다.
 
loop_idt: 라벨이 Descriptor를 만드는 부분이다.

현재 256개의 모든 Descriptor가 다 같다.

rep movsb

를 이용하여 8바이트 만큼 DS:ESI->ES:EDI 방향으로 복사하고 있다.

 

lidt를 이용해 IDTR(Interrupt Descriptor Table Register: 16bit Limit, 32bit Base Address)를 설정해 준다.

Limit = 0x7FF(2047), Base Address = 0x20000

 

Before
 
 
After
 

 

IDT와 ISR을 만든다.

 

 
먼저 GDT에 IDT의 Descriptor를 저장할 Segment를 새로 만든다.

dd 0x0000FFFF, 0x00CF9202 (Little Endian)

Base Address를 0x20000으로 지정해 주고 있다.

 

IDTR에서 사용할 Limit, Base Address를 지정해 준다.

idt_ignore: 라벨부분이 실제 IDT에 들어갈 Descriptor이다.

ist_ignore: 부분은 실제 Interrupt가 발생되었을 때 실행되어야 하는 Routine(ISR-Interrupt Service Routine)이다.

모든 Register와 Flag값을 스택에 Push후 메시지를 출력하고 끝날 때 Pop하여 본래 있던 값으로 돌려 놓는다.

 

 


위 그림에서 0x20000에 찾아가 보면 0x00080109, 0x00008E00로 ID(Interrupt Descriptor) 값이 저장되어 있다.

Offset = 0x00000109(idt_ignore Offset)

Handler Segment = 0x0008(SysCodeSelector)

P = 1(Ram에 존재), DPL = 0(privilege Level), D = 1(32bit Code Segment)

 


실제로 0x10109에 가서 확인해보면  isr_ignore Code와 같은 것으로 보아 이 곳이 ISR임을 알 수 있다.

 

Hex

Disassembler

 실제 Hex값에서도 2번째 Sector의 0x109로 가보면 ISR이라는 것을 확인 할 수 있다.

 

 

IDT와 ISR을 다 만들었다면 실험을 위해

int 0x77

을 이용하여 119번째 Interrupt를 발생시키면 ISR로 진입하는 것을 확인 할 수 있다.

 

 
 

int 0x77

와 같은 Interrupt가 걸렸을때 동작하는 과정이다.

1. CPU는 IDTR 참조

2. IDTR의 Base Address(0x20000)로 IDT의 시작 위치에 접근

3. 119 Index의 IDT에 접근

4. Handler Code Segment(SysCodeSelector) Bass Address(0x10000)

5. GDT Basee Address + IDT Offset에 접근

6. ISR 실행

7. iret 명령으로 Interrupt가 걸렸던 명령문 바로 다음명령으로 이동 

 

 

Result


  실제로 돌려보면 잘 된다는 것을 확인 할 수 있다. 만약 bochs로 실행을 했는데 자꾸 재부팅이 되는 현상이 일어난다면 그것은 IDT와 GDT 그리고 ISR이 제대로 설정이 되지 않아서 일어나는 것이다. Segment영역을 잘 맞추어 주어야만 Interrupt가 제대로 실행이 된다.

 

'Operating Systems > OS 만들기 프로젝트' 카테고리의 다른 글

OS 만들기 -7- (Hardware Interrupt)  (0) 2010.10.26
OS 만들기 -5- (GDT)  (0) 2010.09.08
OS 만들기 -4- (Protected Mode)  (3) 2010.09.08
OS 만들기 -3- (Kernel)  (0) 2010.09.08
OS 만들기 -2- (MBR)  (2) 2010.09.02
AND

OS 만들기 -5- (GDT)

Operating Systems/OS 만들기 프로젝트 2010. 9. 8. 07:47 Posted by 알 수 없는 사용자




GDT(Global Descriptor Table)은 각 Segment에 대한 내용이 저장되어 있다.

 

사용할 코드/데이터/스택 등등의 영역(Segment)에 대한 정보를 기술하는 디스크립터(Descriptor)이며, 모아 놓은 것은 Descriptor Table이라 한다.

 

 

 

 BASE Address = Segment의 시작 주소. 하위 16bit 상위 16bit로 나누어서 저장.


 Limit = Segment의 한계점. Offset은 이 한계를 넘을 수 없다.

 G = 1일때는 Segment의 Limit 단위가 4KB(0xFFF)

  ex) 0xFFFFF 이고 G=1 이라면 Limit=0xFFFFFFFF

 DPL = CPU 레벨은 0~3값이 있지만 현재 0과 3만을 이용하고 있다. 보통 0이면 Kernel Level, 3이면 User Level이라 한다.

 S = System Segment(0), Code or Data Segment(1)

 Type = Code, Data 구별. Access bit, etc.

 

 

가장 첫 부분에는 항상 NULL Descriptor가 설정이 된다.

 

 

 

SysDataSelector Descriptor

Limit는 G 비트가 1이므로 0xFFFFF*FFF=0xFFFFFFFF이 되고, Base Address는 0x00010000이다.

DPL이 0이므로 커널레벨이라는 것을 알 수 있고, 네번째 줄의 0x9(1001) 첫번째 비트가 1이므로 Code Segment 이다.

 

위 그림처럼 CPU가 알 수 있도록 GDT를 등록해 놓는다.

GDTR Register 가 있는데 16bit Limit 와 32bit GDT Address 총 48bit를 가지고 있다.

두 가지 값을 알고 있다면 Memory 공간의 어느곳에 있는지 CPU가 알아 낼 수 있기 때문에 GDTR이라는 Register를 사용하고 있다.

 

위 Code에서 'gdtr:' 부분에서 dw(Word 16bit)로 Limit를 설정해주고 dd(Double Word 32bit)로 주소를 설정해 준다.

메모리 0x10000으로 불러들였기 때문에 이 주소와 GDT가 있는 Offset을 더하여 실제 메모리에 있는 주소를 구한다.

Limit는 GDT의 끝 부분과 처음 부분의 주소를 빼주어 그 크기 만큼으로 정하고 lgdt로 GDTR을 설정한다.

 

VideoSelector equ 0x18

이 부분은 간단하게 Index를 설정해 주는 것이라 보면 된다.

이 Index를 가지고 GDT의 어느 곳에 위치하고 있는 것인가를 알 수 있는 것이다.

실제 0~2bit는 TI, RPL이며 이 RPL은 DPL과 비교하는 bit이다. 나머지 bit 부분이 Index이다.

RPL와 DPL이 같다면 Index를 통하여 Offset을 구하고 GDTR에서 GDT의 시작주소를 더하게 되면 필요로 하는 Descriptor의 주소를 알 수 있다.

이 주소를 통하여 실제 메모리의 주소를 구할 수 있는 Segment Descriptor Register에 값을 설정해 줄 수 있다.

설정된 Segment Descriptor Register의 Base Address와 Segment안에 있는 Code의 Offset을 더하게 되면 실제 메모리의 주소가 된다.

 


 

 

 Protected Mode -> Segment Register : Segment Selector(16bit), Segment Descriptor Register(64bit)

 Segment Selector : CS, DS, ES, FS, GS, SS

 Descriptor Register : TYPE, Base Address, Limit

 

 

ex) VideoSelector Segment의 Base Address를 0x000B8020으로 변하게 되면

Base Address 0x000B8000


 

 Base Address 0x000B8020 

 

 20만큼 더 이동된 것을 확인 할 수 있다.


실제 msgPmode의 Data가 있는 Offset은 그대로지만 Base Address가 증가하였기 때문에 메모리 주소가 증가하였고 따라서 문자 출력도 바뀌게 된 것이다.


 다른 예로 DPL 값을 RPL과 틀리게 하면 DS Descriptor Register 설정이 안되어 메모리에 접근이 되지 않아 출력문이 나오지 않을 것을 확인 할 수 있다.


 (bochsdbg 로 확인 결과 SysDataSelector의 DPL을 0x11으로 바꿨더니 ss Selector 설정 부분에서 CPL[Current Privilege Level]과 DPL이 서로 다르다는 메시지가 뜨고 재부팅이 된다.)

 




'Operating Systems > OS 만들기 프로젝트' 카테고리의 다른 글

OS 만들기 -7- (Hardware Interrupt)  (0) 2010.10.26
OS 만들기 -6- (IDT)  (0) 2010.09.14
OS 만들기 -4- (Protected Mode)  (3) 2010.09.08
OS 만들기 -3- (Kernel)  (0) 2010.09.08
OS 만들기 -2- (MBR)  (2) 2010.09.02
AND

OS 만들기 -4- (Protected Mode)

Operating Systems/OS 만들기 프로젝트 2010. 9. 8. 07:23 Posted by 알 수 없는 사용자






Real Mode 같은 경우 16bit로 0xFFFF:FFFF로 지정할 수 있는 최대 였기 때문에 실제 0x10FFEF 까지만 인식을 할 수 있다.

 

현재는 GB이상 Ram으로 맞추는 경우도 있기 때문에 32bit Protected Mode로 변환을 해야 한다.

 

이 변환을 위해 사용되는 것이 GDT(Global Descriptor Table) 이다.

 

이 GDT를 이용하면 0xFFFFFFFF(4GB) 까지 이용이 가능 하며, DPL값을 이용하여 권한 설정등을 할 수 있다.

 

 

 


 lgdt 명령어로 GDTR Register(16bit GDT Limit, 32bit GDT Address) 을 설정하고 있다.

 

Before

 

After

 

 Base가 0x00010081로 바뀌었다. 이것은 0x10000으로 커널을 적재 시켰기 때문에 GDT또한 이 곳에 있으며, Offset으로 0x81이기 때문에 더하여 GDT의 주소가 위 처럼 변하게 된다.  Limit같은 경우엔 Descriptor(8byte)*4개 -> 0x1f(0~31) 값이 나오게 된다.


GDB Hex

 

 55AA로 끝나는 부분(0x1FF)이 첫번째 Sector(MBR)가 되고 두번째 Sector 부터 실제 Kernel 부분 Code 이다.

 그 중 GDT가 있는 부분이 0x281인 것을 알 수 있다. 결국 Offset은 0x81이 되어 실제 메모리 부분에서 0x10000 + 0x81이 되어 0x10081이 GDT 시작주소이다.

 


 

 

Before

 

CR0은 Control Register로서 4개의 레지스터 중 하나이다.

현재 Protected Mode라는 것을 CPU에 알려주기 위해 PE bit를 1로 설정을 해주고 있다.

 

After

 


실제 CPU에는 각각의 유닛이 있는데 동기적으로 실행(CPU 파이프라인 구조)이 된다.

A, B, C 각각의 순서대로 들어온 instruction이 있다고 하면 C는 읽기, B는 해석, A는 실행 유닛에 각각 들어있다.

결국 A에서 Protected Mode로 바뀌게 되면 다음에 있는 B, C 같은 경우 Real Mode로 되고 있는 상태였기 때문에

JMP와 NOP로 프로그램 수행에 차질이 없도록 한다.

 

Protected Mode에서는 Extended Register를 사용해야 한다.

처음에 [bits 16] 이였기 때문에 바꿔주기 위해 입력을 했지만 현재의 NASM은 32bit Code 지원이 된다.

JMP DWORD SysCodeSelector:PM_Start

울 입력하여도 JMP가 가능하다.

 

 

 

 Protected Mode에서 각 Segment Register를 등록해 준다.

edi Register에는 출력할 위치를 정해주고 esi Register는 "We are in Protected Mode" 부분의 주소를 가져온다.

그리고 출력할 부분은 전 Code와 동일 하다.

 

 

 

 잘 되는 것을 확인 할 수 있다.

중요한 포인트는 Real Mode에서 Protected Mode로의 변환을 위해 GDT가 필요하기 때문에 GDT를 등록 시키고 GDT를 이용하여 메모리에 접근을 하고 있다.


'Operating Systems > OS 만들기 프로젝트' 카테고리의 다른 글

OS 만들기 -6- (IDT)  (0) 2010.09.14
OS 만들기 -5- (GDT)  (0) 2010.09.08
OS 만들기 -3- (Kernel)  (0) 2010.09.08
OS 만들기 -2- (MBR)  (2) 2010.09.02
OS 만들기 -1- (bochs)  (1) 2010.09.02
AND

OS 만들기 -3- (Kernel)

Operating Systems/OS 만들기 프로젝트 2010. 9. 8. 05:33 Posted by 알 수 없는 사용자


 

MBR Code를 만들었다면 다음으로 실제 OS를 위한 Kernel을 만들어야 한다.

 

정말 간단한 문자를 출력하는  Kernel을 만들어보자.

 


es=1000 ip=0000이 되어 1000:0000이며 이것은 실제 메모리 주소는 0x10000에 커널을 적재 시킨다.


 

중요한 포인트는 Bios interrupt call 이다.

 

우선 리눅스와 비슷하게 메모리의 0x10000 지점에 커널을 적재할 수 있도록 지정을 해 준다.

 

int 0x13, ah=0x02  Bios interrupt call을 이용하여 Drive로부터 Sector을 읽어들인다.

al값을 이용하여 읽을 섹터수를 지정하고 다른 레지스터를 이용하여 인자값을 준 후 interrupt를 건다.

만약 실패(Flag Register CF비트가 1로 set)하면 아래의 .end로 가서 대기상태로 만들고 만약 성공한다면 0x10000으로 이동하게 한다.

실제 Kernel Code는 2번째 Sector에 존재하기 때문에 이 sector를 불러 들이는 작업을 하는 것이다.

 

 

 

위 그림은 실제 두번째 sector안에 들어갈 asm code 이다.

특별하게 하는 일은 없고 단순히 "Kernel program in 2 Sector"을 출력해 주는 일을 한다.

 

실제 동작하는 것에 대한 확인 작업을 해 보자.

Prompt>> nasm -fbin -o mbr.bin boot.asm

Prompt>> nasm -fbin -o kernel.bin kernel.asm

각각에 대한 컴파일을 한다.

Prompt>> copy mbr.bin+kernel.bin /b kernel

두개의 파일을 하나의 바이너리 파일로 만드는 작업이다.

 

 

 

앞에서 사용했던 것과 조금 다르게 인자를 줄수 있게 sys.argv[1]을 이용하였다.

 

Prompt>> python mbr.py os.img kernel

os.img에 아까 합쳐서 만들어 두었던 kernel의 code를 넣고 있는 것이다.

 

성공 했다면 bochs를 실행하여 실제 동작이 되는지 확인해 보자.

 

Prompt>> bochs -q

 

 

 

가장 중요한 포인트 Bios interrupt call 부분만 바뀌고 나머지 부분은 크게 변동된 점이 없다.

두번재 sector에 적재될 kernel code를 넣고 sector를 읽어들인 다음 그 주소로 jump 시키고 있을 뿐이다. (실제 MBR도 비슷한 원리)


'Operating Systems > OS 만들기 프로젝트' 카테고리의 다른 글

OS 만들기 -6- (IDT)  (0) 2010.09.14
OS 만들기 -5- (GDT)  (0) 2010.09.08
OS 만들기 -4- (Protected Mode)  (3) 2010.09.08
OS 만들기 -2- (MBR)  (2) 2010.09.02
OS 만들기 -1- (bochs)  (1) 2010.09.02
AND