Message ID | e2e536b4b3d6ef417efbc399842b58aa420c1e3f.1658804819.git-series.marmarek@invisiblethingslab.com (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | Add Xue - console over USB 3 Debug Capability | expand |
On 26.07.2022 05:23, Marek Marczykowski-Górecki wrote: > +/* Defines the size in bytes of TRB rings as 2^DBC_TRB_RING_ORDER * 4096 */ > +#ifndef DBC_TRB_RING_ORDER > +#define DBC_TRB_RING_ORDER 4 > +#endif > +#define DBC_TRB_RING_CAP (DBC_TRB_PER_PAGE * (1 << DBC_TRB_RING_ORDER)) I have to admit that I'm always puzzled when seeing such - why not #define DBC_TRB_RING_CAP (DBC_TRB_PER_PAGE << DBC_TRB_RING_ORDER) ? > +struct dbc { > + struct dbc_reg __iomem *dbc_reg; > + struct xhci_dbc_ctx *dbc_ctx; > + struct xhci_erst_segment *dbc_erst; > + struct xhci_trb_ring dbc_ering; > + struct xhci_trb_ring dbc_oring; > + struct xhci_trb_ring dbc_iring; > + struct dbc_work_ring dbc_owork; > + struct xhci_string_descriptor *dbc_str; I'm afraid I still don't see why the static page this field is being initialized with is necessary. Can't you have a static variable (of some struct type) all pre-filled at build time, which you then apply virt_to_maddr() to in order to fill the respective dbc_ctx fields? That struct will be quite a bit less than a page's worth in size. If you build the file with -fshort-wchar, you may even be able to use easy to read string literals for the initializer. > +static void *dbc_sys_map_xhc(uint64_t phys, size_t size) > +{ > + size_t i; > + > + if ( size != MAX_XHCI_PAGES * DBC_PAGE_SIZE ) > + return NULL; > + > + for ( i = FIX_XHCI_END; i >= FIX_XHCI_BEGIN; i-- ) > + { > + set_fixmap_nocache(i, phys); > + phys += DBC_PAGE_SIZE; While there may be an assumption of DBC_PAGE_SIZE == PAGE_SIZE, the constant used here clearly needs to be PAGE_SIZE, as that's the unit set_fixmap_nocache() deals with. > +static bool __init dbc_init_xhc(struct dbc *dbc) > +{ > + uint32_t bar0; > + uint64_t bar1; > + uint64_t bar_size; > + uint64_t devfn; > + uint16_t cmd; > + size_t xhc_mmio_size; > + > + /* > + * Search PCI bus 0 for the xHC. All the host controllers supported so far > + * are part of the chipset and are on bus 0. > + */ > + for ( devfn = 0; devfn < 256; devfn++ ) > + { > + pci_sbdf_t sbdf = PCI_SBDF(0, 0, devfn); > + uint8_t hdr = pci_conf_read8(sbdf, PCI_HEADER_TYPE); > + > + if ( hdr == 0 || hdr == 0x80 ) > + { > + if ( (pci_conf_read32(sbdf, PCI_CLASS_REVISION) >> 8) == DBC_XHC_CLASSC ) > + { > + dbc->sbdf = sbdf; > + break; > + } > + } > + } > + > + if ( !dbc->sbdf.sbdf ) > + { > + dbc_error("Compatible xHC not found on bus 0\n"); > + return false; > + } > + > + /* ...we found it, so parse the BAR and map the registers */ > + bar0 = pci_conf_read32(dbc->sbdf, PCI_BASE_ADDRESS_0); > + bar1 = pci_conf_read32(dbc->sbdf, PCI_BASE_ADDRESS_1); > + > + /* IO BARs not allowed; BAR must be 64-bit */ > + if ( (bar0 & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY || > + (bar0 & PCI_BASE_ADDRESS_MEM_TYPE_MASK) != PCI_BASE_ADDRESS_MEM_TYPE_64 ) > + return false; > + > + cmd = pci_conf_read16(dbc->sbdf, PCI_COMMAND); > + pci_conf_write16(dbc->sbdf, PCI_COMMAND, cmd & ~PCI_COMMAND_MEMORY); > + > + pci_conf_write32(dbc->sbdf, PCI_BASE_ADDRESS_0, 0xFFFFFFFF); > + pci_conf_write32(dbc->sbdf, PCI_BASE_ADDRESS_1, 0xFFFFFFFF); > + bar_size = pci_conf_read32(dbc->sbdf, PCI_BASE_ADDRESS_0); > + bar_size |= (uint64_t)pci_conf_read32(dbc->sbdf, PCI_BASE_ADDRESS_1) << 32; > + xhc_mmio_size = ~(bar_size & PCI_BASE_ADDRESS_MEM_MASK) + 1; > + pci_conf_write32(dbc->sbdf, PCI_BASE_ADDRESS_0, bar0); > + pci_conf_write32(dbc->sbdf, PCI_BASE_ADDRESS_1, bar1); > + > + pci_conf_write16(dbc->sbdf, PCI_COMMAND, cmd); > + > + dbc->xhc_mmio_phys = (bar0 & PCI_BASE_ADDRESS_MEM_MASK) | (bar1 << 32); > + dbc->xhc_mmio = dbc_sys_map_xhc(dbc->xhc_mmio_phys, xhc_mmio_size); Before actually using the address to map the MMIO you will want to make somewhat sure firmware did initialize it: The EHCI driver checks for all zeroes or all ones in the writable bits. > +/** > + * The first register of the debug capability is found by traversing the > + * host controller's capability list (xcap) until a capability > + * with ID = 0xA is found. The xHCI capability list begins at address > + * mmio + (HCCPARAMS1[31:16] << 2). > + */ > +static struct dbc_reg __iomem *xhci_find_dbc(struct dbc *dbc) > +{ > + uint32_t *xcap; const? > + uint32_t xcap_val; > + uint32_t next; > + uint32_t id = 0; > + uint8_t *mmio = (uint8_t *)dbc->xhc_mmio; Can't this be const void * (and probably wants to also use __iomem), avoiding the cast here, ... > + uint32_t *hccp1 = (uint32_t *)(mmio + 0x10); ... here, ... > + const uint32_t DBC_ID = 0xA; > + int ttl = 48; > + > + xcap = (uint32_t *)dbc->xhc_mmio; ... and here (if actually using the local variable you've got). > +/* > + * Note that if IN transfer support is added, then this > + * will need to be changed; it assumes an OUT transfer ring only > + */ Hmm, is this comment telling me that this driver is an output-only one? Or is it simply that input doesn't use this code path? > +static void dbc_init_string_single(struct xhci_string_descriptor *string, > + char *ascii_str, If this function has to survive, then const please here and ... > + uint64_t *str_ptr, > + uint8_t *str_size_ptr) > +{ > + size_t i, len = strlen(ascii_str); > + > + string->size = offsetof(typeof(*string), string) + len * 2; > + string->type = XHCI_DT_STRING; > + /* ASCII to UTF16 conversion */ > + for (i = 0; i < len; i++) ... this missing blanks added here. > +static struct xhci_trb evt_trb[DBC_TRB_RING_CAP]; > +static struct xhci_trb out_trb[DBC_TRB_RING_CAP]; > +static struct xhci_trb in_trb[DBC_TRB_RING_CAP]; > +static struct xhci_erst_segment erst __aligned(64); > +static struct xhci_dbc_ctx ctx __aligned(64); > +static uint8_t out_wrk_buf[DBC_WORK_RING_CAP] __aligned(DBC_PAGE_SIZE); > +static struct xhci_string_descriptor str_buf[DBC_STRINGS_COUNT]; > +static char __initdata opt_dbgp[30]; > + > +string_param("dbgp", opt_dbgp); This duplicates what ehci-dbgp.c already has. I guess we can live with it for now and de-duplicate later, but it's still a little odd. In any even please move the blank line up be a line, so that string_param() and its referenced array are kept together. > +void __init xhci_dbc_uart_init(void) > +{ > + struct dbc_uart *uart = &dbc_uart; > + struct dbc *dbc = &uart->dbc; > + > + if ( strncmp(opt_dbgp, "xhci", 4) ) > + return; > + > + memset(dbc, 0, sizeof(*dbc)); Why? dbc_uart is a static variable, and hence already zero-initialized. Jan
On Thu, Aug 04, 2022 at 02:57:49PM +0200, Jan Beulich wrote: > On 26.07.2022 05:23, Marek Marczykowski-Górecki wrote: > > +/* Defines the size in bytes of TRB rings as 2^DBC_TRB_RING_ORDER * 4096 */ > > +#ifndef DBC_TRB_RING_ORDER > > +#define DBC_TRB_RING_ORDER 4 > > +#endif > > +#define DBC_TRB_RING_CAP (DBC_TRB_PER_PAGE * (1 << DBC_TRB_RING_ORDER)) > > I have to admit that I'm always puzzled when seeing such - why not > > #define DBC_TRB_RING_CAP (DBC_TRB_PER_PAGE << DBC_TRB_RING_ORDER) > > ? I think the former is a bit more clear what's the actual size, but the end result is the same, I can change that. > > +struct dbc { > > + struct dbc_reg __iomem *dbc_reg; > > + struct xhci_dbc_ctx *dbc_ctx; > > + struct xhci_erst_segment *dbc_erst; > > + struct xhci_trb_ring dbc_ering; > > + struct xhci_trb_ring dbc_oring; > > + struct xhci_trb_ring dbc_iring; > > + struct dbc_work_ring dbc_owork; > > + struct xhci_string_descriptor *dbc_str; > > I'm afraid I still don't see why the static page this field is being > initialized with is necessary. Can't you have a static variable (of > some struct type) all pre-filled at build time, which you then apply > virt_to_maddr() to in order to fill the respective dbc_ctx fields? I need to keep this structure somewhere DMA-reachable for the device (as in - included in appropriate IOMMU context). Patch 8/10 is doing it. And also, patch 8/10 is putting it together with other DMA-reachable structures (not a separate page on its own). If I'd make it a separate static variable (not part of that later struct), I'd need to reserve the whole page for it - to guarantee no unrelated data lives on the same (DMA-reachable) page. As for statically initializing it, if would require the whole (multi-page DMA-reachable) thing living in .data, not .bss, so a bigger binary (not a huge concern due to compression, but still). But more importantly, I don't know how to do it in a readable way, and you have complained about readability of initializer of this structure in v2. > That struct will be quite a bit less than a page's worth in size. See above - it cannot share page with unrelated Xen data. > If you build the file with -fshort-wchar, you may even be able to > use easy to read string literals for the initializer. I can try, but I'm not exactly sure how to make readable UTF-16 literals... > > +static void *dbc_sys_map_xhc(uint64_t phys, size_t size) > > +{ > > + size_t i; > > + > > + if ( size != MAX_XHCI_PAGES * DBC_PAGE_SIZE ) > > + return NULL; > > + > > + for ( i = FIX_XHCI_END; i >= FIX_XHCI_BEGIN; i-- ) > > + { > > + set_fixmap_nocache(i, phys); > > + phys += DBC_PAGE_SIZE; > > While there may be an assumption of DBC_PAGE_SIZE == PAGE_SIZE, the > constant used here clearly needs to be PAGE_SIZE, as that's the unit > set_fixmap_nocache() deals with. Ok. > > +static bool __init dbc_init_xhc(struct dbc *dbc) > > +{ > > + uint32_t bar0; > > + uint64_t bar1; > > + uint64_t bar_size; > > + uint64_t devfn; > > + uint16_t cmd; > > + size_t xhc_mmio_size; > > + > > + /* > > + * Search PCI bus 0 for the xHC. All the host controllers supported so far > > + * are part of the chipset and are on bus 0. > > + */ > > + for ( devfn = 0; devfn < 256; devfn++ ) > > + { > > + pci_sbdf_t sbdf = PCI_SBDF(0, 0, devfn); > > + uint8_t hdr = pci_conf_read8(sbdf, PCI_HEADER_TYPE); > > + > > + if ( hdr == 0 || hdr == 0x80 ) > > + { > > + if ( (pci_conf_read32(sbdf, PCI_CLASS_REVISION) >> 8) == DBC_XHC_CLASSC ) > > + { > > + dbc->sbdf = sbdf; > > + break; > > + } > > + } > > + } > > + > > + if ( !dbc->sbdf.sbdf ) > > + { > > + dbc_error("Compatible xHC not found on bus 0\n"); > > + return false; > > + } > > + > > + /* ...we found it, so parse the BAR and map the registers */ > > + bar0 = pci_conf_read32(dbc->sbdf, PCI_BASE_ADDRESS_0); > > + bar1 = pci_conf_read32(dbc->sbdf, PCI_BASE_ADDRESS_1); > > + > > + /* IO BARs not allowed; BAR must be 64-bit */ > > + if ( (bar0 & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY || > > + (bar0 & PCI_BASE_ADDRESS_MEM_TYPE_MASK) != PCI_BASE_ADDRESS_MEM_TYPE_64 ) > > + return false; > > + > > + cmd = pci_conf_read16(dbc->sbdf, PCI_COMMAND); > > + pci_conf_write16(dbc->sbdf, PCI_COMMAND, cmd & ~PCI_COMMAND_MEMORY); > > + > > + pci_conf_write32(dbc->sbdf, PCI_BASE_ADDRESS_0, 0xFFFFFFFF); > > + pci_conf_write32(dbc->sbdf, PCI_BASE_ADDRESS_1, 0xFFFFFFFF); > > + bar_size = pci_conf_read32(dbc->sbdf, PCI_BASE_ADDRESS_0); > > + bar_size |= (uint64_t)pci_conf_read32(dbc->sbdf, PCI_BASE_ADDRESS_1) << 32; > > + xhc_mmio_size = ~(bar_size & PCI_BASE_ADDRESS_MEM_MASK) + 1; > > + pci_conf_write32(dbc->sbdf, PCI_BASE_ADDRESS_0, bar0); > > + pci_conf_write32(dbc->sbdf, PCI_BASE_ADDRESS_1, bar1); > > + > > + pci_conf_write16(dbc->sbdf, PCI_COMMAND, cmd); > > + > > + dbc->xhc_mmio_phys = (bar0 & PCI_BASE_ADDRESS_MEM_MASK) | (bar1 << 32); > > + dbc->xhc_mmio = dbc_sys_map_xhc(dbc->xhc_mmio_phys, xhc_mmio_size); > > Before actually using the address to map the MMIO you will want to make > somewhat sure firmware did initialize it: The EHCI driver checks for > all zeroes or all ones in the writable bits. Ok. > > > +/** > > + * The first register of the debug capability is found by traversing the > > + * host controller's capability list (xcap) until a capability > > + * with ID = 0xA is found. The xHCI capability list begins at address > > + * mmio + (HCCPARAMS1[31:16] << 2). > > + */ > > +static struct dbc_reg __iomem *xhci_find_dbc(struct dbc *dbc) > > +{ > > + uint32_t *xcap; > > const? > > > + uint32_t xcap_val; > > + uint32_t next; > > + uint32_t id = 0; > > + uint8_t *mmio = (uint8_t *)dbc->xhc_mmio; > > Can't this be const void * (and probably wants to also use __iomem), > avoiding the cast here, ... > > > + uint32_t *hccp1 = (uint32_t *)(mmio + 0x10); > > ... here, ... > > > + const uint32_t DBC_ID = 0xA; > > + int ttl = 48; > > + > > + xcap = (uint32_t *)dbc->xhc_mmio; > > ... and here (if actually using the local variable you've got). Ok. > > +/* > > + * Note that if IN transfer support is added, then this > > + * will need to be changed; it assumes an OUT transfer ring only > > + */ > > Hmm, is this comment telling me that this driver is an output-only one? At this point, yes. Input support is added in patch 10/10. > Or is it simply that input doesn't use this code path? > > > +static void dbc_init_string_single(struct xhci_string_descriptor *string, > > + char *ascii_str, > > If this function has to survive, then const please here and ... > > > + uint64_t *str_ptr, > > + uint8_t *str_size_ptr) > > +{ > > + size_t i, len = strlen(ascii_str); > > + > > + string->size = offsetof(typeof(*string), string) + len * 2; > > + string->type = XHCI_DT_STRING; > > + /* ASCII to UTF16 conversion */ > > + for (i = 0; i < len; i++) > > ... this missing blanks added here. Ok. > > +static struct xhci_trb evt_trb[DBC_TRB_RING_CAP]; > > +static struct xhci_trb out_trb[DBC_TRB_RING_CAP]; > > +static struct xhci_trb in_trb[DBC_TRB_RING_CAP]; > > +static struct xhci_erst_segment erst __aligned(64); > > +static struct xhci_dbc_ctx ctx __aligned(64); > > +static uint8_t out_wrk_buf[DBC_WORK_RING_CAP] __aligned(DBC_PAGE_SIZE); > > +static struct xhci_string_descriptor str_buf[DBC_STRINGS_COUNT]; > > +static char __initdata opt_dbgp[30]; > > + > > +string_param("dbgp", opt_dbgp); > > This duplicates what ehci-dbgp.c already has. I guess we can live with > it for now and de-duplicate later, but it's still a little odd. In any > even please move the blank line up be a line, so that string_param() > and its referenced array are kept together. > > > +void __init xhci_dbc_uart_init(void) > > +{ > > + struct dbc_uart *uart = &dbc_uart; > > + struct dbc *dbc = &uart->dbc; > > + > > + if ( strncmp(opt_dbgp, "xhci", 4) ) > > + return; > > + > > + memset(dbc, 0, sizeof(*dbc)); > > Why? dbc_uart is a static variable, and hence already zero-initialized. Ok.
On 04.08.2022 15:43, Marek Marczykowski-Górecki wrote: > On Thu, Aug 04, 2022 at 02:57:49PM +0200, Jan Beulich wrote: >> On 26.07.2022 05:23, Marek Marczykowski-Górecki wrote: >>> +struct dbc { >>> + struct dbc_reg __iomem *dbc_reg; >>> + struct xhci_dbc_ctx *dbc_ctx; >>> + struct xhci_erst_segment *dbc_erst; >>> + struct xhci_trb_ring dbc_ering; >>> + struct xhci_trb_ring dbc_oring; >>> + struct xhci_trb_ring dbc_iring; >>> + struct dbc_work_ring dbc_owork; >>> + struct xhci_string_descriptor *dbc_str; >> >> I'm afraid I still don't see why the static page this field is being >> initialized with is necessary. Can't you have a static variable (of >> some struct type) all pre-filled at build time, which you then apply >> virt_to_maddr() to in order to fill the respective dbc_ctx fields? > > I need to keep this structure somewhere DMA-reachable for the device (as > in - included in appropriate IOMMU context). Patch 8/10 is doing it. And > also, patch 8/10 is putting it together with other DMA-reachable > structures (not a separate page on its own). If I'd make it a separate > static variable (not part of that later struct), I'd need to reserve the > whole page for it - to guarantee no unrelated data lives on the same > (DMA-reachable) page. > > As for statically initializing it, if would require the whole > (multi-page DMA-reachable) thing living in .data, not .bss, so a bigger > binary (not a huge concern due to compression, but still). But more > importantly, I don't know how to do it in a readable way, and you have > complained about readability of initializer of this structure in v2. > >> That struct will be quite a bit less than a page's worth in size. > > See above - it cannot share page with unrelated Xen data. I have to admit that I'd see no issue if these lived side by side with e.g. other string literals. The more that the device is supposed to be exposed to Dom0 only anyway, and hence that'll be the only domain able to get at that data. >> If you build the file with -fshort-wchar, you may even be able to >> use easy to read string literals for the initializer. > > I can try, but I'm not exactly sure how to make readable UTF-16 > literals... L"Xen" looks sufficiently readable to me. We use this all over the place in the EFI interfacing code. Jan
On Thu, Aug 04, 2022 at 04:21:01PM +0200, Jan Beulich wrote: > On 04.08.2022 15:43, Marek Marczykowski-Górecki wrote: > > I need to keep this structure somewhere DMA-reachable for the device (as > > in - included in appropriate IOMMU context). Patch 8/10 is doing it. And > > also, patch 8/10 is putting it together with other DMA-reachable > > structures (not a separate page on its own). If I'd make it a separate > > static variable (not part of that later struct), I'd need to reserve the > > whole page for it - to guarantee no unrelated data lives on the same > > (DMA-reachable) page. > > > > As for statically initializing it, if would require the whole > > (multi-page DMA-reachable) thing living in .data, not .bss, so a bigger > > binary (not a huge concern due to compression, but still). But more > > importantly, I don't know how to do it in a readable way, and you have > > complained about readability of initializer of this structure in v2. > > > >> That struct will be quite a bit less than a page's worth in size. > > > > See above - it cannot share page with unrelated Xen data. > > I have to admit that I'd see no issue if these lived side by side with > e.g. other string literals. The more that the device is supposed to be > exposed to Dom0 only anyway, and hence that'll be the only domain able > to get at that data. Other string literals are fine. But for example `struct dbc` itself is not. See how it is combined with other data in patch 8. > >> If you build the file with -fshort-wchar, you may even be able to > >> use easy to read string literals for the initializer. > > > > I can try, but I'm not exactly sure how to make readable UTF-16 > > literals... > > L"Xen" looks sufficiently readable to me. We use this all over the > place in the EFI interfacing code. Ok, I can try that. But given later adjustments, IIUC it will make the whole 50+ pages structure land in .data. Is that okay?
On 04.08.2022 16:21, Jan Beulich wrote: > On 04.08.2022 15:43, Marek Marczykowski-Górecki wrote: >> On Thu, Aug 04, 2022 at 02:57:49PM +0200, Jan Beulich wrote: >>> On 26.07.2022 05:23, Marek Marczykowski-Górecki wrote: >>>> +struct dbc { >>>> + struct dbc_reg __iomem *dbc_reg; >>>> + struct xhci_dbc_ctx *dbc_ctx; >>>> + struct xhci_erst_segment *dbc_erst; >>>> + struct xhci_trb_ring dbc_ering; >>>> + struct xhci_trb_ring dbc_oring; >>>> + struct xhci_trb_ring dbc_iring; >>>> + struct dbc_work_ring dbc_owork; >>>> + struct xhci_string_descriptor *dbc_str; >>> >>> I'm afraid I still don't see why the static page this field is being >>> initialized with is necessary. Can't you have a static variable (of >>> some struct type) all pre-filled at build time, which you then apply >>> virt_to_maddr() to in order to fill the respective dbc_ctx fields? >> >> I need to keep this structure somewhere DMA-reachable for the device (as >> in - included in appropriate IOMMU context). Patch 8/10 is doing it. And >> also, patch 8/10 is putting it together with other DMA-reachable >> structures (not a separate page on its own). If I'd make it a separate >> static variable (not part of that later struct), I'd need to reserve the >> whole page for it - to guarantee no unrelated data lives on the same >> (DMA-reachable) page. >> >> As for statically initializing it, if would require the whole >> (multi-page DMA-reachable) thing living in .data, not .bss, so a bigger >> binary (not a huge concern due to compression, but still). But more >> importantly, I don't know how to do it in a readable way, and you have >> complained about readability of initializer of this structure in v2. >> >>> That struct will be quite a bit less than a page's worth in size. >> >> See above - it cannot share page with unrelated Xen data. > > I have to admit that I'd see no issue if these lived side by side with > e.g. other string literals. The more that the device is supposed to be > exposed to Dom0 only anyway, and hence that'll be the only domain able > to get at that data. Actually: With your plan to expose the device to a DomU, how is that going to work without tool stack adjustments? Wouldn't you need to prevent in particular HVM/PVH guests from using the GFNs corresponding to the MFNs where this Xen data is? The minimal requirement then would seem to be an E820 reserved range for the area. Jan
On 04.08.2022 16:28, Marek Marczykowski-Górecki wrote: > On Thu, Aug 04, 2022 at 04:21:01PM +0200, Jan Beulich wrote: >> On 04.08.2022 15:43, Marek Marczykowski-Górecki wrote: >>> I need to keep this structure somewhere DMA-reachable for the device (as >>> in - included in appropriate IOMMU context). Patch 8/10 is doing it. And >>> also, patch 8/10 is putting it together with other DMA-reachable >>> structures (not a separate page on its own). If I'd make it a separate >>> static variable (not part of that later struct), I'd need to reserve the >>> whole page for it - to guarantee no unrelated data lives on the same >>> (DMA-reachable) page. >>> >>> As for statically initializing it, if would require the whole >>> (multi-page DMA-reachable) thing living in .data, not .bss, so a bigger >>> binary (not a huge concern due to compression, but still). But more >>> importantly, I don't know how to do it in a readable way, and you have >>> complained about readability of initializer of this structure in v2. >>> >>>> That struct will be quite a bit less than a page's worth in size. >>> >>> See above - it cannot share page with unrelated Xen data. >> >> I have to admit that I'd see no issue if these lived side by side with >> e.g. other string literals. The more that the device is supposed to be >> exposed to Dom0 only anyway, and hence that'll be the only domain able >> to get at that data. > > Other string literals are fine. But for example `struct dbc` itself is > not. > See how it is combined with other data in patch 8. > >>>> If you build the file with -fshort-wchar, you may even be able to >>>> use easy to read string literals for the initializer. >>> >>> I can try, but I'm not exactly sure how to make readable UTF-16 >>> literals... >> >> L"Xen" looks sufficiently readable to me. We use this all over the >> place in the EFI interfacing code. > > Ok, I can try that. But given later adjustments, IIUC it will make the > whole 50+ pages structure land in .data. Is that okay? No. I was actually expecting the piece of data we're talking about here to land in .rodata, and hence be re-usable at the same address for all devices. Hence my reference to string literals. Jan
On Thu, Aug 04, 2022 at 04:36:35PM +0200, Jan Beulich wrote: > On 04.08.2022 16:28, Marek Marczykowski-Górecki wrote: > > On Thu, Aug 04, 2022 at 04:21:01PM +0200, Jan Beulich wrote: > >> L"Xen" looks sufficiently readable to me. We use this all over the > >> place in the EFI interfacing code. > > > > Ok, I can try that. But given later adjustments, IIUC it will make the > > whole 50+ pages structure land in .data. Is that okay? > > No. I was actually expecting the piece of data we're talking about here > to land in .rodata, and hence be re-usable at the same address for all > devices. Hence my reference to string literals. "all devices" - this driver supports only a single xhci debug console at a time. Anyway, as explained earlier, it would require reserving the whole page for it (there are no other xhci-related structures that can live in .rodata), which given your earlier comments about memory usage is probably worse.
On 04.08.2022 16:41, Marek Marczykowski-Górecki wrote: > On Thu, Aug 04, 2022 at 04:36:35PM +0200, Jan Beulich wrote: >> On 04.08.2022 16:28, Marek Marczykowski-Górecki wrote: >>> On Thu, Aug 04, 2022 at 04:21:01PM +0200, Jan Beulich wrote: >>>> L"Xen" looks sufficiently readable to me. We use this all over the >>>> place in the EFI interfacing code. >>> >>> Ok, I can try that. But given later adjustments, IIUC it will make the >>> whole 50+ pages structure land in .data. Is that okay? >> >> No. I was actually expecting the piece of data we're talking about here >> to land in .rodata, and hence be re-usable at the same address for all >> devices. Hence my reference to string literals. > > "all devices" - this driver supports only a single xhci debug console at > a time. Oh, sorry - I've got confused by your multiple consoles patch here. > Anyway, as explained earlier, it would require reserving the > whole page for it (there are no other xhci-related structures that can > live in .rodata), which given your earlier comments about memory usage > is probably worse. In your earlier reply you did say you'd see no issue with this sitting side by side with other string literals. Which is precisely how I would envision to avoid the need to reserve the entire page. But yes, if that's not feasible, then the current model of keeping the stuff in .bss is likely best. A remark in the description towards the purpose here and the further intentions might help, not the least to avoid me coming up with the same comment again for a future version of the series. Jan
On 04.08.2022 16:34, Jan Beulich wrote: > On 04.08.2022 16:21, Jan Beulich wrote: >> On 04.08.2022 15:43, Marek Marczykowski-Górecki wrote: >>> On Thu, Aug 04, 2022 at 02:57:49PM +0200, Jan Beulich wrote: >>>> On 26.07.2022 05:23, Marek Marczykowski-Górecki wrote: >>>>> +struct dbc { >>>>> + struct dbc_reg __iomem *dbc_reg; >>>>> + struct xhci_dbc_ctx *dbc_ctx; >>>>> + struct xhci_erst_segment *dbc_erst; >>>>> + struct xhci_trb_ring dbc_ering; >>>>> + struct xhci_trb_ring dbc_oring; >>>>> + struct xhci_trb_ring dbc_iring; >>>>> + struct dbc_work_ring dbc_owork; >>>>> + struct xhci_string_descriptor *dbc_str; >>>> >>>> I'm afraid I still don't see why the static page this field is being >>>> initialized with is necessary. Can't you have a static variable (of >>>> some struct type) all pre-filled at build time, which you then apply >>>> virt_to_maddr() to in order to fill the respective dbc_ctx fields? >>> >>> I need to keep this structure somewhere DMA-reachable for the device (as >>> in - included in appropriate IOMMU context). Patch 8/10 is doing it. And >>> also, patch 8/10 is putting it together with other DMA-reachable >>> structures (not a separate page on its own). If I'd make it a separate >>> static variable (not part of that later struct), I'd need to reserve the >>> whole page for it - to guarantee no unrelated data lives on the same >>> (DMA-reachable) page. >>> >>> As for statically initializing it, if would require the whole >>> (multi-page DMA-reachable) thing living in .data, not .bss, so a bigger >>> binary (not a huge concern due to compression, but still). But more >>> importantly, I don't know how to do it in a readable way, and you have >>> complained about readability of initializer of this structure in v2. >>> >>>> That struct will be quite a bit less than a page's worth in size. >>> >>> See above - it cannot share page with unrelated Xen data. >> >> I have to admit that I'd see no issue if these lived side by side with >> e.g. other string literals. The more that the device is supposed to be >> exposed to Dom0 only anyway, and hence that'll be the only domain able >> to get at that data. > > Actually: With your plan to expose the device to a DomU, how is that > going to work without tool stack adjustments? Wouldn't you need to > prevent in particular HVM/PVH guests from using the GFNs corresponding > to the MFNs where this Xen data is? The minimal requirement then would > seem to be an E820 reserved range for the area. I guess this was rubbish - by mimic-ing RMRRs or their AMD equivalents, the tool stack ought to be taking care of this already. Jan
On 26.07.2022 05:23, Marek Marczykowski-Górecki wrote: > +static uint64_t dbc_work_ring_size(const struct dbc_work_ring *ring) > +{ > + if ( ring->enq >= ring->deq ) > + return ring->enq - ring->deq; > + > + return DBC_WORK_RING_CAP - ring->deq + ring->enq; > +} Doesn't unsigned int suffice as a return type here? > +static int64_t dbc_push_work(struct dbc *dbc, struct dbc_work_ring *ring, > + const char *buf, unsigned int len) > +{ > + unsigned int i = 0; > + unsigned int end, start = ring->enq; > + > + while ( !dbc_work_ring_full(ring) && i < len ) > + { > + ring->buf[ring->enq] = buf[i++]; > + ring->enq = (ring->enq + 1) & (DBC_WORK_RING_CAP - 1); > + } > + > + end = ring->enq; > + > + if ( end > start ) > + cache_flush(&ring->buf[start], end - start); > + else if ( i > 0 ) > + { > + cache_flush(&ring->buf[start], DBC_WORK_RING_CAP - start); > + cache_flush(&ring->buf[0], end); > + } > + > + return i; > +} The function's return type is int64_t but the sole return statement hands back an unsigned int - what's the deal here? > +static struct xhci_trb evt_trb[DBC_TRB_RING_CAP]; > +static struct xhci_trb out_trb[DBC_TRB_RING_CAP]; > +static struct xhci_trb in_trb[DBC_TRB_RING_CAP]; > +static struct xhci_erst_segment erst __aligned(64); > +static struct xhci_dbc_ctx ctx __aligned(64); > +static uint8_t out_wrk_buf[DBC_WORK_RING_CAP] __aligned(DBC_PAGE_SIZE); I've been trying to identify the reason for the alignment here, compared to the other buffers which are no longer page-aligned. I haven't even been able to locate the place where the address of this buffer is actually written to hardware; all I could find was the respective virt_to_maddr(). Could you please point me at that? Jan
On Fri, Aug 05, 2022 at 09:23:32AM +0200, Jan Beulich wrote: > On 26.07.2022 05:23, Marek Marczykowski-Górecki wrote: > > +static uint64_t dbc_work_ring_size(const struct dbc_work_ring *ring) > > +{ > > + if ( ring->enq >= ring->deq ) > > + return ring->enq - ring->deq; > > + > > + return DBC_WORK_RING_CAP - ring->deq + ring->enq; > > +} > > Doesn't unsigned int suffice as a return type here? Yes, it does. > > +static int64_t dbc_push_work(struct dbc *dbc, struct dbc_work_ring *ring, > > + const char *buf, unsigned int len) > > +{ > > + unsigned int i = 0; > > + unsigned int end, start = ring->enq; > > + > > + while ( !dbc_work_ring_full(ring) && i < len ) > > + { > > + ring->buf[ring->enq] = buf[i++]; > > + ring->enq = (ring->enq + 1) & (DBC_WORK_RING_CAP - 1); > > + } > > + > > + end = ring->enq; > > + > > + if ( end > start ) > > + cache_flush(&ring->buf[start], end - start); > > + else if ( i > 0 ) > > + { > > + cache_flush(&ring->buf[start], DBC_WORK_RING_CAP - start); > > + cache_flush(&ring->buf[0], end); > > + } > > + > > + return i; > > +} > > The function's return type is int64_t but the sole return statement > hands back an unsigned int - what's the deal here? And also, the only use for the return value is comparing to 0. So, yes, should be unsigned int. > > +static struct xhci_trb evt_trb[DBC_TRB_RING_CAP]; > > +static struct xhci_trb out_trb[DBC_TRB_RING_CAP]; > > +static struct xhci_trb in_trb[DBC_TRB_RING_CAP]; > > +static struct xhci_erst_segment erst __aligned(64); > > +static struct xhci_dbc_ctx ctx __aligned(64); > > +static uint8_t out_wrk_buf[DBC_WORK_RING_CAP] __aligned(DBC_PAGE_SIZE); > > I've been trying to identify the reason for the alignment here, > compared to the other buffers which are no longer page-aligned. I > haven't even been able to locate the place where the address of > this buffer is actually written to hardware; all I could find was > the respective virt_to_maddr(). Could you please point me at that? It's dbc_flush() -> dbc_push_trb(). And indeed, I think I can drop the alignment when it's moved into structure dedicated for DMA-accessible buffers.
On 05.08.2022 11:51, Marek Marczykowski-Górecki wrote: > On Fri, Aug 05, 2022 at 09:23:32AM +0200, Jan Beulich wrote: >> On 26.07.2022 05:23, Marek Marczykowski-Górecki wrote: >>> +static struct xhci_trb evt_trb[DBC_TRB_RING_CAP]; >>> +static struct xhci_trb out_trb[DBC_TRB_RING_CAP]; >>> +static struct xhci_trb in_trb[DBC_TRB_RING_CAP]; >>> +static struct xhci_erst_segment erst __aligned(64); >>> +static struct xhci_dbc_ctx ctx __aligned(64); >>> +static uint8_t out_wrk_buf[DBC_WORK_RING_CAP] __aligned(DBC_PAGE_SIZE); >> >> I've been trying to identify the reason for the alignment here, >> compared to the other buffers which are no longer page-aligned. I >> haven't even been able to locate the place where the address of >> this buffer is actually written to hardware; all I could find was >> the respective virt_to_maddr(). Could you please point me at that? > > It's dbc_flush() -> dbc_push_trb(). > And indeed, I think I can drop the alignment when it's moved into > structure dedicated for DMA-accessible buffers. Why would you be able to drop the alignment then, but not here? Jan
On Fri, Aug 05, 2022 at 11:54:06AM +0200, Jan Beulich wrote: > On 05.08.2022 11:51, Marek Marczykowski-Górecki wrote: > > On Fri, Aug 05, 2022 at 09:23:32AM +0200, Jan Beulich wrote: > >> On 26.07.2022 05:23, Marek Marczykowski-Górecki wrote: > >>> +static struct xhci_trb evt_trb[DBC_TRB_RING_CAP]; > >>> +static struct xhci_trb out_trb[DBC_TRB_RING_CAP]; > >>> +static struct xhci_trb in_trb[DBC_TRB_RING_CAP]; > >>> +static struct xhci_erst_segment erst __aligned(64); > >>> +static struct xhci_dbc_ctx ctx __aligned(64); > >>> +static uint8_t out_wrk_buf[DBC_WORK_RING_CAP] __aligned(DBC_PAGE_SIZE); > >> > >> I've been trying to identify the reason for the alignment here, > >> compared to the other buffers which are no longer page-aligned. I > >> haven't even been able to locate the place where the address of > >> this buffer is actually written to hardware; all I could find was > >> the respective virt_to_maddr(). Could you please point me at that? > > > > It's dbc_flush() -> dbc_push_trb(). > > And indeed, I think I can drop the alignment when it's moved into > > structure dedicated for DMA-accessible buffers. > > Why would you be able to drop the alignment then, but not here? Similar reason as previously - to guarantee it isn't put with unrelated stuff on the same page, as this array will be accessed by the controller directly. But, since this patch doesn't attempt to handle IOMMU situation yet, I think I can drop it here too as there is little benefit for trying here.
diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc index da18172e50c5..f936283cd187 100644 --- a/docs/misc/xen-command-line.pandoc +++ b/docs/misc/xen-command-line.pandoc @@ -721,10 +721,15 @@ Available alternatives, with their meaning, are: ### dbgp > `= ehci[ <integer> | @pci<bus>:<slot>.<func> ]` +> `= xhci` Specify the USB controller to use, either by instance number (when going over the PCI busses sequentially) or by PCI device (must be on segment 0). +Use `ehci` for EHCI debug port, use `xhci` for XHCI debug capability (output +only). XHCI driver will wait indefinitely for the debug host to connect - make +sure the cable is connected. + ### debug_stack_lines > `= <integer>` diff --git a/xen/arch/x86/include/asm/fixmap.h b/xen/arch/x86/include/asm/fixmap.h index 20746afd0a2a..bc39ffe896b1 100644 --- a/xen/arch/x86/include/asm/fixmap.h +++ b/xen/arch/x86/include/asm/fixmap.h @@ -25,6 +25,8 @@ #include <asm/msi.h> #include <acpi/apei.h> +#define MAX_XHCI_PAGES 16 + /* * Here we define all the compile-time 'special' virtual * addresses. The point is to have a constant address at @@ -43,6 +45,8 @@ enum fixed_addresses { FIX_COM_BEGIN, FIX_COM_END, FIX_EHCI_DBGP, + FIX_XHCI_BEGIN, + FIX_XHCI_END = FIX_XHCI_BEGIN + MAX_XHCI_PAGES - 1, #ifdef CONFIG_XEN_GUEST FIX_PV_CONSOLE, FIX_XEN_SHARED_INFO, diff --git a/xen/arch/x86/setup.c b/xen/arch/x86/setup.c index f08b07b8dea6..e05189f64997 100644 --- a/xen/arch/x86/setup.c +++ b/xen/arch/x86/setup.c @@ -950,6 +950,7 @@ void __init noreturn __start_xen(unsigned long mbi_p) ns16550.irq = 3; ns16550_init(1, &ns16550); ehci_dbgp_init(); + xhci_dbc_uart_init(); console_init_preirq(); if ( pvh_boot ) diff --git a/xen/drivers/char/Kconfig b/xen/drivers/char/Kconfig index dec58bc99360..06350c387371 100644 --- a/xen/drivers/char/Kconfig +++ b/xen/drivers/char/Kconfig @@ -84,3 +84,12 @@ config SERIAL_TX_BUFSIZE the nearest power of 2. Default value is 16384 (16kiB). + +config XHCI + bool "XHCI DbC UART driver" + depends on X86 + help + This selects the USB based XHCI debug capability to be usable as a UART. + Enabling this option makes Xen use extra ~230KiB memory, even if XHCI UART + is not selected. + If you have an x86 based system with USB3, say Y. diff --git a/xen/drivers/char/Makefile b/xen/drivers/char/Makefile index 14e67cf072d7..e7e374775d32 100644 --- a/xen/drivers/char/Makefile +++ b/xen/drivers/char/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_HAS_MVEBU) += mvebu-uart.o obj-$(CONFIG_HAS_OMAP) += omap-uart.o obj-$(CONFIG_HAS_SCIF) += scif-uart.o obj-$(CONFIG_HAS_EHCI) += ehci-dbgp.o +obj-$(CONFIG_XHCI) += xhci-dbc.o obj-$(CONFIG_HAS_IMX_LPUART) += imx-lpuart.o obj-$(CONFIG_ARM) += arm-uart.o obj-y += serial.o diff --git a/xen/drivers/char/xhci-dbc.c b/xen/drivers/char/xhci-dbc.c new file mode 100644 index 000000000000..f0e60d1b86aa --- /dev/null +++ b/xen/drivers/char/xhci-dbc.c @@ -0,0 +1,1024 @@ +/* + * drivers/char/xhci-dbc.c + * + * Xen port for the xue debugger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; If not, see <http://www.gnu.org/licenses/>. + * + * Copyright (c) 2019 Assured Information Security. + */ + +#include <xen/delay.h> +#include <xen/mm.h> +#include <xen/param.h> +#include <xen/serial.h> +#include <xen/timer.h> +#include <xen/types.h> +#include <asm/fixmap.h> +#include <asm/io.h> +#include <asm/string.h> +#include <asm/system.h> + +/* uncomment to have dbc_uart_dump() debug function */ +/* #define DBC_DEBUG 1 */ + +#define DBC_POLL_INTERVAL 100 /* us */ + +#define DBC_PAGE_SIZE 4096U + +/* Supported xHC PCI configurations */ +#define DBC_XHC_CLASSC 0xC0330U + +/* DbC idVendor and idProduct */ +#define DBC_DBC_VENDOR 0x1D6B +#define DBC_DBC_PRODUCT 0x0010 +#define DBC_DBC_PROTOCOL 0x0000 + +/* DCCTRL fields */ +#define DBC_CTRL_DCR 0 +#define DBC_CTRL_HOT 2 +#define DBC_CTRL_HIT 3 +#define DBC_CTRL_DRC 4 +#define DBC_CTRL_DCE 31 + +/* DCPORTSC fields */ +#define DBC_PSC_PED 1 +#define DBC_PSC_CSC 17 +#define DBC_PSC_PRC 21 +#define DBC_PSC_PLC 22 +#define DBC_PSC_CEC 23 + +#define DBC_PSC_ACK_MASK \ + ((1UL << DBC_PSC_CSC) | (1UL << DBC_PSC_PRC) | (1UL << DBC_PSC_PLC) | \ + (1UL << DBC_PSC_CEC)) + +#define dbc_debug(...) printk("dbc debug: " __VA_ARGS__) +#define dbc_alert(...) printk("dbc alert: " __VA_ARGS__) +#define dbc_error(...) printk("dbc error: " __VA_ARGS__) + +/****************************************************************************** + * TRB ring (summarized from the manual): + * + * TRB rings are circular queues of TRBs shared between the xHC and the driver. + * Each ring has one producer and one consumer. The DbC has one event + * ring and two transfer rings; one IN and one OUT. + * + * The DbC hardware is the producer on the event ring, and + * dbc driver is the consumer. This means that event TRBs are read-only from + * the dbc driver. + * + * OTOH, dbc drive is the producer of transfer TRBs on the two transfer + * rings, so dbc driver enqueues transfers, and the hardware dequeues + * them. The dequeue pointer of a transfer ring is read by + * dbc driver by examining the latest transfer event TRB on the event ring. The + * transfer event TRB contains the address of the transfer TRB that generated + * the event. + * + * To make each transfer ring circular, the last TRB must be a link TRB, which + * points to the beginning of the next queue. Note that this implementation + * does not support multiple segments, so each link TRB points back to the + * beginning of its own segment. + ******************************************************************************/ + +/* TRB types */ +enum { + XHCI_TRB_NORM = 1, + XHCI_TRB_LINK = 6, + XHCI_TRB_TFRE = 32, + XHCI_TRB_PSCE = 34 +}; + +/* TRB completion codes */ +enum { + XHCI_TRB_CC_SUCCESS = 1, + XHCI_TRB_CC_TRB_ERR = 5, +}; + +/* DbC endpoint types */ +enum { + XHCI_EP_BULK_OUT = 2, + XHCI_EP_BULK_IN = 6, +}; + +/* DMA/MMIO structures */ +struct xhci_trb { + uint64_t params; + uint32_t status; + uint32_t ctrl; +}; + +/* log2(sizeof(struct xhci_trb)) */ +#define XHCI_TRB_SHIFT 4 + +struct xhci_erst_segment { + uint64_t base; + uint16_t size; + uint8_t rsvdz[6]; +}; + +/* Arbitrary length, must fit every DBC_STRING_* */ +#define MAX_STRING_LENGTH 16 + +#define DBC_STRINGS_COUNT 4 +#define DBC_STRING_LANGID "\x09\x04" +#define DBC_STRING_MANUFACTURER "Xen" +#define DBC_STRING_PRODUCT "Debug console" +#define DBC_STRING_SERIAL "0" + +#define XHCI_DT_STRING 3 + +struct xhci_string_descriptor { + uint8_t size; + uint8_t type; + uint16_t string[MAX_STRING_LENGTH]; +}; + +#define DBC_CTX_SIZE 16 +#define DBC_CTX_BYTES (DBC_CTX_SIZE * 4) + +struct xhci_dbc_ctx { + union { + uint32_t info[DBC_CTX_SIZE]; + struct { + uint64_t string0_ptr; + uint64_t manufacturer_ptr; + uint64_t product_ptr; + uint64_t serial_ptr; + uint8_t string0_size; + uint8_t manufacturer_size; + uint8_t product_size; + uint8_t serial_size; + }; + }; + uint32_t ep_out[DBC_CTX_SIZE]; + uint32_t ep_in[DBC_CTX_SIZE]; +}; + +struct dbc_reg { + uint32_t id; + uint32_t db; + uint32_t erstsz; + uint32_t rsvdz; + uint64_t erstba; + uint64_t erdp; + uint32_t ctrl; + uint32_t st; + uint32_t portsc; + uint32_t rsvdp; + uint64_t cp; + uint32_t ddi1; + uint32_t ddi2; +}; + +#define DBC_TRB_MAX_TFR (DBC_PAGE_SIZE << 4) +#define DBC_TRB_PER_PAGE (DBC_PAGE_SIZE / sizeof(struct xhci_trb)) + +/* Defines the size in bytes of TRB rings as 2^DBC_TRB_RING_ORDER * 4096 */ +#ifndef DBC_TRB_RING_ORDER +#define DBC_TRB_RING_ORDER 4 +#endif +#define DBC_TRB_RING_CAP (DBC_TRB_PER_PAGE * (1 << DBC_TRB_RING_ORDER)) +#define DBC_TRB_RING_BYTES (DBC_TRB_RING_CAP * sizeof(struct xhci_trb)) +#define DBC_TRB_RING_MASK (DBC_TRB_RING_BYTES - 1U) + +struct xhci_trb_ring { + struct xhci_trb *trb; /* Array of TRBs */ + uint32_t enq; /* The offset of the enqueue ptr */ + uint32_t deq; /* The offset of the dequeue ptr */ + uint8_t cyc; /* Cycle state toggled on each wrap-around */ + uint8_t db; /* Doorbell target */ + uint64_t dma; /* Physical address (for the device) */ +}; + +#define DBC_DB_OUT 0x0 +#define DBC_DB_IN 0x1 +#define DBC_DB_INVAL 0xFF + +/* Defines the size in bytes of work rings as 2^DBC_WORK_RING_ORDER * 4096 */ +#ifndef DBC_WORK_RING_ORDER +#define DBC_WORK_RING_ORDER 3 +#endif +#define DBC_WORK_RING_CAP (DBC_PAGE_SIZE * (1 << DBC_WORK_RING_ORDER)) +#define DBC_WORK_RING_BYTES DBC_WORK_RING_CAP + +#if DBC_WORK_RING_CAP > DBC_TRB_MAX_TFR +#error "DBC_WORK_RING_ORDER must be at most 4" +#endif + +struct dbc_work_ring { + uint8_t *buf; + uint32_t enq; + uint32_t deq; + uint64_t dma; +}; + +struct dbc { + struct dbc_reg __iomem *dbc_reg; + struct xhci_dbc_ctx *dbc_ctx; + struct xhci_erst_segment *dbc_erst; + struct xhci_trb_ring dbc_ering; + struct xhci_trb_ring dbc_oring; + struct xhci_trb_ring dbc_iring; + struct dbc_work_ring dbc_owork; + struct xhci_string_descriptor *dbc_str; + + pci_sbdf_t sbdf; + uint64_t xhc_mmio_phys; + uint64_t xhc_dbc_offset; + void __iomem *xhc_mmio; + + bool open; +}; + +static void *dbc_sys_map_xhc(uint64_t phys, size_t size) +{ + size_t i; + + if ( size != MAX_XHCI_PAGES * DBC_PAGE_SIZE ) + return NULL; + + for ( i = FIX_XHCI_END; i >= FIX_XHCI_BEGIN; i-- ) + { + set_fixmap_nocache(i, phys); + phys += DBC_PAGE_SIZE; + } + + /* + * The fixmap grows downward, so the lowest virt is + * at the highest index + */ + return fix_to_virt(FIX_XHCI_END); +} + +static bool __init dbc_init_xhc(struct dbc *dbc) +{ + uint32_t bar0; + uint64_t bar1; + uint64_t bar_size; + uint64_t devfn; + uint16_t cmd; + size_t xhc_mmio_size; + + /* + * Search PCI bus 0 for the xHC. All the host controllers supported so far + * are part of the chipset and are on bus 0. + */ + for ( devfn = 0; devfn < 256; devfn++ ) + { + pci_sbdf_t sbdf = PCI_SBDF(0, 0, devfn); + uint8_t hdr = pci_conf_read8(sbdf, PCI_HEADER_TYPE); + + if ( hdr == 0 || hdr == 0x80 ) + { + if ( (pci_conf_read32(sbdf, PCI_CLASS_REVISION) >> 8) == DBC_XHC_CLASSC ) + { + dbc->sbdf = sbdf; + break; + } + } + } + + if ( !dbc->sbdf.sbdf ) + { + dbc_error("Compatible xHC not found on bus 0\n"); + return false; + } + + /* ...we found it, so parse the BAR and map the registers */ + bar0 = pci_conf_read32(dbc->sbdf, PCI_BASE_ADDRESS_0); + bar1 = pci_conf_read32(dbc->sbdf, PCI_BASE_ADDRESS_1); + + /* IO BARs not allowed; BAR must be 64-bit */ + if ( (bar0 & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY || + (bar0 & PCI_BASE_ADDRESS_MEM_TYPE_MASK) != PCI_BASE_ADDRESS_MEM_TYPE_64 ) + return false; + + cmd = pci_conf_read16(dbc->sbdf, PCI_COMMAND); + pci_conf_write16(dbc->sbdf, PCI_COMMAND, cmd & ~PCI_COMMAND_MEMORY); + + pci_conf_write32(dbc->sbdf, PCI_BASE_ADDRESS_0, 0xFFFFFFFF); + pci_conf_write32(dbc->sbdf, PCI_BASE_ADDRESS_1, 0xFFFFFFFF); + bar_size = pci_conf_read32(dbc->sbdf, PCI_BASE_ADDRESS_0); + bar_size |= (uint64_t)pci_conf_read32(dbc->sbdf, PCI_BASE_ADDRESS_1) << 32; + xhc_mmio_size = ~(bar_size & PCI_BASE_ADDRESS_MEM_MASK) + 1; + pci_conf_write32(dbc->sbdf, PCI_BASE_ADDRESS_0, bar0); + pci_conf_write32(dbc->sbdf, PCI_BASE_ADDRESS_1, bar1); + + pci_conf_write16(dbc->sbdf, PCI_COMMAND, cmd); + + dbc->xhc_mmio_phys = (bar0 & PCI_BASE_ADDRESS_MEM_MASK) | (bar1 << 32); + dbc->xhc_mmio = dbc_sys_map_xhc(dbc->xhc_mmio_phys, xhc_mmio_size); + + if ( dbc->xhc_mmio == NULL ) + return false; + + if ( (cmd & PCI_COMMAND_MEMORY) == 0 ) + pci_conf_write16(dbc->sbdf, PCI_COMMAND, cmd | PCI_COMMAND_MEMORY); + + return true; +} + +/** + * The first register of the debug capability is found by traversing the + * host controller's capability list (xcap) until a capability + * with ID = 0xA is found. The xHCI capability list begins at address + * mmio + (HCCPARAMS1[31:16] << 2). + */ +static struct dbc_reg __iomem *xhci_find_dbc(struct dbc *dbc) +{ + uint32_t *xcap; + uint32_t xcap_val; + uint32_t next; + uint32_t id = 0; + uint8_t *mmio = (uint8_t *)dbc->xhc_mmio; + uint32_t *hccp1 = (uint32_t *)(mmio + 0x10); + const uint32_t DBC_ID = 0xA; + int ttl = 48; + + xcap = (uint32_t *)dbc->xhc_mmio; + /* + * This is initially an offset to the first capability. All the offsets + * (both in HCCP1 and then next capability pointer are dword-based. + */ + next = (readl(hccp1) & 0xFFFF0000) >> 16; + + while ( id != DBC_ID && next && ttl-- ) + { + xcap += next; + xcap_val = readl(xcap); + id = xcap_val & 0xFF; + next = (xcap_val & 0xFF00) >> 8; + } + + if ( id != DBC_ID ) + return NULL; + + dbc->xhc_dbc_offset = (uint64_t)xcap - (uint64_t)mmio; + return (struct dbc_reg __iomem *)xcap; +} + +/** + * Fields with the same interpretation for every TRB type (section 4.11.1). + * These are the fields defined in the TRB template, minus the ENT bit. That + * bit is the toggle cycle bit in link TRBs, so it shouldn't be in the + * template. + */ +static uint32_t xhci_trb_cyc(const struct xhci_trb *trb) +{ + return trb->ctrl & 0x1; +} + +static uint32_t xhci_trb_type(const struct xhci_trb *trb) +{ + return (trb->ctrl & 0xFC00) >> 10; +} + +static void xhci_trb_set_cyc(struct xhci_trb *trb, uint32_t c) +{ + trb->ctrl &= ~0x1U; + trb->ctrl |= c; +} + +static void xhci_trb_set_type(struct xhci_trb *trb, uint32_t t) +{ + trb->ctrl &= ~0xFC00U; + trb->ctrl |= (t << 10); +} + +/* Fields for normal TRBs */ +static void xhci_trb_norm_set_buf(struct xhci_trb *trb, uint64_t addr) +{ + trb->params = addr; +} + +static void xhci_trb_norm_set_len(struct xhci_trb *trb, uint32_t len) +{ + trb->status &= ~0x1FFFFU; + trb->status |= len; +} + +static void xhci_trb_norm_set_ioc(struct xhci_trb *trb) +{ + trb->ctrl |= 0x20; +} + +/** + * Fields for Transfer Event TRBs (see section 6.4.2.1). Note that event + * TRBs are read-only from software + */ +static uint64_t xhci_trb_tfre_ptr(const struct xhci_trb *trb) +{ + return trb->params; +} + +static uint32_t xhci_trb_tfre_cc(const struct xhci_trb *trb) +{ + return trb->status >> 24; +} + +/* Fields for link TRBs (section 6.4.4.1) */ +static void xhci_trb_link_set_rsp(struct xhci_trb *trb, uint64_t rsp) +{ + trb->params = rsp; +} + +static void xhci_trb_link_set_tc(struct xhci_trb *trb) +{ + trb->ctrl |= 0x2; +} + +static void xhci_trb_ring_init(const struct dbc *dbc, + struct xhci_trb_ring *ring, int producer, + int doorbell) +{ + memset(ring->trb, 0, DBC_TRB_RING_CAP * sizeof(ring->trb[0])); + + ring->enq = 0; + ring->deq = 0; + ring->cyc = 1; + ring->db = (uint8_t)doorbell; + ring->dma = virt_to_maddr(ring->trb); + + /* + * Producer implies transfer ring, so we have to place a + * link TRB at the end that points back to trb[0] + */ + if ( producer ) + { + struct xhci_trb *trb = &ring->trb[DBC_TRB_RING_CAP - 1]; + xhci_trb_set_type(trb, XHCI_TRB_LINK); + xhci_trb_link_set_tc(trb); + xhci_trb_link_set_rsp(trb, virt_to_maddr(ring->trb)); + } +} + +static int xhci_trb_ring_full(const struct xhci_trb_ring *ring) +{ + return ((ring->enq + 1) & (DBC_TRB_RING_CAP - 1)) == ring->deq; +} + +static int dbc_work_ring_full(const struct dbc_work_ring *ring) +{ + return ((ring->enq + 1) & (DBC_WORK_RING_CAP - 1)) == ring->deq; +} + +static uint64_t dbc_work_ring_size(const struct dbc_work_ring *ring) +{ + if ( ring->enq >= ring->deq ) + return ring->enq - ring->deq; + + return DBC_WORK_RING_CAP - ring->deq + ring->enq; +} + +static void dbc_push_trb(struct dbc *dbc, struct xhci_trb_ring *ring, + uint64_t dma, uint64_t len) +{ + struct xhci_trb trb; + + if ( ring->enq == DBC_TRB_RING_CAP - 1 ) + { + /* + * We have to make sure the xHC processes the link TRB in order + * for wrap-around to work properly. We do this by marking the + * xHC as owner of the link TRB by setting the TRB's cycle bit + * (just like with normal TRBs). + */ + struct xhci_trb *link = &ring->trb[ring->enq]; + xhci_trb_set_cyc(link, ring->cyc); + + ring->enq = 0; + ring->cyc ^= 1; + } + + trb.params = 0; + trb.status = 0; + trb.ctrl = 0; + + xhci_trb_set_type(&trb, XHCI_TRB_NORM); + xhci_trb_set_cyc(&trb, ring->cyc); + + xhci_trb_norm_set_buf(&trb, dma); + xhci_trb_norm_set_len(&trb, (uint32_t)len); + xhci_trb_norm_set_ioc(&trb); + + ring->trb[ring->enq++] = trb; + cache_flush(&ring->trb[ring->enq - 1], sizeof(trb)); +} + +static int64_t dbc_push_work(struct dbc *dbc, struct dbc_work_ring *ring, + const char *buf, unsigned int len) +{ + unsigned int i = 0; + unsigned int end, start = ring->enq; + + while ( !dbc_work_ring_full(ring) && i < len ) + { + ring->buf[ring->enq] = buf[i++]; + ring->enq = (ring->enq + 1) & (DBC_WORK_RING_CAP - 1); + } + + end = ring->enq; + + if ( end > start ) + cache_flush(&ring->buf[start], end - start); + else if ( i > 0 ) + { + cache_flush(&ring->buf[start], DBC_WORK_RING_CAP - start); + cache_flush(&ring->buf[0], end); + } + + return i; +} + +/* + * Note that if IN transfer support is added, then this + * will need to be changed; it assumes an OUT transfer ring only + */ +static void dbc_pop_events(struct dbc *dbc) +{ + struct dbc_reg *reg = dbc->dbc_reg; + struct xhci_trb_ring *er = &dbc->dbc_ering; + struct xhci_trb_ring *tr = &dbc->dbc_oring; + struct xhci_trb *event = &er->trb[er->deq]; + uint64_t erdp = readq(®->erdp); + uint32_t portsc; + uint64_t event_ptr; + unsigned int trb_idx; + + BUILD_BUG_ON((1 << XHCI_TRB_SHIFT) != sizeof(struct xhci_trb)); + + rmb(); + + while ( xhci_trb_cyc(event) == er->cyc ) + { + switch (xhci_trb_type(event)) + { + case XHCI_TRB_TFRE: + event_ptr = xhci_trb_tfre_ptr(event); + /* + * trb_idx is just completed TRB, so set the dequeue ptr one + * position further. + */ + if ( event_ptr - tr->dma < DBC_TRB_RING_BYTES ) + { + trb_idx = (event_ptr - tr->dma) >> XHCI_TRB_SHIFT; + tr->deq = (trb_idx + 1) & (DBC_TRB_RING_CAP - 1); + } + else + dbc_alert("event: TRB 0x%lx not found in any ring\n", + event_ptr); + break; + case XHCI_TRB_PSCE: + portsc = readl(®->portsc); + portsc |= DBC_PSC_ACK_MASK & portsc; + writel(portsc, ®->portsc); + break; + default: + break; + } + + er->cyc = (er->deq == DBC_TRB_RING_CAP - 1) ? er->cyc ^ 1 : er->cyc; + er->deq = (er->deq + 1) & (DBC_TRB_RING_CAP - 1); + event = &er->trb[er->deq]; + } + + erdp = er->dma + (er->deq << XHCI_TRB_SHIFT); + wmb(); + writeq(erdp, ®->erdp); +} + +/** + * dbc_init_ep + * + * Initializes the endpoint as specified in sections 7.6.3.2 and 7.6.9.2. + * Each endpoint is Bulk, so the MaxPStreams, LSA, HID, CErr, FE, + * Interval, Mult, and Max ESIT Payload fields are all 0. + * + * Max packet size: 1024 + * Max burst size: debug mbs (from dbc_reg->ctrl register) + * EP type: 2 for OUT bulk, 6 for IN bulk + * TR dequeue ptr: physical base address of transfer ring + * Avg TRB length: software defined (see 4.14.1.1 for suggested defaults) + */ +static void dbc_init_ep(uint32_t *ep, uint64_t mbs, uint32_t type, + uint64_t ring_dma) +{ + memset(ep, 0, DBC_CTX_BYTES); + + ep[1] = (1024 << 16) | ((uint32_t)mbs << 8) | (type << 3); + ep[2] = (ring_dma & 0xFFFFFFFF) | 1; + ep[3] = ring_dma >> 32; + ep[4] = 3 * 1024; +} + +static void dbc_init_string_single(struct xhci_string_descriptor *string, + char *ascii_str, + uint64_t *str_ptr, + uint8_t *str_size_ptr) +{ + size_t i, len = strlen(ascii_str); + + string->size = offsetof(typeof(*string), string) + len * 2; + string->type = XHCI_DT_STRING; + /* ASCII to UTF16 conversion */ + for (i = 0; i < len; i++) + string->string[i] = ascii_str[i]; + *str_ptr = virt_to_maddr(string); + *str_size_ptr = string->size; +} + +/* Initialize the DbC info with USB string descriptor addresses */ +static void dbc_init_strings(struct dbc *dbc, uint32_t *info) +{ + BUILD_BUG_ON(sizeof(DBC_STRING_LANGID) > MAX_STRING_LENGTH); + BUILD_BUG_ON(sizeof(DBC_STRING_MANUFACTURER) > MAX_STRING_LENGTH); + BUILD_BUG_ON(sizeof(DBC_STRING_PRODUCT) > MAX_STRING_LENGTH); + BUILD_BUG_ON(sizeof(DBC_STRING_SERIAL) > MAX_STRING_LENGTH); + + dbc_init_string_single(&dbc->dbc_str[0], DBC_STRING_LANGID, + &dbc->dbc_ctx->string0_ptr, + &dbc->dbc_ctx->string0_size); + dbc_init_string_single(&dbc->dbc_str[1], DBC_STRING_MANUFACTURER, + &dbc->dbc_ctx->manufacturer_ptr, + &dbc->dbc_ctx->manufacturer_size); + dbc_init_string_single(&dbc->dbc_str[2], DBC_STRING_PRODUCT, + &dbc->dbc_ctx->product_ptr, + &dbc->dbc_ctx->product_size); + dbc_init_string_single(&dbc->dbc_str[3], DBC_STRING_SERIAL, + &dbc->dbc_ctx->serial_ptr, + &dbc->dbc_ctx->serial_size); +} + +static void dbc_enable_dbc(struct dbc *dbc) +{ + struct dbc_reg *reg = dbc->dbc_reg; + + wmb(); + writel(readl(®->ctrl) | (1U << DBC_CTRL_DCE), ®->ctrl); + wmb(); + + while ( (readl(®->ctrl) & (1U << DBC_CTRL_DCE)) == 0 ) + cpu_relax(); + + wmb(); + writel(readl(®->portsc) | (1U << DBC_PSC_PED), ®->portsc); + wmb(); + + while ( (readl(®->ctrl) & (1U << DBC_CTRL_DCR)) == 0 ) + cpu_relax(); +} + +static void dbc_disable_dbc(struct dbc *dbc) +{ + struct dbc_reg *reg = dbc->dbc_reg; + + writel(readl(®->portsc) & ~(1U << DBC_PSC_PED), ®->portsc); + wmb(); + writel(readl(®->ctrl) & ~(1U << DBC_CTRL_DCE), ®->ctrl); + + while ( readl(®->ctrl) & (1U << DBC_CTRL_DCE) ) + cpu_relax(); +} + +static int dbc_init_dbc(struct dbc *dbc) +{ + uint64_t erdp = 0; + uint64_t mbs = 0; + uint16_t cmd; + struct dbc_reg *reg = xhci_find_dbc(dbc); + + if ( !reg ) + return 0; + + dbc->dbc_reg = reg; + dbc_disable_dbc(dbc); + + xhci_trb_ring_init(dbc, &dbc->dbc_ering, 0, DBC_DB_INVAL); + xhci_trb_ring_init(dbc, &dbc->dbc_oring, 1, DBC_DB_OUT); + xhci_trb_ring_init(dbc, &dbc->dbc_iring, 1, DBC_DB_IN); + + erdp = virt_to_maddr(dbc->dbc_ering.trb); + if ( !erdp ) + return 0; + + memset(dbc->dbc_erst, 0, sizeof(*dbc->dbc_erst)); + dbc->dbc_erst->base = erdp; + dbc->dbc_erst->size = DBC_TRB_RING_CAP; + + mbs = (readl(®->ctrl) & 0xFF0000) >> 16; + + memset(dbc->dbc_ctx, 0, sizeof(*dbc->dbc_ctx)); + dbc_init_strings(dbc, dbc->dbc_ctx->info); + dbc_init_ep(dbc->dbc_ctx->ep_out, mbs, XHCI_EP_BULK_OUT, + dbc->dbc_oring.dma); + dbc_init_ep(dbc->dbc_ctx->ep_in, mbs, XHCI_EP_BULK_IN, + dbc->dbc_iring.dma); + + writel(1, ®->erstsz); + writeq(virt_to_maddr(dbc->dbc_erst), ®->erstba); + writeq(erdp, ®->erdp); + writeq(virt_to_maddr(dbc->dbc_ctx), ®->cp); + writel((DBC_DBC_VENDOR << 16) | DBC_DBC_PROTOCOL, ®->ddi1); + writel(DBC_DBC_PRODUCT, ®->ddi2); + + cache_flush(dbc->dbc_ctx, sizeof(*dbc->dbc_ctx)); + cache_flush(dbc->dbc_erst, sizeof(*dbc->dbc_erst)); + cache_flush(dbc->dbc_ering.trb, DBC_TRB_RING_BYTES); + cache_flush(dbc->dbc_oring.trb, DBC_TRB_RING_BYTES); + cache_flush(dbc->dbc_iring.trb, DBC_TRB_RING_BYTES); + cache_flush(dbc->dbc_owork.buf, DBC_WORK_RING_BYTES); + + cmd = pci_conf_read16(dbc->sbdf, PCI_COMMAND); + pci_conf_write16(dbc->sbdf, PCI_COMMAND, cmd | PCI_COMMAND_MASTER); + + return 1; +} + +static void dbc_init_work_ring(struct dbc *dbc, + struct dbc_work_ring *wrk) +{ + wrk->enq = 0; + wrk->deq = 0; + wrk->dma = virt_to_maddr(wrk->buf); +} + +/** + * Initialize the DbC and enable it for transfers. First map in the DbC + * registers from the host controller's MMIO region. Then allocate and map + * DMA for the event and transfer rings. Finally, enable the DbC for + * the host to enumerate. On success, the DbC is ready to send packets. + * + * @param dbc the dbc to open (!= NULL) + * @return true iff dbc_open succeeded + */ +static bool __init dbc_open(struct dbc *dbc) +{ + if ( !dbc ) + return false; + + if ( !dbc_init_xhc(dbc) ) + return false; + + if ( !dbc_init_dbc(dbc) ) + return false; + + dbc_init_work_ring(dbc, &dbc->dbc_owork); + dbc_enable_dbc(dbc); + dbc->open = true; + + return true; +} + +/* + * Ensure DbC is still running, handle events, and possibly re-enable if cable + * was re-plugged. Returns true if DbC is operational. + */ +static bool dbc_ensure_running(struct dbc *dbc) +{ + struct dbc_reg *reg = dbc->dbc_reg; + uint32_t ctrl; + uint32_t cmd; + + dbc_pop_events(dbc); + + ctrl = readl(®->ctrl); + if ( !(ctrl & (1U << DBC_CTRL_DCR)) ) + { + return false; + } + + if ( ctrl & (1U << DBC_CTRL_DRC) ) + { + writel(ctrl | (1U << DBC_CTRL_DRC), ®->ctrl); + writel(readl(®->portsc) | (1U << DBC_PSC_PED), ®->portsc); + wmb(); + } + + return true; +} + +/** + * Commit the pending transfer TRBs to the DbC. This notifies + * the DbC of any previously-queued data on the work ring and + * rings the doorbell. + * + * @param dbc the dbc to flush + * @param trb the ring containing the TRBs to transfer + * @param wrk the work ring containing data to be flushed + */ +static void dbc_flush(struct dbc *dbc, struct xhci_trb_ring *trb, + struct dbc_work_ring *wrk) +{ + struct dbc_reg *reg = dbc->dbc_reg; + uint32_t db = (readl(®->db) & 0xFFFF00FF) | (trb->db << 8); + + if ( xhci_trb_ring_full(trb) ) + return; + + if ( wrk->enq == wrk->deq ) + return; + else if ( wrk->enq > wrk->deq ) + { + dbc_push_trb(dbc, trb, wrk->dma + wrk->deq, wrk->enq - wrk->deq); + wrk->deq = wrk->enq; + } + else + { + dbc_push_trb(dbc, trb, wrk->dma + wrk->deq, + DBC_WORK_RING_CAP - wrk->deq); + wrk->deq = 0; + if ( wrk->enq > 0 && !xhci_trb_ring_full(trb) ) + { + dbc_push_trb(dbc, trb, wrk->dma, wrk->enq); + wrk->deq = wrk->enq; + } + } + + wmb(); + writel(db, ®->db); +} + +/** + * Queue a single character to the DbC. A transfer TRB will be created + * if the character is a newline and the DbC will be notified that data is + * available for writing to the debug host. + * + * @param dbc the dbc to write to + * @param c the character to write + * @return the number of bytes written + */ +static int64_t dbc_putc(struct dbc *dbc, char c) +{ + if ( !dbc_push_work(dbc, &dbc->dbc_owork, &c, 1) ) + return 0; + + if ( !dbc_ensure_running(dbc) ) + return 1; + + if ( c == '\n' ) + dbc_flush(dbc, &dbc->dbc_oring, &dbc->dbc_owork); + + return 1; +} + +struct dbc_uart { + struct dbc dbc; + struct timer timer; + spinlock_t *lock; +}; + +static struct dbc_uart dbc_uart; + +static void cf_check dbc_uart_poll(void *data) +{ + struct serial_port *port = data; + struct dbc_uart *uart = port->uart; + struct dbc *dbc = &uart->dbc; + unsigned long flags = 0; + + if ( spin_trylock_irqsave(&port->tx_lock, flags) ) + { + if ( dbc_ensure_running(dbc) ) + { + dbc_flush(dbc, &dbc->dbc_oring, &dbc->dbc_owork); + dbc_enqueue_in(dbc, &dbc->dbc_iring, &dbc->dbc_iwork); + } + spin_unlock_irqrestore(&port->tx_lock, flags); + } + + serial_tx_interrupt(port, guest_cpu_user_regs()); + set_timer(&uart->timer, NOW() + MICROSECS(DBC_POLL_INTERVAL)); +} + +static void __init cf_check dbc_uart_init_preirq(struct serial_port *port) +{ + struct dbc_uart *uart = port->uart; + uart->lock = &port->tx_lock; +} + +static void __init cf_check dbc_uart_init_postirq(struct serial_port *port) +{ + struct dbc_uart *uart = port->uart; + + serial_async_transmit(port); + init_timer(&uart->timer, dbc_uart_poll, port, 0); + set_timer(&uart->timer, NOW() + MILLISECS(1)); + + if ( pci_ro_device(0, uart->dbc.sbdf.bus, uart->dbc.sbdf.devfn) ) + printk(XENLOG_WARNING + "Failed to mark read-only %pp used for XHCI console\n", + &uart->dbc.sbdf); +} + +static int cf_check dbc_uart_tx_ready(struct serial_port *port) +{ + struct dbc_uart *uart = port->uart; + struct dbc *dbc = &uart->dbc; + + return DBC_WORK_RING_CAP - dbc_work_ring_size(&dbc->dbc_owork); +} + +static void cf_check dbc_uart_putc(struct serial_port *port, char c) +{ + struct dbc_uart *uart = port->uart; + dbc_putc(&uart->dbc, c); +} + +static void cf_check dbc_uart_flush(struct serial_port *port) +{ + s_time_t goal; + struct dbc_uart *uart = port->uart; + struct dbc *dbc = &uart->dbc; + + if ( dbc_ensure_running(dbc) ) + dbc_flush(dbc, &dbc->dbc_oring, &dbc->dbc_owork); + + goal = NOW() + MICROSECS(DBC_POLL_INTERVAL); + if ( uart->timer.expires > goal ) + set_timer(&uart->timer, goal); +} + +static struct uart_driver dbc_uart_driver = { + .init_preirq = dbc_uart_init_preirq, + .init_postirq = dbc_uart_init_postirq, + .tx_ready = dbc_uart_tx_ready, + .putc = dbc_uart_putc, + .flush = dbc_uart_flush, +}; + +static struct xhci_trb evt_trb[DBC_TRB_RING_CAP]; +static struct xhci_trb out_trb[DBC_TRB_RING_CAP]; +static struct xhci_trb in_trb[DBC_TRB_RING_CAP]; +static struct xhci_erst_segment erst __aligned(64); +static struct xhci_dbc_ctx ctx __aligned(64); +static uint8_t out_wrk_buf[DBC_WORK_RING_CAP] __aligned(DBC_PAGE_SIZE); +static struct xhci_string_descriptor str_buf[DBC_STRINGS_COUNT]; +static char __initdata opt_dbgp[30]; + +string_param("dbgp", opt_dbgp); + +void __init xhci_dbc_uart_init(void) +{ + struct dbc_uart *uart = &dbc_uart; + struct dbc *dbc = &uart->dbc; + + if ( strncmp(opt_dbgp, "xhci", 4) ) + return; + + memset(dbc, 0, sizeof(*dbc)); + + dbc->dbc_ctx = &ctx; + dbc->dbc_erst = &erst; + dbc->dbc_ering.trb = evt_trb; + dbc->dbc_oring.trb = out_trb; + dbc->dbc_iring.trb = in_trb; + dbc->dbc_owork.buf = out_wrk_buf; + dbc->dbc_str = str_buf; + + if ( dbc_open(dbc) ) + serial_register_uart(SERHND_DBGP, &dbc_uart_driver, &dbc_uart); +} + +#ifdef DBC_DEBUG +static void dbc_dump(struct dbc *dbc) +{ + struct dbc_reg *r = dbc->dbc_reg; + + dbc_debug("XHCI DBC DUMP:\n"); + dbc_debug(" ctrl: 0x%x stat: 0x%x psc: 0x%x\n", + readl(&r->ctrl), readl(&r->st), readl(&r->portsc)); + dbc_debug(" id: 0x%x, db: 0x%x\n", + readl(&r->id), readl(&r->db)); + dbc_debug(" erstsz: %u, erstba: 0x%lx\n", + readl(&r->erstsz), readq(&r->erstba)); + dbc_debug(" erdp: 0x%lx, cp: 0x%lx\n", + readq(&r->erdp), readq(&r->cp)); + dbc_debug(" ddi1: 0x%x, ddi2: 0x%x\n", + readl(&r->ddi1), readl(&r->ddi2)); + dbc_debug(" erstba == virt_to_dma(erst): %d\n", + readq(&r->erstba) == virt_to_maddr(dbc->dbc_erst)); + dbc_debug(" erdp == virt_to_dma(erst[0].base): %d\n", + readq(&r->erdp) == dbc->dbc_erst[0].base); + dbc_debug(" cp == virt_to_dma(ctx): %d\n", + readq(&r->cp) == virt_to_maddr(dbc->dbc_ctx)); +} + +static void dbc_uart_dump(void) +{ + struct dbc_uart *uart = &dbc_uart; + struct dbc *dbc = &uart->dbc; + + dbc_dump(dbc); +} +#endif diff --git a/xen/include/xen/serial.h b/xen/include/xen/serial.h index 6548f0b0a9cf..181e026967bc 100644 --- a/xen/include/xen/serial.h +++ b/xen/include/xen/serial.h @@ -171,6 +171,11 @@ struct ns16550_defaults { }; void ns16550_init(int index, struct ns16550_defaults *defaults); void ehci_dbgp_init(void); +#ifdef CONFIG_XHCI +void xhci_dbc_uart_init(void); +#else +static void inline xhci_dbc_uart_init(void) {}; +#endif void arm_uart_init(void);