[bootsvc] Mark bootsvc as critical to root job
Set bootsvc as critical to the root job. This will cause the root job to
be terminated if bootsvc terminates.
This CL also:
* Changes the default action for when root job terminates, so that the
system reboots. This seems like the most sensible default for
production.
* Adds the ability to print a notice when the root job terminates. This
allows us to write ZBI tests for this behaviour.
Change-Id: I08658e0bee2e92481fec90dfd1cd14f8f9112277
diff --git a/docs/reference/kernel/kernel_cmdline.md b/docs/reference/kernel/kernel_cmdline.md
index c08cebb..720ae1b 100644
--- a/docs/reference/kernel/kernel_cmdline.md
+++ b/docs/reference/kernel/kernel_cmdline.md
@@ -41,11 +41,6 @@
between the program and individual arguments. For example,
'bootsvc.next=bin/mybin,arg1,arg2'.
-## bootsvc.on_next_process_exit=reboot|shutdown
-
-What bootsvc should do when the "next process" (see
-[bootsvc.next](#bootsvc_next_bootfs-path)) exits. This defaults to 'reboot'.
-
## clock\.backstop=\<seconds\>
Sets the initial offset (from the Unix epoch, in seconds) for the UTC clock.
@@ -372,19 +367,22 @@
The `k oom info` command will show the current value of this and other
parameters.
-## kernel.root-job.reboot=\<bool>
+## kernel.root-job.behavior=\<string>
-This option specifies whether the kernel should reboot the system when the root
-job is either: terminated, or has no jobs and no processes.
+This option specifies what action the kernel should take when the root job is
+either terminated, or has no jobs and no processes. This can take one of the
+following values:
-By default, the kernel will halt the system, stopping any further execution.
+* halt -- Halt the system
+* reboot -- Reboot the system (the default)
+* bootloader -- Reboot into the bootloader
+* recovery -- Reboot into the recovery partition
+* shutdown -- Shut down the system
-## kernel.root-job.shutdown=\<bool>
+## kernel.root-job.notice=\<string>
-This option specifies whether the kernel should shutdown the system when the
-root job is either: terminated, or has no jobs and no processes.
-
-By default, the kernel will halt the system, stopping any further execution.
+The option allows a notice to be printed when the root job is either:
+terminated, or has no jobs and no processes.
## kernel.x86.disable_spec_mitigations=\<bool>
@@ -447,7 +445,7 @@
If false, this option leaves PCI devices running when calling mexec. Defaults
to true.
-## kernel.serial=\<string\>
+## kernel.serial=\<string>
This controls what serial port is used. If provided, it overrides the serial
port described by the system's bootdata. The kernel debug serial port is
diff --git a/zircon/kernel/object/glue.cc b/zircon/kernel/object/glue.cc
index 092f355..f3f28a6 100644
--- a/zircon/kernel/object/glue.cc
+++ b/zircon/kernel/object/glue.cc
@@ -12,6 +12,7 @@
#include <inttypes.h>
#include <lib/cmdline.h>
#include <lib/crashlog.h>
+#include <lib/debuglog.h>
#include <zircon/syscalls/object.h>
#include <zircon/types.h>
@@ -52,18 +53,40 @@
if (HasChild(state)) {
return 0;
}
- if (gCmdline.GetBool("kernel.root-job.reboot", false)) {
- printf("root-job: rebooting\n");
- platform_halt(HALT_ACTION_REBOOT, HALT_REASON_SW_RESET);
- } else if (gCmdline.GetBool("kernel.root-job.shutdown", true)) {
- printf("root-job: shutting down\n");
- platform_halt(HALT_ACTION_SHUTDOWN, HALT_REASON_SW_RESET);
- } else {
- printf("root-job: halting\n");
- platform_halt(HALT_ACTION_HALT, HALT_REASON_SW_RESET);
- }
+ // We may be in an interrupt context, e.g. thread_process_pending_signals(),
+ // so we schedule a DPC.
+ dpc_queue(&dpc_, true);
return kNeedRemoval;
}
+
+ static void Halt(dpc_t* dpc) {
+ const char* notice = gCmdline.GetString("kernel.root-job.notice");
+ if (notice != nullptr) {
+ printf("root-job: notice: %s\n", notice);
+ }
+
+ const char* behavior = gCmdline.GetString("kernel.root-job.behavior");
+ if (behavior == nullptr) {
+ behavior = "reboot";
+ }
+
+ printf("root-job: taking %s action\n", behavior);
+ dlog_shutdown();
+
+ if (!strcmp(behavior, "halt")) {
+ platform_halt(HALT_ACTION_HALT, HALT_REASON_SW_RESET);
+ } else if (!strcmp(behavior, "bootloader")) {
+ platform_halt(HALT_ACTION_REBOOT_BOOTLOADER, HALT_REASON_SW_RESET);
+ } else if (!strcmp(behavior, "recovery")) {
+ platform_halt(HALT_ACTION_REBOOT_RECOVERY, HALT_REASON_SW_RESET);
+ } else if (!strcmp(behavior, "shutdown")) {
+ platform_halt(HALT_ACTION_SHUTDOWN, HALT_REASON_SW_RESET);
+ } else {
+ platform_halt(HALT_ACTION_REBOOT, HALT_REASON_SW_RESET);
+ }
+ }
+
+ dpc_t dpc_{LIST_INITIAL_CLEARED_VALUE, &Halt, nullptr};
};
static ktl::unique_ptr<RootJobObserver> root_job_observer;
diff --git a/zircon/public/gn/test/zbi_test.gni b/zircon/public/gn/test/zbi_test.gni
index e4f9854..917882d 100644
--- a/zircon/public/gn/test/zbi_test.gni
+++ b/zircon/public/gn/test/zbi_test.gni
@@ -12,7 +12,7 @@
# zero, but shutting down. This string includes some random data that
# shouldn't appear elsewhere, to avoid false-positive matches.
zbi_test_success_string =
- "*** ZBI test successful! MDd7/O65SuVZ23yGAaQG4CedYQGH9E1/58r73pSAVK0= ***"
+ "***ZBI-test-successful!-MDd7/O65SuVZ23yGAaQG4CedYQGH9E1/58r73pSAVK0=***"
# Build a ZBI file to be run as a standalone ZBI test.
#
diff --git a/zircon/system/core/bootsvc/main.cc b/zircon/system/core/bootsvc/main.cc
index a054abc..6b9d1ad 100644
--- a/zircon/system/core/bootsvc/main.cc
+++ b/zircon/system/core/bootsvc/main.cc
@@ -159,13 +159,13 @@
// - fuchsia.boot.RootJob, which provides root job handles
// - fuchsia.boot.RootResource, which provides root resource handles
// - A loader that can load libraries from /boot, hosted by bootsvc
-// If set to true `shutdown` will cause the system to power off when the next
-// process exits. If set to false, the system will reboot insted of powering
-// off.
+//
+// If the next process terminates, bootsvc will quit.
void LaunchNextProcess(fbl::RefPtr<bootsvc::BootfsService> bootfs,
fbl::RefPtr<bootsvc::SvcfsService> svcfs,
- fbl::RefPtr<bootsvc::BootfsLoaderService> loader_svc, bool shutdown,
- const zx::resource& root_rsrc, const zx::debuglog& log) {
+ fbl::RefPtr<bootsvc::BootfsLoaderService> loader_svc,
+ const zx::resource& root_rsrc, const zx::debuglog& log,
+ async::Loop& loop) {
const char* bootsvc_next = getenv("bootsvc.next");
if (bootsvc_next == nullptr) {
bootsvc_next =
@@ -255,23 +255,24 @@
printf("bootsvc: Launched %s\n", next_program);
// wait for termination and then reboot or power off the system
- zx_signals_t observed = ZX_SIGNAL_NONE;
+ zx_signals_t observed;
zx_status_t termination_result =
proc_handle.wait_one(ZX_TASK_TERMINATED, zx::time::basic_time::infinite(), &observed);
if (termination_result != ZX_OK) {
printf("bootsvc: failure waiting for next process termination %i\n", termination_result);
- return;
}
- if (shutdown) {
- zx_system_powerctl(root_rsrc.get(), ZX_SYSTEM_POWERCTL_SHUTDOWN, NULL);
- } else {
- zx_system_powerctl(root_rsrc.get(), ZX_SYSTEM_POWERCTL_REBOOT, NULL);
- }
+
+ // If the next process terminated, quit the main loop.
+ loop.Quit();
}
} // namespace
int main(int argc, char** argv) {
+ // Close the loader-service channel so the service can go away.
+ // We won't use it any more (no dlopen calls in this process).
+ zx_handle_close(dl_set_loader_service(ZX_HANDLE_INVALID));
+
// NOTE: This will be the only source of zx::debuglog in the system.
// Eventually, we will receive this through a startup handle from userboot.
zx::debuglog log;
@@ -283,9 +284,9 @@
printf("bootsvc: Starting...\n");
- // Close the loader-service channel so the service can go away.
- // We won't use it any more (no dlopen calls in this process).
- zx_handle_close(dl_set_loader_service(ZX_HANDLE_INVALID));
+ status = zx::job::default_job()->set_critical(0, *zx::process::self());
+ ZX_ASSERT_MSG(status == ZX_OK, "Failed to set bootsvc as critical to root-job: %s\n",
+ zx_status_get_string(status));
async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread);
@@ -299,7 +300,7 @@
ZX_ASSERT_MSG(status == ZX_OK, "BootfsService creation failed: %s\n",
zx_status_get_string(status));
status = bootfs_svc->AddBootfs(std::move(bootfs_vmo));
- ZX_ASSERT_MSG(status == ZX_OK, "bootfs add failed: %s\n", zx_status_get_string(status));
+ ZX_ASSERT_MSG(status == ZX_OK, "Bootfs add failed: %s\n", zx_status_get_string(status));
// Process the ZBI boot image
printf("bootsvc: Retrieving boot image...\n");
@@ -351,24 +352,16 @@
ZX_ASSERT_MSG(status == ZX_OK, "BootfsLoaderService creation failed: %s\n",
zx_status_get_string(status));
- const char* suspend_behavior = getenv("bootsvc.on_next_process_exit");
- bool shutdown = false;
- if (suspend_behavior != nullptr) {
- if (strlen(suspend_behavior) == 8 && strcmp("shutdown", suspend_behavior) == 0) {
- shutdown = true;
- }
- }
-
// Launch the next process in the chain. This must be in a thread, since
// it may issue requests to the loader, which runs in the async loop that
// starts running after this.
printf("bootsvc: Launching next process...\n");
- std::thread(LaunchNextProcess, bootfs_svc, svcfs_svc, loader_svc, shutdown,
- std::cref(root_resource), std::cref(log))
+ std::thread(LaunchNextProcess, bootfs_svc, svcfs_svc, loader_svc,
+ std::cref(root_resource), std::cref(log), std::ref(loop))
.detach();
// Begin serving the bootfs fileystem and loader
loop.Run();
- printf("bootsvc: exiting\n");
+ printf("bootsvc: Exiting\n");
return 0;
}
diff --git a/zircon/system/core/bootsvc/test/BUILD.gn b/zircon/system/core/bootsvc/test/BUILD.gn
index 4b37a56..ad644f6e 100644
--- a/zircon/system/core/bootsvc/test/BUILD.gn
+++ b/zircon/system/core/bootsvc/test/BUILD.gn
@@ -8,6 +8,7 @@
testonly = true
deps = [
":bootsvc-integration-tests",
+ ":bootsvc-root-job-test",
":bootsvc-unit-test",
]
}
@@ -117,6 +118,7 @@
args = [
"userboot=bin/bootsvc",
"bootsvc.next=bin/bootsvc-integration-test,testargument",
+ "kernel.root-job.behavior=shutdown",
]
deps = [
# We need a zircon kernel to get off the ground at all.
@@ -138,3 +140,25 @@
output_dir = root_build_dir
output_name = "$target_name-$current_cpu"
}
+
+zbi_test("bootsvc-root-job-test") {
+ args = [
+ "userboot=bin/bootsvc",
+ "bootsvc.next=test/core/zbi-child-test",
+ "kernel.root-job.behavior=shutdown",
+ "kernel.root-job.notice=$zbi_test_success_string",
+ ]
+ deps = [
+ # We need a zircon kernel to get off the ground at all.
+ "$zx/kernel",
+
+ # Include bootsvc itself, since that's what we're testing here. Note
+ # that this uses the package() target for bootsvc, which comes with its
+ # own data_deps to exercise the `userboot.root` option to find bootsvc
+ # and its libraries inside a package directory in the BOOTFS.
+ "..",
+
+ # Include the integration test binary, which bootsvc will launch.
+ "$zx/system/utest/mexec:zbi-child",
+ ]
+}