do not edit — generated by btf.
git.druid.rocksindexdruid520kaboomsrc/kernel/idt.nsc

src/kernel/idt.nsc


/*
 * idt: 32 cpu exception vectors, 16 remapped pic irq vectors (masked
 * for now -- no device driver hooks them up yet), one syscall gate at
 * 0x80. every vector's dispatch funnels through isr_dispatch below,
 * called by the single shared trampoline in idt_asm.s.
 *
 * same packing constraint as gdt.nsc: an idt entry can't be a nsc
 * struct (fields are word-addressed, not byte-packed), so each 16-byte
 * entry is built as two hand-packed u64 words.
 */
 
include "klog.nsh";
include "../fs/kfs.nsh";
include "exec.nsh";
include "alloc.nsh";
 
global struct arena heap_colosseum;
 
void outb(u16 port, u8 val);
u8 inb(u16 port);
void load_idt(ptr idtr_addr);
void serial_puts(ptr str);
void vga_puts(ptr str);
 
global i8 idt_table[4096];   /* 256 entries * 16 bytes */
global i8 idtr[10];
 
void
idt_set_entry(i32 idx, ptr handler, u16 selector, u8 type_attr)
{
	ptr lo_slot;
	ptr hi_slot;
	u64 addr;
	u64 lo;
	u64 hi;
 
	addr = (u64)handler;
 
	lo = addr & (u64)0xffff;
	lo = lo | ((u64)selector << (u64)16);
	lo = lo | ((u64)type_attr << (u64)40);
	lo = lo | (((addr >> (u64)16) & (u64)0xffff) << (u64)48);
 
	hi = addr >> (u64)32; /* right shift on a u64 already zero-fills the top bits */
 
	lo_slot = &idt_table[0] + idx * 16;
	hi_slot = lo_slot + 8;
	*lo_slot = (i64)lo;
	*hi_slot = (i64)hi;
}
 
/* one extern per address-getter, generated in idt_asm.s (see the
 * comment there for why this has to work through a getter function
 * instead of nsc just taking each stub's address directly). */
ptr get_isr0_addr(void);
ptr get_isr1_addr(void);
ptr get_isr2_addr(void);
ptr get_isr3_addr(void);
ptr get_isr4_addr(void);
ptr get_isr5_addr(void);
ptr get_isr6_addr(void);
ptr get_isr7_addr(void);
ptr get_isr8_addr(void);
ptr get_isr9_addr(void);
ptr get_isr10_addr(void);
ptr get_isr11_addr(void);
ptr get_isr12_addr(void);
ptr get_isr13_addr(void);
ptr get_isr14_addr(void);
ptr get_isr15_addr(void);
ptr get_isr16_addr(void);
ptr get_isr17_addr(void);
ptr get_isr18_addr(void);
ptr get_isr19_addr(void);
ptr get_isr20_addr(void);
ptr get_isr21_addr(void);
ptr get_isr22_addr(void);
ptr get_isr23_addr(void);
ptr get_isr24_addr(void);
ptr get_isr25_addr(void);
ptr get_isr26_addr(void);
ptr get_isr27_addr(void);
ptr get_isr28_addr(void);
ptr get_isr29_addr(void);
ptr get_isr30_addr(void);
ptr get_isr31_addr(void);
ptr get_isr32_addr(void);
ptr get_isr33_addr(void);
ptr get_isr34_addr(void);
ptr get_isr35_addr(void);
ptr get_isr36_addr(void);
ptr get_isr37_addr(void);
ptr get_isr38_addr(void);
ptr get_isr39_addr(void);
ptr get_isr40_addr(void);
ptr get_isr41_addr(void);
ptr get_isr42_addr(void);
ptr get_isr43_addr(void);
ptr get_isr44_addr(void);
ptr get_isr45_addr(void);
ptr get_isr46_addr(void);
ptr get_isr47_addr(void);
ptr get_isr128_addr(void);
 
void
idt_install(void)
{
	idt_set_entry(0, get_isr0_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(1, get_isr1_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(2, get_isr2_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(3, get_isr3_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(4, get_isr4_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(5, get_isr5_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(6, get_isr6_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(7, get_isr7_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(8, get_isr8_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(9, get_isr9_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(10, get_isr10_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(11, get_isr11_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(12, get_isr12_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(13, get_isr13_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(14, get_isr14_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(15, get_isr15_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(16, get_isr16_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(17, get_isr17_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(18, get_isr18_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(19, get_isr19_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(20, get_isr20_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(21, get_isr21_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(22, get_isr22_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(23, get_isr23_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(24, get_isr24_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(25, get_isr25_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(26, get_isr26_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(27, get_isr27_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(28, get_isr28_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(29, get_isr29_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(30, get_isr30_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(31, get_isr31_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(32, get_isr32_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(33, get_isr33_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(34, get_isr34_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(35, get_isr35_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(36, get_isr36_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(37, get_isr37_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(38, get_isr38_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(39, get_isr39_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(40, get_isr40_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(41, get_isr41_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(42, get_isr42_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(43, get_isr43_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(44, get_isr44_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(45, get_isr45_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(46, get_isr46_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(47, get_isr47_addr(), (u16)0x08, (u8)0x8e);
	idt_set_entry(128, get_isr128_addr(), (u16)0x08, (u8)0xee);  /* syscall gate: dpl=3 so user mode can int $0x80 later */
}
 
void io_wait(void);
void halt(void);
void kbd_handle_irq(void);
void serial_handle_irq(void);
 
void
pic_remap(void)
{
	outb((u16)0x20, (u8)0x11);
	io_wait();
	outb((u16)0xa0, (u8)0x11);
	io_wait();
	outb((u16)0x21, (u8)0x20);   /* master irq0-7 -> vectors 32-39 */
	io_wait();
	outb((u16)0xa1, (u8)0x28);   /* slave irq8-15 -> vectors 40-47 */
	io_wait();
	outb((u16)0x21, (u8)0x04);
	io_wait();
	outb((u16)0xa1, (u8)0x02);
	io_wait();
	outb((u16)0x21, (u8)0x01);
	io_wait();
	outb((u16)0xa1, (u8)0x01);
	io_wait();
	/* mask every irq except irq1 (keyboard) and irq4 (com1, bits 1
	 * and 4 of the master mask): 0xff with both cleared = 0xed. com1
	 * unmasked so a byte arriving over serial -- the ONLY input path
	 * that exists at all under -nographic, where there's no separate
	 * ps/2-backed keyboard focus -- actually wakes up a halted cpu
	 * (see serial.nsc's own note on why this was missing entirely
	 * until someone tried typing into a real -nographic session).
	 * remapping matters even for the still-masked ones -- it's what
	 * keeps a spurious/late irq from ever landing on a cpu exception
	 * vector by accident. */
	outb((u16)0x21, (u8)0xed);
	outb((u16)0xa1, (u8)0xff);
}
 
void
pic_send_eoi(u64 vector)
{
	if(vector >= (u64)40)
	{
		outb((u16)0xa0, (u8)0x20); /* slave-handled irq: eoi both */
	}
	outb((u16)0x20, (u8)0x20);
}
 
/* saved-register block layout, from isr_common_stub's push order
 * (last pushed = lowest address = offset 0): r15,r14,r13,r12,r11,r10,
 * r9,r8,rbp,rdi,rsi,rdx,rcx,rbx,rax. rax (first pushed, so highest
 * offset among the 15) lands at 14*8=112; rdi (arg1 in sysv, our
 * syscall convention's arg1 too) at 9*8=72. */
u64
regs_get_rax(ptr regs)
{
	ptr slot;
	slot = regs + 112;
	return (u64)*slot;
}
 
void
regs_set_rax(ptr regs, u64 val)
{
	ptr slot;
	slot = regs + 112;
	*slot = (i64)val;
}
 
u64
regs_get_rdi(ptr regs)
{
	ptr slot;
	slot = regs + 72;
	return (u64)*slot;
}
 
u64
regs_get_rsi(ptr regs)
{
	ptr slot;
	slot = regs + 80;
	return (u64)*slot;
}
 
u64
regs_get_rdx(ptr regs)
{
	ptr slot;
	slot = regs + 88;
	return (u64)*slot;
}
 
u64
regs_get_r10(ptr regs)
{
	ptr slot;
	slot = regs + 40;
	return (u64)*slot;
}
 
void
print_udec(u64 val)
{
	i8 buf[21];
	i32 i;
	u64 v;
 
	i = 20;
	buf[20] = 0;
	v = val;
	if(v == (u64)0)
	{
		i = i - 1;
		buf[i] = (i8)48;
	}
	while(v > (u64)0)
	{
		i = i - 1;
		buf[i] = (i8)(48 + (i32)(v % (u64)10));
		v = v / (u64)10;
	}
	serial_puts(&buf[i]);
	vga_puts(&buf[i]);
}
 
/* same digit-extraction as print_udec, but into the kernel log
 * instead of the live console -- sys_doerror's own pid number. kept
 * as its own tiny copy rather than reusing print_udec directly: this
 * one specifically must NOT also write to serial/vga (an error log
 * line isn't the same thing as a boot/panic message the user is
 * meant to see immediately on the console). */
void
klog_write_udec(u64 val)
{
	i8 buf[21];
	i32 i;
	u64 v;
 
	i = 20;
	buf[20] = 0;
	v = val;
	if(v == (u64)0)
	{
		i = i - 1;
		buf[i] = (i8)48;
	}
	while(v > (u64)0)
	{
		i = i - 1;
		buf[i] = (i8)(48 + (i32)(v % (u64)10));
		v = v / (u64)10;
	}
	klog_write(&buf[i]);
}
 
/* the shared error path every permission-checked syscall branch below
 * calls instead of hand-rolling its own "log it, then set rax"
 * boilerplate. is_error is the CALLER's own already-correct "did this
 * fail" test -- the failure convention genuinely differs per syscall
 * (some return a negative value, some a plain 0/1 boolean, see the
 * syscall table's own comment), so this never tries to guess it from
 * result alone. op_name is a short label for the log line.
 *
 * logs "pid <N>: <op_name>: permission denied" specifically when
 * kaboom_errno says that's really why (kfs_check_perm, kfs.nsc, is
 * the one place that ever sets it -- see its own comment), or a
 * plainer "<op_name>: failed" otherwise (not found, already exists,
 * a directory that isn't empty, ...) -- then always sets rax to
 * result, exactly like every syscall branch already did by hand. */
void
sys_doerror(ptr regs, i64 result, i32 is_error, ptr op_name)
{
	if(is_error == 1)
	{
		klog_write("pid ");
		klog_write_udec(kaboom_current_pid());
		klog_write(": ");
		klog_write(op_name);
		if(kaboom_errno == (i32)1)
		{
			klog_write(": permission denied\n");
		}
		else
		{
			klog_write(": failed\n");
		}
	}
	regs_set_rax(regs, (u64)result);
}
 
i64 fd_read(i32 fd, ptr buf, u64 maxlen);
i64 fd_write(i32 fd, ptr buf, u64 len);
i32 fd_open(ptr path, u64 path_len);
i32 fd_close(i32 fd);
i64 fd_fsize(i32 fd);
u64 klog_read(ptr buf, u64 maxlen);
void vga_clear(void);
void serial_clear(void);
void rtc_read_datetime(ptr buf);
 
/* syscall table: number in rax (also where the return value goes),
 * args in rdi/rsi/rdx -- same register order/positions as the real
 * x86-64 sysv/syscall convention, kept for familiarity even though
 * the actual entry mechanism here is the classic int-gate, not the
 * syscall instruction.
 *
 * every syscall below that can genuinely fail routes its result
 * through sys_doerror (this file, right above syscall_dispatch)
 * instead of setting rax by hand -- the return VALUE and its meaning
 * per syscall are unchanged (still exactly what's documented below),
 * sys_doerror only adds a kernel-log line, "pid <N>: <name>:
 * permission denied" or "<name>: failed", whenever the call actually
 * failed. it distinguishes the two using kaboom_errno (kfs.nsc),
 * which kfs_check_perm is the one and only place that ever sets --
 * every other failure reason (not found, already exists, a directory
 * that isn't empty, ...) logs as a plain "failed" instead. `klogs`
 * shows these lines like any other kernel log entry.
 *   1 = debug print a nul-terminated string (rdi) -- the original
 *       proof-of-mechanism syscall, kept for back-compat.
 *   2 = write(fd, buf, len) -> bytes written, or -1
 *   3 = read(fd, buf, maxlen) -> bytes read, or -1
 *   4 = open(path, path_len) -> fd, or -1
 *   5 = close(fd) -> 0, or -1
 *   6 = listdir(names_out, max) -> count of entries written
 *   7 = exec(path, path_len, argc, argv) -> the child's exit code
 *       (whatever it left in rax when it ret'd), or -1
 *   8 = klog_read(buf, maxlen) -> bytes copied from the kernel log
 *   9 = mkdir(name, name_len, perm) -> new inode number, or -1
 *  10 = rmdir(name, name_len) -> 1, or 0
 *  11 = rm(name, name_len) -> 1, or 0
 *  12 = cd(name, name_len) -> 1, or 0
 *  13 = pwd(buf) -> path length
 *  14 = info(buf) -> fills buf (room for 4 u64s) with total_blocks,
 *       blocks_used, inode_count, inodes_used; always returns 0
 *  15 = writefile(name, name_len, data, len) -> 1, or 0 -- finds or
 *       creates name in the current directory and overwrites its
 *       entire content; 4 args, same r10-for-the-4th-arg convention
 *       as exec (7)
 *  16 = vga_clear() -> void, no args
 *  17 = chmod(name, name_len, perm) -> 1, or 0
 *  18 = alloc(size) -> ptr, or (ptr)0 if the heap arena is exhausted
 *       -- userspace's way to get memory dynamically instead of
 *       declaring a huge fixed-size global/stack buffer; wraps the
 *       same arena_alloc every kernel-side subsystem uses
 *  19 = free(ptr) -> void, no args used beyond the pointer, always a
 *       no-op -- kaboom's arena allocator never frees (see
 *       alloc.nsc's own note); this exists as real api surface (so a
 *       program can call free without knowing that) not because it
 *       does anything yet
 *  20 = stat(path, path_len) -> -1 not found, 1 a file, 2 a directory
 *  21 = listdir_path(path, path_len, names_out, max) -> count, or -1
 *       if path doesn't resolve to a directory; 4 args, same
 *       r10-for-the-4th-arg convention as exec (7)
 *  22 = date(buf) -> void, fills buf (room for 6 u64s) with second,
 *       minute, hour, day, month, year (full) from the cmos rtc
 *  23 = errno(void) -> kaboom_errno (0 generic, 1 EPERM) as left by
 *       the most recent syscall THIS process made -- check it right
 *       after a failing call, before making another one
 *  24 = getperm(path, path_len) -> the resolved inode's permission
 *       bitmask (r=1 w=2 x=4), or -1 if path doesn't resolve --
 *       chmod's read-back counterpart, for a `perms` command to show
 *       what's actually set
 *  25 = fsize(fd) -> the open file's full content size in bytes, or
 *       -1 if fd isn't an open file -- lets a program size one buffer
 *       to the whole file before a single read, instead of assuming a
 *       fixed maximum
 *  26 = ino(path, path_len) -> the inode number path resolves to, or
 *       -1 if it doesn't resolve -- file identity, so a program can
 *       tell two different path spellings of the same file apart
 *       from two different files (mv's same-file check)
 * grows further once real coreutils show what else they need. */
void
syscall_dispatch(ptr regs)
{
	u64 num;
	u64 arg1;
	u64 arg2;
	u64 arg3;
	i64 result;
 
	num = regs_get_rax(regs);
 
	if(num == (u64)1)
	{
		arg1 = regs_get_rdi(regs);
		serial_puts((ptr)arg1);
		vga_puts((ptr)arg1);
		regs_set_rax(regs, (u64)0);
		return;
	}
	if(num == (u64)2)
	{
		arg1 = regs_get_rdi(regs);
		arg2 = regs_get_rsi(regs);
		arg3 = regs_get_rdx(regs);
		result = fd_write((i32)arg1, (ptr)arg2, arg3);
		sys_doerror(regs, result, (i32)(result < (i64)0), "write");
		return;
	}
	if(num == (u64)3)
	{
		arg1 = regs_get_rdi(regs);
		arg2 = regs_get_rsi(regs);
		arg3 = regs_get_rdx(regs);
		result = fd_read((i32)arg1, (ptr)arg2, arg3);
		sys_doerror(regs, result, (i32)(result < (i64)0), "read");
		return;
	}
	if(num == (u64)4)
	{
		arg1 = regs_get_rdi(regs);
		arg2 = regs_get_rsi(regs);
		result = (i64)fd_open((ptr)arg1, arg2);
		sys_doerror(regs, result, (i32)(result < (i64)0), "open");
		return;
	}
	if(num == (u64)5)
	{
		arg1 = regs_get_rdi(regs);
		result = (i64)fd_close((i32)arg1);
		sys_doerror(regs, result, (i32)(result < (i64)0), "close");
		return;
	}
	if(num == (u64)6)
	{
		arg1 = regs_get_rdi(regs);
		arg2 = regs_get_rsi(regs);
		result = (i64)kfs_dir_list((ptr)arg1, (i32)arg2);
		sys_doerror(regs, result, (i32)(result < (i64)0), "listdir");
		return;
	}
	if(num == (u64)7)
	{
		arg1 = regs_get_rdi(regs);
		arg2 = regs_get_rsi(regs);
		arg3 = regs_get_rdx(regs);
		result = sys_exec((ptr)arg1, arg2, (i64)arg3, (ptr)regs_get_r10(regs));
		/* -1 is ambiguous here (exec itself failing vs. the child's
		 * own legitimate exit code happening to BE -1) -- a
		 * pre-existing wart (sh.nsc already treats any -1 as "command
		 * not found" the same way), not something this makes worse. */
		sys_doerror(regs, result, (i32)(result == (i64)(-1)), "exec");
		return;
	}
	if(num == (u64)8)
	{
		arg1 = regs_get_rdi(regs);
		arg2 = regs_get_rsi(regs);
		result = (i64)klog_read((ptr)arg1, arg2);
		regs_set_rax(regs, (u64)result);
		return;
	}
	if(num == (u64)9)
	{
		arg1 = regs_get_rdi(regs);
		arg2 = regs_get_rsi(regs);
		arg3 = regs_get_rdx(regs);
		result = (i64)kfs_mkdir((ptr)arg1, arg2, arg3);
		sys_doerror(regs, result, (i32)(result < (i64)0), "mkdir");
		return;
	}
	if(num == (u64)10)
	{
		arg1 = regs_get_rdi(regs);
		arg2 = regs_get_rsi(regs);
		result = (i64)kfs_rmdir((ptr)arg1, arg2);
		sys_doerror(regs, result, (i32)(result == (i64)0), "rmdir");
		return;
	}
	if(num == (u64)11)
	{
		arg1 = regs_get_rdi(regs);
		arg2 = regs_get_rsi(regs);
		result = (i64)kfs_rm((ptr)arg1, arg2);
		sys_doerror(regs, result, (i32)(result == (i64)0), "rm");
		return;
	}
	if(num == (u64)12)
	{
		arg1 = regs_get_rdi(regs);
		arg2 = regs_get_rsi(regs);
		result = (i64)kfs_cd((ptr)arg1, arg2);
		sys_doerror(regs, result, (i32)(result == (i64)0), "cd");
		return;
	}
	if(num == (u64)13)
	{
		arg1 = regs_get_rdi(regs);
		result = (i64)kfs_pwd((ptr)arg1);
		regs_set_rax(regs, (u64)result);
		return;
	}
	if(num == (u64)14)
	{
		arg1 = regs_get_rdi(regs);
		kfs_info((ptr)arg1);
		regs_set_rax(regs, (u64)0);
		return;
	}
	if(num == (u64)15)
	{
		arg1 = regs_get_rdi(regs);
		arg2 = regs_get_rsi(regs);
		arg3 = regs_get_rdx(regs);
		result = (i64)kfs_save((ptr)arg1, arg2, (ptr)arg3, regs_get_r10(regs));
		sys_doerror(regs, result, (i32)(result == (i64)0), "writefile");
		return;
	}
	if(num == (u64)16)
	{
		vga_clear();
		serial_clear();
		regs_set_rax(regs, (u64)0);
		return;
	}
	if(num == (u64)17)
	{
		arg1 = regs_get_rdi(regs);
		arg2 = regs_get_rsi(regs);
		arg3 = regs_get_rdx(regs);
		result = (i64)kfs_chmod((ptr)arg1, arg2, arg3);
		sys_doerror(regs, result, (i32)(result == (i64)0), "chmod");
		return;
	}
	if(num == (u64)18)
	{
		arg1 = regs_get_rdi(regs);
		regs_set_rax(regs, (u64)arena_alloc(&heap_colosseum, arg1));
		return;
	}
	if(num == (u64)19)
	{
		regs_set_rax(regs, (u64)0);
		return;
	}
	if(num == (u64)20)
	{
		arg1 = regs_get_rdi(regs);
		arg2 = regs_get_rsi(regs);
		result = kfs_stat((ptr)arg1, arg2);
		sys_doerror(regs, result, (i32)(result == (i64)(-1)), "stat");
		return;
	}
	if(num == (u64)21)
	{
		arg1 = regs_get_rdi(regs);
		arg2 = regs_get_rsi(regs);
		arg3 = regs_get_rdx(regs);
		result = (i64)kfs_dir_list_path((ptr)arg1, arg2, (ptr)arg3, (i32)regs_get_r10(regs));
		sys_doerror(regs, result, (i32)(result < (i64)0), "listdir_path");
		return;
	}
	if(num == (u64)22)
	{
		arg1 = regs_get_rdi(regs);
		rtc_read_datetime((ptr)arg1);
		regs_set_rax(regs, (u64)0);
		return;
	}
	if(num == (u64)23)
	{
		/* the userspace half of kaboom_errno/sys_doerror: klogs
		 * already gets the real reason a syscall failed, but a
		 * command's OWN user-visible error message had no way to ask
		 * "was that permission denied, or something else" until now.
		 * no args -- reads whatever kaboom_errno was left at by the
		 * MOST RECENT syscall this same process made, so a caller
		 * needs to check it right after a failing call, before making
		 * another one. */
		regs_set_rax(regs, (u64)kaboom_errno);
		return;
	}
	if(num == (u64)24)
	{
		arg1 = regs_get_rdi(regs);
		arg2 = regs_get_rsi(regs);
		result = (i64)kfs_getperm((ptr)arg1, arg2);
		sys_doerror(regs, result, (i32)(result < (i64)0), "getperm");
		return;
	}
	if(num == (u64)25)
	{
		arg1 = regs_get_rdi(regs);
		result = fd_fsize((i32)arg1);
		sys_doerror(regs, result, (i32)(result < (i64)0), "fsize");
		return;
	}
	if(num == (u64)26)
	{
		arg1 = regs_get_rdi(regs);
		arg2 = regs_get_rsi(regs);
		result = (i64)kfs_resolve((ptr)arg1, arg2);
		sys_doerror(regs, result, (i32)(result < (i64)0), "ino");
		return;
	}
 
	regs_set_rax(regs, ~(u64)0);
}
 
global void
isr_dispatch(u64 vector, u64 error_code, ptr regs)
{
	if(vector == (u64)128)
	{
		syscall_dispatch(regs);
		return;
	}
	if(vector < (u64)32)
	{
		klog_write("kaboom: PANIC: exception, see serial for vector/error code\n");
		serial_puts("\nkaboom: PANIC: exception vector ");
		print_udec(vector);
		serial_puts(", error code ");
		print_udec(error_code);
		serial_puts("\n");
		while(1)
		{
			halt();
		}
	}
	/* remapped hardware irq (32-47). every one needs an eoi sent back
	 * to the pic regardless of whether anything actually handles it,
	 * or the pic stops delivering further interrupts entirely -- easy
	 * to lose track of since nothing breaks until the SECOND
	 * keypress, not the first. */
	pic_send_eoi(vector);
	if(vector == (u64)33)
	{
		kbd_handle_irq();
	}
	if(vector == (u64)36)
	{
		serial_handle_irq();
	}
}
 
global void
idt_init(void)
{
	i32 limit;
	u64 addr;
	i32 i;
 
	idt_install();
	pic_remap();
 
	limit = 4095;
	idtr[0] = (i8)(limit & 0xff);
	idtr[1] = (i8)((limit >> 8) & 0xff);
 
	addr = (u64)&idt_table[0];
	i = 0;
	while(i < 8)
	{
		idtr[2 + i] = (i8)((addr >> (u64)(i * 8)) & (u64)0xff);
		i = i + 1;
	}
 
	load_idt(&idtr[0]);
}
powered by btf.