summaryrefslogtreecommitdiffstats
path: root/DynamicTablesPkg/Library/Common/AcpiHelperLib/AcpiHelper.c
blob: 5afd257e49804320c67302d2e411a8ca8347b15f (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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
/** @file
  Acpi Helper

  Copyright (c) 2017 - 2021, Arm Limited. All rights reserved.<BR>

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

#include <Library/BaseLib.h>
#include <Library/DebugLib.h>

// Module specific include files.
#include <Library/AcpiHelperLib.h>

/** Convert a hex number to its ASCII code.

 @param [in]  Hex   Hex number to convert.
                    Must be 0 <= x < 16.

 @return The ASCII code corresponding to x.
         -1 if error.
**/
UINT8
EFIAPI
AsciiFromHex (
  IN  UINT8   Hex
  )
{
  if (Hex < 10) {
    return (UINT8)(Hex + '0');
  }

  if (Hex < 16) {
    return (UINT8)(Hex - 10 + 'A');
  }

  ASSERT (FALSE);
  return (UINT8)-1;
}

/** Convert an ASCII char representing an hexadecimal number
    to its integer value.

 @param [in]  Char  Char to convert.
                    Must be between '0'-'9' or 'A'-'F' or 'a'-'f'.

 @return The corresponding integer (between 0-16).
         -1 if error.
**/
UINT8
EFIAPI
HexFromAscii (
  IN  CHAR8   Char
  )
{
  if ((Char >= '0') && (Char <= '9')) {
    return (UINT8)(Char - '0');
  }

  if ((Char >= 'A') && (Char <= 'F')) {
    return (UINT8)(Char - 'A' + 10);
  }

  if ((Char >= 'a') && (Char <= 'f')) {
    return (UINT8)(Char - 'a' + 10);
  }

  ASSERT (FALSE);
  return (UINT8)-1;
}

/** Check if a HID is a valid PNP ID.

  @param     [in] Hid     The Hid to validate.

  @retval    TRUE         The Hid is a valid PNP ID.
  @retval    FALSE        The Hid is not a valid PNP ID.
**/
BOOLEAN
IsValidPnpId (
  IN  CONST CHAR8  * Hid
  )
{
  UINTN Index;

  if (AsciiStrLen (Hid) != 7) {
    return FALSE;
  }

  // A valid PNP ID must be of the form "AAA####"
  // where A is an uppercase letter and # is a hex digit.
  for (Index = 0; Index < 3; Index++) {
    if (!IS_UPPER_CHAR (Hid[Index])) {
      return FALSE;
    }
  }

  for (Index = 3; Index < 7; Index++) {
    if (!IS_UPPER_HEX (Hid[Index])) {
      return FALSE;
    }
  }

  return TRUE;
}

/** Check if a HID is a valid ACPI ID.

  @param     [in] Hid     The Hid to validate.

  @retval    TRUE         The Hid is a valid ACPI ID.
  @retval    FALSE        The Hid is not a valid ACPI ID.
**/
BOOLEAN
IsValidAcpiId (
  IN  CONST CHAR8  * Hid
  )
{
  UINTN Index;

  if (AsciiStrLen (Hid) != 8) {
    return FALSE;
  }

  // A valid ACPI ID must be of the form "NNNN####"
  // where N is an uppercase letter or a digit ('0'-'9')
  // and # is a hex digit.
  for (Index = 0; Index < 4; Index++) {
    if (!(IS_UPPER_CHAR (Hid[Index]) || IS_DIGIT (Hid[Index]))) {
      return FALSE;
    }
  }

  for (Index = 4; Index < 8; Index++) {
    if (!IS_UPPER_HEX (Hid[Index])) {
      return FALSE;
    }
  }

  return TRUE;
}

/** Convert a EisaId string to its compressed UINT32 equivalent.

  Cf. ACPI 6.4 specification, s19.3.4 "ASL Macros": "Eisaid"

  @param  [in]  EisaIdStr   Input EisaId string.
  @param  [out] EisaIdInt   Output EisaId UINT32 (compressed).

  @retval EFI_SUCCESS             The function completed successfully.
  @retval EFI_INVALID_PARAMETER   Invalid parameter.
**/
EFI_STATUS
EFIAPI
AmlGetEisaIdFromString (
  IN  CONST CHAR8   * EisaIdStr,
  OUT       UINT32  * EisaIdInt
  )
{
  if ((EisaIdStr == NULL)         ||
      (!IsValidPnpId (EisaIdStr)) ||
      (EisaIdInt == NULL)) {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  /* Cf. ACPI 6.4 specification, s19.3.4 "ASL Macros": "Eisaid"

  Converts and compresses the 7-character text argument into its corresponding
  4-byte numeric EISA ID encoding (Integer). This can be used when declaring
  IDs for devices that are EISA IDs.

  The algorithm used to convert the TextID is as shown in the following
  example:
    Starting with a seven character input string "PNP0303", we want to create
    a DWordConst. This string contains a three character manufacturer code
    "PNP", a three character hex product identifier "030", and a one character
    revision identifier "3".
    The compressed manufacturer code is created as follows:
      1) Find hex ASCII value for each letter
      2) Subtract 40h from each ASCII value
      3) Retain 5 least significant bits for each letter and discard remaining
         0's:

      Byte 0:
        Bit 7: reserved (0)
        Bit 6-2: 1st character of compressed mfg code "P"
        Bit 1-0: Upper 2 bits of 2nd character of mfg code "N"
      Byte 1:
        Bit 7-5: Lower 3 bits of 2nd character of mfg code "N"
        Bit 4-0: 3rd character of mfg code "P"
      Byte 2:
        Bit 7-4: 1st hex digit of product number "0"
        Bit 3-0: 2nd hex digit of product number "3"
      Byte 3:
        Bit 7-4: 3rd hex digit of product number "0"
        Bit 3-0: 4th hex digit of product number "3"
  */
  *EisaIdInt = SwapBytes32 (
    ((EisaIdStr[0] - 0x40) << 26)       |
    ((EisaIdStr[1] - 0x40) << 21)       |
    ((EisaIdStr[2] - 0x40) << 16)       |
    (HexFromAscii (EisaIdStr[3]) << 12) |
    (HexFromAscii (EisaIdStr[4]) << 8)  |
    (HexFromAscii (EisaIdStr[5]) << 4)  |
    (HexFromAscii (EisaIdStr[6]))
    );

  return EFI_SUCCESS;
}