diff --git a/.gitignore b/.gitignore index 2780f30..2f62d09 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ .tmp_versions /br2_cli /br2_local +/ignore.sh /rootfs_overlay/etc/init.d/S99 /rootfs_overlay/ignore.sh /9p diff --git a/README.adoc b/README.adoc index 4f437c7..aaf8901 100644 --- a/README.adoc +++ b/README.adoc @@ -139,7 +139,7 @@ The root filesystem is persistent across: .... ./run date >f -sync +# poweroff syncs by default without -n. poweroff .... @@ -150,6 +150,14 @@ then: cat f .... +The command: + +.... +sync +.... + +also saves the disk. + This is particularly useful to re-run shell commands from the history of a previous session with `Ctrl + R`. However, when you do: @@ -240,23 +248,49 @@ root every time. -Instead, you can either run them from a minimal init: +Here are some methods to automate that. + +==== Replace init + +This method replaces init and evals a command from the <>: + +.... +./run -E 'echo "asdf qwer";insmod /hello.ko;/poweroff.out' -n +.... + +It is basically a shortcut for: .... ./run -e 'init=/eval.sh - lkmc_eval="insmod /hello.ko;/poweroff.out"' -n .... -or if the script is large, add it to a gitignored file that will go into the guest: +although `-E` allows for quoting and newlines by using base64 encoding, so you should almost always use it, unless you are really counting each cycle ;-) + +If the script is large, you can add it to a gitignored file and pass that to `-E` as in: .... echo ' insmod /hello.ko /poweroff.out +' > ignore.sh +./run -E "$(cat ignore.sh)" -n +.... + +or add it to a file to the root filesystem guest and rebuild: + +.... +echo '#!/bin/sh +insmod /hello.ko +/poweroff.out ' > rootfs_overlay/ignore.sh +chmod +x rootfs_overlay/ignore.sh +./build ./run -e 'init=/ignore.sh' -n .... -or run them at the end of the BusyBox init, which does things like setting up networking: +==== Run command at the end of BusyBox init + +If you rely on something that BusyBox' init set up for you like networking, this is the way to go: .... ./run -e '- lkmc_eval="insmod /hello.ko;wget -S google.com;poweroff.out;"' @@ -277,6 +311,26 @@ and they will be run automatically before the login prompt. Scripts under `/etc/init.d` are run by `/etc/init.d/rcS`, which gets called by the line `::sysinit:/etc/init.d/rcS` in `/etc/inittab`. +==== The kernel panics despite poweroff + +Just using Busybox' `poweroff` at the end of the `init` does not work and the kernel panics: + +.... +./run -E poweroff +.... + +because BusyBox' `poweroff` tries to do some fancy stuff like killing init, likely to allow userland to shutdown nicely. + +But this fails when we are `init` itself! + +`poweroff` works more brutally and effectively if you add `-f`: + +.... +./run -E 'poweroff -f' +.... + +but why not just use your super simple and effective `/poweroff.out` and be done with it? + === Kernel command line parameters Bootloaders can pass a string as input to the Linux kernel when it is booting to control its behaviour, much like the `execve` system call does to userland processes. @@ -1110,9 +1164,9 @@ wc -l trace-boot.txt gem5: .... -./run -a arm -g -e 'init=/eval.sh - lkmc_eval="m5 exit"' +./run -a arm -g -E 'm5 exit' # Or: -# ./run -a arm -g -e 'init=/eval.sh - lkmc_eval="m5 exit"' -- --cpu-type=HPI --caches +# ./run -a arm -g -E 'm5 exit' -- --cpu-type=HPI --caches grep sim_insts m5out/stats.txt .... @@ -1810,7 +1864,7 @@ Those problems should be insignificant if the benchmark runs for long enough how TODO: even if we don't switch to the detailed CPU model, the cycle counts on the original run and the one with checkpoint restore differ slightly. Why? Multiple checkpoint restores give the same results as expected however: .... -./run -a arm -e 'init=/eval.sh - lkmc_eval="m5 checkpoint;m5 resetstats;dhrystone 1000;m5 exit"' -g +./run -a arm -E 'm5 checkpoint;m5 resetstats;dhrystone 1000;m5 exit' -g ./run -a arm -g -- -r 1 .... @@ -2337,7 +2391,7 @@ contains the `date`. The file `f` wouldn't exist had we used the first checkpoin If you automate things with <> as in: .... -./run -a arm -e 'init=/eval.sh - lkmc_eval="m5 checkpoint;m5 resetstats;dhrystone 1000;m5 exit"' -g +./run -a arm -E 'm5 checkpoint;m5 resetstats;dhrystone 1000;m5 exit' -g .... Then there is no need to pass the kernel command line again to gem5 for replay: diff --git a/busybox_config_fragment b/busybox_config_fragment index 5f2aa54..2080446 100644 --- a/busybox_config_fragment +++ b/busybox_config_fragment @@ -1,3 +1,4 @@ +CONFIG_BASE64=y CONFIG_DEPMOD=y CONFIG_MODINFO=y CONFIG_NC=y diff --git a/kernel_module/user/poweroff.c b/kernel_module/user/poweroff.c index f306573..c91c7e3 100644 --- a/kernel_module/user/poweroff.c +++ b/kernel_module/user/poweroff.c @@ -1,6 +1,6 @@ /* Userspace is for the weak. Die. * https://stackoverflow.com/questions/28812514/how-to-shutdown-linux-using-c-or-qt-without-call-to-system - * BusyBox's /sbin/poweroff is under init/halt.c, but it does extra crap like killing init, so I don't trust it. */ + **/ #include #include diff --git a/rootfs_overlay/eval.sh b/rootfs_overlay/eval.sh index 997b51b..18128ea 100755 --- a/rootfs_overlay/eval.sh +++ b/rootfs_overlay/eval.sh @@ -1,2 +1,18 @@ #!/bin/sh +echo "$lkmc_eval" eval "$lkmc_eval" + +# Ideally, this script would do just: +# +## Get rid of the '-'. +#shift +#echo "$@" +# +# However, the kernel CLI parsing is crap, and the 4.14 docs lie. +# +# In particular, not all that is passed after "-" goes to an argument to init, +# e.g. stuff with dots like "- /poweroff.out" still gets treated specially and +# does not go to init. +# +# This also likely means that the above solution is also unreliable in some cases, +# and that in the end you just have to add a script to the root filesystem. diff --git a/rootfs_overlay/eval_base64.sh b/rootfs_overlay/eval_base64.sh new file mode 100755 index 0000000..c3eddcf --- /dev/null +++ b/rootfs_overlay/eval_base64.sh @@ -0,0 +1,2 @@ +#!/bin/sh +eval "$(printf "$lkmc_eval" | base64 -d)" diff --git a/run b/run index 504445e..e957ac4 100755 --- a/run +++ b/run @@ -17,9 +17,10 @@ extra_flags='' extra_flags_qemu='' gem5=false gem5opts='' +lkmc_eval='' initrd=false root='' -while getopts a:c:Dde:G:giKknt:x OPT; do +while getopts a:c:DdE:e:G:giKknt:x OPT; do case "$OPT" in a) arch="$OPTARG" @@ -33,6 +34,9 @@ while getopts a:c:Dde:G:giKknt:x OPT; do D) debug_vm='gdb -q -ex start --args' ;; + E) + lkmc_eval="$OPTARG" + ;; e) extra_append="$extra_append $OPTARG" ;; @@ -71,6 +75,9 @@ root_dir="$(pwd)" buildroot_dir="${root_dir}/buildroot" out_dir="${root_dir}/buildroot/output.${arch_dir}~" images_dir="${out_dir}/images" +if [ -n "$lkmc_eval" ]; then + extra_append="$extra_append init=/eval_base64.sh - lkmc_eval=\"$(printf "$lkmc_eval" | base64)\"" +fi if "$gem5"; then build_dir="${out_dir}/build/gem5-1.0"