blob: ac554f63cc0a3367f4e336e9c20b6ba460eb4408 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <lib/efi/testing/stub_runtime_services.h>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <iterator>
#include <string_view>
#include <efi/variable/variable.h>
#include <fbl/vector.h>
namespace efi {
namespace {
using ::testing::_;
using ::testing::DoAll;
using ::testing::Return;
using ::testing::SetArgPointee;
// We need to stash the global StubRuntimeServices object here since there's
// no "self" parameter to any of these functions.
StubRuntimeServices* active_stub = nullptr;
// Wrapper to bounce the EFI C function pointer into our global StubRuntimeServices
// object.
template <auto func, typename... Args>
EFIAPI efi_status Wrap(Args... args) {
if (!active_stub) {
// Someone held onto the underlying function table after deleting
// the parent StubRuntimeServices.
return EFI_NOT_READY;
}
return (active_stub->*func)(args...);
}
} // namespace
StubRuntimeServices::StubRuntimeServices()
: services_{
.GetTime = Wrap<&StubRuntimeServices::GetTime>,
.SetTime = Wrap<&StubRuntimeServices::SetTime>,
.GetWakeupTime = Wrap<&StubRuntimeServices::GetWakeupTime>,
.SetWakeupTime = Wrap<&StubRuntimeServices::SetWakeupTime>,
.SetVirtualAddressMap = Wrap<&StubRuntimeServices::SetVirtualAddressMap>,
.ConvertPointer = Wrap<&StubRuntimeServices::ConvertPointer>,
.GetVariable = Wrap<&StubRuntimeServices::GetVariable>,
.GetNextVariableName = Wrap<&StubRuntimeServices::GetNextVariableName>,
.SetVariable = Wrap<&StubRuntimeServices::SetVariable>,
.GetNextHighMonotonicCount = Wrap<&StubRuntimeServices::GetNextHighMonotonicCount>,
.ResetSystem = Wrap<&StubRuntimeServices::ResetSystem>,
.UpdateCapsule = Wrap<&StubRuntimeServices::UpdateCapsule>,
.QueryCapsuleCapabilities = Wrap<&StubRuntimeServices::QueryCapsuleCapabilities>,
.QueryVariableInfo = Wrap<&StubRuntimeServices::QueryVariableInfo>,
} {
if (active_stub) {
// We cannot support multiple StubRuntimeServices due to the global singleton
// nature. Rather than causing hard-to-debug test behavior here, just fail
// loudly and immediately.
fprintf(stderr, "ERROR: cannot create multiple StubRuntimeService objects - exiting\n");
exit(1);
}
active_stub = this;
}
StubRuntimeServices::~StubRuntimeServices() { active_stub = nullptr; }
void StubRuntimeServices::SetVariables(const std::list<Variable>& vars) {
vars_ = vars;
var_it_ = vars_->end();
}
void StubRuntimeServices::UnsetVariables() { vars_.reset(); }
// Safe length() implementation. Returns number of elements before `u'\0'` or `len`
inline size_t StrNLength(const char16_t* str, size_t len) {
const char16_t* eof = std::char_traits<char16_t>::find(str, len, u'\0');
return eof ? std::distance(str, eof) : len;
}
efi_status StubRuntimeServices::GetVariable(char16_t* var_name, efi_guid* vendor_guid,
uint32_t* attributes, size_t* data_size, void* data) {
if (!vars_)
return EFI_UNSUPPORTED;
auto it = std::find_if(vars_->begin(), vars_->end(), [&](const Variable& it) {
return it.id == VariableId{String(var_name), *vendor_guid};
});
if (it == vars_->end())
return EFI_NOT_FOUND;
if (*data_size < it->value.size()) {
*data_size = it->value.size();
return EFI_BUFFER_TOO_SMALL;
}
std::copy_n(it->value.begin(), it->value.size(), (uint8_t*)data);
*data_size = it->value.size();
return EFI_SUCCESS;
}
efi_status StubRuntimeServices::GetNextVariableName(size_t* var_name_size, char16_t* var_name,
efi_guid* vendor_guid) {
if (!vars_)
return EFI_UNSUPPORTED;
size_t str_len_safe = StrNLength(var_name, *var_name_size);
efi::String var_name_str({var_name, str_len_safe});
auto it = var_it_;
if (var_name_str == efi::kInvalidVariableName) {
// startover from the beginning
var_it_ = vars_->begin();
it = var_it_;
} else {
// If last value is passed increment tmp iterator
if (it == vars_->end())
return EFI_NOT_FOUND;
if (it->id == VariableId{var_name_str, *vendor_guid})
++it;
}
if (it == vars_->end())
return EFI_NOT_FOUND;
const auto& it_var_name = std::u16string_view(it->id.name);
const size_t it_var_name_size = (it_var_name.size() + 1) * sizeof(it_var_name[0]);
if (it_var_name_size > *var_name_size) {
*var_name_size = it_var_name_size;
return EFI_BUFFER_TOO_SMALL;
}
std::copy_n(it_var_name.begin(), it_var_name.size(), var_name);
var_name[it_var_name.size()] = u'\0';
*vendor_guid = it->id.vendor_guid;
*var_name_size = it_var_name_size;
var_it_ = it;
return EFI_SUCCESS;
}
efi_status StubRuntimeServices::QueryVariableInfo(uint32_t attributes,
uint64_t* max_var_storage_size,
uint64_t* remaining_var_storage_size,
uint64_t* max_var_size) {
*max_var_storage_size = 1;
*remaining_var_storage_size = 2;
*max_var_size = 3;
return EFI_SUCCESS;
}
} // namespace efi