VirtualBox

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

Last change on this file since 74942 was 74754, checked in by vboxsync, 6 years ago

DevSerial: Return error if uartR3Init fails.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use