summaryrefslogtreecommitdiffstats
path: root/ArmVirtPkg/Library/ArmVirtMonitorPeiLib/ArmVirtMonitorPeiLib.c
blob: 7b0ba6262b2fcc06854c6adeb56d290a97f5402a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/** @file
  Arm Monitor Library that chooses the conduit based on the PSCI node in the
  device tree provided by the VMM.

  Copyright (c) 2022, Arm Limited. All rights reserved.<BR>
  Copyright (c) 2024, Google LLC. All rights reserved.<BR>

  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include <Base.h>

#include <Library/ArmHvcLib.h>
#include <Library/ArmMonitorLib.h>
#include <Library/ArmSmcLib.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/FdtLib.h>

typedef enum {
  SmcccConduitUnknown,
  SmcccConduitSmc,
  SmcccConduitHvc,
} SMCCC_CONDUIT;

/**
  Discover the SMCCC conduit by parsing the PSCI device tree node.

  @return the discovered SMCCC conduit
**/
STATIC
SMCCC_CONDUIT
DiscoverSmcccConduit (
  VOID
  )
{
  VOID                *DeviceTreeBase;
  INT32               Node, Prev;
  INT32               Len;
  CONST FDT_PROPERTY  *Compatible;
  CONST CHAR8         *CompatibleItem;
  CONST FDT_PROPERTY  *Prop;

  DeviceTreeBase = (VOID *)(UINTN)PcdGet64 (PcdDeviceTreeInitialBaseAddress);
  ASSERT (FdtCheckHeader (DeviceTreeBase) == 0);

  //
  // Enumerate all FDT nodes looking for the PSCI node and capture the conduit
  //
  for (Prev = 0; ; Prev = Node) {
    Node = FdtNextNode (DeviceTreeBase, Prev, NULL);
    if (Node < 0) {
      break;
    }

    Compatible = FdtGetProperty (DeviceTreeBase, Node, "compatible", &Len);
    if (Compatible == NULL) {
      continue;
    }

    //
    // Iterate over the NULL-separated items in the compatible string
    //
    for (CompatibleItem = Compatible->Data; CompatibleItem < Compatible->Data + Len;
         CompatibleItem += 1 + AsciiStrLen (CompatibleItem))
    {
      if (AsciiStrCmp (CompatibleItem, "arm,psci-0.2") != 0) {
        continue;
      }

      Prop = FdtGetProperty (DeviceTreeBase, Node, "method", NULL);
      if (Prop == NULL) {
        DEBUG ((
          DEBUG_ERROR,
          "%a: Missing PSCI method property\n",
          __func__
          ));

        return SmcccConduitUnknown;
      }

      if (AsciiStrnCmp (Prop->Data, "hvc", 3) == 0) {
        return SmcccConduitHvc;
      } else if (AsciiStrnCmp (Prop->Data, "smc", 3) == 0) {
        return SmcccConduitSmc;
      } else {
        DEBUG ((
          DEBUG_ERROR,
          "%a: Unknown PSCI method \"%a\"\n",
          __func__,
          Prop
          ));

        return SmcccConduitUnknown;
      }
    }
  }

  return SmcccConduitUnknown;
}

/** Monitor call.

  An HyperVisor Call (HVC) or System Monitor Call (SMC) will be issued
  depending on the default conduit.

  @param [in,out]  Args    Arguments for the HVC/SMC.
**/
VOID
EFIAPI
ArmMonitorCall (
  IN OUT ARM_MONITOR_ARGS  *Args
  )
{
  switch (DiscoverSmcccConduit ()) {
    case SmcccConduitHvc:
      ArmCallHvc ((ARM_HVC_ARGS *)Args);
      break;

    case SmcccConduitSmc:
      ArmCallSmc ((ARM_SMC_ARGS *)Args);
      break;

    default:
      ASSERT (FALSE);
  }
}