1
1
mirror of https://github.com/NixOS/mobile-nixos.git synced 2024-09-11 03:55:23 +03:00

boot/init: SwitchRoot: prep for stage-0 deprecation

For now, only moves the code to make it obvious which parts are for
stage-0.
This commit is contained in:
Samuel Dionne-Riel 2024-03-06 21:22:49 -05:00
parent cbc35593db
commit 1e06638c17

View File

@ -22,6 +22,137 @@ class Tasks::SwitchRoot < SingletonTask
end
end
# Extracted bits for stage-0 support.
# This should make deprecating and removing easier down the line.
module StageZeroSupport
# Returns whether the current boot will `kexec` into the generation.
def will_kexec?()
# Only stage-0 bootloader-flavoured init will kexec.
return false unless STAGE == 0
# The user wants to use the generation's kernel
return false unless @use_generation_kernel
# AND if we find the required files.
[
"initrd",
"kernel",
"kernel-params",
]
.map { |file| generation_file(file) }
.map { |file| File.exist?(file) }
.all?
end
def kexec_in_generation(selected_generation)
if Tasks.constants.include?(:SetupGadgetMode)
Progress.exec_with_message("Tearing down USB Gadget mode") do
begin
Tasks::SetupGadgetMode.instance.teardown()
rescue => e
$logger.fatal("Caught an error during teardown for kexec...")
$logger.fatal(e.inspect)
end
end
end
log("About to kexec...")
System.run(
"kexec", "--load",
generation_file("kernel"),
"--initrd=#{generation_file("initrd")}",
*[maybe_dtb()].compact(),
"--command-line",
[
["init", selected_generation.path("init", readlink: true)].join("="),
# Flag used to describe we're in a kexec situation.
# For the time being, the flag is the whole string, not the value yes to that key.
"mobile-nixos.kexec=yes",
"mobile-nixos.generation=#{selected_generation.path}",
File.read(generation_file("kernel-params")),
].join(" ")
)
System.exec("kexec", "-e")
# Execution shouldn't reach here, as `exec` replaces this process with `kexec`.
ensure
raise "Failed to kexec into #{selected_generation.path}"
end
# If the system build exports a DTB file name to load, return the appropriate
# command-line argument for `kexec`. Otherwise nil.
def maybe_dtb()
log("Looking for a DTB file...")
mapping_path = generation_file("mobile-nixos/dtb-mapping.json", missing_allowed: true)
unless mapping_path && File.exist?(mapping_path)
log(" DTB: dtb-mapping.json not found... skipping DTB mapping...")
return nil
end
log(" DTB: Mappings from: `#{mapping_path}`...")
mapping = JSON.parse(File.read(mapping_path))
# Work only off the first compatible name.
# It is assumed that the loader uses the exact same scheme as the kernel
# build does. If this assumption stops holding true, it will be a new
# feature to implement.
board_compatible = File.read("/proc/device-tree/compatible").split("\0").first
log(" DTB: board_compatible: #{board_compatible}")
desired_dtb = mapping[board_compatible]
if desired_dtb
log(" DTB: wants: #{desired_dtb}")
else
log(" DTB: no DTB mapping found for #{board_compatible}...")
return nil
end
# The desired_dtb path is an absolute path in the mounted system.
file = File.join(SYSTEM_MOUNT_POINT, desired_dtb)
if File.exist?(file)
log(" DTB: file `#{file}` found")
"--dtb=#{forward_fdt_bootloader_info(file)}"
else
log(" DTB: file `#{file}` not found... skipping DTB mapping")
nil
end
end
# Given a path to a DTB file, merges required properties that the bootloader
# has setup. It will additionally merge optional properties.
def forward_fdt_bootloader_info(path)
args = [
"--print-header",
"--copy-dtb", path,
Configuration["stage-0"]["forward"]["nodes"].map {|path| [ "--forward-node", path] },
Configuration["stage-0"]["forward"]["props"].map {|pair| [ "--forward-prop", *pair] },
].flatten
log(" $ fdt-forward #{args.shelljoin}")
dts = `fdt-forward #{args.shelljoin}`
# Declare we booted using stage-0's kexec
# And additional useful debugging data...
dts << [
"\n",
"// Declare we booted using kexec",
%Q{/ { mobile-nixos,stage-0; };},
%Q{/ { mobile-nixos,stage-0,timestamp = #{Time.now.to_s.to_json}; };},
%Q{/ { mobile-nixos,stage-0,uname = #{`uname -a`.to_json}; };},
%Q{/ { mobile-nixos,stage-0,uptime = #{`uptime`.to_json}; };},
].join("\n")
FileUtils.mkdir_p("/run/boot/")
File.write("/run/boot/fdt.dts", dts)
System.run("fdt-forward --to-dtb < /run/boot/fdt.dts > /run/boot/fdt.dtb")
return "/run/boot/fdt.dtb"
end
end
include StageZeroSupport
# Where the system rootfs will be mounted.
SYSTEM_MOUNT_POINT = "/mnt"
@ -238,95 +369,6 @@ class Tasks::SwitchRoot < SingletonTask
@selected_generation
end
# If the system build exports a DTB file name to load, return the appropriate
# command-line argument for `kexec`. Otherwise nil.
def maybe_dtb()
log("Looking for a DTB file...")
mapping_path = generation_file("mobile-nixos/dtb-mapping.json", missing_allowed: true)
unless mapping_path && File.exist?(mapping_path)
log(" DTB: dtb-mapping.json not found... skipping DTB mapping...")
return nil
end
log(" DTB: Mappings from: `#{mapping_path}`...")
mapping = JSON.parse(File.read(mapping_path))
# Work only off the first compatible name.
# It is assumed that the loader uses the exact same scheme as the kernel
# build does. If this assumption stops holding true, it will be a new
# feature to implement.
board_compatible = File.read("/proc/device-tree/compatible").split("\0").first
log(" DTB: board_compatible: #{board_compatible}")
desired_dtb = mapping[board_compatible]
if desired_dtb
log(" DTB: wants: #{desired_dtb}")
else
log(" DTB: no DTB mapping found for #{board_compatible}...")
return nil
end
# The desired_dtb path is an absolute path in the mounted system.
file = File.join(SYSTEM_MOUNT_POINT, desired_dtb)
if File.exist?(file)
log(" DTB: file `#{file}` found")
"--dtb=#{forward_fdt_bootloader_info(file)}"
else
log(" DTB: file `#{file}` not found... skipping DTB mapping")
nil
end
end
# Given a path to a DTB file, merges required properties that the bootloader
# has setup. It will additionally merge optional properties.
def forward_fdt_bootloader_info(path)
args = [
"--print-header",
"--copy-dtb", path,
Configuration["stage-0"]["forward"]["nodes"].map {|path| [ "--forward-node", path] },
Configuration["stage-0"]["forward"]["props"].map {|pair| [ "--forward-prop", *pair] },
].flatten
log(" $ fdt-forward #{args.shelljoin}")
dts = `fdt-forward #{args.shelljoin}`
# Declare we booted using stage-0's kexec
# And additional useful debugging data...
dts << [
"\n",
"// Declare we booted using kexec",
%Q{/ { mobile-nixos,stage-0; };},
%Q{/ { mobile-nixos,stage-0,timestamp = #{Time.now.to_s.to_json}; };},
%Q{/ { mobile-nixos,stage-0,uname = #{`uname -a`.to_json}; };},
%Q{/ { mobile-nixos,stage-0,uptime = #{`uptime`.to_json}; };},
].join("\n")
FileUtils.mkdir_p("/run/boot/")
File.write("/run/boot/fdt.dts", dts)
System.run("fdt-forward --to-dtb < /run/boot/fdt.dts > /run/boot/fdt.dtb")
return "/run/boot/fdt.dtb"
end
def will_kexec?()
# Only stage-0 bootloader-flavourd init will kexec.
return false unless STAGE == 0
# The user wants to use the generation's kernel
return false unless @use_generation_kernel
# AND if we find the required files.
[
"initrd",
"kernel",
"kernel-params",
]
.map { |file| generation_file(file) }
.map { |file| File.exist?(file) }
.all?
end
def generation_file(name, missing_allowed: false)
begin
selected_generation.mounted_path(name, readlink: true)
@ -359,38 +401,7 @@ class Tasks::SwitchRoot < SingletonTask
log("***")
log("")
if will_kexec?
if Tasks.constants.include?(:SetupGadgetMode)
Progress.exec_with_message("Tearing down USB Gadget mode") do
begin
Tasks::SetupGadgetMode.instance.teardown()
rescue => e
$logger.fatal("Caught an error during teardown for kexec...")
$logger.fatal(e.inspect)
end
end
end
log("About to kexec...")
System.run(
"kexec", "--load",
generation_file("kernel"),
"--initrd=#{generation_file("initrd")}",
*[maybe_dtb()].compact(),
"--command-line",
[
["init", selected_generation.path("init", readlink: true)].join("="),
# Flag used to describe we're in a kexec situation.
# For the time being, the flag is the whole string, not the value yes to that key.
"mobile-nixos.kexec=yes",
"mobile-nixos.generation=#{selected_generation.path}",
File.read(generation_file("kernel-params")),
].join(" ")
)
System.exec("kexec", "-e")
raise "Failed to kexec into #{selected_generation.path}"
end
kexec_in_generation(selected_generation) if will_kexec?
Tasks::UDev.instance.teardown()