From 7b0379074c332567529aa92645d801d5b44a7b51 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Mon, 21 Sep 2015 18:43:07 +0300 Subject: [PATCH 01/47] checkpatch: avoid showing uint*_t warnings for tools/ files tools/ files are compiled in userspace so using types like uint8_t is legitimate. Signed-off-by: Octavian Purdila --- scripts/checkpatch.pl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index f2a1131b2f8baf..407eb6c9218404 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -5221,8 +5221,9 @@ sub process { "Using weak declarations can have unintended link defects\n" . $herecurr); } -# check for c99 types like uint8_t used outside of uapi/ +# check for c99 types like uint8_t used outside of uapi/ and tools/ if ($realfile !~ m@\binclude/uapi/@ && + $realfile !~ m@\btools/@ && $line =~ /\b($Declare)\s*$Ident\s*[=;,\[]/) { my $type = $1; if ($type =~ /\b($typeC99Typedefs)\b/) { From 2daea0404ed539d8077f5b6e29dfaeb55254f2b8 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Mon, 21 Sep 2015 18:49:19 +0300 Subject: [PATCH 02/47] checkpatch: avoid showing BIT_ULL warnings for tools/ files Directly using shift operations in userspace compiled code should not trigger warnings as BIT_ULL macros are not available outside the kernel. Signed-off-by: Octavian Purdila --- scripts/checkpatch.pl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 407eb6c9218404..93557bfea27510 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -5527,7 +5527,8 @@ sub process { if ($line =~ /#\s*define\s+\w+\s+\(?\s*1\s*([ulUL]*)\s*\<\<\s*(?:\d+|$Ident)\s*\)?/) { my $ull = ""; $ull = "_ULL" if (defined($1) && $1 =~ /ll/i); - if (CHK("BIT_MACRO", + if ($realfile !~ m@\btools/@ && + CHK("BIT_MACRO", "Prefer using the BIT$ull macro\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\(?\s*1\s*[ulUL]*\s*<<\s*(\d+|$Ident)\s*\)?/BIT${ull}($1)/; From d7f3cf092a729a8c2a35d1eac3fce6506c331163 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Thu, 23 Jul 2015 17:55:48 +0300 Subject: [PATCH 03/47] asm-generic: atomic64: allow using generic atomic64 on 64bit platforms Signed-off-by: Octavian Purdila --- 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 d48e78ccad3dd8..d0eb6cda66e336 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 00a5763e850e94..81814c81eacccd 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 #ifdef CONFIG_GENERIC_ATOMIC64 #include #endif +#include #ifndef atomic64_andnot static inline void atomic64_andnot(long long i, atomic64_t *v) From 0cc6fd28c6090cd85e21335c7755857f1ccacf6f Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Mon, 2 Nov 2015 15:09:28 +0200 Subject: [PATCH 04/47] kbuild: allow architectures to automatically define kconfig symbols 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 --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index d5b37391195f80..668b8fe527ce91 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),) From bdcd92b52d58b8ec0dfc5b23e8b4472535d18414 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Wed, 16 Sep 2015 00:43:18 +0300 Subject: [PATCH 05/47] lkl: architecture skeleton for Linux kernel library 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 --- 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 77ed3a02862588..e2a737fe654225 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 +S: Maintained +F: arch/lkl/ + LINUX SECURITY MODULE (LSM) FRAMEWORK M: Chris Wright L: linux-security-module@vger.kernel.org diff --git a/arch/lkl/.gitignore b/arch/lkl/.gitignore new file mode 100644 index 00000000000000..c619b7d9c7918f --- /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 00000000000000..064960befb742d --- /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 00000000000000..4bfd65a02d735e --- /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 00000000000000..90f385d1f253bd --- /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 00000000000000..55acf3fd1f6941 --- /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 00000000000000..282b081a67acbb --- /dev/null +++ b/arch/lkl/include/asm/bitsperlong.h @@ -0,0 +1,11 @@ +#ifndef __LKL_BITSPERLONG_H +#define __LKL_BITSPERLONG_H + +#include + +#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 00000000000000..19a5f0ee0eb77d --- /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 +#else +#include +#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 00000000000000..a2e0bd995422ac --- /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 + +#endif diff --git a/arch/lkl/include/asm/elf.h b/arch/lkl/include/asm/elf.h new file mode 100644 index 00000000000000..cada3ab5d4d9c5 --- /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 00000000000000..77c2c5316893ae --- /dev/null +++ b/arch/lkl/include/asm/mutex.h @@ -0,0 +1,7 @@ +#ifndef _ASM_LKL_MUTEX_H +#define _ASM_LKL_MUTEX_H + +#include + +#endif + diff --git a/arch/lkl/include/asm/processor.h b/arch/lkl/include/asm/processor.h new file mode 100644 index 00000000000000..7f6bdb41ea7d02 --- /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 00000000000000..f3c27e78d4fb88 --- /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 00000000000000..392c94a7850d8d --- /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 + +#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 00000000000000..cfa727b73dbb5f --- /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 00000000000000..12b522d0dbf9f8 --- /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 00000000000000..99b2d53fcd7b01 --- /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 + +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 00000000000000..9e263112a6e2f8 --- /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 00000000000000..44d47365bfdf1e --- /dev/null +++ b/arch/lkl/kernel/misc.c @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include +#include + +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 00000000000000..cf9692252b2a9a --- /dev/null +++ b/arch/lkl/kernel/vmlinux.lds.S @@ -0,0 +1,45 @@ +#include +#include +#include +#include + +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 +} From c37082e12177c4a19f76395ff35c8d222a7a9fb1 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Mon, 2 Nov 2015 16:39:06 +0200 Subject: [PATCH 06/47] lkl: host interface 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 --- 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 00000000000000..7fb0381ababf96 --- /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 + +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 00000000000000..e126154cbeaa4b --- /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 From 5456f358f57771f9e4b6ee99922010eeedb5372e Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Wed, 16 Sep 2015 00:59:54 +0300 Subject: [PATCH 07/47] lkl: memory handling 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 --- 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 00000000000000..455bf627f3be2f --- /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 + +#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 00000000000000..726675ad5e8006 --- /dev/null +++ b/arch/lkl/include/asm/pgtable.h @@ -0,0 +1,60 @@ +#ifndef _LKL_PGTABLE_H +#define _LKL_PGTABLE_H + +#include + +/* + * (C) Copyright 2000-2002, Greg Ungerer + */ + +#include +#include +#include + +#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 + +#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 00000000000000..225c2ccd5117eb --- /dev/null +++ b/arch/lkl/kernel/mem.c @@ -0,0 +1,67 @@ +#include +#include +#include + +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); +} From 359c42b78c3ba50f713495e6921e314d7c030b7c Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Wed, 16 Sep 2015 15:25:47 +0300 Subject: [PATCH 08/47] lkl: kernel threads support 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 --- 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 00000000000000..7636227a2f1658 --- /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 +#include + +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 00000000000000..aa13e57a3cf00c --- /dev/null +++ b/arch/lkl/kernel/threads.c @@ -0,0 +1,235 @@ +#include +#include +#include +#include + +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); +} From cbf0c5761c5bf4d898e4cb192339d1719ec47ef0 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Wed, 16 Sep 2015 15:52:29 +0300 Subject: [PATCH 09/47] lkl: interrupt support 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 --- 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 00000000000000..f4ceb5aa33983f --- /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 + +#endif diff --git a/arch/lkl/include/uapi/asm/irq.h b/arch/lkl/include/uapi/asm/irq.h new file mode 100644 index 00000000000000..a33f5c5ab1df96 --- /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 99b2d53fcd7b01..77aba401107bdd 100644 --- a/arch/lkl/include/uapi/asm/sigcontext.h +++ b/arch/lkl/include/uapi/asm/sigcontext.h @@ -4,6 +4,7 @@ #include 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 00000000000000..9ff0e12279d4b8 --- /dev/null +++ b/arch/lkl/kernel/irq.c @@ -0,0 +1,131 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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"); +} From f088db94e716c5e3c850bb85b0c0364c997a585c Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Wed, 16 Sep 2015 17:19:22 +0300 Subject: [PATCH 10/47] lkl: system call interface and application API 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 --- 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 00000000000000..73c1e7017fe6a6 --- /dev/null +++ b/arch/lkl/include/asm/unistd.h @@ -0,0 +1,93 @@ +#ifndef _ASM_LKL_UNISTD_H +#define _ASM_LKL_UNISTD_H + +#include + +/* + * 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 00000000000000..68b5423c8b4019 --- /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 +#include +#include +#include +#include +#include +#include +#define __KERNEL__ /* to pull in S_ definitions */ +#include +#undef __KERNEL__ +#include +#include +#include +#include + +/* 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 00000000000000..48b129674a1209 --- /dev/null +++ b/arch/lkl/kernel/syscalls.c @@ -0,0 +1,213 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 00000000000000..eb69a5e4098d86 --- /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() From bba49f9eebdf7d82bda1dfef7451b7f752180a45 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Wed, 16 Sep 2015 22:09:43 +0300 Subject: [PATCH 11/47] lkl: timers, time and delay support Clockevent driver based on host timer operations and clocksource driver and udelay support based on host time operations. Signed-off-by: Octavian Purdila --- 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 00000000000000..d099736e75dbbf --- /dev/null +++ b/arch/lkl/kernel/time.c @@ -0,0 +1,125 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +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"); +} From ea4fda5a730390e07f2e689f528ec12108d766f2 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Thu, 17 Sep 2015 14:36:24 +0300 Subject: [PATCH 12/47] lkl: memory mapped I/O support All memory mapped I/O access is redirected to the host via the iomem_access host operation. The host can setup the memory mapped I/O region via the ioremap operation. This allows the host to implement support for various devices, such as block or network devices. Signed-off-by: Octavian Purdila --- arch/lkl/include/asm/io.h | 104 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 arch/lkl/include/asm/io.h diff --git a/arch/lkl/include/asm/io.h b/arch/lkl/include/asm/io.h new file mode 100644 index 00000000000000..fd6f4afa961c14 --- /dev/null +++ b/arch/lkl/include/asm/io.h @@ -0,0 +1,104 @@ +#ifndef _ASM_LKL_IO_H +#define _ASM_LKL_IO_H + +#include +#include + +#define __raw_readb __raw_readb +static inline u8 __raw_readb(const volatile void __iomem *addr) +{ + int ret; + u8 value; + + ret = lkl_ops->iomem_access(addr, &value, sizeof(value), 0); + WARN(ret, "error reading iomem %p", addr); + + return value; +} + +#define __raw_readw __raw_readw +static inline u16 __raw_readw(const volatile void __iomem *addr) +{ + int ret; + u16 value; + + ret = lkl_ops->iomem_access(addr, &value, sizeof(value), 0); + WARN(ret, "error reading iomem %p", addr); + + return value; +} + +#define __raw_readl __raw_readl +static inline u32 __raw_readl(const volatile void __iomem *addr) +{ + int ret; + u32 value; + + ret = lkl_ops->iomem_access(addr, &value, sizeof(value), 0); + WARN(ret, "error reading iomem %p", addr); + + return value; +} + +#ifdef CONFIG_64BIT +#define __raw_readq __raw_readq +static inline u64 __raw_readq(const volatile void __iomem *addr) +{ + int ret; + u64 value; + + ret = lkl_ops->iomem_access(addr, &value, sizeof(value), 0); + WARN(ret, "error reading iomem %p", addr); + + return value; +} +#endif /* CONFIG_64BIT */ + +#define __raw_writeb __raw_writeb +static inline void __raw_writeb(u8 value, volatile void __iomem *addr) +{ + int ret; + + ret = lkl_ops->iomem_access(addr, &value, sizeof(value), 1); + WARN(ret, "error writing iomem %p", addr); +} + +#define __raw_writew __raw_writew +static inline void __raw_writew(u16 value, volatile void __iomem *addr) +{ + int ret; + + ret = lkl_ops->iomem_access(addr, &value, sizeof(value), 1); + WARN(ret, "error writing iomem %p", addr); +} + +#define __raw_writel __raw_writel +static inline void __raw_writel(u32 value, volatile void __iomem *addr) +{ + int ret; + + ret = lkl_ops->iomem_access(addr, &value, sizeof(value), 1); + WARN(ret, "error writing iomem %p", addr); +} + +#ifdef CONFIG_64BIT +#define __raw_writeq __raw_writeq +static inline void __raw_writeq(u64 value, volatile void __iomem *addr) +{ + int ret; + + ret = lkl_ops->iomem_access(addr, &value, sizeof(value), 1); + WARN(ret, "error writing iomem %p", addr); +} +#endif /* CONFIG_64BIT */ + +#define ioremap ioremap +static inline void __iomem *ioremap(phys_addr_t offset, size_t size) +{ + return (void __iomem *)lkl_ops->ioremap(offset, size); +} + +#include + +#endif /* _ASM_LKL_IO_H */ + From 6e8fb31f8ecb2f92e021e47cb21ea2639f78a045 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Thu, 17 Sep 2015 14:44:30 +0300 Subject: [PATCH 13/47] lkl: basic kernel console support Write operations are deferred to the host print operation. Signed-off-by: Octavian Purdila --- arch/lkl/kernel/console.c | 41 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 arch/lkl/kernel/console.c diff --git a/arch/lkl/kernel/console.c b/arch/lkl/kernel/console.c new file mode 100644 index 00000000000000..bd3a686810daed --- /dev/null +++ b/arch/lkl/kernel/console.c @@ -0,0 +1,41 @@ +#include +#include +#include +#include + +static void console_write(struct console *con, const char *str, unsigned len) +{ + if (lkl_ops->print) + lkl_ops->print(str, len); +} + +#ifdef CONFIG_LKL_EARLY_CONSOLE +static struct console lkl_boot_console = { + .name = "lkl_boot_console", + .write = console_write, + .flags = CON_PRINTBUFFER | CON_BOOT, + .index = -1, +}; + +int __init lkl_boot_console_init(void) +{ + register_console(&lkl_boot_console); + return 0; +} +early_initcall(lkl_boot_console_init); +#endif + +static struct console lkl_console = { + .name = "lkl_console", + .write = console_write, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + +int __init lkl_console_init(void) +{ + register_console(&lkl_console); + return 0; +} +core_initcall(lkl_console_init); + From 47493a47e95aa3975580e54281df5b84c88a0688 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Wed, 19 Aug 2015 16:18:52 +0300 Subject: [PATCH 14/47] init: allow architecture code to overide run_init_process This is needed for arch/lkl where where execve can not be implemented and init only runs in kernel space. Signed-off-by: Octavian Purdila --- init/main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/init/main.c b/init/main.c index 9e64d7097f1ad4..efd8f7cf2f5593 100644 --- a/init/main.c +++ b/init/main.c @@ -905,13 +905,15 @@ void __init load_default_modules(void) load_default_elevator_module(); } -static int run_init_process(const char *init_filename) +#ifndef ARCH_RUN_INIT_PROCESS +int run_init_process(const char *init_filename) { argv_init[0] = init_filename; return do_execve(getname_kernel(init_filename), (const char __user *const __user *)argv_init, (const char __user *const __user *)envp_init); } +#endif static int try_to_run_init_process(const char *init_filename) { From 1ee887eef2cc41d9b97b811bfcf210bb2911ba96 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Thu, 17 Sep 2015 14:50:14 +0300 Subject: [PATCH 15/47] lkl: initialization and cleanup 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 --- 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 00000000000000..b82cdbf328794e --- /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 00000000000000..aad2ad7d0cd830 --- /dev/null +++ b/arch/lkl/kernel/setup.c @@ -0,0 +1,176 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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); From a7ef6b638bd68fb1f772e957ea15eb87d2268b87 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Thu, 17 Sep 2015 15:57:39 +0300 Subject: [PATCH 16/47] lkl: plug in the build system Basic Makefiles for building LKL. Add a new architecture specific target for installing the resulting library files and headers. Signed-off-by: Octavian Purdila --- 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 00000000000000..75458301827966 --- /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 00000000000000..47036c0ed2d3e5 --- /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 From 4fc2010defdc81f20ca7e8337fe6a05a13191cb9 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Fri, 18 Sep 2015 13:56:48 +0300 Subject: [PATCH 17/47] lkl tools: skeleton for host side library, tests and tools This patch adds the skeleton for the host library, tests and application examples. The host library is implementing the host operations needed by LKL and is split into host dependent (depends on a specific host, e.g. POSIX hosts) and host independent parts (will work on all supported hosts). Signed-off-by: Octavian Purdila --- MAINTAINERS | 1 + tools/lkl/.gitignore | 0 tools/lkl/Makefile | 28 ++++++++++++++++++++++++++++ tools/lkl/include/.gitignore | 1 + tools/lkl/include/lkl.h | 15 +++++++++++++++ tools/lkl/include/lkl_host.h | 9 +++++++++ tools/lkl/lib/.gitignore | 3 +++ 7 files changed, 57 insertions(+) create mode 100644 tools/lkl/.gitignore create mode 100644 tools/lkl/Makefile 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 diff --git a/MAINTAINERS b/MAINTAINERS index e2a737fe654225..4e7866916c08cc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6359,6 +6359,7 @@ LINUX KERNEL LIBRARY M: Octavian Purdila S: Maintained F: arch/lkl/ +F: tools/lkl/ LINUX SECURITY MODULE (LSM) FRAMEWORK M: Chris Wright diff --git a/tools/lkl/.gitignore b/tools/lkl/.gitignore new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/tools/lkl/Makefile b/tools/lkl/Makefile new file mode 100644 index 00000000000000..b13472bfee0481 --- /dev/null +++ b/tools/lkl/Makefile @@ -0,0 +1,28 @@ +CFLAGS := -Iinclude -Wall -g + +ifdef CROSS_COMPILE +CC=$(CROSS_COMPILE)gcc +AR=$(CROSS_COMPILE)ar +endif + +lib_source = $(filter-out %-host.c,$(wildcard lib/*.c)) +lib_objs = $(patsubst %.c,%.o, $(lib_source)) lib/lkl.o + +all: lib/liblkl.a + +lib/liblkl.a: $(lib_objs) + $(AR) -rc $@ $^ + +lib/lkl.o: + $(MAKE) -C ../.. ARCH=lkl defconfig + $(MAKE) -C ../.. ARCH=lkl install INSTALL_PATH=$(PWD) + +%: %.o + $(CC) -o $@ $^ $(LDFLAGS) + +$(lib_objs): lib/lkl.o +$(objs): lib/lkl.o +$(execs): lib/liblkl.a + +clean: + -rm -rf include/lkl/ lib/liblkl.a $(lib_objs) diff --git a/tools/lkl/include/.gitignore b/tools/lkl/include/.gitignore new file mode 100644 index 00000000000000..c41a463c898d93 --- /dev/null +++ b/tools/lkl/include/.gitignore @@ -0,0 +1 @@ +lkl/ \ No newline at end of file diff --git a/tools/lkl/include/lkl.h b/tools/lkl/include/lkl.h new file mode 100644 index 00000000000000..2de5ed40b8f1e7 --- /dev/null +++ b/tools/lkl/include/lkl.h @@ -0,0 +1,15 @@ +#ifndef _LKL_H +#define _LKL_H + +#include + +/** + * lkl_sys_lseek - wrapper for lkl_sys_llseek + */ +static inline long lkl_sys_lseek(unsigned int fd, __lkl__kernel_loff_t off, + __lkl__kernel_loff_t *res, unsigned int whence) +{ + return lkl_sys_llseek(fd, off >> 32, off & 0xffffffff, res, whence); +} + +#endif diff --git a/tools/lkl/include/lkl_host.h b/tools/lkl/include/lkl_host.h new file mode 100644 index 00000000000000..8ee9ba073d159c --- /dev/null +++ b/tools/lkl/include/lkl_host.h @@ -0,0 +1,9 @@ +#ifndef _LKL_HOST_H +#define _LKL_HOST_H + +#include +#include + +extern struct lkl_host_operations lkl_host_ops; + +#endif diff --git a/tools/lkl/lib/.gitignore b/tools/lkl/lib/.gitignore new file mode 100644 index 00000000000000..427ae0273fdd99 --- /dev/null +++ b/tools/lkl/lib/.gitignore @@ -0,0 +1,3 @@ +lkl.o +liblkl.a + From d72f51598aaf28f17b6dc60de4b22614981c1b82 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Fri, 18 Sep 2015 14:12:37 +0300 Subject: [PATCH 18/47] lkl tools: host lib: add lkl_strerror and lkl_printf 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 --- 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 2de5ed40b8f1e7..958614dc762a8c 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 8ee9ba073d159c..9f1c2709f3be85 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 00000000000000..f8b676f983037a --- /dev/null +++ b/tools/lkl/lib/utils.c @@ -0,0 +1,177 @@ +#include +#include +#include + +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; +} From 1a8636cfb766131bda50689ba5de47578d7612c9 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Fri, 18 Sep 2015 14:28:25 +0300 Subject: [PATCH 19/47] lkl tools: host lib: memory mapped I/O helpers 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 --- 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 00000000000000..bef6b71e1ef324 --- /dev/null +++ b/tools/lkl/lib/iomem.c @@ -0,0 +1,119 @@ +#include +#include +#include + +#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 00000000000000..53707d7a6179c4 --- /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 */ From da15a1bb873c429464aa8d1077ea739b513a856f Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Fri, 18 Sep 2015 15:16:11 +0300 Subject: [PATCH 20/47] lkl tools: host lib: virtio devices Add helpers for implementing host virtio devices. It uses the memory mapped I/O helpers to interact with the Linux MMIO virtio transport driver and offers support to setup and add a new virtio device, dispatch requests from the incoming queues as well as support for completing requests. All added virtio devices are stored in lkl_virtio_devs as strings, per the Linux MMIO virtio transport driver command line specification. Signed-off-by: Octavian Purdila --- tools/lkl/include/lkl_host.h | 7 + tools/lkl/lib/virtio.c | 365 +++++++++++++++++++++++++++++++++++ tools/lkl/lib/virtio.h | 86 +++++++++ 3 files changed, 458 insertions(+) create mode 100644 tools/lkl/lib/virtio.c create mode 100644 tools/lkl/lib/virtio.h diff --git a/tools/lkl/include/lkl_host.h b/tools/lkl/include/lkl_host.h index 9f1c2709f3be85..26d3e437c3658b 100644 --- a/tools/lkl/include/lkl_host.h +++ b/tools/lkl/include/lkl_host.h @@ -13,4 +13,11 @@ extern struct lkl_host_operations lkl_host_ops; */ int lkl_printf(const char *fmt, ...); +char lkl_virtio_devs[256]; + +struct lkl_dev_buf { + void *addr; + unsigned int len; +}; + #endif diff --git a/tools/lkl/lib/virtio.c b/tools/lkl/lib/virtio.c new file mode 100644 index 00000000000000..034152e8e57082 --- /dev/null +++ b/tools/lkl/lib/virtio.c @@ -0,0 +1,365 @@ +#include +#include +#include +#include "iomem.h" +#include "virtio.h" + +#define VIRTIO_DEV_MAGIC 0x74726976 +#define VIRTIO_DEV_VERSION 2 + +#define VIRTIO_MMIO_MAGIC_VALUE 0x000 +#define VIRTIO_MMIO_VERSION 0x004 +#define VIRTIO_MMIO_DEVICE_ID 0x008 +#define VIRTIO_MMIO_VENDOR_ID 0x00c +#define VIRTIO_MMIO_DEVICE_FEATURES 0x010 +#define VIRTIO_MMIO_DEVICE_FEATURES_SEL 0x014 +#define VIRTIO_MMIO_DRIVER_FEATURES 0x020 +#define VIRTIO_MMIO_DRIVER_FEATURES_SEL 0x024 +#define VIRTIO_MMIO_QUEUE_SEL 0x030 +#define VIRTIO_MMIO_QUEUE_NUM_MAX 0x034 +#define VIRTIO_MMIO_QUEUE_NUM 0x038 +#define VIRTIO_MMIO_QUEUE_READY 0x044 +#define VIRTIO_MMIO_QUEUE_NOTIFY 0x050 +#define VIRTIO_MMIO_INTERRUPT_STATUS 0x060 +#define VIRTIO_MMIO_INTERRUPT_ACK 0x064 +#define VIRTIO_MMIO_STATUS 0x070 +#define VIRTIO_MMIO_QUEUE_DESC_LOW 0x080 +#define VIRTIO_MMIO_QUEUE_DESC_HIGH 0x084 +#define VIRTIO_MMIO_QUEUE_AVAIL_LOW 0x090 +#define VIRTIO_MMIO_QUEUE_AVAIL_HIGH 0x094 +#define VIRTIO_MMIO_QUEUE_USED_LOW 0x0a0 +#define VIRTIO_MMIO_QUEUE_USED_HIGH 0x0a4 +#define VIRTIO_MMIO_CONFIG_GENERATION 0x0fc +#define VIRTIO_MMIO_CONFIG 0x100 +#define VIRTIO_MMIO_INT_VRING 0x01 +#define VIRTIO_MMIO_INT_CONFIG 0x02 + +#define VIRTIO_DEV_STATUS_ACK 0x01 +#define VIRTIO_DEV_STATUS_DRV 0x02 +#define VIRTIO_DEV_STATUS_FEATURES_OK 0x08 +#define VIRTIO_DEV_STATUS_DRV_OK 0x04 +#define VIRTIO_DEV_STATUS_FAILED 0x80 + +#define VIRTIO_F_VERSION_1 (1ULL << 32) +#define VIRTIO_RING_F_EVENT_IDX (1ULL << 29) +#define VIRTIO_DESC_F_NEXT 1 + +static inline uint16_t virtio_get_used_event(struct virtio_queue *q) +{ + return q->avail->ring[q->num]; +} + +static inline void virtio_set_avail_event(struct virtio_queue *q, uint16_t val) +{ + *((uint16_t *)&q->used->ring[q->num]) = val; +} + +#define ring_entry(q, r, idx) \ + q->r->ring[idx & (q->num - 1)] + +static inline void virtio_deliver_irq(struct virtio_dev *dev) +{ + dev->int_status |= VIRTIO_MMIO_INT_VRING; + lkl_trigger_irq(dev->irq, NULL); +} + +void virtio_dev_complete(struct virtio_dev_req *req, uint32_t len) +{ + struct virtio_queue *q = req->q; + struct virtio_dev *dev = req->dev; + uint16_t idx = le16toh(q->used->idx) & (q->num - 1); + int send_irq = 0; + + q->used->ring[idx].id = htole16(req->desc_idx); + q->used->ring[idx].len = htole16(len); + if (virtio_get_used_event(q) == q->used->idx) + send_irq = 1; + q->used->idx = htole16(le16toh(q->used->idx) + 1); + + if (send_irq) + virtio_deliver_irq(dev); + + lkl_host_ops.mem_free(req); +} + +static void virtio_process_avail_one(struct virtio_dev *dev, + struct virtio_queue *q, + int avail_idx) +{ + int j; + uint16_t desc_idx; + struct virtio_desc *i; + struct virtio_dev_req *req = NULL; + + avail_idx = avail_idx & (q->num - 1); + desc_idx = le16toh(q->avail->ring[avail_idx]) & (q->num - 1); + + i = &q->desc[desc_idx]; + j = 1; + while (le16toh(i->flags) & VIRTIO_DESC_F_NEXT) { + desc_idx = le16toh(i->next) & (q->num - 1); + i = &q->desc[desc_idx]; + j++; + } + + req = lkl_host_ops.mem_alloc((uintptr_t)&req->buf[j]); + if (!req) + return; + + req->dev = dev; + req->q = q; + req->desc_idx = q->avail->ring[avail_idx]; + req->buf_count = j; + + desc_idx = le16toh(q->avail->ring[avail_idx]) & (q->num - 1); + i = &q->desc[desc_idx]; + j = 0; + req->buf[j].addr = (void *)(uintptr_t)le64toh(i->addr); + req->buf[j].len = le32toh(i->len); + while (le16toh(i->flags) & VIRTIO_DESC_F_NEXT) { + desc_idx = le16toh(i->next) & (q->num - 1); + i = &q->desc[desc_idx]; + j++; + req->buf[j].addr = (void *)(uintptr_t)le64toh(i->addr); + req->buf[j].len = le32toh(i->len); + } + + dev->ops->queue(dev, req); +} + +static void virtio_process_avail(struct virtio_dev *dev, uint32_t qidx) +{ + struct virtio_queue *q = &dev->queue[qidx]; + + virtio_set_avail_event(q, q->avail->idx); + + while (q->last_avail_idx != le16toh(q->avail->idx)) { + virtio_process_avail_one(dev, q, q->last_avail_idx); + q->last_avail_idx++; + } +} + +static inline uint32_t virtio_read_device_features(struct virtio_dev *dev) +{ + if (dev->device_features_sel) + return (uint32_t)(dev->device_features >> 32); + + return (uint32_t)dev->device_features; +} + +static inline void virtio_write_driver_features(struct virtio_dev *dev, + uint32_t val) +{ + uint64_t tmp; + + if (dev->driver_features_sel) { + tmp = dev->driver_features & 0xFFFFFFFF; + dev->driver_features = tmp | (uint64_t)val << 32; + } else { + tmp = dev->driver_features & 0xFFFFFFFF00000000; + dev->driver_features = tmp | val; + } +} + +static int virtio_read(void *data, int offset, void *res, int size) +{ + uint32_t val; + struct virtio_dev *dev = (struct virtio_dev *)data; + int ret = 0; + + if (offset >= VIRTIO_MMIO_CONFIG) { + offset -= VIRTIO_MMIO_CONFIG; + if (offset + size > dev->config_len) + return -LKL_EINVAL; + memcpy(res, dev->config_data + offset, size); + return 0; + } + + if (size != sizeof(uint32_t)) + return -LKL_EINVAL; + + switch (offset) { + case VIRTIO_MMIO_MAGIC_VALUE: + val = VIRTIO_DEV_MAGIC; + break; + case VIRTIO_MMIO_VERSION: + val = VIRTIO_DEV_VERSION; + break; + case VIRTIO_MMIO_DEVICE_ID: + val = dev->device_id; + break; + case VIRTIO_MMIO_VENDOR_ID: + val = dev->vendor_id; + break; + case VIRTIO_MMIO_DEVICE_FEATURES: + val = virtio_read_device_features(dev); + break; + case VIRTIO_MMIO_QUEUE_NUM_MAX: + val = dev->queue[dev->queue_sel].num_max; + break; + case VIRTIO_MMIO_QUEUE_READY: + val = dev->queue[dev->queue_sel].ready; + break; + case VIRTIO_MMIO_INTERRUPT_STATUS: + val = dev->int_status; + break; + case VIRTIO_MMIO_STATUS: + val = dev->status; + break; + case VIRTIO_MMIO_CONFIG_GENERATION: + val = dev->config_gen; + break; + default: + ret = -1; + } + + *(uint32_t *)res = htole32(val); + + return ret; +} + +static inline void set_ptr_low(void **ptr, uint32_t val) +{ + uint64_t tmp = (uintptr_t)*ptr; + + tmp = (tmp & 0xFFFFFFFF00000000) | val; + *ptr = (void *)(long)tmp; +} + +static inline void set_ptr_high(void **ptr, uint32_t val) +{ + uint64_t tmp = (uintptr_t)*ptr; + + tmp = (tmp & 0x00000000FFFFFFFF) | ((uint64_t)val << 32); + *ptr = (void *)(long)tmp; +} + +static int virtio_write(void *data, int offset, void *res, int size) +{ + struct virtio_dev *dev = (struct virtio_dev *)data; + struct virtio_queue *q = &dev->queue[dev->queue_sel]; + uint32_t val; + int ret = 0; + + if (offset >= VIRTIO_MMIO_CONFIG) { + offset -= VIRTIO_MMIO_CONFIG; + + if (offset + size >= dev->config_len) + return -LKL_EINVAL; + memcpy(dev->config_data + offset, res, size); + return 0; + } + + if (size != sizeof(uint32_t)) + return -LKL_EINVAL; + + val = le32toh(*(uint32_t *)res); + + switch (offset) { + case VIRTIO_MMIO_DEVICE_FEATURES_SEL: + if (val > 1) + return -LKL_EINVAL; + dev->device_features_sel = val; + break; + case VIRTIO_MMIO_DRIVER_FEATURES_SEL: + if (val > 1) + return -LKL_EINVAL; + dev->driver_features_sel = val; + break; + case VIRTIO_MMIO_DRIVER_FEATURES: + virtio_write_driver_features(dev, val); + break; + case VIRTIO_MMIO_QUEUE_SEL: + dev->queue_sel = val; + break; + case VIRTIO_MMIO_QUEUE_NUM: + dev->queue[dev->queue_sel].num = val; + break; + case VIRTIO_MMIO_QUEUE_READY: + dev->queue[dev->queue_sel].ready = val; + break; + case VIRTIO_MMIO_QUEUE_NOTIFY: + virtio_process_avail(dev, val); + break; + case VIRTIO_MMIO_INTERRUPT_ACK: + dev->int_status = 0; + break; + case VIRTIO_MMIO_STATUS: + if (val & VIRTIO_DEV_STATUS_FEATURES_OK && + (!(dev->driver_features & VIRTIO_F_VERSION_1) || + !(dev->driver_features & VIRTIO_RING_F_EVENT_IDX) || + dev->ops->check_features(dev->driver_features & 0xFFFFFF))) + val &= ~VIRTIO_DEV_STATUS_FEATURES_OK; + dev->status = val; + break; + case VIRTIO_MMIO_QUEUE_DESC_LOW: + set_ptr_low((void **)&q->desc, val); + break; + case VIRTIO_MMIO_QUEUE_DESC_HIGH: + set_ptr_high((void **)&q->desc, val); + break; + case VIRTIO_MMIO_QUEUE_AVAIL_LOW: + set_ptr_low((void **)&q->avail, val); + break; + case VIRTIO_MMIO_QUEUE_AVAIL_HIGH: + set_ptr_high((void **)&q->avail, val); + break; + case VIRTIO_MMIO_QUEUE_USED_LOW: + set_ptr_low((void **)&q->used, val); + break; + case VIRTIO_MMIO_QUEUE_USED_HIGH: + set_ptr_high((void **)&q->used, val); + break; + default: + ret = -1; + } + + return ret; +} + +static const struct lkl_iomem_ops virtio_ops = { + .read = virtio_read, + .write = virtio_write, +}; + +char lkl_virtio_devs[256]; +static char *devs = lkl_virtio_devs; + +int virtio_dev_setup(struct virtio_dev *dev, int queues, int num_max) +{ + int qsize = queues * sizeof(*dev->queue); + int ret, avail, mmio_size; + int i; + + dev->irq = lkl_get_free_irq("virtio"); + if (dev->irq < 0) + return dev->irq; + + dev->device_features |= VIRTIO_F_VERSION_1 | VIRTIO_RING_F_EVENT_IDX; + dev->queue = lkl_host_ops.mem_alloc(qsize); + if (!dev->queue) + return -LKL_ENOMEM; + + memset(dev->queue, 0, qsize); + for (i = 0; i < queues; i++) + dev->queue[i].num_max = num_max; + + mmio_size = VIRTIO_MMIO_CONFIG + dev->config_len; + ret = register_iomem(dev, mmio_size, &virtio_ops); + if (ret) + lkl_host_ops.mem_free(dev->queue); + + avail = sizeof(lkl_virtio_devs) - (devs - lkl_virtio_devs); + devs += snprintf(devs, avail, " virtio_mmio.device=%d@%p:%d", + mmio_size, dev, dev->irq); + + return ret; +} + +void virtio_dev_cleanup(struct virtio_dev *dev) +{ + lkl_put_irq(dev->irq, "virtio"); + unregister_iomem(dev); + lkl_host_ops.mem_free(dev->queue); +} + diff --git a/tools/lkl/lib/virtio.h b/tools/lkl/lib/virtio.h new file mode 100644 index 00000000000000..1bacbe62878b89 --- /dev/null +++ b/tools/lkl/lib/virtio.h @@ -0,0 +1,86 @@ +#ifndef _LKL_LIB_VIRTIO_H +#define _LKL_LIB_VIRTIO_H + +#include +#include + +struct virtio_desc { + uint64_t addr; + uint32_t len; + uint16_t flags; + uint16_t next; +}; + +struct virtio_avail { + uint16_t flags; + uint16_t idx; + uint16_t ring[]; +}; + +struct virtio_used_elem { + uint32_t id; + uint32_t len; +}; + +struct virtio_used { + uint16_t flags; + uint16_t idx; + struct virtio_used_elem ring[]; +}; + +struct virtio_queue { + uint32_t num_max; + uint32_t num; + uint32_t ready; + + struct virtio_desc *desc; + struct virtio_avail *avail; + struct virtio_used *used; + uint16_t last_avail_idx; + void *config_data; + int config_len; +}; + +struct virtio_dev_req { + struct virtio_dev *dev; + struct virtio_queue *q; + uint16_t desc_idx; + uint16_t buf_count; + struct lkl_dev_buf buf[]; +}; + +struct virtio_dev_ops { + int (*check_features)(uint32_t features); + void (*queue)(struct virtio_dev *dev, struct virtio_dev_req *req); +}; + +struct virtio_dev { + uint32_t device_id; + uint32_t vendor_id; + uint64_t device_features; + uint32_t device_features_sel; + uint64_t driver_features; + uint32_t driver_features_sel; + uint32_t queue_sel; + struct virtio_queue *queue; + uint32_t queue_notify; + uint32_t int_status; + uint32_t status; + uint32_t config_gen; + + struct virtio_dev_ops *ops; + int irq; + void *config_data; + int config_len; +}; + +int virtio_dev_setup(struct virtio_dev *dev, int queues, int num_max); +void virtio_dev_cleanup(struct virtio_dev *dev); +void virtio_dev_complete(struct virtio_dev_req *req, uint32_t len); + +#define container_of(ptr, type, member) \ + (type *)((char *)(ptr) - __builtin_offsetof(type, member)) + +#include + +#endif /* _LKL_LIB_VIRTIO_H */ From 059ac9c46b6bc86f96c1410d7e5e42fe3df6266a Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Fri, 18 Sep 2015 16:29:13 +0300 Subject: [PATCH 21/47] lkl tools: host lib: virtio block device Host independent implementation for virtio block devices. The host dependent part of the host library must provide an implementation for lkl_dev_block_ops. Disks can be added to the LKL configuration via lkl_disk_add(), a new LKL application API. Signed-off-by: Octavian Purdila --- tools/lkl/include/lkl.h | 20 ++++++ tools/lkl/include/lkl_host.h | 21 +++++++ tools/lkl/lib/virtio_blk.c | 116 +++++++++++++++++++++++++++++++++++ 3 files changed, 157 insertions(+) create mode 100644 tools/lkl/lib/virtio_blk.c diff --git a/tools/lkl/include/lkl.h b/tools/lkl/include/lkl.h index 958614dc762a8c..0c30b23cf4c6de 100644 --- a/tools/lkl/include/lkl.h +++ b/tools/lkl/include/lkl.h @@ -20,4 +20,24 @@ static inline long lkl_sys_lseek(unsigned int fd, __lkl__kernel_loff_t off, */ const char *lkl_strerror(int err); +/** + * lkl_disk_backstore - host dependend disk backstore + * + * @fd - an open file descriptor that can be used by preadv/pwritev; used by + * POSIX hosts + */ +union lkl_disk_backstore { + int fd; +}; + +/** + * lkl_disk_add - add a new disk + * + * Must be called before calling lkl_start_kernel. + * + * @backstore - the disk backstore + * @returns a disk id (0 is valid) or a strictly negative value in case of error + */ +int lkl_disk_add(union lkl_disk_backstore backstore); + #endif diff --git a/tools/lkl/include/lkl_host.h b/tools/lkl/include/lkl_host.h index 26d3e437c3658b..2dafaa803e9f77 100644 --- a/tools/lkl/include/lkl_host.h +++ b/tools/lkl/include/lkl_host.h @@ -20,4 +20,25 @@ struct lkl_dev_buf { unsigned int len; }; +extern struct lkl_dev_blk_ops lkl_dev_blk_ops; + +#define LKL_DEV_BLK_TYPE_READ 0 +#define LKL_DEV_BLK_TYPE_WRITE 1 +#define LKL_DEV_BLK_TYPE_FLUSH 4 +#define LKL_DEV_BLK_TYPE_FLUSH_OUT 5 + +struct lkl_dev_blk_ops { + int (*get_capacity)(union lkl_disk_backstore bs, + unsigned long long *res); + void (*request)(union lkl_disk_backstore bs, unsigned int type, + unsigned int prio, unsigned long long sector, + struct lkl_dev_buf *bufs, int count); +}; + +#define LKL_DEV_BLK_STATUS_OK 0 +#define LKL_DEV_BLK_STATUS_IOERR 1 +#define LKL_DEV_BLK_STATUS_UNSUP 2 + +void lkl_dev_blk_complete(struct lkl_dev_buf *bufs, unsigned char status, + int len); #endif diff --git a/tools/lkl/lib/virtio_blk.c b/tools/lkl/lib/virtio_blk.c new file mode 100644 index 00000000000000..3262f429c82f5a --- /dev/null +++ b/tools/lkl/lib/virtio_blk.c @@ -0,0 +1,116 @@ +#include +#include "virtio.h" + +struct virtio_blk_dev { + struct virtio_dev dev; + struct { + uint64_t capacity; + } config; + struct lkl_dev_blk_ops *ops; + union lkl_disk_backstore backstore; +}; + +struct virtio_blk_req_header { + uint32_t type; + uint32_t prio; + uint64_t sector; +}; + +struct virtio_blk_req_trailer { + uint8_t status; +}; + +static int blk_check_features(uint32_t features) +{ + if (!features) + return 0; + + return -LKL_EINVAL; +} + +void lkl_dev_blk_complete(struct lkl_dev_buf *bufs, unsigned char status, + int len) +{ + struct virtio_dev_req *req; + struct virtio_blk_req_trailer *f; + + req = container_of(bufs - 1, struct virtio_dev_req, buf); + + if (req->buf_count < 2) { + lkl_printf("virtio_blk: no status buf\n"); + return; + } + + if (req->buf[req->buf_count - 1].len != sizeof(*f)) { + lkl_printf("virtio_blk: bad status buf\n"); + } else { + f = req->buf[req->buf_count - 1].addr; + f->status = status; + } + + virtio_dev_complete(req, len); +} + +static void blk_queue(struct virtio_dev *dev, struct virtio_dev_req *req) +{ + struct virtio_blk_req_header *h; + struct virtio_blk_dev *blk_dev; + + if (req->buf[0].len != sizeof(struct virtio_blk_req_header)) { + lkl_printf("virtio_blk: bad header buf\n"); + lkl_dev_blk_complete(&req->buf[1], LKL_DEV_BLK_STATUS_UNSUP, 0); + return; + } + + h = req->buf[0].addr; + blk_dev = container_of(dev, struct virtio_blk_dev, dev); + + blk_dev->ops->request(blk_dev->backstore, le32toh(h->type), + le32toh(h->prio), le32toh(h->sector), + &req->buf[1], req->buf_count - 2); +} + +static struct virtio_dev_ops blk_ops = { + .check_features = blk_check_features, + .queue = blk_queue, +}; + +int lkl_disk_add(union lkl_disk_backstore backstore) +{ + struct virtio_blk_dev *dev; + unsigned long long capacity; + int ret; + static int count; + + dev = lkl_host_ops.mem_alloc(sizeof(*dev)); + if (!dev) + return -LKL_ENOMEM; + + dev->dev.device_id = 2; + dev->dev.vendor_id = 0; + dev->dev.device_features = 0; + dev->dev.config_gen = 0; + dev->dev.config_data = &dev->config; + dev->dev.config_len = sizeof(dev->config); + dev->dev.ops = &blk_ops; + dev->ops = &lkl_dev_blk_ops; + dev->backstore = backstore; + + ret = dev->ops->get_capacity(backstore, &capacity); + if (ret) { + ret = -LKL_ENOMEM; + goto out_free; + } + dev->config.capacity = capacity; + + ret = virtio_dev_setup(&dev->dev, 1, 65536); + if (ret) + goto out_free; + + return count++; + +out_free: + lkl_host_ops.mem_free(dev); + + return ret; +} From 5fd23eab363a87a4c2029369b6313f0c6f7d85d1 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Fri, 18 Sep 2015 16:44:22 +0300 Subject: [PATCH 22/47] lkl tools: host lib: filesystem helpers Add LKL applications APIs to mount and unmount a filesystem from a disk added via lkl_disk_add(). Also add open/close/read directory wrappers on top of lkl_sys_getdents64. Signed-off-by: Octavian Purdila --- tools/lkl/include/lkl.h | 66 ++++++++++++ tools/lkl/lib/fs.c | 218 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 284 insertions(+) create mode 100644 tools/lkl/lib/fs.c diff --git a/tools/lkl/include/lkl.h b/tools/lkl/include/lkl.h index 0c30b23cf4c6de..e6a9c777b498aa 100644 --- a/tools/lkl/include/lkl.h +++ b/tools/lkl/include/lkl.h @@ -40,4 +40,70 @@ union lkl_disk_backstore { */ int lkl_disk_add(union lkl_disk_backstore backstore); +/** + * lkl_mount_dev - mount a disk + * + * This functions creates a device file for the given disk, creates a mount + * point and mounts the device over the mount point. + * + * @disk_id - the disk id identifying the disk to be mounted + * @fs_type - filesystem type + * @flags - mount flags + * @data - additional filesystem specific mount data + * @mnt_str - a string that will be filled by this function with the path where + * the filisystem has been mounted + * @mnt_str_len - size of mnt_str + * @returns - 0 on success, a negative value on error + */ +long lkl_mount_dev(unsigned int disk_id, const char *fs_type, int flags, + void *data, char *mnt_str, int mnt_str_len); + +/** + * lkl_umount_dev - umount a disk + * + * This functions umounts the given disks and removes the device file and the + * mount point. + * + * @disk_id - the disk id identifying the disk to be mounted + * @flags - umount flags + * @timeout_ms - timeout to wait for the kernel to flush closed files so that + * umount can succeed + * @returns - 0 on success, a negative value on error + */ +long lkl_umount_dev(unsigned int disk_id, int flags, long timeout_ms); + +/** + * lkl_opendir - open a directory + * + * @path - directory path + * @err - pointer to store the error in case of failure + * @returns - a handle to be used when calling lkl_readdir + */ +struct lkl_dir *lkl_opendir(const char *path, int *err); + +/** + * lkl_closedir - close the directory + * + * @dir - the directory handler as returned by lkl_opendir + */ +void lkl_closedir(struct lkl_dir *dir); + +/** + * lkl_readdir - get the next available entry of the directory + * + * @dir - the directory handler as returned by lkl_opendir + * @returns - a lkl_dirent64 entry or NULL if the end of the directory stream is + * reached or if an error occurred; check lkl_errdir() to distinguish between + * errors or end of the directory stream + */ +struct lkl_dirent64 *lkl_readdir(struct lkl_dir *dir); + +/** + * lkl_errdir - checks if an error occurred during the last lkl_readdir call + * + * @dir - the directory handler as returned by lkl_opendir + * @returns - 0 if no error occurred, or a negative value otherwise + */ +int lkl_errdir(struct lkl_dir *dir); + #endif diff --git a/tools/lkl/lib/fs.c b/tools/lkl/lib/fs.c new file mode 100644 index 00000000000000..16f4d44156855b --- /dev/null +++ b/tools/lkl/lib/fs.c @@ -0,0 +1,218 @@ +#include +#include +#include + +long lkl_mount_sysfs(void) +{ + long ret; + static int sysfs_mounted; + + if (sysfs_mounted) + return 0; + + ret = lkl_sys_mkdir("/sys", 0700); + if (ret) + return ret; + + ret = lkl_sys_mount("none", "sys", "sysfs", 0, NULL); + + if (ret == 0) + sysfs_mounted = 1; + + return ret; +} + +static long get_virtio_blkdev(int disk_id) +{ + char sysfs_path[] = "/sys/block/vda/dev"; + char buf[16] = { 0, }; + long fd, ret; + int major, minor; + + ret = lkl_mount_sysfs(); + if (ret) + return ret; + + sysfs_path[strlen("/sys/block/vd")] += disk_id; + + fd = lkl_sys_open(sysfs_path, LKL_O_RDONLY, 0); + if (fd < 0) + return fd; + + ret = lkl_sys_read(fd, buf, sizeof(buf)); + if (ret < 0) + goto out_close; + + if (ret == sizeof(buf)) { + ret = -LKL_ENOBUFS; + goto out_close; + } + + ret = sscanf(buf, "%d:%d", &major, &minor); + if (ret != 2) { + ret = -LKL_EINVAL; + goto out_close; + } + + ret = LKL_MKDEV(major, minor); + +out_close: + lkl_sys_close(fd); + + return ret; +} + +long lkl_mount_dev(unsigned int disk_id, const char *fs_type, int flags, + void *data, char *mnt_str, int mnt_str_len) +{ + char dev_str[] = { "/dev/xxxxxxxx" }; + unsigned int dev; + int err; + + if (mnt_str_len < sizeof("/mnt/xxxxxxxx")) + return -LKL_ENOMEM; + + dev = get_virtio_blkdev(disk_id); + + snprintf(dev_str, sizeof(dev_str), "/dev/%08x", dev); + snprintf(mnt_str, mnt_str_len, "/mnt/%08x", dev); + + err = lkl_sys_access("/dev", LKL_S_IRWXO); + if (err < 0) { + if (err == -LKL_ENOENT) + err = lkl_sys_mkdir("/dev", 0700); + if (err < 0) + return err; + } + + err = lkl_sys_mknod(dev_str, LKL_S_IFBLK | 0600, dev); + if (err < 0) + return err; + + err = lkl_sys_access("/mnt", LKL_S_IRWXO); + if (err < 0) { + if (err == -LKL_ENOENT) + err = lkl_sys_mkdir("/mnt", 0700); + if (err < 0) + return err; + } + + err = lkl_sys_mkdir(mnt_str, 0700); + if (err < 0) { + lkl_sys_unlink(dev_str); + return err; + } + + err = lkl_sys_mount(dev_str, mnt_str, fs_type, flags, data); + if (err < 0) { + lkl_sys_unlink(dev_str); + lkl_sys_rmdir(mnt_str); + return err; + } + + return 0; +} + +long lkl_umount_dev(unsigned int disk_id, int flags, long timeout_ms) +{ + char dev_str[] = { "/dev/xxxxxxxx" }; + char mnt_str[] = { "/mnt/xxxxxxxx" }; + long incr = 10000000; /* 10 ms */ + struct lkl_timespec ts = { + .tv_sec = 0, + .tv_nsec = incr, + }; + unsigned int dev; + int err; + + dev = get_virtio_blkdev(disk_id); + + snprintf(dev_str, sizeof(dev_str), "/dev/%08x", dev); + snprintf(mnt_str, sizeof(mnt_str), "/mnt/%08x", dev); + + do { + err = lkl_sys_umount(mnt_str, flags); + if (err == -LKL_EBUSY) { + lkl_sys_nanosleep(&ts, NULL); + timeout_ms -= incr / 1000000; + } + } while (err == -LKL_EBUSY && timeout_ms > 0); + + if (err) + return err; + + err = lkl_sys_unlink(dev_str); + if (err) + return err; + + return lkl_sys_rmdir(mnt_str); +} + +struct lkl_dir { + int fd; + char buf[1024]; + char *pos; + int len; +}; + +struct lkl_dir *lkl_opendir(const char *path, int *err) +{ + struct lkl_dir *dir = lkl_host_ops.mem_alloc(sizeof(struct lkl_dir)); + + if (!dir) { + *err = -LKL_ENOMEM; + return NULL; + } + + dir->fd = lkl_sys_open(path, LKL_O_RDONLY | LKL_O_DIRECTORY, 0); + if (dir->fd < 0) { + *err = dir->fd; + lkl_host_ops.mem_free(dir); + return NULL; + } + + dir->len = 0; + dir->pos = NULL; + + return dir; +} + +void lkl_closedir(struct lkl_dir *dir) +{ + lkl_sys_close(dir->fd); + lkl_host_ops.mem_free(dir); +} + +struct lkl_dirent64 *lkl_readdir(struct lkl_dir *dir) +{ + struct lkl_dirent64 *de; + + if (dir->len < 0) + return NULL; + + if (!dir->pos || dir->pos - dir->buf >= dir->len) + goto read_buf; + +return_de: + de = (struct lkl_dirent64 *)dir->pos; + dir->pos += de->d_reclen; + + return de; + +read_buf: + dir->pos = NULL; + dir->len = lkl_sys_getdents64(dir->fd, dir->buf, sizeof(dir->buf)); + if (dir->len <= 0) + return NULL; + + dir->pos = dir->buf; + goto return_de; +} + +int lkl_errdir(struct lkl_dir *dir) +{ + if (dir->len >= 0) + return 0; + + return dir->len; +} From 2cd5d370deb54bda953ff8325dc4dca549391d14 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Fri, 18 Sep 2015 17:02:49 +0300 Subject: [PATCH 23/47] lkl tools: host lib: posix host operations Implement LKL host operations for POSIX hosts. Signed-off-by: Octavian Purdila --- 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 b13472bfee0481..cf97d27bfd3aee 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 00000000000000..4bc1de70c3d715 --- /dev/null +++ b/tools/lkl/lib/posix-host.c @@ -0,0 +1,206 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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, +}; From 461a58d65f8ef0e697fb171f8c0d388e171a4067 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Fri, 18 Sep 2015 17:11:25 +0300 Subject: [PATCH 24/47] lkl tools: "boot" test 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 --- 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 e69de29bb2d1d6..7c456f2b6fb855 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 cf97d27bfd3aee..1ae4481a18c171 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 00000000000000..f5945aac86f1f5 --- /dev/null +++ b/tools/lkl/tests/boot.c @@ -0,0 +1,488 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 00000000000000..3fb7b1f4785f4d --- /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 From 6c52ac5ba5d18c6a491ca24bf56757a31bb78bdf Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Fri, 18 Sep 2015 17:14:58 +0300 Subject: [PATCH 25/47] lkl tools: tool that converts a filesystem image to tar Simple utility that converts a filesystem image to a tar file, preserving file rights and extended attributes. Signed-off-by: Octavian Purdila --- tools/lkl/.gitignore | 1 + tools/lkl/Makefile | 3 + tools/lkl/fs2tar.c | 397 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 401 insertions(+) create mode 100644 tools/lkl/fs2tar.c diff --git a/tools/lkl/.gitignore b/tools/lkl/.gitignore index 7c456f2b6fb855..a345a79c54935a 100644 --- a/tools/lkl/.gitignore +++ b/tools/lkl/.gitignore @@ -1 +1,2 @@ test/boot +fs2tar diff --git a/tools/lkl/Makefile b/tools/lkl/Makefile index 1ae4481a18c171..9aeab49ba6efdc 100644 --- a/tools/lkl/Makefile +++ b/tools/lkl/Makefile @@ -9,6 +9,7 @@ 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)) +source += $(wildcard *.c) lib_source += lib/posix-host.c LDFLAGS += -lpthread -lrt endif @@ -35,3 +36,5 @@ $(execs): lib/liblkl.a clean: -rm -rf include/lkl/ lib/liblkl.a $(lib_objs) $(objs) $(execs) + +fs2tar: LDFLAGS += -larchive diff --git a/tools/lkl/fs2tar.c b/tools/lkl/fs2tar.c new file mode 100644 index 00000000000000..b74da6628fd85f --- /dev/null +++ b/tools/lkl/fs2tar.c @@ -0,0 +1,397 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#undef st_atime +#undef st_mtime +#undef st_ctime +#include +#include + +char doc[] = ""; +char args_doc[] = "-t fstype fsimage_path tar_path"; +static struct argp_option options[] = { + {"enable-printk", 'p', 0, 0, "show Linux printks"}, + {"filesystem-type", 't', "string", 0, + "select filesystem type - mandatory"}, + {"selinux-contexts", 's', "file", 0, + "export sexlinux contexts to file"}, + {0}, +}; + +static struct cl_args { + int printk; + const char *fsimg_type; + const char *fsimg_path; + const char *tar_path; + FILE *selinux; +} cla; + +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 's': + cla->selinux = fopen(arg, "w"); + if (!cla->selinux) { + fprintf(stderr, "failed to open selinux contexts file: %s\n", + strerror(errno)); + return -1; + } + break; + case ARGP_KEY_ARG: + if (!cla->fsimg_path) + cla->fsimg_path = arg; + else if (!cla->tar_path) + cla->tar_path = arg; + else + return -1; + break; + case ARGP_KEY_END: + if (state->arg_num < 2 || !cla->fsimg_type) + argp_usage(state); + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +static struct argp argp = { options, parse_opt, args_doc, doc }; + +static struct archive *tar; + +static int searchdir(const char *fsimg_path, const char *path); + +static int copy_file(const char *fsimg_path, const char *path) +{ + long fsimg_fd; + char buff[4096]; + long len, wrote; + int ret = 0; + + fsimg_fd = lkl_sys_open(fsimg_path, LKL_O_RDONLY, 0); + if (fsimg_fd < 0) { + fprintf(stderr, "fsimg error opening %s: %s\n", fsimg_path, + lkl_strerror(fsimg_fd)); + return fsimg_fd; + } + + do { + len = lkl_sys_read(fsimg_fd, buff, sizeof(buff)); + if (len > 0) { + wrote = archive_write_data(tar, buff, len); + if (wrote != len) { + fprintf(stderr, "error writing file %s to archive: %s [%d %ld]\n", + path, archive_error_string(tar), ret, + len); + ret = -archive_errno(tar); + break; + } + } + + if (len < 0) { + fprintf(stderr, "error reading fsimg file %s: %s\n", + fsimg_path, lkl_strerror(len)); + ret = len; + } + + } while (len > 0); + + lkl_sys_close(fsimg_fd); + + return ret; +} + +static int add_link(const char *fsimg_path, const char *path, + struct archive_entry *entry) +{ + char buf[4096] = { 0, }; + long len; + + len = lkl_sys_readlink(fsimg_path, buf, sizeof(buf)); + if (len < 0) { + fprintf(stderr, "fsimg readlink error %s: %s\n", + fsimg_path, lkl_strerror(len)); + return len; + } + + archive_entry_set_symlink(entry, buf); + + return 0; +} + +static inline void fsimg_copy_stat(struct stat *st, struct lkl_stat64 *fst) +{ + st->st_dev = fst->st_dev; + st->st_ino = fst->st_ino; + st->st_mode = fst->st_mode; + st->st_nlink = fst->st_nlink; + st->st_uid = fst->st_uid; + st->st_gid = fst->st_gid; + st->st_rdev = fst->st_rdev; + st->st_size = fst->st_size; + st->st_blksize = fst->st_blksize; + st->st_blocks = fst->st_blocks; + st->st_atim.tv_sec = fst->st_atime; + st->st_atim.tv_nsec = fst->st_atime_nsec; + st->st_mtim.tv_sec = fst->st_mtime; + st->st_mtim.tv_nsec = fst->st_mtime_nsec; + st->st_ctim.tv_sec = fst->st_ctime; + st->st_ctim.tv_nsec = fst->st_ctime_nsec; +} + +static int copy_xattr(const char *fsimg_path, const char *path, + struct archive_entry *entry) +{ + long ret; + char *xattr_list, *i; + long xattr_list_size; + + ret = lkl_sys_llistxattr(fsimg_path, NULL, 0); + if (ret < 0) { + fprintf(stderr, "fsimg llistxattr(%s) error: %s\n", + path, lkl_strerror(ret)); + return ret; + } + + if (!ret) + return 0; + + xattr_list = malloc(ret); + + ret = lkl_sys_llistxattr(fsimg_path, xattr_list, ret); + if (ret < 0) { + fprintf(stderr, "fsimg llistxattr(%s) error: %s\n", path, + lkl_strerror(ret)); + free(xattr_list); + return ret; + } + + xattr_list_size = ret; + + for (i = xattr_list; i - xattr_list < xattr_list_size; + i += strlen(i) + 1) { + void *xattr_buf; + + ret = lkl_sys_lgetxattr(fsimg_path, i, NULL, 0); + if (ret < 0) { + fprintf(stderr, "fsimg lgetxattr(%s) error: %s\n", path, + lkl_strerror(ret)); + free(xattr_list); + return ret; + } + + xattr_buf = malloc(ret); + + ret = lkl_sys_lgetxattr(fsimg_path, i, xattr_buf, ret); + if (ret < 0) { + fprintf(stderr, "fsimg lgetxattr2(%s) error: %s\n", + path, lkl_strerror(ret)); + free(xattr_list); + free(xattr_buf); + return ret; + } + + if (cla.selinux && strcmp(i, "security.selinux") == 0) + fprintf(cla.selinux, "%s %s\n", path, + (char *)xattr_buf); + + archive_entry_xattr_clear(entry); + archive_entry_xattr_add_entry(entry, i, xattr_buf, ret); + + free(xattr_buf); + } + + free(xattr_list); + + return 0; +} + +static int do_entry(const char *fsimg_path, const char *path, + const struct lkl_dirent64 *de) +{ + char fsimg_new_path[PATH_MAX], new_path[PATH_MAX]; + struct lkl_stat64 fsimg_stat; + struct stat stat; + struct archive_entry *entry; + int ftype; + long ret; + + snprintf(new_path, sizeof(new_path), "%s/%s", path, de->d_name); + snprintf(fsimg_new_path, sizeof(fsimg_new_path), "%s/%s", fsimg_path, + de->d_name); + + ret = lkl_sys_lstat64(fsimg_new_path, &fsimg_stat); + if (ret) { + fprintf(stderr, "fsimg fstat64(%s) error: %s\n", + path, lkl_strerror(ret)); + return ret; + } + + entry = archive_entry_new(); + + archive_entry_set_pathname(entry, new_path); + fsimg_copy_stat(&stat, &fsimg_stat); + archive_entry_copy_stat(entry, &stat); + ret = copy_xattr(fsimg_new_path, new_path, entry); + if (ret) + return ret; + /* TODO: ACLs */ + + ftype = stat.st_mode & S_IFMT; + + switch (ftype) { + case S_IFREG: + archive_write_header(tar, entry); + ret = copy_file(fsimg_new_path, new_path); + break; + case S_IFDIR: + archive_write_header(tar, entry); + ret = searchdir(fsimg_new_path, new_path); + break; + case S_IFLNK: + ret = add_link(fsimg_new_path, new_path, entry); + /* fall through */ + case S_IFSOCK: + case S_IFBLK: + case S_IFCHR: + case S_IFIFO: + if (ret) + break; + archive_write_header(tar, entry); + break; + default: + printf("skipping %s: unsupported entry type %d\n", new_path, + ftype); + } + + archive_entry_free(entry); + + if (ret) + printf("error processing entry %s, aborting\n", new_path); + + return ret; +} + +static int searchdir(const char *fsimg_path, const char *path) +{ + long ret, fd; + char buf[1024], *pos; + long buf_len; + + fd = lkl_sys_open(fsimg_path, LKL_O_RDONLY | LKL_O_DIRECTORY, 0); + if (fd < 0) { + fprintf(stderr, "failed to open dir %s: %s", fsimg_path, + lkl_strerror(fd)); + return fd; + } + + do { + struct lkl_dirent64 *de; + + buf_len = lkl_sys_getdents64(fd, buf, sizeof(buf)); + if (buf_len < 0) { + fprintf(stderr, "gentdents64 error: %s\n", + lkl_strerror(buf_len)); + break; + } + + for (pos = buf; pos - buf < buf_len; pos += de->d_reclen) { + de = (struct lkl_dirent64 *)pos; + + if (!strcmp(de->d_name, ".") || + !strcmp(de->d_name, "..")) + continue; + + ret = do_entry(fsimg_path, path, de); + if (ret) + goto out; + } + + } while (buf_len > 0); + +out: + lkl_sys_close(fd); + return ret; +} + +int main(int argc, char **argv) +{ + union lkl_disk_backstore bs; + long ret; + char mpoint[32]; + unsigned int disk_id; + + if (argp_parse(&argp, argc, argv, 0, 0, &cla) < 0) + return -1; + + if (!cla.printk) + lkl_host_ops.print = NULL; + + bs.fd = open(cla.fsimg_path, 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, 10 * 1024 * 1024, ""); + + ret = lkl_mount_dev(disk_id, cla.fsimg_type, LKL_MS_RDONLY, NULL, + mpoint, sizeof(mpoint)); + if (ret) { + fprintf(stderr, "can't mount disk: %s\n", lkl_strerror(ret)); + goto out_close; + } + + ret = lkl_sys_chdir(mpoint); + if (ret) { + fprintf(stderr, "can't chdir to %s: %s\n", mpoint, + lkl_strerror(ret)); + goto out_umount; + } + + tar = archive_write_new(); + archive_write_set_format_pax_restricted(tar); + archive_write_open_filename(tar, cla.tar_path); + + ret = searchdir(mpoint, ""); + + archive_write_free(tar); + + if (cla.selinux) + fclose(cla.selinux); + +out_umount: + lkl_umount_dev(disk_id, 0, 1000); + +out_close: + close(bs.fd); + +out: + lkl_sys_halt(); + + return ret; +} From c8e0586d3bb70a68f9f82b0a973d1edd3107add2 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Fri, 18 Sep 2015 17:27:20 +0300 Subject: [PATCH 26/47] lkl tools: tool that reads/writes to/from a filesystem image Signed-off-by: Octavian Purdila --- 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 a345a79c54935a..10483238adc146 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 9aeab49ba6efdc..4084609c6fc25f 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 00000000000000..0017395eacf119 --- /dev/null +++ b/tools/lkl/cptofs.c @@ -0,0 +1,467 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#undef st_atime +#undef st_mtime +#undef st_ctime +#include +#include +#include + +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; +} From e2b0cf0125181e9ee5d432803b9a23ef91c1b29b Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Tue, 29 Sep 2015 17:11:48 +0300 Subject: [PATCH 27/47] signal: use CONFIG_X86_32 instead of __i386__ Signed-off-by: Octavian Purdila --- kernel/signal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/signal.c b/kernel/signal.c index 0f6bbbe77b46c0..03671315324920 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; From 4145718a5ae5c74a3b0c433de31adb0d42a3ebe3 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Tue, 27 Oct 2015 12:51:14 +0200 Subject: [PATCH 28/47] asm-generic: vmlinux.lds.h: allow customized rodata section name 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 --- 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 392c94a7850d8d..7c1a640fb6e306 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 -#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 1781e54ea6d307..8bd8b90b969346 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) = .; \ From c9411ed0d0d62014d663ef5e62b3d248ccf37589 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Tue, 3 Nov 2015 14:30:32 +0200 Subject: [PATCH 29/47] lkl: add support for Windows hosts This patch allows LKL to be compiled for windows hosts with the mingw toolchain. Note that patches [1] that fix weak symbols linking are required to successfully compile LKL with mingw. The patch disables the modpost pass over vmlinux since modpost only works with ELF objects. It also adds and workaround to an #include_next error which is apparently caused by using -nosdtinc. [1] https://sourceware.org/ml/binutils/2015-10/msg00234.html Signed-off-by: Octavian Purdila --- arch/lkl/Kconfig | 1 + arch/lkl/Makefile | 4 ++++ arch/lkl/include/system/stdarg.h | 1 + include/linux/compiler-gcc.h | 4 ++++ scripts/Makefile | 2 ++ scripts/link-vmlinux.sh | 12 +++++++----- 6 files changed, 19 insertions(+), 5 deletions(-) create mode 100644 arch/lkl/include/system/stdarg.h diff --git a/arch/lkl/Kconfig b/arch/lkl/Kconfig index 064960befb742d..567533bd0004c2 100644 --- a/arch/lkl/Kconfig +++ b/arch/lkl/Kconfig @@ -18,6 +18,7 @@ config LKL select ARCH_WANT_FRAME_POINTERS select PHYS_ADDR_T_64BIT if 64BIT select 64BIT if OUTPUT_FORMAT = "elf64-x86-64" + select HAVE_UNDERSCORE_SYMBOL_PREFIX if OUTPUT_FORMAT = "pe-i386" config OUTPUTFORMAT string diff --git a/arch/lkl/Makefile b/arch/lkl/Makefile index 75458301827966..51ad096f6ee99a 100644 --- a/arch/lkl/Makefile +++ b/arch/lkl/Makefile @@ -4,6 +4,10 @@ KBUILD_CFLAGS += -fno-builtin ifeq ($(OUTPUT_FORMAT),elf64-x86-64) KBUILD_CFLAGS += -fPIC +else ifeq ($(OUTPUT_FORMAT),pe-i386) +prefix=_ +# workaround for #include_next errors +LINUXINCLUDE := -isystem arch/lkl/include/system $(LINUXINCLUDE) endif LDFLAGS_vmlinux += -r diff --git a/arch/lkl/include/system/stdarg.h b/arch/lkl/include/system/stdarg.h new file mode 100644 index 00000000000000..db463c50ea1ffe --- /dev/null +++ b/arch/lkl/include/system/stdarg.h @@ -0,0 +1 @@ +/* empty file to avoid #include_next error */ diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h index 8efb40e61d6e48..87ddae7d5b38ff 100644 --- a/include/linux/compiler-gcc.h +++ b/include/linux/compiler-gcc.h @@ -116,7 +116,11 @@ */ #define __pure __attribute__((pure)) #define __aligned(x) __attribute__((aligned(x))) +#ifdef __MINGW32__ +#define __printf(a, b) __attribute__((format(gnu_printf, a, b))) +#else #define __printf(a, b) __attribute__((format(printf, a, b))) +#endif #define __scanf(a, b) __attribute__((format(scanf, a, b))) #define __attribute_const__ __attribute__((__const__)) #define __maybe_unused __attribute__((unused)) diff --git a/scripts/Makefile b/scripts/Makefile index 1b2661712d449a..3036a7b6f835ff 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -37,7 +37,9 @@ build_docproc: $(obj)/docproc @: subdir-$(CONFIG_MODVERSIONS) += genksyms +ifeq ($(findstring elf,$(if $(CONFIG_OUTPUT_FORMAT),$(CONFIG_OUTPUT_FORMAT),elf)),elf) subdir-y += mod +endif subdir-$(CONFIG_SECURITY_SELINUX) += selinux subdir-$(CONFIG_DTC) += dtc subdir-$(CONFIG_GDB_SCRIPTS) += gdb diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh index 1a10d8ac81620f..f9e758699ddbf8 100755 --- a/scripts/link-vmlinux.sh +++ b/scripts/link-vmlinux.sh @@ -161,12 +161,14 @@ case "${KCONFIG_CONFIG}" in . "./${KCONFIG_CONFIG}" esac -#link vmlinux.o -info LD vmlinux.o -modpost_link vmlinux.o +if [ -e scripts/mod/modpost ]; then + #link vmlinux.o + info LD vmlinux.o + modpost_link vmlinux.o -# modpost vmlinux.o to check for section mismatches -${MAKE} -f "${srctree}/scripts/Makefile.modpost" vmlinux.o + # modpost vmlinux.o to check for section mismatches + ${MAKE} -f "${srctree}/scripts/Makefile.modpost" vmlinux.o +fi # Update version info GEN .version From d5bc73f1ce81f504b41e94e757a682ade6083a8f Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Tue, 3 Nov 2015 14:55:58 +0200 Subject: [PATCH 30/47] lkl tools: add support for Windows host Add host operations for Windows host and virtio disk support. Trivial changes to the generic virtio host code are made since mingw %p format is different then what the MMIO virtion driver expects. The boot test is updated to support Window hosts as well. Signed-off-by: Octavian Purdila --- tools/lkl/Makefile | 5 +- tools/lkl/include/lkl.h | 5 +- tools/lkl/lib/nt-host.c | 227 ++++++++++++++++++++++++++++++++++++++++ tools/lkl/lib/virtio.c | 4 +- tools/lkl/lib/virtio.h | 8 ++ tools/lkl/tests/boot.c | 26 +++++ 6 files changed, 270 insertions(+), 5 deletions(-) create mode 100644 tools/lkl/lib/nt-host.c diff --git a/tools/lkl/Makefile b/tools/lkl/Makefile index 4084609c6fc25f..d3d0e0b8f4068d 100644 --- a/tools/lkl/Makefile +++ b/tools/lkl/Makefile @@ -14,6 +14,9 @@ lib_source += lib/posix-host.c LDFLAGS += -lpthread -lrt source += $(wildcard *.c) execs = cpfromfs +else ifeq ($(shell $(LD) -r -print-output-format),pe-i386) +lib_source += lib/nt-host.c +KOPT="KALLSYMS_EXTRA_PASS=1" endif lib_objs = $(patsubst %.c,%.o, $(lib_source)) lib/lkl.o @@ -27,7 +30,7 @@ lib/liblkl.a: $(lib_objs) lib/lkl.o: $(MAKE) -C ../.. ARCH=lkl defconfig - $(MAKE) -C ../.. ARCH=lkl install INSTALL_PATH=$(PWD) + $(MAKE) -C ../.. ARCH=lkl $(KOPT) install INSTALL_PATH=$(PWD) %: %.o $(CC) -o $@ $^ $(LDFLAGS) diff --git a/tools/lkl/include/lkl.h b/tools/lkl/include/lkl.h index e6a9c777b498aa..aebd6358f95514 100644 --- a/tools/lkl/include/lkl.h +++ b/tools/lkl/include/lkl.h @@ -23,11 +23,12 @@ const char *lkl_strerror(int err); /** * lkl_disk_backstore - host dependend disk backstore * - * @fd - an open file descriptor that can be used by preadv/pwritev; used by - * POSIX hosts + * @fd - a POSIX file descriptor that can be used by preadv/pwritev + * @handle - an NT file handle that can be used by ReadFile/WriteFile */ union lkl_disk_backstore { int fd; + void *handle; }; /** diff --git a/tools/lkl/lib/nt-host.c b/tools/lkl/lib/nt-host.c new file mode 100644 index 00000000000000..9ac2dd7fd59359 --- /dev/null +++ b/tools/lkl/lib/nt-host.c @@ -0,0 +1,227 @@ +#include +#include +#include +#include +#include "iomem.h" + +static void *sem_alloc(int count) +{ + return CreateSemaphore(NULL, count, 100, NULL); +} + +static void sem_up(void *sem) +{ + ReleaseSemaphore(sem, 1, NULL); +} + +static void sem_down(void *sem) +{ + WaitForSingleObject(sem, INFINITE); +} + +static void sem_free(void *sem) +{ + CloseHandle(sem); +} + +static int thread_create(void (*fn)(void *), void *arg) +{ + DWORD WINAPI (*win_fn)(LPVOID arg) = (DWORD WINAPI (*)(LPVOID))fn; + + return CreateThread(NULL, 0, win_fn, arg, 0, NULL) ? 0 : -1; +} + +static void thread_exit(void) +{ + ExitThread(0); +} + + +/* + * With 64 bits, we can cover about 583 years at a nanosecond resolution. + * Windows counts time from 1601 so we do have about 100 years before we + * overflow. + */ +static unsigned long long time_ns(void) +{ + SYSTEMTIME st; + FILETIME ft; + LARGE_INTEGER li; + + GetSystemTime(&st); + SystemTimeToFileTime(&st, &ft); + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + + return li.QuadPart*100; +} + +struct timer { + HANDLE queue; + void (*callback)(void *); + void *arg; +}; + +static void *timer_alloc(void (*fn)(void *), void *arg) +{ + struct timer *t; + + t = malloc(sizeof(*t)); + if (!t) + return NULL; + + t->queue = CreateTimerQueue(); + if (!t->queue) { + free(t); + return NULL; + } + + t->callback = fn; + t->arg = arg; + + return t; +} + +static void CALLBACK timer_callback(void *arg, BOOLEAN TimerOrWaitFired) +{ + struct timer *t = (struct timer *)arg; + + if (TimerOrWaitFired) + t->callback(t->arg); +} + +static int timer_set_oneshot(void *timer, unsigned long ns) +{ + struct timer *t = (struct timer *)timer; + HANDLE tmp; + + return !CreateTimerQueueTimer(&tmp, t->queue, timer_callback, t, + ns / 1000000, 0, 0); +} + +static void timer_free(void *timer) +{ + struct timer *t = (struct timer *)timer; + HANDLE completion; + + completion = CreateEvent(NULL, FALSE, FALSE, NULL); + DeleteTimerQueueEx(t->queue, completion); + WaitForSingleObject(completion, INFINITE); + free(t); +} + +static void panic(void) +{ + int *x = NULL; + + *x = 1; + assert(0); +} + +static void print(const char *str, int len) +{ + write(1, str, len); +} + +static void *mem_alloc(unsigned long size) +{ + return malloc(size); +} + +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 = mem_alloc, + .mem_free = free, + .ioremap = lkl_ioremap, + .iomem_access = lkl_iomem_access, + .virtio_devices = lkl_virtio_devs, +}; + +int handle_get_capacity(union lkl_disk_backstore bs, unsigned long long *res) +{ + LARGE_INTEGER tmp; + + if (!GetFileSizeEx(bs.handle, &tmp)) + return -1; + + *res = tmp.QuadPart; + return 0; +} + +void handle_do_rw(union lkl_disk_backstore bs, unsigned int type, + unsigned int prio, unsigned long long sector, + struct lkl_dev_buf *bufs, int count) +{ + unsigned long long offset = sector * 512; + OVERLAPPED ov = { 0, }; + int err = 0, ret; + + switch (type) { + case LKL_DEV_BLK_TYPE_READ: + case LKL_DEV_BLK_TYPE_WRITE: + { + int i; + + for (i = 0; i < count; i++) { + DWORD res; + + ov.Offset = offset & 0xffffffff; + ov.OffsetHigh = offset >> 32; + + if (type == LKL_DEV_BLK_TYPE_READ) + ret = ReadFile(bs.handle, bufs[i].addr, + bufs[i].len, &res, &ov); + else + ret = WriteFile(bs.handle, bufs[i].addr, + bufs[i].len, &res, &ov); + if (!ret) { + lkl_printf("%s: I/O error: %d\n", __func__, + GetLastError()); + err = -1; + goto out; + } + + if (res != bufs[i].len) { + lkl_printf("%s: I/O error: short: %d %d\n", + res, bufs[i].len); + err = -1; + goto out; + } + + offset += bufs[i].len; + } + break; + } + case LKL_DEV_BLK_TYPE_FLUSH: + case LKL_DEV_BLK_TYPE_FLUSH_OUT: + ret = FlushFileBuffers(bs.handle); + if (!ret) + err = 1; + break; + default: + lkl_dev_blk_complete(bufs, LKL_DEV_BLK_STATUS_UNSUP, 0); + return; + } + +out: + 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 = handle_get_capacity, + .request = handle_do_rw, +}; diff --git a/tools/lkl/lib/virtio.c b/tools/lkl/lib/virtio.c index 034152e8e57082..17522b2aeb106a 100644 --- a/tools/lkl/lib/virtio.c +++ b/tools/lkl/lib/virtio.c @@ -350,8 +350,8 @@ int virtio_dev_setup(struct virtio_dev *dev, int queues, int num_max) lkl_host_ops.mem_free(dev->queue); avail = sizeof(lkl_virtio_devs) - (devs - lkl_virtio_devs); - devs += snprintf(devs, avail, " virtio_mmio.device=%d@%p:%d", - mmio_size, dev, dev->irq); + devs += snprintf(devs, avail, " virtio_mmio.device=%d@0x%lx:%d", + mmio_size, (uintptr_t)dev, dev->irq); return ret; } diff --git a/tools/lkl/lib/virtio.h b/tools/lkl/lib/virtio.h index 1bacbe62878b89..b76b18b74ca560 100644 --- a/tools/lkl/lib/virtio.h +++ b/tools/lkl/lib/virtio.h @@ -81,6 +81,14 @@ void virtio_dev_complete(struct virtio_dev_req *req, uint32_t len); #define container_of(ptr, type, member) \ (type *)((char *)(ptr) - __builtin_offsetof(type, member)) +#ifndef __MINGW32__ #include +#else +#define le32toh(x) (x) +#define le16toh(x) (x) +#define htole32(x) (x) +#define htole16(x) (x) +#define le64toh(x) (x) +#endif #endif /* _LKL_LIB_VIRTIO_H */ diff --git a/tools/lkl/tests/boot.c b/tools/lkl/tests/boot.c index f5945aac86f1f5..8b401b729ba817 100644 --- a/tools/lkl/tests/boot.c +++ b/tools/lkl/tests/boot.c @@ -4,10 +4,17 @@ #include #include #include +#ifndef __MINGW32__ +#include +#endif #include #include +#ifndef __MINGW32__ #include #include +#else +#include +#endif static struct cl_args { int printk; @@ -60,6 +67,7 @@ static void do_test(char *name, int (*fn)(char *, int)) #define sleep_ns 87654321 +#ifndef __MINGW32__ int test_nanosleep(char *str, int len) { struct lkl_timespec ts = { @@ -84,6 +92,7 @@ int test_nanosleep(char *str, int len) return 0; } +#endif int test_getpid(char *str, int len) { @@ -270,8 +279,14 @@ static int disk_id = -1; int test_disk_add(char *str, int len) { +#ifdef __MINGW32__ + bs.handle = CreateFile(cla.disk_filename, GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, 0, NULL); + if (!bs.handle) +#else bs.fd = open(cla.disk_filename, O_RDWR); if (bs.fd < 0) +#endif goto out_unlink; disk_id = lkl_disk_add(bs); @@ -281,9 +296,18 @@ int test_disk_add(char *str, int len) goto out; out_close: +#ifdef __MINGW32__ + CloseHandle(bs.handle); +#else close(bs.fd); +#endif + out_unlink: +#ifdef __MINGW32__ + DeleteFile(cla.disk_filename); +#else unlink(cla.disk_filename); +#endif out: snprintf(str, len, "%x %d", bs.fd, disk_id); @@ -472,7 +496,9 @@ int main(int argc, char **argv) TEST(fstat64); TEST(mkdir); TEST(stat64); +#ifndef __MINGW32__ TEST(nanosleep); +#endif TEST(mount); TEST(chdir); TEST(opendir); From 90814da23bbc629edcb262a51fbeb1bd8b884250 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Mon, 28 Sep 2015 14:47:58 +0300 Subject: [PATCH 31/47] lkl: add documentation Signed-off-by: Octavian Purdila --- Documentation/lkl.txt | 65 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 Documentation/lkl.txt diff --git a/Documentation/lkl.txt b/Documentation/lkl.txt new file mode 100644 index 00000000000000..c9e30f7455c86e --- /dev/null +++ b/Documentation/lkl.txt @@ -0,0 +1,65 @@ + +Introduction +------------ + +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 uses host operations +defined by the application or a host library (tools/lkl/lib). + + +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 +--------------- + +The supported hosts for now are POSIX and Windows userspace applications. + +FAQ +--- + +Q: How is LKL different from UML? + +A: UML prodivides 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 implemented. + + From 8ec7b897588ae2eda606ab78976d0e0a82a93bde Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Sun, 8 Nov 2015 06:20:02 +0200 Subject: [PATCH 32/47] lkl: fix zero page implementation Signed-off-by: Octavian Purdila --- arch/lkl/include/asm/pgtable.h | 3 ++- arch/lkl/kernel/mem.c | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/arch/lkl/include/asm/pgtable.h b/arch/lkl/include/asm/pgtable.h index 726675ad5e8006..9bb4593c9b3312 100644 --- a/arch/lkl/include/asm/pgtable.h +++ b/arch/lkl/include/asm/pgtable.h @@ -37,7 +37,8 @@ void paging_init(void); * 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)) +extern void *empty_zero_page; +#define ZERO_PAGE(vaddr) (virt_to_page(empty_zero_page)) /* * No page table caches to initialise. diff --git a/arch/lkl/kernel/mem.c b/arch/lkl/kernel/mem.c index 225c2ccd5117eb..f6d1ee74ec1046 100644 --- a/arch/lkl/kernel/mem.c +++ b/arch/lkl/kernel/mem.c @@ -5,6 +5,8 @@ unsigned long memory_start, memory_end; static unsigned long _memory_start, mem_size; +void *empty_zero_page; + void __init bootmem_init(int mem_size) { int bootmap_size; @@ -36,6 +38,9 @@ void __init bootmem_init(int mem_size) free_bootmem(memory_start, mem_size); reserve_bootmem(memory_start, bootmap_size, BOOTMEM_DEFAULT); + empty_zero_page = alloc_bootmem_pages(PAGE_SIZE); + memset((void *)empty_zero_page, 0, PAGE_SIZE); + { unsigned long zones_size[MAX_NR_ZONES] = {0, }; From fe49079ad073f7a470de8b68e3bee3c6f210d8ba Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Sun, 8 Nov 2015 06:21:58 +0200 Subject: [PATCH 33/47] lkl: add more filesystem related system calls Add symblink, fallocate, link, pread64, pwrite64, fsync, fadatasync, removexattr, utimensat. Signed-off-by: Octavian Purdila --- arch/lkl/include/uapi/asm/unistd.h | 26 +++++++++++++++++++++++++- arch/lkl/kernel/syscalls.c | 9 +++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/arch/lkl/include/uapi/asm/unistd.h b/arch/lkl/include/uapi/asm/unistd.h index 68b5423c8b4019..b2e89f198e32e1 100644 --- a/arch/lkl/include/uapi/asm/unistd.h +++ b/arch/lkl/include/uapi/asm/unistd.h @@ -57,8 +57,17 @@ #define __NR_setxattr 52 #define __NR_lsetxattr 53 #define __NR_fsetxattr 54 +#define __NR_symlink 55 +#define __NR_fallocate 56 +#define __NR_link 57 +#define __NR_pread64 58 +#define __NR_pwrite64 59 +#define __NR_fsync 60 +#define __NR_fdatasync 61 +#define __NR_removexattr 62 +#define __NR_utimensat 63 #ifdef __KERNEL__ -#define NR_syscalls 55 +#define NR_syscalls 64 #endif #define __ARCH_WANT_SYS_UTIME @@ -248,6 +257,21 @@ 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); +LKL_SYSCALL2(symlink, const char *, oldname, const char *, newname); +LKL_SYSCALL2(link, const char *, oldname, const char *, newname); +LKL_SYSCALL3(chown, const char *, filename, __lkl__kernel_uid32_t, uid, + __lkl__kernel_gid32_t, gid); +LKL_SYSCALL4(pread64, unsigned int, fd, char *, buf, + __lkl__kernel_size_t, count, __lkl__kernel_loff_t, pos); +LKL_SYSCALL4(pwrite64, unsigned int, fd, const char *, buf, + __lkl__kernel_size_t, count, __lkl__kernel_loff_t, pos); +LKL_SYSCALL1(fsync, unsigned int, fd); +LKL_SYSCALL1(fdatasync, unsigned int, fd); +LKL_SYSCALL2(removexattr, const char *, path, const char *, name); +LKL_SYSCALL4(utimensat, int, dirfd, const char *, path, + struct lkl_timespec *, utimes, int, flags); +LKL_SYSCALL4(fallocate, int, fd, int, mode, __lkl__kernel_loff_t, offset, + __lkl__kernel_loff_t, len); long lkl_sys_halt(void); diff --git a/arch/lkl/kernel/syscalls.c b/arch/lkl/kernel/syscalls.c index 48b129674a1209..51031cbfa61faa 100644 --- a/arch/lkl/kernel/syscalls.c +++ b/arch/lkl/kernel/syscalls.c @@ -195,6 +195,15 @@ void init_syscall_table(void) INIT_STE(setxattr); INIT_STE(lsetxattr); INIT_STE(fsetxattr); + INIT_STE(symlink); + INIT_STE(fallocate); + INIT_STE(link); + INIT_STE(pread64); + INIT_STE(pwrite64); + INIT_STE(fsync); + INIT_STE(fdatasync); + INIT_STE(removexattr); + INIT_STE(utimensat); } int __init syscall_init(void) From d88e0bd7562f6c5937c359e588d370c0bf57aec3 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Sun, 8 Nov 2015 06:22:41 +0200 Subject: [PATCH 34/47] lkl tools: git .gitignore Fix a typo in the .gitignore. Signed-off-by: Octavian Purdila --- tools/lkl/.gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/lkl/.gitignore b/tools/lkl/.gitignore index 10483238adc146..4e8fd21dd812a5 100644 --- a/tools/lkl/.gitignore +++ b/tools/lkl/.gitignore @@ -1,4 +1,4 @@ -test/boot +tests/boot fs2tar cptofs cpfromfs From c4591a958d333ee4243523a9b9dead7faa2009d4 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Sun, 8 Nov 2015 06:25:02 +0200 Subject: [PATCH 35/47] lkl tools: add a new lkl_dirfd API Add lkl_dirfd that returns the file descriptor associated with the directory handle. This patch also changes the lkl_closedir signature to return an error. Signed-off-by: Octavian Purdila --- tools/lkl/include/lkl.h | 11 ++++++++++- tools/lkl/lib/fs.c | 13 +++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/tools/lkl/include/lkl.h b/tools/lkl/include/lkl.h index aebd6358f95514..220235b14bc3a1 100644 --- a/tools/lkl/include/lkl.h +++ b/tools/lkl/include/lkl.h @@ -87,7 +87,7 @@ struct lkl_dir *lkl_opendir(const char *path, int *err); * * @dir - the directory handler as returned by lkl_opendir */ -void lkl_closedir(struct lkl_dir *dir); +int lkl_closedir(struct lkl_dir *dir); /** * lkl_readdir - get the next available entry of the directory @@ -107,4 +107,13 @@ struct lkl_dirent64 *lkl_readdir(struct lkl_dir *dir); */ int lkl_errdir(struct lkl_dir *dir); +/** + * lkl_dirfd - gets the file descriptor associated with the directory handle + * + * @dir - the directory handle as returned by lkl_opendir + * @returns - a positive value,which is the LKL file descriptor associated with + * the directory handle, or a negative value otherwise + */ +int lkl_dirfd(struct lkl_dir *dir); + #endif diff --git a/tools/lkl/lib/fs.c b/tools/lkl/lib/fs.c index 16f4d44156855b..d09ae061a60abc 100644 --- a/tools/lkl/lib/fs.c +++ b/tools/lkl/lib/fs.c @@ -177,10 +177,14 @@ struct lkl_dir *lkl_opendir(const char *path, int *err) return dir; } -void lkl_closedir(struct lkl_dir *dir) +int lkl_closedir(struct lkl_dir *dir) { - lkl_sys_close(dir->fd); + int ret; + + ret = lkl_sys_close(dir->fd); lkl_host_ops.mem_free(dir); + + return ret; } struct lkl_dirent64 *lkl_readdir(struct lkl_dir *dir) @@ -216,3 +220,8 @@ int lkl_errdir(struct lkl_dir *dir) return dir->len; } + +int lkl_dirfd(struct lkl_dir *dir) +{ + return dir->fd; +} From 2a3cf55c0f53987b5f2ea540960a79810f6a22d1 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Sun, 8 Nov 2015 06:26:28 +0200 Subject: [PATCH 36/47] lkl tools: add lklfuse Add a simple fuse based program that can mount filesystem in userspace using LKL. Signed-off-by: Octavian Purdila --- tools/lkl/.gitignore | 1 + tools/lkl/Makefile | 5 + tools/lkl/lklfuse.c | 597 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 603 insertions(+) create mode 100644 tools/lkl/lklfuse.c diff --git a/tools/lkl/.gitignore b/tools/lkl/.gitignore index 4e8fd21dd812a5..74e9c7756fd7f2 100644 --- a/tools/lkl/.gitignore +++ b/tools/lkl/.gitignore @@ -2,3 +2,4 @@ tests/boot fs2tar cptofs cpfromfs +lklfuse diff --git a/tools/lkl/Makefile b/tools/lkl/Makefile index d3d0e0b8f4068d..1a53e74d3beb9f 100644 --- a/tools/lkl/Makefile +++ b/tools/lkl/Makefile @@ -19,6 +19,10 @@ lib_source += lib/nt-host.c KOPT="KALLSYMS_EXTRA_PASS=1" endif +ifeq ($(shell $(LD) -r -print-output-format),elf64-x86-64) +CFLAGS += -D_FILE_OFFSET_BITS=64 +endif + lib_objs = $(patsubst %.c,%.o, $(lib_source)) lib/lkl.o objs = $(patsubst %.c,%.o, $(source)) execs += $(patsubst %.c,%, $(source)) @@ -43,5 +47,6 @@ clean: -rm -rf include/lkl/ lib/liblkl.a $(lib_objs) $(objs) $(execs) fs2tar: LDFLAGS += -larchive +lklfuse: LDFLAGS += -lfuse cpfromfs: cptofs if ! [ -e $@ ]; then ln -s $< $@; fi diff --git a/tools/lkl/lklfuse.c b/tools/lkl/lklfuse.c new file mode 100644 index 00000000000000..4f0ae97c1cc1bb --- /dev/null +++ b/tools/lkl/lklfuse.c @@ -0,0 +1,597 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#define FUSE_USE_VERSION 26 +#include +#include +#include +#undef st_atime +#undef st_mtime +#undef st_ctime +#include +#include + +#define LKLFUSE_VERSION "0.1" + +struct lklfuse { + const char *file; + const char *log; + const char *type; + union lkl_disk_backstore bs; + int disk_id; + int ro; +} lklfuse; + +#define LKLFUSE_OPT(t, p, v) { t, offsetof(struct lklfuse, p), v } + +enum { + KEY_HELP, + KEY_VERSION, +}; + +static struct fuse_opt lklfuse_opts[] = { + LKLFUSE_OPT("log=%s", log, 0), + LKLFUSE_OPT("type=%s", type, 0), + FUSE_OPT_KEY("-h", KEY_HELP), + FUSE_OPT_KEY("--help", KEY_HELP), + FUSE_OPT_KEY("-V", KEY_VERSION), + FUSE_OPT_KEY("--version", KEY_VERSION), + FUSE_OPT_END +}; + +static void usage(void) +{ + printf( +"usage: lklfuse file mountpoint [options]\n" +"\n" +"general options:\n" +" -o opt,[opt...] mount options\n" +" -h --help print help\n" +" -V --version print version\n" +"\n" +"lklfuse options:\n" +" -o log=FILE log file\n" +" -o type=fstype filesystem type\n" +); +} + +static int lklfuse_opt_proc(void *data, const char *arg, int key, + struct fuse_args *args) +{ + switch (key) { + case FUSE_OPT_KEY_OPT: + if (strcmp(arg, "ro") == 0) { + printf("ro\n"); + lklfuse.ro = 1; + } + return 1; + + case FUSE_OPT_KEY_NONOPT: + if (!lklfuse.file) { + lklfuse.file = strdup(arg); + return 0; + } + return 1; + + case KEY_HELP: + usage(); + fuse_opt_add_arg(args, "-ho"); + fuse_main(args->argc, args->argv, NULL, NULL); + exit(1); + + case KEY_VERSION: + printf("lklfuse version %s\n", LKLFUSE_VERSION); + fuse_opt_add_arg(args, "--version"); + fuse_main(args->argc, args->argv, NULL, NULL); + exit(0); + + default: + fprintf(stderr, "internal error\n"); + abort(); + } +} + +static int lklfuse_getattr(const char *path, struct stat *st) +{ + long ret; + struct lkl_stat64 lkl_stat; + + ret = lkl_sys_lstat64(path, &lkl_stat); + if (ret) + return ret; + + st->st_dev = lkl_stat.st_dev; + st->st_ino = lkl_stat.st_ino; + st->st_mode = lkl_stat.st_mode; + st->st_nlink = lkl_stat.st_nlink; + st->st_uid = lkl_stat.st_uid; + st->st_gid = lkl_stat.st_gid; + st->st_rdev = lkl_stat.st_rdev; + st->st_size = lkl_stat.st_size; + st->st_blksize = lkl_stat.st_blksize; + st->st_blocks = lkl_stat.st_blocks; + st->st_atim.tv_sec = lkl_stat.st_atime; + st->st_atim.tv_nsec = lkl_stat.st_atime_nsec; + st->st_mtim.tv_sec = lkl_stat.st_mtime; + st->st_mtim.tv_nsec = lkl_stat.st_mtime_nsec; + st->st_ctim.tv_sec = lkl_stat.st_ctime; + st->st_ctim.tv_nsec = lkl_stat.st_ctime_nsec; + + return 0; +} + +static int lklfuse_readlink(const char *path, char *buf, size_t len) +{ + long ret; + + ret = lkl_sys_readlink(path, buf, len); + if (ret < 0) + return ret; + + if (ret == len) + ret = len - 1; + + buf[ret] = 0; + + return 0; +} + +static int lklfuse_mknod(const char *path, mode_t mode, dev_t dev) +{ + return lkl_sys_mknod(path, mode, dev); +} + +static int lklfuse_mkdir(const char *path, mode_t mode) +{ + return lkl_sys_mkdir(path, mode); +} + +static int lklfuse_unlink(const char *path) +{ + return lkl_sys_unlink(path); +} + +static int lklfuse_rmdir(const char *path) +{ + return lkl_sys_rmdir(path); +} + +static int lklfuse_symlink(const char *oldname, const char *newname) +{ + return lkl_sys_symlink(oldname, newname); +} + + +static int lklfuse_rename(const char *oldname, const char *newname) +{ + return lkl_sys_rename(oldname, newname); +} + +static int lklfuse_link(const char *oldname, const char *newname) +{ + return lkl_sys_link(oldname, newname); +} + +static int lklfuse_chmod(const char *path, mode_t mode) +{ + return lkl_sys_chmod(path, mode); +} + + +static int lklfuse_chown(const char *path, uid_t uid, gid_t gid) +{ + return lkl_sys_chown(path, uid, gid); +} + +static int lklfuse_truncate(const char *path, off_t off) +{ + return lkl_sys_truncate(path, off); +} + +static int lklfuse_open(const char *path, struct fuse_file_info *fi) +{ + long ret; + int flags; + + if ((fi->flags & O_ACCMODE) == O_RDONLY) + flags = LKL_O_RDONLY; + else if ((fi->flags & O_ACCMODE) == O_WRONLY) + flags = LKL_O_WRONLY; + else if ((fi->flags & O_ACCMODE) == O_RDWR) + flags = LKL_O_RDWR; + else + return -LKL_EINVAL; + + ret = lkl_sys_open(path, flags, 0); + if (ret < 0) + return ret; + + fi->fh = ret; + + return 0; +} + +static int lklfuse_read(const char *path, char *buf, size_t size, off_t offset, + struct fuse_file_info *fi) +{ + long ret; + int orig_size = size; + + do { + ret = lkl_sys_pread64(fi->fh, buf, size, offset); + if (ret <= 0) + break; + size -= ret; + offset += ret; + buf += ret; + } while (size > 0); + + return ret < 0 ? ret : orig_size - size; + +} + +static int lklfuse_write(const char *path, const char *buf, size_t size, + off_t offset, struct fuse_file_info *fi) +{ + long ret; + int orig_size = size; + + do { + ret = lkl_sys_pwrite64(fi->fh, buf, size, offset); + if (ret <= 0) + break; + size -= ret; + offset += ret; + buf += ret; + } while (size > 0); + + return ret < 0 ? ret : orig_size - size; +} + + +static int lklfuse_statfs(const char *path, struct statvfs *stat) +{ + long ret; + struct lkl_statfs64 lkl_statfs; + + ret = lkl_sys_statfs64(path, &lkl_statfs); + if (ret < 0) + return ret; + + stat->f_bsize = lkl_statfs.f_bsize; + stat->f_frsize = lkl_statfs.f_frsize; + stat->f_blocks = lkl_statfs.f_blocks; + stat->f_bfree = lkl_statfs.f_bfree; + stat->f_bavail = lkl_statfs.f_bavail; + stat->f_files = lkl_statfs.f_files; + stat->f_ffree = lkl_statfs.f_ffree; + stat->f_favail = stat->f_ffree; + stat->f_fsid = *(unsigned long *)&lkl_statfs.f_fsid.val[0]; + stat->f_flag = lkl_statfs.f_flags; + stat->f_namemax = lkl_statfs.f_namelen; + + return 0; +} + +static int lklfuse_flush(const char *path, struct fuse_file_info *fi) +{ + return 0; +} + +static int lklfuse_release(const char *path, struct fuse_file_info *fi) +{ + return lkl_sys_close(fi->fh); +} + +static int lklfuse_fsync(const char *path, int datasync, + struct fuse_file_info *fi) +{ + if (datasync) + return lkl_sys_fdatasync(fi->fh); + else + return lkl_sys_fsync(fi->fh); +} + +static int lklfuse_setxattr(const char *path, const char *name, const char *val, + size_t size, int flags) +{ + return lkl_sys_setxattr(path, name, val, size, flags); +} + +static int lklfuse_getxattr(const char *path, const char *name, char *val, + size_t size) +{ + return lkl_sys_getxattr(path, name, val, size); +} + +static int lklfuse_listxattr(const char *path, char *list, size_t size) +{ + return lkl_sys_listxattr(path, list, size); +} + +static int lklfuse_removexattr(const char *path, const char *name) +{ + return lkl_sys_removexattr(path, name); +} + +static int lklfuse_opendir(const char *path, struct fuse_file_info *fi) +{ + struct lkl_dir *dir; + int err; + + dir = lkl_opendir(path, &err); + if (!dir) + return err; + + fi->fh = (uintptr_t)dir; + + return 0; +} + +/** Read directory + * + * This supersedes the old getdir() interface. New applications + * should use this. + * + * The filesystem may choose between two modes of operation: + * + * 1) The readdir implementation ignores the offset parameter, and + * passes zero to the filler function's offset. The filler + * function will not return '1' (unless an error happens), so the + * whole directory is read in a single readdir operation. This + * works just like the old getdir() method. + * + * 2) The readdir implementation keeps track of the offsets of the + * directory entries. It uses the offset parameter and always + * passes non-zero offset to the filler function. When the buffer + * is full (or an error happens) the filler function will return + * '1'. + * + * Introduced in version 2.3 + */ +static int lklfuse_readdir(const char *path, void *buf, fuse_fill_dir_t fill, + off_t off, struct fuse_file_info *fi) +{ + struct lkl_dir *dir = (struct lkl_dir *)(uintptr_t)fi->fh; + struct lkl_dirent64 *de; + + while ((de = lkl_readdir(dir))) { + struct stat st = { 0, }; + + st.st_ino = de->d_ino; + st.st_mode = de->d_type << 12; + + if (fill(buf, de->d_name, &st, 0)) + break; + } + + if (!de) + return lkl_errdir(dir); + + return 0; +} + +static int lklfuse_releasedir(const char *path, struct fuse_file_info *fi) +{ + struct lkl_dir *dir = (struct lkl_dir *)(uintptr_t)fi->fh; + + return lkl_closedir(dir); +} + +static int lklfuse_fsyncdir(const char *path, int datasync, + struct fuse_file_info *fi) +{ + struct lkl_dir *dir = (struct lkl_dir *)(uintptr_t)fi->fh; + int fd = lkl_dirfd(dir); + + if (datasync) + return lkl_sys_fdatasync(fd); + else + return lkl_sys_fsync(fd); +} + +static int lklfuse_access(const char *path, int mode) +{ + return lkl_sys_access(path, mode); +} + +static int lklfuse_utimens(const char *path, const struct timespec tv[2]) +{ + struct lkl_timespec ts[2]; + + ts[0].tv_sec = tv[0].tv_sec; + ts[0].tv_nsec = tv[0].tv_nsec; + ts[1].tv_sec = tv[0].tv_sec; + ts[1].tv_nsec = tv[0].tv_nsec; + + return lkl_sys_utimensat(-1, path, ts, 0); +} + +static int lklfuse_fallocate(const char *path, int mode, off_t offset, + off_t len, struct fuse_file_info *fi) +{ + return lkl_sys_fallocate(fi->fh, mode, offset, len); +} + +const struct fuse_operations lklfuse_ops = { + .flag_nullpath_ok = 1, + .flag_nopath = 1, + .flag_utime_omit_ok = 1, + + .getattr = lklfuse_getattr, + .readlink = lklfuse_readlink, + .mknod = lklfuse_mknod, + .mkdir = lklfuse_mkdir, + .unlink = lklfuse_unlink, + .rmdir = lklfuse_rmdir, + .symlink = lklfuse_symlink, + .rename = lklfuse_rename, + .link = lklfuse_link, + .chmod = lklfuse_chmod, + .chown = lklfuse_chown, + .truncate = lklfuse_truncate, + .open = lklfuse_open, + .read = lklfuse_read, + .write = lklfuse_write, + .statfs = lklfuse_statfs, + .flush = lklfuse_flush, + .release = lklfuse_release, + .fsync = lklfuse_fsync, + .setxattr = lklfuse_setxattr, + .getxattr = lklfuse_getxattr, + .listxattr = lklfuse_listxattr, + .removexattr = lklfuse_removexattr, + .opendir = lklfuse_opendir, + .readdir = lklfuse_readdir, + .releasedir = lklfuse_releasedir, + .fsyncdir = lklfuse_fsyncdir, + .access = lklfuse_access, + .utimens = lklfuse_utimens, + .fallocate = lklfuse_fallocate, +}; + +static int init_lkl(void) +{ + long ret; + char mpoint[32]; + + lklfuse.bs.fd = open(lklfuse.file, lklfuse.ro ? O_RDONLY : O_RDWR); + if (lklfuse.bs.fd < 0) { + fprintf(stderr, "can't open file %s: %s\n", lklfuse.file, + strerror(errno)); + return -1; + } + + ret = lkl_disk_add(lklfuse.bs); + if (ret < 0) { + fprintf(stderr, "can't add disk: %s\n", lkl_strerror(ret)); + goto out_close; + } + + lklfuse.disk_id = ret; + + ret = lkl_start_kernel(&lkl_host_ops, 64 * 1024 * 1024, ""); + if (ret) { + fprintf(stderr, "can't start kernel: %s\n", lkl_strerror(ret)); + goto out_close; + } + + ret = lkl_mount_dev(lklfuse.disk_id, lklfuse.type, + lklfuse.ro ? LKL_MS_RDONLY : 0, NULL, + mpoint, sizeof(mpoint)); + + if (ret) { + fprintf(stderr, "can't mount disk: %s\n", lkl_strerror(ret)); + goto out_halt; + } + + ret = lkl_sys_chroot(mpoint); + if (ret) { + fprintf(stderr, "can't chdir to %s: %s\n", mpoint, + lkl_strerror(ret)); + goto out_umount; + } + + return 0; + +out_umount: + lkl_umount_dev(lklfuse.disk_id, 0, 1000); + +out_halt: + lkl_sys_halt(); + +out_close: + close(lklfuse.bs.fd); + + return ret; +} + +static void cleanup_lkl(void) +{ + int ret; + + ret = lkl_sys_chdir("/"); + if (ret) + fprintf(stderr, "can't chdir to /: %s\n", lkl_strerror(ret)); + ret = lkl_sys_umount("/", 0); + if (ret) + fprintf(stderr, "failed to umount disk: %d: %s\n", + lklfuse.disk_id, lkl_strerror(ret)); + lkl_sys_halt(); + close(lklfuse.bs.fd); +} + +int main(int argc, char **argv) +{ + struct fuse_args args = FUSE_ARGS_INIT(argc, argv); + struct fuse_chan *ch; + struct fuse *fuse; + struct stat st; + char *mnt; + int fg, mt, ret; + + if (fuse_opt_parse(&args, &lklfuse, lklfuse_opts, lklfuse_opt_proc)) + return 1; + + if (!lklfuse.file || !lklfuse.type) { + fprintf(stderr, "no file or filesystem type specified\n"); + return 1; + } + + if (fuse_parse_cmdline(&args, &mnt, &mt, &fg)) + return 1; + + ret = stat(mnt, &st); + if (ret) { + perror(mnt); + goto out_free; + } + + ret = init_lkl(); + if (ret) + goto out_free; + + ch = fuse_mount(mnt, &args); + if (!ch) { + ret = -1; + goto out_cleanup_lkl; + } + + fuse = fuse_new(ch, &args, &lklfuse_ops, sizeof(lklfuse_ops), NULL); + if (!fuse) { + ret = -1; + goto out_fuse_unmount; + } + + if (fuse_daemonize(fg) || + fuse_set_signal_handlers(fuse_get_session(fuse))) { + ret = -1; + goto out_fuse_destroy; + } + + if (mt) + ret = fuse_loop_mt(fuse); + else + ret = fuse_loop(fuse); + + fuse_remove_signal_handlers(fuse_get_session(fuse)); + +out_fuse_destroy: + fuse_destroy(fuse); + +out_fuse_unmount: + fuse_unmount(mnt, ch); + +out_cleanup_lkl: + cleanup_lkl(); + +out_free: + free(mnt); + + return ret < 0 ? 1 : 0; +} From 5ee93e6452443a47ac5408e2dee51a666d1f1e32 Mon Sep 17 00:00:00 2001 From: Conrad Meyer Date: Mon, 9 Nov 2015 09:44:26 -0800 Subject: [PATCH 37/47] lkl: sys_statfs64 takes a middle size parameter Signed-off-by: Conrad Meyer Signed-off-by: Octavian Purdila --- arch/lkl/include/uapi/asm/unistd.h | 3 ++- tools/lkl/lklfuse.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/lkl/include/uapi/asm/unistd.h b/arch/lkl/include/uapi/asm/unistd.h index b2e89f198e32e1..224a4c08df046f 100644 --- a/arch/lkl/include/uapi/asm/unistd.h +++ b/arch/lkl/include/uapi/asm/unistd.h @@ -240,7 +240,8 @@ 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(statfs64, const char *, path, __lkl__kernel_size_t, sz, + 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); diff --git a/tools/lkl/lklfuse.c b/tools/lkl/lklfuse.c index 4f0ae97c1cc1bb..8562b14523b882 100644 --- a/tools/lkl/lklfuse.c +++ b/tools/lkl/lklfuse.c @@ -259,7 +259,7 @@ static int lklfuse_statfs(const char *path, struct statvfs *stat) long ret; struct lkl_statfs64 lkl_statfs; - ret = lkl_sys_statfs64(path, &lkl_statfs); + ret = lkl_sys_statfs64(path, sizeof(lkl_statfs), &lkl_statfs); if (ret < 0) return ret; From 69259d84bec842bc2ae151a0b4e5ab2307c4afa0 Mon Sep 17 00:00:00 2001 From: Conrad Meyer Date: Sun, 8 Nov 2015 20:03:47 -0800 Subject: [PATCH 38/47] lkl: headers_install: Use env(1) to locate python binary Signed-off-by: Conrad Meyer Signed-off-by: Octavian Purdila --- arch/lkl/scripts/headers_install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/lkl/scripts/headers_install.py b/arch/lkl/scripts/headers_install.py index eb69a5e4098d86..cada8e9c66ba8b 100755 --- a/arch/lkl/scripts/headers_install.py +++ b/arch/lkl/scripts/headers_install.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python import re, os, sys, argparse, multiprocessing header_paths = [ "include/uapi/", "arch/lkl/include/uapi/", From 40ce53e46b0ca53f05d78d71907ad3c00ec66813 Mon Sep 17 00:00:00 2001 From: Conrad Meyer Date: Sun, 8 Nov 2015 20:09:08 -0800 Subject: [PATCH 39/47] lkl tools: Makefile: Drop lkl.o:lkl.o circular dependency Signed-off-by: Conrad Meyer Signed-off-by: Octavian Purdila --- tools/lkl/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/lkl/Makefile b/tools/lkl/Makefile index 1a53e74d3beb9f..afa0265b4e3ca7 100644 --- a/tools/lkl/Makefile +++ b/tools/lkl/Makefile @@ -23,13 +23,13 @@ ifeq ($(shell $(LD) -r -print-output-format),elf64-x86-64) CFLAGS += -D_FILE_OFFSET_BITS=64 endif -lib_objs = $(patsubst %.c,%.o, $(lib_source)) lib/lkl.o +lib_objs = $(patsubst %.c,%.o, $(lib_source)) objs = $(patsubst %.c,%.o, $(source)) execs += $(patsubst %.c,%, $(source)) all: lib/liblkl.a $(execs) -lib/liblkl.a: $(lib_objs) +lib/liblkl.a: $(lib_objs) lib/lkl.o $(AR) -rc $@ $^ lib/lkl.o: @@ -44,7 +44,7 @@ $(objs): lib/lkl.o $(execs): lib/liblkl.a clean: - -rm -rf include/lkl/ lib/liblkl.a $(lib_objs) $(objs) $(execs) + -rm -rf include/lkl/ lib/liblkl.a $(lib_objs) lib/lkl.o $(objs) $(execs) fs2tar: LDFLAGS += -larchive lklfuse: LDFLAGS += -lfuse From 4efe4427a9556bcafdb7281006906ec64516ef98 Mon Sep 17 00:00:00 2001 From: Conrad Meyer Date: Sun, 8 Nov 2015 20:22:01 -0800 Subject: [PATCH 40/47] lkl: convert makefile echo \t to inline tab Signed-off-by: Conrad Meyer [Octavian: -e does not work from make with dash switch to using inline tab] Signed-off-by: Octavian Purdila --- arch/lkl/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/lkl/Makefile b/arch/lkl/Makefile index 51ad096f6ee99a..c3bbf01c6f18ad 100644 --- a/arch/lkl/Makefile +++ b/arch/lkl/Makefile @@ -22,7 +22,7 @@ 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" + @echo " INSTALL $(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))) \ From ed4362d16f198542afc8ad624faa09fad2e1e33a Mon Sep 17 00:00:00 2001 From: Conrad Meyer Date: Sun, 8 Nov 2015 20:42:56 -0800 Subject: [PATCH 41/47] lkl tools: Add kernel clean to clean target Signed-off-by: Conrad Meyer Signed-off-by: Octavian Purdila --- tools/lkl/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/lkl/Makefile b/tools/lkl/Makefile index afa0265b4e3ca7..7e468a34f2f3be 100644 --- a/tools/lkl/Makefile +++ b/tools/lkl/Makefile @@ -45,6 +45,7 @@ $(execs): lib/liblkl.a clean: -rm -rf include/lkl/ lib/liblkl.a $(lib_objs) lib/lkl.o $(objs) $(execs) + $(MAKE) -C ../.. ARCH=lkl clean fs2tar: LDFLAGS += -larchive lklfuse: LDFLAGS += -lfuse From 2bd6dcada8232e252583a25c10bbe7557904a072 Mon Sep 17 00:00:00 2001 From: Conrad Meyer Date: Sun, 8 Nov 2015 22:21:50 -0800 Subject: [PATCH 42/47] tools lkl: Use OUTPUT_FORMAT variable instead of duplicating LD invocation Signed-off-by: Conrad Meyer Signed-off-by: Octavian Purdila --- tools/lkl/Makefile | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tools/lkl/Makefile b/tools/lkl/Makefile index 7e468a34f2f3be..6ea6e3d87160ac 100644 --- a/tools/lkl/Makefile +++ b/tools/lkl/Makefile @@ -6,20 +6,24 @@ AR=$(CROSS_COMPILE)ar LD=$(CROSS_COMPILE)ld endif +OUTPUT_FORMAT=$(shell $(LD) -r -print-output-format) + 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)) +ifneq (,$(filter $(OUTPUT_FORMAT),elf64-x86-64 elf32-i386)) source += $(wildcard *.c) lib_source += lib/posix-host.c LDFLAGS += -lpthread -lrt source += $(wildcard *.c) execs = cpfromfs -else ifeq ($(shell $(LD) -r -print-output-format),pe-i386) +else ifeq ($(OUTPUT_FORMAT),pe-i386) lib_source += lib/nt-host.c KOPT="KALLSYMS_EXTRA_PASS=1" +else +$(error Unrecognized platform: $(OUTPUT_FORMAT)) endif -ifeq ($(shell $(LD) -r -print-output-format),elf64-x86-64) +ifneq (,$(filter $(OUTPUT_FORMAT),elf64-x86-64)) CFLAGS += -D_FILE_OFFSET_BITS=64 endif From 5b51c3171ba0b24d697880b63e33e67abe14357d Mon Sep 17 00:00:00 2001 From: Conrad Meyer Date: Sun, 8 Nov 2015 22:22:05 -0800 Subject: [PATCH 43/47] lkl tools: build with GCC, turn on the optimizer and more warnings Signed-off-by: Conrad Meyer [Octavian: squashed commits, removed -Werror] Signed-off-by: Octavian Purdila --- tools/lkl/Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/lkl/Makefile b/tools/lkl/Makefile index 6ea6e3d87160ac..4c0227922be17a 100644 --- a/tools/lkl/Makefile +++ b/tools/lkl/Makefile @@ -1,9 +1,12 @@ -CFLAGS := -Iinclude -Wall -g +CFLAGS := -Iinclude -Wall -g -O2 -Wextra -Wno-unused-parameter \ + -Wno-missing-field-initializers -fno-strict-aliasing ifdef CROSS_COMPILE CC=$(CROSS_COMPILE)gcc AR=$(CROSS_COMPILE)ar LD=$(CROSS_COMPILE)ld +else +CC=gcc endif OUTPUT_FORMAT=$(shell $(LD) -r -print-output-format) From ce68a0cc7f7e646fe21d71f3541fe17f625b12a9 Mon Sep 17 00:00:00 2001 From: Conrad Meyer Date: Sun, 8 Nov 2015 22:24:54 -0800 Subject: [PATCH 44/47] lkl tools: Fix compiler warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cptofs.c: In function ‘main’: cptofs.c:429:2: warning: comparison of unsigned expression < 0 is always false [-Wtype-limits] if (disk_id < 0) { fs2tar.c:355:2: warning: comparison of unsigned expression < 0 is always false [-Wtype-limits] if (disk_id < 0) { ^ tests/boot.c: In function ‘printk’: tests/boot.c:54:8: error: ignoring return value of ‘write’, declared with attribute warn_unused_result [-Werror=unused-result] write(STDOUT_FILENO, str, len); ^ lib/posix-host.c: In function ‘print’: lib/posix-host.c:20:7: error: ignoring return value of ‘write’, declared with attribute warn_unused_result [-Werror=unused-result] write(STDOUT_FILENO, str, len); lib/fs.c: In function ‘lkl_mount_dev’: lib/fs.c:72:18: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] if (mnt_str_len < sizeof("/mnt/xxxxxxxx")) ^ lib/utils.c: In function ‘lkl_strerror’: lib/utils.c:147:10: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] if (err >= sizeof(lkl_err_strings) / sizeof(const char *)) lib/virtio.c: In function ‘virtio_read’: lib/virtio.c:216:19: warning: ‘val’ may be used uninitialized in this function [-Wmaybe-uninitialized] *(uint32_t *)res = htole32(val); lklfuse.c: In function ‘lklfuse_readlink’: lklfuse.c:136:10: warning: comparison between signed and unsigned integer expressions [-Wsign-compare] if (ret == len) ^ lklfuse.c: In function ‘lklfuse_read’: lklfuse.c:234:23: warning: signed and unsigned type in conditional expression [-Wsign-compare] return ret < 0 ? ret : orig_size - size; ^ lklfuse.c: In function ‘lklfuse_write’: lklfuse.c:253:23: warning: signed and unsigned type in conditional expression [-Wsign-compare] return ret < 0 ? ret : orig_size - size; Signed-off-by: Conrad Meyer [Octavian: change lkl_mount_dev to take unsigned int size arg, squashed into single commit] Signed-off-by: Octavian Purdila --- tools/lkl/cptofs.c | 5 +++-- tools/lkl/fs2tar.c | 9 +++++++-- tools/lkl/include/lkl.h | 2 +- tools/lkl/lib/fs.c | 4 ++-- tools/lkl/lib/posix-host.c | 4 +++- tools/lkl/lib/utils.c | 2 +- tools/lkl/lib/virtio.c | 5 ++--- tools/lkl/lklfuse.c | 12 ++++++------ tools/lkl/tests/boot.c | 4 +++- 9 files changed, 28 insertions(+), 19 deletions(-) diff --git a/tools/lkl/cptofs.c b/tools/lkl/cptofs.c index 0017395eacf119..cd4c1e8c5950d3 100644 --- a/tools/lkl/cptofs.c +++ b/tools/lkl/cptofs.c @@ -425,11 +425,12 @@ int main(int argc, char **argv) goto out; } - disk_id = lkl_disk_add(bs); - if (disk_id < 0) { + ret = lkl_disk_add(bs); + if (ret < 0) { fprintf(stderr, "can't add disk: %s\n", lkl_strerror(ret)); goto out_close; } + disk_id = ret; lkl_start_kernel(&lkl_host_ops, 100 * 1024 * 1024, ""); diff --git a/tools/lkl/fs2tar.c b/tools/lkl/fs2tar.c index b74da6628fd85f..02f5ba75e0347b 100644 --- a/tools/lkl/fs2tar.c +++ b/tools/lkl/fs2tar.c @@ -1,3 +1,7 @@ +#ifdef __FreeBSD__ +#include +#endif + #include #include #include @@ -351,11 +355,12 @@ int main(int argc, char **argv) goto out; } - disk_id = lkl_disk_add(bs); - if (disk_id < 0) { + ret = lkl_disk_add(bs); + if (ret < 0) { fprintf(stderr, "can't add disk: %s\n", lkl_strerror(ret)); goto out_close; } + disk_id = ret; lkl_start_kernel(&lkl_host_ops, 10 * 1024 * 1024, ""); diff --git a/tools/lkl/include/lkl.h b/tools/lkl/include/lkl.h index 220235b14bc3a1..a59c872fa4c613 100644 --- a/tools/lkl/include/lkl.h +++ b/tools/lkl/include/lkl.h @@ -57,7 +57,7 @@ int lkl_disk_add(union lkl_disk_backstore backstore); * @returns - 0 on success, a negative value on error */ long lkl_mount_dev(unsigned int disk_id, const char *fs_type, int flags, - void *data, char *mnt_str, int mnt_str_len); + void *data, char *mnt_str, unsigned int mnt_str_len); /** * lkl_umount_dev - umount a disk diff --git a/tools/lkl/lib/fs.c b/tools/lkl/lib/fs.c index d09ae061a60abc..70e55f17da90ad 100644 --- a/tools/lkl/lib/fs.c +++ b/tools/lkl/lib/fs.c @@ -63,13 +63,13 @@ static long get_virtio_blkdev(int disk_id) } long lkl_mount_dev(unsigned int disk_id, const char *fs_type, int flags, - void *data, char *mnt_str, int mnt_str_len) + void *data, char *mnt_str, unsigned int mnt_str_len) { char dev_str[] = { "/dev/xxxxxxxx" }; unsigned int dev; int err; - if (mnt_str_len < sizeof("/mnt/xxxxxxxx")) + if (mnt_str_len < sizeof(dev_str)) return -LKL_ENOMEM; dev = get_virtio_blkdev(disk_id); diff --git a/tools/lkl/lib/posix-host.c b/tools/lkl/lib/posix-host.c index 4bc1de70c3d715..5b30ee30f02393 100644 --- a/tools/lkl/lib/posix-host.c +++ b/tools/lkl/lib/posix-host.c @@ -17,7 +17,9 @@ static void print(const char *str, int len) { - write(STDOUT_FILENO, str, len); + int ret __attribute__((unused)); + + ret = write(STDOUT_FILENO, str, len); } struct pthread_sem { diff --git a/tools/lkl/lib/utils.c b/tools/lkl/lib/utils.c index f8b676f983037a..d6a76e69556bc1 100644 --- a/tools/lkl/lib/utils.c +++ b/tools/lkl/lib/utils.c @@ -144,7 +144,7 @@ const char *lkl_strerror(int err) if (err < 0) err = -err; - if (err >= sizeof(lkl_err_strings) / sizeof(const char *)) + if ((size_t)err >= sizeof(lkl_err_strings) / sizeof(const char *)) return "Bad error code"; return lkl_err_strings[err]; diff --git a/tools/lkl/lib/virtio.c b/tools/lkl/lib/virtio.c index 17522b2aeb106a..5b961ef5e2a93b 100644 --- a/tools/lkl/lib/virtio.c +++ b/tools/lkl/lib/virtio.c @@ -165,7 +165,6 @@ static int virtio_read(void *data, int offset, void *res, int size) { uint32_t val; struct virtio_dev *dev = (struct virtio_dev *)data; - int ret = 0; if (offset >= VIRTIO_MMIO_CONFIG) { offset -= VIRTIO_MMIO_CONFIG; @@ -210,12 +209,12 @@ static int virtio_read(void *data, int offset, void *res, int size) val = dev->config_gen; break; default: - ret = -1; + return -1; } *(uint32_t *)res = htole32(val); - return ret; + return 0; } static inline void set_ptr_low(void **ptr, uint32_t val) diff --git a/tools/lkl/lklfuse.c b/tools/lkl/lklfuse.c index 8562b14523b882..0823ab07c268f8 100644 --- a/tools/lkl/lklfuse.c +++ b/tools/lkl/lklfuse.c @@ -133,8 +133,8 @@ static int lklfuse_readlink(const char *path, char *buf, size_t len) if (ret < 0) return ret; - if (ret == len) - ret = len - 1; + if ((size_t)ret == len) + ret = len - 1; buf[ret] = 0; @@ -220,7 +220,7 @@ static int lklfuse_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { long ret; - int orig_size = size; + ssize_t orig_size = size; do { ret = lkl_sys_pread64(fi->fh, buf, size, offset); @@ -231,7 +231,7 @@ static int lklfuse_read(const char *path, char *buf, size_t size, off_t offset, buf += ret; } while (size > 0); - return ret < 0 ? ret : orig_size - size; + return ret < 0 ? ret : orig_size - (ssize_t)size; } @@ -239,7 +239,7 @@ static int lklfuse_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { long ret; - int orig_size = size; + ssize_t orig_size = size; do { ret = lkl_sys_pwrite64(fi->fh, buf, size, offset); @@ -250,7 +250,7 @@ static int lklfuse_write(const char *path, const char *buf, size_t size, buf += ret; } while (size > 0); - return ret < 0 ? ret : orig_size - size; + return ret < 0 ? ret : orig_size - (ssize_t)size; } diff --git a/tools/lkl/tests/boot.c b/tools/lkl/tests/boot.c index 8b401b729ba817..19d97c903b5987 100644 --- a/tools/lkl/tests/boot.c +++ b/tools/lkl/tests/boot.c @@ -50,8 +50,10 @@ static int parse_opt(int key, char *arg) void printk(const char *str, int len) { + int ret __attribute__((unused)); + if (cla.printk) - write(STDOUT_FILENO, str, len); + ret = write(STDOUT_FILENO, str, len); } #define TEST(name) do_test(#name, test_##name) From 3aa876882468d48ab90d4de9f95953a76dea1f0e Mon Sep 17 00:00:00 2001 From: Conrad Meyer Date: Sun, 8 Nov 2015 19:54:59 -0800 Subject: [PATCH 45/47] lkl: add support for 64bit FreeBSD Signed-off-by: Conrad Meyer [Octavian: use OUTPUT_FORMAT to detect 64bit FreeBSD, squash commits] Signed-off-by: Octavian Purdila --- arch/lkl/Kconfig | 1 + arch/lkl/Makefile | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/lkl/Kconfig b/arch/lkl/Kconfig index 567533bd0004c2..77fe2e3c2b7f2a 100644 --- a/arch/lkl/Kconfig +++ b/arch/lkl/Kconfig @@ -19,6 +19,7 @@ config LKL select PHYS_ADDR_T_64BIT if 64BIT select 64BIT if OUTPUT_FORMAT = "elf64-x86-64" select HAVE_UNDERSCORE_SYMBOL_PREFIX if OUTPUT_FORMAT = "pe-i386" + select 64BIT if OUTPUT_FORMAT = "elf64-x86-64-freebsd" config OUTPUTFORMAT string diff --git a/arch/lkl/Makefile b/arch/lkl/Makefile index c3bbf01c6f18ad..5fdec6426c19db 100644 --- a/arch/lkl/Makefile +++ b/arch/lkl/Makefile @@ -2,12 +2,14 @@ include arch/lkl/auto.conf KBUILD_CFLAGS += -fno-builtin -ifeq ($(OUTPUT_FORMAT),elf64-x86-64) +ifneq (,$(filter $(OUTPUT_FORMAT),elf64-x86-64 elf64-x86-64-freebsd)) KBUILD_CFLAGS += -fPIC else ifeq ($(OUTPUT_FORMAT),pe-i386) prefix=_ # workaround for #include_next errors LINUXINCLUDE := -isystem arch/lkl/include/system $(LINUXINCLUDE) +else +$(error Unrecognized platform: $(OUTPUT_FORMAT)) endif LDFLAGS_vmlinux += -r From dc4223e24a31873b3cf9cd64c00c2d6ea6ecea28 Mon Sep 17 00:00:00 2001 From: Conrad Meyer Date: Sun, 8 Nov 2015 20:03:17 -0800 Subject: [PATCH 46/47] lkl: On FreeBSD, use hw.ncpu sysctl instead of Linux nproc(1) Signed-off-by: Conrad Meyer Signed-off-by: Octavian Purdila --- arch/lkl/Makefile | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/arch/lkl/Makefile b/arch/lkl/Makefile index 5fdec6426c19db..9b23c736fd9c35 100644 --- a/arch/lkl/Makefile +++ b/arch/lkl/Makefile @@ -12,6 +12,12 @@ else $(error Unrecognized platform: $(OUTPUT_FORMAT)) endif +ifneq (,$(filter $(OUTPUT_FORMAT),elf64-x86-64-freebsd)) +NPROC=$(shell sysctl -n hw.ncpu) +else +NPROC=$(shell nproc) +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 @@ -27,7 +33,7 @@ install: lkl.o __headers @echo " INSTALL $(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))) \ + $(subst -j,-j$(NPROC),$(findstring -j,$(MAKEFLAGS))) \ $(INSTALL_PATH)/include archclean: From 12f7c7375d8c212590edfb42acdf821f3e507169 Mon Sep 17 00:00:00 2001 From: Conrad Meyer Date: Wed, 11 Nov 2015 15:16:15 +0200 Subject: [PATCH 47/47] lkl tools: add support for 64bit FreeBSD Signed-off-by: Conrad Meyer [Octavian: squash commits] Signed-off-by: Octavian Purdila --- Documentation/lkl.txt | 15 +++++++++++++++ tools/lkl/Makefile | 8 ++++++-- tools/lkl/cptofs.c | 4 ++++ tools/lkl/lib/posix-host.c | 6 +++++- tools/lkl/lib/virtio.h | 8 ++++++-- 5 files changed, 36 insertions(+), 5 deletions(-) diff --git a/Documentation/lkl.txt b/Documentation/lkl.txt index c9e30f7455c86e..4da91f11d7f995 100644 --- a/Documentation/lkl.txt +++ b/Documentation/lkl.txt @@ -19,6 +19,21 @@ LKL is implemented as an architecture port in arch/lkl. It uses host operations defined by the application or a host library (tools/lkl/lib). +Building LKL on FreeBSD +----------------------- + +$ pkg install binutils gcc49 + +If you don't have a gcc binary: +$ ln -sf /usr/local/bin/gcc49 /usr/local/bin/gcc + +Prefer ports binutils: +$ export PATH=/sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/usr/lib64/ccache + +$ cd tools/lkl +$ gmake + + Building LKL the host library and LKL applications -------------------------------------------------- diff --git a/tools/lkl/Makefile b/tools/lkl/Makefile index 4c0227922be17a..6b50988d701e91 100644 --- a/tools/lkl/Makefile +++ b/tools/lkl/Makefile @@ -13,7 +13,7 @@ OUTPUT_FORMAT=$(shell $(LD) -r -print-output-format) lib_source = $(filter-out %-host.c,$(wildcard lib/*.c)) source = $(wildcard tests/*.c) -ifneq (,$(filter $(OUTPUT_FORMAT),elf64-x86-64 elf32-i386)) +ifneq (,$(filter $(OUTPUT_FORMAT),elf64-x86-64 elf32-i386 elf64-x86-64-freebsd)) source += $(wildcard *.c) lib_source += lib/posix-host.c LDFLAGS += -lpthread -lrt @@ -26,7 +26,7 @@ else $(error Unrecognized platform: $(OUTPUT_FORMAT)) endif -ifneq (,$(filter $(OUTPUT_FORMAT),elf64-x86-64)) +ifneq (,$(filter $(OUTPUT_FORMAT),elf64-x86-64 elf64-x86-64-freebsd)) CFLAGS += -D_FILE_OFFSET_BITS=64 endif @@ -56,5 +56,9 @@ clean: fs2tar: LDFLAGS += -larchive lklfuse: LDFLAGS += -lfuse +ifneq (,$(filter $(OUTPUT_FORMAT),elf64-x86-64-freebsd)) +cptofs: LDFLAGS += -largp +fs2tar: LDFLAGS += -largp +endif cpfromfs: cptofs if ! [ -e $@ ]; then ln -s $< $@; fi diff --git a/tools/lkl/cptofs.c b/tools/lkl/cptofs.c index cd4c1e8c5950d3..051e1e961076ed 100644 --- a/tools/lkl/cptofs.c +++ b/tools/lkl/cptofs.c @@ -1,3 +1,7 @@ +#ifdef __FreeBSD__ +#include +#endif + #include #include #include diff --git a/tools/lkl/lib/posix-host.c b/tools/lkl/lib/posix-host.c index 5b30ee30f02393..71fa943bf206c5 100644 --- a/tools/lkl/lib/posix-host.c +++ b/tools/lkl/lib/posix-host.c @@ -1,5 +1,5 @@ #include -#include +#include #include #include #include @@ -189,7 +189,11 @@ void fd_do_rw(union lkl_disk_backstore bs, unsigned int type, unsigned int prio, break; case LKL_DEV_BLK_TYPE_FLUSH: case LKL_DEV_BLK_TYPE_FLUSH_OUT: +#ifdef __linux__ err = fdatasync(bs.fd); +#else + err = fsync(bs.fd); +#endif break; default: lkl_dev_blk_complete(bufs, LKL_DEV_BLK_STATUS_UNSUP, 0); diff --git a/tools/lkl/lib/virtio.h b/tools/lkl/lib/virtio.h index b76b18b74ca560..4ca774ba26e2e1 100644 --- a/tools/lkl/lib/virtio.h +++ b/tools/lkl/lib/virtio.h @@ -82,13 +82,17 @@ void virtio_dev_complete(struct virtio_dev_req *req, uint32_t len); (type *)((char *)(ptr) - __builtin_offsetof(type, member)) #ifndef __MINGW32__ -#include +#ifdef __FreeBSD__ +#include #else +#include +#endif /* __FreeBSD__ */ +#else /* !__MINGW32__ */ #define le32toh(x) (x) #define le16toh(x) (x) #define htole32(x) (x) #define htole16(x) (x) #define le64toh(x) (x) -#endif +#endif /* __MINGW32__ */ #endif /* _LKL_LIB_VIRTIO_H */