1 | #include <uacpi/types.h> |
2 | #include <uacpi/internal/types.h> |
3 | #include <uacpi/internal/stdlib.h> |
4 | #include <uacpi/internal/shareable.h> |
5 | #include <uacpi/internal/dynamic_array.h> |
6 | #include <uacpi/internal/log.h> |
7 | #include <uacpi/internal/namespace.h> |
8 | #include <uacpi/kernel_api.h> |
9 | |
10 | const uacpi_char *uacpi_object_type_to_string(uacpi_object_type type) |
11 | { |
12 | switch (type) { |
13 | case UACPI_OBJECT_UNINITIALIZED: |
14 | return "Uninitialized" ; |
15 | case UACPI_OBJECT_INTEGER: |
16 | return "Integer" ; |
17 | case UACPI_OBJECT_STRING: |
18 | return "String" ; |
19 | case UACPI_OBJECT_BUFFER: |
20 | return "Buffer" ; |
21 | case UACPI_OBJECT_PACKAGE: |
22 | return "Package" ; |
23 | case UACPI_OBJECT_FIELD_UNIT: |
24 | return "Field Unit" ; |
25 | case UACPI_OBJECT_DEVICE: |
26 | return "Device" ; |
27 | case UACPI_OBJECT_EVENT: |
28 | return "Event" ; |
29 | case UACPI_OBJECT_REFERENCE: |
30 | return "Reference" ; |
31 | case UACPI_OBJECT_BUFFER_INDEX: |
32 | return "Buffer Index" ; |
33 | case UACPI_OBJECT_METHOD: |
34 | return "Method" ; |
35 | case UACPI_OBJECT_MUTEX: |
36 | return "Mutex" ; |
37 | case UACPI_OBJECT_OPERATION_REGION: |
38 | return "Operation Region" ; |
39 | case UACPI_OBJECT_POWER_RESOURCE: |
40 | return "Power Resource" ; |
41 | case UACPI_OBJECT_PROCESSOR: |
42 | return "Processor" ; |
43 | case UACPI_OBJECT_THERMAL_ZONE: |
44 | return "Thermal Zone" ; |
45 | case UACPI_OBJECT_BUFFER_FIELD: |
46 | return "Buffer Field" ; |
47 | case UACPI_OBJECT_DEBUG: |
48 | return "Debug" ; |
49 | default: |
50 | return "<Invalid type>" ; |
51 | } |
52 | } |
53 | |
54 | const uacpi_char *uacpi_address_space_to_string( |
55 | enum uacpi_address_space space |
56 | ) |
57 | { |
58 | switch (space) { |
59 | case UACPI_ADDRESS_SPACE_SYSTEM_MEMORY: |
60 | return "SystemMemory" ; |
61 | case UACPI_ADDRESS_SPACE_SYSTEM_IO: |
62 | return "SystemIO" ; |
63 | case UACPI_ADDRESS_SPACE_PCI_CONFIG: |
64 | return "PCI_Config" ; |
65 | case UACPI_ADDRESS_SPACE_EMBEDDED_CONTROLLER: |
66 | return "EmbeddedControl" ; |
67 | case UACPI_ADDRESS_SPACE_SMBUS: |
68 | return "SMBus" ; |
69 | case UACPI_ADDRESS_SPACE_SYSTEM_CMOS: |
70 | return "SystemCMOS" ; |
71 | case UACPI_ADDRESS_SPACE_PCI_BAR_TARGET: |
72 | return "PciBarTarget" ; |
73 | case UACPI_ADDRESS_SPACE_IPMI: |
74 | return "IPMI" ; |
75 | case UACPI_ADDRESS_SPACE_GENERAL_PURPOSE_IO: |
76 | return "GeneralPurposeIO" ; |
77 | case UACPI_ADDRESS_SPACE_GENERIC_SERIAL_BUS: |
78 | return "GenericSerialBus" ; |
79 | case UACPI_ADDRESS_SPACE_PCC: |
80 | return "PCC" ; |
81 | case UACPI_ADDRESS_SPACE_PRM: |
82 | return "PRM" ; |
83 | case UACPI_ADDRESS_SPACE_FFIXEDHW: |
84 | return "FFixedHW" ; |
85 | case UACPI_ADDRESS_SPACE_TABLE_DATA: |
86 | return "TableData" ; |
87 | default: |
88 | return "<vendor specific>" ; |
89 | } |
90 | } |
91 | |
92 | static uacpi_bool buffer_alloc(uacpi_object *obj, uacpi_size initial_size) |
93 | { |
94 | uacpi_buffer *buf; |
95 | |
96 | buf = uacpi_kernel_calloc(count: 1, size: sizeof(uacpi_buffer)); |
97 | if (uacpi_unlikely(buf == UACPI_NULL)) |
98 | return UACPI_FALSE; |
99 | |
100 | uacpi_shareable_init(buf); |
101 | |
102 | if (initial_size) { |
103 | buf->data = uacpi_kernel_alloc(size: initial_size); |
104 | if (uacpi_unlikely(buf->data == UACPI_NULL)) { |
105 | uacpi_free(buf, sizeof(*buf)); |
106 | return UACPI_FALSE; |
107 | } |
108 | |
109 | buf->size = initial_size; |
110 | } |
111 | |
112 | obj->buffer = buf; |
113 | return UACPI_TRUE; |
114 | } |
115 | |
116 | static uacpi_bool empty_buffer_or_string_alloc(uacpi_object *object) |
117 | { |
118 | return buffer_alloc(obj: object, initial_size: 0); |
119 | } |
120 | |
121 | uacpi_bool uacpi_package_fill(uacpi_package *pkg, uacpi_size num_elements) |
122 | { |
123 | uacpi_size i; |
124 | |
125 | pkg->objects = uacpi_kernel_calloc(count: num_elements, size: sizeof(uacpi_handle)); |
126 | if (uacpi_unlikely(pkg->objects == UACPI_NULL)) |
127 | return UACPI_FALSE; |
128 | |
129 | pkg->count = num_elements; |
130 | for (i = 0; i < num_elements; ++i) { |
131 | pkg->objects[i] = uacpi_create_object(type: UACPI_OBJECT_UNINITIALIZED); |
132 | |
133 | if (uacpi_unlikely(pkg->objects[i] == UACPI_NULL)) |
134 | return UACPI_FALSE; |
135 | } |
136 | |
137 | return UACPI_TRUE; |
138 | } |
139 | |
140 | static uacpi_bool package_alloc(uacpi_object *obj, uacpi_size initial_size) |
141 | { |
142 | uacpi_package *pkg; |
143 | |
144 | pkg = uacpi_kernel_calloc(count: 1, size: sizeof(uacpi_package)); |
145 | if (uacpi_unlikely(pkg == UACPI_NULL)) |
146 | return UACPI_FALSE; |
147 | |
148 | uacpi_shareable_init(pkg); |
149 | |
150 | if (initial_size) { |
151 | if (uacpi_unlikely(!uacpi_package_fill(pkg, initial_size))) { |
152 | uacpi_free(pkg, sizeof(*pkg)); |
153 | return UACPI_FALSE; |
154 | } |
155 | } |
156 | |
157 | obj->package = pkg; |
158 | return UACPI_TRUE; |
159 | } |
160 | |
161 | static uacpi_bool empty_package_alloc(uacpi_object *object) |
162 | { |
163 | return package_alloc(obj: object, initial_size: 0); |
164 | } |
165 | |
166 | uacpi_mutex *uacpi_create_mutex(void) |
167 | { |
168 | uacpi_mutex *mutex; |
169 | |
170 | mutex = uacpi_kernel_calloc(count: 1, size: sizeof(uacpi_mutex)); |
171 | if (uacpi_unlikely(mutex == UACPI_NULL)) |
172 | return UACPI_NULL; |
173 | |
174 | mutex->owner = UACPI_THREAD_ID_NONE; |
175 | |
176 | mutex->handle = uacpi_kernel_create_mutex(); |
177 | if (mutex->handle == UACPI_NULL) { |
178 | uacpi_free(mutex, sizeof(*mutex)); |
179 | return UACPI_NULL; |
180 | } |
181 | |
182 | uacpi_shareable_init(mutex); |
183 | return mutex; |
184 | } |
185 | |
186 | static uacpi_bool mutex_alloc(uacpi_object *obj) |
187 | { |
188 | obj->mutex = uacpi_create_mutex(); |
189 | return obj->mutex != UACPI_NULL; |
190 | } |
191 | |
192 | static uacpi_bool event_alloc(uacpi_object *obj) |
193 | { |
194 | uacpi_event *event; |
195 | |
196 | event = uacpi_kernel_calloc(count: 1, size: sizeof(uacpi_event)); |
197 | if (uacpi_unlikely(event == UACPI_NULL)) |
198 | return UACPI_FALSE; |
199 | |
200 | event->handle = uacpi_kernel_create_event(); |
201 | if (event->handle == UACPI_NULL) { |
202 | uacpi_free(event, sizeof(*event)); |
203 | return UACPI_FALSE; |
204 | } |
205 | |
206 | uacpi_shareable_init(event); |
207 | obj->event = event; |
208 | |
209 | return UACPI_TRUE; |
210 | } |
211 | |
212 | static uacpi_bool method_alloc(uacpi_object *obj) |
213 | { |
214 | uacpi_control_method *method; |
215 | |
216 | method = uacpi_kernel_calloc(count: 1, size: sizeof(*method)); |
217 | if (uacpi_unlikely(method == UACPI_NULL)) |
218 | return UACPI_FALSE; |
219 | |
220 | uacpi_shareable_init(method); |
221 | obj->method = method; |
222 | |
223 | return UACPI_TRUE; |
224 | } |
225 | |
226 | static uacpi_bool op_region_alloc(uacpi_object *obj) |
227 | { |
228 | uacpi_operation_region *op_region; |
229 | |
230 | op_region = uacpi_kernel_calloc(count: 1, size: sizeof(*op_region)); |
231 | if (uacpi_unlikely(op_region == UACPI_NULL)) |
232 | return UACPI_FALSE; |
233 | |
234 | uacpi_shareable_init(op_region); |
235 | obj->op_region = op_region; |
236 | |
237 | return UACPI_TRUE; |
238 | } |
239 | |
240 | static uacpi_bool field_unit_alloc(uacpi_object *obj) |
241 | { |
242 | uacpi_field_unit *field_unit; |
243 | |
244 | field_unit = uacpi_kernel_calloc(count: 1, size: sizeof(*field_unit)); |
245 | if (uacpi_unlikely(field_unit == UACPI_NULL)) |
246 | return UACPI_FALSE; |
247 | |
248 | uacpi_shareable_init(field_unit); |
249 | obj->field_unit = field_unit; |
250 | |
251 | return UACPI_TRUE; |
252 | } |
253 | |
254 | static uacpi_bool processor_alloc(uacpi_object *obj) |
255 | { |
256 | uacpi_processor *processor; |
257 | |
258 | processor = uacpi_kernel_calloc(count: 1, size: sizeof(*processor)); |
259 | if (uacpi_unlikely(processor == UACPI_NULL)) |
260 | return UACPI_FALSE; |
261 | |
262 | uacpi_shareable_init(processor); |
263 | obj->processor = processor; |
264 | |
265 | return UACPI_TRUE; |
266 | } |
267 | |
268 | static uacpi_bool device_alloc(uacpi_object *obj) |
269 | { |
270 | uacpi_device *device; |
271 | |
272 | device = uacpi_kernel_calloc(count: 1, size: sizeof(*device)); |
273 | if (uacpi_unlikely(device == UACPI_NULL)) |
274 | return UACPI_FALSE; |
275 | |
276 | uacpi_shareable_init(device); |
277 | obj->device = device; |
278 | |
279 | return UACPI_TRUE; |
280 | } |
281 | |
282 | static uacpi_bool thermal_zone_alloc(uacpi_object *obj) |
283 | { |
284 | uacpi_thermal_zone *thermal_zone; |
285 | |
286 | thermal_zone = uacpi_kernel_calloc(count: 1, size: sizeof(*thermal_zone)); |
287 | if (uacpi_unlikely(thermal_zone == UACPI_NULL)) |
288 | return UACPI_FALSE; |
289 | |
290 | uacpi_shareable_init(thermal_zone); |
291 | obj->thermal_zone = thermal_zone; |
292 | |
293 | return UACPI_TRUE; |
294 | } |
295 | |
296 | typedef uacpi_bool (*object_ctor)(uacpi_object *obj); |
297 | |
298 | static object_ctor object_constructor_table[UACPI_OBJECT_MAX_TYPE_VALUE + 1] = { |
299 | [UACPI_OBJECT_STRING] = empty_buffer_or_string_alloc, |
300 | [UACPI_OBJECT_BUFFER] = empty_buffer_or_string_alloc, |
301 | [UACPI_OBJECT_PACKAGE] = empty_package_alloc, |
302 | [UACPI_OBJECT_FIELD_UNIT] = field_unit_alloc, |
303 | [UACPI_OBJECT_MUTEX] = mutex_alloc, |
304 | [UACPI_OBJECT_EVENT] = event_alloc, |
305 | [UACPI_OBJECT_OPERATION_REGION] = op_region_alloc, |
306 | [UACPI_OBJECT_METHOD] = method_alloc, |
307 | [UACPI_OBJECT_PROCESSOR] = processor_alloc, |
308 | [UACPI_OBJECT_DEVICE] = device_alloc, |
309 | [UACPI_OBJECT_THERMAL_ZONE] = thermal_zone_alloc, |
310 | }; |
311 | |
312 | uacpi_object *uacpi_create_object(uacpi_object_type type) |
313 | { |
314 | uacpi_object *ret; |
315 | object_ctor ctor; |
316 | |
317 | ret = uacpi_kernel_calloc(count: 1, size: sizeof(*ret)); |
318 | if (uacpi_unlikely(ret == UACPI_NULL)) |
319 | return ret; |
320 | |
321 | uacpi_shareable_init(ret); |
322 | ret->type = type; |
323 | |
324 | ctor = object_constructor_table[type]; |
325 | if (ctor == UACPI_NULL) |
326 | return ret; |
327 | |
328 | if (uacpi_unlikely(!ctor(ret))) { |
329 | uacpi_free(ret, sizeof(*ret)); |
330 | return UACPI_NULL; |
331 | } |
332 | |
333 | return ret; |
334 | } |
335 | |
336 | static void free_buffer(uacpi_handle handle) |
337 | { |
338 | uacpi_buffer *buf = handle; |
339 | |
340 | if (buf->data != UACPI_NULL) |
341 | /* |
342 | * If buffer has a size of 0 but a valid data pointer it's probably an |
343 | * "empty" buffer allocated by the interpreter in make_null_buffer |
344 | * and its real size is actually 1. |
345 | */ |
346 | uacpi_free(buf->data, UACPI_MAX(buf->size, 1)); |
347 | |
348 | uacpi_free(buf, sizeof(*buf)); |
349 | } |
350 | |
351 | DYNAMIC_ARRAY_WITH_INLINE_STORAGE(free_queue, uacpi_package*, 4) |
352 | DYNAMIC_ARRAY_WITH_INLINE_STORAGE_IMPL(free_queue, uacpi_package*, static) |
353 | |
354 | static uacpi_bool free_queue_push(struct free_queue *queue, uacpi_package *pkg) |
355 | { |
356 | uacpi_package **slot; |
357 | |
358 | slot = free_queue_alloc(arr: queue); |
359 | if (uacpi_unlikely(slot == UACPI_NULL)) |
360 | return UACPI_FALSE; |
361 | |
362 | *slot = pkg; |
363 | return UACPI_TRUE; |
364 | } |
365 | |
366 | static void free_object(uacpi_object *obj); |
367 | |
368 | // No references allowed here, only plain objects |
369 | static void free_plain_no_recurse(uacpi_object *obj, struct free_queue *queue) |
370 | { |
371 | switch (obj->type) { |
372 | case UACPI_OBJECT_PACKAGE: |
373 | if (uacpi_shareable_unref(obj->package) > 1) |
374 | break; |
375 | |
376 | if (uacpi_unlikely(!free_queue_push(queue, |
377 | obj->package))) { |
378 | uacpi_warn( |
379 | "unable to free nested package @%p: not enough memory\n" , |
380 | obj->package |
381 | ); |
382 | } |
383 | |
384 | // Don't call free_object here as that will recurse |
385 | uacpi_free(obj, sizeof(*obj)); |
386 | break; |
387 | default: |
388 | /* |
389 | * This call is guaranteed to not recurse further as we handle |
390 | * recursive cases elsewhere explicitly. |
391 | */ |
392 | free_object(obj); |
393 | } |
394 | } |
395 | |
396 | static void unref_plain_no_recurse(uacpi_object *obj, struct free_queue *queue) |
397 | { |
398 | if (uacpi_shareable_unref(obj) > 1) |
399 | return; |
400 | |
401 | free_plain_no_recurse(obj, queue); |
402 | } |
403 | |
404 | static void unref_chain_no_recurse(uacpi_object *obj, struct free_queue *queue) |
405 | { |
406 | uacpi_object *next_obj = UACPI_NULL; |
407 | |
408 | while (obj) { |
409 | if (obj->type == UACPI_OBJECT_REFERENCE) |
410 | next_obj = obj->inner_object; |
411 | |
412 | if (uacpi_shareable_unref(obj) > 1) |
413 | goto do_next; |
414 | |
415 | if (obj->type == UACPI_OBJECT_REFERENCE) { |
416 | uacpi_free(obj, sizeof(*obj)); |
417 | } else { |
418 | free_plain_no_recurse(obj, queue); |
419 | } |
420 | |
421 | do_next: |
422 | obj = next_obj; |
423 | next_obj = UACPI_NULL; |
424 | } |
425 | } |
426 | |
427 | static void unref_object_no_recurse(uacpi_object *obj, struct free_queue *queue) |
428 | { |
429 | if (obj->type == UACPI_OBJECT_REFERENCE) { |
430 | unref_chain_no_recurse(obj, queue); |
431 | return; |
432 | } |
433 | |
434 | unref_plain_no_recurse(obj, queue); |
435 | } |
436 | |
437 | static void free_package(uacpi_handle handle) |
438 | { |
439 | struct free_queue queue = { 0 }; |
440 | uacpi_package *pkg = handle; |
441 | uacpi_object *obj; |
442 | uacpi_size i; |
443 | |
444 | free_queue_push(queue: &queue, pkg); |
445 | |
446 | while (free_queue_size(arr: &queue) != 0) { |
447 | pkg = *free_queue_last(arr: &queue); |
448 | free_queue_pop(arr: &queue); |
449 | |
450 | /* |
451 | * 1. Unref/free every object in the package. Note that this might add |
452 | * even more packages into the free queue. |
453 | */ |
454 | for (i = 0; i < pkg->count; ++i) { |
455 | obj = pkg->objects[i]; |
456 | unref_object_no_recurse(obj, queue: &queue); |
457 | } |
458 | |
459 | // 2. Release the object array |
460 | uacpi_free(pkg->objects, sizeof(*pkg->objects) * pkg->count); |
461 | |
462 | // 3. Release the package itself |
463 | uacpi_free(pkg, sizeof(*pkg)); |
464 | } |
465 | |
466 | free_queue_clear(arr: &queue); |
467 | } |
468 | |
469 | static void free_mutex(uacpi_handle handle) |
470 | { |
471 | uacpi_mutex *mutex = handle; |
472 | |
473 | uacpi_kernel_free_mutex(mutex->handle); |
474 | uacpi_free(mutex, sizeof(*mutex)); |
475 | } |
476 | |
477 | void uacpi_mutex_unref(uacpi_mutex *mutex) |
478 | { |
479 | if (mutex == UACPI_NULL) |
480 | return; |
481 | |
482 | uacpi_shareable_unref_and_delete_if_last(mutex, do_free: free_mutex); |
483 | } |
484 | |
485 | static void free_event(uacpi_handle handle) |
486 | { |
487 | uacpi_event *event = handle; |
488 | |
489 | uacpi_kernel_free_event(event->handle); |
490 | uacpi_free(event, sizeof(*event)); |
491 | } |
492 | |
493 | static void free_address_space_handler(uacpi_handle handle) |
494 | { |
495 | uacpi_address_space_handler *handler = handle; |
496 | uacpi_free(handler, sizeof(*handler)); |
497 | } |
498 | |
499 | static void free_address_space_handlers( |
500 | uacpi_address_space_handler *handler |
501 | ) |
502 | { |
503 | uacpi_address_space_handler *next_handler; |
504 | |
505 | while (handler) { |
506 | next_handler = handler->next; |
507 | uacpi_shareable_unref_and_delete_if_last( |
508 | handler, do_free: free_address_space_handler |
509 | ); |
510 | handler = next_handler; |
511 | } |
512 | } |
513 | |
514 | static void free_device_notify_handlers(uacpi_device_notify_handler *handler) |
515 | { |
516 | uacpi_device_notify_handler *next_handler; |
517 | |
518 | while (handler) { |
519 | next_handler = handler->next; |
520 | uacpi_free(handler, sizeof(*handler)); |
521 | handler = next_handler; |
522 | } |
523 | } |
524 | |
525 | static void free_handlers(uacpi_handle handle) |
526 | { |
527 | uacpi_handlers *handlers = handle; |
528 | |
529 | free_address_space_handlers(handler: handlers->address_space_head); |
530 | free_device_notify_handlers(handler: handlers->notify_head); |
531 | } |
532 | |
533 | void uacpi_address_space_handler_unref(uacpi_address_space_handler *handler) |
534 | { |
535 | uacpi_shareable_unref_and_delete_if_last( |
536 | handler, do_free: free_address_space_handler |
537 | ); |
538 | } |
539 | |
540 | static void free_op_region(uacpi_handle handle) |
541 | { |
542 | uacpi_operation_region *op_region = handle; |
543 | |
544 | if (uacpi_unlikely(op_region->handler != UACPI_NULL)) { |
545 | uacpi_warn( |
546 | "BUG: attempting to free an opregion@%p with a handler attached\n" , |
547 | op_region |
548 | ); |
549 | } |
550 | |
551 | uacpi_free(op_region, sizeof(*op_region)); |
552 | } |
553 | |
554 | static void free_device(uacpi_handle handle) |
555 | { |
556 | uacpi_device *device = handle; |
557 | free_handlers(handle: device); |
558 | uacpi_free(device, sizeof(*device)); |
559 | } |
560 | |
561 | static void free_processor(uacpi_handle handle) |
562 | { |
563 | uacpi_processor *processor = handle; |
564 | free_handlers(handle: processor); |
565 | uacpi_free(processor, sizeof(*processor)); |
566 | } |
567 | |
568 | static void free_thermal_zone(uacpi_handle handle) |
569 | { |
570 | uacpi_thermal_zone *thermal_zone = handle; |
571 | free_handlers(handle: thermal_zone); |
572 | uacpi_free(thermal_zone, sizeof(*thermal_zone)); |
573 | } |
574 | |
575 | static void free_field_unit(uacpi_handle handle) |
576 | { |
577 | uacpi_field_unit *field_unit = handle; |
578 | |
579 | switch (field_unit->kind) { |
580 | case UACPI_FIELD_UNIT_KIND_NORMAL: |
581 | uacpi_namespace_node_unref(node: field_unit->region); |
582 | break; |
583 | case UACPI_FIELD_UNIT_KIND_BANK: |
584 | uacpi_namespace_node_unref(node: field_unit->bank_region); |
585 | uacpi_shareable_unref_and_delete_if_last( |
586 | field_unit->bank_selection, do_free: free_field_unit |
587 | ); |
588 | break; |
589 | case UACPI_FIELD_UNIT_KIND_INDEX: |
590 | uacpi_shareable_unref_and_delete_if_last( |
591 | field_unit->index, do_free: free_field_unit |
592 | ); |
593 | uacpi_shareable_unref_and_delete_if_last( |
594 | field_unit->data, do_free: free_field_unit |
595 | ); |
596 | break; |
597 | default: |
598 | break; |
599 | } |
600 | |
601 | uacpi_free(field_unit, sizeof(*field_unit)); |
602 | } |
603 | |
604 | static void free_method(uacpi_handle handle) |
605 | { |
606 | uacpi_control_method *method = handle; |
607 | |
608 | uacpi_shareable_unref_and_delete_if_last( |
609 | method->mutex, do_free: free_mutex |
610 | ); |
611 | |
612 | uacpi_free(method, sizeof(*method)); |
613 | } |
614 | |
615 | static void free_object_storage(uacpi_object *obj) |
616 | { |
617 | switch (obj->type) { |
618 | case UACPI_OBJECT_STRING: |
619 | case UACPI_OBJECT_BUFFER: |
620 | uacpi_shareable_unref_and_delete_if_last(obj->buffer, do_free: free_buffer); |
621 | break; |
622 | case UACPI_OBJECT_BUFFER_FIELD: |
623 | uacpi_shareable_unref_and_delete_if_last(obj->buffer_field.backing, |
624 | do_free: free_buffer); |
625 | break; |
626 | case UACPI_OBJECT_BUFFER_INDEX: |
627 | uacpi_shareable_unref_and_delete_if_last(obj->buffer_index.buffer, |
628 | do_free: free_buffer); |
629 | break; |
630 | case UACPI_OBJECT_METHOD: |
631 | uacpi_shareable_unref_and_delete_if_last(obj->method, |
632 | do_free: free_method); |
633 | break; |
634 | case UACPI_OBJECT_PACKAGE: |
635 | uacpi_shareable_unref_and_delete_if_last(obj->package, |
636 | do_free: free_package); |
637 | break; |
638 | case UACPI_OBJECT_FIELD_UNIT: |
639 | uacpi_shareable_unref_and_delete_if_last(obj->field_unit, |
640 | do_free: free_field_unit); |
641 | break; |
642 | case UACPI_OBJECT_MUTEX: |
643 | uacpi_mutex_unref(mutex: obj->mutex); |
644 | break; |
645 | case UACPI_OBJECT_EVENT: |
646 | uacpi_shareable_unref_and_delete_if_last(obj->event, |
647 | do_free: free_event); |
648 | break; |
649 | case UACPI_OBJECT_OPERATION_REGION: |
650 | uacpi_shareable_unref_and_delete_if_last(obj->op_region, |
651 | do_free: free_op_region); |
652 | break; |
653 | case UACPI_OBJECT_PROCESSOR: |
654 | uacpi_shareable_unref_and_delete_if_last(obj->processor, |
655 | do_free: free_processor); |
656 | break; |
657 | case UACPI_OBJECT_DEVICE: |
658 | uacpi_shareable_unref_and_delete_if_last(obj->device, |
659 | do_free: free_device); |
660 | break; |
661 | case UACPI_OBJECT_THERMAL_ZONE: |
662 | uacpi_shareable_unref_and_delete_if_last(obj->thermal_zone, |
663 | do_free: free_thermal_zone); |
664 | break; |
665 | default: |
666 | break; |
667 | } |
668 | } |
669 | |
670 | static void free_object(uacpi_object *obj) |
671 | { |
672 | free_object_storage(obj); |
673 | uacpi_free(obj, sizeof(*obj)); |
674 | } |
675 | |
676 | static void make_chain_bugged(uacpi_object *obj) |
677 | { |
678 | uacpi_warn("object refcount bug, marking chain @%p as bugged\n" , obj); |
679 | |
680 | while (obj) { |
681 | uacpi_make_shareable_bugged(obj); |
682 | |
683 | if (obj->type == UACPI_OBJECT_REFERENCE) |
684 | obj = obj->inner_object; |
685 | else |
686 | obj = UACPI_NULL; |
687 | } |
688 | } |
689 | |
690 | void uacpi_object_ref(uacpi_object *obj) |
691 | { |
692 | uacpi_object *this_obj = obj; |
693 | |
694 | while (obj) { |
695 | if (uacpi_unlikely(uacpi_bugged_shareable(obj))) { |
696 | make_chain_bugged(obj: this_obj); |
697 | return; |
698 | } |
699 | |
700 | uacpi_shareable_ref(obj); |
701 | |
702 | if (obj->type == UACPI_OBJECT_REFERENCE) |
703 | obj = obj->inner_object; |
704 | else |
705 | obj = UACPI_NULL; |
706 | } |
707 | } |
708 | |
709 | static void free_chain(uacpi_object *obj) |
710 | { |
711 | uacpi_object *next_obj = UACPI_NULL; |
712 | |
713 | while (obj) { |
714 | if (obj->type == UACPI_OBJECT_REFERENCE) |
715 | next_obj = obj->inner_object; |
716 | |
717 | if (uacpi_shareable_refcount(obj) == 0) |
718 | free_object(obj); |
719 | |
720 | obj = next_obj; |
721 | next_obj = UACPI_NULL; |
722 | } |
723 | } |
724 | |
725 | void uacpi_object_unref(uacpi_object *obj) |
726 | { |
727 | uacpi_object *this_obj = obj; |
728 | uacpi_u32 parent_refcount; |
729 | |
730 | if (!obj) |
731 | return; |
732 | |
733 | parent_refcount = obj->shareable.reference_count; |
734 | |
735 | while (obj) { |
736 | if (uacpi_unlikely(uacpi_bugged_shareable(obj))) { |
737 | make_chain_bugged(obj: this_obj); |
738 | return; |
739 | } |
740 | |
741 | if (uacpi_unlikely(uacpi_shareable_refcount(obj) < parent_refcount)) { |
742 | make_chain_bugged(obj: this_obj); |
743 | return; |
744 | } |
745 | |
746 | parent_refcount = uacpi_shareable_unref(obj); |
747 | |
748 | if (obj->type == UACPI_OBJECT_REFERENCE) { |
749 | obj = obj->inner_object; |
750 | } else { |
751 | obj = UACPI_NULL; |
752 | } |
753 | } |
754 | |
755 | if (uacpi_shareable_refcount(this_obj) == 0) |
756 | free_chain(obj: this_obj); |
757 | } |
758 | |
759 | static uacpi_status buffer_alloc_and_store( |
760 | uacpi_object *obj, uacpi_size buf_size, |
761 | const void *src, uacpi_size src_size |
762 | ) |
763 | { |
764 | if (uacpi_unlikely(!buffer_alloc(obj, buf_size))) |
765 | return UACPI_STATUS_OUT_OF_MEMORY; |
766 | |
767 | uacpi_memcpy_zerout(dst: obj->buffer->data, src, dst_size: buf_size, src_size); |
768 | return UACPI_STATUS_OK; |
769 | } |
770 | |
771 | static uacpi_status assign_buffer(uacpi_object *dst, uacpi_object *src, |
772 | enum uacpi_assign_behavior behavior) |
773 | { |
774 | if (behavior == UACPI_ASSIGN_BEHAVIOR_SHALLOW_COPY) { |
775 | dst->buffer = src->buffer; |
776 | uacpi_shareable_ref(dst->buffer); |
777 | return UACPI_STATUS_OK; |
778 | } |
779 | |
780 | return buffer_alloc_and_store(obj: dst, buf_size: src->buffer->size, |
781 | src: src->buffer->data, src_size: src->buffer->size); |
782 | } |
783 | |
784 | struct pkg_copy_req { |
785 | uacpi_object *dst; |
786 | uacpi_package *src; |
787 | }; |
788 | |
789 | DYNAMIC_ARRAY_WITH_INLINE_STORAGE(pkg_copy_reqs, struct pkg_copy_req, 2) |
790 | DYNAMIC_ARRAY_WITH_INLINE_STORAGE_IMPL( |
791 | pkg_copy_reqs, struct pkg_copy_req, static |
792 | ) |
793 | |
794 | static uacpi_bool pkg_copy_reqs_push( |
795 | struct pkg_copy_reqs *reqs, |
796 | uacpi_object *dst, uacpi_package *pkg |
797 | ) |
798 | { |
799 | struct pkg_copy_req *req; |
800 | |
801 | req = pkg_copy_reqs_alloc(arr: reqs); |
802 | if (uacpi_unlikely(req == UACPI_NULL)) |
803 | return UACPI_FALSE; |
804 | |
805 | req->dst = dst; |
806 | req->src = pkg; |
807 | |
808 | return UACPI_TRUE; |
809 | } |
810 | |
811 | static uacpi_status deep_copy_package_no_recurse( |
812 | uacpi_object *dst, uacpi_package *src, |
813 | struct pkg_copy_reqs *reqs |
814 | ) |
815 | { |
816 | uacpi_size i; |
817 | uacpi_package *dst_package; |
818 | |
819 | if (uacpi_unlikely(!package_alloc(dst, src->count))) |
820 | return UACPI_STATUS_OUT_OF_MEMORY; |
821 | |
822 | dst->type = UACPI_OBJECT_PACKAGE; |
823 | dst_package = dst->package; |
824 | |
825 | for (i = 0; i < src->count; ++i) { |
826 | uacpi_status st; |
827 | uacpi_object *src_obj = src->objects[i]; |
828 | uacpi_object *dst_obj = dst_package->objects[i]; |
829 | |
830 | // Don't copy the internal package index reference |
831 | if (src_obj->type == UACPI_OBJECT_REFERENCE && |
832 | src_obj->flags == UACPI_REFERENCE_KIND_PKG_INDEX) |
833 | src_obj = src_obj->inner_object; |
834 | |
835 | if (src_obj->type == UACPI_OBJECT_PACKAGE) { |
836 | uacpi_bool ret; |
837 | |
838 | ret = pkg_copy_reqs_push(reqs, dst: dst_obj, pkg: src_obj->package); |
839 | if (uacpi_unlikely(!ret)) |
840 | return UACPI_STATUS_OUT_OF_MEMORY; |
841 | |
842 | continue; |
843 | } |
844 | |
845 | st = uacpi_object_assign(dst: dst_obj, src: src_obj, |
846 | UACPI_ASSIGN_BEHAVIOR_DEEP_COPY); |
847 | if (uacpi_unlikely_error(st)) |
848 | return st; |
849 | } |
850 | |
851 | return UACPI_STATUS_OK; |
852 | } |
853 | |
854 | static uacpi_status deep_copy_package(uacpi_object *dst, uacpi_object *src) |
855 | { |
856 | uacpi_status ret = UACPI_STATUS_OK; |
857 | struct pkg_copy_reqs reqs = { 0 }; |
858 | |
859 | pkg_copy_reqs_push(reqs: &reqs, dst, pkg: src->package); |
860 | |
861 | while (pkg_copy_reqs_size(arr: &reqs) != 0) { |
862 | struct pkg_copy_req req; |
863 | |
864 | req = *pkg_copy_reqs_last(arr: &reqs); |
865 | pkg_copy_reqs_pop(arr: &reqs); |
866 | |
867 | ret = deep_copy_package_no_recurse(dst: req.dst, src: req.src, reqs: &reqs); |
868 | if (uacpi_unlikely_error(ret)) |
869 | break; |
870 | } |
871 | |
872 | pkg_copy_reqs_clear(arr: &reqs); |
873 | return ret; |
874 | } |
875 | |
876 | static uacpi_status assign_mutex(uacpi_object *dst, uacpi_object *src, |
877 | enum uacpi_assign_behavior behavior) |
878 | { |
879 | if (behavior == UACPI_ASSIGN_BEHAVIOR_DEEP_COPY) { |
880 | if (uacpi_likely(mutex_alloc(dst))) { |
881 | dst->mutex->sync_level = src->mutex->sync_level; |
882 | return UACPI_STATUS_OK; |
883 | } |
884 | |
885 | return UACPI_STATUS_OUT_OF_MEMORY; |
886 | } |
887 | |
888 | dst->mutex = src->mutex; |
889 | uacpi_shareable_ref(dst->mutex); |
890 | |
891 | return UACPI_STATUS_OK; |
892 | } |
893 | |
894 | static uacpi_status assign_event(uacpi_object *dst, uacpi_object *src, |
895 | enum uacpi_assign_behavior behavior) |
896 | { |
897 | if (behavior == UACPI_ASSIGN_BEHAVIOR_DEEP_COPY) { |
898 | if (uacpi_likely(event_alloc(dst))) |
899 | return UACPI_STATUS_OK; |
900 | |
901 | return UACPI_STATUS_OUT_OF_MEMORY; |
902 | } |
903 | |
904 | dst->event = src->event; |
905 | uacpi_shareable_ref(dst->event); |
906 | |
907 | return UACPI_STATUS_OK; |
908 | } |
909 | |
910 | static uacpi_status assign_package(uacpi_object *dst, uacpi_object *src, |
911 | enum uacpi_assign_behavior behavior) |
912 | { |
913 | if (behavior == UACPI_ASSIGN_BEHAVIOR_SHALLOW_COPY) { |
914 | dst->package = src->package; |
915 | uacpi_shareable_ref(dst->package); |
916 | return UACPI_STATUS_OK; |
917 | } |
918 | |
919 | return deep_copy_package(dst, src); |
920 | } |
921 | |
922 | void uacpi_object_attach_child(uacpi_object *parent, uacpi_object *child) |
923 | { |
924 | uacpi_u32 refs_to_add; |
925 | |
926 | parent->inner_object = child; |
927 | |
928 | if (uacpi_unlikely(uacpi_bugged_shareable(parent))) { |
929 | make_chain_bugged(obj: child); |
930 | return; |
931 | } |
932 | |
933 | refs_to_add = uacpi_shareable_refcount(parent); |
934 | while (refs_to_add--) |
935 | uacpi_object_ref(obj: child); |
936 | } |
937 | |
938 | void uacpi_object_detach_child(uacpi_object *parent) |
939 | { |
940 | uacpi_u32 refs_to_remove; |
941 | uacpi_object *child; |
942 | |
943 | child = parent->inner_object; |
944 | parent->inner_object = UACPI_NULL; |
945 | |
946 | if (uacpi_unlikely(uacpi_bugged_shareable(parent))) |
947 | return; |
948 | |
949 | refs_to_remove = uacpi_shareable_refcount(parent); |
950 | while (refs_to_remove--) |
951 | uacpi_object_unref(obj: child); |
952 | } |
953 | |
954 | uacpi_status uacpi_object_assign(uacpi_object *dst, uacpi_object *src, |
955 | enum uacpi_assign_behavior behavior) |
956 | { |
957 | uacpi_status ret = UACPI_STATUS_OK; |
958 | |
959 | if (src == dst) |
960 | return ret; |
961 | |
962 | switch (dst->type) { |
963 | case UACPI_OBJECT_REFERENCE: |
964 | uacpi_object_detach_child(parent: dst); |
965 | break; |
966 | case UACPI_OBJECT_STRING: |
967 | case UACPI_OBJECT_BUFFER: |
968 | case UACPI_OBJECT_METHOD: |
969 | case UACPI_OBJECT_PACKAGE: |
970 | case UACPI_OBJECT_MUTEX: |
971 | case UACPI_OBJECT_EVENT: |
972 | case UACPI_OBJECT_PROCESSOR: |
973 | case UACPI_OBJECT_DEVICE: |
974 | case UACPI_OBJECT_THERMAL_ZONE: |
975 | free_object_storage(obj: dst); |
976 | break; |
977 | default: |
978 | break; |
979 | } |
980 | |
981 | switch (src->type) { |
982 | case UACPI_OBJECT_UNINITIALIZED: |
983 | case UACPI_OBJECT_DEBUG: |
984 | break; |
985 | case UACPI_OBJECT_BUFFER: |
986 | case UACPI_OBJECT_STRING: |
987 | dst->flags = src->flags; |
988 | ret = assign_buffer(dst, src, behavior); |
989 | break; |
990 | case UACPI_OBJECT_BUFFER_FIELD: |
991 | dst->buffer_field = src->buffer_field; |
992 | uacpi_shareable_ref(dst->buffer_field.backing); |
993 | break; |
994 | case UACPI_OBJECT_BUFFER_INDEX: |
995 | dst->buffer_index = src->buffer_index; |
996 | uacpi_shareable_ref(dst->buffer_index.buffer); |
997 | break; |
998 | case UACPI_OBJECT_INTEGER: |
999 | dst->integer = src->integer; |
1000 | break; |
1001 | case UACPI_OBJECT_METHOD: |
1002 | dst->method = src->method; |
1003 | uacpi_shareable_ref(dst->method); |
1004 | break; |
1005 | case UACPI_OBJECT_MUTEX: |
1006 | ret = assign_mutex(dst, src, behavior); |
1007 | break; |
1008 | case UACPI_OBJECT_EVENT: |
1009 | ret = assign_event(dst, src, behavior); |
1010 | break; |
1011 | case UACPI_OBJECT_OPERATION_REGION: |
1012 | dst->op_region = src->op_region; |
1013 | uacpi_shareable_ref(dst->op_region); |
1014 | break; |
1015 | case UACPI_OBJECT_PACKAGE: |
1016 | ret = assign_package(dst, src, behavior); |
1017 | break; |
1018 | case UACPI_OBJECT_FIELD_UNIT: |
1019 | dst->field_unit = src->field_unit; |
1020 | uacpi_shareable_ref(dst->field_unit); |
1021 | break; |
1022 | case UACPI_OBJECT_REFERENCE: |
1023 | uacpi_object_attach_child(parent: dst, child: src->inner_object); |
1024 | break; |
1025 | case UACPI_OBJECT_PROCESSOR: |
1026 | dst->processor = src->processor; |
1027 | uacpi_shareable_ref(dst->processor); |
1028 | break; |
1029 | case UACPI_OBJECT_DEVICE: |
1030 | dst->device = src->device; |
1031 | uacpi_shareable_ref(dst->device); |
1032 | break; |
1033 | case UACPI_OBJECT_THERMAL_ZONE: |
1034 | dst->thermal_zone = src->thermal_zone; |
1035 | uacpi_shareable_ref(dst->thermal_zone); |
1036 | break; |
1037 | default: |
1038 | ret = UACPI_STATUS_UNIMPLEMENTED; |
1039 | } |
1040 | |
1041 | if (ret == UACPI_STATUS_OK) |
1042 | dst->type = src->type; |
1043 | |
1044 | return ret; |
1045 | } |
1046 | |
1047 | struct uacpi_object *uacpi_create_internal_reference( |
1048 | enum uacpi_reference_kind kind, uacpi_object *child |
1049 | ) |
1050 | { |
1051 | uacpi_object *ret; |
1052 | |
1053 | ret = uacpi_create_object(type: UACPI_OBJECT_REFERENCE); |
1054 | if (uacpi_unlikely(ret == UACPI_NULL)) |
1055 | return ret; |
1056 | |
1057 | ret->flags = kind; |
1058 | uacpi_object_attach_child(parent: ret, child); |
1059 | return ret; |
1060 | } |
1061 | |
1062 | uacpi_object *uacpi_unwrap_internal_reference(uacpi_object *object) |
1063 | { |
1064 | for (;;) { |
1065 | if (object->type != UACPI_OBJECT_REFERENCE || |
1066 | (object->flags == UACPI_REFERENCE_KIND_REFOF || |
1067 | object->flags == UACPI_REFERENCE_KIND_PKG_INDEX)) |
1068 | return object; |
1069 | |
1070 | object = object->inner_object; |
1071 | } |
1072 | } |
1073 | |