반응형

*부트 로더를 메모리에 정상적으로 복사하려면 부트섹터 512 byte에서 마지막 2 byte를 0x55, 0xAA로 저장해야 한다


해당 포스팅에서는 어셈블리를 사용할 것이므로 

http://gijadi.tistory.com/category/system/shellcode 포스팅을 먼저 보고 와주기를 바란다.


QEMU를 실행할 것인데,실행할 때마다 옵션을 입력하기는 불편하니까 배치 파일로 실행을 자동화 시킬 것이다. QEMU 디렉토리로 넘어가서  qemu-x86_64.bat 파일을 찾아 에디터로 열고 다음과 같이 수정해준다.

qemu-system-x86_64.exe -L . -m 64 -fda c:/MINT64/Disk.img -localtime -M pc

~>64mb의 물리 메모리 할당, 플로피 디스크 이미지로 MINT 64 OS이미지(Disk.img)설정 , 가상 머신을 일반 PC환경으로 설정


이제부터 화면에 문자를 띄울것인데 그 이전에 세그먼트 레지스터를 초기화하는 코드가 필요하다. 왜냐하면 BIOS가 부트 로더를 실행했을 때 세그먼트 레지스터에는 BIOS 값이 들어있기 때문이다 세그먼트 레지스터를 초기화 하지 않는다면 이상한 어드레스에 접근할 수 있으므로 미리 초기화하고 사용해야 한다.

해당 OS에서는 0x7c0으로 초기화 하였다(BIOS가 부트 로더를 디스크에서 읽어 메모리에 복사하는 위치가 0x7c0이기 때문)

또한 ES 세그먼트 레지스터는 화면 출력에 관련된 세그먼트로 사용하려고 0xB800을 설정하였다.


ex. 세그먼트 레지스터에 접두사를 사용하는 방법

mov byte [es:0x00], 'M' ;ES 세그먼트: 오프셋 0xB800:0x0000에 'M'을 복사

mov byte [es:0x01], 0x4A    ;ES 세그먼트:오프셋 0xB800:0x0001에 0x4A를 복사


화면 정리, 부팅 메세지를 출력하는 어셈블 코드를 모두 정리해보면 다음과 같은 소스가 나오게 된다.

<최종 부트로더 소스>





반응형
,
반응형

실습 환경 : Eclipse


디렉터리 구조는 이렇게 만들었고, 해당 디렉토리별 설명은 위와 같다.

-부트 로더 디렉터리와 대부분 유틸리티 디렉터리는 다른 디렉터리와 달리 소스파일 디렉터리와 임시 파일 디렉터리를 구분하지 않는다

-부트 로더는 어셈블리어 파일 하나로 이루어진 작은 프로그램이므로 굳이 디렉터리를 구분하지 않아도됨

-하지만, 보호모드 커널, IA-32e 커널, 각 응용프로그램은 여러 파일로 복잡하게 구성되므로 혼잡함을 줄이려고 임시 파일 디렉토리를 별도로 생성


make 프로그램

-소스 파일을 이용해서 자동으로 실행 파일 또는 라이브러리 파일을 만들어주는 빌드 관련 유틸리티

-소스 파일과 목적 파일을 비교한 뒤 마지막 빌드 후에 수정된 파일만 선택하여 빌드를 수행하므로 빌드 시간을 크게 줄여준다.

-하지만, make 프로그램이 빌드를 자동으로 수행하려면 각 소스 파일의 의존 관계나 빌드 순서, 빌드 옵션등에 대한 정보가 필요하다. 이러한 내용이 저장된 파일이 바로 'makefile'

~>해당 운영체제는 운영 모드별로 구분된 디렉터리가 최종 빌드 결과물을 최상위 디렉터리로 복사하고, 최상위 디렉터리의 makefile은 이 결과물을 이용해서 최종적으로 OS이미지를 생성하는 방식

~>각각의 하위 디렉터리에 있는 makefile은 해당 디렉터리의 C언어의 파일과 어셈블리 파일을 자동으로 추출함으로써, 효율적인 빌드를 수행하도록 하였다


<make 문법>~> Tab써준 부분에 반드시 Tab을 해줘야 한다.빌드하는 도중에 다른 make를 실행하는 것 도한 가능

Target: Dependency

<tab> Command

<tab> Command

...

-Target  

-생성할 파일.특정 레이블을 지정하여 해당 레이블과 관련된 부분만 빌드하는것도 가능

-Dependency

-Target 생성에 필요한 소스 파일이나 오브젝트 파일등을 나타냄.

-Command

-Dependency에 관련된 파일이 수정되면 실행할 명령을 의미

-명령창이나 터미널에서 실행할 명령 또는 프로그램을 기술


최상위 makefile ~> OS 이미지 생성을 위해 각 하위 디렉터리의 makefile을 실행하는 것이 목적

~> 현재는 부트 로더만 존재해서 해당 디렉터리로 이동해서 빌드하고, 빌드한 결과물을 복사하여 OS이미지를 생성하는 것이 전부

다음으로 부트로더 디렉토리의 makefile

~>BootLoader.asm 파일을 nasm 어셈블리어 컴파일러로 빌드하여 BootLoader.bin파일을 생성하는 것이 목적.

~clean Target의 경우 자신의 디렉터리에 있는 BootLoader.bin 파일을 삭제




반응형
,
반응형

윈도우 OS, 맥 OS,리눅스 OS 모두 동등하게 512바이트 크기의 작은 코드에서 시작한다. 512바이트의 작은 코드는 '부트로더(Boot Loader)라고 불리며, OS의 나머지 코드를 메모리에 복사해 실행 시킨다.


부팅과 BIOS

부팅

PC가 켜진 후에 OS가 실행되기 전까지 수행되는 일련의 작업 과정, 부팅 과정에서 수행하는 작업에는 프로세서 초기화(멀티코어 관련 처리 포함), 메모리와 외부 디바이스 검사 및 초기화, 부트로더를 메모리에 복사하고 OS를 시작하는 과정 등이 포함된다.

BIOS

 PC 환경에서는 부팅 과정중 하드웨어와 관련된 작업을 BIOS(Basic Input/Output System)가 담당하며, BIOS에서 수행하는 각종 테스트나 초기화를 'POST(Power On Self Test)'라 부른다.

BIOS는 메인보드에 포함된 펌웨어의 일종으로, 이름 그대로 입출력을 담당하는 작은 프로그램이다. 보통 PC메인 보드에 롬(ROM)이나 플래시 메모리로 존재하며, 전원이 켜짐과 동시에 프로세서가 가장 먼저 실행하는 코드이다.

~>또한 BIOS는 부팅 옵션 설정이나 시스템 전반적인 설정 값을 관리하는 역할도 겸하고 있으며, 설정 값으로 시스템을 초기화 하여 OS를 실행할 수 있는 환경을 만든다.

~>BIOS에서 제공하는 기능은 '인터럽트'를 통해 사용할 수 있으며, MS-Dos 같은 과거의 16비트 OS는 BIOS 기능에 많이 의존하였다. 


*BIOS는 부팅 과정에서 시스템 초기화 외에 수많은 작업을 하는데, 그 중 우리에게 가장 중요한 것은 부트 로더 이미지를 메모리로 복사하는 단계다. 부트로더는 부트스트랩(Bootstrap)코드라고도 불리고, 우리가 BIOS에서 처음으로 제어를 넘겨받는 부분이다. 부트로더는 플로피 디스크나 하드 디스크 등 저장 매체의 가장 앞 부분에 존재한다. PC는 다양한 장치로 부팅할 수 있으므로 BIOS는 POST가 완료된 후 여러 장치를 검사하여 앞부분에 부트로더가 있는지 확인한다. 부트로더가 존재한다면 코드를 0x7C00 어드레스에 복사한 후 프로세서가 0x7C00 어드레스 부터 코드를 수행하도록 한다.

~>부팅 가능한 모든 장치를 검사했는데도 부트로더를 찾을 수 없다면 BIOS는 'Operating System Not Found"와 같은 메세지를 출력하고 작업을 중단한다.

~>또한 , 부트로더가 디스크에서 메모리로 복사되어 실행되었다는 것은 BIOS에 의해 PC가 정상적으로 구동되었다는 것을 의미한다.(=OS를 메모리에 올려서 실행할 준비가 되었다)


부트로더는 플로피 디스크나 하드 디스크 같은 외부 저장매체에 있으며, 저장 매체에서 가장 첫 번째 섹터 MBR(Master Boot Record)에 있는 작은 프로그램이다. 섹터는 디스크를 구성하는 데이터 단위로, 섹터하나는 512 바이트로 구성된다.

~> 부트로더의 가장 큰 역할은 OS 실행에 필요한 환경을 설정하고, OS 이미지를 메모리에 복사하는 일이다.

~>부트로더의 크기가 512 바이트로 정해져 있기 때문에, 가장 먼저 실행되는 중요한 프로그램이지만 처리할 수 있는 기능은 한정되어 있다.



그렇다면 BIOS에게 첫번 째 섹터가 부트로더란 것을 어떻게 알려줄 것이며, BIOS는 디스크에서 읽은 첫번째 섹터가 정상적인 부트 로더 인지 어떻게 판단할까?

~> 디스크를 부팅할 용도로 사용하지 않는다면, 첫 번째 섹터는 부트 로더가 아닌 '일반 데이터'가 저장된다. 만약, BIOS가 실수로 데이터를 메모리에 올려 실행한다면 모니터에 섬광탄이 투척될것이다.. (ㅋㅋ;;). 즉, 이러한 사태를 방지하려면 BIOS는 첫 번째 섹터에 있는 데이터가 부트로더 인지를 확인해야한다.

~> 이를 위해 BIOS는 읽어들인 512 바이트 중에 가장 마지막 2바이트 값이 0x55,0xAA인지 검사해서 부트 로더인지 확인한다. 읽은 데이터가 0x55, 0xAA로 끝나지 않는다면 데이터로 인식하고 부팅 과정을 더 진행하지 않는다.


c.f) 디스크의 첫 번째 섹터인 MBR 영역에는 부트 로더 외에 디스크의 파티션 정보도 존재한다.

~> 파티션(Partition)은 디스크 영역을 논리적으로 구분하는 단위다. MBR 영역에는 4개의 파티션 영역이 있으며, 파티션 영역에서 정의된 영역은 독립된 공간을 보장받는다.

~>따라서 파티션을 분할하여 중요한 데이터를 여러 곳에 보관하면 특정 파티션에 문제가 발생해도 다른 파티션에서 데이터를 복원할 수 있다.





반응형
,
반응형

IA-32e 모드는 두가지 서브모드, 호환 모드와 64비트 모드가 있다. 호환 모드는 보호모드와 동작이 같아서 64비트만 포스팅 한다. 

IA -32 e 모드의 세그먼테이션은 보호 모드의 세그먼 테이션과 큰 차이가 없다. 다만, 주소 공간이 확장되고 두 가지 서브모드를 지원하는 등 몇가지 기능이 추가되면서 약간의 차이가 생겼다.


<차이점>

1. 세그먼트 디스크립터에 설정된 기준 주소와 크기에 관계없이 모든 세그먼트가 기준 주소는 0, 크기는 64비트 전체로 설정된다. 이는 보호 모드에서 사용하던 세그먼트 디스크립터가 32비트 어드레스만 저장하게 설계한 탓이며, 64비트 어드레스 지원을 위해 디스크립터를 확장하지 않고 강제로 값을 고정함으로써 이를 해결한다.

~> 이와 같은 이유로 해당 모드에서는 선형 주소를 기준 주소가 다른 여러 개의 세그먼트로 구분할 수 없음을 고려해서 OS를 설계 해야한다.


2. IA-32e 모드는 호환 모드와 64비트 모드의 두 가지 서브모드를 지원하므로 이를 구분하고자 코드 세그먼트 디스크립터에 L(비트 21)가 추가 됨

~> 이 필드를 0으로 설정하면 호환 모드로 동작, 1로 설정하면 64비트 모드로 동작해서 IA-32e 모드에서는 보호 모드로 돌아가지 않고도 32비트 코드를 실행할 수 있다.


IA-32e 모드의 페이징은 보호 모드와 달리 주소 공간이 64비트로 늘어나므로 PAE 기능이 기본으로 활성화 된다. 또한, 어드레스가 늘어난 만큼 변환 단계도 늘어나서 4KB 페이지는 5단계로, 2MB 페이지는 4단계로 변경된다. 그로 인해 새롭게 추가된 테이블은 페이지 맵 4 테이블(PML4,Page Map Level 4 Table)과 페이지 디렉터리 포인터 테이블(PDPT,Page Directory pointer Table)이고, 변환 과정은 이전 포스팅과 같다.

~>해당 모드의 페이지 엔트리는 64비트로 늘어난 어드레스로 인해 8바이트로 늘어났다. 하위 4 바이트는 보호 모드와 구조가 같고 상위 4바이트는 기준 주소 필드와 예약된 영역, 임의로 사용 가능 한 영역, EXB로 구성된다. 이중 EXB 필드는 해당 페이지에서 명령어가 실행되는 것을 막는 필드이다. ~> 이 기능을 이용하면 데이터 영역에서 명령어가 실행되는 것을 막을 수 있다.


*데이터 영역에서 명령어가 실행되는 것이 어떤 문제가 되는가?

- 데이터 영역이 데이터만 있는 즉, 프로그램 코드가 포함되지 않은 영역이기 때문이다. 일반적으로 프로그램은 크게 코드 영역과 데이터 영역으로 구분되고, 정상적인 프로그램이라면 데이터 영역에서 명령어가 실행되지 않는다. 하지만 간혹 데이터 영역에서 명령어가 실행되는 경우가 있는데 그 대표적인 예가 버퍼 오버플로우, 스택 오버플로우 이다.


반응형
,
반응형

메모리 관리 기법은 크게 '페이징 기법'과 '세그먼테이션' 두가지 존재한다. 

세그먼테이션

원하는 크기로 잘라쓸 수 있는 공간. 즉, 전체 영역을 원하는 크기로 나누어 관리하는 방식.

세그먼테이션은 세그먼트 레지스터에 '세그먼트 시작 주소' 혹은 '디스크립터(Descriptor)'라고 불리는 자료 구조 위치를 설정해야 한다.


페이징

정해진 크기로 잘라져 있는 공간. 일정한 단위로 잘라진 조각을 모아 원하는 크기로 관리하는 방식. 페이징은 컨트롤 레지스터 중에 'CR3' 레지스터에 페이지 디렉터리라고 불리는 자료구조의 물리 주소를 설정해야 사용할 수 있다.


하지만, 모든 운영 모드가 세그먼트와 페이징을 지원하는 것은 아니다. 각 모드에 따라 지원하지 않거나 지원하지만 일부 기능이 제한되는 경우도 존재한다. 또한, 같은 메모리 관리 기법을 사용하더라도 모드에 따라 필드의 의미가 달라지는 경우가 있다.


리얼 모드의 메모리 관리 방식

최대 1MB까지 주소 공간을 사용하며 세그먼테이션만 지원한다. 리얼 모드에서 세그먼트 크기는 64K로 고정이고, 세그먼트의 시작 어드레스는 세그먼트 레지스터에 직접 설정한다. 세그먼테이션에서 세그먼트의 시작 어드레스는 코드나 메모리에 접근할 때 기준 어드레스(Base Address)로 사용된다.

아래의 그림은 리얼모드의 세그먼트 레지스터와 세그먼트, 물리 주소의 관계를 보여준다

리얼 모드는 페이징을 사용하지 않아서 물리주소로 변환하는 방식이 비교적 간단하다. 세그먼테이션을 거쳐 나온 어드레스가 바로 '물리 주소' 가 된다.리얼 모드의 세그먼테이션은 세그먼트 레지스터의 값에 범용 레지스터 값을 더하는 방식으로 동작한다.

CF)리얼모드에서의 물리주소 계산방법 

세그먼테이션 레지스터(레지스터에 설정된값X16) + 범용레지스터(레지스터에 설정된값X16)

☞보호모드에서도 이러한 세그먼테이션 기법을 사용하긴 하지만, 페이징 기법이 추가되어서 계산결과는 물리주소가 아닌 '선형 주소'라고 불리는 논리주소로 바뀌엇고, 선형주소는 페이징을 거쳐 물리 주소로 바뀌게 된다.


보호모드의 메모리 관리 방식

보호 모드는 리얼모드와 달리 '세그먼테이션' 과 '페이징' 모두 지원한다. 또한, 보호 모드의 세그먼테이션은 리얼모드의 세그먼테이션보다 많은 기능을 제공한다. 더불어 보호모드의 세그먼테이션은 세그먼트 레지스터에 세그먼트 기준 주소를 직접설정하는 대신 디스크립터 자료구조 위치(OFFSET)를 설정하는 방식으로 바뀌었다. 세그먼트 레지스터의 명칭도 세그먼트 디스크립터를 선택한다는 의미에서 '세그먼트 설렉터'로 변경되었다.

~>디스크립터는 메모리 영역의 정보를 저장하는 구조로 여러 종류가 있고, 그중에서 세그먼트에 대한 정보를 나타내는 디스크립터를 '세그먼트 디스크립터'라고 부른다.

~> 세그먼트 디스크립터에는 세그먼트의 시작 어드레스와 크기, 권한(Previlege), 타입(Type)등의 정보가 있다. 세그먼트 

디스크립터에 포함된 특권 레벨(DPL, Descriptor Privilege Level)은 해당 세그먼트에 접속하기 위한 최소한의 권한을 나타내며, 특권 레벨은 0~3 사이를 가진다.(숫자가 작을수록 권한이 높고, 세그먼트에 접근하려면 현재 수행중인 특정레벨(CPL,Current Privilege Leve)이 적어도 디스크립터에 설정된 권한가 같거나 높아야 한다.

~>여기서 조건을 만족하지 않는다면 프로세서는 예외를 발생시켜 문제가 발생했음을 알린다. 접근하는 어드레스가 세그먼트의 크기를 넘어서는 경우도 역시 예외가 발생한다.


보호모드에서 세그먼트 레지스터는'세그먼트 디스크립터의 위치'를 가리킨다. 세그먼트 디스크립터는 메모리 상에 위치하는 자료구조의 일종으로 GDT(Global Descriptor Table)라 불리는 곳에 모여있다(나중에 GDT에 관한 자세한 포스팅을 따로 하나 올리도록 하겠다)

GDT는 연속된 디스크립터의 집합이며, 최대 8192개의 디스크립터를 포함할 수 있는 테이블 형태의 자료구조이다. GDT역시 메모리에 위치하는 자료구조에 불과하므로 프로세서에 GDT의 위치를 직접 알려야 한다.GDT와 관련있는 레지스터는 GDTR 레지스터이며, 16비트 GDT 크기 필드와 32비트 기준 주소 필드로 구성된 자료구조의 물리 주소를 넘겨받는다.

~> 프로세서는 이 값을 내부에 저장했다가 세그먼트 설렉터를 통해 어드레스에 접근할 때마다 GDT의 위치를 찾는데 참조한다.

보호모드에서 주소를 계산하는 방법은 리얼모드와 마찬가지로 계산을 한다. 하지만 위에서 설명했듯 이 주소는 선형주소이므로  프로세서는 실제 메모리 주소와는 다를수도 있고 같을 수 도있다(페이징 기법이 적용되기 떄문이다). 

*선형주소는 이후에 나올 페이징의 입력값이 되며, 페이징을 사용하지 않는다면 선형주소는 리얼모드처럼 물리주소와 1:1 대응한다.


페이징

 물리 메모리를 페이지(Page)라고 불리는 일정한 크기로 나누고, 선형 주소와 물리주소를 나눠 놓은 페이지로 연결하는 방식. 페이징을 사용하면 물리 메모리 크기보다 더 큰 영역의 선형 주소도 물리 페이지만 연결하면 사용이 가능하므로, 주소 공간을 더 넓게 사용할 수 있는 장점이 있다. 또한, 같은 물리 페이지를 여러 선형 주소에 연결함으로써 응용프로그램끼리 공유하는 메모리를 손쉽게 처리할 수 있다.

보호모드에서 페이징은 페이지 크기에 따라 크게 두가지 방식으로 구분한다.

1. 물리 메모리를 4KB 크기로 나누고 선형 주소를 3단계로 구분하는 방식

2.물리 메모리를 4MB 크기로 나누고 선형 주소를 2단계로 구분하는 방식

~>기본원리는 두가지 방법 모두 같다.

3단계 페이징은 선형주소를 디렉터리, 테이블, 오프셋 세 부분으로 나누며 물리 메모리를 4KB 페이지로 나누어 관리하는 방식이다. 선형 주소의 디렉터리 부분과 테이블 부분은 각기 페이지 디렉터리와 테이블에 있는 엔트리의 위치를 나타낸다.

~>페이지 디렉토리와 테이블은 앞서 설명한 GDT와 마찬가지로 메모리 공간에 있는 자료구조일 뿐이다.

프로세서가 페이징 처리과정에서 해당 테이블을 사용하려면 우리가 직접 위치를 알려줘야 한다. GDT에서  GDTR이 존재하듯 페이징에는 CR3 컨트롤 레지스터가 존재한다. CR3 컨트롤 레지스터는 페이지 디렉토리의 '시작 주소'를 가리키며 페이지 디렉초리 엔트리의 위치 계산에 사용한다(페이지 디렉토리 엔트리와 페이지 테이블 엔트리는 크기가 모두 4Byte, 페이지 크기가 최소 4KB이므로 비트 12~비트 31로 기준 주소를 나타내고 비트 11~비트 0은 속성 필드로 사용한다).

요약 해보면 선형주소에서 물리주소를  구하는 과정은 다음과 같다

1.CR3 레지스터에 설정된 어드레스로 페이지 디렉토리의 '시작 주소'를 찾는다

2. 페이지 디렉토리의 시작 주소에 선형 주소의 '디렉토리 오프셋'을 이용해서 해당 페이지 테이블 엔트리를 찾는다(페이지 테이블 엔트리에 설정된 값이  페이지 테이블의 시작주소)

3. 페이지 테이블의 시작 주소에 선형 주소의 오프셋을 이용해서 해당 페이지 테이블 엔트리를 찾는다(페이지 테이블 엔트리에 설정된 값이 4KB 페이지의 시작주소)

4. 페이지의 시작 주소에 선형 주소의 페이지 '오프셋 값을 더해 실제 물리주소로 변환한다.


다음 포스팅에서는 나머지 한개 IA-32e 모드의 메모리 관리에 대해 살펴볼 것이다.





반응형
,
반응형

범용 레지스터(General Purpose Register)

운영 모드와 가장 관계가 깊은 레지스터. 범용 레지스터는 계산, 메모리 어드레스 지정, 임시 저장 공간등의 목적으로 사용한다. 운영 모드 앞에 붙는 숫자와 범용 레지스터의 크기는 대체로 일치하지만, 범용 레지스터의 수는 프로세서가 지원하는 운영 모드에 따라 다르다.

범용 레지스터의 수가 늘어나면 어떻게 될까?

가장 큰 장점은 수행 속도의 개선이다. 관련된 값을 레지스터에 모두 올려서 계산함으로써 메모리에 접근하는 시간을 줄이는 것이 가능하다. 함수 호출에도 이와 같은 개념이 그대로 적용된다. 호출되는 함수가 작업을 처리하려면 정보가 필요하고, 이러한 정보는 호출하는 쪽에서 파라미터를 통해 넘겨준다. 다수의 범용 레지스터에 함수 파라미터를 넣어 넘겨줌으로써 스택 영역의 메모리에 접근하는 시간과 스택을 정리하는 시간을 줄일 수 있다.

c.f)레지스터

프로세서 내부에 있는 작은 공간, 연산, 제어, 상태 표시, 디버깅 등의 목적으로 사용한다. x86-64 프로세서에는 여러 종류의 레지스터가 존재한다. 이는 곧 프로세서가 다양한 기능을 제공한다는 것을 의미하고, 구조 또한 복잡함을 의미한다.


~> 범용 레지스터의 용도는 고정된 것이 아니고, 다양한 용도로 사용된다(우리가 쉘코드를 만들때처럼 .. ). 하지만, 모든 상황에서 가능한 것은 아니므로 특정 명령어는 특정 레지스터와 같이 사용해야 한다.

여러 레지스터가 존재하는데, 다른 포스팅에서 처럼 레지스터를 다시한번 살펴보겠다.

 범용 레지스터 

용도 

AX 

산술 연산을 수행할 누산기(ACCUMULATOR) 

BX 

데이터 어드레스를 지정할 때 데이터 포인터로 사용 

CX 

루프 또는 문자열의 카운터로 사용 

DX 

I/O 어드레스를 지정할 때 사용되며, 산술 연산을 수행할 때 보조 레지스터로 사용 

SI 

문자열에 관련된 작업을 수행할 때 원본 문자열의 인덱스로 사용 (SOURCE)

DI 

문자열에 관련된 작업을 수행할 때 목적지 문자열 인덱스로 사용 (DESTINATION)

SP 

스택의 포인터로 사용 

BP 

스택의 데이터에 접근할 때 데이터의 포인터로 사용 (BASE POINTER)

R8~R15 

x86-64 프로세서에서 추가된 범용 레지스터로, 다양한 용도로 사용가능. 


c.f) 레지스터가 특수한 용도로 사용되는 예는 '디바이스 드라이버 코드'에서 찾을 수 있다.  PC에 연결된 디바이스를 제어하려면 장치가 연결된 I/O 어드레스에 접근해야 한다. 이때 I/O 어드레스를 저장할 수 있는 레지스터는 'DX'를 통해서만 가능하다

~>범용 레지스터의 크기는 프로세서의 '운영 모드'와 관련이 있다. 또한, 운영 모드에 따라 접두사를 결합하는 방법에는 일정한 규칙이 있으며 사용한 접두사에 따라 명령어(instruction)가 처리하는 오퍼랜드나 어드레스의 크기가 달라진다.


세그먼트 레지스터

세그먼트 레지스터의 주된 역할은 어드레스 영역의 구분이지만, 모드마다 조금씩 역할의 차이가 존재한다. 예를 들어, '리얼 모드'에서는 단순히 고정된 크기의 어드레스 영역을 지정하는 역할만 하지만, '보호 모드'와 'IA-32' 모드에서는 접근 권한, 세그먼트의 시작 어드레스와 크기 등을 지정하는데 사용되기도 한다.~> 이러한 기능을 이용하면 응용프로그램으로 부터 커널영역을 보호하는 기능을 쉽게 구현할 수 있다.

 세그먼트 레지스터 이름

설명 

CS 

-코드 영역을 가리키는 레지스터

-데이터 이동 명령으로 값을 변경할 수 없으며, 점프 명령이나 인터럽트 관련 명령으로 변경 가능 

DS 

-데이터 영역을 가리키는 레지스터

-데이터 이동 명령으로 값을 변경할 수 있음

-DS 레지스터는 데이터 영역에 접근할 때 암시적으로 사용됨

-ES 레지스터는 문자열과 관련된 작업을 처리할 때 암시적으로 사용됨

-데이터 영역에 접근하면서 DS레지스터 이외의 세그먼트 레지스터를 사용하려면 세그먼트 레지스터 '접두사'를 사용해야 함

ES

FS

SS 

-스택 영역을 가리키는 레지스터

-데이터 이동 명령으로 값을 변경할 수 있음

-스택 관련 레지스터(SP,BP)를 통해 스택에 접근할 때 암시적으로 사용됨 

~> 세그먼트 레지스터 접두사를 통해 명시적으로 특정 세그먼트를 설정할 수 있다. 스택 관련 작업을 수행할 때 역시 세그먼트 레지스터 접두사를 통해 ss 레지스터가 아닌 기타 세그먼트(CS 레지스터 제외)를 설정할 수 있다. 명시적으로 지정하지 않으면 기본적으로 사용되는 세그먼트 레지스터는 위의 표와 같다.

~> 세그먼트 레지스터의 역할은 주소 공간을 목적에 따라 구분하는 것이고, 주소 공간을 구분하는 방법은 메모리 관리 기법과 깊은 관계가 있다.(지금 이 포스팅을 하고 있는 사람도 메모리 때문에 운영체제를 공부하고 있다 ..)

메모리 관리 기법에는  크게 ' 세그먼테이션 기법'과 '페이징 기법 ' 두가지가 존재한다. 이 중 세그먼트 레지스터를 통해 주소 공간을 구분하는 방식이 '세그먼테이션' 이다. 


컨트롤 레지스터

운영 모드를 변경하고, 현재 운영 중인 모드의 특정 기능을 제어하는 레지스터, X64 프로세서에서는 CR0,CR1,CR2,CR3,CR4의 5개 의 컨트롤 레지스터가 존재하며, X86-64 프로세서에는 CR8이 추가 되어 총 6개의 컨트롤 레지스터가 있다.

 컨트롤 레지스터

설명 

CR0 

-운영 모드를 제어하는 레지스터

-리얼 모드에서 보호모드로 전환하는 역할과, 캐시, 페이징 기능 등을 활성화 시킴 

CR1 

-프로세서에 의해 예약된 레지스터 

CR2 

-페이지 폴트 발생시 페이지 폴트가 발생한 선형 주소가 저장되는 레지스터 

-페이징 기법을 활성화한 후에는 페이지 폴트 발생 시만 유효한 값을 가짐

CR3 

-페이지 디렉토리의 물리주소와 페이지 캐시에 관련된 기능을 설정하는 레지스터 

(페이징)

CR4 

-프로세서에서 지원하는 각종 확장 기능을 제어하는 레지스터

-페이지 크기 확장이나 메모리 영역의 확장 등의 기능을 활성화 시킴 

CR5 

-프로세서에서 지원하는 각종 확장 기능을 제어하는 레지스터

-페이지 크기 확장이나 메모리 영역 확장 등의 기능을 활성화 시킴 

CR8

-태스크 우선순위 레지스터의 값을 제어하는 레지스터

-프로세서 외부에서 발생하는 인터럽트를 걸러주는 필터 역할

-IA-32e 모드에서만 접근가능 

여기서 CR0 와 CR4, CR8 레지스터에서는 64비트 중 상위 32비트를 0으로 설정해야 한다.  CR2의 경우 64비트 영역을 모두 사용할 수 있고, CR3 레지스터는 비트 40~ 51 까지 모두 0으로 설정해야한다.

컨트롤 레지스터의 각 필드는 저마다 특정 기능을 '활성화/비활성화' 하며 , 현재 운영모드에 따라 필수 필드와 옵션 필드가 달라진다. 

여기서 중요한건 컨트롤 레지스터의 특정기능은 해당 비트를 1로 설정해도 충분하긴 하나, 특정 기능은 1로 설정 하기전에 프로세서가 사용할 자료구조를 미리 준비해두어야 한다.


반응형
,
반응형

후,, 컴터가 맛이가서 3번 복구하고 와서 이제서야 포스팅을 시작하네요 ... 본론으로 바로 들어가겟습니다 

존칭 생략합니다.



각 모드에는 아래의 표와 같은 특징이 있으며, '컨트롤 레지스터'와 '인터럽트'라는 특수한 이벤트를 통해 각 모드를 전환할 수 있다.

 운영모드

설명 

리얼모드 

-프로세서의 초기 상태로서 16비트 모드로 동작하며 8086 프로세서와 호환되는 모드

-최대 1MB(2^20)의 주소 공간을 지원 

보호모드 

-32비트 모드로 동작하며 세그먼트,페이징,보호,멀티태스킹 등의 기능을 제공하는 모드

-4GB92^32)의 주소 공간을 지원 

IA-32e 모드 

-32비트 호환모드와 64비트 모드의 두 가지 서브모드로 구성 

-16EB(2^64)의 주소공간을 지원하는 모드

시스템 관리 모드 

-전원 관리나 하드웨어 제어 같은 특수 기능을 제공하는 모드 

가상 8086 모드 

-보호 모드 내부에서 가상의 환경을 설정하여 리얼 모드 처럼 동작하는 모드 

x86-64 프로세서는 위와 같이 총 다섯가지 운영모드를 지원하지만, 다섯 가지 운영 모드를 모두 구현해야 0S를 만들 수 있는 것은 아니다. 목적에 따라 몇몇 운영모드는 구현하지 않거나 필요하면 구현하는 방식으로 진행할 수 있다.


여기서 64비트 OS가 반드시 지원해야 하는 세가지 모드는 '리얼 모드', '보호 모드' IA-32e 모드 중 64bit 서브모드'이다.

~> 시스템 관리모드나 가상 8086모드는 특수한 상황에서 사용되므로 OS에서 해당 기능을 사용하지 않는다면 구현하지 않아도 된다.


리얼모드

프로세서가 어떤 상태 또는 모드에 있든 전원이 켜지거나 리셋되면 프로세서는 리얼 모드로 진입한다. 리얼 모드는 과거의 16비트 프로세서와 동일하게 동작하며, 이후 BIOS(Basic Input Output System)의 여러 기능을 사용할 수 있다. 

~> BIOS는 디스크 읽기 및 쓰기 기능부터 그래픽 모드로 전환하는 기능까지 여러 가지 기능을 제공하며, 이를 사용하면 별도의 디바이스 드라이버를 제작하지 않고도 원하는 작업을 수행할 수 있다. 

리얼 모드는 디바이스 드라이버를 제작하지 않아도 되는 장점이 있지만, 상당히 부담스러운 부분이 존재한다.(리얼 모드에서 하는 작업은 OS이미지를 디스크에서 메모리로 복사여 보호모드로 변경하는 것 밖에 없지만 대부분 작업을 어셈블리어로 처리해야 하기 때문에, 하지만 이부분만 지난다면 C언어로 작업할 수 있다)


보호모드

IA-32e 모드로 전환하려면 반드시 거쳐야 하는 모드로, 32비트 윈도우나 리눅스 OS가 동작하는 기본 모드.최대 4G(2^32) 주소 공간을 제공하며 OS의 필수 기능으로 자리 잡은 보호, 멀티태스킹, 세그먼테이션, 페이징 등의 기능을 하드웨어 적으로 지원한다.(여러 기능을 제공하는 만큼 상당히 복잡하고, 레지스터와 자료구조가 다양하다.. 아마 나도 이부분을 건드리면서 암에 걸릴지도 모르겟다..)

~> 보호 모드에서 사용하는 레지스터는 대부분 IA-32e 모드에서도 같이 사용하므로 보호모드를 이해하는 것이 매우 중요하다.


IA-32e모드

서브 모드로 32 비트 호환 모드와 64비트 모드가 있고, 우리가 투자해야할 많은 시간이 이곳에 존재한다. 


~>프로세서가 32비트 호환모드 일 때는 보호모드에 있는 것 처럼 동작하므로 32비트 코드를 그대로 실행할 수 있다. 이는 간단히 서브모드만 변경함으로써 보호모드 코드를 실행할 수 있다는 것을 의미한다.


운영 모드 사이의 관계와 전환

특정 모드에서 다른 모드로 전환하는 작업은 현재 동작 중인 모드에 따라 차이가 있다. 아래 그림과 같고, 화살표가 연결되지 않은 모드 끼리는 다이렉트로 전환이 불가능하며, 무리하게 시도하면 리셋이나 예외가 발생할 수 있다.(그림 장인 정신으로 그려 보았다... 혹시 문제가 있다면 글 남겨주길 바란다)

조건에는 컨트롤 레지스터와 인터럽트 발생만 표시되어 있지만, 기본적으로 각 모드에 필요한 '자료 구조'는 미리 설정 되어 있어야 한다.

다음 포스팅에서는 프로세서 레지스터를 다시 한번 살펴보고(다른 포스팅에서 짧게나마 설명했었다), 메모리 모델에 대해서 알아보도록 하겠다.



반응형
,