
일요일은 제가 공부동에 안 나갈거기 때문에 오늘 미리 올립니다.
int N 대신 syscall을 사용하는 경우int 0x30 말고 syscall을 사용iretq 말고 sysretq를 사용syscall_handler에 전달하는 것까진 동일syscall 실행 시, CPU가 자동으로 커널에 값을 푸시해 주지 않음.RIP를 RCX에, EFLAGS를 R11에 저장만 해 줌. 믈론 어셈블리어에서 수동으로 푸시해야 함.RSP는 어셈블리어 코드에서 수동으로 RBX에 복사한 뒤, 해당 값을 푸시해야 함.syscall은 CPU가 자동으로 커널 스택으로 RSP를 전환해 주지도 않음.sysretq에서도 cpu가 자동으로 레지스터를 팝해주지 않음.sysretq는 RCX, R11에 저장된 값을 RIP, EFLAGS로 복원만 해 줌.sysretq 전 어셈블리어 코드에서 RSP를 팝해주고, RIP에 넣을 값은 RCX, EFLAGS에 넣을 값은 R11로 팝해야 함.syscall_handler 인자에서 struct intr_frame*로 커널 스택에 동일하게 접근 가능intr_frame 구조체 순서대로 푸시하기 때문#include "threads/loader.h"
// syscall 실행 후 CPU 자동 처리 후 실행되는 어셈블리어 코드
syscall_entry:
movq %rbx, temp1(%rip)
movq %r12, temp2(%rip)
/* rsp(사용자 스택포인터 값)를 rbx에 저장 */
movq %rsp, %rbx
/* 사용자 스택 -> 커널 스택으로 주소 전환 */
movabs $tss, %r12
movq (%r12), %r12
movq 4(%r12), %rsp
// push A는 A 레지스터의 값을 푸시한다는 뜻
// int N에선 CPU가 자동으로 했었지만, 여기선 우리가 직접 푸시해야 함
push $(SEL_UDSEG) /* if->ss */
push %rbx /* if->rsp */
push %r11 /* if->eflags */
push $(SEL_UCSEG) /* if->cs */
push %rcx /* if->rip */
// int N에선 error_code, vec_no을 푸시했지만, 여기선 건너뜀
subq $16, %rsp /* skip error_code, vec_no */
// 여기서부터 int N에서도 어셈블리어가 푸시하던 애들이 나옴 (범용 레지스터)
push $(SEL_UDSEG) /* if->ds */
push $(SEL_UDSEG) /* if->es */
push %rax
movq temp1(%rip), %rbx
push %rbx
pushq $0 /* rcx는 생략 */
push %rdx
push %rbp
push %rdi
push %rsi
push %r8
push %r9
push %r10
pushq $0 /* r11은 생략 */
movq temp2(%rip), %r12
push %r12
push %r13
push %r14
push %r15
// syscall_handler의 인자로 스택 포인터를 전달
movq %rsp, %rdi
// 몰라도됨
check_intr:
btsq $9, %r11 /* Check whether we recover the interrupt */
jnb no_sti
sti /* restore interrupt */
no_sti:
// 여기서 syscall_handler 함수가 실행됨
movabs $syscall_handler, %r12
call *%r12
// 이후 값을 다시 팝
// pop B는 커널스택의 값을 팝해 B에 복사한다는 뜻
popq %r15
popq %r14
popq %r13
popq %r12
popq %r11
popq %r10
popq %r9
popq %r8
popq %rsi
popq %rdi
popq %rbp
popq %rdx
popq %rcx
popq %rbx
popq %rax
addq $32, %rsp
popq %rcx /* if->rip 값을 RCX에 저장 -> sysretq에서 RIP로 복원됨 */
addq $8, %rsp
popq %r11 /* if->eflags 값을 R11에 저장 -> sysretq에서 EFLAGS로 복원됨 */
popq %rsp /* if->rsp 값을 RSP에 저장 */
// sysretq로 종료
sysretq /* `RCX`, `R11`에 저장된 값을 `RIP`, `EFLAGS`로 복원해 줌. */
syscall_handler
userprog/syscall.c 내 syscall_handler 함수 구현rax 레지스터에 저장됨 -> f -> R -> rax로 확인include/lib/syscall_nr.h에서 확인 가능 (열거형인데 0, 1, 2 순)f -> R의 rdi, rsi, rdx, r10, r8, r9 멤버로 확인 가능rax 레지스터에 저장해야 함// userprog/syscall.c
void
syscall_handler (struct intr_frame *f UNUSED) {
// TODO: Your implementation goes here.
int number = f -> R -> rax; // ??? 맞는진 모름
switch(number){
case SYS_HALT: /* Halt the operating system. */
case SYS_EXIT: /* Terminate this process. */
case SYS_FORK: /* Clone current process. */
case SYS_EXEC: /* Switch current process. */
case SYS_WAIT: /* Wait for a child process to die. */
case SYS_CREATE: /* Create a file. */
case SYS_REMOVE: /* Delete a file. */
case SYS_OPEN: /* Open a file. */
case SYS_FILESIZE: /* Obtain a file's size. */
case SYS_READ: /* Read from a file. */
case SYS_WRITE: /* Write to a file. */
case SYS_SEEK: /* Change position in a file. */
case SYS_TELL: /* Report current position in a file. */
case SYS_CLOSE: /* Close a file. */
}
printf ("system call!\n"); // 이걸 다른 코드로 바꿔야겠지
thread_exit ();
}
// include/lib/syscall_nr.h
/* System call numbers. */
enum {
/* Projects 2 and later. */
SYS_HALT, /* Halt the operating system. */
SYS_EXIT, /* Terminate this process. */
SYS_FORK, /* Clone current process. */
SYS_EXEC, /* Switch current process. */
SYS_WAIT, /* Wait for a child process to die. */
SYS_CREATE, /* Create a file. */
SYS_REMOVE, /* Delete a file. */
SYS_OPEN, /* Open a file. */
SYS_FILESIZE, /* Obtain a file's size. */
SYS_READ, /* Read from a file. */
SYS_WRITE, /* Write to a file. */
SYS_SEEK, /* Change position in a file. */
SYS_TELL, /* Report current position in a file. */
SYS_CLOSE, /* Close a file. */
// 뒤에서부턴 Project 3 이후 내용이라 생략
};
syscall_handler에서 Address validation 수행f -> R의 rdi, rsi, rdx, r10, r8, r9 멤버로 확인 가능threads/vaddr.h threads/mmu.h 의 함수들을 사용하면 됨0인지 확인 -> 빡꾸is_kernel_vaddr(주소)가 true면 커널영역의 주소 -> 빡꾸pml4_get_page(thread_current() -> pml4, 주소)가 널 포인터를 반환하면 매핑되지 않은 주소 -> 빡꾸thread_exit() 쓰면 될듯?syscall 함수를 통해 시스템 콜을 보냄switch문에서 시스템 콜 번호에 따른 올바른 처리를 하면 됨.syscall0, syscall1, ... 등 함수 뒤에 붙은 숫자는, 시스템 콜에 보내는 인자 수를 뜻함. 결국엔 해당 함수 안에서 syscall이 호출됨. rdi부터 r9까지 6개 레지스터가 있으므로 syscall6까지 존재.halthalt 구현하기src/include/threads/init.h에 선언된 power_off()를 호출하면 해결됨. 참 쉽죠?// 사용자 측 (lib/user/syscall.c`)
void halt (void) {
syscall0 (SYS_HALT);
NOT_REACHED ();
}
// 커널 측 (userprog/syscall.c)
switch(number) {
// 생략
case SYS_HALT:
power_off();
}
switch(number){
case SYS_HALT:
/* Halt the operating system. */
// [구현 3] 핀토스를 종료. 매우 쉽죠?
power_off();
case SYS_EXIT:
/* Terminate this process. */
case SYS_FORK:
/* Clone current process. */
case SYS_EXEC: /* Switch current process. */
case SYS_WAIT: /* Wait for a child process to die. */
case SYS_CREATE: /* Create a file. */
case SYS_REMOVE: /* Delete a file. */
case SYS_OPEN: /* Open a file. */
case SYS_FILESIZE: /* Obtain a file's size. */
case SYS_READ: /* Read from a file. */
case SYS_WRITE: /* Write to a file. */
case SYS_SEEK: /* Change position in a file. */
case SYS_TELL: /* Report current position in a file. */
case SYS_CLOSE: /* Close a file. */
}