├── CONTRIBUTING.md ├── LICENSE ├── README.md └── pcie ├── tlp.go └── tlp_test.go /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement. You (or your employer) retain the copyright to your contribution; 10 | this simply gives us permission to use and redistribute your contributions as 11 | part of the project. Head over to to see 12 | your current agreements on file or to sign a new one. 13 | 14 | You generally only need to submit a CLA once, so if you've already submitted one 15 | (even if it was for a different project), you probably don't need to do it 16 | again. 17 | 18 | ## Code reviews 19 | 20 | All submissions, including submissions by project members, require review. We 21 | use GitHub pull requests for this purpose. Consult 22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 23 | information on using pull requests. 24 | 25 | ## Community Guidelines 26 | 27 | This project follows 28 | [Google's Open Source Community Guidelines](https://opensource.google.com/conduct/). 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GO-PCIe-TLP 2 | 3 | ## About 4 | 5 | This Go library builds and parses PCIe Transport Layer Packets (TLP) as 6 | specified in the 7 | [PCI Express base specification](https://pcisig.com/specifications/pciexpress). 8 | Coupled with [go-pcie-screamer](https://github.com/google/go-pcie-screamer), 9 | this library can be used to run PCIe security tests. 10 | 11 | ## Usage 12 | 13 | ```go 14 | import "github.com/google/go-pcie-tlp/pcie" 15 | 16 | // Build memory read TLP. 17 | reqID = pcie.DeviceID { 18 | Bus: 0x61, 19 | Device: 0, 20 | Function: 0, 21 | } 22 | tag := uint8(0x80) 23 | addr := uint64(0x12000) 24 | maxLen := uint32(4096) 25 | tlp, err := pcie.NewMRd(reqID, tag, addr, maxLen) 26 | 27 | // Build memory write TLP. 28 | data := []byte{0x41, 0x41, 0x41, 0x41} 29 | tlp, err := pcie.NewMWr(reqID, addr, data) 30 | 31 | // Parse completion TLP. 32 | buf := []byte{ 33 | 0x4a, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x61, 0x00, 0x80, 0x00, 0x41, 0x41, 0x41, 0x41, 34 | 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41} 35 | tlp, err := NewCplFromBytes(buf) 36 | ``` 37 | 38 | ## Disclaimer 39 | 40 | This is not an official Google product (experimental or otherwise), it is just 41 | code that happens to be owned by Google. 42 | -------------------------------------------------------------------------------- /pcie/tlp.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package pcie builds and parses PCIe Transport Layer Packets (TLP). 16 | package pcie 17 | 18 | import ( 19 | "bytes" 20 | "encoding/binary" 21 | "errors" 22 | "fmt" 23 | "math" 24 | "math/bits" 25 | ) 26 | 27 | // abbreviations 28 | var ( 29 | be = binary.BigEndian 30 | ) 31 | 32 | // errors 33 | var ( 34 | ErrBadType = errors.New("bad TLP header type") 35 | ErrBadLength = errors.New("bad TLP data length") 36 | ErrBadAddress = errors.New("bad TLP address") 37 | ErrTooShort = errors.New("TLP packet too short") 38 | ) 39 | 40 | const ( 41 | dwordLen = 4 42 | maxDataLen = 1024 * dwordLen 43 | // MaxTLPBuffer is a 4 dword header + max data payload. 44 | MaxTLPBuffer = 4*dwordLen + maxDataLen*dwordLen 45 | ) 46 | 47 | const ( 48 | fmt3DWNoData = 0b000 49 | fmt4DWNoData = 0b001 50 | fmt3DWWithData = 0b010 51 | fmt4DWWithData = 0b011 52 | fmtTlpPrefix = 0b100 53 | ) 54 | 55 | // TlpType is the format and type field in the TLP header. 56 | // See Table 2-3 in PCI EXPRESS BASE SPECIFICATION, REV. 3.1a. 57 | type TlpType uint8 58 | 59 | const ( 60 | // MRd3 is a Memory Read Request encoded with 3 dwords. 61 | MRd3 TlpType = (fmt3DWNoData << 5) | 0b00000 62 | // MRd4 is a Memory Read Request encoded with 4 dwords. 63 | MRd4 TlpType = (fmt4DWNoData << 5) | 0b00000 64 | // MRdLk3 is a Memory Read Request-Locked encoded with 3 dwords. 65 | MRdLk3 TlpType = (fmt3DWNoData << 5) | 0b00001 66 | // MRdLk4 is a Memory Read Request-Locked encoded with 4 dwords. 67 | MRdLk4 TlpType = (fmt4DWNoData << 5) | 0b00001 68 | // MWr3 is a Memory Write Request encoded with 3 dwords. 69 | MWr3 TlpType = (fmt3DWWithData << 5) | 0b00000 70 | // MWr4 is a Memory Write Request encoded with 4 dwords. 71 | MWr4 TlpType = (fmt4DWWithData << 5) | 0b00000 72 | // IORdT is an I/O Read Request. 73 | IORdT TlpType = (fmt3DWNoData << 5) | 0b00010 74 | // IOWrtT is an I/O Write Request. 75 | IOWrtT TlpType = (fmt3DWWithData << 5) | 0b00010 76 | // CfgRd0 is a Configuration Read of Type 0. 77 | CfgRd0 TlpType = (fmt3DWNoData << 5) | 0b00100 78 | // CfgWr0 is a Configuration Write of Type 0. 79 | CfgWr0 TlpType = (fmt3DWWithData << 5) | 0b00100 80 | // CfgRd1 is a Configuration Read of Type 1. 81 | CfgRd1 TlpType = (fmt3DWNoData << 5) | 0b00101 82 | // CfgWr1 is a Configuration Write of Type 1. 83 | CfgWr1 TlpType = (fmt3DWWithData << 5) | 0b00101 84 | // CplE is a Completion without Data. Used for I/O and 85 | // Configuration Write Completions with any 86 | // Completion Status. 87 | CplE TlpType = (fmt3DWNoData << 5) | 0b01010 88 | // CplD is a Completion with Data. Used for Memory, 89 | // I/O, and Configuration Read Completions. 90 | CplD TlpType = (fmt3DWWithData << 5) | 0b01010 91 | // CplLk is a Completion for Locked Memory Read without 92 | // Data. Used only in error case. 93 | CplLk TlpType = (fmt3DWNoData << 5) | 0b01011 94 | // CplLkD is a Completion for Locked Memory Read – 95 | // otherwise like CplD. 96 | CplLkD TlpType = (fmt3DWWithData << 5) | 0b01011 97 | // MRIOV is a Multi-Root I/O Virtualization and Sharing (MR-IOV) TLP prefix. 98 | MRIOV TlpType = (fmtTlpPrefix << 5) | 0b00000 99 | // LocalVendPrefix is a Local TLP prefix with vendor sub-field. 100 | LocalVendPrefix TlpType = (fmtTlpPrefix << 5) | 0b01110 101 | // ExtTPH is an Extended TPH TLP prefix. 102 | ExtTPH TlpType = (fmtTlpPrefix << 5) | 0b10000 103 | // PASID is a Process Address Space ID (PASID) TLP Prefix. 104 | PASID TlpType = (fmtTlpPrefix << 5) | 0b10001 105 | // EndEndVendPrefix is an End-to-End TLP prefix with vendor sub-field. 106 | EndEndVendPrefix TlpType = (fmtTlpPrefix << 5) | 0b11110 107 | ) 108 | 109 | // AddressType is the address type field in the request header. 110 | type AddressType uint8 111 | 112 | // Supported address types. 113 | const ( 114 | DefaultUntranslated AddressType = 0b00 115 | TranslationRequest AddressType = 0b01 116 | Translated AddressType = 0b10 117 | AddressTypeReserved AddressType = 0b11 118 | ) 119 | 120 | // TrafficClass is the traffic class field in the request header and used 121 | // to set quality of service (QoS). 122 | type TrafficClass uint8 123 | 124 | // Supported traffic classes. 125 | const ( 126 | TC0 TrafficClass = iota 127 | TC1 128 | TC2 129 | TC3 130 | TC4 131 | TC5 132 | TC6 133 | TC7 134 | ) 135 | 136 | // CompletionStatus is the completion status field in the completion header. 137 | type CompletionStatus uint8 138 | 139 | // Supported completion status. 140 | const ( 141 | SuccessfulCompletion CompletionStatus = 0b000 142 | UnsupportedRequest CompletionStatus = 0b001 143 | ConfigurationRequestRetry CompletionStatus = 0b010 144 | CompleterAbort CompletionStatus = 0b100 145 | ) 146 | 147 | // Address is the address field in the request header. 148 | type Address uint64 149 | 150 | func (a Address) is64() bool { 151 | return a > math.MaxUint32 152 | } 153 | 154 | func (a Address) toBuffer(buf *bytes.Buffer) { 155 | if a.is64() { 156 | binary.Write(buf, binary.BigEndian, uint32(a>>32)) 157 | } 158 | // The 2 lower bits of the Address are reserved for TLP processing hint. 159 | // See Figure 2-8: "32-bit Address Routing" and 160 | // Figure 2-7: "64-bit Address Routing". 161 | binary.Write(buf, binary.BigEndian, uint32(a&0xfffffffc)) 162 | } 163 | 164 | func (a *Address) fromBuffer(is64 bool, buf *bytes.Buffer) { 165 | if is64 { 166 | var high uint32 167 | binary.Read(buf, binary.BigEndian, &high) 168 | *a = Address(uint64(high) << 32) 169 | } 170 | var low uint32 171 | binary.Read(buf, binary.BigEndian, &low) 172 | *a |= Address(low) 173 | } 174 | 175 | // TlpHeader is the first header dword, common on all TLPs. 176 | // See section 2.2.1. Common Packet Header Fields. 177 | type TlpHeader struct { 178 | // Format and type. 179 | Type TlpType 180 | // Traffic class (3b). 181 | TC TrafficClass 182 | // Indicates that a Memory Request is an LN Read or LN Write (1b). 183 | LN bool 184 | // Presence of TLP Processing Hints (1b). 185 | TH bool 186 | // Presence of TLP digest in the form of a single DW at the end of the TLP (1b). 187 | TD bool 188 | // Indicates the TLP is poisoned (1b). 189 | EP bool 190 | // Attributes (3b): no-snoop, relaxed ordering, id-based ordering. 191 | NS bool 192 | RO bool 193 | IBO bool 194 | // Address Type (2b). 195 | AT AddressType 196 | // Length of data payload in DW (10b). 197 | Length int 198 | } 199 | 200 | func computeBit(value bool, pos int) int { 201 | if value { 202 | return 1 << pos 203 | } 204 | return 0 205 | } 206 | 207 | func (h *TlpHeader) toBuffer(buf *bytes.Buffer) { 208 | dw := make([]byte, dwordLen) 209 | dw[0] = byte(h.Type) 210 | dw[1] = byte( 211 | (int(h.TC) << 4) | 212 | computeBit(h.IBO, 2) | 213 | computeBit(h.LN, 1) | 214 | computeBit(h.TH, 0)) 215 | dw[2] = byte( 216 | computeBit(h.TD, 7) | 217 | computeBit(h.EP, 6) | 218 | computeBit(h.RO, 5) | 219 | computeBit(h.NS, 4) | 220 | (int(h.AT) << 2) | 221 | ((h.Length >> 8) & 3)) 222 | dw[3] = byte(h.Length & 0xff) 223 | buf.Write(dw) 224 | } 225 | 226 | func getSubField(input byte, shift, mask int) int { 227 | return (int(input) >> shift) & mask 228 | } 229 | 230 | func getBit(input byte, pos int) bool { 231 | return int(input>>pos)&1 > 0 232 | } 233 | 234 | func (h *TlpHeader) fromBuffer(buf *bytes.Buffer) { 235 | dw := buf.Next(dwordLen) 236 | h.Type = TlpType(uint8(dw[0])) 237 | h.TC = TrafficClass(getSubField(dw[1], 4, 7)) 238 | h.IBO = getBit(dw[1], 2) 239 | h.LN = getBit(dw[1], 1) 240 | h.TH = getBit(dw[1], 0) 241 | h.TD = getBit(dw[2], 7) 242 | h.EP = getBit(dw[2], 6) 243 | h.RO = getBit(dw[2], 5) 244 | h.NS = getBit(dw[2], 4) 245 | h.AT = AddressType(getSubField(dw[2], 2, 3)) 246 | h.Length = getSubField(dw[2], 0, 3)<<8 | int(dw[3]) 247 | } 248 | 249 | // setLength sets the encoded TLP data length based on Table 2-4 250 | // Length[9:0] Field Encoding. 251 | func (h *TlpHeader) setLength(bytesLen int) error { 252 | if bytesLen&3 > 0 { 253 | return fmt.Errorf("%w: TLP length %d is not dword aligned", ErrBadLength, bytesLen) 254 | } 255 | if bytesLen > maxDataLen { 256 | return fmt.Errorf("%w: TLP length %d is too big, expected <= %d", ErrBadLength, bytesLen, maxDataLen) 257 | } 258 | 259 | h.Length = bytesLen >> 2 260 | if h.Length == 1024 { 261 | h.Length = 0 262 | } 263 | return nil 264 | } 265 | 266 | // DataLength decodes h.Length to data length. 267 | // See Table 2-4 Length[9:0] Field Encoding. 268 | func (h *TlpHeader) DataLength() int { 269 | l := h.Length 270 | if l == 0 { 271 | l = 1024 272 | } 273 | return l * dwordLen 274 | } 275 | 276 | // DeviceID is a configuration space address that uniquely identifies 277 | // the device on the PCIe fabric. 278 | type DeviceID struct { 279 | Bus uint8 280 | Device uint8 281 | Function uint8 282 | } 283 | 284 | func (id *DeviceID) toBytes() []byte { 285 | b := make([]byte, 2) 286 | be.PutUint16(b, id.ToUint16()) 287 | return b 288 | } 289 | 290 | func (id *DeviceID) fromBytes(b []byte) { 291 | id.FromUint16(be.Uint16(b)) 292 | } 293 | 294 | // ToUint16 encodes DeviceID to a uint16 value. 295 | func (id *DeviceID) ToUint16() uint16 { 296 | return uint16(int(id.Bus)<<8 | int(id.Device)<<3 | int(id.Function)) 297 | } 298 | 299 | // FromUint16 assigns DeviceID from an encoded uint16 value. 300 | func (id *DeviceID) FromUint16(value uint16) { 301 | id.Bus = uint8(value >> 8) 302 | id.Device = uint8((value >> 3) & 0x1f) 303 | id.Function = uint8(value & 0x07) 304 | } 305 | 306 | // NewDeviceID builds a new DeviceID from an encoded uint16 value. 307 | func NewDeviceID(value uint16) (addr DeviceID) { 308 | addr.FromUint16(value) 309 | return addr 310 | } 311 | 312 | func (id DeviceID) String() string { 313 | return fmt.Sprintf("%02x:%02x.%01x", id.Bus, id.Device, id.Function) 314 | } 315 | 316 | // FromString assigns DeviceID from an encoded string value. 317 | func (id *DeviceID) FromString(value string) error { 318 | n, err := fmt.Sscanf(value, "%02x:%02x.%01x", &id.Bus, &id.Device, &id.Function) 319 | if n != 3 || err != nil { 320 | return err 321 | } 322 | return nil 323 | } 324 | 325 | // RequestHeader extends TlpHeader and includes the second header dword 326 | // on Memory, IO, and Config Request TLPs. 327 | type RequestHeader struct { 328 | TlpHeader 329 | // Requester ID. 330 | ReqID DeviceID 331 | // Unique tag for all outstanding requests. 332 | Tag uint8 333 | // First Byte Enable (4b). 334 | FirstBE uint8 335 | // Last Byte Enable (4b). 336 | LastBE uint8 337 | } 338 | 339 | func (h *RequestHeader) toBuffer(buf *bytes.Buffer) { 340 | h.TlpHeader.toBuffer(buf) 341 | 342 | dw := make([]byte, dwordLen) 343 | copy(dw[0:2], h.ReqID.toBytes()) 344 | dw[2] = byte(h.Tag) 345 | dw[3] = byte(h.LastBE<<4 | h.FirstBE) 346 | buf.Write(dw) 347 | } 348 | 349 | func (h *RequestHeader) fromBuffer(buf *bytes.Buffer) { 350 | h.TlpHeader.fromBuffer(buf) 351 | 352 | dw := buf.Next(dwordLen) 353 | h.ReqID.fromBytes(dw[0:2]) 354 | h.Tag = dw[2] 355 | h.FirstBE = uint8(getSubField(dw[3], 0, 0xf)) 356 | h.LastBE = uint8(getSubField(dw[3], 4, 0xf)) 357 | } 358 | 359 | func (h *RequestHeader) setByteEnables() { 360 | // See section 2.2.5. First/Last DW Byte Enables Rules. 361 | h.FirstBE = 0xf 362 | if h.Length == 1 { 363 | h.LastBE = 0 364 | } else { 365 | h.LastBE = 0xf 366 | } 367 | } 368 | 369 | // CplHeader extends TlpHeader and includes the second and third header dwords 370 | // for Completion TLPs. 371 | // See section 2.2.9. Completion Rules 372 | type CplHeader struct { 373 | TlpHeader 374 | // Completer ID. 375 | CplID DeviceID 376 | // Byte count: the number of bytes left for transmission, including those in 377 | // the current packet (12b). 378 | BC int 379 | // Completion status. 380 | Status CompletionStatus 381 | // Requester ID. 382 | ReqID DeviceID 383 | // Unique tag for all outstanding requests. 384 | Tag uint8 385 | // Lower Byte Address for starting byte of Completion (7b). 386 | AddressLow uint8 387 | } 388 | 389 | func (h *CplHeader) toBuffer(buf *bytes.Buffer) { 390 | h.TlpHeader.toBuffer(buf) 391 | 392 | dw1 := make([]byte, dwordLen) 393 | copy(dw1[0:2], h.CplID.toBytes()) 394 | dw1[2] = byte( 395 | (int(h.Status) << 5) | 396 | (h.BC>>8)&0xf) 397 | dw1[3] = byte(h.BC & 0xff) 398 | buf.Write(dw1) 399 | 400 | dw2 := make([]byte, dwordLen) 401 | copy(dw2[0:2], h.ReqID.toBytes()) 402 | dw2[2] = byte(h.Tag) 403 | dw2[3] = byte(h.AddressLow & 0x7f) 404 | buf.Write(dw2) 405 | } 406 | 407 | func (h *CplHeader) fromBuffer(buf *bytes.Buffer) { 408 | h.TlpHeader.fromBuffer(buf) 409 | 410 | dw1 := buf.Next(dwordLen) 411 | h.CplID.fromBytes(dw1[0:2]) 412 | h.Status = CompletionStatus(getSubField(dw1[2], 5, 7)) 413 | h.BC = getSubField(dw1[2], 0, 0xf)<<8 | int(dw1[3]) 414 | 415 | dw2 := buf.Next(dwordLen) 416 | h.ReqID.fromBytes(dw2[0:2]) 417 | h.Tag = uint8(dw2[2]) 418 | h.AddressLow = uint8(dw2[3] & 0x7f) 419 | } 420 | 421 | // MRd TLP: Memory read request. 422 | type MRd struct { 423 | RequestHeader 424 | Address Address 425 | } 426 | 427 | // ToBytes encodes MRd to wire format. 428 | func (tlp *MRd) ToBytes() []byte { 429 | buf := new(bytes.Buffer) 430 | tlp.RequestHeader.toBuffer(buf) 431 | tlp.Address.toBuffer(buf) 432 | return buf.Bytes() 433 | } 434 | 435 | // NewMRd builds memory read request. 436 | // |length| is the number of BYTES to read and must be DWORD aligned. 437 | func NewMRd(reqID DeviceID, tag uint8, addr uint64, length uint32) (*MRd, error) { 438 | tlp := &MRd{} 439 | tlp.Address = Address(addr) 440 | if tlp.Address.is64() { 441 | tlp.Type = MRd4 442 | } else { 443 | tlp.Type = MRd3 444 | } 445 | 446 | if err := tlp.setLength(int(length)); err != nil { 447 | return nil, err 448 | } 449 | 450 | tlp.ReqID = reqID 451 | tlp.Tag = tag 452 | tlp.setByteEnables() 453 | return tlp, nil 454 | } 455 | 456 | // NewMRdFromBytes builds a memory read request from a TLP buffer. 457 | func NewMRdFromBytes(b []byte) (*MRd, error) { 458 | if len(b) < 3*dwordLen { 459 | return nil, fmt.Errorf("%w: TLP buffer too short (%d), expected at least 12 bytes", ErrTooShort, len(b)) 460 | } 461 | // Verify type. 462 | var hdr TlpHeader 463 | hdr.fromBuffer(bytes.NewBuffer(b)) 464 | if hdr.Type != MRd3 && hdr.Type != MRd4 { 465 | return nil, fmt.Errorf("%w: type %x is not supported. supported types: MRd3, MRd4", ErrBadType, hdr.Type) 466 | } 467 | if hdr.Type == MRd3 && len(b) < 3*dwordLen { 468 | return nil, fmt.Errorf("%w: TLP buffer too short (%d), want at least %d", ErrTooShort, len(b), 3*dwordLen) 469 | } 470 | if hdr.Type == MRd4 && len(b) < 4*dwordLen { 471 | return nil, fmt.Errorf("%w: TLP buffer too short (%d), want at least %d", ErrTooShort, len(b), 4*dwordLen) 472 | } 473 | // Decode MRd. 474 | tlp := &MRd{} 475 | buf := bytes.NewBuffer(b) 476 | tlp.RequestHeader.fromBuffer(buf) 477 | is64 := hdr.Type == MRd4 478 | tlp.Address.fromBuffer(is64, buf) 479 | return tlp, nil 480 | } 481 | 482 | // MWr TLP: Memory write request. 483 | type MWr struct { 484 | RequestHeader 485 | Address Address 486 | Data []byte 487 | } 488 | 489 | // ToBytes encodes MWr to wire format. 490 | func (tlp *MWr) ToBytes() []byte { 491 | buf := new(bytes.Buffer) 492 | tlp.RequestHeader.toBuffer(buf) 493 | tlp.Address.toBuffer(buf) 494 | buf.Write(tlp.Data) 495 | return buf.Bytes() 496 | } 497 | 498 | // NewMWr builds memory write request. 499 | // len(data) must be DWORD aligned. 500 | func NewMWr(reqID DeviceID, addr uint64, data []byte) (*MWr, error) { 501 | tlp := &MWr{} 502 | tlp.Address = Address(addr) 503 | if tlp.Address.is64() { 504 | tlp.Type = MWr4 505 | } else { 506 | tlp.Type = MWr3 507 | } 508 | 509 | if err := tlp.setLength(len(data)); err != nil { 510 | return nil, err 511 | } 512 | 513 | tlp.ReqID = reqID 514 | tlp.Tag = 0 515 | tlp.setByteEnables() 516 | 517 | tlp.Data = make([]byte, len(data)) 518 | copy(tlp.Data, data) 519 | return tlp, nil 520 | } 521 | 522 | // NewMWrFromBytes builds a memory write request from a TLP buffer. 523 | func NewMWrFromBytes(b []byte) (*MWr, error) { 524 | if len(b) < 3*dwordLen { 525 | return nil, fmt.Errorf("%w: TLP buffer too short (%d), expected at least 12 bytes", ErrTooShort, len(b)) 526 | } 527 | // Verify type. 528 | var hdr TlpHeader 529 | hdr.fromBuffer(bytes.NewBuffer(b)) 530 | if hdr.Type != MWr3 && hdr.Type != MWr4 { 531 | return nil, fmt.Errorf("%w: type %x is not supported. supported types: MWr3, MWr4", ErrBadType, hdr.Type) 532 | } 533 | if hdr.Type == MWr3 && len(b) < 3*dwordLen { 534 | return nil, fmt.Errorf("%w: TLP buffer too short (%d), want at least %d", ErrTooShort, len(b), 3*dwordLen) 535 | } 536 | if hdr.Type == MWr4 && len(b) < 4*dwordLen { 537 | return nil, fmt.Errorf("%w: TLP buffer too short (%d), want at least %d", ErrTooShort, len(b), 4*dwordLen) 538 | } 539 | // Decode MWr. 540 | tlp := &MWr{} 541 | buf := bytes.NewBuffer(b) 542 | tlp.RequestHeader.fromBuffer(buf) 543 | is64 := hdr.Type == MWr4 544 | tlp.Address.fromBuffer(is64, buf) 545 | 546 | tlp.Data = make([]byte, tlp.DataLength()) 547 | if buf.Len() < len(tlp.Data) { 548 | return nil, fmt.Errorf("%w: TLP data too short (%d), expected %d bytes", ErrTooShort, buf.Len(), len(tlp.Data)) 549 | } 550 | copy(tlp.Data, buf.Next(len(tlp.Data))) 551 | return tlp, nil 552 | } 553 | 554 | // Cpl TLP: Completion response. 555 | type Cpl struct { 556 | CplHeader 557 | Data []byte 558 | } 559 | 560 | // ToBytes encodes Cpl to wire format. 561 | func (tlp *Cpl) ToBytes() []byte { 562 | buf := new(bytes.Buffer) 563 | tlp.CplHeader.toBuffer(buf) 564 | buf.Write(tlp.Data) 565 | return buf.Bytes() 566 | } 567 | 568 | // NewCplFromBytes builds completion response from TLP buffer. 569 | func NewCplFromBytes(b []byte) (*Cpl, error) { 570 | if len(b) < 3*dwordLen { 571 | return nil, fmt.Errorf("%w: TLP buffer too short (%d), expected at least 12 bytes", ErrTooShort, len(b)) 572 | } 573 | 574 | // Verify type. 575 | var hdr TlpHeader 576 | hdr.fromBuffer(bytes.NewBuffer(b)) 577 | if hdr.Type != CplE && hdr.Type != CplD { 578 | return nil, fmt.Errorf("%w: type %x is not supported. Supported types: CplE, CplD", ErrBadType, hdr.Type) 579 | } 580 | 581 | // Decode CPL. 582 | tlp := &Cpl{} 583 | buf := bytes.NewBuffer(b) 584 | tlp.CplHeader.fromBuffer(buf) 585 | if tlp.Type == CplD { 586 | tlp.Data = make([]byte, tlp.DataLength()) 587 | if buf.Len() < len(tlp.Data) { 588 | return nil, fmt.Errorf("%w: TLP data too short (%d), expected %d bytes", ErrTooShort, buf.Len(), len(tlp.Data)) 589 | } 590 | copy(tlp.Data, buf.Next(len(tlp.Data))) 591 | } 592 | return tlp, nil 593 | } 594 | 595 | // NewCpl builds completion response. 596 | func NewCpl(cplID DeviceID, bc int, status CompletionStatus, reqID DeviceID, tag, addressLow uint8, data []byte) (*Cpl, error) { 597 | tlp := &Cpl{} 598 | tlp.CplID = cplID 599 | tlp.BC = bc 600 | tlp.Status = status 601 | tlp.ReqID = reqID 602 | tlp.Tag = tag 603 | tlp.AddressLow = addressLow 604 | 605 | if len(data) > 0 { 606 | tlp.Type = CplD 607 | if err := tlp.setLength(len(data)); err != nil { 608 | return nil, err 609 | } 610 | tlp.Data = make([]byte, len(data)) 611 | copy(tlp.Data, data) 612 | } else { 613 | tlp.Type = CplE 614 | } 615 | return tlp, nil 616 | } 617 | 618 | // See Table 2-37: Calculating Byte Count from Length and Byte Enables. 619 | func CplCalcByteCount(firstBE, lastBE, length int) int { 620 | if firstBE&0b1001 == 0b1001 && lastBE == 0b0000 { 621 | return 4 622 | } 623 | if firstBE&0b1101 == 0b0101 && lastBE == 0b0000 { 624 | return 3 625 | } 626 | if firstBE&0b1011 == 0b1010 && lastBE == 0b0000 { 627 | return 3 628 | } 629 | if firstBE == 0b0011 && lastBE == 0b0000 { 630 | return 2 631 | } 632 | if firstBE == 0b0110 && lastBE == 0b0000 { 633 | return 2 634 | } 635 | if firstBE == 0b1100 && lastBE == 0b0000 { 636 | return 2 637 | } 638 | if firstBE == 0b0001 && lastBE == 0b0000 { 639 | return 1 640 | } 641 | if firstBE == 0b0010 && lastBE == 0b0000 { 642 | return 1 643 | } 644 | if firstBE == 0b0100 && lastBE == 0b0000 { 645 | return 1 646 | } 647 | if firstBE == 0b1000 && lastBE == 0b0000 { 648 | return 1 649 | } 650 | if firstBE == 0b0000 && lastBE == 0b0000 { 651 | return 1 652 | } 653 | if firstBE&0b0001 == 0b0001 && lastBE&0b1000 == 0b1000 { 654 | return length * 4 655 | } 656 | if firstBE&0b0001 == 0b0001 && lastBE&0b1100 == 0b0100 { 657 | return length*4 - 1 658 | } 659 | if firstBE&0b0001 == 0b0001 && lastBE&0b1110 == 0b0010 { 660 | return length*4 - 2 661 | } 662 | if firstBE&0b0001 == 0b0001 && lastBE == 0b0001 { 663 | return length*4 - 3 664 | } 665 | if firstBE&0b0011 == 0b0010 && lastBE&0b1000 == 0b1000 { 666 | return length*4 - 1 667 | } 668 | if firstBE&0b0011 == 0b0010 && lastBE&0b1100 == 0b0100 { 669 | return length*4 - 2 670 | } 671 | if firstBE&0b0011 == 0b0010 && lastBE&0b1110 == 0b0010 { 672 | return length*4 - 3 673 | } 674 | if firstBE&0b0011 == 0b0010 && lastBE == 0b0001 { 675 | return length*4 - 4 676 | } 677 | if firstBE&0b0111 == 0b0100 && lastBE&0b1000 == 0b1000 { 678 | return length*4 - 2 679 | } 680 | if firstBE&0b0111 == 0b0100 && lastBE&0b1100 == 0b0100 { 681 | return length*4 - 3 682 | } 683 | if firstBE&0b0111 == 0b0100 && lastBE&0b1110 == 0b0010 { 684 | return length*4 - 4 685 | } 686 | if firstBE&0b0111 == 0b0100 && lastBE == 0b0001 { 687 | return length*4 - 5 688 | } 689 | if firstBE == 0b1000 && lastBE&0b1000 == 0b1000 { 690 | return length*4 - 3 691 | } 692 | if firstBE == 0b1000 && lastBE&0b1100 == 0b0100 { 693 | return length*4 - 4 694 | } 695 | if firstBE == 0b1000 && lastBE&0b1110 == 0b0010 { 696 | return length*4 - 5 697 | } 698 | if firstBE == 0b1000 && lastBE == 0b0001 { 699 | return length*4 - 6 700 | } 701 | return 0 702 | } 703 | 704 | // Table 2-38: Calculating Lower Address from 1st DW BE. 705 | func CplCalcLowerAddress(firstBE int, readAddress Address) byte { 706 | addr := byte(readAddress & 0x7c) 707 | if firstBE == 0b0000 { 708 | return addr + 0b00 709 | } 710 | if firstBE&0b0001 == 0b0001 { 711 | return addr + 0b00 712 | } 713 | if firstBE&0b0011 == 0b0010 { 714 | return addr + 0b01 715 | } 716 | if firstBE&0b0111 == 0b0100 { 717 | return addr + 0b10 718 | } 719 | if firstBE == 0b1000 { 720 | return addr + 0b11 721 | } 722 | return 0 723 | } 724 | 725 | // NewCplForMrd builds a completion response that matches the given memory read request. 726 | func NewCplForMrd(cplID DeviceID, status CompletionStatus, mrd *MRd, data []byte) (*Cpl, error) { 727 | if len(data) != mrd.DataLength() { 728 | return nil, fmt.Errorf("%w: buffer size (%d) does not match expected DataLength (%d)", ErrBadLength, len(data), mrd.DataLength()) 729 | } 730 | bc := CplCalcByteCount(int(mrd.FirstBE), int(mrd.LastBE), mrd.Length) 731 | addressLow := CplCalcLowerAddress(int(mrd.FirstBE), mrd.Address) 732 | return NewCpl(cplID, bc, status, mrd.ReqID, mrd.Tag, addressLow, data) 733 | } 734 | 735 | // CfgHeader extends RequestHeader and includes the third header dword 736 | // for configuration read TLPs. 737 | type CfgHeader struct { 738 | RequestHeader 739 | Target DeviceID 740 | // Register number (6b) 741 | RegisterNumber int 742 | // Extended register number (4b) 743 | ExtRegisterNumber int 744 | } 745 | 746 | func (h *CfgHeader) toBuffer(buf *bytes.Buffer) { 747 | h.RequestHeader.toBuffer(buf) 748 | 749 | dw := make([]byte, dwordLen) 750 | copy(dw[0:2], h.Target.toBytes()) 751 | dw[2] = byte(h.ExtRegisterNumber & 0xf) 752 | dw[3] = byte(h.RegisterNumber << 2) 753 | buf.Write(dw) 754 | } 755 | func (h *CfgHeader) fromBuffer(buf *bytes.Buffer) { 756 | h.RequestHeader.fromBuffer(buf) 757 | dw := buf.Next(dwordLen) 758 | h.Target.fromBytes(dw[0:2]) 759 | h.ExtRegisterNumber = int(dw[2] & 0xf) 760 | h.RegisterNumber = int(dw[3]>>2) & 0x3f 761 | } 762 | 763 | // CfgRd TLP: Configuration read request. 764 | type CfgRd struct { 765 | CfgHeader 766 | } 767 | 768 | // ToBytes encodes CfgRd to wire format. 769 | func (tlp *CfgRd) ToBytes() []byte { 770 | buf := new(bytes.Buffer) 771 | tlp.CfgHeader.toBuffer(buf) 772 | return buf.Bytes() 773 | } 774 | 775 | // NewCfgRd builds configuration read request. 776 | func NewCfgRd(reqID DeviceID, tag uint8, target DeviceID, register int) *CfgRd { 777 | tlp := &CfgRd{} 778 | // See Figure 2-18: Request Header Format for Configuration Transactions. 779 | tlp.Type = CfgRd0 780 | tlp.Length = 1 781 | tlp.ReqID = reqID 782 | tlp.Tag = tag 783 | tlp.FirstBE = 0xf 784 | tlp.LastBE = 0 785 | tlp.Target = target 786 | tlp.RegisterNumber = (register << 2) & 0x3f 787 | tlp.ExtRegisterNumber = register & 0xf 788 | return tlp 789 | } 790 | 791 | // CfgWr TLP: Configuration write request. 792 | type CfgWr struct { 793 | CfgHeader 794 | Data []byte 795 | } 796 | 797 | // ToBytes encodes CfgWr to wire format. 798 | func (tlp *CfgWr) ToBytes() []byte { 799 | buf := new(bytes.Buffer) 800 | tlp.CfgHeader.toBuffer(buf) 801 | buf.Write(tlp.Data) 802 | return buf.Bytes() 803 | } 804 | 805 | // NewCfgWr builds configuration write request. 806 | func NewCfgWr(reqID DeviceID, tag uint8, target DeviceID, register int, data [4]byte) *CfgWr { 807 | tlp := &CfgWr{} 808 | // See Figure 2-18: Request Header Format for Configuration Transactions. 809 | tlp.Type = CfgWr0 810 | tlp.Length = 1 811 | tlp.ReqID = reqID 812 | tlp.Tag = tag 813 | tlp.FirstBE = 0xf 814 | tlp.LastBE = 0 815 | tlp.Target = target 816 | tlp.RegisterNumber = (register << 2) & 0x3f 817 | tlp.ExtRegisterNumber = register & 0xf 818 | tlp.Data = make([]byte, len(data)) 819 | copy(tlp.Data, data[:]) 820 | return tlp 821 | } 822 | 823 | // NewCfgWrFromBytes builds a memory read request from a TLP buffer. 824 | func NewCfgWrFromBytes(b []byte) (*CfgWr, error) { 825 | if len(b) < 3*dwordLen { 826 | return nil, fmt.Errorf("%w: TLP buffer too short (%d), expected at least 12 bytes", ErrTooShort, len(b)) 827 | } 828 | // Verify type. 829 | var hdr TlpHeader 830 | hdr.fromBuffer(bytes.NewBuffer(b)) 831 | if hdr.Type != CfgWr0 && hdr.Type != CfgWr1 { 832 | return nil, fmt.Errorf("%w: type %x is not supported. supported types: CfgWr0, CfgWr1", ErrBadType, hdr.Type) 833 | } 834 | // Decode CfgWr. 835 | tlp := &CfgWr{} 836 | buf := bytes.NewBuffer(b) 837 | tlp.CfgHeader.fromBuffer(buf) 838 | tlp.Data = make([]byte, tlp.DataLength()) 839 | if buf.Len() < len(tlp.Data) { 840 | return nil, fmt.Errorf("%w: TLP data too short (%d), expected %d bytes", ErrTooShort, buf.Len(), len(tlp.Data)) 841 | } 842 | copy(tlp.Data, buf.Next(len(tlp.Data))) 843 | if tlp.Length != 1 { 844 | return nil, fmt.Errorf("%w: TLP bad length in request (%d), expected 1", ErrBadLength, tlp.Length) 845 | } 846 | if tlp.LastBE != 0 { 847 | return nil, fmt.Errorf("%w: TLP bad LastBE in request (%d), expected 0", ErrBadLength, tlp.LastBE) 848 | } 849 | return tlp, nil 850 | } 851 | 852 | // Returns the config space memory address. 853 | // Table 7-1: Enhanced Configuration Address Mapping. 854 | func (tlp *CfgWr) MemoryAddress() int { 855 | offset := bits.TrailingZeros8(tlp.FirstBE) 856 | return tlp.ExtRegisterNumber<<8 + tlp.RegisterNumber<<2 + offset 857 | } 858 | 859 | // Returns the first enabled data. 860 | func (tlp *CfgWr) FirstDataByte() byte { 861 | offset := bits.TrailingZeros8(tlp.FirstBE) 862 | return tlp.Data[offset] 863 | } 864 | 865 | // IORd TLP: I/O read request. 866 | type IORd struct { 867 | RequestHeader 868 | Address Address 869 | } 870 | 871 | // ToBytes encodes IORd to wire format. 872 | func (tlp *IORd) ToBytes() []byte { 873 | buf := new(bytes.Buffer) 874 | tlp.RequestHeader.toBuffer(buf) 875 | tlp.Address.toBuffer(buf) 876 | return buf.Bytes() 877 | } 878 | 879 | // NewIORd builds memory read request. 880 | // |length| is the number of BYTES to read and must be DWORD aligned. 881 | func NewIORd(reqID DeviceID, tag uint8, addr uint64, length uint32) (*IORd, error) { 882 | tlp := &IORd{} 883 | tlp.Address = Address(addr) 884 | if tlp.Address.is64() { 885 | return nil, fmt.Errorf("%w: 64bit address %x is not supported", ErrBadAddress, tlp.Address) 886 | } 887 | tlp.Type = IORdT 888 | 889 | if err := tlp.setLength(int(length)); err != nil { 890 | return nil, err 891 | } 892 | 893 | tlp.ReqID = reqID 894 | tlp.Tag = tag 895 | tlp.setByteEnables() 896 | 897 | return tlp, nil 898 | } 899 | 900 | // IOWrt TLP: Memory write request. 901 | type IOWrt struct { 902 | RequestHeader 903 | Address Address 904 | Data []byte 905 | } 906 | 907 | // ToBytes encodes IOWrt to wire format. 908 | func (tlp *IOWrt) ToBytes() []byte { 909 | buf := new(bytes.Buffer) 910 | tlp.RequestHeader.toBuffer(buf) 911 | tlp.Address.toBuffer(buf) 912 | buf.Write(tlp.Data) 913 | return buf.Bytes() 914 | } 915 | 916 | // NewIOWrt builds memory write request. 917 | // len(data) must be DW aligned. 918 | func NewIOWrt(reqID DeviceID, addr uint64, data []byte) (*IOWrt, error) { 919 | tlp := &IOWrt{} 920 | tlp.Address = Address(addr) 921 | if tlp.Address.is64() { 922 | return nil, fmt.Errorf("%w: 64bit address %x is not supported", ErrBadAddress, tlp.Address) 923 | } 924 | tlp.Type = IOWrtT 925 | 926 | if err := tlp.setLength(len(data)); err != nil { 927 | return nil, err 928 | } 929 | 930 | tlp.ReqID = reqID 931 | tlp.Tag = 0 932 | tlp.setByteEnables() 933 | 934 | tlp.Data = make([]byte, len(data)) 935 | copy(tlp.Data, data) 936 | return tlp, nil 937 | } 938 | -------------------------------------------------------------------------------- /pcie/tlp_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package pcie 16 | 17 | import ( 18 | "bytes" 19 | "errors" 20 | "testing" 21 | "testing/quick" 22 | 23 | "github.com/google/go-cmp/cmp" 24 | ) 25 | 26 | var ( 27 | rootID = DeviceID{ 28 | Bus: 0, 29 | Device: 0, 30 | Function: 0, 31 | } 32 | reqID = DeviceID{ 33 | Bus: 0x61, 34 | Device: 0, 35 | Function: 0, 36 | } 37 | ) 38 | 39 | // Round-trip Header encoding/decoding. 40 | func TestHeaderEncoding(t *testing.T) { 41 | f := func(src TlpHeader) bool { 42 | src.TC &= 0x7 43 | src.AT &= 0x3 44 | src.Length &= 0x3ff 45 | buf := new(bytes.Buffer) 46 | src.toBuffer(buf) 47 | var dst TlpHeader 48 | dst.fromBuffer(buf) 49 | return cmp.Equal(src, dst) 50 | } 51 | if err := quick.Check(f, nil); err != nil { 52 | t.Error(err) 53 | } 54 | } 55 | 56 | // Round-trip DeviceID encoding/decoding. 57 | func TestDeviceIDUint16Encoding(t *testing.T) { 58 | f := func(src DeviceID) bool { 59 | src.Device &= 0x1f 60 | src.Function &= 0x7 61 | var dst DeviceID 62 | dst.FromUint16(src.ToUint16()) 63 | return cmp.Equal(src, dst) 64 | } 65 | if err := quick.Check(f, nil); err != nil { 66 | t.Error(err) 67 | } 68 | } 69 | 70 | // Round-trip DeviceID encoding/decoding. 71 | func TestDeviceIDBytesEncoding(t *testing.T) { 72 | f := func(src DeviceID) bool { 73 | src.Device &= 0x1f 74 | src.Function &= 0x7 75 | var dst DeviceID 76 | dst.fromBytes(src.toBytes()) 77 | return cmp.Equal(src, dst) 78 | } 79 | if err := quick.Check(f, nil); err != nil { 80 | t.Error(err) 81 | } 82 | } 83 | 84 | // Round-trip DeviceID encoding/decoding. 85 | func TestDeviceIDStringEncoding(t *testing.T) { 86 | f := func(src DeviceID) bool { 87 | src.Device &= 0x1f 88 | src.Function &= 0x7 89 | var dst DeviceID 90 | dst.FromString(src.String()) 91 | return cmp.Equal(src, dst) 92 | } 93 | if err := quick.Check(f, nil); err != nil { 94 | t.Error(err) 95 | } 96 | } 97 | 98 | // Round-trip RequestHeader encoding/decoding. 99 | func TestRequestHeaderEncoding(t *testing.T) { 100 | f := func(src RequestHeader) bool { 101 | src.TC &= 0x7 102 | src.AT &= 0x3 103 | src.Length &= 0x3ff 104 | src.ReqID.Device &= 0x1f 105 | src.ReqID.Function &= 0x7 106 | src.FirstBE &= 0xf 107 | src.LastBE &= 0xf 108 | buf := new(bytes.Buffer) 109 | src.toBuffer(buf) 110 | 111 | var dst RequestHeader 112 | dst.fromBuffer(buf) 113 | return cmp.Equal(src, dst) 114 | } 115 | if err := quick.Check(f, nil); err != nil { 116 | t.Error(err) 117 | } 118 | } 119 | 120 | // Round-trip CplHeader encoding/decoding. 121 | func TestCplHeaderEncoding(t *testing.T) { 122 | f := func(src CplHeader) bool { 123 | src.TC &= 0x7 124 | src.AT &= 0x3 125 | src.Length &= 0x3ff 126 | src.CplID.Device &= 0x1f 127 | src.CplID.Function &= 0x7 128 | src.ReqID.Device &= 0x1f 129 | src.ReqID.Function &= 0x7 130 | src.Status &= 0x7 131 | src.BC &= 0xfff 132 | src.AddressLow &= 0x7f 133 | buf := new(bytes.Buffer) 134 | src.toBuffer(buf) 135 | 136 | var dst CplHeader 137 | dst.fromBuffer(buf) 138 | return cmp.Equal(src, dst) 139 | } 140 | if err := quick.Check(f, nil); err != nil { 141 | t.Error(err) 142 | } 143 | } 144 | 145 | // Round-trip MRd encoding/decoding. 146 | func TestMRdEncoding(t *testing.T) { 147 | f := func(reqID DeviceID, tag uint8, addr uint64, length uint32) bool { 148 | reqID.Device &= 0x1f 149 | reqID.Function &= 0x7 150 | addr &= ^uint64(3) 151 | length &= 0x3fc 152 | src, err := NewMRd(reqID, tag, addr, length) 153 | if err != nil { 154 | return false 155 | } 156 | dst, err := NewMRdFromBytes(src.ToBytes()) 157 | if err != nil { 158 | return false 159 | } 160 | return cmp.Equal(src, dst) 161 | } 162 | if err := quick.Check(f, nil); err != nil { 163 | t.Error(err) 164 | } 165 | } 166 | 167 | func TestMRdEncodingMatchesPciLeech(t *testing.T) { 168 | tag := uint8(0x80) 169 | addr32 := uint64(0x12000) 170 | maxLen := uint32(4096) 171 | tlp, err := NewMRd(reqID, tag, addr32, maxLen) 172 | if err != nil { 173 | t.Fatalf("NewMRd(%d, %d, %d, %d) = _, %v, want nil err", reqID, tag, addr32, maxLen, err) 174 | } 175 | 176 | // Copied from pcileech: 177 | // TX: MRd32: Len: 000 ReqID: 6100 BE_FL: ff Tag: 80 Addr: 00012000 178 | // 0000 00 00 00 00 61 00 80 ff 00 01 20 00 ....a..... . 179 | want := []byte{0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x80, 0xff, 0x00, 0x01, 0x20, 0x00} 180 | got := tlp.ToBytes() 181 | if diff := cmp.Diff(want, got); diff != "" { 182 | t.Errorf("MRd encoding with max length didn't match:\n%s", diff) 183 | } 184 | } 185 | 186 | func TestNewMRdFailsOnUnalignedLength(t *testing.T) { 187 | tag := uint8(0x80) 188 | addr32 := uint64(0x12000) 189 | unalignedLength := uint32(3) 190 | _, err := NewMRd(reqID, tag, addr32, unalignedLength) 191 | if !errors.Is(err, ErrBadLength) { 192 | t.Errorf("NewMRd(%d, %d, %d, %d) = _, %v, want ErrBadLength", reqID, tag, addr32, unalignedLength, err) 193 | } 194 | } 195 | 196 | func TestNewMRdFailsOnBigLength(t *testing.T) { 197 | tag := uint8(0x80) 198 | addr32 := uint64(0x12000) 199 | bigLength := uint32(4100) 200 | _, err := NewMRd(reqID, tag, addr32, bigLength) 201 | if !errors.Is(err, ErrBadLength) { 202 | t.Errorf("NewMRd(%d, %d, %d, %d) = _, %v, want ErrBadLength", reqID, tag, addr32, bigLength, err) 203 | } 204 | } 205 | 206 | func TestMWrEncodingMatchesPciLeech(t *testing.T) { 207 | // Copied from pcileech: 208 | // TX: MWr32: Len: 020 ReqID: 6100 BE_FL: ff Tag: 00 Addr: 0012cf80 209 | // 0000 40 00 00 20 61 00 00 ff 00 12 cf 80 41 41 41 41 @.. a.......AAAA 210 | // 0010 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA 211 | // 0020 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA 212 | // 0030 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA 213 | // 0040 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA 214 | // 0050 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA 215 | // 0060 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA 216 | // 0070 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA 217 | // 0080 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAA 218 | addr32 := uint64(0x12cf80) 219 | data := []byte{ 220 | 0x41, 0x41, 0x41, 0x41, 221 | 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 222 | 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 223 | 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 224 | 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 225 | 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 226 | 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 227 | 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 228 | 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 229 | } 230 | tlp, err := NewMWr(reqID, addr32, data) 231 | if err != nil { 232 | t.Fatalf("NewMWr(%d, %d, %X) = _, %v, want nil err", reqID, addr32, data, err) 233 | } 234 | 235 | want := []byte{ 236 | 0x40, 0x00, 0x00, 0x20, 0x61, 0x00, 0x00, 0xff, 0x00, 0x12, 0xcf, 0x80, 0x41, 0x41, 0x41, 0x41, 237 | 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 238 | 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 239 | 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 240 | 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 241 | 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 242 | 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 243 | 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 244 | 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 245 | } 246 | got := tlp.ToBytes() 247 | if diff := cmp.Diff(want, got); diff != "" { 248 | t.Errorf("MWr encoding didn't match recorded:\n%s", diff) 249 | } 250 | } 251 | 252 | // Round-trip MWr encoding/decoding. 253 | func TestMWrEncoding(t *testing.T) { 254 | f := func(reqID DeviceID, addr uint64, data []byte) bool { 255 | reqID.Device &= 0x1f 256 | reqID.Function &= 0x7 257 | addr &= ^uint64(3) 258 | if len(data) < 4 { 259 | // Empty / short data buffer not supported. 260 | return true 261 | } 262 | src, err := NewMWr(reqID, addr, data[:len(data)&0x3fc]) 263 | if err != nil { 264 | return false 265 | } 266 | dst, err := NewMWrFromBytes(src.ToBytes()) 267 | if err != nil { 268 | return false 269 | } 270 | return cmp.Equal(src, dst) 271 | } 272 | if err := quick.Check(f, nil); err != nil { 273 | t.Error(err) 274 | } 275 | } 276 | 277 | // Round-trip Cpl encoding/decoding. 278 | func TestCplEncoding(t *testing.T) { 279 | f := func(cplID DeviceID, bc int, status CompletionStatus, reqID DeviceID, tag, addressLow uint8, data []byte) bool { 280 | cplID.Device &= 0x1f 281 | cplID.Function &= 0x7 282 | bc &= 0xfff 283 | status &= 0x7 284 | reqID.Device &= 0x1f 285 | reqID.Function &= 0x7 286 | addressLow &= 0x7f 287 | src, err := NewCpl(cplID, bc, status, reqID, tag, addressLow, data[:len(data)&0x3fc]) 288 | if err != nil { 289 | return false 290 | } 291 | dst, err := NewCplFromBytes(src.ToBytes()) 292 | if err != nil { 293 | return false 294 | } 295 | return cmp.Equal(src, dst) 296 | } 297 | if err := quick.Check(f, nil); err != nil { 298 | t.Error(err) 299 | } 300 | } 301 | 302 | func TestCplDecodingMatchesPciLeech(t *testing.T) { 303 | // Copied off the wire [TLP-RX]: 304 | // 00000000 4a 00 00 04 00 00 00 10 61 00 80 00 41 41 41 41 |J.......a...AAAA| 305 | // 00000010 41 41 41 41 41 41 41 41 41 41 41 41 |AAAAAAAAAAAA| 306 | buf := []byte{ 307 | 0x4a, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x61, 0x00, 0x80, 0x00, 0x41, 0x41, 0x41, 0x41, 308 | 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41} 309 | tlp, err := NewCplFromBytes(buf) 310 | if err != nil { 311 | t.Errorf("NewCplFromBytes(%X) = _, %v, want nil err", buf, err) 312 | } 313 | if diff := cmp.Diff(rootID, tlp.CplID); diff != "" { 314 | t.Errorf("Unexpected CplID:\n%s", diff) 315 | } 316 | if diff := cmp.Diff(reqID, tlp.ReqID); diff != "" { 317 | t.Errorf("Unexpected ReqID:\n%s", diff) 318 | } 319 | if tlp.BC != tlp.Length*4 { 320 | t.Errorf("Unexpected byte count (%v)", tlp.BC) 321 | } 322 | want := []byte{0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 323 | 0x41, 0x41, 0x41, 0x41, 0x41, 0x41} 324 | if diff := cmp.Diff(want, tlp.Data); diff != "" { 325 | t.Errorf("Unexpected Data:\n%s", diff) 326 | } 327 | } 328 | 329 | func TestCplForMrd(t *testing.T) { 330 | tag := uint8(0x80) 331 | addr32 := uint64(0x12340) 332 | data := []byte{1, 2, 3, 4} 333 | mrd, err := NewMRd(reqID, tag, addr32, uint32(len(data))) 334 | if err != nil { 335 | t.Fatalf("NewMRd(%d, %d, %d, %d) = _, %v, want nil err", reqID, tag, addr32, len(data), err) 336 | } 337 | cpl, err := NewCplForMrd(rootID, SuccessfulCompletion, mrd, data) 338 | if err != nil { 339 | t.Fatalf("NewCplForMrd(%v, %d, %v, % X) = _, %v, want nil err", rootID, SuccessfulCompletion, mrd, data, err) 340 | } 341 | if cpl.AddressLow != byte(addr32&0x7f) { 342 | t.Errorf("Unexpected AddressLow (%d)", cpl.AddressLow) 343 | } 344 | if cpl.Tag != tag { 345 | t.Errorf("Unexpected tag (%d)", cpl.Tag) 346 | } 347 | if cpl.BC != len(data) { 348 | t.Errorf("Unexpected BC (%d)", cpl.BC) 349 | } 350 | if diff := cmp.Diff(data, cpl.Data); diff != "" { 351 | t.Errorf("Unexpected Data:\n%s", diff) 352 | } 353 | } 354 | 355 | func TestCplDecodingFailsOnSmallPacket(t *testing.T) { 356 | buf := []byte{ 357 | 0x4a, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x61, 0x00, 0x80} 358 | _, err := NewCplFromBytes(buf) 359 | if !errors.Is(err, ErrTooShort) { 360 | t.Errorf("NewCplFromBytes(%X) = _, %v, want ErrTooShort", buf, err) 361 | } 362 | } 363 | 364 | func TestCplDecodingFailsOnUnsupportedType(t *testing.T) { 365 | buf := []byte{ 366 | 0xcc, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x61, 0x00, 0x80, 0x00, 0x41, 0x41, 0x41, 0x41, 367 | } 368 | _, err := NewCplFromBytes(buf) 369 | if !errors.Is(err, ErrBadType) { 370 | t.Errorf("NewCplFromBytes(%X) = _, %v, want ErrBadType", buf, err) 371 | } 372 | } 373 | 374 | func TestCplDecodingFailsOnTruncatedPayload(t *testing.T) { 375 | buf := []byte{ 376 | 0x4a, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x61, 0x00, 0x80, 0x00, 0x41, 0x41, 0x41, 0x41, 377 | 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41} 378 | _, err := NewCplFromBytes(buf) 379 | if !errors.Is(err, ErrTooShort) { 380 | t.Errorf("NewCplFromBytes(%X) = _, %v, want ErrTooShort", buf, err) 381 | } 382 | } 383 | 384 | // Round-trip CfgWr encoding/decoding. 385 | func TestCfgWrEncoding(t *testing.T) { 386 | f := func(reqID DeviceID, tag uint8, target DeviceID, register int, data [4]byte) bool { 387 | reqID.Device &= 0x1f 388 | reqID.Function &= 0x7 389 | target.Device &= 0x1f 390 | target.Function &= 0x7 391 | register &= 0x3ff 392 | src := NewCfgWr(reqID, tag, target, register, data) 393 | dst, err := NewCfgWrFromBytes(src.ToBytes()) 394 | if err != nil { 395 | return false 396 | } 397 | return cmp.Equal(src, dst) 398 | } 399 | if err := quick.Check(f, nil); err != nil { 400 | t.Error(err) 401 | } 402 | } 403 | 404 | func TestIOTlpDoNotSupport64bitAddresses(t *testing.T) { 405 | addr64 := uint64(0xffffffff00012000) 406 | _, err := NewIORd(reqID, 0x80, addr64, 4) 407 | if !errors.Is(err, ErrBadAddress) { 408 | t.Errorf("NewIORd(%d, %d, %d, %d) = _, %v, want ErrBadAddress", reqID, 0x80, addr64, 4, err) 409 | } 410 | data := []byte{0x11, 0x22, 0x33, 0x44} 411 | _, err = NewIOWrt(reqID, addr64, data) 412 | if !errors.Is(err, ErrBadAddress) { 413 | t.Errorf("NewIOWrt(%d, %d, %X) = _, %v, want ErrBadAddress", reqID, addr64, data, err) 414 | } 415 | } 416 | --------------------------------------------------------------------------------