Every driver reached its registers with P2V(bar.base), which assumes the BAR sits inside the 0-4 GB the direct map covers. Under SeaBIOS it always does. Under OVMF it does not: firmware may place a 64-bit BAR anywhere, and it puts virtio-gpu's at about 768 GB. P2V produced an address with no translation behind it, so the driver's first register write took a page fault, then a double fault, then a triple fault and a reboot loop -- which, coming from a driver bind, reads as broken hardware rather than a missing mapping. ioremap() carries each region on a 1 GB page in PDPT entries 4-511, which were unused: the direct map occupies 0-3, and kpml4[0], kpml4[KERNBASE] and every process's PML4 all share that one table, so a mapping appears in every address space at once with no page table to allocate. The pages are cache-disabled and write-through for the same reason the top GB of the direct map is -- these are registers, and a write sitting in a cache line is a write the device never sees. A physical address below 4 GB returns the direct-map address unchanged, so the BIOS path, where every BAR is low, is byte for byte what it was. virtio-gpu now initialises under OVMF (1280x800, queue 64) instead of triple faulting. e1000 and xhci had the identical latent bug; xhci was already refusing to bind above 4 GB with a comment saying the mapping did not exist yet, and now it does. BIOS unchanged: bootcheck 108 [ok] with -Fb -Tty -Usb -Sock -Evdev -Ps, e1000 and xhci both binding. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
471 lines
18 KiB
C
471 lines
18 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, uint);
|
|
void brelse(struct buf*);
|
|
void bwrite(struct buf*);
|
|
|
|
// 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);
|
|
int mount_add(uint dev, uint64 inum, uint fsdev, uint64 root);
|
|
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);
|
|
|
|
// 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;
|
|
void schfs_kmount(uint dev);
|
|
int schfs_kmounted(void);
|
|
uint64 schfs_klookup(const char *path);
|
|
int schfs_kstat(uint64 ino, struct schfs_inode *out);
|
|
long schfs_kread(uint64 ino, uint64 off, void *buf, unsigned n);
|
|
int schfs_kreaddir(uint64 dir,
|
|
int (*fn)(uint64, unsigned short, const char *,
|
|
unsigned, void *),
|
|
void *arg);
|
|
int schfs_kinfo(char *buf, int cap);
|
|
|
|
int schfs_kcreate(uint64 dir, const char *name, unsigned namelen,
|
|
unsigned short type, uint mode,
|
|
unsigned short major, unsigned short minor, uint64 *out);
|
|
long schfs_kwrite(uint64 ino, uint64 off, const void *buf,
|
|
unsigned n);
|
|
int schfs_ktruncate(uint64 ino, uint64 size);
|
|
int schfs_kunlink(uint64 dir, const char *name, unsigned namelen);
|
|
int schfs_klink(uint64 dir, const char *name, unsigned namelen,
|
|
uint64 ino);
|
|
int schfs_krename(uint64 olddir, const char *oldname,
|
|
unsigned oldlen, uint64 newdir, const char *newname,
|
|
unsigned newlen);
|
|
int schfs_ksetattr(uint64 ino, const struct schfs_inode *in);
|
|
int schfs_ksync(void);
|
|
|
|
// 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.
|
|
uint64 schfs_klookup_at(uint64 dir, const char *name, unsigned len);
|
|
int schfs_kdirempty(uint64 dir);
|
|
int schfs_kalloc_object(unsigned short type, uint mode,
|
|
uint64 *out);
|
|
int schfs_kdiradd(uint64 dir, const char *name, unsigned len,
|
|
uint64 ino, unsigned short type);
|
|
int schfs_kdirdel(uint64 dir, const char *name, unsigned len);
|
|
int schfs_kfree(uint64 ino);
|
|
uint64 schfs_kroot(void);
|
|
|
|
// ramdisk.c
|
|
uint uptime(void);
|
|
|
|
// procfs.c
|
|
int procread(struct inode*, uint, char*, int);
|
|
int procwrite(struct inode*, uint, 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*, uint, char*, int);
|
|
int inputwrite(struct inode*, uint, 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);
|
|
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);
|
|
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*);
|
|
|
|
// proc.c
|
|
void exit(int status);
|
|
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);
|
|
// 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*, uint, char*, int);
|
|
int fbwrite(struct inode*, uint, char*, int);
|
|
int fb_ioctl(int request, addr_t arg);
|
|
// 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);
|
|
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]))
|