Chapter 3,4: Reset Vector

1231·2024년 5월 26일

임베디드 OS 개발

목록 보기
2/8

Reset vector 는 메모리 0에 위치하고 있다.
Reset Exception handler 에서 가장 먼저 해야할 일은 메모리 맵을 설정해주는 작업이다.
동작 모드 별 할당된 스택 주소를 각 동작 모드 SP 에 설정해야한다.
그 후, C 언어 main() 함수로 진입할 것 이다.

USR,SYS: 0x0010000 ~ 0x002FFFFF (2MB)
SVC: 0x00300000 ~ 0x003FFFFF (1MB)
IRQ: 0x00400000 ~ 0x004FFFFF (1MB)
FIQ: 0x00500000 ~ 0x005FFFFF (1MB)
ABT: 0x00600000 ~ 0x006FFFFF (1MB)
UND: 0x00700000 ~ 0x007FFFFF (1MB)

나머지 태스트 스택은 64MB, 전역 변수 영역은 1MB, 동적할당 영역은 55MB 을 할당할 것 이다.
이러한 메모리 주소를 MemoryMap.h 에 정의한다.
include/MemoryMap.h

  1 #define INST_ADDR_START 0
  2 #define USRSYS_STACK_START      0x00100000 //User & Sys mode
  3 #define SVC_STACK_START 0x00300000 //Supervisor mode
  4 #define IRQ_STACK_START 0x00400000 //Interrupt Request mode
  5 #define FIQ_STACK_START 0x00500000 //Fast Interrupt Request mode
  6 #define ABT_STACK_START 0x00600000 //Abort mode
  7 #define UND_STACK_START 0x00700000 //Undefined mode
  8 #define TASK_STACK_START        0x00800000 //task stack
  9 #define GLOBAL_STACK_START      0x04800000 //glboal stack
 10 #define DALLOC_STACK_START      0x04900000 //Dynamically allocated stack
 11
 12 #define INST_MEM_SIZE           (USRSYS_STACK_START - INST_ADDR_START)
 13 #define USRSYS_STACK_SIZE       (SVC_STACK_START - USRSYS_STACK_START)
 14 #define SVC_STACK_SIZE  (IRQ_STACK_START - SVC_STACK_START)
 15 #define IRQ_STACK_SIZE  (FIQ_STACK_START - IRQ_STACK_START)
 16 #define FIQ_STACK_SIZE  (ABT_STACK_START - FIQ_STACK_START)
 17 #define ABT_STACK_SIZE  (UND_STACK_START - ABT_STACK_START)
 18 #define UND_STACK_SIZE  (TASK_STACK_START - UND_STACK_START)
 19 #define TASK_STACK_SIZE (GLOBAL_STACK_START - TASK_STACK_START)
 20 #define GLOBAL_STACK_SIZE       (DALLOC_STACK_START - GLOBAL_STACK_START)
 21 #define DALLOC_STACK_SIZE       (55 * 1024 * 1024) //55 MB
 22
 23 #define USRSYS_STACK_TOP        (USRSYS_STACK_START + USRSYS_STACK_SIZE - 4)
 24 #define SVC_STACK_TOP           (SVC_STACK_START + SVC_STACK_SIZE - 4)
 25 #define IRQ_STACK_TOP           (IRQ_STACK_START + IRQ_STACK_SIZE - 4)
 26 #define FIQ_STACK_TOP           (FIQ_STACK_START + FIQ_STACK_SIZE - 4)
 27 #define ABT_STACK_TOP           (ABT_STACK_START + ABT_STACK_SIZE - 4)
 28 #define UND_STACK_TOP           (UND_STACK_START + UND_STACK_SIZE - 4

또한 cpsr 을 수정하여 동작모드를 변경하기 위한 동작 모드 값을 정의하자.
include/ARMv7AR.h

  1 //CPSR; Current Program Status Register "M" bit value
  2 #define ARM_MODE_BIT_USR        0x10
  3 #define ARM_MODE_BIT_FIQ        0x11
  4 #define ARM_MODE_BIT_IRQ        0x12
  5 #define ARM_MODE_BIT_SVC        0x13
  6 #define ARM_MODE_BIT_ABT        0x17
  7 #define ARM_MODE_BIT_UND        0x1B
  8 #define ARM_MODE_BIT_USRSYS     0x1F
  9 #define ARM_MODE_BIT_MON        0x16

그다음 벡터 테이블을 작성한다.

vector_start 은 각 핸들러의 주소를 Program Counter 에 Write 함.
리셋 핸들러는 cpsr 의 m 비트값을 동작 모드 값으로 변경 후, SP 레지스터에 Top of the Stack 을 Write한다. 이 과정을 모든 동작 모드에 반복시킨다.
BL instruction 은 Branch operation 수행하기전에 R14(LR) 을 BL 바로 다음으로 설정하여 Return addr 을 저장한다. BL 을 통해 C 의 main 함수로 branch 함.
컴파일러는 C 언어 함수 이름을 링커가 자동으로 접근할 수 있는 전역 심벌로 만들기 때문에 이 어셈블리 코드에서 점프 가능한 것이다.

  1 #include "MemoryMap.h"
  2 #include "ARMv7AR.h"
  3
  4 .text
  5   .code 32
  6
  7   .global vector_start
  8   .global vector_end
  9
 10   vector_start:
 11         LDR PC, reset_handler_addr
 12         LDR PC, undef_handler_addr
 13         LDR PC, svc_handler_addr
 14         LDR PC, pftch_abt_handler_addr
 15         LDR PC, data_abt_handler_addr
 16         B . @infinite loop
 17         LDR PC, irq_handler_addr
 18         LDR PC, fiq_handler_addr
 19
 20   reset_handler_addr:           .word reset_handler
 21   undef_handler_addr:           .word dummy_handler
 22   svc_handler_addr:             .word dummy_handler
 23   pftch_abt_handler_addr:       .word dummy_handler
 24   data_abt_handler_addr:        .word dummy_handler
 25   irq_handler_addr:             .word dummy_handler
 26   fiq_handler_addr:             .word dummy_handler
 27
 28   vector_end:
 29
 30   reset_handler:
 31         @SVC mode
 32         MRS     r0, cpsr
 33         BIC     r1, r0, #0x1f
 34         ORR     r1, r1, #ARM_MODE_BIT_SVC
 35         MSR     cpsr, r1
 36         LDR     sp, =SVC_STACK_TOP
  .....
 73         BL       main
 74
 75   dummy_handler:
 76         B .
 77
 78 .end
 79

boot/Main.c
현재는 아무 의미없는 더미 코드이다.
즉, 정상적으로 main 함수로 점프했다면 100MB 메모리 주소에 "4" 라는 것이 작성되어 있어야한다.

  1 #include "stdint.h"
  2
  3 void main(void) {
  4   uint32_t *dummyAddr = (uint32_t*)(1024*1024*100);
  5   *dummyAddr = sizeof(long);
  6 }

다음은 링커 스크립트를 작성해야함.
펌웨어가 동작하는 하드웨어에 맞춰서 펌웨어의 섹션 배치를 세세하게 조정하는 일이 잦다. 그래서 링커 스크립터로 링커의 동작을 제어하여 원하는 형태의 ELF 파일을 생성하도록 함.

navilos.ld

  1 ENTRY(vector_start) //First executable instruction in the output 
  2 SECTIONS //input - output 섹션 매핑 설정 
  3 {
  4   . = 0x0; //first section will be placed in 0x0
  5
  6   .text : //text 섹션 output 매핑 설정 
  7   {
  8     *(vector_start) //0x0에 리셋 벡터가 위치해야 하므로 vector_start 심벌을 먼저 놔둠.
  9     *(.text .rodata)
 10   }
 11
 12   .data :
 13   {
 14     *(.data)
 15   }
 16
 17   .bss :
 18   {
 19     *(.bss)
 20   }
 21 }

빌드 자동화를 위해서 Makefile 을 작성하였음.

  1 ARCH = armv7-a
  2 MCPU = cortex-a8
  3
  4 CC = arm-none-eabi-gcc
  5 AS = arm-none-eabi-as
  6 LD = arm-none-eabi-ld
  7 OC = arm-none-eabi-objcopy
  8
  9 LINKER_SCRIPT = ./navilos.ld
 10 MAP_FILE = build/navilos.map
 11
 12 ASM_SRCS = $(wildcard boot/*.S)
 13 ASM_OBJS = $(patsubst boot/%.S, build/%.o, $(ASM_SRCS))
 14
 15 C_SRCS = $(wildcard boot/*.c)
 16 C_OBJS = $(patsubst boot/%.c, build/%.o, $(C_SRCS))
 17
 18 INC_DIRS = include
 19
 20 navilos = build/navilos.axf
 21 navilos_bin = build/navilos.bin
 22
 23 .PHONY: all clean run debug gdb
 24
 25 all: $(navilos)
 26
 27 clean:
 28         @rm -rf build
 29
 30 run: $(navilos)
 31         qemu-system-arm -M realview-pb-a8 -kernel $(navilos) -nographic
 32
 33 debug: $(navilos)
 34         qemu-system-arm -M realview-pb-a8 -kernel $(navilos) -S -gdb tcp::1234,ipv4 -nographic
 35
 36 gdb:
 37         arm-none-eabi-gdb
 38
 39 $(navilos): $(ASM_OBJS) $(C_OBJS) $(LINKER_SCRIPT)
 40         $(LD) -n -T $(LINKER_SCRIPT) -o $(navilos) $(ASM_OBJS) $(C_OBJS) -Map=$(MAP_FILE)
 41         $(OC) -O binary $(navilos) $(navilos_bin)
 42
 43 build/%.o: boot/%.S
 44         mkdir -p $(shell dirname $@)
 45         $(CC) -march=$(ARCH) -mcpu=$(MCPU) -I $(INC_DIRS) -c -g -o $@ $<
 46
 47 build/%.o: boot/%.c
 48         mkdir -p $(shell dirname $@)
 49         $(CC) -march=$(ARCH) -mcpu=$(MCPU) -I $(INC_DIRS) -c -g -o $@ $<

여기까지 Reset vector 을 읽어서 각 동작 별로 Stack 을 할당해주고, C 코드의 main 함수로 넘어오는 과정이다.

0개의 댓글