<aside> 💡 c 파일 ⇒ userprog h 파일 ⇒ include/userprog
</aside>
63 48 47 39 38 30 29 21 20 12 11 0
+-------------+----------------+----------------+----------------+-------------+------------+
| Sign Extend | Page-Map | Page-Directory | Page-directory | Page-Table | Physical |
| | Level-4 Offset | Pointer | Offset | Offset | Offset |
+-------------+----------------+----------------+----------------+-------------+------------+
| | | | | |
+------- 9 ------+------- 9 ------+------- 9 ------+----- 9 -----+---- 12 ----+
Virtual Address
<aside> 💡 [ offset ] offset은 메모리 주소의 일부분으로, 해당 주소가 속한 블록의 시작 위치로부터 얼마나 떨어져 있는지를 나타냅니다. 예를 들어, 0x12345678 주소에서의 offset이 0x8인 경우, 이는 0x12345670에서 시작하는 블록 내에서 8바이트 떨어져 있다는 것을 의미합니다. 이를 통해 주소에서 필요한 데이터가 실제로 어디에 저장되어 있는지 계산할 수 있습니다.
</aside>
#define BITMASK(SHIFT, CNT) (((1ul << (CNT)) - 1) << (SHIFT))
#define PGSHIFT 0 /* 가상 주소의 offset 부분의 비트 인덱스 */
#define PGBITS 12 /* 가상 주소의 offset 부분의 비트 수 */
#define PGSIZE (1 << PGBITS) /* 페이지 크기(4,096) */
#define PGMASK BITMASK(PGSHIFT, PGBITS) /* 페이지 offset의 비트가 1로 설정되고 나머지가 0으로 설정된 비트 마스크(0xfff) */
#define pg_ofs(va) ((uint64_t) (va) & PGMASK) /* 가상 주소 va에서 페이지 offset을 추출하여 반환 */
#define pg_no(va) ((uint64_t) (va) >> PGBITS) /* 가상 주소 va에서 페이지 번호를 추출하여 반환 */
#define pg_round_up(va) ((void *) (((uint64_t) (va) + PGSIZE - 1) & ~PGMASK)) /* va를 가장 가까운 페이지 경계로 반올림하여 반환 */
/* Round down to nearest page boundary. */
#define pg_round_down(va) (void *) ((uint64_t) (va) & ~PGMASK) /* va가 가리키는 가상 페이지의 시작 위치를 반환 */
/* 커널 가상 메모리의 시작 주소 */
#define KERN_BASE LOADER_KERN_BASE
/* 유저 스택 시작 주소 */
#define USER_STACK 0x47480000
/* 만약 VADDR이 유저 가상 주소인 경우 true를 반환 */
#define is_user_vaddr(vaddr) (!is_kernel_vaddr((vaddr)))
/* 만약 VADDR이 커널 가상 주소인 경우 true를 반환 */
#define is_kernel_vaddr(vaddr) ((uint64_t)(vaddr) >= KERN_BASE)
/* 주어진 물리 주소(PADDR)에 매핑되는 커널 가상 주소를 반환 */
#define ptov(paddr) ((void *) (((uint64_t) paddr) + KERN_BASE))
/* 함수는 커널 가상 주소 VADDR이 매핑된 물리 주소를 반환 */
#define vtop(vaddr) \\
({ \\
ASSERT(is_kernel_vaddr(vaddr)); \\
((uint64_t) (vaddr) - (uint64_t) KERN_BASE);\\
})
typedef bool pte_for_each_func (uint64_t *pte, void *va, void *aux);
uint64_t *pml4e_walk (uint64_t *pml4, const uint64_t va, int create);
uint64_t *pml4_create (void);
bool pml4_for_each (uint64_t *, pte_for_each_func *, void *);
void pml4_destroy (uint64_t *pml4);
void pml4_activate (uint64_t *pml4);
void *pml4_get_page (uint64_t *pml4, const void *upage);
bool pml4_set_page (uint64_t *pml4, void *upage, void *kpage, bool rw);
void pml4_clear_page (uint64_t *pml4, void *upage);
bool pml4_is_dirty (uint64_t *pml4, const void *upage);
void pml4_set_dirty (uint64_t *pml4, const void *upage, bool dirty);
bool pml4_is_accessed (uint64_t *pml4, const void *upage);
void pml4_set_accessed (uint64_t *pml4, const void *upage, bool accessed);
/* PTE가 가리키는 가상 주소가 쓰기 가능한지 여부를 확인하는 매크로 */
#define is_writable(pte) (*(pte) & PTE_W)
/* PTE(page table entry)가 유저 또는 커널 소유인지를 확인하는 매크로 */
#define is_user_pte(pte) (*(pte) & PTE_U)
#define is_kern_pte(pte) (!is_user_pte (pte))
#define pte_get_paddr(pte) (pg_round_down(*(pte)))
/* Segment descriptors for x86-64. */
struct desc_ptr {
uint16_t size;
uint64_t address;
} __attribute__((packed));
ELF 바이너리를 로드하고 프로세스를 시작
/* ELF types. See [ELF1] 1-2. */
#define EI_NIDENT 16
#define PT_NULL 0 /* Ignore. */
#define PT_LOAD 1 /* Loadable segment. */
#define PT_DYNAMIC 2 /* Dynamic linking info. */
#define PT_INTERP 3 /* Name of dynamic loader. */
#define PT_NOTE 4 /* Auxiliary info. */
#define PT_SHLIB 5 /* Reserved. */
#define PT_PHDR 6 /* Program header table. */
#define PT_STACK 0x6474e551 /* Stack segment. */
#define PF_X 1 /* Executable. */
#define PF_W 2 /* Writable. */
#define PF_R 4 /* Readable. */
/* Executable header. See [ELF1] 1-4 to 1-8.
* This appears at the very beginning of an ELF binary. */
struct ELF64_hdr {
unsigned char e_ident[EI_NIDENT];
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
uint64_t e_entry;
uint64_t e_phoff;
uint64_t e_shoff;
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
};
struct ELF64_PHDR {
uint32_t p_type;
uint32_t p_flags;
uint64_t p_offset;
uint64_t p_vaddr;
uint64_t p_paddr;
uint64_t p_filesz;
uint64_t p_memsz;
uint64_t p_align;
};
/* Abbreviations */
#define ELF ELF64_hdr
#define Phdr ELF64_PHDR
사용자 프로세스가 커널 기능에 액세스하려면 호출합니다. 이것은 스켈레톤 시스템 호출 핸들러입니다.
/* Process identifier. */
typedef int pid_t;
#define PID_ERROR ((pid_t) -1)
/* Map region identifier. */
typedef int off_t;
#define MAP_FAILED ((void *) NULL)
/* Maximum characters in a filename written by readdir(). */
#define READDIR_MAX_LEN 14
/* Typical return values from main() and arguments to exit(). */
#define EXIT_SUCCESS 0 /* Successful execution. */
#define EXIT_FAILURE 1 /* Unsuccessful execution. */
#define MSR_STAR 0xc0000081
#define MSR_LSTAR 0xc0000082
#define MSR_SYSCALL_MASK 0xc0000084
사용자 프로세스가 특권 또는 금지된 작업을 수행할 때, 예외나 오류로 커널에 트랩됩니다. 이 파일들은 예외를 처리합니다. 현재 모든 예외는 간단한 메시지를 출력하고 프로세스를 종료합니다.