안녕하세요?
제가 리눅스 커널을 공부한지 얼마안되어서;; 이 곳 자료를 많이 참조하면서 공부하고 있습니다 :)
이점에서 정말 감사드리고요. 저도 서울 지역에 거주하게 될 경우, 꼭 스터디에 참석하도록 하겠습니다.~
그리고 초면에 이렇게 질문 드리는 것은 예의가 아닌데, 마땅히 질문할 곳이 있지 않아서 이렇게 글을 적습니다. (백창우님 개인적으로 존경함, 멤버십에서 OS 과제, 필수 유틸리티 등으로 인해)
일단, 본론을 말씀드리자면, 제가 Kernel(DPL:0)과 User(DPL:3) 사이에 뭔가를 새로 집어넣고, 그 곳의 코드를 User(DPL:3)에서 실행토록 하는 것입니다. 물론, 거대한 프로젝트를 위한 테스트 작업 수준입니다.
현재까지 진행 상황은 kernel 2.6.24.4를 받아서 소스의 arch/x86/kernel/cpu/common.c 파일에서 gdt_page가 선언되어 있는 곳에 제가 원하는 새로운 Segment 정의를 했습니다.(물론, include/asm-x86/segment_32.h에 선언되어 있는 GDT 번호를 살펴본 후에, 사용되어 있지 않은(unused) 번호를 사용하고 있습니다. 이것은 제가 비어있다고 여기는 것으로, 실제는 모르겠네요;; 일단, 12,13번이 Kernel code, data segment로 할당되어 있고, 등등 적혀 있는데, 저는 28,29,30번을 사용했습니다.)
header 파일에서의 정의는 아래와 같이 추가하였습니다.
-------------------------------------------------------------------------
#define GDT_ENTRY_VM_BASE 28
#define GDT_ENTRY_VM_CS (GDT_ENTRY_VM_BASE + 0)
#define __VM_CS (GDT_ENTRY_VM_CS * 8 + 3)
#define GDT_ENTRY_VM_DS (GDT_ENTRY_VM_BASE + 1)
#define __VM_DS (GDT_ENTRY_VM_DS * 8 + 3)
#define GDT_VM_CALLGATE 30
#define __VM_CALLGATE (GDT_VM_CALLGATE * 8 + 3)
-------------------------------------------------------------------------
위를 보시면 아시겠지만, + 3을 한 이유는 User Code에서 수행할 수 있도록 한 것입니다. 일단, 테스트 후에 DPL = 1 권한으로 변경할 예정입니다.(처음부터 1로 했었는데, 아예 접근 에러가 나서... 다시 이렇게 한 것입니다.) 이름은 그냥 VM이라고 임시로 했습니다.;;
그러면, 다시 gdt_page 테이블로 와서, 맨 마지막에
-------------------------------------------------------------------------
[GDT_ENTRY_VM_CS] = { 0x0000ffff, 0x00cffa00 }, /* vm 4GB code */
[GDT_ENTRY_VM_DS] = { 0x0000ffff, 0x00cff200 }, /* vm 4GB data */
/* 계산을 위한 참고 사항입니다.
<call-gate vm-user>
.word 0x0000 routine offset 15~0
.word __VMCODE_CS code segment selector
.word 0xec00 P:1,DPL:11,0,type:1100(Call-gate)
.word 0x0000 routine offset 31~16
<temp code>
_VM_CS = 28*8 + 1 = 225(0xe1)
0x0000 0x00e0 0xec00 0x0000 (word)
00 00 e0 00 00 ec 00 00 (byte)
0x00e00000 0x0000ec00 (dword)
*/
[GDT_VM_CALLGATE] = { 0x00e30000, 0x0000ec00 }
-------------------------------------------------------------------------
그러면, 부팅할 때, 저절로 lgdt 명령에 의해서 읽어지는 것 같더군요. 제 소견으로는 부팅시 cpu_init() 함수가 호출되는데, switch_to_new_gdt() 함수가 호출되는 것으로 보이며, 관련된 값으로 GDT_SIZE가 있는데, 이것은 gdt_page 테이블 전체를 읽어들이는 것으로 보입니다.
제가 질문 드리고자 하는 것은 이제부터입니다. ;; 조금만 참고 읽어주시면 감사하겠습니다.
아래 코드는 이 arch/x86/kernel/cpu/common.c 파일에서 cpu_init() 함수의 내용에서 switch_to_new_gdt()가 호출되고 나서 바로 수행토록 한 것입니다. 근데, 아래 소스를 집어넣으면 부팅시에 멈춰버리는 현상이 있습니다.
-------------------------------------------------------------------------
load_idt(&idt_descr);
switch_to_new_gdt();
/**************************************
* Add my code [by ntames8] *
**************************************/
/*
Selector
0x60 : kernel code
0x68 : kernel data
0xe3 : vm code
0xeb : vm data
0xf3 : call-gate
*/
printk(KERN_INFO "-_- mycode startn");
buff = kmalloc(1024, GFP_KERNEL);
if (buff != NULL)
{
asm(
// it copys from vm_test to vm_code area(buff)
"pusha;"
"leal vm_test,%%eax;"
"movl %%eax,%0;"
"push %%ds;"
"push %%es;"
: "=m" (addr));
printk(KERN_INFO "vm_test addr in kernel = %8pn", addr);
asm(
//일단 DS로 할당해봄. 원래는 CS로 해야함.
"movl $0xeb,%%eax;" // __VM_DS = 0xe9
"mov %%ax,%%es;"
"leal vm_test,%%esi;"
"leal %0,%%eax;"
"movl (%%eax),%%edi;" // edi <= buff(kmalloc)
"movl $(vm_end-vm_test),%%ecx;"
"rep;"
"movsb;"
"jmp vm_end;"
"vm_test:"
"pusha;"
"movl $0x4,%%eax;"
"movl $0x1,%%ebx;" // unsigned int
"leal str_hello,%%ecx;" // const char*
"movl $14,%%edx;" // size_t
"int $0x80;"
"popa;"
"lret;"
"str_hello:"
".string "Hello world!n";"
"vm_end:"
"pop %%es;"
"pop %%ds;"
"popa;"
: // no output
: "m" (buff));
printk(KERN_INFO "vm_test addr as buff in vm_ds = %8pn", addr);
}
else
{
printk(KERN_INFO "no kmalloc");
}
printk(KERN_INFO "-_- mycode endn");
/******************
* End *
******************/
-------------------------------------------------------------------------
부팅시 Uncompressing Linux... OK, booting the kernel.이라는 메시지가 나온 후에 그냥 멈춰있는 상태입니다.
이 메시지는 arch/x86/boot/compressed/misc_32.c 파일의 decompress_kernel() 함수에서 gunzip()하고 나서 나오는 메시지군요.;;;
혹시 제가 잘못한 것이 무엇인지 알 수 있을까요?? 커널에 임의의 코드를 심어놓고 작동하게 하는 데, 제한적인 요소가 어떤 것들이 있는지 알 수 있을까요?;;
제가 간단하게 살펴보니, 리눅스 커널이 하나의 페이지에 올라와서 수행된다는 말이 있던데, 혹시 페이지에 관련된 메모리 제한(제약) 때문인가요? 페이지를 제가 새로 생성해야하나요?
저도 혹시나 해서 kmalloc()함수로 할당해본 것이거든요.(할당하든 안하든 똑같은 에러입니다.)
제가 페이징이 어떻게 이루어지고, 정확하게 모르는 상황이라, 어느 쪽으로 접근하면 답을 알 수 있는지라도 알려주시면 감사하겠습니다. __;
이 작업을 하면서 제가 틈틈히 기록하는 공간은 http://linu.sarang.net/wiki/?%EB%A6%AC%EB%88%85%EC%8A%A4%EC%BB%A4%EB%84%90 으로 혹시나 모르시는 내용이 있으시면 이 쪽을 살펴보시면 될 것 같아요. 여기에 리플로 다시 재질문 하셔도 되고요.;;
말이 많았네요. 아, 참고로, 위에서 작성된 어셈블리어 프로그램은 유저 레벨에서 어느 정도 검증된 상태입니다. 코드상 에러보다는, 코드 자체가 커널 부팅 과정에 포함되어 있는 상황에 초점을 더 맞춰주셨으면 합니다.;;;
그러면 여기까지 읽어주신 것만으로도 감사드립니다. __; -- 2008-05-31 02:43 wbhacker
제가 리눅스 커널을 공부한지 얼마안되어서;; 이 곳 자료를 많이 참조하면서 공부하고 있습니다 :)
이점에서 정말 감사드리고요. 저도 서울 지역에 거주하게 될 경우, 꼭 스터디에 참석하도록 하겠습니다.~
그리고 초면에 이렇게 질문 드리는 것은 예의가 아닌데, 마땅히 질문할 곳이 있지 않아서 이렇게 글을 적습니다. (백창우님 개인적으로 존경함, 멤버십에서 OS 과제, 필수 유틸리티 등으로 인해)
일단, 본론을 말씀드리자면, 제가 Kernel(DPL:0)과 User(DPL:3) 사이에 뭔가를 새로 집어넣고, 그 곳의 코드를 User(DPL:3)에서 실행토록 하는 것입니다. 물론, 거대한 프로젝트를 위한 테스트 작업 수준입니다.
현재까지 진행 상황은 kernel 2.6.24.4를 받아서 소스의 arch/x86/kernel/cpu/common.c 파일에서 gdt_page가 선언되어 있는 곳에 제가 원하는 새로운 Segment 정의를 했습니다.(물론, include/asm-x86/segment_32.h에 선언되어 있는 GDT 번호를 살펴본 후에, 사용되어 있지 않은(unused) 번호를 사용하고 있습니다. 이것은 제가 비어있다고 여기는 것으로, 실제는 모르겠네요;; 일단, 12,13번이 Kernel code, data segment로 할당되어 있고, 등등 적혀 있는데, 저는 28,29,30번을 사용했습니다.)
header 파일에서의 정의는 아래와 같이 추가하였습니다.
-------------------------------------------------------------------------
#define GDT_ENTRY_VM_BASE 28
#define GDT_ENTRY_VM_CS (GDT_ENTRY_VM_BASE + 0)
#define __VM_CS (GDT_ENTRY_VM_CS * 8 + 3)
#define GDT_ENTRY_VM_DS (GDT_ENTRY_VM_BASE + 1)
#define __VM_DS (GDT_ENTRY_VM_DS * 8 + 3)
#define GDT_VM_CALLGATE 30
#define __VM_CALLGATE (GDT_VM_CALLGATE * 8 + 3)
-------------------------------------------------------------------------
위를 보시면 아시겠지만, + 3을 한 이유는 User Code에서 수행할 수 있도록 한 것입니다. 일단, 테스트 후에 DPL = 1 권한으로 변경할 예정입니다.(처음부터 1로 했었는데, 아예 접근 에러가 나서... 다시 이렇게 한 것입니다.) 이름은 그냥 VM이라고 임시로 했습니다.;;
그러면, 다시 gdt_page 테이블로 와서, 맨 마지막에
-------------------------------------------------------------------------
[GDT_ENTRY_VM_CS] = { 0x0000ffff, 0x00cffa00 }, /* vm 4GB code */
[GDT_ENTRY_VM_DS] = { 0x0000ffff, 0x00cff200 }, /* vm 4GB data */
/* 계산을 위한 참고 사항입니다.
<call-gate vm-user>
.word 0x0000 routine offset 15~0
.word __VMCODE_CS code segment selector
.word 0xec00 P:1,DPL:11,0,type:1100(Call-gate)
.word 0x0000 routine offset 31~16
<temp code>
_VM_CS = 28*8 + 1 = 225(0xe1)
0x0000 0x00e0 0xec00 0x0000 (word)
00 00 e0 00 00 ec 00 00 (byte)
0x00e00000 0x0000ec00 (dword)
*/
[GDT_VM_CALLGATE] = { 0x00e30000, 0x0000ec00 }
-------------------------------------------------------------------------
그러면, 부팅할 때, 저절로 lgdt 명령에 의해서 읽어지는 것 같더군요. 제 소견으로는 부팅시 cpu_init() 함수가 호출되는데, switch_to_new_gdt() 함수가 호출되는 것으로 보이며, 관련된 값으로 GDT_SIZE가 있는데, 이것은 gdt_page 테이블 전체를 읽어들이는 것으로 보입니다.
제가 질문 드리고자 하는 것은 이제부터입니다. ;; 조금만 참고 읽어주시면 감사하겠습니다.
아래 코드는 이 arch/x86/kernel/cpu/common.c 파일에서 cpu_init() 함수의 내용에서 switch_to_new_gdt()가 호출되고 나서 바로 수행토록 한 것입니다. 근데, 아래 소스를 집어넣으면 부팅시에 멈춰버리는 현상이 있습니다.
-------------------------------------------------------------------------
load_idt(&idt_descr);
switch_to_new_gdt();
/**************************************
* Add my code [by ntames8] *
**************************************/
/*
Selector
0x60 : kernel code
0x68 : kernel data
0xe3 : vm code
0xeb : vm data
0xf3 : call-gate
*/
printk(KERN_INFO "-_- mycode startn");
buff = kmalloc(1024, GFP_KERNEL);
if (buff != NULL)
{
asm(
// it copys from vm_test to vm_code area(buff)
"pusha;"
"leal vm_test,%%eax;"
"movl %%eax,%0;"
"push %%ds;"
"push %%es;"
: "=m" (addr));
printk(KERN_INFO "vm_test addr in kernel = %8pn", addr);
asm(
//일단 DS로 할당해봄. 원래는 CS로 해야함.
"movl $0xeb,%%eax;" // __VM_DS = 0xe9
"mov %%ax,%%es;"
"leal vm_test,%%esi;"
"leal %0,%%eax;"
"movl (%%eax),%%edi;" // edi <= buff(kmalloc)
"movl $(vm_end-vm_test),%%ecx;"
"rep;"
"movsb;"
"jmp vm_end;"
"vm_test:"
"pusha;"
"movl $0x4,%%eax;"
"movl $0x1,%%ebx;" // unsigned int
"leal str_hello,%%ecx;" // const char*
"movl $14,%%edx;" // size_t
"int $0x80;"
"popa;"
"lret;"
"str_hello:"
".string "Hello world!n";"
"vm_end:"
"pop %%es;"
"pop %%ds;"
"popa;"
: // no output
: "m" (buff));
printk(KERN_INFO "vm_test addr as buff in vm_ds = %8pn", addr);
}
else
{
printk(KERN_INFO "no kmalloc");
}
printk(KERN_INFO "-_- mycode endn");
/******************
* End *
******************/
-------------------------------------------------------------------------
부팅시 Uncompressing Linux... OK, booting the kernel.이라는 메시지가 나온 후에 그냥 멈춰있는 상태입니다.
이 메시지는 arch/x86/boot/compressed/misc_32.c 파일의 decompress_kernel() 함수에서 gunzip()하고 나서 나오는 메시지군요.;;;
혹시 제가 잘못한 것이 무엇인지 알 수 있을까요?? 커널에 임의의 코드를 심어놓고 작동하게 하는 데, 제한적인 요소가 어떤 것들이 있는지 알 수 있을까요?;;
제가 간단하게 살펴보니, 리눅스 커널이 하나의 페이지에 올라와서 수행된다는 말이 있던데, 혹시 페이지에 관련된 메모리 제한(제약) 때문인가요? 페이지를 제가 새로 생성해야하나요?
저도 혹시나 해서 kmalloc()함수로 할당해본 것이거든요.(할당하든 안하든 똑같은 에러입니다.)
제가 페이징이 어떻게 이루어지고, 정확하게 모르는 상황이라, 어느 쪽으로 접근하면 답을 알 수 있는지라도 알려주시면 감사하겠습니다. __;
이 작업을 하면서 제가 틈틈히 기록하는 공간은 http://linu.sarang.net/wiki/?%EB%A6%AC%EB%88%85%EC%8A%A4%EC%BB%A4%EB%84%90 으로 혹시나 모르시는 내용이 있으시면 이 쪽을 살펴보시면 될 것 같아요. 여기에 리플로 다시 재질문 하셔도 되고요.;;
말이 많았네요. 아, 참고로, 위에서 작성된 어셈블리어 프로그램은 유저 레벨에서 어느 정도 검증된 상태입니다. 코드상 에러보다는, 코드 자체가 커널 부팅 과정에 포함되어 있는 상황에 초점을 더 맞춰주셨으면 합니다.;;;
그러면 여기까지 읽어주신 것만으로도 감사드립니다. __; -- 2008-05-31 02:43 wbhacker
댓글 2
.
먼저 다른 루틴 다 빼고, printk()만 넣어서 메세지 출력 되는지 확인해 보셨는지요?