summaryrefslogtreecommitdiffstats
path: root/pintos-progos/userprog
diff options
context:
space:
mode:
Diffstat (limited to 'pintos-progos/userprog')
-rw-r--r--pintos-progos/userprog/.gitignore3
-rw-r--r--pintos-progos/userprog/Make.vars7
-rw-r--r--pintos-progos/userprog/Makefile1
-rw-r--r--pintos-progos/userprog/exception.c174
-rw-r--r--pintos-progos/userprog/exception.h12
-rw-r--r--pintos-progos/userprog/gdt.c146
-rw-r--r--pintos-progos/userprog/gdt.h15
-rw-r--r--pintos-progos/userprog/pagedir.c263
-rw-r--r--pintos-progos/userprog/pagedir.h18
-rw-r--r--pintos-progos/userprog/process.c721
-rw-r--r--pintos-progos/userprog/process.h47
-rw-r--r--pintos-progos/userprog/syscall.c563
-rw-r--r--pintos-progos/userprog/syscall.h5
-rw-r--r--pintos-progos/userprog/tss.c106
-rw-r--r--pintos-progos/userprog/tss.h11
15 files changed, 2092 insertions, 0 deletions
diff --git a/pintos-progos/userprog/.gitignore b/pintos-progos/userprog/.gitignore
new file mode 100644
index 0000000..6d5357c
--- /dev/null
+++ b/pintos-progos/userprog/.gitignore
@@ -0,0 +1,3 @@
1build
2bochsrc.txt
3bochsout.txt
diff --git a/pintos-progos/userprog/Make.vars b/pintos-progos/userprog/Make.vars
new file mode 100644
index 0000000..e4dbb08
--- /dev/null
+++ b/pintos-progos/userprog/Make.vars
@@ -0,0 +1,7 @@
1# -*- makefile -*-
2
3kernel.bin: DEFINES = -DUSERPROG -DFILESYS
4KERNEL_SUBDIRS = threads devices lib lib/kernel userprog filesys
5TEST_SUBDIRS = tests/userprog tests/userprog/no-vm tests/filesys/base
6GRADING_FILE = $(SRCDIR)/tests/userprog/Grading
7SIMULATOR = --qemu
diff --git a/pintos-progos/userprog/Makefile b/pintos-progos/userprog/Makefile
new file mode 100644
index 0000000..34c10aa
--- /dev/null
+++ b/pintos-progos/userprog/Makefile
@@ -0,0 +1 @@
include ../Makefile.kernel
diff --git a/pintos-progos/userprog/exception.c b/pintos-progos/userprog/exception.c
new file mode 100644
index 0000000..17620ad
--- /dev/null
+++ b/pintos-progos/userprog/exception.c
@@ -0,0 +1,174 @@
1#include "userprog/exception.h"
2#include <inttypes.h>
3#include <stdio.h>
4#include "userprog/gdt.h"
5#include "threads/interrupt.h"
6#include "threads/thread.h"
7#include "threads/vaddr.h"
8
9/* Number of page faults processed. */
10static long long page_fault_cnt;
11
12static void kill (struct intr_frame *);
13static void page_fault (struct intr_frame *);
14
15/* Registers handlers for interrupts that can be caused by user
16 programs.
17
18 In a real Unix-like OS, most of these interrupts would be
19 passed along to the user process in the form of signals, as
20 described in [SV-386] 3-24 and 3-25, but we don't implement
21 signals. Instead, we'll make them simply kill the user
22 process.
23
24 Page faults are an exception. Here they are treated the same
25 way as other exceptions, but this will need to change to
26 implement virtual memory.
27
28 Refer to [IA32-v3a] section 5.15 "Exception and Interrupt
29 Reference" for a description of each of these exceptions. */
30void
31exception_init (void)
32{
33 /* These exceptions can be raised explicitly by a user program,
34 e.g. via the INT, INT3, INTO, and BOUND instructions. Thus,
35 we set DPL==3, meaning that user programs are allowed to
36 invoke them via these instructions. */
37 intr_register_int (3, 3, INTR_ON, kill, "#BP Breakpoint Exception");
38 intr_register_int (4, 3, INTR_ON, kill, "#OF Overflow Exception");
39 intr_register_int (5, 3, INTR_ON, kill,
40 "#BR BOUND Range Exceeded Exception");
41
42 /* These exceptions have DPL==0, preventing user processes from
43 invoking them via the INT instruction. They can still be
44 caused indirectly, e.g. #DE can be caused by dividing by
45 0. */
46 intr_register_int (0, 0, INTR_ON, kill, "#DE Divide Error");
47 intr_register_int (1, 0, INTR_ON, kill, "#DB Debug Exception");
48 intr_register_int (6, 0, INTR_ON, kill, "#UD Invalid Opcode Exception");
49 intr_register_int (7, 0, INTR_ON, kill,
50 "#NM Device Not Available Exception");
51 intr_register_int (11, 0, INTR_ON, kill, "#NP Segment Not Present");
52 intr_register_int (12, 0, INTR_ON, kill, "#SS Stack Fault Exception");
53 intr_register_int (13, 0, INTR_ON, kill, "#GP General Protection Exception");
54 intr_register_int (16, 0, INTR_ON, kill, "#MF x87 FPU Floating-Point Error");
55 intr_register_int (19, 0, INTR_ON, kill,
56 "#XF SIMD Floating-Point Exception");
57
58 /* Most exceptions can be handled with interrupts turned on.
59 We need to disable interrupts for page faults because the
60 fault address is stored in CR2 and needs to be preserved. */
61 intr_register_int (14, 0, INTR_OFF, page_fault, "#PF Page-Fault Exception");
62}
63
64/* Prints exception statistics. */
65void
66exception_print_stats (void)
67{
68 printf ("Exception: %lld page faults\n", page_fault_cnt);
69}
70
71/* Handler for an exception (probably) caused by a user process. */
72static void
73kill (struct intr_frame *f)
74{
75 /* This interrupt is one (probably) caused by a user process.
76 For example, the process might have tried to access unmapped
77 virtual memory (a page fault). For now, we simply kill the
78 user process. Later, we'll want to handle page faults in
79 the kernel. Real Unix-like operating systems pass most
80 exceptions back to the process via signals, but we don't
81 implement them. */
82
83 /* The interrupt frame's code segment value tells us where the
84 exception originated. */
85 switch (f->cs)
86 {
87 case SEL_UCSEG:
88 /* User's code segment, so it's a user exception, as we
89 expected. Kill the user process. */
90 printf ("%s: dying due to interrupt %#04x (%s).\n",
91 thread_name (), f->vec_no, intr_name (f->vec_no));
92 intr_dump_frame (f);
93 thread_exit ();
94
95 case SEL_KCSEG:
96 /* Kernel's code segment, which indicates a kernel bug.
97 Kernel code shouldn't throw exceptions. (Page faults
98 may cause kernel exceptions--but they shouldn't arrive
99 here.) Panic the kernel to make the point. */
100 intr_dump_frame (f);
101 PANIC ("Kernel bug - unexpected interrupt in kernel");
102
103 default:
104 /* Some other code segment? Shouldn't happen. Panic the
105 kernel. */
106 printf ("Interrupt %#04x (%s) in unknown segment %04x\n",
107 f->vec_no, intr_name (f->vec_no), f->cs);
108 thread_exit ();
109 }
110}
111
112/* Page fault handler. This is a skeleton that must be filled in
113 to implement virtual memory. Some solutions to project 2 may
114 also require modifying this code.
115
116 At entry, the address that faulted is in CR2 (Control Register
117 2) and information about the fault, formatted as described in
118 the PF_* macros in exception.h, is in F's error_code member. The
119 example code here shows how to parse that information. You
120 can find more information about both of these in the
121 description of "Interrupt 14--Page Fault Exception (#PF)" in
122 [IA32-v3a] section 5.15 "Exception and Interrupt Reference". */
123static void
124page_fault (struct intr_frame *f)
125{
126 bool not_present; /* True: not-present page, false: writing r/o page. */
127 bool write; /* True: access was write, false: access was read. */
128 bool user; /* True: access by user, false: access by kernel. */
129 void *fault_addr; /* Fault address. */
130
131 /* Obtain faulting address, the virtual address that was
132 accessed to cause the fault. It may point to code or to
133 data. It is not necessarily the address of the instruction
134 that caused the fault (that's f->eip).
135 See [IA32-v2a] "MOV--Move to/from Control Registers" and
136 [IA32-v3a] 5.15 "Interrupt 14--Page Fault Exception
137 (#PF)". */
138 asm ("movl %%cr2, %0" : "=r" (fault_addr));
139
140 /* Turn interrupts back on (they were only off so that we could
141 be assured of reading CR2 before it changed). */
142 intr_enable ();
143
144 /* Count page faults. */
145 page_fault_cnt++;
146
147 /* Determine cause. */
148 not_present = (f->error_code & PF_P) == 0;
149 write = (f->error_code & PF_W) != 0;
150 user = (f->error_code & PF_U) != 0;
151
152 /* To implement virtual memory, adapt the rest of the function
153 body, adding code that brings in the page to
154 which fault_addr refers. */
155 if (is_user_vaddr(fault_addr)) {
156 if (! user) {
157 /* syscall exception; set eax and eip */
158 f->eip = (void*)f->eax;
159 f->eax = 0xFFFFFFFF;
160 return;
161 } else {
162 /* user process access violation */
163 thread_exit();
164 }
165 } else {
166 printf ("Page fault at %p: %s error %s page in %s context.\n",
167 fault_addr,
168 not_present ? "not present" : "rights violation",
169 write ? "writing" : "reading",
170 user ? "user" : "kernel");
171 kill (f);
172 }
173}
174
diff --git a/pintos-progos/userprog/exception.h b/pintos-progos/userprog/exception.h
new file mode 100644
index 0000000..f83e615
--- /dev/null
+++ b/pintos-progos/userprog/exception.h
@@ -0,0 +1,12 @@
1#ifndef USERPROG_EXCEPTION_H
2#define USERPROG_EXCEPTION_H
3
4/* Page fault error code bits that describe the cause of the exception. */
5#define PF_P 0x1 /* 0: not-present page. 1: access rights violation. */
6#define PF_W 0x2 /* 0: read, 1: write. */
7#define PF_U 0x4 /* 0: kernel, 1: user process. */
8
9void exception_init (void);
10void exception_print_stats (void);
11
12#endif /* userprog/exception.h */
diff --git a/pintos-progos/userprog/gdt.c b/pintos-progos/userprog/gdt.c
new file mode 100644
index 0000000..e866037
--- /dev/null
+++ b/pintos-progos/userprog/gdt.c
@@ -0,0 +1,146 @@
1#include "userprog/gdt.h"
2#include <debug.h>
3#include "userprog/tss.h"
4#include "threads/palloc.h"
5#include "threads/vaddr.h"
6
7/* The Global Descriptor Table (GDT).
8
9 The GDT, an x86-specific structure, defines segments that can
10 potentially be used by all processes in a system, subject to
11 their permissions. There is also a per-process Local
12 Descriptor Table (LDT) but that is not used by modern
13 operating systems.
14
15 Each entry in the GDT, which is known by its byte offset in
16 the table, identifies a segment. For our purposes only three
17 types of segments are of interest: code, data, and TSS or
18 Task-State Segment descriptors. The former two types are
19 exactly what they sound like. The TSS is used primarily for
20 stack switching on interrupts.
21
22 For more information on the GDT as used here, refer to
23 [IA32-v3a] 3.2 "Using Segments" through 3.5 "System Descriptor
24 Types". */
25static uint64_t gdt[SEL_CNT];
26
27/* GDT helpers. */
28static uint64_t make_code_desc (int dpl);
29static uint64_t make_data_desc (int dpl);
30static uint64_t make_tss_desc (void *laddr);
31static uint64_t make_gdtr_operand (uint16_t limit, void *base);
32
33/* Sets up a proper GDT. The bootstrap loader's GDT didn't
34 include user-mode selectors or a TSS, but we need both now. */
35void
36gdt_init (void)
37{
38 uint64_t gdtr_operand;
39
40 /* Initialize GDT. */
41 gdt[SEL_NULL / sizeof *gdt] = 0;
42 gdt[SEL_KCSEG / sizeof *gdt] = make_code_desc (0);
43 gdt[SEL_KDSEG / sizeof *gdt] = make_data_desc (0);
44 gdt[SEL_UCSEG / sizeof *gdt] = make_code_desc (3);
45 gdt[SEL_UDSEG / sizeof *gdt] = make_data_desc (3);
46 gdt[SEL_TSS / sizeof *gdt] = make_tss_desc (tss_get ());
47
48 /* Load GDTR, TR. See [IA32-v3a] 2.4.1 "Global Descriptor
49 Table Register (GDTR)", 2.4.4 "Task Register (TR)", and
50 6.2.4 "Task Register". */
51 gdtr_operand = make_gdtr_operand (sizeof gdt - 1, gdt);
52 asm volatile ("lgdt %0" : : "m" (gdtr_operand));
53 asm volatile ("ltr %w0" : : "q" (SEL_TSS));
54}
55
56/* System segment or code/data segment? */
57enum seg_class
58 {
59 CLS_SYSTEM = 0, /* System segment. */
60 CLS_CODE_DATA = 1 /* Code or data segment. */
61 };
62
63/* Limit has byte or 4 kB page granularity? */
64enum seg_granularity
65 {
66 GRAN_BYTE = 0, /* Limit has 1-byte granularity. */
67 GRAN_PAGE = 1 /* Limit has 4 kB granularity. */
68 };
69
70/* Returns a segment descriptor with the given 32-bit BASE and
71 20-bit LIMIT (whose interpretation depends on GRANULARITY).
72 The descriptor represents a system or code/data segment
73 according to CLASS, and TYPE is its type (whose interpretation
74 depends on the class).
75
76 The segment has descriptor privilege level DPL, meaning that
77 it can be used in rings numbered DPL or lower. In practice,
78 DPL==3 means that user processes can use the segment and
79 DPL==0 means that only the kernel can use the segment. See
80 [IA32-v3a] 4.5 "Privilege Levels" for further discussion. */
81static uint64_t
82make_seg_desc (uint32_t base,
83 uint32_t limit,
84 enum seg_class class,
85 int type,
86 int dpl,
87 enum seg_granularity granularity)
88{
89 uint32_t e0, e1;
90
91 ASSERT (limit <= 0xfffff);
92 ASSERT (class == CLS_SYSTEM || class == CLS_CODE_DATA);
93 ASSERT (type >= 0 && type <= 15);
94 ASSERT (dpl >= 0 && dpl <= 3);
95 ASSERT (granularity == GRAN_BYTE || granularity == GRAN_PAGE);
96
97 e0 = ((limit & 0xffff) /* Limit 15:0. */
98 | (base << 16)); /* Base 15:0. */
99
100 e1 = (((base >> 16) & 0xff) /* Base 23:16. */
101 | (type << 8) /* Segment type. */
102 | (class << 12) /* 0=system, 1=code/data. */
103 | (dpl << 13) /* Descriptor privilege. */
104 | (1 << 15) /* Present. */
105 | (limit & 0xf0000) /* Limit 16:19. */
106 | (1 << 22) /* 32-bit segment. */
107 | (granularity << 23) /* Byte/page granularity. */
108 | (base & 0xff000000)); /* Base 31:24. */
109
110 return e0 | ((uint64_t) e1 << 32);
111}
112
113/* Returns a descriptor for a readable code segment with base at
114 0, a limit of 4 GB, and the given DPL. */
115static uint64_t
116make_code_desc (int dpl)
117{
118 return make_seg_desc (0, 0xfffff, CLS_CODE_DATA, 10, dpl, GRAN_PAGE);
119}
120
121/* Returns a descriptor for a writable data segment with base at
122 0, a limit of 4 GB, and the given DPL. */
123static uint64_t
124make_data_desc (int dpl)
125{
126 return make_seg_desc (0, 0xfffff, CLS_CODE_DATA, 2, dpl, GRAN_PAGE);
127}
128
129/* Returns a descriptor for an "available" 32-bit Task-State
130 Segment with its base at the given linear address, a limit of
131 0x67 bytes (the size of a 32-bit TSS), and a DPL of 0.
132 See [IA32-v3a] 6.2.2 "TSS Descriptor". */
133static uint64_t
134make_tss_desc (void *laddr)
135{
136 return make_seg_desc ((uint32_t) laddr, 0x67, CLS_SYSTEM, 9, 0, GRAN_BYTE);
137}
138
139
140/* Returns a descriptor that yields the given LIMIT and BASE when
141 used as an operand for the LGDT instruction. */
142static uint64_t
143make_gdtr_operand (uint16_t limit, void *base)
144{
145 return limit | ((uint64_t) (uint32_t) base << 16);
146}
diff --git a/pintos-progos/userprog/gdt.h b/pintos-progos/userprog/gdt.h
new file mode 100644
index 0000000..81fe50c
--- /dev/null
+++ b/pintos-progos/userprog/gdt.h
@@ -0,0 +1,15 @@
1#ifndef USERPROG_GDT_H
2#define USERPROG_GDT_H
3
4#include "threads/loader.h"
5
6/* Segment selectors.
7 More selectors are defined by the loader in loader.h. */
8#define SEL_UCSEG 0x1B /* User code selector. */
9#define SEL_UDSEG 0x23 /* User data selector. */
10#define SEL_TSS 0x28 /* Task-state segment. */
11#define SEL_CNT 6 /* Number of segments. */
12
13void gdt_init (void);
14
15#endif /* userprog/gdt.h */
diff --git a/pintos-progos/userprog/pagedir.c b/pintos-progos/userprog/pagedir.c
new file mode 100644
index 0000000..a6a87b8
--- /dev/null
+++ b/pintos-progos/userprog/pagedir.c
@@ -0,0 +1,263 @@
1#include "userprog/pagedir.h"
2#include <stdbool.h>
3#include <stddef.h>
4#include <string.h>
5#include "threads/init.h"
6#include "threads/pte.h"
7#include "threads/palloc.h"
8
9static uint32_t *active_pd (void);
10static void invalidate_pagedir (uint32_t *);
11
12/* Creates a new page directory that has mappings for kernel
13 virtual addresses, but none for user virtual addresses.
14 Returns the new page directory, or a null pointer if memory
15 allocation fails. */
16uint32_t *
17pagedir_create (void)
18{
19 uint32_t *pd = palloc_get_page (0);
20 if (pd != NULL)
21 memcpy (pd, init_page_dir, PGSIZE);
22 return pd;
23}
24
25/* Destroys page directory PD, freeing all the pages it
26 references. */
27void
28pagedir_destroy (uint32_t *pd)
29{
30 uint32_t *pde;
31
32 if (pd == NULL)
33 return;
34
35 ASSERT (pd != init_page_dir);
36 for (pde = pd; pde < pd + pd_no (PHYS_BASE); pde++)
37 if (*pde & PTE_P)
38 {
39 uint32_t *pt = pde_get_pt (*pde);
40 uint32_t *pte;
41
42 for (pte = pt; pte < pt + PGSIZE / sizeof *pte; pte++)
43 if (*pte & PTE_P)
44 palloc_free_page (pte_get_page (*pte));
45 palloc_free_page (pt);
46 }
47 palloc_free_page (pd);
48}
49
50/* Returns the address of the page table entry for virtual
51 address VADDR in page directory PD.
52 If PD does not have a page table for VADDR, behavior depends
53 on CREATE. If CREATE is true, then a new page table is
54 created and a pointer into it is returned. Otherwise, a null
55 pointer is returned. */
56static uint32_t *
57lookup_page (uint32_t *pd, const void *vaddr, bool create)
58{
59 uint32_t *pt, *pde;
60
61 ASSERT (pd != NULL);
62
63 /* Shouldn't create new kernel virtual mappings. */
64 ASSERT (!create || is_user_vaddr (vaddr));
65
66 /* Check for a page table for VADDR.
67 If one is missing, create one if requested. */
68 pde = pd + pd_no (vaddr);
69 if (*pde == 0)
70 {
71 if (create)
72 {
73 pt = palloc_get_page (PAL_ZERO);
74 if (pt == NULL)
75 return NULL;
76
77 *pde = pde_create (pt);
78 }
79 else
80 return NULL;
81 }
82
83 /* Return the page table entry. */
84 pt = pde_get_pt (*pde);
85 return &pt[pt_no (vaddr)];
86}
87
88/* Adds a mapping in page directory PD from user virtual page
89 UPAGE to the physical frame identified by kernel virtual
90 address KPAGE.
91 UPAGE must not already be mapped.
92 KPAGE should probably be a page obtained from the user pool
93 with palloc_get_page().
94 If WRITABLE is true, the new page is read/write;
95 otherwise it is read-only.
96 Returns true if successful, false if memory allocation
97 failed. */
98bool
99pagedir_set_page (uint32_t *pd, void *upage, void *kpage, bool writable)
100{
101 uint32_t *pte;
102
103 ASSERT (pg_ofs (upage) == 0);
104 ASSERT (pg_ofs (kpage) == 0);
105 ASSERT (is_user_vaddr (upage));
106 ASSERT (vtop (kpage) >> PTSHIFT < init_ram_pages);
107 ASSERT (pd != init_page_dir);
108
109 pte = lookup_page (pd, upage, true);
110
111 if (pte != NULL)
112 {
113 ASSERT ((*pte & PTE_P) == 0);
114 *pte = pte_create_user (kpage, writable);
115 return true;
116 }
117 else
118 return false;
119}
120
121/* Looks up the physical address that corresponds to user virtual
122 address UADDR in PD. Returns the kernel virtual address
123 corresponding to that physical address, or a null pointer if
124 UADDR is unmapped. */
125void *
126pagedir_get_page (uint32_t *pd, const void *uaddr)
127{
128 uint32_t *pte;
129
130 ASSERT (is_user_vaddr (uaddr));
131
132 pte = lookup_page (pd, uaddr, false);
133 if (pte != NULL && (*pte & PTE_P) != 0)
134 return pte_get_page (*pte) + pg_ofs (uaddr);
135 else
136 return NULL;
137}
138
139/* Marks user virtual page UPAGE "not present" in page
140 directory PD. Later accesses to the page will fault. Other
141 bits in the page table entry are preserved.
142 UPAGE need not be mapped. */
143void
144pagedir_clear_page (uint32_t *pd, void *upage)
145{
146 uint32_t *pte;
147
148 ASSERT (pg_ofs (upage) == 0);
149 ASSERT (is_user_vaddr (upage));
150
151 pte = lookup_page (pd, upage, false);
152 if (pte != NULL && (*pte & PTE_P) != 0)
153 {
154 *pte &= ~PTE_P;
155 invalidate_pagedir (pd);
156 }
157}
158
159/* Returns true if the PTE for virtual page VPAGE in PD is dirty,
160 that is, if the page has been modified since the PTE was
161 installed.
162 Returns false if PD contains no PTE for VPAGE. */
163bool
164pagedir_is_dirty (uint32_t *pd, const void *vpage)
165{
166 uint32_t *pte = lookup_page (pd, vpage, false);
167 return pte != NULL && (*pte & PTE_D) != 0;
168}
169
170/* Set the dirty bit to DIRTY in the PTE for virtual page VPAGE
171 in PD. */
172void
173pagedir_set_dirty (uint32_t *pd, const void *vpage, bool dirty)
174{
175 uint32_t *pte = lookup_page (pd, vpage, false);
176 if (pte != NULL)
177 {
178 if (dirty)
179 *pte |= PTE_D;
180 else
181 {
182 *pte &= ~(uint32_t) PTE_D;
183 invalidate_pagedir (pd);
184 }
185 }
186}
187
188/* Returns true if the PTE for virtual page VPAGE in PD has been
189 accessed recently, that is, between the time the PTE was
190 installed and the last time it was cleared. Returns false if
191 PD contains no PTE for VPAGE. */
192bool
193pagedir_is_accessed (uint32_t *pd, const void *vpage)
194{
195 uint32_t *pte = lookup_page (pd, vpage, false);
196 return pte != NULL && (*pte & PTE_A) != 0;
197}
198
199/* Sets the accessed bit to ACCESSED in the PTE for virtual page
200 VPAGE in PD. */
201void
202pagedir_set_accessed (uint32_t *pd, const void *vpage, bool accessed)
203{
204 uint32_t *pte = lookup_page (pd, vpage, false);
205 if (pte != NULL)
206 {
207 if (accessed)
208 *pte |= PTE_A;
209 else
210 {
211 *pte &= ~(uint32_t) PTE_A;
212 invalidate_pagedir (pd);
213 }
214 }
215}
216
217/* Loads page directory PD into the CPU's page directory base
218 register. */
219void
220pagedir_activate (uint32_t *pd)
221{
222 if (pd == NULL)
223 pd = init_page_dir;
224
225 /* Store the physical address of the page directory into CR3
226 aka PDBR (page directory base register). This activates our
227 new page tables immediately. See [IA32-v2a] "MOV--Move
228 to/from Control Registers" and [IA32-v3a] 3.7.5 "Base
229 Address of the Page Directory". */
230 asm volatile ("movl %0, %%cr3" : : "r" (vtop (pd)) : "memory");
231}
232
233/* Returns the currently active page directory. */
234static uint32_t *
235active_pd (void)
236{
237 /* Copy CR3, the page directory base register (PDBR), into
238 `pd'.
239 See [IA32-v2a] "MOV--Move to/from Control Registers" and
240 [IA32-v3a] 3.7.5 "Base Address of the Page Directory". */
241 uintptr_t pd;
242 asm volatile ("movl %%cr3, %0" : "=r" (pd));
243 return ptov (pd);
244}
245
246/* Seom page table changes can cause the CPU's translation
247 lookaside buffer (TLB) to become out-of-sync with the page
248 table. When this happens, we have to "invalidate" the TLB by
249 re-activating it.
250
251 This function invalidates the TLB if PD is the active page
252 directory. (If PD is not active then its entries are not in
253 the TLB, so there is no need to invalidate anything.) */
254static void
255invalidate_pagedir (uint32_t *pd)
256{
257 if (active_pd () == pd)
258 {
259 /* Re-activating PD clears the TLB. See [IA32-v3a] 3.12
260 "Translation Lookaside Buffers (TLBs)". */
261 pagedir_activate (pd);
262 }
263}
diff --git a/pintos-progos/userprog/pagedir.h b/pintos-progos/userprog/pagedir.h
new file mode 100644
index 0000000..cd92447
--- /dev/null
+++ b/pintos-progos/userprog/pagedir.h
@@ -0,0 +1,18 @@
1#ifndef USERPROG_PAGEDIR_H
2#define USERPROG_PAGEDIR_H
3
4#include <stdbool.h>
5#include <stdint.h>
6
7uint32_t *pagedir_create (void);
8void pagedir_destroy (uint32_t *pd);
9bool pagedir_set_page (uint32_t *pd, void *upage, void *kpage, bool rw);
10void *pagedir_get_page (uint32_t *pd, const void *upage);
11void pagedir_clear_page (uint32_t *pd, void *upage);
12bool pagedir_is_dirty (uint32_t *pd, const void *upage);
13void pagedir_set_dirty (uint32_t *pd, const void *upage, bool dirty);
14bool pagedir_is_accessed (uint32_t *pd, const void *upage);
15void pagedir_set_accessed (uint32_t *pd, const void *upage, bool accessed);
16void pagedir_activate (uint32_t *pd);
17
18#endif /* userprog/pagedir.h */
diff --git a/pintos-progos/userprog/process.c b/pintos-progos/userprog/process.c
new file mode 100644
index 0000000..adb6b66
--- /dev/null
+++ b/pintos-progos/userprog/process.c
@@ -0,0 +1,721 @@
1#include "userprog/process.h"
2#include <debug.h>
3#include <inttypes.h>
4#include <round.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include "userprog/gdt.h"
9#include "userprog/pagedir.h"
10#include "userprog/tss.h"
11#include "filesys/directory.h"
12#include "filesys/file.h"
13#include "filesys/filesys.h"
14#include "threads/flags.h"
15#include "threads/init.h"
16#include "threads/interrupt.h"
17#include "threads/palloc.h"
18#include "threads/synch.h"
19#include "threads/thread.h"
20#include "threads/vaddr.h"
21
22/* data structure to communicate with the thread initializing a new process */
23struct start_aux_data {
24 char *filename;
25 struct semaphore startup_sem;
26 struct thread *parent_thread;
27 struct process *new_process;
28};
29
30/* filesystem lock */
31struct lock filesys_lock;
32
33/* prototypes */
34static thread_func start_process NO_RETURN;
35static bool load (char *filename, void (**eip) (void), void **esp);
36static bool setup_stack (void **esp);
37static bool init_fd_table (struct fd_table * table);
38
39/* Initialize the filesystem lock */
40void
41process_init ()
42{
43 lock_init (&filesys_lock);
44}
45
46/* Get current process (only valid for processes) */
47struct process*
48process_current ()
49{
50 ASSERT (thread_current()->process != NULL);
51 return thread_current()->process;
52}
53
54/* Starts a new thread running a user program loaded from
55 `filename`.
56 The new thread may be scheduled (and may even exit)
57 before process_execute() returns. Returns the new process's
58 thread id, or TID_ERROR if the thread cannot be created.
59
60 In the first assignment, you should change this to function to
61
62 process_execute (const char *cmd)
63
64 and support command strings such as "echo A B C". You
65 will also need to change `load` and `setup_stack`. */
66tid_t
67process_execute (const char *filename)
68{
69 tid_t tid = TID_ERROR;
70 char *fn_copy = NULL;
71 struct start_aux_data *aux_data = NULL;
72
73 /* Setup the auxiliary data for starting up the new process */
74 fn_copy = palloc_get_page (0);
75 aux_data = palloc_get_page (0);
76 if (aux_data == NULL || fn_copy == NULL)
77 goto done;
78 strlcpy (fn_copy, filename, PGSIZE);
79 aux_data->filename = fn_copy;
80 aux_data->parent_thread = thread_current ();
81 aux_data->new_process = NULL;
82 sema_init (&aux_data->startup_sem, 0);
83
84 /* Create a new thread to execute FILE_NAME. */
85 tid = thread_create (fn_copy, PRI_DEFAULT, start_process, aux_data);
86 if (tid == TID_ERROR)
87 goto done;
88
89 /* wait for startup */
90 sema_down (&aux_data->startup_sem);
91 if (aux_data->new_process == NULL) {
92 tid = TID_ERROR;
93 goto done;
94 }
95 /* register child process */
96 list_push_back (&thread_current()->children,
97 &aux_data->new_process->parentelem);
98
99 done:
100 palloc_free_page (fn_copy);
101 palloc_free_page (aux_data);
102 return tid;
103}
104
105/* A thread function that loads a user process and starts it
106 running. */
107static void
108start_process (void *aux)
109{
110 struct intr_frame if_;
111 struct start_aux_data *aux_data = (struct start_aux_data*) aux;
112 struct thread *thread = thread_current ();
113
114 /* Initialize Process */
115 struct process *process = palloc_get_page (PAL_ZERO);
116 if (process == NULL)
117 goto signal;
118 sema_init (&process->exit_sem, 0);
119 lock_init (&process->exit_lock);
120 process->exit_status = -1;
121 if (! init_fd_table (&process->fd_table))
122 goto signal;
123
124 /* register process */
125 process->thread_id = thread->tid;
126 process->parent_tid = aux_data->parent_thread->tid;
127 thread->process = process;
128
129 /* Initialize interrupt frame and load executable. */
130 memset (&if_, 0, sizeof if_);
131 if_.gs = if_.fs = if_.es = if_.ds = if_.ss = SEL_UDSEG;
132 if_.cs = SEL_UCSEG;
133 if_.eflags = FLAG_IF | FLAG_MBS;
134 if (! load (aux_data->filename, &if_.eip, &if_.esp)) {
135 thread->process = NULL;
136 } else {
137 aux_data->new_process = thread->process;
138 }
139
140 signal:
141 /* Signal the parent process that loading is finished */
142 sema_up (&aux_data->startup_sem); /* transfer ownership of aux_data */
143
144 /* If process startup failed, quit. */
145 if (thread->process == NULL) {
146 if (process != NULL) {
147 if (process->fd_table.fds != NULL)
148 palloc_free_page (process->fd_table.fds);
149 palloc_free_page (process);
150 }
151 thread_exit ();
152 }
153
154 /* Start the user process by simulating a return from an
155 interrupt, implemented by intr_exit (in
156 threads/intr-stubs.S). Because intr_exit takes all of its
157 arguments on the stack in the form of a `struct intr_frame',
158 we just point the stack pointer (%esp) to our stack frame
159 and jump to it. */
160 asm volatile ("movl %0, %%esp; jmp intr_exit" : : "g" (&if_) : "memory");
161 NOT_REACHED ();
162}
163
164/* Waits for thread TID to die and returns its exit status. If
165 it was terminated by the kernel (i.e. killed due to an
166 exception), returns -1. If TID is invalid or if it was not a
167 child of the calling process, or if process_wait() has already
168 been successfully called for the given TID, returns -1
169 immediately, without waiting. */
170int
171process_wait (tid_t child_tid)
172{
173 struct thread *cur = thread_current ();
174 struct process *child = NULL;
175
176 /* iterate over child processes */
177 struct list_elem *e = list_head (&cur->children);
178 while ((e = list_next (e)) != list_end (&cur->children)) {
179 struct process* t = list_entry (e, struct process, parentelem);
180 if (t->thread_id == child_tid) {
181 list_remove (e);
182 child = t;
183 break;
184 }
185 }
186 if (child == NULL) {
187 return -1;
188 }
189 sema_down (&child->exit_sem);
190 int exit_status = child->exit_status;
191 palloc_free_page (child);
192 return exit_status;
193}
194
195/* Free the current process's resources. */
196void
197process_exit (void)
198{
199 struct thread *thread = thread_current ();
200 ASSERT (thread != NULL);
201
202 /* remove (and if necessary clean up) child processes */
203 struct list_elem *e = list_head (&thread->children);
204 while ((e = list_next (e)) != list_end (&thread->children)) {
205 struct process *p = list_entry (e, struct process, parentelem);
206 bool process_dying;
207 lock_acquire (&p->exit_lock);
208 process_dying = p->parent_tid < 0;
209 p->parent_tid = -1;
210 lock_release (&p->exit_lock);
211 if (process_dying)
212 palloc_free_page (p);
213 }
214
215 if (thread->process == NULL)
216 return; /* not a process, nothing else left to do */
217
218 struct process *proc = thread->process;
219 uint32_t *pd;
220
221 printf ("%s: exit(%d)\n", thread->name, proc->exit_status);
222
223 /* close executable, allow write */
224 if (proc->executable != NULL) {
225 lock_acquire (&filesys_lock);
226 file_close (proc->executable);
227 lock_release (&filesys_lock);
228 }
229
230 int fd;
231 for (fd = 2; fd <= proc->fd_table.fd_max; fd++) {
232 process_close_file (fd);
233 }
234 palloc_free_page (proc->fd_table.fds);
235
236 /* Destroy the current process's page directory and switch back
237 to the kernel-only page directory. */
238 pd = thread->pagedir;
239 if (pd != NULL) {
240 /* Correct ordering here is crucial. We must set
241 cur->pagedir to NULL before switching page directories,
242 so that a timer interrupt can't switch back to the
243 process page directory. We must activate the base page
244 directory before destroying the process's page
245 directory, or our active page directory will be one
246 that's been freed (and cleared). */
247 thread->pagedir = NULL;
248 pagedir_activate (NULL);
249 pagedir_destroy (pd);
250 }
251
252 /* Destroy the process structure if the parent is not alive
253 * any more. Atomic test and set would be sufficient here.
254 */
255 bool parent_dead = false;
256 lock_acquire (&proc->exit_lock);
257 parent_dead = proc->parent_tid < 0;
258 proc->parent_tid = -1;
259 lock_release (&proc->exit_lock);
260 if (parent_dead) {
261 palloc_free_page (proc);
262 } else {
263 sema_up (&proc->exit_sem);
264 }
265}
266
267/* Sets up the CPU for running user code in the current
268 thread.
269 This function is called on every context switch. */
270void
271process_activate (void)
272{
273 struct thread *t = thread_current ();
274 /* Activate thread's page tables. */
275 pagedir_activate (t->pagedir);
276
277 /* Set thread's kernel stack for use in processing
278 interrupts. */
279 tss_update ();
280}
281
282/* We load ELF binaries. The following definitions are taken
283 from the ELF specification, [ELF1], more-or-less verbatim. */
284
285/* ELF types. See [ELF1] 1-2. */
286typedef uint32_t Elf32_Word, Elf32_Addr, Elf32_Off;
287typedef uint16_t Elf32_Half;
288
289/* For use with ELF types in printf(). */
290#define PE32Wx PRIx32 /* Print Elf32_Word in hexadecimal. */
291#define PE32Ax PRIx32 /* Print Elf32_Addr in hexadecimal. */
292#define PE32Ox PRIx32 /* Print Elf32_Off in hexadecimal. */
293#define PE32Hx PRIx16 /* Print Elf32_Half in hexadecimal. */
294
295/* Executable header. See [ELF1] 1-4 to 1-8.
296 This appears at the very beginning of an ELF binary. */
297struct Elf32_Ehdr
298{
299 unsigned char e_ident[16];
300 Elf32_Half e_type;
301 Elf32_Half e_machine;
302 Elf32_Word e_version;
303 Elf32_Addr e_entry;
304 Elf32_Off e_phoff;
305 Elf32_Off e_shoff;
306 Elf32_Word e_flags;
307 Elf32_Half e_ehsize;
308 Elf32_Half e_phentsize;
309 Elf32_Half e_phnum;
310 Elf32_Half e_shentsize;
311 Elf32_Half e_shnum;
312 Elf32_Half e_shstrndx;
313};
314
315/* Program header. See [ELF1] 2-2 to 2-4.
316 There are e_phnum of these, starting at file offset e_phoff
317 (see [ELF1] 1-6). */
318struct Elf32_Phdr
319{
320 Elf32_Word p_type;
321 Elf32_Off p_offset;
322 Elf32_Addr p_vaddr;
323 Elf32_Addr p_paddr;
324 Elf32_Word p_filesz;
325 Elf32_Word p_memsz;
326 Elf32_Word p_flags;
327 Elf32_Word p_align;
328};
329
330/* Values for p_type. See [ELF1] 2-3. */
331#define PT_NULL 0 /* Ignore. */
332#define PT_LOAD 1 /* Loadable segment. */
333#define PT_DYNAMIC 2 /* Dynamic linking info. */
334#define PT_INTERP 3 /* Name of dynamic loader. */
335#define PT_NOTE 4 /* Auxiliary info. */
336#define PT_SHLIB 5 /* Reserved. */
337#define PT_PHDR 6 /* Program header table. */
338#define PT_STACK 0x6474e551 /* Stack segment. */
339
340/* Flags for p_flags. See [ELF3] 2-3 and 2-4. */
341#define PF_X 1 /* Executable. */
342#define PF_W 2 /* Writable. */
343#define PF_R 4 /* Readable. */
344
345static bool validate_segment (const struct Elf32_Phdr *, struct file *);
346static bool load_segment (struct file *file, off_t ofs, uint8_t *upage,
347 uint32_t read_bytes, uint32_t zero_bytes,
348 bool writable);
349
350/* Loads an ELF executable from file_name (the first word of
351 cmd) into the current thread.
352 Stores the executable's entry point into *EIP
353 and its initial stack pointer into *ESP.
354 Returns true if successful, false otherwise. */
355bool
356load (char *file_name, void (**eip) (void), void **esp)
357{
358 struct thread *t = thread_current ();
359 struct Elf32_Ehdr ehdr;
360 struct file *file = NULL;
361 off_t file_ofs;
362 bool success = false;
363 int i;
364
365 /* Allocate and activate page directory. */
366 t->pagedir = pagedir_create ();
367 if (t->pagedir == NULL)
368 return false;
369 process_activate ();
370
371 /* Coarse grained filesystem lock for loading */
372 lock_acquire (&filesys_lock);
373
374 /* Open executable file. */
375 file = filesys_open (file_name);
376 if (file == NULL)
377 goto done;
378
379 /* Deny writes to the file during loading */
380 file_deny_write (file);
381
382 /* Read and verify executable header. */
383 if (file_read (file, &ehdr, sizeof ehdr) != sizeof ehdr
384 || memcmp (ehdr.e_ident, "\177ELF\1\1\1", 7)
385 || ehdr.e_type != 2
386 || ehdr.e_machine != 3
387 || ehdr.e_version != 1
388 || ehdr.e_phentsize != sizeof (struct Elf32_Phdr)
389 || ehdr.e_phnum > 1024)
390 {
391 printf ("load: %s: error loading executable\n", file_name);
392 goto done;
393 }
394
395 /* Read program headers. */
396 file_ofs = ehdr.e_phoff;
397 for (i = 0; i < ehdr.e_phnum; i++)
398 {
399 struct Elf32_Phdr phdr;
400
401 if (file_ofs < 0 || file_ofs > file_length (file))
402 goto done;
403 file_seek (file, file_ofs);
404
405 if (file_read (file, &phdr, sizeof phdr) != sizeof phdr)
406 goto done;
407 file_ofs += sizeof phdr;
408 if (phdr.p_vaddr < PGSIZE)
409 continue; /* Ignore build-id segment */
410 switch (phdr.p_type)
411 {
412 case PT_NULL:
413 case PT_NOTE:
414 case PT_PHDR:
415 case PT_STACK:
416 default:
417 /* Ignore this segment. */
418 break;
419 case PT_DYNAMIC:
420 case PT_INTERP:
421 case PT_SHLIB:
422 goto done;
423 case PT_LOAD:
424 if (phdr.p_vaddr == 0)
425 break; // Ignore the .note.gnu.build-i segment
426 if (validate_segment (&phdr, file))
427 {
428 bool writable = (phdr.p_flags & PF_W) != 0;
429 uint32_t file_page = phdr.p_offset & ~PGMASK;
430 uint32_t mem_page = phdr.p_vaddr & ~PGMASK;
431 uint32_t page_offset = phdr.p_vaddr & PGMASK;
432 uint32_t read_bytes, zero_bytes;
433 if (phdr.p_filesz > 0)
434 {
435 /* Normal segment.
436 Read initial part from disk and zero the rest. */
437 read_bytes = page_offset + phdr.p_filesz;
438 zero_bytes = (ROUND_UP (page_offset + phdr.p_memsz, PGSIZE)
439 - read_bytes);
440 }
441 else
442 {
443 /* Entirely zero.
444 Don't read anything from disk. */
445 read_bytes = 0;
446 zero_bytes = ROUND_UP (page_offset + phdr.p_memsz, PGSIZE);
447 }
448 if (!load_segment (file, file_page, (void *) mem_page,
449 read_bytes, zero_bytes, writable))
450 goto done;
451 }
452 else
453 goto done;
454 break;
455 }
456 }
457
458 /* Set up stack. */
459 if (!setup_stack (esp))
460 goto done;
461
462 /* Start address. */
463 *eip = (void (*) (void)) ehdr.e_entry;
464
465 success = true;
466
467 done:
468 /* We arrive here whether the load is successful or not. */
469 if (success) {
470 process_current()->executable = file;
471 } else {
472 file_close (file);
473 }
474 lock_release (&filesys_lock);
475 return success;
476}
477
478/* load() helpers. */
479
480static bool install_page (void *upage, void *kpage, bool writable);
481
482/* Checks whether PHDR describes a valid, loadable segment in
483 FILE and returns true if so, false otherwise. */
484static bool
485validate_segment (const struct Elf32_Phdr *phdr, struct file *file)
486{
487 /* p_offset and p_vaddr must have the same page offset. */
488 if ((phdr->p_offset & PGMASK) != (phdr->p_vaddr & PGMASK))
489 return false;
490
491 /* p_offset must point within FILE. */
492 if (phdr->p_offset > (Elf32_Off) file_length (file))
493 return false;
494
495 /* p_memsz must be at least as big as p_filesz. */
496 if (phdr->p_memsz < phdr->p_filesz)
497 return false;
498
499 /* The segment must not be empty. */
500 if (phdr->p_memsz == 0)
501 return false;
502
503 /* The virtual memory region must both start and end within the
504 user address space range. */
505 if (!is_user_vaddr ((void *) phdr->p_vaddr))
506 return false;
507 if (!is_user_vaddr ((void *) (phdr->p_vaddr + phdr->p_memsz)))
508 return false;
509
510 /* The region cannot "wrap around" across the kernel virtual
511 address space. */
512 if (phdr->p_vaddr + phdr->p_memsz < phdr->p_vaddr)
513 return false;
514
515 /* Disallow mapping page 0.
516 Not only is it a bad idea to map page 0, but if we allowed
517 it then user code that passed a null pointer to system calls
518 could quite likely panic the kernel by way of null pointer
519 assertions in memcpy(), etc. */
520 if (phdr->p_vaddr < PGSIZE)
521 return false;
522
523 /* It's okay. */
524 return true;
525}
526
527/* Loads a segment starting at offset OFS in FILE at address
528 UPAGE. In total, READ_BYTES + ZERO_BYTES bytes of virtual
529 memory are initialized, as follows:
530
531 - READ_BYTES bytes at UPAGE must be read from FILE
532 starting at offset OFS.
533
534 - ZERO_BYTES bytes at UPAGE + READ_BYTES must be zeroed.
535
536 The pages initialized by this function must be writable by the
537 user process if WRITABLE is true, read-only otherwise.
538
539 Return true if successful, false if a memory allocation error
540 or disk read error occurs. */
541static bool
542load_segment (struct file *file, off_t ofs, uint8_t *upage,
543 uint32_t read_bytes, uint32_t zero_bytes, bool writable)
544{
545 ASSERT ((read_bytes + zero_bytes) % PGSIZE == 0);
546 ASSERT (pg_ofs (upage) == 0);
547 ASSERT (ofs % PGSIZE == 0);
548
549 file_seek (file, ofs);
550 while (read_bytes > 0 || zero_bytes > 0)
551 {
552 /* Calculate how to fill this page.
553 We will read PAGE_READ_BYTES bytes from FILE
554 and zero the final PAGE_ZERO_BYTES bytes. */
555 size_t page_read_bytes = read_bytes < PGSIZE ? read_bytes : PGSIZE;
556 size_t page_zero_bytes = PGSIZE - page_read_bytes;
557
558 /* Get a page of memory. */
559 uint8_t *kpage = palloc_get_page (PAL_USER);
560 if (kpage == NULL)
561 return false;
562
563 /* Load this page. */
564 if (file_read (file, kpage, page_read_bytes) != (int) page_read_bytes)
565 {
566 palloc_free_page (kpage);
567 return false;
568 }
569 memset (kpage + page_read_bytes, 0, page_zero_bytes);
570
571 /* Add the page to the process's address space. */
572 if (!install_page (upage, kpage, writable))
573 {
574 palloc_free_page (kpage);
575 return false;
576 }
577
578 /* Advance. */
579 read_bytes -= page_read_bytes;
580 zero_bytes -= page_zero_bytes;
581 upage += PGSIZE;
582 }
583 return true;
584}
585
586/* Create a minimal stack by mapping a zeroed page at the top of
587 user virtual memory.
588 You will implement this function in the Project 0.
589 Consider using `hex_dump` for debugging purposes */
590static bool
591setup_stack (void **esp)
592{
593 uint8_t *kpage = NULL;
594
595 kpage = palloc_get_page (PAL_USER | PAL_ZERO);
596 if (kpage == NULL)
597 return false;
598
599 if (! install_page (((uint8_t *) PHYS_BASE) - PGSIZE, kpage, true)) {
600 palloc_free_page (kpage);
601 return false;
602 }
603
604 /* Currently we assume that 'argc = 0' */
605 *esp = PHYS_BASE - 12;
606
607 return true;
608}
609
610/* Adds a mapping from user virtual address UPAGE to kernel
611 virtual address KPAGE to the page table.
612 If WRITABLE is true, the user process may modify the page;
613 otherwise, it is read-only.
614 UPAGE must not already be mapped.
615 KPAGE should probably be a page obtained from the user pool
616 with palloc_get_page().
617 Returns true on success, false if UPAGE is already mapped or
618 if memory allocation fails. */
619static bool
620install_page (void *upage, void *kpage, bool writable)
621{
622 struct thread *t = thread_current ();
623
624 /* Verify that there's not already a page at that virtual
625 address, then map our page there. */
626 return (pagedir_get_page (t->pagedir, upage) == NULL
627 && pagedir_set_page (t->pagedir, upage, kpage, writable));
628}
629
630static
631bool
632init_fd_table (struct fd_table *table)
633{
634 table->fds = palloc_get_page (PAL_ZERO);
635 if (table->fds == NULL)
636 return false;
637 table->fd_cap = PGSIZE / sizeof (table->fds[0]);
638 table->fd_free = 2;
639 table->fd_max = 1;
640 return true;
641}
642
643/* Open the file with the given name; returns
644 a file descriptor for this file if successful,
645 and a negative value otherwise */
646int
647process_open_file (const char* fname)
648{
649 struct fd_table *fdt = &process_current()->fd_table;
650 if (fdt->fd_free >= fdt->fd_cap)
651 return -1;
652
653 lock_acquire (&filesys_lock);
654 struct file *f = filesys_open (fname);
655 lock_release (&filesys_lock);
656
657 if (f == NULL)
658 return -1;
659
660 int fd = fdt->fd_free++;
661 fdt->fds[fd] = f;
662
663 /* update index of free/max file descriptor index*/
664 if (fd > fdt->fd_max) fdt->fd_max = fd;
665 while (fdt->fds[fdt->fd_free] != NULL) {
666 fdt->fd_free++;
667 if (fdt->fd_free >= fdt->fd_cap)
668 break;
669 }
670 return fd;
671}
672
673/* Get the file associated with the given file
674 descriptor; return NULL if no file is associated
675 with the given descriptor */
676struct file*
677process_get_file (int fd)
678{
679 struct fd_table *fdt = &process_current()->fd_table;
680 if (fd < 2 || fd >= fdt->fd_cap || ! fdt->fds[fd])
681 return NULL;
682 return fdt->fds[fd];
683}
684
685/* Acquire global lock for the filesystem */
686void process_lock_filesys (void)
687{
688 lock_acquire (&filesys_lock);
689}
690
691/* Release global filesystem lock */
692void process_unlock_filesys (void)
693{
694 lock_release (&filesys_lock);
695}
696
697/* Close the file associated with the given file
698 descriptor; returns true if close was successful */
699bool
700process_close_file (int fd)
701{
702 struct file *file = process_get_file (fd);
703 if (file == NULL)
704 return false;
705
706 lock_acquire (&filesys_lock);
707 file_close (file);
708 lock_release (&filesys_lock);
709
710 struct fd_table *fdt = &process_current()->fd_table;
711 fdt->fds[fd] = NULL;
712
713 /* update index of free/max file descriptor index*/
714 if (fd < fdt->fd_free) fdt->fd_free = fd;
715 while (fdt->fds[fdt->fd_max] == NULL) {
716 fdt->fd_max--;
717 if (fdt->fd_max < 2)
718 break;
719 }
720 return true;
721}
diff --git a/pintos-progos/userprog/process.h b/pintos-progos/userprog/process.h
new file mode 100644
index 0000000..1609801
--- /dev/null
+++ b/pintos-progos/userprog/process.h
@@ -0,0 +1,47 @@
1#ifndef USERPROG_PROCESS_H
2#define USERPROG_PROCESS_H
3
4#include "threads/thread.h"
5
6/* In the current implementation, the capacity is fixed to 1024 (PGSIZE/4) */
7struct fd_table {
8 struct file** fds;
9 int fd_free; /* lowest-index free FD table entry */
10 int fd_max; /* highest-index used FD table entry */
11 int fd_cap; /* FD table capacity */
12};
13
14struct process {
15 /* process tree */
16 tid_t thread_id;
17 tid_t parent_tid;
18 struct list_elem parentelem; /* Owned by parent */
19
20 /* communication with parent process */
21 struct semaphore exit_sem;
22 struct lock exit_lock;
23 int exit_status;
24
25 /* files */
26 struct file *executable; /* Loaded executable, if any. */
27 struct fd_table fd_table; /* File descriptor table */
28
29 /* Owned by syscall.c */
30 void* syscall_buffer;
31 size_t syscall_buffer_page_cnt;
32};
33
34void process_init (void);
35struct process* process_current (void);
36tid_t process_execute (const char *file_name);
37int process_wait (tid_t);
38void process_exit (void);
39void process_activate (void);
40
41int process_open_file(const char* fname);
42struct file* process_get_file(int fd);
43void process_lock_filesys (void);
44void process_unlock_filesys (void);
45bool process_close_file(int fd);
46
47#endif /* userprog/process.h */
diff --git a/pintos-progos/userprog/syscall.c b/pintos-progos/userprog/syscall.c
new file mode 100644
index 0000000..f8e0197
--- /dev/null
+++ b/pintos-progos/userprog/syscall.c
@@ -0,0 +1,563 @@
1#include <stdio.h>
2#include <syscall-nr.h>
3#include "devices/input.h"
4#include "devices/shutdown.h"
5#include "filesys/file.h"
6#include "filesys/filesys.h"
7#include "filesys/inode.h"
8#include "lib/string.h"
9#include "threads/interrupt.h"
10#include "threads/palloc.h"
11#include "threads/synch.h"
12#include "threads/thread.h"
13#include "threads/vaddr.h"
14#include "userprog/pagedir.h"
15#include "userprog/process.h"
16#include "userprog/syscall.h"
17
18#define STACK_SLOT_SIZE sizeof(int)
19
20/* Prototypes for Utilities */
21static int get_user (const uint8_t *uaddr);
22static bool put_user (uint8_t *udst, uint8_t byte);
23static void* memcpy_from_user (void *kaddr, void *uaddr, size_t bytes);
24static void* memcpy_to_user (void *kaddr, void *addr, size_t bytes);
25static void* copy_string_arg (void *usp, bool *segfault);
26static void free_string_arg_buf (void *kbuf);
27
28/* Reads a byte at user virtual address UADDR.
29 UADDR must be below PHYS_BASE.
30 Returns the byte value if successful, -1 if a segfault
31 occurred. */
32static int
33get_user (const uint8_t *uaddr)
34{
35 int result;
36 asm ("movl $1f, %0; " /* save eip in eax */
37 "movzbl %1, %0; " /* read byte from user memory into eax */
38 "1:" /* continue here on page fault, with eax set to -1 */
39 : "=&a" (result) : "m" (*uaddr));
40 return result;
41}
42
43/* Writes BYTE to user address UDST.
44 UDST must be below PHYS_BASE.
45 Returns true if successful, false if a segfault occurred. */
46static bool
47put_user (uint8_t *udst, uint8_t byte)
48{
49 int error_code;
50 asm ("movl $1f, %0;" /* save EIP in EAX */
51 "movb %b2, %1;" /* write byte to user memory */
52 "1:" /* continue here on page fault, with eax set to -1 */
53 : "=&a" (error_code), "=m" (*udst) : "q" (byte));
54 return error_code != -1;
55}
56
57/* Copy bytes from user space; returns NULL if a segfault
58 occured, and kaddr otherwise */
59static void*
60memcpy_from_user (void *kaddr, void *uaddr, size_t bytes)
61{
62 uint8_t* kp = kaddr;
63 size_t i;
64 if (! is_user_vaddr (uaddr) ||
65 ! is_user_vaddr (uaddr + bytes - 1))
66 return false;
67 for (i=0;i<bytes;i++) {
68 int b = get_user (uaddr+i);
69 if (b < 0)
70 break; /* segfault */
71 else
72 kp[i] = b;
73 }
74 if (i != bytes)
75 return NULL; /* segfault */
76 return kaddr;
77}
78
79/* Copy bytes to user space; returns NULL if a segfault
80 occured, and uaddr otherwise */
81static void*
82memcpy_to_user (void *uaddr, void *kaddr, size_t bytes)
83{
84 uint8_t* kp = kaddr;
85 size_t i;
86 if (! is_user_vaddr (uaddr) ||
87 ! is_user_vaddr (uaddr + bytes - 1))
88 return false;
89 for (i=0;i<bytes;i++) {
90 if (! put_user (uaddr+i, kp[i]))
91 break; /* segfault */
92 }
93 if (i != bytes)
94 return NULL; /* segfault */
95 return uaddr;
96}
97
98/* Copy string (at most cnt bytes) from user memory to the kernel address
99 `kaddr`. The number of bytes copied is returned; 0 indicates a segfault. */
100static size_t
101strncpy_from_user (void *kaddr, void *uaddr, size_t cnt)
102{
103 uint8_t *kp = (uint8_t*) kaddr;
104 size_t i;
105 int c = 1;
106 for (i = 0; i < cnt && c; i++) {
107 if (! is_user_vaddr (uaddr+i))
108 return 0; /* sefault */
109 c = get_user (uaddr+i);
110 if (c < 0)
111 return 0; /* segfault */
112 kp[i] = c;
113 }
114 return i;
115}
116
117/* Copy a value of scalar type `typeof(*kdst)` from user space pointer
118 `usrc` to kernel space variable pointed to by `kdst`. Returns
119 false on segfault, and true otherwise. */
120#define copy_from_user(kdst, usrc) \
121 (memcpy_from_user (kdst, usrc, sizeof (*kdst)) != NULL)
122
123/* Takes user space stack pointer, which points to a string
124 in user space. Copies that string (at most PGSIZE bytes)
125 to a freshly allocated buffer in kernel space. Reurns NULL on
126 error; on a segfault, additionally sets `segfault` to true */
127static void*
128copy_string_arg (void *usp, bool *segfault)
129{
130 size_t bytes_copied;
131 char *uptr;
132 char *kpage;
133 if (! copy_from_user (&uptr, usp)) {
134 *segfault = true;
135 return NULL;
136 }
137 kpage = palloc_get_page (PAL_ZERO);
138 if (kpage == NULL)
139 return NULL;
140 bytes_copied = strncpy_from_user (kpage, uptr, PGSIZE);
141 if (bytes_copied == 0) {
142 *segfault = true;
143 palloc_free_page (kpage);
144 return NULL;
145 }
146 if (kpage[bytes_copied-1] != '\0') {
147 palloc_free_page (kpage);
148 return NULL;
149 };
150 return kpage;
151}
152
153/* free buffer allocated by `copy_string_arg` */
154static void free_string_arg_buf (void *kbuf)
155{
156 palloc_free_page (kbuf);
157}
158
159/* Stack slot address */
160#define STACK_ADDR(sp, slot) (sp + STACK_SLOT_SIZE*slot)
161
162/* syscall handler prototype */
163static void syscall_handler (struct intr_frame *);
164
165typedef int (handler)(void *sp, bool *segfault);
166
167/* Prototypes for syscall handlers */
168static handler
169 syscall_halt,
170 syscall_exit,
171 syscall_write,
172 syscall_wait ,
173 syscall_exec,
174 syscall_create,
175 syscall_remove,
176 syscall_open,
177 syscall_filesize,
178 syscall_read,
179 syscall_seek,
180 syscall_tell,
181 syscall_close;
182
183/* Register syscall_handler for interrupt 0x30 */
184void
185syscall_init (void)
186{
187 intr_register_int (0x30, 3, INTR_ON, syscall_handler, "syscall");
188}
189
190/* Syscall handler, delegating known syscalls to the respective implementations */
191static void
192syscall_handler (struct intr_frame *f)
193{
194 int syscall_nr;
195 handler* fp;
196 bool segfault = false;
197 int result;
198 void *sp = f->esp;
199
200 /* The system call number and the arguments are on the stack */
201 if (! copy_from_user (&syscall_nr,sp))
202 goto fail;
203 switch (syscall_nr) {
204 case SYS_HALT: fp = syscall_halt; break;
205 case SYS_EXIT: fp = syscall_exit; break;
206 case SYS_EXEC: fp = syscall_exec; break;
207 case SYS_WAIT: fp = syscall_wait; break;
208 case SYS_CREATE: fp = syscall_create; break;
209 case SYS_REMOVE: fp = syscall_remove; break;
210 case SYS_OPEN: fp = syscall_open; break;
211 case SYS_FILESIZE: fp = syscall_filesize; break;
212 case SYS_READ: fp = syscall_read; break;
213 case SYS_WRITE: fp = syscall_write; break;
214 case SYS_SEEK: fp = syscall_seek; break;
215 case SYS_TELL: fp = syscall_tell; break;
216 case SYS_CLOSE: fp = syscall_close; break;
217 default:
218 goto fail;
219 }
220 result = fp (sp, &segfault);
221 if (segfault)
222 goto fail;
223 f->eax = result;
224 return;
225
226 fail:
227 process_current()->exit_status = -1;
228 thread_exit ();
229}
230
231/* Shutdown machine */
232static int
233syscall_halt (void *sp UNUSED, bool *segfault UNUSED)
234{
235 shutdown ();
236 NOT_REACHED ();
237}
238
239/* Exit current process with given exit code */
240static int
241syscall_exit (void *sp, bool *segfault)
242{
243 int exit_status;
244 if (! copy_from_user (&exit_status, STACK_ADDR (sp,1))) {
245 *segfault = true;
246 return -1;
247 }
248 process_current()->exit_status = exit_status;
249 thread_exit ();
250 NOT_REACHED ();
251}
252
253/* Spawn new process executing the supplied command */
254static int
255syscall_exec (void *sp, bool *segfault)
256{
257 char *kbuf;
258 int result = TID_ERROR;
259 if ((kbuf = copy_string_arg (STACK_ADDR (sp, 1), segfault)) != NULL) {
260 result = process_execute (kbuf);
261 free_string_arg_buf (kbuf);
262 }
263 return result;
264}
265
266/* Wait until specified process exits */
267static int
268syscall_wait (void *sp, bool *segfault)
269{
270 tid_t arg;
271 if (! copy_from_user (&arg, STACK_ADDR (sp,1))) {
272 *segfault = true;
273 return 0;
274 }
275 return process_wait (arg);
276}
277
278/* Create a new file with given initial size */
279static int
280syscall_create (void *sp, bool *segfault)
281{
282 bool success = false;
283 char *fname;
284 int initial_size;
285
286 if (! copy_from_user (&initial_size, STACK_ADDR (sp,2))) {
287 *segfault = true;
288 return false;
289 }
290 if ((fname = copy_string_arg (STACK_ADDR (sp, 1), segfault)) == NULL)
291 return false;
292
293 process_lock_filesys ();
294 success = filesys_create (fname, initial_size);
295 process_unlock_filesys ();
296 free_string_arg_buf (fname);
297 return success;
298}
299
300/* Remove name file, returns true if successful */
301static int
302syscall_remove (void *sp, bool *segfault)
303{
304 bool success;
305 char *fname;
306
307 if ((fname = copy_string_arg (STACK_ADDR (sp, 1), segfault)) == NULL)
308 return false;
309 process_lock_filesys ();
310 success = filesys_remove (fname);
311 process_unlock_filesys ();
312 free_string_arg_buf (fname);
313 return (int)success;
314}
315
316/* Open file, returning non-negative file descriptor if successful */
317static int
318syscall_open (void *sp, bool *segfault)
319{
320 char *fname;
321 int fd;
322 if ((fname = copy_string_arg (STACK_ADDR (sp, 1), segfault)) == NULL)
323 return false;
324 fd = process_open_file (fname);
325 free_string_arg_buf (fname);
326 return fd;
327}
328
329/* Return size of file described by file descriptor */
330static int
331syscall_filesize (void *sp, bool *segfault)
332{
333 int fd;
334 struct file *f;
335 int size;
336
337 if (! copy_from_user (&fd, STACK_ADDR (sp,1))) {
338 *segfault = true;
339 return -1;
340 }
341 if ((f = process_get_file (fd)) == NULL)
342 return -1;
343 process_lock_filesys ();
344 size = inode_length (file_get_inode (f));
345 process_unlock_filesys ();
346 return size;
347}
348
349/* Read bytes from the file referenced by the given file
350 descriptor into the supplied user space buffer, returning
351 number of bytes read. */
352static int
353syscall_read (void *sp, bool *segfault)
354{
355 int fd;
356 uint8_t *user_buffer;
357 size_t size, bytes_to_read;
358
359 /* get arguments */
360 if (! copy_from_user (&fd, STACK_ADDR (sp,1)) ||
361 ! copy_from_user (&user_buffer, STACK_ADDR (sp, 2)) ||
362 ! copy_from_user (&size, STACK_ADDR (sp,3))) {
363 *segfault = true;
364 return -1;
365 }
366
367 /* ensure buffer is in user space */
368 if (! is_user_vaddr (user_buffer) ||
369 ! is_user_vaddr (user_buffer + size - 1)) {
370 *segfault = true;
371 return -1;
372 }
373
374 bytes_to_read = size;
375 /* handle stdin */
376 if (fd == STDIN_FILENO) {
377 char c;
378 while (bytes_to_read--) {
379 c = input_getc ();
380 if (! put_user (user_buffer++, c)) {
381 *segfault = true;
382 return -1;
383 }
384 }
385 return size;
386 }
387 /* get file */
388 struct file *file = process_get_file (fd);
389 if (file == NULL)
390 return -1;
391
392 char *kbuf = palloc_get_page (0);
393 if (kbuf == NULL)
394 return -1;
395
396 /* read loop */
397 do {
398 int bytes_read;
399 int blocksize = bytes_to_read;
400 if (bytes_to_read > PGSIZE)
401 blocksize = PGSIZE;
402
403 /* read bytes */
404 process_lock_filesys ();
405 bytes_read = file_read (file, kbuf, blocksize);
406 process_unlock_filesys ();
407
408 /* Stop when EOF has been reached */
409 if (bytes_read == 0)
410 break;
411 bytes_to_read -= bytes_read;
412 if (! memcpy_to_user (user_buffer, kbuf, bytes_read)) {
413 *segfault = true;
414 break;
415 }
416 user_buffer += bytes_read;
417 } while (bytes_to_read > 0);
418
419 palloc_free_page (kbuf);
420 return size - bytes_to_read;
421}
422
423/* Write bytes from user buffer into the specified
424 file, returning number of bytes written. */
425static int
426syscall_write (void *sp, bool *segfault)
427{
428 int fd;
429 size_t size, bytes_to_write;
430 char *user_buffer;
431
432 /* get arguments */
433 if (! copy_from_user (&fd, STACK_ADDR (sp,1)) ||
434 ! copy_from_user (&user_buffer, STACK_ADDR (sp, 2)) ||
435 ! copy_from_user (&size, STACK_ADDR (sp,3))) {
436 *segfault = true;
437 return -1;
438 }
439
440 /* ensure buffer is in user space */
441 if (! is_user_vaddr (user_buffer) ||
442 ! is_user_vaddr (user_buffer + size - 1)) {
443 *segfault = true;
444 return -1;
445 }
446
447 /* get file handle */
448 struct file *file = NULL;
449 if (fd != STDOUT_FILENO) {
450 file = process_get_file (fd);
451 if (file == NULL)
452 return -1;
453 }
454
455 /* allocate kernel buffer */
456 char *kbuf = palloc_get_page (0);
457 if (kbuf == NULL)
458 return -1;
459
460 /* write loop */
461 bytes_to_write = size;
462 do {
463 int blocksize = bytes_to_write;
464 if (bytes_to_write > PGSIZE)
465 blocksize = PGSIZE;
466 if (memcpy_from_user (kbuf, user_buffer, blocksize) == NULL) {
467 *segfault = true;
468 break;
469 }
470 if (fd == STDOUT_FILENO) {
471 putbuf (kbuf, blocksize);
472 bytes_to_write -= blocksize;
473 } else {
474 int bytes_written = 0;
475 int bytes_left_filesys = blocksize;
476
477 process_lock_filesys ();
478 while (bytes_left_filesys > 0) {
479 bytes_written = file_write (file, kbuf, bytes_left_filesys);
480 if (bytes_written <= 0) {
481 break;
482 }
483 bytes_left_filesys -= bytes_written;
484 }
485 process_unlock_filesys ();
486
487 if (bytes_written <= 0)
488 break;
489 bytes_to_write -= blocksize;
490 }
491 user_buffer += blocksize;
492 } while (bytes_to_write > 0);
493
494 /* return bytes written */
495 palloc_free_page (kbuf);
496 return size - bytes_to_write;
497}
498
499/* Change the position where the next byte will be read or written */
500static int
501syscall_seek (void *sp, bool *segfault)
502{
503 int fd;
504 off_t new_pos;
505
506 /* get arguments */
507 if (! copy_from_user (&fd, STACK_ADDR (sp,1)) ||
508 ! copy_from_user (&new_pos, STACK_ADDR (sp, 2))) {
509 *segfault = true;
510 return 0;
511 }
512
513 /* no way to return something sensible (void function) */
514 struct file *file = process_get_file (fd);
515 if (file == NULL)
516 return 0;
517
518 process_lock_filesys ();
519 file_seek (file, new_pos);
520 process_unlock_filesys ();
521 return 0;
522}
523
524/* Returns the position of the next byte to be read or written */
525static int
526syscall_tell (void *sp, bool *segfault)
527{
528 int fd;
529 unsigned r = 0;
530
531 /* get arguments */
532 if (! copy_from_user (&fd, STACK_ADDR (sp,1))) {
533 *segfault = true;
534 return 0;
535 }
536
537 /* no way to return something sensible function */
538 struct file *file = process_get_file (fd);
539 if (file == NULL)
540 return 0;
541
542 process_lock_filesys ();
543 r = file_tell (file);
544 process_unlock_filesys ();
545 return r;
546}
547
548/* Close the given file */
549static int
550syscall_close (void *sp, bool *segfault)
551{
552 int fd;
553
554 /* get arguments */
555 if (! copy_from_user (&fd, STACK_ADDR (sp,1))) {
556 *segfault = true;
557 return 0;
558 }
559
560 /* no way to return something sensible function (void) */
561 (void) process_close_file (fd);
562 return 0;
563}
diff --git a/pintos-progos/userprog/syscall.h b/pintos-progos/userprog/syscall.h
new file mode 100644
index 0000000..f7ab2f3
--- /dev/null
+++ b/pintos-progos/userprog/syscall.h
@@ -0,0 +1,5 @@
1#ifndef USERPROG_SYSCALL_H
2#define USERPROG_SYSCALL_H
3
4void syscall_init (void);
5#endif /* userprog/syscall.h */
diff --git a/pintos-progos/userprog/tss.c b/pintos-progos/userprog/tss.c
new file mode 100644
index 0000000..f8ed9a9
--- /dev/null
+++ b/pintos-progos/userprog/tss.c
@@ -0,0 +1,106 @@
1#include "userprog/tss.h"
2#include <debug.h>
3#include <stddef.h>
4#include "userprog/gdt.h"
5#include "threads/thread.h"
6#include "threads/palloc.h"
7#include "threads/vaddr.h"
8
9/* The Task-State Segment (TSS).
10
11 Instances of the TSS, an x86-specific structure, are used to
12 define "tasks", a form of support for multitasking built right
13 into the processor. However, for various reasons including
14 portability, speed, and flexibility, most x86 OSes almost
15 completely ignore the TSS. We are no exception.
16
17 Unfortunately, there is one thing that can only be done using
18 a TSS: stack switching for interrupts that occur in user mode.
19 When an interrupt occurs in user mode (ring 3), the processor
20 consults the ss0 and esp0 members of the current TSS to
21 determine the stack to use for handling the interrupt. Thus,
22 we must create a TSS and initialize at least these fields, and
23 this is precisely what this file does.
24
25 When an interrupt is handled by an interrupt or trap gate
26 (which applies to all interrupts we handle), an x86 processor
27 works like this:
28
29 - If the code interrupted by the interrupt is in the same
30 ring as the interrupt handler, then no stack switch takes
31 place. This is the case for interrupts that happen when
32 we're running in the kernel. The contents of the TSS are
33 irrelevant for this case.
34
35 - If the interrupted code is in a different ring from the
36 handler, then the processor switches to the stack
37 specified in the TSS for the new ring. This is the case
38 for interrupts that happen when we're in user space. It's
39 important that we switch to a stack that's not already in
40 use, to avoid corruption. Because we're running in user
41 space, we know that the current process's kernel stack is
42 not in use, so we can always use that. Thus, when the
43 scheduler switches threads, it also changes the TSS's
44 stack pointer to point to the new thread's kernel stack.
45 (The call is in thread_schedule_tail() in thread.c.)
46
47 See [IA32-v3a] 6.2.1 "Task-State Segment (TSS)" for a
48 description of the TSS. See [IA32-v3a] 5.12.1 "Exception- or
49 Interrupt-Handler Procedures" for a description of when and
50 how stack switching occurs during an interrupt. */
51struct tss
52 {
53 uint16_t back_link, :16;
54 void *esp0; /* Ring 0 stack virtual address. */
55 uint16_t ss0, :16; /* Ring 0 stack segment selector. */
56 void *esp1;
57 uint16_t ss1, :16;
58 void *esp2;
59 uint16_t ss2, :16;
60 uint32_t cr3;
61 void (*eip) (void);
62 uint32_t eflags;
63 uint32_t eax, ecx, edx, ebx;
64 uint32_t esp, ebp, esi, edi;
65 uint16_t es, :16;
66 uint16_t cs, :16;
67 uint16_t ss, :16;
68 uint16_t ds, :16;
69 uint16_t fs, :16;
70 uint16_t gs, :16;
71 uint16_t ldt, :16;
72 uint16_t trace, bitmap;
73 };
74
75/* Kernel TSS. */
76static struct tss *tss;
77
78/* Initializes the kernel TSS. */
79void
80tss_init (void)
81{
82 /* Our TSS is never used in a call gate or task gate, so only a
83 few fields of it are ever referenced, and those are the only
84 ones we initialize. */
85 tss = palloc_get_page (PAL_ASSERT | PAL_ZERO);
86 tss->ss0 = SEL_KDSEG;
87 tss->bitmap = 0xdfff;
88 tss_update ();
89}
90
91/* Returns the kernel TSS. */
92struct tss *
93tss_get (void)
94{
95 ASSERT (tss != NULL);
96 return tss;
97}
98
99/* Sets the ring 0 stack pointer in the TSS to point to the end
100 of the thread stack. */
101void
102tss_update (void)
103{
104 ASSERT (tss != NULL);
105 tss->esp0 = (uint8_t *) thread_current () + PGSIZE;
106}
diff --git a/pintos-progos/userprog/tss.h b/pintos-progos/userprog/tss.h
new file mode 100644
index 0000000..467bd19
--- /dev/null
+++ b/pintos-progos/userprog/tss.h
@@ -0,0 +1,11 @@
1#ifndef USERPROG_TSS_H
2#define USERPROG_TSS_H
3
4#include <stdint.h>
5
6struct tss;
7void tss_init (void);
8struct tss *tss_get (void);
9void tss_update (void);
10
11#endif /* userprog/tss.h */