arm계열 cpu에서 가상주소를 물리주소로 변환하는 모듈을 작성중입니다.
글쓴이: lmk378 / 작성시간: 수, 2011/04/06 - 11:36오전
정확히 말하면 s3c2440 에서 동작하는 user program의 가상주소를 ioctl을 통해
작성한 모듈로 넘겨주면 모듈이 물리주소를 계산해서 다시 user program으로 넘겨주는 방식입니다.
x86에서는 작성했는데 arm에서는 메모리 변관과정이 x86과 완전 틀려 포팅이 안되더군요
작성시 궁금한 점이 몇가지 있어 질문드립니다.
1. virt_to_phys함수가 가상주소를 물리주소로 변환해준다던데 커널의 가상메모리를 말하는건가요?
user program은 program마다 가상주소가 동일하더라도 물리주소가 다르니 안될것 같아서 그렇습니다.
2. arm에도 pte_offset과 pgd_offset 같은 함수가 존재하던데 왜 존재하는지 모르겠습니다.
x86계열에서나 필요한 함수 아닌가요? x86상에서 작성한 모듈을 arm으로 돌리니 컴파일도 잘 되고
실행도 잘됩니다만 변환된 물리주소값이 0xCxxxxxxx 와 같은 식으로 나옵니다. 메모리 맵상
sdram은 0x30000000 ~ 0x40000000 까지 메핑이 되어있는데 말이죠.
아래는 x86상에서도 동작하고 arm에서도 동작하는 코드를 pseudo로 변환한 것입니다.
pgd = pgd_offset(vaddr);
pte = pte_offset(pgd);
paddr = offset + pte_page(pte);
힌트가 될만한 정보가 없을까요?
Forums:
current->mm->mmap 을 조사해서,
current->mm->mmap 을 조사해서, vaddr 속한 vma 를 찾으면, 해당 page 를 찾을 수 있지 않을까요?
밥굶지말자
>1. virt_to_phys함수가 가상주소를
>1. virt_to_phys함수가 가상주소를 물리주소로 변환해준다던데 커널의 가상메모리를 말하는건가요?
>user program은 program마다 가상주소가 동일하더라도 물리주소가 다르니 안될것 같아서 그렇습니다.
virt_to_phys는 커널의 가상주소를 물리주소로 변환해 주는 간단한 매크로 혹은 함수입니다.
물리메모리는 0xC00000000 혹은 0x80000000으로 direct mapping되어 있는 점을 이용한 함수이죠.
>2. arm에도 pte_offset과 pgd_offset 같은 함수가 존재하던데 왜 존재하는지 모르겠습니다.
>x86계열에서나 필요한 함수 아닌가요? x86상에서 작성한 모듈을 arm으로 돌리니 컴파일도 잘 되고
>실행도 잘됩니다만 변환된 물리주소값이 0xCxxxxxxx 와 같은 식으로 나옵니다. 메모리 맵상
>sdram은 0x30000000 ~ 0x40000000 까지 메핑이 되어있는데 말이죠.
커널에 page table walking을 위한 좋은 함수가 이미 커널안에 존재합니다.
follow_page(struct vm_area_struct *vma, unsigned long address, unsigned int flags)
1. task_struct와 virtual address를 이용하여 vma를 구하고
2. follow_page()를 이용하여 물리주소에 해당하는 page를 구함
3. 예외처리..(상위 메모리 일 경우.. 제로 페이지일 경우....)
위와 같이 하시면 제대로된 물리주소를 구하실 수 있습니다.
빠른 답변 감사합니다. 답변을 보니 좀더 궁금한게
빠른 답변 감사합니다.
답변을 보니 좀더 궁금한게 생겼습니다.
1. 커널에서 물리메모리를 0xC0000000에 mapping 한다 하셨는데
그렇다면 0xC0000000 주소는 커널의 가상메모리에 해당하는건가요?
또한 해당 정보를 어디서 찾아몰 수 있을까요?
혹시 head.S의 KERNEL_RAM_VADDR, KERNEL_RAM_PADDR 이것과 관계가 있는지...
2. virt_to_phys 함수가 커널의 가상주소를 물리주소로 변환해주는 함수라 하셨는데
커널의 가상주소만 해당하는 건가요? user영역의 가상주소의 물리주소를 알아낼때는
활용이 불가능 할까요?
>1. 커널에서 물리메모리를 0xC0000000에
>1. 커널에서 물리메모리를 0xC0000000에 mapping 한다 하셨는데
>그렇다면 0xC0000000 주소는 커널의 가상메모리에 해당하는건가요?
>또한 해당 정보를 어디서 찾아몰 수 있을까요?
>혹시 head.S의 KERNEL_RAM_VADDR, KERNEL_RAM_PADDR 이것과 관계가 있는지...
head.S 의 __create_page_tables를 보시면
커널에서 사용할 가상주소를 위해 page table을 만들어 주는 것을 볼 수 있습니다.
page table의 위치 : swapper_pg_dir
1MB 단위의 섹션매핑을 하는 것으로 보이네요.
>2. virt_to_phys 함수가 커널의 가상주소를 물리주소로 변환해주는 함수라 하셨는데
>커널의 가상주소만 해당하는 건가요? user영역의 가상주소의 물리주소를 알아낼때는
>활용이 불가능 할까요?
static inline unsigned long virt_to_phys(volatile const void *address)
{
return (unsigned long)address - PAGE_OFFSET + PHYS_OFFSET;
}
먼저 답변 감사합니다. 1번 질문에 대해 아직 해결이
먼저 답변 감사합니다. 1번 질문에 대해 아직 해결이 안되어 다시 질문드립니다.
제가 궁금했던것은 mmu가 on 되기 전
0xc0008000과 같은 주소에 str 또는 ldr을 수행할경우
어떻게 오류가 나지 않는지 궁금했던것입니다.
실제로 board의 memory map을 보면 0x00000000 번지부터 0x3fffffff 까지가 dram 영역으로 되어있습니다.
0xc0000000 역역은 AHB bus 이구요;;
__create_page_table함수에서 0xc000xxxx를 접근하던데 mmu off 상태에서 이것이 가능한 것인가요?
다음은 __create_page_table 중 일부입니다.
219 __create_page_tables:
220 pgtbl r4 @ page table address
221
222 /*
223 * Clear the 16K level 1 swapper page table
224 */
225 mov r0, r4
226 mov r3, #0
227 add r6, r0, #0x4000
228 1: str r3, [r0], #4
229 str r3, [r0], #4
230 str r3, [r0], #4
231 str r3, [r0], #4
232 teq r0, r6
233 bne 1b
여기서 보면
228 1: str r3, [r0], #4
이부분에서 r0는 맨처음 0xc0004000의 값이 들어가게 됩니다.
즉 page table의 base address인데 mmu off상태에서 어떻게 r3값을 0xc0004000에 저장하는 코드에 이상이 없을 수 있을까요?
간단히 정리하면 mmu off상태에서 0xc000xxxx를 접근해도 되도록 설정한 곳이 어딘지 궁금한 것입니다.
arch/arm/boot/compressed/head
arch/arm/boot/compressed/head.S 에 __setup_mmu 를 보세요. 이미 여기서 부터 mmu_on 됩니다. 압축된 커널을 풀고 나서 __create_page_table 전에 mmu 를 off 하던가?? 안하는거 같았는데..기억이 가물하네요.
__create_page_table 에선 커널 이미지 사이즈만큼은 맵을 만들고, 추후에 paging_init 인가에서 필요한 만큼 하더군요.
아..글구 r0가 c0008000 이 아니라 20008000 이었던거 같아요. __setup_mmu 에서 1:1 로 1MB 단위로 맵핑을 만들거든요.
밥굶지말자
답글 감사합니다. kernel/head.S 의
답글 감사합니다.
kernel/head.S 의 kernel startup entry point 부분 주석에 mmu = off 이 requirement라고 표시되어있습니다.
r0가 0xc0008000이 아니라 0x20008000이라는건 어떤의미신지요. 같은 arm이라도 다 틀린걸로 알고있습니다만...
compressed/head.S를 분석후 추가 답변 올리겠습니다.
아키텍처마다 mmu정보를 참조해서 물리주소를 가지고
아키텍처마다 mmu정보를 참조해서 물리주소를 가지고 오는 방법은 별로 좋지 않아 보입니다.
특히나 user memory는 swap되어있을수도 있기 때문에 동작자체도 완전히 보장되지 않습니다.
물리주소를 얻었다고 해도 이를 이용해서 다른쪽에서 사용한다면 캐시때문에 문제가 생길수도 있습니다.
user memory의 물리주소가 필요한 작업이라면 드라이버 내에서 get_user_pages()등으로 매핑된 페이지 정보를 가져와서 처리하시는것이 가장 나을것같습니다.
답변 감사합니다. 적어도 제가 사용하는 arm
답변 감사합니다.
적어도 제가 사용하는 arm 보드는(다른 보드는 어떤지 모르지만^_^;;) swap 파티션을 따로 잡아주지 않습니다.
보드를 3개정도 사용해봤는데 swap 파티션을 따로 잡아주는 임베디드 보드는 아직 접해보지 못했습니다.
(아니면 제가 모르는 부분일수도 있구요^^;;)
헌데 user memory 스왑과 물리메모리 주소값을 얻는 것이 어떤 관계가 있나요?
만약 특정 가상주소가 물리메모리 부족으로 스왑되어 하드에 있다고 하더라도
해당 가상주소를 엑세스 하는 순간 memory로 다시 swap되어 올라올것이라 생각됩니다만.
get_user_pages() 함수를 차후 윗분께서 말씀해주신 compressed/head.S 분석할때 같이 분석하여 다시 답글로 올리도록 하겠습니다.
ps. 아직 분석은 안해봤지만 get_user_pages() 함수로 더 모듈을 좀더 깔끔하게 작성할 수 있을지도 모르겠습니다. 감사합니다.
지금 사용하시는 유저 프로그램을 background
지금 사용하시는 유저 프로그램을 background 로 돌리면서 해보십시오 ( ./a.out & 등으로 )
swap 이 아니어도 page off 는 일어납니다.
Neogeo - Future is Now.
답변 감사합니다. 리눅스상에서 특정 process의
답변 감사합니다.
리눅스상에서 특정 process의 page fault를 확인하려면 어떻게 해야 하나요?
해결된지 안된지 모르겠으나 일단 제가 사용하는
해결된지 안된지 모르겠으나
일단 제가 사용하는 임베디드 보드에서 테스트 결과 물리메모리가 RAM 영역의 memory map 이내의 값이 나옵니다.
s3c2440 보드의 예로 들면
0x30000000 ~ 0x3fffffff 까지가 물리적인 dram의 memory map 주소였으면
vaddr : 0x12345678 / paddr : 0x31234567 과 같은 식의 물리주소가 출력됩니다.
약 100번정도의 test결과 해당 주소범위를 넘어가는 경우는 발견하지 못하였습니다.
다른 보드도 마찬가지로 해당보드의 물리메모리 주소 번지의 범위는 넘어가지 않았습니다.
일단 제 소스의 문제점은 pte_page라는 함수였습니다.
pte_val로 수정하니 잘 동작하는군요.
댓글 달기