Initial commit after splitting userspace from kernel tree

This commit is contained in:
Mark 2020-01-07 16:22:33 +02:00
commit 638edb5166
36 changed files with 1856 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
kernel-hdr
build

96
Makefile Normal file
View File

@ -0,0 +1,96 @@
O?=build
STAGE=$(O)/stage
KERNEL_HDRS?=kernel-hdr
CC=$(CROSS_COMPILE)gcc
DIRS=$(O) \
$(O)/libc \
$(STAGE)
HDRS=$(shell find $(S) -type f -name "*.h")
STAGE_BIN=$(STAGE)/init \
$(STAGE)/bin/hexd \
$(STAGE)/bin/ls \
$(STAGE)/bin/reboot
CFLAGS=-ffreestanding \
-nostdlib \
-mno-sse \
-mno-sse2 \
-Ilibc/include \
-ggdb \
-O0 \
-I$(KERNEL_HDRS)
usr_LDFLAGS=-nostdlib \
-lgcc \
-Tlibc/program.ld
usr_STATIC_LIBS=$(O)/libc.a
libc_CRTI=$(O)/libc/crti.o
libc_CRTN=$(O)/libc/crtn.o
libc_OBJS=$(O)/libc/crt0.o \
$(O)/libc/syscall.o \
$(O)/libc/vsnprintf.o \
$(O)/libc/printf.o \
$(O)/libc/string.o \
$(O)/libc/errno.o \
$(O)/libc/stdio.o \
$(O)/libc/init.o \
$(O)/libc/malloc.o \
$(O)/libc/dirent.o \
$(O)/libc/signal.o \
$(O)/libc/global.o \
$(O)/libc/time.o
sys_CRTBEGIN=$(shell $(CC) $(CFLAGS) -print-file-name=crtbegin.o)
sys_CRTEND=$(shell $(CC) $(CFLAGS) -print-file-name=crtend.o)
all: mkdirs $(O)/initrd.img
clean:
rm -rf $(O)
$(O)/initrd.img: mkstage-etc $(STAGE_BIN)
cd $(STAGE) && tar czf $(abspath $@) *
mkdirs:
mkdir -p $(DIRS)
mkstage-etc:
mkdir -p $(STAGE)/dev $(STAGE)/mnt $(STAGE)/bin
cp -r etc $(STAGE)
# Application building
$(STAGE)/init: init.c $(libc_CRTI) $(libc_CRTN) $(O)/libc.a
@printf " CC\t%s\n" $(@:$(STAGE)/%=/%)
@$(CC) -o $@ $(CFLAGS) $(usr_LDFLAGS) \
$(libc_CRTI) \
$(sys_CRTBEGIN) \
init.c \
$(sys_CRTEND) \
$(libc_CRTN) \
$(usr_STATIC_LIBS)
$(STAGE)/bin/%: core/bin/%.c $(libc_CRTI) $(libc_CRTN) $(O)/libc.a
@printf " CC\t%s\n" $(@:$(STAGE)/%=/%)
@$(CC) -o $@ $(CFLAGS) $(usr_LDFLAGS) \
$(libc_CRTI) \
$(sys_CRTBEGIN) \
$< \
$(sys_CRTEND) \
$(libc_CRTN) \
$(usr_STATIC_LIBS)
# libc building
$(O)/libc.a: $(libc_OBJS)
@printf " AR\t%s\n" $(@:$(O)/%=%)
@$(AR) rcs $@ $(libc_OBJS)
$(O)/libc/%.o: libc/%.c $(HDRS)
@printf " CC\t%s\n" $(@:$(O)/%=%)
@$(CC) -c -o $@ $(CFLAGS) $<
$(O)/libc/%.o: libc/%.S
@printf " AS\t%s\n" $(@:$(O)/%=%)
@$(CC) -c -o $@ $(CFLAGS) $<

3
README.md Normal file
View File

@ -0,0 +1,3 @@
Make a symlink to kernel headers (<kernel git tree>/include) directory named kernel-hdr or
specify direct path using `KERNEL_HDRS` env variable, specify proper ``CROSS_COMPILE` and run
make

95
core/bin/hexd.c Normal file
View File

@ -0,0 +1,95 @@
#include <sys/fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#define LINE_LENGTH 16
static void line_print(size_t off, const char *line, size_t len) {
printf("%08x: ", off);
for (size_t i = 0; i < LINE_LENGTH; ++i) {
// XXX: This is needed because I didn't implement h/hh modifiers in printf
uint64_t byte = (uint64_t) line[i] & 0xFF;
if (i < len) {
printf("%02x", byte);
} else {
printf(" ");
}
if (i % 2) {
printf(" ");
}
}
printf("| ");
for (size_t i = 0; i < len; ++i) {
// TODO: isprint?
if (line[i] >= ' ') {
printf("%c", line[i]);
} else {
printf(".");
}
}
printf("\n");
}
int main(int argc, char **argv) {
int fd;
const char *path;
char buf[512];
char line[LINE_LENGTH];
ssize_t bread;
size_t offset;
size_t linel;
size_t n_full_zero;
if (argc != 2) {
printf("wrong\n");
return -1;
}
path = argv[1];
printf("open file %s\n", path);
if ((fd = open(path, O_RDONLY, 0)) < 0) {
perror(path);
return -1;
}
offset = 0;
linel = 0;
n_full_zero = 0;
while ((bread = read(fd, buf, sizeof(buf))) > 0) {
for (size_t i = 0; i < bread; ++i) {
line[linel++] = buf[i];
if (linel == LINE_LENGTH) {
// Check if the line is all zeros
int all_zeros = 1;
for (size_t j = 0; j < LINE_LENGTH; ++j) {
if (line[j]) {
all_zeros = 0;
break;
}
}
if (all_zeros) {
++n_full_zero;
} else {
n_full_zero = 0;
}
if (n_full_zero < 3) {
line_print(offset, line, linel);
} else if (n_full_zero == 3) {
printf(" ... \n");
}
offset += LINE_LENGTH;
linel = 0;
}
}
}
if (linel) {
line_print(offset, line, linel);
}
close(fd);
return 0;
}

136
core/bin/ls.c Normal file
View File

@ -0,0 +1,136 @@
#include <sys/stat.h>
#include <dirent.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#define LS_COLOR (1 << 0)
#define LS_DETAIL (1 << 1)
#define COLOR_DIR "\033[36m"
#define COLOR_DEV "\033[33m"
#define COLOR_UNKNOWN "\033[43m"
#define COLOR_RESET "\033[0m"
static int ls_dir(const char *path, int flags) {
int res;
DIR *dir;
struct dirent *ent;
struct stat ent_stat;
char ent_path[512];
char t;
if (!(dir = opendir(path))) {
perror(path);
return -1;
}
while ((ent = readdir(dir))) {
if (ent->d_name[0] != '.') {
if (flags & LS_DETAIL) {
snprintf(ent_path, sizeof(ent_path), "%s/%s",
strcmp(path, "") ? path : ".", ent->d_name);
if (stat(ent_path, &ent_stat) != 0) {
printf("?????????? ? ? ? ");
} else {
switch (ent_stat.st_mode & S_IFMT) {
case S_IFDIR:
t = 'd';
break;
case S_IFREG:
t = '-';
break;
case S_IFBLK:
t = 'b';
break;
case S_IFCHR:
t = 'c';
break;
default:
t = '?';
break;
}
printf("%c%c%c%c%c%c%c%c%c%c ",
t,
(ent_stat.st_mode & S_IRUSR) ? 'r' : '-',
(ent_stat.st_mode & S_IWUSR) ? 'w' : '-',
(ent_stat.st_mode & S_IXUSR) ? 'x' : '-',
(ent_stat.st_mode & S_IRGRP) ? 'r' : '-',
(ent_stat.st_mode & S_IWGRP) ? 'w' : '-',
(ent_stat.st_mode & S_IXGRP) ? 'x' : '-',
(ent_stat.st_mode & S_IROTH) ? 'r' : '-',
(ent_stat.st_mode & S_IWOTH) ? 'w' : '-',
(ent_stat.st_mode & S_IXOTH) ? 'x' : '-');
printf("%4u %4u %8u ",
ent_stat.st_gid,
ent_stat.st_uid,
ent_stat.st_blocks);
}
}
if (flags & LS_COLOR) {
switch (ent->d_type) {
case DT_REG:
break;
case DT_BLK:
case DT_CHR:
write(STDOUT_FILENO, COLOR_DEV, 5);
break;
case DT_DIR:
write(STDOUT_FILENO, COLOR_DIR, 5);
break;
default:
write(STDOUT_FILENO, COLOR_UNKNOWN, 5);
break;
}
}
write(STDOUT_FILENO, ent->d_name, strlen(ent->d_name));
if (flags & LS_COLOR) {
write(STDOUT_FILENO, COLOR_RESET, 4);
}
putchar('\n');
}
}
closedir(dir);
return 0;
}
int main(int argc, char **argv) {
// Basic getopt-like features
int first = -1;
int flags = 0;
for (int i = 1; i < argc; ++i) {
if (argv[i][0] != '-') {
first = i;
break;
} else {
if (!strcmp(argv[i], "-c")) {
flags |= LS_COLOR;
} else if (!strcmp(argv[i], "-d")) {
flags |= LS_DETAIL;
} else {
printf("Unknown option: %s\n", argv[i]);
}
}
}
if (first == -1) {
ls_dir("", flags);
} else {
for (int i = first; i < argc; ++i) {
if (first != argc - 1) {
printf("%s:\n", argv[i]);
}
ls_dir(argv[i], flags);
}
}
return 0;
}

33
core/bin/reboot.c Normal file
View File

@ -0,0 +1,33 @@
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <time.h>
int main(int argc, char **argv) {
const char *arg = NULL;
if (argc == 2) {
arg = argv[1];
}
int res;
unsigned int cmd = YGG_REBOOT_RESTART;
if (arg) {
if (!strcmp(arg, "-s")) {
cmd = YGG_REBOOT_POWER_OFF;
} else if (!strcmp(arg, "-h")) {
cmd = YGG_REBOOT_HALT;
}
}
if ((res = reboot(YGG_REBOOT_MAGIC1, YGG_REBOOT_MAGIC2, cmd, NULL)) < 0) {
perror("reboot()");
return res;
}
while (1) {
usleep(1000000);
}
}

1
etc/file.txt Normal file
View File

@ -0,0 +1 @@
This file is inside the initrd.

410
init.c Normal file
View File

@ -0,0 +1,410 @@
#include <sys/fcntl.h>
#include <sys/reboot.h>
#include <signal.h>
#include <dirent.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
// Cursor helpers
#define curs_save() \
write(STDOUT_FILENO, "\033[s", 3)
#define curs_unsave() \
write(STDOUT_FILENO, "\033[u", 3)
#define curs_set(row, col) \
printf("\033[%d;%df", row, col)
#define clear() \
write(STDOUT_FILENO, "\033[2J", 4)
#define BOX_ANGLE_UL "\332"
#define BOX_ANGLE_UR "\277"
#define BOX_ANGLE_LL "\300"
#define BOX_ANGLE_LR "\331"
#define BOX_HOR "\304"
#define BOX_VERT "\263"
//
extern __attribute__((noreturn)) void abort(void);
#define assert(x) \
if (!(x)) abort()
struct builtin {
const char *name;
const char *desc;
int (*run) (const char *arg);
};
static int b_cd(const char *path);
static int b_pwd(const char *_);
static int b_cat(const char *path);
static int b_curs(const char *arg);
static int b_sleep(const char *arg);
static int b_help(const char *arg);
static int b_clear(const char *arg);
static struct builtin builtins[] = {
{
"cd",
"Change working directory",
b_cd,
},
{
"pwd",
"Print working directory",
b_pwd,
},
{
"cat",
"Print file contents" /* Concatenate files */,
b_cat
},
{
"sleep",
"Sleep N seconds",
b_sleep
},
{
"clear",
"Clear terminal",
b_clear,
},
{
"help",
"Please help me",
b_help
},
{ NULL, NULL, NULL }
};
static int b_cd(const char *arg) {
int res;
if (!arg) {
arg = "";
}
if ((res = chdir(arg)) < 0) {
perror(arg);
}
return res;
}
static int b_pwd(const char *arg) {
char buf[512];
if (!getcwd(buf, sizeof(buf))) {
perror("getcwd()");
return -1;
} else {
puts(buf);
}
return 0;
}
static int b_cat(const char *arg) {
char buf[512];
int fd;
ssize_t bread;
if (!arg) {
return -1;
}
if ((fd = open(arg, O_RDONLY, 0)) < 0) {
perror(arg);
return fd;
}
while ((bread = read(fd, buf, sizeof(buf))) > 0) {
write(STDOUT_FILENO, buf, bread);
}
close(fd);
return 0;
}
static int b_curs(const char *arg) {
char c;
size_t w = 60;
size_t h = 20;
size_t off_y = (25 - h) / 2 + 1;
size_t off_x = (80 - w) / 2;
const char *lines[18] = {
NULL,
"Demo something",
NULL,
"Slow as hell"
};
clear();
while (1) {
printf("\033[47;30m");
curs_set(off_y, off_x);
printf(BOX_ANGLE_UL);
for (size_t i = 0; i < w; ++i) {
printf(BOX_HOR);
}
printf(BOX_ANGLE_UR);
for (size_t i = 0; i < h - 2; ++i) {
curs_set(off_y + 1 + i, off_x);
printf(BOX_VERT);
for (size_t j = 0; j < w; ++j) {
printf(" ");
}
if (lines[i]) {
curs_set(off_y + 1 + i, off_x + (w - strlen(lines[i])) / 2);
printf("%s", lines[i]);
}
curs_set(off_y + 1 + i, off_x + w + 1);
printf(BOX_VERT);
}
curs_set(off_y + h - 3, off_x + (w - 8) / 2);
printf("\033[0m\033[7m[ OK ]\033[47;30m");
curs_set(off_y + h - 1, off_x);
printf(BOX_ANGLE_LL);
for (size_t i = 0; i < w; ++i) {
printf(BOX_HOR);
}
printf(BOX_ANGLE_LR);
printf("\033[0m");
curs_set(1, 1);
if (read(STDIN_FILENO, &c, 1) < 0) {
break;
}
if (c == 'q' || c == '\n') {
break;
}
}
curs_set(1, 1);
return 0;
}
static int b_sleep(const char *arg) {
if (!arg) {
return -1;
}
int seconds = 0;
while (*arg) {
seconds *= 10;
seconds += *arg - '0';
++arg;
}
struct timespec ts = { seconds, 0 };
if ((seconds = nanosleep(&ts, NULL))) {
perror("nanosleep()");
return seconds;
}
return 0;
}
static int b_clear(const char *arg) {
clear();
curs_set(1, 1);
return 0;
}
static int b_help(const char *arg) {
if (arg) {
// Describe a specific command
for (size_t i = 0; builtins[i].run; ++i) {
if (!strcmp(arg, builtins[i].name)) {
printf("%s: %s\n", builtins[i].name, builtins[i].desc);
return 0;
}
}
printf("%s: Unknown command\n", arg);
return -1;
} else {
for (size_t i = 0; builtins[i].run; ++i) {
printf("%s: %s\n", builtins[i].name, builtins[i].desc);
}
return 0;
}
}
static void prompt(void) {
char cwd[512];
if (!getcwd(cwd, sizeof(cwd))) {
cwd[0] = '?';
cwd[1] = 0;
}
printf("\033[36mygg\033[0m %s > ", cwd);
}
static int cmd_subproc_exec(const char *abs_path, const char *cmd, const char *e) {
// Maximum of 8 arguments 64 chars each (63)
// Split input argument into pieces by space
char args[64 * 8] = {0};
const char *p = e;
const char *t = NULL;
size_t argc = 0;
while (p) {
t = strchr(p, ' ');
if (!t) {
// Last argument
assert(strlen(p) < 64);
strcpy(&args[argc++ * 64], p);
break;
} else {
assert(t - p < 64);
strncpy(&args[argc * 64], p, t - p);
args[(argc++ * 64) + (t - p)] = 0;
p = t + 1;
while (*p == ' ') {
++p;
}
if (!*p) {
break;
}
}
}
// Fill arg pointer array
const char *argp[10] = { cmd };
for (size_t i = 0; i < argc; ++i) {
argp[i + 1] = &args[i * 64];
}
argp[argc + 1] = NULL;
int pid = fork();
int res;
int status;
switch (pid) {
case -1:
perror("fork()");
return -1;
case 0:
if (execve(abs_path, argp, NULL) != 0) {
perror("execve()");
}
exit(-1);
default:
if (waitpid(pid, &status) != 0) {
perror("waitpid()");
}
return 0;
}
}
static int cmd_exec(const char *line) {
char cmd[64];
const char *e = strchr(line, ' ');
if (!e) {
assert(strlen(line) < 64);
strcpy(cmd, line);
} else {
assert(e - line < 64);
strncpy(cmd, line, e - line);
cmd[e - line] = 0;
++e;
while (*e == ' ') {
++e;
}
}
if (e && !*e) {
e = NULL;
}
for (size_t i = 0; builtins[i].run; ++i) {
if (!strcmp(cmd, builtins[i].name)) {
return builtins[i].run(e);
}
}
// If command starts with ./ or /, try to execute it
if (((cmd[0] == '.' && cmd[1] == '/') || cmd[0] == '/') && access(cmd, X_OK) == 0) {
return cmd_subproc_exec(cmd, cmd, e);
}
// Try to execute binary from /bin
char path_buf[512];
snprintf(path_buf, sizeof(path_buf), "/bin/%s", cmd);
if (access(path_buf, X_OK) == 0) {
return cmd_subproc_exec(path_buf, cmd, e);
}
printf("%s: Unknown command\n", cmd);
return -1;
}
int main(int argc, char **argv) {
if (getpid() != 1) {
printf("Won't work if PID is not 1\n");
return -1;
}
char linebuf[512];
char c;
size_t l = 0;
int res;
prompt();
#if 0
if ((res = fork()) < 0) {
perror("fork()");
return -1;
} else if (res == 0) {
if (execve("/time", NULL, NULL) != 0) {
perror("execve()");
}
exit(-1);
}
#endif
while (1) {
if (read(STDIN_FILENO, &c, 1) < 0) {
break;
}
if (c == '\b') {
if (l) {
linebuf[--l] = 0;
printf("\033[D \033[D");
}
continue;
}
if (c == '\n') {
write(STDOUT_FILENO, &c, 1);
linebuf[l] = 0;
if (!strcmp(linebuf, "exit")) {
break;
}
l = 0;
if ((res = cmd_exec(linebuf)) != 0) {
printf("\033[31m= %d\033[0m\n", res);
}
prompt();
continue;
}
linebuf[l++] = c;
write(STDOUT_FILENO, &c, 1);
}
return -1;
}

25
libc/crt0.S Normal file
View File

@ -0,0 +1,25 @@
.section .text
.global _start
.type _start, %function
// Arguments:
// %rdi - argp
_start:
movq $0, %rbp
// For callee linkage
pushq %rbp
pushq %rbp
movq %rsp, %rbp
push %rdi
call __libc_init
call _init
mov __libc_argc, %rdi
pop %rsi
// exit(main(argc, argv))
call main
movq %rax, %rdi
call exit
.size _start, . - _start

16
libc/crti.S Normal file
View File

@ -0,0 +1,16 @@
/* x86_64 crti.s */
.section .init
.global _init
.type _init, @function
_init:
push %rbp
movq %rsp, %rbp
/* gcc will nicely put the contents of crtbegin.o's .init section here. */
.section .fini
.global _fini
.type _fini, @function
_fini:
push %rbp
movq %rsp, %rbp
/* gcc will nicely put the contents of crtbegin.o's .fini section here. */

10
libc/crtn.S Normal file
View File

@ -0,0 +1,10 @@
/* x86_64 crtn.s */
.section .init
/* gcc will nicely put the contents of crtend.o's .init section here. */
popq %rbp
ret
.section .fini
/* gcc will nicely put the contents of crtend.o's .fini section here. */
popq %rbp
ret

57
libc/dirent.c Normal file
View File

@ -0,0 +1,57 @@
#include "dirent.h"
#include "bits/syscall.h"
#include <sys/fcntl.h>
#include <string.h>
#include <errno.h>
extern void *malloc(size_t count);
extern void free(void *p);
struct DIR_private {
int fd;
struct dirent buf;
};
DIR *opendir(const char *path) {
int fd;
DIR *res;
if (!(res = malloc(sizeof(struct DIR_private)))) {
errno = ENOMEM;
return NULL;
}
if ((fd = open(path, O_DIRECTORY | O_RDONLY, 0)) < 0) {
fd = errno;
free(res);
errno = fd;
// errno is set
return NULL;
}
res->fd = fd;
memset(&res->buf, 0, sizeof(res->buf));
return res;
}
void closedir(DIR *dirp) {
if (!dirp) {
return;
}
close(dirp->fd);
free(dirp);
}
struct dirent *readdir(DIR *dirp) {
ssize_t res;
if (!dirp) {
errno = EBADF;
return NULL;
}
if ((res = sys_readdir(dirp->fd, &dirp->buf)) <= 0) {
return NULL;
}
return &dirp->buf;
}

57
libc/errno.c Normal file
View File

@ -0,0 +1,57 @@
#include <errno.h>
#include <stdio.h>
static const char *err_strings0[35] = {
"Success",
"Operation not permitted",
"No such file or directory",
"No such process",
"Interrupted system call",
"I/O error",
"No such device or address",
"Argument list too long",
"Exec format error",
"Bad file number",
"No child processes",
"Try again",
"Out of memory",
"Permission denied",
"Bad address",
"Block device required",
"Device or resource busy",
"File exists",
"Cross-device link",
"No such device",
"Not a directory",
"Is a directory",
"Invalid argument",
"File table overflow",
"Too many open files",
"Not a typewriter",
"Text file busy",
"File too large",
"No space left on device",
"Illegal seek",
"Read-only file system",
"Too many links",
"Broken pipe",
"Math argument out of domain of func",
"Math result not representable"
};
int errno;
char *strerror(int n) {
if (n >= 0 && n <= 34) {
return (char *) err_strings0[n];
}
return (char *) "Unknown error";
}
void perror(const char *e) {
if (e && *e) {
printf("%s: %s\n", e, strerror(errno));
} else {
puts(strerror(errno));
}
}

4
libc/global.c Normal file
View File

@ -0,0 +1,4 @@
#include "bits/global.h"
void *__cur_brk;
uint64_t __libc_argc = 0;

View File

@ -0,0 +1,5 @@
#pragma once
#include <stdint.h>
extern void *__cur_brk;
extern uint64_t __libc_argc;

View File

@ -0,0 +1,7 @@
#pragma once
#include <sys/types.h>
#include <stdarg.h>
int printf(const char *fmt, ...);
int snprintf(char *buf, size_t size, const char *fmt, ...);
int vsnprintf(char *buf, size_t size, const char *fmt, va_list args);

View File

@ -0,0 +1,30 @@
#pragma once
#include <sys/fs/dirent.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
int open(const char *filename, int flags, int mode);
void close(int fd);
int stat(const char *filename, struct stat *st);
void *mmap(void *addr, size_t len, int prot, int flags, int fd, uintptr_t offset);
int fork(void);
int execve(const char *filename, const char *const argv[], const char *const envp[]);
ssize_t sys_readdir(int fd, struct dirent *entp);
int kill(int pid, int signum);
void __kernel_signal(uintptr_t handler);
__attribute__((noreturn)) void __kernel_sigret(void);
int getpid(void);
int chdir(const char *filename);
char *getcwd(char *buf, size_t size);
int nanosleep(const struct timespec *req, struct timespec *rem);
int openpty(int *amaster, int *aslave);
int gettimeofday(struct timeval *tv, struct timezone *tz);
int reboot(int magic1, int magic2, unsigned int cmd, void *arg);
int access(const char *filename, int mode);
int waitpid(int pid, int *status);
__attribute__((noreturn)) void exit(int code);

8
libc/include/dirent.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#include <sys/fs/dirent.h>
typedef struct DIR_private DIR;
DIR *opendir(const char *path);
void closedir(DIR *dirp);
struct dirent *readdir(DIR *dirp);

7
libc/include/errno.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include <sys/errno.h>
extern int errno;
char *strerror(int errnum);
void perror(const char *s);

8
libc/include/signal.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#include <sys/signum.h>
typedef void (*sighandler_t) (int);
sighandler_t signal(int signum, sighandler_t handler);
int raise(int signum);

6
libc/include/stdio.h Normal file
View File

@ -0,0 +1,6 @@
#pragma once
int puts(const char *s);
int putchar(int c);
#include "bits/printf.h"

3
libc/include/stdlib.h Normal file
View File

@ -0,0 +1,3 @@
#pragma once
void abort(void);

17
libc/include/string.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#include <sys/types.h>
void *memcpy(void *restrict dst, const void *restrict src, size_t cnt);
void *memmove(void *dst, const void *src, size_t cnt);
void *memset(void *dst, int v, size_t count);
int memcmp(const void *a, const void *b, size_t count);
size_t strlen(const char *s);
int strncmp(const char *a, const char *b, size_t lim);
int strcmp(const char *a, const char *b);
char *strchr(const char *a, int c);
char *strrchr(const char *a, int c);
char *strcpy(char *dst, const char *src);
char *strncpy(char *dst, const char *src, size_t lim);
char *strcat(char *dst, const char *src);
char *strncat(char *dst, const char *src, size_t lim);

4
libc/include/time.h Normal file
View File

@ -0,0 +1,4 @@
#pragma once
#include <sys/time.h>
int usleep(long us);

10
libc/include/unistd.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#include "bits/syscall.h"
#include <sys/reboot.h>
#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
void *sbrk(intptr_t diff);
int reboot(int magic1, int magic2, unsigned int cmd, void *arg);

18
libc/init.c Normal file
View File

@ -0,0 +1,18 @@
#include <errno.h>
#include "bits/global.h"
extern char __heap_start;
extern void __libc_signal_init(void);
static const char *argp_dummy[] = { 0 };
void __libc_init(char **argp) {
__libc_signal_init();
__libc_argc = 0;
if (argp) {
while (argp[__libc_argc]) {
++__libc_argc;
}
}
__cur_brk = &__heap_start;
errno = 0;
}

15
libc/malloc.c Normal file
View File

@ -0,0 +1,15 @@
#include <unistd.h>
#include "bits/global.h"
// XXX: I'll implement a proper heap in the following commits
void *malloc(size_t count) {
void *p = __cur_brk;
if (sbrk(count) == p) {
return NULL;
}
return p;
}
void free(void *ptr) {
}

25
libc/printf.c Normal file
View File

@ -0,0 +1,25 @@
#include "bits/printf.h"
#include <unistd.h>
#define PRINTF_BUFFER_SIZE 1024
int printf(const char *fmt, ...) {
char buf[PRINTF_BUFFER_SIZE];
va_list args;
va_start(args, fmt);
int res = vsnprintf(buf, PRINTF_BUFFER_SIZE, fmt, args);
va_end(args);
// TODO: use libc's FILE * instead
return write(STDOUT_FILENO, buf, res);
}
int snprintf(char *buf, size_t lim, const char *fmt, ...) {
va_list args;
int res;
va_start(args, fmt);
res = vsnprintf(buf, PRINTF_BUFFER_SIZE, fmt, args);
va_end(args);
return res;
}

35
libc/program.ld Normal file
View File

@ -0,0 +1,35 @@
/*
* Userspace program linker script,
* partially derived from GCC's -static one
*/
ENTRY(_start)
SECTIONS {
. = 0x400000;
.text : ALIGN(4K) {
KEEP (*(SORT_NONE(.init)))
*(.text)
KEEP (*(SORT_NONE(.fini)))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
}
.rodata : {
*(.rodata)
*(.eh_frame)
}
.data : ALIGN(4K) {
*(.data)
}
.bss : ALIGN(4K) {
*(.bss)
}
__program_end = .;
__heap_start = .;
}

71
libc/signal.c Normal file
View File

@ -0,0 +1,71 @@
#include <bits/syscall.h>
#include <signal.h>
#include <stdio.h>
static sighandler_t signal_handlers[16] = {0};
void SIG_IGN(int signum) {
printf("Ignored: %d\n", signum);
while (1);
}
void SIG_DFL(int signum) {
int pid = getpid();
switch (signum) {
case SIGKILL:
printf("%d: killed\n", pid);
break;
case SIGABRT:
printf("%d: aborted\n", pid);
break;
case SIGSEGV:
printf("%d: segmentation fault\n", pid);
break;
default:
printf("%d: signal %d\n", pid, signum);
break;
}
exit(1);
}
static void __libc_signal_handle(int signum) {
if (signum >= 16) {
SIG_DFL(signum);
} else {
signal_handlers[signum](signum);
}
__kernel_sigret();
}
void __libc_signal_init(void) {
for (size_t i = 0; i < 16; ++i) {
signal_handlers[i] = SIG_DFL;
}
__kernel_signal((uintptr_t) __libc_signal_handle);
}
sighandler_t signal(int signum, sighandler_t new_handler) {
sighandler_t old_handler;
if (signum >= 16) {
printf("Fuck\n");
exit(1);
}
old_handler = signal_handlers[signum];
signal_handlers[signum] = new_handler;
return old_handler;
}
int raise(int signum) {
return kill(getpid(), signum);
}
void abort(void) {
kill(getpid(), SIGABRT);
}

22
libc/stdio.c Normal file
View File

@ -0,0 +1,22 @@
#include <unistd.h>
#include <string.h>
#include <stdio.h>
int puts(const char *s) {
size_t l = strlen(s);
int w = write(STDOUT_FILENO, s, l);
char c = '\n';
if (w == l) {
write(STDOUT_FILENO, &c, 1);
}
return w;
}
int putchar(int c) {
int res;
if ((res = write(STDOUT_FILENO, &c, 1)) < 0) {
return res;
}
return c;
}

167
libc/string.c Normal file
View File

@ -0,0 +1,167 @@
#include <string.h>
char *strcat(char *dst, const char *src) {
size_t l = strlen(dst);
strcpy(dst + l, src);
return dst;
}
char *strncat(char *dst, const char *src, size_t lim) {
size_t l = strlen(dst);
if (l < lim) {
return dst;
}
size_t r = lim - l;
strncpy(dst + l, src, r);
return dst;
}
size_t strlen(const char *a) {
size_t s = 0;
while (*a++) {
++s;
}
return s;
}
int strncmp(const char *a, const char *b, size_t n) {
size_t c = 0;
for (; c < n && (*a || *b); ++c, ++a, ++b) {
if (*a != *b) {
return -1;
}
}
return 0;
}
int strcmp(const char *a, const char *b) {
if (a == b) {
return 0;
}
if (a == NULL || b == NULL) {
return -1;
}
for (; *a || *b; ++a, ++b) {
if (*a != *b) {
return 1;
}
}
return 0;
}
char *strcpy(char *dst, const char *src) {
size_t i;
for (i = 0; src[i]; ++i) {
dst[i] = src[i];
}
dst[i] = 0;
return dst;
}
char *strncpy(char *dst, const char *src, size_t lim) {
size_t i = 0;
while (i < lim) {
if (!src[i]) {
dst[i] = 0;
return dst;
}
dst[i] = src[i];
++i;
}
return dst;
}
char *strchr(const char *s, int c) {
while (*s) {
if (*s == (char) c) {
return (char *) s;
}
++s;
}
return NULL;
}
char *strrchr(const char *s, int c) {
ssize_t l = (ssize_t) strlen(s);
if (!l) {
return NULL;
}
while (l >= 0) {
if (s[l] == (char) c) {
return (char *) &s[l];
}
--l;
}
return NULL;
}
int memcmp(const void *a, const void *b, size_t n) {
const char *l = a;
const char *r = b;
for (; n && *l == *r; --n, ++l, ++r);
return n ? *l - *r : 0;
}
void *memmove(void *dest, const void *src, size_t n) {
char *d = dest;
const char *s = src;
if (d == s) {
return d;
}
if ((s + n) <= d || (d + n) <= s) {
return memcpy(d, s, n);
}
if (d < s) {
if (((uintptr_t) s) % sizeof(size_t) == ((uintptr_t) d) % sizeof(size_t)) {
while (((uintptr_t) d) % sizeof(size_t)) {
if (!n--) {
return dest;
}
*d++ = *s++;
}
for (; n >= sizeof(size_t); n -= sizeof(size_t), d += sizeof(size_t), s += sizeof(size_t)) {
*((size_t *) d) = *((size_t *) s);
}
}
for (; n; n--) {
*d++ = *s++;
}
} else {
if (((uintptr_t) s) % sizeof(size_t) == ((uintptr_t) d) % sizeof(size_t)) {
while (((uintptr_t) (d + n)) % sizeof(size_t)) {
if (!n--) {
return dest;
}
d[n] = s[n];
}
while (n >= sizeof(size_t)) {
n -= sizeof(size_t);
*((size_t *) (d + n)) = *((size_t *) (s + n));
}
}
while (n) {
n--;
d[n] = s[n];
}
}
return dest;
}
void *memset(void *blk, int v, size_t sz) {
for (size_t i = 0; i < sz; ++i) {
((char *) blk)[i] = v;
}
return blk;
}
void *memcpy(void *dst, const void *src, size_t sz) {
for (size_t i = 0; i < sz; ++i) {
((char *) dst)[i] = ((const char *) src)[i];
}
return dst;
}

179
libc/syscall.c Normal file
View File

@ -0,0 +1,179 @@
#include "bits/syscall.h"
#include "bits/global.h"
#include <errno.h>
#define ASM_REGISTER(name) \
register uint64_t name asm (#name)
#define ASM_SYSCALL0(r0) ({ \
ASM_REGISTER(rax) = (uint64_t) (r0); \
asm volatile ("syscall":::"memory"); \
rax; \
})
#define ASM_SYSCALL1(r0, r1) ({ \
ASM_REGISTER(rdi) = (uint64_t) (r1); \
/*
* Should be the last one because memory accesses for arguments
* fuck up %rax
*/ \
ASM_REGISTER(rax) = (uint64_t) (r0); \
asm volatile ("syscall":::"memory"); \
rax; \
})
#define ASM_SYSCALL2(r0, r1, r2) ({ \
ASM_REGISTER(rdi) = (uint64_t) (r1); \
ASM_REGISTER(rsi) = (uint64_t) (r2); \
/*
* Should be the last one because memory accesses for arguments
* fuck up %rax
*/ \
ASM_REGISTER(rax) = (uint64_t) (r0); \
asm volatile ("syscall":::"memory", "rax", "rdi", "rsi", "rdx"); \
rax; \
})
#define ASM_SYSCALL3(r0, r1, r2, r3) ({ \
ASM_REGISTER(rdi) = (uint64_t) (r1); \
ASM_REGISTER(rsi) = (uint64_t) (r2); \
ASM_REGISTER(rdx) = (uint64_t) (r3); \
/*
* Should be the last one because memory accesses for arguments
* fuck up %rax
*/ \
ASM_REGISTER(rax) = (uint64_t) (r0); \
asm volatile ("syscall":::"memory", "rax", "rdi", "rsi", "rdx"); \
rax; \
})
#define ASM_SYSCALL4(r0, r1, r2, r3, r4) ({ \
ASM_REGISTER(rdi) = (uint64_t) (r1); \
ASM_REGISTER(rsi) = (uint64_t) (r2); \
ASM_REGISTER(rdx) = (uint64_t) (r3); \
ASM_REGISTER(r10) = (uint64_t) (r4); \
/*
* Should be the last one because memory accesses for arguments
* fuck up %rax
*/ \
ASM_REGISTER(rax) = (uint64_t) (r0); \
asm volatile ("syscall":::"memory", "rax", "rdi", "rsi", "rdx", "r10"); \
rax; \
})
#define SET_ERRNO(t, r) ({ \
t res = (t) r; \
if (res < 0) { \
errno = -res; \
} \
((int) res) < 0 ? (t) -1 : res; \
})
ssize_t write(int fd, const void *buf, size_t count) {
return SET_ERRNO(ssize_t, ASM_SYSCALL3(SYSCALL_NR_WRITE, fd, buf, count));
}
ssize_t read(int fd, void *buf, size_t count) {
return SET_ERRNO(ssize_t, ASM_SYSCALL3(SYSCALL_NR_READ, fd, buf, count));
}
ssize_t sys_readdir(int fd, struct dirent *entp) {
return SET_ERRNO(ssize_t, ASM_SYSCALL2(SYSCALL_NR_READDIR, fd, entp));
}
int open(const char *filename, int flags, int mode) {
return SET_ERRNO(int, ASM_SYSCALL3(SYSCALL_NR_OPEN, filename, flags, mode));
}
void close(int fd) {
(void) ASM_SYSCALL1(SYSCALL_NR_CLOSE, fd);
}
int stat(const char *filename, struct stat *st) {
return SET_ERRNO(int, ASM_SYSCALL2(SYSCALL_NR_STAT, filename, st));
}
int brk(void *addr) {
return SET_ERRNO(int, ASM_SYSCALL1(SYSCALL_NR_BRK, addr));
}
int fork(void) {
return SET_ERRNO(int, ASM_SYSCALL0(SYSCALL_NR_FORK));
}
int execve(const char *filename, const char *const argv[], const char *const envp[]) {
return SET_ERRNO(int, ASM_SYSCALL3(SYSCALL_NR_EXECVE, filename, argv, envp));
}
__attribute__((noreturn)) void exit(int code) {
(void) ASM_SYSCALL1(SYSCALL_NR_EXIT, code);
while (1);
}
int kill(int pid, int signum) {
return SET_ERRNO(int, ASM_SYSCALL2(SYSCALL_NR_KILL, pid, signum));
}
void __kernel_signal(uintptr_t entry) {
(void) ASM_SYSCALL1(SYSCALL_NRX_SIGNAL, entry);
}
__attribute__((noreturn)) void __kernel_sigret(void) {
(void) ASM_SYSCALL0(SYSCALL_NRX_SIGRET);
while (1);
}
int getpid(void) {
return ASM_SYSCALL0(SYSCALL_NR_GETPID);
}
int chdir(const char *filename) {
return SET_ERRNO(int, ASM_SYSCALL1(SYSCALL_NR_CHDIR, filename));
}
char *getcwd(char *buf, size_t lim) {
if (SET_ERRNO(int, ASM_SYSCALL2(SYSCALL_NR_GETCWD, buf, lim)) == 0) {
return buf;
} else {
return NULL;
}
}
int nanosleep(const struct timespec *req, struct timespec *rem) {
return SET_ERRNO(int, ASM_SYSCALL2(SYSCALL_NR_NANOSLEEP, req, rem));
}
int openpty(int *master, int *slave) {
return SET_ERRNO(int, ASM_SYSCALL2(SYSCALL_NRX_OPENPTY, master, slave));
}
int gettimeofday(struct timeval *tv, struct timezone *tz) {
return SET_ERRNO(int, ASM_SYSCALL2(SYSCALL_NR_GETTIMEOFDAY, tv, tz));
}
int reboot(int magic1, int magic2, unsigned int cmd, void *arg) {
return SET_ERRNO(int, ASM_SYSCALL4(SYSCALL_NR_REBOOT, magic1, magic2, cmd, arg));
}
int access(const char *filename, int mode) {
return SET_ERRNO(int, ASM_SYSCALL2(SYSCALL_NR_ACCESS, filename, mode));
}
int waitpid(int pid, int *status) {
return SET_ERRNO(int, ASM_SYSCALL2(SYSCALL_NRX_WAITPID, pid, status));
}
// Although sbrk() is implemented in userspace, I guess it should also be here
void *sbrk(intptr_t inc) {
if (inc == 0) {
return __cur_brk;
}
void *new_brk = (void *) ((uintptr_t) __cur_brk + inc);
if (brk(new_brk) != 0) {
return __cur_brk;
} else {
return __cur_brk = new_brk;
}
}

7
libc/time.c Normal file
View File

@ -0,0 +1,7 @@
#include "time.h"
#include "bits/syscall.h"
int usleep(long us) {
struct timespec tv = { us / 1000000, (us % 1000) * 1000 };
return nanosleep(&tv, NULL);
}

255
libc/vsnprintf.c Normal file
View File

@ -0,0 +1,255 @@
#include "bits/printf.h"
#include <string.h>
#include <stdint.h>
union printfv {
int value_int;
unsigned int value_uint;
long value_long;
uint32_t value_uint32;
uint64_t value_uint64;
uintptr_t value_ptr;
const char *value_str;
};
static const char *s_print_xs_set0 = "0123456789abcdef";
static const char *s_print_xs_set1 = "0123456789ABCDEF";
static int vsnprintf_ds(int64_t x, char *res, int s, int sz) {
if (!x) {
res[0] = '0';
res[1] = 0;
return 1;
}
int c;
uint64_t v;
if (sz) {
if (s && x < 0) {
v = (uint64_t) -x;
} else {
s = 0;
v = (uint64_t) x;
}
} else {
if (s && ((int32_t) x) < 0) {
v = (uint64_t) -((int32_t) x);
} else {
s = 0;
v = (uint64_t) x;
}
}
c = 0;
while (v) {
res[c++] = '0' + v % 10;
v /= 10;
}
if (s) {
res[c++] = '-';
}
res[c] = 0;
for (int i = 0, j = c - 1; i < j; ++i, --j) {
res[i] ^= res[j];
res[j] ^= res[i];
res[i] ^= res[j];
}
return c;
}
static int vsnprintf_xs(uint64_t v, char *res, const char *set) {
if (!v) {
res[0] = '0';
res[1] = 0;
return 1;
}
int c = 0;
while (v) {
res[c++] = set[v & 0xF];
v >>= 4;
}
res[c] = 0;
for (int i = 0, j = c - 1; i < j; ++i, --j) {
res[i] ^= res[j];
res[j] ^= res[i];
res[i] ^= res[j];
}
return c;
}
int vsnprintf(char *buf, size_t len, const char *fmt, va_list args) {
union printfv val;
char cbuf[64];
size_t p = 0;
// padn > 0:
// --------vvvvv
// padn < 0:
// vvvv----------
int padn;
char padc;
int pads;
int clen;
char c;
#define __putc(ch) \
if (p == len - 1) { \
goto __end; \
} else { \
buf[p++] = ch; \
}
#define __puts(s, l) \
if (padn >= 0) { \
if (l < padn) { \
size_t padd = padn - l; \
if (p + padd < len + 1) { \
memset(buf + p, padc, padd); \
p += padd; \
} else { \
memset(buf + p, padc, len - 1 - p); \
p = len - 1; \
goto __end; \
} \
} \
if (p + l < len + 1) { \
strncpy(buf + p, s, l); \
p += l; \
} else { \
strncpy(buf + p, s, len - 1 - p); \
p = len - 1; \
goto __end; \
} \
} else { \
padn = -padn; \
if (p + l < len + 1) { \
strncpy(buf + p, s, l); \
p += l; \
} else { \
strncpy(buf + p, s, len - 1 - p); \
p = len - 1; \
goto __end; \
} \
if (l < padn) { \
size_t padd = padn - l; \
if (p + padd < len + 1) { \
memset(buf + p, padc, padd); \
p += padd; \
} else { \
memset(buf + p, padc, len - 1 - p); \
p = len - 1; \
goto __end; \
} \
} \
}
while ((c = *fmt)) {
switch (c) {
case '%':
c = *++fmt;
padn = 0;
padc = ' ';
pads = 1;
if (c == '0') {
padc = c;
c = *++fmt;
}
if (c == '-') {
pads = -1;
c = *++fmt;
}
while (c >= '0' && c <= '9') {
padn *= 10;
padn += c - '0';
c = *++fmt;
}
padn *= pads;
switch (c) {
case 'l':
// Not supported
return -1;
case 's':
if ((val.value_str = va_arg(args, const char *))) {
__puts(val.value_str, strlen(val.value_str));
} else {
__puts("(null)", 6);
}
break;
case 'p':
val.value_ptr = va_arg(args, uintptr_t);
__putc('0');
__putc('x');
padn = sizeof(uintptr_t) * 2;
padc = '0';
clen = vsnprintf_xs(val.value_ptr, cbuf, s_print_xs_set0);
__puts(cbuf, clen);
break;
case 'i':
case 'd':
val.value_int = va_arg(args, int);
clen = vsnprintf_ds(val.value_int, cbuf, 1, 1);
__puts(cbuf, clen);
break;
case 'u':
val.value_uint = va_arg(args, unsigned int);
clen = vsnprintf_ds(val.value_uint, cbuf, 0, 1);
__puts(cbuf, clen);
break;
case 'x':
val.value_uint32 = va_arg(args, uint32_t);
clen = vsnprintf_xs(val.value_uint32, cbuf, s_print_xs_set0);
__puts(cbuf, clen);
break;
case 'X':
val.value_uint32 = va_arg(args, uint32_t);
clen = vsnprintf_xs(val.value_uint32, cbuf, s_print_xs_set1);
__puts(cbuf, clen);
break;
case 'c':
val.value_int = va_arg(args, int);
__putc(val.value_int);
break;
default:
__putc('%');
__putc(c);
break;
}
break;
default:
__putc(c);
break;
}
++fmt;
}
#undef __puts
#undef __putc
__end:
buf[p] = 0;
return p;
}

12
time.c Normal file
View File

@ -0,0 +1,12 @@
#include <stdio.h>
#include <sys/time.h>
int main() {
struct timeval tv;
while (1) {
usleep(100000);
gettimeofday(&tv, NULL);
printf("\033[s\033[1;70f%u\033[u", tv.tv_sec);
}
}