VirtualBox

source: vbox/trunk/src/VBox/Devices/Serial/DrvChar.cpp@ 73045

Last change on this file since 73045 was 71987, checked in by vboxsync, 7 years ago

Serial/DrvChar: Use critical section to protect send path against sending stale data under certain circumstances

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.9 KB
Line 
1/* $Id: DrvChar.cpp 71987 2018-04-23 16:54:18Z vboxsync $ */
2/** @file
3 * Driver that adapts PDMISTREAM into PDMICHARCONNECTOR / PDMICHARPORT.
4 *
5 * Converts synchronous calls (PDMICHARCONNECTOR::pfnWrite, PDMISTREAM::pfnRead)
6 * into asynchronous ones.
7 *
8 * Note that we don't use a send buffer here to be able to handle
9 * dropping of bytes for xmit at device level.
10 */
11
12/*
13 * Copyright (C) 2006-2017 Oracle Corporation
14 *
15 * This file is part of VirtualBox Open Source Edition (OSE), as
16 * available from http://www.virtualbox.org. This file is free software;
17 * you can redistribute it and/or modify it under the terms of the GNU
18 * General Public License (GPL) as published by the Free Software
19 * Foundation, in version 2 as it comes in the "COPYING" file of the
20 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
21 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
22 */
23
24
25/*********************************************************************************************************************************
26* Header Files *
27*********************************************************************************************************************************/
28#define LOG_GROUP LOG_GROUP_DRV_CHAR
29#include <VBox/vmm/pdmdrv.h>
30#include <iprt/asm.h>
31#include <iprt/assert.h>
32#include <iprt/poll.h>
33#include <iprt/stream.h>
34#include <iprt/critsect.h>
35#include <iprt/semaphore.h>
36#include <iprt/uuid.h>
37
38#include "VBoxDD.h"
39
40
41/*********************************************************************************************************************************
42* Defined Constants And Macros *
43*********************************************************************************************************************************/
44
45
46/*********************************************************************************************************************************
47* Structures and Typedefs *
48*********************************************************************************************************************************/
49/**
50 * Char driver instance data.
51 *
52 * @implements PDMICHARCONNECTOR
53 */
54typedef struct DRVCHAR
55{
56 /** Pointer to the driver instance structure. */
57 PPDMDRVINS pDrvIns;
58 /** Pointer to the char port interface of the driver/device above us. */
59 PPDMICHARPORT pDrvCharPort;
60 /** Pointer to the stream interface of the driver below us. */
61 PPDMISTREAM pDrvStream;
62 /** Our char interface. */
63 PDMICHARCONNECTOR ICharConnector;
64 /** Flag to notify the receive thread it should terminate. */
65 volatile bool fShutdown;
66 /** I/O thread. */
67 PPDMTHREAD pThrdIo;
68 /** Thread to relay read data to the device above without
69 * blocking send operations.
70 * @todo: This has to go but needs changes in the interface
71 * between device and driver.
72 */
73 PPDMTHREAD pThrdRead;
74 /** Event semaphore for the read relay thread. */
75 RTSEMEVENT hEvtSemRead;
76 /** Critical section protection the send part. */
77 RTCRITSECT CritSectSend;
78
79 /** Internal send FIFO queue */
80 uint8_t volatile u8SendByte;
81 bool volatile fSending;
82 uint8_t Alignment[2];
83
84 /** Receive buffer. */
85 uint8_t abBuffer[256];
86 /** Number of bytes remaining in the receive buffer. */
87 volatile size_t cbRemaining;
88 /** Current position into the read buffer. */
89 uint8_t *pbBuf;
90
91 /** Read/write statistics */
92 STAMCOUNTER StatBytesRead;
93 STAMCOUNTER StatBytesWritten;
94} DRVCHAR, *PDRVCHAR;
95AssertCompileMemberAlignment(DRVCHAR, StatBytesRead, 8);
96
97
98
99
100/* -=-=-=-=- IBase -=-=-=-=- */
101
102/**
103 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
104 */
105static DECLCALLBACK(void *) drvCharQueryInterface(PPDMIBASE pInterface, const char *pszIID)
106{
107 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
108 PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR);
109
110 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
111 PDMIBASE_RETURN_INTERFACE(pszIID, PDMICHARCONNECTOR, &pThis->ICharConnector);
112 return NULL;
113}
114
115
116/* -=-=-=-=- ICharConnector -=-=-=-=- */
117
118/**
119 * @interface_method_impl{PDMICHARCONNECTOR,pfnWrite}
120 */
121static DECLCALLBACK(int) drvCharWrite(PPDMICHARCONNECTOR pInterface, const void *pvBuf, size_t cbWrite)
122{
123 PDRVCHAR pThis = RT_FROM_MEMBER(pInterface, DRVCHAR, ICharConnector);
124 const char *pbBuffer = (const char *)pvBuf;
125 int rc = VINF_SUCCESS;
126
127 LogFlow(("%s: pvBuf=%#p cbWrite=%d\n", __FUNCTION__, pvBuf, cbWrite));
128
129 RTCritSectEnter(&pThis->CritSectSend);
130
131 for (uint32_t i = 0; i < cbWrite && RT_SUCCESS(rc); i++)
132 {
133 if (!ASMAtomicXchgBool(&pThis->fSending, true))
134 {
135 pThis->u8SendByte = pbBuffer[i];
136 pThis->pDrvStream->pfnPollInterrupt(pThis->pDrvStream);
137 STAM_COUNTER_INC(&pThis->StatBytesWritten);
138 }
139 else
140 rc = VERR_BUFFER_OVERFLOW;
141 }
142
143 RTCritSectLeave(&pThis->CritSectSend);
144 return rc;
145}
146
147
148/**
149 * @interface_method_impl{PDMICHARCONNECTOR,pfnSetParameters}
150 */
151static DECLCALLBACK(int) drvCharSetParameters(PPDMICHARCONNECTOR pInterface, unsigned Bps, char chParity,
152 unsigned cDataBits, unsigned cStopBits)
153{
154 RT_NOREF(pInterface, Bps, chParity, cDataBits, cStopBits);
155
156 LogFlow(("%s: Bps=%u chParity=%c cDataBits=%u cStopBits=%u\n", __FUNCTION__, Bps, chParity, cDataBits, cStopBits));
157 return VINF_SUCCESS;
158}
159
160
161/* -=-=-=-=- I/O thread -=-=-=-=- */
162
163/**
164 * Send thread loop - pushes data down thru the driver chain.
165 *
166 * @returns VBox status code.
167 * @param pDrvIns The char driver instance.
168 * @param pThread The worker thread.
169 */
170static DECLCALLBACK(int) drvCharIoLoop(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
171{
172 RT_NOREF(pDrvIns);
173 PDRVCHAR pThis = (PDRVCHAR)pThread->pvUser;
174
175 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
176 return VINF_SUCCESS;
177
178 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
179 {
180 uint32_t fEvts = 0;
181
182 if ( !pThis->cbRemaining
183 && pThis->pDrvStream->pfnRead)
184 fEvts |= RTPOLL_EVT_READ;
185 if (pThis->fSending)
186 fEvts |= RTPOLL_EVT_WRITE;
187
188 uint32_t fEvtsRecv = 0;
189 int rc = pThis->pDrvStream->pfnPoll(pThis->pDrvStream, fEvts, &fEvtsRecv, RT_INDEFINITE_WAIT);
190 if (RT_SUCCESS(rc))
191 {
192 if (fEvtsRecv & RTPOLL_EVT_WRITE)
193 {
194 RTCritSectEnter(&pThis->CritSectSend);
195 Assert(pThis->fSending);
196
197 size_t cbProcessed = 1;
198 uint8_t ch = pThis->u8SendByte;
199 rc = pThis->pDrvStream->pfnWrite(pThis->pDrvStream, &ch, &cbProcessed);
200 if (RT_SUCCESS(rc))
201 {
202 ASMAtomicXchgBool(&pThis->fSending, false);
203 Assert(cbProcessed == 1);
204 }
205 else if (rc == VERR_TIMEOUT)
206 {
207 /* Normal case, just means that the stream didn't accept a new
208 * character before the timeout elapsed. Just retry. */
209
210 /* do not change the rc status here, otherwise the (rc == VERR_TIMEOUT) branch
211 * in the wait above will never get executed */
212 /* rc = VINF_SUCCESS; */
213 }
214 else
215 {
216 LogRel(("Write failed with %Rrc; skipping\n", rc));
217 break;
218 }
219 RTCritSectLeave(&pThis->CritSectSend);
220 }
221
222 if (fEvtsRecv & RTPOLL_EVT_READ)
223 {
224 AssertPtr(pThis->pDrvStream->pfnRead);
225 Assert(!pThis->cbRemaining);
226
227 size_t cbRead = sizeof(pThis->abBuffer);
228 rc = pThis->pDrvStream->pfnRead(pThis->pDrvStream, &pThis->abBuffer[0], &cbRead);
229 if (RT_FAILURE(rc))
230 {
231 LogFlow(("Read failed with %Rrc\n", rc));
232 break;
233 }
234 pThis->pbBuf = &pThis->abBuffer[0];
235 ASMAtomicWriteZ(&pThis->cbRemaining, cbRead);
236 RTSemEventSignal(pThis->hEvtSemRead); /* Wakeup relay thread to continue. */
237 }
238 }
239 else if (rc != VERR_INTERRUPTED)
240 LogRelMax(10, ("Char#%d: Polling failed with %Rrc\n", pDrvIns->iInstance, rc));
241 }
242
243 return VINF_SUCCESS;
244}
245
246
247/**
248 * Unblock the send worker thread so it can respond to a state change.
249 *
250 * @returns VBox status code.
251 * @param pDrvIns The char driver instance.
252 * @param pThread The worker thread.
253 */
254static DECLCALLBACK(int) drvCharIoLoopWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
255{
256 PDRVCHAR pThis = (PDRVCHAR)pThread->pvUser;
257
258 RT_NOREF(pDrvIns);
259 return pThis->pDrvStream->pfnPollInterrupt(pThis->pDrvStream);
260}
261
262
263static DECLCALLBACK(int) drvCharReadRelayLoop(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
264{
265 RT_NOREF(pDrvIns);
266 PDRVCHAR pThis = (PDRVCHAR)pThread->pvUser;
267
268 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
269 return VINF_SUCCESS;
270
271 int rc = VINF_SUCCESS;
272 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
273 {
274 size_t cbRem = ASMAtomicReadZ(&pThis->cbRemaining);
275
276 /* Block as long as there is nothing to relay. */
277 if (!pThis->cbRemaining)
278 rc = RTSemEventWait(pThis->hEvtSemRead, RT_INDEFINITE_WAIT);
279
280 if (pThread->enmState != PDMTHREADSTATE_RUNNING)
281 break;
282
283 cbRem = ASMAtomicReadZ(&pThis->cbRemaining);
284 if (cbRem)
285 {
286 /* Send data to guest. */
287 size_t cbProcessed = cbRem;
288 rc = pThis->pDrvCharPort->pfnNotifyRead(pThis->pDrvCharPort, pThis->pbBuf, &cbProcessed);
289 if (RT_SUCCESS(rc))
290 {
291 Assert(cbProcessed);
292 pThis->pbBuf += cbProcessed;
293
294 /* Wake up the I/o thread so it can read new data to process. */
295 cbRem = ASMAtomicSubZ(&pThis->cbRemaining, cbProcessed);
296 if (cbRem == cbProcessed)
297 pThis->pDrvStream->pfnPollInterrupt(pThis->pDrvStream);
298 STAM_COUNTER_ADD(&pThis->StatBytesRead, cbProcessed);
299 }
300 else if (rc == VERR_TIMEOUT)
301 {
302 /* Normal case, just means that the guest didn't accept a new
303 * character before the timeout elapsed. Just retry. */
304 rc = VINF_SUCCESS;
305 }
306 else
307 {
308 LogFlow(("NotifyRead failed with %Rrc\n", rc));
309 break;
310 }
311 }
312 }
313
314 return VINF_SUCCESS;
315}
316
317
318/**
319 * Unblock the read relay worker thread so it can respond to a state change.
320 *
321 * @returns VBox status code.
322 * @param pDrvIns The char driver instance.
323 * @param pThread The worker thread.
324 */
325static DECLCALLBACK(int) drvCharReadRelayLoopWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
326{
327 PDRVCHAR pThis = (PDRVCHAR)pThread->pvUser;
328
329 RT_NOREF(pDrvIns);
330 return RTSemEventSignal(pThis->hEvtSemRead);
331}
332
333
334/**
335 * @callback_method_impl{PDMICHARCONNECTOR,pfnSetModemLines}
336 */
337static DECLCALLBACK(int) drvCharSetModemLines(PPDMICHARCONNECTOR pInterface, bool fRequestToSend, bool fDataTerminalReady)
338{
339 /* Nothing to do here. */
340 RT_NOREF(pInterface, fRequestToSend, fDataTerminalReady);
341 return VINF_SUCCESS;
342}
343
344
345/**
346 * @callback_method_impl{PDMICHARCONNECTOR,pfnSetBreak}
347 */
348static DECLCALLBACK(int) drvCharSetBreak(PPDMICHARCONNECTOR pInterface, bool fBreak)
349{
350 /* Nothing to do here. */
351 RT_NOREF(pInterface, fBreak);
352 return VINF_SUCCESS;
353}
354
355
356/* -=-=-=-=- driver interface -=-=-=-=- */
357
358/**
359 * Destruct a char driver instance.
360 *
361 * Most VM resources are freed by the VM. This callback is provided so that
362 * any non-VM resources can be freed correctly.
363 *
364 * @param pDrvIns The driver instance data.
365 */
366static DECLCALLBACK(void) drvCharDestruct(PPDMDRVINS pDrvIns)
367{
368 PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR);
369 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
370 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
371
372 if (RTCritSectIsInitialized(&pThis->CritSectSend))
373 RTCritSectDelete(&pThis->CritSectSend);
374
375 if (pThis->hEvtSemRead != NIL_RTSEMEVENT)
376 {
377 RTSemEventDestroy(pThis->hEvtSemRead);
378 pThis->hEvtSemRead = NIL_RTSEMEVENT;
379 }
380}
381
382
383/**
384 * Construct a char driver instance.
385 *
386 * @copydoc FNPDMDRVCONSTRUCT
387 */
388static DECLCALLBACK(int) drvCharConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
389{
390 RT_NOREF(pCfg);
391 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
392 PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR);
393 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
394
395 /*
396 * Init basic data members and interfaces.
397 */
398 pThis->pDrvIns = pDrvIns;
399 pThis->pThrdIo = NIL_RTTHREAD;
400 pThis->pThrdRead = NIL_RTTHREAD;
401 pThis->hEvtSemRead = NIL_RTSEMEVENT;
402 /* IBase. */
403 pDrvIns->IBase.pfnQueryInterface = drvCharQueryInterface;
404 /* ICharConnector. */
405 pThis->ICharConnector.pfnWrite = drvCharWrite;
406 pThis->ICharConnector.pfnSetParameters = drvCharSetParameters;
407 pThis->ICharConnector.pfnSetModemLines = drvCharSetModemLines;
408 pThis->ICharConnector.pfnSetBreak = drvCharSetBreak;
409
410 int rc = RTCritSectInit(&pThis->CritSectSend);
411 if (RT_FAILURE(rc))
412 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
413 N_("Char#%d: Failed to create critical section"), pDrvIns->iInstance);
414
415 /*
416 * Get the ICharPort interface of the above driver/device.
417 */
418 pThis->pDrvCharPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMICHARPORT);
419 if (!pThis->pDrvCharPort)
420 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("Char#%d has no char port interface above"), pDrvIns->iInstance);
421
422 /*
423 * Attach driver below and query its stream interface.
424 */
425 PPDMIBASE pBase;
426 rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBase);
427 if (RT_FAILURE(rc))
428 return rc; /* Don't call PDMDrvHlpVMSetError here as we assume that the driver already set an appropriate error */
429 pThis->pDrvStream = PDMIBASE_QUERY_INTERFACE(pBase, PDMISTREAM);
430 if (!pThis->pDrvStream)
431 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW, RT_SRC_POS, N_("Char#%d has no stream interface below"), pDrvIns->iInstance);
432
433 /* Don't start the receive relay thread if reading is not supported. */
434 if (pThis->pDrvStream->pfnRead)
435 {
436 rc = PDMDrvHlpThreadCreate(pThis->pDrvIns, &pThis->pThrdRead, pThis, drvCharReadRelayLoop,
437 drvCharReadRelayLoopWakeup, 0, RTTHREADTYPE_IO, "CharReadRel");
438 if (RT_FAILURE(rc))
439 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create read relay thread"), pDrvIns->iInstance);
440
441 rc = RTSemEventCreate(&pThis->hEvtSemRead);
442 AssertRCReturn(rc, rc);
443 }
444
445 rc = PDMDrvHlpThreadCreate(pThis->pDrvIns, &pThis->pThrdIo, pThis, drvCharIoLoop,
446 drvCharIoLoopWakeup, 0, RTTHREADTYPE_IO, "CharIo");
447 if (RT_FAILURE(rc))
448 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create send thread"), pDrvIns->iInstance);
449
450
451 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes written", "/Devices/Char%d/Written", pDrvIns->iInstance);
452 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes read", "/Devices/Char%d/Read", pDrvIns->iInstance);
453
454 return VINF_SUCCESS;
455}
456
457
458/**
459 * Char driver registration record.
460 */
461const PDMDRVREG g_DrvChar =
462{
463 /* u32Version */
464 PDM_DRVREG_VERSION,
465 /* szName */
466 "Char",
467 /* szRCMod */
468 "",
469 /* szR0Mod */
470 "",
471 /* pszDescription */
472 "Generic char driver.",
473 /* fFlags */
474 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
475 /* fClass. */
476 PDM_DRVREG_CLASS_CHAR,
477 /* cMaxInstances */
478 ~0U,
479 /* cbInstance */
480 sizeof(DRVCHAR),
481 /* pfnConstruct */
482 drvCharConstruct,
483 /* pfnDestruct */
484 drvCharDestruct,
485 /* pfnRelocate */
486 NULL,
487 /* pfnIOCtl */
488 NULL,
489 /* pfnPowerOn */
490 NULL,
491 /* pfnReset */
492 NULL,
493 /* pfnSuspend */
494 NULL,
495 /* pfnResume */
496 NULL,
497 /* pfnAttach */
498 NULL,
499 /* pfnDetach */
500 NULL,
501 /* pfnPowerOff */
502 NULL,
503 /* pfnSoftReset */
504 NULL,
505 /* u32EndVersion */
506 PDM_DRVREG_VERSION
507};
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