오후 7:09 2006-04-27
kernel 영역으로 올리기
조경민 bro@shinbiro.com
http://neri.cafe24.com
============================================================

;=====================================================
; boot test
; by bro, bro@shinbiro.com 오전 10:23 2006-04-26

;=====================================================

        ; main.c의 커널 main 함수 심볼 쓰기 위해서
        IMPORT main
        IMPORT        ||Image$$KERNEL$$ZI$$Limit||
        EXPORT        __user_initial_stackheap
        EXPORT        vector

        ; KERNEL region 복사를 위해 심볼 사용
        IMPORT        ||Image$$KERNEL$$Base||
        IMPORT        ||Image$$KERNEL$$Length||
        IMPORT        ||Load$$KERNEL$$Base||

        AREA boot, CODE, READONLY
        ENTRY
vector                                ; 인터럽트 벡터 테이블
        b        reset                 ; 주소 0번지에 reset 인터럽트가 오면 바로 reset레이블로 점프
        b        error                 ; undefined_instruction
        b        error                 ; software_interrupt
        b        error                 ; prefetch_abort
        b        error                 ; data_abort
        b        error                 ; not_used
        b        error                 ; IRQ
        b        error                 ; FIQ

error
        b        error

reset
        ; 현재 모드에서 IRQ|FIQ를 끈다.
        mrs     r0, CPSR
        bic     r0, r0, #0x1f
        orr     r0, r0, #(0x13 | 0xc0)        ; 0x13(0001 0011) | 0xC0 (1101 0000) = 0xD3(1101 0011)
        msr     CPSR_cxsf, r0
        ; 스택 초기화        0x9000
        mov        sp, #0x00080000

        ; 커널 복사 (memory.map에서 커널영역은 0x10000에서 하겠다고 했으니 복사해야함;)
        bl copy_kernel

        ldr        pc, =main
        b        end

__user_initial_stackheap
        LDR        r0, =||Image$$KERNEL$$ZI$$Limit||
        MOV        pc, lr
copy_kernel
        ldr        r0, =||Load$$KERNEL$$Base||        ; source (FLASH ROM같은 영역)
        ldr        r1, =||Image$$KERNEL$$Base||        ; destination (SDRAM 같은 영역)
        ldr        r2, =||Image$$KERNEL$$Length||        ; kernel length
        mov        r3, #0                                ; counter
loop
        ldr        r4, [r0], #4
        str        r4, [r1], #4
        add        r3, r3, #4
        cmp        r3, r2
        bne        loop
        mov        pc, lr
end
        END
-----------------------------------------------------------------------------
main.c

void main()
{
        int a;
        for( a = 0; a < 10; a = a + 1);
}
-----------------------------------------------------------------------------
memory.map

MEMORY_MAP 0x0
{
        BOOT 0x0
        {
                boot.o (boot, +First)
                * (+RO, +RW, +ZI)
        }
        KERNEL 0x10000
        {
                main.o (+RO, +RW, +ZI)
        }
}
-----------------------------------------------------------------------------
armasm boot.s -G -o boot.o
armcc -c main.c --arm --debug -g -o main.o
armlink boot.o main.o --map --scatter memory.map --entry vector -o kernel.axf

scatter 파일
여러 object파일을 링크할때 각 object 파일이 실제 실행될때 어디로 로드될지 결정할 수 있다.

region이름 시작주소, [크기]
{
        서브region이름 시작주소, [크기]
        {
                object.o (섹션명,+First)  ;처음에 둔다는 의미
        }

        서브region이름 시작주소, [크기]
        {
                main.o (+RO, +RW) ; ReadOnly와 ReadWrite 섹션만 둔다고 설정가능
                * (+RO, +RW, +ZI) ; main.o의 ZI (Zero Intilaize)와 다른 object들의 모든 섹션이 놓임
        }
}

RO는 보통 실행코드 영역또는 const상수(text), RW는 데이터 영역, ZI는 0으로 초기화되는 변수(static변수)

처음에는 region으로 나누는 것을 시도했는데 잘 안되었다. 이유는 메모리 맵을 잡아주면
해당 main.o 모듈의 0x10000번지에서 시작되는것까지는 맞지만, kernel.axf를 AXD Debugger로
로드 하면 아직 0x13C 정도 번지에 boot.o 모듈의 다음 주소 공간에 로드되어 있을 뿐이었다.;

아래의 scatterload에 관한 ARM Limited의 문서를 읽어보면
http://www.arm.com/pdfs/DAI0048A_scatterload.pdf
armlink에 의해서 boot.s에서 사용 가능한 링킹 심볼 정보 몇개를 사용할 수 있다.
각 region의 로드 된 직후의 Base 주소, Length, 그리고 실제 실행되어야할 Base와 Length이다.

-----------------------------------------------------------------------
참고:위의 pdf문서에서 Initialization Code for Scatter Loading를 보면
커널 영역을 복사하는 것 외에도 ZI영역을 실제로 직접 0으로 초기화하는 코드도 필요하다.
일단 간단하게 하기 위해서 그 과정은 생략하였다.
아래 예에서는 RAM 이라는 이름의 region이 zi 초기화 되는 코드이다.
region을 둔 이유도 사실 0번지 ROM에 로드된 후 RAM이나 다른 주소 영역에 복사하여 실행하게
하기 위해서 존재한다.
        ; r0 contains the load address of the region RAM
        LDR r0, = |Load$$RAM$$Base|
        ; r1 contains the execution address of the region RAM
        LDR r1, = |Image$$RAM$$Base|
        CMP r0, r1 ; check source & destination are different
        BEQ do_zi_init ; if not, do not copy this region

do_zi_init
        ; r1 contains the execution address of the region
        LDR r1, = |Image$$RAM$$ZI$$Base|
        ; r2 contains the address of the word beyond the end of this
        ; execution region
        MOV r2, r1
        LDR r4, = |Image$$RAM$$ZI$$length|
        ADD r2, r2, r4
        ; r3 contains the value to be used to initialise area
        MOV r3, #0
        BL zi_init ; call subroutine zi_init
-----------------------------------------------------------------------
예를 들어
MEMORY_MAP 0x0
{
        BOOT 0x0
        {
                boot.o (boot, +First)
                * (+RO, +RW, +ZI)
        }
        KERNEL 0x10000
        {
                main.o (+RO, +RW, +ZI)
        }
}

라면 각 region 마다 아래와 같은 심볼을 사용할 수 있다.

||Image$$BOOT$$Base|| : BOOT region의 실행 가능한 시작 주소
||Load$$BOOT$$Base|| : BOOT region이 로드 된 직후의 시작 주소
||Image$$BOOT$$Length|| : BOOT region의 길이

||Image$$KERNEL$$Base|| : KERNEL region의 실행 가능한 시작 주소
||Image$$KERNEL$$Length|| : KERNEL region 길이
||Load$$KERNEL$$Base|| : KERNEL region의 로드 된 직후의 시작 주소

참고: * (+RO, +RW, +ZI) 를 KERNEL region에 두면 root region이 아니라서 내부 라이브러리
오브젝 파일을 그 region에 둘 수 없다는 에러가 발생된다.
root region은 로드 후 바로 실행 가능한 region이고 non root region은 로드 후 복사를 해야
실행 가능한 region을 의미한다.
http://www.arm.com/support/faqdev/1462.html
만일 굳이 KERNEL region에 두고 싶다면, root region에 있어야하는 내부 라이브러리 모듈만
BOOT region에 두면 된다.
MEMORY_MAP 0x0
{
        BOOT 0x0
        {
                boot.o (boot, +First)
                * (InRoot$$Sections)
        }
        KERNEL 0x10000
        {
                main.o (+RO, +RW, +ZI)
                * (+RO, +RW, +ZI)
        }
}

이 중 BOOT region은 로드되었을 때의 시작 주소와, 실행 가능한 시작 주소가 이미 일치하기
때문에 할일이 없다. 하지만 KERNEL region은 로드되었을 때의 ||Load$$KERNEL$$Base||는
대충 0x13C 정도 뿐이라, 실제 실행 가능한 주소 ||Image$$KERNEL$$Base||가 가리키는 0x10000으로
로드된 KERNEL region을 복사해야한다.

따라서 boot.s에서는 아래와 같이 커널을 복사한다.

        ; 커널 복사 (memory.map에서 커널영역은 0x10000에서 하겠다고 했으니 복사해야함;)
        bl copy_kernel

        ldr        pc, =main
        b        end

copy_kernel
        ldr        r0, =||Load$$KERNEL$$Base||        ; source (FLASH ROM같은 영역)
        ldr        r1, =||Image$$KERNEL$$Base||        ; destination (SDRAM 같은 영역)
        ldr        r2, =||Image$$KERNEL$$Length||        ; kernel length
        mov        r3, #0                                ; counter
loop
        ldr        r4, [r0], #4                        ; 4바이트를 읽어서 r0는 읽히고 4바이트 전진
        str        r4, [r1], #4                        ; 4바이트 쓴다.
        add        r3, r3, #4                        ; 카운터를 4바이트씩 증가
        cmp        r3, r2                                ; 커널 길이만큼 복사했는지 검사
        bne        loop                                ; 같지 않다면 아직 덜 복사되었음 계속 복사
        mov        pc, lr                                ; 리턴

그런데 먼저 이런 링킹 심볼 정보를 사용하려면 외부 심볼이기 때문에 IMPORT로 선언해야한다.
        ; KERNEL region 복사를 위해 심볼 사용
        IMPORT        ||Image$$KERNEL$$Base||
        IMPORT        ||Image$$KERNEL$$Length||
        IMPORT        ||Load$$KERNEL$$Base||

'KB > embbeded sw' 카테고리의 다른 글

arm-6: TRACE32 사용법 익히기  (0) 2006.05.03
arm-5 : task switch 하기  (0) 2006.05.02
arm-3 : c코드로 점프~  (0) 2006.04.26
arm-2: boot 테스트  (0) 2006.04.26
arm-1: ADS로 ARM 공부 add  (0) 2006.04.25

+ Recent posts