blob: 84ca05923c40f36aa091cbfdc1cffa9f7e987c73 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <lib/boot-options/boot-options.h>
#include <lib/crashlog.h>
#include <lib/zbitl/error-stdio.h>
#include <lib/zbitl/image.h>
#include <lib/zbitl/memory.h>
#include <lib/zx/status.h>
#include <mexec.h>
#include <stdio.h>
#include <zircon/assert.h>
#include <zircon/limits.h>
#include <fbl/alloc_checker.h>
#include <fbl/array.h>
#include <fbl/ref_ptr.h>
#include <ktl/byte.h>
#include <ktl/span.h>
#include <ktl/type_traits.h>
#include <ktl/variant.h>
#include <lk/init.h>
#include <phys/handoff.h>
#include <vm/vm_object.h>
#include <ktl/enforce.h>
namespace {
// TODO(fxbug.dev/84107): Later this will arrive in a whole page from the
// physboot handoff so it can be stuffed into a VMO and extended there.
// Mexec data as gleaned from the physboot hand-off.
zbitl::Image<fbl::Array<ktl::byte>> gImageAtHandoff;
void ConstructMexecDataZbi(uint level) {
constexpr size_t kInitialBuffSize = PAGE_SIZE;
ZX_ASSERT(!gImageAtHandoff.storage());
{
fbl::AllocChecker ac;
auto* buff = new (&ac) ktl::byte[kInitialBuffSize];
ZX_ASSERT(ac.check());
ZX_ASSERT(buff != nullptr);
gImageAtHandoff.storage() = fbl::Array<ktl::byte>(buff, kInitialBuffSize);
}
if (auto result = gImageAtHandoff.clear(); result.is_error()) {
zbitl::PrintViewError(result.error_value());
abort();
}
// Transfer initial data from the physboot handoff.
zbitl::View handoff(gPhysHandoff->mexec_data.get());
if (auto result = gImageAtHandoff.Extend(handoff.begin(), handoff.end()); result.is_error()) {
zbitl::PrintViewCopyError(result.error_value());
abort();
}
ZX_ASSERT(handoff.take_error().is_ok());
}
} // namespace
// After the VM is initialized so that we can allocate.
LK_INIT_HOOK(construct_mexec_data_zbi, ConstructMexecDataZbi, LK_INIT_LEVEL_VM)
zx::status<size_t> WriteMexecData(ktl::span<ktl::byte> buffer) {
// Storage or write errors resulting from a span-backed Image imply buffer
// overflow.
constexpr auto error = [](const auto& err) -> zx::status<size_t> {
return zx::error{err.storage_error ? ZX_ERR_BUFFER_TOO_SMALL : ZX_ERR_INTERNAL};
};
constexpr auto extend_error = [](const auto& err) -> zx::status<size_t> {
return zx::error{err.write_error ? ZX_ERR_BUFFER_TOO_SMALL : ZX_ERR_INTERNAL};
};
zbitl::Image image(buffer);
if (auto result = image.clear(); result.is_error()) {
zbitl::PrintViewError(result.error_value());
return error(result.error_value());
}
if (auto result = image.Extend(gImageAtHandoff.begin(), gImageAtHandoff.end());
result.is_error()) {
zbitl::PrintViewCopyError(result.error_value());
return extend_error(result.error_value());
}
if (auto result = gImageAtHandoff.take_error(); result.is_error()) {
zbitl::PrintViewError(result.error_value());
return zx::error{ZX_ERR_INTERNAL};
}
// Propagate any stashed crashlog to the next kernel.
if (const fbl::RefPtr<VmObject> crashlog = crashlog_get_stashed()) {
const zbi_header_t header = {
.type = ZBI_TYPE_CRASHLOG,
.length = static_cast<uint32_t>(crashlog->size()),
};
if (auto result = image.Append(header); result.is_error()) {
printf("mexec: could not append crashlog: ");
zbitl::PrintViewError(result.error_value());
return error(result.error_value());
} else {
auto it = ktl::move(result).value();
ktl::span<ktl::byte> payload = it->payload;
zx_status_t status = crashlog->Read(payload.data(), 0, payload.size());
if (status != ZX_OK) {
return zx::error{status};
}
}
}
return zx::ok(image.size_bytes());
}