xref: /xnu-8792.61.2/iokit/Kernel/IOWorkloadConfig.cpp (revision 42e220869062b56f8d7d0726fd4c88954f87902c) !
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