참고. BIOS Call을 사용하는 방법들

 

들어가기 전에...

0.시작하면서...

 후배들하고 이야기하다가 32bit Mode에서 16bit 코드의 집합으로 되어있는 BIOS Call을 호출하는 방법에 대해서 깜짝 놀랄만한 이야기를 들었다. 대단한 놈들... ㅜ_ㅜ... 완전 감동이다.. ㅜ_ㅜ... 니들은 최고로구나.. ㅜ_ㅜ)/~!!!

 32bit에서 BIOS 함수를 사용하기위해서는 많은 제약이 따르는데, 그럼에도 불구하고 사용하는 이유는 많은 편리한 기능들을 제공하기 때문이다. 그럼 지금부터 2가지 방법에 대해서 알아보자.

 

방법 1. V86 Task를 이용한다.

 내가 기존에 사용하던 방법은 V86 Mode를 사용하는 것이었다. Interrupt 시에 32bit Interrupt Table을 사용하므로 커널 서비스를 그대로 사용할 수 있는 장점이 있는 반면 16bit BIOS 함수를 사용하기 위해서는 16bit Stack을 조작해야 하는 복잡합이 있다. 특히 스택 조작 부분이 최강 난이도.. @0@)/~

16bit와 32bit에 대한 이해를 바르게 하고 있고 함수 호출에 대한 개념(??)이 제대로 박혀 있어야 fault 없는 동작이 가능하다.

 하나라도 삐끗한다면... 죽음의 재부팅 또는 멈춤 현상이... ㅡ_ㅡ;;;;

 

 방법을 간단히 설명하면 이러하다. 일단 TSS에 태스크를 생성할 때, EFLAG 레지스터에 V86 FLAG를 설정해 놓고 태스크를 스위칭하여 실행한다. 그 상태에서 인터럽트를 통하여 커널모드에 있는 서비스 루틴을 호출하고 커널모두의 서비스 루틴은 32bit 커널 스택의 상위에 들어있는 16bit 스택을 얻어서 조작한다.

 여기서 중요한 점은 원래 32bit 커널 서비스가 끝나면 당연히 16bit 인터럽트 호출 코드 다음으로 돌아갈껀데, 이 리턴하는 주소를 BIOS Service Routine의 주소로 변경하는 것이다. 그렇게 되면 32bit 커널 서비스를 나갔을 때, BIOS 함수를 호출할 수 있다. 그럼 BIOS 함수를 호출한 뒤에는 어떻게 될까? 당연히 BIOS 함수도 수행을 끝내고 Return 하게 될 것인데... Return Address라고 생각하는 것을 꺼내면 잘못된 주소가 될것이므로 당근 문제가 생긴다. 고로 이 BIOS의 함수를 위해 주소를 인터럽트 호출 루틴 다음으로 넣어줘야 한다. 

 이것이 바로 스택 조작인데 상당한 내공을 필요로 한다. 말로 하니 상당히 어려운데, 이것을 그림으로 보면 조금 더 쉽다.

V86_Interrupt.PNG

<V86 모드에서 인터럽트 발생 시>

 

 위의 그림에서 보면 V86 모드에서 인터럽트가 발생하면 커널 스택에 V86 태스크 스택 포인터와 리턴 어드레스가 들어있는 것을 알 수 있다. 이 값을 이용하여 iretd라는 어셈블리어 명령이 V86 모드로 정상적으로 돌아갈 수 있는 것이다. 이 스택 포인터와 리턴 어드레스를 아래와 같이 조작하면 BIOS 함수를 실행하고 다시 정상적으로 복귀할 수 있다.

V86_Interrupt2.PNG

<V86 모드에서 BIOS 함수 호출 방법>

 위에서 붉은 색 부분을 유심히 봐야 한다. V86 모드 스택은 스택에 강제로 현재의 FLAG, CS, IP(INT를 호출한 코드의 주소)를 스택에 삽입하여 BIOS함수에서 iret를 호출했을 때 정상적으로 Int xxx 호출한 다음 코드로 돌아갈 수 있도록 준비한다. 이 상태에서 커널 서비스 루틴을 부르면 커널 서비스 루틴은 EIP 레지스터의 값을 수정하여 BIOS 서비스 루틴의 시작을 가리키도록 변경한 후 iretd를 한다.

 iretd를 하는 순간 어떻게 될까? 커널 모드 스택에 있는 값들이 다시 CPU로 복구되므로 당연히 리턴 어드레스에 설정된 BIOS 서비스 루틴으로 EIP가 설정되게 되고 이때 V86 모드의 스택은 FLAG, SS, IP의 값이 들어가있는 상태가 된다. BIOS에서 정상적으로 수행을 끝낸 뒤에 iret를 하게되면 V86 스택에서 값을 꺼내어 CPU에 복구하게 되는데, 이때 강제로 설정한 FLAG, CS, IP(Int xxx 주소)가 복구되므로 다시 V86 Task로 돌아오게 된다.

 

 이것이 스택 삽질(??)을 하여 BIOS 함수를 호출하는 방법이다. 말은 좀 간단한데... 실제로 해보면.... 수백번 좌절 먹는다.. ㅜ_ㅜ

 

 

방법 2. 16bit 모드로 전환했다가 다시 32bit 모드로 돌아온다.

 이것이 바로 궁극의 방법.... 16bit 관련 GDT를 모두 가지고 있다가 필요할때 CR0 레지스터와 GDT 스위칭을 통해 16bit <-> 32bit를 서로 전환하는 방법이다. 물론 16bit 모드로 돌아갔으니 32bit 태스크들이 멀티로 동작할 수 없다는 단점이 있으나 아예 다 일시 중지하고 BIOS 서비스를 사용하는 만큼 아주 간단하고 멋진 방법이다. @0@)/~!!

 물론 너무 자주 호출되거나 16bit 모드에서 하는 작업이 시간이 많이 걸리면 전체적으로 성능이 낮아지는 단점이 있지만... 간단한 BIOS 함수 호출 같은 것은 시도해볼만 하다.

 나중에 한번 적용을 고려해보자.

 

 

첨부

 

 

 

 

 

 

이 글은 스프링노트에서 작성되었습니다.

+ Recent posts