summaryrefslogtreecommitdiff
path: root/MdeModulePkg/Library/GptLib/UnitTest/GptLibUnitTestCommon.c
blob: e7061a7e8fa499ce00c06d374b78d5aeb11878b6 (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
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
/** @file
  Shared mock disk and GPT construction helpers used by the GptLib
  host-based unit tests.

  Copyright (c) 2026, SUSE LLC. All rights reserved.<BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent
**/

#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include "GptLibUnitTestCommon.h"

UINT8  mDiskImage[DISK_IMAGE_SIZE];

EFI_BLOCK_IO_MEDIA     mBlockIoMedia;
EFI_BLOCK_IO_PROTOCOL  mBlockIo;
EFI_DISK_IO_PROTOCOL   mDiskIo;
BOOLEAN                mWriteProtected;

CONST EFI_GUID  mEspTypeGuid = {
  0xC12A7328, 0xF81F, 0x11D2, { 0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B }
};

CONST EFI_GUID  mPartitionGuid1 = {
  0x11111111, 0x2222, 0x3333, { 0x44, 0x44, 0x55, 0x55, 0x66, 0x66, 0x77, 0x77 }
};

CONST EFI_GUID  mPartitionGuid2 = {
  0x88888888, 0x9999, 0xAAAA, { 0xBB, 0xBB, 0xCC, 0xCC, 0xDD, 0xDD, 0xEE, 0xEE }
};

/**
  Mock implementation of EFI_DISK_IO_PROTOCOL.ReadDisk backed by the in-memory
  disk image.

  @param[in]   This        Pointer to the EFI_DISK_IO_PROTOCOL instance.
  @param[in]   MediaId     ID of the medium to read from.
  @param[in]   Offset      Starting byte offset on the logical disk.
  @param[in]   BufferSize  Number of bytes to read.
  @param[out]  Buffer      Buffer into which the data is read.

  @retval  EFI_SUCCESS       The data was read successfully.
  @retval  EFI_DEVICE_ERROR  The request was invalid or out of range.
**/
STATIC
EFI_STATUS
EFIAPI
MockReadDisk (
  IN  EFI_DISK_IO_PROTOCOL  *This,
  IN  UINT32                MediaId,
  IN  UINT64                Offset,
  IN  UINTN                 BufferSize,
  OUT VOID                  *Buffer
  )
{
  if ((Buffer == NULL) ||
      (MediaId != mBlockIoMedia.MediaId) ||
      (Offset > DISK_IMAGE_SIZE) ||
      (BufferSize > (DISK_IMAGE_SIZE - Offset)))
  {
    return EFI_DEVICE_ERROR;
  }

  CopyMem (Buffer, mDiskImage + Offset, BufferSize);
  return EFI_SUCCESS;
}

/**
  Mock implementation of EFI_DISK_IO_PROTOCOL.WriteDisk backed by the in-memory
  disk image, honoring the write-protected flag.

  @param[in]  This        Pointer to the EFI_DISK_IO_PROTOCOL instance.
  @param[in]  MediaId     ID of the medium to write to.
  @param[in]  Offset      Starting byte offset on the logical disk.
  @param[in]  BufferSize  Number of bytes to write.
  @param[in]  Buffer      Buffer holding the data to write.

  @retval  EFI_SUCCESS         The data was written successfully.
  @retval  EFI_WRITE_PROTECTED  The mock medium is write-protected.
  @retval  EFI_DEVICE_ERROR    The request was invalid or out of range.
**/
STATIC
EFI_STATUS
EFIAPI
MockWriteDisk (
  IN EFI_DISK_IO_PROTOCOL  *This,
  IN UINT32                MediaId,
  IN UINT64                Offset,
  IN UINTN                 BufferSize,
  IN VOID                  *Buffer
  )
{
  if (mWriteProtected) {
    return EFI_WRITE_PROTECTED;
  }

  if ((Buffer == NULL) ||
      (MediaId != mBlockIoMedia.MediaId) ||
      (Offset > DISK_IMAGE_SIZE) ||
      (BufferSize > (DISK_IMAGE_SIZE - Offset)))
  {
    return EFI_DEVICE_ERROR;
  }

  CopyMem (mDiskImage + Offset, Buffer, BufferSize);
  return EFI_SUCCESS;
}

/**
  Return a pointer into the in-memory disk image at the given LBA.

  @param[in]  Lba  Logical block address to locate.

  @return  Pointer to the start of the requested block within the disk image.
**/
UINT8 *
GetDiskLba (
  IN EFI_LBA  Lba
  )
{
  return mDiskImage + (UINTN)(Lba * SECTOR_SIZE);
}

/**
  Return a pointer to the primary GPT header within the in-memory disk image.

  @return  Pointer to the primary EFI_PARTITION_TABLE_HEADER.
**/
EFI_PARTITION_TABLE_HEADER *
GetPrimaryHeader (
  VOID
  )
{
  return (EFI_PARTITION_TABLE_HEADER *)(VOID *)GetDiskLba (PRIMARY_PART_HEADER_LBA);
}

/**
  Recompute and store the header CRC32 for the given GPT header.

  @param[in,out]  Header  The GPT header whose CRC32 field is updated.
**/
VOID
UpdateGptHeaderCrc (
  IN OUT EFI_PARTITION_TABLE_HEADER  *Header
  )
{
  Header->Header.CRC32 = 0;
  Header->Header.CRC32 = CalculateCrc32 (Header, Header->Header.HeaderSize);
}

/**
  Populate a partition entry with the ESP type GUID and the given attributes.

  @param[in,out]  Entry                The partition entry to fill.
  @param[in]      UniquePartitionGuid  Unique partition GUID to assign.
  @param[in]      StartingLba          Starting LBA of the partition.
  @param[in]      EndingLba            Ending LBA of the partition.
**/
VOID
FillPartitionEntry (
  IN OUT EFI_PARTITION_ENTRY  *Entry,
  IN CONST EFI_GUID           *UniquePartitionGuid,
  IN EFI_LBA                  StartingLba,
  IN EFI_LBA                  EndingLba
  )
{
  CopyGuid (&Entry->PartitionTypeGUID, &mEspTypeGuid);
  CopyGuid (&Entry->UniquePartitionGUID, UniquePartitionGuid);
  Entry->StartingLBA = StartingLba;
  Entry->EndingLBA   = EndingLba;
}

/**
  Construct a complete GPT header and partition entry array at the given LBA in
  the in-memory disk image, recalculating all CRCs.

  @param[in]  HeaderLba      LBA at which to write the GPT header.
  @param[in]  AlternateLba   Value stored in the header AlternateLBA field.
  @param[in]  EntryArrayLba  LBA at which to write the partition entry array.
  @param[in]  NumEntries     Number of partition entries in the array.
  @param[in]  EntrySize      Size in bytes of each partition entry.
  @param[in]  NumPartitions  Number of populated partition entries.
**/
VOID
WriteGptTableAt (
  IN EFI_LBA  HeaderLba,
  IN EFI_LBA  AlternateLba,
  IN EFI_LBA  EntryArrayLba,
  IN UINT32   NumEntries,
  IN UINT32   EntrySize,
  IN UINT32   NumPartitions
  )
{
  EFI_PARTITION_TABLE_HEADER  *Header;
  UINT8                       *Array;
  UINT32                      ArraySize;

  ArraySize = NumEntries * EntrySize;
  Array     = GetDiskLba (EntryArrayLba);
  ZeroMem (Array, ArraySize);
  if (NumPartitions >= 1) {
    FillPartitionEntry ((EFI_PARTITION_ENTRY *)(VOID *)Array, &mPartitionGuid1, 34, 133);
  }

  if (NumPartitions >= 2) {
    FillPartitionEntry ((EFI_PARTITION_ENTRY *)(VOID *)(Array + EntrySize), &mPartitionGuid2, 134, 233);
  }

  Header = (EFI_PARTITION_TABLE_HEADER *)(VOID *)GetDiskLba (HeaderLba);
  ZeroMem (Header, SECTOR_SIZE);
  Header->Header.Signature         = EFI_PTAB_HEADER_ID;
  Header->Header.Revision          = TEST_GPT_REVISION_V1;
  Header->Header.HeaderSize        = sizeof (EFI_PARTITION_TABLE_HEADER);
  Header->MyLBA                    = HeaderLba;
  Header->AlternateLBA             = AlternateLba;
  Header->FirstUsableLBA           = FIRST_USABLE_LBA;
  Header->LastUsableLBA            = LAST_USABLE_LBA;
  Header->PartitionEntryLBA        = EntryArrayLba;
  Header->NumberOfPartitionEntries = NumEntries;
  Header->SizeOfPartitionEntry     = EntrySize;
  Header->PartitionEntryArrayCRC32 = CalculateCrc32 (Array, ArraySize);
  UpdateGptHeaderCrc (Header);
}

/**
  Reset the mock disk and protocol state and lay down a valid primary GPT.
**/
VOID
SetupDiskWithValidPrimary (
  VOID
  )
{
  ZeroMem (mDiskImage, sizeof (mDiskImage));

  ZeroMem (&mBlockIoMedia, sizeof (mBlockIoMedia));
  mBlockIoMedia.MediaId   = 1;
  mBlockIoMedia.BlockSize = SECTOR_SIZE;
  mBlockIoMedia.LastBlock = LAST_LBA;

  ZeroMem (&mBlockIo, sizeof (mBlockIo));
  mBlockIo.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION;
  mBlockIo.Media    = &mBlockIoMedia;

  ZeroMem (&mDiskIo, sizeof (mDiskIo));
  mDiskIo.Revision  = EFI_DISK_IO_PROTOCOL_REVISION;
  mDiskIo.ReadDisk  = MockReadDisk;
  mDiskIo.WriteDisk = MockWriteDisk;

  mWriteProtected = FALSE;

  WriteGptTableAt (
    PRIMARY_PART_HEADER_LBA,
    LAST_LBA,
    PRIMARY_PART_HEADER_LBA + 1,
    NUM_PARTITION_ENTRIES,
    PARTITION_ENTRY_SIZE,
    2
    );
}

/**
  Invoke PartitionValidGptTable against the mock disk at the given LBA.

  @param[in]   Lba     LBA of the GPT header to validate.
  @param[out]  Header  Buffer to receive the validated GPT header.

  @retval  TRUE   The GPT table at Lba is valid.
  @retval  FALSE  The GPT table at Lba is invalid.
**/
BOOLEAN
ValidateAt (
  IN  EFI_LBA                     Lba,
  OUT EFI_PARTITION_TABLE_HEADER  *Header
  )
{
  return PartitionValidGptTable (&mBlockIo, &mDiskIo, Lba, Header);
}

/**
  Initialize a GPT header in memory for PartitionCheckGptEntry tests.

  @param[out]  Header      The GPT header to initialize.
  @param[in]   NumEntries  Number of partition entries to record.
  @param[in]   EntrySize   Size in bytes of each partition entry.
**/
VOID
InitCheckEntryHeader (
  OUT EFI_PARTITION_TABLE_HEADER  *Header,
  IN  UINT32                      NumEntries,
  IN  UINT32                      EntrySize
  )
{
  ZeroMem (Header, sizeof (*Header));
  Header->FirstUsableLBA           = FIRST_USABLE_LBA;
  Header->LastUsableLBA            = 200;
  Header->NumberOfPartitionEntries = NumEntries;
  Header->SizeOfPartitionEntry     = EntrySize;
}