[kernel][physboot] Handle legacy x86 trampoline booting
This makes use_physboot mode handle the legacy x86 kernel
that needs to be loaded at the 1M fixed address.
Bug: 32414, 32255
Test: fx set ... --args=use_physboot=true && fx run-zbi-test core-tests
Change-Id: I80b9de32417fb6681929e7590b5102be4a8c3acf
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/489403
Commit-Queue: Auto-Submit <auto-submit@fuchsia-infra.iam.gserviceaccount.com>
Fuchsia-Auto-Submit: Roland McGrath <mcgrathr@google.com>
Reviewed-by: Joshua Seaton <joshuaseaton@google.com>
diff --git a/zircon/kernel/arch/x86/phys/boot-shim/BUILD.gn b/zircon/kernel/arch/x86/phys/boot-shim/BUILD.gn
index f054255..3bb51e3 100644
--- a/zircon/kernel/arch/x86/phys/boot-shim/BUILD.gn
+++ b/zircon/kernel/arch/x86/phys/boot-shim/BUILD.gn
@@ -70,7 +70,10 @@
if (toolchain.environment == "kernel.phys" ||
toolchain.environment == "kernel.phys32") {
source_set("trampoline-boot") {
- visibility = [ ":*" ]
+ visibility = [
+ ":*",
+ "//zircon/kernel/phys/*",
+ ]
sources = [ "trampoline-boot.cc" ]
public = [ "trampoline-boot.h" ]
public_deps = [ "//zircon/kernel/phys:boot-zbi" ]
diff --git a/zircon/kernel/arch/x86/phys/boot-shim/trampoline-boot.cc b/zircon/kernel/arch/x86/phys/boot-shim/trampoline-boot.cc
index 5724e3d..a46e764 100644
--- a/zircon/kernel/arch/x86/phys/boot-shim/trampoline-boot.cc
+++ b/zircon/kernel/arch/x86/phys/boot-shim/trampoline-boot.cc
@@ -168,6 +168,8 @@
}
[[noreturn]] void TrampolineBoot::Boot() {
+ ZX_ASSERT(!MustRelocateDataZbi());
+
uintptr_t entry = static_cast<uintptr_t>(KernelEntryAddress());
ZX_ASSERT(entry == KernelEntryAddress());
diff --git a/zircon/kernel/arch/x86/phys/boot-shim/trampoline-boot.h b/zircon/kernel/arch/x86/phys/boot-shim/trampoline-boot.h
index f2fcd74..78e2b5c6 100644
--- a/zircon/kernel/arch/x86/phys/boot-shim/trampoline-boot.h
+++ b/zircon/kernel/arch/x86/phys/boot-shim/trampoline-boot.h
@@ -16,13 +16,17 @@
using BootZbi::BootZbi;
+ // In the legacy fixed-address format, the entry address is always above 1M.
+ // In the new format, it's an offset and in practice it's never > 1M. So
+ // this is a safe-enough heuristic to distinguish the new from the old.
+ bool Relocating() const { return KernelHeader()->entry > kFixedLoadAddress; }
+
uint64_t KernelEntryAddress() const {
- // In the legacy fixed-address format, the entry address is always
- // above 1M. In the new format, it's an offset and in practice it's
- // never > 1M. So this is a safe-enough heuristic to distinguish the
- // new format from the old.
- uint64_t entry = KernelHeader()->entry;
- return entry < kFixedLoadAddress ? BootZbi::KernelEntryAddress() : entry;
+ return Relocating() ? KernelHeader()->entry : BootZbi::KernelEntryAddress();
+ }
+
+ bool MustRelocateDataZbi() const {
+ return Relocating() && FixedKernelOverlapsData(kFixedLoadAddress);
}
fitx::result<Error> Load(uint32_t extra_data_capacity = 0);
diff --git a/zircon/kernel/phys/BUILD.gn b/zircon/kernel/phys/BUILD.gn
index 11ae459..b91d879 100644
--- a/zircon/kernel/phys/BUILD.gn
+++ b/zircon/kernel/phys/BUILD.gn
@@ -293,4 +293,8 @@
"//zircon/system/ulib/pretty",
"//zircon/system/ulib/zbitl",
]
+ if (current_cpu == "x64") {
+ deps += [ "//zircon/kernel/arch/x86/phys/boot-shim:trampoline-boot" ]
+ include_dirs = [ "//zircon/kernel/arch/x86/phys/boot-shim" ]
+ }
}
diff --git a/zircon/kernel/phys/boot-zbi.cc b/zircon/kernel/phys/boot-zbi.cc
index 5003172..7451300 100644
--- a/zircon/kernel/phys/boot-zbi.cc
+++ b/zircon/kernel/phys/boot-zbi.cc
@@ -131,6 +131,14 @@
return in_place_space >= KernelMemorySize();
}
+bool BootZbi::FixedKernelOverlapsData(uint64_t kernel_load_address) const {
+ uint64_t start1 = kernel_load_address;
+ uint64_t start2 = reinterpret_cast<uintptr_t>(data_.storage().data());
+ uint64_t end1 = start1 + KernelMemorySize();
+ uint64_t end2 = start2 + data_.storage().size();
+ return start1 <= start2 ? start2 < end1 : start1 < end2;
+}
+
fitx::result<BootZbi::Error> BootZbi::Load(uint32_t extra_data_capacity,
ktl::optional<uintptr_t> kernel_load_address) {
auto input_address = reinterpret_cast<uintptr_t>(zbi_.storage().data());
@@ -276,17 +284,11 @@
};
}
- if (kernel_load_address) {
+ if (kernel_load_address && FixedKernelOverlapsData(*kernel_load_address)) {
// There's a fixed kernel load address, so the data ZBI cannot be allowed
// to reuse the memory where it will go. This memory will already have
// been reserved from the allocator, but the incoming data might be there.
- uintptr_t start1 = *kernel_load_address;
- size_t len1 = static_cast<size_t>(KernelMemorySize());
- uintptr_t start2 = reinterpret_cast<uintptr_t>(data_.storage().data());
- size_t len2 = data_.storage().size();
- if (start1 <= start2 ? start1 + len1 > start2 : start1 < start2 + len2) {
- data_.storage() = {};
- }
+ data_.storage() = {};
}
// If we can reuse either the kernel image or the data ZBI items in place,
diff --git a/zircon/kernel/phys/include/phys/boot-zbi.h b/zircon/kernel/phys/include/phys/boot-zbi.h
index 65a68bf..6d42b65 100644
--- a/zircon/kernel/phys/include/phys/boot-zbi.h
+++ b/zircon/kernel/phys/include/phys/boot-zbi.h
@@ -51,6 +51,10 @@
BootZbi(BootZbi&&) = default;
BootZbi& operator=(BootZbi&&) = default;
+ // These are overridden in TrampolineBoot (see x86/phys/boot-shim).
+ bool Relocating() const { return false; }
+ bool MustRelocateDataZbi() const { return false; }
+
// Suggest allocation parameters for a whole bootable ZBI image whose
// incoming size is known but whose contents haven't been seen yet. A
// conforming allocation will be optimal for reuse by Load().
@@ -113,6 +117,8 @@
void LogAddresses();
void LogBoot(uint64_t entry) const;
+ bool FixedKernelOverlapsData(uint64_t kernel_load_address) const;
+
private:
// These are set on construction by Init().
InputZbi zbi_;
diff --git a/zircon/kernel/phys/physboot.cc b/zircon/kernel/phys/physboot.cc
index 976b0a3..2ed4f09 100644
--- a/zircon/kernel/phys/physboot.cc
+++ b/zircon/kernel/phys/physboot.cc
@@ -22,6 +22,17 @@
#include <phys/zbitl-allocation.h>
#include <pretty/cpp/sizes.h>
+#ifdef __x86_64__
+#include "trampoline-boot.h"
+
+using ChainBoot = TrampolineBoot;
+
+#else
+
+using ChainBoot = BootZbi;
+
+#endif
+
const char Symbolize::kProgramName_[] = "physboot";
namespace {
@@ -32,7 +43,7 @@
struct LoadedZircon {
Allocation buffer;
- BootZbi boot;
+ ChainBoot boot;
arch::EarlyTicks decompress_ts;
};
@@ -71,21 +82,28 @@
pretty::FormattedBytes(kernel_zbi.size_bytes()).c_str());
}
- BootZbi boot;
+ ChainBoot boot;
if (auto result = boot.Init(kernel_zbi); result.is_error()) {
printf("physboot: Cannot read STORAGE_KERNEL item ZBI: ");
zbitl::PrintViewCopyError(result.error_value());
abort();
}
+ if (auto result = boot.Load(reserve_memory_estimate); result.is_error()) {
+ printf("physboot: Cannot load decompressed kernel: ");
+ zbitl::PrintViewCopyError(result.error_value());
+ abort();
+ }
+
return {std::move(buffer), std::move(boot), decompress_ts};
}
[[noreturn]] void BootZircon(BootZbi::InputZbi& zbi, BootZbi::InputZbi::iterator kernel_item,
arch::EarlyTicks entry_ts) {
auto zircon = LoadZircon(zbi, kernel_item, kKernelBssEstimate);
- if (!zircon.boot.KernelCanLoadInPlace() ||
- zircon.buffer.size_bytes() < zircon.boot.KernelMemorySize()) {
+ if (!zircon.boot.Relocating() && //
+ (!zircon.boot.KernelCanLoadInPlace() ||
+ zircon.buffer.size_bytes() < zircon.boot.KernelMemorySize())) {
printf("physboot: Kernel ZBI at %#" PRIx64 " cannot be loaded in place!\n",
zircon.boot.KernelLoadAddress());
uint64_t bss_size = zircon.boot.KernelHeader()->reserve_memory_size;
@@ -113,6 +131,28 @@
zbi.storage().size(),
};
+ Allocation relocated_zbi;
+ if (boot.MustRelocateDataZbi()) {
+ // Actually, the original data ZBI must be moved elsewhere since it
+ // overlaps the space where the fixed-address kernel will be loaded.
+ fbl::AllocChecker ac;
+ relocated_zbi = Allocation::New(ac, zbi.storage().size(), arch::kZbiBootDataAlignment);
+ if (!ac.check()) {
+ printf("physboot: Cannot allocate %#zx bytes aligned to %#zx for relocated data ZBI!\n",
+ zbi.storage().size(), arch::kZbiBootDataAlignment);
+ abort();
+ }
+ if (auto result = zbi.Copy(relocated_zbi.data(), zbi.begin(), zbi.end()); result.is_error()) {
+ zbi.ignore_error();
+ printf("physboot: Failed to relocate data ZBI: ");
+ zbitl::PrintViewCopyError(result.error_value());
+ printf("\n");
+ abort();
+ }
+ ZX_ASSERT(zbi.take_error().is_ok());
+ boot.DataZbi().storage() = relocated_zbi.data();
+ }
+
boot.Boot();
}