#pragma once struct buf; struct context; struct file; struct inode; struct pipe; struct proc; struct rtcdate; struct spinlock; struct sleeplock; struct stat; struct superblock; struct trapframe; struct dirent; struct device; // entryother.S / syscall.S void syscall_entry(void); void ignore_sysret(void); // bio.c void binit(void); struct buf* bread(uint, uint64); void brelse(struct buf*); void bwrite(struct buf*); // Modify without writing. The buffer stays dirty in the cache until an // eviction or a bflush() takes it out; the caller is promising to ask for the // ordering it needs explicitly rather than relying on write order. void bdirty(struct buf*); // Write out every dirty buffer for `dev`, then make the device commit them. // Returns 0, or -1 if the device could not confirm durability. int bflush(uint dev); // console.c struct termios; void consoleinit(void); // The terminal's line-discipline settings. TCGETS/TCSETS in sys_fs.c are the // only callers; the console honours ICANON, ECHO, ECHOE, ECHONL, ISIG, // ICRNL, INLCR, IGNCR, ISTRIP and VMIN. void cons_get_termios(int minor, struct termios *); void cons_set_termios(int minor, const struct termios *); void cons_flush_input(int minor); int cons_fg_pgrp(int minor); void cons_set_fg_pgrp(int minor, int pg); int console_vt_hotkey(int key); extern uint vt_chord_mods; // kernel/dev/kbd.c -- the switch chord extern uint vt_chord_base; struct vt *vt_for_minor(int minor); extern uint lapic_timer_hz; // measured, kernel/arch/x86_64/lapic.c extern uint64 tsc_hz; void cprintf(char*, ...); // A log line: the serial port only, never the display. // // console_takeover() hands the framebuffer to userspace on the first exec, and // after that the screen belongs to whoever is using the machine. A kernel // message arriving then is not information they asked for -- it lands in the // middle of their shell, or their editor, or a compositor's output. The DHCP // lease is what made this necessary: it turns up seconds after the prompt does, // so the first thing anyone sees after booting is the kernel talking over them. // // The same rule the boot path already follows, and the reason cons_serial_only // starts set: the display is the product, the serial line is the diagnostics. // klog is that rule for messages that cannot be timed to happen before // userspace starts. // // A macro rather than a function because cprintf's varargs cannot be forwarded // -- there is no vcprintf, and adding one to duplicate the formatter would cost // more than it buys. cprintf holds cons.lock for the whole message, so the flag // cannot be seen half-changed by another CPU printing at the same time. // // Nothing is lost: serial is where bootcheck reads from and where you want // these when something is wrong, and anything a *user* should see belongs in a // file they can read -- /proc/netif, for the example above. extern int cons_serial_only; #define klog(...) do { \ int _klog_was = cons_serial_only; \ cons_serial_only = 1; \ cprintf(__VA_ARGS__); \ cons_serial_only = _klog_was; \ } while (0) void consoleintr(int(*)(void)); void panic(char*) __attribute__((noreturn)); // exec.c int exec(char*, char**, char**); // file.c struct file* filealloc(void); int fdalloc(struct file*); void fileclose(struct file*); struct file* filedup(struct file*); void fileinit(void); int fileread(struct file*, char*, int n); int file_poll(struct file*, int events); int filestat(struct file*, struct stat*); int filewrite(struct file*, char*, int n); // fs.c void fsinit(int); int dirlink(struct inode*, char*, uint64); struct inode* dirlookup(struct inode*, char*, uint*); // Remove the entry `name` at the cookie dirlookup returned in *poff. int dirunlink(struct inode*, char*, uint); // 1 if the directory holds no real entries, 0 if it does, -1 on error. int dirempty(struct inode*); // Fill `out` with getdents64 records starting at *poff, which is an opaque // position the filesystem advances. Returns bytes written, 0 at end of // directory. The inode must be locked. int getdents(struct inode*, char *out, int cap, uint64 *poff); // Mount table: a substitution applied during path resolution. Nothing on disk // records a mount, so unmounting is forgetting. void mountinit(void); // 1 if the filesystem on this device refuses writes -- a mounted snapshot. // Ask before mutating, not after: the create path panics on a failed dirlink. int fs_readonly(uint dev); int mount_add(uint dev, uint64 inum, uint fsdev, uint64 root, const char *path, const char *source, const char *type); // Walk the mount table for /proc/mounts. `path` and `source` need MAXPATH // bytes, `type` 16. Returns 0 for a live entry, -1 for an empty slot. int mount_get(int i, char *path, char *source, char *type, uint *fsdev); int mount_del_by_root(uint fsdev, uint64 root); struct inode* ialloc(uint, short); struct inode* iget(uint, uint64); struct inode* idup(struct inode*); void iinit(void); void ilock(struct inode*); void iput(struct inode*); void iunlock(struct inode*); void iunlockput(struct inode*); void iupdate(struct inode*); int namecmp(const char*, const char*); struct inode* namei(char*); struct inode* nameiparent(char*, char*); struct inode* nameifrom(struct inode*, char*); struct inode* nameiparentfrom(struct inode*, char*, char*); int readi(struct inode*, int, uint64, uint64, uint); void stati(struct inode*, struct stat*); int writei(struct inode*, int, uint64, uint64, uint); void itrunc(struct inode*); void itrunc_to(struct inode*, uint64); unsigned int BKDRHash(char * str); uint tree_expand(struct inode *ip, uint off); void tree_search(struct inode* dp, char* name, uint* poff, struct dirent* de,uint add); void build_dir_tree(struct inode* dp,uint64 pinum); struct inode* divesymlink(struct inode *); void readsb(int dev, struct superblock *sb); // pci.c -- bus enumeration. Drivers that need to *find* their hardware use // these; drivers for things that have lived at a fixed address since 1984 // (the PIC, the PIT, the legacy IDE ports) do not. struct pci_dev; struct pci_driver; void pci_init(void); uint32 pci_read32(uint8, uint8, uint8, uint8); uint16 pci_read16(uint8, uint8, uint8, uint8); uint8 pci_read8(uint8, uint8, uint8, uint8); void pci_write32(uint8, uint8, uint8, uint8, uint32); void pci_enable(struct pci_dev *, uint16 bits); uint8 pci_find_cap(struct pci_dev *, uint8 id); int pci_msi_enable(struct pci_dev *, int vector, int cpu); int pci_count(void); struct pci_dev *pci_get(int); struct pci_dev *pci_find_class(uint8 class, uint8 subclass); struct pci_dev *pci_find_id(uint16 vendor, uint16 device); void pci_bind(const struct pci_driver *); int pci_info(char *buf, int cap); // drivers/registry.c -- the one list of every driver in the kernel. void pci_bind_all(void); // xhci.c — `echo u > /proc/sysrq` void xhci_dump(void); // ide.c void ideinit(void); void ideintr(void); void iderw(struct buf*); // For probes, not for reads: iderw() panics on an absent drive, so anything // that is guessing whether a disk exists has to ask here first. int ide_disk_present(int dev); // ATA FLUSH CACHE: block until the drive has committed its write cache. int ideflush(int dev); // fs/schfs/kmount.c -- SchFS. // // Objects are named by 64-bit object id, not by a path: resolution is the // caller's business, so these compose with a VFS rather than assuming one. // Every schfs_k* that changes anything is one transaction and is durable by the // time it returns. struct schfs_inode; // Mounting. Slot 0 is the root filesystem, mounted by proc.c before pid 1 has a // working directory; schfs_kmount_dev takes any other SchFS disk and returns // the filesystem device number it landed on, or a negative errno. void schfs_kmount(uint bdev); int schfs_kmount_dev(uint bdev); int schfs_kumount(uint dev); int schfs_kmounted(void); // Read side. `dev` is a filesystem device number -- a live volume or a mounted // snapshot -- not the block device underneath it. uint64 schfs_klookup(uint dev, const char *path); long schfs_kread_dev(uint dev, uint64 ino, uint64 off, void *buf, unsigned n); int schfs_kstat_dev(uint dev, uint64 ino, struct schfs_inode *out); uint64 schfs_klookup_at_dev(uint dev, uint64 dir, const char *name, unsigned len); int schfs_kreaddir_dev(uint dev, uint64 dir, int (*fn)(uint64, unsigned short, const char *, unsigned, void *), void *arg); // Resume a directory walk at a tree key. The callback gets each entry's hash, // which is what getdents hands back to userland as its position. int schfs_kreaddir_from_dev(uint dev, uint64 dir, uint64 start_hash, int (*fn)(uint64, unsigned short, const char *, unsigned, uint64, void *), void *arg); uint64 schfs_kroot(uint dev); void schfs_lockinfo(int slot, int *locked, int *owner_pid, int *depth); int schfs_kinfo(char *buf, int cap); // Write side. Each is one transaction and is durable by the time it returns, // unless the caller has a bracket open -- see schfs_ktx_open. int schfs_kcreate(uint dev, uint64 dir, const char *name, unsigned namelen, unsigned short type, uint mode, unsigned short major, unsigned short minor, uint64 *out); long schfs_kwrite(uint dev, uint64 ino, uint64 off, const void *buf, unsigned n); int schfs_ktruncate(uint dev, uint64 ino, uint64 size); int schfs_kunlink(uint dev, uint64 dir, const char *name, unsigned namelen); int schfs_klink(uint dev, uint64 dir, const char *name, unsigned namelen, uint64 ino); int schfs_krename(uint dev, uint64 olddir, const char *oldname, unsigned oldlen, uint64 newdir, const char *newname, unsigned newlen); int schfs_ksetattr(uint dev, uint64 ino, const struct schfs_inode *in); int schfs_ksync(uint dev); int schfs_ksync_all(void); // Share src's data blocks with dst without copying. Both stay writable; the // first write to either copies only the run it touches. Behind FICLONE. int schfs_kreflink(uint dev, uint64 src, uint64 dst); // Attributes, and the index over them. See kernel/fs/schfs/attr.c. int schfs_kattr_set(uint dev, uint64 ino, const char *name, unsigned namelen, const void *val, unsigned vallen, unsigned short type); int schfs_kattr_get(uint dev, uint64 ino, const char *name, unsigned namelen, void *out, unsigned cap, unsigned *vallen); int schfs_kattr_del(uint dev, uint64 ino, const char *name, unsigned namelen); int schfs_kindex(uint dev, const char *name, unsigned namelen); int schfs_kquery(uint dev, const char *name, unsigned namelen, const void *val, unsigned vallen, uint64 *out, int cap); // Bracket a run of changes into one transaction group. Every schfs_k* call made // between open and close joins it and commits once, at the close; the bracket // nests, and holds the volume lock for its whole extent. Costly to leave open — // nothing else touches that volume meanwhile — so bracket a bounded piece of // work such as one write(2), not a whole syscall's worth of unrelated steps. // schfs_ktx_close returns the commit's status, or -1 if the caller had no // bracket open. void schfs_ktx_open(uint dev); int schfs_ktx_close(uint dev); // Volume counts for statfs. 0 if `dev` is a mounted SchFS volume (live or // snapshot) and the counts are filled in, -1 if it is not. int schfs_kstatfs(uint dev, uint64 *total, uint64 *freeblocks, uint64 *objects); // Pieces the VFS layer (fs/schfs/vfs.c) needs, where a directory entry and the // object it names are separate operations. The VFS keeps the link count. int schfs_kdirempty(uint dev, uint64 dir); int schfs_kalloc_object(uint dev, unsigned short type, uint mode, uint64 *out); int schfs_kdiradd(uint dev, uint64 dir, const char *name, unsigned len, uint64 ino, unsigned short type); int schfs_kdirdel(uint dev, uint64 dir, const char *name, unsigned len); int schfs_kfree(uint dev, uint64 ino); // A snapshot mounted read-only: the same block device with an older tree root. // The read path takes a device number so the VFS can address either view; there // is no write path, because a snapshot that could be written to would not be // one. See kernel/fs/schfs/kmount.c. // Returns the device number of the slot the snapshot landed in, or negative. int schfs_ksnap_mount(uint dev, const char *name, unsigned namelen); void schfs_ksnap_unmount(uint dev); uint64 schfs_ksnap_root(uint dev); // ramdisk.c uint uptime(void); // procfs.c int procread(struct inode*, uint64, char*, int); int procwrite(struct inode*, uint64, char*, int); // Shared with the /proc filesystem (kernel/fs/procfs.c): one owns the // namespace, the other the content. int proc_gen_global(int id, char *buf, int cap); int proc_write_sysrq(int user_src, uint64 src, uint n); void procfs_mount(void); void devfs_mount(void); // input.c -- raw evdev event devices void inputinit(void); int inputread(struct inode*, uint64, char*, int); int inputwrite(struct inode*, uint64, char*, int); int inputready(struct inode*); void inputclose(struct inode*); void inputopen(struct inode*); int input_ioctl(struct inode*, int request, addr_t arg); int input_grab(int dev, int on); int input_is_grabbed(int dev); void input_report(int dev, int type, int code, int value); // An absolute pointing device (usb-tablet). Coordinates are already in screen // pixels; see input_mouse_abs in kernel/dev/input.c for why this is separate // from the relative path. void input_mouse_abs(int x, int y, int buttons); void mouse_set_abs(int x, int y, int buttons); void input_sync(int dev); int input_ready(int dev); void input_ps2_scancode(int raw, int e0); void input_key(int hid_usage, int down); int input_hid_keycode(int hid_usage); void input_mouse(int dx, int dy, int buttons); void ramdiskinit(void); void ramdiskrw(struct buf*); int ramdisk_present(void); // usb_storage.c void usb_storage_rw(struct buf*); int usb_storage_present(void); // SCSI SYNCHRONIZE CACHE: the same barrier as ideflush, over bulk transport. int usb_storage_flush(void); void usb_storage_dump(void); // `echo s > /proc/sysrq` // ioapic.c void ioapicenable(int irq, int cpu); void ioapicroute(int irq, int cpu, int level_low); void ioapicmask(int irq); extern uchar ioapicid; void ioapicinit(void); // dma.c — see include/dma.h for the full contract void dma_init(void*, void*); void *dma_alloc(uint64 size, uint64 align); void dma_free(void*, uint64 size); addr_t dma_phys(const void*); uint64 dma_free_bytes(void); uint64 dma_total_bytes(void); // kalloc.c char* kalloc(void); uint64 kalloc_free_bytes(void); void kfree(char*); void kinit1(void*, void*); void kinit2(void); // kbd.c void kbdintr(void); // mouse.c void mouseinit(void); void mouseintr(void); void mouse_inject(int dx, int dy, int buttons); int get_mouse_x(void); int get_mouse_y(void); uint8 get_mouse_buttons(void); // lapic.c void cmostime(struct rtcdate *r); // time.c -- wall-clock time, from the RTC read once at boot plus uptime. void time_init(void); void wallclock(uint64 *sec, uint64 *usec); extern uint64 boot_epoch; int cpunum(void); extern volatile uint* lapic; void lapiceoi(void); void lapicinit(void); void lapicstartap(uchar, uint); void microdelay(int); // log.c void initlog(int dev); void log_write(struct buf*); void begin_op(); void end_op(); // mp.c extern int ismp; void mpinit(void); // pipe.c int pipealloc(struct file**, struct file**); void pipeclose(struct pipe*, int); int piperead(struct pipe*, char*, int); int pipewrite(struct pipe*, char*, int); // net/unix.c struct socket; void sockinit(void); struct socket* socknew(int domain, int type, int proto); void sockclose(struct socket*); void sockdup(struct socket*); int sockread(struct socket*, char*, int); int sockwrite(struct socket*, char*, int); int sockbind(struct socket*, const char *path); int socklisten(struct socket*, int backlog); struct socket* sockaccept(struct socket*); int sockconnect(struct socket*, const char *path); int sockreadable(struct socket*); int sock_hup(struct socket*); int sock_shutdown(struct socket*, int how); int sockpair(struct socket **a, struct socket **b); const char* sockname(struct socket*); // AF_INET addressing. Separate from sockbind/sockconnect because the families // do not name the same kind of thing; `addr` and `port` are network order. int sockrecvfrom(struct socket*, char *buf, int n, uint *addr, ushort *port); int socksendto(struct socket*, char *buf, int n, uint addr, ushort port); int sockbind_in(struct socket*, uint addr, ushort port); int sockconnect_in(struct socket*, uint addr, ushort port); int sock_domain(struct socket*); // Socket options. The syscall layer does the user-memory marshalling and these // do the work, because struct socket's fields live in kernel/net/unix.c and the // syscall layer deliberately cannot see them. int sockopt_set(struct socket*, int level, int name, int val); int sockopt_get(struct socket*, int level, int name, int *out); // net/inet.c -- AF_INET on lwIP's raw API. Called only from net/unix.c, which // is where the per-domain dispatch lives. void inet_init(void); void* inet_new(int type, int proto); int inet_bind(void *h, uint addr, ushort port); int inet_listen(void *h, int backlog); int inet_setopt(void *h, int level, int name, int val); void* inet_accept(void *h); int inet_connect(void *h, uint addr, ushort port); int inet_read(void *h, char *buf, int n); int inet_write(void *h, char *buf, int n); int inet_recvfrom(void *h, char *buf, int n, uint *addr, ushort *port); int inet_sendto(void *h, char *buf, int n, uint addr, ushort port); int inet_readable(void *h); int inet_hup(void *h); void inet_close(void *h); // proc.c void exit(int status); // Start a kernel thread: a process that runs `fn` in kernel mode forever. It // has no files, no cwd and no parent, and must never return. Returns the pid. int kthread_create(void (*fn)(void), const char *name); int fork(void); int growproc(int64); int kill(int); int sigsend_pgrp(int pgid, int sig); int proc_pgid(int pid); int proc_setpgid(int pid, int pgid); int proc_setsid(void); int sigsend(int pid, int sig); int proc_exists(int); int proc_num(void); struct procinfo; int proc_snapshot(int idx, struct procinfo *out); int proc_snapshot_pid(int pid, struct procinfo *out); void pinit(void); void procdump(void); void scheduler(void) __attribute__((noreturn)); void sched(void); void sleep(void*, struct spinlock*); int wait(void); int waitpid(int, int*, int); void wakeup(void*); void yield(void); void sinit(void); void kstack_check_canary(const char *where); // swtch.S void swtch(struct context**, struct context*); // spinlock.c void acquire(struct spinlock*); void getcallerpcs(void*, addr_t*); void getstackpcs(addr_t*, addr_t*); int holding(struct spinlock*); void initlock(struct spinlock*, char*); void release(struct spinlock*); void pushcli(void); void popcli(void); // sleeplock.c void acquiresleep(struct sleeplock*); void releasesleep(struct sleeplock*); int holdingsleep(struct sleeplock*); void initsleeplock(struct sleeplock*, char*); // string.c int memcmp(const void*, const void*, uint64); void* memmove(void*, const void*, uint64); void* memset(void*, int, uint64); char* safestrcpy(char*, const char*, int); int strlen(const char*); int strnlen(const char*, int); int strcmp(const char*, const char*); int strncmp(const char*, const char*, uint64); char* strncpy(char*, const char*, int); // syscall.c void syscall(struct trapframe *); void syscallinit(void); void deliver_signals(struct trapframe *); int argint(int, int*); int argptr(int, char**, int); int argstr(int, char**); int argaddr(int, addr_t*); int fetchaddr(addr_t, addr_t*); int fetchstr(addr_t, char**); int fetchint(addr_t, int*); // trap.c void idtinit(void); extern uint ticks; void tvinit(void); extern struct spinlock tickslock; // uart.c void uartearlyinit(void); void uartinit(void); void uartintr(void); void uartputc(int); // Drawing, the text console and the framebuffer geometry are NOT declared // here: they live in include/gfx.h, and the display drivers that back them in // include/display.h. Declaring vga_fill_rect() here is what let the crash // screen draw on one named driver. // fb.c -- /dev/fb0, major 2. void fbclose(struct inode*); // Called from sys_mmap: a mapping of /dev/fb0 is a licence to draw with no // further syscalls, so it is the last the kernel hears about it. void fb_mapped(void); int fbread(struct inode*, uint64, char*, int); int fbwrite(struct inode*, uint64, char*, int); int fb_ioctl(int request, addr_t arg); // dev/disk.c -- /dev/hdN, raw seekable access to a whole disk. The minor is the // IDE device number. Writes bypass every filesystem above them. int diskread(struct inode*, uint64, char*, int); int diskwrite(struct inode*, uint64, char*, int); // Network *drivers* are not declared here at all. They are reached through the // netdev class in include/netdev.h -- a driver publishes a vtable and nothing // above it ever learns its name. Declaring e1000_transmit() here was what let // the protocol stack call it directly. // net/net.c -- the TCP/IP stack (lwIP). netinit starts the network thread; // net_is_up reports whether an address has been obtained. void netinit(void); int net_is_up(void); int net_is_ready(void); void net_wake_counts(uint64 *rx, uint64 *tick); int net_info(char *buf, int cap); // net/lwip/ -- the port layer and the Ethernet interfaces, called only from // net.c. One netif per bound network device; the index is that interface's. void lwip_sys_init(void); void lwip_sys_lock(void); void lwip_sys_unlock(void); int net_eth_attach(struct device *); int net_eth_poll(void); int net_eth_count(void); void net_eth_counts(int i, uint64 *rx, uint64 *tx); struct netif *net_eth_netif(int i); uint netif_addr4(struct netif *); uint netif_mask4(struct netif *); uint ntohl_u32(uint); uint htonl_u32(uint); // rtnetlink: turn one request into a whole dump. See kernel/net/netlink.c. int netlink_request(const char *req, uint reqlen, char *out, uint outcap); struct device *net_eth_device(int i); // registry.c // Platform devices, and the boot framebuffer. Takes the framebuffer address // the loader handed over, because bringing that up is the first thing it does. void platform_register_devices(uint64 fb_addr); // drivers/gpu/vga -- the VBE framebuffer the loader programmed. Called only // from registry.c; everything else draws through include/gfx.h. void vga_fb_init(uint64 phys_addr); uint64 vga_fb_phys(void); uint64 vga_fb_size(void); void e820_init(void); int acpi_init(void); // console.c / panic.c void cons_panic_mode(void); extern int cons_serial_only; void console_takeover(void); // vm.c void seginit(void); void kvmalloc(void); // Map device registers. Returns a kernel address for `size` bytes of physical // MMIO at `pa`, or 0 if the window is exhausted. Use this rather than P2V for // anything that came out of a BAR: firmware may place a 64-bit BAR far above // the 4 GB the direct map covers. void* ioremap(uint64 pa, uint64 size); pml4e_t* setupkvm(void); char* uva2ka(pml4e_t*, char*); addr_t allocuvm(pml4e_t*, uint64, uint64); addr_t deallocuvm(pml4e_t*, uint64, uint64); void freevm(pml4e_t*); void inituvm(pml4e_t*, char*, uint64); int loaduvm(pml4e_t*, char*, struct inode*, uint64, uint64); pml4e_t* copyuvm(pml4e_t*, uint64); void switchuvm(struct proc*); void switchkvm(void); int pipe_poll(struct pipe*, int readable, int writable); int console_input_ready(int minor); int copyin(pml4e_t*, addr_t, void*, uint64); int copyout(pml4e_t*, addr_t, void*, uint64); void vm_unmap_range(pml4e_t*, uint64, uint64); // Unmap without freeing: for pages this process does not own — device memory // and shared segments. vm_unmap_range() would hand them to kfree. void vm_unmap_norelease(pml4e_t*, uint64, uint64); // Shared memory (sys_mem.c). shm_ref/shm_detach maintain the attachment // count that decides when a segment's pages go back. void shm_ref(int id); void shm_detach(int id); void proc_release_shared(struct proc *); int copy_vma_range(pml4e_t *dstpgdir, pml4e_t *srcpgdir, uint64 va, uint64 length, int is_fb); void clearpteu(pml4e_t *pgdir, char *uva); #define NELEM(x) (sizeof(x)/sizeof((x)[0]))