VirtualBox

source: vbox/trunk/src/VBox/Devices/Serial/DevSerial.cpp@ 80862

Last change on this file since 80862 was 80704, checked in by vboxsync, 5 years ago

PDM,Devices: Changed PDM_DEVREG_FLAGS_MSI_X into a registration field giving the max MSI-X vector count config for the device (typically VBOX_MSIX_MAX_ENTRIES). bugref:9218

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.7 KB
Line 
1/* $Id: DevSerial.cpp 80704 2019-09-10 15:19:39Z vboxsync $ */
2/** @file
3 * DevSerial - 16550A UART emulation.
4 *
5 * The documentation for this device was taken from the PC16550D spec from TI.
6 */
7
8/*
9 * Copyright (C) 2018-2019 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 */
19
20
21/*********************************************************************************************************************************
22* Header Files *
23*********************************************************************************************************************************/
24#define LOG_GROUP LOG_GROUP_DEV_SERIAL
25#include <VBox/vmm/pdmdev.h>
26#include <VBox/vmm/pdmserialifs.h>
27#include <iprt/assert.h>
28#include <iprt/uuid.h>
29#include <iprt/string.h>
30#include <iprt/semaphore.h>
31#include <iprt/critsect.h>
32
33#include "VBoxDD.h"
34#include "UartCore.h"
35
36
37/*********************************************************************************************************************************
38* Structures and Typedefs *
39*********************************************************************************************************************************/
40
41/**
42 * Serial device.
43 */
44typedef struct DEVSERIAL
45{
46 /** Pointer to the device instance - R3 Ptr. */
47 PPDMDEVINSR3 pDevInsR3;
48 /** Pointer to the device instance - R0 Ptr. */
49 PPDMDEVINSR0 pDevInsR0;
50 /** Pointer to the device instance - RC Ptr. */
51 PPDMDEVINSRC pDevInsRC;
52 /** Alignment. */
53 RTRCPTR Alignment0;
54 /** Flag whether the R0 portion of this device is enabled. */
55 bool fR0Enabled;
56 /** Flag whether the RC portion of this device is enabled. */
57 bool fRCEnabled;
58 /** Alignment. */
59 bool afAlignment1[2];
60 /** The IRQ value. */
61 uint8_t uIrq;
62 /** The base I/O port the device is registered at. */
63 RTIOPORT PortBase;
64
65 /** The UART core. */
66 UARTCORE UartCore;
67} DEVSERIAL;
68/** Pointer to the serial device state. */
69typedef DEVSERIAL *PDEVSERIAL;
70
71#ifndef VBOX_DEVICE_STRUCT_TESTCASE
72
73
74
75PDMBOTHCBDECL(void) serialIrqReq(PPDMDEVINS pDevIns, PUARTCORE pUart, unsigned iLUN, int iLvl)
76{
77 RT_NOREF(pUart, iLUN);
78 PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
79 PDMDevHlpISASetIrqNoWait(pDevIns, pThis->uIrq, iLvl);
80}
81
82
83/* -=-=-=-=-=-=-=-=- I/O Port Access Handlers -=-=-=-=-=-=-=-=- */
84
85/**
86 * @callback_method_impl{FNIOMIOPORTOUT}
87 */
88PDMBOTHCBDECL(int) serialIoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint32_t u32, unsigned cb)
89{
90 PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
91 RT_NOREF_PV(pvUser);
92
93 return uartRegWrite(&pThis->UartCore, uPort - pThis->PortBase, u32, cb);
94}
95
96
97/**
98 * @callback_method_impl{FNIOMIOPORTIN}
99 */
100PDMBOTHCBDECL(int) serialIoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint32_t *pu32, unsigned cb)
101{
102 PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
103 RT_NOREF_PV(pvUser);
104
105 return uartRegRead(&pThis->UartCore, uPort - pThis->PortBase, pu32, cb);
106}
107
108
109#ifdef IN_RING3
110
111
112/**
113 * Returns the matching UART type from the given string.
114 *
115 * @returns UART type based on the given string or UARTTYPE_INVALID if an invalid type was passed.
116 * @param pszUartType The UART type.
117 */
118static UARTTYPE serialR3GetUartTypeFromString(const char *pszUartType)
119{
120 if (!RTStrCmp(pszUartType, "16450"))
121 return UARTTYPE_16450;
122 else if (!RTStrCmp(pszUartType, "16550A"))
123 return UARTTYPE_16550A;
124 else if (!RTStrCmp(pszUartType, "16750"))
125 return UARTTYPE_16750;
126
127 AssertLogRelMsgFailedReturn(("Unknown UART type \"%s\" specified", pszUartType),
128 UARTTYPE_INVALID);
129}
130
131
132/* -=-=-=-=-=-=-=-=- Saved State -=-=-=-=-=-=-=-=- */
133
134/**
135 * @callback_method_impl{FNSSMDEVLIVEEXEC}
136 */
137static DECLCALLBACK(int) serialR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
138{
139 RT_NOREF(uPass);
140 PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
141 SSMR3PutU8(pSSM, pThis->uIrq);
142 SSMR3PutIOPort(pSSM, pThis->PortBase);
143 SSMR3PutU32(pSSM, pThis->UartCore.enmType);
144
145 return VINF_SSM_DONT_CALL_AGAIN;
146}
147
148
149/**
150 * @callback_method_impl{FNSSMDEVSAVEEXEC}
151 */
152static DECLCALLBACK(int) serialR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
153{
154 PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
155
156 SSMR3PutU8( pSSM, pThis->uIrq);
157 SSMR3PutIOPort(pSSM, pThis->PortBase);
158 SSMR3PutU32( pSSM, pThis->UartCore.enmType);
159
160 uartR3SaveExec(&pThis->UartCore, pSSM);
161 return SSMR3PutU32(pSSM, UINT32_MAX); /* sanity/terminator */
162}
163
164
165/**
166 * @callback_method_impl{FNSSMDEVLOADEXEC}
167 */
168static DECLCALLBACK(int) serialR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
169{
170 PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
171 uint8_t bIrq;
172 RTIOPORT PortBase;
173 UARTTYPE enmType;
174 int rc;
175
176 AssertMsgReturn(uVersion >= UART_SAVED_STATE_VERSION_16450, ("%d\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
177 if (uVersion > UART_SAVED_STATE_VERSION_LEGACY_CODE)
178 {
179 SSMR3GetU8( pSSM, &bIrq);
180 SSMR3GetIOPort(pSSM, &PortBase);
181 rc = SSMR3GetU32( pSSM, (uint32_t *)&enmType);
182 AssertRCReturn(rc, rc);
183 if (uPass == SSM_PASS_FINAL)
184 {
185 rc = uartR3LoadExec(&pThis->UartCore, pSSM, uVersion, uPass, NULL, NULL);
186 AssertRCReturn(rc, rc);
187 }
188 }
189 else
190 {
191 enmType = uVersion > UART_SAVED_STATE_VERSION_16450 ? UARTTYPE_16550A : UARTTYPE_16450;
192 if (uPass != SSM_PASS_FINAL)
193 {
194 int32_t iIrqTmp;
195 SSMR3GetS32(pSSM, &iIrqTmp);
196 uint32_t uPortBaseTmp;
197 rc = SSMR3GetU32(pSSM, &uPortBaseTmp);
198 AssertRCReturn(rc, rc);
199
200 bIrq = (uint8_t)iIrqTmp;
201 PortBase = (uint32_t)uPortBaseTmp;
202 }
203 else
204 {
205 rc = uartR3LoadExec(&pThis->UartCore, pSSM, uVersion, uPass, &bIrq, &PortBase);
206 AssertRCReturn(rc, rc);
207 }
208 }
209
210 if (uPass == SSM_PASS_FINAL)
211 {
212 /* The marker. */
213 uint32_t u32;
214 rc = SSMR3GetU32(pSSM, &u32);
215 AssertRCReturn(rc, rc);
216 AssertMsgReturn(u32 == UINT32_MAX, ("%#x\n", u32), VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
217 }
218
219 /*
220 * Check the config.
221 */
222 if ( pThis->uIrq != bIrq
223 || pThis->PortBase != PortBase
224 || pThis->UartCore.enmType != enmType)
225 return SSMR3SetCfgError(pSSM, RT_SRC_POS,
226 N_("Config mismatch - saved IRQ=%#x PortBase=%#x Type=%d; configured IRQ=%#x PortBase=%#x Type=%d"),
227 bIrq, PortBase, enmType, pThis->uIrq, pThis->PortBase, pThis->UartCore.enmType);
228
229 return VINF_SUCCESS;
230}
231
232
233/**
234 * @callback_method_impl{FNSSMDEVLOADDONE}
235 */
236static DECLCALLBACK(int) serialR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
237{
238 PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
239 return uartR3LoadDone(&pThis->UartCore, pSSM);
240}
241
242
243/* -=-=-=-=-=-=-=-=- PDMDEVREG -=-=-=-=-=-=-=-=- */
244
245/**
246 * @interface_method_impl{PDMDEVREG,pfnRelocate}
247 */
248static DECLCALLBACK(void) serialR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
249{
250 PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
251 uartR3Relocate(&pThis->UartCore, offDelta);
252}
253
254
255/**
256 * @interface_method_impl{PDMDEVREG,pfnReset}
257 */
258static DECLCALLBACK(void) serialR3Reset(PPDMDEVINS pDevIns)
259{
260 PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
261 uartR3Reset(&pThis->UartCore);
262}
263
264
265/**
266 * @interface_method_impl{PDMDEVREG,pfnAttach}
267 */
268static DECLCALLBACK(int) serialR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
269{
270 RT_NOREF(fFlags);
271 PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
272 return uartR3Attach(&pThis->UartCore, iLUN);
273}
274
275
276/**
277 * @interface_method_impl{PDMDEVREG,pfnDetach}
278 */
279static DECLCALLBACK(void) serialR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
280{
281 RT_NOREF(iLUN, fFlags);
282 PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
283 uartR3Detach(&pThis->UartCore);
284}
285
286
287/**
288 * @interface_method_impl{PDMDEVREG,pfnDestruct}
289 */
290static DECLCALLBACK(int) serialR3Destruct(PPDMDEVINS pDevIns)
291{
292 PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
293 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
294
295 uartR3Destruct(&pThis->UartCore);
296 return VINF_SUCCESS;
297}
298
299
300/**
301 * @interface_method_impl{PDMDEVREG,pfnConstruct}
302 */
303static DECLCALLBACK(int) serialR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
304{
305 PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL);
306 int rc = VINF_SUCCESS;
307
308 Assert(iInstance < 4);
309 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
310
311 /*
312 * Initialize the instance data.
313 * (Do this early or the destructor might choke on something!)
314 */
315 pThis->pDevInsR3 = pDevIns;
316 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
317 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
318
319 /*
320 * Validate and read the configuration.
321 */
322 if (!CFGMR3AreValuesValid(pCfg, "IRQ\0"
323 "IOBase\0"
324 "GCEnabled\0"
325 "R0Enabled\0"
326 "YieldOnLSRRead\0"
327 "UartType\0"
328 ))
329 {
330 AssertMsgFailed(("serialConstruct Invalid configuration values\n"));
331 return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
332 }
333
334 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &pThis->fRCEnabled, true);
335 if (RT_FAILURE(rc))
336 return PDMDEV_SET_ERROR(pDevIns, rc,
337 N_("Configuration error: Failed to get the \"GCEnabled\" value"));
338
339 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &pThis->fR0Enabled, true);
340 if (RT_FAILURE(rc))
341 return PDMDEV_SET_ERROR(pDevIns, rc,
342 N_("Configuration error: Failed to get the \"R0Enabled\" value"));
343
344 bool fYieldOnLSRRead = false;
345 rc = CFGMR3QueryBoolDef(pCfg, "YieldOnLSRRead", &fYieldOnLSRRead, false);
346 if (RT_FAILURE(rc))
347 return PDMDEV_SET_ERROR(pDevIns, rc,
348 N_("Configuration error: Failed to get the \"YieldOnLSRRead\" value"));
349
350 uint8_t uIrq = 0;
351 rc = CFGMR3QueryU8(pCfg, "IRQ", &uIrq);
352 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
353 {
354 /* Provide sensible defaults. */
355 if (iInstance == 0)
356 uIrq = 4;
357 else if (iInstance == 1)
358 uIrq = 3;
359 else
360 AssertReleaseFailed(); /* irq_lvl is undefined. */
361 }
362 else if (RT_FAILURE(rc))
363 return PDMDEV_SET_ERROR(pDevIns, rc,
364 N_("Configuration error: Failed to get the \"IRQ\" value"));
365
366 uint16_t uIoBase = 0;
367 rc = CFGMR3QueryU16(pCfg, "IOBase", &uIoBase);
368 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
369 {
370 if (iInstance == 0)
371 uIoBase = 0x3f8;
372 else if (iInstance == 1)
373 uIoBase = 0x2f8;
374 else
375 AssertReleaseFailed(); /* uIoBase is undefined */
376 }
377 else if (RT_FAILURE(rc))
378 return PDMDEV_SET_ERROR(pDevIns, rc,
379 N_("Configuration error: Failed to get the \"IOBase\" value"));
380
381 char *pszUartType;
382 rc = CFGMR3QueryStringAllocDef(pCfg, "UartType", &pszUartType, "16550A");
383 if (RT_FAILURE(rc))
384 return PDMDEV_SET_ERROR(pDevIns, rc,
385 N_("Configuration error: failed to read \"UartType\" as string"));
386
387 UARTTYPE enmUartType = serialR3GetUartTypeFromString(pszUartType);
388
389 if (enmUartType != UARTTYPE_INVALID)
390 LogRel(("Serial#%d: emulating %s (IOBase: %04x IRQ: %u)\n",
391 pDevIns->iInstance, pszUartType, uIoBase, uIrq));
392
393 MMR3HeapFree(pszUartType);
394
395 if (enmUartType == UARTTYPE_INVALID)
396 return PDMDEV_SET_ERROR(pDevIns, VERR_INVALID_PARAMETER,
397 N_("Configuration error: \"UartType\" contains invalid type"));
398
399 pThis->uIrq = uIrq;
400 pThis->PortBase = uIoBase;
401
402 /*
403 * Init locks, using explicit locking where necessary.
404 */
405 rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
406 if (RT_FAILURE(rc))
407 return rc;
408
409 /*
410 * Register the I/O ports.
411 */
412 rc = PDMDevHlpIOPortRegister(pDevIns, uIoBase, 8, 0,
413 serialIoPortWrite, serialIoPortRead,
414 NULL, NULL, "SERIAL");
415 if (RT_FAILURE(rc))
416 return rc;
417
418 PVM pVM = PDMDevHlpGetVM(pDevIns);
419 RTR0PTR pfnSerialIrqReqR0 = NIL_RTR0PTR;
420 RTRCPTR pfnSerialIrqReqRC = NIL_RTRCPTR;
421
422#ifdef VBOX_WITH_RAW_MODE_KEEP
423 if (pThis->fRCEnabled)
424 {
425 rc = PDMDevHlpIOPortRegisterRC(pDevIns, uIoBase, 8, 0, "serialIoPortWrite", "serialIoPortRead", NULL, NULL, "SERIAL");
426 if ( RT_SUCCESS(rc)
427 && VM_IS_RAW_MODE_ENABLED(pVM)) /** @todo this dynamic symbol resolving will be reworked later! */
428 rc = PDMR3LdrGetSymbolRC(pVM, pDevIns->pReg->pszRCMod, "serialIrqReq", &pfnSerialIrqReqRC);
429 if (RT_FAILURE(rc))
430 return rc;
431 }
432#endif
433
434 if (pThis->fR0Enabled)
435 {
436 rc = PDMDevHlpIOPortRegisterR0(pDevIns, uIoBase, 8, 0, "serialIoPortWrite", "serialIoPortRead", NULL, NULL, "SERIAL");
437 if (RT_SUCCESS(rc)) /** @todo this dynamic symbol resolving will be reworked later! */
438 rc = PDMR3LdrGetSymbolR0(pVM, pDevIns->pReg->pszR0Mod, "serialIrqReq", &pfnSerialIrqReqR0);
439 if (RT_FAILURE(rc))
440 return rc;
441 }
442
443 /*
444 * Saved state.
445 */
446 rc = PDMDevHlpSSMRegisterEx(pDevIns, UART_SAVED_STATE_VERSION, sizeof(*pThis), NULL,
447 NULL, serialR3LiveExec, NULL,
448 NULL, serialR3SaveExec, NULL,
449 NULL, serialR3LoadExec, serialR3LoadDone);
450 if (RT_FAILURE(rc))
451 return rc;
452
453 /* Init the UART core structure. */
454 rc = uartR3Init(&pThis->UartCore, pDevIns, enmUartType, 0,
455 fYieldOnLSRRead ? UART_CORE_YIELD_ON_LSR_READ : 0, serialIrqReq, pfnSerialIrqReqR0, pfnSerialIrqReqRC);
456 if (RT_FAILURE(rc))
457 return rc;
458
459 serialR3Reset(pDevIns);
460 return VINF_SUCCESS;
461}
462
463#endif /* IN_RING3 */
464
465/**
466 * The device registration structure.
467 */
468const PDMDEVREG g_DeviceSerialPort =
469{
470 /* .u32Version = */ PDM_DEVREG_VERSION,
471 /* .uReserved0 = */ 0,
472 /* .szName = */ "serial",
473 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
474 /* .fClass = */ PDM_DEVREG_CLASS_SERIAL,
475 /* .cMaxInstances = */ UINT32_MAX,
476 /* .uSharedVersion = */ 42,
477 /* .cbInstanceShared = */ sizeof(DEVSERIAL),
478 /* .cbInstanceCC = */ 0,
479 /* .cbInstanceRC = */ 0,
480 /* .cMaxPciDevices = */ 0,
481 /* .cMaxMsixVectors = */ 0,
482 /* .pszDescription = */ "Serial Communication Port",
483#if defined(IN_RING3)
484 /* .pszRCMod = */ "VBoxDDRC.rc",
485 /* .pszR0Mod = */ "VBoxDDR0.r0",
486 /* .pfnConstruct = */ serialR3Construct,
487 /* .pfnDestruct = */ serialR3Destruct,
488 /* .pfnRelocate = */ serialR3Relocate,
489 /* .pfnMemSetup = */ NULL,
490 /* .pfnPowerOn = */ NULL,
491 /* .pfnReset = */ serialR3Reset,
492 /* .pfnSuspend = */ NULL,
493 /* .pfnResume = */ NULL,
494 /* .pfnAttach = */ serialR3Attach,
495 /* .pfnDetach = */ serialR3Detach,
496 /* .pfnQueryInterface = */ NULL,
497 /* .pfnInitComplete = */ NULL,
498 /* .pfnPowerOff = */ NULL,
499 /* .pfnSoftReset = */ NULL,
500 /* .pfnReserved0 = */ NULL,
501 /* .pfnReserved1 = */ NULL,
502 /* .pfnReserved2 = */ NULL,
503 /* .pfnReserved3 = */ NULL,
504 /* .pfnReserved4 = */ NULL,
505 /* .pfnReserved5 = */ NULL,
506 /* .pfnReserved6 = */ NULL,
507 /* .pfnReserved7 = */ NULL,
508#elif defined(IN_RING0)
509 /* .pfnEarlyConstruct = */ NULL,
510 /* .pfnConstruct = */ NULL,
511 /* .pfnDestruct = */ NULL,
512 /* .pfnFinalDestruct = */ NULL,
513 /* .pfnRequest = */ NULL,
514 /* .pfnReserved0 = */ NULL,
515 /* .pfnReserved1 = */ NULL,
516 /* .pfnReserved2 = */ NULL,
517 /* .pfnReserved3 = */ NULL,
518 /* .pfnReserved4 = */ NULL,
519 /* .pfnReserved5 = */ NULL,
520 /* .pfnReserved6 = */ NULL,
521 /* .pfnReserved7 = */ NULL,
522#elif defined(IN_RC)
523 /* .pfnConstruct = */ NULL,
524 /* .pfnReserved0 = */ NULL,
525 /* .pfnReserved1 = */ NULL,
526 /* .pfnReserved2 = */ NULL,
527 /* .pfnReserved3 = */ NULL,
528 /* .pfnReserved4 = */ NULL,
529 /* .pfnReserved5 = */ NULL,
530 /* .pfnReserved6 = */ NULL,
531 /* .pfnReserved7 = */ NULL,
532#else
533# error "Not in IN_RING3, IN_RING0 or IN_RC!"
534#endif
535 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
536};
537
538#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
539
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