VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxBFE/VBoxBFE.cpp@ 99196

Last change on this file since 99196 was 99080, checked in by vboxsync, 2 years ago

FE/VBoxBFE: Configure RAM the ARMv8 way and add a hack to load the device tree from a file until we have our own FDT generator, bugref:10397

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.9 KB
Line 
1/* $Id: VBoxBFE.cpp 99080 2023-03-21 11:06:54Z vboxsync $ */
2/** @file
3 * VBoxBFE - The basic VirtualBox frontend for running VMs without using Main/COM/XPCOM.
4 * Mainly serves as a playground for the ARMv8 VMM bringup for now.
5 */
6
7/*
8 * Copyright (C) 2023 Oracle and/or its affiliates.
9 *
10 * This file is part of VirtualBox base platform packages, as
11 * available from https://www.virtualbox.org.
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation, in version 3 of the
16 * License.
17 *
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, see <https://www.gnu.org/licenses>.
25 *
26 * SPDX-License-Identifier: GPL-3.0-only
27 */
28
29
30/*********************************************************************************************************************************
31* Header Files *
32*********************************************************************************************************************************/
33#define LOG_GROUP LOG_GROUP_GUI
34
35#include <VBox/log.h>
36#include <VBox/version.h>
37#include <VBox/vmm/vmmr3vtable.h>
38#include <VBox/vmm/vmapi.h>
39#include <VBox/vmm/pdm.h>
40#include <iprt/buildconfig.h>
41#include <iprt/ctype.h>
42#include <iprt/initterm.h>
43#include <iprt/message.h>
44#include <iprt/semaphore.h>
45#include <iprt/file.h>
46#include <iprt/path.h>
47#include <iprt/stream.h>
48#include <iprt/ldr.h>
49#include <iprt/getopt.h>
50#include <iprt/env.h>
51#include <iprt/errcore.h>
52#include <iprt/thread.h>
53
54
55/*********************************************************************************************************************************
56* Defined Constants And Macros *
57*********************************************************************************************************************************/
58
59#define LogError(m,rc) \
60 do { \
61 Log(("VBoxBFE: ERROR: " m " [rc=0x%08X]\n", rc)); \
62 RTPrintf("%s\n", m); \
63 } while (0)
64
65#define DTB_ADDR 0x40000000
66
67
68/*********************************************************************************************************************************
69* Structures and Typedefs *
70*********************************************************************************************************************************/
71
72
73/*********************************************************************************************************************************
74* Global Variables *
75*********************************************************************************************************************************/
76/** flag whether frontend should terminate */
77static volatile bool g_fTerminateFE = false;
78static RTLDRMOD g_hModVMM = NIL_RTLDRMOD;
79static PCVMMR3VTABLE g_pVMM = NULL;
80static PVM g_pVM = NULL;
81static PUVM g_pUVM = NULL;
82static uint32_t g_u32MemorySizeMB = 512;
83static VMSTATE g_enmVmState = VMSTATE_CREATING;
84static const char *g_pszLoadMem = NULL;
85static const char *g_pszLoadDtb = NULL;
86
87/** @todo currently this is only set but never read. */
88static char szError[512];
89
90
91/*********************************************************************************************************************************
92* Internal Functions *
93*********************************************************************************************************************************/
94
95/**
96 * VM state callback function. Called by the VMM
97 * using its state machine states.
98 *
99 * Primarily used to handle VM initiated power off, suspend and state saving,
100 * but also for doing termination completed work (VMSTATE_TERMINATE).
101 *
102 * In general this function is called in the context of the EMT.
103 *
104 * @todo g_enmVmState is set to VMSTATE_RUNNING before all devices have received power on events
105 * this can prematurely allow the main thread to enter the event loop
106 *
107 * @param pVM The VM handle.
108 * @param enmState The new state.
109 * @param enmOldState The old state.
110 * @param pvUser The user argument.
111 */
112static DECLCALLBACK(void) vboxbfeVmStateChangeCallback(PUVM pUVM, PCVMMR3VTABLE pVMM, VMSTATE enmState, VMSTATE enmOldState, void *pvUser)
113{
114 RT_NOREF(pUVM, pVMM, enmOldState, pvUser);
115 LogFlow(("vmstateChangeCallback: changing state from %d to %d\n", enmOldState, enmState));
116 g_enmVmState = enmState;
117
118 switch (enmState)
119 {
120 /*
121 * The VM has terminated
122 */
123 case VMSTATE_OFF:
124 {
125 break;
126 }
127
128 /*
129 * The VM has been completely destroyed.
130 *
131 * Note: This state change can happen at two points:
132 * 1) At the end of VMR3Destroy() if it was not called from EMT.
133 * 2) At the end of vmR3EmulationThread if VMR3Destroy() was called by EMT.
134 */
135 case VMSTATE_TERMINATED:
136 {
137 break;
138 }
139
140 default: /* shut up gcc */
141 break;
142 }
143}
144
145
146/**
147 * VM error callback function. Called by the various VM components.
148 *
149 * @param pVM The VM handle.
150 * @param pvUser The user argument.
151 * @param vrc VBox status code.
152 * @param pszFormat Error message format string.
153 * @param args Error message arguments.
154 * @thread EMT.
155 */
156DECLCALLBACK(void) vboxbfeSetVMErrorCallback(PUVM pUVM, void *pvUser, int vrc, RT_SRC_POS_DECL, const char *pszFormat, va_list args)
157{
158 RT_NOREF(pUVM, pvUser, pszFile, iLine, pszFunction);
159
160 /** @todo accessing shared resource without any kind of synchronization */
161 if (RT_SUCCESS(vrc))
162 szError[0] = '\0';
163 else
164 {
165 va_list va2;
166 va_copy(va2, args); /* Have to make a copy here or GCC will break. */
167 RTStrPrintf(szError, sizeof(szError),
168 "%N!\nVBox status code: %d (%Rrc)", pszFormat, &va2, vrc, vrc);
169 RTPrintf("%s\n", szError);
170 va_end(va2);
171 }
172}
173
174
175/**
176 * VM Runtime error callback function. Called by the various VM components.
177 *
178 * @param pVM The VM handle.
179 * @param pvUser The user argument.
180 * @param fFlags The action flags. See VMSETRTERR_FLAGS_*.
181 * @param pszErrorId Error ID string.
182 * @param pszFormat Error message format string.
183 * @param va Error message arguments.
184 * @thread EMT.
185 */
186DECLCALLBACK(void) vboxbfeSetVMRuntimeErrorCallback(PUVM pUVM, void *pvUser, uint32_t fFlags,
187 const char *pszErrorId, const char *pszFormat, va_list va)
188{
189 RT_NOREF(pUVM, pvUser);
190
191 va_list va2;
192 va_copy(va2, va); /* Have to make a copy here or GCC/AMD64 will break. */
193 RTPrintf("%s: %s!\n%N!\n",
194 fFlags & VMSETRTERR_FLAGS_FATAL ? "Error" : "Warning",
195 pszErrorId, pszFormat, &va2);
196 RTStrmFlush(g_pStdErr);
197 va_end(va2);
198}
199
200
201#if 0
202/**
203 * Register the main drivers.
204 *
205 * @returns VBox status code.
206 * @param pCallbacks Pointer to the callback table.
207 * @param u32Version VBox version number.
208 */
209DECLCALLBACK(int) VBoxDriversRegister(PCPDMDRVREGCB pCallbacks, uint32_t u32Version)
210{
211 LogFlow(("VBoxDriversRegister: u32Version=%#x\n", u32Version));
212 AssertReleaseMsg(u32Version == VBOX_VERSION, ("u32Version=%#x VBOX_VERSION=%#x\n", u32Version, VBOX_VERSION));
213
214 /** @todo */
215 RT_NOREF(pCallbacks);
216
217 return VINF_SUCCESS;
218}
219#endif
220
221
222/**
223 * Constructs the VMM configuration tree.
224 *
225 * @returns VBox status code.
226 * @param pVM VM handle.
227 */
228static DECLCALLBACK(int) vboxbfeConfigConstructor(PUVM pUVM, PVM pVM, PCVMMR3VTABLE pVMM, void *pvConsole)
229{
230 int rcAll = VINF_SUCCESS;
231 int rc;
232
233 RT_NOREF(pvConsole);
234 g_pUVM = pUVM;
235
236#define UPDATE_RC() do { if (RT_FAILURE(rc) && RT_SUCCESS(rcAll)) rcAll = rc; } while (0)
237
238 /*
239 * Root values.
240 */
241 PCFGMNODE pRoot = pVMM->pfnCFGMR3GetRoot(pVM);
242 rc = pVMM->pfnCFGMR3InsertString(pRoot, "Name", "Default VM"); UPDATE_RC();
243 rc = pVMM->pfnCFGMR3InsertInteger(pRoot, "TimerMillies", 1000); UPDATE_RC();
244
245 /*
246 * Memory setup.
247 */
248 PCFGMNODE pMem = NULL;
249 rc = pVMM->pfnCFGMR3InsertNode(pRoot, "MM", &pMem); UPDATE_RC();
250 rc = pVMM->pfnCFGMR3InsertNode(pMem, "MemRegions", &pMem); UPDATE_RC();
251
252 PCFGMNODE pMemRegion = NULL;
253 rc = pVMM->pfnCFGMR3InsertNode(pMem, "Flash", &pMemRegion); UPDATE_RC();
254 rc = pVMM->pfnCFGMR3InsertInteger(pMemRegion, "GCPhysStart", 0); UPDATE_RC();
255 rc = pVMM->pfnCFGMR3InsertInteger(pMemRegion, "Size", 128 * _1M); UPDATE_RC();
256
257 rc = pVMM->pfnCFGMR3InsertNode(pMem, "Conventional", &pMemRegion); UPDATE_RC();
258 rc = pVMM->pfnCFGMR3InsertInteger(pMemRegion, "GCPhysStart", DTB_ADDR); UPDATE_RC();
259 rc = pVMM->pfnCFGMR3InsertInteger(pMemRegion, "Size", (uint64_t)g_u32MemorySizeMB * _1M); UPDATE_RC();
260
261
262 /*
263 * PDM.
264 */
265 //rc = pVMM->pfnPDMR3DrvStaticRegistration(pVM, VBoxDriversRegister); UPDATE_RC();
266
267 /*
268 * Devices
269 */
270 PCFGMNODE pDevices = NULL;
271 rc = pVMM->pfnCFGMR3InsertNode(pRoot, "Devices", &pDevices); UPDATE_RC();
272
273#undef UPDATE_RC
274#undef UPDATE_RC
275
276 pVMM->pfnVMR3AtRuntimeErrorRegister (pUVM, vboxbfeSetVMRuntimeErrorCallback, NULL);
277
278 return rc;
279}
280
281
282/**
283 * Loads the VMM if needed.
284 *
285 * @returns VBox status code.
286 * @param pszVmmMod The VMM module to load.
287 */
288int vboxbfeLoadVMM(const char *pszVmmMod)
289{
290 Assert(!g_pVMM);
291
292 RTERRINFOSTATIC ErrInfo;
293 RTLDRMOD hModVMM = NIL_RTLDRMOD;
294 int vrc = SUPR3HardenedLdrLoadAppPriv(pszVmmMod, &hModVMM, RTLDRLOAD_FLAGS_LOCAL, RTErrInfoInitStatic(&ErrInfo));
295 if (RT_SUCCESS(vrc))
296 {
297 PFNVMMGETVTABLE pfnGetVTable = NULL;
298 vrc = RTLdrGetSymbol(hModVMM, VMMR3VTABLE_GETTER_NAME, (void **)&pfnGetVTable);
299 if (pfnGetVTable)
300 {
301 PCVMMR3VTABLE pVMM = pfnGetVTable();
302 if (pVMM)
303 {
304 if (VMMR3VTABLE_IS_COMPATIBLE(pVMM->uMagicVersion))
305 {
306 if (pVMM->uMagicVersion == pVMM->uMagicVersionEnd)
307 {
308 g_hModVMM = hModVMM;
309 g_pVMM = pVMM;
310 LogFunc(("mhLdrVMM=%p phVMM=%p uMagicVersion=%#RX64\n", hModVMM, pVMM, pVMM->uMagicVersion));
311 return VINF_SUCCESS;
312 }
313
314 LogRel(("Bogus VMM vtable: uMagicVersion=%#RX64 uMagicVersionEnd=%#RX64",
315 pVMM->uMagicVersion, pVMM->uMagicVersionEnd));
316 }
317 else
318 LogRel(("Incompatible of bogus VMM version magic: %#RX64", pVMM->uMagicVersion));
319 }
320 else
321 LogRel(("pfnGetVTable return NULL!"));
322 }
323 else
324 LogRel(("Failed to locate symbol '%s' in VBoxVMM: %Rrc", VMMR3VTABLE_GETTER_NAME, vrc));
325 RTLdrClose(hModVMM);
326 }
327 else
328 LogRel(("Failed to load VBoxVMM: %#RTeic", &ErrInfo.Core));
329
330 return vrc;
331}
332
333
334/**
335 * Loads the content of a given file into guest RAM starting at the given guest physical address.
336 *
337 * @returns VBox status code.
338 * @param pszFile The file to load.
339 * @param GCPhysStart The physical start address to load the file at.
340 */
341static int vboxbfeLoadFileAtGCPhys(const char *pszFile, RTGCPHYS GCPhysStart)
342{
343 RTFILE hFile = NIL_RTFILE;
344 int rc = RTFileOpen(&hFile, pszFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
345 if (RT_SUCCESS(rc))
346 {
347 uint8_t abRead[GUEST_PAGE_SIZE];
348 RTGCPHYS GCPhys = GCPhysStart;
349
350 for (;;)
351 {
352 size_t cbThisRead = 0;
353 rc = RTFileRead(hFile, &abRead[0], sizeof(abRead), &cbThisRead);
354 if (RT_FAILURE(rc))
355 break;
356
357 rc = g_pVMM->pfnPGMPhysSimpleWriteGCPhys(g_pVM, GCPhys, &abRead[0], cbThisRead);
358 if (RT_FAILURE(rc))
359 break;
360
361 GCPhys += cbThisRead;
362 if (cbThisRead < sizeof(abRead))
363 break;
364 }
365
366 RTFileClose(hFile);
367 }
368 else
369 RTPrintf("Loading file %s failed -> %Rrc\n", pszFile, rc);
370
371 return rc;
372}
373
374
375/** VM asynchronous operations thread */
376DECLCALLBACK(int) vboxbfeVMPowerUpThread(RTTHREAD hThread, void *pvUser)
377{
378 RT_NOREF(hThread, pvUser);
379
380 int rc = VINF_SUCCESS;
381 int rc2;
382
383#if 0
384 /*
385 * Setup the release log instance in current directory.
386 */
387 if (g_fReleaseLog)
388 {
389 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
390 static char s_szError[RTPATH_MAX + 128] = "";
391 PRTLOGGER pLogger;
392 rc2 = RTLogCreateEx(&pLogger, RTLOGFLAGS_PREFIX_TIME_PROG, "all",
393 "VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups, RTLOGDEST_FILE,
394 NULL /* pfnBeginEnd */, 0 /* cHistory */, 0 /* cbHistoryFileMax */, 0 /* uHistoryTimeMax */,
395 s_szError, sizeof(s_szError), "./VBoxBFE.log");
396 if (RT_SUCCESS(rc2))
397 {
398 /* some introductory information */
399 RTTIMESPEC TimeSpec;
400 char szNowUct[64];
401 RTTimeSpecToString(RTTimeNow(&TimeSpec), szNowUct, sizeof(szNowUct));
402 RTLogRelLogger(pLogger, 0, ~0U,
403 "VBoxBFE %s (%s %s) release log\n"
404 "Log opened %s\n",
405 VBOX_VERSION_STRING, __DATE__, __TIME__,
406 szNowUct);
407
408 /* register this logger as the release logger */
409 RTLogRelSetDefaultInstance(pLogger);
410 }
411 else
412 RTPrintf("Could not open release log (%s)\n", s_szError);
413 }
414#endif
415
416 /*
417 * Start VM (also from saved state) and track progress
418 */
419 LogFlow(("VMPowerUp\n"));
420
421 /*
422 * Create empty VM.
423 */
424 rc = g_pVMM->pfnVMR3Create(1, NULL, 0 /*fFlags*/, vboxbfeSetVMErrorCallback, NULL, vboxbfeConfigConstructor, NULL, &g_pVM, NULL);
425 if (RT_FAILURE(rc))
426 {
427 RTPrintf("Error: VM creation failed with %Rrc.\n", rc);
428 goto failure;
429 }
430
431
432 /*
433 * Register VM state change handler
434 */
435 rc = g_pVMM->pfnVMR3AtStateRegister(g_pUVM, vboxbfeVmStateChangeCallback, NULL);
436 if (RT_FAILURE(rc))
437 {
438 RTPrintf("Error: VMR3AtStateRegister failed with %Rrc.\n", rc);
439 goto failure;
440 }
441
442 /*
443 * Prepopulate the memory?
444 */
445 if (g_pszLoadMem)
446 {
447 rc = vboxbfeLoadFileAtGCPhys(g_pszLoadMem, 0 /*GCPhysStart*/);
448 if (RT_FAILURE(rc))
449 goto failure;
450 }
451
452 if (g_pszLoadDtb)
453 {
454 rc = vboxbfeLoadFileAtGCPhys(g_pszLoadDtb, DTB_ADDR);
455 if (RT_FAILURE(rc))
456 goto failure;
457 }
458
459 /*
460 * Power on the VM (i.e. start executing).
461 */
462 if (RT_SUCCESS(rc))
463 {
464#if 0
465 if ( g_fRestoreState
466 && g_pszStateFile
467 && *g_pszStateFile
468 && RTPathExists(g_pszStateFile))
469 {
470 startProgressInfo("Restoring");
471 rc = VMR3LoadFromFile(gpVM, g_pszStateFile, callProgressInfo, (uintptr_t)NULL);
472 endProgressInfo();
473 if (RT_SUCCESS(rc))
474 {
475 rc = VMR3Resume(gpVM);
476 AssertRC(rc);
477 }
478 else
479 AssertMsgFailed(("VMR3LoadFromFile failed, rc=%Rrc\n", rc));
480 }
481 else
482#endif
483 {
484 rc = g_pVMM->pfnVMR3PowerOn(g_pUVM);
485 if (RT_FAILURE(rc))
486 AssertMsgFailed(("VMR3PowerOn failed, rc=%Rrc\n", rc));
487 }
488 }
489
490 /*
491 * On failure destroy the VM.
492 */
493 if (RT_FAILURE(rc))
494 goto failure;
495
496 return VINF_SUCCESS;
497
498failure:
499 if (g_pVM)
500 {
501 rc2 = g_pVMM->pfnVMR3Destroy(g_pUVM);
502 AssertRC(rc2);
503 g_pVM = NULL;
504 }
505 g_enmVmState = VMSTATE_TERMINATED;
506
507 return VINF_SUCCESS;
508}
509
510
511static void show_usage()
512{
513 RTPrintf("Usage:\n"
514 " --start-paused Start the VM in paused state\n"
515 "\n");
516}
517
518/**
519 * Entry point.
520 */
521extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp)
522{
523 RT_NOREF(envp);
524 unsigned fPaused = 0;
525
526 LogFlow(("VBoxBFE STARTED.\n"));
527 RTPrintf(VBOX_PRODUCT " Basic Interface " VBOX_VERSION_STRING "\n"
528 "Copyright (C) 2023-" VBOX_C_YEAR " " VBOX_VENDOR "\n\n");
529
530 static const RTGETOPTDEF s_aOptions[] =
531 {
532 { "--start-paused", 'p', 0 },
533 { "--memory-size-mib", 'm', RTGETOPT_REQ_UINT32 },
534 { "--load-file-into-ram", 'l', RTGETOPT_REQ_STRING },
535 { "--load-dtb", 'd', RTGETOPT_REQ_STRING },
536 { "--load-vmm", 'v', RTGETOPT_REQ_STRING },
537 };
538
539 const char *pszVmmMod = "VBoxVMM";
540
541 /* Parse the config. */
542 int ch;
543 RTGETOPTUNION ValueUnion;
544 RTGETOPTSTATE GetState;
545 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0 /* fFlags */);
546 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
547 {
548 switch(ch)
549 {
550 case 'p':
551 fPaused = true;
552 break;
553 case 'm':
554 g_u32MemorySizeMB = ValueUnion.u32;
555 break;
556 case 'l':
557 g_pszLoadMem = ValueUnion.psz;
558 break;
559 case 'd':
560 g_pszLoadDtb = ValueUnion.psz;
561 break;
562 case 'v':
563 pszVmmMod = ValueUnion.psz;
564 break;
565 case 'h':
566 show_usage();
567 return 0;
568 case 'V':
569 RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
570 return 0;
571 default:
572 ch = RTGetOptPrintError(ch, &ValueUnion);
573 show_usage();
574 return ch;
575 }
576 }
577
578 int vrc = vboxbfeLoadVMM(pszVmmMod);
579 if (RT_FAILURE(vrc))
580 return RTEXITCODE_FAILURE;
581
582 /*
583 * Start the VM execution thread. This has to be done
584 * asynchronously as powering up can take some time
585 * (accessing devices such as the host DVD drive). In
586 * the meantime, we have to service the SDL event loop.
587 */
588
589 RTTHREAD thread;
590 vrc = RTThreadCreate(&thread, vboxbfeVMPowerUpThread, 0, 0, RTTHREADTYPE_MAIN_WORKER, 0, "PowerUp");
591 if (RT_FAILURE(vrc))
592 {
593 RTPrintf("Error: Thread creation failed with %d\n", vrc);
594 return RTEXITCODE_FAILURE;
595 }
596
597
598 /* loop until the powerup processing is done */
599 do
600 {
601 RTThreadSleep(1000);
602 }
603 while ( g_enmVmState == VMSTATE_CREATING
604 || g_enmVmState == VMSTATE_LOADING);
605
606 /** @todo Mainloop. */
607 for (;;)
608 RTThreadSleep(1000);
609
610 LogRel(("VBoxBFE: exiting\n"));
611 return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
612}
613
614
615#ifndef VBOX_WITH_HARDENING
616/**
617 * Main entry point.
618 */
619int main(int argc, char **argv, char **envp)
620{
621 int rc = RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_TRY_SUPLIB);
622 if (RT_SUCCESS(rc))
623 return TrustedMain(argc, argv, envp);
624 RTPrintf("VBoxBFE: Runtime initialization failed: %Rrc - %Rrf\n", rc, rc);
625 return RTEXITCODE_FAILURE;
626}
627#endif /* !VBOX_WITH_HARDENING */
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette