Lab 3
This commit is contained in:
		
							parent
							
								
									2d1187aa3c
								
							
						
					
					
						commit
						a9d7717cc4
					
				
							
								
								
									
										18
									
								
								GNUmakefile
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								GNUmakefile
									
									
									
									
									
								
							@ -138,6 +138,8 @@ $(OBJDIR)/.vars.%: FORCE
 | 
			
		||||
# Include Makefrags for subdirectories
 | 
			
		||||
include boot/Makefrag
 | 
			
		||||
include kern/Makefrag
 | 
			
		||||
include lib/Makefrag
 | 
			
		||||
include user/Makefrag
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
QEMUOPTS = -drive file=$(OBJDIR)/kern/kernel.img,index=0,media=disk,format=raw -serial mon:stdio -gdb tcp::$(GDBPORT)
 | 
			
		||||
@ -298,6 +300,22 @@ myapi.key:
 | 
			
		||||
#handin-prep:
 | 
			
		||||
#	@./handin-prep
 | 
			
		||||
 | 
			
		||||
# For test runs
 | 
			
		||||
 | 
			
		||||
prep-%:
 | 
			
		||||
	$(V)$(MAKE) "INIT_CFLAGS=${INIT_CFLAGS} -DTEST=`case $* in *_*) echo $*;; *) echo user_$*;; esac`" $(IMAGES)
 | 
			
		||||
 | 
			
		||||
run-%-nox-gdb: prep-% pre-qemu
 | 
			
		||||
	$(QEMU) -nographic $(QEMUOPTS) -S
 | 
			
		||||
 | 
			
		||||
run-%-gdb: prep-% pre-qemu
 | 
			
		||||
	$(QEMU) $(QEMUOPTS) -S
 | 
			
		||||
 | 
			
		||||
run-%-nox: prep-% pre-qemu
 | 
			
		||||
	$(QEMU) -nographic $(QEMUOPTS)
 | 
			
		||||
 | 
			
		||||
run-%: prep-% pre-qemu
 | 
			
		||||
	$(QEMU) $(QEMUOPTS)
 | 
			
		||||
 | 
			
		||||
# This magic automatically generates makefile dependencies
 | 
			
		||||
# for header files included from C source files we compile,
 | 
			
		||||
 | 
			
		||||
@ -1,2 +1,2 @@
 | 
			
		||||
LAB=2
 | 
			
		||||
PACKAGEDATE=Wed Sep 12 14:51:29 EDT 2018
 | 
			
		||||
LAB=3
 | 
			
		||||
PACKAGEDATE=Tue Sep 25 12:21:10 EDT 2018
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										135
									
								
								grade-lab3
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										135
									
								
								grade-lab3
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,135 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
 | 
			
		||||
from gradelib import *
 | 
			
		||||
 | 
			
		||||
r = Runner(save("jos.out"),
 | 
			
		||||
           stop_breakpoint("readline"))
 | 
			
		||||
 | 
			
		||||
@test(10)
 | 
			
		||||
def test_divzero():
 | 
			
		||||
    r.user_test("divzero")
 | 
			
		||||
    r.match('Incoming TRAP frame at 0xefffff..',
 | 
			
		||||
            'TRAP frame at 0xf.......',
 | 
			
		||||
            '  trap 0x00000000 Divide error',
 | 
			
		||||
            '  eip  0x008.....',
 | 
			
		||||
            '  ss   0x----0023',
 | 
			
		||||
            '.00001000. free env 00001000',
 | 
			
		||||
            no=['1/0 is ........!'])
 | 
			
		||||
 | 
			
		||||
@test(10)
 | 
			
		||||
def test_softint():
 | 
			
		||||
    r.user_test("softint")
 | 
			
		||||
    r.match('Welcome to the JOS kernel monitor!',
 | 
			
		||||
            'Incoming TRAP frame at 0xefffffbc',
 | 
			
		||||
            'TRAP frame at 0xf.......',
 | 
			
		||||
            '  trap 0x0000000d General Protection',
 | 
			
		||||
            '  eip  0x008.....',
 | 
			
		||||
            '  ss   0x----0023',
 | 
			
		||||
            '.00001000. free env 0000100')
 | 
			
		||||
 | 
			
		||||
@test(10)
 | 
			
		||||
def test_badsegment():
 | 
			
		||||
    r.user_test("badsegment")
 | 
			
		||||
    r.match('Incoming TRAP frame at 0xefffffbc',
 | 
			
		||||
            'TRAP frame at 0xf.......',
 | 
			
		||||
            '  trap 0x0000000d General Protection',
 | 
			
		||||
            '  err  0x00000028',
 | 
			
		||||
            '  eip  0x008.....',
 | 
			
		||||
            '  ss   0x----0023',
 | 
			
		||||
            '.00001000. free env 0000100')
 | 
			
		||||
 | 
			
		||||
end_part("A")
 | 
			
		||||
 | 
			
		||||
@test(5)
 | 
			
		||||
def test_faultread():
 | 
			
		||||
    r.user_test("faultread")
 | 
			
		||||
    r.match('.00001000. user fault va 00000000 ip 008.....',
 | 
			
		||||
            'Incoming TRAP frame at 0xefffffbc',
 | 
			
		||||
            'TRAP frame at 0xf.......',
 | 
			
		||||
            '  trap 0x0000000e Page Fault',
 | 
			
		||||
            '  err  0x00000004.*',
 | 
			
		||||
            '.00001000. free env 0000100',
 | 
			
		||||
            no=['I read ........ from location 0!'])
 | 
			
		||||
 | 
			
		||||
@test(5)
 | 
			
		||||
def test_faultreadkernel():
 | 
			
		||||
    r.user_test("faultreadkernel")
 | 
			
		||||
    r.match('.00001000. user fault va f0100000 ip 008.....',
 | 
			
		||||
            'Incoming TRAP frame at 0xefffffbc',
 | 
			
		||||
            'TRAP frame at 0xf.......',
 | 
			
		||||
            '  trap 0x0000000e Page Fault',
 | 
			
		||||
            '  err  0x00000005.*',
 | 
			
		||||
            '.00001000. free env 00001000',
 | 
			
		||||
            no=['I read ........ from location 0xf0100000!'])
 | 
			
		||||
 | 
			
		||||
@test(5)
 | 
			
		||||
def test_faultwrite():
 | 
			
		||||
    r.user_test("faultwrite")
 | 
			
		||||
    r.match('.00001000. user fault va 00000000 ip 008.....',
 | 
			
		||||
            'Incoming TRAP frame at 0xefffffbc',
 | 
			
		||||
            'TRAP frame at 0xf.......',
 | 
			
		||||
            '  trap 0x0000000e Page Fault',
 | 
			
		||||
            '  err  0x00000006.*',
 | 
			
		||||
            '.00001000. free env 0000100')
 | 
			
		||||
 | 
			
		||||
@test(5)
 | 
			
		||||
def test_faultwritekernel():
 | 
			
		||||
    r.user_test("faultwritekernel")
 | 
			
		||||
    r.match('.00001000. user fault va f0100000 ip 008.....',
 | 
			
		||||
            'Incoming TRAP frame at 0xefffffbc',
 | 
			
		||||
            'TRAP frame at 0xf.......',
 | 
			
		||||
            '  trap 0x0000000e Page Fault',
 | 
			
		||||
            '  err  0x00000007.*',
 | 
			
		||||
            '.00001000. free env 0000100')
 | 
			
		||||
 | 
			
		||||
@test(5)
 | 
			
		||||
def test_breakpoint():
 | 
			
		||||
    r.user_test("breakpoint")
 | 
			
		||||
    r.match('Welcome to the JOS kernel monitor!',
 | 
			
		||||
            'Incoming TRAP frame at 0xefffffbc',
 | 
			
		||||
            'TRAP frame at 0xf.......',
 | 
			
		||||
            '  trap 0x00000003 Breakpoint',
 | 
			
		||||
            '  eip  0x008.....',
 | 
			
		||||
            '  ss   0x----0023',
 | 
			
		||||
            no=['.00001000. free env 00001000'])
 | 
			
		||||
 | 
			
		||||
@test(5)
 | 
			
		||||
def test_testbss():
 | 
			
		||||
    r.user_test("testbss")
 | 
			
		||||
    r.match('Making sure bss works right...',
 | 
			
		||||
            'Yes, good.  Now doing a wild write off the end...',
 | 
			
		||||
            '.00001000. user fault va 00c..... ip 008.....',
 | 
			
		||||
            '.00001000. free env 0000100')
 | 
			
		||||
 | 
			
		||||
@test(5)
 | 
			
		||||
def test_hello():
 | 
			
		||||
    r.user_test("hello")
 | 
			
		||||
    r.match('.00000000. new env 00001000',
 | 
			
		||||
            'hello, world',
 | 
			
		||||
            'i am environment 00001000',
 | 
			
		||||
            '.00001000. exiting gracefully',
 | 
			
		||||
            '.00001000. free env 00001000',
 | 
			
		||||
            'Destroyed the only environment - nothing more to do!')
 | 
			
		||||
 | 
			
		||||
@test(5)
 | 
			
		||||
def test_buggyhello():
 | 
			
		||||
    r.user_test("buggyhello")
 | 
			
		||||
    r.match('.00001000. user_mem_check assertion failure for va 00000001',
 | 
			
		||||
            '.00001000. free env 00001000')
 | 
			
		||||
 | 
			
		||||
@test(5)
 | 
			
		||||
def test_buggyhello2():
 | 
			
		||||
    r.user_test("buggyhello2")
 | 
			
		||||
    r.match('.00001000. user_mem_check assertion failure for va 0....000',
 | 
			
		||||
            '.00001000. free env 00001000',
 | 
			
		||||
            no=['hello, world'])
 | 
			
		||||
 | 
			
		||||
@test(5)
 | 
			
		||||
def test_evilhello():
 | 
			
		||||
    r.user_test("evilhello")
 | 
			
		||||
    r.match('.00001000. user_mem_check assertion failure for va f0100...',
 | 
			
		||||
            '.00001000. free env 00001000')
 | 
			
		||||
 | 
			
		||||
end_part("B")
 | 
			
		||||
 | 
			
		||||
run_tests()
 | 
			
		||||
							
								
								
									
										59
									
								
								inc/env.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								inc/env.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,59 @@
 | 
			
		||||
/* See COPYRIGHT for copyright information. */
 | 
			
		||||
 | 
			
		||||
#ifndef JOS_INC_ENV_H
 | 
			
		||||
#define JOS_INC_ENV_H
 | 
			
		||||
 | 
			
		||||
#include <inc/types.h>
 | 
			
		||||
#include <inc/trap.h>
 | 
			
		||||
#include <inc/memlayout.h>
 | 
			
		||||
 | 
			
		||||
typedef int32_t envid_t;
 | 
			
		||||
 | 
			
		||||
// An environment ID 'envid_t' has three parts:
 | 
			
		||||
//
 | 
			
		||||
// +1+---------------21-----------------+--------10--------+
 | 
			
		||||
// |0|          Uniqueifier             |   Environment    |
 | 
			
		||||
// | |                                  |      Index       |
 | 
			
		||||
// +------------------------------------+------------------+
 | 
			
		||||
//                                       \--- ENVX(eid) --/
 | 
			
		||||
//
 | 
			
		||||
// The environment index ENVX(eid) equals the environment's index in the
 | 
			
		||||
// 'envs[]' array.  The uniqueifier distinguishes environments that were
 | 
			
		||||
// created at different times, but share the same environment index.
 | 
			
		||||
//
 | 
			
		||||
// All real environments are greater than 0 (so the sign bit is zero).
 | 
			
		||||
// envid_ts less than 0 signify errors.  The envid_t == 0 is special, and
 | 
			
		||||
// stands for the current environment.
 | 
			
		||||
 | 
			
		||||
#define LOG2NENV		10
 | 
			
		||||
#define NENV			(1 << LOG2NENV)
 | 
			
		||||
#define ENVX(envid)		((envid) & (NENV - 1))
 | 
			
		||||
 | 
			
		||||
// Values of env_status in struct Env
 | 
			
		||||
enum {
 | 
			
		||||
	ENV_FREE = 0,
 | 
			
		||||
	ENV_DYING,
 | 
			
		||||
	ENV_RUNNABLE,
 | 
			
		||||
	ENV_RUNNING,
 | 
			
		||||
	ENV_NOT_RUNNABLE
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Special environment types
 | 
			
		||||
enum EnvType {
 | 
			
		||||
	ENV_TYPE_USER = 0,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Env {
 | 
			
		||||
	struct Trapframe env_tf;	// Saved registers
 | 
			
		||||
	struct Env *env_link;		// Next free Env
 | 
			
		||||
	envid_t env_id;			// Unique environment identifier
 | 
			
		||||
	envid_t env_parent_id;		// env_id of this env's parent
 | 
			
		||||
	enum EnvType env_type;		// Indicates special system environments
 | 
			
		||||
	unsigned env_status;		// Status of the environment
 | 
			
		||||
	uint32_t env_runs;		// Number of times environment has run
 | 
			
		||||
 | 
			
		||||
	// Address space
 | 
			
		||||
	pde_t *env_pgdir;		// Kernel virtual address of page dir
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // !JOS_INC_ENV_H
 | 
			
		||||
							
								
								
									
										56
									
								
								inc/lib.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								inc/lib.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,56 @@
 | 
			
		||||
// Main public header file for our user-land support library,
 | 
			
		||||
// whose code lives in the lib directory.
 | 
			
		||||
// This library is roughly our OS's version of a standard C library,
 | 
			
		||||
// and is intended to be linked into all user-mode applications
 | 
			
		||||
// (NOT the kernel or boot loader).
 | 
			
		||||
 | 
			
		||||
#ifndef JOS_INC_LIB_H
 | 
			
		||||
#define JOS_INC_LIB_H 1
 | 
			
		||||
 | 
			
		||||
#include <inc/types.h>
 | 
			
		||||
#include <inc/stdio.h>
 | 
			
		||||
#include <inc/stdarg.h>
 | 
			
		||||
#include <inc/string.h>
 | 
			
		||||
#include <inc/error.h>
 | 
			
		||||
#include <inc/assert.h>
 | 
			
		||||
#include <inc/env.h>
 | 
			
		||||
#include <inc/memlayout.h>
 | 
			
		||||
#include <inc/syscall.h>
 | 
			
		||||
 | 
			
		||||
#define USED(x)		(void)(x)
 | 
			
		||||
 | 
			
		||||
// main user program
 | 
			
		||||
void	umain(int argc, char **argv);
 | 
			
		||||
 | 
			
		||||
// libmain.c or entry.S
 | 
			
		||||
extern const char *binaryname;
 | 
			
		||||
extern const volatile struct Env *thisenv;
 | 
			
		||||
extern const volatile struct Env envs[NENV];
 | 
			
		||||
extern const volatile struct PageInfo pages[];
 | 
			
		||||
 | 
			
		||||
// exit.c
 | 
			
		||||
void	exit(void);
 | 
			
		||||
 | 
			
		||||
// readline.c
 | 
			
		||||
char*	readline(const char *buf);
 | 
			
		||||
 | 
			
		||||
// syscall.c
 | 
			
		||||
void	sys_cputs(const char *string, size_t len);
 | 
			
		||||
int	sys_cgetc(void);
 | 
			
		||||
envid_t	sys_getenvid(void);
 | 
			
		||||
int	sys_env_destroy(envid_t);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* File open modes */
 | 
			
		||||
#define	O_RDONLY	0x0000		/* open for reading only */
 | 
			
		||||
#define	O_WRONLY	0x0001		/* open for writing only */
 | 
			
		||||
#define	O_RDWR		0x0002		/* open for reading and writing */
 | 
			
		||||
#define	O_ACCMODE	0x0003		/* mask for above modes */
 | 
			
		||||
 | 
			
		||||
#define	O_CREAT		0x0100		/* create if nonexistent */
 | 
			
		||||
#define	O_TRUNC		0x0200		/* truncate to zero length */
 | 
			
		||||
#define	O_EXCL		0x0400		/* error if already exists */
 | 
			
		||||
#define O_MKDIR		0x0800		/* create directory, not regular file */
 | 
			
		||||
 | 
			
		||||
#endif	// !JOS_INC_LIB_H
 | 
			
		||||
							
								
								
									
										13
									
								
								inc/syscall.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								inc/syscall.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
			
		||||
#ifndef JOS_INC_SYSCALL_H
 | 
			
		||||
#define JOS_INC_SYSCALL_H
 | 
			
		||||
 | 
			
		||||
/* system call numbers */
 | 
			
		||||
enum {
 | 
			
		||||
	SYS_cputs = 0,
 | 
			
		||||
	SYS_cgetc,
 | 
			
		||||
	SYS_getenvid,
 | 
			
		||||
	SYS_env_destroy,
 | 
			
		||||
	NSYSCALLS
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif /* !JOS_INC_SYSCALL_H */
 | 
			
		||||
							
								
								
									
										80
									
								
								inc/trap.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								inc/trap.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,80 @@
 | 
			
		||||
#ifndef JOS_INC_TRAP_H
 | 
			
		||||
#define JOS_INC_TRAP_H
 | 
			
		||||
 | 
			
		||||
// Trap numbers
 | 
			
		||||
// These are processor defined:
 | 
			
		||||
#define T_DIVIDE     0		// divide error
 | 
			
		||||
#define T_DEBUG      1		// debug exception
 | 
			
		||||
#define T_NMI        2		// non-maskable interrupt
 | 
			
		||||
#define T_BRKPT      3		// breakpoint
 | 
			
		||||
#define T_OFLOW      4		// overflow
 | 
			
		||||
#define T_BOUND      5		// bounds check
 | 
			
		||||
#define T_ILLOP      6		// illegal opcode
 | 
			
		||||
#define T_DEVICE     7		// device not available
 | 
			
		||||
#define T_DBLFLT     8		// double fault
 | 
			
		||||
/* #define T_COPROC  9 */	// reserved (not generated by recent processors)
 | 
			
		||||
#define T_TSS       10		// invalid task switch segment
 | 
			
		||||
#define T_SEGNP     11		// segment not present
 | 
			
		||||
#define T_STACK     12		// stack exception
 | 
			
		||||
#define T_GPFLT     13		// general protection fault
 | 
			
		||||
#define T_PGFLT     14		// page fault
 | 
			
		||||
/* #define T_RES    15 */	// reserved
 | 
			
		||||
#define T_FPERR     16		// floating point error
 | 
			
		||||
#define T_ALIGN     17		// aligment check
 | 
			
		||||
#define T_MCHK      18		// machine check
 | 
			
		||||
#define T_SIMDERR   19		// SIMD floating point error
 | 
			
		||||
 | 
			
		||||
// These are arbitrarily chosen, but with care not to overlap
 | 
			
		||||
// processor defined exceptions or interrupt vectors.
 | 
			
		||||
#define T_SYSCALL   48		// system call
 | 
			
		||||
#define T_DEFAULT   500		// catchall
 | 
			
		||||
 | 
			
		||||
#define IRQ_OFFSET	32	// IRQ 0 corresponds to int IRQ_OFFSET
 | 
			
		||||
 | 
			
		||||
// Hardware IRQ numbers. We receive these as (IRQ_OFFSET+IRQ_WHATEVER)
 | 
			
		||||
#define IRQ_TIMER        0
 | 
			
		||||
#define IRQ_KBD          1
 | 
			
		||||
#define IRQ_SERIAL       4
 | 
			
		||||
#define IRQ_SPURIOUS     7
 | 
			
		||||
#define IRQ_IDE         14
 | 
			
		||||
#define IRQ_ERROR       19
 | 
			
		||||
 | 
			
		||||
#ifndef __ASSEMBLER__
 | 
			
		||||
 | 
			
		||||
#include <inc/types.h>
 | 
			
		||||
 | 
			
		||||
struct PushRegs {
 | 
			
		||||
	/* registers as pushed by pusha */
 | 
			
		||||
	uint32_t reg_edi;
 | 
			
		||||
	uint32_t reg_esi;
 | 
			
		||||
	uint32_t reg_ebp;
 | 
			
		||||
	uint32_t reg_oesp;		/* Useless */
 | 
			
		||||
	uint32_t reg_ebx;
 | 
			
		||||
	uint32_t reg_edx;
 | 
			
		||||
	uint32_t reg_ecx;
 | 
			
		||||
	uint32_t reg_eax;
 | 
			
		||||
} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
struct Trapframe {
 | 
			
		||||
	struct PushRegs tf_regs;
 | 
			
		||||
	uint16_t tf_es;
 | 
			
		||||
	uint16_t tf_padding1;
 | 
			
		||||
	uint16_t tf_ds;
 | 
			
		||||
	uint16_t tf_padding2;
 | 
			
		||||
	uint32_t tf_trapno;
 | 
			
		||||
	/* below here defined by x86 hardware */
 | 
			
		||||
	uint32_t tf_err;
 | 
			
		||||
	uintptr_t tf_eip;
 | 
			
		||||
	uint16_t tf_cs;
 | 
			
		||||
	uint16_t tf_padding3;
 | 
			
		||||
	uint32_t tf_eflags;
 | 
			
		||||
	/* below here only when crossing rings, such as from user to kernel */
 | 
			
		||||
	uintptr_t tf_esp;
 | 
			
		||||
	uint16_t tf_ss;
 | 
			
		||||
	uint16_t tf_padding4;
 | 
			
		||||
} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif /* !__ASSEMBLER__ */
 | 
			
		||||
 | 
			
		||||
#endif /* !JOS_INC_TRAP_H */
 | 
			
		||||
@ -36,7 +36,20 @@ KERN_SRCFILES :=	kern/entry.S \
 | 
			
		||||
KERN_SRCFILES := $(wildcard $(KERN_SRCFILES))
 | 
			
		||||
 | 
			
		||||
# Binary program images to embed within the kernel.
 | 
			
		||||
KERN_BINFILES := 
 | 
			
		||||
# Binary files for LAB3
 | 
			
		||||
KERN_BINFILES :=	user/hello \
 | 
			
		||||
			user/buggyhello \
 | 
			
		||||
			user/buggyhello2 \
 | 
			
		||||
			user/evilhello \
 | 
			
		||||
			user/testbss \
 | 
			
		||||
			user/divzero \
 | 
			
		||||
			user/breakpoint \
 | 
			
		||||
			user/softint \
 | 
			
		||||
			user/badsegment \
 | 
			
		||||
			user/faultread \
 | 
			
		||||
			user/faultreadkernel \
 | 
			
		||||
			user/faultwrite \
 | 
			
		||||
			user/faultwritekernel
 | 
			
		||||
 | 
			
		||||
KERN_OBJFILES := $(patsubst %.c, $(OBJDIR)/%.o, $(KERN_SRCFILES))
 | 
			
		||||
KERN_OBJFILES := $(patsubst %.S, $(OBJDIR)/%.o, $(KERN_OBJFILES))
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,7 @@
 | 
			
		||||
 | 
			
		||||
#include <inc/mmu.h>
 | 
			
		||||
#include <inc/memlayout.h>
 | 
			
		||||
#include <inc/trap.h>
 | 
			
		||||
 | 
			
		||||
# Shift Right Logical 
 | 
			
		||||
#define SRL(val, shamt)		(((val) >> (shamt)) & ~(-1 << (32 - (shamt))))
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										463
									
								
								kern/env.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										463
									
								
								kern/env.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,463 @@
 | 
			
		||||
/* See COPYRIGHT for copyright information. */
 | 
			
		||||
 | 
			
		||||
#include <inc/x86.h>
 | 
			
		||||
#include <inc/mmu.h>
 | 
			
		||||
#include <inc/error.h>
 | 
			
		||||
#include <inc/string.h>
 | 
			
		||||
#include <inc/assert.h>
 | 
			
		||||
#include <inc/elf.h>
 | 
			
		||||
 | 
			
		||||
#include <kern/env.h>
 | 
			
		||||
#include <kern/pmap.h>
 | 
			
		||||
#include <kern/trap.h>
 | 
			
		||||
#include <kern/monitor.h>
 | 
			
		||||
 | 
			
		||||
struct Env *envs = NULL;		// All environments
 | 
			
		||||
struct Env *curenv = NULL;		// The current env
 | 
			
		||||
static struct Env *env_free_list;	// Free environment list
 | 
			
		||||
					// (linked by Env->env_link)
 | 
			
		||||
 | 
			
		||||
#define ENVGENSHIFT	12		// >= LOGNENV
 | 
			
		||||
 | 
			
		||||
// Global descriptor table.
 | 
			
		||||
//
 | 
			
		||||
// Set up global descriptor table (GDT) with separate segments for
 | 
			
		||||
// kernel mode and user mode.  Segments serve many purposes on the x86.
 | 
			
		||||
// We don't use any of their memory-mapping capabilities, but we need
 | 
			
		||||
// them to switch privilege levels. 
 | 
			
		||||
//
 | 
			
		||||
// The kernel and user segments are identical except for the DPL.
 | 
			
		||||
// To load the SS register, the CPL must equal the DPL.  Thus,
 | 
			
		||||
// we must duplicate the segments for the user and the kernel.
 | 
			
		||||
//
 | 
			
		||||
// In particular, the last argument to the SEG macro used in the
 | 
			
		||||
// definition of gdt specifies the Descriptor Privilege Level (DPL)
 | 
			
		||||
// of that descriptor: 0 for kernel and 3 for user.
 | 
			
		||||
//
 | 
			
		||||
struct Segdesc gdt[] =
 | 
			
		||||
{
 | 
			
		||||
	// 0x0 - unused (always faults -- for trapping NULL far pointers)
 | 
			
		||||
	SEG_NULL,
 | 
			
		||||
 | 
			
		||||
	// 0x8 - kernel code segment
 | 
			
		||||
	[GD_KT >> 3] = SEG(STA_X | STA_R, 0x0, 0xffffffff, 0),
 | 
			
		||||
 | 
			
		||||
	// 0x10 - kernel data segment
 | 
			
		||||
	[GD_KD >> 3] = SEG(STA_W, 0x0, 0xffffffff, 0),
 | 
			
		||||
 | 
			
		||||
	// 0x18 - user code segment
 | 
			
		||||
	[GD_UT >> 3] = SEG(STA_X | STA_R, 0x0, 0xffffffff, 3),
 | 
			
		||||
 | 
			
		||||
	// 0x20 - user data segment
 | 
			
		||||
	[GD_UD >> 3] = SEG(STA_W, 0x0, 0xffffffff, 3),
 | 
			
		||||
 | 
			
		||||
	// 0x28 - tss, initialized in trap_init_percpu()
 | 
			
		||||
	[GD_TSS0 >> 3] = SEG_NULL
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Pseudodesc gdt_pd = {
 | 
			
		||||
	sizeof(gdt) - 1, (unsigned long) gdt
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// Converts an envid to an env pointer.
 | 
			
		||||
// If checkperm is set, the specified environment must be either the
 | 
			
		||||
// current environment or an immediate child of the current environment.
 | 
			
		||||
//
 | 
			
		||||
// RETURNS
 | 
			
		||||
//   0 on success, -E_BAD_ENV on error.
 | 
			
		||||
//   On success, sets *env_store to the environment.
 | 
			
		||||
//   On error, sets *env_store to NULL.
 | 
			
		||||
//
 | 
			
		||||
int
 | 
			
		||||
envid2env(envid_t envid, struct Env **env_store, bool checkperm)
 | 
			
		||||
{
 | 
			
		||||
	struct Env *e;
 | 
			
		||||
 | 
			
		||||
	// If envid is zero, return the current environment.
 | 
			
		||||
	if (envid == 0) {
 | 
			
		||||
		*env_store = curenv;
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Look up the Env structure via the index part of the envid,
 | 
			
		||||
	// then check the env_id field in that struct Env
 | 
			
		||||
	// to ensure that the envid is not stale
 | 
			
		||||
	// (i.e., does not refer to a _previous_ environment
 | 
			
		||||
	// that used the same slot in the envs[] array).
 | 
			
		||||
	e = &envs[ENVX(envid)];
 | 
			
		||||
	if (e->env_status == ENV_FREE || e->env_id != envid) {
 | 
			
		||||
		*env_store = 0;
 | 
			
		||||
		return -E_BAD_ENV;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check that the calling environment has legitimate permission
 | 
			
		||||
	// to manipulate the specified environment.
 | 
			
		||||
	// If checkperm is set, the specified environment
 | 
			
		||||
	// must be either the current environment
 | 
			
		||||
	// or an immediate child of the current environment.
 | 
			
		||||
	if (checkperm && e != curenv && e->env_parent_id != curenv->env_id) {
 | 
			
		||||
		*env_store = 0;
 | 
			
		||||
		return -E_BAD_ENV;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	*env_store = e;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Mark all environments in 'envs' as free, set their env_ids to 0,
 | 
			
		||||
// and insert them into the env_free_list.
 | 
			
		||||
// Make sure the environments are in the free list in the same order
 | 
			
		||||
// they are in the envs array (i.e., so that the first call to
 | 
			
		||||
// env_alloc() returns envs[0]).
 | 
			
		||||
//
 | 
			
		||||
void
 | 
			
		||||
env_init(void)
 | 
			
		||||
{
 | 
			
		||||
	// Set up envs array
 | 
			
		||||
	// LAB 3: Your code here.
 | 
			
		||||
 | 
			
		||||
	// Per-CPU part of the initialization
 | 
			
		||||
	env_init_percpu();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Load GDT and segment descriptors.
 | 
			
		||||
void
 | 
			
		||||
env_init_percpu(void)
 | 
			
		||||
{
 | 
			
		||||
	lgdt(&gdt_pd);
 | 
			
		||||
	// The kernel never uses GS or FS, so we leave those set to
 | 
			
		||||
	// the user data segment.
 | 
			
		||||
	asm volatile("movw %%ax,%%gs" : : "a" (GD_UD|3));
 | 
			
		||||
	asm volatile("movw %%ax,%%fs" : : "a" (GD_UD|3));
 | 
			
		||||
	// The kernel does use ES, DS, and SS.  We'll change between
 | 
			
		||||
	// the kernel and user data segments as needed.
 | 
			
		||||
	asm volatile("movw %%ax,%%es" : : "a" (GD_KD));
 | 
			
		||||
	asm volatile("movw %%ax,%%ds" : : "a" (GD_KD));
 | 
			
		||||
	asm volatile("movw %%ax,%%ss" : : "a" (GD_KD));
 | 
			
		||||
	// Load the kernel text segment into CS.
 | 
			
		||||
	asm volatile("ljmp %0,$1f\n 1:\n" : : "i" (GD_KT));
 | 
			
		||||
	// For good measure, clear the local descriptor table (LDT),
 | 
			
		||||
	// since we don't use it.
 | 
			
		||||
	lldt(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// Initialize the kernel virtual memory layout for environment e.
 | 
			
		||||
// Allocate a page directory, set e->env_pgdir accordingly,
 | 
			
		||||
// and initialize the kernel portion of the new environment's address space.
 | 
			
		||||
// Do NOT (yet) map anything into the user portion
 | 
			
		||||
// of the environment's virtual address space.
 | 
			
		||||
//
 | 
			
		||||
// Returns 0 on success, < 0 on error.  Errors include:
 | 
			
		||||
//	-E_NO_MEM if page directory or table could not be allocated.
 | 
			
		||||
//
 | 
			
		||||
static int
 | 
			
		||||
env_setup_vm(struct Env *e)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	struct PageInfo *p = NULL;
 | 
			
		||||
 | 
			
		||||
	// Allocate a page for the page directory
 | 
			
		||||
	if (!(p = page_alloc(ALLOC_ZERO)))
 | 
			
		||||
		return -E_NO_MEM;
 | 
			
		||||
 | 
			
		||||
	// Now, set e->env_pgdir and initialize the page directory.
 | 
			
		||||
	//
 | 
			
		||||
	// Hint:
 | 
			
		||||
	//    - The VA space of all envs is identical above UTOP
 | 
			
		||||
	//	(except at UVPT, which we've set below).
 | 
			
		||||
	//	See inc/memlayout.h for permissions and layout.
 | 
			
		||||
	//	Can you use kern_pgdir as a template?  Hint: Yes.
 | 
			
		||||
	//	(Make sure you got the permissions right in Lab 2.)
 | 
			
		||||
	//    - The initial VA below UTOP is empty.
 | 
			
		||||
	//    - You do not need to make any more calls to page_alloc.
 | 
			
		||||
	//    - Note: In general, pp_ref is not maintained for
 | 
			
		||||
	//	physical pages mapped only above UTOP, but env_pgdir
 | 
			
		||||
	//	is an exception -- you need to increment env_pgdir's
 | 
			
		||||
	//	pp_ref for env_free to work correctly.
 | 
			
		||||
	//    - The functions in kern/pmap.h are handy.
 | 
			
		||||
 | 
			
		||||
	// LAB 3: Your code here.
 | 
			
		||||
 | 
			
		||||
	// UVPT maps the env's own page table read-only.
 | 
			
		||||
	// Permissions: kernel R, user R
 | 
			
		||||
	e->env_pgdir[PDX(UVPT)] = PADDR(e->env_pgdir) | PTE_P | PTE_U;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// Allocates and initializes a new environment.
 | 
			
		||||
// On success, the new environment is stored in *newenv_store.
 | 
			
		||||
//
 | 
			
		||||
// Returns 0 on success, < 0 on failure.  Errors include:
 | 
			
		||||
//	-E_NO_FREE_ENV if all NENV environments are allocated
 | 
			
		||||
//	-E_NO_MEM on memory exhaustion
 | 
			
		||||
//
 | 
			
		||||
int
 | 
			
		||||
env_alloc(struct Env **newenv_store, envid_t parent_id)
 | 
			
		||||
{
 | 
			
		||||
	int32_t generation;
 | 
			
		||||
	int r;
 | 
			
		||||
	struct Env *e;
 | 
			
		||||
 | 
			
		||||
	if (!(e = env_free_list))
 | 
			
		||||
		return -E_NO_FREE_ENV;
 | 
			
		||||
 | 
			
		||||
	// Allocate and set up the page directory for this environment.
 | 
			
		||||
	if ((r = env_setup_vm(e)) < 0)
 | 
			
		||||
		return r;
 | 
			
		||||
 | 
			
		||||
	// Generate an env_id for this environment.
 | 
			
		||||
	generation = (e->env_id + (1 << ENVGENSHIFT)) & ~(NENV - 1);
 | 
			
		||||
	if (generation <= 0)	// Don't create a negative env_id.
 | 
			
		||||
		generation = 1 << ENVGENSHIFT;
 | 
			
		||||
	e->env_id = generation | (e - envs);
 | 
			
		||||
 | 
			
		||||
	// Set the basic status variables.
 | 
			
		||||
	e->env_parent_id = parent_id;
 | 
			
		||||
	e->env_type = ENV_TYPE_USER;
 | 
			
		||||
	e->env_status = ENV_RUNNABLE;
 | 
			
		||||
	e->env_runs = 0;
 | 
			
		||||
 | 
			
		||||
	// Clear out all the saved register state,
 | 
			
		||||
	// to prevent the register values
 | 
			
		||||
	// of a prior environment inhabiting this Env structure
 | 
			
		||||
	// from "leaking" into our new environment.
 | 
			
		||||
	memset(&e->env_tf, 0, sizeof(e->env_tf));
 | 
			
		||||
 | 
			
		||||
	// Set up appropriate initial values for the segment registers.
 | 
			
		||||
	// GD_UD is the user data segment selector in the GDT, and
 | 
			
		||||
	// GD_UT is the user text segment selector (see inc/memlayout.h).
 | 
			
		||||
	// The low 2 bits of each segment register contains the
 | 
			
		||||
	// Requestor Privilege Level (RPL); 3 means user mode.  When
 | 
			
		||||
	// we switch privilege levels, the hardware does various
 | 
			
		||||
	// checks involving the RPL and the Descriptor Privilege Level
 | 
			
		||||
	// (DPL) stored in the descriptors themselves.
 | 
			
		||||
	e->env_tf.tf_ds = GD_UD | 3;
 | 
			
		||||
	e->env_tf.tf_es = GD_UD | 3;
 | 
			
		||||
	e->env_tf.tf_ss = GD_UD | 3;
 | 
			
		||||
	e->env_tf.tf_esp = USTACKTOP;
 | 
			
		||||
	e->env_tf.tf_cs = GD_UT | 3;
 | 
			
		||||
	// You will set e->env_tf.tf_eip later.
 | 
			
		||||
 | 
			
		||||
	// commit the allocation
 | 
			
		||||
	env_free_list = e->env_link;
 | 
			
		||||
	*newenv_store = e;
 | 
			
		||||
 | 
			
		||||
	cprintf("[%08x] new env %08x\n", curenv ? curenv->env_id : 0, e->env_id);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// Allocate len bytes of physical memory for environment env,
 | 
			
		||||
// and map it at virtual address va in the environment's address space.
 | 
			
		||||
// Does not zero or otherwise initialize the mapped pages in any way.
 | 
			
		||||
// Pages should be writable by user and kernel.
 | 
			
		||||
// Panic if any allocation attempt fails.
 | 
			
		||||
//
 | 
			
		||||
static void
 | 
			
		||||
region_alloc(struct Env *e, void *va, size_t len)
 | 
			
		||||
{
 | 
			
		||||
	// LAB 3: Your code here.
 | 
			
		||||
	// (But only if you need it for load_icode.)
 | 
			
		||||
	//
 | 
			
		||||
	// Hint: It is easier to use region_alloc if the caller can pass
 | 
			
		||||
	//   'va' and 'len' values that are not page-aligned.
 | 
			
		||||
	//   You should round va down, and round (va + len) up.
 | 
			
		||||
	//   (Watch out for corner-cases!)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// Set up the initial program binary, stack, and processor flags
 | 
			
		||||
// for a user process.
 | 
			
		||||
// This function is ONLY called during kernel initialization,
 | 
			
		||||
// before running the first user-mode environment.
 | 
			
		||||
//
 | 
			
		||||
// This function loads all loadable segments from the ELF binary image
 | 
			
		||||
// into the environment's user memory, starting at the appropriate
 | 
			
		||||
// virtual addresses indicated in the ELF program header.
 | 
			
		||||
// At the same time it clears to zero any portions of these segments
 | 
			
		||||
// that are marked in the program header as being mapped
 | 
			
		||||
// but not actually present in the ELF file - i.e., the program's bss section.
 | 
			
		||||
//
 | 
			
		||||
// All this is very similar to what our boot loader does, except the boot
 | 
			
		||||
// loader also needs to read the code from disk.  Take a look at
 | 
			
		||||
// boot/main.c to get ideas.
 | 
			
		||||
//
 | 
			
		||||
// Finally, this function maps one page for the program's initial stack.
 | 
			
		||||
//
 | 
			
		||||
// load_icode panics if it encounters problems.
 | 
			
		||||
//  - How might load_icode fail?  What might be wrong with the given input?
 | 
			
		||||
//
 | 
			
		||||
static void
 | 
			
		||||
load_icode(struct Env *e, uint8_t *binary)
 | 
			
		||||
{
 | 
			
		||||
	// Hints:
 | 
			
		||||
	//  Load each program segment into virtual memory
 | 
			
		||||
	//  at the address specified in the ELF segment header.
 | 
			
		||||
	//  You should only load segments with ph->p_type == ELF_PROG_LOAD.
 | 
			
		||||
	//  Each segment's virtual address can be found in ph->p_va
 | 
			
		||||
	//  and its size in memory can be found in ph->p_memsz.
 | 
			
		||||
	//  The ph->p_filesz bytes from the ELF binary, starting at
 | 
			
		||||
	//  'binary + ph->p_offset', should be copied to virtual address
 | 
			
		||||
	//  ph->p_va.  Any remaining memory bytes should be cleared to zero.
 | 
			
		||||
	//  (The ELF header should have ph->p_filesz <= ph->p_memsz.)
 | 
			
		||||
	//  Use functions from the previous lab to allocate and map pages.
 | 
			
		||||
	//
 | 
			
		||||
	//  All page protection bits should be user read/write for now.
 | 
			
		||||
	//  ELF segments are not necessarily page-aligned, but you can
 | 
			
		||||
	//  assume for this function that no two segments will touch
 | 
			
		||||
	//  the same virtual page.
 | 
			
		||||
	//
 | 
			
		||||
	//  You may find a function like region_alloc useful.
 | 
			
		||||
	//
 | 
			
		||||
	//  Loading the segments is much simpler if you can move data
 | 
			
		||||
	//  directly into the virtual addresses stored in the ELF binary.
 | 
			
		||||
	//  So which page directory should be in force during
 | 
			
		||||
	//  this function?
 | 
			
		||||
	//
 | 
			
		||||
	//  You must also do something with the program's entry point,
 | 
			
		||||
	//  to make sure that the environment starts executing there.
 | 
			
		||||
	//  What?  (See env_run() and env_pop_tf() below.)
 | 
			
		||||
 | 
			
		||||
	// LAB 3: Your code here.
 | 
			
		||||
 | 
			
		||||
	// Now map one page for the program's initial stack
 | 
			
		||||
	// at virtual address USTACKTOP - PGSIZE.
 | 
			
		||||
 | 
			
		||||
	// LAB 3: Your code here.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// Allocates a new env with env_alloc, loads the named elf
 | 
			
		||||
// binary into it with load_icode, and sets its env_type.
 | 
			
		||||
// This function is ONLY called during kernel initialization,
 | 
			
		||||
// before running the first user-mode environment.
 | 
			
		||||
// The new env's parent ID is set to 0.
 | 
			
		||||
//
 | 
			
		||||
void
 | 
			
		||||
env_create(uint8_t *binary, enum EnvType type)
 | 
			
		||||
{
 | 
			
		||||
	// LAB 3: Your code here.
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// Frees env e and all memory it uses.
 | 
			
		||||
//
 | 
			
		||||
void
 | 
			
		||||
env_free(struct Env *e)
 | 
			
		||||
{
 | 
			
		||||
	pte_t *pt;
 | 
			
		||||
	uint32_t pdeno, pteno;
 | 
			
		||||
	physaddr_t pa;
 | 
			
		||||
 | 
			
		||||
	// If freeing the current environment, switch to kern_pgdir
 | 
			
		||||
	// before freeing the page directory, just in case the page
 | 
			
		||||
	// gets reused.
 | 
			
		||||
	if (e == curenv)
 | 
			
		||||
		lcr3(PADDR(kern_pgdir));
 | 
			
		||||
 | 
			
		||||
	// Note the environment's demise.
 | 
			
		||||
	cprintf("[%08x] free env %08x\n", curenv ? curenv->env_id : 0, e->env_id);
 | 
			
		||||
 | 
			
		||||
	// Flush all mapped pages in the user portion of the address space
 | 
			
		||||
	static_assert(UTOP % PTSIZE == 0);
 | 
			
		||||
	for (pdeno = 0; pdeno < PDX(UTOP); pdeno++) {
 | 
			
		||||
 | 
			
		||||
		// only look at mapped page tables
 | 
			
		||||
		if (!(e->env_pgdir[pdeno] & PTE_P))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		// find the pa and va of the page table
 | 
			
		||||
		pa = PTE_ADDR(e->env_pgdir[pdeno]);
 | 
			
		||||
		pt = (pte_t*) KADDR(pa);
 | 
			
		||||
 | 
			
		||||
		// unmap all PTEs in this page table
 | 
			
		||||
		for (pteno = 0; pteno <= PTX(~0); pteno++) {
 | 
			
		||||
			if (pt[pteno] & PTE_P)
 | 
			
		||||
				page_remove(e->env_pgdir, PGADDR(pdeno, pteno, 0));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// free the page table itself
 | 
			
		||||
		e->env_pgdir[pdeno] = 0;
 | 
			
		||||
		page_decref(pa2page(pa));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// free the page directory
 | 
			
		||||
	pa = PADDR(e->env_pgdir);
 | 
			
		||||
	e->env_pgdir = 0;
 | 
			
		||||
	page_decref(pa2page(pa));
 | 
			
		||||
 | 
			
		||||
	// return the environment to the free list
 | 
			
		||||
	e->env_status = ENV_FREE;
 | 
			
		||||
	e->env_link = env_free_list;
 | 
			
		||||
	env_free_list = e;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// Frees environment e.
 | 
			
		||||
//
 | 
			
		||||
void
 | 
			
		||||
env_destroy(struct Env *e)
 | 
			
		||||
{
 | 
			
		||||
	env_free(e);
 | 
			
		||||
 | 
			
		||||
	cprintf("Destroyed the only environment - nothing more to do!\n");
 | 
			
		||||
	while (1)
 | 
			
		||||
		monitor(NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// Restores the register values in the Trapframe with the 'iret' instruction.
 | 
			
		||||
// This exits the kernel and starts executing some environment's code.
 | 
			
		||||
//
 | 
			
		||||
// This function does not return.
 | 
			
		||||
//
 | 
			
		||||
void
 | 
			
		||||
env_pop_tf(struct Trapframe *tf)
 | 
			
		||||
{
 | 
			
		||||
	asm volatile(
 | 
			
		||||
		"\tmovl %0,%%esp\n"
 | 
			
		||||
		"\tpopal\n"
 | 
			
		||||
		"\tpopl %%es\n"
 | 
			
		||||
		"\tpopl %%ds\n"
 | 
			
		||||
		"\taddl $0x8,%%esp\n" /* skip tf_trapno and tf_errcode */
 | 
			
		||||
		"\tiret\n"
 | 
			
		||||
		: : "g" (tf) : "memory");
 | 
			
		||||
	panic("iret failed");  /* mostly to placate the compiler */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// Context switch from curenv to env e.
 | 
			
		||||
// Note: if this is the first call to env_run, curenv is NULL.
 | 
			
		||||
//
 | 
			
		||||
// This function does not return.
 | 
			
		||||
//
 | 
			
		||||
void
 | 
			
		||||
env_run(struct Env *e)
 | 
			
		||||
{
 | 
			
		||||
	// Step 1: If this is a context switch (a new environment is running):
 | 
			
		||||
	//	   1. Set the current environment (if any) back to
 | 
			
		||||
	//	      ENV_RUNNABLE if it is ENV_RUNNING (think about
 | 
			
		||||
	//	      what other states it can be in),
 | 
			
		||||
	//	   2. Set 'curenv' to the new environment,
 | 
			
		||||
	//	   3. Set its status to ENV_RUNNING,
 | 
			
		||||
	//	   4. Update its 'env_runs' counter,
 | 
			
		||||
	//	   5. Use lcr3() to switch to its address space.
 | 
			
		||||
	// Step 2: Use env_pop_tf() to restore the environment's
 | 
			
		||||
	//	   registers and drop into user mode in the
 | 
			
		||||
	//	   environment.
 | 
			
		||||
 | 
			
		||||
	// Hint: This function loads the new environment's state from
 | 
			
		||||
	//	e->env_tf.  Go back through the code you wrote above
 | 
			
		||||
	//	and make sure you have set the relevant parts of
 | 
			
		||||
	//	e->env_tf to sensible values.
 | 
			
		||||
 | 
			
		||||
	// LAB 3: Your code here.
 | 
			
		||||
 | 
			
		||||
	panic("env_run not yet implemented");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										35
									
								
								kern/env.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								kern/env.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,35 @@
 | 
			
		||||
/* See COPYRIGHT for copyright information. */
 | 
			
		||||
 | 
			
		||||
#ifndef JOS_KERN_ENV_H
 | 
			
		||||
#define JOS_KERN_ENV_H
 | 
			
		||||
 | 
			
		||||
#include <inc/env.h>
 | 
			
		||||
 | 
			
		||||
extern struct Env *envs;		// All environments
 | 
			
		||||
extern struct Env *curenv;		// Current environment
 | 
			
		||||
extern struct Segdesc gdt[];
 | 
			
		||||
 | 
			
		||||
void	env_init(void);
 | 
			
		||||
void	env_init_percpu(void);
 | 
			
		||||
int	env_alloc(struct Env **e, envid_t parent_id);
 | 
			
		||||
void	env_free(struct Env *e);
 | 
			
		||||
void	env_create(uint8_t *binary, enum EnvType type);
 | 
			
		||||
void	env_destroy(struct Env *e);	// Does not return if e == curenv
 | 
			
		||||
 | 
			
		||||
int	envid2env(envid_t envid, struct Env **env_store, bool checkperm);
 | 
			
		||||
// The following two functions do not return
 | 
			
		||||
void	env_run(struct Env *e) __attribute__((noreturn));
 | 
			
		||||
void	env_pop_tf(struct Trapframe *tf) __attribute__((noreturn));
 | 
			
		||||
 | 
			
		||||
// Without this extra macro, we couldn't pass macros like TEST to
 | 
			
		||||
// ENV_CREATE because of the C pre-processor's argument prescan rule.
 | 
			
		||||
#define ENV_PASTE3(x, y, z) x ## y ## z
 | 
			
		||||
 | 
			
		||||
#define ENV_CREATE(x, type)						\
 | 
			
		||||
	do {								\
 | 
			
		||||
		extern uint8_t ENV_PASTE3(_binary_obj_, x, _start)[];	\
 | 
			
		||||
		env_create(ENV_PASTE3(_binary_obj_, x, _start),		\
 | 
			
		||||
			   type);					\
 | 
			
		||||
	} while (0)
 | 
			
		||||
 | 
			
		||||
#endif // !JOS_KERN_ENV_H
 | 
			
		||||
							
								
								
									
										19
									
								
								kern/init.c
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								kern/init.c
									
									
									
									
									
								
							@ -8,6 +8,8 @@
 | 
			
		||||
#include <kern/console.h>
 | 
			
		||||
#include <kern/pmap.h>
 | 
			
		||||
#include <kern/kclock.h>
 | 
			
		||||
#include <kern/env.h>
 | 
			
		||||
#include <kern/trap.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
@ -29,9 +31,20 @@ i386_init(void)
 | 
			
		||||
	// Lab 2 memory management initialization functions
 | 
			
		||||
	mem_init();
 | 
			
		||||
 | 
			
		||||
	// Drop into the kernel monitor.
 | 
			
		||||
	while (1)
 | 
			
		||||
		monitor(NULL);
 | 
			
		||||
	// Lab 3 user environment initialization functions
 | 
			
		||||
	env_init();
 | 
			
		||||
	trap_init();
 | 
			
		||||
 | 
			
		||||
#if defined(TEST)
 | 
			
		||||
	// Don't touch -- used by grading script!
 | 
			
		||||
	ENV_CREATE(TEST, ENV_TYPE_USER);
 | 
			
		||||
#else
 | 
			
		||||
	// Touch all you want.
 | 
			
		||||
	ENV_CREATE(user_hello, ENV_TYPE_USER);
 | 
			
		||||
#endif // TEST*
 | 
			
		||||
 | 
			
		||||
	// We only have one user environment for now, so just run it.
 | 
			
		||||
	env_run(&envs[0]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -4,12 +4,21 @@
 | 
			
		||||
#include <inc/assert.h>
 | 
			
		||||
 | 
			
		||||
#include <kern/kdebug.h>
 | 
			
		||||
#include <kern/pmap.h>
 | 
			
		||||
#include <kern/env.h>
 | 
			
		||||
 | 
			
		||||
extern const struct Stab __STAB_BEGIN__[];	// Beginning of stabs table
 | 
			
		||||
extern const struct Stab __STAB_END__[];	// End of stabs table
 | 
			
		||||
extern const char __STABSTR_BEGIN__[];		// Beginning of string table
 | 
			
		||||
extern const char __STABSTR_END__[];		// End of string table
 | 
			
		||||
 | 
			
		||||
struct UserStabData {
 | 
			
		||||
	const struct Stab *stabs;
 | 
			
		||||
	const struct Stab *stab_end;
 | 
			
		||||
	const char *stabstr;
 | 
			
		||||
	const char *stabstr_end;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// stab_binsearch(stabs, region_left, region_right, type, addr)
 | 
			
		||||
//
 | 
			
		||||
@ -123,8 +132,24 @@ debuginfo_eip(uintptr_t addr, struct Eipdebuginfo *info)
 | 
			
		||||
		stabstr = __STABSTR_BEGIN__;
 | 
			
		||||
		stabstr_end = __STABSTR_END__;
 | 
			
		||||
	} else {
 | 
			
		||||
		// Can't search for user-level addresses yet!
 | 
			
		||||
  	        panic("User address");
 | 
			
		||||
		// The user-application linker script, user/user.ld,
 | 
			
		||||
		// puts information about the application's stabs (equivalent
 | 
			
		||||
		// to __STAB_BEGIN__, __STAB_END__, __STABSTR_BEGIN__, and
 | 
			
		||||
		// __STABSTR_END__) in a structure located at virtual address
 | 
			
		||||
		// USTABDATA.
 | 
			
		||||
		const struct UserStabData *usd = (const struct UserStabData *) USTABDATA;
 | 
			
		||||
 | 
			
		||||
		// Make sure this memory is valid.
 | 
			
		||||
		// Return -1 if it is not.  Hint: Call user_mem_check.
 | 
			
		||||
		// LAB 3: Your code here.
 | 
			
		||||
 | 
			
		||||
		stabs = usd->stabs;
 | 
			
		||||
		stab_end = usd->stab_end;
 | 
			
		||||
		stabstr = usd->stabstr;
 | 
			
		||||
		stabstr_end = usd->stabstr_end;
 | 
			
		||||
 | 
			
		||||
		// Make sure the STABS and string table memory is valid.
 | 
			
		||||
		// LAB 3: Your code here.
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// String table validity checks
 | 
			
		||||
 | 
			
		||||
@ -10,6 +10,7 @@
 | 
			
		||||
#include <kern/console.h>
 | 
			
		||||
#include <kern/monitor.h>
 | 
			
		||||
#include <kern/kdebug.h>
 | 
			
		||||
#include <kern/trap.h>
 | 
			
		||||
 | 
			
		||||
#define CMDBUF_SIZE	80	// enough for one VGA text line
 | 
			
		||||
 | 
			
		||||
@ -115,6 +116,8 @@ monitor(struct Trapframe *tf)
 | 
			
		||||
	cprintf("Welcome to the JOS kernel monitor!\n");
 | 
			
		||||
	cprintf("Type 'help' for a list of commands.\n");
 | 
			
		||||
 | 
			
		||||
	if (tf != NULL)
 | 
			
		||||
		print_trapframe(tf);
 | 
			
		||||
 | 
			
		||||
	while (1) {
 | 
			
		||||
		buf = readline("K> ");
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										63
									
								
								kern/pmap.c
									
									
									
									
									
								
							
							
						
						
									
										63
									
								
								kern/pmap.c
									
									
									
									
									
								
							@ -8,6 +8,7 @@
 | 
			
		||||
 | 
			
		||||
#include <kern/pmap.h>
 | 
			
		||||
#include <kern/kclock.h>
 | 
			
		||||
#include <kern/env.h>
 | 
			
		||||
 | 
			
		||||
// These variables are set by i386_detect_memory()
 | 
			
		||||
size_t npages;			// Amount of physical memory (in pages)
 | 
			
		||||
@ -150,6 +151,10 @@ mem_init(void)
 | 
			
		||||
	// Your code goes here:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	//////////////////////////////////////////////////////////////////////
 | 
			
		||||
	// Make 'envs' point to an array of size 'NENV' of 'struct Env'.
 | 
			
		||||
	// LAB 3: Your code here.
 | 
			
		||||
 | 
			
		||||
	//////////////////////////////////////////////////////////////////////
 | 
			
		||||
	// Now that we've allocated the initial kernel data structures, we set
 | 
			
		||||
	// up the list of free physical pages. Once we've done so, all further
 | 
			
		||||
@ -173,6 +178,14 @@ mem_init(void)
 | 
			
		||||
	//    - pages itself -- kernel RW, user NONE
 | 
			
		||||
	// Your code goes here:
 | 
			
		||||
 | 
			
		||||
	//////////////////////////////////////////////////////////////////////
 | 
			
		||||
	// Map the 'envs' array read-only by the user at linear address UENVS
 | 
			
		||||
	// (ie. perm = PTE_U | PTE_P).
 | 
			
		||||
	// Permissions:
 | 
			
		||||
	//    - the new image at UENVS  -- kernel R, user R
 | 
			
		||||
	//    - envs itself -- kernel RW, user NONE
 | 
			
		||||
	// LAB 3: Your code here.
 | 
			
		||||
 | 
			
		||||
	//////////////////////////////////////////////////////////////////////
 | 
			
		||||
	// Use the physical memory that 'bootstack' refers to as the kernel
 | 
			
		||||
	// stack.  The kernel stack grows down from virtual address KSTACKTOP.
 | 
			
		||||
@ -430,6 +443,51 @@ tlb_invalidate(pde_t *pgdir, void *va)
 | 
			
		||||
	invlpg(va);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uintptr_t user_mem_check_addr;
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// Check that an environment is allowed to access the range of memory
 | 
			
		||||
// [va, va+len) with permissions 'perm | PTE_P'.
 | 
			
		||||
// Normally 'perm' will contain PTE_U at least, but this is not required.
 | 
			
		||||
// 'va' and 'len' need not be page-aligned; you must test every page that
 | 
			
		||||
// contains any of that range.  You will test either 'len/PGSIZE',
 | 
			
		||||
// 'len/PGSIZE + 1', or 'len/PGSIZE + 2' pages.
 | 
			
		||||
//
 | 
			
		||||
// A user program can access a virtual address if (1) the address is below
 | 
			
		||||
// ULIM, and (2) the page table gives it permission.  These are exactly
 | 
			
		||||
// the tests you should implement here.
 | 
			
		||||
//
 | 
			
		||||
// If there is an error, set the 'user_mem_check_addr' variable to the first
 | 
			
		||||
// erroneous virtual address.
 | 
			
		||||
//
 | 
			
		||||
// Returns 0 if the user program can access this range of addresses,
 | 
			
		||||
// and -E_FAULT otherwise.
 | 
			
		||||
//
 | 
			
		||||
int
 | 
			
		||||
user_mem_check(struct Env *env, const void *va, size_t len, int perm)
 | 
			
		||||
{
 | 
			
		||||
	// LAB 3: Your code here.
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// Checks that environment 'env' is allowed to access the range
 | 
			
		||||
// of memory [va, va+len) with permissions 'perm | PTE_U | PTE_P'.
 | 
			
		||||
// If it can, then the function simply returns.
 | 
			
		||||
// If it cannot, 'env' is destroyed and, if env is the current
 | 
			
		||||
// environment, this function will not return.
 | 
			
		||||
//
 | 
			
		||||
void
 | 
			
		||||
user_mem_assert(struct Env *env, const void *va, size_t len, int perm)
 | 
			
		||||
{
 | 
			
		||||
	if (user_mem_check(env, va, len, perm | PTE_U) < 0) {
 | 
			
		||||
		cprintf("[%08x] user_mem_check assertion failure for "
 | 
			
		||||
			"va %08x\n", env->env_id, user_mem_check_addr);
 | 
			
		||||
		env_destroy(env);	// may not return
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// --------------------------------------------------------------
 | 
			
		||||
// Checking functions.
 | 
			
		||||
@ -595,6 +653,10 @@ check_kern_pgdir(void)
 | 
			
		||||
	for (i = 0; i < n; i += PGSIZE)
 | 
			
		||||
		assert(check_va2pa(pgdir, UPAGES + i) == PADDR(pages) + i);
 | 
			
		||||
 | 
			
		||||
	// check envs array (new test for lab 3)
 | 
			
		||||
	n = ROUNDUP(NENV*sizeof(struct Env), PGSIZE);
 | 
			
		||||
	for (i = 0; i < n; i += PGSIZE)
 | 
			
		||||
		assert(check_va2pa(pgdir, UENVS + i) == PADDR(envs) + i);
 | 
			
		||||
 | 
			
		||||
	// check phys mem
 | 
			
		||||
	for (i = 0; i < npages * PGSIZE; i += PGSIZE)
 | 
			
		||||
@ -611,6 +673,7 @@ check_kern_pgdir(void)
 | 
			
		||||
		case PDX(UVPT):
 | 
			
		||||
		case PDX(KSTACKTOP-1):
 | 
			
		||||
		case PDX(UPAGES):
 | 
			
		||||
		case PDX(UENVS):
 | 
			
		||||
			assert(pgdir[i] & PTE_P);
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,7 @@
 | 
			
		||||
 | 
			
		||||
#include <inc/memlayout.h>
 | 
			
		||||
#include <inc/assert.h>
 | 
			
		||||
struct Env;
 | 
			
		||||
 | 
			
		||||
extern char bootstacktop[], bootstack[];
 | 
			
		||||
 | 
			
		||||
@ -62,6 +63,9 @@ void	page_decref(struct PageInfo *pp);
 | 
			
		||||
 | 
			
		||||
void	tlb_invalidate(pde_t *pgdir, void *va);
 | 
			
		||||
 | 
			
		||||
int	user_mem_check(struct Env *env, const void *va, size_t len, int perm);
 | 
			
		||||
void	user_mem_assert(struct Env *env, const void *va, size_t len, int perm);
 | 
			
		||||
 | 
			
		||||
static inline physaddr_t
 | 
			
		||||
page2pa(struct PageInfo *pp)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										80
									
								
								kern/syscall.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								kern/syscall.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,80 @@
 | 
			
		||||
/* See COPYRIGHT for copyright information. */
 | 
			
		||||
 | 
			
		||||
#include <inc/x86.h>
 | 
			
		||||
#include <inc/error.h>
 | 
			
		||||
#include <inc/string.h>
 | 
			
		||||
#include <inc/assert.h>
 | 
			
		||||
 | 
			
		||||
#include <kern/env.h>
 | 
			
		||||
#include <kern/pmap.h>
 | 
			
		||||
#include <kern/trap.h>
 | 
			
		||||
#include <kern/syscall.h>
 | 
			
		||||
#include <kern/console.h>
 | 
			
		||||
 | 
			
		||||
// Print a string to the system console.
 | 
			
		||||
// The string is exactly 'len' characters long.
 | 
			
		||||
// Destroys the environment on memory errors.
 | 
			
		||||
static void
 | 
			
		||||
sys_cputs(const char *s, size_t len)
 | 
			
		||||
{
 | 
			
		||||
	// Check that the user has permission to read memory [s, s+len).
 | 
			
		||||
	// Destroy the environment if not.
 | 
			
		||||
 | 
			
		||||
	// LAB 3: Your code here.
 | 
			
		||||
 | 
			
		||||
	// Print the string supplied by the user.
 | 
			
		||||
	cprintf("%.*s", len, s);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Read a character from the system console without blocking.
 | 
			
		||||
// Returns the character, or 0 if there is no input waiting.
 | 
			
		||||
static int
 | 
			
		||||
sys_cgetc(void)
 | 
			
		||||
{
 | 
			
		||||
	return cons_getc();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Returns the current environment's envid.
 | 
			
		||||
static envid_t
 | 
			
		||||
sys_getenvid(void)
 | 
			
		||||
{
 | 
			
		||||
	return curenv->env_id;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Destroy a given environment (possibly the currently running environment).
 | 
			
		||||
//
 | 
			
		||||
// Returns 0 on success, < 0 on error.  Errors are:
 | 
			
		||||
//	-E_BAD_ENV if environment envid doesn't currently exist,
 | 
			
		||||
//		or the caller doesn't have permission to change envid.
 | 
			
		||||
static int
 | 
			
		||||
sys_env_destroy(envid_t envid)
 | 
			
		||||
{
 | 
			
		||||
	int r;
 | 
			
		||||
	struct Env *e;
 | 
			
		||||
 | 
			
		||||
	if ((r = envid2env(envid, &e, 1)) < 0)
 | 
			
		||||
		return r;
 | 
			
		||||
	if (e == curenv)
 | 
			
		||||
		cprintf("[%08x] exiting gracefully\n", curenv->env_id);
 | 
			
		||||
	else
 | 
			
		||||
		cprintf("[%08x] destroying %08x\n", curenv->env_id, e->env_id);
 | 
			
		||||
	env_destroy(e);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Dispatches to the correct kernel function, passing the arguments.
 | 
			
		||||
int32_t
 | 
			
		||||
syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5)
 | 
			
		||||
{
 | 
			
		||||
	// Call the function corresponding to the 'syscallno' parameter.
 | 
			
		||||
	// Return any appropriate return value.
 | 
			
		||||
	// LAB 3: Your code here.
 | 
			
		||||
 | 
			
		||||
	panic("syscall not implemented");
 | 
			
		||||
 | 
			
		||||
	switch (syscallno) {
 | 
			
		||||
	default:
 | 
			
		||||
		return -E_INVAL;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										11
									
								
								kern/syscall.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								kern/syscall.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,11 @@
 | 
			
		||||
#ifndef JOS_KERN_SYSCALL_H
 | 
			
		||||
#define JOS_KERN_SYSCALL_H
 | 
			
		||||
#ifndef JOS_KERNEL
 | 
			
		||||
# error "This is a JOS kernel header; user programs should not #include it"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <inc/syscall.h>
 | 
			
		||||
 | 
			
		||||
int32_t syscall(uint32_t num, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5);
 | 
			
		||||
 | 
			
		||||
#endif /* !JOS_KERN_SYSCALL_H */
 | 
			
		||||
							
								
								
									
										218
									
								
								kern/trap.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										218
									
								
								kern/trap.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,218 @@
 | 
			
		||||
#include <inc/mmu.h>
 | 
			
		||||
#include <inc/x86.h>
 | 
			
		||||
#include <inc/assert.h>
 | 
			
		||||
 | 
			
		||||
#include <kern/pmap.h>
 | 
			
		||||
#include <kern/trap.h>
 | 
			
		||||
#include <kern/console.h>
 | 
			
		||||
#include <kern/monitor.h>
 | 
			
		||||
#include <kern/env.h>
 | 
			
		||||
#include <kern/syscall.h>
 | 
			
		||||
 | 
			
		||||
static struct Taskstate ts;
 | 
			
		||||
 | 
			
		||||
/* For debugging, so print_trapframe can distinguish between printing
 | 
			
		||||
 * a saved trapframe and printing the current trapframe and print some
 | 
			
		||||
 * additional information in the latter case.
 | 
			
		||||
 */
 | 
			
		||||
static struct Trapframe *last_tf;
 | 
			
		||||
 | 
			
		||||
/* Interrupt descriptor table.  (Must be built at run time because
 | 
			
		||||
 * shifted function addresses can't be represented in relocation records.)
 | 
			
		||||
 */
 | 
			
		||||
struct Gatedesc idt[256] = { { 0 } };
 | 
			
		||||
struct Pseudodesc idt_pd = {
 | 
			
		||||
	sizeof(idt) - 1, (uint32_t) idt
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static const char *trapname(int trapno)
 | 
			
		||||
{
 | 
			
		||||
	static const char * const excnames[] = {
 | 
			
		||||
		"Divide error",
 | 
			
		||||
		"Debug",
 | 
			
		||||
		"Non-Maskable Interrupt",
 | 
			
		||||
		"Breakpoint",
 | 
			
		||||
		"Overflow",
 | 
			
		||||
		"BOUND Range Exceeded",
 | 
			
		||||
		"Invalid Opcode",
 | 
			
		||||
		"Device Not Available",
 | 
			
		||||
		"Double Fault",
 | 
			
		||||
		"Coprocessor Segment Overrun",
 | 
			
		||||
		"Invalid TSS",
 | 
			
		||||
		"Segment Not Present",
 | 
			
		||||
		"Stack Fault",
 | 
			
		||||
		"General Protection",
 | 
			
		||||
		"Page Fault",
 | 
			
		||||
		"(unknown trap)",
 | 
			
		||||
		"x87 FPU Floating-Point Error",
 | 
			
		||||
		"Alignment Check",
 | 
			
		||||
		"Machine-Check",
 | 
			
		||||
		"SIMD Floating-Point Exception"
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	if (trapno < ARRAY_SIZE(excnames))
 | 
			
		||||
		return excnames[trapno];
 | 
			
		||||
	if (trapno == T_SYSCALL)
 | 
			
		||||
		return "System call";
 | 
			
		||||
	return "(unknown trap)";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
trap_init(void)
 | 
			
		||||
{
 | 
			
		||||
	extern struct Segdesc gdt[];
 | 
			
		||||
 | 
			
		||||
	// LAB 3: Your code here.
 | 
			
		||||
 | 
			
		||||
	// Per-CPU setup 
 | 
			
		||||
	trap_init_percpu();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Initialize and load the per-CPU TSS and IDT
 | 
			
		||||
void
 | 
			
		||||
trap_init_percpu(void)
 | 
			
		||||
{
 | 
			
		||||
	// Setup a TSS so that we get the right stack
 | 
			
		||||
	// when we trap to the kernel.
 | 
			
		||||
	ts.ts_esp0 = KSTACKTOP;
 | 
			
		||||
	ts.ts_ss0 = GD_KD;
 | 
			
		||||
	ts.ts_iomb = sizeof(struct Taskstate);
 | 
			
		||||
 | 
			
		||||
	// Initialize the TSS slot of the gdt.
 | 
			
		||||
	gdt[GD_TSS0 >> 3] = SEG16(STS_T32A, (uint32_t) (&ts),
 | 
			
		||||
					sizeof(struct Taskstate) - 1, 0);
 | 
			
		||||
	gdt[GD_TSS0 >> 3].sd_s = 0;
 | 
			
		||||
 | 
			
		||||
	// Load the TSS selector (like other segment selectors, the
 | 
			
		||||
	// bottom three bits are special; we leave them 0)
 | 
			
		||||
	ltr(GD_TSS0);
 | 
			
		||||
 | 
			
		||||
	// Load the IDT
 | 
			
		||||
	lidt(&idt_pd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
print_trapframe(struct Trapframe *tf)
 | 
			
		||||
{
 | 
			
		||||
	cprintf("TRAP frame at %p\n", tf);
 | 
			
		||||
	print_regs(&tf->tf_regs);
 | 
			
		||||
	cprintf("  es   0x----%04x\n", tf->tf_es);
 | 
			
		||||
	cprintf("  ds   0x----%04x\n", tf->tf_ds);
 | 
			
		||||
	cprintf("  trap 0x%08x %s\n", tf->tf_trapno, trapname(tf->tf_trapno));
 | 
			
		||||
	// If this trap was a page fault that just happened
 | 
			
		||||
	// (so %cr2 is meaningful), print the faulting linear address.
 | 
			
		||||
	if (tf == last_tf && tf->tf_trapno == T_PGFLT)
 | 
			
		||||
		cprintf("  cr2  0x%08x\n", rcr2());
 | 
			
		||||
	cprintf("  err  0x%08x", tf->tf_err);
 | 
			
		||||
	// For page faults, print decoded fault error code:
 | 
			
		||||
	// U/K=fault occurred in user/kernel mode
 | 
			
		||||
	// W/R=a write/read caused the fault
 | 
			
		||||
	// PR=a protection violation caused the fault (NP=page not present).
 | 
			
		||||
	if (tf->tf_trapno == T_PGFLT)
 | 
			
		||||
		cprintf(" [%s, %s, %s]\n",
 | 
			
		||||
			tf->tf_err & 4 ? "user" : "kernel",
 | 
			
		||||
			tf->tf_err & 2 ? "write" : "read",
 | 
			
		||||
			tf->tf_err & 1 ? "protection" : "not-present");
 | 
			
		||||
	else
 | 
			
		||||
		cprintf("\n");
 | 
			
		||||
	cprintf("  eip  0x%08x\n", tf->tf_eip);
 | 
			
		||||
	cprintf("  cs   0x----%04x\n", tf->tf_cs);
 | 
			
		||||
	cprintf("  flag 0x%08x\n", tf->tf_eflags);
 | 
			
		||||
	if ((tf->tf_cs & 3) != 0) {
 | 
			
		||||
		cprintf("  esp  0x%08x\n", tf->tf_esp);
 | 
			
		||||
		cprintf("  ss   0x----%04x\n", tf->tf_ss);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
print_regs(struct PushRegs *regs)
 | 
			
		||||
{
 | 
			
		||||
	cprintf("  edi  0x%08x\n", regs->reg_edi);
 | 
			
		||||
	cprintf("  esi  0x%08x\n", regs->reg_esi);
 | 
			
		||||
	cprintf("  ebp  0x%08x\n", regs->reg_ebp);
 | 
			
		||||
	cprintf("  oesp 0x%08x\n", regs->reg_oesp);
 | 
			
		||||
	cprintf("  ebx  0x%08x\n", regs->reg_ebx);
 | 
			
		||||
	cprintf("  edx  0x%08x\n", regs->reg_edx);
 | 
			
		||||
	cprintf("  ecx  0x%08x\n", regs->reg_ecx);
 | 
			
		||||
	cprintf("  eax  0x%08x\n", regs->reg_eax);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
trap_dispatch(struct Trapframe *tf)
 | 
			
		||||
{
 | 
			
		||||
	// Handle processor exceptions.
 | 
			
		||||
	// LAB 3: Your code here.
 | 
			
		||||
 | 
			
		||||
	// Unexpected trap: The user process or the kernel has a bug.
 | 
			
		||||
	print_trapframe(tf);
 | 
			
		||||
	if (tf->tf_cs == GD_KT)
 | 
			
		||||
		panic("unhandled trap in kernel");
 | 
			
		||||
	else {
 | 
			
		||||
		env_destroy(curenv);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
trap(struct Trapframe *tf)
 | 
			
		||||
{
 | 
			
		||||
	// The environment may have set DF and some versions
 | 
			
		||||
	// of GCC rely on DF being clear
 | 
			
		||||
	asm volatile("cld" ::: "cc");
 | 
			
		||||
 | 
			
		||||
	// Check that interrupts are disabled.  If this assertion
 | 
			
		||||
	// fails, DO NOT be tempted to fix it by inserting a "cli" in
 | 
			
		||||
	// the interrupt path.
 | 
			
		||||
	assert(!(read_eflags() & FL_IF));
 | 
			
		||||
 | 
			
		||||
	cprintf("Incoming TRAP frame at %p\n", tf);
 | 
			
		||||
 | 
			
		||||
	if ((tf->tf_cs & 3) == 3) {
 | 
			
		||||
		// Trapped from user mode.
 | 
			
		||||
		assert(curenv);
 | 
			
		||||
 | 
			
		||||
		// Copy trap frame (which is currently on the stack)
 | 
			
		||||
		// into 'curenv->env_tf', so that running the environment
 | 
			
		||||
		// will restart at the trap point.
 | 
			
		||||
		curenv->env_tf = *tf;
 | 
			
		||||
		// The trapframe on the stack should be ignored from here on.
 | 
			
		||||
		tf = &curenv->env_tf;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Record that tf is the last real trapframe so
 | 
			
		||||
	// print_trapframe can print some additional information.
 | 
			
		||||
	last_tf = tf;
 | 
			
		||||
 | 
			
		||||
	// Dispatch based on what type of trap occurred
 | 
			
		||||
	trap_dispatch(tf);
 | 
			
		||||
 | 
			
		||||
	// Return to the current environment, which should be running.
 | 
			
		||||
	assert(curenv && curenv->env_status == ENV_RUNNING);
 | 
			
		||||
	env_run(curenv);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
page_fault_handler(struct Trapframe *tf)
 | 
			
		||||
{
 | 
			
		||||
	uint32_t fault_va;
 | 
			
		||||
 | 
			
		||||
	// Read processor's CR2 register to find the faulting address
 | 
			
		||||
	fault_va = rcr2();
 | 
			
		||||
 | 
			
		||||
	// Handle kernel-mode page faults.
 | 
			
		||||
 | 
			
		||||
	// LAB 3: Your code here.
 | 
			
		||||
 | 
			
		||||
	// We've already handled kernel-mode exceptions, so if we get here,
 | 
			
		||||
	// the page fault happened in user mode.
 | 
			
		||||
 | 
			
		||||
	// Destroy the environment that caused the fault.
 | 
			
		||||
	cprintf("[%08x] user fault va %08x ip %08x\n",
 | 
			
		||||
		curenv->env_id, fault_va, tf->tf_eip);
 | 
			
		||||
	print_trapframe(tf);
 | 
			
		||||
	env_destroy(curenv);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										23
									
								
								kern/trap.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								kern/trap.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
			
		||||
/* See COPYRIGHT for copyright information. */
 | 
			
		||||
 | 
			
		||||
#ifndef JOS_KERN_TRAP_H
 | 
			
		||||
#define JOS_KERN_TRAP_H
 | 
			
		||||
#ifndef JOS_KERNEL
 | 
			
		||||
# error "This is a JOS kernel header; user programs should not #include it"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <inc/trap.h>
 | 
			
		||||
#include <inc/mmu.h>
 | 
			
		||||
 | 
			
		||||
/* The kernel's interrupt descriptor table */
 | 
			
		||||
extern struct Gatedesc idt[];
 | 
			
		||||
extern struct Pseudodesc idt_pd;
 | 
			
		||||
 | 
			
		||||
void trap_init(void);
 | 
			
		||||
void trap_init_percpu(void);
 | 
			
		||||
void print_regs(struct PushRegs *regs);
 | 
			
		||||
void print_trapframe(struct Trapframe *tf);
 | 
			
		||||
void page_fault_handler(struct Trapframe *);
 | 
			
		||||
void backtrace(struct Trapframe *);
 | 
			
		||||
 | 
			
		||||
#endif /* JOS_KERN_TRAP_H */
 | 
			
		||||
							
								
								
									
										55
									
								
								kern/trapentry.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								kern/trapentry.S
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,55 @@
 | 
			
		||||
/* See COPYRIGHT for copyright information. */
 | 
			
		||||
 | 
			
		||||
#include <inc/mmu.h>
 | 
			
		||||
#include <inc/memlayout.h>
 | 
			
		||||
#include <inc/trap.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
###################################################################
 | 
			
		||||
# exceptions/interrupts
 | 
			
		||||
###################################################################
 | 
			
		||||
 | 
			
		||||
/* TRAPHANDLER defines a globally-visible function for handling a trap.
 | 
			
		||||
 * It pushes a trap number onto the stack, then jumps to _alltraps.
 | 
			
		||||
 * Use TRAPHANDLER for traps where the CPU automatically pushes an error code.
 | 
			
		||||
 *
 | 
			
		||||
 * You shouldn't call a TRAPHANDLER function from C, but you may
 | 
			
		||||
 * need to _declare_ one in C (for instance, to get a function pointer
 | 
			
		||||
 * during IDT setup).  You can declare the function with
 | 
			
		||||
 *   void NAME();
 | 
			
		||||
 * where NAME is the argument passed to TRAPHANDLER.
 | 
			
		||||
 */
 | 
			
		||||
#define TRAPHANDLER(name, num)						\
 | 
			
		||||
	.globl name;		/* define global symbol for 'name' */	\
 | 
			
		||||
	.type name, @function;	/* symbol type is function */		\
 | 
			
		||||
	.align 2;		/* align function definition */		\
 | 
			
		||||
	name:			/* function starts here */		\
 | 
			
		||||
	pushl $(num);							\
 | 
			
		||||
	jmp _alltraps
 | 
			
		||||
 | 
			
		||||
/* Use TRAPHANDLER_NOEC for traps where the CPU doesn't push an error code.
 | 
			
		||||
 * It pushes a 0 in place of the error code, so the trap frame has the same
 | 
			
		||||
 * format in either case.
 | 
			
		||||
 */
 | 
			
		||||
#define TRAPHANDLER_NOEC(name, num)					\
 | 
			
		||||
	.globl name;							\
 | 
			
		||||
	.type name, @function;						\
 | 
			
		||||
	.align 2;							\
 | 
			
		||||
	name:								\
 | 
			
		||||
	pushl $0;							\
 | 
			
		||||
	pushl $(num);							\
 | 
			
		||||
	jmp _alltraps
 | 
			
		||||
 | 
			
		||||
.text
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Lab 3: Your code here for generating entry points for the different traps.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Lab 3: Your code here for _alltraps
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										31
									
								
								lib/Makefrag
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								lib/Makefrag
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,31 @@
 | 
			
		||||
OBJDIRS += lib
 | 
			
		||||
 | 
			
		||||
LIB_SRCFILES :=		lib/console.c \
 | 
			
		||||
			lib/libmain.c \
 | 
			
		||||
			lib/exit.c \
 | 
			
		||||
			lib/panic.c \
 | 
			
		||||
			lib/printf.c \
 | 
			
		||||
			lib/printfmt.c \
 | 
			
		||||
			lib/readline.c \
 | 
			
		||||
			lib/string.c \
 | 
			
		||||
			lib/syscall.c
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
LIB_OBJFILES := $(patsubst lib/%.c, $(OBJDIR)/lib/%.o, $(LIB_SRCFILES))
 | 
			
		||||
LIB_OBJFILES := $(patsubst lib/%.S, $(OBJDIR)/lib/%.o, $(LIB_OBJFILES))
 | 
			
		||||
 | 
			
		||||
$(OBJDIR)/lib/%.o: lib/%.c $(OBJDIR)/.vars.USER_CFLAGS
 | 
			
		||||
	@echo + cc[USER] $<
 | 
			
		||||
	@mkdir -p $(@D)
 | 
			
		||||
	$(V)$(CC) -nostdinc $(USER_CFLAGS) -c -o $@ $<
 | 
			
		||||
 | 
			
		||||
$(OBJDIR)/lib/%.o: lib/%.S $(OBJDIR)/.vars.USER_CFLAGS
 | 
			
		||||
	@echo + as[USER] $<
 | 
			
		||||
	@mkdir -p $(@D)
 | 
			
		||||
	$(V)$(CC) -nostdinc $(USER_CFLAGS) -c -o $@ $<
 | 
			
		||||
 | 
			
		||||
$(OBJDIR)/lib/libjos.a: $(LIB_OBJFILES)
 | 
			
		||||
	@echo + ar $@
 | 
			
		||||
	$(V)$(AR) r $@ $(LIB_OBJFILES)
 | 
			
		||||
							
								
								
									
										25
									
								
								lib/console.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								lib/console.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,25 @@
 | 
			
		||||
 | 
			
		||||
#include <inc/string.h>
 | 
			
		||||
#include <inc/lib.h>
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
cputchar(int ch)
 | 
			
		||||
{
 | 
			
		||||
	char c = ch;
 | 
			
		||||
 | 
			
		||||
	// Unlike standard Unix's putchar,
 | 
			
		||||
	// the cputchar function _always_ outputs to the system console.
 | 
			
		||||
	sys_cputs(&c, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
getchar(void)
 | 
			
		||||
{
 | 
			
		||||
	int r;
 | 
			
		||||
	// sys_cgetc does not block, but getchar should.
 | 
			
		||||
	while ((r = sys_cgetc()) == 0)
 | 
			
		||||
		;
 | 
			
		||||
	return r;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										35
									
								
								lib/entry.S
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								lib/entry.S
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,35 @@
 | 
			
		||||
#include <inc/mmu.h>
 | 
			
		||||
#include <inc/memlayout.h>
 | 
			
		||||
 | 
			
		||||
.data
 | 
			
		||||
// Define the global symbols 'envs', 'pages', 'uvpt', and 'uvpd'
 | 
			
		||||
// so that they can be used in C as if they were ordinary global arrays.
 | 
			
		||||
	.globl envs
 | 
			
		||||
	.set envs, UENVS
 | 
			
		||||
	.globl pages
 | 
			
		||||
	.set pages, UPAGES
 | 
			
		||||
	.globl uvpt
 | 
			
		||||
	.set uvpt, UVPT
 | 
			
		||||
	.globl uvpd
 | 
			
		||||
	.set uvpd, (UVPT+(UVPT>>12)*4)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Entrypoint - this is where the kernel (or our parent environment)
 | 
			
		||||
// starts us running when we are initially loaded into a new environment.
 | 
			
		||||
.text
 | 
			
		||||
.globl _start
 | 
			
		||||
_start:
 | 
			
		||||
	// See if we were started with arguments on the stack
 | 
			
		||||
	cmpl $USTACKTOP, %esp
 | 
			
		||||
	jne args_exist
 | 
			
		||||
 | 
			
		||||
	// If not, push dummy argc/argv arguments.
 | 
			
		||||
	// This happens when we are loaded by the kernel,
 | 
			
		||||
	// because the kernel does not know about passing arguments.
 | 
			
		||||
	pushl $0
 | 
			
		||||
	pushl $0
 | 
			
		||||
 | 
			
		||||
args_exist:
 | 
			
		||||
	call libmain
 | 
			
		||||
1:	jmp 1b
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								lib/exit.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								lib/exit.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
			
		||||
 | 
			
		||||
#include <inc/lib.h>
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
exit(void)
 | 
			
		||||
{
 | 
			
		||||
	sys_env_destroy(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										28
									
								
								lib/libmain.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								lib/libmain.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
			
		||||
// Called from entry.S to get us going.
 | 
			
		||||
// entry.S already took care of defining envs, pages, uvpd, and uvpt.
 | 
			
		||||
 | 
			
		||||
#include <inc/lib.h>
 | 
			
		||||
 | 
			
		||||
extern void umain(int argc, char **argv);
 | 
			
		||||
 | 
			
		||||
const volatile struct Env *thisenv;
 | 
			
		||||
const char *binaryname = "<unknown>";
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
libmain(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	// set thisenv to point at our Env structure in envs[].
 | 
			
		||||
	// LAB 3: Your code here.
 | 
			
		||||
	thisenv = 0;
 | 
			
		||||
 | 
			
		||||
	// save the name of the program so that panic() can use it
 | 
			
		||||
	if (argc > 0)
 | 
			
		||||
		binaryname = argv[0];
 | 
			
		||||
 | 
			
		||||
	// call user main routine
 | 
			
		||||
	umain(argc, argv);
 | 
			
		||||
 | 
			
		||||
	// exit gracefully
 | 
			
		||||
	exit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										26
									
								
								lib/panic.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								lib/panic.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
			
		||||
 | 
			
		||||
#include <inc/lib.h>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Panic is called on unresolvable fatal errors.
 | 
			
		||||
 * It prints "panic: <message>", then causes a breakpoint exception,
 | 
			
		||||
 * which causes JOS to enter the JOS kernel monitor.
 | 
			
		||||
 */
 | 
			
		||||
void
 | 
			
		||||
_panic(const char *file, int line, const char *fmt, ...)
 | 
			
		||||
{
 | 
			
		||||
	va_list ap;
 | 
			
		||||
 | 
			
		||||
	va_start(ap, fmt);
 | 
			
		||||
 | 
			
		||||
	// Print the panic message
 | 
			
		||||
	cprintf("[%08x] user panic in %s at %s:%d: ",
 | 
			
		||||
		sys_getenvid(), binaryname, file, line);
 | 
			
		||||
	vcprintf(fmt, ap);
 | 
			
		||||
	cprintf("\n");
 | 
			
		||||
 | 
			
		||||
	// Cause a breakpoint exception
 | 
			
		||||
	while (1)
 | 
			
		||||
		asm volatile("int3");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										62
									
								
								lib/printf.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								lib/printf.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,62 @@
 | 
			
		||||
// Implementation of cprintf console output for user environments,
 | 
			
		||||
// based on printfmt() and the sys_cputs() system call.
 | 
			
		||||
//
 | 
			
		||||
// cprintf is a debugging statement, not a generic output statement.
 | 
			
		||||
// It is very important that it always go to the console, especially when
 | 
			
		||||
// debugging file descriptor code!
 | 
			
		||||
 | 
			
		||||
#include <inc/types.h>
 | 
			
		||||
#include <inc/stdio.h>
 | 
			
		||||
#include <inc/stdarg.h>
 | 
			
		||||
#include <inc/lib.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Collect up to 256 characters into a buffer
 | 
			
		||||
// and perform ONE system call to print all of them,
 | 
			
		||||
// in order to make the lines output to the console atomic
 | 
			
		||||
// and prevent interrupts from causing context switches
 | 
			
		||||
// in the middle of a console output line and such.
 | 
			
		||||
struct printbuf {
 | 
			
		||||
	int idx;	// current buffer index
 | 
			
		||||
	int cnt;	// total bytes printed so far
 | 
			
		||||
	char buf[256];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
putch(int ch, struct printbuf *b)
 | 
			
		||||
{
 | 
			
		||||
	b->buf[b->idx++] = ch;
 | 
			
		||||
	if (b->idx == 256-1) {
 | 
			
		||||
		sys_cputs(b->buf, b->idx);
 | 
			
		||||
		b->idx = 0;
 | 
			
		||||
	}
 | 
			
		||||
	b->cnt++;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
vcprintf(const char *fmt, va_list ap)
 | 
			
		||||
{
 | 
			
		||||
	struct printbuf b;
 | 
			
		||||
 | 
			
		||||
	b.idx = 0;
 | 
			
		||||
	b.cnt = 0;
 | 
			
		||||
	vprintfmt((void*)putch, &b, fmt, ap);
 | 
			
		||||
	sys_cputs(b.buf, b.idx);
 | 
			
		||||
 | 
			
		||||
	return b.cnt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
cprintf(const char *fmt, ...)
 | 
			
		||||
{
 | 
			
		||||
	va_list ap;
 | 
			
		||||
	int cnt;
 | 
			
		||||
 | 
			
		||||
	va_start(ap, fmt);
 | 
			
		||||
	cnt = vcprintf(fmt, ap);
 | 
			
		||||
	va_end(ap);
 | 
			
		||||
 | 
			
		||||
	return cnt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										63
									
								
								lib/syscall.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								lib/syscall.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,63 @@
 | 
			
		||||
// System call stubs.
 | 
			
		||||
 | 
			
		||||
#include <inc/syscall.h>
 | 
			
		||||
#include <inc/lib.h>
 | 
			
		||||
 | 
			
		||||
static inline int32_t
 | 
			
		||||
syscall(int num, int check, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5)
 | 
			
		||||
{
 | 
			
		||||
	int32_t ret;
 | 
			
		||||
 | 
			
		||||
	// Generic system call: pass system call number in AX,
 | 
			
		||||
	// up to five parameters in DX, CX, BX, DI, SI.
 | 
			
		||||
	// Interrupt kernel with T_SYSCALL.
 | 
			
		||||
	//
 | 
			
		||||
	// The "volatile" tells the assembler not to optimize
 | 
			
		||||
	// this instruction away just because we don't use the
 | 
			
		||||
	// return value.
 | 
			
		||||
	//
 | 
			
		||||
	// The last clause tells the assembler that this can
 | 
			
		||||
	// potentially change the condition codes and arbitrary
 | 
			
		||||
	// memory locations.
 | 
			
		||||
 | 
			
		||||
	asm volatile("int %1\n"
 | 
			
		||||
		     : "=a" (ret)
 | 
			
		||||
		     : "i" (T_SYSCALL),
 | 
			
		||||
		       "a" (num),
 | 
			
		||||
		       "d" (a1),
 | 
			
		||||
		       "c" (a2),
 | 
			
		||||
		       "b" (a3),
 | 
			
		||||
		       "D" (a4),
 | 
			
		||||
		       "S" (a5)
 | 
			
		||||
		     : "cc", "memory");
 | 
			
		||||
 | 
			
		||||
	if(check && ret > 0)
 | 
			
		||||
		panic("syscall %d returned %d (> 0)", num, ret);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
sys_cputs(const char *s, size_t len)
 | 
			
		||||
{
 | 
			
		||||
	syscall(SYS_cputs, 0, (uint32_t)s, len, 0, 0, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
sys_cgetc(void)
 | 
			
		||||
{
 | 
			
		||||
	return syscall(SYS_cgetc, 0, 0, 0, 0, 0, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
sys_env_destroy(envid_t envid)
 | 
			
		||||
{
 | 
			
		||||
	return syscall(SYS_env_destroy, 1, envid, 0, 0, 0, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
envid_t
 | 
			
		||||
sys_getenvid(void)
 | 
			
		||||
{
 | 
			
		||||
	 return syscall(SYS_getenvid, 0, 0, 0, 0, 0, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										16
									
								
								user/Makefrag
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								user/Makefrag
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,16 @@
 | 
			
		||||
OBJDIRS += user
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
USERLIBS += jos
 | 
			
		||||
 | 
			
		||||
$(OBJDIR)/user/%.o: user/%.c $(OBJDIR)/.vars.USER_CFLAGS
 | 
			
		||||
	@echo + cc[USER] $<
 | 
			
		||||
	@mkdir -p $(@D)
 | 
			
		||||
	$(V)$(CC) -nostdinc $(USER_CFLAGS) -c -o $@ $<
 | 
			
		||||
 | 
			
		||||
$(OBJDIR)/user/%: $(OBJDIR)/user/%.o $(OBJDIR)/lib/entry.o $(USERLIBS:%=$(OBJDIR)/lib/lib%.a) user/user.ld
 | 
			
		||||
	@echo + ld $@
 | 
			
		||||
	$(V)$(LD) -o $@ $(ULDFLAGS) $(LDFLAGS) -nostdlib $(OBJDIR)/lib/entry.o $@.o -L$(OBJDIR)/lib $(USERLIBS:%=-l%) $(GCC_LIB)
 | 
			
		||||
	$(V)$(OBJDUMP) -S $@ > $@.asm
 | 
			
		||||
	$(V)$(NM) -n $@ > $@.sym
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										11
									
								
								user/badsegment.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								user/badsegment.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,11 @@
 | 
			
		||||
// program to cause a general protection exception
 | 
			
		||||
 | 
			
		||||
#include <inc/lib.h>
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
umain(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	// Try to load the kernel's TSS selector into the DS register.
 | 
			
		||||
	asm volatile("movw $0x28,%ax; movw %ax,%ds");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										10
									
								
								user/breakpoint.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								user/breakpoint.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
			
		||||
// program to cause a breakpoint trap
 | 
			
		||||
 | 
			
		||||
#include <inc/lib.h>
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
umain(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	asm volatile("int $3");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										11
									
								
								user/buggyhello.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								user/buggyhello.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,11 @@
 | 
			
		||||
// buggy hello world -- unmapped pointer passed to kernel
 | 
			
		||||
// kernel should destroy user environment in response
 | 
			
		||||
 | 
			
		||||
#include <inc/lib.h>
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
umain(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	sys_cputs((char*)1, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										13
									
								
								user/buggyhello2.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								user/buggyhello2.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
			
		||||
// buggy hello world 2 -- pointed-to region extends into unmapped memory
 | 
			
		||||
// kernel should destroy user environment in response
 | 
			
		||||
 | 
			
		||||
#include <inc/lib.h>
 | 
			
		||||
 | 
			
		||||
const char *hello = "hello, world\n";
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
umain(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	sys_cputs(hello, 1024*1024);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										13
									
								
								user/divzero.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								user/divzero.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
			
		||||
// buggy program - causes a divide by zero exception
 | 
			
		||||
 | 
			
		||||
#include <inc/lib.h>
 | 
			
		||||
 | 
			
		||||
int zero;
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
umain(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	zero = 0;
 | 
			
		||||
	cprintf("1/0 is %08x!\n", 1/zero);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										12
									
								
								user/evilhello.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								user/evilhello.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
// evil hello world -- kernel pointer passed to kernel
 | 
			
		||||
// kernel should destroy user environment in response
 | 
			
		||||
 | 
			
		||||
#include <inc/lib.h>
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
umain(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	// try to print the kernel entry point as a string!  mua ha ha!
 | 
			
		||||
	sys_cputs((char*)0xf010000c, 100);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										10
									
								
								user/faultread.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								user/faultread.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
			
		||||
// buggy program - faults with a read from location zero
 | 
			
		||||
 | 
			
		||||
#include <inc/lib.h>
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
umain(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	cprintf("I read %08x from location 0!\n", *(unsigned*)0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										10
									
								
								user/faultreadkernel.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								user/faultreadkernel.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
			
		||||
// buggy program - faults with a read from kernel space
 | 
			
		||||
 | 
			
		||||
#include <inc/lib.h>
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
umain(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	cprintf("I read %08x from location 0xf0100000!\n", *(unsigned*)0xf0100000);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										10
									
								
								user/faultwrite.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								user/faultwrite.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
			
		||||
// buggy program - faults with a write to location zero
 | 
			
		||||
 | 
			
		||||
#include <inc/lib.h>
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
umain(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	*(unsigned*)0 = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										10
									
								
								user/faultwritekernel.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								user/faultwritekernel.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
			
		||||
// buggy program - faults with a write to a kernel location
 | 
			
		||||
 | 
			
		||||
#include <inc/lib.h>
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
umain(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	*(unsigned*)0xf0100000 = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								user/hello.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								user/hello.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
			
		||||
// hello, world
 | 
			
		||||
#include <inc/lib.h>
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
umain(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	cprintf("hello, world\n");
 | 
			
		||||
	cprintf("i am environment %08x\n", thisenv->env_id);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								user/softint.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								user/softint.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
			
		||||
// buggy program - causes an illegal software interrupt
 | 
			
		||||
 | 
			
		||||
#include <inc/lib.h>
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
umain(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	asm volatile("int $14");	// page fault
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										27
									
								
								user/testbss.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								user/testbss.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,27 @@
 | 
			
		||||
// test reads and writes to a large bss
 | 
			
		||||
 | 
			
		||||
#include <inc/lib.h>
 | 
			
		||||
 | 
			
		||||
#define ARRAYSIZE (1024*1024)
 | 
			
		||||
 | 
			
		||||
uint32_t bigarray[ARRAYSIZE];
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
umain(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	cprintf("Making sure bss works right...\n");
 | 
			
		||||
	for (i = 0; i < ARRAYSIZE; i++)
 | 
			
		||||
		if (bigarray[i] != 0)
 | 
			
		||||
			panic("bigarray[%d] isn't cleared!\n", i);
 | 
			
		||||
	for (i = 0; i < ARRAYSIZE; i++)
 | 
			
		||||
		bigarray[i] = i;
 | 
			
		||||
	for (i = 0; i < ARRAYSIZE; i++)
 | 
			
		||||
		if (bigarray[i] != i)
 | 
			
		||||
			panic("bigarray[%d] didn't hold its value!\n", i);
 | 
			
		||||
 | 
			
		||||
	cprintf("Yes, good.  Now doing a wild write off the end...\n");
 | 
			
		||||
	bigarray[ARRAYSIZE+1024] = 0;
 | 
			
		||||
	panic("SHOULD HAVE TRAPPED!!!");
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										72
									
								
								user/user.ld
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								user/user.ld
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,72 @@
 | 
			
		||||
/* Simple linker script for JOS user-level programs.
 | 
			
		||||
   See the GNU ld 'info' manual ("info ld") to learn the syntax. */
 | 
			
		||||
 | 
			
		||||
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
 | 
			
		||||
OUTPUT_ARCH(i386)
 | 
			
		||||
ENTRY(_start)
 | 
			
		||||
 | 
			
		||||
SECTIONS
 | 
			
		||||
{
 | 
			
		||||
	/* Load programs at this address: "." means the current address */
 | 
			
		||||
	. = 0x800020;
 | 
			
		||||
 | 
			
		||||
	.text : {
 | 
			
		||||
		*(.text .stub .text.* .gnu.linkonce.t.*)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	PROVIDE(etext = .);	/* Define the 'etext' symbol to this value */
 | 
			
		||||
 | 
			
		||||
	.rodata : {
 | 
			
		||||
		*(.rodata .rodata.* .gnu.linkonce.r.*)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Adjust the address for the data segment to the next page */
 | 
			
		||||
	. = ALIGN(0x1000);
 | 
			
		||||
 | 
			
		||||
	.data : {
 | 
			
		||||
		*(.data)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	PROVIDE(edata = .);
 | 
			
		||||
 | 
			
		||||
	.bss : {
 | 
			
		||||
		*(.bss)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	PROVIDE(end = .);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	/* Place debugging symbols so that they can be found by
 | 
			
		||||
	 * the kernel debugger.
 | 
			
		||||
	 * Specifically, the four words at 0x200000 mark the beginning of
 | 
			
		||||
	 * the stabs, the end of the stabs, the beginning of the stabs
 | 
			
		||||
	 * string table, and the end of the stabs string table, respectively.
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	.stab_info 0x200000 : {
 | 
			
		||||
		LONG(__STAB_BEGIN__);
 | 
			
		||||
		LONG(__STAB_END__);
 | 
			
		||||
		LONG(__STABSTR_BEGIN__);
 | 
			
		||||
		LONG(__STABSTR_END__);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.stab : {
 | 
			
		||||
		__STAB_BEGIN__ = DEFINED(__STAB_BEGIN__) ? __STAB_BEGIN__ : .;
 | 
			
		||||
		*(.stab);
 | 
			
		||||
		__STAB_END__ = DEFINED(__STAB_END__) ? __STAB_END__ : .;
 | 
			
		||||
		BYTE(0)		/* Force the linker to allocate space
 | 
			
		||||
				   for this section */
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	.stabstr : {
 | 
			
		||||
		__STABSTR_BEGIN__ = DEFINED(__STABSTR_BEGIN__) ? __STABSTR_BEGIN__ : .;
 | 
			
		||||
		*(.stabstr);
 | 
			
		||||
		__STABSTR_END__ = DEFINED(__STABSTR_END__) ? __STABSTR_END__ : .;
 | 
			
		||||
		BYTE(0)		/* Force the linker to allocate space
 | 
			
		||||
				   for this section */
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/DISCARD/ : {
 | 
			
		||||
		*(.eh_frame .note.GNU-stack .comment)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user