PatchworkOS
Loading...
Searching...
No Matches
tests.c
Go to the documentation of this file.
2
3#ifdef TESTING
4
5#include "acpica_tests/all_tests.h"
6
14#include <kernel/acpi/tables.h>
15#include <kernel/log/log.h>
16#include <kernel/sched/timer.h>
17
18#include <stdlib.h>
19#include <string.h>
20#include <sys/list.h>
21
22static uint64_t aml_tests_check_object_leak(void)
23{
25 DEREF_DEFER(root);
26
28 uint64_t rootChildren = aml_object_count_children(root);
29 LOG_INFO("total objects after parsing %llu\n", totalObjects);
30 if (totalObjects != rootChildren + 1)
31 {
32 LOG_ERR("memory leak detected, total objects %llu, but root has %llu children\n", totalObjects, rootChildren);
33 return ERR;
34 }
35 return 0;
36}
37
38static void aml_tests_exception_handler(aml_state_t* state, aml_exception_t code)
39{
40 // Some tests will trigger exceptions to validate that they actually happen, we are then expected to notify
41 // the code that an exception occurred using the \_ERR object.
42 aml_object_t* err = aml_namespace_find(&state->overlay, NULL, 1, AML_NAME('_', 'E', 'R', 'R'));
43 if (err == NULL)
44 {
45 LOG_ERR("test does not contain a valid _ERR object\n");
46 return;
47 }
48 DEREF_DEFER(err);
49
51 if (arg0 == NULL)
52 {
53 return;
54 }
55 DEREF_DEFER(arg0);
56
57 if (aml_integer_set(arg0, code) == ERR)
58 {
59 return;
60 }
61
63 if (arg1 == NULL)
64 {
65 return;
66 }
67 DEREF_DEFER(arg1);
68
69 if (aml_string_set(arg1, aml_exception_to_string(code)) == ERR)
70 {
71 return;
72 }
73
75 if (arg2 == NULL)
76 {
77 return;
78 }
79 DEREF_DEFER(arg2);
80
81 if (aml_integer_set(arg2, 0) == ERR) // Should be the thread id but just set to 0 for now.
82 {
83 return;
84 }
85
86 aml_object_t* args[] = {arg0, arg1, arg2, NULL};
87 aml_object_t* result = aml_method_evaluate(state, &err->method, args);
88 if (result == NULL)
89 {
90 return;
91 }
92 DEREF_DEFER(result);
93
94 if (result->type != AML_INTEGER || result->integer.value != 0)
95 {
96 LOG_ERR("_ERR method did not return 0, returned '%s' instead\n", aml_type_to_string(result->type));
97 return;
98 }
99}
100
101static uint64_t aml_tests_acpica_do_test(const acpica_test_t* test)
102{
103 LOG_INFO("running test '%s'\n", test->name);
104
105 ssdt_t* testAml = (ssdt_t*)test->aml;
106 const uint8_t* end = (const uint8_t*)testAml + testAml->header.length;
107
108 aml_state_t state;
109 if (aml_state_init(&state, NULL) == ERR)
110 {
111 return ERR;
112 }
113
115 DEREF_DEFER(root);
116
117 if (aml_term_list_read(&state, root, testAml->definitionBlock, end, NULL) == ERR)
118 {
119 LOG_ERR("test '%s' failed to parse AML\n", test->name);
120 aml_state_deinit(&state);
121 return ERR;
122 }
123
124 // Set the "Settings number, used to adjust the aslts tests for different releases of ACPICA".
125 // We set it to 6 as that is the latest version as of writing this.
126 aml_object_t* setn = aml_namespace_find(&state.overlay, root, 1, AML_NAME('S', 'E', 'T', 'N'));
127 if (setn == NULL)
128 {
129 LOG_ERR("test '%s' does not contain a valid SETN object\n", test->name);
130 aml_state_deinit(&state);
131 return ERR;
132 }
133 DEREF_DEFER(setn);
134
135 if (aml_integer_set(setn, 6) == ERR)
136 {
137 LOG_ERR("test '%s' failed to set SETN value\n", test->name);
138 aml_state_deinit(&state);
139 return ERR;
140 }
141
142 // We dont use the \MAIN method directly instead we use the \MN01 method which enables "slack mode".
143 // Basically, certain features that would normally just result in a crash are allowed in slack mode, for example
144 // implicit returns, which some firmware depends on. See section 5.2 of the ACPICA reference for more details.
145 aml_object_t* mainObj = aml_namespace_find(&state.overlay, root, 1, AML_NAME('M', 'N', '0', '1'));
146 if (mainObj == NULL || mainObj->type != AML_METHOD)
147 {
148 LOG_ERR("test '%s' does not contain a valid method\n", test->name);
149 aml_state_deinit(&state);
150 return ERR;
151 }
152 DEREF_DEFER(mainObj);
153
154 aml_object_t* result = aml_method_evaluate(&state, &mainObj->method, NULL);
155 if (result == NULL)
156 {
157 LOG_ERR("test '%s' method evaluation failed\n", test->name);
158 aml_state_deinit(&state);
159 return ERR;
160 }
161 DEREF_DEFER(result);
162
163 aml_state_deinit(&state);
164
165 if (result->type != AML_INTEGER)
166 {
167 LOG_ERR("test '%s' method did not return an integer\n", test->name);
168 return ERR;
169 }
170
171 if (result->integer.value != 0)
172 {
173 LOG_ERR("test '%s' failed, returned %llu\n", test->name, result->integer.value);
174 return ERR;
175 }
176
177 LOG_INFO("test '%s' passed\n", test->name);
178 return 0;
179}
180
181static uint64_t aml_tests_acpica_run_all(void)
182{
183 if (aml_exception_register(aml_tests_exception_handler) == ERR)
184 {
185 return ERR;
186 }
187
188 for (uint32_t i = 0; i < ACPICA_TEST_COUNT; i++)
189 {
190 const acpica_test_t* test = &acpicaTests[i];
191 if (aml_tests_acpica_do_test(test) == ERR)
192 {
193 aml_exception_unregister(aml_tests_exception_handler);
194 return ERR;
195 }
196 }
197 aml_exception_unregister(aml_tests_exception_handler);
198 return 0;
199}
200
201uint64_t aml_tests_post_init(void)
202{
203 LOG_INFO("running post init tests\n");
205
206 uint64_t startingObjects = aml_object_get_total_count();
207
208 if (aml_tests_acpica_run_all() == ERR)
209 {
210 // For now this is definetly going to fail as we havent implemented everything yet.
211 // So just log it and continue.
212 LOG_WARN("ACPICA tests failed, this is expected until more AML features are implemented\n");
213 // return ERR;
214 }
215
216 clock_t end = timer_uptime();
217
218 aml_tests_perf_report();
219
220 if (startingObjects != aml_object_get_total_count())
221 {
222 LOG_ERR("memory leak detected, total objects before test %llu, after test %llu\n", startingObjects,
224 return ERR;
225 }
226
227 LOG_INFO("post init tests passed in %llums\n", (end - start) * 1000 / CLOCKS_PER_SEC);
228 return 0;
229}
230
231uint64_t aml_tests_post_parse_all(void)
232{
233 if (aml_tests_check_object_leak() == ERR)
234 {
235 return ERR;
236 }
237
238 LOG_INFO("post parse all tests passed\n");
239 return 0;
240}
241
242typedef struct
243{
244 list_entry_t entry;
245 aml_token_num_t token;
247 clock_t childTime;
248} aml_perf_stack_entry_t;
249
250static clock_t timeTakenPerToken[AML_MAX_TOKEN] = {0};
251static uint64_t tokenOccurrences[AML_MAX_TOKEN] = {0};
252static list_t perfStack = LIST_CREATE(perfStack);
253
254void aml_tests_perf_start(aml_token_t* token)
255{
256 if (token->num >= AML_MAX_TOKEN)
257 {
258 return;
259 }
260
261 aml_perf_stack_entry_t* entry = malloc(sizeof(aml_perf_stack_entry_t));
262 if (entry == NULL)
263 {
264 LOG_ERR("Performance profiler stack allocation failed\n");
265 return;
266 }
267
268 list_entry_init(&entry->entry);
269 entry->token = token->num;
270 entry->startTime = timer_uptime();
271 entry->childTime = 0;
272
273 list_push(&perfStack, &entry->entry);
274
275 tokenOccurrences[token->num]++;
276}
277
278void aml_tests_perf_end(void)
279{
280 if (list_is_empty(&perfStack))
281 {
282 LOG_ERR("Performance profiler stack underflow\n");
283 return;
284 }
285
286 aml_perf_stack_entry_t* entry = CONTAINER_OF_SAFE(list_pop(&perfStack), aml_perf_stack_entry_t, entry);
287
288 clock_t totalTime = timer_uptime() - entry->startTime;
289 clock_t exclusiveTime = (entry->childTime >= totalTime) ? 0 : (totalTime - entry->childTime);
290 if (entry->token < AML_MAX_TOKEN)
291 {
292 timeTakenPerToken[entry->token] += exclusiveTime;
293 }
294
295 if (!list_is_empty(&perfStack))
296 {
297 aml_perf_stack_entry_t* perfStackTop = CONTAINER_OF(list_last(&perfStack), aml_perf_stack_entry_t, entry);
298 perfStackTop->childTime += totalTime;
299 }
300
301 free(entry);
302}
303
304void aml_tests_perf_report(void)
305{
306 if (!list_is_empty(&perfStack))
307 {
308 LOG_WARN("Performance report called with %d unclosed measurements\n", list_length(&perfStack));
309 while (!list_is_empty(&perfStack))
310 {
311 aml_perf_stack_entry_t* entry = CONTAINER_OF_SAFE(list_pop(&perfStack), aml_perf_stack_entry_t, entry);
312 free(entry);
313 }
314 }
315
316 typedef struct
317 {
318 aml_token_num_t tokenNum;
320 } token_time_pair_t;
321
322 token_time_pair_t sorted[AML_MAX_TOKEN];
323 uint32_t count = 0;
324
325 for (uint32_t i = 0; i < AML_MAX_TOKEN; i++)
326 {
327 if (timeTakenPerToken[i] > 0)
328 {
329 sorted[count].tokenNum = i;
330 sorted[count].time = timeTakenPerToken[i];
331 count++;
332 }
333 }
334
335 for (uint32_t i = 1; i < count; i++)
336 {
337 token_time_pair_t key = sorted[i];
338 int32_t j = i - 1;
339
340 while (j >= 0 && sorted[j].time < key.time)
341 {
342 sorted[j + 1] = sorted[j];
343 j--;
344 }
345 sorted[j + 1] = key;
346 }
347
348 LOG_INFO("performance report:\n");
349 for (uint32_t i = 0; i < count; i++)
350 {
351 LOG_INFO(" %s: total=%llums, occurrences=%llu, avg=%lluns\n", aml_token_lookup(sorted[i].tokenNum)->name,
352 sorted[i].time / (CLOCKS_PER_SEC / 1000), tokenOccurrences[sorted[i].tokenNum],
353 sorted[i].time / tokenOccurrences[sorted[i].tokenNum]);
354 }
355
356 for (uint32_t i = 0; i < AML_MAX_TOKEN; i++)
357 {
358 timeTakenPerToken[i] = 0;
359 }
360}
361
362#endif
#define CLOCKS_PER_SEC
Definition clock_t.h:15
static clock_t startTime
Definition clock.c:5
uint64_t aml_term_list_read(aml_state_t *state, aml_object_t *scope, const uint8_t *start, const uint8_t *end, aml_term_list_ctx_t *parentCtx)
Reads a TermList structure from the AML byte stream.
Definition term.c:216
const char * aml_exception_to_string(aml_exception_t code)
Converts an AML exception code to a string.
Definition exception.c:54
void aml_exception_unregister(aml_exception_handler_t handler)
Unregisters an AML exception handler.
Definition exception.c:99
uint64_t aml_exception_register(aml_exception_handler_t handler)
Registers an AML exception handler.
Definition exception.c:66
aml_exception_t
AML exception codes.
Definition exception.h:29
aml_object_t * aml_method_evaluate(aml_state_t *parentState, aml_method_obj_t *method, aml_object_t **args)
Evaluate a method with the given arguments.
Definition method.c:11
aml_object_t * aml_namespace_find(aml_namespace_overlay_t *overlay, aml_object_t *start, uint64_t nameCount,...)
Find an object in the namespace heirarchy by name segments.
Definition namespace.c:129
#define AML_NAME(a, b, c, d)
Macro to create an aml_name_t from 4 characters.
Definition namespace.h:112
aml_object_t * aml_namespace_get_root(void)
Get the root object of the namespace heirarchy.
Definition namespace.c:86
uint64_t aml_integer_set(aml_object_t *object, aml_integer_t value)
Set a object as an integer with the given value and bit width.
Definition object.c:708
uint64_t aml_object_count_children(aml_object_t *parent)
Recursively count how many children an object has.
Definition object.c:264
aml_object_t * aml_object_new(void)
Allocate a new ACPI object.
Definition object.c:54
uint64_t aml_string_set(aml_object_t *object, const char *str)
Set a object as a string with the given value.
Definition object.c:988
uint64_t aml_object_get_total_count(void)
Get the total amount of allocated ACPI objects.
Definition object.c:24
@ AML_METHOD
Definition object.h:75
@ AML_INTEGER
Definition object.h:65
uint64_t aml_state_init(aml_state_t *state, aml_object_t **args)
Initialize an AML state.
Definition state.c:8
void aml_state_deinit(aml_state_t *state)
Deinitialize an AML state.
Definition state.c:79
const char * aml_type_to_string(aml_type_t type)
Convert an aml data type to a string.
Definition to_string.c:5
aml_token_num_t
Token numbers.
Definition token.h:35
static const aml_token_props_t * aml_token_lookup(aml_token_num_t num)
Lookup token properties.
Definition token.h:278
@ AML_MAX_TOKEN
Definition token.h:205
#define LOG_ERR(format,...)
Definition log.h:89
#define LOG_WARN(format,...)
Definition log.h:88
#define LOG_INFO(format,...)
Definition log.h:87
clock_t timer_uptime(void)
Time since boot.
Definition timer.c:73
#define DEREF_DEFER(ptr)
RAII-style cleanup for scoped references.
Definition ref.h:54
static uint64_t list_length(list_t *list)
Gets the length of the list.
Definition list.h:248
static list_entry_t * list_last(list_t *list)
Gets the last entry in the list without removing it.
Definition list.h:400
#define LIST_CREATE(name)
Creates a list initializer.
Definition list.h:176
static void list_push(list_t *list, list_entry_t *entry)
Pushes an entry to the end of the list.
Definition list.h:345
static bool list_is_empty(list_t *list)
Checks if a list is empty.
Definition list.h:229
static void list_entry_init(list_entry_t *entry)
Initializes a list entry.
Definition list.h:184
static list_entry_t * list_pop(list_t *list)
Pops the first entry from the list.
Definition list.h:361
#define NULL
Pointer error value.
Definition NULL.h:23
#define ERR
Integer error value.
Definition ERR.h:17
#define CONTAINER_OF(ptr, type, member)
Container of macro.
#define CONTAINER_OF_SAFE(ptr, type, member)
Safe container of macro.
__UINT64_TYPE__ clock_t
A nanosecond time.
Definition clock_t.h:13
static uint64_t totalObjects
Definition object.c:17
static void start()
Definition main.c:542
static atomic_long count
Definition main.c:9
__UINT32_TYPE__ uint32_t
Definition stdint.h:15
__INT32_TYPE__ int32_t
Definition stdint.h:14
__UINT64_TYPE__ uint64_t
Definition stdint.h:17
__UINT8_TYPE__ uint8_t
Definition stdint.h:11
_PUBLIC void * malloc(size_t size)
Definition malloc.c:5
_PUBLIC void free(void *ptr)
Definition free.c:11
aml_integer_t value
Definition object.h:266
ACPI object.
Definition object.h:425
aml_integer_obj_t integer
Definition object.h:435
aml_method_obj_t method
Definition object.h:437
AML State.
Definition state.h:25
aml_namespace_overlay_t overlay
Holds any named objects created during parsing.
Definition state.h:30
Token.
Definition token.h:253
aml_token_num_t num
Definition token.h:254
A entry in a doubly linked list.
Definition list.h:38
A doubly linked list.
Definition list.h:51
Secondary System Description Table.
Definition tables.h:228
_PUBLIC time_t time(time_t *timer)
Definition time.c:5