Skip to content

Where the OCTEON TX knowledge came from

This page is the OCTEON TX (Cavium CN83XX) counterpart of ../provenance.md, and it is separate on purpose. The two families reached us through different upstream components, from different release channels, and a reader checking what was taken from where should not have to disentangle two upstreams inside one page. There will be one of these per family that ever gets a driver here.

This project is BSD-2-Clause. Every file says so, and LICENSE is the whole of it.

It is not legal advice. It is a statement of practice, written so that anyone reviewing the project can check the practice against the tree.

The rule, unchanged

Facts are transcribed. Expression is not copied.

A register offset, a magic word, a field width, the order of fields in a structure the hardware itself defines, the minimum length a device will accept - these are facts about an interface. The code that acts on them is written here, from nothing.

Where that rule is visible in this family: contrib/octep/ declares no structure over device memory at all. The vendor's barmap.h and desc_queue.h both declare C structs, and it would have been shorter to transcribe them. Instead octep.h and octep_mgmt.c carry named byte offsets - OCTEP_BARMAP_FCLT_OFF, OCTEP_DQ_CONS_IDX, OCTEP_DESC_SIZE - and every access goes through bus_read_* and bus_write_* at a computed offset.

That is the same decision, for the same two reasons, as contrib/npuep/npugiu.h: a C struct laid over MMIO invites the compiler to merge, split, reorder or elide accesses that must happen exactly once, and a list of offsets is the only form that could be published under this licence.

Where it came from, exactly

Sophos publishes the source. It is four archives deep, and the path is worth recording because the component matters more than the ISO:

SFOS_OSS-22.0.1_MR-1-490.iso
  BSP/npuos-gpl-package-2026.0213-1238-56-v22.0.Maint.040.Luzon/
    gpl-package-...tar.bz2
      gpl-package/gpl-pkg.tar.bz2
        extensions-sources-pcie_ep_octeontx-SDK10.22.03/pcie_ep_octeontx/
          sources-pcie_ep_octeontx-SDK10.22.03.tar.bz2

manifest-gpl-package.json at the top of that package names 46 components with a git remote and commit for each. The two that matter here:

component branch commit
pcie_ep_octeontx pcie_ep_octeontx-release-SDK10.22.03 c5bf95b85650575727e0a81f4afbccb5eacdb63c
sdk-ext-pcie-ep sdk-ext-pcie-ep-release-SDK10.22.03 d133e9c8125c15b01c3f595d4cd395d5286f002b

Licence. Every file consulted carries Copyright (c) 2020 Marvell and SPDX-License-Identifier: GPL-2.0.

A second component in the same package, and it is not the same component. The SDP work needed the code that runs on the coprocessor, which is not in pcie_ep_octeontx at all. It is the coprocessor's Linux kernel, shipped in the same GPL package by a different route:

marvell-base-sdk-sources/linux/sources-linux-4.14.76-SDK10.22.03.tar.bz2
  drivers/net/ethernet/cavium/octeontx-83xx/

The manifest records marvell-base-sdk-sources and linux as version 10.22.03 with no git remote and no commit - unlike the components above, which each name one. So this material is traceable to a release, not to a revision, and that is worth saying plainly rather than implying a precision the manifest does not offer.

And it is not the kernel that is running. The drop carries 4.14.76; the coprocessor runs 4.14.207-10.22.03. Where the two could disagree, this page says which one a claim rests on.

How this differs from the ARMADA material, and why the two pages are apart: that came from the pcie_ep_armada-release-SDK10.22.03 release, a different component with its own branch and commit, and its files are marked GPL-2.0-only. The licence family is the same; the origin is not, and traceability is per component rather than per licence. Keeping one page per family means any claim here can be checked against one named commit of one named component.

What was read, and what was taken

Source Licence What was taken
host/.../common/cn83xx_pf_regs.h GPL-2.0 CSR offsets: the scratch register at 0x20180, the SLI window register pair, the EPF stride. For SDP: SDP_EPF_RINFO at 0x20190 and its four field positions, the 128 KiB ring stride, the per-ring R_IN_* and R_OUT_* offsets, and the IDLE/RDSIZE/IS_64B/IMODE bit positions
host/.../osi/cn83xx_pf_device.c GPL-2.0 That the ring CSRs are reached as mmio[0] plus an offset, i.e. BAR0 directly and not through the SLI window; that the host walks every ring it owns reading these same registers; and the names and order of the bring-up steps, recorded in the family page as a ladder and not implemented here
drivers/net/ethernet/cavium/octeontx-83xx/octeontx_main.c GPL-2.0, and a different component - see above The one fact that separates the vocabulary: net_port_count is the BGX front ports, pci_port_count is the SDP host-facing ports. Three lines of a structure assignment, read as a definition, not copied
drivers/net/ethernet/cavium/octeontx-83xx/slipf_main.c GPL-2.0, same component The identity of slipf: module name, PCI driver name octeontx-sli, and that /sys/module/slipf/parameters/pci_port is its own. And the target half of the EP-mode handshake: the order of the four steps, that both of its waits are untimed busy loops, that it publishes its timer rate in the low sixteen bits of its reply, and that it creates the SDP port only after the exchange completes
drivers/net/ethernet/cavium/octeontx-83xx/sli.h GPL-2.0, same component The four handshake constants and the field positions of the info word
host/.../osi/octeon_device.c and .../kernel/drv/octeon_main.c GPL-2.0 The host half of the same handshake: which value the host writes first, what goes into the info word and where each field sits, and that the host answers on a once-a-second poll rather than inline
host/.../osi/octeon_device.c (octeon_enable_irq), .../kernel/drv/octnic/octeon_network.c and .../common/cn83xx_pf_regs.h GPL-2.0 That bit 59 of R_OUT_CNTS is named RESEND; that the vendor's poll of a ring, when it finishes, writes the count it took and then that bit; and what its comment says the bit is for. Described in a visit is not a pass as something tried on the appliance; nothing in this tree writes the bit
host/.../osi/octeon_droq.c and .../osi/cn83xx_pf_device.c GPL-2.0 That the vendor's host grants an output ring its own size when it enables it, afterwards one credit for each buffer it refills, and that it keeps count of the buffers it has yet to refill. Cited in a ring that fills beside what this driver had been granting
host/.../osi/octeon_droq.c and .../osi/octeon_droq.h, with driver.mk for the mode it is built in GPL-2.0 That a packet longer than a receive buffer goes on into the buffers after it and is counted once; how many buffers a length word means when the ring is in buffer-pointer mode - the first holds the length word, the response word and what is left of it, every later one is all packet - and that the length word counts the response word; that the vendor's host reads a register before it copies out of the later buffers, and the reason its comment gives. Cited in a packet is not a buffer beside what this driver's pass had been doing
usfp/.../include/cmsg_types.h and .../x86/cmsg.c Sophos's GPL source drop; see the note below The control message types by number, among them connection statistics pushed in bulk from the fast path to the host; that such a message is a header, a count of entries and of bursts, and bursts of consecutive entries, at most 800 entries; and that the vendor's host makes a control message contiguous before it reads one. The entry's size is not from there: it was worked out from a message's own length, in the same page
drivers/net/ethernet/cavium/octeontx-83xx/slipf_main.c, with Sophos's patch 0016-increase-sdp-out-wmark to it GPL-2.0, the coprocessor's kernel component listed above That sdp_port_config writes the output watermark SDP_OUT_WMARK and enables backpressure for the port's channels: 0x100 in the stock source, 0x400 in the patch. The value on the appliance was read with the vendor's txcsr on the coprocessor's console; that the block stops at it, and everything else on that page, was measured
target/drivers/pcie_ep/src/barmap.h GPL-2.0 The barmap's field order and sizes, the version encoding, the 4 MB BAR index granularity, that entry 15 is GICD, GICD_SETSPI_NSR and the SPI base
host/.../osi/octeon_device.c and Sophos's 29 patches to it GPL-2.0 The readiness magic 0xABCDABCD, that the high half of the scratch word is an offset, that an all-ones version means retry, and which mapped BAR the table lives in per chip
host/.../kernel/drv/facility.c GPL-2.0 That a doorbell is a 32-bit store of an SPI number to gicd_offset, and that the number is range-checked first
host/drivers/mgmt_net/bar_space_mgmt_net.h GPL-2.0 The management register map - status, interrupt and mailbox offsets, the state numbering, the two ring offsets
host/drivers/mgmt_net/desc_queue.h GPL-2.0 The descriptor queue header layout and the descriptor bitfield positions
host/drivers/mgmt_net/host_ethdev.c GPL-2.0 The order of the bring-up: shadows and buffers before any address is published, rings complete before HOST_READY, and that a polling mode is a legitimate alternative to interrupts here
target/drivers/mgmt_net/target_ethdev.c GPL-2.0 What the target validates, and that it goes to TARGET_FATAL rather than refusing - including the ETH_ZLEN minimum that a host must pad to
pcie_ep.ko on the appliance proprietary binary Confirmation of the constants, by decoding the module's own MOVZ/MOVK immediates, and its printf formats
octeon_drv.ko on the appliance proprietary binary That a CN83xx path exists at all, from its two distinct readiness log formats
xgs-bsp/data/AMDA0202-0004R00.txt Sophos's own platform database, same GPL package The board's port map: that device1 is an 88E6193X switch, and which panel label sits on which module and port. A table of facts about one assembly, read and summarised, not copied
The appliance's own logs and registers - Confirmation that every reading above was right
usfp/.../common/ipsec_fpop.c and .../sa_table.h Sophos's GPL source drop; see the note below SA_ADD's return paths and what each means, including that an index already in use is refused and that a queue-full code signals a duplicate; that an encrypt association's lif_index is stored and never examined while a decrypt one's is decomposed and keyed; that cfg.version is bumped on success; that SA_GET_STATS exists and what it returns; the negative-index terminator of a table read
usfp/.../common/mflow_fpop.c and .../mflow_table.h same That the programming gate is the entry's own validity flag and its six-bit revision rather than the firewall revision, and that a mismatch is a silent skip reported as success; the reply entry's true length; that the request is 32 bytes and where the reverse half sits; that the read is filtered and packed unless a flag turns the filter off
usfp/.../common/fw_state_fpop.c and .../cntrs_fpop.c, with .../fp_count.h same That setting the firewall revision stores it and then queues one invalidation request rather than sweeping the table; that the forwarding path compares the revision per packet and counts a mismatch as counter 42, which is what makes the bump a barrier; that the per-port counter array is counter-major with a 256 stride; which counter arrays exist and how each is read and cleared
usfp/.../common/conn_fpop.c, .../lif_fpop.c, .../nhop_fpop.c and their headers same Confirmation of the connection entry's layout field by field, including the NAT block's offset and the bit positions this project had measured; that two of the vendor's own inline size comments are wrong; that 0xff in a LIF update mask is a sentinel meaning add rather than a mask; that a next hop's port tag is discarded and recomputed from the interface id
usfp/.../include/lif_table.h, .../mflow_table.h, .../x86/usfp_netdev_gr.c and .../common/ipsec_fpop.c same That a LIF index is the interface number shifted by twelve with the VLAN below it, and that an association's stored LIF is that bare port value with VLAN zero, keyed on its port bits alone for decryption; that the microflow key is the LIF, the two link addresses, the ethertype, the family, the protocol, the ports or ICMP identifier or SPI, and the addresses, and nothing else - the association index and the direction bit are in the entry's operational half, applied after a match. Read for the flow carries the association, where it settles that the driver cannot close the clear-text exposure of its third setting
usfp/.../x86/usfp_ipsec.c and .../x86/sp2fp_sa.c, with .../include/sa_table.h same That the vendor's host sets udp_enable and the two ports in the association request for a state with encapsulation, and the en_nat_t control bit it maps from; the option word's bit positions. Read for the UDP header is the host's to write; that the coprocessor then expects the UDP header in the frame it is handed was measured, not read
usfp/.../x86/sp2fp_sa.c, with .../include/sa_table.h same That the vendor's host sets the control word's ext_seq bit for a state with extended sequence numbers, and the bit's position. Read for under ESN, what arrives stays with the kernel; that the coprocessor refuses the peer's frames after a burst under it was measured, not read, and its cause was not looked for in the fast path
usfp/.../x86/sp2fp_sa.c (generate_usfp_control), .../x86/usfp_ipsec.c, with .../include/sa_table.h same That the vendor's host maps an AES-CBC state to cipher mode CBC with the HMAC's hash type and truncation (SHA1_96, SHA256_128, SHA384_192, SHA512_256) and the matching overhead type, and GCM-12 to GF128_96; that it refuses transport mode (x->props.mode != XFRM_MODE_TUNNEL); the auth-key field's union with the AEAD salt. Read for a block cipher at two thirds and GCM-12 and transport are not ours; the rate of a block cipher on the coprocessor was measured, not read
usfp_rh.ko on the appliance, its DWARF proprietary binary, debug information shipped with it The order of the fast path's disposition counters - 183 enumerators - which settled three numberings that disagreed, and named the counters this project reads by index
usfp on the appliance, disassembled proprietary binary That the fast path keeps separate lanes for a frame from the wire and a frame it decrypted, counted apart; that a decrypted frame carries its association in a registered mbuf field and the receive descriptor's security bit becomes an mbuf flag; that in the clear lane no such marker is consulted before a microflow match is honoured, no action or verdict value asks for one, and no drop reason names one - an absence in everything readable, the worker's branches not being fully resolvable from the text. Nothing from the binary is reproduced; what was read is the shape of a decision

Every one of those is a description of an interface. None of it is in this tree as code.

A note on the handler source, because it arrived differently from everything above. It is 174 files extracted from Sophos's GPL source drop - the same package the ARMADA material came from - and the extracted copy carries no per-file licence header. They are Linux kernel modules and include Linux headers, which is a strong indication but not a statement. So: what is recorded from them above is interface description only, in this project's own words, and nothing is copied. If anything from that tree is ever to be copied rather than described, its licence has to be established first rather than assumed - which is the same rule this page applies to everything else, stated here because the usual evidence for it is missing.

Where source and hardware disagreed, the hardware won and the comment says so - which is also how the one real error was caught: an early note put the facility table in the wrong mapped BAR, because the patch context being read was the CN9xxx branch rather than the CN83XX one.

Two later disagreements are recorded the same way, both on the family page. The published poll_for_ep_mode abandons the handshake eleven seconds after the coprocessor boots; the running kernel answered it after five hours, and the running kernel is 4.14.207 against a published 4.14.76. And the vendor's host driver decodes the coprocessor's reported timer rate from the wrong sixteen bits, so what this tree reads as 800 ticks/us their own log would have printed as 44510. In both cases the appliance decided it.

Quotation

Two fragments appear verbatim, both in contrib/octep/octep_mgmt.c and both in the comment that explains why frames are padded:

  • the condition if (len < ETH_ZLEN || is_frag), which is the target's own test; and
  • the log line "mgmt_net:bad rx pkt", which is what it prints.

They are there because the consequence is severe and non-obvious - the target refuses the frame without consuming the descriptor, so one short frame stops the ring permanently - and a reader should be able to check that claim against what the vendor actually wrote. A paraphrase would be worse for exactly that reason.

If any rights holder disagrees with that judgement, the remedy is two lines and the surrounding explanation stands without them. Open an issue.

Nothing was taken from Linux's octeon_ep

Mainline Linux has an octeon_ep driver, also GPL-2.0, by the same Marvell author. It was read, and it is cited in ../families/octeon-tx2.md because it covers 0xB100 and 0xB200 and therefore matters to anyone with those boards. It does not cover 0xA300, it is a different protocol generation - a different magic word, and a mailbox found by construction rather than by following a pointer - and nothing from it is in this tree in any form.

What is deliberately not here

  • No vendor source. Not a file, not a function, not a structure declaration.
  • No firmware and no boot images. The coprocessor runs the vendor's own, which stays on the appliance. The per-platform U-Boot images and CPLD files exist on the bench and are not published.
  • No binaries. Nothing is redistributed.
  • No credentials, no real addresses, no hardware identifiers. tools/check-private-data.py runs in CI and fails the build over any of them. It caught a real MAC address and a live IP in a draft of the OCTEON TX family page, before that page was ever pushed.

Discussion

The discussion of this page is held in the project's GitHub Discussions. Showing it here loads giscus from giscus.app and GitHub, and writing in it needs a GitHub account - what that means.