Cortex-A 프로세서 : Cortex-A는 Armv7-A, Armv8-A 아키텍처를 사용해 만든 프로세서. 고성능을 필요로 하는 애플리케이션용으로 개발된 프로세서이며 주로 스마트폰에 사용
Cortex-R 프로세서 : Cortex-R는 Armv7-R, Armv8-R 아키텍처를 사용. 실시간성을 필요로 하는 응용분야를 위해 개발된 프로세서. 자동차의 브레이크 시스템, 항공시스템, 공장자동화에 사용되는 PLC(Programmale Logic Coontroller)와 같은 실시간 응답이 아주 중요한 분야에 사용된다.
Cortex-M 프로세서 : Cortex-M는 Armv7-M, Armv8-M 아키텍처를 사용해 만든 프로세서. 저비용,저전력 , 저성능 용으로 개발된 프로세서이다. 저전력을 필요로 하기 때문에 주로 배터리로 동작하는 모바일 디바이스의 설계에 적합. iot 디바이스 용으로 많이 고려되고 있다.
x86의 경우 IP(Instruction pointer)를 통해 다음 명령어의 위치를 담는다.
ARM의 경우 PC를 사용한다. r15레지스터에 PC값이 담긴다. -> 32bit인 경우
CPU는 PC가 가리키는 곳에 있는 명령어를 읽어온 후, pc를 자동으로 증가시킨다.
arm cpu의 경우 PC는 4바이트씩 증가하도록 설계되어 있다. 왜냐하면 arm 명령어의 크기는 모두 4바이트이기 때문이다.
처음 주소는 cpu 제조사마다 다른데 x86의 경우 16진수로 0xFFFF_FFF0에서 시작하고
arm의 경우 대부분 0x0에서 시작하도록 설계되어 있다.
여기서 부터는 armv8, 64비트에 대한 설명이다.
ARMv8 에서는 익셉션 모델이라는 개념이 추가되었다. ARMv8 프로세서는 EL0, EL1, EL2, EL3 중 하나의 레벨에서 동작한다. EL0는 유저 애플리케이션 , EL1은 커널, EL2는 하이퍼바이저, EL3는 보안 모니터가 동작한다.
각 레벨별로 받을 수 있는 익셉션 종류가 다르다. 발생할 수 있는 익셉션 종류는 다음과 같다.
- IRQ, FIQ
- 메모리 시스템 abort
- undefined 인스트럭션 실행
- 시스템 콜
- 보안 모니터, 하이퍼바이저 트랩
2. 실행 상태
ARMv8 아키텍쳐는 AArch64라는 64비트 모드와 AArch32라는 32비트 모드 두 가지 상태로 실행될 수 있다.
AArch32의 경우 기존의 ARMv7의 아키텍처로 돌아가게 된다. 이런 실행 상태의 변경은 익셉션 레벨이 변경될 때 이루어지게 된다.
ARMv8 아키텍쳐는 새로운 64비트 크기의 범용 레지스터 31개와 다양한 특수 레지스터들을 사용한다. 기존의 ARMv7과 다른 점은 프로세서 모드에 따라 사용하는 레지스터 세트가 달라지지 않고 같은 레지스터 세트를 사용한다는 것이다. 범용 레지스터의 64비트 참조는 X0~X30을 이용하고 하위 32비트는 W0~ W30이라는 이름을 사용한다.
다음은 PSTATE 특수 레지스터이다
ARM의 레지스터 셋이 32비트에서 64비트로 넘어오면서 이렇게 구조가 바뀔 수밖에 없었던 이유는 ARM의 명령어 셋의 OPCode가 상위 bit에 존재해서인 것 같다. 복잡하다.
한눈에 보기에는 눈에 잘 들어오지 않는 이 명령어 셋(ISA, Instruction Set Architecture) 표를 보면 상위 bit에 Opcode가 존재함을 알 수 있다.
또한 ARM 64bit 에서는 A64 명령어 셋(ISA, Instruction Set Architecture)을 사용하는데 기존 T32와 A32를 이용하는 방식이다.
PC (Program Counter)
PC 레지스터는 다음에 실행할 인스트럭션의 위치를 저장하는 레지스터다.
ARMv8에서 PC 레지스터는 프로그래머가 직접 접근하지 못한다. 대신 PC 로드와 상대 주소 지정 같은 인스트럭션에서 내부적으로 사용한다. 이런 제약 덕분에 복귀 예측이 쉬워지고 ABI 스펙이 간단해졌다.
ELR (Exception Link Register)
ELR 레지스터는 익셉션 복귀 시 돌아갈 실행 위치를 저장하는 레지스터다.
프로세서에 의해 현재 익셉션 레벨에 해당하는 레지스터 값이 PC에 복사된다.
복귀할 익셉션 레벨이 없는 EL0를 제외한 익셉션 레벨마다 존재하며, 이름은 ELR_ELn이다.
SPSR (Saved Process Status Register)
SPSR 레지스터는 특정 시점의 프로세서 상태를 저장하는 레지스터다.
익셉션이 발생하면 프로세서에 의해 프로세서 상태 PSTATE로부터 SPSR에 저장된다.
익셉션에서 복귀할 떄 프로세서에 의해 SPSR에서 PSTATE로 복원된다.
SPSR은 익셉션을 취할 수 없는 EL0를 제외한 익셉션 레벨마다 존재하며, 이름은 SPSR_ELn이다.
그 밖의 필드들은 PSTATE 필드와 동일하다.
M [4] (실행 상태 제어) > 익셉션이 발생했을 때의 실행 상태, aarch64일 때 0이다.
M [3:0] (실행 상태 제어) > 익셉션이 발생했을 때의 모드 또는 익셉션 레벨이다.
- 분기 명령어
L1 : addeq r3, r4, #10 0x204
....
....
....
b L1 0x14
원래는 PC값이 다음 명령어를 수행하기 위해 4씩 더해가지만 분기 명령어가 올 시에 PC값이 다르게 변경된다.
여기서는 PC값이 b L1후에 0x204가 들어간다.
armv7, v8에 따라서 분기 명령어가 달라지는 건지 잘 모르겠다.
하지만 대표적으론 B와 BL이 있는 것 같다.
B는 if, while, for, case 같은 조건문이고 b뒤에 eq, hi, gt와 같은 조건 코드를 붙일 수 있다.
beq L1은 eq조건을 만족하면 PC를 L1레이블의 위치로 설정하라는 것이다.
프로그램 실행 방향 쪽으로 PC를 설정하면 forward branch라고 하고 뒤쪽으로 간다면 backward branch이다.
bl명령어는 함수 호출을 할 때 사용한다.
B there ; 라벨이 there인 곳으로 무조건 분기한다.
BL sub+ROM ; 계산된 위치의 서브루틴을 호출한다.
bl명령어를 실행하면 하드웨어 적으로 두 가지 동작을 한다. 첫째, PC를 함수명(레이블)으로 설정한다.
둘째, 돌아올 곳의 주소를 Link Register인 r14(=32bit)에 저장한다. bl도 다른 명령어와 마찬가지로 bl뒤에 eq, hi, gt와 같은 조건 코드를 붙일 수 있다.
그럼 분기 명령어를 이용해 얼마나 멀리 있는 곳의 레이블까지 갈 수 있을까?
b와 bl명령어 형식을 보면
이런 식으로 되어있는데 offset이라고 표현되어있다.
그러니까 해당 분기 명령어의 위치가 pc라고 가정하면 pc + offset이 되는 것이다.
이러한 방법을 사용하는 이유는 파이프라인이라는 방법을 사용하기 때문이다.
파이프라인은 처리량을 높이는 방법으로, 요즘은 거의 모든 cpu설계에서 사용된다.
가장 기본적인 파이프라인은 5단계, 즉 IF, ID, EXE, MEM, WB로 구성되어 있다. 이러한 기본 파이프라인을 이용해 설계된 cpu에서 각 명령어는 5단계를 모두 거쳐야 실행이 완료된다.
pc+8에 대해 알아보자( =offset을 정하기 위한 일종의 표준 같음)
beq L 1분기 명령어는 메모리 0x104 위치에 있다. 이 위치가 현재 pc이다.
beq L 1 명령어는 실행을 위해 4단계를 거쳐야 하고, 분기할 목적지는 파이프라인의 실행단계 exe에서 계산된다.
104 108 10c 200 204
0x100 IF ID EXE MEM WB
0x104 IF ID. EXE. MEM.
0x108 IF ID. EXE.......
0X10 c IF. ID.
그런데 beq l1의 실행단계에서 pc는 이미 0x10 C(즉, PC+8)로 바뀌어 있고 분기 목적 위치를 계산할 때 바뀐 pc를 사용하여 기존 예상한 것보다 넘어간 PC를 사용할 수가 있다.
이를 해결하기 위해 처리를 해주어야 하고 forward branch이냐 backward branch이냐에 따라 달라질 수 있다.
이를 b, bl명령어의 하위 비트에 표시를 해준다.
forward에서는
만약 아래 비트에 +2가 들어 있다면 분기 명령어의 목적지가 pc+8에서 시작하여 프로그램 실행방향으로 2개 명령어 앞에 있다는 것이다.
backward에서는 0 xff_fffa라고 한다면 이는 -6이다.
pc+8에서 6개 뒤로 간다는 것이라고 보면 된다.
- 소프트웨어 인터럽트
소프트웨어 인터럽트는 명령어로 제공되며, Arm은 소프트웨어 인터럽트를 발생시키기 위해 'SVC'라는 명령어를 제공한다.
이러한 인터럽트는 운영체제에서 '시스템 콜'을 구현할 때 주로 사용한다.
리눅스에서 시스템 콜을 구현할 때는 SVC명령어 실행 전, 특정 레지스터에 설정한 인자에 따라, 어떤 함수(open, close, read, write...)를 호출하는지 구별한다.
SVC명령어를 실행하면 supervisor모드로 작동 모드가 바뀐다. 따라서 supervisor 모드에 있는 레지스터들을 사용하게 된다.
조금 더 자세히 알아보면
user mode에서 코드가 실행되다가 svc #3을 호출한다.
이때 cpu는 하드웨어 적으로 몇 가지 작업을 한다.
1. lr_svc(=elr) <-svc : 다음 명령어의 위치 , 즉 svc 다음 명령어의 위치로 복귀할 곳을 설정한다. 이를 통해 마지막에 pc = lr_svc로 복구한다.
2.spsr_svc <-cpsr : 여기서 psr은 Program Status Register이다. 그렇다면 current의 register를 spsr에 저장해둔다는 것이다.
3. cpsr'm [4:0] = supervisor mode : supervisor mode 설정
cpsr'i <- 1 = : i bit는 irq, 즉 하드웨어 인터럽트를 masking 하는 bit이다. 이 상태라도 소프트웨어 인터럽트는 여전히 발생 가능함
4. pc = 0x08 이것은 어떤 interrupt냐 따라서 달라짐, svc를 통해 인터럽트 vector table로 가게 되는데 여기서 svc는 offset이 0x08이다.
3번까지는 설정이고
interrupt vector table을 가기 위해선 코드에서(= 어셈블리 8)에서 VBAR(=Vector Base Address Register)를 IVT(=interrupt vector table)의 시작 주소로 설정해줄 것이다.
그리고 svc 모드에서 쓰는 stack을 설정해준다. ( = sp 설정)
svc #10?
#10은 별 의미 없는 숫자이다. (그럼 이걸 정하는 기준이 어떻게 되는지는 의문, 어떤 시스템 콜이 해당 번호를 가지게 되는 건지...)
구현에 따라서는 ISR(interrupt service routine)에서 메모리에 있는 'svc #10' 명령어를 읽어와 imm값을 빼낸 후, imm에 따라 필요한 작업을 할 수가 있다.
이는 lr_svc가 svc#10의 다음 명령 주소를 가리키고 있고 여기서 -4를 하면 svc#10의 위치라서 가능하다고 함
정리하자면 svc #10을 실행하면 pc=0x08로 설정되고 cpu는 IVT에 있는 'b svc_ISR'을 실행한다.
그럼 이 ISR은 system call 루틴을 진행한다.
그리고 끝난 후에는 pc <- lr_svc , cpsr <- spsr_svc를 하여 사용자 프로그램으로 복귀함.
'Study' 카테고리의 다른 글
OPTEE pc레지스터 & secure storage(수정중) (0) | 2021.07.26 |
---|---|
OPTEE Systemcall Hash연산 (0) | 2021.07.25 |
ELF파일 (0) | 2021.07.25 |
OPTEE system call 실습 (0) | 2021.07.24 |
ARM TrustZone 메커니즘 (2) | 2021.07.22 |