├── base ├── bin │ └── .gitkeep ├── dev │ └── .gitkeep ├── proc │ └── .gitkeep ├── tmp │ └── .gitkeep └── root │ ├── music.qoa │ ├── kodim23.qoi │ ├── banner.txt │ └── invictus.txt ├── common ├── libgen.h ├── strings.h ├── stdlib.h ├── math.h ├── stdio.h ├── libgen.c ├── strings.c ├── panic.h ├── string.h ├── ctype.h ├── calendar.h ├── math.c ├── stdlib.c └── extra.h ├── kernel ├── console │ ├── console.h │ ├── console.c │ ├── screen │ │ ├── screen.c │ │ └── screen.h │ ├── font.h │ ├── system_console.c │ ├── serial_console.c │ └── private.h ├── api │ ├── linux │ │ ├── vt.h │ │ ├── major.h │ │ ├── keyboard.h │ │ └── kd.h │ ├── stdio.h │ ├── sys │ │ ├── time.h │ │ ├── prctl.h │ │ ├── limits.h │ │ ├── uio.h │ │ ├── times.h │ │ ├── un.h │ │ ├── sysmacros.h │ │ ├── poll.h │ │ ├── socket.h │ │ ├── wait.h │ │ ├── ioctl.h │ │ ├── mman.h │ │ ├── types.h │ │ ├── reboot.h │ │ ├── utsname.h │ │ ├── sysinfo.h │ │ └── stat.h │ ├── sound.h │ ├── unistd.h │ ├── fcntl.h │ ├── asm │ │ └── ldt.h │ ├── time.h │ ├── sched.h │ ├── err.h │ ├── dirent.h │ └── elf.h ├── drivers │ ├── rtc.h │ ├── drivers.h │ ├── hid │ │ ├── hid.h │ │ ├── ps2.h │ │ ├── ps2.c │ │ └── keyboard.c │ ├── virtio │ │ ├── virtio_config.h │ │ ├── virtio.h │ │ ├── virtio_blk.h │ │ ├── virtio_queue.h │ │ └── virtio_pci.h │ ├── serial.h │ ├── graphics │ │ ├── graphics.h │ │ ├── multiboot.c │ │ └── bochs.c │ ├── drivers.c │ ├── pit.c │ ├── pci.h │ └── rtc.c ├── memory │ ├── memory.c │ ├── private.h │ └── kmalloc.c ├── panic.h ├── ksyms.S ├── kmsg.h ├── socket.h ├── lock.h ├── time.h ├── containers │ ├── vec.h │ ├── mpsc.h │ └── vec.c ├── interrupts │ ├── asm.S │ ├── i8259.c │ └── interrupts.h ├── fs │ ├── proc │ │ └── private.h │ ├── private.h │ ├── path.h │ └── initrd.c ├── sched.h ├── linker.ld ├── acpi.h ├── device │ └── device.h ├── kmsg.c ├── system.h ├── gdt.h ├── ksyms.c ├── safe_string.h ├── cmdline.c ├── gdt.c ├── lock.c └── time.c ├── docs ├── imgs │ ├── eyes.png │ ├── imgview.png │ └── mandelbrot.png ├── cmdline.md └── gallery.md ├── imgs └── screenshot.png ├── .clang-format ├── .vscode └── settings.json ├── userland ├── sync.c ├── lib │ ├── errno.h │ ├── sys │ │ ├── auxv.h │ │ ├── times.h │ │ ├── ioctl.h │ │ ├── sysinfo.h │ │ ├── utsname.h │ │ ├── poll.h │ │ ├── times.c │ │ ├── utsname.c │ │ ├── mount.h │ │ ├── sysinfo.c │ │ ├── poll.c │ │ ├── prctl.h │ │ ├── uio.h │ │ ├── wait.h │ │ ├── mount.c │ │ ├── ioctl.c │ │ ├── uio.c │ │ ├── wait.c │ │ ├── mman.h │ │ ├── prctl.c │ │ ├── socket.h │ │ ├── time.h │ │ ├── select.c │ │ ├── time.c │ │ ├── mman.c │ │ ├── socket.c │ │ ├── stat.h │ │ ├── select.h │ │ └── stat.c │ ├── panic.h │ ├── string.h │ ├── termios.h │ ├── errno.c │ ├── fcntl.h │ ├── sched.h │ ├── panic.c │ ├── dirent.h │ ├── stdlib.h │ ├── stdio.h │ ├── termios.c │ ├── sched.c │ ├── fcntl.c │ ├── signal.h │ ├── time.h │ ├── private.h │ ├── stdio.c │ ├── pthread.h │ ├── unistd.h │ ├── dirent.c │ ├── crt0.c │ ├── signal.c │ └── asm.S ├── clear.c ├── halt.c ├── poweroff.c ├── reboot.c ├── env.c ├── pwd.c ├── moused.h ├── echo.c ├── date.c ├── mv.c ├── mkfifo.c ├── rm.c ├── rmdir.c ├── mkdir.c ├── sleep.c ├── touch.c ├── readlink.c ├── ln.c ├── fib.c ├── kill.c ├── cal.c ├── cat.c ├── cp.c ├── getty.c ├── mknod.c ├── init-test.c ├── mount.c ├── ps.c ├── uname.c ├── wc.c ├── grep.c ├── stat.c ├── Makefile └── hexdump.c ├── disk └── boot │ └── grub │ └── grub.cfg ├── .gitignore ├── run_tests.sh ├── .clang-tidy ├── LICENSE ├── .github └── workflows │ └── ci.yml ├── Makefile ├── README.md └── run.sh /base/bin/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /base/dev/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /base/proc/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /base/tmp/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /common/libgen.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | char* basename(char* path); 4 | -------------------------------------------------------------------------------- /kernel/console/console.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void console_init(void); 4 | -------------------------------------------------------------------------------- /base/root/music.qoa: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mosmeh/yagura/HEAD/base/root/music.qoa -------------------------------------------------------------------------------- /docs/imgs/eyes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mosmeh/yagura/HEAD/docs/imgs/eyes.png -------------------------------------------------------------------------------- /imgs/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mosmeh/yagura/HEAD/imgs/screenshot.png -------------------------------------------------------------------------------- /base/root/kodim23.qoi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mosmeh/yagura/HEAD/base/root/kodim23.qoi -------------------------------------------------------------------------------- /docs/imgs/imgview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mosmeh/yagura/HEAD/docs/imgs/imgview.png -------------------------------------------------------------------------------- /docs/imgs/mandelbrot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mosmeh/yagura/HEAD/docs/imgs/mandelbrot.png -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: LLVM 4 | IndentWidth: 4 5 | PointerAlignment: Left 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | "**/*.o": true, 4 | "**/*.d": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /kernel/api/linux/vt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define VT_ACTIVATE 0x5606 4 | #define VT_WAITACTIVE 0x5607 5 | -------------------------------------------------------------------------------- /userland/sync.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | sync(); 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /kernel/api/stdio.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define SEEK_SET 0 4 | #define SEEK_CUR 1 5 | #define SEEK_END 2 6 | -------------------------------------------------------------------------------- /kernel/drivers/rtc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | time_t rtc_now(void); 6 | -------------------------------------------------------------------------------- /userland/lib/errno.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | extern _Thread_local int errno; 6 | -------------------------------------------------------------------------------- /kernel/api/sys/time.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct linux_timeval { 4 | long tv_sec; 5 | long tv_usec; 6 | }; 7 | -------------------------------------------------------------------------------- /userland/lib/sys/auxv.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | unsigned long getauxval(unsigned long type); 6 | -------------------------------------------------------------------------------- /userland/lib/sys/times.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | clock_t times(struct tms* buf); 6 | -------------------------------------------------------------------------------- /userland/lib/sys/ioctl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | int ioctl(int fd, unsigned cmd, ...); 6 | -------------------------------------------------------------------------------- /userland/lib/sys/sysinfo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | int sysinfo(struct sysinfo* info); 6 | -------------------------------------------------------------------------------- /userland/lib/sys/utsname.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | int uname(struct utsname* buf); 6 | -------------------------------------------------------------------------------- /kernel/api/sys/prctl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define PR_SET_NAME 15 /* Set process name */ 4 | #define PR_GET_NAME 16 /* Get process name */ 5 | -------------------------------------------------------------------------------- /userland/lib/sys/poll.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | int poll(struct pollfd* fds, nfds_t nfds, int timeout); 6 | -------------------------------------------------------------------------------- /kernel/drivers/drivers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef struct multiboot_info multiboot_info_t; 4 | 5 | void drivers_init(const multiboot_info_t*); 6 | -------------------------------------------------------------------------------- /disk/boot/grub/grub.cfg: -------------------------------------------------------------------------------- 1 | set timeout=0 2 | set default="0" 3 | 4 | menuentry "yagura" { 5 | multiboot /boot/kernel 6 | module /boot/initrd 7 | } 8 | -------------------------------------------------------------------------------- /userland/clear.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(void) { 5 | printf("\x1b[H\x1b[2J"); 6 | return EXIT_SUCCESS; 7 | } 8 | -------------------------------------------------------------------------------- /userland/lib/sys/times.c: -------------------------------------------------------------------------------- 1 | #include "times.h" 2 | #include 3 | 4 | clock_t times(struct tms* buf) { 5 | return __syscall_return(SYSCALL1(times, buf)); 6 | } 7 | -------------------------------------------------------------------------------- /common/strings.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | int strcasecmp(const char* s1, const char* s2); 6 | int strncasecmp(const char* s1, const char* s2, size_t n); 7 | -------------------------------------------------------------------------------- /kernel/api/sound.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | enum { 4 | SOUND_GET_SAMPLE_RATE, 5 | SOUND_SET_SAMPLE_RATE, 6 | SOUND_GET_ATTENUATION, 7 | SOUND_SET_ATTENUATION 8 | }; 9 | -------------------------------------------------------------------------------- /userland/lib/panic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define ASSERT_OK(result) ASSERT((result) >= 0) 6 | #define ASSERT_ERR(result) ASSERT((result) < 0) 7 | -------------------------------------------------------------------------------- /userland/lib/sys/utsname.c: -------------------------------------------------------------------------------- 1 | #include "utsname.h" 2 | #include 3 | 4 | int uname(struct utsname* buf) { 5 | return __syscall_return(SYSCALL1(uname, buf)); 6 | } 7 | -------------------------------------------------------------------------------- /userland/lib/sys/mount.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | int mount(const char* source, const char* target, const char* filesystemtype, 4 | unsigned long mountflags, const void* data); 5 | -------------------------------------------------------------------------------- /userland/lib/sys/sysinfo.c: -------------------------------------------------------------------------------- 1 | #include "sysinfo.h" 2 | #include 3 | 4 | int sysinfo(struct sysinfo* info) { 5 | return __syscall_return(SYSCALL1(sysinfo, info)); 6 | } 7 | -------------------------------------------------------------------------------- /base/root/banner.txt: -------------------------------------------------------------------------------- 1 | _ _ __ _ __ _ _ _ _ __ __ _ 2 | | | | |/ _` |/ _` | | | | '__/ _` | 3 | | |_| | (_| | (_| | |_| | | | (_| | 4 | \__, |\__,_|\__, |\__,_|_| \__,_| 5 | |___/ |___/ 6 | -------------------------------------------------------------------------------- /userland/lib/string.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | char* strdup(const char* src); 6 | 7 | char* strerror(int errnum); 8 | 9 | char* strsignal(int signum); 10 | -------------------------------------------------------------------------------- /kernel/api/sys/limits.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define ARG_MAX 131072 4 | #define PATH_MAX 4096 5 | #define OPEN_MAX 1024 6 | #define SYMLINK_MAX 255 7 | #define SYMLOOP_MAX 8 8 | #define PIPE_BUF 4096 9 | -------------------------------------------------------------------------------- /kernel/memory/memory.c: -------------------------------------------------------------------------------- 1 | #include "memory.h" 2 | #include "private.h" 3 | 4 | void memory_init(const multiboot_info_t* mb_info) { 5 | page_init(mb_info); 6 | vm_init(); 7 | vm_obj_init(); 8 | } 9 | -------------------------------------------------------------------------------- /kernel/panic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "api/err.h" 4 | #include 5 | 6 | #define ASSERT_OK(result) ASSERT(IS_OK(result)) 7 | #define ASSERT_PTR(ptr) ASSERT(!IS_ERR_OR_NULL(ptr)) 8 | -------------------------------------------------------------------------------- /kernel/api/sys/uio.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct iovec { 6 | void* iov_base; // Starting address 7 | size_t iov_len; // Size of the memory pointed to by iov_base. 8 | }; 9 | -------------------------------------------------------------------------------- /userland/lib/sys/poll.c: -------------------------------------------------------------------------------- 1 | #include "poll.h" 2 | #include 3 | 4 | int poll(struct pollfd* fds, nfds_t nfds, int timeout) { 5 | return __syscall_return(SYSCALL3(poll, fds, nfds, timeout)); 6 | } 7 | -------------------------------------------------------------------------------- /kernel/api/sys/times.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "types.h" 4 | 5 | struct tms { 6 | clock_t tms_utime; 7 | clock_t tms_stime; 8 | clock_t tms_cutime; 9 | clock_t tms_cstime; 10 | }; 11 | -------------------------------------------------------------------------------- /kernel/api/sys/un.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "socket.h" 4 | 5 | #define UNIX_PATH_MAX 108 6 | 7 | struct sockaddr_un { 8 | sa_family_t sun_family; 9 | char sun_path[UNIX_PATH_MAX]; 10 | }; 11 | -------------------------------------------------------------------------------- /userland/lib/sys/prctl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | int prctl(int op, ... 6 | /* unsigned long arg2, unsigned long arg3, 7 | unsigned long arg4, unsigned long arg5 */); 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | compile_flags.txt 2 | *.[oad] 3 | /kernel/kernel 4 | /kernel/kernel.* 5 | /base/bin/** 6 | !/base/**/.gitkeep 7 | /base/root/src 8 | /initrd 9 | /disk/boot/* 10 | !/disk/boot/grub 11 | /disk_image 12 | -------------------------------------------------------------------------------- /userland/lib/termios.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | int tcgetattr(int fd, struct termios* termios_p); 6 | int tcsetattr(int fd, int optional_actions, const struct termios* termios_p); 7 | -------------------------------------------------------------------------------- /kernel/api/linux/major.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define UNNAMED_MAJOR 0 4 | #define MEM_MAJOR 1 5 | #define TTY_MAJOR 4 6 | #define TTYAUX_MAJOR 5 7 | #define MISC_MAJOR 10 8 | #define SOUND_MAJOR 14 9 | #define FB_MAJOR 29 10 | -------------------------------------------------------------------------------- /common/stdlib.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | int atoi(const char* str); 6 | int abs(int i); 7 | 8 | void qsort(void* base, size_t nmemb, size_t size, 9 | int (*compar)(const void*, const void*)); 10 | -------------------------------------------------------------------------------- /kernel/ksyms.S: -------------------------------------------------------------------------------- 1 | #define STRINGIFY(s) _STRINGIFY(s) 2 | #define _STRINGIFY(s) #s 3 | 4 | .data 5 | .globl ksyms_start, ksyms_end 6 | ksyms_start: 7 | #ifdef KSYMS 8 | .incbin STRINGIFY(KSYMS) 9 | #endif 10 | ksyms_end: 11 | -------------------------------------------------------------------------------- /userland/lib/sys/uio.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | ssize_t readv(int fd, const struct iovec* iov, int iovcnt); 7 | ssize_t writev(int fd, const struct iovec* iov, int iovcnt); 8 | -------------------------------------------------------------------------------- /userland/halt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(void) { 7 | if (reboot(RB_HALT_SYSTEM) < 0) 8 | perror("reboot"); 9 | return EXIT_FAILURE; 10 | } 11 | -------------------------------------------------------------------------------- /userland/poweroff.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(void) { 7 | if (reboot(RB_POWER_OFF) < 0) 8 | perror("reboot"); 9 | return EXIT_FAILURE; 10 | } 11 | -------------------------------------------------------------------------------- /userland/reboot.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(void) { 7 | if (reboot(RB_AUTOBOOT) < 0) 8 | perror("reboot"); 9 | return EXIT_FAILURE; 10 | } 11 | -------------------------------------------------------------------------------- /userland/env.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char* const argv[], char* const envp[]) { 5 | (void)argc; 6 | (void)argv; 7 | while (*envp) 8 | puts(*envp++); 9 | return EXIT_SUCCESS; 10 | } 11 | -------------------------------------------------------------------------------- /userland/lib/errno.c: -------------------------------------------------------------------------------- 1 | #include "errno.h" 2 | #include 3 | 4 | _Thread_local int errno; 5 | 6 | uintptr_t __syscall_return(uintptr_t rc) { 7 | if (IS_ERR(rc)) { 8 | errno = -rc; 9 | return -1; 10 | } 11 | return rc; 12 | } 13 | -------------------------------------------------------------------------------- /userland/lib/sys/wait.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct rusage; 7 | 8 | pid_t waitpid(pid_t pid, int* wstatus, int options); 9 | pid_t wait4(pid_t pid, int* wstatus, int options, struct rusage* rusage); 10 | -------------------------------------------------------------------------------- /kernel/drivers/hid/hid.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct keyboard_events { 6 | void (*raw)(unsigned char scancode); 7 | void (*key)(unsigned char keycode, bool down); 8 | }; 9 | 10 | void keyboard_set_event_handlers(const struct keyboard_events*); 11 | -------------------------------------------------------------------------------- /userland/lib/fcntl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | int open(const char* pathname, int flags, ...); 7 | int creat(const char* pathname, mode_t mode); 8 | int fcntl(int fd, int cmd, ...); 9 | int fcntl64(int fd, int cmd, ...); 10 | -------------------------------------------------------------------------------- /kernel/drivers/virtio/virtio_config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define VIRTIO_CONFIG_S_ACKNOWLEDGE 1 4 | #define VIRTIO_CONFIG_S_DRIVER 2 5 | #define VIRTIO_CONFIG_S_DRIVER_OK 4 6 | #define VIRTIO_CONFIG_S_FEATURES_OK 8 7 | #define VIRTIO_CONFIG_S_NEEDS_RESET 0x40 8 | #define VIRTIO_CONFIG_S_FAILED 0x80 9 | -------------------------------------------------------------------------------- /userland/lib/sched.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | int clone(int (*fn)(void*), void* stack, int flags, void* arg, ... 6 | /* pid_t* parent_tid, void* tls */); 7 | 8 | int sched_yield(void); 9 | 10 | int getcpu(unsigned int* cpu, unsigned int* node); 11 | -------------------------------------------------------------------------------- /userland/pwd.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(void) { 6 | static char buf[1024]; 7 | if (!getcwd(buf, 1024)) { 8 | perror("getcwd"); 9 | return EXIT_FAILURE; 10 | } 11 | puts(buf); 12 | return EXIT_SUCCESS; 13 | } 14 | -------------------------------------------------------------------------------- /common/math.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | double sqrt(double x); 4 | double log2(double x); 5 | double log10(double x); 6 | double fabs(double x); 7 | double exp2(double exponent); 8 | double pow(double x, double y); 9 | double sin(double angle); 10 | double cos(double angle); 11 | double atan2(double y, double x); 12 | -------------------------------------------------------------------------------- /kernel/api/sys/sysmacros.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define makedev(major, minor) \ 4 | (((minor)&0xffu) | ((major) << 8u) | (((minor) & ~0xffu) << 12u)) 5 | #define major(dev) (((dev)&0xfff00u) >> 8u) 6 | #define minor(dev) (((dev)&0xffu) | (((dev) >> 12u) & 0xfff00u)) 7 | -------------------------------------------------------------------------------- /userland/moused.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define MOUSE_BUTTON_LEFT 0x1 6 | #define MOUSE_BUTTON_RIGHT 0x2 7 | #define MOUSE_BUTTON_MIDDLE 0x4 8 | 9 | struct moused_event { 10 | uint32_t x; 11 | uint32_t y; 12 | int16_t dx; 13 | int16_t dy; 14 | uint8_t buttons; 15 | }; 16 | -------------------------------------------------------------------------------- /userland/lib/sys/mount.c: -------------------------------------------------------------------------------- 1 | #include "mount.h" 2 | #include 3 | 4 | int mount(const char* source, const char* target, const char* filesystemtype, 5 | unsigned long mountflags, const void* data) { 6 | return __syscall_return( 7 | SYSCALL5(mount, source, target, filesystemtype, mountflags, data)); 8 | } 9 | -------------------------------------------------------------------------------- /kernel/api/unistd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define STDIN_FILENO 0 4 | #define STDOUT_FILENO 1 5 | #define STDERR_FILENO 2 6 | 7 | #define R_OK 4 /* Test for read permission. */ 8 | #define W_OK 2 /* Test for write permission. */ 9 | #define X_OK 1 /* Test for execute permission. */ 10 | #define F_OK 0 /* Test for existence. */ 11 | -------------------------------------------------------------------------------- /userland/lib/sys/ioctl.c: -------------------------------------------------------------------------------- 1 | #include "ioctl.h" 2 | #include 3 | #include 4 | 5 | int ioctl(int fd, unsigned cmd, ...) { 6 | va_list args; 7 | va_start(args, cmd); 8 | unsigned long arg = va_arg(args, unsigned long); 9 | va_end(args); 10 | return __syscall_return(SYSCALL3(ioctl, fd, cmd, arg)); 11 | } 12 | -------------------------------------------------------------------------------- /kernel/api/sys/poll.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define POLLIN (1 << 0) 4 | #define POLLPRI (1 << 1) 5 | #define POLLOUT (1 << 2) 6 | #define POLLERR (1 << 3) 7 | #define POLLHUP (1 << 4) 8 | #define POLLNVAL (1 << 5) 9 | 10 | typedef unsigned nfds_t; 11 | 12 | struct pollfd { 13 | int fd; 14 | short events; 15 | short revents; 16 | }; 17 | -------------------------------------------------------------------------------- /userland/echo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char* const argv[]) { 5 | if (argc < 2) { 6 | putchar('\n'); 7 | return EXIT_SUCCESS; 8 | } 9 | for (int i = 1; i < argc - 1; ++i) 10 | printf("%s ", argv[i]); 11 | puts(argv[argc - 1]); 12 | return EXIT_SUCCESS; 13 | } 14 | -------------------------------------------------------------------------------- /userland/lib/sys/uio.c: -------------------------------------------------------------------------------- 1 | #include "uio.h" 2 | #include 3 | 4 | ssize_t readv(int fd, const struct iovec* iov, int iovcnt) { 5 | return __syscall_return(SYSCALL3(readv, fd, iov, iovcnt)); 6 | } 7 | 8 | ssize_t writev(int fd, const struct iovec* iov, int iovcnt) { 9 | return __syscall_return(SYSCALL3(writev, fd, iov, iovcnt)); 10 | } 11 | -------------------------------------------------------------------------------- /kernel/console/console.c: -------------------------------------------------------------------------------- 1 | #include "console.h" 2 | #include "private.h" 3 | #include "screen/screen.h" 4 | #include 5 | 6 | void console_init(void) { 7 | serial_console_init(); 8 | 9 | struct screen* screen = screen_init(); 10 | if (IS_OK(screen)) 11 | virtual_console_init(screen); 12 | 13 | system_console_init(); 14 | } 15 | -------------------------------------------------------------------------------- /kernel/api/sys/socket.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define AF_UNIX 1 6 | #define AF_LOCAL AF_UNIX 7 | 8 | #define SOCK_STREAM 1 9 | 10 | enum { SHUT_RD, SHUT_WR, SHUT_RDWR }; 11 | 12 | typedef uint16_t sa_family_t; 13 | typedef uint32_t socklen_t; 14 | 15 | struct sockaddr { 16 | sa_family_t sa_family; 17 | char sa_data[14]; 18 | }; 19 | -------------------------------------------------------------------------------- /kernel/api/sys/wait.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define WEXITSTATUS(status) (((status)&0xff00) >> 8) 4 | #define WTERMSIG(status) ((status)&0x7f) 5 | #define WIFEXITED(status) (WTERMSIG(status) == 0) 6 | #define WIFSIGNALED(status) \ 7 | ((0 < WTERMSIG(status)) && (WTERMSIG(status) < 0x7f)) 8 | 9 | #define WNOHANG 0x1 10 | -------------------------------------------------------------------------------- /userland/date.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(void) { 6 | time_t now; 7 | if (time(&now) < 0) { 8 | perror("time"); 9 | return EXIT_FAILURE; 10 | } 11 | struct tm tm; 12 | char buf[1024]; 13 | asctime_r(gmtime_r(&now, &tm), buf); 14 | puts(buf); 15 | return EXIT_SUCCESS; 16 | } 17 | -------------------------------------------------------------------------------- /kernel/api/fcntl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define F_DUPFD 0 4 | #define F_GETFL 3 5 | #define F_SETFL 4 6 | 7 | #define O_ACCMODE 00000003 8 | #define O_RDONLY 00000000 9 | #define O_WRONLY 00000001 10 | #define O_RDWR 00000002 11 | #define O_CREAT 00000100 12 | #define O_EXCL 00000200 13 | #define O_TRUNC 00001000 14 | #define O_NONBLOCK 00004000 15 | #define O_NOFOLLOW 00400000 16 | -------------------------------------------------------------------------------- /kernel/drivers/serial.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define SERIAL_NUM_PORTS 4 8 | 9 | void serial_early_init(void); 10 | bool serial_is_port_enabled(uint8_t index); 11 | void serial_write(uint8_t index, const char* s, size_t count); 12 | void serial_set_input_handler(void (*handler)(uint8_t index, char)); 13 | -------------------------------------------------------------------------------- /userland/lib/sys/wait.c: -------------------------------------------------------------------------------- 1 | #include "wait.h" 2 | #include 3 | 4 | pid_t waitpid(pid_t pid, int* wstatus, int options) { 5 | return __syscall_return(SYSCALL3(waitpid, pid, wstatus, options)); 6 | } 7 | 8 | pid_t wait4(pid_t pid, int* wstatus, int options, struct rusage* rusage) { 9 | return __syscall_return(SYSCALL4(wait4, pid, wstatus, options, rusage)); 10 | } 11 | -------------------------------------------------------------------------------- /userland/lib/sys/mman.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | void* mmap(void* addr, size_t length, int prot, int flags, int fd, 8 | off_t offset); 9 | int munmap(void* addr, size_t length); 10 | int mprotect(void* addr, size_t len, int prot); 11 | int msync(void* addr, size_t length, int flags); 12 | -------------------------------------------------------------------------------- /kernel/kmsg.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #define KMSG_BUF_SIZE 16384 7 | 8 | int kprint(const char*); 9 | int kprintf(const char* format, ...) PRINTF_LIKE(1, 2); 10 | int kvprintf(const char* format, va_list args) PRINTF_LIKE(1, 0); 11 | 12 | size_t kmsg_read(char* buf, size_t count); 13 | void kmsg_write(const char* buf, size_t count); 14 | -------------------------------------------------------------------------------- /run_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | ! qemu-system-i386 \ 6 | -kernel kernel/kernel \ 7 | -initrd initrd \ 8 | -append 'panic=poweroff init=/bin/init-test console=ttyS0' \ 9 | -d guest_errors \ 10 | -no-reboot \ 11 | -cpu max \ 12 | -serial stdio \ 13 | -vga none -display none \ 14 | -m 256M \ 15 | 2>&1 | tee >(cat 1>&2) | grep -q PANIC 16 | -------------------------------------------------------------------------------- /userland/lib/sys/prctl.c: -------------------------------------------------------------------------------- 1 | #include "prctl.h" 2 | #include 3 | #include 4 | 5 | int prctl(int op, ...) { 6 | unsigned long x[4]; 7 | va_list ap; 8 | va_start(ap, op); 9 | for (int i = 0; i < 4; ++i) 10 | x[i] = va_arg(ap, unsigned long); 11 | va_end(ap); 12 | return __syscall_return(SYSCALL5(prctl, op, x[0], x[1], x[2], x[3])); 13 | } 14 | -------------------------------------------------------------------------------- /kernel/api/asm/ldt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct user_desc { 4 | unsigned int entry_number; 5 | unsigned int base_addr; 6 | unsigned int limit; 7 | unsigned int seg_32bit : 1; 8 | unsigned int contents : 2; 9 | unsigned int read_exec_only : 1; 10 | unsigned int limit_in_pages : 1; 11 | unsigned int seg_not_present : 1; 12 | unsigned int useable : 1; 13 | }; 14 | -------------------------------------------------------------------------------- /userland/mv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(int argc, char* argv[]) { 6 | if (argc != 3) { 7 | dprintf(STDERR_FILENO, "Usage: mv SOURCE DEST\n"); 8 | return EXIT_FAILURE; 9 | } 10 | if (rename(argv[1], argv[2]) < 0) { 11 | perror("rename"); 12 | return EXIT_FAILURE; 13 | } 14 | return EXIT_SUCCESS; 15 | } 16 | -------------------------------------------------------------------------------- /kernel/api/time.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "sys/types.h" 4 | 5 | #define TIMER_ABSTIME 1 6 | 7 | enum { 8 | CLOCK_REALTIME, 9 | CLOCK_MONOTONIC, 10 | }; 11 | 12 | typedef int clockid_t; 13 | 14 | struct timespec { 15 | time_t tv_sec; 16 | long long tv_nsec; 17 | }; 18 | 19 | typedef int32_t time32_t; 20 | 21 | struct timespec32 { 22 | time32_t tv_sec; 23 | int32_t tv_nsec; 24 | }; 25 | -------------------------------------------------------------------------------- /kernel/api/sys/ioctl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define TCGETS 0x5401 4 | #define TCSETS 0x5402 5 | #define TCSETSW 0x5403 6 | #define TCSETSF 0x5404 7 | #define TIOCGPGRP 0x540f 8 | #define TIOCSPGRP 0x5410 9 | #define TIOCGWINSZ 0x5413 10 | #define TIOCSWINSZ 0x5414 11 | 12 | struct winsize { 13 | unsigned short ws_row; 14 | unsigned short ws_col; 15 | unsigned short ws_xpixel; 16 | unsigned short ws_ypixel; 17 | }; 18 | -------------------------------------------------------------------------------- /userland/mkfifo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(int argc, char* const argv[]) { 7 | if (argc != 2) { 8 | dprintf(STDERR_FILENO, "Usage: mkfifo NAME\n"); 9 | return EXIT_FAILURE; 10 | } 11 | if (mkfifo(argv[1], 0) < 0) { 12 | perror("mkfifo"); 13 | return EXIT_FAILURE; 14 | } 15 | return EXIT_SUCCESS; 16 | } 17 | -------------------------------------------------------------------------------- /userland/lib/panic.c: -------------------------------------------------------------------------------- 1 | #include "panic.h" 2 | #include "stdio.h" 3 | #include "stdlib.h" 4 | #include "unistd.h" 5 | 6 | noreturn void panic(const char* file, size_t line, const char* format, ...) { 7 | dprintf(STDERR_FILENO, "PANIC: "); 8 | va_list args; 9 | va_start(args, format); 10 | vdprintf(STDERR_FILENO, format, args); 11 | va_end(args); 12 | dprintf(STDERR_FILENO, " at %s:%u\n", file, line); 13 | abort(); 14 | } 15 | -------------------------------------------------------------------------------- /userland/rm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(int argc, char* argv[]) { 6 | if (argc < 2) { 7 | dprintf(STDERR_FILENO, "Usage: rm FILE...\n"); 8 | return EXIT_FAILURE; 9 | } 10 | for (int i = 1; i < argc; ++i) { 11 | if (unlink(argv[i]) < 0) { 12 | perror("unlink"); 13 | return EXIT_FAILURE; 14 | } 15 | } 16 | return EXIT_SUCCESS; 17 | } 18 | -------------------------------------------------------------------------------- /common/stdio.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "extra.h" 4 | #include 5 | #include 6 | 7 | int sprintf(char* buffer, const char* format, ...) PRINTF_LIKE(2, 3); 8 | int snprintf(char* buffer, size_t bufsz, const char* format, ...) 9 | PRINTF_LIKE(3, 4); 10 | int vsprintf(char* buffer, const char* format, va_list args) PRINTF_LIKE(2, 0); 11 | int vsnprintf(char* buffer, size_t size, const char* format, va_list args) 12 | PRINTF_LIKE(3, 0); 13 | -------------------------------------------------------------------------------- /kernel/console/screen/screen.c: -------------------------------------------------------------------------------- 1 | #include "screen.h" 2 | #include 3 | 4 | struct screen* fb_screen_init(void); 5 | struct screen* vga_text_screen_init(void); 6 | 7 | struct screen* screen_init(void) { 8 | struct screen* screen = fb_screen_init(); 9 | if (IS_OK(screen)) 10 | return screen; 11 | 12 | screen = vga_text_screen_init(); 13 | if (IS_OK(screen)) 14 | return screen; 15 | 16 | return ERR_PTR(-ENODEV); 17 | } 18 | -------------------------------------------------------------------------------- /userland/rmdir.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(int argc, char* argv[]) { 6 | if (argc < 2) { 7 | dprintf(STDERR_FILENO, "Usage: rmdir DIRECTORY...\n"); 8 | return EXIT_FAILURE; 9 | } 10 | for (int i = 1; i < argc; ++i) { 11 | if (rmdir(argv[i]) < 0) { 12 | perror("rmdir"); 13 | return EXIT_FAILURE; 14 | } 15 | } 16 | return EXIT_SUCCESS; 17 | } 18 | -------------------------------------------------------------------------------- /kernel/drivers/graphics/graphics.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct fb_info { 7 | char id[16]; 8 | uintptr_t phys_addr; 9 | size_t width; 10 | size_t height; 11 | size_t pitch; 12 | size_t bpp; 13 | }; 14 | 15 | struct fb { 16 | int (*get_info)(struct fb_info* out_info); 17 | int (*set_info)(struct fb_info* inout_info); 18 | }; 19 | 20 | struct fb* fb_get(void); 21 | struct vm_obj* fb_mmap(void); 22 | -------------------------------------------------------------------------------- /kernel/drivers/drivers.c: -------------------------------------------------------------------------------- 1 | #include "drivers.h" 2 | #include 3 | 4 | void pit_init(void); 5 | void serial_late_init(void); 6 | void ps2_init(void); 7 | void fb_init(const multiboot_info_t*); 8 | void virtio_blk_init(void); 9 | void ac97_init(void); 10 | 11 | void drivers_init(const multiboot_info_t* mb_info) { 12 | pit_init(); 13 | serial_late_init(); 14 | ps2_init(); 15 | fb_init(mb_info); 16 | virtio_blk_init(); 17 | ac97_init(); 18 | } 19 | -------------------------------------------------------------------------------- /kernel/socket.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fs/fs.h" 4 | 5 | void socket_init(void); 6 | 7 | struct inode* unix_socket_create(void); 8 | NODISCARD int unix_socket_bind(struct inode* socket, struct inode* addr_inode); 9 | NODISCARD int unix_socket_listen(struct inode* socket, int backlog); 10 | NODISCARD struct inode* unix_socket_accept(struct file*); 11 | NODISCARD int unix_socket_connect(struct file*, struct inode* addr_inode); 12 | NODISCARD int unix_socket_shutdown(struct file*, int how); 13 | -------------------------------------------------------------------------------- /common/libgen.c: -------------------------------------------------------------------------------- 1 | #include "libgen.h" 2 | #include "string.h" 3 | 4 | char* basename(char* path) { 5 | if (!path) 6 | return "."; 7 | size_t len = strlen(path); 8 | if (len == 0) 9 | return "."; 10 | 11 | while (len > 1 && path[len - 1] == '/') 12 | path[--len] = 0; 13 | 14 | char* last_slash = strrchr(path, '/'); 15 | if (!last_slash) 16 | return path; 17 | 18 | if (len == 1) 19 | return "/"; 20 | return last_slash + 1; 21 | } 22 | -------------------------------------------------------------------------------- /kernel/api/sys/mman.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define PROT_NONE 0x0 4 | #define PROT_READ 0x1 5 | #define PROT_WRITE 0x2 6 | 7 | #define MAP_SHARED 0x1 8 | #define MAP_PRIVATE 0x2 9 | #define MAP_FIXED 0x10 10 | #define MAP_ANONYMOUS 0x20 11 | #define MAP_ANON MAP_ANONYMOUS 12 | 13 | #define MAP_FAILED ((void*)-1) 14 | 15 | #define MS_ASYNC 1 /* Sync memory asynchronously. */ 16 | #define MS_SYNC 4 /* Synchronous memory sync. */ 17 | #define MS_INVALIDATE 2 /* Invalidate the caches. */ 18 | -------------------------------------------------------------------------------- /userland/mkdir.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(int argc, char* const argv[]) { 7 | if (argc < 2) { 8 | dprintf(STDERR_FILENO, "Usage: mkdir DIRECTORY...\n"); 9 | return EXIT_FAILURE; 10 | } 11 | for (int i = 1; i < argc; ++i) { 12 | if (mkdir(argv[i], 0) < 0) { 13 | perror("mkdir"); 14 | return EXIT_FAILURE; 15 | } 16 | } 17 | return EXIT_SUCCESS; 18 | } 19 | -------------------------------------------------------------------------------- /userland/sleep.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(int argc, char* const argv[]) { 7 | if (argc != 2) { 8 | dprintf(STDERR_FILENO, "Usage: sleep NUMBER\n"); 9 | return EXIT_FAILURE; 10 | } 11 | struct timespec req = {.tv_sec = atoi(argv[1]), .tv_nsec = 0}; 12 | if (nanosleep(&req, NULL) < 0) { 13 | perror("nanosleep"); 14 | return EXIT_FAILURE; 15 | } 16 | return EXIT_SUCCESS; 17 | } 18 | -------------------------------------------------------------------------------- /userland/lib/sys/socket.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | int socket(int domain, int type, int protocol); 6 | int bind(int sockfd, const struct sockaddr* addr, socklen_t addrlen); 7 | int listen(int sockfd, int backlog); 8 | int accept(int sockfd, struct sockaddr* addr, socklen_t* addrlen); 9 | int accept4(int sockfd, struct sockaddr* addr, socklen_t* addrlen, int flags); 10 | int connect(int sockfd, const struct sockaddr* addr, socklen_t addrlen); 11 | int shutdown(int sockfd, int how); 12 | -------------------------------------------------------------------------------- /userland/lib/dirent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | typedef struct __DIR DIR; 8 | 9 | struct dirent { 10 | ino_t d_ino; // Inode number 11 | off_t d_off; 12 | unsigned short d_reclen; // Length of this record 13 | unsigned char d_type; // Type of file 14 | char d_name[256]; // Null-terminated filename 15 | }; 16 | 17 | DIR* opendir(const char* name); 18 | int closedir(DIR* dirp); 19 | struct dirent* readdir(DIR* dirp); 20 | -------------------------------------------------------------------------------- /kernel/api/sys/types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef uint32_t dev_t; 6 | typedef uint32_t uid_t; 7 | typedef uint32_t gid_t; 8 | typedef uint32_t ino_t; 9 | typedef uint32_t mode_t; 10 | typedef uint32_t nlink_t; 11 | typedef int32_t off_t; 12 | typedef int64_t loff_t; 13 | typedef int32_t pid_t; 14 | typedef uint32_t clock_t; 15 | typedef int64_t time_t; 16 | typedef uint32_t useconds_t; 17 | typedef int32_t suseconds_t; 18 | typedef int32_t blksize_t; 19 | typedef int32_t blkcnt_t; 20 | typedef int32_t ssize_t; 21 | -------------------------------------------------------------------------------- /userland/lib/sys/time.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct timeval { 7 | time_t tv_sec; /* seconds */ 8 | suseconds_t tv_usec; /* microseconds */ 9 | }; 10 | 11 | struct timezone { 12 | int tz_minuteswest; /* minutes west of Greenwich */ 13 | int tz_dsttime; /* type of DST correction */ 14 | }; 15 | 16 | int gettimeofday(struct timeval* restrict tv, struct timezone* restrict tz); 17 | int settimeofday(const struct timeval* tv, const struct timezone* tz); 18 | -------------------------------------------------------------------------------- /userland/touch.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(int argc, char* const argv[]) { 7 | if (argc < 2) { 8 | dprintf(STDERR_FILENO, "Usage: touch FILE...\n"); 9 | return EXIT_FAILURE; 10 | } 11 | 12 | for (int i = 1; i < argc; ++i) { 13 | int fd = open(argv[i], O_CREAT, 0); 14 | if (fd < 0) { 15 | perror("open"); 16 | return EXIT_FAILURE; 17 | } 18 | close(fd); 19 | } 20 | 21 | return EXIT_SUCCESS; 22 | } 23 | -------------------------------------------------------------------------------- /kernel/lock.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | struct mutex { 8 | volatile struct task* holder; 9 | volatile uint32_t level; 10 | volatile atomic_bool lock; 11 | }; 12 | 13 | void mutex_lock(struct mutex*); 14 | void mutex_unlock(struct mutex*); 15 | bool mutex_is_locked_by_current(const struct mutex*); 16 | 17 | struct spinlock { 18 | uint32_t level; 19 | volatile atomic_uint lock; 20 | }; 21 | 22 | void spinlock_lock(struct spinlock*); 23 | void spinlock_unlock(struct spinlock*); 24 | -------------------------------------------------------------------------------- /userland/readlink.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(int argc, char* argv[]) { 7 | if (argc < 2) { 8 | dprintf(STDERR_FILENO, "Usage: FILE...\n"); 9 | return EXIT_FAILURE; 10 | } 11 | 12 | char target[SYMLINK_MAX + 1] = {0}; 13 | for (int i = 1; i < argc; i++) { 14 | if (readlink(argv[i], target, SYMLINK_MAX) < 0) { 15 | perror("readlink"); 16 | return EXIT_FAILURE; 17 | } 18 | puts(target); 19 | } 20 | 21 | return EXIT_SUCCESS; 22 | } 23 | -------------------------------------------------------------------------------- /userland/lib/stdlib.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define EXIT_SUCCESS 0 8 | #define EXIT_FAILURE 1 9 | 10 | #define RAND_MAX 2147483647 11 | 12 | noreturn void exit(int status); 13 | noreturn void abort(void); 14 | 15 | void* malloc(size_t size); 16 | void* aligned_alloc(size_t alignment, size_t size); 17 | void* calloc(size_t num, size_t size); 18 | void* realloc(void* ptr, size_t new_size); 19 | void free(void* ptr); 20 | 21 | char* getenv(const char* name); 22 | 23 | int rand(void); 24 | void srand(unsigned seed); 25 | -------------------------------------------------------------------------------- /kernel/api/sys/reboot.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define LINUX_REBOOT_MAGIC1 0xfee1dead 4 | #define LINUX_REBOOT_MAGIC2 0x28121969 5 | #define LINUX_REBOOT_MAGIC2A 0x05121996 6 | #define LINUX_REBOOT_MAGIC2B 0x16041998 7 | #define LINUX_REBOOT_MAGIC2C 0x20112000 8 | 9 | #define LINUX_REBOOT_CMD_RESTART 0x1234567 10 | #define LINUX_REBOOT_CMD_RESTART2 0xa1b2c3d4 11 | #define LINUX_REBOOT_CMD_HALT 0xcdef0123 12 | #define LINUX_REBOOT_CMD_POWER_OFF 0x4321fedc 13 | 14 | #define RB_AUTOBOOT LINUX_REBOOT_CMD_RESTART 15 | #define RB_HALT_SYSTEM LINUX_REBOOT_CMD_HALT 16 | #define RB_POWER_OFF LINUX_REBOOT_CMD_POWER_OFF 17 | -------------------------------------------------------------------------------- /base/root/invictus.txt: -------------------------------------------------------------------------------- 1 | Out of the night that covers me, 2 | Black as the pit from pole to pole, 3 | I thank whatever gods may be 4 | For my unconquerable soul. 5 | 6 | In the fell clutch of circumstance 7 | I have not winced nor cried aloud. 8 | Under the bludgeonings of chance 9 | My head is bloody, but unbowed. 10 | 11 | Beyond this place of wrath and tears 12 | Looms but the Horror of the shade, 13 | And yet the menace of the years 14 | Finds and shall find me unafraid. 15 | 16 | It matters not how strait the gate, 17 | How charged with punishments the scroll, 18 | I am the master of my fate, 19 | I am the captain of my soul. 20 | -------------------------------------------------------------------------------- /kernel/console/screen/screen.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define NUM_COLORS 16 9 | 10 | struct screen { 11 | void (*get_size)(size_t* out_columns, size_t* out_rows); 12 | void (*put)(size_t x, size_t y, char c, uint8_t fg_color, uint8_t bg_color); 13 | void (*clear)(uint8_t bg_color); 14 | 15 | void (*set_cursor)(size_t x, size_t y, bool visible); 16 | void (*set_palette)(const uint32_t palette[NUM_COLORS]); 17 | void (*set_font)(const struct font*); 18 | }; 19 | 20 | struct screen* screen_init(void); 21 | -------------------------------------------------------------------------------- /userland/lib/stdio.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | extern const char* const sys_errlist[]; 7 | 8 | int putchar(int ch); 9 | int puts(const char* str); 10 | int printf(const char* format, ...) PRINTF_LIKE(1, 2); 11 | int vprintf(const char* format, va_list ap) PRINTF_LIKE(1, 0); 12 | int dprintf(int fd, const char* format, ...) PRINTF_LIKE(2, 3); 13 | int vdprintf(int fd, const char* format, va_list ap) PRINTF_LIKE(2, 0); 14 | 15 | void perror(const char*); 16 | 17 | int getchar(void); 18 | 19 | int remove(const char* pathname); 20 | 21 | int dbgprint(const char* str); 22 | -------------------------------------------------------------------------------- /kernel/drivers/virtio/virtio.h: -------------------------------------------------------------------------------- 1 | #include "virtio_pci.h" 2 | #include "virtio_queue.h" 3 | #include 4 | #include 5 | #include 6 | 7 | struct pci_addr; 8 | 9 | struct virtio { 10 | void* notify_space; 11 | size_t num_virtqs; 12 | struct virtq* virtqs[]; 13 | }; 14 | 15 | struct virtio* virtio_create(const struct pci_addr*, size_t num_virtqs); 16 | void virtio_destroy(struct virtio*); 17 | 18 | DEFINE_FREE(virtio, struct virtio*, virtio_destroy) 19 | 20 | NODISCARD bool virtio_find_pci_cap(const struct pci_addr*, uint8_t cfg_type, 21 | struct virtio_pci_cap* out_cap); 22 | -------------------------------------------------------------------------------- /kernel/time.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "api/time.h" 4 | #include 5 | #include 6 | 7 | #define CLK_TCK 250 8 | 9 | struct timespec; 10 | 11 | extern volatile atomic_uint uptime; 12 | 13 | void timespec_add(struct timespec*, const struct timespec*); 14 | void timespec_saturating_sub(struct timespec*, const struct timespec*); 15 | int timespec_compare(const struct timespec*, const struct timespec*); 16 | 17 | void time_init(void); 18 | void time_tick(void); 19 | NODISCARD int time_now(clockid_t, struct timespec*); 20 | NODISCARD int time_set(clockid_t, const struct timespec*); 21 | NODISCARD int time_get_resolution(clockid_t, struct timespec*); 22 | -------------------------------------------------------------------------------- /common/strings.c: -------------------------------------------------------------------------------- 1 | #include "strings.h" 2 | #include "ctype.h" 3 | 4 | int strcasecmp(const char* s1, const char* s2) { 5 | for (; tolower(*s1) == tolower(*s2); ++s1, ++s2) { 6 | if (*s1 == 0) 7 | return 0; 8 | } 9 | return tolower(*s1) < tolower(*s2) ? -1 : 1; 10 | } 11 | 12 | int strncasecmp(const char* s1, const char* s2, size_t n) { 13 | if (!n) 14 | return 0; 15 | do { 16 | if (tolower(*s1) != tolower(*s2++)) 17 | return tolower(*(const unsigned char*)s1) - 18 | tolower(*(const unsigned char*)--s2); 19 | if (*s1++ == 0) 20 | break; 21 | } while (--n); 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /kernel/api/sys/utsname.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define UTSNAME_LENGTH 65 4 | 5 | struct utsname { 6 | char sysname[UTSNAME_LENGTH]; 7 | char nodename[UTSNAME_LENGTH]; 8 | char release[UTSNAME_LENGTH]; 9 | char version[UTSNAME_LENGTH]; 10 | char machine[UTSNAME_LENGTH]; 11 | char domainname[UTSNAME_LENGTH]; 12 | }; 13 | 14 | struct linux_oldold_utsname { 15 | char sysname[9]; 16 | char nodename[9]; 17 | char release[9]; 18 | char version[9]; 19 | char machine[9]; 20 | }; 21 | 22 | struct linux_old_utsname { 23 | char sysname[65]; 24 | char nodename[65]; 25 | char release[65]; 26 | char version[65]; 27 | char machine[65]; 28 | }; 29 | -------------------------------------------------------------------------------- /userland/lib/termios.c: -------------------------------------------------------------------------------- 1 | #include "termios.h" 2 | #include "errno.h" 3 | #include "sys/ioctl.h" 4 | 5 | int tcgetattr(int fd, struct termios* termios_p) { 6 | return ioctl(fd, TCGETS, termios_p); 7 | } 8 | 9 | int tcsetattr(int fd, int optional_actions, const struct termios* termios_p) { 10 | int cmd; 11 | switch (optional_actions) { 12 | case TCSANOW: 13 | cmd = TCSETS; 14 | break; 15 | case TCSADRAIN: 16 | cmd = TCSETSW; 17 | break; 18 | case TCSAFLUSH: 19 | cmd = TCSETSF; 20 | break; 21 | default: 22 | errno = EINVAL; 23 | return -1; 24 | } 25 | return ioctl(fd, cmd, (void*)termios_p); 26 | } 27 | -------------------------------------------------------------------------------- /kernel/api/sched.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Set if VM shared between processes. 4 | #define CLONE_VM 0x00000100 5 | 6 | // Set if fs info shared between processes. 7 | #define CLONE_FS 0x00000200 8 | 9 | // Set if open files shared between processes. 10 | #define CLONE_FILES 0x00000400 11 | 12 | // Set if signal handlers and blocked signals shared 13 | #define CLONE_SIGHAND 0x00000800 14 | 15 | // Set if the parent wants the child to wake it up on mm_release 16 | #define CLONE_VFORK 0x00004000 17 | 18 | // Set to add to same thread group. 19 | #define CLONE_THREAD 0x00010000 20 | 21 | // Set TLS info. 22 | #define CLONE_SETTLS 0x00080000 23 | 24 | // Store TID in userlevel buffer before MM copy. 25 | #define CLONE_PARENT_SETTID 0x00100000 26 | -------------------------------------------------------------------------------- /kernel/api/err.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "errno.h" 4 | #include 5 | #include 6 | 7 | // Casts error value to pointer 8 | #define ERR_PTR(x) ((void*)(x)) 9 | 10 | // Casts pointer to error value 11 | #define PTR_ERR(x) ((long)(x)) 12 | 13 | // Valid pointers never point to the last page of the address space. 14 | // We use this fact to encode error values in pointers. 15 | #define IS_ERR(x) ((uintptr_t)(x) > (uintptr_t)(-4096)) 16 | STATIC_ASSERT(IS_ERR(-EMAXERRNO)); 17 | 18 | static inline bool IS_ERR_OR_NULL(const volatile void* ptr) { 19 | return !ptr || IS_ERR(ptr); 20 | } 21 | 22 | #define IS_OK(x) (!IS_ERR(x)) 23 | 24 | // Casts error-valued pointer to pointer of another type 25 | #define ERR_CAST(x) ((void*)(x)) 26 | -------------------------------------------------------------------------------- /userland/lib/sys/select.c: -------------------------------------------------------------------------------- 1 | #include "select.h" 2 | #include 3 | 4 | int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, 5 | struct timeval* timeout) { 6 | struct linux_timeval linux_timeout; 7 | if (timeout) { 8 | linux_timeout.tv_sec = timeout->tv_sec; 9 | linux_timeout.tv_usec = timeout->tv_usec; 10 | } 11 | int rc = SYSCALL5(_newselect, nfds, readfds, writefds, exceptfds, 12 | timeout ? &linux_timeout : NULL); 13 | if (timeout) { 14 | // Update the timeout regardless of whether the syscall was successful. 15 | timeout->tv_sec = linux_timeout.tv_sec; 16 | timeout->tv_usec = linux_timeout.tv_usec; 17 | } 18 | return __syscall_return(rc); 19 | } 20 | -------------------------------------------------------------------------------- /userland/lib/sys/time.c: -------------------------------------------------------------------------------- 1 | #include "time.h" 2 | #include 3 | #include 4 | #include 5 | 6 | int gettimeofday(struct timeval* tv, struct timezone* tz) { 7 | (void)tz; 8 | struct linux_timeval linux_tv; 9 | int rc = SYSCALL2(gettimeofday, &linux_tv, NULL); 10 | if (IS_ERR(rc)) { 11 | errno = -rc; 12 | return -1; 13 | } 14 | tv->tv_sec = linux_tv.tv_sec; 15 | tv->tv_usec = linux_tv.tv_usec; 16 | return 0; 17 | } 18 | 19 | int settimeofday(const struct timeval* tv, const struct timezone* tz) { 20 | (void)tz; 21 | struct linux_timeval linux_tv = { 22 | .tv_sec = tv->tv_sec, 23 | .tv_usec = tv->tv_usec, 24 | }; 25 | return __syscall_return(SYSCALL2(settimeofday, &linux_tv, NULL)); 26 | } 27 | -------------------------------------------------------------------------------- /kernel/containers/vec.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | struct vec { 9 | unsigned char* data; 10 | uint64_t capacity, size; 11 | }; 12 | 13 | void vec_destroy(struct vec*); 14 | 15 | NODISCARD ssize_t vec_pread(struct vec*, void* bytes, size_t count, 16 | uint64_t offset); 17 | NODISCARD ssize_t vec_pwrite(struct vec*, const void* bytes, size_t count, 18 | uint64_t offset); 19 | NODISCARD ssize_t vec_append(struct vec*, const void* bytes, size_t count); 20 | 21 | int vec_printf(struct vec*, const char* format, ...) PRINTF_LIKE(2, 3); 22 | int vec_vsprintf(struct vec*, const char* format, va_list args) 23 | PRINTF_LIKE(2, 0); 24 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | Checks: > 2 | -*, 3 | bugprone-*, 4 | cert-*, 5 | clang-analyzer-*, 6 | concurrency-*, 7 | misc-*, 8 | performance-*, 9 | portability-*, 10 | readability-*, 11 | -bugprone-easily-swappable-parameters, 12 | -bugprone-reserved-identifier, 13 | -cert-dcl37-c, 14 | -cert-dcl51-cpp, 15 | -cert-err34-c, 16 | -concurrency-mt-unsafe, 17 | -misc-no-recursion, 18 | -performance-no-int-to-ptr, 19 | -readability-braces-around-statements, 20 | -readability-identifier-length, 21 | -readability-magic-numbers, 22 | -readability-inconsistent-declaration-parameter-name, 23 | -readability-function-cognitive-complexity, 24 | CheckOptions: 25 | - key: bugprone-narrowing-conversions.WarnOnIntegerNarrowingConversion 26 | value: false 27 | -------------------------------------------------------------------------------- /common/panic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #define PANIC(...) panic(__FILE__, __LINE__, __VA_ARGS__) 7 | #define UNREACHABLE() PANIC("Unreachable") 8 | #define UNIMPLEMENTED() PANIC("Unimplemented") 9 | #define ASSERT(cond) \ 10 | ({ \ 11 | __typeof__(cond) _cond = (cond); \ 12 | if (!_cond) \ 13 | PANIC("Assertion failed: " #cond); \ 14 | _cond; \ 15 | }) 16 | 17 | noreturn void panic(const char* file, size_t line, const char* message, ...); 18 | -------------------------------------------------------------------------------- /kernel/drivers/pit.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define TIMER0_CTL 0x40 7 | #define PIT_CTL 0x43 8 | #define TIMER0_SELECT 0x00 9 | #define WRITE_WORD 0x30 10 | #define MODE_SQUARE_WAVE 0x06 11 | #define BASE_FREQUENCY 1193182 12 | 13 | static void tick(struct registers* regs) { 14 | time_tick(); 15 | 16 | // When SMP is active, the sched_tick is called from the per-CPU 17 | // local APIC timer interrupt handler. 18 | if (!smp_active) 19 | sched_tick(regs); 20 | } 21 | 22 | void pit_init(void) { 23 | uint16_t div = BASE_FREQUENCY / CLK_TCK; 24 | out8(PIT_CTL, TIMER0_SELECT | WRITE_WORD | MODE_SQUARE_WAVE); 25 | out8(TIMER0_CTL, div & 0xff); 26 | out8(TIMER0_CTL, div >> 8); 27 | idt_set_interrupt_handler(IRQ(0), tick); 28 | } 29 | -------------------------------------------------------------------------------- /userland/lib/sys/mman.c: -------------------------------------------------------------------------------- 1 | #include "mman.h" 2 | #include 3 | #include 4 | 5 | #define MMAP2_PAGE_UNIT 4096 6 | 7 | void* mmap(void* addr, size_t length, int prot, int flags, int fd, 8 | off_t offset) { 9 | if (offset % MMAP2_PAGE_UNIT) { 10 | errno = EINVAL; 11 | return MAP_FAILED; 12 | } 13 | return (void*)__syscall_return(SYSCALL6(mmap2, addr, length, prot, flags, 14 | fd, offset / MMAP2_PAGE_UNIT)); 15 | } 16 | 17 | int munmap(void* addr, size_t length) { 18 | return __syscall_return(SYSCALL2(munmap, addr, length)); 19 | } 20 | 21 | int mprotect(void* addr, size_t len, int prot) { 22 | return __syscall_return(SYSCALL3(mprotect, addr, len, prot)); 23 | } 24 | 25 | int msync(void* addr, size_t length, int flags) { 26 | return __syscall_return(SYSCALL3(msync, addr, length, flags)); 27 | } 28 | -------------------------------------------------------------------------------- /userland/lib/sched.c: -------------------------------------------------------------------------------- 1 | #include "sched.h" 2 | #include "errno.h" 3 | #include 4 | #include 5 | #include 6 | 7 | int clone(int (*fn)(void*), void* stack, int flags, void* arg, ...) { 8 | if (!fn || !stack) 9 | return -EINVAL; 10 | 11 | pid_t* parent_tid = NULL; 12 | void* tls = NULL; 13 | 14 | va_list ap; 15 | va_start(ap, arg); 16 | if (flags & (CLONE_PARENT_SETTID | CLONE_SETTLS)) 17 | parent_tid = va_arg(ap, pid_t*); 18 | if (flags & CLONE_SETTLS) 19 | tls = va_arg(ap, void*); 20 | va_end(ap); 21 | 22 | return __syscall_return( 23 | __clone(fn, stack, flags, arg, parent_tid, tls, NULL)); 24 | } 25 | 26 | int sched_yield(void) { return __syscall_return(SYSCALL0(sched_yield)); } 27 | 28 | int getcpu(unsigned int* cpu, unsigned int* node) { 29 | return __syscall_return(SYSCALL3(getcpu, cpu, node, NULL)); 30 | } 31 | -------------------------------------------------------------------------------- /kernel/api/dirent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define DT_UNKNOWN 0 6 | #define DT_FIFO 1 7 | #define DT_CHR 2 8 | #define DT_DIR 4 9 | #define DT_BLK 6 10 | #define DT_REG 8 11 | #define DT_LNK 10 12 | #define DT_SOCK 12 13 | #define DT_WHT 14 14 | 15 | struct linux_dirent { 16 | unsigned long d_ino; // Inode number 17 | unsigned long d_off; 18 | unsigned short d_reclen; // Length of this linux_dirent 19 | char d_name[]; // Filename (null-terminated) 20 | // char pad; // Zero padding byte 21 | // char d_type; // File type 22 | }; 23 | 24 | struct linux_old_dirent { 25 | unsigned long d_ino; 26 | unsigned long d_offset; 27 | unsigned short d_namlen; 28 | char d_name[]; 29 | }; 30 | 31 | struct linux_dirent64 { 32 | uint64_t d_ino; 33 | int64_t d_off; 34 | unsigned short d_reclen; 35 | unsigned char d_type; 36 | char d_name[]; 37 | }; 38 | -------------------------------------------------------------------------------- /kernel/interrupts/asm.S: -------------------------------------------------------------------------------- 1 | #define ASM_FILE 2 | #include 3 | 4 | .text 5 | .globl isr_entry 6 | isr_entry: 7 | pushl %eax 8 | pushl %ebx 9 | pushl %ecx 10 | pushl %edx 11 | pushl %ebp 12 | pushl %esi 13 | pushl %edi 14 | pushl %ds 15 | pushl %es 16 | pushl %fs 17 | pushl %gs 18 | 19 | movw $KERNEL_DS, %ax 20 | movw %ax, %ds 21 | movw %ax, %es 22 | movw %ax, %fs 23 | movw %ax, %gs 24 | 25 | cld 26 | 27 | movl %esp, %eax 28 | pushl %eax 29 | 30 | call isr_handler 31 | 32 | addl $4, %esp # pop esp 33 | 34 | // falls through 35 | 36 | .globl do_iret 37 | do_iret: 38 | popl %gs 39 | popl %fs 40 | popl %es 41 | popl %ds 42 | popl %edi 43 | popl %esi 44 | popl %ebp 45 | popl %edx 46 | popl %ecx 47 | popl %ebx 48 | popl %eax 49 | 50 | addl $8, %esp # pop error_code and interrupt_num 51 | iret 52 | -------------------------------------------------------------------------------- /kernel/drivers/hid/ps2.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define PS2_DATA 0x60 6 | #define PS2_STATUS 0x64 7 | #define PS2_COMMAND 0x64 8 | 9 | #define PS2_READ_CONFIG 0x20 10 | #define PS2_WRITE_CONFIG 0x60 11 | #define PS2_DISABLE_PORT2 0xa7 12 | #define PS2_ENABLE_PORT2 0xa8 13 | #define PS2_TEST_CONTROLLER 0xaa 14 | #define PS2_DISABLE_PORT1 0xad 15 | #define PS2_ENABLE_PORT1 0xae 16 | 17 | #define PS2_TEST_PASSED 0x55 18 | #define PS2_ACK 0xfa 19 | 20 | #define PS2_INTERRUPT_PORT1 0x1 21 | #define PS2_INTERRUPT_PORT2 0x2 22 | #define PS2_TRANSLATE_SCANCODES 0x40 23 | 24 | #define PS2_MOUSE_ENABLE_PACKET_STREAMING 0xf4 25 | #define PS2_MOUSE_SET_DEFAULTS 0xf6 26 | 27 | static inline uint8_t ps2_read(uint8_t port) { 28 | while (!(in8(PS2_STATUS) & 1)) 29 | ; 30 | return in8(port); 31 | } 32 | 33 | static inline void ps2_write(uint8_t port, uint8_t data) { 34 | while (in8(PS2_COMMAND) & 2) 35 | ; 36 | out8(port, data); 37 | } 38 | -------------------------------------------------------------------------------- /userland/lib/fcntl.c: -------------------------------------------------------------------------------- 1 | #include "fcntl.h" 2 | #include "private.h" 3 | #include 4 | 5 | int open(const char* pathname, int flags, ...) { 6 | unsigned mode = 0; 7 | if (flags & O_CREAT) { 8 | va_list args; 9 | va_start(args, flags); 10 | mode = va_arg(args, unsigned); 11 | va_end(args); 12 | } 13 | return __syscall_return(SYSCALL3(open, pathname, flags, mode)); 14 | } 15 | 16 | int creat(const char* pathname, mode_t mode) { 17 | return __syscall_return(SYSCALL2(creat, pathname, mode)); 18 | } 19 | 20 | int fcntl(int fd, int cmd, ...) { 21 | va_list args; 22 | va_start(args, cmd); 23 | int arg = va_arg(args, int); 24 | va_end(args); 25 | return __syscall_return(SYSCALL3(fcntl, fd, cmd, arg)); 26 | } 27 | 28 | int fcntl64(int fd, int cmd, ...) { 29 | va_list args; 30 | va_start(args, cmd); 31 | int arg = va_arg(args, int); 32 | va_end(args); 33 | return __syscall_return(SYSCALL3(fcntl64, fd, cmd, arg)); 34 | } 35 | -------------------------------------------------------------------------------- /userland/lib/signal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | extern const char* const sys_signame[]; 7 | extern const char* const sys_siglist[]; 8 | 9 | int kill(pid_t pid, int sig); 10 | int raise(int sig); 11 | 12 | sighandler_t signal(int signum, sighandler_t handler); 13 | int sigaction(int signum, const struct sigaction* act, 14 | struct sigaction* oldact); 15 | int sigprocmask(int how, const sigset_t* set, sigset_t* oldset); 16 | int sigsuspend(const sigset_t* mask); 17 | int sigpending(sigset_t* set); 18 | 19 | int sigemptyset(sigset_t* set); 20 | int sigfillset(sigset_t* set); 21 | 22 | int sigaddset(sigset_t* set, int signum); 23 | int sigdelset(sigset_t* set, int signum); 24 | int sigorset(sigset_t* dest, const sigset_t* left, const sigset_t* right); 25 | int sigandset(sigset_t* dest, const sigset_t* left, const sigset_t* right); 26 | 27 | int sigismember(const sigset_t* set, int signum); 28 | int sigisemptyset(const sigset_t* set); 29 | -------------------------------------------------------------------------------- /userland/lib/time.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define CLOCKS_PER_SEC 1000000 6 | 7 | struct tm { 8 | int tm_sec; 9 | int tm_min; 10 | int tm_hour; 11 | int tm_mday; 12 | int tm_mon; 13 | int tm_year; 14 | int tm_wday; 15 | int tm_yday; 16 | int tm_isdst; 17 | }; 18 | 19 | clock_t clock(void); 20 | 21 | time_t time(time_t* tloc); 22 | int stime(const time_t* t); 23 | 24 | double difftime(time_t time1, time_t time0); 25 | 26 | struct tm* gmtime_r(time_t const* t, struct tm* tm); 27 | char* asctime_r(const struct tm* time_ptr, char* buf); 28 | 29 | int nanosleep(const struct timespec* req, struct timespec* rem); 30 | 31 | int clock_gettime(clockid_t clockid, struct timespec* tp); 32 | int clock_settime(clockid_t clockid, const struct timespec* tp); 33 | int clock_getres(clockid_t clockid, struct timespec* res); 34 | int clock_nanosleep(clockid_t clockid, int flags, 35 | const struct timespec* request, struct timespec* remain); 36 | -------------------------------------------------------------------------------- /kernel/api/linux/keyboard.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define KG_SHIFT 0 4 | #define KG_CTRL 2 5 | #define KG_ALT 3 6 | #define KG_ALTGR 1 7 | #define KG_SHIFTL 4 8 | #define KG_KANASHIFT 4 9 | #define KG_SHIFTR 5 10 | #define KG_CTRLL 6 11 | #define KG_CTRLR 7 12 | #define KG_CAPSSHIFT 8 13 | 14 | #define NR_KEYS 256 15 | #define MAX_NR_KEYMAPS 256 16 | 17 | #define MAX_NR_FUNC 256 18 | 19 | #define K(t, v) (((t) << 8) | (v)) 20 | #define KTYP(x) ((x) >> 8) 21 | #define KVAL(x) ((x) & 0xff) 22 | 23 | #define K_HOLE K(KT_SPEC, 0) 24 | #define K_ALLOCATED K(KT_SPEC, 126) // dynamically allocated keymap 25 | #define K_NOSUCHMAP K(KT_SPEC, 127) 26 | 27 | #define KT_LATIN 0 28 | #define KT_FN 1 29 | #define KT_SPEC 2 30 | #define KT_PAD 3 31 | #define KT_DEAD 4 32 | #define KT_CONS 5 33 | #define KT_CUR 6 34 | #define KT_SHIFT 7 35 | #define KT_META 8 36 | #define KT_ASCII 9 37 | #define KT_LOCK 10 38 | #define KT_LETTER 11 39 | #define KT_SLOCK 12 40 | #define KT_DEAD2 13 41 | #define KT_BRL 14 42 | 43 | #define MAX_DIACR 256 44 | -------------------------------------------------------------------------------- /docs/cmdline.md: -------------------------------------------------------------------------------- 1 | # Kernel command-line parameters 2 | 3 | The kernel command-line parameters are in the form of `key=value`, where the `=value` part is optional depending on the parameter. Multiple parameters are separated by spaces. 4 | 5 | - `init=`: The path to the init program. If not specified or the kernel is not able to execute the specified program, it will try the following paths in order: 6 | - `/sbin/init` 7 | - `/etc/init` 8 | - `/bin/init` 9 | - `/bin/sh` 10 | - `panic=`: Defines the behavior of the kernel when a panic occurs. 11 | - `timeout` > `0`: Wait for the specified number of seconds and then reboot the system. 12 | - `timeout` = `0`: Halt the system. 13 | - `timeout` < `0`: Reboot the system. 14 | - `timeout` = `poweroff`: Power off the system. 15 | - `console=`: The device to use as the system console `/dev/console`. The default is `tty1`. 16 | - `nosmp`: Disables symmetric multiprocessing. 17 | - `ni_syscall_log`: Log a message when an unimplemented system call is invoked. 18 | -------------------------------------------------------------------------------- /kernel/fs/proc/private.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #define PROC_ROOT_INO 1 7 | #define PROC_PID_INO_SHIFT 10 8 | 9 | typedef int (*proc_print_fn)(struct file*, struct vec*); 10 | 11 | struct proc_entry { 12 | const char* name; 13 | mode_t mode; 14 | proc_print_fn print; 15 | }; 16 | 17 | struct inode* proc_create_inode(struct mount*, ino_t, struct proc_entry*); 18 | struct inode* proc_lookup(struct inode* parent, const char* name, 19 | struct proc_entry* entries, size_t num_entries); 20 | int proc_getdents(struct file*, getdents_callback_fn, void* ctx, 21 | const struct proc_entry* entries, size_t num_entries); 22 | 23 | struct inode* proc_root_lookup(struct inode* parent, const char* name); 24 | int proc_root_getdents(struct file*, getdents_callback_fn, void* ctx); 25 | 26 | struct inode* proc_pid_lookup(struct inode* parent, const char* name); 27 | int proc_pid_getdents(struct file*, getdents_callback_fn, void* ctx); 28 | -------------------------------------------------------------------------------- /kernel/api/sys/sysinfo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct sysinfo { 6 | long uptime; /* Seconds since boot */ 7 | unsigned long loads[3]; /* 1, 5, and 15 minute load averages */ 8 | unsigned long totalram; /* Total usable main memory size */ 9 | unsigned long freeram; /* Available memory size */ 10 | unsigned long sharedram; /* Amount of shared memory */ 11 | unsigned long bufferram; /* Memory used by buffers */ 12 | unsigned long totalswap; /* Total swap space size */ 13 | unsigned long freeswap; /* swap space still available */ 14 | uint16_t procs; /* Number of current processes */ 15 | uint16_t pad; /* Explicit padding for m68k */ 16 | unsigned long totalhigh; /* Total high memory size */ 17 | unsigned long freehigh; /* Available high memory size */ 18 | uint32_t mem_unit; /* Memory unit size in bytes */ 19 | char _f[20 - 2 * sizeof(unsigned long) - 20 | sizeof(uint32_t)]; /* Padding: libc5 uses this.. */ 21 | }; 22 | -------------------------------------------------------------------------------- /kernel/sched.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | struct task; 9 | struct registers; 10 | struct timespec; 11 | 12 | void sched_init(void); 13 | 14 | // Registers a task to be scheduled. 15 | void sched_register(struct task*); 16 | 17 | // Starts the scheduler on the current CPU. 18 | noreturn void sched_start(void); 19 | 20 | // Yields the current CPU to other tasks. 21 | void sched_yield(bool requeue_current); 22 | 23 | // Should be called on every timer tick. 24 | void sched_tick(struct registers*); 25 | 26 | #define BLOCK_UNINTERRUPTIBLE 1 27 | 28 | // Returns true if the task should be unblocked. 29 | typedef bool (*unblock_fn)(void*); 30 | 31 | // Blocks the current task until the unblock function returns true. 32 | // Returns -EINTR if the task was interrupted. 33 | NODISCARD int sched_block(unblock_fn, void* data, int flags); 34 | 35 | // Blocks the current task for the specified duration. 36 | void sched_sleep(const struct timespec*); 37 | -------------------------------------------------------------------------------- /kernel/fs/private.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fs.h" 4 | 5 | struct vec; 6 | 7 | void path_init(void); 8 | void filemap_init(void); 9 | void vfs_init(const multiboot_module_t* initrd_mod); 10 | void pipe_init(void); 11 | 12 | extern const struct file_ops pipe_fops; 13 | 14 | struct filemap { 15 | struct inode* inode; 16 | struct page* pages; 17 | }; 18 | 19 | struct filemap* filemap_create(struct inode*); 20 | void filemap_destroy(struct filemap*); 21 | 22 | // Ensures that the page at the given index exists in the filemap. 23 | // If `write` is true, the page is created even if it's outside the current size 24 | // of the file. 25 | struct page* filemap_ensure_page(struct filemap*, size_t index, bool write); 26 | 27 | // Writes back all dirty pages in the given range of indices. 28 | NODISCARD int filemap_sync(struct filemap*, size_t start, size_t end); 29 | 30 | // Truncates the filemap to the given length. 31 | NODISCARD int filemap_truncate(struct filemap*, uint64_t length); 32 | 33 | int proc_print_mounts(struct file*, struct vec*); 34 | -------------------------------------------------------------------------------- /kernel/interrupts/i8259.c: -------------------------------------------------------------------------------- 1 | #include "interrupts.h" 2 | 3 | #define PIC1_CMD 0x20 4 | #define PIC1_DATA 0x21 5 | #define PIC2_CMD 0xa0 6 | #define PIC2_DATA 0xa1 7 | 8 | #define PIC_EOI 0x20 9 | 10 | #define ICW1_ICW4 0x01 11 | #define ICW1_INIT 0x10 12 | #define ICW4_8086 0x01 13 | 14 | #define NUM_IRQS_PER_PIC 8 15 | 16 | static void remap_pic(void) { 17 | out8(PIC1_CMD, ICW1_INIT | ICW1_ICW4); 18 | out8(PIC2_CMD, ICW1_INIT | ICW1_ICW4); 19 | out8(PIC1_DATA, IRQ(0)); 20 | out8(PIC2_DATA, IRQ(NUM_IRQS_PER_PIC)); 21 | out8(PIC1_DATA, 4); 22 | out8(PIC2_DATA, 2); 23 | out8(PIC1_DATA, ICW4_8086); 24 | out8(PIC2_DATA, ICW4_8086); 25 | out8(PIC1_DATA, 0); 26 | out8(PIC2_DATA, 0); 27 | } 28 | 29 | void i8259_init(void) { remap_pic(); } 30 | 31 | void i8259_disable(void) { 32 | out8(PIC1_DATA, 0xff); 33 | out8(PIC2_DATA, 0xff); 34 | } 35 | 36 | void i8259_eoi(uint8_t irq) { 37 | ASSERT(irq < NUM_IRQS); 38 | if (irq >= NUM_IRQS_PER_PIC) 39 | out8(PIC2_CMD, PIC_EOI); 40 | out8(PIC1_CMD, PIC_EOI); 41 | } 42 | -------------------------------------------------------------------------------- /userland/lib/sys/socket.c: -------------------------------------------------------------------------------- 1 | #include "socket.h" 2 | #include 3 | 4 | int socket(int domain, int type, int protocol) { 5 | return __syscall_return(SYSCALL3(socket, domain, type, protocol)); 6 | } 7 | 8 | int bind(int sockfd, const struct sockaddr* addr, socklen_t addrlen) { 9 | return __syscall_return(SYSCALL3(bind, sockfd, addr, addrlen)); 10 | } 11 | 12 | int listen(int sockfd, int backlog) { 13 | return __syscall_return(SYSCALL2(listen, sockfd, backlog)); 14 | } 15 | 16 | int accept(int sockfd, struct sockaddr* addr, socklen_t* addrlen) { 17 | return accept4(sockfd, addr, addrlen, 0); 18 | } 19 | 20 | int accept4(int sockfd, struct sockaddr* addr, socklen_t* addrlen, int flags) { 21 | return __syscall_return(SYSCALL4(accept4, sockfd, addr, addrlen, flags)); 22 | } 23 | 24 | int connect(int sockfd, const struct sockaddr* addr, socklen_t addrlen) { 25 | return __syscall_return(SYSCALL3(connect, sockfd, addr, addrlen)); 26 | } 27 | 28 | int shutdown(int sockfd, int how) { 29 | return __syscall_return(SYSCALL2(shutdown, sockfd, how)); 30 | } 31 | -------------------------------------------------------------------------------- /common/string.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | void* memset(void* s, int c, size_t n); 6 | void* memcpy(void* dest_ptr, const void* src_ptr, size_t n); 7 | void* memmove(void* dest_ptr, const void* src_ptr, size_t n); 8 | int memcmp(const void* s1, const void* s2, size_t n); 9 | void* memchr(const void* s, int c, size_t n); 10 | 11 | int strcmp(const char* s1, const char* s2); 12 | int strncmp(const char* s1, const char* s2, size_t n); 13 | 14 | size_t strlen(const char* str); 15 | size_t strnlen(const char* str, size_t n); 16 | 17 | char* strcpy(char* dest, const char* src); 18 | char* strncpy(char* dest, const char* src, size_t n); 19 | size_t strlcpy(char* dst, const char* src, size_t size); 20 | 21 | char* strcat(char* dest, const char* src); 22 | char* strncat(char* dest, const char* src, size_t n); 23 | 24 | char* strchr(const char* str, int ch); 25 | char* strrchr(char const* str, int ch); 26 | char* strstr(const char* str, const char* substr); 27 | 28 | char* strtok(char* str, char const* sep); 29 | char* strtok_r(char* str, const char* sep, char** last); 30 | -------------------------------------------------------------------------------- /userland/ln.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static void usage(void) { 7 | dprintf(STDERR_FILENO, "Usage: ln TARGET LINK_NAME\n"); 8 | exit(EXIT_FAILURE); 9 | } 10 | 11 | int main(int argc, char* argv[]) { 12 | if (argc < 3) 13 | usage(); 14 | 15 | const char* target = NULL; 16 | const char* link_name = NULL; 17 | bool symbolic = false; 18 | for (int i = 1; i < argc; i++) { 19 | const char* arg = argv[i]; 20 | if (!strcmp(arg, "-s")) 21 | symbolic = true; 22 | else if (!target) 23 | target = arg; 24 | else if (!link_name) 25 | link_name = arg; 26 | else 27 | usage(); 28 | } 29 | 30 | if (symbolic) { 31 | if (symlink(target, link_name) < 0) { 32 | perror("symlink"); 33 | return EXIT_FAILURE; 34 | } 35 | } else if (link(target, link_name) < 0) { 36 | perror("link"); 37 | return EXIT_FAILURE; 38 | } 39 | return EXIT_SUCCESS; 40 | } 41 | -------------------------------------------------------------------------------- /kernel/linker.ld: -------------------------------------------------------------------------------- 1 | ENTRY (_start) 2 | 3 | PHDRS { 4 | multiboot PT_LOAD; 5 | init_text PT_LOAD; 6 | init_bss PT_LOAD; 7 | text PT_LOAD; 8 | data PT_LOAD; 9 | bss PT_LOAD; 10 | } 11 | 12 | KERNEL_VIRT_ADDR = 0xc0000000; 13 | 14 | SECTIONS { 15 | . = 0x100000; 16 | 17 | .multiboot ALIGN(4K) : AT(ADDR(.multiboot)) { 18 | KEEP(*(.multiboot)) 19 | } :multiboot 20 | 21 | .init_text : AT(ADDR(.init_text)) { 22 | *(.init_text) 23 | } :init_text 24 | 25 | .init_bss ALIGN(4K) : AT(ADDR(.init_bss)) { 26 | *(.init_bss) 27 | } :init_bss 28 | 29 | init_end = .; 30 | 31 | . += KERNEL_VIRT_ADDR; 32 | 33 | .text ALIGN(4K) : AT(ADDR(.text) - KERNEL_VIRT_ADDR) { 34 | *(.text) 35 | } :text 36 | 37 | .rodata ALIGN (4K) : AT(ADDR(.rodata) - KERNEL_VIRT_ADDR) { 38 | *(.rodata) 39 | } :data 40 | 41 | .data ALIGN (4K) : AT(ADDR(.data) - KERNEL_VIRT_ADDR) { 42 | *(.data) 43 | } :data 44 | 45 | .bss ALIGN(4K) : AT(ADDR(.bss) - KERNEL_VIRT_ADDR) { 46 | *(COMMON) 47 | *(.bss) 48 | } :bss 49 | 50 | kernel_end = .; 51 | } 52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License Copyright (c) 2024 Yuta Imazu 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice (including the next 11 | paragraph) shall be included in all copies or substantial portions of the 12 | Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 17 | OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 18 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF 19 | OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /userland/lib/sys/stat.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct stat { 7 | dev_t st_dev; /* ID of device containing file */ 8 | ino_t st_ino; /* Inode number */ 9 | mode_t st_mode; /* File type and mode */ 10 | nlink_t st_nlink; /* Number of hard links */ 11 | uid_t st_uid; /* User ID of owner */ 12 | gid_t st_gid; /* Group ID of owner */ 13 | dev_t st_rdev; /* Device ID (if special file) */ 14 | off_t st_size; /* Total size, in bytes */ 15 | blksize_t st_blksize; /* Block size for filesystem I/O */ 16 | blkcnt_t st_blocks; /* Number of 512 B blocks allocated */ 17 | 18 | struct timespec st_atim; /* Time of last access */ 19 | struct timespec st_mtim; /* Time of last modification */ 20 | struct timespec st_ctim; /* Time of last status change */ 21 | }; 22 | 23 | int stat(const char* pathname, struct stat* buf); 24 | int lstat(const char* pathname, struct stat* buf); 25 | int fstat(int fd, struct stat* buf); 26 | int mkdir(const char* pathname, mode_t mode); 27 | int mkfifo(const char* pathname, mode_t mode); 28 | -------------------------------------------------------------------------------- /userland/fib.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define MAX_N 45 7 | 8 | static uint32_t recursion(uint32_t n) { 9 | if (n < 2) 10 | return n; 11 | return recursion(n - 1) + recursion(n - 2); 12 | } 13 | 14 | static uint32_t memo[MAX_N + 1]; 15 | 16 | static uint32_t memoized_recursion(uint32_t n) { 17 | if (memo[n]) 18 | return memo[n]; 19 | if (n < 2) 20 | return memo[n] = n; 21 | return memo[n] = memoized_recursion(n - 1) + memoized_recursion(n - 2); 22 | } 23 | 24 | static void measure_and_report(uint32_t (*func)(uint32_t)) { 25 | for (size_t i = 30; i <= MAX_N; ++i) { 26 | clock_t start = clock(); 27 | uint32_t res = func(i); 28 | clock_t end = clock(); 29 | double elapsed = (double)((end - start) * 1000) / CLOCKS_PER_SEC; 30 | printf("%u%6u ms: %u\n", i, (unsigned)elapsed, res); 31 | } 32 | } 33 | 34 | int main(void) { 35 | puts("memoized recursion:"); 36 | measure_and_report(memoized_recursion); 37 | 38 | puts("recursion:"); 39 | measure_and_report(recursion); 40 | 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-24.04 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | compiler: [gcc, clang] 12 | env: 13 | CC: ${{ matrix.compiler }} 14 | steps: 15 | - uses: actions/checkout@v4 16 | - run: sudo apt-get update && sudo apt-get install -y gcc-multilib cpio qemu-system-x86 grub2 mtools xorriso 17 | - run: make EXTRA_CFLAGS="-Werror" 18 | - run: nm -n kernel/kernel | awk 'NF==3' 19 | - run: make test 20 | timeout-minutes: 3 21 | - run: grub-file --is-x86-multiboot kernel/kernel 22 | - run: make disk_image 23 | ubsan: 24 | runs-on: ubuntu-24.04 25 | env: 26 | CC: gcc 27 | steps: 28 | - uses: actions/checkout@v4 29 | - run: sudo apt-get update && sudo apt-get install -y gcc-multilib cpio qemu-system-x86 30 | - run: make EXTRA_CFLAGS="-Werror -fsanitize=undefined" 31 | - run: make test 32 | timeout-minutes: 5 33 | -------------------------------------------------------------------------------- /common/ctype.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | static inline int isprint(int c) { return (int)(0x20 <= c && c <= 0x7e); } 4 | 5 | static inline int isspace(int c) { 6 | switch (c) { 7 | case '\t': 8 | case '\n': 9 | case '\v': 10 | case '\f': 11 | case '\r': 12 | case ' ': 13 | return 1; 14 | } 15 | return 0; 16 | } 17 | 18 | static inline int isgraph(int c) { return (int)(0x21 <= c && c <= 0x7e); } 19 | 20 | static inline int isdigit(int c) { return (int)('0' <= c && c <= '9'); } 21 | 22 | static inline int isalnum(int c) { 23 | if (isdigit(c)) 24 | return 1; 25 | if ('A' <= c && c <= 'Z') 26 | return 1; 27 | return 'a' <= c && c <= 'z'; 28 | } 29 | 30 | static inline int isxdigit(int c) { 31 | if (isdigit(c)) 32 | return 1; 33 | if ('A' <= c && c <= 'F') 34 | return 1; 35 | return 'a' <= c && c <= 'f'; 36 | } 37 | 38 | static inline int isascii(int c) { return (unsigned)c <= 127; } 39 | 40 | static inline int tolower(int c) { 41 | if ('A' <= c && c <= 'Z') 42 | return c | 0x20; 43 | return c; 44 | } 45 | 46 | static inline int toupper(int c) { 47 | if ('a' <= c && c <= 'z') 48 | return c & ~0x20; 49 | return c; 50 | } 51 | -------------------------------------------------------------------------------- /userland/kill.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static void usage(void) { 8 | dprintf(STDERR_FILENO, "Usage: kill [-SIGNUM] PID\n"); 9 | exit(EXIT_FAILURE); 10 | } 11 | 12 | static int parse_signum(const char* s) { 13 | if (str_is_uint(s)) 14 | return atoi(s); 15 | for (int i = 0; i < NSIG; ++i) { 16 | if (!strcmp(s, sys_signame[i])) 17 | return i; 18 | } 19 | return -1; 20 | } 21 | 22 | int main(int argc, char* argv[]) { 23 | if (argc < 2) 24 | usage(); 25 | 26 | pid_t pid = -1; 27 | int signum = SIGTERM; 28 | for (int i = 1; i < argc; ++i) { 29 | const char* arg = argv[i]; 30 | if (arg[0] == '-') { 31 | signum = parse_signum(arg + 1); 32 | if (signum < 0 || signum >= NSIG) 33 | usage(); 34 | } else if (str_is_uint(arg)) { 35 | pid = atoi(arg); 36 | } else { 37 | usage(); 38 | } 39 | } 40 | if (pid < 0) 41 | usage(); 42 | 43 | if (kill(pid, signum) < 0) { 44 | perror("kill"); 45 | return EXIT_FAILURE; 46 | } 47 | return EXIT_SUCCESS; 48 | } 49 | -------------------------------------------------------------------------------- /userland/cal.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(void) { 7 | time_t now; 8 | if (time(&now) < 0) { 9 | perror("time"); 10 | return EXIT_FAILURE; 11 | } 12 | struct tm tm; 13 | gmtime_r(&now, &tm); 14 | 15 | static const char* month_names[] = { 16 | "Jan", "Feb", "Mar", "Apr", "May", "Jun", 17 | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 18 | }; 19 | printf(" %s %d\nSu Mo Tu We Th Fr Sa\n", month_names[tm.tm_mon], 20 | tm.tm_year + 1900); 21 | 22 | int wday_of_first_day_of_this_month = 23 | (tm.tm_wday - tm.tm_mday + 7 * 5 + 1) % 7; 24 | for (int i = 0; i < wday_of_first_day_of_this_month; ++i) 25 | printf(" "); 26 | 27 | int days_in_this_month = days_in_month(tm.tm_year + 1900, tm.tm_mon + 1); 28 | int wday = wday_of_first_day_of_this_month; 29 | for (int mday = 1; mday <= days_in_this_month; ++mday) { 30 | if (++wday >= 7) { 31 | printf("%2d\n", mday); 32 | wday = 0; 33 | } else { 34 | printf("%2d ", mday); 35 | } 36 | } 37 | if (wday > 0) 38 | putchar('\n'); 39 | 40 | return EXIT_SUCCESS; 41 | } 42 | -------------------------------------------------------------------------------- /kernel/drivers/graphics/multiboot.c: -------------------------------------------------------------------------------- 1 | #include "graphics.h" 2 | #include 3 | #include 4 | #include 5 | 6 | static struct fb_info info; 7 | 8 | static int multiboot_fb_get_info(struct fb_info* out_info) { 9 | *out_info = info; 10 | return 0; 11 | } 12 | 13 | static int multiboot_fb_set_info(struct fb_info* inout_info) { 14 | (void)inout_info; 15 | return -ENOTSUP; 16 | } 17 | 18 | struct fb* multiboot_fb_init(const multiboot_info_t* mb_info) { 19 | if (!(mb_info->flags & MULTIBOOT_INFO_FRAMEBUFFER_INFO)) 20 | return NULL; 21 | if (mb_info->framebuffer_type != MULTIBOOT_FRAMEBUFFER_TYPE_RGB) 22 | return NULL; 23 | 24 | info = (struct fb_info){ 25 | .id = "multiboot", 26 | .phys_addr = mb_info->framebuffer_addr, 27 | .width = mb_info->framebuffer_width, 28 | .height = mb_info->framebuffer_height, 29 | .pitch = mb_info->framebuffer_pitch, 30 | .bpp = mb_info->framebuffer_bpp, 31 | }; 32 | kprintf("multiboot_fb: found framebuffer at P%#x\n", info.phys_addr); 33 | 34 | static struct fb fb = { 35 | .get_info = multiboot_fb_get_info, 36 | .set_info = multiboot_fb_set_info, 37 | }; 38 | return &fb; 39 | } 40 | -------------------------------------------------------------------------------- /userland/cat.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define BUF_SIZE 1024 8 | 9 | static int dump_file(const char* filename) { 10 | int fd = strcmp(filename, "-") ? open(filename, O_RDONLY) : STDIN_FILENO; 11 | if (fd < 0) { 12 | perror("open"); 13 | return -1; 14 | } 15 | for (;;) { 16 | static char buf[BUF_SIZE]; 17 | ssize_t nread = read(fd, buf, BUF_SIZE); 18 | if (nread < 0) { 19 | perror("read"); 20 | if (fd != STDIN_FILENO) 21 | close(fd); 22 | return -1; 23 | } 24 | if (nread == 0) 25 | break; 26 | if (write(STDOUT_FILENO, buf, nread) < 0) { 27 | perror("write"); 28 | return -1; 29 | } 30 | } 31 | if (fd != STDIN_FILENO) 32 | close(fd); 33 | return 0; 34 | } 35 | 36 | int main(int argc, char* argv[]) { 37 | int ret = EXIT_SUCCESS; 38 | if (argc < 2) { 39 | if (dump_file("-") < 0) 40 | ret = EXIT_FAILURE; 41 | } 42 | for (int i = 1; i < argc; ++i) { 43 | if (dump_file(argv[i]) < 0) 44 | ret = EXIT_FAILURE; 45 | } 46 | return ret; 47 | } 48 | -------------------------------------------------------------------------------- /userland/cp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define BUF_SIZE 1024 7 | 8 | int main(int argc, char* argv[]) { 9 | if (argc != 3) { 10 | dprintf(STDERR_FILENO, "Usage: cp SOURCE DEST\n"); 11 | return EXIT_FAILURE; 12 | } 13 | 14 | int src_fd = open(argv[1], O_RDONLY); 15 | if (src_fd < 0) { 16 | perror("open"); 17 | return EXIT_FAILURE; 18 | } 19 | int dest_fd = open(argv[2], O_CREAT | O_TRUNC | O_WRONLY); 20 | if (dest_fd < 0) { 21 | perror("open"); 22 | return EXIT_FAILURE; 23 | } 24 | 25 | for (;;) { 26 | static char buf[BUF_SIZE]; 27 | ssize_t nread = read(src_fd, buf, BUF_SIZE); 28 | if (nread < 0) { 29 | perror("read"); 30 | close(src_fd); 31 | close(dest_fd); 32 | return EXIT_FAILURE; 33 | } 34 | if (nread == 0) 35 | break; 36 | if (write(dest_fd, buf, nread) < 0) { 37 | perror("write"); 38 | close(src_fd); 39 | close(dest_fd); 40 | return EXIT_FAILURE; 41 | } 42 | } 43 | 44 | close(src_fd); 45 | close(dest_fd); 46 | 47 | return EXIT_SUCCESS; 48 | } 49 | -------------------------------------------------------------------------------- /kernel/acpi.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct ics_header { 7 | uint8_t type; 8 | uint8_t length; 9 | } __attribute__((packed)); 10 | 11 | #define ACPI_LOCAL_APIC_ENABLED 0x1 12 | #define ACPI_LOCAL_APIC_ONLINE_CAPABLE 0x2 13 | 14 | struct local_apic { 15 | struct ics_header header; 16 | uint8_t acpi_processor_uid; 17 | uint8_t apic_id; 18 | uint32_t flags; 19 | } __attribute__((packed)); 20 | 21 | struct io_apic { 22 | struct ics_header header; 23 | uint8_t io_apic_id; 24 | uint8_t reserved; 25 | uint32_t io_apic_addr; 26 | uint32_t global_system_interrupt_base; 27 | } __attribute__((packed)); 28 | 29 | struct interrupt_source_override { 30 | struct ics_header header; 31 | uint8_t bus; 32 | uint8_t source; 33 | uint32_t global_system_interrupt; 34 | uint16_t flags; 35 | } __attribute__((packed)); 36 | 37 | struct acpi { 38 | uintptr_t lapic_addr; 39 | 40 | // Null-terminated arrays of pointers to the respective structures 41 | const struct local_apic** local_apics; 42 | const struct io_apic** io_apics; 43 | const struct interrupt_source_override** interrupt_source_overrides; 44 | }; 45 | 46 | void acpi_init(void); 47 | const struct acpi* acpi_get(void); 48 | -------------------------------------------------------------------------------- /kernel/fs/path.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct path { 6 | // The inode associated with the path. 7 | // NULL if vfs_resolve_path was called with O_ALLOW_NOENT and 8 | // the last component of the path does not exist. 9 | struct inode* inode; 10 | 11 | // The basename of the path. NULL for the root path. 12 | char* basename; 13 | 14 | // The parent of the path. NULL for the root path. 15 | struct path* parent; 16 | }; 17 | 18 | // Creates a path representing the root directory. 19 | struct path* path_create_root(struct inode* root); 20 | 21 | // Returns a string representation of the path. 22 | char* path_to_string(const struct path*); 23 | 24 | // Returns a clone of the path. 25 | struct path* path_dup(const struct path*); 26 | 27 | // Joins a path with a basename associated with an inode. 28 | // 29 | // The parent is consumed by the function. 30 | struct path* path_join(struct path* parent, struct inode* inode, 31 | const char* basename); 32 | 33 | // Destroys the last component of the path, but not the parents. 34 | void path_destroy_last(struct path*); 35 | 36 | // Destroys the path and all its parents. 37 | void path_destroy_recursive(struct path*); 38 | 39 | DEFINE_FREE(path, struct path*, path_destroy_recursive) 40 | -------------------------------------------------------------------------------- /common/calendar.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | static inline bool is_leap_year(unsigned year) { 6 | return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); 7 | } 8 | 9 | static inline unsigned days_in_year(unsigned year) { 10 | return is_leap_year(year) ? 366 : 365; 11 | } 12 | 13 | // `month`: January is 1 14 | 15 | static inline unsigned days_in_month(unsigned year, unsigned month) { 16 | static const unsigned table[] = {31, 28, 31, 30, 31, 30, 17 | 31, 31, 30, 31, 30, 31}; 18 | if (month == 2 && is_leap_year(year)) 19 | return 29; 20 | return table[month - 1]; 21 | } 22 | 23 | static inline unsigned day_of_year(unsigned year, unsigned month, 24 | unsigned day) { 25 | unsigned result = day - 1; 26 | for (unsigned m = 1; m < month; ++m) 27 | result += days_in_month(year, m); 28 | return result; 29 | } 30 | 31 | static inline unsigned day_of_week(unsigned year, unsigned month, 32 | unsigned day) { 33 | // Zeller's congruence 34 | if (month <= 2) { 35 | --year; 36 | month += 12; 37 | } 38 | return (day + (13 * month + 8) / 5 + year + year / 4 - year / 100 + 39 | year / 400) % 40 | 7; 41 | } 42 | -------------------------------------------------------------------------------- /userland/getty.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(int argc, char* const argv[]) { 7 | if (argc != 2) { 8 | dprintf(STDERR_FILENO, "Usage: getty TTY\n"); 9 | return EXIT_FAILURE; 10 | } 11 | 12 | const char* tty_name = argv[1]; 13 | char tty_path[32]; 14 | int len = snprintf(tty_path, sizeof(tty_path), 15 | tty_name[0] == '/' ? "%s" : "/dev/%s", tty_name); 16 | if ((size_t)len >= sizeof(tty_path)) { 17 | dprintf(STDERR_FILENO, "TTY name too long\n"); 18 | return EXIT_FAILURE; 19 | } 20 | 21 | close(STDIN_FILENO); 22 | int fd = open(tty_path, O_RDWR); 23 | if (fd < 0) { 24 | perror("open"); 25 | return EXIT_FAILURE; 26 | } 27 | if (dup2(fd, STDOUT_FILENO) < 0) { 28 | perror("dup2"); 29 | return EXIT_FAILURE; 30 | } 31 | if (dup2(fd, STDERR_FILENO) < 0) { 32 | perror("dup2"); 33 | return EXIT_FAILURE; 34 | } 35 | 36 | if (tcsetpgrp(STDIN_FILENO, getpid()) < 0) { 37 | perror("tcsetpgrp"); 38 | return EXIT_FAILURE; 39 | } 40 | 41 | static char* shell = "/bin/sh"; 42 | execve(shell, (char* const[]){shell, NULL}, environ); 43 | perror("execve"); 44 | 45 | return EXIT_FAILURE; 46 | } 47 | -------------------------------------------------------------------------------- /kernel/console/font.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define MAX_FONT_WIDTH 64 6 | #define MAX_FONT_HEIGHT 128 7 | #define MAX_FONT_GLYPHS 512 8 | #define MAX_FONT_SIZE (MAX_FONT_GLYPHS * MAX_FONT_WIDTH * MAX_FONT_HEIGHT) 9 | 10 | extern struct font default_font; 11 | 12 | struct font_meta { 13 | size_t num_glyphs; 14 | 15 | // Glyph size in pixels 16 | size_t width, height; 17 | 18 | // Horizontal and vertical pitch in bytes 19 | // Each glyph takes up hpitch * vpitch bytes 20 | size_t hpitch, vpitch; 21 | }; 22 | 23 | struct font { 24 | struct font_meta meta; 25 | unsigned char* data; 26 | refcount_t refcount; 27 | }; 28 | 29 | static inline struct font* font_ref(struct font* font) { 30 | ASSERT(font); 31 | refcount_inc(&font->refcount); 32 | return font; 33 | } 34 | 35 | static inline void font_unref(struct font* font) { 36 | if (!font) 37 | return; 38 | if (refcount_dec(&font->refcount)) 39 | return; 40 | kfree(font->data); 41 | kfree(font); 42 | } 43 | 44 | DEFINE_FREE(font, struct font*, font_unref) 45 | 46 | // Returns the size in bytes of the font data 47 | static inline size_t font_size(const struct font* font) { 48 | const struct font_meta* meta = &font->meta; 49 | return meta->hpitch * meta->vpitch * meta->num_glyphs; 50 | } 51 | -------------------------------------------------------------------------------- /docs/gallery.md: -------------------------------------------------------------------------------- 1 | # Gallery 2 | 3 | ```sh 4 | imgview kodim23.qoi 5 | ``` 6 | 7 | ![](imgs/imgview.png) 8 | 9 | --- 10 | 11 | 12 | ```sh 13 | mandelbrot 14 | ``` 15 | 16 | ![](imgs/mandelbrot.png) 17 | 18 | --- 19 | 20 | ```sh 21 | eyes 22 | ``` 23 | 24 | ![](imgs/eyes.png) 25 | 26 | --- 27 | 28 | ``` 29 | /root $ sleep 10 & 30 | /root $ ps 31 | PID CMD 32 | 1 init 33 | 3 moused 34 | 5 sh 35 | 6 sh 36 | 7 sleep 37 | 8 ps 38 | /root $ kill 7 39 | ``` 40 | 41 | --- 42 | 43 | ``` 44 | /root $ cat invictus.txt 45 | Out of the night that covers me, 46 | Black as the pit from pole to pole, 47 | I thank whatever gods may be 48 | For my unconquerable soul. 49 | 50 | In the fell clutch of circumstance 51 | I have not winced nor cried aloud. 52 | Under the bludgeonings of chance 53 | My head is bloody, but unbowed. 54 | 55 | Beyond this place of wrath and tears 56 | Looms but the Horror of the shade, 57 | And yet the menace of the years 58 | Finds and shall find me unafraid. 59 | 60 | It matters not how strait the gate, 61 | How charged with punishments the scroll, 62 | I am the master of my fate, 63 | I am the captain of my soul. 64 | /root $ cat invictus.txt | wc 65 | 19 103 535 66 | ``` 67 | 68 | --- 69 | 70 | ``` 71 | /root $ play music.qoa 72 | [==========================================> ] 73 | ``` 74 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | MAKEFLAGS += --jobs=32 2 | SUBDIRS := kernel userland 3 | 4 | GIT_HASH := $(shell git describe --always --dirty --exclude '*' 2> /dev/null) 5 | 6 | export CFLAGS := \ 7 | -std=c11 \ 8 | -m32 \ 9 | -static \ 10 | -nostdlib -ffreestanding \ 11 | -fno-omit-frame-pointer \ 12 | -U_FORTIFY_SOURCE \ 13 | -Wall -Wextra -pedantic -Wno-gnu-statement-expression-from-macro-expansion \ 14 | -O2 -g \ 15 | $(if $(GIT_HASH),-DYAGURA_VERSION=\"$(GIT_HASH)\") \ 16 | $(EXTRA_CFLAGS) 17 | 18 | export LDFLAGS := \ 19 | -Wl,--build-id=none \ 20 | -Wl,-z,noexecstack 21 | 22 | .PHONY: all run clean $(SUBDIRS) base 23 | 24 | all: kernel initrd 25 | 26 | initrd: base 27 | find $< -mindepth 1 ! -name '.gitkeep' -printf "%P\n" | sort | cpio -oc -D $< -F $@ 28 | 29 | base: $@/* userland 30 | $(RM) -r $@/root/src 31 | -git -c advice.detachedHead=false clone . $@/root/src 32 | $(RM) -r $@/root/src/.git 33 | 34 | $(SUBDIRS): 35 | $(MAKE) -C $@ all 36 | 37 | disk_image: kernel initrd 38 | cp kernel/kernel initrd disk/boot 39 | grub-mkrescue -o '$@' disk -d /usr/lib/grub/i386-pc 40 | 41 | clean: 42 | for dir in $(SUBDIRS); do $(MAKE) -C $$dir $@; done 43 | $(RM) -r base/root/src 44 | $(RM) initrd disk_image disk/boot/kernel disk/boot/initrd 45 | 46 | run: kernel initrd 47 | ./run.sh 48 | 49 | shell: kernel initrd 50 | ./run.sh shell 51 | 52 | test: kernel initrd 53 | ./run_tests.sh 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # yagura 2 | 3 | [![build](https://github.com/mosmeh/yagura/workflows/build/badge.svg)](https://github.com/mosmeh/yagura/actions) 4 | 5 | A Unix-like operating system for x86 6 | 7 | ![](imgs/screenshot.png) 8 | 9 | See [Gallery](docs/gallery.md) for more screenshots. 10 | 11 | ## How to run 12 | 13 | First, install dependencies: 14 | 15 | ```sh 16 | # on Ubuntu 17 | sudo apt install gcc-multilib cpio qemu-system-x86 18 | ``` 19 | 20 | Then run the following command to build and run: 21 | 22 | ```sh 23 | make run 24 | ``` 25 | 26 | The following commands start the system with different options: 27 | 28 | ```sh 29 | make shell # run in text mode 30 | 31 | make test # run self-test 32 | ``` 33 | 34 | ## Installation on bare-metal 35 | 36 | You will need additional dependencies: 37 | 38 | ```sh 39 | # on Ubuntu 40 | sudo apt install grub2 mtools xorriso 41 | ``` 42 | 43 | The following command creates a disk image file called `disk_image`. You can simply copy it onto a disk and boot it. 44 | 45 | ```sh 46 | make disk_image 47 | ``` 48 | 49 | ## Inspirations and learning resources 50 | 51 | - [xv6](https://github.com/mit-pdos/xv6-public) 52 | - [SerenityOS](https://github.com/SerenityOS/serenity) 53 | - [ToaruOS](https://github.com/klange/toaruos) 54 | - [JamesM's kernel development tutorials](http://www.jamesmolloy.co.uk/tutorial_html/) 55 | - [OSDev Wiki](https://wiki.osdev.org/) 56 | -------------------------------------------------------------------------------- /userland/mknod.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static noreturn void usage(void) { 8 | dprintf(STDERR_FILENO, "Usage: mknod NAME TYPE [MAJOR MINOR]\n"); 9 | exit(EXIT_FAILURE); 10 | } 11 | 12 | int main(int argc, char* const argv[]) { 13 | if (argc < 3 || argc > 5) 14 | usage(); 15 | 16 | const char* filename = argv[1]; 17 | const char* type_str = argv[2]; 18 | if (!type_str[0] || type_str[1]) 19 | usage(); 20 | 21 | char type = type_str[0]; 22 | mode_t mode; 23 | dev_t dev = 0; 24 | 25 | switch (type) { 26 | case 'b': 27 | case 'c': 28 | case 'u': { 29 | if (argc != 5) 30 | usage(); 31 | if (!str_is_uint(argv[3]) || !str_is_uint(argv[4])) 32 | usage(); 33 | mode = type == 'b' ? S_IFBLK : S_IFCHR; 34 | int major = atoi(argv[3]); 35 | int minor = atoi(argv[4]); 36 | dev = makedev(major, minor); 37 | break; 38 | } 39 | case 'p': 40 | if (argc != 3) 41 | usage(); 42 | mode = S_IFIFO; 43 | break; 44 | default: 45 | usage(); 46 | } 47 | 48 | if (mknod(filename, mode, dev) < 0) { 49 | perror("mknod"); 50 | return EXIT_FAILURE; 51 | } 52 | 53 | return EXIT_SUCCESS; 54 | } 55 | -------------------------------------------------------------------------------- /userland/init-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | static int spawn(char* filename) { 14 | pid_t pid = fork(); 15 | if (pid < 0) { 16 | perror("fork"); 17 | abort(); 18 | } 19 | if (pid == 0) { 20 | char* argv[] = {filename, NULL}; 21 | static char* envp[] = {NULL}; 22 | if (execve(argv[0], argv, envp) < 0) { 23 | perror("execve"); 24 | abort(); 25 | } 26 | } 27 | return waitpid(pid, NULL, 0); 28 | } 29 | 30 | int main(void) { 31 | ASSERT(getpid() == 1); 32 | 33 | ASSERT_OK(mount("tmpfs", "/dev", "tmpfs", 0, NULL)); 34 | 35 | ASSERT_OK(mknod("/dev/console", S_IFCHR, makedev(TTYAUX_MAJOR, 1))); 36 | int fd = open("/dev/console", O_RDWR); 37 | ASSERT(fd == STDIN_FILENO); 38 | ASSERT_OK(dup2(fd, STDOUT_FILENO)); 39 | ASSERT_OK(dup2(fd, STDERR_FILENO)); 40 | 41 | ASSERT_OK(mkdir("/dev/shm", 0)); 42 | ASSERT_OK(mount("tmpfs", "/dev/shm", "tmpfs", 0, NULL)); 43 | 44 | ASSERT_OK(spawn("/bin/usertests")); 45 | ASSERT_OK(spawn("/bin/xv6-usertests")); 46 | 47 | reboot(RB_POWER_OFF); 48 | UNREACHABLE(); 49 | } 50 | -------------------------------------------------------------------------------- /userland/lib/sys/select.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "time.h" 4 | 5 | #define FD_SETSIZE 1024 6 | #define __NFDBITS (8 * sizeof(long)) 7 | 8 | typedef struct { 9 | unsigned long fds_bits[FD_SETSIZE / __NFDBITS]; 10 | } fd_set; 11 | 12 | #define FD_CLR(fd, set) \ 13 | ((set)->fds_bits[(fd) / __NFDBITS] &= ~(1U << ((fd) % __NFDBITS))) 14 | 15 | #define FD_SET(fd, set) \ 16 | ((set)->fds_bits[(fd) / __NFDBITS] |= (1U << ((fd) % __NFDBITS))) 17 | 18 | #define FD_ISSET(fd, set) \ 19 | ((set)->fds_bits[(fd) / __NFDBITS] & (1U << ((fd) % __NFDBITS))) 20 | 21 | #define FD_ZERO(set) \ 22 | do { \ 23 | fd_set* __set = (set); \ 24 | for (int __i = 0; __i < sizeof(__set) / sizeof(__set->fds_bits[0]); \ 25 | ++__i) \ 26 | __set->fds_bits[__i] = 0; \ 27 | } while (0) 28 | 29 | int select(int nfds, fd_set* restrict readfds, fd_set* restrict writefds, 30 | fd_set* restrict exceptfds, struct timeval* restrict timeout); 31 | -------------------------------------------------------------------------------- /userland/lib/private.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | extern size_t __tls_size; 9 | struct pthread* __init_tls(void* tls); 10 | 11 | struct pthread { 12 | struct pthread* self; 13 | volatile atomic_uint state; 14 | void* alloc_base; 15 | void* (*fn)(void*); 16 | void* arg; 17 | pid_t tid; 18 | void* retval; 19 | }; 20 | 21 | int syscall(int num, int, int, int, int, int, int); 22 | 23 | #define SYSCALL0(name) SYSCALL1(name, 0) 24 | #define SYSCALL1(name, arg1) SYSCALL2(name, arg1, 0) 25 | #define SYSCALL2(name, arg1, arg2) SYSCALL3(name, arg1, arg2, 0) 26 | #define SYSCALL3(name, arg1, arg2, arg3) SYSCALL4(name, arg1, arg2, arg3, 0) 27 | #define SYSCALL4(name, arg1, arg2, arg3, arg4) \ 28 | SYSCALL5(name, arg1, arg2, arg3, arg4, 0) 29 | #define SYSCALL5(name, arg1, arg2, arg3, arg4, arg5) \ 30 | SYSCALL6(name, arg1, arg2, arg3, arg4, arg5, 0) 31 | #define SYSCALL6(name, arg1, arg2, arg3, arg4, arg5, arg6) \ 32 | syscall(SYS_##name, (int)(arg1), (int)(arg2), (int)(arg3), (int)(arg4), \ 33 | (int)(arg5), (int)(arg6)) 34 | 35 | uintptr_t __syscall_return(uintptr_t rc); 36 | 37 | int __clone(int (*fn)(void*), void* stack, int flags, void* arg, 38 | pid_t* parent_tid, void* tls, pid_t* child_tid); 39 | -------------------------------------------------------------------------------- /kernel/drivers/hid/ps2.c: -------------------------------------------------------------------------------- 1 | #include "ps2.h" 2 | #include 3 | 4 | void ps2_keyboard_init(void); 5 | void ps2_mouse_init(void); 6 | 7 | static void drain_output_buffer(void) { 8 | for (int timeout = 0; timeout < 1024; ++timeout) { 9 | if (!(in8(PS2_STATUS) & 1)) 10 | break; 11 | in8(PS2_DATA); 12 | } 13 | } 14 | 15 | static bool self_test(void) { 16 | for (int timeout = 0; timeout < 1024; ++timeout) { 17 | out8(PS2_COMMAND, PS2_TEST_CONTROLLER); 18 | if (in8(PS2_DATA) == PS2_TEST_PASSED) 19 | return true; 20 | } 21 | return false; 22 | } 23 | 24 | void ps2_init(void) { 25 | drain_output_buffer(); 26 | if (!self_test()) { 27 | kprint("ps2: PS/2 controller self-test failed. PS/2 controller is " 28 | "faulty or not present.\n"); 29 | return; 30 | } 31 | 32 | ps2_write(PS2_COMMAND, PS2_DISABLE_PORT1); 33 | ps2_write(PS2_COMMAND, PS2_DISABLE_PORT2); 34 | 35 | drain_output_buffer(); 36 | 37 | ps2_write(PS2_COMMAND, PS2_READ_CONFIG); 38 | uint8_t config = ps2_read(PS2_DATA); 39 | config |= 40 | PS2_INTERRUPT_PORT1 | PS2_INTERRUPT_PORT2 | PS2_TRANSLATE_SCANCODES; 41 | ps2_write(PS2_COMMAND, PS2_WRITE_CONFIG); 42 | ps2_write(PS2_DATA, config); 43 | 44 | drain_output_buffer(); 45 | ps2_keyboard_init(); 46 | 47 | drain_output_buffer(); 48 | ps2_mouse_init(); 49 | } 50 | -------------------------------------------------------------------------------- /userland/mount.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static void usage(void) { 8 | dprintf(STDERR_FILENO, "Usage: mount [-t fstype] SOURCE TARGET\n"); 9 | exit(EXIT_FAILURE); 10 | } 11 | 12 | int main(int argc, char* argv[]) { 13 | if (argc < 3) 14 | usage(); 15 | 16 | size_t num_positionals = 0; 17 | const char* source = NULL; 18 | const char* target = NULL; 19 | const char* fs_type = NULL; 20 | for (int i = 1; i < argc; ++i) { 21 | const char* arg = argv[i]; 22 | if (arg[0] != '-') { 23 | switch (num_positionals++) { 24 | case 0: 25 | source = arg; 26 | break; 27 | case 1: 28 | target = arg; 29 | break; 30 | default: 31 | usage(); 32 | } 33 | continue; 34 | } 35 | if (!strcmp(arg, "-t")) { 36 | fs_type = argv[++i]; 37 | continue; 38 | } 39 | usage(); 40 | } 41 | if (!source || !target) 42 | usage(); 43 | if (!fs_type) { 44 | dprintf(STDERR_FILENO, "you must specify the filesystem type\n"); 45 | return EXIT_FAILURE; 46 | } 47 | 48 | if (mount(source, target, fs_type, 0, NULL) < 0) { 49 | perror("mount"); 50 | return EXIT_FAILURE; 51 | } 52 | 53 | return EXIT_SUCCESS; 54 | } 55 | -------------------------------------------------------------------------------- /kernel/drivers/pci.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define PCI_BAR_SPACE 0x1 8 | #define PCI_BAR_SPACE_IO 0x1 9 | 10 | #define PCI_CAP_ID_VNDR 0x09 11 | 12 | struct pci_addr { 13 | uint8_t bus; 14 | uint8_t slot; 15 | uint8_t function; 16 | }; 17 | 18 | typedef void (*pci_device_callback_fn)(const struct pci_addr*, 19 | uint16_t vendor_id, uint16_t device_id, 20 | void* ctx); 21 | void pci_enumerate_devices(pci_device_callback_fn, void* ctx); 22 | 23 | typedef void (*pci_capability_callback_fn)(const struct pci_addr*, uint8_t id, 24 | uint8_t pointer, void* ctx); 25 | void pci_enumerate_capabilities(const struct pci_addr*, 26 | pci_capability_callback_fn, void* ctx); 27 | 28 | uint8_t pci_read_field8(const struct pci_addr*, uint8_t field); 29 | uint16_t pci_read_field16(const struct pci_addr*, uint8_t field); 30 | uint32_t pci_read_field32(const struct pci_addr*, uint8_t field); 31 | 32 | uint16_t pci_get_type(const struct pci_addr*); 33 | 34 | uint32_t pci_get_bar(const struct pci_addr*, uint8_t bar); 35 | void* pci_map_bar(const struct pci_addr*, uint8_t bar); 36 | 37 | uint8_t pci_get_interrupt_line(const struct pci_addr*); 38 | void pci_set_interrupt_line_enabled(const struct pci_addr*, bool enabled); 39 | void pci_set_bus_mastering_enabled(const struct pci_addr*, bool enabled); 40 | -------------------------------------------------------------------------------- /kernel/console/system_console.c: -------------------------------------------------------------------------------- 1 | #include "private.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static struct tty* backing_tty; 10 | 11 | static int system_console_open(struct file* file) { 12 | ASSERT(backing_tty); 13 | file->private_data = backing_tty; 14 | file->fops = &tty_fops; 15 | return 0; 16 | } 17 | 18 | void system_console_init(void) { 19 | const char* console = cmdline_lookup("console"); 20 | if (!console) 21 | console = "tty1"; 22 | 23 | struct char_dev* backing_dev = char_dev_find_by_name(console); 24 | if (!backing_dev) { 25 | kprintf("system_console: device %s not found\n", console); 26 | return; 27 | } 28 | switch (major(backing_dev->dev)) { 29 | case TTY_MAJOR: 30 | case TTYAUX_MAJOR: 31 | break; 32 | default: 33 | kprintf("system_console: device %s is not a tty\n", console); 34 | return; 35 | } 36 | 37 | backing_tty = CONTAINER_OF(backing_dev, struct tty, char_dev); 38 | kprintf("system_console: using %s\n", console); 39 | 40 | static const struct file_ops fops = { 41 | .open = system_console_open, 42 | }; 43 | static struct char_dev char_dev = { 44 | .name = "console", 45 | .dev = makedev(TTYAUX_MAJOR, 1), 46 | .fops = &fops, 47 | }; 48 | ASSERT_OK(char_dev_register(&char_dev)); 49 | } 50 | -------------------------------------------------------------------------------- /common/math.c: -------------------------------------------------------------------------------- 1 | #include "math.h" 2 | 3 | double sqrt(double x) { 4 | double res; 5 | __asm__("fsqrt" : "=t"(res) : "0"(x)); 6 | return res; 7 | } 8 | 9 | double log2(double x) { 10 | double ret; 11 | __asm__("fld1\n" 12 | "fxch %%st(1)\n" 13 | "fyl2x" 14 | : "=t"(ret) 15 | : "0"(x)); 16 | return ret; 17 | } 18 | 19 | double log10(double x) { 20 | double ret; 21 | __asm__("fldlg2\n" 22 | "fxch %%st(1)\n" 23 | "fyl2x\n" 24 | : "=t"(ret) 25 | : "0"(x)); 26 | return ret; 27 | } 28 | 29 | double fabs(double x) { 30 | __asm__("fabs" : "+t"(x)); 31 | return x; 32 | } 33 | 34 | double exp2(double exponent) { 35 | double res; 36 | __asm__("fld1\n" 37 | "fld %%st(1)\n" 38 | "fprem\n" 39 | "f2xm1\n" 40 | "faddp\n" 41 | "fscale\n" 42 | "fstp %%st(1)" 43 | : "=t"(res) 44 | : "0"(exponent)); 45 | return res; 46 | } 47 | 48 | double pow(double x, double y) { return exp2(y * log2(x)); } 49 | 50 | double sin(double angle) { 51 | double ret; 52 | __asm__("fsin" : "=t"(ret) : "0"(angle)); 53 | return ret; 54 | } 55 | 56 | double cos(double angle) { 57 | double ret; 58 | __asm__("fcos" : "=t"(ret) : "0"(angle)); 59 | return ret; 60 | } 61 | 62 | double atan2(double y, double x) { 63 | double ret; 64 | __asm__("fpatan" : "=t"(ret) : "0"(x), "u"(y) : "st(1)"); 65 | return ret; 66 | } 67 | -------------------------------------------------------------------------------- /kernel/device/device.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | void device_init(void); 8 | 9 | struct char_dev { 10 | char name[16]; 11 | dev_t dev; 12 | const struct file_ops* fops; 13 | struct char_dev* next; 14 | }; 15 | 16 | extern const struct file_ops char_dev_fops; 17 | 18 | NODISCARD int char_dev_register(struct char_dev*); 19 | struct char_dev* char_dev_get(dev_t); 20 | struct char_dev* char_dev_find_by_name(const char*); 21 | 22 | #define SECTOR_SHIFT 9 23 | #define SECTOR_SIZE (1 << SECTOR_SHIFT) 24 | 25 | #define BLOCK_DEV_INIT {.vfs_inode = INODE_INIT} 26 | 27 | struct block_dev { 28 | struct inode vfs_inode; 29 | char name[16]; 30 | dev_t dev; 31 | const struct block_ops* bops; 32 | uint8_t block_bits; // Block size as 2^block_bits 33 | size_t num_blocks; 34 | }; 35 | 36 | struct block_ops { 37 | int (*read)(struct block_dev*, void* buffer, uint64_t index, 38 | uint64_t nblocks); 39 | int (*write)(struct block_dev*, const void* buffer, uint64_t index, 40 | uint64_t nblocks); 41 | int (*flush)(struct block_dev*); 42 | }; 43 | 44 | extern const struct file_ops block_dev_fops; 45 | 46 | NODISCARD int block_dev_register(struct block_dev*); 47 | struct block_dev* block_dev_get(dev_t); 48 | 49 | void block_dev_ref(struct block_dev*); 50 | void block_dev_unref(struct block_dev*); 51 | 52 | void block_dev_lock(struct block_dev*); 53 | void block_dev_unlock(struct block_dev*); 54 | -------------------------------------------------------------------------------- /userland/ps.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main(void) { 10 | DIR* dirp = opendir("/proc"); 11 | if (!dirp) { 12 | perror("opendir"); 13 | return EXIT_FAILURE; 14 | } 15 | 16 | puts(" PID CMD"); 17 | for (;;) { 18 | errno = 0; 19 | struct dirent* dent = readdir(dirp); 20 | if (!dent) { 21 | if (errno == 0) 22 | break; 23 | perror("readdir"); 24 | closedir(dirp); 25 | return EXIT_FAILURE; 26 | } 27 | 28 | if (!str_is_uint(dent->d_name)) 29 | continue; 30 | 31 | pid_t pid = atoi(dent->d_name); 32 | 33 | char pathname[32]; 34 | (void)snprintf(pathname, sizeof(pathname), "/proc/%d/comm", pid); 35 | int fd = open(pathname, O_RDONLY); 36 | if (fd < 0) { 37 | perror("open"); 38 | closedir(dirp); 39 | return EXIT_FAILURE; 40 | } 41 | char comm[32]; 42 | ssize_t nread = read(fd, comm, sizeof(comm)); 43 | close(fd); 44 | 45 | if (nread < 0) { 46 | perror("read"); 47 | closedir(dirp); 48 | return EXIT_FAILURE; 49 | } 50 | if (nread == 0) 51 | continue; 52 | 53 | if (comm[nread - 1] == '\n') 54 | comm[nread - 1] = 0; 55 | 56 | printf("%5d %s\n", pid, comm); 57 | } 58 | 59 | closedir(dirp); 60 | return EXIT_SUCCESS; 61 | } 62 | -------------------------------------------------------------------------------- /kernel/api/linux/kd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define GIO_CMAP 0x4B70 // gets colour palette on VGA+ 4 | #define PIO_CMAP 0x4B71 // sets colour palette on VGA+ 5 | 6 | #define KDGKBTYPE 0x4b33 // get keyboard type 7 | #define KB_84 0x01 8 | #define KB_101 0x02 9 | #define KB_OTHER 0x03 10 | 11 | #define KDGKBMODE 0x4b44 // gets current keyboard mode 12 | #define KDSKBMODE 0x4b45 // sets current keyboard mode 13 | #define K_RAW 0x00 14 | #define K_XLATE 0x01 15 | #define K_MEDIUMRAW 0x02 16 | 17 | struct kbentry { 18 | unsigned char kb_table; 19 | unsigned char kb_index; 20 | unsigned short kb_value; 21 | }; 22 | 23 | #define KDGKBENT 0x4b46 // gets one entry in translation table 24 | #define KDSKBENT 0x4b47 // sets one entry in translation table 25 | 26 | struct kbdiacruc { 27 | unsigned int diacr, base, result; 28 | }; 29 | 30 | #define KDFONTOP 0x4b72 31 | 32 | struct console_font_op { 33 | unsigned int op; // operation code KD_FONT_OP_* 34 | unsigned int flags; // KD_FONT_FLAG_* 35 | unsigned int width, height; // font size 36 | unsigned int charcount; 37 | 38 | // font data with vpitch fixed to 32 for KD_FONT_OP_SET/GET 39 | unsigned char* data; 40 | }; 41 | 42 | // Set font 43 | #define KD_FONT_OP_SET 0 44 | 45 | // Get font 46 | #define KD_FONT_OP_GET 1 47 | 48 | // Set font to default, data points to name / NULL 49 | #define KD_FONT_OP_SET_DEFAULT 2 50 | 51 | // Obsolete, do not use 52 | #define KD_FONT_OP_COPY 3 53 | 54 | // Set font with vpitch = height 55 | #define KD_FONT_OP_SET_TALL 4 56 | 57 | // Get font with vpitch = height 58 | #define KD_FONT_OP_GET_TALL 5 59 | -------------------------------------------------------------------------------- /userland/uname.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static void usage(void) { 7 | dprintf(STDERR_FILENO, "Usage: uname [-asnrvm]\n"); 8 | exit(EXIT_FAILURE); 9 | } 10 | 11 | int main(int argc, char* const argv[]) { 12 | bool all = false; 13 | bool nodename = false; 14 | bool release = false; 15 | bool version = false; 16 | bool machine = false; 17 | for (int i = 1; i < argc; ++i) { 18 | const char* arg = argv[i]; 19 | if (arg[0] != '-' || arg[1] == '\0' || arg[2] != '\0') 20 | usage(); 21 | switch (arg[1]) { 22 | case 'a': 23 | all = true; 24 | break; 25 | case 's': 26 | break; 27 | case 'n': 28 | nodename = true; 29 | break; 30 | case 'r': 31 | release = true; 32 | break; 33 | case 'v': 34 | version = true; 35 | break; 36 | case 'm': 37 | machine = true; 38 | break; 39 | default: 40 | usage(); 41 | } 42 | } 43 | 44 | struct utsname buf; 45 | if (uname(&buf) < 0) { 46 | perror("uname"); 47 | return EXIT_FAILURE; 48 | } 49 | 50 | printf("%s ", buf.sysname); 51 | if (all || nodename) 52 | printf("%s ", buf.nodename); 53 | if (all || release) 54 | printf("%s ", buf.release); 55 | if (all || version) 56 | printf("%s ", buf.version); 57 | if (all || machine) 58 | printf("%s", buf.machine); 59 | puts(""); 60 | 61 | return EXIT_SUCCESS; 62 | } 63 | -------------------------------------------------------------------------------- /kernel/kmsg.c: -------------------------------------------------------------------------------- 1 | #include "kmsg.h" 2 | #include "drivers/serial.h" 3 | #include "lock.h" 4 | #include 5 | #include 6 | #include 7 | 8 | int kprint(const char* str) { 9 | size_t len = strlen(str); 10 | kmsg_write(str, len); 11 | return len; 12 | } 13 | 14 | int kprintf(const char* format, ...) { 15 | va_list args; 16 | va_start(args, format); 17 | int ret = kvprintf(format, args); 18 | va_end(args); 19 | return ret; 20 | } 21 | 22 | int kvprintf(const char* format, va_list args) { 23 | char buf[1024]; 24 | int ret = vsnprintf(buf, sizeof(buf), format, args); 25 | kprint(buf); 26 | return ret; 27 | } 28 | 29 | static char ring_buf[KMSG_BUF_SIZE]; 30 | static size_t read_index = 0; 31 | static size_t write_index = 0; 32 | static struct spinlock lock; 33 | 34 | size_t kmsg_read(char* buf, size_t count) { 35 | size_t dest_index = 0; 36 | 37 | spinlock_lock(&lock); 38 | for (size_t src_index = read_index; 39 | dest_index < count && src_index != write_index; 40 | src_index = (src_index + 1) % KMSG_BUF_SIZE) 41 | buf[dest_index++] = ring_buf[src_index]; 42 | spinlock_unlock(&lock); 43 | 44 | return dest_index; 45 | } 46 | 47 | void kmsg_write(const char* buf, size_t count) { 48 | spinlock_lock(&lock); 49 | for (size_t i = 0; i < count; ++i) { 50 | ring_buf[write_index] = buf[i]; 51 | write_index = (write_index + 1) % KMSG_BUF_SIZE; 52 | if (write_index == read_index) 53 | read_index = (read_index + 1) % KMSG_BUF_SIZE; 54 | } 55 | serial_write(0, buf, count); 56 | spinlock_unlock(&lock); 57 | } 58 | -------------------------------------------------------------------------------- /userland/lib/stdio.c: -------------------------------------------------------------------------------- 1 | #include "stdio.h" 2 | #include "errno.h" 3 | #include "private.h" 4 | #include "string.h" 5 | #include "unistd.h" 6 | 7 | int putchar(int ch) { 8 | char c = ch; 9 | if (write(STDOUT_FILENO, &c, 1) < 0) 10 | return -1; 11 | return ch; 12 | } 13 | 14 | int puts(const char* str) { 15 | int rc = write(STDOUT_FILENO, str, strlen(str)); 16 | if (rc < 0) 17 | return -1; 18 | if (write(STDOUT_FILENO, "\n", 1) < 0) 19 | return -1; 20 | return rc + 1; 21 | } 22 | 23 | int printf(const char* format, ...) { 24 | va_list args; 25 | va_start(args, format); 26 | int ret = vprintf(format, args); 27 | va_end(args); 28 | return ret; 29 | } 30 | 31 | int vprintf(const char* format, va_list ap) { 32 | return vdprintf(STDOUT_FILENO, format, ap); 33 | } 34 | 35 | int dprintf(int fd, const char* format, ...) { 36 | va_list args; 37 | va_start(args, format); 38 | int ret = vdprintf(fd, format, args); 39 | va_end(args); 40 | return ret; 41 | } 42 | 43 | int vdprintf(int fd, const char* format, va_list ap) { 44 | char buf[1024]; 45 | int len = vsnprintf(buf, 1024, format, ap); 46 | return write(fd, buf, len); 47 | } 48 | 49 | void perror(const char* s) { 50 | dprintf(STDERR_FILENO, "%s: %s\n", s, strerror(errno)); 51 | } 52 | 53 | int getchar(void) { 54 | char c; 55 | if (read(STDIN_FILENO, &c, 1) < 0) 56 | return -1; 57 | return c; 58 | } 59 | 60 | int remove(const char* pathname) { 61 | if (unlink(pathname) >= 0) 62 | return 0; 63 | if (errno == EISDIR) 64 | return rmdir(pathname); 65 | return -1; 66 | } 67 | 68 | int dbgprint(const char* str) { 69 | return __syscall_return(SYSCALL1(dbgprint, str)); 70 | } 71 | -------------------------------------------------------------------------------- /common/stdlib.c: -------------------------------------------------------------------------------- 1 | #include "stdlib.h" 2 | #include "ctype.h" 3 | 4 | int atoi(const char* str) { 5 | if (!*str) 6 | return 0; 7 | while (*str && isspace(*str)) 8 | ++str; 9 | if (*str == '-') 10 | return -atoi(str + 1); 11 | int res = 0; 12 | while ('0' <= *str && *str <= '9') { 13 | res *= 10; 14 | res += *str++ - '0'; 15 | } 16 | return res; 17 | } 18 | 19 | int abs(int i) { return i < 0 ? -i : i; } 20 | 21 | static void memswap(void* s1, void* s2, size_t n) { 22 | unsigned char* c1 = (unsigned char*)s1; 23 | unsigned char* c2 = (unsigned char*)s2; 24 | for (size_t i = 0; i < n; ++i) { 25 | unsigned char tmp = c1[i]; 26 | c1[i] = c2[i]; 27 | c2[i] = tmp; 28 | } 29 | } 30 | 31 | void qsort(void* base, size_t nmemb, size_t size, 32 | int (*compar)(const void*, const void*)) { 33 | unsigned char* start = base; 34 | while (nmemb > 1) { 35 | size_t pivot_point = nmemb / 2; 36 | if (pivot_point) 37 | memswap(start + pivot_point * size, start, size); 38 | 39 | const unsigned char* pivot = start; 40 | 41 | size_t i = 1; 42 | for (size_t j = 1; j < nmemb; ++j) { 43 | if (compar(start + j * size, pivot) < 0) { 44 | memswap(start + j * size, start + i * size, size); 45 | ++i; 46 | } 47 | } 48 | 49 | memswap(start, start + (i - 1) * size, size); 50 | if (i > nmemb / 2) { 51 | qsort(start + i * size, nmemb - i, size, compar); 52 | nmemb = i - 1; 53 | } else { 54 | qsort(start, i - 1, size, compar); 55 | nmemb -= i; 56 | start += i * size; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /userland/wc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define BUF_SIZE 1024 10 | 11 | static int process_file(const char* name, const char* filename) { 12 | int fd = strcmp(filename, "-") ? open(filename, O_RDONLY) : STDIN_FILENO; 13 | if (fd < 0) { 14 | perror("open"); 15 | return -1; 16 | } 17 | 18 | size_t lines = 0; 19 | size_t words = 0; 20 | size_t bytes = 0; 21 | bool in_word = false; 22 | for (;;) { 23 | static char buf[BUF_SIZE]; 24 | ssize_t nread = read(fd, buf, BUF_SIZE); 25 | if (nread < 0) { 26 | perror("read"); 27 | if (fd != STDIN_FILENO) 28 | close(fd); 29 | return -1; 30 | } 31 | if (nread == 0) 32 | break; 33 | for (ssize_t i = 0; i < nread; ++i) { 34 | ++bytes; 35 | if (buf[i] == '\n') 36 | ++lines; 37 | if (isspace(buf[i])) { 38 | in_word = false; 39 | } else if (!in_word) { 40 | ++words; 41 | in_word = true; 42 | } 43 | } 44 | } 45 | 46 | printf("%7u %7u %7u %s\n", lines, words, bytes, name); 47 | 48 | if (fd != STDIN_FILENO) 49 | close(fd); 50 | return 0; 51 | } 52 | 53 | int main(int argc, char* argv[]) { 54 | int ret = EXIT_SUCCESS; 55 | if (argc < 2) { 56 | if (process_file("", "-") < 0) 57 | ret = EXIT_FAILURE; 58 | } 59 | for (int i = 1; i < argc; ++i) { 60 | if (process_file(argv[i], argv[i]) < 0) 61 | ret = EXIT_FAILURE; 62 | } 63 | return ret; 64 | } 65 | -------------------------------------------------------------------------------- /kernel/system.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define AP_TRAMPOLINE_ADDR 0x8000 4 | #define STACK_SIZE 0x4000 5 | 6 | #ifndef ASM_FILE 7 | 8 | #include "api/sys/types.h" 9 | #include "api/sys/utsname.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | typedef struct multiboot_info multiboot_info_t; 18 | 19 | extern unsigned char init_end[]; 20 | extern unsigned char kernel_end[]; 21 | 22 | struct registers { 23 | uint32_t gs, fs, es, ds; 24 | uint32_t edi, esi, ebp, edx, ecx, ebx, eax; 25 | uint32_t interrupt_num, error_code; 26 | uint32_t eip, cs, eflags, esp, ss; 27 | }; 28 | 29 | void dump_context(const struct registers*); 30 | 31 | struct fpu_state { 32 | alignas(16) unsigned char buffer[512]; 33 | }; 34 | 35 | void syscall_init(void); 36 | 37 | void utsname_get(struct utsname*); 38 | NODISCARD int utsname_set_hostname(const char*, size_t); 39 | NODISCARD int utsname_set_domainname(const char*, size_t); 40 | 41 | void cmdline_init(const multiboot_info_t*); 42 | const char* cmdline_get_raw(void); 43 | const char* cmdline_lookup(const char* key); 44 | bool cmdline_contains(const char* key); 45 | 46 | struct symbol { 47 | uintptr_t addr; 48 | char type; 49 | const char* name; 50 | }; 51 | 52 | void ksyms_init(void); 53 | const struct symbol* ksyms_lookup(uintptr_t addr); 54 | const struct symbol* ksyms_next(const struct symbol* symbol); 55 | 56 | void random_init(void); 57 | ssize_t random_get(void* buffer, size_t count); 58 | 59 | extern atomic_bool smp_active; 60 | 61 | void smp_init(void); 62 | void smp_start(void); 63 | 64 | noreturn void reboot(void); 65 | noreturn void halt(void); 66 | noreturn void poweroff(void); 67 | 68 | void handle_sysrq(char); 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /kernel/memory/private.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "memory.h" 4 | #include 5 | 6 | // In the current setup, kernel image (including 1MiB offset) has to fit in 7 | // a single page table (< 4MiB). 8 | #define KERNEL_IMAGE_END (KERNEL_VIRT_ADDR + (1024 << PAGE_SHIFT)) 9 | 10 | #define PAGES_START KERNEL_IMAGE_END 11 | #define PAGES_END \ 12 | (PAGES_START + ROUND_UP(MAX_NUM_PAGES * sizeof(struct page), PAGE_SIZE)) 13 | 14 | #define KMAP_START PAGES_END 15 | #define KMAP_END (KMAP_START + MAX_NUM_KMAPS_PER_CPU * MAX_NUM_CPUS * PAGE_SIZE) 16 | 17 | #define KERNEL_VM_START KMAP_END 18 | 19 | // Last 4MiB is for recursive mapping 20 | #define KERNEL_VM_END 0xffc00000 21 | 22 | struct vm; 23 | typedef struct multiboot_info multiboot_info_t; 24 | 25 | #define MAX_NUM_PAGES (1 << 20) 26 | 27 | void page_init(const multiboot_info_t*); 28 | 29 | struct page_directory* page_directory_create(void); 30 | void page_directory_destroy(struct page_directory*); 31 | 32 | void vm_init(void); 33 | 34 | // Finds a gap in the address space that can fit a region of the given size. 35 | // Returns the region before the gap, or NULL if the vm is empty. 36 | // If start is not NULL, (the start virtual address >> PAGE_SHIFT) of the gap is 37 | // stored in *start. 38 | struct vm_region* vm_find_gap(const struct vm*, size_t npages, size_t* start); 39 | 40 | // Inserts the region `inserted` after the region `prev`. 41 | // If `prev` is NULL, `inserted` is inserted at the beginning of the vm's region 42 | // list. 43 | void vm_insert_region_after(struct vm*, struct vm_region* prev, 44 | struct vm_region* inserted); 45 | 46 | // Removes a region from the vm's region list. 47 | void vm_region_remove(struct vm_region*); 48 | 49 | void vm_obj_init(void); 50 | -------------------------------------------------------------------------------- /kernel/gdt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define NUM_GDT_ENTRIES 10 4 | #define GDT_ENTRY_KERNEL_CS 1 5 | #define GDT_ENTRY_KERNEL_DS 2 6 | #define GDT_ENTRY_USER_CS 3 7 | #define GDT_ENTRY_USER_DS 4 8 | #define GDT_ENTRY_TSS 5 9 | #define GDT_ENTRY_TLS_MIN 6 10 | #define NUM_GDT_TLS_ENTRIES 3 11 | #define GDT_ENTRY_CPU_ID 9 12 | 13 | #define KERNEL_CS (GDT_ENTRY_KERNEL_CS * 8) 14 | #define KERNEL_DS (GDT_ENTRY_KERNEL_DS * 8) 15 | #define USER_CS (GDT_ENTRY_USER_CS * 8) 16 | #define USER_DS (GDT_ENTRY_USER_DS * 8) 17 | #define TSS_SELECTOR (GDT_ENTRY_TSS * 8) 18 | #define CPU_ID_SELECTOR (GDT_ENTRY_CPU_ID * 8) 19 | 20 | #ifndef ASM_FILE 21 | 22 | #include 23 | 24 | struct gdt_segment { 25 | uint16_t limit_lo; 26 | uint16_t base_lo; 27 | uint8_t base_mid; 28 | union { 29 | uint8_t access; 30 | struct { 31 | uint8_t type : 4; 32 | uint8_t s : 1; 33 | uint8_t dpl : 2; 34 | uint8_t p : 1; 35 | }; 36 | }; 37 | union { 38 | struct { 39 | uint8_t limit_hi : 4; 40 | uint8_t flags : 4; 41 | }; 42 | struct { 43 | uint8_t pad : 4; 44 | uint8_t avl : 1; 45 | uint8_t l : 1; 46 | uint8_t db : 1; 47 | uint8_t g : 1; 48 | }; 49 | }; 50 | uint8_t base_hi; 51 | }; 52 | 53 | struct gdtr { 54 | uint16_t limit; 55 | uint32_t base; 56 | } __attribute__((packed)); 57 | 58 | struct tss { 59 | uint32_t prev_tss; 60 | uint32_t esp0, ss0, esp1, ss1, esp2, ss2; 61 | uint32_t cr3; 62 | uint32_t eip, eflags, eax, ecx, edx, ebx, esp, ebp, esi, edi; 63 | uint32_t es, cs, ss, ds, fs, gs; 64 | uint32_t ldt; 65 | uint16_t trap, iomap_base; 66 | }; 67 | 68 | void gdt_init_cpu(void); 69 | void gdt_set_cpu_kernel_stack(uintptr_t stack_top); 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /userland/lib/pthread.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "signal.h" 4 | #include 5 | #include 6 | 7 | #define PTHREAD_STACK_MIN 16384 8 | 9 | typedef struct pthread* pthread_t; 10 | typedef struct pthread_attr* pthread_attr_t; 11 | 12 | int pthread_create(pthread_t* restrict thread, 13 | const pthread_attr_t* restrict attr, 14 | void* (*start_routine)(void*), void* restrict arg); 15 | int pthread_detach(pthread_t thread); 16 | noreturn void pthread_exit(void* retval); 17 | int pthread_join(pthread_t thread, void** retval); 18 | 19 | pthread_t pthread_self(void); 20 | int pthread_equal(pthread_t t1, pthread_t t2); 21 | 22 | int pthread_kill(pthread_t thread, int sig); 23 | int pthread_sigmask(int how, const sigset_t* set, sigset_t* oldset); 24 | 25 | int pthread_attr_init(pthread_attr_t* attr); 26 | int pthread_attr_destroy(pthread_attr_t* attr); 27 | 28 | int pthread_attr_setguardsize(pthread_attr_t* attr, size_t guardsize); 29 | int pthread_attr_getguardsize(const pthread_attr_t* restrict attr, 30 | size_t* restrict guardsize); 31 | 32 | int pthread_attr_setstack(pthread_attr_t* attr, void* stackaddr, 33 | size_t stacksize); 34 | int pthread_attr_getstack(const pthread_attr_t* restrict attr, 35 | void** restrict stackaddr, 36 | size_t* restrict stacksize); 37 | 38 | int pthread_attr_setstacksize(pthread_attr_t* attr, size_t stacksize); 39 | int pthread_attr_getstacksize(const pthread_attr_t* restrict attr, 40 | size_t* restrict stacksize); 41 | 42 | #define PTHREAD_CREATE_JOINABLE 0 43 | #define PTHREAD_CREATE_DETACHED 1 44 | 45 | int pthread_attr_setdetachstate(pthread_attr_t* attr, int detachstate); 46 | int pthread_attr_getdetachstate(const pthread_attr_t* attr, int* detachstate); 47 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eo pipefail 4 | 5 | KERNEL='kernel/kernel' 6 | INITRD='initrd' 7 | CMDLINE=() 8 | NUM_CPUS=1 9 | 10 | case "$1" in 11 | shell) # Serial console 12 | QEMU_DISPLAY_ARGS=(-display none -vga none) 13 | CMDLINE+=(console=ttyS0) 14 | ;; 15 | text) # Text console 16 | QEMU_DISPLAY_ARGS=(-display "sdl,gl=off" -vga cirrus) 17 | CMDLINE+=(console=tty1) 18 | ;; 19 | *) # Framebuffer console 20 | QEMU_DISPLAY_ARGS=(-display "sdl,gl=off,show-cursor=off") 21 | CMDLINE+=(console=tty1) 22 | ;; 23 | esac 24 | 25 | QEMU_BINARY_PREFIX='' 26 | QEMU_BINARY_SUFFIX='' 27 | QEMU_VIRT_TECH_ARGS=() 28 | 29 | if [ -r /dev/kvm ] && [ -w /dev/kvm ] && 30 | command -v kvm-ok >/dev/null && kvm-ok &>/dev/null; then 31 | QEMU_VIRT_TECH_ARGS=(-enable-kvm) 32 | fi 33 | 34 | if command -v wslpath >/dev/null; then 35 | PATH=${PATH}:/mnt/c/Windows/System32 36 | QEMU_INSTALL_DIR=$(reg.exe query 'HKLM\Software\QEMU' /v Install_Dir /t REG_SZ | grep '^ Install_Dir' | sed 's/ / /g' | cut -f4- -d' ') 37 | QEMU_BINARY_PREFIX="$(wslpath -- "${QEMU_INSTALL_DIR}" | tr -d '\r\n')/" 38 | QEMU_BINARY_SUFFIX='.exe' 39 | QEMU_VIRT_TECH_ARGS=(-accel "whpx,kernel-irqchip=off" -accel tcg) 40 | KERNEL=$(wslpath -w "${KERNEL}") 41 | INITRD=$(wslpath -w "${INITRD}") 42 | fi 43 | 44 | QEMU_BIN="${QEMU_BINARY_PREFIX}qemu-system-i386${QEMU_BINARY_SUFFIX}" 45 | "${QEMU_BIN}" \ 46 | -kernel "${KERNEL}" \ 47 | -initrd "${INITRD}" \ 48 | -append "${CMDLINE[*]}" \ 49 | -d guest_errors \ 50 | "${QEMU_DISPLAY_ARGS[@]}" \ 51 | -device ac97 \ 52 | -chardev stdio,mux=on,id=char0 \ 53 | -serial chardev:char0 \ 54 | -mon char0,mode=readline \ 55 | -cpu max \ 56 | -m 512M \ 57 | -smp "sockets=1,cores=${NUM_CPUS},threads=1" \ 58 | "${QEMU_VIRT_TECH_ARGS[@]}" 59 | -------------------------------------------------------------------------------- /kernel/containers/mpsc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | // Multiple-producer single-consumer lock-free queue. 9 | struct mpsc { 10 | size_t capacity; 11 | atomic_size_t size, head, tail; 12 | atomic_uintptr_t slots[]; 13 | }; 14 | 15 | static inline struct mpsc* mpsc_create(size_t capacity) { 16 | struct mpsc* mpsc = 17 | kmalloc(sizeof(struct mpsc) + capacity * sizeof(atomic_uintptr_t)); 18 | if (!mpsc) 19 | return ERR_PTR(-ENOMEM); 20 | *mpsc = (struct mpsc){.capacity = capacity}; 21 | memset(mpsc->slots, 0, capacity * sizeof(atomic_uintptr_t)); 22 | return mpsc; 23 | } 24 | 25 | // Returns true if the element was enqueued, false if the queue was full. 26 | NODISCARD static inline bool mpsc_enqueue(struct mpsc* mpsc, void* x) { 27 | size_t size = 28 | atomic_fetch_add_explicit(&mpsc->size, 1, memory_order_acquire); 29 | if (size >= mpsc->capacity) { 30 | atomic_fetch_sub_explicit(&mpsc->size, 1, memory_order_release); 31 | return false; 32 | } 33 | size_t head = 34 | atomic_fetch_add_explicit(&mpsc->head, 1, memory_order_acquire); 35 | atomic_uintptr_t* slot = &mpsc->slots[head % mpsc->capacity]; 36 | ASSERT(!*slot); 37 | ASSERT(!atomic_exchange_explicit(slot, (uintptr_t)x, memory_order_release)); 38 | return true; 39 | } 40 | 41 | // Returns NULL if the queue was empty. 42 | static inline void* mpsc_dequeue(struct mpsc* mpsc) { 43 | atomic_uintptr_t* slot = &mpsc->slots[mpsc->tail]; 44 | uintptr_t x = atomic_exchange_explicit(slot, 0, memory_order_acquire); 45 | if (!x) 46 | return NULL; 47 | if (++mpsc->tail >= mpsc->capacity) 48 | mpsc->tail = 0; 49 | ASSERT(atomic_fetch_sub_explicit(&mpsc->size, 1, memory_order_release)); 50 | return (void*)x; 51 | } 52 | -------------------------------------------------------------------------------- /userland/grep.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(int argc, char* const argv[]) { 9 | if (argc < 2) { 10 | dprintf(STDERR_FILENO, "Usage: grep PATTERN [FILE]...\n"); 11 | return EXIT_FAILURE; 12 | } 13 | 14 | const char* pattern = argv[1]; 15 | 16 | for (int i = 2; i < argc; i++) { 17 | const char* filename = argv[i]; 18 | 19 | struct stat st; 20 | if (stat(filename, &st) < 0) { 21 | perror("stat"); 22 | return EXIT_FAILURE; 23 | } 24 | 25 | char* buf = malloc(st.st_size + 1); // +1 for null terminator 26 | if (!buf) { 27 | perror("malloc"); 28 | return EXIT_FAILURE; 29 | } 30 | 31 | int fd = open(filename, O_RDONLY); 32 | if (fd < 0) { 33 | perror("open"); 34 | goto done; 35 | } 36 | 37 | size_t buf_len = 0; 38 | ssize_t nread; 39 | while ((nread = read(fd, buf + buf_len, st.st_size - buf_len)) > 0) 40 | buf_len += nread; 41 | if (nread < 0) { 42 | perror("read"); 43 | goto done; 44 | } 45 | close(fd); 46 | fd = -1; 47 | buf[buf_len] = 0; 48 | 49 | static const char* sep = "\n"; 50 | char* saved_ptr; 51 | for (char* line = strtok_r(buf, sep, &saved_ptr); line; 52 | line = strtok_r(NULL, sep, &saved_ptr)) { 53 | if (strstr(line, pattern)) { 54 | if (argc > 3) 55 | printf("%s:%s\n", filename, line); 56 | else 57 | puts(line); 58 | } 59 | } 60 | 61 | done: 62 | free(buf); 63 | if (fd >= 0) 64 | close(fd); 65 | } 66 | 67 | return EXIT_SUCCESS; 68 | } 69 | -------------------------------------------------------------------------------- /userland/lib/sys/stat.c: -------------------------------------------------------------------------------- 1 | #include "stat.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static void stat64_to_stat(const struct linux_stat64* stat64, 8 | struct stat* stat) { 9 | *stat = (struct stat){ 10 | .st_dev = stat64->st_dev, 11 | .st_ino = stat64->st_ino, 12 | .st_mode = stat64->st_mode, 13 | .st_nlink = stat64->st_nlink, 14 | .st_uid = stat64->st_uid, 15 | .st_gid = stat64->st_gid, 16 | .st_rdev = stat64->st_rdev, 17 | .st_size = stat64->st_size, 18 | .st_blksize = stat64->st_blksize, 19 | .st_blocks = stat64->st_blocks, 20 | .st_atim = {stat64->st_atime, stat64->st_atime_nsec}, 21 | .st_mtim = {stat64->st_mtime, stat64->st_mtime_nsec}, 22 | .st_ctim = {stat64->st_ctime, stat64->st_ctime_nsec}, 23 | }; 24 | } 25 | 26 | int stat(const char* pathname, struct stat* buf) { 27 | struct linux_stat64 stat64; 28 | int rc = SYSCALL2(stat64, pathname, &stat64); 29 | if (IS_ERR(rc)) { 30 | errno = -rc; 31 | return -1; 32 | } 33 | stat64_to_stat(&stat64, buf); 34 | return rc; 35 | } 36 | 37 | int lstat(const char* pathname, struct stat* buf) { 38 | struct linux_stat64 stat64; 39 | int rc = SYSCALL2(lstat64, pathname, &stat64); 40 | if (IS_ERR(rc)) { 41 | errno = -rc; 42 | return -1; 43 | } 44 | stat64_to_stat(&stat64, buf); 45 | return rc; 46 | } 47 | 48 | int fstat(int fd, struct stat* buf) { 49 | struct linux_stat64 stat64; 50 | int rc = SYSCALL2(fstat64, fd, &stat64); 51 | if (IS_ERR(rc)) { 52 | errno = -rc; 53 | return -1; 54 | } 55 | stat64_to_stat(&stat64, buf); 56 | return rc; 57 | } 58 | 59 | int mkdir(const char* pathname, mode_t mode) { 60 | return __syscall_return(SYSCALL2(mkdir, pathname, mode)); 61 | } 62 | 63 | int mkfifo(const char* pathname, mode_t mode) { 64 | return mknod(pathname, mode | S_IFIFO, 0); 65 | } 66 | -------------------------------------------------------------------------------- /kernel/console/serial_console.c: -------------------------------------------------------------------------------- 1 | #include "private.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define MINOR_BASE 64 12 | 13 | struct serial_console { 14 | struct tty tty; 15 | uint8_t index; 16 | }; 17 | 18 | static struct serial_console* consoles[SERIAL_NUM_PORTS]; 19 | 20 | static void on_char(uint8_t index, char ch) { 21 | ASSERT(index < SERIAL_NUM_PORTS); 22 | struct serial_console* console = consoles[index]; 23 | if (console) 24 | tty_emit(&console->tty, &ch, 1); 25 | } 26 | 27 | static void serial_console_echo(struct tty* tty, const char* buf, 28 | size_t count) { 29 | struct serial_console* console = 30 | CONTAINER_OF(tty, struct serial_console, tty); 31 | serial_write(console->index, buf, count); 32 | } 33 | 34 | static struct serial_console* serial_console_create(uint8_t index) { 35 | struct serial_console* console = kmalloc(sizeof(struct serial_console)); 36 | ASSERT(console); 37 | *console = (struct serial_console){ 38 | .index = index, 39 | }; 40 | 41 | static const struct tty_ops tty_ops = { 42 | .echo = serial_console_echo, 43 | }; 44 | 45 | struct tty* tty = &console->tty; 46 | (void)snprintf(tty->name, sizeof(tty->name), "ttyS%u", index); 47 | tty->dev = makedev(TTY_MAJOR, MINOR_BASE + index); 48 | tty->ops = &tty_ops; 49 | 50 | ASSERT_OK(tty_register(tty)); 51 | return console; 52 | } 53 | 54 | void serial_console_init(void) { 55 | for (uint8_t i = 0; i < SERIAL_NUM_PORTS; ++i) { 56 | if (serial_is_port_enabled(i)) { 57 | struct serial_console* console = serial_console_create(i); 58 | ASSERT_PTR(console); 59 | consoles[i] = console; 60 | } 61 | } 62 | serial_set_input_handler(on_char); 63 | } 64 | -------------------------------------------------------------------------------- /kernel/ksyms.c: -------------------------------------------------------------------------------- 1 | #include "memory/memory.h" 2 | #include "panic.h" 3 | #include "system.h" 4 | #include 5 | 6 | extern char ksyms_start[]; 7 | extern char ksyms_end[]; 8 | 9 | static size_t num_symbols; 10 | static struct symbol* symbols; 11 | static uintptr_t lowest_addr; 12 | 13 | static unsigned int parse_hex_digit(char c) { 14 | if ('0' <= c && c <= '9') 15 | return c - '0'; 16 | ASSERT('a' <= c && c <= 'f'); 17 | return c - 'a' + 10; 18 | } 19 | 20 | void ksyms_init(void) { 21 | // Parse the output of `nm -n` 22 | // e.g. c0110580 T start 23 | 24 | size_t n = 0; 25 | for (const char* p = ksyms_start; p < ksyms_end; ++p) 26 | n += *p == '\n'; 27 | symbols = kmalloc(n * sizeof(struct symbol)); 28 | ASSERT(symbols); 29 | 30 | struct symbol* symbol = symbols; 31 | for (char* p = ksyms_start; p < ksyms_end; ++p, ++symbol) { 32 | uintptr_t addr = 0; 33 | for (; *p != ' '; ++p) 34 | addr = addr * 16 + parse_hex_digit(*p); 35 | if (symbol == symbols) 36 | lowest_addr = addr; 37 | symbol->addr = addr; 38 | 39 | while (*p == ' ') 40 | ++p; 41 | 42 | symbol->type = *p++; 43 | 44 | while (*p == ' ') 45 | ++p; 46 | 47 | symbol->name = p; 48 | while (*p != '\n') 49 | ++p; 50 | *p = 0; // Replace '\n' with '\0' 51 | } 52 | 53 | num_symbols = n; 54 | } 55 | 56 | const struct symbol* ksyms_lookup(uintptr_t addr) { 57 | if (num_symbols == 0 || addr < lowest_addr) 58 | return NULL; 59 | for (size_t i = 0; i < num_symbols - 1; ++i) { 60 | if (addr < symbols[i + 1].addr) 61 | return symbols + i; 62 | } 63 | return NULL; 64 | } 65 | 66 | const struct symbol* ksyms_next(const struct symbol* symbol) { 67 | if (num_symbols == 0) 68 | return NULL; 69 | if (symbol == NULL || symbol < symbols) 70 | return symbols; 71 | if (symbol >= symbols + num_symbols - 1) 72 | return NULL; 73 | return symbol + 1; 74 | } 75 | -------------------------------------------------------------------------------- /kernel/safe_string.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "api/sys/types.h" 4 | #include 5 | 6 | struct registers; 7 | 8 | // Safe string functions that can handle untrusted pointers without causing page 9 | // faults. 10 | 11 | // Copies n bytes from src to dest. Returns 0 on success, -EFAULT on failure. 12 | NODISCARD int safe_memcpy(void* dest, const void* src, size_t n); 13 | 14 | // Sets n bytes of s to c. Returns 0 on success, -EFAULT on failure. 15 | NODISCARD int safe_memset(void* s, unsigned char c, size_t n); 16 | 17 | // Gets the length of a string in kernel space. 18 | // Returns the shorter of the string length and n, or -EFAULT on failure. 19 | NODISCARD ssize_t safe_strnlen(const char* str, size_t n); 20 | 21 | // Copies n bytes from src to dest. 22 | // If the string is shorter than n, the rest of the dest buffer is zeroed. 23 | // Returns the shorter of the string length and n, or -EFAULT on failure. 24 | NODISCARD ssize_t safe_strncpy(char* dest, const char* src, size_t n); 25 | 26 | // Copies data from user space to kernel space. 27 | // Returns 0 on success, -EFAULT on failure. 28 | NODISCARD int copy_from_user(void* to, const void* user_from, size_t n); 29 | 30 | // Copies data from kernel space to user space. 31 | // Returns 0 on success, -EFAULT on failure. 32 | NODISCARD int copy_to_user(void* user_to, const void* from, size_t n); 33 | 34 | // Zeroes a block of memory in user space. 35 | // Returns 0 on success, -EFAULT on failure. 36 | NODISCARD int clear_user(void* user_to, size_t n); 37 | 38 | // Gets the length of a string in user space. 39 | // Returns the shorter of the string length and n, or -EFAULT on failure. 40 | NODISCARD ssize_t strnlen_user(const char* user_str, size_t n); 41 | 42 | // Copies a string from user space to kernel space. 43 | // If the string is shorter than n, the rest of the dest buffer is zeroed. 44 | // Returns the shorter of the string length and n, or -EFAULT on failure. 45 | NODISCARD ssize_t strncpy_from_user(char* dest, const char* user_src, size_t n); 46 | 47 | NODISCARD bool safe_string_handle_page_fault(struct registers* regs); 48 | -------------------------------------------------------------------------------- /userland/stat.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static const char* file_type(const struct stat* buf) { 9 | if (S_ISREG(buf->st_mode)) 10 | return buf->st_size == 0 ? "regular empty file" : "regular file"; 11 | if (S_ISDIR(buf->st_mode)) 12 | return "directory"; 13 | if (S_ISBLK(buf->st_mode)) 14 | return "block special file"; 15 | if (S_ISCHR(buf->st_mode)) 16 | return "character special file"; 17 | if (S_ISFIFO(buf->st_mode)) 18 | return "fifo"; 19 | if (S_ISLNK(buf->st_mode)) 20 | return "symbolic link"; 21 | if (S_ISSOCK(buf->st_mode)) 22 | return "socket"; 23 | return "weird file"; 24 | } 25 | 26 | int main(int argc, char* argv[]) { 27 | if (argc < 2) { 28 | dprintf(STDERR_FILENO, "Usage: stat FILE...\n"); 29 | return EXIT_FAILURE; 30 | } 31 | for (int i = 1; i < argc; ++i) { 32 | const char* filename = argv[i]; 33 | struct stat buf; 34 | if (lstat(filename, &buf) < 0) { 35 | perror("lstat"); 36 | return EXIT_FAILURE; 37 | } 38 | 39 | printf(" File: %s", filename); 40 | 41 | if (S_ISLNK(buf.st_mode)) { 42 | char target[SYMLINK_MAX + 1] = {0}; 43 | if (readlink(filename, target, SYMLINK_MAX) < 0) { 44 | perror("readlink"); 45 | return EXIT_FAILURE; 46 | } 47 | printf(" -> %s\n", target); 48 | } else { 49 | putchar('\n'); 50 | } 51 | 52 | printf(" Size: %-10d\t%s\n" 53 | "Device: %d,%d\tLinks: %-5d", 54 | buf.st_size, file_type(&buf), major(buf.st_dev), 55 | minor(buf.st_dev), buf.st_nlink); 56 | if (S_ISCHR(buf.st_mode) || S_ISBLK(buf.st_mode)) { 57 | printf("\tDevice type: %d,%d", major(buf.st_rdev), 58 | minor(buf.st_rdev)); 59 | } 60 | putchar('\n'); 61 | } 62 | return EXIT_SUCCESS; 63 | } 64 | -------------------------------------------------------------------------------- /kernel/cmdline.c: -------------------------------------------------------------------------------- 1 | #include "kmsg.h" 2 | #include "memory/memory.h" 3 | #include "multiboot.h" 4 | #include "system.h" 5 | #include 6 | 7 | #define MAX_CMDLINE_LEN 1024 8 | #define MAX_NUM_KEYS 1024 9 | 10 | static char raw[MAX_CMDLINE_LEN]; 11 | static char buf[MAX_CMDLINE_LEN]; 12 | static size_t num_keys = 0; 13 | static char* keys[MAX_NUM_KEYS]; 14 | static char* values[MAX_NUM_KEYS]; 15 | 16 | void cmdline_init(const multiboot_info_t* mb_info) { 17 | if (!(mb_info->flags & MULTIBOOT_INFO_CMDLINE)) { 18 | kprint("cmdline: no cmdline provided\n"); 19 | return; 20 | } 21 | 22 | const char* str = (const char*)(mb_info->cmdline + KERNEL_VIRT_ADDR); 23 | kprintf("cmdline: \"%s\"\n", str); 24 | strlcpy(raw, str, MAX_CMDLINE_LEN); 25 | strlcpy(buf, str, MAX_CMDLINE_LEN); 26 | 27 | char* saved_ptr; 28 | static const char* sep = " "; 29 | for (char* token = strtok_r(buf, sep, &saved_ptr); token; 30 | token = strtok_r(NULL, sep, &saved_ptr)) { 31 | keys[num_keys] = token; 32 | 33 | char* next_equal = strchr(token, '='); 34 | char* next_space = strchr(token, ' '); 35 | if (next_equal) { 36 | if (!next_space || next_equal < next_space) 37 | values[num_keys] = next_equal + 1; 38 | } 39 | 40 | ++num_keys; 41 | } 42 | 43 | // null terminate 44 | for (size_t i = 0; i < num_keys; ++i) { 45 | char* space = strchr(keys[i], ' '); 46 | if (space) 47 | *space = 0; 48 | if (values[i]) { 49 | char* equal = strchr(keys[i], '='); 50 | if (equal) 51 | *equal = 0; 52 | } 53 | } 54 | } 55 | 56 | const char* cmdline_get_raw(void) { return raw; } 57 | 58 | const char* cmdline_lookup(const char* key) { 59 | for (size_t i = 0; i < num_keys; ++i) { 60 | if (!strcmp(keys[i], key)) 61 | return values[i]; 62 | } 63 | return NULL; 64 | } 65 | 66 | bool cmdline_contains(const char* key) { 67 | for (size_t i = 0; i < num_keys; ++i) { 68 | if (!strcmp(keys[i], key)) 69 | return true; 70 | } 71 | return false; 72 | } 73 | -------------------------------------------------------------------------------- /kernel/memory/kmalloc.c: -------------------------------------------------------------------------------- 1 | #include "vm.h" 2 | #include 3 | #include 4 | 5 | void* kmalloc(size_t size) { 6 | size_t npages = DIV_CEIL(size, PAGE_SIZE); 7 | 8 | struct vm_obj* anon FREE(vm_obj) = anon_create(); 9 | if (IS_ERR(ASSERT(anon))) 10 | return NULL; 11 | 12 | void* addr = vm_obj_map(anon, 0, npages, VM_READ | VM_WRITE | VM_SHARED); 13 | if (IS_ERR(ASSERT(addr))) 14 | return NULL; 15 | return addr; 16 | } 17 | 18 | void* kaligned_alloc(size_t alignment, size_t size) { 19 | void* addr = kmalloc(size); 20 | // TODO: support non-page-aligned allocations 21 | ASSERT(((uintptr_t)addr % alignment) == 0); 22 | return addr; 23 | } 24 | 25 | void* krealloc(void* ptr, size_t new_size) { 26 | if (!ptr) 27 | return kmalloc(new_size); 28 | 29 | mutex_lock(&kernel_vm->lock); 30 | 31 | struct vm_region* region = vm_find(kernel_vm, ptr); 32 | ASSERT(region); 33 | ASSERT(ptr == vm_region_to_virt(region)); 34 | 35 | size_t new_npages = DIV_CEIL(new_size, PAGE_SIZE); 36 | size_t old_npages = region->end - region->start; 37 | if (new_npages == old_npages) { 38 | mutex_unlock(&kernel_vm->lock); 39 | return ptr; 40 | } 41 | 42 | int rc = vm_region_resize(region, new_npages); 43 | mutex_unlock(&kernel_vm->lock); 44 | if (IS_ERR(rc)) 45 | return NULL; 46 | 47 | // The region might have been moved 48 | return vm_region_to_virt(region); 49 | } 50 | 51 | void kfree(void* ptr) { vm_obj_unmap(ptr); } 52 | 53 | char* kstrdup(const char* src) { 54 | if (!src) 55 | return NULL; 56 | 57 | size_t len = strlen(src); 58 | char* buf = kmalloc((len + 1) * sizeof(char)); 59 | if (!buf) 60 | return NULL; 61 | 62 | memcpy(buf, src, len); 63 | buf[len] = '\0'; 64 | return buf; 65 | } 66 | 67 | char* kstrndup(const char* src, size_t n) { 68 | if (!src) 69 | return NULL; 70 | 71 | size_t len = strnlen(src, n); 72 | char* buf = kmalloc((len + 1) * sizeof(char)); 73 | if (!buf) 74 | return NULL; 75 | 76 | memcpy(buf, src, len); 77 | buf[len] = '\0'; 78 | return buf; 79 | } 80 | -------------------------------------------------------------------------------- /kernel/console/private.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "screen/screen.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | struct screen; 10 | struct tty; 11 | 12 | void serial_console_init(void); 13 | void virtual_console_init(struct screen*); 14 | void system_console_init(void); 15 | 16 | struct attr_char { 17 | char ch; 18 | bool eol; 19 | }; 20 | 21 | struct tty_ops { 22 | void (*echo)(struct tty*, const char* buf, size_t size); 23 | int (*ioctl)(struct tty*, struct file*, unsigned cmd, unsigned long arg); 24 | }; 25 | 26 | struct tty { 27 | struct char_dev char_dev; 28 | char name[16]; 29 | dev_t dev; 30 | const struct tty_ops* ops; 31 | size_t num_columns; 32 | size_t num_rows; 33 | 34 | struct termios termios; 35 | 36 | struct ring_buf input_buf; 37 | struct attr_char line_buf[1024]; 38 | size_t line_len; 39 | 40 | pid_t pgid; 41 | 42 | struct spinlock lock; 43 | }; 44 | 45 | extern const struct file_ops tty_fops; 46 | 47 | NODISCARD int tty_register(struct tty*); 48 | 49 | // Inputs the given string into the tty. 50 | ssize_t tty_emit(struct tty*, const char* buf, size_t count); 51 | 52 | // Creates a virtual terminal with the given screen as the backend. 53 | struct vt* vt_create(struct screen*); 54 | 55 | // Inputs into the vt. 56 | // Writes to the backing screen will be deferred until vt_flush is called. 57 | void vt_write(struct vt*, const char* buf, size_t count); 58 | 59 | // Sets the color palette for the vt. 60 | // The palette should be an array of 32-bit RGB values corresponding to 61 | // the ANSI color codes. 62 | void vt_set_palette(struct vt*, const uint32_t[NUM_COLORS]); 63 | 64 | // Gets the current font used by the vt. 65 | // Returns NULL if no font is set. 66 | NODISCARD struct font* vt_get_font(struct vt*); 67 | 68 | // Sets the font used by the vt, returning the previous font. 69 | // Returns NULL if no font was previously set. 70 | NODISCARD struct font* vt_swap_font(struct vt*, struct font*); 71 | 72 | // Invalidates the entire screen, forcing a redraw on the next flush. 73 | void vt_invalidate_all(struct vt*); 74 | 75 | // Flushes the updates to the screen. 76 | void vt_flush(struct vt*); 77 | -------------------------------------------------------------------------------- /kernel/drivers/rtc.c: -------------------------------------------------------------------------------- 1 | #include "rtc.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static uint8_t cmos_read(uint8_t idx) { 8 | out8(0x70, idx); 9 | return in8(0x71); 10 | } 11 | 12 | static uint8_t bcd_to_bin(uint8_t bcd) { 13 | return (bcd & 0xf) + ((bcd >> 4) * 10); 14 | } 15 | 16 | static unsigned days_since_epoch(unsigned year, unsigned month, unsigned day) { 17 | ASSERT(year >= 1970); 18 | unsigned days = day_of_year(year, month, day); 19 | for (unsigned y = 1970; y < year; ++y) 20 | days += days_in_year(y); 21 | return days; 22 | } 23 | 24 | time_t rtc_now(void) { 25 | int timeout = 100; 26 | bool update_finished = false; 27 | while (--timeout >= 0) { 28 | if (!(cmos_read(0x0a) & 0x80)) { 29 | update_finished = true; 30 | break; 31 | } 32 | delay(1000); 33 | } 34 | 35 | unsigned year = 1970; 36 | unsigned month = 1; 37 | unsigned day = 1; 38 | unsigned hour = 0; 39 | unsigned minute = 0; 40 | unsigned second = 0; 41 | 42 | if (update_finished) { 43 | uint8_t status = cmos_read(0xb); 44 | second = cmos_read(0x0); 45 | minute = cmos_read(0x2); 46 | hour = cmos_read(0x4); 47 | day = cmos_read(0x7); 48 | month = cmos_read(0x8); 49 | year = cmos_read(0x9); 50 | 51 | if (!(status & 0x4)) { 52 | second = bcd_to_bin(second); 53 | minute = bcd_to_bin(minute); 54 | hour = bcd_to_bin(hour & 0x7f); 55 | day = bcd_to_bin(day); 56 | month = bcd_to_bin(month); 57 | year = bcd_to_bin(year); 58 | } 59 | if (!(status & 0x2)) { 60 | hour %= 12; 61 | if (hour & 0x80) 62 | hour += 12; 63 | } 64 | 65 | year += 2000; 66 | 67 | kprintf("rtc: year=%u month=%u day=%u hour=%u minute=%u second=%u\n", 68 | year, month, day, hour, minute, second); 69 | } else { 70 | kprint("rtc: update did not finish within timeout. Falling back to " 71 | "UNIX epoch\n"); 72 | } 73 | 74 | time_t days = days_since_epoch(year, month, day); 75 | time_t hours = days * 24 + hour; 76 | time_t minutes = hours * 60 + minute; 77 | return minutes * 60 + second; 78 | } 79 | -------------------------------------------------------------------------------- /kernel/drivers/virtio/virtio_blk.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct virtio_blk_config { 6 | uint64_t capacity; 7 | uint32_t size_max; 8 | uint32_t seg_max; 9 | struct virtio_blk_geometry { 10 | uint16_t cylinders; 11 | uint8_t heads; 12 | uint8_t sectors; 13 | } geometry; 14 | uint32_t blk_size; 15 | struct virtio_blk_topology { 16 | // # of logical blocks per physical block (log2) 17 | uint8_t physical_block_exp; 18 | // offset of first aligned logical block 19 | uint8_t alignment_offset; 20 | // suggested minimum I/O size in blocks 21 | uint16_t min_io_size; 22 | // optimal (suggested maximum) I/O size in blocks 23 | uint32_t opt_io_size; 24 | } topology; 25 | uint8_t writeback; 26 | uint8_t unused0; 27 | uint16_t num_queues; 28 | uint32_t max_discard_sectors; 29 | uint32_t max_discard_seg; 30 | uint32_t discard_sector_alignment; 31 | uint32_t max_write_zeroes_sectors; 32 | uint32_t max_write_zeroes_seg; 33 | uint8_t write_zeroes_may_unmap; 34 | uint8_t unused1[3]; 35 | uint32_t max_secure_erase_sectors; 36 | uint32_t max_secure_erase_seg; 37 | uint32_t secure_erase_sector_alignment; 38 | struct virtio_blk_zoned_characteristics { 39 | uint32_t zone_sectors; 40 | uint32_t max_open_zones; 41 | uint32_t max_active_zones; 42 | uint32_t max_append_sectors; 43 | uint32_t write_granularity; 44 | uint8_t model; 45 | uint8_t unused2[3]; 46 | } zoned; 47 | } __attribute__((packed)); 48 | 49 | #define VIRTIO_BLK_T_IN 0 50 | #define VIRTIO_BLK_T_OUT 1 51 | #define VIRTIO_BLK_T_FLUSH 4 52 | #define VIRTIO_BLK_T_GET_ID 8 53 | #define VIRTIO_BLK_T_DISCARD 11 54 | #define VIRTIO_BLK_T_WRITE_ZEROES 13 55 | #define VIRTIO_BLK_T_SECURE_ERASE 14 56 | #define VIRTIO_BLK_T_ZONE_APPEND 15 57 | #define VIRTIO_BLK_T_ZONE_REPORT 16 58 | #define VIRTIO_BLK_T_ZONE_OPEN 18 59 | #define VIRTIO_BLK_T_ZONE_CLOSE 20 60 | #define VIRTIO_BLK_T_ZONE_FINISH 22 61 | #define VIRTIO_BLK_T_ZONE_RESET 24 62 | #define VIRTIO_BLK_T_ZONE_RESET_ALL 26 63 | 64 | #define VIRTIO_BLK_S_OK 0 65 | #define VIRTIO_BLK_S_IOERR 1 66 | #define VIRTIO_BLK_S_UNSUPP 2 67 | 68 | struct virtio_blk_req_header { 69 | uint32_t type; 70 | uint32_t reserved; 71 | uint64_t sector; 72 | }; 73 | 74 | struct virtio_blk_req_footer { 75 | uint8_t status; 76 | }; 77 | -------------------------------------------------------------------------------- /kernel/interrupts/interrupts.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | struct registers; 8 | 9 | #define IRQ(i) (0x20 + (i)) 10 | #define NUM_IRQS 16 11 | #define LAPIC_TIMER_VECTOR 0x82 12 | #define IPI_VECTOR 0x83 13 | #define LAPIC_ERROR_VECTOR 0x84 14 | #define SPURIOUS_VECTOR 0xff 15 | 16 | void idt_init(void); 17 | void idt_set_gate_user_callable(uint8_t index); 18 | void idt_flush(void); 19 | 20 | void i8259_init(void); 21 | void i8259_disable(void); 22 | void i8259_eoi(uint8_t irq); 23 | 24 | typedef void (*interrupt_handler_fn)(struct registers*); 25 | void idt_set_interrupt_handler(uint8_t num, interrupt_handler_fn handler); 26 | 27 | void lapic_init(void); 28 | void lapic_init_cpu(void); 29 | 30 | uint8_t lapic_get_id(void); 31 | void lapic_eoi(void); 32 | 33 | #define LAPIC_ICRLO_INIT 0x00000500 // INIT/RESET 34 | #define LAPIC_ICRLO_STARTUP 0x00000600 // Startup IPI 35 | #define LAPIC_ICRLO_LOGICAL 0x00000800 // Destination mode 36 | #define LAPIC_ICRLO_DELIVS 0x00001000 // Delivery status 37 | #define LAPIC_ICRLO_ASSERT 0x00004000 // Assert interrupt (vs deassert) 38 | #define LAPIC_ICRLO_DEASSERT 0x00000000 39 | #define LAPIC_ICRLO_LEVEL 0x00008000 // Level triggered 40 | #define LAPIC_ICRLO_ALL_INCL_SELF \ 41 | 0x00080000 // Send to all APICs, including self. 42 | #define LAPIC_ICRLO_ALL_EXCL_SELF \ 43 | 0x000c0000 // Send to all APICs, excluding self. 44 | 45 | void lapic_write_icr(uint32_t hi, uint32_t lo); 46 | void lapic_broadcast_ipi(void); 47 | void lapic_unicast_ipi(uint8_t apic_id); 48 | 49 | void io_apic_init(void); 50 | 51 | static inline bool interrupts_enabled(void) { 52 | return read_eflags() & X86_EFLAGS_IF; 53 | } 54 | 55 | static inline bool push_cli(void) { 56 | bool enabled = interrupts_enabled(); 57 | cli(); 58 | return enabled; 59 | } 60 | 61 | static inline void pop_cli(bool was_enabled) { 62 | ASSERT(!interrupts_enabled()); 63 | if (was_enabled) 64 | sti(); 65 | } 66 | 67 | static inline bool push_sti(void) { 68 | bool enabled = interrupts_enabled(); 69 | sti(); 70 | return enabled; 71 | } 72 | 73 | static inline void pop_sti(bool was_enabled) { 74 | ASSERT(interrupts_enabled()); 75 | if (!was_enabled) 76 | cli(); 77 | } 78 | 79 | void do_iret(struct registers); 80 | -------------------------------------------------------------------------------- /userland/lib/unistd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | extern char** environ; 8 | 9 | pid_t getpid(void); 10 | pid_t getppid(void); 11 | pid_t gettid(void); 12 | int setpgid(pid_t pid, pid_t pgid); 13 | pid_t getpgid(pid_t pid); 14 | pid_t getpgrp(void); 15 | 16 | pid_t fork(void); 17 | pid_t vfork(void); 18 | int execve(const char* pathname, char* const argv[], char* const envp[]); 19 | int execvpe(const char* file, char* const argv[], char* const envp[]); 20 | 21 | uid_t getuid(void); 22 | uid_t geteuid(void); 23 | gid_t getgid(void); 24 | gid_t getegid(void); 25 | 26 | int access(const char* pathname, int mode); 27 | int close(int fd); 28 | ssize_t read(int fd, void* buf, size_t count); 29 | ssize_t write(int fd, const void* buf, size_t count); 30 | ssize_t pread(int fd, void* buf, size_t count, off_t offset); 31 | ssize_t pwrite(int fd, const void* buf, size_t count, off_t offset); 32 | int truncate(const char* path, off_t length); 33 | int ftruncate(int fd, off_t length); 34 | off_t lseek(int fd, off_t offset, int whence); 35 | int mknod(const char* pathname, mode_t mode, dev_t dev); 36 | int link(const char* oldpath, const char* newpath); 37 | int symlink(const char* target, const char* linkpath); 38 | int unlink(const char* pathname); 39 | ssize_t readlink(const char* pathname, char* buf, size_t bufsiz); 40 | int rename(const char* oldpath, const char* newpath); 41 | int rmdir(const char* pathname); 42 | 43 | int dup(int oldfd); 44 | int dup2(int oldfd, int newfd); 45 | int dup3(int oldfd, int newfd, int flags); 46 | int pipe(int pipefd[2]); 47 | int pipe2(int pipefd[2], int flags); 48 | 49 | void sync(void); 50 | int syncfs(int fd); 51 | int fsync(int fd); 52 | int fdatasync(int fd); 53 | 54 | char* getcwd(char* buf, size_t size); 55 | int chdir(const char* path); 56 | 57 | pid_t tcgetpgrp(int fd); 58 | int tcsetpgrp(int fd, pid_t pgrp); 59 | 60 | unsigned int sleep(unsigned int seconds); 61 | int usleep(useconds_t usec); 62 | 63 | int pause(void); 64 | 65 | int gethostname(char* name, size_t len); 66 | int sethostname(const char* name, size_t len); 67 | int getdomainname(char* name, size_t len); 68 | int setdomainname(const char* name, size_t len); 69 | 70 | int reboot(int howto); 71 | 72 | enum { 73 | _SC_ARG_MAX, 74 | _SC_CLK_TCK, 75 | _SC_MONOTONIC_CLOCK, 76 | _SC_OPEN_MAX, 77 | _SC_PAGESIZE, 78 | _SC_PAGE_SIZE = _SC_PAGESIZE, 79 | _SC_SYMLOOP_MAX, 80 | }; 81 | 82 | long sysconf(int name); 83 | -------------------------------------------------------------------------------- /userland/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS += \ 2 | -isystemlib \ 3 | -isystem.. \ 4 | -isystem../common \ 5 | -isystem../kernel/api 6 | LDFLAGS += -L. 7 | 8 | BIN_TARGET_NAMES := \ 9 | cal \ 10 | cat \ 11 | clear \ 12 | cp \ 13 | date \ 14 | dd \ 15 | echo \ 16 | env \ 17 | eyes \ 18 | fib \ 19 | getty \ 20 | grep \ 21 | halt \ 22 | hexdump \ 23 | imgview \ 24 | init \ 25 | init-test \ 26 | kill \ 27 | less \ 28 | ln \ 29 | loadfont \ 30 | ls \ 31 | mandelbrot \ 32 | mkdir \ 33 | mkfifo \ 34 | mknod \ 35 | mount \ 36 | mouse-cursor \ 37 | moused \ 38 | mv \ 39 | play \ 40 | poweroff \ 41 | ps \ 42 | pwd \ 43 | readlink \ 44 | reboot \ 45 | rm \ 46 | rmdir \ 47 | sh \ 48 | sleep \ 49 | stat \ 50 | stty \ 51 | touch \ 52 | uname \ 53 | usertests \ 54 | wc \ 55 | xv6-usertests 56 | OUTDIR := ../base/bin 57 | 58 | BIN_TARGETS := $(BIN_TARGET_NAMES:%=$(OUTDIR)/%) 59 | BIN_TARGET_OBJS := $(BIN_TARGET_NAMES:=.o) 60 | BIN_TARGET_DEPS := $(BIN_TARGET_NAMES:=.d) 61 | 62 | LIB_TARGET := libc.a 63 | 64 | LIB_OBJS := \ 65 | ../common/math.o \ 66 | ../common/string.o \ 67 | ../common/strings.o \ 68 | ../common/stdio.o \ 69 | ../common/stdlib.o \ 70 | ../common/ubsan.o \ 71 | lib/asm.o \ 72 | lib/crt0.o \ 73 | lib/dirent.o \ 74 | lib/errno.o \ 75 | lib/fcntl.o \ 76 | lib/panic.o \ 77 | lib/pthread.o \ 78 | lib/sched.o \ 79 | lib/signal.o \ 80 | lib/stdio.o \ 81 | lib/stdlib.o \ 82 | lib/string.o \ 83 | lib/sys/ioctl.o \ 84 | lib/sys/mman.o \ 85 | lib/sys/mount.o \ 86 | lib/sys/poll.o \ 87 | lib/sys/prctl.o \ 88 | lib/sys/select.o \ 89 | lib/sys/socket.o \ 90 | lib/sys/stat.o \ 91 | lib/sys/sysinfo.o \ 92 | lib/sys/time.o \ 93 | lib/sys/times.o \ 94 | lib/sys/uio.o \ 95 | lib/sys/utsname.o \ 96 | lib/sys/wait.o \ 97 | lib/termios.o \ 98 | lib/time.o \ 99 | lib/unistd.o 100 | 101 | LIB_DEPS := $(LIB_OBJS:.o=.d) 102 | 103 | .PHONY: all clean 104 | 105 | all: $(BIN_TARGETS) 106 | 107 | $(BIN_TARGETS): $(OUTDIR)/% : %.o $(LIB_TARGET) 108 | @echo "[LD] $@" 109 | @$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -lc 110 | 111 | $(LIB_TARGET): $(LIB_OBJS) 112 | @echo "[AR] $@" 113 | @$(AR) rcs $@ $^ 114 | 115 | .c.o: 116 | @echo "[CC] $<" 117 | @$(CC) $(CFLAGS) -MMD -MP -c -o $@ $< 118 | 119 | .S.o: 120 | @echo "[AS] $<" 121 | @$(CC) $(CFLAGS) -MMD -MP -c -o $@ $< 122 | 123 | clean: 124 | $(RM) $(BIN_TARGETS) $(BIN_TARGET_OBJS) $(BIN_TARGET_DEPS) $(LIB_TARGET) $(LIB_OBJS) $(LIB_DEPS) 125 | 126 | -include $(BIN_TARGET_DEPS) 127 | -include $(LIB_DEPS) 128 | -------------------------------------------------------------------------------- /userland/lib/dirent.c: -------------------------------------------------------------------------------- 1 | #include "dirent.h" 2 | #include "errno.h" 3 | #include "fcntl.h" 4 | #include "private.h" 5 | #include "stdlib.h" 6 | #include "string.h" 7 | #include "unistd.h" 8 | #include 9 | 10 | static ssize_t getdents(int fd, struct linux_dirent* dirp, size_t count) { 11 | return __syscall_return(SYSCALL3(getdents, fd, dirp, count)); 12 | } 13 | 14 | typedef struct __DIR { 15 | int fd; 16 | struct linux_dirent* buf; 17 | size_t buf_capacity, buf_size; 18 | size_t buf_cursor; 19 | struct dirent dent; 20 | } DIR; 21 | 22 | DIR* opendir(const char* name) { 23 | DIR* dirp = malloc(sizeof(DIR)); 24 | if (!dirp) 25 | return NULL; 26 | *dirp = (DIR){0}; 27 | dirp->fd = open(name, O_RDONLY); 28 | if (dirp->fd < 0) { 29 | free(dirp); 30 | return NULL; 31 | } 32 | dirp->buf_capacity = 1024; 33 | return dirp; 34 | } 35 | 36 | int closedir(DIR* dirp) { 37 | int rc = close(dirp->fd); 38 | free(dirp->buf); 39 | free(dirp); 40 | return rc; 41 | } 42 | 43 | struct dirent* readdir(DIR* dirp) { 44 | if (dirp->buf_cursor >= dirp->buf_size) { 45 | int saved_errno = errno; 46 | for (;;) { 47 | struct linux_dirent* new_buf = 48 | realloc(dirp->buf, dirp->buf_capacity); 49 | if (!new_buf) 50 | return NULL; 51 | dirp->buf = new_buf; 52 | errno = 0; 53 | ssize_t nread = getdents(dirp->fd, dirp->buf, dirp->buf_capacity); 54 | if (nread == 0) { 55 | errno = saved_errno; 56 | return NULL; 57 | } 58 | if (nread > 0) { 59 | dirp->buf_size = nread; 60 | break; 61 | } 62 | if (errno != EINVAL) 63 | return NULL; 64 | dirp->buf_capacity *= 2; 65 | } 66 | errno = saved_errno; 67 | dirp->buf_cursor = 0; 68 | } 69 | 70 | const struct linux_dirent* src = 71 | (struct linux_dirent*)((unsigned char*)dirp->buf + dirp->buf_cursor); 72 | struct dirent* dest = &dirp->dent; 73 | dest->d_ino = src->d_ino; 74 | dest->d_off = src->d_off; 75 | dest->d_reclen = sizeof(struct dirent); 76 | size_t name_size = 77 | src->d_reclen - 2 - offsetof(struct linux_dirent, d_name); 78 | name_size = MIN(name_size, sizeof(dest->d_name)); 79 | strlcpy(dest->d_name, src->d_name, name_size); 80 | dest->d_type = *(src->d_name + name_size + 2); 81 | dirp->buf_cursor += src->d_reclen; 82 | return dest; 83 | } 84 | -------------------------------------------------------------------------------- /kernel/drivers/virtio/virtio_queue.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | struct virtq_desc { 9 | /* Address (guest-physical). */ 10 | uint64_t addr; 11 | /* Length. */ 12 | uint32_t len; 13 | 14 | /* This marks a buffer as continuing via the next field. */ 15 | #define VIRTQ_DESC_F_NEXT 1 16 | /* This marks a buffer as write-only (otherwise read-only). */ 17 | #define VIRTQ_DESC_F_WRITE 2 18 | /* This means the buffer contains a list of buffer descriptors. */ 19 | #define VIRTQ_DESC_F_INDIRECT 4 20 | /* The flags as indicated above. */ 21 | uint16_t flags; 22 | /* Next field if flags & NEXT */ 23 | uint16_t next; 24 | }; 25 | 26 | struct virtq_avail { 27 | #define VIRTQ_AVAIL_F_NO_INTERRUPT 1 28 | uint16_t flags; 29 | uint16_t idx; 30 | uint16_t ring[]; 31 | }; 32 | 33 | struct virtq_used_elem { 34 | /* Index of start of used descriptor chain. */ 35 | uint32_t id; 36 | /* 37 | * The number of bytes written into the device writable portion of 38 | * the buffer described by the descriptor chain. 39 | */ 40 | uint32_t len; 41 | }; 42 | 43 | struct virtq_used { 44 | #define VIRTQ_USED_F_NO_NOTIFY 1 45 | uint16_t flags; 46 | uint16_t idx; 47 | struct virtq_used_elem ring[]; 48 | }; 49 | 50 | struct virtq { 51 | uint16_t index; // The index of the queue 52 | size_t size; // The number of descriptors 53 | atomic_size_t num_free_descs; // The number of free descriptors 54 | size_t free_head; // The index of the first free descriptor 55 | uint16_t avail_index_shadow; // The copy of avail->idx 56 | 57 | // The actual descriptors (16 bytes each) 58 | struct virtq_desc* desc; 59 | 60 | // A ring of available descriptor heads with free-running index. 61 | struct virtq_avail* avail; 62 | 63 | // A ring of used descriptor heads with free-running index. 64 | volatile struct virtq_used* used; 65 | 66 | uint16_t* notify; // The notification address 67 | }; 68 | 69 | bool virtq_is_ready(const struct virtq*); 70 | 71 | struct virtq_desc_chain { 72 | struct virtq* virtq; 73 | size_t num_pushed; 74 | uint16_t head; 75 | uint16_t tail; 76 | }; 77 | 78 | bool virtq_desc_chain_init(struct virtq_desc_chain*, struct virtq*, 79 | size_t num_descriptors); 80 | void virtq_desc_chain_push_buf(struct virtq_desc_chain*, void* buf, size_t len, 81 | bool device_writable); 82 | int virtq_desc_chain_submit(struct virtq_desc_chain*); 83 | -------------------------------------------------------------------------------- /userland/lib/crt0.c: -------------------------------------------------------------------------------- 1 | #include "errno.h" 2 | #include "panic.h" 3 | #include "private.h" 4 | #include "stdlib.h" 5 | #include "string.h" 6 | #include "sys/auxv.h" 7 | #include "unistd.h" 8 | #include 9 | #include 10 | #include 11 | 12 | static uint32_t auxv[32]; 13 | 14 | unsigned long getauxval(unsigned long type) { 15 | if (type >= ARRAY_SIZE(auxv)) { 16 | errno = ENOENT; 17 | return 0; 18 | } 19 | return auxv[type]; 20 | } 21 | 22 | static const Elf32_Phdr* tls_phdr; 23 | 24 | static const Elf32_Phdr* find_tls_phdr(void) { 25 | Elf32_Phdr* phdr = (Elf32_Phdr*)getauxval(AT_PHDR); 26 | size_t n = getauxval(AT_PHNUM); 27 | for (size_t i = 0; i < n; ++i) { 28 | if (phdr[i].p_type == PT_TLS) 29 | return &phdr[i]; 30 | } 31 | return NULL; 32 | } 33 | 34 | size_t __tls_size; 35 | 36 | struct pthread* __init_tls(void* tls) { 37 | memset(tls, 0, __tls_size); 38 | 39 | uintptr_t p = (uintptr_t)tls + __tls_size - sizeof(struct pthread); 40 | p = ROUND_DOWN(p, tls_phdr->p_align); 41 | memcpy((unsigned char*)p - tls_phdr->p_memsz, (void*)tls_phdr->p_vaddr, 42 | tls_phdr->p_filesz); 43 | 44 | struct pthread* pth = (struct pthread*)p; 45 | pth->self = pth; 46 | return pth; 47 | } 48 | 49 | static void set_thread_area(void* addr) { 50 | struct user_desc tls_desc = { 51 | .entry_number = -1, 52 | .base_addr = (unsigned)addr, 53 | .limit = 0xfffff, 54 | .seg_32bit = 1, 55 | .limit_in_pages = 1, 56 | }; 57 | ASSERT_OK(SYSCALL1(set_thread_area, &tls_desc)); 58 | uint16_t gs = (tls_desc.entry_number * 8) | 3; 59 | __asm__ volatile("movw %0, %%gs" ::"r"(gs)); 60 | } 61 | 62 | int main(int argc, char* const argv[], char* const envp[]); 63 | 64 | void __start(unsigned long* args) { 65 | int argc = args[0]; 66 | char** argv = (char**)(args + 1); 67 | environ = argv + argc + 1; 68 | 69 | // Initialize auxv 70 | char** p = environ; 71 | while (*p++) 72 | ; 73 | for (Elf32_auxv_t* aux = (Elf32_auxv_t*)p; aux->a_type != AT_NULL; ++aux) { 74 | if (aux->a_type < ARRAY_SIZE(auxv)) 75 | auxv[aux->a_type] = aux->a_un.a_val; 76 | } 77 | 78 | // Initialize TLS 79 | tls_phdr = find_tls_phdr(); 80 | ASSERT(tls_phdr); 81 | __tls_size = tls_phdr->p_memsz + tls_phdr->p_align + sizeof(struct pthread); 82 | 83 | // Initialize TLS for the main thread 84 | unsigned char* tls = malloc(__tls_size); 85 | ASSERT(tls); 86 | set_thread_area(__init_tls(tls)); 87 | 88 | exit(main(argc, argv, environ)); 89 | } 90 | -------------------------------------------------------------------------------- /userland/lib/signal.c: -------------------------------------------------------------------------------- 1 | #include "signal.h" 2 | #include "errno.h" 3 | #include 4 | #include 5 | 6 | int kill(pid_t pid, int sig) { 7 | return __syscall_return(SYSCALL2(kill, pid, sig)); 8 | } 9 | 10 | int raise(int sig) { return kill(gettid(), sig); } 11 | 12 | sighandler_t signal(int signum, sighandler_t handler) { 13 | struct sigaction act = { 14 | .sa_handler = handler, 15 | .sa_flags = SA_RESTART, 16 | }; 17 | struct sigaction oldact; 18 | if (sigaction(signum, &act, &oldact) < 0) 19 | return SIG_ERR; 20 | return oldact.sa_handler; 21 | } 22 | 23 | void __sa_restorer(void); 24 | 25 | int sigaction(int signum, const struct sigaction* act, 26 | struct sigaction* oldact) { 27 | struct sigaction sa = *act; 28 | sa.sa_flags |= SA_RESTORER; 29 | sa.sa_restorer = __sa_restorer; 30 | return __syscall_return(SYSCALL3(sigaction, signum, &sa, oldact)); 31 | } 32 | 33 | int sigprocmask(int how, const sigset_t* set, sigset_t* oldset) { 34 | return __syscall_return(SYSCALL3(sigprocmask, how, set, oldset)); 35 | } 36 | 37 | int sigsuspend(const sigset_t* mask) { 38 | return __syscall_return(SYSCALL1(sigsuspend, mask)); 39 | } 40 | 41 | int sigpending(sigset_t* set) { 42 | return __syscall_return(SYSCALL1(sigpending, set)); 43 | } 44 | 45 | int sigemptyset(sigset_t* set) { 46 | *set = 0; 47 | return 0; 48 | } 49 | 50 | int sigfillset(sigset_t* set) { 51 | *set = ~0; 52 | return 0; 53 | } 54 | 55 | #define VALIDATE_SIGNUM(signum) \ 56 | if ((signum) <= 0 || NSIG <= (signum)) { \ 57 | errno = EINVAL; \ 58 | return -1; \ 59 | } 60 | 61 | int sigaddset(sigset_t* set, int signum) { 62 | VALIDATE_SIGNUM(signum); 63 | *set |= sigmask(signum); 64 | return 0; 65 | } 66 | 67 | int sigdelset(sigset_t* set, int signum) { 68 | VALIDATE_SIGNUM(signum); 69 | *set &= ~sigmask(signum); 70 | return 0; 71 | } 72 | 73 | int sigorset(sigset_t* dest, const sigset_t* left, const sigset_t* right) { 74 | *dest = *left | *right; 75 | return 0; 76 | } 77 | 78 | int sigandset(sigset_t* dest, const sigset_t* left, const sigset_t* right) { 79 | *dest = *left & *right; 80 | return 0; 81 | } 82 | 83 | int sigismember(const sigset_t* set, int signum) { 84 | VALIDATE_SIGNUM(signum); 85 | return (*set & sigmask(signum)) != 0; 86 | } 87 | 88 | int sigisemptyset(const sigset_t* set) { return !*set; } 89 | -------------------------------------------------------------------------------- /kernel/drivers/virtio/virtio_pci.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | /* Common configuration */ 6 | #define VIRTIO_PCI_CAP_COMMON_CFG 1 7 | /* Notifications */ 8 | #define VIRTIO_PCI_CAP_NOTIFY_CFG 2 9 | /* ISR Status */ 10 | #define VIRTIO_PCI_CAP_ISR_CFG 3 11 | /* Device specific configuration */ 12 | #define VIRTIO_PCI_CAP_DEVICE_CFG 4 13 | /* PCI configuration access */ 14 | #define VIRTIO_PCI_CAP_PCI_CFG 5 15 | /* Shared memory region */ 16 | #define VIRTIO_PCI_CAP_SHARED_MEMORY_CFG 8 17 | /* Vendor-specific data */ 18 | #define VIRTIO_PCI_CAP_VENDOR_CFG 9 19 | 20 | struct virtio_pci_cap { 21 | uint8_t cap_vndr; /* Generic PCI field: PCI_CAP_ID_VNDR */ 22 | uint8_t cap_next; /* Generic PCI field: next ptr. */ 23 | uint8_t cap_len; /* Generic PCI field: capability length */ 24 | uint8_t cfg_type; /* Identifies the structure. */ 25 | uint8_t bar; /* Where to find it. */ 26 | uint8_t id; /* Multiple capabilities of the same type */ 27 | uint8_t padding[2]; /* Pad to full dword. */ 28 | uint32_t offset; /* Offset within bar. */ 29 | uint32_t length; /* Length of the structure, in bytes. */ 30 | }; 31 | 32 | struct virtio_pci_common_cfg { 33 | /* About the whole device. */ 34 | uint32_t device_feature_select; /* read-write */ 35 | uint32_t device_feature; /* read-only for driver */ 36 | uint32_t driver_feature_select; /* read-write */ 37 | uint32_t driver_feature; /* read-write */ 38 | uint16_t config_msix_vector; /* read-write */ 39 | uint16_t num_queues; /* read-only for driver */ 40 | uint8_t device_status; /* read-write */ 41 | uint8_t config_generation; /* read-only for driver */ 42 | 43 | /* About a specific virtqueue. */ 44 | uint16_t queue_select; /* read-write */ 45 | uint16_t queue_size; /* read-write */ 46 | uint16_t queue_msix_vector; /* read-write */ 47 | uint16_t queue_enable; /* read-write */ 48 | uint16_t queue_notify_off; /* read-only for driver */ 49 | uint64_t queue_desc; /* read-write */ 50 | uint64_t queue_driver; /* read-write */ 51 | uint64_t queue_device; /* read-write */ 52 | uint16_t queue_notif_config_data; /* read-only for driver */ 53 | uint16_t queue_reset; /* read-write */ 54 | 55 | /* About the administration virtqueue. */ 56 | uint16_t admin_queue_index; /* read-only for driver */ 57 | uint16_t admin_queue_num; /* read-only for driver */ 58 | }; 59 | 60 | struct virtio_pci_notify_cap { 61 | struct virtio_pci_cap cap; 62 | uint32_t notify_off_multiplier; /* Multiplier for queue_notify_off. */ 63 | }; 64 | -------------------------------------------------------------------------------- /userland/hexdump.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static int dump_file(int fd, bool canonical) { 8 | size_t offset = 0; 9 | for (;;) { 10 | uint8_t buf[16]; 11 | ssize_t nread; 12 | size_t buf_len = 0; 13 | while (buf_len < sizeof(buf)) { 14 | nread = read(fd, (char*)buf + buf_len, sizeof(buf) - buf_len); 15 | if (nread < 0) { 16 | perror("read"); 17 | return -1; 18 | } 19 | if (nread == 0) 20 | break; 21 | buf_len += nread; 22 | } 23 | if (buf_len == 0) 24 | break; 25 | 26 | printf("%08x", offset); 27 | offset += 0x10; 28 | 29 | if (canonical) { 30 | putchar(' '); 31 | for (size_t i = 0; i < sizeof(buf); ++i) { 32 | if (i < buf_len) 33 | printf(" %02x", buf[i]); 34 | else 35 | printf(" "); 36 | if (i == 7) 37 | putchar(' '); 38 | } 39 | printf(" |"); 40 | for (size_t i = 0; i < buf_len; ++i) 41 | putchar(isprint(buf[i]) ? buf[i] : '.'); 42 | printf("|\n"); 43 | } else { 44 | for (size_t i = 0; i < buf_len; i += 2) { 45 | uint16_t word = buf[i]; 46 | if (i + 1 < buf_len) 47 | word |= buf[i + 1] << 8; 48 | printf(" %04x", word); 49 | } 50 | putchar('\n'); 51 | } 52 | 53 | if (nread == 0) 54 | break; 55 | } 56 | return 0; 57 | } 58 | 59 | int main(int argc, char* argv[]) { 60 | bool canonical = false; 61 | size_t num_files = 0; 62 | for (int i = 1; i < argc; ++i) { 63 | if (!strcmp(argv[i], "-C")) 64 | canonical = true; 65 | else 66 | ++num_files; 67 | } 68 | 69 | if (num_files == 0) { 70 | if (dump_file(STDIN_FILENO, canonical) < 0) 71 | return EXIT_FAILURE; 72 | return EXIT_SUCCESS; 73 | } 74 | 75 | int ret = EXIT_SUCCESS; 76 | for (int i = 1; i < argc; ++i) { 77 | const char* filename = argv[i]; 78 | if (!strcmp(filename, "-C")) 79 | continue; 80 | int fd = 81 | strcmp(filename, "-") ? open(filename, O_RDONLY) : STDIN_FILENO; 82 | if (fd < 0) { 83 | perror("open"); 84 | continue; 85 | } 86 | if (dump_file(fd, canonical) < 0) 87 | ret = EXIT_FAILURE; 88 | if (fd != STDIN_FILENO) 89 | close(fd); 90 | } 91 | return ret; 92 | } 93 | -------------------------------------------------------------------------------- /kernel/containers/vec.c: -------------------------------------------------------------------------------- 1 | #include "vec.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | void vec_destroy(struct vec* vec) { kfree(vec->data); } 8 | 9 | ssize_t vec_pread(struct vec* vec, void* bytes, size_t count, uint64_t offset) { 10 | if (offset >= vec->size) 11 | return 0; 12 | if (offset + count >= vec->size) 13 | count = vec->size - offset; 14 | 15 | memcpy(bytes, vec->data + offset, count); 16 | return count; 17 | } 18 | 19 | NODISCARD static int grow_capacity(struct vec* vec, size_t requested_size) { 20 | size_t new_capacity = vec->capacity * 2; 21 | if (new_capacity < vec->capacity) 22 | return -EOVERFLOW; 23 | new_capacity = MAX(new_capacity, requested_size); 24 | if (new_capacity == 0) 25 | new_capacity = PAGE_SIZE; 26 | new_capacity = ROUND_UP(new_capacity, PAGE_SIZE); 27 | if (new_capacity == 0) 28 | return -EOVERFLOW; 29 | ASSERT(new_capacity > vec->capacity); 30 | 31 | unsigned char* new_buf = krealloc(vec->data, new_capacity); 32 | if (!new_buf) 33 | return -ENOMEM; 34 | 35 | memset(new_buf + vec->size, 0, new_capacity - vec->size); 36 | 37 | vec->data = new_buf; 38 | vec->capacity = new_capacity; 39 | return 0; 40 | } 41 | 42 | ssize_t vec_pwrite(struct vec* vec, const void* bytes, size_t count, 43 | uint64_t offset) { 44 | uint64_t end = offset + count; 45 | if (end > vec->capacity) { 46 | int rc = grow_capacity(vec, end); 47 | if (IS_ERR(rc)) 48 | return rc; 49 | } 50 | 51 | memcpy(vec->data + offset, bytes, count); 52 | if (vec->size < end) 53 | vec->size = end; 54 | 55 | return count; 56 | } 57 | 58 | ssize_t vec_append(struct vec* vec, const void* bytes, size_t count) { 59 | return vec_pwrite(vec, bytes, count, vec->size); 60 | } 61 | 62 | int vec_printf(struct vec* vec, const char* format, ...) { 63 | va_list args; 64 | va_start(args, format); 65 | int ret = vec_vsprintf(vec, format, args); 66 | va_end(args); 67 | return ret; 68 | } 69 | 70 | int vec_vsprintf(struct vec* vec, const char* format, va_list args) { 71 | for (;;) { 72 | uint64_t max_len = vec->capacity - vec->size; 73 | if (max_len > 0) { 74 | char* dest = (char*)(vec->data + vec->size); 75 | int len = vsnprintf(dest, max_len, format, args); 76 | if ((uint64_t)len < max_len) { 77 | vec->size += len; 78 | return len; 79 | } 80 | } 81 | int rc = 82 | grow_capacity(vec, 0); // specify 0 to let grow_capacity decide size 83 | if (IS_ERR(rc)) 84 | return rc; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /kernel/gdt.c: -------------------------------------------------------------------------------- 1 | #include "gdt.h" 2 | #include "cpu.h" 3 | #include "interrupts/interrupts.h" 4 | 5 | static void gdt_set_segment(struct gdt_segment* gdt, size_t index, 6 | uint32_t base, uint32_t limit, uint8_t access, 7 | uint8_t flags) { 8 | ASSERT(index < NUM_GDT_ENTRIES); 9 | struct gdt_segment* s = gdt + index; 10 | s->base_lo = base & 0xffff; 11 | s->base_mid = (base >> 16) & 0xff; 12 | s->base_hi = (base >> 24) & 0xff; 13 | 14 | s->limit_lo = limit & 0xffff; 15 | s->limit_hi = (limit >> 16) & 0xf; 16 | 17 | s->access = access; 18 | s->flags = flags & 0xf; 19 | } 20 | 21 | void gdt_init_cpu(void) { 22 | // Avoid using cpu_get_current() here, as it relies on GDT_ENTRY_CPU_ID 23 | struct cpu* cpu = NULL; 24 | size_t cpu_id = 0; 25 | uint8_t apic_id = lapic_get_id(); 26 | for (; cpu_id < num_cpus; ++cpu_id) { 27 | if (cpus[cpu_id]->apic_id == apic_id) { 28 | cpu = cpus[cpu_id]; 29 | break; 30 | } 31 | } 32 | ASSERT(cpu); 33 | 34 | struct gdtr* gdtr = &cpu->gdtr; 35 | struct gdt_segment* gdt = cpu->gdt; 36 | struct tss* tss = &cpu->tss; 37 | 38 | *gdtr = (struct gdtr){ 39 | .limit = sizeof(cpu->gdt) - 1, 40 | .base = (uint32_t)gdt, 41 | }; 42 | 43 | gdt_set_segment(gdt, 0, 0, 0, 0, 0); 44 | gdt_set_segment(gdt, GDT_ENTRY_KERNEL_CS, 0, 0xfffff, 0x9a, 0xc); 45 | gdt_set_segment(gdt, GDT_ENTRY_KERNEL_DS, 0, 0xfffff, 0x92, 0xc); 46 | gdt_set_segment(gdt, GDT_ENTRY_USER_CS, 0, 0xfffff, 0xfa, 0xc); 47 | gdt_set_segment(gdt, GDT_ENTRY_USER_DS, 0, 0xfffff, 0xf2, 0xc); 48 | gdt_set_segment(gdt, GDT_ENTRY_TSS, (uint32_t)tss, sizeof(struct tss) - 1, 49 | 0x89, 0); 50 | 51 | for (size_t i = 0; i < NUM_GDT_TLS_ENTRIES; ++i) 52 | gdt_set_segment(gdt, GDT_ENTRY_TLS_MIN + i, 0, 0, 0, 0); 53 | 54 | gdt_set_segment(gdt, GDT_ENTRY_CPU_ID, 0, cpu_id, 0x95, 0x4); 55 | 56 | *tss = (struct tss){ 57 | .ss0 = KERNEL_DS, 58 | .iomap_base = sizeof(struct tss), 59 | }; 60 | 61 | __asm__ volatile("lgdt %0\n" 62 | "movw %%ax, %%ds\n" 63 | "movw %%ax, %%es\n" 64 | "movw %%ax, %%fs\n" 65 | "movw %%ax, %%gs\n" 66 | "movw %%ax, %%ss\n" 67 | "ljmpl $0x8, $1f\n" 68 | "1:" 69 | : 70 | : "m"(*gdtr), "a"(KERNEL_DS) 71 | : "memory"); 72 | 73 | __asm__ volatile("ltr %%ax" ::"a"(TSS_SELECTOR)); 74 | } 75 | 76 | void gdt_set_cpu_kernel_stack(uintptr_t stack_top) { 77 | ASSERT(!interrupts_enabled()); 78 | ASSERT(stack_top); 79 | cpu_get_current()->tss.esp0 = stack_top; 80 | } 81 | -------------------------------------------------------------------------------- /kernel/fs/initrd.c: -------------------------------------------------------------------------------- 1 | #include "fs.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | struct cpio_odc_header { 10 | char c_magic[6]; 11 | char c_dev[6]; 12 | char c_ino[6]; 13 | char c_mode[6]; 14 | char c_uid[6]; 15 | char c_gid[6]; 16 | char c_nlink[6]; 17 | char c_rdev[6]; 18 | char c_mtime[11]; 19 | char c_namesize[6]; 20 | char c_filesize[11]; 21 | } __attribute__((packed)); 22 | 23 | static size_t parse_octal(const char* s, size_t len) { 24 | size_t res = 0; 25 | for (size_t i = 0; i < len; ++i) { 26 | res += s[i] - '0'; 27 | if (i < len - 1) 28 | res *= 8; 29 | } 30 | return res; 31 | } 32 | 33 | #define PARSE_FIELD(field) parse_octal(field, sizeof(field)) 34 | 35 | void initrd_populate_root_fs(uintptr_t phys_addr, size_t size) { 36 | void* initrd FREE(phys) = phys_map(phys_addr, size, VM_READ); 37 | ASSERT_PTR(initrd); 38 | 39 | struct path* root FREE(path) = vfs_get_root(); 40 | ASSERT_PTR(root); 41 | 42 | unsigned char* cursor = initrd; 43 | for (;;) { 44 | const struct cpio_odc_header* header = (const void*)cursor; 45 | ASSERT(!strncmp(header->c_magic, "070707", 6)); 46 | 47 | size_t name_size = PARSE_FIELD(header->c_namesize); 48 | char* filename = (char*)(cursor + sizeof(struct cpio_odc_header)); 49 | if (!strncmp(filename, "TRAILER!!!", name_size)) 50 | break; 51 | 52 | size_t mode = PARSE_FIELD(header->c_mode); 53 | mode_t rdev = PARSE_FIELD(header->c_rdev); 54 | size_t file_size = PARSE_FIELD(header->c_filesize); 55 | unsigned char* content = (void*)(filename + name_size); 56 | 57 | if (S_ISREG(mode) || S_ISLNK(mode)) { 58 | struct file* file FREE(file) = 59 | vfs_open_at(root, filename, O_CREAT | O_EXCL | O_WRONLY, mode); 60 | ASSERT_PTR(file); 61 | 62 | file->inode->rdev = rdev; 63 | 64 | ASSERT_OK(file_truncate(file, file_size)); 65 | 66 | struct vm_obj* obj FREE(vm_obj) = file_mmap(file); 67 | ASSERT_PTR(obj); 68 | 69 | unsigned char* dest = 70 | vm_obj_map(obj, 0, DIV_CEIL(file_size, PAGE_SIZE), 71 | VM_WRITE | VM_READ | VM_SHARED); 72 | ASSERT_PTR(dest); 73 | memcpy(dest, content, file_size); 74 | vm_obj_unmap(dest); 75 | } else { 76 | struct inode* inode FREE(inode) = 77 | vfs_create_at(root, filename, mode); 78 | ASSERT_PTR(inode); 79 | inode->rdev = rdev; 80 | } 81 | 82 | cursor = content + file_size; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /kernel/drivers/hid/keyboard.c: -------------------------------------------------------------------------------- 1 | #include "hid.h" 2 | #include "ps2.h" 3 | #include 4 | 5 | static const struct keyboard_events noop_handlers = {0}; 6 | static _Atomic(const struct keyboard_events*) event_handlers = &noop_handlers; 7 | 8 | void keyboard_set_event_handlers(const struct keyboard_events* handlers) { 9 | event_handlers = handlers; 10 | } 11 | 12 | static const unsigned char keycodes[256] = { 13 | 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 14 | 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 15 | 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 16 | 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 17 | 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 18 | 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 19 | 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 20 | 0x63, 0x0, 0x56, 0x57, 0x58, 0x75, 0x0, 0x0, 0x5f, 0xb7, 0xb8, 0xb9, 21 | 0x0, 0x0, 0x0, 0x0, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 22 | 0xbf, 0xc0, 0xc1, 0x0, 0x5d, 0x0, 0x0, 0x59, 0x0, 0x0, 0xc2, 0x5b, 23 | 0x5a, 0x5c, 0x0, 0x5e, 0x0, 0x7c, 0x79, 0x0, 0x0, 0x0, 0x0, 0x0, 24 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 25 | 0xa5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa3, 0x0, 0x0, 26 | 0x60, 0x61, 0x0, 0x0, 0x71, 0x8c, 0xa4, 0x0, 0xa6, 0x0, 0x0, 0x0, 27 | 0x0, 0x0, 0xff, 0x0, 0x0, 0x0, 0x72, 0x0, 0x73, 0x0, 0xac, 0x0, 28 | 0x0, 0x62, 0xff, 0x63, 0x64, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 29 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x77, 0x77, 0x66, 0x67, 0x68, 0x0, 0x69, 30 | 0x70, 0x6a, 0x76, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x0, 0x0, 0x0, 0x0, 31 | 0x0, 0x0, 0x0, 0x7d, 0x7e, 0x7f, 0x74, 0x8e, 0x0, 0x0, 0x0, 0x8f, 32 | 0x0, 0xd9, 0x9c, 0xad, 0x80, 0x9f, 0x9e, 0x9d, 0x9b, 0xe2, 0x0, 0x70, 33 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 34 | 0x0, 0x0, 0x0, 0x0, 35 | }; 36 | 37 | static bool received_e0 = false; 38 | 39 | static void irq_handler(struct registers* reg) { 40 | (void)reg; 41 | 42 | uint8_t data = in8(PS2_DATA); 43 | 44 | if (event_handlers->raw) 45 | event_handlers->raw(data); 46 | 47 | if (data == 0xe0) { 48 | received_e0 = true; 49 | return; 50 | } 51 | 52 | unsigned char ch = data & 0x7f; 53 | bool up = data & 0x80; 54 | 55 | unsigned char code = ch; 56 | if (received_e0) 57 | code |= 0x80; 58 | if (event_handlers->key) 59 | event_handlers->key(keycodes[code], !up); 60 | 61 | received_e0 = false; 62 | } 63 | 64 | void ps2_keyboard_init(void) { 65 | ps2_write(PS2_COMMAND, PS2_ENABLE_PORT1); 66 | idt_set_interrupt_handler(IRQ(1), irq_handler); 67 | } 68 | -------------------------------------------------------------------------------- /userland/lib/asm.S: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | .text 4 | .global _start 5 | _start: 6 | pushl %esp 7 | call __start // __start(&argc) 8 | ud2 9 | 10 | // int syscall(int num, int, int, int, int, int, int); 11 | .global syscall 12 | .hidden syscall 13 | .type syscall, @function 14 | syscall: 15 | pushl %ebx 16 | pushl %esi 17 | pushl %edi 18 | pushl %ebp 19 | movl 20(%esp), %eax 20 | movl 24(%esp), %ebx 21 | movl 28(%esp), %ecx 22 | movl 32(%esp), %edx 23 | movl 36(%esp), %esi 24 | movl 40(%esp), %edi 25 | movl 44(%esp), %ebp 26 | int $SYSCALL_VECTOR 27 | popl %ebp 28 | popl %edi 29 | popl %esi 30 | popl %ebx 31 | ret 32 | 33 | // In case of CLONE_VM, the child shares the same memory space with the parent, 34 | // and fn and arg will be gone as soon as the parent returns from clone. 35 | // Thus, we have to keep the fn and arg on the new stack so that the child can 36 | // call fn(arg) after the clone. 37 | 38 | // int __clone(int (*fn)(void*), void* stack, int flags, void* arg, 39 | // pid_t* parent_tid, void* tls, pid_t* child_tid); 40 | .globl __clone 41 | .hidden __clone 42 | .type __clone, @function 43 | __clone: 44 | pushl %ebx 45 | pushl %esi 46 | pushl %edi 47 | movl 20(%esp), %ecx // ecx = new stack 48 | andl $-16, %ecx // align the stack to 16 bytes boundary 49 | subl $8, %ecx // reserve space for fn and arg 50 | movl 28(%esp), %eax 51 | movl %eax, 4(%ecx) // push arg to the new stack 52 | movl 16(%esp), %eax 53 | movl %eax, (%ecx) // push fn to the new stack 54 | movl 24(%esp), %ebx // ebx = flags 55 | movl 32(%esp), %edx // edx = parent_tid 56 | movl 40(%esp), %esi // esi = child_tid 57 | movl 36(%esp), %edi // edi = tls 58 | movl $SYS_clone, %eax 59 | int $SYSCALL_VECTOR // clone(flags, stack, parent_tid, child_tid, tls) 60 | testl %eax, %eax 61 | jz child 62 | popl %edi 63 | popl %esi 64 | popl %ebx 65 | ret 66 | child: 67 | popl %eax // eax = fn 68 | call *%eax // fn(arg) 69 | movl %eax, %ebx // ebx = return value 70 | movl $SYS_exit, %eax 71 | int $SYSCALL_VECTOR // exit(fn(arg)) 72 | ud2 73 | 74 | // int __syscall_return(int rc); 75 | .hidden __syscall_return 76 | .type __syscall_return, @function 77 | 78 | // pid_t vfork(void); 79 | .globl vfork 80 | .type vfork, @function 81 | vfork: 82 | popl %edx // save return address 83 | movl $SYS_vfork, %eax 84 | int $SYSCALL_VECTOR 85 | pushl %edx 86 | pushl %eax 87 | call __syscall_return 88 | popl %eax 89 | ret 90 | 91 | // void __sa_restorer(void); 92 | .globl __sa_restorer 93 | .hidden __sa_restorer 94 | .type __sa_restorer, @object 95 | __sa_restorer: 96 | movl $SYS_sigreturn, %eax 97 | int $SYSCALL_VECTOR 98 | ud2 99 | -------------------------------------------------------------------------------- /kernel/api/sys/stat.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define S_IFMT 0170000 6 | #define S_IFDIR 0040000 7 | #define S_IFCHR 0020000 8 | #define S_IFBLK 0060000 9 | #define S_IFREG 0100000 10 | #define S_IFIFO 0010000 11 | #define S_IFLNK 0120000 12 | #define S_IFSOCK 0140000 13 | 14 | #define S_ISUID 04000 15 | #define S_ISGID 02000 16 | #define S_ISVTX 01000 17 | #define S_IRUSR 0400 18 | #define S_IWUSR 0200 19 | #define S_IXUSR 0100 20 | #define S_IREAD S_IRUSR 21 | #define S_IWRITE S_IWUSR 22 | #define S_IEXEC S_IXUSR 23 | #define S_IRGRP 0040 24 | #define S_IWGRP 0020 25 | #define S_IXGRP 0010 26 | #define S_IROTH 0004 27 | #define S_IWOTH 0002 28 | #define S_IXOTH 0001 29 | 30 | #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) 31 | #define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) 32 | #define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) 33 | #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) 34 | #define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) 35 | #define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) 36 | #define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) 37 | 38 | struct linux_stat { 39 | unsigned long st_dev; 40 | unsigned long st_ino; 41 | unsigned short st_mode; 42 | unsigned short st_nlink; 43 | unsigned short st_uid; 44 | unsigned short st_gid; 45 | unsigned long st_rdev; 46 | unsigned long st_size; 47 | unsigned long st_blksize; 48 | unsigned long st_blocks; 49 | unsigned long st_atime; 50 | unsigned long st_atime_nsec; 51 | unsigned long st_mtime; 52 | unsigned long st_mtime_nsec; 53 | unsigned long st_ctime; 54 | unsigned long st_ctime_nsec; 55 | unsigned long __unused4; 56 | unsigned long __unused5; 57 | }; 58 | 59 | struct linux_stat64 { 60 | unsigned long long st_dev; 61 | unsigned char __pad0[4]; 62 | 63 | unsigned long __st_ino; 64 | 65 | unsigned int st_mode; 66 | unsigned int st_nlink; 67 | 68 | unsigned long st_uid; 69 | unsigned long st_gid; 70 | 71 | unsigned long long st_rdev; 72 | unsigned char __pad3[4]; 73 | 74 | long long st_size; 75 | unsigned long st_blksize; 76 | 77 | /* Number 512-byte blocks allocated. */ 78 | unsigned long long st_blocks; 79 | 80 | unsigned long st_atime; 81 | unsigned long st_atime_nsec; 82 | 83 | unsigned long st_mtime; 84 | unsigned int st_mtime_nsec; 85 | 86 | unsigned long st_ctime; 87 | unsigned long st_ctime_nsec; 88 | 89 | unsigned long long st_ino; 90 | }; 91 | 92 | struct linux_old_stat { 93 | unsigned short st_dev; 94 | unsigned short st_ino; 95 | unsigned short st_mode; 96 | unsigned short st_nlink; 97 | unsigned short st_uid; 98 | unsigned short st_gid; 99 | unsigned short st_rdev; 100 | unsigned long st_size; 101 | unsigned long st_atime; 102 | unsigned long st_mtime; 103 | unsigned long st_ctime; 104 | }; 105 | -------------------------------------------------------------------------------- /kernel/lock.c: -------------------------------------------------------------------------------- 1 | #include "lock.h" 2 | #include "cpu.h" 3 | #include "interrupts/interrupts.h" 4 | #include "panic.h" 5 | #include "sched.h" 6 | #include "task.h" 7 | 8 | void mutex_lock(struct mutex* m) { 9 | ASSERT(interrupts_enabled()); 10 | 11 | for (;;) { 12 | bool expected = false; 13 | if (atomic_compare_exchange_strong_explicit(&m->lock, &expected, true, 14 | memory_order_acq_rel, 15 | memory_order_acquire)) { 16 | if (!m->holder || m->holder == current) { 17 | m->holder = current; 18 | ++m->level; 19 | atomic_store_explicit(&m->lock, false, memory_order_release); 20 | return; 21 | } 22 | atomic_store_explicit(&m->lock, false, memory_order_release); 23 | } 24 | sched_yield(true); 25 | } 26 | } 27 | 28 | void mutex_unlock(struct mutex* m) { 29 | ASSERT(interrupts_enabled()); 30 | 31 | for (;;) { 32 | bool expected = false; 33 | if (atomic_compare_exchange_strong_explicit(&m->lock, &expected, true, 34 | memory_order_acq_rel, 35 | memory_order_acquire)) { 36 | ASSERT(m->holder == current); 37 | ASSERT(m->level > 0); 38 | if (--m->level == 0) 39 | m->holder = NULL; 40 | atomic_store_explicit(&m->lock, false, memory_order_release); 41 | return; 42 | } 43 | sched_yield(true); 44 | } 45 | } 46 | 47 | bool mutex_is_locked_by_current(const struct mutex* m) { 48 | if (m->holder != current) 49 | return false; 50 | ASSERT(m->level > 0); 51 | return true; 52 | } 53 | 54 | #define SPINLOCK_LOCKED 0x1 55 | #define SPINLOCK_PUSHED_INTERRUPT 0x2 56 | #define SPINLOCK_CPU_ID_SHIFT 2 57 | 58 | void spinlock_lock(struct spinlock* s) { 59 | unsigned desired = SPINLOCK_LOCKED; 60 | if (interrupts_enabled()) 61 | desired |= SPINLOCK_PUSHED_INTERRUPT; 62 | cli(); 63 | uint8_t cpu_id = cpu_get_id(); 64 | desired |= (unsigned)cpu_id << SPINLOCK_CPU_ID_SHIFT; 65 | for (;;) { 66 | unsigned expected = 0; 67 | if (atomic_compare_exchange_strong(&s->lock, &expected, desired)) { 68 | ASSERT(s->level == 0); 69 | break; 70 | } 71 | if ((expected >> SPINLOCK_CPU_ID_SHIFT) == cpu_id) { 72 | ASSERT(expected & SPINLOCK_LOCKED); 73 | break; 74 | } 75 | cpu_pause(); 76 | } 77 | ++s->level; 78 | } 79 | 80 | void spinlock_unlock(struct spinlock* s) { 81 | ASSERT(!interrupts_enabled()); 82 | unsigned v = s->lock; 83 | ASSERT(v & SPINLOCK_LOCKED); 84 | ASSERT((v >> SPINLOCK_CPU_ID_SHIFT) == cpu_get_id()); 85 | ASSERT(s->level > 0); 86 | if (--s->level == 0) { 87 | atomic_store_explicit(&s->lock, 0, memory_order_release); 88 | if (v & SPINLOCK_PUSHED_INTERRUPT) 89 | sti(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /kernel/time.c: -------------------------------------------------------------------------------- 1 | #include "api/time.h" 2 | #include "api/errno.h" 3 | #include "drivers/rtc.h" 4 | #include "lock.h" 5 | #include "time.h" 6 | 7 | #define NANOS 1000000000 8 | 9 | void timespec_add(struct timespec* this, const struct timespec* other) { 10 | this->tv_sec += other->tv_sec; 11 | this->tv_nsec += other->tv_nsec; 12 | if (this->tv_nsec >= NANOS) { 13 | ++this->tv_sec; 14 | this->tv_nsec -= NANOS; 15 | } 16 | } 17 | 18 | void timespec_saturating_sub(struct timespec* this, 19 | const struct timespec* other) { 20 | this->tv_sec -= other->tv_sec; 21 | this->tv_nsec -= other->tv_nsec; 22 | if (this->tv_nsec < 0) { 23 | --this->tv_sec; 24 | this->tv_nsec += NANOS; 25 | } 26 | if (this->tv_sec < 0) 27 | this->tv_sec = this->tv_nsec = 0; 28 | } 29 | 30 | int timespec_compare(const struct timespec* a, const struct timespec* b) { 31 | if (a->tv_sec > b->tv_sec) 32 | return 1; 33 | if (a->tv_sec < b->tv_sec) 34 | return -1; 35 | if (a->tv_nsec > b->tv_nsec) 36 | return 1; 37 | if (a->tv_nsec < b->tv_nsec) 38 | return -1; 39 | return 0; 40 | } 41 | 42 | volatile atomic_uint uptime; 43 | static struct timespec now; 44 | static struct spinlock now_lock; 45 | 46 | void time_init(void) { now.tv_sec = rtc_now(); } 47 | 48 | void time_tick(void) { 49 | ++uptime; 50 | 51 | spinlock_lock(&now_lock); 52 | now.tv_nsec += NANOS / CLK_TCK; 53 | if (now.tv_nsec >= NANOS) { 54 | ++now.tv_sec; 55 | now.tv_nsec -= NANOS; 56 | } 57 | spinlock_unlock(&now_lock); 58 | } 59 | 60 | int time_now(clockid_t clock_id, struct timespec* tp) { 61 | switch (clock_id) { 62 | case CLOCK_REALTIME: 63 | spinlock_lock(&now_lock); 64 | *tp = now; 65 | spinlock_unlock(&now_lock); 66 | break; 67 | case CLOCK_MONOTONIC: { 68 | unsigned t = uptime; 69 | tp->tv_sec = t / CLK_TCK; 70 | tp->tv_nsec = 71 | divmodi64(((uint64_t)t - (uint64_t)tp->tv_sec * CLK_TCK) * NANOS, 72 | CLK_TCK, NULL); 73 | break; 74 | } 75 | default: 76 | return -EINVAL; 77 | } 78 | return 0; 79 | } 80 | 81 | int time_set(clockid_t clock_id, const struct timespec* tp) { 82 | if (tp->tv_sec < 0 || tp->tv_nsec < 0 || tp->tv_nsec >= NANOS) 83 | return -EINVAL; 84 | 85 | switch (clock_id) { 86 | case CLOCK_REALTIME: 87 | spinlock_lock(&now_lock); 88 | now = *tp; 89 | spinlock_unlock(&now_lock); 90 | break; 91 | default: 92 | return -EINVAL; 93 | } 94 | return 0; 95 | } 96 | 97 | int time_get_resolution(clockid_t clock_id, struct timespec* res) { 98 | switch (clock_id) { 99 | case CLOCK_REALTIME: 100 | case CLOCK_MONOTONIC: 101 | res->tv_sec = 0; 102 | res->tv_nsec = NANOS / CLK_TCK; 103 | break; 104 | default: 105 | return -EINVAL; 106 | } 107 | return 0; 108 | } 109 | -------------------------------------------------------------------------------- /kernel/drivers/graphics/bochs.c: -------------------------------------------------------------------------------- 1 | #include "graphics.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define VBE_DISPI_IOPORT_INDEX 0x01ce 10 | #define VBE_DISPI_IOPORT_DATA 0x01cf 11 | 12 | #define VBE_DISPI_INDEX_ID 0x0 13 | #define VBE_DISPI_INDEX_XRES 0x1 14 | #define VBE_DISPI_INDEX_YRES 0x2 15 | #define VBE_DISPI_INDEX_BPP 0x3 16 | #define VBE_DISPI_INDEX_ENABLE 0x4 17 | #define VBE_DISPI_INDEX_BANK 0x5 18 | #define VBE_DISPI_INDEX_VIRT_WIDTH 0x6 19 | #define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7 20 | #define VBE_DISPI_INDEX_X_OFFSET 0x8 21 | #define VBE_DISPI_INDEX_Y_OFFSET 0x9 22 | 23 | #define VBE_DISPI_DISABLED 0x00 24 | #define VBE_DISPI_ENABLED 0x01 25 | #define VBE_DISPI_LFB_ENABLED 0x40 26 | 27 | static struct fb_info info = { 28 | .id = "bochs", 29 | }; 30 | static struct mutex lock; 31 | 32 | static void pci_device_callback(const struct pci_addr* addr, uint16_t vendor_id, 33 | uint16_t device_id, void* ctx) { 34 | (void)ctx; 35 | if ((vendor_id == 0x1234 && device_id == 0x1111) || 36 | (vendor_id == 0x80ee && device_id == 0xbeef)) 37 | info.phys_addr = pci_get_bar(addr, 0) & 0xfffffff0; 38 | } 39 | 40 | static uint16_t read_reg(uint16_t index) { 41 | out16(VBE_DISPI_IOPORT_INDEX, index); 42 | return in16(VBE_DISPI_IOPORT_DATA); 43 | } 44 | 45 | static void write_reg(uint16_t index, uint16_t data) { 46 | out16(VBE_DISPI_IOPORT_INDEX, index); 47 | out16(VBE_DISPI_IOPORT_DATA, data); 48 | } 49 | 50 | static void configure(size_t width, size_t height, size_t bpp) { 51 | write_reg(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED); 52 | write_reg(VBE_DISPI_INDEX_XRES, width); 53 | write_reg(VBE_DISPI_INDEX_YRES, height); 54 | write_reg(VBE_DISPI_INDEX_VIRT_WIDTH, width); 55 | write_reg(VBE_DISPI_INDEX_VIRT_HEIGHT, height); 56 | write_reg(VBE_DISPI_INDEX_BPP, bpp); 57 | write_reg(VBE_DISPI_INDEX_ENABLE, 58 | VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED); 59 | 60 | info.width = read_reg(VBE_DISPI_INDEX_XRES); 61 | info.height = read_reg(VBE_DISPI_INDEX_YRES); 62 | info.bpp = read_reg(VBE_DISPI_INDEX_BPP); 63 | info.pitch = info.width * (info.bpp / 8); 64 | } 65 | 66 | static int bochs_fb_get_info(struct fb_info* out_info) { 67 | mutex_lock(&lock); 68 | *out_info = info; 69 | mutex_unlock(&lock); 70 | return 0; 71 | } 72 | 73 | static int bochs_fb_set_info(struct fb_info* inout_info) { 74 | mutex_lock(&lock); 75 | configure(inout_info->width, inout_info->height, inout_info->bpp); 76 | *inout_info = info; 77 | mutex_unlock(&lock); 78 | return 0; 79 | } 80 | 81 | struct fb* bochs_fb_init(void) { 82 | pci_enumerate_devices(pci_device_callback, NULL); 83 | if (!info.phys_addr) 84 | return NULL; 85 | 86 | kprintf("bochs_fb: found framebuffer at P%#x\n", info.phys_addr); 87 | configure(640, 480, 32); 88 | 89 | static struct fb fb = { 90 | .get_info = bochs_fb_get_info, 91 | .set_info = bochs_fb_set_info, 92 | }; 93 | return &fb; 94 | } 95 | -------------------------------------------------------------------------------- /kernel/api/elf.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef uint32_t Elf32_Addr; 6 | typedef uint32_t Elf32_Off; 7 | typedef uint32_t Elf32_Word; 8 | typedef uint16_t Elf32_Half; 9 | 10 | #define EI_MAG0 0 11 | #define EI_MAG1 1 12 | #define EI_MAG2 2 13 | #define EI_MAG3 3 14 | #define EI_CLASS 4 15 | #define EI_DATA 5 16 | #define EI_VERSION 6 17 | #define EI_OSABI 7 18 | #define EI_ABIVERSION 8 19 | #define EI_NIDENT 16 20 | 21 | #define ELFMAG0 0x7f 22 | #define ELFMAG1 'E' 23 | #define ELFMAG2 'L' 24 | #define ELFMAG3 'F' 25 | 26 | #define ELFCLASS32 1 27 | #define ELFDATA2LSB 1 28 | 29 | #define ELFOSABI_NONE 0 // UNIX System V ABI 30 | #define ELFOSABI_SYSV ELFOSABI_NONE // Alias. 31 | #define ELFOSABI_GNU 3 // Object uses GNU ELF extensions. 32 | #define ELFOSABI_LINUX ELFOSABI_GNU // Compatibility alias. 33 | 34 | #define IS_ELF(ehdr) \ 35 | ((ehdr).e_ident[EI_MAG0] == ELFMAG0 && \ 36 | (ehdr).e_ident[EI_MAG1] == ELFMAG1 && \ 37 | (ehdr).e_ident[EI_MAG2] == ELFMAG2 && (ehdr).e_ident[EI_MAG3] == ELFMAG3) 38 | 39 | #define ET_EXEC 2 40 | #define ET_DYN 3 41 | 42 | #define EM_386 3 43 | #define EV_CURRENT 1 44 | 45 | typedef struct elfhdr { 46 | unsigned char e_ident[EI_NIDENT]; 47 | Elf32_Half e_type; 48 | Elf32_Half e_machine; 49 | Elf32_Word e_version; 50 | Elf32_Addr e_entry; 51 | Elf32_Off e_phoff; 52 | Elf32_Off e_shoff; 53 | Elf32_Word e_flags; 54 | Elf32_Half e_ehsize; 55 | Elf32_Half e_phentsize; 56 | Elf32_Half e_phnum; 57 | Elf32_Half e_shentsize; 58 | Elf32_Half e_shnum; 59 | Elf32_Half e_shstrndx; 60 | } Elf32_Ehdr; 61 | 62 | typedef struct { 63 | Elf32_Word p_type; 64 | Elf32_Off p_offset; 65 | Elf32_Addr p_vaddr; 66 | Elf32_Addr p_paddr; 67 | Elf32_Word p_filesz; 68 | Elf32_Word p_memsz; 69 | Elf32_Word p_flags; 70 | Elf32_Word p_align; 71 | } Elf32_Phdr; 72 | 73 | #define PT_NULL 0 // Program header table entry unused 74 | #define PT_LOAD 1 // Loadable program segment 75 | #define PT_TLS 7 // Thread-local storage segment 76 | 77 | #define PF_X 0x1 78 | #define PF_W 0x2 79 | #define PF_R 0x4 80 | 81 | typedef struct { 82 | uint32_t a_type; 83 | union { 84 | uint32_t a_val; 85 | } a_un; 86 | } Elf32_auxv_t; 87 | 88 | #define AT_NULL 0 // End of vector 89 | #define AT_IGNORE 1 // Entry should be ignored 90 | #define AT_PHDR 3 // Program headers for program 91 | #define AT_PHENT 4 // Size of program header entry 92 | #define AT_PHNUM 5 // Number of program headers 93 | #define AT_PAGESZ 6 // System page size 94 | #define AT_ENTRY 9 // Entry point of program 95 | #define AT_UID 11 // Real uid 96 | #define AT_EUID 12 // Effective uid 97 | #define AT_GID 13 // Real gid 98 | #define AT_EGID 14 // Effective gid 99 | #define AT_HWCAP 16 // Machine-dependent hints about processor capabilities. 100 | #define AT_CLKTCK 17 // Frequency of times() 101 | #define AT_SECURE 23 // Boolean, was exec setuid-like? 102 | #define AT_RANDOM 25 // Address of 16 random bytes. 103 | #define AT_EXECFN 31 // Filename of executable. 104 | -------------------------------------------------------------------------------- /common/extra.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ctype.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define _STRINGIFY(x) #x 10 | #define STRINGIFY(x) _STRINGIFY(x) 11 | 12 | #define MIN(x, y) ((x) < (y) ? (x) : (y)) 13 | #define MAX(x, y) ((x) > (y) ? (x) : (y)) 14 | 15 | #define SIZEOF_FIELD(t, f) sizeof(((t*)0)->f) 16 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 17 | 18 | #define CONTAINER_OF(ptr, type, member) \ 19 | ({ \ 20 | const __typeof__(((type*)0)->member)* __mptr = (ptr); \ 21 | (type*)((char*)__mptr - offsetof(type, member)); \ 22 | }) 23 | 24 | #define ROUND_UP(x, align) (((x) + ((align) - 1)) & ~((align) - 1)) 25 | #define ROUND_DOWN(x, align) ((x) & ~((align) - 1)) 26 | #define DIV_CEIL(lhs, rhs) (((lhs) + (rhs) - 1) / (rhs)) 27 | 28 | #define NODISCARD __attribute__((__warn_unused_result__)) 29 | #define NOINLINE __attribute__((noinline)) 30 | #define STATIC_ASSERT(x) _Static_assert(x, "Static assertion failed") 31 | 32 | #define PRINTF_LIKE(a, b) __attribute__((format(printf, a, b))) 33 | 34 | static inline size_t next_power_of_two(size_t x) { 35 | if (x <= 1) 36 | return 1; 37 | return (SIZE_MAX >> __builtin_clz(x - 1)) + 1; 38 | } 39 | 40 | // NOLINTNEXTLINE(readability-non-const-parameter) 41 | static inline void memset16(uint16_t* dest, uint16_t c, size_t n) { 42 | __asm__ volatile("rep stosw" 43 | : "=D"(dest), "=c"(n) 44 | : "D"(dest), "c"(n), "a"(c) 45 | : "memory"); 46 | } 47 | 48 | // NOLINTNEXTLINE(readability-non-const-parameter) 49 | static inline void memset32(uint32_t* dest, uint32_t c, size_t n) { 50 | __asm__ volatile("rep stosl" 51 | : "=D"(dest), "=c"(n) 52 | : "D"(dest), "c"(n), "a"(c) 53 | : "memory"); 54 | } 55 | 56 | // NOLINTNEXTLINE(readability-non-const-parameter) 57 | static inline void memcpy32(uint32_t* dest, const uint32_t* src, size_t n) { 58 | __asm__ volatile("rep movsl" : "+S"(src), "+D"(dest), "+c"(n)::"memory"); 59 | } 60 | 61 | static inline int32_t divmodi64(int64_t a, int32_t b, int32_t* rem) { 62 | int32_t q; 63 | int32_t r; 64 | __asm__("idivl %[b]" 65 | : "=a"(q), "=d"(r) 66 | : "d"((int32_t)(a >> 32)), 67 | "a"((int32_t)(a & 0xffffffff)), [b] "rm"(b)); 68 | if (rem) 69 | *rem = r; 70 | return q; 71 | } 72 | 73 | static inline uint32_t divmodu64(uint64_t a, uint32_t b, uint32_t* rem) { 74 | uint32_t q; 75 | uint32_t r; 76 | __asm__("divl %[b]" 77 | : "=a"(q), "=d"(r) 78 | : "d"((uint32_t)(a >> 32)), 79 | "a"((uint32_t)(a & 0xffffffff)), [b] "rm"(b)); 80 | if (rem) 81 | *rem = r; 82 | return q; 83 | } 84 | 85 | static inline bool str_is_uint(const char* s) { 86 | while (*s) { 87 | if (!isdigit(*s)) 88 | return false; 89 | ++s; 90 | } 91 | return true; 92 | } 93 | 94 | static inline void full_memory_barrier(void) { 95 | atomic_signal_fence(memory_order_acq_rel); 96 | atomic_thread_fence(memory_order_acq_rel); 97 | } 98 | --------------------------------------------------------------------------------