[PintOS 2주차] Day 6

💥 pml4 이슈 발생
어제 write()까지 system call 기능을 구현했지만, pml4에서 assertion이 발생하는 에러가 발생했다.
Kernel PANIC at ../../threads/mmu.c:206 in pml4_activate():
assertion `is_kernel_vaddr(pml4 ? pml4 : base_pml4)' failed.
새로 작성한 구역을 주석 처리를 해도 문제가 해결되지 않아서, 이전의 커밋으로 돌아가서 다시 작업을 시작했다.

argument passing할 때 실행했던 유저 프로그램 args-single이 실행되지 않아 살펴보니 filesys.dsk가 초기화되어 있었다. 분명 argument passing을 구현할 때 디스크를 만들어주었는데 사라진 점이 이상하다.
$ ../../utils/pintos-mkdisk filesys.dsk 10
$ pintos -v --fs-disk=filesys.dsk -- -f -q
$ cp tests/userprog/args-single ./args-single
$ pintos -v -k --fs-disk=filesys.dsk -p args-single -- -f -q
$ pintos -v -k --fs-disk=filesys.dsk -- -q run 'args-single onearg'
🚨 Page fault 발생
args-single 테스트에서 출력이 제대로 나오는지 확인하기 위해 SYS_WRITE 일부를 구현했다.
case SYS_WRITE: /* Write to a file. */
printf("%s", f->R.rsi);
// f->R.rax = write(f->R.rdi, f->R.rsi, f->R.rdx);
break;

그런데 SYS_WRITE를 설정해주니 (args) argv[0] = 'args-single'가 나와야 할 부분부터 Page Fault가 발생했다. f->R.rsi를 받는 데에서 문제가 생겼나 싶어 "write"를 출력하게 바꿔도 똑같이 세 번째 "write"가 출력되야 할 부분에서 PF가 발생했다.

위에서 rsi 부분을 잘 보자. rsp와 rbp에는 유저 스택 메모리 주소값이 잘 들어가 있지만, rsi에는 이상한 값이 들어가 있다.
process_exec() {
…
_if.R.rsi = (uint64_t) argv;
…
argument_stack(&_if);
hex_dump(_if.rsp, _if.rsp, USER_STACK - (uint64_t)_if.rsp, true);
}
기존에 설정한 _if.R.rsi는 커널 영역의 배열을 가리키고 있었기 때문에 rsi가 이상한 주소를 가리키고 있었던 것이다. 그래서 유저 스택을 쌓은 후에 다시 rsi 값을 유저 스택 상의 주소로 설정해 줘야 된다고 한다.

argument_stack(&_if);
_if.R.rsi = _if.rsp + sizeof(uintptr_t); // ← 코드 추가
hex_dump(_if.rsp, _if.rsp, USER_STACK - (uint64_t)_if.rsp, true);

이제 page fault가 발생하지 않고 잘 출력된다 😇


System call 구현
🔹 시스템 콜 핸들러
void syscall_handler (struct intr_frame *f UNUSED) {
switch(f->R.rax) {
case SYS_HALT: /* Halt the operating system. */
halt();
case SYS_EXIT: /* Terminate this process. */
exit(f->R.rdi);
case SYS_FORK: /* Clone current process. */
break;
case SYS_EXEC: /* Switch current process. */
break;
case SYS_WAIT: /* Wait for a child process to die. */
break;
case SYS_CREATE: /* Create a file. */
f->R.rax = create(f->R.rdi, f->R.rsi);
break;
case SYS_REMOVE: /* Delete a file. */
f->R.rax = remove(f->R.rdi);
break;
case SYS_OPEN: /* Open a file. */
f->R.rax = open(f->R.rdi);
break;
case SYS_FILESIZE: /* Obtain a file's size. */
f->R.rax = filesize(f->R.rdi);
break;
case SYS_READ: /* Read from a file. */
f->R.rax = read(f->R.rdi, f->R.rsi, f->R.rdx);
break;
case SYS_WRITE: /* Write to a file. */
f->R.rax = write(f->R.rdi, f->R.rsi, f->R.rdx);
break;
case SYS_SEEK: /* Change position in a file. */
seek(f->R.rdi, f->R.rsi);
break;
case SYS_TELL: /* Report current position in a file. */
f->R.rax = tell(f->R.rdi);
break;
case SYS_CLOSE: /* Close a file. */
close(f->R.rdi);
break;
default:
exit(f->R.rdi);
}
}
🔹 halt
void halt (void) {
power_off();
}
🔹 exit
void exit(int status) {
printf("%s: exit(%d)\n", thread_current()->name, status);
thread_exit();
}

🔹 create
bool create (const char *file, unsigned initial_size) {
if (file == NULL) exit(-1); // return false;
return filesys_create(file, initial_size);
}

🔹 remove
bool remove (const char *file) {
return filesys_remove(file);
}
🔹 open
int open (const char *filename) {
struct file *file = filesys_open(filename);
if (file == NULL) return -1; // Return -1 if file is not opened
struct thread *curr = thread_current();
int fd;
for(fd = 2; fd <= curr->next_fd; fd++) {
if (curr->fdt[fd] == NULL) {
curr->fdt[fd] = file;
break;
}
}
if (fd == curr->next_fd && curr->next_fd < 63) curr->next_fd++;
else if (fd == curr->next_fd+1) {
fd = -1; // Return -1 if the FDT is full
file_close(file);
}
return fd;
}

🔹 filesize
int filesize (int fd) {
if (fd < 2 || fd > 63) exit(-1); // invalid fd
struct file *file = thread_current()->fdt[fd];
if (file == NULL) return 0; // file not found
return file_length(file);
}
🔹 read
int read (int fd, void *buffer, unsigned size) {
if (fd == 0) return input_getc(); // fd0 is stdin
else if (fd == 1) exit(-1); // fd1 is stdout
else if (fd < 2 || fd > 63) exit(-1); // invalid fd
else {
struct file *file = thread_current()->fdt[fd];
if (file == NULL) exit(-1);
return file_read(file, buffer, size);
}
}

🔹 write
int write(int fd, const void *buffer, unsigned size) {
if(fd == 1) { // fd1 is stdout
putbuf(buffer, size);
return size;
} else if (fd == 0) exit(-1); // fd0 is stdin
else if (fd < 2 || fd > 63) exit(-1); // invalid fd
else {
struct file *file = thread_current()->fdt[fd];
if (file == NULL) exit(-1);
return file_write(file, buffer, size);
}
}

🔹 seek
void seek (int fd, unsigned position) {
if (fd < 2 || fd > 63) exit(-1); // invalid fd
struct thread *curr = thread_current();
struct file *file = curr->fdt[fd];
if (file == NULL) exit(-1); // file not found
file_seek(file, position);
}
🔹 tell
void seek (int fd, unsigned position) {
if (fd < 2 || fd > 63) exit(-1); // invalid fd
struct thread *curr = thread_current();
struct file *file = curr->fdt[fd];
if (file == NULL) exit(-1); // file not found
file_seek(file, position);
}
🔹 close
void close (int fd) {
if (fd < 2 || fd > 63) exit(-1); // invalid fd
struct thread *curr = thread_current();
struct file *file = curr->fdt[fd];
if (file == NULL) exit(-1); // file not found
curr->fdt[fd] = NULL;
if (fd == curr->next_fd && curr->next_fd > 2) curr->next_fd--;
file_close(file);
}
