VirtualBox

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

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

Devices: Use new volatile SSM getters and enum macros. 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 81765 2019-11-11 16:00:31Z 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 = PDMDEVINS_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 = PDMDEVINS_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 = PDMDEVINS_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 = PDMDEVINS_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 = PDMDEVINS_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 = PDMDEVINS_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 PDMDEVHLP_SSM_GET_ENUM32_RET(pDevIns->pHlpR3, pSSM, enmType, UARTTYPE);
182 if (uPass == SSM_PASS_FINAL)
183 {
184 rc = uartR3LoadExec(&pThis->UartCore, pSSM, uVersion, uPass, NULL, NULL);
185 AssertRCReturn(rc, rc);
186 }
187 }
188 else
189 {
190 enmType = uVersion > UART_SAVED_STATE_VERSION_16450 ? UARTTYPE_16550A : UARTTYPE_16450;
191 if (uPass != SSM_PASS_FINAL)
192 {
193 int32_t iIrqTmp;
194 SSMR3GetS32(pSSM, &iIrqTmp);
195 uint32_t uPortBaseTmp;
196 rc = SSMR3GetU32(pSSM, &uPortBaseTmp);
197 AssertRCReturn(rc, rc);
198
199 bIrq = (uint8_t)iIrqTmp;
200 PortBase = (uint32_t)uPortBaseTmp;
201 }
202 else
203 {
204 rc = uartR3LoadExec(&pThis->UartCore, pSSM, uVersion, uPass, &bIrq, &PortBase);
205 AssertRCReturn(rc, rc);
206 }
207 }
208
209 if (uPass == SSM_PASS_FINAL)
210 {
211 /* The marker. */
212 uint32_t u32;
213 rc = SSMR3GetU32(pSSM, &u32);
214 AssertRCReturn(rc, rc);
215 AssertMsgReturn(u32 == UINT32_MAX, ("%#x\n", u32), VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
216 }
217
218 /*
219 * Check the config.
220 */
221 if ( pThis->uIrq != bIrq
222 || pThis->PortBase != PortBase
223 || pThis->UartCore.enmType != enmType)
224 return SSMR3SetCfgError(pSSM, RT_SRC_POS,
225 N_("Config mismatch - saved IRQ=%#x PortBase=%#x Type=%d; configured IRQ=%#x PortBase=%#x Type=%d"),
226 bIrq, PortBase, enmType, pThis->uIrq, pThis->PortBase, pThis->UartCore.enmType);
227
228 return VINF_SUCCESS;
229}
230
231
232/**
233 * @callback_method_impl{FNSSMDEVLOADDONE}
234 */
235static DECLCALLBACK(int) serialR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
236{
237 PDEVSERIAL pThis = PDMDEVINS_2_DATA(pDevIns, PDEVSERIAL);
238 return uartR3LoadDone(&pThis->UartCore, pSSM);
239}
240
241
242/* -=-=-=-=-=-=-=-=- PDMDEVREG -=-=-=-=-=-=-=-=- */
243
244/**
245 * @interface_method_impl{PDMDEVREG,pfnRelocate}
246 */
247static DECLCALLBACK(void) serialR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
248{
249 PDEVSERIAL pThis = PDMDEVINS_2_DATA(pDevIns, PDEVSERIAL);
250 uartR3Relocate(&pThis->UartCore, offDelta);
251}
252
253
254/**
255 * @interface_method_impl{PDMDEVREG,pfnReset}
256 */
257static DECLCALLBACK(void) serialR3Reset(PPDMDEVINS pDevIns)
258{
259 PDEVSERIAL pThis = PDMDEVINS_2_DATA(pDevIns, PDEVSERIAL);
260 uartR3Reset(&pThis->UartCore);
261}
262
263
264/**
265 * @interface_method_impl{PDMDEVREG,pfnAttach}
266 */
267static DECLCALLBACK(int) serialR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
268{
269 RT_NOREF(fFlags);
270 PDEVSERIAL pThis = PDMDEVINS_2_DATA(pDevIns, PDEVSERIAL);
271 return uartR3Attach(&pThis->UartCore, iLUN);
272}
273
274
275/**
276 * @interface_method_impl{PDMDEVREG,pfnDetach}
277 */
278static DECLCALLBACK(void) serialR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
279{
280 RT_NOREF(iLUN, fFlags);
281 PDEVSERIAL pThis = PDMDEVINS_2_DATA(pDevIns, PDEVSERIAL);
282 uartR3Detach(&pThis->UartCore);
283}
284
285
286/**
287 * @interface_method_impl{PDMDEVREG,pfnDestruct}
288 */
289static DECLCALLBACK(int) serialR3Destruct(PPDMDEVINS pDevIns)
290{
291 PDEVSERIAL pThis = PDMDEVINS_2_DATA(pDevIns, PDEVSERIAL);
292 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
293
294 uartR3Destruct(&pThis->UartCore);
295 return VINF_SUCCESS;
296}
297
298
299/**
300 * @interface_method_impl{PDMDEVREG,pfnConstruct}
301 */
302static DECLCALLBACK(int) serialR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
303{
304 PDEVSERIAL pThis = PDMDEVINS_2_DATA(pDevIns, PDEVSERIAL);
305 int rc = VINF_SUCCESS;
306
307 Assert(iInstance < 4);
308 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
309
310 /*
311 * Initialize the instance data.
312 * (Do this early or the destructor might choke on something!)
313 */
314 pThis->pDevInsR3 = pDevIns;
315 pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
316 pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
317
318 /*
319 * Validate and read the configuration.
320 */
321 if (!CFGMR3AreValuesValid(pCfg, "IRQ\0"
322 "IOBase\0"
323 "GCEnabled\0"
324 "R0Enabled\0"
325 "YieldOnLSRRead\0"
326 "UartType\0"
327 ))
328 {
329 AssertMsgFailed(("serialConstruct Invalid configuration values\n"));
330 return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
331 }
332
333 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &pThis->fRCEnabled, true);
334 if (RT_FAILURE(rc))
335 return PDMDEV_SET_ERROR(pDevIns, rc,
336 N_("Configuration error: Failed to get the \"GCEnabled\" value"));
337
338 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &pThis->fR0Enabled, true);
339 if (RT_FAILURE(rc))
340 return PDMDEV_SET_ERROR(pDevIns, rc,
341 N_("Configuration error: Failed to get the \"R0Enabled\" value"));
342
343 bool fYieldOnLSRRead = false;
344 rc = CFGMR3QueryBoolDef(pCfg, "YieldOnLSRRead", &fYieldOnLSRRead, false);
345 if (RT_FAILURE(rc))
346 return PDMDEV_SET_ERROR(pDevIns, rc,
347 N_("Configuration error: Failed to get the \"YieldOnLSRRead\" value"));
348
349 uint8_t uIrq = 0;
350 rc = CFGMR3QueryU8(pCfg, "IRQ", &uIrq);
351 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
352 {
353 /* Provide sensible defaults. */
354 if (iInstance == 0)
355 uIrq = 4;
356 else if (iInstance == 1)
357 uIrq = 3;
358 else
359 AssertReleaseFailed(); /* irq_lvl is undefined. */
360 }
361 else if (RT_FAILURE(rc))
362 return PDMDEV_SET_ERROR(pDevIns, rc,
363 N_("Configuration error: Failed to get the \"IRQ\" value"));
364
365 uint16_t uIoBase = 0;
366 rc = CFGMR3QueryU16(pCfg, "IOBase", &uIoBase);
367 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
368 {
369 if (iInstance == 0)
370 uIoBase = 0x3f8;
371 else if (iInstance == 1)
372 uIoBase = 0x2f8;
373 else
374 AssertReleaseFailed(); /* uIoBase is undefined */
375 }
376 else if (RT_FAILURE(rc))
377 return PDMDEV_SET_ERROR(pDevIns, rc,
378 N_("Configuration error: Failed to get the \"IOBase\" value"));
379
380 char *pszUartType;
381 rc = CFGMR3QueryStringAllocDef(pCfg, "UartType", &pszUartType, "16550A");
382 if (RT_FAILURE(rc))
383 return PDMDEV_SET_ERROR(pDevIns, rc,
384 N_("Configuration error: failed to read \"UartType\" as string"));
385
386 UARTTYPE enmUartType = serialR3GetUartTypeFromString(pszUartType);
387
388 if (enmUartType != UARTTYPE_INVALID)
389 LogRel(("Serial#%d: emulating %s (IOBase: %04x IRQ: %u)\n",
390 pDevIns->iInstance, pszUartType, uIoBase, uIrq));
391
392 MMR3HeapFree(pszUartType);
393
394 if (enmUartType == UARTTYPE_INVALID)
395 return PDMDEV_SET_ERROR(pDevIns, VERR_INVALID_PARAMETER,
396 N_("Configuration error: \"UartType\" contains invalid type"));
397
398 pThis->uIrq = uIrq;
399 pThis->PortBase = uIoBase;
400
401 /*
402 * Init locks, using explicit locking where necessary.
403 */
404 rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
405 if (RT_FAILURE(rc))
406 return rc;
407
408 /*
409 * Register the I/O ports.
410 */
411 rc = PDMDevHlpIOPortRegister(pDevIns, uIoBase, 8, 0,
412 serialIoPortWrite, serialIoPortRead,
413 NULL, NULL, "SERIAL");
414 if (RT_FAILURE(rc))
415 return rc;
416
417 PVM pVM = PDMDevHlpGetVM(pDevIns);
418 RTR0PTR pfnSerialIrqReqR0 = NIL_RTR0PTR;
419 RTRCPTR pfnSerialIrqReqRC = NIL_RTRCPTR;
420
421#ifdef VBOX_WITH_RAW_MODE_KEEP
422 if (pThis->fRCEnabled)
423 {
424 rc = PDMDevHlpIOPortRegisterRC(pDevIns, uIoBase, 8, 0, "serialIoPortWrite", "serialIoPortRead", NULL, NULL, "SERIAL");
425 if ( RT_SUCCESS(rc)
426 && VM_IS_RAW_MODE_ENABLED(pVM)) /** @todo this dynamic symbol resolving will be reworked later! */
427 rc = PDMR3LdrGetSymbolRC(pVM, pDevIns->pReg->pszRCMod, "serialIrqReq", &pfnSerialIrqReqRC);
428 if (RT_FAILURE(rc))
429 return rc;
430 }
431#endif
432
433 if (pThis->fR0Enabled)
434 {
435 rc = PDMDevHlpIOPortRegisterR0(pDevIns, uIoBase, 8, 0, "serialIoPortWrite", "serialIoPortRead", NULL, NULL, "SERIAL");
436 if (RT_SUCCESS(rc)) /** @todo this dynamic symbol resolving will be reworked later! */
437 rc = PDMR3LdrGetSymbolR0(pVM, pDevIns->pReg->pszR0Mod, "serialIrqReq", &pfnSerialIrqReqR0);
438 if (RT_FAILURE(rc))
439 return rc;
440 }
441
442 /*
443 * Saved state.
444 */
445 rc = PDMDevHlpSSMRegisterEx(pDevIns, UART_SAVED_STATE_VERSION, sizeof(*pThis), NULL,
446 NULL, serialR3LiveExec, NULL,
447 NULL, serialR3SaveExec, NULL,
448 NULL, serialR3LoadExec, serialR3LoadDone);
449 if (RT_FAILURE(rc))
450 return rc;
451
452 /* Init the UART core structure. */
453 rc = uartR3Init(&pThis->UartCore, pDevIns, enmUartType, 0,
454 fYieldOnLSRRead ? UART_CORE_YIELD_ON_LSR_READ : 0, serialIrqReq, pfnSerialIrqReqR0, pfnSerialIrqReqRC);
455 if (RT_FAILURE(rc))
456 return rc;
457
458 serialR3Reset(pDevIns);
459 return VINF_SUCCESS;
460}
461
462#endif /* IN_RING3 */
463
464/**
465 * The device registration structure.
466 */
467const PDMDEVREG g_DeviceSerialPort =
468{
469 /* .u32Version = */ PDM_DEVREG_VERSION,
470 /* .uReserved0 = */ 0,
471 /* .szName = */ "serial",
472 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ,
473 /* .fClass = */ PDM_DEVREG_CLASS_SERIAL,
474 /* .cMaxInstances = */ UINT32_MAX,
475 /* .uSharedVersion = */ 42,
476 /* .cbInstanceShared = */ sizeof(DEVSERIAL),
477 /* .cbInstanceCC = */ 0,
478 /* .cbInstanceRC = */ 0,
479 /* .cMaxPciDevices = */ 0,
480 /* .cMaxMsixVectors = */ 0,
481 /* .pszDescription = */ "Serial Communication Port",
482#if defined(IN_RING3)
483 /* .pszRCMod = */ "VBoxDDRC.rc",
484 /* .pszR0Mod = */ "VBoxDDR0.r0",
485 /* .pfnConstruct = */ serialR3Construct,
486 /* .pfnDestruct = */ serialR3Destruct,
487 /* .pfnRelocate = */ serialR3Relocate,
488 /* .pfnMemSetup = */ NULL,
489 /* .pfnPowerOn = */ NULL,
490 /* .pfnReset = */ serialR3Reset,
491 /* .pfnSuspend = */ NULL,
492 /* .pfnResume = */ NULL,
493 /* .pfnAttach = */ serialR3Attach,
494 /* .pfnDetach = */ serialR3Detach,
495 /* .pfnQueryInterface = */ NULL,
496 /* .pfnInitComplete = */ NULL,
497 /* .pfnPowerOff = */ NULL,
498 /* .pfnSoftReset = */ NULL,
499 /* .pfnReserved0 = */ NULL,
500 /* .pfnReserved1 = */ NULL,
501 /* .pfnReserved2 = */ NULL,
502 /* .pfnReserved3 = */ NULL,
503 /* .pfnReserved4 = */ NULL,
504 /* .pfnReserved5 = */ NULL,
505 /* .pfnReserved6 = */ NULL,
506 /* .pfnReserved7 = */ NULL,
507#elif defined(IN_RING0)
508 /* .pfnEarlyConstruct = */ NULL,
509 /* .pfnConstruct = */ NULL,
510 /* .pfnDestruct = */ NULL,
511 /* .pfnFinalDestruct = */ NULL,
512 /* .pfnRequest = */ NULL,
513 /* .pfnReserved0 = */ NULL,
514 /* .pfnReserved1 = */ NULL,
515 /* .pfnReserved2 = */ NULL,
516 /* .pfnReserved3 = */ NULL,
517 /* .pfnReserved4 = */ NULL,
518 /* .pfnReserved5 = */ NULL,
519 /* .pfnReserved6 = */ NULL,
520 /* .pfnReserved7 = */ NULL,
521#elif defined(IN_RC)
522 /* .pfnConstruct = */ NULL,
523 /* .pfnReserved0 = */ NULL,
524 /* .pfnReserved1 = */ NULL,
525 /* .pfnReserved2 = */ NULL,
526 /* .pfnReserved3 = */ NULL,
527 /* .pfnReserved4 = */ NULL,
528 /* .pfnReserved5 = */ NULL,
529 /* .pfnReserved6 = */ NULL,
530 /* .pfnReserved7 = */ NULL,
531#else
532# error "Not in IN_RING3, IN_RING0 or IN_RC!"
533#endif
534 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
535};
536
537#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
538
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