The AF_INET blocking paths -- connect, accept, read and write -- woke on every timer tick and looked to see whether anything had changed. That is a wakeup per blocked socket per 10 ms whether or not the network did anything, and it answers late even when the data was already sitting in the buffer. What stood in the way was a lock mismatch, and it is written down in the file now because it is the reason this is not simply sleep on the socket: sleep() wants a spinlock, and the lwIP lock is a sleeplock -- it must be, since a call into lwIP can reach the driver and the driver can sleep. Nor can a waiter hold the lwIP lock while sleeping, or the network thread could never deliver the thing it is waiting for. So the wait is on a counter with a spinlock of its own. A waiter reads the counter while it still holds the lwIP lock, right after checking its condition, then releases and sleeps only if the counter has not moved. The lwIP callbacks bump it and wake the channel. No wakeup can be lost: anything that changes a condition does so after the reader's snapshot, and therefore bumps the counter after it too, and the sleeper re-checks under the spinlock before committing. One counter and one channel for all sixteen sockets rather than one each. Waking a socket with nothing to do costs a few instructions, and a per-socket channel would have to be torn down in close() against callbacks that can still arrive -- which is a use-after-free waiting to be written. tcp_sent is registered now, on both connected and accepted pcbs. It was not needed while writes woke on the clock; with this change a write blocked on a full send buffer would otherwise sleep with nobody left to wake it, because an acknowledgement is the only thing that frees space. Verified: bootcheck -Net passes, 38 [ok], no failures. It exercises the path that matters -- nettest blocks in read() until the host answers, and the host answers only after reading what the guest sent, so the wakeup comes from a callback and not from a tick that would have fired anyway. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
604 lines
26 KiB
C
604 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;
|
|
|
|
// 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 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_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);
|
|
// drivers/net/e1000 -- raw Ethernet. No protocol stack above it yet, so these
|
|
// are the whole interface: queue a frame, take a frame, and the MAC.
|
|
int e1000_transmit(const void *frame, int len);
|
|
int e1000_receive(void *out, int cap);
|
|
const unsigned char *e1000_mac(void);
|
|
int e1000_present(void);
|
|
|
|
// 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 interface, called only from net.c.
|
|
void lwip_sys_init(void);
|
|
void lwip_sys_lock(void);
|
|
void lwip_sys_unlock(void);
|
|
int net_e1000_attach(void);
|
|
int net_e1000_poll(void);
|
|
void net_e1000_counts(uint64 *rx, uint64 *tx);
|
|
struct netif *net_e1000_netif(void);
|
|
void e1000_selftest(void);
|
|
|
|
// 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]))
|