metropolis/node: move misplaced packages out of core

abloader, bios_bootcode and minit don't run as part of the core process,
so it doesn't make sense to have them in //metropolis/node/core.
This changes moves these three to //metropolis/node.

Change-Id: I908efb311a138f07a9f1de8e3c23437ff00131ee
Reviewed-on: https://review.monogon.dev/c/monogon/+/4196
Reviewed-by: Tim Windelschmidt <tim@monogon.tech>
Tested-by: Jenkins CI
diff --git a/metropolis/node/core/abloader/BUILD.bazel b/metropolis/node/core/abloader/BUILD.bazel
deleted file mode 100644
index 1210368..0000000
--- a/metropolis/node/core/abloader/BUILD.bazel
+++ /dev/null
@@ -1,32 +0,0 @@
-load("@rules_rust//rust:defs.bzl", "rust_binary")
-load("//osbase/build:def.bzl", "ignore_unused_configuration_target")
-
-rust_binary(
-    name = "abloader_bin",
-    srcs = ["main.rs"],
-    edition = "2021",
-    platform = select({
-        "@platforms//cpu:x86_64": "//build/platforms:uefi_x86_64",
-        "@platforms//cpu:aarch64": "//build/platforms:uefi_aarch64",
-    }),
-    # rust_binary depends on the status files by default, even if no stamp
-    # variables are used, which causes unnecessary rebuilds when the stable
-    # status file changes.
-    stamp = 0,
-    target_compatible_with = [
-        "@platforms//os:uefi",
-    ],
-    visibility = ["//visibility:private"],
-    deps = [
-        "//metropolis/node/core/abloader/spec:abloader_proto_rs",
-        "@crate_index_efi//:prost",
-        "@crate_index_efi//:uefi",
-        "@crate_index_efi//:uefi-services",
-    ],
-)
-
-ignore_unused_configuration_target(
-    name = "abloader",
-    dep = ":abloader_bin",
-    visibility = ["//visibility:public"],
-)
diff --git a/metropolis/node/core/abloader/main.rs b/metropolis/node/core/abloader/main.rs
deleted file mode 100644
index 4489b73..0000000
--- a/metropolis/node/core/abloader/main.rs
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Copyright The Monogon Project Authors.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-#![no_main]
-#![no_std]
-
-extern crate alloc;
-
-use alloc::vec::Vec;
-use core::result::Result;
-use core::fmt;
-use prost::Message;
-use uefi::fs::FileSystem;
-use uefi::proto::device_path::build::media::FilePath;
-use uefi::proto::device_path::build::DevicePathBuilder;
-use uefi::proto::device_path::{DeviceSubType, DeviceType, LoadedImageDevicePath};
-use uefi::table::boot;
-use uefi::{prelude::*, CStr16};
-use uefi_services::println;
-
-use abloader_proto::metropolis::node::core::abloader::spec::*;
-
-const A_LOADER_PATH: &CStr16 = cstr16!("\\EFI\\metropolis\\boot-a.efi");
-const B_LOADER_PATH: &CStr16 = cstr16!("\\EFI\\metropolis\\boot-b.efi");
-
-const LOADER_STATE_PATH: &CStr16 = cstr16!("\\EFI\\metropolis\\loader_state.pb");
-
-enum ValidSlot {
-    A,
-    B,
-}
-
-impl ValidSlot {
-    // other returns B if the value is A and A if the value is B.
-    fn other(&self) -> Self {
-        match self {
-            ValidSlot::A => ValidSlot::B,
-            ValidSlot::B => ValidSlot::A,
-        }
-    }
-    // path returns the path to the slot's EFI payload.
-    fn path(&self) -> &'static CStr16 {
-        match self {
-            ValidSlot::A => A_LOADER_PATH,
-            ValidSlot::B => B_LOADER_PATH,
-        }
-    }
-}
-
-enum ReadLoaderStateError {
-    FSReadError(uefi::fs::Error),
-    DecodeError(prost::DecodeError),
-}
-
-impl fmt::Display for ReadLoaderStateError {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        match self {
-           ReadLoaderStateError::FSReadError(e) => write!(f, "while reading state file: {}", e),
-           ReadLoaderStateError::DecodeError(e) => write!(f, "while decoding state file contents: {}", e),
-        }
-    }
-}
-
-fn read_loader_state(fs: &mut FileSystem) -> Result<AbLoaderData, ReadLoaderStateError> {
-    let state_raw = fs.read(&LOADER_STATE_PATH).map_err(|e| ReadLoaderStateError::FSReadError(e))?;
-    AbLoaderData::decode(state_raw.as_slice()).map_err(|e| ReadLoaderStateError::DecodeError(e))
-}
-
-fn load_slot_image(slot: &ValidSlot, boot_services: &BootServices) -> uefi::Result<Handle> {
-    let mut storage = Vec::new();
-
-    // Build the path to the slot payload. This takes the path to the loader
-    // itself, strips off the file path and following element(s) and appends
-    // the path to the correct slot payload.
-    let new_image_path = {
-        let loaded_image_device_path = boot_services
-            .open_protocol_exclusive::<LoadedImageDevicePath>(boot_services.image_handle())?;
-
-        let mut builder = DevicePathBuilder::with_vec(&mut storage);
-
-        for node in loaded_image_device_path.node_iter() {
-            if node.full_type() == (DeviceType::MEDIA, DeviceSubType::MEDIA_FILE_PATH) {
-                break;
-            }
-
-            builder = builder.push(&node).unwrap();
-        }
-
-        builder = builder
-            .push(&FilePath {
-                path_name: slot.path(),
-            })
-            .unwrap();
-
-        builder.finalize().unwrap()
-    };
-
-    boot_services
-        .load_image(
-            boot_services.image_handle(),
-            boot::LoadImageSource::FromDevicePath {
-                device_path: new_image_path,
-                from_boot_manager: false,
-            },
-        )
-}
-
-#[entry]
-fn main(_handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
-    uefi_services::init(&mut system_table).unwrap();
-
-    let boot_services = system_table.boot_services();
-
-    let boot_slot_raw = {
-        let mut esp_fs = boot_services
-            .get_image_file_system(boot_services.image_handle())
-            .expect("image filesystem not available");
-
-        let mut loader_data = match read_loader_state(&mut esp_fs) {
-            Ok(d) => d, 
-            Err(e) => {
-                println!("Unable to load A/B loader state, using default slot A: {}", e);
-                AbLoaderData {
-                    active_slot: Slot::A.into(),
-                    next_slot: Slot::None.into(),
-                }
-            }
-        };
-
-        // If next_slot is set, use it as slot to boot but clear it in the
-        // state file as the next boot should not use it again. If it should
-        // be permanently activated, it is the OS's job to put it into 
-        if loader_data.next_slot != Slot::None.into() {
-            let next_slot = loader_data.next_slot;
-            loader_data.next_slot = Slot::None.into();
-            let new_loader_data = loader_data.encode_to_vec();
-            esp_fs
-                .write(&LOADER_STATE_PATH, new_loader_data)
-                .expect("failed to write back abdata");
-            next_slot
-        } else {
-            loader_data.active_slot
-        }
-    };
-
-    let boot_slot = match Slot::try_from(boot_slot_raw) {
-        Ok(Slot::A) => ValidSlot::A,
-        Ok(Slot::B) => ValidSlot::B,
-        _ => {
-            println!("Invalid slot ({}) active, falling back to A", boot_slot_raw);
-            ValidSlot::A
-        }
-    };
-
-    let payload_image = match load_slot_image(&boot_slot, boot_services) {
-        Ok(img) => img,
-        Err(e) => {
-            println!("Error loading intended slot, falling back to other slot: {}", e);
-            match load_slot_image(&boot_slot.other(), boot_services) {
-                Ok(img) => img,
-                Err(e) => {
-                    panic!("Loading from both slots failed, second slot error: {}", e);
-                },
-            }
-        }
-    };
-
-    // Boot the payload.
-    boot_services
-        .start_image(payload_image)
-        .expect("failed to start payload");
-    Status::SUCCESS
-}
diff --git a/metropolis/node/core/abloader/spec/BUILD.bazel b/metropolis/node/core/abloader/spec/BUILD.bazel
deleted file mode 100644
index 0506d4c..0000000
--- a/metropolis/node/core/abloader/spec/BUILD.bazel
+++ /dev/null
@@ -1,44 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library")
-load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
-load("@rules_proto//proto:defs.bzl", "proto_library")
-load("@rules_proto_grpc_buf//:defs.bzl", "buf_proto_lint_test")
-load("@rules_rust_prost//:defs.bzl", "rust_prost_library")
-
-buf_proto_lint_test(
-    name = "abloader_proto_lint_test",
-    except_rules = [
-        "PACKAGE_VERSION_SUFFIX",
-        "ENUM_ZERO_VALUE_SUFFIX",
-    ],
-    protos = [":abloader_proto"],
-    use_rules = [
-        "DEFAULT",
-        "COMMENTS",
-    ],
-)
-
-proto_library(
-    name = "abloader_proto",
-    srcs = ["abloader.proto"],
-    visibility = ["//visibility:public"],
-)
-
-rust_prost_library(
-    name = "abloader_proto_rs",
-    proto = ":abloader_proto",
-    visibility = ["//visibility:public"],
-)
-
-go_proto_library(
-    name = "abloader_go_proto",
-    importpath = "source.monogon.dev/metropolis/node/core/abloader/spec",
-    proto = ":abloader_proto",
-    visibility = ["//visibility:public"],
-)
-
-go_library(
-    name = "spec",
-    embed = [":abloader_go_proto"],
-    importpath = "source.monogon.dev/metropolis/node/core/abloader/spec",
-    visibility = ["//visibility:public"],
-)
diff --git a/metropolis/node/core/abloader/spec/abloader.proto b/metropolis/node/core/abloader/spec/abloader.proto
deleted file mode 100644
index a86cb90..0000000
--- a/metropolis/node/core/abloader/spec/abloader.proto
+++ /dev/null
@@ -1,22 +0,0 @@
-syntax = "proto3";
-
-package metropolis.node.core.abloader.spec;
-
-enum Slot {
-    SLOT_NONE = 0;
-    SLOT_A = 1;
-    SLOT_B = 2;
-}
-
-// ABLoaderData contains data the A/B loader needs to chose which slot to boot
-// as well as for auto-rollback functionality.
-message ABLoaderData {
-    // The currently-active slot. This slot will be booted unless next_slot is
-    // set to a non-default value.
-    Slot active_slot = 1;
-    // The slot to boot next, but only once. Once the the slot has been selected
-    // to be booted by the loader, this value is reset before booting into that
-    // slot. If the OS boots successfully, it will update the active_slot to
-    // permanently boot from the new slot.
-    Slot next_slot = 2;
-}
\ No newline at end of file
diff --git a/metropolis/node/core/abloader/spec/gomod-generated-placeholder.go b/metropolis/node/core/abloader/spec/gomod-generated-placeholder.go
deleted file mode 100644
index ca2145c..0000000
--- a/metropolis/node/core/abloader/spec/gomod-generated-placeholder.go
+++ /dev/null
@@ -1,4 +0,0 @@
-// Copyright The Monogon Project Authors.
-// SPDX-License-Identifier: Apache-2.0
-
-package spec
diff --git a/metropolis/node/core/bios_bootcode/BUILD.bazel b/metropolis/node/core/bios_bootcode/BUILD.bazel
deleted file mode 100644
index 4b02276..0000000
--- a/metropolis/node/core/bios_bootcode/BUILD.bazel
+++ /dev/null
@@ -1,17 +0,0 @@
-load("//metropolis/node/core/bios_bootcode/genlogo:def.bzl", "gen_logo")
-
-gen_logo(
-    name = "logo.asm",
-    logo = ":logo.png",
-)
-
-genrule(
-    name = "bios_bootcode",
-    srcs = [
-        ":boot.asm",
-        ":logo.asm",
-    ],
-    outs = ["boot.bin"],
-    cmd = "nasm -d LOGO=$(location :logo.asm) $(location :boot.asm) -f bin -o $@",
-    visibility = ["//visibility:public"],
-)
diff --git a/metropolis/node/core/bios_bootcode/README.md b/metropolis/node/core/bios_bootcode/README.md
deleted file mode 100644
index 4480344..0000000
--- a/metropolis/node/core/bios_bootcode/README.md
+++ /dev/null
@@ -1,11 +0,0 @@
-# BIOS Bootcode
-
-This package contains legacy bootcode which is displayed to non-UEFI users.
-It's sole purpose is to explain users their wrongdoing and tell them to use UEFI.
-It also shows a cute ascii-art logo.
-
-## Build
- 
-Bazel generates the logo content with `genlogo`.
-It takes a black/white png-file and converts it to RLE encoded data,
-which is rendered as ascii-art at runtime.
diff --git a/metropolis/node/core/bios_bootcode/boot.asm b/metropolis/node/core/bios_bootcode/boot.asm
deleted file mode 100644
index 222e913..0000000
--- a/metropolis/node/core/bios_bootcode/boot.asm
+++ /dev/null
@@ -1,89 +0,0 @@
-org 7c00h
-
-start:
-	jmp main
-
-; si: string data, null terminated
-; di: start offset
-writestring:
-	mov al, [si]
-	or al, al
-	jz writestring_done
-	inc si
-	mov byte [fs:di], al
-	add di, 2
-	jmp writestring
-writestring_done:
-	ret
-
-; si: rle encoded data (high bit == color, lower 7: length)
-; di: start offset
-writegfx:
-	mov al, [si]
-	or al, al
-	jz writegfx_done
-	inc si
-
-	mov cl, al
-	and cx, 0b01111111
-	shr al, 7
-
-writegfx_nextinner:
-	or al, al
-	jz writegfx_space
-	mov byte [fs:di], 'M'
-writegfx_space:
-	add di, 2
-	sub cx, 1
-	jz writegfx
-	jmp writegfx_nextinner
-writegfx_done:
-	ret
-
-main:
-	xor ax, ax
-	mov ds, ax
-
-	; set mode 3 (text 80x25, 16 color)
-	mov ax, 0x3
-	int 0x10
-
-	; set up fs segment to point at framebuffer
-	mov ax, 0xb800
-	mov fs, ax
-
-	mov di, 4
-	mov si, logo
-	call writegfx
-
-	mov di, 3400
-	mov si, line1
-	call writestring
-
-	mov di, 3544
-	mov si, line2
-	call writestring
-
-end:
-	jmp end
-
-; Workaround to pass file as argument
-%macro incdef 1
-    %push _incdef_
-	%defstr %$file %{1}
-	%include %{$file}
-	%pop
-%endmacro
-
-incdef LOGO
-
-line1:
-	db "Hi there! Didn't see you coming in.", 0
-
-line2:
-	db "Unfortunately, Metropolis can only boot in UEFI mode.", 0
-
-db 0x55
-db 0xAA
-
-; We don't fill the rest with zeros, as this is done by mkimage and friends.
\ No newline at end of file
diff --git a/metropolis/node/core/bios_bootcode/genlogo/BUILD.bazel b/metropolis/node/core/bios_bootcode/genlogo/BUILD.bazel
deleted file mode 100644
index eb31744..0000000
--- a/metropolis/node/core/bios_bootcode/genlogo/BUILD.bazel
+++ /dev/null
@@ -1,14 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
-
-go_library(
-    name = "genlogo_lib",
-    srcs = ["main.go"],
-    importpath = "source.monogon.dev/metropolis/node/core/bios_bootcode/genlogo",
-    visibility = ["//visibility:private"],
-)
-
-go_binary(
-    name = "genlogo",
-    embed = [":genlogo_lib"],
-    visibility = ["//visibility:public"],
-)
diff --git a/metropolis/node/core/bios_bootcode/genlogo/def.bzl b/metropolis/node/core/bios_bootcode/genlogo/def.bzl
deleted file mode 100644
index 5db80ee..0000000
--- a/metropolis/node/core/bios_bootcode/genlogo/def.bzl
+++ /dev/null
@@ -1,32 +0,0 @@
-def _build_logo_impl(ctx):
-    arguments = ctx.actions.args()
-
-    arguments.add_all(["--input"] + ctx.files.logo)
-    output = ctx.actions.declare_file("logo.asm")
-    arguments.add_all(["--output", output])
-
-    ctx.actions.run(
-        outputs = [output],
-        inputs = ctx.files.logo,
-        arguments = [arguments],
-        executable = ctx.executable._genlogo,
-    )
-
-    return DefaultInfo(
-        files = depset([output]),
-    )
-
-gen_logo = rule(
-    implementation = _build_logo_impl,
-    attrs = {
-        "logo": attr.label(
-            allow_single_file = True,
-        ),
-        "_genlogo": attr.label(
-            default = Label(":genlogo"),
-            allow_single_file = True,
-            executable = True,
-            cfg = "exec",
-        ),
-    },
-)
diff --git a/metropolis/node/core/bios_bootcode/genlogo/main.go b/metropolis/node/core/bios_bootcode/genlogo/main.go
deleted file mode 100644
index f810357..0000000
--- a/metropolis/node/core/bios_bootcode/genlogo/main.go
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright The Monogon Project Authors.
-// SPDX-License-Identifier: Apache-2.0
-
-package main
-
-import (
-	"flag"
-	"fmt"
-	"image/color"
-	"image/png"
-	"log"
-	"os"
-)
-
-func main() {
-	input := flag.String("input", "", "")
-	output := flag.String("output", "", "")
-	flag.Parse()
-
-	if *input == "" || *output == "" {
-		log.Fatal("missing input or output flag")
-	}
-
-	inputFile, err := os.Open(*input)
-	if err != nil {
-		log.Fatal("Error opening image file:", err)
-		return
-	}
-	defer inputFile.Close()
-
-	img, err := png.Decode(inputFile)
-	if err != nil {
-		log.Fatal("Error decoding image:", err)
-	}
-
-	if img.Bounds().Dx() != 80 || img.Bounds().Dy() != 20 {
-		log.Fatal("Image dimensions must be 80x20")
-	}
-
-	var linear []uint8
-	for y := 0; y < img.Bounds().Dy(); y++ {
-		for x := 0; x < img.Bounds().Dx(); x++ {
-			gray := color.GrayModel.Convert(img.At(x, y)).(color.Gray).Y
-			linear = append(linear, gray)
-		}
-	}
-
-	// Perform RLE compression
-	var rle []uint8
-	for len(linear) > 0 {
-		val := linear[0]
-		l := uint8(1)
-		for i := 1; i < len(linear); i++ {
-			if linear[i] != val {
-				break
-			}
-			l++
-		}
-
-		L := l
-		for l > 0 {
-			block := l
-			if block > 127 {
-				block = 127
-			}
-			rle = append(rle, (val<<7)|block)
-			l -= block
-		}
-		linear = linear[L:]
-	}
-
-	rle = append(rle, 0)
-
-	outputFile, err := os.Create(*output)
-	if err != nil {
-		log.Fatalf("failed creating output file: %v", err)
-	}
-	defer outputFile.Close()
-
-	outputFile.WriteString("logo: db ")
-	for i, r := range rle {
-		if i > 0 {
-			outputFile.WriteString(", ")
-		}
-		fmt.Fprintf(outputFile, "0x%02x", r)
-	}
-	outputFile.WriteString("\n")
-}
diff --git a/metropolis/node/core/bios_bootcode/logo.png b/metropolis/node/core/bios_bootcode/logo.png
deleted file mode 100644
index 5f395df..0000000
--- a/metropolis/node/core/bios_bootcode/logo.png
+++ /dev/null
Binary files differ
diff --git a/metropolis/node/core/minit/BUILD.bazel b/metropolis/node/core/minit/BUILD.bazel
deleted file mode 100644
index bff1f69..0000000
--- a/metropolis/node/core/minit/BUILD.bazel
+++ /dev/null
@@ -1,21 +0,0 @@
-load("@aspect_bazel_lib//lib:expand_template.bzl", "expand_template")
-load("@rules_cc//cc:defs.bzl", "cc_binary")
-
-expand_template(
-    name = "stamp",
-    out = "stamp.h",
-    stamp = 1,
-    stamp_substitutions = {"copyright": "{{STABLE_MONOGON_copyright}}"},
-    template = ["#define COPYRIGHT_LINE \"copyright\""],
-)
-
-cc_binary(
-    name = "minit",
-    srcs = [
-        "main.c",
-        ":stamp",
-    ],
-    visibility = [
-        "//metropolis/node:__pkg__",
-    ],
-)
diff --git a/metropolis/node/core/minit/main.c b/metropolis/node/core/minit/main.c
deleted file mode 100644
index 834b023..0000000
--- a/metropolis/node/core/minit/main.c
+++ /dev/null
@@ -1,176 +0,0 @@
-// minit is a barebones Linux-compatible init (PID 1) process.
-//
-// Its goal is to run the Metropolis core executable and reap any children that
-// it stumbles upon. It does not support running under a TTY and is not
-// configurable in any way.
-//
-// The only reason this exists is because Go's child process reaping (when
-// using os/exec.Command) races any PID 1 process reaping, thereby preventing
-// running a complex Go binary as PID 1. In the future this might be rewritten
-// in a memory-safe language like Zig or Rust, but this implementation will do
-// for now, as long as it keeps having basically zero attack surface.
-//
-// This code has been vaguely inspired by github.com/Yelp/dumb-init and
-// github.com/krallin/tini, two already existing minimal init implementations.
-// These, however, attempt to handle being run in a TTY and some
-// configurability, as they're meant to be run in containers. We don't need any
-// of that, and we'd rather have as little C as possible.
-
-#include <errno.h>
-#include <linux/reboot.h>
-#include <signal.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/reboot.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include "metropolis/node/core/minit/stamp.h"
-
-void handle_signal(pid_t child_pid, int signum);
-
-#define NUM_CONSOLES 3
-FILE *consoles[NUM_CONSOLES] = {};
-
-// open_consoles populates the consoles array with FILE pointers to opened
-// character devices that should receive log messages. Some of these pointers
-// are likely to be null, meaning that particular console is not available.
-void open_consoles() {
-    consoles[0] = fopen("/dev/tty0", "w");
-    consoles[1] = fopen("/dev/ttyS0", "w");
-    consoles[2] = fopen("/dev/ttyS1", "w");
-
-    // Set all open consoles to be line-buffered.
-    for (int i = 0; i < NUM_CONSOLES; i++) {
-        if (consoles[i] == NULL) {
-            continue;
-        }
-        setvbuf(consoles[i], NULL, _IOLBF, BUFSIZ);
-    }
-
-    // TODO(q3k): disable hardware and software flow control on TTYs. This
-    // shouldn't be necessary on our current platform, but should be ensured
-    // regardless, to make sure we never block writing to any console.
-}
-
-// cprintf emits a format string to all opened consoles.
-void cprintf(const char *fmt, ...) {
-    va_list args;
-    va_start(args, fmt);
-
-    for (int i = 0; i < NUM_CONSOLES; i++) {
-        FILE *console = consoles[i];
-        if (console == NULL) {
-            continue;
-        }
-        vfprintf(console, fmt, args);
-    }
-
-    va_end(args);
-}
-
-int main() {
-    // Block all signals. We'll unblock them in the child.
-    sigset_t all_signals;
-    sigfillset(&all_signals);
-    sigprocmask(SIG_BLOCK, &all_signals, NULL);
-
-    open_consoles();
-
-    // Say hello.
-    cprintf(
-        "\n"
-        "  Metropolis Cluster Operating System\n"
-        "  " COPYRIGHT_LINE "\n"
-        "\n"
-    );
-
-
-    pid_t pid = fork();
-    if (pid < 0) {
-        cprintf("fork(): %s\n", strerror(errno));
-        return 1;
-    }
-
-    if (pid == 0) {
-        // In the child. Unblock all signals.
-        sigprocmask(SIG_UNBLOCK, &all_signals, NULL);
-        if (setsid() == -1) {
-            cprintf("setsid: %s\n", strerror(errno));
-            return 1;
-        }
-
-        // Then, start the core executable.
-        char *argv[] = {
-            "/core",
-            NULL,
-        };
-        execvp(argv[0], argv);
-        cprintf("execvpe(/core) failed: %s\n", strerror(errno));
-        return 1;
-    }
-
-    // In the parent. Wait for any signal, then handle it and any other pending
-    // ones.
-    for (;;) {
-        int signum;
-        sigwait(&all_signals, &signum);
-        handle_signal(pid, signum);
-    }
-}
-
-// handle_signal is called by the main reap loop for every signal received. It
-// reaps children if SIGCHLD is received, and otherwise dispatches the signal to
-// its direct child.
-void handle_signal(pid_t child_pid, int signum) {
-    // Anything other than SIGCHLD should just be forwarded to the child.
-    if (signum != SIGCHLD) {
-        kill(-child_pid, signum);
-        return;
-    }
-
-    // A SIGCHLD was received. Go through all children and reap them, checking
-    // if any of them is our direct child.
-
-    // exit_status will be set if the direct child process exited.
-    int exit_status = -1;
-
-    pid_t killed_pid;
-    int status;
-    while ((killed_pid = waitpid(-1, &status, WNOHANG)) > 0) {
-        if (killed_pid != child_pid) {
-            // Something else than our direct child died, just reap it.
-            continue;
-        }
-
-        // Our direct child exited. Translate its status into an exit code.
-        if (WIFEXITED(status)) {
-            // For processes which exited, just use the exit code directly.
-            exit_status = WEXITSTATUS(status);
-        } else if (WIFSIGNALED(status)) {
-            // Otherwise, emulate what sh/bash do and return 128 + the signal
-            // number that the child received.
-            exit_status = 128 + WTERMSIG(status);
-        } else {
-            // Something unexpected happened. Attempt to handle this gracefully,
-            // but complain.
-            cprintf("child status not EXITED nor SIGNALED: %d\n", status);
-            exit_status = 1;
-        }
-
-        // Direct child exited, let's also exit.
-        if (exit_status >= 0) {
-            if (exit_status == 0) {
-                reboot(LINUX_REBOOT_CMD_RESTART);
-                return;
-            }
-            cprintf("\n Metropolis encountered an uncorrectable error and this node must be restarted.\n");
-            cprintf("core exit status: %d\n", exit_status);
-            sync();
-            cprintf("  Disks synced, rebooting...\n\n");
-            reboot(LINUX_REBOOT_CMD_RESTART);
-        }
-    }
-}
diff --git a/metropolis/node/core/update/BUILD.bazel b/metropolis/node/core/update/BUILD.bazel
index 8dac43e..532b917 100644
--- a/metropolis/node/core/update/BUILD.bazel
+++ b/metropolis/node/core/update/BUILD.bazel
@@ -4,13 +4,13 @@
     name = "update",
     srcs = ["update.go"],
     embedsrcs = [
-        "//metropolis/node/core/abloader",  #keep
+        "//metropolis/node/abloader",  #keep
     ],
     importpath = "source.monogon.dev/metropolis/node/core/update",
     visibility = ["//visibility:public"],
     deps = [
         "//go/logging",
-        "//metropolis/node/core/abloader/spec",
+        "//metropolis/node/abloader/spec",
         "//metropolis/proto/api",
         "//metropolis/version",
         "//osbase/blockdev",
diff --git a/metropolis/node/core/update/e2e/BUILD.bazel b/metropolis/node/core/update/e2e/BUILD.bazel
index 5dcf3f7..a5dcc5e 100644
--- a/metropolis/node/core/update/e2e/BUILD.bazel
+++ b/metropolis/node/core/update/e2e/BUILD.bazel
@@ -10,7 +10,7 @@
         # For the initial image creation
         "//metropolis/node/core/update/e2e/testos:verity_rootfs_x",
         "//metropolis/node/core/update/e2e/testos:kernel_efi_x",
-        "//metropolis/node/core/abloader",
+        "//metropolis/node/abloader",
         # For the two update tests
         "//metropolis/node/core/update/e2e/testos:testos_image_y",
         "//metropolis/node/core/update/e2e/testos:testos_image_z",
@@ -22,7 +22,7 @@
         "xOvmfCodePath": "$(rlocationpath //third_party/edk2:OVMF_CODE.fd )",
         "xBootPath": "$(rlocationpath //metropolis/node/core/update/e2e/testos:kernel_efi_x )",
         "xSystemXPath": "$(rlocationpath //metropolis/node/core/update/e2e/testos:verity_rootfs_x )",
-        "xAbloaderPath": "$(rlocationpath //metropolis/node/core/abloader )",
+        "xAbloaderPath": "$(rlocationpath //metropolis/node/abloader )",
     },
     deps = [
         "//osbase/blockdev",
diff --git a/metropolis/node/core/update/update.go b/metropolis/node/core/update/update.go
index 6e41768..3d3060d 100644
--- a/metropolis/node/core/update/update.go
+++ b/metropolis/node/core/update/update.go
@@ -36,7 +36,7 @@
 	"source.monogon.dev/osbase/oci/registry"
 	"source.monogon.dev/version"
 
-	abloaderpb "source.monogon.dev/metropolis/node/core/abloader/spec"
+	abloaderpb "source.monogon.dev/metropolis/node/abloader/spec"
 	apb "source.monogon.dev/metropolis/proto/api"
 )
 
@@ -425,7 +425,7 @@
 	return nil
 }
 
-//go:embed metropolis/node/core/abloader/abloader_bin.efi
+//go:embed metropolis/node/abloader/abloader_bin.efi
 var abloader []byte
 
 func (s *Service) fixupPreloader() error {