Repository: minosproject/minos2 Branch: main Commit: 47d21453fb3b Files: 2570 Total size: 6.1 MB Directory structure: gitextract_nsoa8kf4/ ├── .gitignore ├── Makefile ├── README.md ├── generic/ │ └── include/ │ └── uapi/ │ ├── bootdata.h │ ├── gvm.h │ ├── hypervisor.h │ ├── ramdisk.h │ ├── time.h │ └── virtio_mmio.h ├── kernel/ │ ├── .gitignore │ ├── Kconfig │ ├── LICENSE │ ├── Makefile │ ├── VERSION │ ├── apps/ │ │ ├── Kconfig │ │ ├── Makefile │ │ ├── esh/ │ │ │ ├── COPYING │ │ │ ├── Kconfig │ │ │ ├── Makefile │ │ │ ├── README.md │ │ │ ├── esh.c │ │ │ ├── esh.h │ │ │ ├── esh_argparser.c │ │ │ ├── esh_argparser.h │ │ │ ├── esh_config.h │ │ │ ├── esh_hist.c │ │ │ ├── esh_hist.h │ │ │ ├── esh_incl_config.h │ │ │ ├── esh_internal.h │ │ │ └── shell.c │ │ └── init.c │ ├── arch/ │ │ └── aarch64/ │ │ ├── Kconfig │ │ ├── Makefile │ │ ├── core/ │ │ │ ├── Kconfig │ │ │ ├── Makefile │ │ │ ├── aarch64.S │ │ │ ├── aarch64_IRQ.c │ │ │ ├── aarch64_sync.c │ │ │ ├── arch.c │ │ │ ├── arm_arch_timer.c │ │ │ ├── asm-offset.c │ │ │ ├── boot.S │ │ │ ├── cache.S │ │ │ ├── cpu.c │ │ │ ├── cpu_feature.c │ │ │ ├── entry.S │ │ │ ├── fpsimd.c │ │ │ ├── mem_map.S │ │ │ ├── stage1.c │ │ │ ├── stage1.h │ │ │ └── vector.S │ │ ├── include/ │ │ │ └── asm/ │ │ │ ├── aarch64_common.h │ │ │ ├── aarch64_el1_reg.h │ │ │ ├── aarch64_el2_reg.h │ │ │ ├── aarch64_el2_vhe_reg.h │ │ │ ├── aarch64_helper.h │ │ │ ├── aarch64_reg.h │ │ │ ├── arch.h │ │ │ ├── asm_current.h │ │ │ ├── asm_marco.S │ │ │ ├── asm_types.h │ │ │ ├── atomic.h │ │ │ ├── barrier.h │ │ │ ├── bitops.h │ │ │ ├── cache.h │ │ │ ├── cmpxchg.h │ │ │ ├── cpu_feature.h │ │ │ ├── div64.h │ │ │ ├── gic_reg.h │ │ │ ├── io.h │ │ │ ├── power.h │ │ │ ├── psci.h │ │ │ ├── reg.h │ │ │ ├── svccc.h │ │ │ ├── syscall.h │ │ │ ├── tcb.h │ │ │ ├── time.h │ │ │ ├── tlb.h │ │ │ ├── trap.h │ │ │ ├── uaccess.h │ │ │ ├── virt.h │ │ │ └── vtcb.h │ │ ├── lds/ │ │ │ ├── Makefile │ │ │ └── minos.lds.S │ │ ├── lib/ │ │ │ ├── Makefile │ │ │ ├── atomic.S │ │ │ ├── bitops.S │ │ │ ├── memchr.S │ │ │ ├── memcmp.S │ │ │ ├── memcpy.S │ │ │ ├── memmove.S │ │ │ ├── memset.S │ │ │ ├── spinlock.S │ │ │ ├── strchr.S │ │ │ ├── strcmp.S │ │ │ ├── strcpy.S │ │ │ ├── strlen.S │ │ │ ├── strncmp.S │ │ │ ├── strnlen.S │ │ │ ├── strrchr.S │ │ │ └── ticket_lock.S │ │ ├── userspace/ │ │ │ ├── Makefile │ │ │ └── asm_syscall.c │ │ └── virt/ │ │ ├── Kconfig │ │ ├── Makefile │ │ ├── arch_virt.c │ │ ├── smc_service.c │ │ ├── stage2.c │ │ ├── stage2.h │ │ ├── svc_service.c │ │ ├── trap.c │ │ ├── vfp.c │ │ ├── vmsa.c │ │ └── vtimer.c │ ├── configs/ │ │ ├── espressobin_defconfig │ │ ├── fvp_a76_defconfig │ │ ├── fvp_defconfig │ │ ├── fvp_rtos_defconfig │ │ ├── kvim3_defconfig │ │ ├── qemu_arm64_defconfig │ │ ├── r8a7795_defconfig │ │ ├── rpi_3_defconfig │ │ └── rpi_4_defconfig │ ├── core/ │ │ ├── Kconfig │ │ ├── Makefile │ │ ├── bitmap.c │ │ ├── bootarg.c │ │ ├── calltrace.c │ │ ├── delay.c │ │ ├── event.c │ │ ├── find_bit.c │ │ ├── flag.c │ │ ├── hook.c │ │ ├── host_vspace.c │ │ ├── hweight.c │ │ ├── idle.c │ │ ├── init.c │ │ ├── iomem.c │ │ ├── irq.c │ │ ├── kmem.c │ │ ├── mbox.c │ │ ├── mem.c │ │ ├── minos.c │ │ ├── mutex.c │ │ ├── page.c │ │ ├── percpu.c │ │ ├── print.c │ │ ├── queue.c │ │ ├── ramdisk.c │ │ ├── sched.c │ │ ├── sem.c │ │ ├── slab.c │ │ ├── smp.c │ │ ├── stdlib.c │ │ ├── string.c │ │ ├── task.c │ │ └── timer.c │ ├── drivers/ │ │ ├── Kconfig │ │ ├── Makefile │ │ ├── console.c │ │ ├── device_id.c │ │ ├── iommu/ │ │ │ ├── Kconfig │ │ │ ├── Makefile │ │ │ ├── ipmmu-plat.c │ │ │ └── ipmmu.c │ │ ├── irq-chips/ │ │ │ ├── Kconfig │ │ │ ├── Makefile │ │ │ ├── gicv2.c │ │ │ ├── gicv3.c │ │ │ ├── irq-bcm2836.c │ │ │ └── irqchip.c │ │ ├── of/ │ │ │ ├── Kconfig │ │ │ ├── Makefile │ │ │ ├── of.c │ │ │ └── of_mm.c │ │ ├── serial/ │ │ │ ├── Kconfig │ │ │ ├── Makefile │ │ │ ├── pl011.h │ │ │ ├── serial_bcm283x_mu.c │ │ │ ├── serial_meson.c │ │ │ ├── serial_mvebu_a3700.c │ │ │ ├── serial_pl011.c │ │ │ └── serial_scif.c │ │ └── tty.c │ ├── dtbs/ │ │ ├── Makefile │ │ ├── armada-3720-community-v5.dts │ │ ├── bcm2837-rpi-3-b-plus.dts │ │ ├── bcm2838-rpi-4-b-32bit.dts │ │ ├── bcm2838-rpi-4-b.dts │ │ ├── foundation-v8-gicv2.dts │ │ ├── foundation-v8-gicv3-zephyr.dts │ │ ├── foundation-v8-gicv3.dts │ │ ├── kvim3.dts │ │ ├── qemu-arm64.dts │ │ └── r8a7795.dts │ ├── include/ │ │ ├── device/ │ │ │ ├── bcm_irq.h │ │ │ ├── gicv2.h │ │ │ └── gicv3.h │ │ ├── libfdt/ │ │ │ ├── fdt.h │ │ │ ├── libfdt.h │ │ │ └── libfdt_env.h │ │ ├── minos/ │ │ │ ├── arch.h │ │ │ ├── atomic.h │ │ │ ├── bitmap.h │ │ │ ├── bitops.h │ │ │ ├── bootarg.h │ │ │ ├── calltrace.h │ │ │ ├── channel.h │ │ │ ├── compiler.h │ │ │ ├── console.h │ │ │ ├── const.h │ │ │ ├── cpumask.h │ │ │ ├── current.h │ │ │ ├── device_id.h │ │ │ ├── errno.h │ │ │ ├── event.h │ │ │ ├── flag.h │ │ │ ├── hook.h │ │ │ ├── init.h │ │ │ ├── irq.h │ │ │ ├── kbuild.h │ │ │ ├── list.h │ │ │ ├── math64.h │ │ │ ├── mbox.h │ │ │ ├── memattr.h │ │ │ ├── memory.h │ │ │ ├── minos.h │ │ │ ├── mm.h │ │ │ ├── mutex.h │ │ │ ├── of.h │ │ │ ├── os.h │ │ │ ├── page.h │ │ │ ├── percpu.h │ │ │ ├── platform.h │ │ │ ├── pm.h │ │ │ ├── preempt.h │ │ │ ├── print.h │ │ │ ├── queue.h │ │ │ ├── ramdisk.h │ │ │ ├── raw_spinlock.h │ │ │ ├── sched.h │ │ │ ├── sem.h │ │ │ ├── shell_command.h │ │ │ ├── slab.h │ │ │ ├── smp.h │ │ │ ├── softirq.h │ │ │ ├── spinlock.h │ │ │ ├── stdlib.h │ │ │ ├── string.h │ │ │ ├── symbol.h │ │ │ ├── task.h │ │ │ ├── task_def.h │ │ │ ├── task_info.h │ │ │ ├── time.h │ │ │ ├── timer.h │ │ │ ├── tty.h │ │ │ ├── types.h │ │ │ └── varlist.h │ │ ├── uapi/ │ │ │ ├── kobject_uapi.h │ │ │ └── procinfo_uapi.h │ │ ├── uspace/ │ │ │ ├── elf.h │ │ │ ├── handle.h │ │ │ ├── iqueue.h │ │ │ ├── kobject.h │ │ │ ├── poll.h │ │ │ ├── proc.h │ │ │ ├── procinfo.h │ │ │ ├── socket.h │ │ │ ├── syscall.h │ │ │ ├── uaccess.h │ │ │ └── vspace.h │ │ └── virt/ │ │ ├── hypercall.h │ │ ├── iommu.h │ │ ├── os.h │ │ ├── resource.h │ │ ├── vdev.h │ │ ├── virq.h │ │ ├── virq_chip.h │ │ ├── virt.h │ │ ├── virtio.h │ │ ├── vm.h │ │ ├── vm_mmap.h │ │ ├── vm_pm.h │ │ ├── vmbox.h │ │ ├── vmcs.h │ │ ├── vmm.h │ │ └── vmodule.h │ ├── libs/ │ │ ├── Kconfig │ │ ├── Makefile │ │ ├── libfdt/ │ │ │ ├── Makefile │ │ │ ├── fdt.c │ │ │ ├── fdt_addresses.c │ │ │ ├── fdt_empty_tree.c │ │ │ ├── fdt_overlay.c │ │ │ ├── fdt_ro.c │ │ │ ├── fdt_rw.c │ │ │ ├── fdt_strerror.c │ │ │ ├── fdt_sw.c │ │ │ ├── fdt_wip.c │ │ │ └── libfdt_internal.h │ │ └── shell_command/ │ │ ├── Kconfig │ │ ├── Makefile │ │ ├── clear.c │ │ ├── help_cmd.c │ │ ├── shell_command.c │ │ └── task_cmd.c │ ├── platform/ │ │ ├── Kconfig │ │ ├── Makefile │ │ ├── amlogic/ │ │ │ ├── Kconfig │ │ │ ├── Makefile │ │ │ ├── amlogic_smc.c │ │ │ └── kvim3.c │ │ ├── espressobin/ │ │ │ ├── Kconfig │ │ │ ├── Makefile │ │ │ └── espressobin.c │ │ ├── fvp/ │ │ │ ├── Kconfig │ │ │ ├── Makefile │ │ │ └── fvp.c │ │ ├── platform.c │ │ ├── qemu/ │ │ │ ├── Kconfig │ │ │ ├── Makefile │ │ │ └── qemu.c │ │ ├── r8a7795/ │ │ │ ├── Kconfig │ │ │ ├── Makefile │ │ │ └── r8a7795.c │ │ ├── raspberry3/ │ │ │ ├── Kconfig │ │ │ ├── Makefile │ │ │ └── raspberry3.c │ │ └── raspberry4/ │ │ ├── Kconfig │ │ ├── Makefile │ │ └── raspberry4.c │ ├── scripts/ │ │ ├── Kconfiglib/ │ │ │ ├── LICENSE.txt │ │ │ ├── MANIFEST.in │ │ │ ├── README.rst │ │ │ ├── alldefconfig.py │ │ │ ├── allmodconfig.py │ │ │ ├── allnoconfig.py │ │ │ ├── allyesconfig.py │ │ │ ├── defconfig.py │ │ │ ├── examples/ │ │ │ │ ├── Kmenuconfig │ │ │ │ ├── allnoconfig_walk.py │ │ │ │ ├── defconfig_oldconfig.py │ │ │ │ ├── dumpvars.py │ │ │ │ ├── eval_expr.py │ │ │ │ ├── find_symbol.py │ │ │ │ ├── help_grep.py │ │ │ │ ├── list_undefined.py │ │ │ │ ├── menuconfig_example.py │ │ │ │ ├── merge_config.py │ │ │ │ ├── print_config_tree.py │ │ │ │ ├── print_sym_info.py │ │ │ │ └── print_tree.py │ │ │ ├── genconfig.py │ │ │ ├── guiconfig.py │ │ │ ├── kconfiglib.py │ │ │ ├── listnewconfig.py │ │ │ ├── menuconfig.py │ │ │ ├── oldconfig.py │ │ │ ├── olddefconfig.py │ │ │ ├── savedefconfig.py │ │ │ ├── setconfig.py │ │ │ ├── setup.cfg │ │ │ ├── setup.py │ │ │ ├── tests/ │ │ │ │ ├── Kappend │ │ │ │ ├── Kassignable │ │ │ │ ├── Kchoice │ │ │ │ ├── Kdefconfig_existent │ │ │ │ ├── Kdefconfig_existent_but_n │ │ │ │ ├── Kdefconfig_nonexistent │ │ │ │ ├── Kdefconfig_srctree │ │ │ │ ├── Kdepcopy │ │ │ │ ├── Kdeploop0 │ │ │ │ ├── Kdeploop1 │ │ │ │ ├── Kdeploop10 │ │ │ │ ├── Kdeploop2 │ │ │ │ ├── Kdeploop3 │ │ │ │ ├── Kdeploop4 │ │ │ │ ├── Kdeploop5 │ │ │ │ ├── Kdeploop6 │ │ │ │ ├── Kdeploop7 │ │ │ │ ├── Kdeploop8 │ │ │ │ ├── Kdeploop9 │ │ │ │ ├── Kdirdep │ │ │ │ ├── Kescape │ │ │ │ ├── Keval │ │ │ │ ├── Kexpr_items │ │ │ │ ├── Kheader │ │ │ │ ├── Khelp │ │ │ │ ├── Kifremoval │ │ │ │ ├── Kimply │ │ │ │ ├── Kinclude_path │ │ │ │ ├── Kinclude_path_sourced_1 │ │ │ │ ├── Kinclude_path_sourced_2 │ │ │ │ ├── Kitemlists │ │ │ │ ├── Klocation │ │ │ │ ├── Klocation_sourced │ │ │ │ ├── Kmainmenu │ │ │ │ ├── Kmenuconfig │ │ │ │ ├── Kmisc │ │ │ │ ├── Kmissingrsource │ │ │ │ ├── Kmissingsource │ │ │ │ ├── Korder │ │ │ │ ├── Kpreprocess │ │ │ │ ├── Krange │ │ │ │ ├── Krecursive1 │ │ │ │ ├── Krecursive2 │ │ │ │ ├── Kreferenced │ │ │ │ ├── Krelation │ │ │ │ ├── Krepr │ │ │ │ ├── Kstr │ │ │ │ ├── Kundef │ │ │ │ ├── Kuserfunctions │ │ │ │ ├── Kvisibility │ │ │ │ ├── config_indented │ │ │ │ ├── config_set_bool │ │ │ │ ├── config_set_string │ │ │ │ ├── defconfig_1 │ │ │ │ ├── defconfig_2 │ │ │ │ ├── empty │ │ │ │ ├── kconfigfunctions.py │ │ │ │ ├── reltest │ │ │ │ └── sub/ │ │ │ │ ├── Kconfig_symlink_2 │ │ │ │ ├── Kconfig_symlink_3 │ │ │ │ ├── Klocation_grsourced1 │ │ │ │ ├── Klocation_grsourced2 │ │ │ │ ├── Klocation_gsourced1 │ │ │ │ ├── Klocation_gsourced2 │ │ │ │ ├── Klocation_rsourced │ │ │ │ ├── defconfig_in_sub │ │ │ │ └── sub/ │ │ │ │ └── Kconfig_symlink_1 │ │ │ └── testsuite.py │ │ ├── Minos.build.mk │ │ ├── Minos.clean.mk │ │ ├── Minos.config.mk │ │ └── generate_allsymbols.py │ ├── userspace/ │ │ ├── Kconfig │ │ ├── Makefile │ │ ├── endpoint.c │ │ ├── futex.c │ │ ├── handle.c │ │ ├── iqueue.c │ │ ├── irq.c │ │ ├── kobject.c │ │ ├── kobject_copy.c │ │ ├── kobject_copy.h │ │ ├── notify.c │ │ ├── pma.c │ │ ├── poll.c │ │ ├── port.c │ │ ├── process.c │ │ ├── procinfo.c │ │ ├── root_service.c │ │ ├── socket.c │ │ ├── stdio.c │ │ ├── syscall.c │ │ ├── thread.c │ │ ├── time.c │ │ ├── uaccess.c │ │ └── vspace.c │ └── virt/ │ ├── Kconfig │ ├── Makefile │ ├── debug_console.c │ ├── hypercall.c │ ├── iommu.c │ ├── os/ │ │ ├── Kconfig │ │ ├── Makefile │ │ ├── os.c │ │ ├── os_linux.c │ │ ├── os_other.c │ │ └── os_xnu.c │ ├── resource.c │ ├── shmem.c │ ├── varm_timer.c │ ├── vdev.c │ ├── vfault.c │ ├── virq.c │ ├── virq_chips/ │ │ ├── Kconfig │ │ ├── Makefile │ │ ├── bcm_virq.c │ │ ├── vaic.c │ │ ├── vgic.c │ │ ├── vgic.h │ │ ├── vgicv2.c │ │ ├── vgicv3.c │ │ └── virq_chip.c │ ├── virtio_mmio.c │ ├── vm.c │ ├── vm_daemon.c │ ├── vm_pm.c │ ├── vmbox/ │ │ ├── Kconfig │ │ ├── Makefile │ │ ├── vmbox.c │ │ └── vmbox_hvc.c │ ├── vmcs.c │ ├── vmm.c │ ├── vmodule.c │ ├── vrtc.c │ └── vwdt.c ├── scripts/ │ ├── app_build.mk │ └── lib_build.mk ├── tools/ │ ├── install.sh │ ├── make_ramdisk.sh │ ├── mkrmd/ │ │ ├── Makefile │ │ └── mkrmd.c │ ├── qemu.sh │ └── u-boot.img ├── user.driver/ │ └── virtio-blk/ │ ├── Makefile │ ├── main.c │ ├── virtio-blk.c │ ├── virtio.c │ └── virtio.h ├── user.libc/ │ ├── .gitignore │ ├── .mailmap │ ├── COPYRIGHT │ ├── INSTALL │ ├── Makefile │ ├── README │ ├── VERSION │ ├── WHATSNEW │ ├── arch/ │ │ ├── aarch64/ │ │ │ ├── atomic_arch.h │ │ │ ├── bits/ │ │ │ │ ├── alltypes.h.in │ │ │ │ ├── fcntl.h │ │ │ │ ├── fenv.h │ │ │ │ ├── float.h │ │ │ │ ├── hwcap.h │ │ │ │ ├── posix.h │ │ │ │ ├── reg.h │ │ │ │ ├── setjmp.h │ │ │ │ ├── signal.h │ │ │ │ ├── stat.h │ │ │ │ ├── stdint.h │ │ │ │ ├── syscall.h.in │ │ │ │ └── user.h │ │ │ ├── crt_arch.h │ │ │ ├── fp_arch.h │ │ │ ├── kstat.h │ │ │ ├── pthread_arch.h │ │ │ ├── reloc.h │ │ │ └── syscall_arch.h │ │ ├── arm/ │ │ │ ├── arch.mak │ │ │ ├── atomic_arch.h │ │ │ ├── bits/ │ │ │ │ ├── alltypes.h.in │ │ │ │ ├── fcntl.h │ │ │ │ ├── fenv.h │ │ │ │ ├── float.h │ │ │ │ ├── hwcap.h │ │ │ │ ├── ioctl_fix.h │ │ │ │ ├── ipcstat.h │ │ │ │ ├── msg.h │ │ │ │ ├── posix.h │ │ │ │ ├── ptrace.h │ │ │ │ ├── reg.h │ │ │ │ ├── sem.h │ │ │ │ ├── setjmp.h │ │ │ │ ├── shm.h │ │ │ │ ├── signal.h │ │ │ │ ├── stat.h │ │ │ │ ├── stdint.h │ │ │ │ ├── syscall.h.in │ │ │ │ └── user.h │ │ │ ├── crt_arch.h │ │ │ ├── kstat.h │ │ │ ├── pthread_arch.h │ │ │ ├── reloc.h │ │ │ └── syscall_arch.h │ │ ├── generic/ │ │ │ ├── bits/ │ │ │ │ ├── dirent.h │ │ │ │ ├── errno.h │ │ │ │ ├── fcntl.h │ │ │ │ ├── fenv.h │ │ │ │ ├── hwcap.h │ │ │ │ ├── io.h │ │ │ │ ├── ioctl.h │ │ │ │ ├── ioctl_fix.h │ │ │ │ ├── ipc.h │ │ │ │ ├── ipcstat.h │ │ │ │ ├── kd.h │ │ │ │ ├── limits.h │ │ │ │ ├── link.h │ │ │ │ ├── mman.h │ │ │ │ ├── msg.h │ │ │ │ ├── poll.h │ │ │ │ ├── ptrace.h │ │ │ │ ├── resource.h │ │ │ │ ├── sem.h │ │ │ │ ├── shm.h │ │ │ │ ├── socket.h │ │ │ │ ├── soundcard.h │ │ │ │ ├── statfs.h │ │ │ │ ├── termios.h │ │ │ │ └── vt.h │ │ │ └── fp_arch.h │ │ ├── i386/ │ │ │ ├── arch.mak │ │ │ ├── atomic_arch.h │ │ │ ├── bits/ │ │ │ │ ├── alltypes.h.in │ │ │ │ ├── fenv.h │ │ │ │ ├── float.h │ │ │ │ ├── io.h │ │ │ │ ├── ipcstat.h │ │ │ │ ├── limits.h │ │ │ │ ├── mman.h │ │ │ │ ├── msg.h │ │ │ │ ├── posix.h │ │ │ │ ├── ptrace.h │ │ │ │ ├── reg.h │ │ │ │ ├── sem.h │ │ │ │ ├── setjmp.h │ │ │ │ ├── shm.h │ │ │ │ ├── signal.h │ │ │ │ ├── stat.h │ │ │ │ ├── stdint.h │ │ │ │ ├── syscall.h.in │ │ │ │ └── user.h │ │ │ ├── crt_arch.h │ │ │ ├── kstat.h │ │ │ ├── pthread_arch.h │ │ │ ├── reloc.h │ │ │ └── syscall_arch.h │ │ ├── m68k/ │ │ │ ├── arch.mak │ │ │ ├── atomic_arch.h │ │ │ ├── bits/ │ │ │ │ ├── alltypes.h.in │ │ │ │ ├── fcntl.h │ │ │ │ ├── fenv.h │ │ │ │ ├── float.h │ │ │ │ ├── ipcstat.h │ │ │ │ ├── msg.h │ │ │ │ ├── posix.h │ │ │ │ ├── ptrace.h │ │ │ │ ├── reg.h │ │ │ │ ├── sem.h │ │ │ │ ├── setjmp.h │ │ │ │ ├── shm.h │ │ │ │ ├── signal.h │ │ │ │ ├── stat.h │ │ │ │ ├── stdint.h │ │ │ │ ├── syscall.h.in │ │ │ │ └── user.h │ │ │ ├── crt_arch.h │ │ │ ├── kstat.h │ │ │ ├── pthread_arch.h │ │ │ ├── reloc.h │ │ │ └── syscall_arch.h │ │ ├── microblaze/ │ │ │ ├── arch.mak │ │ │ ├── atomic_arch.h │ │ │ ├── bits/ │ │ │ │ ├── alltypes.h.in │ │ │ │ ├── float.h │ │ │ │ ├── ipcstat.h │ │ │ │ ├── msg.h │ │ │ │ ├── posix.h │ │ │ │ ├── reg.h │ │ │ │ ├── sem.h │ │ │ │ ├── setjmp.h │ │ │ │ ├── shm.h │ │ │ │ ├── signal.h │ │ │ │ ├── stat.h │ │ │ │ ├── stdint.h │ │ │ │ ├── syscall.h.in │ │ │ │ └── user.h │ │ │ ├── crt_arch.h │ │ │ ├── kstat.h │ │ │ ├── pthread_arch.h │ │ │ ├── reloc.h │ │ │ └── syscall_arch.h │ │ ├── mips/ │ │ │ ├── arch.mak │ │ │ ├── atomic_arch.h │ │ │ ├── bits/ │ │ │ │ ├── alltypes.h.in │ │ │ │ ├── errno.h │ │ │ │ ├── fcntl.h │ │ │ │ ├── fenv.h │ │ │ │ ├── float.h │ │ │ │ ├── hwcap.h │ │ │ │ ├── ioctl.h │ │ │ │ ├── ipcstat.h │ │ │ │ ├── mman.h │ │ │ │ ├── msg.h │ │ │ │ ├── poll.h │ │ │ │ ├── posix.h │ │ │ │ ├── ptrace.h │ │ │ │ ├── reg.h │ │ │ │ ├── resource.h │ │ │ │ ├── sem.h │ │ │ │ ├── setjmp.h │ │ │ │ ├── shm.h │ │ │ │ ├── signal.h │ │ │ │ ├── socket.h │ │ │ │ ├── stat.h │ │ │ │ ├── statfs.h │ │ │ │ ├── stdint.h │ │ │ │ ├── syscall.h.in │ │ │ │ ├── termios.h │ │ │ │ └── user.h │ │ │ ├── crt_arch.h │ │ │ ├── ksigaction.h │ │ │ ├── kstat.h │ │ │ ├── pthread_arch.h │ │ │ ├── reloc.h │ │ │ └── syscall_arch.h │ │ ├── mips64/ │ │ │ ├── atomic_arch.h │ │ │ ├── bits/ │ │ │ │ ├── alltypes.h.in │ │ │ │ ├── errno.h │ │ │ │ ├── fcntl.h │ │ │ │ ├── fenv.h │ │ │ │ ├── float.h │ │ │ │ ├── hwcap.h │ │ │ │ ├── ioctl.h │ │ │ │ ├── ipc.h │ │ │ │ ├── mman.h │ │ │ │ ├── poll.h │ │ │ │ ├── posix.h │ │ │ │ ├── ptrace.h │ │ │ │ ├── reg.h │ │ │ │ ├── resource.h │ │ │ │ ├── setjmp.h │ │ │ │ ├── signal.h │ │ │ │ ├── socket.h │ │ │ │ ├── stat.h │ │ │ │ ├── statfs.h │ │ │ │ ├── stdint.h │ │ │ │ ├── syscall.h.in │ │ │ │ ├── termios.h │ │ │ │ └── user.h │ │ │ ├── crt_arch.h │ │ │ ├── ksigaction.h │ │ │ ├── kstat.h │ │ │ ├── pthread_arch.h │ │ │ ├── reloc.h │ │ │ └── syscall_arch.h │ │ ├── mipsn32/ │ │ │ ├── arch.mak │ │ │ ├── atomic_arch.h │ │ │ ├── bits/ │ │ │ │ ├── alltypes.h.in │ │ │ │ ├── errno.h │ │ │ │ ├── fcntl.h │ │ │ │ ├── fenv.h │ │ │ │ ├── float.h │ │ │ │ ├── hwcap.h │ │ │ │ ├── ioctl.h │ │ │ │ ├── ipcstat.h │ │ │ │ ├── mman.h │ │ │ │ ├── msg.h │ │ │ │ ├── poll.h │ │ │ │ ├── posix.h │ │ │ │ ├── ptrace.h │ │ │ │ ├── reg.h │ │ │ │ ├── resource.h │ │ │ │ ├── sem.h │ │ │ │ ├── setjmp.h │ │ │ │ ├── shm.h │ │ │ │ ├── signal.h │ │ │ │ ├── socket.h │ │ │ │ ├── stat.h │ │ │ │ ├── statfs.h │ │ │ │ ├── stdint.h │ │ │ │ ├── syscall.h.in │ │ │ │ ├── termios.h │ │ │ │ └── user.h │ │ │ ├── crt_arch.h │ │ │ ├── ksigaction.h │ │ │ ├── kstat.h │ │ │ ├── pthread_arch.h │ │ │ ├── reloc.h │ │ │ └── syscall_arch.h │ │ ├── or1k/ │ │ │ ├── arch.mak │ │ │ ├── atomic_arch.h │ │ │ ├── bits/ │ │ │ │ ├── alltypes.h.in │ │ │ │ ├── float.h │ │ │ │ ├── ipcstat.h │ │ │ │ ├── limits.h │ │ │ │ ├── msg.h │ │ │ │ ├── posix.h │ │ │ │ ├── reg.h │ │ │ │ ├── sem.h │ │ │ │ ├── setjmp.h │ │ │ │ ├── shm.h │ │ │ │ ├── signal.h │ │ │ │ ├── stat.h │ │ │ │ ├── stdint.h │ │ │ │ ├── syscall.h.in │ │ │ │ └── user.h │ │ │ ├── crt_arch.h │ │ │ ├── kstat.h │ │ │ ├── pthread_arch.h │ │ │ ├── reloc.h │ │ │ └── syscall_arch.h │ │ ├── powerpc/ │ │ │ ├── arch.mak │ │ │ ├── atomic_arch.h │ │ │ ├── bits/ │ │ │ │ ├── alltypes.h.in │ │ │ │ ├── errno.h │ │ │ │ ├── fcntl.h │ │ │ │ ├── fenv.h │ │ │ │ ├── float.h │ │ │ │ ├── hwcap.h │ │ │ │ ├── ioctl.h │ │ │ │ ├── ipc.h │ │ │ │ ├── ipcstat.h │ │ │ │ ├── mman.h │ │ │ │ ├── msg.h │ │ │ │ ├── posix.h │ │ │ │ ├── ptrace.h │ │ │ │ ├── reg.h │ │ │ │ ├── sem.h │ │ │ │ ├── setjmp.h │ │ │ │ ├── shm.h │ │ │ │ ├── signal.h │ │ │ │ ├── socket.h │ │ │ │ ├── stat.h │ │ │ │ ├── stdint.h │ │ │ │ ├── syscall.h.in │ │ │ │ ├── termios.h │ │ │ │ └── user.h │ │ │ ├── crt_arch.h │ │ │ ├── kstat.h │ │ │ ├── pthread_arch.h │ │ │ ├── reloc.h │ │ │ └── syscall_arch.h │ │ ├── powerpc64/ │ │ │ ├── atomic_arch.h │ │ │ ├── bits/ │ │ │ │ ├── alltypes.h.in │ │ │ │ ├── errno.h │ │ │ │ ├── fcntl.h │ │ │ │ ├── fenv.h │ │ │ │ ├── float.h │ │ │ │ ├── hwcap.h │ │ │ │ ├── ioctl.h │ │ │ │ ├── ipc.h │ │ │ │ ├── mman.h │ │ │ │ ├── posix.h │ │ │ │ ├── ptrace.h │ │ │ │ ├── reg.h │ │ │ │ ├── setjmp.h │ │ │ │ ├── shm.h │ │ │ │ ├── signal.h │ │ │ │ ├── socket.h │ │ │ │ ├── stat.h │ │ │ │ ├── stdint.h │ │ │ │ ├── syscall.h.in │ │ │ │ ├── termios.h │ │ │ │ └── user.h │ │ │ ├── crt_arch.h │ │ │ ├── kstat.h │ │ │ ├── pthread_arch.h │ │ │ ├── reloc.h │ │ │ └── syscall_arch.h │ │ ├── riscv64/ │ │ │ ├── atomic_arch.h │ │ │ ├── bits/ │ │ │ │ ├── alltypes.h.in │ │ │ │ ├── fenv.h │ │ │ │ ├── float.h │ │ │ │ ├── posix.h │ │ │ │ ├── reg.h │ │ │ │ ├── setjmp.h │ │ │ │ ├── signal.h │ │ │ │ ├── stat.h │ │ │ │ ├── stdint.h │ │ │ │ ├── syscall.h.in │ │ │ │ └── user.h │ │ │ ├── crt_arch.h │ │ │ ├── kstat.h │ │ │ ├── pthread_arch.h │ │ │ ├── reloc.h │ │ │ └── syscall_arch.h │ │ ├── s390x/ │ │ │ ├── atomic_arch.h │ │ │ ├── bits/ │ │ │ │ ├── alltypes.h.in │ │ │ │ ├── fcntl.h │ │ │ │ ├── fenv.h │ │ │ │ ├── float.h │ │ │ │ ├── hwcap.h │ │ │ │ ├── ioctl_fix.h │ │ │ │ ├── limits.h │ │ │ │ ├── link.h │ │ │ │ ├── posix.h │ │ │ │ ├── ptrace.h │ │ │ │ ├── reg.h │ │ │ │ ├── setjmp.h │ │ │ │ ├── signal.h │ │ │ │ ├── stat.h │ │ │ │ ├── statfs.h │ │ │ │ ├── stdint.h │ │ │ │ ├── syscall.h.in │ │ │ │ └── user.h │ │ │ ├── crt_arch.h │ │ │ ├── kstat.h │ │ │ ├── pthread_arch.h │ │ │ ├── reloc.h │ │ │ └── syscall_arch.h │ │ ├── sh/ │ │ │ ├── arch.mak │ │ │ ├── atomic_arch.h │ │ │ ├── bits/ │ │ │ │ ├── alltypes.h.in │ │ │ │ ├── fenv.h │ │ │ │ ├── float.h │ │ │ │ ├── hwcap.h │ │ │ │ ├── ioctl.h │ │ │ │ ├── ipcstat.h │ │ │ │ ├── limits.h │ │ │ │ ├── msg.h │ │ │ │ ├── posix.h │ │ │ │ ├── ptrace.h │ │ │ │ ├── sem.h │ │ │ │ ├── setjmp.h │ │ │ │ ├── shm.h │ │ │ │ ├── signal.h │ │ │ │ ├── stat.h │ │ │ │ ├── stdint.h │ │ │ │ ├── syscall.h.in │ │ │ │ └── user.h │ │ │ ├── crt_arch.h │ │ │ ├── ksigaction.h │ │ │ ├── kstat.h │ │ │ ├── pthread_arch.h │ │ │ ├── reloc.h │ │ │ └── syscall_arch.h │ │ ├── x32/ │ │ │ ├── atomic_arch.h │ │ │ ├── bits/ │ │ │ │ ├── alltypes.h.in │ │ │ │ ├── fcntl.h │ │ │ │ ├── fenv.h │ │ │ │ ├── float.h │ │ │ │ ├── io.h │ │ │ │ ├── ioctl_fix.h │ │ │ │ ├── ipc.h │ │ │ │ ├── limits.h │ │ │ │ ├── mman.h │ │ │ │ ├── msg.h │ │ │ │ ├── posix.h │ │ │ │ ├── ptrace.h │ │ │ │ ├── reg.h │ │ │ │ ├── sem.h │ │ │ │ ├── setjmp.h │ │ │ │ ├── shm.h │ │ │ │ ├── signal.h │ │ │ │ ├── socket.h │ │ │ │ ├── stat.h │ │ │ │ ├── statfs.h │ │ │ │ ├── stdint.h │ │ │ │ ├── syscall.h.in │ │ │ │ └── user.h │ │ │ ├── crt_arch.h │ │ │ ├── ksigaction.h │ │ │ ├── kstat.h │ │ │ ├── pthread_arch.h │ │ │ ├── reloc.h │ │ │ └── syscall_arch.h │ │ └── x86_64/ │ │ ├── atomic_arch.h │ │ ├── bits/ │ │ │ ├── alltypes.h.in │ │ │ ├── fenv.h │ │ │ ├── float.h │ │ │ ├── io.h │ │ │ ├── limits.h │ │ │ ├── mman.h │ │ │ ├── posix.h │ │ │ ├── ptrace.h │ │ │ ├── reg.h │ │ │ ├── sem.h │ │ │ ├── setjmp.h │ │ │ ├── signal.h │ │ │ ├── stat.h │ │ │ ├── stdint.h │ │ │ ├── syscall.h.in │ │ │ └── user.h │ │ ├── crt_arch.h │ │ ├── ksigaction.h │ │ ├── kstat.h │ │ ├── pthread_arch.h │ │ ├── reloc.h │ │ └── syscall_arch.h │ ├── build.sh │ ├── compat/ │ │ └── time32/ │ │ ├── __xstat.c │ │ ├── adjtime32.c │ │ ├── adjtimex_time32.c │ │ ├── aio_suspend_time32.c │ │ ├── clock_adjtime32.c │ │ ├── clock_getres_time32.c │ │ ├── clock_gettime32.c │ │ ├── clock_nanosleep_time32.c │ │ ├── clock_settime32.c │ │ ├── cnd_timedwait_time32.c │ │ ├── ctime32.c │ │ ├── ctime32_r.c │ │ ├── difftime32.c │ │ ├── fstat_time32.c │ │ ├── fstatat_time32.c │ │ ├── ftime32.c │ │ ├── futimens_time32.c │ │ ├── futimes_time32.c │ │ ├── futimesat_time32.c │ │ ├── getitimer_time32.c │ │ ├── getrusage_time32.c │ │ ├── gettimeofday_time32.c │ │ ├── gmtime32.c │ │ ├── gmtime32_r.c │ │ ├── localtime32.c │ │ ├── localtime32_r.c │ │ ├── lstat_time32.c │ │ ├── lutimes_time32.c │ │ ├── mktime32.c │ │ ├── mq_timedreceive_time32.c │ │ ├── mq_timedsend_time32.c │ │ ├── mtx_timedlock_time32.c │ │ ├── nanosleep_time32.c │ │ ├── ppoll_time32.c │ │ ├── pselect_time32.c │ │ ├── pthread_cond_timedwait_time32.c │ │ ├── pthread_mutex_timedlock_time32.c │ │ ├── pthread_rwlock_timedrdlock_time32.c │ │ ├── pthread_rwlock_timedwrlock_time32.c │ │ ├── pthread_timedjoin_np_time32.c │ │ ├── recvmmsg_time32.c │ │ ├── sched_rr_get_interval_time32.c │ │ ├── select_time32.c │ │ ├── sem_timedwait_time32.c │ │ ├── semtimedop_time32.c │ │ ├── setitimer_time32.c │ │ ├── settimeofday_time32.c │ │ ├── sigtimedwait_time32.c │ │ ├── stat_time32.c │ │ ├── stime32.c │ │ ├── thrd_sleep_time32.c │ │ ├── time32.c │ │ ├── time32.h │ │ ├── time32gm.c │ │ ├── timer_gettime32.c │ │ ├── timer_settime32.c │ │ ├── timerfd_gettime32.c │ │ ├── timerfd_settime32.c │ │ ├── timespec_get_time32.c │ │ ├── utime_time32.c │ │ ├── utimensat_time32.c │ │ ├── utimes_time32.c │ │ ├── wait3_time32.c │ │ └── wait4_time32.c │ ├── configure │ ├── crt/ │ │ ├── Scrt1.c │ │ ├── crt1.c │ │ ├── crti.c │ │ ├── crtn.c │ │ └── rcrt1.c │ ├── dynamic.list │ ├── include/ │ │ ├── aio.h │ │ ├── alloca.h │ │ ├── alltypes.h.in │ │ ├── ar.h │ │ ├── arpa/ │ │ │ ├── ftp.h │ │ │ ├── inet.h │ │ │ ├── nameser.h │ │ │ ├── nameser_compat.h │ │ │ ├── telnet.h │ │ │ └── tftp.h │ │ ├── assert.h │ │ ├── bitmap.h │ │ ├── bitops.h │ │ ├── byteswap.h │ │ ├── complex.h │ │ ├── cpio.h │ │ ├── crypt.h │ │ ├── ctype.h │ │ ├── dirent.h │ │ ├── dlfcn.h │ │ ├── elf.h │ │ ├── endian.h │ │ ├── err.h │ │ ├── errno.h │ │ ├── fcntl.h │ │ ├── features.h │ │ ├── fenv.h │ │ ├── float.h │ │ ├── fmtmsg.h │ │ ├── fnmatch.h │ │ ├── ftw.h │ │ ├── getopt.h │ │ ├── glob.h │ │ ├── grp.h │ │ ├── iconv.h │ │ ├── ifaddrs.h │ │ ├── inttypes.h │ │ ├── iso646.h │ │ ├── langinfo.h │ │ ├── lastlog.h │ │ ├── libgen.h │ │ ├── libintl.h │ │ ├── limits.h │ │ ├── link.h │ │ ├── locale.h │ │ ├── malloc.h │ │ ├── math.h │ │ ├── memory.h │ │ ├── minos/ │ │ │ ├── barrier.h │ │ │ ├── compiler.h │ │ │ ├── debug.h │ │ │ ├── device.h │ │ │ ├── kobject.h │ │ │ ├── kobject_uapi.h │ │ │ ├── libc.h │ │ │ ├── list.h │ │ │ ├── mutex.h │ │ │ ├── poll.h │ │ │ ├── procinfo.h │ │ │ ├── procinfo_uapi.h │ │ │ ├── proto.h │ │ │ ├── sched.h │ │ │ ├── service.h │ │ │ ├── spinlock.h │ │ │ ├── thread.h │ │ │ └── types.h │ │ ├── mntent.h │ │ ├── monetary.h │ │ ├── mqueue.h │ │ ├── net/ │ │ │ ├── ethernet.h │ │ │ ├── if.h │ │ │ ├── if_arp.h │ │ │ └── route.h │ │ ├── netdb.h │ │ ├── netinet/ │ │ │ ├── ether.h │ │ │ ├── icmp6.h │ │ │ ├── if_ether.h │ │ │ ├── igmp.h │ │ │ ├── in.h │ │ │ ├── in_systm.h │ │ │ ├── ip.h │ │ │ ├── ip6.h │ │ │ ├── ip_icmp.h │ │ │ ├── tcp.h │ │ │ └── udp.h │ │ ├── netpacket/ │ │ │ └── packet.h │ │ ├── nl_types.h │ │ ├── paths.h │ │ ├── poll.h │ │ ├── pthread.h │ │ ├── pty.h │ │ ├── pwd.h │ │ ├── regex.h │ │ ├── resolv.h │ │ ├── sched.h │ │ ├── scsi/ │ │ │ ├── scsi.h │ │ │ ├── scsi_ioctl.h │ │ │ └── sg.h │ │ ├── search.h │ │ ├── semaphore.h │ │ ├── setjmp.h │ │ ├── shadow.h │ │ ├── signal.h │ │ ├── spawn.h │ │ ├── stdalign.h │ │ ├── stdarg.h │ │ ├── stdbool.h │ │ ├── stdc-predef.h │ │ ├── stddef.h │ │ ├── stdint.h │ │ ├── stdio.h │ │ ├── stdio_ext.h │ │ ├── stdlib.h │ │ ├── stdnoreturn.h │ │ ├── string.h │ │ ├── strings.h │ │ ├── stropts.h │ │ ├── sys/ │ │ │ ├── acct.h │ │ │ ├── auxv.h │ │ │ ├── cachectl.h │ │ │ ├── dir.h │ │ │ ├── epoll.h │ │ │ ├── errno.h │ │ │ ├── eventfd.h │ │ │ ├── fanotify.h │ │ │ ├── fcntl.h │ │ │ ├── file.h │ │ │ ├── fsuid.h │ │ │ ├── inotify.h │ │ │ ├── io.h │ │ │ ├── ioctl.h │ │ │ ├── ipc.h │ │ │ ├── kd.h │ │ │ ├── klog.h │ │ │ ├── membarrier.h │ │ │ ├── mman.h │ │ │ ├── msg.h │ │ │ ├── mtio.h │ │ │ ├── param.h │ │ │ ├── personality.h │ │ │ ├── poll.h │ │ │ ├── prctl.h │ │ │ ├── procfs.h │ │ │ ├── ptrace.h │ │ │ ├── quota.h │ │ │ ├── random.h │ │ │ ├── reboot.h │ │ │ ├── reg.h │ │ │ ├── resource.h │ │ │ ├── select.h │ │ │ ├── sem.h │ │ │ ├── sendfile.h │ │ │ ├── shm.h │ │ │ ├── signal.h │ │ │ ├── signalfd.h │ │ │ ├── socket.h │ │ │ ├── soundcard.h │ │ │ ├── stat.h │ │ │ ├── statfs.h │ │ │ ├── statvfs.h │ │ │ ├── stropts.h │ │ │ ├── swap.h │ │ │ ├── syscall.h │ │ │ ├── sysinfo.h │ │ │ ├── syslog.h │ │ │ ├── sysmacros.h │ │ │ ├── termios.h │ │ │ ├── time.h │ │ │ ├── timeb.h │ │ │ ├── timerfd.h │ │ │ ├── times.h │ │ │ ├── timex.h │ │ │ ├── ttydefaults.h │ │ │ ├── types.h │ │ │ ├── ucontext.h │ │ │ ├── uio.h │ │ │ ├── un.h │ │ │ ├── user.h │ │ │ ├── utsname.h │ │ │ ├── vfs.h │ │ │ ├── vt.h │ │ │ ├── wait.h │ │ │ └── xattr.h │ │ ├── syscall.h │ │ ├── sysexits.h │ │ ├── syslog.h │ │ ├── tar.h │ │ ├── termios.h │ │ ├── tgmath.h │ │ ├── threads.h │ │ ├── time.h │ │ ├── uchar.h │ │ ├── ucontext.h │ │ ├── ulimit.h │ │ ├── unistd.h │ │ ├── utime.h │ │ ├── utmp.h │ │ ├── utmpx.h │ │ ├── values.h │ │ ├── wait.h │ │ ├── wchar.h │ │ ├── wctype.h │ │ └── wordexp.h │ ├── ldso/ │ │ ├── dlstart.c │ │ └── dynlink.c │ ├── src/ │ │ ├── bitmap/ │ │ │ ├── bitmap.c │ │ │ ├── find_bit.c │ │ │ └── hweight.c │ │ ├── complex/ │ │ │ ├── __cexp.c │ │ │ ├── __cexpf.c │ │ │ ├── cabs.c │ │ │ ├── cabsf.c │ │ │ ├── cabsl.c │ │ │ ├── cacos.c │ │ │ ├── cacosf.c │ │ │ ├── cacosh.c │ │ │ ├── cacoshf.c │ │ │ ├── cacoshl.c │ │ │ ├── cacosl.c │ │ │ ├── carg.c │ │ │ ├── cargf.c │ │ │ ├── cargl.c │ │ │ ├── casin.c │ │ │ ├── casinf.c │ │ │ ├── casinh.c │ │ │ ├── casinhf.c │ │ │ ├── casinhl.c │ │ │ ├── casinl.c │ │ │ ├── catan.c │ │ │ ├── catanf.c │ │ │ ├── catanh.c │ │ │ ├── catanhf.c │ │ │ ├── catanhl.c │ │ │ ├── catanl.c │ │ │ ├── ccos.c │ │ │ ├── ccosf.c │ │ │ ├── ccosh.c │ │ │ ├── ccoshf.c │ │ │ ├── ccoshl.c │ │ │ ├── ccosl.c │ │ │ ├── cexp.c │ │ │ ├── cexpf.c │ │ │ ├── cexpl.c │ │ │ ├── cimag.c │ │ │ ├── cimagf.c │ │ │ ├── cimagl.c │ │ │ ├── clog.c │ │ │ ├── clogf.c │ │ │ ├── clogl.c │ │ │ ├── conj.c │ │ │ ├── conjf.c │ │ │ ├── conjl.c │ │ │ ├── cpow.c │ │ │ ├── cpowf.c │ │ │ ├── cpowl.c │ │ │ ├── cproj.c │ │ │ ├── cprojf.c │ │ │ ├── cprojl.c │ │ │ ├── creal.c │ │ │ ├── crealf.c │ │ │ ├── creall.c │ │ │ ├── csin.c │ │ │ ├── csinf.c │ │ │ ├── csinh.c │ │ │ ├── csinhf.c │ │ │ ├── csinhl.c │ │ │ ├── csinl.c │ │ │ ├── csqrt.c │ │ │ ├── csqrtf.c │ │ │ ├── csqrtl.c │ │ │ ├── ctan.c │ │ │ ├── ctanf.c │ │ │ ├── ctanh.c │ │ │ ├── ctanhf.c │ │ │ ├── ctanhl.c │ │ │ └── ctanl.c │ │ ├── crypt/ │ │ │ ├── crypt.c │ │ │ ├── crypt_blowfish.c │ │ │ ├── crypt_des.c │ │ │ ├── crypt_des.h │ │ │ ├── crypt_md5.c │ │ │ ├── crypt_r.c │ │ │ ├── crypt_sha256.c │ │ │ ├── crypt_sha512.c │ │ │ └── encrypt.c │ │ ├── ctype/ │ │ │ ├── __ctype_b_loc.c │ │ │ ├── __ctype_get_mb_cur_max.c │ │ │ ├── __ctype_tolower_loc.c │ │ │ ├── __ctype_toupper_loc.c │ │ │ ├── alpha.h │ │ │ ├── casemap.h │ │ │ ├── isalnum.c │ │ │ ├── isalpha.c │ │ │ ├── isascii.c │ │ │ ├── isblank.c │ │ │ ├── iscntrl.c │ │ │ ├── isdigit.c │ │ │ ├── isgraph.c │ │ │ ├── islower.c │ │ │ ├── isprint.c │ │ │ ├── ispunct.c │ │ │ ├── isspace.c │ │ │ ├── isupper.c │ │ │ ├── iswalnum.c │ │ │ ├── iswalpha.c │ │ │ ├── iswblank.c │ │ │ ├── iswcntrl.c │ │ │ ├── iswctype.c │ │ │ ├── iswdigit.c │ │ │ ├── iswgraph.c │ │ │ ├── iswlower.c │ │ │ ├── iswprint.c │ │ │ ├── iswpunct.c │ │ │ ├── iswspace.c │ │ │ ├── iswupper.c │ │ │ ├── iswxdigit.c │ │ │ ├── isxdigit.c │ │ │ ├── nonspacing.h │ │ │ ├── punct.h │ │ │ ├── toascii.c │ │ │ ├── tolower.c │ │ │ ├── toupper.c │ │ │ ├── towctrans.c │ │ │ ├── wcswidth.c │ │ │ ├── wctrans.c │ │ │ ├── wcwidth.c │ │ │ └── wide.h │ │ ├── dirent/ │ │ │ ├── __dirent.h │ │ │ ├── alphasort.c │ │ │ ├── closedir.c │ │ │ ├── dirfd.c │ │ │ ├── fdopendir.c │ │ │ ├── opendir.c │ │ │ ├── readdir.c │ │ │ ├── readdir_r.c │ │ │ ├── rewinddir.c │ │ │ ├── scandir.c │ │ │ ├── seekdir.c │ │ │ ├── telldir.c │ │ │ └── versionsort.c │ │ ├── env/ │ │ │ ├── __environ.c │ │ │ ├── __init_tls.c │ │ │ ├── __libc_start_main.c │ │ │ ├── __reset_tls.c │ │ │ ├── __stack_chk_fail.c │ │ │ ├── clearenv.c │ │ │ ├── getenv.c │ │ │ ├── putenv.c │ │ │ ├── secure_getenv.c │ │ │ ├── setenv.c │ │ │ └── unsetenv.c │ │ ├── errno/ │ │ │ ├── __errno_location.c │ │ │ ├── __strerror.h │ │ │ └── strerror.c │ │ ├── exit/ │ │ │ ├── _Exit.c │ │ │ ├── abort.c │ │ │ ├── abort_lock.c │ │ │ ├── arm/ │ │ │ │ └── __aeabi_atexit.c │ │ │ ├── assert.c │ │ │ ├── at_quick_exit.c │ │ │ ├── atexit.c │ │ │ ├── exit.c │ │ │ └── quick_exit.c │ │ ├── fcntl/ │ │ │ ├── creat.c │ │ │ ├── fcntl.c │ │ │ ├── open.c │ │ │ ├── openat.c │ │ │ ├── posix_fadvise.c │ │ │ └── posix_fallocate.c │ │ ├── fenv/ │ │ │ ├── __flt_rounds.c │ │ │ ├── arm/ │ │ │ │ └── fenv.c │ │ │ ├── fegetexceptflag.c │ │ │ ├── feholdexcept.c │ │ │ ├── fenv.c │ │ │ ├── fesetexceptflag.c │ │ │ ├── fesetround.c │ │ │ ├── feupdateenv.c │ │ │ ├── m68k/ │ │ │ │ └── fenv.c │ │ │ ├── mips/ │ │ │ │ └── fenv-sf.c │ │ │ ├── mips64/ │ │ │ │ └── fenv-sf.c │ │ │ ├── mipsn32/ │ │ │ │ └── fenv-sf.c │ │ │ ├── powerpc/ │ │ │ │ └── fenv-sf.c │ │ │ ├── powerpc64/ │ │ │ │ └── fenv.c │ │ │ ├── riscv64/ │ │ │ │ └── fenv-sf.c │ │ │ ├── s390x/ │ │ │ │ └── fenv.c │ │ │ └── sh/ │ │ │ └── fenv-nofpu.c │ │ ├── include/ │ │ │ ├── arpa/ │ │ │ │ └── inet.h │ │ │ ├── crypt.h │ │ │ ├── errno.h │ │ │ ├── features.h │ │ │ ├── langinfo.h │ │ │ ├── pthread.h │ │ │ ├── resolv.h │ │ │ ├── signal.h │ │ │ ├── stdio.h │ │ │ ├── stdlib.h │ │ │ ├── string.h │ │ │ ├── sys/ │ │ │ │ ├── auxv.h │ │ │ │ ├── membarrier.h │ │ │ │ ├── mman.h │ │ │ │ ├── sysinfo.h │ │ │ │ └── time.h │ │ │ ├── time.h │ │ │ ├── unistd.h │ │ │ └── wchar.h │ │ ├── internal/ │ │ │ ├── aio_impl.h │ │ │ ├── asm.inc │ │ │ ├── atomic.h │ │ │ ├── complex_impl.h │ │ │ ├── defsysinfo.c │ │ │ ├── dynlink.h │ │ │ ├── fdpic_crt.h │ │ │ ├── floatscan.c │ │ │ ├── floatscan.h │ │ │ ├── fork_impl.h │ │ │ ├── futex.h │ │ │ ├── intscan.c │ │ │ ├── intscan.h │ │ │ ├── ksigaction.h │ │ │ ├── libc.c │ │ │ ├── libc.h │ │ │ ├── libm.h │ │ │ ├── locale_impl.h │ │ │ ├── lock.h │ │ │ ├── procfdname.c │ │ │ ├── pthread_impl.h │ │ │ ├── sh/ │ │ │ │ └── __shcall.c │ │ │ ├── shgetc.c │ │ │ ├── shgetc.h │ │ │ ├── stdio_impl.h │ │ │ ├── syscall.h │ │ │ ├── syscall_ret.c │ │ │ ├── vdso.c │ │ │ └── version.c │ │ ├── ldso/ │ │ │ ├── __dlsym.c │ │ │ ├── arm/ │ │ │ │ └── find_exidx.c │ │ │ ├── dl_iterate_phdr.c │ │ │ ├── dladdr.c │ │ │ ├── dlclose.c │ │ │ ├── dlerror.c │ │ │ ├── dlinfo.c │ │ │ ├── dlopen.c │ │ │ ├── dlsym.c │ │ │ └── tlsdesc.c │ │ ├── legacy/ │ │ │ ├── cuserid.c │ │ │ ├── daemon.c │ │ │ ├── err.c │ │ │ ├── euidaccess.c │ │ │ ├── ftw.c │ │ │ ├── futimes.c │ │ │ ├── getdtablesize.c │ │ │ ├── getloadavg.c │ │ │ ├── getpagesize.c │ │ │ ├── getpass.c │ │ │ ├── getusershell.c │ │ │ ├── isastream.c │ │ │ ├── lutimes.c │ │ │ ├── ulimit.c │ │ │ ├── utmpx.c │ │ │ └── valloc.c │ │ ├── locale/ │ │ │ ├── __lctrans.c │ │ │ ├── __mo_lookup.c │ │ │ ├── big5.h │ │ │ ├── bind_textdomain_codeset.c │ │ │ ├── c_locale.c │ │ │ ├── catclose.c │ │ │ ├── catgets.c │ │ │ ├── catopen.c │ │ │ ├── codepages.h │ │ │ ├── dcngettext.c │ │ │ ├── duplocale.c │ │ │ ├── freelocale.c │ │ │ ├── gb18030.h │ │ │ ├── hkscs.h │ │ │ ├── iconv.c │ │ │ ├── iconv_close.c │ │ │ ├── jis0208.h │ │ │ ├── ksc.h │ │ │ ├── langinfo.c │ │ │ ├── legacychars.h │ │ │ ├── locale_map.c │ │ │ ├── localeconv.c │ │ │ ├── newlocale.c │ │ │ ├── pleval.c │ │ │ ├── pleval.h │ │ │ ├── revjis.h │ │ │ ├── setlocale.c │ │ │ ├── strcoll.c │ │ │ ├── strfmon.c │ │ │ ├── strxfrm.c │ │ │ ├── textdomain.c │ │ │ ├── uselocale.c │ │ │ ├── wcscoll.c │ │ │ └── wcsxfrm.c │ │ ├── malloc/ │ │ │ ├── calloc.c │ │ │ ├── free.c │ │ │ ├── libc_calloc.c │ │ │ ├── lite_malloc.c │ │ │ ├── mallocng/ │ │ │ │ ├── aligned_alloc.c │ │ │ │ ├── donate.c │ │ │ │ ├── free.c │ │ │ │ ├── glue.h │ │ │ │ ├── malloc.c │ │ │ │ ├── malloc_usable_size.c │ │ │ │ ├── meta.h │ │ │ │ └── realloc.c │ │ │ ├── memalign.c │ │ │ ├── oldmalloc/ │ │ │ │ ├── aligned_alloc.c │ │ │ │ ├── malloc.c │ │ │ │ ├── malloc_impl.h │ │ │ │ └── malloc_usable_size.c │ │ │ ├── posix_memalign.c │ │ │ ├── realloc.c │ │ │ ├── reallocarray.c │ │ │ └── replaced.c │ │ ├── math/ │ │ │ ├── __cos.c │ │ │ ├── __cosdf.c │ │ │ ├── __cosl.c │ │ │ ├── __expo2.c │ │ │ ├── __expo2f.c │ │ │ ├── __fpclassify.c │ │ │ ├── __fpclassifyf.c │ │ │ ├── __fpclassifyl.c │ │ │ ├── __invtrigl.c │ │ │ ├── __invtrigl.h │ │ │ ├── __math_divzero.c │ │ │ ├── __math_divzerof.c │ │ │ ├── __math_invalid.c │ │ │ ├── __math_invalidf.c │ │ │ ├── __math_invalidl.c │ │ │ ├── __math_oflow.c │ │ │ ├── __math_oflowf.c │ │ │ ├── __math_uflow.c │ │ │ ├── __math_uflowf.c │ │ │ ├── __math_xflow.c │ │ │ ├── __math_xflowf.c │ │ │ ├── __polevll.c │ │ │ ├── __rem_pio2.c │ │ │ ├── __rem_pio2_large.c │ │ │ ├── __rem_pio2f.c │ │ │ ├── __rem_pio2l.c │ │ │ ├── __signbit.c │ │ │ ├── __signbitf.c │ │ │ ├── __signbitl.c │ │ │ ├── __sin.c │ │ │ ├── __sindf.c │ │ │ ├── __sinl.c │ │ │ ├── __tan.c │ │ │ ├── __tandf.c │ │ │ ├── __tanl.c │ │ │ ├── aarch64/ │ │ │ │ ├── ceil.c │ │ │ │ ├── ceilf.c │ │ │ │ ├── fabs.c │ │ │ │ ├── fabsf.c │ │ │ │ ├── floor.c │ │ │ │ ├── floorf.c │ │ │ │ ├── fma.c │ │ │ │ ├── fmaf.c │ │ │ │ ├── fmax.c │ │ │ │ ├── fmaxf.c │ │ │ │ ├── fmin.c │ │ │ │ ├── fminf.c │ │ │ │ ├── llrint.c │ │ │ │ ├── llrintf.c │ │ │ │ ├── llround.c │ │ │ │ ├── llroundf.c │ │ │ │ ├── lrint.c │ │ │ │ ├── lrintf.c │ │ │ │ ├── lround.c │ │ │ │ ├── lroundf.c │ │ │ │ ├── nearbyint.c │ │ │ │ ├── nearbyintf.c │ │ │ │ ├── rint.c │ │ │ │ ├── rintf.c │ │ │ │ ├── round.c │ │ │ │ ├── roundf.c │ │ │ │ ├── sqrt.c │ │ │ │ ├── sqrtf.c │ │ │ │ ├── trunc.c │ │ │ │ └── truncf.c │ │ │ ├── acos.c │ │ │ ├── acosf.c │ │ │ ├── acosh.c │ │ │ ├── acoshf.c │ │ │ ├── acoshl.c │ │ │ ├── acosl.c │ │ │ ├── arm/ │ │ │ │ ├── fabs.c │ │ │ │ ├── fabsf.c │ │ │ │ ├── fma.c │ │ │ │ ├── fmaf.c │ │ │ │ ├── sqrt.c │ │ │ │ └── sqrtf.c │ │ │ ├── asin.c │ │ │ ├── asinf.c │ │ │ ├── asinh.c │ │ │ ├── asinhf.c │ │ │ ├── asinhl.c │ │ │ ├── asinl.c │ │ │ ├── atan.c │ │ │ ├── atan2.c │ │ │ ├── atan2f.c │ │ │ ├── atan2l.c │ │ │ ├── atanf.c │ │ │ ├── atanh.c │ │ │ ├── atanhf.c │ │ │ ├── atanhl.c │ │ │ ├── atanl.c │ │ │ ├── cbrt.c │ │ │ ├── cbrtf.c │ │ │ ├── cbrtl.c │ │ │ ├── ceil.c │ │ │ ├── ceilf.c │ │ │ ├── ceill.c │ │ │ ├── copysign.c │ │ │ ├── copysignf.c │ │ │ ├── copysignl.c │ │ │ ├── cos.c │ │ │ ├── cosf.c │ │ │ ├── cosh.c │ │ │ ├── coshf.c │ │ │ ├── coshl.c │ │ │ ├── cosl.c │ │ │ ├── erf.c │ │ │ ├── erff.c │ │ │ ├── erfl.c │ │ │ ├── exp.c │ │ │ ├── exp10.c │ │ │ ├── exp10f.c │ │ │ ├── exp10l.c │ │ │ ├── exp2.c │ │ │ ├── exp2f.c │ │ │ ├── exp2f_data.c │ │ │ ├── exp2f_data.h │ │ │ ├── exp2l.c │ │ │ ├── exp_data.c │ │ │ ├── exp_data.h │ │ │ ├── expf.c │ │ │ ├── expl.c │ │ │ ├── expm1.c │ │ │ ├── expm1f.c │ │ │ ├── expm1l.c │ │ │ ├── fabs.c │ │ │ ├── fabsf.c │ │ │ ├── fabsl.c │ │ │ ├── fdim.c │ │ │ ├── fdimf.c │ │ │ ├── fdiml.c │ │ │ ├── finite.c │ │ │ ├── finitef.c │ │ │ ├── floor.c │ │ │ ├── floorf.c │ │ │ ├── floorl.c │ │ │ ├── fma.c │ │ │ ├── fmaf.c │ │ │ ├── fmal.c │ │ │ ├── fmax.c │ │ │ ├── fmaxf.c │ │ │ ├── fmaxl.c │ │ │ ├── fmin.c │ │ │ ├── fminf.c │ │ │ ├── fminl.c │ │ │ ├── fmod.c │ │ │ ├── fmodf.c │ │ │ ├── fmodl.c │ │ │ ├── frexp.c │ │ │ ├── frexpf.c │ │ │ ├── frexpl.c │ │ │ ├── hypot.c │ │ │ ├── hypotf.c │ │ │ ├── hypotl.c │ │ │ ├── i386/ │ │ │ │ ├── fabs.c │ │ │ │ ├── fabsf.c │ │ │ │ ├── fabsl.c │ │ │ │ ├── fmod.c │ │ │ │ ├── fmodf.c │ │ │ │ ├── fmodl.c │ │ │ │ ├── llrint.c │ │ │ │ ├── llrintf.c │ │ │ │ ├── llrintl.c │ │ │ │ ├── lrint.c │ │ │ │ ├── lrintf.c │ │ │ │ ├── lrintl.c │ │ │ │ ├── remainder.c │ │ │ │ ├── remainderf.c │ │ │ │ ├── remainderl.c │ │ │ │ ├── rint.c │ │ │ │ ├── rintf.c │ │ │ │ ├── rintl.c │ │ │ │ ├── sqrt.c │ │ │ │ ├── sqrtf.c │ │ │ │ └── sqrtl.c │ │ │ ├── ilogb.c │ │ │ ├── ilogbf.c │ │ │ ├── ilogbl.c │ │ │ ├── j0.c │ │ │ ├── j0f.c │ │ │ ├── j1.c │ │ │ ├── j1f.c │ │ │ ├── jn.c │ │ │ ├── jnf.c │ │ │ ├── ldexp.c │ │ │ ├── ldexpf.c │ │ │ ├── ldexpl.c │ │ │ ├── lgamma.c │ │ │ ├── lgamma_r.c │ │ │ ├── lgammaf.c │ │ │ ├── lgammaf_r.c │ │ │ ├── lgammal.c │ │ │ ├── llrint.c │ │ │ ├── llrintf.c │ │ │ ├── llrintl.c │ │ │ ├── llround.c │ │ │ ├── llroundf.c │ │ │ ├── llroundl.c │ │ │ ├── log.c │ │ │ ├── log10.c │ │ │ ├── log10f.c │ │ │ ├── log10l.c │ │ │ ├── log1p.c │ │ │ ├── log1pf.c │ │ │ ├── log1pl.c │ │ │ ├── log2.c │ │ │ ├── log2_data.c │ │ │ ├── log2_data.h │ │ │ ├── log2f.c │ │ │ ├── log2f_data.c │ │ │ ├── log2f_data.h │ │ │ ├── log2l.c │ │ │ ├── log_data.c │ │ │ ├── log_data.h │ │ │ ├── logb.c │ │ │ ├── logbf.c │ │ │ ├── logbl.c │ │ │ ├── logf.c │ │ │ ├── logf_data.c │ │ │ ├── logf_data.h │ │ │ ├── logl.c │ │ │ ├── lrint.c │ │ │ ├── lrintf.c │ │ │ ├── lrintl.c │ │ │ ├── lround.c │ │ │ ├── lroundf.c │ │ │ ├── lroundl.c │ │ │ ├── m68k/ │ │ │ │ └── sqrtl.c │ │ │ ├── mips/ │ │ │ │ ├── fabs.c │ │ │ │ ├── fabsf.c │ │ │ │ ├── sqrt.c │ │ │ │ └── sqrtf.c │ │ │ ├── modf.c │ │ │ ├── modff.c │ │ │ ├── modfl.c │ │ │ ├── nan.c │ │ │ ├── nanf.c │ │ │ ├── nanl.c │ │ │ ├── nearbyint.c │ │ │ ├── nearbyintf.c │ │ │ ├── nearbyintl.c │ │ │ ├── nextafter.c │ │ │ ├── nextafterf.c │ │ │ ├── nextafterl.c │ │ │ ├── nexttoward.c │ │ │ ├── nexttowardf.c │ │ │ ├── nexttowardl.c │ │ │ ├── pow.c │ │ │ ├── pow_data.c │ │ │ ├── pow_data.h │ │ │ ├── powerpc/ │ │ │ │ ├── fabs.c │ │ │ │ ├── fabsf.c │ │ │ │ ├── fma.c │ │ │ │ ├── fmaf.c │ │ │ │ ├── sqrt.c │ │ │ │ └── sqrtf.c │ │ │ ├── powerpc64/ │ │ │ │ ├── ceil.c │ │ │ │ ├── ceilf.c │ │ │ │ ├── fabs.c │ │ │ │ ├── fabsf.c │ │ │ │ ├── floor.c │ │ │ │ ├── floorf.c │ │ │ │ ├── fma.c │ │ │ │ ├── fmaf.c │ │ │ │ ├── fmax.c │ │ │ │ ├── fmaxf.c │ │ │ │ ├── fmin.c │ │ │ │ ├── fminf.c │ │ │ │ ├── lrint.c │ │ │ │ ├── lrintf.c │ │ │ │ ├── lround.c │ │ │ │ ├── lroundf.c │ │ │ │ ├── round.c │ │ │ │ ├── roundf.c │ │ │ │ ├── sqrt.c │ │ │ │ ├── sqrtf.c │ │ │ │ ├── trunc.c │ │ │ │ └── truncf.c │ │ │ ├── powf.c │ │ │ ├── powf_data.c │ │ │ ├── powf_data.h │ │ │ ├── powl.c │ │ │ ├── remainder.c │ │ │ ├── remainderf.c │ │ │ ├── remainderl.c │ │ │ ├── remquo.c │ │ │ ├── remquof.c │ │ │ ├── remquol.c │ │ │ ├── rint.c │ │ │ ├── rintf.c │ │ │ ├── rintl.c │ │ │ ├── riscv64/ │ │ │ │ ├── copysign.c │ │ │ │ ├── copysignf.c │ │ │ │ ├── fabs.c │ │ │ │ ├── fabsf.c │ │ │ │ ├── fma.c │ │ │ │ ├── fmaf.c │ │ │ │ ├── fmax.c │ │ │ │ ├── fmaxf.c │ │ │ │ ├── fmin.c │ │ │ │ ├── fminf.c │ │ │ │ ├── sqrt.c │ │ │ │ └── sqrtf.c │ │ │ ├── round.c │ │ │ ├── roundf.c │ │ │ ├── roundl.c │ │ │ ├── s390x/ │ │ │ │ ├── ceil.c │ │ │ │ ├── ceilf.c │ │ │ │ ├── ceill.c │ │ │ │ ├── fabs.c │ │ │ │ ├── fabsf.c │ │ │ │ ├── fabsl.c │ │ │ │ ├── floor.c │ │ │ │ ├── floorf.c │ │ │ │ ├── floorl.c │ │ │ │ ├── fma.c │ │ │ │ ├── fmaf.c │ │ │ │ ├── nearbyint.c │ │ │ │ ├── nearbyintf.c │ │ │ │ ├── nearbyintl.c │ │ │ │ ├── rint.c │ │ │ │ ├── rintf.c │ │ │ │ ├── rintl.c │ │ │ │ ├── round.c │ │ │ │ ├── roundf.c │ │ │ │ ├── roundl.c │ │ │ │ ├── sqrt.c │ │ │ │ ├── sqrtf.c │ │ │ │ ├── sqrtl.c │ │ │ │ ├── trunc.c │ │ │ │ ├── truncf.c │ │ │ │ └── truncl.c │ │ │ ├── scalb.c │ │ │ ├── scalbf.c │ │ │ ├── scalbln.c │ │ │ ├── scalblnf.c │ │ │ ├── scalblnl.c │ │ │ ├── scalbn.c │ │ │ ├── scalbnf.c │ │ │ ├── scalbnl.c │ │ │ ├── signgam.c │ │ │ ├── significand.c │ │ │ ├── significandf.c │ │ │ ├── sin.c │ │ │ ├── sincos.c │ │ │ ├── sincosf.c │ │ │ ├── sincosl.c │ │ │ ├── sinf.c │ │ │ ├── sinh.c │ │ │ ├── sinhf.c │ │ │ ├── sinhl.c │ │ │ ├── sinl.c │ │ │ ├── sqrt.c │ │ │ ├── sqrt_data.c │ │ │ ├── sqrt_data.h │ │ │ ├── sqrtf.c │ │ │ ├── sqrtl.c │ │ │ ├── tan.c │ │ │ ├── tanf.c │ │ │ ├── tanh.c │ │ │ ├── tanhf.c │ │ │ ├── tanhl.c │ │ │ ├── tanl.c │ │ │ ├── tgamma.c │ │ │ ├── tgammaf.c │ │ │ ├── tgammal.c │ │ │ ├── trunc.c │ │ │ ├── truncf.c │ │ │ ├── truncl.c │ │ │ ├── x32/ │ │ │ │ ├── fma.c │ │ │ │ └── fmaf.c │ │ │ └── x86_64/ │ │ │ ├── fabs.c │ │ │ ├── fabsf.c │ │ │ ├── fabsl.c │ │ │ ├── fma.c │ │ │ ├── fmaf.c │ │ │ ├── fmodl.c │ │ │ ├── llrint.c │ │ │ ├── llrintf.c │ │ │ ├── llrintl.c │ │ │ ├── lrint.c │ │ │ ├── lrintf.c │ │ │ ├── lrintl.c │ │ │ ├── remainderl.c │ │ │ ├── remquol.c │ │ │ ├── rintl.c │ │ │ ├── sqrt.c │ │ │ ├── sqrtf.c │ │ │ └── sqrtl.c │ │ ├── minos/ │ │ │ ├── aarch64/ │ │ │ │ ├── aarch64_kobject.c │ │ │ │ ├── aarch64_svc.S │ │ │ │ └── aarch64_svc.h │ │ │ ├── brk.c │ │ │ ├── device.c │ │ │ ├── grant.c │ │ │ ├── kobject.c │ │ │ ├── map.c │ │ │ ├── poll.c │ │ │ ├── procinfo.c │ │ │ ├── proto.c │ │ │ ├── service.c │ │ │ ├── sys.c │ │ │ ├── thread.c │ │ │ └── yield.c │ │ ├── misc/ │ │ │ ├── a64l.c │ │ │ ├── basename.c │ │ │ ├── dirname.c │ │ │ ├── ffs.c │ │ │ ├── ffsl.c │ │ │ ├── ffsll.c │ │ │ ├── fmtmsg.c │ │ │ ├── forkpty.c │ │ │ ├── get_current_dir_name.c │ │ │ ├── getauxval.c │ │ │ ├── getdomainname.c │ │ │ ├── getentropy.c │ │ │ ├── gethostid.c │ │ │ ├── getopt.c │ │ │ ├── getopt_long.c │ │ │ ├── getsubopt.c │ │ │ ├── initgroups.c │ │ │ ├── issetugid.c │ │ │ ├── lockf.c │ │ │ ├── login_tty.c │ │ │ ├── mntent.c │ │ │ ├── nftw.c │ │ │ ├── openpty.c │ │ │ ├── ptsname.c │ │ │ ├── realpath.c │ │ │ ├── syscall.c │ │ │ ├── syslog.c │ │ │ └── wordexp.c │ │ ├── mman/ │ │ │ ├── madvise.c │ │ │ ├── mmap.c │ │ │ ├── mprotect.c │ │ │ ├── munmap.c │ │ │ └── posix_madvise.c │ │ ├── multibyte/ │ │ │ ├── btowc.c │ │ │ ├── c16rtomb.c │ │ │ ├── c32rtomb.c │ │ │ ├── internal.c │ │ │ ├── internal.h │ │ │ ├── mblen.c │ │ │ ├── mbrlen.c │ │ │ ├── mbrtoc16.c │ │ │ ├── mbrtoc32.c │ │ │ ├── mbrtowc.c │ │ │ ├── mbsinit.c │ │ │ ├── mbsnrtowcs.c │ │ │ ├── mbsrtowcs.c │ │ │ ├── mbstowcs.c │ │ │ ├── mbtowc.c │ │ │ ├── wcrtomb.c │ │ │ ├── wcsnrtombs.c │ │ │ ├── wcsrtombs.c │ │ │ ├── wcstombs.c │ │ │ ├── wctob.c │ │ │ └── wctomb.c │ │ ├── passwd/ │ │ │ ├── fgetgrent.c │ │ │ ├── fgetpwent.c │ │ │ ├── fgetspent.c │ │ │ ├── getgr_a.c │ │ │ ├── getgr_r.c │ │ │ ├── getgrent.c │ │ │ ├── getgrent_a.c │ │ │ ├── getgrouplist.c │ │ │ ├── getpw_a.c │ │ │ ├── getpw_r.c │ │ │ ├── getpwent.c │ │ │ ├── getpwent_a.c │ │ │ ├── getspent.c │ │ │ ├── getspnam.c │ │ │ ├── getspnam_r.c │ │ │ ├── lckpwdf.c │ │ │ ├── nscd.h │ │ │ ├── nscd_query.c │ │ │ ├── putgrent.c │ │ │ ├── putpwent.c │ │ │ ├── putspent.c │ │ │ └── pwf.h │ │ ├── prng/ │ │ │ ├── __rand48_step.c │ │ │ ├── __seed48.c │ │ │ ├── drand48.c │ │ │ ├── lcong48.c │ │ │ ├── lrand48.c │ │ │ ├── mrand48.c │ │ │ ├── rand.c │ │ │ ├── rand48.h │ │ │ ├── rand_r.c │ │ │ ├── random.c │ │ │ ├── seed48.c │ │ │ └── srand48.c │ │ ├── process/ │ │ │ ├── execv.c │ │ │ ├── wait.c │ │ │ ├── waitid.c │ │ │ └── waitpid.c │ │ ├── regex/ │ │ │ ├── fnmatch.c │ │ │ ├── glob.c │ │ │ ├── regcomp.c │ │ │ ├── regerror.c │ │ │ ├── regexec.c │ │ │ ├── tre-mem.c │ │ │ └── tre.h │ │ ├── search/ │ │ │ ├── hsearch.c │ │ │ ├── insque.c │ │ │ ├── lsearch.c │ │ │ ├── tdelete.c │ │ │ ├── tdestroy.c │ │ │ ├── tfind.c │ │ │ ├── tsearch.c │ │ │ ├── tsearch.h │ │ │ └── twalk.c │ │ ├── setjmp/ │ │ │ ├── longjmp.c │ │ │ └── setjmp.c │ │ ├── stdio/ │ │ │ ├── __fclose_ca.c │ │ │ ├── __fdopen.c │ │ │ ├── __fmodeflags.c │ │ │ ├── __fopen_rb_ca.c │ │ │ ├── __lockfile.c │ │ │ ├── __overflow.c │ │ │ ├── __stdio_close.c │ │ │ ├── __stdio_exit.c │ │ │ ├── __stdio_read.c │ │ │ ├── __stdio_seek.c │ │ │ ├── __stdio_write.c │ │ │ ├── __stdout_write.c │ │ │ ├── __toread.c │ │ │ ├── __towrite.c │ │ │ ├── __uflow.c │ │ │ ├── asprintf.c │ │ │ ├── clearerr.c │ │ │ ├── dprintf.c │ │ │ ├── ext.c │ │ │ ├── ext2.c │ │ │ ├── fclose.c │ │ │ ├── feof.c │ │ │ ├── ferror.c │ │ │ ├── fflush.c │ │ │ ├── fgetc.c │ │ │ ├── fgetln.c │ │ │ ├── fgetpos.c │ │ │ ├── fgets.c │ │ │ ├── fgetwc.c │ │ │ ├── fgetws.c │ │ │ ├── fileno.c │ │ │ ├── flockfile.c │ │ │ ├── fmemopen.c │ │ │ ├── fopen.c │ │ │ ├── fopencookie.c │ │ │ ├── fprintf.c │ │ │ ├── fputc.c │ │ │ ├── fputs.c │ │ │ ├── fputwc.c │ │ │ ├── fputws.c │ │ │ ├── fread.c │ │ │ ├── freopen.c │ │ │ ├── fscanf.c │ │ │ ├── fseek.c │ │ │ ├── fsetpos.c │ │ │ ├── ftell.c │ │ │ ├── ftrylockfile.c │ │ │ ├── funlockfile.c │ │ │ ├── fwide.c │ │ │ ├── fwprintf.c │ │ │ ├── fwrite.c │ │ │ ├── fwscanf.c │ │ │ ├── getc.c │ │ │ ├── getc.h │ │ │ ├── getc_unlocked.c │ │ │ ├── getchar.c │ │ │ ├── getchar_unlocked.c │ │ │ ├── getdelim.c │ │ │ ├── getline.c │ │ │ ├── gets.c │ │ │ ├── getw.c │ │ │ ├── getwc.c │ │ │ ├── getwchar.c │ │ │ ├── ofl.c │ │ │ ├── ofl_add.c │ │ │ ├── open_memstream.c │ │ │ ├── open_wmemstream.c │ │ │ ├── perror.c │ │ │ ├── popen.c │ │ │ ├── printf.c │ │ │ ├── putc.c │ │ │ ├── putc.h │ │ │ ├── putc_unlocked.c │ │ │ ├── putchar.c │ │ │ ├── putchar_unlocked.c │ │ │ ├── puts.c │ │ │ ├── putw.c │ │ │ ├── putwc.c │ │ │ ├── putwchar.c │ │ │ ├── rewind.c │ │ │ ├── scanf.c │ │ │ ├── setbuf.c │ │ │ ├── setbuffer.c │ │ │ ├── setlinebuf.c │ │ │ ├── setvbuf.c │ │ │ ├── snprintf.c │ │ │ ├── sprintf.c │ │ │ ├── sscanf.c │ │ │ ├── stderr.c │ │ │ ├── stdin.c │ │ │ ├── stdout.c │ │ │ ├── swprintf.c │ │ │ ├── swscanf.c │ │ │ ├── ungetc.c │ │ │ ├── ungetwc.c │ │ │ ├── vasprintf.c │ │ │ ├── vdprintf.c │ │ │ ├── vfprintf.c │ │ │ ├── vfscanf.c │ │ │ ├── vfwprintf.c │ │ │ ├── vfwscanf.c │ │ │ ├── vprintf.c │ │ │ ├── vscanf.c │ │ │ ├── vsnprintf.c │ │ │ ├── vsprintf.c │ │ │ ├── vsscanf.c │ │ │ ├── vswprintf.c │ │ │ ├── vswscanf.c │ │ │ ├── vwprintf.c │ │ │ ├── vwscanf.c │ │ │ ├── wprintf.c │ │ │ └── wscanf.c │ │ ├── stdlib/ │ │ │ ├── abs.c │ │ │ ├── atof.c │ │ │ ├── atoi.c │ │ │ ├── atol.c │ │ │ ├── atoll.c │ │ │ ├── bsearch.c │ │ │ ├── div.c │ │ │ ├── ecvt.c │ │ │ ├── fcvt.c │ │ │ ├── gcvt.c │ │ │ ├── imaxabs.c │ │ │ ├── imaxdiv.c │ │ │ ├── labs.c │ │ │ ├── ldiv.c │ │ │ ├── llabs.c │ │ │ ├── lldiv.c │ │ │ ├── qsort.c │ │ │ ├── strtod.c │ │ │ ├── strtol.c │ │ │ ├── wcstod.c │ │ │ └── wcstol.c │ │ ├── string/ │ │ │ ├── bcmp.c │ │ │ ├── bcopy.c │ │ │ ├── bzero.c │ │ │ ├── explicit_bzero.c │ │ │ ├── index.c │ │ │ ├── memccpy.c │ │ │ ├── memchr.c │ │ │ ├── memcmp.c │ │ │ ├── memcpy.c │ │ │ ├── memmem.c │ │ │ ├── memmove.c │ │ │ ├── mempcpy.c │ │ │ ├── memrchr.c │ │ │ ├── memset.c │ │ │ ├── rindex.c │ │ │ ├── stpcpy.c │ │ │ ├── stpncpy.c │ │ │ ├── strcasecmp.c │ │ │ ├── strcasestr.c │ │ │ ├── strcat.c │ │ │ ├── strchr.c │ │ │ ├── strchrnul.c │ │ │ ├── strcmp.c │ │ │ ├── strcpy.c │ │ │ ├── strcspn.c │ │ │ ├── strdup.c │ │ │ ├── strerror_r.c │ │ │ ├── strlcat.c │ │ │ ├── strlcpy.c │ │ │ ├── strlen.c │ │ │ ├── strncasecmp.c │ │ │ ├── strncat.c │ │ │ ├── strncmp.c │ │ │ ├── strncpy.c │ │ │ ├── strndup.c │ │ │ ├── strnlen.c │ │ │ ├── strpbrk.c │ │ │ ├── strrchr.c │ │ │ ├── strsep.c │ │ │ ├── strsignal.c │ │ │ ├── strspn.c │ │ │ ├── strstr.c │ │ │ ├── strtok.c │ │ │ ├── strtok_r.c │ │ │ ├── strverscmp.c │ │ │ ├── swab.c │ │ │ ├── wcpcpy.c │ │ │ ├── wcpncpy.c │ │ │ ├── wcscasecmp.c │ │ │ ├── wcscasecmp_l.c │ │ │ ├── wcscat.c │ │ │ ├── wcschr.c │ │ │ ├── wcscmp.c │ │ │ ├── wcscpy.c │ │ │ ├── wcscspn.c │ │ │ ├── wcsdup.c │ │ │ ├── wcslen.c │ │ │ ├── wcsncasecmp.c │ │ │ ├── wcsncasecmp_l.c │ │ │ ├── wcsncat.c │ │ │ ├── wcsncmp.c │ │ │ ├── wcsncpy.c │ │ │ ├── wcsnlen.c │ │ │ ├── wcspbrk.c │ │ │ ├── wcsrchr.c │ │ │ ├── wcsspn.c │ │ │ ├── wcsstr.c │ │ │ ├── wcstok.c │ │ │ ├── wcswcs.c │ │ │ ├── wmemchr.c │ │ │ ├── wmemcmp.c │ │ │ ├── wmemcpy.c │ │ │ ├── wmemmove.c │ │ │ └── wmemset.c │ │ ├── temp/ │ │ │ ├── __randname.c │ │ │ ├── mkdtemp.c │ │ │ ├── mkostemp.c │ │ │ ├── mkostemps.c │ │ │ ├── mkstemp.c │ │ │ ├── mkstemps.c │ │ │ └── mktemp.c │ │ ├── termios/ │ │ │ ├── cfgetospeed.c │ │ │ ├── cfmakeraw.c │ │ │ ├── cfsetospeed.c │ │ │ ├── tcdrain.c │ │ │ ├── tcflow.c │ │ │ ├── tcflush.c │ │ │ ├── tcgetattr.c │ │ │ ├── tcgetsid.c │ │ │ ├── tcgetwinsize.c │ │ │ ├── tcsendbreak.c │ │ │ ├── tcsetattr.c │ │ │ └── tcsetwinsize.c │ │ ├── thread/ │ │ │ ├── __lock.c │ │ │ ├── __set_thread_area.c │ │ │ ├── __syscall_cp.c │ │ │ ├── __timedwait.c │ │ │ ├── __tls_get_addr.c │ │ │ ├── __unmapself.c │ │ │ ├── __wait.c │ │ │ ├── aarch64/ │ │ │ │ ├── __set_thread_area.S │ │ │ │ ├── __unmapself.S │ │ │ │ ├── clone.S │ │ │ │ └── syscall_cp.S │ │ │ ├── arm/ │ │ │ │ └── __set_thread_area.c │ │ │ ├── call_once.c │ │ │ ├── clone.c │ │ │ ├── cnd_broadcast.c │ │ │ ├── cnd_destroy.c │ │ │ ├── cnd_init.c │ │ │ ├── cnd_signal.c │ │ │ ├── cnd_timedwait.c │ │ │ ├── cnd_wait.c │ │ │ ├── default_attr.c │ │ │ ├── lock_ptc.c │ │ │ ├── mtx_destroy.c │ │ │ ├── mtx_init.c │ │ │ ├── mtx_lock.c │ │ │ ├── mtx_timedlock.c │ │ │ ├── mtx_trylock.c │ │ │ ├── mtx_unlock.c │ │ │ ├── pthread_atfork.c │ │ │ ├── pthread_attr_destroy.c │ │ │ ├── pthread_attr_get.c │ │ │ ├── pthread_attr_init.c │ │ │ ├── pthread_attr_setdetachstate.c │ │ │ ├── pthread_attr_setguardsize.c │ │ │ ├── pthread_attr_setinheritsched.c │ │ │ ├── pthread_attr_setschedparam.c │ │ │ ├── pthread_attr_setschedpolicy.c │ │ │ ├── pthread_attr_setscope.c │ │ │ ├── pthread_attr_setstack.c │ │ │ ├── pthread_attr_setstacksize.c │ │ │ ├── pthread_barrier_destroy.c │ │ │ ├── pthread_barrier_init.c │ │ │ ├── pthread_barrier_wait.c │ │ │ ├── pthread_barrierattr_destroy.c │ │ │ ├── pthread_barrierattr_init.c │ │ │ ├── pthread_barrierattr_setpshared.c │ │ │ ├── pthread_cancel.c │ │ │ ├── pthread_cleanup_push.c │ │ │ ├── pthread_cond_broadcast.c │ │ │ ├── pthread_cond_destroy.c │ │ │ ├── pthread_cond_init.c │ │ │ ├── pthread_cond_signal.c │ │ │ ├── pthread_cond_timedwait.c │ │ │ ├── pthread_cond_wait.c │ │ │ ├── pthread_condattr_destroy.c │ │ │ ├── pthread_condattr_init.c │ │ │ ├── pthread_condattr_setclock.c │ │ │ ├── pthread_condattr_setpshared.c │ │ │ ├── pthread_create.c │ │ │ ├── pthread_detach.c │ │ │ ├── pthread_equal.c │ │ │ ├── pthread_getattr_np.c │ │ │ ├── pthread_getconcurrency.c │ │ │ ├── pthread_getcpuclockid.c │ │ │ ├── pthread_getschedparam.c │ │ │ ├── pthread_getspecific.c │ │ │ ├── pthread_join.c │ │ │ ├── pthread_key_create.c │ │ │ ├── pthread_kill.c │ │ │ ├── pthread_mutex_consistent.c │ │ │ ├── pthread_mutex_destroy.c │ │ │ ├── pthread_mutex_getprioceiling.c │ │ │ ├── pthread_mutex_init.c │ │ │ ├── pthread_mutex_lock.c │ │ │ ├── pthread_mutex_setprioceiling.c │ │ │ ├── pthread_mutex_timedlock.c │ │ │ ├── pthread_mutex_trylock.c │ │ │ ├── pthread_mutex_unlock.c │ │ │ ├── pthread_mutexattr_destroy.c │ │ │ ├── pthread_mutexattr_init.c │ │ │ ├── pthread_mutexattr_setprotocol.c │ │ │ ├── pthread_mutexattr_setpshared.c │ │ │ ├── pthread_mutexattr_setrobust.c │ │ │ ├── pthread_mutexattr_settype.c │ │ │ ├── pthread_once.c │ │ │ ├── pthread_rwlock_destroy.c │ │ │ ├── pthread_rwlock_init.c │ │ │ ├── pthread_rwlock_rdlock.c │ │ │ ├── pthread_rwlock_timedrdlock.c │ │ │ ├── pthread_rwlock_timedwrlock.c │ │ │ ├── pthread_rwlock_tryrdlock.c │ │ │ ├── pthread_rwlock_trywrlock.c │ │ │ ├── pthread_rwlock_unlock.c │ │ │ ├── pthread_rwlock_wrlock.c │ │ │ ├── pthread_rwlockattr_destroy.c │ │ │ ├── pthread_rwlockattr_init.c │ │ │ ├── pthread_rwlockattr_setpshared.c │ │ │ ├── pthread_self.c │ │ │ ├── pthread_setattr_default_np.c │ │ │ ├── pthread_setcancelstate.c │ │ │ ├── pthread_setcanceltype.c │ │ │ ├── pthread_setconcurrency.c │ │ │ ├── pthread_setname_np.c │ │ │ ├── pthread_setschedparam.c │ │ │ ├── pthread_setschedprio.c │ │ │ ├── pthread_setspecific.c │ │ │ ├── pthread_sigmask.c │ │ │ ├── pthread_spin_destroy.c │ │ │ ├── pthread_spin_init.c │ │ │ ├── pthread_spin_lock.c │ │ │ ├── pthread_spin_trylock.c │ │ │ ├── pthread_spin_unlock.c │ │ │ ├── pthread_testcancel.c │ │ │ ├── sem_destroy.c │ │ │ ├── sem_getvalue.c │ │ │ ├── sem_init.c │ │ │ ├── sem_open.c │ │ │ ├── sem_post.c │ │ │ ├── sem_timedwait.c │ │ │ ├── sem_trywait.c │ │ │ ├── sem_unlink.c │ │ │ ├── sem_wait.c │ │ │ ├── sh/ │ │ │ │ ├── __set_thread_area.c │ │ │ │ └── __unmapself.c │ │ │ ├── synccall.c │ │ │ ├── syscall_cp.c │ │ │ ├── thrd_create.c │ │ │ ├── thrd_exit.c │ │ │ ├── thrd_join.c │ │ │ ├── thrd_sleep.c │ │ │ ├── thrd_yield.c │ │ │ ├── tls.c │ │ │ ├── tss_create.c │ │ │ ├── tss_delete.c │ │ │ ├── tss_set.c │ │ │ └── vmlock.c │ │ ├── time/ │ │ │ ├── __month_to_secs.c │ │ │ ├── __secs_to_tm.c │ │ │ ├── __tm_to_secs.c │ │ │ ├── __year_to_secs.c │ │ │ ├── asctime.c │ │ │ ├── asctime_r.c │ │ │ ├── clock.c │ │ │ ├── clock_getres.c │ │ │ ├── clock_gettime.c │ │ │ ├── clock_nanosleep.c │ │ │ ├── clock_settime.c │ │ │ ├── ctime.c │ │ │ ├── ctime_r.c │ │ │ ├── difftime.c │ │ │ ├── ftime.c │ │ │ ├── getdate.c │ │ │ ├── gettimeofday.c │ │ │ ├── gmtime.c │ │ │ ├── gmtime_r.c │ │ │ ├── localtime.c │ │ │ ├── localtime_r.c │ │ │ ├── mktime.c │ │ │ ├── nanosleep.c │ │ │ ├── strptime.c │ │ │ ├── time.c │ │ │ ├── time_impl.h │ │ │ ├── timegm.c │ │ │ ├── times.c │ │ │ ├── timespec_get.c │ │ │ ├── utime.c │ │ │ └── wcsftime.c │ │ └── unistd/ │ │ ├── aarch64/ │ │ │ └── __unistd.c │ │ ├── access.c │ │ ├── chdir.c │ │ ├── close.c │ │ ├── getpid.c │ │ ├── gettid.c │ │ ├── read.c │ │ └── write.c │ ├── stat/ │ │ └── stat.c │ └── tools/ │ ├── add-cfi.common.awk │ ├── add-cfi.i386.awk │ ├── add-cfi.x86_64.awk │ ├── install.sh │ ├── ld.musl-clang.in │ ├── mkalltypes.sed │ ├── musl-clang.in │ ├── musl-gcc.specs.sh │ └── version.sh ├── user.libs/ │ ├── libfdt/ │ │ ├── Makefile │ │ ├── include/ │ │ │ └── libfdt/ │ │ │ ├── fdt.h │ │ │ ├── libfdt.h │ │ │ └── libfdt_env.h │ │ └── src/ │ │ ├── fdt.c │ │ ├── fdt_addresses.c │ │ ├── fdt_empty_tree.c │ │ ├── fdt_overlay.c │ │ ├── fdt_ro.c │ │ ├── fdt_rw.c │ │ ├── fdt_strerror.c │ │ ├── fdt_sw.c │ │ ├── fdt_wip.c │ │ └── libfdt_internal.h │ ├── liblwext4/ │ │ ├── Makefile │ │ ├── ext4_mem.c │ │ ├── ext4_server.c │ │ ├── include/ │ │ │ └── lwext4/ │ │ │ ├── ext4.h │ │ │ ├── ext4_balloc.h │ │ │ ├── ext4_bcache.h │ │ │ ├── ext4_bitmap.h │ │ │ ├── ext4_blkdev.h │ │ │ ├── ext4_block_group.h │ │ │ ├── ext4_blockdev.h │ │ │ ├── ext4_config.h │ │ │ ├── ext4_crc32.h │ │ │ ├── ext4_debug.h │ │ │ ├── ext4_dir.h │ │ │ ├── ext4_dir_idx.h │ │ │ ├── ext4_errno.h │ │ │ ├── ext4_extent.h │ │ │ ├── ext4_fs.h │ │ │ ├── ext4_hash.h │ │ │ ├── ext4_ialloc.h │ │ │ ├── ext4_inode.h │ │ │ ├── ext4_journal.h │ │ │ ├── ext4_mbr.h │ │ │ ├── ext4_misc.h │ │ │ ├── ext4_mkfs.h │ │ │ ├── ext4_oflags.h │ │ │ ├── ext4_super.h │ │ │ ├── ext4_trans.h │ │ │ ├── ext4_types.h │ │ │ ├── ext4_xattr.h │ │ │ └── misc/ │ │ │ ├── queue.h │ │ │ └── tree.h │ │ └── src/ │ │ ├── ext4.c │ │ ├── ext4_balloc.c │ │ ├── ext4_bcache.c │ │ ├── ext4_bitmap.c │ │ ├── ext4_block_group.c │ │ ├── ext4_blockdev.c │ │ ├── ext4_crc32.c │ │ ├── ext4_debug.c │ │ ├── ext4_dir.c │ │ ├── ext4_dir_idx.c │ │ ├── ext4_extent.c │ │ ├── ext4_fs.c │ │ ├── ext4_hash.c │ │ ├── ext4_ialloc.c │ │ ├── ext4_inode.c │ │ ├── ext4_journal.c │ │ ├── ext4_mbr.c │ │ ├── ext4_mkfs.c │ │ ├── ext4_super.c │ │ ├── ext4_trans.c │ │ └── ext4_xattr.c │ └── libmisc/ │ ├── Makefile │ ├── include/ │ │ └── misc.h │ └── misc.c └── user.sbin/ ├── chiyou/ │ ├── Makefile │ ├── chiyou.c │ ├── of.c │ └── of.h ├── fuxi/ │ ├── Makefile │ └── fuxi.c ├── nvwa/ │ ├── Makefile │ ├── elf.h │ ├── elf_std.c │ └── nvwa.c └── pangu/ ├── .gitignore ├── Makefile ├── include/ │ └── pangu/ │ ├── bootarg.h │ ├── elf.h │ ├── kmalloc.h │ ├── mm.h │ ├── proc.h │ └── ramdisk.h └── src/ ├── bootarg.c ├── elf_ramdisk.c ├── kmalloc.c ├── mm.c ├── of.c ├── pangu.c ├── process.c ├── procinfo.c └── ramdisk.c ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ out/ tags cscope.in.out cscope.out cscope.po.out linkmap.txt include/config/config.h ramdisk.sh ramdisk2.sh fat32.sh *.srv *.drv *.app .* *.o *.o.* *.a *.s *.ko *.so *.so.dbg *.mod.c *.i *.lst *.symtypes *.order *.elf *.bin !user.bin/ *.tar *.gz *.bz2 *.lzma *.xz *.d *.lz4 *.lzo *.patch *.gcno modules.builtin Module.symvers *.dwo *.su # # Top-level generic files # /tags /TAGS /linux /vmlinux /vmlinux.32 /vmlinux-gdb.py /vmlinuz /System.map /Module.markers # # Debian directory (make deb-pkg) # /debian/ # # tar directory (make tar*-pkg) # /tar-install/ # # git files that we don't want to ignore even if they are dot-files # !.gitignore !.mailmap # # Generated include files # include/config/ include/generated arch/*/include/generated # stgit generated dirs patches-* # quilt's files patches series # cscope files cscope.* ncscope.* # gnu global files GPATH GRTAGS GSYMS GTAGS # id-utils files ID *.orig *~ \#*# # # Leavings from module signing # extra_certificates signing_key.pem signing_key.priv signing_key.x509 x509.genkey # Kconfig presets all.config # Kdevelop4 *.kdev4 *.lds allsymbols.S tmp.minos.symbols *.dtb *.py[co] tools/Kconfiglib/build/ *.egg-info/ tools/Kconfiglib/dist/ tools/fdt_parse/parse_dtb asm-offset.h ================================================ FILE: Makefile ================================================ PHONY := _all _all: ifeq ($(DEBUG),1) BUILD_DEBUG = 1 else BUILD_DEBUG = 0 endif ifeq ($(VERBOSE),1) Q = else Q = @ endif projtree := $(shell pwd) srctree := . src := $(srctree) VPATH := $(srctree) export BUILD_DEBUG VERBOSE srctree projtree ARCH ?= aarch64 CROSS_COMPILE ?= aarch64-linux-gnu- PLATFORM ?= fvp TARGET_ARCH = $(ARCH) TARGET_CROSS_COMPILE = $(CROSS_COMPILE) TARGET_PLATFORM = $(PLATFORM) export TARGET_ARCH TARGET_CROSS_COMPILE TARGET_PLATFORM # Make variables (CC, etc...) TARGET_AS = $(TARGET_CROSS_COMPILE)as TARGET_LD = $(TARGET_CROSS_COMPILE)ld TARGET_CC = $(TARGET_CROSS_COMPILE)gcc TARGET_APP_CC = $(projtree)/out/bin/musl-gcc TARGET_CPP = $(TARGET_CC) -E TARGET_AR = $(TARGET_CROSS_COMPILE)ar TARGET_NM = $(TARGET_CROSS_COMPILE)nm TARGET_STRIP = $(TARGET_CROSS_COMPILE)strip TARGET_OBJCOPY = $(TARGET_CROSS_COMPILE)objcopy TARGET_OBJDUMP = $(TARGET_CROSS_COMPILE)objdump TARGET_INSTALL = $(projtree)/tools/install.sh TARGET_INCLUDE_DIR = $(projtree)/out/include TARGET_LIBS_DIR = $(projtree)/out/lib TARGET_OUT_DIR = $(projtree)/out UAPI_INCLUDE_DIR = $(projtree)/generic/include/ HOST_LEX = flex HOST_YACC = bison HOST_AWK = awk HOST_PERL = perl HOST_PYTHON = python HOST_PYTHON2 = python2 HOST_PYTHON3 = python3 HOST_CHECK = sparse HOST_CC = gcc HOST_AS = as HOST_LD = ld HOST_CC = gcc HOST_CPP = gcc -E HOST_AR = ar HOST_NM = nm HOST_STRIP = strip HOST_OBJCOPY = objcopy HOST_OBJDUMP = objdump MAKE = make MFLAGS := --no-print-directory export TARGET_AS TARGET_LD TARGET_CC TARGET_APP_CC TARGET_CPP TARGET_AR TARGET_NM TARGET_STRIP TARGET_OBJCOPY TARGET_OBJDUMP MAKE TARGET_INCLUDE_DIR TARGET_LIBS_DIR TARGET_INSTALL TARGET_OUT_DIR UAPI_INCLUDE_DIR export HOST_LEX HOST_YACC HOST_AWK HOST_PERL HOST_PYTHON HOST_PYTHON2 HOST_PYTHON3 HOST_CHECK HOST_CC HOST_AS HOST_LD HOST_CC HOST_APP_CC HOST_CPP HOST_AR HOST_NM HOST_STRIP HOST_OBJCOPY HOST_OBJDUMP LIB_DIRS := user.libs APP_DIRS := user.sbin user.bin user.driver # get all the libs folder under LIB_DIRS LIB_SUB_DIRS = $(foreach dir, $(LIB_DIRS), $(shell find $(dir) -maxdepth 1 -type d)) LIB_TARGETS = $(filter-out $(LIB_DIRS),$(LIB_SUB_DIRS)) # get the all application folders under APP_DIRS APP_SUB_DIRS = $(foreach dir, $(APP_DIRS), $(shell find $(dir) -maxdepth 1 -type d)) APP_TARGETS = $(filter-out $(APP_DIRS),$(APP_SUB_DIRS)) PHONY += all _all: all PHONY += libc libs apps kernel all: apps kernel libc libs apps kernel: objdirs apps: libs $(Q) set -e; \ for i in $(APP_TARGETS); do \ if [ -f $$i/Makefile ]; then \ echo "\n\033[32m ---> Compiling App $$i ... \033[0m \n"; \ $(MAKE) $(MFLAGS) -C $$i ; \ $(MAKE) $(MFLAGS) -C $$i install; \ fi \ done libs: libc $(Q) set -e; \ for i in $(LIB_TARGETS); do \ if [ -f $$i/Makefile ]; then \ echo "\n\033[32m ---> Compiling Lib $$i ... \033[0m \n"; \ $(MAKE) $(MFLAGS) -C $$i ; \ $(MAKE) $(MFLAGS) -C $$i install; \ fi \ done libc: $(Q) echo "\n\033[32m---> Build LIBC ... \033[0m \n" $(Q) $(MAKE) $(MFLAGS) -C user.libc -j 16 $(Q) $(MAKE) $(MFLAGS) -C user.libc install kernel: $(Q) echo "\n\033[32m ---> Build Kernel ... \033[0m \n" $(Q) $(MAKE) $(MFLAGS) -C kernel $(Q) $(MAKE) $(MFLAGS) -C kernel dtbs $(Q) $(MAKE) $(MFLAGS) -C kernel install clean-kernel: $(Q) echo "\n\033[32m ---> Clean Kernel ... \033[0m \n" $(Q) $(MAKE) $(MFLAGS) -C kernel clean objdirs: $(Q) mkdir -p $(srctree)/out $(Q) mkdir -p $(srctree)/out/include $(Q) mkdir -p $(srctree)/out/lib $(Q) mkdir -p $(srctree)/out/ramdisk $(Q) mkdir -p $(srctree)/out/rootfs/bin $(Q) mkdir -p $(srctree)/out/rootfs/sbin $(Q) mkdir -p $(srctree)/out/rootfs/driver $(Q) mkdir -p $(srctree)/out/rootfs/etc PHONY += images ramdisk rootfs prepare clean clean-libs clean-apps clean-libs: $(Q)set -e; \ for i in $(LIB_TARGETS); do \ if [ -f $$i/Makefile ]; then \ echo "\033[32m Clean $$i \033[0m"; \ $(MAKE) $(MFLAGS) -C $$i clean; \ fi \ done clean-apps: $(Q)set -e; \ for i in $(APP_TARGETS); do \ if [ -f $$i/Makefile ]; then \ echo "\033[32m Clean $$i \033[0m"; \ $(MAKE) $(MFLAGS) -C $$i clean; \ fi \ done clean: clean-libs clean-apps $(Q) echo "\033[32m Clean libc \033[0m" $(Q) $(MAKE) $(MFLAGS) -C user.libc clean $(Q) echo "\033[32m Clean kernel \033[0m" $(Q) $(MAKE) $(MFLAGS) -C kernel clean $(Q) rm -rf out $(Q) echo "\033[32m Clean done ... \033[0m" images: ramdisk rootfs kernel ramdisk: apps kernel $(Q) echo "\n\033[32m ---> Packing Ramdisk image ... \033[0m \n" $(Q) mkrmd -d out/ramdisk.bin out/ramdisk rootfs: apps $(Q) echo "\n\033[32m ---> Packing Rootfs image ... \033[0m \n" $(Q) mkdir -p /tmp/minos-mnt $(Q) dd if=/dev/zero of=/tmp/rootfs.img bs=1M count=64 $(Q) mkfs.vfat -n "ROOTFS" -F 32 /tmp/rootfs.img $(Q) sudo mount /tmp/rootfs.img /tmp/minos-mnt $(Q) sudo cp -r out/rootfs/* /tmp/minos-mnt $(Q) sudo umount /tmp/minos-mnt $(Q) cp -f /tmp/rootfs.img out/rootfs.img $(Q) rm -rf /tmp/rootfs.img /tmp/minos-mnt prepare: objdirs $(Q) cd user.libc; ./build.sh $(TARGET_OUT_DIR) $(TARGET_ARCH) $(TARGET_CROSS_COMPILE) $(Q) cd kernel; make $(TARGET_PLATFORM)_defconfig cp -f kernel/include/uapi/* user.libc/include/minos/ bin/% sbin/% driver/% libs/%: $(Q)set -e; \ if [ -f user.$@/Makefile ]; then \ $(MAKE) $(MFLAGS) -C user.$@; \ $(MAKE) $(MFLAGS) -C user.$@ install; \ else \ echo "Target user.$@ not found"; \ fi .PHONY: $(PHONY) ================================================ FILE: README.md ================================================ # Minos2 **Minos2 is a micro-kernel OS.** - [x] Multi-process - [x] SMP - [x] Multi-thread - [x] Virtual memory management - [x] Libc (based on musl-libc) - [x] IPC - [x] VFS - [x] Ext4 (based on lwext4) - [x] Virtio-blk driver - [x] Qemu - [x] ARM FVP - [ ] Virtualization ## Build Minos2 Below command tested on Ubuntu-18.04. 1. Create a working directory ```bash # mkdir ~/minos2-workspace # cd ~/minos2-workspace ``` 2. Install AARCH64 GCC cross compilation tool (Other GCC version is also work fine) ```bash # wget https://releases.linaro.org/components/toolchain/binaries/7.2-2017.11/aarch64-linux-gnu/gcc-linaro-7.2.1-2017.11-x86_64_aarch64-linux-gnu.tar.xz # tar xjf gcc-linaro-7.2.1-2017.11-x86_64_aarch64-linux-gnu.tar.xz # sudo mv gcc-linaro-7.2.1-2017.11-x86_64_aarch64-linux-gnu /opt # echo "export PATH=/opt/gcc-linaro-7.2.1-2017.11-x86_64_aarch64-linux-gnu/bin:$PATH" >> ~/.bashrc # source ~/.bashrc ``` 3. Install device-tree tool ```bash # sudo apt-get install device-tree-compiler ``` 4. Download minos2 source code ```bash # git clone https://github.com/minosproject/minos2.git ``` 5. Compile minos2 ```bash # make PLATFORM=xxx prepare (platform can be fvp or qemu_arm64) # make ramdisk (build kernel, libc, system service, application, ramdisk in out/ directory) ``` ## Download Virtio-blk image Minos2 support Qemu and FVP now, and both use virtio-blk disk with Ext4 filesystem as rootfs, this image can be create by qemu-img tool. If you do not want to create it by self, you can download the example one here. ``` virtio-sd.img 链接: https://pan.baidu.com/s/1hMaQT20s7n8HNEZ-BqG7XQ 提取码: 9wyh ``` ## Run Minos2 on Qemu 1. Install qemu-system-aarch64 ```bash # apt install qemu-system-arm ``` 2. Download and compile u-boot ``` # git clone https://github.com/u-boot/u-boot.git ``` Before compile the U-boot, please apply below patch to support boot minos2. ```c diff --git a/common/image-fdt.c b/common/image-fdt.c index eb552ca207..987817546d 100644 --- a/common/image-fdt.c +++ b/common/image-fdt.c @@ -175,6 +175,8 @@ int boot_relocate_fdt(struct lmb *lmb, char **of_flat_tree, ulong *of_size) if (fdt_high) { void *desired_addr = (void *)simple_strtoul(fdt_high, NULL, 16); + desired_addr = (void *)-1; + if (((ulong) desired_addr) == ~0UL) { /* All ones means use fdt in place */ of_start = fdt_blob; ``` Build u-boot ```bash # make qemu_arm64_defconfig # make -j16 CROSS_COMPILE=aarch64-linux-gnu- ``` 3. Build Minos ```bash # make PLATFORM=qemu_arm64 prepare # make ramdisk ``` 4. Copy files to the virtio image (use the image which from the pan.baidu.com as an example) Mount the virtio image, please refer to https://unix.stackexchange.com/questions/82314/how-to-find-the-type-of-an-img-file-and-mount-it. There are two partitions in the virtio-sd.img, one is fat16 and other is ext4, please put below file in fat16 partitions. ``` out/kernel.bin out/ramdisk.bin out/qemu-arm64.dtb ``` and put below file in ext4 partiotion ``` # mkdir bin (create a directory named bin first) # cp out/rootfs/bin/ps.app out/rootfs/bin/shell.app to $(virtio_ext_partition)/bin ``` 5. Run minos2 on Qemu ```bash # qemu-system-aarch64 -nographic -bios u-boot.bin qemu-system-aarch64 -nographic -bios u-boot.bin -cpu cortex-a53 -machine type=virt -smp 4 -m 2G -machine virtualization=true -drive if=none,file=sd.img,format=raw,id=hd0 -device virtio-blk-device,drive=hd0 -device virtio-net-device,netdev=net0 -netdev user,id=net0,hostfwd=tcp:127.0.0.1:5555-:22 After qemu boot, use below command to boot minos2 # fatload virtio 0:1 0x40000000 kernel.bin;fatload virtio 0:1 0x44000000 ramdisk.bin;fatload virtio 0:1 0x43e00000 qemu-arm64.dtb;booti 0x40000000 - 0x43e00000 ``` Below is the boot log for Qemu ``` minle@minle-Z840:~/work/github/u-boot$ qemu-system-aarch64 -nographic -machine virt -bios u-boot.bin -cpu cortex-a57 -smp 4 -m 2G -drive if=none,file=/home/minle/work/minos-next/fvp_debug/sd.img,format=raw,id=hd0 \ -device virtio-blk-device,drive=hd0 U-Boot 2019.07-rc4-00358-g1f83431f00-dirty (Dec 24 2021 - 15:20:30 +0800) DRAM: 2 GiB (virtio_mmio@a003e00): device (2) vendor (554d4551) version (1) Flash: 128 MiB *** Warning - bad CRC, using default environment In: pl011@9000000 Out: pl011@9000000 Err: pl011@9000000 Net: No ethernet found. Hit any key to stop autoboot: 0 => fatload virtio 0:1 0x40000000 kernel.bin;fatload virtio 0:1 0x44000000 ramdisk.bin;fatload virtio 0:1 0x43e00000 qemu-arm64.dtb;booti 0x40000000 - 0x43e00000 257656 bytes read in 4 ms (61.4 MiB/s) 695304 bytes read in 1 ms (663.1 MiB/s) 4143 bytes read in 1 ms (4 MiB/s) ## Flattened Device Tree blob at 43e00000 Booting using the fdt blob at 0x43e00000 Using Device Tree in place at 0000000043e00000, end 0000000043e0402e Starting kernel ... [ 0.000000@00 000] NIC Starting Minos AARCH64 [ 0.000000@00 000] NIC DTB address [0x43e00000] [ 0.000000@00 000] NIC Minos v0.3.3 unstable [ 0.000000@00 000] NIC memory node address_cells:2 size_cells:2 [ 0.000000@00 000] NIC DTB - 0x43e00000 ---> 0x2000 [ 0.000000@00 000] NIC MEM: 0x0000000044000000 ---> 0x00000000c0000000 [0x000000007c000000] Normal [ 0.000000@00 000] NIC MEM: 0x0000000040000000 ---> 0x0000000043c00000 [0x0000000003c00000] Kernel [ 0.000000@00 000] NIC MEM: 0x0000000043e02000 ---> 0x0000000044000000 [0x00000000001fe000] Kernel [ 0.000000@00 000] NIC MEM: 0x0000000043e00000 ---> 0x0000000043e02000 [0x0000000000002000] DTB [ 0.000000@00 000] NIC MEM: 0x0000000043c00000 ---> 0x0000000043e00000 [0x0000000000200000] RamDisk [ 0.000000@00 000] NIC kmem [0xffffff804004d000 0xffffff8043c00000] [ 0.000000@00 000] NIC kmem [0xffffff8043e02000 0xffffff8044000000] [ 0.000000@00 000] NIC umem [0x44000000 0xc0000000] [ 0.000000@00 000] NIC slab memory allocator init ... [ 0.000000@00 000] NIC bootargs: bootwait=3 tty=vm0 rootfs=virtio-blk.drv [ 0.000000@00 000] NIC platform : linux,qemu-arm64 [ 0.000000@00 000] NIC current EL is 1 [ 0.000000@00 000] NIC *** gicv2 init *** [ 0.000000@00 000] NIC gicv2 information: gic_dist_addr=0000000008000000 size=0x10000 gic_cpu_addr=0000000008010000 size=0x10000 gic_hyp_addr=0000000000000000 size=0x0 gic_vcpu_addr=0000000000000000 size=0x0 [ 0.000000@00 000] NIC GICv2: 288 lines, 4 cpus (IID 0). [ 0.000000@00 000] WRN not support unmask irq_percpu [ 0.000000@00 000] WRN not support unmask irq_percpu [ 0.000000@00 000] WRN not support unmask irq_percpu [ 0.000000@00 000] WRN not support unmask irq_percpu [ 0.000000@00 000] NIC Register kobject type [5] name [endpoint] [ 0.000000@00 000] NIC Register kobject type [1] name [process] [ 0.000000@00 000] NIC Register kobject type [4] name [pma] [ 0.000000@00 000] NIC Register kobject type [2] name [thread] [ 0.000000@00 000] NIC Register kobject type [9] name [irq] [ 0.000000@00 000] NIC Register kobject type [12] name [poll_hub] [ 0.000000@00 000] NIC Register kobject type [3] name [notify] [ 0.000000@00 000] NIC Register kobject type [13] name [port] [ 0.000000@00 000] NIC sec_phy_timer : 29 [ 0.000000@00 000] NIC nonsec_phy_timer : 30 [ 0.000000@00 000] NIC virt_timer : 27 [ 0.000000@00 000] NIC hypervisor_timer : 26 [ 16.687407@00 000] NIC get timer clock freq from reg 62500 [ 0.000000@00 000] NIC boot ticks is :0x3e2a8f97 [ 0.004660@00 X95] NIC waiting 2 seconds for cpu-1 up [ 0.006231@00 X95] NIC waiting 2 seconds for cpu-2 up [ 0.005760@01 000] NIC cpu-1 is up [ 0.006962@02 000] NIC cpu-2 is up [ 0.007815@00 X95] NIC waiting 2 seconds for cpu-3 up [ 0.008542@03 000] NIC cpu-3 is up [ 0.010513@02 000] NIC current EL is 1 [ 0.010518@01 000] NIC current EL is 1 [ 0.010652@03 000] NIC current EL is 1 [ 0.012897@00 X95] NIC Root service load successfully prepare to run... PanGu service start... pangu: dtb [0x4043e00000 0x4043e02000] pangu: ramdisk [0x4043c00000 0x4043e00000] pangu: vmap [0x1040000000 0x3fc0000000] pangu: heap [0x1000000000 0x1010000000] pangu: sys max proc 4096 pangu: uproc_info 5 pangu: ktask_stat 6 pangu: bootargs: bootwait=3 tty=vm0 rootfs=virtio-blk.drv pangu: handle send to fuxi.srv [handle@4] pangu: Start fuxi.srv and waitting ... FuXi service start... fuxi: fuxi handle 4 pangu: Get response from fuxi.srv service 4132 fuxi: waitting request pangu: handle send to nvwa.srv [handle@4] pangu: Start nvwa.srv and waitting ... NvWa service start... pangu: Get response from nvwa.srv service 4132 nvwa: nvwa waitting elf load request pangu: handle send to chiyou.srv [handle@4,5] pangu: only support map anon mapping for process virtio-blk: virtio-dev: legacy mode virtio-blk: virtio supports unsupported option VIRTIO_BLK_F_SEG_MAX (Maximum number of segments in a request is in seg_max.) virtio-blk: virtio supports unsupported option VIRTIO_BLK_F_GEOMETRY (Disk-style geometry specified in geometry.) virtio-blk: virtio supports unsupported option VIRTIO_BLK_F_BLK_SIZE (Block size of disk is in blk_size.) virtio-blk: virtio supports unsupported option VIRTIO_BLK_F_FLUSH (Cache flush command support.) virtio-blk: virtio supports unsupported option VIRTIO_BLK_F_TOPOLOGY (Device exports information on optimal I/O alignment.) virtio-blk: virtio supports unsupported option VIRTIO_BLK_F_CONFIG_WCE (Device can toggle its cache between writeback and writethrough modes.) virtio-blk: virtio supports unsupported option VIRTIO_F_RING_INDIRECT_DESC (Negotiating this feature indicates that the driver can use descriptors with the VIRTQ_DESC_F_INDIRECT flag set, as described in 2.4.5.3 Indirect Descriptors.) virtio-blk: virtio supports unsupported option VIRTIO_F_RING_EVENT_IDX (This feature enables the used_event and the avail_event fields as described in 2.4.7 and 2.4.8.) virtio-blk: virtio-blk: device supports unknown bits 0x1000080 in bank 0 pangu: only support map anon mapping for process virtio-blk: virtio-blk virtq size 8192 virtio-blk: vd0 capacity : 512MB ext4_mbr: l: 75 [info] ext4_mbr_scan ext4_mbr: l: 96 mbr_part: bootstrap: 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 9f, 27, ext4_mbr: l: 106 mbr_part: 0 ext4_mbr: l: 107 status: 0x80 ext4_mbr: l: 108 type 0xe: ext4_mbr: l: 109 first_lba: 0x3f ext4_mbr: l: 110 sectors: 0x25fc0 ext4_mbr: l: 106 mbr_part: 1 ext4_mbr: l: 107 status: 0x0 ext4_mbr: l: 108 type 0x83: ext4_mbr: l: 109 first_lba: 0x26000 ext4_mbr: l: 110 sectors: 0xda000 ext4_mbr: l: 106 mbr_part: 2 ext4_mbr: l: 107 status: 0x0 ext4_mbr: l: 108 type 0x0: ext4_mbr: l: 109 first_lba: 0x0 ext4_mbr: l: 110 sectors: 0x0 ext4_mbr: l: 106 mbr_part: 3 ext4_mbr: l: 107 status: 0x0 ext4_mbr: l: 108 type 0x0: ext4_mbr: l: 109 first_lba: 0x0 ext4_mbr: l: 110 sectors: 0x0 liblwext4: ext4_mbr_scan: liblwext4: mbr_entry 0: liblwext4: empty/unknown liblwext4: mbr_entry 1: liblwext4: offeset: 0x4c00000, 76MB liblwext4: size: 0x1b400000, 436MB liblwext4: mbr_entry 2: liblwext4: empty/unknown liblwext4: mbr_entry 3: liblwext4: empty/unknown ext4_fs: l: 219 [info] sblock features_incompatible: ext4_fs: l: 144 filetype ext4_fs: l: 152 extents ext4_fs: l: 154 64bit ext4_fs: l: 158 flex_bg ext4_fs: l: 222 [info] sblock features_compatible: ext4_fs: l: 177 has_journal ext4_fs: l: 179 ext_attr ext4_fs: l: 181 resize_inode ext4_fs: l: 183 dir_index ext4_fs: l: 225 [info] sblock features_read_only: ext4_fs: l: 189 sparse_super ext4_fs: l: 191 large_file ext4_fs: l: 195 huge_file ext4_fs: l: 199 dir_nlink ext4_fs: l: 201 extra_isize ext4_fs: l: 207 metadata_csum liblwext4: ext4 server epfd:10 root_fd:9 pangu: loading init shell.app ... liblwext4: ext4 server start, waitting for request... chiyou: rootfs is ready, exit chiyou event loop pangu: only support map anon mapping for process _ _ _ _ _ _ / \ / \ / \ / \ / \ / \ (M | i | n | o | s | 2 ) \_/ \_/ \_/ \_/ \_/ \_/ Welcome to Minos2 minos # help cd : "change directory" pwd : "current directory" clear : "clear the screen" ls : "list directory" help : "get help" exec : "run a application on the filesystem" exit : "exit the shell" minos # ls pangu: only support map anon mapping for process drw- c/ total 1 minos # cd c minos # ls drw- ./ drw- ../ -rw- kernel.bin drw- bin/ drw- etc/ drw- home/ -rw- ramdisk.bin -rw- qemu-arm64.dtb total 8 minos # cd bin minos # ls drw- ./ drw- ../ -rw- ps.app -rw- shell.app total 4 minos # ps PID CMD 0 pangu.srv 1 fuxi.srv 2 nvwa.srv 3 chiyou.srv 4 virtio-blk.drv 5 /c/bin/shell.app 6 /c/bin/ps.app minos # ``` ## Video tutorial **TBD** ================================================ FILE: generic/include/uapi/bootdata.h ================================================ #ifndef __LIBMINOS_BOOTDATA_H__ #define __LIBMINOS_BOOTDATA_H__ #ifdef __KERNEL__ #include #define ROOTSRV_USTACK_PAGES 4 #else #include #endif #define CMDLINE_SIZE 512 #define BOOTDATA_MAGIC 0xe4b990e6958f struct bootdata { uint64_t magic; uint64_t dtb_start; uint64_t dtb_end; uint64_t ramdisk_start; uint64_t ramdisk_end; uint64_t heap_start; uint64_t heap_end; uint64_t vmap_start; uint64_t vmap_end; int max_proc; int task_stat_handle; }; #endif ================================================ FILE: generic/include/uapi/gvm.h ================================================ #ifndef __LIBMINOS_GVM_H__ #define __LIBMINOS_GVM_H__ /* * each guest vm may have at least 64 spi irqs, * the 32 - 63 virqs is for host vdevs and the * 64 - 95 virqs is for guest vms. also the hypervisor * provide auto allocation API to allocate virqs. * the only case to use the allocate API is to allocate * the vmcs irq after all the virtual devices has been * created * * the memory map for a guest vm showed as below: * 0x00000000 -> 0x1fffffff [IOMEM for device] * 0x00000000 -> 0x0fffffff [Map to directly io device passthough] * 0x10000000 -> 0x1fffffff [virtual device] * 0x20000000 -> 0xffffffff [Normal memory] */ #define GVM_VIRT_TIMER_INT 27 #define GIC_IRQ 9 #define GICV2_GICD_IOMEM_BASE 0x10000000 #define GICV2_GICD_IOMEM_SIZE 0x10000 #define GICV2_GICC_IOMEM_BASE 0x10010000 #define GICV2_GICC_IOMEM_SIZE 0x2000 #define GICV2_GICH_IOMEM_BASE 0x10012000 #define GICV2_GICH_IOMEM_SIZE 0x2000 #define GICV2_GICV_IOMEM_BASE 0x10014000 #define GICV2_GICV_IOMEM_SIZE 0x2000 #define GICV3_GICD_IOMEM_BASE 0x10000000 #define GICV3_GICD_IOMEM_SIZE 0x10000 #define GICV3_GICR_IOMEM_BASE 0x10200000 #define GICV3_GICR_IOMEM_SIZE 0x200000 #define GICV3_GICC_IOMEM_BASE 0x10400000 #define GICV3_GICC_IOMEM_SIZE 0x2000 #define GICV3_GICH_IOMEM_BASE 0x10410000 #define GICV3_GICH_IOMEM_SIZE 0x2000 #define GICV3_GICV_IOMEM_BASE 0x10420000 #define GICV3_GICV_IOMEM_SIZE 0x2000 #define GICV3_ITS_IOMEM_BASE 0x10430000 #define GICV3_ITS_IOMEM_SIZE 0x2000 #define SP805_IRQ 32 #define SP805_CLK_RATE 100000 #define SP805_IOMEM_BASE 0x10440000 #define SP805_IOMEM_SIZE 0x1000 #define PL031_IRQ 33 #define PL031_IOMEM_BASE 0x10441000 #define PL031_IOMEM_SIZE 0x1000 #define GVM_IRQ_BASE 64 #define GVM_IRQ_COUNT 32 #define GVM_IRQ_END (GVM_IRQ_BASE + GVM_IRQ_COUNT) #define VM_MAX_VIRTIO_DEVICES 32 #define VM_VIRTIO_IOMEM_BASE 0x1fe00000 #define VM_VIRTIO_IOMEM_SIZE (0x1000 * VM_MAX_VIRTIO_DEVICES) #endif ================================================ FILE: generic/include/uapi/hypervisor.h ================================================ #ifndef __LIBMINOS_HYPERVISOR_H__ #define __LIBMINOS_HYPERVISOR_H__ #ifdef __KERNEL__ #include #else #include #include #endif #include #define VM_NAME_SIZE 32 #define VM_TYPE_SIZE 16 #define VM_FLAGS_64BIT (1 << 0) #define VM_FLAGS_NATIVE (1 << 1) #define VM_FLAGS_DYNAMIC_AFF (1 << 2) #define VM_FLAGS_NO_RAMDISK (1 << 3) #define VM_FLAGS_NO_BOOTIMAGE (1 << 4) #define VM_FLAGS_HAS_EARLYPRINTK (1 << 5) #define VM_FLAGS_NATIVE_WFI (1 << 6) #define VM_FLAGS_NO_OF_RESOURCE (1 << 7) #define VM_FLAGS_SETUP_OF (1 << 8) #define VM_FLAGS_SETUP_ACPI (1 << 9) #define VM_FLAGS_SETUP_ATAG (1 << 10) #define VM_FLAGS_SETUP_OTHER (1 << 11) #define VM_FLAGS_SETUP_MASK (0xf00) #define VM_FLAGS_XNU_APPLE (1 << 12) struct vmtag { uint32_t vmid; char name[VM_NAME_SIZE]; char os_type[VM_TYPE_SIZE]; int32_t nr_vcpu; uint64_t mem_base; uint64_t mem_size; uint64_t entry; uint64_t setup_data; uint64_t flags; uint32_t vcpu_affinity[8]; uint64_t load_address; char image_file[RAMDISK_FNAME_SIZE]; char dtb_file[RAMDISK_FNAME_SIZE]; }; #define IOCTL_CREATE_VM 0xf000 #define IOCTL_DESTROY_VM 0xf001 #define IOCTL_RESTART_VM 0xf002 #define IOCTL_POWER_DOWN_VM 0xf003 #define IOCTL_POWER_UP_VM 0xf004 #define IOCTL_VM_MMAP 0xf005 #define IOCTL_VM_UNMAP 0xf006 #define IOCTL_REGISTER_VCPU 0xf007 #define IOCTL_SEND_VIRQ 0xf008 #define IOCTL_CREATE_VMCS 0xf00a #define IOCTL_CREATE_VMCS_IRQ 0xf00b #define IOCTL_UNREGISTER_VCPU 0xf00c #define IOCTL_VIRTIO_MMIO_INIT 0xf00d #define IOCTL_VIRTIO_MMIO_DEINIT 0xf00e #define IOCTL_REQUEST_VIRQ 0xf00f #define IOCTL_CREATE_VM_RESOURCE 0xf010 struct vm_ring { volatile uint32_t ridx; volatile uint32_t widx; uint32_t size; char buf[0]; }; #define VM_RING_IDX(idx, size) (idx & (size - 1)) #endif ================================================ FILE: generic/include/uapi/ramdisk.h ================================================ #ifndef __LIBMINOS_RAMDISK_H__ #define __LIBMINOS_RAMDISK_H__ #ifdef __KERNEL__ #include #else #include #include #endif #define RAMDISK_MAGIC "MINOSRAMDISK...." #define RAMDISK_MAGIC_SIZE 16 #define RAMDISK_FNAME_SIZE 32 struct ramdisk_inode { char f_name[RAMDISK_FNAME_SIZE]; uint64_t f_offset; // data offset from ramdisk_start. uint64_t f_size; // data size of this file } __attribute__((__packed__)); struct ramdisk_sb { uint32_t file_cnt; uint32_t block_size; // always 4096 uint64_t inode_offset; // inode offset uint64_t data_offset; // file data offset. uint64_t ramdisk_size; // total size of the ramdisk. }; struct ramdisk_file { struct ramdisk_inode *inode; unsigned long pos; // reserved }; #endif ================================================ FILE: generic/include/uapi/time.h ================================================ #ifndef __LIBMINOS_TIME_H__ #define __LIBMINOS_TIME_H__ struct timespec { long ts_sec; long ts_nsec; }; #endif ================================================ FILE: generic/include/uapi/virtio_mmio.h ================================================ #ifndef __LIBMINOS_VIRTIO_MMIO_H__ #define __LIBMIONS_VIRTIO_MMIO_H__ /* * for detail of the below mmio register, refer * to the Documents/virtio-v1.0.pdf */ #define VIRTIO_MMIO_MAGIC_VALUE 0x000 #define VIRTIO_MMIO_VERSION 0x004 #define VIRTIO_MMIO_DEVICE_ID 0x008 #define VIRTIO_MMIO_VENDOR_ID 0x00c #define VIRTIO_MMIO_HOST_FEATURES 0x010 #define VIRTIO_MMIO_HOST_FEATURES_SEL 0x014 #define VIRTIO_MMIO_GUEST_FEATURES 0x020 #define VIRTIO_MMIO_GUEST_FEATURES_SEL 0x024 #define VIRTIO_MMIO_GUEST_PAGE_SIZE 0x028 #define VIRTIO_MMIO_QUEUE_SEL 0x030 #define VIRTIO_MMIO_QUEUE_NUM_MAX 0x034 #define VIRTIO_MMIO_QUEUE_NUM 0x038 #define VIRTIO_MMIO_QUEUE_ALIGN 0x03c #define VIRTIO_MMIO_QUEUE_READY 0x044 #define VIRTIO_MMIO_QUEUE_PFN 0x040 #define VIRTIO_MMIO_QUEUE_NOTIFY 0x050 #define VIRTIO_MMIO_INTERRUPT_STATUS 0x060 #define VIRTIO_MMIO_INTERRUPT_ACK 0x064 #define VIRTIO_MMIO_STATUS 0x070 #define VIRTIO_MMIO_QUEUE_DESC_LOW 0x080 #define VIRTIO_MMIO_QUEUE_DESC_HIGH 0x084 #define VIRTIO_MMIO_QUEUE_AVAIL_LOW 0x090 #define VIRTIO_MMIO_QUEUE_AVAIL_HIGH 0x094 #define VIRTIO_MMIO_QUEUE_USED_LOW 0x0a0 #define VIRTIO_MMIO_QUEUE_USED_HIGH 0x0a4 #define VIRTIO_MMIO_CONFIG_GENERATION 0x0fc #define VIRTIO_MMIO_CONFIG 0x100 /* below register from 0x300 is used for hypervisor */ #define VIRTIO_MMIO_GVM_ADDR 0x300 #define VIRTIO_MMIO_DEV_FLAGS 0x304 #define VIRTIO_MMIO_HOST_FEATURE0 0x308 #define VIRTIO_MMIO_HOST_FEATURE1 0x30c #define VIRTIO_MMIO_HOST_FEATURE2 0x310 #define VIRTIO_MMIO_HOST_FEATURE3 0x314 #define VIRTIO_MMIO_DRIVER_FEATURE0 0x318 #define VIRTIO_MMIO_DRIVER_FEATURE1 0x31c #define VIRTIO_MMIO_DRIVER_FEATURE2 0x320 #define VIRTIO_MMIO_DRIVER_FEATURE3 0x324 #define VIRTIO_DEVICE_IOMEM_SIZE 0x1000 #define VIRTIO_MMIO_INT_VRING (1 << 0) #define VIRTIO_MMIO_INT_CONFIG (1 << 1) #define VIRTIO_EVENT_BUFFER_READY (1 << 0) #define VIRTIO_EVENT_QUEUE_READY (1 << 1) #define VIRTIO_EVENT_STATUS_CHANGE (1 << 2) #define VIRTIO_EVENT_MMIO (1 << 3) #define VIRTIO_FEATURE_OFFSET(index) \ (VIRTIO_MMIO_HOST_FEATURE0 + index * 4) #endif ================================================ FILE: kernel/.gitignore ================================================ os/out/ tools/mvm/mvm tags cscope.in.out cscope.out cscope.po.out linkmap.txt include/config/config.h .* *.o *.o.* *.a *.s *.ko *.so *.so.dbg *.mod.c *.i *.lst *.symtypes *.order *.elf *.bin *.tar *.gz *.bz2 *.lzma *.xz *.lz4 *.lzo *.patch *.gcno modules.builtin Module.symvers *.dwo *.su # # Top-level generic files # /tags /TAGS /linux /vmlinux /vmlinux.32 /vmlinux-gdb.py /vmlinuz /System.map /Module.markers # # Debian directory (make deb-pkg) # /debian/ # # tar directory (make tar*-pkg) # /tar-install/ # # git files that we don't want to ignore even if they are dot-files # !.gitignore !.mailmap # # Generated include files # include/config/ include/generated arch/*/include/generated # stgit generated dirs patches-* # quilt's files patches series # cscope files cscope.* ncscope.* # gnu global files GPATH GRTAGS GSYMS GTAGS # id-utils files ID *.orig *~ \#*# # # Leavings from module signing # extra_certificates signing_key.pem signing_key.priv signing_key.x509 x509.genkey # Kconfig presets all.config # Kdevelop4 *.kdev4 *.lds allsymbols.S tmp.minos.symbols *.dtb *.py[co] tools/Kconfiglib/build/ *.egg-info/ tools/Kconfiglib/dist/ tools/fdt_parse/parse_dtb asm-offset.h ================================================ FILE: kernel/Kconfig ================================================ # Kconfig - main configuration options for Minos # # Copyright (c) 2019 Min Le # # mainmenu "Minos $ARCH Configuration" config SRCARCH string option env="SRCARCH" source "arch/$(SRCARCH)/Kconfig" ================================================ FILE: kernel/LICENSE ================================================ GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. ================================================ FILE: kernel/Makefile ================================================ # SPDX-License-Identifier: GPL-2.0 VERSION = 0 PATCHLEVEL = 0 SUBLEVEL = 1 EXTRAVERSION = NAME = unstable PHONY := _all _all: MAKEFLAGS += -rR --no-print-directory ifeq ("$(origin V)", "command line") MBUILD_VERBOSE = $(V) endif ifndef MBUILD_VERBOSE MBUILD_VERBOSE = 0 endif ifeq ($(VERBOSE),1) MBUILD_VERBOSE = 1 endif ifeq ($(MBUILD_VERBOSE),1) quiet = Q = else quiet=quiet_ Q = @ endif ifeq ("$(origin O)", "command line") O_LEVEL = $(O) endif ifndef O_LEVEL O_LEVEL = 2 endif ifeq ($(BUILD_DEBUG),1) O_LEVEL = 0 endif export quiet Q MBUILD_VERBOSE srctree := . objtree := . src := $(srctree) obj := $(objtree) VPATH := $(srctree) export srctree objtree VPATH version_h := include/config/version.h clean-targets := %clean no-dot-config-targets := $(clean-targets) cscope gtags TAGS tags help% $(version_h) no-sync-config-targets := $(no-dot-config-targets) config-targets := 0 mixed-targets := 0 dot-config := 1 may-sync-config := 1 ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),) ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),) dot-config := 0 endif endif ifneq ($(filter $(no-sync-config-targets), $(MAKECMDGOALS)),) ifeq ($(filter-out $(no-sync-config-targets), $(MAKECMDGOALS)),) may-sync-config := 0 endif endif # For "make -j clean all", "make -j mrproper defconfig all", etc. ifneq ($(filter $(clean-targets),$(MAKECMDGOALS)),) ifneq ($(filter-out $(clean-targets),$(MAKECMDGOALS)),) mixed-targets := 1 endif endif ifeq ($(mixed-targets),1) # =========================================================================== # We're called with mixed targets (*config and build targets). # Handle them one by one. PHONY += $(MAKECMDGOALS) __build_one_by_one $(filter-out __build_one_by_one, $(MAKECMDGOALS)): __build_one_by_one @: __build_one_by_one: $(Q)set -e; \ for i in $(MAKECMDGOALS); do \ $(MAKE) -f $(srctree)/Makefile $$i; \ done else include scripts/Minos.config.mk # Read KERNELRELEASE from include/config/kernel.release (if it exists) KERNELRELEASE = $(shell cat include/config/kernel.release 2> /dev/null) KERNELVERSION = $(VERSION)$(if $(PATCHLEVEL),.$(PATCHLEVEL)$(if $(SUBLEVEL),.$(SUBLEVEL)))$(EXTRAVERSION) export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION ARCH ?= aarch64 CROSS_COMPILE ?= aarch64-linux-gnu- SRCARCH := $(ARCH) offset_h := $(srctree)/arch/$(SRCARCH)/include/asm/asm-offset.h offset_s := $(srctree)/arch/$(SRCARCH)/core/asm-offset.s offset_c := $(srctree)/arch/$(SRCARCH)/core/asm-offset.c MCONFIG_CONFIG ?= .config export MCONFIG_CONFIG # Make variables (CC, etc...) AS = $(CROSS_COMPILE)as LD = $(CROSS_COMPILE)ld CC = $(CROSS_COMPILE)gcc CPP = $(CC) -E AR = $(CROSS_COMPILE)ar NM = $(CROSS_COMPILE)nm STRIP = $(CROSS_COMPILE)strip OBJCOPY = $(CROSS_COMPILE)objcopy OBJDUMP = $(CROSS_COMPILE)objdump LEX = flex YACC = bison AWK = awk PERL = perl PYTHON = python PYTHON2 = python2 PYTHON3 = python3 CHECK = sparse DTC = dtc CHECKFLAGS := -D__minos__ -Dminos -D__STDC__ -Dunix -D__unix__ \ -Wbitwise -Wno-return-void -Wno-unknown-attribute $(CF) # Use LINUXINCLUDE when you must reference the include/ directory. # Needed to be compatible with the O= option MINOSINCLUDE := \ -I$(srctree)/arch/$(SRCARCH)/include \ -I$(objtree)/include \ -I$(srctree)/../generic/include MBUILD_AFLAGS := -D__ASSEMBLY__ MBUILD_CFLAGS := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \ -fno-strict-aliasing -fno-common -fshort-wchar \ -Werror-implicit-function-declaration -D__KERNEL__ \ -Wno-format-security -O$(O_LEVEL) -DBUILD_HYPERVISOR \ -std=gnu89 --static -nostdlib -fno-builtin -g $(MINOSINCLUDE) MBUILD_CPPFLAGS := -D__KERNEL__ MBUILD_LDFLAGS := --no-undefined export ARCH SRCARCH CONFIG_SHELL HOSTCC MBUILD_HOSTCFLAGS CROSS_COMPILE AS LD CC DTC export CPP AR NM STRIP OBJCOPY OBJDUMP MBUILD_HOSTLDFLAGS MBUILD_HOSTLDLIBS export MAKE LEX YACC AWK GENKSYMS INSTALLKERNEL PERL PYTHON PYTHON2 PYTHON3 UTS_MACHINE export MBUILD_CPPFLAGS NOSTDINC_FLAGS MINOSINCLUDE OBJCOPYFLAGS MBUILD_LDFLAGS export MBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE export CFLAGS_KASAN CFLAGS_KASAN_NOSANITIZE CFLAGS_UBSAN export MBUILD_AFLAGS AFLAGS_KERNEL AFLAGS_MODULE export MBUILD_AFLAGS_KERNEL MBUILD_CFLAGS_KERNEL export RCS_FIND_IGNORE := \( -name SCCS -o -name BitKeeper -o -name .svn -o \ -name CVS -o -name .pc -o -name .hg -o -name .git \) \ -prune -o export RCS_TAR_IGNORE := --exclude SCCS --exclude BitKeeper --exclude .svn \ --exclude CVS --exclude .pc --exclude .hg --exclude .git PHONY += all _all: all core-y := core/ userspace/ drivers-y := drivers/ platform/ external-y := libs/ libs-y := -include .config drivers-$(CONFIG_VIRT) += virt/ # The arch Makefile can set ARCH_{CPP,A,C}FLAGS to override the default # values of the respective MBUILD_* variables ARCH_CPPFLAGS := ARCH_AFLAGS := ARCH_CFLAGS := -include arch/$(SRCARCH)/Makefile MBUILD_IMAGE := minos.bin MBUILD_IMAGE_ELF := minos.elf MBUILD_IMAGE_SYMBOLS := allsymbols.o all: include/config/config.h $(version_h) $(offset_h) minos dtbs minos-dirs := $(patsubst %/,%,$(filter %/, $(core-y) $(external-y) $(drivers-y) $(libs-y))) minos-alldirs := $(sort $(minos-dirs) $(patsubst %/,%,$(filter %/, \ $(core-) $(external-) $(drivers-) $(libs-)))) minos-clean-dirs = $(minos-dirs) minos-cleandirs = $(minos-alldirs) dtbs/ core-y := $(patsubst %/, %/built-in.o, $(core-y)) external-y := $(patsubst %/, %/built-in.o, $(external-y)) drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y)) libs-y1 := $(patsubst %/, %/lib.a, $(libs-y)) libs-y2 := $(patsubst %/, %/built-in.o, $(filter-out %.o, $(libs-y))) # Externally visible symbols (used by link-minos.sh) export MBUILD_MINOS_INIT := $(head-y) export MBUILD_MINOS_MAIN := $(core-y) $(libs-y2) $(drivers-y) $(external-y) export MBUILD_MINOS_LIBS := $(libs-y1) export MBUILD_LDS := $(objtree)/arch/$(SRCARCH)/lds/minos.lds export LDFLAGS_minos # used by scripts/package/Makefile export MBUILD_ALLDIRS := $(sort $(filter-out arch/%,$(minos-alldirs)) arch Documentation include samples scripts tools) minos-deps := $(MBUILD_LDS) $(MBUILD_MINOS_INIT) $(MBUILD_MINOS_MAIN) $(MBUILD_MINOS_LIBS) CLEAN_DIRS := clean: rm-dirs := $(CLEAN_DIRS) clean-dirs := $(addprefix _clean_, . $(minos-cleandirs)) minos_LDFLAGS := $(MBUILD_LDFLAGS) minos_LDFLAGS += -T$(MBUILD_LDS) -Map=$(srctree)/linkmap.txt PHONY += $(clean-dirs) clean distclean $(clean-dirs): $(Q) $(MAKE) $(clean)=$(patsubst _clean_%,%,$@) minos: $(minos-deps) scripts/generate_allsymbols.py $(Q) echo " LD .tmp.minos.elf" $(Q) $(LD) $(minos_LDFLAGS) -o .tmp.minos.elf $(MBUILD_MINOS_INIT) $(MBUILD_MINOS_MAIN) $(MBUILD_MINOS_LIBS) $(Q) echo " NM .tmp.minos.symbols" $(Q) $(NM) -n .tmp.minos.elf > .tmp.minos.symbols $(Q) echo " PYTHON allsymbols.S" $(Q) python3 scripts/generate_allsymbols.py .tmp.minos.symbols allsymbols.S $(Q) echo " CC $(MBUILD_IMAGE_SYMBOLS)" $(Q) $(CC) $(CCFLAG) $(MBUILD_CFLAGS) -c allsymbols.S -o $(MBUILD_IMAGE_SYMBOLS) $(Q) echo " LD $(MBUILD_IMAGE_ELF)" $(Q) $(LD) $(minos_LDFLAGS) -o $(MBUILD_IMAGE_ELF) $(MBUILD_MINOS_INIT) $(MBUILD_MINOS_MAIN) $(MBUILD_MINOS_LIBS) $(MBUILD_IMAGE_SYMBOLS) $(Q) echo " OBJCOPY $(MBUILD_IMAGE)" $(Q) $(OBJCOPY) -O binary $(MBUILD_IMAGE_ELF) $(MBUILD_IMAGE) $(Q) echo " OBJDUMP minos.s" $(Q) $(OBJDUMP) $(MBUILD_IMAGE_ELF) -D > minos.s # The actual objects are generated when descending, # make sure no implicit rule kicks in $(sort $(minos-deps)): $(minos-dirs) ; # here goto each directory to generate built-in.o PHONY += $(minos-dirs) $(minos-dirs): $(Q)$(MAKE) $(build)=$@ define sed-y "/^->/{s:->#\(.*\):/* \1 */:; \ s:^->\([^ ]*\) [\$$#]*\([^ ]*\) \(.*\):#define \1 \2 /* \3 */:; \ s:->::; p;}" endef define cmd_offsets (set -e; \ echo "#ifndef __ASM_OFFSETS_H__"; \ echo "#define __ASM_OFFSETS_H__"; \ echo "/*"; \ echo " * DO NOT MODIFY."; \ echo " *"; \ echo " * This file was generated by Kbuild"; \ echo " *"; \ echo " */"; \ echo ""; \ sed -ne $(sed-y) $<; \ echo ""; \ echo "#endif" ) > $@ endef define filechk_version.h (echo \#define MINOS_VERSION_CODE $(shell \ expr $(VERSION) \* 65536 + 0$(PATCHLEVEL) \* 256 + 0$(SUBLEVEL)) > $@; \ echo '#define MINOS_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))' >> $@; \ echo '#define MINOS_VERSION_STR "v$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) $(NAME)"' >> $@;) endef PHONY += scriptconfig iscriptconfig menuconfig guiconfig dumpvarsconfig install ALL_DTS = $(notdir $(wildcard dtbs/*.dts)) ALL_DTB = $(ALL_DTS:%.dts=$(TARGET_OUT_DIR)/%.dtb) $(TARGET_OUT_DIR)/kernel.bin: minos.bin $(Q) cp minos.bin $(TARGET_OUT_DIR)/kernel.bin $(TARGET_OUT_DIR)/%.dtb: dtbs/%.dtb $(Q) cp $< $@ install: $(TARGET_OUT_DIR)/kernel.bin $(ALL_DTB) PYTHONCMD ?= python kpython := PYTHONPATH=$(srctree)/scripts/Kconfiglib:$$PYTHONPATH $(PYTHONCMD) KCONFIG ?= $(srctree)/Kconfig ifneq ($(filter scriptconfig,$(MAKECMDGOALS)),) ifndef SCRIPT $(error Use "make scriptconfig SCRIPT= [SCRIPT_ARG=]") endif endif $(offset_s): $(offset_c) $(Q) echo " CC $(offset_s)" $(Q) gcc $(MINOSINCLUDE) -S $< -o $@ $(offset_h): $(offset_s) $(Q) $(call cmd_offsets) $(version_h) : Makefile $(Q) mkdir -p include/config $(Q) $(call filechk_version.h) PHONY += include/config/config.h include/config/config.h: .config $(Q) mkdir -p include/config $(Q) $(kpython) $(srctree)/scripts/Kconfiglib/genconfig.py --header-path=include/config/config.h dtbs: FORCE $(Q) $(MAKE) $(build)=$@ clean: $(clean-dirs) # $(Q) echo " CLEAN all .o .*.d *.dtb built-in.o" # $(Q) echo " CLEAN allsymbols.o allsymbols.S linkmap.txt minos.s .tmp.minos.elf .tmp.minos.symbols minos.bin minos.elf" $(Q) rm -f allsymbols.o allsymbols.S linkmap.txt minos.s .tmp.minos.elf .tmp.minos.symbols minos.bin minos.elf $(Q) rm -rf $(offset_h) $(Q) rm -rf $(offset_s) $(Q) echo " Clean kernel done..." distclean: clean $(Q) echo " CLEAN .config include/config" $(Q) rm -rf include/config .config .config.old $(Q) echo " CLEAN tags cscope.in.out cscope.out cscope.po.out" $(Q) rm -f tags cscope.in.out cscope.out cscope.po.out scriptconfig: $(Q)$(kpython) $(SCRIPT) $(Kconfig) $(if $(SCRIPT_ARG),"$(SCRIPT_ARG)") iscriptconfig: $(Q)$(kpython) -i -c \ "import kconfiglib; \ kconf = kconfiglib.Kconfig('$(Kconfig)'); \ print('A Kconfig instance \'kconf\' for the architecture $(ARCH) has been created.')" menuconfig: $(Q)$(kpython) $(srctree)/scripts/Kconfiglib/menuconfig.py $(Kconfig) guiconfig: $(Q)$(kpython) $(srctree)/scripts/Kconfiglib/guiconfig.py $(Kconfig) dumpvarsconfig: $(Q)$(kpython) $(srctree)/scripts/Kconfiglib/examples/dumpvars.py $(Kconfig) genconfig: $(Q)$(kpython) $(srctree)/scripts/Kconfiglib/genconfig.py $(Kconfig) %defconfig: $(Q)test -e configs/$@ || ( \ echo >&2; \ echo >&2 " ERROR: $@ doest exist."; \ echo >&2 ; \ /bin/false) $(Q) echo " GEN .config From configs/$@" $(Q) mkdir -p include/config $(Q) python $(srctree)/scripts/Kconfiglib/defconfig.py $(Kconfig) configs/$@ endif PHONY += FORCE FORCE: # Declare the contents of the PHONY variable as phony. We keep that # information in a variable so we can use it in if_changed and friends. .PHONY: $(PHONY) ================================================ FILE: kernel/VERSION ================================================ MAJOR_VERSION=0 MINOR_VERSION=0 EXTRA_VERSION=1 Known issue: ================================================ FILE: kernel/apps/Kconfig ================================================ menu "Application Config" config SHELL bool "Shell support" default n help "hell support using ESH from Chris Pavlina" source "apps/esh/Kconfig" endmenu ================================================ FILE: kernel/apps/Makefile ================================================ obj-$(CONFIG_SHELL) += esh/ obj-y += init.o ================================================ FILE: kernel/apps/esh/COPYING ================================================ GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. ================================================ FILE: kernel/apps/esh/Kconfig ================================================ if SHELL menu "Shell config" config SHELL_TASK_PRIO int "The priority of shell task" default 63 endmenu endif ================================================ FILE: kernel/apps/esh/Makefile ================================================ obj-$(CONFIG_SHELL) += shell.o esh.o esh_hist.o esh_argparser.o ================================================ FILE: kernel/apps/esh/README.md ================================================ esh - embedded shell ==================== esh is a lightweight command shell for embedded applications in C or Rust, small enough to be used for (and intended for) debug UART consoles on microcontrollers. This readme describes esh and how to try it out. If you think you'd like to use it, see the guides in the source for informations on how to set it up and integrate it into your project: [C header](esh.h), [Rust library](esh_rust/src/esh/lib.rs). Demo ==== There is a simple demo in the `demo` subdirectory, which can be compiled and run on a unix-like system by moving into that directory and issuing the `make` command. There are no dependencies other than libc and a compiler. Once built, execute `./demo` and try it out! The Rust demo is in `demo_rust`, and can be compiled and run on a unix-like system by moving into that directory and issuing `cargo build` and `cargo run`. Features ======== Line editing ------------ esh supports basic line editing, understanding the backspace key to delete characters, left and right arrow to move the insertion point, and Ctrl-C to ditch the entire line. Ctrl-left/right to move by a word is upcoming. Argument tokenizing ------------------- esh automatically splits a command string into arguments, and understands bash-style quoting. The command handler callback receives a simple argc/argv array of arguments, ready to use. Environment variables are not yet supported, but may be in the future. History (optional) ------------------ If compiled in, esh supports history, allowing the use of the up/down arrow keys to browse previously entered commands and edit/re-issue them. A ring buffer is used to store a fixed number of characters, so more commands can be remembered if they're shorter. ================================================ FILE: kernel/apps/esh/esh.c ================================================ /* * esh - embedded shell * Copyright (C) 2017 Chris Pavlina * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "esh.h" #define ESH_INTERNAL_INCLUDE #include "esh_argparser.h" #include "esh_internal.h" #include enum esh_flags { IN_ESCAPE = 0x01, IN_BRACKET_ESCAPE = 0x02, IN_NUMERIC_ESCAPE = 0x04, }; static struct esh * allocate_esh(void); static void free_last_allocated(struct esh * esh); static void do_print_callback(struct esh * esh, char c); static void do_command(struct esh * esh, int argc, char ** argv); static void do_overflow_callback(struct esh * esh, char const * buffer); static bool command_is_nop(struct esh * esh); static void execute_command(struct esh * esh); static void handle_char(struct esh * esh, char c); static void handle_esc(struct esh * esh, char esc); static void handle_ctrl(struct esh * esh, char c); static void ins_del(struct esh * esh, char c); static void term_cursor_move(struct esh * esh, int n); static void cursor_move(struct esh * esh, int n); static void word_move(struct esh * esh, int dir); void esh_default_overflow(struct esh * esh, char const * buffer, void * arg); #ifdef ESH_STATIC_CALLBACKS extern void ESH_PRINT_CALLBACK(struct esh * esh, char c, void * arg); extern void ESH_COMMAND_CALLBACK( struct esh * esh, int argc, char ** argv, void * arg); __attribute__((weak)) void ESH_OVERFLOW_CALLBACK(struct esh * esh, char const * buffer, void * arg) { (void) arg; esh_default_overflow(esh, buffer, arg); } #else void esh_register_command(struct esh * esh, esh_cb_command callback) { (void) esh; ESH_INSTANCE->cb_command = callback; } void esh_register_print(struct esh * esh, esh_cb_print callback) { (void) esh; ESH_INSTANCE->print = callback; } void esh_register_overflow(struct esh * esh, esh_cb_overflow overflow) { (void) esh; ESH_INSTANCE->overflow = (overflow ? overflow : &esh_default_overflow); } #endif // API WARNING: This function is separately declared in lib.rs void esh_set_command_arg(struct esh * esh, void * arg) { (void) esh; ESH_INSTANCE->cb_command_arg = arg; } // API WARNING: This function is separately declared in lib.rs void esh_set_print_arg(struct esh * esh, void * arg) { (void) esh; ESH_INSTANCE->cb_print_arg = arg; } // API WARNING: This function is separately declared in lib.rs void esh_set_overflow_arg(struct esh * esh, void * arg) { (void) esh; ESH_INSTANCE->cb_overflow_arg = arg; } static void do_print_callback(struct esh * esh, char c) { (void) esh; #ifdef ESH_STATIC_CALLBACKS ESH_PRINT_CALLBACK(ESH_INSTANCE, c, ESH_INSTANCE->cb_print_arg); #else ESH_INSTANCE->print(ESH_INSTANCE, c, ESH_INSTANCE->cb_print_arg); #endif } static void do_command(struct esh * esh, int argc, char ** argv) { (void) esh; #ifdef ESH_STATIC_CALLBACKS ESH_COMMAND_CALLBACK(ESH_INSTANCE, argc, argv, ESH_INSTANCE->cb_command_arg); #else ESH_INSTANCE->cb_command(ESH_INSTANCE, argc, argv, ESH_INSTANCE->cb_command_arg); #endif } static void do_overflow_callback(struct esh * esh, char const * buffer) { (void) esh; #ifdef ESH_STATIC_CALLBACKS ESH_OVERFLOW_CALLBACK(ESH_INSTANCE, buffer, ESH_INSTANCE->cb_overflow_arg); #else ESH_INSTANCE->overflow(ESH_INSTANCE, buffer, ESH_INSTANCE->cb_overflow_arg); #endif } /** * For the static allocator, this is global so free_last_allocated() can * decrement it. */ #if ESH_ALLOC == STATIC static bool g_allocated = false; struct esh g_esh_struct; #endif /** * Allocate a new struct esh, or return a new statically allocated one from the pool. * This does not perform initialization. */ static struct esh * allocate_esh(void) { #if ESH_ALLOC == STATIC if (g_allocated) { return NULL; } else { g_allocated = true; return &g_esh_struct; } #elif ESH_ALLOC == MALLOC return malloc(sizeof(struct esh)); #else # error "ESH_ALLOC must be STATIC or MALLOC" #endif } /** * Free the last struct esh that was allocated, in case an initialization error * occurs after allocation. */ static void free_last_allocated(struct esh *esh) { #if ESH_ALLOC == STATIC (void) esh; g_allocated = false; #elif ESH_ALLOC == MALLOC free(esh); #endif } // API WARNING: This function is separately declared in lib.rs struct esh * esh_init(void) { struct esh * esh = allocate_esh(); memset(esh, 0, sizeof(*esh)); #ifndef ESH_STATIC_CALLBACKS esh->overflow = &esh_default_overflow; #endif if (esh_hist_init(ESH_INSTANCE)) { free_last_allocated(ESH_INSTANCE); return NULL; } else { return esh; } } // API WARNING: This function is separately declared in lib.rs void esh_rx(struct esh * esh, char c) { (void) esh; if (ESH_INSTANCE->flags & (IN_BRACKET_ESCAPE | IN_NUMERIC_ESCAPE)) { handle_esc(ESH_INSTANCE, c); } else if (ESH_INSTANCE->flags & IN_ESCAPE) { if (c == '[' || c == 'O') { ESH_INSTANCE->flags |= IN_BRACKET_ESCAPE; } else { ESH_INSTANCE->flags &= ~(IN_ESCAPE | IN_BRACKET_ESCAPE); } } else { // Verify the c is valid non-extended ASCII (and thus also valid // UTF-8, for Rust), regardless of whether this platform's isprint() // accepts things above 0x7f. if (c >= 0x20 && (unsigned char) c < 0x7f) { handle_char(ESH_INSTANCE, c); } else { handle_ctrl(ESH_INSTANCE, c); } } } /** * Process a normal text character. If there is room in the buffer, it is * inserted directly. Otherwise, the buffer is set into the overflow state. */ static void handle_char(struct esh * esh, char c) { (void) esh; esh_hist_substitute(ESH_INSTANCE); if (ESH_INSTANCE->cnt < ESH_BUFFER_LEN) { ins_del(ESH_INSTANCE, c); } else { // If we let esh->cnt keep counting past the buffer limit, it could // eventually wrap around. Let it sit right past the end, and make sure // there is a NUL terminator in the buffer (we promise the overflow // handler that). ESH_INSTANCE->cnt = ESH_BUFFER_LEN + 1; // Note that the true buffer length is actually one greater than // ESH_BUFFER_LEN (which is the number of characters NOT including the // terminator that it can hold). ESH_INSTANCE->buffer[ESH_BUFFER_LEN] = 0; } } /** * Process a single-character control byte. */ static void handle_ctrl(struct esh * esh, char c) { (void) esh; switch (c) { case 27: // escape ESH_INSTANCE->flags |= IN_ESCAPE; break; case 3: // ^C esh_puts_flash(ESH_INSTANCE, FSTR("^C\n")); esh_print_prompt(ESH_INSTANCE); ESH_INSTANCE->cnt = ESH_INSTANCE->ins = 0; break; case '\n': execute_command(ESH_INSTANCE); break; case 8: // backspace case 127: // delete esh_hist_substitute(ESH_INSTANCE); if (ESH_INSTANCE->cnt > 0 && ESH_INSTANCE->cnt <= ESH_BUFFER_LEN) { ins_del(ESH_INSTANCE, 0); } break; default: // nop ; } } /** * Process the last character in an escape sequence. */ static void handle_esc(struct esh * esh, char esc) { (void) esh; int cdelta; if (esc >= '0' && esc <= '9') { ESH_INSTANCE->flags |= IN_ESCAPE | IN_BRACKET_ESCAPE | IN_NUMERIC_ESCAPE; return; } if (ESH_INSTANCE->flags & IN_NUMERIC_ESCAPE) { // Numeric escapes can contain numbers and semicolons; they terminate // at letters and ~ if (esc == '~' || isalpha(esc)) { ESH_INSTANCE->flags &= ~(IN_BRACKET_ESCAPE | IN_NUMERIC_ESCAPE | IN_ESCAPE); } } else { ESH_INSTANCE->flags &= ~(IN_BRACKET_ESCAPE | IN_NUMERIC_ESCAPE | IN_ESCAPE); } switch (esc) { case ESCCHAR_UP: case ESCCHAR_DOWN: if (esc == ESCCHAR_UP) { ++ESH_INSTANCE->hist.idx; } else if (ESH_INSTANCE->hist.idx) { --ESH_INSTANCE->hist.idx; } if (ESH_INSTANCE->hist.idx) { int offset = esh_hist_nth(ESH_INSTANCE, ESH_INSTANCE->hist.idx - 1); if (offset >= 0 || esc == ESCCHAR_DOWN) { esh_hist_print(ESH_INSTANCE, offset); } else if (esc == ESCCHAR_UP) { // Don't overscroll the top --ESH_INSTANCE->hist.idx; } } else { esh_restore(ESH_INSTANCE); } break; case ESCCHAR_LEFT: cdelta = -1; goto cmove; case ESCCHAR_RIGHT: cdelta = 1; goto cmove; case ESCCHAR_HOME: cdelta = -ESH_INSTANCE->ins; goto cmove; case ESCCHAR_END: cdelta = ESH_INSTANCE->cnt - ESH_INSTANCE->ins; goto cmove; case ESCCHAR_CTRLLEFT: cdelta = -1; goto wmove; case ESCCHAR_CTRLRIGHT: cdelta = 1; goto wmove; } return; cmove: // micro-optimization, yo! cursor_move(ESH_INSTANCE, cdelta); return; wmove: word_move(ESH_INSTANCE, cdelta); } /** * Return whether the command in the edit buffer is a NOP and should be ignored. * This does not substitute the selected history item. */ static bool command_is_nop(struct esh * esh) { int i; (void) esh; for (i = 0; ESH_INSTANCE->buffer[i]; ++i) { if (ESH_INSTANCE->buffer[i] != ' ') { return false; } } return true; } /** * Process the command in the buffer and give it to the command callback. If * the buffer has overflowed, call the overflow callback instead. */ static void execute_command(struct esh * esh) { (void) esh; // If a command from the history is selected, put it in the edit buffer. esh_hist_substitute(ESH_INSTANCE); if (ESH_INSTANCE->cnt >= ESH_BUFFER_LEN) { do_overflow_callback(ESH_INSTANCE, ESH_INSTANCE->buffer); ESH_INSTANCE->cnt = ESH_INSTANCE->ins = 0; esh_print_prompt(ESH_INSTANCE); return; } else { ESH_INSTANCE->buffer[ESH_INSTANCE->cnt] = 0; } esh_putc(ESH_INSTANCE, '\n'); if (!command_is_nop(ESH_INSTANCE)) { esh_hist_add(ESH_INSTANCE, ESH_INSTANCE->buffer); int argc = esh_parse_args(ESH_INSTANCE); if (argc > ESH_ARGC_MAX) { do_overflow_callback(ESH_INSTANCE, ESH_INSTANCE->buffer); } else if (argc > 0) { do_command(ESH_INSTANCE, argc, ESH_INSTANCE->argv); } } ESH_INSTANCE->cnt = ESH_INSTANCE->ins = 0; esh_print_prompt(ESH_INSTANCE); } void esh_print_prompt(struct esh * esh) { (void) esh; esh_puts_flash(ESH_INSTANCE, FSTR(ESH_PROMPT)); } /** * Default overflow callback. This just prints a message. */ // API WARNING: This function is separately declared in lib.rs void esh_default_overflow(struct esh * esh, char const * buffer, void * arg) { (void) esh; (void) buffer; (void) arg; esh_puts_flash(ESH_INSTANCE, FSTR("\nesh: command buffer overflow\n")); } bool esh_putc(struct esh * esh, char c) { (void) esh; do_print_callback(ESH_INSTANCE, c); return false; } bool esh_puts(struct esh * esh, char const * s) { (void) esh; char c; while ((c = *s++)) { esh_putc(ESH_INSTANCE, c); } return false; } #ifdef __AVR_ARCH__ bool esh_puts_flash(struct esh * esh, char const __flash * s) { (void) esh; char c; while ((c = *s++)) { esh_putc(ESH_INSTANCE, c); } return false; } #endif // __AVR_ARCH__ void esh_restore(struct esh * esh) { (void) esh; esh_puts_flash(ESH_INSTANCE, FSTR(ESC_ERASE_LINE "\r")); // Clear line esh_print_prompt(ESH_INSTANCE); ESH_INSTANCE->buffer[ESH_INSTANCE->cnt] = 0; esh_puts(ESH_INSTANCE, ESH_INSTANCE->buffer); term_cursor_move(ESH_INSTANCE, -(int)(ESH_INSTANCE->cnt - ESH_INSTANCE->ins)); } #ifdef ESH_RUST // API WARNING: This function is separately declared in lib.rs size_t esh_get_slice_size(void) { return sizeof (struct char_slice); } #endif /** * Move only the terminal cursor. This does not move the insertion point. */ static void term_cursor_move(struct esh * esh, int n) { (void) esh; for ( ; n > 0; --n) { esh_puts_flash(ESH_INSTANCE, FSTR(ESC_CURSOR_RIGHT)); } for ( ; n < 0; ++n) { esh_puts_flash(ESH_INSTANCE, FSTR(ESC_CURSOR_LEFT)); } } /** * Move the esh cursor. This applies history substitution, moves the terminal * cursor, and moves the insertion point. */ static void cursor_move(struct esh * esh, int n) { (void) esh; esh_hist_substitute(ESH_INSTANCE); if ((int) ESH_INSTANCE->ins + n < 0) { n = -ESH_INSTANCE->ins; } else if ((int) ESH_INSTANCE->ins + n > (int) ESH_INSTANCE->cnt) { n = ESH_INSTANCE->cnt - ESH_INSTANCE->ins; } term_cursor_move(ESH_INSTANCE, n); ESH_INSTANCE->ins += n; } /** * Move the esh cursor backwards or forwards one word. This applies history * substitution, moves the terminal cursor, and moves the insertion point. * * @param dir - move forward if +1 or negative if -1. All other numbers produce * undefined behavior. */ static void word_move(struct esh * esh, int dir) { (void) esh; size_t ins = ESH_INSTANCE->ins; esh_hist_substitute(ESH_INSTANCE); if (dir == 0) { return; } else if (dir < 0) { for (; ins > 0 && ESH_INSTANCE->buffer[ins - 1] == ' '; --ins); for (; ins > 0 && ESH_INSTANCE->buffer[ins - 1] != ' '; --ins); } else { const size_t cnt = ESH_INSTANCE->cnt; for (; ins < cnt && ESH_INSTANCE->buffer[ins] != ' '; ++ins); for (; ins < cnt && ESH_INSTANCE->buffer[ins] == ' '; ++ins); } term_cursor_move(ESH_INSTANCE, ins - ESH_INSTANCE->ins); ESH_INSTANCE->ins = ins; } /** * Either insert or delete a character at the current insertion point. * @param esh - esh instance * @param c - character to insert, or 0 to delete */ static void ins_del(struct esh * esh, char c) { (void) esh; int sgn = c ? 1 : -1; bool move = (ESH_INSTANCE->ins != ESH_INSTANCE->cnt); memmove(&ESH_INSTANCE->buffer[ESH_INSTANCE->ins + sgn], &ESH_INSTANCE->buffer[ESH_INSTANCE->ins], ESH_INSTANCE->cnt - ESH_INSTANCE->ins); if (c) { ESH_INSTANCE->buffer[ESH_INSTANCE->ins] = c; } ESH_INSTANCE->cnt += sgn; ESH_INSTANCE->ins += sgn; if (move) { esh_restore(ESH_INSTANCE); } else if (!c) { esh_puts_flash(ESH_INSTANCE, FSTR("\b \b")); } else { esh_putc(ESH_INSTANCE, c); } } ================================================ FILE: kernel/apps/esh/esh.h ================================================ /** * esh - embedded shell * ==================== * * ***************************************************************************** * * PLEASE read ALL of this documentation (all comment blocks starting with a * * * double-asterisk **). esh is simple, but a number of things need to be * * * addressed by every esh user. * * ***************************************************************************** * * esh is a lightweight command shell for embedded applications in C or rust, * small enough to be used for (and intended for) debug UART consoles on * microcontrollers. Features include line editing, automatic argument * tokenizing (including sh-like quoting), and an optional history ring buffer. * * esh - embedded shell * Copyright (C) 2017 Chris Pavlina * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * ----------------------------------------------------------------------------- * * 1. Rust users * 2. Configuring esh * 2.1. Line endings * 2.2. Static callbacks * 2.3. History (optional) * 3. Compiling esh * 4. Code documentation * 4.1. Basic interface: initialization and input * 4.2. Callback types and registration functions * 4.3. Advanced functions * * ----------------------------------------------------------------------------- * * 1. Rust users * ============= * * The Rust API and configuration is different, so if you want to use esh with * Rust, see esh_rust/src/esh/lib.rs for separate documentation. None of this * documentation (including the definitions for `esh_config.h`) applies. * * 2. Configuring esh * ================== * * esh expects a file called `esh_config.h` to be on the quoted include path. It * should define the following: * * #define ESH_PROMPT "% " // Prompt string * #define ESH_BUFFER_LEN 200 // Maximum length of a command * #define ESH_ARGC_MAX 10 // Maximum argument count * #define ESH_ALLOC STATIC // How to allocate struct esh (or MALLOC) * * Then, to use esh, include `esh.h`, and initialize an esh instance with: * * struct esh * esh = esh_init(); * * Unless you're using static callbacks (see below), register your callbacks * with: * * esh_register_command(esh, &command_callback); * esh_register_print(esh, &print_callback); * * // Optional, see the documentation for this function: * esh_register_overflow(esh, &overflow_callback); * * Now, just begin receiving characters from your serial interface and feeding * them in with: * * esh_rx(esh, c); * * 2.1. Line endings * ----------------- * * Internally, esh uses strictly `\n` line endings. A great many IO sources use * different line endings; the user is responsible for translating them for esh. * In general, most raw-mode unix-like terminals will give `\r` from the * keyboard and require `\r\n` as output, so your input functions should * translate `\r` to `\n`, and your output function should insert `\r` before * `\n`. * * 2.2. Static callbacks * --------------------- * * If you're only using one esh instance, or all your esh instances use the * same callbacks, callbacks can be compiled in statically, saving a bit of code * space, runtime, and RAM from keeping and following the pointers. Add the * following to your `esh_config.h`: * * #define ESH_STATIC_CALLBACKS * * Now, simply name your callback functions `ESH_PRINT_CALLBACK`, * `ESH_COMMAND_CALLBACK`, and `ESH_OVERFLOW_CALLBACK` (the overflow callback * is still optional), and the linker will find them. The esh_register_* * functions are not defined when ESH_STATIC_CALLBACKS is set. * * 2.3. History (optional) * ----------------------- * * To enable the optional history, define the following in `esh_config.h`: * * #define ESH_HIST_ALLOC STATIC // STATIC, MANUAL, or MALLOC * #define ESH_HIST_LEN 512 // Length. Use powers of 2 for * // efficiency on arithmetic-weak * // devices. * * If you chose `MANUAL` allocation, call `esh_set_histbuf()` once you have * allocated your own buffer of length ESH_HIST_LEN: * * esh_set_histbuf(esh, &buffer[0]); * * Manual allocation was created for one specific purpose: history buffer in * external SRAM on AVR (the compiler and allocator don't generally know about * external SRAM unless you jump through hoops). However, it's there for * whatever you like :) * * WARNING: static allocation is only valid when using a SINGLE esh instance. * Using multiple esh instances with static allocation is undefined and WILL * make demons fly out your nose. * * 3. Compiling esh * ================ * * esh has no build script of its own; building it is trivial and it's meant to * be integrated directly into your project. * * 1. Put the `esh` subdirectory on the include path. * 2. Make sure `esh_config.h` is on the quoted include path (`-iquote`). * 3. Make sure selected C standard is one of `c99`, `c11`, `gnu99`, or * `gnu11`. On AVR, use only `gnu99` or `gnu11` (esh on AVR uses the * Named Address Spaces GNU extension). * 4. Include *all* esh C source files in the build (whether or not you used * the feature - e.g. esh_hist.c). * * esh should compile quietly with most warning settings, including * `-Wall -Wextra -pedantic`. */ #ifndef ESH_H #define ESH_H #define ESH_INTERNAL_INCLUDE #include "esh_incl_config.h" #include "esh_hist.h" #undef ESH_INTERNAL_INCLUDE #include struct esh; /** * ----------------------------------------------------------------------------- * * 4. Code documentation */ /** * ----------------------------------------------------------------------------- * 4.1. Basic interface: initialization and input */ /* * Return a pointer to an initialized esh object. Must be called before * any other functions. * * See ESH_ALLOC in esh_config.h - this should be STATIC or MALLOC. * If STATIC, only a single instance can be used. esh_init() will return a * pointer to it on the first call, and all subsequent calls will return * NULL. * * @return esh instance, or NULL in the following cases: * - using malloc to allocate either the esh struct itself or the history * buffer, and malloc returns NULL. * - using static allocation and esh has already been initialized. * - whichever allocation method was chosen for ESH_HIST_ALLOC, if any, * failed. */ struct esh *esh_init(void); /** * Pass in a character that was received. */ void esh_rx(struct esh * esh, char c); #ifndef ESH_STATIC_CALLBACKS /** * ----------------------------------------------------------------------------- * 4.2. Callback types and registration functions * * These only exist if ESH_STATIC_CALLBACKS is not defined. */ /** * Callback to handle commands. * @param argc - number of arguments, including the command name * @param argv - arguments * @param arg - arbitrary argument passed to esh_set_command_arg() */ typedef void (*esh_cb_command)(struct esh *esh, int argc, char **argv, void *arg); /** * Callback to print a character. * @param esh - the esh instance calling * @param c - the character to print * @param arg - arbitrary argument passed to esh_set_print_arg() */ typedef void (*esh_cb_print)(struct esh * esh, char c, void *arg); /** * Callback to notify about overflow. * @param esh - the esh instance calling * @param buffer - the internal buffer, NUL-terminated * @param arg - arbitrary argument passed to esh_set_overflow_arg() */ typedef void (*esh_cb_overflow)(struct esh *esh, char const *buffer, void *rg); /** * Register a callback to execute a command. */ void esh_register_command(struct esh *esh, esh_cb_command callback); /** * Register a callback to print a character. */ void esh_register_print(struct esh *esh, esh_cb_print callback); /** * Register a callback to notify about overflow. Optional; esh has an internal * overflow handler. To reset to that, set the handler to NULL. */ void esh_register_overflow(struct esh *esh, esh_cb_overflow overflow); #endif /** * ----------------------------------------------------------------------------- * 4.3. Advanced functions */ /** * Set the location of the history buffer, if ESH_HIST_ALLOC is defined and * set to MANUAL. If ESH_HIST_ALLOC is not defined or not set to MANUAL, this * is a no-op. */ void esh_set_histbuf(struct esh *esh, char *buffer); /** * Set an argument to be given to the command callback. Default is NULL. */ void esh_set_command_arg(struct esh *esh, void *arg); /** * Set an argument to be given to the print callback. Default is NULL. */ void esh_set_print_arg(struct esh * esh, void *arg); /** * Set an argument to be given to the overflow callback. Default is NULL. */ void esh_set_overflow_arg(struct esh *esh, void *arg); #endif // ESH_H ================================================ FILE: kernel/apps/esh/esh_argparser.c ================================================ /* * esh - embedded shell * Copyright (C) 2017 Chris Pavlina * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #define ESH_INTERNAL #include "esh.h" #define ESH_INTERNAL_INCLUDE #include "esh_argparser.h" #include "esh_internal.h" #define DEST(esh) ((esh)->buffer) /** * Consume a quoted string. The source string will be modified into the * destination string as follows: * * source: " b" * dest: b * * This is safe to use when the destination and source buffer are the same; * it will only ever contract the data, not expand it. */ static void consume_quoted(struct esh *esh, size_t *src_i, size_t *dest_i) { (void) esh; char quote = ESH_INSTANCE->buffer[*src_i]; for (++*src_i; *src_i < ESH_INSTANCE->cnt; ++*src_i) { char c = ESH_INSTANCE->buffer[*src_i]; if (c == quote) { // End of quoted string break; } else { DEST(ESH_INSTANCE)[*dest_i] = c; ++*dest_i; } } } int esh_parse_args(struct esh *esh) { size_t i; (void) esh; int argc = 0; bool last_was_space = true; size_t dest = 0; for (i = 0; i < ESH_INSTANCE->cnt; ++i) { if (ESH_INSTANCE->buffer[i] == ' ') { last_was_space = true; ESH_INSTANCE->buffer[dest] = 0; ++dest; } else { if (last_was_space) { if (argc < ESH_ARGC_MAX) { ESH_INSTANCE->argv[argc] = &ESH_INSTANCE->buffer[dest]; } ++argc; } if (ESH_INSTANCE->buffer[i] == '\'' || ESH_INSTANCE->buffer[i] == '\"') { consume_quoted(ESH_INSTANCE, &i, &dest); } else { ESH_INSTANCE->buffer[dest] = ESH_INSTANCE->buffer[i]; ++dest; } last_was_space = false; } } ESH_INSTANCE->buffer[dest] = 0; ESH_INSTANCE->buffer[ESH_BUFFER_LEN] = 0; return argc; } ================================================ FILE: kernel/apps/esh/esh_argparser.h ================================================ /* * esh - embedded shell * Copyright (C) 2017 Chris Pavlina * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef ESH_INTERNAL_INCLUDE #error "esh_argparser.h is an internal header and should not be included by the user." #endif // ESH_INTERNAL_INCLUDE #ifndef ESH_ARGPARSER_H #define ESH_ARGPARSER_H struct esh; /** * Map the buffer to the argv array, and return argc. If argc exceeds the * maximum, the full buffer will still be processed; argument pointers will * just not be stored beyond the maximum. The number that would have been * stored is returned. * * Handles whitespace and quotes. The entire buffer is processed in place, * with any changes leaveing the length equal or shorter. * * Following is an example buffer before and after processing (# for NUL), * with pointers stored in argv[] marked with ^ * * before: git config user.name "My Name" * after: git###config#user.name#My Name# * argv: ^ ^ ^ ^ * * Rearranging the buffer is necessary because quotes can occur in the * middle of arguments. For example: * * before: why" would you ever"'"'"do this??" * after: why would you ever"do this??# * argv: ^ * */ int esh_parse_args(struct esh *esh); #endif // ESH_ARGPARSER_H ================================================ FILE: kernel/apps/esh/esh_config.h ================================================ #define ESH_PROMPT "minos # " #define ESH_BUFFER_LEN 200 #define ESH_ARGC_MAX 10 #define ESH_HIST_ALLOC STATIC #define ESH_HIST_LEN 4096 #define ESH_ALLOC STATIC #define ESH_INSTANCES 1 ================================================ FILE: kernel/apps/esh/esh_hist.c ================================================ /* * esh - embedded shell * Copyright (C) 2017 Chris Pavlina * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ // History allocation types #define STATIC 1 #define MANUAL 2 #define MALLOC 3 #include "esh.h" #define ESH_INTERNAL_INCLUDE #include "esh_internal.h" #include #ifdef ESH_HIST_ALLOC // Begin actual history implementation /** * Initialize the history buffer. * * The initial value is a NUL byte followed by a fill of 0xff. This avoids * the extra empty-string history entry that would be seen if the buffer * were filled with 0x00. */ static void init_buffer(char * buffer) { memset(buffer, 0xff, ESH_HIST_LEN); buffer[0] = 0; } /** * True signed modulo, for wraparound signed arithmetic. This is mathematically * equivalent to "0 + a mod b". */ static int modulo(int n, int modulus) { int rem = n % modulus; return (rem >= 0) ? rem : rem + modulus; } /** * Given an offset in the ring buffer, call the callback once for each * character in the string starting there. This is meant to abstract away * ring buffer access. * * @param esh - esh instance * @param offset - offset into the ring buffer * @param callback - will be called once per character * - callback:param esh - esh instance * - callback:param c - character * - callback:return - true to stop iterating, false to continue * * Regardless of the callback's return value, iteration will always stop at NUL * or if the loop wraps all the way around. */ static void for_each_char(struct esh * esh, int offset, bool (*callback)(struct esh * esh, char c)) { int i; (void) esh; for (i = offset; ESH_INSTANCE->hist.hist[i]; i = (i + 1) % ESH_HIST_LEN) { if (i == modulo(offset - 1, ESH_HIST_LEN)) { // Wrapped around and didn't encounter NUL. Stop here to prevent // an infinite loop. return; } if (callback(ESH_INSTANCE, ESH_INSTANCE->hist.hist[i])) { return; } } } /** * Internal callback passed to for_each_char by clobber_buffer */ static bool clobber_cb(struct esh * esh, char c) { (void) esh; ESH_INSTANCE->buffer[ESH_INSTANCE->cnt] = c; ++ESH_INSTANCE->cnt; ++ESH_INSTANCE->ins; return false; } /** * Put the selected history item in the buffer. Make sure to call * esh_restore afterward to display the buffer. * @param esh - esh instance * @param offset - offset into the ring buffer */ static void clobber_buffer(struct esh * esh, int offset) { (void) esh; if (offset < 0 || offset >= ESH_HIST_LEN) { return; } ESH_INSTANCE->cnt = 0; ESH_INSTANCE->ins = 0; for_each_char(ESH_INSTANCE, offset, &clobber_cb); } bool esh_hist_init(struct esh * esh) { (void) esh; #if ESH_HIST_ALLOC == STATIC static char esh_hist[ESH_HIST_LEN] = {0}; ESH_INSTANCE->hist.hist = &esh_hist[0]; init_buffer(ESH_INSTANCE->hist.hist); return false; #elif ESH_HIST_ALLOC == MALLOC ESH_INSTANCE->hist.hist = malloc(ESH_HIST_LEN); if (ESH_INSTANCE->hist.hist) { init_buffer(ESH_INSTANCE->hist.hist); return false; } else { return true; } #elif ESH_HIST_ALLOC == MANUAL ESH_INSTANCE->hist.hist = NULL; return false; #endif } int esh_hist_nth(struct esh * esh, int n) { int i; (void) esh; const int start = modulo(ESH_INSTANCE->hist.tail - 1, ESH_HIST_LEN); const int stop = (ESH_INSTANCE->hist.tail + 1) % ESH_HIST_LEN; for (i = start; i != stop; i = modulo(i - 1, ESH_HIST_LEN)) { if (n && ESH_INSTANCE->hist.hist[i] == 0) { --n; } else if (ESH_INSTANCE->hist.hist[i] == 0) { return (i + 1) % ESH_HIST_LEN; } } return -1; } bool esh_hist_add(struct esh * esh, char const * s) { int i; (void) esh; const int start = (ESH_INSTANCE->hist.tail + 1) % ESH_HIST_LEN; for (i = start; ; i = (i + 1) % ESH_HIST_LEN) { if (i == modulo(ESH_INSTANCE->hist.tail - 1, ESH_HIST_LEN)) { // Wrapped around ESH_INSTANCE->hist.tail = 0; init_buffer(ESH_INSTANCE->hist.hist); return true; } ESH_INSTANCE->hist.hist[i] = *s; if (*s) { ++s; } else { ESH_INSTANCE->hist.tail = i; return false; } } } void esh_hist_print(struct esh * esh, int offset) { (void) esh; // Clear the line esh_puts_flash(ESH_INSTANCE, FSTR(ESC_ERASE_LINE "\r")); esh_print_prompt(ESH_INSTANCE); if (offset >= 0) { for_each_char(ESH_INSTANCE, offset, esh_putc); } } bool esh_hist_substitute(struct esh * esh) { (void) esh; if (ESH_INSTANCE->hist.idx) { int offset = esh_hist_nth(ESH_INSTANCE, ESH_INSTANCE->hist.idx - 1); clobber_buffer(ESH_INSTANCE, offset); esh_restore(ESH_INSTANCE); ESH_INSTANCE->hist.idx = 0; return true; } else { return false; } } #endif // ESH_HIST_ALLOC #if defined(ESH_HIST_ALLOC) && ESH_HIST_ALLOC == MANUAL void esh_set_histbuf(struct esh * esh, char * buffer) { ESH_INSTANCE->hist.hist = buffer; init_buffer(ESH_INSTANCE->hist.hist); } #else // ESH_HIST_ALLOC == MANUAL void esh_set_histbuf(struct esh * esh, char * buffer) { (void) esh; (void) buffer; } #endif // ESH_HIST_ALLOC == MANUAL ================================================ FILE: kernel/apps/esh/esh_hist.h ================================================ /* * esh - embedded shell * Copyright (C) 2017 Chris Pavlina * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef ESH_HIST_H #define ESH_HIST_H #include /* * esh history support. This provides either a full history implementation or * a placeholder, depending on whether history was enabled in configuration. * This allows the main esh code to not be conditionally compiled. */ struct esh; #ifdef ESH_HIST_ALLOC // Begin actual history implementation struct esh_hist { char *hist; int tail; int idx; }; /** * Initialize history. * @param esh - esh instance * @return true on error. Can only return error if the history buffer is to be * allocated on heap. */ bool esh_hist_init(struct esh *esh); /** * Count back n strings from the current tail of the ring buffer and return the * index the string starts at. * * @param esh - esh instance * @param n - count to the nth string back, where 0 is the last string added * @return offset in the ring buffer where the nth string starts, or -1 if * there are not n-1 strings in the buffer. */ int esh_hist_nth(struct esh *esh, int n); /** * Add a string into the buffer. If the string doesn't fit, the buffer is * intentionally reset to avoid restoring a corrupted string later. * @param esh - esh instance * @param s - string to add * @return true iff the string didn't fit (this is destructive!) */ bool esh_hist_add(struct esh *esh, char const *s); /** * Overwrite the prompt and print a history suggestion. * @param esh - esh instance * @param offset - offset into the ring buffer */ void esh_hist_print(struct esh *esh, int offset); /** * If history is currently being browsed, substitute the selected history item * for the buffer and redraw the buffer for editing. * @param esh - esh instance * @return true iff the substitution was made (i.e. history was being browsed) */ bool esh_hist_substitute(struct esh *esh); #else // ESH_HIST_ALLOC // Begin placeholder implementation struct esh_hist { size_t idx; }; #define INL static inline __attribute__((always_inline)) INL bool esh_hist_init(struct esh *esh) { (void) esh; return false; } INL int esh_hist_nth(struct esh *esh, int n) { (void) esh; (void) n; return -1; } INL bool esh_hist_add(struct esh *esh, char const *s) { (void) esh; (void) s; return true; } INL void esh_hist_for_each_char( struct esh *esh, int offset, bool (*callback)(struct esh * esh, char c)) { (void) esh; (void) offset; (void) callback; } INL void esh_hist_print(struct esh *esh, int offset) { (void) esh; (void) offset; } INL void esh_hist_restore(struct esh *esh) { (void) esh; } INL void esh_hist_clobber(struct esh *esh, int offset) { (void) esh; (void) offset; } INL bool esh_hist_substitute(struct esh *esh) { (void) esh; return false; } #undef INL #endif // ESH_HIST_ALLOC #endif // ESH_HIST_H ================================================ FILE: kernel/apps/esh/esh_incl_config.h ================================================ /* * esh - embedded shell * Copyright (C) 2017 Chris Pavlina * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef ESH_INCL_CONFIG_H #define ESH_INCL_CONFIG_H #define STATIC 1 #define MANUAL 2 #define MALLOC 3 #include "esh_config.h" #ifdef ESH_RUST #define ESH_STATIC_CALLBACKS #endif #endif // ESH_INCL_CONFIG_H ================================================ FILE: kernel/apps/esh/esh_internal.h ================================================ /* * esh - embedded shell * Copyright (C) 2017 Chris Pavlina * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef ESH_INTERNAL_H #define ESH_INTERNAL_H #include "esh_incl_config.h" #include "esh_hist.h" #include struct tty; /** * If we're building for Rust, we need to know the size of a &[u8] in order * to allocate space for it. This definition should be equivalent. Because the * internal representation of a slice has not been stabilized [1], this is not * guaranteed to remain constant in the future; the Rust bindings will check * sizeof(struct char_slice) against mem::size_of::<&[u8]>(). * * [1] https://github.com/rust-lang/rust/issues/27751 */ #ifdef ESH_RUST struct char_slice { char *p; size_t sz; }; #endif /** * esh instance struct. This holds all of the state that needs to be saved * between calls to esh_rx(). */ struct esh { /** * The config item ESH_BUFFER_LEN is only the number of characters to be * stored, not characters plus termination. */ char buffer[ESH_BUFFER_LEN + 1]; /** * The Rust bindings require space allocated for an argv array of &[u8], * which can share memory with C's char* array to save limited SRAM. */ #ifdef ESH_RUST union { char * argv[ESH_ARGC_MAX]; struct char_slice rust_argv[ESH_ARGC_MAX]; }; #else char * argv[ESH_ARGC_MAX]; #endif size_t cnt; ///< Number of characters currently held in .buffer size_t ins; ///< Position of the current insertion point uint8_t flags; ///< State flags for escape sequence parser struct esh_hist hist; #ifndef ESH_STATIC_CALLBACKS esh_cb_command cb_command; esh_cb_print print; esh_cb_overflow overflow; #endif void *cb_command_arg; void *cb_print_arg; void *cb_overflow_arg; struct tty *tty; }; /** * On AVR, a number of strings should be stored in and read from flash space. * Other architectures have linearized address spaces and don't require this. */ #ifdef __AVR_ARCH__ # define FSTR(s) (__extension__({ \ static const __flash char __c[] = (s); \ &__c[0];})) # define AVR_ONLY(x) x #else # define FSTR(s) (s) # define AVR_ONLY(x) #endif // __AVR_ARCH__ /** * Print one character. * @return false (allows it to be an esh_hist_for_each_char callback) */ bool esh_putc(struct esh *esh, char c); /** * @internal * Print a string located in RAM. */ bool esh_puts(struct esh *esh, char const *s); /** * @internal * Print a string located in flash. On all but AVR this is an alias for * esh_puts(). */ #ifdef __AVR_ARCH__ bool esh_puts_flash(struct esh *esh, char const __flash * s); #else #define esh_puts_flash esh_puts #endif /** * Print the prompt string */ void esh_print_prompt(struct esh *esh); /** * Overwrite the prompt and restore the buffer. */ void esh_restore(struct esh *esh); /** * Call the print callback. Wrapper to avoid ifdefs for static callback. */ void esh_do_print_callback(struct esh *esh, char c); /** * Call the main callback. Wrapper to avoid ifdefs for static callback. */ void esh_do_callback(struct esh *esh, int argc, char **argv); /** * Call the overflow callback. Wrapper to avoid ifdefs for the static * callback. */ void esh_do_overflow_callback(struct esh *esh, char const * buffer); #ifdef ESH_RUST /** * Return what we think the size of a Rust &[u8] slice is. This is used to * verify that the statically allocated slice array is long enough, and also * to make sure a linker error is produced if ESH_RUST wasn't enabled * (which would mean the slice array wasn't allocated at all). */ size_t esh_get_slice_size(void); #endif #define ESC_CURSOR_RIGHT "\33[1C" #define ESC_CURSOR_LEFT "\33[1D" #define ESC_ERASE_LINE "\33[2K" #define ESCCHAR_UP 'A' #define ESCCHAR_DOWN 'B' #define ESCCHAR_RIGHT 'C' #define ESCCHAR_LEFT 'D' #define ESCCHAR_HOME 'H' #define ESCCHAR_END 'F' #define ESCCHAR_CTRLLEFT 'd' #define ESCCHAR_CTRLRIGHT 'c' #if ESH_ALLOC == STATIC extern struct esh g_esh_struct; #define ESH_INSTANCE (&g_esh_struct) #else #define ESH_INSTANCE esh #endif // ESH_ALLOC #endif // ESH_INTERNAL_H ================================================ FILE: kernel/apps/esh/shell.c ================================================ /* * Copyright (C) 2020 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include "esh.h" #include "esh_internal.h" static struct esh *pesh; static void __esh_putc(struct esh *esh, char c, void *arg) { console_putc(c); } static void esh_excute_command(struct esh *esh, int argc, char **argv, void *arg) { int ret; ret = excute_shell_command(argc, argv); if (ret == -ENOENT) printf("Command \'%s\' not found\n", argv[0]); } static void shell_detach_tty(void) { close_tty(pesh->tty); printf("\nDetach tty: %s\n", pesh->tty->name); pesh->tty = NULL; } static int shell_cmd_tty(int argc, char **argv) { if (argc > 2 && strcmp(argv[1], "attach") == 0) { printf("Attach tty: %s press any key to active the console\n", argv[2]); pesh->tty = open_tty(argv[2]); if (!pesh->tty) { printf("no such tty\n"); return -EINVAL; } } else { printf("unsupport action now\n"); } return 0; } DEFINE_SHELL_COMMAND(tty, "tty", "tty related command", shell_cmd_tty, 2); int shell_task(void *data) { char buf[32]; char ch; int i, copy; char *tty = NULL; pesh = esh_init(); esh_register_command(pesh, esh_excute_command); esh_register_print(pesh, __esh_putc); esh_rx(pesh, '\n'); while (console_gets(buf, 32, 0) != 0); if ((long)data == 0) { i = bootarg_parse_string("tty", &tty); if (!i && tty && !strncmp(tty, "vm", 2)) { printf("\nAttach tty: %s\n", tty); pesh->tty = open_tty(tty); } } for (; ;) { copy = console_gets(buf, 32, -1); for (i = 0; i < copy; i++) { ch = buf[i]; if (ch <= 0) continue; if (pesh->tty) { /* * use Ctrl-\ to exit the vm tty */ if (ch == 28) { shell_detach_tty(); esh_rx(pesh, '\n'); } else { pesh->tty->ops->put_char(pesh->tty, ch); } } else { if (ch == '\r') ch = '\n'; esh_rx(pesh, ch); } } } return 0; } ================================================ FILE: kernel/apps/init.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #ifdef CONFIG_VIRT #include #endif #ifdef CONFIG_VIRT static long __skip_vm_boot; #else static long __skip_vm_boot = 1; #endif static void skip_vm_boot(void) { #ifdef CONFIG_VIRT #ifdef CONFIG_SHELL uint32_t wait; char str[8]; int i; bootarg_parse_uint("bootwait", &wait); if (wait > 0) { printf("\nPress any key to stop vm startup: %d ", wait); for (i = 0; i < wait; i++) { printf("\b\b%d ", wait - i); if (console_gets(str, 8, 1000) > 0) { __skip_vm_boot = 1; break; } } } if (!__skip_vm_boot) { printf("\b\b "); printf("\n"); } #endif if (!__skip_vm_boot) start_all_vm(); #endif } static void start_shell_task(void) { #ifdef CONFIG_SHELL extern int shell_task(void *data); create_task("shell_task", shell_task, 0x2000, OS_PRIO_SYSTEM, -1, 0, (void *)__skip_vm_boot); #endif } int init_task(void *data) { skip_vm_boot(); start_shell_task(); return 0; } ================================================ FILE: kernel/arch/aarch64/Kconfig ================================================ config ARCH_AARCH64 def_bool y select DEVICE_TREE select 64BIT help AARCH64 Minos support config 64BIT def_bool y source "arch/aarch64/core/Kconfig" if VIRT source "arch/aarch64/virt/Kconfig" endif source "core/Kconfig" # source "virt/Kconfig" source "drivers/Kconfig" source "platform/Kconfig" source "libs/Kconfig" source "userspace/Kconfig" ================================================ FILE: kernel/arch/aarch64/Makefile ================================================ # # arch/arm64/Makefile # # This file is included by the global makefile so that you can add your own # architecture-specific flags and dependencies. # # This file is subject to the terms and conditions of the GNU General Public # License. See the file "COPYING" in the main directory of this archive # for more details. # # Copyright (C) 1995-2001 by Russell King # ifeq ($(CONFIG_ARM64_LSE_ATOMICS), y) ifeq ($(lseinstr),) $(warning LSE atomics not supported by binutils) endif endif ifeq ($(CONFIG_ARCH_AARCH64), y) brokengasinst := $(call as-instr,1:\n.inst 0\n.rept . - 1b\n\nnop\n.endr\n,,-DCONFIG_BROKEN_GAS_INST=1) ifneq ($(brokengasinst),) $(warning Detected assembler with broken .inst; disassembly will be unreliable) endif endif ifeq ($(CONFIG_ARM_ATOMIC_LSE), y) ARMV8_ARCH = armv8.1-a else ARMV8_ARCH = armv8-a endif MBUILD_LDFLAGS += -X MBUILD_CFLAGS += -mgeneral-regs-only $(lseinstr) $(brokengasinst) MBUILD_CFLAGS += -fno-asynchronous-unwind-tables -march=$(ARMV8_ARCH) -ffixed-x18 MBUILD_AFLAGS += $(lseinstr) $(brokengasinst) ifeq ($(CONFIG_CPU_BIG_ENDIAN), y) MBUILD_CFLAGS += -mbig-endian # Prefer the baremetal ELF build target, but not all toolchains include # it so fall back to the standard linux version if needed. MBUILD_LDFLAGS += -EB else MBUILD_CFLAGS += -mlittle-endian -mcmodel=large MBUILD_LDFLAGS += -EL endif # Default value head-y := arch/aarch64/core/boot.o core-y += arch/aarch64/core/ core-y += arch/aarch64/lib/ core-y += arch/aarch64/lds/ core-y += arch/aarch64/userspace/ core-$(CONFIG_VIRT) += arch/aarch64/virt/ ================================================ FILE: kernel/arch/aarch64/core/Kconfig ================================================ menu "Minos aarch64 Arch Feature" config NR_CPUS_CLUSTER0 int "cpu number in cluster0" default 4 help cpu count in SOC cluster0, this will help to caculate the cpuid config NR_CPUS_CLUSTER1 int "cpu number in cluster1" default 0 help cpu count in SOC cluster1, this will help to caculate the cpuid config EXCEPTION_STACK_SIZE hex "the stack size of the exception vector" default 0x2000 help the stack size of the exception vector config ARM_ATOMIC_LSE bool "use lse atomic instruction" default n help this require at leaset armv8.1 soc config ARM_ADDRESS_TAGGING bool default (VIRT && ARM_VHE) || (!VIRT) config PTOV_MASK hex default 0xffffff8000000000 if ARM_ADDRESS_TAGGING default 0x0 config VTOP_MASK hex default 0x0000007fffffffff if ARM_ADDRESS_TAGGING default 0x0 endmenu ================================================ FILE: kernel/arch/aarch64/core/Makefile ================================================ obj-y += aarch64_IRQ.o obj-y += aarch64.o obj-y += aarch64_sync.o obj-y += arch.o obj-y += arm_arch_timer.o obj-y += boot.o obj-y += cache.o obj-y += cpu.o obj-y += entry.o obj-y += mem_map.o obj-y += vector.o obj-y += cpu_feature.o obj-y += stage1.o obj-y += fpsimd.o ================================================ FILE: kernel/arch/aarch64/core/aarch64.S ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include .global arch_raw_smp_processor_id .global arch_get_fp .global arch_get_lr .global arch_get_sp .global smc_call .global hvc_call .global aarch64_task_exit func arch_raw_smp_processor_id mrs x0, MPIDR_EL1 ldr x4, =(1 << 24) and x5, x0, x4 cbnz x5, __shift_mpidr ubfx x1, x0, #MPIDR_EL1_AFF0_LSB, #MPIDR_EL1_AFF_WIDTH ubfx x2, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH b __out __shift_mpidr: ubfx x1, x0, #MPIDR_EL1_AFF1_LSB, #MPIDR_EL1_AFF_WIDTH ubfx x2, x0, #MPIDR_EL1_AFF2_LSB, #MPIDR_EL1_AFF_WIDTH __out: mov x3, #CONFIG_NR_CPUS_CLUSTER0 mul x2, x2, x3 add x0, x2, x1 dsb sy ret endfunc arch_raw_smp_processor_id func arch_get_fp mov x0, x29 ret endfunc arch_get_fp func arch_get_lr mov x0, x30 ret endfunc arch_get_lr func arch_get_sp mov x0, sp ret endfunc arch_get_sp func smc_call smc #0 ldr x4, [sp] stp x0, x1, [x4, #0] stp x2, x3, [x4, #16] ret endfunc smc_call func hvc_call hvc #0 ldr x4, [sp] stp x0, x1, [x4, #0] stp x2, x3, [x4, #16] ret endfunc hvc_call func aarch64_task_exit mov x0, x0 bl task_exit 1: b 1b endfunc aarch64_task_exit ================================================ FILE: kernel/arch/aarch64/core/aarch64_IRQ.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include static inline void irq_handler(gp_regs *regs) { do_irq_handler(); } void irq_from_lower_el(gp_regs *regs) { irq_handler(regs); } void irq_from_current_el(gp_regs *regs) { irq_handler(regs); } ================================================ FILE: kernel/arch/aarch64/core/aarch64_sync.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include static char *mode_info[] = { "Sync taken from current EL with SP0", "IRQ taken from current EL with SP0", "FIQ taken from current EL with SP0", "Serr taken from current EL with SP0", "Sync taken from current EL with SPx", "IRQ taken from current EL with SPx", "FIQ taken from current EL with SPx", "Serr taken from current EL with SPx", "Sync taken from lower EL with AARCH32" "IRQ taken from lower EL with AARCH32" "FIQ taken from lower EL with AARCH32" "Serr taken from lower EL with AARCH32" "Sync taken from lower EL with AARCH64" "IRQ taken from lower EL with AARCH64" "FIQ taken from lower EL with AARCH64" "Serr taken from lower EL with AARCH64" }; static const char *esr_class_str[] = { [0 ... ESR_ELx_EC_MAX] = "UNRECOGNIZED EC", [ESR_ELx_EC_UNKNOWN] = "Unknown/Uncategorized", [ESR_ELx_EC_WFx] = "WFI/WFE", [ESR_ELx_EC_CP15_32] = "CP15 MCR/MRC", [ESR_ELx_EC_CP15_64] = "CP15 MCRR/MRRC", [ESR_ELx_EC_CP14_MR] = "CP14 MCR/MRC", [ESR_ELx_EC_CP14_LS] = "CP14 LDC/STC", [ESR_ELx_EC_FP_ASIMD] = "ASIMD", [ESR_ELx_EC_CP10_ID] = "CP10 MRC/VMRS", [ESR_ELx_EC_CP14_64] = "CP14 MCRR/MRRC", [ESR_ELx_EC_ILL] = "PSTATE.IL", [ESR_ELx_EC_SVC32] = "SVC (AArch32)", [ESR_ELx_EC_HVC32] = "HVC (AArch32)", [ESR_ELx_EC_SMC32] = "SMC (AArch32)", [ESR_ELx_EC_SVC64] = "SVC (AArch64)", [ESR_ELx_EC_HVC64] = "HVC (AArch64)", [ESR_ELx_EC_SMC64] = "SMC (AArch64)", [ESR_ELx_EC_SYS64] = "MSR/MRS (AArch64)", [ESR_ELx_EC_SVE] = "SVE", [ESR_ELx_EC_IMP_DEF] = "EL3 IMP DEF", [ESR_ELx_EC_IABT_LOW] = "IABT (lower EL)", [ESR_ELx_EC_IABT_CUR] = "IABT (current EL)", [ESR_ELx_EC_PC_ALIGN] = "PC Alignment", [ESR_ELx_EC_DABT_LOW] = "DABT (lower EL)", [ESR_ELx_EC_DABT_CUR] = "DABT (current EL)", [ESR_ELx_EC_SP_ALIGN] = "SP Alignment", [ESR_ELx_EC_FP_EXC32] = "FP (AArch32)", [ESR_ELx_EC_FP_EXC64] = "FP (AArch64)", [ESR_ELx_EC_SERROR] = "SError", [ESR_ELx_EC_BREAKPT_LOW] = "Breakpoint (lower EL)", [ESR_ELx_EC_BREAKPT_CUR] = "Breakpoint (current EL)", [ESR_ELx_EC_SOFTSTP_LOW] = "Software Step (lower EL)", [ESR_ELx_EC_SOFTSTP_CUR] = "Software Step (current EL)", [ESR_ELx_EC_WATCHPT_LOW] = "Watchpoint (lower EL)", [ESR_ELx_EC_WATCHPT_CUR] = "Watchpoint (current EL)", [ESR_ELx_EC_BKPT32] = "BKPT (AArch32)", [ESR_ELx_EC_VECTOR32] = "Vector catch (AArch32)", [ESR_ELx_EC_BRK64] = "BRK (AArch64)", }; void bad_mode(gp_regs *regs, int mode) { pr_fatal("bad error: %s\n", mode_info[mode]); arch_dump_register(regs); panic("Bad error received\n"); } static const char *get_ec_class_string(int ec) { return esr_class_str[ec]; } static int kernel_mem_fault(gp_regs *regs, int ec, uint32_t esr) { __panic(regs, "Memory fault in kernel space\n"); } static int unknown_trap_handler(gp_regs *regs, int ec, uint32_t esr) { __panic(regs, "Unknown exception class: ESR: 0x%x -- %s\n", esr, get_ec_class_string(ec)); return 0; } static inline int da_is_write_fault(uint32_t esr) { return !!(esr & ESR_ELx_WNR); } static int __handle_user_page_fault(uint64_t addr, int is_write, unsigned long status) { panic("Unsupport User Page Fault\n"); return -EFAULT; } weak_alias(__handle_user_page_fault, handle_user_page_fault); static int __handle_user_ia_fault(void) { panic("Unsupport User IA Fault\n"); return -EFAULT; } weak_alias(__handle_user_ia_fault, handle_user_ia_fault); static int user_da_fault(gp_regs *regs, int ec, uint32_t esr) { unsigned long fault_status; uint64_t far; int is_write; switch (esr & ESR_ELx_FSC) { case FSC_SEA: case FSC_SEA_TTW0: case FSC_SEA_TTW1: case FSC_SEA_TTW2: case FSC_SEA_TTW3: case FSC_SECC: case FSC_SECC_TTW0: case FSC_SECC_TTW1: case FSC_SECC_TTW2: case FSC_SECC_TTW3: panic("TBD\n"); break; default: break; } if (esr & ESR_ELx_FnV) { pr_err("far is not vaild external abort ?\n"); panic("TBD\n"); } /* * currently we only handle FSC_FAULT and FSC_PERM, Access fault * will not support currently. */ fault_status = esr & ESR_ELx_FSC_TYPE; if (fault_status != FSC_FAULT && fault_status != FSC_PERM) { pr_err("Unsupported FSC: EC=%#x xFSC=%#lx ESR_EL1=%#lx\n", ec, fault_status, esr); panic("TBD\n"); } far = read_sysreg(ARM64_FAR); is_write = da_is_write_fault(esr); return handle_user_page_fault(far, is_write, fault_status); } int user_ia_fault(gp_regs *regs, int ec, uint32_t esr) { struct task *task = current; pr_err("User instruction abort pid:%d tid:%d ESR:0x%x IP:0x%x\n", task->pid, task->tid, esr, regs->pc); return handle_user_ia_fault(); } int user_svc64(gp_regs *regs, int ec, uint32_t esr) { extern int aarch64_do_syscall(gp_regs *regs); return aarch64_do_syscall(regs); } DEFINE_SYNC_DESC(trap_unknown, EC_TYPE_AARCH64, unknown_trap_handler, 1, 0); DEFINE_SYNC_DESC(trap_kernel_da, EC_TYPE_AARCH64, kernel_mem_fault, 1, 0); DEFINE_SYNC_DESC(trap_kernel_ia, EC_TYPE_AARCH64, kernel_mem_fault, 1, 0); DEFINE_SYNC_DESC(trap_user_da, EC_TYPE_AARCH64, user_da_fault, 1, 0); DEFINE_SYNC_DESC(trap_user_ia, EC_TYPE_AARCH64, user_ia_fault, 1, 0); DEFINE_SYNC_DESC(trap_user_svc64, EC_TYPE_AARCH64, user_svc64, 1, 0); static struct sync_desc *process_sync_descs[] = { [0 ... ESR_ELx_EC_MAX] = &sync_desc_trap_unknown, [ESR_ELx_EC_IABT_CUR] = &sync_desc_trap_kernel_ia, [ESR_ELx_EC_DABT_CUR] = &sync_desc_trap_kernel_da, [ESR_ELx_EC_IABT_LOW] = &sync_desc_trap_user_ia, [ESR_ELx_EC_DABT_LOW] = &sync_desc_trap_user_da, [ESR_ELx_EC_SVC64] = &sync_desc_trap_user_svc64, }; static inline uint32_t read_esr(void) { #ifdef CONFIG_VIRT return read_esr_el2(); #else return read_esr_el1(); #endif } static void handle_sync_exception(gp_regs *regs) { uint32_t esr_value; uint32_t ec_type; struct sync_desc *ec; esr_value = read_esr(); ec_type = ESR_ELx_EC(esr_value); if (ec_type >= ESR_ELx_EC_MAX) panic("unknown sync exception type from current EL %d\n", ec_type); /* * for normal userspace process the return address shall * be adjust */ ec = process_sync_descs[ec_type]; regs->pc += ec->ret_addr_adjust; ec->handler(regs, ec_type, esr_value); } void sync_exception_from_current_el(gp_regs *regs) { handle_sync_exception(regs); } void sync_exception_from_lower_el(gp_regs *regs) { #ifdef CONFIG_VIRT extern void handle_vcpu_sync_exception(gp_regs *regs); /* * check whether this task is a vcpu task or a normal * userspace task. * 1 - TGE bit means a normal task * 2 - current->flags */ if ((current->flags & TASK_FLAGS_VCPU) && !(read_hcr_el2() & HCR_EL2_TGE)) handle_vcpu_sync_exception(regs); else #endif handle_sync_exception(regs); } ================================================ FILE: kernel/arch/aarch64/core/arch.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_VIRT #define read_esr() read_esr_el2() #else #define read_esr() read_esr_el1() #endif #ifdef CONFIG_VIRT extern void vcpu_context_restore(struct task *task); extern void vcpu_context_save(struct task *task); #endif #ifdef CONFIG_DEVICE_TREE extern void of_parse_host_device_tree(void); #endif void arch_dump_register(gp_regs *regs) { unsigned long spsr; if (!regs) return; spsr = regs->pstate; pr_fatal("SPSR:0x%x Mode:%d-%s F:%d I:%d A:%d D:%d NZCV:%x\n", spsr, (spsr & 0x7), (spsr & 0x8) ? "aarch64" : "aarch32", (spsr & (BIT(6))) >> 6, (spsr & (BIT(7))) >> 7, (spsr & (BIT(8))) >> 8, (spsr & (BIT(9))) >> 9, spsr >> 28); pr_fatal("x0:0x%p x1:0x%p x2:0x%p\n", regs->x0, regs->x1, regs->x2); pr_fatal("x3:0x%p x4:0x%p x5:0x%p\n", regs->x3, regs->x4, regs->x5); pr_fatal("x6:0x%p x7:0x%p x8:0x%p\n", regs->x6, regs->x7, regs->x8); pr_fatal("x9:0x%p x10:0x%p x11:0x%p\n", regs->x9, regs->x10, regs->x11); pr_fatal("x12:0x%p x13:0x%p x14:0x%p\n", regs->x12, regs->x13, regs->x14); pr_fatal("x15:0x%p x16:0x%p x17:0x%p\n", regs->x15, regs->x16, regs->x17); pr_fatal("x18:0x%p x19:0x%p x20:0x%p\n", regs->x18, regs->x19, regs->x20); pr_fatal("x21:0x%p x22:0x%p x23:0x%p\n", regs->x21, regs->x22, regs->x23); pr_fatal("x24:0x%p x25:0x%p x26:0x%p\n", regs->x24, regs->x25, regs->x26); pr_fatal("x27:0x%p x28:0x%p x29:0x%p\n", regs->x27, regs->x28, regs->x29); pr_fatal("lr:0x%p sp_el0:0x%p spsr:0x%p\n", regs->lr, regs->sp, regs->pstate); pr_fatal("pc:0x%p esr:0x%p\n", regs->pc, read_esr()); } void arch_dump_stack(gp_regs *regs, unsigned long *stack) { struct task *task = get_current_task(); unsigned long fp, lr = 0; if ((task) && os_is_running()) { pr_fatal("current task: tid:%d prio:%d name:%s\n", get_task_tid(task), get_task_prio(task), task->name); } arch_dump_register(regs); if (!stack) { if (regs) { fp = regs->x29; lr = regs->pc; } else { fp = arch_get_fp(); lr = arch_get_lr(); } } else { fp = *stack; } pr_fatal("Call Trace :\n"); pr_fatal("------------ cut here ------------\n"); do { print_symbol(lr); if ((fp < (unsigned long)task->stack_bottom) || (fp >= (unsigned long)task->stack_top)) break; lr = *(unsigned long *)(fp + sizeof(unsigned long)); lr -= 4; fp = *(unsigned long *)fp; } while (1); } int arch_taken_from_guest(gp_regs *regs) { return !!((regs->pstate & 0xf) != (AARCH64_SPSR_EL2h)); } int arch_is_exit_to_user(struct task *task) { gp_regs *regs = (gp_regs *)task->stack_base; return !!((regs->pstate & 0xf) != (AARCH64_SPSR_EL2h)); } static inline uint64_t task_ttbr_value(struct task *task) { struct vspace *vs = task->vs; return (uint64_t)vtop(vs->pgdp) | ((uint64_t)vs->asid << 48); } static inline void user_task_sched_out(struct task *task) { struct cpu_context *c = &task->cpu_context; extern void fpsimd_state_save(struct task *task, struct fpsimd_context *c); c->tpidr_el0 = read_sysreg(TPIDR_EL0); fpsimd_state_save(task, &c->fpsimd_state); } static inline void user_task_sched_in(struct task *task) { struct cpu_context *c = &task->cpu_context; extern void fpsimd_state_restore(struct task *task, struct fpsimd_context *c); write_sysreg(c->tpidr_el0, TPIDR_EL0); write_sysreg(c->tpidrro_el0, TPIDRRO_EL0); fpsimd_state_restore(task, &c->fpsimd_state); write_sysreg(c->ttbr_el0, TTBR0_EL1); } void arch_task_sched_out(struct task *task) { if (!(task->flags & TASK_FLAGS_KERNEL)) user_task_sched_out(task); } void arch_task_sched_in(struct task *task) { if (!(task->flags & TASK_FLAGS_KERNEL)) user_task_sched_in(task); } void arch_init_task(struct task *task, void *entry, void *user_sp, void *arg) { extern void aarch64_task_exit(void); gp_regs *regs = stack_to_gp_regs(task->stack_top); memset(regs, 0, sizeof(gp_regs)); task->stack_base = (void *)regs; regs->pc = (uint64_t)entry; regs->sp = (uint64_t)user_sp; regs->x18 = (uint64_t)task; if (task->flags & TASK_FLAGS_KERNEL) { /* * if the task is not a deadloop the task will exist * by itself like below * int main(int argc, char **argv) * { * do_some_thing(); * return 0; * } * then the lr register should store a function to * handle the task's exist * * kernel task will not use fpsimd now, so kernel task * do not need to save/restore it */ regs->lr = (uint64_t)aarch64_task_exit; regs->pstate = AARCH64_SPSR_EL1h; } else { regs->pstate = AARCH64_SPSR_EL0t; task->cpu_context.tpidr_el0 = 0; task->cpu_context.tpidrro_el0 = (uint64_t)task->pid << 32 | (task->tid); task->cpu_context.ttbr_el0 = task_ttbr_value(task); } } void arch_set_task_user_stack(struct task *task, unsigned long stack) { gp_regs *regs = stack_to_gp_regs(task->stack_top); regs->sp = stack; } void arch_set_task_reg0(struct task *task, unsigned long data) { gp_regs *regs = stack_to_gp_regs(task->stack_top); regs->x0 = data; } void arch_set_tls(struct task *task, unsigned long tls) { struct cpu_context *ctx = &task->cpu_context; ctx->tpidr_el0 = tls; } void arch_set_task_entry_point(struct task *task, long entry) { gp_regs *regs = stack_to_gp_regs(task->stack_top); regs->pc = entry; } pgd_t *arch_alloc_process_page_table(void) { pgd_t *pgt; /* * - 3 levels page table. * - 4KB page size. * - 512G virtual memory space. * - 4KB PGD */ pgt = get_free_pages(1, GFP_KERNEL); if (!pgt) return NULL; memset(pgt, 0, PAGE_SIZE); return pgt; } int arch_get_asid_size(void) { // TBD, get from the register return 256; } void arch_release_task(struct task *task) { } static int __init_text aarch64_init_percpu(void) { uint64_t reg; reg = read_CurrentEl(); pr_notice("current EL is %d\n", GET_EL(reg)); /* * set IMO and FMO let physic irq and fiq taken to * EL2, without this irq and fiq will not send to * the cpu */ #ifdef CONFIG_VIRT reg = read_sysreg64(HCR_EL2); reg |= HCR_EL2_IMO | HCR_EL2_FMO | HCR_EL2_AMO; write_sysreg64(reg, HCR_EL2); // write_sysreg64(0x3 << 20, CPACR_EL2); dsb(); isb(); #else write_sysreg64(0x3 << 20, CPACR_EL1); dsb(); isb(); #endif return 0; } arch_initcall_percpu(aarch64_init_percpu); int arch_early_init(void) { #ifdef CONFIG_DEVICE_TREE /* * set up the platform from the dtb file then get the spin * table information if the platform is using spin table to * wake up other cores */ of_setup_platform(); #endif return 0; } int __arch_init(void) { #ifdef CONFIG_DEVICE_TREE of_parse_host_device_tree(); #endif return 0; } uint64_t cpuid_to_affinity(int cpuid) { int aff0, aff1; if (cpu_has_feature(ARM_FEATURE_MPIDR_SHIFT)) { if (cpuid < CONFIG_NR_CPUS_CLUSTER0) return (cpuid << MPIDR_EL1_AFF1_LSB); else { aff0 = cpuid - CONFIG_NR_CPUS_CLUSTER0; aff1 = 1; return (aff1 << MPIDR_EL1_AFF2_LSB) | (aff0 << MPIDR_EL1_AFF1_LSB); } } else { if (cpuid < CONFIG_NR_CPUS_CLUSTER0) { return cpuid; } else { aff0 = cpuid - CONFIG_NR_CPUS_CLUSTER0; aff1 = 1; return (aff1 << MPIDR_EL1_AFF1_LSB) + aff0; } } } int affinity_to_cpuid(unsigned long affinity) { int aff0, aff1; if (cpu_has_feature(ARM_FEATURE_MPIDR_SHIFT)) { aff0 = (affinity >> MPIDR_EL1_AFF1_LSB) & 0xff; aff1 = (affinity >> MPIDR_EL1_AFF2_LSB) & 0xff; } else { aff0 = (affinity >> MPIDR_EL1_AFF0_LSB) & 0xff; aff1 = (affinity >> MPIDR_EL1_AFF1_LSB) & 0xff; } return (aff1 * CONFIG_NR_CPUS_CLUSTER0) + aff0; } void arch_smp_init(phy_addr_t *smp_h_addr) { #ifdef CONFIG_DEVICE_TREE of_spin_table_init(smp_h_addr); #endif } static void *relocate_dtb_address(unsigned long dtb) { unsigned long mem_end, relocated_base = dtb; extern unsigned long minos_end; size_t size; ASSERT(!fdt_check_header((void *)dtb)); /* * DTB image start address should bigger than minos_end, or * DTB image should included in minos.bin. */ mem_end = PAGE_BALIGN(ptov(minos_end)); size = fdt_totalsize(dtb); if ((dtb < minos_end) && ((dtb + size) > minos_end)) panic("minos data overlaped by dtb image\n"); if (dtb > mem_end) { pr_notice("relocate dtb from 0x%x to 0x%x\n", dtb, mem_end); memmove((void *)mem_end, (void *)dtb, size); /* * call of init again to check and setup the new * device tree memory address. */ relocated_base = mem_end; minos_end += PAGE_BALIGN(size); } return (void *)relocated_base; } void arch_main(void *dtb) { extern void boot_main(void); char *name = NULL; pr_notice("Starting Minos AARCH64\n"); #ifdef CONFIG_DTB_LOAD_ADDRESS dtb = (void *)ptov(CONFIG_DTB_LOAD_ADDRESS); #else dtb = (void *)ptov(dtb); #endif pr_notice("DTB address [0x%x]\n", dtb); dtb = relocate_dtb_address((unsigned long)dtb); of_init(dtb); of_get_console_name(&name); console_init(name); /* * here start the kernel */ boot_main(); } ================================================ FILE: kernel/arch/aarch64/core/arm_arch_timer.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include enum timer_type { SEC_PHY_TIMER, NONSEC_PHY_TIMER, VIRT_TIMER, HYP_TIMER, TIMER_MAX, }; static char *timer_name[TIMER_MAX] = { " sec_phy_timer ", "nonsec_phy_timer ", " virt_timer ", "hypervisor_timer " }; struct armv8_timer_info { uint32_t irq; unsigned long flags; }; static struct armv8_timer_info timer_info[TIMER_MAX]; uint32_t cpu_khz = 0; uint64_t boot_tick = 0; extern unsigned long sched_tick_handler(unsigned long data); void arch_enable_timer(unsigned long expires) { uint64_t deadline; unsigned long ctl; if (expires == 0) { write_sysreg32(0, ARM64_CNTSCHED_CTL); return; } deadline = ns_to_ticks(expires); write_sysreg64(deadline, ARM64_CNTSCHED_CVAL); ctl = read_sysreg(ARM64_CNTSCHED_CTL); ctl |= CNT_CTL_ENABLE; ctl &= ~CNT_CTL_IMASK; write_sysreg(ctl, ARM64_CNTSCHED_CTL); isb(); } unsigned long get_sys_ticks(void) { isb(); return read_sysreg64(CNTPCT_EL0); } unsigned long get_current_time(void) { isb(); return ticks_to_ns(read_sysreg64(CNTPCT_EL0) - boot_tick); } unsigned long get_sys_time(void) { isb(); return ticks_to_ns(read_sysreg64(CNTPCT_EL0)); } static int __init_text timers_arch_init(void) { int i, ret, from_dt; struct armv8_timer_info *info; struct device_node *node = NULL; #ifdef CONFIG_DEVICE_TREE node = of_find_node_by_compatible(of_root_node, arm_arch_timer_match_table); #endif if (!node) { pr_err("can not find arm-arch-timer\n"); return -EINVAL; } for (i = 0; i < TIMER_MAX; i++) { info = &timer_info[i]; ret = get_device_irq_index(node, &info->irq, &info->flags, i); if (ret) { pr_err("error found in arm timer config\n"); return -ENOENT; } pr_notice("%s : %d\n", timer_name[i], info->irq); } ret = of_get_u32_array(node, "clock-frequency", &cpu_khz, 1); if (cpu_khz > 0) { cpu_khz = cpu_khz / 1000; from_dt = 1; } else { cpu_khz = read_sysreg32(CNTFRQ_EL0) / 1000; from_dt = 0; } isb(); boot_tick = read_sysreg64(CNTPCT_EL0); pr_notice("clock freq from %s %d\n", from_dt ? "DTB" : "REG", cpu_khz); pr_notice("boot ticks is :0x%x\n", boot_tick); if (platform->time_init) platform->time_init(); #ifdef CONFIG_VIRT extern int arch_vtimer_init(uint32_t virtual_irq, uint32_t phy_irq); arch_vtimer_init(timer_info[VIRT_TIMER].irq, timer_info[NONSEC_PHY_TIMER].irq); #endif return 0; } static int timer_interrupt_handler(uint32_t irq, void *data) { extern void soft_timer_interrupt(void); unsigned long ctl; ctl = read_sysreg(ARM64_CNTSCHED_CTL); ctl |= CNT_CTL_IMASK; // disable the interrupt. write_sysreg(ctl, ARM64_CNTSCHED_CTL); soft_timer_interrupt(); return 0; } static int __init_text timers_init(void) { struct armv8_timer_info *sched_timer_info = NULL; #ifdef CONFIG_VIRT struct armv8_timer_info *info; extern int virtual_timer_irq_handler(uint32_t irq, void *data); write_sysreg64(0, CNTVOFF_EL2); /* el1/el0 can read CNTPCT_EL0 */ write_sysreg32(1 << 0, CNTHCTL_EL2); /* disable hyper and phy timer */ write_sysreg32(0, CNTP_CTL_EL0); write_sysreg32(0, CNTHP_CTL_EL2); isb(); info = &timer_info[VIRT_TIMER]; if (info->irq) { request_irq(info->irq, virtual_timer_irq_handler, info->flags & 0xf, "virt timer irq", NULL); } sched_timer_info = &timer_info[HYP_TIMER]; #else sched_timer_info = &timer_info[VIRT_TIMER]; #endif ASSERT(sched_timer_info && sched_timer_info->irq); request_irq(sched_timer_info->irq, timer_interrupt_handler, sched_timer_info->flags & 0xf, "sched_timer_int", NULL); return 0; } subsys_initcall_percpu(timers_init); subsys_initcall(timers_arch_init); ================================================ FILE: kernel/arch/aarch64/core/asm-offset.c ================================================ /* * Copyright (C) 2019 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #define __NO_STUBS 1 int main(void) { DEFINE(TASK_INFO_SIZE, sizeof(struct task_info)); DEFINE(TASK_INFO_FLAGS_OFFSET, offsetof(struct task_info, flags)); DEFINE(PCPU_SIZE, sizeof(struct pcpu)); DEFINE(PCPU_ID_OFFSET, offsetof(struct pcpu, pcpu_id)); DEFINE(PCPU_STACK_OFFSET, offsetof(struct pcpu, stack)); DEFINE(TASK_SIZE, sizeof(struct task)); DEFINE(TASK_STACK_OFFSET, offsetof(struct task, stack_base)); DEFINE(PCPU_CURRENT_TASK, offsetof(struct pcpu, running_task)); DEFINE(GP_REGS_SIZE, sizeof(gp_regs)); DEFINE(TASK_USER_REGS_OFFSET, offsetof(struct task, user_regs)); return 0; } ================================================ FILE: kernel/arch/aarch64/core/boot.S ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include .section __start_up, "ax" .balign 4 .macro clear_ttbr0 #ifdef CONFIG_VIRT #ifdef CONFIG_ARM_VHE msr ARM64_TTBR0, xzr #endif tlbi alle2 #else msr ARM64_TTBR0, xzr tlbi vmalle1 #endif .endm // current task is store in the x18 .macro set_percpu_idle_task tsk, tsk_size, cpuid ldr \tsk, =idle_tasks ldr \tsk_size, =TASK_SIZE mul \tsk_size, \tsk_size, \cpuid add \tsk, \tsk, \tsk_size mov x18, \tsk .endm // TPIDR_ELx will store the pcpu data .macro set_percpu_pcpu pcpu, pcpu_size, cpuid ldr \pcpu, =pcpus ldr \pcpu_size, =PCPU_SIZE mul \pcpu_size, \pcpu_size, \cpuid add \pcpu, \pcpu, \pcpu_size msr ARM64_TPIDR, \pcpu asm_vtop \pcpu str x19, [\pcpu, #PCPU_ID_OFFSET] .endm .global _start .type _start, "function" _start: /* interrupt disabled mmu/dcache/icache off */ msr daifset, #2 b do_start .quad 0 /* Image load offset from start of RAM */ .quad __code_end - _start /* reserved */ .quad 8 /* kernel flags */ .quad 0 /* reserved */ .quad 0 /* reserved */ .quad 0 /* reserved */ .byte 0x41 /* Magic number, "ARM\x64" */ .byte 0x52 .byte 0x4d .byte 0x64 .long 0x0 do_start: mov x27, x0 // save the dtb blob to x27 // see which level we are now, minos currently can // support running on EL1 and EL2 but can not run // on EL3. mrs x0, CurrentEl mov x1, x0, lsr #2 and x1, x1, #3 // can not run on EL3. cmp x1, #3 b.eq minos_panic mov x26, #0 cmp x1, #2 #ifdef CONFIG_VIRT b.ne minos_panic mov x0, 0 msr HCR_EL2, x0 isb #ifdef CONFIG_ARM_VHE // if VHE is enabled, check whether the soc has // implement this feature and enable it mrs x0, ID_AA64MMFR1_EL1 ubfx x1, x0, #8, #4 cbz x1, minos_panic mrs x0, HCR_EL2 ldr x1, =HCR_EL2_E2H orr x0, x0, x1 msr HCR_EL2, x0 isb #endif #else // if current EL is not EL2 then jump to EL1 // directly, othewise set 1 to x26 indicate that // need to jump to EL1 b.ne minos_os_start mov x26, #1 mov x0, 0 msr HCR_EL2, x0 isb #endif // CONFIG_VIRT // init the EL2 env mrs x0, midr_el1 mrs x1, mpidr_el1 msr vpidr_el2, x0 msr vmpidr_el2, x1 /* * neither EL3 nor EL2 trap floating point or * accesses to CPACR */ ldr x0, =0x300000 msr CPTR_EL2, x0 msr VTTBR_EL2, xzr dsb sy isb cmp x26, #1 b.ne minos_os_start // drop to EL1 mov x1, #15 msr ICC_SRE_EL1, x1 mrs x1, HCR_EL2 mov x2, #HCR_EL2_RW orr x2, x2, x1 msr HCR_EL2, x2 isb msr CPTR_EL2, xzr mov x1, #0 msr SCTLR_EL2, x1 mov x1, #3 msr CNTHCTL_EL2, x1 dsb sy isb drop_to_el1: adr x1, minos_os_start msr ELR_EL2, x1 mov x1, #(AARCH64_SPSR_EL1h | AARCH64_SPSR_F | AARCH64_SPSR_I | AARCH64_SPSR_A) msr SPSR_EL2, x1 eret dsb nsh isb minos_os_start: adr x1, _start // entry address must 4K align and x0, x1, #0xfff cbnz x0, minos_panic // minos_start to __code_start is the reserve memory // for minos ldr x3, =0xffffffffffe00000 and x1, x1, x3 adr x2, minos_start str x1, [x2] bl arch_raw_smp_processor_id // cpuid save to x19 mov x19, x0 /* using current EL stack register */ msr spsel, #1 dsb nsh isb ldr x1, =elx_vectors msr ARM64_VBAR, x1 /* enable Abort now for early system abort */ msr daifclr, #4 dsb nsh isb /* invalid the dcache and flush the tlb */ bl inv_dcache_all dsb sy isb #ifdef CONFG_VIRT tlbi alle2 #else tlbi vmalle1 #endif dsb sy isb ldr x1, =ARM64_SCTLR_VALUE msr ARM64_SCTLR, x1 dsb nsh isb /* setup the el2 page table */ ldr x1, = __stage1_page_table asm_vtop x1 msr ARM64_TTBR0, x1 msr ARM64_TTBR1, x1 dsb nsh isb /* * 0xff440c0400 * MT_DEVICE_NGNRNE 0 * MT_DEVICE_NGNRE 1 * MT_DEVICE_GRE 2 * MT_NORMAL_NC 3 * MT_NORMAL 4 * 0x00 - MT_DEVICE_NGNRNE * 0x04 - MT_DEVICE_NGNRE * 0x0c - MT_DEVICE_GRE * 0x44 - MT_NORMAL_NC * 0xff - MT_NORMAL * 0xbb - MT_NORMAL_WT */ ldr x1, =0xbbff440c0400 msr ARM64_MAIR, x1 /* get the physical address range */ mrs x0, ID_AA64MMFR0_EL1 and x0, x0, #0xf #if defined(CONFIG_VIRT) && !defined(CONFIG_ARM_VHE) mov x2, x0, lsl #16 ldr x3, =0xfffffffffff8ffff #else mov x2, x0, lsl #32 ldr x3, =0xfffffff8ffffffff #endif ldr x1, =ARM64_TCR_VALUE // load the value of TCR. and x1, x1, x3 // update the IPA/PA for TCR_ELx. orr x1, x1, x2 msr ARM64_TCR, x1 // set the TCR_ELx. dsb sy isb // idle task is in Current_EL mov x1, #ARM64_SPSR_VALUE msr ARM64_SPSR, x1 dsb nsh cbnz x19, secondary_start_up ldr x0, =__minos_end asm_vtop x0 adr x1, minos_stack_top adr x2, minos_bootmem_base adr x3, minos_stack_bottom // store the bootmem base add x0, x0, #4095 mov x4, #4095 mvn x5, x4 and x0, x0, x5 str x0, [x2] // store the minos stack bottom str x0, [x3] // store the minos stack top mov x4, #CONFIG_TASK_STACK_SIZE mov x5, #CONFIG_NR_CPUS mul x5, x4, x5 add x0, x0, x5 str x0, [x1] sub x0, x0, x19, lsl #CONFIG_TASK_STACK_SHIFT asm_ptov x0 mov sp, x0 ldr x0, =__bss_start asm_vtop x0 mov x1, #0 ldr x2, =__bss_end asm_vtop x2 sub x2, x2, x0 bl memset ldr x0, =__percpu_start asm_vtop x0 mov x1, #0 ldr x2, =__percpu_end asm_vtop x2 sub x2, x2, x0 bl memset // map the boot memory when booting bl map_boot_mem dsb ishst isb // current task is store in the x18 // x18 will store the current task pointer set_percpu_idle_task x0, x1, x19 // TPIDR_ELx will store the pcpu data set_percpu_pcpu x0, x1, x19 ldr x26, =mmu_on // enable the mmu and disable the aligment check mrs x1, ARM64_SCTLR orr x1, x1, #SCTLR_ELx_M orr x1, x1, #SCTLR_ELx_C orr x1, x1, #SCTLR_ELx_I bic x1, x1, #SCTLR_ELx_SA bic x1, x1, #SCTLR_ELx_A msr ARM64_SCTLR, x1 dsb sy isb br x26 mmu_on: ic ialluis dsb sy isb clear_ttbr0 dsb sy isb mov x0, x27 // restore the dtb address. bl arch_main // never return. dead_loop: b dead_loop secondary_start_up: // setup the idle task stack adr x0, minos_stack_top ldr x0, [x0] sub x0, x0, x19, lsl #CONFIG_TASK_STACK_SHIFT asm_ptov x0 mov sp, x0 // current task is store in the x18 // x18 will store the current task pointer set_percpu_idle_task x0, x1, x19 // TPIDR_ELx will store the pcpu data set_percpu_pcpu x0, x1, x19 ldr x1, =ARM64_SCTLR_VALUE msr ARM64_SCTLR, x1 dsb sy isb ldr x26, =mmu_on_secondary // enable the dcache and the icache mrs x1, ARM64_SCTLR orr x1, x1, #SCTLR_ELx_C orr x1, x1, #SCTLR_ELx_I orr x1, x1, #SCTLR_ELx_M bic x1, x1, #SCTLR_ELx_SA bic x1, x1, #SCTLR_ELx_A msr ARM64_SCTLR, x1 dsb sy isb br x26 mmu_on_secondary: ic ialluis dsb sy isb clear_ttbr0 ldr x1, =__smp_affinity_id add x1, x1, x19, lsl #3 mrs x2, MPIDR_EL1 ldr x4, =0x000000ff00ffffff and x2, x2, x4 str x2, [x1] dsb sy isb /* here wait for boot cpu finish tht init work */ /* pass the cpuid to the boot_secondary */ mov x0, x19 bl boot_secondary nop minos_panic: nop b minos_panic .data .global minos_start .global minos_bootmem_base .global minos_stack_top .global minos_stack_bottom .global minos_end .balignl 16, 0xdeadbeef minos_start: .quad 0x0 minos_bootmem_base: .quad 0x0 minos_stack_top: .quad 0x0 minos_stack_bottom: .quad 0x0 minos_end: .quad 0x0 ================================================ FILE: kernel/arch/aarch64/core/cache.S ================================================ /* * Copyright (C) 2001 Deep Blue Solutions Ltd. * Copyright (C) 2012 ARM Ltd. * Copyright (C) 1996-2000 Russell King * Copyright (C) 2012 ARM Ltd. * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include .global flush_dcache_range .global inv_dcache_range .global inv_dcache_all .global flush_dcache_all .global flush_cache_all .macro dcache_line_size reg, tmp mrs \tmp, ctr_el0 ubfx \tmp, \tmp, #16, #4 mov \reg, #4 lsl \reg, \reg, \tmp .endm .macro icache_line_size reg, tmp mrs \tmp, ctr_el0 and \tmp, \tmp, #0xf mov \reg, #4 lsl \reg, \reg, \tmp .endm .macro do_dcache_maintenance_by_mva op /* Exit early if size is zero */ cbz x1, exit_loop_\op dcache_line_size x2, x3 add x1, x0, x1 sub x3, x2, #1 bic x0, x0, x3 loop_\op: dc \op, x0 add x0, x0, x2 cmp x0, x1 b.lo loop_\op dsb sy exit_loop_\op: ret .endm func flush_dcache_range do_dcache_maintenance_by_mva civac endfunc flush_dcache_range func inv_dcache_range do_dcache_maintenance_by_mva ivac endfunc inv_dcache_range func inv_dcache_all // From the ARM ARMv8-A Architecture Reference Manual dmb ish // ensure all prior inner-shareable accesses have been observed mrs x0, CLIDR_EL1 and w3, w0, #0x07000000 // get 2 x level of coherence lsr w3, w3, #23 cbz w3, finished mov w10, #0 // w10 = 2 x cache level mov w8, #1 // w8 = constant 0b1 loop_level: add w2, w10, w10, lsr #1 // calculate 3 x cache level lsr w1, w0, w2 // extract 3-bit cache type for this level and w1, w1, #0x7 cmp w1, #2 b.lt next_level // no data or unified cache at this level msr CSSELR_EL1, x10 // select this cache level isb // synchronize change of csselr mrs x1, CCSIDR_EL1 // read ccsidr and w2, w1, #7 // w2 = log2(linelen)-4 add w2, w2, #4 // w2 = log2(linelen) ubfx w4, w1, #3, #10 // w4 = max way number, right aligned clz w5, w4 // w5 = 32-log2(ways), bit position of way in dc operand lsl w9, w4, w5 // w9 = max way number, aligned to position in dc operand lsl w16, w8, w5 // w16 = amount to decrement way number per iteration loop_way: ubfx w7, w1, #13, #15 // w7 = max set number, right aligned lsl w7, w7, w2 // w7 = max set number, aligned to position in dc operand lsl w17, w8, w2 // w17 = amount to decrement set number per iteration loop_set: orr w11, w10, w9 // w11 = combine way number and cache number ... orr w11, w11, w7 // ... and set number for dc operand dc isw, x11 // do data cache invalidate by set and way subs w7, w7, w17 // decrement set number b.ge loop_set subs x9, x9, x16 // decrement way number b.ge loop_way next_level: add w10, w10, #2 // increment 2 x cache level cmp w3, w10 b.gt loop_level dsb sy // ensure completion of previous cache maintenance operation isb finished: ret endfunc inv_dcache_all func flush_dcache_all dmb sy // ensure ordering with previous memory accesses mrs x0, clidr_el1 // read clidr and x3, x0, #0x7000000 // extract loc from clidr lsr x3, x3, #23 // left align loc bit field cbz x3, finish // if loc is 0, then no need to clean mov x10, #0 // start clean at cache level 0 loop4: add x2, x10, x10, lsr #1 // work out 3x current cache level lsr x1, x0, x2 // extract cache type bits from clidr and x1, x1, #7 // mask of the bits for current cache only cmp x1, #2 // see what cache we have at this level b.lt skip // skip if no cache, or just i-cache mrs x9, daif msr daifset, #2 msr csselr_el1, x10 // select current cache level in csselr isb // isb to sych the new cssr&csidr mrs x1, ccsidr_el1 // read the new ccsidr msr daif, x9 and x2, x1, #7 // extract the length of the cache lines add x2, x2, #4 // add 4 (line length offset) mov x4, #0x3ff and x4, x4, x1, lsr #3 // find maximum number on the way size clz w5, w4 // find bit position of way size increment mov x7, #0x7fff and x7, x7, x1, lsr #13 // extract max number of the index size loop2: mov x9, x4 // create working copy of max way size loop3: lsl x6, x9, x5 orr x11, x10, x6 // factor way and cache number into x11 lsl x6, x7, x2 orr x11, x11, x6 // factor index number into x11 dc cisw, x11 // clean & invalidate by set/way subs x9, x9, #1 // decrement the way b.ge loop3 subs x7, x7, #1 // decrement the index b.ge loop2 skip: add x10, x10, #2 // increment cache number cmp x3, x10 b.gt loop4 finish: mov x10, #0 // swith back to cache level 0 msr csselr_el1, x10 // select current cache level in csselr dsb sy isb ret endfunc flush_dcache_all /* * flush_cache_all() * * Flush the entire cache system. The data cache flush is now achieved * using atomic clean / invalidates working outwards from L1 cache. This * is done using Set/Way based cache maintainance instructions. The * instruction cache can still be invalidated back to the point of * unification in a single instruction. */ func flush_cache_all mov x12, x30 bl flush_dcache_all mov x0, #0 ic ialluis // I+BTB cache invalidate ret x12 endfunc flush_cache_all ================================================ FILE: kernel/arch/aarch64/core/cpu.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include extern unsigned char __smp_affinity_id; extern phy_addr_t smp_holding_address[CONFIG_NR_CPUS]; static inline unsigned long psci_fn(uint32_t id, unsigned long a1, unsigned long a2, unsigned long a3) { struct arm_smc_res res; smc_call(id, a1, a2, a3, 0, 0, 0, 0, &res); return res.a0; } static inline unsigned long psci_fn_hvc(uint32_t id, unsigned long a1, unsigned long a2, unsigned long a3) { struct arm_smc_res res; hvc_call(id, a1, a2, a3, 0, 0, 0, 0, &res); return res.a0; } int psci_cpu_on(unsigned long cpu, unsigned long entry) { return (int)psci_fn(PSCI_0_2_FN_CPU_ON, cpu, entry, 0); } int psci_cpu_off(unsigned long cpu) { return (int)psci_fn(PSCI_0_2_FN_CPU_OFF, cpu, 0, 0); } void psci_system_reboot(int mode, const char *cmd) { psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0); } void psci_system_shutdown(void) { psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0); } int psci_cpu_on_hvc(unsigned long cpu, unsigned long entry) { return (int)psci_fn_hvc(PSCI_0_2_FN_CPU_ON, cpu, entry, 0); } int psci_cpu_off_hvc(unsigned long cpu) { return (int)psci_fn_hvc(PSCI_0_2_FN_CPU_OFF, cpu, 0, 0); } void psci_system_reboot_hvc(int mode, const char *cmd) { psci_fn_hvc(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0); } void psci_system_shutdown_hvc(void) { psci_fn_hvc(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0); } int spin_table_cpu_on(unsigned long affinity, unsigned long entry) { void *addr; int cpu = affinity_to_cpuid(affinity); if (smp_holding_address[cpu] != 0) { addr = io_remap(smp_holding_address[cpu], sizeof(uint64_t)); *(unsigned long *)addr = entry; /* flush the cache and send signal to other cpu */ flush_dcache_range((unsigned long)addr, PAGE_SIZE); sev(); io_unmap((virt_addr_t)addr, sizeof(unsigned long)); return 0; } return -EINVAL; } ================================================ FILE: kernel/arch/aarch64/core/cpu_feature.c ================================================ /* * Copyright (C) 2020 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #define CPU_FEATURE_BITS 128 #define CPU_FEATURE_VHE 0 static DEFINE_PER_CPU(unsigned long *, cpu_feature); int cpu_has_feature(int feature) { int ret; unsigned long *cf; if (feature >= CPU_FEATURE_BITS) return 0; cf = get_cpu_data(cpu_feature); ret = test_bit(feature, cf); put_cpu_data(cpu_feature); return ret; } int cpu_has_vhe(void) { static int vhe_enable = -1; if (vhe_enable == -1) { vhe_enable = cpu_has_feature(CPU_FEATURE_VHE); } return vhe_enable; } static int arch_cpu_feature_init(void) { unsigned long *cf; uint64_t value; cf = malloc(BITS_TO_LONGS(CPU_FEATURE_BITS)); if (!cf) panic("can not allocate memory for cpu feature\n"); memset(cf, 0, BITMAP_SIZE(CPU_FEATURE_BITS)); get_cpu_var(cpu_feature) = cf; value = read_mpidr_el1(); if (value & MPIDR_EL1_MT) set_bit(ARM_FEATURE_MPIDR_SHIFT, cf); return 0; } arch_initcall_percpu(arch_cpu_feature_init); ================================================ FILE: kernel/arch/aarch64/core/entry.S ================================================ /* * Copyright (C) 2004-2017 ARM Ltd. All rights reserved. * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include // .align n = align 2^n // balign 0x80 = align with 0x80 boundary. .global elx_vectors .section __el2_vectors, "ax" .align 12 .macro BAD_MODE mode stp x29, x30, [sp, #-16] mov x29, #\mode b __bad_mode .endm elx_vectors: c0sync: // Current EL with SP0 BAD_MODE VECTOR_C0_SYNC .balign 0x80 c0irq: BAD_MODE VECTOR_C0_IRQ .balign 0x80 c0fiq: BAD_MODE VECTOR_C0_FIQ .balign 0x80 c0serr: BAD_MODE VECTOR_C0_SERR .balign 0x80 // Current EL with SPx cxsync: b __sync_exception_from_current_el .balign 0x80 cxirq: b __irq_exception_from_current_el .balign 0x80 cxfiq: BAD_MODE VECTOR_CX_FIQ .balign 0x80 cxserr: BAD_MODE VECTOR_CX_SERR .balign 0x80 //Lower EL using AArch64 l64sync: b __sync_exception_from_lower_el .balign 0x80 l64irq: b __irq_exception_from_lower_el .balign 0x80 l64fiq: BAD_MODE VECTOR_L64_FIQ .balign 0x80 l64serr: BAD_MODE VECTOR_L64_SERR .balign 0x80 // Lower EL using AArch32 l32sync: b __sync_exception_from_lower_el .balign 0x80 l32irq: b __irq_exception_from_lower_el .balign 0x80 l32fiq: BAD_MODE VECTOR_L32_FIQ .balign 0x80 l32serr: BAD_MODE VECTOR_L32_SERR .balign 0x80 ================================================ FILE: kernel/arch/aarch64/core/fpsimd.c ================================================ /* * Copyright (C) 2020 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include void fpsimd_state_save(struct task *task, struct fpsimd_context *c) { #ifdef CONFIG_VIRT if (task_is_vcpu(task) && task_is_32bit(task)) c->fpexc32_el2 = read_sysreg32(FPEXC32_EL2); #endif c->fpsr = read_sysreg32(FPSR); c->fpcr = read_sysreg32(FPCR); asm volatile("stp q0, q1, [%1, #16 * 0]\n\t" "stp q2, q3, [%1, #16 * 2]\n\t" "stp q4, q5, [%1, #16 * 4]\n\t" "stp q6, q7, [%1, #16 * 6]\n\t" "stp q8, q9, [%1, #16 * 8]\n\t" "stp q10, q11, [%1, #16 * 10]\n\t" "stp q12, q13, [%1, #16 * 12]\n\t" "stp q14, q15, [%1, #16 * 14]\n\t" "stp q16, q17, [%1, #16 * 16]\n\t" "stp q18, q19, [%1, #16 * 18]\n\t" "stp q20, q21, [%1, #16 * 20]\n\t" "stp q22, q23, [%1, #16 * 22]\n\t" "stp q24, q25, [%1, #16 * 24]\n\t" "stp q26, q27, [%1, #16 * 26]\n\t" "stp q28, q29, [%1, #16 * 28]\n\t" "stp q30, q31, [%1, #16 * 30]\n\t" : "=Q" (*c->regs) : "r" (c->regs)); } void fpsimd_state_restore(struct task *task, struct fpsimd_context *c) { #ifdef CONFIG_VIRT if (task_is_vcpu(task) && task_is_32bit(task)) write_sysreg(c->fpexc32_el2, FPEXC32_EL2); #endif write_sysreg(c->fpsr, FPSR); write_sysreg(c->fpcr, FPCR); asm volatile("ldp q0, q1, [%1, #16 * 0]\n\t" "ldp q2, q3, [%1, #16 * 2]\n\t" "ldp q4, q5, [%1, #16 * 4]\n\t" "ldp q6, q7, [%1, #16 * 6]\n\t" "ldp q8, q9, [%1, #16 * 8]\n\t" "ldp q10, q11, [%1, #16 * 10]\n\t" "ldp q12, q13, [%1, #16 * 12]\n\t" "ldp q14, q15, [%1, #16 * 14]\n\t" "ldp q16, q17, [%1, #16 * 16]\n\t" "ldp q18, q19, [%1, #16 * 18]\n\t" "ldp q20, q21, [%1, #16 * 20]\n\t" "ldp q22, q23, [%1, #16 * 22]\n\t" "ldp q24, q25, [%1, #16 * 24]\n\t" "ldp q26, q27, [%1, #16 * 26]\n\t" "ldp q28, q29, [%1, #16 * 28]\n\t" "ldp q30, q31, [%1, #16 * 30]\n\t" : : "Q" (*c->regs), "r" (c->regs)); } ================================================ FILE: kernel/arch/aarch64/core/mem_map.S ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "stage1.h" #include /* * map the code memory VA->PA, if need to using * dcache need to enable the MMU, first clear * the page table, below is the var defination * Note, this function will call memset, and memset * will use x0 - x4, so need pay attention for x0-x4 * if there some value saved in these register */ page_table .req x24 ttb0_pgd .req x20 ttb0_pud .req x21 ttb0_pmd .req x22 vaddr .req x5 paddr .req x6 size .req x7 pte_attr .req x8 tmp_const .req x9 pud_tmp .req x10 pmd_tmp .req x12 tmp .req x13 pte_value .req x14 pte_index .req x15 entry_size .req x16 entry_align .req x17 entry_mask .req x18 pagetable_base .req x23 // store the pagetable for page allocation .section __start_up, "ax" .balign 4 .global map_boot_mem .macro asm_get_boot_page reg mov \reg, pagetable_base add pagetable_base, pagetable_base, #4096 .endm #define HOST_TABLE_DES (S1_DES_TABLE | S1_TABLE_UAP | S1_TABLE_UXN) map_boot_mem: /* save the lr register */ mov x26, x30 /* get the base address of free memory */ adr pagetable_base, minos_stack_top ldr pagetable_base, [pagetable_base] /* kernel pgd is defined in lds */ mov x1, #0 mov x2, #4096 ldr page_table, = __stage1_page_table asm_vtop page_table mov x0, page_table bl memset // boot memory must at 0 - 4GB space, here will // alloc all the 4GB memory's PUD bl build_page_table // map the reserve memory as rw adr vaddr, minos_start ldr vaddr, [vaddr] ldr tmp, =__code_start asm_vtop tmp sub size, tmp, vaddr cbz size, __map_code_section mov paddr, vaddr ldr pte_attr, =BOOTMEM_DATA_ATTR bl build_normal_pte_table // map the code section rx __map_code_section: ldr vaddr, =__code_start ldr tmp, =__code_end sub size, tmp, vaddr asm_vtop vaddr mov paddr, vaddr ldr pte_attr, =BOOTMEM_CODE_ATTR bl build_normal_pte_table // map the init data section rwx, data section will // be freed after system is bootup ldr vaddr, =__init_start ldr tmp, =__init_end sub size, tmp, vaddr asm_vtop vaddr mov paddr, vaddr ldr pte_attr, =BOOTMEM_INIT_ATTR bl build_normal_pte_table // map the data section ldr vaddr, =__data_start ldr tmp, =__data_end sub size, tmp, vaddr asm_vtop vaddr mov paddr, vaddr ldr pte_attr, =BOOTMEM_DATA_ATTR bl build_normal_pte_table // map the RO data section like symbol and string // RO section is the last section in the dts, so the // the range is from __rodata_start to minos_bootmem_base ldr vaddr, =__rodata_start asm_vtop vaddr adr tmp, minos_bootmem_base ldr tmp, [tmp] sub size, tmp, vaddr mov paddr, vaddr ldr pte_attr, =BOOTMEM_DATA_RO_ATTR bl build_normal_pte_table // map the left 4K pages adr vaddr, minos_bootmem_base ldr vaddr, [vaddr] mov tmp, vaddr ldr entry_mask, =0x1fffff add tmp, tmp, entry_mask mvn entry_align, entry_mask and tmp, tmp, entry_align mov paddr, vaddr sub size, tmp, vaddr ldr pte_attr, =BOOTMEM_DATA_ATTR bl build_normal_pte_table // map the UART memory for early log rw-device ldr vaddr, =CONFIG_UART_BASE mov paddr, vaddr ldr size, =CONFIG_UART_IO_SIZE ldr pte_attr, =BOOTMEM_IO_ATTR bl build_io_pte_table // update the pagetable base adr x0, minos_end str pagetable_base, [x0] ret x26 build_normal_pmd_table: mov x25, x30 ldr entry_mask, =0x1fffff ldr entry_align, =0xffe00000 ret x25 build_io_pte_table: mov x25, x30 ldr entry_size, =0x1000 ldr entry_mask, =0xfff ldr entry_align, =0xfffffffffffff000 // alloc one page to map 2M IO pmd mov x0, pagetable_base add pagetable_base, pagetable_base, #4096 mov pmd_tmp, x0 mov x1, #0 mov x2, #4096 bl memset // if the memory region is not in a 2M range ? add x1, vaddr, size add x1, x1, entry_mask and x1, x1, entry_align and vaddr, vaddr, entry_align and paddr, paddr, entry_align sub size, x1, vaddr ubfx x0, vaddr, #21, #11 ldr x1, =HOST_TABLE_DES orr x1, pmd_tmp, x1 str x1, [ttb0_pud, x0, lsl #3] ldr entry_align, =0x1fffff and x2, vaddr, entry_align lsr x2, x2, #12 orr paddr, paddr, pte_attr loop_io_pte: str paddr, [pmd_tmp, x2, lsl #3] sub size, size, entry_size add paddr, paddr, entry_size add x2, x2, #1 cbnz size, loop_io_pte ret x25 build_normal_pte_table: ldr entry_size, =0x1000 ldr entry_mask, =0xfff ldr entry_align, =0xfffffffffffff000 // the va and pa must page align, so the info // from lds must correct and vaddr, vaddr, entry_align and paddr, paddr, entry_align and size, size, entry_align // get the va offset in bootmem adr x0, minos_start ldr x0, [x0] sub x0, vaddr, x0 ubfx pte_index, x0, #12, #20 mov pmd_tmp, ttb0_pmd add pmd_tmp, pmd_tmp, pte_index, lsl #3 bic paddr, paddr, entry_mask bic paddr, paddr, #0xffff000000000000 orr paddr, paddr, pte_attr loop_normal_pte: cbz size, exit_loop str paddr, [pmd_tmp] sub size, size, entry_size add pmd_tmp, pmd_tmp, #8 add paddr, paddr, entry_size b loop_normal_pte exit_loop: ret build_page_table: mov x25, x30 // map first 4GB for minos boot memory, here // need 4 pages mov x3, #4 mov ttb0_pud, pagetable_base ldr x2, =(4096 << 2) add pagetable_base, pagetable_base, x2 mov x0, ttb0_pud mov x1, #0 bl memset mov vaddr, #0x0 mov entry_size, #4 mov tmp, ttb0_pud mov pte_index, 0 ldr x1, =HOST_TABLE_DES loop_pud: orr pte_value, tmp, x1 str pte_value, [page_table, pte_index, lsl #3] add pte_index, pte_index, #1 add tmp, tmp, #0x1000 sub entry_size, entry_size, #1 cbnz entry_size, loop_pud // count how many memory is mapped as 4K page // other memory will mapped as PMD block adr vaddr, minos_bootmem_base ldr vaddr, [vaddr] mov tmp, vaddr ldr entry_mask, =0x1fffff add tmp, tmp, entry_mask mvn entry_align, entry_mask and tmp, tmp, entry_align adr pte_value, minos_start ldr pte_value, [pte_value] // size record the 4K page size // tmp_const record the 2M block start address mov tmp_const, tmp sub size, tmp, pte_value lsr x3, size, #21 lsl x2, x3, #12 mov ttb0_pmd, pagetable_base add pagetable_base, pagetable_base, x2 mov x0, ttb0_pmd mov x1, #0 bl memset // minos_start address 2M align minos_start // will set at boot stage correctly adr vaddr, minos_start ldr vaddr, [vaddr] lsr pte_index, vaddr, #21 mov tmp, ttb0_pmd ldr x1, =HOST_TABLE_DES loop_pmd: orr pte_value, tmp, x1 str pte_value, [ttb0_pud, pte_index, lsl #3] add pte_index, pte_index, #1 add tmp, tmp, #4096 sub x3, x3, #1 cbnz x3, loop_pmd // continue map the pmd block memory ldr x2, =CONFIG_MINOS_RAM_SIZE sub size, x2, size lsr size, size, #21 ldr entry_mask, =0x200000 mov paddr, tmp_const ldr pte_attr, =BOOTMEM_DATA_BLK_ATTR loop_pmd_blk: orr pte_value, paddr, pte_attr str pte_value, [ttb0_pud, pte_index, lsl #3] add pte_index, pte_index, #1 add paddr, paddr, entry_mask sub size, size, #1 cbnz size, loop_pmd_blk ret x25 ================================================ FILE: kernel/arch/aarch64/core/stage1.c ================================================ /* * Copyright (C) 2020 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "stage1.h" /* * 40bit stage1 IPA use 3 levels page table, the pagetable * need 2 pages */ #define S1_PGTABLE_LEVELS 3 #define S1_PAGETABLE_SIZE 4096 #define S1_PGD_SHIFT 39 #define S1_PGD_SIZE (1UL << S1_PGD_SHIFT) #define S1_PGD_MASK (~(S1_PGD_SIZE - 1)) #define S1_PUD_SHIFT 30 #define S1_PUD_SIZE (1UL << S1_PUD_SHIFT) #define S1_PUD_MASK (~(S1_PUD_SIZE - 1)) #define S1_PMD_SHIFT 21 #define S1_PMD_SIZE (1UL << S1_PMD_SHIFT) #define S1_PMD_MASK (~(S1_PMD_SIZE - 1)) #define S1_PTE_SHIFT 12 #define S1_PTE_SIZE (1UL << S1_PTE_SHIFT) #define S1_PTE_MASK (~(S1_PTE_SIZE - 1)) #define S1_PHYSICAL_MASK 0x0000fffffffff000UL #define S1_PHYSICAL_MAX (1UL << 40) #define S1_VIRT_MAX (1UL << 39) /* * The number of PTRS across all concatenated stage1 tables given by the * number of bits resolved at the initial level. * * since we use 3 level pagetable, and translation walk will from pud, the * PTRS in one pud is 1024 */ #define PTRS_PER_S1_PGD 512 #define PTRS_PER_S1_PUD 512 #define PTRS_PER_S1_PMD 512 #define PTRS_PER_S1_PTE 512 #define stage1_pud_value(pudp) (*(pudp)) #define stage1_pmd_value(pmdp) (*(pmdp)) #define stage1_pte_value(ptep) (*(ptep)) #define stage1_pgd_index(addr) (((addr) >> S1_PGD_SHIFT) & (PTRS_PER_S1_PGD - 1)) #define stage1_pud_index(addr) (((addr) >> S1_PUD_SHIFT) & (PTRS_PER_S1_PUD - 1)) #define stage1_pmd_index(addr) (((addr) >> S1_PMD_SHIFT) & (PTRS_PER_S1_PMD - 1)) #define stage1_pte_index(addr) (((addr) >> S1_PTE_SHIFT) & (PTRS_PER_S1_PTE - 1)) #define stage1_pgd_offset(pgdp, addr) ((pgd_t *)(pgdp) + stage1_pgd_index((unsigned long)addr)) #define stage1_pud_offset(pudp, addr) ((pud_t *)(pudp) + stage1_pud_index((unsigned long)addr)) #define stage1_pmd_offset(pmdp, addr) ((pmd_t *)(pmdp) + stage1_pmd_index((unsigned long)addr)) #define stage1_pte_offset(ptep, addr) ((pte_t *)(ptep) + stage1_pte_index((unsigned long)addr)) #define stage1_pud_huge(pud) ((pud) && ((pud) & 0x03) == S1_DES_BLOCK) #define stage1_pmd_huge(pmd) ((pmd) && ((pmd) & 0x03) == S1_DES_BLOCK) #define stage1_pud_table_addr(pgd) (pud_t *)(unsigned long)((pgd) & S1_PHYSICAL_MASK) #define stage1_pmd_table_addr(pud) (pmd_t *)(unsigned long)((pud) & S1_PHYSICAL_MASK) #define stage1_pte_table_addr(pmd) (pte_t *)(unsigned long)((pmd) & S1_PHYSICAL_MASK) #define stage1_pgd_none(pgd) ((pgd) == 0) #define stage1_pud_none(pud) ((pud) == 0) #define stage1_pmd_none(pmd) ((pmd) == 0) #define stage1_pte_none(pte) ((pte) == 0) #define stage1_phy_pte(pte) (void *)((pte) & S1_PHYSICAL_MASK) #define stage1_phy_pmd(pmd) (void *)((pmd) & S1_PHYSICAL_MASK) static void inline flush_tlb_va_range(unsigned long va, size_t size) { flush_tlb_va_host(va, size); } static inline void flush_dcache_pte(unsigned long addr) { flush_dcache_range(addr, PAGE_SIZE); } static inline void flush_dcache_pmd(unsigned long addr) { flush_dcache_range(addr, S1_PMD_SIZE); } static void inline stage1_pgd_clear(pud_t *pgdp) { WRITE_ONCE(*pgdp, 0); __dsb(ishst); isb(); } static void inline stage1_pud_clear(pud_t *pudp) { WRITE_ONCE(*pudp, 0); __dsb(ishst); isb(); } static void inline stage1_pmd_clear(pmd_t *pmdp) { WRITE_ONCE(*pmdp, 0); __dsb(ishst); isb(); } static void *stage1_get_free_page(unsigned long flags) { return get_free_page(GFP_KERNEL); } static unsigned long stage1_xxx_addr_end(unsigned long start, unsigned long end, size_t map_size) { unsigned long boundary = (start + map_size) & ~((unsigned long)map_size - 1); return ((boundary - 1) < (end - 1)) ? boundary : end; } #define stage1_pgd_addr_end(start, end) \ stage1_xxx_addr_end(start, end, S1_PGD_SIZE) #define stage1_pud_addr_end(start, end) \ stage1_xxx_addr_end(start, end, S1_PUD_SIZE) #define stage1_pmd_addr_end(start, end) \ stage1_xxx_addr_end(start, end, S1_PMD_SIZE) static inline void stage1_set_pte(pte_t *ptep, pte_t new_pte) { WRITE_ONCE(*ptep, new_pte); __dsb(ishst); } static inline void stage1_set_pmd(pmd_t *pmdp, pmd_t new_pmd) { WRITE_ONCE(*pmdp, new_pmd); __dsb(ishst); } static inline void stage1_set_pud(pud_t *pudp, pud_t new_pud) { WRITE_ONCE(*pudp, new_pud); __dsb(ishst); } static inline void stage1_set_pgd(pgd_t *pgdp, pgd_t new_pgd) { WRITE_ONCE(*pgdp, new_pgd); __dsb(ishst); } static inline void stage1_pgd_populate(pgd_t *pgdp, unsigned long addr, unsigned long flags) { uint64_t attrs = S1_DES_TABLE; if (flags & VM_HOST) attrs |= S1_TABLE_UAP | S1_TABLE_UXN; stage1_set_pgd(pgdp, vtop(addr) | attrs); } static inline void stage1_pud_populate(pud_t *pudp, unsigned long addr, unsigned long flags) { uint64_t attrs = S1_DES_TABLE; if (flags & VM_HOST) attrs |= S1_TABLE_UAP | S1_TABLE_UXN; stage1_set_pud(pudp, vtop(addr) | attrs); } static inline void stage1_pmd_populate(pmd_t *pmdp, unsigned long addr, unsigned long flags) { uint64_t attrs = S1_DES_TABLE; if (flags & VM_HOST) attrs |= S1_TABLE_UAP | S1_TABLE_UXN; stage1_set_pmd(pmdp, vtop(addr) | attrs); } static inline pmd_t stage1_pmd_attr(unsigned long phy, unsigned long flags) { pmd_t pmd = phy & S1_PMD_MASK; switch (flags & VM_TYPE_MASK) { case __VM_NORMAL_NC: pmd |= S1_BLOCK_NORMAL_NC; break; case __VM_IO: pmd |= S1_BLOCK_DEVICE; break; break; case __VM_WT: pmd |= S1_BLOCK_WT; break; default: pmd |= S1_BLOCK_NORMAL; break; } if (flags & __VM_HOST) { if ((flags & VM_RW_MASK) == __VM_RO) pmd |= S1_AP_RO; else pmd |= S1_AP_RW; } else { pmd |= S1_nG; if ((flags & VM_RW_MASK) == __VM_RO) pmd |= S1_AP_RO_URO; else pmd |= S1_AP_RW_URW; } if (!(flags & __VM_EXEC)) pmd |= (S1_XN | S1_PXN); if (flags & __VM_PFNMAP) pmd |= S1_PFNMAP; if (flags & __VM_DEVMAP) pmd |= S1_DEVMAP; if (flags & (__VM_SHARED | __VM_PMA)) pmd |= S1_SHARED; return pmd; } static inline pte_t stage1_pte_attr(unsigned long phy, unsigned long flags) { pte_t pte = phy & S1_PTE_MASK; switch (flags & VM_TYPE_MASK) { case __VM_NORMAL_NC: pte |= S1_PAGE_NORMAL_NC; break; case __VM_IO: pte |= S1_PAGE_DEVICE; break; case __VM_WT: pte |= S1_PAGE_WT; break; default: pte |= S1_PAGE_NORMAL; break; } if (flags & __VM_HOST) { if ((flags & VM_RW_MASK) == __VM_RO) pte |= S1_AP_RO; else pte |= S1_AP_RW; } else { pte |= S1_nG; if ((flags & VM_RW_MASK) == __VM_RO) pte |= S1_AP_RO_URO; else pte |= S1_AP_RW_URW; } if (!(flags & __VM_EXEC)) pte |= (S1_XN | S1_PXN); if (flags & __VM_PFNMAP) pte |= S1_PFNMAP; if (flags & __VM_DEVMAP) pte |= S1_DEVMAP; if (flags & (__VM_SHARED | __VM_PMA)) pte |= S1_SHARED; return pte; } static void add_release_page(struct vspace *vs, unsigned long addr) { struct page *page = addr_to_page(addr); ASSERT(page != NULL); page->next = vs->release_pages; vs->release_pages = page; } static void stage1_unmap_pte_range(struct vspace *vs, pte_t *ptep, unsigned long addr, unsigned long end, int flags) { pte_t *pte; pte = stage1_pte_offset(ptep, addr); do { if (!stage1_pte_none(*pte)) { pte_t old_pte = *pte; stage1_set_pte(pte, 0); /* pfnmap and shared page don not free the page */ if (!(old_pte & S1_PFNMAP) && !(old_pte & S1_SHARED)) add_release_page(vs, ptov(stage1_phy_pte(old_pte))); } } while (pte++, addr += PAGE_SIZE, addr != end); } static void stage1_unmap_pmd_range(struct vspace *vs, pmd_t *pmdp, unsigned long addr, unsigned long end, int flags) { unsigned long next; pmd_t *pmd; pte_t *ptep; pmd = stage1_pmd_offset(pmdp, addr); do { next = stage1_pmd_addr_end(addr, end); if (!stage1_pmd_none(*pmd)) { if (stage1_pmd_huge(*pmd)) { pmd_t old_pmd = *pmd; stage1_pmd_clear(pmd); if (!(old_pmd & S1_PFNMAP) && !(old_pmd & S1_SHARED)) add_release_page(vs, ptov(stage1_phy_pte(old_pmd))); } else { ptep = (pte_t *)ptov(stage1_pte_table_addr(*pmd)); stage1_unmap_pte_range(vs, ptep, addr, next, flags); if (next - addr == S1_PMD_SIZE) add_release_page(vs, (unsigned long)ptep); } } } while (pmd++, addr = next, addr != end); } static int stage1_unmap_pud_range(struct vspace *vs, unsigned long addr, unsigned long end, int flags) { unsigned long next; pud_t *pud; pmd_t *pmdp; pud = stage1_pud_offset((pud_t *)vs->pgdp, end); do { next = stage1_pud_addr_end(addr, end); if (!stage1_pud_none(*pud)) { pmdp = (pmd_t *)ptov(stage1_pmd_table_addr(*pud)); stage1_unmap_pmd_range(vs, pmdp, addr, next, flags); if (next - addr == S1_PUD_SIZE) add_release_page(vs, (unsigned long)pmdp); } } while (pud++, addr = next, addr != end); flush_tlb_asid_all(vs->asid); if (vs->notifier_ops && vs->notifier_ops->unmap_range) vs->notifier_ops->unmap_range(vs, addr, end, flags); return 0; } static int stage1_map_pte_range(struct vspace *vs, pte_t *ptep, unsigned long start, unsigned long end, unsigned long physical, unsigned long flags) { unsigned long pte_attr; pte_t *pte; pte_t old_pte; pte = stage1_pte_offset(ptep, start); pte_attr = stage1_pte_attr(0, flags); do { old_pte = *pte; if (old_pte) pr_err("error: pte remaped 0x%x\n", start); stage1_set_pte(pte, pte_attr | physical); } while (pte++, start += PAGE_SIZE, physical += PAGE_SIZE, start != end); return 0; } static inline bool stage1_pmd_huge_page(pmd_t old_pmd, unsigned long start, unsigned long phy, size_t size, unsigned long flags) { if (!(flags & __VM_HUGE_2M) || old_pmd) return false; if (!IS_BLOCK_ALIGN(start) || !IS_BLOCK_ALIGN(phy) || !(IS_BLOCK_ALIGN(size))) return false; return true; } static int stage1_map_pmd_range(struct vspace *vs, pmd_t *pmdp, unsigned long start, unsigned long end, unsigned long physical, unsigned long flags) { unsigned long next; unsigned long attr; pmd_t *pmd; pmd_t old_pmd; pte_t *ptep; size_t size; int ret; pmd = stage1_pmd_offset(pmdp, start); do { next = stage1_pmd_addr_end(start, end); size = next - start; old_pmd = *pmd; /* * virtual memory need to map as PMD huge page */ if (stage1_pmd_huge_page(old_pmd, start, physical, size, flags)) { attr = stage1_pmd_attr(physical, flags); stage1_set_pmd(pmd, attr); } else { if (stage1_pmd_none(old_pmd)) { ptep = (pte_t *)stage1_get_free_page(flags); if (!ptep) return -ENOMEM; memset(ptep, 0, PAGE_SIZE); stage1_pmd_populate(pmd, (unsigned long)ptep, flags); } else { ptep = (pte_t *)ptov(stage1_pte_table_addr(old_pmd)); } ret = stage1_map_pte_range(vs, ptep, start, next, physical, flags); if (ret) return ret; } } while (pmd++, physical += size, start = next, start != end); return 0; } static int stage1_map_pud_range(struct vspace *vs, unsigned long start, unsigned long end, unsigned long physical, unsigned long flags) { unsigned long next; pud_t *pud; pmd_t *pmdp; size_t size; int ret; pud = stage1_pud_offset((pud_t *)vs->pgdp, start); do { next = stage1_pud_addr_end(start, end); size = next - start; if (stage1_pud_none(*pud)) { pmdp = (pmd_t *)stage1_get_free_page(flags); if (!pmdp) return -ENOMEM; memset(pmdp, 0, PAGE_SIZE); stage1_pud_populate(pud, (unsigned long)pmdp, flags); } else { pmdp = (pmd_t *)ptov(stage1_pmd_table_addr(*pud)); } ret = stage1_map_pmd_range(vs, pmdp, start, next, physical, flags); if (ret) return ret; } while (pud++, physical += size, start = next, start != end); return 0; } static int stage1_get_leaf_entry(struct vspace *vs, unsigned long va, pmd_t **pmdpp, pte_t **ptepp) { pud_t *pudp; pmd_t *pmdp; pte_t *ptep; pudp = stage1_pud_offset(vs->pgdp, va); if (stage1_pud_none(*pudp)) return -ENOMEM; pmdp = stage1_pmd_offset(stage1_pmd_table_addr(*pudp), va); if (stage1_pmd_none(*pmdp)) return -ENOMEM; if (stage1_pmd_huge(*pmdp)) { *pmdpp = pmdp; return 0; } ptep = stage1_pte_offset(stage1_pte_table_addr(*pmdp), va); *ptepp = ptep; return 0; } int arch_host_change_map(struct vspace *vs, unsigned long vir, unsigned long phy, unsigned long flags) { int ret; pmd_t *pmdp = NULL; pte_t *ptep = NULL; ret = stage1_get_leaf_entry(vs, vir, &pmdp, &ptep); if (ret) return ret; if (pmdp) { stage1_set_pmd(pmdp, 0); flush_tlb_va_range(vir, S1_PMD_SIZE); stage1_set_pmd(pmdp, stage1_pmd_attr(phy, flags)); return 0; } stage1_set_pte(ptep, 0); flush_tlb_va_range(vir, S1_PTE_SIZE); stage1_set_pte(ptep, stage1_pte_attr(phy, flags)); return 0; } static inline phy_addr_t stage1_va_to_pa(struct vspace *vs, unsigned long va) { unsigned long pte_offset = va & ~S1_PTE_MASK; unsigned long pmd_offset = va & ~S1_PMD_MASK; unsigned long phy = 0; pud_t *pudp; pmd_t *pmdp; pte_t *ptep; pudp = stage1_pud_offset(vs->pgdp, va); if (stage1_pud_none(*pudp)) return 0; pmdp = stage1_pmd_offset(ptov(stage1_pmd_table_addr(*pudp)), va); if (stage1_pmd_none(*pmdp)) return 0; if (stage1_pmd_huge(*pmdp)) { phy = ((*pmdp) & S1_PHYSICAL_MASK) + pmd_offset; return 0; } ptep = stage1_pte_offset(ptov(stage1_pte_table_addr(*pmdp)), va); phy = *ptep & S1_PHYSICAL_MASK; if (phy == 0) return 0; return phy + pte_offset; } phy_addr_t arch_translate_va_to_pa(struct vspace *vs, unsigned long va) { return stage1_va_to_pa(vs, va); } int arch_host_map(struct vspace *vs, unsigned long start, unsigned long end, unsigned long physical, unsigned long flags) { if (end == start) return -EINVAL; ASSERT((start < S1_VIRT_MAX) && (end <= S1_VIRT_MAX)); ASSERT(physical < S1_PHYSICAL_MAX); ASSERT(IS_PAGE_ALIGN(start) && IS_PAGE_ALIGN(end) && IS_PAGE_ALIGN(physical)); return stage1_map_pud_range(vs, start, end, physical, flags); } int arch_host_unmap(struct vspace *vs, unsigned long start, unsigned long end, int mode) { ASSERT((start < S1_VIRT_MAX) && (end <= S1_VIRT_MAX)); return stage1_unmap_pud_range(vs, start, end, mode); } unsigned long arch_kernel_pgd_base(void) { extern unsigned char __stage1_page_table; return (unsigned long)&__stage1_page_table; } ================================================ FILE: kernel/arch/aarch64/core/stage1.h ================================================ #ifndef __MINOS_ARM64_STAGE1_H__ #define __MINOS_ARM64_STAGE1_H__ #include /* * stage 1 VMSAv8-64 Table Descriptors */ #define S1_DES_FAULT (0b00 << 0) #define S1_DES_BLOCK (0b01 << 0) /* level 1/2 */ #define S1_DES_TABLE (0b11 << 0) /* level 0/1/2 */ #define S1_DES_PAGE (0b11 << 0) /* level 3 */ #define S1_TABLE_NS (UL(1) << 63) #define S1_TABLE_AP (0) #define S1_TABLE_XN (UL(1) << 60) #define S1_TABLE_UAP (UL(1) << 61) #define S1_TABLE_UXN (UL(1) << 60) #define S1_CONTIGUOUS (UL(1) << 52) #define S1_PXN (UL(1) << 53) #define S1_XN (UL(1) << 54) #define S1_PFNMAP (UL(1) << 55) // 55 - 58 is for software #define S1_DEVMAP (UL(1) << 56) // 55 - 58 is for software #define S1_SHARED (UL(1) << 57) // 55 - 58 is for software #define S1_NS (1 << 5) #define S1_AP_RW (0b00 << 6) // for EL2 ap[2] is valid #define S1_AP_RW_URW (0b01 << 6) #define S1_AP_RO (0b10 << 6) #define S1_AP_RO_URO (0b11 << 6) #define S1_SH_NON (0b00 << 8) #define S1_SH_OUTER (0b10 << 8) #define S1_SH_INNER (0b11 << 8) #define S1_AF (1 << 10) #define S1_nG (1 << 11) #define S1_ATTR_IDX(n) ((n & 0xf) << 2) #define MT_DEVICE_nGnRnE 0 #define MT_DEVICE_nGnRE 1 #define MT_DEVICE_GRE 2 #define MT_NORMAL_NC 3 #define MT_NORMAL 4 #define MT_NORMAL_WT 5 #define S1_PAGE_NORMAL (S1_DES_PAGE | S1_AF | S1_NS | S1_SH_INNER | S1_ATTR_IDX(MT_NORMAL)) #define S1_PAGE_NC (S1_DES_PAGE | S1_AF | S1_NS | S1_SH_INNER | S1_ATTR_IDX(MT_DEVICE_nGnRE)) #define S1_PAGE_DEVICE (S1_DES_PAGE | S1_AF | S1_NS | S1_SH_INNER | S1_ATTR_IDX(MT_DEVICE_nGnRnE)) #define S1_PAGE_NORMAL_NC (S1_DES_PAGE | S1_AF | S1_NS | S1_SH_INNER | S1_ATTR_IDX(MT_NORMAL_NC)) #define S1_PAGE_WT (S1_DES_PAGE | S1_AF | S1_NS | S1_SH_INNER | S1_ATTR_IDX(MT_NORMAL_WT)) #define S1_BLOCK_NORMAL (S1_DES_BLOCK | S1_AF | S1_NS | S1_SH_INNER | S1_ATTR_IDX(MT_NORMAL)) #define S1_BLOCK_NC (S1_DES_BLOCK | S1_AF | S1_NS | S1_SH_INNER | S1_ATTR_IDX(MT_DEVICE_nGnRE)) #define S1_BLOCK_DEVICE (S1_DES_BLOCK | S1_AF | S1_NS | S1_SH_INNER | S1_ATTR_IDX(MT_DEVICE_nGnRnE)) #define S1_BLOCK_NORMAL_NC (S1_DES_BLOCK | S1_AF | S1_NS | S1_SH_INNER | S1_ATTR_IDX(MT_NORMAL_NC)) #define S1_BLOCK_WT (S1_DES_BLOCK | S1_AF | S1_NS | S1_SH_INNER | S1_ATTR_IDX(MT_NORMAL_WT)) #define BOOTMEM_CODE_ATTR (S1_ATTR_IDX(MT_NORMAL) | S1_DES_PAGE | S1_NS | S1_AP_RO | S1_SH_INNER | S1_AF | S1_XN) #define BOOTMEM_DATA_ATTR (S1_ATTR_IDX(MT_NORMAL) | S1_DES_PAGE | S1_NS | S1_AP_RW | S1_SH_INNER | S1_AF | S1_XN | S1_PXN) #define BOOTMEM_DATA_RO_ATTR (S1_ATTR_IDX(MT_NORMAL) | S1_DES_PAGE | S1_NS | S1_AP_RO | S1_SH_INNER | S1_AF | S1_XN | S1_PXN) #define BOOTMEM_INIT_ATTR (S1_ATTR_IDX(MT_NORMAL) | S1_DES_PAGE | S1_NS | S1_AP_RW | S1_SH_INNER | S1_AF | S1_XN) #define BOOTMEM_IO_ATTR (S1_ATTR_IDX(MT_DEVICE_nGnRnE) | S1_DES_PAGE | S1_NS | S1_AP_RW | S1_SH_INNER | S1_AF | S1_XN | S1_PXN) #define BOOTMEM_DATA_BLK_ATTR (S1_ATTR_IDX(MT_NORMAL) | S1_DES_BLOCK | S1_NS | S1_AP_RW | S1_SH_INNER | S1_AF | S1_XN | S1_PXN) #endif ================================================ FILE: kernel/arch/aarch64/core/vector.S ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include .section __elx_vectors, "ax" .balign 8 .macro vfunc name .global \name .type \name "function" .cfi_startproc \name: .endm .macro vfunc_end name .cfi_endproc .endm // ARM64_TPIDR will store the pcpu data // x18 will store the current_task .macro PCPU_SAVE_CURRENT_TASK tmp0 mrs \tmp0, ARM64_TPIDR str x18, [\tmp0, #PCPU_CURRENT_TASK] .endm .macro PCPU_LOAD_CURRENT_TASK mrs x18, ARM64_TPIDR ldr x18, [x18, #PCPU_CURRENT_TASK] .endm .macro LOAD_PCPU_STACK, tmp0 mrs \tmp0, ARM64_TPIDR ldr \tmp0, [\tmp0, #PCPU_STACK_OFFSET] mov sp, \tmp0 .endm .macro __SAVE_GP_REGS stp x27, x28, [sp, #-16]! stp x25, x26, [sp, #-16]! stp x23, x24, [sp, #-16]! stp x21, x22, [sp, #-16]! stp x19, x20, [sp, #-16]! stp x17, x18, [sp, #-16]! stp x15, x16, [sp, #-16]! stp x13, x14, [sp, #-16]! stp x11, x12, [sp, #-16]! stp x9, x10, [sp, #-16]! stp x7, x8, [sp, #-16]! stp x5, x6, [sp, #-16]! stp x3, x4, [sp, #-16]! stp x1, x2, [sp, #-16]! str x0, [sp, #-8]! mrs x0, SP_EL0 str x0, [sp, #-8]! mrs x0, ARM64_SPSR str x0, [sp, #-8]! mrs x0, ARM64_ELR str x0, [sp, #-8]! dsb nsh .endm .macro SAVE_GP_REGS stp x29, x30, [sp, #-16]! __SAVE_GP_REGS .endm .macro LOAD_GP_REGS ldr x0, [sp], #8 // restore task context msr ARM64_ELR, x0 ldr x0, [sp], #8 msr ARM64_SPSR, x0 ldr x0, [sp], #8 msr SP_EL0, x0 ldp x0, x1, [sp], #16 ldp x2, x3, [sp], #16 ldp x4, x5, [sp], #16 ldp x6, x7, [sp], #16 ldp x8, x9, [sp], #16 ldp x10, x11, [sp], #16 ldp x12, x13, [sp], #16 ldp x14, x15, [sp], #16 ldp x16, x17, [sp], #16 ldp x18, x19, [sp], #16 ldp x20, x21, [sp], #16 ldp x22, x23, [sp], #16 ldp x24, x25, [sp], #16 ldp x26, x27, [sp], #16 ldp x28, x29, [sp], #16 ldr x30, [sp], #8 dsb nsh .endm vfunc __bad_mode __SAVE_GP_REGS mov x0, sp mov x1, x29 b bad_mode /* will never return */ lb: b lb vfunc_end __bad_mode vfunc exception_return LOAD_PCPU_STACK x1 // load percpu stack, need ensure the irq is off. bl exception_return_handler // check whether need to resched. x18 will the next task. ldr x1, [x18, #TASK_STACK_OFFSET] // load the running task's stack mov sp, x1 // change to the new stack address ldr x1, [sp, #8] // load spsr and x1, x1, #0x0f cmp x1, #ARM64_SPSR_KERNEL // whether the task will return to user b.eq __do_exception_return mov x0, sp bl task_return_to_user str xzr, [x18, #TASK_USER_REGS_OFFSET] // clear the user_regs for task __do_exception_return: LOAD_GP_REGS eret vfunc_end exception_return vfunc __sync_exception_from_current_el SAVE_GP_REGS mov x0, sp str x0, [x18, #TASK_STACK_OFFSET] // use SVC for sched() , other type will // go to the exception handler. mrs x1, ARM64_ESR ubfx x2, x1, #ESR_ELx_EC_SHIFT, #ESR_ELx_EC_WIDTH cmp x2, #ESR_ELx_EC_SVC64 b.eq __sync_current_out bl sync_exception_from_current_el // go to the c handler, will die. __sync_current_out: b exception_return vfunc_end __sync_exception_from_current_el vfunc __sync_exception_from_lower_el SAVE_GP_REGS PCPU_LOAD_CURRENT_TASK // x18 will be the current task. bl task_exit_from_user mov x0, sp str x0, [x18, #TASK_USER_REGS_OFFSET] // save the user_regs to task bl sync_exception_from_lower_el // go to the c handler. mov x0, sp bl task_return_to_user LOAD_GP_REGS eret vfunc_end __sync_exception_from_lower_el vfunc __irq_exception_from_lower_el SAVE_GP_REGS PCPU_LOAD_CURRENT_TASK // x18 will store the current task // Set the irq flags into ti->flags. ldr x1, [x18, #TASK_INFO_FLAGS_OFFSET] orr x1, x1, #__TIF_HARDIRQ_MASK str x1, [x18, #TASK_INFO_FLAGS_OFFSET] dsb sy mov x0, sp // x0 is the gp_regs pass to irq_c_handler str x0, [x18, #TASK_STACK_OFFSET] // save the current task's stack to task str x0, [x18, #TASK_USER_REGS_OFFSET] // save the user_regs to task bl task_exit_from_user mov x0, sp bl irq_from_lower_el // call the c irq handler // clear the irq flags into ti->flags. ldr x1, [x18, #TASK_INFO_FLAGS_OFFSET] and x1, x1, #(~__TIF_HARDIRQ_MASK) str x1, [x18, #TASK_INFO_FLAGS_OFFSET] dsb sy b exception_return vfunc_end __irq_exception_from_lower_el vfunc __irq_exception_from_current_el SAVE_GP_REGS // Set the irq flags into ti->flags. ldr x1, [x18, #TASK_INFO_FLAGS_OFFSET] orr x1, x1, #__TIF_HARDIRQ_MASK str x1, [x18, #TASK_INFO_FLAGS_OFFSET] dsb sy mov x0, sp str x0, [x18, #TASK_STACK_OFFSET] // store the stack in case this task will scheded out. bl irq_from_current_el // irq is disabled all the time // clear the irq flags into ti->flags. ldr x1, [x18, #TASK_INFO_FLAGS_OFFSET] and x1, x1, #(~__TIF_HARDIRQ_MASK) str x1, [x18, #TASK_INFO_FLAGS_OFFSET] dsb sy b exception_return vfunc_end __irq_exception_from ================================================ FILE: kernel/arch/aarch64/include/asm/aarch64_common.h ================================================ #ifndef _MINOS_AARCH64_COMMON_H_ #define _MINOS_AARCH64_COMMON_H_ #include #define ESR_ELx_EC_UNKNOWN (0x00) #define ESR_ELx_EC_WFx (0x01) /* Unallocated EC: 0x02 */ #define ESR_ELx_EC_CP15_32 (0x03) #define ESR_ELx_EC_CP15_64 (0x04) #define ESR_ELx_EC_CP14_MR (0x05) #define ESR_ELx_EC_CP14_LS (0x06) #define ESR_ELx_EC_FP_ASIMD (0x07) #define ESR_ELx_EC_CP10_ID (0x08) /* Unallocated EC: 0x09 - 0x0B */ #define ESR_ELx_EC_CP14_64 (0x0C) /* Unallocated EC: 0x0d */ #define ESR_ELx_EC_ILL (0x0E) /* Unallocated EC: 0x0F - 0x10 */ #define ESR_ELx_EC_SVC32 (0x11) #define ESR_ELx_EC_HVC32 (0x12) #define ESR_ELx_EC_SMC32 (0x13) /* Unallocated EC: 0x14 */ #define ESR_ELx_EC_SVC64 (0x15) #define ESR_ELx_EC_HVC64 (0x16) #define ESR_ELx_EC_SMC64 (0x17) #define ESR_ELx_EC_SYS64 (0x18) #define ESR_ELx_EC_SVE (0x19) /* Unallocated EC: 0x1A - 0x1E */ #define ESR_ELx_EC_IMP_DEF (0x1f) #define ESR_ELx_EC_IABT_LOW (0x20) #define ESR_ELx_EC_IABT_CUR (0x21) #define ESR_ELx_EC_PC_ALIGN (0x22) /* Unallocated EC: 0x23 */ #define ESR_ELx_EC_DABT_LOW (0x24) #define ESR_ELx_EC_DABT_CUR (0x25) #define ESR_ELx_EC_SP_ALIGN (0x26) /* Unallocated EC: 0x27 */ #define ESR_ELx_EC_FP_EXC32 (0x28) /* Unallocated EC: 0x29 - 0x2B */ #define ESR_ELx_EC_FP_EXC64 (0x2C) /* Unallocated EC: 0x2D - 0x2E */ #define ESR_ELx_EC_SERROR (0x2F) #define ESR_ELx_EC_BREAKPT_LOW (0x30) #define ESR_ELx_EC_BREAKPT_CUR (0x31) #define ESR_ELx_EC_SOFTSTP_LOW (0x32) #define ESR_ELx_EC_SOFTSTP_CUR (0x33) #define ESR_ELx_EC_WATCHPT_LOW (0x34) #define ESR_ELx_EC_WATCHPT_CUR (0x35) /* Unallocated EC: 0x36 - 0x37 */ #define ESR_ELx_EC_BKPT32 (0x38) /* Unallocated EC: 0x39 */ #define ESR_ELx_EC_VECTOR32 (0x3A) /* Unallocted EC: 0x3B */ #define ESR_ELx_EC_BRK64 (0x3C) /* Unallocated EC: 0x3D - 0x3F */ #define ESR_ELx_EC_MAX (0x3F) #define ESR_ELx_EC_SHIFT (26) #define ESR_ELx_EC_WIDTH (6) #define ESR_ELx_EC_MASK (ULONG(0x3F) << ESR_ELx_EC_SHIFT) #define ESR_ELx_EC(esr) (((esr) & ESR_ELx_EC_MASK) >> ESR_ELx_EC_SHIFT) #define ESR_ELx_IL_SHIFT (25) #define ESR_ELx_IL (ULONG(1) << ESR_ELx_IL_SHIFT) #define ESR_ELx_ISS_MASK (ESR_ELx_IL - 1) /* ISS field definitions shared by different classes */ #define ESR_ELx_WNR_SHIFT (6) #define ESR_ELx_WNR (ULONG(1) << ESR_ELx_WNR_SHIFT) /* Asynchronous Error Type */ #define ESR_ELx_IDS_SHIFT (24) #define ESR_ELx_IDS (ULONG(1) << ESR_ELx_IDS_SHIFT) #define ESR_ELx_AET_SHIFT (10) #define ESR_ELx_AET (ULONG(0x7) << ESR_ELx_AET_SHIFT) #define ESR_ELx_AET_UC (ULONG(0) << ESR_ELx_AET_SHIFT) #define ESR_ELx_AET_UEU (ULONG(1) << ESR_ELx_AET_SHIFT) #define ESR_ELx_AET_UEO (ULONG(2) << ESR_ELx_AET_SHIFT) #define ESR_ELx_AET_UER (ULONG(3) << ESR_ELx_AET_SHIFT) #define ESR_ELx_AET_CE (ULONG(6) << ESR_ELx_AET_SHIFT) /* Shared ISS field definitions for Data/Instruction aborts */ #define ESR_ELx_SET_SHIFT (11) #define ESR_ELx_SET_MASK (ULONG(3) << ESR_ELx_SET_SHIFT) #define ESR_ELx_FnV_SHIFT (10) #define ESR_ELx_FnV (ULONG(1) << ESR_ELx_FnV_SHIFT) #define ESR_ELx_EA_SHIFT (9) #define ESR_ELx_EA (ULONG(1) << ESR_ELx_EA_SHIFT) #define ESR_ELx_S1PTW_SHIFT (7) #define ESR_ELx_S1PTW (ULONG(1) << ESR_ELx_S1PTW_SHIFT) /* Shared ISS fault status code(IFSC/DFSC) for Data/Instruction aborts */ #define ESR_ELx_FSC (0x3F) #define ESR_ELx_FSC_TYPE (0x3C) #define ESR_ELx_FSC_EXTABT (0x10) #define ESR_ELx_FSC_SERROR (0x11) #define ESR_ELx_FSC_ACCESS (0x08) #define ESR_ELx_FSC_FAULT (0x04) #define ESR_ELx_FSC_PERM (0x0C) /* ISS field definitions for Data Aborts */ #define ESR_ELx_ISV_SHIFT (24) #define ESR_ELx_ISV (ULONG(1) << ESR_ELx_ISV_SHIFT) #define ESR_ELx_SAS_SHIFT (22) #define ESR_ELx_SAS (ULONG(3) << ESR_ELx_SAS_SHIFT) #define ESR_ELx_SSE_SHIFT (21) #define ESR_ELx_SSE (ULONG(1) << ESR_ELx_SSE_SHIFT) #define ESR_ELx_SRT_SHIFT (16) #define ESR_ELx_SRT_MASK (ULONG(0x1F) << ESR_ELx_SRT_SHIFT) #define ESR_ELx_SRT(val) (((val) & ESR_ELx_SRT_MASK) >> ESR_ELx_SRT_SHIFT) #define ESR_ELx_SF_SHIFT (15) #define ESR_ELx_SF (ULONG(1) << ESR_ELx_SF_SHIFT) #define ESR_ELx_AR_SHIFT (14) #define ESR_ELx_AR (ULONG(1) << ESR_ELx_AR_SHIFT) #define ESR_ELx_CM_SHIFT (8) #define ESR_ELx_CM (ULONG(1) << ESR_ELx_CM_SHIFT) /* ISS field definitions for exceptions taken in to Hyp */ #define ESR_ELx_CV (ULONG(1) << 24) #define ESR_ELx_COND_SHIFT (20) #define ESR_ELx_COND_MASK (ULONG(0xF) << ESR_ELx_COND_SHIFT) #define ESR_ELx_WFx_ISS_WFE (ULONG(1) << 0) #define ESR_ELx_xVC_IMM_MASK ((1UL << 16) - 1) #define FSC_FAULT ESR_ELx_FSC_FAULT #define FSC_ACCESS ESR_ELx_FSC_ACCESS #define FSC_PERM ESR_ELx_FSC_PERM #define FSC_SEA ESR_ELx_FSC_EXTABT #define FSC_SEA_TTW0 (0x14) #define FSC_SEA_TTW1 (0x15) #define FSC_SEA_TTW2 (0x16) #define FSC_SEA_TTW3 (0x17) #define FSC_SECC (0x18) #define FSC_SECC_TTW0 (0x1c) #define FSC_SECC_TTW1 (0x1d) #define FSC_SECC_TTW2 (0x1e) #define FSC_SECC_TTW3 (0x1f) #define DISR_EL1_IDS (ULONG(1) << 24) /* * DISR_EL1 and ESR_ELx share the bottom 13 bits, but the RES0 bits may mean * different things in the future... */ #define DISR_EL1_ESR_MASK (ESR_ELx_AET | ESR_ELx_EA | ESR_ELx_FSC) #define HPFAR_MASK GENMASK(39, 4) #define EC_TYPE_AARCH64 (0x1) #define EC_TYPE_AARCH32 (0X2) #define EC_TYPE_BOTH (0x3) /* * Current EL with SP0 0 - 3 * Current EL with SPx 4 - 7 * Lower EL with aarch64 8 - 11 * Lower EL with aarch32 12 - 15 */ #define VECTOR_C0_SYNC 0 #define VECTOR_C0_IRQ 1 #define VECTOR_C0_FIQ 2 #define VECTOR_C0_SERR 3 #define VECTOR_CX_SYNC 4 #define VECTOR_CX_IRQ 5 #define VECTOR_CX_FIQ 6 #define VECTOR_CX_SERR 7 #define VECTOR_L64_SYNC 8 #define VECTOR_L64_IRQ 9 #define VECTOR_L64_FIQ 10 #define VECTOR_L64_SERR 11 #define VECTOR_L32_SYNC 12 #define VECTOR_L32_IRQ 13 #define VECTOR_L32_FIQ 14 #define VECTOR_L32_SERR 15 #define VECTOR_MAX 16 #define AARCH64_SPSR_EL3h 0b1101 // el3 using sp_el3 #define AARCH64_SPSR_EL3t 0b1100 // el3 using sp_el0 #define AARCH64_SPSR_EL2h 0b1001 // el2 using sp_el2 #define AARCH64_SPSR_EL2t 0b1000 // el2 using sp_el0 #define AARCH64_SPSR_EL1h 0b0101 // el1 using sp_el1 #define AARCH64_SPSR_EL1t 0b0100 // el1 using sp_el0 #define AARCH64_SPSR_EL0t 0b0000 // el0 using sp_el0 #define AARCH64_SPSR_RW (1 << 4) #define AARCH64_SPSR_F (1 << 6) #define AARCH64_SPSR_I (1 << 7) #define AARCH64_SPSR_A (1 << 8) #define AARCH64_SPSR_D (1 << 9) #define AARCH64_SPSR_IL (1 << 20) #define AARCH64_SPSR_SS (1 << 21) #define AARCH64_SPSR_V (1 << 28) #define AARCH64_SPSR_C (1 << 29) #define AARCH64_SPSR_Z (1 << 30) #define AARCH64_SPSR_N (1 << 31) #define AARCH32_USER 0b0000 #define AARCH32_FIQ 0b0001 #define AARCH32_IRQ 0b0010 #define AARCH32_SVC 0b0011 #define AARCH32_MON 0b0110 #define AARCH32_ABT 0b0111 #define AARCH32_HYP 0b1010 #define AARCH32_UND 0b1011 #define AARCH32_SYSTEM 0b1111 #define MODE_EL3 (0x3UL) #define MODE_EL2 (0x2UL) #define MODE_EL1 (0x1UL) #define MODE_EL0 (0x0UL) #define MODE_EL_SHIFT (0x2UL) #define MODE_EL_MASK (0x3UL) #define GET_EL(mode) (((mode) >> MODE_EL_SHIFT) & MODE_EL_MASK) #define MPIDR_EL1_AFF3_LSB 32 #define MPIDR_EL1_U (1 << 30) #define MPIDR_EL1_MT (1 << 24) #define MPIDR_EL1_AFF2_LSB 16 #define MPIDR_EL1_AFF1_LSB 8 #define MPIDR_EL1_AFF0_LSB 0 #define MPIDR_EL1_AFF_WIDTH 8 #define MIPIDR_AFF_SHIFT 2 #define DCZID_EL0_BS_LSB 0 #define DCZID_EL0_BS_WIDTH 4 #define DCZID_EL0_DZP_LSB 5 #define DCZID_EL0_DZP (1 << 5) #define SCTLR_EL1_UCI (1 << 26) #define SCTLR_ELx_EE (1 << 25) #define SCTLR_EL1_E0E (1 << 24) #define SCTLR_ELx_WXN (1 << 19) #define SCTLR_EL1_nTWE (1 << 18) #define SCTLR_EL1_nTWI (1 << 16) #define SCTLR_EL1_UCT (1 << 15) #define SCTLR_EL1_DZE (1 << 14) #define SCTLR_ELx_I (1 << 12) #define SCTLR_EL1_UMA (1 << 9) #define SCTLR_EL1_SED (1 << 8) #define SCTLR_EL1_ITD (1 << 7) #define SCTLR_EL1_THEE (1 << 6) #define SCTLR_EL1_CP15BEN (1 << 5) #define SCTLR_EL1_SA0 (1 << 4) #define SCTLR_ELx_SA (1 << 3) #define SCTLR_ELx_C (1 << 2) #define SCTLR_ELx_A (1 << 1) #define SCTLR_ELx_M (1 << 0) #define SCTLR_ELx_C_BIT (2) #define SCTLR_ELx_A_BIT (1) #define SCTLR_ELx_M_BIT (0) #define CPACR_EL1_TTA (1 << 28) #define CPACR_EL1_FPEN (3 << 20) #define CPTR_ELx_TCPAC (1 << 31) #define CPTR_ELx_TTA (1 << 20) #define CPTR_ELx_TFP (1 << 10) #define SCR_EL3_TWE (1 << 13) #define SCR_EL3_TWI (1 << 12) #define SCR_EL3_ST (1 << 11) #define SCR_EL3_RW (1 << 10) #define SCR_EL3_SIF (1 << 9) #define SCR_EL3_HCE (1 << 8) #define SCR_EL3_SMD (1 << 7) #define SCR_EL3_EA (1 << 3) #define SCR_EL3_FIQ (1 << 2) #define SCR_EL3_IRQ (1 << 1) #define SCR_EL3_NS (1 << 0) #define HCR_EL2_VM (1ul << 0) #define HCR_EL2_SWIO (1ul << 1) #define HCR_EL2_PTW (1ul << 2) #define HCR_EL2_FMO (1ul << 3) #define HCR_EL2_IMO (1ul << 4) #define HCR_EL2_AMO (1ul << 5) #define HCR_EL2_VF (1ul << 6) #define HCR_EL2_VI (1ul << 7) #define HCR_EL2_VSE (1ul << 8) #define HCR_EL2_FB (1ul << 9) #define HCR_EL2_BSU_IS (1ul << 10) #define HCR_EL2_BSU_OS (2ul << 10) #define HCR_EL2_BSU_FS (3ul << 10) #define HCR_EL2_DC (1ul << 12) #define HCR_EL2_TWI (1ul << 13) #define HCR_EL2_TWE (1ul << 14) #define HCR_EL2_TID0 (1ul << 15) #define HCR_EL2_TID1 (1ul << 16) #define HCR_EL2_TID2 (1ul << 17) #define HCR_EL2_TID3 (1ul << 18) #define HCR_EL2_TSC (1ul << 19) #define HCR_EL2_TIDCP (1ul << 20) #define HCR_EL2_TACR (1ul << 21) #define HCR_EL2_TSW (1ul << 22) #define HCR_EL2_TPC (1ul << 23) #define HCR_EL2_TPU (1ul << 24) #define HCR_EL2_TTLB (1ul << 25) #define HCR_EL2_TVM (1ul << 26) #define HCR_EL2_TGE (1ul << 27) #define HCR_EL2_TDZ (1ul << 28) #define HCR_EL2_HCD (1ul << 29) #define HCR_EL2_TRVM (1ul << 30) #define HCR_EL2_RW (1ul << 31) #define HCR_EL2_CD (1ul << 32) #define HCR_EL2_ID (1ul << 33) #define HCR_EL2_E2H (1ul << 34) #define LOUIS_SHIFT (21) #define LOC_SHIFT (24) #define CLIDR_FIELD_WIDTH (3) #define DAIF_F_BIT 6 #define DAIF_I_BIT 7 #define DAIF_A_BIT 8 #define DAIF_D_BIT 9 #define LEVEL_SHIFT (1) /* * TCR flags. */ #define TCR_T0SZ_OFFSET 0 #define TCR_T1SZ_OFFSET 16 #define TCR_T0SZ(x) ((UL(64) - (x)) << TCR_T0SZ_OFFSET) #define TCR_T1SZ(x) ((UL(64) - (x)) << TCR_T1SZ_OFFSET) #define TCR_TxSZ(x) (TCR_T0SZ(x) | TCR_T1SZ(x)) #define TCR_TxSZ_WIDTH 6 #define TCR_T0SZ_MASK (((UL(1) << TCR_TxSZ_WIDTH) - 1) << TCR_T0SZ_OFFSET) #define TCR_IRGN0_SHIFT 8 #define TCR_IRGN0_MASK (UL(3) << TCR_IRGN0_SHIFT) #define TCR_IRGN0_NC (UL(0) << TCR_IRGN0_SHIFT) #define TCR_IRGN0_WBWA (UL(1) << TCR_IRGN0_SHIFT) #define TCR_IRGN0_WT (UL(2) << TCR_IRGN0_SHIFT) #define TCR_IRGN0_WBnWA (UL(3) << TCR_IRGN0_SHIFT) #define TCR_IRGN1_SHIFT 24 #define TCR_IRGN1_MASK (UL(3) << TCR_IRGN1_SHIFT) #define TCR_IRGN1_NC (UL(0) << TCR_IRGN1_SHIFT) #define TCR_IRGN1_WBWA (UL(1) << TCR_IRGN1_SHIFT) #define TCR_IRGN1_WT (UL(2) << TCR_IRGN1_SHIFT) #define TCR_IRGN1_WBnWA (UL(3) << TCR_IRGN1_SHIFT) #define TCR_IRGN_NC (TCR_IRGN0_NC | TCR_IRGN1_NC) #define TCR_IRGN_WBWA (TCR_IRGN0_WBWA | TCR_IRGN1_WBWA) #define TCR_IRGN_WT (TCR_IRGN0_WT | TCR_IRGN1_WT) #define TCR_IRGN_WBnWA (TCR_IRGN0_WBnWA | TCR_IRGN1_WBnWA) #define TCR_IRGN_MASK (TCR_IRGN0_MASK | TCR_IRGN1_MASK) #define TCR_ORGN0_SHIFT 10 #define TCR_ORGN0_MASK (UL(3) << TCR_ORGN0_SHIFT) #define TCR_ORGN0_NC (UL(0) << TCR_ORGN0_SHIFT) #define TCR_ORGN0_WBWA (UL(1) << TCR_ORGN0_SHIFT) #define TCR_ORGN0_WT (UL(2) << TCR_ORGN0_SHIFT) #define TCR_ORGN0_WBnWA (UL(3) << TCR_ORGN0_SHIFT) #define TCR_ORGN1_SHIFT 26 #define TCR_ORGN1_MASK (UL(3) << TCR_ORGN1_SHIFT) #define TCR_ORGN1_NC (UL(0) << TCR_ORGN1_SHIFT) #define TCR_ORGN1_WBWA (UL(1) << TCR_ORGN1_SHIFT) #define TCR_ORGN1_WT (UL(2) << TCR_ORGN1_SHIFT) #define TCR_ORGN1_WBnWA (UL(3) << TCR_ORGN1_SHIFT) #define TCR_ORGN_NC (TCR_ORGN0_NC | TCR_ORGN1_NC) #define TCR_ORGN_WBWA (TCR_ORGN0_WBWA | TCR_ORGN1_WBWA) #define TCR_ORGN_WT (TCR_ORGN0_WT | TCR_ORGN1_WT) #define TCR_ORGN_WBnWA (TCR_ORGN0_WBnWA | TCR_ORGN1_WBnWA) #define TCR_ORGN_MASK (TCR_ORGN0_MASK | TCR_ORGN1_MASK) #define TCR_SH0_SHIFT 12 #define TCR_SH0_MASK (UL(3) << TCR_SH0_SHIFT) #define TCR_SH0_INNER (UL(3) << TCR_SH0_SHIFT) #define TCR_SH1_SHIFT 28 #define TCR_SH1_MASK (UL(3) << TCR_SH1_SHIFT) #define TCR_SH1_INNER (UL(3) << TCR_SH1_SHIFT) #define TCR_SHARED (TCR_SH0_INNER | TCR_SH1_INNER) #define TCR_TG0_SHIFT 14 #define TCR_TG0_MASK (UL(3) << TCR_TG0_SHIFT) #define TCR_TG0_4K (UL(0) << TCR_TG0_SHIFT) #define TCR_TG0_64K (UL(1) << TCR_TG0_SHIFT) #define TCR_TG0_16K (UL(2) << TCR_TG0_SHIFT) #define TCR_TG1_SHIFT 30 #define TCR_TG1_MASK (UL(3) << TCR_TG1_SHIFT) #define TCR_TG1_16K (UL(1) << TCR_TG1_SHIFT) #define TCR_TG1_4K (UL(2) << TCR_TG1_SHIFT) #define TCR_TG1_64K (UL(3) << TCR_TG1_SHIFT) #define TCR_IPS_SHIFT 32 #define TCR_IPS_MASK (UL(7) << TCR_IPS_SHIFT) #define TCR_A1 (UL(1) << 22) #define TCR_ASID16 (UL(1) << 36) #define TCR_TBI0 (UL(1) << 37) #define TCR_HA (UL(1) << 39) #define TCR_HD (UL(1) << 40) #define TCR_NFD1 (UL(1) << 54) #define CNT_CTL_ISTATUS (1 << 2) #define CNT_CTL_IMASK (1 << 1) #define CNT_CTL_ENABLE (1 << 0) #endif ================================================ FILE: kernel/arch/aarch64/include/asm/aarch64_el1_reg.h ================================================ /* * Copyright (C) 2020 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __MINOS_AARCH64_EL1_REG_H__ #define __MINOS_ARRCH64_EL1_REG_H__ #include #define ARM64_SCTLR SCTLR_EL1 #define ARM64_CPACR CAPACR_EL1 #define ARM64_TRFCR TRFCR_EL1 #define ARM64_TTBR0 TTBR0_EL1 #define ARM64_TTBR1 TTBR1_EL1 #define ARM64_TCR TCR_EL1 #define ARM64_AFSR0 AFSR0_EL1 #define ARM64_AFSR1 AFSR1_EL1 #define ARM64_ESR ESR_EL1 #define ARM64_FAR FAR_EL1 #define ARM64_MAIR MAIR_EL1 #define ARM64_AMAIR AMAIR_EL1 #define ARM64_VBAR VBAR_EL1 #define ARM64_CONTEXTIDR CONTEXTIDR_EL1 #define ARM64_CNTKCTL CNTHCTL_EL1 #define ARM64_SPSR SPSR_EL1 #define ARM64_ELR ELR_EL1 #define ARM64_TPIDR TPIDR_EL1 #define ARM64_CNTSIRQ_TVAL CNTP_TVAL_EL0 #define ARM64_CNTSIRQ_CTL CNTP_CTL_EL0 #define ARM64_CNTSIRQ_CVAL CNTP_CVAL_EL0 #define ARM64_CNTSCHED_TVAL CNTV_TVAL_EL0 #define ARM64_CNTSCHED_CTL CNTV_CTL_EL0 #define ARM64_CNTSCHED_CVAL CNTV_CVAL_EL0 #define ARM64_SPSR_VALUE 0x1c5 #define ARM64_SPSR_KERNEL AARCH64_SPSR_EL1h #define ARM64_SPSR_USER AARCH64_SPSR_EL0t #define ARM64_SCTLR_VALUE \ SCTLR_EL1_UCI | SCTLR_EL1_UCT | SCTLR_EL1_DZE // #define ARM64_TCR_VALUE 0x25B5103510 // VA[55] == 0 : TCR_ELx.TBI0 determines whether address tags are used. // VA[55] == 1 : TCR_ELx.TBI1 determines whether address tags are used. // use 512GB VA size, 1T IPA #define ARM64_TCR_VALUE \ TCR_T0SZ(39) | TCR_T1SZ(39) | TCR_IRGN0_WBWA | TCR_IRGN1_WBWA | \ TCR_ORGN0_WBWA | TCR_ORGN1_WBWA | TCR_SH0_INNER | TCR_SH1_INNER | \ TCR_TG1_4K | TCR_TG0_4K | TCR_ASID16 | TCR_TBI0 #endif ================================================ FILE: kernel/arch/aarch64/include/asm/aarch64_el2_reg.h ================================================ /* * Copyright (C) 2020 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __MINOS_AARCH64_EL2_REG_H__ #define __MINOS_AARCH64_EL2_REG_H__ #define ARM64_SCTLR SCTLR_EL2 #define ARM64_CPACR CPACR_EL2 #define ARM64_TRFCR TRFCR_EL2 #define ARM64_TTBR0 TTBR0_EL2 #define ARM64_TTBR1 TTBR0_EL2 #define ARM64_TCR TCR_EL2 #define ARM64_AFSR0 AFSR0_EL2 #define ARM64_AFSR1 AFSR1_EL2 #define ARM64_ESR ESR_EL2 #define ARM64_FAR FAR_EL2 #define ARM64_MAIR MAIR_EL2 #define ARM64_AMAIR AMAIR_EL2 #define ARM64_VBAR VBAR_EL2 #define ARM64_CONTEXTIDR CONTEXTIDR_EL2 #define ARM64_CNTKCTL CNTHCTL_EL2 #define ARM64_SPSR SPSR_EL2 #define ARM64_ELR ELR_EL2 #define ARM64_TPIDR TPIDR_EL2 #define ARM64_CNTSIRQ_TVAL CNTP_TVAL_EL0 #define ARM64_CNTSIRQ_CTL CNTP_CTL_EL0 #define ARM64_CNTSIRQ_CVAL CNTP_CVAL_EL0 #define ARM64_CNTSCHED_TVAL CNTHP_TVAL_EL2 #define ARM64_CNTSCHED_CTL CNTHP_CTL_EL2 #define ARM64_CNTSCHED_CVAL CNTHP_CVAL_EL2 #define ARM64_VTCR_EL2 VTCR_EL2 #define ARM64_VTTBR_EL2 VTTBR_EL2 #define ARM64_VMPIDR_EL2 VMPIDR_EL2 #define ARM64_VPIDR_EL2 VPIDR_EL2 #define ARM64_HCR_EL2 HCR_EL2 #define ARM64_DACR32_EL2 DACR32_EL2 #define ARM64_IFSR32_EL2 IFSR32_EL2 #define ARM64_CNTVOFF_EL2 CNTVOFF_EL2 /* the register for guest */ #define ARM64_SCTLR_EL1 SCTLR_EL1 #define ARM64_CPACR_EL1 CPACR_EL1 #define ARM64_ZCR_EL1 ZCR_EL1 #define ARM64_TRFCR_EL1 TRFCR_EL1 #define ARM64_TTBR0_EL1 TTBR0_EL1 #define ARM64_TTBR1_EL1 TTBR1_EL1 #define ARM64_TCR_EL1 TCR_EL1 #define ARM64_AFSR0_EL1 AFSR0_EL1 #define ARM64_AFSR1_EL1 AFSR1_EL1 #define ARM64_ESR_EL1 ESR_EL1 #define ARM64_FAR_EL1 FAR_EL1 #define ARM64_PMSCR_EL1 PMSCR_EL1 #define ARM64_PAR_EL1 PAR_EL1 #define ARM64_MAIR_EL1 MAIR_EL1 #define ARM64_AMAIR_EL1 AMAIR_EL1 #define ARM64_VBAR_EL1 VBAR_EL1 #define ARM64_CONTEXTIDR_EL1 CONTEXTIDR_EL1 #define ARM64_CNTKCTL_EL1 CNTKCTL_EL1 #define ARM64_SPSR_EL1 SPSR_EL1 #define ARM64_ELR_EL1 ELR_EL1 #define ARM64_SP_EL1 SP_EL1 #define ARM64_ACTLR_EL1 ACTLR_EL1 #define ARM64_TPIDR_EL1 TPIDR_EL1 #define ARM64_CSSELR_EL1 CSSELR_EL1 #define ARM64_SP_EL0 SP_EL0 #define ARM64_TPIDR_EL0 TPIDR_EL0 #define ARM64_TPIDRRO_EL0 TPIDRRO_EL0 #define ARM64_CNTV_TVAL_EL0 CNTV_TVAL_EL0 #define ARM64_CNTV_CTL_EL0 CNTV_CTL_EL0 #define ARM64_CNTV_CVAL_EL0 CNTV_CVAL_EL0 #define ARM64_SCTLR_VALUE 0x30c51878 #define ARM64_SPSR_VALUE 0x1c9 #define ARM64_SPSR_KERNEL AARCH64_SPSR_EL2h #define ARM64_SPSR_USER AARCH64_SPSR_EL0t /* * VA 48 bit address range 1TB and translation start at lvl1 * IRGN0 : Normal memory, Inner Write-Back Write-Allocate Cacheable * ORGN0 : Normal memory, Outer Write-Back Write-Allocate Cacheable * SH0 : Inner shareable * BIT23 :Reserved, res1. */ #define ARM64_TCR_VALUE \ TCR_T0SZ(39) | TCR_IRGN0_WBWA | TCR_ORGN0_WBWA | TCR_SH0_INNER | TCR_TG0_4K #endif ================================================ FILE: kernel/arch/aarch64/include/asm/aarch64_el2_vhe_reg.h ================================================ /* * Copyright (C) 2020 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __MINOS_AARCH64_EL2_VHE_REG_H__ #define __MINOS_AARCH64_EL2_VHE_REG_H__ #define ARM64_SCTLR SCTLR_EL1 #define ARM64_CPTR CPACR_EL1 #define ARM64_TRFCR TRFCR_EL1 #define ARM64_TTBR0 TTBR0_EL1 #define ARM64_TTBR1 TTBR1_EL1 #define ARM64_TCR TCR_EL1 #define ARM64_AFSR0 AFSR0_EL1 #define ARM64_AFSR1 AFSR1_EL1 #define ARM64_ESR ESR_EL1 #define ARM64_FAR FAR_EL1 #define ARM64_MAIR MAIR_EL1 #define ARM64_AMAIR AMAIR_EL1 #define ARM64_VBAR VBAR_EL1 #define ARM64_CONTEXTIDR CONTEXTIDR_EL1 #define ARM64_CNTHCTL CNTKCTL_EL1 #define ARM64_SPSR SPSR_EL1 #define ARM64_ELR ELR_EL1 #define ARM64_TPIDR TPIDR_EL2 #define ARM64_CNTSIRQ_TVAL CNTP_TVAL_EL02 #define ARM64_CNTSIRQ_CTL CNTP_CTL_EL02 #define ARM64_CNTSIRQ_CVAL CNTP_CVAL_EL02 #define ARM64_CNTSCHED_TVAL CNTHP_TVAL_EL2 #define ARM64_CNTSCHED_CTL CNTHP_CTL_EL2 #define ARM64_CNTSCHED_CVAL CNTHP_CVAL_EL2 #define ARM64_VTCR_EL2 VTCR_EL2 #define ARM64_VTTBR_EL2 VTTBR_EL2 #define ARM64_VMPIDR_EL2 VMPIDR_EL2 #define ARM64_VPIDR_EL2 VPIDR_EL2 #define ARM64_HCR_EL2 HCR_EL2 #define ARM64_DACR32_EL2 DACR32_EL2 #define ARM64_IFSR32_EL2 IFSR32_EL2 #define ARM64_CNTVOFF_EL2 CNTVOFF_EL2 /* the register for guest */ #define ARM64_SCTLR_EL1 SCTLR_EL12 #define ARM64_CPACR_EL1 CPACR_EL12 #define ARM64_ZCR_EL1 ZCR_EL12 #define ARM64_TRFCR_EL1 TRFCR_EL12 #define ARM64_TTBR0_EL1 TTBR0_EL12 #define ARM64_TTBR1_EL1 TTBR1_EL12 #define ARM64_TCR_EL1 TCR_EL12 #define ARM64_AFSR0_EL1 AFSR0_EL12 #define ARM64_AFSR1_EL1 AFSR1_EL12 #define ARM64_ESR_EL1 ESR_EL12 #define ARM64_FAR_EL1 FAR_EL12 #define ARM64_PMSCR_EL1 PMSCR_EL12 #define ARM64_MAIR_EL1 MAIR_EL12 #define ARM64_AMAIR_EL1 AMAIR_EL12 #define ARM64_VBAR_EL1 VBAR_EL12 #define ARM64_CONTEXTIDR_EL1 CONTEXTIDR_EL12 #define ARM64_CNTKCTL_EL1 CNTKCTL_EL12 #define ARM64_SPSR_EL1 SPSR_EL12 #define ARM64_ELR_EL1 ELR_EL12 #define ARM64_SP_EL1 SP_EL1 #define ARM64_ACTLR_EL1 ACTLR_EL1 #define ARM64_TPIDR_EL1 TPIDR_EL1 #define ARM64_CSSELR_EL1 CSSELR_EL1 #define ARM64_PAR_EL1 PAR_EL1 #define ARM64_SP_EL0 SP_EL0 #define ARM64_TPIDR_EL0 TPIDR_EL0 #define ARM64_TPIDRRO_EL0 TPIDRRO_EL0 #define ARM64_CNTV_TVAL_EL0 CNTV_TVAL_EL02 #define ARM64_CNTV_CTL_EL0 CNTV_CTL_EL02 #define ARM64_CNTV_CVAL_EL0 CNTV_CVAL_EL02 #define ARM64_SCTLR_VALUE 0x30c50838 #define ARM64_SPSR_VALUE 0x1c9 #define ARM64_SPSR_KERNEL AARCH64_SPSR_EL2h #define ARM64_SPSR_USER AARCH64_SPSR_EL0t /* config the TCR_EL1 for kernel * mov x1, #0x0 * orr x1, x1, #(0x10 << 16) // VA 48 bit address range and translation start at lvl0 for kernel * orr x1, x1, #(1 << 24) // IRGN1 : Normal memory, Inner Write-Back Write-Allocate Cacheable * orr x1, x1, #(1 << 26) // ORGN1 : Normal memory, Outer Write-Back Write-Allocate Cacheable * orr x1, x1, #(3 << 28) // Inner shareable * orr x1, x1, #(2 << 30) // 4KB Granule size * orr x1, x1, #(1 << 37) // Top bit do not used for address caculate for TTBR0_EL1 (user space). * orr x1, x1, #(0 << 38) // Top bit used for address caculate for TTBR1_EL1 (kernel space). * orr x1, x1, #0x10 // VA 48 bit address range t0sz * orr x1, x1, #(1 << 8) // IRGN0 : Normal memory, Inner Write-Back Write-Allocate Cacheable * orr x1, x1, #(1 << 10) // ORGN0 : Normal memory, Outer Write-Back Write-Allocate Cacheable * orr x1, x1, #(3 << 12) // Inner shareable * ldr x2, =(5 << 32) // 256TB IPS * orr x1, x1, x2 */ #define ARM64_TCR_VALUE 0x25B5103510 #endif ================================================ FILE: kernel/arch/aarch64/include/asm/aarch64_helper.h ================================================ /* * Copyright (c) 2013-2017, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #ifndef _MINOS_ARMV8_H_ #define _MINOS_ARMV8_H_ #include #include #include #ifdef CONFIG_ERRATA_A57_813419 #define ERRATA_A57_813419 1 #else #define ERRATA_A57_813419 0 #endif #define _DEFINE_SYSREG_READ_FUNC(_name, _reg_name) \ static inline uint64_t read_ ## _name(void) \ { \ uint64_t v; \ __asm__ volatile ("mrs %0, " #_reg_name : "=r" (v)); \ return v; \ } #define _DEFINE_SYSREG_WRITE_FUNC(_name, _reg_name) \ static inline void write_ ## _name(uint64_t v) \ { \ __asm__ volatile ("msr " #_reg_name ", %0" : : "r" (v)); \ } #define SYSREG_WRITE_CONST(reg_name, v) \ __asm__ volatile ("msr " #reg_name ", %0" : : "i" (v)) /* Define read function for system register */ #define DEFINE_SYSREG_READ_FUNC(_name) \ _DEFINE_SYSREG_READ_FUNC(_name, _name) /* Define read & write function for system register */ #define DEFINE_SYSREG_RW_FUNCS(_name) \ _DEFINE_SYSREG_READ_FUNC(_name, _name) \ _DEFINE_SYSREG_WRITE_FUNC(_name, _name) /* Define read & write function for renamed system register */ #define DEFINE_RENAME_SYSREG_RW_FUNCS(_name, _reg_name) \ _DEFINE_SYSREG_READ_FUNC(_name, _reg_name) \ _DEFINE_SYSREG_WRITE_FUNC(_name, _reg_name) /* Define read function for renamed system register */ #define DEFINE_RENAME_SYSREG_READ_FUNC(_name, _reg_name) \ _DEFINE_SYSREG_READ_FUNC(_name, _reg_name) /* Define write function for renamed system register */ #define DEFINE_RENAME_SYSREG_WRITE_FUNC(_name, _reg_name) \ _DEFINE_SYSREG_WRITE_FUNC(_name, _reg_name) /********************************************************************** * Macros to create inline functions for system instructions *********************************************************************/ /* Define function for simple system instruction */ #define DEFINE_SYSOP_FUNC(_op) \ static inline void _op(void) \ { \ __asm__ (#_op); \ } /* Define function for system instruction with type specifier */ #define DEFINE_SYSOP_TYPE_FUNC(_op, _type) \ static inline void _op ## _type(void) \ { \ __asm__ (#_op " " #_type); \ } /* Define function for system instruction with register parameter */ #define DEFINE_SYSOP_TYPE_PARAM_FUNC(_op, _type) \ static inline void _op ## _type(uint64_t v) \ { \ __asm__ (#_op " " #_type ", %0" : : "r" (v)); \ } /******************************************************************************* * TLB maintenance accessor prototypes ******************************************************************************/ #if ERRATA_A57_813419 /* * Define function for TLBI instruction with type specifier that implements * the workaround for errata 813419 of Cortex-A57. */ #define DEFINE_TLBIOP_ERRATA_A57_813419_TYPE_FUNC(_type)\ static inline void tlbi ## _type(void) \ { \ __asm__("tlbi " #_type "\n" \ "dsb ish\n" \ "tlbi " #_type); \ } /* * Define function for TLBI instruction with register parameter that implements * the workaround for errata 813419 of Cortex-A57. */ #define DEFINE_TLBIOP_ERRATA_A57_813419_TYPE_PARAM_FUNC(_type) \ static inline void tlbi ## _type(uint64_t v) \ { \ __asm__("tlbi " #_type ", %0\n" \ "dsb ish\n" \ "tlbi " #_type ", %0" : : "r" (v)); \ } #endif /* ERRATA_A57_813419 */ DEFINE_SYSOP_TYPE_FUNC(tlbi, alle1) DEFINE_SYSOP_TYPE_FUNC(tlbi, alle1is) DEFINE_SYSOP_TYPE_FUNC(tlbi, alle2) DEFINE_SYSOP_TYPE_FUNC(tlbi, alle2is) #if ERRATA_A57_813419 DEFINE_TLBIOP_ERRATA_A57_813419_TYPE_FUNC(alle3) DEFINE_TLBIOP_ERRATA_A57_813419_TYPE_FUNC(alle3is) #else DEFINE_SYSOP_TYPE_FUNC(tlbi, alle3) DEFINE_SYSOP_TYPE_FUNC(tlbi, alle3is) #endif DEFINE_SYSOP_TYPE_FUNC(tlbi, vmalle1) DEFINE_SYSOP_TYPE_PARAM_FUNC(tlbi, vaae1is) DEFINE_SYSOP_TYPE_PARAM_FUNC(tlbi, vaale1is) DEFINE_SYSOP_TYPE_PARAM_FUNC(tlbi, vae2is) DEFINE_SYSOP_TYPE_PARAM_FUNC(tlbi, vale2is) #if ERRATA_A57_813419 DEFINE_TLBIOP_ERRATA_A57_813419_TYPE_PARAM_FUNC(vae3is) DEFINE_TLBIOP_ERRATA_A57_813419_TYPE_PARAM_FUNC(vale3is) #else DEFINE_SYSOP_TYPE_PARAM_FUNC(tlbi, vae3is) DEFINE_SYSOP_TYPE_PARAM_FUNC(tlbi, vale3is) #endif /******************************************************************************* * Cache maintenance accessor prototypes ******************************************************************************/ DEFINE_SYSOP_TYPE_PARAM_FUNC(dc, isw) DEFINE_SYSOP_TYPE_PARAM_FUNC(dc, cisw) DEFINE_SYSOP_TYPE_PARAM_FUNC(dc, csw) DEFINE_SYSOP_TYPE_PARAM_FUNC(dc, cvac) DEFINE_SYSOP_TYPE_PARAM_FUNC(dc, ivac) DEFINE_SYSOP_TYPE_PARAM_FUNC(dc, civac) DEFINE_SYSOP_TYPE_PARAM_FUNC(dc, cvau) DEFINE_SYSOP_TYPE_PARAM_FUNC(dc, zva) /******************************************************************************* * Address translation accessor prototypes ******************************************************************************/ DEFINE_SYSOP_TYPE_PARAM_FUNC(at, s12e1r) DEFINE_SYSOP_TYPE_PARAM_FUNC(at, s12e1w) DEFINE_SYSOP_TYPE_PARAM_FUNC(at, s12e0r) DEFINE_SYSOP_TYPE_PARAM_FUNC(at, s12e0w) void disable_mmu_el1(void); void disable_mmu_el3(void); void disable_mmu_icache_el1(void); void disable_mmu_icache_el3(void); /******************************************************************************* * Misc. accessor prototypes ******************************************************************************/ #define write_daifclr(val) SYSREG_WRITE_CONST(daifclr, val) #define write_daifset(val) SYSREG_WRITE_CONST(daifset, val) DEFINE_SYSREG_READ_FUNC(par_el1) DEFINE_SYSREG_READ_FUNC(id_pfr1_el1) DEFINE_SYSREG_READ_FUNC(id_aa64pfr0_el1) DEFINE_SYSREG_READ_FUNC(id_aa64dfr0_el1) DEFINE_SYSREG_READ_FUNC(CurrentEl) DEFINE_SYSREG_RW_FUNCS(daif) DEFINE_SYSREG_RW_FUNCS(spsr_el1) DEFINE_SYSREG_RW_FUNCS(spsr_el2) DEFINE_SYSREG_RW_FUNCS(spsr_el3) DEFINE_SYSREG_RW_FUNCS(elr_el1) DEFINE_SYSREG_RW_FUNCS(elr_el2) DEFINE_SYSREG_RW_FUNCS(elr_el3) DEFINE_SYSOP_FUNC(wfi) DEFINE_SYSOP_FUNC(wfe) DEFINE_SYSOP_FUNC(sev) DEFINE_SYSOP_TYPE_FUNC(dsb, sy) DEFINE_SYSOP_TYPE_FUNC(dmb, sy) DEFINE_SYSOP_TYPE_FUNC(dmb, st) DEFINE_SYSOP_TYPE_FUNC(dmb, ld) DEFINE_SYSOP_TYPE_FUNC(dsb, ish) DEFINE_SYSOP_TYPE_FUNC(dsb, ishst) DEFINE_SYSOP_TYPE_FUNC(dmb, ish) DEFINE_SYSOP_TYPE_FUNC(dmb, ishst) DEFINE_SYSOP_TYPE_FUNC(dsb, nsh) DEFINE_SYSOP_TYPE_FUNC(dsb, nshst) DEFINE_SYSOP_TYPE_FUNC(dmb, nsh) DEFINE_SYSOP_TYPE_FUNC(dmb, nshst) uint32_t get_afflvl_shift(uint32_t); uint32_t mpidr_mask_lower_afflvls(uint64_t, uint32_t); /******************************************************************************* * System register accessor prototypes ******************************************************************************/ DEFINE_SYSREG_READ_FUNC(midr_el1) DEFINE_SYSREG_READ_FUNC(mpidr_el1) DEFINE_SYSREG_READ_FUNC(id_aa64mmfr0_el1) DEFINE_SYSREG_RW_FUNCS(scr_el3) DEFINE_SYSREG_RW_FUNCS(hcr_el2) DEFINE_SYSREG_RW_FUNCS(vbar_el1) DEFINE_SYSREG_RW_FUNCS(vbar_el2) DEFINE_SYSREG_RW_FUNCS(vbar_el3) DEFINE_SYSREG_RW_FUNCS(sctlr_el1) DEFINE_SYSREG_RW_FUNCS(sctlr_el2) DEFINE_SYSREG_RW_FUNCS(sctlr_el3) DEFINE_SYSREG_RW_FUNCS(actlr_el1) DEFINE_SYSREG_RW_FUNCS(actlr_el2) DEFINE_SYSREG_RW_FUNCS(actlr_el3) DEFINE_SYSREG_RW_FUNCS(esr_el1) DEFINE_SYSREG_RW_FUNCS(esr_el2) DEFINE_SYSREG_RW_FUNCS(esr_el3) DEFINE_SYSREG_RW_FUNCS(afsr0_el1) DEFINE_SYSREG_RW_FUNCS(afsr0_el2) DEFINE_SYSREG_RW_FUNCS(afsr0_el3) DEFINE_SYSREG_RW_FUNCS(afsr1_el1) DEFINE_SYSREG_RW_FUNCS(afsr1_el2) DEFINE_SYSREG_RW_FUNCS(afsr1_el3) DEFINE_SYSREG_RW_FUNCS(far_el1) DEFINE_SYSREG_RW_FUNCS(far_el2) DEFINE_SYSREG_RW_FUNCS(far_el3) DEFINE_SYSREG_RW_FUNCS(mair_el1) DEFINE_SYSREG_RW_FUNCS(mair_el2) DEFINE_SYSREG_RW_FUNCS(mair_el3) DEFINE_SYSREG_RW_FUNCS(amair_el1) DEFINE_SYSREG_RW_FUNCS(amair_el2) DEFINE_SYSREG_RW_FUNCS(amair_el3) DEFINE_SYSREG_READ_FUNC(rvbar_el1) DEFINE_SYSREG_READ_FUNC(rvbar_el2) DEFINE_SYSREG_READ_FUNC(rvbar_el3) DEFINE_SYSREG_RW_FUNCS(rmr_el1) DEFINE_SYSREG_RW_FUNCS(rmr_el2) DEFINE_SYSREG_RW_FUNCS(rmr_el3) DEFINE_SYSREG_RW_FUNCS(tcr_el1) DEFINE_SYSREG_RW_FUNCS(tcr_el2) DEFINE_SYSREG_RW_FUNCS(tcr_el3) DEFINE_SYSREG_RW_FUNCS(ttbr0_el1) DEFINE_SYSREG_RW_FUNCS(ttbr0_el2) DEFINE_SYSREG_RW_FUNCS(ttbr0_el3) DEFINE_SYSREG_RW_FUNCS(ttbr1_el1) DEFINE_SYSREG_RW_FUNCS(vttbr_el2) DEFINE_SYSREG_RW_FUNCS(cptr_el2) DEFINE_SYSREG_RW_FUNCS(cptr_el3) DEFINE_SYSREG_RW_FUNCS(cpacr_el1) DEFINE_SYSREG_RW_FUNCS(cntfrq_el0) DEFINE_SYSREG_RW_FUNCS(cntps_ctl_el1) DEFINE_SYSREG_RW_FUNCS(cntps_tval_el1) DEFINE_SYSREG_RW_FUNCS(cntps_cval_el1) DEFINE_SYSREG_READ_FUNC(cntpct_el0) DEFINE_SYSREG_RW_FUNCS(cnthctl_el2) DEFINE_SYSREG_RW_FUNCS(tpidr_el3) DEFINE_SYSREG_RW_FUNCS(cntvoff_el2) DEFINE_SYSREG_RW_FUNCS(vpidr_el2) DEFINE_SYSREG_RW_FUNCS(vmpidr_el2) DEFINE_SYSREG_RW_FUNCS(cntp_ctl_el0) DEFINE_SYSREG_READ_FUNC(isr_el1) DEFINE_SYSREG_READ_FUNC(ctr_el0) DEFINE_SYSREG_RW_FUNCS(mdcr_el2) DEFINE_SYSREG_RW_FUNCS(hstr_el2) DEFINE_SYSREG_RW_FUNCS(cnthp_ctl_el2) DEFINE_SYSREG_RW_FUNCS(pmcr_el0) DEFINE_RENAME_SYSREG_RW_FUNCS(icc_sre_el1, ICC_SRE_EL1) DEFINE_RENAME_SYSREG_RW_FUNCS(icc_sre_el2, ICC_SRE_EL2) DEFINE_RENAME_SYSREG_RW_FUNCS(icc_sre_el3, ICC_SRE_EL3) DEFINE_RENAME_SYSREG_RW_FUNCS(icc_pmr_el1, ICC_PMR_EL1) DEFINE_RENAME_SYSREG_READ_FUNC(icc_rpr_el1, ICC_RPR_EL1) DEFINE_RENAME_SYSREG_RW_FUNCS(icc_igrpen1_el3, ICC_IGRPEN1_EL3) DEFINE_RENAME_SYSREG_RW_FUNCS(icc_igrpen0_el1, ICC_IGRPEN0_EL1) DEFINE_RENAME_SYSREG_READ_FUNC(icc_hppir0_el1, ICC_HPPIR0_EL1) DEFINE_RENAME_SYSREG_READ_FUNC(icc_hppir1_el1, ICC_HPPIR1_EL1) DEFINE_RENAME_SYSREG_READ_FUNC(icc_iar0_el1, ICC_IAR0_EL1) DEFINE_RENAME_SYSREG_READ_FUNC(icc_iar1_el1, ICC_IAR1_EL1) DEFINE_RENAME_SYSREG_WRITE_FUNC(icc_eoir0_el1, ICC_EOIR0_EL1) DEFINE_RENAME_SYSREG_WRITE_FUNC(icc_eoir1_el1, ICC_EOIR1_EL1) DEFINE_RENAME_SYSREG_WRITE_FUNC(icc_dir_el1, ICC_DIR_EL1) DEFINE_RENAME_SYSREG_READ_FUNC(icc_sgi1r_el1, ICC_SGI1R_EL1) DEFINE_RENAME_SYSREG_READ_FUNC(ich_vtr_el2, ICH_VTR_EL2) #define IS_IN_EL(x) \ (GET_EL(read_CurrentEl()) == MODE_EL##x) #define IS_IN_EL1() IS_IN_EL(1) #define IS_IN_EL3() IS_IN_EL(3) #define IS_IN_EL2() IS_IN_EL(2) /* * Check if an EL is implemented from AA64PFR0 register fields. 'el' argument * must be one of 1, 2 or 3. */ #define EL_IMPLEMENTED(el) \ ((read_id_aa64pfr0_el1() >> ID_AA64PFR0_EL##el##_SHIFT) \ & ID_AA64PFR0_ELX_MASK) /* Previously defined accesor functions with incomplete register names */ #define read_current_el() read_CurrentEl() #define dsb() dsbsy() #define dmb() dmbsy() #define read_midr() read_midr_el1() #define read_mpidr() read_mpidr_el1() #define read_scr() read_scr_el3() #define write_scr(_v) write_scr_el3(_v) #define read_hcr() read_hcr_el2() #define write_hcr(_v) write_hcr_el2(_v) #define read_cpacr() read_cpacr_el1() #define write_cpacr(_v) write_cpacr_el1(_v) #endif ================================================ FILE: kernel/arch/aarch64/include/asm/aarch64_reg.h ================================================ /* * Copyright (C) 2020 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __MINOS_AARCH64_REG_H__ #define __MINOS_AARCH64_REG_H__ #include #ifdef CONFIG_VIRT #include #else #include #endif // CONFIG_VIRT #include #endif ================================================ FILE: kernel/arch/aarch64/include/asm/arch.h ================================================ #ifndef _MINOS_ARCH_AARCH64_H_ #define _MINOS_ARCH_AARCH64_H_ #include #include #include #include #include #include #ifdef CONFIG_VIRT #include #endif #define NR_LOCAL_IRQS (32) #define NR_SGI_IRQS (16) #define NR_PPI_IRQS (16) #define SGI_IRQ_BASE (0) #define PPI_IRQ_BASE (16) #define SPI_OFFSET(n) (n - NR_LOCAL_IRQS); #define LOCAL_OFFSET(n) (n) #define arch_disable_local_irq() write_daifset(2) #define arch_enable_local_irq() write_daifclr(2) #define arch_save_irqflags() read_daif() #define arch_restore_irqflags(flags) write_daif(flags) #define arch_irq_disabled() (read_daif() & (1 << DAIF_I_BIT)) #define local_irq_save(flag) \ do { \ flag = arch_save_irqflags(); \ arch_disable_local_irq(); \ } while (0) #define local_irq_restore(flag) \ do { \ arch_restore_irqflags(flag); \ } while (0) #define stack_to_gp_regs(base) \ (gp_regs *)((base) - sizeof(gp_regs)) #define get_reg_value(regs, index) \ *((unsigned long *)(regs) + index + 3) #define set_reg_value(regs, index, value) \ *((unsigned long *)(regs) + index + 3) = (unsigned long)value #define read_sysreg32(name) ({ \ uint32_t _r; \ asm volatile("mrs %0, "stringify(name) : "=r" (_r)); \ _r; }) #define write_sysreg32(v, name) \ do { \ uint32_t _r = v; \ asm volatile("msr "stringify(name)", %0" : : "r" (_r)); \ } while (0) #define write_sysreg64(v, name) \ do { \ uint64_t _r = v; \ asm volatile("msr "stringify(name)", %0" : : "r" (_r)); \ } while (0) #define read_sysreg64(name) ({ \ uint64_t _r; \ asm volatile("mrs %0, "stringify(name) : "=r" (_r)); \ _r; }) #define read_sysreg(name) read_sysreg64(name) #define write_sysreg(v, name) write_sysreg64(v, name) #define nop() asm ("nop") static inline int affinity_to_logic_cpu(uint32_t aff3, uint32_t aff2, uint32_t aff1, uint32_t aff0) { return (aff1 * CONFIG_NR_CPUS_CLUSTER0) + aff0; } static inline unsigned long va_to_pa(unsigned long va) { uint64_t pa, tmp = read_sysreg64(PAR_EL1); asm volatile ("at s1e2r, %0;" : : "r" (va)); isb(); pa = read_sysreg64(PAR_EL1) & 0x0000fffffffff000; pa = pa | (va & (~(~PAGE_MASK))); write_sysreg64(tmp, PAR_EL1); return pa; } static inline unsigned long guest_va_to_pa(unsigned long va, int read) { uint64_t pa, tmp = read_sysreg64(PAR_EL1); if (read) asm volatile ("at s12e1r, %0;" : : "r" (va)); else asm volatile ("at s12e1w, %0;" : : "r" (va)); isb(); pa = read_sysreg64(PAR_EL1) & 0x0000fffffffff000; pa = pa | (va & PAGE_MASK); write_sysreg64(tmp, PAR_EL1); return pa; } static inline unsigned long guest_va_to_ipa(unsigned long va, int read) { uint64_t pa, tmp = read_sysreg64(PAR_EL1); if (read) asm volatile ("at s1e1w, %0;" : : "r" (va)); else asm volatile ("at s1e1r, %0;" : : "r" (va)); isb(); pa = read_sysreg64(PAR_EL1) & 0x0000fffffffff000; pa = pa | (va & (~(~PAGE_MASK))); write_sysreg64(tmp, PAR_EL1); return pa; } static inline void cpu_relax(void) { asm volatile("yield" ::: "memory"); } static inline unsigned long arch_get_virtual_address_size(void) { return (unsigned long)1 << 39; } int arch_is_exit_to_user(struct task *task); int arch_is_taken_from_guest(gp_regs *regs); void arch_switch_task_sw(struct task *cur, struct task *next); void arch_dump_stack(gp_regs *regs, unsigned long *sp); void arch_dump_register(gp_regs *regs); unsigned long arch_get_fp(void); unsigned long arch_get_sp(void); unsigned long arch_get_lr(void); void arch_smp_init(phy_addr_t *smp_h_addr); int __arch_init(void); int arch_early_init(void); void arch_init_task(struct task *task, void *entry, void *usp, void *data); void arch_set_user_stack(struct task *task, unsigned long stack); void arch_release_task(struct task *task); uint64_t cpuid_to_affinity(int cpuid); int affinity_to_cpuid(unsigned long affinity); pgd_t *arch_alloc_process_page_table(void); #endif ================================================ FILE: kernel/arch/aarch64/include/asm/asm_current.h ================================================ #ifndef __MINOS_ASM_CURRENT_H__ #define __MINOS_ASM_CURRENT_H__ #include #include #include static inline void *asm_get_current_task(void) { void *tsk; __asm__ volatile ("mov %0, x18" : "=r" (tsk)); return tsk; } static inline void *asm_get_current_task_info(void) { void *tsk_info; __asm__ volatile ("mov %0, x18" : "=r" (tsk_info)); return tsk_info; } static inline void asm_set_current_task(void *task) { __asm__ volatile ("mov x18, %0" : : "r" (task)); } #ifdef CONFIG_VIRT static inline void arch_set_pcpu_data(void *pcpu) { __asm__ volatile("msr TPIDR_EL2, %0" : : "r" (pcpu)); } static inline void *arch_get_pcpu_data(void) { uint64_t v; __asm__ volatile("mrs %0, TPIDR_EL2" : "=r" (v)); return (void *)v; } static inline int __smp_processor_id(void) { uint64_t v; int cpu; __asm__ volatile ( "mrs %0, TPIDR_EL2\n" "ldrh %w1, [%0, #0]\n" : "=r" (v), "=r" (cpu) : : "memory" ); return cpu; } #else static inline void arch_set_pcpu_data(void *pcpu) { __asm__ volatile("msr TPIDR_EL1, %0" : : "r" (pcpu)); } static inline void *arch_get_pcpu_data(void) { uint64_t v; __asm__ volatile("mrs %0, TPIDR_EL1" : "=r" (v)); return (void *)v; } static inline int __smp_processor_id(void) { uint64_t v; int cpu; __asm__ volatile ( "mrs %0, TPIDR_EL1\n" "ldrh %w1, [%0, #0]\n" : "=r" (v), "=r" (cpu) : : "memory" ); return cpu; } #endif static inline void arch_sys_sched(void) { __asm__ volatile("svc #0"); } #endif ================================================ FILE: kernel/arch/aarch64/include/asm/asm_marco.S ================================================ #ifndef _MINOS_ASM_MARCO_H_ #define _MINOS_ASM_MARCO_H_ #include // current kernel and user space all used 512G virtual // address range, so: // ttbr0_el1 : 0x0000000000000000 - 0x00000007fffffffff // ttbr1_el1 : 0xfffffff800000000 - 0xfffffffffffffffff #ifdef CONFIG_ARM_ADDRESS_TAGGING .macro asm_vtop reg and \reg, \reg, #CONFIG_VTOP_MASK .endm .macro asm_ptov reg orr \reg, \reg, #CONFIG_PTOV_MASK .endm #else .macro asm_vtop reg .endm .macro asm_ptov reg .endm #endif .macro func _name, align=2 .cfi_sections .debug_frame .section __asm_code, "ax" .type \_name, %function .func \_name .cfi_startproc .align \align \_name: .endm .macro endfunc _name .endfunc .cfi_endproc .size \_name, . - \_name .endm .macro PRINT adr x0, 98f bl puts b 99f 98: .asciz "-------1\r\n" .align 2 99: .endm #endif ================================================ FILE: kernel/arch/aarch64/include/asm/asm_types.h ================================================ #ifndef _MINOS_AARCH64_TYPES_H_ #define _MINOS_AARCH64_TYPES_H_ #include typedef unsigned long __u64; typedef unsigned int __u32; typedef unsigned short __u16; typedef unsigned char __u8; typedef signed long __s64; typedef signed int __s32; typedef signed short __s16; typedef signed char __s8; #ifndef __aarch64__ #define __aarch64__ #endif #ifdef CONFIG_ARM_ADDRESS_TAGGING #define ptov(addr) ((unsigned long)(addr) | CONFIG_PTOV_MASK) #define vtop(addr) ((unsigned long)(addr) & CONFIG_VTOP_MASK) #define __va(addr) ((unsigned long)(addr) & CONFIG_VTOP_MASK) #define is_kva(va) (((unsigned long)(va) & CONFIG_PTOV_MASK) == CONFIG_PTOV_MASK) #else #define ptov(addr) ((unsigned long)addr) #define vtop(addr) ((unsigned long)addr) #define __va(va) ((unsigned long)(va)) #define is_kva(va) ((unsigned long)va >= 4096) #endif /* * 512G for user space process */ #define USER_PROCESS_ADDR_LIMIT (1UL << 39) #endif ================================================ FILE: kernel/arch/aarch64/include/asm/atomic.h ================================================ #ifndef __ASM_ATOMIC_H__ #define __ASM_ATOMIC_H__ #include #include #include static inline int atomic_read(atomic_t *t) { smp_mb(); return t->value; } static inline void atomic_set(int i, atomic_t *t) { t->value = i; smp_mb(); } #ifndef CONFIG_ARM_ATOMIC_LSE static inline void atomic_add(int i, atomic_t *v) { unsigned long tmp; int ret; asm volatile( "1: ldxr %w0, %2\n" " add %w0, %w0, %w3\n" " stxr %w1, %w0, %2\n" " cbnz %w1, 1b" : "=&r" (ret), "=&r" (tmp), "+Q" (v->value) : "Ir" (i) ); } static inline int atomic_add_return(int i, atomic_t *v) { unsigned long tmp; int ret; asm volatile( "1: ldxr %w0, %2\n" " add %w0, %w0, %w3\n" " stlxr %w1, %w0, %2\n" " cbnz %w1, 1b" : "=&r" (ret), "=&r" (tmp), "+Q" (v->value) : "Ir" (i) : "memory" ); smp_mb(); return ret; } static inline int atomic_add_return_old(int i, atomic_t *v) { unsigned long tmp; int ret; asm volatile( "1: ldxr %w0, %2\n" " add %w0, %w0, %w3\n" " stlxr %w1, %w0, %2\n" " cbnz %w1, 1b\n" " sub %w0, %w0, %w3" : "=&r" (ret), "=&r" (tmp), "+Q" (v->value) : "Ir" (i) : "memory" ); smp_mb(); return ret; } static inline void atomic_sub(int i, atomic_t *v) { unsigned long tmp; int ret; asm volatile( "1: ldxr %w0, %2\n" " sub %w0, %w0, %w3\n" " stlxr %w1, %w0, %2\n" " cbnz %w1, 1b" : "=&r" (ret), "=&r" (tmp), "+Q" (v->value) : "Ir" (i) ); } static inline int atomic_sub_return(int i, atomic_t *v) { unsigned long tmp; int ret; asm volatile( "1: ldaxr %w0, %2\n" " sub %w0, %w0, %w3\n" " stlxr %w1, %w0, %2\n" " cbnz %w1, 1b" : "=&r" (ret), "=&r" (tmp), "+Q" (v->value) : "Ir" (i) : "memory" ); smp_mb(); return ret; } static inline int atomic_sub_return_old(int i, atomic_t *v) { unsigned long tmp; int ret; asm volatile( "1: ldaxr %w0, %2\n" " sub %w0, %w0, %w3\n" " stlxr %w1, %w0, %2\n" " cbnz %w1, 1b\n" " add %w0, %w0, %w3" : "=&r" (ret), "=&r" (tmp), "+Q" (v->value) : "Ir" (i) : "memory" ); return ret; } static inline int atomic_cmpxchg(atomic_t *t, int old, int new) { unsigned long tmp; int oldval; smp_mb(); asm volatile( "1: ldxr %w1, %2\n" " cmp %w1, %w3\n" " b.ne 2f\n" " stxr %w0, %w4, %2\n" " cbnz %w0, 1b\n" "2: " : "=&r" (tmp), "=&r" (oldval), "+Q" (t->value) : "Ir" (old), "r" (new) : "cc" ); smp_mb(); return oldval; } static inline int atomic_inc_if_postive(atomic_t *t) { unsigned long tmp; int oldval; int value; smp_mb(); asm volatile( "1: ldxr %w1, %2\n" " cmp %w1, #0\n" " b.lt 2f\n" " add %w3, %w1, #1\n" " stxr %w0, %w3, %2\n" " cbnz %w0, 1b\n" "2: " : "=&r" (tmp), "=&r" (oldval), "+Q" (t->value), "=&r" (value) : : "cc" ); smp_mb(); return oldval; } static inline int atomic_dec_set_negtive_if_zero(atomic_t *t) { unsigned long tmp; int oldval; int value; smp_mb(); asm volatile( "1: ldxr %w1, %2\n" " cmp %w1, #0\n" " b.le 2f\n" " sub %w3, %w1, #1\n" " cmp %w3, #0\n" " b.ne 3f\n" " sub %w3, %w3, #1\n" // set the value to -1 if new value is -1 "3: " " stxr %w0, %w3, %2\n" " cbnz %w0, 1b\n" "2: " : "=&r" (tmp), "=&r" (oldval), "+Q" (t->value), "=&r" (value) : : "cc" ); smp_mb(); return oldval; } static inline int atomic_cmpsub(atomic_t *t, int old, int value) { unsigned long tmp; int oldval; smp_mb(); asm volatile( "1: ldxr %w1, %2\n" " cmp %w1, %w3\n" " sub %w4, %w1, %4\n" " b.eq 2f\n" " stxr %w0, %w4, %2\n" " cbnz %w0, 1b\n" "2: " : "=&r" (tmp), "=&r" (oldval), "+Q" (t->value) : "Ir" (old), "r" (value) : "cc" ); smp_mb(); return oldval; } #else #define ATOMIC_OP(op, asm_op) \ static inline void atomic_##op(int i, atomic_t *t) \ { \ register int w0 asm ("w0") = i; \ register atomic_t *x1 asm ("x1") = t; \ \ asm volatile ( \ " " #asm_op " %w[i], %[v]\n" \ : [i] "+r" (w0), [v] "+Q" (t->value) \ : "r" (x1) \ : "x16", "x17", "x30" \ ); \ } ATOMIC_OP(andnot, stclr) ATOMIC_OP(or, stset) ATOMIC_OP(xor, steor) ATOMIC_OP(add, stadd) #define ATOMIC_OP_ADD_RETURN(name, mb, cl...) \ static inline int atomic_add_return##name(int i, atomic_t *v) \ { \ register int w0 asm ("w0") = i; \ register atomic_t *x1 asm ("x1") = v; \ \ asm volatile( \ " ldadd" #mb " %w[i], w30, %[v]\n" \ " add %w[i], %w[i], w30" \ : [i] "+r" (w0), [v] "+Q" (v->value) \ : "r" (x1) \ : "x16", "x17", "x30", ##cl \ ); \ \ return w0; \ } static inline int atomic_add_return_old(int i, atomic_t *v) { register int w0 asm ("w0") = i; register atomic_t *x1 asm ("x1") = v; asm volatile( " ldaddal %w[i], w30, %[v]\n" " add %w[i], %w[i], w30" : [i] "+r" (w0), [v] "+Q" (v->value) : "r" (x1) : "x16", "x17", "x30", "memory" ); return w0; } ATOMIC_OP_ADD_RETURN(_relaxed, ) ATOMIC_OP_ADD_RETURN(_acquire, a, "memory") ATOMIC_OP_ADD_RETURN(_release, l, "memory") ATOMIC_OP_ADD_RETURN( , al, "memory") static inline void atomic_sub(int i, atomic_t *v) { register int w0 asm ("w0") = i; register atomic_t *x1 asm ("x1") = v; asm volatile( " neg %w[i], %w[i]\n" " stadd %w[i], %[v]" : [i] "+&r" (w0), [v] "+Q" (v->value) : "r" (x1) : "x16", "x17", "x30" ); } #define ATOMIC_OP_SUB_RETURN(name, mb, cl...) \ static inline int atomic_sub_return##name(int i, atomic_t *v) \ { \ register int w0 asm ("w0") = i; \ register atomic_t *x1 asm ("x1") = v; \ \ asm volatile( \ " neg %w[i], %w[i]\n" \ " ldadd" #mb " %w[i], w30, %[v]\n" \ " add %w[i], %w[i], w30" \ : [i] "+&r" (w0), [v] "+Q" (v->value) \ : "r" (x1) \ : "x16", "x17", "x30", ##cl); \ \ return w0; \ } ATOMIC_OP_SUB_RETURN(_relaxed, ) ATOMIC_OP_SUB_RETURN(_acquire, a, "memory") ATOMIC_OP_SUB_RETURN(_release, l, "memory") ATOMIC_OP_SUB_RETURN( , al, "memory") static inline int atomic_sub_return_old(int i, atomic_t *v) { register int w0 asm ("w0") = i; register atomic_t *x1 asm ("x1") = v; asm volatile( " neg %w[i], %w[i]\n" " ldaddal %w[i], w30, %[v]" : [i] "+&r" (w0), [v] "+Q" (v->value) : "r" (x1) : "x16", "x17", "x30", "memory"); return w0; } #define __CMPXCHG_CASE(w, sz, name, mb, cl...) \ static inline unsigned long __cmpxchg_case_##name(volatile void *ptr, \ unsigned long old, \ unsigned long new) \ { \ register unsigned long x0 asm ("x0") = (unsigned long)ptr; \ register unsigned long x1 asm ("x1") = old; \ register unsigned long x2 asm ("x2") = new; \ \ asm volatile( \ " mov " #w "30, %" #w "[old]\n" \ " cas" #mb #sz "\t" #w "30, %" #w "[new], %[v]\n" \ " mov %" #w "[ret], " #w "30" \ : [ret] "+r" (x0), [v] "+Q" (*(unsigned long *)ptr) \ : [old] "r" (x1), [new] "r" (x2) \ : "x16", "x17", "x30", ##cl); \ \ return x0; \ } __CMPXCHG_CASE(w, b, 1, ) __CMPXCHG_CASE(w, h, 2, ) __CMPXCHG_CASE(w, , 4, ) __CMPXCHG_CASE(x, , 8, ) __CMPXCHG_CASE(w, b, acq_1, a, "memory") __CMPXCHG_CASE(w, h, acq_2, a, "memory") __CMPXCHG_CASE(w, , acq_4, a, "memory") __CMPXCHG_CASE(x, , acq_8, a, "memory") __CMPXCHG_CASE(w, b, rel_1, l, "memory") __CMPXCHG_CASE(w, h, rel_2, l, "memory") __CMPXCHG_CASE(w, , rel_4, l, "memory") __CMPXCHG_CASE(x, , rel_8, l, "memory") __CMPXCHG_CASE(w, b, mb_1, al, "memory") __CMPXCHG_CASE(w, h, mb_2, al, "memory") __CMPXCHG_CASE(w, , mb_4, al, "memory") __CMPXCHG_CASE(x, , mb_8, al, "memory") #endif // CONFIG_ARM_ATOMIC_LSE #endif ================================================ FILE: kernel/arch/aarch64/include/asm/barrier.h ================================================ #ifndef __MINOS_ASM_BARRIER_H__ #define __MINOS_ASM_BARRIER_H__ #define __isb() asm volatile("isb" : : : "memory") #define __dmb(opt) asm volatile("dmb " #opt : : : "memory") #define __dsb(opt) asm volatile("dsb " #opt : : : "memory") #define isb() __isb(); #define mb() __dsb(sy) #define rmb() __dsb(ld) #define wmb() __dsb(st) #define dma_rmb() __dmb(oshld) #define dma_wmb() __dmb(oshst) #define iormb() dma_rmb() #define iowmb() dma_wmb() #define smp_mb() __dmb(ish) #define smp_rmb() __dmb(ishld) #define smp_wmb() __dmb(ishst) #endif ================================================ FILE: kernel/arch/aarch64/include/asm/bitops.h ================================================ #ifndef _ASM_GENERIC_BITOPS_H_ #define _ASM_GENERIC_BITOPS_H_ #include /** * __ffs - find first bit in word. * @word: The word to search * * Undefined if no bit exists, so code should check against 0 first. */ static inline unsigned long __ffs(unsigned long word) { int num = 0; #if BITS_PER_LONG == 64 if ((word & 0xffffffff) == 0) { num += 32; word >>= 32; } #endif if ((word & 0xffff) == 0) { num += 16; word >>= 16; } if ((word & 0xff) == 0) { num += 8; word >>= 8; } if ((word & 0xf) == 0) { num += 4; word >>= 4; } if ((word & 0x3) == 0) { num += 2; word >>= 2; } if ((word & 0x1) == 0) num += 1; return num; } static inline unsigned long __fls(unsigned long word) { int num = BITS_PER_LONG - 1; #if BITS_PER_LONG == 64 if (!(word & (~0ul << 32))) { num -= 32; word <<= 32; } #endif if (!(word & (~0ul << (BITS_PER_LONG-16)))) { num -= 16; word <<= 16; } if (!(word & (~0ul << (BITS_PER_LONG-8)))) { num -= 8; word <<= 8; } if (!(word & (~0ul << (BITS_PER_LONG-4)))) { num -= 4; word <<= 4; } if (!(word & (~0ul << (BITS_PER_LONG-2)))) { num -= 2; word <<= 2; } if (!(word & (~0ul << (BITS_PER_LONG-1)))) num -= 1; return num; } static inline int fls(int x) { int r = 32; if (!x) return 0; if (!(x & 0xffff0000u)) { x <<= 16; r -= 16; } if (!(x & 0xff000000u)) { x <<= 8; r -= 8; } if (!(x & 0xf0000000u)) { x <<= 4; r -= 4; } if (!(x & 0xc0000000u)) { x <<= 2; r -= 2; } if (!(x & 0x80000000u)) { x <<= 1; r -= 1; } return r; } #if BITS_PER_LONG == 32 static inline int fls64(__u64 x) { __u32 h = x >> 32; if (h) return fls(h) + 32; return fls(x); } #elif BITS_PER_LONG == 64 static inline int fls64(__u64 x) { if (x == 0) return 0; return __fls(x) + 1; } #else #error BITS_PER_LONG not 32 or 64 #endif #define ffz(x) __ffs(~(x)) #endif /* _ASM_GENERIC_BITOPS___FFS_H_ */ ================================================ FILE: kernel/arch/aarch64/include/asm/cache.h ================================================ #ifndef __MINOS_AARCH64_CACHE_H__ #define __MINOS_AARCH64_CACHE_H__ void flush_dcache_range(unsigned long addr, size_t size); void inv_dcache_range(unsigned long addr, size_t size); void flush_cache_all(void); void flush_dcache_all(void); void inv_dcache_all(void); static inline void inv_icache_local(void) { asm volatile("ic iallu"); dsbsy(); isb(); } static inline void inv_icache_all(void) { asm volatile("ic ialluis"); dsbsy(); } #endif ================================================ FILE: kernel/arch/aarch64/include/asm/cmpxchg.h ================================================ /* * Based on arch/arm/include/asm/cmpxchg.h * * Copyright (C) 2012 ARM Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __ASM_CMPXCHG_H__ #define __ASM_CMPXCHG_H__ #ifndef CONFIG_ARM_ATOMIC_LSE #define __XCHG_CASE(w, sz, name, mb, nop_lse, acq, acq_lse, rel, cl) \ static inline unsigned long __xchg_case_##name(unsigned long x, \ volatile void *ptr) \ { \ unsigned long ret, tmp; \ \ asm volatile ( \ " prfm pstl1strm, %2\n" \ "1: ld" #acq "xr" #sz "\t%" #w "0, %2\n" \ " st" #rel "xr" #sz "\t%w1, %" #w "3, %2\n" \ " cbnz %w1, 1b\n" \ " " #mb \ : "=&r" (ret), "=&r" (tmp), "+Q" (*(u8 *)ptr) \ : "r" (x) \ : cl); \ \ return ret; \ } #define __CMPXCHG_CASE(w, sz, name, mb, acq, rel, cl) \ static inline unsigned long __cmpxchg_case_##name(volatile void *ptr, \ unsigned long old, \ unsigned long new) \ { \ unsigned long tmp, oldval; \ \ asm volatile( \ " prfm pstl1strm, %[v]\n" \ "1: ld" #acq "xr" #sz "\t%" #w "[oldval], %[v]\n" \ " eor %" #w "[tmp], %" #w "[oldval], %" #w "[old]\n" \ " cbnz %" #w "[tmp], 2f\n" \ " st" #rel "xr" #sz "\t%w[tmp], %" #w "[new], %[v]\n" \ " cbnz %w[tmp], 1b\n" \ " " #mb "\n" \ " mov %" #w "[oldval], %" #w "[old]\n" \ "2:" \ : [tmp] "=&r" (tmp), [oldval] "=&r" (oldval), \ [v] "+Q" (*(unsigned long *)ptr) \ : [old] "Lr" (old), [new] "r" (new) \ : cl); \ \ return oldval; \ } \ __CMPXCHG_CASE(w, b, 1, , , , ) __CMPXCHG_CASE(w, h, 2, , , , ) __CMPXCHG_CASE(w, , 4, , , , ) __CMPXCHG_CASE( , , 8, , , , ) __CMPXCHG_CASE(w, b, acq_1, , a, , "memory") __CMPXCHG_CASE(w, h, acq_2, , a, , "memory") __CMPXCHG_CASE(w, , acq_4, , a, , "memory") __CMPXCHG_CASE( , , acq_8, , a, , "memory") __CMPXCHG_CASE(w, b, rel_1, , , l, "memory") __CMPXCHG_CASE(w, h, rel_2, , , l, "memory") __CMPXCHG_CASE(w, , rel_4, , , l, "memory") __CMPXCHG_CASE( , , rel_8, , , l, "memory") __CMPXCHG_CASE(w, b, mb_1, dmb ish, , l, "memory") __CMPXCHG_CASE(w, h, mb_2, dmb ish, , l, "memory") __CMPXCHG_CASE(w, , mb_4, dmb ish, , l, "memory") __CMPXCHG_CASE( , , mb_8, dmb ish, , l, "memory") #else #define __XCHG_CASE(w, sz, name, mb, nop_lse, acq, acq_lse, rel, cl) \ static inline unsigned long __xchg_case_##name(unsigned long x, \ volatile void *ptr) \ { \ unsigned long ret; \ \ asm volatile ( \ " nop\n" \ " nop\n" \ " swp" #acq_lse #rel #sz "\t%" #w "3, %" #w "0, %2\n" \ " nop\n" \ " " #nop_lse \ : "=&r" (ret), "=&r" (tmp), "+Q" (*(u8 *)ptr) \ : "r" (x) \ : cl); \ \ return ret; \ } #define __CMPXCHG_CASE(w, sz, name, mb, cl...) \ static inline unsigned long __cmpxchg_case_##name(volatile void *ptr, \ unsigned long old, \ unsigned long new) \ { \ register unsigned long x0 asm ("x0") = (unsigned long)ptr; \ register unsigned long x1 asm ("x1") = old; \ register unsigned long x2 asm ("x2") = new; \ \ asm volatile ( \ " mov " #w "30, %" #w "[old]\n" \ " cas" #mb #sz "\t" #w "30, %" #w "[new], %[v]\n" \ " mov %" #w "[ret], " #w "30") \ : [ret] "+r" (x0), [v] "+Q" (*(unsigned long *)ptr) \ : [old] "r" (x1), [new] "r" (x2) \ : "x30" , ##cl; \ \ return x0; \ } __CMPXCHG_CASE(w, b, 1, ) __CMPXCHG_CASE(w, h, 2, ) __CMPXCHG_CASE(w, , 4, ) __CMPXCHG_CASE(x, , 8, ) __CMPXCHG_CASE(w, b, acq_1, a, "memory") __CMPXCHG_CASE(w, h, acq_2, a, "memory") __CMPXCHG_CASE(w, , acq_4, a, "memory") __CMPXCHG_CASE(x, , acq_8, a, "memory") __CMPXCHG_CASE(w, b, rel_1, l, "memory") __CMPXCHG_CASE(w, h, rel_2, l, "memory") __CMPXCHG_CASE(w, , rel_4, l, "memory") __CMPXCHG_CASE(x, , rel_8, l, "memory") __CMPXCHG_CASE(w, b, mb_1, al, "memory") __CMPXCHG_CASE(w, h, mb_2, al, "memory") __CMPXCHG_CASE(w, , mb_4, al, "memory") __CMPXCHG_CASE(x, , mb_8, al, "memory") #endif // CONFIG_ARM_ATOMIC_LSE __XCHG_CASE(w, b, 1, , , , , , ) __XCHG_CASE(w, h, 2, , , , , , ) __XCHG_CASE(w, , 4, , , , , , ) __XCHG_CASE( , , 8, , , , , , ) __XCHG_CASE(w, b, acq_1, , , a, a, , "memory") __XCHG_CASE(w, h, acq_2, , , a, a, , "memory") __XCHG_CASE(w, , acq_4, , , a, a, , "memory") __XCHG_CASE( , , acq_8, , , a, a, , "memory") __XCHG_CASE(w, b, rel_1, , , , , l, "memory") __XCHG_CASE(w, h, rel_2, , , , , l, "memory") __XCHG_CASE(w, , rel_4, , , , , l, "memory") __XCHG_CASE( , , rel_8, , , , , l, "memory") __XCHG_CASE(w, b, mb_1, dmb ish, nop, , a, l, "memory") __XCHG_CASE(w, h, mb_2, dmb ish, nop, , a, l, "memory") __XCHG_CASE(w, , mb_4, dmb ish, nop, , a, l, "memory") __XCHG_CASE( , , mb_8, dmb ish, nop, , a, l, "memory") #undef __XCHG_CASE #define __XCHG_GEN(sfx) \ static inline unsigned long __xchg##sfx(unsigned long x, \ volatile void *ptr, \ int size) \ { \ switch (size) { \ case 1: \ return __xchg_case##sfx##_1(x, ptr); \ case 2: \ return __xchg_case##sfx##_2(x, ptr); \ case 4: \ return __xchg_case##sfx##_4(x, ptr); \ case 8: \ return __xchg_case##sfx##_8(x, ptr); \ default: \ return 0; \ } \ } __XCHG_GEN() __XCHG_GEN(_acq) __XCHG_GEN(_rel) __XCHG_GEN(_mb) #undef __XCHG_GEN #define __xchg_wrapper(sfx, ptr, x) \ ({ \ __typeof__(*(ptr)) __ret; \ __ret = (__typeof__(*(ptr))) \ __xchg##sfx((unsigned long)(x), (ptr), sizeof(*(ptr))); \ __ret; \ }) /* xchg */ #define xchg_relaxed(...) __xchg_wrapper( , __VA_ARGS__) #define xchg_acquire(...) __xchg_wrapper(_acq, __VA_ARGS__) #define xchg_release(...) __xchg_wrapper(_rel, __VA_ARGS__) #define xchg(...) __xchg_wrapper( _mb, __VA_ARGS__) #define __CMPXCHG_GEN(sfx) \ static inline unsigned long __cmpxchg##sfx(volatile void *ptr, \ unsigned long old, \ unsigned long new, \ int size) \ { \ switch (size) { \ case 1: \ return __cmpxchg_case##sfx##_1(ptr, (u8)old, new); \ case 2: \ return __cmpxchg_case##sfx##_2(ptr, (u16)old, new); \ case 4: \ return __cmpxchg_case##sfx##_4(ptr, old, new); \ case 8: \ return __cmpxchg_case##sfx##_8(ptr, old, new); \ default: \ return 0; \ } \ } __CMPXCHG_GEN() __CMPXCHG_GEN(_acq) __CMPXCHG_GEN(_rel) __CMPXCHG_GEN(_mb) #undef __CMPXCHG_GEN #define __cmpxchg_wrapper(sfx, ptr, o, n) \ ({ \ __typeof__(*(ptr)) __ret; \ __ret = (__typeof__(*(ptr))) \ __cmpxchg##sfx((ptr), (unsigned long)(o), \ (unsigned long)(n), sizeof(*(ptr))); \ __ret; \ }) /* cmpxchg */ #define cmpxchg_relaxed(...) __cmpxchg_wrapper( , __VA_ARGS__) #define cmpxchg_acquire(...) __cmpxchg_wrapper(_acq, __VA_ARGS__) #define cmpxchg_release(...) __cmpxchg_wrapper(_rel, __VA_ARGS__) #define cmpxchg(...) __cmpxchg_wrapper( _mb, __VA_ARGS__) #define cmpxchg_local cmpxchg_relaxed /* cmpxchg64 */ #define cmpxchg64_relaxed cmpxchg_relaxed #define cmpxchg64_acquire cmpxchg_acquire #define cmpxchg64_release cmpxchg_release #define cmpxchg64 cmpxchg #define cmpxchg64_local cmpxchg_local #endif ================================================ FILE: kernel/arch/aarch64/include/asm/cpu_feature.h ================================================ #ifndef __MINOS_CPU_FEATURE_H__ #define __MINOS_CPU_FEATURE_H__ #define ARM_FEATURE_MPIDR_SHIFT 0 int cpu_has_feature(int feature); int cpu_has_vhe(void); #endif ================================================ FILE: kernel/arch/aarch64/include/asm/div64.h ================================================ #ifndef _ASM_GENERIC_DIV64_H #define _ASM_GENERIC_DIV64_H /* * Copyright (C) 2003 Bernardo Innocenti * Based on former asm-ppc/div64.h and asm-m68knommu/div64.h * * The semantics of do_div() are: * * uint32_t do_div(uint64_t *n, uint32_t base) * { * uint32_t remainder = *n % base; * *n = *n / base; * return remainder; * } * * NOTE: macro parameter n is evaluated multiple times, * beware of side effects! */ #include #if BITS_PER_LONG == 64 # define do_div(n,base) ({ \ uint32_t __base = (base); \ uint32_t __rem; \ __rem = ((uint64_t)(n)) % __base; \ (n) = ((uint64_t)(n)) / __base; \ __rem; \ }) #elif BITS_PER_LONG == 32 extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor); /* The unnecessary pointer compare is there * to check for type safety (n must be 64bit) */ # define do_div(n,base) ({ \ uint32_t __base = (base); \ uint32_t __rem; \ (void)(((typeof((n)) *)0) == ((uint64_t *)0)); \ if (likely(((n) >> 32) == 0)) { \ __rem = (uint32_t)(n) % __base; \ (n) = (uint32_t)(n) / __base; \ } else \ __rem = __div64_32(&(n), __base); \ __rem; \ }) #else /* BITS_PER_LONG == ?? */ # error do_div() does not yet support the C64 #endif /* BITS_PER_LONG */ #endif /* _ASM_GENERIC_DIV64_H */ ================================================ FILE: kernel/arch/aarch64/include/asm/gic_reg.h ================================================ #ifndef _MINOS_GIC_REG_H_ #define _MINOS_GIC_REG_H_ /* * Mapping of MSR and MRS to physical and virtual CPU interface registers * * ARM Generic Interrupt Controller Architecture Specification * GIC architecture version 3.0 and version 4.0 * Table 8-5 */ #define ICC_AP0R0_EL1 S3_0_C12_C8_4 #define ICC_AP0R1_EL1 S3_0_C12_C8_5 #define ICC_AP0R2_EL1 S3_0_C12_C8_6 #define ICC_AP0R3_EL1 S3_0_C12_C8_7 #define ICC_AP1R0_EL1 S3_0_C12_C9_0 #define ICC_AP1R1_EL1 S3_0_C12_C9_1 #define ICC_AP1R2_EL1 S3_0_C12_C9_2 #define ICC_AP1R3_EL1 S3_0_C12_C9_3 #define ICC_ASGI1R_EL1 S3_0_C12_C11_6 #define ICC_BPR0_EL1 S3_0_C12_C8_3 #define ICC_BPR1_EL1 S3_0_C12_C12_3 #define ICC_CTLR_EL1 S3_0_C12_C12_4 #define ICC_CTLR_EL3 S3_6_C12_C12_4 #define ICC_DIR_EL1 S3_0_C12_C11_1 #define ICC_EOIR0_EL1 S3_0_C12_C8_1 #define ICC_EOIR1_EL1 S3_0_C12_C12_1 #define ICC_HPPIR0_EL1 S3_0_C12_C8_2 #define ICC_HPPIR1_EL1 S3_0_C12_C12_2 #define ICC_IAR0_EL1 S3_0_C12_C8_0 #define ICC_IAR1_EL1 S3_0_C12_C12_0 #define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 #define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 #define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 #define ICC_PMR_EL1 S3_0_C4_C6_0 #define ICC_RPR_EL1 S3_0_C12_C11_3 #define ICC_SGI0R_EL1 S3_0_C12_C11_7 #define ICC_SGI1R_EL1 S3_0_C12_C11_5 #define ICC_SRE_EL1 S3_0_C12_C12_5 #define ICC_SRE_EL2 S3_4_C12_C9_5 #define ICC_SRE_EL3 S3_6_C12_C12_5 /* * Mapping of MSR and MRS to virtual interface control registers * * ARM Generic Interrupt Controller Architecture Specification * GIC architecture version 3.0 and version 4.0 * Table 8-6 */ #define ICH_AP0R0_EL2 S3_4_C12_C8_0 #define ICH_AP0R1_EL2 S3_4_C12_C8_1 #define ICH_AP0R2_EL2 S3_4_C12_C8_2 #define ICH_AP0R3_EL2 S3_4_C12_C8_3 #define ICH_AP1R0_EL2 S3_4_C12_C9_0 #define ICH_AP1R1_EL2 S3_4_C12_C9_1 #define ICH_AP1R2_EL2 S3_4_C12_C9_2 #define ICH_AP1R3_EL2 S3_4_C12_C9_3 #define ICH_HCR_EL2 S3_4_C12_C11_0 #define ICH_VTR_EL2 S3_4_C12_C11_1 #define ICH_MISR_EL2 S3_4_C12_C11_2 #define ICH_EISR_EL2 S3_4_C12_C11_3 #define ICH_ELRSR_EL2 S3_4_C12_C11_5 #define ICH_VMCR_EL2 S3_4_C12_C11_7 #define ICH_LR0_EL2 S3_4_C12_C12_0 #define ICH_LR1_EL2 S3_4_C12_C12_1 #define ICH_LR2_EL2 S3_4_C12_C12_2 #define ICH_LR3_EL2 S3_4_C12_C12_3 #define ICH_LR4_EL2 S3_4_C12_C12_4 #define ICH_LR5_EL2 S3_4_C12_C12_5 #define ICH_LR6_EL2 S3_4_C12_C12_6 #define ICH_LR7_EL2 S3_4_C12_C12_7 #define ICH_LR8_EL2 S3_4_C12_C13_0 #define ICH_LR9_EL2 S3_4_C12_C13_1 #define ICH_LR10_EL2 S3_4_C12_C13_2 #define ICH_LR11_EL2 S3_4_C12_C13_3 #define ICH_LR12_EL2 S3_4_C12_C13_4 #define ICH_LR13_EL2 S3_4_C12_C13_5 #define ICH_LR14_EL2 S3_4_C12_C13_6 #define ICH_LR15_EL2 S3_4_C12_C13_7 #endif ================================================ FILE: kernel/arch/aarch64/include/asm/io.h ================================================ /* * Based on arch/arm/include/asm/io.h * * Copyright (C) 1996-2000 Russell King * Copyright (C) 2012 ARM Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef _MINOS_IO_H_ #define _MINOS_IO_H_ #include /* * Generic IO read/write. These perform native-endian accesses. */ #define __raw_writeb __raw_writeb static inline void __raw_writeb(uint8_t val, volatile void *addr) { asm volatile("strb %w0, [%1]" : : "r" (val), "r" (addr)); } #define __raw_writew __raw_writew static inline void __raw_writew(uint16_t val, volatile void *addr) { asm volatile("strh %w0, [%1]" : : "r" (val), "r" (addr)); } #define __raw_writel __raw_writel static inline void __raw_writel(uint32_t val, volatile void *addr) { asm volatile("str %w0, [%1]" : : "r" (val), "r" (addr)); } #define __raw_writeq __raw_writeq static inline void __raw_writeq(uint64_t val, volatile void *addr) { asm volatile("str %0, [%1]" : : "r" (val), "r" (addr)); } #define __raw_readb __raw_readb static inline uint8_t __raw_readb(const volatile void *addr) { uint8_t val; asm volatile("ldrb %w0, [%1]" : "=r" (val) : "r" (addr)); return val; } #define __raw_readw __raw_readw static inline uint16_t __raw_readw(const volatile void *addr) { uint16_t val; asm volatile("ldarh %w0, [%1]" : "=r" (val) : "r" (addr)); return val; } #define __raw_readl __raw_readl static inline uint32_t __raw_readl(const volatile void *addr) { uint32_t val; asm volatile("ldar %w0, [%1]" : "=r" (val) : "r" (addr)); return val; } #define __raw_readq __raw_readq static inline uint64_t __raw_readq(const volatile void *addr) { uint64_t val; asm volatile("ldar %0, [%1]" : "=r" (val) : "r" (addr)); return val; } /* * Relaxed I/O memory access primitives. These follow the Device memory * ordering rules but do not guarantee any ordering relative to Normal memory * accesses. */ #define readb_relaxed(c) ({ uint8_t __v = __raw_readb(c); iormb(); __v; }) #define readw_relaxed(c) ({ uint16_t __v = __raw_readw(c); iormb(); __v; }) #define readl_relaxed(c) ({ uint32_t __v = __raw_readl(c); iormb(); __v; }) #define readq_relaxed(c) ({ uint64_t __v = __raw_readq(c); iormb(); __v; }) #define writeb_relaxed(v,c) ({ iowmb(); (void)__raw_writeb((v), (c)); }) #define writew_relaxed(v,c) ({ iowmb(); (void)__raw_writew((v), (c)); }) #define writel_relaxed(v,c) ({ iowmb(); (void)__raw_writel((v), (c)); }) #define writeq_relaxed(v,c) ({ iowmb(); (void)__raw_writeq((v), (c)); }) #define ioread8(addr) readb_relaxed(addr) #define ioread16(addr) readw_relaxed(addr) #define ioread32(addr) readl_relaxed(addr) #define ioread64(addr) readq_relaxed(addr) #define iowrite8(v, addr) writeb_relaxed((v), (addr)) #define iowrite16(v, addr) writew_relaxed((v), (addr)) #define iowrite32(v, addr) writel_relaxed((v), (addr)) #define iowrite64(v, addr) writeq_relaxed((v), (addr)) #define readb(addr) readb_relaxed(addr) #define readw(addr) readw_relaxed(addr) #define readl(addr) readl_relaxed(addr) #define readq(addr) readq_relaxed(addr) #define writeb(v, addr) writeb_relaxed((v), (addr)) #define writew(v, addr) writew_relaxed((v), (addr)) #define writel(v, addr) writel_relaxed((v), (addr)) #define writeq(v, addr) writeq_relaxed((v), (addr)) #endif ================================================ FILE: kernel/arch/aarch64/include/asm/power.h ================================================ #ifndef __MINOS_CPU_H__ #define __MINOS_CPU_H__ #include int psci_cpu_on(unsigned long cpu, unsigned long entry); int psci_cpu_off(unsigned long cpu); void psci_system_reboot(int mode, const char *cmd); void psci_system_shutdown(void); int psci_cpu_on_hvc(unsigned long cpu, unsigned long entry); int psci_cpu_off_hvc(unsigned long cpu); void psci_system_reboot_hvc(int mode, const char *cmd); void psci_system_shutdown_hvc(void); int spin_table_cpu_on(unsigned long affinity, unsigned long entry); #endif ================================================ FILE: kernel/arch/aarch64/include/asm/psci.h ================================================ /* * ARM Power State and Coordination Interface (PSCI) header * * This header holds common PSCI defines and macros shared * by: ARM kernel, ARM64 kernel, KVM ARM/ARM64 and user space. * * Copyright (C) 2014 Linaro Ltd. * Author: Anup Patel */ #ifndef _MINOS_PSCI_H_ #define _MINOS_PSCI_H_ #define PSCI_0_2_FN_BASE (0x84000000) #define PSCI_0_2_FN(n) (PSCI_0_2_FN_BASE + (n)) #define PSCI_0_2_64BIT (0x40000000) #define PSCI_0_2_FN64_BASE \ (PSCI_0_2_FN_BASE + PSCI_0_2_64BIT) #define PSCI_0_2_FN64(n) (PSCI_0_2_FN64_BASE + (n)) #define PSCI_0_2_FN_PSCI_VERSION PSCI_0_2_FN(0) #define PSCI_0_2_FN_CPU_SUSPEND PSCI_0_2_FN(1) #define PSCI_0_2_FN_CPU_OFF PSCI_0_2_FN(2) #define PSCI_0_2_FN_CPU_ON PSCI_0_2_FN(3) #define PSCI_0_2_FN_AFFINITY_INFO PSCI_0_2_FN(4) #define PSCI_0_2_FN_MIGRATE PSCI_0_2_FN(5) #define PSCI_0_2_FN_MIGRATE_INFO_TYPE PSCI_0_2_FN(6) #define PSCI_0_2_FN_MIGRATE_INFO_UP_CPU PSCI_0_2_FN(7) #define PSCI_0_2_FN_SYSTEM_OFF PSCI_0_2_FN(8) #define PSCI_0_2_FN_SYSTEM_RESET PSCI_0_2_FN(9) #define PSCI_0_2_FN64_CPU_SUSPEND PSCI_0_2_FN64(1) #define PSCI_0_2_FN64_CPU_ON PSCI_0_2_FN64(3) #define PSCI_0_2_FN64_AFFINITY_INFO PSCI_0_2_FN64(4) #define PSCI_0_2_FN64_MIGRATE PSCI_0_2_FN64(5) #define PSCI_0_2_FN64_MIGRATE_INFO_UP_CPU PSCI_0_2_FN64(7) #define PSCI_1_0_FN_PSCI_FEATURES PSCI_0_2_FN(10) #define PSCI_1_0_FN_SYSTEM_SUSPEND PSCI_0_2_FN(14) #define PSCI_1_0_FN64_SYSTEM_SUSPEND PSCI_0_2_FN64(14) #define ARM_SMCCC_VERSION_FUNC_ID 0x80000000 /* PSCI v0.2 power state encoding for CPU_SUSPEND function */ #define PSCI_0_2_POWER_STATE_ID_MASK 0xffff #define PSCI_0_2_POWER_STATE_ID_SHIFT 0 #define PSCI_0_2_POWER_STATE_TYPE_SHIFT 16 #define PSCI_0_2_POWER_STATE_TYPE_MASK \ (0x1 << PSCI_0_2_POWER_STATE_TYPE_SHIFT) #define PSCI_0_2_POWER_STATE_AFFL_SHIFT 24 #define PSCI_0_2_POWER_STATE_AFFL_MASK \ (0x3 << PSCI_0_2_POWER_STATE_AFFL_SHIFT) /* PSCI extended power state encoding for CPU_SUSPEND function */ #define PSCI_1_0_EXT_POWER_STATE_ID_MASK 0xfffffff #define PSCI_1_0_EXT_POWER_STATE_ID_SHIFT 0 #define PSCI_1_0_EXT_POWER_STATE_TYPE_SHIFT 30 #define PSCI_1_0_EXT_POWER_STATE_TYPE_MASK \ (0x1 << PSCI_1_0_EXT_POWER_STATE_TYPE_SHIFT) /* PSCI v0.2 affinity level state returned by AFFINITY_INFO */ #define PSCI_0_2_AFFINITY_LEVEL_ON 0 #define PSCI_0_2_AFFINITY_LEVEL_OFF 1 #define PSCI_0_2_AFFINITY_LEVEL_ON_PENDING 2 /* PSCI v0.2 multicore support in Trusted OS returned by MIGRATE_INFO_TYPE */ #define PSCI_0_2_TOS_UP_MIGRATE 0 #define PSCI_0_2_TOS_UP_NO_MIGRATE 1 #define PSCI_0_2_TOS_MP 2 /* PSCI version decoding (independent of PSCI version) */ #define PSCI_VERSION_MAJOR_SHIFT 16 #define PSCI_VERSION_MINOR_MASK \ ((1U << PSCI_VERSION_MAJOR_SHIFT) - 1) #define PSCI_VERSION_MAJOR_MASK ~PSCI_VERSION_MINOR_MASK #define PSCI_VERSION_MAJOR(ver) \ (((ver) & PSCI_VERSION_MAJOR_MASK) >> PSCI_VERSION_MAJOR_SHIFT) #define PSCI_VERSION_MINOR(ver) \ ((ver) & PSCI_VERSION_MINOR_MASK) /* PSCI features decoding (>=1.0) */ #define PSCI_1_0_FEATURES_CPU_SUSPEND_PF_SHIFT 1 #define PSCI_1_0_FEATURES_CPU_SUSPEND_PF_MASK \ (0x1 << PSCI_1_0_FEATURES_CPU_SUSPEND_PF_SHIFT) /* PSCI return values (inclusive of all PSCI versions) */ #define PSCI_RET_SUCCESS 0 #define PSCI_RET_NOT_SUPPORTED -1 #define PSCI_RET_INVALID_PARAMS -2 #define PSCI_RET_DENIED -3 #define PSCI_RET_ALREADY_ON -4 #define PSCI_RET_ON_PENDING -5 #define PSCI_RET_INTERNAL_FAILURE -6 #define PSCI_RET_NOT_PRESENT -7 #define PSCI_RET_DISABLED -8 #define PSCI_RET_INVALID_ADDRESS -9 #define PSCI_VERSION(major, minor) \ (0 | (major << PSCI_VERSION_MAJOR_SHIFT) | (minor)) #endif ================================================ FILE: kernel/arch/aarch64/include/asm/reg.h ================================================ #ifndef _MINOS_PROCESSER_H_ #define _MINOS_PROCESSER_H_ #include /* ESR.EC == ESR_CP{15,14,10}_32 */ #define HSR_CP32_OP2_MASK (0x000e0000) #define HSR_CP32_OP2_SHIFT (17) #define HSR_CP32_OP1_MASK (0x0001c000) #define HSR_CP32_OP1_SHIFT (14) #define HSR_CP32_CRN_MASK (0x00003c00) #define HSR_CP32_CRN_SHIFT (10) #define HSR_CP32_CRM_MASK (0x0000001e) #define HSR_CP32_CRM_SHIFT (1) #define HSR_CP32_REGS_MASK (HSR_CP32_OP1_MASK|HSR_CP32_OP2_MASK|\ HSR_CP32_CRN_MASK|HSR_CP32_CRM_MASK) /* ESR.EC == ESR_CP{15,14}_64 */ #define HSR_CP64_OP1_MASK (0x000f0000) #define HSR_CP64_OP1_SHIFT (16) #define HSR_CP64_CRM_MASK (0x0000001e) #define HSR_CP64_CRM_SHIFT (1) #define HSR_CP64_REGS_MASK (HSR_CP64_OP1_MASK|HSR_CP64_CRM_MASK) /* ESR.EC == ESR_SYSREG */ #define ESR_SYSREG_OP0_MASK (0x00300000) #define ESR_SYSREG_OP0_SHIFT (20) #define ESR_SYSREG_OP1_MASK (0x0001c000) #define ESR_SYSREG_OP1_SHIFT (14) #define ESR_SYSREG_CRN_MASK (0x00003c00) #define ESR_SYSREG_CRN_SHIFT (10) #define ESR_SYSREG_CRM_MASK (0x0000001e) #define ESR_SYSREG_CRM_SHIFT (1) #define ESR_SYSREG_OP2_MASK (0x000e0000) #define ESR_SYSREG_OP2_SHIFT (17) #define ESR_SYSREG_REGS_MASK (ESR_SYSREG_OP0_MASK|ESR_SYSREG_OP1_MASK|\ ESR_SYSREG_CRN_MASK|ESR_SYSREG_CRM_MASK|\ ESR_SYSREG_OP2_MASK) /* ESR.EC == ESR_{HVC32, HVC64, SMC64, SVC32, SVC64} */ #define ESR_XXC_IMM_MASK (0xffff) #define __ESR_SYSREG_c0 0 #define __ESR_SYSREG_c1 1 #define __ESR_SYSREG_c2 2 #define __ESR_SYSREG_c3 3 #define __ESR_SYSREG_c4 4 #define __ESR_SYSREG_c5 5 #define __ESR_SYSREG_c6 6 #define __ESR_SYSREG_c7 7 #define __ESR_SYSREG_c8 8 #define __ESR_SYSREG_c9 9 #define __ESR_SYSREG_c10 10 #define __ESR_SYSREG_c11 11 #define __ESR_SYSREG_c12 12 #define __ESR_SYSREG_c13 13 #define __ESR_SYSREG_c14 14 #define __ESR_SYSREG_c15 15 #define __ESR_SYSREG_0 0 #define __ESR_SYSREG_1 1 #define __ESR_SYSREG_2 2 #define __ESR_SYSREG_3 3 #define __ESR_SYSREG_4 4 #define __ESR_SYSREG_5 5 #define __ESR_SYSREG_6 6 #define __ESR_SYSREG_7 7 /* These are used to decode traps with ESR.EC==ESR_EC_SYSREG */ #define ESR_SYSREG(op0,op1,crn,crm,op2) \ (((__ESR_SYSREG_##op0) << ESR_SYSREG_OP0_SHIFT) | \ ((__ESR_SYSREG_##op1) << ESR_SYSREG_OP1_SHIFT) | \ ((__ESR_SYSREG_##crn) << ESR_SYSREG_CRN_SHIFT) | \ ((__ESR_SYSREG_##crm) << ESR_SYSREG_CRM_SHIFT) | \ ((__ESR_SYSREG_##op2) << ESR_SYSREG_OP2_SHIFT)) #define ESR_SYSREG_DCISW ESR_SYSREG(1,0,c7,c6,2) #define ESR_SYSREG_DCCSW ESR_SYSREG(1,0,c7,c10,2) #define ESR_SYSREG_DCCISW ESR_SYSREG(1,0,c7,c14,2) #define ESR_SYSREG_DCZVA ESR_SYSREG(1,3,c7,c4,1) #define ESR_SYSREG_MDSCR_EL1 ESR_SYSREG(2,0,c0,c2,2) #define ESR_SYSREG_MDRAR_EL1 ESR_SYSREG(2,0,c1,c0,0) #define ESR_SYSREG_OSLAR_EL1 ESR_SYSREG(2,0,c1,c0,4) #define ESR_SYSREG_OSLSR_EL1 ESR_SYSREG(2,0,c1,c1,4) #define ESR_SYSREG_OSDLR_EL1 ESR_SYSREG(2,0,c1,c3,4) #define ESR_SYSREG_DBGPRCR_EL1 ESR_SYSREG(2,0,c1,c4,4) #define ESR_SYSREG_MDCCSR_EL0 ESR_SYSREG(2,3,c0,c1,0) #define ESR_SYSREG_DBGBVRn_EL1(n) ESR_SYSREG(2,0,c0,c##n,4) #define ESR_SYSREG_DBGBCRn_EL1(n) ESR_SYSREG(2,0,c0,c##n,5) #define ESR_SYSREG_DBGWVRn_EL1(n) ESR_SYSREG(2,0,c0,c##n,6) #define ESR_SYSREG_DBGWCRn_EL1(n) ESR_SYSREG(2,0,c0,c##n,7) #define ESR_SYSREG_DBG_CASES(REG) case ESR_SYSREG_##REG##n_EL1(0): \ case ESR_SYSREG_##REG##n_EL1(1): \ case ESR_SYSREG_##REG##n_EL1(2): \ case ESR_SYSREG_##REG##n_EL1(3): \ case ESR_SYSREG_##REG##n_EL1(4): \ case ESR_SYSREG_##REG##n_EL1(5): \ case ESR_SYSREG_##REG##n_EL1(6): \ case ESR_SYSREG_##REG##n_EL1(7): \ case ESR_SYSREG_##REG##n_EL1(8): \ case ESR_SYSREG_##REG##n_EL1(9): \ case ESR_SYSREG_##REG##n_EL1(10): \ case ESR_SYSREG_##REG##n_EL1(11): \ case ESR_SYSREG_##REG##n_EL1(12): \ case ESR_SYSREG_##REG##n_EL1(13): \ case ESR_SYSREG_##REG##n_EL1(14): \ case ESR_SYSREG_##REG##n_EL1(15) #define ESR_SYSREG_SCTLR_EL1 ESR_SYSREG(3,0,c1, c0,0) #define ESR_SYSREG_ACTLR_EL1 ESR_SYSREG(3,0,c1, c0,1) #define ESR_SYSREG_TTBR0_EL1 ESR_SYSREG(3,0,c2, c0,0) #define ESR_SYSREG_TTBR1_EL1 ESR_SYSREG(3,0,c2, c0,1) #define ESR_SYSREG_TCR_EL1 ESR_SYSREG(3,0,c2, c0,2) #define ESR_SYSREG_AFSR0_EL1 ESR_SYSREG(3,0,c5, c1,0) #define ESR_SYSREG_AFSR1_EL1 ESR_SYSREG(3,0,c5, c1,1) #define ESR_SYSREG_ESR_EL1 ESR_SYSREG(3,0,c5, c2,0) #define ESR_SYSREG_FAR_EL1 ESR_SYSREG(3,0,c6, c0,0) #define ESR_SYSREG_PMINTENSET_EL1 ESR_SYSREG(3,0,c9,c14,1) #define ESR_SYSREG_PMINTENCLR_EL1 ESR_SYSREG(3,0,c9,c14,2) #define ESR_SYSREG_MAIR_EL1 ESR_SYSREG(3,0,c10,c2,0) #define ESR_SYSREG_AMAIR_EL1 ESR_SYSREG(3,0,c10,c3,0) #define ESR_SYSREG_ICC_SGI1R_EL1 ESR_SYSREG(3,0,c12,c11,5) #define ESR_SYSREG_ICC_ASGI1R_EL1 ESR_SYSREG(3,1,c12,c11,6) #define ESR_SYSREG_ICC_SGI0R_EL1 ESR_SYSREG(3,2,c12,c11,7) #define ESR_SYSREG_ICC_SRE_EL1 ESR_SYSREG(3,0,c12,c12,5) #define ESR_SYSREG_CONTEXTIDR_EL1 ESR_SYSREG(3,0,c13,c0,1) #define ESR_SYSREG_PMCR_EL0 ESR_SYSREG(3,3,c9,c12,0) #define ESR_SYSREG_PMCNTENSET_EL0 ESR_SYSREG(3,3,c9,c12,1) #define ESR_SYSREG_PMCNTENCLR_EL0 ESR_SYSREG(3,3,c9,c12,2) #define ESR_SYSREG_PMOVSCLR_EL0 ESR_SYSREG(3,3,c9,c12,3) #define ESR_SYSREG_PMSWINC_EL0 ESR_SYSREG(3,3,c9,c12,4) #define ESR_SYSREG_PMSELR_EL0 ESR_SYSREG(3,3,c9,c12,5) #define ESR_SYSREG_PMCEID0_EL0 ESR_SYSREG(3,3,c9,c12,6) #define ESR_SYSREG_PMCEID1_EL0 ESR_SYSREG(3,3,c9,c12,7) #define ESR_SYSREG_PMCCNTR_EL0 ESR_SYSREG(3,3,c9,c13,0) #define ESR_SYSREG_PMXEVTYPER_EL0 ESR_SYSREG(3,3,c9,c13,1) #define ESR_SYSREG_PMXEVCNTR_EL0 ESR_SYSREG(3,3,c9,c13,2) #define ESR_SYSREG_PMUSERENR_EL0 ESR_SYSREG(3,3,c9,c14,0) #define ESR_SYSREG_PMOVSSET_EL0 ESR_SYSREG(3,3,c9,c14,3) #define ESR_SYSREG_CNTPCT_EL0 ESR_SYSREG(3,3,c14,c0,0) #define ESR_SYSREG_CNTP_TVAL_EL0 ESR_SYSREG(3,3,c14,c2,0) #define ESR_SYSREG_CNTP_CTL_EL0 ESR_SYSREG(3,3,c14,c2,1) #define ESR_SYSREG_CNTP_CVAL_EL0 ESR_SYSREG(3,3,c14,c2,2) #define ESR_SYSREG_ASOC_HID11 ESR_SYSREG(3,0,c15,c13,0) #define ESR_SYSREG_ASOC_HID5 ESR_SYSREG(3,0,c15,c5,0) #define ESR_SYSREG_ASOC_HID4 ESR_SYSREG(3,0,c15,c4,0) #define ESR_SYSREG_ASOC_HID8 ESR_SYSREG(3,0,c15,c8,0) #define ESR_SYSREG_ASOC_HID7 ESR_SYSREG(3,0,c15,c7,0) #define ESR_SYSREG_ASOC_LSU_ERR_STS ESR_SYSREG(3,3,c15,c0,0) #define ESR_SYSREG_ASOC_PMC0 ESR_SYSREG(3,2,c15,c0,0) #define ESR_SYSREG_ASOC_PMC1 ESR_SYSREG(3,2,c15,c1,0) #define ESR_SYSREG_ASOC_PMCR1 ESR_SYSREG(3,1,c15,c1,0) #define ESR_SYSREG_ASOC_PMSR ESR_SYSREG(3,1,c15,c13,0) /* * AArch32 Co-processor registers. * * Note that AArch64 requires many of these definitions in order to * support 32-bit guests. */ #define __HSR_CPREG_c0 0 #define __HSR_CPREG_c1 1 #define __HSR_CPREG_c2 2 #define __HSR_CPREG_c3 3 #define __HSR_CPREG_c4 4 #define __HSR_CPREG_c5 5 #define __HSR_CPREG_c6 6 #define __HSR_CPREG_c7 7 #define __HSR_CPREG_c8 8 #define __HSR_CPREG_c9 9 #define __HSR_CPREG_c10 10 #define __HSR_CPREG_c11 11 #define __HSR_CPREG_c12 12 #define __HSR_CPREG_c13 13 #define __HSR_CPREG_c14 14 #define __HSR_CPREG_c15 15 #define __HSR_CPREG_0 0 #define __HSR_CPREG_1 1 #define __HSR_CPREG_2 2 #define __HSR_CPREG_3 3 #define __HSR_CPREG_4 4 #define __HSR_CPREG_5 5 #define __HSR_CPREG_6 6 #define __HSR_CPREG_7 7 #define _HSR_CPREG32(cp,op1,crn,crm,op2) \ ((__HSR_CPREG_##crn) << HSR_CP32_CRN_SHIFT) | \ ((__HSR_CPREG_##crm) << HSR_CP32_CRM_SHIFT) | \ ((__HSR_CPREG_##op1) << HSR_CP32_OP1_SHIFT) | \ ((__HSR_CPREG_##op2) << HSR_CP32_OP2_SHIFT) #define _HSR_CPREG64(cp,op1,crm) \ ((__HSR_CPREG_##crm) << HSR_CP64_CRM_SHIFT) | \ ((__HSR_CPREG_##op1) << HSR_CP64_OP1_SHIFT) /* Encode a register as per HSR ISS pattern */ #define HSR_CPREG32(X) _HSR_CPREG32(X) #define HSR_CPREG64(X) _HSR_CPREG64(X) /* * Order registers by Coprocessor-> CRn-> Opcode 1-> CRm-> Opcode 2 * * This matches the ordering used in the ARM as well as the groupings * which the CP registers are allocated in. * * This is slightly different to the form of the instruction * arguments, which are cp,opc1,crn,crm,opc2. */ /* Coprocessor 10 */ #define FPSID p10,7,c0,c0,0 /* Floating-Point System ID Register */ #define FPSCR p10,7,c1,c0,0 /* Floating-Point Status and Control Register */ #define MVFR0 p10,7,c7,c0,0 /* Media and VFP Feature Register 0 */ #define FPEXC p10,7,c8,c0,0 /* Floating-Point Exception Control Register */ #define FPINST p10,7,c9,c0,0 /* Floating-Point Instruction Register */ #define FPINST2 p10,7,c10,c0,0 /* Floating-point Instruction Register 2 */ /* Coprocessor 14 */ /* CP14 0: Debug Register interface */ #define DBGDIDR p14,0,c0,c0,0 /* Debug ID Register */ #define DBGDSCRINT p14,0,c0,c1,0 /* Debug Status and Control Internal */ #define DBGDSCREXT p14,0,c0,c2,2 /* Debug Status and Control External */ #define DBGVCR p14,0,c0,c7,0 /* Vector Catch */ #define DBGBVR0 p14,0,c0,c0,4 /* Breakpoint Value 0 */ #define DBGBCR0 p14,0,c0,c0,5 /* Breakpoint Control 0 */ #define DBGWVR0 p14,0,c0,c0,6 /* Watchpoint Value 0 */ #define DBGWCR0 p14,0,c0,c0,7 /* Watchpoint Control 0 */ #define DBGBVR1 p14,0,c0,c1,4 /* Breakpoint Value 1 */ #define DBGBCR1 p14,0,c0,c1,5 /* Breakpoint Control 1 */ #define DBGOSLAR p14,0,c1,c0,4 /* OS Lock Access */ #define DBGOSLSR p14,0,c1,c1,4 /* OS Lock Status Register */ #define DBGOSDLR p14,0,c1,c3,4 /* OS Double Lock */ #define DBGPRCR p14,0,c1,c4,4 /* Debug Power Control Register */ /* CP14 CR0: */ #define TEECR p14,6,c0,c0,0 /* ThumbEE Configuration Register */ /* CP14 CR1: */ #define DBGDRAR64 p14,0,c1 /* Debug ROM Address Register (64-bit access) */ #define DBGDRAR p14,0,c1,c0,0 /* Debug ROM Address Register (32-bit access) */ #define TEEHBR p14,6,c1,c0,0 /* ThumbEE Handler Base Register */ #define JOSCR p14,7,c1,c0,0 /* Jazelle OS Control Register */ /* CP14 CR2: */ #define DBGDSAR64 p14,0,c2 /* Debug Self Address Offset Register (64-bit access) */ #define DBGDSAR p14,0,c2,c0,0 /* Debug Self Address Offset Register (32-bit access) */ #define JMCR p14,7,c2,c0,0 /* Jazelle Main Configuration Register */ /* Coprocessor 15 */ /* CP15 CR0: CPUID and Cache Type Registers */ #define MIDR p15,0,c0,c0,0 /* Main ID Register */ #define MPIDR p15,0,c0,c0,5 /* Multiprocessor Affinity Register */ #define ID_PFR0 p15,0,c0,c1,0 /* Processor Feature Register 0 */ #define ID_PFR1 p15,0,c0,c1,1 /* Processor Feature Register 1 */ #define ID_DFR0 p15,0,c0,c1,2 /* Debug Feature Register 0 */ #define ID_AFR0 p15,0,c0,c1,3 /* Auxiliary Feature Register 0 */ #define ID_MMFR0 p15,0,c0,c1,4 /* Memory Model Feature Register 0 */ #define ID_MMFR1 p15,0,c0,c1,5 /* Memory Model Feature Register 1 */ #define ID_MMFR2 p15,0,c0,c1,6 /* Memory Model Feature Register 2 */ #define ID_MMFR3 p15,0,c0,c1,7 /* Memory Model Feature Register 3 */ #define ID_ISAR0 p15,0,c0,c2,0 /* ISA Feature Register 0 */ #define ID_ISAR1 p15,0,c0,c2,1 /* ISA Feature Register 1 */ #define ID_ISAR2 p15,0,c0,c2,2 /* ISA Feature Register 2 */ #define ID_ISAR3 p15,0,c0,c2,3 /* ISA Feature Register 3 */ #define ID_ISAR4 p15,0,c0,c2,4 /* ISA Feature Register 4 */ #define ID_ISAR5 p15,0,c0,c2,5 /* ISA Feature Register 5 */ #define CCSIDR p15,1,c0,c0,0 /* Cache Size ID Registers */ #define CLIDR p15,1,c0,c0,1 /* Cache Level ID Register */ #define CSSELR p15,2,c0,c0,0 /* Cache Size Selection Register */ #define VPIDR p15,4,c0,c0,0 /* Virtualization Processor ID Register */ #define VMPIDR p15,4,c0,c0,5 /* Virtualization Multiprocessor ID Register */ /* CP15 CR1: System Control Registers */ #define SCTLR p15,0,c1,c0,0 /* System Control Register */ #define ACTLR p15,0,c1,c0,1 /* Auxiliary Control Register */ #define CPACR p15,0,c1,c0,2 /* Coprocessor Access Control Register */ #define SCR p15,0,c1,c1,0 /* Secure Configuration Register */ #define NSACR p15,0,c1,c1,2 /* Non-Secure Access Control Register */ #define HSCTLR p15,4,c1,c0,0 /* Hyp. System Control Register */ #define HCR p15,4,c1,c1,0 /* Hyp. Configuration Register */ #define HDCR p15,4,c1,c1,1 /* Hyp. Debug Configuration Register */ #define HCPTR p15,4,c1,c1,2 /* Hyp. Coprocessor Trap Register */ #define HSTR p15,4,c1,c1,3 /* Hyp. System Trap Register */ /* CP15 CR2: Translation Table Base and Control Registers */ #define TTBCR p15,0,c2,c0,2 /* Translatation Table Base Control Register */ #define TTBR0 p15,0,c2 /* Translation Table Base Reg. 0 */ #define TTBR1 p15,1,c2 /* Translation Table Base Reg. 1 */ #define HTTBR p15,4,c2 /* Hyp. Translation Table Base Register */ #define TTBR0_32 p15,0,c2,c0,0 /* 32-bit access to TTBR0 */ #define TTBR1_32 p15,0,c2,c0,1 /* 32-bit access to TTBR1 */ #define HTCR p15,4,c2,c0,2 /* Hyp. Translation Control Register */ #define VTCR p15,4,c2,c1,2 /* Virtualization Translation Control Register */ #define VTTBR p15,6,c2 /* Virtualization Translation Table Base Register */ /* CP15 CR3: Domain Access Control Register */ #define DACR p15,0,c3,c0,0 /* Domain Access Control Register */ /* CP15 CR4: */ /* CP15 CR5: Fault Status Registers */ #define DFSR p15,0,c5,c0,0 /* Data Fault Status Register */ #define IFSR p15,0,c5,c0,1 /* Instruction Fault Status Register */ #define ADFSR p15,0,c5,c1,0 /* Auxiliary Data Fault Status Register */ #define AIFSR p15,0,c5,c1,1 /* Auxiliary Instruction Fault Status Register */ #define HSR p15,4,c5,c2,0 /* Hyp. Syndrome Register */ /* CP15 CR6: Fault Address Registers */ #define DFAR p15,0,c6,c0,0 /* Data Fault Address Register */ #define IFAR p15,0,c6,c0,2 /* Instruction Fault Address Register */ #define HDFAR p15,4,c6,c0,0 /* Hyp. Data Fault Address Register */ #define HIFAR p15,4,c6,c0,2 /* Hyp. Instruction Fault Address Register */ #define HPFAR p15,4,c6,c0,4 /* Hyp. IPA Fault Address Register */ /* CP15 CR7: Cache and address translation operations */ #define PAR p15,0,c7 /* Physical Address Register */ #define ICIALLUIS p15,0,c7,c1,0 /* Invalidate all instruction caches to PoU inner shareable */ #define BPIALLIS p15,0,c7,c1,6 /* Invalidate entire branch predictor array inner shareable */ #define ICIALLU p15,0,c7,c5,0 /* Invalidate all instruction caches to PoU */ #define ICIMVAU p15,0,c7,c5,1 /* Invalidate instruction caches by MVA to PoU */ #define BPIALL p15,0,c7,c5,6 /* Invalidate entire branch predictor array */ #define BPIMVA p15,0,c7,c5,7 /* Invalidate MVA from branch predictor array */ #define DCIMVAC p15,0,c7,c6,1 /* Invalidate data cache line by MVA to PoC */ #define DCISW p15,0,c7,c6,2 /* Invalidate data cache line by set/way */ #define ATS1CPR p15,0,c7,c8,0 /* Address Translation Stage 1. Non-Secure Kernel Read */ #define ATS1CPW p15,0,c7,c8,1 /* Address Translation Stage 1. Non-Secure Kernel Write */ #define ATS1CUR p15,0,c7,c8,2 /* Address Translation Stage 1. Non-Secure User Read */ #define ATS1CUW p15,0,c7,c8,3 /* Address Translation Stage 1. Non-Secure User Write */ #define ATS12NSOPR p15,0,c7,c8,4 /* Address Translation Stage 1+2 Non-Secure Kernel Read */ #define ATS12NSOPW p15,0,c7,c8,5 /* Address Translation Stage 1+2 Non-Secure Kernel Write */ #define ATS12NSOUR p15,0,c7,c8,6 /* Address Translation Stage 1+2 Non-Secure User Read */ #define ATS12NSOUW p15,0,c7,c8,7 /* Address Translation Stage 1+2 Non-Secure User Write */ #define DCCMVAC p15,0,c7,c10,1 /* Clean data or unified cache line by MVA to PoC */ #define DCCSW p15,0,c7,c10,2 /* Clean data cache line by set/way */ #define DCCMVAU p15,0,c7,c11,1 /* Clean data cache line by MVA to PoU */ #define DCCIMVAC p15,0,c7,c14,1 /* Data cache clean and invalidate by MVA */ #define DCCISW p15,0,c7,c14,2 /* Clean and invalidate data cache line by set/way */ #define ATS1HR p15,4,c7,c8,0 /* Address Translation Stage 1 Hyp. Read */ #define ATS1HW p15,4,c7,c8,1 /* Address Translation Stage 1 Hyp. Write */ /* CP15 CR8: TLB maintenance operations */ #define TLBIALLIS p15,0,c8,c3,0 /* Invalidate entire TLB innrer shareable */ #define TLBIMVAIS p15,0,c8,c3,1 /* Invalidate unified TLB entry by MVA inner shareable */ #define TLBIASIDIS p15,0,c8,c3,2 /* Invalidate unified TLB by ASID match inner shareable */ #define TLBIMVAAIS p15,0,c8,c3,3 /* Invalidate unified TLB entry by MVA all ASID inner shareable */ #define ITLBIALL p15,0,c8,c5,0 /* Invalidate instruction TLB */ #define ITLBIMVA p15,0,c8,c5,1 /* Invalidate instruction TLB entry by MVA */ #define ITLBIASID p15,0,c8,c5,2 /* Invalidate instruction TLB by ASID match */ #define DTLBIALL p15,0,c8,c6,0 /* Invalidate data TLB */ #define DTLBIMVA p15,0,c8,c6,1 /* Invalidate data TLB entry by MVA */ #define DTLBIASID p15,0,c8,c6,2 /* Invalidate data TLB by ASID match */ #define TLBIALL p15,0,c8,c7,0 /* invalidate unified TLB */ #define TLBIMVA p15,0,c8,c7,1 /* invalidate unified TLB entry by MVA */ #define TLBIASID p15,0,c8,c7,2 /* invalid unified TLB by ASID match */ #define TLBIMVAA p15,0,c8,c7,3 /* invalidate unified TLB entries by MVA all ASID */ #define TLBIALLHIS p15,4,c8,c3,0 /* Invalidate Entire Hyp. Unified TLB inner shareable */ #define TLBIMVAHIS p15,4,c8,c3,1 /* Invalidate Unified Hyp. TLB by MVA inner shareable */ #define TLBIALLNSNHIS p15,4,c8,c3,4 /* Invalidate Entire Non-Secure Non-Hyp. Unified TLB inner shareable */ #define TLBIALLH p15,4,c8,c7,0 /* Invalidate Entire Hyp. Unified TLB */ #define TLBIMVAH p15,4,c8,c7,1 /* Invalidate Unified Hyp. TLB by MVA */ #define TLBIALLNSNH p15,4,c8,c7,4 /* Invalidate Entire Non-Secure Non-Hyp. Unified TLB */ /* CP15 CR9: Performance monitors */ #define PMCR p15,0,c9,c12,0 /* Perf. Mon. Control Register */ #define PMCNTENSET p15,0,c9,c12,1 /* Perf. Mon. Count Enable Set register */ #define PMCNTENCLR p15,0,c9,c12,2 /* Perf. Mon. Count Enable Clear register */ #define PMOVSR p15,0,c9,c12,3 /* Perf. Mon. Overflow Flag Status Register */ #define PMSWINC p15,0,c9,c12,4 /* Perf. Mon. Software Increment register */ #define PMSELR p15,0,c9,c12,5 /* Perf. Mon. Event Counter Selection Register */ #define PMCEID0 p15,0,c9,c12,6 /* Perf. Mon. Common Event Identification register 0 */ #define PMCEID1 p15,0,c9,c12,7 /* Perf. Mon. Common Event Identification register 1 */ #define PMCCNTR p15,0,c9,c13,0 /* Perf. Mon. Cycle Count Register */ #define PMXEVTYPER p15,0,c9,c13,1 /* Perf. Mon. Event Type Select Register */ #define PMXEVCNTR p15,0,c9,c13,2 /* Perf. Mon. Event Count Register */ #define PMUSERENR p15,0,c9,c14,0 /* Perf. Mon. User Enable Register */ #define PMINTENSET p15,0,c9,c14,1 /* Perf. Mon. Interrupt Enable Set Register */ #define PMINTENCLR p15,0,c9,c14,2 /* Perf. Mon. Interrupt Enable Clear Register */ #define PMOVSSET p15,0,c9,c14,3 /* Perf. Mon. Overflow Flag Status Set register */ /* CP15 CR10: */ #define MAIR0 p15,0,c10,c2,0 /* Memory Attribute Indirection Register 0 AKA PRRR */ #define MAIR1 p15,0,c10,c2,1 /* Memory Attribute Indirection Register 1 AKA NMRR */ #define HMAIR0 p15,4,c10,c2,0 /* Hyp. Memory Attribute Indirection Register 0 */ #define HMAIR1 p15,4,c10,c2,1 /* Hyp. Memory Attribute Indirection Register 1 */ #define AMAIR0 p15,0,c10,c3,0 /* Aux. Memory Attribute Indirection Register 0 */ #define AMAIR1 p15,0,c10,c3,1 /* Aux. Memory Attribute Indirection Register 1 */ /* CP15 CR11: DMA Operations for TCM Access */ /* CP15 CR12: */ #define ICC_SGI1R p15,0,c12 /* Interrupt Controller SGI Group 1 */ #define ICC_ASGI1R p15,1,c12 /* Interrupt Controller Alias SGI Group 1 Register */ #define ICC_SGI0R p15,2,c12 /* Interrupt Controller SGI Group 0 */ #define VBAR p15,0,c12,c0,0 /* Vector Base Address Register */ #define HVBAR p15,4,c12,c0,0 /* Hyp. Vector Base Address Register */ /* CP15 CR13: */ #define FCSEIDR p15,0,c13,c0,0 /* FCSE Process ID Register */ #define CONTEXTIDR p15,0,c13,c0,1 /* Context ID Register */ #define TPIDRURW p15,0,c13,c0,2 /* Software Thread ID, User, R/W */ #define TPIDRURO p15,0,c13,c0,3 /* Software Thread ID, User, R/O */ #define TPIDRPRW p15,0,c13,c0,4 /* Software Thread ID, Priveleged */ #define HTPIDR p15,4,c13,c0,2 /* HYp Software Thread Id Register */ /* CP15 CR14: */ #define CNTPCT p15,0,c14 /* Time counter value */ #define CNTFRQ p15,0,c14,c0,0 /* Time counter frequency */ #define CNTKCTL p15,0,c14,c1,0 /* Time counter kernel control */ #define CNTP_TVAL p15,0,c14,c2,0 /* Physical Timer value */ #define CNTP_CTL p15,0,c14,c2,1 /* Physical Timer control register */ #define CNTVCT p15,1,c14 /* Time counter value + offset */ #define CNTP_CVAL p15,2,c14 /* Physical Timer comparator */ #define CNTV_CVAL p15,3,c14 /* Virt. Timer comparator */ #define CNTVOFF p15,4,c14 /* Time counter offset */ #define CNTHCTL p15,4,c14,c1,0 /* Time counter hyp. control */ #define CNTHP_TVAL p15,4,c14,c2,0 /* Hyp. Timer value */ #define CNTHP_CTL p15,4,c14,c2,1 /* Hyp. Timer control register */ #define CNTV_TVAL p15,0,c14,c3,0 /* Virt. Timer value */ #define CNTV_CTL p15,0,c14,c3,1 /* Virt. TImer control register */ #define CNTHP_CVAL p15,6,c14 /* Hyp. Timer comparator */ #endif ================================================ FILE: kernel/arch/aarch64/include/asm/svccc.h ================================================ #ifndef _MINOS_ASM_SVCCC_H_ #define _MINOS_ASM_SVCCC_H_ #include /* * ARM_DEN0028B_SMC_Calling_Convention.pdf * Table 2-1 Bit usage within the SMC and HVC Function Identifier * bit[31] : 0-yielding call 1-fast call * bit[30] : 0-smc32/hvc32 1-smc64/hvc64 * bit[29:24] : service call ranges SVC_STYPE_XX * bit[23:16] : must be zero * bit[15:0] : function number with the range call type */ #define SVC_CTYPE_MASK (0x80000000) #define SVC_BTYPE_MASK (0x40000000) #define SVC_STYPE_MASK (0x3f000000) #define SVC_FID_MASK (0x0000ffff) #define SVC_STYPE_ARCH (0x00) #define SVC_STYPE_CPU (0x01) #define SVC_STYPE_SIP (0x02) #define SVC_STYPE_OEM (0x03) #define SVC_STYPE_STDSMC (0x04) #define SVC_STYPE_STDHVC (0x05) #define SVC_STYPE_VNDHVC (0x06) #define SVC_STYPE_TRUST_APP_START (0x30) #define SVC_STYPE_TRUST_APP_END (0x31) #define SVC_STYPE_TRUST_OS_START (0x32) #define SVC_STYPE_TRUST_OS_END (0x3f) #define SVC_STYPE_MAX (64) #define SVC_RET() { \ return 0; \ } #define SVC_RET1(reg, a0) { \ set_reg_value(reg, 0, a0); \ return 0; \ } #define SVC_RET2(reg, a0, a1) { \ set_reg_value(reg, 0, a0); \ set_reg_value(reg, 1, a1); \ return 0; \ } #define SVC_RET3(reg, a0, a1, a2) { \ set_reg_value(reg, 0, a0); \ set_reg_value(reg, 1, a1); \ set_reg_value(reg, 2, a2); \ return 0; \ } #define SVC_RET4(reg, a0, a1, a2, a3) { \ set_reg_value(reg, 0, a0); \ set_reg_value(reg, 1, a1); \ set_reg_value(reg, 2, a2); \ set_reg_value(reg, 3, a3); \ return 0; \ } #define HVC_RET0() SVC_RET0() #define HVC_RET1(reg, a0) SVC_RET1(reg, a0) #define HVC_RET2(reg, a0, a1) SVC_RET2(reg, a0, a1) #define HVC_RET3(reg, a0, a1, a2) SVC_RET3(reg, a0, a1, a2) #define HVC_RET4(reg, a0, a1, a2, a3) SVC_RET4(reg, a0, a1, a2, a3) typedef int (*svc_handler_t)(gp_regs *c, uint32_t id, uint64_t *args); struct svc_desc { char *name; uint16_t type_start; uint16_t type_end; svc_handler_t handler; }; #define DEFINE_SMC_HANDLER(n, start, end, h) \ static struct svc_desc __smc_##h __used \ __section(".__smc_handler") = { \ .name = n, \ .type_start = start, \ .type_end = end, \ .handler = h, \ } #define DEFINE_HVC_HANDLER(n, start, end, h) \ static struct svc_desc __hvc_##h __used \ __section(".__hvc_handler") = { \ .name = n, \ .type_start = start, \ .type_end = end, \ .handler = h, \ } struct arm_smc_res { unsigned long a0; unsigned long a1; unsigned long a2; unsigned long a3; }; void smc_call(uint32_t id, unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5, unsigned long a6, unsigned long a7, struct arm_smc_res *res); void hvc_call(uint32_t id, unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5, unsigned long a6, unsigned long a7, struct arm_smc_res *res); int do_svc_handler(gp_regs *regs, uint32_t svc_id, uint64_t *args, int smc); #endif ================================================ FILE: kernel/arch/aarch64/include/asm/syscall.h ================================================ #ifndef __ASM_SYSCALL_H__ #define __ASM_SYSCALL_H__ #define __NR_kobject_create 0 #define __NR_kobject_open 1 #define __NR_kobject_close 2 #define __NR_kobject_recv 3 #define __NR_kobject_send 4 #define __NR_kobject_reply 5 #define __NR_kobject_reply_recv 6 #define __NR_kobject_ctl 7 #define __NR_kobject_mmap 8 #define __NR_kobject_munmap 9 #define __NR_grant 10 #define __NR_futex 11 #define __NR_yield 12 #define __NR_map 13 #define __NR_unmap 14 #define __NR_trans 15 #define __NR_clock_gettime 16 #define __NR_clock_nanosleep 17 #define __NR_exit 18 #define __NR_exitgroup 19 #define __NR_clone 20 #undef __NR_syscalls #define __NR_syscalls 21 struct syscall_regs { unsigned long regs[8]; }; #endif ================================================ FILE: kernel/arch/aarch64/include/asm/tcb.h ================================================ #ifndef __ASM_TCB_H__ #define __ASM_TCB_H__ #include struct aarch64_regs { uint64_t pc; // elr_el2 uint64_t pstate; // spsr_el2 uint64_t sp; // sp_el0 uint64_t x0; uint64_t x1; uint64_t x2; uint64_t x3; uint64_t x4; uint64_t x5; uint64_t x6; uint64_t x7; uint64_t x8; uint64_t x9; uint64_t x10; uint64_t x11; uint64_t x12; uint64_t x13; uint64_t x14; uint64_t x15; uint64_t x16; uint64_t x17; uint64_t x18; uint64_t x19; uint64_t x20; uint64_t x21; uint64_t x22; uint64_t x23; uint64_t x24; uint64_t x25; uint64_t x26; uint64_t x27; uint64_t x28; uint64_t x29; uint64_t lr; }__packed; #define PT_REG_R0 3 typedef struct aarch64_regs gp_regs; struct fpsimd_context { uint64_t regs[64] __align(16); uint32_t fpsr; uint32_t fpcr; #ifdef CONFIG_VIRT uint32_t fpexc32_el2; uint32_t padding0; #endif }; struct cpu_context { uint64_t tpidr_el0; uint64_t tpidrro_el0; uint64_t ttbr_el0; struct fpsimd_context fpsimd_state; }; #endif ================================================ FILE: kernel/arch/aarch64/include/asm/time.h ================================================ #ifndef _MINOS_ASM_TIME_H_ #define _MINOS_ASM_TIME_H_ #include extern uint64_t boot_tick; extern uint32_t cpu_khz; unsigned long get_sys_time(void); unsigned long get_current_time(void); unsigned long get_sys_ticks(void); void arch_enable_timer(unsigned long e); #endif ================================================ FILE: kernel/arch/aarch64/include/asm/tlb.h ================================================ #ifndef __MINOS_ASM_TLB_H__ #define __MINOS_ASM_TLB_H__ #include static inline void flush_tlb_asid_all(uint16_t asid) { /* * load the ttbr0_el0 value to the register */ asm volatile ( "lsl %x0, %0, #48\n" "dsb sy\n" "tlbi aside1is, %x0\n" "dsb sy\n" "isb\n" : : "Ir" (asid) : "memory" ); } static inline void flush_all_tlb_host(void) { #ifdef CONFIG_VIRT asm volatile ( "dsb sy;" "tlbi alle2is;" "dsb sy;" "isb;" : : : "memory" ); #else asm volatile ( "dsb sy;" "tlbi alle1is;" "dsb sy;" "isb;" : : : "memory" ); #endif } static inline void flush_local_tlb_host(void) { #ifdef CONFIG_VIRT asm volatile ( "dsb sy;" "tlbi alle2;" "dsb sy;" "isb;" : : : "memory" ); #else asm volatile ( "dsb sy;" "tlbi alle2;" "dsb sy;" "isb;" : : : "memory" ); #endif } static inline void flush_tlb_va_host(unsigned long va, unsigned long size) { unsigned long end = va + size; dsb(); #ifdef CONFIG_VIRT while (va < end) { asm volatile("tlbi vae2is, %0;" : : "r" (va >> PAGE_SHIFT) : "memory"); va += PAGE_SIZE; } #else while (va < end) { asm volatile("tlbi vae1is, %0;" : : "r" (va >> PAGE_SHIFT) : "memory"); va += PAGE_SIZE; } #endif dsb(); isb(); } static inline void flush_local_tlb_va_host(unsigned long va, unsigned long size) { unsigned long end = va + size; dsb(); #ifdef CONFIG_VIRT while (va < end) { asm volatile("tlbi vae2, %0;" : : "r" (va >> PAGE_SHIFT) : "memory"); va += PAGE_SIZE; } #else while (va < end) { asm volatile("tlbi vae2, %0;" : : "r" (va >> PAGE_SHIFT) : "memory"); va += PAGE_SIZE; } #endif dsb(); isb(); } static inline void flush_local_tlb_guest(void) { /* current VMID only */ asm volatile ( "dsb sy;" "tlbi vmalls12e1;" "dsb sy;" "isb;" : : : "memory" ); } static inline void flush_all_tlb_guest(void) { /* current vmid only and innershareable TLBS */ asm volatile( "dsb sy;" "tlbi vmalls12e1is;" "dsb sy;" "isb;" : : : "memory" ); } static inline void flush_tlb_ipa_guest(unsigned long ipa, size_t size) { unsigned long end = ipa + size; dsb(); /* * step 1 - flush stage2 tlb for va range * step 2 - flush all stage1 tlb for this VM */ while (ipa < end) { asm volatile("tlbi ipas2e1is, %0;" : : "r" (ipa >> PAGE_SHIFT) : "memory"); ipa += PAGE_SIZE; } asm volatile("tlbi vmalle1;"); dsb(); isb(); } void flush_tlb_vm(struct vspace *mm); void flush_tlb_vm_ipa_range(struct vspace *mm, unsigned long ipa, size_t size); #endif ================================================ FILE: kernel/arch/aarch64/include/asm/trap.h ================================================ #ifndef _MINOS_EXCEPTION_H_ #define _MINOS_EXCEPTION_H_ #include #include typedef int (*sync_handler_t)(gp_regs *reg, int ec, uint32_t esr); struct sync_desc { uint8_t aarch; uint8_t irq_safe; uint8_t ret_addr_adjust; uint8_t resv; sync_handler_t handler; }; #define DEFINE_SYNC_DESC(t, arch, h, is, raa) \ static struct sync_desc sync_desc_##t __used = { \ .aarch = arch, \ .handler = h, \ .irq_safe = is, \ .ret_addr_adjust = raa, \ } struct esr { unsigned long iss:25; /* Instruction Specific Syndrome */ unsigned long len:1; /* Instruction length */ unsigned long ec:6; /* Exception Class */ }; /* Common to all conditional exception classes (0x0N, except 0x00). */ struct esr_cond { unsigned long iss:20; /* Instruction Specific Syndrome */ unsigned long cc:4; /* Condition Code */ unsigned long ccvalid:1;/* CC Valid */ unsigned long len:1; /* Instruction length */ unsigned long ec:6; /* Exception Class */ }; struct esr_wfi_wfe { unsigned long ti:1; /* Trapped instruction */ unsigned long sbzp:19; unsigned long cc:4; /* Condition Code */ unsigned long ccvalid:1;/* CC Valid */ unsigned long len:1; /* Instruction length */ unsigned long ec:6; /* Exception Class */ }; /* reg, reg0, reg1 are 4 bits on AArch32, the fifth bit is sbzp. */ struct esr_cp32 { unsigned long read:1; /* Direction */ unsigned long crm:4; /* CRm */ unsigned long reg:5; /* Rt */ unsigned long crn:4; /* CRn */ unsigned long op1:3; /* Op1 */ unsigned long op2:3; /* Op2 */ unsigned long cc:4; /* Condition Code */ unsigned long ccvalid:1;/* CC Valid */ unsigned long len:1; /* Instruction length */ unsigned long ec:6; /* Exception Class */ }; struct esr_cp64 { unsigned long read:1; /* Direction */ unsigned long crm:4; /* CRm */ unsigned long reg1:5; /* Rt1 */ unsigned long reg2:5; /* Rt2 */ unsigned long sbzp2:1; unsigned long op1:4; /* Op1 */ unsigned long cc:4; /* Condition Code */ unsigned long ccvalid:1;/* CC Valid */ unsigned long len:1; /* Instruction length */ unsigned long ec:6; /* Exception Class */ }; struct esr_cp { unsigned long coproc:4; /* Number of coproc accessed */ unsigned long sbz0p:1; unsigned long tas:1; /* Trapped Advanced SIMD */ unsigned long res0:14; unsigned long cc:4; /* Condition Code */ unsigned long ccvalid:1;/* CC Valid */ unsigned long len:1; /* Instruction length */ unsigned long ec:6; /* Exception Class */ }; /* * This encoding is valid only for ARMv8 (ARM DDI 0487B.a, pages D7-2271 and * G6-4957). On ARMv7, encoding ISS for EC=0x13 is defined as UNK/SBZP * (ARM DDI 0406C.c page B3-1431). UNK/SBZP means that hardware implements * this field as Read-As-Zero. ARMv8 is backwards compatible with ARMv7: * reading CCKNOWNPASS on ARMv7 will return 0, which means that condition * check was passed or instruction was unconditional. */ struct esr_smc32 { unsigned long res0:19; /* Reserved */ unsigned long ccknownpass:1; /* Instruction passed conditional check */ unsigned long cc:4; /* Condition Code */ unsigned long ccvalid:1;/* CC Valid */ unsigned long len:1; /* Instruction length */ unsigned long ec:6; /* Exception Class */ }; struct esr_sysreg { unsigned long read:1; /* Direction */ unsigned long crm:4; /* CRm */ unsigned long reg:5; /* Rt */ unsigned long crn:4; /* CRn */ unsigned long op1:3; /* Op1 */ unsigned long op2:3; /* Op2 */ unsigned long op0:2; /* Op0 */ unsigned long res0:3; unsigned long len:1; /* Instruction length */ unsigned long ec:6; }; struct esr_iabt { unsigned long ifsc:6; /* Instruction fault status code */ unsigned long res0:1; /* RES0 */ unsigned long s1ptw:1; /* Stage 2 fault during stage 1 translation */ unsigned long res1:1; /* RES0 */ unsigned long eat:1; /* External abort type */ unsigned long fnv:1; /* FAR not Valid */ unsigned long res2:14; unsigned long len:1; /* Instruction length */ unsigned long ec:6; /* Exception Class */ }; struct esr_dabt { unsigned long dfsc:6; /* Data Fault Status Code */ unsigned long write:1; /* Write / not Read */ unsigned long s1ptw:1; /* Stage 2 fault during stage 1 translation */ unsigned long cache:1; /* Cache Maintenance */ unsigned long eat:1; /* External Abort Type */ unsigned long fnv:1; /* FAR not Valid */ #ifdef ARM_AARCH32 unsigned long sbzp0:5; #else unsigned long sbzp0:3; unsigned long ar:1; /* Acquire Release */ unsigned long sf:1; /* Sixty Four bit register */ #endif unsigned long reg:5; /* Register */ unsigned long sign:1; /* Sign extend */ unsigned long size:2; /* Access Size */ unsigned long valid:1; /* Syndrome Valid */ unsigned long len:1; /* Instruction length */ unsigned long ec:6; /* Exception Class */ }; /* Contain the common bits between DABT and IABT */ struct esr_xabt { unsigned long fsc:6; /* Fault status code */ unsigned long pad1:1; /* Not common */ unsigned long s1ptw:1; /* Stage 2 fault during stage 1 translation */ unsigned long pad2:1; /* Not common */ unsigned long eat:1; /* External abort type */ unsigned long fnv:1; /* FAR not Valid */ unsigned long pad3:14; /* Not common */ unsigned long len:1; /* Instruction length */ unsigned long ec:6; /* Exception Class */ }; #ifdef ARM_AARCH64 struct esr_brk { unsigned long comment:16; /* Comment */ unsigned long res0:9; unsigned long len:1; /* Instruction length */ unsigned long ec:6; /* Exception Class */ }; #endif #endif ================================================ FILE: kernel/arch/aarch64/include/asm/uaccess.h ================================================ #ifndef __ASM_UACCESS_H__ #define __ASM_UACCESS_H__ #include #include static inline unsigned long user_ranges_ok(const void __user *addr, unsigned long size) { unsigned long ret, limit = USER_PROCESS_ADDR_LIMIT; asm volatile( // A + B <= C + 1 for all A,B,C, in four easy steps: // 1: X = A + B; X' = X % 2^64 " adds %0, %3, %2\n" // 2: Set C = 0 if X > 2^64, to guarantee X' > C in step 4 " csel %1, xzr, %1, hi\n" // 3: Set X' = ~0 if X >= 2^64. For X == 2^64, this decrements X' // to compensate for the carry flag being set in step 4. For // X > 2^64, X' merely has to remain nonzero, which it does. " csinv %0, %0, xzr, cc\n" // 4: For X < 2^64, this gives us X' - C - 1 <= 0, where the -1 // comes from the carry in being clear. Otherwise, we are // testing X' - C == 0, subject to the previous adjustments. " sbcs xzr, %0, %1\n" " cset %0, ls\n" : "=&r" (ret), "+r" (limit) : "Ir" (size), "0" (addr) : "cc"); return ret; } #endif ================================================ FILE: kernel/arch/aarch64/include/asm/virt.h ================================================ #ifndef __MINOS_AARCH64_VIRT_H__ #define __MINOS_AARCH64_VIRT_H__ #include struct vcpu; struct arm_virt_data { int (*dczva_trap)(struct vcpu *vcpu, unsigned long va); int (*sgi1r_el1_trap)(struct vcpu *vcpu, unsigned long value); int (*phy_timer_trap)(struct vcpu *vcpu, int reg, int read, unsigned long *value); int (*smc_handler)(struct vcpu *vcpu, gp_regs *regs, uint32_t esr); int (*hvc_handler)(struct vcpu *vcpu, gp_regs *regs, uint32_t esr); int (*sysreg_emulation)(struct vcpu *vcpu, int reg, int read, unsigned long *value); }; void set_current_vmid(uint32_t vmid); uint32_t get_current_vmid(void); struct vcpu *get_vcpu_from_reg(void); void arch_set_virq_flag(void); void arch_set_vfiq_flag(void); void arch_clear_vfiq_flag(void); void arch_clear_virq_flag(void); void arch_vcpu_init(struct vcpu *, void *, void *); #endif ================================================ FILE: kernel/arch/aarch64/include/asm/vtcb.h ================================================ #ifndef __ASM_VTCB_H__ #define __ASM_VTCB_H__ /* * 0 is reserved as an invalid value. * Order should be kept in sync with the save/restore code. */ enum vcpu_sysreg { __INVALID_SYSREG__, MPIDR_EL1, /* MultiProcessor Affinity Register */ CSSELR_EL1, /* Cache Size Selection Register */ SCTLR_EL1, /* System Control Register */ ACTLR_EL1, /* Auxiliary Control Register */ CPACR_EL1, /* Coprocessor Access Control */ TTBR0_EL1, /* Translation Table Base Register 0 */ TTBR1_EL1, /* Translation Table Base Register 1 */ TCR_EL1, /* Translation Control Register */ ESR_EL1, /* Exception Syndrome Register */ AFSR0_EL1, /* Auxiliary Fault Status Register 0 */ AFSR1_EL1, /* Auxiliary Fault Status Register 1 */ FAR_EL1, /* Fault Address Register */ MAIR_EL1, /* Memory Attribute Indirection Register */ VBAR_EL1, /* Vector Base Address Register */ CONTEXTIDR_EL1, /* Context ID Register */ TPIDR_EL0, /* Thread ID, User R/W */ TPIDRRO_EL0, /* Thread ID, User R/O */ TPIDR_EL1, /* Thread ID, Privileged */ AMAIR_EL1, /* Aux Memory Attribute Indirection Register */ CNTKCTL_EL1, /* Timer Control Register (EL1) */ PAR_EL1, /* Physical Address Register */ MDSCR_EL1, /* Monitor Debug System Control Register */ MDCCINT_EL1, /* Monitor Debug Comms Channel Interrupt Enable Reg */ DISR_EL1, /* Deferred Interrupt Status Register */ /* Performance Monitors Registers */ PMCR_EL0, /* Control Register */ PMSELR_EL0, /* Event Counter Selection Register */ PMEVCNTR0_EL0, /* Event Counter Register (0-30) */ PMEVCNTR30_EL0 = PMEVCNTR0_EL0 + 30, PMCCNTR_EL0, /* Cycle Counter Register */ PMEVTYPER0_EL0, /* Event Type Register (0-30) */ PMEVTYPER30_EL0 = PMEVTYPER0_EL0 + 30, PMCCFILTR_EL0, /* Cycle Count Filter Register */ PMCNTENSET_EL0, /* Count Enable Set Register */ PMINTENSET_EL1, /* Interrupt Enable Set Register */ PMOVSSET_EL0, /* Overflow Flag Status Set Register */ PMSWINC_EL0, /* Software Increment Register */ PMUSERENR_EL0, /* User Enable Register */ /* 32bit specific registers. Keep them at the end of the range */ DACR32_EL2, /* Domain Access Control Register */ IFSR32_EL2, /* Instruction Fault Status Register */ FPEXC32_EL2, /* Floating-Point Exception Control Register */ DBGVCR32_EL2, /* Debug Vector Catch Register */ NR_SYS_REGS /* Nothing after this line! */ }; struct vcpu_context { uint64_t vbar_el1; uint64_t esr_el1; uint64_t sp_el1; uint64_t sp_el0; uint64_t elr_el1; uint64_t vmpidr; uint64_t vpidr; uint64_t sctlr_el1; uint64_t hcr_el2; uint64_t spsr_el1; uint64_t far_el1; uint64_t actlr_el1; uint64_t tpidr_el1; uint64_t csselr; uint64_t cpacr; uint64_t contextidr; uint64_t tpidr_el0; uint64_t tpidrro_el0; uint64_t cntkctl; uint64_t afsr0; uint64_t afsr1; uint32_t teecr; uint32_t teehbr; uint32_t dacr32_el2; uint32_t ifsr32_el2; uint64_t vtcr_el2; uint64_t ttbr0_el1; uint64_t ttbr1_el1; uint64_t vttbr_el2; uint64_t mair_el1; uint64_t amair_el1; uint64_t tcr_el1; uint64_t par_el1; }; #endif ================================================ FILE: kernel/arch/aarch64/lds/Makefile ================================================ obj-y += minos.lds ================================================ FILE: kernel/arch/aarch64/lds/minos.lds.S ================================================ #include ENTRY(_start) SECTIONS { .vectors CONFIG_MINOS_ENTRY_ADDRESS + CONFIG_PTOV_MASK: { /* * put all asm code into this section */ __minos_start = .; __code_start = .; KEEP(*(__start_up)) KEEP(*(__elx_vectors __int_handlers __asm_code)) } .text : { *(.text) } . = ALIGN(4096); __code_end = .; __init_start = .; __init_func_start = .; __init_func_0_start = .; .__init_func_0 : { *(.__init_func_0) } __init_func_1_start = .; .__init_func_1 : { *(.__init_func_1) } __init_func_2_start = .; .__init_func_2 : { *(.__init_func_2) } __init_func_3_start = .; .__init_func_3 : { *(.__init_func_3) } __init_func_4_start = .; .__init_func_4 : { *(.__init_func_4) } __init_func_5_start = .; .__init_func_5 : { *(.__init_func_5) } __init_func_6_start = .; .__init_func_6 : { *(.__init_func_6) } __init_func_7_start = .; .__init_func_7 : { *(.__init_func_7) } __init_func_8_start = .; .__init_func_8 : { *(.__init_func_8) } __init_func_9_start = .; .__init_func_9 : { *(.__init_func_9) } __init_func_end = .; . = ALIGN(8); __init_data_start = .; .__init_data_section : { *(.__init_data_section) } __init_data_end = .; . = ALIGN(8); __init_text_start = .; .__init_text : { *(__init_text) } __init_text_end = .; . = ALIGN(4096); __init_end = .; __data_start = .; .stage1_page_table : { . = ALIGN(4096); __stage1_page_table = .; . = . + 0x1000; } .data : {*(.data)} . = ALIGN(8); .smp_affinity_id : { __smp_affinity_id = .; . = . + (CONFIG_NR_CPUS * 8); __smp_affinity_id_end = .; } . = ALIGN(8); __percpu_start = .; __percpu_cpu_0_start = .; .percpu_0 : { KEEP(*(".__percpu")) } . = ALIGN(64); __percpu_cpu_0_end = .; __percpu_section_size = __percpu_cpu_0_end - __percpu_cpu_0_start; .__percpu_others : { } . = __percpu_cpu_0_end + __percpu_section_size * (CONFIG_NR_CPUS - 1); __percpu_end = .; . = ALIGN(8); __bss_start = .; .bss : {*(.bss)} __bss_end = .; . = ALIGN(8); __vmodule_start = .; .__vmodule : { *(.__vmodule) } __vmodule_end = .; . = ALIGN(8); __platform_start = .; .__platform : { *(.__platform) } __platform_end = .; . = ALIGN(8); __irqchip_start = .; .__irqchip : { *(.__irqchip) } __irqchip_end = .; . = ALIGN(8); __iommu_ops_start = .; .__iommu_ops : { *(.__iommu_ops) } __iommu_ops_end = .; . = ALIGN(8); __virqchip_start = .; .__virqchip : { *(.__virqchip) } __virqchip_end = .; . = ALIGN(8); __vdev_start = .; .__vdev : { *(.__vdev) } __vdev_end = .; __console_start = .; .__console : { *(.__console) } __console_end = .; . = ALIGN(8); __smc_handler_start = .; .__smc_handler : { *(.__smc_handler) } __smc_handler_end = .; . = ALIGN(8); __hvc_handler_start = .; .__hvc_handler : { *(.__hvc_handler) } __hvc_handler_end = .; . = ALIGN(8); __shell_command_start = .; .__shell_command : { *(.__shell_command) } __shell_command_end = .; __kobject_desc_start = .; .__kobject_desc : { *(.__kobject_desc) } __kobject_desc_end = .; . = ALIGN(4096); __data_end = .; __rodata_start = .; __symbols_start = .; .__symbols__ : { KEEP(*(.__symbols__)) } .rodata : { KEEP(*(.rodata)) } .rodata.str1.8 : { KEEP(*(.rodata.str1.8)) } __minos_end = .; } ================================================ FILE: kernel/arch/aarch64/lib/Makefile ================================================ # obj-y += atomic.o obj-y += bitops.o obj-y += memchr.o obj-y += memcmp.o obj-y += memcpy.o obj-y += memmove.o obj-y += memset.o obj-y += spinlock.o obj-y += strchr.o obj-y += strcmp.o obj-y += strcpy.o obj-y += strlen.o obj-y += strncmp.o obj-y += strnlen.o obj-y += strrchr.o obj-y += ticket_lock.o ================================================ FILE: kernel/arch/aarch64/lib/atomic.S ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include .global __atomic_set .global __atomic_get .global atomic_add .global atomic_sub .global atomic_add_return .global atomic_add_return_old .global atomic_sub_return .global atomic_sub_return_old /* * ldaxr : a - acquire (equal add a dmb ins) * x - exclusive * ldar ldarb ldarh ldxr ldxrh * * stlxr : l - release (equal add a dmb ins) * : x - exclusive * stlr stxr stlrb stlrh stxrh etc */ func __atomic_set stlr w0, [x1] ret endfunc __atomic_set func __atomic_get ldar w1, [x0] mov w0, w1 ret endfunc __atomic_get func atomic_add 1: ldaxr w2, [x1] add w2, w2, w0 stxr w3, w2, [x1] cbnz w3, 1b ret endfunc atomic_add func atomic_sub 2: ldaxr w2, [x1] sub w2, w2, w0 stlxr w3, w2, [x1] cbnz w3, 2b ret endfunc atomic_sub func atomic_add_return 3: ldaxr w2, [x1] add w2, w2, w0 stlxr w3, w2, [x1] cbnz w3, 3b mov w0, w2 ret endfunc atomic_add_return func atomic_sub_return 4: ldaxr w2, [x1] sub w2, w2, w0 stlxr w3, w2, [x1] cbnz w3, 4b mov w0, w2 ret endfunc atomic_sub_return func atomic_add_return_old 3: ldaxr w2, [x1] add w2, w2, w0 stlxr w3, w2, [x1] cbnz w3, 3b sub w0, w2, w0 ret endfunc atomic_add_return_old func atomic_sub_return_old 4: ldaxr w2, [x1] sub w2, w2, w0 stlxr w3, w2, [x1] cbnz w3, 4b add w0, w2, w0 ret endfunc atomic_sub_return_old ================================================ FILE: kernel/arch/aarch64/lib/bitops.S ================================================ /* * Based on linux/arch/arm64/lib/bitops.h which in turn is * Based on arch/arm/lib/bitops.h * * Copyright (C) 2013 ARM Ltd. * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * x0: bits 4:0 bit offset * bits 31:5 word offset * x1: address */ #include .section __asm_code, "ax" .global test_bit .macro bitop, name, instr .global \name \name: .func \name .cfi_startproc and w3, w0, #31 // Get bit offset eor w0, w0, w3 // Clear low bits mov x2, #1 add x1, x1, x0, lsr #3 // Get word offset lsl x3, x2, x3 // Create mask 1: ldxr w2, [x1] \instr w2, w2, w3 stxr w0, w2, [x1] cbnz w0, 1b ret .endfunc .cfi_endproc .endm .macro testop, name, instr .global \name \name: .func \name .cfi_startproc and w3, w0, #31 // Get bit offset eor w0, w0, w3 // Clear low bits mov x2, #1 add x1, x1, x0, lsr #3 // Get word offset lsl x4, x2, x3 // Create mask 1: ldxr w2, [x1] lsr w0, w2, w3 // Save old value of bit \instr w2, w2, w4 // toggle bit stlxr w5, w2, [x1] cbnz w5, 1b dmb ish and w0, w0, #1 3: ret .endfunc .cfi_endproc .endm /* * Atomic bit operations. */ bitop change_bit, eor bitop clear_bit, bic bitop set_bit, orr testop test_and_change_bit, eor testop test_and_clear_bit, bic testop test_and_set_bit, orr func test_bit and w3, w0, #31 // Get bit offset eor w0, w0, w3 // Clear low bits add x1, x1, x0, lsr #3 // Get word offset 1: ldxr w2, [x1] lsr w0, w2, w3 // Save old value of bit and w0, w0, #1 ret endfunc test_bit ================================================ FILE: kernel/arch/aarch64/lib/memchr.S ================================================ /* * Based on arch/arm/lib/memchr.S * * Copyright (C) 1995-2000 Russell King * Copyright (C) 2013 ARM Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include /* * Find a character in an area of memory. * * Parameters: * x0 - buf * x1 - c * x2 - n * Returns: * x0 - address of first occurrence of 'c' or 0 */ .global memchr func memchr and w1, w1, #0xff 1: subs x2, x2, #1 b.mi 2f ldrb w3, [x0], #1 cmp w3, w1 b.ne 1b sub x0, x0, #1 ret 2: mov x0, #0 ret endfunc memchr ================================================ FILE: kernel/arch/aarch64/lib/memcmp.S ================================================ /* * Copyright (C) 2013 ARM Ltd. * Copyright (C) 2013 Linaro. * * This code is based on glibc cortex strings work originally authored by Linaro * and re-licensed under GPLv2 for the Linux kernel. The original code can * be found @ * * http://bazaar.launchpad.net/~linaro-toolchain-dev/cortex-strings/trunk/ * files/head:/src/aarch64/ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include .global memcmp /* Parameters and result. */ src1 .req x0 src2 .req x1 limit .req x2 result .req x0 /* Internal variables. */ data1 .req x3 data1w .req w3 data2 .req x4 data2w .req w4 has_nul .req x5 diff .req x6 endloop .req x7 tmp1 .req x8 tmp2 .req x9 tmp3 .req x10 pos .req x11 limit_wd .req x12 mask .req x13 func memcmp cbz limit, .Lret0 eor tmp1, src1, src2 tst tmp1, #7 b.ne .Lmisaligned8 ands tmp1, src1, #7 b.ne .Lmutual_align sub limit_wd, limit, #1 /* limit != 0, so no underflow. */ lsr limit_wd, limit_wd, #3 /* Convert to Dwords. */ /* * The input source addresses are at alignment boundary. * Directly compare eight bytes each time. */ .Lloop_aligned: ldr data1, [src1], #8 ldr data2, [src2], #8 .Lstart_realigned: subs limit_wd, limit_wd, #1 eor diff, data1, data2 /* Non-zero if differences found. */ csinv endloop, diff, xzr, cs /* Last Dword or differences. */ cbz endloop, .Lloop_aligned /* Not reached the limit, must have found a diff. */ tbz limit_wd, #63, .Lnot_limit /* Limit % 8 == 0 => the diff is in the last 8 bytes. */ ands limit, limit, #7 b.eq .Lnot_limit /* * The remained bytes less than 8. It is needed to extract valid data * from last eight bytes of the intended memory range. */ lsl limit, limit, #3 /* bytes-> bits. */ mov mask, #~0 lsl mask, mask, limit bic data1, data1, mask bic data2, data2, mask orr diff, diff, mask b .Lnot_limit .Lmutual_align: /* * Sources are mutually aligned, but are not currently at an * alignment boundary. Round down the addresses and then mask off * the bytes that precede the start point. */ bic src1, src1, #7 bic src2, src2, #7 ldr data1, [src1], #8 ldr data2, [src2], #8 /* * We can not add limit with alignment offset(tmp1) here. Since the * addition probably make the limit overflown. */ sub limit_wd, limit, #1/*limit != 0, so no underflow.*/ and tmp3, limit_wd, #7 lsr limit_wd, limit_wd, #3 add tmp3, tmp3, tmp1 add limit_wd, limit_wd, tmp3, lsr #3 add limit, limit, tmp1/* Adjust the limit for the extra. */ lsl tmp1, tmp1, #3/* Bytes beyond alignment -> bits.*/ neg tmp1, tmp1/* Bits to alignment -64. */ mov tmp2, #~0 /*mask off the non-intended bytes before the start address.*/ /* Little-endian. Early bytes are at LSB. */ lsr tmp2, tmp2, tmp1 orr data1, data1, tmp2 orr data2, data2, tmp2 b .Lstart_realigned /*src1 and src2 have different alignment offset.*/ .Lmisaligned8: cmp limit, #8 b.lo .Ltiny8proc /*limit < 8: compare byte by byte*/ and tmp1, src1, #7 neg tmp1, tmp1 add tmp1, tmp1, #8/*valid length in the first 8 bytes of src1*/ and tmp2, src2, #7 neg tmp2, tmp2 add tmp2, tmp2, #8/*valid length in the first 8 bytes of src2*/ subs tmp3, tmp1, tmp2 csel pos, tmp1, tmp2, hi /*Choose the maximum.*/ sub limit, limit, pos /*compare the proceeding bytes in the first 8 byte segment.*/ .Ltinycmp: ldrb data1w, [src1], #1 ldrb data2w, [src2], #1 subs pos, pos, #1 ccmp data1w, data2w, #0, ne /* NZCV = 0b0000. */ b.eq .Ltinycmp cbnz pos, 1f /*diff occurred before the last byte.*/ cmp data1w, data2w b.eq .Lstart_align 1: sub result, data1, data2 ret .Lstart_align: lsr limit_wd, limit, #3 cbz limit_wd, .Lremain8 ands xzr, src1, #7 b.eq .Lrecal_offset /*process more leading bytes to make src1 aligned...*/ add src1, src1, tmp3 /*backwards src1 to alignment boundary*/ add src2, src2, tmp3 sub limit, limit, tmp3 lsr limit_wd, limit, #3 cbz limit_wd, .Lremain8 /*load 8 bytes from aligned SRC1..*/ ldr data1, [src1], #8 ldr data2, [src2], #8 subs limit_wd, limit_wd, #1 eor diff, data1, data2 /*Non-zero if differences found.*/ csinv endloop, diff, xzr, ne cbnz endloop, .Lunequal_proc /*How far is the current SRC2 from the alignment boundary...*/ and tmp3, tmp3, #7 .Lrecal_offset:/*src1 is aligned now..*/ neg pos, tmp3 .Lloopcmp_proc: /* * Divide the eight bytes into two parts. First,backwards the src2 * to an alignment boundary,load eight bytes and compare from * the SRC2 alignment boundary. If all 8 bytes are equal,then start * the second part's comparison. Otherwise finish the comparison. * This special handle can garantee all the accesses are in the * thread/vcpu space in avoid to overrange access. */ ldr data1, [src1,pos] ldr data2, [src2,pos] eor diff, data1, data2 /* Non-zero if differences found. */ cbnz diff, .Lnot_limit /*The second part process*/ ldr data1, [src1], #8 ldr data2, [src2], #8 eor diff, data1, data2 /* Non-zero if differences found. */ subs limit_wd, limit_wd, #1 csinv endloop, diff, xzr, ne/*if limit_wd is 0,will finish the cmp*/ cbz endloop, .Lloopcmp_proc .Lunequal_proc: cbz diff, .Lremain8 /* There is difference occurred in the latest comparison. */ .Lnot_limit: /* * For little endian,reverse the low significant equal bits into MSB,then * following CLZ can find how many equal bits exist. */ rev diff, diff rev data1, data1 rev data2, data2 /* * The MS-non-zero bit of DIFF marks either the first bit * that is different, or the end of the significant data. * Shifting left now will bring the critical information into the * top bits. */ clz pos, diff lsl data1, data1, pos lsl data2, data2, pos /* * We need to zero-extend (char is unsigned) the value and then * perform a signed subtraction. */ lsr data1, data1, #56 sub result, data1, data2, lsr #56 ret .Lremain8: /* Limit % 8 == 0 =>. all data are equal.*/ ands limit, limit, #7 b.eq .Lret0 .Ltiny8proc: ldrb data1w, [src1], #1 ldrb data2w, [src2], #1 subs limit, limit, #1 ccmp data1w, data2w, #0, ne /* NZCV = 0b0000. */ b.eq .Ltiny8proc sub result, data1, data2 ret .Lret0: mov result, #0 ret endfunc memcmp ================================================ FILE: kernel/arch/aarch64/lib/memcpy.S ================================================ /* * Copyright (C) 2013 ARM Ltd. * Copyright (C) 2013 Linaro. * * This code is based on glibc cortex strings work originally authored by Linaro * and re-licensed under GPLv2 for the Linux kernel. The original code can * be found @ * * http://bazaar.launchpad.net/~linaro-toolchain-dev/cortex-strings/trunk/ * files/head:/src/aarch64/ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include .macro ldrb1 ptr, regB, val ldrb \ptr, [\regB], \val .endm .macro strb1 ptr, regB, val strb \ptr, [\regB], \val .endm .macro ldrh1 ptr, regB, val ldrh \ptr, [\regB], \val .endm .macro strh1 ptr, regB, val strh \ptr, [\regB], \val .endm .macro ldr1 ptr, regB, val ldr \ptr, [\regB], \val .endm .macro str1 ptr, regB, val str \ptr, [\regB], \val .endm .macro ldp1 ptr, regB, regC, val ldp \ptr, \regB, [\regC], \val .endm .macro stp1 ptr, regB, regC, val stp \ptr, \regB, [\regC], \val .endm .global memcpy dstin .req x0 src .req x1 count .req x2 tmp1 .req x3 tmp1w .req w3 tmp2 .req x4 tmp2w .req w4 dst .req x6 A_l .req x7 A_h .req x8 B_l .req x9 B_h .req x10 C_l .req x11 C_h .req x12 D_l .req x13 D_h .req x14 func memcpy mov dst, dstin cmp count, #16 /*When memory length is less than 16, the accessed are not aligned.*/ b.lo .Ltiny15 neg tmp2, src ands tmp2, tmp2, #15/* Bytes to reach alignment. */ b.eq .LSrcAligned sub count, count, tmp2 /* * Copy the leading memory data from src to dst in an increasing * address order.By this way,the risk of overwritting the source * memory data is eliminated when the distance between src and * dst is less than 16. The memory accesses here are alignment. */ tbz tmp2, #0, 1f ldrb1 tmp1w, src, #1 strb1 tmp1w, dst, #1 1: tbz tmp2, #1, 2f ldrh1 tmp1w, src, #2 strh1 tmp1w, dst, #2 2: tbz tmp2, #2, 3f ldr1 tmp1w, src, #4 str1 tmp1w, dst, #4 3: tbz tmp2, #3, .LSrcAligned ldr1 tmp1, src, #8 str1 tmp1, dst, #8 .LSrcAligned: cmp count, #64 b.ge .Lcpy_over64 /* * Deal with small copies quickly by dropping straight into the * exit block. */ .Ltail63: /* * Copy up to 48 bytes of data. At this point we only need the * bottom 6 bits of count to be accurate. */ ands tmp1, count, #0x30 b.eq .Ltiny15 cmp tmp1w, #0x20 b.eq 1f b.lt 2f ldp1 A_l, A_h, src, #16 stp1 A_l, A_h, dst, #16 1: ldp1 A_l, A_h, src, #16 stp1 A_l, A_h, dst, #16 2: ldp1 A_l, A_h, src, #16 stp1 A_l, A_h, dst, #16 .Ltiny15: /* * Prefer to break one ldp/stp into several load/store to access * memory in an increasing address order,rather than to load/store 16 * bytes from (src-16) to (dst-16) and to backward the src to aligned * address,which way is used in original cortex memcpy. If keeping * the original memcpy process here, memmove need to satisfy the * precondition that src address is at least 16 bytes bigger than dst * address,otherwise some source data will be overwritten when memove * call memcpy directly. To make memmove simpler and decouple the * memcpy's dependency on memmove, withdrew the original process. */ tbz count, #3, 1f ldr1 tmp1, src, #8 str1 tmp1, dst, #8 1: tbz count, #2, 2f ldr1 tmp1w, src, #4 str1 tmp1w, dst, #4 2: tbz count, #1, 3f ldrh1 tmp1w, src, #2 strh1 tmp1w, dst, #2 3: tbz count, #0, .Lexitfunc ldrb1 tmp1w, src, #1 strb1 tmp1w, dst, #1 b .Lexitfunc .Lcpy_over64: subs count, count, #128 b.ge .Lcpy_body_large /* * Less than 128 bytes to copy, so handle 64 here and then jump * to the tail. */ ldp1 A_l, A_h, src, #16 stp1 A_l, A_h, dst, #16 ldp1 B_l, B_h, src, #16 ldp1 C_l, C_h, src, #16 stp1 B_l, B_h, dst, #16 stp1 C_l, C_h, dst, #16 ldp1 D_l, D_h, src, #16 stp1 D_l, D_h, dst, #16 tst count, #0x3f b.ne .Ltail63 b .Lexitfunc /* * Critical loop. Start at a new cache line boundary. Assuming * 64 bytes per line this ensures the entire loop is in one line. */ .p2align 6 .Lcpy_body_large: /* pre-get 64 bytes data. */ ldp1 A_l, A_h, src, #16 ldp1 B_l, B_h, src, #16 ldp1 C_l, C_h, src, #16 ldp1 D_l, D_h, src, #16 1: /* * interlace the load of next 64 bytes data block with store of the last * loaded 64 bytes data. */ stp1 A_l, A_h, dst, #16 ldp1 A_l, A_h, src, #16 stp1 B_l, B_h, dst, #16 ldp1 B_l, B_h, src, #16 stp1 C_l, C_h, dst, #16 ldp1 C_l, C_h, src, #16 stp1 D_l, D_h, dst, #16 ldp1 D_l, D_h, src, #16 subs count, count, #64 b.ge 1b stp1 A_l, A_h, dst, #16 stp1 B_l, B_h, dst, #16 stp1 C_l, C_h, dst, #16 stp1 D_l, D_h, dst, #16 tst count, #0x3f b.ne .Ltail63 .Lexitfunc: ret endfunc memcpy ================================================ FILE: kernel/arch/aarch64/lib/memmove.S ================================================ /* * Copyright (C) 2013 ARM Ltd. * Copyright (C) 2013 Linaro. * * This code is based on glibc cortex strings work originally authored by Linaro * and re-licensed under GPLv2 for the Linux kernel. The original code can * be found @ * * http://bazaar.launchpad.net/~linaro-toolchain-dev/cortex-strings/trunk/ * files/head:/src/aarch64/ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include .global memmove /* * Move a buffer from src to test (alignment handled by the hardware). * If dest <= src, call memcpy, otherwise copy in reverse order. * * Parameters: * x0 - dest * x1 - src * x2 - n * Returns: * x0 - dest */ dstin .req x0 src .req x1 count .req x2 tmp1 .req x3 tmp1w .req w3 tmp2 .req x4 tmp2w .req w4 tmp3 .req x5 tmp3w .req w5 dst .req x6 A_l .req x7 A_h .req x8 B_l .req x9 B_h .req x10 C_l .req x11 C_h .req x12 D_l .req x13 D_h .req x14 func memmove cmp dstin, src b.lo memcpy add tmp1, src, count cmp dstin, tmp1 b.hs memcpy /* No overlap. */ add dst, dstin, count add src, src, count cmp count, #16 b.lo .Ltail15 /*probably non-alignment accesses.*/ ands tmp2, src, #15 /* Bytes to reach alignment. */ b.eq .LSrcAligned sub count, count, tmp2 /* * process the aligned offset length to make the src aligned firstly. * those extra instructions' cost is acceptable. It also make the * coming accesses are based on aligned address. */ tbz tmp2, #0, 1f ldrb tmp1w, [src, #-1]! strb tmp1w, [dst, #-1]! 1: tbz tmp2, #1, 2f ldrh tmp1w, [src, #-2]! strh tmp1w, [dst, #-2]! 2: tbz tmp2, #2, 3f ldr tmp1w, [src, #-4]! str tmp1w, [dst, #-4]! 3: tbz tmp2, #3, .LSrcAligned ldr tmp1, [src, #-8]! str tmp1, [dst, #-8]! .LSrcAligned: cmp count, #64 b.ge .Lcpy_over64 /* * Deal with small copies quickly by dropping straight into the * exit block. */ .Ltail63: /* * Copy up to 48 bytes of data. At this point we only need the * bottom 6 bits of count to be accurate. */ ands tmp1, count, #0x30 b.eq .Ltail15 cmp tmp1w, #0x20 b.eq 1f b.lt 2f ldp A_l, A_h, [src, #-16]! stp A_l, A_h, [dst, #-16]! 1: ldp A_l, A_h, [src, #-16]! stp A_l, A_h, [dst, #-16]! 2: ldp A_l, A_h, [src, #-16]! stp A_l, A_h, [dst, #-16]! .Ltail15: tbz count, #3, 1f ldr tmp1, [src, #-8]! str tmp1, [dst, #-8]! 1: tbz count, #2, 2f ldr tmp1w, [src, #-4]! str tmp1w, [dst, #-4]! 2: tbz count, #1, 3f ldrh tmp1w, [src, #-2]! strh tmp1w, [dst, #-2]! 3: tbz count, #0, .Lexitfunc ldrb tmp1w, [src, #-1] strb tmp1w, [dst, #-1] .Lexitfunc: ret .Lcpy_over64: subs count, count, #128 b.ge .Lcpy_body_large /* * Less than 128 bytes to copy, so handle 64 bytes here and then jump * to the tail. */ ldp A_l, A_h, [src, #-16] stp A_l, A_h, [dst, #-16] ldp B_l, B_h, [src, #-32] ldp C_l, C_h, [src, #-48] stp B_l, B_h, [dst, #-32] stp C_l, C_h, [dst, #-48] ldp D_l, D_h, [src, #-64]! stp D_l, D_h, [dst, #-64]! tst count, #0x3f b.ne .Ltail63 ret /* * Critical loop. Start at a new cache line boundary. Assuming * 64 bytes per line this ensures the entire loop is in one line. */ .p2align 6 .Lcpy_body_large: /* pre-load 64 bytes data. */ ldp A_l, A_h, [src, #-16] ldp B_l, B_h, [src, #-32] ldp C_l, C_h, [src, #-48] ldp D_l, D_h, [src, #-64]! 1: /* * interlace the load of next 64 bytes data block with store of the last * loaded 64 bytes data. */ stp A_l, A_h, [dst, #-16] ldp A_l, A_h, [src, #-16] stp B_l, B_h, [dst, #-32] ldp B_l, B_h, [src, #-32] stp C_l, C_h, [dst, #-48] ldp C_l, C_h, [src, #-48] stp D_l, D_h, [dst, #-64]! ldp D_l, D_h, [src, #-64]! subs count, count, #64 b.ge 1b stp A_l, A_h, [dst, #-16] stp B_l, B_h, [dst, #-32] stp C_l, C_h, [dst, #-48] stp D_l, D_h, [dst, #-64]! tst count, #0x3f b.ne .Ltail63 ret endfunc memmove ================================================ FILE: kernel/arch/aarch64/lib/memset.S ================================================ /* * Copyright (C) 2013 ARM Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include /* * Fill in the buffer with character c (alignment handled by the hardware) * * Parameters: * x0 - buf * x1 - c * x2 - n * Returns: * x0 - buf */ .global memset func memset mov x4, x0 and w1, w1, #0xff orr w1, w1, w1, lsl #8 orr w1, w1, w1, lsl #16 orr x1, x1, x1, lsl #32 subs x2, x2, #8 b.mi 2f 1: str x1, [x4], #8 subs x2, x2, #8 b.pl 1b 2: adds x2, x2, #4 b.mi 3f sub x2, x2, #4 str w1, [x4], #4 3: adds x2, x2, #2 b.mi 4f sub x2, x2, #2 strh w1, [x4], #2 4: adds x2, x2, #1 b.mi 5f strb w1, [x4] 5: ret endfunc memset ================================================ FILE: kernel/arch/aarch64/lib/spinlock.S ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include .global arch_spin_lock .global arch_spin_unlock func arch_spin_lock mov w2, #1 sevl l1: wfe l2: ldaxr w1, [x0] cbnz w1, l1 stlxr w1, w2, [x0] cbnz w1, l1 ret endfunc arch_spin_lock func arch_spin_unlock stlr wzr, [x0] ret endfunc arch_spin_unlock ================================================ FILE: kernel/arch/aarch64/lib/strchr.S ================================================ /* * Based on arch/arm/lib/strchr.S * * Copyright (C) 1995-2000 Russell King * Copyright (C) 2013 ARM Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include /* * Find the first occurrence of a character in a string. * * Parameters: * x0 - str * x1 - c * Returns: * x0 - address of first occurrence of 'c' or 0 */ .global strchr func strchr and w1, w1, #0xff 1: ldrb w2, [x0], #1 cmp w2, w1 ccmp w2, wzr, #4, ne b.ne 1b sub x0, x0, #1 cmp w2, w1 csel x0, x0, xzr, eq ret endfunc strchr ================================================ FILE: kernel/arch/aarch64/lib/strcmp.S ================================================ /* * Copyright (C) 2013 ARM Ltd. * Copyright (C) 2013 Linaro. * * This code is based on glibc cortex strings work originally authored by Linaro * and re-licensed under GPLv2 for the Linux kernel. The original code can * be found @ * * http://bazaar.launchpad.net/~linaro-toolchain-dev/cortex-strings/trunk/ * files/head:/src/aarch64/ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include .global strcmp #define REP8_01 0x0101010101010101 #define REP8_7f 0x7f7f7f7f7f7f7f7f #define REP8_80 0x8080808080808080 /* Parameters and result. */ src1 .req x0 src2 .req x1 result .req x0 /* Internal variables. */ data1 .req x2 data1w .req w2 data2 .req x3 data2w .req w3 has_nul .req x4 diff .req x5 syndrome .req x6 tmp1 .req x7 tmp2 .req x8 tmp3 .req x9 zeroones .req x10 pos .req x11 func strcmp eor tmp1, src1, src2 mov zeroones, #REP8_01 tst tmp1, #7 b.ne .Lmisaligned8 ands tmp1, src1, #7 b.ne .Lmutual_align /* * NUL detection works on the principle that (X - 1) & (~X) & 0x80 * (=> (X - 1) & ~(X | 0x7f)) is non-zero iff a byte is zero, and * can be done in parallel across the entire word. */ .Lloop_aligned: ldr data1, [src1], #8 ldr data2, [src2], #8 .Lstart_realigned: sub tmp1, data1, zeroones orr tmp2, data1, #REP8_7f eor diff, data1, data2 /* Non-zero if differences found. */ bic has_nul, tmp1, tmp2 /* Non-zero if NUL terminator. */ orr syndrome, diff, has_nul cbz syndrome, .Lloop_aligned b .Lcal_cmpresult .Lmutual_align: /* * Sources are mutually aligned, but are not currently at an * alignment boundary. Round down the addresses and then mask off * the bytes that preceed the start point. */ bic src1, src1, #7 bic src2, src2, #7 lsl tmp1, tmp1, #3 /* Bytes beyond alignment -> bits. */ ldr data1, [src1], #8 neg tmp1, tmp1 /* Bits to alignment -64. */ ldr data2, [src2], #8 mov tmp2, #~0 /* Big-endian. Early bytes are at MSB. */ /* Little-endian. Early bytes are at LSB. */ lsr tmp2, tmp2, tmp1 /* Shift (tmp1 & 63). */ orr data1, data1, tmp2 orr data2, data2, tmp2 b .Lstart_realigned .Lmisaligned8: /* * Get the align offset length to compare per byte first. * After this process, one string's address will be aligned. */ and tmp1, src1, #7 neg tmp1, tmp1 add tmp1, tmp1, #8 and tmp2, src2, #7 neg tmp2, tmp2 add tmp2, tmp2, #8 subs tmp3, tmp1, tmp2 csel pos, tmp1, tmp2, hi /*Choose the maximum. */ .Ltinycmp: ldrb data1w, [src1], #1 ldrb data2w, [src2], #1 subs pos, pos, #1 ccmp data1w, #1, #0, ne /* NZCV = 0b0000. */ ccmp data1w, data2w, #0, cs /* NZCV = 0b0000. */ b.eq .Ltinycmp cbnz pos, 1f /*find the null or unequal...*/ cmp data1w, #1 ccmp data1w, data2w, #0, cs b.eq .Lstart_align /*the last bytes are equal....*/ 1: sub result, data1, data2 ret .Lstart_align: ands xzr, src1, #7 b.eq .Lrecal_offset /*process more leading bytes to make str1 aligned...*/ add src1, src1, tmp3 add src2, src2, tmp3 /*load 8 bytes from aligned str1 and non-aligned str2..*/ ldr data1, [src1], #8 ldr data2, [src2], #8 sub tmp1, data1, zeroones orr tmp2, data1, #REP8_7f bic has_nul, tmp1, tmp2 eor diff, data1, data2 /* Non-zero if differences found. */ orr syndrome, diff, has_nul cbnz syndrome, .Lcal_cmpresult /*How far is the current str2 from the alignment boundary...*/ and tmp3, tmp3, #7 .Lrecal_offset: neg pos, tmp3 .Lloopcmp_proc: /* * Divide the eight bytes into two parts. First,backwards the src2 * to an alignment boundary,load eight bytes from the SRC2 alignment * boundary,then compare with the relative bytes from SRC1. * If all 8 bytes are equal,then start the second part's comparison. * Otherwise finish the comparison. * This special handle can garantee all the accesses are in the * thread/vcpu space in avoid to overrange access. */ ldr data1, [src1,pos] ldr data2, [src2,pos] sub tmp1, data1, zeroones orr tmp2, data1, #REP8_7f bic has_nul, tmp1, tmp2 eor diff, data1, data2 /* Non-zero if differences found. */ orr syndrome, diff, has_nul cbnz syndrome, .Lcal_cmpresult /*The second part process*/ ldr data1, [src1], #8 ldr data2, [src2], #8 sub tmp1, data1, zeroones orr tmp2, data1, #REP8_7f bic has_nul, tmp1, tmp2 eor diff, data1, data2 /* Non-zero if differences found. */ orr syndrome, diff, has_nul cbz syndrome, .Lloopcmp_proc .Lcal_cmpresult: /* * reversed the byte-order as big-endian,then CLZ can find the most * significant zero bits. */ rev syndrome, syndrome rev data1, data1 rev data2, data2 /* * For big-endian we cannot use the trick with the syndrome value * as carry-propagation can corrupt the upper bits if the trailing * bytes in the string contain 0x01. * However, if there is no NUL byte in the dword, we can generate * the result directly. We ca not just subtract the bytes as the * MSB might be significant. */ /*Re-compute the NUL-byte detection, using a byte-reversed value. */ clz pos, syndrome /* * The MS-non-zero bit of the syndrome marks either the first bit * that is different, or the top bit of the first zero byte. * Shifting left now will bring the critical information into the * top bits. */ lsl data1, data1, pos lsl data2, data2, pos /* * But we need to zero-extend (char is unsigned) the value and then * perform a signed 32-bit subtraction. */ lsr data1, data1, #56 sub result, data1, data2, lsr #56 ret endfunc strcmp ================================================ FILE: kernel/arch/aarch64/lib/strcpy.S ================================================ /* * Copyright (C) 2013 ARM Ltd. * Copyright (C) 2013 Linaro. * Copyright (C) 2014 The Android Open Source Project * * This code is based on glibc cortex strings work originally authored by Linaro * and re-licensed under GPLv2 for the Linux kernel. The original code can * be found @ * * http://bazaar.launchpad.net/~linaro-toolchain-dev/cortex-strings/trunk/ * files/head:/src/aarch64/ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include .global strcpy dst_in .req x0 src .req x1 dst .req x2 data_1 .req x3 data_1_w .req w3 data2 .req x4 data2_w .req w4 has_nul1 .req x5 has_nul1_w .req w5 has_nul2 .req x6 tmp1 .req x7 tmp2 .req x8 tmp3 .req x9 tmp4 .req x10 zero_oness .req x11 zero_oness_w .req w11 pos .req x12 #define REP8_01 0x0101010101010101 #define REP8_7f 0x7f7f7f7f7f7f7f7f #define REP8_80 0x8080808080808080 func strcpy mov zero_oness, #REP8_01 mov dst, dst_in ands tmp1, src, #15 b.ne .Lmisaligned .Lloop: ldp data_1, data2, [src], #16 sub tmp1, data_1, zero_oness orr tmp2, data_1, #REP8_7f bic has_nul1, tmp1, tmp2 cbnz has_nul1, .Lnul_in_data_1 sub tmp3, data2, zero_oness orr tmp4, data2, #REP8_7f bic has_nul2, tmp3, tmp4 cbnz has_nul2, .Lnul_in_data2 stp data_1, data2, [dst], #16 b .Lloop .Lnul_in_data_1: rev has_nul1, has_nul1 clz pos, has_nul1 add tmp1, pos, #0x8 tbz tmp1, #6, 1f str data_1, [dst] ret 1: tbz tmp1, #5, 1f str data_1_w, [dst], #4 lsr data_1, data_1, #32 1: tbz tmp1, #4, 1f strh data_1_w, [dst], #2 lsr data_1, data_1, #16 1: tbz tmp1, #3, 1f strb data_1_w, [dst] 1: ret .Lnul_in_data2: str data_1, [dst], #8 rev has_nul2, has_nul2 clz pos, has_nul2 add tmp1, pos, #0x8 tbz tmp1, #6, 1f str data2, [dst] ret 1: tbz tmp1, #5, 1f str data2_w, [dst], #4 lsr data2, data2, #32 1: tbz tmp1, #4, 1f strh data2_w, [dst], #2 lsr data2, data2, #16 1: tbz tmp1, #3, 1f strb data2_w, [dst] 1: ret .Lmisaligned: tbz src, #0, 1f ldrb data_1_w, [src], #1 strb data_1_w, [dst], #1 cbnz data_1_w, 1f ret 1: tbz src, #1, 1f ldrb data_1_w, [src], #1 strb data_1_w, [dst], #1 cbz data_1_w, .Ldone ldrb data2_w, [src], #1 strb data2_w, [dst], #1 cbnz data2_w, 1f .Ldone: ret 1: tbz src, #2, 1f ldr data_1_w, [src], #4 sub has_nul1_w, data_1_w, zero_oness_w bic has_nul1_w, has_nul1_w, data_1_w ands has_nul1_w, has_nul1_w, #0x80808080 b.ne .Lnul_in_data_1 str data_1_w, [dst], #4 1: tbz src, #3, .Lloop ldr data_1, [src], #8 sub tmp1, data_1, zero_oness orr tmp2, data_1, #REP8_7f bics has_nul1, tmp1, tmp2 b.ne .Lnul_in_data_1 str data_1, [dst], #8 b .Lloop endfunc strcpy ================================================ FILE: kernel/arch/aarch64/lib/strlen.S ================================================ /* * Copyright (C) 2013 ARM Ltd. * Copyright (C) 2013 Linaro. * * This code is based on glibc cortex strings work originally authored by Linaro * and re-licensed under GPLv2 for the Linux kernel. The original code can * be found @ * * http://bazaar.launchpad.net/~linaro-toolchain-dev/cortex-strings/trunk/ * files/head:/src/aarch64/ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include .global strlen /* * calculate the length of a string * * Parameters: * x0 - const string pointer * Returns: * x0 - the return length of specific string */ /* Arguments and results. */ srcin .req x0 len .req x0 /* Locals and temporaries. */ src .req x1 data1 .req x2 data2 .req x3 data2a .req x4 has_nul1 .req x5 has_nul2 .req x6 tmp1 .req x7 tmp2 .req x8 tmp3 .req x9 tmp4 .req x10 zeroones .req x11 pos .req x12 #define REP8_01 0x0101010101010101 #define REP8_7f 0x7f7f7f7f7f7f7f7f #define REP8_80 0x8080808080808080 func strlen mov zeroones, #REP8_01 bic src, srcin, #15 ands tmp1, srcin, #15 b.ne .Lmisaligned /* * NUL detection works on the principle that (X - 1) & (~X) & 0x80 * (=> (X - 1) & ~(X | 0x7f)) is non-zero iff a byte is zero, and * can be done in parallel across the entire word. */ /* * The inner loop deals with two Dwords at a time. This has a * slightly higher start-up cost, but we should win quite quickly, * especially on cores with a high number of issue slots per * cycle, as we get much better parallelism out of the operations. */ .Lloop: ldp data1, data2, [src], #16 .Lrealigned: sub tmp1, data1, zeroones orr tmp2, data1, #REP8_7f sub tmp3, data2, zeroones orr tmp4, data2, #REP8_7f bic has_nul1, tmp1, tmp2 bics has_nul2, tmp3, tmp4 ccmp has_nul1, #0, #0, eq /* NZCV = 0000 */ b.eq .Lloop sub len, src, srcin cbz has_nul1, .Lnul_in_data2 sub len, len, #8 mov has_nul2, has_nul1 .Lnul_in_data2: /* * For big-endian, carry propagation (if the final byte in the * string is 0x01) means we cannot use has_nul directly. The * easiest way to get the correct byte is to byte-swap the data * and calculate the syndrome a second time. */ sub len, len, #8 rev has_nul2, has_nul2 clz pos, has_nul2 add len, len, pos, lsr #3 /* Bits to bytes. */ ret .Lmisaligned: cmp tmp1, #8 neg tmp1, tmp1 ldp data1, data2, [src], #16 lsl tmp1, tmp1, #3 /* Bytes beyond alignment -> bits. */ mov tmp2, #~0 /* Big-endian. Early bytes are at MSB. */ /* Little-endian. Early bytes are at LSB. */ lsr tmp2, tmp2, tmp1 /* Shift (tmp1 & 63). */ orr data1, data1, tmp2 orr data2a, data2, tmp2 csinv data1, data1, xzr, le csel data2, data2, data2a, le b .Lrealigned endfunc strlen ================================================ FILE: kernel/arch/aarch64/lib/strncmp.S ================================================ /* * Copyright (C) 2013 ARM Ltd. * Copyright (C) 2013 Linaro. * * This code is based on glibc cortex strings work originally authored by Linaro * and re-licensed under GPLv2 for the Linux kernel. The original code can * be found @ * * http://bazaar.launchpad.net/~linaro-toolchain-dev/cortex-strings/trunk/ * files/head:/src/aarch64/ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include .global strncmp /* * compare two strings * * Parameters: * x0 - const string 1 pointer * x1 - const string 2 pointer * x2 - the maximal length to be compared * Returns: * x0 - an integer less than, equal to, or greater than zero if s1 is found, * respectively, to be less than, to match, or be greater than s2. */ #define REP8_01 0x0101010101010101 #define REP8_7f 0x7f7f7f7f7f7f7f7f #define REP8_80 0x8080808080808080 /* Parameters and result. */ src1 .req x0 src2 .req x1 limit .req x2 result .req x0 /* Internal variables. */ data1 .req x3 data1w .req w3 data2 .req x4 data2w .req w4 has_nul .req x5 diff .req x6 syndrome .req x7 tmp1 .req x8 tmp2 .req x9 tmp3 .req x10 zeroones .req x11 pos .req x12 limit_wd .req x13 mask .req x14 endloop .req x15 func strncmp cbz limit, .Lret0 eor tmp1, src1, src2 mov zeroones, #REP8_01 tst tmp1, #7 b.ne .Lmisaligned8 ands tmp1, src1, #7 b.ne .Lmutual_align /* Calculate the number of full and partial words -1. */ /* * when limit is mulitply of 8, if not sub 1, * the judgement of last dword will wrong. */ sub limit_wd, limit, #1 /* limit != 0, so no underflow. */ lsr limit_wd, limit_wd, #3 /* Convert to Dwords. */ /* * NUL detection works on the principle that (X - 1) & (~X) & 0x80 * (=> (X - 1) & ~(X | 0x7f)) is non-zero iff a byte is zero, and * can be done in parallel across the entire word. */ .Lloop_aligned: ldr data1, [src1], #8 ldr data2, [src2], #8 .Lstart_realigned: subs limit_wd, limit_wd, #1 sub tmp1, data1, zeroones orr tmp2, data1, #REP8_7f eor diff, data1, data2 /* Non-zero if differences found. */ csinv endloop, diff, xzr, pl /* Last Dword or differences.*/ bics has_nul, tmp1, tmp2 /* Non-zero if NUL terminator. */ ccmp endloop, #0, #0, eq b.eq .Lloop_aligned /*Not reached the limit, must have found the end or a diff. */ tbz limit_wd, #63, .Lnot_limit /* Limit % 8 == 0 => all bytes significant. */ ands limit, limit, #7 b.eq .Lnot_limit lsl limit, limit, #3 /* Bits -> bytes. */ mov mask, #~0 lsl mask, mask, limit bic data1, data1, mask bic data2, data2, mask /* Make sure that the NUL byte is marked in the syndrome. */ orr has_nul, has_nul, mask .Lnot_limit: orr syndrome, diff, has_nul b .Lcal_cmpresult .Lmutual_align: /* * Sources are mutually aligned, but are not currently at an * alignment boundary. Round down the addresses and then mask off * the bytes that precede the start point. * We also need to adjust the limit calculations, but without * overflowing if the limit is near ULONG_MAX. */ bic src1, src1, #7 bic src2, src2, #7 ldr data1, [src1], #8 neg tmp3, tmp1, lsl #3 /* 64 - bits(bytes beyond align). */ ldr data2, [src2], #8 mov tmp2, #~0 sub limit_wd, limit, #1 /* limit != 0, so no underflow. */ /* Big-endian. Early bytes are at MSB. */ /* Little-endian. Early bytes are at LSB. */ lsr tmp2, tmp2, tmp3 /* Shift (tmp1 & 63). */ and tmp3, limit_wd, #7 lsr limit_wd, limit_wd, #3 /* Adjust the limit. Only low 3 bits used, so overflow irrelevant.*/ add limit, limit, tmp1 add tmp3, tmp3, tmp1 orr data1, data1, tmp2 orr data2, data2, tmp2 add limit_wd, limit_wd, tmp3, lsr #3 b .Lstart_realigned /*when src1 offset is not equal to src2 offset...*/ .Lmisaligned8: cmp limit, #8 b.lo .Ltiny8proc /*limit < 8... */ /* * Get the align offset length to compare per byte first. * After this process, one string's address will be aligned.*/ and tmp1, src1, #7 neg tmp1, tmp1 add tmp1, tmp1, #8 and tmp2, src2, #7 neg tmp2, tmp2 add tmp2, tmp2, #8 subs tmp3, tmp1, tmp2 csel pos, tmp1, tmp2, hi /*Choose the maximum. */ /* * Here, limit is not less than 8, so directly run .Ltinycmp * without checking the limit.*/ sub limit, limit, pos .Ltinycmp: ldrb data1w, [src1], #1 ldrb data2w, [src2], #1 subs pos, pos, #1 ccmp data1w, #1, #0, ne /* NZCV = 0b0000. */ ccmp data1w, data2w, #0, cs /* NZCV = 0b0000. */ b.eq .Ltinycmp cbnz pos, 1f /*find the null or unequal...*/ cmp data1w, #1 ccmp data1w, data2w, #0, cs b.eq .Lstart_align /*the last bytes are equal....*/ 1: sub result, data1, data2 ret .Lstart_align: lsr limit_wd, limit, #3 cbz limit_wd, .Lremain8 /*process more leading bytes to make str1 aligned...*/ ands xzr, src1, #7 b.eq .Lrecal_offset add src1, src1, tmp3 /*tmp3 is positive in this branch.*/ add src2, src2, tmp3 ldr data1, [src1], #8 ldr data2, [src2], #8 sub limit, limit, tmp3 lsr limit_wd, limit, #3 subs limit_wd, limit_wd, #1 sub tmp1, data1, zeroones orr tmp2, data1, #REP8_7f eor diff, data1, data2 /* Non-zero if differences found. */ csinv endloop, diff, xzr, ne/*if limit_wd is 0,will finish the cmp*/ bics has_nul, tmp1, tmp2 ccmp endloop, #0, #0, eq /*has_null is ZERO: no null byte*/ b.ne .Lunequal_proc /*How far is the current str2 from the alignment boundary...*/ and tmp3, tmp3, #7 .Lrecal_offset: neg pos, tmp3 .Lloopcmp_proc: /* * Divide the eight bytes into two parts. First,backwards the src2 * to an alignment boundary,load eight bytes from the SRC2 alignment * boundary,then compare with the relative bytes from SRC1. * If all 8 bytes are equal,then start the second part's comparison. * Otherwise finish the comparison. * This special handle can garantee all the accesses are in the * thread/vcpu space in avoid to overrange access. */ ldr data1, [src1,pos] ldr data2, [src2,pos] sub tmp1, data1, zeroones orr tmp2, data1, #REP8_7f bics has_nul, tmp1, tmp2 /* Non-zero if NUL terminator. */ eor diff, data1, data2 /* Non-zero if differences found. */ csinv endloop, diff, xzr, eq cbnz endloop, .Lunequal_proc /*The second part process*/ ldr data1, [src1], #8 ldr data2, [src2], #8 subs limit_wd, limit_wd, #1 sub tmp1, data1, zeroones orr tmp2, data1, #REP8_7f eor diff, data1, data2 /* Non-zero if differences found. */ csinv endloop, diff, xzr, ne/*if limit_wd is 0,will finish the cmp*/ bics has_nul, tmp1, tmp2 ccmp endloop, #0, #0, eq /*has_null is ZERO: no null byte*/ b.eq .Lloopcmp_proc .Lunequal_proc: orr syndrome, diff, has_nul cbz syndrome, .Lremain8 .Lcal_cmpresult: /* * reversed the byte-order as big-endian,then CLZ can find the most * significant zero bits. */ rev syndrome, syndrome rev data1, data1 rev data2, data2 /* * For big-endian we cannot use the trick with the syndrome value * as carry-propagation can corrupt the upper bits if the trailing * bytes in the string contain 0x01. * However, if there is no NUL byte in the dword, we can generate * the result directly. We can't just subtract the bytes as the * MSB might be significant. */ /* * The MS-non-zero bit of the syndrome marks either the first bit * that is different, or the top bit of the first zero byte. * Shifting left now will bring the critical information into the * top bits. */ clz pos, syndrome lsl data1, data1, pos lsl data2, data2, pos /* * But we need to zero-extend (char is unsigned) the value and then * perform a signed 32-bit subtraction. */ lsr data1, data1, #56 sub result, data1, data2, lsr #56 ret .Lremain8: /* Limit % 8 == 0 => all bytes significant. */ ands limit, limit, #7 b.eq .Lret0 .Ltiny8proc: ldrb data1w, [src1], #1 ldrb data2w, [src2], #1 subs limit, limit, #1 ccmp data1w, #1, #0, ne /* NZCV = 0b0000. */ ccmp data1w, data2w, #0, cs /* NZCV = 0b0000. */ b.eq .Ltiny8proc sub result, data1, data2 ret .Lret0: mov result, #0 ret endfunc strncmp ================================================ FILE: kernel/arch/aarch64/lib/strnlen.S ================================================ /* * Copyright (C) 2013 ARM Ltd. * Copyright (C) 2013 Linaro. * * This code is based on glibc cortex strings work originally authored by Linaro * and re-licensed under GPLv2 for the Linux kernel. The original code can * be found @ * * http://bazaar.launchpad.net/~linaro-toolchain-dev/cortex-strings/trunk/ * files/head:/src/aarch64/ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include .global strnlen /* * determine the length of a fixed-size string * * Parameters: * x0 - const string pointer * x1 - maximal string length * Returns: * x0 - the return length of specific string */ /* Arguments and results. */ srcin .req x0 len .req x0 limit .req x1 /* Locals and temporaries. */ src .req x2 data1 .req x3 data2 .req x4 data2a .req x5 has_nul1 .req x6 has_nul2 .req x7 tmp1 .req x8 tmp2 .req x9 tmp3 .req x10 tmp4 .req x11 zeroones .req x12 pos .req x13 limit_wd .req x14 #define REP8_01 0x0101010101010101 #define REP8_7f 0x7f7f7f7f7f7f7f7f #define REP8_80 0x8080808080808080 func strnlen cbz limit, .Lhit_limit mov zeroones, #REP8_01 bic src, srcin, #15 ands tmp1, srcin, #15 b.ne .Lmisaligned /* Calculate the number of full and partial words -1. */ sub limit_wd, limit, #1 /* Limit != 0, so no underflow. */ lsr limit_wd, limit_wd, #4 /* Convert to Qwords. */ /* * NUL detection works on the principle that (X - 1) & (~X) & 0x80 * (=> (X - 1) & ~(X | 0x7f)) is non-zero iff a byte is zero, and * can be done in parallel across the entire word. */ /* * The inner loop deals with two Dwords at a time. This has a * slightly higher start-up cost, but we should win quite quickly, * especially on cores with a high number of issue slots per * cycle, as we get much better parallelism out of the operations. */ .Lloop: ldp data1, data2, [src], #16 .Lrealigned: sub tmp1, data1, zeroones orr tmp2, data1, #REP8_7f sub tmp3, data2, zeroones orr tmp4, data2, #REP8_7f bic has_nul1, tmp1, tmp2 bic has_nul2, tmp3, tmp4 subs limit_wd, limit_wd, #1 orr tmp1, has_nul1, has_nul2 ccmp tmp1, #0, #0, pl /* NZCV = 0000 */ b.eq .Lloop cbz tmp1, .Lhit_limit /* No null in final Qword. */ /* * We know there's a null in the final Qword. The easiest thing * to do now is work out the length of the string and return * MIN (len, limit). */ sub len, src, srcin cbz has_nul1, .Lnul_in_data2 sub len, len, #8 mov has_nul2, has_nul1 .Lnul_in_data2: /* * For big-endian, carry propagation (if the final byte in the * string is 0x01) means we cannot use has_nul directly. The * easiest way to get the correct byte is to byte-swap the data * and calculate the syndrome a second time. */ sub len, len, #8 rev has_nul2, has_nul2 clz pos, has_nul2 add len, len, pos, lsr #3 /* Bits to bytes. */ cmp len, limit csel len, len, limit, ls /* Return the lower value. */ ret .Lmisaligned: /* * Deal with a partial first word. * We're doing two things in parallel here; * 1) Calculate the number of words (but avoiding overflow if * limit is near ULONG_MAX) - to do this we need to work out * limit + tmp1 - 1 as a 65-bit value before shifting it; * 2) Load and mask the initial data words - we force the bytes * before the ones we are interested in to 0xff - this ensures * early bytes will not hit any zero detection. */ ldp data1, data2, [src], #16 sub limit_wd, limit, #1 and tmp3, limit_wd, #15 lsr limit_wd, limit_wd, #4 add tmp3, tmp3, tmp1 add limit_wd, limit_wd, tmp3, lsr #4 neg tmp4, tmp1 lsl tmp4, tmp4, #3 /* Bytes beyond alignment -> bits. */ mov tmp2, #~0 /* Big-endian. Early bytes are at MSB. */ /* Little-endian. Early bytes are at LSB. */ lsr tmp2, tmp2, tmp4 /* Shift (tmp1 & 63). */ cmp tmp1, #8 orr data1, data1, tmp2 orr data2a, data2, tmp2 csinv data1, data1, xzr, le csel data2, data2, data2a, le b .Lrealigned .Lhit_limit: mov len, limit ret endfunc strnlen ================================================ FILE: kernel/arch/aarch64/lib/strrchr.S ================================================ /* * Based on arch/arm/lib/strrchr.S * * Copyright (C) 1995-2000 Russell King * Copyright (C) 2013 ARM Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include .global strrchr /* * Find the last occurrence of a character in a string. * * Parameters: * x0 - str * x1 - c * Returns: * x0 - address of last occurrence of 'c' or 0 */ func strrchr mov x3, #0 and w1, w1, #0xff 1: ldrb w2, [x0], #1 cbz w2, 2f cmp w2, w1 b.ne 1b sub x3, x0, #1 b 1b 2: mov x0, x3 ret endfunc strrchr ================================================ FILE: kernel/arch/aarch64/lib/ticket_lock.S ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include .global arch_ticket_lock .global arch_ticket_unlock .global arch_ticket_trylock func arch_ticket_lock prfm pstl1keep, [x0] add x4, x0, #4 loop1: ldaxr w2, [x4] add w2, w2, #1 stlxr w3, w2, [x4] cbnz w3, loop1 sub w2, w2, #1 ldaxr w1, [x0] cmp w1, w2 b.eq out sevl loop2: wfe ldaxr w1, [x0] cmp w1, w2 b.ne loop2 out: ret endfunc arch_ticket_lock func arch_ticket_trylock prfm pstl1keep, [x0] add x4, x0, #4 mov w3, #0 ldaxr w1, [x0] ldaxr w2, [x4] cmp w1, w2 b.ne fail_trylock add w2, w2, #1 stlxr w3, w2, [x4] cbnz w3, fail_trylock mov w3, #1 /* need double check ? */ fail_trylock: mov w0, w3 ret endfunc arch_ticket_trylock func arch_ticket_unlock ldar w1, [x0] add w1, w1, #1 stlr w1, [x0] dsb ish ret endfunc arch_ticket_unlock ================================================ FILE: kernel/arch/aarch64/userspace/Makefile ================================================ obj-y += asm_syscall.o ================================================ FILE: kernel/arch/aarch64/userspace/asm_syscall.c ================================================ /* * Copyright (C) 2020 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include struct aarch64_syscall_reg { unsigned long regs[8]; } __packed; typedef void (*syscall_handler_t)(gp_regs *regs); static void aarch64_syscall_unsupport(gp_regs *regs) { pr_err("Unsupported syscall:%d\n", regs->x8); regs->x0 = -ENOENT; } static void __sys_kobject_close(gp_regs *regs) { regs->x0 = sys_kobject_close((handle_t)regs->x0); } static void __sys_kobject_create(gp_regs *regs) { regs->x0 = sys_kobject_create((int)regs->x0, (unsigned long)regs->x1); } static void __sys_kobject_recv(gp_regs *regs) { size_t data = 0, extra = 0; regs->x0 = sys_kobject_recv( (int)regs->x0, (void __user *)regs->x1, (size_t)regs->x2, &data, (void __user *)regs->x3, (size_t)regs->x4, &extra, (uint32_t)regs->x5); regs->x1 = data; regs->x2 = extra; } static void __sys_kobject_send(gp_regs *regs) { regs->x0 = sys_kobject_send( (int)regs->x0, (void __user *)regs->x1, (size_t)regs->x2, (void __user *)regs->x3, (size_t)regs->x4, (uint32_t)regs->x5); } static void __sys_kobject_reply(gp_regs *regs) { regs->x0 = sys_kobject_reply( (int)regs->x0, (unsigned long)regs->x1, (long)regs->x2, (handle_t)regs->x3, (right_t)regs->x4); } static void __sys_kobject_ctl(gp_regs *regs) { regs->x0 = sys_kobject_ctl((handle_t)regs->x0, (int)regs->x1, (unsigned long)regs->x2); } static void __sys_kobject_mmap(gp_regs *regs) { void *addr = 0; unsigned long mapsz = 0; regs->x0 = (unsigned long)sys_kobject_mmap((handle_t)regs->x0, &addr, &mapsz); regs->x1 = (unsigned long)addr; regs->x2 = mapsz; } static void __sys_kobject_munmap(gp_regs *regs) { regs->x0 = sys_kobject_munmap((handle_t)regs->x0); } static void __sys_kobject_open(gp_regs *regs) { regs->x0 = sys_kobject_open((handle_t)regs->x0); } static void __sys_map(gp_regs *regs) { regs->x0 = sys_map((handle_t)regs->x0, (handle_t)regs->x1, (unsigned long)regs->x2, (size_t)regs->x3, (right_t)regs->x4); } static void __sys_unmap(gp_regs *regs) { regs->x0 = sys_unmap((handle_t)regs->x0, (handle_t)regs->x1, (unsigned long)regs->x2, (size_t)regs->x3); } static void __sys_yield(gp_regs *regs) { sys_sched_yield(); } static void __sys_futex(gp_regs *regs) { regs->x0 = sys_futex( (uint32_t __user *)regs->x0, (int)regs->x1, (uint32_t)regs->x2, (struct timespec __user *)regs->x3, (uint32_t __user *)regs->x4, (uint32_t)regs->x5); } static void __sys_grant(gp_regs *regs) { regs->x0 = sys_grant((handle_t)regs->x0, (handle_t)regs->x1, (right_t)regs->x2, (int)regs->x3); } static void __sys_mtrans(gp_regs *regs) { regs->x0 = sys_mtrans((unsigned long)regs->x0); } static void __sys_clock_gettime(gp_regs *regs) { regs->x0 = sys_clock_gettime((int)regs->x0, (struct timespec __user *)regs->x1); } static void __sys_clock_nanosleep(gp_regs *regs) { regs->x0 = sys_clock_nanosleep((int)regs->x0, (int)regs->x1, (int64_t)regs->x2, (long)regs->x3, (struct timespec __user *)regs->x4); } static void __sys_exit(gp_regs *regs) { sys_exit((int)regs->x0); } static void __sys_exitgroup(gp_regs *regs) { sys_exitgroup((int)regs->x0); } static void __sys_clone(gp_regs *regs) { regs->x0 = sys_clone((int)regs->x0, (void __user *)regs->x1, (int __user *)regs->x2, (void __user *)regs->x3, (int __user *)regs->x4); } static syscall_handler_t __syscall_table[] = { [0 ... __NR_syscalls] = aarch64_syscall_unsupport, [__NR_kobject_open] = __sys_kobject_open, [__NR_kobject_create] = __sys_kobject_create, [__NR_kobject_reply] = __sys_kobject_reply, [__NR_kobject_send] = __sys_kobject_send, [__NR_kobject_recv] = __sys_kobject_recv, [__NR_kobject_close] = __sys_kobject_close, [__NR_kobject_ctl] = __sys_kobject_ctl, [__NR_kobject_mmap] = __sys_kobject_mmap, [__NR_kobject_munmap] = __sys_kobject_munmap, [__NR_grant] = __sys_grant, [__NR_yield] = __sys_yield, [__NR_futex] = __sys_futex, [__NR_map] = __sys_map, [__NR_unmap] = __sys_unmap, [__NR_trans] = __sys_mtrans, [__NR_clock_gettime] = __sys_clock_gettime, [__NR_clock_nanosleep] = __sys_clock_nanosleep, [__NR_exit] = __sys_exit, [__NR_exitgroup] = __sys_exitgroup, [__NR_clone] = __sys_clone, }; void aarch64_do_syscall(gp_regs *regs) { int nr = regs->x8; arch_enable_local_irq(); if (nr >= __NR_syscalls) { regs->x0 = -EINVAL; return; } __syscall_table[nr](regs); arch_disable_local_irq(); } ================================================ FILE: kernel/arch/aarch64/virt/Kconfig ================================================ menu "Minos aarch64 virtualaztion features" config ARM_VHE bool "enable VHE feature for minos" depends on VIRT default n help this require at leaset armv8.1 soc endmenu ================================================ FILE: kernel/arch/aarch64/virt/Makefile ================================================ obj-y += arch_virt.o obj-y += smc_service.o obj-y += svc_service.o obj-y += trap.o obj-y += vmsa.o obj-y += vtimer.o obj-y += vfp.o obj-y += stage2.o ================================================ FILE: kernel/arch/aarch64/virt/arch_virt.c ================================================ /* * Copyright (C) 2019 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static uint32_t mpidr_el1[NR_CPUS]; void flush_all_tlb_mm(struct mm_struct *mm) { struct vm *vm = container_of(mm, struct vm, mm); uint64_t vttbr = vtop(mm->pgdp) | ((uint64_t)vm->vmid << 48); unsigned long flags; uint64_t old_vttbr; local_irq_save(flags); /* * switch to the target VM's VTTBR to VTTBR_EL2, make sure * use the correct vmid. */ old_vttbr = read_sysreg(VTTBR_EL2); write_sysreg(vttbr, VTTBR_EL2); /* * flush all the tlb with the vmid. */ flush_all_tlb_guest(); /* * restore the origin vttbr. */ write_sysreg(old_vttbr, VTTBR_EL2); local_irq_restore(flags); } void arch_set_virq_flag(void) { uint64_t hcr_el2; hcr_el2 = read_sysreg(HCR_EL2); hcr_el2 |= HCR_EL2_VI; write_sysreg(hcr_el2, HCR_EL2); dsb(); } void arch_set_vfiq_flag(void) { uint64_t hcr_el2; hcr_el2 = read_sysreg(HCR_EL2); hcr_el2 |= HCR_EL2_VF; write_sysreg(hcr_el2, HCR_EL2); dsb(); } void arch_clear_virq_flag(void) { uint64_t hcr_el2; hcr_el2 = read_sysreg(HCR_EL2); hcr_el2 &= ~HCR_EL2_VI; hcr_el2 &= ~HCR_EL2_VF; write_sysreg(hcr_el2, HCR_EL2); dsb(); } void arch_clear_vfiq_flag(void) { uint64_t hcr_el2; hcr_el2 = read_sysreg(HCR_EL2); hcr_el2 &= ~HCR_EL2_VF; write_sysreg(hcr_el2, HCR_EL2); dsb(); } void arch_vcpu_init(struct vcpu *vcpu, void *entry, void *arg) { struct task *task = vcpu->task; gp_regs *regs; regs = stack_to_gp_regs(task->stack_top); memset(regs, 0, sizeof(gp_regs)); task->stack_base = task->stack_top - sizeof(gp_regs); regs->pc = (uint64_t)entry; if (task_is_32bit(vcpu->task)) regs->pstate = AARCH32_SVC | \ AARCH64_SPSR_F | \ AARCH64_SPSR_I | \ AARCH64_SPSR_A | (1 << 4); else regs->pstate = AARCH64_SPSR_EL1h | \ AARCH64_SPSR_F | \ AARCH64_SPSR_I | \ AARCH64_SPSR_A; } static inline uint64_t generate_vtcr_el2(void) { uint64_t value = 0; /* * vtcr_el2 used to defined the memory attribute * for the EL1, this is defined by hypervisor * and may do not related to physical information */ value |= (24 << 0); // t0sz = 0x10 40bits vaddr value |= (0x01 << 6); // SL0: 4kb start at level1 value |= (0x1 << 8); // Normal memory, Inner WBWA value |= (0x1 << 10); // Normal memory, Outer WBWA value |= (0x3 << 12); // Inner Shareable // TG0 4K value |= (0x0 << 14); // PS --- pysical size 1TB value |= (2 << 16); // vmid -- 8bit value |= (0x0 << 19); return value; } static inline uint64_t generate_vttbr_el2(uint32_t vmid, unsigned long base) { return (base | ((uint64_t)vmid << 48)); } void arch_vcpu_state_init(struct vcpu *vcpu, void *c) { struct vcpu_context *context = (struct vcpu_context *)c; struct vm *vm = vcpu->vm; uint64_t value; memset(context, 0, sizeof(*context)); /* * HVC : enable hyper call function * TWI : trap wfi - default enable, disable by dts * TWE : trap wfe - default disable * TIDCP : Trap implementation defined functionality * IMP : physical irq routing * FMO : physical firq routing * BSU_IS : Barrier Shareability upgrade * FB : force broadcast when do some tlb ins * PTW : protect table walk * TSC : trap smc ins * TACR : Trap Auxiliary Control Registers * AMO : Physical SError interrupt routing. * RW : low level is 64bit, when 0 is 32 bit * VM : enable virtualzation */ value = read_sysreg64(HCR_EL2); context->hcr_el2 = value | HCR_EL2_VM | HCR_EL2_TIDCP | HCR_EL2_IMO | HCR_EL2_FMO | HCR_EL2_BSU_IS | HCR_EL2_FB | HCR_EL2_PTW | HCR_EL2_TSC | HCR_EL2_TACR | HCR_EL2_AMO; /* * usually there will be so many wfis from the VM * in some case this will have much infulence to * the system, add this flag to disable WFI trap. */ if (!(vcpu->vm->flags & VM_FLAGS_NATIVE_WFI)) context->hcr_el2 |= HCR_EL2_TWI; if (!task_is_32bit(vcpu->task)) context->hcr_el2 |= HCR_EL2_RW; /* * this require HVM's vcpu affinity need start with 0 */ if (vm_is_host_vm(vcpu->vm)) context->vmpidr = cpuid_to_affinity(get_vcpu_id(vcpu)); else context->vmpidr = get_vcpu_id(vcpu); pr_notice("vmpidr is 0x%x\n", context->vmpidr); context->cpacr = 0x3 << 20; if (vm_is_native(vcpu->vm)) context->vpidr = mpidr_el1[vcpu_affinity(vcpu)]; else context->vpidr = 0x410fc050; /* arm fvp */ /* * enable dc zva trap, the apple soc use zva size 64 * fixed, which may not equal to the target platform * so need trap dc zva */ if (vcpu->vm->os->type == OS_TYPE_XNU) context->hcr_el2 |= HCR_EL2_TDZ; context->vtcr_el2 = generate_vtcr_el2(); context->vttbr_el2 = generate_vttbr_el2(vm->vmid, vtop(vm->mm.pgdp)); context->ttbr0_el1 = 0; context->ttbr1_el1 = 0; context->mair_el1 = 0; context->tcr_el1 = 0; context->par_el1 = 0; context->amair_el1 = 0; } static void arch_vcpu_state_save(struct vcpu *vcpu, void *c) { struct vcpu_context *context = (struct vcpu_context *)c; context->vbar_el1 = read_sysreg(ARM64_VBAR_EL1); context->esr_el1 = read_sysreg(ARM64_ESR_EL1); context->elr_el1 = read_sysreg(ARM64_ELR_EL1); context->vmpidr = read_sysreg(ARM64_VMPIDR_EL2); context->vpidr = read_sysreg(ARM64_VPIDR_EL2); context->sctlr_el1 = read_sysreg(ARM64_SCTLR_EL1); context->hcr_el2 = read_sysreg(ARM64_HCR_EL2); context->sp_el1 = read_sysreg(ARM64_SP_EL1); context->sp_el0 = read_sysreg(ARM64_SP_EL0); context->spsr_el1 = read_sysreg(ARM64_SPSR_EL1); context->far_el1 = read_sysreg(ARM64_FAR_EL1); context->actlr_el1 = read_sysreg(ARM64_ACTLR_EL1); context->tpidr_el1 = read_sysreg(ARM64_TPIDR_EL1); context->csselr = read_sysreg(ARM64_CSSELR_EL1); context->cpacr = read_sysreg(ARM64_CPACR_EL1); context->contextidr = read_sysreg(ARM64_CONTEXTIDR_EL1); context->tpidr_el0 = read_sysreg(ARM64_TPIDR_EL0); context->tpidrro_el0 = read_sysreg(ARM64_TPIDRRO_EL0); context->cntkctl = read_sysreg(ARM64_CNTKCTL_EL1); context->afsr0 = read_sysreg(ARM64_AFSR0_EL1); context->afsr1 = read_sysreg(ARM64_AFSR1_EL1); if (task_is_32bit(vcpu->task)) { //context->teecr = read_sysreg32(TEECR32_EL1); //context->teehbr = read_sysreg32(TEEHBR32_EL1); context->dacr32_el2 = read_sysreg32(ARM64_DACR32_EL2); context->ifsr32_el2 = read_sysreg32(ARM64_IFSR32_EL2); } context->vtcr_el2 = read_sysreg(ARM64_VTCR_EL2); context->vttbr_el2 = read_sysreg(ARM64_VTTBR_EL2); context->ttbr0_el1 = read_sysreg(ARM64_TTBR0_EL1); context->ttbr1_el1 = read_sysreg(ARM64_TTBR1_EL1); context->mair_el1 = read_sysreg(ARM64_MAIR_EL1); context->tcr_el1 = read_sysreg(ARM64_TCR_EL1); context->par_el1 = read_sysreg(ARM64_PAR_EL1); context->amair_el1 = read_sysreg(ARM64_AMAIR_EL1); mb(); } static void arch_vcpu_state_restore(struct vcpu *vcpu, void *c) { struct vcpu_context *context = (struct vcpu_context *)c; write_sysreg(context->vbar_el1, VBAR_EL1); write_sysreg(context->esr_el1, ESR_EL1); write_sysreg(context->elr_el1, ELR_EL1); write_sysreg(context->vmpidr, VMPIDR_EL2); write_sysreg(context->vpidr, VPIDR_EL2); write_sysreg(context->sctlr_el1, SCTLR_EL1); write_sysreg(context->hcr_el2, HCR_EL2); write_sysreg(context->sp_el1, SP_EL1); write_sysreg(context->sp_el0, SP_EL0); write_sysreg(context->spsr_el1, SPSR_EL1); write_sysreg(context->far_el1, FAR_EL1); write_sysreg(context->actlr_el1, ACTLR_EL1); write_sysreg(context->tpidr_el1, TPIDR_EL1); write_sysreg(context->csselr, CSSELR_EL1); write_sysreg(context->cpacr, CPACR_EL1); write_sysreg(context->contextidr, CONTEXTIDR_EL1); write_sysreg(context->tpidr_el0, TPIDR_EL0); write_sysreg(context->tpidrro_el0, TPIDRRO_EL0); write_sysreg(context->cntkctl, CNTKCTL_EL1); write_sysreg(context->afsr0, AFSR0_EL1); write_sysreg(context->afsr1, AFSR1_EL1); if (task_is_32bit(vcpu->task)) { //write_sysreg(context->teecr, TEECR32_EL1); //write_sysreg(context->teehbr, TEEHBR32_EL1); write_sysreg(context->dacr32_el2, DACR32_EL2); write_sysreg(context->ifsr32_el2, IFSR32_EL2); } write_sysreg(context->vtcr_el2, ARM64_VTCR_EL2); write_sysreg(context->vttbr_el2, ARM64_VTTBR_EL2); write_sysreg(context->ttbr0_el1, ARM64_TTBR0_EL1); write_sysreg(context->ttbr1_el1, ARM64_TTBR1_EL1); write_sysreg(context->mair_el1, ARM64_MAIR_EL1); write_sysreg(context->amair_el1, ARM64_AMAIR_EL1); write_sysreg(context->tcr_el1, ARM64_TCR_EL1); write_sysreg(context->par_el1, ARM64_PAR_EL1); mb(); } static void arch_vcpu_state_dump(struct vcpu *vcpu, void *context) { struct vcpu_context *c = (struct vcpu_context *)context; pr_notice("----- dump vcpu context -----\n"); pr_notice(" vbar_el1: 0x%16lx esr_el1: 0x%16lx\n", c->vbar_el1, c->esr_el1); pr_notice(" sp_el1: 0x%16lx contextidr: 0x%16lx\n", c->sp_el1, c->contextidr); pr_notice(" sp_el0: 0x%16lx elr_el1: 0x%16lx\n", c->sp_el0, c->elr_el1); pr_notice(" vmpidr: 0x%16lx tpidr_el0: 0x%16lx\n", c->vmpidr, c->tpidr_el0); pr_notice(" vpidr: 0x%16lx sctlr_el1: 0x%16lx\n", c->vpidr, c->sctlr_el1); pr_notice(" hcr_el2: 0x%16lx tpidrro: 0x%16lx\n", c->hcr_el2, c->tpidrro_el0); pr_notice(" spsr_el1: 0x%16lx far_el1: 0x%16lx\n", c->spsr_el1, c->far_el1); pr_notice("actlr_el1: 0x%16lx cntkctl: 0x%16lx\n", c->actlr_el1, c->cntkctl); pr_notice("tpidr_el1: 0x%16lx csselr: 0x%16lx\n", c->tpidr_el1, c->csselr); pr_notice(" cpacr: 0x%16lx afsr0: 0x%16lx\n", c->cpacr, c->afsr0); pr_notice(" afsr1: 0x%16lx vtcr_el2: 0x%16lx\n", c->afsr1, c->vtcr_el2); pr_notice("ttbr0_el1: 0x%16lx ttbr1_el1: 0x%16lx\n", c->ttbr0_el1, c->ttbr1_el1); pr_notice("vttbr_el2: 0x%16lx mair_el1: 0x%16lx\n", c->vttbr_el2, c->mair_el1); pr_notice("amair_el1: 0x%16lx tcr_el1: 0x%16lx\n", c->mair_el1, c->tcr_el1); pr_notice(" par_el1: 0x%16lx\n", c->par_el1); } static int aarch64_vcpu_context_init(struct vmodule *vmodule) { vmodule->context_size = sizeof(struct vcpu_context); vmodule->state_init = arch_vcpu_state_init; vmodule->state_save = arch_vcpu_state_save; vmodule->state_restore = arch_vcpu_state_restore; vmodule->state_dump = arch_vcpu_state_dump; return 0; } MINOS_MODULE_DECLARE(arch_vcpu, "aarch64 vcpu context", (void *)aarch64_vcpu_context_init); static int arm_create_vm(void *item, void *context) { struct vm *vm = item; struct arm_virt_data *arch_data; arch_data = zalloc(sizeof(struct arm_virt_data)); if (!arch_data) panic("No more memory for arm arch data\n"); vm->arch_data = arch_data; return 0; } static int arm_virt_init(void) { return register_hook(arm_create_vm, OS_HOOK_CREATE_VM); } module_initcall(arm_virt_init); ================================================ FILE: kernel/arch/aarch64/virt/smc_service.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include static int std_smc_handler(gp_regs *c, uint32_t id, unsigned long *args) { int ret; pr_debug("psci function id 0x%x\n", id); switch (id) { case PSCI_0_2_FN_PSCI_VERSION: SVC_RET1(c, PSCI_VERSION(1, 0)); break; case PSCI_0_2_FN64_CPU_ON: case PSCI_0_2_FN_CPU_ON: pr_notice("request vcpu on\n"); ret = vcpu_power_on(get_current_vcpu(), args[0], args[1], args[2]); if (ret) SVC_RET1(c, PSCI_RET_INVALID_PARAMS); break; case PSCI_0_2_FN_CPU_OFF: /* virtual vcpu only support freeze mode */ pr_notice("request vcpu off\n"); ret = vcpu_off(get_current_vcpu()); if (ret) SVC_RET1(c, PSCI_RET_INTERNAL_FAILURE); break; case PSCI_0_2_FN64_CPU_SUSPEND: /* * only can be called by vcpu self */ ret = vcpu_suspend(get_current_vcpu(), c, (uint32_t)args[0], args[1]); if (ret) SVC_RET1(c, PSCI_RET_DENIED); break; case PSCI_0_2_FN_MIGRATE_INFO_TYPE: SVC_RET1(c, PSCI_0_2_TOS_MP); break; case PSCI_0_2_FN_AFFINITY_INFO: case PSCI_0_2_FN64_AFFINITY_INFO: /* all spi irq will send to vm0 do nothing */ SVC_RET1(c, PSCI_0_2_AFFINITY_LEVEL_OFF); break; case PSCI_0_2_FN_SYSTEM_OFF: /* request reset by it self */ vm_power_off(get_vmid(get_current_vcpu()), NULL, VM_PM_ACTION_BY_SELF); break; case PSCI_0_2_FN_SYSTEM_RESET: vm_reset(get_vmid(get_current_vcpu()), NULL, VM_PM_ACTION_BY_SELF); break; case PSCI_1_0_FN_SYSTEM_SUSPEND: case PSCI_1_0_FN64_SYSTEM_SUSPEND: /* only can be called by vcpu0 itself */ vm_suspend(get_vmid(get_current_vcpu())); break; case PSCI_1_0_FN_PSCI_FEATURES: switch (args[0]) { case ARM_SMCCC_VERSION_FUNC_ID: case PSCI_0_2_FN64_CPU_SUSPEND: case PSCI_1_0_FN_SYSTEM_SUSPEND: case PSCI_1_0_FN64_SYSTEM_SUSPEND: SVC_RET1(c, PSCI_RET_SUCCESS); break; default: SVC_RET1(c, PSCI_RET_NOT_SUPPORTED); break; } break; default: break; } SVC_RET1(c, PSCI_RET_SUCCESS); } DEFINE_SMC_HANDLER("std_smc_desc", SVC_STYPE_STDSMC, SVC_STYPE_STDSMC, std_smc_handler); ================================================ FILE: kernel/arch/aarch64/virt/stage2.c ================================================ /* * Copyright (C) 2020 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include "stage2.h" void *arch_alloc_guest_pgd(void) { void *page; page = __get_free_pages(2, 2); if (page) memset(page, 0, PAGE_SIZE * 2); return page; } static void inline flush_tlb_va_range(unsigned long va, size_t size) { flush_tlb_ipa_guest(va, size); } static void inline stage2_pgd_clear(pud_t *pgdp) { WRITE_ONCE(*pgdp, 0); __dsb(ishst); isb(); } static void inline stage2_pud_clear(pud_t *pudp) { WRITE_ONCE(*pudp, 0); __dsb(ishst); isb(); } static void inline stage2_pmd_clear(pmd_t *pmdp) { WRITE_ONCE(*pmdp, 0); __dsb(ishst); isb(); } static void *stage2_get_free_page(unsigned long flags) { return get_free_page(); } static unsigned long stage2_xxx_addr_end(unsigned long start, unsigned long end, size_t map_size) { unsigned long boundary = (start + map_size) & ~((unsigned long)map_size - 1); return ((boundary - 1) < (end - 1)) ? boundary : end; } #define stage2_pgd_addr_end(start, end) \ stage2_xxx_addr_end(start, end, S2_PGD_SIZE) #define stage2_pud_addr_end(start, end) \ stage2_xxx_addr_end(start, end, S2_PUD_SIZE) #define stage2_pmd_addr_end(start, end) \ stage2_xxx_addr_end(start, end, S2_PMD_SIZE) static inline void stage2_set_pte(pte_t *ptep, pte_t new_pte) { WRITE_ONCE(*ptep, new_pte); __dsb(ishst); } static inline void stage2_set_pmd(pmd_t *pmdp, pmd_t new_pmd) { WRITE_ONCE(*pmdp, new_pmd); __dsb(ishst); } static inline void stage2_set_pud(pud_t *pudp, pud_t new_pud) { WRITE_ONCE(*pudp, new_pud); __dsb(ishst); } static inline void stage2_set_pgd(pgd_t *pgdp, pgd_t new_pgd) { WRITE_ONCE(*pgdp, new_pgd); __dsb(ishst); } static inline void stage2_pgd_populate(pgd_t *pgdp, unsigned long addr, unsigned long flags) { uint64_t attrs = S2_DES_TABLE; stage2_set_pgd(pgdp, vtop(addr) | attrs); } static inline void stage2_pud_populate(pud_t *pudp, unsigned long addr, unsigned long flags) { uint64_t attrs = S2_DES_TABLE; stage2_set_pud(pudp, vtop(addr) | attrs); } static inline void stage2_pmd_populate(pmd_t *pmdp, unsigned long addr, unsigned long flags) { uint64_t attrs = S2_DES_TABLE; stage2_set_pmd(pmdp, vtop(addr) | attrs); } static inline uint64_t stage2_block_attr(unsigned long flags) { uint64_t attr = 0; switch (flags & VM_TYPE_MASK) { case __VM_NORMAL_NC: attr |= S2_BLOCK_NORMAL_NC; break; case __VM_IO: attr |= S2_BLOCK_DEVICE; break; case __VM_WT: attr |= S2_BLOCK_WT; break; default: attr |= S2_BLOCK_NORMAL; break; } switch (flags & VM_RW_MASK) { case __VM_RO: attr |= S2_AP_RO; break; case __VM_RW: attr |= S2_AP_RW; break; case __VM_WO: attr |= S2_AP_WO; break; default: attr |= S2_AP_NON; break; } if (!(flags & __VM_EXEC)) attr |= S2_XN; if (flags & __VM_PFNMAP) attr |= S2_PFNMAP; if (flags & __VM_DEVMAP) attr |= S2_DEVMAP; if (flags & __VM_SHARED) attr |= S2_SHARED; return attr; } static inline uint64_t stage2_page_attr(unsigned long flags) { uint64_t pte = 0; switch (flags & VM_TYPE_MASK) { case __VM_NORMAL_NC: pte |= S2_PAGE_NORMAL_NC; break; case __VM_IO: pte |= S2_PAGE_DEVICE; break; case __VM_WT: pte |= S2_PAGE_WT; break; default: pte |= S2_PAGE_NORMAL; break; } switch (flags & VM_RW_MASK) { case __VM_RO: pte |= S2_AP_RO; break; case __VM_RW: pte |= S2_AP_RW; break; case __VM_WO: pte |= S2_AP_WO; break; default: pte |= S2_AP_NON; break; } if (!(flags & __VM_EXEC)) pte |= S2_XN; if (flags & __VM_PFNMAP) pte |= S2_PFNMAP; if (flags & __VM_DEVMAP) pte |= S2_DEVMAP; if (flags & __VM_SHARED) pte |= S2_SHARED; return pte; } static void stage2_unmap_pte_range(struct mm_struct *vs, pte_t *ptep, unsigned long addr, unsigned long end) { pte_t *pte; pte = stage2_pte_offset(ptep, addr); do { if (!stage2_pte_none(*pte)) stage2_set_pte(pte, 0); } while (pte++, addr += PAGE_SIZE, addr != end); } static inline bool is_pmd_range(unsigned long start, unsigned long end) { if (((start & (S2_PMD_SIZE - 1)) == 0) && ((end - start) == S2_PMD_SIZE)) return true; return false; } static inline bool is_pud_range(unsigned long start, unsigned long end) { if (((start & (S2_PUD_SIZE - 1)) == 0) && ((end - start) == S2_PUD_SIZE)) return true; return false; } static void stage2_unmap_pmd_range(struct mm_struct *vs, pmd_t *pmdp, unsigned long addr, unsigned long end) { unsigned long next; pmd_t *pmd; pte_t *ptep; pmd = stage2_pmd_offset(pmdp, addr); do { next = stage2_pmd_addr_end(addr, end); if (!stage2_pmd_none(*pmd)) { if (stage2_pmd_huge(*pmd)) { stage2_pmd_clear(pmd); } else { ptep = (pte_t *)ptov(stage2_pte_table_addr(*pmd)); stage2_unmap_pte_range(vs, ptep, addr, next); if (is_pmd_range(addr, next)) { stage2_pmd_clear(pmd); free_pages(ptep); } } } } while (pmd++, addr = next, addr != end); } static int stage2_unmap_pud_range(struct mm_struct *vs, unsigned long addr, unsigned long end) { unsigned long next; pud_t *pud; pmd_t *pmdp; pud = stage2_pud_offset((pud_t *)vs->pgdp, end); do { next = stage2_pud_addr_end(addr, end); if (!stage2_pud_none(*pud)) { pmdp = (pmd_t *)ptov(stage2_pmd_table_addr(*pud)); stage2_unmap_pmd_range(vs, pmdp, addr, next); if (is_pud_range(addr, next)) { stage2_pud_clear(pud); free_pages(pmdp); } } } while (pud++, addr = next, addr != end); flush_all_tlb_mm(vs); return 0; } static int stage2_map_pte_range(struct mm_struct *vs, pte_t *ptep, unsigned long start, unsigned long end, unsigned long physical, unsigned long flags) { unsigned long pte_attr; pte_t *pte; pte_t old_pte; pte = stage2_pte_offset(ptep, start); pte_attr = stage2_page_attr(flags); do { old_pte = *pte; stage2_set_pte(pte, pte_attr | physical); if (old_pte) { pr_warn("pte range 0x%lx remaped old 0x%lx new 0x%lx\n", start, old_pte, pte_attr | physical); } } while (pte++, start += PAGE_SIZE, physical += PAGE_SIZE, start != end); return 0; } static inline bool stage2_pmd_huge_page(pmd_t old_pmd, unsigned long start, unsigned long phy, size_t size, unsigned long flags) { if (!(flags & (__VM_HUGE_2M | __VM_HUGE_1G)) || old_pmd) return false; if (!IS_BLOCK_ALIGN(start) || !IS_BLOCK_ALIGN(phy) || !(IS_BLOCK_ALIGN(size))) return false; return true; } static int stage2_map_pmd_range(struct mm_struct *vs, pmd_t *pmdp, unsigned long start, unsigned long end, unsigned long physical, unsigned long flags) { unsigned long next; unsigned long attr; pmd_t *pmd; pmd_t old_pmd; pte_t *ptep; size_t size; int ret; pmd = stage2_pmd_offset(pmdp, start); do { next = stage2_pmd_addr_end(start, end); size = next - start; old_pmd = *pmd; /* * virtual memory need to map as PMD huge page */ if (stage2_pmd_huge_page(old_pmd, start, physical, size, flags)) { attr = stage2_block_attr(flags); stage2_set_pmd(pmd, attr | (physical & S2_PMD_MASK)); } else { if (old_pmd && stage2_pmd_huge(old_pmd)) { pr_err("stage2: vaddr 0x%x has mapped as huge page\n", start); return -EINVAL; } if (stage2_pmd_none(old_pmd)) { ptep = (pte_t *)stage2_get_free_page(flags); if (!ptep) return -ENOMEM; memset(ptep, 0, PAGE_SIZE); stage2_pmd_populate(pmd, (unsigned long)ptep, flags); } else { ptep = (pte_t *)ptov(stage2_pte_table_addr(old_pmd)); } ret = stage2_map_pte_range(vs, ptep, start, next, physical, flags); if (ret) return ret; } } while (pmd++, physical += size, start = next, start != end); return 0; } static inline int stage2_pud_huge_page(pud_t old_pud, unsigned long start, unsigned long phy, size_t size, unsigned long flags) { if (!(flags & __VM_HUGE_1G) || old_pud) return false; if (!IS_PUD_ALIGN(start) || !IS_PUD_ALIGN(phy) || !(IS_PUD_ALIGN(size))) return false; return true; } static int stage2_map_pud_range(struct mm_struct *vs, unsigned long start, unsigned long end, unsigned long physical, unsigned long flags) { unsigned long next, attr; pud_t *pud; pmd_t *pmdp; size_t size; pud_t old_pud; int ret; pud = stage2_pud_offset((pud_t *)vs->pgdp, start); do { next = stage2_pud_addr_end(start, end); size = next - start; old_pud = *pud; if (stage2_pud_huge_page(old_pud, start, physical, size, flags)) { attr = stage2_block_attr(flags); stage2_set_pud(pud, attr | (physical & S2_PUD_MASK)); } else { if (old_pud && stage2_pud_huge(old_pud)) { pr_err("stage2: vaddr 0x%x mapped as PUD huge page\n", start); return -EINVAL; } if (stage2_pud_none(*pud)) { pmdp = (pmd_t *)stage2_get_free_page(flags); if (!pmdp) return -ENOMEM; memset(pmdp, 0, PAGE_SIZE); stage2_pud_populate(pud, (unsigned long)pmdp, flags); } else { pmdp = (pmd_t *)ptov(stage2_pmd_table_addr(*pud)); } ret = stage2_map_pmd_range(vs, pmdp, start, next, physical, flags); if (ret) return ret; } } while (pud++, physical += size, start = next, start != end); return 0; } static inline int stage2_ipa_to_pa(struct mm_struct *vs, unsigned long va, phy_addr_t *pa) { unsigned long pte_offset = va & ~S2_PTE_MASK; unsigned long pmd_offset = va & ~S2_PMD_MASK; unsigned long phy = 0; pud_t *pudp; pmd_t *pmdp; pte_t *ptep; pudp = stage2_pud_offset(vs->pgdp, va); if (stage2_pud_none(*pudp)) return -EFAULT; pmdp = stage2_pmd_offset(ptov(stage2_pmd_table_addr(*pudp)), va); if (stage2_pmd_none(*pmdp)) return -EFAULT; if (stage2_pmd_huge(*pmdp)) { *pa = ((*pmdp) & S2_PHYSICAL_MASK) + pmd_offset; return 0; } ptep = stage2_pte_offset(ptov(stage2_pte_table_addr(*pmdp)), va); phy = *ptep & S2_PHYSICAL_MASK; if (phy == 0) return -EFAULT; *pa = phy + pte_offset; return 0; } int arch_translate_guest_ipa(struct mm_struct *vs, unsigned long va, phy_addr_t *pa) { return stage2_ipa_to_pa(vs, va, pa); } int arch_guest_map(struct mm_struct *vs, unsigned long start, unsigned long end, unsigned long physical, unsigned long flags) { if (end == start) return -EINVAL; ASSERT((start < VMM_VIRT_MAX) && (end <= VMM_VIRT_MAX)); ASSERT(physical < S2_PHYSICAL_MAX); ASSERT(IS_PAGE_ALIGN(start) && IS_PAGE_ALIGN(end) && IS_PAGE_ALIGN(physical)); return stage2_map_pud_range(vs, start, end, physical, flags); } int arch_guest_unmap(struct mm_struct *vs, unsigned long start, unsigned long end) { if (end == start) return -EINVAL; ASSERT((start < VMM_VIRT_MAX) && (end <= VMM_VIRT_MAX)); return stage2_unmap_pud_range(vs, start, end); } ================================================ FILE: kernel/arch/aarch64/virt/stage2.h ================================================ #ifndef __MINOS_ARM64_STAGE2_H__ #define __MINOS_ARM64_STAGE2_H__ #include /* * Stage 2 VMSAv8-64 Table Descriptors */ #define S2_DES_FAULT (0b00 << 0) #define S2_DES_BLOCK (0b01 << 0) #define S2_DES_TABLE (0b11 << 0) #define S2_DES_PAGE (0b11 << 0) /* * Stage 2 VMSAv8-64 Page / Block Descriptors */ #define S2_CONTIGUOUS (1UL << 52) #define S2_XN (1UL << 54) #define S2_AF (1UL << 10) #define S2_PFNMAP (1UL << 55) // bit 55 - 58 is for software use #define S2_DEVMAP (1UL << 56) // bit 55 - 58 is for software use #define S2_SHARED (1UL << 57) // bit 55 - 58 is for software use #define S2_SH_NON (0b00 << 8) #define S2_SH_OUTER (0b10 << 8) #define S2_SH_INNER (0b11 << 8) #define S2_AP_NON (0b00 << 6) #define S2_AP_RO (0b01 << 6) #define S2_AP_WO (0b10 << 6) #define S2_AP_RW (0b11 << 6) #define S2_MEMATTR_DEV_nGnRnE (0b0000 << 2) #define S2_MEMATTR_DEV_nGnRE (0b0001 << 2) #define S2_MEMATTR_DEV_nGRE (0b0010 << 2) #define S2_MEMATTR_DEV_GRE (0b0011 << 2) #define S2_MEMATTR_NORMAL_WB (0b1111 << 2) #define S2_MEMATTR_NORMAL_NC (0b0101 << 2) #define S2_MEMATTR_NORMAL_WT (0b1010 << 2) #define S2_MT_NORMAL 0 #define S2_MT_DEVICE 1 #define S2_CACHE_WB 0 #define S2_CACHE_NC 1 #define S2_PAGE_NORMAL (S2_DES_PAGE | S2_AF | S2_SH_INNER | S2_MEMATTR_NORMAL_WB) #define S2_PAGE_NORMAL_NC (S2_DES_PAGE | S2_AF | S2_SH_INNER | S2_MEMATTR_NORMAL_NC) #define S2_PAGE_WT (S2_DES_PAGE | S2_AF | S2_SH_INNER | S2_MEMATTR_NORMAL_WT) // #define S2_PAGE_DEVICE (S2_DES_PAGE | S2_AF | S2_SH_OUTER | S2_MEMATTR_DEV_nGnRnE | S2_XN) #define S2_PAGE_DEVICE (S2_DES_PAGE | S2_AF | S2_SH_OUTER | S2_MEMATTR_DEV_nGnRE | S2_XN) #define S2_BLOCK_NORMAL (S2_DES_BLOCK | S2_AF | S2_SH_INNER | S2_MEMATTR_NORMAL_WB) #define S2_BLOCK_NORMAL_NC (S2_DES_BLOCK | S2_AF | S2_SH_INNER | S2_MEMATTR_NORMAL_NC) #define S2_BLOCK_DEVICE (S2_DES_BLOCK | S2_AF | S2_SH_OUTER | S2_MEMATTR_DEV_nGnRE | S2_XN) // #define S2_BLOCK_DEVICE (S2_DES_BLOCK | S2_AF | S2_SH_OUTER | S2_MEMATTR_DEV_nGnRnE | S2_XN) #define S2_BLOCK_WT (S2_DES_BLOCK | S2_AF | S2_SH_OUTER | S2_MEMATTR_NORMAL_WT) /* * 40bit stage2 IPA use 3 levels page table, the pagetable * need 2 pages */ #define STAGE2_PGTABLE_LEVELS 3 #define S2_PAGETABLE_SIZE 8192 #define S2_PHYSICAL_MASK 0x0000fffffffff000UL #define S2_PHYSICAL_MAX (1UL << 40) #define S2_VIRT_MAX (1UL << 40) #define S2_PHYSICAL_SIZE (1UL << 40) #define GUEST_PGD_PAGES 2 #define GUEST_PGD_PAGE_ALIGN 2 #define S2_PGD_SHIFT 39 #define S2_PGD_SIZE (1UL << S2_PGD_SHIFT) #define S2_PGD_MASK (~(S2_PGD_SIZE - 1)) #define S2_PUD_SHIFT 30 #define S2_PUD_SIZE (1UL << S2_PUD_SHIFT) #define S2_PUD_MASK (~(S2_PUD_SIZE - 1)) #define S2_PMD_SHIFT 21 #define S2_PMD_SIZE (1UL << S2_PMD_SHIFT) #define S2_PMD_MASK (~(S2_PMD_SIZE - 1)) #define S2_PTE_SHIFT 12 #define S2_PTE_SIZE (1UL << S2_PTE_SHIFT) #define S2_PTE_MASK (~(S2_PTE_SIZE - 1)) /* * The number of PTRS across all concatenated stage2 tables given by the * number of bits resolved at the initial level. * * since we use 3 level pagetable, and translation walk will from pud, the * PTRS in one pud is 1024 */ #define PTRS_PER_S2_PGD 512 #define PTRS_PER_S2_PUD 1024 #define PTRS_PER_S2_PMD 512 #define PTRS_PER_S2_PTE 512 #define stage2_pud_value(pudp) (*(pudp)) #define stage2_pmd_value(pmdp) (*(pmdp)) #define stage2_pte_value(ptep) (*(ptep)) #define IS_PUD_ALIGN(addr)\ (!((unsigned long)(addr) & (S2_PUD_SIZE - 1))) #define stage2_pgd_index(addr) (((addr) >> S2_PGD_SHIFT) & (PTRS_PER_S2_PGD - 1)) #define stage2_pud_index(addr) (((addr) >> S2_PUD_SHIFT) & (PTRS_PER_S2_PUD - 1)) #define stage2_pmd_index(addr) (((addr) >> S2_PMD_SHIFT) & (PTRS_PER_S2_PMD - 1)) #define stage2_pte_index(addr) (((addr) >> S2_PTE_SHIFT) & (PTRS_PER_S2_PTE - 1)) #define stage2_pgd_offset(pgdp, addr) ((pgd_t *)(pgdp) + stage2_pgd_index((unsigned long)addr)) #define stage2_pud_offset(pudp, addr) ((pud_t *)(pudp) + stage2_pud_index((unsigned long)addr)) #define stage2_pmd_offset(pmdp, addr) ((pmd_t *)(pmdp) + stage2_pmd_index((unsigned long)addr)) #define stage2_pte_offset(ptep, addr) ((pte_t *)(ptep) + stage2_pte_index((unsigned long)addr)) #define stage2_pud_huge(pud) ((pud) && ((pud) & 0x03) == S2_DES_BLOCK) #define stage2_pmd_huge(pmd) ((pmd) && ((pmd) & 0x03) == S2_DES_BLOCK) #define stage2_pud_table_addr(pgd) (pud_t *)(unsigned long)((pgd) & S2_PHYSICAL_MASK) #define stage2_pmd_table_addr(pud) (pmd_t *)(unsigned long)((pud) & S2_PHYSICAL_MASK) #define stage2_pte_table_addr(pmd) (pte_t *)(unsigned long)((pmd) & S2_PHYSICAL_MASK) #define stage2_pgd_none(pgd) ((pgd) == 0) #define stage2_pud_none(pud) ((pud) == 0) #define stage2_pmd_none(pmd) ((pmd) == 0) #define stage2_pte_none(pte) ((pte) == 0) #define stage2_pmd_pfn(pmd) (pmd >> S2_PMD_SHIFT) #define stage2_pfn_pte(pfn, attr) (((unsigned long)(pfn) << PAGE_SHIFT) | attr) #define S2_PTE_ADDR_MASK (0x0000fffffffff000UL) #define S2_PMD_ADDR_MASK (0x0000ffffffe00000UL) #endif ================================================ FILE: kernel/arch/aarch64/virt/svc_service.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include extern unsigned char __hvc_handler_start; extern unsigned char __hvc_handler_end; extern unsigned char __smc_handler_start; extern unsigned char __smc_handler_end; static struct svc_desc *smc_descs[SVC_STYPE_MAX]; static struct svc_desc *hvc_descs[SVC_STYPE_MAX]; int do_svc_handler(gp_regs *regs, uint32_t svc_id, uint64_t *args, int smc) { uint16_t type; struct svc_desc **table; struct svc_desc *desc; if (smc) table = smc_descs; else table = hvc_descs; type = (svc_id & SVC_STYPE_MASK) >> 24; if (unlikely(type > SVC_STYPE_MAX)) { pr_err("Unsupported SVC type %d\n", type); goto invalid; } desc = table[type]; if (unlikely(!desc)) goto invalid; pr_debug("doing SVC Call %s:0x%x\n", desc->name, svc_id); return desc->handler(regs, svc_id, args); invalid: SVC_RET1(regs, -EINVAL); } static void parse_svc_desc(unsigned long start, unsigned long end, struct svc_desc **table) { struct svc_desc *desc; int32_t j; section_for_each_item_addr(start, end, desc) { BUG_ON((desc->type_start > desc->type_end) || (desc->type_end >= SVC_STYPE_MAX)); for (j = desc->type_start; j <= desc->type_end; j++) { if (table[j]) pr_warn("overwrite SVC_DESC:%d %s\n", j, desc->name); table[j] = desc; } } } static int __init_text svc_service_init(void) { pr_notice("parsing SMC/HVC handler\n"); parse_svc_desc((unsigned long)&__hvc_handler_start, (unsigned long)&__hvc_handler_end, hvc_descs); parse_svc_desc((unsigned long)&__smc_handler_start, (unsigned long)&__smc_handler_end, smc_descs); return 0; } arch_initcall(svc_service_init); ================================================ FILE: kernel/arch/aarch64/virt/trap.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include static inline int taken_from_el1(uint64_t spsr) { return ((spsr & 0xf) != AARCH64_SPSR_EL0t); } static inline void inject_virtual_data_abort(uint32_t esr_value) { uint64_t hcr_el2 = read_sysreg(HCR_EL2) | HCR_EL2_VSE; write_sysreg(hcr_el2, HCR_EL2); write_sysreg(esr_value, ESR_EL1); wmb(); } static int unknown_handler(gp_regs *reg, int ec, uint32_t esr_value) { return 0; } static int wfi_wfe_handler(gp_regs *reg, int ec, uint32_t esr_value) { vcpu_idle(get_current_vcpu()); return 0; } static int mcr_mrc_cp15_handler(gp_regs *reg, int ec, uint32_t esr_value) { switch (esr_value & HSR_CP32_REGS_MASK) { case HSR_CPREG32(ACTLR): break; default: pr_notice("mcr_mrc_cp15_handler 0x%x\n", esr_value); break; } return 0; } static int mcrr_mrrc_cp15_handler(gp_regs *reg, int ec, uint32_t esr_value) { struct esr_cp64 *sysreg = (struct esr_cp64 *)&esr_value; unsigned long reg_value0, reg_value1; unsigned long reg_value; struct vcpu *vcpu = get_current_vcpu(); struct arm_virt_data *arm_data = vcpu->vm->arch_data; switch (esr_value & HSR_CP64_REGS_MASK) { case HSR_CPREG64(CNTP_CVAL): break; /* for aarch32 vm and using gicv3 */ case HSR_CPREG64(ICC_SGI1R): case HSR_CPREG64(ICC_ASGI1R): case HSR_CPREG64(ICC_SGI0R): if (!sysreg->read && arm_data->sgi1r_el1_trap) { reg_value0 = get_reg_value(reg, sysreg->reg1); reg_value1 = get_reg_value(reg, sysreg->reg2); reg_value = (reg_value1 << 32) | reg_value0; arm_data->sgi1r_el1_trap(vcpu, reg_value); } break; } return 0; } static int mcr_mrc_cp14_handler(gp_regs *reg, int ec, uint32_t esr_value) { return 0; } static int ldc_stc_cp14_handler(gp_regs *reg, int ec, uint32_t esr_value) { return 0; } static int access_simd_reg_handler(gp_regs *reg, int ec, uint32_t esr_value) { return 0; } static int mcr_mrc_cp10_handler(gp_regs *reg, int ec, uint32_t esr_value) { return 0; } static int mrrc_cp14_handler(gp_regs *reg, int ec, uint32_t esr_value) { return 0; } static int illegal_exe_state_handler(gp_regs *reg, int ec, uint32_t esr_value) { return 0; } static int __arm_svc_handler(gp_regs *reg, int smc) { uint32_t id; unsigned long args[6]; id = reg->x0; args[0] = reg->x1; args[1] = reg->x2; args[2] = reg->x3; args[3] = reg->x4; args[4] = reg->x5; args[5] = reg->x6; if (!(id & SVC_CTYPE_MASK)) local_irq_enable(); return do_svc_handler(reg, id, args, smc); } static int access_system_reg_handler(gp_regs *reg, int ec, uint32_t esr_value) { struct esr_sysreg *sysreg = (struct esr_sysreg *)&esr_value; struct vcpu *vcpu = get_current_vcpu(); struct arm_virt_data *arm_data = vcpu->vm->arch_data; uint32_t regindex = sysreg->reg; unsigned long reg_value = 0; int ret = 0, reg_name; reg_name = esr_value & ESR_SYSREG_REGS_MASK; reg_value = sysreg->read ? 0 : get_reg_value(reg, regindex); switch (reg_name) { case ESR_SYSREG_ICC_SGI1R_EL1: case ESR_SYSREG_ICC_ASGI1R_EL1: case ESR_SYSREG_ICC_SGI0R_EL1: pr_debug("access system reg SGI1R_EL1\n"); if (!sysreg->read && (arm_data->sgi1r_el1_trap)) arm_data->sgi1r_el1_trap(vcpu, reg_value); break; case ESR_SYSREG_CNTPCT_EL0: case ESR_SYSREG_CNTP_TVAL_EL0: case ESR_SYSREG_CNTP_CTL_EL0: case ESR_SYSREG_CNTP_CVAL_EL0: if (arm_data->phy_timer_trap) { ret = arm_data->phy_timer_trap(vcpu, reg_name, sysreg->read, ®_value); } break; case ESR_SYSREG_DCZVA: if ((arm_data->dczva_trap) && !sysreg->read) ret = arm_data->dczva_trap(vcpu, reg_value); break; default: pr_debug("unsupport register access 0x%x %s\n", reg_name, sysreg->read ? "read" : "write"); if (arm_data->sysreg_emulation) { ret = arm_data->sysreg_emulation(vcpu, reg_name, sysreg->read, ®_value); } break; } if (sysreg->read) set_reg_value(reg, regindex, reg_value); return ret; } static int insabort_tfl_handler(gp_regs *reg, int ec, uint32_t esr_value) { panic("%s\n", __func__); return 0; } static int misaligned_pc_handler(gp_regs *reg, int ec, uint32_t esr_value) { panic("%s\n", __func__); return 0; } static inline bool dabt_isextabt(uint32_t dfsc) { switch (dfsc) { case FSC_SEA: case FSC_SEA_TTW0: case FSC_SEA_TTW1: case FSC_SEA_TTW2: case FSC_SEA_TTW3: case FSC_SECC: case FSC_SECC_TTW0: case FSC_SECC_TTW1: case FSC_SECC_TTW2: case FSC_SECC_TTW3: return true; default: return false; } } static inline unsigned long get_faulting_ipa(unsigned long vaddr) { uint64_t hpfar = read_sysreg(HPFAR_EL2); unsigned long ipa; ipa = (hpfar & HPFAR_MASK) << (12 - 4); ipa |= vaddr & (~(~PAGE_MASK)); return ipa; } static inline bool dabt_iswrite(uint32_t esr_value) { return (!!(esr_value & ESR_ELx_WNR)) || (!!(esr_value & ESR_ELx_S1PTW)); } static int dataabort_tfl_handler(gp_regs *regs, int ec, uint32_t esr_value) { uint32_t dfsc = esr_value & ESR_ELx_FSC_TYPE; unsigned long vaddr, ipa, value; int ret, iswrite, reg; if (dabt_isextabt(dfsc)) { pr_err("data abort is external abort\n"); goto out_fail; } if ((dfsc != FSC_FAULT) && (dfsc != FSC_PERM)) { pr_err("Unsupported data abort FSC: EC=%x xFSC=%x ESR_EL2=%x\n", ec, dfsc, esr_value); goto out_fail; } if (!(esr_value & ESR_ELx_ISV)) { pr_err("Instruction syndrome not valid\n"); goto out_fail; } iswrite = dabt_iswrite(esr_value); reg = ESR_ELx_SRT(esr_value); value = iswrite ? get_reg_value(regs, reg) : 0; vaddr = read_sysreg(FAR_EL2); if ((esr_value &ESR_ELx_S1PTW) || (dfsc == FSC_FAULT)) ipa = get_faulting_ipa(vaddr); else ipa = guest_va_to_ipa(vaddr, 1); ret = vdev_mmio_emulation(regs, iswrite, ipa, &value); if (ret == -EACCES) { pr_warn("Fault on mmio read/write fail 0x%x vmid:%d\n", ipa, get_vmid(get_current_vcpu())); /* * if failed to handle the mmio trap inject a * sync error to guest vm to generate a fault */ goto out_fail; } if (!iswrite) set_reg_value(regs, reg, value); return 0; out_fail: vcpu_fault(current_vcpu, regs); /* * the VCPU will exit when return to guest. */ return -EFAULT; } static int stack_misalign_handler(gp_regs *reg, int ec, uint32_t esr_value) { panic("%s\n", __func__); return 0; } static int floating_aarch32_handler(gp_regs *reg, int ec, uint32_t esr_value) { panic("%s\n", __func__); return 0; } static int floating_aarch64_handler(gp_regs *reg, int ec, uint32_t esr_value) { panic("%s\n", __func__); return 0; } static int serror_handler(gp_regs *reg, int ec, uint32_t esr_value) { panic("%s\n", __func__); return 0; } int aarch64_hypercall_handler(gp_regs *reg, int ec, uint32_t esr_value) { struct vcpu *vcpu = get_current_vcpu(); struct arm_virt_data *arm_data = vcpu->vm->arch_data; if (arm_data->hvc_handler) return arm_data->hvc_handler(vcpu, reg, read_esr_el2()); else return __arm_svc_handler(reg, 0); } int aarch64_smccall_handler(gp_regs *reg, int ec, uint32_t esr_value) { struct vcpu *vcpu = get_current_vcpu(); struct arm_virt_data *arm_data = vcpu->vm->arch_data; if (arm_data->hvc_handler) return arm_data->smc_handler(vcpu, reg, read_esr_el2()); else return __arm_svc_handler(reg, 1); } /* type defination is at armv8-spec 1906 */ DEFINE_SYNC_DESC(guest_ESR_ELx_EC_WFx, EC_TYPE_BOTH, wfi_wfe_handler, 1, 4); DEFINE_SYNC_DESC(guest_ESR_ELx_EC_UNKNOWN, EC_TYPE_BOTH, unknown_handler, 1, 4); DEFINE_SYNC_DESC(guest_ESR_ELx_EC_CP15_32, EC_TYPE_BOTH, mcr_mrc_cp15_handler, 1, 4); DEFINE_SYNC_DESC(guest_ESR_ELx_EC_CP15_64, EC_TYPE_AARCH32, mcrr_mrrc_cp15_handler, 1, 4); DEFINE_SYNC_DESC(guest_ESR_ELx_EC_CP14_MR, EC_TYPE_AARCH32, mcr_mrc_cp14_handler, 1, 4); DEFINE_SYNC_DESC(guest_ESR_ELx_EC_CP14_LS, EC_TYPE_AARCH32, ldc_stc_cp14_handler, 1, 4); DEFINE_SYNC_DESC(guest_ESR_ELx_EC_FP_ASIMD, EC_TYPE_BOTH, access_simd_reg_handler, 1, 4); DEFINE_SYNC_DESC(guest_ESR_ELx_EC_CP10_ID, EC_TYPE_AARCH32, mcr_mrc_cp10_handler, 1, 4); DEFINE_SYNC_DESC(guest_ESR_ELx_EC_CP14_64, EC_TYPE_AARCH32, mrrc_cp14_handler, 1, 4); DEFINE_SYNC_DESC(guest_ESR_ELx_EC_ILL, EC_TYPE_BOTH, illegal_exe_state_handler, 1, 4); DEFINE_SYNC_DESC(guest_ESR_ELx_EC_SYS64, EC_TYPE_AARCH64, access_system_reg_handler, 1, 4); DEFINE_SYNC_DESC(guest_ESR_ELx_EC_IABT_LOW, EC_TYPE_BOTH, insabort_tfl_handler, 1, 4); DEFINE_SYNC_DESC(guest_ESR_ELx_EC_PC_ALIGN, EC_TYPE_BOTH, misaligned_pc_handler, 1, 4); DEFINE_SYNC_DESC(guest_ESR_ELx_EC_DABT_LOW, EC_TYPE_BOTH, dataabort_tfl_handler, 1, 4); DEFINE_SYNC_DESC(guest_ESR_ELx_EC_SP_ALIGN, EC_TYPE_BOTH, stack_misalign_handler, 1, 4); DEFINE_SYNC_DESC(guest_ESR_ELx_EC_FP_EXC32, EC_TYPE_AARCH32, floating_aarch32_handler, 1, 4); DEFINE_SYNC_DESC(guest_ESR_ELx_EC_FP_EXC64, EC_TYPE_AARCH64, floating_aarch64_handler, 1, 4); DEFINE_SYNC_DESC(guest_ESR_ELx_EC_SERROR, EC_TYPE_BOTH, serror_handler, 1, 4); DEFINE_SYNC_DESC(guest_ESR_ELx_EC_HVC32, EC_TYPE_AARCH32, aarch64_hypercall_handler, 1, 0); DEFINE_SYNC_DESC(guest_ESR_ELx_EC_HVC64, EC_TYPE_AARCH64, aarch64_hypercall_handler, 1, 0); DEFINE_SYNC_DESC(guest_ESR_ELx_EC_SMC32, EC_TYPE_AARCH32, aarch64_smccall_handler, 1, 4); DEFINE_SYNC_DESC(guest_ESR_ELx_EC_SMC64, EC_TYPE_AARCH64, aarch64_smccall_handler, 1, 4); static struct sync_desc *guest_sync_descs[] = { [0 ... ESR_ELx_EC_MAX] = &sync_desc_guest_ESR_ELx_EC_UNKNOWN, [ESR_ELx_EC_WFx] = &sync_desc_guest_ESR_ELx_EC_WFx, [ESR_ELx_EC_CP15_32] = &sync_desc_guest_ESR_ELx_EC_CP15_32, [ESR_ELx_EC_CP15_64] = &sync_desc_guest_ESR_ELx_EC_CP15_64, [ESR_ELx_EC_CP14_MR] = &sync_desc_guest_ESR_ELx_EC_CP14_MR, [ESR_ELx_EC_CP14_LS] = &sync_desc_guest_ESR_ELx_EC_CP14_LS, [ESR_ELx_EC_FP_ASIMD] = &sync_desc_guest_ESR_ELx_EC_FP_ASIMD, [ESR_ELx_EC_CP10_ID] = &sync_desc_guest_ESR_ELx_EC_CP10_ID, [ESR_ELx_EC_CP14_64] = &sync_desc_guest_ESR_ELx_EC_CP14_64, [ESR_ELx_EC_ILL] = &sync_desc_guest_ESR_ELx_EC_ILL, [ESR_ELx_EC_SYS64] = &sync_desc_guest_ESR_ELx_EC_SYS64, [ESR_ELx_EC_IABT_LOW] = &sync_desc_guest_ESR_ELx_EC_IABT_LOW, [ESR_ELx_EC_PC_ALIGN] = &sync_desc_guest_ESR_ELx_EC_PC_ALIGN, [ESR_ELx_EC_DABT_LOW] = &sync_desc_guest_ESR_ELx_EC_DABT_LOW, [ESR_ELx_EC_SP_ALIGN] = &sync_desc_guest_ESR_ELx_EC_SP_ALIGN, [ESR_ELx_EC_FP_EXC32] = &sync_desc_guest_ESR_ELx_EC_FP_EXC32, [ESR_ELx_EC_FP_EXC64] = &sync_desc_guest_ESR_ELx_EC_FP_EXC64, [ESR_ELx_EC_SERROR] = &sync_desc_guest_ESR_ELx_EC_SERROR, [ESR_ELx_EC_HVC32] = &sync_desc_guest_ESR_ELx_EC_HVC32, [ESR_ELx_EC_HVC64] = &sync_desc_guest_ESR_ELx_EC_HVC64, [ESR_ELx_EC_SMC32] = &sync_desc_guest_ESR_ELx_EC_SMC32, [ESR_ELx_EC_SMC64] = &sync_desc_guest_ESR_ELx_EC_SMC64, }; void handle_vcpu_sync_exception(gp_regs *regs) { int cpuid = smp_processor_id(); uint32_t esr_value; int ec_type; struct sync_desc *ec; struct vcpu *vcpu = get_current_vcpu(); if ((!vcpu) || (vcpu->task->affinity != cpuid)) panic("this vcpu is not belong to the pcpu"); esr_value = read_esr_el2(); ec_type = (esr_value & ESR_ELx_EC_MASK) >> ESR_ELx_EC_SHIFT; if (ec_type >= ESR_ELx_EC_MAX) { pr_err("unknown sync exception type from guest %d\n", ec_type); goto out; } pr_debug("sync from lower EL, handle 0x%x\n", ec_type); ec = guest_sync_descs[ec_type]; if (ec->irq_safe) local_irq_enable(); regs->pc += ec->ret_addr_adjust; ec->handler(regs, ec_type, esr_value); out: local_irq_disable(); } ================================================ FILE: kernel/arch/aarch64/virt/vfp.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #ifdef CONFIG_VIRT #include #endif struct vfp_context { uint64_t regs[64] __align(16); #ifdef CONFIG_VIRT uint32_t fpexc32_el2; uint32_t padding0; #endif uint32_t fpsr; uint32_t fpcr; uint32_t cptr; }; static void vfp_state_init(struct vcpu *vcpu, void *c) { struct vfp_context *context = (struct vfp_context *)c; memset(context, 0, sizeof(struct vfp_context)); context->cptr = 0x300000; } static void vfp_state_save(struct vcpu *vcpu, void *context) { struct vfp_context *c = (struct vfp_context *)context; if (task_is_32bit(vcpu->task)) c->fpexc32_el2 = read_sysreg32(FPEXC32_EL2); /* * need write CPTR_EL2 first to enable FPEN */ c->cptr = read_sysreg(CPTR_EL2); c->fpsr = read_sysreg(FPSR); c->fpcr = read_sysreg(FPCR); asm volatile("stp q0, q1, [%1, #16 * 0]\n\t" "stp q2, q3, [%1, #16 * 2]\n\t" "stp q4, q5, [%1, #16 * 4]\n\t" "stp q6, q7, [%1, #16 * 6]\n\t" "stp q8, q9, [%1, #16 * 8]\n\t" "stp q10, q11, [%1, #16 * 10]\n\t" "stp q12, q13, [%1, #16 * 12]\n\t" "stp q14, q15, [%1, #16 * 14]\n\t" "stp q16, q17, [%1, #16 * 16]\n\t" "stp q18, q19, [%1, #16 * 18]\n\t" "stp q20, q21, [%1, #16 * 20]\n\t" "stp q22, q23, [%1, #16 * 22]\n\t" "stp q24, q25, [%1, #16 * 24]\n\t" "stp q26, q27, [%1, #16 * 26]\n\t" "stp q28, q29, [%1, #16 * 28]\n\t" "stp q30, q31, [%1, #16 * 30]\n\t" : "=Q" (*c->regs) : "r" (c->regs)); } static void vfp_state_restore(struct vcpu *vcpu, void *context) { struct vfp_context *c = (struct vfp_context *)context; write_sysreg(c->cptr, CPTR_EL2); if (task_is_32bit(vcpu->task)) write_sysreg(c->fpexc32_el2, FPEXC32_EL2); write_sysreg(c->fpsr, FPSR); write_sysreg(c->fpcr, FPCR); asm volatile("ldp q0, q1, [%1, #16 * 0]\n\t" "ldp q2, q3, [%1, #16 * 2]\n\t" "ldp q4, q5, [%1, #16 * 4]\n\t" "ldp q6, q7, [%1, #16 * 6]\n\t" "ldp q8, q9, [%1, #16 * 8]\n\t" "ldp q10, q11, [%1, #16 * 10]\n\t" "ldp q12, q13, [%1, #16 * 12]\n\t" "ldp q14, q15, [%1, #16 * 14]\n\t" "ldp q16, q17, [%1, #16 * 16]\n\t" "ldp q18, q19, [%1, #16 * 18]\n\t" "ldp q20, q21, [%1, #16 * 20]\n\t" "ldp q22, q23, [%1, #16 * 22]\n\t" "ldp q24, q25, [%1, #16 * 24]\n\t" "ldp q26, q27, [%1, #16 * 26]\n\t" "ldp q28, q29, [%1, #16 * 28]\n\t" "ldp q30, q31, [%1, #16 * 30]\n\t" : : "Q" (*c->regs), "r" (c->regs)); } static int vfp_vmodule_init(struct vmodule *vmodule) { vmodule->context_size = sizeof(struct vfp_context); vmodule->state_init = vfp_state_init; vmodule->state_save = vfp_state_save; vmodule->state_restore = vfp_state_restore; return 0; } MINOS_MODULE_DECLARE(vfp, "vfp", (void *)vfp_vmodule_init); ================================================ FILE: kernel/arch/aarch64/virt/vmsa.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include struct aa64mmfr0 { uint64_t pa_range : 4; uint64_t asid : 4; uint64_t big_end : 4; uint64_t sns_mem : 4; uint64_t big_end_el0 : 4; uint64_t t_gran_16k : 4; uint64_t t_gran_64k : 4; uint64_t t_gran_4k : 4; uint64_t res : 32; }; static int __init_text el2_stage2_init(void) { /* * now just support arm fvp, TBD to support more * platform */ uint64_t value, dcache, icache; struct aa64mmfr0 *aa64mmfr0; value = read_id_aa64mmfr0_el1(); aa64mmfr0 = (struct aa64mmfr0 *)&value; pr_info("aa64mmfr0: pa_range:0x%x t_gran_16k:0x%x t_gran_64k" ":0x%x t_gran_4k:0x%x\n", aa64mmfr0->pa_range, aa64mmfr0->t_gran_16k, aa64mmfr0->t_gran_64k, aa64mmfr0->t_gran_4k); value = read_sysreg(CTR_EL0); dcache = 4 << ((value & 0xf0000) >> 16); icache = 4 << ((value & 0xf)); pr_info("dcache_line_size:%d ichache_line_size:%d\n", dcache, icache); return 0; } early_initcall(el2_stage2_init); ================================================ FILE: kernel/arch/aarch64/virt/vtimer.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #define REG_CNTCR 0x000 #define REG_CNTSR 0x004 #define REG_CNTCV_L 0x008 #define REG_CNTCV_H 0x00c #define REG_CNTFID0 0x020 #define REG_CNTVCT_LO 0x08 #define REG_CNTVCT_HI 0x0c #define REG_CNTFRQ 0x10 #define REG_CNTP_CVAL 0x24 #define REG_CNTP_TVAL 0x28 #define REG_CNTP_CTL 0x2c #define REG_CNTV_CVAL 0x30 #define REG_CNTV_TVAL 0x38 #define REG_CNTV_CTL 0x3c #define ACCESS_REG 0x0 #define ACCESS_MEM 0x1 struct vtimer { struct vcpu *vcpu; struct timer timer; int virq; uint32_t cnt_ctl; uint64_t cnt_cval; uint64_t freq; }; struct vtimer_context { struct vtimer phy_timer; struct vtimer virt_timer; unsigned long offset; }; static int arm_phy_timer_trap(struct vcpu *vcpu, int reg, int read, unsigned long *value); static int vtimer_vmodule_id = INVALID_MODULE_ID; #define get_access_vtimer(vtimer, c, access) \ do { \ vtimer = &c->phy_timer; \ } while (0) static void phys_timer_expire_function(unsigned long data) { struct vtimer *vtimer = (struct vtimer *)data; vtimer->cnt_ctl |= CNT_CTL_ISTATUS; vtimer->cnt_cval = 0; if (!(vtimer->cnt_ctl & CNT_CTL_IMASK)) send_virq_to_vcpu(vtimer->vcpu, vtimer->virq); } static void virt_timer_expire_function(unsigned long data) { struct vtimer *vtimer = (struct vtimer *)data; /* * just wake up the target vCPU. when switch to * this vcpu, the value of vtimer will restore and * if the irq is not mask, the vtimer will trigger * the hardware irq again. */ wake(&vtimer->vcpu->vcpu_event); } static void vtimer_state_restore(struct vcpu *vcpu, void *context) { struct vtimer_context *c = (struct vtimer_context *)context; struct vtimer *vtimer = &c->virt_timer; stop_timer(&vtimer->timer); write_sysreg64(c->offset, ARM64_CNTVOFF_EL2); write_sysreg64(vtimer->cnt_cval, ARM64_CNTV_CVAL_EL0); write_sysreg32(vtimer->cnt_ctl, ARM64_CNTV_CTL_EL0); isb(); } static void vtimer_state_save(struct vcpu *vcpu, void *context) { struct task *task = vcpu->task; struct vtimer_context *c = (struct vtimer_context *)context; struct vtimer *vtimer = &c->virt_timer; vtimer->cnt_cval = read_sysreg64(ARM64_CNTV_CVAL_EL0); vtimer->cnt_ctl = read_sysreg32(ARM64_CNTV_CTL_EL0); write_sysreg32(0, CNTV_CTL_EL0); isb(); if ((task->state == TASK_STATE_STOP) || (task->state == TASK_STATE_SUSPEND)) return; if ((vtimer->cnt_ctl & CNT_CTL_ENABLE) && !(vtimer->cnt_ctl & CNT_CTL_IMASK)) { mod_timer(&vtimer->timer, ticks_to_ns(vtimer->cnt_cval + c->offset - boot_tick)); } } static void vtimer_state_init(struct vcpu *vcpu, void *context) { struct vtimer *vtimer; struct arm_virt_data *arm_data = vcpu->vm->arch_data; struct vtimer_context *c = (struct vtimer_context *)context; if (get_vcpu_id(vcpu) == 0) { vcpu->vm->time_offset = get_sys_ticks(); arm_data->phy_timer_trap = arm_phy_timer_trap; } c->offset = vcpu->vm->time_offset; vtimer = &c->virt_timer; vtimer->vcpu = vcpu; vtimer->virq = vcpu->vm->vtimer_virq; vtimer->cnt_ctl = 0; vtimer->cnt_cval = 0; init_timer(&vtimer->timer, virt_timer_expire_function, (unsigned long)vtimer); vtimer = &c->phy_timer; vtimer->vcpu = vcpu; vtimer->virq = 26; vtimer->cnt_ctl = 0; vtimer->cnt_cval = 0; init_timer(&vtimer->timer, phys_timer_expire_function, (unsigned long)vtimer); } static void vtimer_state_stop(struct vcpu *vcpu, void *context) { struct vtimer_context *c = (struct vtimer_context *)context; stop_timer(&c->virt_timer.timer); stop_timer(&c->phy_timer.timer); } static inline void asoc_handle_cntp_ctl(struct vcpu *vcpu, struct vtimer *vtimer) { /* * apple xnu use physical timer's interrupt as a fiq * and read the ctl register to check wheter the timer * is triggered, if the read access is happened in the * fiq handler, need to clear the interrupt */ if ((vtimer->cnt_ctl & CNT_CTL_ISTATUS) && (read_sysreg(HCR_EL2) & HCR_EL2_VF)) { vtimer->cnt_ctl &= ~CNT_CTL_ISTATUS; clear_pending_virq(vcpu, vtimer->virq); } } static void vtimer_handle_cntp_ctl(struct vcpu *vcpu, int access, int read, unsigned long *value) { uint32_t v; struct vtimer *vtimer; struct vtimer_context *c; unsigned long ns; c = get_vmodule_data_by_id(vcpu, vtimer_vmodule_id); get_access_vtimer(vtimer, c, access); if (read) { *value = vtimer->cnt_ctl; if (vcpu->vm->os->type == OS_TYPE_XNU) asoc_handle_cntp_ctl(vcpu, vtimer); } else { v = (uint32_t)(*value); v &= ~CNT_CTL_ISTATUS; if (v & CNT_CTL_ENABLE) v |= vtimer->cnt_ctl & CNT_CTL_ISTATUS; vtimer->cnt_ctl = v; if ((vtimer->cnt_ctl & CNT_CTL_ENABLE) && (vtimer->cnt_cval != 0)) { ns = ticks_to_ns(vtimer->cnt_cval + c->offset); mod_timer(&vtimer->timer, ns); } else { stop_timer(&vtimer->timer); } } } static void vtimer_handle_cntp_tval(struct vcpu *vcpu, int access, int read, unsigned long *value) { struct vtimer *vtimer; unsigned long now; unsigned long ticks; struct vtimer_context *c; c = get_vmodule_data_by_id(vcpu, vtimer_vmodule_id); get_access_vtimer(vtimer, c, access); now = get_sys_ticks() - c->offset; if (read) { ticks = (vtimer->cnt_cval - now - c->offset) & 0xffffffff; *value = ticks; } else { unsigned long v = *value; vtimer->cnt_cval = get_sys_ticks() + v; if (vtimer->cnt_ctl & CNT_CTL_ENABLE) { vtimer->cnt_ctl &= ~CNT_CTL_ISTATUS; ticks = ticks_to_ns(vtimer->cnt_cval); mod_timer(&vtimer->timer, ticks); } } } static void vtimer_handle_cntp_cval(struct vcpu *vcpu, int access, int read, unsigned long *value) { unsigned long ns; struct vtimer *vtimer; struct vtimer_context *c; c = get_vmodule_data_by_id(vcpu, vtimer_vmodule_id); get_access_vtimer(vtimer, c, access); if (read) { *value = vtimer->cnt_cval - c->offset; } else { vtimer->cnt_cval = *value + c->offset; if (vtimer->cnt_ctl & CNT_CTL_ENABLE) { vtimer->cnt_ctl &= ~CNT_CTL_ISTATUS; ns = ticks_to_ns(vtimer->cnt_cval); mod_timer(&vtimer->timer, ns); } } } static int arm_phy_timer_trap(struct vcpu *vcpu, int reg, int read, unsigned long *value) { switch (reg) { case ESR_SYSREG_CNTP_CTL_EL0: vtimer_handle_cntp_ctl(vcpu, ACCESS_REG, read, value); break; case ESR_SYSREG_CNTP_CVAL_EL0: vtimer_handle_cntp_cval(vcpu, ACCESS_REG, read, value); break; case ESR_SYSREG_CNTP_TVAL_EL0: vtimer_handle_cntp_tval(vcpu, ACCESS_REG, read, value); break; default: break; } return 0; } static int vtimer_vmodule_init(struct vmodule *vmodule) { vmodule->context_size = sizeof(struct vtimer_context); vmodule->state_init = vtimer_state_init; vmodule->state_save = vtimer_state_save; vmodule->state_restore = vtimer_state_restore; vmodule->state_stop = vtimer_state_stop; vmodule->state_reset = vtimer_state_stop; vtimer_vmodule_id = vmodule->id; return 0; } int arch_vtimer_init(uint32_t virtual_irq, uint32_t phy_irq) { return register_vcpu_vmodule("vtimer_module", vtimer_vmodule_init); } int virtual_timer_irq_handler(uint32_t irq, void *data) { uint32_t value; struct vcpu *vcpu = get_current_vcpu(); /* * if the current task is not a vcpu, disable the vtimer * since the pending request vtimer irq is set to * the timer */ if (!task_is_vcpu(current)) { write_sysreg32(0, ARM64_CNTV_CTL_EL0); return 0; } /* * this case ususally happened when the vcpu called * WFI to enter idle idle mode, but the vtimer irq is * triggered when context switch. then when in idle task * this IRQ is responsed. two case need consider: * * 1 - the vtimer interrup will send to wrong vcpu ? * 2 - Here the logic of idle can be optimized to avoid * this situation */ value = read_sysreg32(ARM64_CNTV_CTL_EL0); if (!(value & CNT_CTL_ISTATUS)) { pr_debug("vtimer is not trigger\n"); return 0; } value = value | CNT_CTL_IMASK; write_sysreg32(value, ARM64_CNTV_CTL_EL0); return send_virq_to_vcpu(vcpu, vcpu->vm->vtimer_virq); } ================================================ FILE: kernel/configs/espressobin_defconfig ================================================ CONFIG_ARCH_AARCH64=y CONFIG_64BIT=y # # Minos aarch64 Arch Feature # CONFIG_NR_CPUS_CLUSTER0=2 CONFIG_NR_CPUS_CLUSTER1=0 # CONFIG_MPIDR_SHIFT is not set CONFIG_EXCEPTION_STACK_SIZE=0x2000 # end of Minos aarch64 Arch Feature # # Minos aarch64 virtualaztion features # # # Minos OS configuration # CONFIG_MAX_CPU_NR=8 # CONFIG_TASK_STACK_SIZE_4K is not set CONFIG_TASK_STACK_SIZE_8K=y CONFIG_TASK_STACK_SIZE=0x2000 CONFIG_TASK_STACK_SHIFT=13 CONFIG_STACK_PAGE_ALIGN=y CONFIG_MAX_SLAB_BLOCKS=10 CONFIG_TASK_RUN_TIME=100 CONFIG_MINOS_IRQWORK_IRQ=5 CONFIG_SMP_FUNCTION_CALL_IRQ=6 CONFIG_MINOS_RESCHED_IRQ=7 CONFIG_SMP=y CONFIG_NR_SGI_IRQS=16 CONFIG_NR_PPI_IRQS=16 CONFIG_NR_SPI_IRQS=256 # CONFIG_OS_REALTIME_CORE0 is not set # CONFIG_PRINT_DEBUG is not set # CONFIG_PRINT_INFO is not set CONFIG_PRINT_NOTICE=y # CONFIG_PRINT_WARN is not set # CONFIG_PRINT_ERROR is not set CONFIG_DEFAULT_MM_ALLOCATOR=y # CONFIG_SIMPLE_MM_ALLOCATOR is not set CONFIG_LOG_LEVEL=3 # end of Minos OS configuration # # Minos Virtualization Configuration # CONFIG_VIRT=y CONFIG_VIRTIO_MMIO=y CONFIG_MAX_VM=64 CONFIG_VRTC_PL031=y CONFIG_VWDT_SP805=y # # Virqchip controller support # CONFIG_VIRQCHIP_VGICV2=y CONFIG_VIRQCHIP_VGICV3=y # CONFIG_VIRQCHIP_BCM2836 is not set CONFIG_VIRQCHIP_AIC=y # end of Virqchip controller support CONFIG_VMBOX=y # # VM operating system support # CONFIG_OS_LINUX_SUPPORT=y CONFIG_OS_XNU_SUPPORT=y # end of VM operating system support # end of Minos Virtualization Configuration # # Device Drivers # # # Interrupt Controller Driver # CONFIG_IRQCHIP_GICV3=y CONFIG_IRQCHIP_GICV2=y # CONFIG_IRQCHIP_BCM2836 is not set # end of Interrupt Controller Driver # # Serial Drivers # CONFIG_SERIAL=y # CONFIG_SERIAL_BCM283X_MU is not set CONFIG_SERIAL_MVEBU_A3700=y # CONFIG_SERIAL_PL011 is not set # CONFIG_SERIAL_AMLOGIC is not set # end of Serial Drivers CONFIG_DEVICE_TREE=y # end of Device Drivers # # Platform Configuration # # CONFIG_SOC_FVP is not set CONFIG_SOC_MARVELL_A3700=y # CONFIG_SOC_BCM2837 is not set # CONFIG_SOC_BCM2838 is not set # CONFIG_SOC_AMLOGIC is not set CONFIG_MINOS_ENTRY_ADDRESS=0x3c004000 CONFIG_MINOS_RAM_SIZE=0x4000000 CONFIG_NR_CPUS=2 CONFIG_UART_BASE=0xd0012000 CONFIG_UART_IO_SIZE=0x1000 # end of Platform Configuration # # Third Party Library And Module # # # Application Config # # CONFIG_SHELL is not set # end of Application Config # # System libary config # # end of System libary config ================================================ FILE: kernel/configs/fvp_a76_defconfig ================================================ CONFIG_ARCH_AARCH64=y CONFIG_64BIT=y # # Minos aarch64 Arch Feature # CONFIG_NR_CPUS_CLUSTER0=2 CONFIG_NR_CPUS_CLUSTER1=0 # CONFIG_MPIDR_SHIFT is not set CONFIG_EXCEPTION_STACK_SIZE=0x2000 # end of Minos aarch64 Arch Feature # # Minos aarch64 virtualaztion features # # # Minos OS configuration # CONFIG_MAX_CPU_NR=8 # CONFIG_TASK_STACK_SIZE_4K is not set CONFIG_TASK_STACK_SIZE_8K=y CONFIG_TASK_STACK_SIZE=0x2000 CONFIG_TASK_STACK_SHIFT=13 CONFIG_STACK_PAGE_ALIGN=y CONFIG_MAX_SLAB_BLOCKS=10 CONFIG_TASK_RUN_TIME=100 CONFIG_MINOS_IRQWORK_IRQ=5 CONFIG_SMP_FUNCTION_CALL_IRQ=6 CONFIG_MINOS_RESCHED_IRQ=7 CONFIG_SMP=y CONFIG_NR_SGI_IRQS=16 CONFIG_NR_PPI_IRQS=16 CONFIG_NR_SPI_IRQS=256 # CONFIG_OS_REALTIME_CORE0 is not set # CONFIG_PRINT_DEBUG is not set # CONFIG_PRINT_INFO is not set CONFIG_PRINT_NOTICE=y # CONFIG_PRINT_WARN is not set # CONFIG_PRINT_ERROR is not set CONFIG_DEFAULT_MM_ALLOCATOR=y # CONFIG_SIMPLE_MM_ALLOCATOR is not set CONFIG_LOG_LEVEL=3 # end of Minos OS configuration # # Minos Virtualization Configuration # CONFIG_VIRT=y CONFIG_VIRTIO_MMIO=y CONFIG_MAX_VM=64 CONFIG_VRTC_PL031=y CONFIG_VWDT_SP805=y # # Virqchip controller support # CONFIG_VIRQCHIP_VGICV2=y CONFIG_VIRQCHIP_VGICV3=y # CONFIG_VIRQCHIP_BCM2836 is not set CONFIG_VIRQCHIP_AIC=y # end of Virqchip controller support CONFIG_VMBOX=y # # VM operating system support # CONFIG_OS_LINUX_SUPPORT=y CONFIG_OS_XNU_SUPPORT=y # end of VM operating system support # end of Minos Virtualization Configuration # # Device Drivers # # # Interrupt Controller Driver # CONFIG_IRQCHIP_GICV3=y CONFIG_IRQCHIP_GICV2=y # CONFIG_IRQCHIP_BCM2836 is not set # end of Interrupt Controller Driver # # Serial Drivers # CONFIG_SERIAL=y # CONFIG_SERIAL_BCM283X_MU is not set # CONFIG_SERIAL_MVEBU_A3700 is not set CONFIG_SERIAL_PL011=y # CONFIG_SERIAL_AMLOGIC is not set # end of Serial Drivers CONFIG_DEVICE_TREE=y # end of Device Drivers # # Platform Configuration # CONFIG_SOC_FVP=y # CONFIG_SOC_MARVELL_A3700 is not set # CONFIG_SOC_BCM2837 is not set # CONFIG_SOC_BCM2838 is not set # CONFIG_SOC_AMLOGIC is not set CONFIG_MINOS_ENTRY_ADDRESS=0xc0000000 CONFIG_MINOS_RAM_SIZE=0x4000000 CONFIG_NR_CPUS=2 CONFIG_MPIDR_SHIFT=y CONFIG_UART_BASE=0x1c0a0000 CONFIG_UART_IO_SIZE=0x1000 # end of Platform Configuration # # Third Party Library And Module # # # Application Config # CONFIG_SHELL=y # # Shell config # CONFIG_SHELL_TASK_PRIO=63 # end of Shell config # end of Application Config # # System libary config # # # Shell Command Support # CONFIG_SHELL_COMMAND_TASK=y # end of Shell Command Support # end of System libary config ================================================ FILE: kernel/configs/fvp_defconfig ================================================ CONFIG_ARCH_AARCH64=y CONFIG_64BIT=y # # Minos aarch64 Arch Feature # CONFIG_NR_CPUS_CLUSTER0=4 CONFIG_NR_CPUS_CLUSTER1=0 # CONFIG_MPIDR_SHIFT is not set CONFIG_EXCEPTION_STACK_SIZE=0x2000 # end of Minos aarch64 Arch Feature # # Minos aarch64 virtualaztion features # # # Minos OS configuration # CONFIG_MAX_CPU_NR=8 # CONFIG_TASK_STACK_SIZE_4K is not set CONFIG_TASK_STACK_SIZE_8K=y CONFIG_TASK_STACK_SIZE=0x2000 CONFIG_TASK_STACK_SHIFT=13 CONFIG_STACK_PAGE_ALIGN=y CONFIG_MAX_SLAB_BLOCKS=10 CONFIG_TASK_RUN_TIME=100 CONFIG_MINOS_IRQWORK_IRQ=5 CONFIG_SMP_FUNCTION_CALL_IRQ=6 CONFIG_MINOS_RESCHED_IRQ=7 CONFIG_SMP=y CONFIG_NR_SGI_IRQS=16 CONFIG_NR_PPI_IRQS=16 CONFIG_NR_SPI_IRQS=256 # CONFIG_OS_REALTIME_CORE0 is not set # CONFIG_PRINT_DEBUG is not set # CONFIG_PRINT_INFO is not set CONFIG_PRINT_NOTICE=y # CONFIG_PRINT_WARN is not set # CONFIG_PRINT_ERROR is not set CONFIG_DEFAULT_MM_ALLOCATOR=y # CONFIG_SIMPLE_MM_ALLOCATOR is not set CONFIG_LOG_LEVEL=4 # end of Minos OS configuration # # Minos Virtualization Configuration # CONFIG_VIRT=y CONFIG_VIRTIO_MMIO=y CONFIG_MAX_VM=8 CONFIG_VRTC_PL031=y CONFIG_VWDT_SP805=y # # Virqchip controller support # CONFIG_VIRQCHIP_VGICV2=y CONFIG_VIRQCHIP_VGICV3=y # CONFIG_VIRQCHIP_BCM2836 is not set CONFIG_VIRQCHIP_AIC=y # end of Virqchip controller support CONFIG_VMBOX=y # # VM operating system support # CONFIG_OS_LINUX_SUPPORT=y CONFIG_OS_XNU_SUPPORT=y # end of VM operating system support # end of Minos Virtualization Configuration # # Device Drivers # # # Interrupt Controller Driver # CONFIG_IRQCHIP_GICV3=y CONFIG_IRQCHIP_GICV2=y # CONFIG_IRQCHIP_BCM2836 is not set # end of Interrupt Controller Driver # # Serial Drivers # CONFIG_SERIAL=y # CONFIG_SERIAL_BCM283X_MU is not set # CONFIG_SERIAL_MVEBU_A3700 is not set CONFIG_SERIAL_PL011=y # CONFIG_SERIAL_AMLOGIC is not set # end of Serial Drivers CONFIG_DEVICE_TREE=y # end of Device Drivers # # Platform Configuration # CONFIG_SOC_FVP=y # CONFIG_SOC_MARVELL_A3700 is not set # CONFIG_SOC_BCM2837 is not set # CONFIG_SOC_BCM2838 is not set # CONFIG_SOC_AMLOGIC is not set CONFIG_MINOS_ENTRY_ADDRESS=0xc0000000 CONFIG_MINOS_RAM_SIZE=0x4000000 CONFIG_NR_CPUS=4 CONFIG_UART_BASE=0x1c0a0000 CONFIG_UART_IO_SIZE=0x1000 # end of Platform Configuration # # Third Party Library And Module # # # Application Config # CONFIG_SHELL=y # # Shell config # CONFIG_SHELL_TASK_PRIO=63 # end of Shell config # end of Application Config # # System libary config # # # Shell Command Support # CONFIG_SHELL_COMMAND_TASK=y # end of Shell Command Support # end of System libary config ================================================ FILE: kernel/configs/fvp_rtos_defconfig ================================================ CONFIG_ARCH_AARCH64=y CONFIG_DEVICE_TREE=y CONFIG_PLATFORM_FVP=y CONFIG_SERIAL_PL011=y CONFIG_IRQCHIP_GICV3=y CONFIG_IRQCHIP_GICV2=y CONFIG_VIRQCHIP_VGICV2=y CONFIG_VIRQCHIP_VGICV3=y CONFIG_VIRTIO_MMIO=y CONFIG_VRTC_PL031=y CONFIG_VWDT_SP805=y # 0xc0000000 + CONFIG_NR_CPUS * 8K = CONFIG_MINOS_ENTRY_ADDRESS=0xc0008000 CONFIG_MINOS_RAM_SIZE=64M CONFIG_MAX_CPU_NR=8 CONFIG_NR_CPUS=4 CONFIG_NR_CPUS_CLUSTER0=4 CONFIG_NR_CPUS_CLUSTER1=0 CONFIG_UART_BASE=0x1c090000 CONFIG_UART_IO_SIZE=0x40000 # all realtime task will sched at core0 # CONFIG_OS_REALTIME_CORE0 CONFIG_TASK_RUN_TIME=100 # CONFIG_VIRT=y CONFIG_EXCEPTION_SIZE=8192 CONFIG_TASK_STACK_SIZE=8192 CONFIG_TASK_STACK_SHIFT=13 CONFIG_STACK_PAGE_ALIGN=y CONFIG_SMP=y ================================================ FILE: kernel/configs/kvim3_defconfig ================================================ CONFIG_ARCH_AARCH64=y CONFIG_64BIT=y # # Minos aarch64 Arch Feature # CONFIG_NR_CPUS_CLUSTER0=2 CONFIG_NR_CPUS_CLUSTER1=4 # CONFIG_MPIDR_SHIFT is not set CONFIG_EXCEPTION_STACK_SIZE=0x2000 # end of Minos aarch64 Arch Feature # # Minos aarch64 virtualaztion features # # # Minos OS configuration # CONFIG_MAX_CPU_NR=8 # CONFIG_TASK_STACK_SIZE_4K is not set CONFIG_TASK_STACK_SIZE_8K=y CONFIG_TASK_STACK_SIZE=0x2000 CONFIG_TASK_STACK_SHIFT=13 CONFIG_STACK_PAGE_ALIGN=y CONFIG_MAX_SLAB_BLOCKS=10 CONFIG_TASK_RUN_TIME=100 CONFIG_MINOS_IRQWORK_IRQ=5 CONFIG_SMP_FUNCTION_CALL_IRQ=6 CONFIG_MINOS_RESCHED_IRQ=7 CONFIG_SMP=y CONFIG_NR_SGI_IRQS=16 CONFIG_NR_PPI_IRQS=16 CONFIG_NR_SPI_IRQS=512 # CONFIG_OS_REALTIME_CORE0 is not set # CONFIG_PRINT_DEBUG is not set CONFIG_PRINT_INFO=y # CONFIG_PRINT_NOTICE is not set # CONFIG_PRINT_WARN is not set # CONFIG_PRINT_ERROR is not set CONFIG_DEFAULT_MM_ALLOCATOR=y # CONFIG_SIMPLE_MM_ALLOCATOR is not set CONFIG_LOG_LEVEL=4 # end of Minos OS configuration # # Minos Virtualization Configuration # CONFIG_VIRT=y CONFIG_VIRTIO_MMIO=y CONFIG_MAX_VM=64 CONFIG_VRTC_PL031=y CONFIG_VWDT_SP805=y # # Virqchip controller support # CONFIG_VIRQCHIP_VGICV2=y CONFIG_VIRQCHIP_VGICV3=y # CONFIG_VIRQCHIP_BCM2836 is not set CONFIG_VIRQCHIP_AIC=y # end of Virqchip controller support CONFIG_VMBOX=y # # VM operating system support # CONFIG_OS_LINUX_SUPPORT=y CONFIG_OS_XNU_SUPPORT=y # end of VM operating system support # end of Minos Virtualization Configuration # # Device Drivers # # # Interrupt Controller Driver # # CONFIG_IRQCHIP_GICV3 is not set CONFIG_IRQCHIP_GICV2=y # CONFIG_IRQCHIP_BCM2836 is not set # end of Interrupt Controller Driver # # Serial Drivers # CONFIG_SERIAL=y # CONFIG_SERIAL_BCM283X_MU is not set # CONFIG_SERIAL_MVEBU_A3700 is not set # CONFIG_SERIAL_PL011 is not set CONFIG_SERIAL_AMLOGIC=y # end of Serial Drivers CONFIG_DEVICE_TREE=y # end of Device Drivers # # Platform Configuration # # CONFIG_SOC_FVP is not set # CONFIG_SOC_MARVELL_A3700 is not set # CONFIG_SOC_BCM2837 is not set # CONFIG_SOC_BCM2838 is not set CONFIG_SOC_AMLOGIC=y CONFIG_MINOS_ENTRY_ADDRESS=0xeb80c000 CONFIG_MINOS_RAM_SIZE=0x2000000 CONFIG_NR_CPUS=6 CONFIG_UART_BASE=0xff803000 CONFIG_UART_IO_SIZE=0x1000 CONFIG_DTB_LOAD_ADDRESS=0xed600000 # end of Platform Configuration # # Third Party Library And Module # # # Application Config # CONFIG_SHELL=y # # Shell config # CONFIG_SHELL_TASK_PRIO=63 # end of Shell config # end of Application Config # # System libary config # # # Shell Command Support # CONFIG_SHELL_COMMAND_TASK=y # end of Shell Command Support # end of System libary config ================================================ FILE: kernel/configs/qemu_arm64_defconfig ================================================ CONFIG_ARCH_AARCH64=y CONFIG_64BIT=y # # Minos aarch64 Arch Feature # CONFIG_NR_CPUS_CLUSTER0=4 CONFIG_NR_CPUS_CLUSTER1=0 CONFIG_EXCEPTION_STACK_SIZE=0x2000 # CONFIG_ARM_ATOMIC_LSE is not set CONFIG_PTOV_MASK=0x0 CONFIG_VTOP_MASK=0x0 # end of Minos aarch64 Arch Feature # # Minos aarch64 virtualaztion features # # CONFIG_ARM_VHE is not set # end of Minos aarch64 virtualaztion features # # Minos OS configuration # CONFIG_MAX_CPU_NR=8 # CONFIG_TASK_STACK_SIZE_4K is not set CONFIG_TASK_STACK_SIZE_8K=y CONFIG_TASK_STACK_SIZE=0x2000 CONFIG_TASK_STACK_SHIFT=13 CONFIG_STACK_PAGE_ALIGN=y CONFIG_TASK_RUN_TIME=100 CONFIG_MINOS_IRQWORK_IRQ=5 CONFIG_SMP_FUNCTION_CALL_IRQ=6 CONFIG_MINOS_RESCHED_IRQ=7 CONFIG_SMP=y CONFIG_NR_SGI_IRQS=16 CONFIG_NR_PPI_IRQS=16 CONFIG_NR_SPI_IRQS=256 # CONFIG_PRINT_DEBUG is not set CONFIG_PRINT_INFO=y # CONFIG_PRINT_NOTICE is not set # CONFIG_PRINT_WARN is not set # CONFIG_PRINT_ERROR is not set CONFIG_LOG_LEVEL=4 # end of Minos OS configuration # # Minos Virtualization Configuration # CONFIG_VIRT=y CONFIG_VIRTIO_MMIO=y CONFIG_MAX_VM=64 CONFIG_VRTC_PL031=y CONFIG_VWDT_SP805=y # # Virqchip controller support # CONFIG_VIRQCHIP_VGICV2=y CONFIG_VIRQCHIP_VGICV3=y # CONFIG_VIRQCHIP_BCM2836 is not set CONFIG_VIRQCHIP_AIC=y # end of Virqchip controller support CONFIG_VMBOX=y # # VM operating system support # CONFIG_OS_LINUX_SUPPORT=y CONFIG_OS_XNU_SUPPORT=y # end of VM operating system support # end of Minos Virtualization Configuration # # Device Drivers # # # Interrupt Controller Driver # CONFIG_IRQCHIP_GICV3=y CONFIG_IRQCHIP_GICV2=y # CONFIG_IRQCHIP_BCM2836 is not set # end of Interrupt Controller Driver # # Serial Drivers # CONFIG_SERIAL=y # CONFIG_SERIAL_BCM283X_MU is not set # CONFIG_SERIAL_MVEBU_A3700 is not set CONFIG_SERIAL_PL011=y # CONFIG_SERIAL_AMLOGIC is not set # end of Serial Drivers CONFIG_DEVICE_TREE=y # end of Device Drivers # # Platform Configuration # # CONFIG_SOC_FVP is not set # CONFIG_SOC_MARVELL_A3700 is not set # CONFIG_SOC_BCM2837 is not set # CONFIG_SOC_BCM2838 is not set # CONFIG_SOC_AMLOGIC is not set CONFIG_SOC_QEMU=y CONFIG_MINOS_ENTRY_ADDRESS=0x40000000 CONFIG_MINOS_RAM_SIZE=0x4000000 CONFIG_NR_CPUS=4 CONFIG_UART_BASE=0x9000000 CONFIG_UART_IO_SIZE=0x1000 CONFIG_UART_IRQ=33 # end of Platform Configuration CONFIG_SHELL=y # # Third Party Library And Module # CONFIG_SHELL_COMMAND_TASK=y ================================================ FILE: kernel/configs/r8a7795_defconfig ================================================ CONFIG_NR_CPUS_CLUSTER1=4 CONFIG_SMP=y CONFIG_NR_SPI_IRQS=480 CONFIG_LOG_LEVEL_COLORFUL=y CONFIG_VIRT=y CONFIG_SOC_R8A7795=y CONFIG_SHELL=y ================================================ FILE: kernel/configs/rpi_3_defconfig ================================================ CONFIG_ARCH_AARCH64=y CONFIG_64BIT=y # # Minos aarch64 Arch Feature # CONFIG_NR_CPUS_CLUSTER0=4 CONFIG_NR_CPUS_CLUSTER1=0 # CONFIG_MPIDR_SHIFT is not set CONFIG_EXCEPTION_STACK_SIZE=0x2000 # end of Minos aarch64 Arch Feature # # Minos aarch64 virtualaztion features # # # Minos OS configuration # CONFIG_MAX_CPU_NR=8 # CONFIG_TASK_STACK_SIZE_4K is not set CONFIG_TASK_STACK_SIZE_8K=y CONFIG_TASK_STACK_SIZE=0x2000 CONFIG_TASK_STACK_SHIFT=13 CONFIG_STACK_PAGE_ALIGN=y CONFIG_MAX_SLAB_BLOCKS=10 CONFIG_TASK_RUN_TIME=100 CONFIG_MINOS_IRQWORK_IRQ=5 CONFIG_SMP_FUNCTION_CALL_IRQ=6 CONFIG_MINOS_RESCHED_IRQ=7 CONFIG_SMP=y CONFIG_NR_SGI_IRQS=16 CONFIG_NR_PPI_IRQS=16 CONFIG_NR_SPI_IRQS=256 # CONFIG_OS_REALTIME_CORE0 is not set # CONFIG_PRINT_DEBUG is not set # CONFIG_PRINT_INFO is not set CONFIG_PRINT_NOTICE=y # CONFIG_PRINT_WARN is not set # CONFIG_PRINT_ERROR is not set CONFIG_DEFAULT_MM_ALLOCATOR=y # CONFIG_SIMPLE_MM_ALLOCATOR is not set CONFIG_LOG_LEVEL=3 # end of Minos OS configuration # # Minos Virtualization Configuration # CONFIG_VIRT=y CONFIG_VIRTIO_MMIO=y CONFIG_MAX_VM=64 CONFIG_VRTC_PL031=y CONFIG_VWDT_SP805=y # # Virqchip controller support # CONFIG_VIRQCHIP_VGICV2=y CONFIG_VIRQCHIP_VGICV3=y CONFIG_VIRQCHIP_BCM2836=y CONFIG_VIRQCHIP_AIC=y # end of Virqchip controller support CONFIG_VMBOX=y # # VM operating system support # CONFIG_OS_LINUX_SUPPORT=y CONFIG_OS_XNU_SUPPORT=y # end of VM operating system support # end of Minos Virtualization Configuration # # Device Drivers # # # Interrupt Controller Driver # # CONFIG_IRQCHIP_GICV3 is not set # CONFIG_IRQCHIP_GICV2 is not set CONFIG_IRQCHIP_BCM2836=y # end of Interrupt Controller Driver # # Serial Drivers # CONFIG_SERIAL=y CONFIG_SERIAL_BCM283X_MU=y # CONFIG_SERIAL_MVEBU_A3700 is not set # CONFIG_SERIAL_PL011 is not set # CONFIG_SERIAL_AMLOGIC is not set # end of Serial Drivers CONFIG_DEVICE_TREE=y # end of Device Drivers # # Platform Configuration # # CONFIG_SOC_FVP is not set # CONFIG_SOC_MARVELL_A3700 is not set CONFIG_SOC_BCM2837=y # CONFIG_SOC_BCM2838 is not set # CONFIG_SOC_AMLOGIC is not set CONFIG_MINOS_ENTRY_ADDRESS=0x28008000 CONFIG_MINOS_RAM_SIZE=0x2000000 CONFIG_NR_CPUS=4 CONFIG_UART_BASE=0x3f215040 CONFIG_UART_IO_SIZE=0x1000 CONFIG_PLATFORM_BCM2837=y CONFIG_HVM_SPI_VIRQ_NR=96 # end of Platform Configuration # # Third Party Library And Module # # # Application Config # # CONFIG_SHELL is not set # end of Application Config # # System libary config # # end of System libary config ================================================ FILE: kernel/configs/rpi_4_defconfig ================================================ CONFIG_ARCH_AARCH64=y CONFIG_64BIT=y # # Minos aarch64 Arch Feature # CONFIG_NR_CPUS_CLUSTER0=4 CONFIG_NR_CPUS_CLUSTER1=0 # CONFIG_MPIDR_SHIFT is not set CONFIG_EXCEPTION_STACK_SIZE=0x2000 # end of Minos aarch64 Arch Feature # # Minos aarch64 virtualaztion features # # # Minos OS configuration # CONFIG_MAX_CPU_NR=8 # CONFIG_TASK_STACK_SIZE_4K is not set CONFIG_TASK_STACK_SIZE_8K=y CONFIG_TASK_STACK_SIZE=0x2000 CONFIG_TASK_STACK_SHIFT=13 CONFIG_STACK_PAGE_ALIGN=y CONFIG_MAX_SLAB_BLOCKS=10 CONFIG_TASK_RUN_TIME=100 CONFIG_MINOS_IRQWORK_IRQ=5 CONFIG_SMP_FUNCTION_CALL_IRQ=6 CONFIG_MINOS_RESCHED_IRQ=7 CONFIG_SMP=y CONFIG_NR_SGI_IRQS=16 CONFIG_NR_PPI_IRQS=16 CONFIG_NR_SPI_IRQS=256 # CONFIG_OS_REALTIME_CORE0 is not set # CONFIG_PRINT_DEBUG is not set # CONFIG_PRINT_INFO is not set CONFIG_PRINT_NOTICE=y # CONFIG_PRINT_WARN is not set # CONFIG_PRINT_ERROR is not set CONFIG_DEFAULT_MM_ALLOCATOR=y # CONFIG_SIMPLE_MM_ALLOCATOR is not set CONFIG_LOG_LEVEL=3 # end of Minos OS configuration # # Minos Virtualization Configuration # CONFIG_VIRT=y CONFIG_VIRTIO_MMIO=y CONFIG_MAX_VM=64 CONFIG_VRTC_PL031=y CONFIG_VWDT_SP805=y # # Virqchip controller support # CONFIG_VIRQCHIP_VGICV2=y CONFIG_VIRQCHIP_VGICV3=y # CONFIG_VIRQCHIP_BCM2836 is not set CONFIG_VIRQCHIP_AIC=y # end of Virqchip controller support CONFIG_VMBOX=y # # VM operating system support # CONFIG_OS_LINUX_SUPPORT=y CONFIG_OS_XNU_SUPPORT=y # end of VM operating system support # end of Minos Virtualization Configuration # # Device Drivers # # # Interrupt Controller Driver # # CONFIG_IRQCHIP_GICV3 is not set CONFIG_IRQCHIP_GICV2=y # CONFIG_IRQCHIP_BCM2836 is not set # end of Interrupt Controller Driver # # Serial Drivers # CONFIG_SERIAL=y CONFIG_SERIAL_BCM283X_MU=y # CONFIG_SERIAL_MVEBU_A3700 is not set # CONFIG_SERIAL_PL011 is not set # CONFIG_SERIAL_AMLOGIC is not set # end of Serial Drivers CONFIG_DEVICE_TREE=y # end of Device Drivers # # Platform Configuration # # CONFIG_SOC_FVP is not set # CONFIG_SOC_MARVELL_A3700 is not set # CONFIG_SOC_BCM2837 is not set CONFIG_SOC_BCM2838=y # CONFIG_SOC_AMLOGIC is not set CONFIG_MINOS_ENTRY_ADDRESS=0x37408000 CONFIG_MINOS_RAM_SIZE=0x4000000 CONFIG_NR_CPUS=4 CONFIG_UART_BASE=0xfe215040 CONFIG_UART_IO_SIZE=0x1000 # end of Platform Configuration # # Third Party Library And Module # # # Application Config # CONFIG_SHELL=y # # Shell config # CONFIG_SHELL_TASK_PRIO=63 # end of Shell config # end of Application Config # # System libary config # # # Shell Command Support # CONFIG_SHELL_COMMAND_TASK=y # end of Shell Command Support # end of System libary config ================================================ FILE: kernel/core/Kconfig ================================================ menu "Minos OS configuration" config MAX_CPU_NR int "max cpu in system" default 8 help max cpu count in system choice prompt "default task stack size" default TASK_STACK_SIZE_8K config TASK_STACK_SIZE_4K bool "4K" config TASK_STACK_SIZE_8K bool "8k" endchoice config TASK_STACK_SIZE hex default 0x1000 if TASK_STACK_SIZE_4K default 0x2000 if TASK_STACK_SIZE_8K default 0x2000 config TASK_STACK_SHIFT int default 12 if TASK_STACK_SIZE_4K default 13 range 12 14 help the shift size of task stack config STACK_PAGE_ALIGN bool "stack size is 4K align" default n help "the stack for all the task need 4K align" config MAX_SLAB_BLOCKS int "default SLAB blocks" default 10 config TASK_RUN_TIME int "default task run time in ms" default 100 config MINOS_IRQWORK_IRQ int "default irq_work IRQ number" default 5 config SMP_FUNCTION_CALL_IRQ int "default smp cpu function call irq number" default 6 config MINOS_RESCHED_IRQ int "default resched IRQ number" default 7 config SMP bool "SMP system" help smp system config NR_SGI_IRQS int "SGI irq count for each cpu" default 16 help how many SGI irq for each cpu config NR_PPI_IRQS int "Percpu irq count for each cpu" default 16 help how many percpu irq for each cpu config NR_SPI_IRQS int "SPI irq count for each cpu" default 256 help how many spi irq in system config OS_REALTIME_CORE0 bool "all realtime task will affinity to core 0" default n help if enable this feature, all the realtime task will affinity to cpu0 choice prompt "Printf log level" default PRINT_INFO config PRINT_DEBUG bool "Log Level DEBUG" config PRINT_INFO bool "Log Level INFO" config PRINT_NOTICE bool "Log Level NOTICE" config PRINT_WARN bool "Log Level WARNING" config PRINT_ERROR bool "Log Level ERROR" endchoice choice prompt "Memory allocator" default DEFAULT_MM_ALLOCATOR config DEFAULT_MM_ALLOCATOR bool "default mm allocator support" config SIMPLE_MM_ALLOCATOR bool "simple mm allocator for non-virtualaztion" endchoice config LOG_LEVEL int default 5 if PRINT_DEBUG default 4 if PRINT_INFO default 3 if PRINT_NOTICE default 2 if PRINT_WARN default 1 if PRINT_ERROR default 4 endmenu ================================================ FILE: kernel/core/Makefile ================================================ obj-y += bitmap.o obj-y += bootarg.o obj-y += kmem.o obj-y += calltrace.o obj-y += delay.o obj-y += event.o obj-y += find_bit.o obj-y += flag.o obj-y += hook.o obj-y += hweight.o obj-y += idle.o obj-y += init.o obj-y += irq.o obj-y += mbox.o obj-y += minos.o obj-y += mem.o obj-y += kmem.o obj-y += iomem.o obj-y += mutex.o obj-y += page.o obj-y += percpu.o obj-y += print.o obj-y += queue.o obj-y += ramdisk.o obj-y += sched.o obj-y += sem.o obj-y += slab.o obj-y += smp.o obj-y += stdlib.o obj-y += string.o obj-y += task.o obj-y += timer.o obj-y += host_vspace.o ================================================ FILE: kernel/core/bitmap.c ================================================ /* * lib/bitmap.c * Helper functions for bitmap.h. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * This source code is licensed under the GNU General Public License, * Version 2. See the file COPYING for more details. */ #include #include #include #include #include #define __ALIGN_MASK(x, mask) (((x) + (mask)) & ~(mask)) int8_t const ffs_one_table[256] = { -1, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x00 to 0x0F */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x10 to 0x1F */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x20 to 0x2F */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x30 to 0x3F */ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x40 to 0x4F */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x50 to 0x5F */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x60 to 0x6F */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x70 to 0x7F */ 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x80 to 0x8F */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x90 to 0x9F */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xA0 to 0xAF */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xB0 to 0xBF */ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xC0 to 0xCF */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xD0 to 0xDF */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xE0 to 0xEF */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 /* 0xF0 to 0xFF */ }; /* * bitmaps provide an array of bits, implemented using an an * array of unsigned longs. The number of valid bits in a * given bitmap does _not_ need to be an exact multiple of * BITS_PER_LONG. * * The possible unused bits in the last, partially used word * of a bitmap are 'don't care'. The implementation makes * no particular effort to keep them zero. It ensures that * their value will not affect the results of any operation. * The bitmap operations that return Boolean (bitmap_empty, * for example) or scalar (bitmap_weight, for example) results * carefully filter out these unused bits from impacting their * results. * * These operations actually hold to a slightly stronger rule: * if you don't input any bitmaps to these ops that have some * unused bits set, then they won't output any set unused bits * in output bitmaps. * * The byte ordering of bitmaps is more natural on little * endian architectures. See the big-endian headers * include/asm-ppc64/bitops.h and include/asm-s390/bitops.h * for the best explanations of this ordering. */ int __bitmap_weight(const unsigned long *bitmap, unsigned int bits) { unsigned int k, lim = bits/BITS_PER_LONG; int w = 0; for (k = 0; k < lim; k++) w += hweight_long(bitmap[k]); if (bits % BITS_PER_LONG) w += hweight_long(bitmap[k] & BITMAP_LAST_WORD_MASK(bits)); return w; } void bitmap_set(unsigned long *map, unsigned int start, int len) { unsigned long *p = map + BIT_WORD(start); const unsigned int size = start + len; int bits_to_set = BITS_PER_LONG - (start % BITS_PER_LONG); unsigned long mask_to_set = BITMAP_FIRST_WORD_MASK(start); while (len - bits_to_set >= 0) { *p |= mask_to_set; len -= bits_to_set; bits_to_set = BITS_PER_LONG; mask_to_set = ~0UL; p++; } if (len) { mask_to_set &= BITMAP_LAST_WORD_MASK(size); *p |= mask_to_set; } } void bitmap_clear(unsigned long *map, unsigned int start, int len) { unsigned long *p = map + BIT_WORD(start); const unsigned int size = start + len; int bits_to_clear = BITS_PER_LONG - (start % BITS_PER_LONG); unsigned long mask_to_clear = BITMAP_FIRST_WORD_MASK(start); while (len - bits_to_clear >= 0) { *p &= ~mask_to_clear; len -= bits_to_clear; bits_to_clear = BITS_PER_LONG; mask_to_clear = ~0UL; p++; } if (len) { mask_to_clear &= BITMAP_LAST_WORD_MASK(size); *p &= ~mask_to_clear; } } /** * bitmap_find_next_zero_area_off - find a contiguous aligned zero area * @map: The address to base the search on * @size: The bitmap size in bits * @start: The bitnumber to start searching at * @nr: The number of zeroed bits we're looking for * @align_mask: Alignment mask for zero area * @align_offset: Alignment offset for zero area. * * The @align_mask should be one less than a power of 2; the effect is that * the bit offset of all zero areas this function finds plus @align_offset * is multiple of that power of 2. */ unsigned long bitmap_find_next_zero_area_off(unsigned long *map, unsigned long size, unsigned long start, unsigned int nr, unsigned long align_mask, unsigned long align_offset) { unsigned long index, end, i; again: index = find_next_zero_bit(map, size, start); /* Align allocation */ index = __ALIGN_MASK(index + align_offset, align_mask) - align_offset; end = index + nr; if (end > size) return end; i = find_next_bit(map, end, index); if (i < end) { start = i + 1; goto again; } return index; } unsigned long bitmap_find_next_zero_area_align(unsigned long *map, unsigned long size, unsigned long start, unsigned int nr, unsigned long align) { unsigned long index, end, i; again: index = find_next_zero_bit(map, size, start); end = index + nr; if (end > size) return end; if ((index & ((align - 1)))) { start = index + 1; goto again; } i = find_next_bit(map, end, index); if (i < end) { start = i + 1; goto again; } return index; } ================================================ FILE: kernel/core/bootarg.c ================================================ /* * Copyright (C) 2020 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include struct boot_option { char *name; char *args; char *sub_args; struct boot_option *next; }; #define CMDLINE_SIZE 511 static char cmdline[CMDLINE_SIZE + 1]; static struct boot_option *boot_options; int __get_boot_option(char *name, void *value, int (*parse)(char *args, void *value)) { struct boot_option *bo; for (bo = boot_options; bo != NULL; bo = bo->next) { if (strcmp(name, bo->name) != 0) continue; return parse(bo->args, value); } return -ENOENT; } static int __parse_hex32(char *args, void *value) { if (!args) return -EINVAL; *(uint32_t *)value = strtoul(args, NULL, 16); return 0; } static int __parse_hex64(char *args, void *value) { if (!args) return -EINVAL; *(uint64_t *)value = strtoul(args, NULL, 16); return 0; } static int __parse_uint(char *args, void *value) { if (!args) return -EINVAL; *(uint32_t *)value = strtoul(args, NULL, 10); return 0; } static int __parse_bool(char *args, void *value) { *(int*)value = 1; return 0; } static int __parse_string(char *args, void *value) { if (!args) return -EINVAL; *(char **)value = args; return 0; } int bootarg_parse_hex32(char *name, uint32_t *v) { return __get_boot_option(name, v, __parse_hex32); } int bootarg_parse_hex64(char *name, uint64_t *v) { return __get_boot_option(name, v, __parse_hex64); } int bootarg_parse_uint(char *name, uint32_t *v) { return __get_boot_option(name, v, __parse_uint); } int bootarg_parse_bool(char *name, int *v) { *v = 0; return __get_boot_option(name, v, __parse_bool); } int bootarg_parse_string(char *name, char **v) { return __get_boot_option(name, v, __parse_string); } static void bootarg_init_one(char *str) { struct boot_option *bo; if ((*str == 0) || (*str == ' ')) return; bo = malloc(sizeof(struct boot_option)); if (!bo) panic("no more boot memory for boot argument\n"); memset(bo, 0, sizeof(struct boot_option)); bo->name = strsep(&str, "="); bo->args = str; bo->next = boot_options; boot_options = bo; } int __init_text bootargs_init(const char *str, int len) { char *bootarg; char *tmp = &cmdline[0]; pr_notice("bootargs: %s\n", str); if (len > (CMDLINE_SIZE)) pr_warn("cmdline size too long information may lost\n"); len = len > CMDLINE_SIZE ? CMDLINE_SIZE : len; strncpy(cmdline, str, len); cmdline[len] = 0; while ((bootarg = strsep(&tmp, " ")) != NULL) bootarg_init_one(bootarg); return 0; } ================================================ FILE: kernel/core/calltrace.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include static unsigned long *allsyms_address; static unsigned int *allsyms_offset; static int allsyms_count; static char *allsyms_names; extern unsigned char __symbols_start; static DEFINE_SPIN_LOCK(dump_lock); void dump_stack(gp_regs *regs, unsigned long *stack) { unsigned long flags; spin_lock_irqsave(&dump_lock, flags); /* * first dump some percpu data to show the stat * of the cpu such as premeept need_resched etc... */ pr_fatal("preempt:%d need_resche:%d os_running:%d\n", preempt_allowed(), need_resched(), os_is_running()); arch_dump_stack(regs, stack); spin_unlock_irqrestore(&dump_lock, flags); } static void panic_other_cpu(void *data) { pr_fatal("[Panic called by other cpu]\n"); dump_stack(NULL, NULL); for (;;) cpu_relax(); } void __panic(gp_regs *regs, char *fmt, ...) { int cpu; va_list arg; int printed; char buffer[512]; /* * disable local irq panic will directly called * by the code */ local_irq_disable(); va_start(arg, fmt); printed = vsprintf(buffer, fmt, arg); va_end(arg); printed = printed >= 512 ? 511 : printed; buffer[printed + 1] = 0; pr_fatal("[Panic] %s", buffer); dump_stack(regs, NULL); /* inform other cpu to do panic */ for (cpu = 0; cpu < NR_CPUS; cpu++) { if (cpu == smp_processor_id()) continue; smp_function_call(cpu, panic_other_cpu, NULL, 0); } for (;;) cpu_relax(); } static int locate_symbol_pos(unsigned long addr) { int left = 0, right = 1; if (allsyms_count <= 0) return -1; while (1) { if (right == allsyms_count) break;; if (addr == allsyms_address[right]) return right; if ((addr >= allsyms_address[left]) && (addr < allsyms_address[right])) return left; left++; right++; } if ((addr >= allsyms_address[left]) && (addr < (unsigned long)&__symbols_start)) return left; return -1; } void print_symbol(unsigned long addr) { int pos; unsigned long symbol_left; unsigned long symbol_right; unsigned int offset; char *name = NULL; pos = locate_symbol_pos(addr); if (pos == -1) return; symbol_left = allsyms_address[pos]; if (pos == (allsyms_count - 1)) symbol_right = (unsigned long)&__symbols_start; else symbol_right = allsyms_address[pos + 1]; offset = allsyms_offset[pos]; name = allsyms_names + offset; pr_err("[%p] ? %s+0x%x/0x%x\n", addr, name, addr - symbol_left, symbol_right - symbol_left); } int allsymbols_init(void) { int *tmp; unsigned long *symbols_base; symbols_base = (unsigned long *)&__symbols_start; allsyms_address = (unsigned long *)(*symbols_base++); tmp = (int *)(*symbols_base++); allsyms_count = *tmp; allsyms_offset = (unsigned int *)(*symbols_base++); allsyms_names = (char *)(*symbols_base); return 0; } ================================================ FILE: kernel/core/delay.c ================================================ /* * Copyright (C) 2019 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include void udelay(uint32_t us) { unsigned long deadline = get_sys_time() + 1000 * (unsigned long)us; while (get_sys_time() < deadline); dsbsy(); isb(); } void mdelay(uint32_t ms) { unsigned long deadline = get_sys_time(); deadline += 1000000 * (unsigned long)ms; while (get_sys_time() < deadline); dsbsy(); isb(); } void msleep(uint32_t ms) { task_sleep(ms); } ================================================ FILE: kernel/core/event.c ================================================ /* * Copyright (C) 2019 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include static atomic_t event_token = { 1 }; static atomic_t event_token_gen = { 0 }; uint32_t new_event_token(void) { uint32_t value; while (1) { value = (uint32_t)atomic_inc_return_old(&event_token); if (value == 0) atomic_inc(&event_token_gen); else break; } return value; } void event_init(struct event *event, int type, void *pdata) { event->type = type; spin_lock_init(&event->lock); init_list(&event->wait_list); event->data = pdata; event->owner = 0; } void __wait_event(void *ev, int mode, uint32_t to) { struct task *task = current; struct event *event; do_not_preempt(); /* * the process of flag is different with other IPC * method */ if (mode == OS_EVENT_TYPE_FLAG) { task->flag_node = ev; } else { event = (struct event *)ev; list_add_tail(&event->wait_list, &task->event_list); } /* * after event_task_wait, the process will call sched() * by itself, before sched() is called, the task can not * be sched out, since at the same time another thread * may wake up this process, which may case dead lock * with current design. */ task->state = TASK_STATE_WAIT_EVENT; task->pend_state = TASK_STATE_PEND_OK; task->wait_type = mode; task->wait_event = ev; task->delay = (to == -1 ? 0 : to); } static inline void remove_event_waiter(struct event *ev, struct task *task) { unsigned long flags; spin_lock_irqsave(&ev->lock, flags); if (task->event_list.next != NULL) { ASSERT(task->wait_event = (void *)ev); list_del(&task->event_list); task->event_list.next = NULL; } spin_unlock_irqrestore(&ev->lock, flags); } static inline struct task *get_event_waiter(struct event *ev) { struct task *task; if (is_list_empty(&ev->wait_list)) return NULL; task = list_first_entry(&ev->wait_list, struct task, event_list); list_del(&task->event_list); return task; } /* * num - the number need to wake ? <= 0 means, wakeup all. * will return the number of task which have been wake. */ int __wake_up_event_waiter(struct event *ev, long msg, int pend_state, int num) { struct task *task; int ret, cnt = 0; num = (num == 0) ? 1 : num; do { task = get_event_waiter(ev); if (!task) break; ret = __wake_up(task, pend_state, (unsigned long)msg); if (ret) continue; if (++cnt == num) break; } while (1); return cnt; } struct task *wake_up_one_event_waiter(struct event *ev, long msg, int pend_state) { struct task *task; do { task = get_event_waiter(ev); if (!task) break; } while (__wake_up(task, pend_state, (unsigned long)msg)); return task; } void event_pend_down(void) { struct task *task = current; task->pend_state = TASK_STATE_PEND_OK; task->wait_event = NULL; task->wait_type = 0; task->ipcdata = 0; } long __wake(struct event *ev, int pend_state, long retcode) { unsigned long flags; struct task *task; spin_lock_irqsave(&ev->lock, flags); task = wake_up_one_event_waiter(ev, retcode, pend_state); spin_unlock_irqrestore(&ev->lock, flags); return task ? 0 : -ENOENT; } long do_wait_event(struct event *ev) { long ret = 0; sched(); switch (current->pend_state) { case TASK_STATE_PEND_OK: break; case TASK_STATE_PEND_TO: case TASK_STATE_PEND_ABORT: default: remove_event_waiter(ev, current); break; } ret = current->retcode; event_pend_down(); return ret; } ================================================ FILE: kernel/core/find_bit.c ================================================ /* bit search implementation * * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * Copyright (C) 2008 IBM Corporation * 'find_last_bit' is written by Rusty Russell * (Inspired by David Howell's find_next_bit implementation) * * Rewritten by Yury Norov to decrease * size and improve performance, 2015. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #include #include /* * This is a common helper function for find_next_bit and * find_next_zero_bit. The difference is the "invert" argument, which * is XORed with each fetched word before searching it for one bits. */ static unsigned long _find_next_bit(const unsigned long *addr, unsigned long nbits, unsigned long start, unsigned long invert) { unsigned long tmp; if (!nbits || start >= nbits) return nbits; tmp = addr[start / BITS_PER_LONG] ^ invert; /* Handle 1st word. */ tmp &= BITMAP_FIRST_WORD_MASK(start); start = round_down(start, BITS_PER_LONG); while (!tmp) { start += BITS_PER_LONG; if (start >= nbits) return nbits; tmp = addr[start / BITS_PER_LONG] ^ invert; } return min(start + __ffs(tmp), nbits); } /* * Find the next set bit in a memory region. */ unsigned long find_next_bit(const unsigned long *addr, unsigned long size, unsigned long offset) { return _find_next_bit(addr, size, offset, 0UL); } unsigned long find_next_zero_bit(const unsigned long *addr, unsigned long size, unsigned long offset) { return _find_next_bit(addr, size, offset, ~0UL); } unsigned long _find_next_bit_loop(const unsigned long *addr, unsigned long size, unsigned long offset, unsigned long invert) { unsigned long bit; loop: bit = _find_next_bit(addr, size, offset, invert); if (bit >= size) { if (offset != 0) { offset = 0; goto loop; } } return bit; } unsigned long find_next_bit_loop(const unsigned long *addr, unsigned long size, unsigned long offset) { return _find_next_bit_loop(addr, size, offset, 0UL); } unsigned long find_next_zero_bit_loop(const unsigned long *addr, unsigned long size, unsigned long offset) { return _find_next_bit_loop(addr, size, offset, ~0UL); } /* * Find the first set bit in a memory region. */ unsigned long find_first_bit(const unsigned long *addr, unsigned long size) { unsigned long idx; for (idx = 0; idx * BITS_PER_LONG < size; idx++) { if (addr[idx]) return min(idx * BITS_PER_LONG + __ffs(addr[idx]), size); } return size; } /* * Find the first cleared bit in a memory region. */ unsigned long find_first_zero_bit(const unsigned long *addr, unsigned long size) { unsigned long idx; for (idx = 0; idx * BITS_PER_LONG < size; idx++) { if (addr[idx] != ~0UL) return min(idx * BITS_PER_LONG + ffz(addr[idx]), size); } return size; } unsigned long find_last_bit(const unsigned long *addr, unsigned long size) { if (size) { unsigned long val = BITMAP_LAST_WORD_MASK(size); unsigned long idx = (size-1) / BITS_PER_LONG; do { val &= addr[idx]; if (val) return idx * BITS_PER_LONG + __fls(val); val = ~0ul; } while (idx--); } return size; } ================================================ FILE: kernel/core/flag.c ================================================ /* * Copyright (C) 2019 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include struct flag_node { struct list_head list; struct task *task; void *flag_grp; flag_t flags; int wait_type; }; static inline flag_t flag_wait_set_all(struct flag_grp *grp, flag_t flags, int consume) { flag_t flags_rdy; flags_rdy = grp->flags & flags; if (flags_rdy == flags) { if (consume) grp->flags &= ~flags_rdy; } else flags_rdy = 0; return flags_rdy; } static inline flag_t flag_wait_set_any(struct flag_grp *grp, flag_t flags, int consume) { flag_t flags_rdy; flags_rdy = grp->flags & flags; if ((flags_rdy != 0) && consume) grp->flags &= ~flags_rdy; return flags_rdy; } static inline flag_t flag_wait_clr_all(struct flag_grp *grp, flag_t flags, int consume) { flag_t flags_rdy; flags_rdy = ~grp->flags & flags; if (flags_rdy == flags) { if (consume) grp->flags |= flags_rdy; } else flags_rdy = 0; return flags_rdy; } static inline flag_t flag_wait_clr_any(struct flag_grp *grp, flag_t flags, int consume) { flag_t flags_rdy; flags_rdy = ~grp->flags & flags; if ((flags_rdy != 0) && consume) grp->flags |= flags_rdy; return flags_rdy; } flag_t flag_accept(struct flag_grp *grp, flag_t flags, int wait_type) { unsigned long irq; flag_t flags_rdy; int result; int consume; result = wait_type & FLAG_CONSUME; if (result != 0) { wait_type &= ~FLAG_CONSUME; consume = 1; } else consume = 0; spin_lock_irqsave(&grp->lock, irq); switch (wait_type) { case FLAG_WAIT_SET_ALL: flags_rdy = flag_wait_set_all(grp, flags, consume); break; case FLAG_WAIT_SET_ANY: flags_rdy = flag_wait_set_any(grp, flags, consume); break; case FLAG_WAIT_CLR_ALL: flags_rdy = flag_wait_clr_all(grp, flags, consume); break; case FLAG_WAIT_CLR_ANY: flags_rdy = flag_wait_clr_any(grp, flags, consume); break; default: flags_rdy = 0; break; } spin_unlock_irqrestore(&grp->lock, irq); return flags_rdy; } static void flag_task_ready(struct flag_node *node, flag_t flags) { struct task *task = node->task; __wake_up(task, TASK_STATE_PEND_OK, (unsigned long)flags); } static void flag_block(struct flag_grp *grp, struct flag_node *pnode, flag_t flags, int wait_type, uint32_t timeout) { struct task *task = get_current_task(); memset(pnode, 0, sizeof(struct flag_node)); pnode->flags = flags; pnode->wait_type = wait_type; pnode->task = task; pnode->flag_grp = grp; list_add_tail(&grp->wait_list, &pnode->list); __wait_event(pnode, OS_EVENT_TYPE_FLAG, timeout); } flag_t flag_pend(struct flag_grp *grp, flag_t flags, int wait_type, uint32_t timeout) { unsigned long irq; struct flag_node node; flag_t flags_rdy = 0; int result, consume; struct task *task = get_current_task(); might_sleep(); result = wait_type & FLAG_CONSUME; if (result) { wait_type &= ~FLAG_CONSUME; consume = 1; } else { consume = 0; } spin_lock_irqsave(&grp->lock, irq); /* * check the related flags is set or clear, if the * condition is matched, then return. if the type is * not support, the task will wait forever */ switch (wait_type) { case FLAG_WAIT_SET_ALL: flags_rdy = flag_wait_set_all(grp, flags, consume); break; case FLAG_WAIT_SET_ANY: flags_rdy = flag_wait_set_any(grp, flags, consume); break; case FLAG_WAIT_CLR_ALL: flags_rdy = flag_wait_clr_all(grp, flags, consume); break; case FLAG_WAIT_CLR_ANY: flags_rdy = flag_wait_clr_any(grp, flags, consume); break; default: flags_rdy = 0; break; } if (flags_rdy) { spin_unlock_irqrestore(&grp->lock, irq); return flags_rdy; } /* * if the condition does not matched, then the task * will suspend to wait the requested flags */ flag_block(grp, &node, flags, wait_type, timeout); spin_unlock_irqrestore(&grp->lock, irq); sched(); spin_lock_irqsave(&grp->lock, irq); /* * wait timeout or the releated event happened */ if (task->pend_state != TASK_STATE_PEND_OK) { task->pend_state = TASK_STATE_PEND_OK; list_del(&node.list); flags_rdy = 0; } else { flags_rdy = task->flags_rdy; if (consume) { switch (wait_type) { case FLAG_WAIT_SET_ALL: case FLAG_WAIT_SET_ANY: grp->flags &= ~flags_rdy; break; case FLAG_WAIT_CLR_ALL: case FLAG_WAIT_CLR_ANY: grp->flags |= flags_rdy; break; default: flags_rdy = 0; break; } } } spin_unlock_irqrestore(&grp->lock, irq); return flags_rdy; } flag_t flag_pend_get_flags_ready(void) { return current->flags_rdy; } flag_t flag_post(struct flag_grp *grp, flag_t flags, int opt) { flag_t flags_rdy; unsigned long irq; struct flag_node *pnode, *n; if (opt > FLAG_SET) return -EINVAL; spin_lock_irqsave(&grp->lock, irq); switch (opt) { case FLAG_CLR: grp->flags &= ~flags; break; case FLAG_SET: grp->flags |= flags; break; } list_for_each_entry_safe(pnode, n, &grp->wait_list, list) { switch (pnode->wait_type) { case FLAG_WAIT_SET_ALL: flags_rdy = grp->flags & pnode->flags; if (flags_rdy == pnode->flags) flag_task_ready(pnode, flags_rdy); break; case FLAG_WAIT_SET_ANY: flags_rdy = grp->flags & pnode->flags; if (flags_rdy != 0) flag_task_ready(pnode, flags_rdy); break; case FLAG_WAIT_CLR_ALL: flags_rdy = ~grp->flags & pnode->flags; if (flags_rdy == pnode->flags) flag_task_ready(pnode, flags_rdy); break; case FLAG_WAIT_CLR_ANY: flags_rdy = ~grp->flags & pnode->flags; if (flags_rdy != 0) flag_task_ready(pnode, flags_rdy); default: spin_unlock_irqrestore(&grp->lock, irq); return 0; } } spin_unlock_irqrestore(&grp->lock, irq); cond_resched(); return grp->flags; } ================================================ FILE: kernel/core/hook.c ================================================ /* * Copyright (C) 2019 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include static struct list_head hook_lists[OS_HOOK_TYPE_UNKNOWN]; static int hooks_init(void) { int i; for (i = 0; i < OS_HOOK_TYPE_UNKNOWN; i++) init_list(&hook_lists[i]); return 0; } early_initcall(hooks_init); int register_hook(hook_func_t fn, enum hook_type type) { struct hook *hook; if ((fn == NULL) || (type >= OS_HOOK_TYPE_UNKNOWN)) { pr_err("Hook info is invaild\n"); return -EINVAL; } hook = malloc(sizeof(*hook)); if (!hook) return -ENOMEM; memset(hook, 0, sizeof(*hook)); hook->fn = fn; list_add_tail(&hook_lists[type], &hook->list); return 0; } int do_hooks(void *item, void *context, enum hook_type type) { int err = 0; struct hook *hook; list_for_each_entry(hook, &hook_lists[type], list) err += hook->fn(item, context); return err; } ================================================ FILE: kernel/core/host_vspace.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include static struct vspace host_vspace; int create_host_mapping(unsigned long vir, unsigned long phy, size_t size, unsigned long flags) { int ret; if (!IS_PAGE_ALIGN(vir) || !IS_PAGE_ALIGN(phy) || !IS_PAGE_ALIGN(size)) return -EINVAL; spin_lock(&host_vspace.lock); ret = arch_host_map(&host_vspace, __va(vir), __va(vir + size), phy, flags | VM_HOST | VM_HUGE); spin_unlock(&host_vspace.lock); return ret; } int destroy_host_mapping(unsigned long vir, size_t size) { int ret; if (!IS_PAGE_ALIGN(vir) || !IS_PAGE_ALIGN(size)) return -EINVAL; spin_lock(&host_vspace.lock); ret = arch_host_unmap(&host_vspace, __va(vir), __va(vir + size), 0); spin_unlock(&host_vspace.lock); return ret; } int change_host_mapping(unsigned long vir, unsigned long phy, unsigned long new_flags) { int ret; spin_lock(&host_vspace.lock); ret = arch_host_change_map(&host_vspace, __va(vir), phy, new_flags | VM_HOST); spin_unlock(&host_vspace.lock); return ret; } unsigned long translate_va_to_pa(struct vspace *vs, unsigned long va) { unsigned long addr; spin_lock(&vs->lock); addr = (unsigned long)arch_translate_va_to_pa(vs, va); spin_unlock(&vs->lock); return addr; } void *io_remap(virt_addr_t vir, size_t size) { size_t new_size; unsigned long start, end; end = PAGE_BALIGN(vir + size); start = PAGE_ALIGN(vir); new_size = end - vir; if (!create_host_mapping(start, vir, new_size, VM_IO | VM_RW)) return (void *)ptov(vir); return NULL; } int io_unmap(virt_addr_t vir, size_t size) { unsigned long start, end; int ret; vir = __va(vir); start = PAGE_ALIGN(vir); end = PAGE_BALIGN(vir + size); spin_lock(&host_vspace.lock); ret = arch_host_unmap(&host_vspace, start, end, 0); spin_unlock(&host_vspace.lock); return ret; } void release_vspace_pages(struct vspace *vs) { struct page *page = vs->release_pages; struct page *tmp; while (page) { tmp = page->next; __free_pages(page); page = tmp; } vs->release_pages = NULL; } static void host_unmap_range(struct vspace *vs, unsigned long start, unsigned long end, int flags) { release_vspace_pages(vs); } static struct mm_notifier_ops host_mm_notifer_ops = { .unmap_range = host_unmap_range, }; int kernel_vspace_init(void) { struct vspace *vs = &host_vspace; /* * init the host memory struct, the host will * use va->pa mapping, but the mmio address will * allocated a virtual range dynamicly */ spin_lock_init(&vs->lock); vs->pgdp = (pgd_t *)arch_kernel_pgd_base(); vs->notifier_ops = &host_mm_notifer_ops; return 0; } ================================================ FILE: kernel/core/hweight.c ================================================ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include /** * hweightN - returns the hamming weight of a N-bit word * @x: the word to weigh * * The Hamming Weight of a number is the total number of bits set in it. */ unsigned int sw_hweight32(unsigned int w) { #ifdef CONFIG_ARCH_HAS_FAST_MULTIPLIER w -= (w >> 1) & 0x55555555; w = (w & 0x33333333) + ((w >> 2) & 0x33333333); w = (w + (w >> 4)) & 0x0f0f0f0f; return (w * 0x01010101) >> 24; #else unsigned int res = w - ((w >> 1) & 0x55555555); res = (res & 0x33333333) + ((res >> 2) & 0x33333333); res = (res + (res >> 4)) & 0x0F0F0F0F; res = res + (res >> 8); return (res + (res >> 16)) & 0x000000FF; #endif } unsigned int sw_hweight16(unsigned int w) { unsigned int res = w - ((w >> 1) & 0x5555); res = (res & 0x3333) + ((res >> 2) & 0x3333); res = (res + (res >> 4)) & 0x0F0F; return (res + (res >> 8)) & 0x00FF; } unsigned int sw_hweight8(unsigned int w) { unsigned int res = w - ((w >> 1) & 0x55); res = (res & 0x33) + ((res >> 2) & 0x33); return (res + (res >> 4)) & 0x0F; } unsigned long sw_hweight64(__u64 w) { #if BITS_PER_LONG == 32 return __sw_hweight32((unsigned int)(w >> 32)) + __sw_hweight32((unsigned int)w); #elif BITS_PER_LONG == 64 #ifdef CONFIG_ARCH_HAS_FAST_MULTIPLIER w -= (w >> 1) & 0x5555555555555555ul; w = (w & 0x3333333333333333ul) + ((w >> 2) & 0x3333333333333333ul); w = (w + (w >> 4)) & 0x0f0f0f0f0f0f0f0ful; return (w * 0x0101010101010101ul) >> 56; #else __u64 res = w - ((w >> 1) & 0x5555555555555555ul); res = (res & 0x3333333333333333ul) + ((res >> 2) & 0x3333333333333333ul); res = (res + (res >> 4)) & 0x0F0F0F0F0F0F0F0Ful; res = res + (res >> 8); res = res + (res >> 16); return (res + (res >> 32)) & 0x00000000000000FFul; #endif #endif } ================================================ FILE: kernel/core/idle.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_VIRT extern void start_all_vm(void); #endif void system_reboot(void) { if (platform->system_reboot) platform->system_reboot(0, NULL); panic("can not reboot system now\n"); } void system_shutdown(void) { if (platform->system_shutdown) platform->system_shutdown(); panic("cant not shutdown system now\n"); } int system_suspend(void) { if (platform->system_suspend) platform->system_suspend(); wfi(); return 0; } static inline bool pcpu_can_idle(struct pcpu *pcpu) { return (pcpu->local_rdy_grp == (1 << OS_PRIO_IDLE)) && (is_list_empty(&pcpu->stop_list)); } static void pcpu_release_task(struct pcpu *pcpu) { unsigned long flags; struct task *task; local_irq_save(flags); while (!is_list_empty(&pcpu->stop_list)) { task = list_first_entry(&pcpu->stop_list, struct task, state_list); list_del(&task->state_list); do_release_task(task); } local_irq_restore(flags); } static int kworker_task(void *data) { struct pcpu *pcpu = get_pcpu(); flag_t flag; pcpu->kworker = current; flag_init(&pcpu->kworker_flag, 0); for (;;) { flag = flag_pend(&pcpu->kworker_flag, KWORKER_FLAG_MASK, FLAG_WAIT_SET_ANY | FLAG_CONSUME, 0); if (flag == 0) { pr_err("kworker: no event trigger\n"); continue; } if (flag & KWORKER_TASK_RECYCLE) pcpu_release_task(pcpu); } return 0; } static int __init_task(void *main) { #ifdef CONFIG_VIRT printf("\n\nStarting all VMs\n\n"); start_all_vm(); #else printf("\n\nHello Minos\n\n"); #endif return 0; } weak_alias(__init_task, init_task); static void start_system_task(void) { extern int load_root_service(void); int cpu = smp_processor_id(); struct task *task; char name[32]; pr_notice("create kworker task...\n"); sprintf(name, "kworker/%d", cpu); task = create_kthread(name, kworker_task, OS_PRIO_DEFAULT_1, cpu, 0, NULL); ASSERT(task != NULL); if (cpu == 0) { pr_notice("Load Root Service ...\n"); init_task(NULL); } } void cpu_idle(void) { struct pcpu *pcpu = get_pcpu(); start_system_task(); set_os_running(); local_irq_enable(); pcpu_irqwork(pcpu->pcpu_id); while (1) { sched(); /* * need to check whether the pcpu can go to idle * state to avoid the interrupt happend before wfi */ while (!need_resched() && pcpu_can_idle(pcpu)) { local_irq_disable(); if (pcpu_can_idle(pcpu)) { pcpu->state = PCPU_STATE_IDLE; wfi(); nop(); pcpu->state = PCPU_STATE_RUNNING; } local_irq_enable(); } } } ================================================ FILE: kernel/core/init.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include extern unsigned char __init_func_0_start; extern unsigned char __init_func_1_start; extern unsigned char __init_func_2_start; extern unsigned char __init_func_3_start; extern unsigned char __init_func_4_start; extern unsigned char __init_func_5_start; extern unsigned char __init_func_6_start; extern unsigned char __init_func_7_start; extern unsigned char __init_func_8_start; extern unsigned char __init_func_9_start; extern unsigned char __init_func_end; extern void log_init(void); static void call_init_func(unsigned long fn_start, unsigned long fn_end) { init_call *fn; int size, i; size = (fn_end - fn_start) / sizeof(init_call); pr_debug("call init func : 0x%x 0x%x %d\n", fn_start, fn_end, size); if (size <= 0) return; fn = (init_call *)fn_start; for (i = 0; i < size; i++) { (*fn)(); fn++; } } void early_init(void) { /* get the platform for the minos */ arch_early_init(); call_init_func((unsigned long)&__init_func_0_start, (unsigned long)&__init_func_1_start); } void arch_init(void) { __arch_init(); call_init_func((unsigned long)&__init_func_1_start, (unsigned long)&__init_func_2_start); } void subsys_init(void) { call_init_func((unsigned long)&__init_func_2_start, (unsigned long)&__init_func_3_start); } void module_init(void) { call_init_func((unsigned long)&__init_func_3_start, (unsigned long)&__init_func_4_start); } void device_init(void) { call_init_func((unsigned long)&__init_func_4_start, (unsigned long)&__init_func_5_start); } void early_init_percpu(void) { call_init_func((unsigned long)&__init_func_5_start, (unsigned long)&__init_func_6_start); } void arch_init_percpu(void) { call_init_func((unsigned long)&__init_func_6_start, (unsigned long)&__init_func_7_start); } void subsys_init_percpu(void) { call_init_func((unsigned long)&__init_func_7_start, (unsigned long)&__init_func_8_start); } void module_init_percpu(void) { call_init_func((unsigned long)&__init_func_8_start, (unsigned long)&__init_func_9_start); } void device_init_percpu(void) { call_init_func((unsigned long)&__init_func_9_start, (unsigned long)&__init_func_end); } ================================================ FILE: kernel/core/iomem.c ================================================ /* * Copyright (C) 2021 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include struct io_block { void *iobase; int free; struct io_block *next; unsigned long bitmap[BITS_TO_LONGS(PAGES_PER_BLOCK)]; }; static struct io_block *io_block_head; static DEFINE_SPIN_LOCK(iob_lock); static void *get_io_pages_from_block(struct io_block *iob, int pages) { int start; start = bitmap_find_next_zero_area(iob->bitmap, PAGES_PER_BLOCK, 0, pages, 0); if (start >= PAGES_PER_BLOCK) return NULL; bitmap_set(iob->bitmap, start, pages); return (iob->iobase + (start << PAGE_SHIFT)); } static void *alloc_new_io_block(int pages) { void *base; struct io_block *iob; base = get_free_block(GFP_HUGE_IO); if (!base) return NULL; iob = malloc(sizeof(struct io_block)); if (!iob) { free(base); return NULL; } memset(iob, 0, sizeof(struct io_block)); iob->iobase = base; iob->free = PAGES_PER_BLOCK; base = get_io_pages_from_block(iob, pages); ASSERT(base != NULL); /* * add new io block to the global list. */ iob->next = io_block_head; io_block_head = iob; return base; } void *get_io_pages(int pages) { struct io_block *bhead = io_block_head; void *base = NULL; if ((pages == 0) || (pages > PAGES_PER_BLOCK)) { pr_err("io pages can not beyond %d\n", PAGES_PER_BLOCK); return NULL; } spin_lock(&iob_lock); while (bhead) { if (bhead->free < pages) continue; base = get_io_pages_from_block(bhead, pages); if (base) break; } if (!base) base = alloc_new_io_block(pages); spin_unlock(&iob_lock); return base; } void free_io_pages(void *addr) { } ================================================ FILE: kernel/core/irq.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include unsigned long cpu_irq_stack[NR_CPUS]; static struct irq_chip *irq_chip; static int default_irq_handler(uint32_t irq, void *data); static struct irq_desc percpu_irq_descs[PERCPU_IRQ_DESC_SIZE] = { [0 ... (PERCPU_IRQ_DESC_SIZE - 1)] = { default_irq_handler, }, }; static struct irq_desc spi_irq_descs[SPI_IRQ_DESC_SIZE] = { [0 ... (SPI_IRQ_DESC_SIZE - 1)] = { default_irq_handler, }, }; void send_sgi(uint32_t sgi, int cpu) { cpumask_t mask; if ((cpu < 0) || (cpu >= CONFIG_NR_CPUS)) return; if (sgi >= 16) return; cpumask_clearall(&mask); cpumask_set_cpu(cpu, &mask); irq_chip->send_sgi(sgi, SGI_TO_LIST, &mask); } static int default_irq_handler(uint32_t irq, void *data) { pr_warn("irq %d is not register\n", irq); return 0; } static int do_handle_host_irq(int cpuid, struct irq_desc *irq_desc) { int ret; if (cpuid != irq_desc->affinity) { pr_notice("irq %d do not belong to this cpu\n", irq_desc->hno); ret = -EINVAL; goto out; } ret = irq_desc->handler(irq_desc->hno, irq_desc->pdata); irq_chip->irq_eoi(irq_desc->hno); out: /* * 1: if the hw irq is to vcpu do not DIR it. * 2: if the hw irq is to vcpu but failed to send then DIR it. * 3: if the hw irq is to userspace process, do not DIR it. */ if (ret || !(irq_desc->flags & IRQ_FLAGS_VCPU)) irq_chip->irq_dir(irq_desc->hno); return ret; } static inline struct irq_desc *get_irq_desc_cpu(int cpuid, uint32_t irq) { if (irq >= MAX_IRQ_COUNT) return NULL; if (irq < SPI_IRQ_BASE) return &percpu_irq_descs[cpuid * NR_PERCPU_IRQS + irq]; return &spi_irq_descs[irq - SPI_IRQ_BASE]; } /* * notice, when used this function to get the percpu * irqs need to lock the kernel to invoid the thread * sched out from this cpu and running on another cpu * * so usually, percpu irq will handle in kernel contex * and not in task context */ struct irq_desc *get_irq_desc(uint32_t irq) { return get_irq_desc_cpu(smp_processor_id(), irq); } void __irq_enable(uint32_t irq, int enable) { struct irq_desc *irq_desc; unsigned long flags; irq_desc = get_irq_desc(irq); if (!irq_desc) return; /* * some irq controller will directly call its * own function to enable or disable the hw irq * which do not set the bit, so here force to excute * the action */ spin_lock_irqsave(&irq_desc->lock, flags); if (enable) { irq_chip->irq_unmask(irq); irq_desc->flags &= ~IRQ_FLAGS_MASKED; } else { irq_chip->irq_mask(irq); irq_desc->flags |= IRQ_FLAGS_MASKED; } spin_unlock_irqrestore(&irq_desc->lock, flags); } void irq_dir(uint32_t irq) { irq_chip->irq_dir(irq); } void irq_clear_pending(uint32_t irq) { if (irq_chip->irq_clear_pending) irq_chip->irq_clear_pending(irq); } void irq_set_affinity(uint32_t irq, int cpu) { struct irq_desc *irq_desc; if (cpu >= NR_CPUS) return; /* update the hw irq affinity */ irq_desc = get_irq_desc(irq); if (!irq_desc) return; spin_lock(&irq_desc->lock); irq_desc->affinity = cpu; if (irq_chip->irq_set_affinity) irq_chip->irq_set_affinity(irq, cpu); spin_unlock(&irq_desc->lock); } void irq_set_type(uint32_t irq, int type) { struct irq_desc *irq_desc; irq_desc = get_irq_desc(irq); if (!irq_desc) return; spin_lock(&irq_desc->lock); if (type == (irq_desc->flags & IRQ_FLAGS_TYPE_MASK)) goto out; if (irq_chip->irq_set_type) irq_chip->irq_set_type(irq, type); irq_desc->flags &= ~IRQ_FLAGS_TYPE_MASK; irq_desc->flags |= type; out: spin_unlock(&irq_desc->lock); } int do_irq_handler(void) { uint32_t irq; struct irq_desc *irq_desc; int cpuid = smp_processor_id(); while (1) { irq = irq_chip->get_pending_irq(); if (irq >= BAD_IRQ) return 0; irq_desc = get_irq_desc_cpu(cpuid, irq); if (unlikely(!irq_desc)) { pr_err("irq is not actived %d\n", irq); irq_chip->irq_eoi(irq); irq_chip->irq_dir(irq); continue; } do_handle_host_irq(cpuid, irq_desc); } return 0; } int irq_xlate(struct device_node *node, uint32_t *intspec, unsigned int intsize, uint32_t *hwirq, unsigned long *f) { if (irq_chip && irq_chip->irq_xlate) return irq_chip->irq_xlate(node, intspec, intsize, hwirq, f); else pr_warn("WARN - no xlate function for the irqchip\n"); return -ENOENT; } int request_irq_percpu(uint32_t irq, irq_handle_t handler, unsigned long flags, char *name, void *data) { int i; struct irq_desc *irq_desc; unsigned long flag; unused(name); if ((irq >= NR_PERCPU_IRQS) || !handler) return -EINVAL; for (i = 0; i < NR_CPUS; i++) { irq_desc = get_irq_desc_cpu(i, irq); if (!irq_desc) continue; spin_lock_irqsave(&irq_desc->lock, flag); irq_desc->handler = handler; irq_desc->pdata = data; irq_desc->flags |= flags; irq_desc->affinity = i; irq_desc->hno = irq; /* enable the irq here */ irq_chip->irq_unmask_cpu(irq, i); irq_desc->flags &= ~IRQ_FLAGS_MASKED; spin_unlock_irqrestore(&irq_desc->lock, flag); } return 0; } int request_irq(uint32_t irq, irq_handle_t handler, unsigned long flags, char *name, void *data) { int type; struct irq_desc *irq_desc; unsigned long flag; unused(name); if (!handler) return -EINVAL; irq_desc = get_irq_desc(irq); if (!irq_desc) return -ENOENT; type = flags & IRQ_FLAGS_TYPE_MASK; flags &= ~IRQ_FLAGS_TYPE_MASK; spin_lock_irqsave(&irq_desc->lock, flag); irq_desc->handler = handler; irq_desc->pdata = data; irq_desc->flags |= flags; irq_desc->hno = irq; /* enable the hw irq and set the mask bit */ irq_chip->irq_unmask(irq); irq_desc->flags &= ~IRQ_FLAGS_MASKED; if (irq < SPI_IRQ_BASE) irq_desc->affinity = smp_processor_id(); spin_unlock_irqrestore(&irq_desc->lock, flag); if (type) irq_set_type(irq, type); return 0; } static void *irqchip_init(struct device_node *node, void *arg) { extern unsigned char __irqchip_start; extern unsigned char __irqchip_end; void *s, *e; struct irq_chip *chip; if (node->class != DT_CLASS_IRQCHIP) return NULL; s = (void *)&__irqchip_start; e = (void *)&__irqchip_end; chip = (struct irq_chip *)of_device_node_match(node, s, e); if (!chip) return NULL; irq_chip = chip; if (chip->init) chip->init(node); return node; } int irq_init(void) { #ifdef CONFIG_DEVICE_TREE of_iterate_all_node(of_root_node, irqchip_init, NULL); #endif if (!irq_chip) panic("can not find the irqchip for system\n"); /* * now init the irqchip, and in the irq chip * the chip driver need to alloc the irq it * need used in the ssystem */ if (!irq_chip->get_pending_irq) panic("No function to get irq nr\n"); return 0; } int irq_secondary_init(void) { if (irq_chip) irq_chip->secondary_init(); return 0; } ================================================ FILE: kernel/core/kmem.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include struct kmem_section { void *pbase; void *vbase; size_t size; void *free_base; void *free_page_base; size_t free_size; }; extern void add_kernel_page_section(phy_addr_t base, size_t size, int type); static struct kmem_section kmem_section; static struct kmem_section *ks; static inline void *alloc_kpages_from_section(int pages) { size_t request_size = pages << PAGE_SHIFT; void *base = NULL; ks->free_page_base -= request_size; base = ks->free_page_base; ks->free_size -= request_size; return base; } void *alloc_kpages(int pages) { ASSERT(pages >= 0); return alloc_kpages_from_section(pages); } static inline void *alloc_kmem_from_section(size_t size) { void *base = base; ASSERT(ks->free_size >= size); base = ks->free_base; ks->free_base += size; ks->free_size -= size; return base; } /* * kmem will not be freed once it has been allocated * kmem also can alloc small slab and one page, and * kmem will only used at the boot stage when the irq * and scheduler is disabled so there is no spin lock needed * when alloc kmem */ void *alloc_kmem(size_t size) { size_t request_size = BALIGN(size, sizeof(unsigned long)); return alloc_kmem_from_section(request_size); } void *zalloc_kmem(size_t size) { void *base; ASSERT(size != 0); size = BALIGN(size, sizeof(unsigned long)); base = alloc_kmem(size); if (base) memset(base, 0, size); return base; } void add_kmem_section(struct memory_region *region) { if (ks != NULL) { pr_err("mutiple kernel memory section ?\n"); return; } /* * set the kernel memory section pointer. */ ks = &kmem_section; ks->pbase = (void *)region->phy_base; ks->vbase = (void *)ptov(ks->pbase); ks->size = region->size; ks->free_page_base = ks->vbase + ks->size; if (region->phy_base == minos_start) { ks->free_base = (void *)ptov(minos_end); ks->free_size = ks->size - (minos_end - minos_start); } else { ks->free_base = ks->vbase; ks->free_size = ks->size; } if (ks->free_page_base == ks->free_base) { pr_warn("skip wrong kmem section [0x%x 0x%x]\n", ks->pbase, ks->pbase + ks->size); return; } ASSERT(ks->free_page_base > ks->free_base); pr_notice("kmem [0x%x 0x%x]\n", ks->free_base, ks->free_base + ks->free_size); } void kmem_init(void) { unsigned long base, size; base = PAGE_BALIGN(ks->free_base); size = (unsigned long)ks->free_page_base - base; add_kernel_page_section(vtop(base), size, 0); } ================================================ FILE: kernel/core/mbox.c ================================================ /* * Copyright (C) 2019 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include void *mbox_accept(mbox_t *m) { unsigned long flags; void *msg = NULL; spin_lock_irqsave(&m->lock, flags); msg = m->data; spin_unlock_irqrestore(&m->lock, flags); return msg; } int mbox_is_pending(mbox_t *m) { return m->data ? 1 : 0; } void *mbox_pend(mbox_t *m, uint32_t timeout) { void *pmsg; unsigned long flags; might_sleep(); spin_lock_irqsave(&m->lock, flags); if (m->data != NULL) { pmsg = m->data; m->data = NULL; spin_unlock_irqrestore(&m->lock, flags); return pmsg; } /* no mbox message need to suspend the task */ __wait_event(TO_EVENT(m), OS_EVENT_TYPE_MBOX, timeout); spin_unlock_irqrestore(&m->lock, flags); return (void *)do_wait_event(TO_EVENT(m)); } static int __mbox_post_opt(mbox_t *m, void *pmsg, int pend_state, int opt) { unsigned long flags; int ret = 0; if (!pmsg) return -EINVAL; /* * check whether the mbox need to broadcast to * all the waitting task */ spin_lock_irqsave(&m->lock, flags); ret = wake_up_event_waiter(TO_EVENT(m), (long)pmsg, pend_state, opt); if (!ret) { if (m->data != NULL) ret = -ENOSPC; else m->data = pmsg; } spin_unlock_irqrestore(&m->lock, flags); if (ret) cond_resched(); return ret; } int mobox_post_abort(mbox_t *m) { return __mbox_post_opt(m, NULL, TASK_STATE_PEND_ABORT, OS_EVENT_OPT_BROADCAST); } int mbox_post(mbox_t *m, void *pmsg) { return __mbox_post_opt(m, pmsg, TASK_STATE_PEND_OK, OS_EVENT_OPT_NONE); } ================================================ FILE: kernel/core/mem.c ================================================ /* * Copyright (C) 2019 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #define MAX_MEMORY_REGION 16 static struct memory_region memory_regions[MAX_MEMORY_REGION]; static int current_region_id; LIST_HEAD(mem_list); struct memory_region *alloc_memory_region(void) { ASSERT(current_region_id < MAX_MEMORY_REGION); return &memory_regions[current_region_id++]; } int add_memory_region(uint64_t base, uint64_t size, int type, int vmid) { phy_addr_t end = base + size - 1; phy_addr_t r_base, r_end; struct memory_region *region; if ((size == 0) || (type >= MEMORY_REGION_TYPE_MAX)) return -EINVAL; /* * need to check whether this region is confilct with * other region */ list_for_each_entry(region, &mem_list, list) { r_base = region->phy_base; r_end = r_base + region->size - 1; if (!((base > r_end) || (end < r_base))) { pr_err("memory region invalid [0x%lx 0x%lx]\n", base, end); return -EINVAL; } } region = alloc_memory_region(); region->phy_base = base; region->size = size; region->type = type; region->vmid = vmid; list_add_tail(&mem_list, ®ion->list); pr_info("ADD MEM: 0x%p [0x%p] 0x%x\n", region->phy_base, region->size, region->type); return 0; } int split_memory_region(uint64_t base, size_t size, int type, int vmid) { phy_addr_t start, end; phy_addr_t new_end = base + size; struct memory_region *region, *n, *tmp; if ((size == 0) || (type >= MEMORY_REGION_TYPE_MAX)) return -EINVAL; pr_info("SPLIT MEM: 0x%p [0x%p] 0x%x\n", base, size, type); /* * delete the memory for host, these region * usually for vms */ list_for_each_entry_safe(region, n, &mem_list, list) { start = region->phy_base; end = start + region->size; if ((base > end) || (base < start) || (new_end > end)) continue; /* just delete this region from the list */ if ((base == start) && (new_end == end)) { region->type = type; return 0; } else if ((base == start) && (new_end < end)) { region->phy_base = new_end; region->size -= size; } else if ((base > start) && (new_end < end)) { /* create a new region for the tail space */ n = alloc_memory_region(); n->phy_base = new_end; n->size = end - new_end; n->type = region->type; n->vmid = region->vmid; list_add_tail(&mem_list, &n->list); region->size = base - start; } else if ((base > start) && (end == new_end)) { region->size = region->size - size; } else { pr_warn("incorrect memory region 0x%x 0x%x\n", base, size); return -EINVAL; } /* alloc a new memory region for vm memory */ tmp = alloc_memory_region(); tmp->phy_base = base; tmp->size = size; tmp->type = type; tmp->vmid = vmid; list_add_tail(&mem_list, &tmp->list); return 0; } panic("Found Invalid memory config 0x%p [0x%p]\n", base, size); return 0; } void dump_memory_info(void) { struct memory_region *region; char vm[8]; char *mem_attr[MEMORY_REGION_TYPE_MAX] = { "Normal", "RSV", "VM", "DTB", "Kernel", "RamDisk", }; list_for_each_entry(region, &mem_list, list) { sprintf(vm, "VM%d", region->vmid); pr_notice("MEM: 0x%p -> 0x%p [0x%p] %s/%s\n", region->phy_base, region->phy_base + region->size, region->size, mem_attr[region->type], region->vmid == 0 ? "Host" : vm); } } static void handle_normal_memory_region(struct memory_region *region) { int ret; /* * only add the normal memory for user, other memory will used as * other purpose. kernel memeory will use alloc_kernel_mem(), once * the kernel memory is allocated, it will never freed. */ ret = add_page_section(region->phy_base, region->size, region->type); ASSERT(ret == 0) } static void map_all_memory(void) { struct memory_region *re; int ret; pr_notice("map all memory to host space\n"); for_each_memory_region(re) { if (re->type != MEMORY_REGION_TYPE_NORMAL) continue; ret = create_host_mapping(ptov(re->phy_base), re->phy_base, re->size, VM_RW | VM_NORMAL | VM_HUGE); if (ret) pr_err("map memory region [0x%lx +0x%lx] failed\n", re->phy_base, re->size); } } static void prepare_memory_region(struct memory_region *re) { size_t left_size, right_size; unsigned long start, end, tmp; tmp = PAGE_ALIGN(re->phy_base + re->size); re->phy_base = PAGE_BALIGN(re->phy_base); re->size = tmp - re->phy_base; if (re->size == 0) return; if (IS_BLOCK_ALIGN(re->phy_base) && IS_BLOCK_ALIGN(re->size)) return; start = re->phy_base; end = re->phy_base + re->size; tmp = BLOCK_ALIGN(end); re->phy_base = BLOCK_BALIGN(re->phy_base); re->size = tmp - re->phy_base; left_size = re->phy_base - start; right_size = end - (re->phy_base + re->size); /* * add these memory to the kernel. */ if (left_size) add_memory_region(start, left_size, MEMORY_REGION_TYPE_NORMAL, 0); if (right_size) add_memory_region(re->phy_base + re->size, right_size, MEMORY_REGION_TYPE_NORMAL, 0); } static inline int in_os_memory_range(unsigned long addr, size_t size) { return IN_RANGE_UNSIGNED(addr, size, minos_start, CONFIG_MINOS_RAM_SIZE); } void mm_init(void) { extern void set_ramdisk_address(void *start, void *end); extern void slab_init(void); extern void kmem_init(void); extern int kernel_vspace_init(void); struct memory_region *re, *tmp; struct memory_region *kre = NULL; size_t kmem_size; kernel_vspace_init(); #ifdef CONFIG_DEVICE_TREE of_parse_memory_info(); #endif /* * if there is no memory information founded then panic * the system. */ BUG_ON(is_list_empty(&mem_list), "no memory information found\n"); minos_end = PAGE_BALIGN(minos_end); kmem_size = CONFIG_MINOS_RAM_SIZE - (minos_end - minos_start); if (kmem_size <= 0) panic("kmem: memory layout is wrong after boot\n"); /* * first handle the kernel memory region, so later, the mapping * function can be used. */ for_each_memory_region(re) { if (re->type != MEMORY_REGION_TYPE_KERNEL) continue; kre = re; if (in_os_memory_range(re->phy_base, re->size)) add_kmem_section(re); else panic("Wrong kernel memory section [0x%x 0x%x]\n", re->phy_base, re->phy_base + re->size); break; } BUG_ON(kre == NULL, "Wrong memory configuration\n") /* * for the normal and dma memory, need to make sure it is BLOCK align * since the page allocater will based BLOCK memory. */ list_for_each_entry_safe(re, tmp, &mem_list, list) { if (re->type == MEMORY_REGION_TYPE_NORMAL) prepare_memory_region(re); } /* * delete the memory region which the size is 0. */ list_for_each_entry_safe(re, tmp, &mem_list, list) { if (re->size == 0) list_del(&re->list); } for_each_memory_region(re) { if (re->type == MEMORY_REGION_TYPE_RAMDISK) { pr_notice("set ramdisk address 0x%lx 0x%lx\n", re->phy_base, re->size); set_ramdisk_address((void *)re->phy_base, (void *)(re->phy_base + re->size)); } else if (re->type == MEMORY_REGION_TYPE_NORMAL) { handle_normal_memory_region(re); } } slab_init(); kmem_init(); dump_memory_info(); map_all_memory(); } ================================================ FILE: kernel/core/minos.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern void cpu_idle(void); extern void mm_init(void); extern int allsymbols_init(void); extern void platform_init(void); extern int create_idle_task(void); #ifdef CONFIG_VIRT #include #endif void boot_main(void) { allsymbols_init(); percpu_init(0); pr_notice("Minos %s\n", MINOS_VERSION_STR); ASSERT(smp_processor_id() == 0); mm_init(); #ifdef CONFIG_DEVICE_TREE of_init_bootargs(); #endif early_init(); early_init_percpu(); arch_init(); arch_init_percpu(); platform_init(); irq_init(); #ifdef CONFIG_SMP smp_init(); #endif subsys_init(); subsys_init_percpu(); module_init(); module_init_percpu(); sched_init(); local_sched_init(); device_init(); device_init_percpu(); create_idle_task(); #ifdef CONFIG_SMP smp_cpus_up(); #endif #ifdef CONFIG_VIRT virt_init(); #endif ramdisk_init(); cpu_idle(); } void boot_secondary(int cpuid) { pr_notice("cpu-%d is up\n", cpuid); /* * need wait for all cpus up then excuted below * task, otherwise the mem content hold by different * cpu may be different because the cache issue * * eg: the cpu1 called create_idle_task and the * idle task is created sucessfully but at the same * time the cpu2 is powered off * * waitting for all the cpu power on */ while (!is_cpus_all_up()) mb(); early_init_percpu(); arch_init_percpu(); irq_secondary_init(); subsys_init_percpu(); module_init_percpu(); local_sched_init(); device_init_percpu(); create_idle_task(); cpu_idle(); } ================================================ FILE: kernel/core/mutex.c ================================================ /* * Copyright (C) 2019 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include int mutex_accept(mutex_t *mutex) { struct task *task = current; int ret = -EBUSY; spin_lock(&mutex->lock); if (mutex->cnt == OS_MUTEX_AVAILABLE) { mutex->owner = task->tid; mutex->data = task; mutex->cnt = task->tid; ret = 0; } spin_unlock(&mutex->lock); return ret; } int mutex_pend(mutex_t *m, uint32_t timeout) { struct task *task = current; might_sleep(); /* * mutex_pend and mutex_post can not be used in interrupt * context. */ spin_lock(&m->lock); if (m->cnt == OS_MUTEX_AVAILABLE) { m->owner = task->tid; m->data = (void *)task; m->cnt = task->tid; spin_unlock(&m->lock); return 0; } __wait_event(TO_EVENT(m), OS_EVENT_TYPE_MUTEX, timeout); spin_unlock(&m->lock); return do_wait_event(TO_EVENT(m)); } int mutex_post(mutex_t *m) { struct task *task; ASSERT(m->owner == current->tid); /* * find the highest prio task to run, if there is * no task, then set the mutex is available else * resched */ spin_lock(&m->lock); task = wake_up_one_event_waiter(TO_EVENT(m), 0, TASK_STATE_PEND_OK); if (task == NULL) { m->cnt = OS_MUTEX_AVAILABLE; m->data = NULL; m->owner = 0; } else { m->owner = task->tid; m->data = (void *)task; m->cnt = task->tid; } spin_unlock(&m->lock); return 0; } ================================================ FILE: kernel/core/page.c ================================================ /* * Copyright (C) 2020 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include extern void *alloc_kmem(size_t size); extern void *zalloc_kmem(size_t size); extern void *alloc_kpages(int pages); #define __PAGE_F_KERNL __GFP_KERNEL #define __PAGE_F_USER __GFP_USER #define __PAGE_F_GUEST __GFP_GUEST #define __PAGE_F_DMA __GFP_DMA #define __PAGE_F_SLAB __GFP_SLAB #define __PAGE_F_SHARED __GFP_SHARED #define __PAGE_F_IO __GFP_IO #define __PAGE_F_HUGE __GFP_HUGE #define PAGE_F_KERNL GFP_KERNEL #define PAGE_F_USER GFP_USER #define PAGE_F_GUEST GFP_GUEST #define PAGE_F_DMA GFP_DMA #define PAGE_F_SLAB GFP_SLAB #define PAGE_F_SHARED GFP_SHARED #define PAGE_F_SHARED_IO GFP_SHARED_IO #define PAGE_F_HUGE GFP_HUGE #define PAGE_F_HUGE_IO GFP_HUGE_IO #define PAGE_F_HEAD 0x00000100 #define PAGE_F_MASK 0x0000ffff #define MAX_MEM_SECTIONS 32 struct mem_section { int block_section; unsigned long phy_base; unsigned long vir_base; unsigned long vir_end; size_t size; size_t total_cnt; size_t free_cnt; unsigned long *bitmap; unsigned long bm_end; unsigned long bm_current; unsigned long *block_bitmap; size_t total_block; int current_block; size_t free_block; spinlock_t lock; struct page *pages; }; #define PAGE_ADDR(base, bit) ((unsigned long)base + ((unsigned long)bit << PAGE_SHIFT)) /* * ID 0 will reserver for kernel memory section. */ static struct mem_section mem_sections[MAX_MEM_SECTIONS]; static int nr_sections = 1; void add_kernel_page_section(phy_addr_t base, size_t size, int type) { unsigned long end, new_size; size_t page_cnt; struct mem_section *ms = &mem_sections[0]; memset(ms, 0, sizeof(struct mem_section)); end = base + size; page_cnt = size >> PAGE_SHIFT; ms->bitmap = (unsigned long *)ptov(base); base += BITMAP_SIZE(page_cnt); memset(ms->bitmap, 0, BITMAP_SIZE(page_cnt)); ms->pages = (struct page *)ptov(base); base += page_cnt * sizeof(struct page); memset(ms->pages, 0, page_cnt * sizeof(struct page)); base = PAGE_BALIGN(base); new_size = end - base; spin_lock_init(&ms->lock); ms->phy_base = base; ms->vir_base = ptov(base); ms->size = new_size; ms->vir_end = ms->vir_base + ms->size; ms->total_cnt = new_size >> PAGE_SHIFT; ms->free_cnt = ms->total_cnt; ms->bm_current = 0; ms->bm_end = ms->total_cnt; pr_notice("boot memory section [0x%lx +0x%lx]\n", base, new_size); } int add_page_section(phy_addr_t base, size_t size, int type) { struct mem_section *ms; int block_align = 1; if ((size == 0) || (nr_sections >= MAX_MEM_SECTIONS)) { pr_err("no enough memory section for page section\n"); return -EINVAL; } if (!IS_BLOCK_ALIGN(base) || !IS_BLOCK_ALIGN(size)) block_align = 0; pr_notice("umem [0x%x 0x%x] [%s] section\n", base, base + size, block_align ? "Block" : "Page"); ms = &mem_sections[nr_sections]; memset(ms, 0, sizeof(struct mem_section)); spin_lock_init(&ms->lock); ms->phy_base = base; ms->vir_base = ptov(base); ms->size = size; ms->vir_end = ms->vir_base + ms->size; /* * init the page informations. */ ms->total_cnt = size >> PAGE_SHIFT; ms->free_cnt = ms->total_cnt; ms->bitmap = alloc_kmem(BITMAP_SIZE(ms->total_cnt)); ASSERT(ms->bitmap != NULL); memset(ms->bitmap, 0, BITMAP_SIZE(ms->total_cnt)); ms->bm_current = 0; ms->bm_end = ms->total_cnt; /* * just allocate the pages struct for this section * but do not init the memory data to 0, since if the * memory size is too big will slow down the boot time */ ms->pages = alloc_kmem(ms->total_cnt * sizeof(struct page)); ASSERT(ms->pages != NULL); memset(ms->pages, 0, ms->total_cnt * sizeof(struct page)); /* * init the block information for this section, so * can get 2M blocks from this section if needed. */ if (block_align) { ms->block_section = 1; ms->total_block = ms->size >> BLOCK_SHIFT; ms->free_block = ms->total_block; ms->block_bitmap = alloc_kmem(BITMAP_SIZE(ms->total_block)); ASSERT(ms->block_bitmap != NULL); memset(ms->block_bitmap, 0, BITMAP_SIZE(ms->total_block)); } nr_sections++; return 0; } static void alloc_pages_in_block(struct page *page, struct mem_section *ms) { unsigned long start = page_pa(page); unsigned long base = ALIGN(start, BLOCK_SIZE); unsigned long end = BALIGN(start + page->cnt * PAGE_SIZE, BLOCK_SIZE); int size = ((end - base) >> PAGE_SHIFT) / PAGES_PER_BLOCK; start = (base - start) >> BLOCK_SHIFT; bitmap_set(ms->block_bitmap, start, size); } static void free_pages_in_block(struct page *page, struct mem_section *ms) { int count = page_count(page); unsigned long tmp, start, end; unsigned long *baddr; int i, sum, bbit, pbit; tmp = page_pa(page); start = ALIGN(tmp, BLOCK_SIZE); end = BALIGN(tmp + count * PAGE_SIZE, BLOCK_SIZE); bbit = (start - ms->phy_base) >> BLOCK_SHIFT; pbit = (start - ms->phy_base) >> PAGE_SHIFT; /* * clear the related bit in the block map if all * the pages in this block has been freed. */ for (i = 0; i < (end - start) >> BLOCK_SHIFT; i++) { /* * get the start page bit postion in page bitmap. * the check whether all the bits are zero. */ baddr = ms->bitmap + BITS_TO_LONGS(pbit); sum = 0; sum += (baddr[0] != 0); sum += (baddr[1] != 0); sum += (baddr[2] != 0); sum += (baddr[3] != 0); sum += (baddr[4] != 0); sum += (baddr[5] != 0); sum += (baddr[6] != 0); sum += (baddr[7] != 0); if (sum == 0) { clear_bit(bbit, ms->block_bitmap); ms->free_block++; } pbit += PAGES_PER_BLOCK; bbit++; } } static struct mem_section *addr_to_mem_section(unsigned long addr) { struct mem_section *temp; int i; for (i = 0; i < nr_sections; i++) { temp = &mem_sections[i]; if ((addr >= temp->vir_base) && (addr < temp->vir_end)) return temp; } return NULL; } static struct page *__alloc_pages_from_section(struct mem_section *section, int count, int align, int flags) { struct page *page; int bit; if (count == 1) bit = find_next_zero_bit_loop(section->bitmap, section->total_cnt, section->bm_current); else bit = bitmap_find_next_zero_area_align(section->bitmap, section->total_cnt, 0, count, align); if (bit >= section->total_cnt) return NULL; bitmap_set(section->bitmap, bit, count); if ((1 == count) || (1 == align)) { section->bm_current = bit + count; ASSERT(section->bm_current < section->total_cnt); if (section->bm_current == section->total_cnt) section->bm_current = 0; } page = section->pages + bit; page->cnt = count; page->flags = (flags | PAGE_F_HEAD) & 0xffff; page->pfn = PAGE_ADDR(section->phy_base, bit) >> PAGE_SHIFT; /* * update the block bitmap information. */ section->free_cnt -= count; if (section->block_section) alloc_pages_in_block(page, section); return page; } static struct page *alloc_pages_from_section(int pages, int align, int flags) { struct page *page = NULL; struct mem_section *section; int i; flags &= PAGE_F_MASK; for (i = 0; i < nr_sections; i++) { section = &mem_sections[i]; spin_lock(§ion->lock); if (section->free_cnt < pages) { spin_unlock(§ion->lock); continue; } page = __alloc_pages_from_section(section, pages, align, flags); spin_unlock(§ion->lock); if (page) return page; } return NULL; } static void bzero_pages(struct page *page, int pages) { memset((void *)page_va(page), 0, pages << PAGE_SHIFT); } struct page *__alloc_pages(int pages, int align, int flags) { struct page *page; if ((pages <= 0) || (align == 0)) return NULL; page = alloc_pages_from_section(pages, align, flags); if (!page) { pr_warn("no more pages\n"); return NULL; } return page; } void *__get_free_pages(int pages, int align, int flags) { struct page *page = NULL; page = __alloc_pages(pages, align, flags); if (page) { bzero_pages(page, pages); return (void *)page_va(page); } return NULL; } static struct page * get_page_in_section(struct mem_section *section, unsigned long addr) { unsigned long start = (addr - section->vir_base) >> PAGE_SHIFT; return section->pages + start; } struct page *addr_to_page(unsigned long addr) { struct mem_section *section; section = addr_to_mem_section((unsigned long)addr); if (!section) { pr_err("bad address 0x%lx\n", (unsigned long)addr); return NULL; } return get_page_in_section(section, addr); } static int free_pages_in_section(struct page *page, struct mem_section *ms) { unsigned long flags = page_flags(page); unsigned long start, pstart; int count; /* * if the page is not the page head or the page is used * as slab or other, then it means its a slab memory * or can not release by now */ ASSERT((flags != 0) && (flags & PAGE_F_HEAD) && !(flags & PAGE_F_SLAB)); /* * clear all the pages in case the memory data leak. */ count = page_count(page); pstart = page_pa(page); ASSERT((pstart != 0) && (count != 0)); start = (pstart - ms->phy_base) >> PAGE_SHIFT; bitmap_clear(ms->bitmap, start, count); ms->free_cnt += count; /* * update the block information in this section if * needed. */ if (ms->block_section) free_pages_in_block(page, ms); /* * clear the page information last. */ memset(page, 0, sizeof(struct page)); return 0; } int __free_pages(struct page *page) { struct mem_section *section; section = addr_to_mem_section(page_va(page)); if (!section) { pr_err("bad address to free 0x%lx\n", page_va(page)); return -EFAULT; } spin_lock(§ion->lock); free_pages_in_section(page, section); spin_unlock(§ion->lock); return 0; } int free_pages(void *addr) { struct mem_section *section; struct page *page; ASSERT(is_kva(addr)); if (!IS_PAGE_ALIGN(addr)) return -EINVAL; /* * check whether this addr is in page section or * block section or is just a slab memory */ section = addr_to_mem_section((unsigned long)addr); if (!section) { pr_warn("not page address 0x%lx\n", (unsigned long)addr); return -EFAULT; } spin_lock(§ion->lock); /* * if the page is not the page head or the page is used * as slab or other, then it means its a slab memory * or can not release by now */ page = get_page_in_section(section, (unsigned long)addr); if (page_flags(page) & PAGE_F_SLAB) { pr_warn("slab memory can not be freed by free_pages()\n"); spin_unlock(§ion->lock); return -EINVAL; } free_pages_in_section(page, section); spin_unlock(§ion->lock); return 0; } static void *get_free_block_from_section(struct mem_section *ms, unsigned long flags) { struct page *page; unsigned long start; unsigned long base; unsigned long *bitmap_addr; if (ms->free_block == 0) return NULL; start = find_next_zero_bit_loop(ms->block_bitmap, ms->current_block, ms->total_block); if (start >= ms->total_block) return NULL; /* * mask the block bit in block bitmap. */ set_bit(start, ms->block_bitmap); ms->free_block--; ms->current_block = start + 1; if (ms->current_block == ms->total_block) ms->current_block = 0; flags |= PAGE_F_HEAD; page = ms->pages + (start * PAGES_PER_BLOCK); page->flags = (uint16_t)flags & 0xffff; page->cnt = PAGES_PER_BLOCK; base = ms->phy_base + (start << BLOCK_SHIFT); page->pfn = base >> PAGE_SHIFT; /* * update page bitmap for this block in page bitmap. * currently need make sure below limition: * 1 - 64BIT OS, sizeof(unsigned long) = 8 * 2 - 2M huge page, 512 pages per block * 3 - need 8 * unsigned long to store bitmap. */ start = BITS_TO_LONGS(start * PAGES_PER_BLOCK); bitmap_addr = ms->bitmap + start; bitmap_addr[0] = (unsigned long)-1; bitmap_addr[1] = (unsigned long)-1; bitmap_addr[2] = (unsigned long)-1; bitmap_addr[3] = (unsigned long)-1; bitmap_addr[4] = (unsigned long)-1; bitmap_addr[5] = (unsigned long)-1; bitmap_addr[6] = (unsigned long)-1; bitmap_addr[7] = (unsigned long)-1; return (void *)base; } void *get_free_block(unsigned long flags) { struct mem_section *ms; void *base = 0; int i; flags &= PAGE_F_MASK; flags |= PAGE_F_HUGE; for (i = 0; i < nr_sections; i++) { ms = &mem_sections[i]; if (!ms->block_section) continue; spin_lock(&ms->lock); base = get_free_block_from_section(ms, flags); spin_unlock(&ms->lock); if (base) break; } return (void *)ptov(base); } void free_block(void *addr) { free_pages(addr); } ================================================ FILE: kernel/core/percpu.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include struct pcpu pcpus[NR_CPUS]; unsigned long __cache_line_align percpu_offset[CONFIG_NR_CPUS]; #define PCPU_STAT_OFFLINE 0 #define PCPU_STAT_ONLINE 1 void percpu_init(int cpuid) { extern unsigned char __percpu_start; extern unsigned char __percpu_section_size; int i; /* * the data of percpu section has been zeroed at boot code * here do not to zero it again. * * some member of pcpu has been init on boot stage, like cpuid. */ for (i = 0; i < CONFIG_NR_CPUS; i++) { percpu_offset[i] = (phy_addr_t)(&__percpu_start) + (size_t)(&__percpu_section_size) * i; pr_info("percpu [%d] offset 0x%x\n", i, percpu_offset[i]); get_per_cpu(pcpu, i) = &pcpus[i]; } } static int percpu_subsystem_init(void) { struct pcpu *pcpu = get_pcpu(); int pages = TASK_STACK_SIZE >> PAGE_SHIFT; pcpu->stack = get_free_pages(pages, GFP_KERNEL); ASSERT(pcpu->stack != NULL); pcpu->stack += 2 * PAGE_SIZE; pcpu->state = PCPU_STATE_RUNNING; return 0; } subsys_initcall_percpu(percpu_subsystem_init); ================================================ FILE: kernel/core/print.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #ifndef CONFIG_LOG_LEVEL #define CONFIG_LOG_LEVEL PRINT_LEVEL_NOTICE #endif static DEFINE_SPIN_LOCK(print_lock); static unsigned int print_level = CONFIG_LOG_LEVEL; static int get_print_time(char *buffer) { unsigned long us; unsigned long second; int len, left; char buf[64]; us = get_current_time() / 1000; second = us / 1000000; us = us % 1000000; len = uitoa(buf, second); len = len > 8 ? 8 : len; left = 8 - len; if (left > 0) { memset(buffer, ' ', left); buffer += left; } memcpy(buffer, buf, len); buffer += len; *buffer++ = '.'; memset(buf, '0', 8); len = uitoa(buf, us); len = len > 6 ? 6 : len; left = 6 - len; if (left > 0) { memset(buffer, '0', left); buffer += left; } memcpy(buffer, buf, len); return 15; } void change_log_level(unsigned int level) { print_level = level; } int level_print(int level, char *fmt, ...) { va_list arg; int printed, i, cpuid; char buf[64]; char *buffer = buf; unsigned long flags; int tid = 0; struct task *task; if (level > print_level) return 0; preempt_disable(); cpuid = smp_processor_id(); task = get_current_task(); if (task) tid = get_task_tid(task); /* * after to handle the level we change * the level to the current CPU */ *buffer++ = '['; /* get the time of the log when it print */ buffer += get_print_time(buffer); *buffer++ = '@'; /* add the processor id to the buffer */ *buffer++ = (cpuid / 10) + '0'; *buffer++ = (cpuid % 10) + '0'; /* add the task tid to the buffer */ *buffer++ = ' '; *buffer++ = (tid / 100) + '0'; *buffer++ = ((tid % 100) / 10) + '0'; *buffer++ = (tid % 10) + '0'; *buffer++ = ']'; *buffer++ = ' '; /* * printf the header first then process the string * which wanna to show */ printed = buffer - buf; spin_lock_irqsave(&print_lock, flags); for(i = 0; i < printed; i++) console_putc(buf[i]); va_start(arg, fmt); printed += vsprintf(NULL, fmt, arg); va_end(arg); spin_unlock_irqrestore(&print_lock, flags); preempt_enable(); return printed; } int printf(char *fmt, ...) { va_list arg; int printed; unsigned long flags; spin_lock_irqsave(&print_lock, flags); va_start(arg, fmt); printed = vsprintf(NULL, fmt, arg); va_end(arg); spin_unlock_irqrestore(&print_lock, flags); return printed; } /* * TBD */ int puts(char *buf, size_t size) { size_t print = 0; unsigned long flags; spin_lock_irqsave(&print_lock, flags); while (print < size) console_putc(buf[print++]); spin_unlock_irqrestore(&print_lock, flags); return size; } ================================================ FILE: kernel/core/queue.c ================================================ /* * Copyright (C) 2019 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include int queue_init(queue_t *qt, int size, char *name) { struct queue *q; q = zalloc(sizeof(*q)); if (!q) return -ENOMEM; q->q_start = (void **)zalloc(sizeof(void *) * size); if (!q->q_start) { free(q); return -ENOMEM; } q->q_end = q->q_start + size; q->q_in = q->q_start; q->q_out = q->q_start; q->q_cnt = 0; q->q_size = size; event_init(TO_EVENT(qt), OS_EVENT_TYPE_QUEUE, (void *)q); return 0; } static inline void queue_free(queue_t *qt) { struct queue *q; q = qt->data; if (q && q->q_start) free(q->q_start); if (q) free(q); } static inline void *queue_pop(struct queue *q) { void *pmsg; pmsg = *q->q_out++; q->q_cnt--; if (q->q_out == q->q_end) q->q_out = q->q_start; return pmsg; } static inline void queue_push(struct queue *q, void *pmsg) { *q->q_in++ = pmsg; if (q->q_in == q->q_end) q->q_in = q->q_start; q->q_cnt++; } static inline void queue_push_front(struct queue *q, void *pmsg) { if (q->q_out == q->q_start) q->q_out = q->q_end; q->q_out--; *q->q_out = pmsg; q->q_cnt++; } void *queue_accept(queue_t *qt) { unsigned long flags; struct queue *q; void *pmsg = NULL; spin_lock_irqsave(&qt->lock, flags); q = (struct queue *)qt->data; if (q->q_cnt > 0) pmsg = queue_pop(q); spin_unlock_irqrestore(&qt->lock, flags); return pmsg; } int queue_flush(queue_t *qt) { struct queue *q; unsigned long flags; spin_lock_irqsave(&qt->lock, flags); q = (struct queue *)qt->data; q->q_in = q->q_start; q->q_out = q->q_start; q->q_cnt = 0; spin_unlock_irqrestore(&qt->lock, flags); return 0; } void *queue_pend(queue_t *qt, uint32_t timeout) { void *pmsg; struct queue *q; unsigned long flags; might_sleep(); spin_lock_irqsave(&qt->lock, flags); q = (struct queue *)qt->data; if (q->q_cnt > 0) { pmsg = queue_pop(q); spin_unlock_irqrestore(&qt->lock, flags); return pmsg; } __wait_event(TO_EVENT(qt), OS_EVENT_TYPE_QUEUE, timeout); spin_unlock_irqrestore(&qt->lock, flags); return (void *)do_wait_event(TO_EVENT(qt)); } static int __queue_post(queue_t *qt, void *pmsg, int front) { struct queue *q; unsigned long flags; int ret; spin_lock_irqsave(&qt->lock, flags); ret = wake_up_event_waiter(TO_EVENT(qt), (long)pmsg, TASK_STATE_PEND_OK, 1); if (ret) { spin_unlock_irqrestore(&qt->lock, flags); return 0; } q = (struct queue *)qt->data; if (q->q_cnt >= q->q_size) { spin_unlock_irqrestore(&qt->lock, flags); return -ENOSPC; } if (front) queue_push_front(q, pmsg); else queue_push(q, pmsg); spin_unlock_irqrestore(&qt->lock, flags); return ret; } int queue_post_abort(queue_t *qt, int opt) { return 0; } int queue_post(queue_t *qt, void *pmsg) { return __queue_post(qt, pmsg, 0); } int queue_post_front(queue_t *qt, void *pmsg) { return __queue_post(qt, pmsg, 1); } ================================================ FILE: kernel/core/ramdisk.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include void *ramdisk_start, *ramdisk_end; static struct ramdisk_inode *root; static struct ramdisk_sb *sb; static void *ramdisk_data; void set_ramdisk_address(void *start, void *end) { ramdisk_start = start; ramdisk_end = end; } int ramdisk_init(void) { unsigned long start = ptov(ramdisk_start); size_t size = ramdisk_end - ramdisk_start; /* * need remap the ramdisk memory space, if it * is not in the kernel memory space TBD. */ if (!ramdisk_start || !ramdisk_end) { pr_err("ramdisk address is not set\n"); return -EINVAL; } if (!IS_PAGE_ALIGN(ramdisk_start)) { pr_err("ramdisk start address need PAGE align\n"); return -EINVAL; } if (create_host_mapping(start, (unsigned long)ramdisk_start, size, VM_RO)) { pr_err("unable map ramdisk memory\n"); return -ENOMEM; } if (strncmp((void *)start, RAMDISK_MAGIC, RAMDISK_MAGIC_SIZE) != 0) { destroy_host_mapping(start, size); pr_err("bad ramdisk format\n"); return -EBADF; } /* * the ramdisk is read only, init the ramdisk * information, inclue the superblock and the * root inode */ ramdisk_start = (void *)ptov(ramdisk_start); ramdisk_end = (void *)ptov(ramdisk_end); sb = ramdisk_start + RAMDISK_MAGIC_SIZE; root = ramdisk_start + sb->inode_offset; ramdisk_data = ramdisk_start + sb->data_offset; return 0; } const char *ramdisk_file_name(struct ramdisk_file *file) { return file->inode->f_name; } unsigned long ramdisk_file_size(struct ramdisk_file *file) { return file->inode->f_size; } unsigned long ramdisk_file_base(struct ramdisk_file *file) { return (unsigned long)ramdisk_data + file->inode->f_offset; } static struct ramdisk_inode *get_file_inode(const char *name) { struct ramdisk_inode *inode; for (inode = root; inode < root + sb->file_cnt; inode++) { if (strncmp(inode->f_name, name, RAMDISK_FNAME_SIZE - 1) == 0) return inode; } return NULL; } int ramdisk_read(struct ramdisk_file *file, void *buf, size_t size, unsigned long offset) { if (!file) return -EINVAL; if ((offset + size) > file->inode->f_size) return -EINVAL; memcpy(buf, ramdisk_data + file->inode->f_offset + offset, size); return 0; } int ramdisk_open(char *name, struct ramdisk_file *file) { struct ramdisk_inode *inode; if (!sb) { pr_debug("super block not found\n"); return -ENOENT; } inode = get_file_inode(name); if (!inode) return -ENOENT; memset(file, 0, sizeof(struct ramdisk_file)); file->inode = inode; return 0; } ================================================ FILE: kernel/core/sched.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_VIRT #include #include #endif DEFINE_PER_CPU(struct pcpu *, pcpu); extern struct task *os_task_table[OS_NR_TASKS]; #define sched_check() \ do { \ if (in_interrupt() || (irq_disabled() && !preempt_allowed())) \ panic("sched is disabled %s %d\n", __func__, __LINE__); \ } while (0) void __might_sleep(const char *file, int line, int preempt_offset) { struct task *task = current; WARN_ONCE(task->state != TASK_STATE_RUNNING, "do not call blocking ops when !TASK_RUNNING; " "state=%d", task->state); if (preempt_allowed() && !irq_disabled() && !task_is_idle(task)) return; pr_err("BUG: sleeping function called from invalid context at %d %s:%d\n", current->ti.preempt_count, file, line); dump_stack(NULL, (unsigned long *)arch_get_sp()); } static inline void sched_update_sched_timer(void) { struct pcpu *pcpu = get_pcpu(); struct task *task = current; /* * enable the sched timer if there are more than one * ready task on the same prio. */ if ((pcpu->tasks_in_prio[task->prio] > 1)) setup_and_start_timer(&pcpu->sched_timer, MILLISECS(task->run_time)); else stop_timer(&pcpu->sched_timer); } static void add_task_to_ready_list(struct pcpu *pcpu, struct task *task, int preempt) { /* * make sure the new task is insert to front of the current * task. * * if the prio is equal to the current task's prio, insert to * the front of the current task. */ ASSERT(task->state_list.next == NULL); pcpu->tasks_in_prio[task->prio]++; if (current->prio == task->prio) { list_insert_before(¤t->state_list, &task->state_list); if (pcpu->tasks_in_prio[task->prio] == 2) sched_update_sched_timer(); } else { list_add_tail(&pcpu->ready_list[task->prio], &task->state_list); } mb(); pcpu->local_rdy_grp |= BIT(task->prio); if (preempt || current->prio > task->prio) set_need_resched(); } static void remove_task_from_ready_list(struct pcpu *pcpu, struct task *task) { ASSERT(task->state_list.next != NULL); list_del(&task->state_list); if (is_list_empty(&pcpu->ready_list[task->prio])) pcpu->local_rdy_grp &= ~BIT(task->prio); mb(); pcpu->tasks_in_prio[task->prio]--; /* * check whether need to stop the sched timer. */ if ((current->prio == task->prio) && (pcpu->tasks_in_prio[task->prio] == 1)) sched_update_sched_timer(); } void pcpu_resched(int pcpu_id) { send_sgi(CONFIG_MINOS_RESCHED_IRQ, pcpu_id); } void pcpu_irqwork(int pcpu_id) { send_sgi(CONFIG_MINOS_IRQWORK_IRQ, pcpu_id); } static int select_task_run_cpu(void) { /* * TBD */ return (NR_CPUS - 1); } static void percpu_task_ready(struct pcpu *pcpu, struct task *task, int preempt) { unsigned long flags; local_irq_save(flags); add_task_to_ready_list(pcpu, task, preempt); local_irq_restore(flags); } static inline void smp_percpu_task_ready(struct pcpu *pcpu, struct task *task, int preempt) { unsigned long flags; if (preempt) task_set_resched(task); ASSERT(task->state_list.next == NULL); spin_lock_irqsave(&pcpu->lock, flags); list_add_tail(&pcpu->new_list, &task->state_list); spin_unlock_irqrestore(&pcpu->lock, flags); pcpu_irqwork(pcpu->pcpu_id); } int task_ready(struct task *task, int preempt) { struct pcpu *pcpu, *tpcpu; preempt_disable(); task->cpu = task->affinity; if (task->cpu == -1) task->cpu = select_task_run_cpu(); /* * if the task is a precpu task and the cpu is not * the cpu which this task affinity to then put this * cpu to the new_list of the pcpu and send a resched * interrupt to the pcpu */ pcpu = get_pcpu(); if (pcpu->pcpu_id != task->cpu) { tpcpu = get_per_cpu(pcpu, task->cpu); smp_percpu_task_ready(tpcpu, task, preempt); } else { percpu_task_ready(pcpu, task, preempt); } preempt_enable(); return 0; } void task_sleep(uint32_t delay) { struct task *task = current; unsigned long flags; /* * task sleep will wait for the sleep timer expired * or the event happend */ local_irq_save(flags); do_not_preempt(); task->delay = (delay == (uint32_t)-1 ? TASK_WAIT_FOREVER : delay); task->state = TASK_STATE_WAIT_EVENT; task->wait_type = OS_EVENT_TYPE_TIMER; task->wait_event = NULL; local_irq_restore(flags); sched(); } static inline void task_stop(int state) { struct task *task = current; unsigned long flags; do_not_preempt(); local_irq_save(flags); task->delay = 0; task->state = state; task->wait_type = 0; local_irq_restore(flags); sched(); } void task_suspend(void) { task_stop(TASK_STATE_SUSPEND); } void task_die(void) { task_stop(TASK_STATE_STOP); } static struct task *pick_next_task(struct pcpu *pcpu) { struct list_head *head; struct task *task = current; int prio; /* * if the current task need to sleep or waitting some * event happen. delete it from the ready list, then the * next run task can be got. */ mb(); ASSERT(task->state != TASK_STATE_READY); if (!task_is_running(task)) { remove_task_from_ready_list(pcpu, task); if (task->state == TASK_STATE_STOP) { list_add_tail(&pcpu->stop_list, &task->state_list); flag_set(&pcpu->kworker_flag, KWORKER_TASK_RECYCLE); } } /* * get the highest ready task list to running */ prio = ffs_one_table[pcpu->local_rdy_grp]; ASSERT(prio != -1); head = &pcpu->ready_list[prio]; /* * get the first task, then put the next running * task to the end of the ready list. */ ASSERT(!is_list_empty(head)); task = list_first_entry(head, struct task, state_list); list_del(&task->state_list); list_add_tail(head, &task->state_list); return task; } static void switch_to_task(struct task *cur, struct task *next) { struct pcpu *pcpu = get_pcpu(); arch_task_sched_out(cur); do_hooks(cur, NULL, OS_HOOK_TASK_SWITCH_OUT); /* * check the current task's state and do some action * to it, check whether it suspend time is set or not * * if the task is ready state, adjust the run time of * this task. If the task need to wait some event, and * need request a timeout timer then need setup the timer. */ if ((cur->state == TASK_STATE_WAIT_EVENT) && (cur->delay > 0)) setup_and_start_timer(&cur->delay_timer, MILLISECS(cur->delay)); else if (cur->state == TASK_STATE_RUNNING) cur->state = TASK_STATE_READY; cur->last_cpu = cur->cpu; cur->run_time = CONFIG_TASK_RUN_TIME; smp_wmb(); /* * notify the cpu which need to waku-up this task that * the task has been do to sched out, can be wakeed up * safe, the task is offline now. */ cur->cpu = -1; smp_wmb(); /* * change the current task to next task. */ next->state = TASK_STATE_RUNNING; next->ti.flags &= ~__TIF_TICK_EXHAUST; next->cpu = pcpu->pcpu_id; set_current_task(next); pcpu->running_task = next; next->ctx_sw_cnt++; next->wait_event = 0; next->start_ns = NOW(); smp_wmb(); do_hooks(next, NULL, OS_HOOK_TASK_SWITCH_TO); arch_task_sched_in(next); } static void sched_tick_handler(unsigned long data) { struct task *task = current; /* * mark this task has used its running ticket, and the sched * timer is off. */ task->ti.flags |= __TIF_TICK_EXHAUST; set_need_resched(); } static void inline sys_sched(void) { sched_check(); arch_sys_sched(); } void sched(void) { /* * tell the scheduler that I am ok to sched out. */ set_need_resched(); clear_do_not_preempt(); do { sys_sched(); } while (need_resched()); } static inline int sched_allowed(void) { return preempt_allowed() && !irq_disabled(); } void cond_resched(void) { if (need_resched() && sched_allowed()) sched(); } void task_exit(int errno) { set_current_state(TASK_STATE_STOP, 0); sched(); } static inline int __exception_return_handler(void) { struct task *next, *task = current; struct task_info *ti = to_task_info(task); struct pcpu *pcpu = get_pcpu(); /* * if the task is suspend state, means next the cpu * will call sched directly, so do not sched out here * * 1 - when preempt_count > 0, the scheduler whill try * to shced() when preempt_enable. * 2 - __TIF_DONOT_PREEMPT is set, it will call sched() at * once. */ if (!(ti->flags & __TIF_NEED_RESCHED) || (ti->preempt_count > 0) || (ti->flags & __TIF_DONOT_PREEMPT)) goto task_run_again; ti->flags &= ~__TIF_NEED_RESCHED; next = pick_next_task(pcpu); if ((next == task)) goto task_run_again; switch_to_task(task, next); do_hooks(task, next, OS_HOOK_TASK_SWITCH); return 0; task_run_again: if (test_and_clear_bit(TIF_TICK_EXHAUST, &ti->flags)) return -EAGAIN; else return -EACCES; } void exception_return_handler(void) { int ret = __exception_return_handler(); if ((ret == 0) || (ret == -EAGAIN)) sched_update_sched_timer(); } static int irqwork_handler(uint32_t irq, void *data) { struct pcpu *pcpu = get_pcpu(); struct task *task, *n; int preempt = 0, need_preempt; /* * check whether there are new taskes need to * set to ready state again */ raw_spin_lock(&pcpu->lock); list_for_each_entry_safe(task, n, &pcpu->new_list, state_list) { /* * remove it from the new_next. */ list_del(&task->state_list); if (task->state == TASK_STATE_RUNNING) { pr_err("task %s state %d wrong\n", task->name? task->name : "Null", task->state); continue; } need_preempt = task_need_resched(task); preempt += need_preempt; task_clear_resched(task); add_task_to_ready_list(pcpu, task, need_preempt); task->state = TASK_STATE_READY; /* * if the task has delay timer, cancel it. */ if (task->delay) { stop_timer(&task->delay_timer); task->delay = 0; } } raw_spin_unlock(&pcpu->lock); if (preempt || task_is_idle(current)) set_need_resched(); return 0; } static int resched_handler(uint32_t irq, void *data) { set_need_resched(); return 0; } int local_sched_init(void) { struct pcpu *pcpu = get_pcpu(); init_timer(&pcpu->sched_timer, sched_tick_handler, (unsigned long)pcpu); request_irq(CONFIG_MINOS_RESCHED_IRQ, resched_handler, 0, "resched handler", NULL); request_irq(CONFIG_MINOS_IRQWORK_IRQ, irqwork_handler, 0, "irqwork handler", NULL); return 0; } static void pcpu_sched_init(struct pcpu *pcpu) { init_list(&pcpu->new_list); init_list(&pcpu->stop_list); init_list(&pcpu->die_process); init_list(&pcpu->ready_list[0]); init_list(&pcpu->ready_list[1]); init_list(&pcpu->ready_list[2]); init_list(&pcpu->ready_list[3]); init_list(&pcpu->ready_list[4]); init_list(&pcpu->ready_list[5]); init_list(&pcpu->ready_list[6]); init_list(&pcpu->ready_list[7]); } int sched_init(void) { int i; for (i = 0; i < NR_CPUS; i++) pcpu_sched_init(&pcpus[i]); return 0; } static int wake_up_interrupted(struct task *task, long pend_state, unsigned long data) { unsigned long flags; ASSERT(pend_state != TASK_STATE_PEND_TO); if (task->state != TASK_STATE_WAIT_EVENT) return -EACCES; if (!irq_disabled()) panic("unexpected irq happend when wait_event() ?\n"); /* * the interrup occurs when task try to wait_event. in * addition: * 1 - the interrupt is happended in the same cpu. * 2 - will not the delay timer, since the delay time * has not been set already. * 3 - the state must TASK_STATE_WAIT_EVENT * 4 - task has not been in sched routine. * * meanwhile, other cpu may already in the wake up function * try to wake up the task, then need check this suitation * since other cpu while check cpu == -1, this will lead * to dead lock if use spin_lock function. So here use * spin_trylock instead. */ if (!spin_trylock_irqsave(&task->s_lock, flags)) return -EBUSY; if (task->state != TASK_STATE_WAIT_EVENT) { spin_unlock_irqrestore(&task->s_lock, flags); return -EINVAL; } task->ti.flags |= __TIF_WAIT_INTERRUPTED; task->ti.flags &= ~__TIF_DONOT_PREEMPT; /* * here this cpu got this task, and can set the new * state to running and run it again. */ task->pend_state = pend_state; task->state = TASK_STATE_RUNNING; task->delay = 0; task->ipcdata = data; spin_unlock_irqrestore(&task->s_lock, flags); return 0; } static int wake_up_common(struct task *task, long pend_state, unsigned long data) { unsigned long flags; uint32_t timeout; preempt_disable(); spin_lock_irqsave(&task->s_lock, flags); /* * task already waked up, if the stat is set to * TASK_STATE_WAIT_EVENT, it means that the task will * call sched() to sleep or wait something happen. */ if (task->state != TASK_STATE_WAIT_EVENT) { spin_unlock_irqrestore(&task->s_lock, flags); preempt_enable(); return -EPERM; } /* * the task may in sched() routine on other cpu * wait the task really out of running. since the task * will not preempt in the kernel space now, so the cpu * of the task will change to -1 at one time. * * since the kernel can not be preempted so it can make * sure that sched() can be finish its work. */ while (task->cpu != -1) cpu_relax(); /* * here this cpu got this task, and can set the new * state to running and run it again. */ task->pend_state = pend_state; task->state = TASK_STATE_WAKING; timeout = task->delay; task->delay = 0; task->ipcdata = data; spin_unlock_irqrestore(&task->s_lock, flags); /* * here it means that this task has not been timeout, so can * delete the timer for this task. */ if (timeout && (task->pend_state != TASK_STATE_PEND_TO)) stop_timer(&task->delay_timer); /* * find a best cpu to run this task. */ task_ready(task, 1); preempt_enable(); return 0; } int __wake_up(struct task *task, long pend_state, unsigned long data) { if (task == current) return wake_up_interrupted(task, pend_state, data); else return wake_up_common(task, pend_state, data); } ================================================ FILE: kernel/core/sem.c ================================================ /* * Copyright (C) 2019 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include uint32_t sem_accept(sem_t *sem) { uint32_t cnt; unsigned long flags; spin_lock_irqsave(&sem->lock, flags); cnt = sem->cnt; if (cnt > 0) sem->cnt--; spin_unlock_irqrestore(&sem->lock, flags); return cnt == 0 ? -ENOSPC : cnt; } int sem_pend(sem_t *sem, uint32_t timeout) { unsigned long flags; might_sleep(); spin_lock_irqsave(&sem->lock, flags); if (sem->cnt > 0) { sem->cnt--; spin_unlock_irqrestore(&sem->lock, flags); return 0; } __wait_event(TO_EVENT(sem), OS_EVENT_TYPE_SEM, timeout); spin_unlock_irqrestore(&sem->lock, flags); return do_wait_event(TO_EVENT(sem)); } static int sem_post_opt(sem_t *sem, int pend_state, int opt) { unsigned long flags; int ret; spin_lock_irqsave(&sem->lock, flags); ret = wake_up_event_waiter(TO_EVENT(sem), 0, pend_state, opt == OS_EVENT_OPT_BROADCAST ? WAKEUP_ALL : 1); if (pend_state != TASK_STATE_PEND_ABORT) { if (!ret && (sem->cnt < INT_MAX)) sem->cnt++; else ret = -EOVERFLOW; } spin_unlock_irqrestore(&sem->lock, flags); if (ret > 0) cond_resched(); return ret; } /* * the sem is broken, wake up all the waiter. */ int sem_pend_abort(sem_t *sem, int opt) { return sem_post_opt(sem, TASK_STATE_PEND_ABORT, OS_EVENT_OPT_BROADCAST); } int sem_post(sem_t *sem) { return sem_post_opt(sem, TASK_STATE_PEND_OK, OS_EVENT_OPT_NONE); } ================================================ FILE: kernel/core/slab.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include struct slab_header { unsigned long size; union { unsigned long magic; struct slab_header *next; }; } __packed; #define HASH_TABLE_SIZE 8 #define SLAB_MEM_BASE ptov(511UL * 1024 * 1024 * 1024) #define SLAB_MEM_SIZE (128UL * 1024 * 1024) #define SLAB_MEM_END (SLAB_MEM_BASE + SLAB_MEM_SIZE) #define SLAB_MIN_DATA_SIZE (16) #define SLAB_MIN_DATA_SIZE_SHIFT (4) #define SLAB_HEADER_SIZE sizeof(struct slab_header) #define SLAB_MIN_SIZE (SLAB_MIN_DATA_SIZE + SLAB_HEADER_SIZE) #define SLAB_MAGIC (0xdeadbeef) struct slab_type { uint32_t size; struct list_head list; struct slab_header *head; }; /* * will try to get hugepage when first time once * system bootup. */ static DEFINE_SPIN_LOCK(slab_lock); static struct list_head slab_hash_table[HASH_TABLE_SIZE]; static void *slab_base; static uint32_t slab_size; static uint32_t cur_free_size; #define hash_id(size) (((size) >> SLAB_MIN_DATA_SIZE_SHIFT) % HASH_TABLE_SIZE) static int inline is_slab_memory(void *addr) { return (((unsigned long)addr >= SLAB_MEM_BASE) && ((unsigned long)addr < SLAB_MEM_END)); } static size_t inline get_slab_alloc_size(size_t size) { return BALIGN(size, SLAB_MIN_DATA_SIZE); } static void *malloc_from_hash_table(size_t size) { int id = hash_id(size); struct slab_type *st; struct slab_header *sh; /* * find the related slab mem id and try to fetch * a free slab memory from the hash cache. */ list_for_each_entry(st, &slab_hash_table[id], list) { if (st->size != size) continue; if (st->head == NULL) return NULL; sh = st->head; st->head = sh->next; sh->magic = SLAB_MAGIC; return ((void *)sh + SLAB_HEADER_SIZE); } return NULL; } static void *malloc_from_slab_heap(size_t size) { unsigned long base = 0; struct slab_header *sh; uint32_t mapsize; void *page; int i; size += SLAB_HEADER_SIZE; if (slab_size < size) return NULL; if (cur_free_size >= size) { mapsize = 0; cur_free_size -= size; } else { base = (unsigned long)slab_base + cur_free_size; mapsize = PAGE_BALIGN(size - cur_free_size); cur_free_size = cur_free_size + mapsize - size; } /* * if need one new page, need to allocate the needed * pages and map it. */ for (i = 0; i < (mapsize >> PAGE_SHIFT); i++) { page = get_free_page(GFP_KERNEL | GFP_SLAB); ASSERT(page != NULL); ASSERT(create_host_mapping(base, vtop(page), PAGE_SIZE, VM_HOST | VM_RW) == 0); base += PAGE_SIZE; } sh = (struct slab_header *)slab_base; sh->magic = SLAB_MAGIC; sh->size = size - SLAB_HEADER_SIZE; slab_base += size; slab_size -= size; return ((void *)sh + SLAB_HEADER_SIZE); } static void free_slab(void *addr) { struct slab_header *header; struct slab_type *st; int id; header = (struct slab_header *)((unsigned long)addr - SLAB_HEADER_SIZE); if ((header->magic != SLAB_MAGIC) || (header->size < SLAB_MIN_DATA_SIZE)) { pr_warn("memory is not a slab mem 0x%p\n", (unsigned long)addr); return; } id = hash_id(header->size); spin_lock(&slab_lock); list_for_each_entry(st, &slab_hash_table[id], list) { if (st->size != header->size) continue; header->next = st->head; st->head = header; spin_unlock(&slab_lock); return; } /* * create new slab type and add the new slab header * to the slab cache. */ st = malloc_from_slab_heap(sizeof(struct slab_type)); ASSERT(st != NULL); pr_debug("create new slab type for %d %d\n", header->size, id); st->size = header->size; list_add_tail(&slab_hash_table[id], &st->list); header->next = NULL; st->head = header; spin_unlock(&slab_lock); } void free(void *addr) { if (!is_slab_memory(addr)) { if (free_pages(addr) == 0) return; } free_slab(addr); } static void *__malloc(size_t size) { void *mem; spin_lock(&slab_lock); mem = malloc_from_hash_table(size); if (mem == NULL) mem = malloc_from_slab_heap(size); spin_unlock(&slab_lock); if (!mem) { pr_err("malloc fail for 0x%x\n"); dump_stack(NULL, NULL); BUG(); } return mem; } void *malloc(size_t size) { ASSERT(size != 0); size = get_slab_alloc_size(size); return __malloc(size); } void *zalloc(size_t size) { void *addr = malloc(size); if (addr) memset(addr, 0, size); return addr; } void slab_init(void) { int i; pr_notice("slab memory allocator init ...\n"); slab_base = (void *)SLAB_MEM_BASE; slab_size = SLAB_MEM_SIZE; for (i = 0; i < HASH_TABLE_SIZE; i++) init_list(&slab_hash_table[i]); } ================================================ FILE: kernel/core/smp.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #define SMP_CALL_LOCKED (1 << 0) #define SMP_FUNCTION_CALL_IRQ CONFIG_SMP_FUNCTION_CALL_IRQ extern unsigned char __smp_affinity_id; uint64_t *smp_affinity_id; phy_addr_t smp_holding_address[CONFIG_NR_CPUS]; cpumask_t cpu_online; static int cpus_all_up; struct smp_call { smp_function fn; unsigned long flags; void *data; }; struct smp_call_data { struct smp_call smp_calls[NR_CPUS]; }; static DEFINE_PER_CPU(struct smp_call_data, smp_call_data); static void inline smp_call_wait(struct smp_call *call) { /* need wait for last call finished */ while (call->flags & SMP_CALL_LOCKED) cpu_relax(); } static void inline smp_call_lock(struct smp_call *call) { if (call->flags & SMP_CALL_LOCKED) pr_warn("smp call is already locked\n"); call->flags |= SMP_CALL_LOCKED; wmb(); } static void inline smp_call_unlock(struct smp_call *call) { call->flags &= ~SMP_CALL_LOCKED; wmb(); } int is_cpus_all_up(void) { return cpus_all_up; } int smp_function_call(int cpu, smp_function fn, void *data, int wait) { int cpuid; struct smp_call *call; struct smp_call_data *cd; unsigned long flags; preempt_disable(); cpuid = smp_processor_id(); if (cpu >= NR_CPUS) return -EINVAL; /* function call itself just call the function */ if (cpu == cpuid) { local_irq_save(flags); fn(data); local_irq_restore(flags); preempt_enable(); return 0; } cd = &get_per_cpu(smp_call_data, cpu); call = &cd->smp_calls[cpuid]; smp_call_wait(call); call->fn = fn; call->data = data; smp_call_lock(call); send_sgi(SMP_FUNCTION_CALL_IRQ, cpu); if (wait) smp_call_wait(call); preempt_enable(); return 0; } static irqreturn_t smp_function_call_handler(uint32_t irq, void *data) { int i; struct smp_call_data *cd; struct smp_call *call; cd = &get_cpu_var(smp_call_data); for (i = 0; i < NR_CPUS; i++) { call = &cd->smp_calls[i]; if (call->flags & SMP_CALL_LOCKED) { call->fn(call->data); call->fn = NULL; call->data = NULL; smp_call_unlock(call); } } return 0; } int smp_cpu_up(unsigned long cpu, unsigned long entry) { if (platform->cpu_on) return platform->cpu_on(cpu, entry); pr_warn("no cpu on function\n"); return 0; } void smp_cpus_up(void) { int i, ret, cnt; uint64_t affinity; flush_cache_all(); for (i = 1; i < CONFIG_NR_CPUS; i++) { cnt = 0; affinity = cpuid_to_affinity(i); ret = smp_cpu_up(affinity, CONFIG_MINOS_ENTRY_ADDRESS); if (ret) { pr_fatal("failed to bring up cpu-%d\n", i); continue; } } for (i = 1; i < CONFIG_NR_CPUS; i++) { pr_notice("waiting 2 seconds for cpu-%d up\n", i); while ((smp_affinity_id[i] == 0) && (cnt < 2000)) { mdelay(1); cnt++; } if (smp_affinity_id[i] == 0) { pr_err("cpu-%d is not up with affinity id 0x%p\n", i, smp_affinity_id[i]); } else { cpumask_set_cpu(i, &cpu_online); } } cpus_all_up = 1; wmb(); } void smp_init(void) { int i; struct smp_call_data *cd; smp_affinity_id = (uint64_t *)&__smp_affinity_id; memset(smp_affinity_id, 0, sizeof(uint64_t) * NR_CPUS); cpumask_clearall(&cpu_online); cpumask_set_cpu(0, &cpu_online); for (i = 0; i < NR_CPUS; i++) { cd = &get_per_cpu(smp_call_data, i); memset(cd, 0, sizeof(struct smp_call_data)); } arch_smp_init(smp_holding_address); request_irq_percpu(SMP_FUNCTION_CALL_IRQ, smp_function_call_handler, 0, "smp_function_call", NULL); } ================================================ FILE: kernel/core/stdlib.c ================================================ /* * Copyright (c) 2003-2004 Fabrice Bellard * Copyright (c) 2006 Intel Corporation * Copyright (c) 2007 Keir Fraser, XenSource Inc * Copyright (c) 2008 Intel Corporation * Copyright 2009 Red Hat, Inc. and/or its affiliates. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #if 0 uint64_t div64_u64(uint64_t dividend, uint64_t divisor) { uint32_t high = divisor >> 32; uint64_t quot; if (high == 0) { quot = div_u64(dividend, divisor); } else { int n = 1 + fls(high); quot = div_u64(dividend >> n, divisor >> n); if (quot != 0) quot--; if ((dividend - quot * divisor) >= divisor) quot++; } return quot; } #endif /* * Compute with 96 bit intermediate result: (a*b)/c */ uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) { union { uint64_t ll; struct { uint32_t low, high; } l; } u, res; uint64_t rl, rh; u.ll = a; rl = (uint64_t)u.l.low * (uint64_t)b; rh = (uint64_t)u.l.high * (uint64_t)b; rh += (rl >> 32); res.l.high = div64_u64(rh, c); res.l.low = div64_u64(((mod_64(rh, c) << 32) + (rl & 0xffffffff)), c); return res.ll; } ================================================ FILE: kernel/core/string.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #define PRINTF_DEC 0X0001 #define PRINTF_HEX 0x0002 #define PRINTF_OCT 0x0004 #define PRINTF_BIN 0x0008 #define PRINTF_POINTER 0x0010 #define PRINTF_MASK (0xff) #define PRINTF_UNSIGNED 0X0100 #define PRINTF_SIGNED 0x0200 #define PRINTF_LONG 0x0400 typedef char *(*vsprintf_t)(char *dst, const char *src, int size); long absolute(long num) { if (num > 0) return num; return (~num) + 1; } long num_to_str(char *buf, unsigned long num, int b) { static char hex[] ="0123456789abcdef"; long m, len, res; char tmp_buf[64]; char *tmp = tmp_buf; int bdho = b; if (bdho == 32) bdho = 16; memset(tmp_buf, '0', 16); do { m = num % bdho; num = num / bdho; *tmp++ = hex[m]; } while (num >= bdho); if (num != 0) *tmp++ = hex[num]; if (b == 32) res = len = 16; else res = len = tmp - tmp_buf; while (len > 0) { *buf++ = tmp_buf[len - 1]; len--; } return res; } #define xxxtoa(buf, num) \ ({ \ int tmp = 0; \ \ if ((num) < 0) { \ num = ~num + 1; \ *buf++ = '-'; \ tmp = 1; \ } \ \ tmp + num_to_str(buf, num, 10); \ }) long ltoa(char *buf, long num) { return xxxtoa(buf, num); } long itoa(char *buf, int num) { return xxxtoa(buf, num); } long ultoa(char *buf, unsigned long num) { return num_to_str(buf, num, 10); } long uitoa(char *buf, unsigned int num) { return num_to_str(buf, num, 10); } long hextoa(char *buf, unsigned long num) { return num_to_str(buf, num, 16); } long octtoa(char *buf, unsigned long num) { return num_to_str(buf, num, 8); } long bintoa(char *buf, unsigned long num) { return num_to_str(buf, num, 2); } long ptoa(char *buf, unsigned long num) { return num_to_str(buf, num, 32); } char *strncpy(char *des, const char *src, int len) { char *tmp = des; int i; if (des == NULL || src == NULL) return NULL; for (i = 0; i < len; i++) { des[i] = src[i]; } return tmp; } int numbric(char *buf, unsigned long num, int flag) { int len = 0; switch (flag & PRINTF_MASK) { case PRINTF_DEC: if (flag & PRINTF_SIGNED) { if (flag & PRINTF_LONG) len = ltoa(buf, (long)num); else len = itoa(buf, (int)num); } else { if (flag & PRINTF_LONG) len = ultoa(buf, num); else len = uitoa(buf, (unsigned int)num); } break; case PRINTF_HEX: len = hextoa(buf, num); break; case PRINTF_OCT: len = octtoa(buf, num); break; case PRINTF_BIN: len = bintoa(buf, num); break; case PRINTF_POINTER: len = ptoa(buf, num); default: break; } return len; } static inline char *console_vsprintf(char *dst, const char *src, int size) { int i; for (i = 0; i < size; i++) console_putc(src[i]); return (dst + size); } static inline char *memory_vsprintf(char *dst, const char *src, int size) { memcpy(dst, src, size); return (dst + size); } #define PRINT_ALIGN_CHAR(str, align, len) \ ({ \ int index; \ if (align && (align > len)) { \ for (index = 0; index < (align - len); index++) \ str = vst(str, " ", 1); \ } \ }) int vsprintf(char *buf, const char *fmt, va_list arg) { char *str = buf, *tmp; int len, ch, align, i; char num_buf[96]; // 96 is enough for number unsigned long unumber; int flag = 0; vsprintf_t vst; vst = (buf == NULL) ? console_vsprintf : memory_vsprintf; for (str = buf; *fmt; fmt++) { align = 0; if (*fmt != '%') { str = vst(str, fmt, 1); continue; } fmt++; if (is_digit(*fmt)) { align = *fmt - '0'; fmt++; } switch (*fmt) { case 'l': /* * only support %ld %lx or %3ld %3lx style. */ fmt++; ch = *fmt; if ((ch != 'd') && (ch != 'x')) { i = 0; num_buf[i++] = '%'; if (align) num_buf[i++]= align + '0'; num_buf[i++] = 'l'; num_buf[i++] = ch; str = vst(str, num_buf, i); continue; } else { if (ch == 'd') flag |= PRINTF_LONG | PRINTF_DEC; else flag |= PRINTF_LONG | PRINTF_HEX; break; } case 'd': flag |= PRINTF_DEC | PRINTF_SIGNED; break; case 'p': len = 0; flag |= PRINTF_POINTER | PRINTF_UNSIGNED; break; case 'x': flag |= PRINTF_HEX | PRINTF_UNSIGNED; break; case 'u': flag |= PRINTF_DEC | PRINTF_UNSIGNED; break; case 's': len = strlen(tmp = va_arg(arg, char *)); PRINT_ALIGN_CHAR(str, align, len); str = vst(str, (const char *)tmp, len); continue; case 'c': PRINT_ALIGN_CHAR(str, align, 1); ch = (char)(va_arg(arg, int)); str = vst(str, (const char *)&ch, 1); continue; case 'o': flag |= PRINTF_DEC | PRINTF_SIGNED; break; case '%': if (align) { str = vst(str, "%", 1); align = align + '0'; str = vst(str, (char *)&align, 1); } str = vst(str, "%", 1); continue; default: str = vst(str, "%", 1); if (align) { align = align + '0'; str = vst(str, (char *)&align, 1); } str = vst(str, fmt, 1); continue; } unumber = va_arg(arg, unsigned long); len = numbric(num_buf, unumber, flag); PRINT_ALIGN_CHAR(str, align, len); str = vst(str, num_buf, len); flag = 0; } /* * terminated with 0 */ ch = 0; vst(str, (const char *)&ch, 1); return str - buf; } int sprintf(char *str, const char *format, ...) { va_list arg; int count; if (!str) return -EINVAL; va_start(arg, format); count = vsprintf(str, format, arg); va_end(arg); return count; } #if 0 int strlen(char *buf) { int len = 0; if (buf == NULL) return -1; while (*buf++) { len++; } return len; } char *strcpy(char *des, char *src) { char *tmp = des; if (des == NULL || src == NULL) return NULL; while ((*des++=*src++) != '\0'); return tmp; } int strcmp(const char *src, const char *dst) { int ret = 0; while (!(ret = *(unsigned char *)src - *(unsigned char *)dst) && *dst) { ++src, ++dst; } if (ret < 0) ret = -1; else if (ret > 0) ret = 1; return (ret); } int memcmp(const char *src, const char *dst, size_t size) { int i; char ret; if (size == 0) return -EINVAL; for (i = 0; i < size; i++) { if (src[i] != dst[i]) { ret = src[i] - dst[i]; if (ret < 0) return -1; else if (ret > 0) return 1; } } return 0; } int strncmp(const char *src, const char *dst, int n) { int ret = 0; while (n && (!(ret = *(unsigned char *)src - *(unsigned char *)dst))) { ++src, ++dst; n--; } if (ret < 0) ret = -1; else if (ret > 0) ret = 1; return (ret); } char *strchr(char *src, char ch) { for (; *src != (char)ch; ++src) if (*src == '\0') return NULL; return (char *)src; } int memcpy(void *target, void *source, int size) { char *t = (char *)target; char *s = (char *)source; int old_size = size; if(size <= 0) return 0; while (size--) *t++ = *s++; return old_size; } void memset(char *base, char ch, int size) { int i; for (i = 0; i < size; i++) { *(base + i) = ch; } } #endif #define TOLOWER(x) ((x) | 0x20) #define isdigit(ch) ((ch >= '0') && (ch <= '9')) #define isxdigit(ch) (isdigit(ch) || ((ch >= 'a') && (ch <= 'f'))) unsigned long strtoul(const char *cp, char **endp, unsigned int base) { unsigned long result = 0; if (!base) base = 10; if (base == 16 && cp[0] == '0' && TOLOWER(cp[1]) == 'x') cp += 2; while (isxdigit(*cp)) { unsigned int value; value = isdigit(*cp) ? *cp - '0' : TOLOWER(*cp) - 'a' + 10; if (value >= base) break; result = result * base + value; cp++; } if (endp) *endp = (char *)cp; return result; } char *strsep(char **stringp, const char *delim) { char *s; const char *spanp; int c, sc; char *tok; if ((s = *stringp)== NULL) return (NULL); for (tok = s;;) { c = *s++; spanp = delim; do { if ((sc =*spanp++) == c) { if (c == 0) s = NULL; else s[-1] = 0; *stringp = s; return (tok); } } while (sc != 0); } } ================================================ FILE: kernel/core/task.c ================================================ /* * Copyright (C) 2019 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include static DEFINE_SPIN_LOCK(tid_lock); static DECLARE_BITMAP(tid_map, OS_NR_TASKS); struct task *os_task_table[OS_NR_TASKS]; static LIST_HEAD(task_list); /* idle task needed be static defined */ struct task idle_tasks[NR_CPUS]; static DEFINE_PER_CPU(struct task *, idle_task); #define TASK_INFO_INIT(__ti, task) \ do { \ (__ti)->preempt_count = 0; \ (__ti)->flags = 0; \ } while (0) static int alloc_tid(void) { int tid = -1; spin_lock(&tid_lock); tid = find_next_zero_bit(tid_map, OS_NR_TASKS, 1); if (tid >= OS_NR_TASKS) tid = -1; else set_bit(tid, tid_map); spin_unlock(&tid_lock); return tid; } static int request_tid(int tid) { BUG_ON((tid <= 0) || (tid >= OS_NR_TASKS), "no such tid %d\n", tid); return !test_and_set_bit(tid, tid_map); } static void release_tid(int tid) { ASSERT((tid < OS_NR_TASKS) && (tid > 0)); os_task_table[tid] = NULL; smp_wmb(); clear_bit(tid, tid_map); } static int tid_early_init(void) { /* * tid is reserved for system use. */ set_bit(0, tid_map); return 0; } early_initcall(tid_early_init); static void task_timeout_handler(unsigned long data) { struct task *task = (struct task *)data; wake_up_timeout(task); set_need_resched(); } static void task_init(struct task *task, char *name, void *stack, uint32_t stk_size, int prio, int tid, int aff, unsigned long opt, void *arg) { /* * idle task is setup by create_idle task, skip * to setup the stack information of idle task, by * default the kernel stack will set to stack top. */ if (!(opt & TASK_FLAGS_IDLE)) { task->stack_bottom = stack; task->stack_top = stack + stk_size; task->stack_base = task->stack_top; TASK_INFO_INIT(&task->ti, task); } task->tid = tid; task->prio = prio; task->pend_state = 0; task->flags = opt; task->pdata = arg; task->affinity = aff; task->run_time = TASK_RUN_TIME; spin_lock_init(&task->s_lock); task->state = TASK_STATE_SUSPEND; task->cpu = -1; init_timer(&task->delay_timer, task_timeout_handler, (unsigned long)task); os_task_table[tid] = task; if (name) strncpy(task->name, name, MIN(strlen(name), TASK_NAME_SIZE)); else sprintf(task->name, "task%d", tid); } static struct task *do_create_task(char *name, task_func_t func, uint32_t ssize, int prio, int tid, int aff, unsigned long opt, void *arg) { size_t stk_size = PAGE_BALIGN(ssize); struct task *task; void *stack = NULL; /* * allocate the task's kernel stack */ task = zalloc(sizeof(struct task)); if (!task) { pr_err("no more memory for task\n"); return NULL; } stack = get_free_pages(PAGE_NR(stk_size), GFP_KERNEL); if (!stack) { pr_err("no more memory for task stack\n"); free(task); return NULL; } task_init(task, name, stack, stk_size, prio, tid, aff, opt, arg); return task; } static void task_create_hook(struct task *task, void *pdata) { do_hooks((void *)task, pdata, OS_HOOK_CREATE_TASK); } void task_exit_from_user(gp_regs *regs) { struct task *task = current; ASSERT(!(task->flags & TASK_FLAGS_KERNEL)); if (task->exit_from_user) task->exit_from_user(task, regs); } void task_return_to_user(gp_regs *regs) { struct task *task = current; unsigned long flags = task->ti.flags; ASSERT(!(current->flags & TASK_FLAGS_KERNEL)); task->ti.flags &= ~(flags | (__TIF_NEED_STOP | __TIF_NEED_FREEZE)); smp_wmb(); if (flags & __TIF_NEED_STOP) task->state = TASK_STATE_STOP; else if (flags & __TIF_NEED_FREEZE) task->state = TASK_STATE_SUSPEND; if (task->state != TASK_STATE_RUNNING) { sched(); panic("%s %d: should not be here\n", __func__, __LINE__); } if (task->return_to_user) task->return_to_user(task, regs); } void do_release_task(struct task *task) { do_hooks(task, NULL, OS_HOOK_RELEASE_TASK); arch_release_task(task); free_pages(task->stack_bottom); free(task); /* * this function can not be called at interrupt * context, use release_task is more safe */ release_tid(task->tid); } struct task *__create_task(char *name, task_func_t func, uint32_t stk_size, void *usp, int prio, int aff, unsigned long opt, void *arg) { struct task *task; int tid; if ((aff >= NR_CPUS) && (aff != TASK_AFF_ANY)) { pr_warn("task %s afinity will set to 0x%x\n", name, TASK_AFF_ANY); aff = TASK_AFF_ANY; } if ((prio >= OS_PRIO_IDLE) || (prio < 0)) { pr_warn("wrong task prio %d fallback to %d\n", prio, OS_PRIO_DEFAULT_6); prio = OS_PRIO_DEFAULT_6; } tid = alloc_tid(); if (tid < 0) return NULL; preempt_disable(); task = do_create_task(name, func, stk_size, prio, tid, aff, opt, arg); if (!task) { release_tid(tid); preempt_enable(); return NULL; } task_create_hook(task, arg); arch_init_task(task, (void *)func, usp, task->pdata); /* * start the task if need auto started. */ if (!(task->flags & TASK_FLAGS_NO_AUTO_START)) task_ready(task, 0); preempt_enable(); if (os_is_running()) sched(); return task; } struct task *create_task(char *name, task_func_t func, size_t stk_size, void *usp, int prio, int aff, unsigned long opt, void *arg) { if (prio < 0) { if (opt & OS_PRIO_VCPU) prio = OS_PRIO_VCPU; else if (opt & (TASK_FLAGS_SRV | TASK_FLAGS_DRV)) prio = OS_PRIO_SRV; else prio = OS_PRIO_DEFAULT; } return __create_task(name, func, stk_size, usp, prio, aff, opt, arg); } int create_idle_task(void) { struct task *task; char task_name[32]; int aff = smp_processor_id(); int tid = OS_NR_TASKS - 1 - aff; struct pcpu *pcpu = get_pcpu(); task = get_cpu_var(idle_task); BUG_ON(!request_tid(tid), "tid is wrong for idle task cpu%d\n", tid); sprintf(task_name, "idle/%d", aff); task_init(task, task_name, NULL, 0, OS_PRIO_IDLE, tid, aff, TASK_FLAGS_IDLE | TASK_FLAGS_KERNEL, NULL); task->stack_top = (void *)ptov(minos_stack_top) - (aff << CONFIG_TASK_STACK_SHIFT); task->stack_bottom = task->stack_top - TASK_STACK_SIZE; task->state = TASK_STATE_RUNNING; task->cpu = aff; task->run_time = 0; pcpu->running_task = task; set_current_task(task); /* call the hooks for the idle task */ task_create_hook(task, NULL); list_add_tail(&pcpu->ready_list[task->prio], &task->state_list); pcpu->local_rdy_grp |= BIT(task->prio); pcpu->idle_task = task; return 0; } void os_for_all_task(void (*hdl)(struct task *task)) { struct task *task; int idx; // get the tid_lock ? for_each_set_bit(idx, tid_map, OS_NR_TASKS) { task = os_task_table[idx]; if (!task) continue; hdl(task); } } /* * for preempt_disable and preempt_enable need * to set the current task at boot stage */ static int __init_text task_early_init(void) { struct task *task; int i = smp_processor_id(); task = &idle_tasks[i]; memset(task, 0, sizeof(struct task)); get_per_cpu(idle_task, i) = task; /* init the task info for the thread */ TASK_INFO_INIT(current_task_info, task); return 0; } early_initcall_percpu(task_early_init); int create_percpu_tasks(char *name, task_func_t func, int prio, unsigned long flags, void *pdata) { struct task *ret; int cpu; for_each_online_cpu(cpu) { ret = create_task(name, func, TASK_STACK_SIZE, NULL, prio, cpu, flags | TASK_FLAGS_PERCPU, pdata); if (ret == NULL) pr_err("create [%s] fail on cpu%d\n", name, cpu); } return 0; } struct task *create_vcpu_task(char *name, task_func_t func, int aff, unsigned long flags, void *vcpu) { #define VCPU_TASK_FLAG (TASK_FLAGS_VCPU | TASK_FLAGS_NO_AUTO_START) return create_task(name, func, TASK_STACK_SIZE, NULL, OS_PRIO_VCPU, aff, flags | VCPU_TASK_FLAG, vcpu); } struct task *create_kthread(char *name, task_func_t func, int prio, int aff, unsigned long opt, void *arg) { return create_task(name, func, TASK_STACK_SIZE, NULL, prio, aff, opt | TASK_FLAGS_KERNEL, arg); } ================================================ FILE: kernel/core/timer.c ================================================ /* * * Copyright (C) 1991, 1992 Linus Torvalds * * 1997-01-28 Modified by Finn Arne Gangstad to make timers scale better. * * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 * "A Kernel Model for Precision Timekeeping" by Dave Mills * 1998-12-24 Fixed a xtime SMP race (we need the xtime_lock rw spinlock to * serialize accesses to xtime/lost_ticks). * Copyright (C) 1998 Andrea Arcangeli * 1999-03-10 Improved NTP compatibility by Ulrich Windl * 2002-05-31 Move sys_sysinfo here and make its locking sane, Robert Love * 2000-10-05 Implemented scalable SMP per-CPU timer handling. * Copyright (C) 2000, 2001, 2002 Ingo Molnar * 2018-05-02 Changed for Minos hypervisor */ #include #include #include #include #include #include #define TIMER_PRECISION 1000000 // 1ms 1000ns DEFINE_PER_CPU(struct raw_timer, timers); #define DEFAULT_TIMER_MARGIN (TIMER_PRECISION / 2) void soft_timer_interrupt(void) { struct raw_timer *timers = &get_cpu_var(timers); struct timer *timer, *next_timer = NULL; struct list_head tmp_head; uint64_t now; timer_func_t fn; unsigned long data; raw_spin_lock(&timers->lock); init_list(&tmp_head); now = NOW(); while (!is_list_empty(&timers->active)) { timer = list_first_entry(&timers->active, struct timer, entry); if (timer->expires <= (now + DEFAULT_TIMER_MARGIN)) { /* * need to release the spin lock to avoid * dead lock because on the timer handler * function the task may aquire other spinlocks * so load the function and data on the stack. */ timers->running_timer = timer; smp_wmb(); fn = timer->function; data = timer->data; list_del(&timer->entry); timer->entry.next = NULL; raw_spin_unlock(&timers->lock); if (!timer->stop) { fn(data); mb(); } timers->running_timer = NULL; raw_spin_lock(&timers->lock); } else { /* * need to be careful one case, when do the expires * handler, the spin lock will be released, then other * cpu may get this lock to delete the timer from the * active list, if use the .next member to get the next * timer then there will be issues, so if this timer not * expires, then add it to the tmp timer list head, at * the end of this function, switch the actvie to the head */ if (!next_timer || (next_timer->expires > timer->expires)) next_timer = timer; list_del(&timer->entry); list_add_tail(&tmp_head, &timer->entry); } } if (!is_list_empty(&timers->active)) panic("error in timers->active list\n"); /* * swap the tmp head to the active head */ if (!is_list_empty(&tmp_head)) { tmp_head.next->pre = &timers->active; timers->active.next = tmp_head.next; tmp_head.pre->next = &timers->active; timers->active.pre = tmp_head.pre; wmb(); } raw_spin_unlock(&timers->lock); /* * already in interrupt context, will not be interrupted. */ timers->next_timer = next_timer; if (next_timer) enable_timer(next_timer->expires); } static inline int timer_pending(const struct timer * timer) { return ((timer->entry.next) != NULL); } static int detach_timer(struct raw_timer *timers, struct timer *timer) { struct list_head *entry = &timer->entry; if (timer_pending(timer)) { list_del(entry); entry->next = NULL; } return 0; } static int __mod_timer(struct timer *timer) { struct raw_timer *timers = NULL; unsigned long flags; int cpu; preempt_disable(); cpu = smp_processor_id(); /* * if the timer is not on the current cpu's * timers, need to migrate it to the current * cpu's timers list */ ASSERT(!((timer->cpu != -1) && (timer->cpu != cpu))); timers = &get_per_cpu(timers, cpu); spin_lock_irqsave(&timers->lock, flags); detach_timer(timers, timer); timer->stop = 0; timer->raw_timer = timers; smp_wmb(); timer->cpu = cpu; list_add_tail(&timers->active, &timer->entry); /* * reprogram the raw timer if the next expires bigger than * current (expires + DEFAULT_TIMER_MARGIN) */ if (!timers->next_timer || (timers->next_timer->expires > (timer->expires + DEFAULT_TIMER_MARGIN))) { timers->next_timer = timer; enable_timer(timer->expires); } spin_unlock_irqrestore(&timers->lock, flags); preempt_enable(); return 0; } int mod_timer(struct timer *timer, uint64_t cval) { uint64_t now = NOW(); if (cval < (now + TIMER_PRECISION)) timer->expires = now + TIMER_PRECISION; else timer->expires = cval; return __mod_timer(timer); } static int __start_delay_timer(struct timer *timer) { if (timer->timeout < TIMER_PRECISION) timer->timeout = TIMER_PRECISION; timer->expires = NOW() + timer->timeout; return __mod_timer(timer); } void init_timer(struct timer *timer, timer_func_t fn, unsigned long data) { preempt_disable(); timer->cpu = -1; timer->entry.next = NULL; timer->expires = 0; timer->timeout = 0; timer->function = fn; timer->data = data; timer->raw_timer = NULL; preempt_enable(); } int start_timer(struct timer *timer) { return __start_delay_timer(timer); } void setup_timer(struct timer *timer, uint64_t tval) { timer->timeout = tval; } void setup_and_start_timer(struct timer *timer, uint64_t tval) { timer->timeout = tval; start_timer(timer); } int stop_timer(struct timer *timer) { struct raw_timer *timers = timer->raw_timer; unsigned long flags; if (timer->cpu == -1) return 0; timer->stop = 1; ASSERT(timer->raw_timer != NULL); spin_lock_irqsave(&timers->lock, flags); /* * wait the timer finish the action if already * timedout. */ while (timers->running_timer == timer) cpu_relax(); detach_timer(timers, timer); timer->cpu = -1; timer->expires = 0; spin_unlock_irqrestore(&timers->lock, flags); return 0; } static int init_raw_timers(void) { struct raw_timer *timers; int i; for (i = 0; i < CONFIG_NR_CPUS; i++) { timers = &get_per_cpu(timers, i); init_list(&timers->active); timers->next_timer = NULL; timers->running_timer = NULL; spin_lock_init(&timers->lock); } return 0; } arch_initcall(init_raw_timers); ================================================ FILE: kernel/drivers/Kconfig ================================================ menu "Device Drivers" source "drivers/irq-chips/Kconfig" source "drivers/serial/Kconfig" config DEVICE_TREE bool "device tree libfdt support" default y endmenu ================================================ FILE: kernel/drivers/Makefile ================================================ obj-$(CONFIG_SERIAL) += serial/ obj-y += irq-chips/ obj-$(CONFIG_DEVICE_TREE) += of/ obj-y += device_id.o obj-y += tty.o obj-y += console.o ================================================ FILE: kernel/drivers/console.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #define MEM_CONSOLE_SIZE (2048) #define CONSOLE_INBUF_SIZE (2048) static int widx; static char mem_log_buf[MEM_CONSOLE_SIZE]; static char console_inbuf[CONSOLE_INBUF_SIZE]; static uint32_t inbuf_ridx, inbuf_widx; static sem_t console_sem; #define MEM_CONSOLE_IDX(idx) (idx & (MEM_CONSOLE_SIZE - 1)) #define BUFIDX(idx) (idx & (CONSOLE_INBUF_SIZE - 1)) static void mem_console_putc(char ch) { mem_log_buf[MEM_CONSOLE_IDX(widx++)] = ch; } static char mem_console_getc(void) { return 0; } DEFINE_CONSOLE(mem_console, "mem-console", NULL, mem_console_putc, mem_console_getc); static struct console *console = &__console_mem_console; struct console *get_console(char *name) { extern unsigned long __console_start; extern unsigned long __console_end; struct console *console; if (!name) return NULL; section_for_each_item(__console_start, __console_end, console) { if (strcmp(console->name, name) == 0) return console; } return NULL; } void console_init(char *name) { int index; sem_init(&console_sem, 0); if (name) console = get_console(name); if (console->init) console->init(NULL); /* flush the string in the memory log buf */ for (index = 0; index < MEM_CONSOLE_IDX(widx); index++) console->putc(mem_log_buf[index]); } void console_putc(char ch) { console->putc(ch); } char console_getc(void) { return console->getc(); } void console_recv(const char *buf, int cnt) { uint32_t widx; int i; widx = inbuf_widx; rmb(); for (i = 0; i < cnt; i++) console_inbuf[BUFIDX(widx++)] = buf[i]; wmb(); inbuf_widx = widx; sem_post(&console_sem); } void console_puts(char *buf, int len) { puts(buf, len); } int console_gets(char *buf, int max, uint32_t timeout) { uint32_t ridx, widx; long i, copy; do { ridx = inbuf_ridx; widx = inbuf_widx; rmb(); ASSERT((widx - ridx) <= CONSOLE_INBUF_SIZE); copy = widx - ridx > max ? max : widx - ridx; if (copy > 0) { for (i = 0; i < copy; i++) buf[i] = console_inbuf[BUFIDX(ridx++)]; wmb(); inbuf_ridx = ridx; break; } if (timeout > 0) copy = sem_pend(&console_sem, timeout); else break; } while (copy == 0); return copy; } ================================================ FILE: kernel/drivers/device_id.c ================================================ /* * Copyright (C) 2018 - 2019 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include char *gicv2_match_table[] = { "arm,gicv2", "arm,gic-400", "arm,arm11mp-gic", "arm,arm1176jzf-devchip-gic", "arm,cortex-a15-gic", "arm,cortex-a9-gic", "arm,cortex-a7-gic", "qcom,msm-8660-qgic", "qcom,msm-qgic2", NULL }; char *gicv3_match_table[] = { "arm,gic-v3", NULL }; char *bcmirq_match_table[] = { "brcm,bcm2836-armctrl-ic", NULL }; char *pl031_match_table[] = { "arm,pl031", NULL }; char *sp805_match_table[] = { "arm,sp805", NULL }; char *virtio_match_table[] = { "virtio,mmio", NULL }; char *aic_match_table[] = { "aic", NULL }; char *arm_arch_timer_match_table[] = { "arm,armv8-timer", "arm,armv7-timer", NULL, }; ================================================ FILE: kernel/drivers/iommu/Kconfig ================================================ # SPDX-License-Identifier: GPL-2.0 if VIRT menu "IOMMU drivers" config IOMMU def_bool y config IOMMU_IPMMU bool "ipmmu driver" default y if SOC_R8A7795 help iommu driver for r8a7795 endmenu endif comment "IOMMU drivers need virtualization support" depends on !VIRT ================================================ FILE: kernel/drivers/iommu/Makefile ================================================ obj-$(CONFIG_IOMMU_IPMMU) += ipmmu.o obj-$(CONFIG_IOMMU_IPMMU) += ipmmu-plat.o ================================================ FILE: kernel/drivers/iommu/ipmmu-plat.c ================================================ // SPDX-License-Identifier: GPL-2.0 /* * Based on Renesas R-Car System Controller driver (rcar-sysc). */ #include #include #include #include static void *rcar_sysc_base = NULL; /* SYSC MMIO range */ #define RCAR_SYSC_BASE 0xe6180000 #define RCAR_SYSC_SIZE 0x400 /* * These power domain indices match the numbers of the interrupt bits * representing the power areas in the various Interrupt Registers * (e.g. SYSCISR, Interrupt Status Register) */ #define RCAR_GEN3_PD_A3VP 9 #define RCAR_GEN3_PD_A3VC 14 /* Always-on power area */ #define RCAR_GEN3_PD_ALWAYS_ON 32 /* SYSC Common */ #define SYSCSR 0x00 /* SYSC Status Register */ #define SYSCISR 0x04 /* Interrupt Status Register */ #define SYSCISCR 0x08 /* Interrupt Status Clear Register */ #define SYSCIER 0x0c /* Interrupt Enable Register */ #define SYSCIMR 0x10 /* Interrupt Mask Register */ /* SYSC Status Register */ #define SYSCSR_PONENB 1 /* Ready for power resume requests */ /* Power Control Register Offsets inside the register block for each domain */ #define PWRSR_OFFS 0x00 /* Power Status Register */ #define PWRONCR_OFFS 0x0c /* Power Resume Control Register */ #define PWRER_OFFS 0x14 /* Power Shutoff/Resume Error */ #define SYSCSR_RETRIES 1000 #define SYSCSR_DELAY_US 10 #define PWRER_RETRIES 1000 #define PWRER_DELAY_US 10 #define SYSCISR_RETRIES 1000 #define SYSCISR_DELAY_US 10 struct rcar_sysc_ch { const char *name; u16 chan_offs; /* Offset of PWRSR register for this area */ u8 chan_bit; /* Bit in PWR* (except for PWRUP in PWRSR) */ u8 isr_bit; /* Bit in SYSCI*R */ }; /* * For the most of IPMMU-XX which are located in ALWAYS_ON power domain * we don't care at all. But some of them are located in other domains * and must be turned on once at boot. * Hopefully, the each of domains we are dealing with within this file * (A3VP, A3VP) is identically configured across all SoCs (H3, M3 and M3N). * This allow us not to introduce support for each SoC separately. */ static const struct rcar_sysc_ch rcar_sysc_chs[2] = { { .name = "A3VP", .chan_offs = 0x340, .chan_bit = 0, .isr_bit = RCAR_GEN3_PD_A3VP, }, { .name = "A3VC", .chan_offs = 0x380, .chan_bit = 0, .isr_bit = RCAR_GEN3_PD_A3VC, }, }; static int rcar_sysc_init(void) { u32 syscier, syscimr; int i; /* * As this function might be called more then once, just return if we * have already initialized sysc. */ if (rcar_sysc_base) return 0; rcar_sysc_base = (void *)io_remap(RCAR_SYSC_BASE, RCAR_SYSC_SIZE); if (!rcar_sysc_base) { pr_err("failed to map SYSC MMIO range\n"); return -ENOMEM; } syscier = 0; for (i = 0; i < ARRAY_SIZE(rcar_sysc_chs); i++) syscier |= BIT(rcar_sysc_chs[i].isr_bit); /* * Mask all interrupt sources to prevent the CPU from receiving them. * Make sure not to clear reserved bits that were set before. */ syscimr = readl(rcar_sysc_base + SYSCIMR); syscimr |= syscier; writel(syscimr, rcar_sysc_base + SYSCIMR); /* SYSC needs all interrupt sources enabled to control power */ writel(syscier, rcar_sysc_base + SYSCIER); return 0; } static bool rcar_sysc_power_is_off(const struct rcar_sysc_ch *sysc_ch) { unsigned int status; status = readl(rcar_sysc_base + sysc_ch->chan_offs + PWRSR_OFFS); if (status & BIT(sysc_ch->chan_bit)) return true; return false; } static int rcar_sysc_power_on(const struct rcar_sysc_ch *sysc_ch) { unsigned int status; int ret = 0, i, j; writel(BIT(sysc_ch->isr_bit), rcar_sysc_base + SYSCISCR); /* Submit power resume request until it was accepted */ for (i = 0; i < PWRER_RETRIES; i++) { /* Wait until SYSC is ready to accept a power request */ for (j = 0; j < SYSCSR_RETRIES; j++) { if (readl(rcar_sysc_base + SYSCSR) & BIT(SYSCSR_PONENB)) break; udelay(SYSCSR_DELAY_US); } if (j == SYSCSR_RETRIES) return -EAGAIN; /* Submit power resume request */ writel(BIT(sysc_ch->chan_bit), rcar_sysc_base + sysc_ch->chan_offs + PWRONCR_OFFS); status = readl(rcar_sysc_base + sysc_ch->chan_offs + PWRER_OFFS); if (!(status & BIT(sysc_ch->chan_bit))) break; udelay(PWRER_DELAY_US); } if (i == PWRER_RETRIES) return -EIO; /* Wait until the power resume request has completed */ for (i = 0; i < SYSCISR_RETRIES; i++) { if (readl(rcar_sysc_base + SYSCISR) & BIT(sysc_ch->isr_bit)) break; udelay(SYSCISR_DELAY_US); } if (i == SYSCISR_RETRIES) ret = -EIO; writel(BIT(sysc_ch->isr_bit), rcar_sysc_base + SYSCISCR); return ret; } static uint32_t ipmmu_get_mmu_pd(struct device_node *node) { uint32_t value[2]; int ret; ret = of_get_u32_array(node, "power-domains", value, ARRAY_SIZE(value)); if (ret <= 0) return -ENODEV; return value[1]; } /* * Some IPMMU-XX are not located in ALWAYS_ON power domain * (IPMMU-VP0, IPMMU-VC0 belong to A3xx power domains) and as the result * they are in power-off state during booting, therefore they must be * explicitly powered on before initializing. */ int ipmmu_power_on(struct device_node *node) { int i, pd, ret = -ENODEV; pd = ipmmu_get_mmu_pd(node); if (pd < 0 || pd == RCAR_GEN3_PD_ALWAYS_ON) return 0; rcar_sysc_init(); for (i = 0; i < ARRAY_SIZE(rcar_sysc_chs); i++) { if (rcar_sysc_chs[i].isr_bit != pd) continue; if (!rcar_sysc_power_is_off(&rcar_sysc_chs[i])) { pr_notice("%s: %s domain is already powered on\n", devnode_name(node), rcar_sysc_chs[i].name); return 0; } ret = rcar_sysc_power_on(&rcar_sysc_chs[i]); if (ret) { pr_err("%s: failed to power on %s domain\n", devnode_name(node), rcar_sysc_chs[i].name); break; } pr_notice("%s: powered on %s domain\n", devnode_name(node), rcar_sysc_chs[i].name); return 0; } return ret; } /* * Check if we will have to disable IPMMU TLB cache function of IPMMU caches * that belong to non ALWAYS_ON power domain (IPMMU-VP0, IPMMU-VC0 belong * to A3xx power domains) due to H/W restriction. * Required action will be performed right before enabling corresponding * IPMMU-XX. */ bool ipmmu_is_mmu_tlb_disable_needed(struct device_node *node) { int i, pd; pd = ipmmu_get_mmu_pd(node); if (pd < 0 || pd == RCAR_GEN3_PD_ALWAYS_ON) return false; /* Actually check among power domains we have already powered on */ for (i = 0; i < ARRAY_SIZE(rcar_sysc_chs); i++) { if (rcar_sysc_chs[i].isr_bit == pd) return true; } return false; } ================================================ FILE: kernel/drivers/iommu/ipmmu.c ================================================ // SPDX-License-Identifier: GPL-2.0 #include #include #include #include #include extern int ipmmu_power_on(struct device_node *node); extern bool ipmmu_is_mmu_tlb_disable_needed(struct device_node *node); /* * Start of Minos specific code */ /* Minos: Dummy iommu_domain */ struct iommu_domain { atomic_t ref; /* * Used to link iommu_domain contexts for a same VM. * There is at least one per-IPMMU to used by the VM. */ struct list_head list; }; /* Minos: Describes informations required for a Minos VM */ struct vm_ipmmu { spinlock_t lock; /* List of context (i.e iommu_domain) associated to this VM */ struct list_head contexts; struct iommu_domain *base_context; }; #define to_vm_ipmmu(vm) ((struct vm_ipmmu *)(vm)->iommu.priv) #define to_node_ipmmu(node) ((struct node_ipmmu *)(node)->iommu.priv) /* * Start of Linux IPMMU code */ #define IPMMU_CTX_MAX 8 #define IPMMU_PER_DEV_MAX 4 struct ipmmu_device { struct device_node *node; void *base; struct list_head list; bool is_leaf; unsigned int num_utlbs; unsigned int num_ctx; spinlock_t lock; /* Protects ctx and domains[] */ DECLARE_BITMAP(ctx, IPMMU_CTX_MAX); struct ipmmu_domain *domains[IPMMU_CTX_MAX]; }; struct ipmmu_domain { /* Cache IPMMUs the master device can be tied to */ struct ipmmu_device *mmus[IPMMU_PER_DEV_MAX]; unsigned int num_mmus; struct ipmmu_device *root; struct iommu_domain io_domain; unsigned int context_id; spinlock_t lock; /* Protects mappings */ /* Minos: VM associated to this configuration */ struct vm *vm; }; struct ipmmu_utlb { /* Cache IPMMU the uTLB is connected to */ struct ipmmu_device *mmu; unsigned int utlb; }; /* * Minos: Information about each device stored in node->iommu.priv * * On Linux the dev->archdata.iommu only stores the arch specific information, * but, on Minos, we also have to store the iommu domain. */ struct node_ipmmu { struct iommu_domain *io_domain; /* Cache IPMMUs the master device can be tied to */ struct ipmmu_device *mmus[IPMMU_PER_DEV_MAX]; unsigned int num_mmus; struct ipmmu_utlb *utlbs; unsigned int num_utlbs; struct device_node *node; struct list_head list; }; static DEFINE_SPIN_LOCK(ipmmu_devices_lock); static LIST_HEAD(ipmmu_devices); #define to_ipmmu_domain(io_dom) \ (container_of((io_dom), struct ipmmu_domain, io_domain)) #define TLB_LOOP_TIMEOUT 100 /* 100us */ /* * Registers Definition */ #define IM_CTX_SIZE 0x40 #define IMCTR 0x0000 #define IMCTR_VA64 (1 << 29) #define IMCTR_INTEN (1 << 2) #define IMCTR_FLUSH (1 << 1) #define IMCTR_MMUEN (1 << 0) #define IMTTBCR 0x0008 #define IMTTBCR_EAE (1 << 31) #define IMTTBCR_PMB (1 << 30) #define IMTTBCR_SH0_INNER_SHAREABLE (3 << 12) #define IMTTBCR_ORGN0_WB_WA (1 << 10) #define IMTTBCR_IRGN0_WB_WA (1 << 8) #define IMTTBCR_TSZ0_SHIFT 0 #define IMTTBCR_SL0_TWOBIT_LVL_1 (2 << 6) #define IMTTLBR0 0x0010 #define IMTTUBR0 0x0014 #define IMTTLBR_MASK 0xFFFFF000 #define IMSTR 0x0020 #define IMSTR_MHIT (1 << 4) #define IMSTR_ABORT (1 << 2) #define IMSTR_PF (1 << 1) #define IMSTR_TF (1 << 0) #define IMEAR 0x0030 #define IMEUAR 0x0034 #define IMUCTR(n) ((n) < 32 ? IMUCTR0(n) : IMUCTR32(n)) #define IMUCTR0(n) (0x0300 + ((n) * 16)) #define IMUCTR32(n) (0x0600 + (((n) - 32) * 16)) #define IMUCTR_TTSEL_MMU(n) ((n) << 4) #define IMUCTR_FLUSH (1 << 1) #define IMUCTR_MMUEN (1 << 0) #define IMUASID(n) ((n) < 32 ? IMUASID0(n) : IMUASID32(n)) #define IMUASID0(n) (0x0308 + ((n) * 16)) #define IMUASID32(n) (0x0608 + (((n) - 32) * 16)) #define IMSCTLR 0x0500 #define IMSCTLR_DISCACHE 0xE0000000 #define IMSAUXCTLR 0x0504 #define IMSAUXCTLR_S2PTE (1 << 3) /* * Root device handling */ static bool ipmmu_is_root(struct ipmmu_device *mmu) { /* Minos: Fix */ return mmu && !mmu->is_leaf; } static struct ipmmu_device *ipmmu_find_root(struct ipmmu_device *leaf) { struct ipmmu_device *mmu = NULL; if (ipmmu_is_root(leaf)) return leaf; spin_lock(&ipmmu_devices_lock); list_for_each_entry (mmu, &ipmmu_devices, list) { if (ipmmu_is_root(mmu)) break; } spin_unlock(&ipmmu_devices_lock); return mmu; } /* * Read/Write Access */ static u32 ipmmu_read(struct ipmmu_device *mmu, unsigned int offset) { return ioread32(mmu->base + offset); } static void ipmmu_write(struct ipmmu_device *mmu, unsigned int offset, u32 data) { iowrite32(data, mmu->base + offset); } static u32 ipmmu_ctx_read_root(struct ipmmu_domain *domain, unsigned int reg) { return ipmmu_read(domain->root, domain->context_id * IM_CTX_SIZE + reg); } static void ipmmu_ctx_write_root(struct ipmmu_domain *domain, unsigned int reg, u32 data) { ipmmu_write(domain->root, domain->context_id * IM_CTX_SIZE + reg, data); } /* Minos: Write the context for cache IPMMU only. */ static void ipmmu_ctx_write_cache(struct ipmmu_domain *domain, unsigned int reg, u32 data) { unsigned int i; for (i = 0; i < domain->num_mmus; i++) ipmmu_write(domain->mmus[i], domain->context_id * IM_CTX_SIZE + reg, data); } /* * Minos: Write the context for both root IPMMU and all cache IPMMUs * that assigned to this Minos VM. */ static void ipmmu_ctx_write_all(struct ipmmu_domain *domain, unsigned int reg, u32 data) { struct vm_ipmmu *vm_ipmmu = to_vm_ipmmu(domain->vm); struct iommu_domain *io_domain; list_for_each_entry (io_domain, &vm_ipmmu->contexts, list) ipmmu_ctx_write_cache(to_ipmmu_domain(io_domain), reg, data); ipmmu_ctx_write_root(domain, reg, data); } /* * TLB and microTLB Management */ /* Wait for any pending TLB invalidations to complete */ static void ipmmu_tlb_sync(struct ipmmu_domain *domain) { unsigned int count = 0; while (ipmmu_ctx_read_root(domain, IMCTR) & IMCTR_FLUSH) { cpu_relax(); if (++count == TLB_LOOP_TIMEOUT) { pr_err("%s: TLB sync timed out -- MMU may be deadlocked\n", devnode_name(domain->root->node)); return; } udelay(1); } } static void ipmmu_tlb_invalidate(struct ipmmu_domain *domain) { u32 reg; reg = ipmmu_ctx_read_root(domain, IMCTR); reg |= IMCTR_FLUSH; ipmmu_ctx_write_all(domain, IMCTR, reg); ipmmu_tlb_sync(domain); } /* Enable MMU translation for the microTLB. */ static void ipmmu_utlb_enable(struct ipmmu_domain *domain, struct ipmmu_utlb *utlb_p) { struct ipmmu_device *mmu = utlb_p->mmu; unsigned int utlb = utlb_p->utlb; /* * TODO: Reference-count the microTLB as several bus masters can be * connected to the same microTLB. */ /* TODO: What should we set the ASID to ? */ ipmmu_write(mmu, IMUASID(utlb), 0); /* TODO: Do we need to flush the microTLB ? */ ipmmu_write(mmu, IMUCTR(utlb), IMUCTR_TTSEL_MMU(domain->context_id) | IMUCTR_FLUSH | IMUCTR_MMUEN); } /* * Domain/Context Management */ static int ipmmu_domain_allocate_context(struct ipmmu_device *mmu, struct ipmmu_domain *domain) { unsigned long flags; int ret; spin_lock_irqsave(&mmu->lock, flags); ret = find_first_zero_bit(mmu->ctx, mmu->num_ctx); if (ret != mmu->num_ctx) { mmu->domains[ret] = domain; set_bit(ret, mmu->ctx); } else ret = -EBUSY; spin_unlock_irqrestore(&mmu->lock, flags); return ret; } static int ipmmu_domain_init_context(struct ipmmu_domain *domain) { u64 ttbr; u32 tmp; int ret; /* Minos: Initialize context_id with non-existent value */ domain->context_id = domain->root->num_ctx; /* Find an unused context. */ ret = ipmmu_domain_allocate_context(domain->root, domain); if (ret < 0) return ret; domain->context_id = ret; /* * TTBR0 * Use P2M table. With IPA size being forced to 40 bit (pa_range = 2) * we get 3-level P2M with two concatenated translation tables * at level 1. Which seems to be an appropriate case for the IPMMU. */ BUG_ON(!domain->vm); ttbr = domain->vm->mm.pgd_base; /* Minos: */ pr_notice("%s: vm%d: Set IPMMU context %u (pgd 0x%x)\n", devnode_name(domain->root->node), vm_id(domain->vm), domain->context_id, ttbr); ipmmu_ctx_write_root(domain, IMTTLBR0, ttbr & IMTTLBR_MASK); ipmmu_ctx_write_root(domain, IMTTUBR0, ttbr >> 32); /* * TTBCR * We use long descriptors with inner-shareable WBWA tables and allocate * the whole 40-bit VA space to TTBR0. * Bypass stage 1 translation. */ tmp = IMTTBCR_SL0_TWOBIT_LVL_1; tmp |= (64ULL - 40ULL) << IMTTBCR_TSZ0_SHIFT; ipmmu_ctx_write_root( domain, IMTTBCR, IMTTBCR_EAE | IMTTBCR_PMB | IMTTBCR_SH0_INNER_SHAREABLE | IMTTBCR_ORGN0_WB_WA | IMTTBCR_IRGN0_WB_WA | tmp); /* * IMSTR * Clear all interrupt flags. */ ipmmu_ctx_write_root(domain, IMSTR, ipmmu_ctx_read_root(domain, IMSTR)); /* * IMCTR * Enable the MMU and interrupt generation. The long-descriptor * translation table format doesn't use TEX remapping. Don't enable AF * software management as we have no use for it. Flush the TLB as * required when modifying the context registers. * Minos: Enable the context for the root IPMMU only. */ ipmmu_ctx_write_root(domain, IMCTR, IMCTR_VA64 | IMCTR_INTEN | IMCTR_FLUSH | IMCTR_MMUEN); return 0; } static void ipmmu_domain_free_context(struct ipmmu_device *mmu, unsigned int context_id) { unsigned long flags; spin_lock_irqsave(&mmu->lock, flags); clear_bit(context_id, mmu->ctx); mmu->domains[context_id] = NULL; spin_unlock_irqrestore(&mmu->lock, flags); } static void ipmmu_domain_destroy_context(struct ipmmu_domain *domain) { /* Minos: Just return if context_id has non-existent value */ if (!domain->root || domain->context_id >= domain->root->num_ctx) return; /* * Disable the context. Flush the TLB as required when modifying the * context registers. * * TODO: Is TLB flush really needed ? * Minos: Disable the context for the root IPMMU only. */ ipmmu_ctx_write_root(domain, IMCTR, IMCTR_FLUSH); ipmmu_tlb_sync(domain); ipmmu_domain_free_context(domain->root, domain->context_id); /* Minos: Initialize context_id with non-existent value */ domain->context_id = domain->root->num_ctx; } /* * Fault Handling */ /* Minos: Show vmid in every printk */ static int ipmmu_domain_irq(struct ipmmu_domain *domain) { const u32 err_mask = IMSTR_MHIT | IMSTR_ABORT | IMSTR_PF | IMSTR_TF; struct ipmmu_device *mmu = domain->root; u32 status; u64 iova; status = ipmmu_ctx_read_root(domain, IMSTR); if (!(status & err_mask)) return -ENOENT; iova = ipmmu_ctx_read_root(domain, IMEAR) | ((u64)ipmmu_ctx_read_root(domain, IMEUAR) << 32); /* * Clear the error status flags. Unlike traditional interrupt flag * registers that must be cleared by writing 1, this status register * seems to require 0. The error address register must be read before, * otherwise its value will be 0. */ ipmmu_ctx_write_root(domain, IMSTR, 0); /* Log fatal errors. */ if (status & IMSTR_MHIT) pr_err("%s: vm%d: Multiple TLB hits @0x%x\n", devnode_name(mmu->node), vm_id(domain->vm), iova); if (status & IMSTR_ABORT) pr_err("%s: vm%d: Page Table Walk Abort @0x%x\n", devnode_name(mmu->node), vm_id(domain->vm), iova); if (!(status & (IMSTR_PF | IMSTR_TF))) return -ENOENT; /* Flush the TLB as required when IPMMU translation error occurred. */ ipmmu_tlb_invalidate(domain); /* * Try to handle page faults and translation faults. * * TODO: We need to look up the faulty device based on the I/O VA. Use * the IOMMU device for now. */ pr_err("%s: vm%d: Unhandled fault: status 0x%x iova 0x%x\n", devnode_name(mmu->node), vm_id(domain->vm), status, iova); return 0; } static int ipmmu_irq(uint32_t irq, void *dev) { struct ipmmu_device *mmu = dev; int status = -ENOENT; unsigned int i; unsigned long flags; spin_lock_irqsave(&mmu->lock, flags); /* Check interrupts for all active contexts. */ for (i = 0; i < mmu->num_ctx; i++) { if (!mmu->domains[i]) continue; if (ipmmu_domain_irq(mmu->domains[i]) == 0) status = 0; } spin_unlock_irqrestore(&mmu->lock, flags); return status; } bool ipmmus_are_equal(struct ipmmu_domain *domain, struct node_ipmmu *node_ipmmu) { unsigned int i; if (domain->num_mmus != node_ipmmu->num_mmus) return false; for (i = 0; i < node_ipmmu->num_mmus; i++) { if (domain->mmus[i] != node_ipmmu->mmus[i]) return false; } return true; } static int ipmmu_attach_node(struct iommu_domain *io_domain, struct device_node *node) { struct node_ipmmu *node_ipmmu = to_node_ipmmu(node); struct ipmmu_device *root; struct ipmmu_domain *domain = to_ipmmu_domain(io_domain); unsigned long flags; unsigned int i; int ret = 0; for (i = 0; i < node_ipmmu->num_mmus; i++) { if (!node_ipmmu->mmus[i]) break; } if (!node_ipmmu->num_mmus || i != node_ipmmu->num_mmus) { pr_err("%s: Cannot attach to IPMMU\n", devnode_name(node)); return -ENXIO; } root = ipmmu_find_root(node_ipmmu->mmus[0]); if (!root) { pr_err("%s: Unable to locate root IPMMU\n", devnode_name(node)); return -EAGAIN; } spin_lock_irqsave(&domain->lock, flags); if (!domain->mmus[0]) { /* The domain hasn't been used yet, initialize it. */ domain->num_mmus = node_ipmmu->num_mmus; memcpy(domain->mmus, node_ipmmu->mmus, node_ipmmu->num_mmus * sizeof(*node_ipmmu->mmus)); domain->root = root; /* * Minos: We have already initialized and enabled context for root IPMMU * for this Minos VM. Enable context for given cache IPMMU only. * Flush the TLB as required when modifying the context registers. */ ipmmu_ctx_write_cache(domain, IMCTR, ipmmu_ctx_read_root(domain, IMCTR) | IMCTR_FLUSH); pr_info("%s: Using IPMMU context %u\n", devnode_name(node), domain->context_id); } else if (!ipmmus_are_equal(domain, node_ipmmu)) { /* * Something is wrong, we can't attach two devices using * different IOMMUs to the same domain. */ for (i = 0; i < node_ipmmu->num_mmus || i < domain->num_mmus; i++) pr_err("%s: Can't attach IPMMU%d %s to domain on IPMMU%d %s\n", devnode_name(node), i + 1, i < node_ipmmu->num_mmus ? devnode_name(node_ipmmu->mmus[i]->node) : "---", i + 1, i < domain->num_mmus ? devnode_name(domain->mmus[i]->node) : "---"); ret = -EINVAL; } else { pr_info("%s: Reusing IPMMU context %u\n", devnode_name(node), domain->context_id); } spin_unlock_irqrestore(&domain->lock, flags); if (ret < 0) return ret; for (i = 0; i < node_ipmmu->num_utlbs; ++i) ipmmu_utlb_enable(domain, &node_ipmmu->utlbs[i]); return 0; } static int ipmmu_find_utlbs(struct device_node *node, struct ipmmu_utlb *utlbs, unsigned int num_utlbs) { unsigned int i; int ret = -ENODEV; uint32_t iommus[num_utlbs * 2]; ret = of_get_u32_array(node, "iommus", iommus, ARRAY_SIZE(iommus)); if (ret != ARRAY_SIZE(iommus)) return -ENODEV; spin_lock(&ipmmu_devices_lock); for (i = 0; i < num_utlbs; ++i) { struct ipmmu_device *mmu; uint32_t phandle; ret = -ENODEV; list_for_each_entry (mmu, &ipmmu_devices, list) { of_get_u32_array(mmu->node, "phandle", &phandle, 1); if (!phandle || phandle != iommus[i * 2]) continue; /* * TODO Take a reference to the MMU to protect * against device removal. */ ret = 0; break; } if (ret < 0) break; utlbs[i].utlb = iommus[i * 2 + 1]; utlbs[i].mmu = mmu; } spin_unlock(&ipmmu_devices_lock); return ret; } static int ipmmu_node_init(struct device_node *node) { struct node_ipmmu *node_ipmmu; struct ipmmu_device *mmus[IPMMU_PER_DEV_MAX]; struct ipmmu_utlb *utlbs; unsigned int i; int num_utlbs; int num_mmus; int ret; of32_t *iommus; int len; /* Find the master corresponding to the device. */ iommus = of_getprop(node, "iommus", &len); if (!iommus || len < sizeof(*iommus)) return -ENODEV; num_utlbs = len / sizeof(*iommus) / 2; utlbs = zalloc(num_utlbs * sizeof(*utlbs)); if (!utlbs) return -ENOMEM; ret = ipmmu_find_utlbs(node, utlbs, num_utlbs); if (ret < 0) goto error; num_mmus = 0; for (i = 0; i < num_utlbs; i++) { if (!utlbs[i].mmu || utlbs[i].utlb >= utlbs[i].mmu->num_utlbs) { ret = -EINVAL; goto error; } if (!num_mmus || mmus[num_mmus - 1] != utlbs[i].mmu) { if (num_mmus >= IPMMU_PER_DEV_MAX) { ret = -EINVAL; goto error; } else { num_mmus++; mmus[num_mmus - 1] = utlbs[i].mmu; } } } node_ipmmu = zalloc(sizeof(*node_ipmmu)); if (!node_ipmmu) { ret = -ENOMEM; goto error; } node_ipmmu->num_mmus = num_mmus; memcpy(node_ipmmu->mmus, mmus, num_mmus * sizeof(*mmus)); node_ipmmu->utlbs = utlbs; node_ipmmu->num_utlbs = num_utlbs; node_ipmmu->node = node; node->iommu.priv = node_ipmmu; /* Minos: */ pr_notice("%s: Initialized master device (IPMMUs %u micro-TLBs %u)\n", devnode_name(node), num_mmus, num_utlbs); for (i = 0; i < num_mmus; i++) pr_notice("%s: IPMMU%d: %s\n", devnode_name(node), i + 1, devnode_name(mmus[i]->node)); return 0; error: free(utlbs); return ret; } /* * Probe/remove and init */ static void ipmmu_device_reset(struct ipmmu_device *mmu) { unsigned int i; /* Disable all contexts. */ for (i = 0; i < mmu->num_ctx; ++i) ipmmu_write(mmu, i * IM_CTX_SIZE + IMCTR, 0); } /* * Minos: We don't have refcount for allocated memory so manually free memory * when an error occured. */ static int ipmmu_probe(struct device_node *node) { struct ipmmu_device *mmu; phy_addr_t address; size_t size; int len; uint32_t irq; unsigned long flags; int ret; mmu = zalloc(sizeof(*mmu)); if (!mmu) { pr_err("%s: cannot allocate device data\n", devnode_name(node)); return -ENOMEM; } mmu->node = node; mmu->num_utlbs = 48; spin_lock_init(&mmu->lock); bitmap_zero(mmu->ctx, IPMMU_CTX_MAX); /* Map I/O memory and request IRQ. */ ret = of_translate_address(node, &address, &size); if (ret || !size) { ret = -ENOENT; goto out; } mmu->base = (void *)io_remap(address, size); if (!mmu->base) { ret = -ENOMEM; goto out; } /* * The number of contexts varies with generation and instance. * Newer SoCs get a total of 8 contexts enabled, older ones just one. */ mmu->num_ctx = 8; BUG_ON(mmu->num_ctx > IPMMU_CTX_MAX); /* * Determine if this IPMMU instance is a leaf device by checking * if the renesas,ipmmu-main property exists or not. */ if (of_getprop(node, "renesas,ipmmu-main", &len)) mmu->is_leaf = true; /* Root devices have mandatory IRQs */ if (ipmmu_is_root(mmu)) { ret = get_device_irq_index(node, &irq, &flags, 0); if (ret) { pr_err("%s: no IRQ found\n", devnode_name(node)); goto out; } ret = request_irq(irq, ipmmu_irq, flags, NULL, mmu); if (ret) { pr_err("%s: failed to request IRQ %d\n", devnode_name(node), irq); goto out; } ipmmu_device_reset(mmu); /* Use stage 2 translation table format */ ipmmu_write(mmu, IMSAUXCTLR, ipmmu_read(mmu, IMSAUXCTLR) | IMSAUXCTLR_S2PTE); } else { /* Only IPMMU caches are affected */ /* * Disable IPMMU TLB cache function of IPMMU caches * that do require such action. */ if (ipmmu_is_mmu_tlb_disable_needed(node)) ipmmu_write(mmu, IMSCTLR, ipmmu_read(mmu, IMSCTLR) | IMSCTLR_DISCACHE); } spin_lock(&ipmmu_devices_lock); list_add(&ipmmu_devices, &mmu->list); spin_unlock(&ipmmu_devices_lock); /* Minos: */ pr_notice("%s: registered %s IPMMU\n", devnode_name(node), ipmmu_is_root(mmu) ? "root" : "cache"); return 0; out: if (!mmu->base) io_unmap((unsigned long)mmu->base, size); free(mmu); return ret; } /* * Start of Minos specific code */ static int ipmmu_iotlb_flush_all(struct vm *vm) { struct vm_ipmmu *vm_ipmmu = to_vm_ipmmu(vm); if (!vm_ipmmu || !vm_ipmmu->base_context) return 0; spin_lock(&vm_ipmmu->lock); ipmmu_tlb_invalidate(to_ipmmu_domain(vm_ipmmu->base_context)); spin_unlock(&vm_ipmmu->lock); return 0; } static struct iommu_domain *ipmmu_get_domain(struct vm *vm, struct device_node *node) { struct vm_ipmmu *vm_ipmmu = to_vm_ipmmu(vm); struct iommu_domain *io_domain; if (!to_node_ipmmu(node)->mmus[0] || !to_node_ipmmu(node)->num_mmus) return NULL; /* * Loop through the &vm_ipmmu->contexts to locate a context * assigned to this IPMMU */ list_for_each_entry (io_domain, &vm_ipmmu->contexts, list) { if (ipmmus_are_equal(to_ipmmu_domain(io_domain), to_node_ipmmu(node))) return io_domain; } return NULL; } static void ipmmu_destroy_domain(struct iommu_domain *io_domain) { struct ipmmu_domain *domain = to_ipmmu_domain(io_domain); list_del(&io_domain->list); if (domain->num_mmus) { /* * Disable the context for cache IPMMU only. Flush the TLB as required * when modifying the context registers. */ ipmmu_ctx_write_cache(domain, IMCTR, IMCTR_FLUSH); } else { /* * Free main domain resources. We assume that all devices have already * been detached. */ ipmmu_domain_destroy_context(domain); } free(domain); } static int ipmmu_alloc_page_table(struct vm *vm); static int ipmmu_assign_node(struct vm *vm, struct device_node *node) { struct vm_ipmmu *vm_ipmmu = to_vm_ipmmu(vm); struct iommu_domain *io_domain; struct ipmmu_domain *domain; int ret = 0; if (!vm_ipmmu) return -EINVAL; if (!vm_ipmmu->base_context) { ret = ipmmu_alloc_page_table(vm); if (ret) return ret; } if (!to_node_ipmmu(node)) { ret = ipmmu_node_init(node); if (ret) return ret; } spin_lock(&vm_ipmmu->lock); if (to_node_ipmmu(node)->io_domain) { pr_err("%s: already attached to IPMMU domain\n", devnode_name(node)); ret = -EEXIST; goto out; } /* * Check to see if a context bank (iommu_domain) already exists for * this Minos VM under the same IPMMU */ io_domain = ipmmu_get_domain(vm, node); if (!io_domain) { domain = zalloc(sizeof(*domain)); if (!domain) { ret = -ENOMEM; goto out; } spin_lock_init(&domain->lock); domain->vm = vm; domain->context_id = to_ipmmu_domain(vm_ipmmu->base_context)->context_id; io_domain = &domain->io_domain; /* Chain the new context to the Minos VM */ list_add(&vm_ipmmu->contexts, &io_domain->list); } ret = ipmmu_attach_node(io_domain, node); if (ret) { if (atomic_read(&io_domain->ref) == 0) ipmmu_destroy_domain(io_domain); } else { atomic_inc(&io_domain->ref); to_node_ipmmu(node)->io_domain = io_domain; } out: spin_unlock(&vm_ipmmu->lock); return ret; } static int ipmmu_alloc_page_table(struct vm *vm) { struct vm_ipmmu *vm_ipmmu = to_vm_ipmmu(vm); struct ipmmu_domain *domain; struct ipmmu_device *root; int ret; root = ipmmu_find_root(NULL); if (!root) { pr_err("vm%d: Unable to locate root IPMMU\n", vm_id(vm)); return -EAGAIN; } domain = zalloc(sizeof(*domain)); if (!domain) return -ENOMEM; spin_lock_init(&domain->lock); init_list(&domain->io_domain.list); domain->vm = vm; domain->root = root; /* Clear num_mmus explicitly. */ domain->num_mmus = 0; spin_lock(&vm_ipmmu->lock); ret = ipmmu_domain_init_context(domain); if (ret < 0) { pr_err("%s: vm%d: Unable to initialize IPMMU context\n", devnode_name(root->node), vm_id(vm)); spin_unlock(&vm_ipmmu->lock); free(domain); return ret; } vm_ipmmu->base_context = &domain->io_domain; spin_unlock(&vm_ipmmu->lock); return 0; } static int ipmmu_vm_init(struct vm *vm) { struct vm_ipmmu *vm_ipmmu; vm_ipmmu = zalloc(sizeof(*vm_ipmmu)); if (!vm_ipmmu) return -ENOMEM; spin_lock_init(&vm_ipmmu->lock); init_list(&vm_ipmmu->contexts); vm->iommu.priv = vm_ipmmu; return 0; } static void *populate_ipmmu_masters(struct device_node *node, void *arg) { struct device_node *ipmmu_node = arg; uint32_t phandle; uint32_t iommus; of_get_u32_array(ipmmu_node, "phandle", &phandle, 1); of_get_u32_array(node, "iommus", &iommus, 1); if (!phandle || phandle != iommus) return NULL; pr_notice("%s: found master device %s\n", devnode_name(ipmmu_node), devnode_name(node)); return node; } static int ipmmu_init(struct device_node *node) { int rc; /* * Perform platform specific actions such as power-on, errata maintenance * if required. */ rc = ipmmu_power_on(node); if (rc) { pr_err("%s: failed to preinit IPMMU (%d)\n", devnode_name(node), rc); return rc; } rc = ipmmu_probe(node); if (rc) { pr_err("%s: failed to init IPMMU\n", devnode_name(node)); return rc; } of_iterate_all_node_loop(hv_node, populate_ipmmu_masters, node); return 0; } static struct iommu_ops ipmmu_ops = { .init = ipmmu_init, .vm_init = ipmmu_vm_init, .iotlb_flush_all = ipmmu_iotlb_flush_all, .assign_node = ipmmu_assign_node, }; IOMMU_OPS_DECLARE(ipmmu_ops, ipmmu_match_table, (void *)&ipmmu_ops); ================================================ FILE: kernel/drivers/irq-chips/Kconfig ================================================ menu "Interrupt Controller Driver" config IRQCHIP_GICV3 bool "gicv3 driver" default n help "GICv3 interrupt controller driver" config IRQCHIP_GICV2 bool "gicv2 driver" default n help "GICv2 interrupt controller driver" config IRQCHIP_BCM2836 bool "bcm2836/2835 interrupt controller driver" default n help "interrupt controller driver for raspberry PI 3" endmenu ================================================ FILE: kernel/drivers/irq-chips/Makefile ================================================ obj-y += irqchip.o obj-$(CONFIG_IRQCHIP_GICV3) += gicv3.o obj-$(CONFIG_IRQCHIP_GICV2) += gicv2.o obj-$(CONFIG_IRQCHIP_BCM2836) += irq-bcm2836.o ================================================ FILE: kernel/drivers/irq-chips/gicv2.c ================================================ /* * xen/arch/arm/gic-v2.c * * Tim Deegan * Copyright (c) 2011 Citrix Systems. * * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include /* * LR register definitions are GIC v2 specific. * Moved these definitions from header file to here */ #define GICH_V2_LR_VIRTUAL_MASK 0x3ff #define GICH_V2_LR_VIRTUAL_SHIFT 0 #define GICH_V2_LR_PHYSICAL_MASK 0x3ff #define GICH_V2_LR_PHYSICAL_SHIFT 10 #define GICH_V2_LR_STATE_MASK 0x3 #define GICH_V2_LR_STATE_SHIFT 28 #define GICH_V2_LR_PENDING (1U << 28) #define GICH_V2_LR_ACTIVE (1U << 29) #define GICH_V2_LR_PRIORITY_SHIFT 23 #define GICH_V2_LR_PRIORITY_MASK 0x1f #define GICH_V2_LR_HW_SHIFT 31 #define GICH_V2_LR_HW_MASK 0x1 #define GICH_V2_LR_GRP_SHIFT 30 #define GICH_V2_LR_GRP_MASK 0x1 #define GICH_V2_LR_MAINTENANCE_IRQ (1U << 19) #define GICH_V2_LR_GRP1 (1U << 30) #define GICH_V2_LR_HW (1U << GICH_V2_LR_HW_SHIFT) #define GICH_V2_LR_CPUID_SHIFT 10 #define GICH_V2_LR_CPUID_MASK 0x7 #define GICH_V2_VTR_NRLRGS 0x3f #define GICH_V2_VMCR_PRIORITY_MASK 0x1f #define GICH_V2_VMCR_PRIORITY_SHIFT 27 #define GIC_PRI_LOWEST 0xf0 #define GIC_PRI_IRQ 0xa0 #define GIC_PRI_IPI 0x90 /* IPIs must preempt normal interrupts */ #define GIC_PRI_HIGHEST 0x80 /* Higher priorities belong to Secure-World */ static DEFINE_SPIN_LOCK(gicv2_lock); static void *gicv2_dbase; static void *gicv2_cbase; static void *gicv2_hbase; static int gicv2_nr_lines; static uint8_t gic_cpu_mask[8] = { 0xff }; extern int vgicv2_init(uint64_t *data, int len); extern int gic_xlate_irq(struct device_node *node, uint32_t *intspec, unsigned int intsize, uint32_t *hwirq, unsigned long *type); /* Maximum cpu interface per GIC */ #define NR_GIC_CPU_IF 8 static inline void writeb_gicd(uint8_t val, unsigned int offset) { writeb_relaxed(val, gicv2_dbase + offset); } static inline void writel_gicd(uint32_t val, unsigned int offset) { writel_relaxed(val, gicv2_dbase + offset); } static inline uint32_t readl_gicd(unsigned int offset) { return readl_relaxed(gicv2_dbase + offset); } static inline void writel_gicc(uint32_t val, unsigned int offset) { writel_relaxed(val, gicv2_cbase + offset); } static inline uint32_t readl_gicc(unsigned int offset) { return readl_relaxed(gicv2_cbase + offset); } static inline void writel_gich(uint32_t val, unsigned int offset) { writel_relaxed(val, gicv2_hbase + offset); } static inline uint32_t readl_gich(int unsigned offset) { return readl_relaxed(gicv2_hbase + offset); } static void gicv2_eoi_irq(uint32_t irq) { writel_gicc(irq, GICC_EOIR); dsb(); } static void gicv2_dir_irq(uint32_t irq) { writel_gicc(irq, GICC_DIR); dsb(); } static uint32_t gicv2_read_irq(void) { uint32_t irq; irq = readl_gicc(GICC_IAR); isb(); irq = irq & GICC_IA_IRQ; return irq; } static int gicv2_set_irq_type(uint32_t irq, uint32_t type) { uint32_t cfg, edgebit; if (irq < 16) return 0; spin_lock(&gicv2_lock); /* Set edge / level */ cfg = readl_gicd(GICD_ICFGR + (irq / 16) * 4); edgebit = 2u << (2 * (irq % 16)); if ( type & IRQ_FLAGS_LEVEL_BOTH) cfg &= ~edgebit; else if (type & IRQ_FLAGS_EDGE_BOTH) cfg |= edgebit; writel_gicd(cfg, GICD_ICFGR + (irq / 16) * 4); spin_unlock(&gicv2_lock); return 0; } static void __used gicv2_clear_pending(uint32_t irq) { writel_gicd(1UL << (irq % 32), GICD_ICPENDR + (irq / 32) * 4); dsb(); } static int gicv2_set_irq_priority(uint32_t irq, uint32_t pr) { spin_lock(&gicv2_lock); /* Set priority */ writeb_gicd(pr, GICD_IPRIORITYR + irq); spin_unlock(&gicv2_lock); return 0; } static int gicv2_set_irq_affinity(uint32_t irq, uint32_t pcpu) { if (pcpu > NR_GIC_CPU_IF || irq < 32) return -EINVAL; spin_lock(&gicv2_lock); /* Set target CPU mask (RAZ/WI on uniprocessor) */ writeb_gicd(1 << pcpu, GICD_ITARGETSR + irq); spin_unlock(&gicv2_lock); return 0; } static void gicv2_send_sgi(uint32_t sgi, enum sgi_mode mode, cpumask_t *mask) { unsigned int cpu; unsigned int value = 0; switch (mode) { case SGI_TO_OTHERS: writel_gicd(GICD_SGI_TARGET_OTHERS | sgi, GICD_SGIR); break; case SGI_TO_SELF: writel_gicd(GICD_SGI_TARGET_SELF | sgi, GICD_SGIR); break; case SGI_TO_LIST: for_each_cpu(cpu, mask) value |= gic_cpu_mask[cpu]; writel_gicd(GICD_SGI_TARGET_LIST | (value << GICD_SGI_TARGET_SHIFT) | sgi, GICD_SGIR); isb(); break; default: break;; } } static void gicv2_mask_irq(uint32_t irq) { unsigned long flags; spin_lock_irqsave(&gicv2_lock, flags); writel_gicd(1UL << (irq % 32), GICD_ICENABLER + (irq / 32) * 4); dsb(); spin_unlock_irqrestore(&gicv2_lock, flags); } static void gicv2_unmask_irq(uint32_t irq) { unsigned long flags; spin_lock_irqsave(&gicv2_lock, flags); writel_gicd(1UL << (irq % 32), GICD_ISENABLER + (irq / 32) * 4); dsb(); spin_unlock_irqrestore(&gicv2_lock, flags); } static void gicv2_mask_irq_cpu(uint32_t irq, int cpu) { pr_warn("not support mask irq_percpu\n"); } static void gicv2_unmask_irq_cpu(uint32_t irq, int cpu) { pr_warn("not support unmask irq_percpu\n"); } static int __init_text gicv2_is_aliased(unsigned long base, unsigned long size) { uint32_t val_low, val_high; if (size != SIZE_1K * 128) return 0; val_low = readl_gicc(GICC_IIDR); val_high = readl_gicc(GICC_IIDR + 0xf000); return ((val_low & 0xfff0fff) == 0x0202043B && val_low == val_high); } static void __init_text gicv2_cpu_init(void) { int i; int cpuid = smp_processor_id(); gic_cpu_mask[cpuid] = readl_gicd(GICD_ITARGETSR) & 0xff; pr_debug("gicv2 gic mask of cpu%d: 0x%x\n", cpuid, gic_cpu_mask[cpuid]); if (gic_cpu_mask[cpuid] == 0) gic_cpu_mask[cpuid] = 1 << cpuid; /* The first 32 interrupts (PPI and SGI) are banked per-cpu, so * even though they are controlled with GICD registers, they must * be set up here with the other per-cpu state. */ writel_gicd(0xffff0000, GICD_ICENABLER); /* Disable all PPI */ writel_gicd(0x0000ffff, GICD_ISENABLER); /* Enable all SGI */ /* Set SGI priorities */ for ( i = 0; i < 16; i += 4 ) writel_gicd(GIC_PRI_IPI << 24 | GIC_PRI_IPI << 16 | GIC_PRI_IPI << 8 | GIC_PRI_IPI, GICD_IPRIORITYR + (i / 4) * 4); /* Set PPI priorities */ for ( i = 16; i < 32; i += 4 ) writel_gicd(GIC_PRI_IRQ << 24 | GIC_PRI_IRQ << 16 | GIC_PRI_IRQ << 8 | GIC_PRI_IRQ, GICD_IPRIORITYR + (i / 4) * 4); /* Local settings: interface controller */ /* Don't mask by priority */ writel_gicc(0xff, GICC_PMR); /* Finest granularity of priority */ writel_gicc(0x0, GICC_BPR); /* Turn on delivery */ writel_gicc(GICC_CTL_ENABLE|GICC_CTL_EOI, GICC_CTLR); dsb(); } #ifdef CONFIG_VIRT static void __init_text gicv2_hyp_init(void) { } #endif static void __init_text gicv2_dist_init(void) { uint32_t type; uint32_t cpumask; uint32_t gic_cpus; unsigned int nr_lines; int i; cpumask = readl_gicd(GICD_ITARGETSR) & 0xff; cpumask = (cpumask == 0) ? (1 << 0) : cpumask; cpumask |= cpumask << 8; cpumask |= cpumask << 16; /* Disable the distributor */ writel_gicd(0, GICD_CTLR); type = readl_gicd(GICD_TYPER); nr_lines = 32 * ((type & GICD_TYPE_LINES) + 1); gic_cpus = 1 + ((type & GICD_TYPE_CPUS) >> 5); pr_notice("GICv2: %d lines, %d cpu%s%s (IID %x).\n", nr_lines, gic_cpus, (gic_cpus == 1) ? "" : "s", (type & GICD_TYPE_SEC) ? ", secure" : "", readl_gicd(GICD_IIDR)); /* Default all global IRQs to level, active low */ for ( i = 32; i < nr_lines; i += 16 ) writel_gicd(0x0, GICD_ICFGR + (i / 16) * 4); /* Route all global IRQs to this CPU */ for ( i = 32; i < nr_lines; i += 4 ) writel_gicd(cpumask, GICD_ITARGETSR + (i / 4) * 4); /* Default priority for global interrupts */ for ( i = 32; i < nr_lines; i += 4 ) writel_gicd(GIC_PRI_IRQ << 24 | GIC_PRI_IRQ << 16 | GIC_PRI_IRQ << 8 | GIC_PRI_IRQ, GICD_IPRIORITYR + (i / 4) * 4); /* Disable all global interrupts */ for ( i = 32; i < nr_lines; i += 32 ) writel_gicd(~0x0, GICD_ICENABLER + (i / 32) * 4); /* Only 1020 interrupts are supported */ gicv2_nr_lines = min(1020U, nr_lines); /* Turn on the distributor */ writel_gicd(GICD_CTL_ENABLE, GICD_CTLR); dsb(); } static int __init_text gicv2_init(struct device_node *node) { uint64_t array[10]; pr_notice("*** gicv2 init ***\n"); memset(array, 0, sizeof(array)); translate_device_address_index(node, &array[0], &array[1], 0); translate_device_address_index(node, &array[2], &array[3], 1); translate_device_address_index(node, &array[4], &array[5], 2); translate_device_address_index(node, &array[6], &array[7], 3); pr_notice("gicv2 information: gic_dist_addr=%p size=0x%x " "gic_cpu_addr=%p size=0x%x gic_hyp_addr=%p size=0x%x " "gic_vcpu_addr=%p size=0x%x\n", array[0], array[1], array[2], array[3], array[4], array[5], array[6], array[7]); ASSERT((array[0] != 0) && (array[1] != 0)) gicv2_dbase = io_remap((virt_addr_t)array[0], (size_t)array[1]); ASSERT((array[2] != 0) && array[3] !=0); gicv2_cbase = io_remap((virt_addr_t)array[2], (size_t)array[3]); #ifdef CONFIG_VIRT ASSERT((array[4] != 0) && (array[5] != 0)) gicv2_hbase = io_remap((virt_addr_t)array[4], (size_t)array[5]); #endif if (gicv2_is_aliased((unsigned long)array[2], (unsigned long)array[3])) { gicv2_cbase += 0xf000; pr_notice("gicv2 : adjust cpu interface base to 0x%x\n", (unsigned long)gicv2_cbase); } spin_lock(&gicv2_lock); gicv2_dist_init(); gicv2_cpu_init(); #ifdef CONFIG_VIRT gicv2_hyp_init(); #endif spin_unlock(&gicv2_lock); #if defined CONFIG_VIRQCHIP_VGICV2 && defined CONFIG_VIRT vgicv2_init(array, 8); #endif return 0; } static int __init_text gicv2_secondary_init(void) { spin_lock(&gicv2_lock); gicv2_cpu_init(); #ifdef CONFIG_VIRT gicv2_hyp_init(); #endif spin_unlock(&gicv2_lock); return 0; } static struct irq_chip gicv2_chip = { .irq_mask = gicv2_mask_irq, .irq_mask_cpu = gicv2_mask_irq_cpu, .irq_unmask = gicv2_unmask_irq, .irq_unmask_cpu = gicv2_unmask_irq_cpu, .irq_eoi = gicv2_eoi_irq, .irq_dir = gicv2_dir_irq, .irq_set_type = gicv2_set_irq_type, .irq_set_affinity = gicv2_set_irq_affinity, .send_sgi = gicv2_send_sgi, .get_pending_irq = gicv2_read_irq, .irq_set_priority = gicv2_set_irq_priority, .irq_xlate = gic_xlate_irq, .init = gicv2_init, .secondary_init = gicv2_secondary_init, }; IRQCHIP_DECLARE(gicv2_chip, gicv2_match_table, (void *)&gicv2_chip); ================================================ FILE: kernel/drivers/irq-chips/gicv3.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include spinlock_t gicv3_lock; static void *gicd_base = 0; extern int vgicv3_init(uint64_t *data, int len); static DEFINE_PER_CPU(void *, gicr_rd_base); static DEFINE_PER_CPU(void *, gicr_sgi_base); #define gicr_rd_base() get_cpu_var(gicr_rd_base) #define gicr_sgi_base() get_cpu_var(gicr_sgi_base) uint64_t cpus_affinity[NR_CPUS]; extern int gic_xlate_irq(struct device_node *node, uint32_t *intspec, unsigned int intsize, uint32_t *hwirq, unsigned long *type); static void gicv3_gicd_wait_for_rwp(void) { while (ioread32(gicd_base + GICD_CTLR) & (1 << 31)); } static void gicv3_gicr_wait_for_rwp(void) { while (ioread32(gicr_rd_base() + GICR_CTLR) & (1 << 31)); } static void gicv3_eoi_irq(uint32_t irq) { write_sysreg32(irq, ICC_EOIR1_EL1); isb(); } static void gicv3_dir_irq(uint32_t irq) { write_sysreg32(irq, ICC_DIR_EL1); isb(); } static uint32_t gicv3_read_irq(void) { uint32_t irq; irq = read_sysreg32(ICC_IAR1_EL1); dsbsy(); return irq; } static int gicv3_set_irq_type(uint32_t irq, uint32_t type) { void *base; uint32_t cfg, edgebit; /* sgi are always edge-triggered */ if (irq < GICV3_NR_SGI) return 0; spin_lock(&gicv3_lock); if (irq >= GICV3_NR_LOCAL_IRQS) base = (void *)gicd_base + GICD_ICFGR + (irq / 16) * 4; else base = (void *)gicr_sgi_base() + GICR_ICFGR1; cfg = ioread32(base); edgebit = 2u << (2 * (irq % 16)); if (type & IRQ_FLAGS_LEVEL_BOTH) cfg &= ~edgebit; else if (type & IRQ_FLAGS_EDGE_BOTH) cfg |= edgebit; iowrite32(cfg, base); isb(); spin_unlock(&gicv3_lock); return 0; } static void gicv3_clear_pending(uint32_t irq) { uint32_t offset, bit; spin_lock(&gicv3_lock); if (irq < GICV3_NR_LOCAL_IRQS) { iowrite32(BIT(irq), (void *)gicr_sgi_base() + GICR_ICPENDR0); } else { irq = irq - 32; offset = irq / 32; bit = offset % 32; iowrite32(BIT(bit), (void *)gicd_base + \ GICD_ICPENDR + (offset * 4)); } spin_unlock(&gicv3_lock); } static int gicv3_set_irq_priority(uint32_t irq, uint32_t pr) { spin_lock(&gicv3_lock); if (irq < GICV3_NR_LOCAL_IRQS) iowrite8(pr, gicr_sgi_base() + GICR_IPRIORITYR0 + irq); else iowrite8(pr, gicd_base + GICD_IPRIORITYR + irq); spin_unlock(&gicv3_lock); return 0; } static int gicv3_set_irq_affinity(uint32_t irq, uint32_t pcpu) { uint64_t affinity; affinity = cpuid_to_affinity(pcpu); affinity &= ~(1 << 31); //GICD_IROUTER_SPI_MODE_ANY spin_lock(&gicv3_lock); iowrite64(affinity, gicd_base + GICD_IROUTER + irq * 8); spin_unlock(&gicv3_lock); return 0; } static inline void __gicv3_send_sgi_list(uint32_t sgi, cpumask_t *mask) { uint64_t val_cluster0 = 0; uint64_t val_cluster1 = 0; int cpu; for_each_cpu(cpu, mask) { if (cpu >= CONFIG_NR_CPUS_CLUSTER0) val_cluster1 |= cpus_affinity[cpu]; else val_cluster0 |= cpus_affinity[cpu]; } /* * TBD: now only support two cluster */ if (val_cluster0) { val_cluster0 |= (sgi << 24); write_sysreg64(val_cluster0, ICC_SGI1R_EL1); } if (val_cluster1) { val_cluster1 |= (sgi << 24); write_sysreg64(val_cluster1, ICC_SGI1R_EL1); } isb(); } static inline void __gicv3_send_sgi_list_shif_mpidr(uint32_t sgi, cpumask_t *mask) { int cpu; uint64_t value; for_each_cpu(cpu, mask) { value = cpus_affinity[cpu] | (sgi << 24); write_sysreg64(value, ICC_SGI1R_EL1); } isb(); } static void gicv3_send_sgi_list(uint32_t sgi, cpumask_t *mask) { if (cpu_has_feature(ARM_FEATURE_MPIDR_SHIFT)) __gicv3_send_sgi_list_shif_mpidr(sgi, mask); else __gicv3_send_sgi_list(sgi, mask); } static void gicv3_send_sgi(uint32_t sgi, enum sgi_mode mode, cpumask_t *cpu) { cpumask_t cpus_mask; if (sgi > 15) return; cpumask_clearall(&cpus_mask); switch (mode) { case SGI_TO_OTHERS: write_sysreg64(ICH_SGI_TARGET_OTHERS << ICH_SGI_IRQMODE_SHIFT | (uint64_t)sgi << ICH_SGI_IRQ_SHIFT, ICC_SGI1R_EL1); isb(); break; case SGI_TO_SELF: cpumask_set_cpu(smp_processor_id(), &cpus_mask); gicv3_send_sgi_list(sgi, &cpus_mask); break; case SGI_TO_LIST: gicv3_send_sgi_list(sgi, cpu); break; default: pr_err("Sgi mode not supported\n"); break; } } static void gicv3_mask_irq(uint32_t irq) { uint32_t mask = 1 << (irq % 32); spin_lock(&gicv3_lock); if (irq < GICV3_NR_LOCAL_IRQS) { iowrite32(mask, gicr_sgi_base() + GICR_ICENABLER + (irq / 32) * 4); gicv3_gicr_wait_for_rwp(); } else { iowrite32(mask, gicd_base + GICD_ICENABLER + (irq / 32) * 4); gicv3_gicd_wait_for_rwp(); } spin_unlock(&gicv3_lock); } static void gicv3_unmask_irq(uint32_t irq) { uint32_t mask = 1 << (irq % 32); spin_lock(&gicv3_lock); if (irq < GICV3_NR_LOCAL_IRQS) { iowrite32(mask, gicr_sgi_base() + GICR_ISENABLER + (irq / 32) * 4); gicv3_gicr_wait_for_rwp(); } else { iowrite32(mask, gicd_base + GICD_ISENABLER + (irq / 32) * 4); gicv3_gicd_wait_for_rwp(); } spin_unlock(&gicv3_lock); } static void gicv3_mask_irq_cpu(uint32_t irq, int cpu) { void *base; uint32_t mask = 1 << (irq % 32); if (irq >= GICV3_NR_LOCAL_IRQS) return; if (cpu >= NR_CPUS) return; spin_lock(&gicv3_lock); base = get_per_cpu(gicr_sgi_base, cpu); base = base + GICR_ICENABLER + (irq / 32) * 4; iowrite32(mask, base); gicv3_gicr_wait_for_rwp(); spin_unlock(&gicv3_lock); } static void gicv3_unmask_irq_cpu(uint32_t irq, int cpu) { void *base; uint32_t mask = 1 << (irq % 32); if (irq >= GICV3_NR_LOCAL_IRQS) return; if (cpu >= NR_CPUS) return; spin_lock(&gicv3_lock); base = get_per_cpu(gicr_sgi_base, cpu); base = base + GICR_ISENABLER + (irq / 32) * 4; iowrite32(mask, base); gicv3_gicr_wait_for_rwp(); spin_unlock(&gicv3_lock); } static void gicv3_wakeup_gicr(void) { uint32_t gicv3_waker_value; gicv3_waker_value = ioread32(gicr_rd_base() + GICR_WAKER); gicv3_waker_value &= ~(GICR_WAKER_PROCESSOR_SLEEP); iowrite32(gicv3_waker_value, gicr_rd_base() + GICR_WAKER); while ((ioread32(gicr_rd_base() + GICR_WAKER) & GICR_WAKER_CHILDREN_ASLEEP) != 0); } static inline uint64_t read_icc_sre(void) { #ifdef CONFIG_VIRT return read_icc_sre_el2(); #else return read_icc_sre_el1(); #endif } static inline void write_icc_sre(uint64_t val) { #ifdef CONFIG_VIRT write_icc_sre_el2(val); #else write_icc_sre_el1(val); #endif } static int __init_text gicv3_gicc_init(void) { unsigned char aff0, aff1, aff2, aff3; uint64_t reg_value; uint64_t mpidr = read_mpidr_el1(); int cpu = smp_processor_id(); aff0 = mpidr & 0xff; aff1 = (mpidr & 0xff00) >> 8; aff2 = (mpidr & 0xff0000) >> 16; aff3 = (mpidr & 0xff00000000) >> 32; if (aff0 > 16) panic("mpidr 0x%x for cpu%d is wrong\n", mpidr, cpu); /* * MPDIR SHIFT means each cluster has one core */ cpus_affinity[cpu] = (1 << aff0) | (aff1 << 16) | ((uint64_t)aff2 << 32) | ((uint64_t)aff3 << 48); /* enable sre */ reg_value = read_icc_sre(); reg_value |= (1 << 0); write_icc_sre(reg_value); write_sysreg32(0, ICC_BPR1_EL1); write_sysreg32(0xff, ICC_PMR_EL1); write_sysreg32(1 << 1, ICC_CTLR_EL1); write_sysreg32(1, ICC_IGRPEN1_EL1); isb(); return 0; } static int __init_text gicv3_hyp_init(void) { #ifdef CONFIG_VIRT write_sysreg32(GICH_VMCR_VENG1 | (0xff << 24), ICH_VMCR_EL2); write_sysreg32(GICH_HCR_EN, ICH_HCR_EL2); isb(); #endif return 0; } static int __init_text gicv3_gicr_init(void) { int i; uint64_t pr; gicv3_wakeup_gicr(); /* set the priority on PPI and SGI */ pr = (0x90 << 24) | (0x90 << 16) | (0x90 << 8) | 0x90; for (i = 0; i < GICV3_NR_SGI; i += 4) iowrite32(pr, gicr_sgi_base() + GICR_IPRIORITYR0 + (i / 4) * 4); pr = (0xa0 << 24) | (0xa0 << 16) | (0xa0 << 8) | 0xa0; for (i = GICV3_NR_SGI; i < GICV3_NR_LOCAL_IRQS; i += 4) iowrite32(pr, gicr_sgi_base() + GICR_IPRIORITYR0 + (i / 4) * 4); /* disable all PPI and enable all SGI */ iowrite32(0xffff0000, gicr_sgi_base() + GICR_ICENABLER); iowrite32(0x0000ffff, gicr_sgi_base() + GICR_ISENABLER); /* configure SGI and PPI as non-secure Group-1 */ iowrite32(0xffffffff, gicr_sgi_base() + GICR_IGROUPR0); gicv3_gicr_wait_for_rwp(); isb(); return 0; } static void __init_text gicv3_icc_sre_init(void) { #ifdef CONFIG_VIRT write_sysreg(0xf, ICC_SRE_EL2); #endif write_sysreg(0xf, ICC_SRE_EL1); } static int __init_text gicv3_init(struct device_node *node) { int i; uint32_t type; uint32_t nr_lines, pr; void *rbase; uint64_t array[10]; void *__gicr_rd_base = 0; pr_notice("*** gicv3 init ***\n"); memset(array, 0, sizeof(array)); translate_device_address_index(node, &array[0], &array[1], 0); translate_device_address_index(node, &array[2], &array[3], 1); translate_device_address_index(node, &array[4], &array[5], 2); translate_device_address_index(node, &array[6], &array[7], 3); spin_lock_init(&gicv3_lock); /* only map gicd and gicr now */ gicd_base = io_remap((virt_addr_t)array[0], (size_t)array[1]); __gicr_rd_base = io_remap((virt_addr_t)array[2], (size_t)array[3]); pr_notice("gicv3 gicd@0x%x gicr@0x%x\n", (unsigned long)gicd_base, (unsigned long)__gicr_rd_base); #ifdef CONFIG_VIRT uint64_t nr_pr; uint32_t value; value = read_sysreg32(ICH_VTR_EL2); nr_pr = ((value >> 29) & 0x7) + 1; if (!((nr_pr > 4) && (nr_pr < 8))) panic("GICv3: Invalid number of priority bits\n"); #endif for (i = 0; i < CONFIG_NR_CPUS; i++) { rbase = __gicr_rd_base + (128 * 1024) * i; get_per_cpu(gicr_rd_base, i) = rbase; get_per_cpu(gicr_sgi_base, i) = rbase + (64 * 1024); } spin_lock(&gicv3_lock); /* disable gicd */ iowrite32(0, gicd_base + GICD_CTLR); type = ioread32(gicd_base + GICD_TYPER); nr_lines = 32 * ((type & 0x1f)); pr_notice("gicv3 typer-0x%x nr_lines-%d\n", type, nr_lines); /* default all golbal IRQS to level, active low */ for (i = GICV3_NR_LOCAL_IRQS; i < nr_lines; i += 16) iowrite32(0, gicd_base + GICD_ICFGR + (i / 16) * 4); /* default priority for global interrupts */ for (i = GICV3_NR_LOCAL_IRQS; i < nr_lines; i += 4) { pr = (0xa0 << 24) | (0xa0 << 16) | (0xa0 << 8) | 0xa0; iowrite32(pr, gicd_base + GICD_IPRIORITYR + (i / 4) * 4); pr = ioread32(gicd_base + GICD_IPRIORITYR + (i / 4) * 4); } /* disable all global interrupt */ for (i = GICV3_NR_LOCAL_IRQS; i < nr_lines; i += 32) iowrite32(0xffffffff, gicd_base + GICD_ICENABLER + (i / 32) *4); /* configure SPIs as non-secure GROUP-1 */ for (i = GICV3_NR_LOCAL_IRQS; i < nr_lines; i += 32) iowrite32(0xffffffff, gicd_base + GICD_IGROUPR + (i / 32) *4); gicv3_gicd_wait_for_rwp(); /* enable the gicd */ iowrite32(1 | GICD_CTLR_ENABLE_GRP1 | GICD_CTLR_ENABLE_GRP1A | GICD_CTLR_ARE_NS, gicd_base + GICD_CTLR); isb(); gicv3_icc_sre_init(); gicv3_gicr_init(); gicv3_gicc_init(); gicv3_hyp_init(); spin_unlock(&gicv3_lock); #ifdef CONFIG_VIRT #ifdef CONFIG_VIRQCHIP_VGICV3 vgicv3_init(array, 10); #else pr_err("vgicv3 is not enabled, using vgicv2 instead\n"); vgicv2_init(NULL, 0); #endif #endif return 0; } static int __init_text gicv3_secondary_init(void) { spin_lock(&gicv3_lock); gicv3_icc_sre_init(); gicv3_gicr_init(); gicv3_gicc_init(); gicv3_hyp_init(); spin_unlock(&gicv3_lock); return 0; } static struct irq_chip gicv3_chip = { .irq_mask = gicv3_mask_irq, .irq_mask_cpu = gicv3_mask_irq_cpu, .irq_unmask = gicv3_unmask_irq, .irq_unmask_cpu = gicv3_unmask_irq_cpu, .irq_eoi = gicv3_eoi_irq, .irq_dir = gicv3_dir_irq, .irq_set_type = gicv3_set_irq_type, .irq_set_affinity = gicv3_set_irq_affinity, .send_sgi = gicv3_send_sgi, .get_pending_irq = gicv3_read_irq, .irq_clear_pending = gicv3_clear_pending, .irq_set_priority = gicv3_set_irq_priority, .irq_xlate = gic_xlate_irq, .init = gicv3_init, .secondary_init = gicv3_secondary_init, }; IRQCHIP_DECLARE(gicv3_chip, gicv3_match_table, (void *)&gicv3_chip); ================================================ FILE: kernel/drivers/irq-chips/irq-bcm2836.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include extern int bcm_virq_init(unsigned long l1_base, size_t l1_size, unsigned long l2_base, size_t l2_size); static const int reg_pending[] = { 0x00, 0x04, 0x08 }; static const int reg_enable[] = { 0x18, 0x10, 0x14 }; static const int reg_disable[]= { 0x24, 0x1c, 0x20 }; static const int shortcuts[] = { 7, 9, 10, 18, 19, /* Bank 1 */ 21, 22, 23, 24, 25, 30 /* Bank 2 */ }; struct armctrl_ic { void *base; void *pending[NR_BANKS]; void *enable[NR_BANKS]; void *disable[NR_BANKS]; struct irq_domain *domain; void *local_base; }; static struct armctrl_ic intc; static void *bcm2836_base; extern int bcm2836_xlate_irq(struct device_node *node, uint32_t *intspec, unsigned int intsize, uint32_t *hwirq, unsigned long *type); static void bcm2835_mask_irq(uint32_t irq) { writel_relaxed(HWIRQ_BIT(irq), intc.disable[HWIRQ_BANK(irq)]); dsb(); } static void bcm2835_unmask_irq(uint32_t irq) { writel_relaxed(HWIRQ_BIT(irq), intc.enable[HWIRQ_BANK(irq)]); dsb(); } static uint32_t armctrl_translate_bank(int bank) { uint32_t stat = readl_relaxed(intc.pending[bank]); return MAKE_HWIRQ(bank, __ffs(stat)); } static uint32_t armctrl_translate_shortcut(int bank, u32 stat) { return MAKE_HWIRQ(bank, shortcuts[__ffs(stat >> SHORTCUT_SHIFT)]); } static uint32_t bcm2835_get_pending(void) { uint32_t stat = readl_relaxed(intc.pending[0]) & BANK0_VALID_MASK; if (stat == 0) return BAD_IRQ; else if (stat & BANK0_HWIRQ_MASK) return MAKE_HWIRQ(0, __ffs(stat & BANK0_HWIRQ_MASK)); else if (stat & SHORTCUT1_MASK) return armctrl_translate_shortcut(1, stat & SHORTCUT1_MASK); else if (stat & SHORTCUT2_MASK) return armctrl_translate_shortcut(2, stat & SHORTCUT2_MASK); else if (stat & BANK1_HWIRQ) return armctrl_translate_bank(1); else if (stat & BANK2_HWIRQ) return armctrl_translate_bank(2); else BUG(); } static uint32_t bcm2836_get_pending(void) { int cpu = smp_processor_id(); uint32_t stat; uint32_t irq; stat = readl_relaxed(bcm2836_base + LOCAL_IRQ_PENDING0 + 4 * cpu); mb(); if (stat & BIT(LOCAL_IRQ_MAILBOX0)) { void *mailbox0; uint32_t mbox_val; /* * support 32 IPI, here only use 16 bit to routing * to the sgi interrupt */ mailbox0 = bcm2836_base + LOCAL_MAILBOX0_CLR0 + 16 * cpu; mbox_val = readl_relaxed(mailbox0); if (mbox_val == 0) return BAD_IRQ; irq = __ffs(mbox_val); writel_relaxed(1 << irq, mailbox0); dsb(); if (irq >= 16) return BAD_IRQ; return irq; } else if (stat) { /* * map other irq except mailbox to PPI as below: * 16 : CNTPSIRQ * 17 : CNTPNSIRQ * 18 : CNTHPIRQ * 19 : CNTVIRQ * 20 - 23 : Mailbox irq * 24 : GPU interrupt * 25 : PMU interrupt * 26 : AXI outstanding interrupt * 27 : Local timer interrupt */ irq = __ffs(stat) + 16; return irq; } return BAD_IRQ; } static void bcm2836_mask_per_cpu_irq(unsigned int reg_offset, unsigned int bit, int cpu) { void *reg = bcm2836_base + reg_offset + 4 * cpu; writel_relaxed(readl_relaxed(reg) & ~BIT(bit), reg); dsb(); } static void bcm2836_unmask_per_cpu_irq(unsigned int reg_offset, unsigned int bit, int cpu) { void *reg = bcm2836_base + reg_offset + 4 * cpu; writel_relaxed(readl_relaxed(reg) | BIT(bit), reg); dsb(); } static void inline __bcm2836_mask_irq(uint32_t irq, int cpu) { int offset; if (irq >= 32) bcm2835_mask_irq(irq); /* TBD : sgi always enable */ if (irq < 16) return; offset = irq - 16; switch (offset) { case LOCAL_IRQ_CNTPSIRQ: case LOCAL_IRQ_CNTPNSIRQ: case LOCAL_IRQ_CNTHPIRQ: case LOCAL_IRQ_CNTVIRQ: bcm2836_mask_per_cpu_irq(LOCAL_TIMER_INT_CONTROL0, offset, cpu); break; case LOCAL_IRQ_PMU_FAST: writel_relaxed(1 << cpu, bcm2836_base + LOCAL_PM_ROUTING_CLR); dsb(); break; case LOCAL_IRQ_GPU_FAST: break; } } static void inline __bcm2836_unmask_irq(uint32_t irq, int cpu) { int offset; if (irq >= 32) bcm2835_unmask_irq(irq); if (irq < 16) return; offset = irq - 16; switch (offset) { case LOCAL_IRQ_CNTPSIRQ: case LOCAL_IRQ_CNTPNSIRQ: case LOCAL_IRQ_CNTHPIRQ: case LOCAL_IRQ_CNTVIRQ: bcm2836_unmask_per_cpu_irq(LOCAL_TIMER_INT_CONTROL0, offset, cpu); break; case LOCAL_IRQ_PMU_FAST: writel_relaxed(1 << cpu, bcm2836_base + LOCAL_PM_ROUTING_SET); dsb(); break; case LOCAL_IRQ_GPU_FAST: break; } } static int bcm2836_set_irq_priority(uint32_t irq, uint32_t pr) { return 0; } static void bcm2836_mask_irq(uint32_t irq) { __bcm2836_mask_irq(irq, smp_processor_id()); } static void bcm2836_unmask_irq(uint32_t irq) { __bcm2836_unmask_irq(irq, smp_processor_id()); } static void bcm2836_mask_irq_cpu(uint32_t irq, int cpu) { __bcm2836_mask_irq(irq, cpu); } static void bcm2836_unmask_irq_cpu(uint32_t irq, int cpu) { __bcm2836_unmask_irq(irq, cpu); } static void bcm2836_send_sgi(uint32_t sgi, enum sgi_mode mode, cpumask_t *cpu) { int c; void *mailbox0_base = bcm2836_base + LOCAL_MAILBOX0_SET0; dsb(); if (sgi >= 16) return; switch (mode) { case SGI_TO_OTHERS: for_each_cpu(c, cpu) { if (c == smp_processor_id()) continue; writel_relaxed(1 << sgi, mailbox0_base + 16 * c); dsb(); } break; case SGI_TO_SELF: writel_relaxed(1 << sgi, mailbox0_base + 16 * smp_processor_id()); dsb(); break; case SGI_TO_LIST: for_each_cpu(c, cpu) { writel_relaxed(1 << sgi, mailbox0_base + 16 * c); dsb(); } break; } } static int bcm2836_set_irq_affinity(uint32_t irq, uint32_t pcpu) { return 0; } static int bcm2836_set_irq_type(uint32_t irq, uint32_t type) { return 0; } static void bcm2836_dir_irq(uint32_t irq) { if (irq >= 32) bcm2835_unmask_irq(irq); } static void bcm2836_eoi_irq(uint32_t irq) { if (irq >= 32) bcm2835_mask_irq(irq); } static int bcm2835_irq_handler(uint32_t irq, void *data) { uint32_t no; struct irq_desc *irq_desc; while ((no = bcm2835_get_pending()) != BAD_IRQ) { irq_desc = get_irq_desc(no); if (!irq_desc || !irq_desc->handler) { bcm2835_mask_irq(no); pr_err("irq is not register disable it %d\n", no); continue; } irq_desc->handler(irq_desc->hno, irq_desc->pdata); /* * if the hardware irq is for vm mask it here * until the vm notify that the hardware irq * is handled */ if (test_bit(IRQ_FLAGS_VCPU_BIT, &irq_desc->flags)) bcm2835_mask_irq(no); } return 0; } static int __init_text bcm2836_irq_init(struct device_node *node) { void *base; int b; pr_notice("boardcom bcm2836 l1 interrupt init\n"); bcm2836_base = io_remap(0x40000000, 0x100); /* * set the timer to source for the 19.2Mhz crstal clock * and set the timer prescaler to 1:1 */ writel_relaxed(0, bcm2836_base + LOCAL_CONTROL); writel_relaxed(ptov(0x80000000), bcm2836_base + LOCAL_PRESCALER); /* * int rpi-3b there are two irq_chip controller, the * bcm2836 local interrupt controller is percpu and * the bcm2835 is not percpu so : * bcm2836 id : 0 - 31 * bcm2835 id : 32 - 127 * enable mailbox0 interrupt for each core */ writel_relaxed(1, bcm2836_base + LOCAL_MAILBOX_INT_CONTROL0); writel_relaxed(1, bcm2836_base + LOCAL_MAILBOX_INT_CONTROL0 + 0x4); writel_relaxed(1, bcm2836_base + LOCAL_MAILBOX_INT_CONTROL0 + 0x8); writel_relaxed(1, bcm2836_base + LOCAL_MAILBOX_INT_CONTROL0 + 0xc); /* init the bcm2835 interrupt controller for spi */ base = intc.base = io_remap(ptov(0x3f00b200), 0x100); for (b = 0; b < NR_BANKS; b++) { intc.pending[b] = base + reg_pending[b]; intc.enable[b] = base + reg_enable[b]; intc.disable[b] = base + reg_disable[b]; } /* * request the irq handler for the bcm2835 inc * TBD - now the hardware irq only route to cpu0 */ request_irq(24, bcm2835_irq_handler, 0, "bcm2835_irq", NULL); return 0; } static int __init_text bcm2836_secondary_init(void) { return 0; } static struct irq_chip bcm2836_irq_chip = { .irq_mask = bcm2836_mask_irq, .irq_mask_cpu = bcm2836_mask_irq_cpu, .irq_unmask = bcm2836_unmask_irq, .irq_unmask_cpu = bcm2836_unmask_irq_cpu, .irq_eoi = bcm2836_eoi_irq, .irq_dir = bcm2836_dir_irq, .irq_set_type = bcm2836_set_irq_type, .get_pending_irq = bcm2836_get_pending, .irq_set_affinity = bcm2836_set_irq_affinity, .irq_xlate = bcm2836_xlate_irq, .send_sgi = bcm2836_send_sgi, .irq_set_priority = bcm2836_set_irq_priority, .init = bcm2836_irq_init, .secondary_init = bcm2836_secondary_init, }; IRQCHIP_DECLARE(bcm2836_chip, bcmirq_match_table, (void *)&bcm2836_irq_chip); ================================================ FILE: kernel/drivers/irq-chips/irqchip.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include int bcm2836_xlate_irq(struct device_node *node, uint32_t *intspec, unsigned int intsize, uint32_t *hwirq, unsigned long *type) { int phandle; phandle = of_get_phandle(node); if (phandle == 3) { if (intsize != 2) return -EINVAL; /* workaroud for the bcm2835 irq */ if (intspec[0] == 8) return -EINVAL; *hwirq = intspec[0] + 16; *type = intspec[1]; return 0; } if (intsize == 1) { if (intspec[0] < 16) { *hwirq = intspec[0] + 16; *type = 0; return 0; } return -EINVAL; } else if (intsize == 2) { if (intspec[0] >= NR_BANKS) return -EINVAL; if (intspec[1] >= IRQS_PER_BANK) return -EINVAL; *hwirq = MAKE_HWIRQ(intspec[0], intspec[1]); *type = 0; return 0; } else return -EINVAL; } int gic_xlate_irq(struct device_node *node, uint32_t *intspec, unsigned int intsize, uint32_t *hwirq, unsigned long *type) { if (intsize != 3) return -EINVAL; if (intspec[0] == 0) *hwirq = intspec[1] + 32; else if (intspec[0] == 1) { if (intspec[1] >= 16) return -EINVAL; *hwirq = intspec[1] + 16; } else return -EINVAL; *type = intspec[2]; return 0; } ================================================ FILE: kernel/drivers/of/Kconfig ================================================ ================================================ FILE: kernel/drivers/of/Makefile ================================================ obj-y += of.o obj-y += of_mm.o ================================================ FILE: kernel/drivers/of/of.c ================================================ /* * Copyright (C) 2018 - 2019 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #define OF_MAX_DEEPTH 5 void *dtb_address; struct device_node *of_root_node; int of_spin_table_init(phy_addr_t *smp_holding) { int offset, node, i, len; char name[16]; const void *data; fdt32_t *tmp; /* * if the smp cpu boot using spin table * get the spin table address and these * address must mapped to the hypervisor * address space */ offset = of_get_node_by_name(dtb_address, 0, "cpus"); if (offset <= 0) { pr_err("can not find cpus node in dtb\n"); return -EINVAL; } for (i = 0; i < CONFIG_NR_CPUS; i++) { sprintf(name, "cpu@%x", cpuid_to_affinity(i)); node = of_get_node_by_name(dtb_address, offset, name); if (node <= 0) { pr_err("can not find %s\n", name); continue; } /* get the enable methold content */ data = fdt_getprop(dtb_address, node, "enable-method", &len); if (!data || len <= 0) continue; if (strncmp("spin-table", (char *)data, 10)) continue; /* read the holding address */ data = fdt_getprop(dtb_address, node, "cpu-release-addr", &len); if (!data || len <= sizeof(uint32_t)) continue; len = len / sizeof(uint32_t); tmp = (uint32_t *)data; if (len == 1) smp_holding[i] = fdt32_to_cpu(tmp[0]); else smp_holding[i] = fdt32_to_cpu64(tmp[0], tmp[1]); pr_notice("%s using spin-table relase addr 0x%p\n", name, smp_holding[i]); } return 0; } int fdt_n_size_cells(void *dtb, int node) { fdt32_t *v; int parent, child = node; parent = fdt_parent_offset(dtb, child); do { if (parent >= 0) child = parent; v = (fdt32_t *)fdt_getprop(dtb, child, "#size-cells", NULL); if (v) return fdt32_to_cpu(*v); parent = fdt_parent_offset(dtb, child); } while (parent >= 0); return 2; } int fdt_n_addr_cells(void *dtb, int node) { fdt32_t *v; int parent, child = node; parent = fdt_parent_offset(dtb, child); do { if (parent >= 0) child = parent; v = (fdt32_t *)fdt_getprop(dtb, child, "#address-cells", NULL); if (v) return fdt32_to_cpu(*v); parent = fdt_parent_offset(dtb, child); } while (parent >= 0); return 2; } static inline void *__of_getprop(void *dtb, int node, char *attr, int *len) { const void *data; int length; if (!dtb || node <= 0 || !attr) return NULL; data = fdt_getprop(dtb, node, attr, &length); if (!data || (length <= 0)) return NULL; if (len) *len = length; return (void *)data; } void *of_getprop(struct device_node *node, char *attr, int *len) { return __of_getprop(node->data, node->offset, attr, len); } static int __of_get_node_by_name(void *data, int pnode, char *str, int deepth) { const char *name; int node = -1, child, len; if (OF_MAX_DEEPTH >= 10) return -ENOENT; fdt_for_each_subnode(node, data, pnode) { if (NULL == (name = fdt_get_name(data, node, &len))) continue; if (len < 0) continue; if (strncmp(name, str, strlen(str)) == 0) return node; else { child = __of_get_node_by_name(data, node, str, deepth + 1); if (child > 0) return child; } } return -ENOENT; } int of_get_node_by_name(void *data, int pnode, char *str) { if (!data || (pnode < 0) || !str) return -EINVAL; return __of_get_node_by_name(data, pnode, str, 0); } const char *__of_get_compatible(void *dtb, int node) { const void *data; int len; data = fdt_getprop(dtb, node, "compatible", &len); if (!data || len == 0) return NULL; return (const char *)data; } int __of_get_bool(void *dtb, int node, char *attr) { int len; const struct fdt_property *prop; prop = fdt_get_property(dtb, node, attr, &len); return (prop != NULL); } int of_get_bool(struct device_node *node, char *attr) { return __of_get_bool(node->data, node->offset, attr); } int __of_get_string(void *dtb, int node, char *attr, char *str, int len) { char *s; int length; memset(str, 0, len); s = (char *)__of_getprop(dtb, node, attr, &length); if (!s || !str || (length == 0)) return -EINVAL; length = MIN(len - 1, length); strncpy(str, s, length); return length; } char *of_get_cmdline(void *dtb) { int node, len; const void *data = NULL; node = fdt_path_offset(dtb, "/chosen"); if (node <= 0) return NULL; data = fdt_getprop(dtb, node, "bootargs", &len); return (char *)data; } int __of_get_u16_array(void *dtb, int node, char *attr, uint16_t *array, int len) { fdt16_t *val; int length, i; memset(array, 0, sizeof(uint16_t) * len); val = (fdt16_t *)__of_getprop(dtb, node, attr, &length); if (!val) return -EINVAL; if ((length % sizeof(uint16_t)) != 0) { pr_err("node is not a u32 array %d\n", length); return -EINVAL; } length = length / sizeof(fdt16_t); length = MIN(len, length); for (i = 0; i < length; i++) *array++ = fdt16_to_cpu(val[i]); return length; } int __of_get_u32_array(void *dtb, int node, char *attr, uint32_t *array, int len) { fdt32_t *val; int length, i; memset(array, 0, sizeof(uint32_t) * len); val = (fdt32_t *)__of_getprop(dtb, node, attr, &length); if (!val) return -EINVAL; if ((length % sizeof(fdt32_t)) != 0) { pr_err("node is not a u32 array %d\n", length); return -EINVAL; } length = length / sizeof(fdt32_t); length = MIN(len, length); for (i = 0; i < length; i++) *array++ = fdt32_to_cpu(val[i]); return length; } int __of_get_u64_array(void *dtb, int node, char *attr, uint64_t *array, int len) { fdt64_t *val; int length, i; memset(array, 0, sizeof(uint64_t) * len); val = (fdt64_t *)__of_getprop(dtb, node, attr, &length); if (!val) { pr_err("of: attr %s not found\n", attr); return -EINVAL; } if ((length % sizeof(fdt64_t)) != 0) { pr_err("node is not a u64 array %d\n", length); return -EINVAL; } length = length / sizeof(uint64_t); length = MIN(len, length); for (i = 0; i < length; i++) *array++ = fdt64_to_cpu(val[i]); return length; } static inline struct device_node *alloc_device_node(void) { struct device_node *node; node = zalloc(sizeof(struct device_node)); if (!node) { pr_warn("%s no enough memory\n", __func__); return NULL; } return node; } static int of_parse_dt_class(struct device_node *node) { int ret; char type[64]; ret = __of_get_string(node->data, node->offset, "device_type", type, 64); if (ret > 0) { if (strcmp(type, "cpu") == 0) node->class = DT_CLASS_CPU; else if (strcmp(type, "memory") == 0) node->class = DT_CLASS_MEMORY; else if (strcmp(type, "pci") == 0) node->class = DT_CLASS_PCI_BUS; else if (strcmp(type, "virtual_machine") == 0) node->class = DT_CLASS_VM; else if (strcmp(type, "vmbox") == 0) node->class = DT_CLASS_VMBOX; else node->class = DT_CLASS_OTHER; } else { ret = __of_get_bool(node->data, node->offset, "interrupt-controller"); if (ret) { node->class = DT_CLASS_IRQCHIP; return 0; } if (strcmp(node->name, "timer") == 0) { node->class = DT_CLASS_TIMER; return 0; } if (!fdt_node_check_compatible(node->data, node->offset, "simple-bus")) { node->class = DT_CLASS_SIMPLE_BUS; return 0; } ret = __of_get_bool(node->data, node->offset, "virtual_device"); if (ret) { node->class = DT_CLASS_VDEV; return 0; } switch (node->parent->class) { case DT_CLASS_IRQCHIP: case DT_CLASS_CPU: case DT_CLASS_PDEV: case DT_CLASS_VDEV: node->class = node->parent->class; return 0; default: break;; } if (node->compatible) node->class = DT_CLASS_PDEV; else node->class = DT_CLASS_OTHER; } return 0; } int of_device_match(struct device_node *node, char **comp) { if (!node || !comp) return -EINVAL; while (*comp != NULL) { if (!fdt_node_check_compatible(node->data, node->offset, *comp)) return 1; comp++; } return 0; } static int __of_parse_device_node(struct device_node *root, struct device_node *pnode) { int child, index = 0; struct device_node *node, *prev; void *data = pnode->data; if (!pnode) return -EINVAL; fdt_for_each_subnode(child, data, pnode->offset) { node = alloc_device_node(); if (!node) return -ENOMEM; node->name = fdt_get_name(data, child, NULL); node->compatible = __of_get_compatible(data, child); node->offset = child; node->data = data; node->parent = pnode; node->flags |= DEVICE_NODE_F_OF; /* udpate the child and the sibling */ if (index == 0) { pnode->child = node; index = 1; } else { prev->sibling = node; } prev = node; node->next = root->next; root->next = node; of_parse_dt_class(node); __of_parse_device_node(root, node); } return 0; } /* must pass a root device node to this function */ static void *__iterate_device_node(struct device_node *node, of_iterate_fn func, void *arg, int loop) { struct device_node *child, *sibling, *n; if (!node) return NULL; child = node->child; n = func(node, arg); if (n && !loop) return n; while (child) { sibling = child->sibling; n = __iterate_device_node(child, func, arg, loop); if (n && !loop) return n; child = sibling; } return NULL; } void *of_iterate_all_node_loop(struct device_node *node, of_iterate_fn func, void *arg) { return __iterate_device_node(node, func, arg, 1); } void *of_iterate_all_node(struct device_node *node, of_iterate_fn func, void *arg) { return __iterate_device_node(node, func, arg, 0); } static void *find_node_by_compatible(struct device_node *node, void *comp) { char **str = (char **)comp; if (of_device_match(node, str)) return node; return NULL; } static void *find_node_by_name(struct device_node *node, void *name) { if (node->name && !(strcmp(node->name, (char *)name))) return node; return NULL; } struct device_node * of_find_node_by_compatible(struct device_node *root, char **comp) { return (struct device_node *)__iterate_device_node(root, find_node_by_compatible, (void *)comp, 0); } struct device_node * of_find_node_by_name(struct device_node *root, char *name) { return (struct device_node *)__iterate_device_node(root, find_node_by_name, (void *)name, 0); } int of_n_addr_cells(struct device_node *node) { fdt32_t *ip; do { ip = (fdt32_t *)fdt_getprop(node->data, node->offset, "#address-cells", NULL); if (ip) return fdt32_to_cpu(*ip); if (node->parent) node = node->parent; } while (node); return 2; } int of_n_size_cells(struct device_node *node) { fdt32_t *ip; do { ip = (fdt32_t *)fdt_getprop(node->data, node->offset, "#size-cells", NULL); if (ip) return fdt32_to_cpu(*ip); if (node->parent) node = node->parent; } while (node); return 1; } int of_get_phandle(struct device_node *node) { const struct fdt_property *prop; int len; prop = fdt_get_property(node->data, node->offset, "interrupt-parent", &len); if (!prop) return -1; return fdt32_to_cpu(*(fdt32_t *)prop->data); } int of_n_interrupt_cells(struct device_node *node) { int ret, len; uint32_t ni = 0; struct device_node *parent = node; uint32_t phandle; int offset; const struct fdt_property *prop; while (parent) { prop = fdt_get_property(parent->data, parent->offset, "interrupt-parent", &len); if (!prop || !prop->data) goto repeat; phandle = fdt32_to_cpu(*(fdt32_t *)prop->data); offset = fdt_node_offset_by_phandle(parent->data, phandle); if (offset <= 0) return 0; ret = __of_get_u32_array(parent->data, offset, "#interrupt-cells", &ni, 1); if (ret == 1) return ni; repeat: parent = parent->parent; } return 0; } int of_n_addr_count(struct device_node *node) { int na, ns; int len; const void *prop; na = fdt_n_addr_cells(node->data, node->offset); ns = fdt_n_size_cells(node->data, node->offset); prop = fdt_getprop(node->data, node->offset, "reg", &len); if (!prop) return 0; len = len / ((na + ns) * sizeof(fdt32_t)); return len; } int of_data(void *data) { return !fdt_check_header(data); } static inline u64 of_read_number(const fdt32_t *cell, int size) { u64 r = 0; while (size--) { r = (r << 32) | fdt32_to_cpu(*cell); cell++; } return r; } static inline int of_check_counts(int na, int ns) { return ((na) > 0 && (na) <= OF_MAX_ADDR_CELLS && (ns) > 0); } /* Callbacks for bus specific translators */ struct of_bus { const char *name; const char *addresses; void (*count_cells)(void *blob, int parentoffset, int *addrc, int *sizec); uint64_t (*map)(u32 *addr, const u32 *range, int na, int ns, int pna); int (*translate)(u32 *addr, u64 offset, int na); }; /* Default translator (generic bus) */ static void of_bus_default_count_cells(void *blob, int parentoffset, int *addrc, int *sizec) { const fdt32_t *prop; if (addrc) { prop = fdt_getprop(blob, parentoffset, "#address-cells", NULL); if (prop) *addrc = fdt32_to_cpu(*prop); else *addrc = 2; } if (sizec) { prop = fdt_getprop(blob, parentoffset, "#size-cells", NULL); if (prop) *sizec = fdt32_to_cpu(*prop); else *sizec = 1; } } static uint64_t of_bus_default_map(fdt32_t *addr, const fdt32_t *range, int na, int ns, int pna) { uint64_t cp, s, da; cp = of_read_number(range, na); s = of_read_number(range + na + pna, ns); da = of_read_number(addr, na); pr_debug("OF: default map, cp=0x%p, s=0x%p, da=0x%p\n", cp, s, da); if (da < cp || da >= (cp + s)) return OF_BAD_ADDR; return da - cp; } static int of_bus_default_translate(u32 *addr, u64 offset, int na) { uint64_t a = of_read_number(addr, na); memset(addr, 0, na * 4); a += offset; if (na > 1) { addr[na - 2] = a >> 32; addr[na - 2] = cpu_to_fdt32(addr[na - 2]); } addr[na - 1] = a & 0xffffffffu; addr[na - 1] = cpu_to_fdt32(addr[na - 1]); return 0; } /* Array of bus specific translators */ static struct of_bus of_busses[] = { /* Default */ { .name = "default", .addresses = "reg", .count_cells = of_bus_default_count_cells, .map = of_bus_default_map, .translate = of_bus_default_translate, }, }; static int of_translate_one(void * blob, int parent, struct of_bus *bus, struct of_bus *pbus, uint32_t *addr, int na, int ns, int pna, const char *rprop) { const uint32_t *ranges; int rlen; int rone; uint64_t offset = OF_BAD_ADDR; /* Normally, an absence of a "ranges" property means we are * crossing a non-translatable boundary, and thus the addresses * below the current not cannot be converted to CPU physical ones. * Unfortunately, while this is very clear in the spec, it's not * what Apple understood, and they do have things like /uni-n or * /ht nodes with no "ranges" property and a lot of perfectly * useable mapped devices below them. Thus we treat the absence of * "ranges" as equivalent to an empty "ranges" property which means * a 1:1 translation at that level. It's up to the caller not to try * to translate addresses that aren't supposed to be translated in * the first place. --BenH. */ ranges = (uint32_t *)fdt_getprop(blob, parent, rprop, &rlen); if (ranges == NULL || rlen == 0) { offset = of_read_number(addr, na); memset(addr, 0, pna * 4); pr_debug("OF: no ranges, 1:1 translation\n"); goto finish; } pr_debug("OF: walking ranges...\n"); /* Now walk through the ranges */ rlen /= 4; rone = na + pna + ns; for (; rlen >= rone; rlen -= rone, ranges += rone) { offset = bus->map(addr, ranges, na, ns, pna); if (offset != OF_BAD_ADDR) break; } if (offset == OF_BAD_ADDR) { pr_debug("OF: not found !\n"); return 1; } memcpy(addr, ranges + na, 4 * pna); finish: /* Translate it into parent bus space */ return pbus->translate(addr, offset, pna); } /* * Translate an address from the device-tree into a CPU physical address, * this walks up the tree and applies the various bus mappings on the * way. * * Note: We consider that crossing any level with #size-cells == 0 to mean * that translation is impossible (that is we are not dealing with a value * that can be mapped to a cpu physical address). This is not really specified * that way, but this is traditionally the way IBM at least do things */ static uint64_t __of_translate_address(struct device_node *node, const fdt32_t *in_addr, const char *prop) { int node_offset; struct device_node *parent; fdt32_t addr[OF_MAX_ADDR_CELLS]; int na, ns, pna, pns; struct of_bus *bus, *pbus; uint64_t result = OF_BAD_ADDR; void *data; pr_debug("OF: ** translation for device %s **\n", node->name); /* Get parent & match bus type */ parent = node->parent; data = node->data; if (parent == NULL) goto bail; bus = &of_busses[0]; /* Cound address cells & copy address locally */ bus->count_cells(data, parent->offset, &na, &ns); if (!of_check_counts(na, ns)) { pr_warn("%s: Bad cell count for %s\n", __func__, parent->name); goto bail; } memcpy(addr, in_addr, na * 4); pr_debug("OF: bus is %s (na=%d, ns=%d) on %s\n", bus->name, na, ns, parent->name); /* Translate */ for (;;) { /* Switch to parent bus */ node_offset = parent->offset; parent = parent->parent; /* If root, we have finished */ if (!parent || (parent->offset < 0)) { pr_debug("OF: reached root node\n"); result = of_read_number(addr, na); break; } /* Get new parent bus and counts */ pbus = &of_busses[0]; pbus->count_cells(data, parent->offset, &pna, &pns); if (!of_check_counts(pna, pns)) { pr_err("%s: Bad cell count for %s\n", __func__, parent->name); break; } pr_debug("OF: parent bus is %s (na=%d, ns=%d) on %s\n", pbus->name, pna, pns, parent->name); /* Apply bus translation */ if (of_translate_one(data, node_offset, bus, pbus, addr, na, ns, pna, "ranges")) break; /* Complete the move up one level */ na = pna; ns = pns; bus = pbus; } bail: return result; } int of_translate_address_index(struct device_node *np, uint64_t *address, uint64_t *size, int index) { const fdt32_t *reg; int na, ns, node, len = 0; void *data; if (!np || !np->data || (np->offset <=0)) return -EINVAL; data = np->data; node = np->offset; na = fdt_n_addr_cells(np->data, np->offset); if (na < 1) { pr_debug("bad #address-cells %s\n", np->name); return -EINVAL; } ns = fdt_n_size_cells(np->data, np->offset); if (ns < 0) { pr_debug("bad #size-cells %s\n", np->name); return -EINVAL; } reg = fdt_getprop(data, node, "reg", &len); if (!reg || (len <= (index * 4) *(na + ns))) { pr_debug("index out of range\n"); return -ENOENT; } reg += index * (na + ns); if (ns) { *address = __of_translate_address(np, reg, "ranges"); if (*address == OF_BAD_ADDR) return -EINVAL; if (ns == 2) *size = fdt32_to_cpu64(reg[na], reg[na + 1]); else *size = fdt32_to_cpu(reg[na]); } else { *address = of_read_number(reg, na); *size = 0; } return 0; } int of_translate_address(struct device_node *node, uint64_t *address, uint64_t *size) { return of_translate_address_index(node, address, size, 0); } int get_device_irq_index(struct device_node *node, uint32_t *irq, unsigned long *flags, int index) { int irq_cells, len, i; of32_t *value; uint32_t irqv[4]; if (!node) return -EINVAL; value = (of32_t *)of_getprop(node, "interrupts", &len); if (!value || (len < sizeof(of32_t))) return -ENOENT; irq_cells = of_n_interrupt_cells(node); if (irq_cells == 0) { pr_err("bad irqcells - %s\n", node->name); return -ENOENT; } pr_debug("interrupt-cells %d\n", irq_cells); len = len / sizeof(of32_t); if (index >= len) return -ENOENT; value += (index * irq_cells); for (i = 0; i < irq_cells; i++) irqv[i] = of32_to_cpu(*value++); return irq_xlate(node, irqv, irq_cells, irq, flags); } int of_init_bootargs(void) { void *dtb = dtb_address; int node, len; const void *data = NULL; node = fdt_path_offset(dtb, "/chosen"); if (node <= 0) return -ENOENT; data = fdt_getprop(dtb, node, "bootargs", &len); if (!data || (len == 0)) return -ENOENT; bootargs_init(data, len); return 0; } void *of_device_node_match(struct device_node *node, void *s, void *e) { int i, count; struct module_id *module; if (e <= s) return NULL; count = (e - s) / sizeof(struct module_id); if (count == 0) return NULL; for (i = 0; i < count; i++) { module = (struct module_id *)s; if (of_device_match(node, (char **)module->comp)) return module->data; s += sizeof(struct module_id); } return NULL; } void of_release_all_node(struct device_node *node) { struct device_node *tmp = node; struct device_node *tmp2; if (!device_node_is_root(node)) return; do { tmp2 = tmp->next; free(tmp); tmp = tmp2; } while (tmp); } struct device_node *of_parse_device_tree(void *dtb) { struct device_node *node; void *data = dtb; node = alloc_device_node(); if (!node) return node; node->data = data; node->name = "root node"; node->offset = 0; node->parent = NULL; node->sibling = NULL; node->next = NULL; node->class = DT_CLASS_OTHER; node->compatible = fdt_getprop(data, 0, "compatible", NULL); node->flags = DEVICE_NODE_F_OF | DEVICE_NODE_F_ROOT; /* * now parse all the node and convert them to the * device node struct for the hypervisor and vm0 use */ __of_parse_device_node(node, node); return node; } void of_parse_host_device_tree(void) { of_root_node = of_parse_device_tree(dtb_address); ASSERT(of_root_node); } static inline void *of_get_chosen_prop(const char *name, int len) { int node, length; void *d; node = fdt_path_offset(dtb_address, "/chosen"); if (node <= 0) return NULL; d = (void *)fdt_getprop(dtb_address, node, name, &length); if (!d || (length < len)) return NULL; return d; } int of_get_console_name(char **name) { void *dtb = dtb_address; int node, len; const void *data = NULL; node = fdt_path_offset(dtb, "/chosen"); if (node <= 0) return -ENOENT; data = fdt_getprop(dtb, node, "minos,stdout", &len); if (!data || (len == 0)) return -ENOENT; *name = (char *)data; return 0; } static int of_get_chosen_prop32(const char *name, uint32_t *value) { fdt32_t *data = of_get_chosen_prop(name, sizeof(uint32_t)); if (!data) return -ENOENT; *value = fdt32_to_cpu(*data); return 0; } static int __used of_get_chosen_prop64(const char *name, uint64_t *value) { fdt64_t *data = of_get_chosen_prop(name, sizeof(uint64_t)); if (!data) return -ENOENT; *value = fdt64_to_cpu(*data); return 0; } int of_get_ramdisk_address(unsigned long *start, unsigned long *end) { int ret; uint32_t address; ret = of_get_chosen_prop32("minos,ramdisk_start", &address); if (ret) return ret; *start = (unsigned long)address; ret = of_get_chosen_prop32("minos,ramdisk_end", &address); if (ret) return ret; *end = (unsigned long)address; return 0; } void of_setup_platform(void) { int len; const void *data; data = fdt_getprop(dtb_address, 0, "model", &len); if (data) pr_notice("model : %s\n", (char *)data); /* * compatible may like arm,fvp-base\0arm,vexpress\0 * but here, only parsing the first string */ data = fdt_getprop(dtb_address, 0, "compatible", &len); if (data) { pr_notice("platform : %s\n", (char *)data); platform_set_to((const char *)data); } } int of_init(void *dtb) { if (!dtb || fdt_check_header(dtb)) { pr_err("bad device tree address: 0x%lx\n", dtb); BUG(); } dtb_address = dtb; return 0; } int get_system_setup_info(unsigned long *addr, size_t *size) { if (!dtb_address) return -ENOENT; *addr = vtop(dtb_address); *size = PAGE_BALIGN(fdt_totalsize(dtb_address)); return 0; } ================================================ FILE: kernel/drivers/of/of_mm.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include static int __fdt_parse_memory_info(int node, char *attr) { int len = 0; uint64_t base, size; int size_cell, address_cell; fdt32_t *v; v = (fdt32_t *)fdt_getprop(dtb_address, node, attr, &len); if (!v || (len < 8)) { pr_warn("no memory info found in dtb\n"); return -ENOENT; } len = len / 4; size_cell = fdt_n_size_cells(dtb_address, node); address_cell = fdt_n_addr_cells(dtb_address, node); pr_notice("memory node address_cells:%d size_cells:%d\n", address_cell, size_cell); if ((size_cell > 2) || (size_cell == 0)) { pr_warn("memory node size_cells not correct\n"); return -EINVAL; } if ((address_cell > 2) || (address_cell == 0)) { pr_warn("memory node address_cells not correct\n"); return -EINVAL; } while (len >= (size_cell + address_cell)) { if (address_cell == 2) { base = fdt32_to_cpu64(v[0], v[1]); v += 2; } else { base = fdt32_to_cpu(v[0]); v++; } if (size_cell == 2) { size = fdt32_to_cpu64(v[0], v[1]); v += 2; } else { size = fdt32_to_cpu(v[0]); v += 1; } len -= size_cell + address_cell; add_memory_region(base, size, MEMORY_REGION_TYPE_NORMAL, 0); } return 0; } static void __fdt_parse_memreserve(void) { int count, i, ret; uint64_t address, size; /* * system can reseve some memory at the head of minos * memory space */ if (CONFIG_MINOS_ENTRY_ADDRESS != minos_start) { size = CONFIG_MINOS_ENTRY_ADDRESS - minos_start; split_memory_region(minos_start, size, MEMORY_REGION_TYPE_RSV, 1); } count = fdt_num_mem_rsv(dtb_address); if (count == 0) return; for (i = 0; i < count; i++) { ret = fdt_get_mem_rsv(dtb_address, i, &address, &size); if (ret) continue; pr_notice("find rev memory - id: %d addr: 0x%x size: 0x%x\n", i, address, size); split_memory_region(address, size, MEMORY_REGION_TYPE_RSV, 0); } } #ifdef CONFIG_VIRT static void __fdt_parse_vm_mem(void) { const char *name; const char *type; int node, child, len, i; uint64_t array[2 * 10]; phy_addr_t base; size_t size; int vmid; node = fdt_path_offset(dtb_address, "/vms"); if (node <= 0) { pr_warn("no virtual machine found in dts\n"); return; } fdt_for_each_subnode(child, dtb_address, node) { type = (char *)fdt_getprop(dtb_address, child, "device_type", &len); if (!type || (strcmp(type, "virtual_machine") != 0)) continue; __of_get_u32_array(dtb_address, child, "vmid", (uint32_t *)&vmid, 1); /* * get the memory information for the vm, each vm will * have max 10 memory region */ len = __of_get_u64_array(dtb_address, child, "memory", array, 2 * 10); if ((len <= 0) || ((len % 2) != 0)) { name = fdt_get_name(dtb_address, child, NULL); pr_err("wrong memory information for %s\n", name ? name : "unknown"); continue; } for (i = 0; i < len; i += 2 ) { base = (phy_addr_t)array[i]; size = (size_t)array[i + 1]; split_memory_region(base, size, MEMORY_REGION_TYPE_VM, vmid); } } } #endif static void __fdt_parse_kernel_mem(void) { split_memory_region(minos_start, CONFIG_MINOS_RAM_SIZE, MEMORY_REGION_TYPE_KERNEL, 0); } static void __fdt_parse_ramdisk_mem(void) { const fdt32_t *data; uint64_t start, end; int node, len; node = fdt_path_offset(dtb_address, "/chosen"); if (node <= 0) return; data = fdt_getprop(dtb_address, node, "minos,ramdisk-start", &len); if (!data || (len == 0)) return; start = fdt32_to_cpu64(data[0], data[1]); data = fdt_getprop(dtb_address, node, "minos,ramdisk-end", &len); if (!data || (len == 0)) return; end = fdt32_to_cpu64(data[0], data[1]); if (!IS_PAGE_ALIGN(start)) { pr_err("ramdisk region is not page align 0x%x\n", start); return; } split_memory_region(start, PAGE_BALIGN(end - start), MEMORY_REGION_TYPE_RAMDISK, 0); } int of_parse_memory_info(void) { int node; node = of_get_node_by_name(dtb_address, 0, "memory"); if (node <= 0) { pr_warn("no memory node found in dtb\n"); return -ENOENT; } __fdt_parse_memory_info(node, "reg"); /* * split the minos kernel's memory region, need * before to split the dtb memory */ __fdt_parse_kernel_mem(); __fdt_parse_memreserve(); __fdt_parse_ramdisk_mem(); #ifdef CONFIG_VIRT __fdt_parse_vm_mem(); #endif return 0; } int of_mm_init(void) { of_parse_memory_info(); dump_memory_info(); return 0; } ================================================ FILE: kernel/drivers/serial/Kconfig ================================================ menu "Serial Drivers" config SERIAL def_bool y if SERIAL config SERIAL_BCM283X_MU bool "support bcm283x mu serial" default n help serial driver for rpi-3b config SERIAL_MVEBU_A3700 bool "support marvell a3700 serial driver" default n help serial driver for a3700 soc config SERIAL_PL011 bool "pl011 serial driver" default n help serial driver for pl011 config SERIAL_AMLOGIC bool "meson for amlogic soc" default n help serial driver for amlogic soc endif endmenu ================================================ FILE: kernel/drivers/serial/Makefile ================================================ obj-$(CONFIG_SERIAL_BCM283X_MU) += serial_bcm283x_mu.o obj-$(CONFIG_SERIAL_MVEBU_A3700) += serial_mvebu_a3700.o obj-$(CONFIG_SERIAL_PL011) += serial_pl011.o obj-$(CONFIG_SERIAL_AMLOGIC) += serial_meson.o ================================================ FILE: kernel/drivers/serial/pl011.h ================================================ #ifndef _MINOS_ARM_FVP_UART_H_ #define _MINOS_ARM_FVP_UART_H_ #define UARTDR 0x0 #define UARTECR 0x4 #define UARTFR 0x18 #define UARTILPR 0x20 #define UARTIBRD 0x24 #define UARTFBRD 0x28 #define UARTLCR_H 0x2c #define UARTCR 0x30 #define UARTIFLS 0x34 #define UARTIMSC 0x38 #define UARTRIS 0x3c #define UARTMIS 0x40 #define UARTICR 0x44 #define UARTDMACR 0x48 #define INT_CTS (1 << 1) #define INT_RX (1 << 4) #define INT_TX (1 << 5) #define INT_RX_TIMEOUT (1 << 6) #define INT_FRAMING_ERR (1 << 7) #define INT_PARITY_ERR (1 << 8) #define INT_BREAK_ERR (1 << 9) #define INT_OVER_ERR (1 << 10) /* * defines for control/status registers */ #define PL011_LCR_WORD_LENGTH_8 (0x60) #define PL011_LCR_WORD_LENGTH_7 (0x40) #define PL011_LCR_WORD_LENGTH_6 (0x20) #define PL011_LCR_WORD_LENGTH_5 (0x00) #define PL011_LCR_FIFO_ENABLE (0x10) #define PL011_LCR_FIFO_DISABLE (0x00) #define PL011_LCR_TWO_STOP_BITS (0x08) #define PL011_LCR_ONE_STOP_BIT (0x00) #define PL011_LCR_PARITY_ENABLE (0x02) #define PL011_LCR_PARITY_DISABLE (0x00) #define PL011_LCR_BREAK_ENABLE (0x01) #define PL011_LCR_BREAK_DISABLE (0x00) #define PL011_IBRD_DIV_38400 (0x27) #define PL011_FBRD_DIV_38400 (0x09) #define PL011_ICR_CLR_ALL_IRQS (0x07FF) #define PL011_FR_BUSY_FLAG (0x08) #define PL011_FR_RXFE_FLAG (0x10) #define PL011_FR_TXFF_FLAG (0x20) #define PL011_FR_RXFF_FLAG (0x40) #define PL011_FR_TXFE_FLAG (0x80) #define PL011_CR_UART_ENABLE (0x01) #define PL011_CR_TX_ENABLE (0x0100) #define PL011_CR_RX_ENABLE (0x0200) #endif ================================================ FILE: kernel/drivers/serial/serial_bcm283x_mu.c ================================================ /* * copyright (c) 2018 min le (lemin9538@gmail.com) * * this program is free software; you can redistribute it and/or modify * it under the terms of the gnu general public license version 2 as * published by the free software foundation. * * this program is distributed in the hope that it will be useful, * but without any warranty; without even the implied warranty of * merchantability or fitness for a particular purpose. see the * gnu general public license for more details. * * you should have received a copy of the gnu general public license * along with this program. if not, see . */ #include #include #include #include #include #include #define AUX_MU_IO 0x00 #define AUX_MU_IER 0x04 #define AUX_MU_IIR 0x08 #define AUX_MU_LCR 0x0c #define AUX_MU_MCR 0x10 #define AUX_MU_LSR 0x14 #define AUX_MU_MSR 0x18 #define AUX_MU_SCRATCH 0x1c #define AUX_MU_CNTL 0x20 #define AUX_MU_STAT 0x24 #define AUX_MU_BAUD 0x28 #define BCM283X_LCR_DATA_SIZE_8 3 #define BCM283X_MU_LSR_TX_EMPTY (1 << 5) #define BCM283X_MU_LSR_RX_READY (1 << 0) static void *serial_base = (void *)ptov(CONFIG_UART_BASE); static inline void __bcm283x_mu_putc(char c) { while (!(ioread32(serial_base + AUX_MU_LSR) & BCM283X_MU_LSR_TX_EMPTY)); iowrite32(c, serial_base + AUX_MU_IO); } void bcm283x_mu_putc(char c) { if (c == '\n') __bcm283x_mu_putc('\r'); __bcm283x_mu_putc(c); } char bcm283x_mu_getc(void) { if (!(ioread32(serial_base + AUX_MU_LSR) & BCM283X_MU_LSR_RX_READY)) return 0; return ioread32(serial_base + AUX_MU_IO); } int bcm283x_mu_init(char *arg) { #if 0 uint32_t divider; if (!base) return -EINVAL; serial_base = base; divider = clk / (baud * 8); iowrite32(BCM283X_LCR_DATA_SIZE_8, serial_base + AUX_MU_LCR); iowrite32(divider - 1, serial_base + AUX_MU_BAUD); #endif return 0; } DEFINE_CONSOLE(bcm283x, "bcm283x_mu", bcm283x_mu_init, bcm283x_mu_putc, bcm283x_mu_getc); ================================================ FILE: kernel/drivers/serial/serial_meson.c ================================================ /* * copyright (c) 2018 min le (lemin9538@gmail.com) * * this program is free software; you can redistribute it and/or modify * it under the terms of the gnu general public license version 2 as * published by the free software foundation. * * this program is distributed in the hope that it will be useful, * but without any warranty; without even the implied warranty of * merchantability or fitness for a particular purpose. see the * gnu general public license for more details. * * you should have received a copy of the gnu general public license * along with this program. if not, see . */ #include #include #include struct meson_uart { u32 wfifo; u32 rfifo; u32 control; u32 status; u32 misc; }; /* AML_UART_STATUS bits */ #define AML_UART_PARITY_ERR BIT(16) #define AML_UART_FRAME_ERR BIT(17) #define AML_UART_TX_FIFO_WERR BIT(18) #define AML_UART_RX_EMPTY BIT(20) #define AML_UART_TX_FULL BIT(21) #define AML_UART_TX_EMPTY BIT(22) #define AML_UART_XMIT_BUSY BIT(25) #define AML_UART_ERR (AML_UART_PARITY_ERR | \ AML_UART_FRAME_ERR | \ AML_UART_TX_FIFO_WERR) /* AML_UART_CONTROL bits */ #define AML_UART_TX_EN BIT(12) #define AML_UART_RX_EN BIT(13) #define AML_UART_TX_RST BIT(22) #define AML_UART_RX_RST BIT(23) #define AML_UART_CLR_ERR BIT(24) static volatile struct meson_uart *uart = (struct meson_uart *)ptov(0xff803000); static int meson_serial_init(char *arg) { #if 0 u32 val; val = readl(&uart->control); val |= (AML_UART_RX_RST | AML_UART_TX_RST | AML_UART_CLR_ERR); writel(val, &uart->control); val &= ~(AML_UART_RX_RST | AML_UART_TX_RST | AML_UART_CLR_ERR); writel(val, &uart->control); val |= (AML_UART_RX_EN | AML_UART_TX_EN); writel(val, &uart->control); #endif return 0; } static char meson_serial_getc(void) { if ((readl(&uart->status) & 0x7f) == 0) return 0; return readl(&uart->rfifo) & 0xff; } static inline void __meson_serial_putc(char c) { while (readl(&uart->status) & AML_UART_TX_FULL); writel(c, &uart->wfifo); } static void meson_serial_putc(char c) { if (c == '\n') __meson_serial_putc('\r'); __meson_serial_putc(c); } DEFINE_CONSOLE(aml_meson, "aml_meson", meson_serial_init, meson_serial_putc, meson_serial_getc); ================================================ FILE: kernel/drivers/serial/serial_mvebu_a3700.c ================================================ /* * Copyright (C) 2016 Stefan Roese * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include static void *base = (void *)ptov(CONFIG_UART_BASE); /* * Register offset */ /* REG_UART_A3700 */ #define UART_RX_REG 0x00 #define UART_TX_REG 0x04 #define UART_CTRL_REG 0x08 #define UART_STATUS_REG 0x0c #define UART_BAUD_REG 0x10 #define UART_POSSR_REG 0x14 #define UART_STATUS_RX_RDY 0x10 #define UART_STATUS_TXFIFO_FULL 0x800 #define UART_CTRL_RXFIFO_RESET 0x4000 #define UART_CTRL_TXFIFO_RESET 0x8000 #define CONFIG_UART_BASE_CLOCK 25804800 /* REG_UART_A3700_EXT */ #define UART_EXT_RX_REG 0x18 #define UART_EXT_TX_REG 0x1c #define UART_EXT_CTRL_REG 0x04 #define UART_EXT_STATUS_RX_RDY 0x4000 static inline void __serial_putc(char ch) { while (ioread32(base + UART_STATUS_REG) & UART_STATUS_TXFIFO_FULL); iowrite32(ch, base + UART_TX_REG); } static void serial_mvebu_putc(char ch) { if (ch == '\n') __serial_putc('\r'); __serial_putc(ch); } static char serial_mvebu_getc(void) { while (!(ioread32(base + UART_STATUS_REG) & UART_STATUS_RX_RDY)); return ioread32(base + UART_RX_REG) & 0xff; } static int mvebu_serial_setbrg(int baudrate) { /* * Calculate divider * baudrate = clock / 16 / divider */ iowrite32(CONFIG_UART_BASE_CLOCK / baudrate / 16, base + UART_BAUD_REG); /* * Set Programmable Oversampling Stack to 0, * UART defaults to 16x scheme */ iowrite32(0, base + UART_POSSR_REG); return 0; } static int mvebu_serial_probe(char *addr) { #if 0 /* reset FIFOs */ iowrite32(UART_CTRL_RXFIFO_RESET | UART_CTRL_TXFIFO_RESET, base + UART_CTRL_REG); /* No Parity, 1 Stop */ iowrite32(0, base + UART_CTRL_REG); /* set brg to 115200 */ mvebu_serial_setbrg(115200); #endif return 0; } DEFINE_CONSOLE(mvebu, "mvebu", mvebu_serial_probe, serial_mvebu_putc, serial_mvebu_getc); ================================================ FILE: kernel/drivers/serial/serial_pl011.c ================================================ /* * copyright (c) 2018 min le (lemin9538@gmail.com) * * this program is free software; you can redistribute it and/or modify * it under the terms of the gnu general public license version 2 as * published by the free software foundation. * * this program is distributed in the hope that it will be useful, * but without any warranty; without even the implied warranty of * merchantability or fitness for a particular purpose. see the * gnu general public license for more details. * * you should have received a copy of the gnu general public license * along with this program. if not, see . */ #include #include #include #include #include "pl011.h" #include #include static void *base; static int __pl011_init(void *addr, int clock, int baudrate) { unsigned int temp; unsigned int divider; unsigned int remainder; unsigned int fraction; base = (void *)ptov(addr); temp = 16 * baudrate; divider = clock / temp; remainder = clock % temp; temp = (8 * remainder) / baudrate; fraction = (temp >> 1) + (temp & 1); iowrite32(0x0, base + UARTCR); iowrite32(0x0, base + UARTECR); iowrite32(0x0 | PL011_LCR_WORD_LENGTH_8 | \ PL011_LCR_ONE_STOP_BIT | \ PL011_LCR_PARITY_DISABLE | \ PL011_LCR_BREAK_DISABLE, base + UARTLCR_H); iowrite32(divider, base + UARTIBRD); iowrite32(fraction, base + UARTFBRD); iowrite32(INT_RX, base + UARTIMSC); // enable rx interrupt. iowrite32(PL011_ICR_CLR_ALL_IRQS, base + UARTICR); iowrite32(0x0 | PL011_CR_UART_ENABLE | \ PL011_CR_TX_ENABLE | \ PL011_CR_RX_ENABLE, base + UARTCR); return 0; } static int pl011_irq_handler(uint32_t irq, void *data) { unsigned int int_status; char buf[16]; int cnt = 0; /* * only support RX interrupt. */ int_status = ioread32(base + UARTMIS); if (!(int_status & INT_RX)) return 0; iowrite32(INT_RX, base + UARTICR); while (!(ioread32(base + UARTFR) & PL011_FR_RXFE_FLAG)) { buf[cnt++] = ioread32(base + UARTDR) & 0xff; if (cnt == 16) { console_recv(buf, cnt); cnt = 0; } } if (cnt) console_recv(buf, cnt); iowrite32(0, base + UARTECR); return 0; } static int pl011_irq_init(void) { return request_irq(CONFIG_UART_IRQ, pl011_irq_handler, 0, "pl011", NULL); } device_initcall(pl011_irq_init); static int pl011_init(char *arg) { return __pl011_init((void *)ptov(CONFIG_UART_BASE), 24000000, 115200); } static void serial_pl011_putc(char c) { while (ioread32(base + UARTFR) & PL011_FR_BUSY_FLAG); if (c == '\n') iowrite32('\r', base + UARTDR); iowrite32(c, base + UARTDR); } static char serial_pl011_getc(void) { while (ioread32(base + UARTFR) & PL011_FR_BUSY_FLAG); return ioread32(base + UARTDR); } DEFINE_CONSOLE(pl011, "pl011", pl011_init, serial_pl011_putc, serial_pl011_getc); ================================================ FILE: kernel/drivers/serial/serial_scif.c ================================================ // SPDX-License-Identifier: GPL-2.0 #include #include /* Register offsets */ #define SCIF_SCFTDR (0x0C) /* Transmit FIFO data register */ #define SCIF_SCFSR (0x10) /* Serial status register */ #define SCIF_SCFRDR (0x14) /* Receive FIFO data register */ /* Serial Status Register (SCFSR) */ #define SCFSR_TEND (1 << 6) /* Transmission End */ #define SCFSR_RDF (1 << 1) /* Receive FIFO Data Full */ #define SCFSR_DR (1 << 0) /* Receive Data Ready */ static void *serial_base = (void *)CONFIG_UART_BASE; static inline void __scif_serial_putc(char c) { /* Check for empty space in TX FIFO */ while (!(readw(serial_base + SCIF_SCFSR) & SCFSR_TEND)) ; writeb(c, serial_base + SCIF_SCFTDR); /* Clear required TX flags */ writew(readw(serial_base + SCIF_SCFSR) & ~SCFSR_TEND, serial_base + SCIF_SCFSR); } static void scif_serial_putc(char c) { if (c == '\n') __scif_serial_putc('\r'); __scif_serial_putc(c); } static char scif_serial_getc(void) { /* Check for available data bytes in RX FIFO */ if (!(readw(serial_base + SCIF_SCFSR) & (SCFSR_DR | SCFSR_RDF))) return 0; char c = readb(serial_base + SCIF_SCFRDR); /* dummy read */ readw(serial_base + SCIF_SCFSR); /* Clear required RX flags */ writew(~(SCFSR_DR | SCFSR_RDF), serial_base + SCIF_SCFSR); return c; } DEFINE_CONSOLE(scif, "renesas,scif", NULL, scif_serial_putc, scif_serial_getc); ================================================ FILE: kernel/drivers/tty.c ================================================ /* * Copyright (C) 2020 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include static LIST_HEAD(tty_list); static DEFINE_SPIN_LOCK(tty_lock); struct tty *alloc_tty(char *name, uint32_t id, int flags) { struct tty *tty; list_for_each_entry(tty, &tty_list, list) { if ((tty->id == id) || (strcmp(name, tty->name)==0)) { pr_err("tty is areadly register 0x%x\n", id); return NULL; } } tty = zalloc(sizeof(struct tty)); if (!tty) return NULL; tty->id = id; tty->flags = flags; strncpy(tty->name, name, TTY_NAME_SIZE - 1); return tty; } int register_tty(struct tty *tty) { unsigned long iflags; if (tty->ops == NULL) return -EINVAL; spin_lock_irqsave(&tty_lock, iflags); list_add_tail(&tty_list, &tty->list); spin_unlock_irqrestore(&tty_lock, iflags); return 0; } int release_tty(struct tty *tty) { unsigned long flags; if (!tty || (tty->list.next == NULL)) return -EINVAL; spin_lock_irqsave(&tty_lock, flags); list_del(&tty->list); spin_unlock_irqrestore(&tty_lock, flags); free(tty); return 0; } struct tty *open_tty(char *name) { struct tty *tty, *__tty = NULL; list_for_each_entry(tty, &tty_list, list) { if (strcmp(name, tty->name) == 0) { __tty = tty; break; } } if (!__tty || __tty->open) return NULL; if (tty->ops->open) tty->ops->open(tty); tty->open = 1; return __tty; } void close_tty(struct tty *tty) { if (!tty || !tty->open) return; if (tty->ops->close) tty->ops->close(tty); tty->open = 0; } ================================================ FILE: kernel/dtbs/Makefile ================================================ obj-y += foundation-v8-gicv2.dtb obj-y += foundation-v8-gicv3.dtb obj-y += bcm2837-rpi-3-b-plus.dtb obj-y += armada-3720-community-v5.dtb obj-y += bcm2838-rpi-4-b.dtb obj-y += bcm2838-rpi-4-b-32bit.dtb obj-y += kvim3.dtb obj-y += foundation-v8-gicv3-zephyr.dtb obj-y += qemu-arm64.dtb obj-y += r8a7795.dtb ================================================ FILE: kernel/dtbs/armada-3720-community-v5.dts ================================================ /dts-v1/; / { model = "Marvell Armada 3720 Community Board"; compatible = "marvell,armada-3720-community", "marvell,armada3720", "marvell,armada3710"; interrupt-parent = <0x1>; #address-cells = <0x2>; #size-cells = <0x2>; vms { vm0 { device_type = "virtual_machine"; vmid = <0>; vm_name = "espressobin_linux"; type = "linux"; vcpus = <1>; native_wfi; entry = <0x0 0x280000>; vcpu_affinity = <0>; setup_data = <0x0 0xfe00000>; memory = <0x0 0x0 0x0 0x10000000>; cmdline = "console=ttyMV0,115200 earlycon=ar3700_uart,0xd0012000 root=PARTUUID=89708921-01 rw rootwait net.ifnames=0 biosdevname=0"; }; }; cpus { #address-cells = <0x1>; #size-cells = <0x0>; cpu@0 { device_type = "cpu"; compatible = "arm,cortex-a53", "arm,armv8"; reg = <0x0>; clocks = <0x2 0x0>; operating-points-v2 = <0x3>; enable-method = "psci"; }; cpu@1 { device_type = "cpu"; compatible = "arm,cortex-a53", "arm,armv8"; reg = <0x1>; cks = <0x2 0x0>; operating-points-v2 = <0x3>; enable-method = "psci"; }; }; psci { compatible = "arm,psci-0.2"; method = "smc"; }; timer { compatible = "arm,armv8-timer"; interrupts = <0x1 0xd 0x304 0x1 0xe 0x304 0x1 0xb 0x304 0x1 0xa 0x304>; }; soc { compatible = "simple-bus"; #address-cells = <0x2>; #size-cells = <0x2>; ranges; internal-regs { #address-cells = <0x1>; #size-cells = <0x1>; compatible = "simple-bus"; ranges = <0x0 0x0 0xd0000000 0x2000000>; dma-coherent; interrupt-controller@1d00000 { compatible = "arm,gic-v3"; #interrupt-cells = <0x3>; interrupt-controller; interrupts = <0x1 0x9 0xf04>; reg = <0x1d00000 0x10000 0x1d40000 0x40000>; linux,phandle = <0x1>; phandle = <0x1>; }; }; }; chosen { minos,stdout = "mvebu"; }; memory { device_type = "memory"; reg = <0x0 0x0 0x0 0x40000000>; }; }; ================================================ FILE: kernel/dtbs/bcm2837-rpi-3-b-plus.dts ================================================ /dts-v1/; / { compatible = "raspberrypi,3-model-b-plus", "brcm,bcm2837"; model = "Raspberry Pi 3 Model B+"; interrupt-parent = <0x1>; #address-cells = <0x1>; #size-cells = <0x1>; chosen { minos,stdout = "bcm283x_mu"; }; vms { vm0 { device_type = "virtual_machine"; vmid = <0>; vm_name = "rpi3_linux_host"; type = "linux"; vcpus = <2>; native_wfi; entry = <0x0 0x00080000>; setup_data = <0x0 0x03e00000>; vcpu_affinity = <0 1 2 3>; memory = <0x0 0x00000000 0x0 0x28000000>; cmdline = "8250.nr_uarts=1 dwc_otg.lpm_enable=0 net.ifnames=0 earlycon=uart8250,mmio32,0x3f215040 console=ttyS0,115200 cma=64M root=/dev/mmcblk0p2 rw rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait noinitrd"; }; }; soc { compatible = "simple-bus"; #address-cells = <0x1>; #size-cells = <0x1>; ranges = <0x7e000000 0x3f000000 0x1000000 0x40000000 0x40000000 0x1000>; dma-ranges = <0xc0000000 0x0 0x3f000000>; interrupt-controller@7e00b200 { compatible = "brcm,bcm2836-armctrl-ic"; reg = <0x7e00b200 0x200>; interrupt-controller; #interrupt-cells = <0x2>; interrupt-parent = <0x3>; interrupts = <0x8 0x4>; phandle = <0x1>; }; local_intc@40000000 { compatible = "brcm,bcm2836-l1-intc"; reg = <0x40000000 0x100>; interrupt-controller; #interrupt-cells = <0x2>; interrupt-parent = <0x3>; phandle = <0x3>; }; }; timer { compatible = "arm,armv7-timer"; interrupt-parent = <0x3>; interrupts = <0x0 0x4 0x1 0x4 0x3 0x4 0x2 0x4>; always-on; }; cpus { #address-cells = <0x1>; #size-cells = <0x0>; enable-method = "brcm,bcm2836-smp"; cpu@0 { device_type = "cpu"; compatible = "arm,cortex-a53"; reg = <0x0>; enable-method = "spin-table"; cpu-release-addr = <0x0 0xd8>; }; cpu@1 { device_type = "cpu"; compatible = "arm,cortex-a53"; reg = <0x1>; enable-method = "spin-table"; cpu-release-addr = <0x0 0xe0>; }; cpu@2 { device_type = "cpu"; compatible = "arm,cortex-a53"; reg = <0x2>; enable-method = "spin-table"; cpu-release-addr = <0x0 0xe8>; }; cpu@3 { device_type = "cpu"; compatible = "arm,cortex-a53"; reg = <0x3>; enable-method = "spin-table"; cpu-release-addr = <0x0 0xf0>; }; }; memory { device_type = "memory"; reg = <0x0 0x40000000>; }; }; ================================================ FILE: kernel/dtbs/bcm2838-rpi-4-b-32bit.dts ================================================ /dts-v1/; / { compatible = "raspberrypi,4-model-b", "brcm,bcm2838", "brcm,bcm2837"; model = "Raspberry Pi 4 Model B"; interrupt-parent = <0x1>; #address-cells = <0x2>; #size-cells = <0x1>; aliases { serial0 = "/soc/serial@7e201000"; serial1 = "/soc/serial@7e215040"; }; chosen { minos,stdout = "bcm283x_mu"; extra-memory = <0x40000000 0xbc000000>; bootargs = "bootwait=3 tty=vm0 cpu_sched_class_mask=0x0"; }; vms { vm0 { device_type = "virtual_machine"; vmid = <0>; vm_name = "rpi4_linux_host"; type = "linux"; vm_32bit; native_wfi; vcpus = <2>; entry = <0x0 0x00008000>; setup_data = <0x0 0x03e00000>; vcpu_affinity = <0 1 0 0>; memory = <0x0 0x00000000 0x0 0x30000000 0x0 0x40000000 0x0 0x7c000000>; cmdline = "coherent_pool=1M 8250.nr_uarts=1 earlycon=dbcon,io,0x0 loglevel=9 cma=64M cma=256M video=HDMI-A-1:720x576M@50,margin_left=0,margin_right=0,margin_top=0,margin_bottom=0 smsc95xx.macaddr=DC:A6:32:1C:65:A1 vc_mem.mem_base=0x3ec00000 vc_mem.mem_size=0x40000000 dwc_otg.fiq_fix_enable=0 console=tty1 console=hvc0 root=/dev/mmcblk0p2 rw rootfstype=ext4 rootwait rootflags=noload net.ifnames=0 loglevel=9"; vm0_bdi { vm_console_vm0 { virtual_device; vc-dynamic-res; compatible = "minos,vm_console"; }; }; }; vm1 { device_type = "virtual_machine"; vmid = <1>; vm_name = "rpi4_vm1"; type = "linux"; vm_32bit; native_wfi; vcpus = <1>; entry = <0x0 0x30008000>; setup_data = <0x0 0x33e00000>; vcpu_affinity = <2 0 0 0>; memory = <0x0 0x30000000 0x0 0x7200000>; vm1_bdi { vm_console_vm1 { virtual_device; vc-dynamic-res; compatible = "minos,vm_console"; }; }; }; vm2 { device_type = "virtual_machine"; vmid = <2>; vm_name = "fvp_zephyr_vm2"; type = "zephyr"; vcpus = <1>; entry = <0x0 0x372003b8>; setup_data = <0x0 0x0>; vcpu_affinity = <3>; native_wfi; memory = <0x0 0x37200000 0x0 0x00200000>; vm2_bdi { #address-cells = <0x1>; #size-cells = <0x1>; vtimer_irq = <27>; virq_chip_vm2 { compatible = "arm,gicv2"; #address-cells = <0x1>; #size-cells = <0x1>; reg = <0x2f000000 0x10000>, <0x2c000000 0x2000>, <0x2c010000 0x2000>, <0x2c02f000 0x2000>; }; vmbox_controller@20000000 { compatible = "minos,vmbox-controller"; reg = <0x20000000 0x1000>; interrupts = <0 10 4>; }; vm_console_vm2 { virtual_device; compatible = "minos,vm_console"; reg = <0x20001000 0x2000>; interrupts = <0 0 4>; }; }; }; vmboxs { vmbox_hvc@0 { compatible = "minos,vmbox"; vmbox-type = "hvc"; vmbox-owner = <0x1 0x0>; vmbox-shmem-size = <8192>; vmbox-id = <0x3420 0xffff>; platform-device; }; vmbox_veth@1 { compatible = "minos,veth"; vmbox-type = "veth"; vmbox-owner = <0x0 0x1>; vmbox-id = <0x3430 0xffff>; vmbox-vqs = <2>; vmbox-vrings = <16>; vmbox-vring-size = <2048>; }; vmbox_hvc@2 { compatible = "minos,vmbox"; vmbox-type = "hvc"; vmbox-owner = <0x2 0x0>; vmbox-shmem-size = <8192>; vmbox-id = <0x3420 0xffff>; }; }; }; soc { compatible = "simple-bus"; #address-cells = <0x1>; #size-cells = <0x1>; ranges = <0x7e000000 0x0 0xfe000000 0x1800000 0x7c000000 0x0 0xfc000000 0x2000000 0x40000000 0x0 0xff800000 0x800000>; dma-ranges = <0xc0000000 0x0 0x0 0x3c000000>; gic400@40041000 { interrupt-controller; #interrupt-cells = <0x3>; compatible = "arm,gic-400"; reg = <0x40041000 0x1000 0x40042000 0x2000 0x40044000 0x2000 0x40046000 0x2000>; phandle = <0x1>; }; }; timer { compatible = "arm,armv7-timer"; interrupts = <0x1 0xd 0xf08 0x1 0xe 0xf08 0x1 0xb 0xf08 0x1 0xa 0xf08>; arm,cpu-registers-not-fw-configured; always-on; }; cpus { #address-cells = <0x1>; #size-cells = <0x0>; enable-method = "brcm,bcm2836-smp"; cpu@0 { device_type = "cpu"; compatible = "arm,cortex-a72"; reg = <0x0>; enable-method = "spin-table"; cpu-release-addr = <0x0 0xd8>; }; cpu@1 { device_type = "cpu"; compatible = "arm,cortex-a72"; reg = <0x1>; enable-method = "spin-table"; cpu-release-addr = <0x0 0xe0>; }; cpu@2 { device_type = "cpu"; compatible = "arm,cortex-a72"; reg = <0x2>; enable-method = "spin-table"; cpu-release-addr = <0x0 0xe8>; }; cpu@3 { device_type = "cpu"; compatible = "arm,cortex-a72"; reg = <0x3>; enable-method = "spin-table"; cpu-release-addr = <0x0 0xf0>; }; }; memory { device_type = "memory"; }; }; ================================================ FILE: kernel/dtbs/bcm2838-rpi-4-b.dts ================================================ /dts-v1/; / { compatible = "raspberrypi,4-model-b", "brcm,bcm2838", "brcm,bcm2837"; model = "Raspberry Pi 4 Model B"; interrupt-parent = <0x1>; #address-cells = <0x2>; #size-cells = <0x1>; aliases { serial0 = "/soc/serial@7e201000"; serial1 = "/soc/serial@7e215040"; }; chosen { minos,stdout = "bcm283x_mu"; }; vms { vm0 { device_type = "virtual_machine"; vmid = <0>; vm_name = "rpi4_linux_host"; type = "linux"; vcpus = <2>; entry = <0x0 0x00080000>; setup_data = <0x0 0x03e00000>; vcpu_affinity = <0 1 2 3>; memory = <0x0 0x00000000 0x0 0x37400000 0x0 0x40000000 0x0 0x7c000000>; cmdline = "coherent_pool=1M 8250.nr_uarts=1 earlycon=uart8250,mmio32,0xfe215040 loglevel=9 cma=64M cma=256M video=HDMI-A-1:720x576M@50,margin_left=0,margin_right=0,margin_top=0,margin_bottom=0 smsc95xx.macaddr=DC:A6:32:1C:65:A1 vc_mem.mem_base=0x3ec00000 vc_mem.mem_size=0x40000000 dwc_otg.fiq_fix_enable=0 console=tty1 console=ttyS0,115200 root=/dev/mmcblk0p2 rw rootfstype=ext4 rootwait rootflags=noload net.ifnames=0 loglevel=9"; }; }; thermal-zones { cpu-thermal { polling-delay-passive = <0x0>; polling-delay = <0x3e8>; thermal-sensors = <0x2>; coefficients = <0xfffffe19 0x641b8>; trips { cpu-crit { temperature = <0x13880>; hysteresis = <0x0>; type = "critical"; }; }; cooling-maps { }; }; }; soc { compatible = "simple-bus"; #address-cells = <0x1>; #size-cells = <0x1>; ranges = <0x7e000000 0x0 0xfe000000 0x1800000 0x7c000000 0x0 0xfc000000 0x2000000 0x40000000 0x0 0xff800000 0x800000>; dma-ranges = <0xc0000000 0x0 0x0 0x3c000000>; txp@7e004000 { compatible = "brcm,bcm2835-txp"; reg = <0x7e004000 0x20>; interrupts = <0x1 0xb>; }; dma@7e007000 { compatible = "brcm,bcm2835-dma"; reg = <0x7e007000 0xb00>; interrupts = <0x0 0x50 0x4 0x0 0x51 0x4 0x0 0x52 0x4 0x0 0x53 0x4 0x0 0x54 0x4 0x0 0x55 0x4 0x0 0x56 0x4 0x0 0x57 0x4 0x0 0x57 0x4 0x0 0x58 0x4 0x0 0x58 0x4>; interrupt-names = "dma0", "dma1", "dma2", "dma3", "dma4", "dma5", "dma6", "dma7", "dma8", "dma9", "dma10"; #dma-cells = <0x1>; brcm,dma-channel-mask = <0x7f5>; phandle = <0x9>; }; watchdog@7e100000 { compatible = "brcm,bcm2835-pm", "brcm,bcm2835-pm-wdt"; #power-domain-cells = <0x1>; #reset-cells = <0x1>; reg = <0x7e100000 0x114 0x7e00a000 0x24>; clocks = <0x3 0x15 0x3 0x1d 0x3 0x17 0x3 0x16>; clock-names = "v3d", "peri_image", "h264", "isp"; system-power-controller; }; cprman@7e101000 { compatible = "brcm,bcm2838-cprman"; #clock-cells = <0x1>; reg = <0x7e101000 0x2000>; clocks = <0x4 0x5 0x0 0x5 0x1 0x5 0x2 0x6 0x0 0x6 0x1 0x6 0x2>; phandle = <0x3>; }; rng@7e104000 { compatible = "brcm,bcm2835-rng"; reg = <0x7e104000 0x10>; interrupts = <0x2 0x1d>; }; mailbox@7e00b880 { compatible = "brcm,bcm2835-mbox"; reg = <0x7e00b880 0x40>; interrupts = <0x0 0x21 0x4>; #mbox-cells = <0x0>; }; gpio@7e200000 { compatible = "brcm,bcm2838-gpio", "brcm,bcm2835-gpio"; reg = <0x7e200000 0xb4>; interrupts = <0x0 0x71 0x4 0x0 0x72 0x4 0x0 0x73 0x4 0x0 0x74 0x4>; gpio-controller; #gpio-cells = <0x2>; interrupt-controller; #interrupt-cells = <0x2>; phandle = <0xf>; dpi_gpio0 { brcm,pins = <0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf 0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1a 0x1b>; brcm,function = <0x6>; }; emmc_gpio22 { brcm,pins = <0x16 0x17 0x18 0x19 0x1a 0x1b>; brcm,function = <0x7>; }; emmc_gpio34 { brcm,pins = <0x22 0x23 0x24 0x25 0x26 0x27>; brcm,function = <0x7>; brcm,pull = <0x0 0x2 0x2 0x2 0x2 0x2>; }; emmc_gpio48 { brcm,pins = <0x30 0x31 0x32 0x33 0x34 0x35>; brcm,function = <0x7>; }; gpclk0_gpio4 { brcm,pins = <0x4>; brcm,function = <0x4>; }; gpclk1_gpio5 { brcm,pins = <0x5>; brcm,function = <0x4>; }; gpclk1_gpio42 { brcm,pins = <0x2a>; brcm,function = <0x4>; }; gpclk1_gpio44 { brcm,pins = <0x2c>; brcm,function = <0x4>; }; gpclk2_gpio6 { brcm,pins = <0x6>; brcm,function = <0x4>; }; gpclk2_gpio43 { brcm,pins = <0x2b>; brcm,function = <0x4>; brcm,pull = <0x0>; phandle = <0x8>; }; i2c0_gpio0 { brcm,pins = <0x0 0x1>; brcm,function = <0x4>; }; i2c0_gpio28 { brcm,pins = <0x1c 0x1d>; brcm,function = <0x4>; }; i2c0_gpio44 { brcm,pins = <0x2c 0x2d>; brcm,function = <0x5>; }; i2c1_gpio2 { brcm,pins = <0x2 0x3>; brcm,function = <0x4>; }; i2c1_gpio44 { brcm,pins = <0x2c 0x2d>; brcm,function = <0x6>; }; jtag_gpio22 { brcm,pins = <0x16 0x17 0x18 0x19 0x1a 0x1b>; brcm,function = <0x3>; }; pcm_gpio18 { brcm,pins = <0x12 0x13 0x14 0x15>; brcm,function = <0x4>; }; pcm_gpio28 { brcm,pins = <0x1c 0x1d 0x1e 0x1f>; brcm,function = <0x6>; }; pwm0_gpio12 { brcm,pins = <0xc>; brcm,function = <0x4>; }; pwm0_gpio18 { brcm,pins = <0x12>; brcm,function = <0x2>; }; pwm0_gpio40 { brcm,pins = <0x28>; brcm,function = <0x4>; }; pwm1_gpio13 { brcm,pins = <0xd>; brcm,function = <0x4>; }; pwm1_gpio19 { brcm,pins = <0x13>; brcm,function = <0x2>; }; pwm1_gpio41 { brcm,pins = <0x29>; brcm,function = <0x4>; }; pwm1_gpio45 { brcm,pins = <0x2d>; brcm,function = <0x4>; }; sdhost_gpio48 { brcm,pins = <0x30 0x31 0x32 0x33 0x34 0x35>; brcm,function = <0x4>; }; spi0_gpio7 { brcm,pins = <0x7 0x8 0x9 0xa 0xb>; brcm,function = <0x4>; }; spi0_gpio35 { brcm,pins = <0x23 0x24 0x25 0x26 0x27>; brcm,function = <0x4>; }; spi1_gpio16 { brcm,pins = <0x10 0x11 0x12 0x13 0x14 0x15>; brcm,function = <0x3>; }; spi2_gpio40 { brcm,pins = <0x28 0x29 0x2a 0x2b 0x2c 0x2d>; brcm,function = <0x3>; }; uart0_gpio14 { brcm,pins = <0xe 0xf>; brcm,function = <0x4>; }; uart0_ctsrts_gpio16 { brcm,pins = <0x10 0x11>; brcm,function = <0x7>; }; uart0_ctsrts_gpio30 { brcm,pins = <0x1e 0x1f>; brcm,function = <0x7>; brcm,pull = <0x2 0x0>; }; uart0_gpio32 { brcm,pins = <0x20 0x21>; brcm,function = <0x7>; brcm,pull = <0x0 0x2>; phandle = <0x7>; }; uart0_gpio36 { brcm,pins = <0x24 0x25>; brcm,function = <0x6>; }; uart0_ctsrts_gpio38 { brcm,pins = <0x26 0x27>; brcm,function = <0x6>; }; uart1_gpio14 { brcm,pins = <0xe 0xf>; brcm,function = <0x2>; }; uart1_ctsrts_gpio16 { brcm,pins = <0x10 0x11>; brcm,function = <0x2>; }; uart1_gpio32 { brcm,pins = <0x20 0x21>; brcm,function = <0x2>; }; uart1_ctsrts_gpio30 { brcm,pins = <0x1e 0x1f>; brcm,function = <0x2>; }; uart1_gpio40 { brcm,pins = <0x28 0x29>; brcm,function = <0x2>; }; uart1_ctsrts_gpio42 { brcm,pins = <0x2a 0x2b>; brcm,function = <0x2>; }; uart1_pins { brcm,pins; brcm,function; brcm,pull; phandle = <0xb>; }; }; serial@7e201000 { compatible = "brcm,bcm2835-pl011", "arm,pl011", "arm,primecell"; reg = <0x7e201000 0x1000>; interrupts = <0x0 0x79 0x4>; clocks = <0x3 0x13 0x3 0x14>; clock-names = "uartclk", "apb_pclk"; arm,primecell-periphid = <0x241011>; pinctrl-names = "default"; pinctrl-0 = <0x7 0x8>; status = "okay"; }; mmc@7e202000 { compatible = "brcm,bcm2835-sdhost"; reg = <0x7e202000 0x100>; interrupts = <0x0 0x78 0x4>; clocks = <0x3 0x14>; dmas = <0x9 0xd>; dma-names = "rx-tx"; status = "disabled"; }; i2s@7e203000 { compatible = "brcm,bcm2835-i2s"; reg = <0x7e203000 0x24>; clocks = <0x3 0x1f>; dmas = <0x9 0x2 0x9 0x3>; dma-names = "tx", "rx"; status = "disabled"; }; spi@7e204000 { compatible = "brcm,bcm2835-spi"; reg = <0x7e204000 0x200>; interrupts = <0x0 0x76 0x4>; clocks = <0x3 0x14>; #address-cells = <0x1>; #size-cells = <0x0>; status = "disabled"; }; i2c@7e205000 { compatible = "brcm,bcm2835-i2c"; reg = <0x7e205000 0x1000>; interrupts = <0x0 0x75 0x4>; clocks = <0x3 0x14>; #address-cells = <0x1>; #size-cells = <0x0>; status = "disabled"; }; pixelvalve@7e206000 { compatible = "brcm,bcm2835-pixelvalve0"; reg = <0x7e206000 0x100>; interrupts = <0x0 0x6d 0x4>; }; pixelvalve@7e207000 { compatible = "brcm,bcm2835-pixelvalve1"; reg = <0x7e207000 0x100>; interrupts = <0x0 0x6e 0x4>; }; dpi@7e208000 { compatible = "brcm,bcm2835-dpi"; reg = <0x7e208000 0x8c>; clocks = <0x3 0x14 0x3 0x2c>; clock-names = "core", "pixel"; #address-cells = <0x1>; #size-cells = <0x0>; status = "disabled"; }; dsi@7e209000 { compatible = "brcm,bcm2835-dsi0"; reg = <0x7e209000 0x78>; interrupts = <0x0 0x64 0x4>; #address-cells = <0x1>; #size-cells = <0x0>; #clock-cells = <0x1>; clocks = <0x3 0x20 0x3 0x2f 0x3 0x31>; clock-names = "phy", "escape", "pixel"; clock-output-names = "dsi0_byte", "dsi0_ddr2", "dsi0_ddr"; phandle = <0x5>; }; aux@7e215000 { compatible = "brcm,bcm2835-aux"; #clock-cells = <0x1>; reg = <0x7e215000 0x8>; clocks = <0x3 0x14>; phandle = <0xa>; }; serial@7e215040 { compatible = "brcm,bcm2835-aux-uart"; reg = <0x7e215040 0x40>; interrupts = <0x0 0x5d 0x4>; clocks = <0xa 0x0>; status = "okay"; pinctrl-names = "default"; pinctrl-0 = <0xb>; }; spi@7e215080 { compatible = "brcm,bcm2835-aux-spi"; reg = <0x7e215080 0x40>; interrupts = <0x0 0x5d 0x4>; clocks = <0xa 0x1>; #address-cells = <0x1>; #size-cells = <0x0>; status = "disabled"; }; spi@7e2150c0 { compatible = "brcm,bcm2835-aux-spi"; reg = <0x7e2150c0 0x40>; interrupts = <0x0 0x5d 0x4>; clocks = <0xa 0x2>; #address-cells = <0x1>; #size-cells = <0x0>; status = "disabled"; }; pwm@7e20c000 { compatible = "brcm,bcm2835-pwm"; reg = <0x7e20c000 0x28>; clocks = <0x3 0x1e>; assigned-clocks = <0x3 0x1e>; assigned-clock-rates = <0x989680>; #pwm-cells = <0x2>; status = "disabled"; }; sdhci@7e300000 { compatible = "brcm,bcm2835-sdhci"; reg = <0x7e300000 0x100>; interrupts = <0x2 0x1e>; clocks = <0x3 0x1c>; status = "disabled"; }; hvs@7e400000 { compatible = "brcm,bcm2835-hvs"; reg = <0x7e400000 0x6000>; interrupts = <0x0 0x61 0x4>; }; dsi@7e700000 { compatible = "brcm,bcm2835-dsi1"; reg = <0x7e700000 0x8c>; interrupts = <0x0 0x6c 0x4>; #address-cells = <0x1>; #size-cells = <0x0>; #clock-cells = <0x1>; clocks = <0x3 0x23 0x3 0x30 0x3 0x32>; clock-names = "phy", "escape", "pixel"; clock-output-names = "dsi1_byte", "dsi1_ddr2", "dsi1_ddr"; status = "disabled"; phandle = <0x6>; }; i2c@7e804000 { compatible = "brcm,bcm2835-i2c"; reg = <0x7e804000 0x1000>; interrupts = <0x0 0x75 0x4>; clocks = <0x3 0x14>; #address-cells = <0x1>; #size-cells = <0x0>; status = "disabled"; }; i2c@7e805000 { compatible = "brcm,bcm2835-i2c"; reg = <0x7e805000 0x1000>; interrupts = <0x0 0x75 0x4>; clocks = <0x3 0x14>; #address-cells = <0x1>; #size-cells = <0x0>; status = "disabled"; phandle = <0xc>; }; vec@7e806000 { compatible = "brcm,bcm2835-vec"; reg = <0x7e806000 0x1000>; clocks = <0x3 0x18>; interrupts = <0x0 0x7b 0x4>; status = "disabled"; }; pixelvalve@7e807000 { compatible = "brcm,bcm2835-pixelvalve2"; reg = <0x7e807000 0x100>; interrupts = <0x0 0x6a 0x4>; }; hdmi@7e902000 { compatible = "brcm,bcm2835-hdmi"; reg = <0x7e902000 0x600 0x7e808000 0x100>; interrupts = <0x0 0x68 0x4 0x0 0x69 0x4>; ddc = <0xc>; clocks = <0x3 0x10 0x3 0x19>; clock-names = "pixel", "hdmi"; dmas = <0x9 0x11>; dma-names = "audio-rx"; status = "disabled"; }; usb@7e980000 { compatible = "brcm,bcm2835-usb"; reg = <0x7e980000 0x10000>; interrupts = <0x0 0x49 0x4>; #address-cells = <0x1>; #size-cells = <0x0>; clocks = <0xd>; clock-names = "otg"; phys = <0xe>; phy-names = "usb2-phy"; }; gpu { compatible = "brcm,bcm2835-vc4"; }; gic400@40041000 { interrupt-controller; #interrupt-cells = <0x3>; compatible = "arm,gic-400"; reg = <0x40041000 0x1000 0x40042000 0x2000 0x40044000 0x2000 0x40046000 0x2000>; phandle = <0x1>; }; thermal@7d5d2200 { compatible = "brcm,avs-tmon-bcm2838"; reg = <0x7d5d2200 0x2c>; interrupts = <0x0 0x89 0x4>; interrupt-names = "tmon"; clocks = <0x3 0x1b>; #thermal-sensor-cells = <0x0>; status = "okay"; phandle = <0x2>; }; emmc2@7e340000 { compatible = "brcm,bcm2711-emmc2"; status = "okay"; interrupts = <0x0 0x7e 0x4>; clocks = <0x3 0x33>; reg = <0x7e340000 0x100>; }; }; clocks { compatible = "simple-bus"; #address-cells = <0x1>; #size-cells = <0x0>; clock@3 { compatible = "fixed-clock"; reg = <0x3>; #clock-cells = <0x0>; clock-output-names = "osc"; clock-frequency = <0x337f980>; phandle = <0x4>; }; clock@4 { compatible = "fixed-clock"; reg = <0x4>; #clock-cells = <0x0>; clock-output-names = "otg"; clock-frequency = <0x1c9c3800>; phandle = <0xd>; }; }; phy { compatible = "usb-nop-xceiv"; #phy-cells = <0x0>; phandle = <0xe>; }; arm-pmu { compatible = "arm,cortex-a72-pmu", "arm,cortex-a53-pmu"; interrupts = <0x0 0x10 0x4 0x0 0x11 0x4 0x0 0x12 0x4 0x0 0x13 0x4>; }; timer { compatible = "arm,armv7-timer"; interrupts = <0x1 0xd 0xf08 0x1 0xe 0xf08 0x1 0xb 0xf08 0x1 0xa 0xf08>; arm,cpu-registers-not-fw-configured; always-on; }; cpus { #address-cells = <0x1>; #size-cells = <0x0>; enable-method = "brcm,bcm2836-smp"; cpu@0 { device_type = "cpu"; compatible = "arm,cortex-a72"; reg = <0x0>; enable-method = "spin-table"; cpu-release-addr = <0x0 0xd8>; }; cpu@1 { device_type = "cpu"; compatible = "arm,cortex-a72"; reg = <0x1>; enable-method = "spin-table"; cpu-release-addr = <0x0 0xe0>; }; cpu@2 { device_type = "cpu"; compatible = "arm,cortex-a72"; reg = <0x2>; enable-method = "spin-table"; cpu-release-addr = <0x0 0xe8>; }; cpu@3 { device_type = "cpu"; compatible = "arm,cortex-a72"; reg = <0x3>; enable-method = "spin-table"; cpu-release-addr = <0x0 0xf0>; }; }; memory { device_type = "memory"; }; leds { act { gpios = <0xf 0x2f 0x0>; }; }; }; ================================================ FILE: kernel/dtbs/foundation-v8-gicv2.dts ================================================ /dts-v1/; / { model = "FVP Foundation"; compatible = "arm,fvp-base", "arm,vexpress"; interrupt-parent = <0x1>; #address-cells = <0x2>; #size-cells = <0x2>; chosen { bootargs = "console=ttyAMA0 earlycon=pl011,0x1c090000 loglevel=8 consolelog=9 root=/dev/vda2 rw"; minos,stdout = "pl011"; }; aliases { serial0 = "/smb/motherboard/iofpga@3,00000000/uart@090000"; serial1 = "/smb/motherboard/iofpga@3,00000000/uart@0a0000"; serial2 = "/smb/motherboard/iofpga@3,00000000/uart@0b0000"; }; vms { vm1 { device_type = "virtual_machine"; vm_name = "fvp_linux_host"; host_vm; vmid = <1>; type = "linux"; vcpus = <2>; entry = <0x0 0x80080000>; setup_data = <0x0 0x83e00000>; vcpu_affinity = <0 1 2 3>; cmdline = "console=hvc0 earlycon=dbcon,io,0x0 loglevel=8 consolelog=9 root=/dev/vda2 rw"; memory = <0x0 0x80000000 0x0 0x10000000>; vm1_bdi { vm_console_vm1 { virtual_device; vc-dynamic-res; compatible = "minos,vm_console"; }; }; }; }; psci { compatible = "arm,psci-1.0", "arm,psci-0.2", "arm,psci"; method = "smc"; cpu_suspend = <0xc4000001>; cpu_off = <0x84000002>; cpu_on = <0xc4000003>; sys_poweroff = <0x84000008>; sys_reset = <0x84000009>; }; cpus { #address-cells = <0x2>; #size-cells = <0x0>; cpu-map { cluster0 { core0 { cpu = <0x2>; }; core1 { cpu = <0x3>; }; core2 { cpu = <0x4>; }; core3 { cpu = <0x5>; }; }; }; idle-states { entry-method = "arm,psci"; cpu-sleep-0 { compatible = "arm,idle-state"; local-timer-stop; arm,psci-suspend-param = <0x10000>; entry-latency-us = <0x28>; exit-latency-us = <0x64>; min-residency-us = <0x96>; linux,phandle = <0x6>; phandle = <0x6>; }; cluster-sleep-0 { compatible = "arm,idle-state"; local-timer-stop; arm,psci-suspend-param = <0x1010000>; entry-latency-us = <0x1f4>; exit-latency-us = <0x3e8>; min-residency-us = <0x9c4>; linux,phandle = <0x7>; phandle = <0x7>; }; }; cpu@0 { device_type = "cpu"; compatible = "arm,armv8"; reg = <0x0 0x0>; enable-method = "psci"; cpu-idle-states = <0x6 0x7>; next-level-cache = <0x8>; linux,phandle = <0x2>; phandle = <0x2>; }; cpu@1 { device_type = "cpu"; compatible = "arm,armv8"; reg = <0x0 0x1>; enable-method = "psci"; cpu-idle-states = <0x6 0x7>; next-level-cache = <0x8>; linux,phandle = <0x3>; phandle = <0x3>; }; cpu@2 { device_type = "cpu"; compatible = "arm,armv8"; reg = <0x0 0x2>; enable-method = "psci"; cpu-idle-states = <0x6 0x7>; next-level-cache = <0x8>; linux,phandle = <0x4>; phandle = <0x4>; }; cpu@3 { device_type = "cpu"; compatible = "arm,armv8"; reg = <0x0 0x3>; enable-method = "psci"; cpu-idle-states = <0x6 0x7>; next-level-cache = <0x8>; linux,phandle = <0x5>; phandle = <0x5>; }; l2-cache0 { compatible = "cache"; linux,phandle = <0x8>; phandle = <0x8>; }; }; memory@80000000 { device_type = "memory"; reg = <0x0 0x80000000 0x0 0x80000000 0x8 0x80000000 0x0 0x80000000>; }; interrupt-controller@2f000000 { compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic"; #interrupt-cells = <0x3>; #address-cells = <0x2>; #size-cells = <0x2>; interrupt-controller; reg = <0x0 0x2f000000 0x0 0x10000 0x0 0x2c000000 0x0 0x2000 0x0 0x2c010000 0x0 0x2000 0x0 0x2c02f000 0x0 0x2000>; interrupts = <0x1 0x9 0x4>; linux,phandle = <0x1>; phandle = <0x1>; its@2f020000 { compatible = "arm,gic-v3-its"; msi-controller; reg = <0x0 0x2f020000 0x0 0x20000>; }; }; timer { compatible = "arm,armv8-timer"; interrupts = <0x1 0xd 0xff01 0x1 0xe 0xff01 0x1 0xb 0xff01 0x1 0xa 0xff01>; clock-frequency = <0x5f5e100>; }; pmu { compatible = "arm,armv8-pmuv3"; interrupts = <0x0 0x3c 0x4 0x0 0x3d 0x4 0x0 0x3e 0x4 0x0 0x3f 0x4>; }; smb { compatible = "simple-bus"; #address-cells = <0x2>; #size-cells = <0x1>; ranges = <0x0 0x0 0x0 0x8000000 0x4000000 0x1 0x0 0x0 0x14000000 0x4000000 0x2 0x0 0x0 0x18000000 0x4000000 0x3 0x0 0x0 0x1c000000 0x4000000 0x4 0x0 0x0 0xc000000 0x4000000 0x5 0x0 0x0 0x10000000 0x4000000>; motherboard { arm,v2m-memory-map = "rs1"; compatible = "arm,vexpress,v2m-p1", "simple-bus"; #address-cells = <0x2>; #size-cells = <0x1>; ranges; ethernet@2,02000000 { compatible = "smsc,lan91c111"; reg = <0x2 0x2000000 0x10000>; interrupts = <0x0 0xf 0x4>; }; clk24mhz { compatible = "fixed-clock"; #clock-cells = <0x0>; clock-frequency = <0x16e3600>; clock-output-names = "v2m:clk24mhz"; linux,phandle = <0xb>; phandle = <0xb>; }; refclk1mhz { compatible = "fixed-clock"; #clock-cells = <0x0>; clock-frequency = <0xf4240>; clock-output-names = "v2m:refclk1mhz"; linux,phandle = <0xa>; phandle = <0xa>; }; refclk32khz { compatible = "fixed-clock"; #clock-cells = <0x0>; clock-frequency = <0x8000>; clock-output-names = "v2m:refclk32khz"; linux,phandle = <0x9>; phandle = <0x9>; }; iofpga@3,00000000 { compatible = "arm,amba-bus", "simple-bus"; #address-cells = <0x1>; #size-cells = <0x1>; ranges = <0x0 0x3 0x0 0x200000>; sysreg@010000 { compatible = "arm,vexpress-sysreg"; reg = <0x10000 0x1000>; gpio-controller; #gpio-cells = <0x2>; linux,phandle = <0xd>; phandle = <0xd>; }; sysctl@020000 { compatible = "arm,sp810", "arm,primecell"; reg = <0x20000 0x1000>; clocks = <0x9 0xa 0xb>; clock-names = "refclk", "timclk", "apb_pclk"; #clock-cells = <0x1>; clock-output-names = "timerclken0", "timerclken1", "timerclken2", "timerclken3"; linux,phandle = <0xc>; phandle = <0xc>; }; uart@090000 { compatible = "arm,pl011", "arm,primecell"; reg = <0x90000 0x1000>; interrupts = <0x0 0x5 0x4>; clocks = <0xb 0xb>; clock-names = "uartclk", "apb_pclk"; }; uart@0a0000 { compatible = "arm,pl011", "arm,primecell"; reg = <0xa0000 0x1000>; interrupts = <0x0 0x6 0x4>; clocks = <0xb 0xb>; clock-names = "uartclk", "apb_pclk"; }; uart@0b0000 { compatible = "arm,pl011", "arm,primecell"; reg = <0xb0000 0x1000>; interrupts = <0x0 0x7 0x4>; clocks = <0xb 0xb>; clock-names = "uartclk", "apb_pclk"; }; wdt@0f0000 { compatible = "arm,sp805", "arm,primecell"; reg = <0xf0000 0x1000>; interrupts = <0x0 0x0 0x4>; clocks = <0x9 0xb>; clock-names = "wdogclk", "apb_pclk"; }; timer@120000 { compatible = "arm,sp804", "arm,primecell"; reg = <0x120000 0x1000>; interrupts = <0x0 0x3 0x4>; clocks = <0xc 0x2 0xc 0x3 0xb>; clock-names = "timclken1", "timclken2", "apb_pclk"; }; rtc@170000 { compatible = "arm,pl031", "arm,primecell"; reg = <0x170000 0x1000>; interrupts = <0x0 0x4 0x4>; clocks = <0xb>; clock-names = "apb_pclk"; }; virtio_block@0130000 { compatible = "virtio,mmio"; reg = <0x130000 0x1000>; interrupts = <0x0 0x2a 0x4>; }; }; fixedregulator@0 { compatible = "regulator-fixed"; regulator-name = "3V3"; regulator-min-microvolt = <0x325aa0>; regulator-max-microvolt = <0x325aa0>; regulator-always-on; }; mcc { compatible = "arm,vexpress,config-bus", "simple-bus"; arm,vexpress,config-bridge = <0xd>; muxfpga@0 { compatible = "arm,vexpress-muxfpga"; arm,vexpress-sysreg,func = <0x7 0x0>; }; dvimode@0 { compatible = "arm,vexpress-dvimode"; arm,vexpress-sysreg,func = <0xb 0x0>; }; }; }; }; }; ================================================ FILE: kernel/dtbs/foundation-v8-gicv3-zephyr.dts ================================================ /dts-v1/; / { model = "FVP Base"; compatible = "arm,fvp-base", "arm,vexpress"; interrupt-parent = < 0x01 >; #address-cells = < 0x02 >; #size-cells = < 0x02 >; chosen { minos,stdout = "pl011"; bootargs = "bootwait=3 tty=vm0 rootfs=virtio-blk.drv"; minos,ramdisk-start = <0x0 0xc4000000>; minos,ramdisk-end = <0x0 0xC40E7000>; }; aliases { serial0 = "/smb@0,0/motherboard/iofpga@3,00000000/uart@90000"; serial1 = "/smb@0,0/motherboard/iofpga@3,00000000/uart@a0000"; serial2 = "/smb@0,0/motherboard/iofpga@3,00000000/uart@b0000"; serial3 = "/smb@0,0/motherboard/iofpga@3,00000000/uart@c0000"; }; psci { compatible = "arm,psci-1.0", "arm,psci-0.2", "arm,psci"; method = "smc"; cpu_suspend = < 0xc4000001 >; cpu_off = < 0x84000002 >; cpu_on = < 0xc4000003 >; sys_poweroff = < 0x84000008 >; sys_reset = < 0x84000009 >; }; cpus { #address-cells = < 0x02 >; #size-cells = < 0x00 >; cpu-map { cluster0 { core0 { cpu = < 0x02 >; }; core1 { cpu = < 0x03 >; }; core2 { cpu = < 0x04 >; }; core3 { cpu = < 0x05 >; }; }; cluster1 { core0 { cpu = < 0x06 >; }; core1 { cpu = < 0x07 >; }; core2 { cpu = < 0x08 >; }; core3 { cpu = < 0x09 >; }; }; }; idle-states { entry-method = "arm,psci"; cpu-sleep-0 { compatible = "arm,idle-state"; local-timer-stop; arm,psci-suspend-param = < 0x10000 >; entry-latency-us = < 0x28 >; exit-latency-us = < 0x64 >; min-residency-us = < 0x96 >; linux,phandle = < 0x0a >; phandle = < 0x0a >; }; cluster-sleep-0 { compatible = "arm,idle-state"; local-timer-stop; arm,psci-suspend-param = < 0x1010000 >; entry-latency-us = < 0x1f4 >; exit-latency-us = < 0x3e8 >; min-residency-us = < 0x9c4 >; linux,phandle = < 0x0b >; phandle = < 0x0b >; }; }; cpu@0 { device_type = "cpu"; compatible = "arm,armv8"; reg = < 0x00 0x00 >; enable-method = "psci"; cpu-idle-states = < 0x0a 0x0b >; next-level-cache = < 0x0c >; linux,phandle = < 0x02 >; phandle = < 0x02 >; }; cpu@1 { device_type = "cpu"; compatible = "arm,armv8"; reg = < 0x00 0x01 >; enable-method = "psci"; cpu-idle-states = < 0x0a 0x0b >; next-level-cache = < 0x0c >; linux,phandle = < 0x03 >; phandle = < 0x03 >; }; cpu@2 { device_type = "cpu"; compatible = "arm,armv8"; reg = < 0x00 0x02 >; enable-method = "psci"; cpu-idle-states = < 0x0a 0x0b >; next-level-cache = < 0x0c >; linux,phandle = < 0x04 >; phandle = < 0x04 >; }; cpu@3 { device_type = "cpu"; compatible = "arm,armv8"; reg = < 0x00 0x03 >; enable-method = "psci"; cpu-idle-states = < 0x0a 0x0b >; next-level-cache = < 0x0c >; linux,phandle = < 0x05 >; phandle = < 0x05 >; }; cpu@100 { device_type = "cpu"; compatible = "arm,armv8"; reg = < 0x00 0x100 >; enable-method = "psci"; cpu-idle-states = < 0x0a 0x0b >; next-level-cache = < 0x0c >; linux,phandle = < 0x06 >; phandle = < 0x06 >; }; cpu@101 { device_type = "cpu"; compatible = "arm,armv8"; reg = < 0x00 0x101 >; enable-method = "psci"; cpu-idle-states = < 0x0a 0x0b >; next-level-cache = < 0x0c >; linux,phandle = < 0x07 >; phandle = < 0x07 >; }; cpu@102 { device_type = "cpu"; compatible = "arm,armv8"; reg = < 0x00 0x102 >; enable-method = "psci"; cpu-idle-states = < 0x0a 0x0b >; next-level-cache = < 0x0c >; linux,phandle = < 0x08 >; phandle = < 0x08 >; }; cpu@103 { device_type = "cpu"; compatible = "arm,armv8"; reg = < 0x00 0x103 >; enable-method = "psci"; cpu-idle-states = < 0x0a 0x0b >; next-level-cache = < 0x0c >; linux,phandle = < 0x09 >; phandle = < 0x09 >; }; l2-cache0 { compatible = "cache"; linux,phandle = < 0x0c >; phandle = < 0x0c >; }; }; memory@80000000 { device_type = "memory"; reg = <0x0 0x80000000 0x0 0x80000000 0x8 0x80000000 0x0 0x80000000>; }; interrupt-controller@2f000000 { compatible = "arm,gic-v3"; #interrupt-cells = < 0x03 >; #address-cells = < 0x02 >; #size-cells = < 0x02 >; ranges; interrupt-controller; reg = < 0x00 0x2f000000 0x00 0x10000 0x00 0x2f100000 0x00 0x200000 0x00 0x2c000000 0x00 0x2000 0x00 0x2c010000 0x00 0x2000 0x00 0x2c02f000 0x00 0x2000 >; interrupts = < 0x01 0x09 0x04 >; linux,phandle = < 0x01 >; phandle = < 0x01 >; its@2f020000 { compatible = "arm,gic-v3-its"; msi-controller; reg = < 0x00 0x2f020000 0x00 0x20000 >; }; }; timer { compatible = "arm,armv8-timer"; interrupts = < 0x01 0x0d 0xff01 0x01 0x0e 0xff01 0x01 0x0b 0xff01 0x01 0x0a 0xff01 >; clock-frequency = < 0x5f5e100 >; }; timer@2a810000 { compatible = "arm,armv7-timer-mem"; reg = < 0x00 0x2a810000 0x00 0x10000 >; clock-frequency = < 0x5f5e100 >; #address-cells = < 0x02 >; #size-cells = < 0x02 >; ranges; frame@2a830000 { frame-number = < 0x01 >; interrupts = < 0x00 0x1a 0x04 >; reg = < 0x00 0x2a830000 0x00 0x10000 >; }; }; pmu { compatible = "arm,armv8-pmuv3"; interrupts = < 0x00 0x3c 0x04 0x00 0x3d 0x04 0x00 0x3e 0x04 0x00 0x3f 0x04 >; }; smb@0,0 { compatible = "simple-bus"; #address-cells = < 0x02 >; #size-cells = < 0x01 >; ranges = < 0x00 0x00 0x00 0x8000000 0x4000000 0x01 0x00 0x00 0x14000000 0x4000000 0x02 0x00 0x00 0x18000000 0x4000000 0x03 0x00 0x00 0x1c000000 0x4000000 0x04 0x00 0x00 0xc000000 0x4000000 0x05 0x00 0x00 0x10000000 0x4000000 >; motherboard { arm,v2m-memory-map = "rs1"; compatible = "arm,vexpress,v2m-p1\0simple-bus"; #address-cells = < 0x02 >; #size-cells = < 0x01 >; ranges; flash@0,00000000 { compatible = "arm,vexpress-flash\0cfi-flash"; reg = < 0x00 0x00 0x4000000 0x04 0x00 0x4000000 >; bank-width = < 0x04 >; }; vram@2,00000000 { compatible = "arm,vexpress-vram"; reg = < 0x02 0x00 0x800000 >; }; ethernet@2,02000000 { compatible = "smsc,lan91c111"; reg = < 0x02 0x2000000 0x10000 >; interrupts = < 0x00 0x0f 0x04 >; }; clk24mhz { compatible = "fixed-clock"; #clock-cells = < 0x00 >; clock-frequency = < 0x16e3600 >; clock-output-names = "v2m:clk24mhz"; linux,phandle = < 0x0f >; phandle = < 0x0f >; }; refclk1mhz { compatible = "fixed-clock"; #clock-cells = < 0x00 >; clock-frequency = < 0xf4240 >; clock-output-names = "v2m:refclk1mhz"; linux,phandle = < 0x0e >; phandle = < 0x0e >; }; refclk32khz { compatible = "fixed-clock"; #clock-cells = < 0x00 >; clock-frequency = < 0x8000 >; clock-output-names = "v2m:refclk32khz"; linux,phandle = < 0x0d >; phandle = < 0x0d >; }; iofpga@3,00000000 { compatible = "arm,amba-bus\0simple-bus"; #address-cells = < 0x01 >; #size-cells = < 0x01 >; ranges = < 0x00 0x03 0x00 0x200000 >; sysreg@10000 { compatible = "arm,vexpress-sysreg"; reg = < 0x10000 0x1000 >; gpio-controller; #gpio-cells = < 0x02 >; linux,phandle = < 0x10 >; phandle = < 0x10 >; }; sysctl@20000 { compatible = "arm,sp810\0arm,primecell"; reg = < 0x20000 0x1000 >; clocks = < 0x0d 0x0e 0x0f >; clock-names = "refclk\0timclk\0apb_pclk"; #clock-cells = < 0x01 >; clock-output-names = "timerclken0\0timerclken1\0timerclken2\0timerclken3"; linux,phandle = < 0x12 >; phandle = < 0x12 >; }; aaci@40000 { compatible = "arm,pl041\0arm,primecell"; reg = < 0x40000 0x1000 >; interrupts = < 0x00 0x0b 0x04 >; clocks = < 0x0f >; clock-names = "apb_pclk"; }; mmci@50000 { compatible = "arm,pl180\0arm,primecell"; reg = < 0x50000 0x1000 >; interrupts = < 0x00 0x09 0x04 0x00 0x0a 0x04 >; cd-gpios = < 0x10 0x00 0x00 >; wp-gpios = < 0x10 0x01 0x00 >; max-frequency = < 0xb71b00 >; vmmc-supply = < 0x11 >; clocks = < 0x0f 0x0f >; clock-names = "mclk\0apb_pclk"; }; kmi@60000 { compatible = "arm,pl050\0arm,primecell"; reg = < 0x60000 0x1000 >; interrupts = < 0x00 0x0c 0x04 >; clocks = < 0x0f 0x0f >; clock-names = "KMIREFCLK\0apb_pclk"; }; kmi@70000 { compatible = "arm,pl050\0arm,primecell"; reg = < 0x70000 0x1000 >; interrupts = < 0x00 0x0d 0x04 >; clocks = < 0x0f 0x0f >; clock-names = "KMIREFCLK\0apb_pclk"; }; uart@90000 { compatible = "arm,pl011\0arm,primecell"; reg = < 0x90000 0x1000 >; interrupts = < 0x00 0x05 0x04 >; clocks = < 0x0f 0x0f >; clock-names = "uartclk\0apb_pclk"; }; uart@a0000 { compatible = "arm,pl011\0arm,primecell"; reg = < 0xa0000 0x1000 >; interrupts = < 0x00 0x06 0x04 >; clocks = < 0x0f 0x0f >; clock-names = "uartclk\0apb_pclk"; }; uart@b0000 { compatible = "arm,pl011\0arm,primecell"; reg = < 0xb0000 0x1000 >; interrupts = < 0x00 0x07 0x04 >; clocks = < 0x0f 0x0f >; clock-names = "uartclk\0apb_pclk"; }; uart@c0000 { compatible = "arm,pl011\0arm,primecell"; reg = < 0xc0000 0x1000 >; interrupts = < 0x00 0x08 0x04 >; clocks = < 0x0f 0x0f >; clock-names = "uartclk\0apb_pclk"; }; wdt@f0000 { compatible = "arm,sp805\0arm,primecell"; reg = < 0xf0000 0x1000 >; interrupts = < 0x00 0x00 0x04 >; clocks = < 0x0d 0x0f >; clock-names = "wdogclk\0apb_pclk"; }; timer@110000 { compatible = "arm,sp804\0arm,primecell"; reg = < 0x110000 0x1000 >; interrupts = < 0x00 0x02 0x04 >; clocks = < 0x12 0x00 0x12 0x01 0x0f >; clock-names = "timclken1\0timclken2\0apb_pclk"; }; timer@120000 { compatible = "arm,sp804\0arm,primecell"; reg = < 0x120000 0x1000 >; interrupts = < 0x00 0x03 0x04 >; clocks = < 0x12 0x02 0x12 0x03 0x0f >; clock-names = "timclken1\0timclken2\0apb_pclk"; }; rtc@170000 { compatible = "arm,pl031\0arm,primecell"; reg = < 0x170000 0x1000 >; interrupts = < 0x00 0x04 0x04 >; clocks = < 0x0f >; clock-names = "apb_pclk"; }; clcd@1f0000 { compatible = "arm,pl111\0arm,primecell"; reg = < 0x1f0000 0x1000 >; interrupts = < 0x00 0x0e 0x04 >; clocks = < 0x13 0x0f >; clock-names = "clcdclk\0apb_pclk"; mode = "XVGA"; use_dma = < 0x00 >; framebuffer = < 0x18000000 0x180000 >; }; virtio_block@130000 { compatible = "virtio,mmio"; reg = < 0x130000 0x1000 >; interrupts = < 0x00 0x2a 0x04 >; }; }; fixedregulator { compatible = "regulator-fixed"; regulator-name = "3V3"; regulator-min-microvolt = < 0x325aa0 >; regulator-max-microvolt = < 0x325aa0 >; regulator-always-on; linux,phandle = < 0x11 >; phandle = < 0x11 >; }; mcc { compatible = "arm,vexpress,config-bus\0simple-bus"; arm,vexpress,config-bridge = < 0x10 >; osc { compatible = "arm,vexpress-osc"; arm,vexpress-sysreg,func = < 0x01 0x01 >; freq-range = < 0x16a6570 0x3c8eee0 >; #clock-cells = < 0x00 >; clock-output-names = "v2m:oscclk1"; linux,phandle = < 0x13 >; phandle = < 0x13 >; }; muxfpga { compatible = "arm,vexpress-muxfpga"; arm,vexpress-sysreg,func = < 0x07 0x00 >; }; dvimode { compatible = "arm,vexpress-dvimode"; arm,vexpress-sysreg,func = < 0x0b 0x00 >; }; }; }; }; panels { panel { compatible = "panel"; mode = "XVGA"; refresh = < 0x3c >; xres = < 0x400 >; yres = < 0x300 >; pixclock = < 0x3d84 >; left_margin = < 0x98 >; right_margin = < 0x30 >; upper_margin = < 0x17 >; lower_margin = < 0x03 >; hsync_len = < 0x68 >; vsync_len = < 0x04 >; sync = < 0x00 >; vmode = "FB_VMODE_NONINTERLACED"; tim2 = "TIM2_BCD\0TIM2_IPC"; cntl = "CNTL_LCDTFT\0CNTL_BGR\0CNTL_LCDVCOMP(1)"; caps = "CLCD_CAP_5551\0CLCD_CAP_565\0CLCD_CAP_888"; bpp = < 0x10 >; }; }; }; ================================================ FILE: kernel/dtbs/foundation-v8-gicv3.dts ================================================ /dts-v1/; / { model = "FVP Base"; compatible = "arm,fvp-base", "arm,vexpress"; interrupt-parent = <0x1>; #address-cells = <0x2>; #size-cells = <0x2>; chosen { minos,stdout = "pl011"; bootargs = "bootwait=3 tty=vm1"; minos,ramdisk-start = <0x0 0xc4000000>; minos,ramdisk-end = <0x0 0xC522F000>; }; aliases { serial0 = "/smb@0,0/motherboard/iofpga@3,00000000/uart@90000"; }; vms { vm1 { device_type = "virtual_machine"; vm_name = "fvp_linux_host"; host_vm; vmid = <1>; type = "linux"; vcpus = <2>; entry = <0x0 0x80080000>; setup_data = <0x0 0x83e00000>; vcpu_affinity = <0 1 2 3>; cmdline = "console=hvc0 earlycon=dbcon,io,0x0 loglevel=8 consolelog=9 root=/dev/vda2 rw"; memory = <0x0 0x80000000 0x0 0x10000000>; vm1_bdi { vm_console_vm1 { virtual_device; vc-dynamic-res; compatible = "minos,vm_console"; }; }; }; }; psci { compatible = "arm,psci-1.0", "arm,psci-0.2", "arm,psci"; method = "smc"; cpu_suspend = <0xc4000001>; cpu_off = <0x84000002>; cpu_on = <0xc4000003>; sys_poweroff = <0x84000008>; sys_reset = <0x84000009>; }; cpus { #address-cells = <0x2>; #size-cells = <0x0>; cpu-map { cluster0 { core0 { cpu = <0x2>; }; core1 { cpu = <0x3>; }; core2 { cpu = <0x4>; }; core3 { cpu = <0x5>; }; }; }; idle-states { entry-method = "arm,psci"; cpu-sleep-0 { compatible = "arm,idle-state"; local-timer-stop; arm,psci-suspend-param = <0x10000>; entry-latency-us = <0x28>; exit-latency-us = <0x64>; min-residency-us = <0x96>; linux,phandle = <0xa>; phandle = <0xa>; }; cluster-sleep-0 { compatible = "arm,idle-state"; local-timer-stop; arm,psci-suspend-param = <0x1010000>; entry-latency-us = <0x1f4>; exit-latency-us = <0x3e8>; min-residency-us = <0x9c4>; linux,phandle = <0xb>; phandle = <0xb>; }; }; cpu@0 { device_type = "cpu"; compatible = "arm,armv8"; reg = <0x0 0x0>; enable-method = "psci"; cpu-idle-states = <0xa 0xb>; next-level-cache = <0xc>; linux,phandle = <0x2>; phandle = <0x2>; }; cpu@1 { device_type = "cpu"; compatible = "arm,armv8"; reg = <0x0 0x1>; enable-method = "psci"; cpu-idle-states = <0xa 0xb>; next-level-cache = <0xc>; linux,phandle = <0x3>; phandle = <0x3>; }; cpu@2 { device_type = "cpu"; compatible = "arm,armv8"; reg = <0x0 0x2>; enable-method = "psci"; cpu-idle-states = <0xa 0xb>; next-level-cache = <0xc>; linux,phandle = <0x4>; phandle = <0x4>; }; cpu@3 { device_type = "cpu"; compatible = "arm,armv8"; reg = <0x0 0x3>; enable-method = "psci"; cpu-idle-states = <0xa 0xb>; next-level-cache = <0xc>; linux,phandle = <0x5>; phandle = <0x5>; }; l2-cache0 { compatible = "cache"; linux,phandle = <0xc>; phandle = <0xc>; }; }; memory@80000000 { device_type = "memory"; reg = <0x0 0x80000000 0x0 0x80000000 0x8 0x80000000 0x0 0x80000000>; }; interrupt-controller@2f000000 { compatible = "arm,gic-v3"; #interrupt-cells = <0x3>; #address-cells = <0x2>; #size-cells = <0x2>; ranges; interrupt-controller; reg = <0x0 0x2f000000 0x0 0x10000 0x0 0x2f100000 0x0 0x200000 0x0 0x2c000000 0x0 0x2000 0x0 0x2c010000 0x0 0x2000 0x0 0x2c02f000 0x0 0x2000>; interrupts = <0x1 0x9 0x4>; linux,phandle = <0x1>; phandle = <0x1>; its@2f020000 { compatible = "arm,gic-v3-its"; msi-controller; reg = <0x0 0x2f020000 0x0 0x20000>; }; }; timer { compatible = "arm,armv8-timer"; interrupts = <0x1 0xd 0xff01 0x1 0xe 0xff01 0x1 0xb 0xff01 0x1 0xa 0xff01>; clock-frequency = <0x5f5e100>; }; pmu { compatible = "arm,armv8-pmuv3"; interrupts = <0x0 0x3c 0x4 0x0 0x3d 0x4 0x0 0x3e 0x4 0x0 0x3f 0x4>; }; smb@0,0 { compatible = "simple-bus"; #address-cells = <0x2>; #size-cells = <0x1>; ranges = <0x0 0x0 0x0 0x8000000 0x4000000 0x1 0x0 0x0 0x14000000 0x4000000 0x2 0x0 0x0 0x18000000 0x4000000 0x3 0x0 0x0 0x1c000000 0x4000000 0x4 0x0 0x0 0xc000000 0x4000000 0x5 0x0 0x0 0x10000000 0x4000000>; motherboard { arm,v2m-memory-map = "rs1"; compatible = "arm,vexpress,v2m-p1", "simple-bus"; #address-cells = <0x2>; #size-cells = <0x1>; ranges; clk24mhz { compatible = "fixed-clock"; #clock-cells = <0x0>; clock-frequency = <0x16e3600>; clock-output-names = "v2m:clk24mhz"; linux,phandle = <0xf>; phandle = <0xf>; }; refclk1mhz { compatible = "fixed-clock"; #clock-cells = <0x0>; clock-frequency = <0xf4240>; clock-output-names = "v2m:refclk1mhz"; linux,phandle = <0xe>; phandle = <0xe>; }; refclk32khz { compatible = "fixed-clock"; #clock-cells = <0x0>; clock-frequency = <0x8000>; clock-output-names = "v2m:refclk32khz"; linux,phandle = <0xd>; phandle = <0xd>; }; }; }; }; ================================================ FILE: kernel/dtbs/kvim3.dts ================================================ /dts-v1/; / { interrupt-parent = < 0x01 >; #address-cells = < 0x02 >; #size-cells = < 0x02 >; compatible = "khadas,vim3"; model = "Khadas VIM3"; chosen { minos,stdout = "aml_meson"; bootargs = "bootwait=3 tty=vm0"; }; psci { compatible = "arm,psci-1.0"; method = "smc"; }; soc { compatible = "simple-bus"; #address-cells = < 0x02 >; #size-cells = < 0x02 >; ranges; interrupt-controller@ffc01000 { compatible = "arm,gic-400"; reg = < 0x00 0xffc01000 0x00 0x1000 0x00 0xffc02000 0x00 0x2000 0x00 0xffc04000 0x00 0x2000 0x00 0xffc06000 0x00 0x2000 >; interrupt-controller; interrupts = < 0x01 0x09 0xff04 >; #interrupt-cells = < 0x03 >; #address-cells = < 0x00 >; phandle = < 0x01 >; }; }; vms { vm0 { device_type = "virtual_machine"; vmid = <0>; vm_name = "android"; type = "linux"; vm_32bit; native_wfi; no_of_resource; vcpus = <4>; entry = <0x0 0x00108000>; setup_data = <0x0 0x1ffe6000>; vcpu_affinity = <0 1 2 3>; memory = <0x0 0x00000000 0x0 0x80000000>; cmdline = "init=/init console=hvc0 no_console_suspend earlycon=earlycon=dbcon,io,0x0 ramoops.pstore_en=1 ramoops.record_size=0x8000 ramoops.console_size=0x4000 otg_device=1 reboot_mode_android=normal logo=viu2_osd0,loaded,0x3d800000 fb_width=1920 fb_height=1080 vout2=panel,enable vout=1080p60hz,enable panel_type=lcd_1 lcd_ctrl=0x00000083 hdmitx=,444,8bit hdmimode=1080p60hz frac_rate_policy=1 hdmi_read_edid=1 cvbsmode=576cvbs osd_reverse=0 video_reverse=0 irq_check_en=0 androidboot.selinux=permissive androidboot.firstboot=0 jtag=disable wol_enable=0 spi_state=0 fusb302_state=1 hwver=VIM3.V12 factory_mac=c8:63:14:70:47:ac lcd_exist=1 androidboot.hardware=amlogic androidboot.serialno=c863147047ac mac=c8:63:14:70:47:ac androidboot.mac=c8:63:14:70:47:ac ro rootwait skip_initramfs androidboot.dtbo_idx=0 --cmdline root=/dev/mmcblk0p18 buildvariant=userdebug"; vm0_bdi { #address-cells = <0x1>; #size-cells = <0x1>; vtimer_irq = <27>; iomem = <0x0 0xff600000 0x0 0xff600000 0x0 0x45000>, /* 0xff620000 ----> 0xff645000 */ <0x0 0xff650000 0x0 0xff650000 0x0 0x8000>, /* 0xff650000 ----> 0xff658000 */ <0x0 0xff646000 0x0 0xff646000 0x0 0x7000>, /* 0xff646000 ----> 0xff64d000 */ <0x0 0xff900000 0x0 0xff900000 0x0 0x51000>, /* 0xff900000 ----> 0xff951000 */ <0x0 0xff800000 0x0 0xff800000 0x0 0x10000>, /* 0xff800000 ----> 0xff810000 */ <0x0 0xffd00000 0x0 0xffd00000 0x0 0x100000>, /* 0xffd00000 ----> 0xffe00000 */ <0x0 0xff000000 0x0 0xff000000 0x0 0x440000>, /* 0xff000000 ----> 0xff440000 */ <0x0 0xfc000000 0x0 0xfc000000 0x0 0x600000>, /* 0xfc000000 ----> 0xfc600000 */ <0x0 0xff500000 0x0 0xff500000 0x0 0x100000>, /* 0xff500000 ----> 0xff600000 */ <0x0 0xffe01000 0x0 0xffe01000 0x0 0x7f000>, /* 0xffe01000 ----> 0xffe80000 */ <0x0 0xfffe7000 0x0 0xfffe7000 0x0 0x1000>; /* 0xfffe7000 ----> 0xfffe8000 */ virqs = <0x21 0x21>, /* 33 locker */ <0x48 0x48>, /* 72 deinterlace */ <0x4e 0x4e>, /* 78 deinterlace */ <0x78 0x78>, /* 120 phy-csi@ff650000 */ <0x77 0x77>, /* 119 phy-csi@ff650000 */ <0x6a 0x6a>, /* 106 phy-csi@ff650000 */ <0x68 0x68>, /* 104 phy-csi@ff650000 */ <0x4a 0x4a>, /* 74 phy-csi@ff650000 */ <0x49 0x49>, /* 73 phy-csi@ff650000 */ <0xd3 0xd3>, /* 211 isp-adapter@ff650000 */ <0xae 0xae>, /* 174 isp@ff140000 */ <0x31 0x31>, /* 49 isp-sc@ff655400 */ <0x53 0x53>, /* 83 dmc_monitor */ <0x54 0x54>, /* 84 ddr_bandwidth */ <0x42 0x42>, /* 66 nfc@0 */ <0xdd 0xdd>, /* 221 sdio@ffe03000 */ <0xde 0xde>, /* 222 sd@ffe05000 */ <0xdf 0xdf>, /* 223 emmc@ffe07000 */ <0xe6 0xe6>, /* 230 meson-irblaster */ <0x58 0x58>, /* 88 meson-fb */ <0x79 0x79>, /* 121 rdma */ <0xdb 0xdb>, /* 219 hevc_enc */ <0x4d 0x4d>, /* 77 vdec */ <0x4c 0x4c>, /* 76 vdec */ <0x4b 0x4b>, /* 75 vdec */ <0x40 0x40>, /* 64 vdec */ <0x37 0x37>, /* 55 vdec */ <0xb0 0xb0>, /* 176 gdc */ <0x23 0x23>, /* 35 meson-amvideom */ <0xb2 0xb2>, /* 178 ge2d */ <0x75 0x75>, /* 117 vdin1 */ <0x73 0x73>, /* 115 vdin0 */ <0xe7 0xe7>, /* 231 aocec */ <0xeb 0xeb>, /* 235 aocec */ <0xb3 0xb3>, /* 179 galcore */ <0x59 0x59>, /* 89 amhdmitx */ <0xfd 0xfd>, /* 253 pcieA@fc000000 */ <0x7d 0x7d>, /* 125 serial@ffd22000 */ <0x6b 0x6b>, /* 107 serial@ffd23000 */ <0x3a 0x3a>, /* 58 serial@ffd24000 */ <0xe4 0xe4>, /* 228 rc@0xff808040 */ <0xbb 0xbb>, /* 187 pwrdet */ <0xb7 0xb7>, /* 183 spdif */ <0xba 0xba>, /* 186 ddr_manager */ <0xb9 0xb9>, /* 185 ddr_manager */ <0xb8 0xb8>, /* 184 ddr_manager */ <0xb6 0xb6>, /* 182 ddr_manager */ <0xb5 0xb5>, /* 181 ddr_manager */ <0xb4 0xb4>, /* 180 ddr_manager */ <0xe5 0xe5>, /* 229 serial@4000 */ <0xe1 0xe1>, /* 225 serial@3000 */ <0xe2 0xe2>, /* 226 i2c_slave@6000 */ <0xe9 0xe9>, /* 233 i2c@5000 */ <0xe3 0xe3>, /* 227 i2c@5000 */ <0x7a 0x7a>, /* 122 spi@15000 */ <0x71 0x71>, /* 113 spi@13000 */ <0x7f 0x7f>, /* 127 i2c@1c000 */ <0x47 0x47>, /* 71 i2c@1c000 */ <0x7e 0x7e>, /* 126 i2c@1d000 */ <0xf7 0xf7>, /* 247 i2c@1d000 */ <0x7c 0x7c>, /* 124 i2c@1e000 */ <0xf6 0xf6>, /* 246 i2c@1e000 */ <0x7b 0x7b>, /* 123 i2c@1f000 */ <0x35 0x35>, /* 53 i2c@1f000 */ <0x44 0x44>, /* 68 d_tsensor@ff800228 */ <0x43 0x43>, /* 67 p_tsensor@ff634594 */ <0xe8 0xe8>, /* 232 saradc */ <0x3f 0x3f>, /* 63 dwc2_a@ff400000 */ <0x30 0x30>, /* 48 usb3phy@ffe09080 */ <0x3e 0x3e>, /* 62 dwc3@ff500000 */ <0x28 0x28>, /* 40 ethernet@ff3f0000 */ <0xf2 0xf2>, /* 242 mhu@c883c400 */ <0xf1 0xf1>, /* 241 mhu@c883c400 */ <0xcb 0xcb>, /* 203 arm_pmu */ <0xa9 0xa9>, /* 169 arm_pmu */ <0x5c 0x5c>, /* 92 timer_bc */ <0xc2 0xc2>, /* 194 bifrost */ <0xc1 0xc1>, /* 193 bifrost */ <0xc0 0xc0>; /* 192 bifrost */ virq_chip_vm0 { compatible = "arm,gicv2"; #address-cells = <0x1>; #size-cells = <0x1>; reg = <0xffc01000 0x1000>, <0xffc02000 0x2000>, <0xffc04000 0x2000>, <0xffc06000 0x2000>; }; vm_console_vm0 { virtual_device; vc-dynamic-res; compatible = "minos,vm_console"; }; }; }; vm1 { device_type = "virtual_machine"; vmid = <1>; vm_name = "vim3_pro_vm1"; type = "linux"; native_wfi; vcpus = <1>; entry = <0x0 0x80080000>; setup_data = <0x0 0x83e00000>; vcpu_affinity = <4 0 0 0>; memory = <0x0 0x80000000 0x0 0x8000000>; cmdline = "console=hvc0 earlycon=earlycon=dbcon,io,0x0 loglevel=8 consolelog=9"; vm1_bdi { vm_console_vm1 { virtual_device; vc-dynamic-res; compatible = "minos,vm_console"; }; }; }; vm2 { device_type = "virtual_machine"; vmid = <2>; vm_name = "fvp_zephyr_vm2"; type = "zephyr"; vcpus = <1>; entry = <0x0 0x880003b8>; setup_data = <0x0 0x0>; vcpu_affinity = <5>; native_wfi; memory = <0x0 0x88000000 0x0 0x00200000>; vm2_bdi { #address-cells = <0x1>; #size-cells = <0x1>; vtimer_irq = <27>; virq_chip_vm2 { compatible = "arm,gicv2"; #address-cells = <0x1>; #size-cells = <0x1>; reg = <0x2f000000 0x10000>, <0x2c000000 0x2000>, <0x2c010000 0x2000>, <0x2c02f000 0x2000>; }; vmbox_controller@20000000 { compatible = "minos,vmbox-controller"; reg = <0x20000000 0x1000>; interrupts = <0 10 4>; }; vm_console_vm2 { virtual_device; compatible = "minos,vm_console"; reg = <0x20001000 0x2000>; interrupts = <0 0 4>; }; }; }; }; timer { compatible = "arm,armv8-timer"; interrupts = < 0x01 0x0d 0xff08 0x01 0x0e 0xff08 0x01 0x0b 0xff08 0x01 0x0a 0xff08 >; arm,no-tick-in-suspend; }; cpus { #address-cells = < 0x02 >; #size-cells = < 0x00 >; cpu-map { cluster0 { core0 { cpu = < 0x0a >; }; core1 { cpu = < 0x0b >; }; }; cluster1 { core0 { cpu = < 0x0c >; }; core1 { cpu = < 0x0d >; }; core2 { cpu = < 0x0e >; }; core3 { cpu = < 0x0f >; }; }; }; cpu@0 { device_type = "cpu"; compatible = "arm,cortex-a53"; reg = < 0x00 0x00 >; enable-method = "psci"; capacity-dmips-mhz = < 0x250 >; next-level-cache = < 0x46 >; #cooling-cells = < 0x02 >; cpu-supply = < 0x47 >; operating-points-v2 = < 0x48 >; clocks = < 0x02 0xbb >; clock-latency = < 0xc350 >; phandle = < 0x0a >; }; cpu@1 { device_type = "cpu"; compatible = "arm,cortex-a53"; reg = < 0x00 0x01 >; enable-method = "psci"; capacity-dmips-mhz = < 0x250 >; next-level-cache = < 0x46 >; #cooling-cells = < 0x02 >; cpu-supply = < 0x47 >; operating-points-v2 = < 0x48 >; clocks = < 0x02 0xbb >; clock-latency = < 0xc350 >; phandle = < 0x0b >; }; cpu@100 { device_type = "cpu"; compatible = "arm,cortex-a73"; reg = < 0x00 0x100 >; enable-method = "psci"; capacity-dmips-mhz = < 0x400 >; next-level-cache = < 0x46 >; #cooling-cells = < 0x02 >; cpu-supply = < 0x49 >; operating-points-v2 = < 0x4a >; clocks = < 0x02 0xe0 >; clock-latency = < 0xc350 >; phandle = < 0x0c >; }; cpu@101 { device_type = "cpu"; compatible = "arm,cortex-a73"; reg = < 0x00 0x101 >; enable-method = "psci"; capacity-dmips-mhz = < 0x400 >; next-level-cache = < 0x46 >; #cooling-cells = < 0x02 >; cpu-supply = < 0x49 >; operating-points-v2 = < 0x4a >; clocks = < 0x02 0xe0 >; clock-latency = < 0xc350 >; phandle = < 0x0d >; }; cpu@102 { device_type = "cpu"; compatible = "arm,cortex-a73"; reg = < 0x00 0x102 >; enable-method = "psci"; capacity-dmips-mhz = < 0x400 >; next-level-cache = < 0x46 >; #cooling-cells = < 0x02 >; cpu-supply = < 0x49 >; operating-points-v2 = < 0x4a >; clocks = < 0x02 0xe0 >; clock-latency = < 0xc350 >; phandle = < 0x0e >; }; cpu@103 { device_type = "cpu"; compatible = "arm,cortex-a73"; reg = < 0x00 0x103 >; enable-method = "psci"; capacity-dmips-mhz = < 0x400 >; next-level-cache = < 0x46 >; #cooling-cells = < 0x02 >; cpu-supply = < 0x49 >; operating-points-v2 = < 0x4a >; clocks = < 0x02 0xe0 >; clock-latency = < 0xc350 >; phandle = < 0x0f >; }; l2-cache0 { compatible = "cache"; phandle = < 0x46 >; }; }; memory@0 { device_type = "memory"; reg = < 0x00 0x00 0x00 0xed800000 >; }; }; ================================================ FILE: kernel/dtbs/qemu-arm64.dts ================================================ /dts-v1/; / { interrupt-parent = < 0x8001 >; #size-cells = < 0x02 >; #address-cells = < 0x02 >; compatible = "linux,qemu-arm64"; chosen { minos,stdout = "pl011"; bootargs = "bootwait=3 tty=vm1 rootfs=virtio-blk.drv"; minos,ramdisk-start = <0x0 0x44000000>; minos,ramdisk-end = <0x0 0x464ae000>; }; psci { migrate = < 0xc4000005 >; cpu_on = < 0xc4000003 >; cpu_off = < 0x84000002 >; cpu_suspend = < 0xc4000001 >; method = "smc"; compatible = "arm,psci-0.2\0arm,psci"; }; memory@40000000 { reg = < 0x00 0x40000000 0x01 0x00 >; device_type = "memory"; }; vms { vm1 { device_type = "virtual_machine"; vm_name = "fvp_linux_host"; host_vm; vmid = <1>; type = "linux"; kernel_image = "Image"; dtb_image = "qemu-virt.dtb"; vcpus = <1>; entry = <0x0 0x46680000>; setup_data = <0x0 0x50000000>; vcpu_affinity = <0 1 2 3>; cmdline = "console=hvc0 earlycon=dbcon,io,0x0 loglevel=8 consolelog=9 root=/dev/vda2 rw"; memory = <0x0 0x46600000 0x0 0x40000000>; vm1_bdi { vm_console_vm1 { virtual_device; vc-dynamic-res; compatible = "minos,vm_console"; }; }; }; }; platform@c000000 { interrupt-parent = < 0x8001 >; ranges = < 0x00 0x00 0xc000000 0x2000000 >; #address-cells = < 0x01 >; #size-cells = < 0x01 >; compatible = "qemu,platform\0simple-bus"; }; fw-cfg@9020000 { dma-coherent; reg = < 0x00 0x9020000 0x00 0x18 >; compatible = "qemu,fw-cfg-mmio"; }; virtio_mmio@a003e00 { dma-coherent; interrupts = < 0x00 0x2f 0x01 >; reg = < 0x00 0xa003e00 0x00 0x200 >; compatible = "virtio,mmio"; }; gpio-keys { #address-cells = < 0x01 >; #size-cells = < 0x00 >; compatible = "gpio-keys"; poweroff { gpios = < 0x8002 0x03 0x00 >; linux,code = < 0x74 >; label = "GPIO Key Poweroff"; }; }; pl061@9030000 { phandle = < 0x8002 >; clock-names = "apb_pclk"; clocks = < 0x8000 >; interrupts = < 0x00 0x07 0x04 >; gpio-controller; #gpio-cells = < 0x02 >; compatible = "arm,pl061\0arm,primecell"; reg = < 0x00 0x9030000 0x00 0x1000 >; }; pcie@10000000 { interrupt-map-mask = < 0x1800 0x00 0x00 0x07 >; interrupt-map = < 0x00 0x00 0x00 0x01 0x8001 0x00 0x00 0x00 0x03 0x04 0x00 0x00 0x00 0x02 0x8001 0x00 0x00 0x00 0x04 0x04 0x00 0x00 0x00 0x03 0x8001 0x00 0x00 0x00 0x05 0x04 0x00 0x00 0x00 0x04 0x8001 0x00 0x00 0x00 0x06 0x04 0x800 0x00 0x00 0x01 0x8001 0x00 0x00 0x00 0x04 0x04 0x800 0x00 0x00 0x02 0x8001 0x00 0x00 0x00 0x05 0x04 0x800 0x00 0x00 0x03 0x8001 0x00 0x00 0x00 0x06 0x04 0x800 0x00 0x00 0x04 0x8001 0x00 0x00 0x00 0x03 0x04 0x1000 0x00 0x00 0x01 0x8001 0x00 0x00 0x00 0x05 0x04 0x1000 0x00 0x00 0x02 0x8001 0x00 0x00 0x00 0x06 0x04 0x1000 0x00 0x00 0x03 0x8001 0x00 0x00 0x00 0x03 0x04 0x1000 0x00 0x00 0x04 0x8001 0x00 0x00 0x00 0x04 0x04 0x1800 0x00 0x00 0x01 0x8001 0x00 0x00 0x00 0x06 0x04 0x1800 0x00 0x00 0x02 0x8001 0x00 0x00 0x00 0x03 0x04 0x1800 0x00 0x00 0x03 0x8001 0x00 0x00 0x00 0x04 0x04 0x1800 0x00 0x00 0x04 0x8001 0x00 0x00 0x00 0x05 0x04 >; #interrupt-cells = < 0x01 >; ranges = < 0x1000000 0x00 0x00 0x00 0x3eff0000 0x00 0x10000 0x2000000 0x00 0x10000000 0x00 0x10000000 0x00 0x2eff0000 0x3000000 0x80 0x00 0x80 0x00 0x80 0x00 >; reg = < 0x40 0x10000000 0x00 0x10000000 >; dma-coherent; bus-range = < 0x00 0xff >; linux,pci-domain = < 0x00 >; #size-cells = < 0x02 >; #address-cells = < 0x03 >; device_type = "pci"; compatible = "pci-host-ecam-generic"; }; pl031@9010000 { clock-names = "apb_pclk"; clocks = < 0x8000 >; interrupts = < 0x00 0x02 0x04 >; reg = < 0x00 0x9010000 0x00 0x1000 >; compatible = "arm,pl031\0arm,primecell"; }; pl011@9000000 { clock-names = "uartclk\0apb_pclk"; clocks = < 0x8000 0x8000 >; interrupts = < 0x00 0x01 0x04 >; reg = < 0x00 0x9000000 0x00 0x1000 >; compatible = "arm,pl011\0arm,primecell"; }; pmu { interrupts = < 0x01 0x07 0x04 >; compatible = "arm,armv8-pmuv3"; }; intc@8000000 { phandle = < 0x8001 >; interrupts = < 0x01 0x09 0x04 >; reg = <0x0 0x08000000 0 0x10000>, // GICD <0x0 0x080a0000 0 0x200000>, // GICR <0x0 0x08010000 0 0x2000>, // GICC <0x0 0x08030000 0 0x2000>, // GICH <0x0 0x08040000 0 0x2000>; // GICV #redistributor-regions = < 0x01 >; compatible = "arm,gic-v3"; ranges; #size-cells = < 0x02 >; #address-cells = < 0x02 >; interrupt-controller; #interrupt-cells = < 0x03 >; }; flash@0 { bank-width = < 0x04 >; reg = < 0x00 0x00 0x00 0x4000000 0x00 0x4000000 0x00 0x4000000 >; compatible = "cfi-flash"; }; cpus { #size-cells = < 0x00 >; #address-cells = < 0x01 >; cpu@0 { reg = < 0x00 >; enable-method = "psci"; compatible = "arm,cortex-a53"; device_type = "cpu"; }; cpu@1 { reg = < 0x01 >; enable-method = "psci"; compatible = "arm,cortex-a53"; device_type = "cpu"; }; cpu@2 { reg = < 0x02 >; enable-method = "psci"; compatible = "arm,cortex-a53"; device_type = "cpu"; }; cpu@3 { reg = < 0x03 >; enable-method = "psci"; compatible = "arm,cortex-a53"; device_type = "cpu"; }; }; timer { interrupts = < 0x01 0x0d 0x04 0x01 0x0e 0x04 0x01 0x0b 0x04 0x01 0x0a 0x04 >; always-on; compatible = "arm,armv8-timer\0arm,armv7-timer"; }; apb-pclk { phandle = < 0x8000 >; clock-output-names = "clk24mhz"; clock-frequency = < 0x16e3600 >; #clock-cells = < 0x00 >; compatible = "fixed-clock"; }; }; ================================================ FILE: kernel/dtbs/r8a7795.dts ================================================ // SPDX-License-Identifier: GPL-2.0 /dts-v1/; / { model = "board based on r8a7795"; compatible = "renesas,r8a7795"; #address-cells = <2>; #size-cells = <2>; interrupt-parent = <&gic>; chosen { minos,stdout = "renesas,scif"; }; soc { #address-cells = <2>; #size-cells = <2>; gic: interrupt-controller@f1010000 { compatible = "arm,gic-400"; #interrupt-cells = <3>; interrupt-controller; reg = <0x0 0xf1010000 0 0x1000>, <0x0 0xf1020000 0 0x20000>, <0x0 0xf1040000 0 0x20000>, <0x0 0xf1060000 0 0x20000>; }; }; timer { compatible = "arm,armv8-timer"; interrupts = <1 13 0xff08>, <1 14 0xff08>, <1 11 0xff08>, <1 10 0xff08>; }; cpus { #address-cells = <1>; #size-cells = <0>; cpu@0 { reg = <0x0>; }; cpu@1 { reg = <0x1>; }; cpu@2 { reg = <0x2>; }; cpu@3 { reg = <0x3>; }; cpu@100 { reg = <0x100>; }; cpu@101 { reg = <0x101>; }; cpu@102 { reg = <0x102>; }; cpu@103 { reg = <0x103>; }; }; memory@48000000 { /* first 128MB is reserved for secure area. */ reg = <0x0 0x48000000 0x0 0x38000000>, <0x5 0x00000000 0x0 0x40000000>, <0x6 0x00000000 0x0 0x40000000>, <0x7 0x00000000 0x0 0x40000000>; }; /* * 0x48000000 - 0x4a000000 32 MB Minos * 0x4a000000 - 0x65000000 432 MB VM0 * 0x65000000 - 0x80000000 432 MB VM1 * 0x500000000 - 0x540000000 1 GB VM0 * 0x600000000 - 0x620000000 512 MB VM0 * 0x620000000 - 0x640000000 512 MB VM1 * 0x700000000 - 0x740000000 1 GB VM1 * * Minos' memory size should be equal to MINOS_RAM_SIZE. */ vms { vm0 { device_type = "virtual_machine"; vmid = <0>; vm_name = "r8a7795_vm0"; type = "linux"; vcpus = <4>; vcpu_affinity = <0 1 2 3>; memory = <0x0 0x4a000000 0x0 0x1b000000>, <0x5 0x00000000 0x0 0x40000000>, <0x6 0x00000000 0x0 0x20000000>; /* * The entry must be equal to the memory start address plus Linux's * TEXT_OFFSET. */ entry = <0x0 0x4a080000>; setup_data = <0x0 0x4a000000>; native_wfi; vm0_bdi { vm_console_vm0 { virtual_device; compatible = "minos,vm_console"; vc-dynamic-res; }; }; }; vm1 { device_type = "virtual_machine"; vmid = <1>; vm_name = "r8a7795_vm1"; type = "linux"; vcpus = <4>; vcpu_affinity = <4 5 6 7>; /* * The first memory range must be 32-bits for DMA addressing. */ memory = <0x0 0x65000000 0x0 0x1b000000>, <0x6 0x20000000 0x0 0x20000000>, <0x7 0x00000000 0x0 0x40000000>; entry = <0x0 0x65080000>; setup_data = <0x0 0x65000000>; native_wfi; vm1_bdi { vm_console_vm1 { virtual_device; compatible = "minos,vm_console"; vc-dynamic-res; }; }; }; }; }; ================================================ FILE: kernel/include/device/bcm_irq.h ================================================ #ifndef __BCM_IRQ_H__ #define __BCM_IRQ_H__ #define LOCAL_CONTROL 0x000 #define LOCAL_PRESCALER 0x008 /* * The low 2 bits identify the CPU that the GPU IRQ goes to, and the * next 2 bits identify the CPU that the GPU FIQ goes to. */ #define LOCAL_GPU_ROUTING 0x00c /* When setting bits 0-3, enables PMU interrupts on that CPU. */ #define LOCAL_PM_ROUTING_SET 0x010 /* When setting bits 0-3, disables PMU interrupts on that CPU. */ #define LOCAL_PM_ROUTING_CLR 0x014 /* * The low 4 bits of this are the CPU's timer IRQ enables, and the * next 4 bits are the CPU's timer FIQ enables (which override the IRQ * bits). */ #define LOCAL_TIMER_INT_CONTROL0 0x040 #define LOCAL_TIMER_INT_CONTROL3 0x04c /* * The low 4 bits of this are the CPU's per-mailbox IRQ enables, and * the next 4 bits are the CPU's per-mailbox FIQ enables (which * override the IRQ bits). */ #define LOCAL_MAILBOX_INT_CONTROL0 0x050 #define LOCAL_MAILBOX_INT_CONTROL3 0x05c /* * The CPU's interrupt status register. Bits are defined by the the * LOCAL_IRQ_* bits below. */ #define LOCAL_IRQ_PENDING0 0x060 /* Same status bits as above, but for FIQ. */ #define LOCAL_FIQ_PENDING0 0x070 /* * Mailbox write-to-set bits. There are 16 mailboxes, 4 per CPU, and * these bits are organized by mailbox number and then CPU number. We * use mailbox 0 for IPIs. The mailbox's interrupt is raised while * any bit is set. */ #define LOCAL_MAILBOX0_SET0 0x080 #define LOCAL_MAILBOX3_SET0 0x08c #define LOCAL_MAILBOX_SET_START 0x80 #define LOCAL_MAILBOX_SET_END 0xbc /* Mailbox write-to-clear bits. */ #define LOCAL_MAILBOX0_CLR0 0x0c0 #define LOCAL_MAILBOX3_CLR0 0x0cc #define LOCAL_MAILBOX_CLR_START 0x0c0 #define LOCAL_MAILBOX_CLR_END 0x0fc #define LOCAL_IRQ_CNTPSIRQ 0 #define LOCAL_IRQ_CNTPNSIRQ 1 #define LOCAL_IRQ_CNTHPIRQ 2 #define LOCAL_IRQ_CNTVIRQ 3 #define LOCAL_IRQ_MAILBOX0 4 #define LOCAL_IRQ_MAILBOX1 5 #define LOCAL_IRQ_MAILBOX2 6 #define LOCAL_IRQ_MAILBOX3 7 #define LOCAL_IRQ_GPU_FAST 8 #define LOCAL_IRQ_PMU_FAST 9 #define LAST_IRQ LOCAL_IRQ_PMU_FAST #define BCM2836_INC_BASE 0x40000000 #define BCM2835_INC_OFFSET 0x200 /* max support 8 cpus */ #define BCM2836_RELEASE_ADDR 0x40000180 #define BCM2836_RELEASE_OFFSET 0x180 #define BCM2836_RELEASE_OFFSET_END 0x1bf #define BCM2836_IRQ_ACK 0x1c0 #define BCM2835_IRQ_BASIC_PENDING 0x200 #define BCM2835_IRQ_PENDING1 0x204 #define BCM2835_IRQ_PENDING2 0x208 #define BCM2835_FIQ_CRTL 0x20c #define BCM2835_IRQ_ENABLE1 0x210 #define BCM2835_IRQ_ENABLE2 0x214 #define BCM2835_IRQ_BASIC_ENABLE 0x218 #define BCM2835_IRQ_DISABLE1 0x21c #define BCM2835_IRQ_DISABLE2 0x220 #define BCM2835_IRQ_DISABLE_BASIC 0x224 #define BCM2835_IRQ_ACK 0x228 /* Put the bank and irq (32 bits) into the hwirq */ #define MAKE_HWIRQ(b, n) (((b + 1) << 5) | (n)) #define HWIRQ_BANK(i) ((i >> 5) - 1) #define HWIRQ_BIT(i) BIT(i & 0x1f) #define NR_IRQS_BANK0 8 #define BANK0_HWIRQ_MASK 0xff /* Shortcuts can't be disabled so any unknown new ones need to be masked */ #define SHORTCUT1_MASK 0x00007c00 #define SHORTCUT2_MASK 0x001f8000 #define SHORTCUT_SHIFT 10 #define BANK1_HWIRQ BIT(8) #define BANK2_HWIRQ BIT(9) #define BANK0_VALID_MASK (BANK0_HWIRQ_MASK | BANK1_HWIRQ | BANK2_HWIRQ \ | SHORTCUT1_MASK | SHORTCUT2_MASK) #undef ARM_LOCAL_GPU_INT_ROUTING #define ARM_LOCAL_GPU_INT_ROUTING 0x0c #define REG_FIQ_CONTROL 0x0c #define REG_FIQ_ENABLE 0x80 #define REG_FIQ_DISABLE 0 #define NR_BANKS 3 #define IRQS_PER_BANK 32 #define NUMBER_IRQS MAKE_HWIRQ(NR_BANKS, 0) #undef FIQ_START #define FIQ_START (NR_IRQS_BANK0 + MAKE_HWIRQ(NR_BANKS - 1, 0)) /********* The interrupt sources are as follows: Bank 0: 0: ARM_TIMER 1: ARM_MAILBOX 2: ARM_DOORBELL_0 3: ARM_DOORBELL_1 4: VPU0_HALTED 5: VPU1_HALTED 6: ILLEGAL_TYPE0 7: ILLEGAL_TYPE1 8 - 20 : used for gpu 21 - 31 : used for vm0 VMCS Bank 1: 0: TIMER0 1: TIMER1 2: TIMER2 3: TIMER3 4: CODEC0 5: CODEC1 6: CODEC2 7: VC_JPEG 8: ISP 9: VC_USB 10: VC_3D 11: TRANSPOSER 12: MULTICORESYNC0 13: MULTICORESYNC1 14: MULTICORESYNC2 15: MULTICORESYNC3 16: DMA0 17: DMA1 18: VC_DMA2 19: VC_DMA3 20: DMA4 21: DMA5 22: DMA6 23: DMA7 24: DMA8 25: DMA9 26: DMA10 27: DMA11-14 - shared interrupt for DMA 11 to 14 28: DMAALL - triggers on all dma interrupts (including chanel 15) 29: AUX 30: ARM 31: VPUDMA Bank 2: 0: HOSTPORT 1: VIDEOSCALER 2: CCP2TX 3: SDC 4: DSI0 5: AVE 6: CAM0 7: CAM1 8: HDMI0 9: HDMI1 10: PIXELVALVE1 11: I2CSPISLV 12: DSI1 13: PWA0 14: PWA1 15: CPR 16: SMI 17: GPIO0 18: GPIO1 19: GPIO2 20: GPIO3 21: VC_I2C 22: VC_SPI 23: VC_I2SPCM 24: VC_SDIO 25: VC_UART 26: SLIMBUS 27: VEC 28: CPG 29: RNG 30: VC_ARASANSDIO 31: AVSPMON **********/ #endif ================================================ FILE: kernel/include/device/gicv2.h ================================================ /* * ARM Generic Interrupt Controller support * * Tim Deegan * Copyright (c) 2011 Citrix Systems. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef __ASM_ARM_GIC_H__ #define __ASM_ARM_GIC_H__ #define NR_GIC_LOCAL_IRQS NR_LOCAL_IRQS #define NR_GIC_SGI 16 #define GICD_CTLR (0x000) #define GICD_TYPER (0x004) #define GICD_IIDR (0x008) #define GICD_IGROUPR (0x080) #define GICD_IGROUPRN (0x0FC) #define GICD_ISENABLER (0x100) #define GICD_ISENABLERN (0x17C) #define GICD_ICENABLER (0x180) #define GICD_ICENABLERN (0x1fC) #define GICD_ISPENDR (0x200) #define GICD_ISPENDRN (0x27C) #define GICD_ICPENDR (0x280) #define GICD_ICPENDRN (0x2FC) #define GICD_ISACTIVER (0x300) #define GICD_ISACTIVERN (0x37C) #define GICD_ICACTIVER (0x380) #define GICD_ICACTIVERN (0x3FC) #define GICD_IPRIORITYR (0x400) #define GICD_IPRIORITYRN (0x7F8) #define GICD_ITARGETSR (0x800) #define GICD_ITARGETSR7 (0x81C) #define GICD_ITARGETSR8 (0x820) #define GICD_ITARGETSRN (0xBF8) #define GICD_ICFGR (0xC00) #define GICD_ICFGR1 (0xC04) #define GICD_ICFGR2 (0xC08) #define GICD_ICFGRN (0xCFC) #define GICD_NSACR (0xE00) #define GICD_NSACRN (0xEFC) #define GICD_SGIR (0xF00) #define GICD_CPENDSGIR (0xF10) #define GICD_CPENDSGIRN (0xF1C) #define GICD_SPENDSGIR (0xF20) #define GICD_SPENDSGIRN (0xF2C) #define GICD_ICPIDR2 (0xFE8) #define GICD_SGI_TARGET_LIST_SHIFT (24) #define GICD_SGI_TARGET_LIST_MASK (0x3UL << GICD_SGI_TARGET_LIST_SHIFT) #define GICD_SGI_TARGET_SHIFT (16) #define GICD_SGI_TARGET_MASK (0xFFUL << GICD_SGI_TARGET_SHIFT) #define GICD_SGI_GROUP1 (1UL << 15) #define GICD_SGI_INTID_MASK (0xFUL) #define GICD_SGI_TARGET_OTHERS (1UL << GICD_SGI_TARGET_LIST_SHIFT) #define GICD_SGI_TARGET_SELF (2UL << GICD_SGI_TARGET_LIST_SHIFT) #define GICD_SGI_TARGET_LIST (0UL << GICD_SGI_TARGET_LIST_SHIFT) #define GICC_CTLR (0x0000) #define GICC_PMR (0x0004) #define GICC_BPR (0x0008) #define GICC_IAR (0x000C) #define GICC_EOIR (0x0010) #define GICC_RPR (0x0014) #define GICC_HPPIR (0x0018) #define GICC_APR (0x00D0) #define GICC_NSAPR (0x00E0) #define GICC_IIDR (0x00FC) #define GICC_DIR (0x1000) #define GICH_HCR (0x00) #define GICH_VTR (0x04) #define GICH_VMCR (0x08) #define GICH_MISR (0x10) #define GICH_EISR0 (0x20) #define GICH_EISR1 (0x24) #define GICH_ELSR0 (0x30) #define GICH_ELSR1 (0x34) #define GICH_APR (0xF0) #define GICH_LR (0x100) /* Register bits */ #define GICD_CTL_ENABLE 0x1 #define GICD_TYPE_LINES 0x01f #define GICD_TYPE_CPUS_SHIFT 5 #define GICD_TYPE_CPUS 0x0e0 #define GICD_TYPE_SEC 0x400 #define GICD_TYPER_DVIS (1U << 18) #define GICC_CTL_ENABLE 0x1 #define GICC_CTL_EOI (0x1 << 9) #define GICC_IA_IRQ 0x03ff #define GICC_IA_CPU_MASK 0x1c00 #define GICC_IA_CPU_SHIFT 10 #define GICH_HCR_EN (1 << 0) #define GICH_HCR_UIE (1 << 1) #define GICH_HCR_LRENPIE (1 << 2) #define GICH_HCR_NPIE (1 << 3) #define GICH_HCR_VGRP0EIE (1 << 4) #define GICH_HCR_VGRP0DIE (1 << 5) #define GICH_HCR_VGRP1EIE (1 << 6) #define GICH_HCR_VGRP1DIE (1 << 7) #define GICH_MISR_EOI (1 << 0) #define GICH_MISR_U (1 << 1) #define GICH_MISR_LRENP (1 << 2) #define GICH_MISR_NP (1 << 3) #define GICH_MISR_VGRP0E (1 << 4) #define GICH_MISR_VGRP0D (1 << 5) #define GICH_MISR_VGRP1E (1 << 6) #define GICH_MISR_VGRP1D (1 << 7) /* * The minimum GICC_BPR is required to be in the range 0-3. We set * GICC_BPR to 0 but we must expect that it might be 3. This means we * can rely on premption between the following ranges: * 0xf0..0xff * 0xe0..0xdf * 0xc0..0xcf * 0xb0..0xbf * 0xa0..0xaf * 0x90..0x9f * 0x80..0x8f * * Priorities within a range will not preempt each other. * * A GIC must support a mimimum of 16 priority levels. */ #define GIC_PRI_LOWEST 0xf0 #define GIC_PRI_IRQ 0xa0 #define GIC_PRI_IPI 0x90 /* IPIs must preempt normal interrupts */ #define GIC_PRI_HIGHEST 0x80 /* Higher priorities belong to Secure-World */ #define GIC_PRI_TO_GUEST(pri) (pri >> 3) /* GICH_LR and GICH_VMCR only support 5 bits for guest irq priority */ struct gicv2_context { uint32_t hcr; uint32_t vmcr; uint32_t apr; uint32_t lr[64]; }; struct gich_lr { uint32_t vid : 10; uint32_t pid : 10; uint32_t resv : 3; uint32_t pr : 5; uint32_t state : 2; uint32_t grp1 : 1; uint32_t hw : 1; }; #endif ================================================ FILE: kernel/include/device/gicv3.h ================================================ #ifndef _MINOS_GICV3_H_ #define _MINOS_GICV3_H_ //#include #if 0 #define GICD_CTLR_ENABLE_GRP0 (1 << 0) #define GICD_CTLR_ENABLE_GRP1NS (1 << 1) #define GICD_CTLR_ENABLE_GRP1A (1 << 1) #define GICD_CTLR_ENABLE_GRP1S (1 << 2) #define GICD_CTLR_ENABLE_ALL ((1 << 0) | (1 << 1) | (1 << 2)) #define GICD_CTLR_ARE_S (1 << 4) #define GICD_CTLR_ARE_NS (1 << 5) #define GICD_CTLR_DS (1 << 6) #define GICD_CTLR_E1NWF (1 << 7) #else /* for no-secure access */ #define GICD_CTLR_ENABLE_GRP1 (0 << 1) #define GICD_CTLR_ENABLE_GRP1A (1 << 1) #define GICD_CTLR_ARE_NS (1 << 4) #endif #define GICD_IROUTER_MODE_SPECIFIC (0) #define GICD_IROUTER_MODE_ANY (1 << 31) #define GICD_ICFGR_LEVEL (0) #define GICD_ICFGR_EDGE (1 << 31) #define GICD_IGROUPR_G0S (0) #define GICD_IGROUPR_G1NS (1 << 0) #define GICD_IGROUPR_G1S (1 << 2) #define GICR_WAKER_PROCESSOR_SLEEP (1 << 1) #define GICR_WAKER_CHILDREN_ASLEEP (1 << 2) #define GICD_CTLR (0x0000) #define GICD_TYPER (0x0004) #define GICD_IIDR (0x0008) #define GICD_STATUSR (0x0010) #define GICD_SETSPI_NSR (0x0040) #define GICD_CLRSPI_NSR (0x0048) #define GICD_SETSPI_SR (0x0050) #define GICD_CLRSPI_SR (0x0058) #define GICD_SEIR (0x0068) #define GICD_IGROUPR (0x0080) #define GICD_ISENABLER (0x0100) #define GICD_ISENABLER_END (0x017c - 1) #define GICD_ICENABLER (0x0180) #define GICD_ICENABLER_END (0x01fc - 1) #define GICD_ISPENDR (0x0200) #define GICD_ICPENDR (0x0280) #define GICD_ISACTIVER (0x0300) #define GICD_ICACTIVER (0x0380) #define GICD_IPRIORITYR (0x0400) #define GICD_IPRIORITYR_END (0x07f8 - 1) #define GICD_ITARGETSR (0x0800) #define GICD_ICFGR (0x0c00) #define GICD_ICFGR_END (0x0cfc - 1) #define GICD_IGRPMODR (0x0d00) #define GICD_NSACR (0x0e00) #define GICD_SGIR (0x0f00) #define GICD_CPENDSGIR (0x0f10) #define GICD_SPENDSGIR (0x0f20) #define GICD_IROUTER (0x6000) #define GICD_PIDR2 (0xffe8) #define GICR_CTLR (0x0000) #define GICR_IIDR (0x0004) #define GICR_TYPER (0x0008) #define GICR_TYPER_HIGH (0x000c) #define GICR_STATUSR (0X0010) #define GICR_WAKER (0x0014) #define GICR_SETLPIR (0x0040) #define GICR_CLRLPIR (0x0048) #define GICR_SEIR (0x0068) #define GICR_PROPBASER (0x0070) #define GICR_PENDBASER (0x0078) #define GICR_INVLPIR (0x00a0) #define GICR_INVALLR (0x00b0) #define GICR_SYNCR (0x00c0) #define GICR_MOVLPIR (0x0100) #define GICR_MOVALLR (0x0110) #define GICR_IGROUPR0 (0x0080) #define GICR_ISENABLER (0x0100) #define GICR_ICENABLER (0x0180) #define GICR_ISPENDR0 (0x0200) #define GICR_ICPENDR0 (0x0280) #define GICR_ISACTIVER0 (0x0300) #define GICR_ICACTIVER0 (0x0380) #define GICR_IPRIORITYR0 (0x0400) #define GICR_ICFGR0 (0x0c00) #define GICR_ICFGR1 (0x0c04) #define GICR_IGRPMODR0 (0x0d00) #define GICR_NSACR (0x0e00) #define GICR_PIDR2 (0xffe8) #define GICH_VMCR_VENG0 (1 << 0) #define GICH_VMCR_VENG1 (1 << 1) #define GICH_VMCR_VACKCTL (1 << 2) #define GICH_VMCR_VFIQEN (1 << 3) #define GICH_VMCR_VCBPR (1 << 4) #define GICH_VMCR_VEOIM (1 << 9) #define GICH_HCR_EN (1 << 0) #define GICH_HCR_UIE (1 << 1) #define GICH_HCR_LRENPIE (1 << 2) #define GICH_HCR_NPIE (1 << 3) #define GICH_HCR_VGRP0EIE (1 << 4) #define GICH_HCR_VGRP0DIE (1 << 5) #define GICH_HCR_VGRP1EIE (1 << 6) #define GICH_HCR_VGRP1DIE (1 << 7) #define ICH_SGI_IRQMODE_SHIFT (40) #define ICH_SGI_IRQMODE_MASK (0x1) #define ICH_SGI_TARGET_OTHERS (1UL) #define ICH_SGI_TARGET_LIST (0) #define ICH_SGI_IRQ_SHIFT (24) #define ICH_SGI_IRQ_MASK (0xf) #define ICH_SGI_TARGETLIST_MASK (0xffff) #define ICH_SGI_AFFx_MASK (0xff) #define ICH_SGI_AFFINITY_LEVEL(x) (16 * (x)) #define GICV3_NR_LOCAL_IRQS (32) #define GICV3_NR_SGI (16) struct gicv3_context { uint64_t ich_lr0_el2; uint64_t ich_lr1_el2; uint64_t ich_lr2_el2; uint64_t ich_lr3_el2; uint64_t ich_lr4_el2; uint64_t ich_lr5_el2; uint64_t ich_lr6_el2; uint64_t ich_lr7_el2; uint64_t ich_lr8_el2; uint64_t ich_lr9_el2; uint64_t ich_lr10_el2; uint64_t ich_lr11_el2; uint64_t ich_lr12_el2; uint64_t ich_lr13_el2; uint64_t ich_lr14_el2; uint64_t ich_lr15_el2; uint32_t ich_ap0r2_el2; uint32_t ich_ap1r2_el2; uint32_t ich_ap0r1_el2; uint32_t ich_ap1r1_el2; uint32_t ich_ap0r0_el2; uint32_t ich_ap1r0_el2; uint32_t icc_sre_el1; uint32_t ich_vmcr_el2; uint32_t ich_hcr_el2; } __align(sizeof(unsigned long)); struct gic_lr { uint64_t v_intid : 32; uint64_t p_intid : 10; uint64_t res0 : 6; uint64_t priority : 8; uint64_t res1 : 4; uint64_t group : 1; uint64_t hw : 1; uint64_t state : 2; }; #endif ================================================ FILE: kernel/include/libfdt/fdt.h ================================================ #ifndef FDT_H #define FDT_H /* * libfdt - Flat Device Tree manipulation * Copyright (C) 2006 David Gibson, IBM Corporation. * Copyright 2012 Kim Phillips, Freescale Semiconductor. * * libfdt is dual licensed: you can use it either under the terms of * the GPL, or the BSD license, at your option. * * a) This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301 USA * * Alternatively, * * b) Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __ASSEMBLY__ struct fdt_header { fdt32_t magic; /* magic word FDT_MAGIC */ fdt32_t totalsize; /* total size of DT block */ fdt32_t off_dt_struct; /* offset to structure */ fdt32_t off_dt_strings; /* offset to strings */ fdt32_t off_mem_rsvmap; /* offset to memory reserve map */ fdt32_t version; /* format version */ fdt32_t last_comp_version; /* last compatible version */ /* version 2 fields below */ fdt32_t boot_cpuid_phys; /* Which physical CPU id we're booting on */ /* version 3 fields below */ fdt32_t size_dt_strings; /* size of the strings block */ /* version 17 fields below */ fdt32_t size_dt_struct; /* size of the structure block */ }; struct fdt_reserve_entry { fdt64_t address; fdt64_t size; }; struct fdt_node_header { fdt32_t tag; char name[0]; }; struct fdt_property { fdt32_t tag; fdt32_t len; fdt32_t nameoff; char data[0]; }; #endif /* !__ASSEMBLY */ #define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */ #define FDT_TAGSIZE sizeof(fdt32_t) #define FDT_BEGIN_NODE 0x1 /* Start node: full name */ #define FDT_END_NODE 0x2 /* End node */ #define FDT_PROP 0x3 /* Property: name off, size, content */ #define FDT_NOP 0x4 /* nop */ #define FDT_END 0x9 #define FDT_V1_SIZE (7*sizeof(fdt32_t)) #define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(fdt32_t)) #define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(fdt32_t)) #define FDT_V16_SIZE FDT_V3_SIZE #define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(fdt32_t)) #endif /* FDT_H */ ================================================ FILE: kernel/include/libfdt/libfdt.h ================================================ #ifndef LIBFDT_H #define LIBFDT_H /* * libfdt - Flat Device Tree manipulation * Copyright (C) 2006 David Gibson, IBM Corporation. * * libfdt is dual licensed: you can use it either under the terms of * the GPL, or the BSD license, at your option. * * a) This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301 USA * * Alternatively, * * b) Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #define FDT_FIRST_SUPPORTED_VERSION 0x02 #define FDT_LAST_SUPPORTED_VERSION 0x11 /* Error codes: informative error codes */ #define FDT_ERR_NOTFOUND 1 /* FDT_ERR_NOTFOUND: The requested node or property does not exist */ #define FDT_ERR_EXISTS 2 /* FDT_ERR_EXISTS: Attempted to create a node or property which * already exists */ #define FDT_ERR_NOSPACE 3 /* FDT_ERR_NOSPACE: Operation needed to expand the device * tree, but its buffer did not have sufficient space to * contain the expanded tree. Use fdt_open_into() to move the * device tree to a buffer with more space. */ /* Error codes: codes for bad parameters */ #define FDT_ERR_BADOFFSET 4 /* FDT_ERR_BADOFFSET: Function was passed a structure block * offset which is out-of-bounds, or which points to an * unsuitable part of the structure for the operation. */ #define FDT_ERR_BADPATH 5 /* FDT_ERR_BADPATH: Function was passed a badly formatted path * (e.g. missing a leading / for a function which requires an * absolute path) */ #define FDT_ERR_BADPHANDLE 6 /* FDT_ERR_BADPHANDLE: Function was passed an invalid phandle. * This can be caused either by an invalid phandle property * length, or the phandle value was either 0 or -1, which are * not permitted. */ #define FDT_ERR_BADSTATE 7 /* FDT_ERR_BADSTATE: Function was passed an incomplete device * tree created by the sequential-write functions, which is * not sufficiently complete for the requested operation. */ /* Error codes: codes for bad device tree blobs */ #define FDT_ERR_TRUNCATED 8 /* FDT_ERR_TRUNCATED: FDT or a sub-block is improperly * terminated (overflows, goes outside allowed bounds, or * isn't properly terminated). */ #define FDT_ERR_BADMAGIC 9 /* FDT_ERR_BADMAGIC: Given "device tree" appears not to be a * device tree at all - it is missing the flattened device * tree magic number. */ #define FDT_ERR_BADVERSION 10 /* FDT_ERR_BADVERSION: Given device tree has a version which * can't be handled by the requested operation. For * read-write functions, this may mean that fdt_open_into() is * required to convert the tree to the expected version. */ #define FDT_ERR_BADSTRUCTURE 11 /* FDT_ERR_BADSTRUCTURE: Given device tree has a corrupt * structure block or other serious error (e.g. misnested * nodes, or subnodes preceding properties). */ #define FDT_ERR_BADLAYOUT 12 /* FDT_ERR_BADLAYOUT: For read-write functions, the given * device tree has it's sub-blocks in an order that the * function can't handle (memory reserve map, then structure, * then strings). Use fdt_open_into() to reorganize the tree * into a form suitable for the read-write operations. */ /* "Can't happen" error indicating a bug in libfdt */ #define FDT_ERR_INTERNAL 13 /* FDT_ERR_INTERNAL: libfdt has failed an internal assertion. * Should never be returned, if it is, it indicates a bug in * libfdt itself. */ /* Errors in device tree content */ #define FDT_ERR_BADNCELLS 14 /* FDT_ERR_BADNCELLS: Device tree has a #address-cells, #size-cells * or similar property with a bad format or value */ #define FDT_ERR_BADVALUE 15 /* FDT_ERR_BADVALUE: Device tree has a property with an unexpected * value. For example: a property expected to contain a string list * is not NUL-terminated within the length of its value. */ #define FDT_ERR_BADOVERLAY 16 /* FDT_ERR_BADOVERLAY: The device tree overlay, while * correctly structured, cannot be applied due to some * unexpected or missing value, property or node. */ #define FDT_ERR_NOPHANDLES 17 /* FDT_ERR_NOPHANDLES: The device tree doesn't have any * phandle available anymore without causing an overflow */ #define FDT_ERR_MAX 17 /**********************************************************************/ /* Low-level functions (you probably don't need these) */ /**********************************************************************/ #ifndef SWIG /* This function is not useful in Python */ const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int checklen); #endif static inline void *fdt_offset_ptr_w(void *fdt, int offset, int checklen) { return (void *)(uintptr_t)fdt_offset_ptr(fdt, offset, checklen); } uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset); /* * Alignment helpers: * These helpers access words from a device tree blob. They're * built to work even with unaligned pointers on platforms (ike * ARM) that don't like unaligned loads and stores */ static inline uint32_t fdt32_ld(const fdt32_t *p) { fdt32_t v; memcpy(&v, p, sizeof(v)); return fdt32_to_cpu(v); } static inline uint64_t fdt64_ld(const fdt64_t *p) { fdt64_t v; memcpy(&v, p, sizeof(v)); return fdt64_to_cpu(v); } /**********************************************************************/ /* Traversal functions */ /**********************************************************************/ int fdt_next_node(const void *fdt, int offset, int *depth); /** * fdt_first_subnode() - get offset of first direct subnode * * @fdt: FDT blob * @offset: Offset of node to check * @return offset of first subnode, or -FDT_ERR_NOTFOUND if there is none */ int fdt_first_subnode(const void *fdt, int offset); /** * fdt_next_subnode() - get offset of next direct subnode * * After first calling fdt_first_subnode(), call this function repeatedly to * get direct subnodes of a parent node. * * @fdt: FDT blob * @offset: Offset of previous subnode * @return offset of next subnode, or -FDT_ERR_NOTFOUND if there are no more * subnodes */ int fdt_next_subnode(const void *fdt, int offset); /** * fdt_for_each_subnode - iterate over all subnodes of a parent * * @node: child node (int, lvalue) * @fdt: FDT blob (const void *) * @parent: parent node (int) * * This is actually a wrapper around a for loop and would be used like so: * * fdt_for_each_subnode(node, fdt, parent) { * Use node * ... * } * * if ((node < 0) && (node != -FDT_ERR_NOT_FOUND)) { * Error handling * } * * Note that this is implemented as a macro and @node is used as * iterator in the loop. The parent variable be constant or even a * literal. * */ #define fdt_for_each_subnode(node, fdt, parent) \ for (node = fdt_first_subnode(fdt, parent); \ node >= 0; \ node = fdt_next_subnode(fdt, node)) /**********************************************************************/ /* General functions */ /**********************************************************************/ #define fdt_get_header(fdt, field) \ (fdt32_ld(&((const struct fdt_header *)(fdt))->field)) #define fdt_magic(fdt) (fdt_get_header(fdt, magic)) #define fdt_totalsize(fdt) (fdt_get_header(fdt, totalsize)) #define fdt_off_dt_struct(fdt) (fdt_get_header(fdt, off_dt_struct)) #define fdt_off_dt_strings(fdt) (fdt_get_header(fdt, off_dt_strings)) #define fdt_off_mem_rsvmap(fdt) (fdt_get_header(fdt, off_mem_rsvmap)) #define fdt_version(fdt) (fdt_get_header(fdt, version)) #define fdt_last_comp_version(fdt) (fdt_get_header(fdt, last_comp_version)) #define fdt_boot_cpuid_phys(fdt) (fdt_get_header(fdt, boot_cpuid_phys)) #define fdt_size_dt_strings(fdt) (fdt_get_header(fdt, size_dt_strings)) #define fdt_size_dt_struct(fdt) (fdt_get_header(fdt, size_dt_struct)) #define fdt_set_hdr_(name) \ static inline void fdt_set_##name(void *fdt, uint32_t val) \ { \ struct fdt_header *fdth = (struct fdt_header *)fdt; \ fdth->name = cpu_to_fdt32(val); \ } fdt_set_hdr_(magic); fdt_set_hdr_(totalsize); fdt_set_hdr_(off_dt_struct); fdt_set_hdr_(off_dt_strings); fdt_set_hdr_(off_mem_rsvmap); fdt_set_hdr_(version); fdt_set_hdr_(last_comp_version); fdt_set_hdr_(boot_cpuid_phys); fdt_set_hdr_(size_dt_strings); fdt_set_hdr_(size_dt_struct); #undef fdt_set_hdr_ /** * fdt_header_size - return the size of the tree's header * @fdt: pointer to a flattened device tree */ size_t fdt_header_size_(uint32_t version); static inline size_t fdt_header_size(const void *fdt) { return fdt_header_size_(fdt_version(fdt)); } /** * fdt_check_header - sanity check a device tree header * @fdt: pointer to data which might be a flattened device tree * * fdt_check_header() checks that the given buffer contains what * appears to be a flattened device tree, and that the header contains * valid information (to the extent that can be determined from the * header alone). * * returns: * 0, if the buffer appears to contain a valid device tree * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_TRUNCATED, standard meanings, as above */ int fdt_check_header(const void *fdt); /** * fdt_move - move a device tree around in memory * @fdt: pointer to the device tree to move * @buf: pointer to memory where the device is to be moved * @bufsize: size of the memory space at buf * * fdt_move() relocates, if possible, the device tree blob located at * fdt to the buffer at buf of size bufsize. The buffer may overlap * with the existing device tree blob at fdt. Therefore, * fdt_move(fdt, fdt, fdt_totalsize(fdt)) * should always succeed. * * returns: * 0, on success * -FDT_ERR_NOSPACE, bufsize is insufficient to contain the device tree * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, standard meanings */ int fdt_move(const void *fdt, void *buf, int bufsize); /**********************************************************************/ /* Read-only functions */ /**********************************************************************/ int fdt_check_full(const void *fdt, size_t bufsize); /** * fdt_get_string - retrieve a string from the strings block of a device tree * @fdt: pointer to the device tree blob * @stroffset: offset of the string within the strings block (native endian) * @lenp: optional pointer to return the string's length * * fdt_get_string() retrieves a pointer to a single string from the * strings block of the device tree blob at fdt, and optionally also * returns the string's length in *lenp. * * returns: * a pointer to the string, on success * NULL, if stroffset is out of bounds, or doesn't point to a valid string */ const char *fdt_get_string(const void *fdt, int stroffset, int *lenp); /** * fdt_string - retrieve a string from the strings block of a device tree * @fdt: pointer to the device tree blob * @stroffset: offset of the string within the strings block (native endian) * * fdt_string() retrieves a pointer to a single string from the * strings block of the device tree blob at fdt. * * returns: * a pointer to the string, on success * NULL, if stroffset is out of bounds, or doesn't point to a valid string */ const char *fdt_string(const void *fdt, int stroffset); /** * fdt_get_max_phandle - retrieves the highest phandle in a tree * @fdt: pointer to the device tree blob * * fdt_get_max_phandle retrieves the highest phandle in the given * device tree. This will ignore badly formatted phandles, or phandles * with a value of 0 or -1. * * returns: * the highest phandle on success * 0, if no phandle was found in the device tree * -1, if an error occurred */ uint32_t fdt_get_max_phandle(const void *fdt); /** * fdt_num_mem_rsv - retrieve the number of memory reserve map entries * @fdt: pointer to the device tree blob * * Returns the number of entries in the device tree blob's memory * reservation map. This does not include the terminating 0,0 entry * or any other (0,0) entries reserved for expansion. * * returns: * the number of entries */ int fdt_num_mem_rsv(const void *fdt); /** * fdt_get_mem_rsv - retrieve one memory reserve map entry * @fdt: pointer to the device tree blob * @address, @size: pointers to 64-bit variables * * On success, *address and *size will contain the address and size of * the n-th reserve map entry from the device tree blob, in * native-endian format. * * returns: * 0, on success * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, standard meanings */ int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size); /** * fdt_subnode_offset_namelen - find a subnode based on substring * @fdt: pointer to the device tree blob * @parentoffset: structure block offset of a node * @name: name of the subnode to locate * @namelen: number of characters of name to consider * * Identical to fdt_subnode_offset(), but only examine the first * namelen characters of name for matching the subnode name. This is * useful for finding subnodes based on a portion of a larger string, * such as a full path. */ #ifndef SWIG /* Not available in Python */ int fdt_subnode_offset_namelen(const void *fdt, int parentoffset, const char *name, int namelen); #endif /** * fdt_subnode_offset - find a subnode of a given node * @fdt: pointer to the device tree blob * @parentoffset: structure block offset of a node * @name: name of the subnode to locate * * fdt_subnode_offset() finds a subnode of the node at structure block * offset parentoffset with the given name. name may include a unit * address, in which case fdt_subnode_offset() will find the subnode * with that unit address, or the unit address may be omitted, in * which case fdt_subnode_offset() will find an arbitrary subnode * whose name excluding unit address matches the given name. * * returns: * structure block offset of the requested subnode (>=0), on success * -FDT_ERR_NOTFOUND, if the requested subnode does not exist * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE * tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings. */ int fdt_subnode_offset(const void *fdt, int parentoffset, const char *name); /** * fdt_path_offset_namelen - find a tree node by its full path * @fdt: pointer to the device tree blob * @path: full path of the node to locate * @namelen: number of characters of path to consider * * Identical to fdt_path_offset(), but only consider the first namelen * characters of path as the path name. */ #ifndef SWIG /* Not available in Python */ int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen); #endif /** * fdt_path_offset - find a tree node by its full path * @fdt: pointer to the device tree blob * @path: full path of the node to locate * * fdt_path_offset() finds a node of a given path in the device tree. * Each path component may omit the unit address portion, but the * results of this are undefined if any such path component is * ambiguous (that is if there are multiple nodes at the relevant * level matching the given component, differentiated only by unit * address). * * returns: * structure block offset of the node with the requested path (>=0), on * success * -FDT_ERR_BADPATH, given path does not begin with '/' or is invalid * -FDT_ERR_NOTFOUND, if the requested node does not exist * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings. */ int fdt_path_offset(const void *fdt, const char *path); /** * fdt_get_name - retrieve the name of a given node * @fdt: pointer to the device tree blob * @nodeoffset: structure block offset of the starting node * @lenp: pointer to an integer variable (will be overwritten) or NULL * * fdt_get_name() retrieves the name (including unit address) of the * device tree node at structure block offset nodeoffset. If lenp is * non-NULL, the length of this name is also returned, in the integer * pointed to by lenp. * * returns: * pointer to the node's name, on success * If lenp is non-NULL, *lenp contains the length of that name * (>=0) * NULL, on error * if lenp is non-NULL *lenp contains an error code (<0): * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE * tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, standard meanings */ const char *fdt_get_name(const void *fdt, int nodeoffset, int *lenp); /** * fdt_first_property_offset - find the offset of a node's first property * @fdt: pointer to the device tree blob * @nodeoffset: structure block offset of a node * * fdt_first_property_offset() finds the first property of the node at * the given structure block offset. * * returns: * structure block offset of the property (>=0), on success * -FDT_ERR_NOTFOUND, if the requested node has no properties * -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings. */ int fdt_first_property_offset(const void *fdt, int nodeoffset); /** * fdt_next_property_offset - step through a node's properties * @fdt: pointer to the device tree blob * @offset: structure block offset of a property * * fdt_next_property_offset() finds the property immediately after the * one at the given structure block offset. This will be a property * of the same node as the given property. * * returns: * structure block offset of the next property (>=0), on success * -FDT_ERR_NOTFOUND, if the given property is the last in its node * -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_PROP tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings. */ int fdt_next_property_offset(const void *fdt, int offset); /** * fdt_for_each_property_offset - iterate over all properties of a node * * @property_offset: property offset (int, lvalue) * @fdt: FDT blob (const void *) * @node: node offset (int) * * This is actually a wrapper around a for loop and would be used like so: * * fdt_for_each_property_offset(property, fdt, node) { * Use property * ... * } * * if ((property < 0) && (property != -FDT_ERR_NOT_FOUND)) { * Error handling * } * * Note that this is implemented as a macro and property is used as * iterator in the loop. The node variable can be constant or even a * literal. */ #define fdt_for_each_property_offset(property, fdt, node) \ for (property = fdt_first_property_offset(fdt, node); \ property >= 0; \ property = fdt_next_property_offset(fdt, property)) /** * fdt_get_property_by_offset - retrieve the property at a given offset * @fdt: pointer to the device tree blob * @offset: offset of the property to retrieve * @lenp: pointer to an integer variable (will be overwritten) or NULL * * fdt_get_property_by_offset() retrieves a pointer to the * fdt_property structure within the device tree blob at the given * offset. If lenp is non-NULL, the length of the property value is * also returned, in the integer pointed to by lenp. * * Note that this code only works on device tree versions >= 16. fdt_getprop() * works on all versions. * * returns: * pointer to the structure representing the property * if lenp is non-NULL, *lenp contains the length of the property * value (>=0) * NULL, on error * if lenp is non-NULL, *lenp contains an error code (<0): * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_PROP tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ const struct fdt_property *fdt_get_property_by_offset(const void *fdt, int offset, int *lenp); /** * fdt_get_property_namelen - find a property based on substring * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to find * @name: name of the property to find * @namelen: number of characters of name to consider * @lenp: pointer to an integer variable (will be overwritten) or NULL * * Identical to fdt_get_property(), but only examine the first namelen * characters of name for matching the property name. */ #ifndef SWIG /* Not available in Python */ const struct fdt_property *fdt_get_property_namelen(const void *fdt, int nodeoffset, const char *name, int namelen, int *lenp); #endif /** * fdt_get_property - find a given property in a given node * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to find * @name: name of the property to find * @lenp: pointer to an integer variable (will be overwritten) or NULL * * fdt_get_property() retrieves a pointer to the fdt_property * structure within the device tree blob corresponding to the property * named 'name' of the node at offset nodeoffset. If lenp is * non-NULL, the length of the property value is also returned, in the * integer pointed to by lenp. * * returns: * pointer to the structure representing the property * if lenp is non-NULL, *lenp contains the length of the property * value (>=0) * NULL, on error * if lenp is non-NULL, *lenp contains an error code (<0): * -FDT_ERR_NOTFOUND, node does not have named property * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE * tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ const struct fdt_property *fdt_get_property(const void *fdt, int nodeoffset, const char *name, int *lenp); static inline struct fdt_property *fdt_get_property_w(void *fdt, int nodeoffset, const char *name, int *lenp) { return (struct fdt_property *)(uintptr_t) fdt_get_property(fdt, nodeoffset, name, lenp); } /** * fdt_getprop_by_offset - retrieve the value of a property at a given offset * @fdt: pointer to the device tree blob * @ffset: offset of the property to read * @namep: pointer to a string variable (will be overwritten) or NULL * @lenp: pointer to an integer variable (will be overwritten) or NULL * * fdt_getprop_by_offset() retrieves a pointer to the value of the * property at structure block offset 'offset' (this will be a pointer * to within the device blob itself, not a copy of the value). If * lenp is non-NULL, the length of the property value is also * returned, in the integer pointed to by lenp. If namep is non-NULL, * the property's namne will also be returned in the char * pointed to * by namep (this will be a pointer to within the device tree's string * block, not a new copy of the name). * * returns: * pointer to the property's value * if lenp is non-NULL, *lenp contains the length of the property * value (>=0) * if namep is non-NULL *namep contiains a pointer to the property * name. * NULL, on error * if lenp is non-NULL, *lenp contains an error code (<0): * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_PROP tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ #ifndef SWIG /* This function is not useful in Python */ const void *fdt_getprop_by_offset(const void *fdt, int offset, const char **namep, int *lenp); #endif /** * fdt_getprop_namelen - get property value based on substring * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to find * @name: name of the property to find * @namelen: number of characters of name to consider * @lenp: pointer to an integer variable (will be overwritten) or NULL * * Identical to fdt_getprop(), but only examine the first namelen * characters of name for matching the property name. */ #ifndef SWIG /* Not available in Python */ const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, const char *name, int namelen, int *lenp); static inline void *fdt_getprop_namelen_w(void *fdt, int nodeoffset, const char *name, int namelen, int *lenp) { return (void *)(uintptr_t)fdt_getprop_namelen(fdt, nodeoffset, name, namelen, lenp); } #endif /** * fdt_getprop - retrieve the value of a given property * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to find * @name: name of the property to find * @lenp: pointer to an integer variable (will be overwritten) or NULL * * fdt_getprop() retrieves a pointer to the value of the property * named 'name' of the node at offset nodeoffset (this will be a * pointer to within the device blob itself, not a copy of the value). * If lenp is non-NULL, the length of the property value is also * returned, in the integer pointed to by lenp. * * returns: * pointer to the property's value * if lenp is non-NULL, *lenp contains the length of the property * value (>=0) * NULL, on error * if lenp is non-NULL, *lenp contains an error code (<0): * -FDT_ERR_NOTFOUND, node does not have named property * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE * tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ const void *fdt_getprop(const void *fdt, int nodeoffset, const char *name, int *lenp); static inline void *fdt_getprop_w(void *fdt, int nodeoffset, const char *name, int *lenp) { return (void *)(uintptr_t)fdt_getprop(fdt, nodeoffset, name, lenp); } /** * fdt_get_phandle - retrieve the phandle of a given node * @fdt: pointer to the device tree blob * @nodeoffset: structure block offset of the node * * fdt_get_phandle() retrieves the phandle of the device tree node at * structure block offset nodeoffset. * * returns: * the phandle of the node at nodeoffset, on success (!= 0, != -1) * 0, if the node has no phandle, or another error occurs */ uint32_t fdt_get_phandle(const void *fdt, int nodeoffset); /** * fdt_get_alias_namelen - get alias based on substring * @fdt: pointer to the device tree blob * @name: name of the alias th look up * @namelen: number of characters of name to consider * * Identical to fdt_get_alias(), but only examine the first namelen * characters of name for matching the alias name. */ #ifndef SWIG /* Not available in Python */ const char *fdt_get_alias_namelen(const void *fdt, const char *name, int namelen); #endif /** * fdt_get_alias - retrieve the path referenced by a given alias * @fdt: pointer to the device tree blob * @name: name of the alias th look up * * fdt_get_alias() retrieves the value of a given alias. That is, the * value of the property named 'name' in the node /aliases. * * returns: * a pointer to the expansion of the alias named 'name', if it exists * NULL, if the given alias or the /aliases node does not exist */ const char *fdt_get_alias(const void *fdt, const char *name); /** * fdt_get_path - determine the full path of a node * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose path to find * @buf: character buffer to contain the returned path (will be overwritten) * @buflen: size of the character buffer at buf * * fdt_get_path() computes the full path of the node at offset * nodeoffset, and records that path in the buffer at buf. * * NOTE: This function is expensive, as it must scan the device tree * structure from the start to nodeoffset. * * returns: * 0, on success * buf contains the absolute path of the node at * nodeoffset, as a NUL-terminated string. * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag * -FDT_ERR_NOSPACE, the path of the given node is longer than (bufsize-1) * characters and will not fit in the given buffer. * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, standard meanings */ int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen); /** * fdt_supernode_atdepth_offset - find a specific ancestor of a node * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose parent to find * @supernodedepth: depth of the ancestor to find * @nodedepth: pointer to an integer variable (will be overwritten) or NULL * * fdt_supernode_atdepth_offset() finds an ancestor of the given node * at a specific depth from the root (where the root itself has depth * 0, its immediate subnodes depth 1 and so forth). So * fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, NULL); * will always return 0, the offset of the root node. If the node at * nodeoffset has depth D, then: * fdt_supernode_atdepth_offset(fdt, nodeoffset, D, NULL); * will return nodeoffset itself. * * NOTE: This function is expensive, as it must scan the device tree * structure from the start to nodeoffset. * * returns: * structure block offset of the node at node offset's ancestor * of depth supernodedepth (>=0), on success * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag * -FDT_ERR_NOTFOUND, supernodedepth was greater than the depth of * nodeoffset * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, standard meanings */ int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, int supernodedepth, int *nodedepth); /** * fdt_node_depth - find the depth of a given node * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose parent to find * * fdt_node_depth() finds the depth of a given node. The root node * has depth 0, its immediate subnodes depth 1 and so forth. * * NOTE: This function is expensive, as it must scan the device tree * structure from the start to nodeoffset. * * returns: * depth of the node at nodeoffset (>=0), on success * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, standard meanings */ int fdt_node_depth(const void *fdt, int nodeoffset); /** * fdt_parent_offset - find the parent of a given node * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose parent to find * * fdt_parent_offset() locates the parent node of a given node (that * is, it finds the offset of the node which contains the node at * nodeoffset as a subnode). * * NOTE: This function is expensive, as it must scan the device tree * structure from the start to nodeoffset, *twice*. * * returns: * structure block offset of the parent of the node at nodeoffset * (>=0), on success * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, standard meanings */ int fdt_parent_offset(const void *fdt, int nodeoffset); /** * fdt_node_offset_by_prop_value - find nodes with a given property value * @fdt: pointer to the device tree blob * @startoffset: only find nodes after this offset * @propname: property name to check * @propval: property value to search for * @proplen: length of the value in propval * * fdt_node_offset_by_prop_value() returns the offset of the first * node after startoffset, which has a property named propname whose * value is of length proplen and has value equal to propval; or if * startoffset is -1, the very first such node in the tree. * * To iterate through all nodes matching the criterion, the following * idiom can be used: * offset = fdt_node_offset_by_prop_value(fdt, -1, propname, * propval, proplen); * while (offset != -FDT_ERR_NOTFOUND) { * // other code here * offset = fdt_node_offset_by_prop_value(fdt, offset, propname, * propval, proplen); * } * * Note the -1 in the first call to the function, if 0 is used here * instead, the function will never locate the root node, even if it * matches the criterion. * * returns: * structure block offset of the located node (>= 0, >startoffset), * on success * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the * tree after startoffset * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, standard meanings */ int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, const char *propname, const void *propval, int proplen); /** * fdt_node_offset_by_phandle - find the node with a given phandle * @fdt: pointer to the device tree blob * @phandle: phandle value * * fdt_node_offset_by_phandle() returns the offset of the node * which has the given phandle value. If there is more than one node * in the tree with the given phandle (an invalid tree), results are * undefined. * * returns: * structure block offset of the located node (>= 0), on success * -FDT_ERR_NOTFOUND, no node with that phandle exists * -FDT_ERR_BADPHANDLE, given phandle value was invalid (0 or -1) * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, standard meanings */ int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle); /** * fdt_node_check_compatible: check a node's compatible property * @fdt: pointer to the device tree blob * @nodeoffset: offset of a tree node * @compatible: string to match against * * * fdt_node_check_compatible() returns 0 if the given node contains a * 'compatible' property with the given string as one of its elements, * it returns non-zero otherwise, or on error. * * returns: * 0, if the node has a 'compatible' property listing the given string * 1, if the node has a 'compatible' property, but it does not list * the given string * -FDT_ERR_NOTFOUND, if the given node has no 'compatible' property * -FDT_ERR_BADOFFSET, if nodeoffset does not refer to a BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, standard meanings */ int fdt_node_check_compatible(const void *fdt, int nodeoffset, const char *compatible); /** * fdt_node_offset_by_compatible - find nodes with a given 'compatible' value * @fdt: pointer to the device tree blob * @startoffset: only find nodes after this offset * @compatible: 'compatible' string to match against * * fdt_node_offset_by_compatible() returns the offset of the first * node after startoffset, which has a 'compatible' property which * lists the given compatible string; or if startoffset is -1, the * very first such node in the tree. * * To iterate through all nodes matching the criterion, the following * idiom can be used: * offset = fdt_node_offset_by_compatible(fdt, -1, compatible); * while (offset != -FDT_ERR_NOTFOUND) { * // other code here * offset = fdt_node_offset_by_compatible(fdt, offset, compatible); * } * * Note the -1 in the first call to the function, if 0 is used here * instead, the function will never locate the root node, even if it * matches the criterion. * * returns: * structure block offset of the located node (>= 0, >startoffset), * on success * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the * tree after startoffset * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, standard meanings */ int fdt_node_offset_by_compatible(const void *fdt, int startoffset, const char *compatible); /** * fdt_stringlist_contains - check a string list property for a string * @strlist: Property containing a list of strings to check * @listlen: Length of property * @str: String to search for * * This is a utility function provided for convenience. The list contains * one or more strings, each terminated by \0, as is found in a device tree * "compatible" property. * * @return: 1 if the string is found in the list, 0 not found, or invalid list */ int fdt_stringlist_contains(const char *strlist, int listlen, const char *str); /** * fdt_stringlist_count - count the number of strings in a string list * @fdt: pointer to the device tree blob * @nodeoffset: offset of a tree node * @property: name of the property containing the string list * @return: * the number of strings in the given property * -FDT_ERR_BADVALUE if the property value is not NUL-terminated * -FDT_ERR_NOTFOUND if the property does not exist */ int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property); /** * fdt_stringlist_search - find a string in a string list and return its index * @fdt: pointer to the device tree blob * @nodeoffset: offset of a tree node * @property: name of the property containing the string list * @string: string to look up in the string list * * Note that it is possible for this function to succeed on property values * that are not NUL-terminated. That's because the function will stop after * finding the first occurrence of @string. This can for example happen with * small-valued cell properties, such as #address-cells, when searching for * the empty string. * * @return: * the index of the string in the list of strings * -FDT_ERR_BADVALUE if the property value is not NUL-terminated * -FDT_ERR_NOTFOUND if the property does not exist or does not contain * the given string */ int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property, const char *string); /** * fdt_stringlist_get() - obtain the string at a given index in a string list * @fdt: pointer to the device tree blob * @nodeoffset: offset of a tree node * @property: name of the property containing the string list * @index: index of the string to return * @lenp: return location for the string length or an error code on failure * * Note that this will successfully extract strings from properties with * non-NUL-terminated values. For example on small-valued cell properties * this function will return the empty string. * * If non-NULL, the length of the string (on success) or a negative error-code * (on failure) will be stored in the integer pointer to by lenp. * * @return: * A pointer to the string at the given index in the string list or NULL on * failure. On success the length of the string will be stored in the memory * location pointed to by the lenp parameter, if non-NULL. On failure one of * the following negative error codes will be returned in the lenp parameter * (if non-NULL): * -FDT_ERR_BADVALUE if the property value is not NUL-terminated * -FDT_ERR_NOTFOUND if the property does not exist */ const char *fdt_stringlist_get(const void *fdt, int nodeoffset, const char *property, int index, int *lenp); /**********************************************************************/ /* Read-only functions (addressing related) */ /**********************************************************************/ /** * FDT_MAX_NCELLS - maximum value for #address-cells and #size-cells * * This is the maximum value for #address-cells, #size-cells and * similar properties that will be processed by libfdt. IEE1275 * requires that OF implementations handle values up to 4. * Implementations may support larger values, but in practice higher * values aren't used. */ #define FDT_MAX_NCELLS 4 /** * fdt_address_cells - retrieve address size for a bus represented in the tree * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node to find the address size for * * When the node has a valid #address-cells property, returns its value. * * returns: * 0 <= n < FDT_MAX_NCELLS, on success * 2, if the node has no #address-cells property * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid * #address-cells property * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_address_cells(const void *fdt, int nodeoffset); /** * fdt_size_cells - retrieve address range size for a bus represented in the * tree * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node to find the address range size for * * When the node has a valid #size-cells property, returns its value. * * returns: * 0 <= n < FDT_MAX_NCELLS, on success * 2, if the node has no #size-cells property * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid * #size-cells property * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_size_cells(const void *fdt, int nodeoffset); /**********************************************************************/ /* Write-in-place functions */ /**********************************************************************/ /** * fdt_setprop_inplace_namelen_partial - change a property's value, * but not its size * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to change * @namelen: number of characters of name to consider * @idx: index of the property to change in the array * @val: pointer to data to replace the property value with * @len: length of the property value * * Identical to fdt_setprop_inplace(), but modifies the given property * starting from the given index, and using only the first characters * of the name. It is useful when you want to manipulate only one value of * an array and you have a string that doesn't end with \0. */ #ifndef SWIG /* Not available in Python */ int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset, const char *name, int namelen, uint32_t idx, const void *val, int len); #endif /** * fdt_setprop_inplace - change a property's value, but not its size * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to change * @val: pointer to data to replace the property value with * @len: length of the property value * * fdt_setprop_inplace() replaces the value of a given property with * the data in val, of length len. This function cannot change the * size of a property, and so will only work if len is equal to the * current length of the property. * * This function will alter only the bytes in the blob which contain * the given property value, and will not alter or move any other part * of the tree. * * returns: * 0, on success * -FDT_ERR_NOSPACE, if len is not equal to the property's current length * -FDT_ERR_NOTFOUND, node does not have the named property * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ #ifndef SWIG /* Not available in Python */ int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name, const void *val, int len); #endif /** * fdt_setprop_inplace_u32 - change the value of a 32-bit integer property * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to change * @val: 32-bit integer value to replace the property with * * fdt_setprop_inplace_u32() replaces the value of a given property * with the 32-bit integer value in val, converting val to big-endian * if necessary. This function cannot change the size of a property, * and so will only work if the property already exists and has length * 4. * * This function will alter only the bytes in the blob which contain * the given property value, and will not alter or move any other part * of the tree. * * returns: * 0, on success * -FDT_ERR_NOSPACE, if the property's length is not equal to 4 * -FDT_ERR_NOTFOUND, node does not have the named property * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ static inline int fdt_setprop_inplace_u32(void *fdt, int nodeoffset, const char *name, uint32_t val) { fdt32_t tmp = cpu_to_fdt32(val); return fdt_setprop_inplace(fdt, nodeoffset, name, &tmp, sizeof(tmp)); } /** * fdt_setprop_inplace_u64 - change the value of a 64-bit integer property * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to change * @val: 64-bit integer value to replace the property with * * fdt_setprop_inplace_u64() replaces the value of a given property * with the 64-bit integer value in val, converting val to big-endian * if necessary. This function cannot change the size of a property, * and so will only work if the property already exists and has length * 8. * * This function will alter only the bytes in the blob which contain * the given property value, and will not alter or move any other part * of the tree. * * returns: * 0, on success * -FDT_ERR_NOSPACE, if the property's length is not equal to 8 * -FDT_ERR_NOTFOUND, node does not have the named property * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ static inline int fdt_setprop_inplace_u64(void *fdt, int nodeoffset, const char *name, uint64_t val) { fdt64_t tmp = cpu_to_fdt64(val); return fdt_setprop_inplace(fdt, nodeoffset, name, &tmp, sizeof(tmp)); } /** * fdt_setprop_inplace_cell - change the value of a single-cell property * * This is an alternative name for fdt_setprop_inplace_u32() */ static inline int fdt_setprop_inplace_cell(void *fdt, int nodeoffset, const char *name, uint32_t val) { return fdt_setprop_inplace_u32(fdt, nodeoffset, name, val); } /** * fdt_nop_property - replace a property with nop tags * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to nop * @name: name of the property to nop * * fdt_nop_property() will replace a given property's representation * in the blob with FDT_NOP tags, effectively removing it from the * tree. * * This function will alter only the bytes in the blob which contain * the property, and will not alter or move any other part of the * tree. * * returns: * 0, on success * -FDT_ERR_NOTFOUND, node does not have the named property * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_nop_property(void *fdt, int nodeoffset, const char *name); /** * fdt_nop_node - replace a node (subtree) with nop tags * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node to nop * * fdt_nop_node() will replace a given node's representation in the * blob, including all its subnodes, if any, with FDT_NOP tags, * effectively removing it from the tree. * * This function will alter only the bytes in the blob which contain * the node and its properties and subnodes, and will not alter or * move any other part of the tree. * * returns: * 0, on success * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_nop_node(void *fdt, int nodeoffset); /**********************************************************************/ /* Sequential write functions */ /**********************************************************************/ int fdt_create(void *buf, int bufsize); int fdt_resize(void *fdt, void *buf, int bufsize); int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size); int fdt_finish_reservemap(void *fdt); int fdt_begin_node(void *fdt, const char *name); int fdt_property(void *fdt, const char *name, const void *val, int len); static inline int fdt_property_u32(void *fdt, const char *name, uint32_t val) { fdt32_t tmp = cpu_to_fdt32(val); return fdt_property(fdt, name, &tmp, sizeof(tmp)); } static inline int fdt_property_u64(void *fdt, const char *name, uint64_t val) { fdt64_t tmp = cpu_to_fdt64(val); return fdt_property(fdt, name, &tmp, sizeof(tmp)); } #ifndef SWIG /* Not available in Python */ static inline int fdt_property_cell(void *fdt, const char *name, uint32_t val) { return fdt_property_u32(fdt, name, val); } #endif /** * fdt_property_placeholder - add a new property and return a ptr to its value * * @fdt: pointer to the device tree blob * @name: name of property to add * @len: length of property value in bytes * @valp: returns a pointer to where where the value should be placed * * returns: * 0, on success * -FDT_ERR_BADMAGIC, * -FDT_ERR_NOSPACE, standard meanings */ int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp); #define fdt_property_string(fdt, name, str) \ fdt_property(fdt, name, str, strlen(str)+1) int fdt_end_node(void *fdt); int fdt_finish(void *fdt); /**********************************************************************/ /* Read-write functions */ /**********************************************************************/ int fdt_create_empty_tree(void *buf, int bufsize); int fdt_open_into(const void *fdt, void *buf, int bufsize); int fdt_pack(void *fdt); /** * fdt_add_mem_rsv - add one memory reserve map entry * @fdt: pointer to the device tree blob * @address, @size: 64-bit values (native endian) * * Adds a reserve map entry to the given blob reserving a region at * address address of length size. * * This function will insert data into the reserve map and will * therefore change the indexes of some entries in the table. * * returns: * 0, on success * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to * contain the new reservation entry * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size); /** * fdt_del_mem_rsv - remove a memory reserve map entry * @fdt: pointer to the device tree blob * @n: entry to remove * * fdt_del_mem_rsv() removes the n-th memory reserve map entry from * the blob. * * This function will delete data from the reservation table and will * therefore change the indexes of some entries in the table. * * returns: * 0, on success * -FDT_ERR_NOTFOUND, there is no entry of the given index (i.e. there * are less than n+1 reserve map entries) * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_del_mem_rsv(void *fdt, int n); /** * fdt_set_name - change the name of a given node * @fdt: pointer to the device tree blob * @nodeoffset: structure block offset of a node * @name: name to give the node * * fdt_set_name() replaces the name (including unit address, if any) * of the given node with the given string. NOTE: this function can't * efficiently check if the new name is unique amongst the given * node's siblings; results are undefined if this function is invoked * with a name equal to one of the given node's siblings. * * This function may insert or delete data from the blob, and will * therefore change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_NOSPACE, there is insufficient free space in the blob * to contain the new name * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, standard meanings */ int fdt_set_name(void *fdt, int nodeoffset, const char *name); /** * fdt_setprop - create or change a property * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to change * @val: pointer to data to set the property value to * @len: length of the property value * * fdt_setprop() sets the value of the named property in the given * node to the given value and length, creating the property if it * does not already exist. * * This function may insert or delete data from the blob, and will * therefore change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to * contain the new property value * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_setprop(void *fdt, int nodeoffset, const char *name, const void *val, int len); /** * fdt_setprop_placeholder - allocate space for a property * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to change * @len: length of the property value * @prop_data: return pointer to property data * * fdt_setprop_placeholer() allocates the named property in the given node. * If the property exists it is resized. In either case a pointer to the * property data is returned. * * This function may insert or delete data from the blob, and will * therefore change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to * contain the new property value * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_setprop_placeholder(void *fdt, int nodeoffset, const char *name, int len, void **prop_data); /** * fdt_setprop_u32 - set a property to a 32-bit integer * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to change * @val: 32-bit integer value for the property (native endian) * * fdt_setprop_u32() sets the value of the named property in the given * node to the given 32-bit integer value (converting to big-endian if * necessary), or creates a new property with that value if it does * not already exist. * * This function may insert or delete data from the blob, and will * therefore change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to * contain the new property value * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ static inline int fdt_setprop_u32(void *fdt, int nodeoffset, const char *name, uint32_t val) { fdt32_t tmp = cpu_to_fdt32(val); return fdt_setprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); } /** * fdt_setprop_u64 - set a property to a 64-bit integer * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to change * @val: 64-bit integer value for the property (native endian) * * fdt_setprop_u64() sets the value of the named property in the given * node to the given 64-bit integer value (converting to big-endian if * necessary), or creates a new property with that value if it does * not already exist. * * This function may insert or delete data from the blob, and will * therefore change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to * contain the new property value * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ static inline int fdt_setprop_u64(void *fdt, int nodeoffset, const char *name, uint64_t val) { fdt64_t tmp = cpu_to_fdt64(val); return fdt_setprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); } /** * fdt_setprop_cell - set a property to a single cell value * * This is an alternative name for fdt_setprop_u32() */ static inline int fdt_setprop_cell(void *fdt, int nodeoffset, const char *name, uint32_t val) { return fdt_setprop_u32(fdt, nodeoffset, name, val); } /** * fdt_setprop_string - set a property to a string value * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to change * @str: string value for the property * * fdt_setprop_string() sets the value of the named property in the * given node to the given string value (using the length of the * string to determine the new length of the property), or creates a * new property with that value if it does not already exist. * * This function may insert or delete data from the blob, and will * therefore change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to * contain the new property value * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ #define fdt_setprop_string(fdt, nodeoffset, name, str) \ fdt_setprop((fdt), (nodeoffset), (name), (str), strlen(str)+1) /** * fdt_setprop_empty - set a property to an empty value * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to change * * fdt_setprop_empty() sets the value of the named property in the * given node to an empty (zero length) value, or creates a new empty * property if it does not already exist. * * This function may insert or delete data from the blob, and will * therefore change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to * contain the new property value * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ #define fdt_setprop_empty(fdt, nodeoffset, name) \ fdt_setprop((fdt), (nodeoffset), (name), NULL, 0) /** * fdt_appendprop - append to or create a property * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to append to * @val: pointer to data to append to the property value * @len: length of the data to append to the property value * * fdt_appendprop() appends the value to the named property in the * given node, creating the property if it does not already exist. * * This function may insert data into the blob, and will therefore * change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to * contain the new property value * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_appendprop(void *fdt, int nodeoffset, const char *name, const void *val, int len); /** * fdt_appendprop_u32 - append a 32-bit integer value to a property * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to change * @val: 32-bit integer value to append to the property (native endian) * * fdt_appendprop_u32() appends the given 32-bit integer value * (converting to big-endian if necessary) to the value of the named * property in the given node, or creates a new property with that * value if it does not already exist. * * This function may insert data into the blob, and will therefore * change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to * contain the new property value * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ static inline int fdt_appendprop_u32(void *fdt, int nodeoffset, const char *name, uint32_t val) { fdt32_t tmp = cpu_to_fdt32(val); return fdt_appendprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); } /** * fdt_appendprop_u64 - append a 64-bit integer value to a property * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to change * @val: 64-bit integer value to append to the property (native endian) * * fdt_appendprop_u64() appends the given 64-bit integer value * (converting to big-endian if necessary) to the value of the named * property in the given node, or creates a new property with that * value if it does not already exist. * * This function may insert data into the blob, and will therefore * change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to * contain the new property value * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ static inline int fdt_appendprop_u64(void *fdt, int nodeoffset, const char *name, uint64_t val) { fdt64_t tmp = cpu_to_fdt64(val); return fdt_appendprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); } /** * fdt_appendprop_cell - append a single cell value to a property * * This is an alternative name for fdt_appendprop_u32() */ static inline int fdt_appendprop_cell(void *fdt, int nodeoffset, const char *name, uint32_t val) { return fdt_appendprop_u32(fdt, nodeoffset, name, val); } /** * fdt_appendprop_string - append a string to a property * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to change * @name: name of the property to change * @str: string value to append to the property * * fdt_appendprop_string() appends the given string to the value of * the named property in the given node, or creates a new property * with that value if it does not already exist. * * This function may insert data into the blob, and will therefore * change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to * contain the new property value * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ #define fdt_appendprop_string(fdt, nodeoffset, name, str) \ fdt_appendprop((fdt), (nodeoffset), (name), (str), strlen(str)+1) /** * fdt_delprop - delete a property * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node whose property to nop * @name: name of the property to nop * * fdt_del_property() will delete the given property. * * This function will delete data from the blob, and will therefore * change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_NOTFOUND, node does not have the named property * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_delprop(void *fdt, int nodeoffset, const char *name); /** * fdt_add_subnode_namelen - creates a new node based on substring * @fdt: pointer to the device tree blob * @parentoffset: structure block offset of a node * @name: name of the subnode to locate * @namelen: number of characters of name to consider * * Identical to fdt_add_subnode(), but use only the first namelen * characters of name as the name of the new node. This is useful for * creating subnodes based on a portion of a larger string, such as a * full path. */ #ifndef SWIG /* Not available in Python */ int fdt_add_subnode_namelen(void *fdt, int parentoffset, const char *name, int namelen); #endif /** * fdt_add_subnode - creates a new node * @fdt: pointer to the device tree blob * @parentoffset: structure block offset of a node * @name: name of the subnode to locate * * fdt_add_subnode() creates a new node as a subnode of the node at * structure block offset parentoffset, with the given name (which * should include the unit address, if any). * * This function will insert data into the blob, and will therefore * change the offsets of some existing nodes. * returns: * structure block offset of the created nodeequested subnode (>=0), on * success * -FDT_ERR_NOTFOUND, if the requested subnode does not exist * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE * tag * -FDT_ERR_EXISTS, if the node at parentoffset already has a subnode of * the given name * -FDT_ERR_NOSPACE, if there is insufficient free space in the * blob to contain the new node * -FDT_ERR_NOSPACE * -FDT_ERR_BADLAYOUT * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings. */ int fdt_add_subnode(void *fdt, int parentoffset, const char *name); /** * fdt_del_node - delete a node (subtree) * @fdt: pointer to the device tree blob * @nodeoffset: offset of the node to nop * * fdt_del_node() will remove the given node, including all its * subnodes if any, from the blob. * * This function will delete data from the blob, and will therefore * change the offsets of some existing nodes. * * returns: * 0, on success * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTATE, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_del_node(void *fdt, int nodeoffset); /** * fdt_overlay_apply - Applies a DT overlay on a base DT * @fdt: pointer to the base device tree blob * @fdto: pointer to the device tree overlay blob * * fdt_overlay_apply() will apply the given device tree overlay on the * given base device tree. * * Expect the base device tree to be modified, even if the function * returns an error. * * returns: * 0, on success * -FDT_ERR_NOSPACE, there's not enough space in the base device tree * -FDT_ERR_NOTFOUND, the overlay points to some inexistant nodes or * properties in the base DT * -FDT_ERR_BADPHANDLE, * -FDT_ERR_BADOVERLAY, * -FDT_ERR_NOPHANDLES, * -FDT_ERR_INTERNAL, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADMAGIC, * -FDT_ERR_BADOFFSET, * -FDT_ERR_BADPATH, * -FDT_ERR_BADVERSION, * -FDT_ERR_BADSTRUCTURE, * -FDT_ERR_BADSTATE, * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_overlay_apply(void *fdt, void *fdto); /**********************************************************************/ /* Debugging / informational functions */ /**********************************************************************/ const char *fdt_strerror(int errval); static inline uint64_t fdt32_to_cpu64(fdt32_t high, fdt32_t low) { return ((uint64_t)fdt32_to_cpu(high) << 32) | fdt32_to_cpu(low); } int fdt_set_node_reg(void *dtb, int node, unsigned long iomem, size_t iomem_size); #endif /* LIBFDT_H */ ================================================ FILE: kernel/include/libfdt/libfdt_env.h ================================================ #ifndef LIBFDT_ENV_H #define LIBFDT_ENV_H /* * libfdt - Flat Device Tree manipulation * Copyright (C) 2006 David Gibson, IBM Corporation. * Copyright 2012 Kim Phillips, Freescale Semiconductor. * * libfdt is dual licensed: you can use it either under the terms of * the GPL, or the BSD license, at your option. * * a) This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301 USA * * Alternatively, * * b) Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #ifdef __CHECKER__ #define FDT_FORCE __attribute__((force)) #define FDT_BITWISE __attribute__((bitwise)) #else #define FDT_FORCE #define FDT_BITWISE #endif typedef uint16_t FDT_BITWISE fdt16_t; typedef uint32_t FDT_BITWISE fdt32_t; typedef uint64_t FDT_BITWISE fdt64_t; #define EXTRACT_BYTE(x, n) ((unsigned long long)((uint8_t *)&x)[n]) #define CPU_TO_FDT16(x) ((EXTRACT_BYTE(x, 0) << 8) | EXTRACT_BYTE(x, 1)) #define CPU_TO_FDT32(x) ((EXTRACT_BYTE(x, 0) << 24) | (EXTRACT_BYTE(x, 1) << 16) | \ (EXTRACT_BYTE(x, 2) << 8) | EXTRACT_BYTE(x, 3)) #define CPU_TO_FDT64(x) ((EXTRACT_BYTE(x, 0) << 56) | (EXTRACT_BYTE(x, 1) << 48) | \ (EXTRACT_BYTE(x, 2) << 40) | (EXTRACT_BYTE(x, 3) << 32) | \ (EXTRACT_BYTE(x, 4) << 24) | (EXTRACT_BYTE(x, 5) << 16) | \ (EXTRACT_BYTE(x, 6) << 8) | EXTRACT_BYTE(x, 7)) static inline uint16_t fdt16_to_cpu(fdt16_t x) { return (FDT_FORCE uint16_t)CPU_TO_FDT16(x); } static inline fdt16_t cpu_to_fdt16(uint16_t x) { return (FDT_FORCE fdt16_t)CPU_TO_FDT16(x); } static inline uint32_t fdt32_to_cpu(fdt32_t x) { return (FDT_FORCE uint32_t)CPU_TO_FDT32(x); } static inline fdt32_t cpu_to_fdt32(uint32_t x) { return (FDT_FORCE fdt32_t)CPU_TO_FDT32(x); } static inline uint64_t fdt64_to_cpu(fdt64_t x) { return (FDT_FORCE uint64_t)CPU_TO_FDT64(x); } static inline fdt64_t cpu_to_fdt64(uint64_t x) { return (FDT_FORCE fdt64_t)CPU_TO_FDT64(x); } #undef CPU_TO_FDT64 #undef CPU_TO_FDT32 #undef CPU_TO_FDT16 #undef EXTRACT_BYTE #ifdef __APPLE__ #include /* strnlen() is not available on Mac OS < 10.7 */ # if !defined(MAC_OS_X_VERSION_10_7) || (MAC_OS_X_VERSION_MAX_ALLOWED < \ MAC_OS_X_VERSION_10_7) #define strnlen fdt_strnlen /* * fdt_strnlen: returns the length of a string or max_count - which ever is * smallest. * Input 1 string: the string whose size is to be determined * Input 2 max_count: the maximum value returned by this function * Output: length of the string or max_count (the smallest of the two) */ static inline size_t fdt_strnlen(const char *string, size_t max_count) { const char *p = memchr(string, 0, max_count); return p ? p - string : max_count; } #endif /* !defined(MAC_OS_X_VERSION_10_7) || (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7) */ #endif /* __APPLE__ */ #endif /* LIBFDT_ENV_H */ ================================================ FILE: kernel/include/minos/arch.h ================================================ #ifndef __MINOS_ARCH_H_ #define __MINOS_ARCH_H_ #include struct vspace; struct task; #define raw_smp_processor_id() arch_raw_smp_processor_id() #define get_virtual_address_size() arch_get_virtual_address_size() phy_addr_t arch_translate_va_to_pa(struct vspace *mm, unsigned long va); int arch_host_map(struct vspace *mm, unsigned long start, unsigned long end, unsigned long physical, unsigned long flags); int arch_host_unmap(struct vspace *mm, unsigned long start, unsigned long end, int mode); unsigned long arch_kernel_pgd_base(void); int arch_get_asid_size(void); int arch_host_change_map(struct vspace *vs, unsigned long vir, unsigned long phy, unsigned long flags); pgd_t *arch_alloc_process_page_table(void); void arch_task_sched_out(struct task *task); void arch_task_sched_in(struct task *task); void arch_set_task_user_stack(struct task *task, unsigned long stack); void arch_set_task_reg0(struct task *task, unsigned long data); void arch_set_tls(struct task *task, unsigned long tls); void arch_set_task_entry_point(struct task *task, long entry); #ifdef CONFIG_VIRT struct mm_struct; void *arch_alloc_guest_pgd(void); int arch_guest_unmap(struct mm_struct *mm, unsigned long start, unsigned long end); int arch_guest_map(struct mm_struct *mm, unsigned long start, unsigned long end, unsigned long physical, unsigned long flags); int arch_translate_guest_ipa(struct mm_struct *vs, unsigned long va, phy_addr_t *pa); #endif #endif ================================================ FILE: kernel/include/minos/atomic.h ================================================ #ifndef _MINOS_ATOMIC_H_ #define _MINOS_ATOMIC_H_ #include #include #define ATOMIC_INIT(v) { (v) } static inline void atomic_inc(atomic_t *t) { atomic_add(1, t); } static inline void atomic_dec(atomic_t *t) { atomic_sub(1, t); } static inline int atomic_inc_return(atomic_t *t) { return atomic_add_return(1, t); } static inline int atomic_dec_return(atomic_t *t) { return atomic_sub_return(1, t); } static inline int atomic_inc_return_old(atomic_t *t) { return atomic_add_return_old(1, t); } static inline int atomic_dec_return_old(atomic_t *t) { return atomic_sub_return_old(1, t); } static inline int atomic_inc_and_test(atomic_t *t) { return atomic_add_return(1, t) == 0; } static inline int atomic_dec_and_test(atomic_t *t) { return atomic_sub_return(1, t) == 0; } static inline int atomic_add_negative(int i, atomic_t *t) { return atomic_add_return(i, t) < 0; } #endif ================================================ FILE: kernel/include/minos/bitmap.h ================================================ #ifndef __LINUX_BITMAP_H #define __LINUX_BITMAP_H #include #include #include /* * bitmaps provide bit arrays that consume one or more unsigned * longs. The bitmap interface and available operations are listed * here, in bitmap.h * * Function implementations generic to all architectures are in * lib/bitmap.c. Functions implementations that are architecture * specific are in various include/asm-/bitops.h headers * and other arch/ specific files. * * See lib/bitmap.c for more details. */ /* * Also the following operations in asm/bitops.h apply to bitmaps. * * set_bit(bit, addr) *addr |= bit * clear_bit(bit, addr) *addr &= ~bit * change_bit(bit, addr) *addr ^= bit * test_bit(bit, addr) Is bit set in *addr? * test_and_set_bit(bit, addr) Set bit and return old value * test_and_clear_bit(bit, addr) Clear bit and return old value * test_and_change_bit(bit, addr) Change bit and return old value * find_first_zero_bit(addr, nbits) Position first zero bit in *addr * find_first_bit(addr, nbits) Position first set bit in *addr * find_next_zero_bit(addr, nbits, bit) Position next zero bit in *addr >= bit * find_next_bit(addr, nbits, bit) Position next set bit in *addr >= bit */ /* * The DECLARE_BITMAP(name,bits) macro, in linux/types.h, can be used * to declare an array named 'name' of just enough unsigned longs to * contain all bit positions from 0 to 'bits' - 1. */ extern void bitmap_set(unsigned long *map, unsigned int start, int len); extern void bitmap_clear(unsigned long *map, unsigned int start, int len); extern unsigned long bitmap_find_next_zero_area_off(unsigned long *map, unsigned long size, unsigned long start, unsigned int nr, unsigned long align_mask, unsigned long align_offset); /** * bitmap_find_next_zero_area - find a contiguous aligned zero area * @map: The address to base the search on * @size: The bitmap size in bits * @start: The bitnumber to start searching at * @nr: The number of zeroed bits we're looking for * @align_mask: Alignment mask for zero area * * The @align_mask should be one less than a power of 2; the effect is that * the bit offset of all zero areas this function finds is multiples of that * power of 2. A @align_mask of 0 means no alignment is required. */ static inline unsigned long bitmap_find_next_zero_area(unsigned long *map, unsigned long size, unsigned long start, unsigned int nr, unsigned long align_mask) { return bitmap_find_next_zero_area_off(map, size, start, nr, align_mask, 0); } unsigned long bitmap_find_next_zero_area_align(unsigned long *map, unsigned long size, unsigned long start, unsigned int nr, unsigned long align); #define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) & (BITS_PER_LONG - 1))) #define BITMAP_LAST_WORD_MASK(nbits) (~0UL >> (-(nbits) & (BITS_PER_LONG - 1))) #define small_const_nbits(nbits) \ (__builtin_constant_p(nbits) && (nbits) <= BITS_PER_LONG) static inline void bitmap_zero(unsigned long *dst, int nbits) { if (small_const_nbits(nbits)) *dst = 0UL; else { int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long); memset(dst, 0, len); } } static inline void bitmap_fill(unsigned long *dst, int nbits) { size_t nlongs = BITS_TO_LONGS(nbits); if (!small_const_nbits(nbits)) { int len = (nlongs - 1) * sizeof(unsigned long); memset(dst, 0xff, len); } dst[nlongs - 1] = BITMAP_LAST_WORD_MASK(nbits); } #endif /* __LINUX_BITMAP_H */ ================================================ FILE: kernel/include/minos/bitops.h ================================================ #ifndef _LINUX_BITOPS_H #define _LINUX_BITOPS_H #include /* * Create a contiguous bitmask starting at bit position @l and ending at * position @h. For example * GENMASK_ULL(39, 21) gives us the 64bit vector 0x000000ffffe00000. */ extern unsigned int sw_hweight8(unsigned int w); extern unsigned int sw_hweight16(unsigned int w); extern unsigned int sw_hweight32(unsigned int w); extern unsigned long sw_hweight64(__u64 w); /* * Include this here because some architectures need generic_ffs/fls in * scope */ #include unsigned long find_next_bit(const unsigned long *addr, unsigned long size, unsigned long offset); unsigned long find_next_zero_bit(const unsigned long *addr, unsigned long size, unsigned long offset); unsigned long find_next_bit_loop(const unsigned long *addr, unsigned long size, unsigned long offset); unsigned long find_next_zero_bit_loop(const unsigned long *addr, unsigned long size, unsigned long offset); unsigned long find_first_bit(const unsigned long *addr, unsigned long size); unsigned long find_first_zero_bit(const unsigned long *addr, unsigned long size); unsigned long find_last_bit(const unsigned long *addr, unsigned long size); #define for_each_set_bit(bit, addr, size) \ for ((bit) = find_first_bit((addr), (size)); \ (bit) < (size); \ (bit) = find_next_bit((addr), (size), (bit) + 1)) /* same as for_each_set_bit() but use bit as value to start with */ #define for_each_set_bit_from(bit, addr, size) \ for ((bit) = find_next_bit((addr), (size), (bit)); \ (bit) < (size); \ (bit) = find_next_bit((addr), (size), (bit) + 1)) #define for_each_clear_bit(bit, addr, size) \ for ((bit) = find_first_zero_bit((addr), (size)); \ (bit) < (size); \ (bit) = find_next_zero_bit((addr), (size), (bit) + 1)) /* same as for_each_clear_bit() but use bit as value to start with */ #define for_each_clear_bit_from(bit, addr, size) \ for ((bit) = find_next_zero_bit((addr), (size), (bit)); \ (bit) < (size); \ (bit) = find_next_zero_bit((addr), (size), (bit) + 1)) static inline int get_bitmask_order(unsigned int count) { int order; order = fls(count); return order; /* We could be slightly more clever with -1 here... */ } static inline int get_count_order(unsigned int count) { int order; order = fls(count) - 1; if (count & (count - 1)) order++; return order; } static inline unsigned long hweight_long(unsigned long w) { return sizeof(w) == 4 ? sw_hweight32(w) : sw_hweight64(w); } /** * rol64 - rotate a 64-bit value left * @word: value to rotate * @shift: bits to roll */ static inline __u64 rol64(__u64 word, unsigned int shift) { return (word << shift) | (word >> (64 - shift)); } /** * ror64 - rotate a 64-bit value right * @word: value to rotate * @shift: bits to roll */ static inline __u64 ror64(__u64 word, unsigned int shift) { return (word >> shift) | (word << (64 - shift)); } /** * rol32 - rotate a 32-bit value left * @word: value to rotate * @shift: bits to roll */ static inline __u32 rol32(__u32 word, unsigned int shift) { return (word << shift) | (word >> ((-shift) & 31)); } /** * ror32 - rotate a 32-bit value right * @word: value to rotate * @shift: bits to roll */ static inline __u32 ror32(__u32 word, unsigned int shift) { return (word >> shift) | (word << (32 - shift)); } /** * rol16 - rotate a 16-bit value left * @word: value to rotate * @shift: bits to roll */ static inline __u16 rol16(__u16 word, unsigned int shift) { return (word << shift) | (word >> (16 - shift)); } /** * ror16 - rotate a 16-bit value right * @word: value to rotate * @shift: bits to roll */ static inline __u16 ror16(__u16 word, unsigned int shift) { return (word >> shift) | (word << (16 - shift)); } /** * rol8 - rotate an 8-bit value left * @word: value to rotate * @shift: bits to roll */ static inline __u8 rol8(__u8 word, unsigned int shift) { return (word << shift) | (word >> (8 - shift)); } /** * ror8 - rotate an 8-bit value right * @word: value to rotate * @shift: bits to roll */ static inline __u8 ror8(__u8 word, unsigned int shift) { return (word >> shift) | (word << (8 - shift)); } /** * sign_extend32 - sign extend a 32-bit value using specified bit as sign-bit * @value: value to sign extend * @index: 0 based bit index (0<=index<32) to sign bit * * This is safe to use for 16- and 8-bit types as well. */ static inline __s32 sign_extend32(__u32 value, int index) { __u8 shift = 31 - index; return (__s32)(value << shift) >> shift; } /** * sign_extend64 - sign extend a 64-bit value using specified bit as sign-bit * @value: value to sign extend * @index: 0 based bit index (0<=index<64) to sign bit */ static inline __s64 sign_extend64(__u64 value, int index) { __u8 shift = 63 - index; return (__s64)(value << shift) >> shift; } static inline unsigned fls_long(unsigned long l) { if (sizeof(l) == 4) return fls(l); return fls64(l); } /** * __ffs64 - find first set bit in a 64 bit word * @word: The 64 bit word * * On 64 bit arches this is a synomyn for __ffs * The result is not defined if no bits are set, so check that @word * is non-zero before calling this. */ static inline unsigned long __ffs64(__u64 word) { #if BITS_PER_LONG == 32 if (((u32)word) == 0UL) return __ffs((u32)(word >> 32)) + 32; #elif BITS_PER_LONG != 64 #error BITS_PER_LONG not 32 or 64 #endif return __ffs((unsigned long)word); } #ifndef set_mask_bits #define set_mask_bits(ptr, _mask, _bits) \ ({ \ const typeof(*ptr) mask = (_mask), bits = (_bits); \ typeof(*ptr) old, new; \ \ do { \ old = ACCESS_ONCE(*ptr); \ new = (old & ~mask) | bits; \ } while (cmpxchg(ptr, old, new) != old); \ \ new; \ }) #endif #ifndef bit_clear_unless #define bit_clear_unless(ptr, _clear, _test) \ ({ \ const typeof(*ptr) clear = (_clear), test = (_test); \ typeof(*ptr) old, new; \ \ do { \ old = ACCESS_ONCE(*ptr); \ new = old & ~clear; \ } while (!(old & test) && \ cmpxchg(ptr, old, new) != old); \ \ !(old & test); \ }) #endif #ifndef find_last_bit /** * find_last_bit - find the last set bit in a memory region * @addr: The address to start the search at * @size: The number of bits to search * * Returns the bit number of the last set bit, or size. */ extern unsigned long find_last_bit(const unsigned long *addr, unsigned long size); #endif void set_bit(int nr, unsigned long *p); void clear_bit(int nr, unsigned long *p); void change_bit(int nr, unsigned long *p); int test_bit(int nr, unsigned long *p); int test_and_set_bit(int nr, unsigned long *p); int test_and_clear_bit(int nr, unsigned long *p); int test_and_change_bit(int nr, unsigned long *p); #endif ================================================ FILE: kernel/include/minos/bootarg.h ================================================ #ifndef __MINOS_BOOTARG_H__ #define __MINOS_BOOTARG_H__ #include int bootargs_init(const char *str, int len); int __get_boot_option(char *name, void *value, int (*parse)(char *args, void *value)); int bootarg_parse_hex32(char *name, uint32_t *v); int bootarg_parse_hex64(char *name, uint64_t *v); int bootarg_parse_uint(char *name, uint32_t *v); int bootarg_parse_bool(char *name, int *v); int bootarg_parse_string(char *name, char **v); #endif ================================================ FILE: kernel/include/minos/calltrace.h ================================================ #ifndef __MINOS_CALLTRACE_H_ #define __MINOS_CALLTRACE_H_ #include void __panic(gp_regs *regs, char *str, ...) __noreturn; void print_symbol(unsigned long addr); void dump_stack(gp_regs *regs, unsigned long *stack); #define panic(...) __panic(NULL, __VA_ARGS__) #define WARN_ON(condition, ...) \ if ((condition)) { \ do { \ pr_err("WARN: " __VA_ARGS__); \ } while (0); \ } #define BUG_ON(condition, ...) \ if ((condition)) { \ do { \ panic("BUG: " __VA_ARGS__); \ } while (0); \ } #define ASSERT(condition) \ if (!(condition)) { \ do { \ panic("ASSERT FAIL: %s %d\n", __func__, __LINE__); \ } while (0); \ } #endif ================================================ FILE: kernel/include/minos/channel.h ================================================ #ifndef __MINOS_CHANNEL_H__ #define __MINOS_CHANNEL_H__ /* * object 代表内核资源,资源是全局的 每个资源就是一个object, * 每个但是资源会属于某个id 然后可以在各个进程中传递资源, * 每个资源会有唯一的id,资源从属于某一个进程,当资源授予某一个 * 进程的时候,返回一个hanlde, 这个handle属于进程描述符表中。 * 资源传递通过channel来传递. * * 每个server会有默认channel() ----> channel 0, register sever的时候会 * 注册channel的功能 * * 名字叫做port. 每个server使用port对外提供服务,每个port的功能可以不一样 * 比如一个典型的文件系统服务只有一个port * * 另外对于一个带有双工的网卡,可以有两个thread, 一个tx thread一个rx thread * 或者irq thread,则至少会有两个port * * /dev/marvel/port_tx // 文件类型 普通 port * /dev/marver/port_rx // 文件类型 * /proc/port0 * */ struct object { uint8_t type; }; struct vma_handle { struct object object; }; struct pmo { struct object object; }; // 客户端和服务端通信需要协议。 struct channel { int mutex_t mutex; }; 收到了vmo,调用vmo_map()map到自己的内存区域,然后可以进行读写。 fd = open("/dev/procsrv"); write(fd, &msg, sizeof(msg)); // create_process ipc_recv(&msg, sizeof(msg), NULL); if () { } switch (request) { case CREATE_PROCESS: break; } open("/dev/data/bin.elf"); vmo_handle= create_vmo(proc, xxx); ret = ipc_send(fd, &msg, sizeof(msg)); /* * 解析xxx */ ipc_send(fd, &msg, ); //read segment data ipc_send(fd, &msg); // read segment data close(vmo_handle); //可以不需要close ipc_send(fd, void *addr, size_t size, int flags); #endif ================================================ FILE: kernel/include/minos/compiler.h ================================================ #ifndef __MINOS_COMPILER_H_ #define __MINOS_COMPILER_H_ #define __cache_line_size__ (64) #define __section(S) __attribute__((__section__(#S))) #define __used __attribute__((__used__)) #define __unused __attribute__((__unused__)) #define __align(x) __attribute__((__aligned__(x))) #define __cache_line_align __align(__cache_line_size__) #define __packed __attribute__((__packed__)) #define __noreturn __attribute__((noreturn)) #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) #define barrier() __asm__ __volatile__("" ::: "memory") #define unused(__arg__) (void)(__arg__) #define __user #define __guest #ifndef weak_alias #define weak_alias(old, new) \ extern __typeof(old) new __attribute__((__weak__, __alias__(#old))) #endif #endif ================================================ FILE: kernel/include/minos/console.h ================================================ #ifndef __MINOS_CONSOLE_H__ #define __MINOS_CONSOLE_H__ #include struct console { char *name; int (*init)(char *arg); void (*putc)(char ch); char (*getc)(void); }; #define DEFINE_CONSOLE(n, console_name, init_fn, putc_fn, getc_fn) \ static struct console __console_##n __used __section(".__console") = { \ .name = console_name, \ .init = init_fn, \ .putc = putc_fn, \ .getc = getc_fn, \ } void console_init(char *name); void console_putc(char ch); char console_getc(void); void console_puts(char *buf, int len); void console_recv(const char *buf, int cnt); int console_gets(char *buf, int max, uint32_t timeout); #endif ================================================ FILE: kernel/include/minos/const.h ================================================ #ifndef __MINOS_CONST_H__ #define __MINOS_CONST_H__ #ifdef __ASSEMBLY__ #define _AC(X,Y) X #define _AT(T,X) X #else #define __AC(X,Y) (X##Y) #define _AC(X,Y) __AC(X,Y) #define _AT(T,X) ((T)(X)) #endif #define _UL(x) (_AC(x, UL)) #define _ULL(x) (_AC(x, ULL)) #define _BITUL(x) (_UL(1) << (x)) #define _BITULL(x) (_ULL(1) << (x)) #define UL(x) (_UL(x)) #define ULL(x) (_ULL(x)) #endif ================================================ FILE: kernel/include/minos/cpumask.h ================================================ #ifndef _MINOS_CPUMASK_H_ #define _MINOS_CPUMASK_H_ #include #include typedef struct cpumask { DECLARE_BITMAP(bits, CONFIG_NR_CPUS); } cpumask_t; #define nr_cpumask_bits (BITS_TO_LONGS(CONFIG_NR_CPUS) * BITS_PER_LONG) #define nr_cpu_ids CONFIG_NR_CPUS /* verify cpu argument to cpumask_* operators */ static inline unsigned int cpumask_check(unsigned int cpu) { /* * do some check */ return cpu; } static inline void cpumask_set_cpu(int cpu, cpumask_t *dstp) { set_bit(cpumask_check(cpu), dstp->bits); } static inline void __cpumask_set_cpu(int cpu, cpumask_t *dstp) { set_bit(cpumask_check(cpu), dstp->bits); } static inline void cpumask_clear_cpu(int cpu, cpumask_t *dstp) { clear_bit(cpumask_check(cpu), dstp->bits); } static inline void __cpumask_clear_cpu(int cpu, cpumask_t *dstp) { clear_bit(cpumask_check(cpu), dstp->bits); } static inline void cpumask_setall(cpumask_t *dstp) { bitmap_fill(dstp->bits, nr_cpumask_bits); } static inline void cpumask_clearall(cpumask_t *dstp) { bitmap_zero(dstp->bits, nr_cpumask_bits); } static inline int cpumask_first(const cpumask_t *srcp) { return min(nr_cpu_ids, find_first_bit(srcp->bits, nr_cpu_ids)); } static inline int cpumask_next(int n, const cpumask_t *srcp) { /* -1 is a legal arg here. */ if (n != -1) cpumask_check(n); return min(nr_cpu_ids, find_next_bit(srcp->bits, nr_cpu_ids, n + 1)); } #if CONFIG_NR_CPUS > 1 #define for_each_cpu(cpu, mask) \ for ((cpu) = cpumask_first(mask); \ (cpu) < CONFIG_NR_CPUS; \ (cpu) = cpumask_next(cpu, mask)) #else /* CONFIG_NR_CPUS == 1 */ #define for_each_cpu(cpu, mask) \ for ((cpu) = 0; (cpu) < 1; (cpu)++, (void)(mask)) #endif /* CONFIG_NR_CPUS */ #endif ================================================ FILE: kernel/include/minos/current.h ================================================ #ifndef __MINOS_CURRENT_H__ #define __MINOS_CURRENT_H__ #include #include #define current get_current_task() #define current_task_info get_current_task_info() #define current_pid current->pid #define current_tid current->tid #define current_user_regs current->user_regs #endif ================================================ FILE: kernel/include/minos/device_id.h ================================================ #ifndef _MINOS_DEVICE_ID_H_ #define _MINOS_DEVICE_ID_H_ #include #include typedef enum __device_class { DT_CLASS_CPU = 0, DT_CLASS_MEMORY, DT_CLASS_IRQCHIP, DT_CLASS_TIMER, DT_CLASS_SIMPLE_BUS, DT_CLASS_PCI_BUS, DT_CLASS_VDEV, DT_CLASS_PDEV, DT_CLASS_VM, DT_CLASS_VMBOX, DT_CLASS_VIRQCHIP, DT_CLASS_OTHER, } device_class_t; #define DEVICE_NODE_F_ROOT (1 << 0) #define DEVICE_NODE_F_OF (1 << 1) /* * data - the data for all device such as dtb or acpi * offset - node offset * name - the name of the device node * compatible - the compatible used to match device * parent - the parent node of device_node * child - child nodes of the device_node * sibling - brother of the device node */ struct device_node { void *data; int offset; const char *name; const char *compatible; struct device_node *parent; struct device_node *child; struct device_node *sibling; struct device_node *next; device_class_t class; unsigned long flags; }; #define devnode_name(node) node->name struct module_id { const char *name; char **comp; void *data; }; #define MINOS_MODULE_DECLARE(mname, mn, init_fn) \ static const struct module_id __used \ module_match_##mname __section(.__vmodule) = { \ .name = mn, \ .comp = NULL, \ .data = init_fn, \ } #define IRQCHIP_DECLARE(mname, mn, irqchip) \ static const struct module_id __used \ module_match_##mname __section(.__irqchip) = { \ .comp = mn, \ .data = irqchip, \ } #define VIRQCHIP_DECLARE(mname, mn, virqchip) \ static const struct module_id __used \ module_match_##mname __section(.__virqchip) = { \ .comp = mn, \ .data = virqchip, \ } #define VDEV_DECLARE(mname, mn, vdev) \ static const struct module_id __used \ module_match_##mname __section(.__vdev) = { \ .comp = mn, \ .data = vdev, \ } extern char *gicv2_match_table[]; extern char *gicv3_match_table[]; extern char *bcmirq_match_table[]; extern char *pl031_match_table[]; extern char *sp805_match_table[]; extern char *virtio_match_table[]; extern char *aic_match_table[]; extern char *arm_arch_timer_match_table[]; #endif ================================================ FILE: kernel/include/minos/errno.h ================================================ #ifndef _MINOS_ERRNO_H_ #define _MINOS_ERRNO_H_ #define EPERM 1 /* Operation not permitted */ #define ENOENT 2 /* No such file or directory */ #define ESRCH 3 /* No such process */ #define EINTR 4 /* Interrupted system call */ #define EIO 5 /* I/O error */ #define ENXIO 6 /* No such device or address */ #define E2BIG 7 /* Argument list too long */ #define ENOEXEC 8 /* Exec format error */ #define EBADF 9 /* Bad file number */ #define ECHILD 10 /* No child processes */ #define EAGAIN 11 /* Try again */ #define ENOMEM 12 /* Out of memory */ #define EACCES 13 /* Permission denied */ #define EFAULT 14 /* Bad address */ #define ENOTBLK 15 /* Block device required */ #define EBUSY 16 /* Device or resource busy */ #define EEXIST 17 /* File exists */ #define EXDEV 18 /* Cross-device link */ #define ENODEV 19 /* No such device */ #define ENOTDIR 20 /* Not a directory */ #define EISDIR 21 /* Is a directory */ #define EINVAL 22 /* Invalid argument */ #define ENFILE 23 /* File table overflow */ #define EMFILE 24 /* Too many open files */ #define ENOTTY 25 /* Not a typewriter */ #define ETXTBSY 26 /* Text file busy */ #define EFBIG 27 /* File too large */ #define ENOSPC 28 /* No space left on device */ #define ESPIPE 29 /* Illegal seek */ #define EROFS 30 /* Read-only file system */ #define EMLINK 31 /* Too many links */ #define EPIPE 32 /* Broken pipe */ #define EDOM 33 /* Math argument out of domain of func */ #define ERANGE 34 /* Math result not representable */ #define EDEADLK 35 /* Resource deadlock would occur */ #define ENAMETOOLONG 36 /* File name too long */ #define ENOLCK 37 /* No record locks available */ #define ENOSYS 38 /* Function not implemented */ #define ENOTEMPTY 39 /* Directory not empty */ #define ELOOP 40 /* Too many symbolic links encountered */ #define EWOULDBLOCK EAGAIN /* Operation would block */ #define ENOMSG 42 /* No message of desired type */ #define EIDRM 43 /* Identifier removed */ #define ECHRNG 44 /* Channel number out of range */ #define EL2NSYNC 45 /* Level 2 not synchronized */ #define EL3HLT 46 /* Level 3 halted */ #define EL3RST 47 /* Level 3 reset */ #define ELNRNG 48 /* Link number out of range */ #define EUNATCH 49 /* Protocol driver not attached */ #define ENOCSI 50 /* No CSI structure available */ #define EL2HLT 51 /* Level 2 halted */ #define EBADE 52 /* Invalid exchange */ #define EBADR 53 /* Invalid request descriptor */ #define EXFULL 54 /* Exchange full */ #define ENOANO 55 /* No anode */ #define EBADRQC 56 /* Invalid request code */ #define EBADSLT 57 /* Invalid slot */ #define EDEADLOCK EDEADLK #define EBFONT 59 /* Bad font file format */ #define ENOSTR 60 /* Device not a stream */ #define ENODATA 61 /* No data available */ #define ETIME 62 /* Timer expired */ #define ENOSR 63 /* Out of streams resources */ #define ENONET 64 /* Machine is not on the network */ #define ENOPKG 65 /* Package not installed */ #define EREMOTE 66 /* Object is remote */ #define ENOLINK 67 /* Link has been severed */ #define EADV 68 /* Advertise error */ #define ESRMNT 69 /* Srmount error */ #define ECOMM 70 /* Communication error on send */ #define EPROTO 71 /* Protocol error */ #define EMULTIHOP 72 /* Multihop attempted */ #define EDOTDOT 73 /* RFS specific error */ #define EBADMSG 74 /* Not a data message */ #define EOVERFLOW 75 /* Value too large for defined data type */ #define ENOTUNIQ 76 /* Name not unique on network */ #define EBADFD 77 /* File descriptor in bad state */ #define EREMCHG 78 /* Remote address changed */ #define ELIBACC 79 /* Can not access a needed shared library */ #define ELIBBAD 80 /* Accessing a corrupted shared library */ #define ELIBSCN 81 /* .lib section in a.out corrupted */ #define ELIBMAX 82 /* Attempting to link in too many shared libraries */ #define ELIBEXEC 83 /* Cannot exec a shared library directly */ #define EILSEQ 84 /* Illegal byte sequence */ #define ERESTART 85 /* Interrupted system call should be restarted */ #define ESTRPIPE 86 /* Streams pipe error */ #define EUSERS 87 /* Too many users */ #define ENOTSOCK 88 /* Socket operation on non-socket */ #define EDESTADDRREQ 89 /* Destination address required */ #define EMSGSIZE 90 /* Message too long */ #define EPROTOTYPE 91 /* Protocol wrong type for socket */ #define ENOPROTOOPT 92 /* Protocol not available */ #define EPROTONOSUPPORT 93 /* Protocol not supported */ #define ESOCKTNOSUPPORT 94 /* Socket type not supported */ #define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ #define EPFNOSUPPORT 96 /* Protocol family not supported */ #define EAFNOSUPPORT 97 /* Address family not supported by protocol */ #define EADDRINUSE 98 /* Address already in use */ #define EADDRNOTAVAIL 99 /* Cannot assign requested address */ #define ENETDOWN 100 /* Network is down */ #define ENETUNREACH 101 /* Network is unreachable */ #define ENETRESET 102 /* Network dropped connection because of reset */ #define ECONNABORTED 103 /* Software caused connection abort */ #define ECONNRESET 104 /* Connection reset by peer */ #define ENOBUFS 105 /* No buffer space available */ #define EISCONN 106 /* Transport endpoint is already connected */ #define ENOTCONN 107 /* Transport endpoint is not connected */ #define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ #define ETOOMANYREFS 109 /* Too many references: cannot splice */ #define ETIMEDOUT 110 /* Connection timed out */ #define ECONNREFUSED 111 /* Connection refused */ #define EHOSTDOWN 112 /* Host is down */ #define EHOSTUNREACH 113 /* No route to host */ #define EALREADY 114 /* Operation already in progress */ #define EINPROGRESS 115 /* Operation now in progress */ #define ESTALE 116 /* Stale NFS file handle */ #define EUCLEAN 117 /* Structure needs cleaning */ #define ENOTNAM 118 /* Not a XENIX named type file */ #define ENAVAIL 119 /* No XENIX semaphores available */ #define EISNAM 120 /* Is a named type file */ #define EREMOTEIO 121 /* Remote I/O error */ #define EDQUOT 122 /* Quota exceeded */ #define ENOMEDIUM 123 /* No medium found */ #define EMEDIUMTYPE 124 /* Wrong medium type */ #define ECANCELED 125 /* Operation Canceled */ #define ENOKEY 126 /* Required key not available */ #define EKEYEXPIRED 127 /* Key has expired */ #define EKEYREVOKED 128 /* Key has been revoked */ #define EKEYREJECTED 129 /* Key was rejected by service */ /* for robust mutexes */ #define EOWNERDEAD 130 /* Owner died */ #define ENOTRECOVERABLE 131 /* State not recoverable */ #define ERFKILL 132 /* Operation not possible due to RF-kill */ #define EHWPOISON 133 /* Memory page has hardware error */ #define EABORT 134 /* Pend abort process is killed */ #define EOTHERSIDECLOSED 135 /* the otherside of this kobject is closed */ #endif ================================================ FILE: kernel/include/minos/event.h ================================================ #ifndef __MINOS_EVENT_H__ #define __MINOS_EVENT_H__ #include #include #include #define OS_EVENT_OPT_NONE 0x0 #define OS_EVENT_OPT_BROADCAST 0x1 #define WAKEUP_ALL (-1) enum { OS_EVENT_TYPE_NORMAL, OS_EVENT_TYPE_MBOX, OS_EVENT_TYPE_QUEUE, OS_EVENT_TYPE_SEM, OS_EVENT_TYPE_MUTEX, OS_EVENT_TYPE_FLAG, OS_EVENT_TYPE_FUTEX, OS_EVENT_TYPE_IRQ, OS_EVENT_TYPE_POLL, OS_EVENT_TYPE_MAX, OS_EVENT_TYPE_TIMER, }; struct task; struct event { int type; /* event type */ tid_t owner; /* event owner the tid */ uint32_t cnt; /* event cnt */ void *data; /* event pdata for transfer */ spinlock_t lock; /* the lock of the event for smp */ struct list_head wait_list; /* non realtime task waitting list */ }; #define TO_EVENT(e) (struct event *)(e) uint32_t new_event_token(void); void event_init(struct event *event, int type, void *pdata); void event_pend_down(void); void __wait_event(void *ev, int event, uint32_t to); long wake(struct event *ev, long retcode); long do_wait_event(struct event *ev); int __wake_up_event_waiter(struct event *ev, long msg, int pend_state, int num); struct task *wake_up_one_event_waiter(struct event *ev, long msg, int pend_state); #define wake_up_event_waiter(ev, msg, pend_state, num) \ __wake_up_event_waiter(TO_EVENT(ev), msg, pend_state, num) /* * wait_event can only get the status of the event, can not get * the retcode from the waker, so the retcode of the waker need * store in otherwhere. */ #define wait_event(ev, condition, _to) \ ({ \ __label__ __out1; \ unsigned long flags; \ long __ret = 0; \ \ if (condition) \ goto __out1; \ \ if (is_task_need_stop(current)) { \ __ret = -EABORT; \ goto __out1; \ } \ \ spin_lock_irqsave(&(ev)->lock, flags); \ if (condition) { \ spin_unlock_irqrestore(&(ev)->lock, flags); \ goto __out1; \ } else if ((_to) == 0) { \ __ret = -EBUSY; \ spin_unlock_irqrestore(&(ev)->lock, flags); \ goto __out1; \ } \ \ __wait_event(ev, OS_EVENT_TYPE_NORMAL, _to); \ spin_unlock_irqrestore(&(ev)->lock, flags); \ __ret = do_wait_event(ev); \ \ __out1: __ret; \ }) long __wake(struct event *ev, int pend_state, long retcode); #define wake(ev, retcode) __wake(ev, TASK_STATE_PEND_OK, retcode) #define wake_abort(ev) __wake(ev, TASK_STATE_PEND_ABORT, -EABORT) #define wake_timeout(ev) __wake(ev, TASK_STATE_PEND_TO, -ETIMEDOUT) #endif ================================================ FILE: kernel/include/minos/flag.h ================================================ #ifndef __MINOS_FLAG_H__ #define __MINOS_FLAG_H__ #include #include #define FLAG_WAIT_CLR_ALL 0 #define FLAG_WAIT_CLR_AND 0 #define FLAG_WAIT_CLR_ANY 1 #define FLAG_WAIT_CLR_OR 1 #define FLAG_WAIT_SET_ALL 2 #define FLAG_WAIT_SET_AND 2 #define FLAG_WAIT_SET_ANY 3 #define FLAG_WAIT_SET_OR 3 #define FLAG_CONSUME 0x80 #define FLAG_CLR 0 #define FLAG_SET 1 struct flag_grp { flag_t flags; spinlock_t lock; struct list_head wait_list; }; static void inline flag_init(struct flag_grp *fg, flag_t flags) { fg->flags = flags; init_list(&fg->wait_list); spin_lock_init(&fg->lock); } flag_t flag_accept(struct flag_grp *grp, flag_t flags, int wait_type); flag_t flag_pend(struct flag_grp *grp, flag_t flags, int wait_type, uint32_t timeout); flag_t flag_pend_get_flags_ready(void); flag_t flag_post(struct flag_grp *grp, flag_t flags, int opt); #define flag_set(grp, flags) flag_post(grp, flags, FLAG_SET) #define flag_clear(grp, flags) flag_post(grp, flags, FLAG_CLEAR) #endif ================================================ FILE: kernel/include/minos/hook.h ================================================ #ifndef __MINOS_HOOK_H__ #define __MINOS_HOOK_H__ #include typedef int (*hook_func_t)(void *item, void *contex); enum hook_type { OS_HOOK_CREATE_TASK = 0, OS_HOOK_RELEASE_TASK, OS_HOOK_TASK_SWITCH, OS_HOOK_TASK_SWITCH_OUT, OS_HOOK_TASK_SWITCH_TO, OS_HOOK_ENTER_IRQ, #ifdef CONFIG_VIRT OS_HOOK_EXIT_FROM_GUEST, OS_HOOK_ENTER_TO_GUEST, OS_HOOK_CREATE_VM, OS_HOOK_DESTROY_VM, OS_HOOK_SUSPEND_VM, OS_HOOK_RESUME_VM, OS_HOOK_SETUP_VM, OS_HOOK_VCPU_INIT, #endif OS_HOOK_TYPE_UNKNOWN, }; struct hook { hook_func_t fn; struct list_head list; }; int do_hooks(void *item, void *context, enum hook_type type); int register_hook(hook_func_t fn, enum hook_type type); #endif ================================================ FILE: kernel/include/minos/init.h ================================================ #ifndef _INIT_H #define _INIT_H #define INIT_PATH_SIZE 128 #define CMDLINE_TAG "xxoo" #define ARCH_NAME_SIZE 8 #define BOARD_NAME_SIZE 16 #define MEM_MAX_REGION 16 struct cmdline { unsigned long tag; unsigned long head; char arg[256]; }; typedef int (*init_call)(void); #define __init_data __section(".__init_data_section") #define __init_text __section(".__init_text") #define __init_0 __section(".__init_func_0") #define __init_1 __section(".__init_func_1") #define __init_2 __section(".__init_func_2") #define __init_3 __section(".__init_func_3") #define __init_4 __section(".__init_func_4") #define __init_5 __section(".__init_func_5") #define __init_6 __section(".__init_func_6") #define __init_7 __section(".__init_func_7") #define __init_8 __section(".__init_func_8") #define __init_9 __section(".__init_func_9") #define __define_initcall(fn, id) \ static init_call __init_call_##fn __used __init_##id = fn #define early_initcall(fn) __define_initcall(fn, 0) #define arch_initcall(fn) __define_initcall(fn, 1) #define subsys_initcall(fn) __define_initcall(fn, 2) #define module_initcall(fn) __define_initcall(fn, 3) #define device_initcall(fn) __define_initcall(fn, 4) #define early_initcall_percpu(fn) __define_initcall(fn, 5) #define arch_initcall_percpu(fn) __define_initcall(fn, 6) #define subsys_initcall_percpu(fn) __define_initcall(fn, 7) #define module_initcall_percpu(fn) __define_initcall(fn, 8) #define device_initcall_percpu(fn) __define_initcall(fn, 9) void arch_init(void); void early_init(void); void subsys_init(void); void module_init(void); void device_init(void); void early_init_percpu(void); void arch_init_percpu(void); void subsys_init_percpu(void); void module_init_percpu(void); void device_init_percpu(void); #endif ================================================ FILE: kernel/include/minos/irq.h ================================================ #ifndef _MINOS_IRQ_H_ #define _MINOS_IRQ_H_ #include #include #include #include #include #include #include #include #define NR_PERCPU_IRQS (CONFIG_NR_PPI_IRQS + CONFIG_NR_SGI_IRQS) #define PERCPU_IRQ_DESC_SIZE (NR_PERCPU_IRQS * NR_CPUS) #define SPI_IRQ_DESC_SIZE CONFIG_NR_SPI_IRQS #define SPI_IRQ_BASE NR_PERCPU_IRQS #define MAX_IRQ_COUNT (CONFIG_NR_SPI_IRQS + NR_PERCPU_IRQS) #define BAD_IRQ (1023) #define IRQ_FLAGS_NONE (0x00000000) #define IRQ_FLAGS_EDGE_RISING (0x00000001) #define IRQ_FLAGS_EDGE_FALLING (0x00000002) #define IRQ_FLAGS_LEVEL_HIGH (0x00000004) #define IRQ_FLAGS_LEVEL_LOW (0x00000008) #define IRQ_FLAGS_SENSE_MASK (0x0000000f) #define IRQ_FLAGS_INVALID (0x00000010) #define IRQ_FLAGS_EDGE_BOTH \ (IRQ_FLAGS_EDGE_FALLING | IRQ_FLAGS_EDGE_RISING) #define IRQ_FLAGS_LEVEL_BOTH \ (IRQ_FLAGS_LEVEL_LOW | IRQ_FLAGS_LEVEL_HIGH) #define IRQ_FLAGS_TYPE_MASK (0x000000ff) #define IRQ_FLAGS_MASKED_BIT (8) #define IRQ_FLAGS_MASKED (BIT(IRQ_FLAGS_MASKED_BIT)) #define IRQ_FLAGS_PERCPU_BIT (9) #define IRQ_FLAGS_PERCPU (BIT(IRQ_FLAGS_PERCPU_BIT)) #define IRQ_FLAGS_VCPU_BIT (10) #define IRQ_FLAGS_VCPU (BIT(IRQ_FLAGS_VCPU_BIT)) #define IRQ_FLAGS_USER_BIT (11) #define IRQ_FLAGS_USER (BIT(IRQ_FLAGS_USER_BIT)) #define IRQ_FLAGS_RECEIVER_MASK (IRQ_FLAGS_USER | IRQ_FLAGS_VCPU) #define IRQ_FLAGS_PENDING_BIT (12) typedef enum sgi_mode { SGI_TO_LIST = 0, SGI_TO_OTHERS, SGI_TO_SELF, } sgi_mode_t; struct irq_desc; struct virq_desc; struct kobject; struct poll_event_kernel; typedef int (*irq_handle_t)(uint32_t irq, void *data); struct irq_chip { uint32_t (*get_pending_irq)(void); void (*irq_mask)(uint32_t irq); void (*irq_mask_cpu)(uint32_t irq, int cpu); void (*irq_unmask_cpu)(uint32_t irq, int cpu); void (*irq_unmask)(uint32_t irq); void (*irq_eoi)(uint32_t irq); void (*irq_dir)(uint32_t irq); int (*irq_set_affinity)(uint32_t irq, uint32_t pcpu); int (*irq_set_type)(uint32_t irq, unsigned int flow_type); int (*irq_set_priority)(uint32_t irq, uint32_t pr); void (*irq_clear_pending)(uint32_t irq); int (*irq_xlate)(struct device_node *node, uint32_t *intspec, unsigned int intsize, uint32_t *hwirq, unsigned long *f); void (*send_sgi)(uint32_t irq, enum sgi_mode mode, cpumask_t *mask); int (*init)(struct device_node *node); int (*secondary_init)(void); }; /* * if a irq is handled by minos, then need to register * the irq handler otherwise it will return the vnum * to the handler and pass the virq to the vm */ struct irq_desc { irq_handle_t handler; uint16_t hno; uint16_t affinity; unsigned long flags; spinlock_t lock; unsigned long irq_count; void *pdata; }; #define local_irq_enable() arch_enable_local_irq() #define local_irq_disable() arch_disable_local_irq() #define irq_disabled() arch_irq_disabled() int irq_init(void); int irq_secondary_init(void); void setup_irqs(void); int do_irq_handler(void); int request_irq(uint32_t irq, irq_handle_t handler, unsigned long flags, char *name, void *data); int request_irq_percpu(uint32_t irq, irq_handle_t handler, unsigned long flags, char *name, void *data); void send_sgi(uint32_t sgi, int cpu); void irq_set_affinity(uint32_t irq, int cpu); void irq_set_type(uint32_t irq, int type); void irq_clear_pending(uint32_t irq); int irq_xlate(struct device_node *node, uint32_t *intspec, unsigned int intsize, uint32_t *hwirq, unsigned long *f); struct irq_desc *get_irq_desc(uint32_t irq); void __irq_enable(uint32_t irq, int enable); void irq_dir(uint32_t irq); static inline void irq_unmask(uint32_t irq) { __irq_enable(irq, 1); } static inline void irq_mask(uint32_t irq) { __irq_enable(irq, 0); } #endif ================================================ FILE: kernel/include/minos/kbuild.h ================================================ #ifndef __MINOS_KBUILD_H__ #define __MINOS_KBUILD_H__ #define DEFINE(sym, val) \ asm volatile("\n->" #sym " %0 " #val : : "i" (val)) #endif ================================================ FILE: kernel/include/minos/list.h ================================================ #ifndef _MINOS_LIST_H_ #define _MINOS_LIST_H_ struct list_head; struct list_head{ struct list_head *next, *pre; }; #define LIST_HEAD(list) \ struct list_head list = { \ .next = &list, \ .pre = &list, \ } static void inline init_list(struct list_head *list) { list->next = list; list->pre = list; } static void inline list_add(struct list_head *head, struct list_head *new) { head->next->pre = new; new->next = head->next; new->pre = head; head->next = new; } static void inline list_add_tail(struct list_head *head, struct list_head *new) { head->pre->next = new; new->next = head; new->pre = head->pre; head->pre = new; } static void inline list_insert_before(struct list_head *head, struct list_head *new) { new->pre = head->pre; new->next = head; head->pre->next = new; head->pre = new; } static void inline list_del(struct list_head *list) { list->next->pre = list->pre; list->pre->next = list->next; list->next = (void *)0x0; list->pre = (void *)0x0; } static void inline list_del_tail(struct list_head *head) { head->pre->pre->next = head; head->pre = head->pre->pre; } static int inline is_list_empty(struct list_head *head) { return (head->next == head); } static int inline is_list_last(struct list_head *head, struct list_head *list) { return list->next == head; } static inline struct list_head *list_next(struct list_head *list) { return list->next; } static inline struct list_head *list_prve(struct list_head *list) { return list->pre; } #define list_entry(ptr, type, member) \ container_of(ptr, type, member) #define list_first_entry(ptr, type, member) \ list_entry((ptr)->next, type, member) #define list_next_entry(pos, member) \ list_entry((pos)->member.next, typeof(*(pos)), member) #define list_for_each(head, list) \ for(list = (head)->next; list != (head); list = list->next) #define list_for_each_entry(pos, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member); \ &pos->member != (head); \ pos = list_entry(pos->member.next, typeof(*pos), member)) #define list_for_each_entry_safe(pos, n, head, member) \ for (pos = list_first_entry(head, typeof(*pos), member), \ n = list_next_entry(pos, member); \ &pos->member != (head); \ pos = n, n = list_next_entry(n, member)) #endif ================================================ FILE: kernel/include/minos/math64.h ================================================ #ifndef _LINUX_MATH64_H #define _LINUX_MATH64_H #include #include #if BITS_PER_LONG == 64 #define div64_long(x, y) div64_s64((x), (y)) #define div64_ul(x, y) div64_u64((x), (y)) /** * div_u64_rem - unsigned 64bit divide with 32bit divisor with remainder * * This is commonly provided by 32bit archs to provide an optimized 64bit * divide. */ static inline u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder) { *remainder = dividend % divisor; return dividend / divisor; } /** * div_s64_rem - signed 64bit divide with 32bit divisor with remainder */ static inline s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder) { *remainder = dividend % divisor; return dividend / divisor; } /** * div64_u64 - unsigned 64bit divide with 64bit divisor */ static inline u64 div64_u64(u64 dividend, u64 divisor) { return dividend / divisor; } /** * div64_s64 - signed 64bit divide with 64bit divisor */ static inline s64 div64_s64(s64 dividend, s64 divisor) { return dividend / divisor; } #elif BITS_PER_LONG == 32 #define div64_long(x, y) div_s64((x), (y)) #define div64_ul(x, y) div_u64((x), (y)) #ifndef div_u64_rem static inline u64 div_u64_rem(u64 dividend, u32 divisor, u32 *remainder) { *remainder = do_div(dividend, divisor); return dividend; } #endif #ifndef div_s64_rem extern s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder); #endif #ifndef div64_u64 extern u64 div64_u64(u64 dividend, u64 divisor); #endif #ifndef div64_s64 extern s64 div64_s64(s64 dividend, s64 divisor); #endif #endif /* BITS_PER_LONG */ /** * div_u64 - unsigned 64bit divide with 32bit divisor * * This is the most common 64bit divide and should be used if possible, * as many 32bit archs can optimize this variant better than a full 64bit * divide. */ #ifndef div_u64 static inline u64 div_u64(u64 dividend, u32 divisor) { u32 remainder; return div_u64_rem(dividend, divisor, &remainder); } #endif /** * div_s64 - signed 64bit divide with 32bit divisor */ #ifndef div_s64 static inline s64 div_s64(s64 dividend, s32 divisor) { s32 remainder; return div_s64_rem(dividend, divisor, &remainder); } #endif u32 iter_div_u64_rem(u64 dividend, u32 divisor, u64 *remainder); static inline u32 __iter_div_u64_rem(u64 dividend, u32 divisor, u64 *remainder) { u32 ret = 0; while (dividend >= divisor) { /* The following asm() prevents the compiler from optimising this loop into a modulo operation. */ asm("" : "+rm"(dividend)); dividend -= divisor; ret++; } *remainder = dividend; return ret; } #endif /* _LINUX_MATH64_H */ ================================================ FILE: kernel/include/minos/mbox.h ================================================ #ifndef __MINOS_MBOX_H__ #define __MINOS_MBOX_H__ #include typedef struct event mbox_t; #define DEFINE_MBOX(nam) \ mbox_t name = { \ .type = 0xff, \ } void *mbox_accept(mbox_t *m); void *mbox_pend(mbox_t *m, uint32_t timeout); int mbox_post(mbox_t *m, void *pmsg); int mbox_post_opt(mbox_t *m, void *pmsg, int opt); int mbox_is_pending(mbox_t *m); static void inline mbox_init(mbox_t *mbox, void *pmsg) { event_init(TO_EVENT(mbox), OS_EVENT_TYPE_MBOX, pmsg); } #endif ================================================ FILE: kernel/include/minos/memattr.h ================================================ #ifndef __MINOS_MEM_ATTR_H__ #define __MINOS_MEM_ATTR_H__ #define VM_NONE (0x00000000) #define __VM_IO (0x00000001) /* IO memory */ #define __VM_NORMAL (0x00000002) /* Normal memory */ #define __VM_NORMAL_NC (0x00000004) #define __VM_WT (0x00000008) /* Write thought */ #define __VM_PFNMAP (0x00000100) /* map to the physical normal memory directly */ #define __VM_HUGE_2M (0x00000200) #define __VM_HUGE_1G (0x00000400) #define __VM_DEVMAP (0x00000800) #define __VM_SHARED (0x00001000) /* do not release the memory, kobject will release it */ #define __VM_HOST (0x00002000) #define __VM_GUEST (0x00004000) #define __VM_SHMEM (0x00008000) /* prviate memory, will not be shared */ #define __VM_PMA (0x00010000) #define __VM_RW_NON (0x00000000) #define __VM_READ (0x00100000) #define __VM_WRITE (0x00200000) #define __VM_EXEC (0x00400000) #define __VM_RO (__VM_READ) #define __VM_WO (__VM_WRITE) #define __VM_RW (__VM_READ | __VM_WRITE) #define VM_TYPE_MASK (__VM_IO | __VM_NORMAL | __VM_NORMAL_NC | __VM_WT) #define VM_HOST (__VM_HOST) #define VM_GUEST (__VM_GUEST) #define VM_RW_NON (__VM_RW_NON) #define VM_RO (__VM_RO) #define VM_WO (__VM_WO) #define VM_RW (__VM_READ | __VM_WRITE) #define VM_RWX (__VM_READ | __VM_WRITE | __VM_EXEC) #define VM_RW_MASK (__VM_READ | __VM_WRITE) #define VM_IO (__VM_IO | __VM_DEVMAP | __VM_PFNMAP) #define VM_NORMAL (__VM_NORMAL) #define VM_NORMAL_NC (__VM_NORMAL_NC) #define VM_NORMAL_WT (__VM_WT) #define VM_DMA (__VM_NORMAL_NC) #define VM_HUGE (__VM_HUGE_2M) #define VM_SHARED (__VM_SHARED) #define VM_SHMEM (__VM_SHMEM) #define VM_PFNMAP (__VM_PFNMAP) #define VM_DEVMAP (__VM_DEVMAP) #define VM_PMA (__VM_PMA) #define VM_MAP_BK (0X01000000) /* mapped as block */ #define VM_MAP_PT (0x02000000) /* mapped as pass though, PFN_MAP */ #define VM_MAP_TYPE_MASK (0x0f000000) #define VM_HOST_NORMAL (VM_NORMAL | VM_PFNMAP | VM_HOST) #define VM_HOST_NORMAL_NC (__VM_NORMAL_NC | VM_PFNMAP | VM_HOST) #define VM_HOST_IO (VM_IO | VM_HOST) #define VM_GUEST_NORMAL (VM_NORMAL | VM_RWX | VM_MAP_BK | VM_GUEST) #define VM_GUEST_IO (VM_IO | VM_MAP_PT | VM_DEVMAP | VM_GUEST) /* passthough device for guest VM */ #define VM_GUEST_VDEV (VM_GUEST) /* virtual device created by host for guest VM, memory R/W will trapped */ #define VM_GUEST_SHMEM (VM_NORMAL_NC | VM_SHMEM | VM_GUEST) /* shared memory between guests, memory will managemented by host */ #define VM_NATIVE_NORMAL (VM_NORMAL | VM_RWX | VM_PFNMAP | VM_HUGE | VM_GUEST) /* native VM using the memory config from device tree */ #define MEM_BLOCK_SIZE (0x200000) #define MEM_BLOCK_SHIFT (21) #define BFN2PHY(bfn) ((unsigned long)(bfn) << MEM_BLOCK_SHIFT) #define PHY2BFN(phy) ((unsigned long)(phy) >> MEM_BLOCK_SHIFT) #define PAGES_IN_BLOCK (MEM_BLOCK_SIZE >> PAGE_SHIFT) #define __PAGE_MASK (~((1UL << PAGE_SHIFT) - 1)) #define __BLOCK_MASK (~((1UL << MEM_BLOCK_SHIFT) - 1)) #define BLOCK_MASK ((1 << MEM_BLOCK_SHIFT) - 1) #define GFB_SLAB (1 << 0) #define GFB_PAGE (1 << 1) #define GPF_PAGE_META (1 << 2) #define GFB_VM (1 << 3) #define GFB_FIXED (1 << 5) #define GFB_SLAB_BIT (0) #define GFB_PAGE_BIT (1) #define GFB_PAGE_META_BIT (2) #define GFB_VM_BIT (3) #define GFB_IO_BIT (4) #define GFB_FIXED_BIT (5) #define GFB_MASK (0xffff) #endif ================================================ FILE: kernel/include/minos/memory.h ================================================ #ifndef __MINOS_MEMORY_H__ #define __MINOS_MEMORY_H__ #include #include enum { MEMORY_REGION_TYPE_NORMAL = 0, MEMORY_REGION_TYPE_RSV, MEMORY_REGION_TYPE_VM, MEMORY_REGION_TYPE_DTB, MEMORY_REGION_TYPE_KERNEL, MEMORY_REGION_TYPE_RAMDISK, MEMORY_REGION_TYPE_MAX }; extern unsigned long minos_start; extern unsigned long minos_bootmem_base; extern unsigned long minos_stack_top; extern unsigned long minos_stack_bottom; extern unsigned long minos_end; extern struct list_head mem_list; #define for_each_memory_region(region) \ list_for_each_entry(region, &mem_list, list) struct memory_region { int type; int vmid; // 0 is host phy_addr_t phy_base; size_t size; struct list_head list; }; int add_memory_region(uint64_t base, uint64_t size, int flags, int vmid); int split_memory_region(uint64_t base, size_t size, int flags, int vmid); int add_page_section(phy_addr_t base, size_t size, int type); void add_kmem_section(struct memory_region *region); void dump_memory_info(void); #endif ================================================ FILE: kernel/include/minos/minos.h ================================================ #ifndef _MINOS_MINOS_H_ #define _MINOS_MINOS_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define section_for_each_item_addr(__start_addr, __end_addr, __var) \ size_t _i, _cnt; \ unsigned long _base, _end; \ _base = __start_addr; \ _end = __end_addr; \ _cnt = (_end - _base) / sizeof(*(__var)); \ __var = (__typeof__(__var))(_base); \ for (_i = 0; _i < _cnt; ++_i, ++(__var)) #define section_for_each_item(__start, __end, __var) \ section_for_each_item_addr((unsigned long)&(__start), \ (unsigned long)&(__end), __var) #define WARN(condition, format...) ({ \ int __ret_warn_on = !!(condition); \ if (unlikely(__ret_warn_on)) \ pr_warn(format); \ unlikely(__ret_warn_on); \ }) #define WARN_ONCE(condition, format...) ({ \ static bool __section(.data.unlikely) __warned; \ int __ret_warn_once = !!(condition); \ \ if (unlikely(__ret_warn_once)) \ if (WARN(!__warned, format)) \ __warned = true; \ unlikely(__ret_warn_once); \ }) #endif ================================================ FILE: kernel/include/minos/mm.h ================================================ #ifndef _MINOS_MM_H_ #define _MINOS_MM_H_ #include #include #include #include #include #include struct vspace; struct mm_notifier_ops { void (*unmap_range)(struct vspace *vspace, unsigned long start, unsigned long end, int flags); }; struct vspace { pgd_t *pgdp; spinlock_t lock; uint16_t asid; /* * indicate that the vspace is used in kernel, means * kernel is acess the userspace pagees, so do not release * the pages if unmap to avoid kernel hang or data breach. */ atomic_t inuse; struct page *release_pages; struct mm_notifier_ops *notifier_ops; void *pdata; }; void release_vspace_pages(struct vspace *vs); int create_host_mapping(unsigned long vir, unsigned long phy, size_t size, unsigned long flags); int destroy_host_mapping(unsigned long vir, size_t size); int change_host_mapping(unsigned long vir, unsigned long phy, unsigned long new_flags); void *io_remap(virt_addr_t vir, size_t size); int io_unmap(virt_addr_t vir, size_t size); #endif ================================================ FILE: kernel/include/minos/mutex.h ================================================ #ifndef __MINOS_MUTEX_H__ #define __MINOS_MUTEX_H__ #include typedef struct event mutex_t; #define OS_MUTEX_AVAILABLE (-1) #define DEFINE_MUTEX(name) \ mutex_t name = { \ .type = OS_EVENT_TYPE_MUTEX, \ .owner = 0, \ .cnt = OS_MUTEX_AVAILABLE, \ .data = NULL, \ .lock = {0, 0}, \ .wait_list = { \ .prev = &wait_list, \ .next = &wait_list, \ } \ } mutex_t *mutex_create(char *name); int mutex_accept(mutex_t *mutex); int mutex_del(mutex_t *mutex, int opt); int mutex_pend(mutex_t *m, uint32_t timeout); int mutex_post(mutex_t *m); static void inline mutex_init(mutex_t *mutex) { event_init(TO_EVENT(mutex), OS_EVENT_TYPE_MUTEX, NULL); mutex->cnt = OS_MUTEX_AVAILABLE; } #endif ================================================ FILE: kernel/include/minos/of.h ================================================ #ifndef __MINOS_OF_H__ #define __MINOS_OF_H__ #include #include typedef fdt16_t of16_t; typedef fdt32_t of32_t; typedef fdt64_t of64_t; #define MAX_DTB_SIZE (MEM_BLOCK_SIZE) #define OF_MAX_ADDR_CELLS 4 #define OF_BAD_ADDR ((u64)-1) typedef void * (*of_iterate_fn)(struct device_node *, void *arg); extern struct device_node *of_root_node; extern void *dtb_address; #define of_node_for_each_child(node, child) \ for (child = node->child; child != NULL; child = child->sibling) static fdt32_t inline cpu_to_of32(uint32_t v) { return cpu_to_fdt32(v); } static uint32_t inline of16_to_cpu(of16_t v) { return fdt32_to_cpu((fdt16_t)v); } static uint32_t inline of32_to_cpu(of32_t v) { return fdt32_to_cpu((fdt32_t)v); } static uint64_t inline of32_to_cpu64(of32_t high, of32_t low) { return ((uint64_t)fdt32_to_cpu((fdt32_t)high) << 32) | fdt32_to_cpu((fdt32_t)low); } int __of_get_u64_array(void *, int, char *, uint64_t *, int); int __of_get_u32_array(void *, int, char *, uint32_t *, int); int __of_get_u16_array(void *, int, char *, uint16_t *, int); int __of_get_string(void *, int, char *, char *, int); int __of_get_bool(void *dtb, int node, char *attr); char *of_get_cmdline(void *dtb); int of_get_bool(struct device_node *node, char *attr); void *of_getprop(struct device_node *node, char *attr, int *len); int of_get_node_by_name(void *data, int pnode, char *str); const char *__of_get_compatible(void *dtb, int node); int of_device_match(struct device_node *node, char **comp); void *of_iterate_all_node_loop(struct device_node *node, of_iterate_fn func, void *arg); void *of_iterate_all_node(struct device_node *node, of_iterate_fn func, void *arg); struct device_node * of_find_node_by_compatible(struct device_node *root, char **comp); int of_n_addr_cells(struct device_node *node); int of_n_size_cells(struct device_node *node); int of_n_interrupt_cells(struct device_node *node); int of_n_addr_count(struct device_node *node); int of_data(void *data); int of_translate_address_index(struct device_node *node, uint64_t *address, uint64_t *size, int index); int of_translate_address(struct device_node *node, uint64_t *address, uint64_t *size); struct device_node *of_parse_device_tree(void *); void of_release_all_node(struct device_node *node); void *of_device_node_match(struct device_node *node, void *s, void *e); int of_get_phandle(struct device_node *node); struct device_node * of_find_node_by_name(struct device_node *root, char *name); int fdt_n_size_cells(void *dtb, int node); int fdt_n_addr_cells(void *dtb, int node); static inline int of_get_u64_array(struct device_node *node, char *attr, uint64_t *array, int len) { if (!node || !attr || !array) return -EINVAL; return __of_get_u64_array(node->data, node->offset, attr, array, len); } static inline int of_get_u32_array(struct device_node *node, char *attr, uint32_t *array, int len) { if (!node || !attr || !array) return -EINVAL; return __of_get_u32_array(node->data, node->offset, attr, array, len); } static inline int of_get_u16_array(struct device_node *node, char *attr, uint16_t *array, int len) { if (!node || !attr || !array) return -EINVAL; return __of_get_u16_array(node->data, node->offset, attr, array, len); } static inline int of_get_string(struct device_node *node, char *attr, char *str, int len) { if (!node || !attr || !str) return -EINVAL; return __of_get_string(node->data, node->offset, attr, str, len); } static int inline device_node_is_root(struct device_node *node) { return (node->parent == NULL); } static int inline translate_device_address_index(struct device_node *node, uint64_t *base, uint64_t *size, int index) { if (node->flags & DEVICE_NODE_F_OF) return of_translate_address_index(node, base, size, index); return -EINVAL; } static inline int translate_device_address(struct device_node *node, uint64_t *base, uint64_t *size) { return translate_device_address_index(node, base, size, 0); } int get_device_irq_index(struct device_node *node, uint32_t *irq, unsigned long *flags, int index); int of_get_console_name(char **name); int of_init_bootargs(void); int of_init(void *dtb); void of_setup_platform(void); int of_get_ramdisk_address(unsigned long *start, unsigned long *end); int of_spin_table_init(phy_addr_t *smp_holding); int of_parse_memory_info(void); #endif ================================================ FILE: kernel/include/minos/os.h ================================================ #ifndef __MINOS_OS_H__ #define __MINOS_OS_H__ #include #include #include #include #include static inline int os_is_running(void) { return get_pcpu()->os_is_running; } static inline void set_os_running(void) { /* * os running is set before the irq is enable * so do not need to aquire lock or disable the * interrupt here */ get_pcpu()->os_is_running = 1; wmb(); } #endif ================================================ FILE: kernel/include/minos/page.h ================================================ #ifndef __MINOS_PAGE_H__ #define __MINOS_PAGE_H__ #include #include #include #define __GFP_KERNEL 0x00000001 #define __GFP_USER 0x00000002 #define __GFP_GUEST 0x00000004 #define __GFP_DMA 0x00000008 #define __GFP_SHARED 0x00000010 #define __GFP_SLAB 0x00000020 #define __GFP_HUGE 0x00000040 #define __GFP_IO 0x00000080 #define GFP_KERNEL __GFP_KERNEL #define GFP_USER __GFP_USER #define GFP_GUEST __GFP_GUEST #define GFP_DMA __GFP_DMA #define GFP_SLAB __GFP_SLAB #define GFP_SHARED __GFP_SHARED #define GFP_SHARED_IO (__GFP_SHARED | __GFP_IO) #define GFP_HUGE (__GFP_USER | __GFP_HUGE) #define GFP_HUGE_IO (__GFP_USER | __GFP_HUGE | __GFP_IO) struct page { uint16_t cnt; uint16_t flags; uint32_t pfn; // this need make sure the physical range need smaller than 44BITs struct page *next; } __packed; #define page_count(page) (page)->cnt #define page_pa(page) ((page)->pfn << PAGE_SHIFT) #define page_va(page) ptov(page_pa(page)) #define page_flags(page) (page)->flags int free_pages(void *addr); struct page *addr_to_page(unsigned long addr); void *__get_free_pages(int pages, int align, int flags); void *get_free_block(unsigned long flags); void free_block(void *addr); void page_init(void); void *get_io_pages(int pages); void free_io_pages(void *addr); int __free_pages(struct page *page); struct page *__alloc_pages(int pages, int align, int flags); static inline struct page *alloc_pages(int pages, int flags) { return __alloc_pages(pages, 1, flags); } static inline void *get_free_page(int flags) { return __get_free_pages(1, 1, flags); } static inline void *get_free_pages(int pages, int flags) { // if (flags & __VM_IO) // return get_io_pages(pages); // else return __get_free_pages(pages, 1, flags); } #define get_static_pages(pages) alloc_kpages(pages) #endif ================================================ FILE: kernel/include/minos/percpu.h ================================================ #ifndef _MINOS_PERCPU_H_ #define _MINOS_PERCPU_H_ #include #include #include #include #include typedef enum { PCPU_STATE_OFFLINE = 0x0, PCPU_STATE_RUNNING, PCPU_STATE_IDLE, } pcpu_state_t; struct task; struct pcpu { int pcpu_id; // fixed place, do not change. volatile int state; void *stack; unsigned long percpu_offset; /* * each pcpu has its local sched list, 8 priority * local_rdy_grp only use [0 - 8], in these 8 * priority: * 7 - used for idle task * 6 - used for vcpu task * * only the new_list can be changed by other cpu, the * lock is for the new_list. */ spinlock_t lock; struct list_head new_list; struct list_head die_process; struct list_head stop_list; struct task *running_task; struct task *idle_task; uint32_t nr_pcpu_task; uint8_t local_rdy_grp; uint8_t padding[3]; struct list_head ready_list[OS_PRIO_MAX]; int tasks_in_prio[OS_PRIO_MAX]; struct timer sched_timer; int os_is_running; struct task *kworker; struct flag_grp kworker_flag; } __cache_line_align; extern unsigned long percpu_offset[]; extern struct pcpu pcpus[NR_CPUS]; void percpu_init(int cpuid); static inline int smp_processor_id(void) { return ((struct pcpu *)arch_get_pcpu_data())->pcpu_id; } #define DEFINE_PER_CPU(type, name) \ __section(".__percpu") __typeof__(type) per_cpu_##name #define DECLARE_PER_CPU(type, name) \ extern __section(".__percpu") __typeof__(type) per_cpu_##name #define get_per_cpu(name, cpu) \ (*((__typeof__(per_cpu_##name)*)((unsigned char*)&per_cpu_##name - percpu_offset[0] + percpu_offset[cpu]))) #define get_cpu_var(name) get_per_cpu(name, smp_processor_id()) #define get_pcpu() ((struct pcpu *)arch_get_pcpu_data()) #define __get_pcpu() \ ({ \ preempt_disable(); \ ((struct pcpu *)arch_get_pcpu_data()); \ }) #define __put_pcpu(pcpu) \ ({ \ preempt_enable(); \ }) #define get_cpu_data(name) \ ({ \ preempt_disable(); \ get_cpu_var(name); \ }) #define put_cpu_data(name) \ ({ \ preempt_enable(); \ }) #define get_cpu() \ ({ \ preempt_disable(); \ smp_processor_id(); \ }) \ #define put_cpu() \ ({ \ preempt_enable(); \ }) #endif ================================================ FILE: kernel/include/minos/platform.h ================================================ #ifndef __MINOS_PLATFORM_H__ #define __MINOS_PLATFORM_H__ #include #ifdef CONFIG_VIRT struct vm; #endif struct platform { const char *name; int (*cpu_on)(unsigned long cpu, unsigned long entry); int (*cpu_off)(unsigned long cpu); void (*system_reboot)(int mode, const char *cmd); void (*system_shutdown)(void); int (*system_suspend)(void); int (*time_init)(void); int (*iomem_valid)(unsigned long addr); #ifdef CONFIG_VIRT int (*setup_hvm)(struct vm *vm, void *data); #endif int (*platform_init)(void); void (*parse_mem_info)(void); }; extern struct platform *platform; #define DEFINE_PLATFORM(pl) \ static struct platform *__platform__##pl __used \ __section(".__platform") = &pl void platform_set_to(const char *name); int platform_iomem_valid(unsigned long addr); #endif ================================================ FILE: kernel/include/minos/pm.h ================================================ #ifndef _MINOS_PM_H_ #define _MINOS_PM_H_ void cpu_idle(void); void system_reboot(void); void system_shutdown(void); int system_suspend(void); #endif ================================================ FILE: kernel/include/minos/preempt.h ================================================ #ifndef __MINOS_PREEMPT_H__ #define __MINOS_PREEMPT_H__ #include extern void cond_resched(void); static inline int preempt_allowed(void) { return !get_current_task_info()->preempt_count; } static inline void preempt_enable(void) { get_current_task_info()->preempt_count--; wmb(); cond_resched(); } static void inline preempt_disable(void) { get_current_task_info()->preempt_count++; wmb(); } #endif ================================================ FILE: kernel/include/minos/print.h ================================================ #ifndef _MINOS_PRINT_H_ #define _MINOS_PRINT_H_ #include #define PRINT_LEVEL_FATAL 0 #define PRINT_LEVEL_ERROR 1 #define PRINT_LEVEL_WARN 2 #define PRINT_LEVEL_NOTICE 3 #define PRINT_LEVEL_INFO 4 #define PRINT_LEVEL_DEBUG 5 int level_print(int level, char *fmt, ...); void change_log_level(unsigned int level); int printf(char *fmt, ...); int puts(char *buf, size_t size); #define pr_debug(...) level_print(PRINT_LEVEL_DEBUG, "DBG " __VA_ARGS__) #define pr_info(...) level_print(PRINT_LEVEL_INFO, "INF " __VA_ARGS__) #define pr_notice(...) level_print(PRINT_LEVEL_NOTICE,"NIC " __VA_ARGS__) #define pr_warn(...) level_print(PRINT_LEVEL_WARN, "WRN " __VA_ARGS__) #define pr_err(...) level_print(PRINT_LEVEL_ERROR, "ERR " __VA_ARGS__) #define pr_fatal(...) level_print(PRINT_LEVEL_FATAL, "FAT " __VA_ARGS__) #endif ================================================ FILE: kernel/include/minos/queue.h ================================================ #ifndef __MINOS_QUEUE_H__ #define __MINOS_QUEUE_H__ #include typedef struct event queue_t; struct queue { void **q_start; /* contain the pointer of the data */ void **q_end; /* end of the queue buffer */ void **q_in; /* next message in */ void **q_out; /* next message out */ int q_size; /* the total size of the queue */ int q_cnt; /* current queue entriy size */ }; int queue_init(queue_t *qt, int size, char *name); void *queue_accept(queue_t *qt); int queue_flush(queue_t *qt); void *queue_pend(queue_t *qt, uint32_t timeout); int queue_post_abort(queue_t *qt, int opt); int queue_post(queue_t *qt, void *pmsg); int queue_post_front(queue_t *qt, void *pmsg); int queue_post_opt(queue_t *qt, int opt, void *pmsg); #endif ================================================ FILE: kernel/include/minos/ramdisk.h ================================================ #ifndef __MINOS_RAMDISK_H__ #define __MINOS_RAMDISK_H__ #include extern void *ramdisk_start, *ramdisk_end; void set_ramdisk_address(void *start, void *end); int ramdisk_init(void); int ramdisk_read(struct ramdisk_file *file, void *buf, size_t size, unsigned long offset); int ramdisk_open(char *name, struct ramdisk_file *file); unsigned long ramdisk_file_base(struct ramdisk_file *file); unsigned long ramdisk_file_size(struct ramdisk_file *file); const char *ramdisk_file_name(struct ramdisk_file *file); #endif ================================================ FILE: kernel/include/minos/raw_spinlock.h ================================================ /* * Created by Le Min 2017/1212 */ #ifndef __MINOS_RAW_SPINLOCK_H__ #define __MINOS_RAW_SPINLOCK_H__ #include #include #include #ifdef CONFIG_SMP void arch_ticket_lock(spinlock_t *lock); void arch_ticket_unlock(spinlock_t *lock); int arch_ticket_trylock(spinlock_t *lock); #define DEFINE_SPIN_LOCK(name) \ spinlock_t name = { \ .current_ticket = 0, \ .next_ticket = 0, \ } static void inline spin_lock_init(spinlock_t *lock) { lock->current_ticket = 0; lock->next_ticket = 0; } static void inline raw_spin_lock(spinlock_t *lock) { arch_ticket_lock(lock); } static void inline raw_spin_unlock(spinlock_t *lock) { arch_ticket_unlock(lock); } static int inline raw_spin_trylock(spinlock_t *lock) { return arch_ticket_trylock(lock); } #else #define DEFINE_SPIN_LOCK(name) spinlock_t name static void inline spin_lock_init(spinlock_t *lock) { } static void inline raw_spin_lock(spinlock_t *lock) { } static void inline raw_spin_unlock(spinlock_t *lock) { } static void inline raw_spin_trylock(spinlock_t *lock) { return 1; } #endif #endif ================================================ FILE: kernel/include/minos/sched.h ================================================ #ifndef _MINOS_SCHED_H_ #define _MINOS_SCHED_H_ #include #include #include #include DECLARE_PER_CPU(struct pcpu *, pcpu); struct process; void sched(void); void cond_resched(void); int sched_init(void); int local_sched_init(void); void pcpu_resched(int pcpu_id); void pcpu_irqwork(int pcpu_id); void task_sleep(uint32_t ms); int task_ready(struct task *task, int preempt); void __might_sleep(const char *file, int line, int preempt_offset); int __wake_up(struct task *task, long pend_state, unsigned long data); static inline int wake_up(struct task *task, long retcode) { return __wake_up(task, TASK_STATE_PEND_OK, (unsigned long)retcode); } static inline int wake_up_timeout(struct task *task) { return __wake_up(task, TASK_STATE_PEND_TO, -ETIMEDOUT); } static inline int wake_up_abort(struct task *task) { return __wake_up(task, TASK_STATE_PEND_ABORT, -EABORT); } #define might_sleep() \ do { \ __might_sleep(__FILE__, __LINE__, 0); \ } while (0) #endif ================================================ FILE: kernel/include/minos/sem.h ================================================ #ifndef __MINOS_SEM_H__ #define __MINOS_SEM_H__ #include typedef struct event sem_t; #define DEFINE_SEMAPHORE(name) \ sem_t name = { \ .type = 0xff, \ } uint32_t sem_accept(sem_t *sem); int sem_pend(sem_t *sem, uint32_t timeout); int sem_pend_abort(sem_t *sem, int opt); int sem_post(sem_t *sem); static void inline sem_init(sem_t *sem, uint32_t cnt) { event_init(TO_EVENT(sem), OS_EVENT_TYPE_SEM, NULL); sem->cnt = cnt; } #endif ================================================ FILE: kernel/include/minos/shell_command.h ================================================ #ifndef __MINOS_COMMAND_H__ #define __MINOS_COMMAND_H__ #include struct shell_command { int min_args; char *name; char *cmd_info; int (*hdl)(int argc, char **argv); }; #define DEFINE_SHELL_COMMAND(a_name, c_name, c_cmd_info, c_hdl, args) \ static struct shell_command __used \ shell_command_##a_name __section(.__shell_command) = { \ .min_args = args, \ .name = c_name, \ .cmd_info = c_cmd_info, \ .hdl = c_hdl, \ } int excute_shell_command(int argc, char **argv); int shell_task(void *data); #endif ================================================ FILE: kernel/include/minos/slab.h ================================================ #ifndef __MINOS_SLAB_H__ #define __MINOS_SLAB_H__ #include void *malloc(size_t size); void *zalloc(size_t size); void free(void *addr); #endif ================================================ FILE: kernel/include/minos/smp.h ================================================ #ifndef _MINOS_SMP_H_ #define _MINOS_SMP_H_ #include #include #include #include #include #define for_all_cpu(cpu) \ for (cpu = 0; cpu < NR_CPUS; cpu++) extern cpumask_t cpu_online; #define for_each_online_cpu(cpu) for_each_cpu(cpu, &cpu_online) typedef void (*smp_function)(void *); void smp_cpus_up(void); int is_cpus_all_up(void); void smp_init(void); int smp_function_call(int cpu, smp_function fn, void *data, int wait); #endif ================================================ FILE: kernel/include/minos/softirq.h ================================================ #ifndef _MINOS_SOFTIRQ_H_ #define _MINOS_SOFTIRQ_H_ /* * refer to the linux kernel softirq code */ enum { TIMER_SOFTIRQ, VCPULET_SOFTIRQ, SCHED_SOFTIRQ, NR_SOFTIRQS }; struct softirq_action { void (*action)(struct softirq_action *); }; void do_softirq(void); void open_softirq(int nr, void (*action)(struct softirq_action *)); void softirq_init(void); void raise_softirq_irqoff(unsigned int nr); void raise_softirq(unsigned int nr); void irq_softirq_exit(void); #endif ================================================ FILE: kernel/include/minos/spinlock.h ================================================ /* * Created by Le Min 2017/1212 */ #ifndef __MINOS_SPINLOCK_H__ #define __MINOS_SPINLOCK_H__ #include #include #ifdef CONFIG_SMP #define spin_lock(l) \ do { \ preempt_disable(); \ raw_spin_lock(l); \ } while (0) #define spin_unlock(l) \ do { \ raw_spin_unlock(l); \ preempt_enable(); \ } while (0) #define spin_trylock(l) \ ({ \ preempt_disable(); \ raw_spin_trylock(l); \ }) #define spin_lock_irqsave(l, flags) \ do { \ preempt_disable(); \ flags = arch_save_irqflags(); \ arch_disable_local_irq(); \ raw_spin_lock(l); \ } while (0) #define spin_trylock_irqsave(l, flags) \ ({ \ int ret; \ preempt_disable(); \ flags = arch_save_irqflags(); \ arch_disable_local_irq(); \ ret = raw_spin_trylock(l); \ if (!ret) { \ arch_restore_irqflags(flags); \ preempt_enable(); \ } \ ret; \ }) #define spin_unlock_irqrestore(l, flags) \ do { \ raw_spin_unlock(l); \ arch_restore_irqflags(flags); \ preempt_enable(); \ } while (0) #else #define spin_lock(l) preempt_disable() #define spin_unlock(l) preempt_enable() #define spin_trylock(l) raw_spin_trylock() #define spin_lock_irqsave(l, flags) local_irq_save(flags) #define spin_trylock_irqsave(l, flags) \ ({ \ local_irqsave(flags); \ raw_spin_trylock(l); \ }) #define spin_lock_irqrestore(l, flags) local_irqrestore(flags); #endif #endif ================================================ FILE: kernel/include/minos/stdlib.h ================================================ #ifndef _MINOS_STDLIB_H_ #define _MINOS_STDLIB_H_ #include #include #include #ifndef CONFIG_X86_64 #define mod_64(x, y) ((x) - (y) * div64_u64(x, y)) #else #define mod_64(x, y) ((x) % (y)) #endif uint64_t muldiv64(u64 a, u32 b, u32 c); #endif ================================================ FILE: kernel/include/minos/string.h ================================================ #ifndef _STRING_H #define _STRING_H #include #include long absolute(long num); long num_to_str(char *buf, unsigned long num, int bdho); long itoa(char *buf, int num); long ltoa(char *buf, long num); long uitoa(char *buf, unsigned int num); long ultoa(char *buf, unsigned long num); long hextoa(char *buf, unsigned long num); long octtoa(char *buf, unsigned long num); long bintoa(char *buf, unsigned long num); size_t strlen(const char *s); char *strcpy(char *des, const char *src); char *strncpy(char *des, const char *src, int len); int strcmp(const char *src, const char *dst); int memcmp(const char *src, const char *dst, size_t size); int strncmp(const char *src, const char *dst, int n); char *strchr(const char *src, char ch); void memset(void *base, char ch, int size); void *memmove(void *dest, const void *src, size_t n); size_t strnlen(const char *s, size_t maxlen); void *memcpy(void *dest, const void *src, size_t n); void *memchr(const void *s, int c, size_t n); int vsprintf(char *buf, const char *fmt, va_list arg); int sprintf(char *str, const char *format, ...); char *strrchr(const char *s, int c); unsigned long strtoul(const char *cp, char **endp, unsigned int base); char *strsep(char **stringp, const char *delim); static inline int is_digit(char ch) { return ((ch <= '9') && (ch >= '0')); } static inline int isalpha(char ch) { return (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z'))); } #define atoi(str) strtoul((str), NULL, 10) #endif ================================================ FILE: kernel/include/minos/symbol.h ================================================ #ifndef __MINOS_SYMBOL_H__ #define __MINOS_SYMBOL_H__ #define EXPORT_SYMBOL(sym) #endif ================================================ FILE: kernel/include/minos/task.h ================================================ #ifndef __MINOS_TASK_H__ #define __MINOS_TASK_H__ #include #include #include #include #define to_task_info(task) (&(task)->ti) #ifdef CONFIG_TASK_RUN_TIME #define TASK_RUN_TIME CONFIG_TASK_RUN_TIME #else #define TASK_RUN_TIME 50 #endif static int inline task_is_idle(struct task *task) { return (task->flags & TASK_FLAGS_IDLE); } static inline int get_task_tid(struct task *task) { return task->tid; } static inline uint8_t get_task_prio(struct task *task) { return task->prio; } static inline int task_is_suspend(struct task *task) { return !!(task->state & TASK_STATE_WAIT_EVENT); } static inline int task_is_running(struct task *task) { return (task->state == TASK_STATE_RUNNING); } static inline int task_is_vcpu(struct task *task) { return (task->flags & TASK_FLAGS_VCPU); } static inline int task_is_32bit(struct task *task) { return (task->flags & TASK_FLAGS_32BIT); } static inline void task_set_resched(struct task *task) { task->ti.flags |= TIF_NEED_RESCHED; } static inline void task_clear_resched(struct task *task) { task->ti.flags &= ~TIF_NEED_RESCHED; } static inline int task_need_resched(struct task *task) { return (task->ti.flags & TIF_NEED_RESCHED); } static inline void task_need_stop(struct task *task) { set_bit(TIF_NEED_STOP, &task->ti.flags); smp_wmb(); } static inline void task_need_freeze(struct task *task) { set_bit(TIF_NEED_FREEZE, &task->ti.flags); smp_wmb(); } static inline int is_task_need_stop(struct task *task) { return !!(task->ti.flags & (__TIF_NEED_FREEZE | __TIF_NEED_STOP)); } #define task_state_pend_ok(status) \ ((status) == TASK_STATE_PEND_OK) #define task_state_pend_timeout(status) \ ((status) == TASK_STATE_PEND_TO) #define task_state_pend_abort(status) \ ((status) == TASK_STATE_PEND_ABORT) /* * set current running task's state do not need to obtain * a lock, when need to wakeup the task, below state the state * can be changed: * 1 - running -> wait_event * 2 - wait_event -> running (waked up by event) * 3 - new -> running * 4 - running -> stopped */ #define set_current_state(_state, to) \ do { \ current->state = (_state); \ current->delay = (to); \ smp_mb(); \ } while (0) void do_release_task(struct task *task); struct task *create_task(char *name, task_func_t func, size_t stk_size, void *usp, int prio, int aff, unsigned long opt, void *arg); struct task *create_vcpu_task(char *name, task_func_t func, int aff, unsigned long flags, void *vcpu); struct task *create_kthread(char *name, task_func_t func, int prio, int aff, unsigned long opt, void *arg); void os_for_all_task(void (*hdl)(struct task *task)); void task_die(void); void task_suspend(void); #endif ================================================ FILE: kernel/include/minos/task_def.h ================================================ #ifndef __TASK_DEF_H__ #define __TASK_DEF_H__ #include #include #include #include #include #include #ifdef CONFIG_TASK_STACK_SIZE #define TASK_STACK_SIZE CONFIG_TASK_STACK_SIZE #else #define TASK_STACK_SIZE (2 * PAGE_SIZE) #endif #ifndef CONFIG_NR_TASKS #define CONFIG_NR_TASKS 256 #endif #define OS_NR_TASKS CONFIG_NR_TASKS #define OS_PRIO_MAX 8 #define OS_PRIO_DEFAULT_0 0 #define OS_PRIO_DEFAULT_1 1 #define OS_PRIO_DEFAULT_2 2 #define OS_PRIO_DEFAULT_3 3 #define OS_PRIO_DEFAULT_4 4 #define OS_PRIO_DEFAULT_5 5 #define OS_PRIO_DEFAULT_6 6 #define OS_PRIO_DEFAULT_7 7 #define OS_PRIO_REALTIME OS_PRIO_DEFAULT_0 #define OS_PRIO_SRV OS_PRIO_DEFAULT_2 #define OS_PRIO_SYSTEM OS_PRIO_DEFAULT_3 #define OS_PRIO_VCPU OS_PRIO_DEFAULT_4 #define OS_PRIO_DEFAULT OS_PRIO_DEFAULT_5 #define OS_PRIO_IDLE OS_PRIO_DEFAULT_7 #define OS_PRIO_LOWEST OS_PRIO_IDLE #define TASK_FLAGS_SRV BIT(0) // should not change, need keep same as pangu #define TASK_FLAGS_DRV BIT(1) #define TASK_FLAGS_VCPU BIT(2) #define TASK_FLAGS_REALTIME BIT(3) #define TASK_FLAGS_IDLE BIT(4) #define TASK_FLAGS_NO_AUTO_START BIT(5) #define TASK_FLAGS_32BIT BIT(6) #define TASK_FLAGS_PERCPU BIT(7) #define TASK_FLAGS_DEDICATED_HEAP BIT(8) #define TASK_FLAGS_ROOT BIT(9) #define TASK_FLAGS_KERNEL BIT(10) #define TASK_AFF_ANY (-1) #define TASK_NAME_SIZE (32) #define TASK_STATE_RUNNING 0x00 #define TASK_STATE_READY 0x01 #define TASK_STATE_WAIT_EVENT 0x02 #define TASK_STATE_WAKING 0x04 #define TASK_STATE_SUSPEND 0x08 #define TASK_STATE_STOP 0x10 #define KWORKER_FLAG_MASK 0xffff #define KWORKER_TASK_RECYCLE BIT(0) #define TASK_STATE_PEND_OK 0u /* Pending status OK, not pending, or pending complete */ #define TASK_STATE_PEND_TO 1u /* Pending timed out */ #define TASK_STATE_PEND_ABORT 2u /* Pending aborted */ #define KWORKER_FLAG_MASK 0xffff #define KWORKER_TASK_RECYCLE BIT(0) #define TASK_WAIT_FOREVER (0xfffffffe) typedef int (*task_func_t)(void *data); struct vspace; struct task { struct task_info ti; void *stack_base; void *stack_top; void *stack_bottom; gp_regs *user_regs; unsigned long flags; int pid; int tid; struct list_head list; struct list_head proc_list; struct list_head task_list; // link to the task list, if is a thread. struct list_head state_list; // link to the sched list used for sched. uint32_t delay; struct timer delay_timer; /* * the next task belongs to the same process */ struct task *next; /* * the spinlock will use to protect the below member * which may modified by different cpu at the same * time: * 1 - state * 2 - pend_state */ spinlock_t s_lock; int state; long pend_state; int wait_type; // which event is task waitting for. void *wait_event; // the event instance which the task is waitting. struct list_head event_list; struct flag_node *flag_node; // used for the flag event. union { unsigned long ipcdata; long retcode; void *msg; long flags_rdy; }; /* * affinity - the cpu node which the task affinity to */ int cpu; int last_cpu; int affinity; int prio; unsigned long run_time; unsigned long ctx_sw_cnt; // switch count of this task. unsigned long start_ns; // when the task started last time. char name[TASK_NAME_SIZE]; void (*exit_from_user)(struct task *task, gp_regs *regs); void (*return_to_user)(struct task *task, gp_regs *regs); struct vspace *vs; // the virtual memory space of this task. void *pdata; // the private data of this task for vcpu or process. struct cpu_context cpu_context; } __cache_line_align; #define OS_TASK_RESERVED ((struct task *)1) #endif ================================================ FILE: kernel/include/minos/task_info.h ================================================ #ifndef __MINOS_TASK_INFO_H__ #define __MINOS_TASK_INFO_H__ #include #define TIF_NEED_RESCHED 0 #define TIF_32BIT 1 #define TIF_DONOT_PREEMPT 2 #define TIF_TICK_EXHAUST 3 #define TIF_IN_USER 4 #define TIF_HARDIRQ_MASK 8 #define TIF_SOFTIRQ_MASK 9 #define TIF_NEED_STOP 10 #define TIF_NEED_FREEZE 11 #define TIF_WAIT_INTERRUPTED 12 #define __TIF_NEED_RESCHED (UL(1) << TIF_NEED_RESCHED) #define __TIF_32BIT (UL(1) << TIF_32BIT) #define __TIF_DONOT_PREEMPT (UL(1) << TIF_DONOT_PREEMPT) #define __TIF_TICK_EXHAUST (UL(1) << TIF_TICK_EXHAUST) #define __TIF_IN_USER (UL(1) << TIF_IN_USER) #define __TIF_HARDIRQ_MASK (UL(1) << TIF_HARDIRQ_MASK) #define __TIF_SOFTIRQ_MASK (UL(1) << TIF_SOFTIRQ_MASK) #define __TIF_NEED_STOP (UL(1) << TIF_NEED_STOP) #define __TIF_NEED_FREEZE (UL(1) << TIF_NEED_FREEZE) // only used for VCPU. #define __TIF_WAIT_INTERRUPTED (UL(1) << TIF_WAIT_INTERRUPTED) #define __TIF_IN_INTERRUPT (__TIF_HARDIRQ_MASK | __TIF_SOFTIRQ_MASK) #ifndef __ASSEMBLY__ #include #include #include /* * this task_info is stored at the top of the task's * stack */ struct task_info { int preempt_count; unsigned long flags; }; static inline struct task *get_current_task(void) { return (struct task *)asm_get_current_task(); } static inline struct task_info *get_current_task_info(void) { return (struct task_info *)asm_get_current_task_info(); } static inline void set_current_task(struct task *task) { asm_set_current_task(task); } static inline void set_need_resched(void) { get_current_task_info()->flags |= __TIF_NEED_RESCHED; wmb(); } static inline void clear_need_resched(void) { get_current_task_info()->flags &= ~__TIF_NEED_RESCHED; wmb(); } static inline void clear_do_not_preempt(void) { get_current_task_info()->flags &= ~__TIF_DONOT_PREEMPT; wmb(); } static inline int need_resched(void) { return !!(get_current_task_info()->flags & __TIF_NEED_RESCHED); } static inline void do_not_preempt(void) { get_current_task_info()->flags |= __TIF_DONOT_PREEMPT; wmb(); } static inline int in_interrupt(void) { return (get_current_task_info()->flags & __TIF_IN_INTERRUPT); } #endif #endif ================================================ FILE: kernel/include/minos/time.h ================================================ #ifndef _MINOS_TIME_H_ #define _MINOS_TIME_H_ #include #include #define NOW() get_sys_time() #define SYSTEM_TIME_HZ 1000000000ULL #define SECONDS(s) ((uint64_t)((s) * 1000000000ULL)) #define MILLISECS(ms) ((uint64_t)((ms) * 1000000ULL)) #define MICROSECS(us) ((uint64_t)((us) * 1000ULL)) #define CLOCK_REALTIME 0 #define CLOCK_MONOTONIC 1 #define CLOCK_PROCESS_CPUTIME_ID 2 #define CLOCK_THREAD_CPUTIME_ID 3 #define CLOCK_MONOTONIC_RAW 4 #define CLOCK_REALTIME_COARSE 5 #define CLOCK_MONOTONIC_COARSE 6 #define CLOCK_BOOTTIME 7 #define CLOCK_REALTIME_ALARM 8 #define CLOCK_BOOTTIME_ALARM 9 #define CLOCK_SGI_CYCLE 10 #define CLOCK_TAI 11 struct timespec { long tv_sec; long tv_nsec; }; static inline unsigned long ticks_to_ns(uint64_t ticks) { return muldiv64(ticks, SECONDS(1), 1000 * cpu_khz); } static inline uint64_t ns_to_ticks(unsigned long ns) { return muldiv64(ns, 1000 * cpu_khz, SECONDS(1)); } static inline void enable_timer(unsigned long e) { arch_enable_timer(e); } void udelay(uint32_t us); void mdelay(uint32_t ms); void msleep(uint32_t ms); #endif ================================================ FILE: kernel/include/minos/timer.h ================================================ #ifndef _MINOS_TIMER_H_ #define _MINOS_TIMER_H_ /* * refer to linux kernel timer code */ #include #include #include typedef void (*timer_func_t)(unsigned long); struct timer { int cpu; int stop; uint64_t expires; uint64_t timeout; timer_func_t function; unsigned long data; struct list_head entry; struct raw_timer *raw_timer; }; /* * raw timer is a hardware timer which use to * handle timer request. */ struct raw_timer { struct list_head active; struct timer *next_timer; struct timer *running_timer; spinlock_t lock; }; void init_timer(struct timer *timer, timer_func_t fn, unsigned long data); int start_timer(struct timer *timer); int stop_timer(struct timer *timer); int read_timer(struct timer *timer); void setup_timer(struct timer *timer, uint64_t tval); void setup_and_start_timer(struct timer *timer, uint64_t tval); int mod_timer(struct timer *timer, uint64_t cval); #endif ================================================ FILE: kernel/include/minos/tty.h ================================================ #ifndef __MINOS__TTY_H__ #define __MINOS_TTY_H__ #include #include struct tty; #define TTY_NAME_SIZE 16 struct tty_ops { int (*put_char)(struct tty *tty, char ch); int (*put_chars)(struct tty *tty, char *str, int count); int (*open)(struct tty *tty); void (*close)(struct tty *tty); }; struct tty { char name[TTY_NAME_SIZE]; uint32_t id; int open; int flags; struct tty_ops *ops; void *pdata; struct list_head list; }; struct tty *alloc_tty(char *name, uint32_t id, int flags); int register_tty(struct tty *tty); int release_tty(struct tty *tty); void close_tty(struct tty *tty); struct tty *open_tty(char *name); #endif ================================================ FILE: kernel/include/minos/types.h ================================================ #ifndef _MINOS_TYPES_H_ #define _MINOS_TYPES_H_ #include #include #include #include #include typedef __u32 u32; typedef __s32 s32; typedef __u16 u16; typedef __s16 s16; typedef __u8 u8; typedef __s8 s8; typedef __u64 u64; typedef __s64 s64; typedef __u32 uint32_t; typedef __s32 int32_t; typedef __u16 uint16_t; typedef __s16 int16_t; typedef __u8 uint8_t; typedef __s8 int8_t; typedef __u64 uint64_t; typedef __s64 int64_t; typedef long ssize_t; typedef unsigned long size_t; typedef unsigned long phy_addr_t; typedef unsigned long virt_addr_t; typedef unsigned long paddr_t; typedef unsigned long vaddr_t; typedef int16_t handle_t; typedef uint32_t right_t; typedef int16_t tid_t; typedef int16_t pid_t; typedef unsigned long uintptr_t; typedef int irqreturn_t; typedef int bool; typedef uint64_t pgd_t; typedef uint64_t pud_t; typedef uint64_t pmd_t; typedef uint64_t pte_t; enum { false = 0, true = 1, }; #define FILENAME_MAX 256 #define ULONG(v) ((unsigned long)(v)) #define MAX(a, b) (a) > (b) ? (a) : (b) #define MIN(a, b) (a) < (b) ? (a) : (b) #define max(a, b) (a) > (b) ? (a) : (b) #define min(a, b) (a) < (b) ? (a) : (b) #define ERROR_PTR(value) \ (void *)(unsigned long)(value) #define IS_ERROR_PTR(ptr) \ (((long)(ptr)) > -4096 && ((long)(ptr) < 4096)) #define u8_to_u16(low, high) ((high << 8) | low) #define u8_to_u32(u1, u2, u3, u4) \ ((u4 << 24) | (u3 << 16) | (u2 << 8) | (u1)) #define u16_to_u32(low, high) ((high << 16) | low) #define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER) #define NULL ((void *)0) typedef void (*void_func_t)(void); #define container_of(ptr, name, member) \ (name *)((unsigned char *)ptr - ((unsigned char *)&(((name *)0)->member))) #define BIT(nr) (ULONG(1) << (nr)) #define stringify_no_expansion(x) #x #define stringify(x) stringify_no_expansion(x) #define SIZE_1G (0x40000000UL) #define SIZE_4K (0x1000) #define SIZE_1M (0x100000) #define SIZE_1K (0x400) #define SIZE_16K (16 * SIZE_1K) #define SIZE_32M (32 * SIZE_1M) #define SIZE_64K (64 * SIZE_1K) #define SIZE_512M (512 * SIZE_1M) #define SIZE_2M (2 * 1024 * 1024) #define SIZE_8M (8 * 1024 * 1024) #define PAGE_SIZE (SIZE_4K) #define PAGE_SHIFT (12) #define PAGE_MASK (0xfffUL) #define BLOCK_SIZE (0x200000) #define BLOCK_SHIFT (21) #define PAGES_PER_BLOCK (BLOCK_SIZE >> PAGE_SHIFT) #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) #define INVALID_ADDR 0 #define BITS_PER_BYTE (8) #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) #define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) #define DECLARE_BITMAP(name, bits) \ unsigned long name[BITS_TO_LONGS(bits)] #define BITMAP_SIZE(size) (BITS_TO_LONGS((size)) * sizeof(long)) #define BITS_PER_LONG 64 #define BIT_ULL(nr) (1ULL << (nr)) #define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) #define BIT_WORD(nr) ((nr) / BITS_PER_LONG) #define BIT_ULL_MASK(nr) (1ULL << ((nr) % BITS_PER_LONG_LONG)) #define BIT_ULL_WORD(nr) ((nr) / BITS_PER_LONG_LONG) #define __round_mask(x, y) ((__typeof__(x))((y)-1)) #define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1) #define round_down(x, y) ((x) & ~__round_mask(x, y)) #define ALIGN(x, y) ((x) & ~__round_mask(x, y)) #define BALIGN(x, y) (((x) + (y) - 1) & ~__round_mask(x, y)) #define PAGE_BALIGN(x) BALIGN((unsigned long)(x), PAGE_SIZE) #define PAGE_ALIGN(x) ALIGN((unsigned long)(x), PAGE_SIZE) #define BLOCK_BALIGN(x) BALIGN((unsigned long)(x), BLOCK_SIZE) #define BLOCK_ALIGN(x) ALIGN((unsigned long)(x), BLOCK_SIZE) #define PAGE_NR(size) (PAGE_BALIGN(size) >> PAGE_SHIFT) #define IS_PAGE_ALIGN(x) (!((unsigned long)(x) & (PAGE_SIZE - 1))) #define IS_BLOCK_ALIGN(x) (!((unsigned long)(x) & (0x1fffff))) #define __IN_RANGE_UNSIGNED(sbase, ssize, dbase, dsize) \ (((sbase) >= (dbase)) && (((sbase) + (ssize) - 1) < ((dbase) + (dsize)))) #define IN_RANGE_UNSIGNED(sbase, ssize, dbase, dsize) \ __IN_RANGE_UNSIGNED((unsigned long)(sbase), (unsigned long)(ssize), (unsigned long)(dbase), (size_t)(dsize)) #define __stringify_1(x...) #x #define __stringify(x...) __stringify_1(x) #define GENMASK(h, l) \ (((~0UL) << (l)) & (~0UL >> (BITS_PER_LONG - 1 - (h)))) #define GENMASK_ULL(h, l) \ (((~0ULL) << (l)) & (~0ULL >> (BITS_PER_LONG - 1 - (h)))) #define INT_MAX (2147483647) #define INT_MIN (-2147483648) #define BUG() \ while (1) #define NR_CPUS CONFIG_NR_CPUS #define BAD_ADDRESS (-1) #define OS_PRIO_MAX 8 extern int8_t const ffs_one_table[256]; typedef uint32_t flag_t; typedef struct { int value; } atomic_t; /* * [0 - 15] - current number * [16 - 31] - next number */ typedef struct spinlock { #ifdef CONFIG_SMP int current_ticket; int next_ticket; #endif } spinlock_t; static inline void __write_once_size(volatile void *p, void *res, int size) { switch (size) { case 1: *(volatile uint8_t *)p = *(uint8_t *)res; break; case 2: *(volatile uint16_t *)p = *(uint16_t *)res; break; case 4: *(volatile uint32_t *)p = *(uint32_t *)res; break; case 8: *(volatile uint64_t *)p = *(uint64_t *)res; break; default: barrier(); __builtin_memcpy((void *)p, (const void *)res, size); barrier(); } } #define WRITE_ONCE(x, val) \ ({ \ union { typeof(x) __val; char __c[1]; } __u = \ { .__val = (typeof(x)) (val) }; \ __write_once_size(&(x), __u.__c, sizeof(x)); \ __u.__val; \ }) #endif ================================================ FILE: kernel/include/minos/varlist.h ================================================ #ifndef _MINOS_VARLIST_H_ #define _MINOS_VARLIST_H_ #include /* * offset(n) get the offset align of the stack for different arch * va_start(ap,n) to get the second var of the function in the stack * va_end just a indicate of the va_list operation ending. * va_arg(ap,type) get the value of the var, */ #if 0 typedef char *va_list; #define addr(n) (&n) #define offset(n) \ ((sizeof(n) + sizeof(unsigned long) - 1) & ~(sizeof(unsigned long) - 1)) #define va_start(ap, n) \ ((ap) = ((char *)(&n)) + offset(n)) #define va_end(ap) (void)0 #define va_arg(ap, type) \ (*(type *)((ap) += offset(type), (ap) - offset(type))) #define va_start(ap, last) \ __builtin_va_start((ap), (last)) #define va_arg(ap, type) \ __builtin_va_arg((ap), type) #define __va_copy(dest, src) \ __builtin_va_copy((dest), (src)) #endif #endif ================================================ FILE: kernel/include/uapi/kobject_uapi.h ================================================ #ifndef __MINOS_KOBJECT_UAPI_H__ #define __MINOS_KOBJECT_UAPI_H__ #define KOBJ_RIGHT_NONE 0x0000 // do not have any right. #define KOBJ_RIGHT_READ 0x0001 // can read this kobject, usually for IPC between two process. #define KOBJ_RIGHT_WRITE 0x0002 // can write this kobject, usually for IPC between two process. #define KOBJ_RIGHT_EXEC 0x0004 // can be exectued. #define KOBJ_RIGHT_MMAP 0x0008 // can be mapped to process address space #define KOBJ_RIGHT_CTL 0x0010 // can call kobject_ctl for this kobject #define KOBJ_RIGHT_MASK 0x001f #define KOBJ_RIGHT_RW (KOBJ_RIGHT_READ | KOBJ_RIGHT_WRITE) #define KOBJ_RIGHT_RO (KOBJ_RIGHT_READ) #define KOBJ_RIGHT_WO (KOBJ_RIGHT_WRITE) #define KOBJ_RIGHT_RWX (KOBJ_RIGHT_RW | KOBJ_RIGHT_EXEC) enum { KOBJ_TYPE_NONE, KOBJ_TYPE_PROCESS, // process, can be only created by root service KOBJ_TYPE_NOTIFY, // a port, which is a service hub KOBJ_TYPE_PMA, // physical memory region, usually used to shared with each other. KOBJ_TYPE_ENDPOINT, // endpoint, an point to point ipc way KOBJ_TYPE_SOCKET, // point to point ipc way. KOBJ_TYPE_VM, // virtual machine, for Virtualization KOBJ_TYPE_VCPU, // vcpu for vm KOBJ_TYPE_IRQ, // irq for user-space driver KOBJ_TYPE_VIRQ, // virq for vcpu process in user-space. KOBJ_TYPE_STDIO, // dedicated for system debuging KOBJ_TYPE_POLLHUB, // hub for events need to send. KOBJ_TYPE_PORT, KOBJ_TYPE_MAX }; enum { KOBJ_GET_MMAP_ADDR = 0x100, }; /* * for process control */ enum { KOBJ_PROCESS_GET_PID = 0x1000, KOBJ_PROCESS_SETUP_SP, KOBJ_PROCESS_SETUP_REG0, KOBJ_PROCESS_WAKEUP, KOBJ_PROCESS_KILL, KOBJ_PROCESS_GRANT_RIGHT, KOBJ_PROCESS_SET_NAME, }; struct process_create_arg { unsigned long entry; unsigned long stack; int aff; int prio; int pid; int flags; }; /* * for pma kobject */ enum { PMA_TYPE_NORMAL = 0, PMA_TYPE_MMIO, PMA_TYPE_DMA, PMA_TYPE_PMEM, PMA_TYPE_KCACHE, PMA_TYPE_MAX }; enum { KOBJ_PMA_ADD_PAGES = 0x4000, KOBJ_PMA_GET_SIZE, }; struct pma_create_arg { int type; int right; int consequent; unsigned long start; unsigned long size; }; /* * for kobject poll */ enum { KOBJ_POLLHUB_OP_BASE = 0x2000, KOBJ_POLL_OP_ADD, KOBJ_POLL_OP_DEL, KOBJ_POLL_OP_MOD, }; #endif ================================================ FILE: kernel/include/uapi/procinfo_uapi.h ================================================ #ifndef __MINOS_PROC_UAPI_INFO_H__ #define __MINOS_PROC_UAPI_INFO_H__ #define PROC_NAME_SIZE 256 struct task_stat { int tid; int root_tid; int pid; int state; int cpu; int cpu_usage; int prio; unsigned long long start_ns; char cmd[PROC_NAME_SIZE]; }; #endif ================================================ FILE: kernel/include/uspace/elf.h ================================================ #ifndef __MINOS_ELF_H__ #define __MINOS_ELF_H__ #include typedef uint8_t Elf_Byte; typedef uint32_t Elf32_Addr; /* Unsigned program address */ typedef uint32_t Elf32_Off; /* Unsigned file offset */ typedef int32_t Elf32_Sword; /* Signed large integer */ typedef uint32_t Elf32_Word; /* Unsigned large integer */ typedef uint16_t Elf32_Half; /* Unsigned medium integer */ typedef uint64_t Elf64_Addr; typedef uint64_t Elf64_Off; typedef int32_t Elf64_Shalf; #ifdef __alpha__ typedef int64_t Elf64_Sword; typedef uint64_t Elf64_Word; #else typedef int32_t Elf64_Sword; typedef uint32_t Elf64_Word; #endif typedef int64_t Elf64_Sxword; typedef uint64_t Elf64_Xword; typedef uint32_t Elf64_Half; typedef uint16_t Elf64_Quarter; #if defined(__i386__) #define EM_THIS EM_386 #define EL_ARCH_USES_REL #elif defined(__amd64__) #define EM_THIS EM_AMD64 #define EL_ARCH_USES_RELA #elif defined(__arm__) #define EM_THIS EM_ARM #elif defined(__aarch64__) #define EM_THIS EM_AARCH64 #define EL_ARCH_USES_RELA #define EL_ARCH_USES_REL #else #error specify your ELF architecture #endif #if defined(__LP64__) || defined(__LLP64__) #define ELFSIZE 64 #else #define ELFSIZE 32 #endif #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #define ELFDATATHIS ELFDATA2LSB #else #define ELFDATATHIS ELFDATA2MSB #endif #define EI_MAG0 0 /* file ID */ #define EI_MAG1 1 /* file ID */ #define EI_MAG2 2 /* file ID */ #define EI_MAG3 3 /* file ID */ #define EI_CLASS 4 /* file class */ #define EI_DATA 5 /* data encoding */ #define EI_VERSION 6 /* ELF header version */ #define EI_OSABI 7 /* OS/ABI ID */ #define EI_ABIVERSION 8 /* ABI version */ #define EI_PAD 9 /* start of pad bytes */ #define EI_NIDENT 16 /* Size of e_ident[] */ /* e_ident[] magic number */ #define ELFMAG0 0x7f /* e_ident[EI_MAG0] */ #define ELFMAG1 'E' /* e_ident[EI_MAG1] */ #define ELFMAG2 'L' /* e_ident[EI_MAG2] */ #define ELFMAG3 'F' /* e_ident[EI_MAG3] */ #define ELFMAG "\177ELF" /* magic */ #define SELFMAG 4 /* size of magic */ /* e_ident[] file class */ #define ELFCLASSNONE 0 /* invalid */ #define ELFCLASS32 1 /* 32-bit objs */ #define ELFCLASS64 2 /* 64-bit objs */ #define ELFCLASSNUM 3 /* number of classes */ /* e_ident[] data encoding */ #define ELFDATANONE 0 /* invalid */ #define ELFDATA2LSB 1 /* Little-Endian */ #define ELFDATA2MSB 2 /* Big-Endian */ #define ELFDATANUM 3 /* number of data encode defines */ /* e_ident[] Operating System/ABI */ #define ELFOSABI_SYSV 0 /* UNIX System V ABI */ #define ELFOSABI_HPUX 1 /* HP-UX operating system */ #define ELFOSABI_NETBSD 2 /* NetBSD */ #define ELFOSABI_LINUX 3 /* GNU/Linux */ #define ELFOSABI_HURD 4 /* GNU/Hurd */ #define ELFOSABI_86OPEN 5 /* 86Open common IA32 ABI */ #define ELFOSABI_SOLARIS 6 /* Solaris */ #define ELFOSABI_MONTEREY 7 /* Monterey */ #define ELFOSABI_IRIX 8 /* IRIX */ #define ELFOSABI_FREEBSD 9 /* FreeBSD */ #define ELFOSABI_TRU64 10 /* TRU64 UNIX */ #define ELFOSABI_MODESTO 11 /* Novell Modesto */ #define ELFOSABI_OPENBSD 12 /* OpenBSD */ #define ELFOSABI_ARM 97 /* ARM */ #define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ /* e_ident */ #define IS_ELF(ehdr) ((ehdr).e_ident[EI_MAG0] == ELFMAG0 && \ (ehdr).e_ident[EI_MAG1] == ELFMAG1 && \ (ehdr).e_ident[EI_MAG2] == ELFMAG2 && \ (ehdr).e_ident[EI_MAG3] == ELFMAG3) /* ELF Header */ typedef struct { unsigned char e_ident[EI_NIDENT]; /* ELF Identification */ Elf32_Half e_type; /* object file type */ Elf32_Half e_machine; /* machine */ Elf32_Word e_version; /* object file version */ Elf32_Addr e_entry; /* virtual entry point */ Elf32_Off e_phoff; /* program header table offset */ Elf32_Off e_shoff; /* section header table offset */ Elf32_Word e_flags; /* processor-specific flags */ Elf32_Half e_ehsize; /* ELF header size */ Elf32_Half e_phentsize; /* program header entry size */ Elf32_Half e_phnum; /* number of program header entries */ Elf32_Half e_shentsize; /* section header entry size */ Elf32_Half e_shnum; /* number of section header entries */ Elf32_Half e_shstrndx; /* section header table's "section header string table" entry offset */ } Elf32_Ehdr; typedef struct { unsigned char e_ident[EI_NIDENT]; /* Id bytes */ Elf64_Quarter e_type; /* file type */ Elf64_Quarter e_machine; /* machine type */ Elf64_Half e_version; /* version number */ Elf64_Addr e_entry; /* entry point */ Elf64_Off e_phoff; /* Program hdr offset */ Elf64_Off e_shoff; /* Section hdr offset */ Elf64_Half e_flags; /* Processor flags */ Elf64_Quarter e_ehsize; /* sizeof ehdr */ Elf64_Quarter e_phentsize; /* Program header entry size */ Elf64_Quarter e_phnum; /* Number of program headers */ Elf64_Quarter e_shentsize; /* Section header entry size */ Elf64_Quarter e_shnum; /* Number of section headers */ Elf64_Quarter e_shstrndx; /* String table index */ } Elf64_Ehdr; /* e_type */ #define ET_NONE 0 /* No file type */ #define ET_REL 1 /* relocatable file */ #define ET_EXEC 2 /* executable file */ #define ET_DYN 3 /* shared object file */ #define ET_CORE 4 /* core file */ #define ET_NUM 5 /* number of types */ #define ET_LOPROC 0xff00 /* reserved range for processor */ #define ET_HIPROC 0xffff /* specific e_type */ /* e_machine */ #define EM_NONE 0 /* No Machine */ #define EM_M32 1 /* AT&T WE 32100 */ #define EM_SPARC 2 /* SPARC */ #define EM_386 3 /* Intel 80386 */ #define EM_68K 4 /* Motorola 68000 */ #define EM_88K 5 /* Motorola 88000 */ #define EM_486 6 /* Intel 80486 - unused? */ #define EM_860 7 /* Intel 80860 */ #define EM_MIPS 8 /* MIPS R3000 Big-Endian only */ /* * Don't know if EM_MIPS_RS4_BE, * EM_SPARC64, EM_PARISC, * or EM_PPC are ABI compliant */ #define EM_MIPS_RS4_BE 10 /* MIPS R4000 Big-Endian */ #define EM_SPARC64 11 /* SPARC v9 64-bit unofficial */ #define EM_PARISC 15 /* HPPA */ #define EM_SPARC32PLUS 18 /* Enhanced instruction set SPARC */ #define EM_PPC 20 /* PowerPC */ #define EM_ARM 40 /* ARM AArch32 */ #define EM_ALPHA 41 /* DEC ALPHA */ #define EM_SH 42 /* Hitachi/Renesas Super-H */ #define EM_SPARCV9 43 /* SPARC version 9 */ #define EM_IA_64 50 /* Intel IA-64 Processor */ #define EM_AMD64 62 /* AMD64 architecture */ #define EM_VAX 75 /* DEC VAX */ #define EM_AARCH64 183 /* ARM AArch64 */ /* Non-standard */ #define EM_ALPHA_EXP 0x9026 /* DEC ALPHA */ /* Version */ #define EV_NONE 0 /* Invalid */ #define EV_CURRENT 1 /* Current */ #define EV_NUM 2 /* number of versions */ /* Section Header */ typedef struct { Elf32_Word sh_name; /* name - index into section header * string table section */ Elf32_Word sh_type; /* type */ Elf32_Word sh_flags; /* flags */ Elf32_Addr sh_addr; /* address */ Elf32_Off sh_offset; /* file offset */ Elf32_Word sh_size; /* section size */ Elf32_Word sh_link; /* section header table index link */ Elf32_Word sh_info; /* extra information */ Elf32_Word sh_addralign; /* address alignment */ Elf32_Word sh_entsize; /* section entry size */ } Elf32_Shdr; typedef struct { Elf64_Half sh_name; /* section name */ Elf64_Half sh_type; /* section type */ Elf64_Xword sh_flags; /* section flags */ Elf64_Addr sh_addr; /* virtual address */ Elf64_Off sh_offset; /* file offset */ Elf64_Xword sh_size; /* section size */ Elf64_Half sh_link; /* link to another */ Elf64_Half sh_info; /* misc info */ Elf64_Xword sh_addralign; /* memory alignment */ Elf64_Xword sh_entsize; /* table entry size */ } Elf64_Shdr; /* Special Section Indexes */ #define SHN_UNDEF 0 /* undefined */ #define SHN_LORESERVE 0xff00 /* lower bounds of reserved indexes */ #define SHN_LOPROC 0xff00 /* reserved range for processor */ #define SHN_HIPROC 0xff1f /* specific section indexes */ #define SHN_ABS 0xfff1 /* absolute value */ #define SHN_COMMON 0xfff2 /* common symbol */ #define SHN_HIRESERVE 0xffff /* upper bounds of reserved indexes */ /* sh_type */ #define SHT_NULL 0 /* inactive */ #define SHT_PROGBITS 1 /* program defined information */ #define SHT_SYMTAB 2 /* symbol table section */ #define SHT_STRTAB 3 /* string table section */ #define SHT_RELA 4 /* relocation section with addends*/ #define SHT_HASH 5 /* symbol hash table section */ #define SHT_DYNAMIC 6 /* dynamic section */ #define SHT_NOTE 7 /* note section */ #define SHT_NOBITS 8 /* no space section */ #define SHT_REL 9 /* relation section without addends */ #define SHT_SHLIB 10 /* reserved - purpose unknown */ #define SHT_DYNSYM 11 /* dynamic symbol table section */ #define SHT_NUM 12 /* number of section types */ #define SHT_LOPROC 0x70000000 /* reserved range for processor */ #define SHT_HIPROC 0x7fffffff /* specific section header types */ #define SHT_LOUSER 0x80000000 /* reserved range for application */ #define SHT_HIUSER 0xffffffff /* specific indexes */ /* Section names */ #define ELF_BSS ".bss" /* uninitialized data */ #define ELF_DATA ".data" /* initialized data */ #define ELF_DEBUG ".debug" /* debug */ #define ELF_DYNAMIC ".dynamic" /* dynamic linking information */ #define ELF_DYNSTR ".dynstr" /* dynamic string table */ #define ELF_DYNSYM ".dynsym" /* dynamic symbol table */ #define ELF_FINI ".fini" /* termination code */ #define ELF_GOT ".got" /* global offset table */ #define ELF_HASH ".hash" /* symbol hash table */ #define ELF_INIT ".init" /* initialization code */ #define ELF_REL_DATA ".rel.data" /* relocation data */ #define ELF_REL_FINI ".rel.fini" /* relocation termination code */ #define ELF_REL_INIT ".rel.init" /* relocation initialization code */ #define ELF_REL_DYN ".rel.dyn" /* relocation dynamic link info */ #define ELF_REL_RODATA ".rel.rodata" /* relocation read-only data */ #define ELF_REL_TEXT ".rel.text" /* relocation code */ #define ELF_RODATA ".rodata" /* read-only data */ #define ELF_SHSTRTAB ".shstrtab" /* section header string table */ #define ELF_STRTAB ".strtab" /* string table */ #define ELF_SYMTAB ".symtab" /* symbol table */ #define ELF_TEXT ".text" /* code */ /* Section Attribute Flags - sh_flags */ #define SHF_WRITE 0x1 /* Writable */ #define SHF_ALLOC 0x2 /* occupies memory */ #define SHF_EXECINSTR 0x4 /* executable */ #define SHF_TLS 0x400 /* thread local storage */ #define SHF_MASKPROC 0xf0000000 /* reserved bits for processor * specific section attributes */ /* Symbol Table Entry */ typedef struct elf32_sym { Elf32_Word st_name; /* name - index into string table */ Elf32_Addr st_value; /* symbol value */ Elf32_Word st_size; /* symbol size */ unsigned char st_info; /* type and binding */ unsigned char st_other; /* 0 - no defined meaning */ Elf32_Half st_shndx; /* section header index */ } Elf32_Sym; typedef struct { Elf64_Half st_name; /* Symbol name index in str table */ Elf_Byte st_info; /* type / binding attrs */ Elf_Byte st_other; /* unused */ Elf64_Quarter st_shndx; /* section index of symbol */ Elf64_Xword st_value; /* value of symbol */ Elf64_Xword st_size; /* size of symbol */ } Elf64_Sym; /* Symbol table index */ #define STN_UNDEF 0 /* undefined */ /* Extract symbol info - st_info */ #define ELF32_ST_BIND(x) ((x) >> 4) #define ELF32_ST_TYPE(x) (((unsigned int) x) & 0xf) #define ELF32_ST_INFO(b,t) (((b) << 4) + ((t) & 0xf)) #define ELF64_ST_BIND(x) ((x) >> 4) #define ELF64_ST_TYPE(x) (((unsigned int) x) & 0xf) #define ELF64_ST_INFO(b,t) (((b) << 4) + ((t) & 0xf)) /* Symbol Binding - ELF32_ST_BIND - st_info */ #define STB_LOCAL 0 /* Local symbol */ #define STB_GLOBAL 1 /* Global symbol */ #define STB_WEAK 2 /* like global - lower precedence */ #define STB_NUM 3 /* number of symbol bindings */ #define STB_LOPROC 13 /* reserved range for processor */ #define STB_HIPROC 15 /* specific symbol bindings */ /* Symbol type - ELF32_ST_TYPE - st_info */ #define STT_NOTYPE 0 /* not specified */ #define STT_OBJECT 1 /* data object */ #define STT_FUNC 2 /* function */ #define STT_SECTION 3 /* section */ #define STT_FILE 4 /* file */ #define STT_TLS 6 /* thread local storage */ #define STT_LOPROC 13 /* reserved range for processor */ #define STT_HIPROC 15 /* specific symbol types */ /* Relocation entry with implicit addend */ typedef struct { Elf32_Addr r_offset; /* offset of relocation */ Elf32_Word r_info; /* symbol table index and type */ } Elf32_Rel; /* Relocation entry with explicit addend */ typedef struct { Elf32_Addr r_offset; /* offset of relocation */ Elf32_Word r_info; /* symbol table index and type */ Elf32_Sword r_addend; } Elf32_Rela; /* Extract relocation info - r_info */ #define ELF32_R_SYM(i) ((i) >> 8) #define ELF32_R_TYPE(i) ((unsigned char) (i)) #define ELF32_R_INFO(s,t) (((s) << 8) + (unsigned char)(t)) typedef struct { Elf64_Xword r_offset; /* where to do it */ Elf64_Xword r_info; /* index & type of relocation */ } Elf64_Rel; typedef struct { Elf64_Xword r_offset; /* where to do it */ Elf64_Xword r_info; /* index & type of relocation */ Elf64_Sxword r_addend; /* adjustment value */ } Elf64_Rela; #define ELF64_R_SYM(info) ((info) >> 32) #define ELF64_R_TYPE(info) ((info) & 0xFFFFFFFF) #define ELF64_R_INFO(s,t) (((s) << 32) + (__uint32_t)(t)) #if defined(__mips64__) && defined(__MIPSEL__) /* * The 64-bit MIPS ELF ABI uses a slightly different relocation format * than the regular ELF ABI: the r_info field is split into several * pieces (see gnu/usr.bin/binutils/include/elf/mips.h for details). */ #undef ELF64_R_SYM #undef ELF64_R_TYPE #undef ELF64_R_INFO #define ELF64_R_TYPE(info) (swap32((info) >> 32)) #define ELF64_R_SYM(info) ((info) & 0xFFFFFFFF) #define ELF64_R_INFO(s,t) (((__uint64_t)swap32(t) << 32) + (__uint32_t)(s)) #endif /* __mips64__ && __MIPSEL__ */ /* Program Header */ typedef struct { Elf32_Word p_type; /* segment type */ Elf32_Off p_offset; /* segment offset */ Elf32_Addr p_vaddr; /* virtual address of segment */ Elf32_Addr p_paddr; /* physical address - ignored? */ Elf32_Word p_filesz; /* number of bytes in file for seg. */ Elf32_Word p_memsz; /* number of bytes in mem. for seg. */ Elf32_Word p_flags; /* flags */ Elf32_Word p_align; /* memory alignment */ } Elf32_Phdr; typedef struct { Elf64_Half p_type; /* entry type */ Elf64_Half p_flags; /* flags */ Elf64_Off p_offset; /* offset */ Elf64_Addr p_vaddr; /* virtual address */ Elf64_Addr p_paddr; /* physical address */ Elf64_Xword p_filesz; /* file size */ Elf64_Xword p_memsz; /* memory size */ Elf64_Xword p_align; /* memory & file alignment */ } Elf64_Phdr; /* Segment types - p_type */ #define PT_NULL 0 /* unused */ #define PT_LOAD 1 /* loadable segment */ #define PT_DYNAMIC 2 /* dynamic linking section */ #define PT_INTERP 3 /* the RTLD */ #define PT_NOTE 4 /* auxiliary information */ #define PT_SHLIB 5 /* reserved - purpose undefined */ #define PT_PHDR 6 /* program header */ #define PT_TLS 7 /* thread local storage */ #define PT_LOOS 0x60000000 /* reserved range for OS */ #define PT_HIOS 0x6fffffff /* specific segment types */ #define PT_LOPROC 0x70000000 /* reserved range for processor */ #define PT_HIPROC 0x7fffffff /* specific segment types */ #define PT_OPENBSD_RANDOMIZE 0x65a3dbe6 /* fill with random data */ #define PT_GANDR_KERNEL 0x67646b6c /* gdkl */ /* Segment flags - p_flags */ #define PF_X 0x1 /* Executable */ #define PF_W 0x2 /* Writable */ #define PF_R 0x4 /* Readable */ #define PF_MASKPROC 0xf0000000 /* reserved bits for processor */ /* specific segment flags */ /* Dynamic structure */ typedef struct { Elf32_Sword d_tag; /* controls meaning of d_val */ union { Elf32_Word d_val; /* Multiple meanings - see d_tag */ Elf32_Addr d_ptr; /* program virtual address */ } d_un; } Elf32_Dyn; typedef struct { Elf64_Xword d_tag; /* controls meaning of d_val */ union { Elf64_Addr d_ptr; Elf64_Xword d_val; } d_un; } Elf64_Dyn; /* Dynamic Array Tags - d_tag */ #define DT_NULL 0 /* marks end of _DYNAMIC array */ #define DT_NEEDED 1 /* string table offset of needed lib */ #define DT_PLTRELSZ 2 /* size of relocation entries in PLT */ #define DT_PLTGOT 3 /* address PLT/GOT */ #define DT_HASH 4 /* address of symbol hash table */ #define DT_STRTAB 5 /* address of string table */ #define DT_SYMTAB 6 /* address of symbol table */ #define DT_RELA 7 /* address of relocation table */ #define DT_RELASZ 8 /* size of relocation table */ #define DT_RELAENT 9 /* size of relocation entry */ #define DT_STRSZ 10 /* size of string table */ #define DT_SYMENT 11 /* size of symbol table entry */ #define DT_INIT 12 /* address of initialization func. */ #define DT_FINI 13 /* address of termination function */ #define DT_SONAME 14 /* string table offset of shared obj */ #define DT_RPATH 15 /* string table offset of library * search path */ #define DT_SYMBOLIC 16 /* start sym search in shared obj. */ #define DT_REL 17 /* address of rel. tbl. w addends */ #define DT_RELSZ 18 /* size of DT_REL relocation table */ #define DT_RELENT 19 /* size of DT_REL relocation entry */ #define DT_PLTREL 20 /* PLT referenced relocation entry */ #define DT_DEBUG 21 /* bugger */ #define DT_TEXTREL 22 /* Allow rel. mod. to unwritable seg */ #define DT_JMPREL 23 /* add. of PLT's relocation entries */ #define DT_BIND_NOW 24 /* Bind now regardless of env setting */ #define DT_LOOS 0x6000000d /* reserved range for OS */ #define DT_HIOS 0x6ffff000 /* specific dynamic array tags */ #define DT_LOPROC 0x70000000 /* reserved range for processor */ #define DT_HIPROC 0x7fffffff /* specific dynamic array tags */ /* some other useful tags */ #define DT_RELACOUNT 0x6ffffff9 /* if present, number of RELATIVE */ #define DT_RELCOUNT 0x6ffffffa /* relocs, which must come first */ #define DT_FLAGS_1 0x6ffffffb /* Dynamic Flags - DT_FLAGS_1 .dynamic entry */ #define DF_1_NOW 0x00000001 #define DF_1_GLOBAL 0x00000002 #define DF_1_GROUP 0x00000004 #define DF_1_NODELETE 0x00000008 #define DF_1_LOADFLTR 0x00000010 #define DF_1_INITFIRST 0x00000020 #define DF_1_NOOPEN 0x00000040 #define DF_1_ORIGIN 0x00000080 #define DF_1_DIRECT 0x00000100 #define DF_1_TRANS 0x00000200 #define DF_1_INTERPOSE 0x00000400 #define DF_1_NODEFLIB 0x00000800 #define DF_1_NODUMP 0x00001000 #define DF_1_CONLFAT 0x00002000 /* ld.so: number of low tags that are used saved internally (0 .. DT_NUM-1) */ #define DT_NUM (DT_JMPREL+1) /* * Note Definitions */ typedef struct { Elf32_Word namesz; Elf32_Word descsz; Elf32_Word type; } Elf32_Note; typedef struct { Elf64_Half namesz; Elf64_Half descsz; Elf64_Half type; } Elf64_Note; #define ELFSIZE 64 #if defined(ELFSIZE) && (ELFSIZE == 32) #define Elf_Ehdr Elf32_Ehdr #define Elf_Phdr Elf32_Phdr #define Elf_Shdr Elf32_Shdr #define Elf_Sym Elf32_Sym #define Elf_Rel Elf32_Rel #define Elf_RelA Elf32_Rela #define Elf_Dyn Elf32_Dyn #define Elf_Half Elf32_Half #define Elf_Word Elf32_Word #define Elf_Sword Elf32_Sword #define Elf_Addr Elf32_Addr #define Elf_Off Elf32_Off #define Elf_Nhdr Elf32_Nhdr #define Elf_Note Elf32_Note #define ELF_R_SYM ELF32_R_SYM #define ELF_R_TYPE ELF32_R_TYPE #define ELF_R_INFO ELF32_R_INFO #define ELFCLASS ELFCLASS32 #define ELF_ST_BIND ELF32_ST_BIND #define ELF_ST_TYPE ELF32_ST_TYPE #define ELF_ST_INFO ELF32_ST_INFO #elif defined(ELFSIZE) && (ELFSIZE == 64) #define Elf_Ehdr Elf64_Ehdr #define Elf_Phdr Elf64_Phdr #define Elf_Shdr Elf64_Shdr #define Elf_Sym Elf64_Sym #define Elf_Rel Elf64_Rel #define Elf_RelA Elf64_Rela #define Elf_Dyn Elf64_Dyn #define Elf_Half Elf64_Half #define Elf_Word Elf64_Word #define Elf_Sword Elf64_Sword #define Elf_Addr Elf64_Addr #define Elf_Off Elf64_Off #define Elf_Nhdr Elf64_Nhdr #define Elf_Note Elf64_Note #define ELF_R_SYM ELF64_R_SYM #define ELF_R_TYPE ELF64_R_TYPE #define ELF_R_INFO ELF64_R_INFO #define ELFCLASS ELFCLASS64 #define ELF_ST_BIND ELF64_ST_BIND #define ELF_ST_TYPE ELF64_ST_TYPE #define ELF_ST_INFO ELF64_ST_INFO #endif /* dedicated for minos */ #define AT_SERVER_HANDLE 61/* for minos talk to root service */ #define AT_HEAP_BASE 62 #define AT_HEAP_END 63 #endif ================================================ FILE: kernel/include/uspace/handle.h ================================================ #ifndef __MINOS_HANDLE_H__ #define __MINOS_HANDLE_H__ #include #include struct kobject; struct handle_desc { struct kobject *kobj; int right; int padding; } __packed; struct handle_table_desc { uint32_t index; uint32_t left; struct handle_desc *next; } __packed; #define HANDLE_NULL (-1) #define NR_DESC_PER_PAGE (PAGE_SIZE / sizeof(struct handle_desc) - 1) #define PROC_MAX_HANDLE (NR_DESC_PER_PAGE * 128) #define WRONG_HANDLE(handle) \ ((handle == HANDLE_NULL) || (handle >= PROC_MAX_HANDLE)) void __release_handle(struct process *proc, handle_t handle); int release_handle(handle_t handle, struct kobject **kobj, right_t *right); handle_t __alloc_handle(struct process *proc, struct kobject *kobj, right_t right); handle_t alloc_handle(struct kobject *kobj, right_t right); int get_kobject_from_process(struct process *proc, handle_t handle, struct kobject **kobj, right_t *right); int get_kobject(handle_t handle, struct kobject **kobj, right_t *right); int put_kobject(struct kobject *kobj); void release_proc_kobjects(struct process *proc); void process_handles_deinit(struct process *proc); int init_proc_handles(struct process *proc); handle_t send_handle(struct process *psrc, struct process *pdst, handle_t handle, right_t right_send); #endif ================================================ FILE: kernel/include/uspace/iqueue.h ================================================ #ifndef __MINOS_IQUEUE_H__ #define __MINOS_IQUEUE_H__ #include #include #include struct kobject; struct task; #define IMSG_STATE_INIT 0 #define IMSG_STATE_IN_PROCESS 1 #define IMSG_STATE_ERROR 2 struct imsg { void *data; long token; long retcode; int state; int submit; struct list_head list; struct event ievent; }; struct iqueue { int mutil_writer; int rstate; int wstate; spinlock_t lock; struct list_head pending_list; struct list_head processing_list; struct kobject *kobj; sem_t isem; }; static void inline imsg_init(struct imsg *imsg, struct task *task) { imsg->data = task; imsg->retcode = 0; imsg->token = new_event_token(); imsg->state = IMSG_STATE_INIT; imsg->submit = 0; event_init(&imsg->ievent, OS_EVENT_TYPE_NORMAL, task); } long iqueue_recv(struct iqueue *iqueue, void __user *data, size_t data_size, size_t *actual_data, void __user *extra, size_t extra_size, size_t *actual_extra, uint32_t timeout); long iqueue_send(struct iqueue *iqueue, void __user *data, size_t data_size, void __user *extra, size_t extra_size, uint32_t timeout); int iqueue_reply(struct iqueue *iqueue, right_t right, long token, long errno, handle_t fd, right_t fd_right); int iqueue_close(struct iqueue *iqueue, right_t right, struct process *proc); void iqueue_init(struct iqueue *iq, int mutil_writer, struct kobject *kobj); #endif ================================================ FILE: kernel/include/uspace/kobject.h ================================================ #ifndef __MINOS_KOBJECT_H__ #define __MINOS_KOBJECT_H__ #include #include #include #include struct task; struct process; struct kobject_ops; struct poll_struct; #define KOBJ_FLAGS_NON_SHARED (1 << 0) /* * Kernel object is a object than can provide some ability * to user space thread. * * type : the type of this kobj defined as above. * ref : reference count of this kernel object, when * 0 can be released. * right_mask : the right mask for this kobj used for grant * list : list all the kernel object for a task or global. */ struct kobject { uint8_t type; uint8_t flags; uint16_t padding; right_t right_mask; atomic_t ref; spinlock_t lock; struct poll_struct *poll_struct; struct kobject_ops *ops; unsigned long data; struct list_head list; }; struct kobject_ops { long (*send)(struct kobject *kobj, void __user *data, size_t data_size, void __user *extra, size_t extra_size, uint32_t timeout); long (*recv)(struct kobject *kobj, void __user *data, size_t data_size, size_t *actual_data, void __user *extra, size_t extra_size, size_t *actual_extra, uint32_t timeout); void (*release)(struct kobject *kobj); int (*open)(struct kobject *kobj, handle_t handle, right_t right); int (*poll)(struct kobject *ksrc, struct kobject *kdst, int event, bool enable); int (*close)(struct kobject *kobj, right_t right, struct process *proc); int (*reply)(struct kobject *kobj, right_t right, long token, long err_code, handle_t fd, right_t fd_right); int (*mmap)(struct kobject *kobj, right_t right, void **addr, unsigned long *msize); int (*munmap)(struct kobject *kobj, right_t right); long (*ctl)(struct kobject *kobj, int req, unsigned long data); }; typedef int(*kobject_create_cb)(struct kobject **kobj, right_t *right, unsigned long data); struct kobject_desc { char *name; int type; kobject_create_cb ops; }; #define DEFINE_KOBJECT(kname, ktype, kops) \ static struct kobject_desc __kobject_##kname __used __section(".__kobject_desc") = { \ .name = #kname, \ .type = ktype, \ .ops = kops, \ } void register_kobject_ops(struct kobject_ops *ops, int type); uint32_t kobject_token(void); int kobject_get(struct kobject *kobj); int kobject_put(struct kobject *kobj); void kobject_init(struct kobject *kobj, int type, right_t right_mask, unsigned long data); int kobject_close(struct kobject *kobj, right_t right, struct process *proc); int kobject_create(int type, struct kobject **kobj, right_t *right, unsigned long data); int kobject_poll(struct kobject *ksrc, struct kobject *dst, int event, int enable); long kobject_recv(struct kobject *kobj, void __user *data, size_t data_size, size_t *actual_data, void __user *extra, size_t extra_size, size_t *actual_extra, uint32_t timeout); long kobject_send(struct kobject *kobj, void __user *data, size_t data_size, void __user *extra, size_t extra_size, uint32_t timeout); int kobject_reply(struct kobject *kobj, right_t right, unsigned long token, long err_code, handle_t fd, right_t fd_right); int kobject_munmap(struct kobject *kobj, right_t right); int kobject_mmap(struct kobject *kobj, right_t right, void **addr, unsigned long *msize); long kobject_ctl(struct kobject *kobj, right_t right, int req, unsigned long data); int kobject_open(struct kobject *kobj, handle_t handle, right_t right); int create_new_pma(struct kobject **kobj, right_t *right, struct pma_create_arg *args); #endif ================================================ FILE: kernel/include/uspace/poll.h ================================================ #ifndef __MINOS_POLL_H__ #define __MINOS_POLL_H__ #include #include #define POLLIN 0x001 #define POLLOUT 0x002 #define POLLROPEN 0X004 #define POLLRCLOSE 0x008 #define POLLWOPEN 0x010 #define POLLWCLOSE 0x020 #define POLLKERNEL 0x040 enum { EV_IN = 0, EV_OUT, EV_ROPEN, EV_RCLOSE, EV_WOPEN, EV_WCLOSE, EV_KERNEL, EV_MAX, }; #define POLL_EVENT_MASK \ (POLLIN | POLLOUT | POLLROPEN | POLLRCLOSE | \ POLLWOPEN | POLLWCLOSE | POLLKERNEL) #define POLL_READ_RIGHT_EVENT \ (POLLIN | POLLWOPEN | POLLWCLOSE | POLLKERNEL) #define POLL_WRITE_RIGHT_EVENT \ (POLLOUT | POLLROPEN | POLLRCLOSE) /* * kernel events - which sended by kernel which * happend on the kobject. */ #define POLL_KEV_NULL 0x0 #define POLL_KEV_PAGE_FAULT 0x1 #define POLL_KEV_PROCESS_EXIT 0x2 struct poll_hub { struct list_head event_list; spinlock_t lock; struct kobject kobj; struct event event; }; struct pevent_item { struct poll_hub *poller; unsigned long data; struct pevent_item *next; }; struct poll_struct { struct pevent_item *pevents[EV_MAX]; }; struct poll_data { union { void *ptr; unsigned long pdata; }; int fd; int type; uint64_t data0; uint64_t data1; uint64_t data2; }; struct poll_event { uint32_t events; struct poll_data data; }; struct poll_event_kernel { struct poll_event event; struct list_head list; int release; }; static inline int event_is_polled(struct poll_struct *ps, int ev) { return (ps && (ps->pevents[ev])); } int poll_event_send_static(struct pevent_item *pi, struct poll_event_kernel *evk); int poll_event_send(struct poll_struct *ps, int ev); int poll_event_send_with_data(struct poll_struct *ps, int event, int type, uint64_t data0, uint64_t data1, uint64_t data2); struct poll_event *alloc_poll_event(void); void release_poll_struct(struct kobject *kobj); #endif ================================================ FILE: kernel/include/uspace/proc.h ================================================ #ifndef __MINOS_PROC_H__ #define __MINOS_PROC_H__ #include #include #include #include #include #define PROCESS_NAME_SIZE 32 struct task; #define PROC_FLAGS_VMCTL (1 << 0) #define PROC_FLAGS_HWCTL (1 << 1) #define PROC_FLAGS_ROOT (1 << 31) #define PROC_FLAGS_MASK (PROC_FLAGS_VMCTL | PROC_FLAGS_HWCTL) struct process { int pid; int flags; int task_cnt; int stopped; struct vspace vspace; /* * handle_desc_table will store all the kobjects created * and kobjects connected by this process. and * * when close or open kobject, it will only clear or * set the right for related kobject in kobj_table. */ struct handle_desc *handle_desc_table; struct task *root_task; struct list_head task_list; spinlock_t lock; struct kobject kobj; struct iqueue iqueue; }; #define current_proc (struct process *)current->vs->pdata #define task_to_proc(task) (struct process *)((task)->vs->pdata) static inline int proc_is_root(struct process *proc) { return !!(proc->flags & PROC_FLAGS_ROOT); } static inline int proc_can_vmctl(struct process *proc) { return !!(proc->flags & PROC_FLAGS_VMCTL); } static inline int proc_can_hwctl(struct process *proc) { return !!(proc->flags & PROC_FLAGS_HWCTL); } struct process *create_process(int pid, task_func_t func, void *usp, int prio, int aff, unsigned long opt); void process_die(void); void kill_process(struct process *proc, int handle); void clean_process_on_pcpu(struct pcpu *pcpu); int process_page_fault(struct process *proc, uint64_t virtaddr, uint64_t info); int wake_up_process(struct process *proc); #endif ================================================ FILE: kernel/include/uspace/procinfo.h ================================================ #ifndef __MINOS_PROC_INFO_H__ #define __MINOS_PROC_INFO_H__ #include #include struct task; void init_task_stat(struct task *task); void release_task_stat(int tid); void update_task_stat(struct task *task); struct task_stat *get_task_stat(int tid); #endif ================================================ FILE: kernel/include/uspace/socket.h ================================================ #ifndef __MINOS_SOCKET_H__ #define __MINOS_SOCKET_H__ #include struct socket { int flags; void *data_addr; int pages; struct kobject kobj; }; #endif ================================================ FILE: kernel/include/uspace/syscall.h ================================================ #ifndef __MINOS_SYSCALL_H__ #define __MINOS_SYSCALL_H__ #include #include extern void sys_sched_yield(void); extern int sys_kobject_connect(char __user *path, right_t right); extern int sys_kobject_close(int handle); extern int sys_kobject_open(handle_t handle); extern handle_t sys_kobject_create(int type, unsigned long data); extern ssize_t sys_kobject_recv(handle_t handle, void __user *data, size_t data_size, size_t *actual_data, void __user *extra, size_t extra_size, size_t *actual_extra, uint32_t timeout); extern ssize_t sys_kobject_send(handle_t handle, void __user *data, size_t data_size, void __user *extra, size_t extra_size, uint32_t timeout); extern int sys_kobject_reply(handle_t handle, long token, long err_code, handle_t fd, right_t fd_right); extern long sys_futex(uint32_t __user *uaddr, int op, uint32_t val, struct timespec __user *utime, uint32_t __user *uaddr2, uint32_t val3); extern int sys_unmap(handle_t proc_handle, handle_t pma_handle, unsigned long virt, size_t size); extern int sys_map(handle_t proc_handle, handle_t pma_handle, unsigned long virt, size_t size, right_t right); extern unsigned long sys_mtrans(unsigned long virt); extern handle_t sys_grant(handle_t proc, handle_t handle, right_t right, int release); extern int sys_kobject_munmap(handle_t handle); extern int sys_kobject_mmap(handle_t handle, void **addr, unsigned long *msize); extern long sys_kobject_ctl(handle_t handle, int req, unsigned long data); extern int sys_clock_gettime(int id, struct timespec __user *ts); extern int sys_clock_nanosleep(int id, int flags, long time, long ns, struct timespec __user *rem); extern int sys_exit(int errno); extern int sys_exitgroup(int errno); extern int sys_clone(int flags, void *stack, int *ptid, void *tls, int *ctid); #endif ================================================ FILE: kernel/include/uspace/uaccess.h ================================================ #ifndef __MINOS_ACCESS_H__ #define __MINOS_ACCESS_H__ #include #include struct vspace; int copy_string_from_user(char *dst, char __user *src, int max); int __copy_from_user(void *dst, struct vspace *vsrc, void __user *src, size_t size); int __copy_to_user(struct vspace *vdst, void __user *dst, void *src, size_t size); int copy_from_user(void *dst, void __user *src, size_t size); int copy_to_user(void __user *dst, void *src, size_t size); int copy_user_to_user(struct vspace *vdst, void __user *dst, struct vspace *vsrc, void __user *src, size_t size); int copy_string_from_user_safe(char *dst, char __user *src, size_t max); #endif ================================================ FILE: kernel/include/uspace/vspace.h ================================================ #ifndef __MINOS_VSPACE_H__ #define __MINOS_VSPACE_H__ #include #include #include #include #define HUGE_PAGE_SIZE 0x200000 #define HUGE_PAGE_SHIFT 21 #define IS_HUGE_ALIGN(x) (!((unsigned long)(x) & (HUGE_PAGE_SIZE - 1))) #define PROCESS_TOP_HALF_BASE (USER_PROCESS_ADDR_LIMIT >> 1) #define VMA_SHARED_BASE PROCESS_TOP_HALF_BASE #define pa2sva(phy) ((phy) + VMA_SHARED_BASE) #define va2sva(va) (pa2sva(vtop(va))) #define va2pa(va) vtop(va) #define pa2va(pa) ptov(pa) /* * 0 - (256G - 1) user space memory region * 256G - (512G - 1) shared memory mapping space. * * 0 - ( 64G - 1) ELF (text data bss and other) * 64G -> (64G + 256M) heap area * 65G - (255G - 1) VMAP area * ((256G - 32K - 4K) -> (256G - 4K)) stack * * system process can handle it heap by itself, the heap * region for system process if 64G --- 64G + 256M * kernel will handle the page fault for this heap region. */ #define SYS_PROC_HEAP_BASE (64UL * 1024 * 1024 * 1024) #define SYS_PROC_HEAP_SIZE (256UL * 1024 * 1024) #define SYS_PROC_HEAP_END (SYS_PROC_HEAP_BASE + SYS_PROC_HEAP_SIZE) #define SYS_PROC_VMAP_BASE (65UL * 1024 * 1024 * 1024) #define SYS_PROC_VMAP_END (255UL * 1024 * 1024 * 1024) #define MIN_ELF_LOAD_BASE 0x1000 #define ROOTSRV_USTACK_TOP (PROCESS_TOP_HALF_BASE - PAGE_SIZE * 8) #define ROOTSRV_USTACK_BOTTOM (ROOTSRV_USTACK_TOP - ROOTSRV_USTACK_PAGES * PAGE_SIZE) #define ROOTSRV_BOOTDATA_BASE (PROCESS_TOP_HALF_BASE - PAGE_SIZE * 2) int vspace_init(struct process *proc); void vspace_deinit(struct process *proc); int map_process_memory(struct process *proc, unsigned long vaddr, size_t size, unsigned long phy, unsigned long flags); int unmap_process_memory(struct process *proc, unsigned long vaddr, size_t size); unsigned long translate_va_to_pa(struct vspace *vs, unsigned long va); void *uva_to_kva(struct vspace *vs, unsigned long va, size_t size, unsigned long right); int handle_page_fault(unsigned long virt, int write, unsigned long flags); void inc_vspace_usage(struct vspace *vs); void dec_vspace_usage(struct vspace *vs); void add_released_page_to_vspace(struct vspace *vs, unsigned long addr); #endif ================================================ FILE: kernel/include/virt/hypercall.h ================================================ #ifndef __MINOS_HYPERCALL_H__ #define __MINOS_HYPERCALL_h__ /* below defination is for HVC call */ #define HVC_TYPE_VM0 (0x8) #define HVC_TYPE_MISC (0x9) #define HVC_TYPE_VMBOX (0xa) #define HVC_TYPE_DEBUG_CONSOLE (0xb) #define HVC_CALL_BASE (0xc0000000) #define HVC_CALL_NUM(t, n) (HVC_CALL_BASE + (t << 24) + n) #define HVC_VM0_FN(n) HVC_CALL_NUM(HVC_TYPE_VM0, n) #define HVC_MISC_FN(n) HVC_CALL_NUM(HVC_TYPE_MISC, n) #define HVC_VMBOX_FN(n) HVC_CALL_NUM(HVC_TYPE_VMBOX, n) #define HVC_VMBOX_DEBUG_CONSOLE(n) HVC_CALL_NUM(HVC_TYPE_DEBUG_CONSOLE, n) /* hypercall for vm releated operation */ #define HVC_VM_CREATE HVC_VM0_FN(0) #define HVC_VM_DESTORY HVC_VM0_FN(1) #define HVC_VM_RESTART HVC_VM0_FN(2) #define HVC_VM_POWER_UP HVC_VM0_FN(3) #define HVC_VM_POWER_DOWN HVC_VM0_FN(4) #define HVC_VM_MMAP HVC_VM0_FN(5) #define HVC_VM_UNMMAP HVC_VM0_FN(6) #define HVC_VM_SEND_VIRQ HVC_VM0_FN(7) #define HVC_VM_CREATE_VMCS HVC_VM0_FN(8) #define HVC_VM_CREATE_VMCS_IRQ HVC_VM0_FN(9) #define HVC_VM_REQUEST_VIRQ HVC_VM0_FN(10) #define HVC_VM_VIRTIO_MMIO_INIT HVC_VM0_FN(11) #define HVC_VM_VIRTIO_MMIO_DEINIT HVC_VM0_FN(12) #define HVC_VM_CREATE_RESOURCE HVC_VM0_FN(13) #define HVC_CHANGE_LOG_LEVEL HVC_VM0_FN(14) #define HVC_GET_VMID HVC_MISC_FN(0) #define HVC_SCHED_OUT HVC_MISC_FN(1) #define HVC_GET_VM_CAP HVC_MISC_FN(2) #define VM_CAP_HOST (1 << 0) #define VM_CAP_NATIVE (1 << 1) #define HVC_DC_GET_STAT HVC_VMBOX_DEBUG_CONSOLE(0) #define HVC_DC_GET_RING HVC_VMBOX_DEBUG_CONSOLE(1) #define HVC_DC_GET_IRQ HVC_VMBOX_DEBUG_CONSOLE(2) #define HVC_DC_WRITE HVC_VMBOX_DEBUG_CONSOLE(3) #define HVC_DC_OPEN HVC_VMBOX_DEBUG_CONSOLE(4) #define HVC_DC_CLOSE HVC_VMBOX_DEBUG_CONSOLE(5) #endif ================================================ FILE: kernel/include/virt/iommu.h ================================================ // SPDX-License-Identifier: GPL-2.0 #ifndef _MINOS_IOMMU_H_ #define _MINOS_IOMMU_H_ struct vm; struct device_node; struct iommu_ops { int (*init)(struct device_node *node); int (*vm_init)(struct vm *vm); int (*vm_destroy)(struct vm *vm); int (*iotlb_flush_all)(struct vm *vm); int (*assign_node)(struct vm *vm, struct device_node *node); int (*deassign_node)(struct vm *vm, struct device_node *node); }; int iommu_vm_init(struct vm *vm); int iommu_vm_destroy(struct vm *vm); int iommu_iotlb_flush_all(struct vm *vm); int iommu_assign_node(struct vm *vm, struct device_node *node); int iommu_deassign_node(struct vm *vm, struct device_node *node); #endif ================================================ FILE: kernel/include/virt/os.h ================================================ #ifndef _MINOS_OS_H_ #define _MINOS_OS_H_ struct vcpu; struct vm; #define MINOS_OS_NAME_SIZE (32) enum { OS_TYPE_OTHERS = 0, OS_TYPE_LINUX, OS_TYPE_XNU, OS_TYPE_LUSHAN, OS_TYPE_MAX, }; struct os_ops { void (*vm_init)(struct vm *vm); void (*vcpu_init)(struct vcpu *vcpu); void (*vcpu_power_on)(struct vcpu *vcpu, unsigned long entry); void (*vm_setup)(struct vm *vm); int (*create_gvm_res)(struct vm *vm); int (*create_nvm_res)(struct vm *vm); }; struct os { char name[MINOS_OS_NAME_SIZE]; int type; struct os_ops *ops; }; int register_os(char *name, int type, struct os_ops *ops); struct os *alloc_os(char *name, int type); struct os *get_vm_os(char *type); void os_vm_init(struct vm *vm); void os_setup_vm(struct vm *vm); void os_vcpu_init(struct vcpu *vcpu); void os_vcpu_power_on(struct vcpu *vcpu, unsigned long entry); int os_create_native_vm_resource(struct vm *vm); int os_create_guest_vm_resource(struct vm *vm); #endif ================================================ FILE: kernel/include/virt/resource.h ================================================ #ifndef __MINOS_RESOURCE_H__ #define __MINOS_RESOURCE_H__ #include struct vm; int parse_vm_info_of(struct device_node *node, struct vmtag *vmtag); int create_vm_resource_of(struct vm *vm, void *data); int create_native_vm_resource_common(struct vm *vm); int vm_get_device_irq_index(struct vm *vm, struct device_node *node, uint32_t *irq, unsigned long *flags, int index); #endif ================================================ FILE: kernel/include/virt/vdev.h ================================================ #ifndef __MINOS_VDEV_H__ #define __MINOS_VDEV_H__ #include #include #include #include #include #define VDEV_NAME_SIZE (15) typedef void *(*vdev_init_t)(struct vm *vm, struct device_node *node); struct vdev { char name[VDEV_NAME_SIZE + 1]; int host; struct vm *vm; struct vmm_area *gvm_area; struct list_head list; int (*read)(struct vdev *, gp_regs *, int, unsigned long, unsigned long *); int (*write)(struct vdev *, gp_regs *, int, unsigned long, unsigned long *); void (*deinit)(struct vdev *vdev); void (*reset)(struct vdev *vdev); int (*suspend)(struct vdev *vdev); int (*resume)(struct vdev *vdev); }; struct vdev *create_host_vdev(struct vm *vm, const char *name); void vdev_release(struct vdev *vdev); void host_vdev_init(struct vm *vm, struct vdev *vdev, const char *name); int vdev_mmio_emulation(gp_regs *regs, int write, unsigned long address, unsigned long *value); int vdev_add_iomem_range(struct vdev *vdev, unsigned long base, size_t size); struct vmm_area *vdev_alloc_iomem_range(struct vdev *vdev, size_t size, int flags); struct vmm_area *vdev_get_vmm_area(struct vdev *vdev, int idx); void vdev_add(struct vdev *vdev); static int inline vdev_notify_gvm(struct vdev *vdev, uint32_t irq) { return send_virq_to_vm(vdev->vm, irq); } static int inline vdev_notify_hvm(struct vdev *vdev, uint32_t irq) { return send_virq_to_vm(get_host_vm(), irq); } #endif ================================================ FILE: kernel/include/virt/virq.h ================================================ #ifndef __MINOS_VIRQ_H__ #define __MINOS_VIRQ_H__ #include #include #include struct irqtag; #define VIRQ_STATE_INACTIVE (0x0) #define VIRQ_STATE_PENDING (0x1) #define VIRQ_STATE_ACTIVE (0x2) #define VIRQ_STATE_ACTIVE_AND_PENDING (0x3) #define VCPU_MAX_ACTIVE_VIRQS (64) #define VIRQ_INVALID_ID (0xff) #define VIRQ_ACTION_REMOVE (0x0) #define VIRQ_ACTION_ADD (0x1) #define VIRQ_ACTION_CLEAR (0x2) #define VIRQ_AFFINITY_VM_ANY (0xff) #define VIRQ_AFFINITY_VCPU_ANY (0xff) #define VM_SGI_VIRQ_NR (CONFIG_NR_SGI_IRQS) #define VM_PPI_VIRQ_NR (CONFIG_NR_PPI_IRQS) #define VM_LOCAL_VIRQ_NR (VM_SGI_VIRQ_NR + VM_PPI_VIRQ_NR) #ifndef CONFIG_HVM_SPI_VIRQ_NR #define HVM_SPI_VIRQ_NR (384) #else #define HVM_SPI_VIRQ_NR CONFIG_HVM_SPI_VIRQ_NR #endif #define HVM_SPI_VIRQ_BASE (VM_LOCAL_VIRQ_NR) #ifndef CONFIG_GVM_SPI_VIRQ_NR #define GVM_SPI_VIRQ_NR (64) #else #define GVM_SPI_VIRQ_NR CONFIG_GVM_SPI_VIRQ_NR #endif #define GVM_SPI_VIRQ_BASE (VM_LOCAL_VIRQ_NR) #define VIRQ_SPI_OFFSET(virq) ((virq) - VM_LOCAL_VIRQ_NR) #define VIRQ_SPI_NR(count) ((count) > VM_LOCAL_VIRQ_NR ? VIRQ_SPI_OFFSET((count)) : 0) #define VM_VIRQ_NR(nr) ((nr) + VM_LOCAL_VIRQ_NR) #define MAX_HVM_VIRQ (HVM_SPI_VIRQ_NR + VM_LOCAL_VIRQ_NR) #define MAX_GVM_VIRQ (GVM_SPI_VIRQ_NR + VM_LOCAL_VIRQ_NR) #define VIRQS_NEED_EXPORT_BIT (0) #define VIRQS_ENABLED_BIT (1) #define VIRQS_SUSPEND_BIT (2) #define VIRQS_HW_BIT (3) #define VIRQS_CAN_WAKEUP_BIT (4) #define VIRQS_REQUESTED_BIT (6) #define VIRQS_FIQ_BIT (7) #define VIRQS_NEED_EXPORT (1 << VIRQS_NEED_EXPORT_BIT) #define VIRQS_ENABLED (1 << VIRQS_ENABLED_BIT) #define VIRQS_SUSPEND (1 << VIRQS_SUSPEND_BIT) #define VIRQS_HW (1 << VIRQS_HW_BIT) #define VIRQS_CAN_WAKEUP (1 << VIRQS_CAN_WAKEUP_BIT) #define VIRQS_REQUESTED (1 << VIRQS_REQUESTED_BIT) #define VIRQS_FIQ (1 << VIRQS_FIQ_BIT) #define VIRQF_CAN_WAKEUP VIRQS_CAN_WAKEUP #define VIRQF_ENABLE VIRQS_ENABLED #define VIRQF_FIQ VIRQS_FIQ #define VIRQF_NEED_EXPORT VIRQS_NEED_EXPORT #define FIQ_HAS_INJECT (1 << 31) struct virq_desc { int32_t flags; uint16_t vno; uint16_t hno; uint8_t id; uint8_t state; uint8_t pr; uint8_t src; uint8_t type; uint8_t vcpu_id; uint8_t vmid; uint8_t padding; } __packed; #define VGIC_MAX_LRS 128 struct virq_struct { int nr_lrs; int last_fail_virq; atomic_t pending_virq; uint32_t active_virq; struct virq_desc local_desc[VM_LOCAL_VIRQ_NR]; unsigned long *pending_bitmap; unsigned long *active_bitmap; unsigned long lrs_bitmap[BITS_TO_LONGS(VGIC_MAX_LRS)]; }; static inline int vm_irq_count(struct vm *vm) { return vm->vspi_nr + VM_LOCAL_VIRQ_NR; } static void inline virq_set_enable(struct virq_desc *d) { d->flags |= VIRQS_ENABLED; } static void inline virq_clear_enable(struct virq_desc *d) { d->flags &= ~VIRQS_ENABLED; } static int inline virq_is_enabled(struct virq_desc *d) { return !!(d->flags & VIRQS_ENABLED); } static void inline virq_set_wakeup(struct virq_desc *d) { d->flags |= VIRQS_CAN_WAKEUP; } static void inline virq_clear_wakeup(struct virq_desc *d) { d->flags &= ~VIRQS_CAN_WAKEUP; } static int inline virq_can_wakeup(struct virq_desc *d) { return !!(d->flags & VIRQS_CAN_WAKEUP); } static void inline virq_set_suspend(struct virq_desc *d) { d->flags |= VIRQS_SUSPEND; } static void inline virq_clear_suspend(struct virq_desc *d) { d->flags &= ~VIRQS_SUSPEND; } static int inline virq_is_suspend(struct virq_desc *d) { return !!(d->flags & VIRQS_SUSPEND); } static void inline virq_set_hw(struct virq_desc *d) { d->flags |= VIRQS_HW; } static void inline virq_clear_hw(struct virq_desc *d) { d->flags &= ~VIRQS_HW; } static int inline virq_is_hw(struct virq_desc *d) { return !!(d->flags & VIRQS_HW); } static int inline virq_is_requested(struct virq_desc *d) { return !!(d->flags & VIRQS_REQUESTED); } static void inline __virq_set_fiq(struct virq_desc *d) { d->flags |= VIRQS_FIQ; } static int inline virq_is_fiq(struct virq_desc *d) { return !!(d->flags & VIRQS_FIQ); } static void inline virq_clear_fiq(struct virq_desc *d) { d->flags &= ~VIRQS_FIQ; } int virq_enable(struct vcpu *vcpu, uint32_t virq); int virq_disable(struct vcpu *vcpu, uint32_t virq); void vcpu_virq_struct_init(struct vcpu *vcpu); void vcpu_virq_struct_reset(struct vcpu *vcpu); void vm_virq_reset(struct vm *vm); void send_vsgi(struct vcpu *sender, uint32_t sgi, cpumask_t *cpumask); void clear_pending_virq(struct vcpu *vcpu, uint32_t irq); int virq_set_priority(struct vcpu *vcpu, uint32_t virq, int pr); int virq_set_type(struct vcpu *vcpu, uint32_t virq, int value); uint32_t virq_get_type(struct vcpu *vcpu, uint32_t virq); uint32_t virq_get_affinity(struct vcpu *vcpu, uint32_t virq); uint32_t virq_get_pr(struct vcpu *vcpu, uint32_t virq); uint32_t virq_get_state(struct vcpu *vcpu, uint32_t virq); int virq_can_request(struct vcpu *vcpu, uint32_t virq); int virq_need_export(struct vcpu *vcpu, uint32_t virq); uint32_t get_pending_virq(struct vcpu *vcpu); int virq_set_fiq(struct vcpu *vcpu, uint32_t virq); int send_virq_to_vcpu(struct vcpu *vcpu, uint32_t virq); int send_virq_to_vm(struct vm *vm, uint32_t virq); int vcpu_has_irq(struct vcpu *vcpu); int alloc_vm_virq(struct vm *vm); void release_vm_virq(struct vm *vm, int virq); int request_virq_affinity(struct vm *vm, uint32_t virq, uint32_t hwirq, int affinity, unsigned long flags); int request_hw_virq(struct vm *vm, uint32_t virq, uint32_t hwirq, unsigned long flags); int request_virq_pervcpu(struct vm *vm, uint32_t virq, unsigned long flags); int request_virq(struct vm *vm, uint32_t virq, unsigned long flags); struct virq_desc *get_virq_desc(struct vcpu *vcpu, uint32_t virq); static inline int alloc_hvm_virq(void) { return alloc_vm_virq(get_host_vm()); } static inline int alloc_gvm_virq(struct vm *vm) { return alloc_vm_virq(vm); } static void inline release_hvm_virq(int virq) { return release_vm_virq(get_host_vm(), virq); } static void inline release_gvm_virq(struct vm *vm, int virq) { return release_vm_virq(vm, virq); } struct virq_chip *alloc_virq_chip(void); int virqchip_get_virq_state(struct vcpu *vcpu, struct virq_desc *virq); void virqchip_send_virq(struct vcpu *vcpu, struct virq_desc *virq); void virqchip_update_virq(struct vcpu *vcpu, struct virq_desc *virq, int action); #endif ================================================ FILE: kernel/include/virt/virq_chip.h ================================================ #ifndef __VIRQ_CHIP_H__ #define __VIRQ_CHIP_H__ #include #include struct vcpu; #define VIRQCHIP_F_HW_VIRT (1 << 0) struct virq_chip { int (*exit_from_guest)(struct vcpu *vcpu, void *data); int (*enter_to_guest)(struct vcpu *vcpu, void *data); int (*generate_virq)(uint32_t *array, int virq); int (*xlate)(struct device_node *node, uint32_t *intspec, unsigned int intsize, uint32_t *hwirq, unsigned long *type); int (*send_virq)(struct vcpu *vcpu, struct virq_desc *virq); int (*get_virq_state)(struct vcpu *vcpu, struct virq_desc *virq); int (*update_virq)(struct vcpu *vcpu, struct virq_desc *virq, int action); int (*vcpu_init)(struct vcpu *vcpu, void *pdata, unsigned long flags); void *inc_pdata; unsigned long flags; }; struct virq_chip *alloc_virq_chip(void); int virqchip_get_virq_state(struct vcpu *vcpu, struct virq_desc *virq); void virqchip_send_virq(struct vcpu *vcpu, struct virq_desc *virq); void virqchip_update_virq(struct vcpu *vcpu, struct virq_desc *virq, int action); #endif ================================================ FILE: kernel/include/virt/virt.h ================================================ #ifndef __MINOS_VIRT_H__ #define __MINOS_VIRT_H__ #include struct mm_struct; int virt_init(void); void start_all_vm(void); void flush_all_tlb_mm(struct mm_struct *mm); #endif ================================================ FILE: kernel/include/virt/virtio.h ================================================ #ifndef __MINOS_VIRTIO_H__ #define __MINOS_VIRTIO_H__ #include struct vm; struct virtio_device { struct vdev vdev; void *iomem; }; int virtio_mmio_init(struct vm *vm, unsigned long gbase, size_t size, unsigned long *hbase); #endif ================================================ FILE: kernel/include/virt/vm.h ================================================ /* * created by Le MIn 2017/12/09 */ #ifndef _MINOS_VM_H_ #define _MINOS_VM_H_ #include #include #include #include #include #include #include #include #include #define VM_MAX_VCPU CONFIG_NR_CPUS #define VM_STATE_OFFLINE (0) #define VM_STATE_FREEZEING (1) #define VM_STATE_ONLINE (2) #define VM_STATE_SUSPEND (3) #define VCPU_MAX_LOCAL_IRQS (32) #define CONFIG_VCPU_MAX_ACTIVE_IRQS (16) #define VCPU_NAME_SIZE (64) #define VCPU_KICK_REASON_NONE 0x0 #define VCPU_KICK_REASON_HIRQ 0x1 #define VCPU_KICK_REASON_VIRQ 0x2 struct os; struct vm; struct virq_struct; struct virq_chip; struct device_node; extern struct list_head vm_list; extern struct list_head mem_list; #define VCPU_STATE_RUNNING TASK_STATE_RUNNING #define VCPU_STATE_IDLE TASK_STATE_WAIT_EVENT #define VCPU_STATE_STOP TASK_STATE_STOP #define VCPU_STATE_SUSPEND TASK_STATE_SUSPEND enum { IN_GUEST_MODE, OUTSIDE_GUEST_MODE, IN_ROOT_MODE, OUTSIDE_ROOT_MODE, }; struct vcpu { uint32_t vcpu_id; struct vm *vm; struct task *task; struct vcpu *next; volatile int mode; /* * member to record the irq list which the * vcpu is handling now */ struct virq_struct *virq_struct; struct event vcpu_event; struct vmcs *vmcs; int vmcs_irq; /* * context for this vcpu. */ void **context; } __cache_line_align; struct vm { int vmid; uint32_t vcpu_nr; int state; unsigned long flags; uint32_t vcpu_affinity[VM_MAX_VCPU]; void *entry_point; void *setup_data; void *load_address; int native; struct ramdisk_file *kernel_file; struct ramdisk_file *dtb_file; struct ramdisk_file *initrd_file; char name[VM_NAME_SIZE]; struct vcpu **vcpus; struct list_head vcpu_list; struct mm_struct mm; struct os *os; struct list_head vm_list; struct device_node *dev_node; /* the device node in dts */ unsigned long time_offset; struct list_head vdev_list; uint32_t vspi_nr; struct virq_desc *vspi_desc; unsigned long *vspi_map; struct virq_chip *virq_chip; uint32_t vtimer_virq; void *vmcs; void *hvm_vmcs; void *resource; void *os_data; void *arch_data; struct vm_iommu iommu; } __align(sizeof(unsigned long)); #define vm_name(vm) devnode_name(vm->dev_node) extern struct vm *vms[CONFIG_MAX_VM]; extern int total_vms; #define for_each_vm(vm) \ list_for_each_entry(vm, &vm_list, vm_list) #define vm_for_each_vcpu(vm, vcpu) \ for (vcpu = vm->vcpus[0]; vcpu != NULL; vcpu = vcpu->next) #define current_vcpu (struct vcpu *)current->pdata static int inline get_vcpu_id(struct vcpu *vcpu) { return vcpu->vcpu_id; } static int inline get_vmid(struct vcpu *vcpu) { return (vcpu->vm->vmid); } static int inline vcpu_affinity(struct vcpu *vcpu) { return vcpu->task->affinity; } static inline struct vm *vcpu_to_vm(struct vcpu *vcpu) { return vcpu->vm; } static inline struct vcpu *task_to_vcpu(struct task *task) { return (struct vcpu *)task->pdata; } static inline struct vcpu *get_current_vcpu(void) { return task_to_vcpu(get_current_task()); } static inline struct vm *task_to_vm(struct task *task) { struct vcpu *vcpu = (struct vcpu *)task->pdata; return vcpu->vm; } static inline struct vm* get_current_vm(void) { return task_to_vm(get_current_task()); } struct vcpu *get_vcpu_in_vm(struct vm *vm, uint32_t vcpu_id); struct vcpu *get_vcpu_by_id(uint32_t vmid, uint32_t vcpu_id); struct vcpu *create_idle_vcpu(void); int vm_vcpus_init(struct vm *vm); int vcpu_idle(struct vcpu *vcpu); int vcpu_suspend(struct vcpu *vcpu, gp_regs *c, uint32_t state, unsigned long entry); int vcpu_off(struct vcpu *vcpu); int vcpu_power_on(struct vcpu *caller, unsigned long affinity, unsigned long entry, unsigned long unsed); int vcpu_power_off(struct vcpu *vcpu, int timeout); int kick_vcpu(struct vcpu *vcpu, int preempt); struct vm *create_vm(struct vmtag *vme, struct device_node *node); int create_guest_vm(struct vmtag *tag); void destroy_vm(struct vm *vm); struct vm *get_host_vm(void); static inline struct vm *get_vm_by_id(uint32_t vmid) { if (unlikely(vmid >= CONFIG_MAX_VM) || unlikely(vmid == 0)) return NULL; return vms[vmid]; } static inline int vm_is_host_vm(struct vm *vm) { return !!(vm->flags & VM_FLAGS_HOST); } static inline int vm_is_32bit(struct vm *vm) { return vm->flags & VM_FLAGS_32BIT; } static inline int vm_is_native(struct vm *vm) { return !!(vm->flags & VM_FLAGS_NATIVE); } static inline int vm_id(struct vm *vm) { return vm->vmid; } int create_vm_mmap(int vmid, unsigned long offset, unsigned long size, unsigned long *addr); int vm_create_host_vdev(struct vm *vm); int request_vm_virqs(struct vm *vm, int base, int nr); void arch_init_vcpu(struct vcpu *vcpu, void *entry, void *arg); static inline int check_vcpu_state(struct vcpu *vcpu, int state) { return (vcpu->task->state == state); } static inline int check_vm_state(struct vm *vm, int state) { return (vm->state == state); } int start_native_vm(struct vm *vm); int start_guest_vm(struct vm *vm); void vcpu_dump(struct vcpu *vcpu, gp_regs *regs); void vcpu_fault(struct vcpu *vcpu, gp_regs *regs); #endif ================================================ FILE: kernel/include/virt/vm_mmap.h ================================================ #ifndef __MINOS_VM_MMAP_H__ #define __MINOS_VM_MMAP_H__ #include /* * guest vm memory map * * |-------------------------| -- 0x00000000 * | 2G IO MEM | * |-------------------------| -- 0x80000000 * | | * | 126G DRAM/NORMAL MEM | * |-------------------------| -- 0x2000000000 * | 2G GUEST VM IO MMAP | * |-------------------------| -- 0x2080000000 * | GUEST VM MMAP | * |-------------------------| -- 0x10000000000 * */ #define VMM_VIRT_MAX (1UL << 40) #define GVM_PHYSIC_MEM_RANGE (1UL << CONFIG_PLATFORM_ADDRESS_RANGE) #define GVM_PHYSIC_MEM_START (0x0000000000000000UL) #define GVM_IO_MEM_START (0x00000000UL) #define GVM_IO_MEM_SIZE (0x80000000UL) #define GVM_NORMAL_MEM_START (GVM_IO_MEM_START + GVM_IO_MEM_SIZE) #define GVM_NORMAL_MEM_SIZE (126 * 1024 * 1024 * 1024UL) #define GVM_NORMAL_MEM_END (GVM_NORMAL_MEM_START + GVM_NORMAL_MEM_SIZE) #define HVM_IO_MMAP_START (GVM_NORMAL_MEM_END) #define HVM_IO_MMAP_SIZE (2 * 1024 * 1024 * 1024UL) #define HVM_NORMAL_MMAP_START (HVM_IO_MMAP_START + HVM_IO_MMAP_SIZE) #define HVM_NORMAL_MMAP_SIZE (GVM_PHYSIC_MEM_RANGE - HVM_NORMAL_MMAP_START) #define GVM_IOMEM_BASE(addr) (addr - CONFIG_PLATFORM_IO_BASE + GVM_IO_MEM_START) #define GVM_MEM_BASE(addr) (addr - CONFIG_PLATFORM_DRAM_BASE + GVM_NORMAL_MEM_START) #endif ================================================ FILE: kernel/include/virt/vm_pm.h ================================================ #ifndef __MINOS_VM_PM_H__ #define __MINOS_VM_PM_H__ #include #define VM_PM_ACTION_BY_SELF (1 << 0) #define VM_PM_ACTION_BY_MVM (1 << 1) #define VM_PM_ACTION_BY_HOST (1 << 2) #define VM_PM_FLAGS_DESTROY (1 << 8) int vm_reset(int vmid, void *args, int byself); int reboot_native_vm(struct vm *vm); int vm_power_off(int vmid, void *arg, int byself); int shutdown_native_vm(struct vm *vm); int vm_suspend(int vmid); static inline bool pm_action_by_self(int flags) { return !!(flags & VM_PM_ACTION_BY_SELF); } static inline bool pm_action_by_mvm(int flags) { return !!(flags & VM_PM_ACTION_BY_MVM); } static inline bool pm_action_by_host(int flags) { return !!(flags & VM_PM_ACTION_BY_HOST); } static inline char *pm_action_caller(int flags) { if (pm_action_by_self(flags)) return "itself"; else if (pm_action_by_mvm(flags)) return "mvm"; else return "host"; } int send_vm_shutdown_request(struct vm *vm); int send_vm_reboot_request(struct vm *vm); int power_up_guest_vm(int vmid); void destroy_guest_vm(struct vm *vm); #endif ================================================ FILE: kernel/include/virt/vmbox.h ================================================ #ifndef __MINOS_VMBOX_H__ #define __MINOS_VMBOX_H__ #include #include #define VMBOX_VRING_ALGIN_SIZE 8 #define VMBOX_DEV_STAT_ONLINE 0x1 #define VMBOX_IPC_ALL_ENTRY_SIZE 0x100 struct vmbox_device { int devid; int is_backend; int state; struct vm *vm; struct vmbox *vmbox; uint32_t vring_virq; uint32_t ipc_virq; uint32_t ipc_type; spinlock_t lock; unsigned long iomem; size_t iomem_size; struct vmbox_controller *vc; struct vmbox_device *bro; }; /* * owner : owner[0] is the server vm and the * owner[1] is the client vm * devid : device id and vendor id of this device * vmbox_reg_base : the iobase of this controller */ struct vmbox { int id; char name[32]; uint32_t owner[2]; uint32_t devid[2]; void *shmem; size_t shmem_size; uint32_t vqs; uint32_t vring_num; uint32_t vring_size; unsigned long flags; struct vmbox_device *devices[2]; }; #define VMBOX_F_PLATFORM_DEV (1 << 0) struct vmbox_hook_ops { int (*vmbox_init)(struct vmbox *vmbox); int (*vmbox_be_init)(struct vm *vm, struct vmbox *vmbox, struct vmbox_device *vdev); int (*vmbox_fe_init)(struct vm *vm, struct vmbox *vmbox, struct vmbox_device *vdev); }; struct vmbox_controller { void *va; struct vm *vm; int dev_cnt; int status; uint32_t virq; uint32_t irq_state; uint32_t dev_state; struct list_head list; struct vdev vdev; struct vmbox_device *devices[32]; }; #define vdev_to_vmbox_con(dev) \ container_of(dev, struct vmbox_controller, vdev) /* * below are the defination of the vmbox controller * and device register map, each controller will have * a 4K IO memory space, 0x0-0xff is for controller * itself, and 0x100 - 0xfff is for the vmbox devices */ #define VMBOX_DEVICE_MAGIC 0xabcdef00 #define VMBOX_CON_DEV_STAT 0x00 /* RO state of each device */ #define VMBOX_CON_ONLINE 0x04 /* WO to inform the controller is online */ #define VMBOX_CON_INT_STATUS 0x08 /* RO virq will send by hypervisor */ #define VMBOX_CON_INT_TYPE_DEV_ONLINE (1 << 0) #define VMBOX_CON_DEV_BASE 0x100 #define VMBOX_CON_DEV_SIZE 0x40 #define VMBOX_DEV_ID 0x00 /* RO */ #define VMBOX_DEV_VQS 0x04 /* RO */ #define VMBOX_DEV_VRING_NUM 0X08 /* RO */ #define VMBOX_DEV_VRING_SIZE 0x0c /* RO */ #define VMBOX_DEV_VRING_BASE_HI 0x10 /* RO */ #define VMBOX_DEV_VRING_BASE_LOW 0x14 /* RO */ #define VMBOX_DEV_MEM_SIZE 0x18 #define VMBOX_DEV_DEVICE_ID 0x1c /* RO */ #define VMBOX_DEV_VENDOR_ID 0x20 /* RO */ #define VMBOX_DEV_VRING_IRQ 0x24 /* RO */ #define VMBOX_DEV_IPC_IRQ 0x28 /* RO */ #define VMBOX_DEV_VRING_EVENT 0x2c /* WO trigger a vring event */ #define VMBOX_DEV_IPC_EVENT 0x30 /* WO trigger a config event */ #define VMBOX_DEV_VDEV_ONLINE 0x34 /* only for client device */ #define VMBOX_DEV_IPC_TYPE 0x38 /* read and clear */ #define VMBOX_DEV_IPC_COUNT 32 int vmbox_init(struct vm *vm); int register_vmbox_hook(char *name, struct vmbox_hook_ops *ops); int of_create_vmbox(struct device_node *node); int vmbox_register_platdev(struct vmbox_device *vdev, void *dtb, char *type); int create_vmbox_controller(struct vm *vm); #endif ================================================ FILE: kernel/include/virt/vmcs.h ================================================ #ifndef __VMCS_H__ #define __VMCS_H__ #include struct vmcs { volatile uint32_t vcpu_id; volatile uint32_t trap_type; volatile uint32_t trap_reason; volatile int32_t trap_ret; volatile unsigned long trap_data; volatile unsigned long trap_result; volatile uint64_t host_index; volatile uint64_t guest_index; volatile unsigned long data[0]; } __align(1024); #define VMCS_DATA_SIZE (1024 - 48) #define VMCS_SIZE(nr) PAGE_BALIGN(nr * sizeof(struct vmcs)) enum vm_trap_type { VMTRAP_TYPE_MMIO = 0, VMTRAP_TYPE_COMMON, VMTRAP_TYPE_UNKNOWN, }; enum vm_trap_reason { VMTRAP_REASON_READ = 0, VMTRAP_REASON_WRITE, VMTRAP_REASON_CONFIG, VMTRAP_REASON_REBOOT, VMTRAP_REASON_SHUTDOWN, VMTRAP_REASON_VM_SUSPEND, VMTRAP_REASON_VM_RESUMED, VMTRAP_REASON_WDT_TIMEOUT, VMTRAP_REASON_GET_TIME, VMTRAP_REASON_UNKNOWN, }; int vm_create_vmcs_irq(struct vm *vm, int vcpu_id); unsigned long vm_create_vmcs(struct vm *vm); int setup_vmcs_data(void *data, size_t size); int __vcpu_trap(uint32_t type, uint32_t reason, unsigned long data, unsigned long *ret, int nonblock); static inline int trap_vcpu(uint32_t type, uint32_t reason, unsigned long data, unsigned long *ret) { return __vcpu_trap(type, reason, data, ret, 0); } static inline int trap_vcpu_nonblock(uint32_t type, uint32_t reason, unsigned long data, unsigned long *ret) { return __vcpu_trap(type, reason, data, ret, 1); } static inline int trap_mmio_read(unsigned long addr, unsigned long *value) { return __vcpu_trap(VMTRAP_TYPE_MMIO, VMTRAP_REASON_READ, addr, value, 0); } static inline int trap_mmio_read_nonblock(unsigned long addr, unsigned long *value) { return __vcpu_trap(VMTRAP_TYPE_MMIO, VMTRAP_REASON_READ, addr, value, 1); } static inline int trap_mmio_write(unsigned long addr, unsigned long *value) { return __vcpu_trap(VMTRAP_TYPE_MMIO, VMTRAP_REASON_WRITE, addr, value, 0); } static inline int trap_mmio_write_nonblock(unsigned long addr, unsigned long *value) { return __vcpu_trap(VMTRAP_TYPE_MMIO, VMTRAP_REASON_WRITE, addr, value, 1); } #endif ================================================ FILE: kernel/include/virt/vmm.h ================================================ #ifndef __MINOS_VIRT_VMM_H__ #define __MINOS_VIRT_VMM_H__ #include #include #include #include #include struct vm; struct mem_block { uint32_t bfn; struct mem_block *next; }; struct vm_iommu { /* private information for iommu drivers */ void *priv; const struct iommu_ops *ops; /* list of device nodes assigned to this vm */ struct list_head nodes; }; /* * pstart - if this area is mapped as continous the pstart * is the phsical address of this vmm_area */ struct vmm_area { unsigned long start; unsigned long end; unsigned long pstart; int flags; int vmid; /* 0 - for self other for VM */ struct list_head list; struct mem_block *b_head; /* if this vmm_area is belong to VDEV, this will link * to the next vmm_area of the VDEV */ struct vmm_area *next; }; struct mm_struct { void *pgdp; spinlock_t lock; /* * vmm_area_free : list to all the free vmm_area * vmm_area_used : list to all the used vmm_area * lock : spin lock for vmm_area allocate */ struct list_head vmm_area_free; struct list_head vmm_area_used; }; int vm_mm_init(struct vm *vm); int vm_mm_struct_init(struct vm *vm); int copy_from_guest(void *target, void __guest *source, size_t size); int alloc_vm_memory(struct vm *vm); void release_vm_memory(struct vm *vm); int create_guest_mapping(struct mm_struct *mm, unsigned long vir, unsigned long phy, size_t size, unsigned long flags); struct vmm_area *vm_mmap(struct vm *vm, unsigned long offset, unsigned long size); void *vm_alloc_pages(struct vm *vm, int pages); unsigned long create_hvm_shmem_map(struct vm *vm, unsigned long phy, uint32_t size); void destroy_hvm_iomem_map(unsigned long vir, uint32_t size); int create_early_pmd_mapping(unsigned long vir, unsigned long phy); struct vmm_area *split_vmm_area(struct mm_struct *mm, unsigned long base, unsigned long size, int flags); struct vmm_area *request_vmm_area(struct mm_struct *mm, unsigned long base, unsigned long pbase, size_t size, int flags); int map_vmm_area(struct mm_struct *mm, struct vmm_area *va, unsigned long pbase); struct vmm_area *alloc_free_vmm_area(struct mm_struct *mm, size_t size, unsigned long mask, int flags); int unmap_vmm_area(struct mm_struct *mm, struct vmm_area *va); struct mem_block *vmm_alloc_memblock(void); int vmm_free_memblock(struct mem_block *mb); int vmm_has_enough_memory(size_t size); int release_vmm_area(struct mm_struct *mm, struct vmm_area *va); int translate_guest_ipa(struct mm_struct *mm, unsigned long offset, unsigned long *pa); void free_shmem(void *addr); void *alloc_shmem(int pages); #endif ================================================ FILE: kernel/include/virt/vmodule.h ================================================ #ifndef _MINOS_MODULE_H_ #define _MINOS_MODULE_H_ #include #include #include struct vcpu; #define INVALID_MODULE_ID (-1) struct vmodule { char name[32]; int id; struct list_head list; uint32_t context_size; /* * below member usually used for vcpu * * state_save - save the context when sched out * state_restore - restore the context when sched in * state_init - init the state when the vcpu is create * state_deinit - destroy the state when the vcpu is releas * state_reset - reset the state when the vcpu is reset * state_stop - stop the state when the vcpu is stop * state_suspend - suspend the state when the vcpu suspend * state_resume - resume the state when the vcpu is resume */ void (*state_save)(struct vcpu *vcpu, void *context); void (*state_restore)(struct vcpu *vcpu, void *context); void (*state_init)(struct vcpu *vcpu, void *context); void (*state_deinit)(struct vcpu *vcpu, void *context); void (*state_reset)(struct vcpu *vcpu, void *context); void (*state_stop)(struct vcpu *vcpu, void *context); void (*state_suspend)(struct vcpu *vcpu, void *context); void (*state_resume)(struct vcpu *vcpu, void *context); void (*state_dump)(struct vcpu *vcpu, void *context); }; typedef int (*vmodule_init_fn)(struct vmodule *); int vcpu_vmodules_init(struct vcpu *vcpu); int vcpu_vmodules_deinit(struct vcpu *vcpu); void reset_vcpu_vmodule_state(struct vcpu *vcpu); void save_vcpu_vmodule_state(struct vcpu *vcpu); void restore_vcpu_vmodule_state(struct vcpu *vcpu); void suspend_vcpu_vmodule_state(struct vcpu *vcpu); void resume_vcpu_vmodule_state(struct vcpu *vcpu); void stop_vcpu_vmodule_state(struct vcpu *vcpu); void dump_vcpu_vmodule_state(struct vcpu *vcpu); void *get_vmodule_data_by_id(struct vcpu *vcpu, int id); int register_vcpu_vmodule(const char *name, vmodule_init_fn fn); #endif ================================================ FILE: kernel/libs/Kconfig ================================================ menu "System libary config" source "libs/shell_command/Kconfig" endmenu ================================================ FILE: kernel/libs/Makefile ================================================ obj-$(CONFIG_SHELL) += shell_command/ obj-$(CONFIG_DEVICE_TREE) += libfdt/ ================================================ FILE: kernel/libs/libfdt/Makefile ================================================ obj-y += fdt_addresses.o fdt.o fdt_empty_tree.o fdt_overlay.o \ fdt_ro.o fdt_rw.o fdt_strerror.o fdt_sw.o fdt_wip.o ================================================ FILE: kernel/libs/libfdt/fdt.c ================================================ /* * libfdt - Flat Device Tree manipulation * Copyright (C) 2006 David Gibson, IBM Corporation. * * libfdt is dual licensed: you can use it either under the terms of * the GPL, or the BSD license, at your option. * * a) This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301 USA * * Alternatively, * * b) Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "libfdt_internal.h" /* * Minimal sanity check for a read-only tree. fdt_ro_probe_() checks * that the given buffer contains what appears to be a flattened * device tree with sane information in its header. */ int fdt_ro_probe_(const void *fdt) { if (fdt_magic(fdt) == FDT_MAGIC) { /* Complete tree */ if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) return -FDT_ERR_BADVERSION; if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION) return -FDT_ERR_BADVERSION; } else if (fdt_magic(fdt) == FDT_SW_MAGIC) { /* Unfinished sequential-write blob */ if (fdt_size_dt_struct(fdt) == 0) return -FDT_ERR_BADSTATE; } else { return -FDT_ERR_BADMAGIC; } return 0; } static int check_off_(uint32_t hdrsize, uint32_t totalsize, uint32_t off) { return (off >= hdrsize) && (off <= totalsize); } static int check_block_(uint32_t hdrsize, uint32_t totalsize, uint32_t base, uint32_t size) { if (!check_off_(hdrsize, totalsize, base)) return 0; /* block start out of bounds */ if ((base + size) < base) return 0; /* overflow */ if (!check_off_(hdrsize, totalsize, base + size)) return 0; /* block end out of bounds */ return 1; } size_t fdt_header_size_(uint32_t version) { if (version <= 1) return FDT_V1_SIZE; else if (version <= 2) return FDT_V2_SIZE; else if (version <= 3) return FDT_V3_SIZE; else if (version <= 16) return FDT_V16_SIZE; else return FDT_V17_SIZE; } int fdt_check_header(const void *fdt) { size_t hdrsize; if (fdt_magic(fdt) != FDT_MAGIC) return -FDT_ERR_BADMAGIC; hdrsize = fdt_header_size(fdt); if ((fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) || (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION)) return -FDT_ERR_BADVERSION; if (fdt_version(fdt) < fdt_last_comp_version(fdt)) return -FDT_ERR_BADVERSION; if ((fdt_totalsize(fdt) < hdrsize) || (fdt_totalsize(fdt) > INT_MAX)) return -FDT_ERR_TRUNCATED; /* Bounds check memrsv block */ if (!check_off_(hdrsize, fdt_totalsize(fdt), fdt_off_mem_rsvmap(fdt))) return -FDT_ERR_TRUNCATED; /* Bounds check structure block */ if (fdt_version(fdt) < 17) { if (!check_off_(hdrsize, fdt_totalsize(fdt), fdt_off_dt_struct(fdt))) return -FDT_ERR_TRUNCATED; } else { if (!check_block_(hdrsize, fdt_totalsize(fdt), fdt_off_dt_struct(fdt), fdt_size_dt_struct(fdt))) return -FDT_ERR_TRUNCATED; } /* Bounds check strings block */ if (!check_block_(hdrsize, fdt_totalsize(fdt), fdt_off_dt_strings(fdt), fdt_size_dt_strings(fdt))) return -FDT_ERR_TRUNCATED; return 0; } const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len) { unsigned absoffset = offset + fdt_off_dt_struct(fdt); if ((absoffset < offset) || ((absoffset + len) < absoffset) || (absoffset + len) > fdt_totalsize(fdt)) return NULL; if (fdt_version(fdt) >= 0x11) if (((offset + len) < offset) || ((offset + len) > fdt_size_dt_struct(fdt))) return NULL; return fdt_offset_ptr_(fdt, offset); } uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset) { const fdt32_t *tagp, *lenp; uint32_t tag; int offset = startoffset; const char *p; *nextoffset = -FDT_ERR_TRUNCATED; tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE); if (!tagp) return FDT_END; /* premature end */ tag = fdt32_to_cpu(*tagp); offset += FDT_TAGSIZE; *nextoffset = -FDT_ERR_BADSTRUCTURE; switch (tag) { case FDT_BEGIN_NODE: /* skip name */ do { p = fdt_offset_ptr(fdt, offset++, 1); } while (p && (*p != '\0')); if (!p) return FDT_END; /* premature end */ break; case FDT_PROP: lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp)); if (!lenp) return FDT_END; /* premature end */ /* skip-name offset, length and value */ offset += sizeof(struct fdt_property) - FDT_TAGSIZE + fdt32_to_cpu(*lenp); if (fdt_version(fdt) < 0x10 && fdt32_to_cpu(*lenp) >= 8 && ((offset - fdt32_to_cpu(*lenp)) % 8) != 0) offset += 4; break; case FDT_END: case FDT_END_NODE: case FDT_NOP: break; default: return FDT_END; } if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset)) return FDT_END; /* premature end */ *nextoffset = FDT_TAGALIGN(offset); return tag; } int fdt_check_node_offset_(const void *fdt, int offset) { if ((offset < 0) || (offset % FDT_TAGSIZE) || (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE)) return -FDT_ERR_BADOFFSET; return offset; } int fdt_check_prop_offset_(const void *fdt, int offset) { if ((offset < 0) || (offset % FDT_TAGSIZE) || (fdt_next_tag(fdt, offset, &offset) != FDT_PROP)) return -FDT_ERR_BADOFFSET; return offset; } int fdt_next_node(const void *fdt, int offset, int *depth) { int nextoffset = 0; uint32_t tag; if (offset >= 0) if ((nextoffset = fdt_check_node_offset_(fdt, offset)) < 0) return nextoffset; do { offset = nextoffset; tag = fdt_next_tag(fdt, offset, &nextoffset); switch (tag) { case FDT_PROP: case FDT_NOP: break; case FDT_BEGIN_NODE: if (depth) (*depth)++; break; case FDT_END_NODE: if (depth && ((--(*depth)) < 0)) return nextoffset; break; case FDT_END: if ((nextoffset >= 0) || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth)) return -FDT_ERR_NOTFOUND; else return nextoffset; } } while (tag != FDT_BEGIN_NODE); return offset; } int fdt_first_subnode(const void *fdt, int offset) { int depth = 0; offset = fdt_next_node(fdt, offset, &depth); if (offset < 0 || depth != 1) return -FDT_ERR_NOTFOUND; return offset; } int fdt_next_subnode(const void *fdt, int offset) { int depth = 1; /* * With respect to the parent, the depth of the next subnode will be * the same as the last. */ do { offset = fdt_next_node(fdt, offset, &depth); if (offset < 0 || depth < 1) return -FDT_ERR_NOTFOUND; } while (depth > 1); return offset; } const char *fdt_find_string_(const char *strtab, int tabsize, const char *s) { int len = strlen(s) + 1; const char *last = strtab + tabsize - len; const char *p; for (p = strtab; p <= last; p++) if (memcmp(p, s, len) == 0) return p; return NULL; } int fdt_move(const void *fdt, void *buf, int bufsize) { FDT_RO_PROBE(fdt); if (fdt_totalsize(fdt) > bufsize) return -FDT_ERR_NOSPACE; memmove(buf, fdt, fdt_totalsize(fdt)); return 0; } ================================================ FILE: kernel/libs/libfdt/fdt_addresses.c ================================================ /* * libfdt - Flat Device Tree manipulation * Copyright (C) 2014 David Gibson * Copyright (C) 2018 embedded brains GmbH * * libfdt is dual licensed: you can use it either under the terms of * the GPL, or the BSD license, at your option. * * a) This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301 USA * * Alternatively, * * b) Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "libfdt_internal.h" static int fdt_cells(const void *fdt, int nodeoffset, const char *name) { const fdt32_t *c; int val; int len; c = fdt_getprop(fdt, nodeoffset, name, &len); if (!c) return 2; if (len != sizeof(*c)) return -FDT_ERR_BADNCELLS; val = fdt32_to_cpu(*c); if ((val <= 0) || (val > FDT_MAX_NCELLS)) return -FDT_ERR_BADNCELLS; return val; } int fdt_address_cells(const void *fdt, int nodeoffset) { return fdt_cells(fdt, nodeoffset, "#address-cells"); } int fdt_size_cells(const void *fdt, int nodeoffset) { return fdt_cells(fdt, nodeoffset, "#size-cells"); } ================================================ FILE: kernel/libs/libfdt/fdt_empty_tree.c ================================================ /* * libfdt - Flat Device Tree manipulation * Copyright (C) 2012 David Gibson, IBM Corporation. * * libfdt is dual licensed: you can use it either under the terms of * the GPL, or the BSD license, at your option. * * a) This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301 USA * * Alternatively, * * b) Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "libfdt_internal.h" int fdt_create_empty_tree(void *buf, int bufsize) { int err; err = fdt_create(buf, bufsize); if (err) return err; err = fdt_finish_reservemap(buf); if (err) return err; err = fdt_begin_node(buf, ""); if (err) return err; err = fdt_end_node(buf); if (err) return err; err = fdt_finish(buf); if (err) return err; return fdt_open_into(buf, buf, bufsize); } ================================================ FILE: kernel/libs/libfdt/fdt_overlay.c ================================================ /* * libfdt - Flat Device Tree manipulation * Copyright (C) 2016 Free Electrons * Copyright (C) 2016 NextThing Co. * * libfdt is dual licensed: you can use it either under the terms of * the GPL, or the BSD license, at your option. * * a) This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301 USA * * Alternatively, * * b) Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "libfdt_internal.h" /** * overlay_get_target_phandle - retrieves the target phandle of a fragment * @fdto: pointer to the device tree overlay blob * @fragment: node offset of the fragment in the overlay * * overlay_get_target_phandle() retrieves the target phandle of an * overlay fragment when that fragment uses a phandle (target * property) instead of a path (target-path property). * * returns: * the phandle pointed by the target property * 0, if the phandle was not found * -1, if the phandle was malformed */ static uint32_t overlay_get_target_phandle(const void *fdto, int fragment) { const fdt32_t *val; int len; val = fdt_getprop(fdto, fragment, "target", &len); if (!val) return 0; if ((len != sizeof(*val)) || (fdt32_to_cpu(*val) == (uint32_t)-1)) return (uint32_t)-1; return fdt32_to_cpu(*val); } /** * overlay_get_target - retrieves the offset of a fragment's target * @fdt: Base device tree blob * @fdto: Device tree overlay blob * @fragment: node offset of the fragment in the overlay * @pathp: pointer which receives the path of the target (or NULL) * * overlay_get_target() retrieves the target offset in the base * device tree of a fragment, no matter how the actual targetting is * done (through a phandle or a path) * * returns: * the targetted node offset in the base device tree * Negative error code on error */ static int overlay_get_target(const void *fdt, const void *fdto, int fragment, char const **pathp) { uint32_t phandle; const char *path = NULL; int path_len = 0, ret; /* Try first to do a phandle based lookup */ phandle = overlay_get_target_phandle(fdto, fragment); if (phandle == (uint32_t)-1) return -FDT_ERR_BADPHANDLE; /* no phandle, try path */ if (!phandle) { /* And then a path based lookup */ path = fdt_getprop(fdto, fragment, "target-path", &path_len); if (path) ret = fdt_path_offset(fdt, path); else ret = path_len; } else ret = fdt_node_offset_by_phandle(fdt, phandle); /* * If we haven't found either a target or a * target-path property in a node that contains a * __overlay__ subnode (we wouldn't be called * otherwise), consider it a improperly written * overlay */ if (ret < 0 && path_len == -FDT_ERR_NOTFOUND) ret = -FDT_ERR_BADOVERLAY; /* return on error */ if (ret < 0) return ret; /* return pointer to path (if available) */ if (pathp) *pathp = path ? path : NULL; return ret; } /** * overlay_phandle_add_offset - Increases a phandle by an offset * @fdt: Base device tree blob * @node: Device tree overlay blob * @name: Name of the property to modify (phandle or linux,phandle) * @delta: offset to apply * * overlay_phandle_add_offset() increments a node phandle by a given * offset. * * returns: * 0 on success. * Negative error code on error */ static int overlay_phandle_add_offset(void *fdt, int node, const char *name, uint32_t delta) { const fdt32_t *val; uint32_t adj_val; int len; val = fdt_getprop(fdt, node, name, &len); if (!val) return len; if (len != sizeof(*val)) return -FDT_ERR_BADPHANDLE; adj_val = fdt32_to_cpu(*val); if ((adj_val + delta) < adj_val) return -FDT_ERR_NOPHANDLES; adj_val += delta; if (adj_val == (uint32_t)-1) return -FDT_ERR_NOPHANDLES; return fdt_setprop_inplace_u32(fdt, node, name, adj_val); } /** * overlay_adjust_node_phandles - Offsets the phandles of a node * @fdto: Device tree overlay blob * @node: Offset of the node we want to adjust * @delta: Offset to shift the phandles of * * overlay_adjust_node_phandles() adds a constant to all the phandles * of a given node. This is mainly use as part of the overlay * application process, when we want to update all the overlay * phandles to not conflict with the overlays of the base device tree. * * returns: * 0 on success * Negative error code on failure */ static int overlay_adjust_node_phandles(void *fdto, int node, uint32_t delta) { int child; int ret; ret = overlay_phandle_add_offset(fdto, node, "phandle", delta); if (ret && ret != -FDT_ERR_NOTFOUND) return ret; ret = overlay_phandle_add_offset(fdto, node, "linux,phandle", delta); if (ret && ret != -FDT_ERR_NOTFOUND) return ret; fdt_for_each_subnode(child, fdto, node) { ret = overlay_adjust_node_phandles(fdto, child, delta); if (ret) return ret; } return 0; } /** * overlay_adjust_local_phandles - Adjust the phandles of a whole overlay * @fdto: Device tree overlay blob * @delta: Offset to shift the phandles of * * overlay_adjust_local_phandles() adds a constant to all the * phandles of an overlay. This is mainly use as part of the overlay * application process, when we want to update all the overlay * phandles to not conflict with the overlays of the base device tree. * * returns: * 0 on success * Negative error code on failure */ static int overlay_adjust_local_phandles(void *fdto, uint32_t delta) { /* * Start adjusting the phandles from the overlay root */ return overlay_adjust_node_phandles(fdto, 0, delta); } /** * overlay_update_local_node_references - Adjust the overlay references * @fdto: Device tree overlay blob * @tree_node: Node offset of the node to operate on * @fixup_node: Node offset of the matching local fixups node * @delta: Offset to shift the phandles of * * overlay_update_local_nodes_references() update the phandles * pointing to a node within the device tree overlay by adding a * constant delta. * * This is mainly used as part of a device tree application process, * where you want the device tree overlays phandles to not conflict * with the ones from the base device tree before merging them. * * returns: * 0 on success * Negative error code on failure */ static int overlay_update_local_node_references(void *fdto, int tree_node, int fixup_node, uint32_t delta) { int fixup_prop; int fixup_child; int ret; fdt_for_each_property_offset(fixup_prop, fdto, fixup_node) { const fdt32_t *fixup_val; const char *tree_val; const char *name; int fixup_len; int tree_len; int i; fixup_val = fdt_getprop_by_offset(fdto, fixup_prop, &name, &fixup_len); if (!fixup_val) return fixup_len; if (fixup_len % sizeof(uint32_t)) return -FDT_ERR_BADOVERLAY; tree_val = fdt_getprop(fdto, tree_node, name, &tree_len); if (!tree_val) { if (tree_len == -FDT_ERR_NOTFOUND) return -FDT_ERR_BADOVERLAY; return tree_len; } for (i = 0; i < (fixup_len / sizeof(uint32_t)); i++) { fdt32_t adj_val; uint32_t poffset; poffset = fdt32_to_cpu(fixup_val[i]); /* * phandles to fixup can be unaligned. * * Use a memcpy for the architectures that do * not support unaligned accesses. */ memcpy(&adj_val, tree_val + poffset, sizeof(adj_val)); adj_val = cpu_to_fdt32(fdt32_to_cpu(adj_val) + delta); ret = fdt_setprop_inplace_namelen_partial(fdto, tree_node, name, strlen(name), poffset, &adj_val, sizeof(adj_val)); if (ret == -FDT_ERR_NOSPACE) return -FDT_ERR_BADOVERLAY; if (ret) return ret; } } fdt_for_each_subnode(fixup_child, fdto, fixup_node) { const char *fixup_child_name = fdt_get_name(fdto, fixup_child, NULL); int tree_child; tree_child = fdt_subnode_offset(fdto, tree_node, fixup_child_name); if (tree_child == -FDT_ERR_NOTFOUND) return -FDT_ERR_BADOVERLAY; if (tree_child < 0) return tree_child; ret = overlay_update_local_node_references(fdto, tree_child, fixup_child, delta); if (ret) return ret; } return 0; } /** * overlay_update_local_references - Adjust the overlay references * @fdto: Device tree overlay blob * @delta: Offset to shift the phandles of * * overlay_update_local_references() update all the phandles pointing * to a node within the device tree overlay by adding a constant * delta to not conflict with the base overlay. * * This is mainly used as part of a device tree application process, * where you want the device tree overlays phandles to not conflict * with the ones from the base device tree before merging them. * * returns: * 0 on success * Negative error code on failure */ static int overlay_update_local_references(void *fdto, uint32_t delta) { int fixups; fixups = fdt_path_offset(fdto, "/__local_fixups__"); if (fixups < 0) { /* There's no local phandles to adjust, bail out */ if (fixups == -FDT_ERR_NOTFOUND) return 0; return fixups; } /* * Update our local references from the root of the tree */ return overlay_update_local_node_references(fdto, 0, fixups, delta); } /** * overlay_fixup_one_phandle - Set an overlay phandle to the base one * @fdt: Base Device Tree blob * @fdto: Device tree overlay blob * @symbols_off: Node offset of the symbols node in the base device tree * @path: Path to a node holding a phandle in the overlay * @path_len: number of path characters to consider * @name: Name of the property holding the phandle reference in the overlay * @name_len: number of name characters to consider * @poffset: Offset within the overlay property where the phandle is stored * @label: Label of the node referenced by the phandle * * overlay_fixup_one_phandle() resolves an overlay phandle pointing to * a node in the base device tree. * * This is part of the device tree overlay application process, when * you want all the phandles in the overlay to point to the actual * base dt nodes. * * returns: * 0 on success * Negative error code on failure */ static int overlay_fixup_one_phandle(void *fdt, void *fdto, int symbols_off, const char *path, uint32_t path_len, const char *name, uint32_t name_len, int poffset, const char *label) { const char *symbol_path; uint32_t phandle; fdt32_t phandle_prop; int symbol_off, fixup_off; int prop_len; if (symbols_off < 0) return symbols_off; symbol_path = fdt_getprop(fdt, symbols_off, label, &prop_len); if (!symbol_path) return prop_len; symbol_off = fdt_path_offset(fdt, symbol_path); if (symbol_off < 0) return symbol_off; phandle = fdt_get_phandle(fdt, symbol_off); if (!phandle) return -FDT_ERR_NOTFOUND; fixup_off = fdt_path_offset_namelen(fdto, path, path_len); if (fixup_off == -FDT_ERR_NOTFOUND) return -FDT_ERR_BADOVERLAY; if (fixup_off < 0) return fixup_off; phandle_prop = cpu_to_fdt32(phandle); return fdt_setprop_inplace_namelen_partial(fdto, fixup_off, name, name_len, poffset, &phandle_prop, sizeof(phandle_prop)); }; /** * overlay_fixup_phandle - Set an overlay phandle to the base one * @fdt: Base Device Tree blob * @fdto: Device tree overlay blob * @symbols_off: Node offset of the symbols node in the base device tree * @property: Property offset in the overlay holding the list of fixups * * overlay_fixup_phandle() resolves all the overlay phandles pointed * to in a __fixups__ property, and updates them to match the phandles * in use in the base device tree. * * This is part of the device tree overlay application process, when * you want all the phandles in the overlay to point to the actual * base dt nodes. * * returns: * 0 on success * Negative error code on failure */ static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off, int property) { const char *value; const char *label; int len; value = fdt_getprop_by_offset(fdto, property, &label, &len); if (!value) { if (len == -FDT_ERR_NOTFOUND) return -FDT_ERR_INTERNAL; return len; } do { const char *path, *name, *fixup_end; const char *fixup_str = value; uint32_t path_len, name_len; uint32_t fixup_len; char *sep, *endptr; int poffset, ret; fixup_end = memchr(value, '\0', len); if (!fixup_end) return -FDT_ERR_BADOVERLAY; fixup_len = fixup_end - fixup_str; len -= fixup_len + 1; value += fixup_len + 1; path = fixup_str; sep = memchr(fixup_str, ':', fixup_len); if (!sep || *sep != ':') return -FDT_ERR_BADOVERLAY; path_len = sep - path; if (path_len == (fixup_len - 1)) return -FDT_ERR_BADOVERLAY; fixup_len -= path_len + 1; name = sep + 1; sep = memchr(name, ':', fixup_len); if (!sep || *sep != ':') return -FDT_ERR_BADOVERLAY; name_len = sep - name; if (!name_len) return -FDT_ERR_BADOVERLAY; poffset = strtoul(sep + 1, &endptr, 10); if ((*endptr != '\0') || (endptr <= (sep + 1))) return -FDT_ERR_BADOVERLAY; ret = overlay_fixup_one_phandle(fdt, fdto, symbols_off, path, path_len, name, name_len, poffset, label); if (ret) return ret; } while (len > 0); return 0; } /** * overlay_fixup_phandles - Resolve the overlay phandles to the base * device tree * @fdt: Base Device Tree blob * @fdto: Device tree overlay blob * * overlay_fixup_phandles() resolves all the overlay phandles pointing * to nodes in the base device tree. * * This is one of the steps of the device tree overlay application * process, when you want all the phandles in the overlay to point to * the actual base dt nodes. * * returns: * 0 on success * Negative error code on failure */ static int overlay_fixup_phandles(void *fdt, void *fdto) { int fixups_off, symbols_off; int property; /* We can have overlays without any fixups */ fixups_off = fdt_path_offset(fdto, "/__fixups__"); if (fixups_off == -FDT_ERR_NOTFOUND) return 0; /* nothing to do */ if (fixups_off < 0) return fixups_off; /* And base DTs without symbols */ symbols_off = fdt_path_offset(fdt, "/__symbols__"); if ((symbols_off < 0 && (symbols_off != -FDT_ERR_NOTFOUND))) return symbols_off; fdt_for_each_property_offset(property, fdto, fixups_off) { int ret; ret = overlay_fixup_phandle(fdt, fdto, symbols_off, property); if (ret) return ret; } return 0; } /** * overlay_apply_node - Merges a node into the base device tree * @fdt: Base Device Tree blob * @target: Node offset in the base device tree to apply the fragment to * @fdto: Device tree overlay blob * @node: Node offset in the overlay holding the changes to merge * * overlay_apply_node() merges a node into a target base device tree * node pointed. * * This is part of the final step in the device tree overlay * application process, when all the phandles have been adjusted and * resolved and you just have to merge overlay into the base device * tree. * * returns: * 0 on success * Negative error code on failure */ static int overlay_apply_node(void *fdt, int target, void *fdto, int node) { int property; int subnode; fdt_for_each_property_offset(property, fdto, node) { const char *name; const void *prop; int prop_len; int ret; prop = fdt_getprop_by_offset(fdto, property, &name, &prop_len); if (prop_len == -FDT_ERR_NOTFOUND) return -FDT_ERR_INTERNAL; if (prop_len < 0) return prop_len; ret = fdt_setprop(fdt, target, name, prop, prop_len); if (ret) return ret; } fdt_for_each_subnode(subnode, fdto, node) { const char *name = fdt_get_name(fdto, subnode, NULL); int nnode; int ret; nnode = fdt_add_subnode(fdt, target, name); if (nnode == -FDT_ERR_EXISTS) { nnode = fdt_subnode_offset(fdt, target, name); if (nnode == -FDT_ERR_NOTFOUND) return -FDT_ERR_INTERNAL; } if (nnode < 0) return nnode; ret = overlay_apply_node(fdt, nnode, fdto, subnode); if (ret) return ret; } return 0; } /** * overlay_merge - Merge an overlay into its base device tree * @fdt: Base Device Tree blob * @fdto: Device tree overlay blob * * overlay_merge() merges an overlay into its base device tree. * * This is the next to last step in the device tree overlay application * process, when all the phandles have been adjusted and resolved and * you just have to merge overlay into the base device tree. * * returns: * 0 on success * Negative error code on failure */ static int overlay_merge(void *fdt, void *fdto) { int fragment; fdt_for_each_subnode(fragment, fdto, 0) { int overlay; int target; int ret; /* * Each fragments will have an __overlay__ node. If * they don't, it's not supposed to be merged */ overlay = fdt_subnode_offset(fdto, fragment, "__overlay__"); if (overlay == -FDT_ERR_NOTFOUND) continue; if (overlay < 0) return overlay; target = overlay_get_target(fdt, fdto, fragment, NULL); if (target < 0) return target; ret = overlay_apply_node(fdt, target, fdto, overlay); if (ret) return ret; } return 0; } static int get_path_len(const void *fdt, int nodeoffset) { int len = 0, namelen; const char *name; FDT_RO_PROBE(fdt); for (;;) { name = fdt_get_name(fdt, nodeoffset, &namelen); if (!name) return namelen; /* root? we're done */ if (namelen == 0) break; nodeoffset = fdt_parent_offset(fdt, nodeoffset); if (nodeoffset < 0) return nodeoffset; len += namelen + 1; } /* in case of root pretend it's "/" */ if (len == 0) len++; return len; } /** * overlay_symbol_update - Update the symbols of base tree after a merge * @fdt: Base Device Tree blob * @fdto: Device tree overlay blob * * overlay_symbol_update() updates the symbols of the base tree with the * symbols of the applied overlay * * This is the last step in the device tree overlay application * process, allowing the reference of overlay symbols by subsequent * overlay operations. * * returns: * 0 on success * Negative error code on failure */ static int overlay_symbol_update(void *fdt, void *fdto) { int root_sym, ov_sym, prop, path_len, fragment, target; int len, frag_name_len, ret, rel_path_len; const char *s, *e; const char *path; const char *name; const char *frag_name; const char *rel_path; const char *target_path; char *buf; void *p; ov_sym = fdt_subnode_offset(fdto, 0, "__symbols__"); /* if no overlay symbols exist no problem */ if (ov_sym < 0) return 0; root_sym = fdt_subnode_offset(fdt, 0, "__symbols__"); /* it no root symbols exist we should create them */ if (root_sym == -FDT_ERR_NOTFOUND) root_sym = fdt_add_subnode(fdt, 0, "__symbols__"); /* any error is fatal now */ if (root_sym < 0) return root_sym; /* iterate over each overlay symbol */ fdt_for_each_property_offset(prop, fdto, ov_sym) { path = fdt_getprop_by_offset(fdto, prop, &name, &path_len); if (!path) return path_len; /* verify it's a string property (terminated by a single \0) */ if (path_len < 1 || memchr(path, '\0', path_len) != &path[path_len - 1]) return -FDT_ERR_BADVALUE; /* keep end marker to avoid strlen() */ e = path + path_len; /* format: //__overlay__/ */ if (*path != '/') return -FDT_ERR_BADVALUE; /* get fragment name first */ s = strchr(path + 1, '/'); if (!s) return -FDT_ERR_BADOVERLAY; frag_name = path + 1; frag_name_len = s - path - 1; /* verify format; safe since "s" lies in \0 terminated prop */ len = sizeof("/__overlay__/") - 1; if ((e - s) < len || memcmp(s, "/__overlay__/", len)) return -FDT_ERR_BADOVERLAY; rel_path = s + len; rel_path_len = e - rel_path; /* find the fragment index in which the symbol lies */ ret = fdt_subnode_offset_namelen(fdto, 0, frag_name, frag_name_len); /* not found? */ if (ret < 0) return -FDT_ERR_BADOVERLAY; fragment = ret; /* an __overlay__ subnode must exist */ ret = fdt_subnode_offset(fdto, fragment, "__overlay__"); if (ret < 0) return -FDT_ERR_BADOVERLAY; /* get the target of the fragment */ ret = overlay_get_target(fdt, fdto, fragment, &target_path); if (ret < 0) return ret; target = ret; /* if we have a target path use */ if (!target_path) { ret = get_path_len(fdt, target); if (ret < 0) return ret; len = ret; } else { len = strlen(target_path); } ret = fdt_setprop_placeholder(fdt, root_sym, name, len + (len > 1) + rel_path_len + 1, &p); if (ret < 0) return ret; if (!target_path) { /* again in case setprop_placeholder changed it */ ret = overlay_get_target(fdt, fdto, fragment, &target_path); if (ret < 0) return ret; target = ret; } buf = p; if (len > 1) { /* target is not root */ if (!target_path) { ret = fdt_get_path(fdt, target, buf, len + 1); if (ret < 0) return ret; } else memcpy(buf, target_path, len + 1); } else len--; buf[len] = '/'; memcpy(buf + len + 1, rel_path, rel_path_len); buf[len + 1 + rel_path_len] = '\0'; } return 0; } int fdt_overlay_apply(void *fdt, void *fdto) { uint32_t delta = fdt_get_max_phandle(fdt); int ret; FDT_RO_PROBE(fdt); FDT_RO_PROBE(fdto); ret = overlay_adjust_local_phandles(fdto, delta); if (ret) goto err; ret = overlay_update_local_references(fdto, delta); if (ret) goto err; ret = overlay_fixup_phandles(fdt, fdto); if (ret) goto err; ret = overlay_merge(fdt, fdto); if (ret) goto err; ret = overlay_symbol_update(fdt, fdto); if (ret) goto err; /* * The overlay has been damaged, erase its magic. */ fdt_set_magic(fdto, ~0); return 0; err: /* * The overlay might have been damaged, erase its magic. */ fdt_set_magic(fdto, ~0); /* * The base device tree might have been damaged, erase its * magic. */ fdt_set_magic(fdt, ~0); return ret; } ================================================ FILE: kernel/libs/libfdt/fdt_ro.c ================================================ /* * libfdt - Flat Device Tree manipulation * Copyright (C) 2006 David Gibson, IBM Corporation. * * libfdt is dual licensed: you can use it either under the terms of * the GPL, or the BSD license, at your option. * * a) This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301 USA * * Alternatively, * * b) Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "libfdt_internal.h" static int fdt_nodename_eq_(const void *fdt, int offset, const char *s, int len) { int olen; const char *p = fdt_get_name(fdt, offset, &olen); if (!p || olen < len) /* short match */ return 0; if (memcmp(p, s, len) != 0) return 0; if (p[len] == '\0') return 1; else if (!memchr(s, '@', len) && (p[len] == '@')) return 1; else return 0; } const char *fdt_get_string(const void *fdt, int stroffset, int *lenp) { uint32_t absoffset = stroffset + fdt_off_dt_strings(fdt); size_t len; int err; const char *s, *n; err = fdt_ro_probe_(fdt); if (err != 0) goto fail; err = -FDT_ERR_BADOFFSET; if (absoffset >= fdt_totalsize(fdt)) goto fail; len = fdt_totalsize(fdt) - absoffset; if (fdt_magic(fdt) == FDT_MAGIC) { if (stroffset < 0) goto fail; if (fdt_version(fdt) >= 17) { if (stroffset >= fdt_size_dt_strings(fdt)) goto fail; if ((fdt_size_dt_strings(fdt) - stroffset) < len) len = fdt_size_dt_strings(fdt) - stroffset; } } else if (fdt_magic(fdt) == FDT_SW_MAGIC) { if ((stroffset >= 0) || (stroffset < -fdt_size_dt_strings(fdt))) goto fail; if ((-stroffset) < len) len = -stroffset; } else { err = -FDT_ERR_INTERNAL; goto fail; } s = (const char *)fdt + absoffset; n = memchr(s, '\0', len); if (!n) { /* missing terminating NULL */ err = -FDT_ERR_TRUNCATED; goto fail; } if (lenp) *lenp = n - s; return s; fail: if (lenp) *lenp = err; return NULL; } const char *fdt_string(const void *fdt, int stroffset) { return fdt_get_string(fdt, stroffset, NULL); } static int fdt_string_eq_(const void *fdt, int stroffset, const char *s, int len) { int slen; const char *p = fdt_get_string(fdt, stroffset, &slen); return p && (slen == len) && (memcmp(p, s, len) == 0); } uint32_t fdt_get_max_phandle(const void *fdt) { uint32_t max_phandle = 0; int offset; for (offset = fdt_next_node(fdt, -1, NULL);; offset = fdt_next_node(fdt, offset, NULL)) { uint32_t phandle; if (offset == -FDT_ERR_NOTFOUND) return max_phandle; if (offset < 0) return (uint32_t)-1; phandle = fdt_get_phandle(fdt, offset); if (phandle == (uint32_t)-1) continue; if (phandle > max_phandle) max_phandle = phandle; } return 0; } static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n) { int offset = n * sizeof(struct fdt_reserve_entry); int absoffset = fdt_off_mem_rsvmap(fdt) + offset; if (absoffset < fdt_off_mem_rsvmap(fdt)) return NULL; if (absoffset > fdt_totalsize(fdt) - sizeof(struct fdt_reserve_entry)) return NULL; return fdt_mem_rsv_(fdt, n); } int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size) { const struct fdt_reserve_entry *re; FDT_RO_PROBE(fdt); re = fdt_mem_rsv(fdt, n); if (!re) return -FDT_ERR_BADOFFSET; *address = fdt64_ld(&re->address); *size = fdt64_ld(&re->size); return 0; } int fdt_num_mem_rsv(const void *fdt) { int i; const struct fdt_reserve_entry *re; for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) { if (fdt64_ld(&re->size) == 0) return i; } return -FDT_ERR_TRUNCATED; } static int nextprop_(const void *fdt, int offset) { uint32_t tag; int nextoffset; do { tag = fdt_next_tag(fdt, offset, &nextoffset); switch (tag) { case FDT_END: if (nextoffset >= 0) return -FDT_ERR_BADSTRUCTURE; else return nextoffset; case FDT_PROP: return offset; } offset = nextoffset; } while (tag == FDT_NOP); return -FDT_ERR_NOTFOUND; } int fdt_subnode_offset_namelen(const void *fdt, int offset, const char *name, int namelen) { int depth; FDT_RO_PROBE(fdt); for (depth = 0; (offset >= 0) && (depth >= 0); offset = fdt_next_node(fdt, offset, &depth)) if ((depth == 1) && fdt_nodename_eq_(fdt, offset, name, namelen)) return offset; if (depth < 0) return -FDT_ERR_NOTFOUND; return offset; /* error */ } int fdt_subnode_offset(const void *fdt, int parentoffset, const char *name) { return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name)); } int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen) { const char *end = path + namelen; const char *p = path; int offset = 0; FDT_RO_PROBE(fdt); /* see if we have an alias */ if (*path != '/') { const char *q = memchr(path, '/', end - p); if (!q) q = end; p = fdt_get_alias_namelen(fdt, p, q - p); if (!p) return -FDT_ERR_BADPATH; offset = fdt_path_offset(fdt, p); p = q; } while (p < end) { const char *q; while (*p == '/') { p++; if (p == end) return offset; } q = memchr(p, '/', end - p); if (! q) q = end; offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p); if (offset < 0) return offset; p = q; } return offset; } int fdt_path_offset(const void *fdt, const char *path) { return fdt_path_offset_namelen(fdt, path, strlen(path)); } const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) { const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset); const char *nameptr; int err; if (((err = fdt_ro_probe_(fdt)) != 0) || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0)) goto fail; nameptr = nh->name; if (fdt_version(fdt) < 0x10) { /* * For old FDT versions, match the naming conventions of V16: * give only the leaf name (after all /). The actual tree * contents are loosely checked. */ const char *leaf; leaf = strrchr(nameptr, '/'); if (leaf == NULL) { err = -FDT_ERR_BADSTRUCTURE; goto fail; } nameptr = leaf+1; } if (len) *len = strlen(nameptr); return nameptr; fail: if (len) *len = err; return NULL; } int fdt_first_property_offset(const void *fdt, int nodeoffset) { int offset; if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0) return offset; return nextprop_(fdt, offset); } int fdt_next_property_offset(const void *fdt, int offset) { if ((offset = fdt_check_prop_offset_(fdt, offset)) < 0) return offset; return nextprop_(fdt, offset); } static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt, int offset, int *lenp) { int err; const struct fdt_property *prop; if ((err = fdt_check_prop_offset_(fdt, offset)) < 0) { if (lenp) *lenp = err; return NULL; } prop = fdt_offset_ptr_(fdt, offset); if (lenp) *lenp = fdt32_ld(&prop->len); return prop; } const struct fdt_property *fdt_get_property_by_offset(const void *fdt, int offset, int *lenp) { /* Prior to version 16, properties may need realignment * and this API does not work. fdt_getprop_*() will, however. */ if (fdt_version(fdt) < 0x10) { if (lenp) *lenp = -FDT_ERR_BADVERSION; return NULL; } return fdt_get_property_by_offset_(fdt, offset, lenp); } static const struct fdt_property *fdt_get_property_namelen_(const void *fdt, int offset, const char *name, int namelen, int *lenp, int *poffset) { for (offset = fdt_first_property_offset(fdt, offset); (offset >= 0); (offset = fdt_next_property_offset(fdt, offset))) { const struct fdt_property *prop; if (!(prop = fdt_get_property_by_offset_(fdt, offset, lenp))) { offset = -FDT_ERR_INTERNAL; break; } if (fdt_string_eq_(fdt, fdt32_ld(&prop->nameoff), name, namelen)) { if (poffset) *poffset = offset; return prop; } } if (lenp) *lenp = offset; return NULL; } const struct fdt_property *fdt_get_property_namelen(const void *fdt, int offset, const char *name, int namelen, int *lenp) { /* Prior to version 16, properties may need realignment * and this API does not work. fdt_getprop_*() will, however. */ if (fdt_version(fdt) < 0x10) { if (lenp) *lenp = -FDT_ERR_BADVERSION; return NULL; } return fdt_get_property_namelen_(fdt, offset, name, namelen, lenp, NULL); } const struct fdt_property *fdt_get_property(const void *fdt, int nodeoffset, const char *name, int *lenp) { return fdt_get_property_namelen(fdt, nodeoffset, name, strlen(name), lenp); } const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, const char *name, int namelen, int *lenp) { int poffset; const struct fdt_property *prop; prop = fdt_get_property_namelen_(fdt, nodeoffset, name, namelen, lenp, &poffset); if (!prop) return NULL; /* Handle realignment */ if (fdt_version(fdt) < 0x10 && (poffset + sizeof(*prop)) % 8 && fdt32_ld(&prop->len) >= 8) return prop->data + 4; return prop->data; } const void *fdt_getprop_by_offset(const void *fdt, int offset, const char **namep, int *lenp) { const struct fdt_property *prop; prop = fdt_get_property_by_offset_(fdt, offset, lenp); if (!prop) return NULL; if (namep) { const char *name; int namelen; name = fdt_get_string(fdt, fdt32_ld(&prop->nameoff), &namelen); if (!name) { if (lenp) *lenp = namelen; return NULL; } *namep = name; } /* Handle realignment */ if (fdt_version(fdt) < 0x10 && (offset + sizeof(*prop)) % 8 && fdt32_ld(&prop->len) >= 8) return prop->data + 4; return prop->data; } const void *fdt_getprop(const void *fdt, int nodeoffset, const char *name, int *lenp) { return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp); } uint32_t fdt_get_phandle(const void *fdt, int nodeoffset) { const fdt32_t *php; int len; /* FIXME: This is a bit sub-optimal, since we potentially scan * over all the properties twice. */ php = fdt_getprop(fdt, nodeoffset, "phandle", &len); if (!php || (len != sizeof(*php))) { php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len); if (!php || (len != sizeof(*php))) return 0; } return fdt32_ld(php); } const char *fdt_get_alias_namelen(const void *fdt, const char *name, int namelen) { int aliasoffset; aliasoffset = fdt_path_offset(fdt, "/aliases"); if (aliasoffset < 0) return NULL; return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL); } const char *fdt_get_alias(const void *fdt, const char *name) { return fdt_get_alias_namelen(fdt, name, strlen(name)); } int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen) { int pdepth = 0, p = 0; int offset, depth, namelen; const char *name; FDT_RO_PROBE(fdt); if (buflen < 2) return -FDT_ERR_NOSPACE; for (offset = 0, depth = 0; (offset >= 0) && (offset <= nodeoffset); offset = fdt_next_node(fdt, offset, &depth)) { while (pdepth > depth) { do { p--; } while (buf[p-1] != '/'); pdepth--; } if (pdepth >= depth) { name = fdt_get_name(fdt, offset, &namelen); if (!name) return namelen; if ((p + namelen + 1) <= buflen) { memcpy(buf + p, name, namelen); p += namelen; buf[p++] = '/'; pdepth++; } } if (offset == nodeoffset) { if (pdepth < (depth + 1)) return -FDT_ERR_NOSPACE; if (p > 1) /* special case so that root path is "/", not "" */ p--; buf[p] = '\0'; return 0; } } if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) return -FDT_ERR_BADOFFSET; else if (offset == -FDT_ERR_BADOFFSET) return -FDT_ERR_BADSTRUCTURE; return offset; /* error from fdt_next_node() */ } int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, int supernodedepth, int *nodedepth) { int offset, depth; int supernodeoffset = -FDT_ERR_INTERNAL; FDT_RO_PROBE(fdt); if (supernodedepth < 0) return -FDT_ERR_NOTFOUND; for (offset = 0, depth = 0; (offset >= 0) && (offset <= nodeoffset); offset = fdt_next_node(fdt, offset, &depth)) { if (depth == supernodedepth) supernodeoffset = offset; if (offset == nodeoffset) { if (nodedepth) *nodedepth = depth; if (supernodedepth > depth) return -FDT_ERR_NOTFOUND; else return supernodeoffset; } } if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) return -FDT_ERR_BADOFFSET; else if (offset == -FDT_ERR_BADOFFSET) return -FDT_ERR_BADSTRUCTURE; return offset; /* error from fdt_next_node() */ } int fdt_node_depth(const void *fdt, int nodeoffset) { int nodedepth; int err; err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth); if (err) return (err < 0) ? err : -FDT_ERR_INTERNAL; return nodedepth; } int fdt_parent_offset(const void *fdt, int nodeoffset) { int nodedepth = fdt_node_depth(fdt, nodeoffset); if (nodedepth < 0) return nodedepth; return fdt_supernode_atdepth_offset(fdt, nodeoffset, nodedepth - 1, NULL); } int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, const char *propname, const void *propval, int proplen) { int offset; const void *val; int len; FDT_RO_PROBE(fdt); /* FIXME: The algorithm here is pretty horrible: we scan each * property of a node in fdt_getprop(), then if that didn't * find what we want, we scan over them again making our way * to the next node. Still it's the easiest to implement * approach; performance can come later. */ for (offset = fdt_next_node(fdt, startoffset, NULL); offset >= 0; offset = fdt_next_node(fdt, offset, NULL)) { val = fdt_getprop(fdt, offset, propname, &len); if (val && (len == proplen) && (memcmp(val, propval, len) == 0)) return offset; } return offset; /* error from fdt_next_node() */ } int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) { int offset; if ((phandle == 0) || (phandle == -1)) return -FDT_ERR_BADPHANDLE; FDT_RO_PROBE(fdt); /* FIXME: The algorithm here is pretty horrible: we * potentially scan each property of a node in * fdt_get_phandle(), then if that didn't find what * we want, we scan over them again making our way to the next * node. Still it's the easiest to implement approach; * performance can come later. */ for (offset = fdt_next_node(fdt, -1, NULL); offset >= 0; offset = fdt_next_node(fdt, offset, NULL)) { if (fdt_get_phandle(fdt, offset) == phandle) return offset; } return offset; /* error from fdt_next_node() */ } int fdt_stringlist_contains(const char *strlist, int listlen, const char *str) { int len = strlen(str); const char *p; while (listlen >= len) { if (memcmp(str, strlist, len+1) == 0) return 1; p = memchr(strlist, '\0', listlen); if (!p) return 0; /* malformed strlist.. */ listlen -= (p-strlist) + 1; strlist = p + 1; } return 0; } int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property) { const char *list, *end; int length, count = 0; list = fdt_getprop(fdt, nodeoffset, property, &length); if (!list) return length; end = list + length; while (list < end) { length = strnlen(list, end - list) + 1; /* Abort if the last string isn't properly NUL-terminated. */ if (list + length > end) return -FDT_ERR_BADVALUE; list += length; count++; } return count; } int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property, const char *string) { int length, len, idx = 0; const char *list, *end; list = fdt_getprop(fdt, nodeoffset, property, &length); if (!list) return length; len = strlen(string) + 1; end = list + length; while (list < end) { length = strnlen(list, end - list) + 1; /* Abort if the last string isn't properly NUL-terminated. */ if (list + length > end) return -FDT_ERR_BADVALUE; if (length == len && memcmp(list, string, length) == 0) return idx; list += length; idx++; } return -FDT_ERR_NOTFOUND; } const char *fdt_stringlist_get(const void *fdt, int nodeoffset, const char *property, int idx, int *lenp) { const char *list, *end; int length; list = fdt_getprop(fdt, nodeoffset, property, &length); if (!list) { if (lenp) *lenp = length; return NULL; } end = list + length; while (list < end) { length = strnlen(list, end - list) + 1; /* Abort if the last string isn't properly NUL-terminated. */ if (list + length > end) { if (lenp) *lenp = -FDT_ERR_BADVALUE; return NULL; } if (idx == 0) { if (lenp) *lenp = length - 1; return list; } list += length; idx--; } if (lenp) *lenp = -FDT_ERR_NOTFOUND; return NULL; } int fdt_node_check_compatible(const void *fdt, int nodeoffset, const char *compatible) { const void *prop; int len; prop = fdt_getprop(fdt, nodeoffset, "compatible", &len); if (!prop) return len; return !fdt_stringlist_contains(prop, len, compatible); } int fdt_node_offset_by_compatible(const void *fdt, int startoffset, const char *compatible) { int offset, err; FDT_RO_PROBE(fdt); /* FIXME: The algorithm here is pretty horrible: we scan each * property of a node in fdt_node_check_compatible(), then if * that didn't find what we want, we scan over them again * making our way to the next node. Still it's the easiest to * implement approach; performance can come later. */ for (offset = fdt_next_node(fdt, startoffset, NULL); offset >= 0; offset = fdt_next_node(fdt, offset, NULL)) { err = fdt_node_check_compatible(fdt, offset, compatible); if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) return err; else if (err == 0) return offset; } return offset; /* error from fdt_next_node() */ } int fdt_check_full(const void *fdt, size_t bufsize) { int err; int num_memrsv; int offset, nextoffset = 0; uint32_t tag; unsigned depth = 0; const void *prop; const char *propname; if (bufsize < FDT_V1_SIZE) return -FDT_ERR_TRUNCATED; err = fdt_check_header(fdt); if (err != 0) return err; if (bufsize < fdt_totalsize(fdt)) return -FDT_ERR_TRUNCATED; num_memrsv = fdt_num_mem_rsv(fdt); if (num_memrsv < 0) return num_memrsv; while (1) { offset = nextoffset; tag = fdt_next_tag(fdt, offset, &nextoffset); if (nextoffset < 0) return nextoffset; switch (tag) { case FDT_NOP: break; case FDT_END: if (depth != 0) return -FDT_ERR_BADSTRUCTURE; return 0; case FDT_BEGIN_NODE: depth++; if (depth > INT_MAX) return -FDT_ERR_BADSTRUCTURE; break; case FDT_END_NODE: if (depth == 0) return -FDT_ERR_BADSTRUCTURE; depth--; break; case FDT_PROP: prop = fdt_getprop_by_offset(fdt, offset, &propname, &err); if (!prop) return err; break; default: return -FDT_ERR_INTERNAL; } } } ================================================ FILE: kernel/libs/libfdt/fdt_rw.c ================================================ /* * libfdt - Flat Device Tree manipulation * Copyright (C) 2006 David Gibson, IBM Corporation. * * libfdt is dual licensed: you can use it either under the terms of * the GPL, or the BSD license, at your option. * * a) This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301 USA * * Alternatively, * * b) Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "libfdt_internal.h" static int fdt_blocks_misordered_(const void *fdt, int mem_rsv_size, int struct_size) { return (fdt_off_mem_rsvmap(fdt) < FDT_ALIGN(sizeof(struct fdt_header), 8)) || (fdt_off_dt_struct(fdt) < (fdt_off_mem_rsvmap(fdt) + mem_rsv_size)) || (fdt_off_dt_strings(fdt) < (fdt_off_dt_struct(fdt) + struct_size)) || (fdt_totalsize(fdt) < (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt))); } static int fdt_rw_probe_(void *fdt) { FDT_RO_PROBE(fdt); if (fdt_version(fdt) < 17) return -FDT_ERR_BADVERSION; if (fdt_blocks_misordered_(fdt, sizeof(struct fdt_reserve_entry), fdt_size_dt_struct(fdt))) return -FDT_ERR_BADLAYOUT; if (fdt_version(fdt) > 17) fdt_set_version(fdt, 17); return 0; } #define FDT_RW_PROBE(fdt) \ { \ int err_; \ if ((err_ = fdt_rw_probe_(fdt)) != 0) \ return err_; \ } static inline int fdt_data_size_(void *fdt) { return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt); } static int fdt_splice_(void *fdt, void *splicepoint, int oldlen, int newlen) { char *p = splicepoint; char *end = (char *)fdt + fdt_data_size_(fdt); if (((p + oldlen) < p) || ((p + oldlen) > end)) return -FDT_ERR_BADOFFSET; if ((p < (char *)fdt) || ((end - oldlen + newlen) < (char *)fdt)) return -FDT_ERR_BADOFFSET; if ((end - oldlen + newlen) > ((char *)fdt + fdt_totalsize(fdt))) return -FDT_ERR_NOSPACE; memmove(p + newlen, p + oldlen, end - p - oldlen); return 0; } static int fdt_splice_mem_rsv_(void *fdt, struct fdt_reserve_entry *p, int oldn, int newn) { int delta = (newn - oldn) * sizeof(*p); int err; err = fdt_splice_(fdt, p, oldn * sizeof(*p), newn * sizeof(*p)); if (err) return err; fdt_set_off_dt_struct(fdt, fdt_off_dt_struct(fdt) + delta); fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta); return 0; } static int fdt_splice_struct_(void *fdt, void *p, int oldlen, int newlen) { int delta = newlen - oldlen; int err; if ((err = fdt_splice_(fdt, p, oldlen, newlen))) return err; fdt_set_size_dt_struct(fdt, fdt_size_dt_struct(fdt) + delta); fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta); return 0; } static int fdt_splice_string_(void *fdt, int newlen) { void *p = (char *)fdt + fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt); int err; if ((err = fdt_splice_(fdt, p, 0, newlen))) return err; fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen); return 0; } static int fdt_find_add_string_(void *fdt, const char *s) { char *strtab = (char *)fdt + fdt_off_dt_strings(fdt); const char *p; char *new; int len = strlen(s) + 1; int err; p = fdt_find_string_(strtab, fdt_size_dt_strings(fdt), s); if (p) /* found it */ return (p - strtab); new = strtab + fdt_size_dt_strings(fdt); err = fdt_splice_string_(fdt, len); if (err) return err; memcpy(new, s, len); return (new - strtab); } int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size) { struct fdt_reserve_entry *re; int err; FDT_RW_PROBE(fdt); re = fdt_mem_rsv_w_(fdt, fdt_num_mem_rsv(fdt)); err = fdt_splice_mem_rsv_(fdt, re, 0, 1); if (err) return err; re->address = cpu_to_fdt64(address); re->size = cpu_to_fdt64(size); return 0; } int fdt_del_mem_rsv(void *fdt, int n) { struct fdt_reserve_entry *re = fdt_mem_rsv_w_(fdt, n); FDT_RW_PROBE(fdt); if (n >= fdt_num_mem_rsv(fdt)) return -FDT_ERR_NOTFOUND; return fdt_splice_mem_rsv_(fdt, re, 1, 0); } static int fdt_resize_property_(void *fdt, int nodeoffset, const char *name, int len, struct fdt_property **prop) { int oldlen; int err; *prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen); if (!*prop) return oldlen; if ((err = fdt_splice_struct_(fdt, (*prop)->data, FDT_TAGALIGN(oldlen), FDT_TAGALIGN(len)))) return err; (*prop)->len = cpu_to_fdt32(len); return 0; } static int fdt_add_property_(void *fdt, int nodeoffset, const char *name, int len, struct fdt_property **prop) { int proplen; int nextoffset; int namestroff; int err; if ((nextoffset = fdt_check_node_offset_(fdt, nodeoffset)) < 0) return nextoffset; namestroff = fdt_find_add_string_(fdt, name); if (namestroff < 0) return namestroff; *prop = fdt_offset_ptr_w_(fdt, nextoffset); proplen = sizeof(**prop) + FDT_TAGALIGN(len); err = fdt_splice_struct_(fdt, *prop, 0, proplen); if (err) return err; (*prop)->tag = cpu_to_fdt32(FDT_PROP); (*prop)->nameoff = cpu_to_fdt32(namestroff); (*prop)->len = cpu_to_fdt32(len); return 0; } int fdt_set_name(void *fdt, int nodeoffset, const char *name) { char *namep; int oldlen, newlen; int err; FDT_RW_PROBE(fdt); namep = (char *)(uintptr_t)fdt_get_name(fdt, nodeoffset, &oldlen); if (!namep) return oldlen; newlen = strlen(name); err = fdt_splice_struct_(fdt, namep, FDT_TAGALIGN(oldlen+1), FDT_TAGALIGN(newlen+1)); if (err) return err; memcpy(namep, name, newlen+1); return 0; } int fdt_setprop_placeholder(void *fdt, int nodeoffset, const char *name, int len, void **prop_data) { struct fdt_property *prop; int err; FDT_RW_PROBE(fdt); err = fdt_resize_property_(fdt, nodeoffset, name, len, &prop); if (err == -FDT_ERR_NOTFOUND) err = fdt_add_property_(fdt, nodeoffset, name, len, &prop); if (err) return err; *prop_data = prop->data; return 0; } int fdt_setprop(void *fdt, int nodeoffset, const char *name, const void *val, int len) { void *prop_data; int err; err = fdt_setprop_placeholder(fdt, nodeoffset, name, len, &prop_data); if (err) return err; if (len) memcpy(prop_data, val, len); return 0; } int fdt_appendprop(void *fdt, int nodeoffset, const char *name, const void *val, int len) { struct fdt_property *prop; int err, oldlen, newlen; FDT_RW_PROBE(fdt); prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen); if (prop) { newlen = len + oldlen; err = fdt_splice_struct_(fdt, prop->data, FDT_TAGALIGN(oldlen), FDT_TAGALIGN(newlen)); if (err) return err; prop->len = cpu_to_fdt32(newlen); memcpy(prop->data + oldlen, val, len); } else { err = fdt_add_property_(fdt, nodeoffset, name, len, &prop); if (err) return err; memcpy(prop->data, val, len); } return 0; } int fdt_delprop(void *fdt, int nodeoffset, const char *name) { struct fdt_property *prop; int len, proplen; FDT_RW_PROBE(fdt); prop = fdt_get_property_w(fdt, nodeoffset, name, &len); if (!prop) return len; proplen = sizeof(*prop) + FDT_TAGALIGN(len); return fdt_splice_struct_(fdt, prop, proplen, 0); } int fdt_add_subnode_namelen(void *fdt, int parentoffset, const char *name, int namelen) { struct fdt_node_header *nh; int offset, nextoffset; int nodelen; int err; uint32_t tag; fdt32_t *endtag; FDT_RW_PROBE(fdt); offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen); if (offset >= 0) return -FDT_ERR_EXISTS; else if (offset != -FDT_ERR_NOTFOUND) return offset; /* Try to place the new node after the parent's properties */ fdt_next_tag(fdt, parentoffset, &nextoffset); /* skip the BEGIN_NODE */ do { offset = nextoffset; tag = fdt_next_tag(fdt, offset, &nextoffset); } while ((tag == FDT_PROP) || (tag == FDT_NOP)); nh = fdt_offset_ptr_w_(fdt, offset); nodelen = sizeof(*nh) + FDT_TAGALIGN(namelen+1) + FDT_TAGSIZE; err = fdt_splice_struct_(fdt, nh, 0, nodelen); if (err) return err; nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); memset(nh->name, 0, FDT_TAGALIGN(namelen+1)); memcpy(nh->name, name, namelen); endtag = (fdt32_t *)((char *)nh + nodelen - FDT_TAGSIZE); *endtag = cpu_to_fdt32(FDT_END_NODE); return offset; } int fdt_add_subnode(void *fdt, int parentoffset, const char *name) { return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name)); } int fdt_del_node(void *fdt, int nodeoffset) { int endoffset; FDT_RW_PROBE(fdt); endoffset = fdt_node_end_offset_(fdt, nodeoffset); if (endoffset < 0) return endoffset; return fdt_splice_struct_(fdt, fdt_offset_ptr_w_(fdt, nodeoffset), endoffset - nodeoffset, 0); } static void fdt_packblocks_(const char *old, char *new, int mem_rsv_size, int struct_size) { int mem_rsv_off, struct_off, strings_off; mem_rsv_off = FDT_ALIGN(sizeof(struct fdt_header), 8); struct_off = mem_rsv_off + mem_rsv_size; strings_off = struct_off + struct_size; memmove(new + mem_rsv_off, old + fdt_off_mem_rsvmap(old), mem_rsv_size); fdt_set_off_mem_rsvmap(new, mem_rsv_off); memmove(new + struct_off, old + fdt_off_dt_struct(old), struct_size); fdt_set_off_dt_struct(new, struct_off); fdt_set_size_dt_struct(new, struct_size); memmove(new + strings_off, old + fdt_off_dt_strings(old), fdt_size_dt_strings(old)); fdt_set_off_dt_strings(new, strings_off); fdt_set_size_dt_strings(new, fdt_size_dt_strings(old)); } int fdt_open_into(const void *fdt, void *buf, int bufsize) { int err; int mem_rsv_size, struct_size; int newsize; const char *fdtstart = fdt; const char *fdtend = fdtstart + fdt_totalsize(fdt); char *tmp; FDT_RO_PROBE(fdt); mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) * sizeof(struct fdt_reserve_entry); if (fdt_version(fdt) >= 17) { struct_size = fdt_size_dt_struct(fdt); } else { struct_size = 0; while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END) ; if (struct_size < 0) return struct_size; } if (!fdt_blocks_misordered_(fdt, mem_rsv_size, struct_size)) { /* no further work necessary */ err = fdt_move(fdt, buf, bufsize); if (err) return err; fdt_set_version(buf, 17); fdt_set_size_dt_struct(buf, struct_size); fdt_set_totalsize(buf, bufsize); return 0; } /* Need to reorder */ newsize = FDT_ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size + struct_size + fdt_size_dt_strings(fdt); if (bufsize < newsize) return -FDT_ERR_NOSPACE; /* First attempt to build converted tree at beginning of buffer */ tmp = buf; /* But if that overlaps with the old tree... */ if (((tmp + newsize) > fdtstart) && (tmp < fdtend)) { /* Try right after the old tree instead */ tmp = (char *)(uintptr_t)fdtend; if ((tmp + newsize) > ((char *)buf + bufsize)) return -FDT_ERR_NOSPACE; } fdt_packblocks_(fdt, tmp, mem_rsv_size, struct_size); memmove(buf, tmp, newsize); fdt_set_magic(buf, FDT_MAGIC); fdt_set_totalsize(buf, bufsize); fdt_set_version(buf, 17); fdt_set_last_comp_version(buf, 16); fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt)); return 0; } int fdt_pack(void *fdt) { int mem_rsv_size; FDT_RW_PROBE(fdt); mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) * sizeof(struct fdt_reserve_entry); fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt)); fdt_set_totalsize(fdt, fdt_data_size_(fdt)); return 0; } int fdt_set_node_reg(void *dtb, int node, unsigned long iomem, size_t iomem_size) { uint32_t tmp[4]; uint32_t *args = tmp; int size = 0, size_cells, addr_cells; size_cells = fdt_size_cells(dtb, 0); addr_cells = fdt_address_cells(dtb, 0); if (addr_cells == 1) { *args++ = cpu_to_fdt32(iomem); size++; } else { *args++ = cpu_to_fdt32(iomem >> 32); *args++ = cpu_to_fdt32(iomem & 0xffffffff); size += 2; } if (size_cells == 1) { *args++ = cpu_to_fdt32(iomem_size); size++; } else { *args++ = cpu_to_fdt32(iomem_size >> 32); *args++ = cpu_to_fdt32(iomem_size & 0xffffffff); size += 2; } fdt_setprop(dtb, node, "reg", (void *)tmp, size * 4); return 0; } ================================================ FILE: kernel/libs/libfdt/fdt_strerror.c ================================================ /* * libfdt - Flat Device Tree manipulation * Copyright (C) 2006 David Gibson, IBM Corporation. * * libfdt is dual licensed: you can use it either under the terms of * the GPL, or the BSD license, at your option. * * a) This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301 USA * * Alternatively, * * b) Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "libfdt_internal.h" struct fdt_errtabent { const char *str; }; #define FDT_ERRTABENT(val) \ [(val)] = { .str = #val, } static struct fdt_errtabent fdt_errtable[] = { FDT_ERRTABENT(FDT_ERR_NOTFOUND), FDT_ERRTABENT(FDT_ERR_EXISTS), FDT_ERRTABENT(FDT_ERR_NOSPACE), FDT_ERRTABENT(FDT_ERR_BADOFFSET), FDT_ERRTABENT(FDT_ERR_BADPATH), FDT_ERRTABENT(FDT_ERR_BADPHANDLE), FDT_ERRTABENT(FDT_ERR_BADSTATE), FDT_ERRTABENT(FDT_ERR_TRUNCATED), FDT_ERRTABENT(FDT_ERR_BADMAGIC), FDT_ERRTABENT(FDT_ERR_BADVERSION), FDT_ERRTABENT(FDT_ERR_BADSTRUCTURE), FDT_ERRTABENT(FDT_ERR_BADLAYOUT), FDT_ERRTABENT(FDT_ERR_INTERNAL), FDT_ERRTABENT(FDT_ERR_BADNCELLS), FDT_ERRTABENT(FDT_ERR_BADVALUE), FDT_ERRTABENT(FDT_ERR_BADOVERLAY), FDT_ERRTABENT(FDT_ERR_NOPHANDLES), }; #define FDT_ERRTABSIZE (sizeof(fdt_errtable) / sizeof(fdt_errtable[0])) const char *fdt_strerror(int errval) { if (errval > 0) return ""; else if (errval == 0) return ""; else if (errval > -FDT_ERRTABSIZE) { const char *s = fdt_errtable[-errval].str; if (s) return s; } return ""; } ================================================ FILE: kernel/libs/libfdt/fdt_sw.c ================================================ /* * libfdt - Flat Device Tree manipulation * Copyright (C) 2006 David Gibson, IBM Corporation. * * libfdt is dual licensed: you can use it either under the terms of * the GPL, or the BSD license, at your option. * * a) This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301 USA * * Alternatively, * * b) Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "libfdt_internal.h" static int fdt_sw_probe_(void *fdt) { if (fdt_magic(fdt) == FDT_MAGIC) return -FDT_ERR_BADSTATE; else if (fdt_magic(fdt) != FDT_SW_MAGIC) return -FDT_ERR_BADMAGIC; return 0; } #define FDT_SW_PROBE(fdt) \ { \ int err; \ if ((err = fdt_sw_probe_(fdt)) != 0) \ return err; \ } /* 'memrsv' state: Initial state after fdt_create() * * Allowed functions: * fdt_add_reservmap_entry() * fdt_finish_reservemap() [moves to 'struct' state] */ static int fdt_sw_probe_memrsv_(void *fdt) { int err = fdt_sw_probe_(fdt); if (err) return err; if (fdt_off_dt_strings(fdt) != 0) return -FDT_ERR_BADSTATE; return 0; } #define FDT_SW_PROBE_MEMRSV(fdt) \ { \ int err; \ if ((err = fdt_sw_probe_memrsv_(fdt)) != 0) \ return err; \ } /* 'struct' state: Enter this state after fdt_finish_reservemap() * * Allowed functions: * fdt_begin_node() * fdt_end_node() * fdt_property*() * fdt_finish() [moves to 'complete' state] */ static int fdt_sw_probe_struct_(void *fdt) { int err = fdt_sw_probe_(fdt); if (err) return err; if (fdt_off_dt_strings(fdt) != fdt_totalsize(fdt)) return -FDT_ERR_BADSTATE; return 0; } #define FDT_SW_PROBE_STRUCT(fdt) \ { \ int err; \ if ((err = fdt_sw_probe_struct_(fdt)) != 0) \ return err; \ } /* 'complete' state: Enter this state after fdt_finish() * * Allowed functions: none */ static void *fdt_grab_space_(void *fdt, size_t len) { int offset = fdt_size_dt_struct(fdt); int spaceleft; spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt) - fdt_size_dt_strings(fdt); if ((offset + len < offset) || (offset + len > spaceleft)) return NULL; fdt_set_size_dt_struct(fdt, offset + len); return fdt_offset_ptr_w_(fdt, offset); } int fdt_create(void *buf, int bufsize) { const size_t hdrsize = FDT_ALIGN(sizeof(struct fdt_header), sizeof(struct fdt_reserve_entry)); void *fdt = buf; if (bufsize < hdrsize) return -FDT_ERR_NOSPACE; memset(buf, 0, bufsize); fdt_set_magic(fdt, FDT_SW_MAGIC); fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION); fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION); fdt_set_totalsize(fdt, bufsize); fdt_set_off_mem_rsvmap(fdt, hdrsize); fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt)); fdt_set_off_dt_strings(fdt, 0); return 0; } int fdt_resize(void *fdt, void *buf, int bufsize) { size_t headsize, tailsize; char *oldtail, *newtail; FDT_SW_PROBE(fdt); headsize = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); tailsize = fdt_size_dt_strings(fdt); if ((headsize + tailsize) > fdt_totalsize(fdt)) return -FDT_ERR_INTERNAL; if ((headsize + tailsize) > bufsize) return -FDT_ERR_NOSPACE; oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize; newtail = (char *)buf + bufsize - tailsize; /* Two cases to avoid clobbering data if the old and new * buffers partially overlap */ if (buf <= fdt) { memmove(buf, fdt, headsize); memmove(newtail, oldtail, tailsize); } else { memmove(newtail, oldtail, tailsize); memmove(buf, fdt, headsize); } fdt_set_totalsize(buf, bufsize); if (fdt_off_dt_strings(buf)) fdt_set_off_dt_strings(buf, bufsize); return 0; } int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size) { struct fdt_reserve_entry *re; int offset; FDT_SW_PROBE_MEMRSV(fdt); offset = fdt_off_dt_struct(fdt); if ((offset + sizeof(*re)) > fdt_totalsize(fdt)) return -FDT_ERR_NOSPACE; re = (struct fdt_reserve_entry *)((char *)fdt + offset); re->address = cpu_to_fdt64(addr); re->size = cpu_to_fdt64(size); fdt_set_off_dt_struct(fdt, offset + sizeof(*re)); return 0; } int fdt_finish_reservemap(void *fdt) { int err = fdt_add_reservemap_entry(fdt, 0, 0); if (err) return err; fdt_set_off_dt_strings(fdt, fdt_totalsize(fdt)); return 0; } int fdt_begin_node(void *fdt, const char *name) { struct fdt_node_header *nh; int namelen; FDT_SW_PROBE_STRUCT(fdt); namelen = strlen(name) + 1; nh = fdt_grab_space_(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen)); if (! nh) return -FDT_ERR_NOSPACE; nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); memcpy(nh->name, name, namelen); return 0; } int fdt_end_node(void *fdt) { fdt32_t *en; FDT_SW_PROBE_STRUCT(fdt); en = fdt_grab_space_(fdt, FDT_TAGSIZE); if (! en) return -FDT_ERR_NOSPACE; *en = cpu_to_fdt32(FDT_END_NODE); return 0; } static int fdt_find_add_string_(void *fdt, const char *s) { char *strtab = (char *)fdt + fdt_totalsize(fdt); const char *p; int strtabsize = fdt_size_dt_strings(fdt); int len = strlen(s) + 1; int struct_top, offset; p = fdt_find_string_(strtab - strtabsize, strtabsize, s); if (p) return p - strtab; /* Add it */ offset = -strtabsize - len; struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); if (fdt_totalsize(fdt) + offset < struct_top) return 0; /* no more room :( */ memcpy(strtab + offset, s, len); fdt_set_size_dt_strings(fdt, strtabsize + len); return offset; } int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp) { struct fdt_property *prop; int nameoff; FDT_SW_PROBE_STRUCT(fdt); nameoff = fdt_find_add_string_(fdt, name); if (nameoff == 0) return -FDT_ERR_NOSPACE; prop = fdt_grab_space_(fdt, sizeof(*prop) + FDT_TAGALIGN(len)); if (! prop) return -FDT_ERR_NOSPACE; prop->tag = cpu_to_fdt32(FDT_PROP); prop->nameoff = cpu_to_fdt32(nameoff); prop->len = cpu_to_fdt32(len); *valp = prop->data; return 0; } int fdt_property(void *fdt, const char *name, const void *val, int len) { void *ptr; int ret; ret = fdt_property_placeholder(fdt, name, len, &ptr); if (ret) return ret; memcpy(ptr, val, len); return 0; } int fdt_finish(void *fdt) { char *p = (char *)fdt; fdt32_t *end; int oldstroffset, newstroffset; uint32_t tag; int offset, nextoffset; FDT_SW_PROBE_STRUCT(fdt); /* Add terminator */ end = fdt_grab_space_(fdt, sizeof(*end)); if (! end) return -FDT_ERR_NOSPACE; *end = cpu_to_fdt32(FDT_END); /* Relocate the string table */ oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt); newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt)); fdt_set_off_dt_strings(fdt, newstroffset); /* Walk the structure, correcting string offsets */ offset = 0; while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) { if (tag == FDT_PROP) { struct fdt_property *prop = fdt_offset_ptr_w_(fdt, offset); int nameoff; nameoff = fdt32_to_cpu(prop->nameoff); nameoff += fdt_size_dt_strings(fdt); prop->nameoff = cpu_to_fdt32(nameoff); } offset = nextoffset; } if (nextoffset < 0) return nextoffset; /* Finally, adjust the header */ fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt)); fdt_set_magic(fdt, FDT_MAGIC); return 0; } ================================================ FILE: kernel/libs/libfdt/fdt_wip.c ================================================ /* * libfdt - Flat Device Tree manipulation * Copyright (C) 2006 David Gibson, IBM Corporation. * * libfdt is dual licensed: you can use it either under the terms of * the GPL, or the BSD license, at your option. * * a) This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301 USA * * Alternatively, * * b) Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "libfdt_internal.h" int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset, const char *name, int namelen, uint32_t idx, const void *val, int len) { void *propval; int proplen; propval = fdt_getprop_namelen_w(fdt, nodeoffset, name, namelen, &proplen); if (!propval) return proplen; if (proplen < (len + idx)) return -FDT_ERR_NOSPACE; memcpy((char *)propval + idx, val, len); return 0; } int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name, const void *val, int len) { const void *propval; int proplen; propval = fdt_getprop(fdt, nodeoffset, name, &proplen); if (!propval) return proplen; if (proplen != len) return -FDT_ERR_NOSPACE; return fdt_setprop_inplace_namelen_partial(fdt, nodeoffset, name, strlen(name), 0, val, len); } static void fdt_nop_region_(void *start, int len) { fdt32_t *p; for (p = start; (char *)p < ((char *)start + len); p++) *p = cpu_to_fdt32(FDT_NOP); } int fdt_nop_property(void *fdt, int nodeoffset, const char *name) { struct fdt_property *prop; int len; prop = fdt_get_property_w(fdt, nodeoffset, name, &len); if (!prop) return len; fdt_nop_region_(prop, len + sizeof(*prop)); return 0; } int fdt_node_end_offset_(void *fdt, int offset) { int depth = 0; while ((offset >= 0) && (depth >= 0)) offset = fdt_next_node(fdt, offset, &depth); return offset; } int fdt_nop_node(void *fdt, int nodeoffset) { int endoffset; endoffset = fdt_node_end_offset_(fdt, nodeoffset); if (endoffset < 0) return endoffset; fdt_nop_region_(fdt_offset_ptr_w(fdt, nodeoffset, 0), endoffset - nodeoffset); return 0; } ================================================ FILE: kernel/libs/libfdt/libfdt_internal.h ================================================ #ifndef LIBFDT_INTERNAL_H #define LIBFDT_INTERNAL_H /* * libfdt - Flat Device Tree manipulation * Copyright (C) 2006 David Gibson, IBM Corporation. * * libfdt is dual licensed: you can use it either under the terms of * the GPL, or the BSD license, at your option. * * a) This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301 USA * * Alternatively, * * b) Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #define FDT_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) #define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE)) int fdt_ro_probe_(const void *fdt); #define FDT_RO_PROBE(fdt) \ { \ int err_; \ if ((err_ = fdt_ro_probe_(fdt)) != 0) \ return err_; \ } int fdt_check_node_offset_(const void *fdt, int offset); int fdt_check_prop_offset_(const void *fdt, int offset); const char *fdt_find_string_(const char *strtab, int tabsize, const char *s); int fdt_node_end_offset_(void *fdt, int nodeoffset); static inline const void *fdt_offset_ptr_(const void *fdt, int offset) { return (const char *)fdt + fdt_off_dt_struct(fdt) + offset; } static inline void *fdt_offset_ptr_w_(void *fdt, int offset) { return (void *)(uintptr_t)fdt_offset_ptr_(fdt, offset); } static inline const struct fdt_reserve_entry *fdt_mem_rsv_(const void *fdt, int n) { const struct fdt_reserve_entry *rsv_table = (const struct fdt_reserve_entry *) ((const char *)fdt + fdt_off_mem_rsvmap(fdt)); return rsv_table + n; } static inline struct fdt_reserve_entry *fdt_mem_rsv_w_(void *fdt, int n) { return (void *)(uintptr_t)fdt_mem_rsv_(fdt, n); } #define FDT_SW_MAGIC (~FDT_MAGIC) #endif /* LIBFDT_INTERNAL_H */ ================================================ FILE: kernel/libs/shell_command/Kconfig ================================================ if SHELL menu "Shell Command Support" config SHELL_COMMAND_TASK bool "Command for task management" default y help "command for task management" endmenu endif ================================================ FILE: kernel/libs/shell_command/Makefile ================================================ obj-y += shell_command.o obj-y += clear.o obj-$(CONFIG_SHELL_COMMAND_TASK) += task_cmd.o obj-y += help_cmd.o ================================================ FILE: kernel/libs/shell_command/clear.c ================================================ /* * Copyright (C) 2017 - 2020 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include static int clear_cmd(int argc, char **argv) { printf("\x1b[2J\x1b[H"); return 0; } DEFINE_SHELL_COMMAND(clear, "clear", "Clear the screec", clear_cmd, 0); ================================================ FILE: kernel/libs/shell_command/help_cmd.c ================================================ // SPDX-License-Identifier: GPL-2.0 #include #include extern unsigned long __shell_command_start; extern unsigned long __shell_command_end; static int help_cmd(int argc, char **argv) { struct shell_command *cmd; char *spaces = " "; section_for_each_item(__shell_command_start, __shell_command_end, cmd) { printf("%s%s - %s\n", cmd->name, spaces + (MIN(strlen(cmd->name), strlen(spaces))), cmd->cmd_info); } return 0; } DEFINE_SHELL_COMMAND(help, "help", "print command description", help_cmd, 0); ================================================ FILE: kernel/libs/shell_command/shell_command.c ================================================ /* * Copyright (C) 2020 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include int excute_shell_command(int argc, char **argv) { struct shell_command *cmd; extern unsigned long __shell_command_start; extern unsigned long __shell_command_end; if ((argc == 0) || (argv[0] == NULL)) return -EINVAL; section_for_each_item(__shell_command_start, __shell_command_end, cmd) { if (strcmp(argv[0], cmd->name) == 0) { if (cmd->hdl == NULL) return -ENOENT; return cmd->hdl(argc, argv); } } return -ENOENT; } ================================================ FILE: kernel/libs/shell_command/task_cmd.c ================================================ /* * Copyright (C) 2020 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include static char *state_str[7] = { "Running", " Ready", " Wait", " Waking", "Suspend", "Stopped", " Wrong", }; static inline char *get_state_str(struct task *task) { switch (task->state) { case TASK_STATE_RUNNING: return state_str[0]; case TASK_STATE_READY: return state_str[1]; case TASK_STATE_WAIT_EVENT: return state_str[2]; case TASK_STATE_WAKING: return state_str[3]; case TASK_STATE_SUSPEND: return state_str[4]; case TASK_STATE_STOP: return state_str[5]; } return state_str[6]; } static void dump_task_info(struct task *task) { char vm_str[8] = {0}; struct vcpu *vcpu; if (task_is_vcpu(task)) { vcpu = (struct vcpu *)task->pdata; sprintf(vm_str, "vm-%d/", vcpu->vm->vmid); } printf("%4d %3d %s %s%s\n", task->tid, task->cpu, get_state_str(task), vm_str, task->name); } static int ps_cmd(int argc, char **argv) { printf(" PID CPU STATE NAME\n"); os_for_all_task(dump_task_info); return 0; } DEFINE_SHELL_COMMAND(ps, "ps", "List all task information", ps_cmd, 0); ================================================ FILE: kernel/platform/Kconfig ================================================ menu "Platform Configuration" choice prompt "SOC current used" config SOC_FVP bool "arm fix virtual platform" config SOC_MARVELL_A3700 bool "marvell a3700 SOC" config SOC_BCM2837 bool "bcm2837 SOC" config SOC_BCM2838 bool "bcm2838 SOC" config SOC_AMLOGIC bool "amlogic SOC" config SOC_QEMU bool "qemu platform" endchoice source "platform/espressobin/Kconfig" source "platform/fvp/Kconfig" source "platform/raspberry3/Kconfig" source "platform/raspberry4/Kconfig" source "platform/amlogic/Kconfig" source "platform/qemu/Kconfig" endmenu ================================================ FILE: kernel/platform/Makefile ================================================ obj-y += platform.o obj-$(CONFIG_SOC_MARVELL_A3700) += espressobin/ obj-$(CONFIG_SOC_FVP) += fvp/ obj-$(CONFIG_SOC_BCM2837) += raspberry3/ obj-$(CONFIG_SOC_BCM2838) += raspberry4/ obj-$(CONFIG_SOC_AMLOGIC) += amlogic/ obj-$(CONFIG_SOC_QEMU) += qemu/ ================================================ FILE: kernel/platform/amlogic/Kconfig ================================================ if SOC_AMLOGIC config MINOS_ENTRY_ADDRESS hex "entry address of system" default 0xeb80c000 help the entry address is the start address plus nr_cpus * TASK_STACK_SIZE, the kvim3 has 6 cores config MINOS_RAM_SIZE hex "memory size for Minos" default 0x2000000 range 0x1000000 0x4000000 help the memory size for Minos config NR_CPUS int "number of cpu in system" default 6 help how many cpu in current system config UART_BASE hex "uart controller memory base" default 0xff803000 config UART_IO_SIZE hex "uart controller iomem size" default 0x1000 config DTB_LOAD_ADDRESS hex default 0xed600000 endif ================================================ FILE: kernel/platform/amlogic/Makefile ================================================ obj-y += kvim3.o obj-$(CONFIG_VIRT) += amlogic_smc.o ================================================ FILE: kernel/platform/amlogic/amlogic_smc.c ================================================ /* * Copyright (C) 2019 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #define SECMON_IN_BASE_FUNC 0x82000020 #define SECMON_OUT_BASE_FUNC 0x82000021 #define SECKEY_STORAGE_QUERY 0x82000060 #define SECKEY_STORAGE_READ 0x82000061 #define SECKEY_STORAGE_WRITE 0x82000062 #define SECKEY_STORAGE_TELL 0x82000063 #define SECKEY_STORAGE_VERIFY 0x82000064 #define SECKEY_STORAGE_STATUS 0x82000065 #define SECKEY_STORAGE_LIST 0x82000067 #define SECKEY_STORAGE_REMOVE 0x82000068 #define SECKEY_STORAGE_IN_FUNC 0x82000023 #define SECKEY_STORAGE_OUT_FUNC 0x82000024 #define SECKEY_STORAGE_BLOCK_FUNC 0x82000025 #define SECKEY_STORAGE_SIZE_FUNC 0x82000027 #define SECKEY_STORAGE_SET_ENCTYPE 0x8200006a #define SECKEY_STORAGE_GET_ENCTYPE 0x8200006b #define SECKEY_STORAGE_VERSION 0x8200006c #define EFUSE_READ_CMD 0x82000030 #define EFUSE_WRITE_CMD 0x82000031 #define EFUSE_GET_MAX_CMD 0x82000033 #define CPUINFO_CMD 0x82000044 #define AUDIO_QUERY_LICENSE_CMD 0x82000050 #define AMLOGIC_SMC_BASE 0x82000000 #define AMLOGIC_SMC_HANDLER_NUMBER 128 #define AMLOGIC_SMC_END (AMLOGIC_SMC_BASE + AMLOGIC_SMC_HANDLER_NUMBER) static svc_handler_t amlogic_smc_fn[128]; static int amlogic_unknown_smc(gp_regs *c, uint32_t id, unsigned long *args) { struct arm_smc_res res; smc_call(id, args[0], args[1], args[2], args[3], 0, 0, 0, &res); SVC_RET4(c, res.a0, res.a1, res.a2, res.a3); return 0; } static int sip_smc_handler(gp_regs *c, uint32_t id, unsigned long *args) { struct vcpu *vcpu = get_current_vcpu(); struct vm *vm = vcpu->vm; svc_handler_t fn; pr_debug("sip function for amlogic 0x%x\n", id); if (!vm_is_host_vm(vm)) return -EPERM; fn = amlogic_smc_fn[id - AMLOGIC_SMC_BASE]; if (fn) return fn(c, id, args); else return amlogic_unknown_smc(c, id, args); } DEFINE_SMC_HANDLER("sip_smc_desc", SVC_STYPE_SIP, SVC_STYPE_SIP, sip_smc_handler); static int inline amlogic_install_smc(svc_handler_t fn, uint32_t id) { if ((id < AMLOGIC_SMC_BASE) || (id > AMLOGIC_SMC_END)) return -EINVAL; amlogic_smc_fn[id - AMLOGIC_SMC_BASE] = fn; return 0; } static inline int amlogic_smc_call(gp_regs *c, uint32_t id) { struct arm_smc_res res; smc_call(id, 0, 0, 0, 0, 0, 0, 0, &res); SVC_RET1(c, res.a0); return 0; } static inline int amlogic_smc_call2(gp_regs *c, uint32_t id, unsigned long arg) { struct arm_smc_res res; smc_call(id, arg, 0, 0, 0, 0, 0, 0, &res); SVC_RET1(c, res.a0); return 0; } static int secmon_in_base(gp_regs *c, uint32_t id, unsigned long *args) { return amlogic_smc_call(c, id); } static int secmon_out_base(gp_regs *c, uint32_t id, unsigned long *args) { return amlogic_smc_call(c, id); } static int seckey_storage_query(gp_regs *c, uint32_t id, unsigned long *args) { return amlogic_smc_call(c, id); } static int seckey_storage_read(gp_regs *c, uint32_t id, unsigned long *args) { return amlogic_smc_call(c, id); } static int seckey_storage_write(gp_regs *c, uint32_t id, unsigned long *args) { return amlogic_smc_call(c, id); } static int seckey_storage_tell(gp_regs *c, uint32_t id, unsigned long *args) { return amlogic_smc_call(c, id); } static int seckey_storage_verify(gp_regs *c, uint32_t id, unsigned long *args) { return amlogic_smc_call(c, id); } static int seckey_storage_status(gp_regs *c, uint32_t id, unsigned long *args) { return amlogic_smc_call(c, id); } static int seckey_storage_list(gp_regs *c, uint32_t id, unsigned long *args) { return amlogic_smc_call(c, id); } static int seckey_storage_remove(gp_regs *c, uint32_t id, unsigned long *args) { return amlogic_smc_call(c, id); } static int seckey_storage_in_func(gp_regs *c, uint32_t id, unsigned long *args) { return amlogic_smc_call(c, id); } static int seckey_storage_out_func(gp_regs *c, uint32_t id, unsigned long *args) { return amlogic_smc_call(c, id); } static int seckey_storage_block_func(gp_regs *c, uint32_t id, unsigned long *args) { return amlogic_smc_call(c, id); } static int seckey_storage_size_func(gp_regs *c, uint32_t id, unsigned long *args) { return amlogic_smc_call(c, id); } static int seckey_storage_get_enctype(gp_regs *c, uint32_t id, unsigned long *args) { return amlogic_smc_call(c, id); } static int seckey_storage_set_enctype(gp_regs *c, uint32_t id, unsigned long *args) { return amlogic_smc_call2(c, id, args[0]); } static int seckey_storage_version(gp_regs *c, uint32_t id, unsigned long *args) { return amlogic_smc_call(c, id); } static int efuse_read_cmd(gp_regs *c, uint32_t id, unsigned long *args) { struct arm_smc_res res; smc_call(id, args[0], args[1], 0, 0, 0, 0, 0, &res); SVC_RET1(c, res.a0); return 0; } static int efuse_write_cmd(gp_regs *c, uint32_t id, unsigned long *args) { struct arm_smc_res res; smc_call(id, args[0], args[1], 0, 0, 0, 0, 0, &res); SVC_RET1(c, res.a0); return 0; } static int efuse_get_max_cmd(gp_regs *c, uint32_t id, unsigned long *args) { return amlogic_smc_call(c, id); } static int cpuinfo_cmd(gp_regs *c, uint32_t id, unsigned long *args) { struct arm_smc_res res; smc_call(id, args[0], args[1], args[2], 0, 0, 0, 0, &res); SVC_RET1(c, res.a0); return 0; } static int audio_query_license_cmd(gp_regs *c, uint32_t id, unsigned long *args) { struct arm_smc_res res; smc_call(id, args[0], args[1], 0, 0, 0, 0, 0, &res); SVC_RET1(c, res.a0); return 0; } static int __init_text amlogic_smc_init(void) { amlogic_install_smc(secmon_in_base, SECMON_IN_BASE_FUNC); amlogic_install_smc(secmon_out_base, SECMON_OUT_BASE_FUNC); amlogic_install_smc(seckey_storage_query, SECKEY_STORAGE_QUERY); amlogic_install_smc(seckey_storage_read, SECKEY_STORAGE_READ); amlogic_install_smc(seckey_storage_write, SECKEY_STORAGE_WRITE); amlogic_install_smc(seckey_storage_tell, SECKEY_STORAGE_TELL); amlogic_install_smc(seckey_storage_verify, SECKEY_STORAGE_VERIFY); amlogic_install_smc(seckey_storage_status, SECKEY_STORAGE_STATUS); amlogic_install_smc(seckey_storage_list, SECKEY_STORAGE_LIST); amlogic_install_smc(seckey_storage_remove, SECKEY_STORAGE_REMOVE); amlogic_install_smc(seckey_storage_in_func, SECKEY_STORAGE_IN_FUNC); amlogic_install_smc(seckey_storage_out_func, SECKEY_STORAGE_OUT_FUNC); amlogic_install_smc(seckey_storage_block_func, SECKEY_STORAGE_BLOCK_FUNC); amlogic_install_smc(seckey_storage_size_func, SECKEY_STORAGE_SIZE_FUNC); amlogic_install_smc(seckey_storage_get_enctype, SECKEY_STORAGE_SET_ENCTYPE); amlogic_install_smc(seckey_storage_set_enctype, SECKEY_STORAGE_GET_ENCTYPE); amlogic_install_smc(seckey_storage_version, SECKEY_STORAGE_VERSION); amlogic_install_smc(efuse_read_cmd, EFUSE_READ_CMD); amlogic_install_smc(efuse_write_cmd, EFUSE_WRITE_CMD); amlogic_install_smc(efuse_get_max_cmd, EFUSE_GET_MAX_CMD); amlogic_install_smc(cpuinfo_cmd, CPUINFO_CMD); amlogic_install_smc(audio_query_license_cmd, AUDIO_QUERY_LICENSE_CMD); return 0; } module_initcall(amlogic_smc_init); ================================================ FILE: kernel/platform/amlogic/kvim3.c ================================================ /* * Copyright (C) 2019 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #ifdef CONFIG_VIRT #include static int kvim3_setup_hvm(struct vm *vm, void *data) { return 0; } #endif static struct platform platform_kvim3 = { .name = "khadas,vim3", .cpu_on = psci_cpu_on, .cpu_off = psci_cpu_off, #ifdef CONFIG_VIRT .setup_hvm = kvim3_setup_hvm, #endif .system_reboot = psci_system_reboot, .system_shutdown = psci_system_shutdown, }; DEFINE_PLATFORM(platform_kvim3); ================================================ FILE: kernel/platform/espressobin/Kconfig ================================================ if SOC_MARVELL_A3700 config MINOS_ENTRY_ADDRESS hex "entry address of system" default 0x3c004000 help the entry address is the start address plus nr_cpus * TASK_STACK_SIZE config MINOS_RAM_SIZE hex "memory size for Minos" default 0x4000000 range 0x1000000 0x4000000 help the memory size for Minos config NR_CPUS int "number of cpu in system" default 2 help how many cpu in current system config UART_BASE hex "uart controller iomem base address" default 0xd0012000 config UART_IO_SIZE hex "uart controller iomem size" default 0x1000 endif ================================================ FILE: kernel/platform/espressobin/Makefile ================================================ obj-y += espressobin.o ================================================ FILE: kernel/platform/espressobin/espressobin.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #ifdef CONFIG_VIRT static int espressobin_setup_vm0(struct vm *vm, void *dtb) { /* create the pcie region for the vm0 */ create_guest_mapping(&vm->vs, 0xe8000000, 0xe8000000, 0x1000000, VM_IO); create_guest_mapping(&vm->vs, 0xe9000000, 0xe9000000, 0x10000, VM_IO); create_guest_mapping(&vm->vs, 0xd0070000, 0xd0070000, 0x20000, VM_IO); return 0; } #endif static struct platform platform_espressobin = { .name = "marvell,armada-3720-community", .cpu_on = psci_cpu_on, .cpu_off = psci_cpu_off, .system_reboot = psci_system_reboot, .system_shutdown = psci_system_shutdown, #ifdef CONFIG_VIRT .setup_hvm = espressobin_setup_vm0, #endif }; DEFINE_PLATFORM(platform_espressobin); ================================================ FILE: kernel/platform/fvp/Kconfig ================================================ if SOC_FVP config MINOS_ENTRY_ADDRESS hex "entry address of system" default 0xc0000000 help the entry address is the start address plus nr_cpus * TASK_STACK_SIZE config MINOS_RAM_SIZE hex "memory size for Minos" default 0x4000000 range 0x1000000 0x4000000 help the memory size for Minos config NR_CPUS int "number of cpu in system" default 4 help how many cpu in current system config UART_BASE hex "uart controller iomem base address" default 0x1c0a0000 config UART_IO_SIZE hex "uart controller iomem size" default 0x1000 config UART_IRQ int "the irq number for uart" default 38 endif ================================================ FILE: kernel/platform/fvp/Makefile ================================================ obj-y += fvp.o ================================================ FILE: kernel/platform/fvp/fvp.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #ifdef CONFIG_VIRT #include #endif #ifdef CONFIG_VIRT static int inline fvp_setup_hvm_of(struct vm *vm, void *data) { int node, len; const void *val; /* disable the armv7-timer-mem for fvp*/ node = fdt_path_offset(data, "/timer@2a810000"); if (node < 0) return 0; val = fdt_getprop(data, node, "compatible", &len); if (!val || len <= 0) return 0; if (!strcmp((char *)val, "arm,armv7-timer-mem")) { pr_notice("delete the armv7 mem timer\n"); fdt_del_node(data, node); } return 0; } static int fvp_setup_hvm(struct vm *vm, void *data) { if (vm->flags & VM_FLAGS_SETUP_OF) return fvp_setup_hvm_of(vm, data); return 0; } #endif static struct platform platform_fvp = { .name = "arm,fvp-base", .cpu_on = psci_cpu_on, .cpu_off = psci_cpu_off, #ifdef CONFIG_VIRT .setup_hvm = fvp_setup_hvm, #endif .system_reboot = psci_system_reboot, .system_shutdown = psci_system_shutdown, }; DEFINE_PLATFORM(platform_fvp); ================================================ FILE: kernel/platform/platform.c ================================================ /* * Copyright (C) 2019 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include extern unsigned char __platform_start; extern unsigned char __platform_end; struct platform *platform = NULL; void platform_set_to(const char *name) { struct platform **pp; struct platform *p; section_for_each_item(__platform_start, __platform_end, pp) { p = *pp; if (strcmp(p->name, name) == 0) { platform = p; break; } } } int platform_iomem_valid(unsigned long addr) { if (platform->iomem_valid) return platform->iomem_valid(addr); else return 1; } void platform_init(void) { if (platform->platform_init) platform->platform_init(); } static int __init_text platform_early_init(void) { /* check whether the platform has been set * by the arch code in the early boot stage */ if (platform == NULL) panic("no platform found ...\n"); if (platform->parse_mem_info) platform->parse_mem_info(); return 0; } early_initcall(platform_early_init); ================================================ FILE: kernel/platform/qemu/Kconfig ================================================ if SOC_QEMU config MINOS_ENTRY_ADDRESS hex "entry address of system" default 0x40000000 help the entry address is the start address plus nr_cpus * TASK_STACK_SIZE config MINOS_RAM_SIZE hex "memory size for Minos" default 0x4000000 range 0x1000000 0x4000000 help the memory size for Minos config NR_CPUS int "number of cpu in system" default 4 help how many cpu in current system config UART_BASE hex "uart controller iomem base address" default 0x90000000 config UART_IO_SIZE hex "uart controller iomem size" default 0x1000 config UART_IRQ int "the irq number for uart" default 33 endif ================================================ FILE: kernel/platform/qemu/Makefile ================================================ obj-y += qemu.o ================================================ FILE: kernel/platform/qemu/qemu.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #ifdef CONFIG_VIRT #include #include #endif #ifdef CONFIG_VIRT static int qemu_setup_hvm(void *item, void *data) { struct vm *vm = (struct vm *)item; int ret; if (!vm_is_host_vm(vm)) return 0; /* * workaroud, create the PCIE memory region for VM * since currently minos do not support parsing the * PCI range, TBD. * pci_bus 0000:00: root bus resource [bus 00-ff] * pci_bus 0000:00: root bus resource [io 0x0000-0xffff] * pci_bus 0000:00: root bus resource [mem 0x10000000-0x3efeffff] * pci_bus 0000:00: root bus resource [mem 0x8000000000-0xffffffffff] */ split_vmm_area(&vm->mm, 0x0, 0x10000, VM_GUEST_IO | VM_RW); split_vmm_area(&vm->mm, 0x10000000, 0x2EFF0000, VM_GUEST_IO | VM_RW | VM_HUGE); split_vmm_area(&vm->mm, 0x8000000000, 0x8000000000, VM_GUEST_IO | VM_RW | __VM_HUGE_1G); ret = create_guest_mapping(&vm->mm, 0x0, 0x0, 0x10000, VM_GUEST_IO | VM_RW); ret += create_guest_mapping(&vm->mm, 0x10000000, 0x10000000, 0x2EFF0000, VM_GUEST_IO | VM_RW | VM_HUGE); ret += create_guest_mapping(&vm->mm, 0x8000000000, 0x8000000000, 0x8000000000, VM_GUEST_IO | VM_RW | __VM_HUGE_1G); if (ret) pr_err("map PCIE memory region for guest failed\n"); return ret; } static int qemu_platform_init(void) { register_hook(qemu_setup_hvm, OS_HOOK_CREATE_VM); return 0; } device_initcall(qemu_platform_init); #endif static struct platform platform_qemu = { .name = "linux,qemu-arm64", #if 1 .cpu_on = psci_cpu_on, .cpu_off = psci_cpu_off, .system_reboot = psci_system_reboot, .system_shutdown = psci_system_shutdown, #else .cpu_on = psci_cpu_on_hvc, .cpu_off = psci_cpu_off_hvc, .system_reboot = psci_system_reboot_hvc, .system_shutdown = psci_system_shutdown_hvc, #endif }; DEFINE_PLATFORM(platform_qemu); ================================================ FILE: kernel/platform/r8a7795/Kconfig ================================================ # SPDX-License-Identifier: GPL-2.0 if SOC_R8A7795 config MINOS_START_ADDRESS hex "memory start address of system" default 0x48000000 config MINOS_ENTRY_ADDRESS hex "entry address of system" default 0x48010000 help MINOS_START_ADDRESS + TASK_STACK_SIZE * NR_CPUS config MINOS_RAM_SIZE hex "memory size for Minos" range 0x1000000 0x4000000 default 0x2000000 help the memory size for Minos config NR_CPUS int "number of cpu in system" range 1 1 if !SMP range 1 8 default 1 if !SMP default 8 help how many cpu in current system config UART_BASE hex "uart controller iomem base address" default 0xe6e88000 config UART_IO_SIZE hex "uart controller iomem size" default 0x1000 config MINOS_TEXT_OFFSET hex "entry offset from start of ram" default 0x10000 help MINOS_ENTRY_ADDRESS - 0x48000000 config DTB_LOAD_ADDRESS hex "device tree blob address" default 0x49e00000 help MINOS_START_ADDRESS + MINOS_RAM_SIZE - 0x200000 config HVM_SPI_VIRQ_NR int "SPI VIRQ for vm0" default 480 help NR_SPI_IRQS config GVM_SPI_VIRQ_NR int "SPI VIRQ for non-vm0" default 480 help NR_SPI_IRQS endif ================================================ FILE: kernel/platform/r8a7795/Makefile ================================================ obj-y += r8a7795.o ================================================ FILE: kernel/platform/r8a7795/r8a7795.c ================================================ // SPDX-License-Identifier: GPL-2.0 #include #include static struct platform platform_r8a7795 = { .name = "renesas,r8a7795", .cpu_on = psci_cpu_on, .cpu_off = psci_cpu_off, .system_reboot = psci_system_reboot, .system_shutdown = psci_system_shutdown, }; DEFINE_PLATFORM(platform_r8a7795); ================================================ FILE: kernel/platform/raspberry3/Kconfig ================================================ if SOC_BCM2837 config PLATFORM_BCM2837 def_bool y select VIRQCHIP_BCM2836 select IRQCHIP_BCM2836 config MINOS_ENTRY_ADDRESS hex "entry address of system" default 0x28008000 help the entry address is the start address plus nr_cpus * TASK_STACK_SIZE config MINOS_RAM_SIZE hex "memory size for Minos" default 0x2000000 range 0x1000000 0x4000000 help the memory size for Minos config NR_CPUS int "number of cpu in system" default 4 help how many cpu in current system config UART_BASE hex "uart controller iomem base address" default 0x3f215040 config UART_IO_SIZE hex "uart controller iomem size" default 0x1000 config HVM_SPI_VIRQ_NR int "SPI VIRQ for vm0" default 96 endif ================================================ FILE: kernel/platform/raspberry3/Makefile ================================================ obj-y += raspberry3.o ================================================ FILE: kernel/platform/raspberry3/raspberry3.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #ifdef CONFIG_VIRT #include #include #include static int raspberry3_setup_hvm(struct vm *vm, void *dtb) { int i, offset, node; char name[16]; uint64_t addr; uint64_t dtb_addr = 0; uint32_t *tmp = (uint32_t *)&dtb_addr; offset = of_get_node_by_name(dtb, 0, "cpus"); if (offset < 0) { pr_err("can not find vcpus node for hvm\n"); return -ENOENT; } /* * using spin table boot methold redirect the * relase addr to the interrupt controller space */ for (i = 0; i < vm->vcpu_nr; i++) { memset(name, 0, 16); sprintf(name, "cpu@%d", i); node = fdt_subnode_offset(dtb, offset, name); if (node <= 0) continue; addr = BCM2836_RELEASE_ADDR + i * sizeof(uint64_t); pr_notice("vcpu-%d release addr redirect to 0x%p\n", i, addr); tmp[0] = cpu_to_fdt32(addr >> 32); tmp[1] = cpu_to_fdt32(addr & 0xffffffff); fdt_setprop(dtb, node, "cpu-release-addr", (void *)tmp, 2 * sizeof(uint32_t)); } /* * redirect the bcm2835 interrupt controller's iomem * to 0x40000200 */ node = fdt_path_offset(dtb, "/soc/interrupt-controller@7e00b200"); if (node <= 0) { pr_warn("can not find interrupt-controller@7e00b200\n"); return -ENOENT; } tmp[0] = cpu_to_fdt32(0x40000200); tmp[1] = cpu_to_fdt32(0x200); fdt_setprop(dtb, node, "reg", (void *)tmp, 2 * sizeof(uint32_t)); fdt_set_name(dtb, node, "interrupt-controller@40000200"); /* mask 40 - 52 virq for hvm which is internal use */ for (i = 40; i <= 52; i++) request_virq(vm, i, 0); pr_notice("raspberry3 setup vm done\n"); return 0; } #endif static void raspberry3_system_reboot(int mode, const char *cmd) { } static void raspberry3_system_shutdown(void) { } static int raspberry4_iomem_valid(unsigned long addr) { if ((addr >= 0x40000000) && (addr < 0x40001000)) return 0; return 1; } static void raspberry3_parse_mem_info(void) { /* memory start at 0x3b400000 may has been used * by other hardware, need to resve it ? */ //split_memory_region(0x3b400000, 60 * 1024 * 1024, 0); } static struct platform platform_raspberry3 = { .name = "raspberrypi,3-model-b-plus", .cpu_on = spin_table_cpu_on, .system_reboot = raspberry3_system_reboot, .system_shutdown = raspberry3_system_shutdown, #ifdef CONFIG_VIRT .setup_hvm = raspberry3_setup_hvm, #endif .parse_mem_info = raspberry3_parse_mem_info, .iomem_valid = raspberry4_iomem_valid, }; DEFINE_PLATFORM(platform_raspberry3); ================================================ FILE: kernel/platform/raspberry4/Kconfig ================================================ if SOC_BCM2838 config MINOS_ENTRY_ADDRESS hex "entry address of system" default 0x37408000 help the entry address is the start address plus nr_cpus * TASK_STACK_SIZE config MINOS_RAM_SIZE hex "memory size for Minos" default 0x4000000 range 0x1000000 0x4000000 help the memory size for Minos config NR_CPUS int "number of cpu in system" default 4 help how many cpu in current system config UART_BASE hex "uart controller iomem base address" default 0xfe215040 config UART_IO_SIZE hex "uart controller iomem size" default 0x1000 endif ================================================ FILE: kernel/platform/raspberry4/Makefile ================================================ obj-y += raspberry4.o ================================================ FILE: kernel/platform/raspberry4/raspberry4.c ================================================ /* * Copyright (C) 2018 Min Le (lemin9538@gmail.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #ifdef CONFIG_VIRT #include #include #include #include #define BCM2838_RELEASE_ADDR 0xff800000 static int bcm2838_fake_scu_read(struct vdev *vdev, gp_regs *regs, int idx, unsigned long address, unsigned long *value) { return 0; } static int inline bcm2838_bootup_secondary(struct vm *vm, int cpu, unsigned long entry) { if (cpu >= vm->vcpu_nr) { pr_err("no such vcpu vcpu-id:%d\n", cpu); return -EINVAL; } if (cpu == 0) return 0; return vcpu_power_on(get_current_vcpu(), cpuid_to_affinity(cpu), entry, 0); } static int bcm2838_fake_64bit_scu_write(struct vdev *vdev, gp_regs *regs, int idx, unsigned long offset, unsigned long *value) { int cpu; struct vm *vm = vdev->vm; if (offset % sizeof(uint64_t)) { pr_err("unsupport reg offset 0x%x\n", offset); return -EINVAL; } cpu = offset / sizeof(uint64_t); return bcm2838_bootup_secondary(vm, cpu, *value); } static int bcm2838_fake_32bit_scu_write(struct vdev *vdev, gp_regs *regs, int idx, unsigned long offset, unsigned long *value) { int cpu; cpu = (offset - LOCAL_MAILBOX3_SET0) >> 4; return bcm2838_bootup_secondary(vdev->vm, cpu, *value); } static int raspberry4_setup_hvm(struct vm *vm, void *dtb) { int i, offset, node; char name[16]; uint64_t addr; uint64_t dtb_addr = 0; uint32_t *tmp = (uint32_t *)&dtb_addr; struct vdev *vdev; offset = of_get_node_by_name(dtb, 0, "cpus"); if (offset < 0) { pr_err("can not find vcpus node for hvm\n"); return -ENOENT; } /* * using spin table boot methold redirect the * relase addr to the interrupt controller space */ for (i = 0; i < vm->vcpu_nr; i++) { memset(name, 0, 16); sprintf(name, "cpu@%d", i); node = fdt_subnode_offset(dtb, offset, name); if (node <= 0) continue; addr = BCM2838_RELEASE_ADDR + i * sizeof(uint64_t); pr_notice("vcpu-%d release addr redirect to 0x%p\n", i, addr); tmp[0] = cpu_to_fdt32(addr >> 32); tmp[1] = cpu_to_fdt32(addr & 0xffffffff); fdt_setprop(dtb, node, "cpu-release-addr", (void *)tmp, 2 * sizeof(uint32_t)); } /* register a fake system controller for smp up handler */ if (vm->vcpu_nr > 1) { vdev = zalloc(sizeof(struct vdev)); if (!vdev) panic("no more memory for spi-table\n"); host_vdev_init(vm, vdev, "smp-fake-con"); vdev_add_iomem_range(vdev, BCM2838_RELEASE_ADDR, 0x1000); /* * for raspberry4, currently kernel will use the local * interrupt IC base address to wake up other cpu in 32 * bit mode, which is different with 64bit */ vdev->read = bcm2838_fake_scu_read; if (vm->flags & VM_FLAGS_32BIT) vdev->write = bcm2838_fake_32bit_scu_write; else vdev->write = bcm2838_fake_64bit_scu_write; vdev_add(vdev); } /* create pcie address mapping for VM0 */ create_guest_mapping(&vm->mm, 0x600000000, 0x600000000, 0x4000000, VM_GUEST_IO | VM_RW); pr_notice("raspberry4 setup vm done\n"); return 0; } #endif static int raspberry4_iomem_valid(unsigned long addr) { /* * 0xff800000 ---> 0xff800fff will used for local_intc * in 32bit mode, kernel will use this address to wake * up other cpus */ if ((addr >= 0xff800000) && (addr < 0xff800fff)) return 0; /* * 0x3b400000 ---> 0x3ebfffff will used for the framebuffer * in raspberry4 */ if ((addr >= 0x3b400000) && (addr < 0x3ebfffff)) return 1; if ((addr >= 0xf3000000) && (addr < 0xffffffff)) return 1; pr_err("memory region:0x%p not register\n", addr, addr); return 0; } static void raspberry4_system_reboot(int mode, const char *cmd) { } static void raspberry4_system_shutdown(void) { } /* * VC FW passed below memory region to Linux kernel * 0x0 - 0x3b3fffff * 0x40000000 - 0xfc000000 * * memory map get from vc FW and the cmdline is: * 0x0 - 0x3b3fffff [Linux Memory] * 0x3b400000 - 0x3ebfffff [Unknown may be VC] * 0x3ec00000 - 0x7ebfffff [VC memory] * 0x7ec00000 - 0xf2ffffff [Linux] * * need parse the right information to Linux kernel, from * the dtb just can get the first memory information, the * below is the memory map when using Minos Hypervisor * * 0x0 - 0x373fffff [Linux Memeory] * 0x37400000 - 0x3b3fffff@64M [Minos Memory] * 0x3b400000 - 0x3ebfffff [Unknown Memory do not passed to Linux] * 0x3ec00000 - 0x7ebfffff [Need to passed to Linux] */ static void raspberry4_parse_mem_info(void) { int node, len; fdt32_t *v; unsigned long base, size; /* * need to parse other memory region to support 2G or 4G * rpi-4 version, currently, use a simple way, the memory * information will store in bootargs/extra-memory field in * minos device tree */ if (!dtb_address) return; node = fdt_path_offset(dtb_address, "/chosen"); if (node <= 0) return; v = (fdt32_t *)fdt_getprop(dtb_address, node, "extra-memory", &len); if (!v || (len < 8)) return; len = len / 4; if (len % 2 != 0) { pr_err("wrong memory config in extra-memory\n"); return; } while (len > 0) { base = fdt32_to_cpu(v[0]); size = fdt32_to_cpu(v[1]); pr_notice("register extra memory region 0x%x 0x%x\n", base, size); add_memory_region(base, size, MEMORY_REGION_TYPE_NORMAL, 0); len -= 2; v += 2; } } static struct platform platform_raspberry4 = { .name = "raspberrypi,4-model-b", .cpu_on = spin_table_cpu_on, .system_reboot = raspberry4_system_reboot, .system_shutdown = raspberry4_system_shutdown, .iomem_valid = raspberry4_iomem_valid, #ifdef CONFIG_VIRT .setup_hvm = raspberry4_setup_hvm, #endif .parse_mem_info = raspberry4_parse_mem_info, }; DEFINE_PLATFORM(platform_raspberry4); ================================================ FILE: kernel/scripts/Kconfiglib/LICENSE.txt ================================================ Copyright (c) 2011-2019, Ulf Magnusson Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ================================================ FILE: kernel/scripts/Kconfiglib/MANIFEST.in ================================================ # Include the license file in source distributions include LICENSE.txt ================================================ FILE: kernel/scripts/Kconfiglib/README.rst ================================================ .. contents:: Table of contents :backlinks: none News ---- Dependency loop with recent linux-next kernels ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To fix issues with dependency loops on recent linux-next kernels, apply `this patch `_. Hopefully, it will be in ``linux-next`` soon. ``windows-curses`` is no longer automatically installed on Windows ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Starting with Kconfiglib 13.0.0, the `windows-curses `__ package is no longer automatically installed on Windows, and needs to be installed manually for the terminal ``menuconfig`` to work. This fixes installation of Kconfiglib on MSYS2, which is not compatible with ``windows-curses``. See `this issue `__. The ``menuconfig`` now shows a hint re. installing ``windows-curses`` when the ``curses`` module can't be imported on Windows. Sorry if this change caused problems! Overview -------- Kconfiglib is a `Kconfig `__ implementation in Python 2/3. It started out as a helper library, but now has a enough functionality to also work well as a standalone Kconfig implementation (including `terminal and GUI menuconfig interfaces `_ and `Kconfig extensions`_). The entire library is contained in `kconfiglib.py `_. The bundled scripts are implemented on top of it. Implementing your own scripts should be relatively easy, if needed. Kconfiglib is used exclusively by e.g. the `Zephyr `__, `esp-idf `__, and `ACRN `__ projects. It is also used for many small helper scripts in various projects. Since Kconfiglib is based around a library, it can be used e.g. to generate a `Kconfig cross-reference `_ (note: heavy page), using the same robust Kconfig parser used for other Kconfig tools, instead of brittle ad-hoc parsing. The documentation generation script can be found `here `__. Kconfiglib implements the recently added `Kconfig preprocessor `__. For backwards compatibility, environment variables can be referenced both as ``$(FOO)`` (the new syntax) and as ``$FOO`` (the old syntax). The old syntax is deprecated, but will probably be supported for a long time, as it's needed to stay compatible with older Linux kernels. The major version will be increased if support is ever dropped. Using the old syntax with an undefined environment variable keeps the string as is. Note: See `this issue `__ if you run into a "macro expanded to blank string" error with kernel 4.18+. See `this page `__ for some Kconfig tips and best practices. Installation ------------ Installation with pip ~~~~~~~~~~~~~~~~~~~~~ Kconfiglib is available on `PyPI `_ and can be installed with e.g. .. code:: $ pip(3) install kconfiglib Microsoft Windows is supported. The ``pip`` installation will give you both the base library and the following executables. All but two (``genconfig`` and ``setconfig``) mirror functionality available in the C tools. - `menuconfig `_ - `guiconfig `_ - `oldconfig `_ - `olddefconfig `_ - `savedefconfig `_ - `defconfig `_ - `alldefconfig `_ - `allnoconfig `_ - `allmodconfig `_ - `allyesconfig `_ - `listnewconfig `_ - `genconfig `_ - `setconfig `_ ``genconfig`` is intended to be run at build time. It generates a C header from the configuration and (optionally) information that can be used to rebuild only files that reference Kconfig symbols that have changed value. Starting with Kconfiglib version 12.2.0, all utilities are compatible with both Python 2 and Python 3. Previously, ``menuconfig.py`` only ran under Python 3 (i.e., it's now more backwards compatible than before). **Note:** If you install Kconfiglib with ``pip``'s ``--user`` flag, make sure that your ``PATH`` includes the directory where the executables end up. You can list the installed files with ``pip(3) show -f kconfiglib``. All releases have a corresponding tag in the git repository, e.g. ``v13.7.0`` (the latest version). `Semantic versioning `_ is used. There's been ten small changes to the behavior of the API and a Windows packaging change (`1 `_, `2 `_, `3 `_, `4 `_, `5 `_, `6 `_, `7 `_, `8 `_, `9 `_, `10 `_, `packaging change `_), which is why the major version is at 13 rather than 2. I do major version bumps for all behavior changes, even tiny ones, and most of these were fixes for baby issues in the early days of the Kconfiglib 2 API. Manual installation ~~~~~~~~~~~~~~~~~~~ Just drop ``kconfiglib.py`` and the scripts you want somewhere. There are no third-party dependencies, but the terminal ``menuconfig`` won't work on Windows unless a package like `windows-curses `__ is installed. Installation for the Linux kernel ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ See the module docstring at the top of `kconfiglib.py `_. Python version compatibility (2.7/3.2+) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Kconfiglib and all utilities run under both Python 2.7 and Python 3.2 and later. The code mostly uses basic Python features and has no third-party dependencies, so keeping it backwards-compatible is pretty low effort. The 3.2 requirement comes from ``argparse``. ``format()`` with unnumbered ``{}`` is used as well. A recent Python 3 version is recommended if you have a choice, as it'll give you better Unicode handling. Getting started --------------- 1. `Install `_ the library and the utilities. 2. Write `Kconfig `__ files that describe the available configuration options. See `this page `__ for some general Kconfig advice. 3. Generate an initial configuration with e.g. ``menuconfig``/``guiconfig`` or ``alldefconfig``. The configuration is saved as ``.config`` by default. For more advanced projects, the ``defconfig`` utility can be used to generate the initial configuration from an existing configuration file. Usually, this existing configuration file would be a minimal configuration file, as generated by e.g. ``savedefconfig``. 4. Run ``genconfig`` to generate a header file. By default, it is saved as ``config.h``. Normally, ``genconfig`` would be run automatically as part of the build. Before writing a header file or other configuration output, Kconfiglib compares the old contents of the file against the new contents. If there's no change, the write is skipped. This avoids updating file metadata like the modification time, and might save work depending on your build setup. Adding new configuration output formats should be relatively straightforward. See the implementation of ``write_config()`` in `kconfiglib.py `_. The documentation for the ``Symbol.config_string`` property has some tips as well. 5. To update an old ``.config`` file after the Kconfig files have changed (e.g. to add new options), run ``oldconfig`` (prompts for values for new options) or ``olddefconfig`` (gives new options their default value). Entering the ``menuconfig`` or ``guiconfig`` interface and saving the configuration will also update it (the configuration interfaces always prompt for saving on exit if it would modify the contents of the ``.config`` file). Due to Kconfig semantics, simply loading an old ``.config`` file performs an implicit ``olddefconfig``, so building will normally not be affected by having an outdated configuration. Whenever ``.config`` is overwritten, the previous version of the file is saved to ``.config.old`` (or, more generally, to ``$KCONFIG_CONFIG.old``). Using ``.config`` files as Make input ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``.config`` files use Make syntax and can be included directly in Makefiles to read configuration values from there. This is why ``n``-valued ``bool``/``tristate`` values are written out as ``# CONFIG_FOO is not set`` (a Make comment) in ``.config``, allowing them to be tested with ``ifdef`` in Make. If you make use of this, you might want to pass ``--config-out `` to ``genconfig`` and include the configuration file it generates instead of including ``.config`` directly. This has the advantage that the generated configuration file will always be a "full" configuration file, even if ``.config`` is outdated. Otherwise, it might be necessary to run ``old(def)config`` or ``menuconfig``/``guiconfig`` before rebuilding with an outdated ``.config``. If you use ``--sync-deps`` to generate incremental build information, you can include ``deps/auto.conf`` instead, which is also a full configuration file. Useful helper macros ~~~~~~~~~~~~~~~~~~~~ The `include/linux/kconfig.h `_ header in the Linux kernel defines some useful helper macros for testing Kconfig configuration values. ``IS_ENABLED()`` is generally useful, allowing configuration values to be tested in ``if`` statements with no runtime overhead. Incremental building ~~~~~~~~~~~~~~~~~~~~ See the docstring for ``Kconfig.sync_deps()`` in `kconfiglib.py `_ for hints on implementing incremental builds (rebuilding just source files that reference changed configuration values). Running the ``scripts/basic/fixdep.c`` tool from the kernel on the output of ``gcc -MD `` might give you an idea of how it all fits together. Library documentation --------------------- Kconfiglib comes with extensive documentation in the form of docstrings. To view it, run e.g. the following command: .. code:: sh $ pydoc(3) kconfiglib For HTML output, add ``-w``: .. code:: sh $ pydoc(3) -w kconfiglib This will also work after installing Kconfiglib with ``pip(3)``. Documentation for other modules can be viewed in the same way (though a plain ``--help`` will work when they're run as executables): .. code:: sh $ pydoc(3) menuconfig/guiconfig/... A good starting point for learning the library is to read the module docstring (which you could also just read directly at the beginning of `kconfiglib.py `_). It gives an introduction to symbol values, the menu tree, and expressions. After reading the module docstring, a good next step is to read the ``Kconfig`` class documentation, and then the documentation for the ``Symbol``, ``Choice``, and ``MenuNode`` classes. Please tell me if something is unclear or can be explained better. Library features ---------------- Kconfiglib can do the following, among other things: - **Programmatically get and set symbol values** See `allnoconfig.py `_ and `allyesconfig.py `_, which are automatically verified to produce identical output to the standard ``make allnoconfig`` and ``make allyesconfig``. - **Read and write .config and defconfig files** The generated ``.config`` and ``defconfig`` (minimal configuration) files are character-for-character identical to what the C implementation would generate (except for the header comment). The test suite relies on this, as it compares the generated files. - **Write C headers** The generated headers use the same format as ``include/generated/autoconf.h`` from the Linux kernel. Output for symbols appears in the order that they're defined, unlike in the C tools (where the order depends on the hash table implementation). - **Implement incremental builds** This uses the same scheme as the ``include/config`` directory in the kernel: Symbols are translated into files that are touched when the symbol's value changes between builds, which can be used to avoid having to do a full rebuild whenever the configuration is changed. See the ``sync_deps()`` function for more information. - **Inspect symbols** Printing a symbol or other item (which calls ``__str__()``) returns its definition in Kconfig format. This also works for symbols defined in multiple locations. A helpful ``__repr__()`` is on all objects too. All ``__str__()`` and ``__repr__()`` methods are deliberately implemented with just public APIs, so all symbol information can be fetched separately as well. - **Inspect expressions** Expressions use a simple tuple-based format that can be processed manually if needed. Expression printing and evaluation functions are provided, implemented with public APIs. - **Inspect the menu tree** The underlying menu tree is exposed, including submenus created implicitly from symbols depending on preceding symbols. This can be used e.g. to implement menuconfig-like functionality. See `menuconfig.py `_/`guiconfig.py `_ and the minimalistic `menuconfig_example.py `_ example. Kconfig extensions ~~~~~~~~~~~~~~~~~~ The following Kconfig extensions are available: - ``source`` supports glob patterns and includes each matching file. A pattern is required to match at least one file. A separate ``osource`` statement is available for cases where it's okay for the pattern to match no files (in which case ``osource`` turns into a no-op). - A relative ``source`` statement (``rsource``) is available, where file paths are specified relative to the directory of the current Kconfig file. An ``orsource`` statement is available as well, analogous to ``osource``. - Preprocessor user functions can be defined in Python, which makes it simple to integrate information from existing Python tools into Kconfig (e.g. to have Kconfig symbols depend on hardware information stored in some other format). See the *Kconfig extensions* section in the `kconfiglib.py `_ module docstring for more information. - ``def_int``, ``def_hex``, and ``def_string`` are available in addition to ``def_bool`` and ``def_tristate``, allowing ``int``, ``hex``, and ``string`` symbols to be given a type and a default at the same time. These can be useful in projects that make use of symbols defined in multiple locations, and remove some Kconfig inconsistency. - Environment variables are expanded directly in e.g. ``source`` and ``mainmenu`` statements, meaning ``option env`` symbols are redundant. This is the standard behavior with the new `Kconfig preprocessor `__, which Kconfiglib implements. ``option env`` symbols are accepted but ignored, which leads the caveat that they must have the same name as the environment variables they reference (Kconfiglib warns if the names differ). This keeps Kconfiglib compatible with older Linux kernels, where the name of the ``option env`` symbol always matched the environment variable. Compatibility with older Linux kernels is the main reason ``option env`` is still supported. The C tools have dropped support for ``option env``. - Two extra optional warnings can be enabled by setting environment variables, covering cases that are easily missed when making changes to Kconfig files: * ``KCONFIG_WARN_UNDEF``: If set to ``y``, warnings will be generated for all references to undefined symbols within Kconfig files. The only gotcha is that all hex literals must be prefixed with ``0x`` or ``0X``, to make it possible to distinguish them from symbol references. Some projects (e.g. the Linux kernel) use multiple Kconfig trees with many shared Kconfig files, leading to some safe undefined symbol references. ``KCONFIG_WARN_UNDEF`` is useful in projects that only have a single Kconfig tree though. ``KCONFIG_STRICT`` is an older alias for this environment variable, supported for backwards compatibility. * ``KCONFIG_WARN_UNDEF_ASSIGN``: If set to ``y``, warnings will be generated for all assignments to undefined symbols within ``.config`` files. By default, no such warnings are generated. This warning can also be enabled/disabled by setting ``Kconfig.warn_assign_undef`` to ``True``/``False``. Other features -------------- - **Single-file implementation** The entire library is contained in `kconfiglib.py `_. The tools implemented on top of it are one file each. - **Robust and highly compatible with the C Kconfig tools**  The `test suite `_ automatically compares output from Kconfiglib and the C tools by diffing the generated ``.config`` files for the real kernel Kconfig and defconfig files, for all ARCHes. This currently involves comparing the output for 36 ARCHes and 498 defconfig files (or over 18000 ARCH/defconfig combinations in "obsessive" test suite mode). All tests are expected to pass. A comprehensive suite of selftests is included as well. - **Not horribly slow despite being a pure Python implementation** The `allyesconfig.py `_ script currently runs in about 1.3 seconds on the Linux kernel on a Core i7 2600K (with a warm file cache), including the ``make`` overhead from ``make scriptconfig``. Note that the Linux kernel Kconfigs are absolutely massive (over 14k symbols for x86) compared to most projects, and also have overhead from running shell commands via the Kconfig preprocessor. Kconfiglib is especially speedy in cases where multiple ``.config`` files need to be processed, because the ``Kconfig`` files will only need to be parsed once. For long-running jobs, `PyPy `_ gives a big performance boost. CPython is faster for short-running jobs as PyPy needs some time to warm up. Kconfiglib also works well with the `multiprocessing `_ module. No global state is kept. - **Generates more warnings than the C implementation** Generates the same warnings as the C implementation, plus additional ones. Also detects dependency and ``source`` loops. All warnings point out the location(s) in the ``Kconfig`` files where a symbol is defined, where applicable. - **Unicode support** Unicode characters in string literals in ``Kconfig`` and ``.config`` files are correctly handled. This support mostly comes for free from Python. - **Windows support** Nothing Linux-specific is used. Universal newlines mode is used for both Python 2 and Python 3. The `Zephyr `_ project uses Kconfiglib to generate ``.config`` files and C headers on Linux as well as Windows. - **Internals that (mostly) mirror the C implementation** While being simpler to understand and tweak. Menuconfig interfaces --------------------- Three configuration interfaces are currently available: - `menuconfig.py `_ is a terminal-based configuration interface implemented using the standard Python ``curses`` module. ``xconfig`` features like showing invisible symbols and showing symbol names are included, and it's possible to jump directly to a symbol in the menu tree (even if it's currently invisible). .. image:: https://raw.githubusercontent.com/ulfalizer/Kconfiglib/screenshots/screenshots/menuconfig.gif *There is now also a show-help mode that shows the help text of the currently selected symbol in the help window at the bottom.* Starting with Kconfiglib 12.2.0, ``menuconfig.py`` runs under both Python 2 and Python 3 (previously, it only ran under Python 3, so this was a backport). Running it under Python 3 provides better support for Unicode text entry (``get_wch()`` is not available in the ``curses`` module on Python 2). There are no third-party dependencies on \*nix. On Windows, the ``curses`` modules is not available by default, but support can be added by installing the ``windows-curses`` package: .. code-block:: shell $ pip install windows-curses This uses wheels built from `this repository `_, which is in turn based on Christoph Gohlke's `Python Extension Packages for Windows `_. See the docstring at the top of `menuconfig.py `_ for more information about the terminal menuconfig implementation. - `guiconfig.py `_ is a graphical configuration interface written in `Tkinter `_. Like ``menuconfig.py``, it supports showing all symbols (with invisible symbols in red) and jumping directly to symbols. Symbol values can also be changed directly from the jump-to dialog. When single-menu mode is enabled, a single menu is shown at a time, like in the terminal menuconfig. Only this mode distinguishes between symbols defined with ``config`` and symbols defined with ``menuconfig``. ``guiconfig.py`` has been tested on X11, Windows, and macOS, and is compatible with both Python 2 and Python 3. Despite being part of the Python standard library, ``tkinter`` often isn't included by default in Python installations on Linux. These commands will install it on a few different distributions: - Ubuntu: ``sudo apt install python-tk``/``sudo apt install python3-tk`` - Fedora: ``dnf install python2-tkinter``/``dnf install python3-tkinter`` - Arch: ``sudo pacman -S tk`` - Clear Linux: ``sudo swupd bundle-add python3-tcl`` Screenshot below, with show-all mode enabled and the jump-to dialog open: .. image:: https://raw.githubusercontent.com/ulfalizer/Kconfiglib/screenshots/screenshots/guiconfig.png To avoid having to carry around a bunch of GIFs, the image data is embedded in ``guiconfig.py``. To use separate GIF files instead, change ``_USE_EMBEDDED_IMAGES`` to ``False`` in ``guiconfig.py``. The image files can be found in the `screenshots `_ branch. I did my best with the images, but some are definitely only art adjacent. Touch-ups are welcome. :) - `pymenuconfig `_, built by `RomaVis `_, is an older portable Python 2/3 TkInter menuconfig implementation. Screenshot below: .. image:: https://raw.githubusercontent.com/RomaVis/pymenuconfig/master/screenshot.PNG While working on the terminal menuconfig implementation, I added a few APIs to Kconfiglib that turned out to be handy. ``pymenuconfig`` predates ``menuconfig.py`` and ``guiconfig.py``, and so didn't have them available. Blame me for any workarounds. Examples -------- Example scripts ~~~~~~~~~~~~~~~ The `examples/ `_ directory contains some simple example scripts. Among these are the following ones. Make sure you run them with the latest version of Kconfiglib, as they might make use of newly added features. - `eval_expr.py `_ evaluates an expression in the context of a configuration. - `find_symbol.py `_ searches through expressions to find references to a symbol, also printing a "backtrace" with parents for each reference found. - `help_grep.py `_ searches for a string in all help texts. - `print_tree.py `_ prints a tree of all configuration items. - `print_config_tree.py `_ is similar to ``print_tree.py``, but dumps the tree as it would appear in ``menuconfig``, including values. This can be handy for visually diffing between ``.config`` files and different versions of ``Kconfig`` files. - `list_undefined.py `_ finds references to symbols that are not defined by any architecture in the Linux kernel. - `merge_config.py `_ merges configuration fragments to produce a complete .config, similarly to ``scripts/kconfig/merge_config.sh`` from the kernel. - `menuconfig_example.py `_ implements a configuration interface that uses notation similar to ``make menuconfig``. It's deliberately kept as simple as possible to demonstrate just the core concepts. Real-world examples ~~~~~~~~~~~~~~~~~~~ - `kconfig.py `_ from the `Zephyr `_ project handles ``.config`` and header file generation, also doing configuration fragment merging - `genrest.py `_ generates a Kconfig symbol cross-reference, which can be viewed `here `__ - `CMake and IDE integration `_ from the ESP-IDF project, via a configuration server program. - `A script for turning on USB-related options `_, from the `syzkaller `_ project. - `Various automated checks `_, including a check for references to undefined Kconfig symbols in source code. See the ``KconfigCheck`` class. - `Various utilities `_ from the `ACRN `_ project These use the older Kconfiglib 1 API, which was clunkier and not as general (functions instead of properties, no direct access to the menu structure or properties, uglier ``__str__()`` output): - `genboardscfg.py `_ from `Das U-Boot `_ generates some sort of legacy board database by pulling information from a newly added Kconfig-based configuration system (as far as I understand it :). - `gen-manual-lists.py `_ generated listings for an appendix in the `Buildroot `_ manual. (The listing has since been removed.) - `gen_kconfig_doc.py `_ from the `esp-idf `_ project generates documentation from Kconfig files. - `SConf `_ builds an interactive configuration interface (like ``menuconfig``) on top of Kconfiglib, for use e.g. with `SCons `_. - `kconfig-diff.py `_ -- a script by `dubiousjim `_ that compares kernel configurations. - Originally, Kconfiglib was used in chapter 4 of my `master's thesis `_ to automatically generate a "minimal" kernel for a given system. Parts of it bother me a bit now, but that's how it goes with old work. Sample ``make iscriptconfig`` session ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The following log should give some idea of the functionality available in the API: .. code-block:: $ make iscriptconfig A Kconfig instance 'kconf' for the architecture x86 has been created. >>> kconf # Calls Kconfig.__repr__() >>> kconf.mainmenu_text # Expanded main menu text 'Linux/x86 4.14.0-rc7 Kernel Configuration' >>> kconf.top_node # The implicit top-level menu >>> kconf.top_node.list # First child menu node >>> print(kconf.top_node.list) # Calls MenuNode.__str__() config SRCARCH string option env="SRCARCH" default "x86" >>> sym = kconf.top_node.list.next.item # Item contained in next menu node >>> print(sym) # Calls Symbol.__str__() config 64BIT bool "64-bit kernel" if ARCH = "x86" default ARCH != "i386" help Say yes to build a 64-bit kernel - formerly known as x86_64 Say no to build a 32-bit kernel - formerly known as i386 >>> sym # Calls Symbol.__repr__() >>> sym.assignable # Currently assignable values (0, 1, 2 = n, m, y) (0, 2) >>> sym.set_value(0) # Set it to n True >>> sym.tri_value # Check the new value 0 >>> sym = kconf.syms["X86_MPPARSE"] # Look up symbol by name >>> print(sym) config X86_MPPARSE bool "Enable MPS table" if (ACPI || SFI) && X86_LOCAL_APIC default y if X86_LOCAL_APIC help For old smp systems that do not have proper acpi support. Newer systems (esp with 64bit cpus) with acpi support, MADT and DSDT will override it >>> default = sym.defaults[0] # Fetch its first default >>> sym = default[1] # Fetch the default's condition (just a Symbol here) >>> print(sym) config X86_LOCAL_APIC bool default y select IRQ_DOMAIN_HIERARCHY select PCI_MSI_IRQ_DOMAIN if PCI_MSI depends on X86_64 || SMP || X86_32_NON_STANDARD || X86_UP_APIC || PCI_MSI >>> sym.nodes # Show the MenuNode(s) associated with it [] >>> kconfiglib.expr_str(sym.defaults[0][1]) # Print the default's condition 'X86_64 || SMP || X86_32_NON_STANDARD || X86_UP_APIC || PCI_MSI' >>> kconfiglib.expr_value(sym.defaults[0][1]) # Evaluate it (0 = n) 0 >>> kconf.syms["64BIT"].set_value(2) True >>> kconfiglib.expr_value(sym.defaults[0][1]) # Evaluate it again (2 = y) 2 >>> kconf.write_config("myconfig") # Save a .config >>> ^D $ cat myconfig # Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib) CONFIG_64BIT=y CONFIG_X86_64=y CONFIG_X86=y CONFIG_INSTRUCTION_DECODER=y CONFIG_OUTPUT_FORMAT="elf64-x86-64" CONFIG_ARCH_DEFCONFIG="arch/x86/configs/x86_64_defconfig" CONFIG_LOCKDEP_SUPPORT=y CONFIG_STACKTRACE_SUPPORT=y CONFIG_MMU=y ... Test suite ---------- The test suite is run with .. code:: $ python(3) Kconfiglib/testsuite.py `pypy `_ works too, and is much speedier for everything except ``allnoconfig.py``/``allnoconfig_simpler.py``/``allyesconfig.py``, where it doesn't have time to warm up since the scripts are run via ``make scriptconfig``. The test suite must be run from the top-level kernel directory. It requires that the Kconfiglib git repository has been cloned into it and that the makefile patch has been applied. To get rid of warnings generated for the kernel ``Kconfig`` files, add ``2>/dev/null`` to the command to discard ``stderr``. **NOTE: Forgetting to apply the Makefile patch will cause some tests that compare generated configurations to fail** **NOTE: The test suite overwrites .config in the kernel root, so make sure to back it up.** The test suite consists of a set of selftests and a set of compatibility tests that compare configurations generated by Kconfiglib with configurations generated by the C tools, for a number of cases. See `testsuite.py `_ for the available options. The `tests/reltest `_ script runs the test suite and all the example scripts for both Python 2 and Python 3, verifying that everything works. Rarely, the output from the C tools is changed slightly (most recently due to a `change `_ I added). If you get test suite failures, try running the test suite again against the `linux-next tree `_, which has all the latest changes. I will make it clear if any non-backwards-compatible changes appear. A lot of time is spent waiting around for ``make`` and the C utilities (which need to reparse all the Kconfig files for each defconfig test). Adding some multiprocessing to the test suite would make sense too. Notes ----- * This is version 2 of Kconfiglib, which is not backwards-compatible with Kconfiglib 1. A summary of changes between Kconfiglib 1 and Kconfiglib 2 can be found `here `__. * I sometimes see people add custom output formats, which is pretty straightforward to do (see the implementations of ``write_autoconf()`` and ``write_config()`` for a template, and also the documentation of the ``Symbol.config_string`` property). If you come up with something you think might be useful to other people, I'm happy to take it in upstream. Batteries included and all that. * Kconfiglib assumes the modules symbol is ``MODULES``, which is backwards-compatible. A warning is printed by default if ``option modules`` is set on some other symbol. Let me know if you need proper ``option modules`` support. It wouldn't be that hard to add. Thanks ------ - To `RomaVis `_, for making `pymenuconfig `_ and suggesting the ``rsource`` keyword. - To `Mitja Horvat `_, for adding support for user-defined styles to the terminal menuconfig. - To `Philip Craig `_ for adding support for the ``allnoconfig_y`` option and fixing an obscure issue with ``comment``\s inside ``choice``\s (that didn't affect correctness but made outputs differ). ``allnoconfig_y`` is used to force certain symbols to ``y`` during ``make allnoconfig`` to improve coverage. License ------- See `LICENSE.txt `_. SPDX license identifiers are used in the source code. ================================================ FILE: kernel/scripts/Kconfiglib/alldefconfig.py ================================================ #!/usr/bin/env python # Copyright (c) 2018-2019, Ulf Magnusson # SPDX-License-Identifier: ISC """ Writes a configuration file where all symbols are set to their their default values. The default output filename is '.config'. A different filename can be passed in the KCONFIG_CONFIG environment variable. Usage for the Linux kernel: $ make [ARCH=] scriptconfig SCRIPT=Kconfiglib/alldefconfig.py """ import kconfiglib def main(): kconf = kconfiglib.standard_kconfig(__doc__) kconf.load_allconfig("alldef.config") print(kconf.write_config()) if __name__ == "__main__": main() ================================================ FILE: kernel/scripts/Kconfiglib/allmodconfig.py ================================================ #!/usr/bin/env python # Copyright (c) 2018-2019, Ulf Magnusson # SPDX-License-Identifier: ISC """ Writes a configuration file where as many symbols as possible are set to 'm'. The default output filename is '.config'. A different filename can be passed in the KCONFIG_CONFIG environment variable. Usage for the Linux kernel: $ make [ARCH=] scriptconfig SCRIPT=Kconfiglib/allmodconfig.py """ import kconfiglib def main(): kconf = kconfiglib.standard_kconfig(__doc__) # See allnoconfig.py kconf.warn = False for sym in kconf.unique_defined_syms: if sym.orig_type == kconfiglib.BOOL: # 'bool' choice symbols get their default value, as determined by # e.g. 'default's on the choice if not sym.choice: # All other bool symbols get set to 'y', like for allyesconfig sym.set_value(2) elif sym.orig_type == kconfiglib.TRISTATE: sym.set_value(1) for choice in kconf.unique_choices: choice.set_value(2 if choice.orig_type == kconfiglib.BOOL else 1) kconf.warn = True kconf.load_allconfig("allmod.config") print(kconf.write_config()) if __name__ == "__main__": main() ================================================ FILE: kernel/scripts/Kconfiglib/allnoconfig.py ================================================ #!/usr/bin/env python # Copyright (c) 2018-2019, Ulf Magnusson # SPDX-License-Identifier: ISC """ Writes a configuration file where as many symbols as possible are set to 'n'. The default output filename is '.config'. A different filename can be passed in the KCONFIG_CONFIG environment variable. Usage for the Linux kernel: $ make [ARCH=] scriptconfig SCRIPT=Kconfiglib/allnoconfig.py """ # See examples/allnoconfig_walk.py for another way to implement this script import kconfiglib def main(): kconf = kconfiglib.standard_kconfig(__doc__) # Avoid warnings that would otherwise get printed by Kconfiglib for the # following: # # 1. Assigning a value to a symbol without a prompt, which never has any # effect # # 2. Assigning values invalid for the type (only bool/tristate symbols # accept 0/1/2, for n/m/y). The assignments will be ignored for other # symbol types, which is what we want. kconf.warn = False for sym in kconf.unique_defined_syms: sym.set_value(2 if sym.is_allnoconfig_y else 0) kconf.warn = True kconf.load_allconfig("allno.config") print(kconf.write_config()) if __name__ == "__main__": main() ================================================ FILE: kernel/scripts/Kconfiglib/allyesconfig.py ================================================ #!/usr/bin/env python # Copyright (c) 2018-2019, Ulf Magnusson # SPDX-License-Identifier: ISC """ Writes a configuration file where as many symbols as possible are set to 'y'. The default output filename is '.config'. A different filename can be passed in the KCONFIG_CONFIG environment variable. Usage for the Linux kernel: $ make [ARCH=] scriptconfig SCRIPT=Kconfiglib/allyesconfig.py """ import kconfiglib def main(): kconf = kconfiglib.standard_kconfig(__doc__) # See allnoconfig.py kconf.warn = False # Try to set all symbols to 'y'. Dependencies might truncate the value down # later, but this will at least give the highest possible value. # # Assigning 0/1/2 to non-bool/tristate symbols has no effect (int/hex # symbols still take a string, because they preserve formatting). for sym in kconf.unique_defined_syms: # Set choice symbols to 'm'. This value will be ignored for choices in # 'y' mode (the "normal" mode), which will instead just get their # default selection, but will set all symbols in m-mode choices to 'm', # which is as high as they can go. # # Here's a convoluted example of how you might get an m-mode choice # even during allyesconfig: # # choice # tristate "weird choice" # depends on m sym.set_value(1 if sym.choice else 2) # Set all choices to the highest possible mode for choice in kconf.unique_choices: choice.set_value(2) kconf.warn = True kconf.load_allconfig("allyes.config") print(kconf.write_config()) if __name__ == "__main__": main() ================================================ FILE: kernel/scripts/Kconfiglib/defconfig.py ================================================ #!/usr/bin/env python # Copyright (c) 2019, Ulf Magnusson # SPDX-License-Identifier: ISC """ Reads a specified configuration file, then writes a new configuration file. This can be used to initialize the configuration from e.g. an arch-specific configuration file. This input configuration file would usually be a minimal configuration file, as generated by e.g. savedefconfig. The default output filename is '.config'. A different filename can be passed in the KCONFIG_CONFIG environment variable. """ import argparse import kconfiglib def main(): parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, description=__doc__) parser.add_argument( "--kconfig", default="Kconfig", help="Base Kconfig file (default: Kconfig)") parser.add_argument( "config", metavar="CONFIGURATION", help="Input configuration file") args = parser.parse_args() kconf = kconfiglib.Kconfig(args.kconfig, suppress_traceback=True) print(kconf.load_config(args.config)) print(kconf.write_config()) if __name__ == "__main__": main() ================================================ FILE: kernel/scripts/Kconfiglib/examples/Kmenuconfig ================================================ mainmenu "Example Kconfig configuration" config MODULES bool "Enable loadable module support" option modules default y menu "Bool and tristate symbols" config BOOL bool "Bool symbol" default y config BOOL_DEP bool "Dependent bool symbol" depends on BOOL # Mix it up a bit with an 'if' instead of a 'depends on' if BOOL config TRI_DEP tristate "Dependent tristate symbol" select SELECTED_BY_TRI_DEP imply IMPLIED_BY_TRI_DEP endif config TWO_MENU_NODES bool "First prompt" depends on BOOL config TRI tristate "Tristate symbol" config TWO_MENU_NODES bool "Second prompt" comment "These are selected by TRI_DEP" config SELECTED_BY_TRI_DEP tristate "Tristate selected by TRI_DEP" config IMPLIED_BY_TRI_DEP tristate "Tristate implied by TRI_DEP" endmenu menu "String, int, and hex symbols" config STRING string "String symbol" default "foo" config INT int "Int symbol" default 747 config HEX hex "Hex symbol" default 0xABC endmenu menu "Various choices" choice BOOL_CHOICE bool "Bool choice" config BOOL_CHOICE_SYM_1 bool "Bool choice sym 1" config BOOL_CHOICE_SYM_2 bool "Bool choice sym 2" endchoice choice TRI_CHOICE tristate "Tristate choice" config TRI_CHOICE_SYM_1 tristate "Tristate choice sym 1" config TRI_CHOICE_SYM_2 tristate "Tristate choice sym 2" endchoice choice OPT_BOOL_CHOICE bool "Optional bool choice" optional config OPT_BOOL_CHOICE_SYM_1 bool "Optional bool choice sym 1" config OPT_BOOL_CHOICE_SYM_2 bool "Optional bool choice sym 2" endchoice endmenu ================================================ FILE: kernel/scripts/Kconfiglib/examples/allnoconfig_walk.py ================================================ # This is tree-walking version of allnoconfig.py, for demonstration purposes. # Verified by the test suite to generate identical output to 'make allnoconfig' # for all ARCHes. # # Note: A more practical version would use Kconfig.node_iter(). The manual tree # walking is for demonstration purposes. # # Usage for the Linux kernel: # # $ make [ARCH=] scriptconfig SCRIPT=Kconfiglib/examples/allnoconfig_walk.py import sys from kconfiglib import Kconfig, Symbol def do_allnoconfig(node): global changed # Walk the tree of menu nodes. You can imagine this as going down/into menu # entries in the menuconfig interface, setting each to n (or the lowest # assignable value). while node: if isinstance(node.item, Symbol): sym = node.item # Is the symbol a non-allnoconfig_y symbol that can be set to a # lower value than its current value? if (not sym.is_allnoconfig_y and sym.assignable and sym.assignable[0] < sym.tri_value): # Yup, lower it sym.set_value(sym.assignable[0]) changed = True # Recursively lower children if node.list: do_allnoconfig(node.list) node = node.next # Parse the Kconfig files kconf = Kconfig(sys.argv[1]) # Do an initial pass to set 'option allnoconfig_y' symbols to y for sym in kconf.unique_defined_syms: if sym.is_allnoconfig_y: sym.set_value(2) while True: # Changing later symbols in the configuration can sometimes allow earlier # symbols to be lowered, e.g. if a later symbol 'select's an earlier # symbol. To handle such situations, we do additional passes over the tree # until we're no longer able to change the value of any symbol in a pass. changed = False do_allnoconfig(kconf.top_node) # Did the pass change any symbols? if not changed: break print(kconf.write_config()) ================================================ FILE: kernel/scripts/Kconfiglib/examples/defconfig_oldconfig.py ================================================ # Produces exactly the same output as the following script: # # make defconfig # echo CONFIG_ETHERNET=n >> .config # make oldconfig # echo CONFIG_ETHERNET=y >> .config # yes n | make oldconfig # # This came up in https://github.com/ulfalizer/Kconfiglib/issues/15. # # Usage: # # $ make [ARCH=] scriptconfig SCRIPT=Kconfiglib/examples/defconfig_oldconfig.py import sys import kconfiglib kconf = kconfiglib.Kconfig(sys.argv[1]) # Mirrors defconfig kconf.load_config("arch/x86/configs/x86_64_defconfig") kconf.write_config() # Mirrors the first oldconfig kconf.load_config() kconf.syms["ETHERNET"].set_value(0) kconf.write_config() # Mirrors the second oldconfig kconf.load_config() kconf.syms["ETHERNET"].set_value(2) for s in kconf.unique_defined_syms: if s.user_value is None and 0 in s.assignable: s.set_value(0) # Write the final configuration print(kconf.write_config()) ================================================ FILE: kernel/scripts/Kconfiglib/examples/dumpvars.py ================================================ # Prints all (set) environment variables referenced in the Kconfig files # together with their values, as a list of assignments. # # Note: This only works for environment variables referenced via the $(FOO) # preprocessor syntax. The older $FOO syntax is maintained for backwards # compatibility. import os import sys import kconfiglib print(" ".join("{}='{}'".format(var, os.environ[var]) for var in kconfiglib.Kconfig(sys.argv[1]).env_vars)) ================================================ FILE: kernel/scripts/Kconfiglib/examples/eval_expr.py ================================================ # Evaluates an expression (e.g. "X86_64 || (X86_32 && X86_LOCAL_APIC)") in the # context of a configuration. Note that this always yields a tristate value (n, # m, or y). # # Usage: # # $ make [ARCH=] scriptconfig SCRIPT=Kconfiglib/examples/eval_expr.py SCRIPT_ARG= import sys import kconfiglib if len(sys.argv) < 3: sys.exit("Pass the expression to evaluate with SCRIPT_ARG=") kconf = kconfiglib.Kconfig(sys.argv[1]) expr = sys.argv[2] # Enable modules so that m doesn't get demoted to n kconf.modules.set_value(2) print("the expression '{}' evaluates to {}" .format(expr, kconf.eval_string(expr))) ================================================ FILE: kernel/scripts/Kconfiglib/examples/find_symbol.py ================================================ # Prints all menu nodes that reference a given symbol any of their properties # or property conditions, along with their parent menu nodes. # # Usage: # # $ make [ARCH=] scriptconfig SCRIPT=Kconfiglib/examples/find_symbol.py SCRIPT_ARG= # # Example output for SCRIPT_ARG=X86: # # Found 470 locations that reference X86: # # ========== Location 1 (init/Kconfig:1108) ========== # # config SGETMASK_SYSCALL # bool # prompt "sgetmask/ssetmask syscalls support" if EXPERT # default PARISC || M68K || PPC || MIPS || X86 || SPARC || MICROBLAZE || SUPERH # help # sys_sgetmask and sys_ssetmask are obsolete system calls # no longer supported in libc but still enabled by default in some # architectures. # # If unsure, leave the default option here. # # ---------- Parent 1 (init/Kconfig:1077) ---------- # # menuconfig EXPERT # bool # prompt "Configure standard kernel features (expert users)" # select DEBUG_KERNEL # help # This option allows certain base kernel options and settings # to be disabled or tweaked. This is for specialized # environments which can tolerate a "non-standard" kernel. # Only use this if you really know what you are doing. # # ---------- Parent 2 (init/Kconfig:39) ---------- # # menu "General setup" # # ========== Location 2 (arch/Kconfig:29) ========== # # config OPROFILE_EVENT_MULTIPLEX # bool # prompt "OProfile multiplexing support (EXPERIMENTAL)" # default "n" # depends on OPROFILE && X86 # help # The number of hardware counters is limited. The multiplexing # feature enables OProfile to gather more events than counters # are provided by the hardware. This is realized by switching # between events at a user specified time interval. # # If unsure, say N. # # ---------- Parent 1 (arch/Kconfig:16) ---------- # # config OPROFILE # tristate # prompt "OProfile system profiling" # select RING_BUFFER # select RING_BUFFER_ALLOW_SWAP # depends on PROFILING && HAVE_OPROFILE # help # OProfile is a profiling system capable of profiling the # whole system, include the kernel, kernel modules, libraries, # and applications. # # If unsure, say N. # # ---------- Parent 2 (init/Kconfig:39) ---------- # # menu "General setup" # # ... (tons more) import sys import kconfiglib if len(sys.argv) < 3: sys.exit('Pass symbol name (without "CONFIG_" prefix) with SCRIPT_ARG=') kconf = kconfiglib.Kconfig(sys.argv[1]) sym_name = sys.argv[2] if sym_name not in kconf.syms: print("No symbol {} exists in the configuration".format(sym_name)) sys.exit(0) referencing = [node for node in kconf.node_iter() if kconf.syms[sym_name] in node.referenced] if not referencing: print("No references to {} found".format(sym_name)) sys.exit(0) print("Found {} locations that reference {}:\n" .format(len(referencing), sym_name)) for i, node in enumerate(referencing, 1): print("========== Location {} ({}:{}) ==========\n\n{}" .format(i, node.filename, node.linenr, node)) # Print the parents of the menu node too node = node.parent parent_i = 1 while node is not kconf.top_node: print("---------- Parent {} ({}:{}) ----------\n\n{}" .format(parent_i, node.filename, node.linenr, node)) node = node.parent parent_i += 1 ================================================ FILE: kernel/scripts/Kconfiglib/examples/help_grep.py ================================================ # Does a case-insensitive search for a regular expression in the help texts of # symbols and choices and the prompts of menus and comments. Prints the # matching items together with their locations and the matching text. # # Usage: # # $ make [ARCH=] scriptconfig SCRIPT=Kconfiglib/examples/help_grep.py SCRIPT_ARG= # # Shortened example output for SCRIPT_ARG=general: # # menu "General setup" # location: init/Kconfig:39 # # config SYSVIPC # bool # prompt "System V IPC" # help # ... # exchange information. It is generally considered to be a good thing, # ... # # location: init/Kconfig:233 # # config BSD_PROCESS_ACCT # bool # prompt "BSD Process Accounting" if MULTIUSER # help # ... # information. This is generally a good idea, so say Y. # # location: init/Kconfig:403 # # ... import re import sys from kconfiglib import Kconfig, Symbol, Choice, MENU, COMMENT if len(sys.argv) < 3: sys.exit("Pass the regex with SCRIPT_ARG=") search = re.compile(sys.argv[2], re.IGNORECASE).search for node in Kconfig(sys.argv[1]).node_iter(): match = False if isinstance(node.item, (Symbol, Choice)) and \ node.help is not None and search(node.help): print(node.item) match = True elif node.item == MENU and search(node.prompt[0]): print('menu "{}"'.format(node.prompt[0])) match = True elif node.item == COMMENT and search(node.prompt[0]): print('comment "{}"'.format(node.prompt[0])) match = True if match: print("location: {}:{}\n".format(node.filename, node.linenr)) ================================================ FILE: kernel/scripts/Kconfiglib/examples/list_undefined.py ================================================ # Prints a list of symbols that are referenced in the Kconfig files of some # architecture but not defined by the Kconfig files of any architecture. # # A Kconfig file might be shared between many architectures and legitimately # reference undefined symbols for some of them, but if no architecture defines # the symbol, it usually indicates a problem or potential cleanup. # # This script could be sped up a lot if needed. See the comment near the # referencing_nodes() call. # # Run with the following command in the kernel root: # # $ python(3) Kconfiglib/examples/list_undefined.py # # Example output: # # Registering defined and undefined symbols for all arches # Processing mips # Processing ia64 # Processing metag # ... # # Finding references to each undefined symbol # Processing mips # Processing ia64 # Processing metag # ... # # The following globally undefined symbols were found, listed here # together with the locations of the items that reference them. # References might come from enclosing menus and ifs. # # ARM_ERRATA_753970: arch/arm/mach-mvebu/Kconfig:56, arch/arm/mach-mvebu/Kconfig:39 # SUNXI_CCU_MP: drivers/clk/sunxi-ng/Kconfig:14 # SUNXI_CCU_DIV: drivers/clk/sunxi-ng/Kconfig:14 # AC97: sound/ac97/Kconfig:6 # ... import os import subprocess from kconfiglib import Kconfig # Referenced inside the Kconfig files os.environ["KERNELVERSION"] = str( subprocess.check_output(("make", "kernelversion")).decode("utf-8").rstrip() ) def all_arch_srcarch_pairs(): """ Generates all valid (ARCH, SRCARCH) tuples for the kernel, corresponding to different architectures. SRCARCH holds the arch/ subdirectory. """ for srcarch in os.listdir("arch"): # Each subdirectory of arch/ containing a Kconfig file corresponds to # an architecture if os.path.exists(os.path.join("arch", srcarch, "Kconfig")): yield (srcarch, srcarch) # Some architectures define additional ARCH settings with ARCH != SRCARCH # (search for "Additional ARCH settings for" in the top-level Makefile) yield ("i386", "x86") yield ("x86_64", "x86") yield ("sparc32", "sparc") yield ("sparc64", "sparc") yield ("sh64", "sh") yield ("um", "um") def all_arch_srcarch_kconfigs(): """ Generates Kconfig instances for all the architectures in the kernel """ os.environ["srctree"] = "." os.environ["HOSTCC"] = "gcc" os.environ["HOSTCXX"] = "g++" os.environ["CC"] = "gcc" os.environ["LD"] = "ld" for arch, srcarch in all_arch_srcarch_pairs(): print(" Processing " + arch) os.environ["ARCH"] = arch os.environ["SRCARCH"] = srcarch # um (User Mode Linux) uses a different base Kconfig file yield Kconfig("Kconfig" if arch != "um" else "arch/x86/um/Kconfig", warn=False) print("Registering defined and undefined symbols for all arches") # Sets holding the names of all defined and undefined symbols, for all # architectures defined = set() undefined = set() for kconf in all_arch_srcarch_kconfigs(): for name, sym in kconf.syms.items(): if sym.nodes: # If the symbol has a menu node, it is defined defined.add(name) else: # Undefined symbol. We skip some of the uninteresting ones. # Due to how Kconfig works, integer literals show up as symbols # (from e.g. 'default 1'). Skip those. try: int(name, 0) continue except ValueError: # Interesting undefined symbol undefined.add(name) print("\nFinding references to each undefined symbol") def referencing_nodes(kconf, name): # Returns a list of all menu nodes that reference a symbol named 'name' in # any of their properties or property conditions res = [] for node in kconf.node_iter(): for ref in node.referenced: if ref.name == name: res.append(node) return res # Maps each globally undefined symbol to the menu nodes that reference it undef_sym_refs = [(name, set()) for name in undefined - defined] for kconf in all_arch_srcarch_kconfigs(): for name, refs in undef_sym_refs: # This means that we search the entire configuration tree for each # undefined symbol, which is terribly inefficient. We could speed # things up by tweaking referencing_nodes() to compare each symbol to # multiple symbols while walking the configuration tree. for node in referencing_nodes(kconf, name): refs.add("{}:{}".format(node.filename, node.linenr)) print("\nThe following globally undefined symbols were found, listed here\n" "together with the locations of the items that reference them.\n" "References might come from enclosing menus and ifs.\n") for name, refs in undef_sym_refs: print(" {}: {}".format(name, ", ".join(refs))) ================================================ FILE: kernel/scripts/Kconfiglib/examples/menuconfig_example.py ================================================ #!/usr/bin/env python # Implements a simple configuration interface on top of Kconfiglib to # demonstrate concepts for building a menuconfig-like. Emulates how the # standard menuconfig prints menu entries. # # Always displays the entire Kconfig tree to keep things as simple as possible # (all symbols, choices, menus, and comments). # # Usage: # # $ python(3) Kconfiglib/examples/menuconfig.py # # A sample Kconfig is available in Kconfiglib/examples/Kmenuconfig. # # Here's a notation guide. The notation matches the one used by menuconfig # (scripts/kconfig/mconf): # # [ ] prompt - Bool # < > prompt - Tristate # {M} prompt - Tristate selected to m. Can only be set to m or y. # -*- prompt - Bool/tristate selected to y, pinning it # -M- prompt - Tristate selected to m that also has m visibility, # pinning it to m # (foo) prompt - String/int/hex symbol with value "foo" # --> prompt - The selected symbol in a choice in y mode. This # syntax is unique to this example. # # When modules are disabled, the .type attribute of TRISTATE symbols and # choices automatically changes to BOOL. This trick is used by the C # implementation as well, and gives the expected behavior without having to do # anything extra here. The original type is available in .orig_type if needed. # # The Kconfiglib/examples/Kmenuconfig example uses named choices to be able to # refer to choices by name. Named choices are supported in the C tools too, but # I don't think I've ever seen them used in the wild. # # Sample session: # # $ python Kconfiglib/examples/menuconfig.py Kconfiglib/examples/Kmenuconfig # # ======== Example Kconfig configuration ======== # # [*] Enable loadable module support (MODULES) # Bool and tristate symbols # [*] Bool symbol (BOOL) # [ ] Dependent bool symbol (BOOL_DEP) # < > Dependent tristate symbol (TRI_DEP) # [ ] First prompt (TWO_MENU_NODES) # < > Tristate symbol (TRI) # [ ] Second prompt (TWO_MENU_NODES) # *** These are selected by TRI_DEP *** # < > Tristate selected by TRI_DEP (SELECTED_BY_TRI_DEP) # < > Tristate implied by TRI_DEP (IMPLIED_BY_TRI_DEP) # String, int, and hex symbols # (foo) String symbol (STRING) # (747) Int symbol (INT) # (0xABC) Hex symbol (HEX) # Various choices # -*- Bool choice (BOOL_CHOICE) # --> Bool choice sym 1 (BOOL_CHOICE_SYM_1) # Bool choice sym 2 (BOOL_CHOICE_SYM_2) # {M} Tristate choice (TRI_CHOICE) # < > Tristate choice sym 1 (TRI_CHOICE_SYM_1) # < > Tristate choice sym 2 (TRI_CHOICE_SYM_2) # [ ] Optional bool choice (OPT_BOOL_CHOICE) # # Enter a symbol/choice name, "load_config", or "write_config" (or press CTRL+D to exit): BOOL # Value for BOOL (available: n, y): n # # ======== Example Kconfig configuration ======== # # [*] Enable loadable module support (MODULES) # Bool and tristate symbols # [ ] Bool symbol (BOOL) # < > Tristate symbol (TRI) # [ ] Second prompt (TWO_MENU_NODES) # *** These are selected by TRI_DEP *** # < > Tristate selected by TRI_DEP (SELECTED_BY_TRI_DEP) # < > Tristate implied by TRI_DEP (IMPLIED_BY_TRI_DEP) # String, int, and hex symbols # (foo) String symbol (STRING) # (747) Int symbol (INT) # (0xABC) Hex symbol (HEX) # Various choices # -*- Bool choice (BOOL_CHOICE) # --> Bool choice sym 1 (BOOL_CHOICE_SYM_1) # Bool choice sym 2 (BOOL_CHOICE_SYM_2) # {M} Tristate choice (TRI_CHOICE) # < > Tristate choice sym 1 (TRI_CHOICE_SYM_1) # < > Tristate choice sym 2 (TRI_CHOICE_SYM_2) # [ ] Optional bool choice (OPT_BOOL_CHOICE) # # Enter a symbol/choice name, "load_config", or "write_config" (or press CTRL+D to exit): MODULES # Value for MODULES (available: n, y): n # # ======== Example Kconfig configuration ======== # # [ ] Enable loadable module support (MODULES) # Bool and tristate symbols # [ ] Bool symbol (BOOL) # [ ] Tristate symbol (TRI) # [ ] Second prompt (TWO_MENU_NODES) # *** These are selected by TRI_DEP *** # [ ] Tristate selected by TRI_DEP (SELECTED_BY_TRI_DEP) # [ ] Tristate implied by TRI_DEP (IMPLIED_BY_TRI_DEP) # String, int, and hex symbols # (foo) String symbol (STRING) # (747) Int symbol (INT) # (0xABC) Hex symbol (HEX) # Various choices # -*- Bool choice (BOOL_CHOICE) # --> Bool choice sym 1 (BOOL_CHOICE_SYM_1) # Bool choice sym 2 (BOOL_CHOICE_SYM_2) # -*- Tristate choice (TRI_CHOICE) # --> Tristate choice sym 1 (TRI_CHOICE_SYM_1) # Tristate choice sym 2 (TRI_CHOICE_SYM_2) # [ ] Optional bool choice (OPT_BOOL_CHOICE) # # Enter a symbol/choice name, "load_config", or "write_config" (or press CTRL+D to exit): ^D from __future__ import print_function import readline import sys from kconfiglib import Kconfig, \ Symbol, MENU, COMMENT, \ BOOL, TRISTATE, STRING, INT, HEX, UNKNOWN, \ expr_value, \ TRI_TO_STR # Python 2/3 compatibility hack if sys.version_info[0] < 3: input = raw_input def indent_print(s, indent): print(indent*" " + s) def value_str(sc): """ Returns the value part ("[*]", "", "(foo)" etc.) of a menu entry. sc: Symbol or Choice. """ if sc.type in (STRING, INT, HEX): return "({})".format(sc.str_value) # BOOL or TRISTATE # The choice mode is an upper bound on the visibility of choice symbols, so # we can check the choice symbols' own visibility to see if the choice is # in y mode if isinstance(sc, Symbol) and sc.choice and sc.visibility == 2: # For choices in y mode, print '-->' next to the selected symbol return "-->" if sc.choice.selection is sc else " " tri_val_str = (" ", "M", "*")[sc.tri_value] if len(sc.assignable) == 1: # Pinned to a single value return "-{}-".format(tri_val_str) if sc.type == BOOL: return "[{}]".format(tri_val_str) if sc.type == TRISTATE: if sc.assignable == (1, 2): # m and y available return "{" + tri_val_str + "}" # Gets a bit confusing with .format() return "<{}>".format(tri_val_str) def node_str(node): """ Returns the complete menu entry text for a menu node, or "" for invisible menu nodes. Invisible menu nodes are those that lack a prompt or that do not have a satisfied prompt condition. Example return value: "[*] Bool symbol (BOOL)" The symbol name is printed in parentheses to the right of the prompt. This is so that symbols can easily be referred to in the configuration interface. """ if not node.prompt: return "" # Even for menu nodes for symbols and choices, it's wrong to check # Symbol.visibility / Choice.visibility here. The reason is that a symbol # (and a choice, in theory) can be defined in multiple locations, giving it # multiple menu nodes, which do not necessarily all have the same prompt # visibility. Symbol.visibility / Choice.visibility is calculated as the OR # of the visibility of all the prompts. prompt, prompt_cond = node.prompt if not expr_value(prompt_cond): return "" if node.item == MENU: return " " + prompt if node.item == COMMENT: return " *** {} ***".format(prompt) # Symbol or Choice sc = node.item if sc.type == UNKNOWN: # Skip symbols defined without a type (these are obscure and generate # a warning) return "" # {:3} sets the field width to three. Gives nice alignment for empty string # values. res = "{:3} {}".format(value_str(sc), prompt) # Don't print the name for unnamed choices (the normal kind) if sc.name is not None: res += " ({})".format(sc.name) return res def print_menuconfig_nodes(node, indent): """ Prints a tree with all the menu entries rooted at 'node'. Child menu entries are indented. """ while node: string = node_str(node) if string: indent_print(string, indent) if node.list: print_menuconfig_nodes(node.list, indent + 8) node = node.next def print_menuconfig(kconf): """ Prints all menu entries for the configuration. """ # Print the expanded mainmenu text at the top. This is the same as # kconf.top_node.prompt[0], but with variable references expanded. print("\n======== {} ========\n".format(kconf.mainmenu_text)) print_menuconfig_nodes(kconf.top_node.list, 0) print("") def get_value_from_user(sc): """ Prompts the user for a value for the symbol or choice 'sc'. For bool/tristate symbols and choices, provides a list of all the assignable values. """ if not sc.visibility: print(sc.name + " is not currently visible") return False prompt = "Value for {}".format(sc.name) if sc.type in (BOOL, TRISTATE): prompt += " (available: {})" \ .format(", ".join(TRI_TO_STR[val] for val in sc.assignable)) prompt += ": " val = input(prompt) # Automatically add a "0x" prefix for hex symbols, like the menuconfig # interface does. This isn't done when loading .config files, hence why # set_value() doesn't do it automatically. if sc.type == HEX and not val.startswith(("0x", "0X")): val = "0x" + val # Let Kconfiglib itself print a warning here if the value is invalid. We # could also disable warnings temporarily with 'kconf.warn = False' and # print our own warning. return sc.set_value(val) if __name__ == "__main__": if len(sys.argv) != 2: sys.exit("usage: menuconfig.py ") # Load Kconfig configuration files kconf = Kconfig(sys.argv[1]) # Print the initial configuration tree print_menuconfig(kconf) while True: try: cmd = input('Enter a symbol/choice name, "load_config", or ' '"write_config" (or press CTRL+D to exit): ').strip() except EOFError: print("") break if cmd == "load_config": config_filename = input(".config file to load: ") try: # Returns a message telling which file got loaded print(kconf.load_config(config_filename)) except EnvironmentError as e: print(e, file=sys.stderr) print_menuconfig(kconf) continue if cmd == "write_config": config_filename = input("To this file: ") try: # Returns a message telling which file got saved print(kconf.write_config(config_filename)) except EnvironmentError as e: print(e, file=sys.stderr) continue # Assume 'cmd' is the name of a symbol or choice if it isn't one of the # commands above, prompt the user for a value for it, and print the new # configuration tree if cmd in kconf.syms: if get_value_from_user(kconf.syms[cmd]): print_menuconfig(kconf) continue if cmd in kconf.named_choices: if get_value_from_user(kconf.named_choices[cmd]): print_menuconfig(kconf) continue print("No symbol/choice named '{}' in the configuration".format(cmd), file=sys.stderr) ================================================ FILE: kernel/scripts/Kconfiglib/examples/merge_config.py ================================================ #!/usr/bin/env python # This script functions similarly to scripts/kconfig/merge_config.sh from the # kernel tree, merging multiple configurations fragments to produce a complete # .config, with unspecified values filled in as for alldefconfig. # # The generated .config respects symbol dependencies, and a warning is printed # if any symbol gets a different value from the assigned value. # # For a real-world merging example based on this script, see # https://github.com/zephyrproject-rtos/zephyr/blob/master/scripts/kconfig/kconfig.py. # # Here's a demo: # # Kconfig contents: # # config FOO # bool "FOO" # # config BAR # bool "BAR" # # config BAZ # string "BAZ" # # config QAZ # bool "QAZ" if n # # # conf1 contents: # # CONFIG_FOO=y # # # conf2 contents: # # CONFIG_BAR=y # # # conf3 contents: # # # Assigned twice (would generate warning if 'warn_assign_override' was # # True) # # CONFIG_FOO is not set # # # Ops... this symbol doesn't exist # CONFIG_OPS=y # # CONFIG_BAZ="baz string" # # # conf4 contents: # # CONFIG_QAZ=y # # # Running: # # $ python(3) merge_config.py Kconfig merged conf1 conf2 conf3 conf4 # Merged configuration 'conf1' # Merged configuration 'conf2' # conf3:5: warning: attempt to assign the value 'y' to the undefined symbol OPS # Merged configuration 'conf3' # Merged configuration 'conf4' # Configuration saved to 'merged' # warning: QAZ (defined at Kconfig:10) was assigned the value 'y' but got the value 'n' -- check dependencies # $ cat merged # Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib) # # CONFIG_FOO is not set # CONFIG_BAR=y # CONFIG_BAZ="baz string" from __future__ import print_function import sys from kconfiglib import Kconfig, BOOL, TRISTATE, TRI_TO_STR if len(sys.argv) < 4: sys.exit("usage: merge_config.py Kconfig merged_config config1 [config2 ...]") kconf = Kconfig(sys.argv[1], suppress_traceback=True) # Enable warnings for assignments to undefined symbols kconf.warn_assign_undef = True # (This script uses alldefconfig as the base. Other starting states could be # set up here as well. The approach in examples/allnoconfig_simpler.py could # provide an allnoconfig starting state for example.) # Disable warnings generated for multiple assignments to the same symbol within # a (set of) configuration files. Assigning a symbol multiple times might be # done intentionally when merging configuration files. kconf.warn_assign_override = False kconf.warn_assign_redun = False # Create a merged configuration by loading the fragments with replace=False. # load_config() and write_config() returns a message to print. for config in sys.argv[3:]: print(kconf.load_config(config, replace=False)) # Write the merged configuration print(kconf.write_config(sys.argv[2])) # Print warnings for symbols whose actual value doesn't match the assigned # value for sym in kconf.defined_syms: # Was the symbol assigned to? if sym.user_value is not None: # Tristate values are represented as 0, 1, 2. Having them as # "n", "m", "y" is more convenient here, so convert. if sym.type in (BOOL, TRISTATE): user_value = TRI_TO_STR[sym.user_value] else: user_value = sym.user_value if user_value != sym.str_value: print("warning: {} was assigned the value '{}' but got the " "value '{}' -- check dependencies".format( sym.name_and_loc, user_value, sym.str_value), file=sys.stderr) ================================================ FILE: kernel/scripts/Kconfiglib/examples/print_config_tree.py ================================================ # Prints menu entries as a tree with its value in the .config file. This can be # handy e.g. for diffing between different .config files or versions of Kconfig files. # # Usage: # # $ make [ARCH=] scriptconfig SCRIPT=print_config_tree.py [SCRIPT_ARG=<.config>] # # If the variable WITH_HELP_DESC is modified to 'True', the help is added # to the symbols. # # Here's a notation guide. The notation matches the one used by menuconfig # (scripts/kconfig/mconf): # # [ ] prompt - Bool # < > prompt - Tristate # {M} prompt - Tristate selected to m. Can only be set to m or y. # -*- prompt - Bool/tristate selected to y, pinning it # -M- prompt - Tristate selected to m that also has m visibility, # pinning it to m # (foo) prompt - String/int/hex symbol with value "foo" # --> prompt - The selected symbol in a choice in y mode. This # syntax is unique to this example. # # When modules are disabled, the .type attribute of TRISTATE symbols and # choices automatically changes to BOOL. This trick is used by the C # implementation as well, and gives the expected behavior without having to do # anything extra here. The original type is available in .orig_type if needed. # # Example output: # # $ make scriptconfig SCRIPT=Kconfiglib/examples/print_config_tree.py [SCRIPT_ARG=<.config file>] # # ======== Linux/x86 4.9.82 Kernel Configuration ======== # # [*] 64-bit kernel (64BIT) # General setup # () Cross-compiler tool prefix (CROSS_COMPILE) # [ ] Compile also drivers which will not load (COMPILE_TEST) # () Local version - append to kernel release (LOCALVERSION) # [*] Automatically append version information to the version string (LOCALVERSION_AUTO) # -*- Kernel compression mode # ... # # With the variable WITH_HELP_DESC modified to 'True': # # ======== Linux/x86 4.9.82 Kernel Configuration ======== # # [*] 64-bit kernel - Say yes to build a 64-bit kernel - formerly known as x86_64 Say no to build a 32-bit kernel - formerly known as i386 (64BIT) # General setup # () Cross-compiler tool prefix - Same as running 'make CROSS_COMPILE=prefix-' but stored for default make runs in this kernel build directory. You don't need to set this unless you want the configured kernel build directory to select the cross-compiler automatically. (CROSS_COMPILE) # [ ] Compile also drivers which will not load - Some drivers can be compiled on a different platform than they are intended to be run on. Despite they cannot be loaded there (or even when they load they cannot be used due to missing HW support), developers still, opposing to distributors, might want to build such drivers to compile-test them. If you are a developer and want to build everything available, say Y here. If you are a user/distributor, say N here to exclude useless drivers to be distributed. (COMPILE_TEST) # ... import sys from kconfiglib import Kconfig, \ Symbol, MENU, COMMENT, \ BOOL, TRISTATE, STRING, INT, HEX, UNKNOWN, \ expr_value # Add help description to output WITH_HELP_DESC = False def indent_print(s, indent): print(indent*" " + s) def value_str(sc): """ Returns the value part ("[*]", "", "(foo)" etc.) of a menu entry. sc: Symbol or Choice. """ if sc.type in (STRING, INT, HEX): return "({})".format(sc.str_value) # BOOL or TRISTATE # The choice mode is an upper bound on the visibility of choice symbols, so # we can check the choice symbols' own visibility to see if the choice is # in y mode if isinstance(sc, Symbol) and sc.choice and sc.visibility == 2: # For choices in y mode, print '-->' next to the selected symbol return "-->" if sc.choice.selection is sc else " " tri_val_str = (" ", "M", "*")[sc.tri_value] if len(sc.assignable) == 1: # Pinned to a single value return "-{}-".format(tri_val_str) if sc.type == BOOL: return "[{}]".format(tri_val_str) if sc.type == TRISTATE: if sc.assignable == (1, 2): # m and y available return "{" + tri_val_str + "}" # Gets a bit confusing with .format() return "<{}>".format(tri_val_str) def node_str(node): """ Returns the complete menu entry text for a menu node, or "" for invisible menu nodes. Invisible menu nodes are those that lack a prompt or that do not have a satisfied prompt condition. Example return value: "[*] Bool symbol (BOOL)" The symbol name is printed in parentheses to the right of the prompt. """ if not node.prompt: return "" # Even for menu nodes for symbols and choices, it's wrong to check # Symbol.visibility / Choice.visibility here. The reason is that a symbol # (and a choice, in theory) can be defined in multiple locations, giving it # multiple menu nodes, which do not necessarily all have the same prompt # visibility. Symbol.visibility / Choice.visibility is calculated as the OR # of the visibility of all the prompts. prompt, prompt_cond = node.prompt if not expr_value(prompt_cond): return "" if node.item == MENU: return " " + prompt if node.item == COMMENT: return " *** {} ***".format(prompt) # Symbol or Choice sc = node.item if sc.type == UNKNOWN: # Skip symbols defined without a type (these are obscure and generate # a warning) return "" # Add help text if WITH_HELP_DESC: prompt += ' - ' + str(node.help).replace('\n', ' ').replace('\r', '') # {:3} sets the field width to three. Gives nice alignment for empty string # values. res = "{:3} {}".format(value_str(sc), prompt) # Don't print the name for unnamed choices (the normal kind) if sc.name is not None: res += " ({})".format(sc.name) return res def print_menuconfig_nodes(node, indent): """ Prints a tree with all the menu entries rooted at 'node'. Child menu entries are indented. """ while node: string = node_str(node) if string: indent_print(string, indent) if node.list: print_menuconfig_nodes(node.list, indent + 8) node = node.next def print_menuconfig(kconf): """ Prints all menu entries for the configuration. """ # Print the expanded mainmenu text at the top. This is the same as # kconf.top_node.prompt[0], but with variable references expanded. print("\n======== {} ========\n".format(kconf.mainmenu_text)) print_menuconfig_nodes(kconf.top_node.list, 0) print("") if __name__ == "__main__": # Load Kconfig configuration files kconf = Kconfig(sys.argv[1]) # Set default .config file or load it from argv if len(sys.argv) == 2: config_filename = '.config' else: config_filename = sys.argv[2] kconf.load_config(config_filename) # Print the configuration tree print_menuconfig(kconf) ================================================ FILE: kernel/scripts/Kconfiglib/examples/print_sym_info.py ================================================ # Loads a Kconfig and a .config and prints a symbol. # # Usage: # # $ make [ARCH=] scriptconfig SCRIPT=Kconfiglib/examples/print_sym_info.py SCRIPT_ARG= # # Example output for SCRIPT_ARG=MODULES: # # menuconfig MODULES # bool # prompt "Enable loadable module support" # option modules # help # Kernel modules are small pieces of compiled code which can # be inserted in the running kernel, rather than being # permanently built into the kernel. You use the "modprobe" # tool to add (and sometimes remove) them. If you say Y here, # many parts of the kernel can be built as modules (by # answering M instead of Y where indicated): this is most # useful for infrequently used options which are not required # for booting. For more information, see the man pages for # modprobe, lsmod, modinfo, insmod and rmmod. # # If you say Y here, you will need to run "make # modules_install" to put the modules under /lib/modules/ # where modprobe can find them (you may need to be root to do # this). # # If unsure, say Y. # # value = n # visibility = y # currently assignable values: n, y # defined at init/Kconfig:1674 import sys from kconfiglib import Kconfig, TRI_TO_STR if len(sys.argv) < 3: sys.exit('Pass symbol name (without "CONFIG_" prefix) with SCRIPT_ARG=') kconf = Kconfig(sys.argv[1]) sym = kconf.syms[sys.argv[2]] print(sym) print("value = " + sym.str_value) print("visibility = " + TRI_TO_STR[sym.visibility]) print("currently assignable values: " + ", ".join([TRI_TO_STR[v] for v in sym.assignable])) for node in sym.nodes: print("defined at {}:{}".format(node.filename, node.linenr)) ================================================ FILE: kernel/scripts/Kconfiglib/examples/print_tree.py ================================================ # Prints the menu tree of the configuration. Dependencies between symbols can # sometimes implicitly alter the menu structure (see kconfig-language.txt), and # that's implemented too. # # Note: See the Kconfig.node_iter() function as well, which provides a simpler # interface for walking the menu tree. # # Usage: # # $ make [ARCH=] scriptconfig SCRIPT=Kconfiglib/examples/print_tree.py # # Example output: # # ... # config HAVE_KERNEL_LZO # config HAVE_KERNEL_LZ4 # choice # config KERNEL_GZIP # config KERNEL_BZIP2 # config KERNEL_LZMA # config KERNEL_XZ # config KERNEL_LZO # config KERNEL_LZ4 # config DEFAULT_HOSTNAME # config SWAP # config SYSVIPC # config SYSVIPC_SYSCTL # config POSIX_MQUEUE # config POSIX_MQUEUE_SYSCTL # config CROSS_MEMORY_ATTACH # config FHANDLE # config USELIB # config AUDIT # config HAVE_ARCH_AUDITSYSCALL # config AUDITSYSCALL # config AUDIT_WATCH # config AUDIT_TREE # menu "IRQ subsystem" # config MAY_HAVE_SPARSE_IRQ # config GENERIC_IRQ_LEGACY # config GENERIC_IRQ_PROBE # ... import sys from kconfiglib import Kconfig, Symbol, Choice, MENU, COMMENT def indent_print(s, indent): print(indent*" " + s) def print_items(node, indent): while node: if isinstance(node.item, Symbol): indent_print("config " + node.item.name, indent) elif isinstance(node.item, Choice): indent_print("choice", indent) elif node.item == MENU: indent_print('menu "{}"'.format(node.prompt[0]), indent) elif node.item == COMMENT: indent_print('comment "{}"'.format(node.prompt[0]), indent) if node.list: print_items(node.list, indent + 2) node = node.next kconf = Kconfig(sys.argv[1]) print_items(kconf.top_node, 0) ================================================ FILE: kernel/scripts/Kconfiglib/genconfig.py ================================================ #!/usr/bin/env python # Copyright (c) 2018-2019, Ulf Magnusson # SPDX-License-Identifier: ISC """ Generates a header file with #defines from the configuration, matching the format of include/generated/autoconf.h in the Linux kernel. Optionally, also writes the configuration output as a .config file. See --config-out. The --sync-deps, --file-list, and --env-list options generate information that can be used to avoid needless rebuilds/reconfigurations. Before writing a header or configuration file, Kconfiglib compares the old contents of the file against the new contents. If there's no change, the write is skipped. This avoids updating file metadata like the modification time, and might save work depending on your build setup. By default, the configuration is generated from '.config'. A different configuration file can be passed in the KCONFIG_CONFIG environment variable. A custom header string can be inserted at the beginning of generated configuration and header files by setting the KCONFIG_CONFIG_HEADER and KCONFIG_AUTOHEADER_HEADER environment variables, respectively (this also works for other scripts). The string is not automatically made a comment (this is by design, to allow anything to be added), and no trailing newline is added, so add '/* */', '#', and newlines as appropriate. See https://www.gnu.org/software/make/manual/make.html#Multi_002dLine for a handy way to define multi-line variables in makefiles, for use with custom headers. Remember to export the variable to the environment. """ import argparse import os import sys import kconfiglib DEFAULT_SYNC_DEPS_PATH = "deps/" def main(): parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, description=__doc__) parser.add_argument( "--header-path", metavar="HEADER_FILE", help=""" Path to write the generated header file to. If not specified, the path in the environment variable KCONFIG_AUTOHEADER is used if it is set, and 'config.h' otherwise. """) parser.add_argument( "--config-out", metavar="CONFIG_FILE", help=""" Write the configuration to CONFIG_FILE. This is useful if you include .config files in Makefiles, as the generated configuration file will be a full .config file even if .config is outdated. The generated configuration matches what olddefconfig would produce. If you use sync-deps, you can include deps/auto.conf instead. --config-out is meant for cases where incremental build information isn't needed. """) parser.add_argument( "--sync-deps", metavar="OUTPUT_DIR", nargs="?", const=DEFAULT_SYNC_DEPS_PATH, help=""" Enable generation of symbol dependency information for incremental builds, optionally specifying the output directory (default: {}). See the docstring of Kconfig.sync_deps() in Kconfiglib for more information. """.format(DEFAULT_SYNC_DEPS_PATH)) parser.add_argument( "--file-list", metavar="OUTPUT_FILE", help=""" Write a list of all Kconfig files to OUTPUT_FILE, with one file per line. The paths are relative to $srctree (or to the current directory if $srctree is unset). Files appear in the order they're 'source'd. """) parser.add_argument( "--env-list", metavar="OUTPUT_FILE", help=""" Write a list of all environment variables referenced in Kconfig files to OUTPUT_FILE, with one variable per line. Each line has the format NAME=VALUE. Only environment variables referenced with the preprocessor $(VAR) syntax are included, and not variables referenced with the older $VAR syntax (which is only supported for backwards compatibility). """) parser.add_argument( "kconfig", metavar="KCONFIG", nargs="?", default="Kconfig", help="Top-level Kconfig file (default: Kconfig)") args = parser.parse_args() kconf = kconfiglib.Kconfig(args.kconfig, suppress_traceback=True) kconf.load_config() if args.header_path is None: if "KCONFIG_AUTOHEADER" in os.environ: kconf.write_autoconf() else: # Kconfiglib defaults to include/generated/autoconf.h to be # compatible with the C tools. 'config.h' is used here instead for # backwards compatibility. It's probably a saner default for tools # as well. kconf.write_autoconf("config.h") else: kconf.write_autoconf(args.header_path) if args.config_out is not None: kconf.write_config(args.config_out, save_old=False) if args.sync_deps is not None: kconf.sync_deps(args.sync_deps) if args.file_list is not None: with _open_write(args.file_list) as f: for path in kconf.kconfig_filenames: f.write(path + "\n") if args.env_list is not None: with _open_write(args.env_list) as f: for env_var in kconf.env_vars: f.write("{}={}\n".format(env_var, os.environ[env_var])) def _open_write(path): # Python 2/3 compatibility. io.open() is available on both, but makes # write() expect 'unicode' strings on Python 2. if sys.version_info[0] < 3: return open(path, "w") return open(path, "w", encoding="utf-8") if __name__ == "__main__": main() ================================================ FILE: kernel/scripts/Kconfiglib/guiconfig.py ================================================ #!/usr/bin/env python # Copyright (c) 2019, Ulf Magnusson # SPDX-License-Identifier: ISC """ Overview ======== A Tkinter-based menuconfig implementation, based around a treeview control and a help display. The interface should feel familiar to people used to qconf ('make xconfig'). Compatible with both Python 2 and Python 3. The display can be toggled between showing the full tree and showing just a single menu (like menuconfig.py). Only single-menu mode distinguishes between symbols defined with 'config' and symbols defined with 'menuconfig'. A show-all mode is available that shows invisible items in red. Supports both mouse and keyboard controls. The following keyboard shortcuts are available: Ctrl-S : Save configuration Ctrl-O : Open configuration Ctrl-A : Toggle show-all mode Ctrl-N : Toggle show-name mode Ctrl-M : Toggle single-menu mode Ctrl-F, /: Open jump-to dialog ESC : Close Running ======= guiconfig.py can be run either as a standalone executable or by calling the menuconfig() function with an existing Kconfig instance. The second option is a bit inflexible in that it will still load and save .config, etc. When run in standalone mode, the top-level Kconfig file to load can be passed as a command-line argument. With no argument, it defaults to "Kconfig". The KCONFIG_CONFIG environment variable specifies the .config file to load (if it exists) and save. If KCONFIG_CONFIG is unset, ".config" is used. When overwriting a configuration file, the old version is saved to .old (e.g. .config.old). $srctree is supported through Kconfiglib. """ # Note: There's some code duplication with menuconfig.py below, especially for # the help text. Maybe some of it could be moved into kconfiglib.py or a shared # helper script, but OTOH it's pretty nice to have things standalone and # customizable. import errno import os import sys _PY2 = sys.version_info[0] < 3 if _PY2: # Python 2 from Tkinter import * import ttk import tkFont as font import tkFileDialog as filedialog import tkMessageBox as messagebox else: # Python 3 from tkinter import * import tkinter.ttk as ttk import tkinter.font as font from tkinter import filedialog, messagebox from kconfiglib import Symbol, Choice, MENU, COMMENT, MenuNode, \ BOOL, TRISTATE, STRING, INT, HEX, \ AND, OR, \ expr_str, expr_value, split_expr, \ standard_sc_expr_str, \ TRI_TO_STR, TYPE_TO_STR, \ standard_kconfig, standard_config_filename # If True, use GIF image data embedded in this file instead of separate GIF # files. See _load_images(). _USE_EMBEDDED_IMAGES = True # Help text for the jump-to dialog _JUMP_TO_HELP = """\ Type one or more strings/regexes and press Enter to list items that match all of them. Python's regex flavor is used (see the 're' module). Double-clicking an item will jump to it. Item values can be toggled directly within the dialog.\ """ def _main(): menuconfig(standard_kconfig(__doc__)) # Global variables used below: # # _root: # The Toplevel instance for the main window # # _tree: # The Treeview in the main window # # _jump_to_tree: # The Treeview in the jump-to dialog. None if the jump-to dialog isn't # open. Doubles as a flag. # # _jump_to_matches: # List of Nodes shown in the jump-to dialog # # _menupath: # The Label that shows the menu path of the selected item # # _backbutton: # The button shown in single-menu mode for jumping to the parent menu # # _status_label: # Label with status text shown at the bottom of the main window # ("Modified", "Saved to ...", etc.) # # _id_to_node: # We can't use Node objects directly as Treeview item IDs, so we use their # id()s instead. This dictionary maps Node id()s back to Nodes. (The keys # are actually str(id(node)), just to simplify lookups.) # # _cur_menu: # The current menu. Ignored outside single-menu mode. # # _show_all_var/_show_name_var/_single_menu_var: # Tkinter Variable instances bound to the corresponding checkboxes # # _show_all/_single_menu: # Plain Python bools that track _show_all_var and _single_menu_var, to # speed up and simplify things a bit # # _conf_filename: # File to save the configuration to # # _minconf_filename: # File to save minimal configurations to # # _conf_changed: # True if the configuration has been changed. If False, we don't bother # showing the save-and-quit dialog. # # We reset this to False whenever the configuration is saved. # # _*_img: # PhotoImage instances for images def menuconfig(kconf): """ Launches the configuration interface, returning after the user exits. kconf: Kconfig instance to be configured """ global _kconf global _conf_filename global _minconf_filename global _jump_to_tree global _cur_menu _kconf = kconf _jump_to_tree = None _create_id_to_node() _create_ui() # Filename to save configuration to _conf_filename = standard_config_filename() # Load existing configuration and check if it's outdated _set_conf_changed(_load_config()) # Filename to save minimal configuration to _minconf_filename = "defconfig" # Current menu in single-menu mode _cur_menu = _kconf.top_node # Any visible items in the top menu? if not _shown_menu_nodes(kconf.top_node): # Nothing visible. Start in show-all mode and try again. _show_all_var.set(True) if not _shown_menu_nodes(kconf.top_node): # Give up and show an error. It's nice to be able to assume that # the tree is non-empty in the rest of the code. _root.wait_visibility() messagebox.showerror( "Error", "Empty configuration -- nothing to configure.\n\n" "Check that environment variables are set properly.") _root.destroy() return # Build the initial tree _update_tree() # Select the first item and focus the Treeview, so that keyboard controls # work immediately _select(_tree, _tree.get_children()[0]) _tree.focus_set() # Make geometry information available for centering the window. This # indirectly creates the window, so hide it so that it's never shown at the # old location. _root.withdraw() _root.update_idletasks() # Center the window _root.geometry("+{}+{}".format( (_root.winfo_screenwidth() - _root.winfo_reqwidth())//2, (_root.winfo_screenheight() - _root.winfo_reqheight())//2)) # Show it _root.deiconify() # Prevent the window from being automatically resized. Otherwise, it # changes size when scrollbars appear/disappear before the user has # manually resized it. _root.geometry(_root.geometry()) _root.mainloop() def _load_config(): # Loads any existing .config file. See the Kconfig.load_config() docstring. # # Returns True if .config is missing or outdated. We always prompt for # saving the configuration in that case. print(_kconf.load_config()) if not os.path.exists(_conf_filename): # No .config return True return _needs_save() def _needs_save(): # Returns True if a just-loaded .config file is outdated (would get # modified when saving) if _kconf.missing_syms: # Assignments to undefined symbols in the .config return True for sym in _kconf.unique_defined_syms: if sym.user_value is None: if sym.config_string: # Unwritten symbol return True elif sym.orig_type in (BOOL, TRISTATE): if sym.tri_value != sym.user_value: # Written bool/tristate symbol, new value return True elif sym.str_value != sym.user_value: # Written string/int/hex symbol, new value return True # No need to prompt for save return False def _create_id_to_node(): global _id_to_node _id_to_node = {str(id(node)): node for node in _kconf.node_iter()} def _create_ui(): # Creates the main window UI global _root global _tree # Create the root window. This initializes Tkinter and makes e.g. # PhotoImage available, so do it early. _root = Tk() _load_images() _init_misc_ui() _fix_treeview_issues() _create_top_widgets() # Create the pane with the Kconfig tree and description text panedwindow, _tree = _create_kconfig_tree_and_desc(_root) panedwindow.grid(column=0, row=1, sticky="nsew") _create_status_bar() _root.columnconfigure(0, weight=1) # Only the pane with the Kconfig tree and description grows vertically _root.rowconfigure(1, weight=1) # Start with show-name disabled _do_showname() _tree.bind("", _tree_left_key) _tree.bind("", _tree_right_key) # Note: Binding this for the jump-to tree as well would cause issues due to # the Tk bug mentioned in _tree_open() _tree.bind("<>", _tree_open) # add=True to avoid overriding the description text update _tree.bind("<>", _update_menu_path, add=True) _root.bind("", _save) _root.bind("", _open) _root.bind("", _toggle_showall) _root.bind("", _toggle_showname) _root.bind("", _toggle_tree_mode) _root.bind("", _jump_to_dialog) _root.bind("/", _jump_to_dialog) _root.bind("", _on_quit) def _load_images(): # Loads GIF images, creating the global _*_img PhotoImage variables. # Base64-encoded images embedded in this script are used if # _USE_EMBEDDED_IMAGES is True, and separate image files in the same # directory as the script otherwise. # # Using a global variable indirectly prevents the image from being # garbage-collected. Passing an image to a Tkinter function isn't enough to # keep it alive. def load_image(name, data): var_name = "_{}_img".format(name) if _USE_EMBEDDED_IMAGES: globals()[var_name] = PhotoImage(data=data, format="gif") else: globals()[var_name] = PhotoImage( file=os.path.join(os.path.dirname(__file__), name + ".gif"), format="gif") # Note: Base64 data can be put on the clipboard with # $ base64 -w0 foo.gif | xclip load_image("icon", "R0lGODlhMAAwAPEDAAAAAADQAO7u7v///yH5BAUKAAMALAAAAAAwADAAAAL/nI+gy+2Pokyv2jazuZxryQjiSJZmyXxHeLbumH6sEATvW8OLNtf5bfLZRLFITzgEipDJ4mYxYv6A0ubuqYhWk66tVTE4enHer7jcKvt0LLUw6P45lvEprT6c0+v7OBuqhYdHohcoqIbSAHc4ljhDwrh1UlgSydRCWWlp5wiYZvmSuSh4IzrqV6p4cwhkCsmY+nhK6uJ6t1mrOhuJqfu6+WYiCiwl7HtLjNSZZZis/MeM7NY3TaRKS40ooDeoiVqIultsrav92bi9c3a5KkkOsOJZpSS99m4k/0zPng4Gks9JSbB+8DIcoQfnjwpZCHv5W+ip4aQrKrB0uOikYhiMCBw1/uPoQUMBADs=") load_image("n_bool", "R0lGODdhEAAQAPAAAAgICP///ywAAAAAEAAQAAACIISPacHtvp5kcb5qG85hZ2+BkyiRF8BBaEqtrKkqslEAADs=") load_image("y_bool", "R0lGODdhEAAQAPEAAAgICADQAP///wAAACwAAAAAEAAQAAACMoSPacLtvlh4YrIYsst2cV19AvaVF9CUXBNJJoum7ymrsKuCnhiupIWjSSjAFuWhSCIKADs=") load_image("n_tri", "R0lGODlhEAAQAPD/AAEBAf///yH5BAUKAAIALAAAAAAQABAAAAInlI+pBrAKQnCPSUlXvFhznlkfeGwjKZhnJ65h6nrfi6h0st2QXikFADs=") load_image("m_tri", "R0lGODlhEAAQAPEDAAEBAeQMuv///wAAACH5BAUKAAMALAAAAAAQABAAAAI5nI+pBrAWAhPCjYhiAJQCnWmdoElHGVBoiK5M21ofXFpXRIrgiecqxkuNciZIhNOZFRNI24PhfEoLADs=") load_image("y_tri", "R0lGODlhEAAQAPEDAAICAgDQAP///wAAACH5BAUKAAMALAAAAAAQABAAAAI0nI+pBrAYBhDCRRUypfmergmgZ4xjMpmaw2zmxk7cCB+pWiVqp4MzDwn9FhGZ5WFjIZeGAgA7") load_image("m_my", "R0lGODlhEAAQAPEDAAAAAOQMuv///wAAACH5BAUKAAMALAAAAAAQABAAAAI5nIGpxiAPI2ghxFinq/ZygQhc94zgZopmOLYf67anGr+oZdp02emfV5n9MEHN5QhqICETxkABbQ4KADs=") load_image("y_my", "R0lGODlhEAAQAPH/AAAAAADQAAPRA////yH5BAUKAAQALAAAAAAQABAAAAM+SArcrhCMSSuIM9Q8rxxBWIXawIBkmWonupLd565Um9G1PIs59fKmzw8WnAlusBYR2SEIN6DmAmqBLBxYSAIAOw==") load_image("n_locked", "R0lGODlhEAAQAPABAAAAAP///yH5BAUKAAEALAAAAAAQABAAAAIgjB8AyKwN04pu0vMutpqqz4Hih4ydlnUpyl2r23pxUAAAOw==") load_image("m_locked", "R0lGODlhEAAQAPD/AAAAAOQMuiH5BAUKAAIALAAAAAAQABAAAAIylC8AyKwN04ohnGcqqlZmfXDWI26iInZoyiore05walolV39ftxsYHgL9QBBMBGFEFAAAOw==") load_image("y_locked", "R0lGODlhEAAQAPD/AAAAAADQACH5BAUKAAIALAAAAAAQABAAAAIylC8AyKzNgnlCtoDTwvZwrHydIYpQmR3KWq4uK74IOnp0HQPmnD3cOVlUIAgKsShkFAAAOw==") load_image("not_selected", "R0lGODlhEAAQAPD/AAAAAP///yH5BAUKAAIALAAAAAAQABAAAAIrlA2px6IBw2IpWglOvTYhzmUbGD3kNZ5QqrKn2YrqigCxZoMelU6No9gdCgA7") load_image("selected", "R0lGODlhEAAQAPD/AAAAAP///yH5BAUKAAIALAAAAAAQABAAAAIzlA2px6IBw2IpWglOvTah/kTZhimASJomiqonlLov1qptHTsgKSEzh9H8QI0QzNPwmRoFADs=") load_image("edit", "R0lGODlhEAAQAPIFAAAAAKOLAMuuEPvXCvrxvgAAAAAAAAAAACH5BAUKAAUALAAAAAAQABAAAANCWLqw/gqMBp8cszJxcwVC2FEOEIAi5kVBi3IqWZhuCGMyfdpj2e4pnK+WAshmvxeAcETWlsxPkkBtsqBMa8TIBSQAADs=") def _fix_treeview_issues(): # Fixes some Treeview issues global _treeview_rowheight style = ttk.Style() # The treeview rowheight isn't adjusted automatically on high-DPI displays, # so do it ourselves. The font will probably always be TkDefaultFont, but # play it safe and look it up. _treeview_rowheight = font.Font(font=style.lookup("Treeview", "font")) \ .metrics("linespace") + 2 style.configure("Treeview", rowheight=_treeview_rowheight) # Work around regression in https://core.tcl.tk/tk/tktview?name=509cafafae, # which breaks tag background colors for option in "foreground", "background": # Filter out any styles starting with ("!disabled", "!selected", ...). # style.map() returns an empty list for missing options, so this should # be future-safe. style.map( "Treeview", **{option: [elm for elm in style.map("Treeview", query_opt=option) if elm[:2] != ("!disabled", "!selected")]}) def _init_misc_ui(): # Does misc. UI initialization, like setting the title, icon, and theme _root.title(_kconf.mainmenu_text) # iconphoto() isn't available in Python 2's Tkinter _root.tk.call("wm", "iconphoto", _root._w, "-default", _icon_img) # Reducing the width of the window to 1 pixel makes it move around, at # least on GNOME. Prevent weird stuff like that. _root.minsize(128, 128) _root.protocol("WM_DELETE_WINDOW", _on_quit) # Use the 'clam' theme on *nix if it's available. It looks nicer than the # 'default' theme. if _root.tk.call("tk", "windowingsystem") == "x11": style = ttk.Style() if "clam" in style.theme_names(): style.theme_use("clam") def _create_top_widgets(): # Creates the controls above the Kconfig tree in the main window global _show_all_var global _show_name_var global _single_menu_var global _menupath global _backbutton topframe = ttk.Frame(_root) topframe.grid(column=0, row=0, sticky="ew") ttk.Button(topframe, text="Save", command=_save) \ .grid(column=0, row=0, sticky="ew", padx=".05c", pady=".05c") ttk.Button(topframe, text="Save as...", command=_save_as) \ .grid(column=1, row=0, sticky="ew") ttk.Button(topframe, text="Save minimal (advanced)...", command=_save_minimal) \ .grid(column=2, row=0, sticky="ew", padx=".05c") ttk.Button(topframe, text="Open...", command=_open) \ .grid(column=3, row=0) ttk.Button(topframe, text="Jump to...", command=_jump_to_dialog) \ .grid(column=4, row=0, padx=".05c") _show_name_var = BooleanVar() ttk.Checkbutton(topframe, text="Show name", command=_do_showname, variable=_show_name_var) \ .grid(column=0, row=1, sticky="nsew", padx=".05c", pady="0 .05c", ipady=".2c") _show_all_var = BooleanVar() ttk.Checkbutton(topframe, text="Show all", command=_do_showall, variable=_show_all_var) \ .grid(column=1, row=1, sticky="nsew", pady="0 .05c") # Allow the show-all and single-menu status to be queried via plain global # Python variables, which is faster and simpler def show_all_updated(*_): global _show_all _show_all = _show_all_var.get() _trace_write(_show_all_var, show_all_updated) _show_all_var.set(False) _single_menu_var = BooleanVar() ttk.Checkbutton(topframe, text="Single-menu mode", command=_do_tree_mode, variable=_single_menu_var) \ .grid(column=2, row=1, sticky="nsew", padx=".05c", pady="0 .05c") _backbutton = ttk.Button(topframe, text="<--", command=_leave_menu, state="disabled") _backbutton.grid(column=0, row=4, sticky="nsew", padx=".05c", pady="0 .05c") def tree_mode_updated(*_): global _single_menu _single_menu = _single_menu_var.get() if _single_menu: _backbutton.grid() else: _backbutton.grid_remove() _trace_write(_single_menu_var, tree_mode_updated) _single_menu_var.set(False) # Column to the right of the buttons that the menu path extends into, so # that it can grow wider than the buttons topframe.columnconfigure(5, weight=1) _menupath = ttk.Label(topframe) _menupath.grid(column=0, row=3, columnspan=6, sticky="w", padx="0.05c", pady="0 .05c") def _create_kconfig_tree_and_desc(parent): # Creates a Panedwindow with a Treeview that shows Kconfig nodes and a Text # that shows a description of the selected node. Returns a tuple with the # Panedwindow and the Treeview. This code is shared between the main window # and the jump-to dialog. panedwindow = ttk.Panedwindow(parent, orient=VERTICAL) tree_frame, tree = _create_kconfig_tree(panedwindow) desc_frame, desc = _create_kconfig_desc(panedwindow) panedwindow.add(tree_frame, weight=1) panedwindow.add(desc_frame) def tree_select(_): # The Text widget does not allow editing the text in its disabled # state. We need to temporarily enable it. desc["state"] = "normal" sel = tree.selection() if not sel: desc.delete("1.0", "end") desc["state"] = "disabled" return # Text.replace() is not available in Python 2's Tkinter desc.delete("1.0", "end") desc.insert("end", _info_str(_id_to_node[sel[0]])) desc["state"] = "disabled" tree.bind("<>", tree_select) tree.bind("<1>", _tree_click) tree.bind("", _tree_double_click) tree.bind("", _tree_enter) tree.bind("", _tree_enter) tree.bind("", _tree_toggle) tree.bind("n", _tree_set_val(0)) tree.bind("m", _tree_set_val(1)) tree.bind("y", _tree_set_val(2)) return panedwindow, tree def _create_kconfig_tree(parent): # Creates a Treeview for showing Kconfig nodes frame = ttk.Frame(parent) tree = ttk.Treeview(frame, selectmode="browse", height=20, columns=("name",)) tree.heading("#0", text="Option", anchor="w") tree.heading("name", text="Name", anchor="w") tree.tag_configure("n-bool", image=_n_bool_img) tree.tag_configure("y-bool", image=_y_bool_img) tree.tag_configure("m-tri", image=_m_tri_img) tree.tag_configure("n-tri", image=_n_tri_img) tree.tag_configure("m-tri", image=_m_tri_img) tree.tag_configure("y-tri", image=_y_tri_img) tree.tag_configure("m-my", image=_m_my_img) tree.tag_configure("y-my", image=_y_my_img) tree.tag_configure("n-locked", image=_n_locked_img) tree.tag_configure("m-locked", image=_m_locked_img) tree.tag_configure("y-locked", image=_y_locked_img) tree.tag_configure("not-selected", image=_not_selected_img) tree.tag_configure("selected", image=_selected_img) tree.tag_configure("edit", image=_edit_img) tree.tag_configure("invisible", foreground="red") tree.grid(column=0, row=0, sticky="nsew") _add_vscrollbar(frame, tree) frame.columnconfigure(0, weight=1) frame.rowconfigure(0, weight=1) # Create items for all menu nodes. These can be detached/moved later. # Micro-optimize this a bit. insert = tree.insert id_ = id Symbol_ = Symbol for node in _kconf.node_iter(): item = node.item insert("", "end", iid=id_(node), values=item.name if item.__class__ is Symbol_ else "") return frame, tree def _create_kconfig_desc(parent): # Creates a Text for showing the description of the selected Kconfig node frame = ttk.Frame(parent) desc = Text(frame, height=12, wrap="none", borderwidth=0, state="disabled") desc.grid(column=0, row=0, sticky="nsew") # Work around not being to Ctrl-C/V text from a disabled Text widget, with a # tip found in https://stackoverflow.com/questions/3842155/is-there-a-way-to-make-the-tkinter-text-widget-read-only desc.bind("<1>", lambda _: desc.focus_set()) _add_vscrollbar(frame, desc) frame.columnconfigure(0, weight=1) frame.rowconfigure(0, weight=1) return frame, desc def _add_vscrollbar(parent, widget): # Adds a vertical scrollbar to 'widget' that's only shown as needed vscrollbar = ttk.Scrollbar(parent, orient="vertical", command=widget.yview) vscrollbar.grid(column=1, row=0, sticky="ns") def yscrollcommand(first, last): # Only show the scrollbar when needed. 'first' and 'last' are # strings. if float(first) <= 0.0 and float(last) >= 1.0: vscrollbar.grid_remove() else: vscrollbar.grid() vscrollbar.set(first, last) widget["yscrollcommand"] = yscrollcommand def _create_status_bar(): # Creates the status bar at the bottom of the main window global _status_label _status_label = ttk.Label(_root, anchor="e", padding="0 0 0.4c 0") _status_label.grid(column=0, row=3, sticky="ew") def _set_status(s): # Sets the text in the status bar to 's' _status_label["text"] = s def _set_conf_changed(changed): # Updates the status re. whether there are unsaved changes global _conf_changed _conf_changed = changed if changed: _set_status("Modified") def _update_tree(): # Updates the Kconfig tree in the main window by first detaching all nodes # and then updating and reattaching them. The tree structure might have # changed. # If a selected/focused item is detached and later reattached, it stays # selected/focused. That can give multiple selections even though # selectmode=browse. Save and later restore the selection and focus as a # workaround. old_selection = _tree.selection() old_focus = _tree.focus() # Detach all tree items before re-stringing them. This is relatively fast, # luckily. _tree.detach(*_id_to_node.keys()) if _single_menu: _build_menu_tree() else: _build_full_tree(_kconf.top_node) _tree.selection_set(old_selection) _tree.focus(old_focus) def _build_full_tree(menu): # Updates the tree starting from menu.list, in full-tree mode. To speed # things up, only open menus are updated. The menu-at-a-time logic here is # to deal with invisible items that can show up outside show-all mode (see # _shown_full_nodes()). for node in _shown_full_nodes(menu): _add_to_tree(node, _kconf.top_node) # _shown_full_nodes() includes nodes from menus rooted at symbols, so # we only need to check "real" menus/choices here if node.list and not isinstance(node.item, Symbol): if _tree.item(id(node), "open"): _build_full_tree(node) else: # We're just probing here, so _shown_menu_nodes() will work # fine, and might be a bit faster shown = _shown_menu_nodes(node) if shown: # Dummy element to make the open/closed toggle appear _tree.move(id(shown[0]), id(shown[0].parent), "end") def _shown_full_nodes(menu): # Returns the list of menu nodes shown in 'menu' (a menu node for a menu) # for full-tree mode. A tricky detail is that invisible items need to be # shown if they have visible children. def rec(node): res = [] while node: if _visible(node) or _show_all: res.append(node) if node.list and isinstance(node.item, Symbol): # Nodes from menu created from dependencies res += rec(node.list) elif node.list and isinstance(node.item, Symbol): # Show invisible symbols (defined with either 'config' and # 'menuconfig') if they have visible children. This can happen # for an m/y-valued symbol with an optional prompt # ('prompt "foo" is COND') that is currently disabled. shown_children = rec(node.list) if shown_children: res.append(node) res += shown_children node = node.next return res return rec(menu.list) def _build_menu_tree(): # Updates the tree in single-menu mode. See _build_full_tree() as well. for node in _shown_menu_nodes(_cur_menu): _add_to_tree(node, _cur_menu) def _shown_menu_nodes(menu): # Used for single-menu mode. Similar to _shown_full_nodes(), but doesn't # include children of symbols defined with 'menuconfig'. def rec(node): res = [] while node: if _visible(node) or _show_all: res.append(node) if node.list and not node.is_menuconfig: res += rec(node.list) elif node.list and isinstance(node.item, Symbol): shown_children = rec(node.list) if shown_children: # Invisible item with visible children res.append(node) if not node.is_menuconfig: res += shown_children node = node.next return res return rec(menu.list) def _visible(node): # Returns True if the node should appear in the menu (outside show-all # mode) return node.prompt and expr_value(node.prompt[1]) and not \ (node.item == MENU and not expr_value(node.visibility)) def _add_to_tree(node, top): # Adds 'node' to the tree, at the end of its menu. We rely on going through # the nodes linearly to get the correct order. 'top' holds the menu that # corresponds to the top-level menu, and can vary in single-menu mode. parent = node.parent _tree.move(id(node), "" if parent is top else id(parent), "end") _tree.item( id(node), text=_node_str(node), # The _show_all test avoids showing invisible items in red outside # show-all mode, which could look confusing/broken. Invisible symbols # are shown outside show-all mode if an invisible symbol has visible # children in an implicit menu. tags=_img_tag(node) if _visible(node) or not _show_all else _img_tag(node) + " invisible") def _node_str(node): # Returns the string shown to the right of the image (if any) for the node if node.prompt: if node.item == COMMENT: s = "*** {} ***".format(node.prompt[0]) else: s = node.prompt[0] if isinstance(node.item, Symbol): sym = node.item # Print "(NEW)" next to symbols without a user value (from e.g. a # .config), but skip it for choice symbols in choices in y mode, # and for symbols of UNKNOWN type (which generate a warning though) if sym.user_value is None and sym.type and not \ (sym.choice and sym.choice.tri_value == 2): s += " (NEW)" elif isinstance(node.item, Symbol): # Symbol without prompt (can show up in show-all) s = "<{}>".format(node.item.name) else: # Choice without prompt. Use standard_sc_expr_str() so that it shows up # as ''. s = standard_sc_expr_str(node.item) if isinstance(node.item, Symbol): sym = node.item if sym.orig_type == STRING: s += ": " + sym.str_value elif sym.orig_type in (INT, HEX): s = "({}) {}".format(sym.str_value, s) elif isinstance(node.item, Choice) and node.item.tri_value == 2: # Print the prompt of the selected symbol after the choice for # choices in y mode sym = node.item.selection if sym: for sym_node in sym.nodes: # Use the prompt used at this choice location, in case the # choice symbol is defined in multiple locations if sym_node.parent is node and sym_node.prompt: s += " ({})".format(sym_node.prompt[0]) break else: # If the symbol isn't defined at this choice location, then # just use whatever prompt we can find for it for sym_node in sym.nodes: if sym_node.prompt: s += " ({})".format(sym_node.prompt[0]) break # In single-menu mode, print "--->" next to nodes that have menus that can # potentially be entered. Print "----" if the menu is empty. We don't allow # those to be entered. if _single_menu and node.is_menuconfig: s += " --->" if _shown_menu_nodes(node) else " ----" return s def _img_tag(node): # Returns the tag for the image that should be shown next to 'node', or the # empty string if it shouldn't have an image item = node.item if item in (MENU, COMMENT) or not item.orig_type: return "" if item.orig_type in (STRING, INT, HEX): return "edit" # BOOL or TRISTATE if _is_y_mode_choice_sym(item): # Choice symbol in y-mode choice return "selected" if item.choice.selection is item else "not-selected" if len(item.assignable) <= 1: # Pinned to a single value return "" if isinstance(item, Choice) else item.str_value + "-locked" if item.type == BOOL: return item.str_value + "-bool" # item.type == TRISTATE if item.assignable == (1, 2): return item.str_value + "-my" return item.str_value + "-tri" def _is_y_mode_choice_sym(item): # The choice mode is an upper bound on the visibility of choice symbols, so # we can check the choice symbols' own visibility to see if the choice is # in y mode return isinstance(item, Symbol) and item.choice and item.visibility == 2 def _tree_click(event): # Click on the Kconfig Treeview tree = event.widget if tree.identify_element(event.x, event.y) == "image": item = tree.identify_row(event.y) # Select the item before possibly popping up a dialog for # string/int/hex items, so that its help is visible _select(tree, item) _change_node(_id_to_node[item], tree.winfo_toplevel()) return "break" def _tree_double_click(event): # Double-click on the Kconfig treeview # Do an extra check to avoid weirdness when double-clicking in the tree # heading area if not _in_heading(event): return _tree_enter(event) def _in_heading(event): # Returns True if 'event' took place in the tree heading tree = event.widget return hasattr(tree, "identify_region") and \ tree.identify_region(event.x, event.y) in ("heading", "separator") def _tree_enter(event): # Enter press or double-click within the Kconfig treeview. Prefer to # open/close/enter menus, but toggle the value if that's not possible. tree = event.widget sel = tree.focus() if sel: node = _id_to_node[sel] if tree.get_children(sel): _tree_toggle_open(sel) elif _single_menu_mode_menu(node, tree): _enter_menu_and_select_first(node) else: _change_node(node, tree.winfo_toplevel()) return "break" def _tree_toggle(event): # Space press within the Kconfig treeview. Prefer to toggle the value, but # open/close/enter the menu if that's not possible. tree = event.widget sel = tree.focus() if sel: node = _id_to_node[sel] if _changeable(node): _change_node(node, tree.winfo_toplevel()) elif _single_menu_mode_menu(node, tree): _enter_menu_and_select_first(node) elif tree.get_children(sel): _tree_toggle_open(sel) return "break" def _tree_left_key(_): # Left arrow key press within the Kconfig treeview if _single_menu: # Leave the current menu in single-menu mode _leave_menu() return "break" # Otherwise, default action def _tree_right_key(_): # Right arrow key press within the Kconfig treeview sel = _tree.focus() if sel: node = _id_to_node[sel] # If the node can be entered in single-menu mode, do it if _single_menu_mode_menu(node, _tree): _enter_menu_and_select_first(node) return "break" # Otherwise, default action def _single_menu_mode_menu(node, tree): # Returns True if single-menu mode is on and 'node' is an (interface) # menu that can be entered return _single_menu and tree is _tree and node.is_menuconfig and \ _shown_menu_nodes(node) def _changeable(node): # Returns True if 'node' is a Symbol/Choice whose value can be changed sc = node.item if not isinstance(sc, (Symbol, Choice)): return False # This will hit for invisible symbols, which appear in show-all mode and # when an invisible symbol has visible children (which can happen e.g. for # symbols with optional prompts) if not (node.prompt and expr_value(node.prompt[1])): return False return sc.orig_type in (STRING, INT, HEX) or len(sc.assignable) > 1 \ or _is_y_mode_choice_sym(sc) def _tree_toggle_open(item): # Opens/closes the Treeview item 'item' if _tree.item(item, "open"): _tree.item(item, open=False) else: node = _id_to_node[item] if not isinstance(node.item, Symbol): # Can only get here in full-tree mode _build_full_tree(node) _tree.item(item, open=True) def _tree_set_val(tri_val): def tree_set_val(event): # n/m/y press within the Kconfig treeview # Sets the value of the currently selected item to 'tri_val', if that # value can be assigned sel = event.widget.focus() if sel: sc = _id_to_node[sel].item if isinstance(sc, (Symbol, Choice)) and tri_val in sc.assignable: _set_val(sc, tri_val) return tree_set_val def _tree_open(_): # Lazily populates the Kconfig tree when menus are opened in full-tree mode if _single_menu: # Work around https://core.tcl.tk/tk/tktview?name=368fa4561e # ("ttk::treeview open/closed indicators can be toggled while hidden"). # Clicking on the hidden indicator will call _build_full_tree() in # single-menu mode otherwise. return node = _id_to_node[_tree.focus()] # _shown_full_nodes() includes nodes from menus rooted at symbols, so we # only need to check "real" menus and choices here if not isinstance(node.item, Symbol): _build_full_tree(node) def _update_menu_path(_): # Updates the displayed menu path when nodes are selected in the Kconfig # treeview sel = _tree.selection() _menupath["text"] = _menu_path_info(_id_to_node[sel[0]]) if sel else "" def _item_row(item): # Returns the row number 'item' appears on within the Kconfig treeview, # starting from the top of the tree. Used to preserve scrolling. # # ttkTreeview.c in the Tk sources defines a RowNumber() function that does # the same thing, but it's not exposed. row = 0 while True: prev = _tree.prev(item) if prev: item = prev row += _n_rows(item) else: item = _tree.parent(item) if not item: return row row += 1 def _n_rows(item): # _item_row() helper. Returns the number of rows occupied by 'item' and # # its children. rows = 1 if _tree.item(item, "open"): for child in _tree.get_children(item): rows += _n_rows(child) return rows def _attached(item): # Heuristic for checking if a Treeview item is attached. Doesn't seem to be # good APIs for this. Might fail for super-obscure cases with tiny trees, # but you'd just get a small scroll mess-up. return bool(_tree.next(item) or _tree.prev(item) or _tree.parent(item)) def _change_node(node, parent): # Toggles/changes the value of 'node'. 'parent' is the parent window # (either the main window or the jump-to dialog), in case we need to pop up # a dialog. if not _changeable(node): return # sc = symbol/choice sc = node.item if sc.type in (INT, HEX, STRING): s = _set_val_dialog(node, parent) # Tkinter can return 'unicode' strings on Python 2, which Kconfiglib # can't deal with. UTF-8-encode the string to work around it. if _PY2 and isinstance(s, unicode): s = s.encode("utf-8", "ignore") if s is not None: _set_val(sc, s) elif len(sc.assignable) == 1: # Handles choice symbols for choices in y mode, which are a special # case: .assignable can be (2,) while .tri_value is 0. _set_val(sc, sc.assignable[0]) else: # Set the symbol to the value after the current value in # sc.assignable, with wrapping val_index = sc.assignable.index(sc.tri_value) _set_val(sc, sc.assignable[(val_index + 1) % len(sc.assignable)]) def _set_val(sc, val): # Wrapper around Symbol/Choice.set_value() for updating the menu state and # _conf_changed # Use the string representation of tristate values. This makes the format # consistent for all symbol types. if val in TRI_TO_STR: val = TRI_TO_STR[val] if val != sc.str_value: sc.set_value(val) _set_conf_changed(True) # Update the tree and try to preserve the scroll. Do a cheaper variant # than in the show-all case, that might mess up the scroll slightly in # rare cases, but is fast and flicker-free. stayput = _loc_ref_item() # Item to preserve scroll for old_row = _item_row(stayput) _update_tree() # If the reference item disappeared (can happen if the change was done # from the jump-to dialog), then avoid messing with the scroll and hope # for the best if _attached(stayput): _tree.yview_scroll(_item_row(stayput) - old_row, "units") if _jump_to_tree: _update_jump_to_display() def _set_val_dialog(node, parent): # Pops up a dialog for setting the value of the string/int/hex # symbol at node 'node'. 'parent' is the parent window. def ok(_=None): # No 'nonlocal' in Python 2 global _entry_res s = entry.get() if sym.type == HEX and not s.startswith(("0x", "0X")): s = "0x" + s if _check_valid(dialog, entry, sym, s): _entry_res = s dialog.destroy() def cancel(_=None): global _entry_res _entry_res = None dialog.destroy() sym = node.item dialog = Toplevel(parent) dialog.title("Enter {} value".format(TYPE_TO_STR[sym.type])) dialog.resizable(False, False) dialog.transient(parent) dialog.protocol("WM_DELETE_WINDOW", cancel) ttk.Label(dialog, text=node.prompt[0] + ":") \ .grid(column=0, row=0, columnspan=2, sticky="w", padx=".3c", pady=".2c .05c") entry = ttk.Entry(dialog, width=30) # Start with the previous value in the editbox, selected entry.insert(0, sym.str_value) entry.selection_range(0, "end") entry.grid(column=0, row=1, columnspan=2, sticky="ew", padx=".3c") entry.focus_set() range_info = _range_info(sym) if range_info: ttk.Label(dialog, text=range_info) \ .grid(column=0, row=2, columnspan=2, sticky="w", padx=".3c", pady=".2c 0") ttk.Button(dialog, text="OK", command=ok) \ .grid(column=0, row=4 if range_info else 3, sticky="e", padx=".3c", pady=".4c") ttk.Button(dialog, text="Cancel", command=cancel) \ .grid(column=1, row=4 if range_info else 3, padx="0 .3c") # Give all horizontal space to the grid cell with the OK button, so that # Cancel moves to the right dialog.columnconfigure(0, weight=1) _center_on_root(dialog) # Hack to scroll the entry so that the end of the text is shown, from # https://stackoverflow.com/questions/29334544/why-does-tkinters-entry-xview-moveto-fail. # Related Tk ticket: https://core.tcl.tk/tk/info/2513186fff def scroll_entry(_): _root.update_idletasks() entry.unbind("") entry.xview_moveto(1) entry.bind("", scroll_entry) # The dialog must be visible before we can grab the input dialog.wait_visibility() dialog.grab_set() dialog.bind("", ok) dialog.bind("", ok) dialog.bind("", cancel) # Wait for the user to be done with the dialog parent.wait_window(dialog) # Regrab the input in the parent parent.grab_set() return _entry_res def _center_on_root(dialog): # Centers 'dialog' on the root window. It often ends up at some bad place # like the top-left corner of the screen otherwise. See the menuconfig() # function, which has similar logic. dialog.withdraw() _root.update_idletasks() dialog_width = dialog.winfo_reqwidth() dialog_height = dialog.winfo_reqheight() screen_width = _root.winfo_screenwidth() screen_height = _root.winfo_screenheight() x = _root.winfo_rootx() + (_root.winfo_width() - dialog_width)//2 y = _root.winfo_rooty() + (_root.winfo_height() - dialog_height)//2 # Clamp so that no part of the dialog is outside the screen if x + dialog_width > screen_width: x = screen_width - dialog_width elif x < 0: x = 0 if y + dialog_height > screen_height: y = screen_height - dialog_height elif y < 0: y = 0 dialog.geometry("+{}+{}".format(x, y)) dialog.deiconify() def _check_valid(dialog, entry, sym, s): # Returns True if the string 's' is a well-formed value for 'sym'. # Otherwise, pops up an error and returns False. if sym.type not in (INT, HEX): # Anything goes for non-int/hex symbols return True base = 10 if sym.type == INT else 16 try: int(s, base) except ValueError: messagebox.showerror( "Bad value", "'{}' is a malformed {} value".format( s, TYPE_TO_STR[sym.type]), parent=dialog) entry.focus_set() return False for low_sym, high_sym, cond in sym.ranges: if expr_value(cond): low_s = low_sym.str_value high_s = high_sym.str_value if not int(low_s, base) <= int(s, base) <= int(high_s, base): messagebox.showerror( "Value out of range", "{} is outside the range {}-{}".format(s, low_s, high_s), parent=dialog) entry.focus_set() return False break return True def _range_info(sym): # Returns a string with information about the valid range for the symbol # 'sym', or None if 'sym' doesn't have a range if sym.type in (INT, HEX): for low, high, cond in sym.ranges: if expr_value(cond): return "Range: {}-{}".format(low.str_value, high.str_value) return None def _save(_=None): # Tries to save the configuration if _try_save(_kconf.write_config, _conf_filename, "configuration"): _set_conf_changed(False) _tree.focus_set() def _save_as(): # Pops up a dialog for saving the configuration to a specific location global _conf_filename filename = _conf_filename while True: filename = filedialog.asksaveasfilename( title="Save configuration as", initialdir=os.path.dirname(filename), initialfile=os.path.basename(filename), parent=_root) if not filename: break if _try_save(_kconf.write_config, filename, "configuration"): _conf_filename = filename break _tree.focus_set() def _save_minimal(): # Pops up a dialog for saving a minimal configuration (defconfig) to a # specific location global _minconf_filename filename = _minconf_filename while True: filename = filedialog.asksaveasfilename( title="Save minimal configuration as", initialdir=os.path.dirname(filename), initialfile=os.path.basename(filename), parent=_root) if not filename: break if _try_save(_kconf.write_min_config, filename, "minimal configuration"): _minconf_filename = filename break _tree.focus_set() def _open(_=None): # Pops up a dialog for loading a configuration global _conf_filename if _conf_changed and \ not messagebox.askokcancel( "Unsaved changes", "You have unsaved changes. Load new configuration anyway?"): return filename = _conf_filename while True: filename = filedialog.askopenfilename( title="Open configuration", initialdir=os.path.dirname(filename), initialfile=os.path.basename(filename), parent=_root) if not filename: break if _try_load(filename): # Maybe something fancier could be done here later to try to # preserve the scroll _conf_filename = filename _set_conf_changed(_needs_save()) if _single_menu and not _shown_menu_nodes(_cur_menu): # Turn on show-all if we're in single-menu mode and would end # up with an empty menu _show_all_var.set(True) _update_tree() break _tree.focus_set() def _toggle_showname(_): # Toggles show-name mode on/off _show_name_var.set(not _show_name_var.get()) _do_showname() def _do_showname(): # Updates the UI for the current show-name setting # Columns do not automatically shrink/expand, so we have to update # column widths ourselves tree_width = _tree.winfo_width() if _show_name_var.get(): _tree["displaycolumns"] = ("name",) _tree["show"] = "tree headings" name_width = tree_width//3 _tree.column("#0", width=max(tree_width - name_width, 1)) _tree.column("name", width=name_width) else: _tree["displaycolumns"] = () _tree["show"] = "tree" _tree.column("#0", width=tree_width) _tree.focus_set() def _toggle_showall(_): # Toggles show-all mode on/off _show_all_var.set(not _show_all) _do_showall() def _do_showall(): # Updates the UI for the current show-all setting # Don't allow turning off show-all if we'd end up with no visible nodes if _nothing_shown(): _show_all_var.set(True) return # Save scroll information. old_scroll can end up negative here, if the # reference item isn't shown (only invisible items on the screen, and # show-all being turned off). stayput = _vis_loc_ref_item() # Probe the middle of the first row, to play it safe. identify_row(0) seems # to return the row before the top row. old_scroll = _item_row(stayput) - \ _item_row(_tree.identify_row(_treeview_rowheight//2)) _update_tree() if _show_all: # Deep magic: Unless we call update_idletasks(), the scroll adjustment # below is restricted to the height of the old tree, instead of the # height of the new tree. Since the tree with show-all on is guaranteed # to be taller, and we want the maximum range, we only call it when # turning show-all on. # # Strictly speaking, something similar ought to be done when changing # symbol values, but it causes annoying flicker, and in 99% of cases # things work anyway there (with usually minor scroll mess-ups in the # 1% case). _root.update_idletasks() # Restore scroll _tree.yview(_item_row(stayput) - old_scroll) _tree.focus_set() def _nothing_shown(): # _do_showall() helper. Returns True if no nodes would get # shown with the current show-all setting. Also handles the # (obscure) case when there are no visible nodes in the entire # tree, meaning guiconfig was automatically started in # show-all mode, which mustn't be turned off. return not _shown_menu_nodes( _cur_menu if _single_menu else _kconf.top_node) def _toggle_tree_mode(_): # Toggles single-menu mode on/off _single_menu_var.set(not _single_menu) _do_tree_mode() def _do_tree_mode(): # Updates the UI for the current tree mode (full-tree or single-menu) loc_ref_node = _id_to_node[_loc_ref_item()] if not _single_menu: # _jump_to() -> _enter_menu() already updates the tree, but # _jump_to() -> load_parents() doesn't, because it isn't always needed. # We always need to update the tree here, e.g. to add/remove "--->". _update_tree() _jump_to(loc_ref_node) _tree.focus_set() def _enter_menu_and_select_first(menu): # Enters the menu 'menu' and selects the first item. Used in single-menu # mode. _enter_menu(menu) _select(_tree, _tree.get_children()[0]) def _enter_menu(menu): # Enters the menu 'menu'. Used in single-menu mode. global _cur_menu _cur_menu = menu _update_tree() _backbutton["state"] = "disabled" if menu is _kconf.top_node else "normal" def _leave_menu(): # Leaves the current menu. Used in single-menu mode. global _cur_menu if _cur_menu is not _kconf.top_node: old_menu = _cur_menu _cur_menu = _parent_menu(_cur_menu) _update_tree() _select(_tree, id(old_menu)) if _cur_menu is _kconf.top_node: _backbutton["state"] = "disabled" _tree.focus_set() def _select(tree, item): # Selects, focuses, and see()s 'item' in 'tree' tree.selection_set(item) tree.focus(item) tree.see(item) def _loc_ref_item(): # Returns a Treeview item that can serve as a reference for the current # scroll location. We try to make this item stay on the same row on the # screen when updating the tree. # If the selected item is visible, use that sel = _tree.selection() if sel and _tree.bbox(sel[0]): return sel[0] # Otherwise, use the middle item on the screen. If it doesn't exist, the # tree is probably really small, so use the first item in the entire tree. return _tree.identify_row(_tree.winfo_height()//2) or \ _tree.get_children()[0] def _vis_loc_ref_item(): # Like _loc_ref_item(), but finds a visible item around the reference item. # Used when changing show-all mode, where non-visible (red) items will # disappear. item = _loc_ref_item() vis_before = _vis_before(item) if vis_before and _tree.bbox(vis_before): return vis_before vis_after = _vis_after(item) if vis_after and _tree.bbox(vis_after): return vis_after return vis_before or vis_after def _vis_before(item): # _vis_loc_ref_item() helper. Returns the first visible (not red) item, # searching backwards from 'item'. while item: if not _tree.tag_has("invisible", item): return item prev = _tree.prev(item) item = prev if prev else _tree.parent(item) return None def _vis_after(item): # _vis_loc_ref_item() helper. Returns the first visible (not red) item, # searching forwards from 'item'. while item: if not _tree.tag_has("invisible", item): return item next = _tree.next(item) if next: item = next else: item = _tree.parent(item) if not item: break item = _tree.next(item) return None def _on_quit(_=None): # Called when the user wants to exit if not _conf_changed: _quit("No changes to save (for '{}')".format(_conf_filename)) return while True: ync = messagebox.askyesnocancel("Quit", "Save changes?") if ync is None: return if not ync: _quit("Configuration ({}) was not saved".format(_conf_filename)) return if _try_save(_kconf.write_config, _conf_filename, "configuration"): # _try_save() already prints the "Configuration saved to ..." # message _quit() return def _quit(msg=None): # Quits the application # Do not call sys.exit() here, in case we're being run from a script _root.destroy() if msg: print(msg) def _try_save(save_fn, filename, description): # Tries to save a configuration file. Pops up an error and returns False on # failure. # # save_fn: # Function to call with 'filename' to save the file # # description: # String describing the thing being saved try: # save_fn() returns a message to print msg = save_fn(filename) _set_status(msg) print(msg) return True except EnvironmentError as e: messagebox.showerror( "Error saving " + description, "Error saving {} to '{}': {} (errno: {})" .format(description, e.filename, e.strerror, errno.errorcode[e.errno])) return False def _try_load(filename): # Tries to load a configuration file. Pops up an error and returns False on # failure. # # filename: # Configuration file to load try: msg = _kconf.load_config(filename) _set_status(msg) print(msg) return True except EnvironmentError as e: messagebox.showerror( "Error loading configuration", "Error loading '{}': {} (errno: {})" .format(filename, e.strerror, errno.errorcode[e.errno])) return False def _jump_to_dialog(_=None): # Pops up a dialog for jumping directly to a particular node. Symbol values # can also be changed within the dialog. # # Note: There's nothing preventing this from doing an incremental search # like menuconfig.py does, but currently it's a bit jerky for large Kconfig # trees, at least when inputting the beginning of the search string. We'd # need to somehow only update the tree items that are shown in the Treeview # to fix it. global _jump_to_tree def search(_=None): _update_jump_to_matches(msglabel, entry.get()) def jump_to_selected(event=None): # Jumps to the selected node and closes the dialog # Ignore double clicks on the image and in the heading area if event and (tree.identify_element(event.x, event.y) == "image" or _in_heading(event)): return sel = tree.selection() if not sel: return node = _id_to_node[sel[0]] if node not in _shown_menu_nodes(_parent_menu(node)): _show_all_var.set(True) if not _single_menu: # See comment in _do_tree_mode() _update_tree() _jump_to(node) dialog.destroy() def tree_select(_): jumpto_button["state"] = "normal" if tree.selection() else "disabled" dialog = Toplevel(_root) dialog.geometry("+{}+{}".format( _root.winfo_rootx() + 50, _root.winfo_rooty() + 50)) dialog.title("Jump to symbol/choice/menu/comment") dialog.minsize(128, 128) # See _create_ui() dialog.transient(_root) ttk.Label(dialog, text=_JUMP_TO_HELP) \ .grid(column=0, row=0, columnspan=2, sticky="w", padx=".1c", pady=".1c") entry = ttk.Entry(dialog) entry.grid(column=0, row=1, sticky="ew", padx=".1c", pady=".1c") entry.focus_set() entry.bind("", search) entry.bind("", search) ttk.Button(dialog, text="Search", command=search) \ .grid(column=1, row=1, padx="0 .1c", pady="0 .1c") msglabel = ttk.Label(dialog) msglabel.grid(column=0, row=2, sticky="w", pady="0 .1c") panedwindow, tree = _create_kconfig_tree_and_desc(dialog) panedwindow.grid(column=0, row=3, columnspan=2, sticky="nsew") # Clear tree tree.set_children("") _jump_to_tree = tree jumpto_button = ttk.Button(dialog, text="Jump to selected item", state="disabled", command=jump_to_selected) jumpto_button.grid(column=0, row=4, columnspan=2, sticky="ns", pady=".1c") dialog.columnconfigure(0, weight=1) # Only the pane with the Kconfig tree and description grows vertically dialog.rowconfigure(3, weight=1) # See the menuconfig() function _root.update_idletasks() dialog.geometry(dialog.geometry()) # The dialog must be visible before we can grab the input dialog.wait_visibility() dialog.grab_set() tree.bind("", jump_to_selected) tree.bind("", jump_to_selected) tree.bind("", jump_to_selected) # add=True to avoid overriding the description text update tree.bind("<>", tree_select, add=True) dialog.bind("", lambda _: dialog.destroy()) # Wait for the user to be done with the dialog _root.wait_window(dialog) _jump_to_tree = None _tree.focus_set() def _update_jump_to_matches(msglabel, search_string): # Searches for nodes matching the search string and updates # _jump_to_matches. Puts a message in 'msglabel' if there are no matches, # or regex errors. global _jump_to_matches _jump_to_tree.selection_set(()) try: # We could use re.IGNORECASE here instead of lower(), but this is # faster for regexes like '.*debug$' (though the '.*' is redundant # there). Those probably have bad interactions with re.search(), which # matches anywhere in the string. regex_searches = [re.compile(regex).search for regex in search_string.lower().split()] except re.error as e: msg = "Bad regular expression" # re.error.msg was added in Python 3.5 if hasattr(e, "msg"): msg += ": " + e.msg msglabel["text"] = msg # Clear tree _jump_to_tree.set_children("") return _jump_to_matches = [] add_match = _jump_to_matches.append for node in _sorted_sc_nodes(): # Symbol/choice sc = node.item for search in regex_searches: # Both the name and the prompt might be missing, since # we're searching both symbols and choices # Does the regex match either the symbol name or the # prompt (if any)? if not (sc.name and search(sc.name.lower()) or node.prompt and search(node.prompt[0].lower())): # Give up on the first regex that doesn't match, to # speed things up a bit when multiple regexes are # entered break else: add_match(node) # Search menus and comments for node in _sorted_menu_comment_nodes(): for search in regex_searches: if not search(node.prompt[0].lower()): break else: add_match(node) msglabel["text"] = "" if _jump_to_matches else "No matches" _update_jump_to_display() if _jump_to_matches: item = id(_jump_to_matches[0]) _jump_to_tree.selection_set(item) _jump_to_tree.focus(item) def _update_jump_to_display(): # Updates the images and text for the items in _jump_to_matches, and sets # them as the items of _jump_to_tree # Micro-optimize a bit item = _jump_to_tree.item id_ = id node_str = _node_str img_tag = _img_tag visible = _visible for node in _jump_to_matches: item(id_(node), text=node_str(node), tags=img_tag(node) if visible(node) else img_tag(node) + " invisible") _jump_to_tree.set_children("", *map(id, _jump_to_matches)) def _jump_to(node): # Jumps directly to 'node' and selects it if _single_menu: _enter_menu(_parent_menu(node)) else: _load_parents(node) _select(_tree, id(node)) # Obscure Python: We never pass a value for cached_nodes, and it keeps pointing # to the same list. This avoids a global. def _sorted_sc_nodes(cached_nodes=[]): # Returns a sorted list of symbol and choice nodes to search. The symbol # nodes appear first, sorted by name, and then the choice nodes, sorted by # prompt and (secondarily) name. if not cached_nodes: # Add symbol nodes for sym in sorted(_kconf.unique_defined_syms, key=lambda sym: sym.name): # += is in-place for lists cached_nodes += sym.nodes # Add choice nodes choices = sorted(_kconf.unique_choices, key=lambda choice: choice.name or "") cached_nodes += sorted( [node for choice in choices for node in choice.nodes], key=lambda node: node.prompt[0] if node.prompt else "") return cached_nodes def _sorted_menu_comment_nodes(cached_nodes=[]): # Returns a list of menu and comment nodes to search, sorted by prompt, # with the menus first if not cached_nodes: def prompt_text(mc): return mc.prompt[0] cached_nodes += sorted(_kconf.menus, key=prompt_text) cached_nodes += sorted(_kconf.comments, key=prompt_text) return cached_nodes def _load_parents(node): # Menus are lazily populated as they're opened in full-tree mode, but # jumping to an item needs its parent menus to be populated. This function # populates 'node's parents. # Get all parents leading up to 'node', sorted with the root first parents = [] cur = node.parent while cur is not _kconf.top_node: parents.append(cur) cur = cur.parent parents.reverse() for i, parent in enumerate(parents): if not _tree.item(id(parent), "open"): # Found a closed menu. Populate it and all the remaining menus # leading up to 'node'. for parent in parents[i:]: # We only need to populate "real" menus/choices. Implicit menus # are populated when their parents menus are entered. if not isinstance(parent.item, Symbol): _build_full_tree(parent) return def _parent_menu(node): # Returns the menu node of the menu that contains 'node'. In addition to # proper 'menu's, this might also be a 'menuconfig' symbol or a 'choice'. # "Menu" here means a menu in the interface. menu = node.parent while not menu.is_menuconfig: menu = menu.parent return menu def _trace_write(var, fn): # Makes fn() be called whenever the Tkinter Variable 'var' changes value # trace_variable() is deprecated according to the docstring, # which recommends trace_add() if hasattr(var, "trace_add"): var.trace_add("write", fn) else: var.trace_variable("w", fn) def _info_str(node): # Returns information about the menu node 'node' as a string. # # The helper functions are responsible for adding newlines. This allows # them to return "" if they don't want to add any output. if isinstance(node.item, Symbol): sym = node.item return ( _name_info(sym) + _help_info(sym) + _direct_dep_info(sym) + _defaults_info(sym) + _select_imply_info(sym) + _kconfig_def_info(sym) ) if isinstance(node.item, Choice): choice = node.item return ( _name_info(choice) + _help_info(choice) + 'Mode: {}\n\n'.format(choice.str_value) + _choice_syms_info(choice) + _direct_dep_info(choice) + _defaults_info(choice) + _kconfig_def_info(choice) ) # node.item in (MENU, COMMENT) return _kconfig_def_info(node) def _name_info(sc): # Returns a string with the name of the symbol/choice. Choices are shown as # . return (sc.name if sc.name else standard_sc_expr_str(sc)) + "\n\n" def _value_info(sym): # Returns a string showing 'sym's value # Only put quotes around the value for string symbols return "Value: {}\n".format( '"{}"'.format(sym.str_value) if sym.orig_type == STRING else sym.str_value) def _choice_syms_info(choice): # Returns a string listing the choice symbols in 'choice'. Adds # "(selected)" next to the selected one. s = "Choice symbols:\n" for sym in choice.syms: s += " - " + sym.name if sym is choice.selection: s += " (selected)" s += "\n" return s + "\n" def _help_info(sc): # Returns a string with the help text(s) of 'sc' (Symbol or Choice). # Symbols and choices defined in multiple locations can have multiple help # texts. s = "" for node in sc.nodes: if node.help is not None: s += node.help + "\n\n" return s def _direct_dep_info(sc): # Returns a string describing the direct dependencies of 'sc' (Symbol or # Choice). The direct dependencies are the OR of the dependencies from each # definition location. The dependencies at each definition location come # from 'depends on' and dependencies inherited from parent items. return "" if sc.direct_dep is _kconf.y else \ 'Direct dependencies (={}):\n{}\n' \ .format(TRI_TO_STR[expr_value(sc.direct_dep)], _split_expr_info(sc.direct_dep, 2)) def _defaults_info(sc): # Returns a string describing the defaults of 'sc' (Symbol or Choice) if not sc.defaults: return "" s = "Default" if len(sc.defaults) > 1: s += "s" s += ":\n" for val, cond in sc.orig_defaults: s += " - " if isinstance(sc, Symbol): s += _expr_str(val) # Skip the tristate value hint if the expression is just a single # symbol. _expr_str() already shows its value as a string. # # This also avoids showing the tristate value for string/int/hex # defaults, which wouldn't make any sense. if isinstance(val, tuple): s += ' (={})'.format(TRI_TO_STR[expr_value(val)]) else: # Don't print the value next to the symbol name for choice # defaults, as it looks a bit confusing s += val.name s += "\n" if cond is not _kconf.y: s += " Condition (={}):\n{}" \ .format(TRI_TO_STR[expr_value(cond)], _split_expr_info(cond, 4)) return s + "\n" def _split_expr_info(expr, indent): # Returns a string with 'expr' split into its top-level && or || operands, # with one operand per line, together with the operand's value. This is # usually enough to get something readable for long expressions. A fancier # recursive thingy would be possible too. # # indent: # Number of leading spaces to add before the split expression. if len(split_expr(expr, AND)) > 1: split_op = AND op_str = "&&" else: split_op = OR op_str = "||" s = "" for i, term in enumerate(split_expr(expr, split_op)): s += "{}{} {}".format(indent*" ", " " if i == 0 else op_str, _expr_str(term)) # Don't bother showing the value hint if the expression is just a # single symbol. _expr_str() already shows its value. if isinstance(term, tuple): s += " (={})".format(TRI_TO_STR[expr_value(term)]) s += "\n" return s def _select_imply_info(sym): # Returns a string with information about which symbols 'select' or 'imply' # 'sym'. The selecting/implying symbols are grouped according to which # value they select/imply 'sym' to (n/m/y). def sis(expr, val, title): # sis = selects/implies sis = [si for si in split_expr(expr, OR) if expr_value(si) == val] if not sis: return "" res = title for si in sis: res += " - {}\n".format(split_expr(si, AND)[0].name) return res + "\n" s = "" if sym.rev_dep is not _kconf.n: s += sis(sym.rev_dep, 2, "Symbols currently y-selecting this symbol:\n") s += sis(sym.rev_dep, 1, "Symbols currently m-selecting this symbol:\n") s += sis(sym.rev_dep, 0, "Symbols currently n-selecting this symbol (no effect):\n") if sym.weak_rev_dep is not _kconf.n: s += sis(sym.weak_rev_dep, 2, "Symbols currently y-implying this symbol:\n") s += sis(sym.weak_rev_dep, 1, "Symbols currently m-implying this symbol:\n") s += sis(sym.weak_rev_dep, 0, "Symbols currently n-implying this symbol (no effect):\n") return s def _kconfig_def_info(item): # Returns a string with the definition of 'item' in Kconfig syntax, # together with the definition location(s) and their include and menu paths nodes = [item] if isinstance(item, MenuNode) else item.nodes s = "Kconfig definition{}, with parent deps. propagated to 'depends on'\n" \ .format("s" if len(nodes) > 1 else "") s += (len(s) - 1)*"=" for node in nodes: s += "\n\n" \ "At {}:{}\n" \ "{}" \ "Menu path: {}\n\n" \ "{}" \ .format(node.filename, node.linenr, _include_path_info(node), _menu_path_info(node), node.custom_str(_name_and_val_str)) return s def _include_path_info(node): if not node.include_path: # In the top-level Kconfig file return "" return "Included via {}\n".format( " -> ".join("{}:{}".format(filename, linenr) for filename, linenr in node.include_path)) def _menu_path_info(node): # Returns a string describing the menu path leading up to 'node' path = "" while node.parent is not _kconf.top_node: node = node.parent # Promptless choices might appear among the parents. Use # standard_sc_expr_str() for them, so that they show up as # ''. path = " -> " + (node.prompt[0] if node.prompt else standard_sc_expr_str(node.item)) + path return "(Top)" + path def _name_and_val_str(sc): # Custom symbol/choice printer that shows symbol values after symbols # Show the values of non-constant (non-quoted) symbols that don't look like # numbers. Things like 123 are actually symbol references, and only work as # expected due to undefined symbols getting their name as their value. # Showing the symbol value for those isn't helpful though. if isinstance(sc, Symbol) and not sc.is_constant and not _is_num(sc.name): if not sc.nodes: # Undefined symbol reference return "{}(undefined/n)".format(sc.name) return '{}(={})'.format(sc.name, sc.str_value) # For other items, use the standard format return standard_sc_expr_str(sc) def _expr_str(expr): # Custom expression printer that shows symbol values return expr_str(expr, _name_and_val_str) def _is_num(name): # Heuristic to see if a symbol name looks like a number, for nicer output # when printing expressions. Things like 16 are actually symbol names, only # they get their name as their value when the symbol is undefined. try: int(name) except ValueError: if not name.startswith(("0x", "0X")): return False try: int(name, 16) except ValueError: return False return True if __name__ == "__main__": _main() ================================================ FILE: kernel/scripts/Kconfiglib/kconfiglib.py ================================================ # Copyright (c) 2011-2019, Ulf Magnusson # SPDX-License-Identifier: ISC """ Overview ======== Kconfiglib is a Python 2/3 library for scripting and extracting information from Kconfig (https://www.kernel.org/doc/Documentation/kbuild/kconfig-language.txt) configuration systems. See the homepage at https://github.com/ulfalizer/Kconfiglib for a longer overview. Since Kconfiglib 12.0.0, the library version is available in kconfiglib.VERSION, which is a (, , ) tuple, e.g. (12, 0, 0). Using Kconfiglib on the Linux kernel with the Makefile targets ============================================================== For the Linux kernel, a handy interface is provided by the scripts/kconfig/Makefile patch, which can be applied with either 'git am' or the 'patch' utility: $ wget -qO- https://raw.githubusercontent.com/ulfalizer/Kconfiglib/master/makefile.patch | git am $ wget -qO- https://raw.githubusercontent.com/ulfalizer/Kconfiglib/master/makefile.patch | patch -p1 Warning: Not passing -p1 to patch will cause the wrong file to be patched. Please tell me if the patch does not apply. It should be trivial to apply manually, as it's just a block of text that needs to be inserted near the other *conf: targets in scripts/kconfig/Makefile. Look further down for a motivation for the Makefile patch and for instructions on how you can use Kconfiglib without it. If you do not wish to install Kconfiglib via pip, the Makefile patch is set up so that you can also just clone Kconfiglib into the kernel root: $ git clone git://github.com/ulfalizer/Kconfiglib.git $ git am Kconfiglib/makefile.patch (or 'patch -p1 < Kconfiglib/makefile.patch') Warning: The directory name Kconfiglib/ is significant in this case, because it's added to PYTHONPATH by the new targets in makefile.patch. The targets added by the Makefile patch are described in the following sections. make kmenuconfig ---------------- This target runs the curses menuconfig interface with Python 3. As of Kconfiglib 12.2.0, both Python 2 and Python 3 are supported (previously, only Python 3 was supported, so this was a backport). make guiconfig -------------- This target runs the Tkinter menuconfig interface. Both Python 2 and Python 3 are supported. To change the Python interpreter used, pass PYTHONCMD= to 'make'. The default is 'python'. make [ARCH=] iscriptconfig -------------------------------- This target gives an interactive Python prompt where a Kconfig instance has been preloaded and is available in 'kconf'. To change the Python interpreter used, pass PYTHONCMD= to 'make'. The default is 'python'. To get a feel for the API, try evaluating and printing the symbols in kconf.defined_syms, and explore the MenuNode menu tree starting at kconf.top_node by following 'next' and 'list' pointers. The item contained in a menu node is found in MenuNode.item (note that this can be one of the constants kconfiglib.MENU and kconfiglib.COMMENT), and all symbols and choices have a 'nodes' attribute containing their menu nodes (usually only one). Printing a menu node will print its item, in Kconfig format. If you want to look up a symbol by name, use the kconf.syms dictionary. make scriptconfig SCRIPT=