From patchwork Tue Mar 15 12:49:12 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Christophe Lombard X-Patchwork-Id: 1605618 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: bilbo.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=ibm.com header.i=@ibm.com header.a=rsa-sha256 header.s=pp1 header.b=Nq8Ot2bF; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.ozlabs.org (client-ip=112.213.38.117; helo=lists.ozlabs.org; envelope-from=skiboot-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org; receiver=) Received: from lists.ozlabs.org (lists.ozlabs.org [112.213.38.117]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by bilbo.ozlabs.org (Postfix) with ESMTPS id 4KHtYD0Y2Kz9sFq for ; Tue, 15 Mar 2022 23:50:24 +1100 (AEDT) Received: from boromir.ozlabs.org (localhost [IPv6:::1]) by lists.ozlabs.org (Postfix) with ESMTP id 4KHtYC6YqQz30Fn for ; Tue, 15 Mar 2022 23:50:23 +1100 (AEDT) Authentication-Results: lists.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=ibm.com header.i=@ibm.com header.a=rsa-sha256 header.s=pp1 header.b=Nq8Ot2bF; dkim-atps=neutral X-Original-To: skiboot@lists.ozlabs.org Delivered-To: skiboot@lists.ozlabs.org Authentication-Results: lists.ozlabs.org; spf=none (no SPF record) smtp.mailfrom=linux.vnet.ibm.com (client-ip=148.163.156.1; helo=mx0a-001b2d01.pphosted.com; envelope-from=clombard@linux.vnet.ibm.com; receiver=) Authentication-Results: lists.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=ibm.com header.i=@ibm.com header.a=rsa-sha256 header.s=pp1 header.b=Nq8Ot2bF; dkim-atps=neutral Received: from mx0a-001b2d01.pphosted.com (mx0a-001b2d01.pphosted.com [148.163.156.1]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 4KHtX61TWvz3085 for ; Tue, 15 Mar 2022 23:49:26 +1100 (AEDT) Received: from pps.filterd (m0098393.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.16.1.2/8.16.1.2) with SMTP id 22FCmVOO016100 for ; Tue, 15 Mar 2022 12:49:24 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ibm.com; h=from : to : subject : date : message-id : in-reply-to : references : content-type : content-transfer-encoding : mime-version; s=pp1; bh=k/pUd8g8KnI2gPSz53W/XuOltB5/n4Y6R1MAx7C/kcQ=; b=Nq8Ot2bF8SP2GxFTl/kPVzrOmdEj4kpHKujsK36vHvNIq1mZQVLzl1G7NZoqcjt7JzaU iK23yYlP/R1Hi/AFK/xZVLrSceY1BfSGV0bzmUS4LQDzhSEJp/nJd0QuOzPTJvYn/fyQ YL93gSFBRwDpe5hl3d0dljPlsnaAPO1gzSFd2ELE86t21D6REo4zgg1NWZy/fxFUcsnz KGhLr3iwD3NhyxanL4e9kSGrT9ztCQyTPc7m5F0PyfCpIEY3jzqKaUrGj13XTi1Dgzkg BlaVXKal+r9lxA2wff0AJQ6sK1o6Wz8b+Hj0dVrGZD5ntEE6rtLxsf/IwYh1LzekKWLe oA== Received: from ppma03ams.nl.ibm.com (62.31.33a9.ip4.static.sl-reverse.com [169.51.49.98]) by mx0a-001b2d01.pphosted.com with ESMTP id 3etu4q00gf-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT) for ; Tue, 15 Mar 2022 12:49:22 +0000 Received: from pps.filterd (ppma03ams.nl.ibm.com [127.0.0.1]) by ppma03ams.nl.ibm.com (8.16.1.2/8.16.1.2) with SMTP id 22FCn3QN024863 for ; Tue, 15 Mar 2022 12:49:20 GMT Received: from b06cxnps4076.portsmouth.uk.ibm.com (d06relay13.portsmouth.uk.ibm.com [9.149.109.198]) by ppma03ams.nl.ibm.com with ESMTP id 3et95wsxsr-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT) for ; Tue, 15 Mar 2022 12:49:19 +0000 Received: from d06av25.portsmouth.uk.ibm.com (d06av25.portsmouth.uk.ibm.com [9.149.105.61]) by b06cxnps4076.portsmouth.uk.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id 22FCnHDD48365870 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK) for ; Tue, 15 Mar 2022 12:49:17 GMT Received: from d06av25.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id A613D11C052 for ; Tue, 15 Mar 2022 12:49:17 +0000 (GMT) Received: from d06av25.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 4BE9711C04C for ; Tue, 15 Mar 2022 12:49:17 +0000 (GMT) Received: from li-ed209401-43e8-11cb-8043-c0c0b85d70f7.ibm.com.com (unknown [9.171.73.11]) by d06av25.portsmouth.uk.ibm.com (Postfix) with ESMTP for ; Tue, 15 Mar 2022 12:49:17 +0000 (GMT) From: Christophe Lombard To: skiboot@lists.ozlabs.org Date: Tue, 15 Mar 2022 13:49:12 +0100 Message-Id: <20220315124914.51569-9-clombard@linux.vnet.ibm.com> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220315124914.51569-1-clombard@linux.vnet.ibm.com> References: <20220315124914.51569-1-clombard@linux.vnet.ibm.com> X-TM-AS-GCONF: 00 X-Proofpoint-ORIG-GUID: 15JMCnKEbQF6O_mU-pFKH9GSIcAksKNE X-Proofpoint-GUID: 15JMCnKEbQF6O_mU-pFKH9GSIcAksKNE X-Proofpoint-UnRewURL: 0 URL was un-rewritten MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.205,Aquarius:18.0.850,Hydra:6.0.425,FMLib:17.11.64.514 definitions=2022-03-15_02,2022-03-15_01,2022-02-23_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 bulkscore=0 spamscore=0 lowpriorityscore=0 clxscore=1015 impostorscore=0 malwarescore=0 phishscore=0 mlxlogscore=999 mlxscore=0 priorityscore=1501 adultscore=0 suspectscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2202240000 definitions=main-2203150082 Subject: [Skiboot] [PATCH V2 08/10] libmctp: Import libmctp library handling MCTP protocol X-BeenThere: skiboot@lists.ozlabs.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Mailing list for skiboot development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: skiboot-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Skiboot" The Management Component Transport Protocol (MCTP) is a protocol defined by the DMTF Platform Management Component Intercommunications sub-team of the DMTF Pre-OS Workgroup. MCTP is designed to support communications between different intelligent hardware components that make up a platform management subsystem that is provides monitoring and control functions inside a managed system. DMTF standard "DSP2016" This library is intended to be a portable implementation of the Management Component Transport Protocol (MCTP), as defined by DMTF standard "DSP0236", plus transport binding specifications. MCTP has been designed to carry multiple types of manageability-related traffic across a common medium. The base MCTP specifications define message types for supporting the initialization and configuration of MCTP itself, and to support vendor-specific messages over MCTP. Other message types, such as message types to support a Platform Level Data Model (PLDM). The source is located here: https://github.com/openbmc/libmctp and use as is, without any update. The libmctp code is integrated into the folder ./libmctp as a set of sources, compilated if the compiler flag CONFIG_PLDM is set. Signed-off-by: Christophe Lombard --- Makefile | 2 + Makefile.main | 18 +- libmctp/LICENSE | 547 +++++++++++++++ libmctp/Makefile.inc | 22 + libmctp/README.skiboot | 15 + libmctp/alloc.c | 60 ++ libmctp/astlpc.c | 1366 ++++++++++++++++++++++++++++++++++++++ libmctp/config.h | 59 ++ libmctp/container_of.h | 9 + libmctp/core.c | 833 +++++++++++++++++++++++ libmctp/crc32.c | 25 + libmctp/crc32.h | 9 + libmctp/libmctp-alloc.h | 12 + libmctp/libmctp-astlpc.h | 54 ++ libmctp/libmctp-cmds.h | 74 +++ libmctp/libmctp-log.h | 24 + libmctp/libmctp-serial.h | 38 ++ libmctp/libmctp.h | 156 +++++ libmctp/log.c | 75 +++ libmctp/range.h | 18 + libmctp/serial.c | 359 ++++++++++ 21 files changed, 3774 insertions(+), 1 deletion(-) create mode 100644 libmctp/LICENSE create mode 100644 libmctp/Makefile.inc create mode 100644 libmctp/README.skiboot create mode 100644 libmctp/alloc.c create mode 100644 libmctp/astlpc.c create mode 100644 libmctp/config.h create mode 100644 libmctp/container_of.h create mode 100644 libmctp/core.c create mode 100644 libmctp/crc32.c create mode 100644 libmctp/crc32.h create mode 100644 libmctp/libmctp-alloc.h create mode 100644 libmctp/libmctp-astlpc.h create mode 100644 libmctp/libmctp-cmds.h create mode 100644 libmctp/libmctp-log.h create mode 100644 libmctp/libmctp-serial.h create mode 100644 libmctp/libmctp.h create mode 100644 libmctp/log.c create mode 100644 libmctp/range.h create mode 100644 libmctp/serial.c diff --git a/Makefile b/Makefile index 625f212e..23af860d 100644 --- a/Makefile +++ b/Makefile @@ -61,6 +61,8 @@ DEAD_CODE_ELIMINATION ?= 0 CONFIG_FSP?=1 # Try to build without POWER8 support CONFIG_P8?=1 +# Try to build without PLDM code +CONFIG_PLDM?=0 # # Where is the source directory, must be a full path (no ~) diff --git a/Makefile.main b/Makefile.main index 2a346a6c..738e0857 100644 --- a/Makefile.main +++ b/Makefile.main @@ -75,7 +75,7 @@ DBG=-g CPPFLAGS := -I$(SRC)/include -Iinclude -MMD -include $(SRC)/include/config.h CPPFLAGS += -I$(SRC)/libfdt -I$(SRC)/libflash -I$(SRC)/libxz -I$(SRC)/libc/include -I$(SRC) -CPPFLAGS += -I$(SRC)/libpore +CPPFLAGS += -I$(SRC)/libpore -I$(SRC)/libmctp CPPFLAGS += -D__SKIBOOT__ -nostdinc CPPFLAGS += -isystem $(shell $(CC) -print-file-name=include) CPPFLAGS += -DBITS_PER_LONG=64 @@ -91,6 +91,13 @@ else CPPFLAGS += -DHAVE_BIG_ENDIAN endif +# Also add the asm/byte-order.h style ones used by libmctp +ifeq ($(LITTLE_ENDIAN),1) +CPPFLAGS += -D__LITTLE_ENDIAN_BITFIELD +else +CPPFLAGS += -D__BIG_ENDIAN_BITFIELD +endif + ifeq ($(DEBUG),1) CPPFLAGS += -DDEBUG -DCCAN_LIST_DEBUG endif @@ -164,6 +171,9 @@ endif ifeq ($(CONFIG_P8),1) CFLAGS += -DCONFIG_P8=1 endif +ifeq ($(CONFIG_PLDM),1) +CFLAGS += -DCONFIG_PLDM=1 +endif CFLAGS += $(call try-cflag,$(CC),-Wjump-misses-init) \ $(call try-cflag,$(CC),-Wsuggest-attribute=const) \ @@ -295,6 +305,9 @@ include $(SRC)/libc/Makefile.inc include $(SRC)/ccan/Makefile.inc include $(SRC)/$(DEVSRC)/Makefile.inc include $(SRC)/libstb/Makefile.inc +ifeq ($(CONFIG_PLDM),1) +include $(SRC)/libmctp/Makefile.inc +endif # hack for travis-ci and coverity gard: @@ -319,6 +332,9 @@ all: $(TARGET).lid.stb $(TARGET).lid.xz.stb OBJS := $(ASM) $(CORE) $(HW) $(PLATFORMS) $(LIBFDT) $(LIBXZ) $(LIBFLASH) $(LIBSTB) OBJS += $(LIBC) $(CCAN) $(DEVSRC_OBJ) $(LIBPORE) +ifeq ($(CONFIG_PLDM),1) +OBJS += $(LIBMCTP) +endif OBJS_NO_VER = $(OBJS) ALL_OBJS = $(OBJS) version.o diff --git a/libmctp/LICENSE b/libmctp/LICENSE new file mode 100644 index 00000000..37fc4a76 --- /dev/null +++ b/libmctp/LICENSE @@ -0,0 +1,547 @@ +libmctp is released under a dual Apache-2.0 OR GPLv2+ license. The +texts of both licences is included below. + +------------------------------------------------------------------------------- + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +------------------------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/libmctp/Makefile.inc b/libmctp/Makefile.inc new file mode 100644 index 00000000..24d6fcf5 --- /dev/null +++ b/libmctp/Makefile.inc @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later +# Copyright 2022 IBM Corp. + +LIBMCTP_DIR ?= libmctp +SUBDIRS += $(LIBMCTP_DIR) + +LIBMCTP_OBJS = crc32.o core.o alloc.o log.o +LIBMCTP_BINDINGS ?= astlpc + +LIBMCTP_OBJS += $(LIBMCTP_BINDINGS:%=%.o) + +CFLAGS_$(LIBMCTP_DIR)/ = -I$(SRC)/ccan/endian/ \ + -DHAVE_CONFIG_H \ + -Wno-error \ + -Wno-type-limits \ + -Wno-missing-prototypes \ + -Wno-missing-declarations \ + -Wno-suggest-attribute=const + +LIBMCTP = $(LIBMCTP_DIR)/built-in.a + +$(LIBMCTP): $(LIBMCTP_OBJS:%=$(LIBMCTP_DIR)/%) diff --git a/libmctp/README.skiboot b/libmctp/README.skiboot new file mode 100644 index 00000000..d8581a70 --- /dev/null +++ b/libmctp/README.skiboot @@ -0,0 +1,15 @@ +skiboot/libmctp/ is a minimally modified version of upstream libmctp +that is distributed with the openbmc project hosted at +https://github.com/openbmc/libmctp.git + +This version is taken from libmctp.git commit f39c38575828 ("core: +Add TX/RX API that exposes message tag and tag owner ") by copying most +of files from the libmctp/ directory. + +The only modifications from the upstream source are the additions of +this file, Makefile.inc which has been derived from the original +Makefile.inc, astlpc.c (compilation error when MCTP_HAVE_FILEIO is +undefined) and the removal of several unnecessary folders and files. + +Local libmctp changes should be kept to a minimum, and submitted +upstream if possible. diff --git a/libmctp/alloc.c b/libmctp/alloc.c new file mode 100644 index 00000000..b6b6761a --- /dev/null +++ b/libmctp/alloc.c @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */ + +#include + +#include "libmctp.h" +#include "libmctp-alloc.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +struct { + void *(*m_alloc)(size_t); + void (*m_free)(void *); + void *(*m_realloc)(void *, size_t); +} alloc_ops = { +#ifdef MCTP_DEFAULT_ALLOC + malloc, + free, + realloc, +#endif +}; + +/* internal-only allocation functions */ +void *__mctp_alloc(size_t size) +{ + if (alloc_ops.m_alloc) + return alloc_ops.m_alloc(size); + if (alloc_ops.m_realloc) + return alloc_ops.m_realloc(NULL, size); + assert(0); + return NULL; +} + +void __mctp_free(void *ptr) +{ + if (alloc_ops.m_free) + alloc_ops.m_free(ptr); + else if (alloc_ops.m_realloc) + alloc_ops.m_realloc(ptr, 0); + else + assert(0); +} + +void *__mctp_realloc(void *ptr, size_t size) +{ + if (alloc_ops.m_realloc) + return alloc_ops.m_realloc(ptr, size); + assert(0); + return NULL; +} + +void mctp_set_alloc_ops(void *(*m_alloc)(size_t), + void (*m_free)(void *), + void *(m_realloc)(void *, size_t)) +{ + alloc_ops.m_alloc = m_alloc; + alloc_ops.m_free = m_free; + alloc_ops.m_realloc = m_realloc; +} diff --git a/libmctp/astlpc.c b/libmctp/astlpc.c new file mode 100644 index 00000000..407f5efc --- /dev/null +++ b/libmctp/astlpc.c @@ -0,0 +1,1366 @@ +/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#if HAVE_ENDIAN_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#define pr_fmt(x) "astlpc: " x + +#include "container_of.h" +#include "crc32.h" +#include "libmctp.h" +#include "libmctp-alloc.h" +#include "libmctp-log.h" +#include "libmctp-astlpc.h" +#include "range.h" + +#ifdef MCTP_HAVE_FILEIO + +#include +#include +#include +#include +#include + +/* kernel interface */ +static const char *kcs_path = "/dev/mctp0"; +static const char *lpc_path = "/dev/aspeed-lpc-ctrl"; + +#endif + +struct mctp_astlpc_buffer { + uint32_t offset; + uint32_t size; +}; + +struct mctp_astlpc_layout { + struct mctp_astlpc_buffer rx; + struct mctp_astlpc_buffer tx; +}; + +struct mctp_astlpc_protocol { + uint16_t version; + uint32_t (*packet_size)(uint32_t body); + uint32_t (*body_size)(uint32_t packet); + void (*pktbuf_protect)(struct mctp_pktbuf *pkt); + bool (*pktbuf_validate)(struct mctp_pktbuf *pkt); +}; + +struct mctp_binding_astlpc { + struct mctp_binding binding; + + void *lpc_map; + struct mctp_astlpc_layout layout; + + uint8_t mode; + uint32_t requested_mtu; + + const struct mctp_astlpc_protocol *proto; + + /* direct ops data */ + struct mctp_binding_astlpc_ops ops; + void *ops_data; + + /* fileio ops data */ + int kcs_fd; + uint8_t kcs_status; + + bool running; +}; + +#define binding_to_astlpc(b) \ + container_of(b, struct mctp_binding_astlpc, binding) + +#define astlpc_prlog(ctx, lvl, fmt, ...) \ + do { \ + bool __bmc = ((ctx)->mode == MCTP_BINDING_ASTLPC_MODE_BMC); \ + mctp_prlog(lvl, pr_fmt("%s: " fmt), __bmc ? "bmc" : "host", \ + ##__VA_ARGS__); \ + } while (0) + +#define astlpc_prerr(ctx, fmt, ...) \ + astlpc_prlog(ctx, MCTP_LOG_ERR, fmt, ##__VA_ARGS__) +#define astlpc_prwarn(ctx, fmt, ...) \ + astlpc_prlog(ctx, MCTP_LOG_WARNING, fmt, ##__VA_ARGS__) +#define astlpc_prinfo(ctx, fmt, ...) \ + astlpc_prlog(ctx, MCTP_LOG_INFO, fmt, ##__VA_ARGS__) +#define astlpc_prdebug(ctx, fmt, ...) \ + astlpc_prlog(ctx, MCTP_LOG_DEBUG, fmt, ##__VA_ARGS__) + +/* clang-format off */ +#define ASTLPC_MCTP_MAGIC 0x4d435450 +#define ASTLPC_VER_BAD 0 +#define ASTLPC_VER_MIN 1 + +/* Support testing of new binding protocols */ +#ifndef ASTLPC_VER_CUR +#define ASTLPC_VER_CUR 3 +#endif +/* clang-format on */ + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) +#endif + +static uint32_t astlpc_packet_size_v1(uint32_t body) +{ + assert((body + 4) > body); + + return body + 4; +} + +static uint32_t astlpc_body_size_v1(uint32_t packet) +{ + assert((packet - 4) < packet); + + return packet - 4; +} + +void astlpc_pktbuf_protect_v1(struct mctp_pktbuf *pkt) +{ + (void)pkt; +} + +bool astlpc_pktbuf_validate_v1(struct mctp_pktbuf *pkt) +{ + (void)pkt; + return true; +} + +static uint32_t astlpc_packet_size_v3(uint32_t body) +{ + assert((body + 4 + 4) > body); + + return body + 4 + 4; +} + +static uint32_t astlpc_body_size_v3(uint32_t packet) +{ + assert((packet - 4 - 4) < packet); + + return packet - 4 - 4; +} + +void astlpc_pktbuf_protect_v3(struct mctp_pktbuf *pkt) +{ + uint32_t code; + + code = htobe32(crc32(mctp_pktbuf_hdr(pkt), mctp_pktbuf_size(pkt))); + mctp_prdebug("%s: 0x%" PRIx32, __func__, code); + mctp_pktbuf_push(pkt, &code, 4); +} + +bool astlpc_pktbuf_validate_v3(struct mctp_pktbuf *pkt) +{ + uint32_t code; + void *check; + + code = be32toh(crc32(mctp_pktbuf_hdr(pkt), mctp_pktbuf_size(pkt) - 4)); + mctp_prdebug("%s: 0x%" PRIx32, __func__, code); + check = mctp_pktbuf_pop(pkt, 4); + return check && !memcmp(&code, check, 4); +} + +static const struct mctp_astlpc_protocol astlpc_protocol_version[] = { + [0] = { + .version = 0, + .packet_size = NULL, + .body_size = NULL, + .pktbuf_protect = NULL, + .pktbuf_validate = NULL, + }, + [1] = { + .version = 1, + .packet_size = astlpc_packet_size_v1, + .body_size = astlpc_body_size_v1, + .pktbuf_protect = astlpc_pktbuf_protect_v1, + .pktbuf_validate = astlpc_pktbuf_validate_v1, + }, + [2] = { + .version = 2, + .packet_size = astlpc_packet_size_v1, + .body_size = astlpc_body_size_v1, + .pktbuf_protect = astlpc_pktbuf_protect_v1, + .pktbuf_validate = astlpc_pktbuf_validate_v1, + }, + [3] = { + .version = 3, + .packet_size = astlpc_packet_size_v3, + .body_size = astlpc_body_size_v3, + .pktbuf_protect = astlpc_pktbuf_protect_v3, + .pktbuf_validate = astlpc_pktbuf_validate_v3, + }, +}; + +struct mctp_lpcmap_hdr { + uint32_t magic; + + uint16_t bmc_ver_min; + uint16_t bmc_ver_cur; + uint16_t host_ver_min; + uint16_t host_ver_cur; + uint16_t negotiated_ver; + uint16_t pad0; + + struct { + uint32_t rx_offset; + uint32_t rx_size; + uint32_t tx_offset; + uint32_t tx_size; + } layout; +} __attribute__((packed)); + +static const uint32_t control_size = 0x100; + +#define LPC_WIN_SIZE (1 * 1024 * 1024) + +#define KCS_STATUS_BMC_READY 0x80 +#define KCS_STATUS_CHANNEL_ACTIVE 0x40 +#define KCS_STATUS_IBF 0x02 +#define KCS_STATUS_OBF 0x01 + +static inline int mctp_astlpc_kcs_write(struct mctp_binding_astlpc *astlpc, + enum mctp_binding_astlpc_kcs_reg reg, + uint8_t val) +{ + return astlpc->ops.kcs_write(astlpc->ops_data, reg, val); +} + +static inline int mctp_astlpc_kcs_read(struct mctp_binding_astlpc *astlpc, + enum mctp_binding_astlpc_kcs_reg reg, + uint8_t *val) +{ + return astlpc->ops.kcs_read(astlpc->ops_data, reg, val); +} + +static inline int mctp_astlpc_lpc_write(struct mctp_binding_astlpc *astlpc, + const void *buf, long offset, + size_t len) +{ + astlpc_prdebug(astlpc, "%s: %zu bytes to 0x%lx", __func__, len, offset); + + assert(offset >= 0); + + /* Indirect access */ + if (astlpc->ops.lpc_write) { + void *data = astlpc->ops_data; + + return astlpc->ops.lpc_write(data, buf, offset, len); + } + + /* Direct mapping */ + assert(astlpc->lpc_map); + memcpy(&((char *)astlpc->lpc_map)[offset], buf, len); + + return 0; +} + +static inline int mctp_astlpc_lpc_read(struct mctp_binding_astlpc *astlpc, + void *buf, long offset, size_t len) +{ + astlpc_prdebug(astlpc, "%s: %zu bytes from 0x%lx", __func__, len, + offset); + + assert(offset >= 0); + + /* Indirect access */ + if (astlpc->ops.lpc_read) { + void *data = astlpc->ops_data; + + return astlpc->ops.lpc_read(data, buf, offset, len); + } + + /* Direct mapping */ + assert(astlpc->lpc_map); + memcpy(buf, &((char *)astlpc->lpc_map)[offset], len); + + return 0; +} + +static int mctp_astlpc_kcs_set_status(struct mctp_binding_astlpc *astlpc, + uint8_t status) +{ + uint8_t data; + int rc; + + /* Since we're setting the status register, we want the other endpoint + * to be interrupted. However, some hardware may only raise a host-side + * interrupt on an ODR event. + * So, write a dummy value of 0xff to ODR, which will ensure that an + * interrupt is triggered, and can be ignored by the host. + */ + data = 0xff; + + rc = mctp_astlpc_kcs_write(astlpc, MCTP_ASTLPC_KCS_REG_STATUS, status); + if (rc) { + astlpc_prwarn(astlpc, "KCS status write failed"); + return -1; + } + + rc = mctp_astlpc_kcs_write(astlpc, MCTP_ASTLPC_KCS_REG_DATA, data); + if (rc) { + astlpc_prwarn(astlpc, "KCS dummy data write failed"); + return -1; + } + + return 0; +} + +static int mctp_astlpc_layout_read(struct mctp_binding_astlpc *astlpc, + struct mctp_astlpc_layout *layout) +{ + struct mctp_lpcmap_hdr hdr; + int rc; + + rc = mctp_astlpc_lpc_read(astlpc, &hdr, 0, sizeof(hdr)); + if (rc < 0) + return rc; + + /* Flip the buffers as the names are defined in terms of the host */ + if (astlpc->mode == MCTP_BINDING_ASTLPC_MODE_BMC) { + layout->rx.offset = be32toh(hdr.layout.tx_offset); + layout->rx.size = be32toh(hdr.layout.tx_size); + layout->tx.offset = be32toh(hdr.layout.rx_offset); + layout->tx.size = be32toh(hdr.layout.rx_size); + } else { + assert(astlpc->mode == MCTP_BINDING_ASTLPC_MODE_HOST); + + layout->rx.offset = be32toh(hdr.layout.rx_offset); + layout->rx.size = be32toh(hdr.layout.rx_size); + layout->tx.offset = be32toh(hdr.layout.tx_offset); + layout->tx.size = be32toh(hdr.layout.tx_size); + } + + return 0; +} + +static int mctp_astlpc_layout_write(struct mctp_binding_astlpc *astlpc, + struct mctp_astlpc_layout *layout) +{ + uint32_t rx_size_be; + + if (astlpc->mode == MCTP_BINDING_ASTLPC_MODE_BMC) { + struct mctp_lpcmap_hdr hdr; + + /* + * Flip the buffers as the names are defined in terms of the + * host + */ + hdr.layout.rx_offset = htobe32(layout->tx.offset); + hdr.layout.rx_size = htobe32(layout->tx.size); + hdr.layout.tx_offset = htobe32(layout->rx.offset); + hdr.layout.tx_size = htobe32(layout->rx.size); + + return mctp_astlpc_lpc_write(astlpc, &hdr.layout, + offsetof(struct mctp_lpcmap_hdr, layout), + sizeof(hdr.layout)); + } + + assert(astlpc->mode == MCTP_BINDING_ASTLPC_MODE_HOST); + + /* + * As of v2 we only need to write rx_size - the offsets are controlled + * by the BMC, as is the BMC's rx_size (host tx_size). + */ + rx_size_be = htobe32(layout->rx.size); + return mctp_astlpc_lpc_write(astlpc, &rx_size_be, + offsetof(struct mctp_lpcmap_hdr, layout.rx_size), + sizeof(rx_size_be)); +} + +static bool +mctp_astlpc_buffer_validate(const struct mctp_binding_astlpc *astlpc, + const struct mctp_astlpc_buffer *buf, + const char *name) +{ + /* Check for overflow */ + if (buf->offset + buf->size < buf->offset) { + mctp_prerr( + "%s packet buffer parameters overflow: offset: 0x%" PRIx32 + ", size: %" PRIu32, + name, buf->offset, buf->size); + return false; + } + + /* Check that the buffers are contained within the allocated space */ + if (buf->offset + buf->size > LPC_WIN_SIZE) { + mctp_prerr( + "%s packet buffer parameters exceed %uM window size: offset: 0x%" PRIx32 + ", size: %" PRIu32, + name, (LPC_WIN_SIZE / (1024 * 1024)), buf->offset, + buf->size); + return false; + } + + /* Check that the baseline transmission unit is supported */ + if (buf->size < astlpc->proto->packet_size(MCTP_PACKET_SIZE(MCTP_BTU))) { + mctp_prerr( + "%s packet buffer too small: Require %" PRIu32 " bytes to support the %u byte baseline transmission unit, found %" PRIu32, + name, + astlpc->proto->packet_size(MCTP_PACKET_SIZE(MCTP_BTU)), + MCTP_BTU, buf->size); + return false; + } + + /* Check for overlap with the control space */ + if (buf->offset < control_size) { + mctp_prerr( + "%s packet buffer overlaps control region {0x%" PRIx32 + ", %" PRIu32 "}: Rx {0x%" PRIx32 ", %" PRIu32 "}", + name, 0U, control_size, buf->offset, buf->size); + return false; + } + + return true; +} + +static bool +mctp_astlpc_layout_validate(const struct mctp_binding_astlpc *astlpc, + const struct mctp_astlpc_layout *layout) +{ + const struct mctp_astlpc_buffer *rx = &layout->rx; + const struct mctp_astlpc_buffer *tx = &layout->tx; + bool rx_valid, tx_valid; + + rx_valid = mctp_astlpc_buffer_validate(astlpc, rx, "Rx"); + tx_valid = mctp_astlpc_buffer_validate(astlpc, tx, "Tx"); + + if (!(rx_valid && tx_valid)) + return false; + + /* Check that the buffers are disjoint */ + if ((rx->offset <= tx->offset && rx->offset + rx->size > tx->offset) || + (tx->offset <= rx->offset && tx->offset + tx->size > rx->offset)) { + mctp_prerr("Rx and Tx packet buffers overlap: Rx {0x%" PRIx32 + ", %" PRIu32 "}, Tx {0x%" PRIx32 ", %" PRIu32 "}", + rx->offset, rx->size, tx->offset, tx->size); + return false; + } + + return true; +} + +static int mctp_astlpc_init_bmc(struct mctp_binding_astlpc *astlpc) +{ + struct mctp_lpcmap_hdr hdr = { 0 }; + uint8_t status; + uint32_t sz; + + /* + * The largest buffer size is half of the allocated MCTP space + * excluding the control space. + */ + sz = ((LPC_WIN_SIZE - control_size) / 2); + + /* + * Trim the MTU to a multiple of 16 to meet the requirements of 12.17 + * Query Hop in DSP0236 v1.3.0. + */ + sz = MCTP_BODY_SIZE(astlpc->proto->body_size(sz)); + sz &= ~0xfUL; + sz = astlpc->proto->packet_size(MCTP_PACKET_SIZE(sz)); + + if (astlpc->requested_mtu) { + uint32_t rpkt, rmtu; + + rmtu = astlpc->requested_mtu; + rpkt = astlpc->proto->packet_size(MCTP_PACKET_SIZE(rmtu)); + sz = MIN(sz, rpkt); + } + + /* Flip the buffers as the names are defined in terms of the host */ + astlpc->layout.tx.offset = control_size; + astlpc->layout.tx.size = sz; + astlpc->layout.rx.offset = + astlpc->layout.tx.offset + astlpc->layout.tx.size; + astlpc->layout.rx.size = sz; + + if (!mctp_astlpc_layout_validate(astlpc, &astlpc->layout)) { + astlpc_prerr(astlpc, "Cannot support an MTU of %" PRIu32, sz); + return -EINVAL; + } + + hdr = (struct mctp_lpcmap_hdr){ + .magic = htobe32(ASTLPC_MCTP_MAGIC), + .bmc_ver_min = htobe16(ASTLPC_VER_MIN), + .bmc_ver_cur = htobe16(ASTLPC_VER_CUR), + + /* Flip the buffers back as we're now describing the host's + * configuration to the host */ + .layout.rx_offset = htobe32(astlpc->layout.tx.offset), + .layout.rx_size = htobe32(astlpc->layout.tx.size), + .layout.tx_offset = htobe32(astlpc->layout.rx.offset), + .layout.tx_size = htobe32(astlpc->layout.rx.size), + }; + + mctp_astlpc_lpc_write(astlpc, &hdr, 0, sizeof(hdr)); + + /* + * Set status indicating that the BMC is now active. Be explicit about + * clearing OBF; we're reinitialising the binding and so any previous + * buffer state is irrelevant. + */ + status = KCS_STATUS_BMC_READY & ~KCS_STATUS_OBF; + return mctp_astlpc_kcs_set_status(astlpc, status); +} + +static int mctp_binding_astlpc_start_bmc(struct mctp_binding *b) +{ + struct mctp_binding_astlpc *astlpc = + container_of(b, struct mctp_binding_astlpc, binding); + + astlpc->proto = &astlpc_protocol_version[ASTLPC_VER_CUR]; + + return mctp_astlpc_init_bmc(astlpc); +} + +static bool mctp_astlpc_validate_version(uint16_t bmc_ver_min, + uint16_t bmc_ver_cur, + uint16_t host_ver_min, + uint16_t host_ver_cur) +{ + if (!(bmc_ver_min && bmc_ver_cur && host_ver_min && host_ver_cur)) { + mctp_prerr("Invalid version present in [%" PRIu16 ", %" PRIu16 + "], [%" PRIu16 ", %" PRIu16 "]", + bmc_ver_min, bmc_ver_cur, host_ver_min, + host_ver_cur); + return false; + } else if (bmc_ver_min > bmc_ver_cur) { + mctp_prerr("Invalid bmc version range [%" PRIu16 ", %" PRIu16 + "]", + bmc_ver_min, bmc_ver_cur); + return false; + } else if (host_ver_min > host_ver_cur) { + mctp_prerr("Invalid host version range [%" PRIu16 ", %" PRIu16 + "]", + host_ver_min, host_ver_cur); + return false; + } else if ((host_ver_cur < bmc_ver_min) || + (host_ver_min > bmc_ver_cur)) { + mctp_prerr( + "Unable to satisfy version negotiation with ranges [%" PRIu16 + ", %" PRIu16 "] and [%" PRIu16 ", %" PRIu16 "]", + bmc_ver_min, bmc_ver_cur, host_ver_min, host_ver_cur); + return false; + } + + return true; +} + +static int mctp_astlpc_negotiate_layout_host(struct mctp_binding_astlpc *astlpc) +{ + struct mctp_astlpc_layout layout; + uint32_t rmtu; + uint32_t sz; + int rc; + + rc = mctp_astlpc_layout_read(astlpc, &layout); + if (rc < 0) + return rc; + + if (!mctp_astlpc_layout_validate(astlpc, &layout)) { + astlpc_prerr( + astlpc, + "BMC provided invalid buffer layout: Rx {0x%" PRIx32 + ", %" PRIu32 "}, Tx {0x%" PRIx32 ", %" PRIu32 "}", + layout.rx.offset, layout.rx.size, layout.tx.offset, + layout.tx.size); + return -EINVAL; + } + + astlpc_prinfo(astlpc, "Desire an MTU of %" PRIu32 " bytes", + astlpc->requested_mtu); + + rmtu = astlpc->requested_mtu; + sz = astlpc->proto->packet_size(MCTP_PACKET_SIZE(rmtu)); + layout.rx.size = sz; + + if (!mctp_astlpc_layout_validate(astlpc, &layout)) { + astlpc_prerr( + astlpc, + "Generated invalid buffer layout with size %" PRIu32 + ": Rx {0x%" PRIx32 ", %" PRIu32 "}, Tx {0x%" PRIx32 + ", %" PRIu32 "}", + sz, layout.rx.offset, layout.rx.size, layout.tx.offset, + layout.tx.size); + return -EINVAL; + } + + astlpc_prinfo(astlpc, "Requesting MTU of %" PRIu32 " bytes", + astlpc->requested_mtu); + + return mctp_astlpc_layout_write(astlpc, &layout); +} + +static uint16_t mctp_astlpc_negotiate_version(uint16_t bmc_ver_min, + uint16_t bmc_ver_cur, + uint16_t host_ver_min, + uint16_t host_ver_cur) +{ + if (!mctp_astlpc_validate_version(bmc_ver_min, bmc_ver_cur, + host_ver_min, host_ver_cur)) + return ASTLPC_VER_BAD; + + if (bmc_ver_cur < host_ver_cur) + return bmc_ver_cur; + + return host_ver_cur; +} + +static int mctp_astlpc_init_host(struct mctp_binding_astlpc *astlpc) +{ + const uint16_t ver_min_be = htobe16(ASTLPC_VER_MIN); + const uint16_t ver_cur_be = htobe16(ASTLPC_VER_CUR); + uint16_t bmc_ver_min, bmc_ver_cur, negotiated; + struct mctp_lpcmap_hdr hdr; + uint8_t status; + int rc; + + rc = mctp_astlpc_kcs_read(astlpc, MCTP_ASTLPC_KCS_REG_STATUS, &status); + if (rc) { + mctp_prwarn("KCS status read failed"); + return rc; + } + + astlpc->kcs_status = status; + + if (!(status & KCS_STATUS_BMC_READY)) + return -EHOSTDOWN; + + mctp_astlpc_lpc_read(astlpc, &hdr, 0, sizeof(hdr)); + + bmc_ver_min = be16toh(hdr.bmc_ver_min); + bmc_ver_cur = be16toh(hdr.bmc_ver_cur); + + /* Calculate the expected value of negotiated_ver */ + negotiated = mctp_astlpc_negotiate_version(bmc_ver_min, bmc_ver_cur, + ASTLPC_VER_MIN, + ASTLPC_VER_CUR); + if (!negotiated) { + astlpc_prerr(astlpc, "Cannot negotiate with invalid versions"); + return -EINVAL; + } + + /* Assign protocol ops so we can calculate the packet buffer sizes */ + assert(negotiated < ARRAY_SIZE(astlpc_protocol_version)); + astlpc->proto = &astlpc_protocol_version[negotiated]; + + /* Negotiate packet buffers in v2 style if the BMC supports it */ + if (negotiated >= 2) { + rc = mctp_astlpc_negotiate_layout_host(astlpc); + if (rc < 0) + return rc; + } + + /* Advertise the host's supported protocol versions */ + mctp_astlpc_lpc_write(astlpc, &ver_min_be, + offsetof(struct mctp_lpcmap_hdr, host_ver_min), + sizeof(ver_min_be)); + + mctp_astlpc_lpc_write(astlpc, &ver_cur_be, + offsetof(struct mctp_lpcmap_hdr, host_ver_cur), + sizeof(ver_cur_be)); + + /* Send channel init command */ + rc = mctp_astlpc_kcs_write(astlpc, MCTP_ASTLPC_KCS_REG_DATA, 0x0); + if (rc) { + astlpc_prwarn(astlpc, "KCS write failed"); + } + + /* + * Configure the host so `astlpc->proto->version == 0` holds until we + * receive a subsequent status update from the BMC. Until then, + * `astlpc->proto->version == 0` indicates that we're yet to complete + * the channel initialisation handshake. + * + * When the BMC provides a status update with KCS_STATUS_CHANNEL_ACTIVE + * set we will assign the appropriate protocol ops struct in accordance + * with `negotiated_ver`. + */ + astlpc->proto = &astlpc_protocol_version[ASTLPC_VER_BAD]; + + return rc; +} + +static int mctp_binding_astlpc_start_host(struct mctp_binding *b) +{ + struct mctp_binding_astlpc *astlpc = + container_of(b, struct mctp_binding_astlpc, binding); + + return mctp_astlpc_init_host(astlpc); +} + +static bool __mctp_astlpc_kcs_ready(struct mctp_binding_astlpc *astlpc, + uint8_t status, bool is_write) +{ + bool is_bmc; + bool ready_state; + uint8_t flag; + + is_bmc = (astlpc->mode == MCTP_BINDING_ASTLPC_MODE_BMC); + flag = (is_bmc ^ is_write) ? KCS_STATUS_IBF : KCS_STATUS_OBF; + ready_state = is_write ? 0 : 1; + + return !!(status & flag) == ready_state; +} + +static inline bool +mctp_astlpc_kcs_read_ready(struct mctp_binding_astlpc *astlpc, uint8_t status) +{ + return __mctp_astlpc_kcs_ready(astlpc, status, false); +} + +static inline bool +mctp_astlpc_kcs_write_ready(struct mctp_binding_astlpc *astlpc, uint8_t status) +{ + return __mctp_astlpc_kcs_ready(astlpc, status, true); +} + +static int mctp_astlpc_kcs_send(struct mctp_binding_astlpc *astlpc, + uint8_t data) +{ + uint8_t status; + int rc; + + for (;;) { + rc = mctp_astlpc_kcs_read(astlpc, MCTP_ASTLPC_KCS_REG_STATUS, + &status); + if (rc) { + astlpc_prwarn(astlpc, "KCS status read failed"); + return -1; + } + if (mctp_astlpc_kcs_write_ready(astlpc, status)) + break; + /* todo: timeout */ + } + + rc = mctp_astlpc_kcs_write(astlpc, MCTP_ASTLPC_KCS_REG_DATA, data); + if (rc) { + astlpc_prwarn(astlpc, "KCS data write failed"); + return -1; + } + + return 0; +} + +static int mctp_binding_astlpc_tx(struct mctp_binding *b, + struct mctp_pktbuf *pkt) +{ + struct mctp_binding_astlpc *astlpc = binding_to_astlpc(b); + uint32_t len, len_be; + struct mctp_hdr *hdr; + + hdr = mctp_pktbuf_hdr(pkt); + len = mctp_pktbuf_size(pkt); + + astlpc_prdebug(astlpc, + "%s: Transmitting %" PRIu32 + "-byte packet (%hhu, %hhu, 0x%hhx)", + __func__, len, hdr->src, hdr->dest, hdr->flags_seq_tag); + + if (len > astlpc->proto->body_size(astlpc->layout.tx.size)) { + astlpc_prwarn(astlpc, "invalid TX len %" PRIu32 ": %" PRIu32, len, + astlpc->proto->body_size(astlpc->layout.tx.size)); + return -1; + } + + len_be = htobe32(len); + mctp_astlpc_lpc_write(astlpc, &len_be, astlpc->layout.tx.offset, + sizeof(len_be)); + + astlpc->proto->pktbuf_protect(pkt); + len = mctp_pktbuf_size(pkt); + + mctp_astlpc_lpc_write(astlpc, hdr, astlpc->layout.tx.offset + 4, len); + + mctp_binding_set_tx_enabled(b, false); + + mctp_astlpc_kcs_send(astlpc, 0x1); + + return 0; +} + +static uint32_t mctp_astlpc_calculate_mtu(struct mctp_binding_astlpc *astlpc, + struct mctp_astlpc_layout *layout) +{ + uint32_t low, high, limit, rpkt; + + /* Derive the largest MTU the BMC _can_ support */ + low = MIN(astlpc->layout.rx.offset, astlpc->layout.tx.offset); + high = MAX(astlpc->layout.rx.offset, astlpc->layout.tx.offset); + limit = high - low; + + /* Determine the largest MTU the BMC _wants_ to support */ + if (astlpc->requested_mtu) { + uint32_t rmtu = astlpc->requested_mtu; + + rpkt = astlpc->proto->packet_size(MCTP_PACKET_SIZE(rmtu)); + limit = MIN(limit, rpkt); + } + + /* Determine the accepted MTU, applied both directions by convention */ + rpkt = MIN(limit, layout->tx.size); + return MCTP_BODY_SIZE(astlpc->proto->body_size(rpkt)); +} + +static int mctp_astlpc_negotiate_layout_bmc(struct mctp_binding_astlpc *astlpc) +{ + struct mctp_astlpc_layout proposed, pending; + uint32_t sz, mtu; + int rc; + + /* Do we have a valid protocol version? */ + if (!astlpc->proto->version) + return -EINVAL; + + /* Extract the host's proposed layout */ + rc = mctp_astlpc_layout_read(astlpc, &proposed); + if (rc < 0) + return rc; + + /* Do we have a reasonable layout? */ + if (!mctp_astlpc_layout_validate(astlpc, &proposed)) + return -EINVAL; + + /* Negotiate the MTU */ + mtu = mctp_astlpc_calculate_mtu(astlpc, &proposed); + sz = astlpc->proto->packet_size(MCTP_PACKET_SIZE(mtu)); + + /* + * Use symmetric MTUs by convention and to pass constraints in rx/tx + * functions + */ + pending = astlpc->layout; + pending.tx.size = sz; + pending.rx.size = sz; + + if (mctp_astlpc_layout_validate(astlpc, &pending)) { + /* We found a sensible Rx MTU, so honour it */ + astlpc->layout = pending; + + /* Enforce the negotiated MTU */ + rc = mctp_astlpc_layout_write(astlpc, &astlpc->layout); + if (rc < 0) + return rc; + + astlpc_prinfo(astlpc, "Negotiated an MTU of %" PRIu32 " bytes", + mtu); + } else { + astlpc_prwarn(astlpc, "MTU negotiation failed"); + return -EINVAL; + } + + if (astlpc->proto->version >= 2) + astlpc->binding.pkt_size = MCTP_PACKET_SIZE(mtu); + + return 0; +} + +static void mctp_astlpc_init_channel(struct mctp_binding_astlpc *astlpc) +{ + uint16_t negotiated, negotiated_be; + struct mctp_lpcmap_hdr hdr; + uint8_t status; + int rc; + + mctp_astlpc_lpc_read(astlpc, &hdr, 0, sizeof(hdr)); + + /* Version negotiation */ + negotiated = + mctp_astlpc_negotiate_version(ASTLPC_VER_MIN, ASTLPC_VER_CUR, + be16toh(hdr.host_ver_min), + be16toh(hdr.host_ver_cur)); + + /* MTU negotiation requires knowing which protocol we'll use */ + assert(negotiated < ARRAY_SIZE(astlpc_protocol_version)); + astlpc->proto = &astlpc_protocol_version[negotiated]; + + /* Host Rx MTU negotiation: Failure terminates channel init */ + rc = mctp_astlpc_negotiate_layout_bmc(astlpc); + if (rc < 0) + negotiated = ASTLPC_VER_BAD; + + /* Populate the negotiated version */ + negotiated_be = htobe16(negotiated); + mctp_astlpc_lpc_write(astlpc, &negotiated_be, + offsetof(struct mctp_lpcmap_hdr, negotiated_ver), + sizeof(negotiated_be)); + + /* Finalise the configuration */ + status = KCS_STATUS_BMC_READY | KCS_STATUS_OBF; + if (negotiated > 0) { + astlpc_prinfo(astlpc, "Negotiated binding version %" PRIu16, + negotiated); + status |= KCS_STATUS_CHANNEL_ACTIVE; + } else { + astlpc_prerr(astlpc, "Failed to initialise channel"); + } + + mctp_astlpc_kcs_set_status(astlpc, status); + + mctp_binding_set_tx_enabled(&astlpc->binding, + status & KCS_STATUS_CHANNEL_ACTIVE); +} + +static void mctp_astlpc_rx_start(struct mctp_binding_astlpc *astlpc) +{ + struct mctp_pktbuf *pkt; + uint32_t body, packet; + + mctp_astlpc_lpc_read(astlpc, &body, astlpc->layout.rx.offset, + sizeof(body)); + body = be32toh(body); + + if (body > astlpc->proto->body_size(astlpc->layout.rx.size)) { + astlpc_prwarn(astlpc, "invalid RX len 0x%x", body); + return; + } + + assert(astlpc->binding.pkt_size >= 0); + if (body > (uint32_t)astlpc->binding.pkt_size) { + astlpc_prwarn(astlpc, "invalid RX len 0x%x", body); + return; + } + + /* Eliminate the medium-specific header that we just read */ + packet = astlpc->proto->packet_size(body) - 4; + pkt = mctp_pktbuf_alloc(&astlpc->binding, packet); + if (!pkt) { + astlpc_prwarn(astlpc, "unable to allocate pktbuf len 0x%x", packet); + return; + } + + /* + * Read payload and medium-specific trailer from immediately after the + * medium-specific header. + */ + mctp_astlpc_lpc_read(astlpc, mctp_pktbuf_hdr(pkt), + astlpc->layout.rx.offset + 4, packet); + + /* Inform the other side of the MCTP interface that we have read + * the packet off the bus before handling the contents of the packet. + */ + mctp_astlpc_kcs_send(astlpc, 0x2); + + /* + * v3 will validate the CRC32 in the medium-specific trailer and adjust + * the packet size accordingly. On older protocols validation is a no-op + * that always returns true. + */ + if (astlpc->proto->pktbuf_validate(pkt)) { + mctp_bus_rx(&astlpc->binding, pkt); + } else { + /* TODO: Drop any associated assembly */ + mctp_pktbuf_free(pkt); + astlpc_prdebug(astlpc, "Dropped corrupt packet"); + } +} + +static void mctp_astlpc_tx_complete(struct mctp_binding_astlpc *astlpc) +{ + mctp_binding_set_tx_enabled(&astlpc->binding, true); +} + +static int mctp_astlpc_finalise_channel(struct mctp_binding_astlpc *astlpc) +{ + struct mctp_astlpc_layout layout; + uint16_t negotiated; + int rc; + + rc = mctp_astlpc_lpc_read(astlpc, &negotiated, + offsetof(struct mctp_lpcmap_hdr, + negotiated_ver), + sizeof(negotiated)); + if (rc < 0) + return rc; + + negotiated = be16toh(negotiated); + astlpc_prerr(astlpc, "Version negotiation got: %u", negotiated); + + if (negotiated == ASTLPC_VER_BAD || negotiated < ASTLPC_VER_MIN || + negotiated > ASTLPC_VER_CUR) { + astlpc_prerr(astlpc, "Failed to negotiate version, got: %u\n", + negotiated); + return -EINVAL; + } + + assert(negotiated < ARRAY_SIZE(astlpc_protocol_version)); + astlpc->proto = &astlpc_protocol_version[negotiated]; + + rc = mctp_astlpc_layout_read(astlpc, &layout); + if (rc < 0) + return rc; + + if (!mctp_astlpc_layout_validate(astlpc, &layout)) { + mctp_prerr("BMC proposed invalid buffer parameters"); + return -EINVAL; + } + + astlpc->layout = layout; + + if (negotiated >= 2) + astlpc->binding.pkt_size = + astlpc->proto->body_size(astlpc->layout.tx.size); + + return 0; +} + +static int mctp_astlpc_update_channel(struct mctp_binding_astlpc *astlpc, + uint8_t status) +{ + uint8_t updated; + int rc = 0; + + assert(astlpc->mode == MCTP_BINDING_ASTLPC_MODE_HOST); + + updated = astlpc->kcs_status ^ status; + + astlpc_prdebug(astlpc, "%s: status: 0x%x, update: 0x%x", __func__, + status, updated); + + if (updated & KCS_STATUS_BMC_READY) { + if (status & KCS_STATUS_BMC_READY) { + astlpc->kcs_status = status; + return astlpc->binding.start(&astlpc->binding); + } else { + mctp_binding_set_tx_enabled(&astlpc->binding, false); + } + } + + if (astlpc->proto->version == 0 || + updated & KCS_STATUS_CHANNEL_ACTIVE) { + bool enable; + + rc = mctp_astlpc_finalise_channel(astlpc); + enable = (status & KCS_STATUS_CHANNEL_ACTIVE) && rc == 0; + + mctp_binding_set_tx_enabled(&astlpc->binding, enable); + } + + astlpc->kcs_status = status; + + return rc; +} + +int mctp_astlpc_poll(struct mctp_binding_astlpc *astlpc) +{ + uint8_t status, data; + int rc; + + rc = mctp_astlpc_kcs_read(astlpc, MCTP_ASTLPC_KCS_REG_STATUS, &status); + if (rc) { + astlpc_prwarn(astlpc, "KCS read error"); + return -1; + } + + astlpc_prdebug(astlpc, "%s: status: 0x%hhx", __func__, status); + + if (!mctp_astlpc_kcs_read_ready(astlpc, status)) + return 0; + + rc = mctp_astlpc_kcs_read(astlpc, MCTP_ASTLPC_KCS_REG_DATA, &data); + if (rc) { + astlpc_prwarn(astlpc, "KCS data read error"); + return -1; + } + + astlpc_prdebug(astlpc, "%s: data: 0x%hhx", __func__, data); + + if (!astlpc->proto->version && !(data == 0x0 || data == 0xff)) { + astlpc_prwarn(astlpc, "Invalid message for binding state: 0x%x", + data); + return 0; + } + + switch (data) { + case 0x0: + mctp_astlpc_init_channel(astlpc); + break; + case 0x1: + mctp_astlpc_rx_start(astlpc); + break; + case 0x2: + mctp_astlpc_tx_complete(astlpc); + break; + case 0xff: + /* No responsibilities for the BMC on 0xff */ + if (astlpc->mode == MCTP_BINDING_ASTLPC_MODE_HOST) { + rc = mctp_astlpc_update_channel(astlpc, status); + if (rc < 0) + return rc; + } + break; + default: + astlpc_prwarn(astlpc, "unknown message 0x%x", data); + } + + /* Handle silent loss of bmc-ready */ + if (astlpc->mode == MCTP_BINDING_ASTLPC_MODE_HOST) { + if (!(status & KCS_STATUS_BMC_READY && data == 0xff)) + return mctp_astlpc_update_channel(astlpc, status); + } + + return rc; +} + +/* allocate and basic initialisation */ +static struct mctp_binding_astlpc *__mctp_astlpc_init(uint8_t mode, + uint32_t mtu) +{ + struct mctp_binding_astlpc *astlpc; + + assert((mode == MCTP_BINDING_ASTLPC_MODE_BMC) || + (mode == MCTP_BINDING_ASTLPC_MODE_HOST)); + + astlpc = __mctp_alloc(sizeof(*astlpc)); + if (!astlpc) + return NULL; + + memset(astlpc, 0, sizeof(*astlpc)); + astlpc->mode = mode; + astlpc->lpc_map = NULL; + astlpc->requested_mtu = mtu; + astlpc->binding.name = "astlpc"; + astlpc->binding.version = 1; + astlpc->binding.pkt_size = + MCTP_PACKET_SIZE(mtu > MCTP_BTU ? mtu : MCTP_BTU); + astlpc->binding.pkt_header = 4; + astlpc->binding.pkt_trailer = 4; + astlpc->binding.tx = mctp_binding_astlpc_tx; + if (mode == MCTP_BINDING_ASTLPC_MODE_BMC) + astlpc->binding.start = mctp_binding_astlpc_start_bmc; + else if (mode == MCTP_BINDING_ASTLPC_MODE_HOST) + astlpc->binding.start = mctp_binding_astlpc_start_host; + else { + astlpc_prerr(astlpc, "%s: Invalid mode: %d\n", __func__, mode); + __mctp_free(astlpc); + return NULL; + } + + return astlpc; +} + +struct mctp_binding *mctp_binding_astlpc_core(struct mctp_binding_astlpc *b) +{ + return &b->binding; +} + +struct mctp_binding_astlpc * +mctp_astlpc_init(uint8_t mode, uint32_t mtu, void *lpc_map, + const struct mctp_binding_astlpc_ops *ops, void *ops_data) +{ + struct mctp_binding_astlpc *astlpc; + + if (!(mode == MCTP_BINDING_ASTLPC_MODE_BMC || + mode == MCTP_BINDING_ASTLPC_MODE_HOST)) { + mctp_prerr("Unknown binding mode: %u", mode); + return NULL; + } + + astlpc = __mctp_astlpc_init(mode, mtu); + if (!astlpc) + return NULL; + + memcpy(&astlpc->ops, ops, sizeof(astlpc->ops)); + astlpc->ops_data = ops_data; + astlpc->lpc_map = lpc_map; + astlpc->mode = mode; + + return astlpc; +} + +struct mctp_binding_astlpc * +mctp_astlpc_init_ops(const struct mctp_binding_astlpc_ops *ops, void *ops_data, + void *lpc_map) +{ + return mctp_astlpc_init(MCTP_BINDING_ASTLPC_MODE_BMC, MCTP_BTU, lpc_map, + ops, ops_data); +} + +void mctp_astlpc_destroy(struct mctp_binding_astlpc *astlpc) +{ + /* Clear channel-active and bmc-ready */ + if (astlpc->mode == MCTP_BINDING_ASTLPC_MODE_BMC) + mctp_astlpc_kcs_set_status(astlpc, 0); + __mctp_free(astlpc); +} + +#ifdef MCTP_HAVE_FILEIO + +static int mctp_astlpc_init_fileio_lpc(struct mctp_binding_astlpc *astlpc) +{ + struct aspeed_lpc_ctrl_mapping map = { + .window_type = ASPEED_LPC_CTRL_WINDOW_MEMORY, + .window_id = 0, /* There's only one */ + .flags = 0, + .addr = 0, + .offset = 0, + .size = 0 + }; + void *lpc_map_base; + int fd, rc; + + fd = open(lpc_path, O_RDWR | O_SYNC); + if (fd < 0) { + astlpc_prwarn(astlpc, "LPC open (%s) failed", lpc_path); + return -1; + } + + rc = ioctl(fd, ASPEED_LPC_CTRL_IOCTL_GET_SIZE, &map); + if (rc) { + astlpc_prwarn(astlpc, "LPC GET_SIZE failed"); + close(fd); + return -1; + } + + /* + * 🚨🚨🚨 + * + * Decouple ourselves from hiomapd[1] (another user of the FW2AHB) by + * mapping the FW2AHB to the reserved memory here as well. + * + * It's not possible to use the MCTP ASTLPC binding on machines that + * need the FW2AHB bridge mapped anywhere except to the reserved memory + * (e.g. the host SPI NOR). + * + * [1] https://github.com/openbmc/hiomapd/ + * + * 🚨🚨🚨 + * + * The following calculation must align with what's going on in + * hiomapd's lpc.c so as not to disrupt its behaviour: + * + * https://github.com/openbmc/hiomapd/blob/5ff50e3cbd7702aefc185264e4adfb9952040575/lpc.c#L68 + * + * 🚨🚨🚨 + */ + + /* Map the reserved memory at the top of the 28-bit LPC firmware address space */ + map.addr = 0x0FFFFFFF & -map.size; + astlpc_prinfo(astlpc, + "Configuring FW2AHB to map reserved memory at 0x%08x for 0x%x in the LPC FW cycle address-space", + map.addr, map.size); + + rc = ioctl(fd, ASPEED_LPC_CTRL_IOCTL_MAP, &map); + if (rc) { + astlpc_prwarn(astlpc, "Failed to map FW2AHB to reserved memory"); + close(fd); + return -1; + } + + /* Map the reserved memory into our address space */ + lpc_map_base = + mmap(NULL, map.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (lpc_map_base == MAP_FAILED) { + astlpc_prwarn(astlpc, "LPC mmap failed"); + rc = -1; + } else { + astlpc->lpc_map = lpc_map_base + map.size - LPC_WIN_SIZE; + } + + close(fd); + + return rc; +} + +static int mctp_astlpc_init_fileio_kcs(struct mctp_binding_astlpc *astlpc) +{ + astlpc->kcs_fd = open(kcs_path, O_RDWR); + if (astlpc->kcs_fd < 0) + return -1; + + return 0; +} + +static int __mctp_astlpc_fileio_kcs_read(void *arg, + enum mctp_binding_astlpc_kcs_reg reg, uint8_t *val) +{ + struct mctp_binding_astlpc *astlpc = arg; + off_t offset = reg; + int rc; + + rc = pread(astlpc->kcs_fd, val, 1, offset); + + return rc == 1 ? 0 : -1; +} + +static int __mctp_astlpc_fileio_kcs_write(void *arg, + enum mctp_binding_astlpc_kcs_reg reg, uint8_t val) +{ + struct mctp_binding_astlpc *astlpc = arg; + off_t offset = reg; + int rc; + + rc = pwrite(astlpc->kcs_fd, &val, 1, offset); + + return rc == 1 ? 0 : -1; +} + +int mctp_astlpc_get_fd(struct mctp_binding_astlpc *astlpc) +{ + return astlpc->kcs_fd; +} + +struct mctp_binding_astlpc *mctp_astlpc_init_fileio(void) +{ + struct mctp_binding_astlpc *astlpc; + int rc; + + /* + * If we're doing file IO then we're very likely not running + * freestanding, so lets assume that we're on the BMC side. + * + * Requesting an MTU of 0 requests the largest possible MTU, whatever + * value that might take. + */ + astlpc = __mctp_astlpc_init(MCTP_BINDING_ASTLPC_MODE_BMC, 0); + if (!astlpc) + return NULL; + + /* Set internal operations for kcs. We use direct accesses to the lpc + * map area */ + astlpc->ops.kcs_read = __mctp_astlpc_fileio_kcs_read; + astlpc->ops.kcs_write = __mctp_astlpc_fileio_kcs_write; + astlpc->ops_data = astlpc; + + rc = mctp_astlpc_init_fileio_lpc(astlpc); + if (rc) { + free(astlpc); + return NULL; + } + + rc = mctp_astlpc_init_fileio_kcs(astlpc); + if (rc) { + free(astlpc); + return NULL; + } + + return astlpc; +} +#else +struct mctp_binding_astlpc * __attribute__((const)) + mctp_astlpc_init_fileio(void) +{ + // Missing support for file IO + return NULL; +} + +int __attribute__((const)) mctp_astlpc_get_fd( + struct mctp_binding_astlpc *astlpc __attribute__((unused))) +{ + astlpc_prerr(astlpc, "Missing support for file IO"); + return -1; +} +#endif diff --git a/libmctp/config.h b/libmctp/config.h new file mode 100644 index 00000000..3f4a6511 --- /dev/null +++ b/libmctp/config.h @@ -0,0 +1,59 @@ +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* use the ccan endian conversion functions rather than the BSD ones */ +#define HAVE_ENDIAN_H 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to use libc malloc and free for heap memory. + * + * NB: Skiboot's malloc() is defined as a macro and can't + * be used directly. + */ +#undef MCTP_DEFAULT_ALLOC + +/* Support interfaces based on file-descriptors */ +#undef MCTP_HAVE_FILEIO + +/* Define to enable stdio functions */ +#undef MCTP_HAVE_STDIO + +/* Define to enable syslog */ +#undef MCTP_HAVE_SYSLOG + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + + +#include +/* Convert POSIX / Linux errno codes used by libmctp to OPAL return codes */ +#define EHOSTDOWN (-OPAL_CLOSED) diff --git a/libmctp/container_of.h b/libmctp/container_of.h new file mode 100644 index 00000000..a0e181a5 --- /dev/null +++ b/libmctp/container_of.h @@ -0,0 +1,9 @@ +#ifndef _CONTAINER_OF_H +#define _CONTAINER_OF_H + +#ifndef container_of +#define container_of(ptr, type, member) \ + (type *)((char *)(ptr) - (char *)&((type *)0)->member) +#endif + +#endif diff --git a/libmctp/core.c b/libmctp/core.c new file mode 100644 index 00000000..c3ee659b --- /dev/null +++ b/libmctp/core.c @@ -0,0 +1,833 @@ +/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#undef pr_fmt +#define pr_fmt(fmt) "core: " fmt + +#include "libmctp.h" +#include "libmctp-alloc.h" +#include "libmctp-log.h" +#include "libmctp-cmds.h" +#include "range.h" + +/* Internal data structures */ + +enum mctp_bus_state { + mctp_bus_state_constructed = 0, + mctp_bus_state_tx_enabled, + mctp_bus_state_tx_disabled, +}; + +struct mctp_bus { + mctp_eid_t eid; + struct mctp_binding *binding; + enum mctp_bus_state state; + + struct mctp_pktbuf *tx_queue_head; + struct mctp_pktbuf *tx_queue_tail; + + /* todo: routing */ +}; + +struct mctp_msg_ctx { + uint8_t src; + uint8_t dest; + uint8_t tag; + uint8_t last_seq; + void *buf; + size_t buf_size; + size_t buf_alloc_size; + size_t fragment_size; +}; + +struct mctp { + int n_busses; + struct mctp_bus *busses; + + /* Message RX callback */ + mctp_rx_fn message_rx; + void *message_rx_data; + + /* Packet capture callback */ + mctp_capture_fn capture; + void *capture_data; + + /* Message reassembly. + * @todo: flexible context count + */ + struct mctp_msg_ctx msg_ctxs[16]; + + enum { + ROUTE_ENDPOINT, + ROUTE_BRIDGE, + } route_policy; + size_t max_message_size; +}; + +#ifndef BUILD_ASSERT +#define BUILD_ASSERT(x) \ + do { (void)sizeof(char[0-(!(x))]); } while (0) +#endif + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) +#endif + +/* 64kb should be sufficient for a single message. Applications + * requiring higher sizes can override by setting max_message_size.*/ +#ifndef MCTP_MAX_MESSAGE_SIZE +#define MCTP_MAX_MESSAGE_SIZE 65536 +#endif + +static int mctp_message_tx_on_bus(struct mctp_bus *bus, mctp_eid_t src, + mctp_eid_t dest, bool tag_owner, + uint8_t msg_tag, void *msg, size_t msg_len); + +struct mctp_pktbuf *mctp_pktbuf_alloc(struct mctp_binding *binding, size_t len) +{ + struct mctp_pktbuf *buf; + size_t size; + + size = binding->pkt_size + binding->pkt_header + binding->pkt_trailer; + + /* todo: pools */ + buf = __mctp_alloc(sizeof(*buf) + size); + + buf->size = size; + buf->start = binding->pkt_header; + buf->end = buf->start + len; + buf->mctp_hdr_off = buf->start; + buf->next = NULL; + + return buf; +} + +void mctp_pktbuf_free(struct mctp_pktbuf *pkt) +{ + __mctp_free(pkt); +} + +struct mctp_hdr *mctp_pktbuf_hdr(struct mctp_pktbuf *pkt) +{ + return (void *)pkt->data + pkt->mctp_hdr_off; +} + +void *mctp_pktbuf_data(struct mctp_pktbuf *pkt) +{ + return (void *)pkt->data + pkt->mctp_hdr_off + sizeof(struct mctp_hdr); +} + +size_t mctp_pktbuf_size(struct mctp_pktbuf *pkt) +{ + return pkt->end - pkt->start; +} + +void *mctp_pktbuf_alloc_start(struct mctp_pktbuf *pkt, size_t size) +{ + assert(size <= pkt->start); + pkt->start -= size; + return pkt->data + pkt->start; +} + +void *mctp_pktbuf_alloc_end(struct mctp_pktbuf *pkt, size_t size) +{ + void *buf; + + assert(size <= (pkt->size - pkt->end)); + buf = pkt->data + pkt->end; + pkt->end += size; + return buf; +} + +int mctp_pktbuf_push(struct mctp_pktbuf *pkt, void *data, size_t len) +{ + void *p; + + if (pkt->end + len > pkt->size) + return -1; + + p = pkt->data + pkt->end; + + pkt->end += len; + memcpy(p, data, len); + + return 0; +} + +void *mctp_pktbuf_pop(struct mctp_pktbuf *pkt, size_t len) +{ + if (len > mctp_pktbuf_size(pkt)) + return NULL; + + pkt->end -= len; + return pkt->data + pkt->end; +} + +/* Message reassembly */ +static struct mctp_msg_ctx *mctp_msg_ctx_lookup(struct mctp *mctp, + uint8_t src, uint8_t dest, uint8_t tag) +{ + unsigned int i; + + /* @todo: better lookup, if we add support for more outstanding + * message contexts */ + for (i = 0; i < ARRAY_SIZE(mctp->msg_ctxs); i++) { + struct mctp_msg_ctx *ctx = &mctp->msg_ctxs[i]; + if (ctx->src == src && ctx->dest == dest && ctx->tag == tag) + return ctx; + } + + return NULL; +} + +static struct mctp_msg_ctx *mctp_msg_ctx_create(struct mctp *mctp, + uint8_t src, uint8_t dest, uint8_t tag) +{ + struct mctp_msg_ctx *ctx = NULL; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(mctp->msg_ctxs); i++) { + struct mctp_msg_ctx *tmp = &mctp->msg_ctxs[i]; + if (!tmp->src) { + ctx = tmp; + break; + } + } + + if (!ctx) + return NULL; + + ctx->src = src; + ctx->dest = dest; + ctx->tag = tag; + ctx->buf_size = 0; + + return ctx; +} + +static void mctp_msg_ctx_drop(struct mctp_msg_ctx *ctx) +{ + ctx->src = 0; +} + +static void mctp_msg_ctx_reset(struct mctp_msg_ctx *ctx) +{ + ctx->buf_size = 0; + ctx->fragment_size = 0; +} + +static int mctp_msg_ctx_add_pkt(struct mctp_msg_ctx *ctx, + struct mctp_pktbuf *pkt, size_t max_size) +{ + size_t len; + + len = mctp_pktbuf_size(pkt) - sizeof(struct mctp_hdr); + + if (len + ctx->buf_size < ctx->buf_size) { + return -1; + } + + if (ctx->buf_size + len > ctx->buf_alloc_size) { + size_t new_alloc_size; + void *lbuf; + + /* @todo: finer-grained allocation */ + if (!ctx->buf_alloc_size) { + new_alloc_size = MAX(len, 4096UL); + } else { + new_alloc_size = MAX(ctx->buf_alloc_size * 2, len + ctx->buf_size); + } + + /* Don't allow heap to grow beyond a limit */ + if (new_alloc_size > max_size) + return -1; + + + lbuf = __mctp_realloc(ctx->buf, new_alloc_size); + if (lbuf) { + ctx->buf = lbuf; + ctx->buf_alloc_size = new_alloc_size; + } else { + __mctp_free(ctx->buf); + return -1; + } + } + + memcpy(ctx->buf + ctx->buf_size, mctp_pktbuf_data(pkt), len); + ctx->buf_size += len; + + return 0; +} + +/* Core API functions */ +struct mctp *mctp_init(void) +{ + struct mctp *mctp; + + mctp = __mctp_alloc(sizeof(*mctp)); + + if(!mctp) + return NULL; + + memset(mctp, 0, sizeof(*mctp)); + mctp->max_message_size = MCTP_MAX_MESSAGE_SIZE; + + return mctp; +} + +void mctp_set_max_message_size(struct mctp *mctp, size_t message_size) +{ + mctp->max_message_size = message_size; +} + +void mctp_set_capture_handler(struct mctp *mctp, mctp_capture_fn fn, void *user) +{ + mctp->capture = fn; + mctp->capture_data = user; +} + +static void mctp_bus_destroy(struct mctp_bus *bus) +{ + while (bus->tx_queue_head) { + struct mctp_pktbuf *curr = bus->tx_queue_head; + + bus->tx_queue_head = curr->next; + mctp_pktbuf_free(curr); + } +} + +void mctp_destroy(struct mctp *mctp) +{ + size_t i; + + /* Cleanup message assembly contexts */ + BUILD_ASSERT(ARRAY_SIZE(mctp->msg_ctxs) < SIZE_MAX); + for (i = 0; i < ARRAY_SIZE(mctp->msg_ctxs); i++) { + struct mctp_msg_ctx *tmp = &mctp->msg_ctxs[i]; + if (tmp->buf) + __mctp_free(tmp->buf); + } + + while (mctp->n_busses--) + mctp_bus_destroy(&mctp->busses[mctp->n_busses]); + + __mctp_free(mctp->busses); + __mctp_free(mctp); +} + +int mctp_set_rx_all(struct mctp *mctp, mctp_rx_fn fn, void *data) +{ + mctp->message_rx = fn; + mctp->message_rx_data = data; + return 0; +} + +static struct mctp_bus *find_bus_for_eid(struct mctp *mctp, + mctp_eid_t dest __attribute__((unused))) +{ + if (mctp->n_busses == 0) + return NULL; + + /* for now, just use the first bus. For full routing support, + * we will need a table of neighbours */ + return &mctp->busses[0]; +} + +int mctp_register_bus(struct mctp *mctp, + struct mctp_binding *binding, + mctp_eid_t eid) +{ + int rc = 0; + + /* todo: multiple busses */ + assert(mctp->n_busses == 0); + mctp->n_busses = 1; + + mctp->busses = __mctp_alloc(sizeof(struct mctp_bus)); + if (!mctp->busses) + return -ENOMEM; + + memset(mctp->busses, 0, sizeof(struct mctp_bus)); + mctp->busses[0].binding = binding; + mctp->busses[0].eid = eid; + binding->bus = &mctp->busses[0]; + binding->mctp = mctp; + mctp->route_policy = ROUTE_ENDPOINT; + + if (binding->start) { + rc = binding->start(binding); + if (rc < 0) { + mctp_prerr("Failed to start binding: %d", rc); + binding->bus = NULL; + __mctp_free(mctp->busses); + mctp->busses = NULL; + mctp->n_busses = 0; + } + } + + return rc; +} + +void mctp_unregister_bus(struct mctp *mctp, struct mctp_binding *binding) +{ + /* + * We only support one bus right now; once the call completes we will + * have no more busses + */ + mctp->n_busses = 0; + binding->mctp = NULL; + binding->bus = NULL; + free(mctp->busses); +} + +int mctp_bridge_busses(struct mctp *mctp, + struct mctp_binding *b1, struct mctp_binding *b2) +{ + int rc = 0; + + assert(mctp->n_busses == 0); + mctp->busses = __mctp_alloc(2 * sizeof(struct mctp_bus)); + if (!mctp->busses) + return -ENOMEM; + memset(mctp->busses, 0, 2 * sizeof(struct mctp_bus)); + mctp->n_busses = 2; + mctp->busses[0].binding = b1; + b1->bus = &mctp->busses[0]; + b1->mctp = mctp; + mctp->busses[1].binding = b2; + b2->bus = &mctp->busses[1]; + b2->mctp = mctp; + + mctp->route_policy = ROUTE_BRIDGE; + + if (b1->start) { + rc = b1->start(b1); + if (rc < 0) { + mctp_prerr("Failed to start bridged bus %s: %d", + b1->name, rc); + goto done; + } + } + + if (b2->start) { + rc = b2->start(b2); + if (rc < 0) { + mctp_prerr("Failed to start bridged bus %s: %d", + b2->name, rc); + goto done; + } + } + +done: + return rc; +} + +static inline bool mctp_ctrl_cmd_is_transport(struct mctp_ctrl_msg_hdr *hdr) +{ + return ((hdr->command_code >= MCTP_CTRL_CMD_FIRST_TRANSPORT) && + (hdr->command_code <= MCTP_CTRL_CMD_LAST_TRANSPORT)); +} + +static bool mctp_ctrl_handle_msg(struct mctp_bus *bus, mctp_eid_t src, + uint8_t msg_tag, bool tag_owner, void *buffer, + size_t length) +{ + struct mctp_ctrl_msg_hdr *msg_hdr = buffer; + + /* + * Control message is received. If a transport control message handler + * is provided, it will called. If there is no dedicated handler, this + * function returns false and data can be handled by the generic + * message handler. The transport control message handler will be + * provided with messages in the command range 0xF0 - 0xFF. + */ + if (mctp_ctrl_cmd_is_transport(msg_hdr)) { + if (bus->binding->control_rx != NULL) { + /* MCTP bus binding handler */ + bus->binding->control_rx(src, msg_tag, tag_owner, + bus->binding->control_rx_data, + buffer, length); + return true; + } + } + + /* + * Command was not handled, due to lack of specific callback. + * It will be passed to regular message_rx handler. + */ + return false; +} + +static inline bool mctp_rx_dest_is_local(struct mctp_bus *bus, mctp_eid_t dest) +{ + return dest == bus->eid || dest == MCTP_EID_NULL || + dest == MCTP_EID_BROADCAST; +} + +static inline bool mctp_ctrl_cmd_is_request(struct mctp_ctrl_msg_hdr *hdr) +{ + return hdr->ic_msg_type == MCTP_CTRL_HDR_MSG_TYPE && + hdr->rq_dgram_inst & MCTP_CTRL_HDR_FLAG_REQUEST; +} + +/* + * Receive the complete MCTP message and route it. + * Asserts: + * 'buf' is not NULL. + */ +static void mctp_rx(struct mctp *mctp, struct mctp_bus *bus, mctp_eid_t src, + mctp_eid_t dest, bool tag_owner, uint8_t msg_tag, void *buf, + size_t len) +{ + assert(buf != NULL); + + if (mctp->route_policy == ROUTE_ENDPOINT && + mctp_rx_dest_is_local(bus, dest)) { + /* Handle MCTP Control Messages: */ + if (len >= sizeof(struct mctp_ctrl_msg_hdr)) { + struct mctp_ctrl_msg_hdr *msg_hdr = buf; + + /* + * Identify if this is a control request message. + * See DSP0236 v1.3.0 sec. 11.5. + */ + if (mctp_ctrl_cmd_is_request(msg_hdr)) { + bool handled; + handled = mctp_ctrl_handle_msg( + bus, src, msg_tag, tag_owner, buf, len); + if (handled) + return; + } + } + + if (mctp->message_rx) + mctp->message_rx(src, tag_owner, msg_tag, + mctp->message_rx_data, buf, len); + } + + if (mctp->route_policy == ROUTE_BRIDGE) { + int i; + + for (i = 0; i < mctp->n_busses; i++) { + struct mctp_bus *dest_bus = &mctp->busses[i]; + if (dest_bus == bus) + continue; + + mctp_message_tx_on_bus(dest_bus, src, dest, tag_owner, + msg_tag, buf, len); + } + + } +} + +void mctp_bus_rx(struct mctp_binding *binding, struct mctp_pktbuf *pkt) +{ + struct mctp_bus *bus = binding->bus; + struct mctp *mctp = binding->mctp; + uint8_t flags, exp_seq, seq, tag; + struct mctp_msg_ctx *ctx; + struct mctp_hdr *hdr; + bool tag_owner; + size_t len; + void *p; + int rc; + + assert(bus); + + /* Drop packet if it was smaller than mctp hdr size */ + if (mctp_pktbuf_size(pkt) <= sizeof(struct mctp_hdr)) + goto out; + + if (mctp->capture) + mctp->capture(pkt, mctp->capture_data); + + hdr = mctp_pktbuf_hdr(pkt); + + /* small optimisation: don't bother reassembly if we're going to + * drop the packet in mctp_rx anyway */ + if (mctp->route_policy == ROUTE_ENDPOINT && hdr->dest != bus->eid) + goto out; + + flags = hdr->flags_seq_tag & (MCTP_HDR_FLAG_SOM | MCTP_HDR_FLAG_EOM); + tag = (hdr->flags_seq_tag >> MCTP_HDR_TAG_SHIFT) & MCTP_HDR_TAG_MASK; + seq = (hdr->flags_seq_tag >> MCTP_HDR_SEQ_SHIFT) & MCTP_HDR_SEQ_MASK; + tag_owner = + (hdr->flags_seq_tag >> MCTP_HDR_TO_SHIFT) & MCTP_HDR_TO_MASK; + + switch (flags) { + case MCTP_HDR_FLAG_SOM | MCTP_HDR_FLAG_EOM: + /* single-packet message - send straight up to rx function, + * no need to create a message context */ + len = pkt->end - pkt->mctp_hdr_off - sizeof(struct mctp_hdr); + p = pkt->data + pkt->mctp_hdr_off + sizeof(struct mctp_hdr); + mctp_rx(mctp, bus, hdr->src, hdr->dest, tag_owner, tag, p, len); + break; + + case MCTP_HDR_FLAG_SOM: + /* start of a new message - start the new context for + * future message reception. If an existing context is + * already present, drop it. */ + ctx = mctp_msg_ctx_lookup(mctp, hdr->src, hdr->dest, tag); + if (ctx) { + mctp_msg_ctx_reset(ctx); + } else { + ctx = mctp_msg_ctx_create(mctp, + hdr->src, hdr->dest, tag); + /* If context creation fails due to exhaution of contexts we + * can support, drop the packet */ + if (!ctx) { + mctp_prdebug("Context buffers exhausted."); + goto out; + } + } + + /* Save the fragment size, subsequent middle fragments + * should of the same size */ + ctx->fragment_size = mctp_pktbuf_size(pkt); + + rc = mctp_msg_ctx_add_pkt(ctx, pkt, mctp->max_message_size); + if (rc) { + mctp_msg_ctx_drop(ctx); + } else { + ctx->last_seq = seq; + } + + break; + + case MCTP_HDR_FLAG_EOM: + ctx = mctp_msg_ctx_lookup(mctp, hdr->src, hdr->dest, tag); + if (!ctx) + goto out; + + exp_seq = (ctx->last_seq + 1) % 4; + + if (exp_seq != seq) { + mctp_prdebug( + "Sequence number %d does not match expected %d", + seq, exp_seq); + mctp_msg_ctx_drop(ctx); + goto out; + } + + len = mctp_pktbuf_size(pkt); + + if (len > ctx->fragment_size) { + mctp_prdebug("Unexpected fragment size. Expected" \ + " less than %zu, received = %zu", + ctx->fragment_size, len); + mctp_msg_ctx_drop(ctx); + goto out; + } + + rc = mctp_msg_ctx_add_pkt(ctx, pkt, mctp->max_message_size); + if (!rc) + mctp_rx(mctp, bus, ctx->src, ctx->dest, tag_owner, tag, + ctx->buf, ctx->buf_size); + + mctp_msg_ctx_drop(ctx); + break; + + case 0: + /* Neither SOM nor EOM */ + ctx = mctp_msg_ctx_lookup(mctp, hdr->src,hdr->dest, tag); + if (!ctx) + goto out; + + exp_seq = (ctx->last_seq + 1) % 4; + if (exp_seq != seq) { + mctp_prdebug( + "Sequence number %d does not match expected %d", + seq, exp_seq); + mctp_msg_ctx_drop(ctx); + goto out; + } + + len = mctp_pktbuf_size(pkt); + + if (len != ctx->fragment_size) { + mctp_prdebug("Unexpected fragment size. Expected = %zu " \ + "received = %zu", ctx->fragment_size, len); + mctp_msg_ctx_drop(ctx); + goto out; + } + + rc = mctp_msg_ctx_add_pkt(ctx, pkt, mctp->max_message_size); + if (rc) { + mctp_msg_ctx_drop(ctx); + goto out; + } + ctx->last_seq = seq; + + break; + } +out: + mctp_pktbuf_free(pkt); +} + +static int mctp_packet_tx(struct mctp_bus *bus, + struct mctp_pktbuf *pkt) +{ + struct mctp *mctp = bus->binding->mctp; + + if (bus->state != mctp_bus_state_tx_enabled) + return -1; + + if (mctp->capture) + mctp->capture(pkt, mctp->capture_data); + + return bus->binding->tx(bus->binding, pkt); +} + +static void mctp_send_tx_queue(struct mctp_bus *bus) +{ + struct mctp_pktbuf *pkt; + + while ((pkt = bus->tx_queue_head)) { + int rc; + + rc = mctp_packet_tx(bus, pkt); + if (rc) + break; + + bus->tx_queue_head = pkt->next; + mctp_pktbuf_free(pkt); + } + + if (!bus->tx_queue_head) + bus->tx_queue_tail = NULL; + +} + +void mctp_binding_set_tx_enabled(struct mctp_binding *binding, bool enable) +{ + struct mctp_bus *bus = binding->bus; + + switch(bus->state) { + case mctp_bus_state_constructed: + if (!enable) + return; + + if (binding->pkt_size < MCTP_PACKET_SIZE(MCTP_BTU)) { + mctp_prerr("Cannot start %s binding with invalid MTU: %zu", + binding->name, + MCTP_BODY_SIZE(binding->pkt_size)); + return; + } + + bus->state = mctp_bus_state_tx_enabled; + mctp_prinfo("%s binding started", binding->name); + return; + case mctp_bus_state_tx_enabled: + if (enable) + return; + + bus->state = mctp_bus_state_tx_disabled; + mctp_prdebug("%s binding Tx disabled", binding->name); + return; + case mctp_bus_state_tx_disabled: + if (!enable) + return; + + bus->state = mctp_bus_state_tx_enabled; + mctp_prdebug("%s binding Tx enabled", binding->name); + mctp_send_tx_queue(bus); + return; + } +} + +static int mctp_message_tx_on_bus(struct mctp_bus *bus, mctp_eid_t src, + mctp_eid_t dest, bool tag_owner, + uint8_t msg_tag, void *msg, size_t msg_len) +{ + size_t max_payload_len, payload_len, p; + struct mctp_pktbuf *pkt; + struct mctp_hdr *hdr; + int i; + + if (bus->state == mctp_bus_state_constructed) + return -ENXIO; + + if ((msg_tag & MCTP_HDR_TAG_MASK) != msg_tag) + return -EINVAL; + + max_payload_len = MCTP_BODY_SIZE(bus->binding->pkt_size); + + { + const bool valid_mtu = max_payload_len >= MCTP_BTU; + assert(valid_mtu); + if (!valid_mtu) + return -EINVAL; + } + + mctp_prdebug("%s: Generating packets for transmission of %zu byte message from %hhu to %hhu", + __func__, msg_len, src, dest); + + /* queue up packets, each of max MCTP_MTU size */ + for (p = 0, i = 0; p < msg_len; i++) { + payload_len = msg_len - p; + if (payload_len > max_payload_len) + payload_len = max_payload_len; + + pkt = mctp_pktbuf_alloc(bus->binding, + payload_len + sizeof(*hdr)); + hdr = mctp_pktbuf_hdr(pkt); + + hdr->ver = bus->binding->version & 0xf; + hdr->dest = dest; + hdr->src = src; + hdr->flags_seq_tag = (tag_owner << MCTP_HDR_TO_SHIFT) | + (msg_tag << MCTP_HDR_TAG_SHIFT); + + if (i == 0) + hdr->flags_seq_tag |= MCTP_HDR_FLAG_SOM; + if (p + payload_len >= msg_len) + hdr->flags_seq_tag |= MCTP_HDR_FLAG_EOM; + hdr->flags_seq_tag |= + (i & MCTP_HDR_SEQ_MASK) << MCTP_HDR_SEQ_SHIFT; + + memcpy(mctp_pktbuf_data(pkt), msg + p, payload_len); + + /* add to tx queue */ + if (bus->tx_queue_tail) + bus->tx_queue_tail->next = pkt; + else + bus->tx_queue_head = pkt; + bus->tx_queue_tail = pkt; + + p += payload_len; + } + + mctp_prdebug("%s: Enqueued %d packets", __func__, i); + + mctp_send_tx_queue(bus); + + return 0; +} + +int mctp_message_tx(struct mctp *mctp, mctp_eid_t eid, bool tag_owner, + uint8_t msg_tag, void *msg, size_t msg_len) +{ + struct mctp_bus *bus; + + /* TODO: Protect against same tag being used across + * different callers */ + if ((msg_tag & MCTP_HDR_TAG_MASK) != msg_tag) { + mctp_prerr("Incorrect message tag %u passed.", msg_tag); + return -EINVAL; + } + + bus = find_bus_for_eid(mctp, eid); + if (!bus) + return 0; + + return mctp_message_tx_on_bus(bus, bus->eid, eid, tag_owner, msg_tag, + msg, msg_len); +} diff --git a/libmctp/crc32.c b/libmctp/crc32.c new file mode 100644 index 00000000..b5883c28 --- /dev/null +++ b/libmctp/crc32.c @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: Apache-2.0 */ +/* Copyright 2021 IBM Corp. */ + +#include "crc32.h" + +#include + +/* Very dumb CRC-32 implementation */ +uint32_t crc32(const void *buf, size_t len) +{ + const uint8_t *buf8 = buf; + uint32_t rem = 0xffffffff; + + for (; len; len--) { + int i; + + rem = rem ^ *buf8; + for (i = 0; i < CHAR_BIT; i++) + rem = (rem >> 1) ^ ((rem & 1) * 0xEDB88320); + + buf8++; + } + + return rem ^ 0xffffffff; +} diff --git a/libmctp/crc32.h b/libmctp/crc32.h new file mode 100644 index 00000000..c47a27ea --- /dev/null +++ b/libmctp/crc32.h @@ -0,0 +1,9 @@ +#ifndef _CRC32_H +#define _CRC32_H + +#include +#include + +uint32_t crc32(const void *buf, size_t len); + +#endif diff --git a/libmctp/libmctp-alloc.h b/libmctp/libmctp-alloc.h new file mode 100644 index 00000000..2532cfa9 --- /dev/null +++ b/libmctp/libmctp-alloc.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */ + +#ifndef _LIBMCTP_ALLOC_H +#define _LIBMCTP_ALLOC_H + +#include + +void *__mctp_alloc(size_t size); +void __mctp_free(void *ptr); +void *__mctp_realloc(void *ptr, size_t size); + +#endif /* _LIBMCTP_ALLOC_H */ diff --git a/libmctp/libmctp-astlpc.h b/libmctp/libmctp-astlpc.h new file mode 100644 index 00000000..f324b902 --- /dev/null +++ b/libmctp/libmctp-astlpc.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */ + +#ifndef _LIBMCTP_ASTLPCL_H +#define _LIBMCTP_ASTLPCL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +struct mctp_binding_astlpc; + +/* todo: Remove enum from public interfaces */ +enum mctp_binding_astlpc_kcs_reg { + MCTP_ASTLPC_KCS_REG_DATA = 0, + MCTP_ASTLPC_KCS_REG_STATUS = 1, +}; + +struct mctp_binding_astlpc_ops { + int (*kcs_read)(void *data, enum mctp_binding_astlpc_kcs_reg reg, + uint8_t *val); + int (*kcs_write)(void *data, enum mctp_binding_astlpc_kcs_reg reg, + uint8_t val); + int (*lpc_read)(void *data, void *buf, long offset, size_t len); + int (*lpc_write)(void *data, const void *buf, long offset, size_t len); +}; + +#define MCTP_BINDING_ASTLPC_MODE_BMC 0 +#define MCTP_BINDING_ASTLPC_MODE_HOST 1 +struct mctp_binding_astlpc * +mctp_astlpc_init(uint8_t mode, uint32_t mtu, void *lpc_map, + const struct mctp_binding_astlpc_ops *ops, void *ops_data); + +struct mctp_binding_astlpc * +mctp_astlpc_init_ops(const struct mctp_binding_astlpc_ops *ops, void *ops_data, + void *lpc_map); +void mctp_astlpc_destroy(struct mctp_binding_astlpc *astlpc); + +struct mctp_binding *mctp_binding_astlpc_core(struct mctp_binding_astlpc *b); + +int mctp_astlpc_poll(struct mctp_binding_astlpc *astlpc); + +/* fileio-based interface */ +struct mctp_binding_astlpc *mctp_astlpc_init_fileio(void); +int mctp_astlpc_get_fd(struct mctp_binding_astlpc *astlpc); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBMCTP_ASTLPC_H */ diff --git a/libmctp/libmctp-cmds.h b/libmctp/libmctp-cmds.h new file mode 100644 index 00000000..293aca4d --- /dev/null +++ b/libmctp/libmctp-cmds.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */ +#ifndef _LIBMCTP_CMDS_H +#define _LIBMCTP_CMDS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "libmctp.h" + +/* + * Helper structs and functions for MCTP control messages. + * See DSP0236 v1.3.0 sec. 11 for reference. + */ + +struct mctp_ctrl_msg_hdr { + uint8_t ic_msg_type; + uint8_t rq_dgram_inst; + uint8_t command_code; + uint8_t completion_code; +}; + +#define MCTP_CTRL_HDR_MSG_TYPE 0 +#define MCTP_CTRL_HDR_FLAG_REQUEST (1 << 7) +#define MCTP_CTRL_HDR_FLAG_DGRAM (1 << 6) +#define MCTP_CTRL_HDR_INSTANCE_ID_MASK 0x1F + +/* + * MCTP Control Command IDs + * See DSP0236 v1.3.0 Table 12. + */ +#define MCTP_CTRL_CMD_RESERVED 0x00 +#define MCTP_CTRL_CMD_SET_ENDPOINT_ID 0x01 +#define MCTP_CTRL_CMD_GET_ENDPOINT_ID 0x02 +#define MCTP_CTRL_CMD_GET_ENDPOINT_UUID 0x03 +#define MCTP_CTRL_CMD_GET_VERSION_SUPPORT 0x04 +#define MCTP_CTRL_CMD_GET_MESSAGE_TYPE_SUPPORT 0x05 +#define MCTP_CTRL_CMD_GET_VENDOR_MESSAGE_SUPPORT 0x06 +#define MCTP_CTRL_CMD_RESOLVE_ENDPOINT_ID 0x07 +#define MCTP_CTRL_CMD_ALLOCATE_ENDPOINT_IDS 0x08 +#define MCTP_CTRL_CMD_ROUTING_INFO_UPDATE 0x09 +#define MCTP_CTRL_CMD_GET_ROUTING_TABLE_ENTRIES 0x0A +#define MCTP_CTRL_CMD_PREPARE_ENDPOINT_DISCOVERY 0x0B +#define MCTP_CTRL_CMD_ENDPOINT_DISCOVERY 0x0C +#define MCTP_CTRL_CMD_DISCOVERY_NOTIFY 0x0D +#define MCTP_CTRL_CMD_GET_NETWORK_ID 0x0E +#define MCTP_CTRL_CMD_QUERY_HOP 0x0F +#define MCTP_CTRL_CMD_RESOLVE_UUID 0x10 +#define MCTP_CTRL_CMD_QUERY_RATE_LIMIT 0x11 +#define MCTP_CTRL_CMD_REQUEST_TX_RATE_LIMIT 0x12 +#define MCTP_CTRL_CMD_UPDATE_RATE_LIMIT 0x13 +#define MCTP_CTRL_CMD_QUERY_SUPPORTED_INTERFACES 0x14 +#define MCTP_CTRL_CMD_MAX 0x15 +/* 0xF0 - 0xFF are transport specific */ +#define MCTP_CTRL_CMD_FIRST_TRANSPORT 0xF0 +#define MCTP_CTRL_CMD_LAST_TRANSPORT 0xFF + +/* + * MCTP Control Completion Codes + * See DSP0236 v1.3.0 Table 13. + */ +#define MCTP_CTRL_CC_SUCCESS 0x00 +#define MCTP_CTRL_CC_ERROR 0x01 +#define MCTP_CTRL_CC_ERROR_INVALID_DATA 0x02 +#define MCTP_CTRL_CC_ERROR_INVALID_LENGTH 0x03 +#define MCTP_CTRL_CC_ERROR_NOT_READY 0x04 +#define MCTP_CTRL_CC_ERROR_UNSUPPORTED_CMD 0x05 +/* 0x80 - 0xFF are command specific */ + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBMCTP_CMDS_H */ diff --git a/libmctp/libmctp-log.h b/libmctp/libmctp-log.h new file mode 100644 index 00000000..1e574e5e --- /dev/null +++ b/libmctp/libmctp-log.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */ + +#ifndef _LIBMCTP_LOG_H +#define _LIBMCTP_LOG_H + +/* libmctp-internal logging */ + +void mctp_prlog(int level, const char *fmt, ...) + __attribute__((format(printf, 2, 3))); + +#ifndef pr_fmt +#define pr_fmt(x) x +#endif + +#define mctp_prerr(fmt, ...) \ + mctp_prlog(MCTP_LOG_ERR, pr_fmt(fmt), ##__VA_ARGS__) +#define mctp_prwarn(fmt, ...) \ + mctp_prlog(MCTP_LOG_WARNING, pr_fmt(fmt), ##__VA_ARGS__) +#define mctp_prinfo(fmt, ...) \ + mctp_prlog(MCTP_LOG_INFO, pr_fmt(fmt), ##__VA_ARGS__) +#define mctp_prdebug(fmt, ...) \ + mctp_prlog(MCTP_LOG_DEBUG, pr_fmt(fmt), ##__VA_ARGS__) + +#endif /* _LIBMCTP_LOG_H */ diff --git a/libmctp/libmctp-serial.h b/libmctp/libmctp-serial.h new file mode 100644 index 00000000..8a70101e --- /dev/null +++ b/libmctp/libmctp-serial.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */ + +#ifndef _LIBMCTP_SERIAL_H +#define _LIBMCTP_SERIAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +struct mctp_binding_serial; + +struct mctp_binding_serial *mctp_serial_init(void); +void mctp_serial_destroy(struct mctp_binding_serial *serial); + +struct mctp_binding *mctp_binding_serial_core(struct mctp_binding_serial *b); + +/* file-based IO */ +int mctp_serial_get_fd(struct mctp_binding_serial *serial); +int mctp_serial_read(struct mctp_binding_serial *serial); +int mctp_serial_open_path(struct mctp_binding_serial *serial, + const char *path); +void mctp_serial_open_fd(struct mctp_binding_serial *serial, int fd); + +/* direct function call IO */ +typedef int (*mctp_serial_tx_fn)(void *data, void *buf, size_t len) + __attribute__((warn_unused_result)); +void mctp_serial_set_tx_fn(struct mctp_binding_serial *serial, + mctp_serial_tx_fn fn, void *data); +int mctp_serial_rx(struct mctp_binding_serial *serial, + const void *buf, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBMCTP_SERIAL_H */ diff --git a/libmctp/libmctp.h b/libmctp/libmctp.h new file mode 100644 index 00000000..497d536e --- /dev/null +++ b/libmctp/libmctp.h @@ -0,0 +1,156 @@ +/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */ + +#ifndef _LIBMCTP_H +#define _LIBMCTP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + +typedef uint8_t mctp_eid_t; + +/* Special Endpoint ID values */ +#define MCTP_EID_NULL 0 +#define MCTP_EID_BROADCAST 0xff + +/* MCTP packet definitions */ +struct mctp_hdr { + uint8_t ver; + uint8_t dest; + uint8_t src; + uint8_t flags_seq_tag; +}; + +/* Definitions for flags_seq_tag field */ +#define MCTP_HDR_FLAG_SOM (1 << 7) +#define MCTP_HDR_FLAG_EOM (1 << 6) +#define MCTP_HDR_FLAG_TO (1 << 3) +#define MCTP_HDR_TO_SHIFT (3) +#define MCTP_HDR_TO_MASK (1) +#define MCTP_HDR_SEQ_SHIFT (4) +#define MCTP_HDR_SEQ_MASK (0x3) +#define MCTP_HDR_TAG_SHIFT (0) +#define MCTP_HDR_TAG_MASK (0x7) + +#define MCTP_MESSAGE_TO_SRC true +#define MCTP_MESSAGE_TO_DST false + +/* Baseline Transmission Unit and packet size */ +#define MCTP_BTU 64 +#define MCTP_PACKET_SIZE(unit) ((unit) + sizeof(struct mctp_hdr)) +#define MCTP_BODY_SIZE(unit) ((unit) - sizeof(struct mctp_hdr)) + +/* packet buffers */ + +struct mctp_pktbuf { + size_t start, end, size; + size_t mctp_hdr_off; + struct mctp_pktbuf *next; + unsigned char data[]; +}; + +struct mctp_binding; + +struct mctp_pktbuf *mctp_pktbuf_alloc(struct mctp_binding *hw, size_t len); +void mctp_pktbuf_free(struct mctp_pktbuf *pkt); +struct mctp_hdr *mctp_pktbuf_hdr(struct mctp_pktbuf *pkt); +void *mctp_pktbuf_data(struct mctp_pktbuf *pkt); +size_t mctp_pktbuf_size(struct mctp_pktbuf *pkt); +void *mctp_pktbuf_alloc_start(struct mctp_pktbuf *pkt, size_t size); +void *mctp_pktbuf_alloc_end(struct mctp_pktbuf *pkt, size_t size); +int mctp_pktbuf_push(struct mctp_pktbuf *pkt, void *data, size_t len); +void *mctp_pktbuf_pop(struct mctp_pktbuf *pkt, size_t len); + +/* MCTP core */ +struct mctp; +struct mctp_bus; + +struct mctp *mctp_init(void); +void mctp_set_max_message_size(struct mctp *mctp, size_t message_size); +typedef void (*mctp_capture_fn)(struct mctp_pktbuf *pkt, void *user); +void mctp_set_capture_handler(struct mctp *mctp, mctp_capture_fn fn, void *user); +void mctp_destroy(struct mctp *mctp); + +/* Register a binding to the MCTP core, and creates a bus (populating + * binding->bus). + * + * If this function is called, the MCTP stack is initialised as an 'endpoint', + * and will deliver local packets to a RX callback - see `mctp_set_rx_all()` + * below. + */ +int mctp_register_bus(struct mctp *mctp, + struct mctp_binding *binding, + mctp_eid_t eid); + +void mctp_unregister_bus(struct mctp *mctp, struct mctp_binding *binding); + +/* Create a simple bidirectional bridge between busses. + * + * In this mode, the MCTP stack is initialised as a bridge. There is no EID + * defined, so no packets are considered local. Instead, all messages from one + * binding are forwarded to the other. + */ +int mctp_bridge_busses(struct mctp *mctp, + struct mctp_binding *b1, struct mctp_binding *b2); + +typedef void (*mctp_rx_fn)(uint8_t src_eid, bool tag_owner, uint8_t msg_tag, + void *data, void *msg, size_t len); + +int mctp_set_rx_all(struct mctp *mctp, mctp_rx_fn fn, void *data); + +int mctp_message_tx(struct mctp *mctp, mctp_eid_t eid, bool tag_owner, + uint8_t msg_tag, void *msg, size_t msg_len); + +/* hardware bindings */ +struct mctp_binding { + const char *name; + uint8_t version; + struct mctp_bus *bus; + struct mctp *mctp; + size_t pkt_size; + size_t pkt_header; + size_t pkt_trailer; + int (*start)(struct mctp_binding *binding); + int (*tx)(struct mctp_binding *binding, struct mctp_pktbuf *pkt); + mctp_rx_fn control_rx; + void *control_rx_data; +}; + +void mctp_binding_set_tx_enabled(struct mctp_binding *binding, bool enable); + +/* + * Receive a packet from binding to core. Takes ownership of pkt, free()-ing it + * after use. + */ +void mctp_bus_rx(struct mctp_binding *binding, struct mctp_pktbuf *pkt); + +/* environment-specific allocation */ +void mctp_set_alloc_ops(void *(*alloc)(size_t), + void (*free)(void *), + void *(realloc)(void *, size_t)); + +/* environment-specific logging */ + +void mctp_set_log_stdio(int level); +void mctp_set_log_syslog(void); +void mctp_set_log_custom(void (*fn)(int, const char *, va_list)); + +/* these should match the syslog-standard LOG_* definitions, for + * easier use with syslog */ +#define MCTP_LOG_ERR 3 +#define MCTP_LOG_WARNING 4 +#define MCTP_LOG_NOTICE 5 +#define MCTP_LOG_INFO 6 +#define MCTP_LOG_DEBUG 7 + + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBMCTP_H */ diff --git a/libmctp/log.c b/libmctp/log.c new file mode 100644 index 00000000..cbdbbcaa --- /dev/null +++ b/libmctp/log.c @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */ + +#include + +#include "libmctp.h" +#include "libmctp-log.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef MCTP_HAVE_STDIO +#include +#endif + +#ifdef MCTP_HAVE_SYSLOG +#include +#endif + +enum { + MCTP_LOG_NONE, + MCTP_LOG_STDIO, + MCTP_LOG_SYSLOG, + MCTP_LOG_CUSTOM, +} log_type = MCTP_LOG_NONE; + +static int log_stdio_level; +static void (*log_custom_fn)(int, const char *, va_list); + +void mctp_prlog(int level, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + + switch (log_type) { + case MCTP_LOG_NONE: + break; + case MCTP_LOG_STDIO: +#ifdef MCTP_HAVE_STDIO + if (level <= log_stdio_level) { + vfprintf(stderr, fmt, ap); + fputs("\n", stderr); + } +#endif + break; + case MCTP_LOG_SYSLOG: +#ifdef MCTP_HAVE_SYSLOG + vsyslog(level, fmt, ap); +#endif + break; + case MCTP_LOG_CUSTOM: + log_custom_fn(level, fmt, ap); + break; + } + + va_end(ap); +} + +void mctp_set_log_stdio(int level) +{ + log_type = MCTP_LOG_STDIO; + log_stdio_level = level; +} + +void mctp_set_log_syslog(void) +{ + log_type = MCTP_LOG_SYSLOG; +} + +void mctp_set_log_custom(void (*fn)(int, const char *, va_list)) +{ + log_type = MCTP_LOG_CUSTOM; + log_custom_fn = fn; +} diff --git a/libmctp/range.h b/libmctp/range.h new file mode 100644 index 00000000..6c960158 --- /dev/null +++ b/libmctp/range.h @@ -0,0 +1,18 @@ +#ifndef _RANGE_H +#define _RANGE_H + +#define MIN(a, b) \ + ({ \ + typeof(a) _a = a; \ + typeof(b) _b = b; \ + _a < _b ? _a : _b; \ + }) + +#define MAX(a, b) \ + ({ \ + typeof(a) _a = a; \ + typeof(b) _b = b; \ + _a > _b ? _a : _b; \ + }) + +#endif diff --git a/libmctp/serial.c b/libmctp/serial.c new file mode 100644 index 00000000..ac91eb4a --- /dev/null +++ b/libmctp/serial.c @@ -0,0 +1,359 @@ +/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */ + +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef MCTP_HAVE_FILEIO +#include +#include +#else +static const size_t write(int fd, void *buf, size_t len) +{ + return -1; +} +#endif + +#define pr_fmt(x) "serial: " x + +/* Post-condition: All bytes written or an error has occurred */ +#define mctp_write_all(fn, dst, src, len) \ +({ \ + ssize_t wrote; \ + while (len) { \ + wrote = fn(dst, src, len); \ + if (wrote < 0) \ + break; \ + len -= wrote; \ + } \ + len ? -1 : 0; \ +}) + +#include "libmctp.h" +#include "libmctp-alloc.h" +#include "libmctp-log.h" +#include "libmctp-serial.h" +#include "container_of.h" + +struct mctp_binding_serial { + struct mctp_binding binding; + int fd; + unsigned long bus_id; + + mctp_serial_tx_fn tx_fn; + void *tx_fn_data; + + /* receive buffer and state */ + uint8_t rxbuf[1024]; + struct mctp_pktbuf *rx_pkt; + uint8_t rx_exp_len; + uint16_t rx_fcs; + enum { + STATE_WAIT_SYNC_START, + STATE_WAIT_REVISION, + STATE_WAIT_LEN, + STATE_DATA, + STATE_DATA_ESCAPED, + STATE_WAIT_FCS1, + STATE_WAIT_FCS2, + STATE_WAIT_SYNC_END, + } rx_state; + + /* temporary transmit buffer */ + uint8_t txbuf[256]; +}; + +#define binding_to_serial(b) \ + container_of(b, struct mctp_binding_serial, binding) + +#define MCTP_SERIAL_REVISION 0x01 +#define MCTP_SERIAL_FRAMING_FLAG 0x7e +#define MCTP_SERIAL_ESCAPE 0x7d + +struct mctp_serial_header { + uint8_t flag; + uint8_t revision; + uint8_t len; +}; + +struct mctp_serial_trailer { + uint8_t fcs_msb; + uint8_t fcs_lsb; + uint8_t flag; +}; + +static size_t mctp_serial_pkt_escape(struct mctp_pktbuf *pkt, uint8_t *buf) +{ + uint8_t total_len; + uint8_t *p; + int i, j; + + total_len = pkt->end - pkt->mctp_hdr_off; + + p = (void *)mctp_pktbuf_hdr(pkt); + + for (i = 0, j = 0; i < total_len; i++, j++) { + uint8_t c = p[i]; + if (c == 0x7e || c == 0x7d) { + if (buf) + buf[j] = 0x7d; + j++; + c ^= 0x20; + } + if (buf) + buf[j] = c; + } + + return j; +} + +static int mctp_binding_serial_tx(struct mctp_binding *b, + struct mctp_pktbuf *pkt) +{ + struct mctp_binding_serial *serial = binding_to_serial(b); + struct mctp_serial_header *hdr; + struct mctp_serial_trailer *tlr; + uint8_t *buf; + size_t len; + + /* the length field in the header excludes serial framing + * and escape sequences */ + len = mctp_pktbuf_size(pkt); + + hdr = (void *)serial->txbuf; + hdr->flag = MCTP_SERIAL_FRAMING_FLAG; + hdr->revision = MCTP_SERIAL_REVISION; + hdr->len = len; + + buf = (void *)(hdr + 1); + + len = mctp_serial_pkt_escape(pkt, NULL); + if (len + sizeof(*hdr) + sizeof(*tlr) > sizeof(serial->txbuf)) + return -1; + + mctp_serial_pkt_escape(pkt, buf); + + buf += len; + + tlr = (void *)buf; + tlr->flag = MCTP_SERIAL_FRAMING_FLAG; + /* todo: trailer FCS */ + tlr->fcs_msb = 0; + tlr->fcs_lsb = 0; + + len += sizeof(*hdr) + sizeof(*tlr); + + if (!serial->tx_fn) + return mctp_write_all(write, serial->fd, serial->txbuf, len); + + return mctp_write_all(serial->tx_fn, serial->tx_fn_data, serial->txbuf, + len); +} + +static void mctp_serial_finish_packet(struct mctp_binding_serial *serial, + bool valid) +{ + struct mctp_pktbuf *pkt = serial->rx_pkt; + assert(pkt); + + if (valid) + mctp_bus_rx(&serial->binding, pkt); + + serial->rx_pkt = NULL; +} + +static void mctp_serial_start_packet(struct mctp_binding_serial *serial, + uint8_t len) +{ + serial->rx_pkt = mctp_pktbuf_alloc(&serial->binding, len); +} + +static void mctp_rx_consume_one(struct mctp_binding_serial *serial, + uint8_t c) +{ + struct mctp_pktbuf *pkt = serial->rx_pkt; + + mctp_prdebug("state: %d, char 0x%02x", serial->rx_state, c); + + assert(!pkt == (serial->rx_state == STATE_WAIT_SYNC_START || + serial->rx_state == STATE_WAIT_REVISION || + serial->rx_state == STATE_WAIT_LEN)); + + switch (serial->rx_state) { + case STATE_WAIT_SYNC_START: + if (c != MCTP_SERIAL_FRAMING_FLAG) { + mctp_prdebug("lost sync, dropping packet"); + if (pkt) + mctp_serial_finish_packet(serial, false); + } else { + serial->rx_state = STATE_WAIT_REVISION; + } + break; + + case STATE_WAIT_REVISION: + if (c == MCTP_SERIAL_REVISION) { + serial->rx_state = STATE_WAIT_LEN; + } else { + mctp_prdebug("invalid revision 0x%02x", c); + serial->rx_state = STATE_WAIT_SYNC_START; + } + break; + case STATE_WAIT_LEN: + if (c > serial->binding.pkt_size || + c < sizeof(struct mctp_hdr)) { + mctp_prdebug("invalid size %d", c); + serial->rx_state = STATE_WAIT_SYNC_START; + } else { + mctp_serial_start_packet(serial, 0); + pkt = serial->rx_pkt; + serial->rx_exp_len = c; + serial->rx_state = STATE_DATA; + } + break; + + case STATE_DATA: + if (c == MCTP_SERIAL_ESCAPE) { + serial->rx_state = STATE_DATA_ESCAPED; + } else { + mctp_pktbuf_push(pkt, &c, 1); + if (pkt->end - pkt->mctp_hdr_off == serial->rx_exp_len) + serial->rx_state = STATE_WAIT_FCS1; + } + break; + + case STATE_DATA_ESCAPED: + c ^= 0x20; + mctp_pktbuf_push(pkt, &c, 1); + if (pkt->end - pkt->mctp_hdr_off == serial->rx_exp_len) + serial->rx_state = STATE_WAIT_FCS1; + else + serial->rx_state = STATE_DATA; + break; + + case STATE_WAIT_FCS1: + serial->rx_fcs = c << 8; + serial->rx_state = STATE_WAIT_FCS2; + break; + case STATE_WAIT_FCS2: + serial->rx_fcs |= c; + /* todo: check fcs */ + serial->rx_state = STATE_WAIT_SYNC_END; + break; + + case STATE_WAIT_SYNC_END: + if (c == MCTP_SERIAL_FRAMING_FLAG) { + mctp_serial_finish_packet(serial, true); + } else { + mctp_prdebug("missing end frame marker"); + mctp_serial_finish_packet(serial, false); + } + serial->rx_state = STATE_WAIT_SYNC_START; + break; + } + + mctp_prdebug(" -> state: %d", serial->rx_state); +} +static void mctp_rx_consume(struct mctp_binding_serial *serial, + const void *buf, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + mctp_rx_consume_one(serial, *(uint8_t *)(buf + i)); +} + +#ifdef MCTP_HAVE_FILEIO +int mctp_serial_read(struct mctp_binding_serial *serial) +{ + ssize_t len; + + len = read(serial->fd, serial->rxbuf, sizeof(serial->rxbuf)); + if (len == 0) + return -1; + + if (len < 0) { + mctp_prerr("can't read from serial device: %m"); + return -1; + } + + mctp_rx_consume(serial, serial->rxbuf, len); + + return 0; +} + +int mctp_serial_get_fd(struct mctp_binding_serial *serial) +{ + return serial->fd; +} + +int mctp_serial_open_path(struct mctp_binding_serial *serial, + const char *device) +{ + serial->fd = open(device, O_RDWR); + if (serial->fd < 0) + mctp_prerr("can't open device %s: %m", device); + + return 0; +} + +void mctp_serial_open_fd(struct mctp_binding_serial *serial, int fd) +{ + serial->fd = fd; +} +#endif + +void mctp_serial_set_tx_fn(struct mctp_binding_serial *serial, + mctp_serial_tx_fn fn, void *data) +{ + serial->tx_fn = fn; + serial->tx_fn_data = data; +} + +int mctp_serial_rx(struct mctp_binding_serial *serial, + const void *buf, size_t len) +{ + mctp_rx_consume(serial, buf, len); + return 0; +} + +static int mctp_serial_core_start(struct mctp_binding *binding) +{ + mctp_binding_set_tx_enabled(binding, true); + return 0; +} + +struct mctp_binding *mctp_binding_serial_core(struct mctp_binding_serial *b) +{ + return &b->binding; +} + +struct mctp_binding_serial *mctp_serial_init(void) +{ + struct mctp_binding_serial *serial; + + serial = __mctp_alloc(sizeof(*serial)); + memset(serial, 0, sizeof(*serial)); + serial->fd = -1; + serial->rx_state = STATE_WAIT_SYNC_START; + serial->rx_pkt = NULL; + serial->binding.name = "serial"; + serial->binding.version = 1; + serial->binding.pkt_size = MCTP_PACKET_SIZE(MCTP_BTU); + serial->binding.pkt_header = 0; + serial->binding.pkt_header = 0; + + serial->binding.start = mctp_serial_core_start; + serial->binding.tx = mctp_binding_serial_tx; + + return serial; +} + +void mctp_serial_destroy(struct mctp_binding_serial *serial) +{ + __mctp_free(serial); +}