이제 리셋 익셉션 핸들러 구현하면
#include "ARMv7AR.h"
#include "MemoryMap.h"
.text
.code 32
.global vector_start
.global vector_end
vector_start:
LDR PC, reset_handler_addr
LDR PC, undef_handler_addr
LDR PC, svc_handler_addr
LDR PC, pftch_abt_handler_addr
LDR PC, data_abt_handler_addr
B .
LDR PC, irq_handler_addr
LDR PC, fiq_handler_addr
reset_handler_addr: .word reset_handler
undef_handler_addr: .word dummy_handler
svc_handler_addr: .word dummy_handler
pftch_abt_handler_addr: .word dummy_handler
data_abt_handler_addr: .word dummy_handler
irq_handler_addr: .word dummy_handler
fiq_handler_addr: .word dummy_handler
vector_end:
reset_handler:
MRS r0, cpsr
BIC r1, r0, #0x1F
ORR r1, r1, #ARM_MODE_BIT_SVC
MSR cpsr, r1
LDR sp, =SVC_STACK_TOP
MRS r0, cpsr
BIC r1, r0, #0x1F
ORR r1, r1, #ARM_MODE_BIT_IRQ
MSR cpsr, r1
LDR sp, =IRQ_STACK_TOP
MRS r0, cpsr
BIC r1, r0, #0x1F
ORR r1, r1, #ARM_MODE_BIT_FIQ
MSR cpsr, r1
LDR sp, =FIQ_STACK_TOP
MRS r0, cpsr
BIC r1, r0, #0x1F
ORR r1, r1, #ARM_MODE_BIT_ABT
MSR cpsr, r1
LDR sp, =ABT_STACK_TOP
MRS r0, cpsr
BIC r1, r0, #0x1F
ORR r1, r1, #ARM_MODE_BIT_UND
MSR cpsr, r1
LDR sp, =UND_STACK_TOP
MRS r0, cpsr
BIC r1, r0, #0x1F
ORR r1, r1, #ARM_MODE_BIT_SYS
MSR cpsr, r1
LDR sp, =USRSYS_STACK_TOP
dummy_handler:
B .
.end
어셈블리를 제대로 공부하지 않아서
여기서 사용되는 명령어들을 좀 찾아봤다.
ARM 어셈블리 일부 문법
- 상수는 #이 들어감 ( LDR에는 대신 = 사용)
니모닉 |
설명 |
예시 |
비고 |
LDR |
레지스터 로드 명령 |
LDR sp, =USRSYS_STACK_TOP |
|
ORR |
논리 OR |
ORR r1, r1, #ARM_MODE_BIT_SYS |
|
MSR |
레지스터에서 PSR로 이동 |
MSR cpsr, r1 |
|
MRS |
PSR에서 레지스터로 이동 |
MRS r0, cpsr |
|
BIC |
해당되는 비트 클리어 |
BIC r1, r0, #0x1F |
R1 = R0 AND ~ 0x10 |
B |
분기 |
|
|
ref
https://0xsaika.tistory.com/5
ARM assembly 문법정리
니모닉 간단한 설명 페이지 아키텍처[1] ADC , ADD carry 포함 더하기, 더하기 ADD, SUB, RSB, ADC, SBC 및 RSC 모두 ADR 프로그램 또는 레지스터 기준 주소 로드(짧은 범위) ADR 모두 ADRL 의사 명령어 프로그..
0xsaika.tistory.com
https://kyuhyuk.kr/article/raspberry-pi/2019/05/15/ARM-Assembly
ARM Assembly 기초
ARM CPU의 기본 구성 R0 ~ R14 총 15개의 범용 레지스터를 가지고 있습니다. 범용 레지스터 R13 는 특수 레지스터 SP 로 사용됩니다. SP 는 C언어 사용시 스택의 주소를 저장하는 레지스터입니다. 범용
kyuhyuk.kr
http://trace32.com/wiki/index.php/AND,_ORR,_EOR,_BIC_and_ORN
AND, ORR, EOR, BIC and ORN - TRACE32
이번에는 비트연산 명령어인 AND, ORR, EOR, BIC, ORN 명령에 대해 알아보겠습니다. AND : Logical AND 두 개의 비트가 모두 "1"이면 "1", 하나라도 "0"이면 "0"이 나오는 AND연산을 수행합니다. 아래의 "ANDS R6,R6,
trace32.com
동작 모드 전환하는 부분을 보면
1. MSR r0, cpsr
r0에다가 cpsr 저장
2. BIC r1, r0, #0x1F
r1 = r0 AND ~0x1F
3. ORR r1, r1 #ARM_MODE_BIT_UND
r1과 #ARM_MODE_BIT_UND 그러니까 0x1B와 or 연산
4. MSR cpsr, r1
r1의 값을 cpsr에 대입
5. LDR sp, =UND_STACK_TOP
스택 포인터 값을 UND_STACK_TOP (=UND_STACK_START + UND_STACK_SIZE - 4= 0x007F 0000)으로 변경
=> 대강 cpsr의 모드 비트를 UND 모드로 바꾸고 스택 포인터도 UND 스택 탑으로 바꾸는 내용
MRS r0, cpsr
BIC r1, r0, #0x1F
ORR r1, r1, #ARM_MODE_BIT_UND
MSR cpsr, r1
LDR sp, =UND_STACK_TOP
정리하면 위 어셈블리 코드의 리셋 핸들러 부분은
모든 모드를 돌아가면서 스택 탑 주소를 sp에 넣어보는 동작인듯 하다.
ARCH=armv7-a
MCPU=cortex-a8
CC=arm-none-eabi-gcc
AS=arm-none-eabi-as
LD=arm-none-eabi-ld
OC=arm-none-eabi-objcopy
LINKER_SCRIPT=./navilos.ld
ASM_SRCS=$(wildcard boot/*.S)
ASM_OBJS=$(patsubst boot/%.S, build/%.o, $(ASM_SRCS))
INC_DIRS=include
navilos=build/navilos.axf
navilos_bin=build/navilos.bin
.PHONY: all clean run debug gdb
all:$(navilos)
clean:
@rm -fr build
run: $(navilos)
qemu-system-arm -M realview-pb-a8 -kernel $(navilos)
debug: $(navilos)
qemu-system-arm -M realview-pb-a8 -kernel $(navilos) -S -gdb tcp::1234,ipv4
gdb:
gdb-multiarch
$(navilos):$(ASM_OBJS) $(LINKER_SCRIPT)
$(LD) -n -T $(LINKER_SCRIPT) -o $(navilos) $(ASM_OBJS)
$(OC) -O binary $(navilos) $(navilos_bin)
build/%.o: boot/%.S
mkdir -p $(shell dirname $@)
$(CC) -march=$(ARCH) -mcpu=$(MCPU) -I $(INC_DIRS) -c -g -o $@ $<
djc4223@ubuntu:~/github/navilos$
헤더 파일 추가하는 내용이랑
as 대신 gcc, $(CC)
그리고 실행 파일이 아닌 오브젝트 파일이 나와야 링커에 전달할 수 있으니 -c
그런데 AS을 쓸때랑 달리 mcpu랑 march랑 충돌한다고 워닝이 발생해서
march 부분을 빼서 빌드시켰더니 이상이 없다.
디버그 할수 있도록 돌리고
디버거 연결하고
navilos.axf 심볼 로드하고
list를 치면 어셈블리 코드가 보인다.
가장 먼저
vector_start
reset_handler_addr
통해서
MRS r0, cpsr 부터 쭉쭉 가고
36번줄 명령어 실행 후
info register를 치면
지금 SVC_STACK_TOP이 sp에 들어가 있고
지금 그 sp 값이 0x3ffffc인데 이게 맞는건지 잘 모르겠다.
다시 매모리맵을 정리하면 이렇게 된다.
가장 먼저 SVC_STACK_TOP을 찾았는데
SVC 스택은 0x0030 0000 ~ 0x003F FFFF의 범위를 갖는다.
스택은 위에서부터 내려오니 스택포인터는 0x003F FFFF일것이나
다른 스택과 패딩 4바이트 정도 줘서 -4 한 위치인 0x003F FFFC가 SVC_STACK_TOP이 된다.
그다음 로드한 IRQ_STACK_TOP도
0x004f ffff에서 -4한 0x004f fffc 위 그림에다가는 잘못 적었다.
FIQ 모드, 마지막에 오는 USRSYS 모드도
정상적으로 잡힌다.
드디여 메인함수에 진입할 차례
boot/Main.c를 작성하는데
책에서는 stdint.h를 include 디렉토리에다가 넣어두고 ""로 가져오는걸로 적혀있긴한데
실제 코드 내용은 없어서 대신 <>를 썻더니 아무이상없이 가져와지고
내용은 1024 * 1024 * 100한 주소에다가
sizeof(long)한 값을 넣는 내용이다.
32비트 ARM 머신이므로 int가 2바이트 long은 4바이트니 4가 들어가고,
1024 * 1024 * 100은 100mb 지만 계산결과인 104,857,600를
16진수로 바꾸면 0x640 0000이 된다.
0x0640 0000 번지에 4를 입력하는 코드가 되고
#include <stdint.h>
void main(void)
{
uint32_t* dummyAddr = (uint32_t*)(1024 * 1024 * 100);
*dummyAddr = sizeof(long);
}
boot/Entry.S는
대부분 그대로이지만 리셋 핸들러 마지막에
BL main이 추가되어 있어서
main 라벨로 넘어가게 된다. 아래 링크를 보니
BL이 B 처럼 분기 하되 분기 끝나고 돌아오는 내용까지 포함된거라고 한다
그 뒤에는 dummy handler로 무한루프
https://blog.daum.net/tlos6733/132
[Branch] 01 - ARM Branch 명령어 분석
작성자 : 박진범 메일 : jinb.park7@gmail.com :) Goal - b, bl, blx instruction 의 opcode 를 확인한다. 바이너리 파일에서 b, bl, blx instruction 을 해석할 수 있게 한다. - bl, blx 의 차이점을 안다. - bl..
blog.daum.net
#include "ARMv7AR.h"
#include "MemoryMap.h"
.text
.code 32
.global vector_start
.global vector_end
vector_start:
LDR PC, reset_handler_addr
LDR PC, undef_handler_addr
LDR PC, svc_handler_addr
LDR PC, pftch_abt_handler_addr
LDR PC, data_abt_handler_addr
B .
LDR PC, irq_handler_addr
LDR PC, fiq_handler_addr
reset_handler_addr: .word reset_handler
undef_handler_addr: .word dummy_handler
svc_handler_addr: .word dummy_handler
pftch_abt_handler_addr: .word dummy_handler
data_abt_handler_addr: .word dummy_handler
irq_handler_addr: .word dummy_handler
fiq_handler_addr: .word dummy_handler
vector_end:
reset_handler:
MRS r0, cpsr
BIC r1, r0, #0x1F
ORR r1, r1, #ARM_MODE_BIT_SVC
MSR cpsr, r1
LDR sp, =SVC_STACK_TOP
MRS r0, cpsr
BIC r1, r0, #0x1F
ORR r1, r1, #ARM_MODE_BIT_IRQ
MSR cpsr, r1
LDR sp, =IRQ_STACK_TOP
MRS r0, cpsr
BIC r1, r0, #0x1F
ORR r1, r1, #ARM_MODE_BIT_FIQ
MSR cpsr, r1
LDR sp, =FIQ_STACK_TOP
MRS r0, cpsr
BIC r1, r0, #0x1F
ORR r1, r1, #ARM_MODE_BIT_ABT
MSR cpsr, r1
LDR sp, =ABT_STACK_TOP
MRS r0, cpsr
BIC r1, r0, #0x1F
ORR r1, r1, #ARM_MODE_BIT_UND
MSR cpsr, r1
LDR sp, =UND_STACK_TOP
MRS r0, cpsr
BIC r1, r0, #0x1F
ORR r1, r1, #ARM_MODE_BIT_SYS
MSR cpsr, r1
LDR sp, =USRSYS_STACK_TOP
BL main
dummy_handler:
B .
.end
Main.c의 메인 함수로 점프하게 되는데
컴파일러는 C언어 함수명을 자동으로 전역 심벌로 만들어서
Entry.S의 BL main을 통해 메인 함수로 넘어간다고 한다.
C 언어 파일도 컴파일, 링킹하는 내용도 추가하고
ARCH=armv7-a
MCPU=cortex-a8
CC=arm-none-eabi-gcc
AS=arm-none-eabi-as
LD=arm-none-eabi-ld
OC=arm-none-eabi-objcopy
LINKER_SCRIPT=./navilos.ld
MAP_FILE=build/navilos.map
ASM_SRCS=$(wildcard boot/*.S)
ASM_OBJS=$(patsubst boot/%.S, build/%.os, $(ASM_SRCS))
C_SRCS=$(wildcard boot/*.c)
C_OBJS=$(patsubst boot/%.c, build/%.o, $(C_SRCS))
INC_DIRS=include
navilos=build/navilos.axf
navilos_bin=build/navilos.bin
.PHONY: all clean run debug gdb
all:$(navilos)
clean:
@rm -fr build
run: $(navilos)
qemu-system-arm -M realview-pb-a8 -kernel $(navilos)
debug: $(navilos)
qemu-system-arm -M realview-pb-a8 -kernel $(navilos) -S -gdb tcp::1234,ipv4
gdb:
gdb-multiarch
$(navilos):$(ASM_OBJS) $(C_OBJS) $(LINKER_SCRIPT)
$(LD) -n -T $(LINKER_SCRIPT) -o $(navilos) $(ASM_OBJS) $(C_OBJS) -Map=$(MAP_FILE)
$(OC) -O binary $(navilos) $(navilos_bin)
build/%.os: $(ASM_SRCS)
mkdir -p $(shell dirname $@)
$(CC) -mcpu=$(MCPU) -I $(INC_DIRS) -c -g -o $@ $<
build/%.o: $(C_SRCS)
mkdir -p $(shell dirname $@)
$(CC) -mcpu=$(MCPU) -I $(INC_DIRS) -c -g -o $@ $<
디버거로 확인해보니
메인함수에 작성한대로 0x0640 0000번지에 4라는 값이 들어가 있는걸 확인할 수 있다.
메인함수 진입 끝