VirtualBox

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

Last change on this file since 77807 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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