VirtualBox

source: vbox/trunk/src/VBox/Devices/Serial/DevOxPcie958.cpp

Last change on this file was 104225, checked in by vboxsync, 8 weeks ago

Devices/Serial/DevPcieOx958: Be more strict with what the emulation allows for access sizes, fix status code checks, bugref:10635

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 24.7 KB
RevLine 
[73135]1/* $Id: DevOxPcie958.cpp 104225 2024-04-08 11:47:37Z vboxsync $ */
2/** @file
3 * DevOxPcie958 - Oxford Semiconductor OXPCIe958 PCI Express bridge to octal serial port emulation
4 */
5
6/*
[98103]7 * Copyright (C) 2018-2023 Oracle and/or its affiliates.
[73135]8 *
[96407]9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
[73135]26 */
27
28/** @page pg_dev_oxpcie958 OXPCIe958 - Oxford Semiconductor OXPCIe958 PCI Express bridge to octal serial port emulation.
29 * @todo Write something
30 */
31
32
33/*********************************************************************************************************************************
34* Header Files *
35*********************************************************************************************************************************/
36#define LOG_GROUP LOG_GROUP_DEV_SERIAL
37#include <VBox/pci.h>
38#include <VBox/msi.h>
39#include <VBox/vmm/pdm.h>
40#include <VBox/vmm/pdmpci.h>
41#include <VBox/err.h>
42#include <VBox/log.h>
43#include <iprt/assert.h>
44#include <iprt/list.h>
45#include <iprt/asm.h>
46
47#include "VBoxDD.h"
48#include "UartCore.h"
49
50
51/** @name PCI device related constants.
[81369]52 * @{ */
[73135]53/** The PCI device ID. */
54#define OX958_PCI_DEVICE_ID 0xc308
55/** The PCI vendor ID. */
56#define OX958_PCI_VENDOR_ID 0x1415
57/** Where the MSI capability starts. */
58#define OX958_PCI_MSI_CAP_OFS 0x80
59/** Where the MSI-X capability starts. */
60#define OX958_PCI_MSIX_CAP_OFS (OX958_PCI_MSI_CAP_OFS + VBOX_MSI_CAP_SIZE_64)
61/** The BAR for the MSI-X related functionality. */
62#define OX958_PCI_MSIX_BAR 1
63/** @} */
64
65/** Maximum number of UARTs supported by the device. */
66#define OX958_UARTS_MAX 16
67
68/** Offset op the class code and revision ID register. */
69#define OX958_REG_CC_REV_ID 0x00
70/** Offset fof the UART count register. */
71#define OX958_REG_UART_CNT 0x04
72/** Offset of the global UART IRQ status register. */
73#define OX958_REG_UART_IRQ_STS 0x08
74/** Offset of the global UART IRQ enable register. */
75#define OX958_REG_UART_IRQ_ENABLE 0x0c
76/** Offset of the global UART IRQ disable register. */
77#define OX958_REG_UART_IRQ_DISABLE 0x10
78/** Offset of the global UART wake IRQ enable register. */
79#define OX958_REG_UART_WAKE_IRQ_ENABLE 0x14
80/** Offset of the global UART wake IRQ disable register. */
81#define OX958_REG_UART_WAKE_IRQ_DISABLE 0x18
82/** Offset of the region in MMIO space where the UARTs actually start. */
83#define OX958_REG_UART_REGION_OFFSET 0x1000
84/** Register region size for each UART. */
85#define OX958_REG_UART_REGION_SIZE 0x200
86/** Offset where the DMA channels registers start for each UART. */
87#define OX958_REG_UART_DMA_REGION_OFFSET 0x100
88
89
90/**
[81893]91 * Shared OXPCIe958 UART core.
[73135]92 */
93typedef struct OX958UART
94{
95 /** The UART core. */
96 UARTCORE UartCore;
97 /** DMA address configured. */
98 RTGCPHYS GCPhysDmaAddr;
99 /** The DMA transfer length configured. */
100 uint32_t cbDmaXfer;
101 /** The DMA status registers. */
102 uint32_t u32RegDmaSts;
103} OX958UART;
[81893]104/** Pointer to a shared OXPCIe958 UART core. */
[73135]105typedef OX958UART *POX958UART;
106
[81893]107/**
108 * Ring-3 OXPCIe958 UART core.
109 */
110typedef struct OX958UARTR3
111{
112 /** The ring-3 UART core. */
113 UARTCORER3 UartCore;
114} OX958UARTR3;
115/** Pointer to a ring-3 OXPCIe958 UART core. */
116typedef OX958UARTR3 *POX958UARTR3;
[73135]117
118/**
[81893]119 * Ring-0 OXPCIe958 UART core.
[73135]120 */
[81893]121typedef struct OX958UARTR0
122{
123 /** The ring-0 UART core. */
124 UARTCORER0 UartCore;
125} OX958UARTR0;
126/** Pointer to a ring-0 OXPCIe958 UART core. */
127typedef OX958UARTR0 *POX958UARTR0;
128
129
130/**
131 * Raw-mode OXPCIe958 UART core.
132 */
133typedef struct OX958UARTRC
134{
135 /** The raw-mode UART core. */
136 UARTCORERC UartCore;
137} OX958UARTRC;
138/** Pointer to a raw-mode OXPCIe958 UART core. */
139typedef OX958UARTRC *POX958UARTRC;
140
141/** Current context OXPCIe958 UART core. */
142typedef CTX_SUFF(OX958UART) OX958UARTCC;
143/** Pointer to a current context OXPCIe958 UART core. */
144typedef CTX_SUFF(POX958UART) POX958UARTCC;
145
146
147/**
148 * Shared OXPCIe958 device instance data.
149 */
[73135]150typedef struct DEVOX958
151{
152 /** UART global IRQ status. */
153 volatile uint32_t u32RegIrqStsGlob;
154 /** UART global IRQ enable mask. */
155 volatile uint32_t u32RegIrqEnGlob;
156 /** UART wake IRQ enable mask. */
157 volatile uint32_t u32RegIrqEnWake;
158 /** Number of UARTs configured. */
159 uint32_t cUarts;
[81891]160 /** Handle to the MMIO region (PCI region \#0). */
161 IOMMMIOHANDLE hMmio;
[73135]162 /** The UARTs. */
163 OX958UART aUarts[OX958_UARTS_MAX];
164} DEVOX958;
[81893]165/** Pointer to shared OXPCIe958 device instance data. */
[73135]166typedef DEVOX958 *PDEVOX958;
167
[81893]168/**
169 * Ring-3 OXPCIe958 device instance data.
170 */
171typedef struct DEVOX958R3
172{
173 /** The UARTs. */
174 OX958UARTR3 aUarts[OX958_UARTS_MAX];
175} DEVOX958R3;
176/** Pointer to ring-3 OXPCIe958 device instance data. */
177typedef DEVOX958R3 *PDEVOX958R3;
178
179/**
180 * Ring-0 OXPCIe958 device instance data.
181 */
182typedef struct DEVOX958R0
183{
184 /** The UARTs. */
185 OX958UARTR0 aUarts[OX958_UARTS_MAX];
186} DEVOX958R0;
187/** Pointer to ring-0 OXPCIe958 device instance data. */
188typedef DEVOX958R0 *PDEVOX958R0;
189
190/**
191 * Raw-mode OXPCIe958 device instance data.
192 */
193typedef struct DEVOX958RC
194{
195 /** The UARTs. */
196 OX958UARTRC aUarts[OX958_UARTS_MAX];
197} DEVOX958RC;
198/** Pointer to raw-mode OXPCIe958 device instance data. */
199typedef DEVOX958RC *PDEVOX958RC;
200
201/** Current context OXPCIe958 device instance data. */
202typedef CTX_SUFF(DEVOX958) DEVOX958CC;
203/** Pointer to current context OXPCIe958 device instance data. */
204typedef CTX_SUFF(PDEVOX958) PDEVOX958CC;
205
206
[73135]207#ifndef VBOX_DEVICE_STRUCT_TESTCASE
208
209
210
211/**
212 * Update IRQ status of the device.
213 *
[81891]214 * @param pDevIns The device instance.
[81893]215 * @param pThis The shared OXPCIe958 device instance data.
[73135]216 */
[81891]217static void ox958IrqUpdate(PPDMDEVINS pDevIns, PDEVOX958 pThis)
[73135]218{
219 uint32_t u32IrqSts = ASMAtomicReadU32(&pThis->u32RegIrqStsGlob);
220 uint32_t u32IrqEn = ASMAtomicReadU32(&pThis->u32RegIrqEnGlob);
221
222 if (u32IrqSts & u32IrqEn)
[81891]223 PDMDevHlpPCISetIrq(pDevIns, 0, PDM_IRQ_LEVEL_HIGH);
[73135]224 else
[81891]225 PDMDevHlpPCISetIrq(pDevIns, 0, PDM_IRQ_LEVEL_LOW);
[73135]226}
227
228
229/**
230 * Performs a register read from the given UART.
231 *
[81891]232 * @returns Strict VBox status code.
[81893]233 * @param pDevIns The device instance.
234 * @param pThis The shared OXPCIe958 device instance data.
235 * @param pUart The UART accessed, shared bits.
236 * @param pUartCC The UART accessed, current context bits.
[73135]237 * @param offUartReg Offset of the register being read.
238 * @param pv Where to store the read data.
239 * @param cb Number of bytes to read.
240 */
[81893]241static VBOXSTRICTRC ox958UartRegRead(PPDMDEVINS pDevIns, PDEVOX958 pThis, POX958UART pUart, POX958UARTCC pUartCC,
242 uint32_t offUartReg, void *pv, unsigned cb)
[73135]243{
[81891]244 VBOXSTRICTRC rc;
[73135]245 RT_NOREF(pThis);
246
247 if (offUartReg >= OX958_REG_UART_DMA_REGION_OFFSET)
248 {
249 /* Access to the DMA registers. */
[104224]250 rc = VINF_IOM_MMIO_UNUSED_00; /** @todo Misses implementation, just pretend there is nothing there yet. */
[73135]251 }
252 else /* Access UART registers. */
[104225]253 {
254 AssertReturn(cb == 1, VINF_IOM_MMIO_UNUSED_00);
[73135]255
[104225]256 uint32_t u32 = 0;
257 rc = uartRegRead(pDevIns, &pUart->UartCore, &pUartCC->UartCore, offUartReg, &u32, 1);
258 Assert(rc == VINF_SUCCESS || rc == VINF_IOM_R3_IOPORT_READ);
259
260 if (rc == VINF_SUCCESS)
261 *(uint8_t *)pv = (uint8_t)u32;
262 else if (rc == VERR_IOM_IOPORT_UNUSED)
263 rc = VINF_IOM_MMIO_UNUSED_00;
264 else if (rc == VINF_IOM_R3_IOPORT_READ)
265 rc = VINF_IOM_R3_MMIO_READ;
266 }
267
[73135]268 return rc;
269}
270
271
272/**
273 * Performs a register write to the given UART.
274 *
[81891]275 * @returns Strict VBox status code.
[81893]276 * @param pDevIns The device instance.
277 * @param pThis The shared OXPCIe958 device instance data.
278 * @param pUart The UART accessed, shared bits.
279 * @param pUartCC The UART accessed, current context bits.
[73135]280 * @param offUartReg Offset of the register being written.
281 * @param pv The data to write.
282 * @param cb Number of bytes to write.
283 */
[81893]284static VBOXSTRICTRC ox958UartRegWrite(PPDMDEVINS pDevIns, PDEVOX958 pThis, POX958UART pUart, POX958UARTCC pUartCC,
285 uint32_t offUartReg, const void *pv, unsigned cb)
[73135]286{
[81891]287 VBOXSTRICTRC rc;
[73135]288 RT_NOREF(pThis);
289
290 if (offUartReg >= OX958_REG_UART_DMA_REGION_OFFSET)
291 {
292 /* Access to the DMA registers. */
[81891]293 rc = VINF_SUCCESS;
[73135]294 }
295 else /* Access UART registers. */
[104225]296 {
297 AssertReturn(cb == 1, VINF_SUCCESS);
[73135]298
[104225]299 rc = uartRegWrite(pDevIns, &pUart->UartCore, &pUartCC->UartCore, offUartReg, *(const uint8_t *)pv, 1);
300 Assert(rc == VINF_SUCCESS || rc == VINF_IOM_R3_IOPORT_WRITE);
301
302 if (rc == VINF_IOM_R3_IOPORT_WRITE)
303 rc = VINF_IOM_R3_MMIO_WRITE;
304 }
305
[73135]306 return rc;
307}
308
309
310/**
311 * UART core IRQ request callback.
312 *
313 * @param pDevIns The device instance.
314 * @param pUart The UART requesting an IRQ update.
315 * @param iLUN The UART index.
316 * @param iLvl IRQ level requested.
317 */
[95108]318static DECLCALLBACK(void) ox958IrqReq(PPDMDEVINS pDevIns, PUARTCORE pUart, unsigned iLUN, int iLvl)
[73135]319{
320 RT_NOREF(pUart);
[81591]321 PDEVOX958 pThis = PDMDEVINS_2_DATA(pDevIns, PDEVOX958);
[73135]322
323 if (iLvl)
324 ASMAtomicOrU32(&pThis->u32RegIrqStsGlob, RT_BIT_32(iLUN));
325 else
326 ASMAtomicAndU32(&pThis->u32RegIrqStsGlob, ~RT_BIT_32(iLUN));
[81891]327 ox958IrqUpdate(pDevIns, pThis);
[73135]328}
329
330
331/**
[81891]332 * @callback_method_impl{FNIOMMMIONEWREAD}
[73135]333 */
[81891]334static DECLCALLBACK(VBOXSTRICTRC) ox958MmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
[73135]335{
[81893]336 PDEVOX958 pThis = PDMDEVINS_2_DATA(pDevIns, PDEVOX958);
337 PDEVOX958CC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVOX958CC);
338 VBOXSTRICTRC rc = VINF_SUCCESS;
[81891]339 RT_NOREF(pvUser);
[73135]340
[81891]341 if (off < OX958_REG_UART_REGION_OFFSET)
[73135]342 {
[104225]343 AssertReturn(cb == 4, VINF_IOM_MMIO_UNUSED_00);
[73135]344 uint32_t *pu32 = (uint32_t *)pv;
345
[81891]346 switch ((uint32_t)off)
[73135]347 {
348 case OX958_REG_CC_REV_ID:
349 *pu32 = 0x00070002;
350 break;
351 case OX958_REG_UART_CNT:
352 *pu32 = pThis->cUarts;
353 break;
354 case OX958_REG_UART_IRQ_STS:
355 *pu32 = ASMAtomicReadU32(&pThis->u32RegIrqStsGlob);
356 break;
357 case OX958_REG_UART_IRQ_ENABLE:
358 *pu32 = ASMAtomicReadU32(&pThis->u32RegIrqEnGlob);
359 break;
360 case OX958_REG_UART_IRQ_DISABLE:
361 *pu32 = ~ASMAtomicReadU32(&pThis->u32RegIrqEnGlob);
362 break;
363 case OX958_REG_UART_WAKE_IRQ_ENABLE:
364 *pu32 = ASMAtomicReadU32(&pThis->u32RegIrqEnWake);
365 break;
366 case OX958_REG_UART_WAKE_IRQ_DISABLE:
367 *pu32 = ~ASMAtomicReadU32(&pThis->u32RegIrqEnWake);
368 break;
369 default:
370 rc = VINF_IOM_MMIO_UNUSED_00;
371 }
372 }
373 else
374 {
375 /* Figure out the UART accessed from the offset. */
[81891]376 off -= OX958_REG_UART_REGION_OFFSET;
377 uint32_t iUart = (uint32_t)off / OX958_REG_UART_REGION_SIZE;
378 uint32_t offUartReg = (uint32_t)off % OX958_REG_UART_REGION_SIZE;
379 if (iUart < RT_MIN(pThis->cUarts, RT_ELEMENTS(pThis->aUarts)))
[73135]380 {
[81893]381 POX958UART pUart = &pThis->aUarts[iUart];
382 POX958UARTCC pUartCC = &pThisCC->aUarts[iUart];
383 rc = ox958UartRegRead(pDevIns, pThis, pUart, pUartCC, offUartReg, pv, cb);
[73135]384 }
385 else
386 rc = VINF_IOM_MMIO_UNUSED_00;
387 }
388
389 return rc;
390}
391
392
393/**
[81891]394 * @callback_method_impl{FNIOMMMIONEWWRITE}
[73135]395 */
[81891]396static DECLCALLBACK(VBOXSTRICTRC) ox958MmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
[73135]397{
[81893]398 PDEVOX958 pThis = PDMDEVINS_2_DATA(pDevIns, PDEVOX958);
399 PDEVOX958CC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVOX958CC);
400 VBOXSTRICTRC rc = VINF_SUCCESS;
[73135]401 RT_NOREF1(pvUser);
402
[81891]403 if (off < OX958_REG_UART_REGION_OFFSET)
[73135]404 {
[104225]405 AssertReturn(cb == 4, VINF_SUCCESS);
[73135]406 const uint32_t u32 = *(const uint32_t *)pv;
407
[81891]408 switch ((uint32_t)off)
[73135]409 {
410 case OX958_REG_UART_IRQ_ENABLE:
411 ASMAtomicOrU32(&pThis->u32RegIrqEnGlob, u32);
[81891]412 ox958IrqUpdate(pDevIns, pThis);
[73135]413 break;
414 case OX958_REG_UART_IRQ_DISABLE:
415 ASMAtomicAndU32(&pThis->u32RegIrqEnGlob, ~u32);
[81891]416 ox958IrqUpdate(pDevIns, pThis);
[73135]417 break;
418 case OX958_REG_UART_WAKE_IRQ_ENABLE:
419 ASMAtomicOrU32(&pThis->u32RegIrqEnWake, u32);
420 break;
421 case OX958_REG_UART_WAKE_IRQ_DISABLE:
422 ASMAtomicAndU32(&pThis->u32RegIrqEnWake, ~u32);
423 break;
424 case OX958_REG_UART_IRQ_STS: /* Readonly */
425 case OX958_REG_CC_REV_ID: /* Readonly */
426 case OX958_REG_UART_CNT: /* Readonly */
427 default:
[81891]428 break;
[73135]429 }
430 }
431 else
432 {
433 /* Figure out the UART accessed from the offset. */
[81891]434 off -= OX958_REG_UART_REGION_OFFSET;
435 uint32_t iUart = (uint32_t)off / OX958_REG_UART_REGION_SIZE;
436 uint32_t offUartReg = (uint32_t)off % OX958_REG_UART_REGION_SIZE;
437 if (iUart < RT_MIN(pThis->cUarts, RT_ELEMENTS(pThis->aUarts)))
[73135]438 {
[81893]439 POX958UART pUart = &pThis->aUarts[iUart];
440 POX958UARTCC pUartCC = &pThisCC->aUarts[iUart];
441 rc = ox958UartRegWrite(pDevIns, pThis, pUart, pUartCC, offUartReg, pv, cb);
[73135]442 }
443 }
444
445 return rc;
446}
447
448
449#ifdef IN_RING3
450
451/** @interface_method_impl{PDMDEVREG,pfnDetach} */
452static DECLCALLBACK(void) ox958R3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
453{
[81893]454 PDEVOX958 pThis = PDMDEVINS_2_DATA(pDevIns, PDEVOX958);
455 PDEVOX958CC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVOX958CC);
[73135]456 AssertReturnVoid(iLUN >= pThis->cUarts);
457
458 RT_NOREF(fFlags);
459
[81893]460 return uartR3Detach(pDevIns, &pThis->aUarts[iLUN].UartCore, &pThisCC->aUarts[iLUN].UartCore);
[73135]461}
462
463
464/** @interface_method_impl{PDMDEVREG,pfnAttach} */
465static DECLCALLBACK(int) ox958R3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
466{
[81893]467 PDEVOX958 pThis = PDMDEVINS_2_DATA(pDevIns, PDEVOX958);
468 PDEVOX958CC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVOX958CC);
[73135]469
470 RT_NOREF(fFlags);
471
[81891]472 if (iLUN >= RT_MIN(pThis->cUarts, RT_ELEMENTS(pThis->aUarts)))
[73135]473 return VERR_PDM_LUN_NOT_FOUND;
474
[81893]475 return uartR3Attach(pDevIns, &pThis->aUarts[iLUN].UartCore, &pThisCC->aUarts[iLUN].UartCore, iLUN);
[73135]476}
477
478
479/** @interface_method_impl{PDMDEVREG,pfnReset} */
480static DECLCALLBACK(void) ox958R3Reset(PPDMDEVINS pDevIns)
481{
[81893]482 PDEVOX958 pThis = PDMDEVINS_2_DATA(pDevIns, PDEVOX958);
483 PDEVOX958CC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVOX958CC);
[73135]484
485 pThis->u32RegIrqStsGlob = 0x00;
486 pThis->u32RegIrqEnGlob = 0x00;
487 pThis->u32RegIrqEnWake = 0x00;
488
[81891]489 uint32_t const cUarts = RT_MIN(pThis->cUarts, RT_ELEMENTS(pThis->aUarts));
490 for (uint32_t i = 0; i < cUarts; i++)
[81893]491 uartR3Reset(pDevIns, &pThis->aUarts[i].UartCore, &pThisCC->aUarts[i].UartCore);
[73135]492}
493
494
495/** @interface_method_impl{PDMDEVREG,pfnDestruct} */
496static DECLCALLBACK(int) ox958R3Destruct(PPDMDEVINS pDevIns)
497{
[81891]498 PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
[81591]499 PDEVOX958 pThis = PDMDEVINS_2_DATA(pDevIns, PDEVOX958);
[73135]500
[81891]501 uint32_t const cUarts = RT_MIN(pThis->cUarts, RT_ELEMENTS(pThis->aUarts));
502 for (uint32_t i = 0; i < cUarts; i++)
[81893]503 uartR3Destruct(pDevIns, &pThis->aUarts[i].UartCore);
[73135]504
505 return VINF_SUCCESS;
506}
507
508
509/** @interface_method_impl{PDMDEVREG,pfnConstruct} */
510static DECLCALLBACK(int) ox958R3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
511{
[81891]512 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
[73135]513 RT_NOREF(iInstance);
[81893]514 PDEVOX958 pThis = PDMDEVINS_2_DATA(pDevIns, PDEVOX958);
515 PDEVOX958R3 pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVOX958CC);
516 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
[81891]517 bool fMsiXSupported = false;
518 int rc;
[73135]519
[81891]520 /*
521 * Init instance data.
522 */
523 rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
524 AssertRCReturn(rc, rc);
525
[73135]526 /*
527 * Validate and read configuration.
528 */
[81891]529 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "MsiXSupported|UartCount", "");
[73135]530
[81891]531 rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "MsiXSupported", &fMsiXSupported, true);
[73135]532 if (RT_FAILURE(rc))
[81891]533 return PDMDEV_SET_ERROR(pDevIns, rc, N_("OXPCIe958 configuration error: failed to read \"MsiXSupported\" as boolean"));
[73135]534
[81891]535 rc = pHlp->pfnCFGMQueryU32Def(pCfg, "UartCount", &pThis->cUarts, OX958_UARTS_MAX);
[73135]536 if (RT_FAILURE(rc))
[81891]537 return PDMDEV_SET_ERROR(pDevIns, rc, N_("OXPCIe958 configuration error: failed to read \"UartCount\" as unsigned 32bit integer"));
[73135]538
539 if (!pThis->cUarts || pThis->cUarts > OX958_UARTS_MAX)
540 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
541 N_("OXPCIe958 configuration error: \"UartCount\" has invalid value %u (must be in range [1 .. %u]"),
542 pThis->cUarts, OX958_UARTS_MAX);
543
544 /*
[81891]545 * Fill PCI config space.
[73135]546 */
[81031]547 PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
548 PDMPCIDEV_ASSERT_VALID(pDevIns, pPciDev);
549
550 PDMPciDevSetVendorId(pPciDev, OX958_PCI_VENDOR_ID);
551 PDMPciDevSetDeviceId(pPciDev, OX958_PCI_DEVICE_ID);
552 PDMPciDevSetCommand(pPciDev, 0x0000);
[81891]553# ifdef VBOX_WITH_MSI_DEVICES
[81031]554 PDMPciDevSetStatus(pPciDev, VBOX_PCI_STATUS_CAP_LIST);
555 PDMPciDevSetCapabilityList(pPciDev, OX958_PCI_MSI_CAP_OFS);
[81891]556# else
[81031]557 PDMPciDevSetCapabilityList(pPciDev, 0x70);
[81891]558# endif
[81031]559 PDMPciDevSetRevisionId(pPciDev, 0x00);
560 PDMPciDevSetClassBase(pPciDev, 0x07); /* Communication controller. */
561 PDMPciDevSetClassSub(pPciDev, 0x00); /* Serial controller. */
562 PDMPciDevSetClassProg(pPciDev, 0x02); /* 16550. */
[73135]563
[81031]564 PDMPciDevSetRevisionId(pPciDev, 0x00);
565 PDMPciDevSetSubSystemVendorId(pPciDev, OX958_PCI_VENDOR_ID);
566 PDMPciDevSetSubSystemId(pPciDev, OX958_PCI_DEVICE_ID);
[73135]567
[81031]568 PDMPciDevSetInterruptLine(pPciDev, 0x00);
569 PDMPciDevSetInterruptPin(pPciDev, 0x01);
[73135]570 /** @todo More Capabilities. */
571
572 /*
573 * Register PCI device and I/O region.
574 */
[81031]575 rc = PDMDevHlpPCIRegister(pDevIns, pPciDev);
[73135]576 if (RT_FAILURE(rc))
577 return rc;
578
[81891]579# ifdef VBOX_WITH_MSI_DEVICES
[73135]580 PDMMSIREG MsiReg;
581 RT_ZERO(MsiReg);
582 MsiReg.cMsiVectors = 1;
583 MsiReg.iMsiCapOffset = OX958_PCI_MSI_CAP_OFS;
584 MsiReg.iMsiNextOffset = OX958_PCI_MSIX_CAP_OFS;
585 MsiReg.fMsi64bit = true;
586 if (fMsiXSupported)
587 {
588 MsiReg.cMsixVectors = VBOX_MSIX_MAX_ENTRIES;
589 MsiReg.iMsixCapOffset = OX958_PCI_MSIX_CAP_OFS;
590 MsiReg.iMsixNextOffset = 0x00;
591 MsiReg.iMsixBar = OX958_PCI_MSIX_BAR;
592 }
593 rc = PDMDevHlpPCIRegisterMsi(pDevIns, &MsiReg);
594 if (RT_FAILURE(rc))
595 {
[81031]596 PDMPciDevSetCapabilityList(pPciDev, 0x0);
[73135]597 /* That's OK, we can work without MSI */
598 }
[81891]599# endif
[73135]600
[81891]601 rc = PDMDevHlpPCIIORegionCreateMmio(pDevIns, 0 /*iPciRegion*/, _16K, PCI_ADDRESS_SPACE_MEM,
602 ox958MmioWrite, ox958MmioRead, NULL /*pvUser*/,
603 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
604 "OxPCIe958", &pThis->hMmio);
605 AssertRCReturn(rc, rc);
[73135]606
607
[81893]608 /*
609 * Initialize the UARTs.
610 */
[73135]611 for (uint32_t i = 0; i < pThis->cUarts; i++)
612 {
[81893]613 POX958UART pUart = &pThis->aUarts[i];
614 POX958UARTCC pUartCC = &pThisCC->aUarts[i];
615 rc = uartR3Init(pDevIns, &pUart->UartCore, &pUartCC->UartCore, UARTTYPE_16550A, i, 0, ox958IrqReq);
[73135]616 if (RT_FAILURE(rc))
617 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
618 N_("OXPCIe958 configuration error: failed to initialize UART %u"), i);
619 }
620
621 ox958R3Reset(pDevIns);
622 return VINF_SUCCESS;
623}
624
[81891]625#else /* !IN_RING3 */
[73135]626
[81891]627/**
628 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
629 */
630static DECLCALLBACK(int) ox958RZConstruct(PPDMDEVINS pDevIns)
631{
632 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
[81893]633 PDEVOX958 pThis = PDMDEVINS_2_DATA(pDevIns, PDEVOX958);
634 PDEVOX958CC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVOX958CC);
[80531]635
[81891]636 int rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
637 AssertRCReturn(rc, rc);
638
639 rc = PDMDevHlpMmioSetUpContext(pDevIns, pThis->hMmio, ox958MmioWrite, ox958MmioRead, NULL /*pvUser*/);
640 AssertRCReturn(rc, rc);
641
[81893]642 uint32_t const cUarts = RT_MIN(pThis->cUarts, RT_ELEMENTS(pThis->aUarts));
643 for (uint32_t i = 0; i < cUarts; i++)
644 {
645 POX958UARTCC pUartCC = &pThisCC->aUarts[i];
646 rc = uartRZInit(&pUartCC->UartCore, ox958IrqReq);
647 AssertRCReturn(rc, rc);
648 }
649
[81891]650 return VINF_SUCCESS;
651}
652
653#endif /* !IN_RING3 */
654
655
[73135]656const PDMDEVREG g_DeviceOxPcie958 =
657{
[80531]658 /* .u32version = */ PDM_DEVREG_VERSION,
659 /* .uReserved0 = */ 0,
660 /* .szName = */ "oxpcie958uart",
[81893]661 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
[80531]662 /* .fClass = */ PDM_DEVREG_CLASS_SERIAL,
663 /* .cMaxInstances = */ ~0U,
664 /* .uSharedVersion = */ 42,
665 /* .cbInstanceShared = */ sizeof(DEVOX958),
[81893]666 /* .cbInstanceCC = */ sizeof(DEVOX958CC),
667 /* .cbInstanceRC = */ sizeof(DEVOX958RC),
[80701]668 /* .cMaxPciDevices = */ 1,
[80704]669 /* .cMaxMsixVectors = */ VBOX_MSIX_MAX_ENTRIES,
[80531]670 /* .pszDescription = */ "OXPCIe958 based UART controller.\n",
671#if defined(IN_RING3)
672 /* .pszRCMod = */ "VBoxDDRC.rc",
673 /* .pszR0Mod = */ "VBoxDDR0.r0",
674 /* .pfnConstruct = */ ox958R3Construct,
675 /* .pfnDestruct = */ ox958R3Destruct,
[81893]676 /* .pfnRelocate = */ NULL,
[80531]677 /* .pfnMemSetup = */ NULL,
678 /* .pfnPowerOn = */ NULL,
679 /* .pfnReset = */ ox958R3Reset,
680 /* .pfnSuspend = */ NULL,
681 /* .pfnResume = */ NULL,
682 /* .pfnAttach = */ ox958R3Attach,
683 /* .pfnDetach = */ ox958R3Detach,
684 /* .pfnQueryInterface = */ NULL,
685 /* .pfnInitComplete = */ NULL,
686 /* .pfnPowerOff = */ NULL,
687 /* .pfnSoftReset = */ NULL,
688 /* .pfnReserved0 = */ NULL,
689 /* .pfnReserved1 = */ NULL,
690 /* .pfnReserved2 = */ NULL,
691 /* .pfnReserved3 = */ NULL,
692 /* .pfnReserved4 = */ NULL,
693 /* .pfnReserved5 = */ NULL,
694 /* .pfnReserved6 = */ NULL,
695 /* .pfnReserved7 = */ NULL,
696#elif defined(IN_RING0)
697 /* .pfnEarlyConstruct = */ NULL,
[81891]698 /* .pfnConstruct = */ ox958RZConstruct,
[80531]699 /* .pfnDestruct = */ NULL,
700 /* .pfnFinalDestruct = */ NULL,
701 /* .pfnRequest = */ NULL,
702 /* .pfnReserved0 = */ NULL,
703 /* .pfnReserved1 = */ NULL,
704 /* .pfnReserved2 = */ NULL,
705 /* .pfnReserved3 = */ NULL,
706 /* .pfnReserved4 = */ NULL,
707 /* .pfnReserved5 = */ NULL,
708 /* .pfnReserved6 = */ NULL,
709 /* .pfnReserved7 = */ NULL,
710#elif defined(IN_RC)
[81891]711 /* .pfnConstruct = */ ox958RZConstruct,
[80531]712 /* .pfnReserved0 = */ NULL,
713 /* .pfnReserved1 = */ NULL,
714 /* .pfnReserved2 = */ NULL,
715 /* .pfnReserved3 = */ NULL,
716 /* .pfnReserved4 = */ NULL,
717 /* .pfnReserved5 = */ NULL,
718 /* .pfnReserved6 = */ NULL,
719 /* .pfnReserved7 = */ NULL,
720#else
721# error "Not in IN_RING3, IN_RING0 or IN_RC!"
722#endif
723 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
[73135]724};
725
726#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
727
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use