An x86-64 operating system built from xv6, now largely rewritten: SMP, a copy-on-write root filesystem (SchFS), PCI/USB/virtio drivers, a TTF rasteriser, and a Linux-numbered syscall ABI. No history before this point -- the tree was never under version control. This commit exists so the rename that follows has something to undo to.
159 lines
4.5 KiB
C
159 lines
4.5 KiB
C
#include "types.h"
|
|
#include "defs.h"
|
|
#include "irq.h"
|
|
#include "param.h"
|
|
#include "memlayout.h"
|
|
#include "mmu.h"
|
|
#include "proc.h"
|
|
#include "x86.h"
|
|
#include "traps.h"
|
|
#include "spinlock.h"
|
|
#include "posix.h"
|
|
|
|
uint *idt;
|
|
extern addr_t vectors[];
|
|
struct spinlock tickslock;
|
|
uint ticks;
|
|
|
|
static void
|
|
mkgate(uint *idt, uint n, addr_t kva, uint pl)
|
|
{
|
|
uint64 addr = (uint64) kva;
|
|
|
|
n *= 4;
|
|
idt[n+0] = (addr & 0xFFFF) | (KERNEL_CS << 16);
|
|
idt[n+1] = (addr & 0xFFFF0000) | 0x8E00 | ((pl & 3) << 13);
|
|
idt[n+2] = addr >> 32;
|
|
idt[n+3] = 0;
|
|
}
|
|
|
|
void idtinit(void)
|
|
{
|
|
struct {
|
|
ushort limit;
|
|
uint64 base;
|
|
} __attribute__((packed)) idtdesc;
|
|
|
|
idtdesc.limit = PGSIZE - 1;
|
|
idtdesc.base = (uint64)idt;
|
|
|
|
asm volatile("lidt %0" : : "m" (idtdesc));
|
|
}
|
|
|
|
void tvinit(void)
|
|
{
|
|
int n;
|
|
idt = (uint*) kalloc();
|
|
memset(idt, 0, PGSIZE);
|
|
for (n = 0; n < 256; n++)
|
|
mkgate(idt, n, vectors[n], 0);
|
|
mkgate(idt, T_SYSCALL, vectors[T_SYSCALL], 3);
|
|
}
|
|
|
|
uint
|
|
uptime(void)
|
|
{
|
|
uint xticks;
|
|
|
|
acquire(&tickslock);
|
|
xticks = ticks;
|
|
release(&tickslock);
|
|
return xticks;
|
|
}
|
|
|
|
// Which signal a CPU fault corresponds to, so a process killed by a fault is
|
|
// reported to wait() as died-by-signal with a sensible cause rather than a
|
|
// blanket SIGKILL.
|
|
static int
|
|
trapsig(uint64 trapno)
|
|
{
|
|
switch(trapno){
|
|
case T_DIVIDE: return SIGFPE;
|
|
case T_ILLOP: return SIGILL;
|
|
case T_GPFLT: return SIGSEGV;
|
|
case T_PGFLT: return SIGSEGV;
|
|
case T_BRKPT: return SIGTRAP;
|
|
case T_DEBUG: return SIGTRAP;
|
|
default: return SIGSEGV;
|
|
}
|
|
}
|
|
|
|
void
|
|
trap(struct trapframe *tf)
|
|
{
|
|
kstack_check_canary("trap entry");
|
|
switch(tf->trapno){
|
|
case T_IRQ0 + IRQ_TIMER:
|
|
if(cpunum() == 0){
|
|
acquire(&tickslock);
|
|
ticks++;
|
|
wakeup(&ticks);
|
|
release(&tickslock);
|
|
}
|
|
lapiceoi();
|
|
break;
|
|
case T_IRQ0 + IRQ_SPURIOUS:
|
|
cprintf("cpu%d: spurious interrupt at %p:%p\n",
|
|
cpunum(), tf->cs, tf->rip);
|
|
lapiceoi();
|
|
break;
|
|
|
|
default:
|
|
// Every other IOAPIC pin goes through the registration table. The device
|
|
// interrupts used to be listed here one case at a time, which cannot work
|
|
// for anything found on the PCI bus: a driver does not know its line
|
|
// until it reads the device's config space at probe time, so there is no
|
|
// constant to write a case label for. See include/irq.h.
|
|
if(tf->trapno >= T_IRQ0 && tf->trapno < T_IRQ0 + NIRQ){
|
|
irq_dispatch(tf->trapno - T_IRQ0);
|
|
lapiceoi();
|
|
break;
|
|
}
|
|
|
|
if(myproc() == 0 || (tf->cs&3) == 0){
|
|
cprintf("unexpected trap %d from cpu %d rip %p (cr2=0x%p) err=0x%p\n",
|
|
tf->trapno, cpunum(), tf->rip, rcr2(), tf->err);
|
|
if (myproc())
|
|
cprintf("proc id: %d\n", myproc()->pid);
|
|
if (tf->trapno == T_GPFLT) {
|
|
uint64 *iretframe = (uint64*)tf->rsp;
|
|
cprintf("iretq frame @ %p: rip=%p cs=%p rflags=%p rsp=%p ss=%p\n",
|
|
iretframe, iretframe[0], iretframe[1], iretframe[2],
|
|
iretframe[3], iretframe[4]);
|
|
if (myproc())
|
|
cprintf("this tf=%p myproc()->tf=%p myproc()->kstack=%p (tf-kstack=%p)\n",
|
|
tf, myproc()->tf, myproc()->kstack, (uint64)tf - (uint64)myproc()->kstack);
|
|
}
|
|
panic("trap");
|
|
}
|
|
cprintf("pid %d %s: trap %d err %d on cpu %d "
|
|
"rip 0x%p addr 0x%p--kill proc\n",
|
|
myproc()->pid, myproc()->name, tf->trapno, tf->err, cpunum(), tf->rip,
|
|
rcr2());
|
|
// General-purpose register dump. This used to be followed by ~25 lines
|
|
// that walked musl's mallocng group/meta structures out of rax/rdi and
|
|
// dumped the user stack -- scaffolding for the boot-crash investigation
|
|
// recorded in docs/architecture/kernel-history.md section 1. That
|
|
// investigation is closed and the underlying kernel bugs are fixed, so
|
|
// the allocator-specific part is gone; the registers stay, since they are
|
|
// useful for any fault.
|
|
if (tf->trapno == T_GPFLT) {
|
|
cprintf(" rax=%p rbx=%p rcx=%p rdx=%p rsi=%p rdi=%p rbp=%p rsp=%p\n",
|
|
tf->rax, tf->rbx, tf->rcx, tf->rdx, tf->rsi, tf->rdi, tf->rbp, tf->rsp);
|
|
}
|
|
myproc()->killed = 1;
|
|
if(myproc()->killsig == 0)
|
|
myproc()->killsig = trapsig(tf->trapno);
|
|
}
|
|
|
|
if(myproc() && myproc()->killed && (tf->cs&3) == DPL_USER)
|
|
exit(W_EXITCODE(0, myproc()->killsig ? myproc()->killsig : SIGKILL));
|
|
|
|
if(myproc() && myproc()->state == RUNNING && tf->trapno == T_IRQ0+IRQ_TIMER)
|
|
yield();
|
|
|
|
if(myproc() && myproc()->killed && (tf->cs&3) == DPL_USER)
|
|
exit(W_EXITCODE(0, myproc()->killsig ? myproc()->killsig : SIGKILL));
|
|
|
|
kstack_check_canary("trap exit");
|
|
}
|