xGoat
More tea please.

Powering Down: Part 1

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.

The Plan

So, I’ve got a couple of options:

  1. Replace the halt executable with my own.
  2. 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:

Answers

Replacing 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 halt, reboot and 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 reboot.h:

/*
 * 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:

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);

reboot_signal() calls 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:

		CALL(sys_reboot)

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 halt_signal() and 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_POWER_OFF, LINUX_REBOOT_CMD_HALT and LINUX_REBOOT_CMD_RESTART respectively. So,the mapping, with the command line is:

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.

Posted at 7:21 pm on Saturday 14th April 2007

Comments are closed.

Site by Rob Gilton. © 2008 - 2019