SchOS/include/defs.h
Nexoniarz 20cc673a41 net: a network *class*, so the stack stops knowing what an e1000 is
The TCP/IP stack reached straight into the first driver anyone wrote.
net.c called e1000_present(), the lwIP glue was named netif_e1000.c and
called e1000_transmit(), and include/defs.h declared the driver's
internals to the whole kernel. The second NIC driver would therefore
have arrived with a near-identical copy of the glue file and a patch to
the protocol stack, which is the arrangement include/device.h was
written to prevent -- the e1000 even said so, with `.ops = 0, // no
class vtable yet; there is no net class`.

There is now. A driver publishes a struct netdev_ops -- transmit,
receive, mac, link_up -- and is finished: it never mentions lwIP, and
nothing above it learns its name. The display class was already this
shape; this is the same shape for networking, and the two now read the
same way in kernel/dev/device.c.

What that made possible immediately:

  - One netif per bound device instead of one per driver. lwIP's glue is
    netif_eth.c, it takes a struct device, and it holds up to NETIF_MAX
    interfaces. net.c attaches every network device it finds and starts
    DHCP on each, because which interface the network answers on is not
    ours to decide.
  - /proc/netif lists all of them, one block of fields each, with the
    interface name and the driver that claimed it.
  - The ARP self-test moved from the e1000 to the class. Nothing in it
    was chip-specific, and it is the first thing anyone will want to run
    against the next driver. Still `echo n > /proc/sysrq`.
  - Private headers live with their driver: e1000.h was in include/,
    where exactly one file could use it. The driver build now adds the
    driver's own directory to the include path.

The e1000 also drives more than one card. Its probe() refused the second
outright, because there was a single static instance -- so a machine
with two of them drove one and silently ignored the other. Instances are
an array now, and the instance is hung on dev->priv by a new optional
`attached` hook that the bus calls once the device exists. probe() runs
before device_add(), so there was previously nowhere to put it; that is
the whole reason the hook exists rather than a line inside probe.

QEMU gets an explicit NIC on every target (QEMU_NET). The default device
happens to be an e1000 on this machine type today, which is exactly the
sort of thing that stays true until a QEMU release changes it and the
network stops being exercised without anyone noticing.

Verified:
  - bootcheck -Net passes, 48 [ok], no failures -- including the moved
    self-test and two new checks that /proc/netif names both the
    interface and the driver behind it, which together say the stack
    went through the class rather than through a driver it was compiled
    to know.
  - Two NICs attached by hand (-device e1000 twice, second with its own
    MAC): eth0 and eth1 both attach, each reports its own MAC in
    /proc/netif, and each gets its own DHCP lease. Before this commit
    the same machine produced one interface.
  - make schfs-check still passes in full.

One trap worth recording: moving a header broke the build for anyone
with stale .d files -- make stopped with "No rule to make target
'include/e1000.h'". CFLAGS gains -MP, which emits a phony target per
header so a moved or deleted one can never wedge the build again.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 16:42:34 +02:00

613 lines
26 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);
extern int tty_fg_pgrp;
// 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(struct termios *);
void cons_set_termios(const struct termios *);
void cprintf(char*, ...);
void consoleintr(int(*)(void));
void panic(char*) __attribute__((noreturn));
// exec.c
int exec(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);
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);
// 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);
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*);
// 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 inet_bind(void *h, uint addr, ushort port);
int inet_listen(void *h, int backlog);
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 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);
// vga.c
void vga_init(uint64);
void vga_draw_pixel(int, int, uint32);
void vga_draw_char(int, int, uchar, uint32);
void vga_puts(int, int, char*, uint32);
void vga_putc(int, uint32);
void vga_clear(uint32);
void vga_screen_reset(void);
void vga_set_bg(uint32);
// The mode actually in use. The resolution is a boot-time choice now, so no
// caller may hardcode it.
uint32 vga_get_width(void);
uint32 vga_get_height(void);
// Crash-screen primitives: the panic handler lays itself out rather than
// printing. See kernel/core/panic.c.
int vga_ready(void);
void vga_dims(int *w, int *h);
int vga_char_w(void);
int vga_char_h(void);
// Framebuffer geometry. vga.c reads the VBE mode info, so it is the only
// thing that knows where the framebuffer is; nobody else may assume.
uint64 vga_fb_phys(void);
uint64 vga_fb_size(void);
uint32 vga_fb_stride(void);
uint32 vga_fb_bpp(void);
volatile uchar *vga_fb_ptr(void);
// 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_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);
struct device *net_eth_device(int i);
// registry.c
void platform_register_devices(void);
void e820_init(void);
int acpi_init(void);
void vga_fill_rect(int x, int y, int w, int h, uint32);
void vga_fill_circle(int cx, int cy, int r, uint32);
void vga_text_at(int, int, const char*, uint32);
void vga_draw_logo(int x, int y, uint32 color, uint32 bg);
void vga_logo_centered(int cy, uint32 color, uint32 bg);
// Cursor addressing and erasing, for the console's ANSI escape parser.
int vga_cols(void);
int vga_rows(void);
void vga_cursor_get(int *col, int *row);
void vga_cursor_set(int col, int row);
void vga_erase_display(int mode);
void vga_erase_line(int mode);
// 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(void);
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]))