EPOLLET reported a descriptor again only after a scan had seen it not ready. Xorg watches its clients edge-triggered; when it read part of a client's requests and more arrived before its next scan, the socket never looked not-ready, so it was never reported again -- X waiting on the client, the client waiting on X. One CPU almost never hit it; on a 16-thread laptop the desktop froze within seconds: Window Maker's menu stuck, xterm never mapped, text gone, and Caps Lock dead because X owns the LED. Now a still-ready descriptor is reported again whenever anything has signalled readiness since its last report (poll_generation()), which may be spurious, as EPOLLET permits, and is never missed. Reproduced and verified with QEMU at -smp 8: the root menu works and xterm runs cmatrix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
820 lines
37 KiB
C
820 lines
37 KiB
C
#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;
|
|
extern uint64 boot_tsc;
|
|
void monotonic(uint64 *sec, uint64 *usec);
|
|
int proc_write_schinit(int user_src, uint64 src, uint n);
|
|
int proc_wedge(const char *name);
|
|
void pciuser_init(void);
|
|
int pciuser_ioctl(uint64 request, uint64 argp);
|
|
int pciuser_read(struct inode*, uint64, char*, int);
|
|
int pciuser_write(struct inode*, uint64, char*, int);
|
|
int pciuser_mmio_ok(uint64 phys, uint64 len);
|
|
void pciuser_release_all(struct proc*);
|
|
int pciuser_owner_pid(int idx);
|
|
int proc_reincarnate(int status);
|
|
int sig_default_stops(int);
|
|
struct mm* mm_alloc(void);
|
|
void mm_get(struct mm*);
|
|
void mm_put(struct mm*);
|
|
int mm_shared(struct mm*);
|
|
void mm_lock(struct mm*);
|
|
void mm_unlock(struct mm*);
|
|
struct files* files_alloc(void);
|
|
void files_get(struct files*);
|
|
void files_put(struct files*);
|
|
void files_lock(void);
|
|
void files_unlock(void);
|
|
struct sighand* sighand_alloc(void);
|
|
void sighand_get(struct sighand*);
|
|
void sighand_put(struct sighand*);
|
|
struct proc* proc_leader(struct proc*);
|
|
int proc_thread_count(int);
|
|
void thread_exit(void) __attribute__((noreturn));
|
|
int futex_wake_key(void*, int);
|
|
int futex_op(addr_t, int, int, addr_t, addr_t, int);
|
|
int clone_proc(uint64, uint64, uint64, uint64, uint64);
|
|
void proc_exec_threads(void);
|
|
void proc_exec_done(void);
|
|
void thread_reaper_start(void);
|
|
void vm_defer_tick(void);
|
|
void mm_release_shared(struct mm*);
|
|
void proc_stop(int);
|
|
int sig_attention(struct proc*);
|
|
int sig_blocking_check(void);
|
|
void proc_cputime(uint64 *user, uint64 *sys);
|
|
// kernel/dev/procfs.c: /proc/strace
|
|
int strace_wanted(const char *name);
|
|
int proc_write_strace(int user_src, uint64 src, uint n);
|
|
void proc_kill_init(void);
|
|
extern uint init_generation;
|
|
void cprintf(char*, ...);
|
|
// Boot chatter: always in the klog, on the screen only when boot_verbose.
|
|
// See the note above kinfo() in kernel/core/console.c for which is which.
|
|
void kinfo(char*, ...);
|
|
extern int boot_verbose;
|
|
extern int boot_debug;
|
|
|
|
// 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*);
|
|
|
|
// sys_event.c: eventfd, timerfd, signalfd and epoll, as files
|
|
// kernel/fs/lock.c
|
|
int lock_flock(struct file *f, int op);
|
|
int lock_fcntl(struct file *f, int cmd, addr_t arg);
|
|
void lock_close_fd(struct file *f);
|
|
void lock_close_file(struct file *f);
|
|
void lock_exit(int pid);
|
|
struct evobj;
|
|
void ev_close(struct evobj *);
|
|
int ev_poll(struct file *, int events);
|
|
int ev_read(struct file *, char *addr, int n);
|
|
int ev_write(struct file *, char *addr, int n);
|
|
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);
|
|
// A socket node for AF_UNIX bind(2), and unlink by path for the failure that
|
|
// follows it. See the notes on each in kernel/sys/sys_fs.c.
|
|
struct inode* create_sock(char *path);
|
|
int unlink_path(char *path);
|
|
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);
|
|
void pci_disable(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);
|
|
int procfs_mount(const char *target);
|
|
int devfs_mount(const char *target);
|
|
|
|
// 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);
|
|
uint64 ramdisk_blocks(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/core/dma.h for the full contract
|
|
void dma_init(void*, void*);
|
|
|
|
// watch.c -- hardware watchpoints on DR0-DR3. See docs/architecture/debugging.md, D4.
|
|
struct trapframe;
|
|
void watch_init(void);
|
|
void watch_sync(void);
|
|
int watch_trap(struct trapframe *tf);
|
|
|
|
// netuser.c -- /dev/netdev, an interface backed by a process. See netuser.h.
|
|
void netuser_init(void);
|
|
int netuser_read(struct inode*, uint64, char*, int);
|
|
int netuser_write(struct inode*, uint64, char*, int);
|
|
int netuser_ready(struct inode*);
|
|
int netuser_ioctl(uint64 request, uint64 argp);
|
|
void netuser_release_all(struct proc*);
|
|
|
|
// dbg.c -- /dev/dbg, the introspection registry. See include/core/dbg.h.
|
|
int dbg_read(struct inode*, uint64, char*, int);
|
|
void dbg_request_dump(void);
|
|
void dbg_thread_start(void);
|
|
int dbg_write(struct inode*, uint64, char*, int);
|
|
|
|
// klog.c -- the kernel log that outlives the kernel. See include/core/klog.h.
|
|
void klog_init(void *base, uint64 bytes);
|
|
void klog_putc(int c);
|
|
int klog_read(uint64 off, char *dst, int n);
|
|
uint64 klog_len(void);
|
|
uint64 klog_prev_bytes(void);
|
|
int klog_recovered(void);
|
|
int klog_render_syslog(char *dst, int cap);
|
|
|
|
// ksym.c -- naming a kernel address. See docs/architecture/debugging.md, D0.
|
|
int ksym_ready(void);
|
|
const char* ksym_lookup(uint64 addr, uint64 *off);
|
|
int ksym_fmt(char *buf, int cap, uint64 addr);
|
|
void *dma_alloc(uint64 size, uint64 align);
|
|
void dma_free(void*, uint64 size);
|
|
addr_t dma_phys(const void*);
|
|
// /dev/random and /dev/urandom. Seeded once at boot; see kernel/dev/cdev.c.
|
|
void random_init(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 kinit_high(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);
|
|
extern volatile int panic_stop; // kernel/core/panic.c
|
|
void lapic_nmi_others(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);
|
|
// The same two, carrying descriptors. See kernel/net/unix.c.
|
|
int sockread_fds(struct socket*, char*, int, struct file**, int*);
|
|
int sockwrite_fds(struct socket*, char*, int, struct file**, int);
|
|
int sockbind(struct socket*, const char *path, uint dev, uint64 ino);
|
|
int socklisten(struct socket*, int backlog);
|
|
struct socket* sockaccept(struct socket*);
|
|
int sockconnect(struct socket*, uint dev, uint64 ino);
|
|
int sockbind_abstract(struct socket*, uint64 key);
|
|
int sockname_unix(struct socket*, int peer, char *path);
|
|
int sockname_inet(struct socket*, int peer, uint *addr, ushort *port);
|
|
#define SOCK_ABSTRACT_DEV 0xFFFFFFFFu // no filesystem has it; see unix.c
|
|
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*);
|
|
int sock_peercred(struct socket*, int*, uint*, uint*);
|
|
int sock_passcred(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_name(void *h, int peer, uint *addr, ushort *port);
|
|
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);
|
|
int inet_shutdown(void *h, int how);
|
|
int file_describe(struct file *f, char *buf, int cap);
|
|
int proc_fd_mask(int pid, uint64 mask[2]);
|
|
int proc_fd_link(int pid, int fd, char *buf, int cap);
|
|
const char *ev_kind_name(struct evobj *o);
|
|
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);
|
|
void alarm_arm(struct proc *p, uint after, uint every);
|
|
uint alarm_left(struct proc *p);
|
|
void alarm_thread_start(void);
|
|
void poll_kick(void);
|
|
int poll_wait_tick(void);
|
|
int sig_default_kills(int sig);
|
|
int proc_exists(int);
|
|
int proc_num(void);
|
|
struct procinfo;
|
|
int proc_snapshot(int idx, struct procinfo *out);
|
|
int proc_peek(int idx, struct procinfo *out); // no lock: GMMOS
|
|
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, uint64*, uint64*);
|
|
void wakeup(void*);
|
|
void yield(void);
|
|
int sched_slice_expired(void);
|
|
void sched_init_proc(struct proc*, int);
|
|
void sched_set_nice(struct proc*, int);
|
|
int proc_setnice(int, int);
|
|
int proc_getnice(int, int*);
|
|
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/gfx.h, and the display drivers that back them in
|
|
// include/gfx/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);
|
|
|
|
// AudioEngine and /dev/dsp. kernel/dev/audio.c, include/dev/audio.h.
|
|
int dspread(struct inode*, uint64, char*, int);
|
|
int dspwrite(struct inode*, uint64, char*, int);
|
|
void dspclose(struct inode*);
|
|
int dsp_ioctl(int request, uint64 argp);
|
|
void audio_wait(struct device *);
|
|
|
|
// The crash screen, on displays other than the console's. kernel/dev/device.c.
|
|
void display_panic_present(void);
|
|
// Push the console's newly-drawn rows to a second display, if the kernel
|
|
// drives one. kernel/dev/device.c.
|
|
void display_mirror_console(void);
|
|
uint64 display_scanout_phys(void);
|
|
// Where a userspace display driver said it is scanning out, and which kernel
|
|
// driver it took the device from. kernel/dev/pciuser.c.
|
|
int pciuser_panic_surface(int idx, uint64 *paddr, int *w, int *h,
|
|
uint32 *stride,
|
|
const struct pci_driver **drv);
|
|
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/dev/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_resolv(char *, int);
|
|
int net_eth_attach(struct device *);
|
|
int net_eth_detach(struct device *);
|
|
int net_detach(struct device *);
|
|
int net_attach_late(struct device *);
|
|
int net_eth_poll(void);
|
|
void net_eth_link_check(void);
|
|
uint poll_generation(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/gfx.h.
|
|
void vga_fb_init(uint64 phys_addr);
|
|
uint64 vga_fb_phys(void);
|
|
int vga_fb_restore(void); // 0 = there is no fallback
|
|
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);
|
|
void vm_walk_user(pml4e_t*, void (*)(void*, uint64, uint64, uint64), void*);
|
|
// 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);
|
|
char* shm_page(int id, int pg);
|
|
uint64 memfd_size(int id);
|
|
int memfd_truncate(int id, uint64 len);
|
|
int memfd_seals(int id, int add);
|
|
int dsp_writable(void);
|
|
// inotify (kernel/sys/sys_event.c), and the lookup it needs (sys_fs.c)
|
|
void inotify_event(uint dev, uint64 inum, uint32 mask, const char *name, uint32 cookie);
|
|
void inotify_file(struct file *f, uint32 mask);
|
|
uint32 inotify_cookie(void);
|
|
int ev_fionread(struct evobj *o);
|
|
int pipe_avail(struct pipe *p);
|
|
int vm_fault(addr_t va);
|
|
pte_t* walkpte(pml4e_t *pgdir, const void *va);
|
|
int path_identity(char *path, int follow, int onlydir, uint *dev, uint64 *inum);
|
|
int memfd_rw(int id, int user, uint64 buf, uint64 off, int n, int writing);
|
|
void shm_detach(int id);
|
|
|
|
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]))
|