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
|
/* $Id: bs3-memalloc-1.c64 $ */
/** @file
* BS3Kit - bs3-timers-1, 64-bit C code.
*/
/*
* Copyright (C) 2021-2023 Oracle and/or its affiliates.
*
* This file is part of VirtualBox base platform packages, as
* available from https://www.virtualbox.org.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, in version 3 of the
* License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses>.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
* in the VirtualBox distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*
* SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
*/
/*********************************************************************************************************************************
* Header Files *
*********************************************************************************************************************************/
#include <bs3kit.h>
#include <iprt/asm-amd64-x86.h>
#include <VBox/VMMDevTesting.h>
/*********************************************************************************************************************************
* Global Variables *
*********************************************************************************************************************************/
/** Copy of interesting E820 entries. */
static INT15E820ENTRY g_aEntries[16];
/** Number of interesting entires. */
static unsigned g_cEntries = 0;
/** Number of intersting bytes found. */
static uint64_t g_cbInteresting = 0;
/** Lowest interesting address. */
static uint64_t g_uInterestingStart = UINT64_MAX;
/** End of interesting addresses. */
static uint64_t g_uInterestingEnd = 0;
/**
* For subsequence touch iterations that doesn't allocate any RAM.
*
* This may cause page pool activitiy if we've got more memory than we have room
* for in the pool. This depends on amount of guest RAM and how much could be
* backed by large pages.
*/
static uint64_t CheckTouchedMemory(void)
{
unsigned iEntry;
uint64_t iPage = 0;
uint64_t cErrors = 0;
for (iEntry = 0; iEntry < g_cEntries; iEntry++)
{
uint64_t volatile *pu64Cur = (uint64_t *)g_aEntries[iEntry].uBaseAddr;
uint64_t cbLeft = g_aEntries[iEntry].cbRange;
while (cbLeft >= X86_PAGE_SIZE)
{
/* Check first. */
if (RT_LIKELY( pu64Cur[0] == iPage
&& pu64Cur[1] == iPage))
{ /* likely */ }
else
{
Bs3TestFailedF("%p: %#llx + %#llx, expected twice %#llx\n", pu64Cur, pu64Cur[0], pu64Cur[1], iPage);
cErrors++;
}
/* Then write again. */
pu64Cur[0] = iPage;
pu64Cur[1] = iPage;
/* Advance. */
iPage++;
pu64Cur += X86_PAGE_SIZE / sizeof(*pu64Cur);
cbLeft -= X86_PAGE_SIZE;
}
}
return cErrors;
}
/**
* First touching of memory, assuming content is ZERO.
*/
static uint64_t FirstTouchMemory(void)
{
unsigned iEntry;
uint64_t iPage = 0;
for (iEntry = 0; iEntry < g_cEntries; iEntry++)
{
uint64_t volatile *pu64Cur = (uint64_t volatile *)g_aEntries[iEntry].uBaseAddr;
uint64_t cbLeft = g_aEntries[iEntry].cbRange;
while (cbLeft >= X86_PAGE_SIZE)
{
/*
* Write to the page first so we won't waste time mapping the zero
* page and get straight to the actual page allocation.
*/
pu64Cur[0] = iPage;
/* Then check that the 2nd qword is zero before writing it. */
if (RT_LIKELY(pu64Cur[1] == 0))
{ /* likely */ }
else
Bs3TestFailedF("%p: %#llx, expected zero\n", pu64Cur, pu64Cur[1]);
pu64Cur[1] = iPage;
/* Advance. */
iPage++;
pu64Cur += X86_PAGE_SIZE / sizeof(*pu64Cur);
cbLeft -= X86_PAGE_SIZE;
}
}
return iPage;
}
/**
* Translates a E820 entry type to a string.
*/
static const char *getEntryTypeName(uint32_t uType)
{
switch (uType)
{
case INT15E820_TYPE_USABLE: return "USABLE";
case INT15E820_TYPE_RESERVED: return "RESERVED";
case INT15E820_TYPE_ACPI_RECLAIMABLE: return "ACPI_RECLAIMABLE";
case INT15E820_TYPE_ACPI_NVS: return "ACPI_NVS";
case INT15E820_TYPE_BAD: return "BAD";
default: return "unknown";
}
}
BS3_DECL(void) Main_lm64()
{
uint32_t uCont;
unsigned i;
Bs3TestInit("bs3-memalloc-1");
/*
* Get the E820 memory descriptors and pick out those describing memory not
* already used by the Bs3Kit.
*/
Bs3TestSub("INT15h/E820");
for (uCont = i = 0; i < 2048; i++)
{
uint32_t const uEbxCur = uCont;
INT15E820ENTRY Entry = { 0, 0, 0, 0 };
uint32_t cbEntry = sizeof(Entry);
if (!Bs3BiosInt15hE820_lm64(&Entry, &cbEntry, &uCont))
{
Bs3TestFailedF("int15h/E820 failed i=%u", i);
break;
}
Bs3TestPrintf("#%u/%#x: %#018llx LB %#018llx %s (%d)\n",
i, uEbxCur, Entry.uBaseAddr, Entry.cbRange, getEntryTypeName(Entry.uType), Entry.uType);
if (Entry.uType == INT15E820_TYPE_USABLE)
{
if (Entry.uBaseAddr >= _4G)
{
if (g_cEntries < RT_ELEMENTS(g_aEntries))
{
g_cbInteresting += Entry.cbRange;
if (g_uInterestingStart > Entry.uBaseAddr)
g_uInterestingStart = Entry.uBaseAddr;
if (g_uInterestingEnd < Entry.uBaseAddr + Entry.cbRange)
g_uInterestingEnd = Entry.uBaseAddr + Entry.cbRange;
Bs3MemCpy(&g_aEntries[g_cEntries++], &Entry, sizeof(Entry));
}
else
Bs3TestFailedF("Too many interesting E820 entries! Extend g_aEntries!\n");
}
}
/* Done? */
if (uCont == 0)
break;
}
if (g_cEntries == 0)
Bs3TestFailedF("No interesting E820 entries! Make sure you've assigned more than 4GB to the VM!\n");
else
{
uint64_t uFailurePoint = 0;
int rc;
Bs3TestPrintf("Found %u interesting entries covering %#llx bytes (%u GB).\n"
"From %#llx to %#llx\n",
g_cEntries, g_cbInteresting, (unsigned)(g_cbInteresting / _1G), g_uInterestingStart, g_uInterestingEnd);
if (g_uBs3EndOfRamAbove4G < g_uInterestingEnd)
Bs3TestFailedF("g_uBs3EndOfRamAbove4G (%#llx) is lower than g_uInterestingEnd (%#llx)!\n",
g_uBs3EndOfRamAbove4G, g_uInterestingEnd);
/*
* Map all the memory (Bs3Kit only maps memory below 4G).
*/
Bs3TestSub("Mapping memory above 4GB");
if (!(g_uBs3CpuDetected & BS3CPU_F_PSE))
Bs3TestFailedF("PSE was not detected!\n");
else if (!(ASMGetCR4() & X86_CR4_PSE))
Bs3TestFailedF("PSE was not enabled!\n");
else if (RT_SUCCESS(rc = Bs3PagingMapRamAbove4GForLM(&uFailurePoint)))
{
#define PAGES_2_MB(a_cPages) ((a_cPages) / (_1M / X86_PAGE_SIZE))
uint64_t cTotalPages;
unsigned iLoop;
/*
* Time touching all the memory.
*/
Bs3TestSub("Allocation speed");
{
uint64_t const nsStart = Bs3TestNow();
uint64_t const uTscStart = ASMReadTSC();
uint64_t const cPages = FirstTouchMemory();
uint64_t const cTicksElapsed = ASMReadTSC() - uTscStart;
uint64_t const cNsElapsed = Bs3TestNow() - nsStart;
uint64_t uThruput;
Bs3TestValue("Pages", cPages, VMMDEV_TESTING_UNIT_PAGES);
Bs3TestValue("MiBs", PAGES_2_MB(cPages), VMMDEV_TESTING_UNIT_MEGABYTES);
Bs3TestValue("Alloc elapsed", cNsElapsed, VMMDEV_TESTING_UNIT_NS);
Bs3TestValue("Alloc elapsed in ticks", cTicksElapsed, VMMDEV_TESTING_UNIT_TICKS);
Bs3TestValue("Page alloc time", cNsElapsed / cPages, VMMDEV_TESTING_UNIT_NS_PER_PAGE);
Bs3TestValue("Page alloc time in ticks", cTicksElapsed / cPages, VMMDEV_TESTING_UNIT_TICKS_PER_PAGE);
uThruput = cPages * RT_NS_1SEC / cNsElapsed;
Bs3TestValue("Alloc thruput", uThruput, VMMDEV_TESTING_UNIT_PAGES_PER_SEC);
Bs3TestValue("Alloc thruput in MiBs", PAGES_2_MB(uThruput), VMMDEV_TESTING_UNIT_MEGABYTES_PER_SEC);
cTotalPages = cPages;
}
/*
* Time accessing all the memory again. This might give a clue as to page pool performance.
*/
for (iLoop = 0; iLoop < 2; iLoop++)
{
Bs3TestSub(iLoop == 0 ? "2nd access" : "3rd access");
{
uint64_t const nsStart = Bs3TestNow();
uint64_t const uTscStart = ASMReadTSC();
uint64_t const cErrors = CheckTouchedMemory();
uint64_t const cTicksElapsed = ASMReadTSC() - uTscStart;
uint64_t const cNsElapsed = Bs3TestNow() - nsStart;
uint64_t uThruput;
Bs3TestValue("Access elapsed", cNsElapsed, VMMDEV_TESTING_UNIT_NS);
Bs3TestValue("Access elapsed in ticks", cTicksElapsed, VMMDEV_TESTING_UNIT_TICKS);
Bs3TestValue("Page access time", cNsElapsed / cTotalPages, VMMDEV_TESTING_UNIT_NS_PER_PAGE);
Bs3TestValue("Page access time in ticks", cTicksElapsed / cTotalPages, VMMDEV_TESTING_UNIT_TICKS_PER_PAGE);
uThruput = cTotalPages * RT_NS_1SEC / cNsElapsed;
Bs3TestValue("Access thruput", uThruput, VMMDEV_TESTING_UNIT_PAGES_PER_SEC);
Bs3TestValue("Access thruput in MiBs", PAGES_2_MB(uThruput), VMMDEV_TESTING_UNIT_MEGABYTES_PER_SEC);
}
}
}
else
Bs3TestFailedF("Bs3PagingMapRamAbove4GForLM failed at %#llx: %d", uFailurePoint, rc);
}
Bs3TestTerm();
}
|