From c1bd9802645156b482861e89d8ba813ea030d8a6 Mon Sep 17 00:00:00 2001 From: Mark Date: Fri, 17 Jan 2020 12:53:56 +0200 Subject: [PATCH] select() syscall for character device reads --- include/sys/amd64/sys_file.h | 8 ++++ include/sys/chr.h | 2 + include/sys/select.h | 30 +++++++++++++++ include/sys/syscall.h | 1 + install-headers.sh | 3 +- sys/amd64/sys_file.c | 74 ++++++++++++++++++++++++++++++++++++ sys/amd64/syscall.c | 1 + sys/tty.c | 12 +++--- 8 files changed, 125 insertions(+), 6 deletions(-) create mode 100644 include/sys/select.h diff --git a/include/sys/amd64/sys_file.h b/include/sys/amd64/sys_file.h index 639d5d1..9a405b1 100644 --- a/include/sys/amd64/sys_file.h +++ b/include/sys/amd64/sys_file.h @@ -2,6 +2,9 @@ #include "sys/types.h" #include "sys/fs/dirent.h" #include "sys/stat.h" +#include "sys/select.h" + +struct timeval; ssize_t sys_read(int fd, void *buf, size_t lim); ssize_t sys_write(int fd, const void *buf, size_t lim); @@ -20,6 +23,11 @@ int sys_rmdir(const char *pathname); int sys_stat(const char *filename, struct stat *st); int sys_access(const char *path, int mode); +// XXX: Except timeout is const, as I don't want to modify it +// No one cares about how much time is remaining once +// select() returns, right? +int sys_select(int nfds, fd_set *rd, fd_set *wr, fd_set *exc, struct timeval *timeout); + // TODO: const struct termios *termp, const struct winsize *winp int sys_openpty(int *master, int *slave); diff --git a/include/sys/chr.h b/include/sys/chr.h index 077b87f..32ad61b 100644 --- a/include/sys/chr.h +++ b/include/sys/chr.h @@ -1,8 +1,10 @@ #pragma once #include "sys/types.h" +#include "sys/ring.h" struct chrdev { void *dev_data; + struct ring buffer; ssize_t (*write) (struct chrdev *chr, const void *buf, size_t pos, size_t lim); ssize_t (*read) (struct chrdev *chr, void *buf, size_t pos, size_t lim); diff --git a/include/sys/select.h b/include/sys/select.h new file mode 100644 index 0000000..3e2d8ff --- /dev/null +++ b/include/sys/select.h @@ -0,0 +1,30 @@ +#pragma once + +struct timeval; + +// 64 bits +typedef long int __fd_mask; + +#define __NFDBITS (8 * (int) sizeof(__fd_mask)) +// TODO: Guess this should be the same as the maximum +// per-process filedes count +#define __FD_SETSIZE 64 + +typedef struct { + __fd_mask fds_bits[__FD_SETSIZE / __NFDBITS]; +} fd_set; + +#define FD_SET(fd, fds) \ + (fds)->fds_bits[(fd) / __NFDBITS] |= (1ULL << ((fd) % __NFDBITS)) + +#define FD_ISSET(fd, fds) \ + (!!((fds)->fds_bits[(fd) / __NFDBITS] & (1ULL << ((fd) % __NFDBITS)))) + +#define FD_ZERO(fds) \ + for (int __i = 0; __i < (__FD_SETSIZE / __NFDBITS); ++__i) \ + (fds)->fds_bits[__i] = 0 + +#if !defined(__KERNEL__) +// Implemented by libc +int select(int nfds, fd_set *rd, fd_set *wr, fd_set *exc, struct timeval *tv); +#endif diff --git a/include/sys/syscall.h b/include/sys/syscall.h index e1f6877..3bfbeb2 100644 --- a/include/sys/syscall.h +++ b/include/sys/syscall.h @@ -7,6 +7,7 @@ #define SYSCALL_NR_STAT 4 #define SYSCALL_NR_LSEEK 8 #define SYSCALL_NR_ACCESS 21 +#define SYSCALL_NR_SELECT 23 #define SYSCALL_NR_GETCWD 79 #define SYSCALL_NR_CHDIR 80 #define SYSCALL_NR_MKDIR 83 diff --git a/install-headers.sh b/install-headers.sh index 6d0524e..8a8f12f 100755 --- a/install-headers.sh +++ b/install-headers.sh @@ -14,7 +14,8 @@ KERNEL_HEADERS="include/sys/fcntl.h \ include/sys/time.h \ include/sys/errno.h \ include/sys/signum.h \ - include/sys/utsname.h" + include/sys/utsname.h \ + include/sys/select.h" for src_file in $KERNEL_HEADERS; do dst_file=$INSTALL_HDR$(echo $src_file | sed -e 's/^include//g') diff --git a/sys/amd64/sys_file.c b/sys/amd64/sys_file.c index 4408541..df25b79 100644 --- a/sys/amd64/sys_file.c +++ b/sys/amd64/sys_file.c @@ -216,3 +216,77 @@ off_t sys_lseek(int fd, off_t offset, int whence) { return vfs_lseek(&thr->ioctx, ofile, offset, whence); } + +int sys_select(int nfds, fd_set *rd, fd_set *wr, fd_set *exc, struct timeval *tv) { + struct thread *thr = get_cpu()->thread; + _assert(thr); + + // Not implemented yet + _assert(!wr); + _assert(!exc); + + // TODO: write fd_set + fd_set _rd; + memcpy(&_rd, rd, sizeof(fd_set)); + FD_ZERO(rd); + + if (!rd) { + return 0; + } + + // Check file descriptors + for (int i = 0; i < nfds; ++i) { + if (FD_ISSET(i, &_rd)) { + if (i > THREAD_MAX_FDS) { + kwarn("Bad FD: %d\n", i); + return -EBADF; + } + struct ofile *fd = thr->fds[i]; + if (!fd) { + kwarn("Bad FD: %d\n", i); + return -EBADF; + } + + _assert(fd->vnode); + if (fd->vnode->type != VN_CHR) { + // select() does not make sense for non-char devices yet + FD_SET(i, rd); + return 0; + } + } + } + + uint64_t deadline = tv->tv_sec * 1000000000ULL + tv->tv_usec * 1000ULL + system_time; + int res; + + while (1) { + // Check for any ready FD + for (int i = 0; i < nfds; ++i) { + if (FD_ISSET(i, &_rd)) { + struct ofile *fd = thr->fds[i]; + _assert(fd && fd->vnode && fd->vnode->type == VN_CHR); + + struct chrdev *chr = fd->vnode->dev; + _assert(chr); + + if (ring_avail(&chr->buffer) > 0) { + FD_SET(i, rd); + res = 0; + goto done; + } + } + } + + // Check timer deadline + if (system_time >= deadline) { + res = 0; + goto done; + } + + // Yield + asm volatile ("sti; hlt; cli"); + } + +done: + return res; +} diff --git a/sys/amd64/syscall.c b/sys/amd64/syscall.c index 0503314..0d911a8 100644 --- a/sys/amd64/syscall.c +++ b/sys/amd64/syscall.c @@ -18,6 +18,7 @@ const void *amd64_syscall_jmp_table[256] = { [SYSCALL_NR_OPEN] = sys_open, [SYSCALL_NR_CLOSE] = sys_close, [SYSCALL_NR_LSEEK] = sys_lseek, + [SYSCALL_NR_SELECT] = sys_select, [SYSCALL_NR_GETCWD] = sys_getcwd, [SYSCALL_NR_CHDIR] = sys_chdir, [SYSCALL_NR_STAT] = sys_stat, diff --git a/sys/tty.c b/sys/tty.c index 69e22a5..44bf0b7 100644 --- a/sys/tty.c +++ b/sys/tty.c @@ -23,15 +23,17 @@ static struct chrdev _dev_tty0 = { .read = tty_read }; -static struct ring tty_ring = {0}; - // This function receives keystrokes from keyboard drivers void tty_buffer_write(int tty_no, char c) { - ring_putc(NULL, &tty_ring, c); + switch (tty_no) { + case 0: + ring_putc(NULL, &_dev_tty0.buffer, c); + break; + } } void tty_init(void) { - ring_init(&tty_ring, 16); + ring_init(&_dev_tty0.buffer, 16); dev_add(DEV_CLASS_CHAR, DEV_CHAR_TTY, &_dev_tty0, "tty0"); } @@ -67,7 +69,7 @@ static ssize_t tty_read(struct chrdev *tty, void *buf, size_t pos, size_t lim) { size_t p = 0; while (rem) { - ssize_t rd = ring_read(get_cpu()->thread, &tty_ring, ibuf, MIN(16, rem)); + ssize_t rd = ring_read(get_cpu()->thread, &tty->buffer, ibuf, MIN(16, rem)); if (rd < 0) { break; }