/* 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>
#include <kern/sched.h>
#include <kern/kdebug.h>

// function that will map a page, used by sys_page_map, sys_env_copy_mem
// and ipc_send
int
_page_map(envid_t srcenvid, void *srcva,
          envid_t dstenvid, void *dstva, int perm){
    //cprintf("dstva: %08x\n",dstva);
    struct Env *srce = 0;
	//struct Env **srcep = &srce;
	
	// get and check environment
	if (envid2env(srcenvid, &srce, 0) < 0) {
        cprintf("bad source\n");
		return -E_BAD_ENV;
    }
	struct Env *dste = 0;
	//struct Env **dstep = &dste;
	
	// get and check environment
	if (envid2env(dstenvid, &dste, 0) < 0) {
        cprintf("bad destination\n");
		return -E_BAD_ENV;
    }
	//check va
	uintptr_t psrcva = ((uintptr_t) srcva);
	uintptr_t pdstva = ((uintptr_t) dstva);
	
	if ((psrcva >= UTOP)||(psrcva%PGSIZE != 0)) {
        //cprintf("src is not page aligned\n");
		return -E_INVAL;
    }
	if ((pdstva >= UTOP)||(pdstva%PGSIZE != 0)) {
        cprintf("dst is not page aligned\n");
        return -E_INVAL;
	}

	//check permissions
	if (((perm&PTE_U) == 0)||((perm&PTE_P) == 0)||
		(((~(PTE_U|PTE_P|PTE_AVAIL|PTE_W))&perm) != 0)) {
        cprintf("permissions are not valid: %08x\n",perm);
		return -E_INVAL;
	}

	// get page
	pte_t *pte = pgdir_walk(srce->env_pgdir, srcva, 0);
	//pte_t **ppte = &pte;
	//struct Page *p = page_lookup(srce->env_pgdir, srcva, 0);
	
	/* if (p == 0) { */
/*         cprintf("could not find src page\n"); */
/*         return -E_INVAL; */
/* 	} */

    if ((pte == NULL)||((*pte&PTE_P) == 0)) {
        //cprintf("srcva not mapped\n");
    	return -E_INVAL;
    }
    
	if (((*pte&PTE_W) == 0)&&((perm&PTE_W) != 0)) {
        cprintf("write permission not allowed\n");
    	return -E_INVAL;
    }
    
    if (page_insert(dste->env_pgdir, pa2page(PTE_ADDR(*pte)), dstva, perm) < 0) 
		return -E_NO_MEM;
    //cprintf("mapped %08x from parent to child %08x\n",srcva,dstva);
    return 0;
	
}

// 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.
	user_mem_assert(curenv, (void *) s, len, 0);
	// Print the string supplied by the user.
	cprintf("%.*s", len, s);
}

// Read a character from the system console.
// Returns the character.
static int
sys_cgetc(void)
{
	int c;

	// The cons_getc() primitive doesn't wait for a character,
	// but the sys_cgetc() system call does.
	while ((c = cons_getc()) == 0)
		/* do nothing */;

	return c;
}

// 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); */
     
    if (e->env_debug) {
        struct Env *parent;
        if (envid2env(e->env_parent_id, &parent, 0) < 0)
            panic("parent not valid");
        if (parent->env_status != ENV_RUNNABLE) {
            parent->env_status = ENV_RUNNABLE;
            parent->env_tf.tf_regs.reg_eax = -1;
            env_destroy(e);
            env_run(parent);
        }
    }
    env_destroy(e);
	return 0;
}

// Deschedule current environment and pick a different one to run.
static void
sys_yield(void)
{
	sched_yield();
}

// Allocate a new environment.
// Returns envid of new environment, or < 0 on error.  Errors are:
//	-E_NO_FREE_ENV if no free environment is available.
static envid_t
sys_exofork(void)
{
	// Create the new environment with env_alloc(), from kern/env.c.
	// It should be left as env_alloc created it, except that
	// status is set to ENV_NOT_RUNNABLE, and the register set is copied
	// from the current environment -- but tweaked so sys_exofork
	// will appear to return 0.
	
	// LAB 4: Your code here.
	// create new environment
	struct Env *newenv = 0;
	struct Env **newenv_p = &newenv;
	if (env_alloc(newenv_p, curenv->env_id) < 0) {
		cprintf("no free environments\n");
		return -E_NO_FREE_ENV;
	}
	//set up state
	newenv->env_status = ENV_NOT_RUNNABLE;
	newenv->env_tf = curenv->env_tf;
	(newenv->env_tf).tf_regs.reg_eax = 0;
	//cprintf("new env id: %08x\n",newenv->env_id);
	return newenv->env_id;
	
}

//copy the state of a currently running environment into a new
//environment
//the new environment has a new envid and a copy-on-write version of memory 
//but is otherwise exactly the same
static envid_t
sys_env_copy(envid_t envid)
{
    struct Env *e = 0;
	struct Env **ep = &e;
	
	if (envid2env(envid, ep, 1) < 0) 
		return -E_BAD_ENV;
	
    struct Env *newenv = 0;
	struct Env **newenv_p = &newenv;
	if (env_alloc(newenv_p, e->env_id) < 0) {
		cprintf("no free environments\n");
		return -E_NO_FREE_ENV;
	}
	//copy state
	newenv->env_status = ENV_NOT_RUNNABLE;
	newenv->env_tf = e->env_tf;
    newenv->env_debug = e->env_debug;
	newenv->env_parent_id = e->env_parent_id;
    newenv->env_runs = e->env_runs;
    newenv->env_pgfault_upcall = e->env_pgfault_upcall;
    newenv->env_ipc_recving = e->env_ipc_recving;
    newenv->env_ipc_dstva = e->env_ipc_dstva;
    newenv->env_ipc_value = e->env_ipc_value;
    newenv->env_ipc_from = e->env_ipc_from;
    newenv->env_ipc_perm = e->env_ipc_perm;

    //cprintf("new env id: %08x\n",newenv->env_id);
	return newenv->env_id;
	
}

//copies the memory of one environment to another, marking the write
//pages as write perm, used to make checkpoint
static int   
sys_env_copy_mem(envid_t srcenvid, envid_t dstenvid, int write_perm, int share_perm)
{
    int ret;

    struct Env *srce = 0;	
	// get and check environment
	if (envid2env(srcenvid, &srce, 1) < 0)
		return -E_BAD_ENV;

	struct Env *dste = 0;	
	// get and check environment
	if (envid2env(dstenvid, &dste, 1) < 0)
		return -E_BAD_ENV;
	
    int i, j;
    void *addr;
    pde_t pde;
    pte_t *pte_p;
    pte_t pte;

    for (i = 0; i < (UTOP>>PDXSHIFT); i++) {
        pde = srce->env_pgdir[i];
        if ((pde&PTE_P) != 0) {           
            pte_p = (pte_t *) KADDR(PTE_ADDR(pde));
            for (j = 0; j < NPTENTRIES; j++) {
                pte = pte_p[j];
                addr = (void *) PGADDR(i,j,0);
                //only map page if present and never map user exception stack
                if (((pte&PTE_P)!=0) && ((uintptr_t)addr != (UXSTACKTOP-PGSIZE))) {
                    //cprintf("permissions: %08x\n",pte&PTE_USER);
                    if ((pte&share_perm)!=0) {
                        //cprintf("copying addr share %08x\n",addr);
                        ret = _page_map(srcenvid, addr, dstenvid, addr, (pte&PTE_USER));
                        if (ret < 0)
                            return ret;
                    } else if ((pte&(PTE_W|write_perm)) != 0) {
                        if (1) {
                            //cprintf("copying addr READ-WRITE %08x\n",addr);
                            //get new page
                            struct Page *p = 0;
                            if (page_alloc(&p) < 0)
                                return -E_NO_MEM;
                            if (page_insert(dste->env_pgdir, p, addr, PTE_P|PTE_U|PTE_W) < 0) {
                                page_free(p);
                                return -E_NO_MEM;
                            }
                            memmove(page2kva(p),KADDR(PTE_ADDR(pte)),PGSIZE);                       
                        } else {
                            ret = _page_map(srcenvid,addr,dstenvid,addr,PTE_U|PTE_P|write_perm);
                            if (ret < 0)
                                return ret;
                            ret = _page_map(srcenvid,addr,srcenvid,addr,PTE_U|PTE_P|write_perm);
                            if (ret < 0)
                                return ret;
                        }

                    } else {
                        //cprintf("copying addr READ-ONLY %08x\n",addr);
                        ret = _page_map(srcenvid,addr,dstenvid,addr,PTE_U|PTE_P);
                        if (ret < 0)
                            return ret;
                    }
                }
            }
        }
    }
    return 0;
}
// Swaps the state of an environment with that of another, 
// should be very careful using this function as some 
// environments depend on their envid to identify themselves
static int
sys_env_swap(envid_t envid1, envid_t envid2) {
    //cprintf("swap env %08x and env %08x\n",envid1, envid2);
    struct Env *e1 = 0;
	
	if (envid2env(envid1, &e1, 1) < 0) 
		return -E_BAD_ENV;
	
    struct Env *e2 = 0;
	
	if (envid2env(envid2, &e2, 1) < 0) 
		return -E_BAD_ENV;
    
    struct Env etemp;
    memmove(&etemp, e1, sizeof(struct Env));
    memmove(e1, e2, sizeof(struct Env));
    memmove(e2, &etemp, sizeof(struct Env));
    e2->env_id = envid2;
    e1->env_id = envid1;
    
    e1->env_status = ENV_NOT_RUNNABLE;
    e2->env_status = ENV_NOT_RUNNABLE;
	return 0;
    
}

// Set envid's env_status to status, which must be ENV_RUNNABLE
// or ENV_NOT_RUNNABLE.
//
// 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.
//	-E_INVAL if status is not a valid status for an environment.
static int
sys_env_set_status(envid_t envid, int status)
{
  	// Hint: Use the 'envid2env' function from kern/env.c to translate an
  	// envid to a struct Env.
	// You should set envid2env's third argument to 1, which will
	// check whether the current environment has permission to set
	// envid's status.
	
	// LAB 4: Your code here.
	if ((status != ENV_RUNNABLE)&&(status != ENV_NOT_RUNNABLE))
		return -E_INVAL;

	struct Env *e = 0;
	struct Env **ep = &e;
	
	if (envid2env(envid, ep, 1) < 0) 
		return -E_BAD_ENV;
	
	e->env_status = status;
	return 0;
	
}

// Set envid's trap frame to 'tf'.
// tf is modified to make sure that user environments always run at code
// protection level 3 (CPL 3) with interrupts enabled.
//
// 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_set_trapframe(envid_t envid, struct Trapframe *tf)
{
	// LAB 4: Your code here.
	// Remember to check whether the user has supplied us with a good
	// address!
	if (user_mem_check(curenv, tf, sizeof(struct Trapframe), PTE_U) < 0) 
		//not sure if this is what should be return in the case of a bad pointer
		return -E_BAD_ENV;
	struct Env *e = 0;
	if (envid2env(envid, &e, 1) < 0) 
		return -E_BAD_ENV;
	e->env_tf = *tf;
	return 0;
}

// Set the page fault upcall for 'envid' by modifying the corresponding struct
// Env's 'env_pgfault_upcall' field.  When 'envid' causes a page fault, the
// kernel will push a fault record onto the exception stack, then branch to
// 'func'.
//
// 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_set_pgfault_upcall(envid_t envid, void *func)
{

	// LAB 4: Your code here.
    /* if (user_mem_check(curenv, &envs[ENVX(envid)], sizeof(struct Env), PTE_U) < 0) */
/*         return -E_BAD_ENV; */

    struct Env *e = 0;
    struct Env **ep = &e;
    if (envid2env(envid, ep, 1) < 0)
        return -E_BAD_ENV;
    //cprintf("set page fault up call for env %08x\n",e->env_id);
	
    e->env_pgfault_upcall = func;
    //cprintf("upcall: %08x\n",e->env_pgfault_upcall);
    
    return 0;

}

// Allocate a page of memory and map it at 'va' with permission
// 'perm' in the address space of 'envid'.
// The page's contents are set to 0.
// If a page is already mapped at 'va', that page is unmapped as a
// side effect.
//
// perm -- PTE_U | PTE_P must be set, PTE_AVAIL | PTE_W may or may not be set,
//         but no other bits may be set.
//
// Return 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.
//	-E_INVAL if va >= UTOP, or va is not page-aligned.
//	-E_INVAL if perm is inappropriate (see above).
//	-E_NO_MEM if there's no memory to allocate the new page,
//		or to allocate any necessary page tables.
static int
sys_page_alloc(envid_t envid, void *va, int perm)
{
	// Hint: This function is a wrapper around page_alloc() and
	//   page_insert() from kern/pmap.c.
	//   Most of the new code you write should be to check the
	//   parameters for correctness.
	//   If page_insert() fails, remember to free the page you
	//   allocated!

	//cprintf("map new page at %08x\n",va);
		
	// LAB 4: Your code here.
	struct Env *e = 0;
	//struct Env **ep = &e;
	
	// get and check environment
	if (envid2env(envid, &e, 1) < 0)
		return -E_BAD_ENV;

	//check va
	uintptr_t pva = ((uintptr_t) va);
	if ((pva >= UTOP)||(pva%PGSIZE != 0))
		return -E_INVAL;

	//check permissions
	if (((perm&PTE_U) == 0)||((perm&PTE_P) == 0)||
		(((~(PTE_U|PTE_P|PTE_AVAIL|PTE_W))&perm) != 0))
		return -E_INVAL;

	//get new page
	struct Page *p = 0;
	//struct Page **pp = &p;
	
	if (page_alloc(&p) < 0)
		return -E_NO_MEM;
	
	if (page_insert(e->env_pgdir, p, va, perm) < 0) {
		page_free(p);
		return -E_NO_MEM;
	}

	//clear the page
	memset(page2kva(p),0,PGSIZE);
    //cprintf("all done allocating page\n");
	return 0;

}



// Map the page of memory at 'srcva' in srcenvid's address space
// at 'dstva' in dstenvid's address space with permission 'perm'.
// Perm has the same restrictions as in sys_page_alloc, except
// that it also must not grant write access to a read-only
// page.
//
// Return 0 on success, < 0 on error.  Errors are:
//	-E_BAD_ENV if srcenvid and/or dstenvid doesn't currently exist,
//		or the caller doesn't have permission to change one of them.
//	-E_INVAL if srcva >= UTOP or srcva is not page-aligned,
//		or dstva >= UTOP or dstva is not page-aligned.
//	-E_INVAL is srcva is not mapped in srcenvid's address space.
//	-E_INVAL if perm is inappropriate (see sys_page_alloc).
//	-E_INVAL if (perm & PTE_W), but srcva is read-only in srcenvid's
//		address space.
//	-E_NO_MEM if there's no memory to allocate the new page,
//		or to allocate any necessary page tables.
static int
sys_page_map(envid_t srcenvid, void *srcva,
	     envid_t dstenvid, void *dstva, int perm)
{
	// Hint: This function is a wrapper around page_lookup() and
	//   page_insert() from kern/pmap.c.
	//   Again, most of the new code you write should be to check the
	//   parameters for correctness.
	//   Use the third argument to page_lookup() to
	//   check the current permissions on the page.

    //cprintf("map %08x to %08x\n",srcva,dstva);
	// LAB 4: Your code here.
	return _page_map(srcenvid, srcva, dstenvid, dstva, perm);		
}

// Unmap the page of memory at 'va' in the address space of 'envid'.
// If no page is mapped, the function silently succeeds.
//
// Return 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.
//	-E_INVAL if va >= UTOP, or va is not page-aligned.
static int
sys_page_unmap(envid_t envid, void *va)
{
	// Hint: This function is a wrapper around page_remove().
	
	// LAB 4: Your code here.
	struct Env *e = 0;
	//struct Env **ep = &e;
	
	// get and check environment
	if (envid2env(envid, &e, 1) < 0)
		return -E_BAD_ENV;
    
	//check va
	uint32_t pva = ((uint32_t) va);
	if ((pva >= UTOP)||(pva%PGSIZE != 0))
		return -E_INVAL;

	page_remove(e->env_pgdir,va);
	return 0;
}

// Try to send 'value' to the target env 'envid'.
// If va != 0, then also send page currently mapped at 'va',
// so that receiver gets a duplicate mapping of the same page.
//
// The send fails with a return value of -E_IPC_NOT_RECV if the
// target has not requested IPC with sys_ipc_recv.
//
// Otherwise, the send succeeds, and the target's ipc fields are
// updated as follows:
//    env_ipc_recving is set to 0 to block future sends;
//    env_ipc_from is set to the sending envid;
//    env_ipc_value is set to the 'value' parameter;
//    env_ipc_perm is set to 'perm' if a page was transferred, 0 otherwise.
// The target environment is marked runnable again, returning 0
// from the paused ipc_recv system call.
//
// If the sender sends a page but the receiver isn't asking for one,
// then no page mapping is transferred, but no error occurs.
// The ipc doesn't happen unless no errors occur.
//
// Returns 0 on success where no page mapping occurs,
// 1 on success where a page mapping occurs, and < 0 on error.
// Errors are:
//	-E_BAD_ENV if environment envid doesn't currently exist.
//		(No need to check permissions.)
//	-E_IPC_NOT_RECV if envid is not currently blocked in sys_ipc_recv,
//		or another environment managed to send first.
//	-E_INVAL if srcva < UTOP but srcva is not page-aligned.
//	-E_INVAL if srcva < UTOP and perm is inappropriate
//		(see sys_page_alloc).
//	-E_INVAL if srcva < UTOP but srcva is not mapped in the caller's
//		address space.
//	-E_NO_MEM if there's not enough memory to map srcva in envid's
//		address space.
static int
sys_ipc_try_send(envid_t envid, uint32_t value, void *srcva, unsigned perm)
{

    //cprintf("env %08x trying to send %d\n",envid,value);
	
	// LAB 4: Your code here.
    struct Env *e = 0;
	struct Env **ep = &e;
	
	// get and check environment
	if (envid2env(envid, ep, 0) < 0){
        cprintf("bad env: %08x\n",envid);
		return -E_BAD_ENV;
    }
    if (!e->env_ipc_recving)
        return -E_IPC_NOT_RECV;

    e->env_ipc_recving = 0;
    e->env_ipc_from = curenv->env_id;
    //cprintf("current env: %08x\n",curenv->env_id);
    e->env_ipc_value = value;
    //cprintf("current env: %d\n",value);
    
    //check va
	uintptr_t psrcva = ((uintptr_t) srcva);
	uintptr_t pdstva = ((uintptr_t) e->env_ipc_dstva);
    
    
    if ((psrcva < UTOP) && (pdstva < UTOP)) {
        int ret = _page_map(0, srcva, envid, e->env_ipc_dstva, perm);
        if (ret < 0){
            cprintf("could not map page\n");
            return ret;
        }
        e->env_ipc_perm = perm;
        e->env_tf.tf_regs.reg_eax = 0;
        e->env_status = ENV_RUNNABLE;    
        //cprintf("sent to %08x\n",envid);
        return 1;
    } else {
        e->env_ipc_perm = 0;
        e->env_tf.tf_regs.reg_eax = 0;
        e->env_status = ENV_RUNNABLE;    
        //cprintf("sent to %08x\n",envid);
        return 0;
    }
}

// Block until a value is ready.  Record that you want to receive
// using the env_ipc_recving and env_ipc_dstva fields of struct Env,
// mark yourself not runnable, and then give up the CPU.
//
// If 'dstva' is < UTOP, then you are willing to receive a page of data.
// 'dstva' is the virtual address at which the sent page should be mapped.
//
// This function only returns on error, but the system call will eventually
// return 0 on success.
// Return < 0 on error.  Errors are:
//	-E_INVAL if dstva < UTOP but dstva is not page-aligned.
static int
sys_ipc_recv(void *dstva)
{
    //cprintf("env %08x is receiving now\n",curenv->env_id);
	// LAB 4: Your code here.
	uintptr_t pdstva = ((uintptr_t) dstva);
    if ((pdstva < UTOP) && (pdstva%PGSIZE != 0)) 
        return -E_INVAL;
    curenv->env_ipc_recving = 1;
    curenv->env_ipc_dstva = dstva;
    curenv->env_status = ENV_NOT_RUNNABLE;
    sched_yield();
	return 0;
}

void return_to_debugger(int line) {
    curenv->env_status = ENV_NOT_RUNNABLE;
    //cprintf("eip: %08x\n",curenv->env_tf.tf_eip);
    struct Env *e;
    if (envid2env(curenv->env_parent_id, &e, 0) < 0)
        panic("could not find parent");
    e->env_status = ENV_RUNNABLE;
    e->env_tf.tf_regs.reg_eax = line;
    env_run(e);
}

static int
sys_debug_run(envid_t envid) {
    
    struct Env *e;
    if (envid2env(envid, &e, 0) <  0){
        panic("can't find environment\n");
    }
    curenv->env_status = ENV_NOT_RUNNABLE;
    //cprintf("ebx=%d\n",e->env_tf.tf_regs.reg_ebx);
    e->env_debug = 1;
    e->env_status = ENV_RUNNABLE;
    env_run(e);
    
    
}

static int
sys_debug_step(envid_t envid) {

   
    struct Env *e;
    if (envid2env(envid, &e, 0) <  0){
        panic("can't find environment\n");
    }
    curenv->env_status = ENV_NOT_RUNNABLE;
    
    e->env_tf.tf_eflags |= 0x100;
    e->env_debug = 1;
    e->env_status = ENV_RUNNABLE;
    env_run(e);
}


static void 
sys_debug_peek(envid_t envid, char *var) {
    struct Env *e;
    if (envid2env(envid, &e, 0) <  0){
        panic("can't find environment\n");
    }

    uint32_t addr = var_addr(var, &(e->env_tf));

    if (addr) {
        pte_t *pte = pgdir_walk(e->env_pgdir,(void*) addr, 0);
        int val = *((int *) (KADDR(PTE_ADDR(*pte) + PGOFF(addr))));
        //cprintf("va = %08x, ka = %08x\n",addr,KADDR(PTE_ADDR(*pte) + PGOFF(addr)));
        cprintf("%s = %d\n",var,val);
    }
}

static void
sys_debug_poke(envid_t envid, char *var, int value) {
    
    struct Env *e;
    if (envid2env(envid, &e, 0) <  0){
        panic("can't find environment\n");
    }
    uint32_t addr = set_addr(var, &(e->env_tf), (uint32_t) value);
    
    if (addr) {
        pte_t *pte = pgdir_walk(e->env_pgdir,(void*) addr, 0);
        *((int *) (KADDR(PTE_ADDR(*pte) + PGOFF(addr)))) = value;
        //cprintf("va = %08x, ka = %08x\n",addr,KADDR(PTE_ADDR(*pte) + PGOFF(addr)));
        cprintf("%s = %d\n",var,value);
    } 
}

static int
sys_debug_file(envid_t envid, char *file_name, int *line) {
    struct Env *e;
    char buf[100];
    int r;
    if ((r = envid2env(envid, &e, 0)) <  0){
        panic("can't find environment\n");
    }
    struct Eipdebuginfo info;
    if (debuginfo_eip(e->env_tf.tf_eip, &info) < 0) {
        cprintf("could not find file: %s\nline: %d\n",info.eip_file,info.eip_line);
        return -1;
    }
    cprintf("(%s) %s\n",info.eip_file, info.eip_fn_name);
    strcpy(file_name, info.eip_file);
    *line = info.eip_line;
    return 0;
}
    
static void
sys_breakpoint() 
{
    if (curenv->env_debug) {
        curenv->env_tf.tf_eflags = curenv->env_tf.tf_eflags | 0x100;
        return;
    }
}


// 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.
    switch(syscallno){
	case SYS_cputs:
		sys_cputs((char *) a1, a2);
		return 0;
		break;
	case SYS_cgetc:
		return sys_cgetc();
		break;
	case SYS_getenvid:
		return sys_getenvid();
		break;
	case SYS_env_destroy:
		return sys_env_destroy(a1);
		break;
	case SYS_yield:
		sys_yield();
		return 0;
		break;
	case SYS_exofork:
		return sys_exofork();
		break;
	case SYS_env_set_status:
		return sys_env_set_status(a1,a2);
		break;
	case SYS_page_alloc:
		return sys_page_alloc(a1,(void *)a2,a3);
		break;
	case SYS_page_map:
		return sys_page_map(a1,(void *)a2,a3,(void *)a4,a5);
		break;
	case SYS_page_unmap:
		return sys_page_unmap(a1,(void *)a2);
		break;
    case SYS_env_set_trapframe:
        return sys_env_set_trapframe(a1, (void *) a2);
        break;
    case SYS_env_set_pgfault_upcall:
        return sys_env_set_pgfault_upcall(a1, (void *)a2);
        break;
    case SYS_ipc_try_send:
        return sys_ipc_try_send(a1, a2, (void *)a3, a4);
        break;
    case SYS_ipc_recv:
        return sys_ipc_recv((void *)a1);
        break;
    case SYS_env_copy:
        return sys_env_copy(a1);
        break;
    case SYS_env_swap:
        return sys_env_swap(a1,a2);
        break;
    case SYS_env_copy_mem:
        return sys_env_copy_mem(a1,a2,a3,a4);
        break;
    case SYS_debug_run:
        return sys_debug_run(a1);
        break;
    case SYS_debug_step:
        return sys_debug_step(a1);
        break;
    case SYS_debug_peek:
        sys_debug_peek(a1, (char *)a2);
        return 0;
        break;
    case SYS_debug_poke:
        sys_debug_poke(a1, (char *)a2, a3);
        return 0;
        break;
    case SYS_debug_file:
        return sys_debug_file(a1, (char *)a2, (int *)a3);
        break;
    default:
		return -E_INVAL;
	}
	//panic("syscall not implemented");
}

