1 /*
2 * Copyright (c) 2021 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #define IOKIT_ENABLE_SHARED_PTR
30
31 #include <IOKit/IOLib.h>
32 #include <IOKit/IOReturn.h>
33
34 #include <libkern/c++/OSArray.h>
35 #include <libkern/c++/OSDictionary.h>
36 #include <libkern/c++/OSNumber.h>
37 #include <libkern/c++/OSString.h>
38 #include <libkern/c++/OSSymbol.h>
39 #include <libkern/c++/OSUnserialize.h>
40 #include <libkern/c++/OSSharedPtr.h>
41 #include <libkern/c++/OSSerialize.h>
42
43 #include <sys/work_interval.h>
44 #include <sys/param.h>
45
46 #include <kern/thread_group.h>
47 #include <kern/work_interval.h>
48 #include <kern/workload_config.h>
49
50 #if DEVELOPMENT || DEBUG
51 #define WLC_LOG(fmt, args...) IOLog("WorkloadConfig: " fmt, ##args)
52 #else
53 #define WLC_LOG(fmt, args...)
54 #endif
55
56 /* Limit criticality offsets. */
57 #define MAX_CRITICALITY_OFFSET 16
58
59
60 /* Plist keys/values. */
61 #define kWorkloadIDTableKey "WorkloadIDTable"
62 #define kRootKey "Root"
63 #define kPhasesKey "Phases"
64 #define kWorkIntervalTypeKey "WorkIntervalType"
65 #define kWorkloadClassKey "WorkloadClass"
66 #define kCriticalityOffsetKey "CriticalityOffset"
67 #define kDefaultPhaseKey "DefaultPhase"
68 #define kFlagsKey "Flags"
69 #define kWorkloadIDConfigurationFlagsKey "WorkloadIDConfigurationFlags"
70
71 #define kDisableWorkloadClassThreadPolicyValue "DisableWorkloadClassThreadPolicy"
72 #define kWIComplexityAllowedValue "ComplexityAllowed"
73
74 #define ARRAY_LEN(x) (sizeof (x) / sizeof (x[0]))
75
76 #if !CONFIG_THREAD_GROUPS
77 #define THREAD_GROUP_FLAGS_EFFICIENT 0
78 #define THREAD_GROUP_FLAGS_APPLICATION 0
79 #define THREAD_GROUP_FLAGS_CRITICAL 0
80 #define THREAD_GROUP_FLAGS_BEST_EFFORT 0
81 #define THREAD_GROUP_FLAGS_ABSENT 0
82 #endif /* CONFIG_THREAD_GROUPS */
83
84 /* BEGIN IGNORE CODESTYLE */
85 static const struct WorkloadClassData {
86 const char *name;
87 UInt32 workIntervalFlags;
88 UInt32 threadGroupFlags;
89 } wlClassData[] = {
90 [WI_CLASS_NONE] =
91 {
92 .name = "NONE",
93 .workIntervalFlags = WORK_INTERVAL_WORKLOAD_ID_HAS_ID,
94 .threadGroupFlags = THREAD_GROUP_FLAGS_ABSENT,
95 },
96 [WI_CLASS_DISCRETIONARY] =
97 {
98 .name = "DISCRETIONARY",
99 .workIntervalFlags = WORK_INTERVAL_WORKLOAD_ID_HAS_ID,
100 .threadGroupFlags = THREAD_GROUP_FLAGS_EFFICIENT,
101 },
102 [WI_CLASS_BEST_EFFORT] =
103 {
104 .name = "BEST_EFFORT",
105 .workIntervalFlags = WORK_INTERVAL_WORKLOAD_ID_HAS_ID,
106 .threadGroupFlags = THREAD_GROUP_FLAGS_BEST_EFFORT,
107 },
108 [WI_CLASS_APPLICATION] =
109 {
110 .name = "APPLICATION",
111 .workIntervalFlags = WORK_INTERVAL_WORKLOAD_ID_HAS_ID,
112 .threadGroupFlags = THREAD_GROUP_FLAGS_APPLICATION,
113 },
114 [WI_CLASS_SYSTEM] =
115 {
116 .name = "SYSTEM",
117 .workIntervalFlags = WORK_INTERVAL_WORKLOAD_ID_HAS_ID,
118 .threadGroupFlags = 0,
119 },
120 [WI_CLASS_SYSTEM_CRITICAL] =
121 {
122 .name = "SYSTEM_CRITICAL",
123 .workIntervalFlags = WORK_INTERVAL_WORKLOAD_ID_HAS_ID,
124 .threadGroupFlags = THREAD_GROUP_FLAGS_CRITICAL,
125 },
126 [WI_CLASS_REALTIME] =
127 {
128 .name = "REALTIME",
129 .workIntervalFlags = WORK_INTERVAL_WORKLOAD_ID_HAS_ID |
130 WORK_INTERVAL_WORKLOAD_ID_RT_ALLOWED,
131 .threadGroupFlags = 0,
132 },
133 [WI_CLASS_REALTIME_CRITICAL] =
134 {
135 .name = "REALTIME_CRITICAL",
136 .workIntervalFlags = WORK_INTERVAL_WORKLOAD_ID_HAS_ID |
137 WORK_INTERVAL_WORKLOAD_ID_RT_ALLOWED |
138 WORK_INTERVAL_WORKLOAD_ID_RT_CRITICAL,
139 .threadGroupFlags = THREAD_GROUP_FLAGS_CRITICAL,
140 },
141 };
142 /* END IGNORE CODESTYLE */
143
144 struct FlagMap {
145 const char *str;
146 UInt32 flags;
147 };
148
149 static inline IOReturn
stringToFlags(const OSString & str,UInt32 & flags,const struct FlagMap * map,size_t mapLen)150 stringToFlags(const OSString &str, UInt32 &flags, const struct FlagMap *map,
151 size_t mapLen)
152 {
153 for (size_t i = 0; i < mapLen; i++) {
154 if (str.isEqualTo(map[i].str)) {
155 flags = map[i].flags;
156 return kIOReturnSuccess;
157 }
158 }
159
160 return kIOReturnNotFound;
161 }
162
163 static inline IOReturn
flagsToString(const UInt32 flags,OSSharedPtr<OSString> & str,const struct FlagMap * map,size_t mapLen)164 flagsToString(const UInt32 flags, OSSharedPtr<OSString> &str, const struct FlagMap *map,
165 size_t mapLen)
166 {
167 for (size_t i = 0; i < mapLen; i++) {
168 if (flags == map[i].flags) {
169 str = OSString::withCStringNoCopy(map[i].str);
170 return kIOReturnSuccess;
171 }
172 }
173
174 return kIOReturnNotFound;
175 }
176
177 /* BEGIN IGNORE CODESTYLE */
178 static const struct FlagMap typeMap[] = {
179 {
180 .str = "DEFAULT",
181 .flags = WORK_INTERVAL_TYPE_DEFAULT |
182 WORK_INTERVAL_FLAG_UNRESTRICTED,
183 },
184 {
185 .str = "COREAUDIO",
186 .flags = WORK_INTERVAL_TYPE_COREAUDIO |
187 WORK_INTERVAL_FLAG_ENABLE_AUTO_JOIN |
188 WORK_INTERVAL_FLAG_ENABLE_DEFERRED_FINISH,
189 },
190 {
191 .str = "COREANIMATION",
192 .flags = WORK_INTERVAL_TYPE_COREANIMATION,
193 },
194 {
195 .str = "CA_RENDER_SERVER",
196 .flags = WORK_INTERVAL_TYPE_CA_RENDER_SERVER,
197 },
198 {
199 .str = "FRAME_COMPOSITOR",
200 .flags = WORK_INTERVAL_TYPE_FRAME_COMPOSITOR,
201 },
202 {
203 .str = "CA_CLIENT",
204 .flags = WORK_INTERVAL_TYPE_CA_CLIENT |
205 WORK_INTERVAL_FLAG_UNRESTRICTED,
206 },
207 {
208 .str = "HID_DELIVERY",
209 .flags = WORK_INTERVAL_TYPE_HID_DELIVERY,
210 },
211 {
212 .str = "COREMEDIA",
213 .flags = WORK_INTERVAL_TYPE_COREMEDIA,
214 },
215 {
216 .str = "ARKIT",
217 .flags = WORK_INTERVAL_TYPE_ARKIT |
218 WORK_INTERVAL_FLAG_FINISH_AT_DEADLINE,
219 },
220 {
221 .str = "AUDIO_CLIENT",
222 .flags = WORK_INTERVAL_TYPE_COREAUDIO |
223 WORK_INTERVAL_FLAG_UNRESTRICTED |
224 WORK_INTERVAL_FLAG_ENABLE_AUTO_JOIN |
225 WORK_INTERVAL_FLAG_ENABLE_DEFERRED_FINISH
226 },
227 };
228 /* END IGNORE CODESTYLE */
229
230 static IOReturn
unparseWorkIntervalType(const UInt32 createFlags,OSSharedPtr<OSString> & typeStr)231 unparseWorkIntervalType(const UInt32 createFlags, OSSharedPtr<OSString> &typeStr)
232 {
233 IOReturn ret = flagsToString(createFlags, typeStr, typeMap,
234 ARRAY_LEN(typeMap));
235 if (ret != kIOReturnSuccess) {
236 WLC_LOG("unrecognised create flags: 0x%x\n", createFlags);
237 }
238
239 return ret;
240 }
241
242 static IOReturn
parseWorkIntervalType(const OSSymbol & id,const OSObject * typeObj,UInt32 & createFlags)243 parseWorkIntervalType(const OSSymbol &id, const OSObject *typeObj, UInt32 &createFlags)
244 {
245 OSSharedPtr<OSString> defaultIntervalType = OSString::withCString("DEFAULT");
246
247 const OSString *typeStr = OSDynamicCast(OSString, typeObj);
248 if (typeStr == nullptr) {
249 typeStr = defaultIntervalType.get();
250 }
251
252 IOReturn ret = stringToFlags(*typeStr, createFlags, typeMap,
253 ARRAY_LEN(typeMap));
254 if (ret != kIOReturnSuccess) {
255 WLC_LOG("unrecognised \"" kWorkIntervalTypeKey "\": \"%s\"\n",
256 typeStr->getCStringNoCopy());
257 }
258
259 return ret;
260 }
261
262 static IOReturn
parseWorkloadClass(const OSSymbol & id,const OSObject * wlClassObj,wi_class_t & wiClass)263 parseWorkloadClass(const OSSymbol &id, const OSObject *wlClassObj, wi_class_t &wiClass)
264 {
265 const OSString *wlClass = OSDynamicCast(OSString, wlClassObj);
266 if (wlClass == nullptr) {
267 wiClass = WI_CLASS_NONE;
268 return kIOReturnSuccess;
269 }
270
271 for (size_t i = 0; i < ARRAY_LEN(wlClassData); i++) {
272 if (wlClassData[i].name != nullptr &&
273 wlClass->isEqualTo(wlClassData[i].name)) {
274 wiClass = (wi_class_t)i;
275 return kIOReturnSuccess;
276 }
277 }
278
279 WLC_LOG("%s: unknown %s: \"%s\"\n", id.getCStringNoCopy(),
280 kWorkloadClassKey, wlClass->getCStringNoCopy());
281 return kIOReturnError;
282 }
283
284 static IOReturn
parseCriticalityOffset(const OSSymbol & id,const wi_class_t wiClass,const OSObject * cOffsetObj,uint8_t & criticalityOffset)285 parseCriticalityOffset(const OSSymbol &id, const wi_class_t wiClass,
286 const OSObject *cOffsetObj, uint8_t &criticalityOffset)
287 {
288 if (wiClass != WI_CLASS_SYSTEM_CRITICAL &&
289 wiClass != WI_CLASS_REALTIME_CRITICAL &&
290 wiClass != WI_CLASS_BEST_EFFORT) {
291 criticalityOffset = 0;
292 return kIOReturnSuccess;
293 }
294
295 const OSNumber *cOffset = OSDynamicCast(OSNumber, cOffsetObj);
296 if (cOffset == nullptr) {
297 criticalityOffset = 0;
298 return kIOReturnSuccess;
299 }
300
301 UInt64 criticalityOffset64 = cOffset->unsigned64BitValue();
302 const int nBytes = cOffset->numberOfBytes();
303 if (nBytes <= sizeof(criticalityOffset64) &&
304 criticalityOffset64 < MAX_CRITICALITY_OFFSET) {
305 criticalityOffset = (uint8_t)criticalityOffset64;
306 return kIOReturnSuccess;
307 }
308
309 WLC_LOG("%s: criticality offset too large\n", id.getCStringNoCopy());
310 return kIOReturnError;
311 }
312
313 static IOReturn
parseFlags(const OSSymbol & id,const OSObject * flagsObj,UInt32 & threadGroupFlags,UInt32 & workIntervalFlags)314 parseFlags(const OSSymbol &id, const OSObject *flagsObj, UInt32 &threadGroupFlags,
315 UInt32 &workIntervalFlags)
316 {
317 /* Optional, so just carry on if not found. */
318 if (flagsObj == nullptr) {
319 return kIOReturnSuccess;
320 }
321
322 OSArray *flags = OSDynamicCast(OSArray, flagsObj);
323 if (flags == nullptr) {
324 WLC_LOG("failed to parse \"" kFlagsKey "\"\n");
325 return kIOReturnError;
326 }
327
328 /* BEGIN IGNORE CODESTYLE */
329 __block IOReturn ret = kIOReturnSuccess;
330 flags->iterateObjects(^bool (OSObject *object) {
331 const OSString *flag = OSDynamicCast(OSString, object);
332 if (flag == nullptr) {
333 WLC_LOG("%s: non-string flag found\n", id.getCStringNoCopy());
334 ret = kIOReturnError;
335 return true;
336
337 }
338
339 /* Ignore unknown flags. */
340 if (flag->isEqualTo(kWIComplexityAllowedValue)) {
341 workIntervalFlags |= WORK_INTERVAL_WORKLOAD_ID_COMPLEXITY_ALLOWED;
342 }
343
344 return false;
345 });
346 /* END IGNORE CODESTYLE */
347
348 return ret;
349 }
350
351 static
352 IOReturn
parsePhases(workload_config_ctx_t * ctx,const OSSymbol & id,OSObject * phasesObj)353 parsePhases(workload_config_ctx_t *ctx, const OSSymbol &id, OSObject *phasesObj)
354 {
355 __block IOReturn ret = kIOReturnError;
356
357 OSDictionary *phases = OSDynamicCast(OSDictionary, phasesObj);
358 if (phases == nullptr) {
359 WLC_LOG("%s: failed to find dictionary for \"" kPhasesKey "\"\n",
360 id.getCStringNoCopy());
361 return kIOReturnError;
362 }
363
364 /* There should be at least one phase described. */
365 ret = kIOReturnError;
366
367 /* BEGIN IGNORE CODESTYLE */
368 phases->iterateObjects(^bool (const OSSymbol *phase, OSObject *value) {
369 const OSDictionary *dict = OSDynamicCast(OSDictionary, value);
370 if (dict == nullptr) {
371 WLC_LOG("%s: failed to find dictionary for \"%s\" phase\n",
372 id.getCStringNoCopy(), phase->getCStringNoCopy());
373 ret = kIOReturnError;
374 return true;
375 }
376
377 UInt32 createFlags = 0;
378 ret = parseWorkIntervalType(id, dict->getObject(kWorkIntervalTypeKey),
379 createFlags);
380 if (ret != kIOReturnSuccess) {
381 return true;
382 }
383
384 wi_class_t wiClass = WI_CLASS_NONE;
385 ret = parseWorkloadClass(id, dict->getObject(kWorkloadClassKey), wiClass);
386 if (ret != kIOReturnSuccess) {
387 return true;
388 }
389 const struct WorkloadClassData classData = wlClassData[wiClass];
390
391 uint8_t criticalityOffset = 0;
392 ret = parseCriticalityOffset(id, wiClass,
393 dict->getObject(kCriticalityOffsetKey), criticalityOffset);
394 if (ret != kIOReturnSuccess) {
395 return true;
396 }
397
398 UInt32 threadGroupFlags = classData.threadGroupFlags;
399 UInt32 workIntervalFlags = classData.workIntervalFlags;
400 ret = parseFlags(id, dict->getObject(kFlagsKey), threadGroupFlags, workIntervalFlags);
401 if (ret != kIOReturnSuccess) {
402 return true;
403 }
404
405 const workload_config_t config = {
406 .wc_thread_group_flags = threadGroupFlags,
407 .wc_flags = workIntervalFlags,
408 .wc_create_flags = createFlags,
409 .wc_class_offset = (uint8_t)criticalityOffset,
410 .wc_class = wiClass,
411 };
412 ret = workload_config_insert(ctx, id.getCStringNoCopy(), phase->getCStringNoCopy(), &config);
413 if (ret != kIOReturnSuccess) {
414 WLC_LOG("%s: failed to add \"%s\" phase\n",
415 id.getCStringNoCopy(), phase->getCStringNoCopy());
416 return true;
417 }
418
419 return false;
420 });
421 /* END IGNORE CODESTYLE */
422
423 return ret;
424 }
425
426 static IOReturn
parseRoot(const OSSymbol & id,const OSObject * rootDict,OSString * & defaultPhase)427 parseRoot(const OSSymbol &id, const OSObject *rootDict, OSString *&defaultPhase)
428 {
429 const OSDictionary *root = OSDynamicCast(OSDictionary, rootDict);
430 if (root == nullptr) {
431 WLC_LOG("%s: failed to find dictionary for \"" kRootKey "\"\n",
432 id.getCStringNoCopy());
433 return kIOReturnError;
434 }
435
436 defaultPhase = OSDynamicCast(OSString, root->getObject(kDefaultPhaseKey));
437 if (defaultPhase == nullptr) {
438 WLC_LOG("%s: failed to find \"" kDefaultPhaseKey"\" in \"" kRootKey "\" dictionary\n",
439 id.getCStringNoCopy());
440 return kIOReturnError;
441 }
442
443 if (defaultPhase->getLength() == 0) {
444 WLC_LOG("%s: \"" kDefaultPhaseKey" \" is empty in \"" kRootKey "\" dictionary\n",
445 id.getCStringNoCopy());
446 return kIOReturnError;
447 }
448
449 return kIOReturnSuccess;
450 }
451
452 static IOReturn
parseWorkloadIDTable(workload_config_ctx_t * ctx,OSDictionary * IDTable)453 parseWorkloadIDTable(workload_config_ctx_t *ctx, OSDictionary *IDTable)
454 {
455 /*
456 * At least one valid entry is expected, so start off with error to
457 * catch an empty table or one with no valid entries.
458 */
459 __block IOReturn ret = kIOReturnError;
460
461 /* BEGIN IGNORE CODESTYLE */
462 IDTable->iterateObjects(^bool (const OSSymbol *id, OSObject *value) {
463 /* Validate the workload ID. */
464 if (id->getLength() == 0) {
465 WLC_LOG("zero length ID in \"" kWorkloadIDTableKey "\"\n");
466 ret = kIOReturnError;
467 return true;
468 }
469
470 /* Parse its properties. */
471 OSDictionary *idConfig = OSDynamicCast(OSDictionary, value);
472 if (idConfig == nullptr) {
473 WLC_LOG("failed to find dictionary for \"%s\"\n",
474 id->getCStringNoCopy());
475 ret = kIOReturnError;
476 return true;
477 }
478
479 ret = parsePhases(ctx, *id, idConfig->getObject(kPhasesKey));
480 if (ret != kIOReturnSuccess) {
481 return true;
482 }
483
484 OSString *defaultPhase = nullptr;
485 ret = parseRoot(*id, idConfig->getObject(kRootKey), defaultPhase);
486 if (ret != kIOReturnSuccess) {
487 return true;
488 }
489
490 /* Fails if the specified phase doesn't exist.. */
491 ret = workload_config_set_default(ctx, id->getCStringNoCopy(),
492 defaultPhase->getCStringNoCopy());
493 if (ret != kIOReturnSuccess) {
494 WLC_LOG("failed to set default phase (%s) for \"%s\"\n",
495 defaultPhase->getCStringNoCopy(), id->getCStringNoCopy());
496 return true;
497 }
498
499 return false;
500 });
501 /* END IGNORE CODESTYLE */
502
503 return ret;
504 }
505
506 static IOReturn
parseWorkloadIDConfigurationFlags(workload_config_ctx_t * ctx,const OSObject * idTableFlagsObj)507 parseWorkloadIDConfigurationFlags(workload_config_ctx_t *ctx, const OSObject *idTableFlagsObj)
508 {
509 /* Optional, so just carry on if not found. */
510 if (idTableFlagsObj == nullptr) {
511 return kIOReturnSuccess;
512 }
513
514 OSArray *idTableFlags = OSDynamicCast(OSArray, idTableFlagsObj);
515 if (idTableFlags == nullptr) {
516 WLC_LOG("failed to parse \""
517 kWorkloadIDConfigurationFlagsKey "\"\n");
518 return kIOReturnError;
519 }
520
521 /* BEGIN IGNORE CODESTYLE */
522 __block IOReturn ret = kIOReturnSuccess;
523 idTableFlags->iterateObjects(^bool (OSObject *object) {
524 const OSString *flag = OSDynamicCast(OSString, object);
525 if (flag == nullptr) {
526 WLC_LOG("non-string Workload ID Table flag found\n");
527 ret = kIOReturnError;
528 return true;
529 }
530
531 if (flag->isEqualTo(kDisableWorkloadClassThreadPolicyValue)) {
532 workload_config_clear_flag(ctx, WLC_F_THREAD_POLICY);
533 }
534
535 return false;
536 });
537 /* END IGNORE CODESTYLE */
538
539 return ret;
540 }
541
542 static IOReturn
unparseWorkloadIDConfigurationFlags(OSSharedPtr<OSDictionary> & plist)543 unparseWorkloadIDConfigurationFlags(OSSharedPtr<OSDictionary> &plist)
544 {
545 workload_config_flags_t flags = WLC_F_NONE;
546
547 /* There may be no config at all. That's ok. */
548 if (workload_config_get_flags(&flags) != KERN_SUCCESS) {
549 return kIOReturnSuccess;
550 }
551
552 /* Workload config can change thread policy scheduling - the default. */
553 if ((flags & WLC_F_THREAD_POLICY) != 0) {
554 return kIOReturnSuccess;
555 }
556
557 OSSharedPtr<OSArray> idTableFlags = OSArray::withCapacity(1);
558 OSSharedPtr<OSString> flag = OSString::withCString(kDisableWorkloadClassThreadPolicyValue);
559 if (!idTableFlags->setObject(flag) ||
560 !plist->setObject(kWorkloadIDConfigurationFlagsKey, idTableFlags)) {
561 return kIOReturnError;
562 }
563
564 return kIOReturnSuccess;
565 }
566
567 extern "C" {
568 extern IOReturn IOParseWorkloadConfig(workload_config_ctx_t *, const char *, size_t);
569 extern IOReturn IOUnparseWorkloadConfig(char *, size_t *);
570 }
571
572 /* Called locked. */
573 IOReturn
IOParseWorkloadConfig(workload_config_ctx_t * ctx,const char * buffer,size_t size)574 IOParseWorkloadConfig(workload_config_ctx_t *ctx, const char *buffer, size_t size)
575 {
576 IOReturn ret = kIOReturnError;
577
578 OSSharedPtr<OSString> unserializeErrorString = nullptr;
579 OSSharedPtr<OSObject> obj = nullptr;
580 OSDictionary *idTable = nullptr;
581 OSDictionary *dict = nullptr;
582
583 ret = workload_config_init(ctx);
584 if (ret != kIOReturnSuccess) {
585 WLC_LOG("failed to initialize workload configuration\n");
586 goto out;
587 }
588
589 obj = OSUnserializeXML(buffer, unserializeErrorString);
590 dict = OSDynamicCast(OSDictionary, obj.get());
591 if (dict == nullptr) {
592 WLC_LOG("failed to unserialize plist\n");
593 ret = kIOReturnError;
594 goto out;
595 }
596
597 idTable = OSDynamicCast(OSDictionary, dict->getObject(kWorkloadIDTableKey));
598 if (idTable == nullptr) {
599 WLC_LOG("failed to find " kWorkloadIDTableKey "\n");
600 ret = kIOReturnError;
601 goto out;
602 }
603
604 ret = parseWorkloadIDTable(ctx, idTable);
605 if (ret != kIOReturnSuccess) {
606 goto out;
607 }
608
609 ret = parseWorkloadIDConfigurationFlags(ctx, dict->getObject(kWorkloadIDConfigurationFlagsKey));
610 if (ret != kIOReturnSuccess) {
611 goto out;
612 }
613
614 ret = kIOReturnSuccess;
615
616 out:
617 if (ret != kIOReturnSuccess) {
618 workload_config_free(ctx);
619 }
620
621 return ret;
622 }
623
624 /*
625 * Does the reverse of IOParseWorkloadConfig() - i.e. serializes the internal
626 * workload configuration.
627 * The serialized workload config is copied to 'buffer' (if non-NULL).
628 * size is in/out - it describes the size of buffer and on return the length of
629 * the serialized config.
630 */
631 IOReturn
IOUnparseWorkloadConfig(char * buffer,size_t * size)632 IOUnparseWorkloadConfig(char *buffer, size_t *size)
633 {
634 assert(size != nullptr);
635
636 OSSharedPtr<OSDictionary> dict = nullptr;;
637 OSSharedPtr<OSDictionary> idTable = nullptr;
638 OSSharedPtr<OSSerialize> serialize = nullptr;
639
640 serialize = OSSerialize::withCapacity(1);
641 if (serialize == nullptr) {
642 return kIOReturnNoMemory;
643 }
644
645 dict = OSDictionary::withCapacity(1);
646 if (dict == nullptr) {
647 return kIOReturnNoMemory;
648 }
649
650 idTable = OSDictionary::withCapacity(1);
651 if (idTable == nullptr) {
652 return kIOReturnNoMemory;
653 }
654
655 __block IOReturn ret = kIOReturnSuccess;
656 /* BEGIN IGNORE CODESTYLE */
657 workload_config_iterate(^(const char *id_str, const void *config) {
658 OSSharedPtr<OSDictionary> idDict = OSDictionary::withCapacity(1);
659 if (idDict == nullptr) {
660 ret = kIOReturnNoMemory;
661 return true;
662 }
663
664 OSSharedPtr<OSDictionary> phase = OSDictionary::withCapacity(1);
665 if (phase == nullptr) {
666 ret = kIOReturnNoMemory;
667 return true;
668 }
669
670 workload_config_phases_iterate(config, ^(const char *phase_str,
671 const bool is_default, const workload_config_t *wc) {
672 OSSharedPtr<OSDictionary> phaseData = OSDictionary::withCapacity(1);
673 if (phaseData == nullptr) {
674 ret = kIOReturnNoMemory;
675 return true;
676 }
677
678 if (wc->wc_class != WI_CLASS_NONE) {
679 assert3u(wc->wc_class, <, WI_CLASS_COUNT);
680 OSSharedPtr<OSString> wClass = OSString::withCString(wlClassData[wc->wc_class].name);
681 if (wClass == nullptr || !phaseData->setObject(kWorkloadClassKey, wClass)) {
682 ret = kIOReturnError;
683 return true;
684 }
685 }
686
687 if (wc->wc_class_offset > 0) {
688 OSSharedPtr<OSNumber> criticalityOffset = OSNumber::withNumber(wc->wc_class_offset, 8);
689 if (criticalityOffset == nullptr ||
690 !phaseData->setObject(kCriticalityOffsetKey, criticalityOffset)) {
691 ret = kIOReturnError;
692 return true;
693 }
694 }
695
696 OSSharedPtr<OSString> type = nullptr;
697 if (unparseWorkIntervalType(wc->wc_create_flags, type) != kIOReturnSuccess ||
698 !phaseData->setObject(kWorkIntervalTypeKey, type)) {
699 ret = kIOReturnError;
700 return true;
701 }
702
703
704 OSSharedPtr<OSArray> flags = OSArray::withCapacity(2);
705 if (flags == nullptr) {
706 ret = kIOReturnError;
707 return true;
708 }
709 if ((wc->wc_flags & WORK_INTERVAL_WORKLOAD_ID_COMPLEXITY_ALLOWED) != 0) {
710 OSSharedPtr<OSString> WIComplexityAllowedStr =
711 OSString::withCString(kWIComplexityAllowedValue);
712 if (WIComplexityAllowedStr == nullptr || !flags->setObject(WIComplexityAllowedStr)) {
713 ret = kIOReturnError;
714 return true;
715 }
716 }
717 if (flags->getCount() && !phaseData->setObject(kFlagsKey, flags)) {
718 ret = kIOReturnError;
719 return true;
720 }
721
722 if (!phase->setObject(phase_str, phaseData)) {
723 ret = kIOReturnError;
724 return true;
725 }
726
727 if (is_default) {
728 OSSharedPtr<OSDictionary> root = OSDictionary::withCapacity(1);
729 OSSharedPtr<OSString> phaseStr = OSString::withCString(phase_str);
730
731 if (root == nullptr || phaseStr == nullptr ||
732 !root->setObject(kDefaultPhaseKey, phaseStr)) {
733 ret = kIOReturnError;
734 return true;
735 }
736
737 if (!idDict->setObject(kRootKey, root)) {
738 ret = kIOReturnError;
739 return true;
740 }
741 }
742
743 return false;
744
745 });
746
747 if (ret != kIOReturnSuccess) {
748 return true;
749 }
750
751 if (!idDict->setObject(kPhasesKey, phase)) {
752 ret = kIOReturnError;
753 return true;
754 }
755
756 if (!idTable->setObject(id_str, idDict)) {
757 ret = kIOReturnError;
758 return true;
759 }
760
761 return false;
762 });
763 /* END IGNORE CODESTYLE */
764
765 if (ret != kIOReturnSuccess) {
766 return ret;
767 }
768
769 OSSharedPtr<OSDictionary> plist = OSDictionary::withCapacity(1);
770 if (plist == nullptr) {
771 return kIOReturnError;
772 }
773
774 if (idTable->getCount() > 0 &&
775 !plist->setObject(kWorkloadIDTableKey, idTable)) {
776 return kIOReturnError;
777 }
778
779 if (unparseWorkloadIDConfigurationFlags(plist) != kIOReturnSuccess) {
780 return kIOReturnError;
781 }
782
783 if (!plist->serialize(serialize.get())) {
784 return kIOReturnError;
785 }
786
787 if (buffer != nullptr) {
788 (void) strlcpy(buffer, serialize->text(), *size);
789 }
790 *size = serialize->getLength();
791
792 return kIOReturnSuccess;
793 }
794