Problem set 1
khadija 023-17-0042
sumra 051-18-0009
Coding
#include "kernel.hh"
#include "k-ahci.hh"
#include "k-apic.hh"
#include "k-chkfs.hh"
#include "k-chkfsiter.hh"
#include "k-devices.hh"
#include "k-vmiter.hh"
#ifndef CHICKADEE_FIRST_PROCESS
#define CHICKADEE_FIRST_PROCESS "allocator"
#endif
// kernel.cc
//
// This is the kernel.
volatile unsigned long ticks; // # timer interrupts so far on CPU 0
int kdisplay; // type of display; initially
KDISPLAY_CONSOLE
static void kdisplay_ontick();
static void process_setup(pid_t pid, const char* program_name =
nullptr);
// kernel_start(command)
// Initialize the hardware and processes and start running. The
`command`
// string is an optional string passed from the boot loader.
void kernel_start(const char* command) {
init_hardware();
console_clear();
// Set up process descriptors
for (pid_t i = 0; i < NPROC; i++) {
ptable[i] = nullptr;
}
auto irqs = ptable_lock.lock();
process_setup(1, CHICKADEE_FIRST_PROCESS);
ptable_lock.unlock(irqs);
// Switch to the first process
cpus[0].schedule(nullptr);
}
// process_setup(pid, name)
// Load application program `name` as process number `pid`.
// This loads the application's code and data into memory, sets its
// %rip and %rsp, gives it a stack page, and marks it as runnable.
void process_setup(pid_t pid, const char* name) {
memfile_loader ld(memfile::initfs_lookup(name),
kalloc_pagetable());
assert(ld.memfile_ && ld.pagetable_);
int r = proc::load(ld);
assert(r >= 0);
assert(!ptable[pid]);
proc* p = ptable[pid] = knew<proc>();
p->init_user(pid, ld.pagetable_);
p->regs_->reg_rip = ld.entry_rip_;
void* stkpg = kalloc(PAGESIZE);
assert(stkpg);
r = vmiter(p, MEMSIZE_VIRTUAL -
PAGESIZE).map(ka2pa(stkpg));
assert(r >= 0);
p->regs_->reg_rsp = MEMSIZE_VIRTUAL;
int cpu = pid % ncpu;
cpus[cpu].runq_lock_.lock_noirq();
cpus[cpu].enqueue(p);
cpus[cpu].runq_lock_.unlock_noirq();
}
// proc::exception(reg)
// Exception handler (for interrupts, traps, and faults).
//
// The register values from exception time are stored in `reg`.
// The processor responds to an exception by saving application
state on
// the current CPU stack, then jumping to kernel assembly code
(in
// k-exception.S). That code transfers the state to the current
kernel
// task's stack, then calls proc::exception().
void proc::exception(regstate* regs) {
// It can be useful to log events using `log_printf`.
// Events logged this way are stored in the host's `log.txt` file.
/*log_printf("proc %d: exception %d\n", this->id_, regs-
>reg_intno);*/
// Show the current cursor location.
console_show_cursor(cursorpos);
// Actually handle the exception.
switch (regs->reg_intno) {
case INT_IRQ + IRQ_TIMER: {
cpustate* cpu = this_cpu();
if (cpu->index_ == 0) {
++ticks;
kdisplay_ontick();
}
lapicstate::get().ack();
this->regs_ = regs;
this->yield_noreturn();
break; /* will not be reached */
}
case INT_PF: { // pagefault exception
// Analyze faulting address and access type.
uintptr_t addr = rdcr2();
const char* operation = regs->reg_errcode &
PFERR_WRITE
? "write" : "read";
const char* problem = regs->reg_errcode &
PFERR_PRESENT
? "protection problem" : "missing page";
if ((regs->reg_cs & 5) == 0) {
panic("Kernel page fault for %p (%s %s, rip=%p)!\n",
addr, operation, problem, regs->reg_rip);
}
error_printf(CPOS(24, 0), 0x0C00,
"Process %d page fault for %p (%s %s, rip=%p)!\n",
id_, addr, operation, problem, regs->reg_rip);
this->state_ = proc::broken;
this->yield();
break;
}
case INT_IRQ + IRQ_KEYBOARD:
keyboardstate::get().handle_interrupt();
break;
default:
if (sata_disk && regs->reg_intno == INT_IRQ + sata_disk-
>irq_) {
sata_disk->handle_interrupt();
} else {
panic("Unexpected exception %d!\n", regs->reg_intno);
}
break; /* will not be reached */
// Return to the current process.
// If exception arrived in user mode, the process must be
runnable.
assert((regs->reg_cs & 3) == 0 || this->state_ ==
proc::runnable);
}
// proc::syscall(regs)
// System call handler.
//
// The register values from system call time are stored in `regs`.
// The return value from `proc::syscall()` is returned to the user
// process in `%rax`.
uintptr_t proc::syscall(regstate* regs) {
switch (regs->reg_rax) {
case SYSCALL_KDISPLAY:
if (kdisplay != (int) regs->reg_rdi) {
console_clear();
}
kdisplay = regs->reg_rdi;
return 0;
case SYSCALL_PANIC:
panic(nullptr);
break; // will not be reached
case SYSCALL_GETPID:
return id_;
case SYSCALL_YIELD:
this->yield();
return 0;
case SYSCALL_PAGE_ALLOC: {
uintptr_t addr = regs->reg_rdi;
if (addr >= VA_LOWEND || addr & 0xFFF) {
return -1;
}
void* pg = kalloc(PAGESIZE);
if (!pg || vmiter(this, addr).map(ka2pa(pg)) < 0) {
return -1;
}
return 0;
}
case SYSCALL_PAUSE: {
sti();
for (uintptr_t delay = 0; delay < 1000000; ++delay) {
pause();
}
cli();
return 0;
}
case SYSCALL_FORK:
return syscall_fork(regs);
case SYSCALL_READ:
return syscall_read(regs);
case SYSCALL_WRITE:
return syscall_write(regs);
case SYSCALL_READDISKFILE:
return syscall_readdiskfile(regs);
case SYSCALL_SYNC: {
int drop = regs->reg_rdi;
// `drop > 1` asserts that no data blocks are referenced (except
// possibly superblock and FBB blocks). This can only be
ensured on
// tests that run as the first process.
if (drop > 1 && strncmp(CHICKADEE_FIRST_PROCESS,
"test", 4) != 0) {
drop = 1;
}
return bufcache::get().sync(drop);
}
default:
// no such system call
log_printf("%d: no such system call %u\n", id_, regs-
>reg_rax);
return E_NOSYS;
}
}
// proc::syscall_read(regs), proc::syscall_write(regs)
// Handle read and write system calls.
uintptr_t proc::syscall_read(regstate* regs) {
int fd = regs->reg_rdi;
uintptr_t addr = regs->reg_rsi;
size_t sz = regs->reg_rdx;
auto& kbd = keyboardstate::get();
auto irqs = kbd.lock_.lock();
// mark that we are now reading from the keyboard
// (so `q` should not power off)
if (kbd.state_ == kbd.boot) {
kbd.state_ = kbd.input;
}
// yield until a line is available
// (special case: do not block if the user wants to read 0 bytes)
while (sz != 0 && kbd.eol_ == 0) {
kbd.lock_.unlock(irqs);
yield();
irqs = kbd.lock_.lock();
}
// read that line or lines
size_t n = 0;
while (kbd.eol_ != 0 && n < sz) {
if (kbd.buf_[kbd.pos_] == 0x04) {
// Ctrl-D means EOF
if (n == 0) {
kbd.consume(1);
}
break;
} else {
*reinterpret_cast<char*>(addr) = kbd.buf_[kbd.pos_];
++addr;
++n;
kbd.consume(1);
}
}
kbd.lock_.unlock(irqs);
return n;
}
uintptr_t proc::syscall_write(regstate* regs){
int fd = regs->reg_rdi;
uintptr_t addr = regs->reg_rsi;
size_t sz = regs->reg_rdx;
auto& csl = consolestate::get();
auto irqs = csl.lock_.lock();
size_t n = 0;
while (n < sz) {
int ch = *reinterpret_cast<const char*>(addr);
++addr;
++n;
console_printf(0x0F00, "%c", ch);
}
csl.lock_.unlock(irqs);
return n;
}
uintptr_t proc::syscall_fork(regstate* regs){
// auto irqs=ptable_lock.lock();
pid_t cp = SYSCALL_GETPID;
proc* par = current();
pid_t pid = 1;
for(;pid<NPROC;pid++){
if(ptable[pid] == nullptr){
break;
}
else if(pid == 15)
return -1;
}
// Set up process descriptors
auto irqs = ptable_lock.lock();
pid_t i = 0;
for (; i < NPROC; i++) {
if(ptable[i] = nullptr)
{
break;
}
}
process_setup(i, CHICKADEE_FIRST_PROCESS);
ptable_lock.unlock(irqs);
proc* p = knew<proc>();
ptable[pid] = p;
ptable[pid]->pagetable_ = kalloc_pagetable();
memfile_loader ld(pid, p->pagetable_);
p->init_user(pid,ld.pagetable_);
memcpy(p->regs_,par->regs_,sizeof(regs_));
p->regs_->reg_rax=0;
ptable[pid]->regs_->reg_rip = regs->reg_rip;
//p->regs_->reg_rip =regs->entry_rip_;
memcpy(&p->regs_->reg_rcx,&ptable[cp]->regs_-
>reg_rcx,sizeof(regs));
void* stkpg = kalloc(PAGESIZE);
assert(stkpg);
int r = proc::load(ld);
r = vmiter(p, MEMSIZE_VIRTUAL -
PAGESIZE).map(ka2pa(stkpg));
assert(r >= 0);
p->regs_->reg_rsp = MEMSIZE_VIRTUAL;
//scehdualling
int cpu = pid % ncpu;
cpus[cpu].runq_lock_.lock_noirq();
cpus[cpu].enqueue(ptable[pid]);
cpus[cpu].runq_lock_.unlock_noirq();
ptable_lock.unlock(irqs);
return pid++;
uintptr_t proc::syscall_readdiskfile(regstate* regs){
const char* filename = reinterpret_cast<const char*>(regs-
>reg_rdi);
unsigned char* buf = reinterpret_cast<unsigned char*>(regs-
>reg_rsi);
uintptr_t sz = regs->reg_rdx;
uintptr_t off = regs->reg_r10;
if (!sata_disk) {
return E_IO;
}
// read root directory to find file inode number
auto ino = chkfsstate::get().lookup_inode(filename);
if (!ino) {
return 0;//E_NOENT;
}
// read file inode
ino->lock_read();
chkfs_fileiter it(ino);
size_t nread = 0;
while (nread < sz) {
// copy data from current block
if (bcentry* e = it.find(off).get_disk_entry()) {
unsigned b = it.block_relative_offset();
size_t ncopy = min(
size_t(ino->size - it.offset()), // bytes left in file
chkfs::blocksize - b, // bytes left in block
sz - nread // bytes left in request
);
memcpy(buf + nread, e->buf_ + b, ncopy);
e->put();
nread += ncopy;
off += ncopy;
if (ncopy == 0) {
break;
}
} else {
break;
}
}
ino->unlock_read();
ino->put();
return 0;//nread;
}
// memshow()
// Draw a picture of memory (physical and virtual) on the CGA
console.
// Switches to a new process's virtual memory map every 0.25
sec.
// Uses `console_memviewer()`, a function defined in `k-
memviewer.cc`.
static void memshow() {
static unsigned last_ticks = 0;
static int showing = 1;
// switch to a new process every 0.25 sec
if (last_ticks == 0 || ticks - last_ticks >= HZ / 2) {
last_ticks = ticks;
showing = (showing + 1) % NPROC;
}
auto irqs = ptable_lock.lock();
int search = 0;
while ((!ptable[showing]
|| !ptable[showing]->pagetable_
|| ptable[showing]->pagetable_ == early_pagetable)
&& search < NPROC) {
showing = (showing + 1) % NPROC;
++search;
}
extern void console_memviewer(proc* vmp);
console_memviewer(ptable[showing]);
ptable_lock.unlock(irqs);
}
// kdisplay_ontick()
// Shows the currently-configured kdisplay. Called once every
tick
// (every 0.01 sec) by CPU 0.
void kdisplay_ontick() {
if (kdisplay == KDISPLAY_MEMVIEWER) {
memshow();
}
}
Result: