Initial version
This commit is contained in:
commit
6f405265a5
102 changed files with 14486 additions and 0 deletions
24
code/userprog/Makefile
Normal file
24
code/userprog/Makefile
Normal file
|
@ -0,0 +1,24 @@
|
|||
# NOTE: this is a GNU Makefile. You must use "gmake" rather than "make".
|
||||
#
|
||||
# Makefile for the multiprogramming assignment
|
||||
# Defines set up assuming multiprogramming is done before the file system.
|
||||
# If not, use the "filesys first" defines below.
|
||||
#
|
||||
#
|
||||
# Copyright (c) 1992 The Regents of the University of California.
|
||||
# All rights reserved. See copyright.h for copyright notice and limitation
|
||||
# of liability and disclaimer of warranty provisions.
|
||||
|
||||
DEFINES = -DUSER_PROGRAM -DFILESYS_NEEDED -DFILESYS_STUB
|
||||
INCPATH = -I../bin -I../filesys -I../userprog -I../threads -I../machine
|
||||
C_OFILES = $(THREAD_O) $(USERPROG_O)
|
||||
|
||||
# if file system done first!
|
||||
# DEFINES = -DUSER_PROGRAM -DFILESYS_NEEDED -DFILESYS
|
||||
# INCPATH = -I../bin -I../filesys -I../userprog -I../threads -I../machine
|
||||
# C_OFILES = $(THREAD_O) $(USERPROG_O) $(FILESYS_O)
|
||||
|
||||
include ../Makefile.common
|
||||
include ../Makefile.dep
|
||||
|
||||
|
294
code/userprog/addrspace.cc
Normal file
294
code/userprog/addrspace.cc
Normal file
|
@ -0,0 +1,294 @@
|
|||
// addrspace.cc
|
||||
// Routines to manage address spaces (executing user programs).
|
||||
//
|
||||
// In order to run a user program, you must:
|
||||
//
|
||||
// 1. link with the -N -T 0 option
|
||||
// 2. run coff2noff to convert the object file to Nachos format
|
||||
// (Nachos object code format is essentially just a simpler
|
||||
// version of the UNIX executable object code format)
|
||||
// 3. load the NOFF file into the Nachos file system
|
||||
// (if you haven't implemented the file system yet, you
|
||||
// don't need to do this last step)
|
||||
//
|
||||
// Copyright (c) 1992-1993 The Regents of the University of California.
|
||||
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||
// of liability and disclaimer of warranty provisions.
|
||||
|
||||
#include "copyright.h"
|
||||
#include "system.h"
|
||||
#include "addrspace.h"
|
||||
#include "noff.h"
|
||||
#include "syscall.h"
|
||||
#include "new"
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// SwapHeader
|
||||
// Do little endian to big endian conversion on the bytes in the
|
||||
// object file header, in case the file was generated on a little
|
||||
// endian machine, and we're now running on a big endian machine.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
static void
|
||||
SwapHeader (NoffHeader * noffH)
|
||||
{
|
||||
noffH->noffMagic = WordToHost (noffH->noffMagic);
|
||||
noffH->code.size = WordToHost (noffH->code.size);
|
||||
noffH->code.virtualAddr = WordToHost (noffH->code.virtualAddr);
|
||||
noffH->code.inFileAddr = WordToHost (noffH->code.inFileAddr);
|
||||
noffH->initData.size = WordToHost (noffH->initData.size);
|
||||
noffH->initData.virtualAddr = WordToHost (noffH->initData.virtualAddr);
|
||||
noffH->initData.inFileAddr = WordToHost (noffH->initData.inFileAddr);
|
||||
noffH->uninitData.size = WordToHost (noffH->uninitData.size);
|
||||
noffH->uninitData.virtualAddr =
|
||||
WordToHost (noffH->uninitData.virtualAddr);
|
||||
noffH->uninitData.inFileAddr = WordToHost (noffH->uninitData.inFileAddr);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// AddrSpaceList
|
||||
// List of all address spaces, for debugging
|
||||
//----------------------------------------------------------------------
|
||||
List AddrSpaceList;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// AddrSpace::AddrSpace
|
||||
// Create an address space to run a user program.
|
||||
// Load the program from a file "executable", and set everything
|
||||
// up so that we can start executing user instructions.
|
||||
//
|
||||
// Assumes that the object code file is in NOFF format.
|
||||
//
|
||||
// First, set up the translation from program memory to physical
|
||||
// memory. For now, this is really simple (1:1), since we are
|
||||
// only uniprogramming, and we have a single unsegmented page table
|
||||
//
|
||||
// "executable" is the file containing the object code to load into memory
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
AddrSpace::AddrSpace (OpenFile * executable)
|
||||
{
|
||||
unsigned int i, size;
|
||||
|
||||
executable->ReadAt (&noffH, sizeof (noffH), 0);
|
||||
if ((noffH.noffMagic != NOFFMAGIC) &&
|
||||
(WordToHost (noffH.noffMagic) == NOFFMAGIC))
|
||||
SwapHeader (&noffH);
|
||||
/* Check that this is really a MIPS program */
|
||||
ASSERT (noffH.noffMagic == NOFFMAGIC);
|
||||
|
||||
// how big is address space?
|
||||
size = noffH.code.size + noffH.initData.size + noffH.uninitData.size + UserStacksAreaSize; // we need to increase the size
|
||||
// to leave room for the stack
|
||||
numPages = divRoundUp (size, PageSize);
|
||||
size = numPages * PageSize;
|
||||
|
||||
// check we're not trying
|
||||
// to run anything too big --
|
||||
// at least until we have
|
||||
// virtual memory
|
||||
if (numPages > NumPhysPages)
|
||||
throw std::bad_alloc();
|
||||
|
||||
DEBUG ('a', "Initializing address space, num pages %d, total size 0x%x\n",
|
||||
numPages, size);
|
||||
// first, set up the translation
|
||||
pageTable = new TranslationEntry[numPages];
|
||||
for (i = 0; i < numPages; i++)
|
||||
{
|
||||
pageTable[i].physicalPage = i; // for now, phys page # = virtual page #
|
||||
pageTable[i].valid = TRUE;
|
||||
pageTable[i].use = FALSE;
|
||||
pageTable[i].dirty = FALSE;
|
||||
pageTable[i].readOnly = FALSE; // if the code segment was entirely on
|
||||
// a separate page, we could set its
|
||||
// pages to be read-only
|
||||
}
|
||||
|
||||
// then, copy in the code and data segments into memory
|
||||
if (noffH.code.size > 0)
|
||||
{
|
||||
DEBUG ('a', "Initializing code segment, at 0x%x, size 0x%x\n",
|
||||
noffH.code.virtualAddr, noffH.code.size);
|
||||
executable->ReadAt (&(machine->mainMemory[noffH.code.virtualAddr]),
|
||||
noffH.code.size, noffH.code.inFileAddr);
|
||||
}
|
||||
if (noffH.initData.size > 0)
|
||||
{
|
||||
DEBUG ('a', "Initializing data segment, at 0x%x, size 0x%x\n",
|
||||
noffH.initData.virtualAddr, noffH.initData.size);
|
||||
executable->ReadAt (&
|
||||
(machine->mainMemory
|
||||
[noffH.initData.virtualAddr]),
|
||||
noffH.initData.size, noffH.initData.inFileAddr);
|
||||
}
|
||||
|
||||
DEBUG ('a', "Area for stacks at 0x%x, size 0x%x\n",
|
||||
size - UserStacksAreaSize, UserStacksAreaSize);
|
||||
|
||||
pageTable[0].valid = FALSE; // Catch NULL dereference
|
||||
|
||||
AddrSpaceList.Append(this);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// AddrSpace::~AddrSpace
|
||||
// Dealloate an address space. Nothing for now!
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
AddrSpace::~AddrSpace ()
|
||||
{
|
||||
delete [] pageTable;
|
||||
pageTable = NULL;
|
||||
|
||||
AddrSpaceList.Remove(this);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// AddrSpace::InitRegisters
|
||||
// Set the initial values for the user-level register set.
|
||||
//
|
||||
// We write these directly into the "machine" registers, so
|
||||
// that we can immediately jump to user code. Note that these
|
||||
// will be saved/restored into the currentThread->userRegisters
|
||||
// when this thread is context switched out.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
AddrSpace::InitRegisters ()
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NumTotalRegs; i++)
|
||||
machine->WriteRegister (i, 0);
|
||||
|
||||
// Initial program counter -- must be location of "Start"
|
||||
machine->WriteRegister (PCReg, USER_START_ADDRESS);
|
||||
|
||||
// Need to also tell MIPS where next instruction is, because
|
||||
// of branch delay possibility
|
||||
machine->WriteRegister (NextPCReg, machine->ReadRegister(PCReg) + 4);
|
||||
|
||||
// Set the stack register to the end of the address space, where we
|
||||
// allocated the stack; but subtract off a bit, to make sure we don't
|
||||
// accidentally reference off the end!
|
||||
machine->WriteRegister (StackReg, numPages * PageSize - 16);
|
||||
DEBUG ('a', "Initializing stack register to 0x%x\n",
|
||||
numPages * PageSize - 16);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// AddrSpace::Dump
|
||||
// Dump program layout as SVG
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
static void
|
||||
DrawArea(FILE *output, unsigned sections_x, unsigned virtual_x,
|
||||
unsigned y, unsigned blocksize,
|
||||
struct segment *segment, const char *name)
|
||||
{
|
||||
if (segment->size == 0)
|
||||
return;
|
||||
|
||||
ASSERT((segment->virtualAddr % PageSize == 0));
|
||||
ASSERT((segment->size % PageSize == 0));
|
||||
unsigned page = segment->virtualAddr / PageSize;
|
||||
unsigned end = (segment->virtualAddr + segment->size) / PageSize;
|
||||
|
||||
fprintf(output, "<rect x=\"%u\" y=\"%u\" width=\"%u\" height=\"%u\" "
|
||||
"fill=\"#ffffff\" "
|
||||
"stroke=\"#000000\" stroke-width=\"1\"/>\n",
|
||||
sections_x, y - end * blocksize,
|
||||
virtual_x - sections_x, (end - page) * blocksize);
|
||||
|
||||
fprintf(output, "<text x=\"%u\" y=\"%u\" fill=\"#000000\" font-size=\"%u\">%s</text>\n",
|
||||
sections_x, y - page * blocksize, blocksize, name);
|
||||
}
|
||||
|
||||
unsigned
|
||||
AddrSpace::Dump(FILE *output, unsigned addr_x, unsigned sections_x, unsigned virtual_x, unsigned virtual_width,
|
||||
unsigned physical_x, unsigned virtual_y, unsigned y,
|
||||
unsigned blocksize)
|
||||
{
|
||||
unsigned ret = machine->DumpPageTable(output, pageTable, numPages,
|
||||
addr_x, virtual_x, virtual_width, physical_x, virtual_y, y, blocksize);
|
||||
|
||||
DrawArea(output, sections_x, virtual_x, virtual_y, blocksize, &noffH.code, "code");
|
||||
DrawArea(output, sections_x, virtual_x, virtual_y, blocksize, &noffH.initData, "data");
|
||||
DrawArea(output, sections_x, virtual_x, virtual_y, blocksize, &noffH.uninitData, "bss");
|
||||
|
||||
DumpThreadsState(output, this, sections_x, virtual_x, virtual_y, blocksize);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// AddrSpace::AddrSpacesRoom
|
||||
// Return how much room is needed for showing address spaces
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
unsigned
|
||||
AddrSpacesRoom(unsigned blocksize)
|
||||
{
|
||||
ListElement *element;
|
||||
unsigned room = 0;
|
||||
|
||||
for (element = AddrSpaceList.FirstElement ();
|
||||
element;
|
||||
element = element->next) {
|
||||
AddrSpace *space = (AddrSpace*) element->item;
|
||||
room += machine->PageTableRoom(space->NumPages(), blocksize);
|
||||
}
|
||||
|
||||
return room;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// AddrSpace::DumpAddrSpaces
|
||||
// Dump all address spaces
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
DumpAddrSpaces(FILE *output,
|
||||
unsigned addr_x, unsigned sections_x, unsigned virtual_x, unsigned virtual_width,
|
||||
unsigned physical_x, unsigned y, unsigned blocksize)
|
||||
{
|
||||
ListElement *element;
|
||||
unsigned virtual_y = y;
|
||||
|
||||
/* TODO: sort by physical page addresses to avoid too much mess */
|
||||
for (element = AddrSpaceList.FirstElement ();
|
||||
element;
|
||||
element = element->next) {
|
||||
AddrSpace *space = (AddrSpace*) element->item;
|
||||
virtual_y -= space->Dump(output, addr_x, sections_x, virtual_x, virtual_width, physical_x, virtual_y, y, blocksize);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// AddrSpace::SaveState
|
||||
// On a context switch, save any machine state, specific
|
||||
// to this address space, that needs saving.
|
||||
//
|
||||
// For now, nothing!
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
AddrSpace::SaveState ()
|
||||
{
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// AddrSpace::RestoreState
|
||||
// On a context switch, restore the machine state so that
|
||||
// this address space can run.
|
||||
//
|
||||
// For now, tell the machine where to find the page table.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
AddrSpace::RestoreState ()
|
||||
{
|
||||
machine->currentPageTable = pageTable;
|
||||
machine->currentPageTableSize = numPages;
|
||||
}
|
53
code/userprog/addrspace.h
Normal file
53
code/userprog/addrspace.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
// addrspace.h
|
||||
// Data structures to keep track of executing user programs
|
||||
// (address spaces).
|
||||
//
|
||||
// For now, we don't keep any information about address spaces.
|
||||
// The user level CPU state is saved and restored in the thread
|
||||
// executing the user program (see thread.h).
|
||||
//
|
||||
// Copyright (c) 1992-1993 The Regents of the University of California.
|
||||
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||
// of liability and disclaimer of warranty provisions.
|
||||
|
||||
#ifndef ADDRSPACE_H
|
||||
#define ADDRSPACE_H
|
||||
|
||||
#include "copyright.h"
|
||||
#include "filesys.h"
|
||||
#include "translate.h"
|
||||
#include "noff.h"
|
||||
#include "list.h"
|
||||
|
||||
#define UserStacksAreaSize 1024 // increase this as necessary!
|
||||
|
||||
class AddrSpace:public dontcopythis
|
||||
{
|
||||
public:
|
||||
AddrSpace (OpenFile * executable); // Create an address space,
|
||||
// initializing it with the program
|
||||
// stored in the file "executable"
|
||||
~AddrSpace (); // De-allocate an address space
|
||||
|
||||
void InitRegisters (); // Initialize user-level CPU registers,
|
||||
// before jumping to user code
|
||||
|
||||
void SaveState (); // Save/restore address space-specific
|
||||
void RestoreState (); // info on a context switch
|
||||
|
||||
unsigned Dump(FILE *output, unsigned addr_s, unsigned sections_x, unsigned virtual_x, unsigned virtual_width,
|
||||
unsigned physical_x, unsigned virtual_y, unsigned y,
|
||||
unsigned blocksize);
|
||||
// Dump program layout as SVG
|
||||
unsigned NumPages() { return numPages; }
|
||||
|
||||
private:
|
||||
NoffHeader noffH; // Program layout
|
||||
|
||||
TranslationEntry * pageTable; // Page table
|
||||
unsigned int numPages; // Number of pages in the page table
|
||||
};
|
||||
|
||||
extern List AddrspaceList;
|
||||
|
||||
#endif // ADDRSPACE_H
|
172
code/userprog/bitmap.cc
Normal file
172
code/userprog/bitmap.cc
Normal file
|
@ -0,0 +1,172 @@
|
|||
// bitmap.c
|
||||
// Routines to manage a bitmap -- an array of bits each of which
|
||||
// can be either on or off. Represented as an array of integers.
|
||||
//
|
||||
// Copyright (c) 1992-1993 The Regents of the University of California.
|
||||
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||
// of liability and disclaimer of warranty provisions.
|
||||
|
||||
#include "copyright.h"
|
||||
#include "bitmap.h"
|
||||
|
||||
// Definitions helpful for representing a bitmap as an array of integers
|
||||
#define BitsInByte 8
|
||||
#define BitsInWord 32
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// BitMap::BitMap
|
||||
// Initialize a bitmap with "nitems" bits, so that every bit is clear.
|
||||
// it can be added somewhere on a list.
|
||||
//
|
||||
// "nitems" is the number of bits in the bitmap.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
BitMap::BitMap (int nitems)
|
||||
{
|
||||
numBits = nitems;
|
||||
numWords = divRoundUp (numBits, BitsInWord);
|
||||
map = new unsigned int[numWords];
|
||||
for (int i = 0; i < numBits; i++)
|
||||
Clear (i);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// BitMap::~BitMap
|
||||
// De-allocate a bitmap.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
BitMap::~BitMap ()
|
||||
{
|
||||
delete [] map;
|
||||
map = NULL;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// BitMap::Mark
|
||||
// Set the "nth" bit in a bitmap.
|
||||
//
|
||||
// "which" is the number of the bit to be set.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
BitMap::Mark (int which)
|
||||
{
|
||||
ASSERT (which >= 0 && which < numBits);
|
||||
map[which / BitsInWord] |= 1 << (which % BitsInWord);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// BitMap::Clear
|
||||
// Clear the "nth" bit in a bitmap.
|
||||
//
|
||||
// "which" is the number of the bit to be cleared.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
BitMap::Clear (int which)
|
||||
{
|
||||
ASSERT (which >= 0 && which < numBits);
|
||||
map[which / BitsInWord] &= ~(1 << (which % BitsInWord));
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// BitMap::Test
|
||||
// Return TRUE if the "nth" bit is set.
|
||||
//
|
||||
// "which" is the number of the bit to be tested.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
bool
|
||||
BitMap::Test (int which)
|
||||
{
|
||||
ASSERT (which >= 0 && which < numBits);
|
||||
|
||||
if (map[which / BitsInWord] & (1 << (which % BitsInWord)))
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// BitMap::Find
|
||||
// Return the number of the first bit which is clear.
|
||||
// As a side effect, set the bit (mark it as in use).
|
||||
// (In other words, find and allocate a bit.)
|
||||
//
|
||||
// If no bits are clear, return -1.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
int
|
||||
BitMap::Find ()
|
||||
{
|
||||
for (int i = 0; i < numBits; i++)
|
||||
if (!Test (i))
|
||||
{
|
||||
Mark (i);
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// BitMap::NumClear
|
||||
// Return the number of clear bits in the bitmap.
|
||||
// (In other words, how many bits are unallocated?)
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
int
|
||||
BitMap::NumClear ()
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for (int i = 0; i < numBits; i++)
|
||||
if (!Test (i))
|
||||
count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// BitMap::Print
|
||||
// Print the contents of the bitmap, for debugging.
|
||||
//
|
||||
// Could be done in a number of ways, but we just print the #'s of
|
||||
// all the bits that are set in the bitmap.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
BitMap::Print ()
|
||||
{
|
||||
printf ("Bitmap set:\n");
|
||||
for (int i = 0; i < numBits; i++)
|
||||
if (Test (i))
|
||||
printf ("%d, ", i);
|
||||
printf ("\n");
|
||||
}
|
||||
|
||||
// These aren't needed until the FILESYS assignment
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// BitMap::FetchFromFile
|
||||
// Initialize the contents of a bitmap from a Nachos file.
|
||||
//
|
||||
// "file" is the place to read the bitmap from
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
BitMap::FetchFrom (OpenFile * file)
|
||||
{
|
||||
file->ReadAt (map, numWords * sizeof (unsigned), 0);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// BitMap::WriteBack
|
||||
// Store the contents of a bitmap to a Nachos file.
|
||||
//
|
||||
// "file" is the place to write the bitmap to
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
BitMap::WriteBack (OpenFile * file)
|
||||
{
|
||||
file->WriteAt (map, numWords * sizeof (unsigned), 0);
|
||||
}
|
61
code/userprog/bitmap.h
Normal file
61
code/userprog/bitmap.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
// bitmap.h
|
||||
// Data structures defining a bitmap -- an array of bits each of which
|
||||
// can be either on or off.
|
||||
//
|
||||
// Represented as an array of unsigned integers, on which we do
|
||||
// modulo arithmetic to find the bit we are interested in.
|
||||
//
|
||||
// The bitmap can be parameterized with with the number of bits being
|
||||
// managed.
|
||||
//
|
||||
// Copyright (c) 1992-1993 The Regents of the University of California.
|
||||
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||
// of liability and disclaimer of warranty provisions.
|
||||
|
||||
#ifndef BITMAP_H
|
||||
#define BITMAP_H
|
||||
|
||||
#include "copyright.h"
|
||||
#include "utility.h"
|
||||
#include "openfile.h"
|
||||
|
||||
// The following class defines a "bitmap" -- an array of bits,
|
||||
// each of which can be independently set, cleared, and tested.
|
||||
//
|
||||
// Most useful for managing the allocation of the elements of an array --
|
||||
// for instance, disk sectors, or main memory pages.
|
||||
// Each bit represents whether the corresponding sector or page is
|
||||
// in use or free.
|
||||
|
||||
class BitMap:public dontcopythis
|
||||
{
|
||||
public:
|
||||
BitMap (int nitems); // Initialize a bitmap, with "nitems" bits
|
||||
// initially, all bits are cleared.
|
||||
~BitMap (); // De-allocate bitmap
|
||||
|
||||
void Mark (int which); // Set the "nth" bit
|
||||
void Clear (int which); // Clear the "nth" bit
|
||||
bool Test (int which); // Is the "nth" bit set?
|
||||
int Find (); // Return the # of a clear bit, and as a side
|
||||
// effect, set the bit.
|
||||
// If no bits are clear, return -1.
|
||||
int NumClear (); // Return the number of clear bits
|
||||
|
||||
void Print (); // Print contents of bitmap
|
||||
|
||||
// These aren't needed until FILESYS, when we will need to read and
|
||||
// write the bitmap to a file
|
||||
void FetchFrom (OpenFile * file); // fetch contents from disk
|
||||
void WriteBack (OpenFile * file); // write contents to disk
|
||||
|
||||
private:
|
||||
int numBits; // number of bits in the bitmap
|
||||
int numWords; // number of words of bitmap storage
|
||||
// (rounded up if numBits is not a
|
||||
// multiple of the number of bits in
|
||||
// a word)
|
||||
unsigned int *map; // bit storage
|
||||
};
|
||||
|
||||
#endif // BITMAP_H
|
137
code/userprog/exception.cc
Normal file
137
code/userprog/exception.cc
Normal file
|
@ -0,0 +1,137 @@
|
|||
// exception.cc
|
||||
// Entry point into the Nachos kernel from user programs.
|
||||
// There are two kinds of things that can cause control to
|
||||
// transfer back to here from user code:
|
||||
//
|
||||
// syscall -- The user code explicitly requests to call a procedure
|
||||
// in the Nachos kernel. Right now, the only function we support is
|
||||
// "Halt".
|
||||
//
|
||||
// exceptions -- The user code does something that the CPU can't handle.
|
||||
// For instance, accessing memory that doesn't exist, arithmetic errors,
|
||||
// etc.
|
||||
//
|
||||
// Interrupts (which can also cause control to transfer from user
|
||||
// code into the Nachos kernel) are handled elsewhere.
|
||||
//
|
||||
// For now, this only handles the Halt() system call.
|
||||
// Everything else core dumps.
|
||||
//
|
||||
// Copyright (c) 1992-1993 The Regents of the University of California.
|
||||
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||
// of liability and disclaimer of warranty provisions.
|
||||
|
||||
#include "copyright.h"
|
||||
#include "system.h"
|
||||
#include "syscall.h"
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// UpdatePC : Increments the Program Counter register in order to resume
|
||||
// the user program immediately after the "syscall" instruction.
|
||||
//----------------------------------------------------------------------
|
||||
static void
|
||||
UpdatePC ()
|
||||
{
|
||||
int pc = machine->ReadRegister (PCReg);
|
||||
machine->WriteRegister (PrevPCReg, pc);
|
||||
pc = machine->ReadRegister (NextPCReg);
|
||||
machine->WriteRegister (PCReg, pc);
|
||||
pc += 4;
|
||||
machine->WriteRegister (NextPCReg, pc);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// ExceptionHandler
|
||||
// Entry point into the Nachos kernel. Called when a user program
|
||||
// is executing, and either does a syscall, or generates an addressing
|
||||
// or arithmetic exception.
|
||||
//
|
||||
// For system calls, the following is the calling convention:
|
||||
//
|
||||
// system call code -- r2
|
||||
// arg1 -- r4
|
||||
// arg2 -- r5
|
||||
// arg3 -- r6
|
||||
// arg4 -- r7
|
||||
//
|
||||
// The result of the system call, if any, must be put back into r2.
|
||||
//
|
||||
// And don't forget to increment the pc before returning. (Or else you'll
|
||||
// loop making the same system call forever!
|
||||
//
|
||||
// "which" is the kind of exception. The list of possible exceptions
|
||||
// are in machine.h.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
ExceptionHandler (ExceptionType which)
|
||||
{
|
||||
int type = machine->ReadRegister (2);
|
||||
int address = machine->registers[BadVAddrReg];
|
||||
|
||||
switch (which)
|
||||
{
|
||||
case SyscallException:
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case SC_Halt:
|
||||
{
|
||||
DEBUG ('s', "Shutdown, initiated by user program.\n");
|
||||
interrupt->Powerdown ();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
printf("Unimplemented system call %d\n", type);
|
||||
ASSERT(FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
// Do not forget to increment the pc before returning!
|
||||
UpdatePC ();
|
||||
break;
|
||||
}
|
||||
|
||||
case PageFaultException:
|
||||
if (!address) {
|
||||
printf("NULL dereference at PC %x!\n", machine->registers[PCReg]);
|
||||
ASSERT (FALSE);
|
||||
} else {
|
||||
printf ("Page Fault at address %x at PC %x\n", address, machine->registers[PCReg]);
|
||||
ASSERT (FALSE); // For now
|
||||
}
|
||||
break;
|
||||
|
||||
case ReadOnlyException:
|
||||
printf ("Read-Only at address %x at PC %x\n", address, machine->registers[PCReg]);
|
||||
ASSERT (FALSE); // For now
|
||||
break;
|
||||
|
||||
case BusErrorException:
|
||||
printf ("Invalid physical address at address %x at PC %x\n", address, machine->registers[PCReg]);
|
||||
ASSERT (FALSE); // For now
|
||||
break;
|
||||
|
||||
case AddressErrorException:
|
||||
printf ("Invalid address %x at PC %x\n", address, machine->registers[PCReg]);
|
||||
ASSERT (FALSE); // For now
|
||||
break;
|
||||
|
||||
case OverflowException:
|
||||
printf ("Overflow at PC %x\n", machine->registers[PCReg]);
|
||||
ASSERT (FALSE); // For now
|
||||
break;
|
||||
|
||||
case IllegalInstrException:
|
||||
printf ("Illegal instruction at PC %x\n", machine->registers[PCReg]);
|
||||
ASSERT (FALSE); // For now
|
||||
break;
|
||||
|
||||
default:
|
||||
printf ("Unexpected user mode exception %d %d %x at PC %x\n", which, type, address, machine->registers[PCReg]);
|
||||
ASSERT (FALSE);
|
||||
break;
|
||||
}
|
||||
}
|
106
code/userprog/progtest.cc
Normal file
106
code/userprog/progtest.cc
Normal file
|
@ -0,0 +1,106 @@
|
|||
// progtest.cc
|
||||
// Test routines for demonstrating that Nachos can load
|
||||
// a user program and execute it.
|
||||
//
|
||||
// Also, routines for testing the Console hardware device.
|
||||
//
|
||||
// Copyright (c) 1992-1993 The Regents of the University of California.
|
||||
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||
// of liability and disclaimer of warranty provisions.
|
||||
|
||||
#include "copyright.h"
|
||||
#include "system.h"
|
||||
#include "console.h"
|
||||
#include "addrspace.h"
|
||||
#include "synch.h"
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// StartProcess
|
||||
// Run a user program. Open the executable, load it into
|
||||
// memory, and jump to it.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
StartProcess (char *filename)
|
||||
{
|
||||
OpenFile *executable = fileSystem->Open (filename);
|
||||
AddrSpace *space;
|
||||
|
||||
if (executable == NULL)
|
||||
{
|
||||
SetColor (stdout, ColorRed);
|
||||
SetBold (stdout);
|
||||
printf ("Unable to open file %s\n", filename);
|
||||
ClearColor (stdout);
|
||||
return;
|
||||
}
|
||||
space = new AddrSpace (executable);
|
||||
currentThread->space = space;
|
||||
|
||||
delete executable; // close file
|
||||
|
||||
space->InitRegisters (); // set the initial register values
|
||||
space->RestoreState (); // load page table register
|
||||
|
||||
machine->DumpMem ("memory.svg");
|
||||
machine->Run (); // jump to the user progam
|
||||
ASSERT (FALSE); // machine->Run never returns;
|
||||
// the address space exits
|
||||
// by doing the syscall "exit"
|
||||
}
|
||||
|
||||
// Data structures needed for the console test. Threads making
|
||||
// I/O requests wait on a Semaphore to delay until the I/O completes.
|
||||
|
||||
static Console *console;
|
||||
static Semaphore *readAvail;
|
||||
static Semaphore *writeDone;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// ConsoleInterruptHandlers
|
||||
// Wake up the thread that requested the I/O.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
static void
|
||||
ReadAvailHandler (void *arg)
|
||||
{
|
||||
(void) arg;
|
||||
readAvail->V ();
|
||||
}
|
||||
static void
|
||||
WriteDoneHandler (void *arg)
|
||||
{
|
||||
(void) arg;
|
||||
writeDone->V ();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// ConsoleTest
|
||||
// Test the console by echoing characters typed at the input onto
|
||||
// the output. Stop when the user types a 'q'.
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
void
|
||||
ConsoleTest (const char *in, const char *out)
|
||||
{
|
||||
char ch;
|
||||
|
||||
readAvail = new Semaphore ("read avail", 0);
|
||||
writeDone = new Semaphore ("write done", 0);
|
||||
console = new Console (in, out, ReadAvailHandler, WriteDoneHandler, NULL);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
readAvail->P (); // wait for character to arrive
|
||||
ch = console->RX ();
|
||||
console->TX (ch); // echo it!
|
||||
writeDone->P (); // wait for write to finish
|
||||
if (ch == 'q') {
|
||||
printf ("Nothing more, bye!\n");
|
||||
break; // if q, quit
|
||||
}
|
||||
}
|
||||
delete console;
|
||||
delete readAvail;
|
||||
delete writeDone;
|
||||
}
|
18
code/userprog/progtest.h
Normal file
18
code/userprog/progtest.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
// progtest.h
|
||||
// Declarations for test routines for demonstrating that Nachos can load a
|
||||
// user program and execute it.
|
||||
//
|
||||
// Also, routines for testing the Console hardware device.
|
||||
//
|
||||
// Copyright (c) 1992-1993 The Regents of the University of California.
|
||||
// All rights reserved. See copyright.h for copyright notice and limitation
|
||||
// of liability and disclaimer of warranty provisions.
|
||||
|
||||
#ifndef PROGTEST_H
|
||||
#define PROGTEST_H
|
||||
|
||||
extern void StartProcess (char *filename);
|
||||
|
||||
extern void ConsoleTest (const char *in, const char *out);
|
||||
|
||||
#endif // PROGTEST_H
|
135
code/userprog/syscall.h
Normal file
135
code/userprog/syscall.h
Normal file
|
@ -0,0 +1,135 @@
|
|||
/* syscalls.h
|
||||
* Nachos system call interface. These are Nachos kernel operations
|
||||
* that can be invoked from user programs, by trapping to the kernel
|
||||
* via the "syscall" instruction.
|
||||
*
|
||||
* This file is included by user programs and by the Nachos kernel.
|
||||
*
|
||||
* Copyright (c) 1992-1993 The Regents of the University of California.
|
||||
* All rights reserved. See copyright.h for copyright notice and limitation
|
||||
* of liability and disclaimer of warranty provisions.
|
||||
*/
|
||||
|
||||
#ifndef SYSCALLS_H
|
||||
#define SYSCALLS_H
|
||||
|
||||
#include "copyright.h"
|
||||
|
||||
#define USER_START_ADDRESS 0x80
|
||||
|
||||
/* system call codes -- used by the stubs to tell the kernel which system call
|
||||
* is being asked for
|
||||
*/
|
||||
#define SC_Halt 0
|
||||
#define SC_Exit 1
|
||||
#define SC_Exec 2
|
||||
#define SC_Join 3
|
||||
#define SC_Create 4
|
||||
#define SC_Open 5
|
||||
#define SC_Read 6
|
||||
#define SC_Write 7
|
||||
#define SC_Close 8
|
||||
#define SC_Fork 9
|
||||
#define SC_Yield 10
|
||||
|
||||
#ifdef IN_USER_MODE
|
||||
|
||||
// LB: This part is read only on compiling the test/*.c files.
|
||||
// It is *not* read on compiling test/start.S
|
||||
|
||||
|
||||
/* The system call interface. These are the operations the Nachos
|
||||
* kernel needs to support, to be able to run user programs.
|
||||
*
|
||||
* Each of these is invoked by a user program by simply calling the
|
||||
* procedure; an assembly language stub stuffs the system call code
|
||||
* into a register, and traps to the kernel. The kernel procedures
|
||||
* are then invoked in the Nachos kernel, after appropriate error checking,
|
||||
* from the system call entry point in exception.cc.
|
||||
*/
|
||||
|
||||
/* Stop Nachos, and print out performance stats */
|
||||
void Halt () __attribute__ ((__noreturn__));
|
||||
|
||||
|
||||
/* Address space control operations: Exit, Exec, and Join */
|
||||
|
||||
/* This user program is done (status = 0 means exited normally). */
|
||||
void Exit (int status) __attribute__ ((__noreturn__));
|
||||
|
||||
/* A unique identifier for an executing user program (address space) */
|
||||
typedef int SpaceId;
|
||||
|
||||
/* Run the executable, stored in the Nachos file "name", and return the
|
||||
* address space identifier
|
||||
*/
|
||||
SpaceId Exec (const char *name);
|
||||
|
||||
/* Only return once the the user program "id" has finished.
|
||||
* Return the exit status.
|
||||
*/
|
||||
int Join (SpaceId id);
|
||||
|
||||
|
||||
/* File system operations: Create, Open, Read, Write, Close
|
||||
* These functions are patterned after UNIX -- files represent
|
||||
* both files *and* hardware I/O devices.
|
||||
*
|
||||
* If this assignment is done before doing the file system assignment,
|
||||
* note that the Nachos file system has a stub implementation, which
|
||||
* will work for the purposes of testing out these routines.
|
||||
*/
|
||||
|
||||
/* A unique identifier for an open Nachos file. */
|
||||
typedef int OpenFileId;
|
||||
|
||||
/* when an address space starts up, it has two open files, representing
|
||||
* keyboard input and display output (in UNIX terms, stdin and stdout).
|
||||
* Read and Write can be used directly on these, without first opening
|
||||
* the console device.
|
||||
*/
|
||||
|
||||
#define ConsoleInput 0
|
||||
#define ConsoleOutput 1
|
||||
|
||||
/* Create a Nachos file, with "name" */
|
||||
void Create (const char *name);
|
||||
|
||||
/* Open the Nachos file "name", and return an "OpenFileId" that can
|
||||
* be used to read and write to the file.
|
||||
*/
|
||||
OpenFileId Open (const char *name);
|
||||
|
||||
/* Write "size" bytes from "buffer" to the open file. */
|
||||
void Write (const void *buffer, int size, OpenFileId id);
|
||||
|
||||
/* Read "size" bytes from the open file into "buffer".
|
||||
* Return the number of bytes actually read -- if the open file isn't
|
||||
* long enough, or if it is an I/O device, and there aren't enough
|
||||
* characters to read, return whatever is available (for I/O devices,
|
||||
* you should always wait until you can return at least one character).
|
||||
*/
|
||||
int Read (void *buffer, int size, OpenFileId id);
|
||||
|
||||
/* Close the file, we're done reading and writing to it. */
|
||||
void Close (OpenFileId id);
|
||||
|
||||
|
||||
|
||||
/* User-level thread operations: Fork and Yield. To allow multiple
|
||||
* threads to run within a user program.
|
||||
*/
|
||||
|
||||
/* Fork a thread to run a procedure ("func") in the *same* address space
|
||||
* as the current thread.
|
||||
*/
|
||||
void Fork (void (*func) ());
|
||||
|
||||
/* Yield the CPU to another runnable thread, whether in this address space
|
||||
* or not.
|
||||
*/
|
||||
void Yield ();
|
||||
|
||||
#endif // IN_USER_MODE
|
||||
|
||||
#endif /* SYSCALL_H */
|
Loading…
Add table
Add a link
Reference in a new issue