안녕하세요~
6/29 일날 스터디에서 ARM 어셈블러 코드를 보면서 실제로 시뮬레이션 해보는 방법에대해 이야기가 나왔었는데요
저는 IAR 컴파일러를 이용해서 한번 어셈코드를 돌려봤습니다. 확실히 코드만 보면서 하는것보다 훨씬 이해가 빠른 것 같습니다.
앞으로 어셈블리 코드 분석을 할때 일일히 계산하지 말고 코드의 일부분을 넣고 돌려보면서 확인하면
좀 더 원할한 스터디가 될것 같습니다. 분석한 내용을 정리해서 올려봤습니다.
근데 RAM 영역을 설정할때 아래 구문같이 왜 18 Bit 를 쉬프트하는 건지 아직도 모르겠습니다. 아시는 분 있으면
가르켜 주세요ㅜㅜ
이해 안되는 구문!!(__setup_mmu: 구문중)
mov r9,r0,lsr #18
mov r9,r9,lsl #18
http://supp.iar.com/Download/SW/?item=EWARM-EVAL
IAR Embedded Workbench for ARM (평가판)받으셔서 설치하시면 됩니다.
설치하고 평가판 License 등록시에 개인 정보등록하실때 두가지를 선택하실 수 있는데요
날짜 제한, 용량 제한(32K byte) 중 용량 제한 선택하시면 됩니다.
설치후 실행사신다음 file >> new >> workspace 만드신다음 project >> create new project 하시구 asm 파일 선택하시구
ok 하신다음 프로젝트 파일 저장해주시면 그림과 같이 자동으로 asm.s 파일을 생성해주는데요
다음 project >> option 들어가셔서 general option 에서 core 를 A-15 선택하신다음 , debugger 에서 드라이버가 simulator 인지 확인해주시면 됩니다. 메이크 하시구 디버깅 버튼 눌러주셔서 디버깅 화면을 띄운 다음 view >> register 선택하시면 아래 그림과
같이 레지스터 값도 같이 보실 수 있습니다. (R0 ~ R14, R15(pc) , CPSR, SPSR, CP15,14 등)
제일 왼쪽은 어셈블리 코드, 중앙은 Disassembly 창 , 가장 오른쪽은 레지스터 창입니다.
head.S 에서 setup_mmu 일부분만 넣어서 돌려보는식으로 돌려보았습니다. 자동생성된 asm.s 메인 밑에 아래와 같이 코드를
추가했습니다 . gas 문법이랑 약간 차이도 있구 아직 어셈블리 문법이 익숙치 않아서 에러가 많이 나서 시간 많이 걸렸어요 ㅜㅜ
NAME main
PUBLIC __iar_program_start
SECTION .intvec : CODE (2) CODE32
__iar_program_start B main
SECTION .text : CODE (2) CODE32
main NOP ldr r4,R4_init ; 실제 커널 소스코드로 진입하기전에 mov r6,#0x0c ; 필요한 레지스터값 초기화
; 시작 (실제 커널 어셈블리 코드) sub r3,r4,#16384 bic r3,r3,#0xff bic r3,r3,#0x3f00 ; (1) mov r0,r3 mov r9,r0,lsr #18 mov r9,r9,lsl #18 add r10,r9, #0x10000000 mov r1,#0x12 orr r1,r1, #3 << 10 add r2,r3, #16384 ;(2) label1: cmp r1,r9 cmphs r10,r1 bic r1, r1, #0x1c orrlo r1,r1,#0x10 orrhs r1,r1,r6 str r1, [r0],#4 add r1,r1,#1048576 teq r0,r2 bne label1 ;(3) ; 여기까지 돌려봤어요
b main DATA R4_init: DC32 0x50008000 END
|
실제 커널 코드 부분돌리기전에 레지스터 R4 에다 zImage 압축이 풀릴 영역주소로 0x50008000을 입력해주고
R6 에는 0x0c(cacheable, bufferable 값설정을 위한 값) 를 넣어주는 코드를 추가했습니다.
(1) 까지 실행후에 레지스터 창을 통해 R3 에 페이지 디렉토리 시작주소 , R4에 zImage 압축이 풀릴 시작주소가
R6에는 0x0c(c=1,b=1)값이 들어간 것을 확인할 수 있었습니다.
==> (1)까지 실행후 레지스터 값
(2)까지 실행하면 레지스터 값은 다음과 같습니다.
다음 label1 으로 표시된 영역을 돌면서 페이지 디렉토리를 초기화 하는 구문 입니다. 첫번째 루프를 돌리면서 레지스터 값의
변화를 확인해 보았습니다.
cmp r1,r9 (실제동작 r9 - r1) 결과가 음수이므로 CPSR 레지스터의 N=0 에서 N=1로 변경되었습니다.
다음 cmphs 는 CPSR 의 C(carry) 비트가 1이여야 실행되므로 cmphs r10,r1을 실행하지 않고
다음 bic r1, r1, #0x1c을 실행하여 R1 의 값이 0x00000c12 ==> 0x00000c02 변경
orrlo 는 C == 0 , orrhs 는 C == 1일때 실행되므로 orrlo r1,r1, #0x10 만 실행하여 R1 = 0x00000c12
다음 str r1, [r0],#4 를 수행 R1의 값을 R0이 가르키는 주소에다 넣어주고 R0 + 4(주소값을 4byte만큼 증가)
다음 teq r0,r2 에서 teq 명령은 두 레지스터의 값이 같다면 Z=1, 다르다면 Z=0 이므로 명령 수행후 Z=1
따라서 Z=0(not equal) 이므로 bne label1 명령이 실행되어 루프의 시작지점으로..
반복수행하다 R1의 값이 R9 ~ R10 (0x50000000 ~ 0x60000000) 사이에 있게되면 cmphs r10,r1 이 실행되고
r10 > r1 이므로 CPSR의 C==1 로 되고 아래의 orrhs r1,r1,r6가 수행되어 MMU 섹션테이블 C=1,B=1이 설정되도록 하는 것 같습니다.
댓글 7
-
리누즈박
2013.07.02 17:59
-
K
2013.07.02 18:38
허정훈님. 멋지십니다...^^* -
HyunGyu
2013.07.02 20:45
이런게 있다니!!!
감사합니다.
정리도 멋지십니다!! -
구본규
2013.07.03 00:50
좋은 글 고맙습니다. GUI라 사용하기에 더 편리하게 보이네요.
__setup_mmu 에 넘어올 때 ldr r4, =zreladdr로 압축 해제된 커널이 위치할 주소가 지정됩니다.
아키텍쳐마다 arch/arm/mach-XXX/Makefile.boot에 지정합니다.
제가 분석한 소스 기준으로,
다른 녀석들은 0x???08000 정도로 끝나는데 유독 clps711x라는 녀석만 0xc0028000로 끝나네요.
이것까지 고려해서 램의 시작위치를 만들 때 하위 18비트를 날려주는 것입니다.
이후 코드는 그곳에서부터 256MB를 램이라고 가정하고 page table의 속성을 달리 지정해 주고 있네요.
-
이동훈
2013.07.03 17:07
ㅋㅋ좋은 글 감사합니다!
-
jw^.^
2013.07.04 21:51
좋은 글 감사합니다~ ㅎㅎ
근데 질문이 하나 있는데요.
"R4에 zImage 압축이 풀릴 시작주소가 R6에는 0x0c(c=1,b=1)값이 들어간 것을 확인할 수 있었습니다."
여기서 c=1,b=1 이게 무엇을 뜻하는 건가요? CPSR의 C플래그를 가리키는 것 같지는 않고... c와 b.. 아무리 생각해도
떠오르는게 없네요 ㅠ
-
K
2013.07.04 23:01
L1 페이지테이블엔트리의 Cacheable, Bufferable 비트를 말합니다.
아래 그림 참고하시면 될 것 같아요.. (Cortex A15에서는 아래 그림보다 좀 더 세분화되었습니다.)
그림출처 : http://forum.falinux.com/zbxe/index.php?document_srl=781395&mid=lecture_tip
.
시물레이션을 한다니~ 멋집니다.
리눅스용도 있나요? 없으면 wine같은걸로 되는지 해봐야겠군요 : )