summaryrefslogtreecommitdiffstats
path: root/MdeModulePkg/Library/ResetUtilityLib/ResetUtility.c
blob: 4ca8425705f882b60e982e8e12e9813cf7de1901 (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
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
/** @file
  This contains the business logic for the module-specific Reset Helper functions.

  Copyright (c) 2017 - 2019 Intel Corporation. All rights reserved.<BR>
  Copyright (c) 2016 Microsoft Corporation. All rights reserved.<BR>

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

**/
#include <Uefi.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/ResetSystemLib.h>

#pragma pack(1)
typedef struct {
  CHAR16    NullTerminator;
  GUID      ResetSubtype;
} RESET_UTILITY_GUID_SPECIFIC_RESET_DATA;
#pragma pack()

STATIC_ASSERT (
  sizeof (RESET_UTILITY_GUID_SPECIFIC_RESET_DATA) == 18,
  "sizeof (RESET_UTILITY_GUID_SPECIFIC_RESET_DATA) is expected to be 18 bytes"
  );

/**
  This is a shorthand helper function to reset with reset type and a subtype
  so that the caller doesn't have to bother with a function that has half
  a dozen parameters.

  This will generate a reset with status EFI_SUCCESS, a NULL string, and
  no custom data. The subtype will be formatted in such a way that it can be
  picked up by notification registrations and custom handlers.

  NOTE: This call will fail if the architectural ResetSystem underpinnings
        are not initialized. For DXE, you can add gEfiResetArchProtocolGuid
        to your DEPEX.

  @param[in]  ResetType     The default EFI_RESET_TYPE of the reset.
  @param[in]  ResetSubtype  GUID pointer for the reset subtype to be used.

**/
VOID
EFIAPI
ResetSystemWithSubtype (
  IN EFI_RESET_TYPE  ResetType,
  IN CONST  GUID     *ResetSubtype
  )
{
  RESET_UTILITY_GUID_SPECIFIC_RESET_DATA  ResetData;

  ResetData.NullTerminator = CHAR_NULL;
  CopyGuid (
    (GUID *)((UINT8 *)&ResetData + OFFSET_OF (RESET_UTILITY_GUID_SPECIFIC_RESET_DATA, ResetSubtype)),
    ResetSubtype
    );

  ResetSystem (ResetType, EFI_SUCCESS, sizeof (ResetData), &ResetData);
}

/**
  This is a shorthand helper function to reset with the reset type
  'EfiResetPlatformSpecific' and a subtype so that the caller doesn't
  have to bother with a function that has half a dozen parameters.

  This will generate a reset with status EFI_SUCCESS, a NULL string, and
  no custom data. The subtype will be formatted in such a way that it can be
  picked up by notification registrations and custom handlers.

  NOTE: This call will fail if the architectural ResetSystem underpinnings
        are not initialized. For DXE, you can add gEfiResetArchProtocolGuid
        to your DEPEX.

  @param[in]  ResetSubtype  GUID pointer for the reset subtype to be used.

**/
VOID
EFIAPI
ResetPlatformSpecificGuid (
  IN CONST  GUID  *ResetSubtype
  )
{
  ResetSystemWithSubtype (EfiResetPlatformSpecific, ResetSubtype);
}

/**
  This function examines the DataSize and ResetData parameters passed to
  to ResetSystem() and detemrines if the ResetData contains a Null-terminated
  Unicode string followed by a GUID specific subtype.  If the GUID specific
  subtype is present, then a pointer to the GUID value in ResetData is returned.

  @param[in]  DataSize    The size, in bytes, of ResetData.
  @param[in]  ResetData   Pointer to the data buffer passed into ResetSystem().

  @retval     Pointer     Pointer to the GUID value in ResetData.
  @retval     NULL        ResetData is NULL.
  @retval     NULL        ResetData does not start with a Null-terminated
                          Unicode string.
  @retval     NULL        A Null-terminated Unicode string is present, but there
                          are less than sizeof (GUID) bytes after the string.
  @retval     NULL        No subtype is found.

**/
GUID *
EFIAPI
GetResetPlatformSpecificGuid (
  IN UINTN       DataSize,
  IN CONST VOID  *ResetData
  )
{
  UINTN  ResetDataStringSize;
  GUID   *ResetSubtypeGuid;

  //
  // Make sure parameters are valid
  //
  if ((ResetData == NULL) || (DataSize < sizeof (GUID))) {
    return NULL;
  }

  //
  // Determine the number of bytes in the Null-terminated Unicode string
  // at the beginning of ResetData including the Null terminator.
  //
  ResetDataStringSize = StrnSizeS (ResetData, (DataSize / sizeof (CHAR16)));

  //
  // Now, assuming that we have enough data for a GUID after the string, the
  // GUID should be immediately after the string itself.
  //
  if ((ResetDataStringSize < DataSize) && ((DataSize - ResetDataStringSize) >= sizeof (GUID))) {
    ResetSubtypeGuid = (GUID *)((UINT8 *)ResetData + ResetDataStringSize);
    DEBUG ((DEBUG_VERBOSE, "%a - Detected reset subtype %g...\n", __func__, ResetSubtypeGuid));
    return ResetSubtypeGuid;
  }

  return NULL;
}

/**
  This is a helper function that creates the reset data buffer that can be
  passed into ResetSystem().

  The reset data buffer is returned in ResetData and contains ResetString
  followed by the ResetSubtype GUID followed by the ExtraData.

  NOTE: Strings are internally limited by MAX_UINT16.

  @param[in, out] ResetDataSize  On input, the size of the ResetData buffer. On
                                 output, either the total number of bytes
                                 copied, or the required buffer size.
  @param[in, out] ResetData      A pointer to the buffer in which to place the
                                 final structure.
  @param[in]      ResetSubtype   Pointer to the GUID specific subtype.  This
                                 parameter is optional and may be NULL.
  @param[in]      ResetString    Pointer to a Null-terminated Unicode string
                                 that describes the reset.  This parameter is
                                 optional and may be NULL.
  @param[in]      ExtraDataSize  The size, in bytes, of ExtraData buffer.
  @param[in]      ExtraData      Pointer to a buffer of extra data.  This
                                 parameter is optional and may be NULL.

  @retval     RETURN_SUCCESS             ResetDataSize and ResetData are updated.
  @retval     RETURN_INVALID_PARAMETER   ResetDataSize is NULL.
  @retval     RETURN_INVALID_PARAMETER   ResetData is NULL.
  @retval     RETURN_INVALID_PARAMETER   ExtraData was provided without a
                                         ResetSubtype. This is not supported by the
                                         UEFI spec.
  @retval     RETURN_BUFFER_TOO_SMALL    An insufficient buffer was provided.
                                         ResetDataSize is updated with minimum size
                                         required.
**/
RETURN_STATUS
EFIAPI
BuildResetData (
  IN OUT   UINTN   *ResetDataSize,
  IN OUT   VOID    *ResetData,
  IN CONST GUID    *ResetSubtype  OPTIONAL,
  IN CONST CHAR16  *ResetString   OPTIONAL,
  IN       UINTN   ExtraDataSize  OPTIONAL,
  IN CONST VOID    *ExtraData     OPTIONAL
  )
{
  UINTN  ResetStringSize;
  UINTN  ResetDataBufferSize;
  UINT8  *Data;

  //
  // If the size return pointer is NULL.
  //
  if (ResetDataSize == NULL) {
    return RETURN_INVALID_PARAMETER;
  }

  //
  // If extra data is indicated, but pointer is NULL.
  //
  if ((ExtraDataSize > 0) && (ExtraData == NULL)) {
    return RETURN_INVALID_PARAMETER;
  }

  //
  // If extra data is indicated, but no subtype GUID is supplied.
  //
  if ((ResetSubtype == NULL) && (ExtraDataSize > 0)) {
    return RETURN_INVALID_PARAMETER;
  }

  //
  // Determine the final string.
  //
  if (ResetString == NULL) {
    ResetString = L"";     // Use an empty string.
  }

  //
  // Calculate the total buffer required for ResetData.
  //
  ResetStringSize     = StrnSizeS (ResetString, MAX_UINT16);
  ResetDataBufferSize = ResetStringSize + ExtraDataSize;
  if (ResetSubtype != NULL) {
    ResetDataBufferSize += sizeof (GUID);
  }

  //
  // At this point, if the buffer isn't large enough (or if
  // the buffer is NULL) we cannot proceed.
  //
  if (*ResetDataSize < ResetDataBufferSize) {
    *ResetDataSize = ResetDataBufferSize;
    return RETURN_BUFFER_TOO_SMALL;
  }

  *ResetDataSize = ResetDataBufferSize;
  if (ResetData == NULL) {
    return RETURN_INVALID_PARAMETER;
  }

  //
  // Fill in ResetData with ResetString, the ResetSubtype GUID, and extra data
  //
  Data = (UINT8 *)ResetData;
  CopyMem (Data, ResetString, ResetStringSize);
  Data += ResetStringSize;
  if (ResetSubtype != NULL) {
    CopyMem (Data, ResetSubtype, sizeof (GUID));
    Data += sizeof (GUID);
  }

  if (ExtraDataSize > 0) {
    CopyMem (Data, ExtraData, ExtraDataSize);
  }

  return RETURN_SUCCESS;
}