I wrote most of this blog entry about a week ago.
When something on the Gumstix starts the sequence of events that should eventually lead to the gumstix being turned off, the Gumstix needs to send a message to the Gumsense telling it to terminate its power. Investigation follows…
Power Down Procedure
When something in userspace decides that it wants to powerdown, it will usually run “poweroff”, or perhaps sometimes run `
init 0` (there is at least one more option: it can run shutdown. My father insists on running
shutdown -i 0 -g 0 -y). All of these methods will inevitably result in the
halt program being executed.
man halt describes the halt command like so:
“Halt notes that the system is being brought down in the file /var/log/wtmp, and then either tells the kernel to halt, reboot or poweroff the system.”
All of the above is how a non-embedded system does it. The gumstix uses busybox, which provides both poweroff and halt. On the Gumstix, all of the busybox commands are linked into the same executable (I’m not sure if this has to always be the case) and all of the commands provided by busybox are symlinks to that one file, /bin/busybox.
So, I’ve got a couple of options:
- Replace the halt executable with my own.
- Edit the kernel so that it sends the command to turn off the power rail to the Gumsense
The first of those two is perhaps the easiest to perform. However, there are a couple of other things that need to be considered before this can be done:
- If I run poweroff, will /sbin/halt actually get invoked? (Remember that poweroff is also a symlink to busybox).
- Is it completely safe to immediately turn off the rail at that point? Does the kernel, or halt, perform some filesystem syncing after the call to halt?
I tried replacing the /sbin/halt busybox symlink with a shell script that would display “I LIKE BEES” when executed. This did not appear in the output when I shut the gumstix down. So off I go to investigate the busybox code…
There isn’t a one-to-one mapping of command-line command to busybox function, it’s a many-to-one mapping. The commands
poweroff all get piped through to
halt_main() found in
init/halt.c of the busybox source. This function then does one of two things depending upon whether busybox is configured to create init. If there is no init, then it calls the C library’s
reboot() function. In the case of the Gumstix, that’s in uclibc. The implementation of
reboot() can be found in
/libc/sysdeps/linux/common/reboot.c (prototype in
/include/sys/reboot.h – hence the
#include <sys/reboot.h> in the busybox code).
The uclibc reboot function is a wrapper around the kernel’s _reboot syscall. The syscall requires some magic numbers passing to it, which are declared in
include/linux/reboot.h of the kernel source. These magic numbers are presumably implemented so that it’s more difficult to execute mashed up code and reboot/halt the machine. The reboot call takes a
flags argument, which indicates what sort of power switch we want to head for. These are described in Linux’s
/* * Commands accepted by the _reboot() system call. * * RESTART Restart system using default command and mode. * HALT Stop OS and give system control to ROM monitor, if any. * CAD_ON Ctrl-Alt-Del sequence causes RESTART command. * CAD_OFF Ctrl-Alt-Del sequence sends SIGINT to init task. * POWER_OFF Stop OS and remove all power from system, if possible. * RESTART2 Restart system using given command string. * SW_SUSPEND Suspend system using software suspend if compiled in. * KEXEC Restart system using a previously loaded Linux kernel */
However, on the gumstix, busybox does provide init. Therefore, the busybox
halt_main() code sends a kill signal to init. The signal it sends varies depending what’s happening:
- Halt: SIGUSR1
- Power Off: SIGUSR2
- Reboot: SIGTERM
Now I’m surprised that there’s a difference between halt and power off. My first guess would be that “power off” means turn off nicely, whilst “halt” means turn off immediately no matter what. (However, after more inspection it appears that “halt” means go into an infinite loop).
So, into the init process code.
include/applets.h of busybox, indicates that the init “applet” function is
init_main(). This can be found in
init/init.c. Almost immediately, this function sets up the signal handlers for the init process. The lines of importance are:
signal(SIGUSR1, halt_signal); signal(SIGUSR2, halt_signal); signal(SIGTERM, reboot_signal);
init_reboot() after 2 seconds (note: this could/should be removed to reduce energy usage) and calling
sync(). The 2 seconds is apparently so that the reboot message can reach the serial console.
init_reboot() calls the libc
reboot() function, as discussed above.
In passing, I noticed that the kernel has two functions of interest:
register_reboot_notifier(struct notifier_block *) and
unregister_reboot_notifier(struct notifier_block *). Both worth knowing about, but probably not exactly what I’m after in this situation.
Kernel reboot routines
So now I need to know what the kernel does when the reboot syscall is made.
/arch/arm/kernel/calls.S contains a table of syscalls. This gets included three times into
entry-common.S, which redefines the
CALL() macro so that the separate includes do different things. One of those includes performs a count of the syscalls, the other two actually create tables of the function addresses. I’m interested in syscall 88, reboot. The entry in
calls.S that’s relevant is:
sys_reboot is a function declared in
/kernel/sys.c. It checks the magic numbers, and then does different things based on the flags it’s passed. I didn’t note down which flags we’re interested, so going back to
reboot_signal() in busybox’s
/init/init.c, I want RB_POWER_OFF, RB_HALT_SYSTEM and RB_AUTOBOOT. Those constants are declared at the top of
init.c. Strangely, they’re not declared using the constants from the kernel headers. The mapping is
LINUX_REBOOT_CMD_RESTART respectively. So,the mapping, with the command line is:
RB_AUTOBOOTends up with
LINUX_REBOOT_CMD_RESTARTin the kernel.
- Power Off:
RB_POWER_OFFends up as
RB_HALT_SYSTEMends up as
That’s the end of the first part of my shutdown investigation.
Just in case it’s useful at some point, the output of the gumstix during shutdown (ignoring networking related error messages) is:
Stopping sshd: OK The system is going down NOW !! Sending SIGTERM to all processes. Sending SIGKILL to all processes. The system is halted. System halted.
I’ve just been using the gumstix with it’s wifi card as an access point. I’ve seen the current consumption go up to 380 mA. That’s (hopefully) a factor of 105 higher than the sleep current.
Site by Rob Gilton. © 2008 - 2019