1 /** @file
2   SMM Driver Dispatcher Dependency Evaluator
3 
4   This routine evaluates a dependency expression (DEPENDENCY_EXPRESSION) to determine
5   if a driver can be scheduled for execution.  The criteria for
6   schedulability is that the dependency expression is satisfied.
7 
8   Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
9   SPDX-License-Identifier: BSD-2-Clause-Patent
10 
11 **/
12 
13 #include "PiSmmCore.h"
14 
15 ///
16 /// EFI_DEP_REPLACE_TRUE - Used to dynamically patch the dependency expression
17 ///                        to save time.  A EFI_DEP_PUSH is evaluated one an
18 ///                        replaced with EFI_DEP_REPLACE_TRUE. If PI spec's Vol 2
19 ///                        Driver Execution Environment Core Interface use 0xff
20 ///                        as new DEPEX opcode. EFI_DEP_REPLACE_TRUE should be
21 ///                        defined to a new value that is not conflicting with PI spec.
22 ///
23 #define EFI_DEP_REPLACE_TRUE  0xff
24 
25 ///
26 /// Define the initial size of the dependency expression evaluation stack
27 ///
28 #define DEPEX_STACK_SIZE_INCREMENT  0x1000
29 
30 //
31 // Global stack used to evaluate dependency expressions
32 //
33 BOOLEAN  *mDepexEvaluationStack        = NULL;
34 BOOLEAN  *mDepexEvaluationStackEnd     = NULL;
35 BOOLEAN  *mDepexEvaluationStackPointer = NULL;
36 
37 /**
38   Grow size of the Depex stack
39 
40   @retval EFI_SUCCESS           Stack successfully growed.
41   @retval EFI_OUT_OF_RESOURCES  There is not enough system memory to grow the stack.
42 
43 **/
44 EFI_STATUS
GrowDepexStack(VOID)45 GrowDepexStack (
46   VOID
47   )
48 {
49   BOOLEAN     *NewStack;
50   UINTN       Size;
51 
52   Size = DEPEX_STACK_SIZE_INCREMENT;
53   if (mDepexEvaluationStack != NULL) {
54     Size = Size + (mDepexEvaluationStackEnd - mDepexEvaluationStack);
55   }
56 
57   NewStack = AllocatePool (Size * sizeof (BOOLEAN));
58   if (NewStack == NULL) {
59     return EFI_OUT_OF_RESOURCES;
60   }
61 
62   if (mDepexEvaluationStack != NULL) {
63     //
64     // Copy to Old Stack to the New Stack
65     //
66     CopyMem (
67       NewStack,
68       mDepexEvaluationStack,
69       (mDepexEvaluationStackEnd - mDepexEvaluationStack) * sizeof (BOOLEAN)
70       );
71 
72     //
73     // Free The Old Stack
74     //
75     FreePool (mDepexEvaluationStack);
76   }
77 
78   //
79   // Make the Stack pointer point to the old data in the new stack
80   //
81   mDepexEvaluationStackPointer = NewStack + (mDepexEvaluationStackPointer - mDepexEvaluationStack);
82   mDepexEvaluationStack        = NewStack;
83   mDepexEvaluationStackEnd     = NewStack + Size;
84 
85   return EFI_SUCCESS;
86 }
87 
88 /**
89   Push an element onto the Boolean Stack.
90 
91   @param  Value                 BOOLEAN to push.
92 
93   @retval EFI_SUCCESS           The value was pushed onto the stack.
94   @retval EFI_OUT_OF_RESOURCES  There is not enough system memory to grow the stack.
95 
96 **/
97 EFI_STATUS
PushBool(IN BOOLEAN Value)98 PushBool (
99   IN BOOLEAN  Value
100   )
101 {
102   EFI_STATUS  Status;
103 
104   //
105   // Check for a stack overflow condition
106   //
107   if (mDepexEvaluationStackPointer == mDepexEvaluationStackEnd) {
108     //
109     // Grow the stack
110     //
111     Status = GrowDepexStack ();
112     if (EFI_ERROR (Status)) {
113       return Status;
114     }
115   }
116 
117   //
118   // Push the item onto the stack
119   //
120   *mDepexEvaluationStackPointer = Value;
121   mDepexEvaluationStackPointer++;
122 
123   return EFI_SUCCESS;
124 }
125 
126 /**
127   Pop an element from the Boolean stack.
128 
129   @param  Value                 BOOLEAN to pop.
130 
131   @retval EFI_SUCCESS           The value was popped onto the stack.
132   @retval EFI_ACCESS_DENIED     The pop operation underflowed the stack.
133 
134 **/
135 EFI_STATUS
PopBool(OUT BOOLEAN * Value)136 PopBool (
137   OUT BOOLEAN  *Value
138   )
139 {
140   //
141   // Check for a stack underflow condition
142   //
143   if (mDepexEvaluationStackPointer == mDepexEvaluationStack) {
144     return EFI_ACCESS_DENIED;
145   }
146 
147   //
148   // Pop the item off the stack
149   //
150   mDepexEvaluationStackPointer--;
151   *Value = *mDepexEvaluationStackPointer;
152   return EFI_SUCCESS;
153 }
154 
155 /**
156   This is the POSTFIX version of the dependency evaluator.  This code does
157   not need to handle Before or After, as it is not valid to call this
158   routine in this case. POSTFIX means all the math is done on top of the stack.
159 
160   @param  DriverEntry           DriverEntry element to update.
161 
162   @retval TRUE                  If driver is ready to run.
163   @retval FALSE                 If driver is not ready to run or some fatal error
164                                 was found.
165 
166 **/
167 BOOLEAN
SmmIsSchedulable(IN EFI_SMM_DRIVER_ENTRY * DriverEntry)168 SmmIsSchedulable (
169   IN  EFI_SMM_DRIVER_ENTRY   *DriverEntry
170   )
171 {
172   EFI_STATUS  Status;
173   UINT8       *Iterator;
174   BOOLEAN     Operator;
175   BOOLEAN     Operator2;
176   EFI_GUID    DriverGuid;
177   VOID        *Interface;
178 
179   Operator = FALSE;
180   Operator2 = FALSE;
181 
182   if (DriverEntry->After || DriverEntry->Before) {
183     //
184     // If Before or After Depex skip as SmmInsertOnScheduledQueueWhileProcessingBeforeAndAfter ()
185     // processes them.
186     //
187     return FALSE;
188   }
189 
190   DEBUG ((DEBUG_DISPATCH, "Evaluate SMM DEPEX for FFS(%g)\n", &DriverEntry->FileName));
191 
192   if (DriverEntry->Depex == NULL) {
193     //
194     // A NULL Depex means that the SMM driver is not built correctly.
195     // All SMM drivers must have a valid depex expression.
196     //
197     DEBUG ((DEBUG_DISPATCH, "  RESULT = FALSE (Depex is empty)\n"));
198     ASSERT (FALSE);
199     return FALSE;
200   }
201 
202   //
203   // Clean out memory leaks in Depex Boolean stack. Leaks are only caused by
204   // incorrectly formed DEPEX expressions
205   //
206   mDepexEvaluationStackPointer = mDepexEvaluationStack;
207 
208 
209   Iterator = DriverEntry->Depex;
210 
211   while (TRUE) {
212     //
213     // Check to see if we are attempting to fetch dependency expression instructions
214     // past the end of the dependency expression.
215     //
216     if (((UINTN)Iterator - (UINTN)DriverEntry->Depex) >= DriverEntry->DepexSize) {
217       DEBUG ((DEBUG_DISPATCH, "  RESULT = FALSE (Attempt to fetch past end of depex)\n"));
218       return FALSE;
219     }
220 
221     //
222     // Look at the opcode of the dependency expression instruction.
223     //
224     switch (*Iterator) {
225     case EFI_DEP_BEFORE:
226     case EFI_DEP_AFTER:
227       //
228       // For a well-formed Dependency Expression, the code should never get here.
229       // The BEFORE and AFTER are processed prior to this routine's invocation.
230       // If the code flow arrives at this point, there was a BEFORE or AFTER
231       // that were not the first opcodes.
232       //
233       DEBUG ((DEBUG_DISPATCH, "  RESULT = FALSE (Unexpected BEFORE or AFTER opcode)\n"));
234       ASSERT (FALSE);
235 
236     case EFI_DEP_PUSH:
237       //
238       // Push operator is followed by a GUID. Test to see if the GUID protocol
239       // is installed and push the boolean result on the stack.
240       //
241       CopyMem (&DriverGuid, Iterator + 1, sizeof (EFI_GUID));
242 
243       Status = SmmLocateProtocol (&DriverGuid, NULL, &Interface);
244       if (EFI_ERROR (Status)) {
245         //
246         // For SMM Driver, it may depend on uefi protocols
247         //
248         Status = gBS->LocateProtocol (&DriverGuid, NULL, &Interface);
249       }
250 
251       if (EFI_ERROR (Status)) {
252         DEBUG ((DEBUG_DISPATCH, "  PUSH GUID(%g) = FALSE\n", &DriverGuid));
253         Status = PushBool (FALSE);
254       } else {
255         DEBUG ((DEBUG_DISPATCH, "  PUSH GUID(%g) = TRUE\n", &DriverGuid));
256         *Iterator = EFI_DEP_REPLACE_TRUE;
257         Status = PushBool (TRUE);
258       }
259       if (EFI_ERROR (Status)) {
260         DEBUG ((DEBUG_DISPATCH, "  RESULT = FALSE (Unexpected error)\n"));
261         return FALSE;
262       }
263 
264       Iterator += sizeof (EFI_GUID);
265       break;
266 
267     case EFI_DEP_AND:
268       DEBUG ((DEBUG_DISPATCH, "  AND\n"));
269       Status = PopBool (&Operator);
270       if (EFI_ERROR (Status)) {
271         DEBUG ((DEBUG_DISPATCH, "  RESULT = FALSE (Unexpected error)\n"));
272         return FALSE;
273       }
274 
275       Status = PopBool (&Operator2);
276       if (EFI_ERROR (Status)) {
277         DEBUG ((DEBUG_DISPATCH, "  RESULT = FALSE (Unexpected error)\n"));
278         return FALSE;
279       }
280 
281       Status = PushBool ((BOOLEAN)(Operator && Operator2));
282       if (EFI_ERROR (Status)) {
283         DEBUG ((DEBUG_DISPATCH, "  RESULT = FALSE (Unexpected error)\n"));
284         return FALSE;
285       }
286       break;
287 
288     case EFI_DEP_OR:
289       DEBUG ((DEBUG_DISPATCH, "  OR\n"));
290       Status = PopBool (&Operator);
291       if (EFI_ERROR (Status)) {
292         DEBUG ((DEBUG_DISPATCH, "  RESULT = FALSE (Unexpected error)\n"));
293         return FALSE;
294       }
295 
296       Status = PopBool (&Operator2);
297       if (EFI_ERROR (Status)) {
298         DEBUG ((DEBUG_DISPATCH, "  RESULT = FALSE (Unexpected error)\n"));
299         return FALSE;
300       }
301 
302       Status = PushBool ((BOOLEAN)(Operator || Operator2));
303       if (EFI_ERROR (Status)) {
304         DEBUG ((DEBUG_DISPATCH, "  RESULT = FALSE (Unexpected error)\n"));
305         return FALSE;
306       }
307       break;
308 
309     case EFI_DEP_NOT:
310       DEBUG ((DEBUG_DISPATCH, "  NOT\n"));
311       Status = PopBool (&Operator);
312       if (EFI_ERROR (Status)) {
313         DEBUG ((DEBUG_DISPATCH, "  RESULT = FALSE (Unexpected error)\n"));
314         return FALSE;
315       }
316 
317       Status = PushBool ((BOOLEAN)(!Operator));
318       if (EFI_ERROR (Status)) {
319         DEBUG ((DEBUG_DISPATCH, "  RESULT = FALSE (Unexpected error)\n"));
320         return FALSE;
321       }
322       break;
323 
324     case EFI_DEP_TRUE:
325       DEBUG ((DEBUG_DISPATCH, "  TRUE\n"));
326       Status = PushBool (TRUE);
327       if (EFI_ERROR (Status)) {
328         DEBUG ((DEBUG_DISPATCH, "  RESULT = FALSE (Unexpected error)\n"));
329         return FALSE;
330       }
331       break;
332 
333     case EFI_DEP_FALSE:
334       DEBUG ((DEBUG_DISPATCH, "  FALSE\n"));
335       Status = PushBool (FALSE);
336       if (EFI_ERROR (Status)) {
337         DEBUG ((DEBUG_DISPATCH, "  RESULT = FALSE (Unexpected error)\n"));
338         return FALSE;
339       }
340       break;
341 
342     case EFI_DEP_END:
343       DEBUG ((DEBUG_DISPATCH, "  END\n"));
344       Status = PopBool (&Operator);
345       if (EFI_ERROR (Status)) {
346         DEBUG ((DEBUG_DISPATCH, "  RESULT = FALSE (Unexpected error)\n"));
347         return FALSE;
348       }
349       DEBUG ((DEBUG_DISPATCH, "  RESULT = %a\n", Operator ? "TRUE" : "FALSE"));
350       return Operator;
351 
352     case EFI_DEP_REPLACE_TRUE:
353       CopyMem (&DriverGuid, Iterator + 1, sizeof (EFI_GUID));
354       DEBUG ((DEBUG_DISPATCH, "  PUSH GUID(%g) = TRUE\n", &DriverGuid));
355       Status = PushBool (TRUE);
356       if (EFI_ERROR (Status)) {
357         DEBUG ((DEBUG_DISPATCH, "  RESULT = FALSE (Unexpected error)\n"));
358         return FALSE;
359       }
360 
361       Iterator += sizeof (EFI_GUID);
362       break;
363 
364     default:
365       DEBUG ((DEBUG_DISPATCH, "  RESULT = FALSE (Unknown opcode)\n"));
366       goto Done;
367     }
368 
369     //
370     // Skip over the Dependency Op Code we just processed in the switch.
371     // The math is done out of order, but it should not matter. That is
372     // we may add in the sizeof (EFI_GUID) before we account for the OP Code.
373     // This is not an issue, since we just need the correct end result. You
374     // need to be careful using Iterator in the loop as its intermediate value
375     // may be strange.
376     //
377     Iterator++;
378   }
379 
380 Done:
381   return FALSE;
382 }
383