if alignment
[keystone-rtos/rm-lld.git] / src / rm_policy.c
1 /**
2  *   @file  rm_policy.c
3  *
4  *   @brief   
5  *      Resource Manager Policy source.
6  *
7  *  \par
8  *  ============================================================================
9  *  @n   (C) Copyright 2012-2015, Texas Instruments, Inc.
10  * 
11  *  Redistribution and use in source and binary forms, with or without 
12  *  modification, are permitted provided that the following conditions 
13  *  are met:
14  *
15  *    Redistributions of source code must retain the above copyright 
16  *    notice, this list of conditions and the following disclaimer.
17  *
18  *    Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the 
20  *    documentation and/or other materials provided with the   
21  *    distribution.
22  *
23  *    Neither the name of Texas Instruments Incorporated nor the names of
24  *    its contributors may be used to endorse or promote products derived
25  *    from this software without specific prior written permission.
26  *
27  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
28  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
29  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30  *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
31  *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
32  *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
33  *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34  *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35  *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
36  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
37  *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38  *
39  *  \par
40 */
42 /* Standard includes */
43 #include <ctype.h>
45 /* RM external API includes */
46 #include <ti/drv/rm/rm.h>
48 /* RM internal API includes */
49 #include <ti/drv/rm/include/rm_internal.h>
50 #include <ti/drv/rm/include/rm_loc.h>
51 #include <ti/drv/rm/include/rm_allocatorloc.h>
52 #include <ti/drv/rm/include/rm_policyloc.h>
53 #include <ti/drv/rm/include/rm_dtb_utilloc.h>
55 /* RM LIBFDT includes */
56 #include <ti/drv/rm/util/libfdt/libfdt.h>
58 /* Tree algorithm includes */
59 #include <ti/drv/rm/util/tree.h>
61 /* RM OSAL layer */
62 #include <rm_osal.h>
64 /**********************************************************************
65  *********************** Policy Globals *******************************
66  **********************************************************************/
68 /* Character used in policies to specify all RM instances receive
69  * the defined permissions for a resource node */
70 const char Rm_policyGlobalInst[] = "*";
72 /**********************************************************************
73  ******************** Local Policy Functions **************************
74  **********************************************************************/
76 /* FUNCTION PURPOSE: Parses a permissions subgroup
77  ***********************************************************************
78  * DESCRIPTION: Returns a linked list of policy permissions defining
79  *              which RM instance referenced in the permissions subgroup
80  *              get which permissions.  Returns NULL if any syntax
81  *              errors are encountered during the parsing.  The error
82  *              is returned via the result pointer parameter.
83  */
84 static Rm_PolicyPermission *policyParseSubPermission(char *permStrStart,
85                                                      char *permStrEnd,
86                                                      int32_t *result)
87 {
88     Rm_PolicyPermission *startPerm = NULL;
89     Rm_PolicyPermission *newPerm = NULL;
90     Rm_PolicyPermission *prevPerm = NULL;
91     Rm_PolicyPermission *nextPerm = NULL;
92     char                *permStrPtr = NULL;
93     char                *subgroupStart = NULL;
94     char                *subgroupEnd = NULL;
95     uint32_t             permStrLen = (uint32_t)(permStrEnd - permStrStart + 1);
96     char                 instNameTemp[RM_NAME_MAX_CHARS];
97     uint32_t             instNameIndex;
98     int                  foundInstName;
99     int                  instNameComplete;
100     int                  assignmentLeft;
101     int                  assignmentRight;
103     /* Create a local copy of the sub-permission string */
104     permStrPtr = Rm_osalMalloc(permStrLen);
105     rm_strncpy(permStrPtr, permStrStart, permStrLen);
107     permStrStart = permStrPtr;
108     permStrEnd = permStrPtr + strlen(permStrPtr);
110     /* Find the beginning and end of the sub-permission instance group */
111     subgroupStart = strchr(permStrStart, RM_POLICY_PERM_SUBGROUP_START);
112     subgroupEnd = strchr(permStrStart, RM_POLICY_PERM_SUBGROUP_END);
114     if ((!subgroupStart) || (!subgroupEnd) || (subgroupStart > subgroupEnd) ||
115         ((subgroupStart != strrchr(permStrStart,
116                                    RM_POLICY_PERM_SUBGROUP_START)) &&
117          (subgroupEnd != strrchr(permStrStart,
118                                  RM_POLICY_PERM_SUBGROUP_END)))) {
119         /* Free the memory associated with the temp string and return an
120          * error if:
121          * a) Could not find the instance group start
122          * b) Could not find the instance group end
123          * c) Subgroup start and end are out of order
124          * d) There is more than one instance subgroup specified in the string.
125          *    There should only be one subgroup per sub-permission string */
126         *result = RM_ERROR_PERM_STR_TOO_MANY_INST_GROUPS;
127         goto parseError;
128     }
130     /* Create a permission entry for each instance specified in the instance group.
131      * Instances names are separated by one or more spaces. */
132     permStrPtr = subgroupStart + 1;
133     instNameIndex = 0;
134     foundInstName = RM_FALSE;
135     instNameComplete = RM_FALSE;
136     while (permStrPtr <= subgroupEnd) {
137         if ((isspace(*permStrPtr) ||
138             (*permStrPtr == RM_POLICY_PERM_SUBGROUP_END))
139             && foundInstName) {
140             /* First space encountered after copying an instance name.  This
141              * terminates the instance name.  All other space characters are
142              * ignored. */
143             instNameTemp[instNameIndex] = '\0';
144             instNameComplete = RM_TRUE; 
145         } else {
146             if (!foundInstName) {
147                 /* First non-whitespace character encountered is the start of an
148                  * instance name */
149                 foundInstName = RM_TRUE;
150             }
152             /* Copy the character into the temporary instance name string */
153             instNameTemp[instNameIndex++] = *permStrPtr;
154         }
156         if (instNameComplete) {
157             newPerm = Rm_osalMalloc(sizeof(*newPerm));
158             memset((void *)newPerm, 0, sizeof(*newPerm));
160             rm_strncpy(newPerm->instName, instNameTemp, RM_NAME_MAX_CHARS);
161             newPerm->nextPermission = NULL;
163             if (prevPerm == NULL) {
164                 /* Save the first instance so it can be returned */
165                 startPerm = newPerm;
166             } else {
167                 prevPerm->nextPermission = newPerm;
168             }
169             prevPerm = newPerm;
171             instNameComplete = RM_FALSE;
172             instNameIndex = 0;
173             foundInstName = RM_FALSE;
174         }
176         if (instNameIndex == RM_NAME_MAX_CHARS) {
177             /* Instance name is longer than max length */
178             *result = RM_ERROR_INST_NAME_IN_ASSIGNMENT_TOO_LONG;
179             goto parseError;
180         }
182         permStrPtr++;
183     }
185     /* Fill in the permissions for each instance name */
187     /* Look on left of instance group for permission assignments. */
188     permStrPtr = subgroupStart - 1;
189     assignmentLeft = RM_FALSE;
190     while (permStrPtr >= permStrStart)
191     {
192         if (*permStrPtr == RM_POLICY_PERM_ASSIGNMENT) {
193             if (assignmentLeft) {
194                 /* Assignment character has been found more than once.  This
195                  * is a syntax error.  Free the permission list and the
196                  * temporary string and return. */
197                 *result = RM_ERROR_PERM_STR_TOO_MANY_ASSIGN_CHARS;
198                 goto parseError;
199             } else {
200                 assignmentLeft = RM_TRUE;
201             }
202         } else if (!isspace(*permStrPtr)) {
203             if (assignmentLeft) {
204                 if ((*permStrPtr == RM_POLICY_PERM_INIT_LOWER) ||
205                     (*permStrPtr == RM_POLICY_PERM_INIT_UPPER)) {
206                     newPerm = startPerm;
207                     while (newPerm) {
208                         Rm_PolicyPermBits *pBits = &(newPerm->permissionBits);
209                         RM_policy_PERM_SET(pBits, 0, 0,
210                                            RM_POLICY_PERM_INIT_SHIFT, 1);
211                         newPerm = newPerm->nextPermission;
212                     }
213                 } else if ((*permStrPtr == RM_POLICY_PERM_USE_LOWER) ||
214                          (*permStrPtr == RM_POLICY_PERM_USE_UPPER)) {
215                     newPerm = startPerm;
216                     while (newPerm) {
217                         Rm_PolicyPermBits *pBits = &(newPerm->permissionBits);
218                         RM_policy_PERM_SET(pBits, 0, 0,
219                                            RM_POLICY_PERM_USE_SHIFT, 1);
220                         newPerm = newPerm->nextPermission;
221                     }
222                 } else if ((*permStrPtr == RM_POLICY_PERM_EXCLUSIVE_LOWER) ||
223                          (*permStrPtr == RM_POLICY_PERM_EXCLUSIVE_UPPER)) {
224                     newPerm = startPerm;
225                     while (newPerm) {
226                         Rm_PolicyPermBits *pBits = &(newPerm->permissionBits);
227                         RM_policy_PERM_SET(pBits, 0, 0,
228                                            RM_POLICY_PERM_EXCLUSIVE_SHIFT, 1);
229                         newPerm = newPerm->nextPermission;
230                     }
231                 } else if ((*permStrPtr == RM_POLICY_PERM_SHARED_LINUX_LOWER) ||
232                          (*permStrPtr == RM_POLICY_PERM_SHARED_LINUX_UPPER)) {
233                     newPerm = startPerm;
234                     while (newPerm) {
235                         Rm_PolicyPermBits *pBits = &(newPerm->permissionBits);
236                         RM_policy_PERM_SET(pBits, 0, 0,
237                                            RM_POLICY_PERM_SHARED_LINUX_SHIFT,
238                                            1);
239                         newPerm = newPerm->nextPermission;
240                     }
241                 } else if ((*permStrPtr ==
242                             RM_POLICY_PERM_UNSPEC_EXCLUSION_LOWER) ||
243                            (*permStrPtr ==
244                             RM_POLICY_PERM_UNSPEC_EXCLUSION_UPPER)) {
245                     newPerm = startPerm;
246                     while (newPerm) {
247                         Rm_PolicyPermBits *pBits = &(newPerm->permissionBits);
248                         RM_policy_PERM_SET(pBits, 0, 0,
249                                       RM_POLICY_PERM_UNSPEC_EXCLUSION_SHIFT, 1);
250                         newPerm = newPerm->nextPermission;
251                     }
252                 } else {
253                     /* Invalid permission character.  This is a syntax error.
254                      * Free the permission list and the temporary string
255                      * and return. */
256                     *result = RM_ERROR_PERM_STR_INVALID_CHAR;
257                     goto parseError;
258                 }
259             } else {
260                 /* Character found without the assignment character being found.
261                  * This is a syntax error.  Free the permission list and the
262                  * temporary string and return. */
263                 *result = RM_ERROR_PERM_CHAR_WITHOUT_ASSIGN_CHAR;
264                 goto parseError;
265             }
266         }
267         permStrPtr--;
268     }
270     /* Look on right of instance group for permission assignments. */
271     permStrPtr = subgroupEnd + 1;
272     assignmentRight = RM_FALSE;
273     while (permStrPtr < permStrEnd) {
274         if (assignmentLeft && (!isspace(*permStrPtr))) {
275             /* There should be nothing but spaces on right if assignment was
276              * already found on left */
277             *result = RM_ERROR_INVALID_PERMS_CHAR_ON_RIGHT;
278             goto parseError;
279         }
281         if (*permStrPtr == RM_POLICY_PERM_ASSIGNMENT) {
282             if (assignmentRight) {
283                 /* Assignment character has been found more than once.  This is
284                  * a syntax error.  Free the permission list and the temporary
285                  * string and return. */
286                 *result = RM_ERROR_PERM_STR_TOO_MANY_ASSIGN_CHARS;
287                 goto parseError;
288             } else {
289                 assignmentRight = RM_TRUE;
290             }
291         } else if (!isspace(*permStrPtr)) {
292             if (assignmentRight) {
293                 if ((*permStrPtr == RM_POLICY_PERM_INIT_LOWER) ||
294                     (*permStrPtr == RM_POLICY_PERM_INIT_UPPER)) {
295                     newPerm = startPerm;
296                     while (newPerm) {
297                         Rm_PolicyPermBits *pBits = &(newPerm->permissionBits);
298                         RM_policy_PERM_SET(pBits, 0, 0,
299                                            RM_POLICY_PERM_INIT_SHIFT, 1);
300                         newPerm = newPerm->nextPermission;
301                     }
302                 } else if ((*permStrPtr == RM_POLICY_PERM_USE_LOWER) ||
303                          (*permStrPtr == RM_POLICY_PERM_USE_UPPER)) {
304                     newPerm = startPerm;
305                     while (newPerm) {
306                         Rm_PolicyPermBits *pBits = &(newPerm->permissionBits);
307                         RM_policy_PERM_SET(pBits, 0, 0,
308                                            RM_POLICY_PERM_USE_SHIFT, 1);
309                         newPerm = newPerm->nextPermission;
310                     }
311                 } else if ((*permStrPtr == RM_POLICY_PERM_EXCLUSIVE_LOWER) ||
312                          (*permStrPtr == RM_POLICY_PERM_EXCLUSIVE_UPPER)) {
313                     newPerm = startPerm;
314                     while (newPerm) {
315                         Rm_PolicyPermBits *pBits = &(newPerm->permissionBits);
316                         RM_policy_PERM_SET(pBits, 0, 0,
317                                            RM_POLICY_PERM_EXCLUSIVE_SHIFT, 1);
318                         newPerm = newPerm->nextPermission;
319                     }
320                 } else if ((*permStrPtr == RM_POLICY_PERM_SHARED_LINUX_LOWER) ||
321                          (*permStrPtr == RM_POLICY_PERM_SHARED_LINUX_UPPER)) {
322                     newPerm = startPerm;
323                     while (newPerm) {
324                         Rm_PolicyPermBits *pBits = &(newPerm->permissionBits);
325                         RM_policy_PERM_SET(pBits, 0, 0,
326                                            RM_POLICY_PERM_SHARED_LINUX_SHIFT,
327                                            1);
328                         newPerm = newPerm->nextPermission;
329                     }
330                 } else if ((*permStrPtr ==
331                             RM_POLICY_PERM_UNSPEC_EXCLUSION_LOWER) ||
332                            (*permStrPtr ==
333                             RM_POLICY_PERM_UNSPEC_EXCLUSION_UPPER)) {
334                     newPerm = startPerm;
335                     while (newPerm) {
336                         Rm_PolicyPermBits *pBits = &(newPerm->permissionBits);
337                         RM_policy_PERM_SET(pBits, 0, 0,
338                                       RM_POLICY_PERM_UNSPEC_EXCLUSION_SHIFT, 1);
339                         newPerm = newPerm->nextPermission;
340                     }
341                 } else {
342                     /* Invalid permission character.  This is a syntax error.
343                      * Free the permission list and the temporary string
344                      * and return. */
345                     *result = RM_ERROR_PERM_STR_INVALID_CHAR;
346                     goto parseError;
347                 }
348             } else {
349                 /* Character found without the assignment character being found.
350                  * This is a syntax error.  Free the permission list and the
351                  * temporary string and return. */
352                 *result = RM_ERROR_PERM_STR_TOO_MANY_ASSIGN_CHARS;
353                 goto parseError;
354             }
355         }
356         permStrPtr++;
357     }
359     Rm_osalFree((void *)permStrStart, permStrLen);
360     *result = RM_OK;
361     return (startPerm);
363 parseError:
364     while (startPerm) {
365         nextPerm = startPerm->nextPermission;
366         Rm_osalFree((void *)startPerm, sizeof(Rm_PolicyPermission));
367         startPerm = nextPerm;
368     }
369     Rm_osalFree((void *)permStrStart, permStrLen);
370     return(NULL);
373 /* FUNCTION PURPOSE: Frees a linked list of assignment permissions
374  ***********************************************************************
375  * DESCRIPTION: Frees the memory associated with a linked list of
376  *              assignment permissions extracted from a permissions
377  *              assignment subgroup in a policy DTB.
378  */
379 static void policyFreeAssignmentPermissions(Rm_PolicyPermission *permissionList)
381     Rm_PolicyPermission *nextPerm;
383     while (permissionList) {
384         nextPerm = permissionList->nextPermission;
385         Rm_osalFree((void *)permissionList, sizeof(Rm_PolicyPermission));
386         permissionList = nextPerm;
387     }
390 /* FUNCTION PURPOSE: Extracts permissions from a Policy "assignment"
391  ***********************************************************************
392  * DESCRIPTION: Returns a linked list of permissions for a resource node
393  *              containing an "assignment" property in the Policy DTB.
394  *              Each node in the linked list will contain a valid instance
395  *              name along with the permissions assigned to the instance
396  */
397 static Rm_PolicyPermission *policyGetAssignmentPermissions(Rm_PolicyAssignment *assignment,
398                                                            int32_t *result)
400     Rm_PolicyPermission *startPerm = NULL;
401     Rm_PolicyPermission *newPerm = NULL;
402     Rm_PolicyPermission *prevPerm = NULL;
403     char                *permStrStart = assignment->permissionsList;
404     char                *permStrEnd;
405     uint32_t             permStrLen = strlen(assignment->permissionsList) + 1;
406     uint32_t             i = 0;
408     *result = RM_OK;
410     while(i < permStrLen) {
411         /* Find the first sub-permission specification and parse it.  A
412          * sub-permission can be terminated by the termination character or the
413          * end of the string. */
414         if (!(permStrEnd = strchr(permStrStart, RM_POLICY_PERM_TERMINATOR))) {
415             /* Sub-permission termination character not found.  The permission
416              * string end is the end of the entire permission string */
417             permStrEnd = permStrStart + strlen(permStrStart);
418         }
420         newPerm = policyParseSubPermission(permStrStart, permStrEnd, result);
422         if (*result != RM_OK) {
423             /* Delete the permission list that's been created thus far, return
424              * the error and NULL for the permission list */
425             policyFreeAssignmentPermissions(startPerm);
426             return(NULL);
427         }
429         if (prevPerm == NULL) {
430             startPerm = newPerm;
431         } else {
432             prevPerm->nextPermission = newPerm;
433         }
435         /* Set prevPerm to the last sub-permission returned by the
436          * sub-permission parser */
437         prevPerm = newPerm;
438         while(prevPerm->nextPermission != NULL) {
439             prevPerm = prevPerm->nextPermission;
440         }
442         /* Update the number of characters parsed from the permission list and
443          * point to the start of the next sub-permission */
444         i += ((uint32_t)(permStrEnd - permStrStart + 1));
445         permStrStart = permStrEnd + 1;
446     }
448     return(startPerm);
451 /* FUNCTION PURPOSE: Returns number of bytes needed to store permissions
452  ***********************************************************************
453  * DESCRIPTION: Calculates and returns the number of bytes needed to store
454  *              permissions for all valid instances.  Words are packed
455  *              with permissions for as many instances as can fit
456  */
457 static uint32_t policyGetPermBufSize(Rm_PolicyPermBits *permsPtr,
458                                      uint32_t maxValidInst)
460     uint32_t instPerWord;
461     uint32_t numWords;
462     uint32_t totalBytes = 0;
464     instPerWord = RM_policy_PERM_INST_PER_WORD;
465     /* Round up */
466     numWords = (maxValidInst + instPerWord - 1) / instPerWord;
468     totalBytes = sizeof(*permsPtr) * numWords;
469     return(totalBytes);
472 /* FUNCTION PURPOSE: Stores policy permissions in a policy tree node
473  ***********************************************************************
474  * DESCRIPTION: Parses permissions and stores them in a word array in a
475  *              compacted format.  The array is attached to the resource's
476  *              policy tree node.
477  */
478 static int32_t policyStorePermissions(Rm_Inst *rmInst, Rm_PolicyNode *polNode,
479                                       Rm_PolicyPermission *extractedPerms)
481     Rm_PolicyValidInstNode *vInst = NULL;
482     uint32_t                instIdx;
483     uint32_t                wordIndex;
484     uint32_t                wordOffset;
485     int32_t                 retVal = RM_OK;
487     while (extractedPerms) {
488         if (!strncmp(extractedPerms->instName, Rm_policyGlobalInst,
489                      RM_NAME_MAX_CHARS)) {
490             instIdx = RM_POLICY_GLOBAL_PERM_INDEX;
491         } else if ((vInst = rmPolicyGetValidInstNode((Rm_Handle)rmInst,
492                                                    extractedPerms->instName))) {
493             instIdx = vInst->instIdx;
494         } else {
495             retVal = RM_ERROR_PERM_STR_INST_NOT_VALID;
496             goto errorExit;
497         }
499         /* Calculate word index into policy node's permission array
500          * for the instance index */
501         wordIndex  = RM_policy_PERM_INDEX(instIdx);
502         wordOffset = RM_policy_PERM_OFFSET(instIdx);
504         polNode->perms[wordIndex] |= ((extractedPerms->permissionBits &
505                                       RM_policy_PERM_FULL_MASK) << wordOffset);
507         extractedPerms = extractedPerms->nextPermission;
508     }
510 errorExit:
511     return(retVal);
514 /* FUNCTION PURPOSE: Returns resource's allocation alignment from policy
515  ***********************************************************************
516  * DESCRIPTION: Parses the policy DTB to find and return a resource's 
517  *              allocation alignment.
518  */
519 static uint32_t policyGetDtbAllocAlign(void *policyDtb, int32_t resourceOffset)
521     int32_t           offset;
522     const char       *name;
523     int32_t           len;
524     const void       *data;
525     Rm_ResourceValue *alignList = NULL;
526     uint32_t          align = 0;
528     offset = fdt_first_property_offset(policyDtb, resourceOffset);
529     while (offset > RM_DTB_UTIL_STARTING_NODE_OFFSET) {
530         data = fdt_getprop_by_offset(policyDtb, offset, &name, &len);
531         if (rmDtbUtilPolicyGetPropertyType(name) ==
532             Rm_policyPropType_ALLOCATION_ALIGNMENT) {
533             alignList = rmDtbUtilPolicyExtractResourceAlignments(data, len);
534             align = alignList->value;
535             break;
536         }
537         offset = fdt_next_property_offset(policyDtb, offset);
538     }
540     if (alignList) {
541         rmDtbUtilPolicyFreeResourceAlignments(alignList);
542     }
544     if (align == 0) {
545         align = 1;
546     }
547     return(align);
550 /* FUNCTION PURPOSE: Returns resource CD allocation size defined in policy
551  ***********************************************************************
552  * DESCRIPTION: Parses the policy DTB to find and return a resource's 
553  *              allocation size.
554  */
555 static uint32_t policyGetDtbCdAllocSize(void *policyDtb, int32_t resourceOffset)
557     int32_t           offset;
558     const char       *name;
559     int32_t           len;
560     const void       *data;
561     Rm_ResourceValue *allocSizeList = NULL;
562     uint32_t          allocSize = 0;
564     offset = fdt_first_property_offset(policyDtb, resourceOffset);
565     while (offset > RM_DTB_UTIL_STARTING_NODE_OFFSET) {
566         data = fdt_getprop_by_offset(policyDtb, offset, &name, &len);
567         if (rmDtbUtilPolicyGetPropertyType(name) ==
568             Rm_policyPropType_CD_ALLOCATION_SIZE) {
569             allocSizeList = rmDtbUtilPolicyExtractCdAllocationSizes(data, len);
570             allocSize = allocSizeList->value;
571             break;
572         }
573         offset = fdt_next_property_offset(policyDtb, offset);
574     }
576     if (allocSizeList) {
577         rmDtbUtilPolicyFreeCdAllocationSizes(allocSizeList);
578     }
579     return(allocSize);
582 /* FUNCTION PURPOSE: Get a resource's offset into a policy
583  ***********************************************************************
584  * DESCRIPTION: Returns the location of the specified resource node
585  *              within the specified Policy in the form of an offset
586  *              into the DTB.  The resourceName and the Policy
587  *              node name must match.
588  */
589 static int32_t policyGetDtbResourceOffset(void *policyDtb,
590                                           const char *resourceName)
592     int32_t     nodeOffset;
593     int32_t     depth;
594     const char *nodeName;
596     if (policyDtb) {
597         depth = RM_DTB_UTIL_STARTING_DEPTH;
598         nodeOffset = RM_DTB_UTIL_STARTING_NODE_OFFSET;
600         /* Find node offset for provided resource name */
601         while (nodeOffset >= RM_DTB_UTIL_STARTING_NODE_OFFSET) {
602             nodeOffset = fdt_next_node(policyDtb, nodeOffset, &depth);
603             if (depth < RM_DTB_UTIL_STARTING_DEPTH) {
604                 /* Resource name not found */
605                 nodeOffset = RM_ERROR_RES_DOES_NOT_EXIST_IN_POLICY;
606                 break;
607             } else {
608                 nodeName = fdt_get_name(policyDtb, nodeOffset, NULL);
609                 if (strncmp(nodeName, resourceName, RM_NAME_MAX_CHARS) == 0) {
610                     break;
611                 }
612             }
613         }
614     } else {
615         nodeOffset = RM_ERROR_INSTANCE_HAS_NO_POLICY;
616     }
617     return(nodeOffset);
620 /**********************************************************************
621  ************************ Internal Policy APIs ************************
622  **********************************************************************/
624 /* FUNCTION PURPOSE: Get a valid instace node from the valid inst tree
625  ***********************************************************************
626  * DESCRIPTION: Returns a valid instance node from the valid instance
627  *              tree that matches the specified instName
628  */
629 Rm_PolicyValidInstNode *rmPolicyGetValidInstNode(Rm_Handle rmHandle,
630                                                  const char *instName)
632     Rm_Inst                *rmInst = (Rm_Inst *)rmHandle;
633     Rm_PolicyValidInstTree *treeRoot = rmInst->validInstTree;
634     Rm_PolicyValidInstNode  findNode;
636     rm_strncpy(findNode.name, instName, RM_NAME_MAX_CHARS);
637     return(RB_FIND(_Rm_PolicyValidInstTree, treeRoot, &findNode));
640 /* FUNCTION PURPOSE: Gets the Linux Valid instance node
641  ***********************************************************************
642  * DESCRIPTION: Returns a pointer to the valid instance node in the
643  *              valid instance tree that matches the instance name
644  *              reserved for resource assigned to the Linux kernel.
645  */
646 Rm_PolicyValidInstNode *rmPolicyGetLinuxInstNode(Rm_Handle rmHandle)
648     const char linuxName[] = RM_ALLOCATED_TO_LINUX;
650     return(rmPolicyGetValidInstNode(rmHandle, linuxName));
653 /* FUNCTION PURPOSE: Validates resource permissions against a Policy DTB
654  ***********************************************************************
655  * DESCRIPTION: Returns TRUE if the instance name has the specified
656  *              permissions for the specified resource in the Policy
657  *              DTB.  Otherwise, returns FALSE.
658  */
659 int32_t rmPolicyCheckPrivilege(Rm_PolicyCheckCfg *privilegeCfg)
661     uint32_t       permShift;
662     uint32_t       globPermIdx;
663     uint32_t       globPermOffset;
664     uint32_t       instPermIdx;
665     uint32_t       instPermOffset;
666     Rm_PolicyNode  findNode;
667     Rm_PolicyNode *matchNode = NULL;
668     uint32_t       findEnd;
669     uint32_t       matchEnd;
670     int32_t        isApproved = RM_FALSE;
672     switch (privilegeCfg->type) {
673         case Rm_policyCheck_INIT:
674             permShift = RM_POLICY_PERM_INIT_SHIFT;
675             break;
676         case Rm_policyCheck_USE:
677             permShift = RM_POLICY_PERM_USE_SHIFT;
678             break;
679         case Rm_policyCheck_EXCLUSIVE:
680             permShift = RM_POLICY_PERM_EXCLUSIVE_SHIFT;
681             break;
682         case Rm_policyCheck_SHARED_LINUX:
683             permShift = RM_POLICY_PERM_SHARED_LINUX_SHIFT;
684             break;
685         case Rm_policyCheck_UNSPEC_EXCLUSION:
686             permShift = RM_POLICY_PERM_UNSPEC_EXCLUSION_SHIFT;
687             break;
688         default:
689             return(isApproved);
690     }
692     /* Calculate the word indices and offsets for the global permissions and
693      * the specific instance */
694     globPermIdx    = RM_policy_PERM_INDEX(RM_POLICY_GLOBAL_PERM_INDEX);
695     globPermOffset = RM_policy_PERM_OFFSET(RM_POLICY_GLOBAL_PERM_INDEX);
696     instPermIdx    = RM_policy_PERM_INDEX(privilegeCfg->validInstNode->instIdx);
697     instPermOffset = RM_policy_PERM_OFFSET(privilegeCfg->validInstNode->instIdx);
699     memset((void *)&findNode, 0, sizeof(findNode));
700     findNode.base = privilegeCfg->resourceBase;
701     findNode.len  = privilegeCfg->resourceLength;
702     /* Get first matching node. */
703     matchNode = RB_FIND(_Rm_AllocatorPolicyTree, privilegeCfg->polTree,
704                         &findNode);
705     if (matchNode) {
706         /* Request range may not be completely contained within the first
707          * matching node.  Find furthest left matching node for the request
708          * range */
709         while (findNode.base < matchNode->base) {
710             matchNode = RB_PREV(_Rm_AllocatorPolicyTree,
711                                 privilegeCfg->polTree, matchNode);
712         }
713     }
715     /* Check permissions across all policy nodes which the request range
716      * spans.  Assume approved until denial found */
717     isApproved = RM_TRUE;
718     while (matchNode) {
719         if (privilegeCfg->negCheck) {
720             /* Not approved if any matching node is assigned an exclusion
721              * permission */
722             if ((RM_policy_PERM_GET(matchNode->perms, globPermIdx,
723                                     globPermOffset, permShift)) ||
724                 (RM_policy_PERM_GET(matchNode->perms, instPermIdx,
725                                     instPermOffset, permShift))) {
726                 isApproved = RM_FALSE;
727                 break;
728             }
729         } else {
730             /* Not approved if any matching node does not have permission */
731             if ((!RM_policy_PERM_GET(matchNode->perms, globPermIdx,
732                                      globPermOffset, permShift)) &&
733                 (!RM_policy_PERM_GET(matchNode->perms, instPermIdx,
734                                      instPermOffset, permShift))) {
735                 isApproved = RM_FALSE;
736                 break;
737             }
738         }
740         matchEnd = matchNode->base + matchNode->len - 1;
741         findEnd  = findNode.base + findNode.len - 1;
743         /* Check node to right if request range spans matching node to right */
744         if (findEnd > matchEnd) {
745             matchNode = RB_NEXT(_Rm_AllocatorPolicyTree, privilegeCfg->polTree,
746                                 matchNode);
747             if (matchNode == NULL) {
748                 /* Request range outspans actual resource range */
749                 isApproved = RM_FALSE;
750             }
751         } else {
752             break;
753         }
754     }
756     return(isApproved);
759 /* FUNCTION PURPOSE: Returns resource base value according to the Policy
760  ***********************************************************************
761  * DESCRIPTION: Returns a resource base value based on the resource
762  *              ranges assigned to the specified valid instance by the
763  *              Policy DTB.
764  */
765 int32_t rmPolicyGetResourceBase(Rm_PolicyTree *policyTree,
766                                  Rm_PolicyValidInstNode *validInstNode,
767                                  Rm_PolicyCheckType checkType,
768                                  int32_t *resBase)
770     uint32_t       permShift = RM_POLICY_PERM_INIT_SHIFT;
771     uint32_t       globPermIdx;
772     uint32_t       globPermOffset;
773     uint32_t       instPermIdx;
774     uint32_t       instPermOffset;
775     Rm_PolicyNode *polNode;
777     *resBase = RM_RESOURCE_BASE_UNSPECIFIED;
779     if (checkType == Rm_policyCheck_INIT) {
780         permShift = RM_POLICY_PERM_INIT_SHIFT;
781     } else if (checkType == Rm_policyCheck_USE) {
782         permShift = RM_POLICY_PERM_USE_SHIFT;
783     } else {
784         return(RM_ERROR_INVALID_SERVICE_TYPE);
785     }
787     /* Calculate the word indices and offsets for the global permissions and
788      * the specific instance */
789     globPermIdx    = RM_policy_PERM_INDEX(RM_POLICY_GLOBAL_PERM_INDEX);
790     globPermOffset = RM_policy_PERM_OFFSET(RM_POLICY_GLOBAL_PERM_INDEX);
791     instPermIdx    = RM_policy_PERM_INDEX(validInstNode->instIdx);
792     instPermOffset = RM_policy_PERM_OFFSET(validInstNode->instIdx);
794     RB_FOREACH(polNode, _Rm_AllocatorPolicyTree, policyTree) {
795         if (RM_policy_PERM_GET(polNode->perms, globPermIdx, globPermOffset,
796                                permShift) ||
797             RM_policy_PERM_GET(polNode->perms, instPermIdx, instPermOffset,
798                                permShift)) {
799             *resBase = polNode->base;
800             break;
801         }
802     }
804     if (*resBase == RM_RESOURCE_BASE_UNSPECIFIED) {
805         return(RM_SERVICE_DENIED_RES_ALLOC_REQS_NOT_MET);
806     }
808     return(RM_OK);
811 /* FUNCTION PURPOSE: Returns resource's allocation alignment
812  ***********************************************************************
813  * DESCRIPTION: Returns a resource's allocation alignment.
814  */
815 uint32_t rmPolicyGetAllocAlign(Rm_PolicyTree *policyTree)
817     Rm_PolicyNode *node = NULL;
818     uint32_t       align = 1;
820     /* Resource's alignment is global at the moment so all policy tree nodes
821      * store the same value.  Just get the min node and return the allocation
822      * alignment from it */
823     node = RB_MIN(_Rm_AllocatorPolicyTree, policyTree);
824     if (node) {
825         align = node->allocAlign;
826     }
828     return(align);
831 /* FUNCTION PURPOSE: Returns resource's CD allocation size
832  ***********************************************************************
833  * DESCRIPTION: Returns a resource's CD allocation size.
834  */
835 uint32_t rmPolicyGetCdAllocSize(Rm_PolicyTree *policyTree)
837     Rm_PolicyNode *node = NULL;
838     uint32_t       allocSize = 0;
840     /* Resource's CD allocation size is global at the moment so all policy
841      * tree nodes store the same value.  Just get the min node and return
842      * allocation size from it */
843     node = RB_MIN(_Rm_AllocatorPolicyTree, policyTree);
844     if (node) {
845         allocSize = node->cdAllocSize;
846     }
848     return(allocSize);
851 /* FUNCTION PURPOSE: Initializes the valid instance tree for a RM instance
852  ***********************************************************************
853  * DESCRIPTION: Creates the valid instance tree for a RM instance 
854  *              that has been provided a global or static policy
855  *              The valid instance tree is created from the
856  *              "valid-instances" property at the top of the Policy.
857  *              The root entry of the valid instance tree is returned.
858  */
859 Rm_PolicyValidInstTree *rmPolicyVInstTreeInit(Rm_Handle rmHandle,
860                                               void *policyDtb,
861                                               int addLinux,
862                                               int32_t *result)
864     Rm_Inst                *rmInst = (Rm_Inst *)rmHandle;
865     int32_t                 validInstOffset;
866     const char             *validInstName = NULL;
867     int32_t                 validInstLen;
868     const void             *validInstData = NULL;
869     Rm_PolicyPropType       propertyType;
870     Rm_PolicyValidInst     *vInstListStart = NULL;
871     Rm_PolicyValidInst     *validInstList = NULL;
872     Rm_PolicyValidInstTree *rootEntry = NULL;
873     Rm_PolicyValidInstNode *newNode = NULL;
874     char                    linuxName[] = RM_ALLOCATED_TO_LINUX;
875     int32_t                 instIndex;
877     /* Valid instance list must be first and only property in the root node of
878      * the policyDtb */
879     validInstOffset = fdt_first_property_offset(policyDtb,
880                             RM_DTB_UTIL_STARTING_NODE_OFFSET);
881     if (validInstOffset < -FDT_ERR_NOTFOUND) {
882         *result = validInstOffset;
883         return(NULL);
884     }
886     if (validInstOffset == -FDT_ERR_NOTFOUND) {
887         *result = RM_ERROR_NO_VALID_INST_IN_POLICY;
888         return(NULL);
889     }
890     validInstData = fdt_getprop_by_offset(policyDtb, validInstOffset,
891                                           &validInstName, &validInstLen);
892     propertyType = rmDtbUtilPolicyGetPropertyType(validInstName);
893     if (propertyType != Rm_policyPropType_VALID_INSTANCES) {
894         *result = RM_ERROR_NO_VALID_INST_IN_POLICY;
895         return(NULL);
896     }
898     if (!(validInstList = rmDtbUtilPolicyExtractValidInstances(validInstData,
899                                                                validInstLen,
900                                                                result))) {
901         return(NULL);
902     }
904     /* Create the tree */
905     rootEntry = Rm_osalMalloc(sizeof(Rm_PolicyValidInstTree));
906     RB_INIT(rootEntry);
908     vInstListStart = validInstList;
909     /* Zeroeth index is reserved for global permissions */
910     instIndex = 1;
911     while (validInstList) {
912         newNode = rmPolicyValidInstNodeNew(validInstList->instName,
913                                            instIndex);
914         RB_INSERT(_Rm_PolicyValidInstTree, rootEntry, newNode);
916         instIndex++;
917         validInstList = validInstList->nextValidInst;
918     }
919     rmDtbUtilPolicyFreeValidInstances(vInstListStart);
921     /* Add the Linux kernel node */
922     if (addLinux) {
923         newNode = rmPolicyValidInstNodeNew(linuxName, instIndex++);
924         RB_INSERT(_Rm_PolicyValidInstTree, rootEntry, newNode);
925     }
927     /* Save the max index for proper accounting when validating against
928      * policy */
929     rmInst->maxInstIdx = instIndex;
931     *result = RM_OK;
932     return(rootEntry);
935 /* FUNCTION PURPOSE: Deletes a valid instance tree
936  ***********************************************************************
937  * DESCRIPTION: Frees all memory associated with a Policy valid
938  *              instance tree.
939  */
940 void rmPolicyVInstTreeDelete(Rm_Handle rmHandle)
942     Rm_Inst                *rmInst = (Rm_Inst *)rmHandle;
943     Rm_PolicyValidInstTree *treeRoot = rmInst->validInstTree;
944     Rm_PolicyValidInstNode *node;
945     Rm_PolicyValidInstNode *nextNode;
947     if (treeRoot) {
948         if (rmInst->instType == Rm_instType_SHARED_SERVER) {
949             rmPolicyValidInstTreeInv(treeRoot);
950         }
952         for (node = RB_MIN(_Rm_PolicyValidInstTree, treeRoot);
953              node != NULL;
954              node = nextNode) {
955             nextNode = RB_NEXT(_Rm_PolicyValidInstTree, treeRoot, node);
956             RB_REMOVE(_Rm_PolicyValidInstTree, treeRoot, node);
957             rmPolicyValidInstNodeFree(node);
958         }
960         /* Don't need to writeback tree node changes since valid instance will
961          * be made NULL in instance */
963         if (RB_MIN(_Rm_PolicyValidInstTree, treeRoot) == NULL) {
964             /* No more valid instance nodes in tree */
965             Rm_osalFree((void *)treeRoot, sizeof(treeRoot));
966         }
968         rmInst->validInstTree = NULL;
969     }
972 /* FUNCTION PURPOSE: Populates an allocator's policy tree
973  ***********************************************************************
974  * DESCRIPTION: Populates an allocator's policy tree using the policy
975  *              DTB
976  */
977 int32_t rmPolicyPopulateTree(Rm_Handle rmHandle, Rm_PolicyTree *policyTree,
978                              void *policyDtb, const char *resName)
980     Rm_Inst             *rmInst = (Rm_Inst *)rmHandle;
981     int32_t              resOffset;
982     uint32_t             allocAlignment;
983     uint32_t             cdAllocSize;
984     uint32_t             permBufSizeBytes = 0;
985     int32_t              propOffset;
986     const void          *propData;
987     const char          *propName;
988     int32_t              propLen;
989     Rm_PolicyAssignment *assign = NULL;
990     Rm_PolicyAssignment *assignStart = NULL;
991     Rm_PolicyNode       *polNode = NULL;
992     Rm_PolicyPermission *extractedPerms = NULL;
993     int32_t              retVal = RM_OK;
995     /* Offset of resource in policy DTB */
996     resOffset = policyGetDtbResourceOffset(policyDtb, resName);
997     if (resOffset < 0) {
998         retVal = resOffset;
999         goto errorExit;
1000     }
1002     /* Get the allocation alignment and the CD allocation size since these
1003      * will be stored in each tree node */
1004     allocAlignment = policyGetDtbAllocAlign(policyDtb, resOffset);
1005     cdAllocSize = policyGetDtbCdAllocSize(policyDtb, resOffset);
1006     permBufSizeBytes = policyGetPermBufSize(polNode->perms, rmInst->maxInstIdx);
1008     /* Get the resource assignments - should only be one per resource node */
1009     propOffset = fdt_first_property_offset(policyDtb, resOffset);
1010     while (propOffset > RM_DTB_UTIL_STARTING_NODE_OFFSET) {
1011         propData = fdt_getprop_by_offset(policyDtb, propOffset, &propName,
1012                                          &propLen);
1013         if (rmDtbUtilPolicyGetPropertyType(propName) ==
1014             Rm_policyPropType_ASSIGNMENTS) {
1015             assign = assignStart = rmDtbUtilPolicyExtractAssignments(propData,
1016                                                                      propLen);
1017             break;
1018         }
1019         propOffset = fdt_next_property_offset(policyDtb, propOffset);
1020     }
1022     /* Insert a new node into resource's allocator for each assignment range */
1023     while (assign) {
1024         polNode = rmPolicyNodeNew(assign->resourceBase,
1025                                   assign->resourceLength);
1026         polNode->allocAlign = allocAlignment;
1027         polNode->cdAllocSize = cdAllocSize;
1028         polNode->perms =  Rm_osalMalloc(permBufSizeBytes);
1029         polNode->permsLen = permBufSizeBytes;
1031         memset(polNode->perms, 0, polNode->permsLen);
1033         extractedPerms = policyGetAssignmentPermissions(assign, &retVal);
1034         if (retVal != RM_OK) {
1035             goto errorExit;
1036         }
1038         retVal = policyStorePermissions(rmInst, polNode, extractedPerms);
1039         if (retVal != RM_OK) {
1040             goto errorExit;
1041         }
1042         RB_INSERT(_Rm_AllocatorPolicyTree, policyTree, polNode);
1044         policyFreeAssignmentPermissions(extractedPerms);
1045         extractedPerms = NULL;
1046         polNode = NULL;
1048         assign = assign->nextAssignment;
1049     }
1051 errorExit:
1052     if (polNode) {
1053         rmPolicyNodeFree(polNode);
1054     }
1055     if (extractedPerms) {
1056         policyFreeAssignmentPermissions(extractedPerms);
1057     }
1058     if (assignStart) {
1059         rmDtbUtilPolicyFreeAssignments(assignStart);
1060     }
1062     return(retVal);