1 | /* $Id: PGMAllGst-armv8.cpp.h 108142 2025-02-10 14:38:31Z vboxsync $ */
|
---|
2 | /** @file
|
---|
3 | * PGM - Page Manager, ARMv8 Guest Paging Template - All context code.
|
---|
4 | */
|
---|
5 |
|
---|
6 | /*
|
---|
7 | * Copyright (C) 2023-2024 Oracle and/or its affiliates.
|
---|
8 | *
|
---|
9 | * This file is part of VirtualBox base platform packages, as
|
---|
10 | * available from https://www.virtualbox.org.
|
---|
11 | *
|
---|
12 | * This program is free software; you can redistribute it and/or
|
---|
13 | * modify it under the terms of the GNU General Public License
|
---|
14 | * as published by the Free Software Foundation, in version 3 of the
|
---|
15 | * License.
|
---|
16 | *
|
---|
17 | * This program is distributed in the hope that it will be useful, but
|
---|
18 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
20 | * General Public License for more details.
|
---|
21 | *
|
---|
22 | * You should have received a copy of the GNU General Public License
|
---|
23 | * along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
24 | *
|
---|
25 | * SPDX-License-Identifier: GPL-3.0-only
|
---|
26 | */
|
---|
27 |
|
---|
28 |
|
---|
29 |
|
---|
30 | DECLINLINE(int) pgmGstWalkReturnNotPresent(PVMCPUCC pVCpu, PPGMPTWALK pWalk, uint8_t uLevel)
|
---|
31 | {
|
---|
32 | NOREF(pVCpu);
|
---|
33 | pWalk->fNotPresent = true;
|
---|
34 | pWalk->uLevel = uLevel;
|
---|
35 | pWalk->fFailed = PGM_WALKFAIL_NOT_PRESENT
|
---|
36 | | ((uint32_t)uLevel << PGM_WALKFAIL_LEVEL_SHIFT);
|
---|
37 | return VERR_PAGE_TABLE_NOT_PRESENT;
|
---|
38 | }
|
---|
39 |
|
---|
40 | DECLINLINE(int) pgmGstWalkReturnBadPhysAddr(PVMCPUCC pVCpu, PPGMPTWALK pWalk, uint8_t uLevel, int rc)
|
---|
41 | {
|
---|
42 | AssertMsg(rc == VERR_PGM_INVALID_GC_PHYSICAL_ADDRESS, ("%Rrc\n", rc)); NOREF(rc); NOREF(pVCpu);
|
---|
43 | pWalk->fBadPhysAddr = true;
|
---|
44 | pWalk->uLevel = uLevel;
|
---|
45 | pWalk->fFailed = PGM_WALKFAIL_BAD_PHYSICAL_ADDRESS
|
---|
46 | | ((uint32_t)uLevel << PGM_WALKFAIL_LEVEL_SHIFT);
|
---|
47 | return VERR_PAGE_TABLE_NOT_PRESENT;
|
---|
48 | }
|
---|
49 |
|
---|
50 |
|
---|
51 | DECLINLINE(int) pgmGstWalkReturnRsvdError(PVMCPUCC pVCpu, PPGMPTWALK pWalk, uint8_t uLevel)
|
---|
52 | {
|
---|
53 | NOREF(pVCpu);
|
---|
54 | pWalk->fRsvdError = true;
|
---|
55 | pWalk->uLevel = uLevel;
|
---|
56 | pWalk->fFailed = PGM_WALKFAIL_RESERVED_BITS
|
---|
57 | | ((uint32_t)uLevel << PGM_WALKFAIL_LEVEL_SHIFT);
|
---|
58 | return VERR_PAGE_TABLE_NOT_PRESENT;
|
---|
59 | }
|
---|
60 |
|
---|
61 |
|
---|
62 | DECLINLINE(int) pgmGstGetPageArmv8Hack(PVMCPUCC pVCpu, RTGCPTR GCPtr, PPGMPTWALK pWalk)
|
---|
63 | {
|
---|
64 | VMCPU_ASSERT_EMT(pVCpu);
|
---|
65 | Assert(pWalk);
|
---|
66 |
|
---|
67 | pWalk->fSucceeded = false;
|
---|
68 |
|
---|
69 | RTGCPHYS GCPhysPt = CPUMGetEffectiveTtbr(pVCpu, GCPtr);
|
---|
70 | if (GCPhysPt == RTGCPHYS_MAX) /* MMU disabled? */
|
---|
71 | {
|
---|
72 | pWalk->GCPtr = GCPtr;
|
---|
73 | pWalk->fSucceeded = true;
|
---|
74 | pWalk->GCPhys = GCPtr;
|
---|
75 | return VINF_SUCCESS;
|
---|
76 | }
|
---|
77 |
|
---|
78 | /* Do the translation. */
|
---|
79 | /** @todo This is just a sketch to get something working for debugging, assumes 4KiB granules and 48-bit output address.
|
---|
80 | * Needs to be moved to PGMAllGst like on x86 and implemented for 16KiB and 64KiB granule sizes. */
|
---|
81 | uint64_t u64TcrEl1 = CPUMGetTcrEl1(pVCpu);
|
---|
82 | uint8_t u8TxSz = (GCPtr & RT_BIT_64(55))
|
---|
83 | ? ARMV8_TCR_EL1_AARCH64_T1SZ_GET(u64TcrEl1)
|
---|
84 | : ARMV8_TCR_EL1_AARCH64_T0SZ_GET(u64TcrEl1);
|
---|
85 | uint8_t uLookupLvl;
|
---|
86 | RTGCPHYS fLookupMask;
|
---|
87 |
|
---|
88 | /*
|
---|
89 | * From: https://github.com/codingbelief/arm-architecture-reference-manual-for-armv8-a/blob/master/en/chapter_d4/d42_2_controlling_address_translation_stages.md
|
---|
90 | * For all translation stages
|
---|
91 | * The maximum TxSZ value is 39. If TxSZ is programmed to a value larger than 39 then it is IMPLEMENTATION DEFINED whether:
|
---|
92 | * - The implementation behaves as if the field is programmed to 39 for all purposes other than reading back the value of the field.
|
---|
93 | * - Any use of the TxSZ value generates a Level 0 Translation fault for the stage of translation at which TxSZ is used.
|
---|
94 | *
|
---|
95 | * For a stage 1 translation
|
---|
96 | * The minimum TxSZ value is 16. If TxSZ is programmed to a value smaller than 16 then it is IMPLEMENTATION DEFINED whether:
|
---|
97 | * - The implementation behaves as if the field were programmed to 16 for all purposes other than reading back the value of the field.
|
---|
98 | * - Any use of the TxSZ value generates a stage 1 Level 0 Translation fault.
|
---|
99 | *
|
---|
100 | * We currently choose the former for both.
|
---|
101 | */
|
---|
102 | if (/*u8TxSz >= 16 &&*/ u8TxSz <= 24)
|
---|
103 | {
|
---|
104 | uLookupLvl = 0;
|
---|
105 | fLookupMask = RT_BIT_64(24 - u8TxSz + 1) - 1;
|
---|
106 | }
|
---|
107 | else if (u8TxSz >= 25 && u8TxSz <= 33)
|
---|
108 | {
|
---|
109 | uLookupLvl = 1;
|
---|
110 | fLookupMask = RT_BIT_64(33 - u8TxSz + 1) - 1;
|
---|
111 | }
|
---|
112 | else /*if (u8TxSz >= 34 && u8TxSz <= 39)*/
|
---|
113 | {
|
---|
114 | uLookupLvl = 2;
|
---|
115 | fLookupMask = RT_BIT_64(39 - u8TxSz + 1) - 1;
|
---|
116 | }
|
---|
117 | /*else
|
---|
118 | return pgmGstWalkReturnBadPhysAddr(pVCpu, pWalk, 0, VERR_PGM_INVALID_GC_PHYSICAL_ADDRESS);*/ /** @todo Better status (Invalid TCR config). */
|
---|
119 |
|
---|
120 | uint64_t *pu64Pt = NULL;
|
---|
121 | uint64_t uPt;
|
---|
122 | int rc;
|
---|
123 | if (uLookupLvl == 0)
|
---|
124 | {
|
---|
125 | rc = PGM_GCPHYS_2_PTR_BY_VMCPU(pVCpu, GCPhysPt, &pu64Pt);
|
---|
126 | if (RT_SUCCESS(rc)) { /* probable */ }
|
---|
127 | else return pgmGstWalkReturnBadPhysAddr(pVCpu, pWalk, 0, rc);
|
---|
128 |
|
---|
129 | uPt = pu64Pt[(GCPtr >> 39) & fLookupMask];
|
---|
130 | if (uPt & RT_BIT_64(0)) { /* probable */ }
|
---|
131 | else return pgmGstWalkReturnNotPresent(pVCpu, pWalk, 0);
|
---|
132 |
|
---|
133 | if (uPt & RT_BIT_64(1)) { /* probable */ }
|
---|
134 | else return pgmGstWalkReturnRsvdError(pVCpu, pWalk, 0); /** @todo Only supported if TCR_EL1.DS is set. */
|
---|
135 |
|
---|
136 | /* All nine bits from now on. */
|
---|
137 | fLookupMask = RT_BIT_64(9) - 1;
|
---|
138 | GCPhysPt = (RTGCPHYS)(uPt & UINT64_C(0xfffffffff000));
|
---|
139 | }
|
---|
140 |
|
---|
141 | if (uLookupLvl <= 1)
|
---|
142 | {
|
---|
143 | rc = PGM_GCPHYS_2_PTR_BY_VMCPU(pVCpu, GCPhysPt, &pu64Pt);
|
---|
144 | if (RT_SUCCESS(rc)) { /* probable */ }
|
---|
145 | else return pgmGstWalkReturnBadPhysAddr(pVCpu, pWalk, 1, rc);
|
---|
146 |
|
---|
147 | uPt = pu64Pt[(GCPtr >> 30) & fLookupMask];
|
---|
148 | if (uPt & RT_BIT_64(0)) { /* probable */ }
|
---|
149 | else return pgmGstWalkReturnNotPresent(pVCpu, pWalk, 1);
|
---|
150 |
|
---|
151 | if (uPt & RT_BIT_64(1)) { /* probable */ }
|
---|
152 | else
|
---|
153 | {
|
---|
154 | /* Block descriptor (1G page). */
|
---|
155 | pWalk->GCPtr = GCPtr;
|
---|
156 | pWalk->fSucceeded = true;
|
---|
157 | pWalk->GCPhys = (RTGCPHYS)(uPt & UINT64_C(0xffffc0000000)) | (GCPtr & (RTGCPTR)(_1G - 1));
|
---|
158 | pWalk->fGigantPage = true;
|
---|
159 | return VINF_SUCCESS;
|
---|
160 | }
|
---|
161 |
|
---|
162 | /* All nine bits from now on. */
|
---|
163 | fLookupMask = RT_BIT_64(9) - 1;
|
---|
164 | GCPhysPt = (RTGCPHYS)(uPt & UINT64_C(0xfffffffff000));
|
---|
165 | }
|
---|
166 |
|
---|
167 | if (uLookupLvl <= 2)
|
---|
168 | {
|
---|
169 | rc = PGM_GCPHYS_2_PTR_BY_VMCPU(pVCpu, GCPhysPt, &pu64Pt);
|
---|
170 | if (RT_SUCCESS(rc)) { /* probable */ }
|
---|
171 | else return pgmGstWalkReturnBadPhysAddr(pVCpu, pWalk, 2, rc);
|
---|
172 |
|
---|
173 | uPt = pu64Pt[(GCPtr >> 21) & fLookupMask];
|
---|
174 | if (uPt & RT_BIT_64(0)) { /* probable */ }
|
---|
175 | else return pgmGstWalkReturnNotPresent(pVCpu, pWalk, 2);
|
---|
176 |
|
---|
177 | if (uPt & RT_BIT_64(1)) { /* probable */ }
|
---|
178 | else
|
---|
179 | {
|
---|
180 | /* Block descriptor (2M page). */
|
---|
181 | pWalk->GCPtr = GCPtr;
|
---|
182 | pWalk->fSucceeded = true;
|
---|
183 | pWalk->GCPhys = (RTGCPHYS)(uPt & UINT64_C(0xffffffe00000)) | (GCPtr & (RTGCPTR)(_2M - 1));
|
---|
184 | pWalk->fBigPage = true;
|
---|
185 | return VINF_SUCCESS;
|
---|
186 | }
|
---|
187 |
|
---|
188 | /* All nine bits from now on. */
|
---|
189 | fLookupMask = RT_BIT_64(9) - 1;
|
---|
190 | GCPhysPt = (RTGCPHYS)(uPt & UINT64_C(0xfffffffff000));
|
---|
191 | }
|
---|
192 |
|
---|
193 | Assert(uLookupLvl <= 3);
|
---|
194 |
|
---|
195 | /* Next level. */
|
---|
196 | rc = PGM_GCPHYS_2_PTR_BY_VMCPU(pVCpu, GCPhysPt, &pu64Pt);
|
---|
197 | if (RT_SUCCESS(rc)) { /* probable */ }
|
---|
198 | else return pgmGstWalkReturnBadPhysAddr(pVCpu, pWalk, 3, rc);
|
---|
199 |
|
---|
200 | uPt = pu64Pt[(GCPtr & UINT64_C(0x1ff000)) >> 12];
|
---|
201 | if (uPt & RT_BIT_64(0)) { /* probable */ }
|
---|
202 | else return pgmGstWalkReturnNotPresent(pVCpu, pWalk, 3);
|
---|
203 |
|
---|
204 | if (uPt & RT_BIT_64(1)) { /* probable */ }
|
---|
205 | else return pgmGstWalkReturnRsvdError(pVCpu, pWalk, 3); /** No block descriptors. */
|
---|
206 |
|
---|
207 | pWalk->GCPtr = GCPtr;
|
---|
208 | pWalk->fSucceeded = true;
|
---|
209 | pWalk->GCPhys = (RTGCPHYS)(uPt & UINT64_C(0xfffffffff000)) | (GCPtr & (RTGCPTR)(_4K - 1));
|
---|
210 | return VINF_SUCCESS;
|
---|
211 | }
|
---|
212 |
|
---|