Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[RFC PATCH 22/28] lkl tools: "boot" test

149 views
Skip to first unread message

Octavian Purdila

unread,
Nov 3, 2015, 3:30:08 PM11/3/15
to
Add a simple LKL test applications that starts the kernel and performs
simple tests that minimally exercise the LKL API.

Signed-off-by: Octavian Purdila <octavian...@intel.com>
---
tools/lkl/.gitignore | 1 +
tools/lkl/Makefile | 7 +-
tools/lkl/tests/boot.c | 488 ++++++++++++++++++++++++++++++++++++++++++++++++
tools/lkl/tests/boot.sh | 10 +
4 files changed, 504 insertions(+), 2 deletions(-)
create mode 100644 tools/lkl/tests/boot.c
create mode 100755 tools/lkl/tests/boot.sh

diff --git a/tools/lkl/.gitignore b/tools/lkl/.gitignore
index e69de29..7c456f2 100644
--- a/tools/lkl/.gitignore
+++ b/tools/lkl/.gitignore
@@ -0,0 +1 @@
+test/boot
diff --git a/tools/lkl/Makefile b/tools/lkl/Makefile
index cf97d27..1ae4481 100644
--- a/tools/lkl/Makefile
+++ b/tools/lkl/Makefile
@@ -7,14 +7,17 @@ LD=$(CROSS_COMPILE)ld
endif

lib_source = $(filter-out %-host.c,$(wildcard lib/*.c))
+source = $(wildcard tests/*.c)
ifneq (,$(filter $(shell $(LD) -r -print-output-format),elf64-x86-64 elf32-i386))
lib_source += lib/posix-host.c
LDFLAGS += -lpthread -lrt
endif

lib_objs = $(patsubst %.c,%.o, $(lib_source)) lib/lkl.o
+objs = $(patsubst %.c,%.o, $(source))
+execs = $(patsubst %.c,%, $(source))

-all: lib/liblkl.a
+all: lib/liblkl.a $(execs)

lib/liblkl.a: $(lib_objs)
$(AR) -rc $@ $^
@@ -31,4 +34,4 @@ $(objs): lib/lkl.o
$(execs): lib/liblkl.a

clean:
- -rm -rf include/lkl/ lib/liblkl.a $(lib_objs)
+ -rm -rf include/lkl/ lib/liblkl.a $(lib_objs) $(objs) $(execs)
diff --git a/tools/lkl/tests/boot.c b/tools/lkl/tests/boot.c
new file mode 100644
index 0000000..f5945aa
--- /dev/null
+++ b/tools/lkl/tests/boot.c
@@ -0,0 +1,488 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <lkl.h>
+#include <lkl_host.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+static struct cl_args {
+ int printk;
+ const char *disk_filename;
+} cla;
+
+static struct cl_option {
+ const char *long_name;
+ char short_name;
+ const char *help;
+ int has_arg;
+} options[] = {
+ {"enable-printk", 'p', "show Linux printks", 0},
+ {"disk-file", 'd', "disk file to use", 1},
+ {0},
+};
+
+static int parse_opt(int key, char *arg)
+{
+ switch (key) {
+ case 'p':
+ cla.printk = 1;
+ break;
+ case 'd':
+ cla.disk_filename = arg;
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+void printk(const char *str, int len)
+{
+ if (cla.printk)
+ write(STDOUT_FILENO, str, len);
+}
+
+#define TEST(name) do_test(#name, test_##name)
+
+static void do_test(char *name, int (*fn)(char *, int))
+{
+ char str[60];
+ int result;
+
+ result = fn(str, sizeof(str));
+ printf("%-20s %s [%s]\n", name, result ? "passed" : "failed", str);
+}
+
+#define sleep_ns 87654321
+
+int test_nanosleep(char *str, int len)
+{
+ struct lkl_timespec ts = {
+ .tv_sec = 0,
+ .tv_nsec = sleep_ns,
+ };
+ struct timespec start, stop;
+ long delta;
+ long ret;
+
+ clock_gettime(CLOCK_MONOTONIC, &start);
+ ret = lkl_sys_nanosleep(&ts, NULL);
+ clock_gettime(CLOCK_MONOTONIC, &stop);
+
+ delta = (stop.tv_sec - start.tv_sec) +
+ (stop.tv_nsec - start.tv_nsec);
+
+ snprintf(str, len, "%ld", delta);
+
+ if (ret == 0 && delta > sleep_ns * 0.9 && delta < sleep_ns * 1.1)
+ return 1;
+
+ return 0;
+}
+
+int test_getpid(char *str, int len)
+{
+ long ret;
+
+ ret = lkl_sys_getpid();
+
+ snprintf(str, len, "%ld", ret);
+
+ if (ret == 1)
+ return 1;
+
+ return 0;
+}
+
+#define access_rights 0721
+
+int test_creat(char *str, int len)
+{
+ long ret;
+
+ ret = lkl_sys_creat("/file", access_rights);
+
+ snprintf(str, len, "%ld", ret);
+
+ if (ret == 0)
+ return 1;
+
+ return 0;
+}
+
+int test_close(char *str, int len)
+{
+ long ret;
+
+ ret = lkl_sys_close(0);
+
+ snprintf(str, len, "%ld", ret);
+
+ if (ret == 0)
+ return 1;
+
+ return 0;
+}
+
+int test_failopen(char *str, int len)
+{
+ long ret;
+
+ ret = lkl_sys_open("/file2", 0, 0);
+
+ snprintf(str, len, "%ld", ret);
+
+ if (ret == -LKL_ENOENT)
+ return 1;
+
+ return 0;
+}
+
+int test_umask(char *str, int len)
+{
+ long ret, ret2;
+
+ ret = lkl_sys_umask(0777);
+
+ ret2 = lkl_sys_umask(0);
+
+ snprintf(str, len, "%lo %lo", ret, ret2);
+
+ if (ret > 0 && ret2 == 0777)
+ return 1;
+
+ return 0;
+}
+
+int test_open(char *str, int len)
+{
+ long ret;
+
+ ret = lkl_sys_open("/file", LKL_O_RDWR, 0);
+
+ snprintf(str, len, "%ld", ret);
+
+ if (ret == 0)
+ return 1;
+
+ return 0;
+}
+
+static const char write_test[] = "test";
+
+int test_write(char *str, int len)
+{
+ long ret;
+
+ ret = lkl_sys_write(0, write_test, sizeof(write_test));
+
+ snprintf(str, len, "%ld", ret);
+
+ if (ret == sizeof(write_test))
+ return 1;
+
+ return 0;
+}
+
+int test_lseek(char *str, int len)
+{
+ long ret;
+ __lkl__kernel_loff_t res;
+
+ ret = lkl_sys_lseek(0, 0, &res, LKL_SEEK_SET);
+
+ snprintf(str, len, "%ld %lld", ret, res);
+
+ if (ret == 0)
+ return 1;
+
+ return 0;
+}
+
+int test_read(char *str, int len)
+{
+ char buf[10] = { 0, };
+ long ret;
+
+ ret = lkl_sys_read(0, buf, sizeof(buf));
+
+ snprintf(str, len, "%ld %s", ret, buf);
+
+ if (ret == sizeof(write_test) && strcmp(write_test, buf) == 0)
+ return 1;
+
+ return 0;
+}
+
+int test_fstat64(char *str, int len)
+{
+ struct lkl_stat64 stat;
+ long ret;
+
+ ret = lkl_sys_fstat64(0, &stat);
+
+ snprintf(str, len, "%ld %o %lld", ret, stat.st_mode, stat.st_size);
+
+ if (ret == 0 && stat.st_size == sizeof(write_test) &&
+ stat.st_mode == (access_rights | LKL_S_IFREG))
+ return 1;
+
+ return 0;
+}
+
+int test_mkdir(char *str, int len)
+{
+ long ret;
+
+ ret = lkl_sys_mkdir("/mnt", access_rights);
+
+ snprintf(str, len, "%ld", ret);
+
+ if (ret == 0)
+ return 1;
+
+ return 0;
+}
+
+int test_stat64(char *str, int len)
+{
+ struct lkl_stat64 stat;
+ long ret;
+
+ ret = lkl_sys_stat64("/mnt", &stat);
+
+ snprintf(str, len, "%ld %o", ret, stat.st_mode);
+
+ if (ret == 0 && stat.st_mode == (access_rights | LKL_S_IFDIR))
+ return 1;
+
+ return 0;
+}
+
+static const char *tmp_file;
+static union lkl_disk_backstore bs;
+static int disk_id = -1;
+
+int test_disk_add(char *str, int len)
+{
+ bs.fd = open(cla.disk_filename, O_RDWR);
+ if (bs.fd < 0)
+ goto out_unlink;
+
+ disk_id = lkl_disk_add(bs);
+ if (disk_id < 0)
+ goto out_close;
+
+ goto out;
+
+out_close:
+ close(bs.fd);
+out_unlink:
+ unlink(cla.disk_filename);
+
+out:
+ snprintf(str, len, "%x %d", bs.fd, disk_id);
+
+ if (disk_id >= 0)
+ return 1;
+
+ return 0;
+}
+
+static char mnt_point[32];
+
+static int test_mount(char *str, int len)
+{
+ long ret;
+
+ ret = lkl_mount_dev(disk_id, "ext4", 0, NULL, mnt_point,
+ sizeof(mnt_point));
+
+ snprintf(str, len, "%ld", ret);
+
+ if (ret == 0)
+ return 1;
+
+ return 0;
+}
+
+static int test_chdir(char *str, int len)
+{
+ long ret;
+
+ ret = lkl_sys_chdir(mnt_point);
+
+ snprintf(str, len, "%ld", ret);
+
+ if (ret == 0)
+ return 1;
+
+ return 0;
+}
+
+static int dir_fd;
+
+static int test_opendir(char *str, int len)
+{
+ dir_fd = lkl_sys_open(".", LKL_O_RDONLY | LKL_O_DIRECTORY, 0);
+
+ snprintf(str, len, "%d", dir_fd);
+
+ if (dir_fd > 0)
+ return 1;
+
+ return 0;
+}
+
+static int test_getdents64(char *str, int len)
+{
+ long ret;
+ char buf[1024], *pos;
+ struct lkl_dirent64 *de;
+ int wr;
+
+ ret = lkl_sys_getdents64(dir_fd, buf, sizeof(buf));
+
+ wr = snprintf(str, len, "%d ", dir_fd);
+ str += wr;
+ len -= wr;
+
+ if (ret < 0)
+ return 0;
+
+ for (pos = buf; pos - buf < ret; pos += de->d_reclen) {
+ de = (struct lkl_dirent64 *)pos;
+
+ wr = snprintf(str, len, "%s ", de->d_name);
+ str += wr;
+ len -= wr;
+ }
+
+ return 1;
+}
+
+static int test_umount(char *str, int len)
+{
+ long ret, ret2, ret3;
+
+ ret = lkl_sys_close(dir_fd);
+
+ ret2 = lkl_sys_chdir("/");
+
+ ret3 = lkl_umount_dev(disk_id, 0, 1000);
+
+ snprintf(str, len, "%ld %ld %ld", ret, ret2, ret3);
+
+ if (!ret && !ret2 && !ret3)
+ return 1;
+
+ return 0;
+}
+
+static struct cl_option *find_short_opt(char name)
+{
+ struct cl_option *opt;
+
+ for (opt = options; opt->short_name != 0; opt++) {
+ if (opt->short_name == name)
+ return opt;
+ }
+
+ return NULL;
+}
+
+static struct cl_option *find_long_opt(const char *name)
+{
+ struct cl_option *opt;
+
+ for (opt = options; opt->long_name; opt++) {
+ if (strcmp(opt->long_name, name) == 0)
+ return opt;
+ }
+
+ return NULL;
+}
+
+static void print_help(void)
+{
+ struct cl_option *opt;
+
+ printf("usage:\n");
+ for (opt = options; opt->long_name; opt++)
+ printf("-%c, --%-20s %s\n", opt->short_name, opt->long_name,
+ opt->help);
+}
+
+static int parse_opts(int argc, char **argv)
+{
+ int i;
+
+ for (i = 1; i < argc; i++) {
+ struct cl_option *opt = NULL;
+
+ if (argv[i][0] == '-') {
+ if (argv[i][1] != '-')
+ opt = find_short_opt(argv[i][1]);
+ else
+ opt = find_long_opt(&argv[i][2]);
+ }
+
+ if (!opt) {
+ print_help();
+ return -1;
+ }
+
+ if (parse_opt(opt->short_name, argv[i + 1]) < 0) {
+ print_help();
+ return -1;
+ }
+
+ if (opt->has_arg)
+ i++;
+ }
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ if (parse_opts(argc, argv) < 0)
+ return -1;
+
+ lkl_host_ops.print = printk;
+
+ TEST(disk_add);
+
+ lkl_start_kernel(&lkl_host_ops, 10 * 1024 * 1024, "");
+
+ TEST(getpid);
+ TEST(umask);
+ TEST(creat);
+ TEST(close);
+ TEST(failopen);
+ TEST(open);
+ TEST(write);
+ TEST(lseek);
+ TEST(read);
+ TEST(fstat64);
+ TEST(mkdir);
+ TEST(stat64);
+ TEST(nanosleep);
+ TEST(mount);
+ TEST(chdir);
+ TEST(opendir);
+ TEST(getdents64);
+ TEST(umount);
+
+ lkl_sys_halt();
+
+ close(bs.fd);
+ unlink(tmp_file);
+
+ return 0;
+}
diff --git a/tools/lkl/tests/boot.sh b/tools/lkl/tests/boot.sh
new file mode 100755
index 0000000..3fb7b1f
--- /dev/null
+++ b/tools/lkl/tests/boot.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+file=`mktemp`
+dd if=/dev/zero of=$file bs=1024 count=10240
+
+yes | mkfs.ext4 -q $file
+
+./boot -d $file $@
+
+rm $file
--
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majo...@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

Octavian Purdila

unread,
Nov 3, 2015, 3:30:09 PM11/3/15
to
Signed-off-by: Octavian Purdila <octavian...@intel.com>
---
tools/lkl/.gitignore | 2 +
tools/lkl/Makefile | 6 +-
tools/lkl/cptofs.c | 467 +++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 474 insertions(+), 1 deletion(-)
create mode 100644 tools/lkl/cptofs.c

diff --git a/tools/lkl/.gitignore b/tools/lkl/.gitignore
index a345a79..1048323 100644
--- a/tools/lkl/.gitignore
+++ b/tools/lkl/.gitignore
@@ -1,2 +1,4 @@
test/boot
fs2tar
+cptofs
+cpfromfs
diff --git a/tools/lkl/Makefile b/tools/lkl/Makefile
index 9aeab49..4084609 100644
--- a/tools/lkl/Makefile
+++ b/tools/lkl/Makefile
@@ -12,11 +12,13 @@ ifneq (,$(filter $(shell $(LD) -r -print-output-format),elf64-x86-64 elf32-i386)
source += $(wildcard *.c)
lib_source += lib/posix-host.c
LDFLAGS += -lpthread -lrt
+source += $(wildcard *.c)
+execs = cpfromfs
endif

lib_objs = $(patsubst %.c,%.o, $(lib_source)) lib/lkl.o
objs = $(patsubst %.c,%.o, $(source))
-execs = $(patsubst %.c,%, $(source))
+execs += $(patsubst %.c,%, $(source))

all: lib/liblkl.a $(execs)

@@ -38,3 +40,5 @@ clean:
-rm -rf include/lkl/ lib/liblkl.a $(lib_objs) $(objs) $(execs)

fs2tar: LDFLAGS += -larchive
+cpfromfs: cptofs
+ if ! [ -e $@ ]; then ln -s $< $@; fi
diff --git a/tools/lkl/cptofs.c b/tools/lkl/cptofs.c
new file mode 100644
index 0000000..0017395
--- /dev/null
+++ b/tools/lkl/cptofs.c
@@ -0,0 +1,467 @@
+#include <stdio.h>
+#include <time.h>
+#include <argp.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <libgen.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <fnmatch.h>
+#undef st_atime
+#undef st_mtime
+#undef st_ctime
+#include <dirent.h>
+#include <lkl.h>
+#include <lkl_host.h>
+
+static const char doc_cptofs[] = "Copy files to a filesystem image";
+static const char doc_cpfromfs[] = "Copy files from a filesystem image";
+static const char args_doc_cptofs[] = "-t fstype -i fsimage path fs_path";
+static const char args_doc_cpfromfs[] = "-t fstype -i fsimage fs_path path";
+
+static struct argp_option options[] = {
+ {"enable-printk", 'p', 0, 0, "show Linux printks"},
+ {"filesystem-type", 't', "string", 0,
+ "select filesystem type - mandatory"},
+ {"filesystem-image", 'i', "string", 0,
+ "path to the filesystem image - mandatory"},
+ {"selinux", 's', "string", 0, "selinux attributes for destination"},
+ {0},
+};
+
+static struct cl_args {
+ int printk;
+ const char *fsimg_type;
+ const char *fsimg_path;
+ const char *src_path;
+ const char *dst_path;
+ const char *selinux;
+} cla;
+
+static int cptofs;
+
+static error_t parse_opt(int key, char *arg, struct argp_state *state)
+{
+ struct cl_args *cla = state->input;
+
+ switch (key) {
+ case 'p':
+ cla->printk = 1;
+ break;
+ case 't':
+ cla->fsimg_type = arg;
+ break;
+ case 'i':
+ cla->fsimg_path = arg;
+ break;
+ case 's':
+ cla->selinux = arg;
+ break;
+ case ARGP_KEY_ARG:
+ if (!cla->src_path) {
+ cla->src_path = arg;
+ } else if (!cla->dst_path) {
+ cla->dst_path = arg;
+ } else {
+ argp_usage(state);
+ return -1;
+ }
+ break;
+ case ARGP_KEY_END:
+ if (state->arg_num < 2 || !cla->fsimg_type || !cla->fsimg_path)
+ argp_usage(state);
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ return 0;
+}
+
+static struct argp argp_cptofs = {
+ .options = options,
+ .parser = parse_opt,
+ .args_doc = args_doc_cptofs,
+ .doc = doc_cptofs,
+};
+
+static struct argp argp_cpfromfs = {
+ .options = options,
+ .parser = parse_opt,
+ .args_doc = args_doc_cpfromfs,
+ .doc = doc_cpfromfs,
+};
+
+static int searchdir(const char *fs_path, const char *path, const char *match);
+
+static int open_src(const char *path)
+{
+ int fd;
+
+ if (cptofs)
+ fd = open(path, O_RDONLY, 0);
+ else
+ fd = lkl_sys_open(path, LKL_O_RDONLY, 0);
+
+ if (fd < 0)
+ fprintf(stderr, "unable to open file %s for reading: %s\n",
+ path, cptofs ? strerror(errno) : lkl_strerror(fd));
+
+ return fd;
+}
+
+static int open_dst(const char *path, int mode)
+{
+ int fd;
+
+ if (cptofs)
+ fd = lkl_sys_open(path, LKL_O_RDWR | LKL_O_TRUNC | LKL_O_CREAT,
+ mode);
+ else
+ fd = open(path, O_RDWR | O_TRUNC | O_CREAT, mode);
+
+ if (fd < 0)
+ fprintf(stderr, "unable to open file %s for writing: %s\n",
+ path, cptofs ? lkl_strerror(fd) : strerror(errno));
+
+ if (cla.selinux && cptofs) {
+ int ret = lkl_sys_fsetxattr(fd, "security.selinux", cla.selinux,
+ strlen(cla.selinux), 0);
+ if (ret)
+ fprintf(stderr, "unable to set selinux attribute on %s: %s\n",
+ path, lkl_strerror(ret));
+ }
+
+ return fd;
+}
+
+static int read_src(int fd, char *buf, int len)
+{
+ int ret;
+
+ if (cptofs)
+ ret = read(fd, buf, len);
+ else
+ ret = lkl_sys_read(fd, buf, len);
+
+ if (ret < 0)
+ fprintf(stderr, "error reading file: %s\n",
+ cptofs ? strerror(errno) : lkl_strerror(ret));
+
+ return ret;
+}
+
+static int write_dst(int fd, char *buf, int len)
+{
+ int ret;
+
+ if (cptofs)
+ ret = lkl_sys_write(fd, buf, len);
+ else
+ ret = write(fd, buf, len);
+
+ if (ret < 0)
+ fprintf(stderr, "error writing file: %s\n",
+ cptofs ? lkl_strerror(ret) : strerror(errno));
+
+ return ret;
+}
+
+static void close_src(int fd)
+{
+ if (cptofs)
+ close(fd);
+ else
+ lkl_sys_close(fd);
+}
+
+static void close_dst(int fd)
+{
+ if (cptofs)
+ lkl_sys_close(fd);
+ else
+ close(fd);
+}
+
+static int copy_file(const char *src, const char *dst, int mode)
+{
+ long len, to_write, wrote;
+ char buf[4096], *ptr;
+ int ret = 0;
+ int fd_src, fd_dst;
+
+ fd_src = open_src(src);
+ if (fd_src < 0)
+ return fd_src;
+
+ fd_dst = open_dst(dst, mode);
+ if (fd_dst < 0)
+ return fd_dst;
+
+ do {
+ len = read_src(fd_src, buf, sizeof(buf));
+
+ if (len > 0) {
+ ptr = buf;
+ to_write = len;
+ do {
+ wrote = write_dst(fd_dst, ptr, to_write);
+
+ if (wrote < 0) {
+ ret = wrote;
+ goto out;
+ }
+
+ to_write -= wrote;
+ ptr += len;
+
+ } while (to_write > 0);
+ }
+
+ if (len < 0)
+ ret = len;
+
+ } while (len > 0);
+
+out:
+ close_src(fd_src);
+ close_dst(fd_dst);
+
+ return ret;
+}
+
+static int stat_src(const char *path, int *type, int *mode)
+{
+ struct stat stat;
+ struct lkl_stat64 lkl_stat;
+ int ret;
+
+ if (cptofs) {
+ ret = lstat(path, &stat);
+ *type = stat.st_mode & S_IFMT;
+ *mode = stat.st_mode & ~S_IFMT;
+ } else {
+ ret = lkl_sys_lstat64(path, &lkl_stat);
+ *type = lkl_stat.st_mode & S_IFMT;
+ *mode = lkl_stat.st_mode & ~S_IFMT;
+ }
+
+ if (ret)
+ fprintf(stderr, "fsimg fstat64(%s) error: %s\n",
+ path, cptofs ? strerror(errno) : lkl_strerror(ret));
+
+ return ret;
+}
+
+static int mkdir_dst(const char *path, int mode)
+{
+ int ret;
+
+ if (cptofs) {
+ ret = lkl_sys_mkdir(path, mode);
+ if (ret == -LKL_EEXIST)
+ ret = 0;
+ } else {
+ ret = mkdir(path, mode);
+ if (ret < 0 && errno == EEXIST)
+ ret = 0;
+ }
+
+ if (ret)
+ fprintf(stderr, "unable to create directory %s: %s\n",
+ path, cptofs ? strerror(errno) : lkl_strerror(ret));
+
+ return ret;
+}
+
+static int do_entry(const char *_src, const char *_dst, const char *name)
+{
+ char src[PATH_MAX], dst[PATH_MAX];
+ int type, mode;
+ int ret;
+
+ snprintf(src, sizeof(src), "%s/%s", _src, name);
+ snprintf(dst, sizeof(dst), "%s/%s", _dst, name);
+
+ ret = stat_src(src, &type, &mode);
+
+ switch (type) {
+ case S_IFREG:
+ {
+ ret = copy_file(src, dst, mode);
+ break;
+ }
+ case S_IFDIR:
+ ret = mkdir_dst(dst, mode);
+ if (ret)
+ break;
+ ret = searchdir(src, dst, NULL);
+ break;
+ case S_IFLNK:
+ case S_IFSOCK:
+ case S_IFBLK:
+ case S_IFCHR:
+ case S_IFIFO:
+ default:
+ printf("skipping %s: unsupported entry type %d\n", src, type);
+ }
+
+ if (ret)
+ printf("error processing entry %s, aborting\n", src);
+
+ return ret;
+}
+
+static DIR *open_dir(const char *path)
+{
+ DIR *dir;
+ int err;
+
+ if (cptofs)
+ dir = opendir(path);
+ else
+ dir = (DIR *)lkl_opendir(path, &err);
+
+ if (!dir)
+ fprintf(stderr, "unable to open directory %s: %s\n",
+ path, cptofs ? strerror(errno) : lkl_strerror(err));
+ return dir;
+}
+
+static const char *read_dir(DIR *dir, const char *path)
+{
+ struct lkl_dir *lkl_dir = (struct lkl_dir *)dir;
+ const char *name = NULL;
+ const char *err = NULL;
+
+ if (cptofs) {
+ struct dirent *de = readdir(dir);
+
+ if (de)
+ name = de->d_name;
+ } else {
+ struct lkl_dirent64 *de = lkl_readdir(lkl_dir);
+
+ if (de)
+ name = de->d_name;
+ }
+
+ if (!name) {
+ if (cptofs) {
+ if (errno)
+ err = strerror(errno);
+ } else {
+ if (lkl_errdir(lkl_dir))
+ err = lkl_strerror(lkl_errdir(lkl_dir));
+ }
+ }
+
+ if (err)
+ fprintf(stderr, "error while reading directory %s: %s\n",
+ path, err);
+ return name;
+}
+
+static void close_dir(DIR *dir)
+{
+ if (cptofs)
+ closedir(dir);
+ else
+ lkl_closedir((struct lkl_dir *)dir);
+}
+
+static int searchdir(const char *src, const char *dst, const char *match)
+{
+ DIR *dir;
+ const char *name;
+ int ret = 0;
+
+ dir = open_dir(src);
+ if (!dir)
+ return -1;
+
+ while ((name = read_dir(dir, src))) {
+ if (!strcmp(name, ".") || !strcmp(name, "..") ||
+ (match && fnmatch(match, name, 0) != 0))
+ continue;
+
+ ret = do_entry(src, dst, name);
+ if (ret)
+ goto out;
+ }
+
+out:
+ close_dir(dir);
+
+ return ret;
+}
+
+int main(int argc, char **argv)
+{
+ union lkl_disk_backstore bs;
+ long ret;
+ char mpoint[32], src_path[PATH_MAX], dst_path[PATH_MAX];
+ char *src_path_dir, *src_path_base;
+ unsigned int disk_id;
+
+ if (strstr(argv[0], "cptofs")) {
+ cptofs = 1;
+ ret = argp_parse(&argp_cptofs, argc, argv, 0, 0, &cla);
+ } else {
+ ret = argp_parse(&argp_cpfromfs, argc, argv, 0, 0, &cla);
+ }
+
+ if (ret < 0)
+ return -1;
+
+ if (!cla.printk)
+ lkl_host_ops.print = NULL;
+
+ bs.fd = open(cla.fsimg_path, cptofs ? O_RDWR : O_RDONLY);
+ if (bs.fd < 0) {
+ fprintf(stderr, "can't open fsimg %s: %s\n", cla.fsimg_path,
+ strerror(errno));
+ ret = 1;
+ goto out;
+ }
+
+ disk_id = lkl_disk_add(bs);
+ if (disk_id < 0) {
+ fprintf(stderr, "can't add disk: %s\n", lkl_strerror(ret));
+ goto out_close;
+ }
+
+ lkl_start_kernel(&lkl_host_ops, 100 * 1024 * 1024, "");
+
+ ret = lkl_mount_dev(disk_id, cla.fsimg_type, cptofs ? 0 : LKL_MS_RDONLY,
+ NULL, mpoint, sizeof(mpoint));
+ if (ret) {
+ fprintf(stderr, "can't mount disk: %s\n", lkl_strerror(ret));
+ goto out_close;
+ }
+
+ if (cptofs) {
+ snprintf(src_path, sizeof(src_path), "%s", cla.src_path);
+ snprintf(dst_path, sizeof(dst_path), "%s/%s", mpoint,
+ cla.dst_path);
+ } else {
+ snprintf(src_path, sizeof(src_path), "%s/%s", mpoint,
+ cla.src_path);
+ snprintf(dst_path, sizeof(dst_path), "%s", cla.dst_path);
+ }
+
+ src_path_dir = dirname(strdup(src_path));
+ src_path_base = basename(strdup(src_path));
+
+ ret = searchdir(src_path_dir, dst_path, src_path_base);
+
+ ret = lkl_umount_dev(disk_id, 0, 1000);
+
+out_close:
+ close(bs.fd);
+
+out:
+ lkl_sys_halt();
+
+ return ret;
+}

Octavian Purdila

unread,
Nov 3, 2015, 3:30:09 PM11/3/15
to
This patch adds helpers for implementing the memory mapped I/O host
operations that can be used by code that implements host
devices. Generic host operations for lkl_ioremap and lkl_iomem_access
are provided that allows multiplexing multiple I/O memory mapped
regions.

The host device code can create a new memory mapped I/O region with
register_iomem(). Read and write access functions need to be provided
by the caller.

Signed-off-by: Octavian Purdila <octavian...@intel.com>
---
tools/lkl/lib/iomem.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++
tools/lkl/lib/iomem.h | 14 ++++++
2 files changed, 133 insertions(+)
create mode 100644 tools/lkl/lib/iomem.c
create mode 100644 tools/lkl/lib/iomem.h

diff --git a/tools/lkl/lib/iomem.c b/tools/lkl/lib/iomem.c
new file mode 100644
index 0000000..bef6b71
--- /dev/null
+++ b/tools/lkl/lib/iomem.c
@@ -0,0 +1,119 @@
+#include <string.h>
+#include <stdint.h>
+#include <lkl_host.h>
+
+#include "iomem.h"
+
+#define IOMEM_OFFSET_BITS 24
+#define IOMEM_ADDR_MARK 0x8000000
+#define MAX_IOMEM_REGIONS (IOMEM_ADDR_MARK >> IOMEM_OFFSET_BITS)
+
+#define IOMEM_ADDR_TO_INDEX(addr) \
+ ((((uintptr_t)addr & ~IOMEM_ADDR_MARK) >> IOMEM_OFFSET_BITS))
+#define IOMEM_ADDR_TO_OFFSET(addr) \
+ (((uintptr_t)addr) & ((1 << IOMEM_OFFSET_BITS) - 1))
+#define IOMEM_INDEX_TO_ADDR(i) \
+ (void *)(uintptr_t)((i << IOMEM_OFFSET_BITS) | IOMEM_ADDR_MARK)
+
+static struct iomem_region {
+ void *base;
+ void *iomem_addr;
+ int size;
+ const struct lkl_iomem_ops *ops;
+} *iomem_regions[MAX_IOMEM_REGIONS];
+
+static struct iomem_region *find_iomem_reg(void *base)
+{
+ int i;
+
+ for (i = 0; i < MAX_IOMEM_REGIONS; i++)
+ if (iomem_regions[i] && iomem_regions[i]->base == base)
+ return iomem_regions[i];
+
+ return NULL;
+}
+
+int register_iomem(void *base, int size, const struct lkl_iomem_ops *ops)
+{
+ struct iomem_region *iomem_reg;
+ int i;
+
+ if (size > (1 << IOMEM_OFFSET_BITS) - 1)
+ return -1;
+
+ if (find_iomem_reg(base))
+ return -1;
+
+ for (i = 0; i < MAX_IOMEM_REGIONS; i++)
+ if (!iomem_regions[i])
+ break;
+
+ if (i >= MAX_IOMEM_REGIONS)
+ return -1;
+
+ iomem_reg = lkl_host_ops.mem_alloc(sizeof(*iomem_reg));
+ if (!iomem_reg)
+ return -1;
+
+ iomem_reg->base = base;
+ iomem_reg->size = size;
+ iomem_reg->ops = ops;
+ iomem_reg->iomem_addr = IOMEM_INDEX_TO_ADDR(i);
+
+ iomem_regions[i] = iomem_reg;
+
+ return 0;
+}
+
+void unregister_iomem(void *iomem_base)
+{
+ struct iomem_region *iomem_reg = find_iomem_reg(iomem_base);
+ unsigned int index;
+
+ if (!iomem_reg) {
+ lkl_printf("%s: invalid iomem base %p\n", __func__, iomem_base);
+ return;
+ }
+
+ index = IOMEM_ADDR_TO_INDEX(iomem_reg->iomem_addr);
+ if (index >= MAX_IOMEM_REGIONS) {
+ lkl_printf("%s: invalid iomem_addr %p\n", __func__,
+ iomem_reg->iomem_addr);
+ return;
+ }
+
+ iomem_regions[index] = NULL;
+ lkl_host_ops.mem_free(iomem_reg->base);
+ lkl_host_ops.mem_free(iomem_reg);
+}
+
+void *lkl_ioremap(long addr, int size)
+{
+ struct iomem_region *iomem_reg = find_iomem_reg((void *)addr);
+
+ if (iomem_reg && size <= iomem_reg->size)
+ return iomem_reg->iomem_addr;
+
+ return NULL;
+}
+
+int lkl_iomem_access(const volatile void *addr, void *res, int size, int write)
+{
+ struct iomem_region *iomem_reg;
+ int index = IOMEM_ADDR_TO_INDEX(addr);
+ int offset = IOMEM_ADDR_TO_OFFSET(addr);
+ int ret;
+
+ if (index > MAX_IOMEM_REGIONS || !iomem_regions[index] ||
+ offset + size > iomem_regions[index]->size)
+ return -1;
+
+ iomem_reg = iomem_regions[index];
+
+ if (write)
+ ret = iomem_reg->ops->write(iomem_reg->base, offset, res, size);
+ else
+ ret = iomem_reg->ops->read(iomem_reg->base, offset, res, size);
+
+ return ret;
+}
diff --git a/tools/lkl/lib/iomem.h b/tools/lkl/lib/iomem.h
new file mode 100644
index 0000000..53707d7
--- /dev/null
+++ b/tools/lkl/lib/iomem.h
@@ -0,0 +1,14 @@
+#ifndef _LKL_LIB_IOMEM_H
+#define _LKL_LIB_IOMEM_H
+
+struct lkl_iomem_ops {
+ int (*read)(void *data, int offset, void *res, int size);
+ int (*write)(void *data, int offset, void *value, int size);
+};
+
+int register_iomem(void *base, int size, const struct lkl_iomem_ops *ops);
+void unregister_iomem(void *iomem_base);
+void *lkl_ioremap(long addr, int size);
+int lkl_iomem_access(const volatile void *addr, void *res, int size, int write);
+
+#endif /* _LKL_LIB_IOMEM_H */

Octavian Purdila

unread,
Nov 3, 2015, 3:30:09 PM11/3/15
to
LKL (Linux Kernel Library) is aiming to allow reusing the Linux kernel code
as extensively as possible with minimal effort and reduced maintenance
overhead.

Examples of how LKL can be used are: creating userspace applications
(running on Linux and other operating systems) that can read or write Linux
filesystems or can use the Linux networking stack, creating kernel drivers
for other operating systems that can read Linux filesystems, bootloaders
support for reading/writing Linux filesystems, etc.

With LKL, the kernel code is compiled into an object file that can be
directly linked by applications. The API offered by LKL is based on the
Linux system call interface.

LKL is implemented as an architecture port in arch/lkl. It relies on host
operations defined by the application or a host library (tools/lkl/lib).

The latest LKL version can be found at g...@github.com:lkl/linux.git

FAQ
===

Q: How is LKL different from UML?
A: UML provides a full OS environment (e.g. user/kernel separation, user
processes) and also has requirements (a filesystem, processes, etc.) that
makes it hard to use it for standalone applications. UML also relies
heavily on Linux hosts. On the other hand LKL is designed to be linked
directly with the application and hence does not have user/kernel
separation which makes it easier to use it in standalone applications.

Q: How is LKL different from LibOS?
A: LibOS re-implements high-level kernel APIs for timers, softirqs,
scheduling, sysctl, SLAB/SLUB, etc. LKL behaves like any arch port,
implementing the arch level operations requested by the Linux kernel. LKL
also offers a host interface so that support for multiple hosts can be
easily implemented.


Building LKL the host library and LKL applications
==================================================

% cd tools/lkl
% make

will build LKL as a object file, it will install it in tools/lkl/lib together
with the headers files in tools/lkl/include then will build the host library,
tests and a few of application examples:

* tests/boot - a simple applications that uses LKL and exercises the basic
LKL APIs

* fs2tar - a tool that converts a filesystem image to a tar archive

* cptofs/cpfromfs - a tool that copies files to/from a filesystem image


Supported hosts
===============

Currently LKL supports POSIX and Windows userspace applications. New hosts
can be added relatively easy if the host supports gcc and GNU ld. Previous
versions of LKL supported Windows kernel and Haiku kernel hosts.


Octavian Purdila (28):
asm-generic: atomic64: allow using generic atomic64 on 64bit platforms
kbuild: allow architectures to automatically define kconfig symbols
lkl: architecture skeleton for Linux kernel library
lkl: host interface
lkl: memory handling
lkl: kernel threads support
lkl: interrupt support
lkl: system call interface and application API
lkl: timers, time and delay support
lkl: memory mapped I/O support
lkl: basic kernel console support
init: allow architecture code to overide run_init_process
lkl: initialization and cleanup
lkl: plug in the build system
lkl tools: skeleton for host side library, tests and tools
lkl tools: host lib: add lkl_strerror and lkl_printf
lkl tools: host lib: memory mapped I/O helpers
lkl tools: host lib: virtio devices
lkl tools: host lib: virtio block device
lkl tools: host lib: filesystem helpers
lkl tools: host lib: posix host operations
lkl tools: "boot" test
lkl tools: tool that converts a filesystem image to tar
lkl tools: tool that reads/writes to/from a filesystem image
signal: use CONFIG_X86_32 instead of __i386__
asm-generic: vmlinux.lds.h: allow customized rodata section name
lkl: add support for Windows hosts
lkl tools: add support for Windows host

MAINTAINERS | 6 +
Makefile | 1 +
arch/lkl/.gitignore | 1 +
arch/lkl/Kconfig | 83 ++++++
arch/lkl/Makefile | 39 +++
arch/lkl/auto.conf | 1 +
arch/lkl/defconfig | 35 +++
arch/lkl/include/asm/Kbuild | 77 +++++
arch/lkl/include/asm/bitsperlong.h | 11 +
arch/lkl/include/asm/byteorder.h | 10 +
arch/lkl/include/asm/dma-mapping.h | 6 +
arch/lkl/include/asm/elf.h | 13 +
arch/lkl/include/asm/host_ops.h | 9 +
arch/lkl/include/asm/io.h | 104 +++++++
arch/lkl/include/asm/irq.h | 10 +
arch/lkl/include/asm/mutex.h | 7 +
arch/lkl/include/asm/page.h | 13 +
arch/lkl/include/asm/pgtable.h | 60 ++++
arch/lkl/include/asm/processor.h | 53 ++++
arch/lkl/include/asm/ptrace.h | 23 ++
arch/lkl/include/asm/setup.h | 12 +
arch/lkl/include/asm/thread_info.h | 82 +++++
arch/lkl/include/asm/unistd.h | 93 ++++++
arch/lkl/include/asm/vmlinux.lds.h | 10 +
arch/lkl/include/system/stdarg.h | 1 +
arch/lkl/include/uapi/asm/Kbuild | 38 +++
arch/lkl/include/uapi/asm/bitsperlong.h | 12 +
arch/lkl/include/uapi/asm/host_ops.h | 81 +++++
arch/lkl/include/uapi/asm/irq.h | 37 +++
arch/lkl/include/uapi/asm/sigcontext.h | 15 +
arch/lkl/include/uapi/asm/unistd.h | 256 ++++++++++++++++
arch/lkl/kernel/Makefile | 3 +
arch/lkl/kernel/asm-offsets.c | 1 +
arch/lkl/kernel/console.c | 41 +++
arch/lkl/kernel/irq.c | 131 ++++++++
arch/lkl/kernel/mem.c | 67 +++++
arch/lkl/kernel/misc.c | 57 ++++
arch/lkl/kernel/setup.c | 176 +++++++++++
arch/lkl/kernel/syscalls.c | 213 +++++++++++++
arch/lkl/kernel/threads.c | 235 +++++++++++++++
arch/lkl/kernel/time.c | 125 ++++++++
arch/lkl/kernel/vmlinux.lds.S | 45 +++
arch/lkl/scripts/headers_install.py | 117 ++++++++
include/asm-generic/atomic64.h | 2 +
include/asm-generic/vmlinux.lds.h | 9 +-
include/linux/atomic.h | 2 +-
include/linux/compiler-gcc.h | 4 +
init/main.c | 4 +-
kernel/signal.c | 2 +-
scripts/Makefile | 2 +
scripts/link-vmlinux.sh | 12 +-
tools/lkl/.gitignore | 4 +
tools/lkl/Makefile | 47 +++
tools/lkl/cptofs.c | 467 +++++++++++++++++++++++++++++
tools/lkl/fs2tar.c | 397 ++++++++++++++++++++++++
tools/lkl/include/.gitignore | 1 +
tools/lkl/include/lkl.h | 110 +++++++
tools/lkl/include/lkl_host.h | 44 +++
tools/lkl/lib/.gitignore | 3 +
tools/lkl/lib/fs.c | 218 ++++++++++++++
tools/lkl/lib/iomem.c | 119 ++++++++
tools/lkl/lib/iomem.h | 14 +
tools/lkl/lib/nt-host.c | 227 ++++++++++++++
tools/lkl/lib/posix-host.c | 206 +++++++++++++
tools/lkl/lib/utils.c | 177 +++++++++++
tools/lkl/lib/virtio.c | 365 +++++++++++++++++++++++
tools/lkl/lib/virtio.h | 94 ++++++
tools/lkl/lib/virtio_blk.c | 116 +++++++
tools/lkl/tests/boot.c | 514 ++++++++++++++++++++++++++++++++
tools/lkl/tests/boot.sh | 10 +
70 files changed, 5570 insertions(+), 10 deletions(-)
create mode 100644 arch/lkl/.gitignore
create mode 100644 arch/lkl/Kconfig
create mode 100644 arch/lkl/Makefile
create mode 100644 arch/lkl/auto.conf
create mode 100644 arch/lkl/defconfig
create mode 100644 arch/lkl/include/asm/Kbuild
create mode 100644 arch/lkl/include/asm/bitsperlong.h
create mode 100644 arch/lkl/include/asm/byteorder.h
create mode 100644 arch/lkl/include/asm/dma-mapping.h
create mode 100644 arch/lkl/include/asm/elf.h
create mode 100644 arch/lkl/include/asm/host_ops.h
create mode 100644 arch/lkl/include/asm/io.h
create mode 100644 arch/lkl/include/asm/irq.h
create mode 100644 arch/lkl/include/asm/mutex.h
create mode 100644 arch/lkl/include/asm/page.h
create mode 100644 arch/lkl/include/asm/pgtable.h
create mode 100644 arch/lkl/include/asm/processor.h
create mode 100644 arch/lkl/include/asm/ptrace.h
create mode 100644 arch/lkl/include/asm/setup.h
create mode 100644 arch/lkl/include/asm/thread_info.h
create mode 100644 arch/lkl/include/asm/unistd.h
create mode 100644 arch/lkl/include/asm/vmlinux.lds.h
create mode 100644 arch/lkl/include/system/stdarg.h
create mode 100644 arch/lkl/include/uapi/asm/Kbuild
create mode 100644 arch/lkl/include/uapi/asm/bitsperlong.h
create mode 100644 arch/lkl/include/uapi/asm/host_ops.h
create mode 100644 arch/lkl/include/uapi/asm/irq.h
create mode 100644 arch/lkl/include/uapi/asm/sigcontext.h
create mode 100644 arch/lkl/include/uapi/asm/unistd.h
create mode 100644 arch/lkl/kernel/Makefile
create mode 100644 arch/lkl/kernel/asm-offsets.c
create mode 100644 arch/lkl/kernel/console.c
create mode 100644 arch/lkl/kernel/irq.c
create mode 100644 arch/lkl/kernel/mem.c
create mode 100644 arch/lkl/kernel/misc.c
create mode 100644 arch/lkl/kernel/setup.c
create mode 100644 arch/lkl/kernel/syscalls.c
create mode 100644 arch/lkl/kernel/threads.c
create mode 100644 arch/lkl/kernel/time.c
create mode 100644 arch/lkl/kernel/vmlinux.lds.S
create mode 100755 arch/lkl/scripts/headers_install.py
create mode 100644 tools/lkl/.gitignore
create mode 100644 tools/lkl/Makefile
create mode 100644 tools/lkl/cptofs.c
create mode 100644 tools/lkl/fs2tar.c
create mode 100644 tools/lkl/include/.gitignore
create mode 100644 tools/lkl/include/lkl.h
create mode 100644 tools/lkl/include/lkl_host.h
create mode 100644 tools/lkl/lib/.gitignore
create mode 100644 tools/lkl/lib/fs.c
create mode 100644 tools/lkl/lib/iomem.c
create mode 100644 tools/lkl/lib/iomem.h
create mode 100644 tools/lkl/lib/nt-host.c
create mode 100644 tools/lkl/lib/posix-host.c
create mode 100644 tools/lkl/lib/utils.c
create mode 100644 tools/lkl/lib/virtio.c
create mode 100644 tools/lkl/lib/virtio.h
create mode 100644 tools/lkl/lib/virtio_blk.c
create mode 100644 tools/lkl/tests/boot.c
create mode 100755 tools/lkl/tests/boot.sh

Octavian Purdila

unread,
Nov 3, 2015, 3:30:11 PM11/3/15
to
Some architectures needs customized rodata section names (e.g. lkl for
Windows host). Allow them to set the rodata section name.

Signed-off-by: Octavian Purdila <octavian...@intel.com>
---
arch/lkl/include/asm/vmlinux.lds.h | 5 -----
include/asm-generic/vmlinux.lds.h | 9 +++++++--
2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/arch/lkl/include/asm/vmlinux.lds.h b/arch/lkl/include/asm/vmlinux.lds.h
index 392c94a..7c1a640 100644
--- a/arch/lkl/include/asm/vmlinux.lds.h
+++ b/arch/lkl/include/asm/vmlinux.lds.h
@@ -2,14 +2,9 @@
#define _LKL_VMLINUX_LDS_H

#ifdef __MINGW32__
-#define VMLINUX_SYMBOL(sym) _##sym
#define RODATA_SECTION .rdata
#endif

#include <asm-generic/vmlinux.lds.h>

-#ifndef RODATA_SECTION
-#define RODATA_SECTION .rodata
-#endif
-
#endif
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 1781e54..8bd8b90 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -241,11 +241,16 @@
/*
* Read only Data
*/
+
+#ifndef RODATA_SECTION
+#define RODATA_SECTION .rodata
+#endif
+
#define RO_DATA_SECTION(align) \
. = ALIGN((align)); \
- .rodata : AT(ADDR(.rodata) - LOAD_OFFSET) { \
+ RODATA_SECTION : AT(ADDR(RODATA_SECTION) - LOAD_OFFSET) { \
VMLINUX_SYMBOL(__start_rodata) = .; \
- *(.rodata) *(.rodata.*) \
+ *(RODATA_SECTION) *(RODATA_SECTION.*) \
*(__vermagic) /* Kernel version magic */ \
. = ALIGN(8); \
VMLINUX_SYMBOL(__start___tracepoints_ptrs) = .; \

Octavian Purdila

unread,
Nov 3, 2015, 3:30:12 PM11/3/15
to
The LKL application API is based on the kernel system call interface
in order to offer a stable API to applications. Note that we can't
offer the full Linux system call interface due to LKL limitations such
as lack of virtual memory, signal, user processes, etc.

The host is using the LKL interrupt mechanism (lkl_trigger_irq) to
initiate a system call. The system call is executed in the context of
the init process.

To avoid collisions between the Linux API and the LKL API (e.g. struct
stat, MKNOD, etc.) we use a python script to modify the user headers
and to prefix all of the global symbols (structures, typedefs,
defines) with LKL, lkl, _LKL, _lkl, __LKL or __lkl.

Signed-off-by: Octavian Purdila <octavian...@intel.com>
---
arch/lkl/include/asm/unistd.h | 93 +++++++++++++
arch/lkl/include/uapi/asm/unistd.h | 256 ++++++++++++++++++++++++++++++++++++
arch/lkl/kernel/syscalls.c | 213 ++++++++++++++++++++++++++++++
arch/lkl/scripts/headers_install.py | 117 ++++++++++++++++
4 files changed, 679 insertions(+)
create mode 100644 arch/lkl/include/asm/unistd.h
create mode 100644 arch/lkl/include/uapi/asm/unistd.h
create mode 100644 arch/lkl/kernel/syscalls.c
create mode 100755 arch/lkl/scripts/headers_install.py

diff --git a/arch/lkl/include/asm/unistd.h b/arch/lkl/include/asm/unistd.h
new file mode 100644
index 0000000..73c1e70
--- /dev/null
+++ b/arch/lkl/include/asm/unistd.h
@@ -0,0 +1,93 @@
+#ifndef _ASM_LKL_UNISTD_H
+#define _ASM_LKL_UNISTD_H
+
+#include <uapi/asm/unistd.h>
+
+/*
+ * Unsupported system calls due to lack of support in LKL (e.g. related to
+ * virtual memory, signal, user processes). We also only support 64bit version
+ * of system calls where we have two version to keep the same APi across 32 and
+ * 64 bit hosts.
+ */
+#define __NR_restart_syscall 0
+#define __NR_exit 0
+#define __NR_fork 0
+#define __NR_execve 0
+#define __NR_ptrace 0
+#define __NR_alarm 0
+#define __NR_pause 0
+#define __NR_kill 0
+#define __NR_brk 0
+#define __NR_uselib 0
+#define __NR_swapon 0
+#define __NR_mmap 0
+#define __NR_munmap 0
+#define __NR_swapoff 0
+#define __NR_clone 0
+#define __NR_mprotect 0
+#define __NR_init_module 0
+#define __NR_quotactl 0
+#define __NR_msync 0
+#define __NR_mlock 0
+#define __NR_munlock 0
+#define __NR_mlockall 0
+#define __NR_munlockall 0
+#define __NR_mremap 0
+#define __NR_rt_sigreturn 0
+#define __NR_rt_sigaction 0
+#define __NR_rt_sigprocmask 0
+#define __NR_rt_sigpending 0
+#define __NR_rt_sigtimedwait 0
+#define __NR_rt_sigqueueinfo 0
+#define __NR_rt_sigsuspend 0
+#define __NR_sigaltstack 0
+#define __NR_vfork 0
+#define __NR_mincore 0
+#define __NR_madvise 0
+#define __NR_getdents 0 /* we use the 64 bit counter part instead */
+#define __NR_tkill 0
+#define __NR_exit_group 0
+#define __NR_remap_file_pages 0
+#define __NR_statfs 0 /* we use the 64 bit counter part instead */
+#define __NR_fstatfs 0 /* we use the 64 bit counter part instead */
+#define __NR_fstat 0 /* we use the 64 bit counter part instead */
+#define __NR_fadvise64_64 0
+#define __NR_mbind 0
+#define __NR_get_mempolicy 0
+#define __NR_set_mempolicy 0
+#define __NR_mq_open 0
+#define __NR_mq_unlink 0
+#define __NR_mq_timedsend 0
+#define __NR_mq_timedreceive 0
+#define __NR_mq_0
+#define __NR_mq_getsetattr 0
+#define __NR_kexec_load 0
+#define __NR_migrate_pages 0
+#define __NR_unshare 0
+#define __NR_set_robust_list 0
+#define __NR_get_robust_list 0
+#define __NR_sync_file_range 0
+#define __NR_vmsplice 0
+#define __NR_move_pages 0
+#define __NR_mq_notify 0
+#define __NR_umount2 0
+#define __NR_delete_module 0
+#define __NR_signalfd4 0
+#define __NR_preadv 0 /* we use the 64 bit counter part instead */
+#define __NR_pwritev 0 /* we use the 64 bit counter part instead */
+#define __NR_rt_tgsigqueueinfo 0
+#define __NR_perf_event_open 0
+#define __NR_setns 0
+#define __NR_process_vm_readv 0
+#define __NR_process_vm_writev 0
+#define __NR_kcmp 0
+#define __NR_finit_module 0
+#define __NR_seccomp 0
+#define __NR_memfd_create 0
+#define __NR_bpf 0
+#define __NR_execveat 0
+#define __NR_lseek 0 /* we use the 64 bit counter part instead */
+
+int run_syscalls(void);
+
+#endif
diff --git a/arch/lkl/include/uapi/asm/unistd.h b/arch/lkl/include/uapi/asm/unistd.h
new file mode 100644
index 0000000..68b5423
--- /dev/null
+++ b/arch/lkl/include/uapi/asm/unistd.h
@@ -0,0 +1,256 @@
+#ifndef _ASM_UAPI_LKL_UNISTD_H
+#define _ASM_UAPI_LKL_UNISTD_H
+
+#ifdef __KERNEL__
+#define __NR_ni_syscall 0
+#define __NR_reboot 1
+#endif
+#define __NR_getpid 2
+#define __NR_write 3
+#define __NR_close 4
+#define __NR_unlink 5
+#define __NR_open 6
+#define __NR_poll 7
+#define __NR_read 8
+#define __NR_rename 10
+#define __NR_flock 11
+#define __NR_newfstat 12
+#define __NR_chmod 13
+#define __NR_newlstat 14
+#define __NR_mkdir 15
+#define __NR_rmdir 16
+#define __NR_getdents64 17
+#define __NR_newstat 18
+#define __NR_utimes 19
+#define __NR_utime 20
+#define __NR_nanosleep 21
+#define __NR_mknod 22
+#define __NR_mount 23
+#define __NR_umount 24
+#define __NR_chdir 25
+#define __NR_chroot 26
+#define __NR_getcwd 27
+#define __NR_chown 28
+#define __NR_umask 29
+#define __NR_getuid 30
+#define __NR_getgid 31
+#define __NR_socketcall 32
+#define __NR_ioctl 33
+#define __NR_readlink 34
+#define __NR_access 35
+#define __NR_truncate 36
+#define __NR_sync 37
+#define __NR_creat 38
+#define __NR_llseek 39
+#define __NR_stat64 40
+#define __NR_lstat64 41
+#define __NR_fstat64 42
+#define __NR_fstatat64 43
+#define __NR_statfs64 44
+#define __NR_fstatfs64 45
+#define __NR_listxattr 46
+#define __NR_llistxattr 47
+#define __NR_flistxattr 48
+#define __NR_getxattr 49
+#define __NR_lgetxattr 50
+#define __NR_fgetxattr 51
+#define __NR_setxattr 52
+#define __NR_lsetxattr 53
+#define __NR_fsetxattr 54
+#ifdef __KERNEL__
+#define NR_syscalls 55
+#endif
+
+#define __ARCH_WANT_SYS_UTIME
+#define __ARCH_WANT_SYS_SOCKETCALL
+#define __ARCH_WANT_SYS_LLSEEK
+
+long lkl_syscall(long no, long *params);
+
+#ifndef __KERNEL__
+
+#define LKL_SYSCALL0(_syscall) \
+ static inline \
+ long lkl_sys_##_syscall(void) \
+ { \
+ long params[6]; \
+ return lkl_syscall(__lkl__NR_##_syscall, params); \
+ }
+
+#define LKL_SYSCALL1(_syscall, arg1_t, arg1) \
+ static inline \
+ long lkl_sys_##_syscall(arg1_t arg1) \
+ { \
+ long params[6]; \
+ params[0] = (long)arg1; \
+ return lkl_syscall(__lkl__NR_##_syscall, params); \
+ }
+
+#define LKL_SYSCALL2(_syscall, arg1_t, arg1, arg2_t, arg2) \
+ static inline \
+ long lkl_sys_##_syscall(arg1_t arg1, arg2_t arg2) \
+ { \
+ long params[6]; \
+ params[0] = (long)arg1; \
+ params[1] = (long)arg2; \
+ return lkl_syscall(__lkl__NR_##_syscall, params); \
+ }
+
+#define LKL_SYSCALL3(_syscall, arg1_t, arg1, arg2_t, arg2, arg3_t, arg3) \
+ static inline \
+ long lkl_sys_##_syscall(arg1_t arg1, arg2_t arg2, arg3_t arg3) \
+ { \
+ long params[6]; \
+ params[0] = (long)arg1; \
+ params[1] = (long)arg2; \
+ params[2] = (long)arg3; \
+ return lkl_syscall(__lkl__NR_##_syscall, params); \
+ }
+
+#define LKL_SYSCALL4(_syscall, arg1_t, arg1, arg2_t, arg2, arg3_t, arg3, \
+ arg4_t, arg4) \
+ static inline \
+ long lkl_sys_##_syscall(arg1_t arg1, arg2_t arg2, arg3_t arg3, \
+ arg4_t arg4) \
+ { \
+ long params[6]; \
+ params[0] = (long)arg1; \
+ params[1] = (long)arg2; \
+ params[2] = (long)arg3; \
+ params[3] = (long)arg4; \
+ return lkl_syscall(__lkl__NR_##_syscall, params); \
+ }
+
+#define LKL_SYSCALL5(_syscall, arg1_t, arg1, arg2_t, arg2, arg3_t, arg3, \
+ arg4_t, arg4, arg5_t, arg5) \
+ static inline \
+ long lkl_sys_##_syscall(arg1_t arg1, arg2_t arg2, arg3_t arg3, \
+ arg4_t arg4, arg5_t arg5) \
+ { \
+ long params[6]; \
+ params[0] = (long)arg1; \
+ params[1] = (long)arg2; \
+ params[2] = (long)arg3; \
+ params[3] = (long)arg4; \
+ params[4] = (long)arg5; \
+ return lkl_syscall(__lkl__NR_##_syscall, params); \
+ }
+
+#define LKL_SYSCALL6(_syscall, arg1_t, arg1, arg2_t, arg2, arg3_t, arg3, \
+ arg4_t, arg4, arg5_t, arg5, arg6_t, arg6) \
+ static inline \
+ long lkl_sys_##_syscall(arg1_t arg1, arg2_t arg2, arg3_t arg3, \
+ arg4_t arg4, arg5_t arg5, arg6_t arg6) \
+ { \
+ long params[6]; \
+ params[0] = (long)arg1; \
+ params[1] = (long)arg2; \
+ params[2] = (long)arg3; \
+ params[3] = (long)arg4; \
+ params[4] = (long)arg5; \
+ params[5] = (long)arg6; \
+ return lkl_syscall(__lkl__NR_##_syscall, params); \
+ }
+
+#include <autoconf.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/time.h>
+#include <linux/utime.h>
+#include <asm/stat.h>
+#include <asm/statfs.h>
+#define __KERNEL__ /* to pull in S_ definitions */
+#include <linux/stat.h>
+#undef __KERNEL__
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <asm/irq.h>
+#include <linux/kdev_t.h>
+
+/* these types are not exported to userspace so we have to do it here */
+typedef unsigned short lkl_umode_t;
+
+struct lkl_dirent64 {
+ unsigned long long d_ino;
+ long long d_off;
+ unsigned short d_reclen;
+ unsigned char d_type;
+ char d_name[0];
+};
+
+#define LKL_DT_UNKNOWN 0
+#define LKL_DT_FIFO 1
+#define LKL_DT_CHR 2
+#define LKL_DT_DIR 4
+#define LKL_DT_BLK 6
+#define LKL_DT_REG 8
+#define LKL_DT_LNK 10
+#define LKL_DT_SOCK 12
+#define LKL_DT_WHT 14
+
+LKL_SYSCALL0(getpid);
+LKL_SYSCALL3(write, unsigned int, fd, const char *, buf,
+ __lkl__kernel_size_t, count);
+LKL_SYSCALL1(close, unsigned int, fd);
+LKL_SYSCALL1(unlink, const char *, pathname);
+LKL_SYSCALL3(open, const char *, filename, int, flags, lkl_umode_t, mode);
+LKL_SYSCALL2(creat, const char *, filename, lkl_umode_t, mode);
+LKL_SYSCALL3(poll, struct lkl_pollfd *, ufds, unsigned int, nfds, int, timeout);
+LKL_SYSCALL3(read, unsigned int, fd, char *, buf, __lkl__kernel_size_t, count);
+LKL_SYSCALL2(rename, const char *, oldname, const char *, newname);
+LKL_SYSCALL2(flock, unsigned int, fd, unsigned int, cmd);
+LKL_SYSCALL2(chmod, const char *, filename, lkl_umode_t, mode);
+
+LKL_SYSCALL2(mkdir, const char *, pathname, lkl_umode_t, mode);
+LKL_SYSCALL1(rmdir, const char *, pathname);
+LKL_SYSCALL3(getdents64, unsigned int, fd, void *, dirent, unsigned int, size);
+LKL_SYSCALL2(utimes, const char *, filename, struct lkl_timeval *, utimes);
+LKL_SYSCALL2(nanosleep, struct lkl_timespec *, rqtp,
+ struct lkl_timespec *, rmtp);
+LKL_SYSCALL3(mknod, const char *, filename, lkl_umode_t, mode,
+ unsigned int, dev);
+LKL_SYSCALL5(mount, const char *, dev_name, const char *, dir_name,
+ const char *, type, unsigned long, flags, void *, data);
+LKL_SYSCALL2(umount, const char *, name, int, flags);
+LKL_SYSCALL1(chdir, const char *, filename);
+LKL_SYSCALL1(chroot, const char *, filename);
+LKL_SYSCALL2(getcwd, char *, buf, unsigned long, size);
+LKL_SYSCALL2(utime, const char *, filename, const struct lkl_utimbuf *, buf);
+LKL_SYSCALL3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg);
+LKL_SYSCALL1(umask, int, mask);
+LKL_SYSCALL0(getuid);
+LKL_SYSCALL0(getgid);
+LKL_SYSCALL2(access, const char *, filename, int, mode);
+LKL_SYSCALL2(truncate, const char *, path, long, length);
+LKL_SYSCALL0(sync);
+LKL_SYSCALL5(llseek, unsigned int, fd, unsigned long, offset_high,
+ unsigned long, offset_low, __lkl__kernel_loff_t *, result,
+ unsigned int, whence);
+LKL_SYSCALL2(fstat64, unsigned int, fd, struct lkl_stat64 *, statbuf);
+LKL_SYSCALL4(fstatat64, unsigned int, dfd, const char *, filname,
+ struct lkl_stat64 *, statbuf, int, flag);
+LKL_SYSCALL2(stat64, const char *, filename, struct lkl_stat64 *, statbuf);
+LKL_SYSCALL2(lstat64, const char *, filename, struct lkl_stat64 *, statbuf);
+LKL_SYSCALL2(statfs64, const char *, path, struct lkl_statfs64 *, buf);
+LKL_SYSCALL3(readlink, const char *, path, char *, buf, int, bufsiz);
+LKL_SYSCALL3(listxattr, const char *, path, char *, list, int, bufsiz);
+LKL_SYSCALL3(llistxattr, const char *, path, char *, list, int, bufsiz);
+LKL_SYSCALL3(flistxattr, int, fd, char *, list, int, bufsiz);
+LKL_SYSCALL4(getxattr, const char *, path, const char *, name, void *, value,
+ __lkl__kernel_size_t, size);
+LKL_SYSCALL4(lgetxattr, const char *, path, const char *, name, void *, value,
+ __lkl__kernel_size_t, size);
+LKL_SYSCALL4(fgetxattr, int, fd, const char *, name, void *, value,
+ __lkl__kernel_size_t, size);
+LKL_SYSCALL5(setxattr, const char *, path, const char *, name,
+ const void *, value, __lkl__kernel_size_t, size, int, flags);
+LKL_SYSCALL5(lsetxattr, const char *, path, const char *, name,
+ const void *, value, __lkl__kernel_size_t, size, int, flags);
+LKL_SYSCALL5(fsetxattr, int, fd, const char *, name, const void *, value,
+ __lkl__kernel_size_t, size, int, flags);
+
+long lkl_sys_halt(void);
+
+#endif /* __KERNEL__ */
+
+#endif /* _ASM_UAPI_LKL_UNISTD_H */
diff --git a/arch/lkl/kernel/syscalls.c b/arch/lkl/kernel/syscalls.c
new file mode 100644
index 0000000..48b1296
--- /dev/null
+++ b/arch/lkl/kernel/syscalls.c
@@ -0,0 +1,213 @@
+#include <linux/syscalls.h>
+#include <linux/stat.h>
+#include <linux/irq.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/jhash.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/syscalls.h>
+#include <linux/net.h>
+#include <linux/task_work.h>
+#include <asm/unistd.h>
+#include <asm/host_ops.h>
+
+typedef long (*syscall_handler_t)(long arg1, ...);
+
+syscall_handler_t syscall_table[NR_syscalls];
+
+static struct syscall_queue {
+ struct list_head list;
+ wait_queue_head_t wqh;
+} syscall_queue;
+
+struct syscall {
+ long no, *params, ret;
+ void *sem;
+ struct list_head lh;
+};
+
+static struct syscall *dequeue_syscall(struct syscall_queue *sq)
+{
+ struct syscall *s = NULL;
+
+ if (!list_empty(&sq->list)) {
+ s = list_first_entry(&sq->list, typeof(*s), lh);
+ list_del(&s->lh);
+ }
+
+ return s;
+}
+
+static long run_syscall(struct syscall *s)
+{
+ int ret;
+
+ if (s->no < 0 || s->no >= NR_syscalls || !syscall_table[s->no])
+ ret = -ENOSYS;
+ else
+ ret = syscall_table[s->no](s->params[0], s->params[1],
+ s->params[2], s->params[3],
+ s->params[4], s->params[5]);
+ s->ret = ret;
+
+ task_work_run();
+
+ if (s->sem)
+ lkl_ops->sem_up(s->sem);
+ return ret;
+}
+
+int run_syscalls(void)
+{
+ struct syscall_queue *sq = &syscall_queue;
+ struct syscall *s;
+
+ current->flags &= ~PF_KTHREAD;
+
+ snprintf(current->comm, sizeof(current->comm), "init");
+
+ while (1) {
+ wait_event(sq->wqh, (s = dequeue_syscall(sq)) != NULL);
+
+ if (s->no == __NR_reboot)
+ break;
+
+ run_syscall(s);
+ }
+
+ s->ret = 0;
+ lkl_ops->sem_up(s->sem);
+
+ return 0;
+}
+
+static irqreturn_t syscall_irq_handler(int irq, void *dev_id)
+{
+ struct pt_regs *regs = get_irq_regs();
+ struct syscall *s = regs->irq_data;
+
+ list_add_tail(&s->lh, &syscall_queue.list);
+ wake_up(&syscall_queue.wqh);
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction syscall_irqaction = {
+ .handler = syscall_irq_handler,
+ .flags = IRQF_NOBALANCING,
+ .dev_id = &syscall_irqaction,
+ .name = "syscall"
+};
+
+static int syscall_irq;
+
+long lkl_syscall(long no, long *params)
+{
+ struct syscall s;
+
+ s.no = no;
+ s.params = params;
+
+ s.sem = lkl_ops->sem_alloc(0);
+ if (!s.sem)
+ return -ENOMEM;
+
+ lkl_trigger_irq(syscall_irq, &s);
+
+ lkl_ops->sem_down(s.sem);
+ lkl_ops->sem_free(s.sem);
+
+ return s.ret;
+}
+
+asmlinkage
+ssize_t sys_lkl_pwrite64(unsigned int fd, const char *buf, size_t count,
+ off_t pos_hi, off_t pos_lo)
+{
+ return sys_pwrite64(fd, buf, count, ((loff_t)pos_hi << 32) + pos_lo);
+}
+
+asmlinkage
+ssize_t sys_lkl_pread64(unsigned int fd, char *buf, size_t count,
+ off_t pos_hi, off_t pos_lo)
+{
+ return sys_pread64(fd, buf, count, ((loff_t)pos_hi << 32) + pos_lo);
+}
+
+#define INIT_STE(x) syscall_table[__NR_##x] = (syscall_handler_t)sys_##x
+
+void init_syscall_table(void)
+{
+ int i;
+
+ for (i = 0; i < NR_syscalls; i++)
+ syscall_table[i] = (syscall_handler_t)sys_ni_syscall;
+
+ INIT_STE(sync);
+ INIT_STE(reboot);
+ INIT_STE(write);
+ INIT_STE(close);
+ INIT_STE(unlink);
+ INIT_STE(open);
+ INIT_STE(poll);
+ INIT_STE(read);
+ INIT_STE(rename);
+ INIT_STE(chmod);
+ INIT_STE(llseek);
+ INIT_STE(lstat64);
+ INIT_STE(fstat64);
+ INIT_STE(fstatat64);
+ INIT_STE(stat64);
+ INIT_STE(mkdir);
+ INIT_STE(rmdir);
+ INIT_STE(getdents64);
+ INIT_STE(utimes);
+ INIT_STE(utime);
+ INIT_STE(nanosleep);
+ INIT_STE(mknod);
+ INIT_STE(mount);
+ INIT_STE(umount);
+ INIT_STE(chdir);
+ INIT_STE(statfs64);
+ INIT_STE(chroot);
+ INIT_STE(getcwd);
+ INIT_STE(chown);
+ INIT_STE(umask);
+ INIT_STE(getuid);
+ INIT_STE(getgid);
+#ifdef CONFIG_NET
+ INIT_STE(socketcall);
+#endif
+ INIT_STE(ioctl);
+ INIT_STE(access);
+ INIT_STE(truncate);
+ INIT_STE(getpid);
+ INIT_STE(creat);
+ INIT_STE(llseek);
+ INIT_STE(readlink);
+ INIT_STE(listxattr);
+ INIT_STE(llistxattr);
+ INIT_STE(flistxattr);
+ INIT_STE(getxattr);
+ INIT_STE(lgetxattr);
+ INIT_STE(fgetxattr);
+ INIT_STE(setxattr);
+ INIT_STE(lsetxattr);
+ INIT_STE(fsetxattr);
+}
+
+int __init syscall_init(void)
+{
+ init_syscall_table();
+
+ INIT_LIST_HEAD(&syscall_queue.list);
+ init_waitqueue_head(&syscall_queue.wqh);
+
+ syscall_irq = lkl_get_free_irq("syscall");
+ setup_irq(syscall_irq, &syscall_irqaction);
+
+ pr_info("lkl: syscall interface initialized\n");
+ return 0;
+}
+late_initcall(syscall_init);
diff --git a/arch/lkl/scripts/headers_install.py b/arch/lkl/scripts/headers_install.py
new file mode 100755
index 0000000..eb69a5e
--- /dev/null
+++ b/arch/lkl/scripts/headers_install.py
@@ -0,0 +1,117 @@
+#!/usr/bin/python
+import re, os, sys, argparse, multiprocessing
+
+header_paths = [ "include/uapi/", "arch/lkl/include/uapi/",
+ "arch/lkl/include/generated/uapi/", "include/generated/" ]
+
+headers = set()
+
+def find_headers(path):
+ headers.add(path)
+ f = open(path)
+ for l in f.readlines():
+ m = re.search("#include <(.*)>", l)
+ try:
+ i = m.group(1)
+ for p in header_paths:
+ if os.access(p + i, os.R_OK):
+ if p + i not in headers:
+ headers.add(p + i)
+ find_headers(p + i)
+ except:
+ pass
+ f.close()
+
+def has_lkl_prefix(w):
+ return w.startswith("lkl") or w.startswith("_lkl") or w.startswith("LKL") or \
+ w.startswith("_LKL")
+
+def find_symbols(regexp, store):
+ for h in headers:
+ f = open(h)
+ for l in f.readlines():
+ m = re.search(regexp, l)
+ try:
+ e = m.group(1)
+ if not has_lkl_prefix(e):
+ store.add(e)
+ except:
+ pass
+ f.close()
+
+def find_ml_symbols(regexp, store):
+ for h in headers:
+ for i in re.finditer(regexp, open(h).read(), re.MULTILINE|re.DOTALL):
+ for j in i.groups():
+ store.add(j)
+
+def lkl_prefix(w):
+ r = ""
+
+ if w.startswith("__"):
+ r = "__"
+ elif w.startswith("_"):
+ r = "_"
+
+ if w.isupper():
+ r += "LKL"
+ else:
+ r += "lkl"
+
+ if not w.startswith("_"):
+ r += "_"
+
+ r += w
+
+ return r
+
+def replace(h):
+ content = open(h).read()
+ content = re.sub("(#[ \t]*include[ \t]<)(.*>)", "\\1lkl/\\2", content,
+ flags = re.MULTILINE)
+ for d in defines:
+ search_str = "([^_a-zA-Z0-9]+)" + d + "([^_a-zA-Z0-9]+)"
+ replace_str = "\\1" + lkl_prefix(d) + "\\2"
+ content = re.sub(search_str, replace_str, content, flags = re.MULTILINE)
+ for s in structs:
+ search_str = "([^_a-zA-Z0-9]*struct\s+)" + s + "([^_a-zA-Z0-9]+)"
+ replace_str = "\\1" + lkl_prefix(s) + "\\2"
+ content = re.sub(search_str, replace_str, content, flags = re.MULTILINE)
+ open(h, 'w').write(content)
+
+parser = argparse.ArgumentParser(description='install lkl headers')
+parser.add_argument('path', help='path to install to', )
+parser.add_argument('-j', '--jobs', help='number of parallel jobs', default=1, type=int)
+args = parser.parse_args()
+
+find_headers("arch/lkl/include/uapi/asm/unistd.h")
+headers.add("arch/lkl/include/uapi/asm/host_ops.h")
+
+defines = set()
+structs = set()
+
+find_symbols("#[ \t]*define[ \t]*([_a-zA-Z]+[_a-zA-Z0-9]*)[^_a-zA-Z0-9]", defines)
+find_symbols("typedef.*\s+([_a-zA-Z]+[_a-zA-Z0-9]*)\s*;", defines)
+find_ml_symbols("typedef\s+struct\s*\{.*\}\s*([_a-zA-Z]+[_a-zA-Z0-9]*)\s*;", defines)
+find_symbols("struct\s+([_a-zA-Z]+[_a-zA-Z0-9]*)\s*\{", structs)
+
+def process_header(h):
+ dir = os.path.dirname(h)
+ out_dir = args.path + "/" + re.sub("(arch/lkl/include/uapi/|arch/lkl/include/generated/uapi/|include/uapi/|include/generated/uapi/|include/generated)(.*)", "lkl/\\2", dir)
+ try:
+ os.makedirs(out_dir)
+ except:
+ pass
+ print " INSTALL\t%s" % (out_dir + "/" + os.path.basename(h))
+ os.system("scripts/headers_install.sh %s %s %s" % (out_dir, dir,
+ os.path.basename(h)))
+ replace(out_dir + "/" + os.path.basename(h))
+
+p = multiprocessing.Pool(args.jobs)
+try:
+ p.map_async(process_header, headers).wait(999999)
+ p.close()
+except:
+ p.terminate()
+finally:
+ p.join()

Octavian Purdila

unread,
Nov 3, 2015, 3:30:12 PM11/3/15
to
Add the lkl_start_kernel and lkl_sys_halt APIs that start and
respectively stops the Linux kernel.

lkl_start_kernel creates a separate threads that will run the initial
and idle kernel thread. It waits for the kernel to complete
initialization before returning, to avoid races with system calls
issues by the host application.

During the setup phase, we create "/init" in initial ramfs root
filesystem to avoid mounting the "real" rootfs since ramfs is good
enough for now.

lkl_stop_kernel will shutdown the kernel, terminate all threads and
free all host resources used by the kernel before returning.

This patch also introduces idle CPU handling since it is closely
related to the shutdown process. A host semaphore is used to wait for
new interrupts when the kernel switches the CPU to idle to avoid
wasting host CPU cycles. When the kernel is shutdown we terminate the
idle thread at the first CPU idle event.

Signed-off-by: Octavian Purdila <octavian...@intel.com>
---
arch/lkl/include/asm/setup.h | 12 +++
arch/lkl/kernel/setup.c | 176 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 188 insertions(+)
create mode 100644 arch/lkl/include/asm/setup.h
create mode 100644 arch/lkl/kernel/setup.c

diff --git a/arch/lkl/include/asm/setup.h b/arch/lkl/include/asm/setup.h
new file mode 100644
index 0000000..b82cdbf
--- /dev/null
+++ b/arch/lkl/include/asm/setup.h
@@ -0,0 +1,12 @@
+#ifndef _ASM_LKL_SETUP_H
+#define _ASM_LKL_SETUP_H
+
+#define COMMAND_LINE_SIZE 4096
+
+#ifndef __ASSEMBLY__
+#define ARCH_RUN_INIT_PROCESS
+int run_init_process(const char *init_filename);
+void wakeup_cpu(void);
+#endif
+
+#endif
diff --git a/arch/lkl/kernel/setup.c b/arch/lkl/kernel/setup.c
new file mode 100644
index 0000000..aad2ad7
--- /dev/null
+++ b/arch/lkl/kernel/setup.c
@@ -0,0 +1,176 @@
+#include <linux/init.h>
+#include <linux/init_task.h>
+#include <linux/reboot.h>
+#include <linux/fs.h>
+#include <linux/start_kernel.h>
+#include <linux/syscalls.h>
+#include <asm/host_ops.h>
+#include <asm/irq.h>
+#include <asm/unistd.h>
+
+struct lkl_host_operations *lkl_ops;
+static char cmd_line[COMMAND_LINE_SIZE];
+static void *idle_sem;
+static void *init_sem;
+static void *halt_sem;
+static bool halt;
+void (*pm_power_off)(void) = NULL;
+static unsigned long mem_size;
+
+long lkl_panic_blink(int state)
+{
+ lkl_ops->panic();
+ return 0;
+}
+
+void __init setup_arch(char **cl)
+{
+ *cl = cmd_line;
+ panic_blink = lkl_panic_blink;
+ bootmem_init(mem_size);
+}
+
+int run_init_process(const char *init_filename)
+{
+ lkl_ops->sem_up(init_sem);
+
+ run_syscalls();
+
+ kernel_halt();
+
+ /* We want to kill init without panic()ing */
+ init_pid_ns.child_reaper = 0;
+ do_exit(0);
+
+ return 0;
+}
+
+static void __init lkl_run_kernel(void *arg)
+{
+ start_kernel();
+}
+
+int __init lkl_start_kernel(struct lkl_host_operations *ops,
+ unsigned long _mem_size,
+ const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ lkl_ops = ops;
+ mem_size = _mem_size;
+
+ va_start(ap, fmt);
+ ret = vsnprintf(boot_command_line, COMMAND_LINE_SIZE, fmt, ap);
+ va_end(ap);
+
+ if (ops->virtio_devices)
+ strncpy(boot_command_line + ret, ops->virtio_devices,
+ COMMAND_LINE_SIZE - ret);
+
+ memcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
+
+ ret = threads_init();
+ if (ret)
+ return ret;
+
+ init_sem = lkl_ops->sem_alloc(0);
+ if (!init_sem)
+ return -ENOMEM;
+
+ idle_sem = lkl_ops->sem_alloc(0);
+ if (!idle_sem) {
+ ret = -ENOMEM;
+ goto out_free_init_sem;
+ }
+
+ ret = lkl_ops->thread_create(lkl_run_kernel, NULL);
+ if (ret) {
+ ret = -ENOMEM;
+ goto out_free_idle_sem;
+ }
+
+ lkl_ops->sem_down(init_sem);
+
+ return 0;
+
+out_free_idle_sem:
+ lkl_ops->sem_free(idle_sem);
+
+out_free_init_sem:
+ lkl_ops->sem_free(init_sem);
+
+ return ret;
+}
+
+void machine_halt(void)
+{
+ halt = true;
+}
+
+void machine_power_off(void)
+{
+ machine_halt();
+}
+
+void machine_restart(char *unused)
+{
+ machine_halt();
+}
+
+long lkl_sys_halt(void)
+{
+ long err;
+ long params[6] = { 0, };
+
+ halt_sem = lkl_ops->sem_alloc(0);
+ if (!halt_sem)
+ return -ENOMEM;
+
+ err = lkl_syscall(__NR_reboot, params);
+ if (err < 0) {
+ lkl_ops->sem_free(halt_sem);
+ return err;
+ }
+
+ lkl_ops->sem_down(halt_sem);
+
+ lkl_ops->sem_free(halt_sem);
+ lkl_ops->sem_free(idle_sem);
+ lkl_ops->sem_free(init_sem);
+
+ return 0;
+}
+
+void arch_cpu_idle(void)
+{
+ if (halt) {
+ threads_cleanup();
+ free_IRQ();
+ free_mem();
+ lkl_ops->sem_up(halt_sem);
+ lkl_ops->thread_exit();
+ }
+
+ lkl_ops->sem_down(idle_sem);
+
+ local_irq_enable();
+}
+
+void wakeup_cpu(void)
+{
+ lkl_ops->sem_up(idle_sem);
+}
+
+/* skip mounting the "real" rootfs. ramfs is good enough. */
+static int __init fs_setup(void)
+{
+ int fd;
+
+ fd = sys_open("/init", O_CREAT, 0600);
+ WARN_ON(fd < 0);
+ sys_close(fd);
+
+ return 0;
+}
+late_initcall(fs_setup);

Octavian Purdila

unread,
Nov 3, 2015, 3:30:12 PM11/3/15
to
Signed-off-by: Octavian Purdila <octavian...@intel.com>
---
kernel/signal.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/kernel/signal.c b/kernel/signal.c
index 0f6bbbe..0367131 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1137,7 +1137,7 @@ static void print_fatal_signal(int signr)
struct pt_regs *regs = signal_pt_regs();
printk(KERN_INFO "potentially unexpected fatal signal %d.\n", signr);

-#if defined(__i386__) && !defined(__arch_um__)
+#ifdef CONFIG_X86_32
printk(KERN_INFO "code at %08lx: ", regs->ip);
{
int i;

Octavian Purdila

unread,
Nov 3, 2015, 3:30:13 PM11/3/15
to
Add basic utility functions for getting a string from a kernel error
code and a fprintf like function that uses the host print
operation. The latter is useful for informing the user about errors
that occur in the host library.

Signed-off-by: Octavian Purdila <octavian...@intel.com>
---
tools/lkl/include/lkl.h | 8 ++
tools/lkl/include/lkl_host.h | 7 ++
tools/lkl/lib/utils.c | 177 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 192 insertions(+)
create mode 100644 tools/lkl/lib/utils.c

diff --git a/tools/lkl/include/lkl.h b/tools/lkl/include/lkl.h
index 2de5ed4..958614d 100644
--- a/tools/lkl/include/lkl.h
+++ b/tools/lkl/include/lkl.h
@@ -12,4 +12,12 @@ static inline long lkl_sys_lseek(unsigned int fd, __lkl__kernel_loff_t off,
return lkl_sys_llseek(fd, off >> 32, off & 0xffffffff, res, whence);
}

+/**
+ * lkl_strerror - returns a string describing the given error code
+ *
+ * @err - error code
+ * @returns - string for the given error code
+ */
+const char *lkl_strerror(int err);
+
#endif
diff --git a/tools/lkl/include/lkl_host.h b/tools/lkl/include/lkl_host.h
index 8ee9ba0..9f1c270 100644
--- a/tools/lkl/include/lkl_host.h
+++ b/tools/lkl/include/lkl_host.h
@@ -6,4 +6,11 @@

extern struct lkl_host_operations lkl_host_ops;

+/**
+ * lkl_printf - print a message via the host print operation
+ *
+ * @fmt - printf like format string
+ */
+int lkl_printf(const char *fmt, ...);
+
#endif
diff --git a/tools/lkl/lib/utils.c b/tools/lkl/lib/utils.c
new file mode 100644
index 0000000..f8b676f
--- /dev/null
+++ b/tools/lkl/lib/utils.c
@@ -0,0 +1,177 @@
+#include <stdarg.h>
+#include <stdio.h>
+#include <lkl_host.h>
+
+static const char * const lkl_err_strings[] = {
+ "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",
+ "Resource deadlock would occur",
+ "File name too long",
+ "No record locks available",
+ "Invalid system call number",
+ "Directory not empty",
+ "Too many symbolic links encountered",
+ "Bad error code", /* EWOULDBLOCK is EAGAIN */
+ "No message of desired type",
+ "Identifier removed",
+ "Channel number out of range",
+ "Level 2 not synchronized",
+ "Level 3 halted",
+ "Level 3 reset",
+ "Link number out of range",
+ "Protocol driver not attached",
+ "No CSI structure available",
+ "Level 2 halted",
+ "Invalid exchange",
+ "Invalid request descriptor",
+ "Exchange full",
+ "No anode",
+ "Invalid request code",
+ "Invalid slot",
+ "Bad error code", /* EDEADLOCK is EDEADLK */
+ "Bad font file format",
+ "Device not a stream",
+ "No data available",
+ "Timer expired",
+ "Out of streams resources",
+ "Machine is not on the network",
+ "Package not installed",
+ "Object is remote",
+ "Link has been severed",
+ "Advertise error",
+ "Srmount error",
+ "Communication error on send",
+ "Protocol error",
+ "Multihop attempted",
+ "RFS specific error",
+ "Not a data message",
+ "Value too large for defined data type",
+ "Name not unique on network",
+ "File descriptor in bad state",
+ "Remote address changed",
+ "Can not access a needed shared library",
+ "Accessing a corrupted shared library",
+ ".lib section in a.out corrupted",
+ "Attempting to link in too many shared libraries",
+ "Cannot exec a shared library directly",
+ "Illegal byte sequence",
+ "Interrupted system call should be restarted",
+ "Streams pipe error",
+ "Too many users",
+ "Socket operation on non-socket",
+ "Destination address required",
+ "Message too long",
+ "Protocol wrong type for socket",
+ "Protocol not available",
+ "Protocol not supported",
+ "Socket type not supported",
+ "Operation not supported on transport endpoint",
+ "Protocol family not supported",
+ "Address family not supported by protocol",
+ "Address already in use",
+ "Cannot assign requested address",
+ "Network is down",
+ "Network is unreachable",
+ "Network dropped connection because of reset",
+ "Software caused connection abort",
+ "Connection reset by peer",
+ "No buffer space available",
+ "Transport endpoint is already connected",
+ "Transport endpoint is not connected",
+ "Cannot send after transport endpoint shutdown",
+ "Too many references: cannot splice",
+ "Connection timed out",
+ "Connection refused",
+ "Host is down",
+ "No route to host",
+ "Operation already in progress",
+ "Operation now in progress",
+ "Stale file handle",
+ "Structure needs cleaning",
+ "Not a XENIX named type file",
+ "No XENIX semaphores available",
+ "Is a named type file",
+ "Remote I/O error",
+ "Quota exceeded",
+ "No medium found",
+ "Wrong medium type",
+ "Operation Canceled",
+ "Required key not available",
+ "Key has expired",
+ "Key has been revoked",
+ "Key was rejected by service",
+ "Owner died",
+ "State not recoverable",
+ "Operation not possible due to RF-kill",
+ "Memory page has hardware error",
+};
+
+const char *lkl_strerror(int err)
+{
+ if (err < 0)
+ err = -err;
+
+ if (err >= sizeof(lkl_err_strings) / sizeof(const char *))
+ return "Bad error code";
+
+ return lkl_err_strings[err];
+}
+
+int lkl_printf(const char *fmt, ...)
+{
+ char *buffer;
+ va_list args, copy;
+ int n;
+
+ va_start(args, fmt);
+ va_copy(copy, args);
+ n = vsnprintf(NULL, 0, fmt, copy);
+ va_end(copy);
+
+ buffer = lkl_host_ops.mem_alloc(n + 1);
+ if (!buffer) {
+ va_end(args);
+ return 0;
+ }
+ vsnprintf(buffer, n + 1, fmt, args);
+ va_end(args);
+
+ lkl_host_ops.print(buffer, n);
+
+ lkl_host_ops.mem_free(buffer);
+
+ return n;
+}

Octavian Purdila

unread,
Nov 3, 2015, 3:30:13 PM11/3/15
to
Basic Makefiles for building LKL. Add a new architecture specific
target for installing the resulting library files and headers.

Signed-off-by: Octavian Purdila <octavian...@intel.com>
---
arch/lkl/Makefile | 35 +++++++++++++++++++++++++++++++++++
arch/lkl/kernel/Makefile | 3 +++
2 files changed, 38 insertions(+)
create mode 100644 arch/lkl/Makefile
create mode 100644 arch/lkl/kernel/Makefile

diff --git a/arch/lkl/Makefile b/arch/lkl/Makefile
new file mode 100644
index 0000000..7545830
--- /dev/null
+++ b/arch/lkl/Makefile
@@ -0,0 +1,35 @@
+include arch/lkl/auto.conf
+
+KBUILD_CFLAGS += -fno-builtin
+
+ifeq ($(OUTPUT_FORMAT),elf64-x86-64)
+KBUILD_CFLAGS += -fPIC
+endif
+
+LDFLAGS_vmlinux += -r
+LKL_ENTRY_POINTS := lkl_start_kernel lkl_sys_halt lkl_syscall lkl_trigger_irq \
+ lkl_get_free_irq lkl_put_irq
+
+core-y += arch/lkl/kernel/
+
+all: lkl.o
+
+lkl.o: vmlinux
+ $(OBJCOPY) $(foreach sym,$(LKL_ENTRY_POINTS),-G$(prefix)$(sym)) vmlinux lkl.o
+
+install: lkl.o __headers
+ @echo " INSTALL\t$(INSTALL_PATH)/lib/lkl.o"
+ @cp lkl.o $(INSTALL_PATH)/lib/
+ @arch/lkl/scripts/headers_install.py \
+ $(subst -j,-j$(shell nproc),$(findstring -j,$(MAKEFLAGS))) \
+ $(INSTALL_PATH)/include
+
+archclean:
+ $(Q)$(MAKE) $(clean)=$(boot)
+
+define archhelp
+ echo ' install - Install library and headers to INSTALL_PATH/{lib,include}'
+endef
+
+
+
diff --git a/arch/lkl/kernel/Makefile b/arch/lkl/kernel/Makefile
new file mode 100644
index 0000000..47036c0
--- /dev/null
+++ b/arch/lkl/kernel/Makefile
@@ -0,0 +1,3 @@
+extra-y := vmlinux.lds
+
+obj-y = setup.o threads.o irq.o time.o syscalls.o misc.o mem.o console.o

Octavian Purdila

unread,
Nov 3, 2015, 3:30:14 PM11/3/15
to
Implement LKL host operations for POSIX hosts.

Signed-off-by: Octavian Purdila <octavian...@intel.com>
---
tools/lkl/Makefile | 6 ++
tools/lkl/lib/posix-host.c | 206 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 212 insertions(+)
create mode 100644 tools/lkl/lib/posix-host.c

diff --git a/tools/lkl/Makefile b/tools/lkl/Makefile
index b13472b..cf97d27 100644
--- a/tools/lkl/Makefile
+++ b/tools/lkl/Makefile
@@ -3,9 +3,15 @@ CFLAGS := -Iinclude -Wall -g
ifdef CROSS_COMPILE
CC=$(CROSS_COMPILE)gcc
AR=$(CROSS_COMPILE)ar
+LD=$(CROSS_COMPILE)ld
endif

lib_source = $(filter-out %-host.c,$(wildcard lib/*.c))
+ifneq (,$(filter $(shell $(LD) -r -print-output-format),elf64-x86-64 elf32-i386))
+lib_source += lib/posix-host.c
+LDFLAGS += -lpthread -lrt
+endif
+
lib_objs = $(patsubst %.c,%.o, $(lib_source)) lib/lkl.o

all: lib/liblkl.a
diff --git a/tools/lkl/lib/posix-host.c b/tools/lkl/lib/posix-host.c
new file mode 100644
index 0000000..4bc1de7
--- /dev/null
+++ b/tools/lkl/lib/posix-host.c
@@ -0,0 +1,206 @@
+#include <pthread.h>
+#include <malloc.h>
+#include <sys/time.h>
+#include <time.h>
+#include <signal.h>
+#include <assert.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <execinfo.h>
+#include <stdint.h>
+#include <sys/uio.h>
+#include <lkl_host.h>
+#include "iomem.h"
+
+static void print(const char *str, int len)
+{
+ write(STDOUT_FILENO, str, len);
+}
+
+struct pthread_sem {
+ pthread_mutex_t lock;
+ int count;
+ pthread_cond_t cond;
+};
+
+static void *sem_alloc(int count)
+{
+ struct pthread_sem *sem;
+
+ sem = malloc(sizeof(*sem));
+ if (!sem)
+ return NULL;
+
+ pthread_mutex_init(&sem->lock, NULL);
+ sem->count = count;
+ pthread_cond_init(&sem->cond, NULL);
+
+ return sem;
+}
+
+static void sem_free(void *sem)
+{
+ free(sem);
+}
+
+static void sem_up(void *_sem)
+{
+ struct pthread_sem *sem = (struct pthread_sem *)_sem;
+
+ pthread_mutex_lock(&sem->lock);
+ sem->count++;
+ if (sem->count > 0)
+ pthread_cond_signal(&sem->cond);
+ pthread_mutex_unlock(&sem->lock);
+}
+
+static void sem_down(void *_sem)
+{
+ struct pthread_sem *sem = (struct pthread_sem *)_sem;
+
+ pthread_mutex_lock(&sem->lock);
+ while (sem->count <= 0)
+ pthread_cond_wait(&sem->cond, &sem->lock);
+ sem->count--;
+ pthread_mutex_unlock(&sem->lock);
+}
+
+static int thread_create(void (*fn)(void *), void *arg)
+{
+ pthread_t thread;
+
+ return pthread_create(&thread, NULL, (void* (*)(void *))fn, arg);
+}
+
+static void thread_exit(void)
+{
+ pthread_exit(NULL);
+}
+
+static unsigned long long time_ns(void)
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+
+ return tv.tv_sec * 1000000000ULL + tv.tv_usec * 1000ULL;
+}
+
+static void *timer_alloc(void (*fn)(void *), void *arg)
+{
+ int err;
+ timer_t timer;
+ struct sigevent se = {
+ .sigev_notify = SIGEV_THREAD,
+ .sigev_value = {
+ .sival_ptr = arg,
+ },
+ .sigev_notify_function = (void (*)(union sigval))fn,
+ };
+
+ err = timer_create(CLOCK_REALTIME, &se, &timer);
+ if (err)
+ return NULL;
+
+ return (void *)(long)timer;
+}
+
+static int timer_set_oneshot(void *_timer, unsigned long ns)
+{
+ timer_t timer = (timer_t)(long)_timer;
+ struct itimerspec ts = {
+ .it_value = {
+ .tv_sec = ns / 1000000000,
+ .tv_nsec = ns % 1000000000,
+ },
+ };
+
+ if (!ts.it_value.tv_nsec)
+ ts.it_value.tv_nsec++;
+
+ return timer_settime(timer, 0, &ts, NULL);
+}
+
+static void timer_free(void *_timer)
+{
+ timer_t timer = (timer_t)(long)_timer;
+
+ timer_delete(timer);
+}
+
+static void panic(void)
+{
+ assert(0);
+}
+
+struct lkl_host_operations lkl_host_ops = {
+ .panic = panic,
+ .thread_create = thread_create,
+ .thread_exit = thread_exit,
+ .sem_alloc = sem_alloc,
+ .sem_free = sem_free,
+ .sem_up = sem_up,
+ .sem_down = sem_down,
+ .time = time_ns,
+ .timer_alloc = timer_alloc,
+ .timer_set_oneshot = timer_set_oneshot,
+ .timer_free = timer_free,
+ .print = print,
+ .mem_alloc = malloc,
+ .mem_free = free,
+ .ioremap = lkl_ioremap,
+ .iomem_access = lkl_iomem_access,
+ .virtio_devices = lkl_virtio_devs,
+};
+
+int fd_get_capacity(union lkl_disk_backstore bs, unsigned long long *res)
+{
+ off_t off;
+
+ off = lseek(bs.fd, 0, SEEK_END);
+ if (off < 0)
+ return -1;
+
+ *res = off;
+ return 0;
+}
+
+void fd_do_rw(union lkl_disk_backstore bs, unsigned int type, unsigned int prio,
+ unsigned long long sector, struct lkl_dev_buf *bufs, int count)
+{
+ int err = 0;
+ struct iovec *iovec = (struct iovec *)bufs;
+
+ if (count > 1)
+ lkl_printf("%s: %d\n", __func__, count);
+
+ /* TODO: handle short reads/writes */
+ switch (type) {
+ case LKL_DEV_BLK_TYPE_READ:
+ err = preadv(bs.fd, iovec, count, sector * 512);
+ break;
+ case LKL_DEV_BLK_TYPE_WRITE:
+ err = pwritev(bs.fd, iovec, count, sector * 512);
+ break;
+ case LKL_DEV_BLK_TYPE_FLUSH:
+ case LKL_DEV_BLK_TYPE_FLUSH_OUT:
+ err = fdatasync(bs.fd);
+ break;
+ default:
+ lkl_dev_blk_complete(bufs, LKL_DEV_BLK_STATUS_UNSUP, 0);
+ return;
+ }
+
+ if (err < 0)
+ lkl_dev_blk_complete(bufs, LKL_DEV_BLK_STATUS_IOERR, 0);
+ else
+ lkl_dev_blk_complete(bufs, LKL_DEV_BLK_STATUS_OK, err);
+}
+
+struct lkl_dev_blk_ops lkl_dev_blk_ops = {
+ .get_capacity = fd_get_capacity,
+ .request = fd_do_rw,
+};

Octavian Purdila

unread,
Nov 3, 2015, 3:30:15 PM11/3/15
to
This patch calls an architecture hook during the kernel config process
that allows the architecture to automatically define kconfig
symbols. This can be done by exporting environment variables from the
new architecture hook.

Signed-off-by: Octavian Purdila <octavian...@intel.com>
---
Makefile | 1 +
1 file changed, 1 insertion(+)

diff --git a/Makefile b/Makefile
index d5b3739..668b8fe 100644
--- a/Makefile
+++ b/Makefile
@@ -554,6 +554,7 @@ endif # KBUILD_EXTMOD

ifeq ($(dot-config),1)
# Read in config
+-include arch/$(SRCARCH)/auto.conf
-include include/config/auto.conf

ifeq ($(KBUILD_EXTMOD),)

Octavian Purdila

unread,
Nov 3, 2015, 3:30:16 PM11/3/15
to
Clockevent driver based on host timer operations and clocksource
driver and udelay support based on host time operations.

Signed-off-by: Octavian Purdila <octavian...@intel.com>
---
arch/lkl/kernel/time.c | 125 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 125 insertions(+)
create mode 100644 arch/lkl/kernel/time.c

diff --git a/arch/lkl/kernel/time.c b/arch/lkl/kernel/time.c
new file mode 100644
index 0000000..d099736
--- /dev/null
+++ b/arch/lkl/kernel/time.c
@@ -0,0 +1,125 @@
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <asm/host_ops.h>
+
+void __ndelay(unsigned long nsecs)
+{
+ unsigned long long start = lkl_ops->time();
+
+ while (lkl_ops->time() < start + nsecs)
+ ;
+}
+
+void __udelay(unsigned long usecs)
+{
+ __ndelay(usecs * NSEC_PER_USEC);
+}
+
+void __const_udelay(unsigned long xloops)
+{
+ __udelay(xloops / 5);
+}
+
+void calibrate_delay(void)
+{
+}
+
+static cycle_t clock_read(struct clocksource *cs)
+{
+ return lkl_ops->time();
+}
+
+static struct clocksource clocksource = {
+ .name = "lkl",
+ .rating = 499,
+ .read = clock_read,
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+ .mask = CLOCKSOURCE_MASK(64),
+};
+
+static void *timer;
+
+static int timer_irq;
+
+static void timer_fn(void *arg)
+{
+ lkl_trigger_irq(timer_irq, NULL);
+}
+
+static int clockevent_set_state_shutdown(struct clock_event_device *evt)
+{
+ if (timer) {
+ lkl_ops->timer_free(timer);
+ timer = NULL;
+ }
+
+ return 0;
+}
+
+static int clockevent_set_state_oneshot(struct clock_event_device *evt)
+{
+ timer = lkl_ops->timer_alloc(timer_fn, NULL);
+ if (!timer)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static irqreturn_t timer_irq_handler(int irq, void *dev_id)
+{
+ struct clock_event_device *dev = (struct clock_event_device *)dev_id;
+
+ dev->event_handler(dev);
+
+ return IRQ_HANDLED;
+}
+
+static int clockevent_next_event(unsigned long hz,
+ struct clock_event_device *evt)
+{
+ unsigned long ns = 1000000000 * hz / HZ;
+
+ return lkl_ops->timer_set_oneshot(timer, ns);
+}
+
+static struct clock_event_device clockevent = {
+ .name = "lkl",
+ .features = CLOCK_EVT_FEAT_ONESHOT,
+ .set_state_oneshot = clockevent_set_state_oneshot,
+ .set_next_event = clockevent_next_event,
+ .set_state_shutdown = clockevent_set_state_shutdown,
+};
+
+static struct irqaction irq0 = {
+ .handler = timer_irq_handler,
+ .flags = IRQF_NOBALANCING | IRQF_TIMER,
+ .dev_id = &clockevent,
+ .name = "timer"
+};
+
+void __init time_init(void)
+{
+ int ret;
+
+ if (!lkl_ops->timer_alloc || !lkl_ops->timer_free ||
+ !lkl_ops->timer_set_oneshot || !lkl_ops->time) {
+ pr_err("lkl: no time or timer support provided by host\n");
+ return;
+ }
+
+ timer_irq = lkl_get_free_irq("timer");
+ setup_irq(timer_irq, &irq0);
+
+ ret = clocksource_register_khz(&clocksource, 1000000);
+ if (ret)
+ pr_err("lkl: unable to register clocksource\n");
+
+ clockevents_config_and_register(&clockevent, HZ, 0, 0xffffffff);
+
+ pr_info("lkl: time and timers initialized\n");
+}

Octavian Purdila

unread,
Nov 3, 2015, 3:40:06 PM11/3/15
to
LKL is a non MMU architecture and hence there is not much work left to
do other than initializing the boot allocator and providing the page
and page table definitions.

The backstore memory is allocated via a host operation and the memory
size to be used is specified when the kernel is started, in the
lkl_start_kernel call.

Signed-off-by: Octavian Purdila <octavian...@intel.com>
---
arch/lkl/include/asm/page.h | 13 ++++++++
arch/lkl/include/asm/pgtable.h | 60 +++++++++++++++++++++++++++++++++++++
arch/lkl/kernel/mem.c | 67 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 140 insertions(+)
create mode 100644 arch/lkl/include/asm/page.h
create mode 100644 arch/lkl/include/asm/pgtable.h
create mode 100644 arch/lkl/kernel/mem.c

diff --git a/arch/lkl/include/asm/page.h b/arch/lkl/include/asm/page.h
new file mode 100644
index 0000000..455bf62
--- /dev/null
+++ b/arch/lkl/include/asm/page.h
@@ -0,0 +1,13 @@
+#ifndef _ASM_LKL_PAGE_H
+#define _ASM_LKL_PAGE_H
+
+#define CONFIG_KERNEL_RAM_BASE_ADDRESS memory_start
+
+#ifndef __ASSEMBLY__
+void free_mem(void);
+void bootmem_init(int mem_size);
+#endif
+
+#include <asm-generic/page.h>
+
+#endif /* _ASM_LKL_PAGE_H */
diff --git a/arch/lkl/include/asm/pgtable.h b/arch/lkl/include/asm/pgtable.h
new file mode 100644
index 0000000..726675a
--- /dev/null
+++ b/arch/lkl/include/asm/pgtable.h
@@ -0,0 +1,60 @@
+#ifndef _LKL_PGTABLE_H
+#define _LKL_PGTABLE_H
+
+#include <asm-generic/4level-fixup.h>
+
+/*
+ * (C) Copyright 2000-2002, Greg Ungerer <ge...@snapgear.com>
+ */
+
+#include <linux/slab.h>
+#include <asm/processor.h>
+#include <asm/io.h>
+
+#define pgd_present(pgd) (1)
+#define pgd_none(pgd) (0)
+#define pgd_bad(pgd) (0)
+#define pgd_clear(pgdp)
+#define kern_addr_valid(addr) (1)
+#define pmd_offset(a, b) ((void *)0)
+
+#define PAGE_NONE __pgprot(0)
+#define PAGE_SHARED __pgprot(0)
+#define PAGE_COPY __pgprot(0)
+#define PAGE_READONLY __pgprot(0)
+#define PAGE_KERNEL __pgprot(0)
+
+void paging_init(void);
+#define swapper_pg_dir ((pgd_t *)0)
+
+#define __swp_type(x) (0)
+#define __swp_offset(x) (0)
+#define __swp_entry(typ, off) ((swp_entry_t) { ((typ) | ((off) << 7)) })
+#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) })
+#define __swp_entry_to_pte(x) ((pte_t) { (x).val })
+
+/*
+ * ZERO_PAGE is a global shared page that is always zero: used
+ * for zero-mapped memory areas etc..
+ */
+#define ZERO_PAGE(vaddr) (virt_to_page(0))
+
+/*
+ * No page table caches to initialise.
+ */
+#define pgtable_cache_init() do { } while (0)
+
+/*
+ * All 32bit addresses are effectively valid for vmalloc...
+ * Sort of meaningless for non-VM targets.
+ */
+#define VMALLOC_START 0
+#define VMALLOC_END 0xffffffff
+#define KMAP_START 0
+#define KMAP_END 0xffffffff
+
+#include <asm-generic/pgtable.h>
+
+#define check_pgt_cache() do { } while (0)
+
+#endif
diff --git a/arch/lkl/kernel/mem.c b/arch/lkl/kernel/mem.c
new file mode 100644
index 0000000..225c2cc
--- /dev/null
+++ b/arch/lkl/kernel/mem.c
@@ -0,0 +1,67 @@
+#include <linux/bootmem.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+
+unsigned long memory_start, memory_end;
+static unsigned long _memory_start, mem_size;
+
+void __init bootmem_init(int mem_size)
+{
+ int bootmap_size;
+
+ _memory_start = (unsigned long)lkl_ops->mem_alloc(mem_size);
+ memory_start = _memory_start;
+ BUG_ON(!memory_start);
+ memory_end = memory_start + mem_size;
+
+ if (PAGE_ALIGN(memory_start) != memory_start) {
+ mem_size -= PAGE_ALIGN(memory_start) - memory_start;
+ memory_start = PAGE_ALIGN(memory_start);
+ mem_size = (mem_size / PAGE_SIZE) * PAGE_SIZE;
+ }
+
+ /*
+ * Give all the memory to the bootmap allocator, tell it to put the
+ * boot mem_map at the start of memory.
+ */
+ max_low_pfn = virt_to_pfn(memory_end);
+ min_low_pfn = virt_to_pfn(memory_start);
+ bootmap_size = init_bootmem_node(NODE_DATA(0), min_low_pfn, min_low_pfn,
+ max_low_pfn);
+
+ /*
+ * Free the usable memory, we have to make sure we do not free
+ * the bootmem bitmap so we then reserve it after freeing it :-)
+ */
+ free_bootmem(memory_start, mem_size);
+ reserve_bootmem(memory_start, bootmap_size, BOOTMEM_DEFAULT);
+
+ {
+ unsigned long zones_size[MAX_NR_ZONES] = {0, };
+
+ zones_size[ZONE_NORMAL] = (mem_size) >> PAGE_SHIFT;
+ free_area_init(zones_size);
+ }
+}
+
+void __init mem_init(void)
+{
+ max_mapnr = (((unsigned long)high_memory) - PAGE_OFFSET) >> PAGE_SHIFT;
+ /* this will put all memory onto the freelists */
+ totalram_pages = free_all_bootmem();
+ pr_info("Memory available: %luk/%luk RAM\n",
+ (nr_free_pages() << PAGE_SHIFT) >> 10, mem_size >> 10);
+}
+
+/*
+ * In our case __init memory is not part of the page allocator so there is
+ * nothing to free.
+ */
+void free_initmem(void)
+{
+}
+
+void free_mem(void)
+{
+ lkl_ops->mem_free((void *)_memory_start);
+}

Octavian Purdila

unread,
Nov 3, 2015, 3:40:06 PM11/3/15
to
Signed-off-by: Octavian Purdila <octavian...@intel.com>
---
include/asm-generic/atomic64.h | 2 ++
include/linux/atomic.h | 2 +-
2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/include/asm-generic/atomic64.h b/include/asm-generic/atomic64.h
index d48e78c..d0eb6cd 100644
--- a/include/asm-generic/atomic64.h
+++ b/include/asm-generic/atomic64.h
@@ -12,9 +12,11 @@
#ifndef _ASM_GENERIC_ATOMIC64_H
#define _ASM_GENERIC_ATOMIC64_H

+#ifndef CONFIG_64BIT
typedef struct {
long long counter;
} atomic64_t;
+#endif

#define ATOMIC64_INIT(i) { (i) }

diff --git a/include/linux/atomic.h b/include/linux/atomic.h
index 00a5763..81814c8 100644
--- a/include/linux/atomic.h
+++ b/include/linux/atomic.h
@@ -451,10 +451,10 @@ static inline int atomic_dec_if_positive(atomic_t *v)
}
#endif

-#include <asm-generic/atomic-long.h>
#ifdef CONFIG_GENERIC_ATOMIC64
#include <asm-generic/atomic64.h>
#endif
+#include <asm-generic/atomic-long.h>

#ifndef atomic64_andnot
static inline void atomic64_andnot(long long i, atomic64_t *v)

Octavian Purdila

unread,
Nov 3, 2015, 3:40:06 PM11/3/15
to
This patch introduces the host operations that define the interface
between the LKL and the host. These operations must be provided either
by a host library or by the application itself.

Signed-off-by: Octavian Purdila <octavian...@intel.com>
---
arch/lkl/include/asm/host_ops.h | 9 ++++
arch/lkl/include/uapi/asm/host_ops.h | 81 ++++++++++++++++++++++++++++++++++++
2 files changed, 90 insertions(+)
create mode 100644 arch/lkl/include/asm/host_ops.h
create mode 100644 arch/lkl/include/uapi/asm/host_ops.h

diff --git a/arch/lkl/include/asm/host_ops.h b/arch/lkl/include/asm/host_ops.h
new file mode 100644
index 0000000..7fb0381
--- /dev/null
+++ b/arch/lkl/include/asm/host_ops.h
@@ -0,0 +1,9 @@
+#ifndef _ASM_LKL_HOST_OPS_H
+#define _ASM_LKL_HOST_OPS_H
+
+#include "irq.h"
+#include <uapi/asm/host_ops.h>
+
+extern struct lkl_host_operations *lkl_ops;
+
+#endif
diff --git a/arch/lkl/include/uapi/asm/host_ops.h b/arch/lkl/include/uapi/asm/host_ops.h
new file mode 100644
index 0000000..e126154
--- /dev/null
+++ b/arch/lkl/include/uapi/asm/host_ops.h
@@ -0,0 +1,81 @@
+#ifndef _ASM_UAPI_LKL_HOST_OPS_H
+#define _ASM_UAPI_LKL_HOST_OPS_H
+
+/**
+ * lkl_host_operations - host operations used by the Linux kernel
+ *
+ * These operations must be provided by a host library or by the application
+ * itself.
+ *
+ * @virtio_devices - string containg the list of virtio devices in virtio mmio
+ * command line format. This string is appended to the kernel command line and
+ * is provided here for convenience to be implemented by the host library.
+ *
+ * @print - optional operation that receives console messages
+ *
+ * @panic - called during a kernel panic
+ *
+ * @sem_alloc - allocate a host semaphore an initialize it to count
+ * @sem_free - free a host semaphore
+ * @sem_up - perform an up operation on the semaphore
+ * @sem_down - perform a down operation on the semaphore
+ *
+ * @thread_create - create a new thread and run f(arg) in its context; returns a
+ * thread handle or NULL if the thread could not be created
+ * @thread_exit - terminates the current thread
+ *
+ * @mem_alloc - allocate memory
+ * @mem_free - free memory
+ *
+ * @timer_create - allocate a host timer that runs fn(arg) when the timer
+ * fires.
+ * @timer_free - disarms and free the timer
+ * @timer_set_oneshot - arm the timer to fire once, after delta ns.
+ * @timer_set_periodic - arm the timer to fire periodically, with a period of
+ * delta ns.
+ *
+ */
+struct lkl_host_operations {
+ const char *virtio_devices;
+
+ void (*print)(const char *str, int len);
+ void (*panic)(void);
+
+ void* (*sem_alloc)(int count);
+ void (*sem_free)(void *sem);
+ void (*sem_up)(void *sem);
+ void (*sem_down)(void *sem);
+
+ int (*thread_create)(void (*f)(void *), void *arg);
+ void (*thread_exit)(void);
+
+ void* (*mem_alloc)(unsigned long);
+ void (*mem_free)(void *);
+
+ unsigned long long (*time)(void);
+
+ void* (*timer_alloc)(void (*fn)(void *), void *arg);
+ int (*timer_set_oneshot)(void *timer, unsigned long delta);
+ void (*timer_free)(void *timer);
+
+ void* (*ioremap)(long addr, int size);
+ int (*iomem_access)(const volatile void *addr, void *val, int size,
+ int write);
+
+};
+
+/**
+ * lkl_start_kernel - registers the host operations and starts the kernel
+ *
+ * The function returns only after the kernel is shutdown with lkl_sys_halt.
+ *
+ * @lkl_ops - pointer to host operations
+ * @mem_size - how much memory to allocate to the Linux kernel
+ * @cmd_line - format for command line string that is going to be used to
+ * generate the Linux kernel command line
+ */
+int lkl_start_kernel(struct lkl_host_operations *lkl_ops,
+ unsigned long mem_size,
+ const char *cmd_line, ...);
+
+#endif

Octavian Purdila

unread,
Nov 3, 2015, 3:40:07 PM11/3/15
to
Adds the LKL Kconfig, vmlinux linker script, basic architecture
headers and miscellaneous basic functions or stubs such as
dump_stack(), show_regs() and cpuinfo proc ops.

The headers we introduce in this patch are simple wrappers to the
asm-generic headers or stubs for things we don't support, such as
ptrace, DMA, signals, ELF handling and low level processor operations.

The kernel configuration is automatically updated to reflect the
endianness of the host, 64bit support or the output format for
vmlinux's linker script. We do this by looking at the ld's default
output format.

Signed-off-by: Octavian Purdila <octavian...@intel.com>
---
MAINTAINERS | 5 ++
arch/lkl/.gitignore | 1 +
arch/lkl/Kconfig | 82 +++++++++++++++++++++++++++++++++
arch/lkl/auto.conf | 1 +
arch/lkl/defconfig | 35 ++++++++++++++
arch/lkl/include/asm/Kbuild | 77 +++++++++++++++++++++++++++++++
arch/lkl/include/asm/bitsperlong.h | 11 +++++
arch/lkl/include/asm/byteorder.h | 10 ++++
arch/lkl/include/asm/dma-mapping.h | 6 +++
arch/lkl/include/asm/elf.h | 13 ++++++
arch/lkl/include/asm/mutex.h | 7 +++
arch/lkl/include/asm/processor.h | 53 +++++++++++++++++++++
arch/lkl/include/asm/ptrace.h | 23 +++++++++
arch/lkl/include/asm/vmlinux.lds.h | 15 ++++++
arch/lkl/include/uapi/asm/Kbuild | 38 +++++++++++++++
arch/lkl/include/uapi/asm/bitsperlong.h | 12 +++++
arch/lkl/include/uapi/asm/sigcontext.h | 14 ++++++
arch/lkl/kernel/asm-offsets.c | 1 +
arch/lkl/kernel/misc.c | 57 +++++++++++++++++++++++
arch/lkl/kernel/vmlinux.lds.S | 45 ++++++++++++++++++
20 files changed, 506 insertions(+)
create mode 100644 arch/lkl/.gitignore
create mode 100644 arch/lkl/Kconfig
create mode 100644 arch/lkl/auto.conf
create mode 100644 arch/lkl/defconfig
create mode 100644 arch/lkl/include/asm/Kbuild
create mode 100644 arch/lkl/include/asm/bitsperlong.h
create mode 100644 arch/lkl/include/asm/byteorder.h
create mode 100644 arch/lkl/include/asm/dma-mapping.h
create mode 100644 arch/lkl/include/asm/elf.h
create mode 100644 arch/lkl/include/asm/mutex.h
create mode 100644 arch/lkl/include/asm/processor.h
create mode 100644 arch/lkl/include/asm/ptrace.h
create mode 100644 arch/lkl/include/asm/vmlinux.lds.h
create mode 100644 arch/lkl/include/uapi/asm/Kbuild
create mode 100644 arch/lkl/include/uapi/asm/bitsperlong.h
create mode 100644 arch/lkl/include/uapi/asm/sigcontext.h
create mode 100644 arch/lkl/kernel/asm-offsets.c
create mode 100644 arch/lkl/kernel/misc.c
create mode 100644 arch/lkl/kernel/vmlinux.lds.S

diff --git a/MAINTAINERS b/MAINTAINERS
index 77ed3a0..e2a737f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6355,6 +6355,11 @@ F: arch/powerpc/platforms/pasemi/
F: drivers/*/*pasemi*
F: drivers/*/*/*pasemi*

+LINUX KERNEL LIBRARY
+M: Octavian Purdila <octavian...@intel.com>
+S: Maintained
+F: arch/lkl/
+
LINUX SECURITY MODULE (LSM) FRAMEWORK
M: Chris Wright <chr...@sous-sol.org>
L: linux-secu...@vger.kernel.org
diff --git a/arch/lkl/.gitignore b/arch/lkl/.gitignore
new file mode 100644
index 0000000..c619b7d
--- /dev/null
+++ b/arch/lkl/.gitignore
@@ -0,0 +1 @@
+kernel/vmlinux.lds
diff --git a/arch/lkl/Kconfig b/arch/lkl/Kconfig
new file mode 100644
index 0000000..064960b
--- /dev/null
+++ b/arch/lkl/Kconfig
@@ -0,0 +1,82 @@
+config LKL
+ def_bool y
+ depends on !SMP && !MMU && !COREDUMP && !AUDITSYSCALL && !SECCOMP && !TRACEPOINTS && !UPROBES && !COMPAT && !USER_RETURN_NOTIFIER
+ select ARCH_THREAD_INFO_ALLOCATOR
+ select RWSEM_GENERIC_SPINLOCK
+ select GENERIC_ATOMIC64
+ select SEMAPHORE_SLEEPERS
+ select GENERIC_TIME
+ select GENERIC_FIND_NEXT_BIT
+ select GENERIC_HWEIGHT
+ select GENERIC_HARDIRQS
+ select FLATMEM
+ select FLAT_NODE_MEM_MAP
+ select GENERIC_CLOCKEVENTS
+ select GENERIC_CPU_DEVICES
+ select NO_HZ_IDLE
+ select NO_PREEMPT
+ select ARCH_WANT_FRAME_POINTERS
+ select PHYS_ADDR_T_64BIT if 64BIT
+ select 64BIT if OUTPUT_FORMAT = "elf64-x86-64"
+
+config OUTPUTFORMAT
+ string
+ option env="OUTPUT_FORMAT"
+
+config OUTPUT_FORMAT
+ string "Output format"
+ default OUTPUTFORMAT
+
+config ARCH_DMA_ADDR_T_64BIT
+ def_bool 64BIT
+
+config 64BIT
+ def_bool n
+
+config BIG_ENDIAN
+ def_bool n
+
+config NO_DMA
+ def_bool y
+
+config GENERIC_CSUM
+ def_bool y
+
+config GENERIC_HWEIGHT
+ def_bool y
+
+config NO_IOPORT_MAP
+ def_bool y
+
+config RWSEM_GENERIC_SPINLOCK
+ bool
+ default y
+
+source init/Kconfig
+
+source net/Kconfig
+
+source drivers/base/Kconfig
+
+source drivers/virtio/Kconfig
+
+source drivers/block/Kconfig
+
+source fs/Kconfig
+
+source mm/Kconfig
+
+source kernel/Kconfig.preempt
+
+source kernel/Kconfig.locks
+
+source kernel/Kconfig.hz
+
+source security/Kconfig
+
+source crypto/Kconfig
+
+source lib/Kconfig
+
+source lib/Kconfig.debug
+
diff --git a/arch/lkl/auto.conf b/arch/lkl/auto.conf
new file mode 100644
index 0000000..4bfd65a
--- /dev/null
+++ b/arch/lkl/auto.conf
@@ -0,0 +1 @@
+export OUTPUT_FORMAT=$(shell $(LD) -r -print-output-format)
diff --git a/arch/lkl/defconfig b/arch/lkl/defconfig
new file mode 100644
index 0000000..90f385d
--- /dev/null
+++ b/arch/lkl/defconfig
@@ -0,0 +1,35 @@
+# CONFIG_LOCALVERSION_AUTO is not set
+# CONFIG_USELIB is not set
+# CONFIG_SYSFS_SYSCALL is not set
+CONFIG_KALLSYMS_ALL=y
+# CONFIG_BASE_FULL is not set
+# CONFIG_FUTEX is not set
+# CONFIG_EPOLL is not set
+# CONFIG_SIGNALFD is not set
+# CONFIG_TIMERFD is not set
+# CONFIG_EVENTFD is not set
+# CONFIG_AIO is not set
+# CONFIG_ADVISE_SYSCALLS is not set
+CONFIG_EMBEDDED=y
+# CONFIG_VM_EVENT_COUNTERS is not set
+# CONFIG_COMPAT_BRK is not set
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_UEVENT_HELPER is not set
+# CONFIG_FW_LOADER is not set
+CONFIG_VIRTIO_MMIO=y
+CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y
+CONFIG_VIRTIO_BLK=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_EXT4_FS_SECURITY=y
+# CONFIG_FILE_LOCKING is not set
+# CONFIG_DNOTIFY is not set
+# CONFIG_INOTIFY_USER is not set
+# CONFIG_MISC_FILESYSTEMS is not set
+CONFIG_CRYPTO_ANSI_CPRNG=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_INFO_REDUCED=y
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
+# CONFIG_ENABLE_MUST_CHECK is not set
+CONFIG_SLUB_DEBUG_ON=y
diff --git a/arch/lkl/include/asm/Kbuild b/arch/lkl/include/asm/Kbuild
new file mode 100644
index 0000000..55acf3f
--- /dev/null
+++ b/arch/lkl/include/asm/Kbuild
@@ -0,0 +1,77 @@
+generic-y += atomic.h
+generic-y += barrier.h
+generic-y += bitops.h
+generic-y += bug.h
+generic-y += bugs.h
+generic-y += cache.h
+generic-y += cacheflush.h
+generic-y += checksum.h
+generic-y += cmpxchg-local.h
+generic-y += cmpxchg.h
+generic-y += cputime.h
+generic-y += current.h
+generic-y += delay.h
+generic-y += device.h
+generic-y += div64.h
+generic-y += dma.h
+generic-y += emergency-restart.h
+generic-y += errno.h
+generic-y += exec.h
+generic-y += ftrace.h
+generic-y += futex.h
+generic-y += hardirq.h
+generic-y += hw_irq.h
+generic-y += ioctl.h
+generic-y += ipcbuf.h
+generic-y += irq_regs.h
+generic-y += irqflags.h
+generic-y += irq_work.h
+generic-y += kdebug.h
+generic-y += kmap_types.h
+generic-y += linkage.h
+generic-y += local.h
+generic-y += local64.h
+generic-y += mcs_spinlock.h
+generic-y += mmu.h
+generic-y += mmu_context.h
+generic-y += module.h
+generic-y += msgbuf.h
+generic-y += page.h
+generic-y += param.h
+generic-y += parport.h
+generic-y += pci.h
+generic-y += percpu.h
+generic-y += pgalloc.h
+generic-y += poll.h
+generic-y += preempt.h
+generic-y += resource.h
+generic-y += rwsem.h
+generic-y += scatterlist.h
+generic-y += seccomp.h
+generic-y += sections.h
+generic-y += segment.h
+generic-y += sembuf.h
+generic-y += serial.h
+generic-y += shmbuf.h
+generic-y += siginfo.h
+generic-y += signal.h
+generic-y += simd.h
+generic-y += sizes.h
+generic-y += socket.h
+generic-y += sockios.h
+generic-y += stat.h
+generic-y += statfs.h
+generic-y += string.h
+generic-y += swab.h
+generic-y += switch_to.h
+generic-y += termbits.h
+generic-y += termios.h
+generic-y += time.h
+generic-y += timex.h
+generic-y += tlb.h
+generic-y += tlbflush.h
+generic-y += topology.h
+generic-y += trace_clock.h
+generic-y += uaccess.h
+generic-y += unaligned.h
+generic-y += word-at-a-time.h
diff --git a/arch/lkl/include/asm/bitsperlong.h b/arch/lkl/include/asm/bitsperlong.h
new file mode 100644
index 0000000..282b081
--- /dev/null
+++ b/arch/lkl/include/asm/bitsperlong.h
@@ -0,0 +1,11 @@
+#ifndef __LKL_BITSPERLONG_H
+#define __LKL_BITSPERLONG_H
+
+#include <uapi/asm/bitsperlong.h>
+
+#define BITS_PER_LONG __BITS_PER_LONG
+
+#define BITS_PER_LONG_LONG 64
+
+#endif
+
diff --git a/arch/lkl/include/asm/byteorder.h b/arch/lkl/include/asm/byteorder.h
new file mode 100644
index 0000000..19a5f0e
--- /dev/null
+++ b/arch/lkl/include/asm/byteorder.h
@@ -0,0 +1,10 @@
+#ifndef _ASM_LKL_BYTEORDER_H
+#define _ASM_LKL_BYTEORDER_H
+
+#if defined(CONFIG_BIG_ENDIAN)
+#include <linux/byteorder/big_endian.h>
+#else
+#include <linux/byteorder/little_endian.h>
+#endif
+
+#endif /* _ASM_LKL_BYTEORDER_H */
diff --git a/arch/lkl/include/asm/dma-mapping.h b/arch/lkl/include/asm/dma-mapping.h
new file mode 100644
index 0000000..a2e0bd9
--- /dev/null
+++ b/arch/lkl/include/asm/dma-mapping.h
@@ -0,0 +1,6 @@
+#ifndef _ASM_LKL_DMA_MAPPING_H
+#define _ASM_LKL_DMA_MAPPING_H
+
+#include <asm-generic/dma-mapping-broken.h>
+
+#endif
diff --git a/arch/lkl/include/asm/elf.h b/arch/lkl/include/asm/elf.h
new file mode 100644
index 0000000..cada3ab
--- /dev/null
+++ b/arch/lkl/include/asm/elf.h
@@ -0,0 +1,13 @@
+#ifndef _ASM_LKL_ELF_H
+#define _ASM_LKL_ELF_H
+
+#define elf_check_arch(x) 0
+
+#ifdef CONFIG_64BIT
+#define ELF_CLASS ELFCLASS64
+#else
+#define ELF_CLASS ELFCLASS32
+#endif
+
+#endif
+
diff --git a/arch/lkl/include/asm/mutex.h b/arch/lkl/include/asm/mutex.h
new file mode 100644
index 0000000..77c2c53
--- /dev/null
+++ b/arch/lkl/include/asm/mutex.h
@@ -0,0 +1,7 @@
+#ifndef _ASM_LKL_MUTEX_H
+#define _ASM_LKL_MUTEX_H
+
+#include <asm-generic/mutex-dec.h>
+
+#endif
+
diff --git a/arch/lkl/include/asm/processor.h b/arch/lkl/include/asm/processor.h
new file mode 100644
index 0000000..7f6bdb4
--- /dev/null
+++ b/arch/lkl/include/asm/processor.h
@@ -0,0 +1,53 @@
+#ifndef _ASM_LKL_PROCESSOR_H
+#define _ASM_LKL_PROCESSOR_H
+
+struct task_struct;
+
+#define cpu_relax() barrier()
+
+#define current_text_addr() ({ __label__ _l; _l: &&_l; })
+
+static inline unsigned long thread_saved_pc(struct task_struct *tsk)
+{
+ return 0;
+}
+
+static inline void release_thread(struct task_struct *dead_task)
+{
+}
+
+static inline void prepare_to_copy(struct task_struct *tsk)
+{
+}
+
+static inline unsigned long get_wchan(struct task_struct *p)
+{
+ return 0;
+}
+
+static inline void flush_thread(void)
+{
+}
+
+static inline void exit_thread(void)
+{
+}
+
+static inline void trap_init(void)
+{
+}
+
+struct thread_struct { };
+
+#define INIT_THREAD { }
+
+#define task_pt_regs(tsk) (struct pt_regs *)(NULL)
+
+/* We don't have strict user/kernel spaces */
+#define TASK_SIZE ((unsigned long)-1)
+#define TASK_UNMAPPED_BASE 0
+
+#define KSTK_EIP(tsk) (0)
+#define KSTK_ESP(tsk) (0)
+
+#endif
diff --git a/arch/lkl/include/asm/ptrace.h b/arch/lkl/include/asm/ptrace.h
new file mode 100644
index 0000000..f3c27e7
--- /dev/null
+++ b/arch/lkl/include/asm/ptrace.h
@@ -0,0 +1,23 @@
+#ifndef _ASM_LKL_PTRACE_H
+#define _ASM_LKL_PTRACE_H
+
+struct task_struct;
+
+#define user_mode(regs) 0
+#define kernel_mode(regs) 1
+#define profile_pc(regs) 0
+#define instruction_pointer(regs) 0
+#define user_stack_pointer(regs) 0
+
+static inline long arch_ptrace(struct task_struct *child,
+ long request, unsigned long addr,
+ unsigned long data)
+{
+ return -EINVAL;
+}
+
+static inline void ptrace_disable(struct task_struct *child)
+{
+}
+
+#endif
diff --git a/arch/lkl/include/asm/vmlinux.lds.h b/arch/lkl/include/asm/vmlinux.lds.h
new file mode 100644
index 0000000..392c94a
--- /dev/null
+++ b/arch/lkl/include/asm/vmlinux.lds.h
@@ -0,0 +1,15 @@
+#ifndef _LKL_VMLINUX_LDS_H
+#define _LKL_VMLINUX_LDS_H
+
+#ifdef __MINGW32__
+#define VMLINUX_SYMBOL(sym) _##sym
+#define RODATA_SECTION .rdata
+#endif
+
+#include <asm-generic/vmlinux.lds.h>
+
+#ifndef RODATA_SECTION
+#define RODATA_SECTION .rodata
+#endif
+
+#endif
diff --git a/arch/lkl/include/uapi/asm/Kbuild b/arch/lkl/include/uapi/asm/Kbuild
new file mode 100644
index 0000000..cfa727b
--- /dev/null
+++ b/arch/lkl/include/uapi/asm/Kbuild
@@ -0,0 +1,38 @@
+# UAPI Header export list
+include include/uapi/asm-generic/Kbuild.asm
+
+generic-y += auxvec.h
+generic-y += byteorder.h
+generic-y += elf.h
+generic-y += errno.h
+generic-y += fcntl.h
+generic-y += ioctl.h
+generic-y += ioctls.h
+generic-y += ipcbuf.h
+generic-y += kvm_para.h
+generic-y += mman.h
+generic-y += msgbuf.h
+generic-y += param.h
+generic-y += poll.h
+generic-y += posix_types.h
+generic-y += ptrace.h
+generic-y += resource.h
+generic-y += sembuf.h
+generic-y += setup.h
+generic-y += shmbuf.h
+generic-y += shmparam.h
+generic-y += siginfo.h
+generic-y += signal.h
+generic-y += socket.h
+generic-y += sockios.h
+generic-y += stat.h
+generic-y += statfs.h
+generic-y += swab.h
+generic-y += termbits.h
+generic-y += termios.h
+generic-y += timex.h
+generic-y += types.h
+generic-y += unistd.h
+
+# no header-y since we need special user headers handling
+# see arch/lkl/script/headers.py
diff --git a/arch/lkl/include/uapi/asm/bitsperlong.h b/arch/lkl/include/uapi/asm/bitsperlong.h
new file mode 100644
index 0000000..12b522d
--- /dev/null
+++ b/arch/lkl/include/uapi/asm/bitsperlong.h
@@ -0,0 +1,12 @@
+#ifndef _ASM_UAPI_LKL_AUTOCONF_H
+#define _ASM_UAPI_LKL_AUTOCONF_H
+
+#ifdef CONFIG_64BIT
+#define __BITS_PER_LONG 64
+#else
+#define __BITS_PER_LONG 32
+#endif
+
+#define __ARCH_WANT_STAT64
+
+#endif /* _ASM_UAPI_LKL_TARGET_H */
diff --git a/arch/lkl/include/uapi/asm/sigcontext.h b/arch/lkl/include/uapi/asm/sigcontext.h
new file mode 100644
index 0000000..99b2d53
--- /dev/null
+++ b/arch/lkl/include/uapi/asm/sigcontext.h
@@ -0,0 +1,14 @@
+#ifndef _ASM_UAPI_LKL_SIGCONTEXT_H
+#define _ASM_UAPI_LKL_SIGCONTEXT_H
+
+#include <asm/ptrace.h>
+
+struct pt_regs {
+};
+
+struct sigcontext {
+ struct pt_regs regs;
+ unsigned long oldmask;
+};
+
+#endif
diff --git a/arch/lkl/kernel/asm-offsets.c b/arch/lkl/kernel/asm-offsets.c
new file mode 100644
index 0000000..9e26311
--- /dev/null
+++ b/arch/lkl/kernel/asm-offsets.c
@@ -0,0 +1 @@
+/* Dummy asm-offsets.c file. Required by kbuild and ready to be used - hint! */
diff --git a/arch/lkl/kernel/misc.c b/arch/lkl/kernel/misc.c
new file mode 100644
index 0000000..44d4736
--- /dev/null
+++ b/arch/lkl/kernel/misc.c
@@ -0,0 +1,57 @@
+#include <linux/kallsyms.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <asm/ptrace.h>
+#include <asm/host_ops.h>
+
+void dump_stack(void)
+{
+ unsigned long dummy;
+ unsigned long *stack = &dummy;
+ unsigned long addr;
+
+ pr_info("Call Trace:\n");
+ while (((long)stack & (THREAD_SIZE - 1)) != 0) {
+ addr = *stack;
+ if (__kernel_text_address(addr)) {
+ pr_info("%p: [<%08lx>]", stack, addr);
+ print_symbol(KERN_CONT " %s", addr);
+ pr_cont("\n");
+ }
+ stack++;
+ }
+ pr_info("\n");
+}
+
+void show_regs(struct pt_regs *regs)
+{
+}
+
+#ifdef CONFIG_PROC_FS
+static void *cpuinfo_start(struct seq_file *m, loff_t *pos)
+{
+ return NULL;
+}
+
+static void *cpuinfo_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ return NULL;
+}
+
+static void cpuinfo_stop(struct seq_file *m, void *v)
+{
+}
+
+static int show_cpuinfo(struct seq_file *m, void *v)
+{
+ return 0;
+}
+
+const struct seq_operations cpuinfo_op = {
+ .start = cpuinfo_start,
+ .next = cpuinfo_next,
+ .stop = cpuinfo_stop,
+ .show = show_cpuinfo,
+};
+#endif
diff --git a/arch/lkl/kernel/vmlinux.lds.S b/arch/lkl/kernel/vmlinux.lds.S
new file mode 100644
index 0000000..cf96922
--- /dev/null
+++ b/arch/lkl/kernel/vmlinux.lds.S
@@ -0,0 +1,45 @@
+#include <asm/vmlinux.lds.h>
+#include <asm/thread_info.h>
+#include <asm/page.h>
+#include <asm/cache.h>
+
+OUTPUT_FORMAT(CONFIG_OUTPUT_FORMAT)
+
+VMLINUX_SYMBOL(jiffies) = VMLINUX_SYMBOL(jiffies_64);
+
+SECTIONS
+{
+ VMLINUX_SYMBOL(__init_begin) = .;
+ HEAD_TEXT_SECTION
+ INIT_TEXT_SECTION(PAGE_SIZE)
+ INIT_DATA_SECTION(16)
+ PERCPU_SECTION(L1_CACHE_BYTES)
+ VMLINUX_SYMBOL(__init_end) = .;
+
+ VMLINUX_SYMBOL(_stext) = .;
+ VMLINUX_SYMBOL(_text) = . ;
+ VMLINUX_SYMBOL(text) = . ;
+ .text :
+ {
+ TEXT_TEXT
+ SCHED_TEXT
+ LOCK_TEXT
+ }
+ VMLINUX_SYMBOL(_etext) = .;
+
+ VMLINUX_SYMBOL(_sdata) = .;
+ RO_DATA_SECTION(PAGE_SIZE)
+ RW_DATA_SECTION(L1_CACHE_BYTES, PAGE_SIZE, THREAD_SIZE)
+ VMLINUX_SYMBOL(_edata) = .;
+
+ EXCEPTION_TABLE(16)
+ NOTES
+
+ BSS_SECTION(0, 0, 0)
+ VMLINUX_SYMBOL(_end) = .;
+
+ STABS_DEBUG
+ DWARF_DEBUG
+
+ DISCARDS
+}

Octavian Purdila

unread,
Nov 3, 2015, 3:40:08 PM11/3/15
to
LKL does not support user processes but it must support kernel threads
as part as the normal kernel work-flow. It uses host operations to
create and terminate host threads that are going to run the kernel
threads. It also uses semaphores to synchronize those threads and to
allow the Linux kernel scheduler to control how the kernel threads
run.

Each kernel thread runs in a host threads and has a host semaphore
associated with it - the thread's scheduling semaphore. The semaphore
counter is initialized to 0. The first thing a kernel thread does
after getting spawned, before running any kernel code, is to perform a
down operation to block the thread.

The kernel controls host threads scheduling by performing up and down
operations on the scheduling semaphore. In __switch_context an up
operation on the next thread is performed to wake up a blocked thread,
and a down operation is performed on the prev thread to block it.

A thread is terminated by marking it in free_thread_info and
performing an up operation on the scheduling semaphore at which point
the marked thread will terminate itself.

Signed-off-by: Octavian Purdila <octavian...@intel.com>
---
arch/lkl/include/asm/thread_info.h | 82 +++++++++++++
arch/lkl/kernel/threads.c | 235 +++++++++++++++++++++++++++++++++++++
2 files changed, 317 insertions(+)
create mode 100644 arch/lkl/include/asm/thread_info.h
create mode 100644 arch/lkl/kernel/threads.c

diff --git a/arch/lkl/include/asm/thread_info.h b/arch/lkl/include/asm/thread_info.h
new file mode 100644
index 0000000..7636227
--- /dev/null
+++ b/arch/lkl/include/asm/thread_info.h
@@ -0,0 +1,82 @@
+#ifndef _ASM_LKL_THREAD_INFO_H
+#define _ASM_LKL_THREAD_INFO_H
+
+#define THREAD_SIZE (4096)
+
+#ifndef __ASSEMBLY__
+#include <asm/types.h>
+#include <asm/processor.h>
+
+typedef struct {
+ unsigned long seg;
+} mm_segment_t;
+
+struct thread_exit_info {
+ bool dead;
+ void *sched_sem;
+};
+
+struct thread_info {
+ struct task_struct *task;
+ unsigned long flags;
+ int preempt_count;
+ mm_segment_t addr_limit;
+ void *sched_sem;
+ struct thread_exit_info *exit_info;
+ struct task_struct *prev_sched;
+ unsigned long stackend;
+};
+
+#define INIT_THREAD_INFO(tsk) \
+{ \
+ .task = &tsk, \
+ .preempt_count = INIT_PREEMPT_COUNT, \
+ .flags = 0, \
+ .addr_limit = KERNEL_DS, \
+}
+
+#define init_thread_info (init_thread_union.thread_info)
+#define init_stack (init_thread_union.stack)
+
+/* how to get the thread information struct from C */
+extern struct thread_info *_current_thread_info;
+static inline struct thread_info *current_thread_info(void)
+{
+ return _current_thread_info;
+}
+
+/* thread information allocation */
+struct thread_info *alloc_thread_info_node(struct task_struct *, int node);
+void free_thread_info(struct thread_info *);
+
+int threads_init(void);
+void threads_cleanup(void);
+
+#define TIF_SYSCALL_TRACE 0
+#define TIF_NOTIFY_RESUME 1
+#define TIF_SIGPENDING 2
+#define TIF_NEED_RESCHED 3
+#define TIF_RESTORE_SIGMASK 4
+#define TIF_MEMDIE 5
+#define TIF_NOHZ 6
+
+#define __HAVE_THREAD_FUNCTIONS
+
+#define task_thread_info(task) ((struct thread_info *)(task)->stack)
+#define task_stack_page(task) ((task)->stack)
+
+/*
+ * Nothing to do here. The only new tasks created are kernel threads that have a
+ * predefined starting point thus no stack copy is required as for regular
+ * forked tasks.
+ */
+static inline void setup_thread_stack(struct task_struct *p,
+ struct task_struct *org)
+{
+}
+
+#define end_of_stack(p) (&task_thread_info(p)->stackend)
+
+#endif /* __ASSEMBLY__ */
+
+#endif
diff --git a/arch/lkl/kernel/threads.c b/arch/lkl/kernel/threads.c
new file mode 100644
index 0000000..aa13e57
--- /dev/null
+++ b/arch/lkl/kernel/threads.c
@@ -0,0 +1,235 @@
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <asm/host_ops.h>
+
+static int threads_counter;
+static void *threads_counter_lock;
+
+static inline void threads_counter_inc(void)
+{
+ lkl_ops->sem_down(threads_counter_lock);
+ threads_counter++;
+ lkl_ops->sem_up(threads_counter_lock);
+}
+
+static inline void threads_counter_dec(void)
+{
+ lkl_ops->sem_down(threads_counter_lock);
+ threads_counter--;
+ lkl_ops->sem_up(threads_counter_lock);
+}
+
+static inline int threads_counter_get(void)
+{
+ int counter;
+
+ lkl_ops->sem_down(threads_counter_lock);
+ counter = threads_counter;
+ lkl_ops->sem_up(threads_counter_lock);
+
+ return counter;
+}
+
+struct thread_info *alloc_thread_info_node(struct task_struct *task, int node)
+{
+ struct thread_info *ti;
+
+ ti = kmalloc(sizeof(*ti), GFP_KERNEL);
+ if (!ti)
+ return NULL;
+
+ ti->exit_info = NULL;
+ ti->prev_sched = NULL;
+ ti->sched_sem = lkl_ops->sem_alloc(0);
+ ti->task = task;
+ if (!ti->sched_sem) {
+ kfree(ti);
+ return NULL;
+ }
+
+ return ti;
+}
+
+static void kill_thread(struct thread_exit_info *ei)
+{
+ if (WARN_ON(!ei))
+ return;
+
+ ei->dead = true;
+ lkl_ops->sem_up(ei->sched_sem);
+}
+
+void free_thread_info(struct thread_info *ti)
+{
+ struct thread_exit_info *ei = ti->exit_info;
+
+ kfree(ti);
+ kill_thread(ei);
+}
+
+struct thread_info *_current_thread_info = &init_thread_union.thread_info;
+
+struct task_struct *__switch_to(struct task_struct *prev,
+ struct task_struct *next)
+{
+ struct thread_info *_prev = task_thread_info(prev);
+ struct thread_info *_next = task_thread_info(next);
+ /*
+ * schedule() expects the return of this function to be the task that we
+ * switched away from. Returning prev is not going to work because we
+ * are actually going to return the previous taks that was scheduled
+ * before the task we are going to wake up, and not the current task,
+ * e.g.:
+ *
+ * swapper -> init: saved prev on swapper stack is swapper
+ * init -> ksoftirqd0: saved prev on init stack is init
+ * ksoftirqd0 -> swapper: returned prev is swapper
+ */
+ static struct task_struct *abs_prev = &init_task;
+ /*
+ * We need to free the thread_info structure in free_thread_info to
+ * avoid races between the dying thread and other threads. We also need
+ * to cleanup sched_sem and signal to the prev thread that it needs to
+ * exit, and we use this stack varible to pass this info.
+ */
+ struct thread_exit_info ei = {
+ .dead = false,
+ .sched_sem = _prev->sched_sem,
+ };
+
+ _current_thread_info = task_thread_info(next);
+ _next->prev_sched = prev;
+ abs_prev = prev;
+ _prev->exit_info = &ei;
+
+ lkl_ops->sem_up(_next->sched_sem);
+ /* _next may be already gone so use ei instead */
+ lkl_ops->sem_down(ei.sched_sem);
+
+ if (ei.dead) {
+ lkl_ops->sem_free(ei.sched_sem);
+ threads_counter_dec();
+ lkl_ops->thread_exit();
+ }
+
+ _prev->exit_info = NULL;
+
+ return abs_prev;
+}
+
+struct thread_bootstrap_arg {
+ struct thread_info *ti;
+ int (*f)(void *);
+ void *arg;
+};
+
+static void thread_bootstrap(void *_tba)
+{
+ struct thread_bootstrap_arg *tba = (struct thread_bootstrap_arg *)_tba;
+ struct thread_info *ti = tba->ti;
+ int (*f)(void *) = tba->f;
+ void *arg = tba->arg;
+
+ lkl_ops->sem_down(ti->sched_sem);
+ kfree(tba);
+ if (ti->prev_sched)
+ schedule_tail(ti->prev_sched);
+
+ f(arg);
+ do_exit(0);
+}
+
+int copy_thread(unsigned long clone_flags, unsigned long esp,
+ unsigned long unused, struct task_struct *p)
+{
+ struct thread_info *ti = task_thread_info(p);
+ struct thread_bootstrap_arg *tba;
+ int ret;
+
+ tba = kmalloc(sizeof(*tba), GFP_KERNEL);
+ if (!tba)
+ return -ENOMEM;
+
+ tba->f = (int (*)(void *))esp;
+ tba->arg = (void *)unused;
+ tba->ti = ti;
+
+ ret = lkl_ops->thread_create(thread_bootstrap, tba);
+ if (ret) {
+ kfree(tba);
+ return -ENOMEM;
+ }
+
+ threads_counter_inc();
+
+ return 0;
+}
+
+void show_stack(struct task_struct *task, unsigned long *esp)
+{
+}
+
+static inline void pr_early(const char *str)
+{
+ if (lkl_ops->print)
+ lkl_ops->print(str, strlen(str));
+}
+
+/**
+ * This is called before the kernel initializes, so no kernel calls (including
+ * printk) can't be made yet.
+ */
+int threads_init(void)
+{
+ struct thread_info *ti = &init_thread_union.thread_info;
+ int ret = 0;
+
+ ti->exit_info = NULL;
+ ti->prev_sched = NULL;
+
+ ti->sched_sem = lkl_ops->sem_alloc(0);
+ if (!ti->sched_sem) {
+ pr_early("lkl: failed to allocate init schedule semaphore\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ threads_counter_lock = lkl_ops->sem_alloc(1);
+ if (!threads_counter_lock) {
+ pr_early("lkl: failed to alllocate threads counter lock\n");
+ ret = -ENOMEM;
+ goto out_free_init_sched_sem;
+ }
+
+ return 0;
+
+out_free_init_sched_sem:
+ lkl_ops->sem_free(ti->sched_sem);
+
+out:
+ return ret;
+}
+
+void threads_cleanup(void)
+{
+ struct task_struct *p;
+
+ for_each_process(p) {
+ struct thread_info *ti = task_thread_info(p);
+
+ if (p->pid != 1)
+ WARN(!(p->flags & PF_KTHREAD),
+ "non kernel thread task %p\n", p->comm);
+ WARN(p->state == TASK_RUNNING,
+ "thread %s still running while halting\n", p->comm);
+
+ kill_thread(ti->exit_info);
+ }
+
+ while (threads_counter_get())
+ ;
+
+ lkl_ops->sem_free(init_thread_union.thread_info.sched_sem);
+ lkl_ops->sem_free(threads_counter_lock);
+}

Octavian Purdila

unread,
Nov 3, 2015, 3:40:10 PM11/3/15
to
Add APIs that allows the host to reserve and free and interrupt number
and also to trigger an interrupt.

The trigger operation will simply store the interrupt data in
queue. The interrupt handler is run later, at the first opportunity it
has to avoid races with any kernel threads.

Currently, interrupts are run on the first interrupt enable operation
if interrupts are disabled and if we are not already in interrupt
context.

When triggering an interrupt the host can also send a void pointer
that is going to be available to the handler routine via
get_irq_regs()->irq_data. This allows to easly create host <-> kernel
synchronous communication channels and is currently used by the system
call interface.

Signed-off-by: Octavian Purdila <octavian...@intel.com>
---
arch/lkl/include/asm/irq.h | 10 +++
arch/lkl/include/uapi/asm/irq.h | 37 ++++++++++
arch/lkl/include/uapi/asm/sigcontext.h | 1 +
arch/lkl/kernel/irq.c | 131 +++++++++++++++++++++++++++++++++
4 files changed, 179 insertions(+)
create mode 100644 arch/lkl/include/asm/irq.h
create mode 100644 arch/lkl/include/uapi/asm/irq.h
create mode 100644 arch/lkl/kernel/irq.c

diff --git a/arch/lkl/include/asm/irq.h b/arch/lkl/include/asm/irq.h
new file mode 100644
index 0000000..f4ceb5a
--- /dev/null
+++ b/arch/lkl/include/asm/irq.h
@@ -0,0 +1,10 @@
+#ifndef _ASM_LKL_IRQ_H
+#define _ASM_LKL_IRQ_H
+
+#define NR_IRQS 32
+
+void free_IRQ(void);
+
+#include <uapi/asm/irq.h>
+
+#endif
diff --git a/arch/lkl/include/uapi/asm/irq.h b/arch/lkl/include/uapi/asm/irq.h
new file mode 100644
index 0000000..a33f5c5
--- /dev/null
+++ b/arch/lkl/include/uapi/asm/irq.h
@@ -0,0 +1,37 @@
+#ifndef _ASM_UAPI_LKL_IRQ_H
+#define _ASM_UAPI_LKL_IRQ_H
+
+/**
+ * lkl_trigger_irq - generate an interrupt
+ *
+ * This function is used by the device host side to signal its Linux counterpart
+ * that some event happened.
+ *
+ * @irq - the irq number to signal
+ * @data - data to be passed to the irq handler; available via
+ * get_irq_regs()->irq_data
+ */
+int lkl_trigger_irq(int irq, void *data);
+
+/**
+ * lkl_get_free_irq - find and reserve a free IRQ number
+ *
+ * This function is called by the host device code to find an unused IRQ number
+ * and reserved it for its own use.
+ *
+ * @user - a string to identify the user
+ * @returns - and irq number that can be used by request_irq or an negative
+ * value in case of an error
+ */
+int lkl_get_free_irq(const char *user);
+
+/**
+ * lkl_put_irq - release an IRQ number previously obtained with lkl_get_free_irq
+ *
+ * @irq - irq number to release
+ * @user - string identifying the user; should be the same as the one passed to
+ * lkl_get_free_irq when the irq number was obtained
+ */
+void lkl_put_irq(int irq, const char *name);
+
+#endif
diff --git a/arch/lkl/include/uapi/asm/sigcontext.h b/arch/lkl/include/uapi/asm/sigcontext.h
index 99b2d53..77aba40 100644
--- a/arch/lkl/include/uapi/asm/sigcontext.h
+++ b/arch/lkl/include/uapi/asm/sigcontext.h
@@ -4,6 +4,7 @@
#include <asm/ptrace.h>

struct pt_regs {
+ void *irq_data;
};

struct sigcontext {
diff --git a/arch/lkl/kernel/irq.c b/arch/lkl/kernel/irq.c
new file mode 100644
index 0000000..9ff0e12
--- /dev/null
+++ b/arch/lkl/kernel/irq.c
@@ -0,0 +1,131 @@
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/hardirq.h>
+#include <asm/irq_regs.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/tick.h>
+#include <asm/irqflags.h>
+#include <asm/host_ops.h>
+
+static bool irqs_enabled;
+
+#define MAX_IRQS 16
+static struct irq_info {
+ struct pt_regs regs[MAX_IRQS];
+ const char *user;
+ int count;
+} irqs[NR_IRQS];
+static void *irqs_lock;
+
+static void do_IRQ(int irq, struct pt_regs *regs)
+{
+ struct pt_regs *old_regs = set_irq_regs(regs);
+
+ irq_enter();
+ generic_handle_irq(irq);
+ irq_exit();
+
+ set_irq_regs(old_regs);
+}
+
+int lkl_trigger_irq(int irq, void *data)
+{
+ struct pt_regs regs = {
+ .irq_data = data,
+ };
+ int ret = 0;
+
+ if (irq >= NR_IRQS)
+ return -EINVAL;
+
+ lkl_ops->sem_down(irqs_lock);
+ if (irqs[irq].count < MAX_IRQS) {
+ irqs[irq].regs[irqs[irq].count] = regs;
+ irqs[irq].count++;
+ } else {
+ ret = -EOVERFLOW;
+ }
+ lkl_ops->sem_up(irqs_lock);
+
+ wakeup_cpu();
+
+ return ret;
+}
+
+static void run_irqs(void)
+{
+ int i, j;
+
+ lkl_ops->sem_down(irqs_lock);
+ for (i = 0; i < NR_IRQS; i++) {
+ for (j = 0; j < irqs[i].count; j++)
+ do_IRQ(i, &irqs[i].regs[j]);
+ irqs[i].count = 0;
+ }
+ lkl_ops->sem_up(irqs_lock);
+}
+
+int show_interrupts(struct seq_file *p, void *v)
+{
+ return 0;
+}
+
+int lkl_get_free_irq(const char *user)
+{
+ int i;
+ int ret = -EBUSY;
+
+ /* 0 is not a valid IRQ */
+ for (i = 1; i < NR_IRQS; i++) {
+ if (!irqs[i].user) {
+ irqs[i].user = user;
+ ret = i;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+void lkl_put_irq(int i, const char *user)
+{
+ if (!irqs[i].user || strcmp(irqs[i].user, user) != 0) {
+ WARN("%s tried to release %s's irq %d", user, irqs[i].user, i);
+ return;
+ }
+
+ irqs[i].user = NULL;
+}
+
+unsigned long arch_local_save_flags(void)
+{
+ return irqs_enabled;
+}
+
+void arch_local_irq_restore(unsigned long flags)
+{
+ if (flags == ARCH_IRQ_ENABLED && irqs_enabled == ARCH_IRQ_DISABLED &&
+ !in_interrupt())
+ run_irqs();
+ irqs_enabled = flags;
+}
+
+void free_IRQ(void)
+{
+ lkl_ops->sem_free(irqs_lock);
+}
+
+void init_IRQ(void)
+{
+ int i;
+
+ irqs_lock = lkl_ops->sem_alloc(1);
+ BUG_ON(!irqs_lock);
+
+ for (i = 0; i < NR_IRQS; i++)
+ irq_set_chip_and_handler(i, &dummy_irq_chip, handle_simple_irq);
+
+ pr_info("lkl: irqs initialized\n");
+}

Richard Weinberger

unread,
Nov 3, 2015, 4:50:08 PM11/3/15
to
On Tue, Nov 3, 2015 at 9:20 PM, Octavian Purdila
<octavian...@intel.com> wrote:
> LKL (Linux Kernel Library) is aiming to allow reusing the Linux kernel code
> as extensively as possible with minimal effort and reduced maintenance
> overhead.
>
> Examples of how LKL can be used are: creating userspace applications
> (running on Linux and other operating systems) that can read or write Linux
> filesystems or can use the Linux networking stack, creating kernel drivers
> for other operating systems that can read Linux filesystems, bootloaders
> support for reading/writing Linux filesystems, etc.
>
> With LKL, the kernel code is compiled into an object file that can be
> directly linked by applications. The API offered by LKL is based on the
> Linux system call interface.
>
> LKL is implemented as an architecture port in arch/lkl. It relies on host
> operations defined by the application or a host library (tools/lkl/lib).
>
> The latest LKL version can be found at g...@github.com:lkl/linux.git

Or more copy&paste friendly: https://github.com/lkl/linux.git

> FAQ
> ===
>
> Q: How is LKL different from UML?
> A: UML provides a full OS environment (e.g. user/kernel separation, user
> processes) and also has requirements (a filesystem, processes, etc.) that
> makes it hard to use it for standalone applications. UML also relies
> heavily on Linux hosts. On the other hand LKL is designed to be linked
> directly with the application and hence does not have user/kernel
> separation which makes it easier to use it in standalone applications.

So, this is a "liblinux" where applications are directly linked
against the kernel.
IOW system calls are plain function calls into the kernel?

This eliminates UML's most problematic areas, system call handling via ptrace()
and virtual memory management via SIGSEGV. :-)

> Q: How is LKL different from LibOS?
> A: LibOS re-implements high-level kernel APIs for timers, softirqs,
> scheduling, sysctl, SLAB/SLUB, etc. LKL behaves like any arch port,
> implementing the arch level operations requested by the Linux kernel. LKL
> also offers a host interface so that support for multiple hosts can be
> easily implemented.

Yeah, these re-implementations are what I find most worrisome about LibOS.

>
> Building LKL the host library and LKL applications
> ==================================================
>
> % cd tools/lkl
> % make
>
> will build LKL as a object file, it will install it in tools/lkl/lib together
> with the headers files in tools/lkl/include then will build the host library,
> tests and a few of application examples:
>
> * tests/boot - a simple applications that uses LKL and exercises the basic
> LKL APIs
>
> * fs2tar - a tool that converts a filesystem image to a tar archive
>
> * cptofs/cpfromfs - a tool that copies files to/from a filesystem image

Seeing forward to have a libguestfs port. :-)

Is LKL strictly single threaded?

--
Thanks,
//richard

Richard W.M. Jones

unread,
Nov 3, 2015, 5:50:06 PM11/3/15
to
Thanks - I was keeping an eye on libos (and on the NetBSD rump kernel
stuff before), ready to integrate them into libguestfs as soon as they
offered filesystem access.

It's easy to write a libguestfs-compatible backend, which brings all
the virt-* tools from libguestfs to the new code. The UML one looks
like this:

https://github.com/libguestfs/libguestfs/blob/master/src/launch-uml.c

I'm dubious that a lib-based approach could support LVM, partioning,
ntfs-3g, qcow2, vmdk and all the other libguestfs stuff that relies on
userspace tools + qemu as well as just the kernel drivers.
Nevertheless a fast subset of libguestfs supporting just kernel
filesystem drivers could be useful.

Rich.

--
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
Read my programming and virtualization blog: http://rwmj.wordpress.com
virt-top is 'top' for virtual machines. Tiny program with many
powerful monitoring features, net stats, disk stats, logging, etc.
http://people.redhat.com/~rjones/virt-top

Octavian Purdila

unread,
Nov 3, 2015, 6:10:07 PM11/3/15
to
On Tue, Nov 3, 2015 at 11:40 PM, Richard Weinberger
<richard.w...@gmail.com> wrote:

Hi Richard,
More like "thread" calls. All system calls are executed in a dedicate
(kernel) thread to avoid race conditions with the "interrupt" path.

> This eliminates UML's most problematic areas, system call handling via ptrace()
> and virtual memory management via SIGSEGV. :-)
>

:)

>> Q: How is LKL different from LibOS?
>> A: LibOS re-implements high-level kernel APIs for timers, softirqs,
>> scheduling, sysctl, SLAB/SLUB, etc. LKL behaves like any arch port,
>> implementing the arch level operations requested by the Linux kernel. LKL
>> also offers a host interface so that support for multiple hosts can be
>> easily implemented.
>
> Yeah, these re-implementations are what I find most worrisome about LibOS.
>
>>
>> Building LKL the host library and LKL applications
>> ==================================================
>>
>> % cd tools/lkl
>> % make
>>
>> will build LKL as a object file, it will install it in tools/lkl/lib together
>> with the headers files in tools/lkl/include then will build the host library,
>> tests and a few of application examples:
>>
>> * tests/boot - a simple applications that uses LKL and exercises the basic
>> LKL APIs
>>
>> * fs2tar - a tool that converts a filesystem image to a tar archive
>>
>> * cptofs/cpfromfs - a tool that copies files to/from a filesystem image
>
> Seeing forward to have a libguestfs port. :-)
>
> Is LKL strictly single threaded?
>

At this point yes. SMP support is on my todo list though :)

Hajime Tazaki

unread,
Nov 3, 2015, 6:30:06 PM11/3/15
to

At Tue, 3 Nov 2015 22:45:45 +0000,
Richard W.M. Jones wrote:

> > > * cptofs/cpfromfs - a tool that copies files to/from a filesystem image
> >
> > Seeing forward to have a libguestfs port. :-)
>
> Thanks - I was keeping an eye on libos (and on the NetBSD rump kernel
> stuff before), ready to integrate them into libguestfs as soon as they
> offered filesystem access.

I've been working on fs support on libos recently during
the integration with rump kernel _hypercall_, though it's
still in the middle (but open(2) in a specific condition
works fine at least).

https://github.com/libos-nuse/net-next-nuse/tree/rump-hypcall

I would expect to see more concrete patchset in near future.

-- Hajime

Octavian Purdila

unread,
Nov 3, 2015, 6:30:06 PM11/3/15
to
Thanks for the pointers Richard, I am going to take a look at it.

>
> I'm dubious that a lib-based approach could support LVM, partioning,
> ntfs-3g, qcow2, vmdk and all the other libguestfs stuff that relies on
> userspace tools + qemu as well as just the kernel drivers.
> Nevertheless a fast subset of libguestfs supporting just kernel
> filesystem drivers could be useful.
>

LKL uses the full Linux I/O stack and I think LVM and partitioning
should work out of the box. Adding support for qcow2 and vmdk should
be possible as well. ntfs-3g might be problematic.

Hajime Tazaki

unread,
Nov 3, 2015, 6:40:06 PM11/3/15
to

At Tue, 3 Nov 2015 22:20:35 +0200,
Octavian Purdila wrote:
>
> This patch introduces the host operations that define the interface
> between the LKL and the host. These operations must be provided either
> by a host library or by the application itself.
(snip)
this is related to the thing that I'm improving libos right now.
my current conclusion is using rump hypercall interfaces,
which I'm currently working on.

we (libos and lkl) may have matured interface as well as
reduce/share the effort to have more underlying (host)
calls.

-- Hajime

Richard W.M. Jones

unread,
Nov 4, 2015, 9:00:10 AM11/4/15
to
On Wed, Nov 04, 2015 at 01:24:03AM +0200, Octavian Purdila wrote:
> Thanks for the pointers Richard, I am going to take a look at it.

Now I've had a chance to look at some of the example LKL tools, here's
what this actually involves. It's not actually a great deal of work,
it could probably be done in a day or two, but see my question about
`lkl_sys_*' below.

libguestfs (the library part) needs to talk over an RPC connection to
its daemon. See diagram here:

http://libguestfs.org/guestfs-internals.1.html

The code in src/launch-{direct,libvirt,uml,...}.c sets up that
connection and runs the daemon -- normally inside a qemu wrapper, but
it could be inside UML. For LKL I think it should just fork the
daemon directly.

The daemon would then be linked to LKL.

So really what's needed is a src/launch-lkl.c probably modelled after
one of these current backends:

https://github.com/libguestfs/libguestfs/blob/master/src/launch-uml.c
https://github.com/libguestfs/libguestfs/blob/master/src/launch-unix.c

and then recompile the daemon to link to LKL:

https://github.com/libguestfs/libguestfs/tree/master/daemon

and pass the list of disk images to the daemon, probably best to do
that on the guestfsd command line.

My only problem here: you can't just link to daemon to LKL, do you
have to change all of the system calls from `foo' to `lkl_sys_foo'?
That's an awful lot of #ifdefs ...

Rich.

--
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
Read my programming and virtualization blog: http://rwmj.wordpress.com
virt-top is 'top' for virtual machines. Tiny program with many
powerful monitoring features, net stats, disk stats, logging, etc.
http://people.redhat.com/~rjones/virt-top

Octavian Purdila

unread,
Nov 4, 2015, 9:30:08 AM11/4/15
to
We could redefine the syscalls/libc symbols to call lkl_sys_ functions
in launch-lkl, e.g.:

int opendir(const char *path)
{
return lkl_opendir(new_path)

Richard Weinberger

unread,
Nov 6, 2015, 7:40:06 PM11/6/15
to
Am 04.11.2015 um 15:15 schrieb Octavian Purdila:
> We could redefine the syscalls/libc symbols to call lkl_sys_ functions
> in launch-lkl, e.g.:
>
> int opendir(const char *path)
> {
> return lkl_opendir(new_path)
> }

To get a better feeling how LKL behaves I've started with a tool
to mount any Linux filesystem by FUSE.
I.e. such that we can finally automount without root and bugs in filesystem
code won't hurt that much.

lkl_sys_fstatat64() uses the type struct lkl_stat64. Where is it defined?
git grep is unable to locate it.
At least it seems to be incompatible with my local struct stat.

And why is there no lkl_sys_openat() syscall?

Thanks,
//richard

Richard W.M. Jones

unread,
Nov 7, 2015, 2:20:06 AM11/7/15
to
On Sat, Nov 07, 2015 at 01:35:36AM +0100, Richard Weinberger wrote:
> Am 04.11.2015 um 15:15 schrieb Octavian Purdila:
> > We could redefine the syscalls/libc symbols to call lkl_sys_ functions
> > in launch-lkl, e.g.:
> >
> > int opendir(const char *path)
> > {
> > return lkl_opendir(new_path)
> > }
>
> To get a better feeling how LKL behaves I've started with a tool
> to mount any Linux filesystem by FUSE.
> I.e. such that we can finally automount without root and bugs in filesystem
> code won't hurt that much.

guestmount already does this:

http://libguestfs.org/guestmount.1.html

By porting a small amount of code from the daemon/ directory, it could
do it using lkl too. See:

http://www.gossamer-threads.com/lists/linux/kernel/2296116#2296116

Rich.

> lkl_sys_fstatat64() uses the type struct lkl_stat64. Where is it defined?
> git grep is unable to locate it.
> At least it seems to be incompatible with my local struct stat.
>
> And why is there no lkl_sys_openat() syscall?
>
> Thanks,
> //richard

--
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
Read my programming and virtualization blog: http://rwmj.wordpress.com
Fedora Windows cross-compiler. Compile Windows programs, test, and
build Windows installers. Over 100 libraries supported.
http://fedoraproject.org/wiki/MinGW

Richard W.M. Jones

unread,
Nov 7, 2015, 5:50:07 AM11/7/15
to

I just pushed a (very early) WIP branch that contains changes to
libguestfs to add an LKL backend:

https://github.com/rwmjones/libguestfs/tree/lkl

Read the README file in the libguestfs sources before starting,
followed by the instructions in the commit message:

https://github.com/rwmjones/libguestfs/commit/e38525f0b984d0a426f3348d95f2033673d4eaa4

Rich.

--
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
Read my programming and virtualization blog: http://rwmj.wordpress.com
virt-df lists disk usage of guests without needing to install any
software inside the virtual machine. Supports Linux and Windows.
http://people.redhat.com/~rjones/virt-df/

Arnd Bergmann

unread,
Nov 7, 2015, 6:20:07 PM11/7/15
to
On Tuesday 03 November 2015 22:20:52 Octavian Purdila wrote:
> +struct pthread_sem {
> + pthread_mutex_t lock;
> + int count;
> + pthread_cond_t cond;
> +};
> +
> +static void *sem_alloc(int count)
> +{
> + struct pthread_sem *sem;
> +
> + sem = malloc(sizeof(*sem));
> + if (!sem)
> + return NULL;
> +
> + pthread_mutex_init(&sem->lock, NULL);
> + sem->count = count;
> + pthread_cond_init(&sem->cond, NULL);
> +
> + return sem;
> +}

What is the reason to have generalized semaphores in the
host API rather than a simple mutex?

> +static unsigned long long time_ns(void)
> +{
> + struct timeval tv;
> +
> + gettimeofday(&tv, NULL);
> +
> + return tv.tv_sec * 1000000000ULL + tv.tv_usec * 1000ULL;
> +}

clock_gettime() has been around since POSIX.1-2001 and provides the
nanosecond resolution you use in the interface.

Arnd

Arnd Bergmann

unread,
Nov 7, 2015, 6:30:05 PM11/7/15
to
On Tuesday 03 November 2015 22:20:39 Octavian Purdila wrote:
> +
> +/*
> + * Unsupported system calls due to lack of support in LKL (e.g. related to
> + * virtual memory, signal, user processes). We also only support 64bit version
> + * of system calls where we have two version to keep the same APi across 32 and
> + * 64 bit hosts.
> + */
> +#define __NR_restart_syscall 0
> +#define __NR_exit 0
> +#define __NR_fork 0
> +#define __NR_execve 0
> +#define __NR_ptrace 0
> +#define __NR_alarm 0
> +#define __NR_pause 0

Why are these not #undef?

> diff --git a/arch/lkl/include/uapi/asm/unistd.h b/arch/lkl/include/uapi/asm/unistd.h
> new file mode 100644
> index 0000000..68b5423
> --- /dev/null
> +++ b/arch/lkl/include/uapi/asm/unistd.h
> @@ -0,0 +1,256 @@
> +#ifndef _ASM_UAPI_LKL_UNISTD_H
> +#define _ASM_UAPI_LKL_UNISTD_H
> +
> +#ifdef __KERNEL__
> +#define __NR_ni_syscall 0
> +#define __NR_reboot 1
> +#endif
> +#define __NR_getpid 2
> +#define __NR_write 3
> +#define __NR_close 4
> +#define __NR_unlink 5
> +#define __NR_open 6
> +#define __NR_poll 7

Could you use the standard numbers from include/uapi/asm-generic/unistd.h?
Maybe include that header and then #undef the ones you don't support?
That would avoid having to assign a new number of each future syscall
that gets added.

Octavian Purdila

unread,
Nov 7, 2015, 10:50:05 PM11/7/15
to
It would be nice to do that but if we undef them warnings will be
generated during the build (e.g. "warning: #warning syscall pause not
implemented"). Is there a way to disable those warnings?

Octavian Purdila

unread,
Nov 7, 2015, 11:10:07 PM11/7/15
to
On Sun, Nov 8, 2015 at 1:16 AM, Arnd Bergmann <ar...@arndb.de> wrote:
> On Tuesday 03 November 2015 22:20:52 Octavian Purdila wrote:
>> +struct pthread_sem {
>> + pthread_mutex_t lock;
>> + int count;
>> + pthread_cond_t cond;
>> +};
>> +
>> +static void *sem_alloc(int count)
>> +{
>> + struct pthread_sem *sem;
>> +
>> + sem = malloc(sizeof(*sem));
>> + if (!sem)
>> + return NULL;
>> +
>> + pthread_mutex_init(&sem->lock, NULL);
>> + sem->count = count;
>> + pthread_cond_init(&sem->cond, NULL);
>> +
>> + return sem;
>> +}
>
> What is the reason to have generalized semaphores in the
> host API rather than a simple mutex?
>

Currently waking up from idle after an IRQ event requires a semaphore.
I'll see if we can use a simple mutex for this.

>> +static unsigned long long time_ns(void)
>> +{
>> + struct timeval tv;
>> +
>> + gettimeofday(&tv, NULL);
>> +
>> + return tv.tv_sec * 1000000000ULL + tv.tv_usec * 1000ULL;
>> +}
>
> clock_gettime() has been around since POSIX.1-2001 and provides the
> nanosecond resolution you use in the interface.
>

Good point, I will change it to clock_gettime.

Thanks for the review Arnd !

Octavian Purdila

unread,
Nov 7, 2015, 11:20:06 PM11/7/15
to

Octavian Purdila

unread,
Nov 7, 2015, 11:40:06 PM11/7/15
to
On Sat, Nov 7, 2015 at 2:35 AM, Richard Weinberger <ric...@nod.at> wrote:
> Am 04.11.2015 um 15:15 schrieb Octavian Purdila:
>> We could redefine the syscalls/libc symbols to call lkl_sys_ functions
>> in launch-lkl, e.g.:
>>
>> int opendir(const char *path)
>> {
>> return lkl_opendir(new_path)
>> }
>
> To get a better feeling how LKL behaves I've started with a tool
> to mount any Linux filesystem by FUSE.
> I.e. such that we can finally automount without root and bugs in filesystem
> code won't hurt that much.
>

Hi Richard,

I finished coding mine up yesterday :) I've just pushed it to github
if you want to take a look at it (together with some small fixes).

> lkl_sys_fstatat64() uses the type struct lkl_stat64. Where is it defined?
> git grep is unable to locate it.
> At least it seems to be incompatible with my local struct stat.
>

When doing the build in tools/lkl the LKL headers are installed in
include/lkl. You should find it in there:

$:~/src/linux/tools/lkl$ find include/ -type f | xargs grep lkl_stat64
include/lkl/asm-generic/stat.h:struct lkl_stat64 {

> And why is there no lkl_sys_openat() syscall?
>

Didn't get to test it hence I didn't add it to the API yet, as many
other system calls.

Arnd Bergmann

unread,
Nov 8, 2015, 5:30:07 AM11/8/15
to
On Sunday 08 November 2015 05:49:00 Octavian Purdila wrote:
> > Could you use the standard numbers from include/uapi/asm-generic/unistd.h?
> > Maybe include that header and then #undef the ones you don't support?
> > That would avoid having to assign a new number of each future syscall
> > that gets added.
>
> It would be nice to do that but if we undef them warnings will be
> generated during the build (e.g. "warning: #warning syscall pause not
> implemented"). Is there a way to disable those warnings?

Yes:

#define __IGNORE_restart_syscall
#define __IGNORE_exit
...

Arnd

Arnd Bergmann

unread,
Nov 8, 2015, 5:40:06 AM11/8/15
to
On Sunday 08 November 2015 06:01:08 Octavian Purdila wrote:
> >> +static void *sem_alloc(int count)
> >> +{
> >> + struct pthread_sem *sem;
> >> +
> >> + sem = malloc(sizeof(*sem));
> >> + if (!sem)
> >> + return NULL;
> >> +
> >> + pthread_mutex_init(&sem->lock, NULL);
> >> + sem->count = count;
> >> + pthread_cond_init(&sem->cond, NULL);
> >> +
> >> + return sem;
> >> +}
> >
> > What is the reason to have generalized semaphores in the
> > host API rather than a simple mutex?
> >
>
> Currently waking up from idle after an IRQ event requires a semaphore.
> I'll see if we can use a simple mutex for this.

According to the pthread_mutex_unlock() man page, you are not allowed
to unlock a mutex from any thread other than the one that owns the
mutex through pthread_mutex_lock(), so if the IRQ event is sent to
another thread, that would not be safe even if it happens to work
on linux+glibc.

Another option would be to use futexes as the basic primitive, which
might make the implementation for Linux hosts a bit more efficient,
but complicates the implementation for hosts that do not implement
those.

Arnd

Hajime Tazaki

unread,
Nov 8, 2015, 8:50:06 AM11/8/15
to

Hello Octavian,

At Tue, 3 Nov 2015 22:20:31 +0200,
Octavian Purdila wrote:
>
>
> Q: How is LKL different from LibOS?
> A: LibOS re-implements high-level kernel APIs for timers, softirqs,
> scheduling, sysctl, SLAB/SLUB, etc. LKL behaves like any arch port,
> implementing the arch level operations requested by the Linux kernel. LKL
> also offers a host interface so that support for multiple hosts can be
> easily implemented.

I review most of code with the help of document and paper (2010).

I think LKL and LibOS are essentially the same thing.

I describe the current differences of both features, which I
believe there are no fundamental ones (i.e., both can
improve by putting some efforts).

- LKL
(beautiful) arch implementation (I like it)
fully kbuild compliant
rich fs support
host support: POSIX, win, haiku, etc

- LibOS
existing application integration
(semi-automated) system call table generation
multiple process support (via system call proxy)
various network backends (raw socket, DPDK, netmap, tap)
symbol namespace separation
host support: == rump hypercall (POSIX, xen,
qemu/kvm/baremetal(under development)), ns-3 simulator

# I can't find network support within the current patch but
there is/will be a certain code that LKL can play with
networking subsystem.

existing application integration is really important when
you want to configure network stack: since the configuration
of file systems is just a mount(), but configurations of
network stack need much userspace applications like iproute2
(ip, ss, tc) etc, which is not trivial to re-implement.


-- Hajime

Octavian Purdila

unread,
Nov 9, 2015, 10:20:06 AM11/9/15
to
On Sun, Nov 8, 2015 at 4:42 PM, yalin wang <yalin.w...@gmail.com> wrote:
>
> On Nov 4, 2015, at 07:06, Octavian Purdila <octavian...@intel.com>
> why not call sys_XXX() function directly?
> since kernel have implement lots of spin_locks to avoid race with normal
> path
> in IRQ handle , isn’t it ?
> for example, you timer IRQ can be simulated by SIGALARM signal,
> and the signal handler can check if IRQ is disabled ,
> if not , then continuing , otherwise , return directly ..
> it is not safe ?
>

Hi Yalin,

We need to have a proper Linux context in order to issue system calls
(e.g. current needs to point to a proper Linux kernel thread_struct).
We also want to let the Linux scheduler to select what kernel threads
run at a given time. Lets say that currently a ksoftirqd runs and the
application want to issue a system call. In this case we want to wait
for ksoftirqd to complete then run the system call.

The simplest way I found to do that is to have the system calls
execute from Linux kernel threads hence the need to queue the system
calls from the application thread to the Linux kernel system call
thread.

(BTW, we can have multiple system call threads if needed and the 2.6
implementation has that, but in order to simplify the review process I
decided to throw away that for the moment).

Thanks,
Tavi

Octavian Purdila

unread,
Nov 9, 2015, 11:40:07 AM11/9/15
to
On Sat, Nov 7, 2015 at 12:48 PM, Richard W.M. Jones <rjo...@redhat.com> wrote:
>
> I just pushed a (very early) WIP branch that contains changes to
> libguestfs to add an LKL backend:
>
> https://github.com/rwmjones/libguestfs/tree/lkl
>
> Read the README file in the libguestfs sources before starting,
> followed by the instructions in the commit message:
>
> https://github.com/rwmjones/libguestfs/commit/e38525f0b984d0a426f3348d95f2033673d4eaa4
>

Hi Richard,

Thanks for this, I was able to replicate it locally.

I started working on adding a new library, liblkl-redirect.a, that
redefines common libc symbols to use lkl. We then can link the daemon
with this lib and lkl to avoid ugly ifdefs in the daemon code.

Thanks,
Tavi
0 new messages