Repository: Piko-RT/pikoRT Branch: master Commit: 569cee508b34 Files: 242 Total size: 316.2 KB Directory structure: gitextract_0chc9o9f/ ├── .clang-format ├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── arch/ │ ├── v7m-entry.S │ ├── v7m-faults.c │ ├── v7m-head.S │ └── v7m-svcall.S ├── docs/ │ ├── Makefile │ ├── api.rst │ ├── build.rst │ ├── conf.py │ ├── contribute.rst │ ├── debugging.rst │ ├── emulate.rst │ ├── index.rst │ ├── kernel.rst │ ├── make.bat │ ├── posix/ │ │ ├── parse.py │ │ ├── posix_opt/ │ │ │ ├── 01._POSIX_CLOCK_SELECTION │ │ │ ├── 02._POSIX_FSYNC │ │ │ ├── 03._POSIX_MEMLOCK │ │ │ ├── 04.POSIX_MEMLOCK_RANGE │ │ │ ├── 05._POSIX_MONOTONIC_CLOCK │ │ │ ├── 06._POSIX_NO_TRUNC │ │ │ ├── 07._POSIX_REALTIME_SIGNALS │ │ │ ├── 08._POSIX_SEMAPHORES │ │ │ ├── 09._POSIX_SHARED_MEMORY_OBJECTS │ │ │ ├── 10._POSIX_SYNCHRONIZED_IO │ │ │ ├── 11._POSIX_THREAD_ATTR_STACKADDR │ │ │ ├── 12._POSIX_THREAD_ATTR_STACKSIZE │ │ │ ├── 13._POSIX_THREAD_CPUTIME │ │ │ ├── 14._POSIX_THREAD_PRIO_INHERIT │ │ │ ├── 15._POSIX_THREAD_PRIO_PROTECT │ │ │ ├── 16._POSIX_THREAD_PRIORITY_SCHEDULING │ │ │ ├── 17._POSIX_THREAD_SPORADIC_SERVER │ │ │ └── 18._POSIX_TIMERS │ │ └── posix_req/ │ │ ├── 01.POSIX_C_LANG_SUPPORT │ │ ├── 02.POSIX_DEVICE_IO │ │ ├── 03.POSIX_FILE_LOCKING │ │ ├── 04.POSIX_SIGNALS │ │ ├── 05.POSIX_SINGLE_PROCESS │ │ ├── 06.POSIX_THREADS_BASE │ │ ├── 07.XSI_THREAD_MUTEX_EXT │ │ └── 08.XSI_THREADS_EXT │ └── test.rst ├── drivers/ │ ├── char/ │ │ ├── mem.c │ │ └── random.c │ ├── mtd/ │ │ ├── mtd.h │ │ ├── mtdchar.c │ │ ├── mtdcore.c │ │ └── mtdram.c │ ├── serial/ │ │ ├── serialchar.c │ │ ├── serialcore.c │ │ ├── stm32-uart.c │ │ └── stm32-uart.h │ └── timer/ │ ├── systick.c │ └── timercore.c ├── fs/ │ ├── proc.c │ ├── romfs.c │ └── tmpfs.c ├── include/ │ ├── arch/ │ │ ├── semihosting.h │ │ └── v7m-helper.h │ ├── fs/ │ │ └── romfs.h │ ├── kernel/ │ │ ├── bitmap.h │ │ ├── bitops.h │ │ ├── cbuf.h │ │ ├── compiler.h │ │ ├── cond.h │ │ ├── errno-base.h │ │ ├── faults.h │ │ ├── fs.h │ │ ├── hash.h │ │ ├── irq.h │ │ ├── kernel.h │ │ ├── linkage.h │ │ ├── log2.h │ │ ├── mm/ │ │ │ ├── page.h │ │ │ └── slab.h │ │ ├── mutex.h │ │ ├── sched.h │ │ ├── serial.h │ │ ├── signal.h │ │ ├── softirq.h │ │ ├── task.h │ │ ├── thread.h │ │ ├── time.h │ │ └── types.h │ ├── libc/ │ │ ├── pthread.h │ │ ├── ucontext.h │ │ └── utils.h │ ├── linux/ │ │ ├── compiler.h │ │ ├── list.h │ │ ├── poison.h │ │ ├── stddef.h │ │ └── types.h │ ├── piko/ │ │ ├── arpa/ │ │ │ └── inet.h │ │ ├── dirent.h │ │ ├── signal.h │ │ └── sys/ │ │ ├── mman.h │ │ ├── mount.h │ │ └── resource.h │ ├── platform/ │ │ └── compiler.h │ └── version.template.h ├── kernel/ │ ├── cond.c │ ├── config.c │ ├── faults.c │ ├── fs/ │ │ ├── fs.c │ │ ├── readdir.c │ │ └── vfs.c │ ├── irq.c │ ├── main.c │ ├── mm/ │ │ ├── mm.c │ │ ├── page.c │ │ └── slab.c │ ├── mutex.c │ ├── printk.c │ ├── resource.c │ ├── sched/ │ │ ├── bitmap.c │ │ └── rr.c │ ├── sched.c │ ├── signal.c │ ├── softirq.c │ ├── task.c │ ├── thread.c │ └── time.c ├── libc/ │ ├── fcntl.c │ ├── filesystem.c │ ├── piko/ │ │ ├── mman.c │ │ ├── stubs.c │ │ ├── syscalls.S │ │ └── syscalls.h │ ├── pthread.c │ ├── signal.c │ ├── stdio.c │ ├── stdlib.c │ ├── time.c │ ├── ucontext.c │ ├── unistd.c │ ├── utils.c │ └── v7m-pthread.S ├── mk/ │ ├── cmsis.mk │ ├── flags.mk │ └── rules.mk ├── piko.lds.S ├── platform/ │ ├── f429disco/ │ │ ├── Makefile │ │ ├── build.mk │ │ ├── halt.c │ │ ├── init.c │ │ ├── platform.h │ │ └── uart.c │ └── stm32p103/ │ ├── Makefile │ ├── build.mk │ ├── halt.c │ ├── init.c │ ├── platform.h │ └── uart.c ├── scripts/ │ ├── gen-proc-version.py │ ├── gen-syscalls.py │ └── rstlint.py ├── tests/ │ ├── Makefile │ ├── __init__.py │ ├── __main__.py │ ├── bitops_1/ │ │ └── main.c │ ├── cond_1/ │ │ └── main.c │ ├── cond_2/ │ │ └── main.c │ ├── cond_3/ │ │ └── main.c │ ├── fs_1/ │ │ └── main.c │ ├── fs_2/ │ │ └── main.c │ ├── fs_3/ │ │ ├── data/ │ │ │ ├── id_rsa │ │ │ └── id_rsa.pub │ │ └── main.c │ ├── fs_4/ │ │ ├── data/ │ │ │ ├── id_rsa │ │ │ └── id_rsa.pub │ │ └── main.c │ ├── fs_5/ │ │ └── main.c │ ├── fs_6/ │ │ ├── data/ │ │ │ ├── id_rsa │ │ │ └── id_rsa.pub │ │ └── main.c │ ├── fs_7/ │ │ ├── data/ │ │ │ └── .ssh/ │ │ │ ├── id_rsa │ │ │ └── id_rsa.pub │ │ └── main.c │ ├── getpid_1/ │ │ └── main.c │ ├── itoa_1/ │ │ └── main.c │ ├── lib/ │ │ └── unit.h │ ├── malloc_1/ │ │ └── main.c │ ├── mm_1/ │ │ └── main.c │ ├── mm_2/ │ │ └── main.c │ ├── mmap_1/ │ │ └── main.c │ ├── mmap_2/ │ │ ├── data/ │ │ │ ├── id_rsa │ │ │ └── id_rsa.pub │ │ └── main.c │ ├── msleep_1/ │ │ └── main.c │ ├── msleep_2/ │ │ └── main.c │ ├── mtdram_1/ │ │ └── main.c │ ├── mutex_1/ │ │ └── main.c │ ├── mutex_2/ │ │ └── main.c │ ├── mutex_3/ │ │ └── main.c │ ├── mutex_4/ │ │ └── main.c │ ├── mutex_5/ │ │ └── main.c │ ├── page_3/ │ │ └── main.c │ ├── raise_1/ │ │ └── main.c │ ├── raise_2/ │ │ └── main.c │ ├── raise_3/ │ │ └── main.c │ ├── readdir_1/ │ │ └── main.c │ ├── runner.py │ ├── slab_1/ │ │ └── main.c │ ├── slab_2/ │ │ └── main.c │ ├── softirq_1/ │ │ └── main.c │ ├── softirq_2/ │ │ └── main.c │ ├── softirq_3/ │ │ └── main.c │ ├── sprintf_1/ │ │ └── main.c │ ├── stat_1/ │ │ ├── data/ │ │ │ ├── id_rsa │ │ │ └── id_rsa.pub │ │ └── main.c │ ├── syscall_1/ │ │ ├── main.c │ │ └── trampoline.S │ ├── sysconf_1/ │ │ └── main.c │ ├── test_1/ │ │ └── main.c │ ├── test_2/ │ │ └── main.c │ ├── thread_1/ │ │ └── main.c │ ├── thread_2/ │ │ └── main.c │ ├── thread_3/ │ │ └── main.c │ ├── thread_4/ │ │ └── main.c │ ├── thread_5/ │ │ └── main.c │ ├── thread_6/ │ │ └── main.c │ ├── timer_1/ │ │ └── main.c │ ├── timer_2/ │ │ └── main.c │ ├── timer_3/ │ │ └── main.c │ ├── timer_4/ │ │ └── main.c │ ├── timer_5/ │ │ └── main.c │ └── ucontext_1/ │ └── main.c └── user/ ├── cat.c ├── echo.c ├── exit.c ├── halt.c ├── ls.c ├── reboot.c ├── sh.c └── sh.h ================================================ FILE CONTENTS ================================================ ================================================ FILE: .clang-format ================================================ BasedOnStyle: Chromium Language: Cpp MaxEmptyLinesToKeep: 3 IndentCaseLabels: false AllowShortIfStatementsOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortLoopsOnASingleLine: false DerivePointerAlignment: false PointerAlignment: Right SpaceAfterCStyleCast: true TabWidth: 4 UseTab: Never IndentWidth: 4 BreakBeforeBraces: Linux AccessModifierOffset: -4 ================================================ FILE: .gitignore ================================================ # external source external/ *~ *.o *.o.d *.elf *.map *.lds *.hex *.bin # generated fs/version include/kernel/syscalls.h kernel/syscall.c # QEMU generated DAC_OUT_PUT1.txt DAC_OUT_PUT2.txt # Python __pycache__ *.pyc # Sphinx _build ================================================ FILE: .travis.yml ================================================ sudo: required dist: trusty os: linux language: python cache: pip stage: - docs - core jobs: include: - stage: docs env: TESTING=docs python: 3.6 before_install: - sudo apt-get update -qq install: - sudo apt-get install cscope before_script: - cd docs - pip install sphinx script: - make check - make html - &core-stage stage: core python: 3.5 env: TESTING=Piko/RT before_install: - sudo add-apt-repository -y ppa:team-gcc-arm-embedded/ppa - sudo apt-get update -qq install: - sudo apt install build-essential - sudo apt-get install -y gcc-arm-embedded # QEMU deps - sudo apt install libxenstore3.0 - sudo apt install libxen-dev - sudo apt install genromfs - wget https://github.com/PikoRT/tools/raw/master/bin/x86_64-linux/qemu-system-arm - chmod 777 qemu-system-arm - export PATH=$PWD:$PATH before_script: - arm-none-eabi-gcc --version - python --version script: - make PLAT=stm32p103 - make PLAT=stm32p103 check - <<: *core-stage python: 3.6 ================================================ FILE: LICENSE ================================================ Piko/RT is freely redistributable under the two-clause BSD License: Copyright (c) 2017 Piko/RT Developers. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: Makefile ================================================ NAME = piko # select QEMU when the platform is unspecified PLAT ?= f429disco CMSIS = external/cmsis # The platform Makefile contains hw details and flags include platform/$(PLAT)/Makefile # arch-specific SSRC += arch/v7m-head.S arch/v7m-entry.S arch/v7m-svcall.S CSRC += arch/v7m-faults.c LIBPIKO_CSRC = $(wildcard libc/*.c) LIBPIKO_SSRC = $(wildcard libc/*.S) $(wildcard libc/piko/*.S) SSRC += $(LIBPIKO_SSRC) CSRC += kernel/syscall.c CSRC += \ $(wildcard kernel/*.c) \ $(wildcard kernel/fs/*.c) \ $(wildcard kernel/mm/*.c) \ $(wildcard kernel/sched/*.c) \ $(wildcard fs/*.c) \ $(wildcard drivers/char/*.c) \ $(wildcard drivers/mtd/*.c) \ $(wildcard drivers/timer/timercore.c) \ $(wildcard drivers/serial/serial*.c) \ $(wildcard user/*.c) \ libc/piko/stubs.c \ libc/piko/mman.c \ $(LIBPIKO_CSRC) OBJS += $(SSRC:.S=.o) $(CSRC:.c=.o) OBJS := $(sort $(OBJS)) deps := $(OBJS:%.o=.%.o.d) .PHONY: all check clean distclean check_cc all: check_cc $(CMSIS)/$(PLAT) $(NAME).lds $(NAME).bin # generic build rules include mk/flags.mk include mk/rules.mk include mk/cmsis.mk prebuild: $(CMSIS)/$(PLAT) check: check_cc $(PYTHON) -m tests -p $(PLAT) --qemu $(QEMU_SYSTEM_ARM) --cc $(CC) check_cc: @$(eval PYTHON_VERSION=$(shell echo `$(PYTHON) --version 2>&1 | grep -oE '[^ ]+$$'`)) @$(eval PYTHON_VERSION=$(shell echo $(PYTHON_VERSION) | awk -F "." '{print $$1$$2 0}')) @if [ $(PYTHON_VERSION) -lt 350 ]; then \ echo "Error: Python version must >= 3.5, use PYTHON=/python/binary/path"; \ return 1;\ fi; clean: find . -name "*.o" -type f -delete find . -name "*.o.d" -type f -delete rm -f $(deps) ifneq "$(wildcard $(CMSIS) )" "" find $(CMSIS) -name "*.o" -type f -delete endif rm -f $(NAME).map $(NAME).lds rm -f $(NAME).elf $(NAME).bin # Remove GEN files rm -f include/kernel/syscalls.h rm -f kernel/syscall.c rm -f fs/version distclean: clean rm -f kernel/syscall.c include/kernel/syscalls.h fs/version rm -rf $(CMSIS) # platform build contains flashing and running rules include platform/$(PLAT)/build.mk -include $(deps) ================================================ FILE: README.md ================================================ # Piko/RT This is `Piko/RT`, a tiny Linux-like real-time operating system kernel, optimized for ARM Cortex-M series microprocessors. Prerequisites ------------- - [QEMU with an STM32 microcontroller implementation](https://beckus.github.io/qemu_stm32/) ## Run test suite * Run all test ```shell $ make PLAT=stm32p103 check ``` * Run all test with command line tools ```shell $ python -m tests ``` * Run specific test cases ```shell $ python -m tests fs_1 cond_2 ``` ## External Source * `scripts/rstlint.py`: written by Georg Brandl ================================================ FILE: arch/v7m-entry.S ================================================ #include .syntax unified .thumb @ do the thread-context switch @ @ r0: struct thread_info *next @ r1: struct thread_info *prev (i.e. the current thread) @ (r1), r2, r3, r12: scratch registers ENTRY(switch_to) /* save previous thread context */ push {r4-r12, lr} mov r2, sp mrs r3, psp stm r1!, {r2, r3} ENTRY(thread_restore) /* restore next task context */ ldm r0!, {r1-r3} mov sp, r1 msr psp, r2 msr control, r3 @ write to SPSEL is ignored in Handler_Mode pop {r4-r12, pc} ENDPROC(thread_restore) ENDPROC(switch_to) .macro emulate_iret ldrd r0, r1, [sp, #24] /* r0 = ret_addr, r1 = xpsr */ orr r0, #1 str r0, [sp, #24] msr apsr_nzcvq, r1 /* restore flags */ pop {r0-r3, r12, lr} ldr pc, [sp], #8 /* return into user thread */ .endm ENTRY(return_from_sigaction) add sp, #12 /* reclaim siginfo_t storage */ emulate_iret END(return_from_sigaction) ENTRY(return_from_sighandler) emulate_iret END(return_from_sighandler) .set MC_SP, 0 .set MC_LR, 4 .set MC_GPRS, 8 .set MC_PC, 15*4 .set UC_MCONTEXT, 16 .set UC_LINK, 0 /* Swap context to a clean context (created from makecontext()): * - save the non-scratch registers * - just restore scratch registers (for arguments to entry function) * * Swap to a dirty context (inited with getcontext, or return to a * swapped context): * - save the non-scratch registers * - restore all registers */ ENTRY(swapcontext) /* save to oucp */ add r0, #UC_MCONTEXT str sp, [r0, #MC_SP] str lr, [r0, #MC_LR] add r0, #MC_GPRS stm r0, {r0-r12, lr} /* retore from ucp */ add r1, #UC_MCONTEXT ldr sp, [r1, #MC_SP] ldr lr, [r1, #MC_LR] add r1, #MC_GPRS ldm r1, {r0-r12, pc} ENDPROC(swapcontext) ENTRY(return_from_makecontext) /* retrieve the struct& from the top of the stack */ ldr r0, [sp] /* get the machine struct for the linked context */ ldr r0, [r0, #UC_LINK] add r0, #UC_MCONTEXT /* restore the link context */ ldr sp, [r0, #MC_SP] ldr lr, [r0, #MC_LR] add r0, #MC_GPRS //FIXME: return code - the r0 value that is restored from the stack should be 0 ldm r0, {r0-r12, pc} ENDPROC(return_from_makecontext) .macro fault_handler f push {r4-r11} @ save non-scratch registers at time of fault mov r0, sp mov r2, lr @ lr contains EXC_RETURN, fault was taken in thread or interrupt? @ We don't handle faults taken in interrupt handler at the moment. @ Hence the scratch registers auto pushed to the stack by the CPU @ on interrupt-entry have been pushed to PSP (MSP is only used by @ interrupts handler). mrs r1, psp b \f .endm /* fault_handler */ ENTRY(hardf) fault_handler hardfault ENDPROC(hardf) ENTRY(memf) fault_handler memmanage ENDPROC(memf) ENTRY(busf) fault_handler busfault ENDPROC(busf) ENTRY(usgf) fault_handler usagefault ENDPROC(usgf) ENTRY(__do_idle) wfi bx lr ENDPROC(__do_idle) /* void v7m_semihost_exit(int status); */ ENTRY(v7m_semihost_exit) tst r0, r0 ite eq ldreq r1, =0x20026 /* [a] */ movne r1, #0 mov r0, #0x18 /* [b] */ bkpt #0xab 0: b 0b ENDPROC(v7m_semihost_exit) /* [a] https://lists.nongnu.org/archive/html/qemu-devel/2014-12/msg01575.html * ADP_Stopped_ApplicationExit is used for exit(0), anything else is * implemented as exit(1). */ /* [b] http://git.qemu.org/?p=qemu.git;a=blob_plain;f=target-arm/arm-semi.c;hb=HEAD * List of supported semihosting calls in Qemu. */ ================================================ FILE: arch/v7m-faults.c ================================================ #include #include #include "kernel.h" #define UFSR_DIVBYZERO (1 << 9) #define UFSR_UNALIGNED (1 << 8) #define UFSR_NOCP (1 << 3) #define UFSR_INVPC (1 << 2) #define UFSR_INVSTATE (1 << 1) #define UFSR_UNDEFINSTR 1 void dump_frame(struct kernel_context_regs *noscratch, struct thread_context_regs *scratch, u32 exc_return) { printk(" r0: %08x r1: %08x r2: %08x r3: %08x\n", scratch->r0_r3__r12[0], scratch->r0_r3__r12[1], scratch->r0_r3__r12[2], scratch->r0_r3__r12[3]); printk(" r4: %08x r5: %08x r6: %08x r7: %08x\n", noscratch->r4_r12[0], noscratch->r4_r12[1], noscratch->r4_r12[2], noscratch->r4_r12[3]); printk(" r8: %08x r9: %08x r10: %08x r11: %08x\n", noscratch->r4_r12[4], noscratch->r4_r12[5], noscratch->r4_r12[6], noscratch->r4_r12[7]); printk("r12: %08x sp: %08x lr: %08x pc: %08x\n", scratch->r0_r3__r12[4], (u32) scratch, scratch->lr, scratch->ret_addr); printk("\nEXC_RETURN: %08x\n", exc_return); } void usagefault(struct kernel_context_regs *noscratch, struct thread_context_regs *scratch, u32 exc_return) { u32 ufsr = (*((volatile u32 *) 0xe000ed28)) >> 16; const char *cause = NULL; fault_enter("UsageFault"); dump_frame(noscratch, scratch, exc_return); if (ufsr & UFSR_DIVBYZERO) cause = "DIVBYZERO"; else if (ufsr & UFSR_UNALIGNED) cause = "UNALIGNED"; else if (ufsr & UFSR_NOCP) cause = "NOCP"; else if (ufsr & UFSR_INVPC) cause = "INVPC"; else if (ufsr & UFSR_INVSTATE) cause = "INVSTATE"; else if (ufsr & UFSR_UNDEFINSTR) cause = "UNDEFINSTR"; if (cause) printk(" ufsr: %08x <%s>\n", ufsr, cause); else printk(" ufsr: %08x\n", ufsr); fault_exit(); } void busfault(struct kernel_context_regs *noscratch, struct thread_context_regs *scratch, u32 exc_return) { fault_enter("BusFault"); dump_frame(noscratch, scratch, exc_return); fault_exit(); } void memmanage(struct kernel_context_regs *noscratch, struct thread_context_regs *scratch, u32 exc_return) { fault_enter("MemManage"); dump_frame(noscratch, scratch, exc_return); fault_exit(); } ================================================ FILE: arch/v7m-head.S ================================================ #include .syntax unified .thumb .section ".vector", "a" .long __early_stack_start__ @ SP_main value .long reset @ Reset .long 0 @ NMI .long hardf @ HardFault .long memf @ MemManage .long busf @ BusFault .long usgf @ UsageFault .long 0 @ Reserved .long 0 @ Reserved .long 0 @ Reserved .long 0 @ Reserved .long svcall @ SVCall .long 0 @ Debug Monitor .long 0 @ Reserved .long pendsv @ PendSV .long systick @ SysTick .long irq_entry @ IRQ 0 .long irq_entry @ IRQ 1 .long irq_entry @ IRQ 2 .long irq_entry @ IRQ 3 .long irq_entry @ IRQ 4 .long irq_entry @ IRQ 5 .long irq_entry @ IRQ 6 .long irq_entry @ IRQ 7 .long irq_entry @ IRQ 8 .long irq_entry @ IRQ 9 .long irq_entry @ IRQ 10 .long irq_entry @ IRQ 11 .long irq_entry @ IRQ 12 .long irq_entry @ IRQ 13 .long irq_entry @ IRQ 14 .long irq_entry @ IRQ 15 .long irq_entry @ IRQ 16 .long irq_entry @ IRQ 17 .long irq_entry @ IRQ 18 .long irq_entry @ IRQ 19 .long irq_entry @ IRQ 20 .long irq_entry @ IRQ 21 .long irq_entry @ IRQ 22 .long irq_entry @ IRQ 23 .long irq_entry @ IRQ 24 .long irq_entry @ IRQ 25 .long irq_entry @ IRQ 26 .long irq_entry @ IRQ 27 .long irq_entry @ IRQ 28 .long irq_entry @ IRQ 29 .long irq_entry @ IRQ 30 .long irq_entry @ IRQ 31 .long irq_entry @ IRQ 32 .long irq_entry @ IRQ 33 .long irq_entry @ IRQ 34 .long irq_entry @ IRQ 35 .long irq_entry @ IRQ 36 .long irq_entry @ IRQ 37 .long irq_entry @ IRQ 38 .long irq_entry @ IRQ 39 .long irq_entry @ IRQ 40 .long irq_entry @ IRQ 41 .long irq_entry @ IRQ 42 .long irq_entry @ IRQ 43 .long irq_entry @ IRQ 44 .long irq_entry @ IRQ 45 .long irq_entry @ IRQ 46 .long irq_entry @ IRQ 47 .long irq_entry @ IRQ 48 .long irq_entry @ IRQ 49 .long irq_entry @ IRQ 50 .long irq_entry @ IRQ 51 .long irq_entry @ IRQ 52 .long irq_entry @ IRQ 53 .long irq_entry @ IRQ 54 .long irq_entry @ IRQ 55 .long irq_entry @ IRQ 56 .long irq_entry @ IRQ 57 .long irq_entry @ IRQ 58 .long irq_entry @ IRQ 59 .long irq_entry @ IRQ 60 .long irq_entry @ IRQ 61 .long irq_entry @ IRQ 62 .long irq_entry @ IRQ 63 .text /* * mov32 - loads a 32-bit value into a register without a data access */ .macro mov32 rd, imm32 movw \rd, #:lower16:\imm32 .if \imm32 & 0xffff0000 movt \rd, #:upper16:\imm32 .endif .endm ENTRY(reset) ldr r0, =SystemInit /* CMSIS system init */ blx r0 mov32 r0, 0xe000ed00 /* SCB_BASE */ @ switch to Handler_Mode ldr r1, =__early_stack_end__ /* allocate a temporary vector */ str r1, [r0, #8] /* update VTOR */ ldr r2, =0f orr r2, #1 /* set thumb bit */ str r2, [r1, #11 * 4] /* offset to SVC entry */ dsb /* [1] */ svc #0 0: @ restore the early vector ldr r1, =__vector_start__ str r1, [r0, #8] /* restore VTOR */ dsb /* [1] */ @ copy the initialized data sections ldr r0, =__data_start__ ldr r1, =__rodata_end__ ldr r2, =__data_size__ bl memcpy @ zero-fill the non-initialized data sections ldr r0, =__bss_start__ movs r1, #0 ldr r2, =__bss_size__ bl memset @ start_kernel procedure returns the first thread to run on the CPU bl start_kernel cmp r0, #0 itt ne ldrne r1, =thread_restore bxne r1 0: b 0b ENDPROC(reset) /* [1] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0321a/BIHDGBJG.html */ ENTRY(irq_entry) push {lr} ldr r0, =irq_handler mrs r1, ipsr sub r1, #16 ldr.w r0, [r0, r1, lsl #2] blx r0 pop {pc} ENDPROC(irq_entry) .weak systick systick: bx lr .balign 8 ================================================ FILE: arch/v7m-svcall.S ================================================ #include .syntax unified .thumb @ offsets in the frame to the registers saved on interrupt-entry .set R0, 0 .set RET_ADDRESS, 24 .set xPSR, 0x1c ENTRY(svcall) push {lr} mrs lr, psp ldr r12, [lr, #RET_ADDRESS] ldrb r12, [r12, #-2] @ address of the SVC call site tbb [pc, r12] 0: .irpc argc, 0123456 .byte ($\argc - 0b) / 2 .endr .balign 2 .balign 2 $0: ldr r12, =syscall_vect ldr.w r12, [r12, r0, lsl #2] blx r12 b 0f .balign 2 $1: ldr r12, =syscall_vect ldr.w r12, [r12, r1, lsl #2] blx r12 b 0f .balign 2 $2: ldr r12, =syscall_vect ldr.w r12, [r12, r2, lsl #2] blx r12 b 0f .balign 2 $3: ldr r12, =syscall_vect ldr.w r12, [r12, r3, lsl #2] blx r12 b 0f .balign 2 $4: ldr r12, [lr, #xPSR] @ test stack alignment tst r12, #1 << 9 ite eq ldreq lr, [lr, #0x20] @ load syscall id ldrne lr, [lr, #0x24] ldr r12, =syscall_vect ldr.w r12, [r12, lr, lsl #2] blx r12 b 0f .balign 2 $5: ldr r12, [lr, #xPSR] @ test stack alignment tst r12, #1 << 9 ite eq addeq lr, #0x20 addne lr, #0x24 ldm lr, {r12, lr} @ load syscall id, arg4 push {r12} @ copy arg4 to kernel stack ldr r12, =syscall_vect ldr.w r12, [r12, lr, lsl #2] blx r12 add sp, #4 b 0f .balign 2 $6: ldr r12, [lr, #xPSR] @ test stack alignment tst r12, #1 << 9 ite eq addeq lr, #0x20 addne lr, #0x24 ldm lr, {r11, r12, lr} @ load syscall id, arg4, arg5 push {r11, r12} @ copy arg4, arg5 to kernel stack ldr r12, =syscall_vect ldr.w r12, [r12, lr, lsl #2] blx r12 pop {r11, r12} //FIXME: pop {r11}; add sp, #4; .global syscall_return syscall_return: 0: mrs r1, psp str r0, [r1, #R0] @ update return value in exception frame pop {pc} ENDPROC(svcall) ================================================ FILE: docs/Makefile ================================================ # Piko/RT documentation # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build SPHINXPROJ = piko SOURCEDIR = . BUILDDIR = _build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help clean Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) clean: -rm -rf _build/* posix/posix_*.rst check: python3 ../scripts/rstlint.py ================================================ FILE: docs/api.rst ================================================ .. _api: Piko/RT POSIX API ================= ``Piko/RT`` aim to fulfill IEEE 1003.13-2003 PSE51 standard, this is the list of POSIX API that ``Piko/RT`` implemented. Units of Functionality Requirements ----------------------------------- .. include:: posix/posix_req.rst POSIX.1 Option Requirements --------------------------- .. include:: posix/posix_opt.rst ================================================ FILE: docs/build.rst ================================================ .. _build: Build Piko/RT ============= This chapter will help you start to build ``Piko/RT``, you will need to prepare some pacakge and settings. Prerequisites ------------- * svn: used to checkout `CMSIS `_. * wget: used to download required files. * arm-none-eabi-gcc arm-none-eabi-newlib: cross-compile toolchain. Arch Linux:: $ pacman -S base-devel svn lsb-release wget $ pacman -S arm-none-eabi-gcc arm-none-eabi-newlib $ pacman -S python Ubuntu:: $ add-apt-repository -y ppa:team-gcc-arm-embedded/ppa $ apt update -qq $ apt install build-essential svn gcc-arm-embedded macOS:: # If you didn't build any thing on macOS before # please install xcode-select first $ xcode-select --install # install brew from https://brew.sh $ brew install subversion wget $ brew tap PX4/homebrew-px4 $ brew install gcc-arm-none-eabi-49 Build Piko/RT on Linux and macOS -------------------------------- Default make will build for ``f429disco``:: $ make You can change the target via ``PLAT``:: $ make PLAT=stm32p103 You can build faster via passing ``-j`` flag:: $ make PLAT=stm32p103 -j8 You can build with verbose output:: $ make PLAT=stm32p103 -j8 VERBOSE=1 Troubleshooting --------------- If you concur problem when building Piko/RT, please help to fill the issue on `GitHub `_, and maybe help to improve the build process, or improve this troubleshooting part! ================================================ FILE: docs/conf.py ================================================ #!/usr/bin/env python3 # -*- coding: utf-8 -*- # # piko documentation build configuration file, created by # sphinx-quickstart on Tue Aug 15 18:28:39 2017. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # # import os # import sys # sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = 'Piko/RT' copyright = '2017, Piko/RT Developers' author = 'Piko/RT Developers' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '0.1' # The full version, including alpha/beta/rc tags. release = '0.1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'alabaster' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # -- Options for HTMLHelp output ------------------------------------------ # Output file base name for HTML help builder. htmlhelp_basename = 'pikodoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'piko.tex', 'piko Documentation', 'Piko/RT Developers', 'manual'), ] # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'piko', 'piko Documentation', [author], 1) ] # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'piko', 'piko Documentation', author, 'piko', 'One line description of project.', 'Miscellaneous'), ] # -- Generate POSIX API list ---------------------------------------------- import subprocess subprocess.check_output(['python3', 'posix/parse.py']) ================================================ FILE: docs/contribute.rst ================================================ .. _contribute: Contribute to Piko/RT ===================== We welcome everyone to help to develop Piko/RT. This will help you to find were to help, and how to submit a pull request to GitHub. How to contribute ----------------- There are a variety of ways to contribute to this project. Here are the list what you may help: * Write documentation. * Report issue when you occure. * Implement new feature. * Code review -- this is always helpful. * Make a bug fixed and send the patches. * Adding new test. * Integrate new feature -- benchmark, TCP stack, GNU utilt...etc. * Port to new paltform. Create your GitHub account -------------------------- If you have a GitHub account, please skip this section. If you don't have, please follow these step: * Click `Join GitHub `_ * Fill the form * Confirm your email Fork Piko/RT repo ----------------- * Go to `Piko/RT repo `_ * Click the buttom "Fork" at the right top side. * Done! Clone the Piko/RT repo ---------------------- You will need a copy of Piko/RT to work on the code:: $ git clone https://github.com//pikoRT Create a local Git branch ------------------------- If you make some changed to the project, and want to share with everyone, you will need to upload to GitHub. First, create a new branch:: $ git checkout -b Then commit the changed in this branch:: $ git add $ git commit Push it to your fork:: $ git push -u origin Publish your pull request ------------------------- You will then need to go to `Piko/RT's repo `_, Click ``Pull reuqests``, Click green button ``New pull request``. Then reference this tutorial from GitHub: `Creating a pull request from a fork `_ Git remotes ----------- Your local git repo's origin is your fork. So if you do something like this:: $ git pull You won't get the latest commit from Piko/RT. You will need to add a upstream, fetch from it, then rebase:: $ git remote add upstream git@github.com:Piko-RT/pikoRT.git $ git fetch upstream $ git rebase upstream/master .. note:: Before you rebase, you will need to stash all your change via ``git stash``. After rebase, if you want your stash file back, you can type in ``git stash pop`` to get back your stash. ================================================ FILE: docs/debugging.rst ================================================ .. _debugging: Piko/RT Debugging ================= Using tmux ---------- `tmux `_ is a "terminal multiplexer", it allow you accessed and controllerd multiple terminals inside a signle terminal like this: .. image:: https://gifyu.com/images/asciicast.json.gif How to use tmux, please reference these pages: * `tmux wiki `_ * `A tmux Crash Course `_ * `A Quick and Easy Guide to tmux `_ Using OpenOCD ------------- XXX Using gdb scripts ----------------- Here are some debug scripts for gdb. You can copy and paste to your `.gdbinit`, then used in gdb. 1. Dump out bitmap scheduler active and expire queue:: define dump_bitmap set $prio = 0 while $prio < 32 if $arg0 == 1 p (struct thread_info *)((char *)(sched_struct.active->queue[$prio].next) - 0x28) end if $arg0 == 0 p (struct thread_info *)((char *)(sched_struct.expire->queue[$prio].next) - 0x28) end set $prio = $prio + 1 end end how to use:: (gdb) dump_bitmap 1 # Dump active $1 = (struct thread_info *) 0x20000d2c $2 = (struct thread_info *) 0x20000d34 $3 = (struct thread_info *) 0x20000d3c $4 = (struct thread_info *) 0x20000d44 $5 = (struct thread_info *) 0x20000d4c $6 = (struct thread_info *) 0x20000d54 <_active+4> ... (gdb) dump_bitmap 0 # Dump expire 2. Dump out RR scheduler runqueue:: define dump_rr if list_empty(&rr_runq) p "empty rr" else set $pos = rr_runq->next while $pos != &rr_runq p (struct thread_info *)((char *)($pos)-0x28) set $pos = $pos->next end end end how to use:: (gdb) dump_rr ================================================ FILE: docs/emulate.rst ================================================ .. _emulate: Using QEMU to Develop Piko/RT ============================= Prepare QEMU ------------ You will need to prepare QEMU STM32 emulator from: `QEMU STM32 v0.1.3 `_ After download, compile the qemu_stm32 by:: $ ./configure --disable-werror --enable-debug \ --target-list="arm-softmmu" \ --extra-cflags=-DDEBUG_CLKTREE \ --extra-cflags=-DDEBUG_STM32_RCC \ --extra-cflags=-DDEBUG_STM32_UART \ --extra-cflags=-DSTM32_UART_NO_BAUD_DELAY \ --extra-cflags=-DSTM32_UART_ENABLE_OVERRUN --python=python2 $ make -j8 Make sure you have export the ``arm-softmmu`` to ``$PATH````:: $ export PATH=$PATH:~/qemu_stm32-stm32_v0.1.3/arm-softmmu Run Piko/RT on QEMU STM32-p103 ------------------------------ You will need to run these command:: $ make PLAT=stm32p103 $ make PLAT=stm32p103 run If you run correctly, then you will start Piko/RT and get the shell:: Memory map: .text = 00000140--0000315a 12314 Bytes .rodata = 00003190--00003a26 2198 Bytes .data = 20000000--20000514 1300 Bytes .bss = 20000518--20001298 3456 Bytes .heap = 200012a0--200022a0 4096 Bytes .pgmem = 20008000--2000f000 28672 Bytes Order Bitmap 0 00000000 00000000 00000000 00000000 1 00000000 00000000 2 00000000 3 00007fff Created idle_thread at <0x20008200> Created main_thread at <0x20008800> with priority=31 Reclaim early stack's physical memory (2048 Bytes, order=3). Creating /proc/version Creating /proc/meminfo Creating /dev/mem Creating /dev/null Creating /dev/zero Creating /dev/random Creating MTD device mtd0 Kernel bootstrap done. -- $ ================================================ FILE: docs/index.rst ================================================ Welcome to piko's documentation! ================================ .. toctree:: :maxdepth: 2 :caption: Contents: build.rst emulate.rst kernel.rst test.rst contribute.rst debugging.rst .. toctree:: :caption: API: api.rst Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` ================================================ FILE: docs/kernel.rst ================================================ .. _kernel: Piko/RT Design & Concept ======================== Piko/RT is a tiny Linux-like real-time operating system kernel, optimized for Arm Cortex-M series microprocessors. Compatibility with Linux is **NOT** the goal of Piko/RT. Instead, PSE51 (minimal real-time system profile) should be appropriately an analog from the perspective of design. ================================================ FILE: docs/make.bat ================================================ @ECHO OFF pushd %~dp0 REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set SOURCEDIR=. set BUILDDIR=_build set SPHINXPROJ=piko if "%1" == "" goto help %SPHINXBUILD% >NUL 2>NUL if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% goto end :help %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% :end popd ================================================ FILE: docs/posix/parse.py ================================================ # Helper for generate POSIX API table # XXX: used from docs, not here import glob import os import tempfile import shutil import subprocess CSCOPE = shutil.which('cscope') if shutil.which('cscope') else '/usr/bin/cscope' CSCOPE_DB = tempfile.NamedTemporaryFile() BACKTO = '../' LIBC_PATH = BACKTO + 'libc' def read_file(path): # Return list of API return open(path).read().replace(' ', '').replace('\n', '').strip().split(',') def build_cscope(): CSCOPE_DB.write(subprocess.check_output("find %s -name '*.c'" % LIBC_PATH, shell=True)) CSCOPE_DB.write(subprocess.check_output("find %s -name '*.h'" % LIBC_PATH, shell=True)) CSCOPE_DB.write(subprocess.check_output("find %s -name '*.S'" % LIBC_PATH, shell=True)) CSCOPE_DB.flush() return CSCOPE_DB.name def cscope_search(name): return subprocess.check_output("%s -i %s -L1%s" % (CSCOPE, CSCOPE_DB.name, name), shell=True) def parse_path(s): return s.split('\n')[0].strip(BACKTO).split()[0:3:2] if s and s.startswith(LIBC_PATH) else ('', 0) def generate_table(name): header = ['.. table:: %s' % os.path.basename(name), ' :widths: 45 10 35', ''] longest_api_length = 10 apis = read_file(name) result = [] for api in apis: if not api: continue longest_api_length = max(len(api), longest_api_length) s = cscope_search(api.strip('()')).decode('utf-8') result.append([api, s.startswith(LIBC_PATH), *parse_path(s)]) splitline = ' {:=>{}} ===== {}'.format('', longest_api_length, '=' * 30) header.append(splitline) header.append(' {:<{}} IMPL Path'.format('API Name', longest_api_length)) header.append(splitline) for i, r in enumerate(result): result[i] = (' {:<{}} {}'.format(r[0], longest_api_length, 'O' if r[1] else 'X') + ' {}'.format('{}:{}'.format(r[2], r[3]) if r[1] else '')) result.append(splitline) result = header + result return '\n'.join(result) if __name__ == '__main__': build_cscope() reqs = glob.glob('posix/posix_req/*') with open('posix/posix_req.rst', 'w') as f: for req in reqs: f.write(generate_table(req)) f.write('\n\n') opts = glob.glob('posix/posix_opt/*') with open('posix/posix_opt.rst', 'w') as f: for opt in opts: f.write(generate_table(opt)) f.write('\n\n') ================================================ FILE: docs/posix/posix_opt/01._POSIX_CLOCK_SELECTION ================================================ clock_nanosleep() ================================================ FILE: docs/posix/posix_opt/02._POSIX_FSYNC ================================================ fsync() ================================================ FILE: docs/posix/posix_opt/03._POSIX_MEMLOCK ================================================ m lockall(), m unlockall() ================================================ FILE: docs/posix/posix_opt/04.POSIX_MEMLOCK_RANGE ================================================ mlock(), munlock() ================================================ FILE: docs/posix/posix_opt/05._POSIX_MONOTONIC_CLOCK ================================================ ================================================ FILE: docs/posix/posix_opt/06._POSIX_NO_TRUNC ================================================ ================================================ FILE: docs/posix/posix_opt/07._POSIX_REALTIME_SIGNALS ================================================ sigqueue(), sigtim edwait(), sigwaitinfo() ================================================ FILE: docs/posix/posix_opt/08._POSIX_SEMAPHORES ================================================ sem _close(), sem _destroy(), sem _getvalue(), sem _init(), sem _open(), sem _post(), sem _trywait(), sem _wait(), sem _unlink (), sem _tim edwait() ================================================ FILE: docs/posix/posix_opt/09._POSIX_SHARED_MEMORY_OBJECTS ================================================ shm _open(), shm _unlink() ================================================ FILE: docs/posix/posix_opt/10._POSIX_SYNCHRONIZED_IO ================================================ fdatasync() ================================================ FILE: docs/posix/posix_opt/11._POSIX_THREAD_ATTR_STACKADDR ================================================ pthread_attr_getstackaddr(), pthread_attr_setstackaddr() ================================================ FILE: docs/posix/posix_opt/12._POSIX_THREAD_ATTR_STACKSIZE ================================================ pthread_attr_getstack (), pthread_attr_setstack() ================================================ FILE: docs/posix/posix_opt/13._POSIX_THREAD_CPUTIME ================================================ pthread_getcpuclockid() ================================================ FILE: docs/posix/posix_opt/14._POSIX_THREAD_PRIO_INHERIT ================================================ pthread_m utexattr_getprotocol(), pthread_m utexattr_setprotocol() ================================================ FILE: docs/posix/posix_opt/15._POSIX_THREAD_PRIO_PROTECT ================================================ pthread_m utex_getprioceiling(), pthread_m utex_setprioceiling(), pthread_m utexattr_getprioceiling(), pthread_m utexattr_getprotocol(), pthread_m utexattr_setprioceiling(), pthread_m utexattr_setprotocol() ================================================ FILE: docs/posix/posix_opt/16._POSIX_THREAD_PRIORITY_SCHEDULING ================================================ pthread_attr_getinheritsched(), pthread_attr_getschedpolicy(), pthread_attr_getscope(), pthread_attr_setinheritsched(), pthread_attr_setschedpolicy(), pthread_attr_setscope(), pthread_getschedparam (), pthread_setschedparam (), pthread_setschedprio(), sched_get_priority_m ax(), sched_get_priority_m in(), sched_rr_get_interval() ================================================ FILE: docs/posix/posix_opt/17._POSIX_THREAD_SPORADIC_SERVER ================================================ ================================================ FILE: docs/posix/posix_opt/18._POSIX_TIMERS ================================================ clock_getres(), clock_gettim e(), clock_settim e(), nanosleep(), tim er_create(), tim er_detele(), tim er_getoverrun(), tim er_gettim e(), tim er_settim e() ================================================ FILE: docs/posix/posix_req/01.POSIX_C_LANG_SUPPORT ================================================ abs(), asctime(), asctime_r(), atof(), atoi(), atol(), atoll(), bsearch(), calloc(), ctime(), ctime_r(), difftime(), div(), feclearexcept(), fegetenv(), fegetexceptflag(), fegetround(), feholdexcept(), feraiseexcept(), fesetenv(), fesetexceptflag(), fesetround(), fetestexcept(), feupdateenv(), free(), gmtime(), gmtime_r(), imaxabs(), imaxdiv(), isalnum(), isalpha(), isblank(), iscntrl(), isdigit(), isgraph(), islower(), isprint(), ispunct(), isspace(), isupper(), isxdigit(), labs(), ldiv(), llabs(), lldiv(), localeconv(), localtime(), localtime_r(), malloc(), memchr(), memcmp(), memcpy(), memmove(), memset(), mktime(), qsort(), rand(), rand_r(), realloc(), setlocale(), snprintf(), sprintf(), srand(), sscanf(), strcat(), strchr(), strcmp(), strcoll(), strcpy(), strcspn(), strerror(), strerror_r(), strftime(), strlen(), strncat(), strncmp(), strncpy(), strpbrk(), strrchr(), strspn(), strstr(), strtod(), strtof(), strtoimax(), strtok(), strtok_r(), strtol(), strtold(), strtoll(), strtoul(), strtoull(), strtoumax(), strxfrm(), time(), tolower(), toupper(), tzname, tzset(), va_arg(), va_copy(), va_end(), va_start(), vsnprintf(), vsprintf(), vsscanf() ================================================ FILE: docs/posix/posix_req/02.POSIX_DEVICE_IO ================================================ clearerr(), close(), fclose(), fdopen(), feof(), ferror(), fflush (), fgetc(), fgets(), fileno(), fopen(), fprintf(), fputc(), fputs(), fread(), freopen(), fscanf(), fwrite(), getc(), getchar(), gets(), open(), perror(), printf(), putc(), putchar(), puts(), read(), scanf(), setbuf(), setvbuf(), stderr, stdin, stdout, ungetc(), vfprintf(), vfscanf(), vprintf(), vscanf(), write() ================================================ FILE: docs/posix/posix_req/03.POSIX_FILE_LOCKING ================================================ flockfile(), ftrylockfile(), funlockfile(), getc_unlocked(), getchar_unlocked(), putc_unlocked(), putchar_unlocked() ================================================ FILE: docs/posix/posix_req/04.POSIX_SIGNALS ================================================ abort(), alarm(), kill(), pause(), raise(), sigaction(), sigaddset(), sigdelset(), sigemptyset(), sigfillset(), sigismember(), signal(), sigpending(), sigprocmask(), sigsuspend(), sigwait() ================================================ FILE: docs/posix/posix_req/05.POSIX_SINGLE_PROCESS ================================================ confstr(), environ, errno, getenv(), setenv(), sysconf(), uname(), unsetenv() ================================================ FILE: docs/posix/posix_req/06.POSIX_THREADS_BASE ================================================ pthread_atfork(), pthread_attr_destroy(), pthread_attr_getdetachstate(), pthread_attr_getschedparam(), pthread_attr_init(), pthread_attr_setdetachstate(), pthread_attr_setschedparam(), pthread_cancel(), pthread_cleanup_pop(), pthread_cleanup_push(), pthread_cond_broadcast(), pthread_cond_destroy(), pthread_cond_init(), pthread_cond_signal(), pthread_cond_timedwait(), pthread_cond_wait(), pthread_condattr_destroy(), pthread_condattr_init(), pthread_create(), pthread_detach(), pthread_equal(), pthread_exit(), pthread_getspecific(), pthread_join(), pthread_key_create(), pthread_key_delete(), pthread_kill(), pthread_mutex_destroy(), pthread_mutex_init(), pthread_mutex_lock(), pthread_mutex_trylock(), pthread_mutex_unlock(), pthread_mutexattr_destroy(), pthread_mutexattr_init(), pthread_once(), pthread_self(), pthread_setcalcelstate(), pthread_setcanceltype(), pthread_setspecific(), pthread_sigmask(), pthread_testcancel() ================================================ FILE: docs/posix/posix_req/07.XSI_THREAD_MUTEX_EXT ================================================ pthread_mutexattr_gettype(), pthread_mutexattr_settype() ================================================ FILE: docs/posix/posix_req/08.XSI_THREADS_EXT ================================================ pthread_attr_getguardsize(), pthread_attr_getstack(), pthread_attr_setguardsize(), pthread_attr_setstack(), pthread_getconcurrency(), pthread_setconcurrency() ================================================ FILE: docs/test.rst ================================================ .. _test: Piko/RT Test Suite ================== Piko/RT test suite is under ``tests`` directory, it contain a makefile, many test folder and it self is a python package. Each test folder represent a test case. Run Tests --------- Test suite can be run by top directory Makefile:: $ make PLAT=stm32p103 check This will run all test in the test suite, also, you can use ``tests`` as a python package, to run all test via python command line:: $ python -m tests You can also run partial via python:: $ python -m tests fs_1 stat_1 thread_5 Python command line tool verbose default is disable, you can pass the flag ``-v`` to enable:: $ python -m tests -v $ python -m tests fs_1 -v ================================================ FILE: drivers/char/mem.c ================================================ #include #include #include static int mem_open(__unused struct inode *inode, __unused struct file *file) { return 0; } static ssize_t write_mem(__unused struct file *file, const char *buf, size_t count, off_t *offset) { memcpy((void *) offset, buf, count); return count; } static ssize_t read_mem(__unused struct file *file, char *buf, size_t count, off_t offset) { memcpy(buf, (void *) offset, count); return count; } static ssize_t write_null(__unused struct file *file, __unused const char *buf, size_t count, __unused off_t *offset) { return count; } static ssize_t read_null(__unused struct file *file, __unused char *buf, __unused size_t count, __unused off_t offset) { return 0; } static ssize_t read_zero(__unused struct file *file, char *buf, size_t count, __unused off_t offset) { memset(buf, 0, count); return count; } static const struct file_operations mem_fops = { .open = mem_open, .read = read_mem, .write = write_mem, }; static const struct file_operations null_fops = { .open = mem_open, .read = read_null, .write = write_null, }; static const struct file_operations zero_fops = { .open = mem_open, .read = read_zero, .write = write_null, }; extern const struct file_operations random_fops; extern const struct inode_operations tmpfs_iops; static struct inode memdev_inodes[] = { { /* /dev/mem */ .i_ino = 101, .i_op = &tmpfs_iops, .i_fop = &mem_fops, }, { /* /dev/null */ .i_ino = 102, .i_op = &tmpfs_iops, .i_fop = &null_fops, }, { /* /dev/zero */ .i_ino = 103, .i_op = &tmpfs_iops, .i_fop = &zero_fops, }, { /* /dev/random */ .i_ino = 104, .i_op = &tmpfs_iops, .i_fop = &random_fops, }, }; void memdev_init(void) { struct dentry dentry; const char *names[] = {"mem", "null", "zero", "random"}; for (int i = 0; i < 4; i++) { printk("Creating /dev/%s\n", names[i]); dentry.d_inode = &memdev_inodes[i], strcpy(dentry.d_name, names[i]); vfs_link(0, dev_inode(), &dentry); } } ================================================ FILE: drivers/char/random.c ================================================ #include #include #include //#include #include typedef unsigned long long u64; /* xorshift1024* generator, http://vigna.di.unimi.it/ftp/papers/xorshift.pdf */ static u64 next(void) { // clang-format off static u64 s[16] = { 0x84242f96eca9c41dull, 0xa3c65b8776f96855ull, 0x5b34a39f070b5837ull, 0x4489affce4f31a1eull, 0x2ffeeb0a48316f40ull, 0xdc2d9891fe68c022ull, 0x3659132bb12fea70ull, 0xaac17d8efa43cab8ull, 0xc4cb815590989b13ull, 0x5ee975283d71c93bull, 0x691548c86c1bd540ull, 0x7910c41d10a1e6a5ull, 0x0b5fc64563b3e2a8ull, 0x047f7684e9fc949dull, 0xb99181f2d8f685caull, 0x284600e3f30e38c3ull }; // clang-format on static int p; const u64 s0 = s[p]; u64 s1 = s[p = (p + 1) & 15]; s1 ^= s1 << 31; // a s[p] = s1 ^ s0 ^ (s1 >> 11) ^ (s0 >> 30); // b,c return s[p] * UINT64_C(1181783497276652981); } static int open_random(__unused struct inode *inode, __unused struct file *file) { return 0; } static ssize_t read_random(__unused struct file *file, char *buf, size_t count, __unused off_t offset) { static u64 m; static int remaining_bytes = 0; if ((int) count <= remaining_bytes) { memcpy(buf, (char *) &m + 8 - remaining_bytes, count); remaining_bytes -= count; } else { for (int i = count; i > 0; i -= 8, buf = (char *) buf + 8) { m = next(); memcpy(buf, &m, MIN(i, 8)); remaining_bytes = 8 - MIN(i, 8); } } return count; } const struct file_operations random_fops = { .open = open_random, .read = read_random, }; ================================================ FILE: drivers/mtd/mtd.h ================================================ #ifndef _DRIVERS_MTD_MTD_H #define _DRIVERS_MTD_MTD_H #include #include #include #define MTD_ERASE_PENDING (1 << 0) #define MTD_ERASING (1 << 1) #define MTD_ERASE_SUSPEND (1 << 2) #define MTD_ERASE_DONE (1 << 3) #define MTD_ERASE_FAILED (1 << 4) #define MTD_FAIL_ADDR_UNKNOWN -1l #define MTD_WRITEABLE 0x400 /* Device is writeable */ struct erase_info { /* struct mtd_info *mtd; */ u32 addr; u32 len; u32 fail_addr; /* u_long time; */ /* u_long retries; */ /* unsigned dev; */ /* unsigned cell; */ void (*callback)(struct erase_info *self); unsigned long priv; unsigned char state; struct erase_info *next; }; struct mtd_info { unsigned char type; u32 flags; unsigned long size; unsigned long erasesize; unsigned long writesize; const char *name; // can this be a hash? int (*mtd_erase)(struct mtd_info *mtd, struct erase_info *instr); int (*mtd_point)(struct mtd_info *mtd, off_t from, size_t len, size_t *retlen, void **virt /* , resource_size_t *phys */); // virt -> at int (*mtd_unpoint)(struct mtd_info *mtd, off_t from, size_t len); int (*mtd_read)(struct mtd_info *mtd, off_t from, size_t len, size_t *retlen, void *buf); int (*mtd_write)(struct mtd_info *mtd, off_t to, size_t len, size_t *retlen, const void *buf); /* struct device *dev; */ void *priv; // can be the physical address of the flash device }; int mtd_erase(struct mtd_info *mtd, struct erase_info *instr); int mtd_point(struct mtd_info *mtd, off_t from, size_t len, size_t *retlen, void **virt /* , resource_size_t *phys */); int mtd_read(struct mtd_info *mtd, off_t from, size_t len, size_t *retlen, unsigned char *buf); int mtd_write(struct mtd_info *mtd, off_t to, size_t len, size_t *retlen, const unsigned char *buf); void mtd_erase_callback(struct erase_info *instr); int mtdram_init_device(struct mtd_info *mtd, void *mapped_address, unsigned long size, const char *name); void mtdram_init(void); #endif /* !_DRIVERS_MTD_MTD_H */ ================================================ FILE: drivers/mtd/mtdchar.c ================================================ #include #include #include /* romfs depends on mtdchar */ static int mtdchar_open(struct inode *inode, struct file *file) { file->f_private = inode->i_private; return 0; } static ssize_t mtdchar_read(struct file *file, char *buf, size_t count, off_t offset) { size_t retlen; struct mtd_info *mtd = file->f_private; if (mtd_read(mtd, offset, count, &retlen, (unsigned char *) buf) < 0) return -1; return retlen; } static ssize_t mtdchar_write(struct file *file, const char *buf, size_t count, off_t *offset) { size_t retlen; struct mtd_info *mtd = file->f_private; if (mtd_write(mtd, *offset, count, &retlen, (const unsigned char *) buf) < 0) return -1; return retlen; } const struct file_operations mtdchar_fops = { .open = mtdchar_open, .read = mtdchar_read, .write = mtdchar_write, }; ================================================ FILE: drivers/mtd/mtdcore.c ================================================ #include #include void mtd_erase_callback(struct erase_info *instr) { if (instr->callback) instr->callback(instr); } int mtd_erase(struct mtd_info *mtd, struct erase_info *instr) { if ((instr->addr >= mtd->size) || (instr->len > mtd->size - instr->addr)) return -EINVAL; if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; if (!instr->len) { instr->state = MTD_ERASE_DONE; mtd_erase_callback(instr); return 0; } return mtd->mtd_erase(mtd, instr); } int mtd_point(struct mtd_info *mtd, off_t from, size_t len, size_t *retlen, void **virt /* , resource_size_t *phys */) { *retlen = 0; *virt = NULL; if (!mtd->mtd_point) return -1; // return -EOPNOTSUPP; if ((from < 0) || ((unsigned long) from >= mtd->size) || (len > mtd->size - from)) return -EINVAL; if (!len) return 0; return mtd->mtd_point(mtd, from, len, retlen, virt /* , phys */); } int mtd_read(struct mtd_info *mtd, off_t from, size_t len, size_t *retlen, unsigned char *buf) { *retlen = 0; if ((from < 0) || ((unsigned long) from >= mtd->size) || (len > mtd->size - from)) return -EINVAL; if (!len) return 0; return mtd->mtd_read(mtd, from, len, retlen, buf); } int mtd_write(struct mtd_info *mtd, off_t to, size_t len, size_t *retlen, const unsigned char *buf) { *retlen = 0; if ((to < 0) || ((unsigned long) to >= mtd->size) || (len > mtd->size - to)) return -EINVAL; if (!len) return 0; return mtd->mtd_write(mtd, to, len, retlen, buf); } ================================================ FILE: drivers/mtd/mtdram.c ================================================ #include #include #include #include #define SIZE_1KB 1024 static int mtdram_erase(struct mtd_info *mtd, struct erase_info *instr) { memset((char *) mtd->priv + instr->addr, 0xff, instr->len); instr->state = MTD_ERASE_DONE; mtd_erase_callback(instr); return 0; } static int mtdram_point(struct mtd_info *mtd, off_t from, size_t len, size_t *retlen, void **virt /* , resource_size_t *phys */) // void **at { *virt = mtd->priv + from; *retlen = len; return 0; } static int mtdram_unpoint(struct mtd_info *mtd, off_t from, size_t len) { (void) mtd, (void) from, (void) len; return 0; } int mtdram_write(struct mtd_info *mtd, off_t to, size_t len, size_t *retlen, const void *buf) { memcpy((char *) mtd->priv + to, buf, len); *retlen = len; return 0; } int mtdram_read(struct mtd_info *mtd, off_t from, size_t len, size_t *retlen, void *buf) { memcpy(buf, mtd->priv + from, len); *retlen = len; return 0; } int mtdram_init_device(struct mtd_info *mtd, void *mapped_address, unsigned long size, const char *name) { if (size % SIZE_1KB) return -1; mtd->name = name; mtd->size = size; mtd->erasesize = SIZE_1KB, mtd->priv = mapped_address; mtd->mtd_erase = mtdram_erase; mtd->mtd_point = mtdram_point; mtd->mtd_unpoint = mtdram_unpoint; mtd->mtd_read = mtdram_read; mtd->mtd_write = mtdram_write; return 0; } struct mtd_info mtdram; extern char __mtdram_start__; extern char __mtdram_size__; extern const struct file_operations mtdchar_fops; static struct inode mtd0_inode = { .i_fop = &mtdchar_fops, .i_private = &mtdram, }; void mtdram_init(void) { struct dentry dentry = {.d_inode = &mtd0_inode, .d_name = "mtd0"}; printk("Creating MTD device %s\n", dentry.d_name); mtdram_init_device(&mtdram, &__mtdram_start__, (unsigned long) &__mtdram_size__, dentry.d_name); init_tmpfs_inode(&mtd0_inode); vfs_link(NULL, dev_inode(), &dentry); } ================================================ FILE: drivers/serial/serialchar.c ================================================ #include #include #include #include #include static void serialchar_callback(struct serial_info *serial) { sched_enqueue(serial->owner); } static int serialchar_open(struct inode *inode, struct file *file) { file->f_private = inode->i_private; struct serial_info *serial = file->f_private; CURRENT_THREAD_INFO(cur_thread); serial->owner = cur_thread; serial->ops->callback = serialchar_callback; return 0; } static ssize_t serialchar_read(struct file *file, char *buf, size_t count, __unused off_t offset) { size_t retlen; struct serial_info *serial = file->f_private; while (serial->rx_count < count) { CURRENT_THREAD_INFO(cur_thread); sched_dequeue(cur_thread); sched_elect(0); } if (count == 1) return serial_getc(serial, buf); if (serial_gets(serial, count, &retlen, buf) < 0) return -1; return retlen; } static ssize_t serialchar_write(struct file *file, const char *buf, size_t count, __unused off_t *offset) { struct serial_info *serial = file->f_private; return serial_puts(serial, count, buf); } const struct file_operations serialchar_fops = { .open = serialchar_open, .read = serialchar_read, .write = serialchar_write, }; ================================================ FILE: drivers/serial/serialcore.c ================================================ #include #include void serial_init(void) { extern unsigned long __serial_hook_start__; extern unsigned long __serial_hook_end__; for (struct serial_hook *hook = (struct serial_hook *) &__serial_hook_start__; hook < (struct serial_hook *) &__serial_hook_end__; hook++) hook->init(); } void serial_activity_callback(struct serial_info *serial) { if (serial->ops->callback) serial->ops->callback(serial); } int serial_getc(struct serial_info *serial, char *c) { return serial->ops->serial_getc(serial, c); } int serial_gets(struct serial_info *serial, size_t len, size_t *retlen, char *buf) { return serial->ops->serial_gets(serial, len, retlen, buf); } int serial_putc(struct serial_info *serial, char c) { return serial->ops->serial_putc(serial, c); } int serial_puts(struct serial_info *serial, size_t len, const char *buf) { return serial->ops->serial_puts(serial, len, buf); } ================================================ FILE: drivers/serial/stm32-uart.c ================================================ #include #include #include #include #include #include #include #include #include "platform.h" static struct cbuf_info cbuf; static char buf[16]; static int stm32_getc(struct serial_info *serial, char *c) { serial->rx_count--; cbuf_getc(&cbuf, c); return 0; } static int stm32_putc(struct serial_info *serial, char c) { if (!serial || c < 0) return EOF; USART_TypeDef *uart = serial->priv; while (!(uart->SR & USART_SR_TXE)) ; uart->DR = c; return c; } static int stm32_puts(struct serial_info *serial, size_t len, const char *buf) { int i = 0; for (i = 0; i < len; i++) if (stm32_putc(serial, buf[i]) == EOF) return EOF; return i; } static struct serial_ops stm32_ops = { .serial_getc = stm32_getc, .serial_putc = stm32_putc, .serial_puts = stm32_puts, }; struct serial_info stm32_info = { .priv = USARTx, .ops = &stm32_ops, }; static void stm32_uartx_isr(void) { if (USARTx->SR & USART_SR_RXNE) { char c = (char) USARTx->DR; cbuf_putc(&cbuf, c); stm32_info.rx_count++; serial_activity_callback(&stm32_info); } } extern const struct file_operations serialchar_fops; static struct inode stm32_inode = { .i_fop = &serialchar_fops, .i_private = &stm32_info, }; static int stm32_serial_init(void) { uart_init(); struct dentry dentry = {.d_inode = &stm32_inode, .d_name = "ttyS0"}; cbuf_init(&cbuf, buf, 16); init_tmpfs_inode(&stm32_inode); vfs_link(0, dev_inode(), &dentry); /* enable rx interrupt */ request_irq(USARTx_IRQn, stm32_uartx_isr); NVIC_SetPriority(USARTx_IRQn, 0xE); NVIC_EnableIRQ(USARTx_IRQn); return 0; } HOOK_SERIAL_INIT(UART, stm32_serial_init) ================================================ FILE: drivers/serial/stm32-uart.h ================================================ #ifndef _DRIVER_SERIAL_STM32_USART_H #define _DRIVER_SERIAL_STM32_USART_H #include "platform.h" struct stm32_uart_port { GPIO_TypeDef *gpio_tx; GPIO_TypeDef *gpio_rx; GPIO_InitTypeDef gpio_tx_init_info; GPIO_InitTypeDef gpio_rx_init_info; UART_HandleTypeDef uart_init_info; void (*gpio_init)(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init); HAL_StatusTypeDef (*uart_init)(UART_HandleTypeDef *huart); void (*gpio_tx_clk_enable)(void); void (*gpio_rx_clk_enable)(void); void (*uart_clk_enable)(void); }; #endif /* !_DRIVER_SERIAL_STM32_USART_H */ ================================================ FILE: drivers/timer/systick.c ================================================ /* * Low-resolution (1kHz) SysTick-based timers to support the common * driver's timer interface. */ #include #include #include #include #include #include #include #include "linux/list.h" struct systick_timer { unsigned long start_clocktime; unsigned long expire_clocktime; struct list_head list; struct timer_info *timer; /* backlink */ }; static unsigned long clocktime_in_msec; static LIST_HEAD(systick_timers); static int systick_timer_alloc(struct timer_info *timer) { struct systick_timer *systick_timer; systick_timer = malloc(sizeof(struct systick_timer)); if (!systick_timer) { errno = ENOMEM; return -1; } timer->dev = systick_timer; systick_timer->timer = timer; return 0; } static int systick_timer_set(struct timer_info *timer, const struct timespec *value) { struct systick_timer *systick_timer = (struct systick_timer *) timer->dev; if (!value->tv_sec && !value->tv_nsec) list_del(&systick_timer->list); if (value->tv_sec || value->tv_nsec) { systick_timer->start_clocktime = clocktime_in_msec; systick_timer->expire_clocktime = clocktime_in_msec + value->tv_sec * 1000 + value->tv_nsec / 1000000; list_add(&systick_timer->list, &systick_timers); } return 0; } static int systick_timer_get(struct timer_info *timer, struct itimerspec *curr_value) { struct systick_timer *systick_timer = (struct systick_timer *) timer->dev; unsigned long msecs = clocktime_in_msec - systick_timer->start_clocktime; curr_value->it_value.tv_sec = msecs / 1000; curr_value->it_value.tv_nsec = (msecs % 1000) * 1000000; return 0; } static int systick_timer_free(struct timer_info *timer) { free(timer->dev); return 0; } static void systick_bh(void) { // XXX: is list_for_each_entry_safe() reentrant? struct systick_timer *pos, *pos1; list_for_each_entry_safe(pos, pos1, &systick_timers, list) { if (pos->expire_clocktime < clocktime_in_msec) { list_del(&pos->list); struct timer_info *timer = pos->timer; if (timer->type == INTERVAL_TIMER) systick_timer_set(timer, &timer->value.it_interval); timer_expire_callback(timer); } } } #define SYSTICK_FREQ_IN_HZ 1000 #define SYSTICK_PERIOD_IN_MSECS (SYSTICK_FREQ_IN_HZ / 1000) #include "platform.h" void systick(void) { clocktime_in_msec += SYSTICK_PERIOD_IN_MSECS; static struct tasklet_struct *tick_tsklet; if (!tick_tsklet) tick_tsklet = tasklet_init(systick_bh, NULL, TIMER_SOFTIRQ_PRIO); tasklet_schedule(tick_tsklet); SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk; } const struct timer_operations systick_tops = { .timer_alloc = systick_timer_alloc, .timer_set = systick_timer_set, .timer_get = systick_timer_get, .timer_free = systick_timer_free, }; void __attribute__((naked)) pendsv(void) { __asm__ __volatile__( "push {lr} \n\t" "mov r0, #3 \n\t" /*SCHED_OPT_TICK*/ "bl sched_elect \n\t" "pop {pc}" ::); } ================================================ FILE: drivers/timer/timercore.c ================================================ #include #include #include #include #include static struct timer_operations *timer_operations; void config_timer_operations(struct timer_operations *tops) { timer_operations = tops; } struct timer_info *timer_alloc(void) { struct timer_info *timer; timer = malloc(sizeof(struct timer_info)); if (!timer) { errno = ENOMEM; return NULL; } timer->tops = timer_operations; // FIXME: get info from vnode or device if (timer->tops->timer_alloc(timer)) { free(timer); return NULL; } return timer; } int timer_set(struct timer_info *timer, const struct timespec *value) { return timer->tops->timer_set(timer, value); } int timer_get(struct timer_info *timer, struct itimerspec *value) { return timer->tops->timer_get(timer, value); } int timer_free(struct timer_info *timer) { if (!timer->disarmed) { const struct timespec zero_val = {0, 0}; timer_set(timer, &zero_val); } timer->tops->timer_free(timer); free(timer); return 0; } void timer_expire_callback(struct timer_info *timer) { if (timer->callback) timer->callback(timer); } ================================================ FILE: fs/proc.c ================================================ #include #include #include #include // https://linux.die.net/lkmpg/x861.html // https://lwn.net/Articles/22355/ extern const char _version_ptr; extern const int _version_len; static int open_version(__unused struct inode *inode, __unused struct file *file) { return 0; } static ssize_t read_version(__unused struct file *file, char *buf, size_t count, off_t offset) { size_t n = MIN(count, (int) &_version_len - offset); strncpy(buf, &_version_ptr + offset, n); return n; } static int open_meminfo(__unused struct inode *inode, __unused struct file *file) { return 0; } static ssize_t read_meminfo(__unused struct file *file, char *buf, size_t count, off_t offset) { memcpy(buf, (void *) offset, count); return count; } static const struct file_operations version_fops = { .open = open_version, .read = read_version, }; static const struct file_operations meminfo_fops = { .open = open_meminfo, .read = read_meminfo, }; extern const struct inode_operations tmpfs_iops; static struct inode proc_inodes[] = { { /* /proc/version */ .i_ino = 1001, .i_op = &tmpfs_iops, .i_fop = &version_fops, }, { /* /proc/meminfo */ .i_ino = 1002, .i_op = &tmpfs_iops, .i_fop = &meminfo_fops, }, }; void proc_init(void) { struct dentry dentry; const char *names[] = { "version", "meminfo", }; for (int i = 0; i < 2; i++) { printk("Creating /proc/%s\n", names[i]); dentry.d_inode = &proc_inodes[i], strcpy(dentry.d_name, names[i]); vfs_link(0, proc_inode(), &dentry); } } ================================================ FILE: fs/romfs.c ================================================ /* https://www.kernel.org/doc/Documentation/filesystems/romfs.txt */ #include #include #include #include #include #include /* words are big endian in romfs */ #include #include #include #include #include "linux/list.h" const struct inode_operations romfs_iops; const struct file_operations romfs_fops; const struct dentry_operations romfs_dops; static char *basename(const char *filename) { char *p = strrchr(filename, '/'); return p ? p + 1 : (char *) filename; } static off_t offsetof_device_inode(struct romfs_inode *rinode, struct romfs_superblock *super) { return (off_t) rinode - (off_t) super; } static off_t offsetof_first_device_inode(struct romfs_superblock *super) { /* volume_name is a null-terminated string */ int len = align_next(strlen(super->volume_name) + 1, 16); return offsetof(struct romfs_superblock, volume_name) + len; } // mount("/dev/mtd", "/media/flash", "romfs", 0, NULL); int romfs_mount(const char *source, const char *target, __unused const char *filesystemtype, __unused unsigned long mountflags, __unused const void *data) { struct inode *s_inode = inode_from_pathname(source); if (!s_inode) return -1; // FIXME: Use mkdir() or create() struct inode *inode = malloc(sizeof(struct inode)); if (!inode) return -1; init_tmpfs_inode(inode); inode->i_op = &romfs_iops; inode->i_mode = S_IFDIR; inode->i_size = 0; // link mounted-over inode to parent directory struct dentry dentry; printk("Creating /dev/%s\n", basename(target)); dentry.d_inode = inode; strcpy(dentry.d_name, basename(target)); vfs_link(0, dev_inode(), &dentry); /* Allocate a super_block struct that will be released on filesystem * unmount. */ struct super_block *super_block = malloc(sizeof(struct super_block)); if (!super_block) return -1; /* super_block is found at the begining of memory area on MTD dev */ struct mtd_info *mtd = s_inode->i_private; super_block->s_private = mtd; super_block->s_iroot = inode; // FIXME: super_block must point to dentry instead of inode inode->i_sb = super_block; struct romfs_superblock *super = mtd->priv; inode->i_private = (void *) offsetof_first_device_inode(super); return 0; } static struct inode *alloc_inode(struct romfs_inode *ri, struct super_block *sb) { struct inode *inode; static int ino = 0xbeef; inode = malloc(sizeof(struct inode)); if (!inode) return NULL; switch (ntohl(ri->next_filehdr) & ROMFS_FILETYPE_MASK) { case ROMFS_FILETYPE_DIR: inode->i_mode = S_IFDIR; break; case ROMFS_FILETYPE_REG: inode->i_mode = S_IFREG; break; case ROMFS_FILETYPE_LNK: inode->i_mode = S_IFLNK; break; case ROMFS_FILETYPE_BLK: inode->i_mode = S_IFBLK; break; case ROMFS_FILETYPE_CHR: inode->i_mode = S_IFCHR; break; case ROMFS_FILETYPE_FIFO: inode->i_mode = S_IFIFO; break; default: inode->i_mode = 0; } inode->i_ino = ino++; inode->i_size = ntohl(ri->size); inode->i_op = &romfs_iops; inode->i_fop = &romfs_fops; inode->i_sb = sb; /* We store the offset to on-device inode rather than the logical * address of the on-device inode, because that does not work if * fs is stored on a SPI flash for instance, and is not directly * mapped onto logical address space. */ inode->i_private = (void *) offsetof_device_inode(ri, ROMFS_SUPER_BLOCK(sb)); return inode; } struct dentry *romfs_lookup(struct inode *dir, struct dentry *target) { __u32 next_filehdr = 0; struct romfs_superblock *rs; struct romfs_inode *ri; /* get current on-device inode */ rs = ROMFS_SUPER_BLOCK(dir->i_sb); ri = ROMFS_INODE(rs, dir->i_private); /* enter and walk the directory */ next_filehdr = align(ntohl(ri->spec_info), 16); ri = ROMFS_INODE(rs, next_filehdr); for (int i = 0; next_filehdr < rs->full_size; i++) { if (!strcmp(ri->file_name, target->d_name)) { struct inode *inode = alloc_inode(ri, dir->i_sb); if (!inode) return NULL; target->d_inode = inode; target->d_op = &romfs_dops; return target; } /* inspect next file in current directory */ next_filehdr = align(ntohl(ri->next_filehdr), 16); if (!next_filehdr) break; ri = ROMFS_INODE(rs, next_filehdr); } return NULL; } int romfs_open(struct inode *inode, struct file *file) { file->f_private = inode->i_private; return 0; } ssize_t romfs_read(struct file *file, char *buf, size_t count, off_t offset) { size_t retlen; size_t filesize = file->f_dentry->d_inode->i_size; struct inode *inode = file->f_dentry->d_inode; struct super_block *sb = inode->i_sb; struct mtd_info *mtd = sb->s_private; struct romfs_superblock *rs = ROMFS_SUPER_BLOCK(sb); struct romfs_inode *ri = ROMFS_INODE(rs, inode->i_private); int len = sizeof(struct romfs_inode) + align_next(strlen(ri->file_name) + 1, 16); if (file->f_pos + count > filesize) count = filesize - offset; mtd_read(mtd, (off_t) inode->i_private + len + offset, count, &retlen, (unsigned char *) buf); return retlen; } int romfs_mmap(struct file *file, off_t offset, void **addr) { size_t retlen; size_t filesize = file->f_dentry->d_inode->i_size; struct inode *inode = file->f_dentry->d_inode; struct super_block *sb = inode->i_sb; struct mtd_info *mtd = sb->s_private; struct romfs_superblock *rs = ROMFS_SUPER_BLOCK(sb); struct romfs_inode *ri = ROMFS_INODE(rs, inode->i_private); int len = sizeof(struct romfs_inode) + align_next(strlen(ri->file_name) + 1, 16); return mtd_point(mtd, (off_t) inode->i_private + len + offset, filesize, &retlen, addr); } int romfs_delete(struct dentry *dentry) { /* release in-memory inode */ /* the root inode is deleted on unmount(), operation is pointed * by i_sb->s_op->unmount() */ if (dentry->d_inode != dentry->d_inode->i_sb->s_iroot) free(dentry->d_inode); free(dentry); return 0; } const struct inode_operations romfs_iops = { .lookup = romfs_lookup, }; const struct file_operations romfs_fops = { .open = romfs_open, .read = romfs_read, .mmap = romfs_mmap, }; const struct dentry_operations romfs_dops = { .delete = romfs_delete, }; ================================================ FILE: fs/tmpfs.c ================================================ #include #include #include #include #include #include "linux/list.h" struct dirlist { struct inode *inode; char name[NAME_MAX]; struct list_head list; }; int tmpfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) { dir->i_mode = mode | S_IFDIR; dentry->d_inode = dir; struct list_head *dirlist = (struct list_head *) dir->i_private; INIT_LIST_HEAD(dirlist); return 0; } int tmpfs_link(__unused struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) { struct dirlist *new = malloc(sizeof(struct dirlist)); if (!new) return -1; new->inode = dentry->d_inode; strncpy(new->name, dentry->d_name, NAME_MAX); struct list_head *dirlist = (struct list_head *) dir->i_private; list_add_tail(&new->list, dirlist); dir->i_size++; return 0; } int tmpfs_iterate(struct file *file, struct dir_context *ctx) { int res = -1; struct inode *inode = file->f_dentry->d_inode; if (file->f_pos == inode->i_size + 2) return -1; switch (file->f_pos) { case 0: res = dir_emit_dot(file, ctx); break; case 1: res = dir_emit_dotdot(file, ctx); break; default:; struct dirlist *dirlist; struct list_head *head = (struct list_head *) inode->i_private; int i = 2; list_for_each_entry(dirlist, head, list) { if (i++ == file->f_pos) { res = dir_emit(ctx, dirlist->name, strlen(dirlist->name), dirlist->inode->i_ino, 0); break; } } } file->f_pos++; return res; } const struct dentry_operations tmpfs_dops; struct dentry *tmpfs_lookup(struct inode *dir, struct dentry *target) { struct list_head *head = (struct list_head *) dir->i_private; struct dirlist *dirlist; list_for_each_entry(dirlist, head, list) { if (!strcmp(target->d_name, dirlist->name)) { target->d_inode = dirlist->inode; target->d_op = &tmpfs_dops; return target; } } return NULL; } int tmpfs_delete(struct dentry *dentry) { free(dentry); return 0; } const struct inode_operations tmpfs_iops = { .lookup = tmpfs_lookup, .link = tmpfs_link, }; const struct file_operations tmpfs_fops = { .iterate = tmpfs_iterate, }; const struct dentry_operations tmpfs_dops = { .delete = tmpfs_delete, }; static struct inode tmpfs_inodes[] = { { /* / - the root directory */ .i_ino = 1, .i_op = &tmpfs_iops, .i_fop = &tmpfs_fops, .i_mode = S_IFDIR, .i_size = 2, .i_private = &((struct list_head){}), }, { /* /dev - essential device files */ .i_ino = 2, .i_op = &tmpfs_iops, .i_fop = &tmpfs_fops, .i_mode = S_IFDIR, .i_private = &((struct list_head){}), }, { /* /proc - process and kernel information as files */ .i_ino = 3, .i_op = &tmpfs_iops, .i_fop = &tmpfs_fops, .i_mode = S_IFDIR, .i_private = &((struct list_head){}), }, }; struct inode *root_inode(void) { return &tmpfs_inodes[0]; } struct inode *dev_inode(void) { return &tmpfs_inodes[1]; } struct inode *proc_inode(void) { return &tmpfs_inodes[2]; } struct dentry *root_dentry(void) { static struct dentry dentry = { .d_name = "/", .d_inode = &tmpfs_inodes[0], .d_parent = &dentry, .d_op = &tmpfs_dops, }; return &dentry; } struct inode *init_tmpfs_inode(struct inode *inode) { static ino_t ino = 9000; inode->i_ino = ino++; inode->i_op = &tmpfs_iops; return inode; } void tmpfs_init(void) { static struct dirlist entries[] = { { .inode = &tmpfs_inodes[1], .name = "dev", }, { .inode = &tmpfs_inodes[2], .name = "proc", }, }; struct list_head *rootdir = (struct list_head *) tmpfs_inodes[0].i_private; INIT_LIST_HEAD(rootdir); list_add_tail(&entries[0].list, rootdir); list_add_tail(&entries[1].list, rootdir); struct list_head *devdir = (struct list_head *) tmpfs_inodes[1].i_private; INIT_LIST_HEAD(devdir); struct list_head *procdir = (struct list_head *) tmpfs_inodes[2].i_private; INIT_LIST_HEAD(procdir); } ================================================ FILE: include/arch/semihosting.h ================================================ #ifndef _ARCH_SEMIHOSTING_H #define _ARCH_SEMIHOSTING_H void v7m_semihost_exit(int status); #endif /* !_ARCH_SEMIHOSTING_H */ ================================================ FILE: include/arch/v7m-helper.h ================================================ #ifndef _ARCH_V7M_HELPER_H #define _ARCH_V7M_HELPER_H #define V7M_EXC_RETURN_HANDLER_MAIN 0xfffffff1 #define V7M_EXC_RETURN_THREAD_MAIN 0xfffffff9 #define V7M_EXC_RETURN_THREAD_PROCESS 0xfffffffd static inline void *v7m_set_thumb_bit(void *addr) { return (void *) ((unsigned long) addr | 1ul); } static inline void *v7m_clear_thumb_bit(void *addr) { return (void *) ((unsigned long) addr & ~1ul); } #endif /* !_ARCH_V7M_HELPER_H */ ================================================ FILE: include/fs/romfs.h ================================================ #ifndef _KERNEL_FS_ROMFS_H #define _KERNEL_FS_ROMFS_H #include #include #define ROMFS_FILETYPE_MASK 0x7 #define ROMFS_FILETYPE_HARD 0 #define ROMFS_FILETYPE_DIR 1 #define ROMFS_FILETYPE_REG 2 #define ROMFS_FILETYPE_LNK 3 #define ROMFS_FILETYPE_BLK 4 #define ROMFS_FILETYPE_CHR 5 #define ROMFS_FILETYPE_SOCKET 6 #define ROMFS_FILETYPE_FIFO 7 struct romfs_superblock { __u8 magic_number[8]; __u32 full_size; __u32 checksum; char volume_name[0]; }; struct romfs_inode { __u32 next_filehdr; __u32 spec_info; __u32 size; __u32 checksum; char file_name[0]; }; #define ROMFS_SUPER_BLOCK(sb) \ ({ \ struct mtd_info *mtd = (sb)->s_private; \ struct romfs_superblock *rs = mtd->priv; \ rs; \ }) #define ROMFS_INODE(rs, offset) \ ({ \ __u32 addr = (__u32)(rs) + (__u32)(offset); \ struct romfs_inode *ri = (struct romfs_inode *) addr; \ ri; \ }) int romfs_mount(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data); #endif /* !_KERNEL_FS_ROMFS_H */ ================================================ FILE: include/kernel/bitmap.h ================================================ #ifndef _LINUX_BITMAP_H #define _LINUX_BITMAP_H #include "linux/list.h" #include "kernel/bitops.h" struct bitmap_struct { unsigned long map; struct list_head queue[32]; }; static inline void INIT_BITMAP(struct bitmap_struct *bm) { WRITE_ONCE(bm->map, 0UL); for (int i = 0; i < 32; i++) INIT_LIST_HEAD(&bm->queue[i]); } static inline void bitmap_queue_add(struct list_head *new, unsigned long bit, struct bitmap_struct *bm) { list_add_tail(new, &bm->queue[bit]); bitmap_set_bit(&bm->map, bit); } static inline void bitmap_queue_add_first(struct list_head *new, unsigned long bit, struct bitmap_struct *bm) { list_add(new, &bm->queue[bit]); bitmap_set_bit(&bm->map, bit); } static inline void bitmap_enqueue(struct list_head *new, unsigned long bit, struct bitmap_struct *bm) { bitmap_queue_add(new, bit, bm); } static inline void bitmap_enqueue_first(struct list_head *new, unsigned long bit, struct bitmap_struct *bm) { bitmap_queue_add_first(new, bit, bm); } static inline int bitmap_empty(const struct bitmap_struct *bm) { return !(!READ_ONCE(bm->map)); } static inline int bitmap_queue_empty(struct bitmap_struct *bm, unsigned long bit) { return !bitmap_get_bit(&bm->map, bit); } static inline int bitmap_first_bit(const struct bitmap_struct *bm) { return find_first_bit(&bm->map, 32); } static inline void bitmap_queue_del(struct list_head *queue, unsigned long bit, struct bitmap_struct *bm) { list_del(queue); if (list_empty(&bm->queue[bit])) bitmap_clear_bit(&bm->map, bit); } static inline struct list_head *bitmap_dequeue(struct bitmap_struct *bm, unsigned long bit) { struct list_head *first = bm->queue[bit].next; bitmap_queue_del(first, bit, bm); return first; } static inline struct list_head *bitmap_dequeue_tail(struct bitmap_struct *bm, unsigned long bit) { struct list_head *last = bm->queue[bit].prev; bitmap_queue_del(last, bit, bm); return last; } #define bitmap_first_entry(bm, bit, type, member) \ list_entry(bm->queue[bit].next, type, member) #endif ================================================ FILE: include/kernel/bitops.h ================================================ #ifndef KERNEL_BITOPS_H #define KERNEL_BITOPS_H #include #include #include #define BITS_PER_CHAR 8 #define BITS_PER_LONG (BITS_PER_CHAR * sizeof(long)) static inline unsigned long flsl(unsigned long word) { return word ? sizeof(long) * BITS_PER_CHAR - __builtin_clz(word) : 0; } static inline void clear_bit(unsigned long bit, unsigned long *word) { *word &= ~(1 << bit); } static inline void set_bit(unsigned long bit, unsigned long *word) { *word |= (1 << bit); } static inline void bitmap_set_bit(unsigned long *map, unsigned long bit) { set_bit(bit % BITS_PER_LONG, &map[bit / BITS_PER_LONG]); } static inline void bitmap_clear_bit(unsigned long *map, unsigned long bit) { clear_bit(bit % BITS_PER_LONG, &map[bit / BITS_PER_LONG]); } static inline unsigned long bitmap_get_bit(unsigned long *map, unsigned long bit) { return (map[bit / BITS_PER_LONG] >> (bit % BITS_PER_LONG)) & 1; } static inline unsigned long find_first_bit(const unsigned long *addr, unsigned long size) { for (unsigned long i = 0; i * BITS_PER_LONG < size; i++) { if (addr[i]) return MIN(i * BITS_PER_LONG + __builtin_ffsl(addr[i]) - 1, size); } return size; } static inline unsigned long find_first_zero_bit(const unsigned long *addr, unsigned long size) { for (unsigned long i = 0; i * BITS_PER_LONG < size; i++) { if (addr[i] != ~0ul) return MIN(i * BITS_PER_LONG + __builtin_ffsl(~addr[i]) - 1, size); } return size; } #endif /* !KERNEL_BITOPS_H */ ================================================ FILE: include/kernel/cbuf.h ================================================ #ifndef _KERNEL_CBUF_H #define _KERNEL_CBUF_H #include struct cbuf_info { size_t len; int pos_begin; int pos_end; void *buf; }; static inline void cbuf_init(struct cbuf_info *cbuf, void *buf, size_t len) { cbuf->len = len; cbuf->pos_begin = 0; cbuf->pos_end = 0; cbuf->buf = buf; } static inline int cbuf_getc(struct cbuf_info *cbuf, char *c) { if (cbuf->pos_begin == cbuf->pos_end) return 0; *c = *((char *) cbuf->buf + cbuf->pos_begin); cbuf->pos_begin = (cbuf->pos_begin + 1) % cbuf->len; return 1; } static inline int cbuf_putc(struct cbuf_info *cbuf, char c) { *((char *) cbuf->buf + cbuf->pos_end) = c; cbuf->pos_end = (cbuf->pos_end + 1) % cbuf->len; return 0; } #endif /* !_KERNEL_CBUF_H */ ================================================ FILE: include/kernel/compiler.h ================================================ #ifndef _KERNEL_COMPILER_H #define _KERNEL_COMPILER_H /* GCC weak symbol declaration */ #ifndef __weak #define __weak __attribute__((weak)) #endif #endif /* !_KERNEL_COMPILER_H */ ================================================ FILE: include/kernel/cond.h ================================================ #ifndef KERNEL_COND_H #define KERNEL_COND_H #include int sys_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); int sys_pthread_cond_signal(pthread_cond_t *cond); #endif /* !KERNEL_COND_H */ ================================================ FILE: include/kernel/errno-base.h ================================================ #ifndef _KERNEL_ERRNO_BASE_H_ #define _KERNEL_ERRNO_BASE_H_ #define EPERM 1 /* Operation not permitted */ #define ENOENT 2 /* No such file or directory */ #define ESRCH 3 /* No such process */ #define EINTR 4 /* Interrupted system call */ #define EIO 5 /* I/O error */ #define ENXIO 6 /* No such device or address */ #define E2BIG 7 /* Argument list too long */ #define ENOEXEC 8 /* Exec format error */ #define EBADF 9 /* Bad file number */ #define ECHILD 10 /* No child processes */ #define EAGAIN 11 /* Try again */ #define ENOMEM 12 /* Out of memory */ #define EACCES 13 /* Permission denied */ #define EFAULT 14 /* Bad address */ #define ENOTBLK 15 /* Block device required */ #define EBUSY 16 /* Device or resource busy */ #define EEXIST 17 /* File exists */ #define EXDEV 18 /* Cross-device link */ #define ENODEV 19 /* No such device */ #define ENOTDIR 20 /* Not a directory */ #define EISDIR 21 /* Is a directory */ #define EINVAL 22 /* Invalid argument */ #define ENFILE 23 /* File table overflow */ #define EMFILE 24 /* Too many open files */ #define ENOTTY 25 /* Not a typewriter */ #define ETXTBSY 26 /* Text file busy */ #define EFBIG 27 /* File too large */ #define ENOSPC 28 /* No space left on device */ #define ESPIPE 29 /* Illegal seek */ #define EROFS 30 /* Read-only file system */ #define EMLINK 31 /* Too many links */ #define EPIPE 32 /* Broken pipe */ #define EDOM 33 /* Math argument out of domain of func */ #define ERANGE 34 /* Math result not representable */ #endif /* !_KERNEL_ERRNO_BASE_H_ */ ================================================ FILE: include/kernel/faults.h ================================================ #ifndef KERNEL_FAULTS_H #define KERNEL_FAULTS_H #include #include void fault_enter(const char *s); void fault_exit(void); /* arch-dependent */ void dump_frame(struct kernel_context_regs *noscratch, struct thread_context_regs *scratch, u32 exc_return); #endif /* !KERNEL_FAULTS_H */ ================================================ FILE: include/kernel/fs.h ================================================ #ifndef _KERNEL_FS_H #define _KERNEL_FS_H #include #include #include #include #define NAME_MAX 32 // FIXME: Include #define FILE_MAX 8 #define O_DIRECTORY 1 /* * super_block struct */ struct dentry; struct inode; // FIXME: delete me from here, using s_root dentry struct super_block { void *s_private; // for dev, pointing to MTD struct inode *s_iroot; // FIXME: just use s_root and dentry }; /* * inode struct */ struct inode_operations; struct file_operations; struct inode { struct list_head i_list; /* list of inodes */ umode_t i_mode; /* access permissions */ // kernel_ino_t unsigned long i_ino; /* inode number */ atomic_t i_count; /* reference counter */ off_t i_size; /* file size in bytes */ const struct inode_operations *i_op; /* inode ops table */ const struct file_operations *i_fop; /* default inode ops */ struct super_block *i_sb; /* associated superblock */ void *i_private; char i_data[0]; }; struct inode_operations { struct dentry *(*lookup)(struct inode *inode, struct dentry *dentry); int (*link)(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry); }; /* * file struct */ struct file { struct dentry *f_dentry; /* associated dentry object */ const struct file_operations *f_op; /* file operations table */ off_t f_pos; /* file offset (file pointer) */ void *f_private; }; struct dir_context; struct file_operations { off_t (*lseek)(struct file *file, off_t offset, int origin); ssize_t (*read)(struct file *file, char *buf, size_t count, off_t offset); ssize_t (*write)(struct file *file, const char *buf, size_t count, off_t *offset); int (*iterate)(struct file *file, struct dir_context *ctx); int (*mmap)(struct file *file, off_t offset, void **addr); /* struct vm_area_struct *area */ int (*open)(struct inode *inode, struct file *file); }; /* * dentry struct */ struct dentry { _Atomic int d_count; /* usage count */ struct inode *d_inode; /* associated inode */ const struct dentry_operations *d_op; /* dentry operations table */ struct dentry *d_parent; /* dentry object of parent */ char d_name[NAME_MAX]; /* short name */ // struct list_head d_child; /* child of parent list */ // struct list_head d_subdirs; /* our children */ }; struct dentry_operations { int (*delete)(struct dentry *dentry); void (*release)(struct dentry *dentry); }; /* readdir */ typedef int (*filldir_t)(struct dir_context *, const char *, int, off_t, unsigned int, unsigned int); struct dir_context { const filldir_t actor; off_t pos; // XXX: unused because piko_dirent has no offset field }; struct piko_dirent; struct readdir_callback { struct dir_context ctx; struct piko_dirent *dirent; int result; }; struct piko_dirent { ino_t d_ino; /* inode number */ char d_name[NAME_MAX]; /* filename */ }; static inline int dir_emit(struct dir_context *ctx, const char *name, int namelen, unsigned int ino, unsigned int type) { return ctx->actor(ctx, name, namelen, ctx->pos, ino, type); } static inline int dir_emit_dot(struct file *file, struct dir_context *ctx) { return ctx->actor(ctx, ".", 1, ctx->pos, file->f_dentry->d_inode->i_ino, 0); } static inline int dir_emit_dotdot(struct file *file, struct dir_context *ctx) { return ctx->actor(ctx, "..", 2, ctx->pos, file->f_dentry->d_parent->d_inode->i_ino, 0); } /* forward declarations */ int vfs_iterate(struct file *file, struct dir_context *ctx); struct dentry *vfs_lookup(struct inode *dir, struct dentry *target); int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry); int vfs_delete(struct dentry *dentry); void vfs_release(struct dentry *dentry); int vfs_mmap(struct file *file, off_t offset, void **addr); /* syscall entries */ typedef void DIR; int sys_opendir(const char *name); int sys_readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result); int sys_open(const char *pathname, int flags); ssize_t sys_read(int fd, void *buf, size_t count); ssize_t sys_write(int fd, void *buf, size_t count); off_t sys_seek(int fd, off_t offset, int whence); int sys_close(int fd); /* misc functions */ struct inode *root_inode(void); struct inode *dev_inode(void); struct inode *proc_inode(void); struct dentry *root_dentry(void); struct file *fd_to_file(int fd); struct inode *init_tmpfs_inode(struct inode *inode); struct inode *inode_from_pathname(const char *pathname); void tmpfs_init(void); void proc_init(void); #endif /* !_KERNEL_FS_H */ ================================================ FILE: include/kernel/hash.h ================================================ #ifndef _KERNEL_HASH_H #define _KERNEL_HASH_H #include static inline unsigned long hash_djb2(unsigned char *str, size_t len) { unsigned long hash = 5381; for (int i = 0; i < (int) len; i++) { int c = str[i]; hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ } return hash; } #endif /* _KERNEL_HASH_H */ ================================================ FILE: include/kernel/irq.h ================================================ #ifndef KERNEL_IRQ_H #define KERNEL_IRQ_H #define NR_IRQS 64 /* irq status */ enum { IRQ_NOREQUEST = 1, }; /* irq data state(for per-chip) */ enum { IRQD_IRQ_DISABLED = 1, IRQD_INACTIVATED = 1 << 2, IRQD_ACTIVATED = 1 << 3, IRQD_PENDING = 1 << 4, }; /** * struct irq_data - per chip data * * @state: status information for irq chip * XXX: Thanks for CMSIS. We don't have to impl per chip function and data, * but this concept should be remained due to more precious description * of irqs and chips(HW). */ struct irq_data { unsigned int state; }; struct irqaction; /** * struct irq_desc - interrupt descriptor * * @action: the irq action chain * @status: status information * @irq_state: per irq state information */ struct irq_desc { struct irq_data irq_data; struct irqaction *action; unsigned int status; }; typedef void (*irq_handler_t)(void); /** * struct irqaction - per interrupt action descriptor * @handler: interrupt handler function * @irq: interrupt number */ struct irqaction { irq_handler_t handler; unsigned int irq; }; int request_irq(unsigned int irq, irq_handler_t hdlr); int free_irq(unsigned int irq); #endif /* !KERNEL_IRQ_H */ ================================================ FILE: include/kernel/kernel.h ================================================ #ifndef _KERNEL_KERNEL_H #define _KERNEL_KERNEL_H /* round-down to a power of 2 */ #define align(x, a) align_mask(x, (__typeof__(x))((a) -1)) #define align_mask(x, mask) ((x) & ~(mask)) /* round-up to a power of 2 */ #define align_next(x, a) align_next_mask(x, (__typeof__(x))((a) -1)) #define align_next_mask(x, mask) (((x) + (mask)) & ~(mask)) #define ARRAY_SIZE(arr) ((size_t)(sizeof(arr) / sizeof(*(arr)))) #define ARRAY_INDEX(elt, arr) \ ({ \ unsigned int _elt = (unsigned int) (elt); \ unsigned int _arr = (unsigned int) (arr); \ (_elt - _arr) / sizeof(*(elt)); \ }) #define container_of(ptr, type, member) \ ({ \ const __typeof__(((type *) 0)->member) *__mptr = (ptr); \ (type *) ((char *) __mptr - offsetof(type, member)); \ }) #define SWAP(x, y) \ { \ __typeof__(x) tmp = x; \ x = y; \ y = tmp; \ } \ while (0) int printk(const char *fmt, ...); #endif /* !_KERNEL_KERNEL_H */ ================================================ FILE: include/kernel/linkage.h ================================================ #ifndef _KERNEL_LINKAGE_H #define _KERNEL_LINKAGE_H #ifdef __ASSEMBLER__ #define ENTRY(name) \ .globl name; \ .align 4; \ name: #define END(name) .size name, .- name #define ENDPROC(name) \ .type name, % function; \ END(name) #endif /* __ASSEMBLER__ */ #endif /* !_KERNEL_LINKAGE_H */ ================================================ FILE: include/kernel/log2.h ================================================ #ifndef KERNEL_LOG2_H #define KERNEL_LOG2_H #include // clang-format off #define ilog2(x) \ ( \ __builtin_constant_p(x) ? ( \ (x) < 1 ? __ilog2_NaN() : \ (x) & (1ul << 31) ? 31 : \ (x) & (1ul << 30) ? 30 : \ (x) & (1ul << 29) ? 29 : \ (x) & (1ul << 28) ? 28 : \ (x) & (1ul << 27) ? 27 : \ (x) & (1ul << 26) ? 26 : \ (x) & (1ul << 25) ? 25 : \ (x) & (1ul << 24) ? 24 : \ (x) & (1ul << 23) ? 23 : \ (x) & (1ul << 22) ? 22 : \ (x) & (1ul << 21) ? 21 : \ (x) & (1ul << 20) ? 20 : \ (x) & (1ul << 19) ? 19 : \ (x) & (1ul << 18) ? 18 : \ (x) & (1ul << 17) ? 17 : \ (x) & (1ul << 16) ? 16 : \ (x) & (1ul << 15) ? 15 : \ (x) & (1ul << 14) ? 14 : \ (x) & (1ul << 13) ? 13 : \ (x) & (1ul << 12) ? 12 : \ (x) & (1ul << 11) ? 11 : \ (x) & (1ul << 10) ? 10 : \ (x) & (1ul << 9) ? 9 : \ (x) & (1ul << 8) ? 8 : \ (x) & (1ul << 7) ? 7 : \ (x) & (1ul << 6) ? 6 : \ (x) & (1ul << 5) ? 5 : \ (x) & (1ul << 4) ? 4 : \ (x) & (1ul << 3) ? 3 : \ (x) & (1ul << 2) ? 2 : \ (x) & (1ul << 1) ? 1 : \ (x) & (1ul << 0) ? 0 : \ __ilog2_NaN() \ ) : \ __ilog2(x) \ ) // clang-format on static inline unsigned long __ilog2(unsigned long x) { return flsl(x) - 1; } static inline __attribute__((noreturn)) unsigned long __ilog2_NaN(void) { for (;;) ; } #endif /* !KERNEL_LOG2_H */ ================================================ FILE: include/kernel/mm/page.h ================================================ #ifndef _KERNEL_MM_PAGE_H #define _KERNEL_MM_PAGE_H #define MAX_PAGE_ORDER 3 /* max page size is 2 KB */ #define MIN_PAGE_SIZE 256 /* min page size is 256 bytes */ void *alloc_pages(unsigned long order); void free_pages(unsigned long addr, unsigned long order); void show_page_bitmap(void); long size_to_page_order(unsigned long size); unsigned long page_alloc_signature(void); #endif /* !_KERNEL_MM_PAGE_H */ ================================================ FILE: include/kernel/mm/slab.h ================================================ #ifndef _KERNEL_MM_SLAB_H #define _KERNEL_MM_SLAB_H #include #include "linux/list.h" #define CACHE_PAGE_SIZE 256 #define CACHE_NAMELEN 16 #define CACHE_OPT_NONE 0 /* OPT_FORCE: create a new cache, even when the object size matches a generic * cache (i.e. 8B, 16B, 32B...) * OPT_PERSIST: this cache cannot be destroyed (i.e. when a cache w/o OPT_FORCE * has been created and silently merged into a general object * cache and the user try to destroy the cache he created) */ struct kmem_cache { struct list_head slabs_free; struct list_head slabs_partial; struct list_head slabs_full; struct list_head list; /* linked list of caches */ unsigned short objsize; /* size of one object within a slab */ unsigned short objnum; /* number of objects per slab */ int opts; int alloc_succeed; int alloc_fail; char name[CACHE_NAMELEN]; }; struct slab { unsigned long free_bitmap[1]; int free_objects; /* number of free objects in that slab */ struct list_head list; /* pointer to prev/next slabs for that cache */ char data[0]; /* struct kmem_cache *backlink; // backlink to the cache structure */ }; #define KMEM_CACHE(objtype, name) \ kmem_cache_create(name, sizeof(objtype), 0, CACHE_OPT_NONE, NULL) struct kmem_cache *kmem_cache_create(const char *name, size_t size, __unused size_t align, __unused unsigned long flags, __unused void (*ctor)(void *)); void *kmem_cache_alloc(struct kmem_cache *cache, __unused unsigned long flags); void kmem_cache_free(struct kmem_cache *cache, void *obj); void kmem_cache_init(void); #endif /* _KERNEL_MM_SLAB_H */ ================================================ FILE: include/kernel/mutex.h ================================================ #ifndef _KERNEL_MUTEX_H #define _KERNEL_MUTEX_H typedef _Atomic int atomic_s32; typedef struct { atomic_s32 val; } kernel_mutex_t; int sys_pthread_mutex_lock(kernel_mutex_t *mutex); int sys_pthread_mutex_unlock(kernel_mutex_t *mutex); #endif /* !_KERNEL_MUTEX_H */ ================================================ FILE: include/kernel/sched.h ================================================ #ifndef KERNEL_SCHEDULER_H #define KERNEL_SCHEDULER_H #include_next struct thread_info; /* 0 <= PRI_MAX <= PRI_MIN */ #define PRI_MAX 0 #define PRI_MIN 31 #define SCHED_CLASS_RR 0 #define SCHED_CLASS_BITMAP 1 typedef int sched_class_t; #define SCHED_OPT_NONE 0 #define SCHED_OPT_RESTORE_ONLY 1 #define SCHED_OPT_RESET 2 #define SCHED_OPT_TICK 3 /* scheduler implementation hooks */ #define HOOK_SCHED_CLASS(name, sched_struct) \ static struct sched *sched_class_##name \ __attribute__((section(".sched.class"), aligned(sizeof(long)), \ used)) = sched_struct; struct sched { sched_class_t class_type; int (*init)(void); int (*enqueue)(struct thread_info *thread); int (*dequeue)(struct thread_info *thread); int (*elect)(int switch_type); }; int sched_init(); int sched_select(sched_class_t sched_type); int sched_enqueue(struct thread_info *thread); int sched_dequeue(struct thread_info *thread); int sched_elect(int flags); #endif /* !KERNEL_SCHEDULER_H */ ================================================ FILE: include/kernel/serial.h ================================================ /* * Interface for serial devices (UART, ...) */ #ifndef _KERNEL_SERIAL_H #define _KERNEL_SERIAL_H #include #include struct serial_ops; struct device; struct thread_info; struct serial_info { void *priv; unsigned int rx_count; struct serial_ops *ops; // XXX: owner thread pointer could go to priv, and device pripheral // base address should be linked to private data for the device struct thread_info *owner; }; struct serial_ops { int (*serial_init)(struct serial_info *serial); int (*serial_getc)(struct serial_info *serial, char *c); int (*serial_gets)(struct serial_info *serial, size_t len, size_t *retlen, char *buf); int (*serial_putc)(struct serial_info *serial, char c); int (*serial_puts)(struct serial_info *serial, size_t len, const char *buf); /* callback on device activity, set by ioctl() */ void (*callback)(struct serial_info *self); }; struct serial_hook { char name[10]; int (*init)(void); }; /* serial hooks */ #define HOOK_SERIAL_INIT(dev, init_func) \ static struct serial_hook serial_##dev##_hook \ __attribute__((section(".serial.hook"), aligned(sizeof(long)), \ used)) = {#dev, init_func}; /* Generic uart setup */ void uart_init(void); /* Generic serial init */ void serial_init(void); /* XXX: All func below is callback function */ int serial_getc(struct serial_info *serial, char *c); int serial_gets(struct serial_info *serial, size_t len, size_t *retlen, char *buf); int serial_putc(struct serial_info *serial, char c); int serial_puts(struct serial_info *serial, size_t len, const char *buf); void serial_activity_callback(struct serial_info *serial); #endif /* !_KERNEL_SERIAL_H */ ================================================ FILE: include/kernel/signal.h ================================================ #ifndef _KERNEL_SIGNAL_H #define _KERNEL_SIGNAL_H #include #include #include #include "linux/types.h" #define SIGMAX 31 struct signal_info { int signo; struct list_head list; struct sigaction act_storage; }; void do_sigevent(const struct sigevent *sigevent, struct thread_info *thread); #endif /* !_KERNEL_SIGNAL_H */ ================================================ FILE: include/kernel/softirq.h ================================================ #ifndef KERNEL_SOFTIRQ_H #define KERNEL_SOFTIRQ_H #include "linux/list.h" #define PRIO_TASKLET_VEC 32 #define PRIO_TASKLET_MAXPRIO 0 #define PRIO_TASKLET_MINPRIO 31 /* softirq */ enum { TIMER_SOFTIRQ = 0, }; /* softirq default priority */ enum { TIMER_SOFTIRQ_PRIO = 0, }; /* softirq entry */ struct softirq_action { int (*action)(struct softirq_action *); }; /* FIXME: re-entrance */ /* dynamic task */ struct tasklet_struct { struct list_head tsk_q; unsigned long prio; // unsigned long state; void (*func)(void *); void *data; }; enum { TASKLET_STATE_SCHED, /* Tasklet is scheduled for execution */ TASKLET_STATE_RUN, /* Tasklet is running (SMP only) */ TASKLET_STATE_PENDING /* Tasklet is pending */ }; #define TASKLET_STATEF_SCHED (1 << TASKLET_STATE_SCHED) #define TASKLET_STATEF_RUN (1 << TASKLET_STATE_RUN) #define TASKLET_STATEF_PENDING (1 << TASKLET_STATE_PENDING) int open_softirq(unsigned int nr, int (*action)(struct softirq_action *)); int raise_softirq(unsigned int nr); int tasklet_schedule(struct tasklet_struct *task); struct tasklet_struct *tasklet_init(void(*func), void *data, unsigned long prio); int init_softirq(void); #endif /* KERNEL_SOFTIRQ_H */ ================================================ FILE: include/kernel/task.h ================================================ #ifndef _KERNEL_TASK_H #define _KERNEL_TASK_H #include #include #include "linux/list.h" #define PID_BASE 7000 #define PID_MAX 32768 struct task_info { pid_t pid; unsigned long filemap; struct file filetable[FILE_MAX]; struct list_head list; struct list_head thread_head; struct list_head signal_head; /* list of installed handlers */ }; struct task_info *task_init(struct task_info *task); struct task_info *task_create(void *(*start_routine)(void *), void *arg); void task_exit(struct task_info *task); struct task_info *current_task_info(void); #define CURRENT_TASK_INFO(var) struct task_info *var = current_task_info(); #endif /* !_KERNEL_TASK_H */ ================================================ FILE: include/kernel/thread.h ================================================ #ifndef _KERNEL_THREAD_H #define _KERNEL_THREAD_H #include #include #include "linux/types.h" #include "linux/list.h" #define INTR_STACK_ORDER 9 /* 512 Bytes */ #define INTR_STACK_SIZE (1 << INTR_STACK_ORDER) /* machine-specific thread info on ARM */ struct mthread_info { u32 mi_msp; /* +0 */ u32 mi_psp; /* +4 */ u32 mi_priv; /* +8 */ } __attribute__((packed)); struct task_info; struct thread_info { /* machine-specific thread info */ struct mthread_info ti_mach; /* thread description data */ int ti_priority; int ti_id; int ti_state; size_t ti_stacksize; /* user thread stacksize */ struct task_info *ti_task; struct list_head ti_list; /* global list of threads */ struct list_head ti_q; /* shared by sched runq, mutex waitq, thread joinq */ /* http://www.domaigne.com/blog/computing/joinable-and-detached-threads/ */ void *ti_retval; int ti_detached; int ti_joinable; struct thread_info *ti_joining; /* Pointer to mutually exclusive data: the mutex the thread is blocking * on, the exit value when thread is not yet joined, etc. */ void *ti_private; /* /\* local-storage *\/ */ /* struct list_head *ti_lsq; // local-storage queue */ #ifdef CONFIG_KERNEL_STACK_CHECKING u32 ti_canary[2]; #endif char ti_storage[0]; }; enum thread_privilege { THREAD_PRIV_SUPERVISOR = 0, THREAD_PRIV_USER = 1 }; enum thread_state { /* Thread structure allocated but not enqueued in the system scheduler. */ THREAD_STATE_NEW, /** * Ready to run in the system scheduler. * * XXX: why we need two ready states? * * Ans: One is ready for execution in active queue, * the other one is the state for out of * timeslice. In order to achieve O(1), we * adopt specified function to determine * which one is actived or expired. * * XXX: we impl map from `actived` and `expired` to * `ready1` and `ready2` */ THREAD_STATE_READY1, // THREAD_STATE_ACTIVED THREAD_STATE_READY2, // THREAD_STATE_EXPIRED /* Running by the system scheduler. */ THREAD_STATE_RUNNING, /* The thread has normally exited or has called Pthread_exit to exit. Its * resources have not been freed and will be freed if it is detached or * joined. */ THREAD_STATE_TERMINATED, /* Waiting for a mutex or resource. */ THREAD_STATE_BLOCKED }; #define THREAD_STATE_ACTIVED THREAD_SCHED_STATE[ACTIVED] #define THREAD_STATE_EXPIRED THREAD_SCHED_STATE[EXPIRED] enum { ACTIVED, EXPIRED, NR_THREAD_SCHED_STATE, }; static int THREAD_SCHED_STATE[NR_THREAD_SCHED_STATE] = { [ACTIVED] = THREAD_STATE_READY1, [EXPIRED] = THREAD_STATE_READY2, }; static inline void swap_sched_state_map(void) { SWAP(THREAD_SCHED_STATE[ACTIVED], THREAD_SCHED_STATE[EXPIRED]); } /* * This stackframe is built to handle the first scheduling of a task on * the CPU. Running a task for the first time is achieved in two stages. * * Stage 1: * After switch_to() has switched the interrupt stacks for current and * next tasks the function will restore the kernel-context for the next * thread. That context is the non-scratch registers r4 to r11. At this * point we don't care about the values in these registers: they are * not used after that point during in that interrupt, and the next * interrupt context will reinitilized them should it need to. * * Stage 2: * After restoring the non-scratch registers we return from switch_to() * and the value loaded in LR in stage1 is the address of task_kickstart(). * This function is a trampoline that emulate a return 'from interrupt * sequence' by restoring the non-scratch registers for the tasks as * well as triggering a switch from Handler_Mode to Thread_Mode in the * CPU. We initialize the task non-scratch registers to 0. */ struct kernel_context_regs { u32 r4_r12[9]; /* r4 to r12, zero-filled */ u32 lr; /* initially loaded with EXC_RETURN value */ }; struct thread_context_regs { u32 r0_r3__r12[5]; /* r0 to r3, r12; args or zero-filled */ u32 lr; /* initially loaded with pthread_exit() */ u32 ret_addr; /* thread entry-point function */ u32 xpsr; /* forced to Thumb_Mode */ }; /* forward declarations */ void switch_to(struct thread_info *, struct thread_info *); void thread_restore( struct thread_info *); // FIXME: rename to switch_to_restore_only ? meh.. struct thread_info *thread_create(void *(*) (void *), void *, enum thread_privilege, size_t, struct task_info *); int thread_yield(void); int thread_self(void); void thread_exit(void *); int thread_set_priority(struct thread_info *thread, int priority); int thread_detach(pthread_t thread); static inline struct thread_info *current_thread_info(void) { struct thread_info *this; __asm__ __volatile__( "mov %0, sp \n\t" "bfc %0, #0, %1" : "=r"(this) : "M"(INTR_STACK_ORDER)); return this; } #define CURRENT_THREAD_INFO(var) \ struct thread_info *var = current_thread_info(); #define THREAD_INFO(addr) \ ({ (struct thread_info *) align((unsigned long) addr, INTR_STACK_SIZE); }) #define THREAD_CANARY0 0xee48a608 #define THREAD_CANARY1 0x840dc3bc #ifdef CONFIG_KERNEL_STACK_CHECKING #define KERNEL_STACK_CHECKING \ ({ \ __auto_type cur_thread = current_thread_info(); \ if ((cur_thread->ti_canary[0] != THREAD_CANARY0) || \ (cur_thread->ti_canary[1] != THREAD_CANARY1)) { \ printk("\nkernel panic: Overflow in kernel stack\n"); \ printk(" 0 %08x %08x\n", THREAD_CANARY0, \ cur_thread->ti_canary[0]); \ printk(" 1 %08x %08x\n", THREAD_CANARY1, \ cur_thread->ti_canary[1]); \ for (;;) \ ; \ } \ }) #else #define KERNEL_STACK_CHECKING #endif #endif /* !_KERNEL_THREAD_H */ ================================================ FILE: include/kernel/time.h ================================================ #ifndef _KERNEL_TIME_H #define _KERNEL_TIME_H #include #include #include //FIXME: Why including this header explicitly? #include_next #include #include #include "linux/list.h" struct timer_info; enum timer_type { ONESHOT_TIMER, INTERVAL_TIMER }; struct timer_operations { int (*timer_alloc)(struct timer_info *timer /* , int flags */); int (*timer_free)(struct timer_info *timer); int (*timer_set)(struct timer_info *timer, const struct timespec *value); int (*timer_get)(struct timer_info *timer, struct itimerspec *value); }; struct timer_info { timer_t id; u32 flags; int disarmed; enum timer_type type; void (*callback)(struct timer_info *self); struct thread_info *owner; struct itimerspec value; struct sigevent sigev; struct list_head list; const struct timer_operations *tops; void *dev; }; struct timer_info *timer_alloc(void); int timer_free(struct timer_info *timer); int timer_set(struct timer_info *timer, const struct timespec *value); int timer_get(struct timer_info *timer, struct itimerspec *value); void timer_expire_callback(struct timer_info *timer); #endif /* !_KERNEL_TIME_H */ ================================================ FILE: include/kernel/types.h ================================================ #ifndef _KERNEL_TYPES_H #define _KERNEL_TYPES_H #ifndef _U32 #define _U32 typedef unsigned int u32; #endif #ifndef _S32 #define _S32 typedef int s32; #endif #ifndef _U16 #define _U16 typedef unsigned short u16; #endif #ifndef _S16 #define _S16 typedef short s16; #endif #ifndef _U8 #define _U8 typedef unsigned char u8; #endif #ifndef _S8 #define _S8 typedef char s8; #endif #ifndef _UMODE_T #define _UMODE_T typedef unsigned short umode_t; #endif typedef u32 __u32; typedef s32 __s32; typedef u16 __u16; typedef s16 __s16; typedef u8 __u8; typedef s8 __s8; /* import struct list_head */ #include "linux/types.h" #endif /* !_KERNEL_TYPES_H */ ================================================ FILE: include/libc/pthread.h ================================================ #ifndef PTHREAD_H #define PTHREAD_H #include_next extern int pthread_yield(void); #endif /* PTHREAD_H */ ================================================ FILE: include/libc/ucontext.h ================================================ #ifndef UCONTEXT_H #define UCONTEXT_H #include #include "linux/types.h" /* machine context on ARM */ typedef struct mcontext { u32 sp; // FIXME: reuse uc_stack.ss_sp u32 lr; u32 gprs[13]; /* r0-r12 */ u32 pc; } __attribute__((packed)) mcontext_t; typedef struct ucontext { struct ucontext *uc_link; /* sigset_t uc_sigmask; */ stack_t uc_stack; mcontext_t uc_mcontext; /* ... */ } ucontext_t; /* forward declarations */ void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...); int swapcontext(ucontext_t *oucp, ucontext_t *ucp); #endif /* !UCONTEXT_H */ ================================================ FILE: include/libc/utils.h ================================================ #ifndef UTILS_H #define UTILS_H #ifndef __LINKER__ static inline void infinite_loop(void) { for (;;) ; } void strpad(char *buf, char pad_val, int count); char *itoa_base(int value, char *buf, int base); #endif /* !__LINKER__ */ #endif /* !UTILS_H */ ================================================ FILE: include/linux/compiler.h ================================================ #ifndef _LINUX_COMPILER_H #define _LINUX_COMPILER_H #define READ_ONCE(x) (x) #define WRITE_ONCE(x, val) \ ({ \ x = val; \ val; \ }) #endif /* !_LINUX_COMPILER_H */ ================================================ FILE: include/linux/list.h ================================================ #ifndef _LINUX_LIST_H #define _LINUX_LIST_H #include #include #include "linux/types.h" #include "linux/stddef.h" #include "linux/poison.h" #include "linux/compiler.h" /* for READ_ONCE and WRITE_ONCE definitions */ /* * Simple doubly linked list implementation. * * Some of the internal functions ("__xxx") are useful when * manipulating whole lists rather than single entries, as * sometimes we already know the next/prev entries and we can * generate better code by using them directly rather than * using the generic single-entry routines. */ #define LIST_HEAD_INIT(name) \ { \ &(name), &(name) \ } #define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name) static inline void INIT_LIST_HEAD(struct list_head *list) { WRITE_ONCE(list->next, list); list->prev = list; } /* * Insert a new entry between two known consecutive entries. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static inline void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next) { next->prev = new; new->next = next; new->prev = prev; WRITE_ONCE(prev->next, new); } /** * list_add - add a new entry * @new: new entry to be added * @head: list head to add it after * * Insert a new entry after the specified head. * This is good for implementing stacks. */ static inline void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); } /** * list_add_tail - add a new entry * @new: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. */ static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } /* * Delete a list entry by making the prev/next entries * point to each other. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static inline void __list_del(struct list_head *prev, struct list_head *next) { next->prev = prev; WRITE_ONCE(prev->next, next); } /** * list_del - deletes entry from list. * @entry: the element to delete from the list. * Note: list_empty() on entry does not return true after this, the entry is * in an undefined state. */ static inline void __list_del_entry(struct list_head *entry) { __list_del(entry->prev, entry->next); } static inline void list_del(struct list_head *entry) { __list_del(entry->prev, entry->next); entry->next = LIST_POISON1; entry->prev = LIST_POISON2; } /** * list_replace - replace old entry by new one * @old : the element to be replaced * @new : the new element to insert * * If @old was empty, it will be overwritten. */ static inline void list_replace(struct list_head *old, struct list_head *new) { new->next = old->next; new->next->prev = new; new->prev = old->prev; new->prev->next = new; } static inline void list_replace_init(struct list_head *old, struct list_head *new) { list_replace(old, new); INIT_LIST_HEAD(old); } /** * list_del_init - deletes entry from list and reinitialize it. * @entry: the element to delete from the list. */ static inline void list_del_init(struct list_head *entry) { __list_del_entry(entry); INIT_LIST_HEAD(entry); } /** * list_move - delete from one list and add as another's head * @list: the entry to move * @head: the head that will precede our entry */ static inline void list_move(struct list_head *list, struct list_head *head) { __list_del_entry(list); list_add(list, head); } /** * list_move_tail - delete from one list and add as another's tail * @list: the entry to move * @head: the head that will follow our entry */ static inline void list_move_tail(struct list_head *list, struct list_head *head) { __list_del_entry(list); list_add_tail(list, head); } /** * list_is_last - tests whether @list is the last entry in list @head * @list: the entry to test * @head: the head of the list */ static inline int list_is_last(const struct list_head *list, const struct list_head *head) { return list->next == head; } /** * list_empty - tests whether a list is empty * @head: the list to test. */ static inline int list_empty(const struct list_head *head) { return READ_ONCE(head->next) == head; } /** * list_empty_careful - tests whether a list is empty and not being modified * @head: the list to test * * Description: * tests whether a list is empty _and_ checks that no other CPU might be * in the process of modifying either member (next or prev) * * NOTE: using list_empty_careful() without synchronization * can only be safe if the only activity that can happen * to the list entry is list_del_init(). Eg. it cannot be used * if another CPU could re-list_add() it. */ static inline int list_empty_careful(const struct list_head *head) { struct list_head *next = head->next; return (next == head) && (next == head->prev); } /** * list_rotate_left - rotate the list to the left * @head: the head of the list */ static inline void list_rotate_left(struct list_head *head) { struct list_head *first; if (!list_empty(head)) { first = head->next; list_move_tail(first, head); } } /** * list_is_singular - tests whether a list has just one entry. * @head: the list to test. */ static inline int list_is_singular(const struct list_head *head) { return !list_empty(head) && (head->next == head->prev); } static inline void __list_cut_position(struct list_head *list, struct list_head *head, struct list_head *entry) { struct list_head *new_first = entry->next; list->next = head->next; list->next->prev = list; list->prev = entry; entry->next = list; head->next = new_first; new_first->prev = head; } /** * list_cut_position - cut a list into two * @list: a new list to add all removed entries * @head: a list with entries * @entry: an entry within head, could be the head itself * and if so we won't cut the list * * This helper moves the initial part of @head, up to and * including @entry, from @head to @list. You should * pass on @entry an element you know is on @head. @list * should be an empty list or a list you do not care about * losing its data. * */ static inline void list_cut_position(struct list_head *list, struct list_head *head, struct list_head *entry) { if (list_empty(head)) return; if (list_is_singular(head) && (head->next != entry && head != entry)) return; if (entry == head) INIT_LIST_HEAD(list); else __list_cut_position(list, head, entry); } static inline void __list_splice(const struct list_head *list, struct list_head *prev, struct list_head *next) { struct list_head *first = list->next; struct list_head *last = list->prev; first->prev = prev; prev->next = first; last->next = next; next->prev = last; } /** * list_splice - join two lists, this is designed for stacks * @list: the new list to add. * @head: the place to add it in the first list. */ static inline void list_splice(const struct list_head *list, struct list_head *head) { if (!list_empty(list)) __list_splice(list, head, head->next); } /** * list_splice_tail - join two lists, each list being a queue * @list: the new list to add. * @head: the place to add it in the first list. */ static inline void list_splice_tail(struct list_head *list, struct list_head *head) { if (!list_empty(list)) __list_splice(list, head->prev, head); } /** * list_splice_init - join two lists and reinitialise the emptied list. * @list: the new list to add. * @head: the place to add it in the first list. * * The list at @list is reinitialised */ static inline void list_splice_init(struct list_head *list, struct list_head *head) { if (!list_empty(list)) { __list_splice(list, head, head->next); INIT_LIST_HEAD(list); } } /** * list_splice_tail_init - join two lists and reinitialise the emptied list * @list: the new list to add. * @head: the place to add it in the first list. * * Each of the lists is a queue. * The list at @list is reinitialised */ static inline void list_splice_tail_init(struct list_head *list, struct list_head *head) { if (!list_empty(list)) { __list_splice(list, head->prev, head); INIT_LIST_HEAD(list); } } /** * list_entry - get the struct for this entry * @ptr: the &struct list_head pointer. * @type: the type of the struct this is embedded in. * @member: the name of the list_head within the struct. */ #define list_entry(ptr, type, member) container_of(ptr, type, member) /** * list_first_entry - get the first element from a list * @ptr: the list head to take the element from. * @type: the type of the struct this is embedded in. * @member: the name of the list_head within the struct. * * Note, that list is expected to be not empty. */ #define list_first_entry(ptr, type, member) \ list_entry((ptr)->next, type, member) /** * list_last_entry - get the last element from a list * @ptr: the list head to take the element from. * @type: the type of the struct this is embedded in. * @member: the name of the list_head within the struct. * * Note, that list is expected to be not empty. */ #define list_last_entry(ptr, type, member) list_entry((ptr)->prev, type, member) /** * list_first_entry_or_null - get the first element from a list * @ptr: the list head to take the element from. * @type: the type of the struct this is embedded in. * @member: the name of the list_head within the struct. * * Note that if the list is empty, it returns NULL. */ #define list_first_entry_or_null(ptr, type, member) \ (!list_empty(ptr) ? list_first_entry(ptr, type, member) : NULL) /** * list_next_entry - get the next element in list * @pos: the type * to cursor * @member: the name of the list_head within the struct. */ #define list_next_entry(pos, member) \ list_entry((pos)->member.next, __typeof__(*(pos)), member) /** * list_prev_entry - get the prev element in list * @pos: the type * to cursor * @member: the name of the list_head within the struct. */ #define list_prev_entry(pos, member) \ list_entry((pos)->member.prev, __typeof__(*(pos)), member) /** * list_for_each - iterate over a list * @pos: the &struct list_head to use as a loop cursor. * @head: the head for your list. */ #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) /** * list_for_each_prev - iterate over a list backwards * @pos: the &struct list_head to use as a loop cursor. * @head: the head for your list. */ #define list_for_each_prev(pos, head) \ for (pos = (head)->prev; pos != (head); pos = pos->prev) /** * list_for_each_safe - iterate over a list safe against removal of list entry * @pos: the &struct list_head to use as a loop cursor. * @n: another &struct list_head to use as temporary storage * @head: the head for your list. */ #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) /** * list_for_each_prev_safe - iterate over a list backwards safe against removal * of list entry * @pos: the &struct list_head to use as a loop cursor. * @n: another &struct list_head to use as temporary storage * @head: the head for your list. */ #define list_for_each_prev_safe(pos, n, head) \ for (pos = (head)->prev, n = pos->prev; pos != (head); \ pos = n, n = pos->prev) /** * list_for_each_entry - iterate over list of given type * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the list_head within the struct. */ #define list_for_each_entry(pos, head, member) \ for (pos = list_first_entry(head, __typeof__(*pos), member); \ &pos->member != (head); pos = list_next_entry(pos, member)) /** * list_for_each_entry_reverse - iterate backwards over list of given type. * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the list_head within the struct. */ #define list_for_each_entry_reverse(pos, head, member) \ for (pos = list_last_entry(head, __typeof__(*pos), member); \ &pos->member != (head); pos = list_prev_entry(pos, member)) /** * list_prepare_entry - prepare a pos entry for use in * list_for_each_entry_continue() * @pos: the type * to use as a start point * @head: the head of the list * @member: the name of the list_head within the struct. * * Prepares a pos entry for use as a start point in * list_for_each_entry_continue(). */ #define list_prepare_entry(pos, head, member) \ ((pos) ?: list_entry(head, __typeof__(*pos), member)) /** * list_for_each_entry_continue - continue iteration over list of given type * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the list_head within the struct. * * Continue to iterate over list of given type, continuing after * the current position. */ #define list_for_each_entry_continue(pos, head, member) \ for (pos = list_next_entry(pos, member); &pos->member != (head); \ pos = list_next_entry(pos, member)) /** * list_for_each_entry_continue_reverse - iterate backwards from the given point * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the list_head within the struct. * * Start to iterate over list of given type backwards, continuing after * the current position. */ #define list_for_each_entry_continue_reverse(pos, head, member) \ for (pos = list_prev_entry(pos, member); &pos->member != (head); \ pos = list_prev_entry(pos, member)) /** * list_for_each_entry_from - iterate over list of given type from the current * point * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the list_head within the struct. * * Iterate over list of given type, continuing from current position. */ #define list_for_each_entry_from(pos, head, member) \ for (; &pos->member != (head); pos = list_next_entry(pos, member)) /** * list_for_each_entry_safe - iterate over list of given type safe against * removal of list entry * @pos: the type * to use as a loop cursor. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_head within the struct. */ #define list_for_each_entry_safe(pos, n, head, member) \ for (pos = list_first_entry(head, __typeof__(*pos), member), \ n = list_next_entry(pos, member); \ &pos->member != (head); pos = n, n = list_next_entry(n, member)) /** * list_for_each_entry_safe_continue - continue list iteration safe against * removal * @pos: the type * to use as a loop cursor. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_head within the struct. * * Iterate over list of given type, continuing after current point, * safe against removal of list entry. */ #define list_for_each_entry_safe_continue(pos, n, head, member) \ for (pos = list_next_entry(pos, member), n = list_next_entry(pos, member); \ &pos->member != (head); pos = n, n = list_next_entry(n, member)) /** * list_for_each_entry_safe_from - iterate over list from current point safe * against removal * @pos: the type * to use as a loop cursor. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_head within the struct. * * Iterate over list of given type from current point, safe against * removal of list entry. */ #define list_for_each_entry_safe_from(pos, n, head, member) \ for (n = list_next_entry(pos, member); &pos->member != (head); \ pos = n, n = list_next_entry(n, member)) /** * list_for_each_entry_safe_reverse - iterate backwards over list safe against * removal * @pos: the type * to use as a loop cursor. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_head within the struct. * * Iterate backwards over list of given type, safe against removal * of list entry. */ #define list_for_each_entry_safe_reverse(pos, n, head, member) \ for (pos = list_last_entry(head, __typeof__(*pos), member), \ n = list_prev_entry(pos, member); \ &pos->member != (head); pos = n, n = list_prev_entry(n, member)) /** * list_safe_reset_next - reset a stale list_for_each_entry_safe loop * @pos: the loop cursor used in the list_for_each_entry_safe loop * @n: temporary storage used in list_for_each_entry_safe * @member: the name of the list_head within the struct. * * list_safe_reset_next is not safe to use in general if the list may be * modified concurrently (eg. the lock is dropped in the loop body). An * exception to this is if the cursor element (pos) is pinned in the list, * and list_safe_reset_next is called after re-taking the lock and before * completing the current iteration of the loop body. */ #define list_safe_reset_next(pos, n, member) n = list_next_entry(pos, member) /* * Double linked lists with a single pointer list head. * Mostly useful for hash tables where the two pointer list head is * too wasteful. * You lose the ability to access the tail in O(1). */ #define HLIST_HEAD_INIT \ { \ .first = NULL \ } #define HLIST_HEAD(name) struct hlist_head name = {.first = NULL} #define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL) static inline void INIT_HLIST_NODE(struct hlist_node *h) { h->next = NULL; h->pprev = NULL; } static inline int hlist_unhashed(const struct hlist_node *h) { return !h->pprev; } static inline int hlist_empty(const struct hlist_head *h) { return !READ_ONCE(h->first); } static inline void __hlist_del(struct hlist_node *n) { struct hlist_node *next = n->next; struct hlist_node **pprev = n->pprev; WRITE_ONCE(*pprev, next); if (next) next->pprev = pprev; } static inline void hlist_del(struct hlist_node *n) { __hlist_del(n); n->next = LIST_POISON1; n->pprev = LIST_POISON2; } static inline void hlist_del_init(struct hlist_node *n) { if (!hlist_unhashed(n)) { __hlist_del(n); INIT_HLIST_NODE(n); } } static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) { struct hlist_node *first = h->first; n->next = first; if (first) first->pprev = &n->next; WRITE_ONCE(h->first, n); n->pprev = &h->first; } /* next must be != NULL */ static inline void hlist_add_before(struct hlist_node *n, struct hlist_node *next) { n->pprev = next->pprev; n->next = next; next->pprev = &n->next; WRITE_ONCE(*(n->pprev), n); } static inline void hlist_add_behind(struct hlist_node *n, struct hlist_node *prev) { n->next = prev->next; WRITE_ONCE(prev->next, n); n->pprev = &prev->next; if (n->next) n->next->pprev = &n->next; } /* after that we'll appear to be on some hlist and hlist_del will work */ static inline void hlist_add_fake(struct hlist_node *n) { n->pprev = &n->next; } static inline bool hlist_fake(struct hlist_node *h) { return h->pprev == &h->next; } /* * Move a list from one list head to another. Fixup the pprev * reference of the first entry if it exists. */ static inline void hlist_move_list(struct hlist_head *old, struct hlist_head *new) { new->first = old->first; if (new->first) new->first->pprev = &new->first; old->first = NULL; } #define hlist_entry(ptr, type, member) container_of(ptr, type, member) #define hlist_for_each(pos, head) \ for (pos = (head)->first; pos; pos = pos->next) #define hlist_for_each_safe(pos, n, head) \ for (pos = (head)->first; pos && ({ \ n = pos->next; \ 1; \ }); \ pos = n) #define hlist_entry_safe(ptr, type, member) \ ({ \ __typeof__(ptr) ____ptr = (ptr); \ ____ptr ? hlist_entry(____ptr, type, member) : NULL; \ }) /** * hlist_for_each_entry - iterate over list of given type * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the hlist_node within the struct. */ #define hlist_for_each_entry(pos, head, member) \ for (pos = hlist_entry_safe((head)->first, __typeof__(*(pos)), member); \ pos; pos = hlist_entry_safe((pos)->member.next, __typeof__(*(pos)), \ member)) /** * hlist_for_each_entry_continue - iterate over a hlist continuing after current * point * @pos: the type * to use as a loop cursor. * @member: the name of the hlist_node within the struct. */ #define hlist_for_each_entry_continue(pos, member) \ for (pos = \ hlist_entry_safe((pos)->member.next, __typeof__(*(pos)), member); \ pos; pos = hlist_entry_safe((pos)->member.next, __typeof__(*(pos)), \ member)) /** * hlist_for_each_entry_from - iterate over a hlist continuing from current * point * @pos: the type * to use as a loop cursor. * @member: the name of the hlist_node within the struct. */ #define hlist_for_each_entry_from(pos, member) \ for (; pos; pos = hlist_entry_safe((pos)->member.next, __typeof__(*(pos)), \ member)) /** * hlist_for_each_entry_safe - iterate over list of given type safe against * removal of list entry * @pos: the type * to use as a loop cursor. * @n: another &struct hlist_node to use as temporary storage * @head: the head for your list. * @member: the name of the hlist_node within the struct. */ #define hlist_for_each_entry_safe(pos, n, head, member) \ for (pos = hlist_entry_safe((head)->first, __typeof__(*pos), member); \ pos && ({ \ n = pos->member.next; \ 1; \ }); \ pos = hlist_entry_safe(n, __typeof__(*pos), member)) #endif ================================================ FILE: include/linux/poison.h ================================================ #ifndef LINUX_POISON_H #define LINUX_POISON_H /* * These are non-NULL pointers that will result in page faults * under normal circumstances, used to verify that nobody uses * non-initialized list entries. * Make sure the values raise faults when these addresses are * read/written. */ #define LIST_POISON1 ((void *) 0x100) #define LIST_POISON2 ((void *) 0x200) #endif /* !LINUX_POISON_H */ ================================================ FILE: include/linux/stddef.h ================================================ #ifndef _LINUX_STDDEF_H #define _LINUX_STDDEF_H #include /** * offsetofend(TYPE, MEMBER) * * @TYPE: The type of the structure * @MEMBER: The member within the structure to get the end offset of */ #define offsetofend(TYPE, MEMBER) \ (offsetof(TYPE, MEMBER) + sizeof(((TYPE *) 0)->MEMBER)) #endif ================================================ FILE: include/linux/types.h ================================================ #ifndef LINUX_TYPES_H #define LINUX_TYPES_H #define S32_MAX 2147483647 typedef unsigned int u32; typedef int s32; typedef unsigned short u16; typedef short s16; typedef unsigned char u8; typedef char s8; typedef unsigned int size_t; struct list_head { struct list_head *next, *prev; }; struct hlist_head { struct hlist_node *first; }; struct hlist_node { struct hlist_node *next, **pprev; }; typedef struct { volatile s32 val; } atomic_t; #endif /* !LINUX_TYPES_H */ ================================================ FILE: include/piko/arpa/inet.h ================================================ #ifndef LIBC_ARPA_INET_H #define LIBC_ARPA_INET_H #include #include "platform.h" static inline uint32_t htonl(uint32_t hostlong) { return __REV(hostlong); } static inline uint16_t htons(uint16_t hostshort) { return __REV16(hostshort); } static inline uint32_t ntohl(uint32_t netlong) { return __REV(netlong); } static inline uint16_t ntohs(uint16_t netshort) { return __REV16(netshort); } #endif /* !LIBC_ARPA_INET_H */ ================================================ FILE: include/piko/dirent.h ================================================ #ifndef _LIBPIKO_DIRENT_H #define _LIBPIKO_DIRENT_H #include #define NAME_MAX 32 // FIXME: Include typedef void DIR; struct dirent { ino_t d_ino; /* inode number */ char d_name[NAME_MAX]; /* filename */ }; DIR *opendir(const char *dirname); int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result); int closedir(DIR *dirp); #endif /* !_LIBPIKO_DIRENT_H */ ================================================ FILE: include/piko/signal.h ================================================ #ifndef LIBC_SIGNAL_H #define LIBC_SIGNAL_H #include #include "linux/list.h" typedef struct { void *ss_sp; /* Base address of stack */ int ss_flags; /* Flags */ size_t ss_size; /* Number of bytes in stack */ } stack_t; /* sigevent - structure for notification from asynchronous routines */ union sigval { /* Data passed with notification */ int sival_int; /* Integer value */ void *sival_ptr; /* Pointer value */ }; struct sigevent { int sigev_notify; /* Notification method */ int sigev_signo; /* Notification signal */ /* Data passed with notification */ union sigval sigev_value; /* Function used for thread notification (SIGEV_THREAD) */ void (*sigev_notify_function)(union sigval); /* Attributes for notification thread (SIGEV_THREAD) */ void *sigev_notify_attributes; /* ID of thread to signal (SIGEV_THREAD_ID) */ pid_t sigev_notify_thread_id; }; /* sigaction - used to change the action taken by a process on receipt of a specific signal */ typedef struct { int si_signo; /* int si_code; */ union sigval si_value; /* int si_errno; */ pid_t si_pid; /* uid_t si_uid; */ /* void *si_addr; */ /* int si_status; */ /* int si_band; */ } siginfo_t; typedef int sigset_t; struct sigaction { union { void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *, void *); }; sigset_t sa_mask; int sa_flags; /* Storage for kernel fields. Not compliant with the POSIX specs. */ struct list_head sa_list; int sa_signo; }; #define SA_SIGINFO (1 << 0) #define SIGKILL 9 /* Kill (can't be caught or ignored) (POSIX) */ #define SIGUSR1 10 /* User defined signal 1 (POSIX) */ #define SIGUSR2 12 /* User defined signal 2 (POSIX) */ #define SIGSTOP 19 /* Stop executing(can't be caught or ignored) (POSIX) */ int sigaction(int sig, const struct sigaction *restrict act, struct sigaction *restrict oact); int raise(int sig); #endif /* !LIBC_SIGNAL_H */ ================================================ FILE: include/piko/sys/mman.h ================================================ #ifndef _SYS_MMAN_H #define _SYS_MMAN_H #include #define PROT_NONE 0x00 /* page can not be accessed */ #define PROT_READ 0x01 /* page can be read */ #define PROT_WRITE 0x02 /* page can be written */ #define PROT_EXEC 0x04 /* page can be executed */ #define MAP_FAILED ((void *) -1) #define MAP_ANONYMOUS 0x01 /* don't use a file */ #define MAP_UNINITIALIZED 0x02 /* anonymous memory can be uninitialized */ void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); int munmap(void *addr, size_t length); #endif /* !_SYS_MMAN_H */ ================================================ FILE: include/piko/sys/mount.h ================================================ #ifndef _SYS_MOUNT_H #define _SYS_MOUNT_H int mount(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data); #endif /* !_SYS_MOUNT_H */ ================================================ FILE: include/piko/sys/resource.h ================================================ #ifndef SYS_RESOURCE_H #define SYS_RESOURCE_H /* http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_resource.h.html */ /* Unsigned integer type used for limit values. */ typedef unsigned int rlim_t; struct rlimit { rlim_t rlim_cur; /* Soft limit */ rlim_t rlim_max; /* Hard limit (ceiling for rlim_cur) */ }; #define RLIMIT_STACK 0 #ifdef __KERNEL__ int sys_getrlimit(int resource, struct rlimit *rlim); int sys_setrlimit(int resource, const struct rlimit *rlim); #else /* !__KERNEL__ */ int getrlimit(int resource, struct rlimit *rlim); int setrlimit(int resource, const struct rlimit *rlim); #endif /* __KERNEL__ */ #endif /* !SYS_RESOURCE_H */ ================================================ FILE: include/platform/compiler.h ================================================ #ifndef _PLATFORM_COMPILER_H #define _PLATFORM_COMPILER_H #define PLAT_EVAL(macro) \ ({ \ void __wrapper__(void) {macro;} \ __wrapper__; \ }) #endif /* !_PLATFORM_COMPILER_H */ ================================================ FILE: include/version.template.h ================================================ #ifndef VERSION_H #define VERSION_H #define VER_MAJOR "0" #define VER_MINOR "0" #define VER_MICRO "0" #define VER_SLUG "piko-" VER_MAJOR "." VER_MINOR "." VER_MICRO #endif /* !VERSION_H */ ================================================ FILE: kernel/cond.c ================================================ #include #include #include #include #include "linux/list.h" static LIST_HEAD(cond_head); static struct thread_info *find_other_thread(pthread_cond_t *cond) { struct thread_info *other; list_for_each_entry(other, &cond_head, ti_q) { if (other->ti_private == cond) return other; } return NULL; } int sys_pthread_cond_wait(pthread_cond_t *cond, kernel_mutex_t *mutex) { CURRENT_THREAD_INFO(curr_thread); curr_thread->ti_private = cond; curr_thread->ti_state = THREAD_STATE_BLOCKED; list_add_tail(&curr_thread->ti_q, &cond_head); sys_pthread_mutex_unlock(mutex); /* contend for the lock */ sys_pthread_mutex_lock(mutex); return 0; } int sys_pthread_cond_signal(pthread_cond_t *cond) { struct thread_info *other; other = find_other_thread(cond); if (!other) return 0; list_del(&other->ti_q); sched_enqueue(other); CURRENT_THREAD_INFO(curr_thread); if (other->ti_priority >= curr_thread->ti_priority) { sched_enqueue(curr_thread); sched_elect(SCHED_OPT_NONE); } return 0; } ================================================ FILE: kernel/config.c ================================================ #include enum sc_varname { PAGESIZE, }; struct sys_param { const char *name; long value; }; struct sys_param sys_params[] = { {"pagesize", 2048}, {"clock_tick", 0}, // FIXME: set by systick_init() }; long sys_sysconf(int name) { switch (name) { case PAGESIZE: return sys_params[0].value; case _SC_CLK_TCK: return sys_params[1].value; } return -1; } ================================================ FILE: kernel/faults.c ================================================ #include #include #include "kernel.h" #include "utils.h" #include "platform.h" void fault_enter(const char *s) { printk("\n-------------------------------------------------------------\n"); printk(" #%s\n\n", s); } void fault_exit(void) { printk("-------------------------------------------------------------\n"); __platform_halt(); } void hardfault(struct kernel_context_regs *noscratch, struct thread_context_regs *scratch, u32 exc_return) { fault_enter("HardFault"); dump_frame(noscratch, scratch, exc_return); fault_exit(); } ================================================ FILE: kernel/fs/fs.c ================================================ #include #include #include #include #include #include #include #include #include #include struct file *fd_to_file(int fd) { CURRENT_TASK_INFO(curr_task); return &curr_task->filetable[fd]; } static int getfd(void) { int fd; CURRENT_TASK_INFO(curr_task); fd = find_first_zero_bit(&curr_task->filemap, FILE_MAX); if (fd == FILE_MAX) return -1; bitmap_set_bit(&curr_task->filemap, fd); return fd; } static void releasefd(int fd) { CURRENT_TASK_INFO(curr_task); bitmap_clear_bit(&curr_task->filemap, fd); } int release_dentries(struct dentry *dentry) { struct dentry *parent; for (; dentry != root_dentry(); dentry = parent) { if (!dentry->d_count) return -1; if (--dentry->d_count) break; parent = dentry->d_parent; vfs_release(dentry); if (!dentry->d_count) vfs_delete(dentry); } return 0; } static int path_head(char *buf, const char *pathname) { int i, i0 = 0; if (pathname[0] == '\0') return -1; if (pathname[0] == '/') i0++; for (i = i0; i < NAME_MAX && pathname[i] != '/' && pathname[i] != '\0'; i++) ; strncpy(buf, &pathname[i0], i - i0); buf[i - i0] = '\0'; return i; } struct inode *inode_from_pathname( const char *pathname /* , struct dentry *from */) { struct inode *inode = root_inode(); struct dentry *dentry = root_dentry(); struct dentry target; /* remove the trailing slash, relative path is not supported */ pathname++; for (size_t i = 0; i < strlen(pathname);) { i += path_head(target.d_name, &pathname[i]); dentry = vfs_lookup(inode, &target); if (!dentry) return NULL; inode = dentry->d_inode; } return inode; } int sys_open(const char *pathname, int flags) { struct inode *inode = root_inode(); struct dentry *dentry = root_dentry(); struct dentry *parent = dentry; struct dentry *target; /* remove the trailing slash, relative path is not supported */ pathname++; for (size_t i = 0; i < strlen(pathname);) { target = malloc(sizeof(struct dentry)); if (!target) { errno = ENOMEM; return 0; } target->d_count = 1; target->d_parent = parent; i += path_head(target->d_name, &pathname[i]); dentry = vfs_lookup(inode, target); if (!dentry) { errno = ENOENT; release_dentries(target->d_parent); return 0; } inode = dentry->d_inode; parent = dentry; } /* opendir() redirects to sys_open() */ if ((flags & O_DIRECTORY) && !S_ISDIR(inode->i_mode)) { errno = ENOTDIR; return 0; } int fd = getfd(); if (fd < 0) { errno = EBADF; return 0; } struct file *file = fd_to_file(fd); file->f_dentry = dentry; file->f_op = dentry->d_inode->i_fop; file->f_pos = 0; if (file->f_op->open) file->f_op->open(inode, file); return fd; } ssize_t sys_read(int fd, void *buf, size_t count) { struct file *file = fd_to_file(fd); if (count) count = file->f_op->read(file, buf, count, file->f_pos); file->f_pos += count; return count; } ssize_t sys_write(int fd, void *buf, size_t count) { struct file *file = fd_to_file(fd); off_t offset = file->f_pos; count = file->f_op->write(file, buf, count, &offset); file->f_pos += count; return count; } off_t sys_lseek(int fd, off_t offset, int whence) { struct file *file = fd_to_file(fd); off_t size = file->f_dentry->d_inode->i_size; if (file->f_op->lseek) file->f_op->lseek(file, offset, whence); switch (whence) { case SEEK_SET: if (offset) offset = size % offset; break; case SEEK_CUR: offset = (file->f_pos + offset) % size; break; case SEEK_END: offset = (size + offset) % size; break; default: return -1; } file->f_pos = offset; return 0; } int sys_close(int fd) { struct file *file = fd_to_file(fd); release_dentries(file->f_dentry); releasefd(fd); return 0; } int sys_stat(const char *pathname, struct stat *buf) { struct inode *inode = inode_from_pathname(pathname); if (!inode) { errno = ENOENT; return -1; } buf->st_ino = inode->i_ino; buf->st_mode = inode->i_mode; buf->st_size = inode->i_size; return 0; } int sys_mount(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data) { if (!strcmp("romfs", filesystemtype)) return romfs_mount(source, target, filesystemtype, mountflags, data); return -1; } ================================================ FILE: kernel/fs/readdir.c ================================================ #include #include #include #include #include "linux/list.h" static int fillonedir(struct dir_context *ctx, const char *name, int namlen, __unused off_t offset, unsigned int ino, __unused unsigned int d_type) { struct readdir_callback *buf = container_of(ctx, struct readdir_callback, ctx); struct piko_dirent *dirent = buf->dirent; dirent->d_ino = ino; strncpy(dirent->d_name, name, namlen); dirent->d_name[namlen] = '\0'; return 0; } int sys_readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result) { struct file *file = fd_to_file((int) dirp); struct readdir_callback buf = { .ctx = {.actor = fillonedir, .pos = 0}, .dirent = (struct piko_dirent *) entry, }; if (vfs_iterate(file, &buf.ctx)) *result = NULL; else *result = entry; return 0; } ================================================ FILE: kernel/fs/vfs.c ================================================ #include #include #include #include #include #include "linux/list.h" int vfs_iterate(struct file *file, struct dir_context *ctx) { if (!file->f_op->iterate) return -1; return file->f_op->iterate(file, ctx); } struct dentry *vfs_lookup(struct inode *dir, struct dentry *target) { if (!dir->i_op->lookup) return NULL; if (!S_ISDIR(dir->i_mode)) return NULL; return dir->i_op->lookup(dir, target); } int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) { if (!dir->i_op->link) return -1; return dir->i_op->link(old_dentry, dir, dentry); } int vfs_delete(struct dentry *dentry) { if (!dentry->d_op->delete) return -1; return dentry->d_op->delete (dentry); } void vfs_release(struct dentry *dentry) { if (dentry->d_op->release) return dentry->d_op->release(dentry); } int vfs_mmap(struct file *file, off_t offset, void **addr) { if (!file->f_op->mmap) return -1; return file->f_op->mmap(file, offset, addr); } ================================================ FILE: kernel/irq.c ================================================ #include #include #include #include #define IRQ_MAX 64 irq_handler_t irq_handler[IRQ_MAX]; // clang-format off static struct irq_desc irq_desc[NR_IRQS] = { [0 ... NR_IRQS - 1] = { .irq_data = {.state = 0x0}, .action = NULL, .status = 0x0, } }; // clang-format on static struct irq_desc *irq_to_desc(unsigned int irq) { if (irq < NR_IRQS) return irq_desc + irq; return NULL; } int request_irq(unsigned int irq, irq_handler_t hdlr) { struct irqaction *action; struct irq_desc *desc; desc = irq_to_desc(irq); if (desc && (desc->status & IRQ_NOREQUEST)) { action = (struct irqaction *) malloc(sizeof(struct irqaction)); if (!action) { // WARN_ON(!action, "malloc failed\n"); goto fail; } action->irq = irq; action->handler = hdlr; /* install to irq_desc */ desc->action = action; irq_handler[irq] = hdlr; return 0; } else fail: return -1; } int free_irq(unsigned int irq) { struct irq_desc *desc; desc = irq_to_desc(irq); if (desc && !(desc->status & IRQ_NOREQUEST)) { free(desc->action); desc->status |= IRQ_NOREQUEST; irq_handler[irq] = NULL; return 0; } return -1; } void early_irq_init(void) { for (int irq = 0; irq < NR_IRQS; irq++) irq_desc[irq].irq_data.state = IRQD_IRQ_DISABLED; } void init_IRQ(void) { for (int irq = 0; irq < NR_IRQS; irq++) irq_desc[irq].status = IRQ_NOREQUEST; } ================================================ FILE: kernel/main.c ================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "platform.h" extern char __early_stack_start__; extern char __early_stack_end__; extern char __text_start__; extern char __text_end__; extern char __rodata_start__; extern char __rodata_end__; extern char __data_start__; extern char __data_end__; extern char __bss_start__; extern char __bss_end__; extern char __pgmem_start__; extern char __pgmem_end__; extern char __pgmem_size__; extern char __heap_start__; extern char __heap_end__; extern char __heap_size__; void __do_idle(void); void *do_idle(void *); void mtdram_init(void); void __printk_init(void); int minishell(void *options); void memdev_init(void); void kernel_heap_init(void *heap_start, size_t heap_size); void early_irq_init(void); void init_IRQ(void); struct task_info idle_task; struct task_info main_task; void print_version(void) { char buf[] = {0, 0}; int fd = open("/proc/version", 0); while (read(fd, &buf, 1)) printk("%s", buf); close(fd); printk("\n"); } void __weak *main(__unused void *arg) { print_version(); minishell(NULL); return 0; } struct thread_info *thread_idle; /* Cortex-M3/4 system initialization */ static void v7m_init(void) { /* enable UsageFault, BusFault, MemManage faults */ SCB->SHCSR |= (SCB_SHCSR_USGFAULTENA_Msk | SCB_SHCSR_BUSFAULTENA_Msk | SCB_SHCSR_MEMFAULTENA_Msk); /* Configure the System Control Register to ensure 8-byte stack alignment */ SCB->CCR |= SCB_CCR_STKALIGN_Msk; // NVIC_SetPriority(DebugMonitor_IRQn, 0x0, 0); NVIC_SetPriority(MemoryManagement_IRQn, 0x1); NVIC_SetPriority(BusFault_IRQn, 0x1); NVIC_SetPriority(UsageFault_IRQn, 0x1); NVIC_SetPriority(SysTick_IRQn, 0x3); /* Priority 0xF - debug_uart */ NVIC_SetPriority(SVCall_IRQn, 0xF); NVIC_SetPriority(PendSV_IRQn, 0xF); /* follow the architectural requirements */ __DSB(); } void print_linker_sections(void) { printk("Memory map:\n"); printk(" .text = %08x--%08x %6d Bytes\n", &__text_start__, &__text_end__, &__text_end__ - &__text_start__); printk(" .rodata = %08x--%08x %6d Bytes\n", &__rodata_start__, &__rodata_end__, &__rodata_end__ - &__rodata_start__); printk(" .data = %08x--%08x %6d Bytes\n", &__data_start__, &__data_end__, &__data_end__ - &__data_start__); printk(" .bss = %08x--%08x %6d Bytes\n", &__bss_start__, &__bss_end__, &__bss_end__ - &__bss_start__); printk(" .heap = %08x--%08x %6d Bytes\n", &__heap_start__, &__heap_end__, &__heap_end__ - &__heap_start__); printk(" .pgmem = %08x--%08x %6d Bytes\n", &__pgmem_start__, &__pgmem_end__, &__pgmem_end__ - &__pgmem_start__); } struct thread_info *start_kernel(void) { v7m_init(); early_irq_init(); init_IRQ(); /* TODO: Early console */ __printk_init(); /* initialize the kernel's malloc */ kernel_heap_init(&__heap_start__, (size_t) &__heap_size__); print_linker_sections(); /* initialize the physical memory allocator */ show_page_bitmap(); // init_pages(); kmem_cache_init(); /* initialize the scheduler internels */ sched_init(); /* select giving scheduling policy */ sched_select(SCHED_CLASS_BITMAP); /* idle_thread is not added to the runqueue */ task_init(&idle_task); thread_idle = thread_create(do_idle, NULL, THREAD_PRIV_SUPERVISOR, 1024, &idle_task); if (!thread_idle) { printk("[!] Could not create system idle thread.\n"); return NULL; } printk("Created idle_thread at <%p>\n", thread_idle); /* The main_thread is the user's entry-point to the system. It is not * added to the runqueue because it has been implicitly "elected" when * start_kernel() returns. */ task_init(&main_task); struct thread_info *thread_main = thread_create(main, NULL, THREAD_PRIV_USER, 1024, &main_task); if (!thread_main) { printk("[!] Could not create user main thread.\n"); return NULL; } printk("Created main_thread at <%p> with priority=%d\n", thread_main, thread_main->ti_priority); /* Reclaim the early-stack physical memory. In the current context, no * page allocation after this point are allowed. */ printk("Reclaim early stack's physical memory (%d Bytes, order=%d).\n", &__early_stack_start__ - &__early_stack_end__, size_to_page_order(2048)); free_pages((unsigned long) &__early_stack_end__, size_to_page_order(2048)); tmpfs_init(); proc_init(); memdev_init(); mtdram_init(); /* create a test mtdram device */ /* do the platform-specific inits */ __platform_init(); init_softirq(); /* create /dev/ttyS0 */ serial_init(); printk("Kernel bootstrap done.\n--\n"); return thread_main; } void *do_idle(__unused void *arg) { for (;;) __do_idle(); } ================================================ FILE: kernel/mm/mm.c ================================================ #include #include #include #include #include #include #include #include #define M_ISANON(f) (((f) &MAP_ANONYMOUS) == MAP_ANONYMOUS) #define M_ISUNINIT(f) (((f) &MAP_UNINITIALIZED) == MAP_UNINITIALIZED) static void *map_anon(void *addr, size_t length, __unused int prot, int flags) { int order; order = size_to_page_order(length); addr = alloc_pages(order); if (!addr) { printk("mmap: ENOMEM\n"); errno = ENOMEM; return MAP_FAILED; } if (!M_ISUNINIT(flags)) memset(addr, 0, length); return addr; } static void *map_file(__unused size_t length, __unused int prot, __unused int flags, int fd, off_t offset) { void *addr; struct file *file; file = fd_to_file(fd); if (!file) { printk("mmap: BADF\n"); errno = EBADF; return MAP_FAILED; } if (!S_ISREG(file->f_dentry->d_inode->i_mode)) { printk("mmap: ACCESS\n"); errno = EACCES; return MAP_FAILED; } if (vfs_mmap(file, offset, &addr)) { printk("mmap: failed in romfs_map\n"); for (;;) ; return MAP_FAILED; } return addr; } void *sys_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) { if (!length) { errno = EINVAL; printk("mmap: length is 0\n"); return MAP_FAILED; } if (prot & PROT_NONE) { errno = EACCES; printk("mmap: PROT_NONE\n"); return MAP_FAILED; } if (M_ISANON(flags)) addr = map_anon(addr, length, prot, flags); else addr = map_file(length, prot, flags, fd, offset); return addr; } int sys_munmap(__unused void *addr, __unused size_t length) { /* Closing the file descriptor does not unmap the region. */ return 0; } ================================================ FILE: kernel/mm/page.c ================================================ #include #include #include #include #include #include /* .pgmem section is 32KB: * - 128 pages of 256B is a 16 bytes map * - 64 pages of 512B is a 8 bytes map * - 32 pages of 1KB is a 4 bytes map * - 16 pages of 2KB is a 2 bytes map * * Last page is reserved by the early stack, and freed after system init. */ /* 0 = allocated/undefined, 1 = free */ static unsigned long *const page_bitmap[] = { (unsigned long[]){0, 0, 0, 0}, (unsigned long[]){0, 0}, (unsigned long[]){0}, (unsigned long[]){0x7fff}}; static const unsigned long page_bitmap_sz[] = {128, 64, 32, 16}; long size_to_page_order(unsigned long size) { if (size <= 256) return 0; if (size <= 512) return 1; if (size <= 1024) return 2; if (size <= 2048) return 3; return -1; } static long find_first_free_page(unsigned long order) { unsigned long page_idx = find_first_bit(page_bitmap[order], page_bitmap_sz[order]); if (page_idx >= page_bitmap_sz[order]) return -1; return page_idx; } static void split_first_free_page(unsigned long order) { unsigned long page_idx = find_first_bit(page_bitmap[order], page_bitmap_sz[order]); bitmap_clear_bit(page_bitmap[order], page_idx); bitmap_set_bit(page_bitmap[order - 1], page_idx * 2); bitmap_set_bit(page_bitmap[order - 1], page_idx * 2 + 1); } extern char __pgmem_start__; static void *page_idx_to_addr(unsigned long idx, unsigned long order) { return &__pgmem_start__ + idx * (1 << (order + ilog2(MIN_PAGE_SIZE))); } void *alloc_pages(unsigned long order) { unsigned long page_idx, o; for (o = order; (o <= MAX_PAGE_ORDER) && (find_first_free_page(o) < 0); o++) ; if (o > MAX_PAGE_ORDER) return NULL; for (; o > order; o--) split_first_free_page(o); page_idx = find_first_bit(page_bitmap[order], page_bitmap_sz[order]); bitmap_clear_bit(page_bitmap[order], page_idx); /* printk("Returning address %p\n", page_idx_to_addr(page_idx, order)); */ return page_idx_to_addr(page_idx, order); } static inline unsigned long addr_to_page_idx(unsigned long addr, unsigned long order) { // FIXME: multiple zone to allocate from? use the zone's base address // instead return (addr - (unsigned long) &__pgmem_start__) >> (order + ilog2(MIN_PAGE_SIZE)); } static inline unsigned long get_buddy_index(unsigned long idx) { return idx % 2 ? idx - 1 : idx + 1; } /* try to coalesce free buddies */ void free_pages(unsigned long addr, unsigned long order) { unsigned long page_idx, buddy_idx, mask; for (; order < MAX_PAGE_ORDER; order++) { page_idx = addr_to_page_idx(addr, order); buddy_idx = get_buddy_index(page_idx); if (bitmap_get_bit(page_bitmap[order], buddy_idx)) { bitmap_clear_bit(page_bitmap[order], buddy_idx); mask = ~((1 << (order + 1 + ilog2(MIN_PAGE_SIZE))) - 1); addr &= mask; } else { bitmap_set_bit(page_bitmap[order], page_idx); return; } } bitmap_set_bit(page_bitmap[order], addr_to_page_idx(addr, order)); } void show_page_bitmap(void) { printk("Order Bitmap\n"); for (int i = 0; i <= 3; i++) { printk(" %d ", i); for (unsigned long j = 0; j < page_bitmap_sz[i]; j += BITS_PER_LONG) printk("%08x ", *(page_bitmap[i] + j / BITS_PER_LONG)); printk("\n"); } } /* Useful function to get a signature of memory fragmentation before and * after allocating/freeing memory. */ unsigned long page_alloc_signature(void) { unsigned long hash = hash_djb2((unsigned char *) page_bitmap[0], 4 * 4) + hash_djb2((unsigned char *) page_bitmap[1], 2 * 4) + hash_djb2((unsigned char *) page_bitmap[2], 1 * 4) + hash_djb2((unsigned char *) page_bitmap[3], 1 * 4); return hash; } ================================================ FILE: kernel/mm/slab.c ================================================ #include #include #include #include //#include #include #include #include "linux/list.h" #define OBJECTS_PER_SLAB(objsize) \ ((CACHE_PAGE_SIZE - sizeof(struct slab)) / (objsize)) static LIST_HEAD(caches); static struct kmem_cache cache_caches = { .objsize = sizeof(struct kmem_cache), .objnum = OBJECTS_PER_SLAB(sizeof(struct kmem_cache)), .name = "cache-cache", .slabs_free = LIST_HEAD_INIT(cache_caches.slabs_free), .slabs_partial = LIST_HEAD_INIT(cache_caches.slabs_partial), .slabs_full = LIST_HEAD_INIT(cache_caches.slabs_full), .alloc_succeed = 0, .alloc_fail = 0, .opts = CACHE_OPT_NONE, }; static inline struct slab *get_slab_from_obj(void *obj, size_t page_size) { return (struct slab *) align((unsigned long) obj, page_size); } static inline int obj_index_in_slab(void *obj, struct kmem_cache *cache) { return (((unsigned long) obj & ((1 << 8) - 1)) - sizeof(struct slab)) / cache->objsize; } struct kmem_cache *kmem_cache_create(const char *name, size_t size, __unused size_t align, __unused unsigned long flags, __unused void (*ctor)(void *)) { struct kmem_cache *cache; if (size < 8) { printk("error: Use cache for objects with size >= 8 bytes\n"); return NULL; } if (size > (256 - sizeof(struct slab)) / 2) { printk("error: Object size is too big\n"); return NULL; } cache = kmem_cache_alloc(&cache_caches, 0); if (!cache) return NULL; cache->objsize = size; cache->objnum = OBJECTS_PER_SLAB(size); cache->opts = CACHE_OPT_NONE; strncpy(cache->name, name, CACHE_NAMELEN); INIT_LIST_HEAD(&cache->slabs_free); INIT_LIST_HEAD(&cache->slabs_partial); INIT_LIST_HEAD(&cache->slabs_full); list_add(&cache->list, &caches); return cache; } static struct slab *kmem_cache_grow(struct kmem_cache *cache) { struct slab *slab = alloc_pages(0); if (!slab) return NULL; slab->free_bitmap[0] = 0; slab->free_objects = cache->objnum; list_add(&slab->list, &cache->slabs_free); return slab; } void *kmem_cache_alloc(struct kmem_cache *cache, __unused unsigned long flags) { struct slab *slab = NULL; void *mem; if (list_empty(&cache->slabs_partial)) { if (list_empty(&cache->slabs_free)) { cache->alloc_fail++; slab = kmem_cache_grow(cache); if (!slab) return NULL; } else { cache->alloc_succeed++; slab = list_first_entry(&cache->slabs_free, struct slab, list); } list_move(&slab->list, &cache->slabs_partial); } else { cache->alloc_succeed++; slab = list_first_entry(&cache->slabs_partial, struct slab, list); } int bit = find_first_zero_bit(slab->free_bitmap, cache->objnum); bitmap_set_bit(slab->free_bitmap, bit); mem = slab->data + bit * cache->objsize; slab->free_objects--; if (!slab->free_objects) list_move(&slab->list, &cache->slabs_full); return mem; } static int slab_destroy(__unused struct kmem_cache *cache, struct slab *slab) { list_del(&slab->list); free_pages((unsigned long) slab, 0); return 0; } void kmem_cache_free(struct kmem_cache *cache, void *obj) { struct slab *slab; int bit; slab = get_slab_from_obj(obj, 256); bit = obj_index_in_slab(obj, cache); bitmap_clear_bit(slab->free_bitmap, bit); slab->free_objects++; if (slab->free_objects == cache->objnum) slab_destroy(cache, slab); else if (slab->free_objects == 1) list_move(&slab->list, &cache->slabs_partial); } void kmem_cache_init(void) { list_add(&cache_caches.list, &caches); kmem_cache_grow(&cache_caches); } ================================================ FILE: kernel/mutex.c ================================================ #include #include #include #include #include "linux/list.h" static LIST_HEAD(mutex_head); /* The thread owns the mutex on return. We also check the case when the lock * has been released between the test of the mutex and this syscall. */ int sys_pthread_mutex_lock(kernel_mutex_t *mutex) { mutex->val++; if (!mutex->val) return 0; CURRENT_THREAD_INFO(curr_thread); curr_thread->ti_private = mutex; curr_thread->ti_state = THREAD_STATE_BLOCKED; list_add_tail(&curr_thread->ti_q, &mutex_head); sched_elect(SCHED_OPT_NONE); return 0; } static struct thread_info *find_first_blocking_thread(kernel_mutex_t *mutex) { struct thread_info *thread; list_for_each_entry(thread, &mutex_head, ti_q) { if (thread->ti_private == mutex) return thread; } return NULL; } int sys_pthread_mutex_unlock(kernel_mutex_t *mutex) { struct thread_info *waiter = NULL; mutex->val--; if (mutex->val >= 0) { waiter = find_first_blocking_thread(mutex); if (!waiter) { printk("[mutex_unlock] No blocking threads for mutex=<%p>\n", mutex); return -1; } list_del(&waiter->ti_q); sched_enqueue(waiter); } CURRENT_THREAD_INFO(curr_thread); if (curr_thread->ti_state == THREAD_STATE_BLOCKED) { sched_elect(SCHED_OPT_NONE); } else if (waiter && (curr_thread->ti_priority <= waiter->ti_priority)) { sched_enqueue(curr_thread); sched_elect(SCHED_OPT_NONE); } return 0; } ================================================ FILE: kernel/printk.c ================================================ #include #include #include #define VSNPRINTF_BUF_SIZE 256 static ucontext_t printk_context; static ucontext_t vsnprintf_context = {.uc_link = &printk_context}; static unsigned int ctx_stack[128]; static char vsnprintf_buf[VSNPRINTF_BUF_SIZE]; static int retval; /* not thread-safe, not reentrant */ static void co_vsnprintf(const char *format, va_list ap) { retval = vsnprintf(vsnprintf_buf, VSNPRINTF_BUF_SIZE, format, ap); } #include #include "platform.h" void __printk_init(void) { uart_init(); } void __printk_putchar(char c) { if (c == '\n') __printk_putchar('\r'); while (!(USARTx->SR & USART_SR_TXE)) ; USARTx->DR = (0xff) & c; } int printk(const char *format, ...) { /*FIXME: should be interruptable*/ __disable_irq(); va_list ap; va_start(ap, format); vsnprintf_context.uc_stack.ss_sp = &ctx_stack[128]; makecontext(&vsnprintf_context, co_vsnprintf, 2, format, ap); swapcontext(&printk_context, &vsnprintf_context); for (char *c = vsnprintf_buf; *c != '\0'; c++) __printk_putchar(*c); va_end(ap); /*FIXME: should be interruptable*/ __enable_irq(); return retval; } ================================================ FILE: kernel/resource.c ================================================ #include // clang-format off static struct rlimit rlimits[] = { { .rlim_cur = 1024, .rlim_max = 1024 } /* RLIMIT_STACK */ }; int sys_getrlimit(int resource, struct rlimit *rlim) { rlim->rlim_cur = rlimits[resource].rlim_cur; rlim->rlim_max = rlimits[resource].rlim_max; return 0; } int sys_setrlimit(int resource, const struct rlimit *rlim) { rlimits[resource].rlim_cur = rlim->rlim_cur; rlimits[resource].rlim_max = rlim->rlim_max; return 0; } ================================================ FILE: kernel/sched/bitmap.c ================================================ #include #include #include #include "linux/list.h" #include "kernel.h" #include "kernel/bitmap.h" extern struct thread_info *thread_idle; static struct bitmap_struct _active, _expire; static struct { struct bitmap_struct *active; struct bitmap_struct *expire; } sched_struct = { .active = &_active, .expire = &_expire, }; static int sched_bitmap_init(void) { INIT_BITMAP(sched_struct.active); INIT_BITMAP(sched_struct.expire); return 0; } static struct thread_info *find_next_thread(struct bitmap_struct *bm) { int max_prio = find_first_bit(&bm->map, 32); /* all runqueues are empty, return the idle_thread */ if (max_prio == 32) return thread_idle; // idle_thread return bitmap_first_entry(bm, max_prio, struct thread_info, ti_q); } static int thread_enqueue(struct thread_info *thread, struct bitmap_struct *bm) { bitmap_enqueue(&thread->ti_q, thread->ti_priority, bm); return 0; } static int sched_bitmap_enqueue(struct thread_info *thread) { return thread_enqueue(thread, sched_struct.active); } static int thread_dequeue(struct thread_info *thread, struct bitmap_struct *bm) { CURRENT_THREAD_INFO(current); /* active thread is not in the runqueue */ if (thread == current) return -1; bitmap_queue_del(&thread->ti_q, thread->ti_priority, bm); return 0; } static int sched_bitmap_dequeue(struct thread_info *thread) { int state = thread->ti_state; if (state == THREAD_STATE_ACTIVED) return thread_dequeue(thread, sched_struct.active); else if (state == THREAD_STATE_EXPIRED) return thread_dequeue(thread, sched_struct.expire); else return -1; } static int sched_bitmap_elect(int flags) { CURRENT_THREAD_INFO(current); struct thread_info *next; next = find_next_thread(sched_struct.active); // check each thread timeslice in active queue // if necessary swap active and expire queue if (next == thread_idle && find_next_thread(sched_struct.expire) != thread_idle) { SWAP(sched_struct.active, sched_struct.expire); swap_sched_state_map(); next = find_next_thread(sched_struct.active); } if (next != thread_idle) { // idle_thread list_del(&next->ti_q); if (list_empty(&sched_struct.active->queue[next->ti_priority])) bitmap_clear_bit(&sched_struct.active->map, next->ti_priority); } if (flags == SCHED_OPT_RESTORE_ONLY) thread_restore(next); // switch_to_restore_only else { if (flags == SCHED_OPT_TICK && current != thread_idle) { thread_enqueue(current, sched_struct.expire); current->ti_state = THREAD_STATE_EXPIRED; } if (next == current) return 0; switch_to(next, current); } return 0; } // clang-format off static struct sched sched_bitmap = { .class_type = SCHED_CLASS_BITMAP, .init = sched_bitmap_init, .enqueue = sched_bitmap_enqueue, .dequeue = sched_bitmap_dequeue, .elect = sched_bitmap_elect, }; // clang-format on HOOK_SCHED_CLASS(bitmap, &sched_bitmap) ================================================ FILE: kernel/sched/rr.c ================================================ #include #include #include "linux/list.h" #include "kernel.h" static LIST_HEAD(rr_runq); extern struct thread_info *thread_idle; int sched_rr_init(void) { return 0; } static struct thread_info *find_next_thread(struct thread_info *thread) { if (list_is_last(&thread->ti_q, &rr_runq)) return list_first_entry(&rr_runq, struct thread_info, ti_q); return list_next_entry(thread, ti_q); } int sched_rr_enqueue(struct thread_info *thread) { list_add(&thread->ti_q, &rr_runq); return 0; } int sched_rr_dequeue(struct thread_info *thread) { CURRENT_THREAD_INFO(current); if (current == thread) { struct thread_info *next = thread_idle; if (!list_is_singular(&rr_runq)) { next = find_next_thread(current); } list_del(&thread->ti_q); thread_restore(next); // FIXME: rename to switch_to_no_save } else { list_del(&thread->ti_q); } return 0; } /* This function is used when the runqueue has been modified externally, and it is not possible to fetch the next thread. */ static int sched_rr_elect_reset(void) { CURRENT_THREAD_INFO(current); struct thread_info *next = thread_idle; if (!list_empty(&rr_runq)) next = list_first_entry(&rr_runq, struct thread_info, ti_q); switch_to(next, current); return 0; } int sched_rr_elect(int switch_type) { CURRENT_THREAD_INFO(current); struct thread_info *next; if (switch_type & SCHED_OPT_RESET) return sched_rr_elect_reset(); if (list_empty(&rr_runq)) { // go to thread idle. next = thread_idle; } else { next = find_next_thread(current); } /* keep running the previous thread */ if (next == current) return -1; /* Leave _current_ thread for now. The _current_ thread will be elected again after _next_ thread has run. Inform the caller function (in _current_ context) that the thread gently gave way. */ switch_to(next, current); return 0; } // clang-format off static struct sched sched_rr = { .class_type = SCHED_CLASS_RR, .init = sched_rr_init, .enqueue = sched_rr_enqueue, .dequeue = sched_rr_dequeue, .elect = sched_rr_elect, }; // clang-format on HOOK_SCHED_CLASS(RR, &sched_rr) ================================================ FILE: kernel/sched.c ================================================ #include #include extern unsigned long __sched_classes_start__; extern unsigned long __sched_classes_end__; /* static vars to repesent sched classes list */ static struct sched **sched_classes = (struct sched **) &__sched_classes_start__; static struct sched **sched_classes_end = (struct sched **) &__sched_classes_end__; static struct sched *sched; int sched_init() { int ret = 0; /* Initialize each scheduler class by traversing hooks */ for (struct sched **c = sched_classes; c < sched_classes_end; c++) { struct sched *class = *c; ret |= class->init(); } return ret; } int sched_select(sched_class_t sched_type) { int ret = -1; struct sched *class = NULL; /* Examine specified sched class in hooks or not */ for (struct sched **c = sched_classes; c < sched_classes_end; c++) if (sched_type == (*c)->class_type) class = *c; if (class) { sched = class; ret = 0; } return ret; } int sched_enqueue(struct thread_info *thread) { thread->ti_state = THREAD_STATE_ACTIVED; return sched->enqueue(thread); } int sched_dequeue(struct thread_info *thread) { return sched->dequeue(thread); } int sched_elect(int flags) { int r; CURRENT_THREAD_INFO(cur_thread); KERNEL_STACK_CHECKING; r = sched->elect(flags); cur_thread->ti_state = THREAD_STATE_RUNNING; return r; } ================================================ FILE: kernel/signal.c ================================================ #include #include #include #include #include #include #include #include #include #include #include #include "kernel.h" #include "platform.h" extern void return_from_sighandler(void); extern void return_from_sigaction(void); static void *v7m_alloca_thread_context(struct thread_info *tip, size_t len) { tip->ti_mach.mi_psp -= len; return (void *) tip->ti_mach.mi_psp; } static void stage_sighandler(struct sigaction *sigaction) { CURRENT_THREAD_INFO(curr_thread); struct thread_context_regs *ctx; /* update current thread SP_process */ curr_thread->ti_mach.mi_psp = __get_PSP(); /* this is the exception stacked-context */ ctx = (struct thread_context_regs *) curr_thread->ti_mach.mi_psp; /* return value of syscall, cannot fail after this point */ ctx->r0_r3__r12[0] = 0; /* the sigaction context will be poped by cpu on exception return */ v7m_alloca_thread_context(curr_thread, sizeof(struct thread_context_regs)); /* build the sigaction trampoline */ ctx = (struct thread_context_regs *) curr_thread->ti_mach.mi_psp; /* #ifdef SECURE_KERNEL */ ctx->r0_r3__r12[1] = 0; ctx->r0_r3__r12[2] = 0; ctx->r0_r3__r12[3] = 0; ctx->r0_r3__r12[4] = 0; /* #endif */ ctx->lr = (u32) v7m_set_thumb_bit(return_from_sighandler); ctx->ret_addr = (u32) v7m_clear_thumb_bit(sigaction->sa_handler); ctx->xpsr = xPSR_T_Msk; /* update current thread SP_process */ __set_PSP(curr_thread->ti_mach.mi_psp); } static void stage_sigaction(const struct sigaction *sigaction, int sig, union sigval value) { CURRENT_THREAD_INFO(curr_thread); struct thread_context_regs *ctx; /* update current thread SP_process */ curr_thread->ti_mach.mi_psp = __get_PSP(); /* this is the exception stacked-context */ ctx = (struct thread_context_regs *) curr_thread->ti_mach.mi_psp; /* return value of syscall, cannot fail after this point */ ctx->r0_r3__r12[0] = 0; /* The siginfo_t struct is allocated on thread's stack; that memory * will be reclaimed during return_from_sigaction. */ siginfo_t *siginfo_ptr = v7m_alloca_thread_context(curr_thread, sizeof(siginfo_t)); siginfo_ptr->si_signo = sig; siginfo_ptr->si_value = value; siginfo_ptr->si_pid = curr_thread->ti_id; /* the sigaction context will be poped by cpu on exception return */ v7m_alloca_thread_context(curr_thread, sizeof(struct thread_context_regs)); /* build a sigaction trampoline */ ctx = (struct thread_context_regs *) curr_thread->ti_mach.mi_psp; ctx->r0_r3__r12[1] = (u32) siginfo_ptr; ctx->r0_r3__r12[2] = 0; /* ucontext_t *, but commonly unused */ ctx->r0_r3__r12[3] = 0; ctx->r0_r3__r12[4] = 0; ctx->lr = (u32) v7m_set_thumb_bit(return_from_sigaction); ctx->ret_addr = (u32) v7m_clear_thumb_bit(sigaction->sa_sigaction); ctx->xpsr = xPSR_T_Msk; /* update current thread SP_process */ __set_PSP(curr_thread->ti_mach.mi_psp); } void do_sigevent(const struct sigevent *sigevent, struct thread_info *thread) { CURRENT_THREAD_INFO(curr_thread); struct thread_context_regs *ctx; // if (sigevent->sigev_notify == SIGEV_THREAD) { /* update current thread SP_process */ if (thread == curr_thread) thread->ti_mach.mi_psp = __get_PSP(); /* the sigevent context will be poped by cpu on exception return */ v7m_alloca_thread_context(thread, sizeof(struct thread_context_regs)); /* build a sigevent trampoline */ ctx = (struct thread_context_regs *) thread->ti_mach.mi_psp; ctx->r0_r3__r12[0] = sigevent->sigev_value.sival_int; ctx->r0_r3__r12[1] = 0; ctx->r0_r3__r12[2] = 0; ctx->r0_r3__r12[3] = 0; ctx->r0_r3__r12[4] = 0; ctx->lr = (u32) v7m_set_thumb_bit(return_from_sighandler); ctx->ret_addr = (u32) v7m_clear_thumb_bit(sigevent->sigev_notify_function); ctx->xpsr = xPSR_T_Msk; /* update current thread SP_process */ if (thread == curr_thread) __set_PSP(thread->ti_mach.mi_psp); } static struct sigaction *find_sigaction_by_sig(__unused pid_t pid, int sig) { /* FIXME: consider multi-tasking environment */ struct signal_info *signal; CURRENT_TASK_INFO(curr_task); list_for_each_entry(signal, &curr_task->signal_head, list) { if (signal->signo == sig) return &signal->act_storage; } return NULL; } int sys_sigaction(int signo, const struct sigaction *restrict act, struct sigaction *restrict oldact) { if ((signo == SIGKILL) || (signo == SIGSTOP)) { errno = EINVAL; return -1; } if (!act) { errno = EFAULT; return -1; } if (oldact) { struct sigaction *oact = find_sigaction_by_sig(0, signo); if (oact != NULL) memcpy(oldact, oact, sizeof(struct sigaction)); } struct signal_info *signal = malloc(sizeof(struct signal_info)); if (!signal) { errno = ENOMEM; return -1; } signal->signo = signo; CURRENT_TASK_INFO(curr_task); list_add(&signal->list, &curr_task->signal_head); memcpy(&signal->act_storage, act, sizeof(struct sigaction)); return 0; } /* enabled signal mask */ static unsigned long supported_signal_mask = (1 << SIGKILL) | (1 << SIGUSR1) | (1 << SIGUSR2) | (1 << SIGSTOP); static int is_signal_supported(int sig) { if (sig > SIGMAX) return 0; return bitmap_get_bit(&supported_signal_mask, sig); } /* How signal works? * * - A fake exception return context is allocated to the user thread stack. * - This context is a trampoline to the signal handler. * - When the syscall handler returns, the return value is pushed to the user * stack in r0. For signal handling, r0 must contain the first parameter to * the signal handler function. The actual return code of the syscall must * be written into the auto-pushed stack context. The staging functions * handle the update of the error code in the cpu-pushed stackframe. */ int sys_kill(__unused pid_t pid, int sig) { if (!is_signal_supported(sig)) return -EINVAL; struct sigaction *act = find_sigaction_by_sig(0, sig); if (!act) return -EINVAL; if (act->sa_flags & SA_SIGINFO) stage_sigaction(act, sig, (union sigval){.sival_int = 0}); else stage_sighandler(act); return sig; } ================================================ FILE: kernel/softirq.c ================================================ #include #include #include #include #include #include #include "kernel/bitmap.h" enum { PRIO_TASKLET = 0, NR_SOFTIRQS, }; const char *const softirq_to_name[NR_SOFTIRQS] = {"PRIO_TASKLET"}; static struct softirq_action softirq_vec[NR_SOFTIRQS]; /* Priority-based tasklet */ static struct bitmap_struct prio_tasklet; /* softirq_daemon */ static struct task_info softirq_daemon; static struct thread_info *thread_softirqd; int open_softirq(unsigned int nr, int (*action)(struct softirq_action *)) { if (nr >= NR_SOFTIRQS) return -1; softirq_vec[nr].action = action; return 0; } /* XXX: represent softirqd whether in runq or not */ static volatile int softirq_run = 0; int raise_softirq(unsigned int nr) { if (nr >= NR_SOFTIRQS) return -1; /* XXX: we ignore do_softirq routine as Linux PREEMPT_RT */ if (!softirq_run) { sched_enqueue(thread_softirqd); softirq_run = 1; } return 0; } struct tasklet_struct *tasklet_init(void(*func), void *data, unsigned long prio) { struct tasklet_struct *tsk = (struct tasklet_struct *) malloc(sizeof(struct tasklet_struct)); if (!tsk) return NULL; tsk->prio = prio; tsk->func = func; tsk->data = data; INIT_LIST_HEAD(&tsk->tsk_q); return tsk; } int tasklet_schedule(struct tasklet_struct *task) { if (!task || task->prio > PRIO_TASKLET_MINPRIO) return -1; // list_add_tail(&task->tsk_q, &prio_tasklet.runq[task->prio]); // bitmap_set_bit(&prio_tasklet.bitmap, task->prio); bitmap_enqueue(&task->tsk_q, task->prio, &prio_tasklet); return raise_softirq(PRIO_TASKLET); } static int tasklet_action(struct softirq_action __unused *a) { struct tasklet_struct *tsk = NULL; while (1) { if (prio_tasklet.map) { int max_prio = find_first_bit(&prio_tasklet.map, 32); // tsk = list_first_entry(&prio_tasklet.runq[max_prio], struct // tasklet_struct, tsk_q); tsk = bitmap_first_entry((&prio_tasklet), max_prio, struct tasklet_struct, tsk_q); // list_del(&tsk->tsk_q); // if (list_empty(&prio_tasklet.runq[prio])) // bitmap_clear_bit(&prio_tasklet.bitmap, prio); bitmap_queue_del(&tsk->tsk_q, max_prio, &prio_tasklet); if (!tsk->func) { printk( "[!] prio_taskletd: prio_tasklet function is NULL ptr.\n"); break; } tsk->func(tsk->data); } else return 0; } return -1; } static void init_softirq_entry() { return; } extern void sched_yield(); static void *softirqd(__unused void *arg) { int ret = -1; while (1) { ret = tasklet_action(NULL); if (ret == 0) sched_yield(); else break; } printk("[!] softirqd thread should not return.\n"); softirq_run = 0; return NULL; } int init_softirq(void) { /* initialize softirq vector */ open_softirq(PRIO_TASKLET, tasklet_action); /* initialize priority tasklet obj */ INIT_BITMAP(&prio_tasklet); // FIXME: no arg for priority in thread_create() /* initialize softirq daemon thread */ task_init(&softirq_daemon); thread_softirqd = thread_create(softirqd, NULL, THREAD_PRIV_USER, 1024, &softirq_daemon); if (!thread_softirqd) { printk("[!] Could not create softirqd thread.\n"); return -1; } thread_set_priority(thread_softirqd, PRI_MAX); /* initialize softirq entries */ init_softirq_entry(); return 0; } ================================================ FILE: kernel/task.c ================================================ #include #include #include #include "linux/list.h" static LIST_HEAD(task_head); static pid_t alloc_pid() { static pid_t pid = 7000; pid_t retpid; retpid = pid; pid++; return retpid; } struct task_info *task_init(struct task_info *task) { task->pid = alloc_pid(); task->filemap = 0; INIT_LIST_HEAD(&task->thread_head); INIT_LIST_HEAD(&task->signal_head); list_add(&task->list, &task_head); return task; } void task_exit(struct task_info *task) { // this is called after last thread has exited, or when the // task is killed list_del(&task->list); free(task); } struct task_info *current_task_info(void) { CURRENT_THREAD_INFO(curr_thread); return curr_thread->ti_task; } int sys_getpid(void) { CURRENT_TASK_INFO(curr_task); return curr_task->pid; } ================================================ FILE: kernel/thread.c ================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #include "platform.h" static struct kernel_context_regs *alloc_interrupt_stack(void) { char *memp; struct kernel_context_regs *kcr; memp = alloc_pages(size_to_page_order(INTR_STACK_SIZE)); if (!memp) return NULL; kcr = (struct kernel_context_regs *) (memp + INTR_STACK_SIZE - sizeof(struct kernel_context_regs)); memset(kcr->r4_r12, 0, 9 * sizeof(u32)); kcr->lr = V7M_EXC_RETURN_THREAD_PROCESS; return kcr; } /* new thread's LR loaded with pthread_exit address */ void pthread_exit(void *retval); // XXX: Detecting a stack-overflow on v7M: #memf && (SP_Process == MMFAR) static struct thread_context_regs * alloc_thread_stack(void *(*start_routine)(void *), void *arg, size_t stacksize) { char *memp; struct thread_context_regs *tcr; memp = alloc_pages(size_to_page_order(stacksize)); if (!memp) return NULL; tcr = (struct thread_context_regs *) (memp + stacksize - sizeof(struct thread_context_regs)); tcr->r0_r3__r12[0] = (u32) arg; memset(&tcr->r0_r3__r12[1], 0, 4 * sizeof(u32)); tcr->lr = (u32) pthread_exit; // FIXME: Should libc be dynamically loaded? tcr->ret_addr = (u32) v7m_clear_thumb_bit(start_routine); tcr->xpsr = xPSR_T_Msk; return tcr; } struct thread_info *thread_create(void *(*start_routine)(void *), void *arg, enum thread_privilege priv, size_t stacksize, struct task_info *task) { struct thread_info *thread; struct kernel_context_regs *kcr; struct thread_context_regs *tcr; static int thread_count = 0; kcr = alloc_interrupt_stack(); if (!kcr) return NULL; tcr = alloc_thread_stack(start_routine, arg, stacksize); thread = THREAD_INFO(kcr); if (!tcr) { free(thread); return NULL; } thread->ti_mach.mi_psp = (u32) tcr; thread->ti_mach.mi_msp = (u32) kcr; thread->ti_mach.mi_priv = priv; thread->ti_stacksize = stacksize; thread->ti_id = thread_count++; thread->ti_task = task; thread->ti_joinable = false; thread->ti_joining = NULL; thread->ti_detached = false; thread->ti_priority = PRI_MIN; thread->ti_state = THREAD_STATE_NEW; #ifdef CONFIG_KERNEL_STACK_CHECKING thread->ti_canary[0] = THREAD_CANARY0; thread->ti_canary[1] = THREAD_CANARY1; #endif list_add(&thread->ti_list, &task->thread_head); return thread; } int thread_yield(void) { // FIXME: elect iff there is a higher-priority thread ready to run CURRENT_THREAD_INFO(curr_thread); sched_enqueue(curr_thread); return sched_elect(SCHED_OPT_NONE); } int thread_self(void) { CURRENT_THREAD_INFO(curr_thread); return curr_thread->ti_id; } void thread_exit(void *retval) { CURRENT_THREAD_INFO(curr_thread); /* free thread stack memory */ free_pages(align(curr_thread->ti_mach.mi_psp, curr_thread->ti_stacksize), size_to_page_order(curr_thread->ti_stacksize)); if (curr_thread->ti_detached == false) { curr_thread->ti_retval = retval; if (curr_thread->ti_joining) sched_enqueue(curr_thread->ti_joining); else curr_thread->ti_joinable = true; } else { /* We are freeing the stack we are running on, no kernel preemption * is allowed until we call sched_elect(). */ free_pages((unsigned long) curr_thread, size_to_page_order(INTR_STACK_SIZE)); } sched_elect(SCHED_OPT_RESTORE_ONLY); } int thread_set_priority(struct thread_info *thread, int priority) { /* priority change is effective on next scheduling */ thread->ti_priority = priority; return 0; } static struct thread_info *find_thread_by_id(int id) { struct thread_info *tp; CURRENT_TASK_INFO(curr_task); list_for_each_entry(tp, &curr_task->thread_head, ti_list) { if (tp->ti_id == id) return tp; } return NULL; } int thread_join(pthread_t thread, void **retval) { struct thread_info *other; other = find_thread_by_id(thread); if (!other) return -ESRCH; /* No thread with the ID thread could be found. */ if (other->ti_detached == true) return -EINVAL; /* thread is not a joinable thread. */ /* the other thread is not yet joinable, the current thread blocks */ if (other->ti_joinable == false) { CURRENT_THREAD_INFO(curr_thread); if (other->ti_joining) return -EINVAL; /* Another thread is already waiting to join with this thread. */ other->ti_joining = curr_thread; sched_elect(SCHED_OPT_NONE); } *retval = other->ti_retval; // XXX: free other's resources, interrupt stack return 0; } int thread_detach(pthread_t thread) { struct thread_info *thread_info; thread_info = find_thread_by_id(thread); thread_info->ti_detached = true; return 0; } /* pthread interface */ int sys_pthread_yield(void) { return thread_yield(); } pthread_t sys_pthread_self(void) { return (pthread_t) thread_self(); } void sys_pthread_exit(void *retval) { thread_exit(retval); } int sys_pthread_detach(pthread_t thread) { return thread_detach(thread); } int sys_pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) { struct rlimit stacklimit; size_t stacksize; /* get the thread default stack size */ sys_getrlimit(RLIMIT_STACK, &stacklimit); if (attr) stacksize = MIN(attr->stacksize, stacklimit.rlim_max); else stacksize = stacklimit.rlim_cur; // FIXME: Check start_routine's address belongs to process' address-space struct thread_info *thread_info = thread_create( start_routine, arg, THREAD_PRIV_USER, stacksize, current_task_info()); if (!thread_info) return EAGAIN; /* insufficient resources to create another thread */ *thread = (pthread_t) thread_info->ti_id; sched_enqueue(thread_info); return 0; } int sys_pthread_join(pthread_t thread, void **retval) { return thread_join(thread, retval); } ================================================ FILE: kernel/time.c ================================================ #include #include #include #include #include #include #include #include #include #include /* sleep functions */ // XXX: sleep() is part of , but nanosleep() is part of extern struct thread_info *thread_idle; static void msleep_callback(struct timer_info *timer) { sched_enqueue(timer->owner); timer->disarmed = 1; } // FIXME: POSIX standard is sys_nanosleep() int sys_msleep(unsigned int msec) { struct timer_info *timer = timer_alloc(); if (!timer) return -1; CURRENT_THREAD_INFO(curr_thread); timer->owner = curr_thread; timer->disarmed = 0; timer->type = ONESHOT_TIMER; timer->callback = msleep_callback; struct timespec value = {.tv_sec = msec / 1000, .tv_nsec = (msec % 1000) * 1000000}; timer_set(timer, &value); sched_dequeue(curr_thread); sched_elect(SCHED_OPT_NONE); timer_free(timer); return 0; } /* POSIX timers */ static LIST_HEAD(timer_head); static struct timer_info *find_timer_by_id(timer_t timerid, struct list_head *timer_list) { struct timer_info *pos; list_for_each_entry(pos, timer_list, list) { if (pos->id == timerid) return pos; } return NULL; } static int reserve_timer_id(timer_t *timerid) { static unsigned long bitmap = 0; unsigned long bit = find_first_zero_bit(&bitmap, BITS_PER_LONG); if (bit == BITS_PER_LONG) return -1; bitmap_set_bit(&bitmap, bit); *timerid = bit; return 0; } static void timer_callback(struct timer_info *timer) { if (timer->type == ONESHOT_TIMER) timer->disarmed = 1; do_sigevent(&timer->sigev, timer->owner); } static void timer_callback_and_link(struct timer_info *timer) { timer->type = INTERVAL_TIMER; timer->callback = timer_callback; timer_set(timer, &timer->value.it_interval); do_sigevent(&timer->sigev, timer->owner); } int sys_timer_create(__unused clockid_t clockid, struct sigevent *sevp, timer_t *timerid) { struct timer_info *timer = timer_alloc(); if (!timer) return -1; if (reserve_timer_id(&timer->id)) { timer_free(timer); return EAGAIN; } *timerid = timer->id; timer->disarmed = 1; memcpy(&timer->sigev, sevp, sizeof(struct sigevent)); list_add(&timer->list, &timer_head); return 0; } int sys_timer_settime(timer_t timerid, __unused int flags, const struct itimerspec *new_value, struct itimerspec *old_value) { struct timer_info *timer = find_timer_by_id(timerid, &timer_head); if (!timer) return EINVAL; if (old_value != NULL) memcpy(old_value, &timer->value, sizeof(struct itimerspec)); memcpy(&timer->value, new_value, sizeof(struct itimerspec)); /* disarm timer */ if (!new_value->it_value.tv_sec && !new_value->it_value.tv_nsec) { if (!timer->disarmed) { timer_set(timer, &new_value->it_value); timer->disarmed = 1; } return 0; } CURRENT_THREAD_INFO(curr_thread); timer->owner = curr_thread; timer->disarmed = 0; if (new_value->it_interval.tv_sec || new_value->it_interval.tv_nsec) { if ((new_value->it_value.tv_sec == new_value->it_interval.tv_sec) && (new_value->it_value.tv_nsec == new_value->it_interval.tv_nsec)) { timer->type = INTERVAL_TIMER; timer->callback = timer_callback; timer_set(timer, &new_value->it_interval); } else { timer->type = ONESHOT_TIMER; timer->callback = timer_callback_and_link; timer_set(timer, &new_value->it_value); } } else { timer->type = ONESHOT_TIMER; timer->callback = timer_callback; timer_set(timer, &new_value->it_value); } return 0; } int sys_timer_gettime(timer_t timerid, struct itimerspec *curr_value) { struct timer_info *timer = find_timer_by_id(timerid, &timer_head); if (!timer) return EINVAL; timer_get(timer, curr_value); return 0; } ================================================ FILE: libc/fcntl.c ================================================ #include extern int _open(const char *pathname, int flag); extern int _close(int fd); extern int _read(int fd, void *buf, size_t count); extern int _write(int fd, void *buf, size_t count); extern int _lseek(int fd, off_t offset, int whence); int open(const char *pathname, int flag) { return _open(pathname, flag); } int close(int fd) { return _close(fd); } int read(int fd, void *buf, size_t count) { return _read(fd, buf, count); } int write(int fd, void *buf, size_t count) { return _write(fd, buf, count); } int lseek(int fd, off_t offset, int whence) { return _lseek(fd, offset, whence); } ================================================ FILE: libc/filesystem.c ================================================ /* syscall wrappers */ #include #include #include #include #include "piko/syscalls.h" int _open(const char *pathname, int flags) { return do_syscall2((void *) pathname, (void *) flags, SYS_OPEN); } int _close(int fd) { return do_syscall1((void *) fd, SYS_CLOSE); } ssize_t _read(int fd, void *buf, size_t count) { return (ssize_t) do_syscall3((void *) fd, buf, (void *) count, SYS_READ); } ssize_t _write(int fd, void *buf, size_t count) { return (ssize_t) do_syscall3((void *) fd, buf, (void *) count, SYS_WRITE); } off_t _lseek(int fd, off_t offset, int whence) { return (off_t) do_syscall3((void *) fd, (void *) offset, (void *) whence, SYS_LSEEK); } int stat(const char *pathname, struct stat *buf) { return do_syscall2((void *) pathname, (void *) buf, SYS_STAT); } int mount(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data) { return do_syscall5((void *) source, (void *) target, (void *) filesystemtype, (void *) mountflags, (void *) data, SYS_MOUNT); } DIR *opendir(const char *name) { return (DIR *) do_syscall2((void *) name, (void *) O_DIRECTORY, SYS_OPEN); } int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result) { return do_syscall3((void *) dirp, (void *) entry, (void *) result, SYS_READDIR_R); } int closedir(DIR *dirp) { return do_syscall1((void *) dirp, SYS_CLOSE); } ================================================ FILE: libc/piko/mman.c ================================================ #include #include #include "syscalls.h" void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) { return (void *) do_syscall6((void *) addr, (void *) length, (void *) prot, (void *) flags, (void *) fd, (void *) offset, SYS_MMAP); } int munmap(__unused void *addr, __unused size_t length) { return do_syscall2((void *) addr, (void *) length, SYS_MUNMAP); } ================================================ FILE: libc/piko/stubs.c ================================================ /* Newlib stubs */ #include #include #include #include #include #include "syscalls.h" #define HANGS_ON() \ ({ \ printk("error: Newlib needs %s", __func__); \ for (;;) \ ; \ }) int _isatty(__unused int file) { HANGS_ON(); return 1; } int _fstat() { HANGS_ON(); return -1; } void *_sbrk(__unused int incr) { HANGS_ON(); return NULL; } void _exit(__unused int status) { HANGS_ON(); } clock_t _times() { HANGS_ON(); return -1; } void _fini(void) { HANGS_ON(); } int _getpid(void) { return do_syscall0(SYS_GETPID); } ================================================ FILE: libc/piko/syscalls.S ================================================ #include #include .syntax unified .thumb @ int do_syscall0(void, int no) ENTRY(do_syscall0) svc #0 bx lr ENDPROC(do_syscall0) @ int do_syscall1(void *a0, int no) ENTRY(do_syscall1) svc #1 bx lr ENDPROC(do_syscall1) @ int do_syscall2(void *a0, void *a1, int no) ENTRY(do_syscall2) svc #2 bx lr ENDPROC(do_syscall2) @ int do_syscall3(void *a0, void *a1, void *a2, int no) ENTRY(do_syscall3) svc #3 bx lr ENDPROC(do_syscall3) @ int do_syscall4(void *a0, void *a1, void *a2, void *a3, @ int no) ENTRY(do_syscall4) svc #4 bx lr ENDPROC(do_syscall4) @ int do_syscall5(void *a0, void *a1, void *a2, void *a3, @ void *a4, int no) ENTRY(do_syscall5) svc #5 bx lr ENDPROC(do_syscall5) @ int do_syscall6(void *a0, void *a1, void *a2, void *a3, @ void *a4, void *a5, int no) ENTRY(do_syscall6) svc #6 bx lr ENDPROC(do_syscall6) ================================================ FILE: libc/piko/syscalls.h ================================================ /* libc/piko/syscalls.h */ #ifndef LIBC_SYSCALLS_H #define LIBC_SYSCALLS_H int do_syscall0(int no); int do_syscall1(void *a0, int no); int do_syscall2(void *a0, void *a1, int no); int do_syscall3(void *a0, void *a1, void *a2, int no); int do_syscall4(void *a0, void *a1, void *a2, void *a3, int no); int do_syscall5(void *a0, void *a1, void *a2, void *a3, void *a4, int no); int do_syscall6(void *a0, void *a1, void *a2, void *a3, void *a4, void *a5, int no); #endif /* !LIBC_SYSCALLS_H */ ================================================ FILE: libc/pthread.c ================================================ #include #include #include #include "linux/list.h" int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize) { if (!attr) return -1; attr->stacksize = stacksize; return 0; } int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize) { if (!attr) return -1; *stacksize = attr->stacksize; return 0; } const pthread_attr_t pthread_attr_default = {.stacksize = 1024}; int pthread_attr_init(pthread_attr_t *attr) { if (!attr) return -1; memcpy(attr, &pthread_attr_default, sizeof(pthread_attr_t)); return 0; } int pthread_mutex_init(pthread_mutex_t *mutex, __unused const pthread_mutexattr_t *attr) { *mutex = -1; return 0; } int pthread_cond_init(__unused pthread_cond_t *cond, __unused const pthread_condattr_t *attr) { return 0; } /* syscall wrappers */ #include #include "piko/syscalls.h" int sched_yield(void) { return do_syscall0(SYS_PTHREAD_YIELD); } int pthread_yield(void) __attribute__((alias("sched_yield"))); pthread_t pthread_self(void) { return (pthread_t) do_syscall0(SYS_PTHREAD_SELF); } void pthread_exit(void *retval) { do_syscall1((void *) retval, SYS_PTHREAD_EXIT); /* compiler complains about 'noreturn' function does return */ for (;;) ; } int pthread_detach(pthread_t thread) { return do_syscall1((void *) thread, SYS_PTHREAD_DETACH); } int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) { return do_syscall4((void *) thread, (void *) attr, (void *) start_routine, arg, SYS_PTHREAD_CREATE); } int pthread_join(pthread_t thread, void **retval) { return do_syscall2((void *) thread, (void *) retval, SYS_PTHREAD_JOIN); } int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) { return do_syscall2((void *) cond, (void *) mutex, SYS_PTHREAD_COND_WAIT); } int pthread_cond_signal(pthread_cond_t *cond) { return do_syscall1((void *) cond, SYS_PTHREAD_COND_SIGNAL); } ================================================ FILE: libc/signal.c ================================================ /* syscall wrappers */ #include #include #include "piko/syscalls.h" int sigaction(int sig, const struct sigaction *restrict act, struct sigaction *restrict oact) { return do_syscall3((void *) sig, (void *) act, (void *) oact, SYS_SIGACTION); } int _kill(pid_t pid, int sig) { return do_syscall2((void *) pid, (void *) sig, SYS_KILL); } ================================================ FILE: libc/stdio.c ================================================ #include //XXX: for strlen, strcpy #include "utils.h" #include #include "linux/types.h" int vsnprintf(char *str, size_t size, const char *format, va_list ap) { size_t str_pos = 0; /* position in the buffer str */ char itoa_buf[16]; /* contains a 32bit integer in decimal, plus null char */ char pad_char; int pad_count = 0; for (unsigned int i = 0; (i < strlen(format)) && (str_pos < size); i++) { if (format[i] == '%') { if (format[i + 1] == '%') { str[str_pos++] = '%'; i++; continue; } /* format '%6d': value is blank-padded */ if ((format[i + 1] >= '0') && (format[i + 1] <= '9') && (format[i + 2] == 'd')) { pad_char = ' '; pad_count = format[i + 1] - '0'; i += 1; goto print_dec; } /* format '%06d' or '% 6d': value is 0- or blank-padded */ if ((format[i + 1] == ' ') || (format[i + 1] == '0')) { pad_char = format[i + 1]; pad_count = format[i + 2] - '0'; i += 2; } /* conversion modifier */ switch (format[i + 1]) { case 'p': strcpy(&str[str_pos], "0x"); str_pos += 2; pad_char = '0'; pad_count = 8; goto print_hex; case 'd': print_dec: itoa_base(va_arg(ap, unsigned int), itoa_buf, 10); goto print_num; case 'x': print_hex: itoa_base(va_arg(ap, unsigned int), itoa_buf, 16); print_num:; int itoa_buf_len = strlen(itoa_buf); if (pad_count) { pad_count -= itoa_buf_len; strpad(&str[str_pos], pad_char, pad_count); str_pos += pad_count; } strcpy(&str[str_pos], itoa_buf); str_pos += itoa_buf_len; break; case 's':; char *str_varg = va_arg(ap, char *); strcpy(&str[str_pos], str_varg); str_pos += strlen(str_varg); break; default: goto ordinary_char; } i++; } else { ordinary_char: str[str_pos++] = format[i]; } } str[str_pos] = '\0'; return str_pos; } int vsprintf(char *str, const char *format, va_list ap) { return vsnprintf(str, S32_MAX, format, ap); } int sprintf(char *str, const char *format, ...) { int retval; va_list ap; va_start(ap, format); retval = vsprintf(str, format, ap); va_end(ap); return retval; } int snprintf(char *str, size_t size, const char *format, ...) { int retval; va_list ap; va_start(ap, format); retval = vsnprintf(str, size, format, ap); va_end(ap); return retval; } ================================================ FILE: libc/stdlib.c ================================================ #include #include #include #include "linux/types.h" #include "linux/list.h" /* This is a boundary tag, located at the beginning of the block wether it's * free or allocated. */ struct malloc_tag { /* composite field: * - bit 31 0 = block not free, 1 = block free * - bit 30..0 full block length, this tag comprised */ u32 free__length; struct list_head list; char data[0]; }; #define BLOCK_FREE_MASK (1 << 30) #define BLOCK_LENGTH_MASK (~(1 << 30)) static LIST_HEAD(blocks); /* helper functions */ static inline size_t get_block_length(struct malloc_tag *block) { return block->free__length & BLOCK_LENGTH_MASK; } static inline void set_block_length(struct malloc_tag *block, size_t len) { block->free__length = (block->free__length & BLOCK_FREE_MASK) | len; } static inline bool get_block_free(struct malloc_tag *block) { return block->free__length & BLOCK_FREE_MASK ? true : false; } static inline void set_block_free(struct malloc_tag *block, bool free) { if (free) block->free__length |= BLOCK_FREE_MASK; else block->free__length &= BLOCK_LENGTH_MASK; } void kernel_heap_init(void *heap_start, size_t heap_size) { struct malloc_tag *first_block = heap_start; first_block->free__length = BLOCK_FREE_MASK | heap_size; list_add(&first_block->list, &blocks); } void *malloc(size_t size) { struct malloc_tag *free_block, *new_block; /* allocation size is a multiple of 4-byte aligned, plus size of tag */ size = align_next(size, 4) + sizeof(struct malloc_tag); /* find a free block wich is large enough to fullfill the memory requirement */ list_for_each_entry(free_block, &blocks, list) { if (get_block_free(free_block) && (get_block_length(free_block) >= size)) { if ((get_block_length(free_block) - size) > sizeof(struct malloc_tag)) { set_block_length(free_block, get_block_length(free_block) - size); new_block = (struct malloc_tag *) ((u32) free_block + get_block_length(free_block)); set_block_free(new_block, false); set_block_length(new_block, size); list_add(&new_block->list, &free_block->list); return new_block->data; } else { set_block_free(free_block, false); return free_block->data; } } } return NULL; } void free(void *ptr) { struct malloc_tag *block = container_of(ptr, struct malloc_tag, data); struct malloc_tag *prev_block, *next_block; set_block_free(block, true); /* merge with previous block if free */ if (block->list.prev != &blocks) { prev_block = list_prev_entry(block, list); if (get_block_free(prev_block)) { set_block_length(prev_block, get_block_length(prev_block) + get_block_length(block)); list_del(&block->list); block = prev_block; } } /* merge with next block if free */ if (block->list.next != &blocks) { next_block = list_next_entry(block, list); if (get_block_free(next_block)) { set_block_length( block, get_block_length(block) + get_block_length(next_block)); list_del(&next_block->list); } } } ================================================ FILE: libc/time.c ================================================ /* syscall wrappers */ #include #include #include #include #include "piko/syscalls.h" int timer_create(clockid_t clockid, struct sigevent *sevp, timer_t *timerid) { return do_syscall3((void *) clockid, (void *) sevp, (void *) timerid, SYS_TIMER_CREATE); } int timer_settime(timer_t timerid, int flags, const struct itimerspec *new_value, struct itimerspec *old_value) { return do_syscall4((void *) timerid, (void *) flags, (void *) new_value, (void *) old_value, SYS_TIMER_SETTIME); } int timer_gettime(timer_t timerid, struct itimerspec *curr_value) { return do_syscall2((void *) timerid, (void *) curr_value, SYS_TIMER_GETTIME); } ================================================ FILE: libc/ucontext.c ================================================ #include #include #include "linux/types.h" void return_from_makecontext(); void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...) { va_list ap; /* pass arguments to the context entry function */ if (argc) { va_start(ap, argc); /* the 4 first arguments go into registers r0-r3 */ for (int i = 0; (i < argc) && (i < 4); i++) ucp->uc_mcontext.gprs[i] = va_arg(ap, int); /* extra arguments go into the stack */ va_end(ap); } /* top of the stack has a back-link pointer to the context's struct */ ucp->uc_stack.ss_sp = (void *) ((u32) ucp->uc_stack.ss_sp - sizeof(u32)); *(u32 *) ucp->uc_stack.ss_sp = (u32) ucp; /* initialize the machine context */ // FIXME: ss_sp and mcontext_sp are mutually redundant ucp->uc_mcontext.sp = (u32) ucp->uc_stack.ss_sp; ucp->uc_mcontext.lr = (u32) return_from_makecontext; ucp->uc_mcontext.pc = (u32) func | 1; /* force Thumb_Mode */ } ================================================ FILE: libc/unistd.c ================================================ /* syscall wrappers */ #include #include "piko/syscalls.h" long sysconf(int name) { return do_syscall1((void *) name, SYS_SYSCONF); } unsigned int msleep(unsigned int msecs) { return do_syscall1((void *) msecs, SYS_MSLEEP); } /*int close(int fd) { return do_syscall1((void *) fd, SYS_CLOSE); }*/ ================================================ FILE: libc/utils.c ================================================ #include #include static char *strrev(char *s) { char *t = s; char *u = s; if (!s || (*s == '\0')) return s; /* u points to the last char in the string */ while (*(u + 1) != '\0') u++; /* t moves forward, u moves backward */ while (t < u) { char tmp = *t; *t++ = *u; *u-- = tmp; } return s; } void strpad(char *buf, char pad_val, int count) { buf[count--] = '\0'; for (; count >= 0; count--) buf[count] = pad_val; } char *itoa_base(int value, char *buf, int base) { const char base36[] = "0123456789abcdefghijklmnopqrstuvwxyz"; int i = 0; int isneg = 0; unsigned int val = value; if (!buf || (base > 36)) return NULL; if ((value < 0) && (base == 10)) { val = abs(value); isneg = 1; } do { buf[i++] = base36[val % base]; val /= base; } while (val); if (isneg) buf[i++] = '-'; buf[i] = '\0'; return strrev(buf); } ================================================ FILE: libc/v7m-pthread.S ================================================ #include #include .syntax unified .thumb // -1: unlocked, 0: locked, positive: locked, possible waiters /** * FIXME: In order to reduce the code base and we tend to keep just one * 'bx lr'. But it would make it not intuitional. Probably, we * could discard this optimization. */ @ int pthread_mutex_lock(pthread_mutex_t *mutex) ENTRY(pthread_mutex_lock) movs r2, #0 0: ldrex r1, [r0] teq r1, #-1 @ check locked? bne 1f strex r1, r2, [r0] teq r1, #0 @ 'strex' success? bne 0b dmb @ ARMv7-M ARM, A3.4.6 movs r0, #0 @ it also update EQ flag 1: itt ne movne r1, #SYS_PTHREAD_MUTEX_LOCK svcne #1 bx lr ENDPROC(pthread_mutex_lock) @ int pthread_mutex_trylock(pthread_mutex_t *mutex) ENTRY(pthread_mutex_trylock) movs r2, #0 0: ldrex r1, [r0] teq r1, #-1 @ check locked? bne 1f strex r1, r2, [r0] teq r1, #0 @ 'strex' success? bne 1f movs r0, #0 @ it also update EQ flag 1: it ne movne r0, #-1 bx lr ENDPROC(pthread_mutex_trylock) @ int pthread_mutex_unlock(pthread_mutex_t *mutex) ENTRY(pthread_mutex_unlock) movs r2, #-1 0: ldrex r1, [r0] teq r1, #0 @ Just one hold lock? bne 1f strex r1, r2, [r0] teq r1, #0 @ 'strex' success? bne 0b dmb @ ARMv7-M ARM, A3.4.6 movs r0, #0 @ it also update EQ flag 1: itt ne movne r1, #SYS_PTHREAD_MUTEX_UNLOCK svcne #1 bx lr ENDPROC(pthread_mutex_unlock) ================================================ FILE: mk/cmsis.mk ================================================ SVN_REV = 27441 ARM_CMSIS_ASSETS = \ mbed_toolchain.h \ mbed_preprocessor.h \ mbed_assert.h ARM_CMSIS_ASSETS := $(addprefix $(CMSIS)/util/, $(ARM_CMSIS_ASSETS)) $(CMSIS): @mkdir -p $@ $(CMSIS)/$(PLAT): $(CMSIS)/arm $(CMSIS)/TARGET_STM $(ARM_CMSIS_ASSETS) $(CMSIS)/arm: svn export -r$(SVN_REV) -q --force https://github.com/ARMmbed/mbed-os/trunk/cmsis/ $(CMSIS)/arm $(CMSIS)/TARGET_STM: svn export -r$(SVN_REV) -q --force https://github.com/ARMmbed/mbed-os/trunk/targets/TARGET_STM/ $(CMSIS)/TARGET_STM $(ARM_CMSIS_ASSETS): $(VECHO) " WGET\t\t$@\n" $(Q)$(WGET) -q https://raw.github.com/ARMmbed/mbed-os/master/platform/include/platform/$(notdir $@) -P $(CMSIS)/platform ================================================ FILE: mk/flags.mk ================================================ CROSS_COMPILE ?= arm-none-eabi- CC = $(CROSS_COMPILE)gcc AS = $(CROSS_COMPILE)as AR = $(CROSS_COMPILE)ar OBJCOPY = $(CROSS_COMPILE)objcopy GDB = $(CROSS_COMPILE)gdb HOSTCC = gcc WGET = wget PYTHON ?= python QEMU_SYSTEM_ARM ?= qemu-system-arm # FIXME: configurable via menuconfig or command line CFLAGS_OPT = -Os # -flto CFLAGS += \ -std=c99 \ -W -Wall \ -Iinclude -Iinclude/libc -I. \ -I$(CMSIS)/arm -I$(CMSIS)/$(PLAT) -I$(CMSIS)/$(PLAT)/hal \ -Iinclude/kernel \ -D_POSIX_THREADS=1 -D_POSIX_TIMERS=1 -D_POSIX_REALTIME_SIGNALS=1 \ -Wno-main -fdiagnostics-color \ -ffunction-sections -fdata-sections -ggdb \ $(CFLAGS_OPT) # FIXME: make Piko-specific build options configurable CFLAGS += \ -D CONFIG_KERNEL_STACK_CHECKING LDFLAGS += \ -nostartfiles -specs=nano.specs \ -Wl,-Map=$(NAME).map -Wl,-Tpiko.lds -Wl,--gc-sections CFLAGS += -mthumb -mcpu=$(CPU) LDFLAGS += -mthumb -march=$(ARCH) ================================================ FILE: mk/rules.mk ================================================ # Control the build verbosity ifeq ("$(VERBOSE)","1") Q := VECHO = @true else Q := @ VECHO = @printf endif $(NAME).elf: $(OBJS) fs/version.o $(VECHO) " LD\t\t$@\n" $(Q)$(CC) $(LDFLAGS) -o $@ $^ %.o: %.c $(VECHO) " CC\t\t$@\n" $(Q)$(CC) -o $@ $(CFLAGS) -c -D__KERNEL__ -MMD -MF $@.d $< %.o: %.S $(VECHO) " AS\t\t$@\n" $(Q)$(CC) -o $@ $(CFLAGS) -c $< %.lds: %.lds.S $(VECHO) " HOSTCC\t$@\n" $(Q)$(HOSTCC) -E -P -Iinclude -DROMSZ=$(ROMSZ) -DRAMSZ=$(RAMSZ) -o $@ $< kernel/syscall.c: include/kernel/syscalls.h $(VECHO) " GEN\t\t$@\n" $(Q)$(PYTHON) scripts/gen-syscalls.py --source > $@ include/kernel/syscalls.h: $(VECHO) " GEN\t\t$@\n" $(Q)$(PYTHON) scripts/gen-syscalls.py --header > $@ fs/version: $(VECHO) " GEN\t\t$@\n" $(Q)python3 scripts/gen-proc-version.py --cc-version \ --user $(shell whoami) --host $(shell hostname) \ -a $(ARCH) -c $(CPU) -n 'Piko' > $@ fs/version.o: fs/version $(VECHO) " OBJCOPY\t$@\n" $(Q)$(OBJCOPY) -I binary -O elf32-littlearm -B arm \ --rename-section .data=.rodata \ --redefine-sym _binary_$(subst /,_,$<)_start=_version_ptr \ --redefine-sym _binary_$(subst /,_,$<)_size=_version_len \ $< $@ %.bin: %.elf $(VECHO) " OBJCOPY\t$@\n" $(Q)$(OBJCOPY) -Obinary $< $@ ================================================ FILE: piko.lds.S ================================================ OUTPUT_FORMAT("elf32-littlearm") OUTPUT_ARCH(arm) #define RAMORG 0x20000000 MEMORY { rom (rx) : ORIGIN = 0, LENGTH = ROMSZ ram (rwx) : ORIGIN = RAMORG, LENGTH = RAMSZ } SECTIONS { .vector 0 : { PROVIDE(__vector_start__ = .); KEEP(*(.vector)) } > rom .text BLOCK(8) : { PROVIDE(__text_start__ = .); *(.text*) PROVIDE(__text_end__ = .); . = ALIGN(8); PROVIDE(__shell_cmd_start__ = .); KEEP(*(.shell_cmd*)) PROVIDE(__shell_cmd_end__ = .); PROVIDE(__sched_classes_start__ = .); KEEP(*(.sched.class*)) PROVIDE(__sched_classes_end__ = .); PROVIDE(__serial_hook_start__ = .); KEEP(*(.serial.hook*)) PROVIDE(__serial_hook_end__ = .); PROVIDE(__rodata_start__ = .); *(.rodata*) PROVIDE(__rodata_end__ = .); } > rom /* Initialized data (.data* sections) are initially stored in Flash, but need to be copied to a volatile storage for they are not read-only. */ .data : AT (__rodata_end__) { PROVIDE(__data_start__ = .); *(.data*) PROVIDE(__data_end__ = .); } > ram PROVIDE(__data_size__ = SIZEOF(.data)); .bss : { PROVIDE(__bss_start__ = .); *(.bss*) *(COMMON) PROVIDE(__bss_end__ = .); } > ram PROVIDE(__bss_size__ = SIZEOF(.bss)); /* heap for the kernel's malloc */ .heap BLOCK(32) : { PROVIDE(__heap_start__ = .); . += 4k; PROVIDE(__heap_end__ = .); } > ram PROVIDE(__heap_size__ = SIZEOF(.heap)); .pgmem RAMORG + RAMSZ - 32k : { PROVIDE(__pgmem_start__ = .); . += 26k; /* Reserve the last page for the early stack. Must be of the biggest size * such that there will be no coalescing when freeing the page, for lower * booting time. */ PROVIDE(__early_stack_end__ = .); . += 2k; PROVIDE(__early_stack_start__ = .); PROVIDE(__pgmem_end__ = .); } > ram PROVIDE(__pgmem_size__ = SIZEOF(.pgmem)); .mtdram : { PROVIDE(__mtdram_start__ = .); . += 4k; } > ram PROVIDE(__mtdram_size__ = SIZEOF(.mtdram)); /DISCARD/ : { *(.ARM.exidx) *(.ARM.attributes) } } ================================================ FILE: platform/f429disco/Makefile ================================================ CPU = cortex-m4 ARCH = armv7-m ROMSZ = 2048k RAMSZ = 192k CFLAGS += \ -Iplatform/f429disco \ -I$(CMSIS)\ -I$(CMSIS)/platform \ -I$(CMSIS)/arm \ -I$(CMSIS)/arm/TARGET_CORTEX_M \ -I$(CMSIS)/arm/TARGET_CORTEX_M/TOOLCHAIN_GCC \ -I$(CMSIS)/TARGET_STM \ -I$(CMSIS)/TARGET_STM//TARGET_STM32F4 \ -I$(CMSIS)/TARGET_STM//TARGET_STM32F4/device \ -I$(CMSIS)/TARGET_STM//TARGET_STM32F4/TARGET_STM32F429xI \ -I$(CMSIS)/TARGET_STM//TARGET_STM32F4/TARGET_STM32F429xI/device \ -I$(CMSIS)/TARGET_STM//TARGET_STM32F4/TARGET_STM32F429xI/TARGET_DISCO_F429ZI CSRC += \ platform/f429disco/halt.c \ platform/f429disco/init.c \ platform/f429disco/uart.c # CMSIS files ## STM32 HAL CSRC += $(CMSIS)/TARGET_STM/TARGET_STM32F4/device/system_stm32f4xx.c CSRC += $(CMSIS)/TARGET_STM/TARGET_STM32F4/device/stm32f4xx_hal_rcc.c CSRC += $(CMSIS)/TARGET_STM/TARGET_STM32F4/device/stm32f4xx_hal_rcc_ex.c CSRC += $(CMSIS)/TARGET_STM/TARGET_STM32F4/device/stm32f4xx_hal_gpio.c CSRC += $(CMSIS)/TARGET_STM/TARGET_STM32F4/device/stm32f4xx_hal_uart.c ## SystemInit() CSRC += $(CMSIS)/TARGET_STM/TARGET_STM32F4/TARGET_STM32F429xI/TARGET_DISCO_F429ZI/system_clock.c # Timer driver files CSRC += drivers/timer/systick.c # Serial driver files CFLAGS += -Idrivers/serial CSRC += drivers/serial/stm32-uart.c ================================================ FILE: platform/f429disco/build.mk ================================================ run: $(NAME).bin openocd -f interface/stlink-v2.cfg \ -f target/stm32f4x_stlink.cfg \ -c "init" \ -c "reset init" \ -c "reset halt" -c "reset run" st-flash: $(NAME).bin st-flash --reset write \ $(NAME).bin 0x08000000 dbg: $(NAME).bin openocd -f board/stm32f429discovery.cfg gdb: $(NAME).elf $(Q)$(GDB) -q \ $< -ex "target remote :3333" \ -ex "monitor reset halt" wakeup: openocd -f interface/stlink-v2.cfg \ -f target/stm32f4x_stlink.cfg \ -c "init" \ -c "reset init" \ -c "reset run" \ -c "shutdown" ================================================ FILE: platform/f429disco/halt.c ================================================ #include void __platform_halt(void) { v7m_semihost_exit(0); } ================================================ FILE: platform/f429disco/init.c ================================================ #include #include #include "platform.h" #define CPU_FREQ_IN_HZ 168000000 #define SYSTICK_FREQ_IN_HZ 1000 #define SYSTICK_PERIOD_IN_MSECS (SYSTICK_FREQ_IN_HZ / 1000) struct timer_operations; void config_timer_operations(struct timer_operations *tops); extern struct timer_operations systick_tops; __weak void __platform_init(void) { config_timer_operations(&systick_tops); /* SysTick running at 1kHz */ SysTick_Config(CPU_FREQ_IN_HZ / SYSTICK_FREQ_IN_HZ); } __weak void __platform_halt(void) { for (;;) ; } ================================================ FILE: platform/f429disco/platform.h ================================================ #ifndef _PLATFORM_STM32_PLATFORM_H #define _PLATFORM_STM32_PLATFORM_H #include void __platform_init(void); void __platform_halt(void); /* Default USART for output */ #define USARTx USART1 #define USARTx_IRQn USART1_IRQn #endif /* !_PLATFORM_STM32_PLATFORM_H */ ================================================ FILE: platform/f429disco/uart.c ================================================ #include #include #include "platform.h" #include "kernel/kernel.h" #include "stm32-uart.h" #define STM32_USART_MAX 8 static void uart_port_setup(struct stm32_uart_port *port) { /* Enable peripherals and GPIO Clocks */ /* Enable GPIO TX/RX clock */ port->gpio_tx_clk_enable(); port->gpio_rx_clk_enable(); /* Enable USART/UART clock */ port->uart_clk_enable(); } static void uart_port_init(struct stm32_uart_port *port) { /* GPIO initialized with USART/UART Tx/Rx configuration */ port->gpio_init(port->gpio_tx, &port->gpio_tx_init_info); port->gpio_init(port->gpio_rx, &port->gpio_rx_init_info); /* UART init */ port->uart_init(&port->uart_init_info); } void uart_init(void) { // clang-format off struct stm32_uart_port ports[STM32_USART_MAX] = { /* [0] USART1 */ [0] = { .gpio_tx = GPIOA, .gpio_rx = GPIOA, .gpio_tx_init_info = { .Pin = GPIO_PIN_9, .Mode = GPIO_MODE_AF_PP, .Pull = GPIO_NOPULL, .Speed = GPIO_SPEED_FAST, .Alternate = GPIO_AF7_USART1, }, .gpio_rx_init_info = { .Pin = GPIO_PIN_10, .Mode = GPIO_MODE_AF_PP, .Pull = GPIO_NOPULL, .Speed = GPIO_SPEED_FAST, .Alternate = GPIO_AF7_USART1, }, /* UART2 configured as follow: - Word Length = 8 Bits - Stop Bit = One Stop bit - Parity = None - BaudRate = 9600 baud - Hardware flow control disabled (RTS and CTS signals) */ .uart_init_info = { .Instance = USART1, .Init = { .BaudRate = 115200, .WordLength = UART_WORDLENGTH_8B, .StopBits = UART_STOPBITS_1, .Parity = UART_PARITY_NONE, .HwFlowCtl = UART_HWCONTROL_NONE, .Mode = UART_MODE_TX_RX | UART_IT_RXNE, .OverSampling = UART_OVERSAMPLING_16, }, }, .gpio_init = HAL_GPIO_Init, .uart_init = HAL_UART_Init, .gpio_tx_clk_enable = PLAT_EVAL(__HAL_RCC_GPIOA_CLK_ENABLE()), .gpio_rx_clk_enable = PLAT_EVAL(__HAL_RCC_GPIOA_CLK_ENABLE()), .uart_clk_enable = PLAT_EVAL(__HAL_RCC_USART1_CLK_ENABLE()), }, /* [1] USART2 */ /* [2] USART3 */ /* [3] UART4 */ /* [4] UART5 */ /* [5] USART6 */ /* [6] UART7 */ /* [7] UART8 */ }; // clang-format on /* Specify initialize order */ int init_order[] = {0}; /* USART/UART port setup */ for (size_t i = 0; i < ARRAY_SIZE(init_order); i++) uart_port_setup(&ports[init_order[i]]); /* USART/UART port init */ for (size_t i = 0; i < ARRAY_SIZE(init_order); i++) uart_port_init(&ports[init_order[i]]); } ================================================ FILE: platform/stm32p103/Makefile ================================================ CPU = cortex-m3 ARCH = armv7-m ROMSZ = 128k RAMSZ = 64k CFLAGS += \ -Iplatform/stm32p103 \ -I$(CMSIS)\ -I$(CMSIS)/platform \ -I$(CMSIS)/arm \ -I$(CMSIS)/arm/TARGET_CORTEX_M \ -I$(CMSIS)/arm/TARGET_CORTEX_M/TOOLCHAIN_GCC \ -I$(CMSIS)/TARGET_STM \ -I$(CMSIS)/TARGET_STM//TARGET_STM32F1 \ -I$(CMSIS)/TARGET_STM//TARGET_STM32F1/device \ -I$(CMSIS)/TARGET_STM//TARGET_STM32F1/TARGET_NUCLEO_F103RB \ -I$(CMSIS)/TARGET_STM//TARGET_STM32F1/TARGET_NUCLEO_F103RB/device CSRC += \ platform/stm32p103/halt.c \ platform/stm32p103/init.c \ platform/stm32p103/uart.c # CMSIS files ## STM32 HAL CSRC += $(CMSIS)/TARGET_STM/TARGET_STM32F1/device/system_stm32f1xx.c CSRC += $(CMSIS)/TARGET_STM/TARGET_STM32F1/device/stm32f1xx_hal_rcc.c CSRC += $(CMSIS)/TARGET_STM/TARGET_STM32F1/device/stm32f1xx_hal_rcc_ex.c CSRC += $(CMSIS)/TARGET_STM/TARGET_STM32F1/device/stm32f1xx_hal_gpio.c CSRC += $(CMSIS)/TARGET_STM/TARGET_STM32F1/device/stm32f1xx_hal_uart.c ## SystemInit() CSRC += $(CMSIS)/TARGET_STM/TARGET_STM32F1/TARGET_NUCLEO_F103RB/device/system_clock.c # Timer driver files CSRC += drivers/timer/systick.c # Serial driver files CFLAGS += -Idrivers/serial CSRC += drivers/serial/stm32-uart.c ================================================ FILE: platform/stm32p103/build.mk ================================================ ifeq ($(shell lsb_release -c -s),trusty) REDIRECT_SERIAL = -serial stdio endif run: $(NAME).bin $(Q)$(QEMU_SYSTEM_ARM) \ -semihosting \ $(REDIRECT_SERIAL) \ -nographic \ -cpu cortex-m3 \ -machine stm32-p103 \ -kernel $< dbg: $(NAME).bin $(Q)$(QEMU_SYSTEM_ARM) \ -semihosting \ $(REDIRECT_SERIAL) \ -nographic \ -cpu cortex-m3 \ -machine stm32-p103 \ -kernel $< \ -S -s gdb: $(NAME).elf $(Q)$(GDB) -q \ $< -ex "target remote :1234" ================================================ FILE: platform/stm32p103/halt.c ================================================ #include void __platform_halt(void) { v7m_semihost_exit(0); } ================================================ FILE: platform/stm32p103/init.c ================================================ #include #include "platform.h" #define CPU_FREQ_IN_HZ 72000000 #define SYSTICK_FREQ_IN_HZ 1000 #define SYSTICK_PERIOD_IN_MSECS (SYSTICK_FREQ_IN_HZ / 1000) struct timer_operations; void config_timer_operations(struct timer_operations *tops); extern struct timer_operations systick_tops; void rcc_clock_init(void); void __uart_enable(void); __weak void __platform_init(void) { config_timer_operations(&systick_tops); /* SysTick running at 1kHz */ SysTick_Config(CPU_FREQ_IN_HZ / SYSTICK_FREQ_IN_HZ); } __weak void __platform_halt(void) { for (;;) ; } ================================================ FILE: platform/stm32p103/platform.h ================================================ #ifndef _PLATFORM_STM32_PLATFORM_H #define _PLATFORM_STM32_PLATFORM_H #include void __platform_init(void); void __platform_halt(void); /* Default USART for outputing */ #define USARTx USART2 #define USARTx_IRQn USART2_IRQn #endif /* !_PLATFORM_STM32_PLATFORM_H */ ================================================ FILE: platform/stm32p103/uart.c ================================================ #include #include #include "platform.h" #include "kernel/kernel.h" #include "stm32-uart.h" #define STM32_USART_MAX 3 static void uart_port_setup(struct stm32_uart_port *port) { /* Enable peripherals and GPIO Clocks */ /* Enable GPIO TX/RX clock */ port->gpio_tx_clk_enable(); port->gpio_rx_clk_enable(); /* Enable USART/UART clock */ port->uart_clk_enable(); } static void uart_port_init(struct stm32_uart_port *port) { /* GPIO initialized with USART/UART Tx/Rx configuration */ port->gpio_init(port->gpio_tx, &port->gpio_tx_init_info); port->gpio_init(port->gpio_rx, &port->gpio_rx_init_info); /* UART init */ port->uart_init(&port->uart_init_info); } void uart_init(void) { // clang-format off struct stm32_uart_port ports[STM32_USART_MAX] = { /* [0] USART1 */ /* [1] USART2 */ [1] = { .gpio_tx = GPIOA, .gpio_rx = GPIOA, .gpio_tx_init_info = { .Pin = GPIO_PIN_2, .Mode = GPIO_MODE_AF_PP, .Pull = GPIO_PULLUP, .Speed = GPIO_SPEED_FREQ_HIGH, }, .gpio_rx_init_info = { .Pin = GPIO_PIN_3, .Mode = GPIO_MODE_INPUT, .Pull = GPIO_PULLUP, .Speed = GPIO_SPEED_FREQ_HIGH, }, /* UART2 configured as follow: - Word Length = 8 Bits - Stop Bit = One Stop bit - Parity = None - BaudRate = 9600 baud - Hardware flow control disabled (RTS and CTS signals) */ .uart_init_info = { .Instance = USART2, .Init = { .BaudRate = 115200, .WordLength = UART_WORDLENGTH_8B, .StopBits = UART_STOPBITS_1, .Parity = UART_PARITY_NONE, .HwFlowCtl = UART_HWCONTROL_NONE, .Mode = UART_MODE_TX_RX | UART_IT_RXNE, .OverSampling = UART_OVERSAMPLING_16, }, }, .gpio_init = HAL_GPIO_Init, .uart_init = HAL_UART_Init, .gpio_tx_clk_enable = PLAT_EVAL(__HAL_RCC_GPIOA_CLK_ENABLE()), .gpio_rx_clk_enable = PLAT_EVAL(__HAL_RCC_GPIOA_CLK_ENABLE()), .uart_clk_enable = PLAT_EVAL(__HAL_RCC_USART2_CLK_ENABLE()), }, /* [3] USART2 */ }; // clang-format on /* Specify initialize order */ int init_order[] = {1}; /* USART/UART port setup */ for (size_t i = 0; i < ARRAY_SIZE(init_order); i++) uart_port_setup(&ports[init_order[i]]); /* USART/UART port init */ for (size_t i = 0; i < ARRAY_SIZE(init_order); i++) uart_port_init(&ports[init_order[i]]); } ================================================ FILE: scripts/gen-proc-version.py ================================================ import subprocess import string from datetime import datetime import argparse def run_cmd(cmd): res = subprocess.run(cmd, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) if res.returncode == 0: return res.stdout.strip() return 'unknown' def cc_version(cc): cmd = [ cc, '--version' ] return run_cmd(cmd).splitlines()[0] parser = argparse.ArgumentParser() parser.add_argument("-n", "--name", action="store", dest="name") parser.add_argument("-a", "--arch", action="store", dest="arch") parser.add_argument("-c", "--cpu", action="store", dest="cpu") parser.add_argument("-u", "--user", action="store", dest="user", default="unknown") parser.add_argument( "--host", action="store", dest="host", default="unknown") parser.add_argument( "--major", action="store", dest="major", default=0) parser.add_argument( "--minor", action="store", dest="minor", default=0) parser.add_argument( "--micro", action="store", dest="micro", default=0) parser.add_argument( "--cc-version", action="store_true", dest="cc_version") args = parser.parse_args() version = '.'.join(map(lambda x: str(x), [args.major, args.minor, args.micro])) hostname = '@'.join([args.user, args.host]) platform = ', '.join(filter(lambda x: x is not None, [args.arch, args.cpu])) print('{} version {} ({}) ({}) #{}'.format(args.name, version, hostname, platform, datetime.now().strftime("%c"))) if (args.cc_version): print(cc_version('arm-none-eabi-gcc')) ================================================ FILE: scripts/gen-syscalls.py ================================================ #!/usr/bin/env python import string import argparse xs = [ # 'pthread_exit', 'pthread_self', 'pthread_yield', 'pthread_create', 'pthread_join', 'pthread_detach', 'pthread_mutex_lock', 'pthread_mutex_unlock', 'pthread_cond_signal', 'pthread_cond_wait', # 'timer_create', 'timer_settime', 'timer_gettime', # 'msleep', 'sysconf', # 'sigaction', 'kill', # , , ... 'open', 'close', 'read', 'write', 'lseek', 'stat', 'mount', 'readdir_r', # 'getpid', # 'mmap', 'munmap', ] parser = argparse.ArgumentParser() parser.add_argument("--header", action="store_true", dest="header") parser.add_argument("--source", action="store_true", dest="source") args = parser.parse_args() import datetime print('// GENERATED. DO NOT EDIT FROM HERE!') print('// Change definitions in scripts/gen-syscalls.py') print('// Created on ' + datetime.datetime.now().strftime("%Y-%m-%d %H:%M")) print('') #for x in list(enumerate(xs)): # name = 'sys_' + x[1] # print('[{}] = {},'.format(name.upper(), name)) if (args.header): print('#ifndef KERNEL_SYSCALLS_H') print('#define KERNEL_SYSCALLS_H') for x in list(enumerate(xs)): name = 'sys_' + x[1] print('#define {} {}'.format(name.upper(), x[0])) print('') print('#endif /* !KERNEL_SYSCALLS_H */') if (args.source): print('#include ') print('') for x in list(enumerate(xs)): name = 'sys_' + x[1] print('int {}();'.format(name)) print('#define SYS_MAX 48') print('') print('void *syscall_vect[SYS_MAX] = {') for x in list(enumerate(xs)): name = 'sys_' + x[1] print(' [{}] = {},'.format(name.upper(), name)) print('};'); print('') print('int syscall_register(unsigned ix, void *(*fn)()) {') print(' if (ix >= SYS_MAX) return -1;') print(' syscall_vect[ix] = fn;') print('return 0;') print('}'); ================================================ FILE: scripts/rstlint.py ================================================ #!/usr/bin/env python3 # -*- coding: utf-8 -*- # # Copyright (C) 2009 Georg Brandl # # Check for stylistic and formal issues in .rst and .py # files included in the documentation. # # TODO: - wrong versions in versionadded/changed # - wrong markup after versionchanged directive import os import re import sys import getopt from os.path import join, splitext, abspath, exists from collections import defaultdict directives = [ # standard docutils ones 'admonition', 'attention', 'caution', 'class', 'compound', 'container', 'contents', 'csv-table', 'danger', 'date', 'default-role', 'epigraph', 'error', 'figure', 'footer', 'header', 'highlights', 'hint', 'image', 'important', 'include', 'line-block', 'list-table', 'meta', 'note', 'parsed-literal', 'pull-quote', 'raw', 'replace', 'restructuredtext-test-directive', 'role', 'rubric', 'sectnum', 'sidebar', 'table', 'target-notes', 'tip', 'title', 'topic', 'unicode', 'warning', # Sphinx and Python docs custom ones 'acks', 'attribute', 'autoattribute', 'autoclass', 'autodata', 'autoexception', 'autofunction', 'automethod', 'automodule', 'centered', 'cfunction', 'class', 'classmethod', 'cmacro', 'cmdoption', 'cmember', 'code-block', 'confval', 'cssclass', 'ctype', 'currentmodule', 'cvar', 'data', 'decorator', 'decoratormethod', 'deprecated-removed', 'deprecated(?!-removed)', 'describe', 'directive', 'doctest', 'envvar', 'event', 'exception', 'function', 'glossary', 'highlight', 'highlightlang', 'impl-detail', 'index', 'literalinclude', 'method', 'miscnews', 'module', 'moduleauthor', 'opcode', 'pdbcommand', 'productionlist', 'program', 'role', 'sectionauthor', 'seealso', 'sourcecode', 'staticmethod', 'tabularcolumns', 'testcode', 'testoutput', 'testsetup', 'toctree', 'todo', 'todolist', 'versionadded', 'versionchanged' ] all_directives = '(' + '|'.join(directives) + ')' seems_directive_re = re.compile(r'(? 81: # don't complain about tables, links and function signatures if line.lstrip()[0] not in '+|' and \ 'http://' not in line and \ not line.lstrip().startswith(('.. function', '.. method', '.. cfunction')): yield lno+1, "line too long" @checker('.html', severity=2, falsepositives=True) def check_leaked_markup(fn, lines): """Check HTML files for leaked reST markup; this only works if the HTML files have been built. """ for lno, line in enumerate(lines): if leaked_markup_re.search(line): yield lno+1, 'possibly leaked markup: %r' % line def main(argv): usage = '''\ Usage: %s [-v] [-f] [-s sev] [-i path]* [path] Options: -v verbose (print all checked file names) -f enable checkers that yield many false positives -s sev only show problems with severity >= sev -i path ignore subdir or file path ''' % argv[0] try: gopts, args = getopt.getopt(argv[1:], 'vfs:i:') except getopt.GetoptError: print(usage) return 2 verbose = False severity = 1 ignore = [] falsepos = False for opt, val in gopts: if opt == '-v': verbose = True elif opt == '-f': falsepos = True elif opt == '-s': severity = int(val) elif opt == '-i': ignore.append(abspath(val)) if len(args) == 0: path = '.' elif len(args) == 1: path = args[0] else: print(usage) return 2 if not exists(path): print('Error: path %s does not exist' % path) return 2 count = defaultdict(int) for root, dirs, files in os.walk(path): # ignore subdirs in ignore list if abspath(root) in ignore: del dirs[:] continue for fn in files: fn = join(root, fn) if fn[:2] == './': fn = fn[2:] # ignore files in ignore list if abspath(fn) in ignore: continue ext = splitext(fn)[1] checkerlist = checkers.get(ext, None) if not checkerlist: continue if verbose: print('Checking %s...' % fn) try: with open(fn, 'r', encoding='utf-8') as f: lines = list(f) except (IOError, OSError) as err: print('%s: cannot open: %s' % (fn, err)) count[4] += 1 continue for checker in checkerlist: if checker.falsepositives and not falsepos: continue csev = checker.severity if csev >= severity: for lno, msg in checker(fn, lines): print('[%d] %s:%d: %s' % (csev, fn, lno, msg)) count[csev] += 1 if verbose: print() if not count: if severity > 1: print('No problems with severity >= %d found.' % severity) else: print('No problems found.') else: for severity in sorted(count): number = count[severity] print('%d problem%s with severity %d found.' % (number, number > 1 and 's' or '', severity)) return int(bool(count)) if __name__ == '__main__': sys.exit(main(sys.argv)) ================================================ FILE: tests/Makefile ================================================ CSRC = $(wildcard tests/$(TEST)/*.c) SSRC = $(wildcard tests/$(TEST)/*.S) TEST_OBJ = $(wildcard tests/$(TEST)/*.o) LIB_OBJ = $(wildcard tests/lib/*.o) CFLAGS = -Itests/lib # Build a romFS drive if there is a /data directory at the root # of the testcase directory. ifneq ("$(wildcard tests/$(TEST)/data)","") OBJS = tests/$(TEST)/sda1.o endif include Makefile %sda1: $(VECHO) "GENROMFS $@" $(Q)genromfs -f tests/$(TEST)/sda1 -d tests/$(TEST)/data -V sda1 %sda1.o: %sda1 $(VECHO) "BUILDFS\t$@" $(Q)$(OBJCOPY) -I binary -O elf32-littlearm -B arm \ --rename-section .data=.rodata \ --redefine-sym _binary_$(subst /,_,$<)_start=_binary_sda1_start \ --redefine-sym _binary_$(subst /,_,$<)_end=_binary_sda1_end \ --redefine-sym _binary_$(subst /,_,$<)_size=_binary_sda1_size \ tests/$(TEST)/sda1 tests/$(TEST)/sda1.o clean: rm -f $(TEST_OBJ) $(LIB_OBJ) tests/$(TEST)/sda1 tests/$(TEST)/sda1.o clean_test: rm -f $(NAME).elf $(TEST_OBJ) tests/$(TEST)/sda1 tests/$(TEST)/sda1.o ================================================ FILE: tests/__init__.py ================================================ ================================================ FILE: tests/__main__.py ================================================ import sys if sys.version_info < (3, 5): print ("Only support python version >= 3.5") exit(1) import argparse import tests.runner as runner from tests.runner import PikoTest if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('test', nargs='*') parser.add_argument('-v', '--verbose', action='store_true', help='Verbose output') parser.add_argument('--quiet', action='store_true', default=False, help='no output unless one or more tests fail') parser.add_argument('--debug', action='store_true', help='Run test and wait for gdb connect') parser.add_argument('-p', '--platform', default='stm32p103') parser.add_argument('--qemu', default='qemu-system-arm') parser.add_argument('--cc', default='arm-none-eabi-gcc') parser.add_argument('--timeout', type=int, default=60) parser.add_argument('--slowest', action='store_true', dest='print_slow', help='print the slowest 10 tests') ns = parser.parse_args() if ns.test: PikoTest().main(ns.test, ns) else: # This will include all test runner.main(ns) ================================================ FILE: tests/bitops_1/main.c ================================================ #include #include "unit.h" int main() { unsigned long j; unsigned long k[2] = {0, 1}; /* test first bit set */ for (unsigned long i = 0; i < 32; i++) { j = 1 << i; if (find_first_bit(&j, 32) != i) TEST_EXIT(1); } /* test first zero bit */ for (unsigned long i = 0; i < 32; i++) { j = ~(1 << i); if (find_first_zero_bit(&j, 32) != i) TEST_EXIT(1); } /* test boundaries */ if (find_first_bit(k, 32) != 32) TEST_EXIT(1); if (find_first_bit(k, 33) != 32) TEST_EXIT(1); TEST_EXIT(0); } ================================================ FILE: tests/cond_1/main.c ================================================ /* simple test for condition variable */ #include #include #include #include #include "kernel.h" #include "unit.h" pthread_cond_t cond; pthread_mutex_t mutex; void *threadfunc(void *parm) { (void) parm; pthread_mutex_lock(&mutex); printk("Wait for signal\n"); pthread_cond_wait(&cond, &mutex); printk("Thread awake, finish work!\n"); pthread_mutex_unlock(&mutex); return NULL; } int main() { int rc = 0; pthread_t threadid; printk("Enter Testcase\n"); pthread_cond_init(&cond, NULL); pthread_mutex_init(&mutex, NULL); rc = pthread_create(&threadid, NULL, threadfunc, NULL); if (rc) { printk("failed: can't create new posix thread.\n"); TEST_EXIT(1); } pthread_yield(); printk("Wake up a worker, work to do...\n"); rc = pthread_mutex_lock(&mutex); rc = pthread_cond_signal(&cond); // checkResults("pthread_cond_broadcast()\n", rc); rc = pthread_mutex_unlock(&mutex); // checkResults("pthread_mutex_unlock()\n", rc); // sleep(5); /* Sleep is not a very robust way to serialize threads */ printk("Main completed\n"); TEST_EXIT(0); } ================================================ FILE: tests/cond_2/main.c ================================================ /* http://www.ibm.com/support/knowledgecenter/ssw_i5_54/apis/users_76.htm */ #define _MULTI_THREADED #include #include #include "kernel.h" #include "unit.h" /* For safe condition variable usage, must use a boolean predicate and */ /* a mutex with the condition. */ int workToDo = 0; pthread_cond_t cond; pthread_mutex_t mutex; #define NTHREADS 2 unsigned int msleep(unsigned int msecs); int checkResults(const char *s, int rc) { if (rc) { printk("error: %s\n", s); TEST_EXIT(1); } return 0; } void *threadfunc(void *parm) { int rc; (void) parm; while (1) { /* Usually worker threads will loop on these operations */ rc = pthread_mutex_lock(&mutex); checkResults("pthread_mutex_lock()\n", rc); while (!workToDo) { printk("Thread blocked\n"); rc = pthread_cond_wait(&cond, &mutex); checkResults("pthread_cond_wait()\n", rc); } printk("Thread awake, finish work!\n"); /* Under protection of the lock, complete or remove the work */ /* from whatever worker queue we have. Here it is simply a flag */ workToDo = 0; rc = pthread_mutex_unlock(&mutex); checkResults("pthread_mutex_lock()\n", rc); } return NULL; } int main() { int rc = 0; int i; pthread_t threadid[NTHREADS]; pthread_cond_init(&cond, NULL); pthread_mutex_init(&mutex, NULL); printk("Enter Testcase - %s\n", "IBM pthread_cond_*()"); printk("Create %d threads\n", NTHREADS); for (i = 0; i < NTHREADS; ++i) { rc = pthread_create(&threadid[i], NULL, threadfunc, NULL); checkResults("pthread_create()\n", rc); } msleep(40); /* Sleep is not a very robust way to serialize threads */ for (i = 0; i < 5; ++i) { printk("Wake up a worker, work to do...\n"); rc = pthread_mutex_lock(&mutex); checkResults("pthread_mutex_lock()\n", rc); /* In the real world, all the threads might be busy, and */ /* we would add work to a queue instead of simply using a flag */ /* In that case the boolean predicate might be some boolean */ /* statement like: if (the-queue-contains-work) */ if (workToDo) { printk("Work already present, likely threads are busy\n"); } workToDo = 1; rc = pthread_cond_signal(&cond); checkResults("pthread_cond_broadcast()\n", rc); rc = pthread_mutex_unlock(&mutex); checkResults("pthread_mutex_unlock()\n", rc); msleep(40); /* Sleep is not a very robust way to serialize threads */ } printk("Main completed\n"); TEST_EXIT(0); return 0; } ================================================ FILE: tests/cond_3/main.c ================================================ /* http://www.qnx.com/developers/docs/660/index.jsp?topic=%2Fcom.qnx.doc.neutrino.getting_started%2Ftopic%2Fs1_procs_condvar.html */ /* * cp1.c */ #include #include #include #include "unit.h" int count = 0; int data_ready = 0; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t condvar; extern unsigned int msleep(unsigned int msec); void *consumer(void *notused) { (void) notused; printk("In consumer thread...\n"); while (1) { count++; pthread_mutex_lock(&mutex); while (!data_ready) { pthread_cond_wait(&condvar, &mutex); } // process data printk("consumer: got data from producer\n"); data_ready = 0; pthread_cond_signal(&condvar); pthread_mutex_unlock(&mutex); } } void *producer(void *notused) { (void) notused; printk("In producer thread...\n"); while (1) { // get data from hardware // we'll simulate this with a sleep (1) msleep(180); printk("producer: got data from h/w\n"); pthread_mutex_lock(&mutex); while (data_ready) { pthread_cond_wait(&condvar, &mutex); } data_ready = 1; pthread_cond_signal(&condvar); pthread_mutex_unlock(&mutex); } } int main(void) { printk("Starting consumer/producer example...\n"); pthread_cond_init(&condvar, NULL); // create the producer and consumer threads pthread_create(NULL, NULL, producer, NULL); pthread_create(NULL, NULL, consumer, NULL); while (count < 5) pthread_yield(); printk("Bye-bye!\n"); TEST_EXIT(0); } ================================================ FILE: tests/fs_1/main.c ================================================ /* test /dev/random */ #include #include #include #include "unit.h" int main() { unsigned int n, p = 0; int fd = open("/dev/random", 0); if (fd < 0) { printk("error: failed to open /dev/random\n"); TEST_EXIT(1); } for (int j = 0; j < 1000; j++) { read(fd, &n, 4); if (j < 40) printk("%08x\n", n); if (n == p) { printk("error: got same random number %d twice in a row\n", n); TEST_EXIT(1); } p = n; } TEST_EXIT(0); } ================================================ FILE: tests/fs_2/main.c ================================================ /* test /dev/zero */ #include #include #include #include #include "unit.h" char buf[128]; int main() { int fd = open("/dev/zero", 0); if (fd < 0) { printk("error: failed to open /dev/zero\n"); TEST_EXIT(1); } memset(buf, 0xff, 128); read(fd, &buf, 128); for (int j = 0; j < 128; j++) { if (buf[j]) { printk("error: got a non-zero value\n"); TEST_EXIT(1); } } TEST_EXIT(0); } ================================================ FILE: tests/fs_3/data/id_rsa ================================================ -----BEGIN RSA PRIVATE KEY----- MIIBOgIBAAJBAI/rmTOBknHe2ro+8sUNl1MjNTRopU1/WJTcYvsrFML10Z2IBrFi fc3Q1x92uTvyFU21cn+/ekU8L+8qK+V98fMCAwEAAQJAe4uw+REa6Nt5Ana1KsmP BNRtgO7wMEXIgglqgtipuu3CE9CIZ4OyrvELNbI6RiczGYgMmUHcWEmLuBZ+tjQu 4QIhAORy2CQio1J3O2gGdkk2eY2cLIzoMEdig9su/NILvSqDAiEAoUcD1FeM1iub SiUq3ZDIf1FFTgtv9Si0lzJyBY+IP9ECIHi8zclDWUhDZe1TxP5qwRF74fvS13lS 8tdL3SjyNVcbAiEAhZU3o8r8mWy3DFvqvGiu2V3shK9OhYa4xj9/WAHB/fECIH9u Nq1Ww0stz2Oq7Wpn7ywcNemkot/+sO8l8gxXPcqw -----END RSA PRIVATE KEY----- ================================================ FILE: tests/fs_3/data/id_rsa.pub ================================================ -----BEGIN PUBLIC KEY----- MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAI/rmTOBknHe2ro+8sUNl1MjNTRopU1/ WJTcYvsrFML10Z2IBrFifc3Q1x92uTvyFU21cn+/ekU8L+8qK+V98fMCAwEAAQ== -----END PUBLIC KEY----- ================================================ FILE: tests/fs_3/main.c ================================================ /* test simple romFS */ #include #include #include #include #include #include #include #include "unit.h" extern char _binary_sda1_start; struct mtd_info mtd1; static struct inode inode = { .i_private = &mtd1, }; void flash_init(void) { struct dentry dentry = {.d_inode = &inode, .d_name = "mtd1"}; printk("Creating MTD device %s\n", dentry.d_name); if (mtdram_init_device(&mtd1, &_binary_sda1_start, 1024, dentry.d_name)) printk("error: mtdram init device failed\n"); vfs_link(NULL, dev_inode(), &dentry); } int main() { int fd; char buffer[128]; init_tmpfs_inode(&inode); flash_init(); mount("/dev/mtd1", "/dev/flash", "romfs", 0, 0); fd = open("/dev/flash/id_rsa.pub", 0); if (fd < 0) { printk("error: failed to open /data/id_rsa.pub\n"); TEST_EXIT(1); } memset(buffer, 0, 128); read(fd, buffer, 27); printk("read(): %s\n", buffer); if (strncmp(buffer, "-----BEGIN PUBLIC KEY-----", 26)) TEST_EXIT(1); memset(buffer, 0, 128); read(fd, buffer, 65); printk("read(): %s\n", buffer); if (strncmp( buffer, "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAI/rmTOBknHe2ro+8sUNl1MjNTRopU1/", 64)) TEST_EXIT(1); memset(buffer, 0, 128); read(fd, buffer, 65); printk("read(): %s\n", buffer); if (strncmp( buffer, "WJTcYvsrFML10Z2IBrFifc3Q1x92uTvyFU21cn+/ekU8L+8qK+V98fMCAwEAAQ==", 64)) TEST_EXIT(1); /* rewind the file */ lseek(fd, 0, SEEK_SET); memset(buffer, 0, 128); read(fd, buffer, 27); printk("read(): %s\n", buffer); if (strncmp(buffer, "-----BEGIN PUBLIC KEY-----", 26)) TEST_EXIT(1); TEST_EXIT(0); } ================================================ FILE: tests/fs_4/data/id_rsa ================================================ -----BEGIN RSA PRIVATE KEY----- MIIBOgIBAAJBAI/rmTOBknHe2ro+8sUNl1MjNTRopU1/WJTcYvsrFML10Z2IBrFi fc3Q1x92uTvyFU21cn+/ekU8L+8qK+V98fMCAwEAAQJAe4uw+REa6Nt5Ana1KsmP BNRtgO7wMEXIgglqgtipuu3CE9CIZ4OyrvELNbI6RiczGYgMmUHcWEmLuBZ+tjQu 4QIhAORy2CQio1J3O2gGdkk2eY2cLIzoMEdig9su/NILvSqDAiEAoUcD1FeM1iub SiUq3ZDIf1FFTgtv9Si0lzJyBY+IP9ECIHi8zclDWUhDZe1TxP5qwRF74fvS13lS 8tdL3SjyNVcbAiEAhZU3o8r8mWy3DFvqvGiu2V3shK9OhYa4xj9/WAHB/fECIH9u Nq1Ww0stz2Oq7Wpn7ywcNemkot/+sO8l8gxXPcqw -----END RSA PRIVATE KEY----- ================================================ FILE: tests/fs_4/data/id_rsa.pub ================================================ -----BEGIN PUBLIC KEY----- MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAI/rmTOBknHe2ro+8sUNl1MjNTRopU1/ WJTcYvsrFML10Z2IBrFifc3Q1x92uTvyFU21cn+/ekU8L+8qK+V98fMCAwEAAQ== -----END PUBLIC KEY----- ================================================ FILE: tests/fs_4/main.c ================================================ /* test overreading a file */ #include #include #include #include #include #include #include #include "unit.h" extern char _binary_sda1_start; struct mtd_info mtd1; static struct inode inode = { .i_private = &mtd1, }; void flash_init(void) { struct dentry dentry = {.d_inode = &inode, .d_name = "mtd1"}; printk("Creating MTD device %s\n", dentry.d_name); if (mtdram_init_device(&mtd1, &_binary_sda1_start, 1024, dentry.d_name)) printk("error: mtdram init device failed\n"); vfs_link(NULL, dev_inode(), &dentry); } int main() { int fd; char buffer[256]; memset(buffer, 0, 256); init_tmpfs_inode(&inode); flash_init(); mount("/dev/mtd1", "/dev/flash", "romfs", 0, 0); /* file contains -Lorem ipsum\n- */ fd = open("/dev/flash/id_rsa.pub", 0); if (fd < 0) { printk("error: failed to open /data/id_rsa.pub\n"); TEST_EXIT(1); } /* overread the file */ int r = read(fd, buffer, 256); if (r != 182) { printk("error: read incorrect number of char (%d)\n", r); TEST_EXIT(1); } if (strncmp(buffer, "-----BEGIN PUBLIC KEY-----\n", 26)) TEST_EXIT(1); /* try to read again */ r = read(fd, buffer, 1); if (r != 0) { printk("error: read incorrect number of char (%d)\n", r); TEST_EXIT(1); } TEST_EXIT(0); } ================================================ FILE: tests/fs_5/main.c ================================================ /* test multiple opening/closing /dev/random */ #include #include #include #include #include "unit.h" int main() { int fd; for (int i = 0; i < 10000; i++) { /* printk("% 3d/9999\n", i); */ fd = open("/dev/random", 0); if (fd < 0) { printk("error: failed to open /dev/random\n"); TEST_EXIT(1); } if (close(fd)) { printk("error: failed to close /dev/random\n"); TEST_EXIT(1); } } TEST_EXIT(0); } ================================================ FILE: tests/fs_6/data/id_rsa ================================================ -----BEGIN RSA PRIVATE KEY----- MIIBOgIBAAJBAI/rmTOBknHe2ro+8sUNl1MjNTRopU1/WJTcYvsrFML10Z2IBrFi fc3Q1x92uTvyFU21cn+/ekU8L+8qK+V98fMCAwEAAQJAe4uw+REa6Nt5Ana1KsmP BNRtgO7wMEXIgglqgtipuu3CE9CIZ4OyrvELNbI6RiczGYgMmUHcWEmLuBZ+tjQu 4QIhAORy2CQio1J3O2gGdkk2eY2cLIzoMEdig9su/NILvSqDAiEAoUcD1FeM1iub SiUq3ZDIf1FFTgtv9Si0lzJyBY+IP9ECIHi8zclDWUhDZe1TxP5qwRF74fvS13lS 8tdL3SjyNVcbAiEAhZU3o8r8mWy3DFvqvGiu2V3shK9OhYa4xj9/WAHB/fECIH9u Nq1Ww0stz2Oq7Wpn7ywcNemkot/+sO8l8gxXPcqw -----END RSA PRIVATE KEY----- ================================================ FILE: tests/fs_6/data/id_rsa.pub ================================================ -----BEGIN PUBLIC KEY----- MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAI/rmTOBknHe2ro+8sUNl1MjNTRopU1/ WJTcYvsrFML10Z2IBrFifc3Q1x92uTvyFU21cn+/ekU8L+8qK+V98fMCAwEAAQ== -----END PUBLIC KEY----- ================================================ FILE: tests/fs_6/main.c ================================================ /* open files in romFS multiple times */ #include #include #include #include #include #include "unit.h" extern char _binary_sda1_start; struct mtd_info mtd1; static struct inode inode = { .i_private = &mtd1, }; void flash_init(void) { struct dentry dentry = {.d_inode = &inode, .d_name = "mtd1"}; printk("Creating MTD device %s\n", dentry.d_name); if (mtdram_init_device(&mtd1, &_binary_sda1_start, 1024, dentry.d_name)) printk("error: mtdram init device failed\n"); vfs_link(NULL, dev_inode(), &dentry); } int main() { int fd1, fd2; init_tmpfs_inode(&inode); flash_init(); mount("/dev/mtd1", "/dev/flash", "romfs", 0, 0); for (int i = 0; i < 1000; i++) { fd1 = open("/dev/flash/id_rsa", 0); fd2 = open("/dev/flash/id_rsa.pub", 0); if (fd1 < 0 || fd2 < 0) { printk("error: failed to open file in flash\n"); TEST_EXIT(1); } close(fd1); close(fd2); } TEST_EXIT(0); } ================================================ FILE: tests/fs_7/data/.ssh/id_rsa ================================================ -----BEGIN RSA PRIVATE KEY----- MIIBOgIBAAJBAI/rmTOBknHe2ro+8sUNl1MjNTRopU1/WJTcYvsrFML10Z2IBrFi fc3Q1x92uTvyFU21cn+/ekU8L+8qK+V98fMCAwEAAQJAe4uw+REa6Nt5Ana1KsmP BNRtgO7wMEXIgglqgtipuu3CE9CIZ4OyrvELNbI6RiczGYgMmUHcWEmLuBZ+tjQu 4QIhAORy2CQio1J3O2gGdkk2eY2cLIzoMEdig9su/NILvSqDAiEAoUcD1FeM1iub SiUq3ZDIf1FFTgtv9Si0lzJyBY+IP9ECIHi8zclDWUhDZe1TxP5qwRF74fvS13lS 8tdL3SjyNVcbAiEAhZU3o8r8mWy3DFvqvGiu2V3shK9OhYa4xj9/WAHB/fECIH9u Nq1Ww0stz2Oq7Wpn7ywcNemkot/+sO8l8gxXPcqw -----END RSA PRIVATE KEY----- ================================================ FILE: tests/fs_7/data/.ssh/id_rsa.pub ================================================ -----BEGIN PUBLIC KEY----- MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAI/rmTOBknHe2ro+8sUNl1MjNTRopU1/ WJTcYvsrFML10Z2IBrFifc3Q1x92uTvyFU21cn+/ekU8L+8qK+V98fMCAwEAAQ== -----END PUBLIC KEY----- ================================================ FILE: tests/fs_7/main.c ================================================ /* test directory hierarchy in RomFS */ #include #include #include #include #include #include #include #include "unit.h" extern char _binary_sda1_start; struct mtd_info mtd1; static struct inode inode = { .i_private = &mtd1, }; void flash_init(void) { struct dentry dentry = {.d_inode = &inode, .d_name = "mtd1"}; printk("Creating MTD device %s\n", dentry.d_name); if (mtdram_init_device(&mtd1, &_binary_sda1_start, 1024, dentry.d_name)) printk("error: mtdram init device failed\n"); vfs_link(NULL, dev_inode(), &dentry); } int main() { int fd; const char filename[] = "/dev/flash/.ssh/id_rsa.pub"; char buffer[32]; init_tmpfs_inode(&inode); flash_init(); mount("/dev/mtd1", "/dev/flash", "romfs", 0, 0); fd = open(filename, 0); if (fd < 0) { printk("error: failed to open %s\n", filename); TEST_EXIT(1); } memset(buffer, 0, 32); if (read(fd, buffer, 11) != 11) TEST_EXIT(1); if (strncmp(buffer, "-----BEGIN", 10)) TEST_EXIT(1); memset(buffer, 0, 32); if (read(fd, buffer, 10) != 10) TEST_EXIT(1); if (strncmp(buffer, "PUBLIC KEY", 10)) TEST_EXIT(1); TEST_EXIT(0); } ================================================ FILE: tests/getpid_1/main.c ================================================ /* simple test for getpid */ #include #include "unit.h" int main() { pid_t pid = getpid(); if (!pid) TEST_EXIT(1); TEST_EXIT(0); } ================================================ FILE: tests/itoa_1/main.c ================================================ #include "unit.h" #include "utils.h" #include #include int itoa_base_00(void) { char buf[64]; if (strcmp("0", itoa_base(0, buf, 1))) return -1; return 0; } int itoa_base_01(void) { char buf[64]; if (strcmp("0", itoa_base(0, buf, 8))) return -1; return 0; } int itoa_base_02(void) { char buf[64]; if (strcmp("1234567890", itoa_base(1234567890, buf, 10))) return -1; return 0; } int itoa_base_03(void) { char buf[64]; if (strcmp("20", itoa_base(32, buf, 16))) return -1; return 0; } int itoa_base_04(void) { char buf[64]; if (strcmp("21", itoa_base(33, buf, 16))) return -1; return 0; } int itoa_base_05(void) { char buf[64]; if (itoa_base(458, buf, 2048)) return -1; return 0; } int itoa_base_06(void) { char buf[64]; if (strcmp("ffffffff", itoa_base(0xffffffff, buf, 16))) return -1; return 0; } int itoa_base_07(void) { char buf[64]; if (strcmp("-1", itoa_base(0xffffffff, buf, 10))) return -1; return 0; } int itoa_base_08(void) { char buf[64]; if (strcmp("1000", itoa_base(0x1000, buf, 16))) return -1; return 0; } int itoa_base_09(void) { char buf[64]; if (strcmp("-752", itoa_base(-752, buf, 10))) return -1; return 0; } int itoa_base_10(void) { char buf[64]; if (strcmp("0", itoa_base(0, buf, 10))) return -1; return 0; } int main() { int status = 0; int (*test[])(void) = {itoa_base_00, itoa_base_01, itoa_base_02, itoa_base_03, itoa_base_04, itoa_base_05, itoa_base_06, itoa_base_07, itoa_base_08, itoa_base_09, itoa_base_10}; for (int i = 0; i < (int) ARRAY_SIZE(test); i++) { int r = test[i](); /* printk("itoa test #%d %s\n", i, r ? "(failed)" : ""); */ status += r; } if (status) TEST_EXIT(1); TEST_EXIT(0); } ================================================ FILE: tests/lib/unit.h ================================================ #ifndef TEST_LIB_UNIT_H #define TEST_LIB_UNIT_H #include "kernel.h" #define C_GREEN "\033[1;32m" #define C_RED "\033[1;31m" #define C_NORMAL "\033[0m" #define printk_green(format, ...) \ printk(C_GREEN format C_NORMAL, ##__VA_ARGS__); #define printk_red(format, ...) printk(C_RED format C_NORMAL, ##__VA_ARGS__); #include #define TEST_EXIT(status) \ ({ \ if (status) { \ printk_red("test failed: %d", (status)); \ printk("\n"); \ } else { \ printk_green("test passed"); \ printk("\n"); \ } \ v7m_semihost_exit(status); \ }) #endif /* !TEST_LIB_UNIT_H */ ================================================ FILE: tests/malloc_1/main.c ================================================ #include #include #include "unit.h" #include "kernel.h" int main() { char *p[3], *q[3]; p[0] = malloc(48); p[1] = malloc(64); p[2] = malloc(96); for (int i = 0; i < 3; i++) printk("allocated %p\n", p[i]); memset(p[0], 'a', 48); memset(p[1], 'b', 64); memset(p[2], 'c', 96); for (int i = 0; i < 48; i++) { if (p[0][i] != 'a') TEST_EXIT(1); } for (int i = 0; i < 64; i++) { if (p[1][i] != 'b') TEST_EXIT(1); } for (int i = 0; i < 96; i++) { if (p[2][i] != 'c') TEST_EXIT(1); } /* free all pointers */ free(p[0]); free(p[1]); free(p[2]); /* Realloc the same memory mapping, should return the same allocation * mapping. */ q[0] = malloc(48); q[1] = malloc(64); q[2] = malloc(96); for (int i = 0; i < 3; i++) printk("allocated %p\n", p[i]); for (int i = 0; i < 3; i++) { if (p[i] != q[i]) TEST_EXIT(1); } TEST_EXIT(0); } ================================================ FILE: tests/mm_1/main.c ================================================ #include #include #include "unit.h" int main() { void *p, *q; unsigned long order = size_to_page_order(256); if ((p = alloc_pages(order)) == NULL) TEST_EXIT(1); free_pages((unsigned long) p, order); /* same page should be reallocated */ if ((q = alloc_pages(order)) != p) TEST_EXIT(1); TEST_EXIT(0); } ================================================ FILE: tests/mm_2/main.c ================================================ #include #include #include "unit.h" int main() { void *p[14]; int sz[] = {256, 1024, 2048, 256, 256, 256, 512, 512, 1024, 512, 2048, 256, 512, 1024}; for (int j = 0; j < 30; j++) { printk("Iteration #%d\n", j); for (int i = 0; i < 14; i++) { if ((p[i] = alloc_pages(size_to_page_order(sz[i]))) == NULL) TEST_EXIT(1); } for (int i = 0; i < 14; i++) free_pages((unsigned long) p[i], size_to_page_order(sz[i])); } TEST_EXIT(0); } ================================================ FILE: tests/mmap_1/main.c ================================================ /* simple mmap() test, check non-nil memory is allocated */ #include #include #include "unit.h" int main() { void *p; p = mmap(0, 256, 0, MAP_ANONYMOUS, 0, 0); if (p == MAP_FAILED) TEST_EXIT(1); TEST_EXIT(0); } ================================================ FILE: tests/mmap_2/data/id_rsa ================================================ -----BEGIN RSA PRIVATE KEY----- MIIBOgIBAAJBAI/rmTOBknHe2ro+8sUNl1MjNTRopU1/WJTcYvsrFML10Z2IBrFi fc3Q1x92uTvyFU21cn+/ekU8L+8qK+V98fMCAwEAAQJAe4uw+REa6Nt5Ana1KsmP BNRtgO7wMEXIgglqgtipuu3CE9CIZ4OyrvELNbI6RiczGYgMmUHcWEmLuBZ+tjQu 4QIhAORy2CQio1J3O2gGdkk2eY2cLIzoMEdig9su/NILvSqDAiEAoUcD1FeM1iub SiUq3ZDIf1FFTgtv9Si0lzJyBY+IP9ECIHi8zclDWUhDZe1TxP5qwRF74fvS13lS 8tdL3SjyNVcbAiEAhZU3o8r8mWy3DFvqvGiu2V3shK9OhYa4xj9/WAHB/fECIH9u Nq1Ww0stz2Oq7Wpn7ywcNemkot/+sO8l8gxXPcqw -----END RSA PRIVATE KEY----- ================================================ FILE: tests/mmap_2/data/id_rsa.pub ================================================ -----BEGIN PUBLIC KEY----- MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAI/rmTOBknHe2ro+8sUNl1MjNTRopU1/ WJTcYvsrFML10Z2IBrFifc3Q1x92uTvyFU21cn+/ekU8L+8qK+V98fMCAwEAAQ== -----END PUBLIC KEY----- ================================================ FILE: tests/mmap_2/main.c ================================================ /* test mapping file to memory */ #include #include #include #include #include #include #include #include "unit.h" extern char _binary_sda1_start; struct mtd_info mtd1; static struct inode inode = { .i_private = &mtd1, }; void flash_init(void) { struct dentry dentry = {.d_inode = &inode, .d_name = "mtd1"}; printk("Creating MTD device %s\n", dentry.d_name); if (mtdram_init_device(&mtd1, &_binary_sda1_start, 1024, dentry.d_name)) printk("error: mtdram init device failed\n"); vfs_link(NULL, dev_inode(), &dentry); } int main() { init_tmpfs_inode(&inode); flash_init(); mount("/dev/mtd1", "/dev/flash", "romfs", 0, 0); int fd = open("/dev/flash/id_rsa.pub", 0); if (fd < 0) { printk("error: failed to open /data/id_rsa.pub\n"); TEST_EXIT(1); } void *p = mmap(NULL, 256, 0, 0, fd, 0); if (p == MAP_FAILED) TEST_EXIT(1); if (strncmp((char *) p, "-----BEGIN PUBLIC KEY-----", 26)) TEST_EXIT(1); p = mmap(NULL, 256, 0, 0, fd, 27); if (p == MAP_FAILED) TEST_EXIT(1); if (strncmp( (char *) p, "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAI/rmTOBknHe2ro+8sUNl1MjNTRopU1/", 64)) TEST_EXIT(1); TEST_EXIT(0); } ================================================ FILE: tests/msleep_1/main.c ================================================ #include "unit.h" #include "kernel.h" extern void msleep(unsigned int); int main() { for (int i = 1; i < 6; i++) { printk("%d...", i); msleep(1000); } printk("\n"); TEST_EXIT(0); } ================================================ FILE: tests/msleep_2/main.c ================================================ // simple thread create and thread yield #include #include #include "kernel.h" #include "unit.h" extern void msleep(unsigned int); static int val; static void *fn(void *arg) { // msleep(30 * (1 + (int) arg)); msleep(30); val++; return 0; } int main() { pthread_t tip; for (int i = 0; i < 3; i++) { if (pthread_create(&tip, NULL, fn, (void *) i)) { printk("failed: can't create new posix thread.\n"); TEST_EXIT(1); } } while (val != 3) pthread_yield(); TEST_EXIT(0); } ================================================ FILE: tests/mtdram_1/main.c ================================================ /* test simple romFS */ #include #include #include #include #include #include "unit.h" int main() { char str[] = "Hello World!"; char buf[32]; ssize_t len = strlen(str); int fd; printk("Opening /dev/mtd0...\n"); fd = open("/dev/mtd0", 0); if (write(fd, str, len) != len) TEST_EXIT(1); lseek(fd, 0, SEEK_SET); if (read(fd, buf, len) != len) TEST_EXIT(1); printk("Read /dev/mtd0: %s\n", buf); if (strcmp(str, buf)) TEST_EXIT(1); TEST_EXIT(0); } ================================================ FILE: tests/mutex_1/main.c ================================================ #include #include "kernel.h" #include "pthread.h" #include "unit.h" int main(void) { pthread_mutex_t lock; pthread_mutex_init(&lock, NULL); if (pthread_mutex_lock(&lock)) TEST_EXIT(1); printk("mutex locked...\n"); if (pthread_mutex_unlock(&lock)) TEST_EXIT(1); printk("mutex unlocked...\n"); TEST_EXIT(0); } ================================================ FILE: tests/mutex_2/main.c ================================================ #include #include #include #include "kernel.h" #include "pthread.h" #include "unit.h" pthread_mutex_t lock; int has_waited; void *fn(__unused void *arg) { printk("thread 2: acquire mutex...\n"); if (pthread_mutex_lock(&lock)) return NULL; has_waited = 1; printk("thread 2: OK, mutex locked...\n"); if (pthread_mutex_unlock(&lock)) return NULL; printk("thread 2: mutex released...\n"); return NULL; } int main(void) { pthread_t tid; pthread_mutex_init(&lock, NULL); if (pthread_create(&tid, NULL, fn, NULL)) printk("error: Could not create new thread.\n"); if (pthread_mutex_lock(&lock)) TEST_EXIT(1); printk("thread 1: mutex locked, yield now...\n"); sched_yield(); if (has_waited) TEST_EXIT(1); printk("thread 1: return from yield.\n"); if (pthread_mutex_unlock(&lock)) TEST_EXIT(1); printk("thread 1: mutex released...\n"); /* re-acquire the mutex to check the thread released the mutex correctly */ if (pthread_mutex_lock(&lock)) TEST_EXIT(1); TEST_EXIT(0); } ================================================ FILE: tests/mutex_3/main.c ================================================ /* test the trylock interface */ #include #include "kernel.h" #include "pthread.h" #include "unit.h" int main(void) { pthread_mutex_t lock; pthread_mutex_init(&lock, NULL); if (pthread_mutex_lock(&lock)) TEST_EXIT(1); printk("mutex is now locked...\n"); if (!pthread_mutex_trylock(&lock)) TEST_EXIT(1); printk("tried to lock a locked mutex...\n"); if (pthread_mutex_unlock(&lock)) TEST_EXIT(1); printk("mutex unlocked, trylock should succeed...\n"); if (pthread_mutex_trylock(&lock)) TEST_EXIT(1); TEST_EXIT(0); } ================================================ FILE: tests/mutex_4/main.c ================================================ /* strong mutex test, mixing lock/unlock/trylock */ #include #include #include #include "kernel.h" #include "pthread.h" #include "unit.h" pthread_mutex_t lock; void *fn(__unused void *arg) { printk("thread 2: acquire mutex...\n"); if (!pthread_mutex_trylock(&lock)) return NULL; printk("thread 2: mutex was locked...\n"); sched_yield(); printk("thread 2: re-acquire mutex...\n"); if (pthread_mutex_trylock(&lock)) return NULL; sched_yield(); if (pthread_mutex_unlock(&lock)) return NULL; return NULL; } int main(void) { pthread_t tid; pthread_mutex_init(&lock, NULL); if (pthread_create(&tid, NULL, fn, NULL)) printk("error: Could not create new thread.\n"); if (pthread_mutex_trylock(&lock)) TEST_EXIT(1); printk("thread 1: mutex locked, yield now...\n"); sched_yield(); printk("thread 1: return from yield.\n"); if (pthread_mutex_unlock(&lock)) TEST_EXIT(1); printk("thread 1: mutex released...\n"); sched_yield(); /* re-acquire the mutex to check the thread released the mutex correctly */ if (!pthread_mutex_trylock(&lock)) TEST_EXIT(1); if (pthread_mutex_lock(&lock)) TEST_EXIT(1); TEST_EXIT(0); } ================================================ FILE: tests/mutex_5/main.c ================================================ /* test mutexs' waitqueue */ #include #include #include #include "kernel.h" #include "pthread.h" #include "unit.h" static pthread_mutex_t m; void *fn(__unused void *arg) { printk("thread %d locking...\n", (int) arg); pthread_mutex_lock(&m); printk("OK, thread %d got the mutex!\n", (int) arg); pthread_mutex_unlock(&m); return NULL; } int main() { pthread_t tips[4]; pthread_mutex_init(&m, NULL); pthread_mutex_lock(&m); for (int i = 0; i < 4; i++) { if (pthread_create(&tips[i], NULL, fn, (void *) i)) { printk("failed: can't create new posix thread.\n"); TEST_EXIT(1); } } printk("locked the mutex, now yielding...\n"); sched_yield(); printk("unlocking the mutex...\n"); pthread_mutex_unlock(&m); printk("relocking the mutex...\n"); pthread_mutex_lock(&m); TEST_EXIT(0); } ================================================ FILE: tests/page_3/main.c ================================================ /* stress test of page alloc/free */ #include #include #include #include #include #include "unit.h" struct alloc_info { void *addr; int order; }; struct alloc_info alloc_info[128]; int main() { int i; int fd; unsigned int order; unsigned long hash; size_t sz; fd = open("/dev/random", 0); if (fd < 0) { printk("error: failed to open /dev/random\n"); TEST_EXIT(1); } /* take a snapshot of memory state bgefore the test */ hash = page_alloc_signature(); for (int k = 0; k < 1000; k++) { sz = 0; for (i = 0; i < 40; i++) { read(fd, &order, 1); order = order % 4; alloc_info[i].addr = alloc_pages(order); alloc_info[i].order = order; if (!alloc_info[i].addr) break; sz += 1 << (order + 8); /* printk(" %p (order=%d)\n", alloc_info[i].addr, */ /* alloc_info[i].order); */ } /* printk("Allocated %d bytes (%d bytes available)\n", sz, 32 * 1024); */ /* pseudo-randomish freeing of the memory.. */ for (int j = 0; j < i; j += 3) free_pages((unsigned long) alloc_info[j].addr, alloc_info[j].order); for (int j = 1; j < i; j += 3) free_pages((unsigned long) alloc_info[j].addr, alloc_info[j].order); for (int j = 2; j < i; j += 3) free_pages((unsigned long) alloc_info[j].addr, alloc_info[j].order); if (page_alloc_signature() != hash) { printk("error: Memory not correctly restored\n"); TEST_EXIT(1); } } TEST_EXIT(0); } ================================================ FILE: tests/raise_1/main.c ================================================ /*simple signal test */ #include #include #include "kernel.h" #include "unit.h" int val; int signo = SIGUSR1; void handler(int n) { printk("In signal handler, received signal %d\n", n); if (n != signo) TEST_EXIT(1); val = 1; } int main(void *arg) { (void) arg; const struct sigaction act = {.sa_handler = handler, .sa_flags = 0}; sigaction(signo, &act, NULL); if (raise(signo)) TEST_EXIT(1); if (!val) TEST_EXIT(1); TEST_EXIT(0); } ================================================ FILE: tests/raise_2/main.c ================================================ /* test raise() return code */ #include #include #include #include "kernel.h" #include "unit.h" int main(void *arg) { (void) arg; sigaction(SIGUSR1, NULL, NULL); /* shall return -EINVAL */ int retval = raise(0); printk("Got return value %d (negative)\n", -retval); if (retval != -EINVAL) TEST_EXIT(1); TEST_EXIT(0); } ================================================ FILE: tests/raise_3/main.c ================================================ /* test signal handler with SA_SIGINFO set */ #include #include #include "kernel.h" #include "unit.h" int val; int signo = SIGUSR1; void sigact(int sig, siginfo_t *siginfo, void *unused) { (void) unused; printk("In a sigaction handler (signo=%d, sival=0x%x)\n", sig, siginfo->si_value.sival_int); if (sig != signo) TEST_EXIT(1); val = 1; } int main(void *arg) { (void) arg; const struct sigaction act = {.sa_sigaction = sigact, .sa_flags = SA_SIGINFO}; sigaction(signo, &act, NULL); int retval = raise(signo); if (retval) { printk("Syscall returned %d, expected 0\n", retval); TEST_EXIT(1); } if (!val) TEST_EXIT(1); TEST_EXIT(0); } ================================================ FILE: tests/readdir_1/main.c ================================================ #include #include #include #include #include #include "unit.h" int found_dot; int found_dotdot; int found_null; int found_zero; int main() { struct dirent dirent; struct dirent *result; DIR *dir = opendir("/dev"); do { readdir_r(dir, &dirent, &result); if (result != NULL) printk("% 6d %s\n", dirent.d_ino, dirent.d_name); if (!strcmp(result->d_name, ".")) found_dot++; if (!strcmp(result->d_name, "..")) found_dotdot++; if (!strcmp(result->d_name, "zero")) found_zero++; if (!strcmp(result->d_name, "null")) found_null++; } while (result != NULL); closedir(dir); if (found_dot != 1) TEST_EXIT(1); if (found_dotdot != 1) TEST_EXIT(1); if (found_zero != 1) TEST_EXIT(1); if (found_null != 1) TEST_EXIT(1); TEST_EXIT(0); } ================================================ FILE: tests/runner.py ================================================ import datetime import glob import locale import subprocess import sys import time import textwrap import platform import os from time import strftime # Test result constants. PASSED = 1 FAILED = 0 INTERRUPTED = -4 CHILD_ERROR = -5 # error in a child process _FORMAT_TEST_RESULT = { PASSED: '%s passed', FAILED: '%s failed', INTERRUPTED: '%s interrupted', CHILD_ERROR: '%s crashed', } def find_all_tests(excludes=[]): tests = list(map(lambda p: p.strip('/').split('/')[-1], sorted(glob.glob('tests/*_[0-9]*')))) for exclude in excludes: if exclude in tests: tests.remove(exclude) return tests testsuite_v7m = find_all_tests(excludes=['test_2']) class PikoTest: """Execute Piko/RT test suite. """ def __init__(self): # Namespace of command line options self.ns = None # tests self.tests = [] self.selected = [] # test results self.good = [] self.bad = [] self.interrupted = False # used by --slow self.test_times = [] # used to display the progress bar "[ 4/100]" self.start_time = time.monotonic() self.test_count = '' self.test_count_width = 1 def format_test_result(self, test_name, result): fmt = _FORMAT_TEST_RESULT.get(result, "%s") return fmt % test_name def format_duration(self, seconds): if seconds < 1.0: return '%.0f ms' % (seconds * 1e3) if seconds < 60.0: return '%.0f sec' % seconds minutes, seconds = divmod(seconds, 60.0) return '%.0f min %.0f sec' % (minutes, seconds) def display_progress(self, test_index, test): if self.ns.quiet: return # "[ 51/405/1] test_tcl passed" if self.bad: fmt = "{time} [{test_index:{count_width}}{test_count}/{nbad}] {test_name}" else: fmt = "{time} [{test_index:{count_width}}{test_count}] {test_name}" test_time = time.monotonic() - self.start_time test_time = datetime.timedelta(seconds=int(test_time)) line = fmt.format(count_width=self.test_count_width, test_index=test_index, test_count=self.test_count, nbad=len(self.bad), test_name=test, time=test_time) print(line, flush=True) def display_result(self): if self.interrupted: print() print("Test suite interrupted by signla SIGINT.") executed = set(self.good) | set(self.bad) omitted = set(self.selected) - executed print(count(len(omitted), "test"), "omitted:") printlist(omitted) if self.good and not self.ns.quiet: if (not self.bad and not self.interrupted and len(self.good) > 1): print("All", end=' ') print(count(len(self.good), "test"), "OK.") if self.ns.print_slow: self.test_times.sort(reverse=True) print() print("10 slowest tests:") for t, test in self.test_times[:10]: print("- %s: %s" % (test, self.format_duration(t))) if self.bad: print() print(count(len(self.bad), "test"), "failed:") printlist(self.bad) def display_header(self): print('== Welcome to Piko/RT test suite') print("==", platform.python_implementation(), *sys.version.split()) print("==", platform.platform(aliased=True), "%s-endian" % sys.byteorder) print("== cwd:", os.getcwd()) cpu_count = os.cpu_count() if cpu_count: print("== CPU count:", cpu_count) print("== encodings: locale=%s, FS=%s" % (locale.getpreferredencoding(False), sys.getfilesystemencoding())) print() print_qemu_version(self.ns) print_gcc_version(self.ns) def accumulate_result(self, test, result): ok, test_time = result if ok not in (CHILD_ERROR, INTERRUPTED): self.test_times.append((test_time, test)) if ok == PASSED: self.good.append(test) elif ok in (FAILED, CHILD_ERROR): self.bad.append(test) elif ok != INTERRUPTED: raise ValueError("invalid test result: %r" % ok) def run_tests(self): self.display_header() self.test_count = '/{}'.format(len(self.selected)) self.test_count_width = len(self.test_count) - 1 self.run_tests_sequential() def run_tests_sequential(self): print('Run tests sequentially') previous_test = None for test_index, test in enumerate(self.tests, 1): start_time = time.monotonic() text = test if previous_test: text = '%s -- %s' % (text, previous_test) self.display_progress(test_index, text) try: result = run_single_test(test, self.ns) except KeyboardInterrupt: self.interrupted = True self.accumulate_result(test, (INTERRUPTED, None)) break else: self.accumulate_result(test, result) previous_test = self.format_test_result(test, result[0]) test_time = time.monotonic() - start_time if test_time >= 15.0: previous_test = "%s in %s" % (previous_test, self.format_duration(test_time)) elif result[0] == PASSED: previous_test = None if previous_test: print(previous_test) def finalize(self): print() duration = time.monotonic() - self.start_time print("Total duration: %s" % (self.format_duration(duration))) if self.bad: result = "FAILURE" elif self.interrupted: result = "INTERRUPTED" else: result = "SUCCESS" print("Tests result: %s" % result) def main(self, tests, ns): self.ns = ns self.tests = tests self.selected = tests self.run_tests() self.display_result() self.finalize() if self.bad: sys.exit(2) if self.interrupted: sys.exit(130) sys.exit(0) def print_qemu_version(ns): cmd = [ns.qemu, "--version"] res = subprocess.run(cmd, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL) print() print("QEMU version\n", res.stdout.strip()) print() def print_gcc_version(ns): cmd = [ns.cc, "--version"] res = subprocess.run(cmd, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL) print() print("GCC version:\n" + res.stdout.strip()) print() def print_header(testname, arch): print("--------------------------------------------") print("running test: \033[1;37m%s\033[0m" % testname) print("arch : %s" % arch) print("time : %s\n" % strftime("%c")) def run_single_test(test, ns): """Run a single test. ns -- parser namespace of options test -- the name of the test Returns the tuple (result, test_time), where result is one of the constants: INTERRUPTED KeyboardInterrupt FAILED test failed PASS test passed """ cmd = ['make', '--file', 'tests/Makefile', 'clean_test', 'all'] cmd.append('dbg' if ns.debug else 'run') cmd.append('TEST=%s' % (test)) if ns.platform: cmd.append('PLAT=%s' % (ns.platform)) test_time = 0.0 if ns.verbose: print(' '.join(cmd)) try: start_time = time.time() p = subprocess.Popen(cmd, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) output, error = p.communicate(timeout=ns.timeout) test_time = time.time() - start_time if ns.verbose: print(output) except KeyboardInterrupt: p.kill() output, error = p.communicate() if ns.verbose: print(output) raise except subprocess.TimeoutExpired: p.kill() output, error = p.communicate() if ns.verbose: print(output) return FAILED, test_time if p.returncode != 0: if ns.verbose: print(output) return FAILED, test_time return PASSED, test_time def main(ns): PikoTest().main(testsuite_v7m, ns) def printlist(x, width=70, indent=4, file=None): """Print the elements of iterable x to stdout. Optional arg width (default 70) is the maximum line length. Optional arg indent (default 4) is the number of blanks with which to begin each line. """ blanks = ' ' * indent # Print the sorted list: 'x' may be a '--random' list or a set() print(textwrap.fill(' '.join(str(elt) for elt in sorted(x)), width, initial_indent=blanks, subsequent_indent=blanks), file=file) def count(n, word): if n == 1: return "%d %s" % (n, word) else: return "%d %ss" % (n, word) ================================================ FILE: tests/slab_1/main.c ================================================ /* simple test for cache creation and allocation */ #include #include #include "unit.h" struct foo { int a; int b; }; int main(void) { struct kmem_cache *cache = KMEM_CACHE(struct foo, "cache-foo"); if (!cache) { printk("error: Cannot create cache\n"); TEST_EXIT(1); } struct foo *foo = kmem_cache_alloc(cache, CACHE_OPT_NONE); if (!foo) { printk("error: Cannot allocate from cache\n"); TEST_EXIT(1); } foo->a = 1; foo->b = 2; TEST_EXIT(0); } ================================================ FILE: tests/slab_2/main.c ================================================ /* test for cache creation, allocation, destruction */ #include #include #include #include "unit.h" struct foo { int a; int b; }; int main(void) { unsigned long hash; struct kmem_cache *cache = KMEM_CACHE(struct foo, "cache-foo"); if (!cache) { printk("error: Cannot create cache\n"); TEST_EXIT(1); } hash = page_alloc_signature(); struct foo *fp[40]; for (int i = 0; i < 40; i++) { fp[i] = kmem_cache_alloc(cache, CACHE_OPT_NONE); fp[i]->a = i; } if (page_alloc_signature() == hash) { printk("error: No memory allocated\n"); TEST_EXIT(1); } for (int i = 0; i < 40; i++) { if (fp[i]->a != i) { printk("error: Address allocated multiple times\n"); TEST_EXIT(1); } kmem_cache_free(cache, fp[i]); } if (page_alloc_signature() != hash) { printk("error: Memory not correctly restored\n"); TEST_EXIT(1); } TEST_EXIT(0); } ================================================ FILE: tests/softirq_1/main.c ================================================ /* simple softirq task create and rasie */ #include #include #include "unit.h" static volatile int val = 111; static void fn(void *arg) { val += (int) arg; } int main() { struct tasklet_struct *tsk = NULL; tsk = tasklet_init(fn, (void *) 666, PRIO_TASKLET_MAXPRIO); if (!tsk) { printk("failed: can't create new softirq task.\n"); TEST_EXIT(1); } if (tasklet_schedule(tsk) == -1) { printk("failed: can't rasie softirq task.\n"); TEST_EXIT(1); } while (val != 777) ; TEST_EXIT(0); } ================================================ FILE: tests/softirq_2/main.c ================================================ /* softirq task create/rasie 1000 times */ #include #include #include "unit.h" static volatile int val = 1111; static void fn(void *arg) { val += (int) arg; } int main() { int i; static struct tasklet_struct *tsk = NULL; /** * FIXME: modifing to 1000 times would fail to create. * Concludion with this problem is without freeing * softirq task. That's make sense, so we could avoid * it by testing tsk ptr. No more creation of same task. * FIXME: After patch above, we would get another problem about * same tsk with same addr space can concatenate two same tsk? * * ANS: Probably NOT */ for (i = 0; i < 100; i++) { tsk = tasklet_init(fn, (void *) 1, PRIO_TASKLET_MAXPRIO); if (!tsk) { printk("failed: can't create new softirq task.\n"); TEST_EXIT(1); } if (tasklet_schedule(tsk) == -1) { printk("failed: can't rasie softirq task.\n"); TEST_EXIT(1); } } while (val != 1211) ; TEST_EXIT(0); } ================================================ FILE: tests/softirq_3/main.c ================================================ /* test softirq task sequeniality */ #include #include #include "unit.h" static volatile int bucket[6] = {0}; #define DEFINE_FUNC(_id_) \ static void fn##_id_(void *arg) { bucket[_id_] = (int) arg; } DEFINE_FUNC(0) DEFINE_FUNC(1) DEFINE_FUNC(2) DEFINE_FUNC(3) DEFINE_FUNC(4) DEFINE_FUNC(5) int main() { #define SOFTIRQ_TASK_IMPL(_id_) \ do { \ struct tasklet_struct *tsk##_id_ = \ tasklet_init(fn##_id_, (void *) _id_, PRIO_TASKLET_MAXPRIO); \ if (!tsk##_id_) { \ printk("failed: can't create new softirq task" #_id_ ".\n"); \ TEST_EXIT(1); \ } \ \ if (tasklet_schedule(tsk##_id_) == -1) { \ printk("failed: can't rasie softirq task" #_id_ ".\n"); \ TEST_EXIT(1); \ } \ } while (0) SOFTIRQ_TASK_IMPL(0); SOFTIRQ_TASK_IMPL(1); SOFTIRQ_TASK_IMPL(2); SOFTIRQ_TASK_IMPL(3); SOFTIRQ_TASK_IMPL(4); SOFTIRQ_TASK_IMPL(5); for (int i = 0; i < 6; i++) // Test sequenciality while (!(bucket[i] == i)) ; TEST_EXIT(0); } ================================================ FILE: tests/sprintf_1/main.c ================================================ #include "unit.h" #include "utils.h" #include #include "kernel.h" #include int sprintf_00(void) { char buf[128]; sprintf(buf, "%d", 1789); if (strcmp(buf, "1789")) { printk("%s", buf); return -1; } return 0; } int sprintf_01(void) { char buf[128]; sprintf(buf, "%x", 0xdeadbeef); if (strcmp(buf, "deadbeef")) { printk("%s\n", buf); return -1; } return 0; } int sprintf_02(void) { char buf[128]; sprintf(buf, "Hello %s", "World!"); if (strcmp(buf, "Hello World!")) { printk("%s\n", buf); return -1; } return 0; } int sprintf_03(void) { char buf[128]; sprintf(buf, "abcdABCD0123 0x%x, %d, %d, 0x%x", 0xdeadbeef, 1983, 2014, 4096); if (strcmp(buf, "abcdABCD0123 0xdeadbeef, 1983, 2014, 0x1000")) { printk("%s\n", buf); return -1; } return 0; } int sprintf_04(void) { char buf[128]; sprintf(buf, "%w, %%, %s, %d", "foo", 1986); if (strcmp(buf, "%w, %, foo, 1986")) { printk("%s\n", buf); return -1; } return 0; } int sprintf_05(void) { char buf[128]; sprintf(buf, "%08d %06d %04d", 1986, 1986, 1986); if (strcmp(buf, "00001986 001986 1986")) { printk("%s\n", buf); return -1; } return 0; } int sprintf_06(void) { char buf[128]; sprintf(buf, "%08x %06x %04x %02x", 0xef, 0xef, 0xef, 0xef); if (strcmp(buf, "000000ef 0000ef 00ef ef")) { printk("%s\n", buf); return -1; } return 0; } int sprintf_07(void) { char buf[128]; sprintf(buf, "% 7d % 5x", 1986, 0x13); if (strcmp(buf, " 1986 13")) { printk("%s\n", buf); return -1; } return 0; } int main() { int status = 0; int (*test[])(void) = {sprintf_00, sprintf_01, sprintf_02, sprintf_03, sprintf_04, sprintf_05, sprintf_06, sprintf_07}; for (int i = 0; i <= 7; i++) { printk("sprintf test #%d\n", i); status += test[i](); } if (status) TEST_EXIT(1); TEST_EXIT(0); } ================================================ FILE: tests/stat_1/data/id_rsa ================================================ -----BEGIN RSA PRIVATE KEY----- MIIBOgIBAAJBAI/rmTOBknHe2ro+8sUNl1MjNTRopU1/WJTcYvsrFML10Z2IBrFi fc3Q1x92uTvyFU21cn+/ekU8L+8qK+V98fMCAwEAAQJAe4uw+REa6Nt5Ana1KsmP BNRtgO7wMEXIgglqgtipuu3CE9CIZ4OyrvELNbI6RiczGYgMmUHcWEmLuBZ+tjQu 4QIhAORy2CQio1J3O2gGdkk2eY2cLIzoMEdig9su/NILvSqDAiEAoUcD1FeM1iub SiUq3ZDIf1FFTgtv9Si0lzJyBY+IP9ECIHi8zclDWUhDZe1TxP5qwRF74fvS13lS 8tdL3SjyNVcbAiEAhZU3o8r8mWy3DFvqvGiu2V3shK9OhYa4xj9/WAHB/fECIH9u Nq1Ww0stz2Oq7Wpn7ywcNemkot/+sO8l8gxXPcqw -----END RSA PRIVATE KEY----- ================================================ FILE: tests/stat_1/data/id_rsa.pub ================================================ -----BEGIN PUBLIC KEY----- MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAI/rmTOBknHe2ro+8sUNl1MjNTRopU1/ WJTcYvsrFML10Z2IBrFifc3Q1x92uTvyFU21cn+/ekU8L+8qK+V98fMCAwEAAQ== -----END PUBLIC KEY----- ================================================ FILE: tests/stat_1/main.c ================================================ /* simple test for stat() */ #include #include #include #include #include #include "unit.h" extern char _binary_sda1_start; struct mtd_info mtd1; static struct inode inode = { .i_private = &mtd1, }; void flash_init(void) { struct dentry dentry = {.d_inode = &inode, .d_name = "mtd1"}; printk("Creating MTD device %s\n", dentry.d_name); if (mtdram_init_device(&mtd1, &_binary_sda1_start, 1024, dentry.d_name)) printk("error: mtdram init device failed\n"); vfs_link(NULL, dev_inode(), &dentry); } int main() { init_tmpfs_inode(&inode); flash_init(); mount("/dev/mtd1", "/dev/flash", "romfs", 0, 0); struct stat st; if (stat("/dev/flash/id_rsa.pub", &st)) { printk("error: Wrong pathname\n"); TEST_EXIT(1); } printk("Opened file with inode number %d\n", st.st_ino); if (!S_ISREG(st.st_mode)) { printk("error: File is not a regular file\n"); TEST_EXIT(1); } if (st.st_size != 182) { printk("error: Wrong file size (%d != 182)\n", st.st_size); TEST_EXIT(1); } TEST_EXIT(0); } ================================================ FILE: tests/syscall_1/main.c ================================================ #include "unit.h" #include "kernel.h" unsigned long get_sp(void); int syscall_register(unsigned ix, void *(*fn)()); int sysarg_ack[7]; #define SYSARG_A0 0xbabe #define SYSARG_A1 0xbeef #define SYSARG_A2 0x123 #define SYSARG_A3 0x456 #define SYSARG_A4 0xcafe #define SYSARG_A5 0xfeed int sysarg0_handler(void) { printk(" no arg\n"); sysarg_ack[0] = 1; return 0; } int sysarg1_handler(int a0) { printk(" arg0: %x\n", a0); if (a0 != SYSARG_A0) return -1; sysarg_ack[1] = 1; return 0; } int sysarg2_handler(int a0, int a1) { printk(" arg0: %x\n", a0); printk(" arg1: %x\n", a1); if (a0 != SYSARG_A0) return -1; if (a1 != SYSARG_A1) return -1; sysarg_ack[2] = 1; return 0; } int sysarg3_handler(int a0, int a1, int a2) { printk(" arg0: %x\n", a0); printk(" arg1: %x\n", a1); printk(" arg2: %x\n", a2); if (a0 != SYSARG_A0) return -1; if (a1 != SYSARG_A1) return -1; if (a2 != SYSARG_A2) return -1; sysarg_ack[3] = 1; return 0; } int sysarg4_handler(int a0, int a1, int a2, int a3) { printk(" arg0: %x\n", a0); printk(" arg1: %x\n", a1); printk(" arg2: %x\n", a2); printk(" arg3: %x\n", a3); if (a0 != SYSARG_A0) return -1; if (a1 != SYSARG_A1) return -1; if (a2 != SYSARG_A2) return -1; if (a3 != SYSARG_A3) return -1; sysarg_ack[4] = 1; return 0; } int sysarg5_handler(int a0, int a1, int a2, int a3, int a4) { printk(" arg0: %x\n", a0); printk(" arg1: %x\n", a1); printk(" arg2: %x\n", a2); printk(" arg3: %x\n", a3); printk(" arg4: %x\n", a4); if (a0 != SYSARG_A0) return -1; if (a1 != SYSARG_A1) return -1; if (a2 != SYSARG_A2) return -1; if (a3 != SYSARG_A3) return -1; if (a4 != SYSARG_A4) return -1; sysarg_ack[5] = 1; return 0; } int sysarg6_handler(int a0, int a1, int a2, int a3, int a4, int a5) { printk(" arg0: %x\n", a0); printk(" arg1: %x\n", a1); printk(" arg2: %x\n", a2); printk(" arg3: %x\n", a3); printk(" arg4: %x\n", a4); printk(" arg5: %x\n", a5); if (a0 != SYSARG_A0) return -1; if (a1 != SYSARG_A1) return -1; if (a2 != SYSARG_A2) return -1; if (a3 != SYSARG_A3) return -1; if (a4 != SYSARG_A4) return -1; if (a5 != SYSARG_A5) return -1; sysarg_ack[6] = 1; return 0; } void test_svcall0(); void test_svcall1(); void test_svcall2(); void test_svcall3(); void test_svcall4(); void test_svcall5(); void test_svcall6(); int main(void *arg) { (void) arg; void (*test_svcall[])() = { test_svcall0, test_svcall1, test_svcall2, test_svcall3, test_svcall4, test_svcall5, test_svcall6, }; syscall_register(41, (void *(*) ()) sysarg0_handler); syscall_register(42, (void *(*) ()) sysarg1_handler); syscall_register(43, (void *(*) ()) sysarg2_handler); syscall_register(44, (void *(*) ()) sysarg3_handler); syscall_register(45, (void *(*) ()) sysarg4_handler); syscall_register(46, (void *(*) ()) sysarg5_handler); syscall_register(47, (void *(*) ()) sysarg6_handler); for (int i = 0; i <= 6; i++) { printk("Test syscall with %d arg(s).\n", i); // XXX: check SP before and after. also in syscall handler, in // trampoline. unsigned long sp = get_sp(); test_svcall[i](); if (get_sp() != sp) { printk("error: Incorrect r13/sp\n"); TEST_EXIT(1); } if (!sysarg_ack[i]) { printk("error: syscall was not acknowledged\n"); TEST_EXIT(1); } } TEST_EXIT(0); } ================================================ FILE: tests/syscall_1/trampoline.S ================================================ #include .syntax unified .thumb ENTRY(test_svcall0) ldr r0, =41 svc #0 bx lr ENDPROC(test_svcall0) ENTRY(test_svcall1) ldr r0, =0xbabe ldr r1, =42 svc #1 bx lr ENDPROC(test_svcall1) ENTRY(test_svcall2) ldr r0, =0xbabe ldr r1, =0xbeef ldr r2, =43 svc #2 bx lr ENDPROC(test_svcall2) ENTRY(test_svcall3) ldr r0, =0xbabe ldr r1, =0xbeef ldr r2, =0x123 ldr r3, =44 svc #3 bx lr ENDPROC(test_svcall3) ENTRY(test_svcall4) ldr r3, =45 push {r3} ldr r0, =0xbabe ldr r1, =0xbeef ldr r2, =0x123 ldr r3, =0x456 svc #4 add sp, #4 bx lr ENDPROC(test_svcall4) ENTRY(test_svcall5) ldr r2, =0xcafe ldr r3, =46 push {r2, r3} ldr r0, =0xbabe ldr r1, =0xbeef ldr r2, =0x123 ldr r3, =0x456 svc #5 add sp, #8 bx lr ENDPROC(test_svcall5) ENTRY(test_svcall6) ldr r1, =0xcafe ldr r2, =0xfeed ldr r3, =47 push {r1-r3} ldr r0, =0xbabe ldr r1, =0xbeef ldr r2, =0x123 ldr r3, =0x456 svc #6 add sp, #12 bx lr ENDPROC(test_svcall6) ENTRY(get_sp) mov r0, sp bx lr ENDPROC(get_sp) ================================================ FILE: tests/sysconf_1/main.c ================================================ #include "unit.h" #include "unistd.h" int main() { printk("system tick per seconds: %d\n", sysconf(_SC_CLK_TCK)); TEST_EXIT(0); } ================================================ FILE: tests/test_1/main.c ================================================ /* this test must pass */ #include "unit.h" int main() { TEST_EXIT(0); } ================================================ FILE: tests/test_2/main.c ================================================ /* this test must fail */ #include "unit.h" int main() { TEST_EXIT(1); } ================================================ FILE: tests/thread_1/main.c ================================================ /* simple thread create and thread yield */ #include #include #include "unit.h" static int val = 123; static void *fn(void *arg) { val += (int) arg; return 0; } int main() { pthread_t tip; if (pthread_create(&tip, NULL, fn, (void *) 321)) { printk("failed: can't create new posix thread.\n"); TEST_EXIT(1); } if (sched_yield()) { printk("failed: can't yield cpu to another thread.\n"); TEST_EXIT(1); } while (val != 444) ; TEST_EXIT(0); } ================================================ FILE: tests/thread_2/main.c ================================================ #include #include #include "unit.h" int vals[] = {0, 0, 0, 0}; void *fn(void *arg) { for (;;) { vals[(int) arg]++; sched_yield(); } return 0; } int main() { pthread_t tips[4]; for (int i = 0; i < 4; i++) { if (pthread_create(&tips[i], NULL, fn, (void *) i)) { printk("failed: can't create new posix thread.\n"); TEST_EXIT(1); } } for (int i = 0; i < 4; i++) { while (vals[i] < 10) sched_yield(); } TEST_EXIT(0); } ================================================ FILE: tests/thread_3/main.c ================================================ #include #include #include "unit.h" static int count; void *fn(void *arg) { (void) arg; printk("counter=%d\n", count); count++; return 0; } int main() { pthread_t tips[25]; /* reduce the thread stack size to 256 bytes */ pthread_attr_t attr; pthread_attr_setstacksize(&attr, 256); /* create 25 threads */ for (int i = 0; i < 25; i++) { if (pthread_create(&tips[i], &attr, fn, NULL)) { printk("failed: can't create new posix thread.\n"); TEST_EXIT(1); } } while (count < 24) sched_yield(); TEST_EXIT(0); } ================================================ FILE: tests/thread_4/main.c ================================================ #include #include #include #include #include #include "unit.h" static int count; static void *fn(void *arg) { int i = (int) arg; if (i != count) TEST_EXIT(1); count++; return 0; } int main() { struct thread_info *t; CURRENT_TASK_INFO(curr_task); for (int i = 0; i < 15; i++) { t = thread_create(fn, (void *) i, THREAD_PRIV_USER, 256, curr_task); if (!t) { printk("failed: can't create new posix thread.\n"); TEST_EXIT(1); } thread_set_priority(t, i); sched_enqueue(t); } sched_yield(); if (count != 15) { printk("count != 15 (%d)\n", count); TEST_EXIT(1); } TEST_EXIT(0); } ================================================ FILE: tests/thread_5/main.c ================================================ /* test thread_join() */ #include "kernel.h" #include "pthread.h" #include "unit.h" #include "linux/stddef.h" void *fn(void *arg) { /* printk("Running thread with arg=%d\n", (int) arg); */ pthread_exit(arg); return 0; } int main() { void *retval; pthread_t tips[7]; printk("Creating a bunch of threads... "); for (int i = 0; i < 7; i++) { if (pthread_create(&tips[i], NULL, fn, (void *) i)) { printk("failed: can't create new posix thread.\n"); TEST_EXIT(1); } } printk("OK\n"); for (int i = 0; i < 7; i++) { printk("Joining thread with arg=%d... ", i); pthread_join(tips[i], &retval); if ((int) retval != i) { printk("pthread_join: wrong return value: got %d, expected %d\n", (int) retval, i); TEST_EXIT(1); } printk("OK\n"); } TEST_EXIT(0); } ================================================ FILE: tests/thread_6/main.c ================================================ /* create/delete 1000 threads */ #include #include #include #include "unit.h" static void *fn(void *arg) { (void) arg; printk("."); return 0; } int main() { pthread_t thread; for (int i = 0; i < 1000; i++) { if (pthread_create(&thread, NULL, fn, NULL)) { printk("failed: can't create new posix thread.\n"); TEST_EXIT(1); } pthread_detach(thread); sched_yield(); } printk("\n"); TEST_EXIT(0); } ================================================ FILE: tests/timer_1/main.c ================================================ #include #include #include "unit.h" #include #include #define EXPECTED_VALUE 0xabadcafeul static volatile int received_signal; static void event(union sigval sival) { if (sival.sival_int != (int) EXPECTED_VALUE) { printk("error: Did not received expected value (%x != %x)\n", sival.sival_int, EXPECTED_VALUE); TEST_EXIT(1); } received_signal = 1; } int main() { struct sigevent sevp = {.sigev_notify_function = event, .sigev_value.sival_int = EXPECTED_VALUE}; struct itimerspec val = {.it_value = {.tv_sec = 1, .tv_nsec = 0}}; timer_t timerid; timer_create(1, &sevp, &timerid); timer_settime(timerid, 0, &val, NULL); while (!received_signal) ; TEST_EXIT(0); } ================================================ FILE: tests/timer_2/main.c ================================================ /* simple create multiple concurrent timers */ #include #include #include #include "kernel.h" #include "unit.h" static volatile int vals[] = {0, 0, 0, 0}; static void event(union sigval sival) { printk("In event %d.\n", sival.sival_int); vals[sival.sival_int] = 1; } int main() { struct sigevent sevp = {.sigev_notify_function = event}; timer_t timerid[4]; for (int i = 0; i < 4; i++) { printk("Creating timer %d...\n", i); sevp.sigev_value.sival_int = i; timer_create(0, &sevp, &timerid[i]); unsigned long val_in_nsecs = 750 * (i + 1) * 1000000; struct itimerspec val = { .it_value = {.tv_sec = val_in_nsecs / 1000000000, .tv_nsec = val_in_nsecs % 1000000000}}; timer_settime(timerid[i], 0, &val, NULL); } printk("All timers armed.\n"); for (int i = 0; i < 4; i++) { while (vals[i] == 0) ; } TEST_EXIT(0); } ================================================ FILE: tests/timer_3/main.c ================================================ #include #include #include "unit.h" #include #include static void event(union sigval sival) { (void) sival; } int main() { struct sigevent sevp = {.sigev_notify_function = event}; struct itimerspec val = {.it_value = {.tv_sec = 5, .tv_nsec = 0}}; timer_t timerid; timer_create(1, &sevp, &timerid); timer_settime(timerid, 0, &val, NULL); do { timer_gettime(timerid, &val); } while (val.it_value.tv_sec != 4); TEST_EXIT(0); } ================================================ FILE: tests/timer_4/main.c ================================================ /* simple test for interval timers */ #include #include #include #include #include "unit.h" static volatile int count; static void event(union sigval sival) { (void) sival; printk("Counter=%d\n", ++count); } int main() { timer_t timerid; struct sigevent sevp = { .sigev_notify_function = event, }; struct itimerspec val = { .it_value = {.tv_sec = 1, .tv_nsec = 0}, .it_interval = {.tv_sec = 1, .tv_nsec = 0}, }; timer_create(0, &sevp, &timerid); timer_settime(timerid, 0, &val, NULL); while (count < 4) ; TEST_EXIT(0); } ================================================ FILE: tests/timer_5/main.c ================================================ /* test cancellation of timer */ #include #include #include #include #include "unit.h" static volatile int canary; static volatile int count; static void event_should_not_happen(union sigval sival) { (void) sival; canary++; } static void event_check(union sigval sival) { (void) sival; count++; } int main() { timer_t timerid_a; struct sigevent sevp_a = { .sigev_notify_function = event_should_not_happen, }; timer_create(0, &sevp_a, &timerid_a); timer_t timerid_b; struct sigevent sevp_b = { .sigev_notify_function = event_check, }; timer_create(0, &sevp_b, &timerid_b); struct itimerspec val_a = { .it_value = {.tv_sec = 1, .tv_nsec = 0}, .it_interval = {.tv_sec = 0, .tv_nsec = 0}, }; struct itimerspec val_b = { .it_value = {.tv_sec = 2, .tv_nsec = 0}, .it_interval = {.tv_sec = 0, .tv_nsec = 0}, }; struct itimerspec val_zero = { .it_value = {.tv_sec = 0, .tv_nsec = 0}, .it_interval = {.tv_sec = 0, .tv_nsec = 0}, }; timer_settime(timerid_a, 0, &val_a, NULL); timer_settime(timerid_b, 0, &val_b, NULL); timer_settime(timerid_a, 0, &val_zero, NULL); while (!count) ; if (canary) TEST_EXIT(1); TEST_EXIT(0); } ================================================ FILE: tests/ucontext_1/main.c ================================================ #include #include "unit.h" static int test_status; static ucontext_t main_context, other_context; static unsigned int ctx_stack[128]; void __printk_putchar(char c); static void pputs(const char *s) { for (; *s != '\0'; s++) __printk_putchar(*s); } void test(int a1, int a2, int a3, int a4) { pputs("Hello from a new context!\n"); if ((a1 != 9) || (a2 != 0xcafe) || (a3 != 13) || (a4 != 14)) { pputs("failed: incorrect correct arg.\n"); test_status = 1; } } int main() { other_context.uc_link = &main_context; other_context.uc_stack.ss_sp = &ctx_stack[128]; /* pass 4 arguments to the new context, and swap */ makecontext(&other_context, test, 4, 9, 0xcafe, 13, 14); swapcontext(&main_context, &other_context); pputs("And back to the main context.\n"); TEST_EXIT(test_status); } ================================================ FILE: user/cat.c ================================================ #include #include #include #include #include "sh.h" static int cat(__unused int argc, char *argv[]) { char buf[] = {0, 0}; int fd; int retval = 0; for (int i = 1; i < argc; i++) { fd = open(argv[i], 0); if (fd < 0) { printk("cat: %s: No such file or directory\n", argv[1]); retval = 1; } while (read(fd, &buf, 1)) printk("%s", buf); close(fd); } return retval; } HOOK_BUILTIN_CMD(cat, cat); ================================================ FILE: user/echo.c ================================================ #include #include "sh.h" static int echo(int argc, char *argv[]) { if (argc == 1) return 0; for (int i = 1; i < argc; i++) { if (i == 1) printk("%s", argv[i]); else printk(" %s", argv[i]); } printk("\n"); return 0; } HOOK_BUILTIN_CMD(echo, echo); ================================================ FILE: user/exit.c ================================================ #include "sh.h" #include "platform.h" static int _exit(__unused int argc, __unused char *argv[]) { __platform_halt(); return 0; } HOOK_BUILTIN_CMD(exit, _exit); ================================================ FILE: user/halt.c ================================================ #include "sh.h" #include "platform.h" static int halt(__unused int argc, __unused char *argv[]) { __platform_halt(); return 0; } HOOK_BUILTIN_CMD(halt, halt); ================================================ FILE: user/ls.c ================================================ #include #include #include #include #include #include "sh.h" static int ls(int argc, char *argv[]) { DIR *dir; struct dirent dirent; struct dirent *result; if (argc == 1) dir = opendir("/"); // FIXME: get current directory else dir = opendir(argv[1]); do { readdir_r(dir, &dirent, &result); if (result != NULL) printk("% 6d %s\n", dirent.d_ino, dirent.d_name); } while (result != NULL); closedir(dir); return 0; } HOOK_BUILTIN_CMD(ls, ls); ================================================ FILE: user/reboot.c ================================================ #include "sh.h" #include "platform.h" static int reboot(__unused int argc, __unused char *argv[]) { NVIC_SystemReset(); return -1; } HOOK_BUILTIN_CMD(reboot, reboot); ================================================ FILE: user/sh.c ================================================ #include #include #include #include #include #include "sh.h" #include "platform.h" static const char ESC_SEQ_ERASE_LINE[] = "\033[K"; static const char TERM_PROMPT[] = "$ "; static const char TERM_CMD_NOT_FOUND[] = "command not found: "; static const char TERM_CRLF[] = "\r\n"; static int parse_command_line(char *buf, char *argv[]) { int buflen = strlen(buf); int argc = 1; argv[0] = (char *) buf; for (int i = 0; i < buflen; i++) { if (buf[i] == ' ') { buf[i++] = '\0'; while (buf[i] == ' ') i++; argv[argc++] = (char *) &buf[i]; } } return argc; } static void exec_command(char *buf, int fd) { extern unsigned long __shell_cmd_start__; extern unsigned long __shell_cmd_end__; int argc; char *argv[ARG_COUNT_MAX]; struct shell_cmd *cmd; argc = parse_command_line(buf, argv); for (cmd = (struct shell_cmd *) &__shell_cmd_start__; (unsigned long) cmd < (unsigned long) &__shell_cmd_end__; cmd++) if (!strcmp(cmd->name, argv[0])) { cmd->func(argc, argv); return; } write(fd, TERM_CMD_NOT_FOUND, sizeof(TERM_CMD_NOT_FOUND) - 1); write(fd, buf, strlen(buf)); write(fd, TERM_CRLF, sizeof(TERM_CRLF) - 1); } static void cursor_backward(int n, int fd) { char ebuf[8]; if (n > 0) { sprintf(ebuf, "\033[%dD", n); write(fd, ebuf, strlen(ebuf)); } } static int cur; static int cur_eol; static char buf_line[BUF_LINE_LEN]; static void readline(int fd) { char c; read(fd, &c, 1); switch (c) { case ASCII_CARRIAGE_RETURN: write(fd, TERM_CRLF, sizeof(TERM_CRLF) - 1); if (cur_eol > 0) { exec_command(buf_line, fd); cur = 0; /* relative position to prompt's last char */ cur_eol = 0; } write(fd, TERM_PROMPT, sizeof(TERM_PROMPT) - 1); break; case ASCII_BACKSPACE: case ASCII_DELETE: // XXX: QEMU sends DEL instead of BS if (cur > 0) { if (cur < cur_eol) { for (int i = cur; i <= cur_eol; i++) buf_line[i - 1] = buf_line[i]; } buf_line[--cur_eol] = ASCII_NULL; cur--; cursor_backward(1, fd); write(fd, ESC_SEQ_ERASE_LINE, sizeof(ESC_SEQ_ERASE_LINE) - 1); write(fd, &buf_line[cur], strlen(&buf_line[cur])); cursor_backward(cur_eol - cur, fd); } break; case ' ' ... '~': if (cur < cur_eol) { for (int i = cur_eol; i >= cur; i--) buf_line[i + 1] = buf_line[i]; } buf_line[cur++] = c; buf_line[++cur_eol] = '\0'; write(fd, &buf_line[cur - 1], strlen(&buf_line[cur - 1])); cursor_backward(cur_eol - cur, fd); break; case ASCII_ESCAPE: read(fd, &c, 1); if (c != '[') // this is not an escape sequence return; read(fd, &c, 1); switch (c) { case 'C': if (cur < cur_eol) { cur++; write(fd, "\033[C", 3); } break; case 'D': if (cur > 0) { cur--; write(fd, "\033[D", 3); } break; default: printk("Unhandled escape sequence!\n"); return; } default:; } } int minishell(void *options) { (void) options; int fd = open("/dev/ttyS0", 0); if (fd < 0) printk("cannot open /dev/ttyS0\n"); write(fd, TERM_PROMPT, sizeof(TERM_PROMPT) - 1); for (;;) { readline(fd); } return 0; } ================================================ FILE: user/sh.h ================================================ #ifndef USER_SH_H #define USER_SH_H #define ARG_COUNT_MAX 8 #define BUF_LINE_LEN 128 enum ascii_control_char { ASCII_NULL = 000, ASCII_BACKSPACE = 010, ASCII_CARRIAGE_RETURN = 015, ASCII_ESCAPE = 033, ASCII_DELETE = 0177, }; struct shell_cmd { char *name; int (*func)(int argc, char *argv[]); }; #define HOOK_BUILTIN_CMD(_name, _func) \ static struct shell_cmd shell_##_name \ __attribute__((section(".shell_cmd"), aligned(sizeof(long)), \ used)) = {.name = #_name, .func = _func} #endif /* !USER_SH_H */