diff --git a/Documentation/lkl.txt b/Documentation/lkl.txt new file mode 100644 index 00000000000000..4da91f11d7f995 --- /dev/null +++ b/Documentation/lkl.txt @@ -0,0 +1,80 @@ + +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 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 +-------------------------------------------------- + +% 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. + + diff --git a/MAINTAINERS b/MAINTAINERS index 77ed3a02862588..4e7866916c08cc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6355,6 +6355,12 @@ F: arch/powerpc/platforms/pasemi/ F: drivers/*/*pasemi* F: drivers/*/*/*pasemi* +LINUX KERNEL LIBRARY +M: Octavian Purdila +S: Maintained +F: arch/lkl/ +F: tools/lkl/ + LINUX SECURITY MODULE (LSM) FRAMEWORK M: Chris Wright L: linux-security-module@vger.kernel.org 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),) 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..77fe2e3c2b7f2a --- /dev/null +++ b/arch/lkl/Kconfig @@ -0,0 +1,84 @@ +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" + select HAVE_UNDERSCORE_SYMBOL_PREFIX if OUTPUT_FORMAT = "pe-i386" + select 64BIT if OUTPUT_FORMAT = "elf64-x86-64-freebsd" + +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/Makefile b/arch/lkl/Makefile new file mode 100644 index 00000000000000..9b23c736fd9c35 --- /dev/null +++ b/arch/lkl/Makefile @@ -0,0 +1,47 @@ +include arch/lkl/auto.conf + +KBUILD_CFLAGS += -fno-builtin + +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 + +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 + +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 $(INSTALL_PATH)/lib/lkl.o" + @cp lkl.o $(INSTALL_PATH)/lib/ + @arch/lkl/scripts/headers_install.py \ + $(subst -j,-j$(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/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/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/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 */ + 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/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/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..9bb4593c9b3312 --- /dev/null +++ b/arch/lkl/include/asm/pgtable.h @@ -0,0 +1,61 @@ +#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.. + */ +extern void *empty_zero_page; +#define ZERO_PAGE(vaddr) (virt_to_page(empty_zero_page)) + +/* + * 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/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/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/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/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/asm/vmlinux.lds.h b/arch/lkl/include/asm/vmlinux.lds.h new file mode 100644 index 00000000000000..7c1a640fb6e306 --- /dev/null +++ b/arch/lkl/include/asm/vmlinux.lds.h @@ -0,0 +1,10 @@ +#ifndef _LKL_VMLINUX_LDS_H +#define _LKL_VMLINUX_LDS_H + +#ifdef __MINGW32__ +#define RODATA_SECTION .rdata +#endif + +#include + +#endif 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/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/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 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 new file mode 100644 index 00000000000000..77aba401107bdd --- /dev/null +++ b/arch/lkl/include/uapi/asm/sigcontext.h @@ -0,0 +1,15 @@ +#ifndef _ASM_UAPI_LKL_SIGCONTEXT_H +#define _ASM_UAPI_LKL_SIGCONTEXT_H + +#include + +struct pt_regs { + void *irq_data; +}; + +struct sigcontext { + struct pt_regs regs; + unsigned long oldmask; +}; + +#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..224a4c08df046f --- /dev/null +++ b/arch/lkl/include/uapi/asm/unistd.h @@ -0,0 +1,281 @@ +#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 +#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 64 +#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_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); +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); +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); + +#endif /* __KERNEL__ */ + +#endif /* _ASM_UAPI_LKL_UNISTD_H */ 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 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/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); + 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"); +} diff --git a/arch/lkl/kernel/mem.c b/arch/lkl/kernel/mem.c new file mode 100644 index 00000000000000..f6d1ee74ec1046 --- /dev/null +++ b/arch/lkl/kernel/mem.c @@ -0,0 +1,72 @@ +#include +#include +#include + +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; + + _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); + + empty_zero_page = alloc_bootmem_pages(PAGE_SIZE); + memset((void *)empty_zero_page, 0, PAGE_SIZE); + + { + 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); +} 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/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); diff --git a/arch/lkl/kernel/syscalls.c b/arch/lkl/kernel/syscalls.c new file mode 100644 index 00000000000000..51031cbfa61faa --- /dev/null +++ b/arch/lkl/kernel/syscalls.c @@ -0,0 +1,222 @@ +#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); + 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) +{ + 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/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); +} 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"); +} 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 +} diff --git a/arch/lkl/scripts/headers_install.py b/arch/lkl/scripts/headers_install.py new file mode 100755 index 00000000000000..cada8e9c66ba8b --- /dev/null +++ b/arch/lkl/scripts/headers_install.py @@ -0,0 +1,117 @@ +#!/usr/bin/env 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() 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/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) = .; \ 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) 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/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) { 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; 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/checkpatch.pl b/scripts/checkpatch.pl index f2a1131b2f8baf..93557bfea27510 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/) { @@ -5526,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)/; 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 diff --git a/tools/lkl/.gitignore b/tools/lkl/.gitignore new file mode 100644 index 00000000000000..74e9c7756fd7f2 --- /dev/null +++ b/tools/lkl/.gitignore @@ -0,0 +1,5 @@ +tests/boot +fs2tar +cptofs +cpfromfs +lklfuse diff --git a/tools/lkl/Makefile b/tools/lkl/Makefile new file mode 100644 index 00000000000000..6b50988d701e91 --- /dev/null +++ b/tools/lkl/Makefile @@ -0,0 +1,64 @@ +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) + +lib_source = $(filter-out %-host.c,$(wildcard lib/*.c)) +source = $(wildcard tests/*.c) +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 +source += $(wildcard *.c) +execs = cpfromfs +else ifeq ($(OUTPUT_FORMAT),pe-i386) +lib_source += lib/nt-host.c +KOPT="KALLSYMS_EXTRA_PASS=1" +else +$(error Unrecognized platform: $(OUTPUT_FORMAT)) +endif + +ifneq (,$(filter $(OUTPUT_FORMAT),elf64-x86-64 elf64-x86-64-freebsd)) +CFLAGS += -D_FILE_OFFSET_BITS=64 +endif + +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/lkl.o + $(AR) -rc $@ $^ + +lib/lkl.o: + $(MAKE) -C ../.. ARCH=lkl defconfig + $(MAKE) -C ../.. ARCH=lkl $(KOPT) 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) lib/lkl.o $(objs) $(execs) + $(MAKE) -C ../.. ARCH=lkl 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 new file mode 100644 index 00000000000000..051e1e961076ed --- /dev/null +++ b/tools/lkl/cptofs.c @@ -0,0 +1,472 @@ +#ifdef __FreeBSD__ +#include +#endif + +#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; + } + + 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, ""); + + 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; +} diff --git a/tools/lkl/fs2tar.c b/tools/lkl/fs2tar.c new file mode 100644 index 00000000000000..02f5ba75e0347b --- /dev/null +++ b/tools/lkl/fs2tar.c @@ -0,0 +1,402 @@ +#ifdef __FreeBSD__ +#include +#endif + +#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; + } + + 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, ""); + + 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; +} 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..a59c872fa4c613 --- /dev/null +++ b/tools/lkl/include/lkl.h @@ -0,0 +1,119 @@ +#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); +} + +/** + * 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); + +/** + * lkl_disk_backstore - host dependend disk backstore + * + * @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; +}; + +/** + * 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); + +/** + * 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, unsigned 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 + */ +int 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); + +/** + * 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/include/lkl_host.h b/tools/lkl/include/lkl_host.h new file mode 100644 index 00000000000000..2dafaa803e9f77 --- /dev/null +++ b/tools/lkl/include/lkl_host.h @@ -0,0 +1,44 @@ +#ifndef _LKL_HOST_H +#define _LKL_HOST_H + +#include +#include + +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, ...); + +char lkl_virtio_devs[256]; + +struct lkl_dev_buf { + void *addr; + 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/.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 + diff --git a/tools/lkl/lib/fs.c b/tools/lkl/lib/fs.c new file mode 100644 index 00000000000000..70e55f17da90ad --- /dev/null +++ b/tools/lkl/lib/fs.c @@ -0,0 +1,227 @@ +#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, unsigned int mnt_str_len) +{ + char dev_str[] = { "/dev/xxxxxxxx" }; + unsigned int dev; + int err; + + if (mnt_str_len < sizeof(dev_str)) + 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; +} + +int lkl_closedir(struct lkl_dir *dir) +{ + 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) +{ + 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; +} + +int lkl_dirfd(struct lkl_dir *dir) +{ + return dir->fd; +} 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 */ 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/posix-host.c b/tools/lkl/lib/posix-host.c new file mode 100644 index 00000000000000..71fa943bf206c5 --- /dev/null +++ b/tools/lkl/lib/posix-host.c @@ -0,0 +1,212 @@ +#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) +{ + int ret __attribute__((unused)); + + ret = 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: +#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); + 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, +}; diff --git a/tools/lkl/lib/utils.c b/tools/lkl/lib/utils.c new file mode 100644 index 00000000000000..d6a76e69556bc1 --- /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 ((size_t)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; +} diff --git a/tools/lkl/lib/virtio.c b/tools/lkl/lib/virtio.c new file mode 100644 index 00000000000000..5b961ef5e2a93b --- /dev/null +++ b/tools/lkl/lib/virtio.c @@ -0,0 +1,364 @@ +#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; + + 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: + return -1; + } + + *(uint32_t *)res = htole32(val); + + return 0; +} + +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@0x%lx:%d", + mmio_size, (uintptr_t)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..4ca774ba26e2e1 --- /dev/null +++ b/tools/lkl/lib/virtio.h @@ -0,0 +1,98 @@ +#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)) + +#ifndef __MINGW32__ +#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 /* __MINGW32__ */ + +#endif /* _LKL_LIB_VIRTIO_H */ 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; +} diff --git a/tools/lkl/lklfuse.c b/tools/lkl/lklfuse.c new file mode 100644 index 00000000000000..0823ab07c268f8 --- /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 ((size_t)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; + ssize_t 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 - (ssize_t)size; + +} + +static int lklfuse_write(const char *path, const char *buf, size_t size, + off_t offset, struct fuse_file_info *fi) +{ + long ret; + ssize_t 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 - (ssize_t)size; +} + + +static int lklfuse_statfs(const char *path, struct statvfs *stat) +{ + long ret; + struct lkl_statfs64 lkl_statfs; + + ret = lkl_sys_statfs64(path, sizeof(lkl_statfs), &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; +} diff --git a/tools/lkl/tests/boot.c b/tools/lkl/tests/boot.c new file mode 100644 index 00000000000000..19d97c903b5987 --- /dev/null +++ b/tools/lkl/tests/boot.c @@ -0,0 +1,516 @@ +#include +#include +#include +#include +#include +#include +#ifndef __MINGW32__ +#include +#endif +#include +#include +#ifndef __MINGW32__ +#include +#include +#else +#include +#endif + +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) +{ + int ret __attribute__((unused)); + + if (cla.printk) + ret = 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 + +#ifndef __MINGW32__ +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; +} +#endif + +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) +{ +#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); + if (disk_id < 0) + goto out_close; + + 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); + + 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); +#ifndef __MINGW32__ + TEST(nanosleep); +#endif + 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