From 9b9ebaa6af744fd2c0dc83b5b70d92015c958473 Mon Sep 17 00:00:00 2001 From: Jonathon Hall Date: Thu, 16 Jan 2025 09:26:25 -0500 Subject: [PATCH] doc: Start documenting Heads logging and configuration variables Signed-off-by: Jonathon Hall --- bin/find_undocumented_config.sh | 47 +++++++++ doc/config.md | 160 +++++++++++++++++++++++++++++ doc/logging.md | 177 ++++++++++++++++++++++++++++++++ 3 files changed, 384 insertions(+) create mode 100755 bin/find_undocumented_config.sh create mode 100644 doc/config.md create mode 100644 doc/logging.md diff --git a/bin/find_undocumented_config.sh b/bin/find_undocumented_config.sh new file mode 100755 index 000000000..d33637189 --- /dev/null +++ b/bin/find_undocumented_config.sh @@ -0,0 +1,47 @@ +#! /usr/bin/env bash + +set -eo pipefail + +cd "$(dirname "${BASH_SOURCE[0]}")/.." + +# By default, just show the variables. Invoke with --show-files to show where +# each undocumented variable appears (up to 3 occurrences) +SHOW_FILES= +if [ "$1" = --show-files ]; then + SHOW_FILES=y +fi + +# Don't search the entire repo, we only want config variables used by Heads: +# - config and patches contain lots of CONFIG_ variables from other projects, +# ignore them +# - build/crossgcc/packages are all build outputs and will also contain lots of +# other projects, ignore them +# - modules files are mostly relevant (many do define CONFIG_ variables to +# tweak the module), but a few have several variables actually from the +# project being configured, not used by Heads. Exclude specific files only +# +# boards, initrd, Makefile, and modules cover all Heads variables pretty well +# without introducing many false positives. +GREP_VARS=(-EroIh '\bCONFIG_[A-Za-z0-9_]+') +EXCLUDE_MODULES=" +flashrom +flashprog +coreboot +" +ALL_VARS="$(grep "${GREP_VARS[@]}" boards initrd Makefile)" +ALL_VARS+="$(grep --exclude-from=<(echo "${EXCLUDE_MODULES[@]}") "${GREP_VARS[@]}" modules)" + +ALL_VARS="$(echo "$ALL_VARS" | sort | uniq)" + +# Check each variable to see if it's already documented +while IFS= read -r var; do + if ! grep -Eq "\b$var\b" doc/config.md; then + if [ "$SHOW_FILES" = y ]; then + echo + echo "$var" + grep -r "$var" boards initrd Makefile modules | head -3 || true + else + echo "$var" + fi + fi +done < <(echo "$ALL_VARS") diff --git a/doc/config.md b/doc/config.md new file mode 100644 index 000000000..223ea8043 --- /dev/null +++ b/doc/config.md @@ -0,0 +1,160 @@ +# Heads Configuration Variables + +Heads contains a number of configuration variables. + +All variables can be set at build time. +(Variables used only at runtime can still be set at build time, this changes the default runtime setting.) +However some variables can _only_ be set at build time, they cannot be changed later. + +## User Settings + +These variables are explicit user settings managed via the Heads menus. +Setting any of these at build time sets the default setting. + +| Variable | Purpose | +|---|---| +| CONFIG_AUTO_BOOT_TIMEOUT | Whether to boot automatically, and how long to wait if so. Empty disables automatic boo. A positive integer is the number of seconds to wait before booting automatically. | +| CONFIG_AUTOMATIC_POWERON | Whether to power on automatically after power loss. Only available if board provides CONFIG_SUPPORT_AUTOMATIC_POWERON. | +| CONFIG_BASIC | 'Basic' mode - no tamper evident boot. | +| CONFIG_BASIC_NO_AUTOMATIC_DEFAULT | In Basic mode: By default, Basic mode detects the default boot option during boot, so it does not need to be updated when the OS boot options change. Enabling this setting uses a manually-specified boot option instead. | +| CONFIG_BASIC_USB_AUTOBOOT | In Basic mode: Causes Heads to boot to a bootable USB flash drive by default if inserted. Allows headless systems to perform OS recovery using appropriate bootable images designed for network recovery. | + +:point_right: TODO: document these: + +``` +CONFIG_BOOT_DEV +CONFIG_DEBUG_OUTPUT +CONFIG_ENABLE_FUNCTION_TRACING_OUTPUT +CONFIG_FINALIZE_PLATFORM_LOCKING +CONFIG_RESTRICTED_BOOT +CONFIG_ROOT_CHECK_AT_BOOT +CONFIG_ROOT_DEV +CONFIG_ROOT_DIRLIST +CONFIG_USE_BLOB_JAIL +CONFIG_USER_USB_KEYBOARD +``` + +## Build configuration + +These variables are configure the firmware build. +Many are also available at runtime. +These are not intended to be changed in user config. + +| Variable | Purpose | +|---|---| +| CONFIG_BOARD | Internal name of the board being built. Avoid testing this for specific boards in initrd/, instead add a customization point and override it with boards//initrd/bin/. (For example, boards/librem_mini_v2/initrd/bin/board-init.sh.) | +| CONFIG_BOARD_NAME | Display name of the board being built. Use this to show the board name to the user. | +| CONFIG_BRAND_NAME | Brand name to use to refer to the firmware itself. Upstream, this is "Heads". For example, "Heads main menu", "Enable Heads debug tracing", etc. Distributions can override this to their specific brand name (usually in site-local/config). | + +## Feature support + +These variables enable features that can be controlled by the user. +Usually, they require some board-specific support. +These are not intended to be changed in user config. + +| Variable | Purpose | +|---|---| +| CONFIG_REQUIRE_USB_KEYBOARD | Board must always have USB input support, there is no other input method. This hides the USB keyboard support setting from the config GUI, and CONFIG_USER_USB_KEYBOARD is ignored. | +| CONFIG_SUPPORT_AUTOMATIC_POWERON | Board supports powering on automatically after power loss. The board must provide /bin/set_ec_poweron.sh to control this setting. User can set CONFIG_AUTOMATIC_POWERON from the config GUI. | +| CONFIG_SUPPORT_BLOB_JAIL | Board supports the firmware blob jail to provide nonfree device firmware to the OS kernel. The board must provide relevant device firmware. User can set CONFIG_USE_BLOB_JAIL from the config GUI. | + +## Module Variables + +These variables enable modules or functions of modules, usually adding output from that module to the initrd. +These are not intended to be changed in user config. +(A few might work when overridden to 'n', but this is not intentionally supported.) + +| Variable | Purpose | +|---|---| +| CONFIG_BASH | Bash shell. Most of Heads requires this. | +| CONFIG_BUSYBOX | BusyBox userspace tools. Alternative is CONFIG_UROOT | +| CONFIG_CAIRO | Cairo libraries, needed by fbwhiptail. | +| CONFIG_COREBOOT | coreboot is the base firmware that loads Heads. Alternative is CONFIG_LINUXBOOT | +| CONFIG_CRYPTSETUP2 | cryptsetup2 tools (used for LUKS) | +| CONFIG_DROPBEAR | DropBear SSH server (for debug / troubleshooting) | +| CONFIG_FBWHIPTAIL | fbwhiptail, framebuffer-based graphical whiptail implementation. Alternative is CONFIG_NEWT | + +:point_right: TODO: document these: + +``` +CONFIG_CRYPTSETUP +CONFIG_EXFATPROGS +CONFIG_FLASHPROG +CONFIG_FLASHPROG_AST1100 +CONFIG_FLASHROM +CONFIG_FLASHTOOLS +CONFIG_FROTZ +CONFIG_GPG2 +CONFIG_HOTPKEY +CONFIG_IO386 +CONFIG_IOPORT +CONFIG_KBD +CONFIG_KBD_DEVTOOLS +CONFIG_KBD_EXTRATOOLS +CONFIG_KBD_LOADKEYS +CONFIG_KEXEC +CONFIG_LINUXBOOT +CONFIG_LINUX_AHCI +CONFIG_LINUX_ATA +CONFIG_LINUX_BCM +CONFIG_LINUX_BUNDLED +CONFIG_LINUX_COMMAND_LINE +CONFIG_LINUX_CONFIG +CONFIG_LINUX_E1000 +CONFIG_LINUX_E1000E +CONFIG_LINUX_IGB +CONFIG_LINUX_MEGARAID +CONFIG_LINUX_MEI +CONFIG_LINUX_MLX4 +CONFIG_LINUX_NVME +CONFIG_LINUX_SCSI_GDTH +CONFIG_LINUX_SFC +CONFIG_LINUX_USB +CONFIG_LINUX_USB_COMPANION_CONTROLLER +CONFIG_LINUX_VERSION +CONFIG_LVM2 +CONFIG_MBEDTLS +CONFIG_MSRTOOLS +CONFIG_MUSL +CONFIG_NEWT +CONFIG_NKSTORECLI +CONFIG_OPENSSL +CONFIG_PCIUTILS +CONFIG_POWERPC_UTILS +CONFIG_PURISM_BLOBS +CONFIG_QRENCODE +CONFIG_SLANG +CONFIG_SYSCTL +CONFIG_TPM2_TOOLS +CONFIG_TPM2_TSS +CONFIG_UROOT +CONFIG_UTIL_LINUX +CONFIG_ZLIB +CONFIG_ZSTD +``` + +## Historical + +These variables are no longer used, except possibly in a migration for older settings. +Remember that these could still exist in user configs, so avoid reusing the name for a future variable. + +| Variable | Purpose | +|---|---| +| CONFIG_PUREBOOT_BASIC | Migrated to CONFIG_BASIC. | +| CONFIG_SUPPORT_USB_KEYBOARD | All builds now include USB keyboard support. | +| CONFIG_USB_KEYBOARD | This was a build-time setting when USB keyboard support could only be enabled at build time. When this became a runtime setting, the existing variable name was not reused to avoid confusing older firmware if a user would downgrade. (CONFIG_USER_USB_KEYBOARD is the user-controlled setting, CONFIG_REQUIRE_USB_KEYBOARD indicates that a board requires USB keyboard all the time.) | + +# Updating this document + +Use `bin/find_undocumented_config.sh` to find CONFIG_ variables that haven't been documented yet. +It has some exclusions to avoid lots of false matches against subproject configs, etc., see the script implementation. + + diff --git a/doc/logging.md b/doc/logging.md new file mode 100644 index 000000000..034738706 --- /dev/null +++ b/doc/logging.md @@ -0,0 +1,177 @@ +# Heads Debug Logging + +Heads produces debug logging to aid development and troubleshooting. + +Logging is produced in scripts at a _log level_. +Users can set an _output level_ that controls how much output they see on the screen. + +# Log Levels + +In order from "most verbose" to "least verbose": + +LOG > TRACE > DEBUG > INFO > console > warn + +("console" level output is historical and should be replaced with INFO.) + +## LOG + +LOG is for very detailed output or output with uncontrolled length. +It never goes to the screen, this always goes to the log file. +Usually, we dump outputs of commands like 'lsblk', 'lsusb', 'gpg --list-keys', etc. at LOG level (using DO_WITH_DEBUG or SINK_LOG), so we can tell the state of the system from a log submitted by a user. +We rarely want these on the console as they usually hide more relevant output with information that we already know. + +Use this in situations like: +* Dumping information about the state of the system for debugging. The output doesn't indicate any specific action/decision in Heads or a problem, it's just state relevant for troubleshooting the rest of the log. +* Tracing something that might be very long (including "we don't know how long this will be", even if it's sometimes short). Very long output isn't useful on the console, since you can't scroll back, and it hides more important information. +* The output is intended for debugging a specific topic, and usually unintersting otherwise. We want to be able to turn up output to DEBUG/TRACE when working on any topic without excessively filling the console with every topic's detailed output. + +## TRACE + +TRACE is for following execution flow through Heads. +(TRACE_FUNC logs the current source location at TRACE level, you can use this when entering a function or script, this is much more common than using TRACE directly.) + +You can also use TRACE to show parameter values to scripts or functions. +Since TRACE is for execution flow, show the unprocessed parameters as provided by the caller, not an interpreted version. +(This is uncommon though as it is very verbose, and we can also capture interesting call sites with DO_WITH_DEBUG.) + +You can invoke TRACE to show specific execution flow when needed, but if you are tracing the result of a decision, consider using DEBUG instead. + +Use this in situations like: +* Following control flow - use TRACE_FUNC when entering a script or function +* Showing the parameters used to invoke a script/function, when they are especially relevant and not excessively verbose + +## DEBUG + +DEBUG is for most log information that is relevant if you are a Heads developer. + +Use DEBUG to highlight the decisions made in script logic, and the information that affects those decisions. +Generally, focus on decision points (if, else, case, while, for, etc.), because we can keep following straight-line execution without further tracing. + +Decision points usually capture program behavior the best. +Show the information that is about to influence a decision (`DEBUG "Found ${#DEVS[@]} block devices: to check for LUKS:" "${DEVS[@]}"`) and/or the results of the decision (`DEBUG "${DEVS[$i]} is not a LUKS device, ignore it`). + +Use DO_WITH_DEBUG to capture a particular command execution to the debug log. +The command and its arguments are captured at DEBUG level (as they usually indicate the decisions the command will make), and the command's stdout/stderr are captured at LOG level. +See DO_WITH_DEBUG for examples of usage. + +Use this in situations like: + +* Showing information derived or obtained that will influence logical decisions and actions +* Showing the result of decisions and the reasons for them + +## INFO + +INFO is for contextual information that may be of interest to end users, but that is not required for use of Heads. +Users can control whether this is displayed on the console. + +Users might use this to troubleshoot Heads configuration or behavior, but this should not require knowledge of Heads implementation or developer experience. + +For example: + +* "Why can't I enable USB keyboard support?" `INFO "Not showing USB keyboard option, USB keyboard is always enabled for this board"` +* "Why isn't Heads booting automatically?" `INFO "Not booting automatically, automatic boot is disabled in user settings"` +* "Why didn't Heads prompt me for a password?" `INFO "Password has not been changed, using default"`) + +These do not include highly technical details. +They can include configuration values or context, _but_ they should refer to configuration settings using the user-facing names in the configuration menus. + +Use this in situations like: + +* Showing very high level decision-making information, which is reasonably understandable for users not familiar with Heads implementation +* Explaining a behavior that could reasonably be unexpected for some users + +## console + +This level is historical, use INFO for this. +It is documented as there are still some occurrences in Heads, usually `echo`, `echo >&2`, or `echo >/dev/console`, each intended to produce output directly on the console. +The intent is the same as INFO. + +(This is different from `echo` used to produce output that might be captured by a caller, which is not logging at all.) + +Avoid using this, and change existing console output to INFO or another level. + +## warn + +warn is for output that indicates a problem. We think the user should act on it, but we are able to continue, possibly with degraded functionality. +(This level and the utility function are lowercase, as they predate the other levels.) + +This is apppriate when _all_ of the following are true: + +- there is a likely problem +- we are able to continue, possibly with degraded functionality +- there is a reasonable change that could silence the warning if this is intentional + +**Do not overuse this.** Overuse of this level causes user to become accustomed to ignoring warnings. +This level only has value as long as it does not occur frequently, so users will notice warnings. + +Warnings must indicate a _likely_ problem. +(Not a rare or remote possibility of a problem.) + +Warnings are only appropriate if we're able to continue operating. +If we can't, consider prompting the user instead, since we cannot do what they asked. + +Warnings are only appropriate if there is a reasonable change the user can make to avoid the warning. + +For example: +* Warning when using default passphrases that are completely insecure is reasonable - the user has no security, and if they want that, they should use Basic mode. +* Warning when an unknown variable appears in config.user is not reasonable - there's no reasonable way for the user to address this. + +# Output Levels + +Users can choose one of three output levels for extra console information. + +* None - Show no extra output. Only warnings appear on console. (Some 'console' level output appears that has not been addressed yet.) +* Info - Show information about operations in Heads. (INFO and below.) +* Debug - Show detailed information suitable for debugging Heads. (TRACE and below.) Log file captures all levels. + +TODO: Document what happens for kernel messages too. +This is more complex though since it is influenced by the board's config and user config differently (maybe we should improve that.) + +TODO: Document the variables that control these levels + +## None - no extra output + +| Sink | LOG | TRACE | DEBUG | INFO | console | warn | +|-------------------------|-----|-------|-------|------|---------|------| +| Console (via /dev/kmsg) | | | | | Yes* | Yes | +| /tmp/debug.log | | | | | | | + +* Most 'console' output should be changed to INFO, that content isn't intended to be displayed in quiet mode + +No extra output is specified with: + +``` +CONFIG_DEBUG_OUTPUT=n +CONFIG_ENABLE_FUNCTION_TRACING_OUTPUT=n +CONFIG_QUIET_MODE=y +``` + +## Info + +| Sink | LOG | TRACE | DEBUG | INFO | console | warn | +|-------------------------|-----|-------|-------|------|---------|------| +| Console (via /dev/kmsg) | | | | Yes | Yes | Yes | +| /tmp/debug.log | | | | | | | + +Info output is enabled with: + +``` +CONFIG_DEBUG_OUTPUT=n +CONFIG_ENABLE_FUNCTION_TRACING_OUTPUT=n +CONFIG_QUIET_MODE=n +``` + +## Debug + +| Sink | LOG | TRACE | DEBUG | INFO | console | warn | +|-------------------------|-----|-------|-------|------|---------|------| +| Console (via /dev/kmsg) | | Yes | Yes | Yes | Yes | Yes | +| /tmp/debug.log | Yes | Yes | Yes | Yes | Yes | Yes | + +Debug output is enabled with: + +``` +CONFIG_DEBUG_OUTPUT=y +CONFIG_ENABLE_FUNCTION_TRACING_OUTPUT=y +CONFIG_QUIET_MODE=n +```