Esempio n. 1
0
/**
 * Initializes memory mirror.
 *
 * Memory mirror allocates all available ram to specific range address.
 * If 1GB paging is supported, 1GB paging is used, otherwise only 4KB
 * paging is used, which means much more physical ram is used.
 */
void initialize_memory_mirror() {
    section_info_t* head = frame_pool;
    frame_pool = NULL;
    bool first_set = false;
    if (is_1GB_paging_supported() != 0) {
        for (puint_t start=0; start < maxram; start+=1<<30) {
            puint_t vaddress = start + ADDRESS_OFFSET(RESERVED_KBLOCK_RAM_MAPPINGS);
            v_address_t va;
            memcpy(&va, &vaddress, 8);

            puint_t* pml4 = (puint_t*)ALIGN(physical_to_virtual(get_active_page()));
            if (!PRESENT(pml4[va.pml])) {
                pdpt_t pdpt;
                memset(&pdpt, 0, sizeof(pdpt_t));
                pdpt.number = get_free_frame();
                pdpt.flaggable.present = 1;
                pdpt.flaggable.us = 0;
                pdpt.flaggable.rw = 1;
                pml4[va.pml] = pdpt.number;
                memset((void*)physical_to_virtual(ALIGN(pdpt.number)), 0, 0x1000);
            }

            puint_t* pdpt = (puint_t*)ALIGN(physical_to_virtual(pml4[va.pml]));

            page_directory1GB_t dir;
            memset(&dir, 0, sizeof(page_directory1GB_t));
            dir.number = start;
            dir.flaggable.present = 1;
            dir.flaggable.ps = 1;
            dir.flaggable.rw = 1;
            pdpt[va.directory_ptr] = dir.number;

            if (!first_set) {
                first_set = true;
                frame_pool = head;
            }
        }
    } else {
        for (puint_t start=0; start < maxram; start+=0x1000) {
            puint_t kend = kernel_tmp_heap_start - 0xFFFFFFFF80000000 + 0x40000;
            if (kend+0x4000 >= tmp_heap && !first_set) {
                first_set = true;
                frame_pool = head;
            }

            puint_t vaddress = start + ADDRESS_OFFSET(RESERVED_KBLOCK_RAM_MAPPINGS);
            puint_t* paddress = get_page(vaddress, get_active_page(), true);
            page_t page;
            memset(&page, 0, sizeof(page_t));
            page.address = start;
            page.flaggable.present = 1;
            page.flaggable.rw = 1;
            page.flaggable.us = 0;
            *paddress = page.address;
        }

    }
}
Esempio n. 2
0
int arp_register()
{
	uint32_t output_size = 0;
	ncp_header m_ncp ;
	volatile frame * my_frame = get_free_frame();

	create_arp_req(my_frame);
	TransmitPacket(my_frame->f_data, my_frame->f_len);
	release_frame(my_frame);
	return 0;
}
Esempio n. 3
0
int client_register()
{
	uint32_t output_size = 0;

	ncp_header m_ncp ;
	volatile frame * my_frame = get_free_frame();
	ncp_register(&m_ncp,NCP_HEADER_SIZE);

	my_frame->f_len = create_packiet(my_frame->f_data,ETHER_MAX_LEN,&m_ncp,NCP_HEADER_SIZE);
	TransmitPacket(my_frame->f_data, my_frame->f_len);
	release_frame(my_frame);
	return 0;
}
Esempio n. 4
0
/* get the output surface */
static QSVFrame *query_frame(QSVVPPContext *s, AVFilterLink *outlink)
{
    AVFilterContext *ctx = outlink->src;
    QSVFrame        *out_frame;
    int              ret;

    clear_unused_frames(s->out_frame_list);

    out_frame = get_free_frame(&s->out_frame_list);
    if (!out_frame)
        return NULL;

    /* For video memory, get a hw frame;
     * For system memory, get a sw frame and map it into a mfx_surface. */
    if (!IS_SYSTEM_MEMORY(s->out_mem_mode)) {
        out_frame->frame = av_frame_alloc();
        if (!out_frame->frame)
            return NULL;

        ret = av_hwframe_get_buffer(outlink->hw_frames_ctx, out_frame->frame, 0);
        if (ret < 0) {
            av_log(ctx, AV_LOG_ERROR, "Can't allocate a surface.\n");
            return NULL;
        }

        out_frame->surface = (mfxFrameSurface1 *)out_frame->frame->data[3];
    } else {
        /* Get a frame with aligned dimensions.
         * Libmfx need system memory being 128x64 aligned */
        out_frame->frame = ff_get_video_buffer(outlink,
                                               FFALIGN(outlink->w, 128),
                                               FFALIGN(outlink->h, 64));
        if (!out_frame->frame)
            return NULL;

        out_frame->frame->width  = outlink->w;
        out_frame->frame->height = outlink->h;

        ret = map_frame_to_surface(out_frame->frame,
                                  &out_frame->surface_internal);
        if (ret < 0)
            return NULL;

        out_frame->surface = &out_frame->surface_internal;
    }

    out_frame->surface->Info = s->vpp_param.vpp.Out;

    return out_frame;
}
Esempio n. 5
0
int arp_response(frame * arp)
{
	frame *output;
	uint32_t output_size = 0;
	frame  *m_frame = get_free_frame();

	create_arp_res(arp,output);
	TransmitPacket(output->f_data, output->f_len);
	release_frame(output);
	return 0;



}
void interrupt_memory(ExceptionStackFrame *frame){
  dprintf("in interrupt_memory", 0);
	int f;
	int i;
  struct pte *page_table;

	unsigned int addr = ((long)frame->addr);
	int addr_page = get_page_index(addr);
  page_table = pcb_current->page_table_p;

	if(addr_page > pcb_current->brk_index && addr_page<pcb_current->stack_limit_index){
		if(addr_page - pcb_current->brk_index <= 1){
			printf("Stack overflow, address 0x%061x \n", addr);
			Exit(ERROR);
		}
		if(pcb_current->stack_limit_index - addr_page > len_free_frames()){
			printf("Not enough physical memory. address 0x%061x \n", addr);
			Exit(ERROR);
		}
		int k = 0;
		for(i = 0; i<pcb_current->stack_limit_index - addr_page; i++){
		  f = get_free_frame();
          (page_table + pcb_current->stack_limit_index - i-1)->valid = PTE_VALID;
          (page_table + pcb_current->stack_limit_index - i-1)->pfn = f;
          (page_table + pcb_current->stack_limit_index - i-1)->uprot = (PROT_READ | PROT_WRITE);
          (page_table + pcb_current->stack_limit_index - i-1)->kprot = (PROT_READ | PROT_WRITE);
		  //Not sure about the actual index
		  WriteRegister(REG_TLB_FLUSH, TLB_FLUSH_0);
		  k++;
		}
		pcb_current->stack_limit_index = pcb_current->stack_limit_index - k - 1;
	}
	else{
		printf("Process %d: NOOBS ACCESSING INVALID MEMORY AT 0x%061x WITH CODE %d\n", pcb_current->pid, addr, frame->code);
		Exit(ERROR);
	}
}
Esempio n. 7
0
static int submit_frame(QSVEncContext *q, const AVFrame *frame,
                        mfxFrameSurface1 **surface)
{
    QSVFrame *qf;
    int ret;

    ret = get_free_frame(q, &qf);
    if (ret < 0)
        return ret;

    if (frame->format == AV_PIX_FMT_QSV) {
        ret = av_frame_ref(qf->frame, frame);
        if (ret < 0)
            return ret;

        qf->surface = (mfxFrameSurface1*)qf->frame->data[3];
        *surface = qf->surface;
        return 0;
    }

    /* make a copy if the input is not padded as libmfx requires */
    if (     frame->height & (q->height_align - 1) ||
        frame->linesize[0] & (q->width_align - 1)) {
        qf->frame->height = FFALIGN(frame->height, q->height_align);
        qf->frame->width  = FFALIGN(frame->width, q->width_align);

        ret = ff_get_buffer(q->avctx, qf->frame, AV_GET_BUFFER_FLAG_REF);
        if (ret < 0)
            return ret;

        qf->frame->height = frame->height;
        qf->frame->width  = frame->width;
        ret = av_frame_copy(qf->frame, frame);
        if (ret < 0) {
            av_frame_unref(qf->frame);
            return ret;
        }
    } else {
        ret = av_frame_ref(qf->frame, frame);
        if (ret < 0)
            return ret;
    }

    qf->surface_internal.Info = q->param.mfx.FrameInfo;

    qf->surface_internal.Info.PicStruct =
        !frame->interlaced_frame ? MFX_PICSTRUCT_PROGRESSIVE :
        frame->top_field_first   ? MFX_PICSTRUCT_FIELD_TFF :
                                   MFX_PICSTRUCT_FIELD_BFF;
    if (frame->repeat_pict == 1)
        qf->surface_internal.Info.PicStruct |= MFX_PICSTRUCT_FIELD_REPEATED;
    else if (frame->repeat_pict == 2)
        qf->surface_internal.Info.PicStruct |= MFX_PICSTRUCT_FRAME_DOUBLING;
    else if (frame->repeat_pict == 4)
        qf->surface_internal.Info.PicStruct |= MFX_PICSTRUCT_FRAME_TRIPLING;

    qf->surface_internal.Data.PitchLow  = qf->frame->linesize[0];
    qf->surface_internal.Data.Y         = qf->frame->data[0];
    qf->surface_internal.Data.UV        = qf->frame->data[1];
    qf->surface_internal.Data.TimeStamp = av_rescale_q(frame->pts, q->avctx->time_base, (AVRational){1, 90000});

    qf->surface = &qf->surface_internal;

    *surface = qf->surface;

    return 0;
}
Esempio n. 8
0
/* get the input surface */
static QSVFrame *submit_frame(QSVVPPContext *s, AVFilterLink *inlink, AVFrame *picref)
{
    QSVFrame        *qsv_frame;
    AVFilterContext *ctx = inlink->dst;

    clear_unused_frames(s->in_frame_list);

    qsv_frame = get_free_frame(&s->in_frame_list);
    if (!qsv_frame)
        return NULL;

    /* Turn AVFrame into mfxFrameSurface1.
     * For video/opaque memory mode, pix_fmt is AV_PIX_FMT_QSV, and
     * mfxFrameSurface1 is stored in AVFrame->data[3];
     * for system memory mode, raw video data is stored in
     * AVFrame, we should map it into mfxFrameSurface1.
     */
    if (!IS_SYSTEM_MEMORY(s->in_mem_mode)) {
        if (picref->format != AV_PIX_FMT_QSV) {
            av_log(ctx, AV_LOG_ERROR, "QSVVPP gets a wrong frame.\n");
            return NULL;
        }
        qsv_frame->frame   = av_frame_clone(picref);
        qsv_frame->surface = (mfxFrameSurface1 *)qsv_frame->frame->data[3];
    } else {
        /* make a copy if the input is not padded as libmfx requires */
        if (picref->height & 31 || picref->linesize[0] & 31) {
            qsv_frame->frame = ff_get_video_buffer(inlink,
                                                   FFALIGN(inlink->w, 32),
                                                   FFALIGN(inlink->h, 32));
            if (!qsv_frame->frame)
                return NULL;

            qsv_frame->frame->width   = picref->width;
            qsv_frame->frame->height  = picref->height;

            if (av_frame_copy(qsv_frame->frame, picref) < 0) {
                av_frame_free(&qsv_frame->frame);
                return NULL;
            }

            av_frame_copy_props(qsv_frame->frame, picref);
            av_frame_free(&picref);
        } else
            qsv_frame->frame = av_frame_clone(picref);

        if (map_frame_to_surface(qsv_frame->frame,
                                &qsv_frame->surface_internal) < 0) {
            av_log(ctx, AV_LOG_ERROR, "Unsupported frame.\n");
            return NULL;
        }
        qsv_frame->surface = &qsv_frame->surface_internal;
    }

    qsv_frame->surface->Info           = s->frame_infos[FF_INLINK_IDX(inlink)];
    qsv_frame->surface->Data.TimeStamp = av_rescale_q(qsv_frame->frame->pts,
                                                      inlink->time_base, default_tb);

    qsv_frame->surface->Info.PicStruct =
            !qsv_frame->frame->interlaced_frame ? MFX_PICSTRUCT_PROGRESSIVE :
            (qsv_frame->frame->top_field_first ? MFX_PICSTRUCT_FIELD_TFF :
                                                 MFX_PICSTRUCT_FIELD_BFF);
    if (qsv_frame->frame->repeat_pict == 1)
        qsv_frame->surface->Info.PicStruct |= MFX_PICSTRUCT_FIELD_REPEATED;
    else if (qsv_frame->frame->repeat_pict == 2)
        qsv_frame->surface->Info.PicStruct |= MFX_PICSTRUCT_FRAME_DOUBLING;
    else if (qsv_frame->frame->repeat_pict == 4)
        qsv_frame->surface->Info.PicStruct |= MFX_PICSTRUCT_FRAME_TRIPLING;

    return qsv_frame;
}
Esempio n. 9
0
/*******************************************************************************
 * Page fault handler. When the CPU encounters an invalid address mapping in a
 * process' page table, it invokes the CPU via this handler. The OS then
 * allocates a physical frame for the requested page (either by using a free
 * frame or evicting one), changes the process' page table to reflect the
 * mapping and then restarts the interrupted process.
 *
 * @param vpn The virtual page number requested from the current process.
 * @param write If the CPU is writing to the page, this is 1. Otherwise, it's 0.
 * @return The physical frame the OS has mapped to the virtual page.
 */
pfn_t pagefault_handler(vpn_t request_vpn, int write) {
  pfn_t victim_pfn;
  vpn_t victim_vpn;
  pcb_t *victim_pcb;

  /* Sanity Check */
  assert(current_pagetable != NULL);

  /* Find a free frame */
  victim_pfn = get_free_frame();
  assert(victim_pfn < CPU_NUM_FRAMES); /* make sure the victim_pfn is valid */

  /* Use the reverse lookup table to find the victim. */
  victim_vpn = rlt[victim_pfn].vpn;
  victim_pcb = rlt[victim_pfn].pcb;

  /*
   * FIX ME : Problem 4
   * If victim page is occupied - if it is not the pcb will be NULL:
   *
   * 1) If it's dirty, save it to disk with page_to_disk()
   * 2) Invalidate the page's entry in the victim's page table.
   * 3) Clear the victim page's TLB entry using the function tlb_clearone().
   */
	pte_t *victim_entry;
	if (victim_pcb != NULL) {
		victim_entry = victim_pcb->pagetable + victim_vpn;
		if (victim_entry->dirty == 1) {
			page_to_disk(victim_pfn, victim_vpn, victim_pcb->pid);
			victim_entry->dirty = 0;
		}
		victim_entry->valid = 0;
		//victim_entry->used = 0;
		tlb_clearone(victim_vpn);
	}
  printf("PAGE FAULT (VPN %u), evicting (PFN %u VPN %u)\n", request_vpn,
      victim_pfn, victim_vpn);


  /* FIX ME */
  /* Update the reverse lookup table to replace the victim entry info with this
   * process' info instead (pcb and vpn)
   * Update the current process' page table (pfn and valid)
   */
	rlt_t *rlt_entry = rlt + victim_pfn;
	rlt_entry->vpn = request_vpn;
	rlt_entry->pcb = current;

	pte_t *current_entry = current_pagetable + request_vpn;
	current_entry->valid = 1;
	current_entry->pfn = victim_pfn;
  /*
   * Retreive the page from disk. Note that is really a lie: we save pages in
   * memory (since doing file I/O for this simulation would be annoying and
   * wouldn't add that much to the learning). Also, if the page technically
   * does't exist yet (i.e., the page has never been accessed yet, we return a
   * blank page. Real systems would check for invalid pages and possibly read
   * stuff like code pages from disk. For purposes of this simulation, we won't
   * worry about that. =)
   */
  page_from_disk(victim_pfn, request_vpn, current->pid);

  return victim_pfn;
}
Esempio n. 10
0
/*------------------------------------------------------------------------
 *  sysinit  --  initialize all Xinu data structeres and devices
 *------------------------------------------------------------------------
 */
LOCAL
sysinit()
{
	static	long	currsp;
	int	i,j, avail;
	struct	pentry	*pptr;
	struct	sentry	*sptr;
	struct	mblock	*mptr;
	SYSCALL pfintr();

	/*********************/
	set_evec(14, pfintr);

	pptr = &proctab[NULLPROC]; /* initialize null process entry */
	/*********************/
	

	numproc = 0;			/* initialize system variables */
	nextproc = NPROC-1;
	nextsem = NSEM-1;
	nextqueue = NPROC;		/* q[0..NPROC-1] are processes */

	/* initialize free memory list */
	/* PC version has to pre-allocate 640K-1024K "hole" */
	if (maxaddr+1 > HOLESTART) {
		memlist.mnext = mptr = (struct mblock *) roundmb(&end);
		mptr->mnext = (struct mblock *)HOLEEND;
		mptr->mlen = (int) truncew(((unsigned) HOLESTART -
	     		 (unsigned)&end));
        mptr->mlen -= 4;

		mptr = (struct mblock *) HOLEEND;
		mptr->mnext = 0;
		mptr->mlen = (int) truncew((unsigned)maxaddr - HOLEEND -
	      		NULLSTK);
/*
		mptr->mlen = (int) truncew((unsigned)maxaddr - (4096 - 1024 ) *  4096 - HOLEEND - NULLSTK);
*/
	} else {
		/* initialize free memory list */
		memlist.mnext = mptr = (struct mblock *) roundmb(&end);
		mptr->mnext = 0;
		mptr->mlen = (int) truncew((unsigned)maxaddr - (int)&end -
			NULLSTK);
	}
	

	for (i=0 ; i<NPROC ; i++)	/* initialize process table */
		proctab[i].pstate = PRFREE;


#ifdef	MEMMARK
	_mkinit();			/* initialize memory marking */
#endif

#ifdef	RTCLOCK
	clkinit();			/* initialize r.t.clock	*/
#endif

	mon_init();     /* init monitor */

#ifdef NDEVS
	for (i=0 ; i<NDEVS ; i++ ) {	    
	    init_dev(i);
	}
#endif

	pptr = &proctab[NULLPROC];	/* initialize null process entry */
	pptr->pstate = PRCURR;
	for (j=0; j<7; j++)
		pptr->pname[j] = "prnull"[j];
	pptr->plimit = (WORD)(maxaddr + 1) - NULLSTK;
	pptr->pbase = (WORD) maxaddr - 3;
/*
	pptr->plimit = (WORD)(maxaddr + 1) - NULLSTK - (4096 - 1024 )*4096;
	pptr->pbase = (WORD) maxaddr - 3 - (4096-1024)*4096;
*/
	pptr->pesp = pptr->pbase-4;	/* for stkchk; rewritten before used */
	*( (int *)pptr->pbase ) = MAGIC;
	pptr->paddr = (WORD) nulluser;
	pptr->pargs = 0;
	pptr->pprio = 0;
	currpid = NULLPROC;
	
	
	for (i=0 ; i<NSEM ; i++) {	/* initialize semaphores */
		(sptr = &semaph[i])->sstate = SFREE;
		sptr->sqtail = 1 + (sptr->sqhead = newqueue());
	}

	rdytail = 1 + (rdyhead=newqueue());/* initialize ready list */

	init_bsm();
	init_frm();

	kprintf("initialize page tables for null process\n");
	init_glb_pgs(glb_pg_tbl_frm_mapping);
	// init pg dir for proc 0
	frame_t *pg_dir = get_free_frame();
	init_pg_dir(pg_dir, NULLPROC);
	pptr->pdbr = pg_dir->frm_num;
	pptr->pd = pg_dir;
	return(OK);
}
Esempio n. 11
0
/**
 * Reciving singlas from ethernet controller -
 * TX - transmited packet
 * RX - recived packet
 * LinkChange - Connected/Diconncted Ethernet Cable
 *
 */
void ethernet_interrupts_simple()
{
    frame *curr_frame = 0;
	int filtered;
	uint8_t interruption =ior(ISR);    //which interruption

    packet_num++;

    iow(IMR, 0);
 //   iow(IMR, PAR_set);
   // printf("ISR 0x%2X \n",interruption);

    if( interruption & 0x01 )      //RX
    {

    	iow(ISR,0x01);
    	curr_frame = get_free_frame();
    	if(curr_frame == 0)
    	{
    		return; //ERROR no more free frames
    	}

    	aaa=ReceivePacket(curr_frame->f_data,&curr_frame->f_len);
    	if(aaa == 0)
    	{
        	filtered =   filter_packiets(curr_frame->f_data,curr_frame->f_len);

        	if (filtered == 0)
        	{
        		printf(" RX:UDP \n");
        		add_rx_frame(curr_frame);

        	}
        	else if (filtered == 10) //arp
        	{
        		printf(" RX:ARP \n");
        		add_rx_frame(curr_frame);
        	}
        	else
        	{
    			release_frame(curr_frame);
    		}
        	curr_frame = 0;

    	}
    	else
    	{
    		printf("%s Recived Bad Frame \n",__FUNCTION__);

    		release_frame(curr_frame);
    		curr_frame = 0;
    	}

    	/* ethernet_header *tmp = (ethernet_header *) RXT;
    	 ether_addr adres =  { 0x01, 0x60, 0x6E, 0x11, 0x01, 0x1F  };


    	 tmp->eth_src_addr = adres;
         TransmitPacket(RXT,rx_len);*/
    }


    if(interruption & 0x02)    //TX
    {
    	iow(ISR,0x02);
    	tx_control = 0;

    }
    if(interruption & 0x20)    //LinkChange
    {
    	iow(ISR,0x20);

/*    	iow(0x00,0x01);  // software reset
    	usleep(10);
    	  enable interrupts to activate DM9000 ~on
    	  iow(IMR, INTR_set2);    IMR REG. FFH PAR=1 only, or + PTM=1& PRM=1 enable RxTx interrupts
    	  iow(0xFF, 0x80);
    	   enable RX (Broadcast/ ALL_MULTICAST) ~go
    	  iow(RCR , RCR_set | RX_ENABLE | PASS_MULTICAST);   RCR REG. 05 RXEN Bit [0] = 1 to enable the RX machine/ filter */

    	//printf(" LinkCHange \n");
    	if(link_control == 0)
    		link_control = 1;
    	else
    		link_control = 0;
    }




    iow(IMR, INTR_set2);
}
Esempio n. 12
0
/**
 * Returns page allocated for that virtual address.
 *
 * If allocate_new is specified to true, it will allocate substructures,
 * if not present.Otherwise returns NULL if page structures are not present
 * on the way to the virtual address. Returned pointer points to page in page
 * table.
 */
static puint_t* __get_page(uintptr_t vaddress, uintptr_t cr3, bool allocate_new, bool user) {

    v_address_t va;
    memcpy(&va, &vaddress, 8);

    puint_t* pml4 = (puint_t*)ALIGN(physical_to_virtual(cr3));
    if (!PRESENT(pml4[va.pml])) {
        if (!allocate_new) {
            return 0;
        }
        pdpt_t pdpt;
        memset(&pdpt, 0, sizeof(pdpt_t));
        pdpt.number = get_free_frame();
        if (pdpt.number == 0)
            return 0;
        pdpt.flaggable.present = 1;
        pdpt.flaggable.us = user;
        pdpt.flaggable.rw = 1;
        pml4[va.pml] = pdpt.number;
        memset((void*)physical_to_virtual(ALIGN(pdpt.number)), 0, 0x1000);
    }

    puint_t* pdpt = (puint_t*)ALIGN(physical_to_virtual(pml4[va.pml]));

    if (!PRESENT(pdpt[va.directory_ptr])) {
        if (!allocate_new)
            return 0;
        page_directory_t dir;
        memset(&dir, 0, sizeof(page_directory_t));
        dir.number = get_free_frame();
        if (dir.number == 0) {
            return 0;
        }
        dir.flaggable.present = 1;
        dir.flaggable.us = user;
        dir.flaggable.rw = 1;
        pdpt[va.directory_ptr] = dir.number;
        memset((void*)physical_to_virtual(ALIGN(dir.number)), 0, 0x1000);
    }

    puint_t* pdir = (puint_t*)ALIGN(physical_to_virtual(pdpt[va.directory_ptr]));

    if (!PRESENT(pdir[va.directory])) {
        if (!allocate_new)
            return 0;
        page_table_t pt;
        memset(&pt, 0, sizeof(page_table_t));
        pt.number = get_free_frame();
        if (pt.number == 0) {
            return 0;
        }
        pt.flaggable.present = 1;
        pt.flaggable.us = user;
        pt.flaggable.rw = 1;
        pdir[va.directory] = pt.number;
        memset((void*)physical_to_virtual(ALIGN(pt.number)), 0, 0x1000);
    }

    puint_t* pt = (puint_t*)ALIGN(physical_to_virtual(pdir[va.directory]));
    return &pt[va.table];
}
Esempio n. 13
0
/*******************************************************************************
 * Page fault handler. When the CPU encounters an invalid address mapping in a
 * process' page table, it invokes the CPU via this handler. The OS then
 * allocates a physical frame for the requested page (either by using a free
 * frame or evicting one), changes the process' page table to reflect the
 * mapping and then restarts the interrupted process.
 *
 * @param vpn The virtual page number requested.
 * @param write If the CPU is writing to the page, this is 1. Otherwise, it's 0.
 * @return The physical frame the OS has mapped to the virtual page.
 */
pfn_t pagefault_handler(vpn_t request_vpn, int write) {
  pfn_t victim_pfn;
  vpn_t victim_vpn;
  pcb_t *victim_pcb;

  /* Sanity Check */
  assert(current_pagetable != NULL);
  
  /* Find a free frame */
  victim_pfn = get_free_frame();
  assert(victim_pfn < CPU_NUM_FRAMES); /* make sure the victim_pfn is valid */

  /* Use the reverse lookup table to find the victim. */
  victim_vpn = rlt[victim_pfn].vpn;
  victim_pcb = rlt[victim_pfn].pcb;

  /* 
   * FIX ME : Problem 4
   * If victim page is occupied:
   *
   * 1) If it's dirty, save it to disk with page_to_disk()
   * 2) Invalidate the page's entry in the victim's page table.
   * 3) Clear the victim page's TLB entry (hint: tlb_clearone()).
   */
  pte_t* victim;
  pte_t current_v;
  if (victim_pcb != NULL) {
    victim = victim_pcb -> pagetable;
    current_v = victim[victim_vpn];
    if (current_v.dirty && current_v.valid) {
      page_to_disk(victim_pfn, victim_vpn, victim_pcb ->pid);
    }
    victim[victim_vpn].valid = 0;
    tlb_clearone(victim_vpn);
  }


  printf("****PAGE FAULT has occurred at VPN %u. Evicting (PFN %u VPN %u) as victim.\n", request_vpn,
      victim_pfn, victim_vpn);

  /* Update the reverse lookup table so that 
     it knows about the requesting process  */
  /* FIX ME */
  rlt[victim_pfn].pcb = current;
  
  rlt[victim_pfn].vpn = request_vpn;

  /* Update the requesting process' page table */
  /* FIX ME */
  current_pagetable[request_vpn].pfn = victim_pfn;
  current_pagetable[request_vpn].valid = 1;
  current_pagetable[request_vpn].dirty = write;
  current_pagetable[request_vpn].used = 1;

  /*
   * Retreive the page from disk. Note that is really a lie: we save pages in
   * memory (since doing file I/O for this simulation would be annoying and
   * wouldn't add that much to the learning). Also, if the page technically
   * does't exist yet (i.e., the page has never been accessed yet, we return a
   * blank page. Real systems would check for invalid pages and possibly read
   * stuff like code pages from disk. For purposes of this simulation, we won't
   * worry about that. =)
   */
  page_from_disk(victim_pfn, request_vpn, current->pid);

  return victim_pfn;
}