File: AcceptPages.c

package info (click to toggle)
edk2 2025.02-8
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 271,704 kB
  • sloc: ansic: 2,109,987; asm: 263,832; perl: 227,730; python: 149,919; sh: 34,967; cpp: 21,813; makefile: 3,282; xml: 806; pascal: 721; lisp: 35; ruby: 16; sed: 6; tcl: 4
file content (181 lines) | stat: -rw-r--r-- 5,385 bytes parent folder | download | duplicates (7)
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
/** @file

  Unaccepted memory is a special type of private memory. In Td guest
  TDCALL [TDG.MEM.PAGE.ACCEPT] is invoked to accept the unaccepted
  memory before use it.

  Copyright (c) 2020 - 2021, Intel Corporation. All rights reserved.<BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <IndustryStandard/Tdx.h>
#include <Uefi/UefiBaseType.h>
#include <Library/TdxLib.h>
#include <Library/BaseMemoryLib.h>

UINT64  mNumberOfDuplicatedAcceptedPages;

#define TDX_ACCEPTPAGE_MAX_RETRIED  3

// PageSize is mapped to PageLevel like below:
// 4KB - 0, 2MB - 1
UINT32  mTdxAcceptPageLevelMap[2] = {
  SIZE_4KB,
  SIZE_2MB
};

#define INVALID_ACCEPT_PAGELEVEL  ARRAY_SIZE(mTdxAcceptPageLevelMap)

/**
  This function gets the PageLevel according to the input page size.

  @param[in]  PageSize    Page size

  @return UINT32          The mapped page level
**/
UINT32
GetGpaPageLevel (
  UINT32  PageSize
  )
{
  UINT32  Index;

  for (Index = 0; Index < ARRAY_SIZE (mTdxAcceptPageLevelMap); Index++) {
    if (mTdxAcceptPageLevelMap[Index] == PageSize) {
      break;
    }
  }

  return Index;
}

/**
  This function accept a pending private page, and initialize the page to
  all-0 using the TD ephemeral private key.

  Sometimes TDCALL [TDG.MEM.PAGE.ACCEPT] may return
  TDX_EXIT_REASON_PAGE_SIZE_MISMATCH. It indicates the input PageLevel is
  not workable. In this case we need to try to fallback to a smaller
  PageLevel if possible.

  @param[in]  StartAddress      Guest physical address of the private
                                page to accept. [63:52] and [11:0] must be 0.
  @param[in]  NumberOfPages     Number of the pages to be accepted.
  @param[in]  PageSize          GPA page size. Only accept 2M/4K size.

  @return EFI_SUCCESS           Accept successfully
  @return others                Indicate other errors
**/
EFI_STATUS
EFIAPI
TdAcceptPages (
  IN UINT64  StartAddress,
  IN UINT64  NumberOfPages,
  IN UINT32  PageSize
  )
{
  EFI_STATUS  Status;
  UINT64      Address;
  UINT64      TdxStatus;
  UINT64      Index;
  UINT32      GpaPageLevel;
  UINT32      PageSize2;
  UINTN       Retried;

  Retried = 0;

  if ((StartAddress & ~0xFFFFFFFFFF000ULL) != 0) {
    ASSERT (FALSE);
    DEBUG ((DEBUG_ERROR, "Accept page address(0x%llx) is not valid. [63:52] and [11:0] must be 0\n", StartAddress));
    return EFI_INVALID_PARAMETER;
  }

  Address = StartAddress;

  GpaPageLevel = GetGpaPageLevel (PageSize);
  if (GpaPageLevel == INVALID_ACCEPT_PAGELEVEL) {
    ASSERT (FALSE);
    DEBUG ((DEBUG_ERROR, "Accept page size must be 4K/2M. Invalid page size - 0x%llx\n", PageSize));
    return EFI_INVALID_PARAMETER;
  }

  Status = EFI_SUCCESS;
  for (Index = 0; Index < NumberOfPages; Index++) {
    Retried = 0;

DoAcceptPage:
    TdxStatus = TdCall (TDCALL_TDACCEPTPAGE, Address | GpaPageLevel, 0, 0, 0);
    if (TdxStatus != TDX_EXIT_REASON_SUCCESS) {
      if ((TdxStatus & ~0xFFFFULL) == TDX_EXIT_REASON_PAGE_ALREADY_ACCEPTED) {
        //
        // Already accepted
        //
        mNumberOfDuplicatedAcceptedPages++;
        DEBUG ((DEBUG_WARN, "Page at Address (0x%llx) has already been accepted. - %d\n", Address, mNumberOfDuplicatedAcceptedPages));
      } else if ((TdxStatus & ~0xFFFFULL) == TDX_EXIT_REASON_PAGE_SIZE_MISMATCH) {
        //
        // GpaPageLevel is mismatch, fall back to a smaller GpaPageLevel if possible
        //
        DEBUG ((DEBUG_VERBOSE, "Address %llx cannot be accepted in PageLevel of %d\n", Address, GpaPageLevel));

        if (GpaPageLevel == 0) {
          //
          // Cannot fall back to smaller page level
          //
          DEBUG ((DEBUG_ERROR, "AcceptPage cannot fallback from PageLevel %d\n", GpaPageLevel));
          Status = EFI_INVALID_PARAMETER;
          break;
        } else {
          //
          // Fall back to a smaller page size
          //
          PageSize2 = mTdxAcceptPageLevelMap[GpaPageLevel - 1];
          Status    = TdAcceptPages (Address, 512, PageSize2);
          if (EFI_ERROR (Status)) {
            break;
          }
        }
      } else if ((TdxStatus & ~0xFFFFULL) == TDX_EXIT_REASON_OPERAND_BUSY) {
        //
        // Concurrent TDG.MEM.PAGE.ACCEPT is using the same Secure EPT entry
        // So try it again. There is a max retried count. If Retried exceeds the max count,
        // report the error and quit.
        //
        Retried += 1;
        if (Retried > TDX_ACCEPTPAGE_MAX_RETRIED) {
          DEBUG ((
            DEBUG_ERROR,
            "Address %llx (%d) failed to be accepted because of OPERAND_BUSY. Retried %d time.\n",
            Address,
            Index,
            Retried
            ));
          Status = EFI_INVALID_PARAMETER;
          break;
        } else {
          goto DoAcceptPage;
        }
      } else {
        //
        // Other errors
        //
        DEBUG ((
          DEBUG_ERROR,
          "Address %llx (%d) failed to be accepted. Error = 0x%llx\n",
          Address,
          Index,
          TdxStatus
          ));
        Status = EFI_INVALID_PARAMETER;
        break;
      }
    }

    Address += PageSize;
  }

  return Status;
}