aboutsummaryrefslogtreecommitdiffstats
path: root/src/userprog
diff options
context:
space:
mode:
Diffstat (limited to 'src/userprog')
-rw-r--r--src/userprog/.cvsignore3
-rw-r--r--src/userprog/Make.vars8
-rw-r--r--src/userprog/Makefile1
-rw-r--r--src/userprog/bochsrc.txt13
-rw-r--r--src/userprog/exception.c161
-rw-r--r--src/userprog/exception.h12
-rw-r--r--src/userprog/flist.c4
-rw-r--r--src/userprog/flist.h35
-rw-r--r--src/userprog/gdt.c146
-rw-r--r--src/userprog/gdt.h15
-rw-r--r--src/userprog/load.c381
-rw-r--r--src/userprog/load.h7
-rw-r--r--src/userprog/pagedir.c263
-rw-r--r--src/userprog/pagedir.h18
-rw-r--r--src/userprog/plist.c3
-rw-r--r--src/userprog/plist.h33
-rw-r--r--src/userprog/process.c275
-rw-r--r--src/userprog/process.h23
-rw-r--r--src/userprog/syscall.c61
-rw-r--r--src/userprog/syscall.h6
-rw-r--r--src/userprog/test.c66
-rw-r--r--src/userprog/tss.c106
-rw-r--r--src/userprog/tss.h11
23 files changed, 1651 insertions, 0 deletions
diff --git a/src/userprog/.cvsignore b/src/userprog/.cvsignore
new file mode 100644
index 0000000..6d5357c
--- /dev/null
+++ b/src/userprog/.cvsignore
@@ -0,0 +1,3 @@
+build
+bochsrc.txt
+bochsout.txt
diff --git a/src/userprog/Make.vars b/src/userprog/Make.vars
new file mode 100644
index 0000000..4335438
--- /dev/null
+++ b/src/userprog/Make.vars
@@ -0,0 +1,8 @@
+# -*- makefile -*-
+
+os.dsk: DEFINES += -DUSERPROG -DFILESYS
+KERNEL_SUBDIRS = threads devices lib lib/kernel userprog filesys
+TEST_SUBDIRS = tests/klaar tests/userprog tests/filesys/base
+# tests/userprog/no-vm
+GRADING_FILE = $(SRCDIR)/tests/userprog/Grading
+SIMULATOR = --qemu
diff --git a/src/userprog/Makefile b/src/userprog/Makefile
new file mode 100644
index 0000000..34c10aa
--- /dev/null
+++ b/src/userprog/Makefile
@@ -0,0 +1 @@
+include ../Makefile.kernel
diff --git a/src/userprog/bochsrc.txt b/src/userprog/bochsrc.txt
new file mode 100644
index 0000000..ea21837
--- /dev/null
+++ b/src/userprog/bochsrc.txt
@@ -0,0 +1,13 @@
+romimage: file=$BXSHARE/BIOS-bochs-latest, address=0xf0000
+vgaromimage: file=$BXSHARE/VGABIOS-lgpl-latest
+boot: disk
+cpu: ips=1000000
+megs: 4
+log: bochsout.txt
+panic: action=fatal
+clock: sync=none, time0=0
+ata0-master: type=disk, path=/tmp/DcPkTwrKiK.dsk, mode=flat, cylinders=1, heads=16, spt=63, translation=none
+ata0-slave: type=disk, path=fs.dsk, mode=flat, cylinders=4, heads=16, spt=63, translation=none
+ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15
+ata1-master: type=disk, path=/tmp/GFsmC9D5kP.dsk, mode=flat, cylinders=1, heads=16, spt=63, translation=none
+com1: enabled=1, mode=term, dev=/dev/stdout
diff --git a/src/userprog/exception.c b/src/userprog/exception.c
new file mode 100644
index 0000000..3865dc2
--- /dev/null
+++ b/src/userprog/exception.c
@@ -0,0 +1,161 @@
+#include "userprog/exception.h"
+#include <inttypes.h>
+#include <stdio.h>
+#include "userprog/gdt.h"
+#include "threads/interrupt.h"
+#include "threads/thread.h"
+
+/* Number of page faults processed. */
+static long long page_fault_cnt;
+
+static void kill (struct intr_frame *);
+static void page_fault (struct intr_frame *);
+
+/* Registers handlers for interrupts that can be caused by user
+ programs.
+
+ In a real Unix-like OS, most of these interrupts would be
+ passed along to the user process in the form of signals, as
+ described in [SV-386] 3-24 and 3-25, but we don't implement
+ signals. Instead, we'll make them simply kill the user
+ process.
+
+ Page faults are an exception. Here they are treated the same
+ way as other exceptions, but this will need to change to
+ implement virtual memory.
+
+ Refer to [IA32-v3a] section 5.15 "Exception and Interrupt
+ Reference" for a description of each of these exceptions. */
+void
+exception_init (void)
+{
+ /* These exceptions can be raised explicitly by a user program,
+ e.g. via the INT, INT3, INTO, and BOUND instructions. Thus,
+ we set DPL==3, meaning that user programs are allowed to
+ invoke them via these instructions. */
+ intr_register_int (3, 3, INTR_ON, kill, "#BP Breakpoint Exception");
+ intr_register_int (4, 3, INTR_ON, kill, "#OF Overflow Exception");
+ intr_register_int (5, 3, INTR_ON, kill,
+ "#BR BOUND Range Exceeded Exception");
+
+ /* These exceptions have DPL==0, preventing user processes from
+ invoking them via the INT instruction. They can still be
+ caused indirectly, e.g. #DE can be caused by dividing by
+ 0. */
+ intr_register_int (0, 0, INTR_ON, kill, "#DE Divide Error");
+ intr_register_int (1, 0, INTR_ON, kill, "#DB Debug Exception");
+ intr_register_int (6, 0, INTR_ON, kill, "#UD Invalid Opcode Exception");
+ intr_register_int (7, 0, INTR_ON, kill,
+ "#NM Device Not Available Exception");
+ intr_register_int (11, 0, INTR_ON, kill, "#NP Segment Not Present");
+ intr_register_int (12, 0, INTR_ON, kill, "#SS Stack Fault Exception");
+ intr_register_int (13, 0, INTR_ON, kill, "#GP General Protection Exception");
+ intr_register_int (16, 0, INTR_ON, kill, "#MF x87 FPU Floating-Point Error");
+ intr_register_int (19, 0, INTR_ON, kill,
+ "#XF SIMD Floating-Point Exception");
+
+ /* Most exceptions can be handled with interrupts turned on.
+ We need to disable interrupts for page faults because the
+ fault address is stored in CR2 and needs to be preserved. */
+ intr_register_int (14, 0, INTR_OFF, page_fault, "#PF Page-Fault Exception");
+}
+
+/* Prints exception statistics. */
+void
+exception_print_stats (void)
+{
+ printf ("Exception: %lld page faults\n", page_fault_cnt);
+}
+
+/* Handler for an exception (probably) caused by a user process. */
+static void
+kill (struct intr_frame *f)
+{
+ /* This interrupt is one (probably) caused by a user process.
+ For example, the process might have tried to access unmapped
+ virtual memory (a page fault). For now, we simply kill the
+ user process. Later, we'll want to handle page faults in
+ the kernel. Real Unix-like operating systems pass most
+ exceptions back to the process via signals, but we don't
+ implement them. */
+
+ /* The interrupt frame's code segment value tells us where the
+ exception originated. */
+ switch (f->cs)
+ {
+ case SEL_UCSEG:
+ /* User's code segment, so it's a user exception, as we
+ expected. Kill the user process. */
+ printf ("%s: dying due to interrupt %#04x (%s).\n",
+ thread_name (), f->vec_no, intr_name (f->vec_no));
+ intr_dump_frame (f);
+ thread_exit ();
+
+ case SEL_KCSEG:
+ /* Kernel's code segment, which indicates a kernel bug.
+ Kernel code shouldn't throw exceptions. (Page faults
+ may cause kernel exceptions--but they shouldn't arrive
+ here.) Panic the kernel to make the point. */
+ intr_dump_frame (f);
+ PANIC ("Kernel bug - unexpected interrupt in kernel");
+
+ default:
+ /* Some other code segment? Shouldn't happen. Panic the
+ kernel. */
+ printf ("Interrupt %#04x (%s) in unknown segment %04x\n",
+ f->vec_no, intr_name (f->vec_no), f->cs);
+ thread_exit ();
+ }
+}
+
+/* Page fault handler. This is a skeleton that must be filled in to
+ implement virtual memory. Some solutions to system call
+ implementation may also require modifying this code.
+
+ At entry, the address that faulted is in CR2 (Control Register
+ 2) and information about the fault, formatted as described in
+ the PF_* macros in exception.h, is in F's error_code member. The
+ example code here shows how to parse that information. You
+ can find more information about both of these in the
+ description of "Interrupt 14--Page Fault Exception (#PF)" in
+ [IA32-v3a] section 5.15 "Exception and Interrupt Reference". */
+static void
+page_fault (struct intr_frame *f)
+{
+ bool not_present; /* True: not-present page, false: writing r/o page. */
+ bool write; /* True: access was write, false: access was read. */
+ bool user; /* True: access by user, false: access by kernel. */
+ void *fault_addr; /* Fault address. */
+
+ /* Obtain faulting address, the virtual address that was
+ accessed to cause the fault. It may point to code or to
+ data. It is not necessarily the address of the instruction
+ that caused the fault (that's f->eip).
+ See [IA32-v2a] "MOV--Move to/from Control Registers" and
+ [IA32-v3a] 5.15 "Interrupt 14--Page Fault Exception
+ (#PF)". */
+ asm ("movl %%cr2, %0" : "=r" (fault_addr));
+
+ /* Turn interrupts back on (they were only off so that we could
+ be assured of reading CR2 before it changed). */
+ intr_enable ();
+
+ /* Count page faults. */
+ page_fault_cnt++;
+
+ /* Determine cause. */
+ not_present = (f->error_code & PF_P) == 0;
+ write = (f->error_code & PF_W) != 0;
+ user = (f->error_code & PF_U) != 0;
+
+ /* To implement virtual memory, delete the rest of the function
+ body, and replace it with code that brings in the page to
+ which fault_addr refers. */
+ printf ("Page fault at %p: %s error %s page in %s context.\n",
+ fault_addr,
+ not_present ? "not present" : "rights violation",
+ write ? "writing" : "reading",
+ user ? "user" : "kernel");
+ kill (f);
+}
+
diff --git a/src/userprog/exception.h b/src/userprog/exception.h
new file mode 100644
index 0000000..f83e615
--- /dev/null
+++ b/src/userprog/exception.h
@@ -0,0 +1,12 @@
+#ifndef USERPROG_EXCEPTION_H
+#define USERPROG_EXCEPTION_H
+
+/* Page fault error code bits that describe the cause of the exception. */
+#define PF_P 0x1 /* 0: not-present page. 1: access rights violation. */
+#define PF_W 0x2 /* 0: read, 1: write. */
+#define PF_U 0x4 /* 0: kernel, 1: user process. */
+
+void exception_init (void);
+void exception_print_stats (void);
+
+#endif /* userprog/exception.h */
diff --git a/src/userprog/flist.c b/src/userprog/flist.c
new file mode 100644
index 0000000..e88a087
--- /dev/null
+++ b/src/userprog/flist.c
@@ -0,0 +1,4 @@
+#include <stddef.h>
+
+#include "flist.h"
+
diff --git a/src/userprog/flist.h b/src/userprog/flist.h
new file mode 100644
index 0000000..3fcd1d5
--- /dev/null
+++ b/src/userprog/flist.h
@@ -0,0 +1,35 @@
+#ifndef _MAP_H_
+#define _MAP_H_
+
+/* Place functions to handle a process open files here (file list).
+
+ flist.h : Your function declarations and documentation.
+ flist.c : Your implementation.
+
+ The following is strongly recommended:
+
+ - A function that given a file (struct file*, see filesys/file.h)
+ and a process id INSERT this in a list of files. Return an
+ integer that can be used to find the opened file later.
+
+ - A function that given an integer (obtained from above function)
+ and a process id FIND the file in a list. Should return NULL if
+ the specified process did not insert the file or already removed
+ it.
+
+ - A function that given an integer (obtained from above function)
+ and a process id REMOVE the file from a list. Should return NULL
+ if the specified process did not insert the file or already
+ removed it.
+
+ - A function that given a process id REMOVE ALL files the specified
+ process have in the list.
+
+ All files obtained from filesys/filesys.c:filesys_open() are
+ considered OPEN files and must be added to a list or else kept
+ track of, to guarantee ALL open files are eventyally CLOSED
+ (probably when removed from the list(s)).
+ */
+
+
+#endif
diff --git a/src/userprog/gdt.c b/src/userprog/gdt.c
new file mode 100644
index 0000000..a7423b7
--- /dev/null
+++ b/src/userprog/gdt.c
@@ -0,0 +1,146 @@
+#include "userprog/gdt.h"
+#include <debug.h>
+#include "userprog/tss.h"
+#include "threads/palloc.h"
+#include "threads/vaddr.h"
+
+/* The Global Descriptor Table (GDT).
+
+ The GDT, an x86-specific structure, defines segments that can
+ potentially be used by all processes in a system, subject to
+ their permissions. There is also a per-process Local
+ Descriptor Table (LDT) but that is not used by modern
+ operating systems.
+
+ Each entry in the GDT, which is known by its byte offset in
+ the table, identifies a segment. For our purposes only three
+ types of segments are of interest: code, data, and TSS or
+ Task-State Segment descriptors. The former two types are
+ exactly what they sound like. The TSS is used primarily for
+ stack switching on interrupts.
+
+ For more information on the GDT as used here, refer to
+ [IA32-v3a] 3.2 "Using Segments" through 3.5 "System Descriptor
+ Types". */
+static uint64_t gdt[SEL_CNT];
+
+/* GDT helpers. */
+static uint64_t make_code_desc (int dpl);
+static uint64_t make_data_desc (int dpl);
+static uint64_t make_tss_desc (void *laddr);
+static uint64_t make_gdtr_operand (uint16_t limit, void *base);
+
+/* Sets up a proper GDT. The bootstrap loader's GDT didn't
+ include user-mode selectors or a TSS, but we need both now. */
+void
+gdt_init (void)
+{
+ uint64_t gdtr_operand;
+
+ /* Initialize GDT. */
+ gdt[SEL_NULL / sizeof *gdt] = 0;
+ gdt[SEL_KCSEG / sizeof *gdt] = make_code_desc (0);
+ gdt[SEL_KDSEG / sizeof *gdt] = make_data_desc (0);
+ gdt[SEL_UCSEG / sizeof *gdt] = make_code_desc (3);
+ gdt[SEL_UDSEG / sizeof *gdt] = make_data_desc (3);
+ gdt[SEL_TSS / sizeof *gdt] = make_tss_desc (tss_get ());
+
+ /* Load GDTR, TR. See [IA32-v3a] 2.4.1 "Global Descriptor
+ Table Register (GDTR)", 2.4.4 "Task Register (TR)", and
+ 6.2.4 "Task Register". */
+ gdtr_operand = make_gdtr_operand (sizeof gdt - 1, gdt);
+ asm volatile ("lgdt %0" : : "m" (gdtr_operand));
+ asm volatile ("ltr %w0" : : "r" (SEL_TSS));
+}
+
+/* System segment or code/data segment? */
+enum seg_class
+ {
+ CLS_SYSTEM = 0, /* System segment. */
+ CLS_CODE_DATA = 1 /* Code or data segment. */
+ };
+
+/* Limit has byte or 4 kB page granularity? */
+enum seg_granularity
+ {
+ GRAN_BYTE = 0, /* Limit has 1-byte granularity. */
+ GRAN_PAGE = 1 /* Limit has 4 kB granularity. */
+ };
+
+/* Returns a segment descriptor with the given 32-bit BASE and
+ 20-bit LIMIT (whose interpretation depends on GRANULARITY).
+ The descriptor represents a system or code/data segment
+ according to CLASS, and TYPE is its type (whose interpretation
+ depends on the class).
+
+ The segment has descriptor privilege level DPL, meaning that
+ it can be used in rings numbered DPL or lower. In practice,
+ DPL==3 means that user processes can use the segment and
+ DPL==0 means that only the kernel can use the segment. See
+ [IA32-v3a] 4.5 "Privilege Levels" for further discussion. */
+static uint64_t
+make_seg_desc (uint32_t base,
+ uint32_t limit,
+ enum seg_class class,
+ int type,
+ int dpl,
+ enum seg_granularity granularity)
+{
+ uint32_t e0, e1;
+
+ ASSERT (limit <= 0xfffff);
+ ASSERT (class == CLS_SYSTEM || class == CLS_CODE_DATA);
+ ASSERT (type >= 0 && type <= 15);
+ ASSERT (dpl >= 0 && dpl <= 3);
+ ASSERT (granularity == GRAN_BYTE || granularity == GRAN_PAGE);
+
+ e0 = ((limit & 0xffff) /* Limit 15:0. */
+ | (base << 16)); /* Base 15:0. */
+
+ e1 = (((base >> 16) & 0xff) /* Base 23:16. */
+ | (type << 8) /* Segment type. */
+ | (class << 12) /* 0=system, 1=code/data. */
+ | (dpl << 13) /* Descriptor privilege. */
+ | (1 << 15) /* Present. */
+ | (limit & 0xf0000) /* Limit 16:19. */
+ | (1 << 22) /* 32-bit segment. */
+ | (granularity << 23) /* Byte/page granularity. */
+ | (base & 0xff000000)); /* Base 31:24. */
+
+ return e0 | ((uint64_t) e1 << 32);
+}
+
+/* Returns a descriptor for a readable code segment with base at
+ 0, a limit of 4 GB, and the given DPL. */
+static uint64_t
+make_code_desc (int dpl)
+{
+ return make_seg_desc (0, 0xfffff, CLS_CODE_DATA, 10, dpl, GRAN_PAGE);
+}
+
+/* Returns a descriptor for a writable data segment with base at
+ 0, a limit of 4 GB, and the given DPL. */
+static uint64_t
+make_data_desc (int dpl)
+{
+ return make_seg_desc (0, 0xfffff, CLS_CODE_DATA, 2, dpl, GRAN_PAGE);
+}
+
+/* Returns a descriptor for an "available" 32-bit Task-State
+ Segment with its base at the given linear address, a limit of
+ 0x67 bytes (the size of a 32-bit TSS), and a DPL of 0.
+ See [IA32-v3a] 6.2.2 "TSS Descriptor". */
+static uint64_t
+make_tss_desc (void *laddr)
+{
+ return make_seg_desc ((uint32_t) laddr, 0x67, CLS_SYSTEM, 9, 0, GRAN_BYTE);
+}
+
+
+/* Returns a descriptor that yields the given LIMIT and BASE when
+ used as an operand for the LGDT instruction. */
+static uint64_t
+make_gdtr_operand (uint16_t limit, void *base)
+{
+ return limit | ((uint64_t) (uint32_t) base << 16);
+}
diff --git a/src/userprog/gdt.h b/src/userprog/gdt.h
new file mode 100644
index 0000000..81fe50c
--- /dev/null
+++ b/src/userprog/gdt.h
@@ -0,0 +1,15 @@
+#ifndef USERPROG_GDT_H
+#define USERPROG_GDT_H
+
+#include "threads/loader.h"
+
+/* Segment selectors.
+ More selectors are defined by the loader in loader.h. */
+#define SEL_UCSEG 0x1B /* User code selector. */
+#define SEL_UDSEG 0x23 /* User data selector. */
+#define SEL_TSS 0x28 /* Task-state segment. */
+#define SEL_CNT 6 /* Number of segments. */
+
+void gdt_init (void);
+
+#endif /* userprog/gdt.h */
diff --git a/src/userprog/load.c b/src/userprog/load.c
new file mode 100644
index 0000000..eabc056
--- /dev/null
+++ b/src/userprog/load.c
@@ -0,0 +1,381 @@
+#include <inttypes.h> /* Elf32_word etc. */
+#include <round.h> /* ROUND_UP */
+#include <stdio.h>
+#include <string.h> /* memcmp */
+
+#include "userprog/process.h"
+#include "userprog/load.h"
+#include "userprog/pagedir.h"
+#include "filesys/file.h"
+#include "filesys/filesys.h"
+#include "threads/palloc.h" /* PAL_* constants */
+#include "threads/thread.h"
+#include "threads/vaddr.h" /* PGSIZE */
+
+/* We load ELF binaries. The following definitions are taken
+ from the ELF specification, [ELF1], more-or-less verbatim. */
+
+/* ELF types. See [ELF1] 1-2. */
+typedef uint32_t Elf32_Word, Elf32_Addr, Elf32_Off;
+typedef uint16_t Elf32_Half;
+
+/* For use with ELF types in printf(). */
+#define PE32Wx PRIx32 /* Print Elf32_Word in hexadecimal. */
+#define PE32Ax PRIx32 /* Print Elf32_Addr in hexadecimal. */
+#define PE32Ox PRIx32 /* Print Elf32_Off in hexadecimal. */
+#define PE32Hx PRIx16 /* Print Elf32_Half in hexadecimal. */
+
+/* Executable header. See [ELF1] 1-4 to 1-8.
+ This appears at the very beginning of an ELF binary. */
+struct Elf32_Ehdr
+ {
+ unsigned char e_ident[16];
+ Elf32_Half e_type;
+ Elf32_Half e_machine;
+ Elf32_Word e_version;
+ Elf32_Addr e_entry;
+ Elf32_Off e_phoff;
+ Elf32_Off e_shoff;
+ Elf32_Word e_flags;
+ Elf32_Half e_ehsize;
+ Elf32_Half e_phentsize;
+ Elf32_Half e_phnum;
+ Elf32_Half e_shentsize;
+ Elf32_Half e_shnum;
+ Elf32_Half e_shstrndx;
+ };
+
+/* Program header. See [ELF1] 2-2 to 2-4.
+ There are e_phnum of these, starting at file offset e_phoff
+ (see [ELF1] 1-6). */
+struct Elf32_Phdr
+ {
+ Elf32_Word p_type;
+ Elf32_Off p_offset;
+ Elf32_Addr p_vaddr;
+ Elf32_Addr p_paddr;
+ Elf32_Word p_filesz;
+ Elf32_Word p_memsz;
+ Elf32_Word p_flags;
+ Elf32_Word p_align;
+ };
+
+/* Values for p_type. See [ELF1] 2-3. */
+#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. */
+
+/* Flags for p_flags. See [ELF3] 2-3 and 2-4. */
+#define PF_X 1 /* Executable. */
+#define PF_W 2 /* Writable. */
+#define PF_R 4 /* Readable. */
+
+static bool setup_stack (void **esp);
+static bool validate_segment (const struct Elf32_Phdr *, struct file *);
+static bool load_segment (struct file *file, off_t ofs, uint8_t *upage,
+ uint32_t read_bytes, uint32_t zero_bytes,
+ bool writable);
+
+/* Loads an ELF executable from FILE_NAME into the current thread.
+ Stores the executable's entry point into *EIP
+ and its initial stack pointer into *ESP.
+ Returns true if successful, false otherwise. */
+bool
+load (const char *file_name, void (**eip) (void), void **esp)
+{
+ struct thread *t = thread_current ();
+ struct Elf32_Ehdr ehdr;
+ struct file *file = NULL;
+ off_t file_ofs;
+ bool success = false;
+ int i;
+
+ /* Allocate and activate page directory. */
+ t->pagedir = pagedir_create ();
+ if (t->pagedir == NULL)
+ goto done;
+ process_activate ();
+
+ /* Set up stack. */
+ if (!setup_stack (esp)){
+ goto done;
+ }
+
+ /* Open executable file. */
+ file = filesys_open (file_name);
+ if (file == NULL)
+ {
+ printf ("load: %s: open failed\n", file_name);
+ goto done;
+ }
+
+ /* Read and verify executable header. */
+ if (file_read (file, &ehdr, sizeof ehdr) != sizeof ehdr
+ || memcmp (ehdr.e_ident, "\177ELF\1\1\1", 7)
+ || ehdr.e_type != 2
+ || ehdr.e_machine != 3
+ || ehdr.e_version != 1
+ || ehdr.e_phentsize != sizeof (struct Elf32_Phdr)
+ || ehdr.e_phnum > 1024)
+ {
+ printf ("load: %s: error loading executable\n", file_name);
+ goto done;
+ }
+
+ /* Read program headers. */
+ file_ofs = ehdr.e_phoff;
+ for (i = 0; i < ehdr.e_phnum; i++)
+ {
+ struct Elf32_Phdr phdr;
+
+ if (file_ofs < 0 || file_ofs > file_length (file))
+ goto done;
+ file_seek (file, file_ofs);
+
+ if (file_read (file, &phdr, sizeof phdr) != sizeof phdr)
+ goto done;
+ file_ofs += sizeof phdr;
+ switch (phdr.p_type)
+ {
+ case PT_NULL:
+ case PT_NOTE:
+ case PT_PHDR:
+ case PT_STACK:
+ default:
+ /* Ignore this segment. */
+ break;
+ case PT_DYNAMIC:
+ case PT_INTERP:
+ case PT_SHLIB:
+ goto done;
+ case PT_LOAD:
+ if (validate_segment (&phdr, file))
+ {
+ bool writable = (phdr.p_flags & PF_W) != 0;
+ uint32_t file_page = phdr.p_offset & ~PGMASK;
+ uint32_t mem_page = phdr.p_vaddr & ~PGMASK;
+ uint32_t page_offset = phdr.p_vaddr & PGMASK;
+ uint32_t read_bytes, zero_bytes;
+ if (phdr.p_filesz > 0)
+ {
+ /* Normal segment.
+ Read initial part from disk and zero the rest. */
+ read_bytes = page_offset + phdr.p_filesz;
+ zero_bytes = (ROUND_UP (page_offset + phdr.p_memsz, PGSIZE)
+ - read_bytes);
+ }
+ else
+ {
+ /* Entirely zero.
+ Don't read anything from disk. */
+ read_bytes = 0;
+ zero_bytes = ROUND_UP (page_offset + phdr.p_memsz, PGSIZE);
+ }
+ if (!load_segment (file, file_page, (void *) mem_page,
+ read_bytes, zero_bytes, writable))
+ goto done;
+ }
+ else
+ goto done;
+ break;
+ }
+ }
+
+ /* Start address. */
+ *eip = (void (*) (void)) ehdr.e_entry;
+
+ success = true;
+
+ done:
+ /* We arrive here whether the load is successful or not. */
+ file_close (file);
+ return success;
+}
+
+/* load() helpers. */
+
+static bool install_page (void *upage, void *kpage, bool writable);
+
+/* Checks whether PHDR describes a valid, loadable segment in
+ FILE and returns true if so, false otherwise. */
+static bool
+validate_segment (const struct Elf32_Phdr *phdr, struct file *file)
+{
+ /* p_offset and p_vaddr must have the same page offset. */
+ if ((phdr->p_offset & PGMASK) != (phdr->p_vaddr & PGMASK))
+ return false;
+
+ /* p_offset must point within FILE. */
+ if (phdr->p_offset > (Elf32_Off) file_length (file))
+ return false;
+
+ /* p_memsz must be at least as big as p_filesz. */
+ if (phdr->p_memsz < phdr->p_filesz)
+ return false;
+
+ /* The segment must not be empty. */
+ if (phdr->p_memsz == 0)
+ return false;
+
+ /* The virtual memory region must both start and end within the
+ user address space range. */
+ if (!is_user_vaddr ((void *) phdr->p_vaddr))
+ return false;
+ if (!is_user_vaddr ((void *) (phdr->p_vaddr + phdr->p_memsz)))
+ return false;
+
+ /* The region cannot "wrap around" across the kernel virtual
+ address space. */
+ if (phdr->p_vaddr + phdr->p_memsz < phdr->p_vaddr)
+ return false;
+
+ /* Disallow mapping page 0.
+ Not only is it a bad idea to map page 0, but if we allowed
+ it then user code that passed a null pointer to system calls
+ could quite likely panic the kernel by way of null pointer
+ assertions in memcpy(), etc. */
+ if (phdr->p_vaddr < PGSIZE)
+ return false;
+
+ /* It's okay. */
+ return true;
+}
+
+/* Loads a segment starting at offset OFS in FILE at address
+ UPAGE. In total, READ_BYTES + ZERO_BYTES bytes of virtual
+ memory are initialized, as follows:
+
+ - READ_BYTES bytes at UPAGE must be read from FILE
+ starting at offset OFS.
+
+ - ZERO_BYTES bytes at UPAGE + READ_BYTES must be zeroed.
+
+ The pages initialized by this function must be writable by the
+ user process if WRITABLE is true, read-only otherwise.
+
+ Return true if successful, false if a memory allocation error
+ or disk read error occurs. */
+static bool
+load_segment (struct file *file, off_t ofs, uint8_t *upage,
+ uint32_t read_bytes, uint32_t zero_bytes, bool writable)
+{
+ ASSERT ((read_bytes + zero_bytes) % PGSIZE == 0);
+ ASSERT (pg_ofs (upage) == 0);
+ ASSERT (ofs % PGSIZE == 0);
+
+ file_seek (file, ofs);
+ while (read_bytes > 0 || zero_bytes > 0)
+ {
+ /* Calculate how to fill this page.
+ We will read PAGE_READ_BYTES bytes from FILE
+ and zero the final PAGE_ZERO_BYTES bytes. */
+ size_t page_read_bytes = read_bytes < PGSIZE ? read_bytes : PGSIZE;
+ size_t page_zero_bytes = PGSIZE - page_read_bytes;
+
+ /* Get a page of memory. */
+ uint8_t *kpage = palloc_get_page (PAL_USER);
+ if (kpage == NULL)
+ return false;
+
+ /* Load this page. */
+ if (file_read (file, kpage, page_read_bytes) != (int) page_read_bytes)
+ {
+ palloc_free_page (kpage);
+ return false;
+ }
+ memset (kpage + page_read_bytes, 0, page_zero_bytes);
+
+ /* Add the page to the process's address space. */
+ if (!install_page (upage, kpage, writable))
+ {
+ palloc_free_page (kpage);
+ return false;
+ }
+
+ /* Advance. */
+ read_bytes -= page_read_bytes;
+ zero_bytes -= page_zero_bytes;
+ upage += PGSIZE;
+ }
+ return true;
+}
+
+/* Create a minimal stack by mapping a zeroed page at the top of
+ user virtual memory. */
+static bool
+setup_stack (void **esp)
+{
+ uint8_t *kpage;
+ bool success = false;
+
+ kpage = palloc_get_page (PAL_USER | PAL_ZERO);
+ if (kpage != NULL)
+ {
+ success = install_page (((uint8_t *) PHYS_BASE) - PGSIZE, kpage, true);
+ if (success)
+ *esp = PHYS_BASE;
+ else
+ palloc_free_page (kpage);
+ }
+ return success;
+}
+
+/* Adds a mapping from user virtual address UPAGE to kernel
+ virtual address KPAGE to the page table.
+ If WRITABLE is true, the user process may modify the page;
+ otherwise, it is read-only.
+ UPAGE must not already be mapped.
+ KPAGE should probably be a page obtained from the user pool
+ with palloc_get_page().
+ Returns true on success, false if UPAGE is already mapped or
+ if memory allocation fails. */
+static bool
+install_page (void *upage, void *kpage, bool writable)
+{
+ struct thread *t = thread_current ();
+
+ /* Verify that there's not already a page at that virtual
+ address, then map our page there. */
+ return (pagedir_get_page (t->pagedir, upage) == NULL
+ && pagedir_set_page (t->pagedir, upage, kpage, writable));
+}
+
+/* A function that dumps 'size' bytes of memory starting at 'ptr'
+ * it will dump the higher adress first letting the stack grow down.
+ */
+void dump_stack(void* ptr, int size)
+{
+ unsigned from = (unsigned)ptr;
+ unsigned to = (unsigned)(ptr - size);
+ unsigned adr;
+
+ printf("# Adress \thex-data \tchar-data\n");
+
+ for ( adr = from; adr > to; --adr )
+ {
+ unsigned* uadr = (unsigned*)( adr );
+ unsigned char* byte = (unsigned char*)( adr );
+
+ printf("# %08x\t", adr); /* address */
+
+ if ( (adr % 4) == 0 )
+ printf("%08x\t", *uadr); /* content interpreted as address */
+ else
+ printf(" \t"); /* fill */
+
+ if ( *byte >= 32 && *byte < 127 )
+ printf("%c\n", *byte); /* content interpreted as character */
+ else
+ printf("\\0%o\n", *byte); /* octal character code */
+
+ if ( adr == (unsigned)PHYS_BASE )
+ printf("# -- ^ KERNEL SPACE ABOVE ^v USER SPACE BELOW v --\n");
+ else if ( (adr % 4) == 0 )
+ printf("# ------------------------------------------------\n");
+ }
+}
diff --git a/src/userprog/load.h b/src/userprog/load.h
new file mode 100644
index 0000000..b0978c9
--- /dev/null
+++ b/src/userprog/load.h
@@ -0,0 +1,7 @@
+#ifndef USERPROG_LOAD_H
+#define USERPROG_LOAD_H
+
+bool load (const char *file_name, void (**eip) (void), void **esp);
+void dump_stack(void* ptr, int size);
+
+#endif /* userprog/load.h */
diff --git a/src/userprog/pagedir.c b/src/userprog/pagedir.c
new file mode 100644
index 0000000..30bdfe2
--- /dev/null
+++ b/src/userprog/pagedir.c
@@ -0,0 +1,263 @@
+#include "userprog/pagedir.h"
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include "threads/init.h"
+#include "threads/pte.h"
+#include "threads/palloc.h"
+
+static uint32_t *active_pd (void);
+static void invalidate_pagedir (uint32_t *);
+
+/* Creates a new page directory that has mappings for kernel
+ virtual addresses, but none for user virtual addresses.
+ Returns the new page directory, or a null pointer if memory
+ allocation fails. */
+uint32_t *
+pagedir_create (void)
+{
+ uint32_t *pd = palloc_get_page (0);
+ if (pd != NULL)
+ memcpy (pd, base_page_dir, PGSIZE);
+ return pd;
+}
+
+/* Destroys page directory PD, freeing all the pages it
+ references. */
+void
+pagedir_destroy (uint32_t *pd)
+{
+ uint32_t *pde;
+
+ if (pd == NULL)
+ return;
+
+ ASSERT (pd != base_page_dir);
+ for (pde = pd; pde < pd + pd_no (PHYS_BASE); pde++)
+ if (*pde & PTE_P)
+ {
+ uint32_t *pt = pde_get_pt (*pde);
+ uint32_t *pte;
+
+ for (pte = pt; pte < pt + PGSIZE / sizeof *pte; pte++)
+ if (*pte & PTE_P)
+ palloc_free_page (pte_get_page (*pte));
+ palloc_free_page (pt);
+ }
+ palloc_free_page (pd);
+}
+
+/* Returns the address of the page table entry for virtual
+ address VADDR in page directory PD.
+ If PD does not have a page table for VADDR, behavior depends
+ on CREATE. If CREATE is true, then a new page table is
+ created and a pointer into it is returned. Otherwise, a null
+ pointer is returned. */
+static uint32_t *
+lookup_page (uint32_t *pd, const void *vaddr, bool create)
+{
+ uint32_t *pt, *pde;
+
+ ASSERT (pd != NULL);
+
+ /* Shouldn't create new kernel virtual mappings. */
+ ASSERT (!create || is_user_vaddr (vaddr));
+
+ /* Check for a page table for VADDR.
+ If one is missing, create one if requested. */
+ pde = pd + pd_no (vaddr);
+ if (*pde == 0)
+ {
+ if (create)
+ {
+ pt = palloc_get_page (PAL_ZERO);
+ if (pt == NULL)
+ return NULL;
+
+ *pde = pde_create (pt);
+ }
+ else
+ return NULL;
+ }
+
+ /* Return the page table entry. */
+ pt = pde_get_pt (*pde);
+ return &pt[pt_no (vaddr)];
+}
+
+/* Adds a mapping in page directory PD from user virtual page
+ UPAGE to the physical frame identified by kernel virtual
+ address KPAGE.
+ UPAGE must not already be mapped.
+ KPAGE should probably be a page obtained from the user pool
+ with palloc_get_page().
+ If WRITABLE is true, the new page is read/write;
+ otherwise it is read-only.
+ Returns true if successful, false if memory allocation
+ failed. */
+bool
+pagedir_set_page (uint32_t *pd, void *upage, void *kpage, bool writable)
+{
+ uint32_t *pte;
+
+ ASSERT (pg_ofs (upage) == 0);
+ ASSERT (pg_ofs (kpage) == 0);
+ ASSERT (is_user_vaddr (upage));
+ ASSERT (vtop (kpage) >> PTSHIFT < ram_pages);
+ ASSERT (pd != base_page_dir);
+
+ pte = lookup_page (pd, upage, true);
+
+ if (pte != NULL)
+ {
+ ASSERT ((*pte & PTE_P) == 0);
+ *pte = pte_create_user (kpage, writable);
+ return true;
+ }
+ else
+ return false;
+}
+
+/* Looks up the physical address that corresponds to user virtual
+ address UADDR in PD. Returns the kernel virtual address
+ corresponding to that physical address, or a null pointer if
+ UADDR is unmapped. */
+void *
+pagedir_get_page (uint32_t *pd, const void *uaddr)
+{
+ uint32_t *pte;
+
+ ASSERT (is_user_vaddr (uaddr));
+
+ pte = lookup_page (pd, uaddr, false);
+ if (pte != NULL && (*pte & PTE_P) != 0)
+ return pte_get_page (*pte) + pg_ofs (uaddr);
+ else
+ return NULL;
+}
+
+/* Marks user virtual page UPAGE "not present" in page
+ directory PD. Later accesses to the page will fault. Other
+ bits in the page table entry are preserved.
+ UPAGE need not be mapped. */
+void
+pagedir_clear_page (uint32_t *pd, void *upage)
+{
+ uint32_t *pte;
+
+ ASSERT (pg_ofs (upage) == 0);
+ ASSERT (is_user_vaddr (upage));
+
+ pte = lookup_page (pd, upage, false);
+ if (pte != NULL && (*pte & PTE_P) != 0)
+ {
+ *pte &= ~PTE_P;
+ invalidate_pagedir (pd);
+ }
+}
+
+/* Returns true if the PTE for virtual page VPAGE in PD is dirty,
+ that is, if the page has been modified since the PTE was
+ installed.
+ Returns false if PD contains no PTE for VPAGE. */
+bool
+pagedir_is_dirty (uint32_t *pd, const void *vpage)
+{
+ uint32_t *pte = lookup_page (pd, vpage, false);
+ return pte != NULL && (*pte & PTE_D) != 0;
+}
+
+/* Set the dirty bit to DIRTY in the PTE for virtual page VPAGE
+ in PD. */
+void
+pagedir_set_dirty (uint32_t *pd, const void *vpage, bool dirty)
+{
+ uint32_t *pte = lookup_page (pd, vpage, false);
+ if (pte != NULL)
+ {
+ if (dirty)
+ *pte |= PTE_D;
+ else
+ {
+ *pte &= ~(uint32_t) PTE_D;
+ invalidate_pagedir (pd);
+ }
+ }
+}
+
+/* Returns true if the PTE for virtual page VPAGE in PD has been
+ accessed recently, that is, between the time the PTE was
+ installed and the last time it was cleared. Returns false if
+ PD contains no PTE for VPAGE. */
+bool
+pagedir_is_accessed (uint32_t *pd, const void *vpage)
+{
+ uint32_t *pte = lookup_page (pd, vpage, false);
+ return pte != NULL && (*pte & PTE_A) != 0;
+}
+
+/* Sets the accessed bit to ACCESSED in the PTE for virtual page
+ VPAGE in PD. */
+void
+pagedir_set_accessed (uint32_t *pd, const void *vpage, bool accessed)
+{
+ uint32_t *pte = lookup_page (pd, vpage, false);
+ if (pte != NULL)
+ {
+ if (accessed)
+ *pte |= PTE_A;
+ else
+ {
+ *pte &= ~(uint32_t) PTE_A;
+ invalidate_pagedir (pd);
+ }
+ }
+}
+
+/* Loads page directory PD into the CPU's page directory base
+ register. */
+void
+pagedir_activate (uint32_t *pd)
+{
+ if (pd == NULL)
+ pd = base_page_dir;
+
+ /* Store the physical address of the page directory into CR3
+ aka PDBR (page directory base register). This activates our
+ new page tables immediately. See [IA32-v2a] "MOV--Move
+ to/from Control Registers" and [IA32-v3a] 3.7.5 "Base
+ Address of the Page Directory". */
+ asm volatile ("movl %0, %%cr3" : : "r" (vtop (pd)) : "memory");
+}
+
+/* Returns the currently active page directory. */
+static uint32_t *
+active_pd (void)
+{
+ /* Copy CR3, the page directory base register (PDBR), into
+ `pd'.
+ See [IA32-v2a] "MOV--Move to/from Control Registers" and
+ [IA32-v3a] 3.7.5 "Base Address of the Page Directory". */
+ uintptr_t pd;
+ asm volatile ("movl %%cr3, %0" : "=r" (pd));
+ return ptov (pd);
+}
+
+/* Seom page table changes can cause the CPU's translation
+ lookaside buffer (TLB) to become out-of-sync with the page
+ table. When this happens, we have to "invalidate" the TLB by
+ re-activating it.
+
+ This function invalidates the TLB if PD is the active page
+ directory. (If PD is not active then its entries are not in
+ the TLB, so there is no need to invalidate anything.) */
+static void
+invalidate_pagedir (uint32_t *pd)
+{
+ if (active_pd () == pd)
+ {
+ /* Re-activating PD clears the TLB. See [IA32-v3a] 3.12
+ "Translation Lookaside Buffers (TLBs)". */
+ pagedir_activate (pd);
+ }
+}
diff --git a/src/userprog/pagedir.h b/src/userprog/pagedir.h
new file mode 100644
index 0000000..cd92447
--- /dev/null
+++ b/src/userprog/pagedir.h
@@ -0,0 +1,18 @@
+#ifndef USERPROG_PAGEDIR_H
+#define USERPROG_PAGEDIR_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+uint32_t *pagedir_create (void);
+void pagedir_destroy (uint32_t *pd);
+bool pagedir_set_page (uint32_t *pd, void *upage, void *kpage, bool rw);
+void *pagedir_get_page (uint32_t *pd, const void *upage);
+void pagedir_clear_page (uint32_t *pd, void *upage);
+bool pagedir_is_dirty (uint32_t *pd, const void *upage);
+void pagedir_set_dirty (uint32_t *pd, const void *upage, bool dirty);
+bool pagedir_is_accessed (uint32_t *pd, const void *upage);
+void pagedir_set_accessed (uint32_t *pd, const void *upage, bool accessed);
+void pagedir_activate (uint32_t *pd);
+
+#endif /* userprog/pagedir.h */
diff --git a/src/userprog/plist.c b/src/userprog/plist.c
new file mode 100644
index 0000000..da03d54
--- /dev/null
+++ b/src/userprog/plist.c
@@ -0,0 +1,3 @@
+#include <stddef.h>
+
+#include "plist.h"
diff --git a/src/userprog/plist.h b/src/userprog/plist.h
new file mode 100644
index 0000000..ce75524
--- /dev/null
+++ b/src/userprog/plist.h
@@ -0,0 +1,33 @@
+#ifndef _PLIST_H_
+#define _PLIST_H_
+
+
+/* Place functions to handle a running process here (process list).
+
+ plist.h : Your function declarations and documentation.
+ plist.c : Your implementation.
+
+ The following is strongly recommended:
+
+ - A function that given process inforamtion (up to you to create)
+ inserts this in a list of running processes and return an integer
+ that can be used to find the information later on.
+
+ - A function that given an integer (obtained from above function)
+ FIND the process information in the list. Should return some
+ failure code if no process matching the integer is in the list.
+ Or, optionally, several functions to access any information of a
+ particular process that you currently need.
+
+ - A function that given an integer REMOVE the process information
+ from the list. Should only remove the information when no process
+ or thread need it anymore, but must guarantee it is always
+ removed EVENTUALLY.
+
+ - A function that print the entire content of the list in a nice,
+ clean, readable format.
+
+ */
+
+
+#endif
diff --git a/src/userprog/process.c b/src/userprog/process.c
new file mode 100644
index 0000000..7df8778
--- /dev/null
+++ b/src/userprog/process.c
@@ -0,0 +1,275 @@
+#include <debug.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "userprog/gdt.h" /* SEL_* constants */
+#include "userprog/process.h"
+#include "userprog/load.h"
+#include "userprog/pagedir.h" /* pagedir_activate etc. */
+#include "userprog/tss.h" /* tss_update */
+#include "filesys/file.h"
+#include "threads/flags.h" /* FLAG_* constants */
+#include "threads/thread.h"
+#include "threads/vaddr.h" /* PHYS_BASE */
+#include "threads/interrupt.h" /* if_ */
+
+/* Headers not yet used that you may need for various reasons. */
+#include "threads/synch.h"
+#include "threads/malloc.h"
+#include "lib/kernel/list.h"
+
+#include "userprog/flist.h"
+#include "userprog/plist.h"
+
+/* HACK defines code you must remove and implement in a proper way */
+#define HACK
+
+
+/* This function is called at boot time (threads/init.c) to initialize
+ * the process subsystem. */
+void process_init(void)
+{
+}
+
+/* This function is currently never called. As thread_exit does not
+ * have an exit status parameter, this could be used to handle that
+ * instead. Note however that all cleanup after a process must be done
+ * in process_cleanup, and that process_cleanup are already called
+ * from thread_exit - do not call cleanup twice! */
+void process_exit(int status UNUSED)
+{
+}
+
+/* Print a list of all running processes. The list shall include all
+ * relevant debug information in a clean, readable format. */
+void process_print_list()
+{
+}
+
+
+struct parameters_to_start_process
+{
+ char* command_line;
+};
+
+static void
+start_process(struct parameters_to_start_process* parameters) NO_RETURN;
+
+/* Starts a new proccess by creating a new thread to run it. The
+ process is loaded from the file specified in the COMMAND_LINE and
+ started with the arguments on the COMMAND_LINE. The new thread may
+ be scheduled (and may even exit) before process_execute() returns.
+ Returns the new process's thread id, or TID_ERROR if the thread
+ cannot be created. */
+int
+process_execute (const char *command_line)
+{
+ char debug_name[64];
+ int command_line_size = strlen(command_line) + 1;
+ tid_t thread_id = -1;
+ int process_id = -1;
+
+ /* LOCAL variable will cease existence when function return! */
+ struct parameters_to_start_process arguments;
+
+ debug("%s#%d: process_execute(\"%s\") ENTERED\n",
+ thread_current()->name,
+ thread_current()->tid,
+ command_line);
+
+ /* COPY command line out of parent process memory */
+ arguments.command_line = malloc(command_line_size);
+ strlcpy(arguments.command_line, command_line, command_line_size);
+
+
+ strlcpy_first_word (debug_name, command_line, 64);
+
+ /* SCHEDULES function `start_process' to run (LATER) */
+ thread_id = thread_create (debug_name, PRI_DEFAULT,
+ (thread_func*)start_process, &arguments);
+
+ process_id = thread_id;
+
+ /* AVOID bad stuff by turning off. YOU will fix this! */
+ power_off();
+
+
+ /* WHICH thread may still be using this right now? */
+ free(arguments.command_line);
+
+ debug("%s#%d: process_execute(\"%s\") RETURNS %d\n",
+ thread_current()->name,
+ thread_current()->tid,
+ command_line, process_id);
+
+ /* MUST be -1 if `load' in `start_process' return false */
+ return process_id;
+}
+
+/* A thread function that loads a user process and starts it
+ running. */
+static void
+start_process (struct parameters_to_start_process* parameters)
+{
+ /* The last argument passed to thread_create is received here... */
+ struct intr_frame if_;
+ bool success;
+
+ char file_name[64];
+ strlcpy_first_word (file_name, parameters->command_line, 64);
+
+ debug("%s#%d: start_process(\"%s\") ENTERED\n",
+ thread_current()->name,
+ thread_current()->tid,
+ parameters->command_line);
+
+ /* Initialize interrupt frame and load executable. */
+ memset (&if_, 0, sizeof if_);
+ if_.gs = if_.fs = if_.es = if_.ds = if_.ss = SEL_UDSEG;
+ if_.cs = SEL_UCSEG;
+ if_.eflags = FLAG_IF | FLAG_MBS;
+
+ success = load (file_name, &if_.eip, &if_.esp);
+
+ debug("%s#%d: start_process(...): load returned %d\n",
+ thread_current()->name,
+ thread_current()->tid,
+ success);
+
+ if (success)
+ {
+ /* We managed to load the new program to a process, and have
+ allocated memory for a process stack. The stack top is in
+ if_.esp, now we must prepare and place the arguments to main on
+ the stack. */
+
+ /* A temporary solution is to modify the stack pointer to
+ "pretend" the arguments are present on the stack. A normal
+ C-function expects the stack to contain, in order, the return
+ address, the first argument, the second argument etc. */
+
+ HACK if_.esp -= 12; /* Unacceptable solution. */
+
+ /* The stack and stack pointer should be setup correct just before
+ the process start, so this is the place to dump stack content
+ for debug purposes. Disable the dump when it works. */
+
+// dump_stack ( PHYS_BASE + 15, PHYS_BASE - if_.esp + 16 );
+
+ }
+
+ debug("%s#%d: start_process(\"%s\") DONE\n",
+ thread_current()->name,
+ thread_current()->tid,
+ parameters->command_line);
+
+
+ /* If load fail, quit. Load may fail for several reasons.
+ Some simple examples:
+ - File doeas not exist
+ - File do not contain a valid program
+ - Not enough memory
+ */
+ if ( ! success )
+ {
+ thread_exit ();
+ }
+
+ /* Start the user process by simulating a return from an interrupt,
+ implemented by intr_exit (in threads/intr-stubs.S). Because
+ intr_exit takes all of its arguments on the stack in the form of
+ a `struct intr_frame', we just point the stack pointer (%esp) to
+ our stack frame and jump to it. */
+ asm volatile ("movl %0, %%esp; jmp intr_exit" : : "g" (&if_) : "memory");
+ NOT_REACHED ();
+}
+
+/* Wait for process `child_id' to die and then return its exit
+ status. If it was terminated by the kernel (i.e. killed due to an
+ exception), return -1. If `child_id' is invalid or if it was not a
+ child of the calling process, or if process_wait() has already been
+ successfully called for the given `child_id', return -1
+ immediately, without waiting.
+
+ This function will be implemented last, after a communication
+ mechanism between parent and child is established. */
+int
+process_wait (int child_id)
+{
+ int status = -1;
+ struct thread *cur = thread_current ();
+
+ debug("%s#%d: process_wait(%d) ENTERED\n",
+ cur->name, cur->tid, child_id);
+ /* Yes! You need to do something good here ! */
+ debug("%s#%d: process_wait(%d) RETURNS %d\n",
+ cur->name, cur->tid, child_id, status);
+
+ return status;
+}
+
+/* Free the current process's resources. This function is called
+ automatically from thread_exit() to make sure cleanup of any
+ process resources is always done. That is correct behaviour. But
+ know that thread_exit() is called at many places inside the kernel,
+ mostly in case of some unrecoverable error in a thread.
+
+ In such case it may happen that some data is not yet available, or
+ initialized. You must make sure that nay data needed IS available
+ or initialized to something sane, or else that any such situation
+ is detected.
+*/
+
+void
+process_cleanup (void)
+{
+ struct thread *cur = thread_current ();
+ uint32_t *pd = cur->pagedir;
+ int status = -1;
+
+ debug("%s#%d: process_cleanup() ENTERED\n", cur->name, cur->tid);
+
+ /* Later tests DEPEND on this output to work correct. You will have
+ * to find the actual exit status in your process list. It is
+ * important to do this printf BEFORE you tell the parent process
+ * that you exit. (Since the parent may be the main() function,
+ * that may sometimes poweroff as soon as process_wait() returns,
+ * possibly before the prontf is completed.)
+ */
+ printf("%s: exit(%d)\n", thread_name(), status);
+
+ /* Destroy the current process's page directory and switch back
+ to the kernel-only page directory. */
+ if (pd != NULL)
+ {
+ /* Correct ordering here is crucial. We must set
+ cur->pagedir to NULL before switching page directories,
+ so that a timer interrupt can't switch back to the
+ process page directory. We must activate the base page
+ directory before destroying the process's page
+ directory, or our active page directory will be one
+ that's been freed (and cleared). */
+ cur->pagedir = NULL;
+ pagedir_activate (NULL);
+ pagedir_destroy (pd);
+ }
+ debug("%s#%d: process_cleanup() DONE with status %d\n",
+ cur->name, cur->tid, status);
+}
+
+/* Sets up the CPU for running user code in the current
+ thread.
+ This function is called on every context switch. */
+void
+process_activate (void)
+{
+ struct thread *t = thread_current ();
+
+ /* Activate thread's page tables. */
+ pagedir_activate (t->pagedir);
+
+ /* Set thread's kernel stack for use in processing
+ interrupts. */
+ tss_update ();
+}
+
diff --git a/src/userprog/process.h b/src/userprog/process.h
new file mode 100644
index 0000000..0a4e4ac
--- /dev/null
+++ b/src/userprog/process.h
@@ -0,0 +1,23 @@
+#ifndef USERPROG_PROCESS_H
+#define USERPROG_PROCESS_H
+
+#include "threads/thread.h"
+
+void process_init (void);
+void process_print_list (void);
+void process_exit (int status);
+tid_t process_execute (const char *file_name);
+int process_wait (tid_t);
+void process_cleanup (void);
+void process_activate (void);
+
+/* This is unacceptable solutions. */
+#define INFINITE_WAIT() for ( ; ; ) thread_yield()
+#define BUSY_WAIT(n) \
+ do { \
+ int i = n; \
+ while ( i --> 0 ) \
+ thread_yield(); \
+ } while ( 0 )
+
+#endif /* userprog/process.h */
diff --git a/src/userprog/syscall.c b/src/userprog/syscall.c
new file mode 100644
index 0000000..4d46978
--- /dev/null
+++ b/src/userprog/syscall.c
@@ -0,0 +1,61 @@
+#include <stdio.h>
+#include <syscall-nr.h>
+#include "userprog/syscall.h"
+#include "threads/interrupt.h"
+#include "threads/thread.h"
+
+/* header files you probably need, they are not used yet */
+#include <string.h>
+#include "filesys/filesys.h"
+#include "filesys/file.h"
+#include "threads/vaddr.h"
+#include "threads/init.h"
+#include "userprog/pagedir.h"
+#include "userprog/process.h"
+#include "devices/input.h"
+
+static void syscall_handler (struct intr_frame *);
+
+void
+syscall_init (void)
+{
+ intr_register_int (0x30, 3, INTR_ON, syscall_handler, "syscall");
+}
+
+
+/* This array defined the number of arguments each syscall expects.
+ For example, if you want to find out the number of arguments for
+ the read system call you shall write:
+
+ int sys_read_arg_count = argc[ SYS_READ ];
+
+ All system calls have a name such as SYS_READ defined as an enum
+ type, see `lib/syscall-nr.h'. Use them instead of numbers.
+ */
+const int argc[] = {
+ /* basic calls */
+ 0, 1, 1, 1, 2, 1, 1, 1, 3, 3, 2, 1, 1,
+ /* not implemented */
+ 2, 1, 1, 1, 2, 1, 1,
+ /* extended */
+ 0
+};
+
+static void
+syscall_handler (struct intr_frame *f)
+{
+ int32_t* esp = (int32_t*)f->esp;
+
+ switch ( 0 /* retrive syscall number */ )
+ {
+ default:
+ {
+ printf ("Executed an unknown system call!\n");
+
+ printf ("Stack top + 0: %d\n", esp[0]);
+ printf ("Stack top + 1: %d\n", esp[1]);
+
+ thread_exit ();
+ }
+ }
+}
diff --git a/src/userprog/syscall.h b/src/userprog/syscall.h
new file mode 100644
index 0000000..9059096
--- /dev/null
+++ b/src/userprog/syscall.h
@@ -0,0 +1,6 @@
+#ifndef USERPROG_SYSCALL_H
+#define USERPROG_SYSCALL_H
+
+void syscall_init (void);
+
+#endif /* userprog/syscall.h */
diff --git a/src/userprog/test.c b/src/userprog/test.c
new file mode 100644
index 0000000..6d884d6
--- /dev/null
+++ b/src/userprog/test.c
@@ -0,0 +1,66 @@
+#include <stdio.h>
+
+typedef __SIZE_TYPE__ size_t;
+
+/* As strlcpy, but only copies the first word from SRC. Worde are
+ * delimeted by space. Leading space is also ignored.
+ * ( added by klaar@ida )
+ */
+size_t
+strlcpy_first_word (char *dst, const char *src, size_t size)
+{
+ size_t i = size;
+
+ /* skip leading spaces */
+ while (*src != '\0' && *src == ' ')
+ ++src;
+
+ /* copy at most size characters */
+ if (size > 0)
+ {
+ while (i > 1)
+ {
+ if (*src == '\0' || *src == ' ')
+ break;
+
+ *dst++ = *src++;
+ --i;
+ }
+ *dst = '\0';
+ }
+ return size - i;
+}
+
+int main(int argc, char* argv[])
+{
+ char word[100];
+ int i;
+
+ i = strlcpy_first_word(word, argv[1], -1);
+
+ printf("Argv[1]: '%s', (%d characters)\n", argv[1], strlen(argv[1]));
+ printf("First w: '%s', (%d characters)\n", word, strlen(word));
+ printf("Copied %d characters of max %d\n\n", i, -1);
+
+
+ i = strlcpy_first_word(word, argv[1], 0);
+
+ printf("Argv[1]: '%s', (%d characters)\n", argv[1], strlen(argv[1]));
+ printf("First w: '%s', (%d characters)\n", word, strlen(word));
+ printf("Copied %d characters of max %d\n\n", i, 0);
+
+
+ i = strlcpy_first_word(word, argv[1], 1);
+
+ printf("Argv[1]: '%s', (%d characters)\n", argv[1], strlen(argv[1]));
+ printf("First w: '%s', (%d characters)\n", word, strlen(word));
+ printf("Copied %d characters of max %d\n\n", i, 1);
+
+
+ i = strlcpy_first_word(word, argv[1], 10);
+
+ printf("Argv[1]: '%s', (%d characters)\n", argv[1], strlen(argv[1]));
+ printf("First w: '%s', (%d characters)\n", word, strlen(word));
+ printf("Copied %d characters of max %d\n\n", i, 10);
+
+}
diff --git a/src/userprog/tss.c b/src/userprog/tss.c
new file mode 100644
index 0000000..435c780
--- /dev/null
+++ b/src/userprog/tss.c
@@ -0,0 +1,106 @@
+#include "userprog/tss.h"
+#include <debug.h>
+#include <stddef.h>
+#include "userprog/gdt.h"
+#include "threads/palloc.h"
+#include "threads/vaddr.h"
+#include "threads/thread.h"
+
+/* The Task-State Segment (TSS).
+
+ Instances of the TSS, an x86-specific structure, are used to
+ define "tasks", a form of support for multitasking built right
+ into the processor. However, for various reasons including
+ portability, speed, and flexibility, most x86 OSes almost
+ completely ignore the TSS. We are no exception.
+
+ Unfortunately, there is one thing that can only be done using
+ a TSS: stack switching for interrupts that occur in user mode.
+ When an interrupt occurs in user mode (ring 3), the processor
+ consults the ss0 and esp0 members of the current TSS to
+ determine the stack to use for handling the interrupt. Thus,
+ we must create a TSS and initialize at least these fields, and
+ this is precisely what this file does.
+
+ When an interrupt is handled by an interrupt or trap gate
+ (which applies to all interrupts we handle), an x86 processor
+ works like this:
+
+ - If the code interrupted by the interrupt is in the same
+ ring as the interrupt handler, then no stack switch takes
+ place. This is the case for interrupts that happen when
+ we're running in the kernel. The contents of the TSS are
+ irrelevant for this case.
+
+ - If the interrupted code is in a different ring from the
+ handler, then the processor switches to the stack
+ specified in the TSS for the new ring. This is the case
+ for interrupts that happen when we're in user space. It's
+ important that we switch to a stack that's not already in
+ use, to avoid corruption. Because we're running in user
+ space, we know that the current process's kernel stack is
+ not in use, so we can always use that. Thus, when the
+ scheduler switches threads, it also changes the TSS's
+ stack pointer to point to the new thread's kernel stack.
+ (The call is in schedule_tail() in thread.c.)
+
+ See [IA32-v3a] 6.2.1 "Task-State Segment (TSS)" for a
+ description of the TSS. See [IA32-v3a] 5.12.1 "Exception- or
+ Interrupt-Handler Procedures" for a description of when and
+ how stack switching occurs during an interrupt. */
+struct tss
+ {
+ uint16_t back_link, :16;
+ void *esp0; /* Ring 0 stack virtual address. */
+ uint16_t ss0, :16; /* Ring 0 stack segment selector. */
+ void *esp1;
+ uint16_t ss1, :16;
+ void *esp2;
+ uint16_t ss2, :16;
+ uint32_t cr3;
+ void (*eip) (void);
+ uint32_t eflags;
+ uint32_t eax, ecx, edx, ebx;
+ uint32_t esp, ebp, esi, edi;
+ uint16_t es, :16;
+ uint16_t cs, :16;
+ uint16_t ss, :16;
+ uint16_t ds, :16;
+ uint16_t fs, :16;
+ uint16_t gs, :16;
+ uint16_t ldt, :16;
+ uint16_t trace, bitmap;
+ };
+
+/* Kernel TSS. */
+static struct tss *tss;
+
+/* Initializes the kernel TSS. */
+void
+tss_init (void)
+{
+ /* Our TSS is never used in a call gate or task gate, so only a
+ few fields of it are ever referenced, and those are the only
+ ones we initialize. */
+ tss = palloc_get_page (PAL_ASSERT | PAL_ZERO);
+ tss->ss0 = SEL_KDSEG;
+ tss->bitmap = 0xdfff;
+ tss_update ();
+}
+
+/* Returns the kernel TSS. */
+struct tss *
+tss_get (void)
+{
+ ASSERT (tss != NULL);
+ return tss;
+}
+
+/* Sets the ring 0 stack pointer in the TSS to point to the end
+ of the thread stack. */
+void
+tss_update (void)
+{
+ ASSERT (tss != NULL);
+ tss->esp0 = (uint8_t *) thread_current () + PGSIZE;
+}
diff --git a/src/userprog/tss.h b/src/userprog/tss.h
new file mode 100644
index 0000000..467bd19
--- /dev/null
+++ b/src/userprog/tss.h
@@ -0,0 +1,11 @@
+#ifndef USERPROG_TSS_H
+#define USERPROG_TSS_H
+
+#include <stdint.h>
+
+struct tss;
+void tss_init (void);
+struct tss *tss_get (void);
+void tss_update (void);
+
+#endif /* userprog/tss.h */