2#include "pedigree/kernel/Log.h"
3#include "pedigree/kernel/linker/KernelElf.h"
4#include "pedigree/kernel/linker/ModuleImage.h"
5#include "pedigree/kernel/process/Scheduler.h"
6#include "pedigree/kernel/process/Thread.h"
7#include "pedigree/kernel/time/Time.h"
9#include "modules/Module.h"
11#if !HOSTED || !PEDIGREE_HOSTED_SMOKE_TESTS
12#error "The runtime-module lifecycle fixture requires the hosted smoke test configuration"
15extern void system_reset();
17extern const uint8_t runtime_provider_image[];
18extern const size_t runtime_provider_image_size;
19extern const uint8_t runtime_consumer_image[];
20extern const size_t runtime_consumer_image_size;
21extern const uint8_t runtime_undeclared_image[];
22extern const size_t runtime_undeclared_image_size;
26using Load = KernelElf::RuntimeLoadResult;
27using Unload = KernelElf::RuntimeUnloadResult;
28size_t constructors = 0, entries = 0, exits = 0, destructors = 0;
29bool entryFails =
false, visibleDuringEntry =
false, expectHidden =
true;
32bool check(
bool value,
const char* detail) {
34 ERROR(
"HOSTED-RUNTIME-MODULE-TEST: FAIL " << detail);
38Load load(
const uint8_t* image,
size_t length,
char suffix = 0) {
41 const auto admitted = kernel.beginRuntimeModuleLoad(length, transaction);
42 if (admitted != Load::Ready)
44 MemoryCopy(transaction.data(), image, length);
47 uintptr_t address = 0;
49 if (inspection.preflight(transaction.data(), length) != ModuleImage::Result::Valid ||
50 !inspection.findSymbol(
"g_pModuleName", name) ||
51 !inspection.localPointer(name.value, address) ||
52 !inspection.fileOffset(address, 17, offset))
53 return Load::InvalidImage;
55 transaction.data()[offset + StringLength(
"runtime-fixture-")] = suffix;
57 return kernel.loadModuleRuntime(transaction);
62 const auto start = Time::getTicks();
64 for (
size_t attempt = 0;
65 attempt < 100000 && Time::getTicks() - start < 10 * Time::Multiplier::Second; ++attempt) {
67 const auto admitted = kernel.beginRuntimeModuleLoad(runtime_provider_image_size, probe);
68 if (admitted == Load::Ready) {
72 if (admitted != Load::Busy)
73 return check(
false,
"arena admission");
76 if (!check(ready,
"bounded wait for Active boot provider"))
78 NOTICE(
"HOSTED-RUNTIME-MODULE-TEST: PASS active-admission");
80 if (!check(inspection.preflight(runtime_provider_image, runtime_provider_image_size) ==
81 ModuleImage::Result::Valid,
82 "native fixture preflight") ||
83 !check(load(runtime_provider_image, 16) == Load::InvalidImage,
"truncated header") ||
84 !check(load(runtime_consumer_image, runtime_consumer_image_size) == Load::MissingDependency,
87 NOTICE(
"HOSTED-RUNTIME-MODULE-TEST: PASS preflight-missing-provider");
91 if (!check(kernel.beginRuntimeModuleLoad(runtime_provider_image_size, first) == Load::Ready &&
92 kernel.beginRuntimeModuleLoad(runtime_provider_image_size, second) == Load::Busy,
93 "single transition admission"))
96 KernelElf::failRuntimeProtectionForTest(2);
97 if (!check(load(runtime_provider_image, runtime_provider_image_size) == Load::ProtectionFailed,
98 "recoverable protection failure") ||
99 !check(!constructors && !entries && !exits && !destructors,
"protection failure ran code"))
101 NOTICE(
"HOSTED-RUNTIME-MODULE-TEST: PASS protection-rollback");
104 if (!check(load(runtime_provider_image, runtime_provider_image_size) == Load::EntryFailed,
106 !check(constructors == 1 && entries == 1 && exits == 1 && destructors == 1,
107 "entry failure lifecycle cleanup") ||
108 !check(kernel.unloadModuleRuntime(
"runtime-fixture-0") == Unload::NotFound,
109 "failed entry remained published"))
112 if (!check(load(runtime_provider_image, runtime_provider_image_size) == Load::Loaded,
113 "retry after entry failure") ||
114 !check(!visibleDuringEntry,
"incomplete exports became visible") ||
115 !check(kernel.globalLookupSymbol(
"module_runtime_fixture_value") != 0,
"Active export") ||
116 !check(load(runtime_provider_image, runtime_provider_image_size) == Load::Duplicate,
117 "duplicate live name"))
119 NOTICE(
"HOSTED-RUNTIME-MODULE-TEST: PASS entry-rollback-retry-publish");
121 expectHidden =
false;
123 load(runtime_undeclared_image, runtime_undeclared_image_size) == Load::MissingDependency,
124 "undeclared import provider") ||
125 !check(load(runtime_consumer_image, runtime_consumer_image_size) == Load::Loaded,
126 "declared provider import") ||
127 !check(kernel.unloadModuleRuntime(
"runtime-fixture-0") == Unload::DependedOn,
128 "live dependency did not pin provider") ||
129 !check(kernel.unloadModuleRuntime(
"runtime-consumer") == Unload::Unloaded,
130 "consumer unload") ||
131 !check(kernel.unloadModuleRuntime(
"runtime-fixture-0") == Unload::Unloaded,
132 "provider unload") ||
133 !check(kernel.unloadModuleRuntime(
"runtime-fixture-0") == Unload::NotFound,
134 "repeated unload") ||
135 !check(!kernel.globalLookupSymbol(
"module_runtime_fixture_value"),
"retired export visible"))
137 NOTICE(
"HOSTED-RUNTIME-MODULE-TEST: PASS dependency-unload");
139 for (
char suffix =
'0'; suffix <=
'3'; ++suffix) {
140 if (!check(load(runtime_provider_image, runtime_provider_image_size, suffix) == Load::Loaded,
141 "fill all four slots"))
144 if (!check(load(runtime_provider_image, runtime_provider_image_size,
'4') == Load::NoMemory,
145 "bounded arena exhaustion"))
147 for (
char suffix =
'0'; suffix <=
'3'; ++suffix) {
148 char name[] =
"runtime-fixture-0";
149 name[
sizeof(name) - 2] = suffix;
150 if (!check(kernel.unloadModuleRuntime(name) == Unload::Unloaded,
"slot reclaim"))
154 if (!check(load(runtime_provider_image, runtime_provider_image_size) == Load::Loaded,
155 "reload after slot exhaustion") ||
156 !check(kernel.unloadModuleRuntime(
"runtime-fixture-0") == Unload::Unloaded,
"final unload") ||
157 !check(constructors == entries && entries == exits && exits == destructors,
158 "exactly-once lifecycle totals"))
160 NOTICE(
"HOSTED-RUNTIME-MODULE-TEST: PASS exhaustion-reload");
165 const bool passed = run();
166 NOTICE(
"HOSTED-RUNTIME-MODULE-TEST: END " << (passed ?
"PASS" :
"FAIL"));
168 return passed ? 0 : 1;
173 ERROR(
"HOSTED-RUNTIME-MODULE-TEST: FAIL boot preparation");
174 NOTICE(
"HOSTED-RUNTIME-MODULE-TEST: END FAIL");
179 thread->setName(
"Runtime module lifecycle tests");
186extern "C" EXPORTED_PUBLIC
void module_runtime_test_event(
unsigned int event) {
193 if (expectHidden &&
KernelElf::instance().globalLookupSymbol(
"module_runtime_fixture_value"))
194 visibleDuringEntry =
true;
204extern "C" EXPORTED_PUBLIC
bool module_runtime_test_entry() {
207MODULE_INFO_NON_UNLOADABLE(
"module-runtime-tests", &entry, &exit);
static KernelElf & instance()
static Scheduler & instance()