[
  {
    "path": ".clang-format",
    "content": "BasedOnStyle: Chromium\nLanguage: Cpp\nMaxEmptyLinesToKeep: 3\nIndentCaseLabels: false\nAllowShortIfStatementsOnASingleLine: false\nAllowShortCaseLabelsOnASingleLine: false\nAllowShortLoopsOnASingleLine: false\nDerivePointerAlignment: false\nPointerAlignment: Right\nSpaceAfterCStyleCast: true\nTabWidth: 4\nUseTab: Never\nIndentWidth: 4\nBreakBeforeBraces: Linux\nAccessModifierOffset: -4\n"
  },
  {
    "path": ".gitignore",
    "content": "# external source\nexternal/\n\n*~\n*.o\n*.o.d\n*.elf\n*.map\n*.lds\n*.hex\n*.bin\n\n# generated\nfs/version\ninclude/kernel/syscalls.h\nkernel/syscall.c\n\n# QEMU generated\nDAC_OUT_PUT1.txt\nDAC_OUT_PUT2.txt\n\n# Python\n__pycache__\n*.pyc\n\n# Sphinx\n_build\n\n"
  },
  {
    "path": ".travis.yml",
    "content": "sudo: required\ndist: trusty\nos: linux\nlanguage: python\ncache: pip\nstage:\n  - docs\n  - core\n\njobs:\n  include:\n    - stage: docs\n      env: TESTING=docs\n      python: 3.6\n      before_install:\n        - sudo apt-get update -qq\n      install:\n        - sudo apt-get install cscope\n      before_script:\n        - cd docs\n        - pip install sphinx\n      script:\n        - make check\n        - make html\n    - &core-stage\n      stage: core\n      python: 3.5\n      env: TESTING=Piko/RT\n      before_install:\n        - sudo add-apt-repository -y ppa:team-gcc-arm-embedded/ppa\n        - sudo apt-get update -qq\n      install:\n        - sudo apt install build-essential\n        - sudo apt-get install -y gcc-arm-embedded\n        # QEMU deps\n        - sudo apt install libxenstore3.0\n        - sudo apt install libxen-dev\n        - sudo apt install genromfs\n        - wget https://github.com/PikoRT/tools/raw/master/bin/x86_64-linux/qemu-system-arm\n        - chmod 777 qemu-system-arm\n        - export PATH=$PWD:$PATH\n      before_script:\n        - arm-none-eabi-gcc --version\n        - python --version\n      script:\n        - make PLAT=stm32p103\n        - make PLAT=stm32p103 check\n    - <<: *core-stage\n      python: 3.6\n"
  },
  {
    "path": "LICENSE",
    "content": "Piko/RT is freely redistributable under the two-clause BSD License:\n\nCopyright (c) 2017 Piko/RT Developers.\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n  list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n  this list of conditions and the following disclaimer in the documentation\n  and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "Makefile",
    "content": "NAME = piko\n\n# select QEMU when the platform is unspecified\nPLAT ?= f429disco\nCMSIS = external/cmsis\n\n# The platform Makefile contains hw details and flags\ninclude platform/$(PLAT)/Makefile\n\n# arch-specific\nSSRC += arch/v7m-head.S arch/v7m-entry.S arch/v7m-svcall.S\nCSRC += arch/v7m-faults.c\n\nLIBPIKO_CSRC = $(wildcard libc/*.c)\nLIBPIKO_SSRC = $(wildcard libc/*.S) $(wildcard libc/piko/*.S)\n\nSSRC += $(LIBPIKO_SSRC)\n\nCSRC += kernel/syscall.c\nCSRC += \\\n    $(wildcard kernel/*.c) \\\n    $(wildcard kernel/fs/*.c) \\\n    $(wildcard kernel/mm/*.c) \\\n    $(wildcard kernel/sched/*.c) \\\n    $(wildcard fs/*.c) \\\n    $(wildcard drivers/char/*.c) \\\n    $(wildcard drivers/mtd/*.c) \\\n    $(wildcard drivers/timer/timercore.c) \\\n    $(wildcard drivers/serial/serial*.c) \\\n    $(wildcard user/*.c) \\\n    libc/piko/stubs.c \\\n    libc/piko/mman.c \\\n    $(LIBPIKO_CSRC)\n\nOBJS += $(SSRC:.S=.o) $(CSRC:.c=.o)\nOBJS := $(sort $(OBJS))\n\ndeps := $(OBJS:%.o=.%.o.d)\n\n.PHONY: all check clean distclean check_cc\n\nall: check_cc $(CMSIS)/$(PLAT) $(NAME).lds $(NAME).bin\n\n# generic build rules\ninclude mk/flags.mk\ninclude mk/rules.mk\ninclude mk/cmsis.mk\n\nprebuild: $(CMSIS)/$(PLAT)\n\ncheck: check_cc\n\t$(PYTHON) -m tests -p $(PLAT) --qemu $(QEMU_SYSTEM_ARM) --cc $(CC)\n\ncheck_cc:\n\t@$(eval PYTHON_VERSION=$(shell echo `$(PYTHON) --version 2>&1 | grep -oE '[^ ]+$$'`))\n\t@$(eval PYTHON_VERSION=$(shell echo $(PYTHON_VERSION) | awk -F \".\" '{print $$1$$2 0}'))\n\t@if [ $(PYTHON_VERSION) -lt 350 ]; then \\\n\t\techo \"Error: Python version must >= 3.5, use PYTHON=/python/binary/path\"; \\\n\t\treturn 1;\\\n\tfi;\n\nclean:\n\tfind . -name \"*.o\" -type f -delete\n\tfind . -name \"*.o.d\" -type f -delete\n\trm -f $(deps)\nifneq \"$(wildcard $(CMSIS) )\" \"\"\n\tfind $(CMSIS) -name \"*.o\" -type f -delete\nendif\n\trm -f $(NAME).map $(NAME).lds\n\trm -f $(NAME).elf $(NAME).bin\n# Remove GEN files\n\trm -f include/kernel/syscalls.h\n\trm -f kernel/syscall.c\n\trm -f fs/version\n\ndistclean: clean\n\trm -f kernel/syscall.c include/kernel/syscalls.h fs/version\n\trm -rf $(CMSIS)\n\n# platform build contains flashing and running rules\ninclude platform/$(PLAT)/build.mk\n\n-include $(deps)\n"
  },
  {
    "path": "README.md",
    "content": "# Piko/RT\n\nThis is `Piko/RT`, a tiny Linux-like real-time operating system kernel, optimized for ARM Cortex-M series microprocessors.\n\nPrerequisites\n-------------\n- [QEMU with an STM32 microcontroller implementation](https://beckus.github.io/qemu_stm32/)\n\n## Run test suite\n\n* Run all test\n```shell\n$ make PLAT=stm32p103 check\n```\n\n* Run all test with command line tools\n```shell\n$ python -m tests\n```\n\n* Run specific test cases\n```shell\n$ python -m tests fs_1 cond_2\n```\n\n## External Source\n\n* `scripts/rstlint.py`: written by Georg Brandl\n"
  },
  {
    "path": "arch/v7m-entry.S",
    "content": "#include <kernel/linkage.h>\n\n\t.syntax unified\n\t.thumb\n\n\t@ do the thread-context switch\n\t@\n\t@ r0: struct thread_info *next\n\t@ r1: struct thread_info *prev (i.e. the current thread)\n\t@ (r1), r2, r3, r12: scratch registers\nENTRY(switch_to)\n\t/* save previous thread context */\n\tpush\t{r4-r12, lr}\n\tmov\tr2, sp\n\tmrs\tr3, psp\n\tstm\tr1!, {r2, r3}\n\nENTRY(thread_restore)\n\t/* restore next task context */\n\tldm\tr0!, {r1-r3}\n\tmov\tsp, r1\n\tmsr\tpsp, r2\n\tmsr\tcontrol, r3\t\t@ write to SPSEL is ignored in Handler_Mode\n\tpop\t{r4-r12, pc}\nENDPROC(thread_restore)\nENDPROC(switch_to)\n\n\t.macro\temulate_iret\n\tldrd\tr0, r1, [sp, #24]\t/* r0 = ret_addr, r1 = xpsr */\n\torr\tr0, #1\n\tstr\tr0, [sp, #24]\n\tmsr\tapsr_nzcvq, r1\t\t/* restore flags */\n\tpop\t{r0-r3, r12, lr}\n\tldr\tpc, [sp], #8\t\t/* return into user thread */\n\t.endm\n\nENTRY(return_from_sigaction)\n\tadd\tsp, #12\t\t\t/* reclaim siginfo_t storage */\n\temulate_iret\nEND(return_from_sigaction)\n\nENTRY(return_from_sighandler)\n\temulate_iret\nEND(return_from_sighandler)\n\n\t.set MC_SP, 0\n\t.set MC_LR, 4\n\t.set MC_GPRS, 8\n\t.set MC_PC, 15*4\n\t.set UC_MCONTEXT, 16\n\t.set UC_LINK, 0\n\n\t/* Swap context to a clean context (created from makecontext()):\n\t *   - save the non-scratch registers\n\t *   - just restore scratch registers (for arguments to entry function)\n\t *\n\t * Swap to a dirty context (inited with getcontext, or return to a\n\t * swapped context):\n\t *   - save the non-scratch registers\n\t *   - restore all registers\n\t */\n\nENTRY(swapcontext)\n\t/* save to oucp */\n\tadd\tr0, #UC_MCONTEXT\n\tstr\tsp, [r0, #MC_SP]\n\tstr\tlr, [r0, #MC_LR]\n\tadd\tr0, #MC_GPRS\n\tstm\tr0, {r0-r12, lr}\n\n\t/* retore from ucp */\n\tadd\tr1, #UC_MCONTEXT\n\tldr\tsp, [r1, #MC_SP]\n\tldr\tlr, [r1, #MC_LR]\n\tadd\tr1, #MC_GPRS\n\tldm\tr1, {r0-r12, pc}\nENDPROC(swapcontext)\n\nENTRY(return_from_makecontext)\n\t/* retrieve the struct& from the top of the stack */\n\tldr\tr0, [sp]\n\n\t/* get the machine struct for the linked context */\n\tldr\tr0, [r0, #UC_LINK]\n\tadd\tr0, #UC_MCONTEXT\n\n\t/* restore the link context */\n\tldr\tsp, [r0, #MC_SP]\n\tldr\tlr, [r0, #MC_LR]\n\tadd\tr0, #MC_GPRS\n\t//FIXME: return code - the r0 value that is restored from the stack should be 0\n\tldm\tr0, {r0-r12, pc}\nENDPROC(return_from_makecontext)\n\n\t.macro\tfault_handler f\n\tpush\t{r4-r11}\t@ save non-scratch registers at time of fault\n\tmov\tr0, sp\n\tmov\tr2, lr\t\t@ lr contains EXC_RETURN, fault was taken in thread or interrupt?\n\n\t@ We don't handle faults taken in interrupt handler at the moment.\n\t@ Hence the scratch registers auto pushed to the stack by the CPU\n\t@ on interrupt-entry have been pushed to PSP (MSP is only used by\n\t@ interrupts handler).\n\tmrs\tr1, psp\n\n\tb\t\\f\n\t.endm\t/* fault_handler */\n\nENTRY(hardf)\n\tfault_handler hardfault\nENDPROC(hardf)\n\nENTRY(memf)\n\tfault_handler memmanage\nENDPROC(memf)\n\nENTRY(busf)\n\tfault_handler busfault\nENDPROC(busf)\n\nENTRY(usgf)\n\tfault_handler usagefault\nENDPROC(usgf)\n\nENTRY(__do_idle)\n\twfi\n\tbx\tlr\nENDPROC(__do_idle)\n\n\t/* void v7m_semihost_exit(int status); */\nENTRY(v7m_semihost_exit)\n\ttst\tr0, r0\n\tite\teq\n\tldreq\tr1, =0x20026    /* [a] */\n\tmovne\tr1, #0\n\tmov\tr0, #0x18       /* [b] */\n\tbkpt\t#0xab\n0:\tb\t0b\nENDPROC(v7m_semihost_exit)\n\n/* [a] https://lists.nongnu.org/archive/html/qemu-devel/2014-12/msg01575.html\n *     ADP_Stopped_ApplicationExit is used for exit(0), anything else is\n *     implemented as exit(1). */\n\n/* [b] http://git.qemu.org/?p=qemu.git;a=blob_plain;f=target-arm/arm-semi.c;hb=HEAD\n *     List of supported semihosting calls in Qemu. */\n"
  },
  {
    "path": "arch/v7m-faults.c",
    "content": "#include <kernel/faults.h>\n#include <kernel/thread.h>\n\n#include \"kernel.h\"\n\n#define UFSR_DIVBYZERO (1 << 9)\n#define UFSR_UNALIGNED (1 << 8)\n#define UFSR_NOCP (1 << 3)\n#define UFSR_INVPC (1 << 2)\n#define UFSR_INVSTATE (1 << 1)\n#define UFSR_UNDEFINSTR 1\n\nvoid dump_frame(struct kernel_context_regs *noscratch,\n                struct thread_context_regs *scratch,\n                u32 exc_return)\n{\n    printk(\" r0: %08x    r1: %08x    r2: %08x    r3: %08x\\n\",\n           scratch->r0_r3__r12[0], scratch->r0_r3__r12[1],\n           scratch->r0_r3__r12[2], scratch->r0_r3__r12[3]);\n    printk(\" r4: %08x    r5: %08x    r6: %08x    r7: %08x\\n\",\n           noscratch->r4_r12[0], noscratch->r4_r12[1], noscratch->r4_r12[2],\n           noscratch->r4_r12[3]);\n    printk(\" r8: %08x    r9: %08x   r10: %08x   r11: %08x\\n\",\n           noscratch->r4_r12[4], noscratch->r4_r12[5], noscratch->r4_r12[6],\n           noscratch->r4_r12[7]);\n    printk(\"r12: %08x    sp: %08x    lr: %08x    pc: %08x\\n\",\n           scratch->r0_r3__r12[4], (u32) scratch, scratch->lr,\n           scratch->ret_addr);\n    printk(\"\\nEXC_RETURN: %08x\\n\", exc_return);\n}\n\nvoid usagefault(struct kernel_context_regs *noscratch,\n                struct thread_context_regs *scratch,\n                u32 exc_return)\n{\n    u32 ufsr = (*((volatile u32 *) 0xe000ed28)) >> 16;\n    const char *cause = NULL;\n\n    fault_enter(\"UsageFault\");\n    dump_frame(noscratch, scratch, exc_return);\n    if (ufsr & UFSR_DIVBYZERO)\n        cause = \"DIVBYZERO\";\n    else if (ufsr & UFSR_UNALIGNED)\n        cause = \"UNALIGNED\";\n    else if (ufsr & UFSR_NOCP)\n        cause = \"NOCP\";\n    else if (ufsr & UFSR_INVPC)\n        cause = \"INVPC\";\n    else if (ufsr & UFSR_INVSTATE)\n        cause = \"INVSTATE\";\n    else if (ufsr & UFSR_UNDEFINSTR)\n        cause = \"UNDEFINSTR\";\n    if (cause)\n        printk(\"      ufsr: %08x  <%s>\\n\", ufsr, cause);\n    else\n        printk(\"      ufsr: %08x\\n\", ufsr);\n    fault_exit();\n}\n\nvoid busfault(struct kernel_context_regs *noscratch,\n              struct thread_context_regs *scratch,\n              u32 exc_return)\n{\n    fault_enter(\"BusFault\");\n    dump_frame(noscratch, scratch, exc_return);\n    fault_exit();\n}\n\nvoid memmanage(struct kernel_context_regs *noscratch,\n               struct thread_context_regs *scratch,\n               u32 exc_return)\n{\n    fault_enter(\"MemManage\");\n    dump_frame(noscratch, scratch, exc_return);\n    fault_exit();\n}\n"
  },
  {
    "path": "arch/v7m-head.S",
    "content": "#include <kernel/linkage.h>\n\n\t.syntax unified\n\t.thumb\n\n\t.section \".vector\", \"a\"\n\t.long\t__early_stack_start__\t\t@ SP_main value\n\t.long\treset\t\t\t\t@ Reset\n\t.long\t0\t\t\t\t@ NMI\n\t.long\thardf\t\t\t\t@ HardFault\n\t.long\tmemf\t\t\t\t@ MemManage\n\t.long\tbusf\t\t\t\t@ BusFault\n\t.long\tusgf\t\t\t\t@ UsageFault\n\t.long\t0\t\t\t\t@ Reserved\n\t.long\t0\t\t\t\t@ Reserved\n\t.long\t0\t\t\t\t@ Reserved\n\t.long\t0\t\t\t\t@ Reserved\n\t.long\tsvcall\t\t\t\t@ SVCall\n\t.long\t0\t\t\t\t@ Debug Monitor\n\t.long\t0\t\t\t\t@ Reserved\n\t.long\tpendsv\t\t\t\t@ PendSV\n\t.long\tsystick\t\t\t\t@ SysTick\n\t.long\tirq_entry\t\t\t@ IRQ 0\n\t.long\tirq_entry\t\t\t@ IRQ 1\n\t.long\tirq_entry\t\t\t@ IRQ 2\n\t.long\tirq_entry\t\t\t@ IRQ 3\n\t.long\tirq_entry\t\t\t@ IRQ 4\n\t.long\tirq_entry\t\t\t@ IRQ 5\n\t.long\tirq_entry\t\t\t@ IRQ 6\n\t.long\tirq_entry\t\t\t@ IRQ 7\n\t.long\tirq_entry\t\t\t@ IRQ 8\n\t.long\tirq_entry\t\t\t@ IRQ 9\n\t.long\tirq_entry\t\t\t@ IRQ 10\n\t.long\tirq_entry\t\t\t@ IRQ 11\n\t.long\tirq_entry\t\t\t@ IRQ 12\n\t.long\tirq_entry\t\t\t@ IRQ 13\n\t.long\tirq_entry\t\t\t@ IRQ 14\n\t.long\tirq_entry\t\t\t@ IRQ 15\n\t.long\tirq_entry\t\t\t@ IRQ 16\n\t.long\tirq_entry\t\t\t@ IRQ 17\n\t.long\tirq_entry\t\t\t@ IRQ 18\n\t.long\tirq_entry\t\t\t@ IRQ 19\n\t.long\tirq_entry\t\t\t@ IRQ 20\n\t.long\tirq_entry\t\t\t@ IRQ 21\n\t.long\tirq_entry\t\t\t@ IRQ 22\n\t.long\tirq_entry\t\t\t@ IRQ 23\n\t.long\tirq_entry\t\t\t@ IRQ 24\n\t.long\tirq_entry\t\t\t@ IRQ 25\n\t.long\tirq_entry\t\t\t@ IRQ 26\n\t.long\tirq_entry\t\t\t@ IRQ 27\n\t.long\tirq_entry\t\t\t@ IRQ 28\n\t.long\tirq_entry\t\t\t@ IRQ 29\n\t.long\tirq_entry\t\t\t@ IRQ 30\n\t.long\tirq_entry\t\t\t@ IRQ 31\n\t.long\tirq_entry\t\t\t@ IRQ 32\n\t.long\tirq_entry\t\t\t@ IRQ 33\n\t.long\tirq_entry\t\t\t@ IRQ 34\n\t.long\tirq_entry\t\t\t@ IRQ 35\n\t.long\tirq_entry\t\t\t@ IRQ 36\n\t.long\tirq_entry\t\t\t@ IRQ 37\n\t.long\tirq_entry\t\t\t@ IRQ 38\n\t.long\tirq_entry\t\t\t@ IRQ 39\n\t.long\tirq_entry\t\t\t@ IRQ 40\n\t.long\tirq_entry\t\t\t@ IRQ 41\n\t.long\tirq_entry\t\t\t@ IRQ 42\n\t.long\tirq_entry\t\t\t@ IRQ 43\n\t.long\tirq_entry\t\t\t@ IRQ 44\n\t.long\tirq_entry\t\t\t@ IRQ 45\n\t.long\tirq_entry\t\t\t@ IRQ 46\n\t.long\tirq_entry\t\t\t@ IRQ 47\n\t.long\tirq_entry\t\t\t@ IRQ 48\n\t.long\tirq_entry\t\t\t@ IRQ 49\n\t.long\tirq_entry\t\t\t@ IRQ 50\n\t.long\tirq_entry\t\t\t@ IRQ 51\n\t.long\tirq_entry\t\t\t@ IRQ 52\n\t.long\tirq_entry\t\t\t@ IRQ 53\n\t.long\tirq_entry\t\t\t@ IRQ 54\n\t.long\tirq_entry\t\t\t@ IRQ 55\n\t.long\tirq_entry\t\t\t@ IRQ 56\n\t.long\tirq_entry\t\t\t@ IRQ 57\n\t.long\tirq_entry\t\t\t@ IRQ 58\n\t.long\tirq_entry\t\t\t@ IRQ 59\n\t.long\tirq_entry\t\t\t@ IRQ 60\n\t.long\tirq_entry\t\t\t@ IRQ 61\n\t.long\tirq_entry\t\t\t@ IRQ 62\n\t.long\tirq_entry\t\t\t@ IRQ 63\n\n\t.text\n\n/*\n * mov32 - loads a 32-bit value into a register without a data access\n */\n\t.macro  mov32 rd, imm32\n\tmovw    \\rd, #:lower16:\\imm32\n\t.if     \\imm32 & 0xffff0000\n\tmovt    \\rd, #:upper16:\\imm32\n\t.endif\n\t.endm\n\nENTRY(reset)\n\tldr\tr0, =SystemInit\t\t\t/* CMSIS system init */\n\tblx\tr0\n\n\tmov32\tr0, 0xe000ed00\t\t\t/* SCB_BASE */\n\n\t@ switch to Handler_Mode\n\tldr\tr1, =__early_stack_end__\t/* allocate a temporary vector */\n\tstr\tr1, [r0, #8]\t\t\t/* update VTOR */\n\tldr\tr2, =0f\n\torr\tr2, #1\t\t\t\t/* set thumb bit */\n\tstr\tr2, [r1, #11 * 4]\t\t/* offset to SVC entry */\n\tdsb\t\t\t\t\t/* [1] */\n\tsvc\t#0\n0:\n\n\t@ restore the early vector\n\tldr\tr1, =__vector_start__\n\tstr\tr1, [r0, #8]\t\t\t/* restore VTOR */\n\tdsb\t\t\t\t\t/* [1] */\n\n\t@ copy the initialized data sections\n\tldr\tr0, =__data_start__\n\tldr\tr1, =__rodata_end__\n\tldr\tr2, =__data_size__\n\tbl\tmemcpy\n\n\t@ zero-fill the non-initialized data sections\n\tldr\tr0, =__bss_start__\n\tmovs    r1, #0\n\tldr r2, =__bss_size__\n\tbl\tmemset\n\n\t@ start_kernel procedure returns the first thread to run on the CPU\n\tbl\tstart_kernel\n\tcmp\tr0, #0\n\titt\tne\n\tldrne\tr1, =thread_restore\n\tbxne\tr1\n0:\tb\t0b\nENDPROC(reset)\n\n/* [1] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0321a/BIHDGBJG.html */\n\nENTRY(irq_entry)\n\tpush\t{lr}\n\tldr\tr0, =irq_handler\n\tmrs\tr1, ipsr\n\tsub\tr1, #16\n\tldr.w\tr0, [r0, r1, lsl #2]\n\tblx\tr0\n\tpop\t{pc}\nENDPROC(irq_entry)\n\n\t.weak systick\nsystick:\n\tbx\tlr\n\n\t.balign 8\n"
  },
  {
    "path": "arch/v7m-svcall.S",
    "content": "#include <kernel/linkage.h>\n\n\t.syntax unified\n\t.thumb\n\n\t@ offsets in the frame to the registers saved on interrupt-entry\n\t.set\tR0, 0\n\t.set    RET_ADDRESS, 24\n\t.set\txPSR, 0x1c\n\nENTRY(svcall)\n\tpush\t{lr}\n\tmrs\tlr, psp\n\tldr\tr12, [lr, #RET_ADDRESS]\n\tldrb\tr12, [r12, #-2]\t\t@ address of the SVC call site\n\ttbb\t[pc, r12]\n\n0:\t.irpc\targc, 0123456\n\t.byte\t($\\argc - 0b) / 2\n\t.endr\n\t.balign 2\n\n\t.balign\t2\n$0:\tldr\tr12, =syscall_vect\n\tldr.w\tr12, [r12, r0, lsl #2]\n\tblx\tr12\n\tb\t0f\n\n\t.balign\t2\n$1:\tldr\tr12, =syscall_vect\n\tldr.w\tr12, [r12, r1, lsl #2]\n\tblx\tr12\n\tb\t0f\n\n\t.balign\t2\n$2:\tldr\tr12, =syscall_vect\n\tldr.w\tr12, [r12, r2, lsl #2]\n\tblx\tr12\n\tb\t0f\n\n\t.balign\t2\n$3:\tldr\tr12, =syscall_vect\n\tldr.w\tr12, [r12, r3, lsl #2]\n\tblx\tr12\n\tb\t0f\n\n\t.balign\t2\n$4:\tldr\tr12, [lr, #xPSR]\t@ test stack alignment\n\ttst\tr12, #1 << 9\n\tite\teq\n\tldreq\tlr, [lr, #0x20]\t\t@ load syscall id\n\tldrne\tlr, [lr, #0x24]\n\tldr\tr12, =syscall_vect\n\tldr.w\tr12, [r12, lr, lsl #2]\n\tblx\tr12\n\tb\t0f\n\n\t.balign\t2\n$5:\tldr\tr12, [lr, #xPSR]\t@ test stack alignment\n\ttst\tr12, #1 << 9\n\tite\teq\n\taddeq\tlr, #0x20\n\taddne\tlr, #0x24\n\tldm\tlr, {r12, lr}\t\t@ load syscall id, arg4\n\tpush\t{r12}\t\t\t@ copy arg4 to kernel stack\n\tldr\tr12, =syscall_vect\n\tldr.w\tr12, [r12, lr, lsl #2]\n\tblx\tr12\n\tadd\tsp, #4\n\tb\t0f\n\n\t.balign\t2\n$6:\tldr\tr12, [lr, #xPSR]\t@ test stack alignment\n\ttst\tr12, #1 << 9\n\tite\teq\n\taddeq\tlr, #0x20\n\taddne\tlr, #0x24\n\tldm\tlr, {r11, r12, lr}\t@ load syscall id, arg4, arg5\n\tpush\t{r11, r12}\t\t@ copy arg4, arg5 to kernel stack\n\tldr\tr12, =syscall_vect\n\tldr.w\tr12, [r12, lr, lsl #2]\n\tblx\tr12\n\tpop\t{r11, r12}\t\t//FIXME: pop {r11}; add\tsp, #4;\n\n\t.global syscall_return\nsyscall_return:\n0:\tmrs\tr1, psp\n\tstr\tr0, [r1, #R0]           @ update return value in exception frame\n\tpop\t{pc}\nENDPROC(svcall)\n"
  },
  {
    "path": "docs/Makefile",
    "content": "# Piko/RT documentation\n\n# You can set these variables from the command line.\nSPHINXOPTS    =\nSPHINXBUILD   = sphinx-build\nSPHINXPROJ    = piko\nSOURCEDIR     = .\nBUILDDIR      = _build\n\n# Put it first so that \"make\" without argument is like \"make help\".\nhelp:\n\t@$(SPHINXBUILD) -M help \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)\n\n.PHONY: help clean Makefile\n\n# Catch-all target: route all unknown targets to Sphinx using the new\n# \"make mode\" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).\n%: Makefile\n\t@$(SPHINXBUILD) -M $@ \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)\n\nclean:\n\t-rm -rf _build/* posix/posix_*.rst\n\ncheck:\n\tpython3 ../scripts/rstlint.py\n"
  },
  {
    "path": "docs/api.rst",
    "content": ".. _api:\n\n\nPiko/RT POSIX API\n=================\n\n``Piko/RT`` aim to fulfill IEEE 1003.13-2003 PSE51 standard, this is the list of\nPOSIX API that ``Piko/RT`` implemented.\n\nUnits of Functionality Requirements\n-----------------------------------\n\n.. include:: posix/posix_req.rst\n\nPOSIX.1 Option Requirements\n---------------------------\n\n.. include:: posix/posix_opt.rst\n"
  },
  {
    "path": "docs/build.rst",
    "content": ".. _build:\n\nBuild Piko/RT\n=============\n\nThis chapter will help you start to build ``Piko/RT``, you will need to prepare\nsome pacakge and settings.\n\n\nPrerequisites\n-------------\n\n* svn: used to checkout `CMSIS <https://github.com/ARMmbed/mbed-os/cmsis>`_.\n* wget: used to download required files.\n* arm-none-eabi-gcc arm-none-eabi-newlib: cross-compile toolchain.\n\nArch Linux::\n\n   $ pacman -S base-devel svn lsb-release wget\n   $ pacman -S arm-none-eabi-gcc arm-none-eabi-newlib\n   $ pacman -S python\n\nUbuntu::\n\n   $ add-apt-repository -y ppa:team-gcc-arm-embedded/ppa\n   $ apt update -qq\n   $ apt install build-essential svn gcc-arm-embedded\n\nmacOS::\n\n   # If you didn't build any thing on macOS before\n   # please install xcode-select first\n   $ xcode-select --install\n\n   # install brew from https://brew.sh\n   $ brew install subversion wget\n   $ brew tap PX4/homebrew-px4\n   $ brew install gcc-arm-none-eabi-49\n\n\nBuild Piko/RT on Linux and macOS\n--------------------------------\n\nDefault make will build for ``f429disco``::\n\n   $ make\n\nYou can change the target via ``PLAT``::\n\n   $ make PLAT=stm32p103\n\nYou can build faster via passing ``-j`` flag::\n\n   $ make PLAT=stm32p103 -j8\n\nYou can build with verbose output::\n\n   $ make PLAT=stm32p103 -j8 VERBOSE=1\n\n\nTroubleshooting\n---------------\n\nIf you concur problem when building Piko/RT, please help to fill the issue\non `GitHub <https://github.com/piko-rt/pikoRT/issues>`_,\nand maybe help to improve the build process, or improve this troubleshooting\npart!\n"
  },
  {
    "path": "docs/conf.py",
    "content": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n#\n# piko documentation build configuration file, created by\n# sphinx-quickstart on Tue Aug 15 18:28:39 2017.\n#\n# This file is execfile()d with the current directory set to its\n# containing dir.\n#\n# Note that not all possible configuration values are present in this\n# autogenerated file.\n#\n# All configuration values have a default; values that are commented out\n# serve to show the default.\n\n# If extensions (or modules to document with autodoc) are in another directory,\n# add these directories to sys.path here. If the directory is relative to the\n# documentation root, use os.path.abspath to make it absolute, like shown here.\n#\n# import os\n# import sys\n# sys.path.insert(0, os.path.abspath('.'))\n\n\n# -- General configuration ------------------------------------------------\n\n# If your documentation needs a minimal Sphinx version, state it here.\n#\n# needs_sphinx = '1.0'\n\n# Add any Sphinx extension module names here, as strings. They can be\n# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom\n# ones.\nextensions = []\n\n# Add any paths that contain templates here, relative to this directory.\ntemplates_path = ['_templates']\n\n# The suffix(es) of source filenames.\n# You can specify multiple suffix as a list of string:\n#\n# source_suffix = ['.rst', '.md']\nsource_suffix = '.rst'\n\n# The master toctree document.\nmaster_doc = 'index'\n\n# General information about the project.\nproject = 'Piko/RT'\ncopyright = '2017, Piko/RT Developers'\nauthor = 'Piko/RT Developers'\n\n# The version info for the project you're documenting, acts as replacement for\n# |version| and |release|, also used in various other places throughout the\n# built documents.\n#\n# The short X.Y version.\nversion = '0.1'\n# The full version, including alpha/beta/rc tags.\nrelease = '0.1'\n\n# The language for content autogenerated by Sphinx. Refer to documentation\n# for a list of supported languages.\n#\n# This is also used if you do content translation via gettext catalogs.\n# Usually you set \"language\" from the command line for these cases.\nlanguage = None\n\n# List of patterns, relative to source directory, that match files and\n# directories to ignore when looking for source files.\n# This patterns also effect to html_static_path and html_extra_path\nexclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']\n\n# The name of the Pygments (syntax highlighting) style to use.\npygments_style = 'sphinx'\n\n# If true, `todo` and `todoList` produce output, else they produce nothing.\ntodo_include_todos = False\n\n\n# -- Options for HTML output ----------------------------------------------\n\n# The theme to use for HTML and HTML Help pages.  See the documentation for\n# a list of builtin themes.\n#\nhtml_theme = 'alabaster'\n\n# Theme options are theme-specific and customize the look and feel of a theme\n# further.  For a list of options available for each theme, see the\n# documentation.\n#\n# html_theme_options = {}\n\n# Add any paths that contain custom static files (such as style sheets) here,\n# relative to this directory. They are copied after the builtin static files,\n# so a file named \"default.css\" will overwrite the builtin \"default.css\".\nhtml_static_path = ['_static']\n\n\n# -- Options for HTMLHelp output ------------------------------------------\n\n# Output file base name for HTML help builder.\nhtmlhelp_basename = 'pikodoc'\n\n\n# -- Options for LaTeX output ---------------------------------------------\n\nlatex_elements = {\n    # The paper size ('letterpaper' or 'a4paper').\n    #\n    # 'papersize': 'letterpaper',\n\n    # The font size ('10pt', '11pt' or '12pt').\n    #\n    # 'pointsize': '10pt',\n\n    # Additional stuff for the LaTeX preamble.\n    #\n    # 'preamble': '',\n\n    # Latex figure (float) alignment\n    #\n    # 'figure_align': 'htbp',\n}\n\n# Grouping the document tree into LaTeX files. List of tuples\n# (source start file, target name, title,\n#  author, documentclass [howto, manual, or own class]).\nlatex_documents = [\n    (master_doc, 'piko.tex', 'piko Documentation',\n     'Piko/RT Developers', 'manual'),\n]\n\n\n# -- Options for manual page output ---------------------------------------\n\n# One entry per manual page. List of tuples\n# (source start file, name, description, authors, manual section).\nman_pages = [\n    (master_doc, 'piko', 'piko Documentation',\n     [author], 1)\n]\n\n\n# -- Options for Texinfo output -------------------------------------------\n\n# Grouping the document tree into Texinfo files. List of tuples\n# (source start file, target name, title, author,\n#  dir menu entry, description, category)\ntexinfo_documents = [\n    (master_doc, 'piko', 'piko Documentation',\n     author, 'piko', 'One line description of project.',\n     'Miscellaneous'),\n]\n\n# -- Generate POSIX API list ----------------------------------------------\n\nimport subprocess\nsubprocess.check_output(['python3', 'posix/parse.py'])\n\n"
  },
  {
    "path": "docs/contribute.rst",
    "content": ".. _contribute:\n\nContribute to Piko/RT\n=====================\n\nWe welcome everyone to help to develop Piko/RT.\nThis will help you to find were to help, and how to submit a pull request\nto GitHub.\n\n\nHow to contribute\n-----------------\n\nThere are a variety of ways to contribute to this project.\nHere are the list what you may help:\n\n* Write documentation.\n* Report issue when you occure.\n* Implement new feature.\n* Code review -- this is always helpful.\n* Make a bug fixed and send the patches.\n* Adding new test.\n* Integrate new feature -- benchmark, TCP stack, GNU utilt...etc.\n* Port to new paltform.\n\n\nCreate your GitHub account\n--------------------------\n\nIf you have a GitHub account, please skip this section.\n\nIf you don't have, please follow these step:\n\n* Click `Join GitHub <https://github.com/join>`_\n* Fill the form\n* Confirm your email\n\n\nFork Piko/RT repo\n-----------------\n\n* Go to `Piko/RT repo <https://github.com/piko-rt/pikoRT>`_\n* Click the buttom \"Fork\" at the right top side.\n* Done!\n\n\nClone the Piko/RT repo\n----------------------\n\nYou will need a copy of Piko/RT to work on the code::\n\n    $ git clone https://github.com/<your-username>/pikoRT\n\n\nCreate a local Git branch\n-------------------------\n\nIf you make some changed to the project, and want to share with everyone,\nyou will need to upload to GitHub.\n\nFirst, create a new branch::\n\n    $ git checkout -b <branch-name>\n\nThen commit the changed in this branch::\n\n    $ git add <your-changed>\n    $ git commit\n\nPush it to your fork::\n\n    $ git push -u origin <branch-name>\n\n\nPublish your pull request\n-------------------------\n\nYou will then need to go to `Piko/RT's repo <https://github.com/piko-rt/pikoRT>`_,\nClick ``Pull reuqests``, Click green button ``New pull request``.\n\nThen reference this tutorial from GitHub:\n`Creating a pull request from a fork <https://help.github.com/articles/creating-a-pull-request-from-a-fork/>`_\n\n\nGit remotes\n-----------\n\nYour local git repo's origin is your fork. So if you do something like this::\n\n    $ git pull\n\nYou won't get the latest commit from Piko/RT.\n\nYou will need to add a upstream, fetch from it, then rebase::\n\n    $ git remote add upstream git@github.com:Piko-RT/pikoRT.git\n    $ git fetch upstream\n    $ git rebase upstream/master\n\n.. note::\n\n    Before you rebase, you will need to stash all your change via ``git stash``.\n    After rebase, if you want your stash file back, you can type in\n    ``git stash pop`` to get back your stash.\n"
  },
  {
    "path": "docs/debugging.rst",
    "content": ".. _debugging:\n\nPiko/RT Debugging\n=================\n\n\nUsing tmux\n----------\n\n`tmux <https://github.com/tmux/tmux>`_ is a \"terminal multiplexer\", it allow you\naccessed and controllerd multiple terminals inside a signle terminal like this:\n\n.. image:: https://gifyu.com/images/asciicast.json.gif\n\nHow to use tmux, please reference these pages:\n\n* `tmux wiki <https://github.com/tmux/tmux/wiki>`_\n* `A tmux Crash Course <https://robots.thoughtbot.com/a-tmux-crash-course>`_\n* `A Quick and Easy Guide to tmux <http://www.hamvocke.com/blog/a-quick-and-easy-guide-to-tmux/>`_\n\nUsing OpenOCD\n-------------\n\nXXX\n\nUsing gdb scripts\n-----------------\n\nHere are some debug scripts for gdb. You can copy and paste to your `.gdbinit`,\nthen used in gdb.\n\n1. Dump out bitmap scheduler active and expire queue::\n\n      define dump_bitmap\n        set $prio = 0\n        while $prio < 32\n          if $arg0 == 1\n            p (struct thread_info *)((char *)(sched_struct.active->queue[$prio].next) - 0x28)\n          end\n          if $arg0 == 0\n            p (struct thread_info *)((char *)(sched_struct.expire->queue[$prio].next) - 0x28)\n          end\n          set $prio = $prio + 1\n        end\n      end\n\n   how to use::\n\n      (gdb) dump_bitmap 1   # Dump active\n      $1 = (struct thread_info *) 0x20000d2c <vsnprintf_buf+228>\n      $2 = (struct thread_info *) 0x20000d34 <vsnprintf_buf+236>\n      $3 = (struct thread_info *) 0x20000d3c <vsnprintf_buf+244>\n      $4 = (struct thread_info *) 0x20000d44 <vsnprintf_buf+252>\n      $5 = (struct thread_info *) 0x20000d4c <sched>\n      $6 = (struct thread_info *) 0x20000d54 <_active+4>\n      ...\n      (gdb) dump_bitmap 0   # Dump expire\n\n2. Dump out RR scheduler runqueue::\n\n      define dump_rr\n        if list_empty(&rr_runq)\n          p \"empty rr\"\n        else\n          set $pos = rr_runq->next\n          while $pos != &rr_runq\n            p (struct thread_info *)((char *)($pos)-0x28)\n            set $pos = $pos->next\n          end\n        end\n      end\n\n   how to use::\n\n      (gdb) dump_rr\n"
  },
  {
    "path": "docs/emulate.rst",
    "content": ".. _emulate:\n\nUsing QEMU to Develop Piko/RT\n=============================\n\n\nPrepare QEMU\n------------\n\nYou will need to prepare QEMU STM32 emulator from:\n`QEMU STM32 v0.1.3 <https://github.com/beckus/qemu_stm32/releases>`_\n\nAfter download, compile the qemu_stm32 by::\n\n    $ ./configure --disable-werror --enable-debug \\\n            --target-list=\"arm-softmmu\" \\\n            --extra-cflags=-DDEBUG_CLKTREE \\\n            --extra-cflags=-DDEBUG_STM32_RCC \\\n            --extra-cflags=-DDEBUG_STM32_UART \\\n            --extra-cflags=-DSTM32_UART_NO_BAUD_DELAY \\\n            --extra-cflags=-DSTM32_UART_ENABLE_OVERRUN --python=python2\n    $ make -j8\n\n\nMake sure you have export the ``arm-softmmu`` to ``$PATH````::\n\n    $ export PATH=$PATH:~/qemu_stm32-stm32_v0.1.3/arm-softmmu\n\n\nRun Piko/RT on QEMU STM32-p103\n------------------------------\n\nYou will need to run these command::\n\n    $ make PLAT=stm32p103\n    $ make PLAT=stm32p103 run\n\nIf you run correctly, then you will start Piko/RT and get the shell::\n\n    Memory map:\n      .text   = 00000140--0000315a   12314 Bytes\n      .rodata = 00003190--00003a26    2198 Bytes\n      .data   = 20000000--20000514    1300 Bytes\n      .bss    = 20000518--20001298    3456 Bytes\n      .heap   = 200012a0--200022a0    4096 Bytes\n      .pgmem  = 20008000--2000f000   28672 Bytes\n    Order  Bitmap\n        0  00000000  00000000  00000000  00000000\n        1  00000000  00000000\n        2  00000000\n        3  00007fff\n    Created idle_thread at <0x20008200>\n    Created main_thread at <0x20008800> with priority=31\n    Reclaim early stack's physical memory (2048 Bytes, order=3).\n    Creating /proc/version\n    Creating /proc/meminfo\n    Creating /dev/mem\n    Creating /dev/null\n    Creating /dev/zero\n    Creating /dev/random\n    Creating MTD device mtd0\n    Kernel bootstrap done.\n    --\n\n    $\n"
  },
  {
    "path": "docs/index.rst",
    "content": "Welcome to piko's documentation!\n================================\n\n.. toctree::\n   :maxdepth: 2\n   :caption: Contents:\n\n   build.rst\n   emulate.rst\n   kernel.rst\n   test.rst\n   contribute.rst\n   debugging.rst\n\n.. toctree::\n   :caption: API:\n\n   api.rst\n\n\nIndices and tables\n==================\n\n* :ref:`genindex`\n* :ref:`modindex`\n* :ref:`search`\n"
  },
  {
    "path": "docs/kernel.rst",
    "content": ".. _kernel:\n\nPiko/RT Design & Concept\n========================\n\nPiko/RT is a tiny Linux-like real-time operating system kernel, optimized for\nArm Cortex-M series microprocessors.\n\nCompatibility with Linux is **NOT** the goal of Piko/RT. Instead, PSE51\n(minimal real-time system profile) should be appropriately an analog from\nthe perspective of design.\n"
  },
  {
    "path": "docs/make.bat",
    "content": "@ECHO OFF\r\n\r\npushd %~dp0\r\n\r\nREM Command file for Sphinx documentation\r\n\r\nif \"%SPHINXBUILD%\" == \"\" (\r\n\tset SPHINXBUILD=sphinx-build\r\n)\r\nset SOURCEDIR=.\r\nset BUILDDIR=_build\r\nset SPHINXPROJ=piko\r\n\r\nif \"%1\" == \"\" goto help\r\n\r\n%SPHINXBUILD% >NUL 2>NUL\r\nif errorlevel 9009 (\r\n\techo.\r\n\techo.The 'sphinx-build' command was not found. Make sure you have Sphinx\r\n\techo.installed, then set the SPHINXBUILD environment variable to point\r\n\techo.to the full path of the 'sphinx-build' executable. Alternatively you\r\n\techo.may add the Sphinx directory to PATH.\r\n\techo.\r\n\techo.If you don't have Sphinx installed, grab it from\r\n\techo.http://sphinx-doc.org/\r\n\texit /b 1\r\n)\r\n\r\n%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%\r\ngoto end\r\n\r\n:help\r\n%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%\r\n\r\n:end\r\npopd\r\n"
  },
  {
    "path": "docs/posix/parse.py",
    "content": "# Helper for generate POSIX API table\n# XXX: used from docs, not here\n\nimport glob\nimport os\nimport tempfile\nimport shutil\nimport subprocess\n\nCSCOPE = shutil.which('cscope') if shutil.which('cscope') else '/usr/bin/cscope'\nCSCOPE_DB = tempfile.NamedTemporaryFile()\nBACKTO = '../'\nLIBC_PATH = BACKTO + 'libc'\n\n\ndef read_file(path):\n    # Return list of API\n    return open(path).read().replace(' ', '').replace('\\n', '').strip().split(',')\n\n\ndef build_cscope():\n    CSCOPE_DB.write(subprocess.check_output(\"find %s -name '*.c'\" % LIBC_PATH, shell=True))\n    CSCOPE_DB.write(subprocess.check_output(\"find %s -name '*.h'\" % LIBC_PATH, shell=True))\n    CSCOPE_DB.write(subprocess.check_output(\"find %s -name '*.S'\" % LIBC_PATH, shell=True))\n    CSCOPE_DB.flush()\n    return CSCOPE_DB.name\n\n\ndef cscope_search(name):\n    return subprocess.check_output(\"%s -i %s -L1%s\" % (CSCOPE, CSCOPE_DB.name, name), shell=True)\n\n\ndef parse_path(s):\n    return s.split('\\n')[0].strip(BACKTO).split()[0:3:2] if s and s.startswith(LIBC_PATH) else ('', 0)\n\n\ndef generate_table(name):\n    header = ['.. table:: %s' % os.path.basename(name), '   :widths: 45 10 35', '']\n    longest_api_length = 10\n\n    apis = read_file(name)\n    result = []\n\n    for api in apis:\n        if not api:\n            continue\n        longest_api_length = max(len(api), longest_api_length)\n\n        s = cscope_search(api.strip('()')).decode('utf-8')\n        result.append([api, s.startswith(LIBC_PATH), *parse_path(s)])\n\n    splitline = '   {:=>{}} ===== {}'.format('', longest_api_length, '=' * 30)\n    header.append(splitline)\n    header.append('   {:<{}} IMPL  Path'.format('API Name', longest_api_length))\n    header.append(splitline)\n\n    for i, r in enumerate(result):\n        result[i] = ('   {:<{}} {}'.format(r[0], longest_api_length, 'O' if r[1] else 'X') +\n                     '     {}'.format('{}:{}'.format(r[2], r[3]) if r[1] else ''))\n    result.append(splitline)\n    result = header + result\n\n    return '\\n'.join(result)\n\n\nif __name__ == '__main__':\n    build_cscope()\n\n    reqs = glob.glob('posix/posix_req/*')\n    with open('posix/posix_req.rst', 'w') as f:\n        for req in reqs:\n            f.write(generate_table(req))\n            f.write('\\n\\n')\n\n    opts = glob.glob('posix/posix_opt/*')\n    with open('posix/posix_opt.rst', 'w') as f:\n        for opt in opts:\n            f.write(generate_table(opt))\n            f.write('\\n\\n')\n"
  },
  {
    "path": "docs/posix/posix_opt/01._POSIX_CLOCK_SELECTION",
    "content": "clock_nanosleep()\n"
  },
  {
    "path": "docs/posix/posix_opt/02._POSIX_FSYNC",
    "content": "fsync()\n"
  },
  {
    "path": "docs/posix/posix_opt/03._POSIX_MEMLOCK",
    "content": "m lockall(), m unlockall()\n"
  },
  {
    "path": "docs/posix/posix_opt/04.POSIX_MEMLOCK_RANGE",
    "content": "mlock(), munlock()\n"
  },
  {
    "path": "docs/posix/posix_opt/05._POSIX_MONOTONIC_CLOCK",
    "content": ""
  },
  {
    "path": "docs/posix/posix_opt/06._POSIX_NO_TRUNC",
    "content": ""
  },
  {
    "path": "docs/posix/posix_opt/07._POSIX_REALTIME_SIGNALS",
    "content": "sigqueue(), sigtim edwait(), sigwaitinfo()\n"
  },
  {
    "path": "docs/posix/posix_opt/08._POSIX_SEMAPHORES",
    "content": "sem _close(), sem _destroy(), sem _getvalue(), sem _init(), sem _open(), sem _post(),\nsem _trywait(), sem _wait(), sem _unlink (),\nsem _tim edwait()\n"
  },
  {
    "path": "docs/posix/posix_opt/09._POSIX_SHARED_MEMORY_OBJECTS",
    "content": "shm _open(), shm _unlink()\n"
  },
  {
    "path": "docs/posix/posix_opt/10._POSIX_SYNCHRONIZED_IO",
    "content": "fdatasync()\n"
  },
  {
    "path": "docs/posix/posix_opt/11._POSIX_THREAD_ATTR_STACKADDR",
    "content": "pthread_attr_getstackaddr(), pthread_attr_setstackaddr()\n"
  },
  {
    "path": "docs/posix/posix_opt/12._POSIX_THREAD_ATTR_STACKSIZE",
    "content": "pthread_attr_getstack (), pthread_attr_setstack()\n"
  },
  {
    "path": "docs/posix/posix_opt/13._POSIX_THREAD_CPUTIME",
    "content": "pthread_getcpuclockid()\n"
  },
  {
    "path": "docs/posix/posix_opt/14._POSIX_THREAD_PRIO_INHERIT",
    "content": "pthread_m utexattr_getprotocol(), pthread_m utexattr_setprotocol()\n"
  },
  {
    "path": "docs/posix/posix_opt/15._POSIX_THREAD_PRIO_PROTECT",
    "content": "pthread_m utex_getprioceiling(), pthread_m utex_setprioceiling(),\npthread_m utexattr_getprioceiling(), pthread_m utexattr_getprotocol(),\npthread_m utexattr_setprioceiling(), pthread_m utexattr_setprotocol()\n"
  },
  {
    "path": "docs/posix/posix_opt/16._POSIX_THREAD_PRIORITY_SCHEDULING",
    "content": "pthread_attr_getinheritsched(), pthread_attr_getschedpolicy(),\npthread_attr_getscope(), pthread_attr_setinheritsched(),\npthread_attr_setschedpolicy(), pthread_attr_setscope(),\npthread_getschedparam (), pthread_setschedparam (), pthread_setschedprio(),\nsched_get_priority_m ax(), sched_get_priority_m in(), sched_rr_get_interval()\n"
  },
  {
    "path": "docs/posix/posix_opt/17._POSIX_THREAD_SPORADIC_SERVER",
    "content": ""
  },
  {
    "path": "docs/posix/posix_opt/18._POSIX_TIMERS",
    "content": "clock_getres(), clock_gettim e(), clock_settim e(), nanosleep(), tim er_create(),\ntim er_detele(), tim er_getoverrun(), tim er_gettim e(), tim er_settim e()\n"
  },
  {
    "path": "docs/posix/posix_req/01.POSIX_C_LANG_SUPPORT",
    "content": "abs(), asctime(), asctime_r(), atof(), atoi(), atol(),\natoll(), bsearch(), calloc(), ctime(), ctime_r(),\ndifftime(), div(), feclearexcept(), fegetenv(),\nfegetexceptflag(), fegetround(), feholdexcept(),\nferaiseexcept(), fesetenv(), fesetexceptflag(),\nfesetround(), fetestexcept(), feupdateenv(), free(),\ngmtime(), gmtime_r(), imaxabs(), imaxdiv(),\nisalnum(), isalpha(), isblank(), iscntrl(), isdigit(),\nisgraph(), islower(), isprint(), ispunct(), isspace(),\nisupper(), isxdigit(), labs(), ldiv(), llabs(), lldiv(),\nlocaleconv(), localtime(), localtime_r(), malloc(),\nmemchr(), memcmp(), memcpy(), memmove(),\nmemset(), mktime(), qsort(), rand(), rand_r(),\nrealloc(), setlocale(), snprintf(), sprintf(), srand(),\nsscanf(), strcat(), strchr(), strcmp(), strcoll(), strcpy(),\nstrcspn(), strerror(), strerror_r(), strftime(), strlen(),\nstrncat(), strncmp(), strncpy(), strpbrk(), strrchr(),\nstrspn(), strstr(), strtod(), strtof(), strtoimax(),\nstrtok(), strtok_r(), strtol(), strtold(), strtoll(),\nstrtoul(), strtoull(), strtoumax(), strxfrm(), time(),\ntolower(), toupper(), tzname, tzset(), va_arg(),\nva_copy(), va_end(), va_start(), vsnprintf(), vsprintf(),\nvsscanf()\n"
  },
  {
    "path": "docs/posix/posix_req/02.POSIX_DEVICE_IO",
    "content": "clearerr(), close(), fclose(), fdopen(), feof(), ferror(),\nfflush (), fgetc(), fgets(), fileno(), fopen(), fprintf(),\nfputc(), fputs(), fread(), freopen(), fscanf(), fwrite(),\ngetc(), getchar(), gets(), open(), perror(), printf(),\nputc(), putchar(), puts(), read(), scanf(), setbuf(),\nsetvbuf(), stderr, stdin, stdout, ungetc(), vfprintf(),\nvfscanf(), vprintf(), vscanf(), write()\n"
  },
  {
    "path": "docs/posix/posix_req/03.POSIX_FILE_LOCKING",
    "content": "flockfile(), ftrylockfile(), funlockfile(), getc_unlocked(),\ngetchar_unlocked(), putc_unlocked(),\nputchar_unlocked()\n"
  },
  {
    "path": "docs/posix/posix_req/04.POSIX_SIGNALS",
    "content": "abort(), alarm(), kill(), pause(), raise(), sigaction(),\nsigaddset(), sigdelset(), sigemptyset(), sigfillset(),\nsigismember(), signal(), sigpending(), sigprocmask(),\nsigsuspend(), sigwait()\n"
  },
  {
    "path": "docs/posix/posix_req/05.POSIX_SINGLE_PROCESS",
    "content": "confstr(), environ, errno, getenv(), setenv(), sysconf(),\nuname(), unsetenv()\n"
  },
  {
    "path": "docs/posix/posix_req/06.POSIX_THREADS_BASE",
    "content": "pthread_atfork(), pthread_attr_destroy(),\npthread_attr_getdetachstate(),\npthread_attr_getschedparam(), pthread_attr_init(),\npthread_attr_setdetachstate(),\npthread_attr_setschedparam(), pthread_cancel(),\npthread_cleanup_pop(), pthread_cleanup_push(),\npthread_cond_broadcast(), pthread_cond_destroy(),\npthread_cond_init(), pthread_cond_signal(),\npthread_cond_timedwait(), pthread_cond_wait(),\npthread_condattr_destroy(), pthread_condattr_init(),\npthread_create(), pthread_detach(), pthread_equal(),\npthread_exit(), pthread_getspecific(), pthread_join(),\npthread_key_create(), pthread_key_delete(),\npthread_kill(), pthread_mutex_destroy(),\npthread_mutex_init(), pthread_mutex_lock(),\npthread_mutex_trylock(), pthread_mutex_unlock(),\npthread_mutexattr_destroy(),\npthread_mutexattr_init(), pthread_once(),\npthread_self(), pthread_setcalcelstate(),\npthread_setcanceltype(), pthread_setspecific(),\npthread_sigmask(), pthread_testcancel()\n"
  },
  {
    "path": "docs/posix/posix_req/07.XSI_THREAD_MUTEX_EXT",
    "content": "pthread_mutexattr_gettype(),\npthread_mutexattr_settype()\n"
  },
  {
    "path": "docs/posix/posix_req/08.XSI_THREADS_EXT",
    "content": "pthread_attr_getguardsize(), pthread_attr_getstack(),\npthread_attr_setguardsize(),\npthread_attr_setstack(), pthread_getconcurrency(),\npthread_setconcurrency()\n"
  },
  {
    "path": "docs/test.rst",
    "content": ".. _test:\n\n\nPiko/RT Test Suite\n==================\n\nPiko/RT test suite is under ``tests`` directory, it contain a makefile, many test\nfolder and it self is a python package. Each test folder represent a test case.\n\n\nRun Tests\n---------\n\nTest suite can be run by top directory Makefile::\n\n   $ make PLAT=stm32p103 check\n\nThis will run all test in the test suite, also, you can use ``tests`` as a python\npackage, to run all test via python command line::\n\n   $ python -m tests\n\nYou can also run partial via python::\n\n   $ python -m tests fs_1 stat_1 thread_5\n\nPython command line tool verbose default is disable, you can pass the flag ``-v``\nto enable::\n\n   $ python -m tests -v\n   $ python -m tests fs_1 -v\n"
  },
  {
    "path": "drivers/char/mem.c",
    "content": "#include <string.h>\n\n#include <kernel/fs.h>\n#include <kernel/kernel.h>\n\nstatic int mem_open(__unused struct inode *inode, __unused struct file *file)\n{\n    return 0;\n}\n\nstatic ssize_t write_mem(__unused struct file *file,\n                         const char *buf,\n                         size_t count,\n                         off_t *offset)\n{\n    memcpy((void *) offset, buf, count);\n\n    return count;\n}\n\nstatic ssize_t read_mem(__unused struct file *file,\n                        char *buf,\n                        size_t count,\n                        off_t offset)\n{\n    memcpy(buf, (void *) offset, count);\n\n    return count;\n}\n\nstatic ssize_t write_null(__unused struct file *file,\n                          __unused const char *buf,\n                          size_t count,\n                          __unused off_t *offset)\n{\n    return count;\n}\n\nstatic ssize_t read_null(__unused struct file *file,\n                         __unused char *buf,\n                         __unused size_t count,\n                         __unused off_t offset)\n{\n    return 0;\n}\n\nstatic ssize_t read_zero(__unused struct file *file,\n                         char *buf,\n                         size_t count,\n                         __unused off_t offset)\n{\n    memset(buf, 0, count);\n\n    return count;\n}\n\nstatic const struct file_operations mem_fops = {\n    .open = mem_open,\n    .read = read_mem,\n    .write = write_mem,\n};\n\nstatic const struct file_operations null_fops = {\n    .open = mem_open,\n    .read = read_null,\n    .write = write_null,\n};\n\nstatic const struct file_operations zero_fops = {\n    .open = mem_open,\n    .read = read_zero,\n    .write = write_null,\n};\n\nextern const struct file_operations random_fops;\nextern const struct inode_operations tmpfs_iops;\n\nstatic struct inode memdev_inodes[] = {\n    {\n        /* /dev/mem */\n        .i_ino = 101,\n        .i_op = &tmpfs_iops,\n        .i_fop = &mem_fops,\n    },\n    {\n        /* /dev/null */\n        .i_ino = 102,\n        .i_op = &tmpfs_iops,\n        .i_fop = &null_fops,\n    },\n    {\n        /* /dev/zero */\n        .i_ino = 103,\n        .i_op = &tmpfs_iops,\n        .i_fop = &zero_fops,\n    },\n    {\n        /* /dev/random */\n        .i_ino = 104,\n        .i_op = &tmpfs_iops,\n        .i_fop = &random_fops,\n    },\n};\n\nvoid memdev_init(void)\n{\n    struct dentry dentry;\n    const char *names[] = {\"mem\", \"null\", \"zero\", \"random\"};\n\n    for (int i = 0; i < 4; i++) {\n        printk(\"Creating /dev/%s\\n\", names[i]);\n        dentry.d_inode = &memdev_inodes[i], strcpy(dentry.d_name, names[i]);\n        vfs_link(0, dev_inode(), &dentry);\n    }\n}\n"
  },
  {
    "path": "drivers/char/random.c",
    "content": "#include <stdint.h>\n#include <string.h>\n#include <sys/param.h>\n\n//#include <kernel/kernel.h>\n#include <kernel/fs.h>\n\ntypedef unsigned long long u64;\n\n/* xorshift1024* generator, http://vigna.di.unimi.it/ftp/papers/xorshift.pdf */\nstatic u64 next(void)\n{\n    // clang-format off\n    static u64 s[16] = {\n        0x84242f96eca9c41dull, 0xa3c65b8776f96855ull, 0x5b34a39f070b5837ull,\n        0x4489affce4f31a1eull, 0x2ffeeb0a48316f40ull, 0xdc2d9891fe68c022ull,\n        0x3659132bb12fea70ull, 0xaac17d8efa43cab8ull, 0xc4cb815590989b13ull,\n        0x5ee975283d71c93bull, 0x691548c86c1bd540ull, 0x7910c41d10a1e6a5ull,\n        0x0b5fc64563b3e2a8ull, 0x047f7684e9fc949dull, 0xb99181f2d8f685caull,\n        0x284600e3f30e38c3ull\n    };\n    // clang-format on\n\n    static int p;\n    const u64 s0 = s[p];\n    u64 s1 = s[p = (p + 1) & 15];\n\n    s1 ^= s1 << 31;                            // a\n    s[p] = s1 ^ s0 ^ (s1 >> 11) ^ (s0 >> 30);  // b,c\n\n    return s[p] * UINT64_C(1181783497276652981);\n}\n\nstatic int open_random(__unused struct inode *inode, __unused struct file *file)\n{\n    return 0;\n}\n\nstatic ssize_t read_random(__unused struct file *file,\n                           char *buf,\n                           size_t count,\n                           __unused off_t offset)\n{\n    static u64 m;\n    static int remaining_bytes = 0;\n\n    if ((int) count <= remaining_bytes) {\n        memcpy(buf, (char *) &m + 8 - remaining_bytes, count);\n        remaining_bytes -= count;\n    } else {\n        for (int i = count; i > 0; i -= 8, buf = (char *) buf + 8) {\n            m = next();\n            memcpy(buf, &m, MIN(i, 8));\n            remaining_bytes = 8 - MIN(i, 8);\n        }\n    }\n\n    return count;\n}\n\nconst struct file_operations random_fops = {\n    .open = open_random,\n    .read = read_random,\n};\n"
  },
  {
    "path": "drivers/mtd/mtd.h",
    "content": "#ifndef _DRIVERS_MTD_MTD_H\n#define _DRIVERS_MTD_MTD_H\n\n#include <sys/types.h>\n\n#include <kernel/errno-base.h>\n#include <kernel/types.h>\n\n#define MTD_ERASE_PENDING (1 << 0)\n#define MTD_ERASING (1 << 1)\n#define MTD_ERASE_SUSPEND (1 << 2)\n#define MTD_ERASE_DONE (1 << 3)\n#define MTD_ERASE_FAILED (1 << 4)\n\n#define MTD_FAIL_ADDR_UNKNOWN -1l\n\n#define MTD_WRITEABLE 0x400 /* Device is writeable */\n\nstruct erase_info {\n    /* struct mtd_info *mtd; */\n    u32 addr;\n    u32 len;\n    u32 fail_addr;\n    /* u_long time; */\n    /* u_long retries; */\n    /* unsigned dev; */\n    /* unsigned cell; */\n    void (*callback)(struct erase_info *self);\n    unsigned long priv;\n    unsigned char state;\n    struct erase_info *next;\n};\n\nstruct mtd_info {\n    unsigned char type;\n    u32 flags;\n    unsigned long size;\n    unsigned long erasesize;\n    unsigned long writesize;\n\n    const char *name;  // can this be a hash?\n\n    int (*mtd_erase)(struct mtd_info *mtd, struct erase_info *instr);\n    int (*mtd_point)(struct mtd_info *mtd,\n                     off_t from,\n                     size_t len,\n                     size_t *retlen,\n                     void **virt /* , resource_size_t *phys */);  // virt -> at\n    int (*mtd_unpoint)(struct mtd_info *mtd, off_t from, size_t len);\n    int (*mtd_read)(struct mtd_info *mtd,\n                    off_t from,\n                    size_t len,\n                    size_t *retlen,\n                    void *buf);\n    int (*mtd_write)(struct mtd_info *mtd,\n                     off_t to,\n                     size_t len,\n                     size_t *retlen,\n                     const void *buf);\n\n    /* struct device *dev; */\n    void *priv;  // can be the physical address of the flash device\n};\n\nint mtd_erase(struct mtd_info *mtd, struct erase_info *instr);\nint mtd_point(struct mtd_info *mtd,\n              off_t from,\n              size_t len,\n              size_t *retlen,\n              void **virt /* , resource_size_t *phys */);\nint mtd_read(struct mtd_info *mtd,\n             off_t from,\n             size_t len,\n             size_t *retlen,\n             unsigned char *buf);\nint mtd_write(struct mtd_info *mtd,\n              off_t to,\n              size_t len,\n              size_t *retlen,\n              const unsigned char *buf);\n\nvoid mtd_erase_callback(struct erase_info *instr);\n\nint mtdram_init_device(struct mtd_info *mtd,\n                       void *mapped_address,\n                       unsigned long size,\n                       const char *name);\nvoid mtdram_init(void);\n\n#endif /* !_DRIVERS_MTD_MTD_H */\n"
  },
  {
    "path": "drivers/mtd/mtdchar.c",
    "content": "#include <sys/types.h>\n#include <kernel/fs.h>\n#include <drivers/mtd/mtd.h>\n\n/* romfs depends on mtdchar */\n\nstatic int mtdchar_open(struct inode *inode, struct file *file)\n{\n    file->f_private = inode->i_private;\n\n    return 0;\n}\n\nstatic ssize_t mtdchar_read(struct file *file,\n                            char *buf,\n                            size_t count,\n                            off_t offset)\n{\n    size_t retlen;\n    struct mtd_info *mtd = file->f_private;\n\n    if (mtd_read(mtd, offset, count, &retlen, (unsigned char *) buf) < 0)\n        return -1;\n\n    return retlen;\n}\n\nstatic ssize_t mtdchar_write(struct file *file,\n                             const char *buf,\n                             size_t count,\n                             off_t *offset)\n{\n    size_t retlen;\n    struct mtd_info *mtd = file->f_private;\n\n    if (mtd_write(mtd, *offset, count, &retlen, (const unsigned char *) buf) <\n        0)\n        return -1;\n\n    return retlen;\n}\n\nconst struct file_operations mtdchar_fops = {\n    .open = mtdchar_open,\n    .read = mtdchar_read,\n    .write = mtdchar_write,\n};\n"
  },
  {
    "path": "drivers/mtd/mtdcore.c",
    "content": "#include <stddef.h>\n#include <drivers/mtd/mtd.h>\n\nvoid mtd_erase_callback(struct erase_info *instr)\n{\n    if (instr->callback)\n        instr->callback(instr);\n}\n\nint mtd_erase(struct mtd_info *mtd, struct erase_info *instr)\n{\n    if ((instr->addr >= mtd->size) || (instr->len > mtd->size - instr->addr))\n        return -EINVAL;\n    if (!(mtd->flags & MTD_WRITEABLE))\n        return -EROFS;\n    instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;\n    if (!instr->len) {\n        instr->state = MTD_ERASE_DONE;\n        mtd_erase_callback(instr);\n        return 0;\n    }\n\n    return mtd->mtd_erase(mtd, instr);\n}\n\nint mtd_point(struct mtd_info *mtd,\n              off_t from,\n              size_t len,\n              size_t *retlen,\n              void **virt /* , resource_size_t *phys */)\n{\n    *retlen = 0;\n    *virt = NULL;\n    if (!mtd->mtd_point)\n        return -1;  // return -EOPNOTSUPP;\n    if ((from < 0) || ((unsigned long) from >= mtd->size) ||\n        (len > mtd->size - from))\n        return -EINVAL;\n    if (!len)\n        return 0;\n\n    return mtd->mtd_point(mtd, from, len, retlen, virt /* , phys */);\n}\n\nint mtd_read(struct mtd_info *mtd,\n             off_t from,\n             size_t len,\n             size_t *retlen,\n             unsigned char *buf)\n{\n    *retlen = 0;\n    if ((from < 0) || ((unsigned long) from >= mtd->size) ||\n        (len > mtd->size - from))\n        return -EINVAL;\n    if (!len)\n        return 0;\n\n    return mtd->mtd_read(mtd, from, len, retlen, buf);\n}\n\nint mtd_write(struct mtd_info *mtd,\n              off_t to,\n              size_t len,\n              size_t *retlen,\n              const unsigned char *buf)\n{\n    *retlen = 0;\n    if ((to < 0) || ((unsigned long) to >= mtd->size) || (len > mtd->size - to))\n        return -EINVAL;\n    if (!len)\n        return 0;\n\n    return mtd->mtd_write(mtd, to, len, retlen, buf);\n}\n"
  },
  {
    "path": "drivers/mtd/mtdram.c",
    "content": "#include <string.h>\n\n#include <kernel/fs.h>\n#include <kernel/kernel.h>\n#include <drivers/mtd/mtd.h>\n\n#define SIZE_1KB 1024\n\nstatic int mtdram_erase(struct mtd_info *mtd, struct erase_info *instr)\n{\n    memset((char *) mtd->priv + instr->addr, 0xff, instr->len);\n    instr->state = MTD_ERASE_DONE;\n    mtd_erase_callback(instr);\n\n    return 0;\n}\n\nstatic int mtdram_point(struct mtd_info *mtd,\n                        off_t from,\n                        size_t len,\n                        size_t *retlen,\n                        void **virt /* , resource_size_t *phys */)  // void **at\n{\n    *virt = mtd->priv + from;\n    *retlen = len;\n\n    return 0;\n}\n\nstatic int mtdram_unpoint(struct mtd_info *mtd, off_t from, size_t len)\n{\n    (void) mtd, (void) from, (void) len;\n\n    return 0;\n}\n\nint mtdram_write(struct mtd_info *mtd,\n                 off_t to,\n                 size_t len,\n                 size_t *retlen,\n                 const void *buf)\n{\n    memcpy((char *) mtd->priv + to, buf, len);\n    *retlen = len;\n\n    return 0;\n}\n\nint mtdram_read(struct mtd_info *mtd,\n                off_t from,\n                size_t len,\n                size_t *retlen,\n                void *buf)\n{\n    memcpy(buf, mtd->priv + from, len);\n    *retlen = len;\n\n    return 0;\n}\n\nint mtdram_init_device(struct mtd_info *mtd,\n                       void *mapped_address,\n                       unsigned long size,\n                       const char *name)\n{\n    if (size % SIZE_1KB)\n        return -1;\n    mtd->name = name;\n    mtd->size = size;\n    mtd->erasesize = SIZE_1KB, mtd->priv = mapped_address;\n    mtd->mtd_erase = mtdram_erase;\n    mtd->mtd_point = mtdram_point;\n    mtd->mtd_unpoint = mtdram_unpoint;\n    mtd->mtd_read = mtdram_read;\n    mtd->mtd_write = mtdram_write;\n\n    return 0;\n}\n\nstruct mtd_info mtdram;\n\nextern char __mtdram_start__;\nextern char __mtdram_size__;\n\nextern const struct file_operations mtdchar_fops;\n\nstatic struct inode mtd0_inode = {\n    .i_fop = &mtdchar_fops,\n    .i_private = &mtdram,\n};\n\nvoid mtdram_init(void)\n{\n    struct dentry dentry = {.d_inode = &mtd0_inode, .d_name = \"mtd0\"};\n\n    printk(\"Creating MTD device %s\\n\", dentry.d_name);\n    mtdram_init_device(&mtdram, &__mtdram_start__,\n                       (unsigned long) &__mtdram_size__, dentry.d_name);\n\n    init_tmpfs_inode(&mtd0_inode);\n    vfs_link(NULL, dev_inode(), &dentry);\n}\n"
  },
  {
    "path": "drivers/serial/serialchar.c",
    "content": "#include <sys/types.h>\n#include <kernel/fs.h>\n#include <kernel/sched.h>\n#include <kernel/serial.h>\n#include <kernel/thread.h>\n\nstatic void serialchar_callback(struct serial_info *serial)\n{\n    sched_enqueue(serial->owner);\n}\n\nstatic int serialchar_open(struct inode *inode, struct file *file)\n{\n    file->f_private = inode->i_private;\n    struct serial_info *serial = file->f_private;\n    CURRENT_THREAD_INFO(cur_thread);\n    serial->owner = cur_thread;\n    serial->ops->callback = serialchar_callback;\n\n    return 0;\n}\n\nstatic ssize_t serialchar_read(struct file *file,\n                               char *buf,\n                               size_t count,\n                               __unused off_t offset)\n{\n    size_t retlen;\n    struct serial_info *serial = file->f_private;\n\n    while (serial->rx_count < count) {\n        CURRENT_THREAD_INFO(cur_thread);\n        sched_dequeue(cur_thread);\n        sched_elect(0);\n    }\n    if (count == 1)\n        return serial_getc(serial, buf);\n    if (serial_gets(serial, count, &retlen, buf) < 0)\n        return -1;\n\n    return retlen;\n}\n\nstatic ssize_t serialchar_write(struct file *file,\n                                const char *buf,\n                                size_t count,\n                                __unused off_t *offset)\n{\n    struct serial_info *serial = file->f_private;\n\n    return serial_puts(serial, count, buf);\n}\n\nconst struct file_operations serialchar_fops = {\n    .open = serialchar_open,\n    .read = serialchar_read,\n    .write = serialchar_write,\n};\n"
  },
  {
    "path": "drivers/serial/serialcore.c",
    "content": "#include <sys/types.h>\n\n#include <kernel/serial.h>\n\nvoid serial_init(void)\n{\n    extern unsigned long __serial_hook_start__;\n    extern unsigned long __serial_hook_end__;\n\n    for (struct serial_hook *hook = (struct serial_hook *) &__serial_hook_start__;\n            hook < (struct serial_hook *) &__serial_hook_end__; hook++)\n        hook->init();\n}\n\nvoid serial_activity_callback(struct serial_info *serial)\n{\n    if (serial->ops->callback)\n        serial->ops->callback(serial);\n}\n\nint serial_getc(struct serial_info *serial, char *c)\n{\n    return serial->ops->serial_getc(serial, c);\n}\n\nint serial_gets(struct serial_info *serial,\n                size_t len,\n                size_t *retlen,\n                char *buf)\n{\n    return serial->ops->serial_gets(serial, len, retlen, buf);\n}\n\nint serial_putc(struct serial_info *serial, char c)\n{\n    return serial->ops->serial_putc(serial, c);\n}\n\nint serial_puts(struct serial_info *serial,\n                size_t len,\n                const char *buf)\n{\n    return serial->ops->serial_puts(serial, len, buf);\n}\n"
  },
  {
    "path": "drivers/serial/stm32-uart.c",
    "content": "#include <stdio.h>\n#include <sys/types.h>\n\n#include <kernel/cbuf.h>\n#include <kernel/fs.h>\n#include <kernel/irq.h>\n#include <kernel/serial.h>\n#include <kernel/types.h>\n\n#include <cmsis.h>\n#include \"platform.h\"\n\nstatic struct cbuf_info cbuf;\nstatic char buf[16];\n\nstatic int stm32_getc(struct serial_info *serial, char *c)\n{\n    serial->rx_count--;\n    cbuf_getc(&cbuf, c);\n\n    return 0;\n}\n\nstatic int stm32_putc(struct serial_info *serial, char c)\n{\n    if (!serial || c < 0)\n        return EOF;\n\n    USART_TypeDef *uart = serial->priv;\n\n    while (!(uart->SR & USART_SR_TXE))\n        ;\n    uart->DR = c;\n\n    return c;\n}\n\nstatic int stm32_puts(struct serial_info *serial,\n                      size_t len,\n                      const char *buf)\n{\n    int i = 0;\n\n    for (i = 0; i < len; i++)\n        if (stm32_putc(serial, buf[i]) == EOF)\n            return EOF;\n\n    return i;\n}\n\nstatic struct serial_ops stm32_ops = {\n    .serial_getc = stm32_getc,\n    .serial_putc = stm32_putc,\n    .serial_puts = stm32_puts,\n};\n\nstruct serial_info stm32_info = {\n    .priv = USARTx,\n    .ops = &stm32_ops,\n};\n\nstatic void stm32_uartx_isr(void)\n{\n    if (USARTx->SR & USART_SR_RXNE) {\n        char c = (char) USARTx->DR;\n        cbuf_putc(&cbuf, c);\n        stm32_info.rx_count++;\n        serial_activity_callback(&stm32_info);\n    }\n}\n\nextern const struct file_operations serialchar_fops;\n\nstatic struct inode stm32_inode = {\n    .i_fop = &serialchar_fops,\n    .i_private = &stm32_info,\n};\n\nstatic int stm32_serial_init(void)\n{\n    uart_init();\n\n    struct dentry dentry = {.d_inode = &stm32_inode, .d_name = \"ttyS0\"};\n\n    cbuf_init(&cbuf, buf, 16);\n\n    init_tmpfs_inode(&stm32_inode);\n    vfs_link(0, dev_inode(), &dentry);\n\n    /* enable rx interrupt */\n    request_irq(USARTx_IRQn, stm32_uartx_isr);\n    NVIC_SetPriority(USARTx_IRQn, 0xE);\n    NVIC_EnableIRQ(USARTx_IRQn);\n\n    return 0;\n}\n\nHOOK_SERIAL_INIT(UART, stm32_serial_init)\n"
  },
  {
    "path": "drivers/serial/stm32-uart.h",
    "content": "#ifndef _DRIVER_SERIAL_STM32_USART_H\n#define _DRIVER_SERIAL_STM32_USART_H\n\n#include \"platform.h\"\n\nstruct stm32_uart_port {\n    GPIO_TypeDef *gpio_tx;\n    GPIO_TypeDef *gpio_rx;\n\n    GPIO_InitTypeDef gpio_tx_init_info;\n    GPIO_InitTypeDef gpio_rx_init_info;\n\n    UART_HandleTypeDef uart_init_info;\n\n    void (*gpio_init)(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);\n\n    HAL_StatusTypeDef (*uart_init)(UART_HandleTypeDef *huart);\n\n    void (*gpio_tx_clk_enable)(void);\n    void (*gpio_rx_clk_enable)(void);\n    void (*uart_clk_enable)(void);\n};\n\n#endif /* !_DRIVER_SERIAL_STM32_USART_H */\n"
  },
  {
    "path": "drivers/timer/systick.c",
    "content": "/*\n * Low-resolution (1kHz) SysTick-based timers to support the common\n * driver's timer interface.\n */\n\n#include <errno.h>\n#include <stdlib.h>\n#include <string.h>\n#include <time.h>\n\n#include <kernel/errno-base.h>\n#include <kernel/time.h>\n\n#include <kernel/softirq.h>\n\n#include \"linux/list.h\"\n\nstruct systick_timer {\n    unsigned long start_clocktime;\n    unsigned long expire_clocktime;\n    struct list_head list;\n    struct timer_info *timer; /* backlink */\n};\n\nstatic unsigned long clocktime_in_msec;\n\nstatic LIST_HEAD(systick_timers);\n\nstatic int systick_timer_alloc(struct timer_info *timer)\n{\n    struct systick_timer *systick_timer;\n\n    systick_timer = malloc(sizeof(struct systick_timer));\n    if (!systick_timer) {\n        errno = ENOMEM;\n        return -1;\n    }\n    timer->dev = systick_timer;\n    systick_timer->timer = timer;\n\n    return 0;\n}\n\nstatic int systick_timer_set(struct timer_info *timer,\n                             const struct timespec *value)\n{\n    struct systick_timer *systick_timer = (struct systick_timer *) timer->dev;\n\n    if (!value->tv_sec && !value->tv_nsec)\n        list_del(&systick_timer->list);\n    if (value->tv_sec || value->tv_nsec) {\n        systick_timer->start_clocktime = clocktime_in_msec;\n        systick_timer->expire_clocktime =\n            clocktime_in_msec + value->tv_sec * 1000 + value->tv_nsec / 1000000;\n        list_add(&systick_timer->list, &systick_timers);\n    }\n\n    return 0;\n}\n\nstatic int systick_timer_get(struct timer_info *timer,\n                             struct itimerspec *curr_value)\n{\n    struct systick_timer *systick_timer = (struct systick_timer *) timer->dev;\n    unsigned long msecs = clocktime_in_msec - systick_timer->start_clocktime;\n\n    curr_value->it_value.tv_sec = msecs / 1000;\n    curr_value->it_value.tv_nsec = (msecs % 1000) * 1000000;\n\n    return 0;\n}\n\nstatic int systick_timer_free(struct timer_info *timer)\n{\n    free(timer->dev);\n\n    return 0;\n}\n\nstatic void systick_bh(void)\n{\n    // XXX: is list_for_each_entry_safe() reentrant?\n    struct systick_timer *pos, *pos1;\n    list_for_each_entry_safe(pos, pos1, &systick_timers, list)\n    {\n        if (pos->expire_clocktime < clocktime_in_msec) {\n            list_del(&pos->list);\n            struct timer_info *timer = pos->timer;\n            if (timer->type == INTERVAL_TIMER)\n                systick_timer_set(timer, &timer->value.it_interval);\n            timer_expire_callback(timer);\n        }\n    }\n}\n\n#define SYSTICK_FREQ_IN_HZ 1000\n#define SYSTICK_PERIOD_IN_MSECS (SYSTICK_FREQ_IN_HZ / 1000)\n\n#include \"platform.h\"\nvoid systick(void)\n{\n    clocktime_in_msec += SYSTICK_PERIOD_IN_MSECS;\n\n    static struct tasklet_struct *tick_tsklet;\n    if (!tick_tsklet)\n        tick_tsklet = tasklet_init(systick_bh, NULL, TIMER_SOFTIRQ_PRIO);\n\n    tasklet_schedule(tick_tsklet);\n\n    SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;\n}\n\nconst struct timer_operations systick_tops = {\n    .timer_alloc = systick_timer_alloc,\n    .timer_set = systick_timer_set,\n    .timer_get = systick_timer_get,\n    .timer_free = systick_timer_free,\n};\n\nvoid __attribute__((naked)) pendsv(void)\n{\n    __asm__ __volatile__(\n        \"push\t{lr}    \\n\\t\"\n        \"mov\tr0,\t#3  \\n\\t\" /*SCHED_OPT_TICK*/\n        \"bl\tsched_elect \\n\\t\"\n        \"pop\t{pc}\" ::);\n}\n"
  },
  {
    "path": "drivers/timer/timercore.c",
    "content": "#include <errno.h>\n#include <stdlib.h>\n#include <time.h>\n\n#include <kernel/errno-base.h>\n#include <kernel/time.h>\n\nstatic struct timer_operations *timer_operations;\n\nvoid config_timer_operations(struct timer_operations *tops)\n{\n    timer_operations = tops;\n}\n\nstruct timer_info *timer_alloc(void)\n{\n    struct timer_info *timer;\n\n    timer = malloc(sizeof(struct timer_info));\n    if (!timer) {\n        errno = ENOMEM;\n        return NULL;\n    }\n    timer->tops = timer_operations;  // FIXME: get info from vnode or device\n    if (timer->tops->timer_alloc(timer)) {\n        free(timer);\n        return NULL;\n    }\n\n    return timer;\n}\n\nint timer_set(struct timer_info *timer, const struct timespec *value)\n{\n    return timer->tops->timer_set(timer, value);\n}\n\nint timer_get(struct timer_info *timer, struct itimerspec *value)\n{\n    return timer->tops->timer_get(timer, value);\n}\n\nint timer_free(struct timer_info *timer)\n{\n    if (!timer->disarmed) {\n        const struct timespec zero_val = {0, 0};\n        timer_set(timer, &zero_val);\n    }\n    timer->tops->timer_free(timer);\n    free(timer);\n\n    return 0;\n}\n\nvoid timer_expire_callback(struct timer_info *timer)\n{\n    if (timer->callback)\n        timer->callback(timer);\n}\n"
  },
  {
    "path": "fs/proc.c",
    "content": "#include <string.h>\n#include <sys/param.h>\n\n#include <kernel/fs.h>\n#include <kernel/kernel.h>\n\n// https://linux.die.net/lkmpg/x861.html\n// https://lwn.net/Articles/22355/\n\nextern const char _version_ptr;\nextern const int _version_len;\n\nstatic int open_version(__unused struct inode *inode,\n                        __unused struct file *file)\n{\n    return 0;\n}\n\nstatic ssize_t read_version(__unused struct file *file,\n                            char *buf,\n                            size_t count,\n                            off_t offset)\n{\n    size_t n = MIN(count, (int) &_version_len - offset);\n\n    strncpy(buf, &_version_ptr + offset, n);\n\n    return n;\n}\n\nstatic int open_meminfo(__unused struct inode *inode,\n                        __unused struct file *file)\n{\n    return 0;\n}\n\nstatic ssize_t read_meminfo(__unused struct file *file,\n                            char *buf,\n                            size_t count,\n                            off_t offset)\n{\n    memcpy(buf, (void *) offset, count);\n\n    return count;\n}\n\nstatic const struct file_operations version_fops = {\n    .open = open_version,\n    .read = read_version,\n};\n\nstatic const struct file_operations meminfo_fops = {\n    .open = open_meminfo,\n    .read = read_meminfo,\n};\n\nextern const struct inode_operations tmpfs_iops;\n\nstatic struct inode proc_inodes[] = {\n    {\n        /* /proc/version */\n        .i_ino = 1001,\n        .i_op = &tmpfs_iops,\n        .i_fop = &version_fops,\n    },\n    {\n        /* /proc/meminfo */\n        .i_ino = 1002,\n        .i_op = &tmpfs_iops,\n        .i_fop = &meminfo_fops,\n    },\n};\n\nvoid proc_init(void)\n{\n    struct dentry dentry;\n    const char *names[] = {\n        \"version\", \"meminfo\",\n    };\n\n    for (int i = 0; i < 2; i++) {\n        printk(\"Creating /proc/%s\\n\", names[i]);\n        dentry.d_inode = &proc_inodes[i], strcpy(dentry.d_name, names[i]);\n        vfs_link(0, proc_inode(), &dentry);\n    }\n}\n"
  },
  {
    "path": "fs/romfs.c",
    "content": "/* https://www.kernel.org/doc/Documentation/filesystems/romfs.txt */\n\n#include <stddef.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n\n#include <piko/arpa/inet.h> /* words are big endian in romfs */\n\n#include <kernel/fs.h>\n#include <fs/romfs.h>\n#include <kernel/kernel.h>\n\n#include <drivers/mtd/mtd.h>\n\n#include \"linux/list.h\"\n\nconst struct inode_operations romfs_iops;\nconst struct file_operations romfs_fops;\nconst struct dentry_operations romfs_dops;\n\nstatic char *basename(const char *filename)\n{\n    char *p = strrchr(filename, '/');\n\n    return p ? p + 1 : (char *) filename;\n}\n\nstatic off_t offsetof_device_inode(struct romfs_inode *rinode,\n                                   struct romfs_superblock *super)\n{\n    return (off_t) rinode - (off_t) super;\n}\n\nstatic off_t offsetof_first_device_inode(struct romfs_superblock *super)\n{\n    /* volume_name is a null-terminated string */\n    int len = align_next(strlen(super->volume_name) + 1, 16);\n\n    return offsetof(struct romfs_superblock, volume_name) + len;\n}\n\n// mount(\"/dev/mtd\", \"/media/flash\", \"romfs\", 0, NULL);\nint romfs_mount(const char *source,\n                const char *target,\n                __unused const char *filesystemtype,\n                __unused unsigned long mountflags,\n                __unused const void *data)\n{\n    struct inode *s_inode = inode_from_pathname(source);\n    if (!s_inode)\n        return -1;\n\n    // FIXME: Use mkdir() or create()\n    struct inode *inode = malloc(sizeof(struct inode));\n    if (!inode)\n        return -1;\n    init_tmpfs_inode(inode);\n    inode->i_op = &romfs_iops;\n    inode->i_mode = S_IFDIR;\n    inode->i_size = 0;\n\n    // link mounted-over inode to parent directory\n    struct dentry dentry;\n    printk(\"Creating /dev/%s\\n\", basename(target));\n    dentry.d_inode = inode;\n    strcpy(dentry.d_name, basename(target));\n    vfs_link(0, dev_inode(), &dentry);\n\n    /* Allocate a super_block struct that will be released on filesystem\n     * unmount. */\n    struct super_block *super_block = malloc(sizeof(struct super_block));\n    if (!super_block)\n        return -1;\n    /* super_block is found at the begining of memory area on MTD dev */\n    struct mtd_info *mtd = s_inode->i_private;\n    super_block->s_private = mtd;\n    super_block->s_iroot =\n        inode;  // FIXME: super_block must point to dentry instead of inode\n    inode->i_sb = super_block;\n    struct romfs_superblock *super = mtd->priv;\n    inode->i_private = (void *) offsetof_first_device_inode(super);\n\n    return 0;\n}\n\nstatic struct inode *alloc_inode(struct romfs_inode *ri, struct super_block *sb)\n{\n    struct inode *inode;\n    static int ino = 0xbeef;\n\n    inode = malloc(sizeof(struct inode));\n    if (!inode)\n        return NULL;\n\n    switch (ntohl(ri->next_filehdr) & ROMFS_FILETYPE_MASK) {\n    case ROMFS_FILETYPE_DIR:\n        inode->i_mode = S_IFDIR;\n        break;\n    case ROMFS_FILETYPE_REG:\n        inode->i_mode = S_IFREG;\n        break;\n    case ROMFS_FILETYPE_LNK:\n        inode->i_mode = S_IFLNK;\n        break;\n    case ROMFS_FILETYPE_BLK:\n        inode->i_mode = S_IFBLK;\n        break;\n    case ROMFS_FILETYPE_CHR:\n        inode->i_mode = S_IFCHR;\n        break;\n    case ROMFS_FILETYPE_FIFO:\n        inode->i_mode = S_IFIFO;\n        break;\n    default:\n        inode->i_mode = 0;\n    }\n    inode->i_ino = ino++;\n    inode->i_size = ntohl(ri->size);\n    inode->i_op = &romfs_iops;\n    inode->i_fop = &romfs_fops;\n    inode->i_sb = sb;\n\n    /* We store the offset to on-device inode rather than the logical\n     * address of the on-device inode, because that does not work if\n     * fs is stored on a SPI flash for instance, and is not directly\n     * mapped onto logical address space. */\n    inode->i_private =\n        (void *) offsetof_device_inode(ri, ROMFS_SUPER_BLOCK(sb));\n\n    return inode;\n}\n\nstruct dentry *romfs_lookup(struct inode *dir, struct dentry *target)\n{\n    __u32 next_filehdr = 0;\n    struct romfs_superblock *rs;\n    struct romfs_inode *ri;\n\n    /* get current on-device inode */\n    rs = ROMFS_SUPER_BLOCK(dir->i_sb);\n    ri = ROMFS_INODE(rs, dir->i_private);\n\n    /* enter and walk the directory */\n    next_filehdr = align(ntohl(ri->spec_info), 16);\n    ri = ROMFS_INODE(rs, next_filehdr);\n\n    for (int i = 0; next_filehdr < rs->full_size; i++) {\n        if (!strcmp(ri->file_name, target->d_name)) {\n            struct inode *inode = alloc_inode(ri, dir->i_sb);\n            if (!inode)\n                return NULL;\n\n            target->d_inode = inode;\n            target->d_op = &romfs_dops;\n\n            return target;\n        }\n\n        /* inspect next file in current directory */\n        next_filehdr = align(ntohl(ri->next_filehdr), 16);\n        if (!next_filehdr)\n            break;\n        ri = ROMFS_INODE(rs, next_filehdr);\n    }\n\n    return NULL;\n}\n\nint romfs_open(struct inode *inode, struct file *file)\n{\n    file->f_private = inode->i_private;\n\n    return 0;\n}\n\nssize_t romfs_read(struct file *file, char *buf, size_t count, off_t offset)\n{\n    size_t retlen;\n    size_t filesize = file->f_dentry->d_inode->i_size;\n    struct inode *inode = file->f_dentry->d_inode;\n    struct super_block *sb = inode->i_sb;\n    struct mtd_info *mtd = sb->s_private;\n    struct romfs_superblock *rs = ROMFS_SUPER_BLOCK(sb);\n    struct romfs_inode *ri = ROMFS_INODE(rs, inode->i_private);\n    int len =\n        sizeof(struct romfs_inode) + align_next(strlen(ri->file_name) + 1, 16);\n\n    if (file->f_pos + count > filesize)\n        count = filesize - offset;\n    mtd_read(mtd, (off_t) inode->i_private + len + offset, count, &retlen,\n             (unsigned char *) buf);\n\n    return retlen;\n}\n\nint romfs_mmap(struct file *file, off_t offset, void **addr)\n{\n    size_t retlen;\n    size_t filesize = file->f_dentry->d_inode->i_size;\n    struct inode *inode = file->f_dentry->d_inode;\n    struct super_block *sb = inode->i_sb;\n    struct mtd_info *mtd = sb->s_private;\n    struct romfs_superblock *rs = ROMFS_SUPER_BLOCK(sb);\n    struct romfs_inode *ri = ROMFS_INODE(rs, inode->i_private);\n    int len =\n        sizeof(struct romfs_inode) + align_next(strlen(ri->file_name) + 1, 16);\n\n    return mtd_point(mtd, (off_t) inode->i_private + len + offset, filesize,\n                     &retlen, addr);\n}\n\nint romfs_delete(struct dentry *dentry)\n{\n    /* release in-memory inode */\n    /* the root inode is deleted on unmount(), operation is pointed\n     * by i_sb->s_op->unmount() */\n    if (dentry->d_inode != dentry->d_inode->i_sb->s_iroot)\n        free(dentry->d_inode);\n    free(dentry);\n\n    return 0;\n}\n\nconst struct inode_operations romfs_iops = {\n    .lookup = romfs_lookup,\n};\n\nconst struct file_operations romfs_fops = {\n    .open = romfs_open,\n    .read = romfs_read,\n    .mmap = romfs_mmap,\n};\n\nconst struct dentry_operations romfs_dops = {\n    .delete = romfs_delete,\n};\n"
  },
  {
    "path": "fs/tmpfs.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n\n#include <kernel/fs.h>\n#include <kernel/kernel.h>\n\n#include \"linux/list.h\"\n\nstruct dirlist {\n    struct inode *inode;\n    char name[NAME_MAX];\n    struct list_head list;\n};\n\nint tmpfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)\n{\n    dir->i_mode = mode | S_IFDIR;\n    dentry->d_inode = dir;\n\n    struct list_head *dirlist = (struct list_head *) dir->i_private;\n    INIT_LIST_HEAD(dirlist);\n\n    return 0;\n}\n\nint tmpfs_link(__unused struct dentry *old_dentry,\n               struct inode *dir,\n               struct dentry *dentry)\n{\n    struct dirlist *new = malloc(sizeof(struct dirlist));\n    if (!new)\n        return -1;\n    new->inode = dentry->d_inode;\n    strncpy(new->name, dentry->d_name, NAME_MAX);\n\n    struct list_head *dirlist = (struct list_head *) dir->i_private;\n    list_add_tail(&new->list, dirlist);\n    dir->i_size++;\n\n    return 0;\n}\n\nint tmpfs_iterate(struct file *file, struct dir_context *ctx)\n{\n    int res = -1;\n    struct inode *inode = file->f_dentry->d_inode;\n\n    if (file->f_pos == inode->i_size + 2)\n        return -1;\n    switch (file->f_pos) {\n    case 0:\n        res = dir_emit_dot(file, ctx);\n        break;\n    case 1:\n        res = dir_emit_dotdot(file, ctx);\n        break;\n    default:;\n        struct dirlist *dirlist;\n        struct list_head *head = (struct list_head *) inode->i_private;\n        int i = 2;\n        list_for_each_entry(dirlist, head, list)\n        {\n            if (i++ == file->f_pos) {\n                res = dir_emit(ctx, dirlist->name, strlen(dirlist->name),\n                               dirlist->inode->i_ino, 0);\n                break;\n            }\n        }\n    }\n    file->f_pos++;\n\n    return res;\n}\n\nconst struct dentry_operations tmpfs_dops;\n\nstruct dentry *tmpfs_lookup(struct inode *dir, struct dentry *target)\n{\n    struct list_head *head = (struct list_head *) dir->i_private;\n    struct dirlist *dirlist;\n\n    list_for_each_entry(dirlist, head, list)\n    {\n        if (!strcmp(target->d_name, dirlist->name)) {\n            target->d_inode = dirlist->inode;\n            target->d_op = &tmpfs_dops;\n\n            return target;\n        }\n    }\n\n    return NULL;\n}\n\nint tmpfs_delete(struct dentry *dentry)\n{\n    free(dentry);\n\n    return 0;\n}\n\nconst struct inode_operations tmpfs_iops = {\n    .lookup = tmpfs_lookup,\n    .link = tmpfs_link,\n};\n\nconst struct file_operations tmpfs_fops = {\n    .iterate = tmpfs_iterate,\n};\n\nconst struct dentry_operations tmpfs_dops = {\n    .delete = tmpfs_delete,\n};\n\nstatic struct inode tmpfs_inodes[] = {\n    {\n        /* /      - the root directory */\n        .i_ino = 1,\n        .i_op = &tmpfs_iops,\n        .i_fop = &tmpfs_fops,\n        .i_mode = S_IFDIR,\n        .i_size = 2,\n        .i_private = &((struct list_head){}),\n    },\n    {\n        /* /dev   - essential device files */\n        .i_ino = 2,\n        .i_op = &tmpfs_iops,\n        .i_fop = &tmpfs_fops,\n        .i_mode = S_IFDIR,\n        .i_private = &((struct list_head){}),\n    },\n    {\n        /* /proc  - process and kernel information as files */\n        .i_ino = 3,\n        .i_op = &tmpfs_iops,\n        .i_fop = &tmpfs_fops,\n        .i_mode = S_IFDIR,\n        .i_private = &((struct list_head){}),\n    },\n};\n\nstruct inode *root_inode(void)\n{\n    return &tmpfs_inodes[0];\n}\n\nstruct inode *dev_inode(void)\n{\n    return &tmpfs_inodes[1];\n}\n\nstruct inode *proc_inode(void)\n{\n    return &tmpfs_inodes[2];\n}\n\nstruct dentry *root_dentry(void)\n{\n    static struct dentry dentry = {\n        .d_name = \"/\",\n        .d_inode = &tmpfs_inodes[0],\n        .d_parent = &dentry,\n        .d_op = &tmpfs_dops,\n    };\n\n    return &dentry;\n}\n\nstruct inode *init_tmpfs_inode(struct inode *inode)\n{\n    static ino_t ino = 9000;\n\n    inode->i_ino = ino++;\n    inode->i_op = &tmpfs_iops;\n\n    return inode;\n}\n\nvoid tmpfs_init(void)\n{\n    static struct dirlist entries[] = {\n        {\n            .inode = &tmpfs_inodes[1], .name = \"dev\",\n        },\n        {\n            .inode = &tmpfs_inodes[2], .name = \"proc\",\n        },\n    };\n\n    struct list_head *rootdir = (struct list_head *) tmpfs_inodes[0].i_private;\n    INIT_LIST_HEAD(rootdir);\n    list_add_tail(&entries[0].list, rootdir);\n    list_add_tail(&entries[1].list, rootdir);\n\n    struct list_head *devdir = (struct list_head *) tmpfs_inodes[1].i_private;\n    INIT_LIST_HEAD(devdir);\n\n    struct list_head *procdir = (struct list_head *) tmpfs_inodes[2].i_private;\n    INIT_LIST_HEAD(procdir);\n}\n"
  },
  {
    "path": "include/arch/semihosting.h",
    "content": "#ifndef _ARCH_SEMIHOSTING_H\n#define _ARCH_SEMIHOSTING_H\n\nvoid v7m_semihost_exit(int status);\n\n#endif /* !_ARCH_SEMIHOSTING_H */\n"
  },
  {
    "path": "include/arch/v7m-helper.h",
    "content": "#ifndef _ARCH_V7M_HELPER_H\n#define _ARCH_V7M_HELPER_H\n\n#define V7M_EXC_RETURN_HANDLER_MAIN 0xfffffff1\n#define V7M_EXC_RETURN_THREAD_MAIN 0xfffffff9\n#define V7M_EXC_RETURN_THREAD_PROCESS 0xfffffffd\n\nstatic inline void *v7m_set_thumb_bit(void *addr)\n{\n    return (void *) ((unsigned long) addr | 1ul);\n}\n\nstatic inline void *v7m_clear_thumb_bit(void *addr)\n{\n    return (void *) ((unsigned long) addr & ~1ul);\n}\n\n#endif /* !_ARCH_V7M_HELPER_H */\n"
  },
  {
    "path": "include/fs/romfs.h",
    "content": "#ifndef _KERNEL_FS_ROMFS_H\n#define _KERNEL_FS_ROMFS_H\n\n#include <kernel/types.h>\n\n#include <drivers/mtd/mtd.h>\n\n#define ROMFS_FILETYPE_MASK 0x7\n\n#define ROMFS_FILETYPE_HARD 0\n#define ROMFS_FILETYPE_DIR 1\n#define ROMFS_FILETYPE_REG 2\n#define ROMFS_FILETYPE_LNK 3\n#define ROMFS_FILETYPE_BLK 4\n#define ROMFS_FILETYPE_CHR 5\n#define ROMFS_FILETYPE_SOCKET 6\n#define ROMFS_FILETYPE_FIFO 7\n\nstruct romfs_superblock {\n    __u8 magic_number[8];\n    __u32 full_size;\n    __u32 checksum;\n    char volume_name[0];\n};\n\nstruct romfs_inode {\n    __u32 next_filehdr;\n    __u32 spec_info;\n    __u32 size;\n    __u32 checksum;\n    char file_name[0];\n};\n\n#define ROMFS_SUPER_BLOCK(sb)                    \\\n    ({                                           \\\n        struct mtd_info *mtd = (sb)->s_private;  \\\n        struct romfs_superblock *rs = mtd->priv; \\\n        rs;                                      \\\n    })\n\n#define ROMFS_INODE(rs, offset)                               \\\n    ({                                                        \\\n        __u32 addr = (__u32)(rs) + (__u32)(offset);           \\\n        struct romfs_inode *ri = (struct romfs_inode *) addr; \\\n        ri;                                                   \\\n    })\n\nint romfs_mount(const char *source,\n                const char *target,\n                const char *filesystemtype,\n                unsigned long mountflags,\n                const void *data);\n\n#endif /* !_KERNEL_FS_ROMFS_H */\n"
  },
  {
    "path": "include/kernel/bitmap.h",
    "content": "#ifndef _LINUX_BITMAP_H\n#define _LINUX_BITMAP_H\n\n#include \"linux/list.h\"\n#include \"kernel/bitops.h\"\n\nstruct bitmap_struct {\n    unsigned long map;\n    struct list_head queue[32];\n};\n\nstatic inline void INIT_BITMAP(struct bitmap_struct *bm)\n{\n    WRITE_ONCE(bm->map, 0UL);\n    for (int i = 0; i < 32; i++)\n        INIT_LIST_HEAD(&bm->queue[i]);\n}\n\nstatic inline void bitmap_queue_add(struct list_head *new,\n                                    unsigned long bit,\n                                    struct bitmap_struct *bm)\n{\n    list_add_tail(new, &bm->queue[bit]);\n    bitmap_set_bit(&bm->map, bit);\n}\n\nstatic inline void bitmap_queue_add_first(struct list_head *new,\n                                          unsigned long bit,\n                                          struct bitmap_struct *bm)\n{\n    list_add(new, &bm->queue[bit]);\n    bitmap_set_bit(&bm->map, bit);\n}\n\nstatic inline void bitmap_enqueue(struct list_head *new,\n                                  unsigned long bit,\n                                  struct bitmap_struct *bm)\n{\n    bitmap_queue_add(new, bit, bm);\n}\n\nstatic inline void bitmap_enqueue_first(struct list_head *new,\n                                        unsigned long bit,\n                                        struct bitmap_struct *bm)\n{\n    bitmap_queue_add_first(new, bit, bm);\n}\n\nstatic inline int bitmap_empty(const struct bitmap_struct *bm)\n{\n    return !(!READ_ONCE(bm->map));\n}\n\nstatic inline int bitmap_queue_empty(struct bitmap_struct *bm,\n                                     unsigned long bit)\n{\n    return !bitmap_get_bit(&bm->map, bit);\n}\n\nstatic inline int bitmap_first_bit(const struct bitmap_struct *bm)\n{\n    return find_first_bit(&bm->map, 32);\n}\n\nstatic inline void bitmap_queue_del(struct list_head *queue,\n                                    unsigned long bit,\n                                    struct bitmap_struct *bm)\n{\n    list_del(queue);\n    if (list_empty(&bm->queue[bit]))\n        bitmap_clear_bit(&bm->map, bit);\n}\n\nstatic inline struct list_head *bitmap_dequeue(struct bitmap_struct *bm,\n                                               unsigned long bit)\n{\n    struct list_head *first = bm->queue[bit].next;\n\n    bitmap_queue_del(first, bit, bm);\n    return first;\n}\n\nstatic inline struct list_head *bitmap_dequeue_tail(struct bitmap_struct *bm,\n                                                    unsigned long bit)\n{\n    struct list_head *last = bm->queue[bit].prev;\n\n    bitmap_queue_del(last, bit, bm);\n    return last;\n}\n\n#define bitmap_first_entry(bm, bit, type, member) \\\n    list_entry(bm->queue[bit].next, type, member)\n\n#endif\n"
  },
  {
    "path": "include/kernel/bitops.h",
    "content": "#ifndef KERNEL_BITOPS_H\n#define KERNEL_BITOPS_H\n\n#include <sys/param.h>\n\n#include <kernel/bitops.h>\n#include <kernel/kernel.h>\n\n#define BITS_PER_CHAR 8\n#define BITS_PER_LONG (BITS_PER_CHAR * sizeof(long))\n\nstatic inline unsigned long flsl(unsigned long word)\n{\n    return word ? sizeof(long) * BITS_PER_CHAR - __builtin_clz(word) : 0;\n}\n\nstatic inline void clear_bit(unsigned long bit, unsigned long *word)\n{\n    *word &= ~(1 << bit);\n}\n\nstatic inline void set_bit(unsigned long bit, unsigned long *word)\n{\n    *word |= (1 << bit);\n}\n\nstatic inline void bitmap_set_bit(unsigned long *map, unsigned long bit)\n{\n    set_bit(bit % BITS_PER_LONG, &map[bit / BITS_PER_LONG]);\n}\n\nstatic inline void bitmap_clear_bit(unsigned long *map, unsigned long bit)\n{\n    clear_bit(bit % BITS_PER_LONG, &map[bit / BITS_PER_LONG]);\n}\n\nstatic inline unsigned long bitmap_get_bit(unsigned long *map,\n                                           unsigned long bit)\n{\n    return (map[bit / BITS_PER_LONG] >> (bit % BITS_PER_LONG)) & 1;\n}\n\nstatic inline unsigned long find_first_bit(const unsigned long *addr,\n                                           unsigned long size)\n{\n    for (unsigned long i = 0; i * BITS_PER_LONG < size; i++) {\n        if (addr[i])\n            return MIN(i * BITS_PER_LONG + __builtin_ffsl(addr[i]) - 1, size);\n    }\n\n    return size;\n}\n\nstatic inline unsigned long find_first_zero_bit(const unsigned long *addr,\n                                                unsigned long size)\n{\n    for (unsigned long i = 0; i * BITS_PER_LONG < size; i++) {\n        if (addr[i] != ~0ul)\n            return MIN(i * BITS_PER_LONG + __builtin_ffsl(~addr[i]) - 1, size);\n    }\n\n    return size;\n}\n\n#endif /* !KERNEL_BITOPS_H */\n"
  },
  {
    "path": "include/kernel/cbuf.h",
    "content": "#ifndef _KERNEL_CBUF_H\n#define _KERNEL_CBUF_H\n\n#include <sys/types.h>\n\nstruct cbuf_info {\n    size_t len;\n    int pos_begin;\n    int pos_end;\n    void *buf;\n};\n\nstatic inline void cbuf_init(struct cbuf_info *cbuf, void *buf, size_t len)\n{\n    cbuf->len = len;\n    cbuf->pos_begin = 0;\n    cbuf->pos_end = 0;\n    cbuf->buf = buf;\n}\n\nstatic inline int cbuf_getc(struct cbuf_info *cbuf, char *c)\n{\n    if (cbuf->pos_begin == cbuf->pos_end)\n        return 0;\n    *c = *((char *) cbuf->buf + cbuf->pos_begin);\n    cbuf->pos_begin = (cbuf->pos_begin + 1) % cbuf->len;\n\n    return 1;\n}\n\nstatic inline int cbuf_putc(struct cbuf_info *cbuf, char c)\n{\n    *((char *) cbuf->buf + cbuf->pos_end) = c;\n    cbuf->pos_end = (cbuf->pos_end + 1) % cbuf->len;\n\n    return 0;\n}\n\n#endif /* !_KERNEL_CBUF_H */\n"
  },
  {
    "path": "include/kernel/compiler.h",
    "content": "#ifndef _KERNEL_COMPILER_H\n#define _KERNEL_COMPILER_H\n\n/* GCC weak symbol declaration */\n#ifndef __weak\n#define __weak __attribute__((weak))\n#endif\n\n#endif /* !_KERNEL_COMPILER_H */\n"
  },
  {
    "path": "include/kernel/cond.h",
    "content": "#ifndef KERNEL_COND_H\n#define KERNEL_COND_H\n\n#include <sys/types.h>\n\nint sys_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);\nint sys_pthread_cond_signal(pthread_cond_t *cond);\n\n#endif /* !KERNEL_COND_H */\n"
  },
  {
    "path": "include/kernel/errno-base.h",
    "content": "#ifndef _KERNEL_ERRNO_BASE_H_\n#define _KERNEL_ERRNO_BASE_H_\n\n#define EPERM 1    /* Operation not permitted */\n#define ENOENT 2   /* No such file or directory */\n#define ESRCH 3    /* No such process */\n#define EINTR 4    /* Interrupted system call */\n#define EIO 5      /* I/O error */\n#define ENXIO 6    /* No such device or address */\n#define E2BIG 7    /* Argument list too long */\n#define ENOEXEC 8  /* Exec format error */\n#define EBADF 9    /* Bad file number */\n#define ECHILD 10  /* No child processes */\n#define EAGAIN 11  /* Try again */\n#define ENOMEM 12  /* Out of memory */\n#define EACCES 13  /* Permission denied */\n#define EFAULT 14  /* Bad address */\n#define ENOTBLK 15 /* Block device required */\n#define EBUSY 16   /* Device or resource busy */\n#define EEXIST 17  /* File exists */\n#define EXDEV 18   /* Cross-device link */\n#define ENODEV 19  /* No such device */\n#define ENOTDIR 20 /* Not a directory */\n#define EISDIR 21  /* Is a directory */\n#define EINVAL 22  /* Invalid argument */\n#define ENFILE 23  /* File table overflow */\n#define EMFILE 24  /* Too many open files */\n#define ENOTTY 25  /* Not a typewriter */\n#define ETXTBSY 26 /* Text file busy */\n#define EFBIG 27   /* File too large */\n#define ENOSPC 28  /* No space left on device */\n#define ESPIPE 29  /* Illegal seek */\n#define EROFS 30   /* Read-only file system */\n#define EMLINK 31  /* Too many links */\n#define EPIPE 32   /* Broken pipe */\n#define EDOM 33    /* Math argument out of domain of func */\n#define ERANGE 34  /* Math result not representable */\n\n#endif /* !_KERNEL_ERRNO_BASE_H_ */\n"
  },
  {
    "path": "include/kernel/faults.h",
    "content": "#ifndef KERNEL_FAULTS_H\n#define KERNEL_FAULTS_H\n\n#include <sys/types.h>\n#include <kernel/thread.h>\n\nvoid fault_enter(const char *s);\nvoid fault_exit(void);\n\n/* arch-dependent */\nvoid dump_frame(struct kernel_context_regs *noscratch,\n                struct thread_context_regs *scratch,\n                u32 exc_return);\n\n#endif /* !KERNEL_FAULTS_H */\n"
  },
  {
    "path": "include/kernel/fs.h",
    "content": "#ifndef _KERNEL_FS_H\n#define _KERNEL_FS_H\n\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <piko/dirent.h>\n\n#include <kernel/types.h>\n\n#define NAME_MAX 32  // FIXME: Include <limits.h>\n#define FILE_MAX 8\n\n#define O_DIRECTORY 1\n\n/*\n * super_block struct\n */\n\nstruct dentry;\nstruct inode;  // FIXME: delete me from here, using s_root dentry\n\nstruct super_block {\n    void *s_private;        // for dev, pointing to MTD\n    struct inode *s_iroot;  // FIXME: just use s_root and dentry\n};\n\n/*\n * inode struct\n */\n\nstruct inode_operations;\nstruct file_operations;\n\nstruct inode {\n    struct list_head i_list; /* list of inodes */\n    umode_t i_mode;          /* access permissions */\n    // kernel_ino_t\n    unsigned long i_ino;                 /* inode number */\n    atomic_t i_count;                    /* reference counter */\n    off_t i_size;                        /* file size in bytes */\n    const struct inode_operations *i_op; /* inode ops table */\n    const struct file_operations *i_fop; /* default inode ops */\n    struct super_block *i_sb;            /* associated superblock */\n    void *i_private;\n    char i_data[0];\n};\n\nstruct inode_operations {\n    struct dentry *(*lookup)(struct inode *inode, struct dentry *dentry);\n    int (*link)(struct dentry *old_dentry,\n                struct inode *dir,\n                struct dentry *dentry);\n};\n\n/*\n * file struct\n */\n\nstruct file {\n    struct dentry *f_dentry;            /* associated dentry object */\n    const struct file_operations *f_op; /* file operations table */\n    off_t f_pos;                        /* file offset (file pointer) */\n    void *f_private;\n};\n\nstruct dir_context;\n\nstruct file_operations {\n    off_t (*lseek)(struct file *file, off_t offset, int origin);\n    ssize_t (*read)(struct file *file, char *buf, size_t count, off_t offset);\n    ssize_t (*write)(struct file *file,\n                     const char *buf,\n                     size_t count,\n                     off_t *offset);\n    int (*iterate)(struct file *file, struct dir_context *ctx);\n    int (*mmap)(struct file *file,\n                off_t offset,\n                void **addr); /* struct vm_area_struct *area */\n    int (*open)(struct inode *inode, struct file *file);\n};\n\n/*\n * dentry struct\n*/\n\nstruct dentry {\n    _Atomic int d_count;                  /* usage count */\n    struct inode *d_inode;                /* associated inode */\n    const struct dentry_operations *d_op; /* dentry operations table */\n    struct dentry *d_parent;              /* dentry object of parent */\n    char d_name[NAME_MAX];                /* short name */\n\n    // struct list_head d_child;       /* child of parent list */\n    // struct list_head d_subdirs;     /* our children */\n};\n\nstruct dentry_operations {\n    int (*delete)(struct dentry *dentry);\n    void (*release)(struct dentry *dentry);\n};\n\n/* readdir */\n\ntypedef int (*filldir_t)(struct dir_context *,\n                         const char *,\n                         int,\n                         off_t,\n                         unsigned int,\n                         unsigned int);\n\nstruct dir_context {\n    const filldir_t actor;\n    off_t pos;  // XXX: unused because piko_dirent has no offset field\n};\n\nstruct piko_dirent;\n\nstruct readdir_callback {\n    struct dir_context ctx;\n    struct piko_dirent *dirent;\n    int result;\n};\n\nstruct piko_dirent {\n    ino_t d_ino;           /* inode number */\n    char d_name[NAME_MAX]; /* filename */\n};\n\nstatic inline int dir_emit(struct dir_context *ctx,\n                           const char *name,\n                           int namelen,\n                           unsigned int ino,\n                           unsigned int type)\n{\n    return ctx->actor(ctx, name, namelen, ctx->pos, ino, type);\n}\n\nstatic inline int dir_emit_dot(struct file *file, struct dir_context *ctx)\n{\n    return ctx->actor(ctx, \".\", 1, ctx->pos, file->f_dentry->d_inode->i_ino, 0);\n}\n\nstatic inline int dir_emit_dotdot(struct file *file, struct dir_context *ctx)\n{\n    return ctx->actor(ctx, \"..\", 2, ctx->pos,\n                      file->f_dentry->d_parent->d_inode->i_ino, 0);\n}\n\n/* forward declarations */\n\nint vfs_iterate(struct file *file, struct dir_context *ctx);\nstruct dentry *vfs_lookup(struct inode *dir, struct dentry *target);\nint vfs_link(struct dentry *old_dentry,\n             struct inode *dir,\n             struct dentry *dentry);\nint vfs_delete(struct dentry *dentry);\nvoid vfs_release(struct dentry *dentry);\nint vfs_mmap(struct file *file, off_t offset, void **addr);\n\n/* syscall entries */\n\ntypedef void DIR;\n\nint sys_opendir(const char *name);\nint sys_readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);\nint sys_open(const char *pathname, int flags);\nssize_t sys_read(int fd, void *buf, size_t count);\nssize_t sys_write(int fd, void *buf, size_t count);\noff_t sys_seek(int fd, off_t offset, int whence);\nint sys_close(int fd);\n\n/* misc functions */\n\nstruct inode *root_inode(void);\nstruct inode *dev_inode(void);\nstruct inode *proc_inode(void);\nstruct dentry *root_dentry(void);\nstruct file *fd_to_file(int fd);\nstruct inode *init_tmpfs_inode(struct inode *inode);\nstruct inode *inode_from_pathname(const char *pathname);\n\nvoid tmpfs_init(void);\nvoid proc_init(void);\n\n#endif /* !_KERNEL_FS_H */\n"
  },
  {
    "path": "include/kernel/hash.h",
    "content": "#ifndef _KERNEL_HASH_H\n#define _KERNEL_HASH_H\n\n#include <stddef.h>\n\nstatic inline unsigned long hash_djb2(unsigned char *str, size_t len)\n{\n    unsigned long hash = 5381;\n\n    for (int i = 0; i < (int) len; i++) {\n        int c = str[i];\n        hash = ((hash << 5) + hash) + c; /* hash * 33 + c */\n    }\n\n    return hash;\n}\n\n#endif /* _KERNEL_HASH_H */\n"
  },
  {
    "path": "include/kernel/irq.h",
    "content": "#ifndef KERNEL_IRQ_H\n#define KERNEL_IRQ_H\n\n#define NR_IRQS 64\n\n/* irq status */\nenum {\n    IRQ_NOREQUEST = 1,\n};\n\n/* irq data state(for per-chip) */\nenum {\n    IRQD_IRQ_DISABLED = 1,\n    IRQD_INACTIVATED = 1 << 2,\n    IRQD_ACTIVATED = 1 << 3,\n    IRQD_PENDING = 1 << 4,\n};\n\n/**\n * struct irq_data - per chip data\n *\n * @state:  status information for irq chip\n * XXX: Thanks for CMSIS. We don't have to impl per chip function and data,\n *          but this concept should be remained due to more precious description\n *          of irqs and chips(HW).\n */\nstruct irq_data {\n    unsigned int state;\n};\n\nstruct irqaction;\n\n/**\n * struct irq_desc - interrupt descriptor\n *\n * @action:\t\tthe irq action chain\n * @status:\t\tstatus information\n * @irq_state:\tper irq state information\n */\nstruct irq_desc {\n    struct irq_data irq_data;\n    struct irqaction *action;\n    unsigned int status;\n};\n\ntypedef void (*irq_handler_t)(void);\n/**\n * struct irqaction - per interrupt action descriptor\n * @handler:\tinterrupt handler function\n * @irq:\tinterrupt number\n */\nstruct irqaction {\n    irq_handler_t handler;\n    unsigned int irq;\n};\n\nint request_irq(unsigned int irq, irq_handler_t hdlr);\nint free_irq(unsigned int irq);\n\n#endif /* !KERNEL_IRQ_H */\n"
  },
  {
    "path": "include/kernel/kernel.h",
    "content": "#ifndef _KERNEL_KERNEL_H\n#define _KERNEL_KERNEL_H\n\n/* round-down to a power of 2 */\n#define align(x, a) align_mask(x, (__typeof__(x))((a) -1))\n#define align_mask(x, mask) ((x) & ~(mask))\n\n/* round-up to a power of 2 */\n#define align_next(x, a) align_next_mask(x, (__typeof__(x))((a) -1))\n#define align_next_mask(x, mask) (((x) + (mask)) & ~(mask))\n\n#define ARRAY_SIZE(arr) ((size_t)(sizeof(arr) / sizeof(*(arr))))\n\n#define ARRAY_INDEX(elt, arr)                     \\\n    ({                                            \\\n        unsigned int _elt = (unsigned int) (elt); \\\n        unsigned int _arr = (unsigned int) (arr); \\\n        (_elt - _arr) / sizeof(*(elt));           \\\n    })\n\n#define container_of(ptr, type, member)                         \\\n    ({                                                          \\\n        const __typeof__(((type *) 0)->member) *__mptr = (ptr); \\\n        (type *) ((char *) __mptr - offsetof(type, member));    \\\n    })\n\n#define SWAP(x, y)             \\\n    {                          \\\n        __typeof__(x) tmp = x; \\\n        x = y;                 \\\n        y = tmp;               \\\n    }                          \\\n    while (0)\n\nint printk(const char *fmt, ...);\n\n#endif /* !_KERNEL_KERNEL_H */\n"
  },
  {
    "path": "include/kernel/linkage.h",
    "content": "#ifndef _KERNEL_LINKAGE_H\n#define _KERNEL_LINKAGE_H\n\n#ifdef __ASSEMBLER__\n\n#define ENTRY(name) \\\n    .globl name;    \\\n    .align 4;       \\\n    name:\n\n#define END(name) .size name, .- name\n\n#define ENDPROC(name)       \\\n    .type name, % function; \\\n    END(name)\n\n#endif /* __ASSEMBLER__ */\n\n#endif /* !_KERNEL_LINKAGE_H */\n"
  },
  {
    "path": "include/kernel/log2.h",
    "content": "#ifndef KERNEL_LOG2_H\n#define KERNEL_LOG2_H\n\n#include <kernel/bitops.h>\n\n// clang-format off\n\n#define ilog2(x)                        \\\n(                                       \\\n    __builtin_constant_p(x) ? (         \\\n        (x) < 1 ? __ilog2_NaN() :       \\\n        (x) & (1ul << 31) ? 31 :        \\\n        (x) & (1ul << 30) ? 30 :        \\\n        (x) & (1ul << 29) ? 29 :        \\\n        (x) & (1ul << 28) ? 28 :        \\\n        (x) & (1ul << 27) ? 27 :        \\\n        (x) & (1ul << 26) ? 26 :        \\\n        (x) & (1ul << 25) ? 25 :        \\\n        (x) & (1ul << 24) ? 24 :        \\\n        (x) & (1ul << 23) ? 23 :        \\\n        (x) & (1ul << 22) ? 22 :        \\\n        (x) & (1ul << 21) ? 21 :        \\\n        (x) & (1ul << 20) ? 20 :        \\\n        (x) & (1ul << 19) ? 19 :        \\\n        (x) & (1ul << 18) ? 18 :        \\\n        (x) & (1ul << 17) ? 17 :        \\\n        (x) & (1ul << 16) ? 16 :        \\\n        (x) & (1ul << 15) ? 15 :        \\\n        (x) & (1ul << 14) ? 14 :        \\\n        (x) & (1ul << 13) ? 13 :        \\\n        (x) & (1ul << 12) ? 12 :        \\\n        (x) & (1ul << 11) ? 11 :        \\\n        (x) & (1ul << 10) ? 10 :        \\\n        (x) & (1ul <<  9) ?  9 :        \\\n        (x) & (1ul <<  8) ?  8 :        \\\n        (x) & (1ul <<  7) ?  7 :        \\\n        (x) & (1ul <<  6) ?  6 :        \\\n        (x) & (1ul <<  5) ?  5 :        \\\n        (x) & (1ul <<  4) ?  4 :        \\\n        (x) & (1ul <<  3) ?  3 :        \\\n        (x) & (1ul <<  2) ?  2 :        \\\n        (x) & (1ul <<  1) ?  1 :        \\\n        (x) & (1ul <<  0) ?  0 :        \\\n        __ilog2_NaN()                   \\\n        ) :                             \\\n        __ilog2(x)                      \\\n)\n\n// clang-format on\n\nstatic inline unsigned long __ilog2(unsigned long x)\n{\n    return flsl(x) - 1;\n}\n\nstatic inline __attribute__((noreturn)) unsigned long __ilog2_NaN(void)\n{\n    for (;;)\n        ;\n}\n\n#endif /* !KERNEL_LOG2_H */\n"
  },
  {
    "path": "include/kernel/mm/page.h",
    "content": "#ifndef _KERNEL_MM_PAGE_H\n#define _KERNEL_MM_PAGE_H\n\n#define MAX_PAGE_ORDER 3  /* max page size is 2 KB */\n#define MIN_PAGE_SIZE 256 /* min page size is 256 bytes */\n\nvoid *alloc_pages(unsigned long order);\nvoid free_pages(unsigned long addr, unsigned long order);\nvoid show_page_bitmap(void);\nlong size_to_page_order(unsigned long size);\nunsigned long page_alloc_signature(void);\n\n#endif /* !_KERNEL_MM_PAGE_H */\n"
  },
  {
    "path": "include/kernel/mm/slab.h",
    "content": "#ifndef _KERNEL_MM_SLAB_H\n#define _KERNEL_MM_SLAB_H\n\n#include <sys/cdefs.h>\n\n#include \"linux/list.h\"\n\n#define CACHE_PAGE_SIZE 256\n#define CACHE_NAMELEN 16\n\n#define CACHE_OPT_NONE 0\n/* OPT_FORCE: create a new cache, even when the object size matches a generic\n *            cache (i.e. 8B, 16B, 32B...)\n * OPT_PERSIST: this cache cannot be destroyed (i.e. when a cache w/o OPT_FORCE\n *              has been created and silently merged into a general object\n *              cache and the user try to destroy the cache he created)\n */\n\nstruct kmem_cache {\n    struct list_head slabs_free;\n    struct list_head slabs_partial;\n    struct list_head slabs_full;\n    struct list_head list;  /* linked list of caches */\n    unsigned short objsize; /* size of one object within a slab */\n    unsigned short objnum;  /* number of objects per slab */\n    int opts;\n    int alloc_succeed;\n    int alloc_fail;\n    char name[CACHE_NAMELEN];\n};\n\nstruct slab {\n    unsigned long free_bitmap[1];\n    int free_objects;      /* number of free objects in that slab */\n    struct list_head list; /* pointer to prev/next slabs for that cache */\n    char data[0];\n    /* struct kmem_cache *backlink; // backlink to the cache structure */\n};\n\n#define KMEM_CACHE(objtype, name) \\\n    kmem_cache_create(name, sizeof(objtype), 0, CACHE_OPT_NONE, NULL)\n\nstruct kmem_cache *kmem_cache_create(const char *name,\n                                     size_t size,\n                                     __unused size_t align,\n                                     __unused unsigned long flags,\n                                     __unused void (*ctor)(void *));\nvoid *kmem_cache_alloc(struct kmem_cache *cache, __unused unsigned long flags);\nvoid kmem_cache_free(struct kmem_cache *cache, void *obj);\nvoid kmem_cache_init(void);\n\n#endif /* _KERNEL_MM_SLAB_H */\n"
  },
  {
    "path": "include/kernel/mutex.h",
    "content": "#ifndef _KERNEL_MUTEX_H\n#define _KERNEL_MUTEX_H\n\ntypedef _Atomic int atomic_s32;\n\ntypedef struct {\n    atomic_s32 val;\n} kernel_mutex_t;\n\nint sys_pthread_mutex_lock(kernel_mutex_t *mutex);\nint sys_pthread_mutex_unlock(kernel_mutex_t *mutex);\n\n#endif /* !_KERNEL_MUTEX_H */\n"
  },
  {
    "path": "include/kernel/sched.h",
    "content": "#ifndef KERNEL_SCHEDULER_H\n#define KERNEL_SCHEDULER_H\n\n#include_next <sched.h>\n\nstruct thread_info;\n\n/* 0 <= PRI_MAX <= PRI_MIN */\n#define PRI_MAX 0\n#define PRI_MIN 31\n\n#define SCHED_CLASS_RR 0\n#define SCHED_CLASS_BITMAP 1\ntypedef int sched_class_t;\n\n#define SCHED_OPT_NONE 0\n#define SCHED_OPT_RESTORE_ONLY 1\n#define SCHED_OPT_RESET 2\n#define SCHED_OPT_TICK 3\n\n/* scheduler implementation hooks */\n#define HOOK_SCHED_CLASS(name, sched_struct)                            \\\n    static struct sched *sched_class_##name                             \\\n        __attribute__((section(\".sched.class\"), aligned(sizeof(long)),  \\\n                        used)) = sched_struct;\n\nstruct sched {\n    sched_class_t class_type;\n    int (*init)(void);\n    int (*enqueue)(struct thread_info *thread);\n    int (*dequeue)(struct thread_info *thread);\n    int (*elect)(int switch_type);\n};\n\nint sched_init();\nint sched_select(sched_class_t sched_type);\nint sched_enqueue(struct thread_info *thread);\nint sched_dequeue(struct thread_info *thread);\nint sched_elect(int flags);\n\n#endif /* !KERNEL_SCHEDULER_H */\n"
  },
  {
    "path": "include/kernel/serial.h",
    "content": "/*\n * Interface for serial devices (UART, ...)\n */\n\n#ifndef _KERNEL_SERIAL_H\n#define _KERNEL_SERIAL_H\n\n#include <stdio.h>\n#include <sys/types.h>\n\nstruct serial_ops;\nstruct device;\nstruct thread_info;\n\nstruct serial_info {\n    void *priv;\n    unsigned int rx_count;\n\n    struct serial_ops *ops;\n\n    // XXX: owner thread pointer could go to priv, and device pripheral\n    // base address should be linked to private data for the device\n    struct thread_info *owner;\n};\n\nstruct serial_ops {\n    int (*serial_init)(struct serial_info *serial);\n\n    int (*serial_getc)(struct serial_info *serial, char *c);\n    int (*serial_gets)(struct serial_info *serial,\n                       size_t len,\n                       size_t *retlen,\n                       char *buf);\n    int (*serial_putc)(struct serial_info *serial, char c);\n    int (*serial_puts)(struct serial_info *serial,\n                       size_t len,\n                       const char *buf);\n\n    /* callback on device activity, set by ioctl() */\n    void (*callback)(struct serial_info *self);\n};\n\n\nstruct serial_hook {\n    char name[10];\n    int (*init)(void);\n};\n\n/* serial hooks */\n#define HOOK_SERIAL_INIT(dev, init_func)                                \\\n    static struct serial_hook serial_##dev##_hook                              \\\n        __attribute__((section(\".serial.hook\"), aligned(sizeof(long)),  \\\n                        used)) = {#dev, init_func};\n\n/* Generic uart setup */\nvoid uart_init(void);\n/* Generic serial init */\nvoid serial_init(void);\n\n/* XXX: All func below is callback function */\nint serial_getc(struct serial_info *serial, char *c);\nint serial_gets(struct serial_info *serial,\n                size_t len,\n                size_t *retlen,\n                char *buf);\nint serial_putc(struct serial_info *serial, char c);\nint serial_puts(struct serial_info *serial,\n                size_t len,\n                const char *buf);\n\nvoid serial_activity_callback(struct serial_info *serial);\n\n#endif /* !_KERNEL_SERIAL_H */\n"
  },
  {
    "path": "include/kernel/signal.h",
    "content": "#ifndef _KERNEL_SIGNAL_H\n#define _KERNEL_SIGNAL_H\n\n#include <signal.h>\n\n#include <piko/signal.h>\n\n#include <kernel/thread.h>\n\n#include \"linux/types.h\"\n\n#define SIGMAX 31\n\nstruct signal_info {\n    int signo;\n    struct list_head list;\n    struct sigaction act_storage;\n};\n\nvoid do_sigevent(const struct sigevent *sigevent, struct thread_info *thread);\n\n#endif /* !_KERNEL_SIGNAL_H */\n"
  },
  {
    "path": "include/kernel/softirq.h",
    "content": "#ifndef KERNEL_SOFTIRQ_H\n#define KERNEL_SOFTIRQ_H\n\n#include \"linux/list.h\"\n\n#define PRIO_TASKLET_VEC 32\n#define PRIO_TASKLET_MAXPRIO 0\n#define PRIO_TASKLET_MINPRIO 31\n\n/* softirq */\nenum {\n    TIMER_SOFTIRQ = 0,\n};\n\n/* softirq default priority */\nenum {\n    TIMER_SOFTIRQ_PRIO = 0,\n};\n\n/* softirq entry */\nstruct softirq_action {\n    int (*action)(struct softirq_action *);\n};\n\n/* FIXME: re-entrance */\n/* dynamic task */\nstruct tasklet_struct {\n    struct list_head tsk_q;\n    unsigned long prio;\n    // unsigned long state;\n    void (*func)(void *);\n    void *data;\n};\n\nenum {\n    TASKLET_STATE_SCHED,  /* Tasklet is scheduled for execution */\n    TASKLET_STATE_RUN,    /* Tasklet is running (SMP only) */\n    TASKLET_STATE_PENDING /* Tasklet is pending */\n};\n\n#define TASKLET_STATEF_SCHED (1 << TASKLET_STATE_SCHED)\n#define TASKLET_STATEF_RUN (1 << TASKLET_STATE_RUN)\n#define TASKLET_STATEF_PENDING (1 << TASKLET_STATE_PENDING)\n\nint open_softirq(unsigned int nr, int (*action)(struct softirq_action *));\nint raise_softirq(unsigned int nr);\n\nint tasklet_schedule(struct tasklet_struct *task);\nstruct tasklet_struct *tasklet_init(void(*func),\n                                    void *data,\n                                    unsigned long prio);\nint init_softirq(void);\n\n#endif /* KERNEL_SOFTIRQ_H */\n"
  },
  {
    "path": "include/kernel/task.h",
    "content": "#ifndef _KERNEL_TASK_H\n#define _KERNEL_TASK_H\n\n#include <sys/types.h>\n\n#include <kernel/fs.h>\n\n#include \"linux/list.h\"\n\n#define PID_BASE 7000\n#define PID_MAX 32768\n\nstruct task_info {\n    pid_t pid;\n    unsigned long filemap;\n    struct file filetable[FILE_MAX];\n    struct list_head list;\n    struct list_head thread_head;\n    struct list_head signal_head; /* list of installed handlers */\n};\n\nstruct task_info *task_init(struct task_info *task);\nstruct task_info *task_create(void *(*start_routine)(void *), void *arg);\nvoid task_exit(struct task_info *task);\nstruct task_info *current_task_info(void);\n\n#define CURRENT_TASK_INFO(var) struct task_info *var = current_task_info();\n\n#endif /* !_KERNEL_TASK_H */\n"
  },
  {
    "path": "include/kernel/thread.h",
    "content": "#ifndef _KERNEL_THREAD_H\n#define _KERNEL_THREAD_H\n\n#include <sys/types.h>\n\n#include <kernel/kernel.h>\n\n#include \"linux/types.h\"\n#include \"linux/list.h\"\n\n#define INTR_STACK_ORDER 9 /* 512 Bytes */\n#define INTR_STACK_SIZE (1 << INTR_STACK_ORDER)\n\n/* machine-specific thread info on ARM */\nstruct mthread_info {\n    u32 mi_msp;  /* +0 */\n    u32 mi_psp;  /* +4 */\n    u32 mi_priv; /* +8 */\n} __attribute__((packed));\n\n\nstruct task_info;\n\nstruct thread_info {\n    /* machine-specific thread info */\n    struct mthread_info ti_mach;\n\n    /* thread description data */\n    int ti_priority;\n    int ti_id;\n    int ti_state;\n    size_t ti_stacksize; /* user thread stacksize */\n    struct task_info *ti_task;\n\n    struct list_head ti_list; /* global list of threads */\n    struct list_head ti_q; /* shared by sched runq, mutex waitq, thread joinq */\n\n    /* http://www.domaigne.com/blog/computing/joinable-and-detached-threads/ */\n    void *ti_retval;\n    int ti_detached;\n    int ti_joinable;\n    struct thread_info *ti_joining;\n\n    /* Pointer to mutually exclusive data: the mutex the thread is blocking\n     * on, the exit value when thread is not yet joined, etc. */\n    void *ti_private;\n\n/* /\\* local-storage *\\/ */\n/* struct list_head *ti_lsq; // local-storage queue */\n\n#ifdef CONFIG_KERNEL_STACK_CHECKING\n    u32 ti_canary[2];\n#endif\n\n    char ti_storage[0];\n};\n\nenum thread_privilege { THREAD_PRIV_SUPERVISOR = 0, THREAD_PRIV_USER = 1 };\n\nenum thread_state {\n    /* Thread structure allocated but not enqueued in the system scheduler. */\n    THREAD_STATE_NEW,\n\n    /**\n     * Ready to run in the system scheduler.\n     *\n     * XXX: why we need two ready states?\n     *\n     *  Ans: One is ready for execution in active queue,\n     *          the other one is the state for out of\n     *          timeslice. In order to achieve O(1), we\n     *          adopt specified function to determine\n     *          which one is actived or expired.\n     *\n     * XXX: we impl map from `actived` and `expired` to\n     *          `ready1` and `ready2`\n     */\n    THREAD_STATE_READY1,  // THREAD_STATE_ACTIVED\n    THREAD_STATE_READY2,  // THREAD_STATE_EXPIRED\n\n    /* Running by the system scheduler. */\n    THREAD_STATE_RUNNING,\n\n    /* The thread has normally exited or has called Pthread_exit to exit. Its\n     * resources have not been freed and will be freed if it is detached or\n     * joined.    */\n    THREAD_STATE_TERMINATED,\n\n    /* Waiting for a mutex or resource. */\n    THREAD_STATE_BLOCKED\n};\n#define THREAD_STATE_ACTIVED THREAD_SCHED_STATE[ACTIVED]\n#define THREAD_STATE_EXPIRED THREAD_SCHED_STATE[EXPIRED]\n\nenum {\n    ACTIVED,\n    EXPIRED,\n    NR_THREAD_SCHED_STATE,\n};\n\nstatic int THREAD_SCHED_STATE[NR_THREAD_SCHED_STATE] = {\n        [ACTIVED] = THREAD_STATE_READY1, [EXPIRED] = THREAD_STATE_READY2,\n};\n\nstatic inline void swap_sched_state_map(void)\n{\n    SWAP(THREAD_SCHED_STATE[ACTIVED], THREAD_SCHED_STATE[EXPIRED]);\n}\n\n/*\n * This stackframe is built to handle the first scheduling of a task on\n * the CPU. Running a task for the first time is achieved in two stages.\n *\n * Stage 1:\n *   After switch_to() has switched the interrupt stacks for current and\n *   next tasks the function will restore the kernel-context for the next\n *   thread. That context is the non-scratch registers r4 to r11. At this\n *   point we don't care about the values in these registers: they are\n *   not used after that point during in that interrupt, and the next\n *   interrupt context will reinitilized them should it need to.\n *\n * Stage 2:\n *   After restoring the non-scratch registers we return from switch_to()\n *   and the value loaded in LR in stage1 is the address of task_kickstart().\n *   This function is a trampoline that emulate a return 'from interrupt\n *   sequence' by restoring the non-scratch registers for the tasks as\n *   well as triggering a switch from Handler_Mode to Thread_Mode in the\n *   CPU. We initialize the task non-scratch registers to 0.\n */\n\nstruct kernel_context_regs {\n    u32 r4_r12[9]; /* r4 to r12, zero-filled */\n    u32 lr;        /* initially loaded with EXC_RETURN value */\n};\n\nstruct thread_context_regs {\n    u32 r0_r3__r12[5]; /* r0 to r3, r12; args or zero-filled */\n    u32 lr;            /* initially loaded with pthread_exit() */\n    u32 ret_addr;      /* thread entry-point function */\n    u32 xpsr;          /* forced to Thumb_Mode */\n};\n\n/* forward declarations */\n\nvoid switch_to(struct thread_info *, struct thread_info *);\nvoid thread_restore(\n    struct thread_info *);  // FIXME: rename to switch_to_restore_only ? meh..\n\nstruct thread_info *thread_create(void *(*) (void *),\n                                  void *,\n                                  enum thread_privilege,\n                                  size_t,\n                                  struct task_info *);\nint thread_yield(void);\nint thread_self(void);\nvoid thread_exit(void *);\nint thread_set_priority(struct thread_info *thread, int priority);\nint thread_detach(pthread_t thread);\n\nstatic inline struct thread_info *current_thread_info(void)\n{\n    struct thread_info *this;\n\n    __asm__ __volatile__(\n        \"mov %0, sp \\n\\t\"\n        \"bfc %0, #0, %1\"\n        : \"=r\"(this)\n        : \"M\"(INTR_STACK_ORDER));\n\n    return this;\n}\n\n#define CURRENT_THREAD_INFO(var) \\\n    struct thread_info *var = current_thread_info();\n\n#define THREAD_INFO(addr) \\\n    ({ (struct thread_info *) align((unsigned long) addr, INTR_STACK_SIZE); })\n\n#define THREAD_CANARY0 0xee48a608\n#define THREAD_CANARY1 0x840dc3bc\n\n#ifdef CONFIG_KERNEL_STACK_CHECKING\n#define KERNEL_STACK_CHECKING                                     \\\n    ({                                                            \\\n        __auto_type cur_thread = current_thread_info();           \\\n        if ((cur_thread->ti_canary[0] != THREAD_CANARY0) ||       \\\n            (cur_thread->ti_canary[1] != THREAD_CANARY1)) {       \\\n            printk(\"\\nkernel panic: Overflow in kernel stack\\n\"); \\\n            printk(\"  0  %08x  %08x\\n\", THREAD_CANARY0,           \\\n                   cur_thread->ti_canary[0]);                     \\\n            printk(\"  1  %08x  %08x\\n\", THREAD_CANARY1,           \\\n                   cur_thread->ti_canary[1]);                     \\\n            for (;;)                                              \\\n                ;                                                 \\\n        }                                                         \\\n    })\n#else\n#define KERNEL_STACK_CHECKING\n#endif\n\n#endif /* !_KERNEL_THREAD_H */\n"
  },
  {
    "path": "include/kernel/time.h",
    "content": "#ifndef _KERNEL_TIME_H\n#define _KERNEL_TIME_H\n\n#include <signal.h>\n#include <sys/types.h>\n#include <sys/timespec.h>  //FIXME: Why including this header explicitly?\n#include_next <time.h>\n\n#include <piko/signal.h>\n\n#include <kernel/types.h>\n\n#include \"linux/list.h\"\n\nstruct timer_info;\n\nenum timer_type { ONESHOT_TIMER, INTERVAL_TIMER };\n\nstruct timer_operations {\n    int (*timer_alloc)(struct timer_info *timer /* , int flags */);\n    int (*timer_free)(struct timer_info *timer);\n    int (*timer_set)(struct timer_info *timer, const struct timespec *value);\n    int (*timer_get)(struct timer_info *timer, struct itimerspec *value);\n};\n\nstruct timer_info {\n    timer_t id;\n    u32 flags;\n    int disarmed;\n    enum timer_type type;\n    void (*callback)(struct timer_info *self);\n    struct thread_info *owner;\n    struct itimerspec value;\n    struct sigevent sigev;\n    struct list_head list;\n    const struct timer_operations *tops;\n\n    void *dev;\n};\n\nstruct timer_info *timer_alloc(void);\nint timer_free(struct timer_info *timer);\nint timer_set(struct timer_info *timer, const struct timespec *value);\nint timer_get(struct timer_info *timer, struct itimerspec *value);\n\nvoid timer_expire_callback(struct timer_info *timer);\n\n#endif /* !_KERNEL_TIME_H */\n"
  },
  {
    "path": "include/kernel/types.h",
    "content": "#ifndef _KERNEL_TYPES_H\n#define _KERNEL_TYPES_H\n\n#ifndef _U32\n#define _U32\ntypedef unsigned int u32;\n#endif\n\n#ifndef _S32\n#define _S32\ntypedef int s32;\n#endif\n\n#ifndef _U16\n#define _U16\ntypedef unsigned short u16;\n#endif\n\n#ifndef _S16\n#define _S16\ntypedef short s16;\n#endif\n\n#ifndef _U8\n#define _U8\ntypedef unsigned char u8;\n#endif\n\n#ifndef _S8\n#define _S8\ntypedef char s8;\n#endif\n\n#ifndef _UMODE_T\n#define _UMODE_T\ntypedef unsigned short umode_t;\n#endif\n\ntypedef u32 __u32;\ntypedef s32 __s32;\ntypedef u16 __u16;\ntypedef s16 __s16;\ntypedef u8 __u8;\ntypedef s8 __s8;\n\n/* import struct list_head */\n#include \"linux/types.h\"\n\n#endif /* !_KERNEL_TYPES_H */\n"
  },
  {
    "path": "include/libc/pthread.h",
    "content": "#ifndef PTHREAD_H\n#define PTHREAD_H\n\n#include_next <pthread.h>\n\nextern int pthread_yield(void);\n\n#endif /* PTHREAD_H */\n"
  },
  {
    "path": "include/libc/ucontext.h",
    "content": "#ifndef UCONTEXT_H\n#define UCONTEXT_H\n\n#include <signal.h>\n\n#include \"linux/types.h\"\n\n/* machine context on ARM */\ntypedef struct mcontext {\n    u32 sp;  // FIXME: reuse uc_stack.ss_sp\n    u32 lr;\n    u32 gprs[13]; /* r0-r12 */\n    u32 pc;\n} __attribute__((packed)) mcontext_t;\n\ntypedef struct ucontext {\n    struct ucontext *uc_link;\n    /* sigset_t uc_sigmask; */\n    stack_t uc_stack;\n    mcontext_t uc_mcontext;\n    /* ... */\n} ucontext_t;\n\n/* forward declarations */\n\nvoid makecontext(ucontext_t *ucp, void (*func)(), int argc, ...);\nint swapcontext(ucontext_t *oucp, ucontext_t *ucp);\n\n#endif /* !UCONTEXT_H */\n"
  },
  {
    "path": "include/libc/utils.h",
    "content": "#ifndef UTILS_H\n#define UTILS_H\n\n#ifndef __LINKER__\n\nstatic inline void infinite_loop(void)\n{\n    for (;;)\n        ;\n}\n\nvoid strpad(char *buf, char pad_val, int count);\nchar *itoa_base(int value, char *buf, int base);\n\n#endif /* !__LINKER__ */\n\n#endif /* !UTILS_H */\n"
  },
  {
    "path": "include/linux/compiler.h",
    "content": "#ifndef _LINUX_COMPILER_H\n#define _LINUX_COMPILER_H\n\n#define READ_ONCE(x) (x)\n#define WRITE_ONCE(x, val) \\\n    ({                     \\\n        x = val;           \\\n        val;               \\\n    })\n\n#endif /* !_LINUX_COMPILER_H */\n"
  },
  {
    "path": "include/linux/list.h",
    "content": "#ifndef _LINUX_LIST_H\n#define _LINUX_LIST_H\n\n#include <stdbool.h>\n\n#include <kernel/kernel.h>\n\n#include \"linux/types.h\"\n#include \"linux/stddef.h\"\n#include \"linux/poison.h\"\n#include \"linux/compiler.h\" /* for READ_ONCE and WRITE_ONCE definitions */\n\n/*\n * Simple doubly linked list implementation.\n *\n * Some of the internal functions (\"__xxx\") are useful when\n * manipulating whole lists rather than single entries, as\n * sometimes we already know the next/prev entries and we can\n * generate better code by using them directly rather than\n * using the generic single-entry routines.\n */\n\n#define LIST_HEAD_INIT(name) \\\n    {                        \\\n        &(name), &(name)     \\\n    }\n\n#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name)\n\nstatic inline void INIT_LIST_HEAD(struct list_head *list)\n{\n    WRITE_ONCE(list->next, list);\n    list->prev = list;\n}\n\n/*\n * Insert a new entry between two known consecutive entries.\n *\n * This is only for internal list manipulation where we know\n * the prev/next entries already!\n */\nstatic inline void __list_add(struct list_head *new,\n                              struct list_head *prev,\n                              struct list_head *next)\n{\n    next->prev = new;\n    new->next = next;\n    new->prev = prev;\n    WRITE_ONCE(prev->next, new);\n}\n\n/**\n * list_add - add a new entry\n * @new: new entry to be added\n * @head: list head to add it after\n *\n * Insert a new entry after the specified head.\n * This is good for implementing stacks.\n */\nstatic inline void list_add(struct list_head *new, struct list_head *head)\n{\n    __list_add(new, head, head->next);\n}\n\n\n/**\n * list_add_tail - add a new entry\n * @new: new entry to be added\n * @head: list head to add it before\n *\n * Insert a new entry before the specified head.\n * This is useful for implementing queues.\n */\nstatic inline void list_add_tail(struct list_head *new, struct list_head *head)\n{\n    __list_add(new, head->prev, head);\n}\n\n/*\n * Delete a list entry by making the prev/next entries\n * point to each other.\n *\n * This is only for internal list manipulation where we know\n * the prev/next entries already!\n */\nstatic inline void __list_del(struct list_head *prev, struct list_head *next)\n{\n    next->prev = prev;\n    WRITE_ONCE(prev->next, next);\n}\n\n/**\n * list_del - deletes entry from list.\n * @entry: the element to delete from the list.\n * Note: list_empty() on entry does not return true after this, the entry is\n * in an undefined state.\n */\nstatic inline void __list_del_entry(struct list_head *entry)\n{\n    __list_del(entry->prev, entry->next);\n}\n\nstatic inline void list_del(struct list_head *entry)\n{\n    __list_del(entry->prev, entry->next);\n    entry->next = LIST_POISON1;\n    entry->prev = LIST_POISON2;\n}\n\n/**\n * list_replace - replace old entry by new one\n * @old : the element to be replaced\n * @new : the new element to insert\n *\n * If @old was empty, it will be overwritten.\n */\nstatic inline void list_replace(struct list_head *old, struct list_head *new)\n{\n    new->next = old->next;\n    new->next->prev = new;\n    new->prev = old->prev;\n    new->prev->next = new;\n}\n\nstatic inline void list_replace_init(struct list_head *old,\n                                     struct list_head *new)\n{\n    list_replace(old, new);\n    INIT_LIST_HEAD(old);\n}\n\n/**\n * list_del_init - deletes entry from list and reinitialize it.\n * @entry: the element to delete from the list.\n */\nstatic inline void list_del_init(struct list_head *entry)\n{\n    __list_del_entry(entry);\n    INIT_LIST_HEAD(entry);\n}\n\n/**\n * list_move - delete from one list and add as another's head\n * @list: the entry to move\n * @head: the head that will precede our entry\n */\nstatic inline void list_move(struct list_head *list, struct list_head *head)\n{\n    __list_del_entry(list);\n    list_add(list, head);\n}\n\n/**\n * list_move_tail - delete from one list and add as another's tail\n * @list: the entry to move\n * @head: the head that will follow our entry\n */\nstatic inline void list_move_tail(struct list_head *list,\n                                  struct list_head *head)\n{\n    __list_del_entry(list);\n    list_add_tail(list, head);\n}\n\n/**\n * list_is_last - tests whether @list is the last entry in list @head\n * @list: the entry to test\n * @head: the head of the list\n */\nstatic inline int list_is_last(const struct list_head *list,\n                               const struct list_head *head)\n{\n    return list->next == head;\n}\n\n/**\n * list_empty - tests whether a list is empty\n * @head: the list to test.\n */\nstatic inline int list_empty(const struct list_head *head)\n{\n    return READ_ONCE(head->next) == head;\n}\n\n/**\n * list_empty_careful - tests whether a list is empty and not being modified\n * @head: the list to test\n *\n * Description:\n * tests whether a list is empty _and_ checks that no other CPU might be\n * in the process of modifying either member (next or prev)\n *\n * NOTE: using list_empty_careful() without synchronization\n * can only be safe if the only activity that can happen\n * to the list entry is list_del_init(). Eg. it cannot be used\n * if another CPU could re-list_add() it.\n */\nstatic inline int list_empty_careful(const struct list_head *head)\n{\n    struct list_head *next = head->next;\n    return (next == head) && (next == head->prev);\n}\n\n/**\n * list_rotate_left - rotate the list to the left\n * @head: the head of the list\n */\nstatic inline void list_rotate_left(struct list_head *head)\n{\n    struct list_head *first;\n\n    if (!list_empty(head)) {\n        first = head->next;\n        list_move_tail(first, head);\n    }\n}\n\n/**\n * list_is_singular - tests whether a list has just one entry.\n * @head: the list to test.\n */\nstatic inline int list_is_singular(const struct list_head *head)\n{\n    return !list_empty(head) && (head->next == head->prev);\n}\n\nstatic inline void __list_cut_position(struct list_head *list,\n                                       struct list_head *head,\n                                       struct list_head *entry)\n{\n    struct list_head *new_first = entry->next;\n    list->next = head->next;\n    list->next->prev = list;\n    list->prev = entry;\n    entry->next = list;\n    head->next = new_first;\n    new_first->prev = head;\n}\n\n/**\n * list_cut_position - cut a list into two\n * @list: a new list to add all removed entries\n * @head: a list with entries\n * @entry: an entry within head, could be the head itself\n *\tand if so we won't cut the list\n *\n * This helper moves the initial part of @head, up to and\n * including @entry, from @head to @list. You should\n * pass on @entry an element you know is on @head. @list\n * should be an empty list or a list you do not care about\n * losing its data.\n *\n */\nstatic inline void list_cut_position(struct list_head *list,\n                                     struct list_head *head,\n                                     struct list_head *entry)\n{\n    if (list_empty(head))\n        return;\n    if (list_is_singular(head) && (head->next != entry && head != entry))\n        return;\n    if (entry == head)\n        INIT_LIST_HEAD(list);\n    else\n        __list_cut_position(list, head, entry);\n}\n\nstatic inline void __list_splice(const struct list_head *list,\n                                 struct list_head *prev,\n                                 struct list_head *next)\n{\n    struct list_head *first = list->next;\n    struct list_head *last = list->prev;\n\n    first->prev = prev;\n    prev->next = first;\n\n    last->next = next;\n    next->prev = last;\n}\n\n/**\n * list_splice - join two lists, this is designed for stacks\n * @list: the new list to add.\n * @head: the place to add it in the first list.\n */\nstatic inline void list_splice(const struct list_head *list,\n                               struct list_head *head)\n{\n    if (!list_empty(list))\n        __list_splice(list, head, head->next);\n}\n\n/**\n * list_splice_tail - join two lists, each list being a queue\n * @list: the new list to add.\n * @head: the place to add it in the first list.\n */\nstatic inline void list_splice_tail(struct list_head *list,\n                                    struct list_head *head)\n{\n    if (!list_empty(list))\n        __list_splice(list, head->prev, head);\n}\n\n/**\n * list_splice_init - join two lists and reinitialise the emptied list.\n * @list: the new list to add.\n * @head: the place to add it in the first list.\n *\n * The list at @list is reinitialised\n */\nstatic inline void list_splice_init(struct list_head *list,\n                                    struct list_head *head)\n{\n    if (!list_empty(list)) {\n        __list_splice(list, head, head->next);\n        INIT_LIST_HEAD(list);\n    }\n}\n\n/**\n * list_splice_tail_init - join two lists and reinitialise the emptied list\n * @list: the new list to add.\n * @head: the place to add it in the first list.\n *\n * Each of the lists is a queue.\n * The list at @list is reinitialised\n */\nstatic inline void list_splice_tail_init(struct list_head *list,\n                                         struct list_head *head)\n{\n    if (!list_empty(list)) {\n        __list_splice(list, head->prev, head);\n        INIT_LIST_HEAD(list);\n    }\n}\n\n/**\n * list_entry - get the struct for this entry\n * @ptr:\tthe &struct list_head pointer.\n * @type:\tthe type of the struct this is embedded in.\n * @member:\tthe name of the list_head within the struct.\n */\n#define list_entry(ptr, type, member) container_of(ptr, type, member)\n\n/**\n * list_first_entry - get the first element from a list\n * @ptr:\tthe list head to take the element from.\n * @type:\tthe type of the struct this is embedded in.\n * @member:\tthe name of the list_head within the struct.\n *\n * Note, that list is expected to be not empty.\n */\n#define list_first_entry(ptr, type, member) \\\n    list_entry((ptr)->next, type, member)\n\n/**\n * list_last_entry - get the last element from a list\n * @ptr:\tthe list head to take the element from.\n * @type:\tthe type of the struct this is embedded in.\n * @member:\tthe name of the list_head within the struct.\n *\n * Note, that list is expected to be not empty.\n */\n#define list_last_entry(ptr, type, member) list_entry((ptr)->prev, type, member)\n\n/**\n * list_first_entry_or_null - get the first element from a list\n * @ptr:\tthe list head to take the element from.\n * @type:\tthe type of the struct this is embedded in.\n * @member:\tthe name of the list_head within the struct.\n *\n * Note that if the list is empty, it returns NULL.\n */\n#define list_first_entry_or_null(ptr, type, member) \\\n    (!list_empty(ptr) ? list_first_entry(ptr, type, member) : NULL)\n\n/**\n * list_next_entry - get the next element in list\n * @pos:\tthe type * to cursor\n * @member:\tthe name of the list_head within the struct.\n */\n#define list_next_entry(pos, member) \\\n    list_entry((pos)->member.next, __typeof__(*(pos)), member)\n\n/**\n * list_prev_entry - get the prev element in list\n * @pos:\tthe type * to cursor\n * @member:\tthe name of the list_head within the struct.\n */\n#define list_prev_entry(pos, member) \\\n    list_entry((pos)->member.prev, __typeof__(*(pos)), member)\n\n/**\n * list_for_each\t-\titerate over a list\n * @pos:\tthe &struct list_head to use as a loop cursor.\n * @head:\tthe head for your list.\n */\n#define list_for_each(pos, head) \\\n    for (pos = (head)->next; pos != (head); pos = pos->next)\n\n/**\n * list_for_each_prev\t-\titerate over a list backwards\n * @pos:\tthe &struct list_head to use as a loop cursor.\n * @head:\tthe head for your list.\n */\n#define list_for_each_prev(pos, head) \\\n    for (pos = (head)->prev; pos != (head); pos = pos->prev)\n\n/**\n * list_for_each_safe - iterate over a list safe against removal of list entry\n * @pos:\tthe &struct list_head to use as a loop cursor.\n * @n:\t\tanother &struct list_head to use as temporary storage\n * @head:\tthe head for your list.\n */\n#define list_for_each_safe(pos, n, head)                   \\\n    for (pos = (head)->next, n = pos->next; pos != (head); \\\n         pos = n, n = pos->next)\n\n/**\n * list_for_each_prev_safe - iterate over a list backwards safe against removal\n * of list entry\n * @pos:\tthe &struct list_head to use as a loop cursor.\n * @n:\t\tanother &struct list_head to use as temporary storage\n * @head:\tthe head for your list.\n */\n#define list_for_each_prev_safe(pos, n, head)              \\\n    for (pos = (head)->prev, n = pos->prev; pos != (head); \\\n         pos = n, n = pos->prev)\n\n/**\n * list_for_each_entry\t-\titerate over list of given type\n * @pos:\tthe type * to use as a loop cursor.\n * @head:\tthe head for your list.\n * @member:\tthe name of the list_head within the struct.\n */\n#define list_for_each_entry(pos, head, member)                   \\\n    for (pos = list_first_entry(head, __typeof__(*pos), member); \\\n         &pos->member != (head); pos = list_next_entry(pos, member))\n\n/**\n * list_for_each_entry_reverse - iterate backwards over list of given type.\n * @pos:\tthe type * to use as a loop cursor.\n * @head:\tthe head for your list.\n * @member:\tthe name of the list_head within the struct.\n */\n#define list_for_each_entry_reverse(pos, head, member)          \\\n    for (pos = list_last_entry(head, __typeof__(*pos), member); \\\n         &pos->member != (head); pos = list_prev_entry(pos, member))\n\n/**\n * list_prepare_entry - prepare a pos entry for use in\n * list_for_each_entry_continue()\n * @pos:\tthe type * to use as a start point\n * @head:\tthe head of the list\n * @member:\tthe name of the list_head within the struct.\n *\n * Prepares a pos entry for use as a start point in\n * list_for_each_entry_continue().\n */\n#define list_prepare_entry(pos, head, member) \\\n    ((pos) ?: list_entry(head, __typeof__(*pos), member))\n\n/**\n * list_for_each_entry_continue - continue iteration over list of given type\n * @pos:\tthe type * to use as a loop cursor.\n * @head:\tthe head for your list.\n * @member:\tthe name of the list_head within the struct.\n *\n * Continue to iterate over list of given type, continuing after\n * the current position.\n */\n#define list_for_each_entry_continue(pos, head, member)              \\\n    for (pos = list_next_entry(pos, member); &pos->member != (head); \\\n         pos = list_next_entry(pos, member))\n\n/**\n * list_for_each_entry_continue_reverse - iterate backwards from the given point\n * @pos:\tthe type * to use as a loop cursor.\n * @head:\tthe head for your list.\n * @member:\tthe name of the list_head within the struct.\n *\n * Start to iterate over list of given type backwards, continuing after\n * the current position.\n */\n#define list_for_each_entry_continue_reverse(pos, head, member)      \\\n    for (pos = list_prev_entry(pos, member); &pos->member != (head); \\\n         pos = list_prev_entry(pos, member))\n\n/**\n * list_for_each_entry_from - iterate over list of given type from the current\n * point\n * @pos:\tthe type * to use as a loop cursor.\n * @head:\tthe head for your list.\n * @member:\tthe name of the list_head within the struct.\n *\n * Iterate over list of given type, continuing from current position.\n */\n#define list_for_each_entry_from(pos, head, member) \\\n    for (; &pos->member != (head); pos = list_next_entry(pos, member))\n\n/**\n * list_for_each_entry_safe - iterate over list of given type safe against\n * removal of list entry\n * @pos:\tthe type * to use as a loop cursor.\n * @n:\t\tanother type * to use as temporary storage\n * @head:\tthe head for your list.\n * @member:\tthe name of the list_head within the struct.\n */\n#define list_for_each_entry_safe(pos, n, head, member)           \\\n    for (pos = list_first_entry(head, __typeof__(*pos), member), \\\n        n = list_next_entry(pos, member);                        \\\n         &pos->member != (head); pos = n, n = list_next_entry(n, member))\n\n/**\n * list_for_each_entry_safe_continue - continue list iteration safe against\n * removal\n * @pos:\tthe type * to use as a loop cursor.\n * @n:\t\tanother type * to use as temporary storage\n * @head:\tthe head for your list.\n * @member:\tthe name of the list_head within the struct.\n *\n * Iterate over list of given type, continuing after current point,\n * safe against removal of list entry.\n */\n#define list_for_each_entry_safe_continue(pos, n, head, member)                \\\n    for (pos = list_next_entry(pos, member), n = list_next_entry(pos, member); \\\n         &pos->member != (head); pos = n, n = list_next_entry(n, member))\n\n/**\n * list_for_each_entry_safe_from - iterate over list from current point safe\n * against removal\n * @pos:\tthe type * to use as a loop cursor.\n * @n:\t\tanother type * to use as temporary storage\n * @head:\tthe head for your list.\n * @member:\tthe name of the list_head within the struct.\n *\n * Iterate over list of given type from current point, safe against\n * removal of list entry.\n */\n#define list_for_each_entry_safe_from(pos, n, head, member)        \\\n    for (n = list_next_entry(pos, member); &pos->member != (head); \\\n         pos = n, n = list_next_entry(n, member))\n\n/**\n * list_for_each_entry_safe_reverse - iterate backwards over list safe against\n * removal\n * @pos:\tthe type * to use as a loop cursor.\n * @n:\t\tanother type * to use as temporary storage\n * @head:\tthe head for your list.\n * @member:\tthe name of the list_head within the struct.\n *\n * Iterate backwards over list of given type, safe against removal\n * of list entry.\n */\n#define list_for_each_entry_safe_reverse(pos, n, head, member)  \\\n    for (pos = list_last_entry(head, __typeof__(*pos), member), \\\n        n = list_prev_entry(pos, member);                       \\\n         &pos->member != (head); pos = n, n = list_prev_entry(n, member))\n\n/**\n * list_safe_reset_next - reset a stale list_for_each_entry_safe loop\n * @pos:\tthe loop cursor used in the list_for_each_entry_safe loop\n * @n:\t\ttemporary storage used in list_for_each_entry_safe\n * @member:\tthe name of the list_head within the struct.\n *\n * list_safe_reset_next is not safe to use in general if the list may be\n * modified concurrently (eg. the lock is dropped in the loop body). An\n * exception to this is if the cursor element (pos) is pinned in the list,\n * and list_safe_reset_next is called after re-taking the lock and before\n * completing the current iteration of the loop body.\n */\n#define list_safe_reset_next(pos, n, member) n = list_next_entry(pos, member)\n\n/*\n * Double linked lists with a single pointer list head.\n * Mostly useful for hash tables where the two pointer list head is\n * too wasteful.\n * You lose the ability to access the tail in O(1).\n */\n\n#define HLIST_HEAD_INIT \\\n    {                   \\\n        .first = NULL   \\\n    }\n#define HLIST_HEAD(name) struct hlist_head name = {.first = NULL}\n#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)\nstatic inline void INIT_HLIST_NODE(struct hlist_node *h)\n{\n    h->next = NULL;\n    h->pprev = NULL;\n}\n\nstatic inline int hlist_unhashed(const struct hlist_node *h)\n{\n    return !h->pprev;\n}\n\nstatic inline int hlist_empty(const struct hlist_head *h)\n{\n    return !READ_ONCE(h->first);\n}\n\nstatic inline void __hlist_del(struct hlist_node *n)\n{\n    struct hlist_node *next = n->next;\n    struct hlist_node **pprev = n->pprev;\n\n    WRITE_ONCE(*pprev, next);\n    if (next)\n        next->pprev = pprev;\n}\n\nstatic inline void hlist_del(struct hlist_node *n)\n{\n    __hlist_del(n);\n    n->next = LIST_POISON1;\n    n->pprev = LIST_POISON2;\n}\n\nstatic inline void hlist_del_init(struct hlist_node *n)\n{\n    if (!hlist_unhashed(n)) {\n        __hlist_del(n);\n        INIT_HLIST_NODE(n);\n    }\n}\n\nstatic inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)\n{\n    struct hlist_node *first = h->first;\n    n->next = first;\n    if (first)\n        first->pprev = &n->next;\n    WRITE_ONCE(h->first, n);\n    n->pprev = &h->first;\n}\n\n/* next must be != NULL */\nstatic inline void hlist_add_before(struct hlist_node *n,\n                                    struct hlist_node *next)\n{\n    n->pprev = next->pprev;\n    n->next = next;\n    next->pprev = &n->next;\n    WRITE_ONCE(*(n->pprev), n);\n}\n\nstatic inline void hlist_add_behind(struct hlist_node *n,\n                                    struct hlist_node *prev)\n{\n    n->next = prev->next;\n    WRITE_ONCE(prev->next, n);\n    n->pprev = &prev->next;\n\n    if (n->next)\n        n->next->pprev = &n->next;\n}\n\n/* after that we'll appear to be on some hlist and hlist_del will work */\nstatic inline void hlist_add_fake(struct hlist_node *n)\n{\n    n->pprev = &n->next;\n}\n\nstatic inline bool hlist_fake(struct hlist_node *h)\n{\n    return h->pprev == &h->next;\n}\n\n/*\n * Move a list from one list head to another. Fixup the pprev\n * reference of the first entry if it exists.\n */\nstatic inline void hlist_move_list(struct hlist_head *old,\n                                   struct hlist_head *new)\n{\n    new->first = old->first;\n    if (new->first)\n        new->first->pprev = &new->first;\n    old->first = NULL;\n}\n\n#define hlist_entry(ptr, type, member) container_of(ptr, type, member)\n\n#define hlist_for_each(pos, head) \\\n    for (pos = (head)->first; pos; pos = pos->next)\n\n#define hlist_for_each_safe(pos, n, head)        \\\n    for (pos = (head)->first; pos && ({          \\\n                                  n = pos->next; \\\n                                  1;             \\\n                              });                \\\n         pos = n)\n\n#define hlist_entry_safe(ptr, type, member)                  \\\n    ({                                                       \\\n        __typeof__(ptr) ____ptr = (ptr);                     \\\n        ____ptr ? hlist_entry(____ptr, type, member) : NULL; \\\n    })\n\n/**\n * hlist_for_each_entry\t- iterate over list of given type\n * @pos:\tthe type * to use as a loop cursor.\n * @head:\tthe head for your list.\n * @member:\tthe name of the hlist_node within the struct.\n */\n#define hlist_for_each_entry(pos, head, member)                              \\\n    for (pos = hlist_entry_safe((head)->first, __typeof__(*(pos)), member);  \\\n         pos; pos = hlist_entry_safe((pos)->member.next, __typeof__(*(pos)), \\\n                                     member))\n\n/**\n * hlist_for_each_entry_continue - iterate over a hlist continuing after current\n * point\n * @pos:\tthe type * to use as a loop cursor.\n * @member:\tthe name of the hlist_node within the struct.\n */\n#define hlist_for_each_entry_continue(pos, member)                             \\\n    for (pos =                                                                 \\\n             hlist_entry_safe((pos)->member.next, __typeof__(*(pos)), member); \\\n         pos; pos = hlist_entry_safe((pos)->member.next, __typeof__(*(pos)),   \\\n                                     member))\n\n/**\n * hlist_for_each_entry_from - iterate over a hlist continuing from current\n * point\n * @pos:\tthe type * to use as a loop cursor.\n * @member:\tthe name of the hlist_node within the struct.\n */\n#define hlist_for_each_entry_from(pos, member)                                 \\\n    for (; pos; pos = hlist_entry_safe((pos)->member.next, __typeof__(*(pos)), \\\n                                       member))\n\n/**\n * hlist_for_each_entry_safe - iterate over list of given type safe against\n * removal of list entry\n * @pos:\tthe type * to use as a loop cursor.\n * @n:\t\tanother &struct hlist_node to use as temporary storage\n * @head:\tthe head for your list.\n * @member:\tthe name of the hlist_node within the struct.\n */\n#define hlist_for_each_entry_safe(pos, n, head, member)                   \\\n    for (pos = hlist_entry_safe((head)->first, __typeof__(*pos), member); \\\n         pos && ({                                                        \\\n             n = pos->member.next;                                        \\\n             1;                                                           \\\n         });                                                              \\\n         pos = hlist_entry_safe(n, __typeof__(*pos), member))\n\n#endif\n"
  },
  {
    "path": "include/linux/poison.h",
    "content": "#ifndef LINUX_POISON_H\n#define LINUX_POISON_H\n\n/*\n * These are non-NULL pointers that will result in page faults\n * under normal circumstances, used to verify that nobody uses\n * non-initialized list entries.\n * Make sure the values raise faults when these addresses are\n * read/written.\n */\n#define LIST_POISON1 ((void *) 0x100)\n#define LIST_POISON2 ((void *) 0x200)\n\n#endif /* !LINUX_POISON_H */\n"
  },
  {
    "path": "include/linux/stddef.h",
    "content": "#ifndef _LINUX_STDDEF_H\n#define _LINUX_STDDEF_H\n\n#include <stddef.h>\n\n/**\n * offsetofend(TYPE, MEMBER)\n *\n * @TYPE: The type of the structure\n * @MEMBER: The member within the structure to get the end offset of\n */\n#define offsetofend(TYPE, MEMBER) \\\n    (offsetof(TYPE, MEMBER) + sizeof(((TYPE *) 0)->MEMBER))\n\n#endif\n"
  },
  {
    "path": "include/linux/types.h",
    "content": "#ifndef LINUX_TYPES_H\n#define LINUX_TYPES_H\n\n#define S32_MAX 2147483647\n\ntypedef unsigned int u32;\ntypedef int s32;\n\ntypedef unsigned short u16;\ntypedef short s16;\n\ntypedef unsigned char u8;\ntypedef char s8;\n\ntypedef unsigned int size_t;\n\nstruct list_head {\n    struct list_head *next, *prev;\n};\n\nstruct hlist_head {\n    struct hlist_node *first;\n};\n\nstruct hlist_node {\n    struct hlist_node *next, **pprev;\n};\n\ntypedef struct {\n    volatile s32 val;\n} atomic_t;\n\n#endif /* !LINUX_TYPES_H */\n"
  },
  {
    "path": "include/piko/arpa/inet.h",
    "content": "#ifndef LIBC_ARPA_INET_H\n#define LIBC_ARPA_INET_H\n\n#include <stdint.h>\n\n#include \"platform.h\"\n\nstatic inline uint32_t htonl(uint32_t hostlong)\n{\n    return __REV(hostlong);\n}\n\nstatic inline uint16_t htons(uint16_t hostshort)\n{\n    return __REV16(hostshort);\n}\n\nstatic inline uint32_t ntohl(uint32_t netlong)\n{\n    return __REV(netlong);\n}\n\nstatic inline uint16_t ntohs(uint16_t netshort)\n{\n    return __REV16(netshort);\n}\n\n#endif /* !LIBC_ARPA_INET_H */\n"
  },
  {
    "path": "include/piko/dirent.h",
    "content": "#ifndef _LIBPIKO_DIRENT_H\n#define _LIBPIKO_DIRENT_H\n\n#include <sys/types.h>\n\n#define NAME_MAX 32  // FIXME: Include <limits.h>\n\ntypedef void DIR;\n\nstruct dirent {\n    ino_t d_ino;           /* inode number */\n    char d_name[NAME_MAX]; /* filename */\n};\n\nDIR *opendir(const char *dirname);\n\nint readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);\n\nint closedir(DIR *dirp);\n\n#endif /* !_LIBPIKO_DIRENT_H */\n"
  },
  {
    "path": "include/piko/signal.h",
    "content": "#ifndef LIBC_SIGNAL_H\n#define LIBC_SIGNAL_H\n\n#include <sys/types.h>\n\n#include \"linux/list.h\"\n\ntypedef struct {\n    void *ss_sp;    /* Base address of stack */\n    int ss_flags;   /* Flags */\n    size_t ss_size; /* Number of bytes in stack */\n} stack_t;\n\n/* sigevent - structure for notification from asynchronous routines */\n\nunion sigval {       /* Data passed with notification */\n    int sival_int;   /* Integer value */\n    void *sival_ptr; /* Pointer value */\n};\n\nstruct sigevent {\n    int sigev_notify; /* Notification method */\n    int sigev_signo;  /* Notification signal */\n\n    /* Data passed with notification */\n    union sigval sigev_value;\n\n    /* Function used for thread notification (SIGEV_THREAD) */\n    void (*sigev_notify_function)(union sigval);\n\n    /* Attributes for notification thread (SIGEV_THREAD) */\n    void *sigev_notify_attributes;\n\n    /* ID of thread to signal (SIGEV_THREAD_ID) */\n    pid_t sigev_notify_thread_id;\n};\n\n/* sigaction - used to change the action taken by a process on receipt of a\n     specific signal  */\n\ntypedef struct {\n    int si_signo;\n    /* int si_code; */\n    union sigval si_value;\n    /* int si_errno; */\n    pid_t si_pid;\n    /* uid_t si_uid; */\n    /* void *si_addr; */\n    /* int si_status; */\n    /* int si_band; */\n} siginfo_t;\n\ntypedef int sigset_t;\n\nstruct sigaction {\n    union {\n        void (*sa_handler)(int);\n        void (*sa_sigaction)(int, siginfo_t *, void *);\n    };\n    sigset_t sa_mask;\n    int sa_flags;\n\n    /* Storage for kernel fields.  Not compliant with the POSIX specs.  */\n    struct list_head sa_list;\n    int sa_signo;\n};\n\n#define SA_SIGINFO (1 << 0)\n\n#define SIGKILL 9  /* Kill (can't be caught or ignored) (POSIX) */\n#define SIGUSR1 10 /* User defined signal 1 (POSIX) */\n#define SIGUSR2 12 /* User defined signal 2 (POSIX) */\n#define SIGSTOP 19 /* Stop executing(can't be caught or ignored) (POSIX) */\n\nint sigaction(int sig,\n              const struct sigaction *restrict act,\n              struct sigaction *restrict oact);\n\nint raise(int sig);\n\n#endif /* !LIBC_SIGNAL_H */\n"
  },
  {
    "path": "include/piko/sys/mman.h",
    "content": "#ifndef _SYS_MMAN_H\n#define _SYS_MMAN_H\n\n#include <sys/types.h>\n\n#define PROT_NONE 0x00  /* page can not be accessed */\n#define PROT_READ 0x01  /* page can be read */\n#define PROT_WRITE 0x02 /* page can be written */\n#define PROT_EXEC 0x04  /* page can be executed */\n\n#define MAP_FAILED ((void *) -1)\n\n#define MAP_ANONYMOUS 0x01     /* don't use a file */\n#define MAP_UNINITIALIZED 0x02 /* anonymous memory can be uninitialized */\n\nvoid *mmap(void *addr,\n           size_t length,\n           int prot,\n           int flags,\n           int fd,\n           off_t offset);\n\nint munmap(void *addr, size_t length);\n\n#endif /* !_SYS_MMAN_H */\n"
  },
  {
    "path": "include/piko/sys/mount.h",
    "content": "#ifndef _SYS_MOUNT_H\n#define _SYS_MOUNT_H\n\nint mount(const char *source,\n          const char *target,\n          const char *filesystemtype,\n          unsigned long mountflags,\n          const void *data);\n\n#endif /* !_SYS_MOUNT_H */\n"
  },
  {
    "path": "include/piko/sys/resource.h",
    "content": "#ifndef SYS_RESOURCE_H\n#define SYS_RESOURCE_H\n\n/* http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_resource.h.html\n */\n\n/* Unsigned integer type used for limit values. */\ntypedef unsigned int rlim_t;\n\nstruct rlimit {\n    rlim_t rlim_cur; /* Soft limit */\n    rlim_t rlim_max; /* Hard limit (ceiling for rlim_cur) */\n};\n\n#define RLIMIT_STACK 0\n\n#ifdef __KERNEL__\n\nint sys_getrlimit(int resource, struct rlimit *rlim);\n\nint sys_setrlimit(int resource, const struct rlimit *rlim);\n\n#else /* !__KERNEL__ */\n\nint getrlimit(int resource, struct rlimit *rlim);\n\nint setrlimit(int resource, const struct rlimit *rlim);\n\n#endif /* __KERNEL__ */\n\n#endif /* !SYS_RESOURCE_H */\n"
  },
  {
    "path": "include/platform/compiler.h",
    "content": "#ifndef _PLATFORM_COMPILER_H\n#define _PLATFORM_COMPILER_H\n\n#define PLAT_EVAL(macro)                \\\n    ({                                  \\\n        void __wrapper__(void) {macro;} \\\n        __wrapper__;                    \\\n    })\n\n#endif /* !_PLATFORM_COMPILER_H */\n"
  },
  {
    "path": "include/version.template.h",
    "content": "#ifndef VERSION_H\n#define VERSION_H\n\n#define VER_MAJOR \"0\"\n#define VER_MINOR \"0\"\n#define VER_MICRO \"0\"\n\n#define VER_SLUG \"piko-\" VER_MAJOR \".\" VER_MINOR \".\" VER_MICRO\n\n#endif /* !VERSION_H */\n"
  },
  {
    "path": "kernel/cond.c",
    "content": "#include <sys/types.h>\n\n#include <kernel/mutex.h>\n#include <kernel/sched.h>\n#include <kernel/thread.h>\n\n#include \"linux/list.h\"\n\nstatic LIST_HEAD(cond_head);\n\nstatic struct thread_info *find_other_thread(pthread_cond_t *cond)\n{\n    struct thread_info *other;\n\n    list_for_each_entry(other, &cond_head, ti_q)\n    {\n        if (other->ti_private == cond)\n            return other;\n    }\n\n    return NULL;\n}\n\nint sys_pthread_cond_wait(pthread_cond_t *cond, kernel_mutex_t *mutex)\n{\n    CURRENT_THREAD_INFO(curr_thread);\n    curr_thread->ti_private = cond;\n    curr_thread->ti_state = THREAD_STATE_BLOCKED;\n    list_add_tail(&curr_thread->ti_q, &cond_head);\n    sys_pthread_mutex_unlock(mutex);\n\n    /* contend for the lock */\n    sys_pthread_mutex_lock(mutex);\n\n    return 0;\n}\n\nint sys_pthread_cond_signal(pthread_cond_t *cond)\n{\n    struct thread_info *other;\n\n    other = find_other_thread(cond);\n    if (!other)\n        return 0;\n\n    list_del(&other->ti_q);\n    sched_enqueue(other);\n    CURRENT_THREAD_INFO(curr_thread);\n    if (other->ti_priority >= curr_thread->ti_priority) {\n        sched_enqueue(curr_thread);\n        sched_elect(SCHED_OPT_NONE);\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "kernel/config.c",
    "content": "#include <unistd.h>\n\nenum sc_varname {\n    PAGESIZE,\n};\n\nstruct sys_param {\n    const char *name;\n    long value;\n};\n\nstruct sys_param sys_params[] = {\n    {\"pagesize\", 2048},\n    {\"clock_tick\", 0},  // FIXME: set by systick_init()\n};\n\nlong sys_sysconf(int name)\n{\n    switch (name) {\n    case PAGESIZE:\n        return sys_params[0].value;\n    case _SC_CLK_TCK:\n        return sys_params[1].value;\n    }\n\n    return -1;\n}\n"
  },
  {
    "path": "kernel/faults.c",
    "content": "#include <kernel/faults.h>\n#include <kernel/thread.h>\n\n#include \"kernel.h\"\n#include \"utils.h\"\n#include \"platform.h\"\n\nvoid fault_enter(const char *s)\n{\n    printk(\"\\n-------------------------------------------------------------\\n\");\n    printk(\" #%s\\n\\n\", s);\n}\n\nvoid fault_exit(void)\n{\n    printk(\"-------------------------------------------------------------\\n\");\n    __platform_halt();\n}\n\nvoid hardfault(struct kernel_context_regs *noscratch,\n               struct thread_context_regs *scratch,\n               u32 exc_return)\n{\n    fault_enter(\"HardFault\");\n    dump_frame(noscratch, scratch, exc_return);\n    fault_exit();\n}\n"
  },
  {
    "path": "kernel/fs/fs.c",
    "content": "#include <errno.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n\n#include <kernel/bitops.h>\n#include <kernel/errno-base.h>\n#include <kernel/fs.h>\n#include <fs/romfs.h>\n#include <kernel/task.h>\n\nstruct file *fd_to_file(int fd)\n{\n    CURRENT_TASK_INFO(curr_task);\n\n    return &curr_task->filetable[fd];\n}\n\nstatic int getfd(void)\n{\n    int fd;\n    CURRENT_TASK_INFO(curr_task);\n\n    fd = find_first_zero_bit(&curr_task->filemap, FILE_MAX);\n    if (fd == FILE_MAX)\n        return -1;\n    bitmap_set_bit(&curr_task->filemap, fd);\n\n    return fd;\n}\n\nstatic void releasefd(int fd)\n{\n    CURRENT_TASK_INFO(curr_task);\n\n    bitmap_clear_bit(&curr_task->filemap, fd);\n}\n\nint release_dentries(struct dentry *dentry)\n{\n    struct dentry *parent;\n\n    for (; dentry != root_dentry(); dentry = parent) {\n        if (!dentry->d_count)\n            return -1;\n        if (--dentry->d_count)\n            break;\n        parent = dentry->d_parent;\n        vfs_release(dentry);\n        if (!dentry->d_count)\n            vfs_delete(dentry);\n    }\n\n    return 0;\n}\n\nstatic int path_head(char *buf, const char *pathname)\n{\n    int i, i0 = 0;\n\n    if (pathname[0] == '\\0')\n        return -1;\n    if (pathname[0] == '/')\n        i0++;\n    for (i = i0; i < NAME_MAX && pathname[i] != '/' && pathname[i] != '\\0'; i++)\n        ;\n    strncpy(buf, &pathname[i0], i - i0);\n    buf[i - i0] = '\\0';\n\n    return i;\n}\n\nstruct inode *inode_from_pathname(\n    const char *pathname /* , struct dentry *from */)\n{\n    struct inode *inode = root_inode();\n    struct dentry *dentry = root_dentry();\n    struct dentry target;\n\n    /* remove the trailing slash, relative path is not supported */\n    pathname++;\n\n    for (size_t i = 0; i < strlen(pathname);) {\n        i += path_head(target.d_name, &pathname[i]);\n        dentry = vfs_lookup(inode, &target);\n        if (!dentry)\n            return NULL;\n        inode = dentry->d_inode;\n    }\n\n    return inode;\n}\n\nint sys_open(const char *pathname, int flags)\n{\n    struct inode *inode = root_inode();\n    struct dentry *dentry = root_dentry();\n    struct dentry *parent = dentry;\n    struct dentry *target;\n\n    /* remove the trailing slash, relative path is not supported */\n    pathname++;\n\n    for (size_t i = 0; i < strlen(pathname);) {\n        target = malloc(sizeof(struct dentry));\n        if (!target) {\n            errno = ENOMEM;\n            return 0;\n        }\n        target->d_count = 1;\n        target->d_parent = parent;\n        i += path_head(target->d_name, &pathname[i]);\n\n        dentry = vfs_lookup(inode, target);\n        if (!dentry) {\n            errno = ENOENT;\n            release_dentries(target->d_parent);\n            return 0;\n        }\n        inode = dentry->d_inode;\n        parent = dentry;\n    }\n\n    /* opendir() redirects to sys_open() */\n    if ((flags & O_DIRECTORY) && !S_ISDIR(inode->i_mode)) {\n        errno = ENOTDIR;\n        return 0;\n    }\n\n    int fd = getfd();\n    if (fd < 0) {\n        errno = EBADF;\n        return 0;\n    }\n\n    struct file *file = fd_to_file(fd);\n    file->f_dentry = dentry;\n    file->f_op = dentry->d_inode->i_fop;\n    file->f_pos = 0;\n    if (file->f_op->open)\n        file->f_op->open(inode, file);\n\n    return fd;\n}\n\nssize_t sys_read(int fd, void *buf, size_t count)\n{\n    struct file *file = fd_to_file(fd);\n\n    if (count)\n        count = file->f_op->read(file, buf, count, file->f_pos);\n    file->f_pos += count;\n\n    return count;\n}\n\nssize_t sys_write(int fd, void *buf, size_t count)\n{\n    struct file *file = fd_to_file(fd);\n    off_t offset = file->f_pos;\n\n    count = file->f_op->write(file, buf, count, &offset);\n    file->f_pos += count;\n\n    return count;\n}\n\noff_t sys_lseek(int fd, off_t offset, int whence)\n{\n    struct file *file = fd_to_file(fd);\n    off_t size = file->f_dentry->d_inode->i_size;\n\n    if (file->f_op->lseek)\n        file->f_op->lseek(file, offset, whence);\n    switch (whence) {\n    case SEEK_SET:\n        if (offset)\n            offset = size % offset;\n        break;\n    case SEEK_CUR:\n        offset = (file->f_pos + offset) % size;\n        break;\n    case SEEK_END:\n        offset = (size + offset) % size;\n        break;\n    default:\n        return -1;\n    }\n    file->f_pos = offset;\n\n    return 0;\n}\n\nint sys_close(int fd)\n{\n    struct file *file = fd_to_file(fd);\n\n    release_dentries(file->f_dentry);\n    releasefd(fd);\n\n    return 0;\n}\n\nint sys_stat(const char *pathname, struct stat *buf)\n{\n    struct inode *inode = inode_from_pathname(pathname);\n\n    if (!inode) {\n        errno = ENOENT;\n        return -1;\n    }\n    buf->st_ino = inode->i_ino;\n    buf->st_mode = inode->i_mode;\n    buf->st_size = inode->i_size;\n\n    return 0;\n}\n\nint sys_mount(const char *source,\n              const char *target,\n              const char *filesystemtype,\n              unsigned long mountflags,\n              const void *data)\n{\n    if (!strcmp(\"romfs\", filesystemtype))\n        return romfs_mount(source, target, filesystemtype, mountflags, data);\n\n    return -1;\n}\n"
  },
  {
    "path": "kernel/fs/readdir.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n\n#include <kernel/fs.h>\n#include <kernel/kernel.h>\n\n#include \"linux/list.h\"\n\nstatic int fillonedir(struct dir_context *ctx,\n                      const char *name,\n                      int namlen,\n                      __unused off_t offset,\n                      unsigned int ino,\n                      __unused unsigned int d_type)\n{\n    struct readdir_callback *buf =\n        container_of(ctx, struct readdir_callback, ctx);\n    struct piko_dirent *dirent = buf->dirent;\n\n    dirent->d_ino = ino;\n    strncpy(dirent->d_name, name, namlen);\n    dirent->d_name[namlen] = '\\0';\n\n    return 0;\n}\n\nint sys_readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)\n{\n    struct file *file = fd_to_file((int) dirp);\n    struct readdir_callback buf = {\n        .ctx = {.actor = fillonedir, .pos = 0},\n        .dirent = (struct piko_dirent *) entry,\n    };\n\n    if (vfs_iterate(file, &buf.ctx))\n        *result = NULL;\n    else\n        *result = entry;\n\n    return 0;\n}\n"
  },
  {
    "path": "kernel/fs/vfs.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n\n#include <kernel/fs.h>\n#include <kernel/kernel.h>\n\n#include \"linux/list.h\"\n\nint vfs_iterate(struct file *file, struct dir_context *ctx)\n{\n    if (!file->f_op->iterate)\n        return -1;\n\n    return file->f_op->iterate(file, ctx);\n}\n\nstruct dentry *vfs_lookup(struct inode *dir, struct dentry *target)\n{\n    if (!dir->i_op->lookup)\n        return NULL;\n    if (!S_ISDIR(dir->i_mode))\n        return NULL;\n\n    return dir->i_op->lookup(dir, target);\n}\n\nint vfs_link(struct dentry *old_dentry,\n             struct inode *dir,\n             struct dentry *dentry)\n{\n    if (!dir->i_op->link)\n        return -1;\n\n    return dir->i_op->link(old_dentry, dir, dentry);\n}\n\nint vfs_delete(struct dentry *dentry)\n{\n    if (!dentry->d_op->delete)\n        return -1;\n\n    return dentry->d_op->delete (dentry);\n}\n\nvoid vfs_release(struct dentry *dentry)\n{\n    if (dentry->d_op->release)\n        return dentry->d_op->release(dentry);\n}\n\nint vfs_mmap(struct file *file, off_t offset, void **addr)\n{\n    if (!file->f_op->mmap)\n        return -1;\n\n    return file->f_op->mmap(file, offset, addr);\n}\n"
  },
  {
    "path": "kernel/irq.c",
    "content": "#include <stddef.h>\n#include <stdlib.h>\n#include <kernel/types.h>\n#include <kernel/irq.h>\n\n#define IRQ_MAX 64\n\nirq_handler_t irq_handler[IRQ_MAX];\n\n// clang-format off\nstatic struct irq_desc irq_desc[NR_IRQS] = {\n    [0 ... NR_IRQS - 1] = {\n        .irq_data = {.state = 0x0},\n        .action = NULL,\n\t.status = 0x0,\n\t}\n};\n// clang-format on\n\nstatic struct irq_desc *irq_to_desc(unsigned int irq)\n{\n    if (irq < NR_IRQS)\n        return irq_desc + irq;\n    return NULL;\n}\n\nint request_irq(unsigned int irq, irq_handler_t hdlr)\n{\n    struct irqaction *action;\n    struct irq_desc *desc;\n\n    desc = irq_to_desc(irq);\n\n    if (desc && (desc->status & IRQ_NOREQUEST)) {\n        action = (struct irqaction *) malloc(sizeof(struct irqaction));\n        if (!action) {\n            // WARN_ON(!action, \"malloc failed\\n\");\n            goto fail;\n        }\n\n        action->irq = irq;\n        action->handler = hdlr;\n\n        /* install to irq_desc */\n        desc->action = action;\n\n        irq_handler[irq] = hdlr;\n\n        return 0;\n    } else\n    fail:\n        return -1;\n}\n\nint free_irq(unsigned int irq)\n{\n    struct irq_desc *desc;\n\n    desc = irq_to_desc(irq);\n\n    if (desc && !(desc->status & IRQ_NOREQUEST)) {\n        free(desc->action);\n        desc->status |= IRQ_NOREQUEST;\n        irq_handler[irq] = NULL;\n\n        return 0;\n    }\n\n    return -1;\n}\n\nvoid early_irq_init(void)\n{\n    for (int irq = 0; irq < NR_IRQS; irq++)\n        irq_desc[irq].irq_data.state = IRQD_IRQ_DISABLED;\n}\n\nvoid init_IRQ(void)\n{\n    for (int irq = 0; irq < NR_IRQS; irq++)\n        irq_desc[irq].status = IRQ_NOREQUEST;\n}\n"
  },
  {
    "path": "kernel/main.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <sys/cdefs.h>\n#include <sys/fcntl.h>\n\n#include <kernel/fs.h>\n#include <kernel/kernel.h>\n#include <kernel/mm/page.h>\n#include <kernel/mm/slab.h>\n#include <kernel/sched.h>\n#include <kernel/task.h>\n#include <kernel/thread.h>\n#include <kernel/compiler.h>\n#include <kernel/softirq.h>\n#include <kernel/serial.h>\n\n#include \"platform.h\"\n\nextern char __early_stack_start__;\nextern char __early_stack_end__;\nextern char __text_start__;\nextern char __text_end__;\nextern char __rodata_start__;\nextern char __rodata_end__;\nextern char __data_start__;\nextern char __data_end__;\nextern char __bss_start__;\nextern char __bss_end__;\nextern char __pgmem_start__;\nextern char __pgmem_end__;\nextern char __pgmem_size__;\nextern char __heap_start__;\nextern char __heap_end__;\nextern char __heap_size__;\n\nvoid __do_idle(void);\nvoid *do_idle(void *);\nvoid mtdram_init(void);\nvoid __printk_init(void);\nint minishell(void *options);\nvoid memdev_init(void);\nvoid kernel_heap_init(void *heap_start, size_t heap_size);\nvoid early_irq_init(void);\nvoid init_IRQ(void);\n\nstruct task_info idle_task;\nstruct task_info main_task;\n\nvoid print_version(void)\n{\n    char buf[] = {0, 0};\n    int fd = open(\"/proc/version\", 0);\n\n    while (read(fd, &buf, 1))\n        printk(\"%s\", buf);\n    close(fd);\n    printk(\"\\n\");\n}\n\nvoid __weak *main(__unused void *arg)\n{\n    print_version();\n    minishell(NULL);\n\n    return 0;\n}\n\nstruct thread_info *thread_idle;\n\n/* Cortex-M3/4 system initialization */\nstatic void v7m_init(void)\n{\n    /* enable UsageFault, BusFault, MemManage faults */\n    SCB->SHCSR |= (SCB_SHCSR_USGFAULTENA_Msk | SCB_SHCSR_BUSFAULTENA_Msk |\n                   SCB_SHCSR_MEMFAULTENA_Msk);\n\n    /* Configure the System Control Register to ensure 8-byte stack\n       alignment */\n    SCB->CCR |= SCB_CCR_STKALIGN_Msk;\n\n    // NVIC_SetPriority(DebugMonitor_IRQn, 0x0, 0);\n\n    NVIC_SetPriority(MemoryManagement_IRQn, 0x1);\n    NVIC_SetPriority(BusFault_IRQn, 0x1);\n    NVIC_SetPriority(UsageFault_IRQn, 0x1);\n\n    NVIC_SetPriority(SysTick_IRQn, 0x3);\n\n    /* Priority 0xF - debug_uart */\n    NVIC_SetPriority(SVCall_IRQn, 0xF);\n    NVIC_SetPriority(PendSV_IRQn, 0xF);\n\n    /* follow the architectural requirements */\n    __DSB();\n}\n\nvoid print_linker_sections(void)\n{\n    printk(\"Memory map:\\n\");\n    printk(\"  .text\t  = %08x--%08x\t%6d Bytes\\n\", &__text_start__,\n           &__text_end__, &__text_end__ - &__text_start__);\n    printk(\"  .rodata = %08x--%08x\t%6d Bytes\\n\", &__rodata_start__,\n           &__rodata_end__, &__rodata_end__ - &__rodata_start__);\n    printk(\"  .data\t  = %08x--%08x\t%6d Bytes\\n\", &__data_start__,\n           &__data_end__, &__data_end__ - &__data_start__);\n    printk(\"  .bss\t  = %08x--%08x\t%6d Bytes\\n\", &__bss_start__, &__bss_end__,\n           &__bss_end__ - &__bss_start__);\n    printk(\"  .heap\t  = %08x--%08x\t%6d Bytes\\n\", &__heap_start__,\n           &__heap_end__, &__heap_end__ - &__heap_start__);\n    printk(\"  .pgmem  = %08x--%08x\t%6d Bytes\\n\", &__pgmem_start__,\n           &__pgmem_end__, &__pgmem_end__ - &__pgmem_start__);\n}\n\n\nstruct thread_info *start_kernel(void)\n{\n    v7m_init();\n\n    early_irq_init();\n    init_IRQ();\n\n    /* TODO: Early console */\n    __printk_init();\n\n    /* initialize the kernel's malloc */\n    kernel_heap_init(&__heap_start__, (size_t) &__heap_size__);\n\n    print_linker_sections();\n\n    /* initialize the physical memory allocator */\n    show_page_bitmap();  // init_pages();\n    kmem_cache_init();\n\n    /* initialize the scheduler internels */\n    sched_init();\n    /* select giving scheduling policy */\n    sched_select(SCHED_CLASS_BITMAP);\n\n    /* idle_thread is not added to the runqueue */\n    task_init(&idle_task);\n    thread_idle =\n        thread_create(do_idle, NULL, THREAD_PRIV_SUPERVISOR, 1024, &idle_task);\n    if (!thread_idle) {\n        printk(\"[!] Could not create system idle thread.\\n\");\n        return NULL;\n    }\n    printk(\"Created idle_thread at <%p>\\n\", thread_idle);\n\n    /* The main_thread is the user's entry-point to the system.  It is not\n     * added to the runqueue because it has been implicitly \"elected\" when\n     * start_kernel() returns.    */\n    task_init(&main_task);\n    struct thread_info *thread_main =\n        thread_create(main, NULL, THREAD_PRIV_USER, 1024, &main_task);\n    if (!thread_main) {\n        printk(\"[!] Could not create user main thread.\\n\");\n        return NULL;\n    }\n    printk(\"Created main_thread at <%p> with priority=%d\\n\", thread_main,\n           thread_main->ti_priority);\n\n    /* Reclaim the early-stack physical memory.  In the current context, no\n     * page allocation after this point are allowed.    */\n    printk(\"Reclaim early stack's physical memory (%d Bytes, order=%d).\\n\",\n           &__early_stack_start__ - &__early_stack_end__,\n           size_to_page_order(2048));\n    free_pages((unsigned long) &__early_stack_end__, size_to_page_order(2048));\n\n    tmpfs_init();\n    proc_init();\n    memdev_init();\n    mtdram_init(); /* create a test mtdram device */\n\n    /* do the platform-specific inits */\n    __platform_init();\n\n    init_softirq();\n\n    /* create /dev/ttyS0 */\n    serial_init();\n\n    printk(\"Kernel bootstrap done.\\n--\\n\");\n\n    return thread_main;\n}\n\nvoid *do_idle(__unused void *arg)\n{\n    for (;;)\n        __do_idle();\n}\n"
  },
  {
    "path": "kernel/mm/mm.c",
    "content": "#include <errno.h>\n#include <string.h>\n#include <sys/cdefs.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n\n#include <piko/sys/mman.h>\n\n#include <kernel/fs.h>\n#include <kernel/mm/page.h>\n\n#define M_ISANON(f) (((f) &MAP_ANONYMOUS) == MAP_ANONYMOUS)\n#define M_ISUNINIT(f) (((f) &MAP_UNINITIALIZED) == MAP_UNINITIALIZED)\n\nstatic void *map_anon(void *addr, size_t length, __unused int prot, int flags)\n{\n    int order;\n\n    order = size_to_page_order(length);\n    addr = alloc_pages(order);\n    if (!addr) {\n        printk(\"mmap: ENOMEM\\n\");\n        errno = ENOMEM;\n        return MAP_FAILED;\n    }\n    if (!M_ISUNINIT(flags))\n        memset(addr, 0, length);\n\n    return addr;\n}\n\nstatic void *map_file(__unused size_t length,\n                      __unused int prot,\n                      __unused int flags,\n                      int fd,\n                      off_t offset)\n{\n    void *addr;\n    struct file *file;\n\n    file = fd_to_file(fd);\n    if (!file) {\n        printk(\"mmap: BADF\\n\");\n        errno = EBADF;\n        return MAP_FAILED;\n    }\n    if (!S_ISREG(file->f_dentry->d_inode->i_mode)) {\n        printk(\"mmap: ACCESS\\n\");\n        errno = EACCES;\n        return MAP_FAILED;\n    }\n    if (vfs_mmap(file, offset, &addr)) {\n        printk(\"mmap: failed in romfs_map\\n\");\n        for (;;)\n            ;\n        return MAP_FAILED;\n    }\n\n    return addr;\n}\n\nvoid *sys_mmap(void *addr,\n               size_t length,\n               int prot,\n               int flags,\n               int fd,\n               off_t offset)\n{\n    if (!length) {\n        errno = EINVAL;\n        printk(\"mmap: length is 0\\n\");\n        return MAP_FAILED;\n    }\n    if (prot & PROT_NONE) {\n        errno = EACCES;\n        printk(\"mmap: PROT_NONE\\n\");\n        return MAP_FAILED;\n    }\n    if (M_ISANON(flags))\n        addr = map_anon(addr, length, prot, flags);\n    else\n        addr = map_file(length, prot, flags, fd, offset);\n\n    return addr;\n}\n\nint sys_munmap(__unused void *addr, __unused size_t length)\n{\n    /* Closing the file descriptor does not unmap the region. */\n    return 0;\n}\n"
  },
  {
    "path": "kernel/mm/page.c",
    "content": "#include <stddef.h>\n\n#include <kernel/bitops.h>\n#include <kernel/hash.h>\n#include <kernel/kernel.h>\n#include <kernel/log2.h>\n#include <kernel/mm/page.h>\n\n/* .pgmem section is 32KB:\n *      - 128 pages of 256B is a 16 bytes map\n *      -  64 pages of 512B is a  8 bytes map\n *      -  32 pages of 1KB is a  4 bytes map\n *      -  16 pages of 2KB is a  2 bytes map\n *\n * Last page is reserved by the early stack, and freed after system init.\n */\n\n/* 0 = allocated/undefined, 1 = free */\nstatic unsigned long *const page_bitmap[] = {\n    (unsigned long[]){0, 0, 0, 0}, (unsigned long[]){0, 0},\n    (unsigned long[]){0}, (unsigned long[]){0x7fff}};\n\nstatic const unsigned long page_bitmap_sz[] = {128, 64, 32, 16};\n\nlong size_to_page_order(unsigned long size)\n{\n    if (size <= 256)\n        return 0;\n    if (size <= 512)\n        return 1;\n    if (size <= 1024)\n        return 2;\n    if (size <= 2048)\n        return 3;\n\n    return -1;\n}\n\nstatic long find_first_free_page(unsigned long order)\n{\n    unsigned long page_idx =\n        find_first_bit(page_bitmap[order], page_bitmap_sz[order]);\n\n    if (page_idx >= page_bitmap_sz[order])\n        return -1;\n\n    return page_idx;\n}\n\nstatic void split_first_free_page(unsigned long order)\n{\n    unsigned long page_idx =\n        find_first_bit(page_bitmap[order], page_bitmap_sz[order]);\n\n    bitmap_clear_bit(page_bitmap[order], page_idx);\n    bitmap_set_bit(page_bitmap[order - 1], page_idx * 2);\n    bitmap_set_bit(page_bitmap[order - 1], page_idx * 2 + 1);\n}\n\nextern char __pgmem_start__;\n\nstatic void *page_idx_to_addr(unsigned long idx, unsigned long order)\n{\n    return &__pgmem_start__ + idx * (1 << (order + ilog2(MIN_PAGE_SIZE)));\n}\n\nvoid *alloc_pages(unsigned long order)\n{\n    unsigned long page_idx, o;\n\n    for (o = order; (o <= MAX_PAGE_ORDER) && (find_first_free_page(o) < 0); o++)\n        ;\n    if (o > MAX_PAGE_ORDER)\n        return NULL;\n    for (; o > order; o--)\n        split_first_free_page(o);\n    page_idx = find_first_bit(page_bitmap[order], page_bitmap_sz[order]);\n    bitmap_clear_bit(page_bitmap[order], page_idx);\n\n    /* printk(\"Returning address %p\\n\", page_idx_to_addr(page_idx, order)); */\n\n    return page_idx_to_addr(page_idx, order);\n}\n\nstatic inline unsigned long addr_to_page_idx(unsigned long addr,\n                                             unsigned long order)\n{\n    // FIXME: multiple zone to allocate from? use the zone's base address\n    // instead\n    return (addr - (unsigned long) &__pgmem_start__) >>\n           (order + ilog2(MIN_PAGE_SIZE));\n}\n\nstatic inline unsigned long get_buddy_index(unsigned long idx)\n{\n    return idx % 2 ? idx - 1 : idx + 1;\n}\n\n/* try to coalesce free buddies */\nvoid free_pages(unsigned long addr, unsigned long order)\n{\n    unsigned long page_idx, buddy_idx, mask;\n\n    for (; order < MAX_PAGE_ORDER; order++) {\n        page_idx = addr_to_page_idx(addr, order);\n        buddy_idx = get_buddy_index(page_idx);\n        if (bitmap_get_bit(page_bitmap[order], buddy_idx)) {\n            bitmap_clear_bit(page_bitmap[order], buddy_idx);\n            mask = ~((1 << (order + 1 + ilog2(MIN_PAGE_SIZE))) - 1);\n            addr &= mask;\n        } else {\n            bitmap_set_bit(page_bitmap[order], page_idx);\n            return;\n        }\n    }\n    bitmap_set_bit(page_bitmap[order], addr_to_page_idx(addr, order));\n}\n\nvoid show_page_bitmap(void)\n{\n    printk(\"Order  Bitmap\\n\");\n    for (int i = 0; i <= 3; i++) {\n        printk(\"    %d  \", i);\n        for (unsigned long j = 0; j < page_bitmap_sz[i]; j += BITS_PER_LONG)\n            printk(\"%08x  \", *(page_bitmap[i] + j / BITS_PER_LONG));\n        printk(\"\\n\");\n    }\n}\n\n/* Useful function to get a signature of memory fragmentation before and\n * after allocating/freeing memory. */\nunsigned long page_alloc_signature(void)\n{\n    unsigned long hash = hash_djb2((unsigned char *) page_bitmap[0], 4 * 4) +\n                         hash_djb2((unsigned char *) page_bitmap[1], 2 * 4) +\n                         hash_djb2((unsigned char *) page_bitmap[2], 1 * 4) +\n                         hash_djb2((unsigned char *) page_bitmap[3], 1 * 4);\n\n    return hash;\n}\n"
  },
  {
    "path": "kernel/mm/slab.c",
    "content": "#include <stdio.h>\n#include <string.h>\n#include <sys/cdefs.h>\n\n#include <kernel/bitops.h>\n//#include <kernel/kernel.h>\n#include <kernel/mm/page.h>\n#include <kernel/mm/slab.h>\n\n#include \"linux/list.h\"\n\n#define OBJECTS_PER_SLAB(objsize) \\\n    ((CACHE_PAGE_SIZE - sizeof(struct slab)) / (objsize))\n\nstatic LIST_HEAD(caches);\n\nstatic struct kmem_cache cache_caches = {\n    .objsize = sizeof(struct kmem_cache),\n    .objnum = OBJECTS_PER_SLAB(sizeof(struct kmem_cache)),\n    .name = \"cache-cache\",\n    .slabs_free = LIST_HEAD_INIT(cache_caches.slabs_free),\n    .slabs_partial = LIST_HEAD_INIT(cache_caches.slabs_partial),\n    .slabs_full = LIST_HEAD_INIT(cache_caches.slabs_full),\n    .alloc_succeed = 0,\n    .alloc_fail = 0,\n    .opts = CACHE_OPT_NONE,\n};\n\nstatic inline struct slab *get_slab_from_obj(void *obj, size_t page_size)\n{\n    return (struct slab *) align((unsigned long) obj, page_size);\n}\n\nstatic inline int obj_index_in_slab(void *obj, struct kmem_cache *cache)\n{\n    return (((unsigned long) obj & ((1 << 8) - 1)) - sizeof(struct slab)) /\n           cache->objsize;\n}\n\nstruct kmem_cache *kmem_cache_create(const char *name,\n                                     size_t size,\n                                     __unused size_t align,\n                                     __unused unsigned long flags,\n                                     __unused void (*ctor)(void *))\n{\n    struct kmem_cache *cache;\n\n    if (size < 8) {\n        printk(\"error: Use cache for objects with size >= 8 bytes\\n\");\n        return NULL;\n    }\n    if (size > (256 - sizeof(struct slab)) / 2) {\n        printk(\"error: Object size is too big\\n\");\n        return NULL;\n    }\n\n    cache = kmem_cache_alloc(&cache_caches, 0);\n    if (!cache)\n        return NULL;\n    cache->objsize = size;\n    cache->objnum = OBJECTS_PER_SLAB(size);\n    cache->opts = CACHE_OPT_NONE;\n    strncpy(cache->name, name, CACHE_NAMELEN);\n    INIT_LIST_HEAD(&cache->slabs_free);\n    INIT_LIST_HEAD(&cache->slabs_partial);\n    INIT_LIST_HEAD(&cache->slabs_full);\n    list_add(&cache->list, &caches);\n\n    return cache;\n}\n\nstatic struct slab *kmem_cache_grow(struct kmem_cache *cache)\n{\n    struct slab *slab = alloc_pages(0);\n\n    if (!slab)\n        return NULL;\n    slab->free_bitmap[0] = 0;\n    slab->free_objects = cache->objnum;\n    list_add(&slab->list, &cache->slabs_free);\n\n    return slab;\n}\n\nvoid *kmem_cache_alloc(struct kmem_cache *cache, __unused unsigned long flags)\n{\n    struct slab *slab = NULL;\n    void *mem;\n\n    if (list_empty(&cache->slabs_partial)) {\n        if (list_empty(&cache->slabs_free)) {\n            cache->alloc_fail++;\n            slab = kmem_cache_grow(cache);\n            if (!slab)\n                return NULL;\n        } else {\n            cache->alloc_succeed++;\n            slab = list_first_entry(&cache->slabs_free, struct slab, list);\n        }\n        list_move(&slab->list, &cache->slabs_partial);\n    } else {\n        cache->alloc_succeed++;\n        slab = list_first_entry(&cache->slabs_partial, struct slab, list);\n    }\n\n    int bit = find_first_zero_bit(slab->free_bitmap, cache->objnum);\n    bitmap_set_bit(slab->free_bitmap, bit);\n    mem = slab->data + bit * cache->objsize;\n    slab->free_objects--;\n    if (!slab->free_objects)\n        list_move(&slab->list, &cache->slabs_full);\n\n    return mem;\n}\n\nstatic int slab_destroy(__unused struct kmem_cache *cache, struct slab *slab)\n{\n    list_del(&slab->list);\n    free_pages((unsigned long) slab, 0);\n\n    return 0;\n}\n\nvoid kmem_cache_free(struct kmem_cache *cache, void *obj)\n{\n    struct slab *slab;\n    int bit;\n\n    slab = get_slab_from_obj(obj, 256);\n    bit = obj_index_in_slab(obj, cache);\n    bitmap_clear_bit(slab->free_bitmap, bit);\n    slab->free_objects++;\n    if (slab->free_objects == cache->objnum)\n        slab_destroy(cache, slab);\n    else if (slab->free_objects == 1)\n        list_move(&slab->list, &cache->slabs_partial);\n}\n\nvoid kmem_cache_init(void)\n{\n    list_add(&cache_caches.list, &caches);\n    kmem_cache_grow(&cache_caches);\n}\n"
  },
  {
    "path": "kernel/mutex.c",
    "content": "#include <kernel/kernel.h>\n#include <kernel/mutex.h>\n#include <kernel/sched.h>\n#include <kernel/thread.h>\n\n#include \"linux/list.h\"\n\nstatic LIST_HEAD(mutex_head);\n\n/* The thread owns the mutex on return. We also check the case when the lock\n * has been released between the test of the mutex and this syscall. */\nint sys_pthread_mutex_lock(kernel_mutex_t *mutex)\n{\n    mutex->val++;\n    if (!mutex->val)\n        return 0;\n    CURRENT_THREAD_INFO(curr_thread);\n    curr_thread->ti_private = mutex;\n    curr_thread->ti_state = THREAD_STATE_BLOCKED;\n    list_add_tail(&curr_thread->ti_q, &mutex_head);\n    sched_elect(SCHED_OPT_NONE);\n\n    return 0;\n}\n\nstatic struct thread_info *find_first_blocking_thread(kernel_mutex_t *mutex)\n{\n    struct thread_info *thread;\n\n    list_for_each_entry(thread, &mutex_head, ti_q)\n    {\n        if (thread->ti_private == mutex)\n            return thread;\n    }\n\n    return NULL;\n}\n\nint sys_pthread_mutex_unlock(kernel_mutex_t *mutex)\n{\n    struct thread_info *waiter = NULL;\n\n    mutex->val--;\n    if (mutex->val >= 0) {\n        waiter = find_first_blocking_thread(mutex);\n        if (!waiter) {\n            printk(\"[mutex_unlock] No blocking threads for mutex=<%p>\\n\",\n                   mutex);\n            return -1;\n        }\n        list_del(&waiter->ti_q);\n        sched_enqueue(waiter);\n    }\n    CURRENT_THREAD_INFO(curr_thread);\n    if (curr_thread->ti_state == THREAD_STATE_BLOCKED) {\n        sched_elect(SCHED_OPT_NONE);\n    } else if (waiter && (curr_thread->ti_priority <= waiter->ti_priority)) {\n        sched_enqueue(curr_thread);\n        sched_elect(SCHED_OPT_NONE);\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "kernel/printk.c",
    "content": "#include <stdarg.h>\n#include <stdio.h>\n#include <ucontext.h>\n\n#define VSNPRINTF_BUF_SIZE 256\n\nstatic ucontext_t printk_context;\nstatic ucontext_t vsnprintf_context = {.uc_link = &printk_context};\nstatic unsigned int ctx_stack[128];\nstatic char vsnprintf_buf[VSNPRINTF_BUF_SIZE];\nstatic int retval;\n\n/* not thread-safe, not reentrant */\nstatic void co_vsnprintf(const char *format, va_list ap)\n{\n    retval = vsnprintf(vsnprintf_buf, VSNPRINTF_BUF_SIZE, format, ap);\n}\n\n#include <serial.h>\n#include \"platform.h\"\n\nvoid __printk_init(void)\n{\n    uart_init();\n}\n\nvoid __printk_putchar(char c)\n{\n    if (c == '\\n')\n        __printk_putchar('\\r');\n\n    while (!(USARTx->SR & USART_SR_TXE))\n        ;\n    USARTx->DR = (0xff) & c;\n}\n\nint printk(const char *format, ...)\n{\n    /*FIXME: should be interruptable*/\n    __disable_irq();\n    va_list ap;\n\n    va_start(ap, format);\n    vsnprintf_context.uc_stack.ss_sp = &ctx_stack[128];\n    makecontext(&vsnprintf_context, co_vsnprintf, 2, format, ap);\n    swapcontext(&printk_context, &vsnprintf_context);\n    for (char *c = vsnprintf_buf; *c != '\\0'; c++)\n        __printk_putchar(*c);\n    va_end(ap);\n\n    /*FIXME: should be interruptable*/\n    __enable_irq();\n    return retval;\n}\n"
  },
  {
    "path": "kernel/resource.c",
    "content": "#include <piko/sys/resource.h>\n\n// clang-format off\nstatic struct rlimit rlimits[] = {\n    { .rlim_cur = 1024, .rlim_max = 1024 } /* RLIMIT_STACK */\n};\n\nint sys_getrlimit(int resource, struct rlimit *rlim)\n{\n    rlim->rlim_cur = rlimits[resource].rlim_cur;\n    rlim->rlim_max = rlimits[resource].rlim_max;\n\n    return 0;\n}\n\nint sys_setrlimit(int resource, const struct rlimit *rlim)\n{\n    rlimits[resource].rlim_cur = rlim->rlim_cur;\n    rlimits[resource].rlim_max = rlim->rlim_max;\n\n    return 0;\n}\n"
  },
  {
    "path": "kernel/sched/bitmap.c",
    "content": "#include <kernel/bitops.h>\n#include <kernel/sched.h>\n#include <kernel/thread.h>\n\n#include \"linux/list.h\"\n#include \"kernel.h\"\n#include \"kernel/bitmap.h\"\n\nextern struct thread_info *thread_idle;\n\nstatic struct bitmap_struct _active, _expire;\n\nstatic struct {\n    struct bitmap_struct *active;\n    struct bitmap_struct *expire;\n} sched_struct = {\n    .active = &_active,\n    .expire = &_expire,\n};\n\nstatic int sched_bitmap_init(void)\n{\n    INIT_BITMAP(sched_struct.active);\n    INIT_BITMAP(sched_struct.expire);\n\n    return 0;\n}\n\nstatic struct thread_info *find_next_thread(struct bitmap_struct *bm)\n{\n    int max_prio = find_first_bit(&bm->map, 32);\n\n    /* all runqueues are empty, return the idle_thread */\n    if (max_prio == 32)\n        return thread_idle;  // idle_thread\n\n    return bitmap_first_entry(bm, max_prio, struct thread_info, ti_q);\n}\n\nstatic int thread_enqueue(struct thread_info *thread, struct bitmap_struct *bm)\n{\n    bitmap_enqueue(&thread->ti_q, thread->ti_priority, bm);\n\n    return 0;\n}\n\nstatic int sched_bitmap_enqueue(struct thread_info *thread)\n{\n    return thread_enqueue(thread, sched_struct.active);\n}\n\nstatic int thread_dequeue(struct thread_info *thread, struct bitmap_struct *bm)\n{\n    CURRENT_THREAD_INFO(current);\n\n    /* active thread is not in the runqueue */\n    if (thread == current)\n        return -1;\n\n    bitmap_queue_del(&thread->ti_q, thread->ti_priority, bm);\n\n    return 0;\n}\n\nstatic int sched_bitmap_dequeue(struct thread_info *thread)\n{\n    int state = thread->ti_state;\n    if (state == THREAD_STATE_ACTIVED)\n        return thread_dequeue(thread, sched_struct.active);\n    else if (state == THREAD_STATE_EXPIRED)\n        return thread_dequeue(thread, sched_struct.expire);\n    else\n        return -1;\n}\n\nstatic int sched_bitmap_elect(int flags)\n{\n    CURRENT_THREAD_INFO(current);\n    struct thread_info *next;\n\n    next = find_next_thread(sched_struct.active);\n\n    // check each thread timeslice in active queue\n    // if necessary swap active and expire queue\n    if (next == thread_idle &&\n        find_next_thread(sched_struct.expire) != thread_idle) {\n        SWAP(sched_struct.active, sched_struct.expire);\n        swap_sched_state_map();\n        next = find_next_thread(sched_struct.active);\n    }\n\n\n    if (next != thread_idle) {  // idle_thread\n        list_del(&next->ti_q);\n        if (list_empty(&sched_struct.active->queue[next->ti_priority]))\n            bitmap_clear_bit(&sched_struct.active->map, next->ti_priority);\n    }\n\n    if (flags == SCHED_OPT_RESTORE_ONLY)\n        thread_restore(next);  // switch_to_restore_only\n    else {\n        if (flags == SCHED_OPT_TICK && current != thread_idle) {\n            thread_enqueue(current, sched_struct.expire);\n            current->ti_state = THREAD_STATE_EXPIRED;\n        }\n        if (next == current)\n            return 0;\n        switch_to(next, current);\n    }\n\n    return 0;\n}\n\n// clang-format off\nstatic struct sched sched_bitmap = {\n    .class_type = SCHED_CLASS_BITMAP,\n    .init = sched_bitmap_init,\n    .enqueue = sched_bitmap_enqueue,\n    .dequeue = sched_bitmap_dequeue,\n    .elect = sched_bitmap_elect,\n};\n// clang-format on\n\nHOOK_SCHED_CLASS(bitmap, &sched_bitmap)\n"
  },
  {
    "path": "kernel/sched/rr.c",
    "content": "#include <kernel/sched.h>\n#include <kernel/thread.h>\n\n#include \"linux/list.h\"\n#include \"kernel.h\"\n\nstatic LIST_HEAD(rr_runq);\nextern struct thread_info *thread_idle;\n\nint sched_rr_init(void)\n{\n    return 0;\n}\n\nstatic struct thread_info *find_next_thread(struct thread_info *thread)\n{\n    if (list_is_last(&thread->ti_q, &rr_runq))\n        return list_first_entry(&rr_runq, struct thread_info, ti_q);\n\n    return list_next_entry(thread, ti_q);\n}\n\nint sched_rr_enqueue(struct thread_info *thread)\n{\n    list_add(&thread->ti_q, &rr_runq);\n\n    return 0;\n}\n\nint sched_rr_dequeue(struct thread_info *thread)\n{\n    CURRENT_THREAD_INFO(current);\n\n    if (current == thread) {\n        struct thread_info *next = thread_idle;\n        if (!list_is_singular(&rr_runq)) {\n            next = find_next_thread(current);\n        }\n        list_del(&thread->ti_q);\n        thread_restore(next);  // FIXME: rename to switch_to_no_save\n    } else {\n        list_del(&thread->ti_q);\n    }\n\n    return 0;\n}\n\n/* This function is used when the runqueue has been modified externally, and it\n   is not possible to fetch the next thread.    */\nstatic int sched_rr_elect_reset(void)\n{\n    CURRENT_THREAD_INFO(current);\n    struct thread_info *next = thread_idle;\n\n    if (!list_empty(&rr_runq))\n        next = list_first_entry(&rr_runq, struct thread_info, ti_q);\n    switch_to(next, current);\n\n    return 0;\n}\n\nint sched_rr_elect(int switch_type)\n{\n    CURRENT_THREAD_INFO(current);\n    struct thread_info *next;\n\n    if (switch_type & SCHED_OPT_RESET)\n        return sched_rr_elect_reset();\n\n    if (list_empty(&rr_runq)) {\n        // go to thread idle.\n        next = thread_idle;\n    } else {\n        next = find_next_thread(current);\n    }\n\n    /* keep running the previous thread */\n    if (next == current)\n        return -1;\n\n    /* Leave _current_ thread for now. The _current_ thread will be elected\n       again after _next_ thread has run. Inform the caller function (in\n       _current_ context) that the thread gently gave way.    */\n    switch_to(next, current);\n\n    return 0;\n}\n\n// clang-format off\nstatic struct sched sched_rr = {\n    .class_type = SCHED_CLASS_RR,\n    .init = sched_rr_init,\n    .enqueue = sched_rr_enqueue,\n    .dequeue = sched_rr_dequeue,\n    .elect = sched_rr_elect,\n};\n// clang-format on\n\nHOOK_SCHED_CLASS(RR, &sched_rr)\n"
  },
  {
    "path": "kernel/sched.c",
    "content": "#include <kernel/sched.h>\n#include <kernel/thread.h>\n\nextern unsigned long __sched_classes_start__;\nextern unsigned long __sched_classes_end__;\n\n/* static vars to repesent sched classes list */\nstatic struct sched **sched_classes = \n    (struct sched **) &__sched_classes_start__;\nstatic struct sched **sched_classes_end = \n    (struct sched **) &__sched_classes_end__;\n\nstatic struct sched *sched;\n\nint sched_init()\n{\n    int ret = 0;\n\n    /* Initialize each scheduler class by traversing hooks */\n    for (struct sched **c = sched_classes;\n            c < sched_classes_end; c++) {\n        struct sched *class = *c;\n        ret |= class->init();\n    }\n\n    return ret;\n}\n\nint sched_select(sched_class_t sched_type)\n{\n    int ret = -1;\n    struct sched *class = NULL;\n    \n    /* Examine specified sched class in hooks or not */\n    for (struct sched **c = sched_classes;\n            c < sched_classes_end; c++)\n        if (sched_type == (*c)->class_type)\n            class = *c;\n    \n    if (class) {\n        sched = class;\n        ret = 0;\n    }\n\n    return ret;\n}\n\nint sched_enqueue(struct thread_info *thread)\n{\n    thread->ti_state = THREAD_STATE_ACTIVED;\n\n    return sched->enqueue(thread);\n}\n\nint sched_dequeue(struct thread_info *thread)\n{\n    return sched->dequeue(thread);\n}\n\nint sched_elect(int flags)\n{\n    int r;\n    CURRENT_THREAD_INFO(cur_thread);\n\n    KERNEL_STACK_CHECKING;\n\n    r = sched->elect(flags);\n    cur_thread->ti_state = THREAD_STATE_RUNNING;\n\n    return r;\n}\n"
  },
  {
    "path": "kernel/signal.c",
    "content": "#include <errno.h>\n#include <signal.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/types.h>\n\n#include <arch/v7m-helper.h>\n\n#include <kernel/bitops.h>\n#include <kernel/errno-base.h>\n#include <kernel/signal.h>\n#include <kernel/task.h>\n#include <kernel/thread.h>\n\n#include \"kernel.h\"\n#include \"platform.h\"\n\nextern void return_from_sighandler(void);\nextern void return_from_sigaction(void);\n\nstatic void *v7m_alloca_thread_context(struct thread_info *tip, size_t len)\n{\n    tip->ti_mach.mi_psp -= len;\n\n    return (void *) tip->ti_mach.mi_psp;\n}\n\nstatic void stage_sighandler(struct sigaction *sigaction)\n{\n    CURRENT_THREAD_INFO(curr_thread);\n    struct thread_context_regs *ctx;\n\n    /* update current thread SP_process */\n    curr_thread->ti_mach.mi_psp = __get_PSP();\n\n    /* this is the exception stacked-context */\n    ctx = (struct thread_context_regs *) curr_thread->ti_mach.mi_psp;\n\n    /* return value of syscall, cannot fail after this point */\n    ctx->r0_r3__r12[0] = 0;\n\n    /* the sigaction context will be poped by cpu on exception return */\n    v7m_alloca_thread_context(curr_thread, sizeof(struct thread_context_regs));\n\n    /* build the sigaction trampoline */\n    ctx = (struct thread_context_regs *) curr_thread->ti_mach.mi_psp;\n    /* #ifdef SECURE_KERNEL */\n    ctx->r0_r3__r12[1] = 0;\n    ctx->r0_r3__r12[2] = 0;\n    ctx->r0_r3__r12[3] = 0;\n    ctx->r0_r3__r12[4] = 0;\n    /* #endif */\n    ctx->lr = (u32) v7m_set_thumb_bit(return_from_sighandler);\n    ctx->ret_addr = (u32) v7m_clear_thumb_bit(sigaction->sa_handler);\n    ctx->xpsr = xPSR_T_Msk;\n\n    /* update current thread SP_process */\n    __set_PSP(curr_thread->ti_mach.mi_psp);\n}\n\nstatic void stage_sigaction(const struct sigaction *sigaction,\n                            int sig,\n                            union sigval value)\n{\n    CURRENT_THREAD_INFO(curr_thread);\n    struct thread_context_regs *ctx;\n\n    /* update current thread SP_process */\n    curr_thread->ti_mach.mi_psp = __get_PSP();\n\n    /* this is the exception stacked-context */\n    ctx = (struct thread_context_regs *) curr_thread->ti_mach.mi_psp;\n\n    /* return value of syscall, cannot fail after this point */\n    ctx->r0_r3__r12[0] = 0;\n\n    /* The siginfo_t struct is allocated on thread's stack; that memory\n     * will be reclaimed during return_from_sigaction. */\n    siginfo_t *siginfo_ptr =\n        v7m_alloca_thread_context(curr_thread, sizeof(siginfo_t));\n    siginfo_ptr->si_signo = sig;\n    siginfo_ptr->si_value = value;\n    siginfo_ptr->si_pid = curr_thread->ti_id;\n\n    /* the sigaction context will be poped by cpu on exception return */\n    v7m_alloca_thread_context(curr_thread, sizeof(struct thread_context_regs));\n\n    /* build a sigaction trampoline */\n    ctx = (struct thread_context_regs *) curr_thread->ti_mach.mi_psp;\n    ctx->r0_r3__r12[1] = (u32) siginfo_ptr;\n    ctx->r0_r3__r12[2] = 0; /* ucontext_t *, but commonly unused */\n    ctx->r0_r3__r12[3] = 0;\n    ctx->r0_r3__r12[4] = 0;\n    ctx->lr = (u32) v7m_set_thumb_bit(return_from_sigaction);\n    ctx->ret_addr = (u32) v7m_clear_thumb_bit(sigaction->sa_sigaction);\n    ctx->xpsr = xPSR_T_Msk;\n\n    /* update current thread SP_process */\n    __set_PSP(curr_thread->ti_mach.mi_psp);\n}\n\nvoid do_sigevent(const struct sigevent *sigevent, struct thread_info *thread)\n{\n    CURRENT_THREAD_INFO(curr_thread);\n    struct thread_context_regs *ctx;\n\n    // if (sigevent->sigev_notify == SIGEV_THREAD) {\n\n    /* update current thread SP_process */\n    if (thread == curr_thread)\n        thread->ti_mach.mi_psp = __get_PSP();\n\n    /* the sigevent context will be poped by cpu on exception return */\n    v7m_alloca_thread_context(thread, sizeof(struct thread_context_regs));\n\n    /* build a sigevent trampoline */\n    ctx = (struct thread_context_regs *) thread->ti_mach.mi_psp;\n    ctx->r0_r3__r12[0] = sigevent->sigev_value.sival_int;\n    ctx->r0_r3__r12[1] = 0;\n    ctx->r0_r3__r12[2] = 0;\n    ctx->r0_r3__r12[3] = 0;\n    ctx->r0_r3__r12[4] = 0;\n    ctx->lr = (u32) v7m_set_thumb_bit(return_from_sighandler);\n    ctx->ret_addr = (u32) v7m_clear_thumb_bit(sigevent->sigev_notify_function);\n    ctx->xpsr = xPSR_T_Msk;\n\n    /* update current thread SP_process */\n    if (thread == curr_thread)\n        __set_PSP(thread->ti_mach.mi_psp);\n}\n\nstatic struct sigaction *find_sigaction_by_sig(__unused pid_t pid, int sig)\n{\n    /* FIXME: consider multi-tasking environment */\n    struct signal_info *signal;\n    CURRENT_TASK_INFO(curr_task);\n\n    list_for_each_entry(signal, &curr_task->signal_head, list)\n    {\n        if (signal->signo == sig)\n            return &signal->act_storage;\n    }\n\n    return NULL;\n}\n\nint sys_sigaction(int signo,\n                  const struct sigaction *restrict act,\n                  struct sigaction *restrict oldact)\n{\n    if ((signo == SIGKILL) || (signo == SIGSTOP)) {\n        errno = EINVAL;\n        return -1;\n    }\n    if (!act) {\n        errno = EFAULT;\n        return -1;\n    }\n\n    if (oldact) {\n        struct sigaction *oact = find_sigaction_by_sig(0, signo);\n        if (oact != NULL)\n            memcpy(oldact, oact, sizeof(struct sigaction));\n    }\n\n    struct signal_info *signal = malloc(sizeof(struct signal_info));\n    if (!signal) {\n        errno = ENOMEM;\n        return -1;\n    }\n\n    signal->signo = signo;\n    CURRENT_TASK_INFO(curr_task);\n    list_add(&signal->list, &curr_task->signal_head);\n    memcpy(&signal->act_storage, act, sizeof(struct sigaction));\n\n    return 0;\n}\n\n/* enabled signal mask */\nstatic unsigned long supported_signal_mask =\n    (1 << SIGKILL) | (1 << SIGUSR1) | (1 << SIGUSR2) | (1 << SIGSTOP);\n\nstatic int is_signal_supported(int sig)\n{\n    if (sig > SIGMAX)\n        return 0;\n    return bitmap_get_bit(&supported_signal_mask, sig);\n}\n\n/* How signal works?\n *\n * - A fake exception return context is allocated to the user thread stack.\n * - This context is a trampoline to the signal handler.\n * - When the syscall handler returns, the return value is pushed to the user\n *   stack in r0.  For signal handling, r0 must contain the first parameter to\n *   the signal handler function.  The actual return code of the syscall must\n *   be written into the auto-pushed stack context.  The staging functions\n *   handle the update of the error code in the cpu-pushed stackframe.\n */\n\nint sys_kill(__unused pid_t pid, int sig)\n{\n    if (!is_signal_supported(sig))\n        return -EINVAL;\n\n    struct sigaction *act = find_sigaction_by_sig(0, sig);\n    if (!act)\n        return -EINVAL;\n\n    if (act->sa_flags & SA_SIGINFO)\n        stage_sigaction(act, sig, (union sigval){.sival_int = 0});\n    else\n        stage_sighandler(act);\n\n    return sig;\n}\n"
  },
  {
    "path": "kernel/softirq.c",
    "content": "#include <kernel/bitops.h>\n#include <kernel/softirq.h>\n#include <kernel/task.h>\n#include <kernel/thread.h>\n#include <kernel/sched.h>\n\n#include <stdlib.h>\n\n#include \"kernel/bitmap.h\"\n\nenum {\n    PRIO_TASKLET = 0,\n    NR_SOFTIRQS,\n};\n\nconst char *const softirq_to_name[NR_SOFTIRQS] = {\"PRIO_TASKLET\"};\n\nstatic struct softirq_action softirq_vec[NR_SOFTIRQS];\n\n/* Priority-based tasklet */\nstatic struct bitmap_struct prio_tasklet;\n\n/* softirq_daemon */\nstatic struct task_info softirq_daemon;\nstatic struct thread_info *thread_softirqd;\n\nint open_softirq(unsigned int nr, int (*action)(struct softirq_action *))\n{\n    if (nr >= NR_SOFTIRQS)\n        return -1;\n\n    softirq_vec[nr].action = action;\n\n    return 0;\n}\n\n/* XXX: represent softirqd whether in runq or not */\nstatic volatile int softirq_run = 0;\nint raise_softirq(unsigned int nr)\n{\n    if (nr >= NR_SOFTIRQS)\n        return -1;\n\n    /* XXX: we ignore do_softirq routine as Linux PREEMPT_RT */\n    if (!softirq_run) {\n        sched_enqueue(thread_softirqd);\n        softirq_run = 1;\n    }\n\n    return 0;\n}\n\nstruct tasklet_struct *tasklet_init(void(*func), void *data, unsigned long prio)\n{\n    struct tasklet_struct *tsk =\n        (struct tasklet_struct *) malloc(sizeof(struct tasklet_struct));\n    if (!tsk)\n        return NULL;\n\n    tsk->prio = prio;\n    tsk->func = func;\n    tsk->data = data;\n    INIT_LIST_HEAD(&tsk->tsk_q);\n\n    return tsk;\n}\n\nint tasklet_schedule(struct tasklet_struct *task)\n{\n    if (!task || task->prio > PRIO_TASKLET_MINPRIO)\n        return -1;\n\n    //\tlist_add_tail(&task->tsk_q, &prio_tasklet.runq[task->prio]);\n    //  bitmap_set_bit(&prio_tasklet.bitmap, task->prio);\n    bitmap_enqueue(&task->tsk_q, task->prio, &prio_tasklet);\n\n    return raise_softirq(PRIO_TASKLET);\n}\n\nstatic int tasklet_action(struct softirq_action __unused *a)\n{\n    struct tasklet_struct *tsk = NULL;\n\n    while (1) {\n        if (prio_tasklet.map) {\n            int max_prio = find_first_bit(&prio_tasklet.map, 32);\n\n            // tsk = list_first_entry(&prio_tasklet.runq[max_prio], struct\n            // tasklet_struct, tsk_q);\n            tsk = bitmap_first_entry((&prio_tasklet), max_prio,\n                                     struct tasklet_struct, tsk_q);\n\n            // list_del(&tsk->tsk_q);\n            // if (list_empty(&prio_tasklet.runq[prio]))\n            //    bitmap_clear_bit(&prio_tasklet.bitmap, prio);\n            bitmap_queue_del(&tsk->tsk_q, max_prio, &prio_tasklet);\n\n            if (!tsk->func) {\n                printk(\n                    \"[!] prio_taskletd: prio_tasklet function is NULL ptr.\\n\");\n                break;\n            }\n\n            tsk->func(tsk->data);\n        } else\n            return 0;\n    }\n\n    return -1;\n}\n\nstatic void init_softirq_entry()\n{\n    return;\n}\n\nextern void sched_yield();\n\nstatic void *softirqd(__unused void *arg)\n{\n    int ret = -1;\n\n    while (1) {\n        ret = tasklet_action(NULL);\n\n        if (ret == 0)\n            sched_yield();\n        else\n            break;\n    }\n    printk(\"[!] softirqd thread should not return.\\n\");\n\n    softirq_run = 0;\n    return NULL;\n}\n\nint init_softirq(void)\n{\n    /* initialize softirq vector */\n    open_softirq(PRIO_TASKLET, tasklet_action);\n\n    /* initialize priority tasklet obj */\n    INIT_BITMAP(&prio_tasklet);\n\n    // FIXME: no arg for priority in thread_create()\n    /* initialize softirq daemon thread */\n    task_init(&softirq_daemon);\n    thread_softirqd =\n        thread_create(softirqd, NULL, THREAD_PRIV_USER, 1024, &softirq_daemon);\n    if (!thread_softirqd) {\n        printk(\"[!] Could not create softirqd thread.\\n\");\n        return -1;\n    }\n    thread_set_priority(thread_softirqd, PRI_MAX);\n\n    /* initialize softirq entries */\n    init_softirq_entry();\n\n    return 0;\n}\n"
  },
  {
    "path": "kernel/task.c",
    "content": "#include <stdlib.h>\n\n#include <kernel/task.h>\n#include <kernel/thread.h>\n\n#include \"linux/list.h\"\n\nstatic LIST_HEAD(task_head);\n\nstatic pid_t alloc_pid()\n{\n    static pid_t pid = 7000;\n    pid_t retpid;\n\n    retpid = pid;\n    pid++;\n\n    return retpid;\n}\n\nstruct task_info *task_init(struct task_info *task)\n{\n    task->pid = alloc_pid();\n    task->filemap = 0;\n    INIT_LIST_HEAD(&task->thread_head);\n    INIT_LIST_HEAD(&task->signal_head);\n    list_add(&task->list, &task_head);\n\n    return task;\n}\n\nvoid task_exit(struct task_info *task)\n{\n    // this is called after last thread has exited, or when the\n    // task is killed\n    list_del(&task->list);\n    free(task);\n}\n\nstruct task_info *current_task_info(void)\n{\n    CURRENT_THREAD_INFO(curr_thread);\n\n    return curr_thread->ti_task;\n}\n\nint sys_getpid(void)\n{\n    CURRENT_TASK_INFO(curr_task);\n\n    return curr_task->pid;\n}\n"
  },
  {
    "path": "kernel/thread.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/param.h>\n#include <sys/types.h>\n#include <ucontext.h>\n\n#include <piko/sys/resource.h>\n\n#include <arch/v7m-helper.h>\n\n#include <kernel/errno-base.h>\n#include <kernel/mm/page.h>\n#include <kernel/sched.h>\n#include <kernel/thread.h>\n#include <kernel/task.h>\n#include <kernel/types.h>\n\n#include \"utils.h\"\n#include \"platform.h\"\n\nstatic struct kernel_context_regs *alloc_interrupt_stack(void)\n{\n    char *memp;\n    struct kernel_context_regs *kcr;\n\n    memp = alloc_pages(size_to_page_order(INTR_STACK_SIZE));\n    if (!memp)\n        return NULL;\n    kcr = (struct kernel_context_regs *) (memp + INTR_STACK_SIZE -\n                                          sizeof(struct kernel_context_regs));\n    memset(kcr->r4_r12, 0, 9 * sizeof(u32));\n    kcr->lr = V7M_EXC_RETURN_THREAD_PROCESS;\n\n    return kcr;\n}\n\n/* new thread's LR loaded with pthread_exit address */\nvoid pthread_exit(void *retval);\n\n// XXX: Detecting a stack-overflow on v7M: #memf && (SP_Process == MMFAR)\nstatic struct thread_context_regs *\nalloc_thread_stack(void *(*start_routine)(void *), void *arg, size_t stacksize)\n{\n    char *memp;\n    struct thread_context_regs *tcr;\n\n    memp = alloc_pages(size_to_page_order(stacksize));\n    if (!memp)\n        return NULL;\n    tcr = (struct thread_context_regs *) (memp + stacksize -\n                                          sizeof(struct thread_context_regs));\n    tcr->r0_r3__r12[0] = (u32) arg;\n    memset(&tcr->r0_r3__r12[1], 0, 4 * sizeof(u32));\n    tcr->lr = (u32) pthread_exit;  // FIXME: Should libc be dynamically loaded?\n    tcr->ret_addr = (u32) v7m_clear_thumb_bit(start_routine);\n    tcr->xpsr = xPSR_T_Msk;\n\n    return tcr;\n}\n\nstruct thread_info *thread_create(void *(*start_routine)(void *),\n                                  void *arg,\n                                  enum thread_privilege priv,\n                                  size_t stacksize,\n                                  struct task_info *task)\n{\n    struct thread_info *thread;\n    struct kernel_context_regs *kcr;\n    struct thread_context_regs *tcr;\n    static int thread_count = 0;\n\n    kcr = alloc_interrupt_stack();\n    if (!kcr)\n        return NULL;\n    tcr = alloc_thread_stack(start_routine, arg, stacksize);\n    thread = THREAD_INFO(kcr);\n    if (!tcr) {\n        free(thread);\n        return NULL;\n    }\n    thread->ti_mach.mi_psp = (u32) tcr;\n    thread->ti_mach.mi_msp = (u32) kcr;\n    thread->ti_mach.mi_priv = priv;\n    thread->ti_stacksize = stacksize;\n    thread->ti_id = thread_count++;\n    thread->ti_task = task;\n    thread->ti_joinable = false;\n    thread->ti_joining = NULL;\n    thread->ti_detached = false;\n    thread->ti_priority = PRI_MIN;\n    thread->ti_state = THREAD_STATE_NEW;\n#ifdef CONFIG_KERNEL_STACK_CHECKING\n    thread->ti_canary[0] = THREAD_CANARY0;\n    thread->ti_canary[1] = THREAD_CANARY1;\n#endif\n    list_add(&thread->ti_list, &task->thread_head);\n\n    return thread;\n}\n\nint thread_yield(void)\n{\n    // FIXME: elect iff there is a higher-priority thread ready to run\n    CURRENT_THREAD_INFO(curr_thread);\n    sched_enqueue(curr_thread);\n\n    return sched_elect(SCHED_OPT_NONE);\n}\n\nint thread_self(void)\n{\n    CURRENT_THREAD_INFO(curr_thread);\n\n    return curr_thread->ti_id;\n}\n\nvoid thread_exit(void *retval)\n{\n    CURRENT_THREAD_INFO(curr_thread);\n\n    /* free thread stack memory */\n    free_pages(align(curr_thread->ti_mach.mi_psp, curr_thread->ti_stacksize),\n               size_to_page_order(curr_thread->ti_stacksize));\n\n    if (curr_thread->ti_detached == false) {\n        curr_thread->ti_retval = retval;\n        if (curr_thread->ti_joining)\n            sched_enqueue(curr_thread->ti_joining);\n        else\n            curr_thread->ti_joinable = true;\n    } else {\n        /* We are freeing the stack we are running on, no kernel preemption\n         * is allowed until we call sched_elect().  */\n        free_pages((unsigned long) curr_thread,\n                   size_to_page_order(INTR_STACK_SIZE));\n    }\n\n    sched_elect(SCHED_OPT_RESTORE_ONLY);\n}\n\nint thread_set_priority(struct thread_info *thread, int priority)\n{\n    /* priority change is effective on next scheduling */\n    thread->ti_priority = priority;\n\n    return 0;\n}\n\nstatic struct thread_info *find_thread_by_id(int id)\n{\n    struct thread_info *tp;\n    CURRENT_TASK_INFO(curr_task);\n\n    list_for_each_entry(tp, &curr_task->thread_head, ti_list)\n    {\n        if (tp->ti_id == id)\n            return tp;\n    }\n\n    return NULL;\n}\n\nint thread_join(pthread_t thread, void **retval)\n{\n    struct thread_info *other;\n\n    other = find_thread_by_id(thread);\n    if (!other)\n        return -ESRCH; /* No thread with the ID thread could be found. */\n    if (other->ti_detached == true)\n        return -EINVAL; /* thread is not a joinable thread. */\n\n    /* the other thread is not yet joinable, the current thread blocks */\n    if (other->ti_joinable == false) {\n        CURRENT_THREAD_INFO(curr_thread);\n        if (other->ti_joining)\n            return -EINVAL; /* Another thread is already waiting to\n                       join with this thread. */\n        other->ti_joining = curr_thread;\n        sched_elect(SCHED_OPT_NONE);\n    }\n    *retval = other->ti_retval;\n\n    // XXX: free other's resources, interrupt stack\n\n    return 0;\n}\n\nint thread_detach(pthread_t thread)\n{\n    struct thread_info *thread_info;\n\n    thread_info = find_thread_by_id(thread);\n    thread_info->ti_detached = true;\n\n    return 0;\n}\n\n/* pthread interface */\n\nint sys_pthread_yield(void)\n{\n    return thread_yield();\n}\n\npthread_t sys_pthread_self(void)\n{\n    return (pthread_t) thread_self();\n}\n\nvoid sys_pthread_exit(void *retval)\n{\n    thread_exit(retval);\n}\n\nint sys_pthread_detach(pthread_t thread)\n{\n    return thread_detach(thread);\n}\n\nint sys_pthread_create(pthread_t *thread,\n                       const pthread_attr_t *attr,\n                       void *(*start_routine)(void *),\n                       void *arg)\n{\n    struct rlimit stacklimit;\n    size_t stacksize;\n\n    /* get the thread default stack size */\n    sys_getrlimit(RLIMIT_STACK, &stacklimit);\n    if (attr)\n        stacksize = MIN(attr->stacksize, stacklimit.rlim_max);\n    else\n        stacksize = stacklimit.rlim_cur;\n\n    // FIXME: Check start_routine's address belongs to process' address-space\n    struct thread_info *thread_info = thread_create(\n        start_routine, arg, THREAD_PRIV_USER, stacksize, current_task_info());\n    if (!thread_info)\n        return EAGAIN; /* insufficient resources to create another thread */\n    *thread = (pthread_t) thread_info->ti_id;\n    sched_enqueue(thread_info);\n\n    return 0;\n}\n\nint sys_pthread_join(pthread_t thread, void **retval)\n{\n    return thread_join(thread, retval);\n}\n"
  },
  {
    "path": "kernel/time.c",
    "content": "#include <signal.h>\n#include <stdlib.h>\n#include <string.h>\n#include <time.h>\n\n#include <kernel/bitops.h>\n#include <kernel/errno-base.h>\n#include <kernel/sched.h>\n#include <kernel/signal.h>\n#include <kernel/thread.h>\n#include <kernel/time.h>\n\n/* sleep functions */\n\n// XXX: sleep() is part of <unistd.h>, but nanosleep() is part of <time.h>\n\nextern struct thread_info *thread_idle;\n\nstatic void msleep_callback(struct timer_info *timer)\n{\n    sched_enqueue(timer->owner);\n    timer->disarmed = 1;\n}\n\n// FIXME: POSIX standard is sys_nanosleep()\nint sys_msleep(unsigned int msec)\n{\n    struct timer_info *timer = timer_alloc();\n\n    if (!timer)\n        return -1;\n    CURRENT_THREAD_INFO(curr_thread);\n    timer->owner = curr_thread;\n    timer->disarmed = 0;\n    timer->type = ONESHOT_TIMER;\n    timer->callback = msleep_callback;\n    struct timespec value = {.tv_sec = msec / 1000,\n                             .tv_nsec = (msec % 1000) * 1000000};\n    timer_set(timer, &value);\n    sched_dequeue(curr_thread);\n    sched_elect(SCHED_OPT_NONE);\n    timer_free(timer);\n\n    return 0;\n}\n\n/* POSIX timers */\n\nstatic LIST_HEAD(timer_head);\n\nstatic struct timer_info *find_timer_by_id(timer_t timerid,\n                                           struct list_head *timer_list)\n{\n    struct timer_info *pos;\n    list_for_each_entry(pos, timer_list, list)\n    {\n        if (pos->id == timerid)\n            return pos;\n    }\n\n    return NULL;\n}\n\nstatic int reserve_timer_id(timer_t *timerid)\n{\n    static unsigned long bitmap = 0;\n\n    unsigned long bit = find_first_zero_bit(&bitmap, BITS_PER_LONG);\n    if (bit == BITS_PER_LONG)\n        return -1;\n    bitmap_set_bit(&bitmap, bit);\n    *timerid = bit;\n\n    return 0;\n}\n\nstatic void timer_callback(struct timer_info *timer)\n{\n    if (timer->type == ONESHOT_TIMER)\n        timer->disarmed = 1;\n    do_sigevent(&timer->sigev, timer->owner);\n}\n\nstatic void timer_callback_and_link(struct timer_info *timer)\n{\n    timer->type = INTERVAL_TIMER;\n    timer->callback = timer_callback;\n    timer_set(timer, &timer->value.it_interval);\n    do_sigevent(&timer->sigev, timer->owner);\n}\n\nint sys_timer_create(__unused clockid_t clockid,\n                     struct sigevent *sevp,\n                     timer_t *timerid)\n{\n    struct timer_info *timer = timer_alloc();\n    if (!timer)\n        return -1;\n\n    if (reserve_timer_id(&timer->id)) {\n        timer_free(timer);\n        return EAGAIN;\n    }\n\n    *timerid = timer->id;\n    timer->disarmed = 1;\n    memcpy(&timer->sigev, sevp, sizeof(struct sigevent));\n    list_add(&timer->list, &timer_head);\n\n    return 0;\n}\n\nint sys_timer_settime(timer_t timerid,\n                      __unused int flags,\n                      const struct itimerspec *new_value,\n                      struct itimerspec *old_value)\n{\n    struct timer_info *timer = find_timer_by_id(timerid, &timer_head);\n\n    if (!timer)\n        return EINVAL;\n    if (old_value != NULL)\n        memcpy(old_value, &timer->value, sizeof(struct itimerspec));\n    memcpy(&timer->value, new_value, sizeof(struct itimerspec));\n\n    /* disarm timer */\n    if (!new_value->it_value.tv_sec && !new_value->it_value.tv_nsec) {\n        if (!timer->disarmed) {\n            timer_set(timer, &new_value->it_value);\n            timer->disarmed = 1;\n        }\n        return 0;\n    }\n\n    CURRENT_THREAD_INFO(curr_thread);\n    timer->owner = curr_thread;\n    timer->disarmed = 0;\n    if (new_value->it_interval.tv_sec || new_value->it_interval.tv_nsec) {\n        if ((new_value->it_value.tv_sec == new_value->it_interval.tv_sec) &&\n            (new_value->it_value.tv_nsec == new_value->it_interval.tv_nsec)) {\n            timer->type = INTERVAL_TIMER;\n            timer->callback = timer_callback;\n            timer_set(timer, &new_value->it_interval);\n        } else {\n            timer->type = ONESHOT_TIMER;\n            timer->callback = timer_callback_and_link;\n            timer_set(timer, &new_value->it_value);\n        }\n    } else {\n        timer->type = ONESHOT_TIMER;\n        timer->callback = timer_callback;\n        timer_set(timer, &new_value->it_value);\n    }\n\n    return 0;\n}\n\nint sys_timer_gettime(timer_t timerid, struct itimerspec *curr_value)\n{\n    struct timer_info *timer = find_timer_by_id(timerid, &timer_head);\n\n    if (!timer)\n        return EINVAL;\n    timer_get(timer, curr_value);\n\n    return 0;\n}\n"
  },
  {
    "path": "libc/fcntl.c",
    "content": "#include <sys/types.h>\n\nextern int _open(const char *pathname, int flag);\nextern int _close(int fd);\nextern int _read(int fd, void *buf, size_t count);\nextern int _write(int fd, void *buf, size_t count);\nextern int _lseek(int fd, off_t offset, int whence);\n\nint open(const char *pathname, int flag)\n{\n    return _open(pathname, flag);\n}\n\nint close(int fd)\n{\n    return _close(fd);\n}\n\nint read(int fd, void *buf, size_t count)\n{\n    return _read(fd, buf, count);\n}\n\nint write(int fd, void *buf, size_t count)\n{\n    return _write(fd, buf, count);\n}\n\nint lseek(int fd, off_t offset, int whence)\n{\n    return _lseek(fd, offset, whence);\n}\n"
  },
  {
    "path": "libc/filesystem.c",
    "content": "/* syscall wrappers */\n\n#include <sys/stat.h>\n#include <sys/types.h>\n\n#include <kernel/fs.h>\n#include <kernel/syscalls.h>\n#include \"piko/syscalls.h\"\n\nint _open(const char *pathname, int flags)\n{\n    return do_syscall2((void *) pathname, (void *) flags, SYS_OPEN);\n}\n\nint _close(int fd)\n{\n    return do_syscall1((void *) fd, SYS_CLOSE);\n}\n\nssize_t _read(int fd, void *buf, size_t count)\n{\n    return (ssize_t) do_syscall3((void *) fd, buf, (void *) count, SYS_READ);\n}\n\nssize_t _write(int fd, void *buf, size_t count)\n{\n    return (ssize_t) do_syscall3((void *) fd, buf, (void *) count, SYS_WRITE);\n}\n\noff_t _lseek(int fd, off_t offset, int whence)\n{\n    return (off_t) do_syscall3((void *) fd, (void *) offset, (void *) whence,\n                               SYS_LSEEK);\n}\n\nint stat(const char *pathname, struct stat *buf)\n{\n    return do_syscall2((void *) pathname, (void *) buf, SYS_STAT);\n}\n\nint mount(const char *source,\n          const char *target,\n          const char *filesystemtype,\n          unsigned long mountflags,\n          const void *data)\n{\n    return do_syscall5((void *) source, (void *) target,\n                       (void *) filesystemtype, (void *) mountflags,\n                       (void *) data, SYS_MOUNT);\n}\n\nDIR *opendir(const char *name)\n{\n    return (DIR *) do_syscall2((void *) name, (void *) O_DIRECTORY, SYS_OPEN);\n}\n\nint readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)\n{\n    return do_syscall3((void *) dirp, (void *) entry, (void *) result,\n                       SYS_READDIR_R);\n}\n\nint closedir(DIR *dirp)\n{\n    return do_syscall1((void *) dirp, SYS_CLOSE);\n}\n"
  },
  {
    "path": "libc/piko/mman.c",
    "content": "#include <sys/types.h>\n\n#include <kernel/syscalls.h>\n\n#include \"syscalls.h\"\n\nvoid *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)\n{\n    return (void *) do_syscall6((void *) addr, (void *) length, (void *) prot,\n                                (void *) flags, (void *) fd, (void *) offset,\n                                SYS_MMAP);\n}\n\nint munmap(__unused void *addr, __unused size_t length)\n{\n    return do_syscall2((void *) addr, (void *) length, SYS_MUNMAP);\n}\n"
  },
  {
    "path": "libc/piko/stubs.c",
    "content": "/* Newlib stubs */\n\n#include <stdint.h>\n#include <sys/cdefs.h>\n#include <unistd.h>\n\n#include <kernel/kernel.h>\n#include <kernel/syscalls.h>\n\n#include \"syscalls.h\"\n\n#define HANGS_ON()                                  \\\n    ({                                              \\\n        printk(\"error: Newlib needs %s\", __func__); \\\n        for (;;)                                    \\\n            ;                                       \\\n    })\n\nint _isatty(__unused int file)\n{\n    HANGS_ON();\n\n    return 1;\n}\n\nint _fstat()\n{\n    HANGS_ON();\n\n    return -1;\n}\n\nvoid *_sbrk(__unused int incr)\n{\n    HANGS_ON();\n\n    return NULL;\n}\n\nvoid _exit(__unused int status)\n{\n    HANGS_ON();\n}\n\nclock_t _times()\n{\n    HANGS_ON();\n\n    return -1;\n}\n\nvoid _fini(void)\n{\n    HANGS_ON();\n}\n\nint _getpid(void)\n{\n    return do_syscall0(SYS_GETPID);\n}\n"
  },
  {
    "path": "libc/piko/syscalls.S",
    "content": "#include <kernel/linkage.h>\n#include <kernel/syscalls.h>\n\n\t.syntax unified\n\t.thumb\n\n\t@ int do_syscall0(void, int no)\nENTRY(do_syscall0)\n\tsvc\t#0\n\tbx\tlr\nENDPROC(do_syscall0)\n\n\t@ int do_syscall1(void *a0, int no)\nENTRY(do_syscall1)\n\tsvc\t#1\n\tbx\tlr\nENDPROC(do_syscall1)\n\n\t@ int do_syscall2(void *a0, void *a1, int no)\nENTRY(do_syscall2)\n\tsvc\t#2\n\tbx\tlr\nENDPROC(do_syscall2)\n\n\t@ int do_syscall3(void *a0, void *a1, void *a2, int no)\nENTRY(do_syscall3)\n\tsvc\t#3\n\tbx\tlr\nENDPROC(do_syscall3)\n\n\t@ int do_syscall4(void *a0, void *a1, void *a2, void *a3,\n\t@                 int no)\nENTRY(do_syscall4)\n\tsvc\t#4\n\tbx\tlr\nENDPROC(do_syscall4)\n\n\t@ int do_syscall5(void *a0, void *a1, void *a2, void *a3,\n\t@                 void *a4, int no)\nENTRY(do_syscall5)\n\tsvc\t#5\n\tbx\tlr\nENDPROC(do_syscall5)\n\n\t@ int do_syscall6(void *a0, void *a1, void *a2, void *a3,\n\t@                 void *a4, void *a5, int no)\nENTRY(do_syscall6)\n\tsvc\t#6\n\tbx\tlr\nENDPROC(do_syscall6)\n"
  },
  {
    "path": "libc/piko/syscalls.h",
    "content": "/* libc/piko/syscalls.h */\n\n#ifndef LIBC_SYSCALLS_H\n#define LIBC_SYSCALLS_H\n\nint do_syscall0(int no);\nint do_syscall1(void *a0, int no);\nint do_syscall2(void *a0, void *a1, int no);\nint do_syscall3(void *a0, void *a1, void *a2, int no);\nint do_syscall4(void *a0, void *a1, void *a2, void *a3, int no);\nint do_syscall5(void *a0, void *a1, void *a2, void *a3, void *a4, int no);\nint do_syscall6(void *a0,\n                void *a1,\n                void *a2,\n                void *a3,\n                void *a4,\n                void *a5,\n                int no);\n\n#endif /* !LIBC_SYSCALLS_H */\n"
  },
  {
    "path": "libc/pthread.c",
    "content": "#include <pthread.h>\n#include <stddef.h>\n#include <string.h>\n\n#include \"linux/list.h\"\n\nint pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize)\n{\n    if (!attr)\n        return -1;\n    attr->stacksize = stacksize;\n\n    return 0;\n}\n\nint pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize)\n{\n    if (!attr)\n        return -1;\n    *stacksize = attr->stacksize;\n\n    return 0;\n}\n\nconst pthread_attr_t pthread_attr_default = {.stacksize = 1024};\n\nint pthread_attr_init(pthread_attr_t *attr)\n{\n    if (!attr)\n        return -1;\n    memcpy(attr, &pthread_attr_default, sizeof(pthread_attr_t));\n\n    return 0;\n}\n\nint pthread_mutex_init(pthread_mutex_t *mutex,\n                       __unused const pthread_mutexattr_t *attr)\n{\n    *mutex = -1;\n\n    return 0;\n}\n\nint pthread_cond_init(__unused pthread_cond_t *cond,\n                      __unused const pthread_condattr_t *attr)\n{\n    return 0;\n}\n\n/* syscall wrappers */\n\n#include <kernel/syscalls.h>\n\n#include \"piko/syscalls.h\"\n\nint sched_yield(void)\n{\n    return do_syscall0(SYS_PTHREAD_YIELD);\n}\n\nint pthread_yield(void) __attribute__((alias(\"sched_yield\")));\n\npthread_t pthread_self(void)\n{\n    return (pthread_t) do_syscall0(SYS_PTHREAD_SELF);\n}\n\nvoid pthread_exit(void *retval)\n{\n    do_syscall1((void *) retval, SYS_PTHREAD_EXIT);\n\n    /* compiler complains about 'noreturn' function does return */\n    for (;;)\n        ;\n}\n\nint pthread_detach(pthread_t thread)\n{\n    return do_syscall1((void *) thread, SYS_PTHREAD_DETACH);\n}\n\nint pthread_create(pthread_t *thread,\n                   const pthread_attr_t *attr,\n                   void *(*start_routine)(void *),\n                   void *arg)\n{\n    return do_syscall4((void *) thread, (void *) attr, (void *) start_routine,\n                       arg, SYS_PTHREAD_CREATE);\n}\n\nint pthread_join(pthread_t thread, void **retval)\n{\n    return do_syscall2((void *) thread, (void *) retval, SYS_PTHREAD_JOIN);\n}\n\nint pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)\n{\n    return do_syscall2((void *) cond, (void *) mutex, SYS_PTHREAD_COND_WAIT);\n}\n\nint pthread_cond_signal(pthread_cond_t *cond)\n{\n    return do_syscall1((void *) cond, SYS_PTHREAD_COND_SIGNAL);\n}\n"
  },
  {
    "path": "libc/signal.c",
    "content": "/* syscall wrappers */\n\n#include <signal.h>\n\n#include <kernel/syscalls.h>\n#include \"piko/syscalls.h\"\n\nint sigaction(int sig,\n              const struct sigaction *restrict act,\n              struct sigaction *restrict oact)\n{\n    return do_syscall3((void *) sig, (void *) act, (void *) oact,\n                       SYS_SIGACTION);\n}\n\nint _kill(pid_t pid, int sig)\n{\n    return do_syscall2((void *) pid, (void *) sig, SYS_KILL);\n}\n"
  },
  {
    "path": "libc/stdio.c",
    "content": "#include <string.h>  //XXX: for strlen, strcpy\n#include \"utils.h\"\n#include <stdarg.h>\n#include \"linux/types.h\"\n\nint vsnprintf(char *str, size_t size, const char *format, va_list ap)\n{\n    size_t str_pos = 0; /* position in the buffer str */\n    char itoa_buf[16]; /* contains a 32bit integer in decimal, plus null char */\n    char pad_char;\n    int pad_count = 0;\n\n    for (unsigned int i = 0; (i < strlen(format)) && (str_pos < size); i++) {\n        if (format[i] == '%') {\n            if (format[i + 1] == '%') {\n                str[str_pos++] = '%';\n                i++;\n                continue;\n            }\n\n            /* format '%6d': value is blank-padded */\n            if ((format[i + 1] >= '0') && (format[i + 1] <= '9') &&\n                (format[i + 2] == 'd')) {\n                pad_char = ' ';\n                pad_count = format[i + 1] - '0';\n                i += 1;\n                goto print_dec;\n            }\n\n            /* format '%06d' or '% 6d': value is 0- or blank-padded */\n            if ((format[i + 1] == ' ') || (format[i + 1] == '0')) {\n                pad_char = format[i + 1];\n                pad_count = format[i + 2] - '0';\n                i += 2;\n            }\n\n            /* conversion modifier */\n            switch (format[i + 1]) {\n            case 'p':\n                strcpy(&str[str_pos], \"0x\");\n                str_pos += 2;\n                pad_char = '0';\n                pad_count = 8;\n                goto print_hex;\n            case 'd':\n            print_dec:\n                itoa_base(va_arg(ap, unsigned int), itoa_buf, 10);\n                goto print_num;\n            case 'x':\n            print_hex:\n                itoa_base(va_arg(ap, unsigned int), itoa_buf, 16);\n            print_num:;\n                int itoa_buf_len = strlen(itoa_buf);\n                if (pad_count) {\n                    pad_count -= itoa_buf_len;\n                    strpad(&str[str_pos], pad_char, pad_count);\n                    str_pos += pad_count;\n                }\n                strcpy(&str[str_pos], itoa_buf);\n                str_pos += itoa_buf_len;\n                break;\n            case 's':;\n                char *str_varg = va_arg(ap, char *);\n                strcpy(&str[str_pos], str_varg);\n                str_pos += strlen(str_varg);\n                break;\n            default:\n                goto ordinary_char;\n            }\n            i++;\n        } else {\n        ordinary_char:\n            str[str_pos++] = format[i];\n        }\n    }\n    str[str_pos] = '\\0';\n\n    return str_pos;\n}\n\nint vsprintf(char *str, const char *format, va_list ap)\n{\n    return vsnprintf(str, S32_MAX, format, ap);\n}\n\nint sprintf(char *str, const char *format, ...)\n{\n    int retval;\n    va_list ap;\n\n    va_start(ap, format);\n    retval = vsprintf(str, format, ap);\n    va_end(ap);\n\n    return retval;\n}\n\nint snprintf(char *str, size_t size, const char *format, ...)\n{\n    int retval;\n    va_list ap;\n\n    va_start(ap, format);\n    retval = vsnprintf(str, size, format, ap);\n    va_end(ap);\n\n    return retval;\n}\n"
  },
  {
    "path": "libc/stdlib.c",
    "content": "#include <stdbool.h>\n\n#include <kernel/kernel.h>\n#include <kernel/types.h>\n\n#include \"linux/types.h\"\n#include \"linux/list.h\"\n\n/* This is a boundary tag, located at the beginning of the block wether it's\n * free or allocated.    */\nstruct malloc_tag {\n    /* composite field:\n     *   -  bit 31       0 = block not free, 1 = block free\n     *   -  bit 30..0    full block length, this tag comprised\n     */\n    u32 free__length;\n    struct list_head list;\n    char data[0];\n};\n\n#define BLOCK_FREE_MASK (1 << 30)\n#define BLOCK_LENGTH_MASK (~(1 << 30))\n\nstatic LIST_HEAD(blocks);\n\n/* helper functions */\n\nstatic inline size_t get_block_length(struct malloc_tag *block)\n{\n    return block->free__length & BLOCK_LENGTH_MASK;\n}\n\nstatic inline void set_block_length(struct malloc_tag *block, size_t len)\n{\n    block->free__length = (block->free__length & BLOCK_FREE_MASK) | len;\n}\n\nstatic inline bool get_block_free(struct malloc_tag *block)\n{\n    return block->free__length & BLOCK_FREE_MASK ? true : false;\n}\n\nstatic inline void set_block_free(struct malloc_tag *block, bool free)\n{\n    if (free)\n        block->free__length |= BLOCK_FREE_MASK;\n    else\n        block->free__length &= BLOCK_LENGTH_MASK;\n}\n\nvoid kernel_heap_init(void *heap_start, size_t heap_size)\n{\n    struct malloc_tag *first_block = heap_start;\n\n    first_block->free__length = BLOCK_FREE_MASK | heap_size;\n    list_add(&first_block->list, &blocks);\n}\n\nvoid *malloc(size_t size)\n{\n    struct malloc_tag *free_block, *new_block;\n\n    /* allocation size is a multiple of 4-byte aligned, plus size of tag */\n    size = align_next(size, 4) + sizeof(struct malloc_tag);\n\n    /* find a free block wich is large enough to fullfill the memory requirement\n     */\n    list_for_each_entry(free_block, &blocks, list)\n    {\n        if (get_block_free(free_block) &&\n            (get_block_length(free_block) >= size)) {\n            if ((get_block_length(free_block) - size) >\n                sizeof(struct malloc_tag)) {\n                set_block_length(free_block,\n                                 get_block_length(free_block) - size);\n                new_block =\n                    (struct malloc_tag *) ((u32) free_block +\n                                           get_block_length(free_block));\n                set_block_free(new_block, false);\n                set_block_length(new_block, size);\n                list_add(&new_block->list, &free_block->list);\n                return new_block->data;\n            } else {\n                set_block_free(free_block, false);\n                return free_block->data;\n            }\n        }\n    }\n\n    return NULL;\n}\n\nvoid free(void *ptr)\n{\n    struct malloc_tag *block = container_of(ptr, struct malloc_tag, data);\n    struct malloc_tag *prev_block, *next_block;\n\n    set_block_free(block, true);\n\n    /* merge with previous block if free */\n    if (block->list.prev != &blocks) {\n        prev_block = list_prev_entry(block, list);\n        if (get_block_free(prev_block)) {\n            set_block_length(prev_block, get_block_length(prev_block) +\n                                             get_block_length(block));\n            list_del(&block->list);\n            block = prev_block;\n        }\n    }\n\n    /* merge with next block if free */\n    if (block->list.next != &blocks) {\n        next_block = list_next_entry(block, list);\n        if (get_block_free(next_block)) {\n            set_block_length(\n                block, get_block_length(block) + get_block_length(next_block));\n            list_del(&next_block->list);\n        }\n    }\n}\n"
  },
  {
    "path": "libc/time.c",
    "content": "/* syscall wrappers */\n\n#include <signal.h>\n#include <sys/types.h>\n#include <time.h>\n\n#include <kernel/syscalls.h>\n#include \"piko/syscalls.h\"\n\nint timer_create(clockid_t clockid, struct sigevent *sevp, timer_t *timerid)\n{\n    return do_syscall3((void *) clockid, (void *) sevp, (void *) timerid,\n                       SYS_TIMER_CREATE);\n}\n\nint timer_settime(timer_t timerid,\n                  int flags,\n                  const struct itimerspec *new_value,\n                  struct itimerspec *old_value)\n{\n    return do_syscall4((void *) timerid, (void *) flags, (void *) new_value,\n                       (void *) old_value, SYS_TIMER_SETTIME);\n}\n\nint timer_gettime(timer_t timerid, struct itimerspec *curr_value)\n{\n    return do_syscall2((void *) timerid, (void *) curr_value,\n                       SYS_TIMER_GETTIME);\n}\n"
  },
  {
    "path": "libc/ucontext.c",
    "content": "#include <stdarg.h>\n#include <ucontext.h>\n\n#include \"linux/types.h\"\n\nvoid return_from_makecontext();\n\nvoid makecontext(ucontext_t *ucp, void (*func)(), int argc, ...)\n{\n    va_list ap;\n\n    /* pass arguments to the context entry function */\n    if (argc) {\n        va_start(ap, argc);\n\n        /* the 4 first arguments go into registers r0-r3 */\n        for (int i = 0; (i < argc) && (i < 4); i++)\n            ucp->uc_mcontext.gprs[i] = va_arg(ap, int);\n\n        /* extra arguments go into the stack */\n\n        va_end(ap);\n    }\n\n    /* top of the stack has a back-link pointer to the context's struct */\n    ucp->uc_stack.ss_sp = (void *) ((u32) ucp->uc_stack.ss_sp - sizeof(u32));\n    *(u32 *) ucp->uc_stack.ss_sp = (u32) ucp;\n\n    /* initialize the machine context */\n    // FIXME: ss_sp and mcontext_sp are mutually redundant\n    ucp->uc_mcontext.sp = (u32) ucp->uc_stack.ss_sp;\n    ucp->uc_mcontext.lr = (u32) return_from_makecontext;\n    ucp->uc_mcontext.pc = (u32) func | 1; /* force Thumb_Mode */\n}\n"
  },
  {
    "path": "libc/unistd.c",
    "content": "/* syscall wrappers */\n\n#include <kernel/syscalls.h>\n#include \"piko/syscalls.h\"\n\nlong sysconf(int name)\n{\n    return do_syscall1((void *) name, SYS_SYSCONF);\n}\n\nunsigned int msleep(unsigned int msecs)\n{\n    return do_syscall1((void *) msecs, SYS_MSLEEP);\n}\n\n/*int close(int fd)\n{\n    return do_syscall1((void *) fd, SYS_CLOSE);\n}*/\n"
  },
  {
    "path": "libc/utils.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n\nstatic char *strrev(char *s)\n{\n    char *t = s;\n    char *u = s;\n\n    if (!s || (*s == '\\0'))\n        return s;\n\n    /* u points to the last char in the string */\n    while (*(u + 1) != '\\0')\n        u++;\n\n    /* t moves forward, u moves backward */\n    while (t < u) {\n        char tmp = *t;\n        *t++ = *u;\n        *u-- = tmp;\n    }\n\n    return s;\n}\n\nvoid strpad(char *buf, char pad_val, int count)\n{\n    buf[count--] = '\\0';\n    for (; count >= 0; count--)\n        buf[count] = pad_val;\n}\n\nchar *itoa_base(int value, char *buf, int base)\n{\n    const char base36[] = \"0123456789abcdefghijklmnopqrstuvwxyz\";\n    int i = 0;\n    int isneg = 0;\n    unsigned int val = value;\n\n    if (!buf || (base > 36))\n        return NULL;\n\n    if ((value < 0) && (base == 10)) {\n        val = abs(value);\n        isneg = 1;\n    }\n\n    do {\n        buf[i++] = base36[val % base];\n        val /= base;\n    } while (val);\n\n    if (isneg)\n        buf[i++] = '-';\n    buf[i] = '\\0';\n\n    return strrev(buf);\n}\n"
  },
  {
    "path": "libc/v7m-pthread.S",
    "content": "#include <kernel/syscalls.h>\n#include <kernel/linkage.h>\n\n\t.syntax unified\n\t.thumb\n\n\t// -1: unlocked, 0: locked, positive: locked, possible waiters\n\n/**\n * FIXME: In order to reduce the code base and we tend to keep just one\n *\t\t\t'bx lr'. But it would make it not intuitional. Probably, we\n *\t\t\tcould discard this optimization.\n */\n\t@ int pthread_mutex_lock(pthread_mutex_t *mutex)\nENTRY(pthread_mutex_lock)\n\tmovs\tr2, #0\n0:\tldrex\tr1, [r0]\n    teq r1, #-1\t\t\t@ check locked?\n\tbne\t1f\n\tstrex\tr1, r2, [r0]\n    teq r1, #0\t\t\t@ 'strex' success?\n\tbne\t0b\n\tdmb\t\t\t@ ARMv7-M ARM, A3.4.6\n\tmovs\tr0, #0\t\t@ it also update EQ flag\n1:\titt ne\n\tmovne\tr1, #SYS_PTHREAD_MUTEX_LOCK\n\tsvcne\t#1\n\tbx\tlr\nENDPROC(pthread_mutex_lock)\n\n\t@ int pthread_mutex_trylock(pthread_mutex_t *mutex)\nENTRY(pthread_mutex_trylock)\n\tmovs\tr2, #0\n0:\tldrex\tr1, [r0]\n    teq r1, #-1\t\t\t@ check locked?\n    bne 1f\n\tstrex\tr1, r2, [r0]\n\tteq\tr1, #0\t\t\t@ 'strex' success?\n    bne 1f\n\tmovs\tr0, #0\t\t@ it also update EQ flag\n1:  it ne\n    movne\tr0, #-1\n\tbx\tlr\nENDPROC(pthread_mutex_trylock)\n\n\t@ int pthread_mutex_unlock(pthread_mutex_t *mutex)\nENTRY(pthread_mutex_unlock)\n\tmovs\tr2, #-1\n0:\tldrex\tr1, [r0]\n\tteq\tr1, #0\t\t\t@ Just one hold lock?\n    bne 1f\n\tstrex\tr1, r2, [r0]\n\tteq\tr1, #0\t\t\t@ 'strex' success?\n\tbne\t0b\n\tdmb\t\t\t@ ARMv7-M ARM, A3.4.6\n\tmovs\tr0, #0\t\t@ it also update EQ flag\n1:\titt ne\n\tmovne\tr1, #SYS_PTHREAD_MUTEX_UNLOCK\n\tsvcne\t#1\n\tbx\tlr\nENDPROC(pthread_mutex_unlock)\n"
  },
  {
    "path": "mk/cmsis.mk",
    "content": "SVN_REV = 27441\n\nARM_CMSIS_ASSETS = \\\n\tmbed_toolchain.h \\\n\tmbed_preprocessor.h \\\n\tmbed_assert.h\n\nARM_CMSIS_ASSETS := $(addprefix $(CMSIS)/util/, $(ARM_CMSIS_ASSETS))\n\n$(CMSIS):\n\t@mkdir -p $@\n\n$(CMSIS)/$(PLAT): $(CMSIS)/arm $(CMSIS)/TARGET_STM $(ARM_CMSIS_ASSETS)\n\n$(CMSIS)/arm:\n\tsvn export -r$(SVN_REV) -q --force https://github.com/ARMmbed/mbed-os/trunk/cmsis/ $(CMSIS)/arm\n\n$(CMSIS)/TARGET_STM:\n\tsvn export -r$(SVN_REV) -q --force https://github.com/ARMmbed/mbed-os/trunk/targets/TARGET_STM/ $(CMSIS)/TARGET_STM\n\n$(ARM_CMSIS_ASSETS):\n\t$(VECHO) \"  WGET\\t\\t$@\\n\"\n\t$(Q)$(WGET) -q https://raw.github.com/ARMmbed/mbed-os/master/platform/include/platform/$(notdir $@) -P $(CMSIS)/platform\n"
  },
  {
    "path": "mk/flags.mk",
    "content": "CROSS_COMPILE ?= arm-none-eabi-\nCC = $(CROSS_COMPILE)gcc\nAS = $(CROSS_COMPILE)as\nAR = $(CROSS_COMPILE)ar\nOBJCOPY = $(CROSS_COMPILE)objcopy\nGDB = $(CROSS_COMPILE)gdb\nHOSTCC  = gcc\nWGET = wget\nPYTHON ?= python\nQEMU_SYSTEM_ARM ?= qemu-system-arm\n\n# FIXME: configurable via menuconfig or command line\nCFLAGS_OPT = -Os # -flto\n\nCFLAGS += \\\n    -std=c99 \\\n    -W -Wall \\\n    -Iinclude -Iinclude/libc -I. \\\n    -I$(CMSIS)/arm -I$(CMSIS)/$(PLAT) -I$(CMSIS)/$(PLAT)/hal \\\n    -Iinclude/kernel \\\n    -D_POSIX_THREADS=1 -D_POSIX_TIMERS=1 -D_POSIX_REALTIME_SIGNALS=1 \\\n    -Wno-main -fdiagnostics-color \\\n    -ffunction-sections -fdata-sections -ggdb \\\n    $(CFLAGS_OPT)\n\n# FIXME: make Piko-specific build options configurable\nCFLAGS += \\\n    -D CONFIG_KERNEL_STACK_CHECKING\n\nLDFLAGS += \\\n    -nostartfiles -specs=nano.specs \\\n    -Wl,-Map=$(NAME).map -Wl,-Tpiko.lds -Wl,--gc-sections\n\nCFLAGS  += -mthumb -mcpu=$(CPU)\nLDFLAGS += -mthumb -march=$(ARCH)\n"
  },
  {
    "path": "mk/rules.mk",
    "content": "# Control the build verbosity\nifeq (\"$(VERBOSE)\",\"1\")\n    Q :=\n    VECHO = @true\nelse\n    Q := @\n    VECHO = @printf\nendif\n\n$(NAME).elf: $(OBJS) fs/version.o\n\t$(VECHO) \"  LD\\t\\t$@\\n\"\n\t$(Q)$(CC) $(LDFLAGS) -o $@ $^\n\n%.o: %.c\n\t$(VECHO) \"  CC\\t\\t$@\\n\"\n\t$(Q)$(CC) -o $@ $(CFLAGS) -c -D__KERNEL__ -MMD -MF $@.d $<\n\n%.o: %.S\n\t$(VECHO) \"  AS\\t\\t$@\\n\"\n\t$(Q)$(CC) -o $@ $(CFLAGS) -c $<\n\n%.lds: %.lds.S\n\t$(VECHO) \"  HOSTCC\\t$@\\n\"\n\t$(Q)$(HOSTCC) -E -P -Iinclude -DROMSZ=$(ROMSZ) -DRAMSZ=$(RAMSZ) -o $@ $<\n\nkernel/syscall.c: include/kernel/syscalls.h\n\t$(VECHO) \"  GEN\\t\\t$@\\n\"\n\t$(Q)$(PYTHON) scripts/gen-syscalls.py --source > $@\n\ninclude/kernel/syscalls.h:\n\t$(VECHO) \"  GEN\\t\\t$@\\n\"\n\t$(Q)$(PYTHON) scripts/gen-syscalls.py --header > $@\n\nfs/version:\n\t$(VECHO) \"  GEN\\t\\t$@\\n\"\n\t$(Q)python3 scripts/gen-proc-version.py --cc-version    \\\n\t--user $(shell whoami) --host $(shell hostname)\t\t\\\n\t-a $(ARCH) -c $(CPU) -n 'Piko' > $@\n\nfs/version.o: fs/version\n\t$(VECHO) \"  OBJCOPY\\t$@\\n\"\n\t$(Q)$(OBJCOPY) -I binary -O elf32-littlearm -B arm \\\n\t    --rename-section .data=.rodata \\\n\t    --redefine-sym _binary_$(subst /,_,$<)_start=_version_ptr \\\n\t    --redefine-sym _binary_$(subst /,_,$<)_size=_version_len \\\n\t    $< $@\n\n%.bin: %.elf\n\t$(VECHO) \"  OBJCOPY\\t$@\\n\"\n\t$(Q)$(OBJCOPY) -Obinary $< $@\n"
  },
  {
    "path": "piko.lds.S",
    "content": "OUTPUT_FORMAT(\"elf32-littlearm\")\nOUTPUT_ARCH(arm)\n\n#define RAMORG 0x20000000\n\nMEMORY\n{\n    rom (rx)  : ORIGIN = 0, LENGTH = ROMSZ\n    ram (rwx) : ORIGIN = RAMORG, LENGTH = RAMSZ\n}\n\nSECTIONS\n{\n    .vector 0 : {\n        PROVIDE(__vector_start__ = .);\n        KEEP(*(.vector))\n    } > rom\n\n    .text BLOCK(8) : {\n        PROVIDE(__text_start__ = .);\n        *(.text*)\n        PROVIDE(__text_end__ = .);\n        . = ALIGN(8);\n        PROVIDE(__shell_cmd_start__ = .);\n        KEEP(*(.shell_cmd*))\n        PROVIDE(__shell_cmd_end__ = .);\n        PROVIDE(__sched_classes_start__ = .);\n        KEEP(*(.sched.class*))\n        PROVIDE(__sched_classes_end__ = .);\n        PROVIDE(__serial_hook_start__ = .);\n        KEEP(*(.serial.hook*))\n        PROVIDE(__serial_hook_end__ = .);\n        PROVIDE(__rodata_start__ = .);\n        *(.rodata*)\n        PROVIDE(__rodata_end__ = .);\n   } > rom\n\n    /* Initialized data (.data* sections) are initially stored in Flash,\n       but need to be copied to a volatile storage for they are not\n       read-only.    */\n\n    .data : AT (__rodata_end__) {\n        PROVIDE(__data_start__ = .);\n        *(.data*)\n        PROVIDE(__data_end__ = .);\n    } > ram\n\n    PROVIDE(__data_size__ = SIZEOF(.data));\n\n    .bss : {\n        PROVIDE(__bss_start__ = .);\n        *(.bss*)\n        *(COMMON)\n        PROVIDE(__bss_end__ = .);\n    } > ram\n\n    PROVIDE(__bss_size__ = SIZEOF(.bss));\n\n    /* heap for the kernel's malloc */\n    .heap BLOCK(32) : {\n        PROVIDE(__heap_start__ = .);\n        . += 4k;\n        PROVIDE(__heap_end__ = .);\n    } > ram\n\n    PROVIDE(__heap_size__ = SIZEOF(.heap));\n\n    .pgmem RAMORG + RAMSZ - 32k : {\n        PROVIDE(__pgmem_start__ = .);\n        . += 26k;\n\n        /* Reserve the last page for the early stack. Must be of the biggest size\n         * such that there will be no coalescing when freeing the page, for lower\n         * booting time.    */\n        PROVIDE(__early_stack_end__ = .);\n        . += 2k;\n        PROVIDE(__early_stack_start__ = .);\n\n        PROVIDE(__pgmem_end__ = .);\n    } > ram\n\n    PROVIDE(__pgmem_size__ = SIZEOF(.pgmem));\n\n    .mtdram : {\n        PROVIDE(__mtdram_start__ = .);\n        . += 4k;\n    } > ram\n\n    PROVIDE(__mtdram_size__ = SIZEOF(.mtdram));\n\n    /DISCARD/ :\n    {\n        *(.ARM.exidx)\n        *(.ARM.attributes)\n    }\n}\n"
  },
  {
    "path": "platform/f429disco/Makefile",
    "content": "CPU = cortex-m4\nARCH = armv7-m\n\nROMSZ = 2048k\nRAMSZ = 192k\n\nCFLAGS += \\\n    -Iplatform/f429disco \\\n    -I$(CMSIS)\\\n    -I$(CMSIS)/platform \\\n    -I$(CMSIS)/arm \\\n    -I$(CMSIS)/arm/TARGET_CORTEX_M \\\n    -I$(CMSIS)/arm/TARGET_CORTEX_M/TOOLCHAIN_GCC \\\n    -I$(CMSIS)/TARGET_STM \\\n    -I$(CMSIS)/TARGET_STM//TARGET_STM32F4 \\\n    -I$(CMSIS)/TARGET_STM//TARGET_STM32F4/device \\\n    -I$(CMSIS)/TARGET_STM//TARGET_STM32F4/TARGET_STM32F429xI \\\n    -I$(CMSIS)/TARGET_STM//TARGET_STM32F4/TARGET_STM32F429xI/device \\\n    -I$(CMSIS)/TARGET_STM//TARGET_STM32F4/TARGET_STM32F429xI/TARGET_DISCO_F429ZI\n\nCSRC += \\\n    platform/f429disco/halt.c \\\n    platform/f429disco/init.c \\\n    platform/f429disco/uart.c\n\n# CMSIS files\n## STM32 HAL\nCSRC += $(CMSIS)/TARGET_STM/TARGET_STM32F4/device/system_stm32f4xx.c\nCSRC += $(CMSIS)/TARGET_STM/TARGET_STM32F4/device/stm32f4xx_hal_rcc.c\nCSRC += $(CMSIS)/TARGET_STM/TARGET_STM32F4/device/stm32f4xx_hal_rcc_ex.c\nCSRC += $(CMSIS)/TARGET_STM/TARGET_STM32F4/device/stm32f4xx_hal_gpio.c\nCSRC += $(CMSIS)/TARGET_STM/TARGET_STM32F4/device/stm32f4xx_hal_uart.c\n\n## SystemInit()\nCSRC += $(CMSIS)/TARGET_STM/TARGET_STM32F4/TARGET_STM32F429xI/TARGET_DISCO_F429ZI/system_clock.c\n\n# Timer driver files\nCSRC += drivers/timer/systick.c\n\n# Serial driver files\nCFLAGS += -Idrivers/serial\nCSRC += drivers/serial/stm32-uart.c\n"
  },
  {
    "path": "platform/f429disco/build.mk",
    "content": "run: $(NAME).bin\n\topenocd -f interface/stlink-v2.cfg \\\n\t    -f target/stm32f4x_stlink.cfg \\\n\t    -c \"init\" \\\n\t    -c \"reset init\" \\\n\t    -c \"reset halt\"\n\t    -c \"reset run\"\n\nst-flash: $(NAME).bin\n\tst-flash --reset write \\\n\t    $(NAME).bin 0x08000000\n\ndbg: $(NAME).bin\n\topenocd -f board/stm32f429discovery.cfg\n\ngdb: $(NAME).elf\n\t$(Q)$(GDB) -q \\\n\t    $< -ex \"target remote :3333\" \\\n\t    -ex \"monitor reset halt\"\n\nwakeup:\n\topenocd -f interface/stlink-v2.cfg \\\n\t    -f target/stm32f4x_stlink.cfg \\\n\t    -c \"init\" \\\n\t    -c \"reset init\" \\\n\t    -c \"reset run\" \\\n\t    -c \"shutdown\"\n"
  },
  {
    "path": "platform/f429disco/halt.c",
    "content": "\n#include <arch/semihosting.h>\n\nvoid __platform_halt(void)\n{\n    v7m_semihost_exit(0);\n}\n"
  },
  {
    "path": "platform/f429disco/init.c",
    "content": "#include <serial.h>\n#include <kernel/compiler.h>\n\n#include \"platform.h\"\n\n#define CPU_FREQ_IN_HZ 168000000\n#define SYSTICK_FREQ_IN_HZ 1000\n#define SYSTICK_PERIOD_IN_MSECS (SYSTICK_FREQ_IN_HZ / 1000)\n\nstruct timer_operations;\n\nvoid config_timer_operations(struct timer_operations *tops);\n\nextern struct timer_operations systick_tops;\n\n__weak void __platform_init(void)\n{\n    config_timer_operations(&systick_tops);\n\n    /* SysTick running at 1kHz */\n    SysTick_Config(CPU_FREQ_IN_HZ / SYSTICK_FREQ_IN_HZ);\n}\n\n__weak void __platform_halt(void)\n{\n    for (;;)\n        ;\n}\n"
  },
  {
    "path": "platform/f429disco/platform.h",
    "content": "#ifndef _PLATFORM_STM32_PLATFORM_H\n#define _PLATFORM_STM32_PLATFORM_H\n\n#include <cmsis.h>\n\nvoid __platform_init(void);\nvoid __platform_halt(void);\n\n/* Default USART for output */\n#define USARTx          USART1\n#define USARTx_IRQn     USART1_IRQn\n\n#endif /* !_PLATFORM_STM32_PLATFORM_H */\n"
  },
  {
    "path": "platform/f429disco/uart.c",
    "content": "#include <serial.h>\n#include <platform/compiler.h>\n\n#include \"platform.h\"\n#include \"kernel/kernel.h\"\n#include \"stm32-uart.h\"\n\n#define STM32_USART_MAX 8\n\nstatic void uart_port_setup(struct stm32_uart_port *port)\n{\n    /* Enable peripherals and GPIO Clocks */\n    /* Enable GPIO TX/RX clock */\n    port->gpio_tx_clk_enable();\n    port->gpio_rx_clk_enable();\n\n    /* Enable USART/UART clock */\n    port->uart_clk_enable();\n}\n\nstatic void uart_port_init(struct stm32_uart_port *port)\n{\n    /* GPIO initialized with USART/UART Tx/Rx configuration */\n    port->gpio_init(port->gpio_tx, &port->gpio_tx_init_info);\n    port->gpio_init(port->gpio_rx, &port->gpio_rx_init_info);\n\n    /* UART init */\n    port->uart_init(&port->uart_init_info);\n}\n\nvoid uart_init(void)\n{\n    // clang-format off\n    struct stm32_uart_port ports[STM32_USART_MAX] = {\n        /* [0] USART1 */\n        [0] = {\n            .gpio_tx = GPIOA,\n            .gpio_rx = GPIOA,\n     \n            .gpio_tx_init_info = {\n                .Pin = GPIO_PIN_9,\n                .Mode = GPIO_MODE_AF_PP,\n                .Pull = GPIO_NOPULL,\n                .Speed = GPIO_SPEED_FAST,\n                .Alternate = GPIO_AF7_USART1,\n            },\n            .gpio_rx_init_info = {\n                .Pin = GPIO_PIN_10,\n                .Mode = GPIO_MODE_AF_PP,\n                .Pull = GPIO_NOPULL,\n                .Speed = GPIO_SPEED_FAST,\n                .Alternate = GPIO_AF7_USART1,\n            },\n             /* UART2 configured as follow:\n                 - Word Length = 8 Bits\n                 - Stop Bit = One Stop bit\n                 - Parity = None\n                 - BaudRate = 9600 baud\n                 - Hardware flow control disabled (RTS and CTS signals) */\n            .uart_init_info = {\n                .Instance = USART1,\n                .Init = {\n                    .BaudRate = 115200,\n                    .WordLength = UART_WORDLENGTH_8B,\n                    .StopBits = UART_STOPBITS_1,\n                    .Parity = UART_PARITY_NONE,\n                    .HwFlowCtl = UART_HWCONTROL_NONE,\n                    .Mode = UART_MODE_TX_RX | UART_IT_RXNE,\n                    .OverSampling = UART_OVERSAMPLING_16,\n                },\n            },\n    \n            .gpio_init = HAL_GPIO_Init,\n            .uart_init = HAL_UART_Init,\n        \n            .gpio_tx_clk_enable = PLAT_EVAL(__HAL_RCC_GPIOA_CLK_ENABLE()),\n            .gpio_rx_clk_enable = PLAT_EVAL(__HAL_RCC_GPIOA_CLK_ENABLE()),\n            .uart_clk_enable = PLAT_EVAL(__HAL_RCC_USART1_CLK_ENABLE()),\n       },\n        /* [1] USART2 */\n        /* [2] USART3 */\n        /* [3] UART4 */\n        /* [4] UART5 */\n        /* [5] USART6 */\n        /* [6] UART7 */\n        /* [7] UART8 */\n    };\n    // clang-format on\n\n    /* Specify initialize order */\n    int init_order[] = {0};\n\n    /* USART/UART port setup */\n    for (size_t i = 0; i < ARRAY_SIZE(init_order); i++)\n        uart_port_setup(&ports[init_order[i]]);\n\n    /* USART/UART port init */\n    for (size_t i = 0; i < ARRAY_SIZE(init_order); i++)\n        uart_port_init(&ports[init_order[i]]);\n}\n"
  },
  {
    "path": "platform/stm32p103/Makefile",
    "content": "CPU = cortex-m3\nARCH = armv7-m\n\nROMSZ = 128k\nRAMSZ = 64k\n\nCFLAGS += \\\n    -Iplatform/stm32p103 \\\n    -I$(CMSIS)\\\n    -I$(CMSIS)/platform \\\n    -I$(CMSIS)/arm \\\n    -I$(CMSIS)/arm/TARGET_CORTEX_M \\\n    -I$(CMSIS)/arm/TARGET_CORTEX_M/TOOLCHAIN_GCC \\\n    -I$(CMSIS)/TARGET_STM \\\n    -I$(CMSIS)/TARGET_STM//TARGET_STM32F1 \\\n    -I$(CMSIS)/TARGET_STM//TARGET_STM32F1/device \\\n    -I$(CMSIS)/TARGET_STM//TARGET_STM32F1/TARGET_NUCLEO_F103RB \\\n    -I$(CMSIS)/TARGET_STM//TARGET_STM32F1/TARGET_NUCLEO_F103RB/device\n\nCSRC += \\\n    platform/stm32p103/halt.c \\\n    platform/stm32p103/init.c \\\n    platform/stm32p103/uart.c\n\n# CMSIS files\n## STM32 HAL\nCSRC += $(CMSIS)/TARGET_STM/TARGET_STM32F1/device/system_stm32f1xx.c\nCSRC += $(CMSIS)/TARGET_STM/TARGET_STM32F1/device/stm32f1xx_hal_rcc.c\nCSRC += $(CMSIS)/TARGET_STM/TARGET_STM32F1/device/stm32f1xx_hal_rcc_ex.c\nCSRC += $(CMSIS)/TARGET_STM/TARGET_STM32F1/device/stm32f1xx_hal_gpio.c\nCSRC += $(CMSIS)/TARGET_STM/TARGET_STM32F1/device/stm32f1xx_hal_uart.c\n\n## SystemInit()\nCSRC += $(CMSIS)/TARGET_STM/TARGET_STM32F1/TARGET_NUCLEO_F103RB/device/system_clock.c\n\n# Timer driver files\nCSRC += drivers/timer/systick.c\n\n# Serial driver files\nCFLAGS += -Idrivers/serial\nCSRC += drivers/serial/stm32-uart.c\n"
  },
  {
    "path": "platform/stm32p103/build.mk",
    "content": "ifeq ($(shell lsb_release -c -s),trusty)\n    REDIRECT_SERIAL = -serial stdio\nendif\n\nrun: $(NAME).bin\n\t$(Q)$(QEMU_SYSTEM_ARM) \\\n\t    -semihosting \\\n\t    $(REDIRECT_SERIAL) \\\n\t    -nographic \\\n\t    -cpu cortex-m3 \\\n\t    -machine stm32-p103\t\\\n\t    -kernel $<\n\ndbg: $(NAME).bin\n\t$(Q)$(QEMU_SYSTEM_ARM) \\\n\t    -semihosting \\\n\t    $(REDIRECT_SERIAL) \\\n\t    -nographic \\\n\t    -cpu cortex-m3 \\\n\t    -machine stm32-p103 \\\n\t    -kernel $< \\\n\t    -S -s\n\ngdb: $(NAME).elf\n\t$(Q)$(GDB) -q \\\n\t    $< -ex \"target remote :1234\"\n"
  },
  {
    "path": "platform/stm32p103/halt.c",
    "content": "\n#include <arch/semihosting.h>\n\nvoid __platform_halt(void)\n{\n    v7m_semihost_exit(0);\n}\n"
  },
  {
    "path": "platform/stm32p103/init.c",
    "content": "#include <kernel/compiler.h>\n\n#include \"platform.h\"\n\n#define CPU_FREQ_IN_HZ 72000000\n#define SYSTICK_FREQ_IN_HZ 1000\n#define SYSTICK_PERIOD_IN_MSECS (SYSTICK_FREQ_IN_HZ / 1000)\n\nstruct timer_operations;\n\nvoid config_timer_operations(struct timer_operations *tops);\n\nextern struct timer_operations systick_tops;\n\nvoid rcc_clock_init(void);\nvoid __uart_enable(void);\n\n__weak void __platform_init(void)\n{\n    config_timer_operations(&systick_tops);\n\n    /* SysTick running at 1kHz */\n    SysTick_Config(CPU_FREQ_IN_HZ / SYSTICK_FREQ_IN_HZ);\n}\n\n__weak void __platform_halt(void)\n{\n    for (;;)\n        ;\n}\n"
  },
  {
    "path": "platform/stm32p103/platform.h",
    "content": "#ifndef _PLATFORM_STM32_PLATFORM_H\n#define _PLATFORM_STM32_PLATFORM_H\n\n#include <cmsis.h>\n\nvoid __platform_init(void);\nvoid __platform_halt(void);\n\n\n/* Default USART for outputing */\n#define USARTx          USART2\n#define USARTx_IRQn     USART2_IRQn\n\n#endif /* !_PLATFORM_STM32_PLATFORM_H */\n"
  },
  {
    "path": "platform/stm32p103/uart.c",
    "content": "#include <serial.h>\n#include <platform/compiler.h>\n\n#include \"platform.h\"\n#include \"kernel/kernel.h\"\n#include \"stm32-uart.h\"\n\n#define STM32_USART_MAX 3\n\nstatic void uart_port_setup(struct stm32_uart_port *port)\n{\n    /* Enable peripherals and GPIO Clocks */\n    /* Enable GPIO TX/RX clock */\n    port->gpio_tx_clk_enable();\n    port->gpio_rx_clk_enable();\n\n    /* Enable USART/UART clock */\n    port->uart_clk_enable();\n}\n\nstatic void uart_port_init(struct stm32_uart_port *port)\n{\n    /* GPIO initialized with USART/UART Tx/Rx configuration */\n    port->gpio_init(port->gpio_tx, &port->gpio_tx_init_info);\n    port->gpio_init(port->gpio_rx, &port->gpio_rx_init_info);\n\n    /* UART init */\n    port->uart_init(&port->uart_init_info);\n}\n\n\nvoid uart_init(void)\n{\n    // clang-format off\n    struct stm32_uart_port ports[STM32_USART_MAX] = {\n        /* [0] USART1 */\n        /* [1] USART2 */\n        [1] = {\n            .gpio_tx = GPIOA,\n            .gpio_rx = GPIOA,\n     \n            .gpio_tx_init_info = {\n                .Pin = GPIO_PIN_2,\n                .Mode = GPIO_MODE_AF_PP,\n                .Pull = GPIO_PULLUP,\n                .Speed = GPIO_SPEED_FREQ_HIGH,\n            },\n            .gpio_rx_init_info = {\n                .Pin = GPIO_PIN_3,\n                .Mode = GPIO_MODE_INPUT,\n                .Pull = GPIO_PULLUP,\n                .Speed = GPIO_SPEED_FREQ_HIGH,\n            },\n             /* UART2 configured as follow:\n                 - Word Length = 8 Bits\n                 - Stop Bit = One Stop bit\n                 - Parity = None\n                 - BaudRate = 9600 baud\n                 - Hardware flow control disabled (RTS and CTS signals) */\n            .uart_init_info = {\n                .Instance = USART2,\n                .Init = {\n                    .BaudRate = 115200,\n                    .WordLength = UART_WORDLENGTH_8B,\n                    .StopBits = UART_STOPBITS_1,\n                    .Parity = UART_PARITY_NONE,\n                    .HwFlowCtl = UART_HWCONTROL_NONE,\n                    .Mode = UART_MODE_TX_RX | UART_IT_RXNE,\n                    .OverSampling = UART_OVERSAMPLING_16,\n                },\n            },\n    \n            .gpio_init = HAL_GPIO_Init,\n            .uart_init = HAL_UART_Init,\n        \n            .gpio_tx_clk_enable = PLAT_EVAL(__HAL_RCC_GPIOA_CLK_ENABLE()),\n            .gpio_rx_clk_enable = PLAT_EVAL(__HAL_RCC_GPIOA_CLK_ENABLE()),\n            .uart_clk_enable = PLAT_EVAL(__HAL_RCC_USART2_CLK_ENABLE()),\n       },\n        /* [3] USART2 */\n    };\n    // clang-format on\n\n    /* Specify initialize order */\n    int init_order[] = {1};\n\n    /* USART/UART port setup */\n    for (size_t i = 0; i < ARRAY_SIZE(init_order); i++)\n        uart_port_setup(&ports[init_order[i]]);\n\n    /* USART/UART port init */\n    for (size_t i = 0; i < ARRAY_SIZE(init_order); i++)\n        uart_port_init(&ports[init_order[i]]);\n}\n"
  },
  {
    "path": "scripts/gen-proc-version.py",
    "content": "import subprocess\nimport string\nfrom datetime import datetime\nimport argparse\n\ndef run_cmd(cmd):\n    res = subprocess.run(cmd, universal_newlines=True,\n                         stdout=subprocess.PIPE,\n                         stderr=subprocess.STDOUT)\n    if res.returncode == 0:\n        return res.stdout.strip()\n    return 'unknown'\n\ndef cc_version(cc):\n    cmd = [ cc, '--version' ]\n    return run_cmd(cmd).splitlines()[0]\n\nparser = argparse.ArgumentParser()\n\nparser.add_argument(\"-n\", \"--name\", action=\"store\", dest=\"name\")\nparser.add_argument(\"-a\", \"--arch\", action=\"store\", dest=\"arch\")\nparser.add_argument(\"-c\", \"--cpu\", action=\"store\", dest=\"cpu\")\nparser.add_argument(\"-u\", \"--user\", action=\"store\", dest=\"user\", default=\"unknown\")\nparser.add_argument(      \"--host\", action=\"store\", dest=\"host\", default=\"unknown\")\nparser.add_argument(      \"--major\", action=\"store\", dest=\"major\", default=0)\nparser.add_argument(      \"--minor\", action=\"store\", dest=\"minor\", default=0)\nparser.add_argument(      \"--micro\", action=\"store\", dest=\"micro\", default=0)\nparser.add_argument(      \"--cc-version\", action=\"store_true\", dest=\"cc_version\")\n\nargs = parser.parse_args()\n\nversion = '.'.join(map(lambda x: str(x), [args.major, args.minor, args.micro]))\nhostname = '@'.join([args.user, args.host])\nplatform = ', '.join(filter(lambda x: x is not None, [args.arch, args.cpu]))\n\nprint('{} version {} ({}) ({}) #{}'.format(args.name, version, hostname, platform,\n                                           datetime.now().strftime(\"%c\")))\nif (args.cc_version):\n    print(cc_version('arm-none-eabi-gcc'))\n"
  },
  {
    "path": "scripts/gen-syscalls.py",
    "content": "#!/usr/bin/env python\n\nimport string\nimport argparse\n\nxs = [\n    # <pthread.h>\n    'pthread_exit',\n    'pthread_self',\n    'pthread_yield',\n    'pthread_create',\n    'pthread_join',\n    'pthread_detach',\n    'pthread_mutex_lock',\n    'pthread_mutex_unlock',\n    'pthread_cond_signal',\n    'pthread_cond_wait',\n\n    # <time.h>\n    'timer_create',\n    'timer_settime',\n    'timer_gettime',\n\n    # <unistd.h>\n    'msleep',\n    'sysconf',\n\n    # <signal.h>\n    'sigaction',\n    'kill',\n\n    # <fcntl.h>, <unistd.h>, <sys/mount.h>...\n    'open',\n    'close',\n    'read',\n    'write',\n    'lseek',\n    'stat',\n    'mount',\n    'readdir_r',\n\n    # <task.h>\n    'getpid',\n\n    # <sys/mman.h>\n    'mmap',\n    'munmap',\n]\n\nparser = argparse.ArgumentParser()\n\nparser.add_argument(\"--header\", action=\"store_true\", dest=\"header\")\nparser.add_argument(\"--source\", action=\"store_true\", dest=\"source\")\n\nargs = parser.parse_args()\n\nimport datetime\nprint('// GENERATED. DO NOT EDIT FROM HERE!')\nprint('// Change definitions in scripts/gen-syscalls.py')\nprint('// Created on ' +\n      datetime.datetime.now().strftime(\"%Y-%m-%d %H:%M\"))\n\nprint('')\n\n#for x in list(enumerate(xs)):\n#    name = 'sys_' + x[1]\n#    print('[{}] = {},'.format(name.upper(), name))\n\nif (args.header):\n    print('#ifndef KERNEL_SYSCALLS_H')\n    print('#define KERNEL_SYSCALLS_H')\n    for x in list(enumerate(xs)):\n        name = 'sys_' + x[1]\n        print('#define {} {}'.format(name.upper(), x[0]))\n    print('')\n    print('#endif /* !KERNEL_SYSCALLS_H */')\n\n\nif (args.source):\n    print('#include <kernel/syscalls.h>')\n    print('')\n    for x in list(enumerate(xs)):\n        name = 'sys_' + x[1]\n        print('int {}();'.format(name))\n    print('#define SYS_MAX 48')\n    print('')\n    print('void *syscall_vect[SYS_MAX] = {')\n    for x in list(enumerate(xs)):\n        name = 'sys_' + x[1]\n        print('    [{}] = {},'.format(name.upper(), name))\n    print('};');\n    print('')\n    print('int syscall_register(unsigned ix, void *(*fn)()) {')\n    print('    if (ix >= SYS_MAX) return -1;')\n    print('    syscall_vect[ix] = fn;')\n    print('return 0;')\n    print('}');\n"
  },
  {
    "path": "scripts/rstlint.py",
    "content": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n#\n# Copyright (C) 2009 Georg Brandl\n#\n# Check for stylistic and formal issues in .rst and .py\n# files included in the documentation.\n#\n# TODO: - wrong versions in versionadded/changed\n#       - wrong markup after versionchanged directive\n\nimport os\nimport re\nimport sys\nimport getopt\nfrom os.path import join, splitext, abspath, exists\nfrom collections import defaultdict\n\ndirectives = [\n    # standard docutils ones\n    'admonition', 'attention', 'caution', 'class', 'compound', 'container',\n    'contents', 'csv-table', 'danger', 'date', 'default-role', 'epigraph',\n    'error', 'figure', 'footer', 'header', 'highlights', 'hint', 'image',\n    'important', 'include', 'line-block', 'list-table', 'meta', 'note',\n    'parsed-literal', 'pull-quote', 'raw', 'replace',\n    'restructuredtext-test-directive', 'role', 'rubric', 'sectnum', 'sidebar',\n    'table', 'target-notes', 'tip', 'title', 'topic', 'unicode', 'warning',\n    # Sphinx and Python docs custom ones\n    'acks', 'attribute', 'autoattribute', 'autoclass', 'autodata',\n    'autoexception', 'autofunction', 'automethod', 'automodule', 'centered',\n    'cfunction', 'class', 'classmethod', 'cmacro', 'cmdoption', 'cmember',\n    'code-block', 'confval', 'cssclass', 'ctype', 'currentmodule', 'cvar',\n    'data', 'decorator', 'decoratormethod', 'deprecated-removed',\n    'deprecated(?!-removed)', 'describe', 'directive', 'doctest', 'envvar',\n    'event', 'exception', 'function', 'glossary', 'highlight', 'highlightlang',\n    'impl-detail', 'index', 'literalinclude', 'method', 'miscnews', 'module',\n    'moduleauthor', 'opcode', 'pdbcommand', 'productionlist',\n    'program', 'role', 'sectionauthor', 'seealso', 'sourcecode', 'staticmethod',\n    'tabularcolumns', 'testcode', 'testoutput', 'testsetup', 'toctree', 'todo',\n    'todolist', 'versionadded', 'versionchanged'\n]\n\nall_directives = '(' + '|'.join(directives) + ')'\nseems_directive_re = re.compile(r'(?<!\\.)\\.\\. %s([^a-z:]|:(?!:))' % all_directives)\ndefault_role_re = re.compile(r'(^| )`\\w([^`]*?\\w)?`($| )')\nleaked_markup_re = re.compile(r'[a-z]::\\s|`|\\.\\.\\s*\\w+:')\n\n\ncheckers = {}\n\nchecker_props = {'severity': 1, 'falsepositives': False}\n\n\ndef checker(*suffixes, **kwds):\n    \"\"\"Decorator to register a function as a checker.\"\"\"\n    def deco(func):\n        for suffix in suffixes:\n            checkers.setdefault(suffix, []).append(func)\n        for prop in checker_props:\n            setattr(func, prop, kwds.get(prop, checker_props[prop]))\n        return func\n    return deco\n\n\n@checker('.py', severity=4)\ndef check_syntax(fn, lines):\n    \"\"\"Check Python examples for valid syntax.\"\"\"\n    code = ''.join(lines)\n    if '\\r' in code:\n        if os.name != 'nt':\n            yield 0, '\\\\r in code file'\n        code = code.replace('\\r', '')\n    try:\n        compile(code, fn, 'exec')\n    except SyntaxError as err:\n        yield err.lineno, 'not compilable: %s' % err\n\n\n@checker('.rst', severity=2)\ndef check_suspicious_constructs(fn, lines):\n    \"\"\"Check for suspicious reST constructs.\"\"\"\n    inprod = False\n    for lno, line in enumerate(lines):\n        if seems_directive_re.search(line):\n            yield lno+1, 'comment seems to be intended as a directive'\n        if '.. productionlist::' in line:\n            inprod = True\n        elif not inprod and default_role_re.search(line):\n            yield lno+1, 'default role used'\n        elif inprod and not line.strip():\n            inprod = False\n\n\n@checker('.py', '.rst')\ndef check_whitespace(fn, lines):\n    \"\"\"Check for whitespace and line length issues.\"\"\"\n    for lno, line in enumerate(lines):\n        if '\\r' in line:\n            yield lno+1, '\\\\r in line'\n        if '\\t' in line:\n            yield lno+1, 'OMG TABS!!!1'\n        if line[:-1].rstrip(' \\t') != line[:-1]:\n            yield lno+1, 'trailing whitespace'\n\n\n@checker('.rst', severity=0)\ndef check_line_length(fn, lines):\n    \"\"\"Check for line length; this checker is not run by default.\"\"\"\n    for lno, line in enumerate(lines):\n        if len(line) > 81:\n            # don't complain about tables, links and function signatures\n            if line.lstrip()[0] not in '+|' and \\\n               'http://' not in line and \\\n               not line.lstrip().startswith(('.. function',\n                                             '.. method',\n                                             '.. cfunction')):\n                yield lno+1, \"line too long\"\n\n\n@checker('.html', severity=2, falsepositives=True)\ndef check_leaked_markup(fn, lines):\n    \"\"\"Check HTML files for leaked reST markup; this only works if\n    the HTML files have been built.\n    \"\"\"\n    for lno, line in enumerate(lines):\n        if leaked_markup_re.search(line):\n            yield lno+1, 'possibly leaked markup: %r' % line\n\n\ndef main(argv):\n    usage = '''\\\nUsage: %s [-v] [-f] [-s sev] [-i path]* [path]\n\nOptions:  -v       verbose (print all checked file names)\n          -f       enable checkers that yield many false positives\n          -s sev   only show problems with severity >= sev\n          -i path  ignore subdir or file path\n''' % argv[0]\n    try:\n        gopts, args = getopt.getopt(argv[1:], 'vfs:i:')\n    except getopt.GetoptError:\n        print(usage)\n        return 2\n\n    verbose = False\n    severity = 1\n    ignore = []\n    falsepos = False\n    for opt, val in gopts:\n        if opt == '-v':\n            verbose = True\n        elif opt == '-f':\n            falsepos = True\n        elif opt == '-s':\n            severity = int(val)\n        elif opt == '-i':\n            ignore.append(abspath(val))\n\n    if len(args) == 0:\n        path = '.'\n    elif len(args) == 1:\n        path = args[0]\n    else:\n        print(usage)\n        return 2\n\n    if not exists(path):\n        print('Error: path %s does not exist' % path)\n        return 2\n\n    count = defaultdict(int)\n\n    for root, dirs, files in os.walk(path):\n        # ignore subdirs in ignore list\n        if abspath(root) in ignore:\n            del dirs[:]\n            continue\n\n        for fn in files:\n            fn = join(root, fn)\n            if fn[:2] == './':\n                fn = fn[2:]\n\n            # ignore files in ignore list\n            if abspath(fn) in ignore:\n                continue\n\n            ext = splitext(fn)[1]\n            checkerlist = checkers.get(ext, None)\n            if not checkerlist:\n                continue\n\n            if verbose:\n                print('Checking %s...' % fn)\n\n            try:\n                with open(fn, 'r', encoding='utf-8') as f:\n                    lines = list(f)\n            except (IOError, OSError) as err:\n                print('%s: cannot open: %s' % (fn, err))\n                count[4] += 1\n                continue\n\n            for checker in checkerlist:\n                if checker.falsepositives and not falsepos:\n                    continue\n                csev = checker.severity\n                if csev >= severity:\n                    for lno, msg in checker(fn, lines):\n                        print('[%d] %s:%d: %s' % (csev, fn, lno, msg))\n                        count[csev] += 1\n    if verbose:\n        print()\n    if not count:\n        if severity > 1:\n            print('No problems with severity >= %d found.' % severity)\n        else:\n            print('No problems found.')\n    else:\n        for severity in sorted(count):\n            number = count[severity]\n            print('%d problem%s with severity %d found.' %\n                  (number, number > 1 and 's' or '', severity))\n    return int(bool(count))\n\n\nif __name__ == '__main__':\n    sys.exit(main(sys.argv))\n\n"
  },
  {
    "path": "tests/Makefile",
    "content": "CSRC = $(wildcard tests/$(TEST)/*.c)\nSSRC = $(wildcard tests/$(TEST)/*.S)\n\nTEST_OBJ = $(wildcard tests/$(TEST)/*.o)\nLIB_OBJ = $(wildcard tests/lib/*.o)\n\nCFLAGS = -Itests/lib\n\n# Build a romFS drive if there is a /data directory at the root\n# of the testcase directory.\nifneq (\"$(wildcard tests/$(TEST)/data)\",\"\")\n  OBJS = tests/$(TEST)/sda1.o\nendif\n\ninclude Makefile\n\n%sda1:\n\t$(VECHO) \"GENROMFS $@\"\n\t$(Q)genromfs -f tests/$(TEST)/sda1 -d tests/$(TEST)/data -V sda1\n\n%sda1.o: %sda1\n\t$(VECHO) \"BUILDFS\\t$@\"\n\t$(Q)$(OBJCOPY) -I binary -O elf32-littlearm -B arm \\\n\t    --rename-section .data=.rodata \\\n\t    --redefine-sym _binary_$(subst /,_,$<)_start=_binary_sda1_start \\\n\t    --redefine-sym _binary_$(subst /,_,$<)_end=_binary_sda1_end \\\n\t    --redefine-sym _binary_$(subst /,_,$<)_size=_binary_sda1_size \\\n\t    tests/$(TEST)/sda1 tests/$(TEST)/sda1.o\n\nclean:\n\trm -f $(TEST_OBJ) $(LIB_OBJ) tests/$(TEST)/sda1 tests/$(TEST)/sda1.o\n\nclean_test:\n\trm -f $(NAME).elf $(TEST_OBJ) tests/$(TEST)/sda1 tests/$(TEST)/sda1.o\n"
  },
  {
    "path": "tests/__init__.py",
    "content": ""
  },
  {
    "path": "tests/__main__.py",
    "content": "import sys\nif sys.version_info < (3, 5):\n    print (\"Only support python version >= 3.5\")\n    exit(1)\n\nimport argparse\nimport tests.runner as runner\nfrom tests.runner import PikoTest\n\n\nif __name__ == '__main__':\n    parser = argparse.ArgumentParser()\n    parser.add_argument('test', nargs='*')\n    parser.add_argument('-v', '--verbose', action='store_true', help='Verbose output')\n    parser.add_argument('--quiet', action='store_true', default=False,\n                        help='no output unless one or more tests fail')\n    parser.add_argument('--debug', action='store_true', help='Run test and wait for gdb connect')\n    parser.add_argument('-p', '--platform', default='stm32p103')\n    parser.add_argument('--qemu', default='qemu-system-arm')\n    parser.add_argument('--cc', default='arm-none-eabi-gcc')\n    parser.add_argument('--timeout', type=int, default=60)\n    parser.add_argument('--slowest', action='store_true', dest='print_slow',\n                        help='print the slowest 10 tests')\n\n    ns = parser.parse_args()\n    if ns.test:\n        PikoTest().main(ns.test, ns)\n    else:\n        # This will include all test\n        runner.main(ns)\n"
  },
  {
    "path": "tests/bitops_1/main.c",
    "content": "#include <kernel/bitops.h>\n\n#include \"unit.h\"\n\nint main()\n{\n    unsigned long j;\n    unsigned long k[2] = {0, 1};\n\n    /* test first bit set */\n    for (unsigned long i = 0; i < 32; i++) {\n        j = 1 << i;\n        if (find_first_bit(&j, 32) != i)\n            TEST_EXIT(1);\n    }\n\n    /* test first zero bit */\n    for (unsigned long i = 0; i < 32; i++) {\n        j = ~(1 << i);\n        if (find_first_zero_bit(&j, 32) != i)\n            TEST_EXIT(1);\n    }\n\n    /* test boundaries */\n    if (find_first_bit(k, 32) != 32)\n        TEST_EXIT(1);\n    if (find_first_bit(k, 33) != 32)\n        TEST_EXIT(1);\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/cond_1/main.c",
    "content": "/* simple test for condition variable */\n\n#include <pthread.h>\n#include <stddef.h>\n#include <stdlib.h>\n#include <sys/cdefs.h>\n\n#include \"kernel.h\"\n#include \"unit.h\"\n\npthread_cond_t cond;\npthread_mutex_t mutex;\n\nvoid *threadfunc(void *parm)\n{\n    (void) parm;\n\n    pthread_mutex_lock(&mutex);\n\n    printk(\"Wait for signal\\n\");\n    pthread_cond_wait(&cond, &mutex);\n    printk(\"Thread awake, finish work!\\n\");\n\n    pthread_mutex_unlock(&mutex);\n\n    return NULL;\n}\n\nint main()\n{\n    int rc = 0;\n    pthread_t threadid;\n\n    printk(\"Enter Testcase\\n\");\n\n    pthread_cond_init(&cond, NULL);\n    pthread_mutex_init(&mutex, NULL);\n\n    rc = pthread_create(&threadid, NULL, threadfunc, NULL);\n    if (rc) {\n        printk(\"failed: can't create new posix thread.\\n\");\n        TEST_EXIT(1);\n    }\n\n    pthread_yield();\n\n    printk(\"Wake up a worker, work to do...\\n\");\n\n    rc = pthread_mutex_lock(&mutex);\n\n    rc = pthread_cond_signal(&cond);\n    // checkResults(\"pthread_cond_broadcast()\\n\", rc);\n\n    rc = pthread_mutex_unlock(&mutex);\n    // checkResults(\"pthread_mutex_unlock()\\n\", rc);\n    // sleep(5);  /* Sleep is not a very robust way to serialize threads */\n\n    printk(\"Main completed\\n\");\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/cond_2/main.c",
    "content": "/* http://www.ibm.com/support/knowledgecenter/ssw_i5_54/apis/users_76.htm */\n\n#define _MULTI_THREADED\n#include <pthread.h>\n#include <stdio.h>\n#include \"kernel.h\"\n#include \"unit.h\"\n\n/* For safe condition variable usage, must use a boolean predicate and  */\n/* a mutex with the condition.                                          */\nint workToDo = 0;\npthread_cond_t cond;\npthread_mutex_t mutex;\n\n#define NTHREADS 2\n\nunsigned int msleep(unsigned int msecs);\n\nint checkResults(const char *s, int rc)\n{\n    if (rc) {\n        printk(\"error: %s\\n\", s);\n        TEST_EXIT(1);\n    }\n\n    return 0;\n}\n\nvoid *threadfunc(void *parm)\n{\n    int rc;\n    (void) parm;\n\n    while (1) {\n        /* Usually worker threads will loop on these operations */\n        rc = pthread_mutex_lock(&mutex);\n        checkResults(\"pthread_mutex_lock()\\n\", rc);\n\n        while (!workToDo) {\n            printk(\"Thread blocked\\n\");\n            rc = pthread_cond_wait(&cond, &mutex);\n            checkResults(\"pthread_cond_wait()\\n\", rc);\n        }\n        printk(\"Thread awake, finish work!\\n\");\n\n        /* Under protection of the lock, complete or remove the work     */\n        /* from whatever worker queue we have. Here it is simply a flag  */\n        workToDo = 0;\n\n        rc = pthread_mutex_unlock(&mutex);\n        checkResults(\"pthread_mutex_lock()\\n\", rc);\n    }\n    return NULL;\n}\n\nint main()\n{\n    int rc = 0;\n    int i;\n    pthread_t threadid[NTHREADS];\n\n    pthread_cond_init(&cond, NULL);\n    pthread_mutex_init(&mutex, NULL);\n\n    printk(\"Enter Testcase - %s\\n\", \"IBM pthread_cond_*()\");\n\n    printk(\"Create %d threads\\n\", NTHREADS);\n    for (i = 0; i < NTHREADS; ++i) {\n        rc = pthread_create(&threadid[i], NULL, threadfunc, NULL);\n        checkResults(\"pthread_create()\\n\", rc);\n    }\n\n    msleep(40); /* Sleep is not a very robust way to serialize threads   */\n\n    for (i = 0; i < 5; ++i) {\n        printk(\"Wake up a worker, work to do...\\n\");\n\n        rc = pthread_mutex_lock(&mutex);\n        checkResults(\"pthread_mutex_lock()\\n\", rc);\n\n        /* In the real world, all the threads might be busy, and        */\n        /* we would add work to a queue instead of simply using a flag  */\n        /* In that case the boolean predicate might be some boolean     */\n        /* statement like: if (the-queue-contains-work)                 */\n        if (workToDo) {\n            printk(\"Work already present, likely threads are busy\\n\");\n        }\n        workToDo = 1;\n        rc = pthread_cond_signal(&cond);\n        checkResults(\"pthread_cond_broadcast()\\n\", rc);\n\n        rc = pthread_mutex_unlock(&mutex);\n        checkResults(\"pthread_mutex_unlock()\\n\", rc);\n        msleep(40); /* Sleep is not a very robust way to serialize threads */\n    }\n\n    printk(\"Main completed\\n\");\n    TEST_EXIT(0);\n    return 0;\n}\n"
  },
  {
    "path": "tests/cond_3/main.c",
    "content": "/* http://www.qnx.com/developers/docs/660/index.jsp?topic=%2Fcom.qnx.doc.neutrino.getting_started%2Ftopic%2Fs1_procs_condvar.html\n */\n\n/*\n * cp1.c\n */\n\n#include <stdio.h>\n#include <pthread.h>\n#include <unistd.h>\n\n#include \"unit.h\"\n\nint count = 0;\nint data_ready = 0;\npthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;\npthread_cond_t condvar;\n\nextern unsigned int msleep(unsigned int msec);\n\nvoid *consumer(void *notused)\n{\n    (void) notused;\n\n    printk(\"In consumer thread...\\n\");\n    while (1) {\n        count++;\n        pthread_mutex_lock(&mutex);\n        while (!data_ready) {\n            pthread_cond_wait(&condvar, &mutex);\n        }\n        // process data\n        printk(\"consumer:  got data from producer\\n\");\n        data_ready = 0;\n        pthread_cond_signal(&condvar);\n        pthread_mutex_unlock(&mutex);\n    }\n}\n\nvoid *producer(void *notused)\n{\n    (void) notused;\n\n    printk(\"In producer thread...\\n\");\n    while (1) {\n        // get data from hardware\n        // we'll simulate this with a sleep (1)\n        msleep(180);\n        printk(\"producer:  got data from h/w\\n\");\n        pthread_mutex_lock(&mutex);\n        while (data_ready) {\n            pthread_cond_wait(&condvar, &mutex);\n        }\n        data_ready = 1;\n        pthread_cond_signal(&condvar);\n        pthread_mutex_unlock(&mutex);\n    }\n}\n\nint main(void)\n{\n    printk(\"Starting consumer/producer example...\\n\");\n\n    pthread_cond_init(&condvar, NULL);\n\n    // create the producer and consumer threads\n    pthread_create(NULL, NULL, producer, NULL);\n    pthread_create(NULL, NULL, consumer, NULL);\n\n    while (count < 5)\n        pthread_yield();\n    printk(\"Bye-bye!\\n\");\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/fs_1/main.c",
    "content": "/* test /dev/random */\n\n#include <fcntl.h>\n\n#include <kernel/fs.h>\n#include <kernel/kernel.h>\n\n#include \"unit.h\"\n\nint main()\n{\n    unsigned int n, p = 0;\n\n    int fd = open(\"/dev/random\", 0);\n    if (fd < 0) {\n        printk(\"error: failed to open /dev/random\\n\");\n        TEST_EXIT(1);\n    }\n\n    for (int j = 0; j < 1000; j++) {\n        read(fd, &n, 4);\n        if (j < 40)\n            printk(\"%08x\\n\", n);\n        if (n == p) {\n            printk(\"error: got same random number %d twice in a row\\n\", n);\n            TEST_EXIT(1);\n        }\n        p = n;\n    }\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/fs_2/main.c",
    "content": "/* test /dev/zero */\n\n#include <fcntl.h>\n#include <string.h>\n\n#include <kernel/fs.h>\n#include <kernel/kernel.h>\n\n#include \"unit.h\"\n\nchar buf[128];\n\nint main()\n{\n    int fd = open(\"/dev/zero\", 0);\n    if (fd < 0) {\n        printk(\"error: failed to open /dev/zero\\n\");\n        TEST_EXIT(1);\n    }\n\n    memset(buf, 0xff, 128);\n    read(fd, &buf, 128);\n    for (int j = 0; j < 128; j++) {\n        if (buf[j]) {\n            printk(\"error: got a non-zero value\\n\");\n            TEST_EXIT(1);\n        }\n    }\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/fs_3/data/id_rsa",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIBOgIBAAJBAI/rmTOBknHe2ro+8sUNl1MjNTRopU1/WJTcYvsrFML10Z2IBrFi\nfc3Q1x92uTvyFU21cn+/ekU8L+8qK+V98fMCAwEAAQJAe4uw+REa6Nt5Ana1KsmP\nBNRtgO7wMEXIgglqgtipuu3CE9CIZ4OyrvELNbI6RiczGYgMmUHcWEmLuBZ+tjQu\n4QIhAORy2CQio1J3O2gGdkk2eY2cLIzoMEdig9su/NILvSqDAiEAoUcD1FeM1iub\nSiUq3ZDIf1FFTgtv9Si0lzJyBY+IP9ECIHi8zclDWUhDZe1TxP5qwRF74fvS13lS\n8tdL3SjyNVcbAiEAhZU3o8r8mWy3DFvqvGiu2V3shK9OhYa4xj9/WAHB/fECIH9u\nNq1Ww0stz2Oq7Wpn7ywcNemkot/+sO8l8gxXPcqw\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "tests/fs_3/data/id_rsa.pub",
    "content": "-----BEGIN PUBLIC KEY-----\nMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAI/rmTOBknHe2ro+8sUNl1MjNTRopU1/\nWJTcYvsrFML10Z2IBrFifc3Q1x92uTvyFU21cn+/ekU8L+8qK+V98fMCAwEAAQ==\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "tests/fs_3/main.c",
    "content": "/* test simple romFS */\n\n#include <fcntl.h>\n#include <string.h>\n#include <unistd.h>\n\n#include <piko/sys/mount.h>\n\n#include <kernel/fs.h>\n#include <kernel/kernel.h>\n\n#include <drivers/mtd/mtd.h>\n\n#include \"unit.h\"\n\nextern char _binary_sda1_start;\nstruct mtd_info mtd1;\n\nstatic struct inode inode = {\n    .i_private = &mtd1,\n};\n\nvoid flash_init(void)\n{\n    struct dentry dentry = {.d_inode = &inode, .d_name = \"mtd1\"};\n\n    printk(\"Creating MTD device %s\\n\", dentry.d_name);\n    if (mtdram_init_device(&mtd1, &_binary_sda1_start, 1024, dentry.d_name))\n        printk(\"error: mtdram init device failed\\n\");\n    vfs_link(NULL, dev_inode(), &dentry);\n}\n\nint main()\n{\n    int fd;\n    char buffer[128];\n\n    init_tmpfs_inode(&inode);\n    flash_init();\n    mount(\"/dev/mtd1\", \"/dev/flash\", \"romfs\", 0, 0);\n\n    fd = open(\"/dev/flash/id_rsa.pub\", 0);\n    if (fd < 0) {\n        printk(\"error: failed to open /data/id_rsa.pub\\n\");\n        TEST_EXIT(1);\n    }\n\n    memset(buffer, 0, 128);\n    read(fd, buffer, 27);\n    printk(\"read(): %s\\n\", buffer);\n    if (strncmp(buffer, \"-----BEGIN PUBLIC KEY-----\", 26))\n        TEST_EXIT(1);\n\n    memset(buffer, 0, 128);\n    read(fd, buffer, 65);\n    printk(\"read(): %s\\n\", buffer);\n    if (strncmp(\n            buffer,\n            \"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAI/rmTOBknHe2ro+8sUNl1MjNTRopU1/\",\n            64))\n        TEST_EXIT(1);\n\n    memset(buffer, 0, 128);\n    read(fd, buffer, 65);\n    printk(\"read(): %s\\n\", buffer);\n    if (strncmp(\n            buffer,\n            \"WJTcYvsrFML10Z2IBrFifc3Q1x92uTvyFU21cn+/ekU8L+8qK+V98fMCAwEAAQ==\",\n            64))\n        TEST_EXIT(1);\n\n    /* rewind the file */\n    lseek(fd, 0, SEEK_SET);\n    memset(buffer, 0, 128);\n    read(fd, buffer, 27);\n    printk(\"read(): %s\\n\", buffer);\n    if (strncmp(buffer, \"-----BEGIN PUBLIC KEY-----\", 26))\n        TEST_EXIT(1);\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/fs_4/data/id_rsa",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIBOgIBAAJBAI/rmTOBknHe2ro+8sUNl1MjNTRopU1/WJTcYvsrFML10Z2IBrFi\nfc3Q1x92uTvyFU21cn+/ekU8L+8qK+V98fMCAwEAAQJAe4uw+REa6Nt5Ana1KsmP\nBNRtgO7wMEXIgglqgtipuu3CE9CIZ4OyrvELNbI6RiczGYgMmUHcWEmLuBZ+tjQu\n4QIhAORy2CQio1J3O2gGdkk2eY2cLIzoMEdig9su/NILvSqDAiEAoUcD1FeM1iub\nSiUq3ZDIf1FFTgtv9Si0lzJyBY+IP9ECIHi8zclDWUhDZe1TxP5qwRF74fvS13lS\n8tdL3SjyNVcbAiEAhZU3o8r8mWy3DFvqvGiu2V3shK9OhYa4xj9/WAHB/fECIH9u\nNq1Ww0stz2Oq7Wpn7ywcNemkot/+sO8l8gxXPcqw\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "tests/fs_4/data/id_rsa.pub",
    "content": "-----BEGIN PUBLIC KEY-----\nMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAI/rmTOBknHe2ro+8sUNl1MjNTRopU1/\nWJTcYvsrFML10Z2IBrFifc3Q1x92uTvyFU21cn+/ekU8L+8qK+V98fMCAwEAAQ==\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "tests/fs_4/main.c",
    "content": "/* test overreading a file */\n\n#include <fcntl.h>\n#include <string.h>\n#include <unistd.h>\n\n#include <piko/sys/mount.h>\n\n#include <kernel/fs.h>\n#include <kernel/kernel.h>\n\n#include <drivers/mtd/mtd.h>\n\n#include \"unit.h\"\n\nextern char _binary_sda1_start;\nstruct mtd_info mtd1;\n\nstatic struct inode inode = {\n    .i_private = &mtd1,\n};\n\nvoid flash_init(void)\n{\n    struct dentry dentry = {.d_inode = &inode, .d_name = \"mtd1\"};\n\n    printk(\"Creating MTD device %s\\n\", dentry.d_name);\n    if (mtdram_init_device(&mtd1, &_binary_sda1_start, 1024, dentry.d_name))\n        printk(\"error: mtdram init device failed\\n\");\n    vfs_link(NULL, dev_inode(), &dentry);\n}\n\nint main()\n{\n    int fd;\n    char buffer[256];\n\n    memset(buffer, 0, 256);\n    init_tmpfs_inode(&inode);\n    flash_init();\n    mount(\"/dev/mtd1\", \"/dev/flash\", \"romfs\", 0, 0);\n\n    /* file contains -Lorem ipsum\\n- */\n    fd = open(\"/dev/flash/id_rsa.pub\", 0);\n    if (fd < 0) {\n        printk(\"error: failed to open /data/id_rsa.pub\\n\");\n        TEST_EXIT(1);\n    }\n\n    /* overread the file */\n    int r = read(fd, buffer, 256);\n    if (r != 182) {\n        printk(\"error: read incorrect number of char (%d)\\n\", r);\n        TEST_EXIT(1);\n    }\n    if (strncmp(buffer, \"-----BEGIN PUBLIC KEY-----\\n\", 26))\n        TEST_EXIT(1);\n\n    /* try to read again */\n    r = read(fd, buffer, 1);\n    if (r != 0) {\n        printk(\"error: read incorrect number of char (%d)\\n\", r);\n        TEST_EXIT(1);\n    }\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/fs_5/main.c",
    "content": "/* test multiple opening/closing /dev/random */\n\n#include <fcntl.h>\n#include <unistd.h>\n\n#include <kernel/fs.h>\n#include <kernel/kernel.h>\n\n#include \"unit.h\"\n\nint main()\n{\n    int fd;\n\n    for (int i = 0; i < 10000; i++) {\n        /* printk(\"% 3d/9999\\n\", i); */\n        fd = open(\"/dev/random\", 0);\n        if (fd < 0) {\n            printk(\"error: failed to open /dev/random\\n\");\n            TEST_EXIT(1);\n        }\n        if (close(fd)) {\n            printk(\"error: failed to close /dev/random\\n\");\n            TEST_EXIT(1);\n        }\n    }\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/fs_6/data/id_rsa",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIBOgIBAAJBAI/rmTOBknHe2ro+8sUNl1MjNTRopU1/WJTcYvsrFML10Z2IBrFi\nfc3Q1x92uTvyFU21cn+/ekU8L+8qK+V98fMCAwEAAQJAe4uw+REa6Nt5Ana1KsmP\nBNRtgO7wMEXIgglqgtipuu3CE9CIZ4OyrvELNbI6RiczGYgMmUHcWEmLuBZ+tjQu\n4QIhAORy2CQio1J3O2gGdkk2eY2cLIzoMEdig9su/NILvSqDAiEAoUcD1FeM1iub\nSiUq3ZDIf1FFTgtv9Si0lzJyBY+IP9ECIHi8zclDWUhDZe1TxP5qwRF74fvS13lS\n8tdL3SjyNVcbAiEAhZU3o8r8mWy3DFvqvGiu2V3shK9OhYa4xj9/WAHB/fECIH9u\nNq1Ww0stz2Oq7Wpn7ywcNemkot/+sO8l8gxXPcqw\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "tests/fs_6/data/id_rsa.pub",
    "content": "-----BEGIN PUBLIC KEY-----\nMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAI/rmTOBknHe2ro+8sUNl1MjNTRopU1/\nWJTcYvsrFML10Z2IBrFifc3Q1x92uTvyFU21cn+/ekU8L+8qK+V98fMCAwEAAQ==\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "tests/fs_6/main.c",
    "content": "/* open files in romFS multiple times */\n\n#include <fcntl.h>\n\n#include <piko/sys/mount.h>\n\n#include <kernel/fs.h>\n#include <kernel/kernel.h>\n\n#include <drivers/mtd/mtd.h>\n\n#include \"unit.h\"\n\nextern char _binary_sda1_start;\nstruct mtd_info mtd1;\n\nstatic struct inode inode = {\n    .i_private = &mtd1,\n};\n\nvoid flash_init(void)\n{\n    struct dentry dentry = {.d_inode = &inode, .d_name = \"mtd1\"};\n\n    printk(\"Creating MTD device %s\\n\", dentry.d_name);\n    if (mtdram_init_device(&mtd1, &_binary_sda1_start, 1024, dentry.d_name))\n        printk(\"error: mtdram init device failed\\n\");\n    vfs_link(NULL, dev_inode(), &dentry);\n}\n\nint main()\n{\n    int fd1, fd2;\n\n    init_tmpfs_inode(&inode);\n    flash_init();\n    mount(\"/dev/mtd1\", \"/dev/flash\", \"romfs\", 0, 0);\n\n    for (int i = 0; i < 1000; i++) {\n        fd1 = open(\"/dev/flash/id_rsa\", 0);\n        fd2 = open(\"/dev/flash/id_rsa.pub\", 0);\n        if (fd1 < 0 || fd2 < 0) {\n            printk(\"error: failed to open file in flash\\n\");\n            TEST_EXIT(1);\n        }\n        close(fd1);\n        close(fd2);\n    }\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/fs_7/data/.ssh/id_rsa",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIBOgIBAAJBAI/rmTOBknHe2ro+8sUNl1MjNTRopU1/WJTcYvsrFML10Z2IBrFi\nfc3Q1x92uTvyFU21cn+/ekU8L+8qK+V98fMCAwEAAQJAe4uw+REa6Nt5Ana1KsmP\nBNRtgO7wMEXIgglqgtipuu3CE9CIZ4OyrvELNbI6RiczGYgMmUHcWEmLuBZ+tjQu\n4QIhAORy2CQio1J3O2gGdkk2eY2cLIzoMEdig9su/NILvSqDAiEAoUcD1FeM1iub\nSiUq3ZDIf1FFTgtv9Si0lzJyBY+IP9ECIHi8zclDWUhDZe1TxP5qwRF74fvS13lS\n8tdL3SjyNVcbAiEAhZU3o8r8mWy3DFvqvGiu2V3shK9OhYa4xj9/WAHB/fECIH9u\nNq1Ww0stz2Oq7Wpn7ywcNemkot/+sO8l8gxXPcqw\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "tests/fs_7/data/.ssh/id_rsa.pub",
    "content": "-----BEGIN PUBLIC KEY-----\nMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAI/rmTOBknHe2ro+8sUNl1MjNTRopU1/\nWJTcYvsrFML10Z2IBrFifc3Q1x92uTvyFU21cn+/ekU8L+8qK+V98fMCAwEAAQ==\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "tests/fs_7/main.c",
    "content": "/* test directory hierarchy in RomFS */\n\n#include <fcntl.h>\n#include <string.h>\n#include <unistd.h>\n\n#include <piko/sys/mount.h>\n\n#include <kernel/fs.h>\n#include <kernel/kernel.h>\n\n#include <drivers/mtd/mtd.h>\n\n#include \"unit.h\"\n\nextern char _binary_sda1_start;\nstruct mtd_info mtd1;\n\nstatic struct inode inode = {\n    .i_private = &mtd1,\n};\n\nvoid flash_init(void)\n{\n    struct dentry dentry = {.d_inode = &inode, .d_name = \"mtd1\"};\n\n    printk(\"Creating MTD device %s\\n\", dentry.d_name);\n    if (mtdram_init_device(&mtd1, &_binary_sda1_start, 1024, dentry.d_name))\n        printk(\"error: mtdram init device failed\\n\");\n    vfs_link(NULL, dev_inode(), &dentry);\n}\n\nint main()\n{\n    int fd;\n    const char filename[] = \"/dev/flash/.ssh/id_rsa.pub\";\n    char buffer[32];\n\n    init_tmpfs_inode(&inode);\n    flash_init();\n    mount(\"/dev/mtd1\", \"/dev/flash\", \"romfs\", 0, 0);\n\n    fd = open(filename, 0);\n    if (fd < 0) {\n        printk(\"error: failed to open %s\\n\", filename);\n        TEST_EXIT(1);\n    }\n\n    memset(buffer, 0, 32);\n    if (read(fd, buffer, 11) != 11)\n        TEST_EXIT(1);\n    if (strncmp(buffer, \"-----BEGIN\", 10))\n        TEST_EXIT(1);\n\n    memset(buffer, 0, 32);\n    if (read(fd, buffer, 10) != 10)\n        TEST_EXIT(1);\n    if (strncmp(buffer, \"PUBLIC KEY\", 10))\n        TEST_EXIT(1);\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/getpid_1/main.c",
    "content": "/* simple test for getpid */\n\n#include <unistd.h>\n#include \"unit.h\"\n\nint main()\n{\n    pid_t pid = getpid();\n\n    if (!pid)\n        TEST_EXIT(1);\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/itoa_1/main.c",
    "content": "#include \"unit.h\"\n#include \"utils.h\"\n#include <string.h>\n\n#include <kernel/kernel.h>\n\nint itoa_base_00(void)\n{\n    char buf[64];\n    if (strcmp(\"0\", itoa_base(0, buf, 1)))\n        return -1;\n    return 0;\n}\n\nint itoa_base_01(void)\n{\n    char buf[64];\n    if (strcmp(\"0\", itoa_base(0, buf, 8)))\n        return -1;\n    return 0;\n}\n\nint itoa_base_02(void)\n{\n    char buf[64];\n    if (strcmp(\"1234567890\", itoa_base(1234567890, buf, 10)))\n        return -1;\n    return 0;\n}\n\nint itoa_base_03(void)\n{\n    char buf[64];\n    if (strcmp(\"20\", itoa_base(32, buf, 16)))\n        return -1;\n    return 0;\n}\n\nint itoa_base_04(void)\n{\n    char buf[64];\n    if (strcmp(\"21\", itoa_base(33, buf, 16)))\n        return -1;\n    return 0;\n}\n\nint itoa_base_05(void)\n{\n    char buf[64];\n    if (itoa_base(458, buf, 2048))\n        return -1;\n    return 0;\n}\n\nint itoa_base_06(void)\n{\n    char buf[64];\n    if (strcmp(\"ffffffff\", itoa_base(0xffffffff, buf, 16)))\n        return -1;\n    return 0;\n}\n\nint itoa_base_07(void)\n{\n    char buf[64];\n    if (strcmp(\"-1\", itoa_base(0xffffffff, buf, 10)))\n        return -1;\n    return 0;\n}\n\nint itoa_base_08(void)\n{\n    char buf[64];\n    if (strcmp(\"1000\", itoa_base(0x1000, buf, 16)))\n        return -1;\n    return 0;\n}\n\nint itoa_base_09(void)\n{\n    char buf[64];\n    if (strcmp(\"-752\", itoa_base(-752, buf, 10)))\n        return -1;\n    return 0;\n}\n\nint itoa_base_10(void)\n{\n    char buf[64];\n    if (strcmp(\"0\", itoa_base(0, buf, 10)))\n        return -1;\n    return 0;\n}\n\nint main()\n{\n    int status = 0;\n    int (*test[])(void) = {itoa_base_00, itoa_base_01, itoa_base_02,\n                           itoa_base_03, itoa_base_04, itoa_base_05,\n                           itoa_base_06, itoa_base_07, itoa_base_08,\n                           itoa_base_09, itoa_base_10};\n\n    for (int i = 0; i < (int) ARRAY_SIZE(test); i++) {\n        int r = test[i]();\n        /* printk(\"itoa test #%d %s\\n\", i, r ? \"(failed)\" : \"\"); */\n        status += r;\n    }\n    if (status)\n        TEST_EXIT(1);\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/lib/unit.h",
    "content": "#ifndef TEST_LIB_UNIT_H\n#define TEST_LIB_UNIT_H\n\n#include \"kernel.h\"\n\n#define C_GREEN \"\\033[1;32m\"\n#define C_RED \"\\033[1;31m\"\n#define C_NORMAL \"\\033[0m\"\n\n#define printk_green(format, ...) \\\n    printk(C_GREEN format C_NORMAL, ##__VA_ARGS__);\n\n#define printk_red(format, ...) printk(C_RED format C_NORMAL, ##__VA_ARGS__);\n\n#include <arch/semihosting.h>\n\n#define TEST_EXIT(status)                            \\\n    ({                                               \\\n        if (status) {                                \\\n            printk_red(\"test failed: %d\", (status)); \\\n            printk(\"\\n\");                            \\\n        } else {                                     \\\n            printk_green(\"test passed\");             \\\n            printk(\"\\n\");                            \\\n        }                                            \\\n        v7m_semihost_exit(status);                   \\\n    })\n\n#endif /* !TEST_LIB_UNIT_H */\n"
  },
  {
    "path": "tests/malloc_1/main.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n\n#include \"unit.h\"\n#include \"kernel.h\"\n\nint main()\n{\n    char *p[3], *q[3];\n\n    p[0] = malloc(48);\n    p[1] = malloc(64);\n    p[2] = malloc(96);\n\n    for (int i = 0; i < 3; i++)\n        printk(\"allocated %p\\n\", p[i]);\n\n    memset(p[0], 'a', 48);\n    memset(p[1], 'b', 64);\n    memset(p[2], 'c', 96);\n\n    for (int i = 0; i < 48; i++) {\n        if (p[0][i] != 'a')\n            TEST_EXIT(1);\n    }\n    for (int i = 0; i < 64; i++) {\n        if (p[1][i] != 'b')\n            TEST_EXIT(1);\n    }\n    for (int i = 0; i < 96; i++) {\n        if (p[2][i] != 'c')\n            TEST_EXIT(1);\n    }\n\n    /* free all pointers */\n    free(p[0]);\n    free(p[1]);\n    free(p[2]);\n\n    /* Realloc the same memory mapping, should return the same allocation\n     * mapping.    */\n    q[0] = malloc(48);\n    q[1] = malloc(64);\n    q[2] = malloc(96);\n\n    for (int i = 0; i < 3; i++)\n        printk(\"allocated %p\\n\", p[i]);\n\n    for (int i = 0; i < 3; i++) {\n        if (p[i] != q[i])\n            TEST_EXIT(1);\n    }\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/mm_1/main.c",
    "content": "#include <stddef.h>\n\n#include <kernel/mm/page.h>\n#include \"unit.h\"\n\nint main()\n{\n    void *p, *q;\n    unsigned long order = size_to_page_order(256);\n\n    if ((p = alloc_pages(order)) == NULL)\n        TEST_EXIT(1);\n    free_pages((unsigned long) p, order);\n\n    /* same page should be reallocated */\n    if ((q = alloc_pages(order)) != p)\n        TEST_EXIT(1);\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/mm_2/main.c",
    "content": "#include <stddef.h>\n\n#include <kernel/mm/page.h>\n#include \"unit.h\"\n\nint main()\n{\n    void *p[14];\n    int sz[] = {256, 1024, 2048, 256,  256, 256, 512,\n                512, 1024, 512,  2048, 256, 512, 1024};\n\n    for (int j = 0; j < 30; j++) {\n        printk(\"Iteration #%d\\n\", j);\n        for (int i = 0; i < 14; i++) {\n            if ((p[i] = alloc_pages(size_to_page_order(sz[i]))) == NULL)\n                TEST_EXIT(1);\n        }\n        for (int i = 0; i < 14; i++)\n            free_pages((unsigned long) p[i], size_to_page_order(sz[i]));\n    }\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/mmap_1/main.c",
    "content": "/* simple mmap() test, check non-nil memory is allocated */\n\n#include <stddef.h>\n\n#include <piko/sys/mman.h>\n\n#include \"unit.h\"\n\nint main()\n{\n    void *p;\n\n    p = mmap(0, 256, 0, MAP_ANONYMOUS, 0, 0);\n    if (p == MAP_FAILED)\n        TEST_EXIT(1);\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/mmap_2/data/id_rsa",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIBOgIBAAJBAI/rmTOBknHe2ro+8sUNl1MjNTRopU1/WJTcYvsrFML10Z2IBrFi\nfc3Q1x92uTvyFU21cn+/ekU8L+8qK+V98fMCAwEAAQJAe4uw+REa6Nt5Ana1KsmP\nBNRtgO7wMEXIgglqgtipuu3CE9CIZ4OyrvELNbI6RiczGYgMmUHcWEmLuBZ+tjQu\n4QIhAORy2CQio1J3O2gGdkk2eY2cLIzoMEdig9su/NILvSqDAiEAoUcD1FeM1iub\nSiUq3ZDIf1FFTgtv9Si0lzJyBY+IP9ECIHi8zclDWUhDZe1TxP5qwRF74fvS13lS\n8tdL3SjyNVcbAiEAhZU3o8r8mWy3DFvqvGiu2V3shK9OhYa4xj9/WAHB/fECIH9u\nNq1Ww0stz2Oq7Wpn7ywcNemkot/+sO8l8gxXPcqw\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "tests/mmap_2/data/id_rsa.pub",
    "content": "-----BEGIN PUBLIC KEY-----\nMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAI/rmTOBknHe2ro+8sUNl1MjNTRopU1/\nWJTcYvsrFML10Z2IBrFifc3Q1x92uTvyFU21cn+/ekU8L+8qK+V98fMCAwEAAQ==\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "tests/mmap_2/main.c",
    "content": "/* test mapping file to memory */\n\n#include <string.h>\n#include <fcntl.h>\n\n#include <piko/sys/mman.h>\n#include <piko/sys/mount.h>\n\n#include <kernel/fs.h>\n#include <kernel/kernel.h>\n\n#include <drivers/mtd/mtd.h>\n\n#include \"unit.h\"\n\nextern char _binary_sda1_start;\nstruct mtd_info mtd1;\n\nstatic struct inode inode = {\n    .i_private = &mtd1,\n};\n\nvoid flash_init(void)\n{\n    struct dentry dentry = {.d_inode = &inode, .d_name = \"mtd1\"};\n\n    printk(\"Creating MTD device %s\\n\", dentry.d_name);\n    if (mtdram_init_device(&mtd1, &_binary_sda1_start, 1024, dentry.d_name))\n        printk(\"error: mtdram init device failed\\n\");\n    vfs_link(NULL, dev_inode(), &dentry);\n}\n\nint main()\n{\n    init_tmpfs_inode(&inode);\n    flash_init();\n    mount(\"/dev/mtd1\", \"/dev/flash\", \"romfs\", 0, 0);\n\n    int fd = open(\"/dev/flash/id_rsa.pub\", 0);\n    if (fd < 0) {\n        printk(\"error: failed to open /data/id_rsa.pub\\n\");\n        TEST_EXIT(1);\n    }\n\n    void *p = mmap(NULL, 256, 0, 0, fd, 0);\n    if (p == MAP_FAILED)\n        TEST_EXIT(1);\n    if (strncmp((char *) p, \"-----BEGIN PUBLIC KEY-----\", 26))\n        TEST_EXIT(1);\n\n    p = mmap(NULL, 256, 0, 0, fd, 27);\n    if (p == MAP_FAILED)\n        TEST_EXIT(1);\n    if (strncmp(\n            (char *) p,\n            \"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAI/rmTOBknHe2ro+8sUNl1MjNTRopU1/\",\n            64))\n        TEST_EXIT(1);\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/msleep_1/main.c",
    "content": "#include \"unit.h\"\n#include \"kernel.h\"\n\nextern void msleep(unsigned int);\n\nint main()\n{\n    for (int i = 1; i < 6; i++) {\n        printk(\"%d...\", i);\n        msleep(1000);\n    }\n    printk(\"\\n\");\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/msleep_2/main.c",
    "content": "// simple thread create and thread yield\n\n#include <pthread.h>\n#include <stddef.h>\n#include \"kernel.h\"\n#include \"unit.h\"\n\nextern void msleep(unsigned int);\n\nstatic int val;\n\nstatic void *fn(void *arg)\n{\n    // msleep(30 * (1 + (int) arg));\n    msleep(30);\n    val++;\n\n    return 0;\n}\n\nint main()\n{\n    pthread_t tip;\n\n    for (int i = 0; i < 3; i++) {\n        if (pthread_create(&tip, NULL, fn, (void *) i)) {\n            printk(\"failed: can't create new posix thread.\\n\");\n            TEST_EXIT(1);\n        }\n    }\n    while (val != 3)\n        pthread_yield();\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/mtdram_1/main.c",
    "content": "/* test simple romFS */\n\n#include <fcntl.h>\n#include <string.h>\n#include <unistd.h>\n\n#include <kernel/fs.h>\n#include <kernel/kernel.h>\n\n#include \"unit.h\"\n\nint main()\n{\n    char str[] = \"Hello World!\";\n    char buf[32];\n    ssize_t len = strlen(str);\n    int fd;\n\n    printk(\"Opening /dev/mtd0...\\n\");\n    fd = open(\"/dev/mtd0\", 0);\n    if (write(fd, str, len) != len)\n        TEST_EXIT(1);\n    lseek(fd, 0, SEEK_SET);\n    if (read(fd, buf, len) != len)\n        TEST_EXIT(1);\n    printk(\"Read /dev/mtd0: %s\\n\", buf);\n    if (strcmp(str, buf))\n        TEST_EXIT(1);\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/mutex_1/main.c",
    "content": "#include <stddef.h>\n\n#include \"kernel.h\"\n#include \"pthread.h\"\n#include \"unit.h\"\n\nint main(void)\n{\n    pthread_mutex_t lock;\n\n    pthread_mutex_init(&lock, NULL);\n\n    if (pthread_mutex_lock(&lock))\n        TEST_EXIT(1);\n    printk(\"mutex locked...\\n\");\n    if (pthread_mutex_unlock(&lock))\n        TEST_EXIT(1);\n    printk(\"mutex unlocked...\\n\");\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/mutex_2/main.c",
    "content": "#include <stddef.h>\n#include <stdlib.h>\n#include <sys/cdefs.h>\n\n#include \"kernel.h\"\n#include \"pthread.h\"\n#include \"unit.h\"\n\npthread_mutex_t lock;\nint has_waited;\n\nvoid *fn(__unused void *arg)\n{\n    printk(\"thread 2: acquire mutex...\\n\");\n    if (pthread_mutex_lock(&lock))\n        return NULL;\n    has_waited = 1;\n    printk(\"thread 2: OK, mutex locked...\\n\");\n    if (pthread_mutex_unlock(&lock))\n        return NULL;\n    printk(\"thread 2: mutex released...\\n\");\n\n    return NULL;\n}\n\nint main(void)\n{\n    pthread_t tid;\n\n    pthread_mutex_init(&lock, NULL);\n\n    if (pthread_create(&tid, NULL, fn, NULL))\n        printk(\"error: Could not create new thread.\\n\");\n\n    if (pthread_mutex_lock(&lock))\n        TEST_EXIT(1);\n    printk(\"thread 1: mutex locked, yield now...\\n\");\n    sched_yield();\n    if (has_waited)\n        TEST_EXIT(1);\n    printk(\"thread 1: return from yield.\\n\");\n    if (pthread_mutex_unlock(&lock))\n        TEST_EXIT(1);\n    printk(\"thread 1: mutex released...\\n\");\n\n    /* re-acquire the mutex to check the thread released the mutex\n       correctly */\n    if (pthread_mutex_lock(&lock))\n        TEST_EXIT(1);\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/mutex_3/main.c",
    "content": "/* test the trylock interface */\n\n#include <stddef.h>\n\n#include \"kernel.h\"\n#include \"pthread.h\"\n#include \"unit.h\"\n\nint main(void)\n{\n    pthread_mutex_t lock;\n\n    pthread_mutex_init(&lock, NULL);\n\n    if (pthread_mutex_lock(&lock))\n        TEST_EXIT(1);\n    printk(\"mutex is now locked...\\n\");\n    if (!pthread_mutex_trylock(&lock))\n        TEST_EXIT(1);\n    printk(\"tried to lock a locked mutex...\\n\");\n    if (pthread_mutex_unlock(&lock))\n        TEST_EXIT(1);\n    printk(\"mutex unlocked, trylock should succeed...\\n\");\n    if (pthread_mutex_trylock(&lock))\n        TEST_EXIT(1);\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/mutex_4/main.c",
    "content": "/* strong mutex test, mixing lock/unlock/trylock */\n#include <stddef.h>\n#include <stdlib.h>\n#include <sys/cdefs.h>\n\n#include \"kernel.h\"\n#include \"pthread.h\"\n#include \"unit.h\"\n\npthread_mutex_t lock;\n\nvoid *fn(__unused void *arg)\n{\n    printk(\"thread 2: acquire mutex...\\n\");\n    if (!pthread_mutex_trylock(&lock))\n        return NULL;\n    printk(\"thread 2: mutex was locked...\\n\");\n    sched_yield();\n    printk(\"thread 2: re-acquire mutex...\\n\");\n    if (pthread_mutex_trylock(&lock))\n        return NULL;\n    sched_yield();\n    if (pthread_mutex_unlock(&lock))\n        return NULL;\n\n    return NULL;\n}\n\nint main(void)\n{\n    pthread_t tid;\n\n    pthread_mutex_init(&lock, NULL);\n\n    if (pthread_create(&tid, NULL, fn, NULL))\n        printk(\"error: Could not create new thread.\\n\");\n\n    if (pthread_mutex_trylock(&lock))\n        TEST_EXIT(1);\n    printk(\"thread 1: mutex locked, yield now...\\n\");\n    sched_yield();\n    printk(\"thread 1: return from yield.\\n\");\n    if (pthread_mutex_unlock(&lock))\n        TEST_EXIT(1);\n    printk(\"thread 1: mutex released...\\n\");\n    sched_yield();\n\n    /* re-acquire the mutex to check the thread released the mutex\n       correctly */\n    if (!pthread_mutex_trylock(&lock))\n        TEST_EXIT(1);\n    if (pthread_mutex_lock(&lock))\n        TEST_EXIT(1);\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/mutex_5/main.c",
    "content": "/* test mutexs' waitqueue */\n#include <stddef.h>\n#include <stdlib.h>\n#include <sys/cdefs.h>\n\n#include \"kernel.h\"\n#include \"pthread.h\"\n#include \"unit.h\"\n\nstatic pthread_mutex_t m;\n\nvoid *fn(__unused void *arg)\n{\n    printk(\"thread %d locking...\\n\", (int) arg);\n    pthread_mutex_lock(&m);\n    printk(\"OK, thread %d got the mutex!\\n\", (int) arg);\n    pthread_mutex_unlock(&m);\n\n    return NULL;\n}\n\nint main()\n{\n    pthread_t tips[4];\n\n    pthread_mutex_init(&m, NULL);\n    pthread_mutex_lock(&m);\n\n    for (int i = 0; i < 4; i++) {\n        if (pthread_create(&tips[i], NULL, fn, (void *) i)) {\n            printk(\"failed: can't create new posix thread.\\n\");\n            TEST_EXIT(1);\n        }\n    }\n\n    printk(\"locked the mutex, now yielding...\\n\");\n    sched_yield();\n\n    printk(\"unlocking the mutex...\\n\");\n    pthread_mutex_unlock(&m);\n\n    printk(\"relocking the mutex...\\n\");\n    pthread_mutex_lock(&m);\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/page_3/main.c",
    "content": "/* stress test of page alloc/free */\n\n#include <fcntl.h>\n#include <stddef.h>\n#include <unistd.h>\n\n#include <kernel/kernel.h>\n#include <kernel/mm/page.h>\n#include \"unit.h\"\n\nstruct alloc_info {\n    void *addr;\n    int order;\n};\n\nstruct alloc_info alloc_info[128];\n\nint main()\n{\n    int i;\n    int fd;\n    unsigned int order;\n    unsigned long hash;\n    size_t sz;\n\n    fd = open(\"/dev/random\", 0);\n    if (fd < 0) {\n        printk(\"error: failed to open /dev/random\\n\");\n        TEST_EXIT(1);\n    }\n\n    /* take a snapshot of memory state bgefore the test */\n    hash = page_alloc_signature();\n\n    for (int k = 0; k < 1000; k++) {\n        sz = 0;\n        for (i = 0; i < 40; i++) {\n            read(fd, &order, 1);\n            order = order % 4;\n            alloc_info[i].addr = alloc_pages(order);\n            alloc_info[i].order = order;\n            if (!alloc_info[i].addr)\n                break;\n            sz += 1 << (order + 8);\n            /* printk(\"  %p  (order=%d)\\n\", alloc_info[i].addr, */\n            /* \talloc_info[i].order); */\n        }\n        /* printk(\"Allocated %d bytes (%d bytes available)\\n\", sz, 32 * 1024);\n         */\n\n        /* pseudo-randomish freeing of the memory.. */\n        for (int j = 0; j < i; j += 3)\n            free_pages((unsigned long) alloc_info[j].addr, alloc_info[j].order);\n        for (int j = 1; j < i; j += 3)\n            free_pages((unsigned long) alloc_info[j].addr, alloc_info[j].order);\n        for (int j = 2; j < i; j += 3)\n            free_pages((unsigned long) alloc_info[j].addr, alloc_info[j].order);\n\n        if (page_alloc_signature() != hash) {\n            printk(\"error: Memory not correctly restored\\n\");\n            TEST_EXIT(1);\n        }\n    }\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/raise_1/main.c",
    "content": "/*simple signal test */\n\n#include <stddef.h>\n#include <signal.h>\n#include \"kernel.h\"\n#include \"unit.h\"\n\nint val;\nint signo = SIGUSR1;\n\nvoid handler(int n)\n{\n    printk(\"In signal handler, received signal %d\\n\", n);\n\n    if (n != signo)\n        TEST_EXIT(1);\n    val = 1;\n}\n\nint main(void *arg)\n{\n    (void) arg;\n\n    const struct sigaction act = {.sa_handler = handler, .sa_flags = 0};\n\n    sigaction(signo, &act, NULL);\n    if (raise(signo))\n        TEST_EXIT(1);\n    if (!val)\n        TEST_EXIT(1);\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/raise_2/main.c",
    "content": "/* test raise() return code */\n\n#include <stddef.h>\n#include <signal.h>\n\n#include <kernel/errno-base.h>\n#include \"kernel.h\"\n#include \"unit.h\"\n\nint main(void *arg)\n{\n    (void) arg;\n\n    sigaction(SIGUSR1, NULL, NULL); /* shall return -EINVAL */\n    int retval = raise(0);\n    printk(\"Got return value %d (negative)\\n\", -retval);\n    if (retval != -EINVAL)\n        TEST_EXIT(1);\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/raise_3/main.c",
    "content": "/* test signal handler with SA_SIGINFO set */\n\n#include <stddef.h>\n#include <signal.h>\n#include \"kernel.h\"\n#include \"unit.h\"\n\nint val;\nint signo = SIGUSR1;\n\nvoid sigact(int sig, siginfo_t *siginfo, void *unused)\n{\n    (void) unused;\n\n    printk(\"In a sigaction handler (signo=%d, sival=0x%x)\\n\", sig,\n           siginfo->si_value.sival_int);\n    if (sig != signo)\n        TEST_EXIT(1);\n\n    val = 1;\n}\n\nint main(void *arg)\n{\n    (void) arg;\n\n    const struct sigaction act = {.sa_sigaction = sigact,\n                                  .sa_flags = SA_SIGINFO};\n\n    sigaction(signo, &act, NULL);\n    int retval = raise(signo);\n    if (retval) {\n        printk(\"Syscall returned %d, expected 0\\n\", retval);\n        TEST_EXIT(1);\n    }\n    if (!val)\n        TEST_EXIT(1);\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/readdir_1/main.c",
    "content": "#include <stddef.h>\n#include <string.h>\n\n#include <piko/dirent.h>\n\n#include <kernel/fs.h>\n#include <kernel/kernel.h>\n\n#include \"unit.h\"\n\nint found_dot;\nint found_dotdot;\nint found_null;\nint found_zero;\n\nint main()\n{\n    struct dirent dirent;\n    struct dirent *result;\n\n    DIR *dir = opendir(\"/dev\");\n    do {\n        readdir_r(dir, &dirent, &result);\n        if (result != NULL)\n            printk(\"% 6d %s\\n\", dirent.d_ino, dirent.d_name);\n        if (!strcmp(result->d_name, \".\"))\n            found_dot++;\n        if (!strcmp(result->d_name, \"..\"))\n            found_dotdot++;\n        if (!strcmp(result->d_name, \"zero\"))\n            found_zero++;\n        if (!strcmp(result->d_name, \"null\"))\n            found_null++;\n    } while (result != NULL);\n    closedir(dir);\n\n    if (found_dot != 1)\n        TEST_EXIT(1);\n    if (found_dotdot != 1)\n        TEST_EXIT(1);\n    if (found_zero != 1)\n        TEST_EXIT(1);\n    if (found_null != 1)\n        TEST_EXIT(1);\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/runner.py",
    "content": "import datetime\nimport glob\nimport locale\nimport subprocess\nimport sys\nimport time\nimport textwrap\nimport platform\nimport os\nfrom time import strftime\n\n# Test result constants.\nPASSED = 1\nFAILED = 0\nINTERRUPTED = -4\nCHILD_ERROR = -5   # error in a child process\n\n_FORMAT_TEST_RESULT = {\n    PASSED: '%s passed',\n    FAILED: '%s failed',\n    INTERRUPTED: '%s interrupted',\n    CHILD_ERROR: '%s crashed',\n}\n\n\ndef find_all_tests(excludes=[]):\n    tests = list(map(lambda p: p.strip('/').split('/')[-1], sorted(glob.glob('tests/*_[0-9]*'))))\n    for exclude in excludes:\n        if exclude in tests:\n            tests.remove(exclude)\n    return tests\n\n\ntestsuite_v7m = find_all_tests(excludes=['test_2'])\n\n\nclass PikoTest:\n    \"\"\"Execute Piko/RT test suite.\n    \"\"\"\n\n    def __init__(self):\n        # Namespace of command line options\n        self.ns = None\n\n        # tests\n        self.tests = []\n        self.selected = []\n\n        # test results\n        self.good = []\n        self.bad = []\n        self.interrupted = False\n\n        # used by --slow\n        self.test_times = []\n\n        # used to display the progress bar \"[ 4/100]\"\n        self.start_time = time.monotonic()\n        self.test_count = ''\n        self.test_count_width = 1\n\n    def format_test_result(self, test_name, result):\n        fmt = _FORMAT_TEST_RESULT.get(result, \"%s\")\n        return fmt % test_name\n\n    def format_duration(self, seconds):\n        if seconds < 1.0:\n            return '%.0f ms' % (seconds * 1e3)\n        if seconds < 60.0:\n            return '%.0f sec' % seconds\n\n        minutes, seconds = divmod(seconds, 60.0)\n        return '%.0f min %.0f sec' % (minutes, seconds)\n\n    def display_progress(self, test_index, test):\n        if self.ns.quiet:\n            return\n\n        # \"[ 51/405/1] test_tcl passed\"\n        if self.bad:\n            fmt = \"{time} [{test_index:{count_width}}{test_count}/{nbad}] {test_name}\"\n        else:\n            fmt = \"{time} [{test_index:{count_width}}{test_count}] {test_name}\"\n        test_time = time.monotonic() - self.start_time\n        test_time = datetime.timedelta(seconds=int(test_time))\n        line = fmt.format(count_width=self.test_count_width,\n                          test_index=test_index,\n                          test_count=self.test_count,\n                          nbad=len(self.bad),\n                          test_name=test,\n                          time=test_time)\n        print(line, flush=True)\n\n    def display_result(self):\n        if self.interrupted:\n            print()\n            print(\"Test suite interrupted by signla SIGINT.\")\n            executed = set(self.good) | set(self.bad)\n            omitted = set(self.selected) - executed\n            print(count(len(omitted), \"test\"), \"omitted:\")\n            printlist(omitted)\n\n        if self.good and not self.ns.quiet:\n            if (not self.bad\n                and not self.interrupted\n                and len(self.good) > 1):\n                print(\"All\", end=' ')\n            print(count(len(self.good), \"test\"), \"OK.\")\n\n        if self.ns.print_slow:\n            self.test_times.sort(reverse=True)\n            print()\n            print(\"10 slowest tests:\")\n            for t, test in self.test_times[:10]:\n                print(\"- %s: %s\" % (test, self.format_duration(t)))\n\n        if self.bad:\n            print()\n            print(count(len(self.bad), \"test\"), \"failed:\")\n            printlist(self.bad)\n\n    def display_header(self):\n        print('== Welcome to Piko/RT test suite')\n        print(\"==\", platform.python_implementation(), *sys.version.split())\n        print(\"==\", platform.platform(aliased=True),\n              \"%s-endian\" % sys.byteorder)\n        print(\"== cwd:\", os.getcwd())\n        cpu_count = os.cpu_count()\n        if cpu_count:\n            print(\"== CPU count:\", cpu_count)\n        print(\"== encodings: locale=%s, FS=%s\"\n              % (locale.getpreferredencoding(False),\n                 sys.getfilesystemencoding()))\n        print()\n        print_qemu_version(self.ns)\n        print_gcc_version(self.ns)\n\n    def accumulate_result(self, test, result):\n        ok, test_time = result\n        if ok not in (CHILD_ERROR, INTERRUPTED):\n            self.test_times.append((test_time, test))\n        if ok == PASSED:\n            self.good.append(test)\n        elif ok in (FAILED, CHILD_ERROR):\n            self.bad.append(test)\n        elif ok != INTERRUPTED:\n            raise ValueError(\"invalid test result: %r\" % ok)\n\n    def run_tests(self):\n        self.display_header()\n\n        self.test_count = '/{}'.format(len(self.selected))\n        self.test_count_width = len(self.test_count) - 1\n        self.run_tests_sequential()\n\n    def run_tests_sequential(self):\n        print('Run tests sequentially')\n        previous_test = None\n        for test_index, test in enumerate(self.tests, 1):\n            start_time = time.monotonic()\n\n            text = test\n            if previous_test:\n                text = '%s -- %s' % (text, previous_test)\n            self.display_progress(test_index, text)\n\n            try:\n                result = run_single_test(test, self.ns)\n            except KeyboardInterrupt:\n                self.interrupted = True\n                self.accumulate_result(test, (INTERRUPTED, None))\n                break\n            else:\n                self.accumulate_result(test, result)\n\n            previous_test = self.format_test_result(test, result[0])\n            test_time = time.monotonic() - start_time\n            if test_time >= 15.0:\n                previous_test = \"%s in %s\" % (previous_test, self.format_duration(test_time))\n            elif result[0] == PASSED:\n                previous_test = None\n\n        if previous_test:\n            print(previous_test)\n\n    def finalize(self):\n        print()\n        duration = time.monotonic() - self.start_time\n        print(\"Total duration: %s\" % (self.format_duration(duration)))\n\n        if self.bad:\n            result = \"FAILURE\"\n        elif self.interrupted:\n            result = \"INTERRUPTED\"\n        else:\n            result = \"SUCCESS\"\n        print(\"Tests result: %s\" % result)\n\n    def main(self, tests, ns):\n        self.ns = ns\n        self.tests = tests\n        self.selected = tests\n\n        self.run_tests()\n        self.display_result()\n\n        self.finalize()\n        if self.bad:\n            sys.exit(2)\n        if self.interrupted:\n            sys.exit(130)\n        sys.exit(0)\n\n\ndef print_qemu_version(ns):\n    cmd = [ns.qemu, \"--version\"]\n    res = subprocess.run(cmd, universal_newlines=True, stdout=subprocess.PIPE,\n                         stderr=subprocess.DEVNULL)\n    print()\n    print(\"QEMU version\\n\", res.stdout.strip())\n    print()\n\n\ndef print_gcc_version(ns):\n    cmd = [ns.cc, \"--version\"]\n    res = subprocess.run(cmd, universal_newlines=True, stdout=subprocess.PIPE,\n                         stderr=subprocess.DEVNULL)\n    print()\n    print(\"GCC version:\\n\" + res.stdout.strip())\n    print()\n\n\ndef print_header(testname, arch):\n    print(\"--------------------------------------------\")\n    print(\"running test:  \\033[1;37m%s\\033[0m\" % testname)\n    print(\"arch        :  %s\" % arch)\n    print(\"time        :  %s\\n\" % strftime(\"%c\"))\n\n\ndef run_single_test(test, ns):\n    \"\"\"Run a single test.\n\n    ns -- parser namespace of options\n    test -- the name of the test\n\n    Returns the tuple (result, test_time), where result is one of the\n    constants:\n\n        INTERRUPTED     KeyboardInterrupt\n        FAILED          test failed\n        PASS            test passed\n    \"\"\"\n\n    cmd = ['make', '--file', 'tests/Makefile', 'clean_test', 'all']\n    cmd.append('dbg' if ns.debug else 'run')\n    cmd.append('TEST=%s' % (test))\n\n    if ns.platform:\n        cmd.append('PLAT=%s' % (ns.platform))\n\n    test_time = 0.0\n\n    if ns.verbose:\n        print(' '.join(cmd))\n    try:\n        start_time = time.time()\n        p = subprocess.Popen(cmd, universal_newlines=True,\n                             stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n        output, error = p.communicate(timeout=ns.timeout)\n        test_time = time.time() - start_time\n        if ns.verbose:\n            print(output)\n    except KeyboardInterrupt:\n        p.kill()\n        output, error = p.communicate()\n        if ns.verbose:\n            print(output)\n        raise\n    except subprocess.TimeoutExpired:\n        p.kill()\n        output, error = p.communicate()\n        if ns.verbose:\n            print(output)\n        return FAILED, test_time\n\n    if p.returncode != 0:\n        if ns.verbose:\n            print(output)\n        return FAILED, test_time\n\n    return PASSED, test_time\n\n\ndef main(ns):\n    PikoTest().main(testsuite_v7m, ns)\n\n\ndef printlist(x, width=70, indent=4, file=None):\n    \"\"\"Print the elements of iterable x to stdout.\n\n    Optional arg width (default 70) is the maximum line length.\n    Optional arg indent (default 4) is the number of blanks with which to\n    begin each line.\n    \"\"\"\n\n    blanks = ' ' * indent\n    # Print the sorted list: 'x' may be a '--random' list or a set()\n    print(textwrap.fill(' '.join(str(elt) for elt in sorted(x)), width,\n                        initial_indent=blanks, subsequent_indent=blanks),\n          file=file)\n\n\ndef count(n, word):\n    if n == 1:\n        return \"%d %s\" % (n, word)\n    else:\n        return \"%d %ss\" % (n, word)\n"
  },
  {
    "path": "tests/slab_1/main.c",
    "content": "/* simple test for cache creation and allocation */\n\n#include <kernel/kernel.h>\n#include <kernel/mm/slab.h>\n\n#include \"unit.h\"\n\nstruct foo {\n    int a;\n    int b;\n};\n\nint main(void)\n{\n    struct kmem_cache *cache = KMEM_CACHE(struct foo, \"cache-foo\");\n    if (!cache) {\n        printk(\"error: Cannot create cache\\n\");\n        TEST_EXIT(1);\n    }\n\n    struct foo *foo = kmem_cache_alloc(cache, CACHE_OPT_NONE);\n    if (!foo) {\n        printk(\"error: Cannot allocate from cache\\n\");\n        TEST_EXIT(1);\n    }\n\n    foo->a = 1;\n    foo->b = 2;\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/slab_2/main.c",
    "content": "/* test for cache creation, allocation, destruction */\n\n#include <kernel/kernel.h>\n#include <kernel/mm/page.h>\n#include <kernel/mm/slab.h>\n\n#include \"unit.h\"\n\nstruct foo {\n    int a;\n    int b;\n};\n\nint main(void)\n{\n    unsigned long hash;\n\n    struct kmem_cache *cache = KMEM_CACHE(struct foo, \"cache-foo\");\n\n    if (!cache) {\n        printk(\"error: Cannot create cache\\n\");\n        TEST_EXIT(1);\n    }\n\n    hash = page_alloc_signature();\n\n    struct foo *fp[40];\n    for (int i = 0; i < 40; i++) {\n        fp[i] = kmem_cache_alloc(cache, CACHE_OPT_NONE);\n        fp[i]->a = i;\n    }\n\n    if (page_alloc_signature() == hash) {\n        printk(\"error: No memory allocated\\n\");\n        TEST_EXIT(1);\n    }\n\n    for (int i = 0; i < 40; i++) {\n        if (fp[i]->a != i) {\n            printk(\"error: Address allocated multiple times\\n\");\n            TEST_EXIT(1);\n        }\n        kmem_cache_free(cache, fp[i]);\n    }\n\n    if (page_alloc_signature() != hash) {\n        printk(\"error: Memory not correctly restored\\n\");\n        TEST_EXIT(1);\n    }\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/softirq_1/main.c",
    "content": "/* simple softirq task create and rasie */\n\n#include <kernel/kernel.h>\n\n#include <kernel/softirq.h>\n\n#include \"unit.h\"\n\nstatic volatile int val = 111;\n\nstatic void fn(void *arg)\n{\n    val += (int) arg;\n}\n\nint main()\n{\n    struct tasklet_struct *tsk = NULL;\n\n    tsk = tasklet_init(fn, (void *) 666, PRIO_TASKLET_MAXPRIO);\n    if (!tsk) {\n        printk(\"failed: can't create new softirq task.\\n\");\n        TEST_EXIT(1);\n    }\n\n    if (tasklet_schedule(tsk) == -1) {\n        printk(\"failed: can't rasie softirq task.\\n\");\n        TEST_EXIT(1);\n    }\n\n    while (val != 777)\n        ;\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/softirq_2/main.c",
    "content": "/* softirq task create/rasie 1000 times */\n\n#include <kernel/kernel.h>\n\n#include <kernel/softirq.h>\n\n#include \"unit.h\"\n\nstatic volatile int val = 1111;\n\nstatic void fn(void *arg)\n{\n    val += (int) arg;\n}\n\nint main()\n{\n    int i;\n    static struct tasklet_struct *tsk = NULL;\n\n    /**\n     *  FIXME: modifing to 1000 times would fail to create.\n     *          Concludion with this problem is without freeing\n     *          softirq task. That's make sense, so we could avoid\n     *          it by testing tsk ptr. No more creation of same task.\n     *  FIXME: After patch above, we would get another problem about\n     *          same tsk with same addr space can concatenate two same tsk?\n     *\n     *          ANS: Probably NOT\n     */\n    for (i = 0; i < 100; i++) {\n        tsk = tasklet_init(fn, (void *) 1, PRIO_TASKLET_MAXPRIO);\n\n        if (!tsk) {\n            printk(\"failed: can't create new softirq task.\\n\");\n            TEST_EXIT(1);\n        }\n\n        if (tasklet_schedule(tsk) == -1) {\n            printk(\"failed: can't rasie softirq task.\\n\");\n            TEST_EXIT(1);\n        }\n    }\n\n    while (val != 1211)\n        ;\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/softirq_3/main.c",
    "content": "/* test softirq task sequeniality */\n\n#include <kernel/kernel.h>\n\n#include <kernel/softirq.h>\n\n#include \"unit.h\"\n\nstatic volatile int bucket[6] = {0};\n\n#define DEFINE_FUNC(_id_) \\\n    static void fn##_id_(void *arg) { bucket[_id_] = (int) arg; }\nDEFINE_FUNC(0)\nDEFINE_FUNC(1)\nDEFINE_FUNC(2)\nDEFINE_FUNC(3)\nDEFINE_FUNC(4)\nDEFINE_FUNC(5)\n\nint main()\n{\n#define SOFTIRQ_TASK_IMPL(_id_)                                          \\\n    do {                                                                 \\\n        struct tasklet_struct *tsk##_id_ =                               \\\n            tasklet_init(fn##_id_, (void *) _id_, PRIO_TASKLET_MAXPRIO); \\\n        if (!tsk##_id_) {                                                \\\n            printk(\"failed: can't create new softirq task\" #_id_ \".\\n\"); \\\n            TEST_EXIT(1);                                                \\\n        }                                                                \\\n                                                                         \\\n        if (tasklet_schedule(tsk##_id_) == -1) {                         \\\n            printk(\"failed: can't rasie softirq task\" #_id_ \".\\n\");      \\\n            TEST_EXIT(1);                                                \\\n        }                                                                \\\n    } while (0)\n\n    SOFTIRQ_TASK_IMPL(0);\n    SOFTIRQ_TASK_IMPL(1);\n    SOFTIRQ_TASK_IMPL(2);\n    SOFTIRQ_TASK_IMPL(3);\n    SOFTIRQ_TASK_IMPL(4);\n    SOFTIRQ_TASK_IMPL(5);\n\n    for (int i = 0; i < 6; i++)  // Test sequenciality\n        while (!(bucket[i] == i))\n            ;\n\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/sprintf_1/main.c",
    "content": "#include \"unit.h\"\n#include \"utils.h\"\n#include <string.h>\n#include \"kernel.h\"\n#include <stdio.h>\n\nint sprintf_00(void)\n{\n    char buf[128];\n\n    sprintf(buf, \"%d\", 1789);\n    if (strcmp(buf, \"1789\")) {\n        printk(\"%s\", buf);\n        return -1;\n    }\n\n    return 0;\n}\n\nint sprintf_01(void)\n{\n    char buf[128];\n\n    sprintf(buf, \"%x\", 0xdeadbeef);\n    if (strcmp(buf, \"deadbeef\")) {\n        printk(\"%s\\n\", buf);\n        return -1;\n    }\n\n    return 0;\n}\n\nint sprintf_02(void)\n{\n    char buf[128];\n\n    sprintf(buf, \"Hello %s\", \"World!\");\n    if (strcmp(buf, \"Hello World!\")) {\n        printk(\"%s\\n\", buf);\n        return -1;\n    }\n\n    return 0;\n}\n\nint sprintf_03(void)\n{\n    char buf[128];\n\n    sprintf(buf, \"abcdABCD0123 0x%x, %d, %d, 0x%x\", 0xdeadbeef, 1983, 2014,\n            4096);\n    if (strcmp(buf, \"abcdABCD0123 0xdeadbeef, 1983, 2014, 0x1000\")) {\n        printk(\"%s\\n\", buf);\n        return -1;\n    }\n\n    return 0;\n}\n\nint sprintf_04(void)\n{\n    char buf[128];\n\n    sprintf(buf, \"%w, %%, %s, %d\", \"foo\", 1986);\n    if (strcmp(buf, \"%w, %, foo, 1986\")) {\n        printk(\"%s\\n\", buf);\n        return -1;\n    }\n\n    return 0;\n}\n\nint sprintf_05(void)\n{\n    char buf[128];\n\n    sprintf(buf, \"%08d %06d %04d\", 1986, 1986, 1986);\n    if (strcmp(buf, \"00001986 001986 1986\")) {\n        printk(\"%s\\n\", buf);\n        return -1;\n    }\n\n    return 0;\n}\n\nint sprintf_06(void)\n{\n    char buf[128];\n\n    sprintf(buf, \"%08x %06x %04x %02x\", 0xef, 0xef, 0xef, 0xef);\n    if (strcmp(buf, \"000000ef 0000ef 00ef ef\")) {\n        printk(\"%s\\n\", buf);\n        return -1;\n    }\n\n    return 0;\n}\n\nint sprintf_07(void)\n{\n    char buf[128];\n\n    sprintf(buf, \"% 7d % 5x\", 1986, 0x13);\n    if (strcmp(buf, \"   1986    13\")) {\n        printk(\"%s\\n\", buf);\n        return -1;\n    }\n\n    return 0;\n}\n\nint main()\n{\n    int status = 0;\n    int (*test[])(void) = {sprintf_00, sprintf_01, sprintf_02, sprintf_03,\n                           sprintf_04, sprintf_05, sprintf_06, sprintf_07};\n\n    for (int i = 0; i <= 7; i++) {\n        printk(\"sprintf test #%d\\n\", i);\n        status += test[i]();\n    }\n    if (status)\n        TEST_EXIT(1);\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/stat_1/data/id_rsa",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIBOgIBAAJBAI/rmTOBknHe2ro+8sUNl1MjNTRopU1/WJTcYvsrFML10Z2IBrFi\nfc3Q1x92uTvyFU21cn+/ekU8L+8qK+V98fMCAwEAAQJAe4uw+REa6Nt5Ana1KsmP\nBNRtgO7wMEXIgglqgtipuu3CE9CIZ4OyrvELNbI6RiczGYgMmUHcWEmLuBZ+tjQu\n4QIhAORy2CQio1J3O2gGdkk2eY2cLIzoMEdig9su/NILvSqDAiEAoUcD1FeM1iub\nSiUq3ZDIf1FFTgtv9Si0lzJyBY+IP9ECIHi8zclDWUhDZe1TxP5qwRF74fvS13lS\n8tdL3SjyNVcbAiEAhZU3o8r8mWy3DFvqvGiu2V3shK9OhYa4xj9/WAHB/fECIH9u\nNq1Ww0stz2Oq7Wpn7ywcNemkot/+sO8l8gxXPcqw\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "tests/stat_1/data/id_rsa.pub",
    "content": "-----BEGIN PUBLIC KEY-----\nMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAI/rmTOBknHe2ro+8sUNl1MjNTRopU1/\nWJTcYvsrFML10Z2IBrFifc3Q1x92uTvyFU21cn+/ekU8L+8qK+V98fMCAwEAAQ==\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "tests/stat_1/main.c",
    "content": "/* simple test for stat() */\n\n#include <sys/stat.h>\n\n#include <piko/sys/mount.h>\n\n#include <kernel/fs.h>\n#include <kernel/kernel.h>\n\n#include <drivers/mtd/mtd.h>\n\n#include \"unit.h\"\n\nextern char _binary_sda1_start;\nstruct mtd_info mtd1;\n\nstatic struct inode inode = {\n    .i_private = &mtd1,\n};\n\nvoid flash_init(void)\n{\n    struct dentry dentry = {.d_inode = &inode, .d_name = \"mtd1\"};\n\n    printk(\"Creating MTD device %s\\n\", dentry.d_name);\n    if (mtdram_init_device(&mtd1, &_binary_sda1_start, 1024, dentry.d_name))\n        printk(\"error: mtdram init device failed\\n\");\n    vfs_link(NULL, dev_inode(), &dentry);\n}\n\nint main()\n{\n    init_tmpfs_inode(&inode);\n    flash_init();\n    mount(\"/dev/mtd1\", \"/dev/flash\", \"romfs\", 0, 0);\n\n    struct stat st;\n    if (stat(\"/dev/flash/id_rsa.pub\", &st)) {\n        printk(\"error: Wrong pathname\\n\");\n        TEST_EXIT(1);\n    }\n    printk(\"Opened file with inode number %d\\n\", st.st_ino);\n    if (!S_ISREG(st.st_mode)) {\n        printk(\"error: File is not a regular file\\n\");\n        TEST_EXIT(1);\n    }\n    if (st.st_size != 182) {\n        printk(\"error: Wrong file size (%d != 182)\\n\", st.st_size);\n        TEST_EXIT(1);\n    }\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/syscall_1/main.c",
    "content": "#include \"unit.h\"\n#include \"kernel.h\"\n\nunsigned long get_sp(void);\nint syscall_register(unsigned ix, void *(*fn)());\n\nint sysarg_ack[7];\n\n#define SYSARG_A0 0xbabe\n#define SYSARG_A1 0xbeef\n#define SYSARG_A2 0x123\n#define SYSARG_A3 0x456\n#define SYSARG_A4 0xcafe\n#define SYSARG_A5 0xfeed\n\nint sysarg0_handler(void)\n{\n    printk(\"    no arg\\n\");\n    sysarg_ack[0] = 1;\n\n    return 0;\n}\n\nint sysarg1_handler(int a0)\n{\n    printk(\"    arg0: %x\\n\", a0);\n    if (a0 != SYSARG_A0)\n        return -1;\n    sysarg_ack[1] = 1;\n\n    return 0;\n}\n\nint sysarg2_handler(int a0, int a1)\n{\n    printk(\"    arg0: %x\\n\", a0);\n    printk(\"    arg1: %x\\n\", a1);\n    if (a0 != SYSARG_A0)\n        return -1;\n    if (a1 != SYSARG_A1)\n        return -1;\n    sysarg_ack[2] = 1;\n\n    return 0;\n}\n\nint sysarg3_handler(int a0, int a1, int a2)\n{\n    printk(\"    arg0: %x\\n\", a0);\n    printk(\"    arg1: %x\\n\", a1);\n    printk(\"    arg2: %x\\n\", a2);\n    if (a0 != SYSARG_A0)\n        return -1;\n    if (a1 != SYSARG_A1)\n        return -1;\n    if (a2 != SYSARG_A2)\n        return -1;\n    sysarg_ack[3] = 1;\n\n    return 0;\n}\n\nint sysarg4_handler(int a0, int a1, int a2, int a3)\n{\n    printk(\"    arg0: %x\\n\", a0);\n    printk(\"    arg1: %x\\n\", a1);\n    printk(\"    arg2: %x\\n\", a2);\n    printk(\"    arg3: %x\\n\", a3);\n    if (a0 != SYSARG_A0)\n        return -1;\n    if (a1 != SYSARG_A1)\n        return -1;\n    if (a2 != SYSARG_A2)\n        return -1;\n    if (a3 != SYSARG_A3)\n        return -1;\n    sysarg_ack[4] = 1;\n\n    return 0;\n}\n\nint sysarg5_handler(int a0, int a1, int a2, int a3, int a4)\n{\n    printk(\"    arg0: %x\\n\", a0);\n    printk(\"    arg1: %x\\n\", a1);\n    printk(\"    arg2: %x\\n\", a2);\n    printk(\"    arg3: %x\\n\", a3);\n    printk(\"    arg4: %x\\n\", a4);\n    if (a0 != SYSARG_A0)\n        return -1;\n    if (a1 != SYSARG_A1)\n        return -1;\n    if (a2 != SYSARG_A2)\n        return -1;\n    if (a3 != SYSARG_A3)\n        return -1;\n    if (a4 != SYSARG_A4)\n        return -1;\n    sysarg_ack[5] = 1;\n\n    return 0;\n}\n\nint sysarg6_handler(int a0, int a1, int a2, int a3, int a4, int a5)\n{\n    printk(\"    arg0: %x\\n\", a0);\n    printk(\"    arg1: %x\\n\", a1);\n    printk(\"    arg2: %x\\n\", a2);\n    printk(\"    arg3: %x\\n\", a3);\n    printk(\"    arg4: %x\\n\", a4);\n    printk(\"    arg5: %x\\n\", a5);\n    if (a0 != SYSARG_A0)\n        return -1;\n    if (a1 != SYSARG_A1)\n        return -1;\n    if (a2 != SYSARG_A2)\n        return -1;\n    if (a3 != SYSARG_A3)\n        return -1;\n    if (a4 != SYSARG_A4)\n        return -1;\n    if (a5 != SYSARG_A5)\n        return -1;\n    sysarg_ack[6] = 1;\n\n    return 0;\n}\n\nvoid test_svcall0();\nvoid test_svcall1();\nvoid test_svcall2();\nvoid test_svcall3();\nvoid test_svcall4();\nvoid test_svcall5();\nvoid test_svcall6();\n\nint main(void *arg)\n{\n    (void) arg;\n\n    void (*test_svcall[])() = {\n        test_svcall0, test_svcall1, test_svcall2, test_svcall3,\n        test_svcall4, test_svcall5, test_svcall6,\n    };\n\n    syscall_register(41, (void *(*) ()) sysarg0_handler);\n    syscall_register(42, (void *(*) ()) sysarg1_handler);\n    syscall_register(43, (void *(*) ()) sysarg2_handler);\n    syscall_register(44, (void *(*) ()) sysarg3_handler);\n    syscall_register(45, (void *(*) ()) sysarg4_handler);\n    syscall_register(46, (void *(*) ()) sysarg5_handler);\n    syscall_register(47, (void *(*) ()) sysarg6_handler);\n\n    for (int i = 0; i <= 6; i++) {\n        printk(\"Test syscall with %d arg(s).\\n\", i);\n        // XXX: check SP before and after. also in syscall handler, in\n        // trampoline.\n        unsigned long sp = get_sp();\n        test_svcall[i]();\n        if (get_sp() != sp) {\n            printk(\"error: Incorrect r13/sp\\n\");\n            TEST_EXIT(1);\n        }\n        if (!sysarg_ack[i]) {\n            printk(\"error: syscall was not acknowledged\\n\");\n            TEST_EXIT(1);\n        }\n    }\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/syscall_1/trampoline.S",
    "content": "#include <kernel/linkage.h>\n\n\t.syntax unified\n\t.thumb\n\nENTRY(test_svcall0)\n\tldr\tr0, =41\n\tsvc\t#0\n\tbx\tlr\nENDPROC(test_svcall0)\n\nENTRY(test_svcall1)\n\tldr\tr0, =0xbabe\n\tldr\tr1, =42\n\tsvc\t#1\n\tbx\tlr\nENDPROC(test_svcall1)\n\nENTRY(test_svcall2)\n\tldr\tr0, =0xbabe\n\tldr\tr1, =0xbeef\n\tldr\tr2, =43\n\tsvc\t#2\n\tbx\tlr\nENDPROC(test_svcall2)\n\nENTRY(test_svcall3)\n\tldr\tr0, =0xbabe\n\tldr\tr1, =0xbeef\n\tldr\tr2, =0x123\n\tldr\tr3, =44\n\tsvc\t#3\n\tbx\tlr\nENDPROC(test_svcall3)\n\nENTRY(test_svcall4)\n\tldr\tr3, =45\n\tpush\t{r3}\n\tldr\tr0, =0xbabe\n\tldr\tr1, =0xbeef\n\tldr\tr2, =0x123\n\tldr\tr3, =0x456\n\tsvc\t#4\n\tadd\tsp, #4\n\tbx\tlr\nENDPROC(test_svcall4)\n\nENTRY(test_svcall5)\n\tldr\tr2, =0xcafe\n\tldr\tr3, =46\n\tpush\t{r2, r3}\n\tldr\tr0, =0xbabe\n\tldr\tr1, =0xbeef\n\tldr\tr2, =0x123\n\tldr\tr3, =0x456\n\tsvc\t#5\n\tadd\tsp, #8\n\tbx\tlr\nENDPROC(test_svcall5)\n\nENTRY(test_svcall6)\n\tldr\tr1, =0xcafe\n\tldr\tr2, =0xfeed\n\tldr\tr3, =47\n\tpush\t{r1-r3}\n\tldr\tr0, =0xbabe\n\tldr\tr1, =0xbeef\n\tldr\tr2, =0x123\n\tldr\tr3, =0x456\n\tsvc\t#6\n\tadd\tsp, #12\n\tbx\tlr\nENDPROC(test_svcall6)\n\nENTRY(get_sp)\n\tmov\tr0, sp\n\tbx\tlr\nENDPROC(get_sp)\n"
  },
  {
    "path": "tests/sysconf_1/main.c",
    "content": "#include \"unit.h\"\n#include \"unistd.h\"\n\nint main()\n{\n    printk(\"system tick per seconds: %d\\n\", sysconf(_SC_CLK_TCK));\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/test_1/main.c",
    "content": "/* this test must pass */\n\n#include \"unit.h\"\n\nint main()\n{\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/test_2/main.c",
    "content": "/* this test must fail */\n\n#include \"unit.h\"\n\nint main()\n{\n    TEST_EXIT(1);\n}\n"
  },
  {
    "path": "tests/thread_1/main.c",
    "content": "/* simple thread create and thread yield */\n\n#include <pthread.h>\n\n#include <kernel/kernel.h>\n\n#include \"unit.h\"\n\nstatic int val = 123;\n\nstatic void *fn(void *arg)\n{\n    val += (int) arg;\n\n    return 0;\n}\n\nint main()\n{\n    pthread_t tip;\n\n    if (pthread_create(&tip, NULL, fn, (void *) 321)) {\n        printk(\"failed: can't create new posix thread.\\n\");\n        TEST_EXIT(1);\n    }\n\n    if (sched_yield()) {\n        printk(\"failed: can't yield cpu to another thread.\\n\");\n        TEST_EXIT(1);\n    }\n\n    while (val != 444)\n        ;\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/thread_2/main.c",
    "content": "#include <pthread.h>\n\n#include <kernel/kernel.h>\n\n#include \"unit.h\"\n\nint vals[] = {0, 0, 0, 0};\n\nvoid *fn(void *arg)\n{\n    for (;;) {\n        vals[(int) arg]++;\n        sched_yield();\n    }\n\n    return 0;\n}\n\nint main()\n{\n    pthread_t tips[4];\n\n    for (int i = 0; i < 4; i++) {\n        if (pthread_create(&tips[i], NULL, fn, (void *) i)) {\n            printk(\"failed: can't create new posix thread.\\n\");\n            TEST_EXIT(1);\n        }\n    }\n\n    for (int i = 0; i < 4; i++) {\n        while (vals[i] < 10)\n            sched_yield();\n    }\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/thread_3/main.c",
    "content": "#include <pthread.h>\n\n#include <kernel/kernel.h>\n\n#include \"unit.h\"\n\nstatic int count;\n\nvoid *fn(void *arg)\n{\n    (void) arg;\n    printk(\"counter=%d\\n\", count);\n    count++;\n\n    return 0;\n}\n\nint main()\n{\n    pthread_t tips[25];\n\n    /* reduce the thread stack size to 256 bytes */\n    pthread_attr_t attr;\n    pthread_attr_setstacksize(&attr, 256);\n\n    /* create 25 threads */\n    for (int i = 0; i < 25; i++) {\n        if (pthread_create(&tips[i], &attr, fn, NULL)) {\n            printk(\"failed: can't create new posix thread.\\n\");\n            TEST_EXIT(1);\n        }\n    }\n\n    while (count < 24)\n        sched_yield();\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/thread_4/main.c",
    "content": "#include <pthread.h>\n\n#include <kernel/kernel.h>\n#include <kernel/sched.h>\n#include <kernel/task.h>\n#include <kernel/thread.h>\n\n#include \"unit.h\"\n\nstatic int count;\n\nstatic void *fn(void *arg)\n{\n    int i = (int) arg;\n\n    if (i != count)\n        TEST_EXIT(1);\n    count++;\n\n    return 0;\n}\n\nint main()\n{\n    struct thread_info *t;\n    CURRENT_TASK_INFO(curr_task);\n\n    for (int i = 0; i < 15; i++) {\n        t = thread_create(fn, (void *) i, THREAD_PRIV_USER, 256, curr_task);\n        if (!t) {\n            printk(\"failed: can't create new posix thread.\\n\");\n            TEST_EXIT(1);\n        }\n        thread_set_priority(t, i);\n        sched_enqueue(t);\n    }\n\n    sched_yield();\n\n    if (count != 15) {\n        printk(\"count != 15 (%d)\\n\", count);\n        TEST_EXIT(1);\n    }\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/thread_5/main.c",
    "content": "/* test thread_join() */\n\n#include \"kernel.h\"\n#include \"pthread.h\"\n#include \"unit.h\"\n#include \"linux/stddef.h\"\n\nvoid *fn(void *arg)\n{\n    /* printk(\"Running thread with arg=%d\\n\", (int) arg); */\n    pthread_exit(arg);\n\n    return 0;\n}\n\nint main()\n{\n    void *retval;\n    pthread_t tips[7];\n\n    printk(\"Creating a bunch of threads...  \");\n    for (int i = 0; i < 7; i++) {\n        if (pthread_create(&tips[i], NULL, fn, (void *) i)) {\n            printk(\"failed: can't create new posix thread.\\n\");\n            TEST_EXIT(1);\n        }\n    }\n    printk(\"OK\\n\");\n\n    for (int i = 0; i < 7; i++) {\n        printk(\"Joining thread with arg=%d...  \", i);\n        pthread_join(tips[i], &retval);\n        if ((int) retval != i) {\n            printk(\"pthread_join: wrong return value: got %d, expected %d\\n\",\n                   (int) retval, i);\n            TEST_EXIT(1);\n        }\n        printk(\"OK\\n\");\n    }\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/thread_6/main.c",
    "content": "/* create/delete 1000 threads */\n\n#include <pthread.h>\n#include <stddef.h>\n#include <kernel/kernel.h>\n#include \"unit.h\"\n\nstatic void *fn(void *arg)\n{\n    (void) arg;\n    printk(\".\");\n\n    return 0;\n}\n\nint main()\n{\n    pthread_t thread;\n\n    for (int i = 0; i < 1000; i++) {\n        if (pthread_create(&thread, NULL, fn, NULL)) {\n            printk(\"failed: can't create new posix thread.\\n\");\n            TEST_EXIT(1);\n        }\n        pthread_detach(thread);\n        sched_yield();\n    }\n    printk(\"\\n\");\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/timer_1/main.c",
    "content": "#include <stddef.h>\n#include <time.h>\n\n#include \"unit.h\"\n#include <kernel/time.h>\n#include <kernel/signal.h>\n\n#define EXPECTED_VALUE 0xabadcafeul\n\nstatic volatile int received_signal;\n\nstatic void event(union sigval sival)\n{\n    if (sival.sival_int != (int) EXPECTED_VALUE) {\n        printk(\"error: Did not received expected value (%x != %x)\\n\",\n               sival.sival_int, EXPECTED_VALUE);\n        TEST_EXIT(1);\n    }\n    received_signal = 1;\n}\n\nint main()\n{\n    struct sigevent sevp = {.sigev_notify_function = event,\n                            .sigev_value.sival_int = EXPECTED_VALUE};\n    struct itimerspec val = {.it_value = {.tv_sec = 1, .tv_nsec = 0}};\n    timer_t timerid;\n\n    timer_create(1, &sevp, &timerid);\n    timer_settime(timerid, 0, &val, NULL);\n\n    while (!received_signal)\n        ;\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/timer_2/main.c",
    "content": "/* simple create  multiple concurrent timers */\n\n#include <signal.h>\n#include <stddef.h>\n#include <time.h>\n\n#include \"kernel.h\"\n#include \"unit.h\"\n\nstatic volatile int vals[] = {0, 0, 0, 0};\n\nstatic void event(union sigval sival)\n{\n    printk(\"In event %d.\\n\", sival.sival_int);\n    vals[sival.sival_int] = 1;\n}\n\nint main()\n{\n    struct sigevent sevp = {.sigev_notify_function = event};\n    timer_t timerid[4];\n\n    for (int i = 0; i < 4; i++) {\n        printk(\"Creating timer %d...\\n\", i);\n        sevp.sigev_value.sival_int = i;\n        timer_create(0, &sevp, &timerid[i]);\n\n        unsigned long val_in_nsecs = 750 * (i + 1) * 1000000;\n        struct itimerspec val = {\n            .it_value = {.tv_sec = val_in_nsecs / 1000000000,\n                         .tv_nsec = val_in_nsecs % 1000000000}};\n        timer_settime(timerid[i], 0, &val, NULL);\n    }\n    printk(\"All timers armed.\\n\");\n    for (int i = 0; i < 4; i++) {\n        while (vals[i] == 0)\n            ;\n    }\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/timer_3/main.c",
    "content": "#include <stddef.h>\n#include <time.h>\n\n#include \"unit.h\"\n#include <kernel/time.h>\n#include <kernel/signal.h>\n\nstatic void event(union sigval sival)\n{\n    (void) sival;\n}\n\nint main()\n{\n    struct sigevent sevp = {.sigev_notify_function = event};\n    struct itimerspec val = {.it_value = {.tv_sec = 5, .tv_nsec = 0}};\n    timer_t timerid;\n\n    timer_create(1, &sevp, &timerid);\n    timer_settime(timerid, 0, &val, NULL);\n    do {\n        timer_gettime(timerid, &val);\n    } while (val.it_value.tv_sec != 4);\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/timer_4/main.c",
    "content": "/* simple test for interval timers */\n\n#include <stddef.h>\n#include <time.h>\n\n#include <kernel/time.h>\n#include <kernel/signal.h>\n\n#include \"unit.h\"\n\nstatic volatile int count;\n\nstatic void event(union sigval sival)\n{\n    (void) sival;\n\n    printk(\"Counter=%d\\n\", ++count);\n}\n\nint main()\n{\n    timer_t timerid;\n    struct sigevent sevp = {\n        .sigev_notify_function = event,\n    };\n    struct itimerspec val = {\n        .it_value = {.tv_sec = 1, .tv_nsec = 0},\n        .it_interval = {.tv_sec = 1, .tv_nsec = 0},\n    };\n\n    timer_create(0, &sevp, &timerid);\n    timer_settime(timerid, 0, &val, NULL);\n    while (count < 4)\n        ;\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/timer_5/main.c",
    "content": "/* test cancellation of timer */\n\n#include <stddef.h>\n#include <time.h>\n\n#include <kernel/time.h>\n#include <kernel/signal.h>\n\n#include \"unit.h\"\n\nstatic volatile int canary;\nstatic volatile int count;\n\nstatic void event_should_not_happen(union sigval sival)\n{\n    (void) sival;\n\n    canary++;\n}\n\nstatic void event_check(union sigval sival)\n{\n    (void) sival;\n\n    count++;\n}\n\nint main()\n{\n    timer_t timerid_a;\n    struct sigevent sevp_a = {\n        .sigev_notify_function = event_should_not_happen,\n    };\n    timer_create(0, &sevp_a, &timerid_a);\n\n    timer_t timerid_b;\n    struct sigevent sevp_b = {\n        .sigev_notify_function = event_check,\n    };\n    timer_create(0, &sevp_b, &timerid_b);\n\n    struct itimerspec val_a = {\n        .it_value = {.tv_sec = 1, .tv_nsec = 0},\n        .it_interval = {.tv_sec = 0, .tv_nsec = 0},\n    };\n    struct itimerspec val_b = {\n        .it_value = {.tv_sec = 2, .tv_nsec = 0},\n        .it_interval = {.tv_sec = 0, .tv_nsec = 0},\n    };\n    struct itimerspec val_zero = {\n        .it_value = {.tv_sec = 0, .tv_nsec = 0},\n        .it_interval = {.tv_sec = 0, .tv_nsec = 0},\n    };\n    timer_settime(timerid_a, 0, &val_a, NULL);\n    timer_settime(timerid_b, 0, &val_b, NULL);\n    timer_settime(timerid_a, 0, &val_zero, NULL);\n\n    while (!count)\n        ;\n    if (canary)\n        TEST_EXIT(1);\n\n    TEST_EXIT(0);\n}\n"
  },
  {
    "path": "tests/ucontext_1/main.c",
    "content": "#include <ucontext.h>\n\n#include \"unit.h\"\n\nstatic int test_status;\nstatic ucontext_t main_context, other_context;\nstatic unsigned int ctx_stack[128];\n\nvoid __printk_putchar(char c);\n\nstatic void pputs(const char *s)\n{\n    for (; *s != '\\0'; s++)\n        __printk_putchar(*s);\n}\n\nvoid test(int a1, int a2, int a3, int a4)\n{\n    pputs(\"Hello from a new context!\\n\");\n\n    if ((a1 != 9) || (a2 != 0xcafe) || (a3 != 13) || (a4 != 14)) {\n        pputs(\"failed: incorrect correct arg.\\n\");\n        test_status = 1;\n    }\n}\n\nint main()\n{\n    other_context.uc_link = &main_context;\n    other_context.uc_stack.ss_sp = &ctx_stack[128];\n\n    /* pass 4 arguments to the new context, and swap */\n    makecontext(&other_context, test, 4, 9, 0xcafe, 13, 14);\n    swapcontext(&main_context, &other_context);\n\n    pputs(\"And back to the main context.\\n\");\n\n    TEST_EXIT(test_status);\n}\n"
  },
  {
    "path": "user/cat.c",
    "content": "#include <fcntl.h>\n#include <sys/cdefs.h>\n#include <unistd.h>\n\n#include <kernel/kernel.h>\n\n#include \"sh.h\"\n\nstatic int cat(__unused int argc, char *argv[])\n{\n    char buf[] = {0, 0};\n    int fd;\n    int retval = 0;\n\n    for (int i = 1; i < argc; i++) {\n        fd = open(argv[i], 0);\n        if (fd < 0) {\n            printk(\"cat: %s: No such file or directory\\n\", argv[1]);\n            retval = 1;\n        }\n        while (read(fd, &buf, 1))\n            printk(\"%s\", buf);\n        close(fd);\n    }\n\n    return retval;\n}\n\nHOOK_BUILTIN_CMD(cat, cat);\n"
  },
  {
    "path": "user/echo.c",
    "content": "#include <kernel/kernel.h>\n\n#include \"sh.h\"\n\nstatic int echo(int argc, char *argv[])\n{\n    if (argc == 1)\n        return 0;\n    for (int i = 1; i < argc; i++) {\n        if (i == 1)\n            printk(\"%s\", argv[i]);\n        else\n            printk(\" %s\", argv[i]);\n    }\n    printk(\"\\n\");\n\n    return 0;\n}\n\nHOOK_BUILTIN_CMD(echo, echo);\n"
  },
  {
    "path": "user/exit.c",
    "content": "#include \"sh.h\"\n#include \"platform.h\"\n\nstatic int _exit(__unused int argc, __unused char *argv[])\n{\n    __platform_halt();\n    return 0;\n}\n\nHOOK_BUILTIN_CMD(exit, _exit);\n"
  },
  {
    "path": "user/halt.c",
    "content": "#include \"sh.h\"\n#include \"platform.h\"\n\nstatic int halt(__unused int argc, __unused char *argv[])\n{\n    __platform_halt();\n    return 0;\n}\n\nHOOK_BUILTIN_CMD(halt, halt);\n"
  },
  {
    "path": "user/ls.c",
    "content": "#include <stdio.h>\n#include <unistd.h>\n\n#include <piko/dirent.h>\n\n#include <kernel/fs.h>\n#include <kernel/kernel.h>\n\n#include \"sh.h\"\n\nstatic int ls(int argc, char *argv[])\n{\n    DIR *dir;\n    struct dirent dirent;\n    struct dirent *result;\n\n    if (argc == 1)\n        dir = opendir(\"/\");  // FIXME: get current directory\n    else\n        dir = opendir(argv[1]);\n\n    do {\n        readdir_r(dir, &dirent, &result);\n        if (result != NULL)\n            printk(\"% 6d %s\\n\", dirent.d_ino, dirent.d_name);\n    } while (result != NULL);\n    closedir(dir);\n\n    return 0;\n}\n\nHOOK_BUILTIN_CMD(ls, ls);\n"
  },
  {
    "path": "user/reboot.c",
    "content": "#include \"sh.h\"\n#include \"platform.h\"\n\nstatic int reboot(__unused int argc, __unused char *argv[])\n{\n    NVIC_SystemReset();\n\n    return -1;\n}\n\nHOOK_BUILTIN_CMD(reboot, reboot);\n"
  },
  {
    "path": "user/sh.c",
    "content": "#include <fcntl.h>\n#include <stdio.h>\n#include <string.h>\n#include <unistd.h>\n\n#include <kernel/kernel.h>\n\n#include \"sh.h\"\n#include \"platform.h\"\n\nstatic const char ESC_SEQ_ERASE_LINE[] = \"\\033[K\";\n\nstatic const char TERM_PROMPT[] = \"$ \";\nstatic const char TERM_CMD_NOT_FOUND[] = \"command not found: \";\nstatic const char TERM_CRLF[] = \"\\r\\n\";\n\nstatic int parse_command_line(char *buf, char *argv[])\n{\n    int buflen = strlen(buf);\n\n    int argc = 1;\n    argv[0] = (char *) buf;\n    for (int i = 0; i < buflen; i++) {\n        if (buf[i] == ' ') {\n            buf[i++] = '\\0';\n            while (buf[i] == ' ')\n                i++;\n            argv[argc++] = (char *) &buf[i];\n        }\n    }\n\n    return argc;\n}\n\nstatic void exec_command(char *buf, int fd)\n{\n    extern unsigned long __shell_cmd_start__;\n    extern unsigned long __shell_cmd_end__;\n\n    int argc;\n    char *argv[ARG_COUNT_MAX];\n    struct shell_cmd *cmd;\n\n    argc = parse_command_line(buf, argv);\n    for (cmd = (struct shell_cmd *) &__shell_cmd_start__;\n         (unsigned long) cmd < (unsigned long) &__shell_cmd_end__; cmd++)\n        if (!strcmp(cmd->name, argv[0])) {\n            cmd->func(argc, argv);\n            return;\n        }\n\n    write(fd, TERM_CMD_NOT_FOUND, sizeof(TERM_CMD_NOT_FOUND) - 1);\n    write(fd, buf, strlen(buf));\n    write(fd, TERM_CRLF, sizeof(TERM_CRLF) - 1);\n}\n\nstatic void cursor_backward(int n, int fd)\n{\n    char ebuf[8];\n\n    if (n > 0) {\n        sprintf(ebuf, \"\\033[%dD\", n);\n        write(fd, ebuf, strlen(ebuf));\n    }\n}\n\nstatic int cur;\nstatic int cur_eol;\nstatic char buf_line[BUF_LINE_LEN];\n\nstatic void readline(int fd)\n{\n    char c;\n\n    read(fd, &c, 1);\n    switch (c) {\n    case ASCII_CARRIAGE_RETURN:\n        write(fd, TERM_CRLF, sizeof(TERM_CRLF) - 1);\n        if (cur_eol > 0) {\n            exec_command(buf_line, fd);\n            cur = 0; /* relative position to prompt's last char */\n            cur_eol = 0;\n        }\n        write(fd, TERM_PROMPT, sizeof(TERM_PROMPT) - 1);\n        break;\n    case ASCII_BACKSPACE:\n    case ASCII_DELETE:  // XXX: QEMU sends DEL instead of BS\n        if (cur > 0) {\n            if (cur < cur_eol) {\n                for (int i = cur; i <= cur_eol; i++)\n                    buf_line[i - 1] = buf_line[i];\n            }\n            buf_line[--cur_eol] = ASCII_NULL;\n            cur--;\n            cursor_backward(1, fd);\n            write(fd, ESC_SEQ_ERASE_LINE, sizeof(ESC_SEQ_ERASE_LINE) - 1);\n            write(fd, &buf_line[cur], strlen(&buf_line[cur]));\n            cursor_backward(cur_eol - cur, fd);\n        }\n        break;\n    case ' ' ... '~':\n        if (cur < cur_eol) {\n            for (int i = cur_eol; i >= cur; i--)\n                buf_line[i + 1] = buf_line[i];\n        }\n        buf_line[cur++] = c;\n        buf_line[++cur_eol] = '\\0';\n        write(fd, &buf_line[cur - 1], strlen(&buf_line[cur - 1]));\n        cursor_backward(cur_eol - cur, fd);\n        break;\n    case ASCII_ESCAPE:\n        read(fd, &c, 1);\n        if (c != '[')  // this is not an escape sequence\n            return;\n        read(fd, &c, 1);\n        switch (c) {\n        case 'C':\n            if (cur < cur_eol) {\n                cur++;\n                write(fd, \"\\033[C\", 3);\n            }\n            break;\n        case 'D':\n            if (cur > 0) {\n                cur--;\n                write(fd, \"\\033[D\", 3);\n            }\n            break;\n        default:\n            printk(\"Unhandled escape sequence!\\n\");\n            return;\n        }\n    default:;\n    }\n}\n\nint minishell(void *options)\n{\n    (void) options;\n\n    int fd = open(\"/dev/ttyS0\", 0);\n    if (fd < 0)\n        printk(\"cannot open /dev/ttyS0\\n\");\n\n    write(fd, TERM_PROMPT, sizeof(TERM_PROMPT) - 1);\n    for (;;) {\n        readline(fd);\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "user/sh.h",
    "content": "#ifndef USER_SH_H\n#define USER_SH_H\n\n#define ARG_COUNT_MAX 8\n#define BUF_LINE_LEN 128\n\nenum ascii_control_char {\n    ASCII_NULL = 000,\n    ASCII_BACKSPACE = 010,\n    ASCII_CARRIAGE_RETURN = 015,\n    ASCII_ESCAPE = 033,\n    ASCII_DELETE = 0177,\n};\n\nstruct shell_cmd {\n    char *name;\n    int (*func)(int argc, char *argv[]);\n};\n\n#define HOOK_BUILTIN_CMD(_name, _func)                               \\\n    static struct shell_cmd shell_##_name                            \\\n        __attribute__((section(\".shell_cmd\"), aligned(sizeof(long)), \\\n                       used)) = {.name = #_name, .func = _func}\n\n#endif /* !USER_SH_H */\n"
  }
]