├── .gitignore ├── LICENSE ├── README.md ├── netlink ├── .gitignore ├── README.md ├── genz_genl.py ├── kuns.c ├── kuns.py └── kuns.sh ├── shim_bridge ├── Makefile ├── fee.h ├── fee_IVSHMSG.c ├── fee_MSI-X.c ├── fee_adapter.c ├── fee_link.c ├── fee_pci.c ├── fee_register.c ├── genz_fee-stop.service ├── gf_bridge.c └── gf_bridge.h └── subsystem ├── Makefile ├── drivers.base.base.h ├── genz_bus.c ├── genz_bus.h ├── genz_class.c ├── genz_class.h ├── genz_control.h ├── genz_device.c ├── genz_device.h ├── genz_routing_fabric.h └── genz_subsystem.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.cmd 2 | *.mod.* 3 | *.ko 4 | *.o 5 | *.o.d 6 | *.swp 7 | .tmp_versions 8 | kallsyms 9 | Module.symvers 10 | modules.order 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Loading the Gen-Z prototype subsystem into F.E.E. 2 | 3 | This assumes you already have the Fabric Emulation Environment running 4 | and have created at least one virtual machine configured to join the 5 | emulated fabric. Those instructions [given in the F.E.E. repo.](https://github.com/linux-genz/F.E.E.) 6 | 7 | If the switch process is running, and you've started the VM, you should 8 | also see a description 9 | 10 | Driverless QEMU 11 | 12 | in one of the slots of the switch interface. 13 | 14 | ### Log into VM and clone this repo 15 | 16 | First, 'lspci -v' and insure you have the RedHat IVSHMEM pseudo device 17 | with 16 MSI-X interrupts. 18 | 19 | Depending on how you built your VM, you may need to download additional 20 | packages like git, Linux headers for your kernel, build-essential, etc. 21 | 22 | 1. git clone https://github.com/linux-genz/EmerGen-Z.git 23 | 1. cd EmerGen-Z/subsystem 24 | 1. make all 25 | 1. make modules install 26 | 1. cd ../shim_driver 27 | 1. make all 28 | 1. make modules install 29 | 30 | Now try 31 | 32 | sudo modprobe genz_fee 33 | 34 | Two modules should load and the description in the switch window should 35 | change. There should also be about twenty genz entries under /sys/class. 36 | 37 | A third module is needed for the actual bridge driver: 38 | 39 | sudo modprobe genz_fee_bridge 40 | 41 | Insure there is a device file /dev/genz_fee_bridgeXX. 42 | 43 | Messages are sent with 44 | 45 | echo "CID,SID:the message" > /dev/genz_fee_bridgeXX 46 | 47 | If the CID,SID is not assigned (to be documented SOON) then you can use 48 | a single digit to target the emulated fabric index. 49 | 50 | "cat < /dev/famez_bridgeXX" to read data. 51 | -------------------------------------------------------------------------------- /netlink/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | 3 | kuns 4 | -------------------------------------------------------------------------------- /netlink/README.md: -------------------------------------------------------------------------------- 1 | Program Snippets for kernel communiction via netlink(7) 2 | 3 | - KUNS: Kernel Udev Netlink Socket 4 | 5 | insmod and rmmod of "fee_bridge" will show you lots of things as you run these programs. 6 | 7 | kuns.sh merely wraps "udevadm monitor". 8 | 9 | kuns.c is a simple blocking read on the NETLINK_KEVENT_UDEV family "bus". 10 | 11 | kuns.py is a fancier way leveraging the pyroute2 encasulation of netlink. 12 | 13 | -------------------------------------------------------------------------------- /netlink/genz_genl.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | # Get on the Generic bus. Uses the llamas test module. 4 | 5 | # https://docs.pyroute2.org/ for info, but the source is best, especially 6 | # /usr/lib/python3/dist-packages/pyroute2/netlink/generic/__init__.py 7 | 8 | import errno 9 | import os 10 | import socket 11 | import sys 12 | import time 13 | import uuid 14 | 15 | from pdb import set_trace 16 | from pprint import pprint 17 | from types import SimpleNamespace 18 | 19 | from pyroute2.common import map_namespace 20 | from pyroute2.netlink import NETLINK_GENERIC, NetlinkError 21 | from pyroute2.netlink import NLM_F_REQUEST, NLM_F_DUMP, NLM_F_ACK 22 | from pyroute2.netlink import nla, nla_base, genlmsg 23 | from pyroute2.netlink.generic import GenericNetlinkSocket 24 | from pyroute2.netlink.nlsocket import Marshal 25 | 26 | # Used in kernel module: genl_register_family(struct genl_family.name). 27 | # After insmod, run "genl -d ctrl list" and eventually see 28 | # Name: genz_cmd 29 | # ID: 0x18 Version: 0x1 header size: 0 max attribs: 3 30 | # commands supported: 31 | # #1: ID-0x0 32 | # #2: ID-0x1 33 | # #3: ID-0x2 34 | # 35 | # ID == 0x18 (== 24) is dynamic and only valid in this scenario, and I've 36 | # seen the 24 in embedded structures. Version in kernel == 1, no header, 37 | # max 3 attribs == fields in 'MsgProps': gcid, cclass, uuid (user_send.c). 38 | 39 | GENZ_GENL_FAMILY_NAME = 'genz_cmd' 40 | GENZ_GENL_VERSION = 1 41 | 42 | # Commands are matched from kern_recv.c::struct genl_ops genz_gnl_ops. 43 | # Kernel convention is not to use zero as an index or base value. 44 | 45 | GENZ_C_PREFIX = 'GENZ_C_' 46 | 47 | GENZ_C_ADD_COMPONENT = 1 # from genz_genl.h "enum" 48 | GENZ_C_REMOVE_COMPONENT = 2 49 | GENZ_C_SYMLINK_COMPONENT = 3 50 | 51 | # Coalesce the commands into a forward and reverse map. 52 | # From https://www.open-mesh.org/attachments/857/neighbor_extend_dump.py 53 | 54 | (GENZ_C_name2num, GENZ_C_num2name) = map_namespace(GENZ_C_PREFIX, globals()) 55 | 56 | # These mixins are given to pyroute2 to build packed structs for sending to 57 | # C routines (libnl). They're just linked lookup tables whose attributes are 58 | # proscribed by internals. The choices for encoding (uint32, asciiz, etc) 59 | # are each a class in netlink/__init.__.py "class nlmsg_atoms". If you 60 | # don't like those choices, add a class here then add it as a new class 61 | # attribute to nlmsg. Only override __init__ for a set_trace, nothing else. 62 | # See also netlink/__init__.py::register_nlas() for optional fields. 63 | 64 | 65 | class GENZ_genlmsg(genlmsg): 66 | '''The set of all Netlink Attributes (NLAs) that could be passed. 67 | This is the analog of the kernel "struct nla_policy".''' 68 | 69 | prefix = 'GENZ_A_' # A for "Attribute", a convenice for me. 70 | 71 | # Zero-based arrays are sort-of needed here, but also somewhat frowned 72 | # upon. This needs further research, maybe in pyroute2 itself. 73 | 74 | nla_map = ( 75 | ('UnUsed', 'none'), 76 | (prefix + 'GCID', 'uint32'), 77 | (prefix + 'CCLASS', 'uint16'), 78 | (prefix + 'UUID', 'string') # bytearray not supported 79 | ) 80 | 81 | 82 | class GENZ_Marshal(Marshal): 83 | '''The set of all command numbers and their associated message structures. 84 | This is the analog of the kernel "struct genl_ops".''' 85 | 86 | msg_map = { 87 | GENZ_C_ADD_COMPONENT: GENZ_genlmsg, 88 | GENZ_C_REMOVE_COMPONENT: GENZ_genlmsg, 89 | GENZ_C_SYMLINK_COMPONENT: GENZ_genlmsg, 90 | } 91 | 92 | 93 | class GENZ_Netlink(GenericNetlinkSocket): 94 | 95 | def __init__(self, *args, **kwargs): 96 | super().__init__(*args, **kwargs) 97 | self.marshal = GENZ_Marshal() # Now pyroute2 methods see it. 98 | 99 | def bind(self, **kwargs): 100 | '''Exposed here instead of just automatically doing it.''' 101 | try: 102 | super().bind( 103 | GENZ_GENL_FAMILY_NAME, 104 | GENZ_genlmsg, 105 | groups=0, 106 | pid=os.getpid(), 107 | **kwargs) 108 | return # self.prid is now set, BTW 109 | 110 | except NetlinkError as exc: 111 | self.close() 112 | if exc.code == errno.ENOENT: 113 | raise RuntimeError( 114 | 'No kernel response for "%s"' % GENZ_GENL_FAMILY_NAME) 115 | raise(exc) 116 | except Exception as exc: 117 | self.close() 118 | raise RuntimeError('bind() failed: %s' % str(exc)) 119 | 120 | def newmsg(self, cmd, GCID, CCLASS, UUID): 121 | if not isinstance(UUID, uuid.UUID): 122 | raise RuntimeError('UUID must be type uuid.UUID') 123 | msg = GENZ_genlmsg() 124 | msg['cmd'] = GENZ_C_name2num[cmd] 125 | msg['pid'] = os.getpid() 126 | msg['version'] = GENZ_GENL_VERSION 127 | 128 | # The policy map in the kernel code says always send three. 129 | msg['attrs'].append([ 'GENZ_A_GCID', GCID ]) 130 | msg['attrs'].append([ 'GENZ_A_CCLASS', CCLASS ]) 131 | msg['attrs'].append([ 'GENZ_A_UUID', UUID.bytes ]) 132 | return msg 133 | 134 | def sendmsg(self, msg): 135 | return self.nlm_request(msg, 136 | msg_type=self.prid, 137 | msg_flags=NLM_F_REQUEST|NLM_F_ACK) 138 | 139 | # See https://docs.python.org/3.5/library/uuid.html 140 | # Remember, RFC 4122 mixes endianness in subfields so watch that ntoh! 141 | 142 | def YodelAyHeHUUID(random=True): 143 | '''Return a uuid.UUID object.''' 144 | if random: 145 | return uuid.uuid4() 146 | 147 | # Pick your favorite constructor, and refactor this routine accordingly. 148 | 149 | this = uuid.UUID('12345678123456781234567812345678') 150 | this = uuid.UUID(int=0x12345678123456781234567812345678) 151 | this = uuid.UUID('urn:uuid:12345678-1234-5678-1234-567812345678') 152 | this = uuid.UUID(bytes=b'\x12\x34\x56\x78' * 4) 153 | this = uuid.UUID(bytes_le=b'\x78\x56\x34\x12\x34\x12\x78\x56' + 154 | b'\x12\x34\x56\x78\x12\x34\x56\x78') 155 | this = uuid.UUID( 156 | fields=(0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x567812345678)) 157 | this = uuid.UUID('{12345678-1234-5678-1234-567812345678}') 158 | this = uuid.UUID('12345678-1234-5678-1234-567812345678') 159 | return this 160 | 161 | 162 | if __name__ == '__main__': 163 | genznl = GENZ_Netlink() 164 | genznl.bind() 165 | UUID = YodelAyHeHUUID() 166 | msg = genznl.newmsg('GENZ_C_ADD_COMPONENT', 4242, 43, UUID) 167 | print('Sending PID=%d UUID=%s' % (msg['pid'], str(UUID))) 168 | try: 169 | # If it works, get a packet. If not, raise an error. 170 | retval = genznl.sendmsg(msg) 171 | resperr = retval[0]['header']['error'] 172 | if resperr: 173 | pprint(retval) 174 | raise RuntimeError(resperr) 175 | print('Success') 176 | except Exception as exc: 177 | raise SystemExit(str(exc)) 178 | 179 | raise SystemExit(0) 180 | -------------------------------------------------------------------------------- /netlink/kuns.c: -------------------------------------------------------------------------------- 1 | // Camp on the UDEV netlink bus and report activity. Started from 2 | // http://experimentswithtrusth.blogspot.com/2014/06/handling-kernel-device-uevent-in.html 3 | // Kernel Udev Netlink Socket == kuns 4 | // gcc -Werror -o kuns kuns.c 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | void die(char *s) { 16 | perror(s); 17 | exit(1); 18 | } 19 | 20 | int main(int argc, char *argv[]) { 21 | struct sockaddr_nl nls; 22 | struct pollfd pfd; 23 | char buf[4096]; 24 | int ret; 25 | 26 | // Create a socket for the hotplug event netlink bus, then bind it. 27 | // Socket type is a noop for AF_NETLINK. 28 | 29 | if ((pfd.fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT)) == -1) 30 | die("socket()"); 31 | 32 | memset(&nls, 0, sizeof(nls)); 33 | nls.nl_family = AF_NETLINK; 34 | nls.nl_pid = getpid(); // Non-zero and recoverable 35 | nls.nl_groups = -1; // Bitmask, all set 36 | 37 | if (bind(pfd.fd, (void *)&nls, sizeof(struct sockaddr_nl))) 38 | die("bind()"); 39 | 40 | pfd.events = POLLIN; 41 | while ((ret = poll(&pfd, 1, -1)) != -1) { 42 | int len; 43 | char *start, *eobuf;; 44 | 45 | if (!ret) // Timeout should not occur 46 | die("timeout"); 47 | 48 | // Insure there is a null-terminated buffer at the end of it all. 49 | memset(buf, 0, sizeof(buf)); 50 | if ((len = recv(pfd.fd, buf, sizeof(buf) - 1, MSG_DONTWAIT)) == -1) 51 | die("recv"); 52 | 53 | start = buf; 54 | eobuf = buf + len; 55 | // Mostly NUL-terminated strings. Ignore the blob in 'libudev' 56 | while (start < eobuf) { 57 | if (!strcmp(start, "libudev") || strchr(start, '=')) 58 | printf("%s\n", start); 59 | start += strlen(start) + 1; 60 | } 61 | fflush(stdout); 62 | } 63 | die("poll"); 64 | return 0; 65 | } 66 | -------------------------------------------------------------------------------- /netlink/kuns.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | # Co-opt the pyroute2 NetlinkSocket (which defaults to GenericNetlinkSocket) to 4 | # listen on the Netlink KOBJECT_UEVENT bus for module and device activity. 5 | # modprobe/rmmod of the fee_bridge module shows lots of stuff. 6 | 7 | # https://docs.pyroute2.org/ for info, but the source is best, especially 8 | # /usr/lib/python3/dist-packages/pyroute2/netlink/nlsocket.py 9 | 10 | import os 11 | import socket 12 | import sys 13 | import time 14 | 15 | from pdb import set_trace 16 | from pprint import pprint 17 | from types import SimpleNamespace 18 | 19 | from pyroute2.netlink import NETLINK_KOBJECT_UEVENT 20 | from pyroute2.netlink.nlsocket import NetlinkSocket # RTFF 21 | 22 | 23 | class KobjectUeventNetlinkSocket(NetlinkSocket): 24 | 25 | def __init__(self, *args, **kwargs): 26 | # NetlinkSocket has no __init__ so end up in nlsocket.py::NetlinkMixin.__init__ 27 | # which is expecting family, port, pid, and fileno. Its "post_init()" creates 28 | # self._sock (visible here). It also provides accessor methods so I should 29 | # never hit self._sock directly. 30 | kwargs['family'] = NETLINK_KOBJECT_UEVENT # Override 31 | async = 'async' 32 | if async in kwargs: 33 | asyncval = bool(kwargs[async]) 34 | del kwargs[async] 35 | else: 36 | asyncval = False 37 | super().__init__(*args, **kwargs) 38 | self.bind(groups=-1, async=asyncval) # zero was a bad choice 39 | 40 | def _cooked(self, raw): 41 | elems = [ r for r in raw.split(b'\x00') ] 42 | elem0 = elems.pop(0).decode() 43 | if elem0 == 'libudev': 44 | retobj = SimpleNamespace(src='libudev') 45 | grunge = bytes() # FIXME: grunge kernel src, decode this mess 46 | while not elems[0].startswith(b'ACTION='): 47 | grunge += elems.pop(0) 48 | retobj.header = grunge 49 | else: 50 | retobj = SimpleNamespace(src='kernel', header=elem0) 51 | # Should just be key=value left 52 | for e in elems: 53 | if e: 54 | key, value = e.decode().split('=') 55 | setattr(retobj, key.strip(), value.strip()) 56 | return retobj 57 | 58 | def __call__(self): 59 | '''Returns None or an object based on blocking/timeout.''' 60 | try: 61 | raw = self.recv(4096) 62 | except BlockingIOError as err: # EWOULDBLOCK 63 | return None 64 | return self._cooked(raw) 65 | 66 | @property 67 | def blocking(self): 68 | '''Per docs, True -> timeout == None, False -> timeount == 0.0''' 69 | to = self.gettimeout() 70 | return True if to is None else bool(to) 71 | 72 | @blocking.setter 73 | def blocking(self, newstate): 74 | '''Per docs, True -> timeout == None, False -> timeount == 0.0''' 75 | self.setblocking(bool(newstate)) 76 | 77 | @property 78 | def timeout(self): 79 | return self.gettimeout() 80 | 81 | @timeout.setter 82 | def timeout(self, newto): 83 | '''newto must be None or float(>= 0.0)''' 84 | assert newto is None or float(newto) >= 0.0, 'bad timeout value' 85 | self.settimeout(newto) 86 | 87 | @property 88 | def timeout(self): 89 | return self.gettimeout() 90 | 91 | @property 92 | def rdlen(self): 93 | return self.buffer_queue.qsize() 94 | 95 | def dequeue(self, count=-1): 96 | retlist = [] 97 | if count <= 0: 98 | while True: 99 | try: 100 | retlist.append( 101 | self._cooked(self.buffer_queue.get(block=False))) 102 | except Empty as err: 103 | return retlist 104 | 105 | while count > 0: 106 | retlist.append(self._cooked(self.buffer_queue.get(block=True))) 107 | count -= 1 108 | return retlist 109 | 110 | # Callback is hit only during kuns.get() which can hang. The message it gets 111 | # is crap; maybe the logic is more suited to (generic) netlink than the 112 | # multiple udev notifiers? 113 | 114 | if __name__ == '__main__': 115 | async = len(sys.argv) > 1 116 | kuns = KobjectUeventNetlinkSocket(async=async) 117 | if async: 118 | print('\nAsync reads...', end='') 119 | while True: 120 | while not kuns.rdlen: 121 | print('.', end='') 122 | sys.stdout.flush() 123 | time.sleep(1) 124 | print('\n', kuns.dequeue(count=1)) 125 | 126 | print('\nBlocking reads...') 127 | kuns.blocking = True # It's the default value; play with others. 128 | while True: 129 | val = kuns() 130 | if val is None: 131 | print('Nada') 132 | else: 133 | pprint(val) 134 | -------------------------------------------------------------------------------- /netlink/kuns.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | exec udevadm monitor -p 4 | -------------------------------------------------------------------------------- /shim_bridge/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for linux-genz Fabric Emulation Environment and bridge 2 | 3 | VERBOSE=0 4 | 5 | CONFIG_GENZ_FEE ?= m 6 | 7 | KERNELBASE = /lib/modules/$(shell uname -r) 8 | KERNELDIR ?= $(KERNELBASE)/build 9 | SHELL=/bin/bash 10 | PWD:=$(shell /bin/pwd) 11 | 12 | # struct wait_queue_head, among others, didn't show up until 4.13, but 13 | # was backported into SLES15 (kernel 4.12). It's a gross test but 14 | # better than nothing. 15 | VMIN:=4 16 | PMIN:=13 17 | V:=$(shell make --no-print-directory -C ${KERNELDIR} kernelversion | cut -d. -f1) 18 | P:=$(shell make --no-print-directory -C ${KERNELDIR} kernelversion | cut -d. -f2) 19 | VFAIL:=Kernel headers are $V.$P, need \>= ${VMIN}.${PMIN} 20 | VFAILNOBACK:=Kernel headers are $V.$P, no backport from \>= ${VMIN}.${PMIN} 21 | 22 | obj-$(CONFIG_GENZ_FEE) += genz_fee.o fee_bridge.o 23 | 24 | # fee_pci.c has the MODULE declarations 25 | 26 | genz_fee-objs := fee_pci.o fee_adapter.o fee_IVSHMSG.o \ 27 | fee_register.o fee_MSI-X.o fee_link.o 28 | 29 | fee_bridge-objs := gf_bridge.o 30 | 31 | ccflags-y:=-I$(src)/../subsystem 32 | 33 | RUNNING_ARCH := $(shell dpkg-architecture -qDEB_BUILD_ARCH_CPU 2>/dev/null) 34 | 35 | all: modules 36 | 37 | modules: versioncheck 38 | ifeq "$(RUNNING_ARCH)" "amd64" 39 | make V=$(VERBOSE) -C $(KERNELDIR) M=$(PWD) ARCH=x86 modules 40 | else 41 | make V=$(VERBOSE) -C $(KERNELDIR) M=$(PWD) modules 42 | endif 43 | 44 | modules_install: modules 45 | INSTALL_MOD_DIR=genz/FEE sudo -E make V=$(VERBOSE) -C $(KERNELDIR) M=$(PWD) modules_install 46 | sudo -E depmod -a 47 | 48 | clean: 49 | ifeq "$(architecture)" "amd64" 50 | make -C $(KERNELDIR) M=$(PWD) ARCH=x86 clean 51 | else 52 | make -C $(KERNELDIR) M=$(PWD) clean 53 | endif 54 | 55 | # Kernel 3 is bad, 5 is good, 4 needs a closer look. 56 | versioncheck: 57 | @[ $V -lt ${VMIN} ] && echo ${VFAIL} && exit 1; \ 58 | [ $V -gt ${VMIN} ] && exit 0; \ 59 | [ $P -ge ${PMIN} ] && exit 0; \ 60 | grep -q 'struct wait_queue_head' ${KERNELDIR}/include/linux/wait.h && exit 0; \ 61 | echo ${VFAILNOBACK}; exit 1 62 | 63 | -------------------------------------------------------------------------------- /shim_bridge/fee.h: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Hewlett Packard Enterprise Development LP. 3 | * All rights reserved. 4 | * 5 | * This source code file is part of the EmerGen-Z project. 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, version 2 of the License, or 10 | * (at your option) any later version. 11 | 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | // Global definitions. 22 | 23 | #ifndef FEE_DOT_H 24 | #define FEE_DOT_H 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | 33 | #define FEE_DEBUG // See "Debug assistance" below 34 | 35 | #define FEE_NAME "FEE" 36 | #define FEE_VERSION FEE_NAME " v0.9.0: using Gen-Z subsystem" 37 | 38 | #define FEE "FEE: " // pr_xxxx header 39 | #define FEESP " " // pr_xxxx header same length indent 40 | #define DEFAULT_CCLASS "FEEadapter" // no spaces 41 | 42 | struct ivshmem_registers { // BAR 0 43 | uint32_t Rev1Reserved1, // Rev 0: Interrupt mask 44 | Rev1Reserved2, // Rev 0: Interrupt status 45 | IVPosition, // My peer id 46 | Doorbell; // Upper and lower half 47 | }; 48 | 49 | struct ivshmem_msi_x_table_pba { // BAR 1: Not mapped, not used. YET. 50 | uint32_t junk1, junk2; 51 | }; 52 | 53 | // ivshmsg_server.py controls the mailbox slot size and number of slots 54 | // (and therefore the total file size). It gives these numbers to this driver. 55 | // There are always a power-of-two number of mailbox slots, indexed by IVSHMSG 56 | // client ID. Slot 0 is reserved for global data cuz it's easy to find :-) 57 | // Besides, ID 0 doesn't seem to work in the QEMU doorbell mechanism. The 58 | // last slot (with ID == nClients + 1) is for the Python server. The remaining 59 | // slots are for client IDs 1 through nClients. 60 | 61 | struct FEE_globals { // BAR 2: Start of IVSHMEM 62 | uint64_t slotsize, buf_offset, nClients, nEvents, server_id; 63 | }; 64 | 65 | // Use only uint64_t and keep the buf[] on a 32-byte alignment for this: 66 | // od -Ad -w32 -c -tx8 /dev/shm/ivshmsg_mailbox 67 | struct __attribute__ ((packed)) FEE_mailslot { 68 | char nodename[32]; // off 0: of the owning client 69 | char cclass[32]; // off 32: of the owning client 70 | uint64_t buflen, // off 64: 71 | peer_id, // off 72: Convenience; set by server 72 | last_responder, // off 80: To assist stale stompage 73 | peer_SID, // off 88: Calculated in MSI-X... 74 | peer_CID, // off 96: ...from last_responder 75 | pad[3]; // off 104 76 | char buf[]; // off 128 == globals->buf_offset 77 | }; 78 | 79 | // The primary configuration/context data. 80 | struct FEE_adapter { 81 | struct list_head lister; 82 | atomic_t nr_users; // User-space actors 83 | struct pci_dev *pdev; // Paranoid reverse ptr 84 | int slot; // pdev->devfn >> 3 85 | uint64_t max_buflen; 86 | uint16_t my_id; // match ringer field 87 | struct ivshmem_registers __iomem *regs; // BAR0 88 | struct FEE_globals __iomem *globals; // BAR2 89 | struct FEE_mailslot *my_slot; // indexed by my_id 90 | void *IRQ_private; // arch-dependent? 91 | 92 | // Per-adapter handshaking between doorbell/mail delivery and a 93 | // driver read(). Doorbell comes in and sets the pointer then 94 | // issues a wakeup. read() follows the pointer then sets it 95 | // to NULL for next one. Since reading is more of a one-to-many 96 | // relationship this module can hold the one. 97 | 98 | struct FEE_mailslot *incoming_slot; 99 | struct wait_queue_head incoming_slot_wqh; 100 | spinlock_t incoming_slot_lock; 101 | 102 | // Writing is many to one, so support buffers etc are the 103 | // responsibility of that module, managed by open() & release(). 104 | void *outgoing; 105 | 106 | struct genz_core_structure *core; // Primary data structure 107 | struct genz_char_device *genz_chrdev; // Convenience backpointers 108 | void *teardown; 109 | }; 110 | 111 | //------------------------------------------------------------------------- 112 | // fee_pci.c - insmod/rmmod handling with pci_register probe()/remove() 113 | 114 | extern int verbose; // insmod parameter 115 | extern struct list_head FEE_adapter_list; 116 | extern struct semaphore FEE_adapter_sema; 117 | 118 | //------------------------------------------------------------------------- 119 | // fee_adapter.c - create/populate and destroy an adapter structure 120 | 121 | // Linked in to genzfee.ko, used by various other source modules 122 | struct FEE_adapter *FEE_adapter_create(struct pci_dev *); 123 | void FEE_adapter_destroy(struct FEE_adapter *); 124 | struct FEE_mailslot __iomem *calculate_mailslot(struct FEE_adapter *, unsigned); 125 | 126 | // Nothing EXPORTed 127 | 128 | //......................................................................... 129 | // fee_IVSHMSG.c - the actual messaging IO. 130 | 131 | #define GENZ_FEE_SID_DEFAULT 27 // see twisted_server.py 132 | #define GENZ_FEE_SID_CID_IS_PEER_ID -42 // interpret cid as peer_id 133 | 134 | // EXPORTed 135 | extern struct FEE_mailslot *FEE_await_incoming(struct FEE_adapter *, int); 136 | extern void FEE_release_incoming(struct FEE_adapter *); 137 | extern int FEE_create_outgoing(int, int, char *, size_t, struct FEE_adapter *); 138 | 139 | //......................................................................... 140 | // FEE_???.c - handle interrupts from other FEE peers (input). By arch: 141 | // x86_64: FEE_MSI-X.c 142 | // ARM64: FEE_MSI-X.c with assist from QEMU vfio modules 143 | // RISCV: not written yet 144 | 145 | irqreturn_t FEE_link_request(struct FEE_mailslot __iomem *, struct FEE_adapter *); 146 | 147 | // EXPORTed 148 | int FEE_ISR_setup(struct pci_dev *); 149 | void FEE_ISR_teardown(struct pci_dev *); 150 | 151 | //......................................................................... 152 | // fee_register.c - accept end-driver requests to use FEE. 153 | 154 | // EXPORTed 155 | extern int FEE_register(const struct genz_core_structure *, 156 | const struct file_operations *, 157 | const struct bin_attribute *, 158 | int); 159 | extern int FEE_unregister(const struct file_operations *); 160 | 161 | //------------------------------------------------------------------------- 162 | // Legibility assistance 163 | 164 | // Send a command for the switch interpreter 165 | #define UPDATE_SWITCH(AdApTeR) FEE_create_outgoing( \ 166 | AdApTeR->globals->server_id, \ 167 | GENZ_FEE_SID_CID_IS_PEER_ID, \ 168 | "dump", 4, AdApTeR); 169 | 170 | // linux/pci.h missed one 171 | #ifndef pci_resource_name 172 | #define pci_resource_name(dev, bar) (char *)((dev)->resource[(bar)].name) 173 | #endif 174 | 175 | #define CARDLOC(ptr) (pci_resource_name(ptr, 1)) 176 | 177 | #define STREQ(s1, s2) (!strcmp(s1, s2)) 178 | #define STREQ_N(s1, s2, lll) (!strncmp(s1, s2, lll)) 179 | #define STARTS(s1, s2) (!strncmp(s1, s2, strlen(s2))) 180 | 181 | //------------------------------------------------------------------------- 182 | // Debug assistance 183 | 184 | #ifndef PR_V1 185 | #ifdef FEE_DEBUG 186 | #define PR_V1(a...) { if (verbose) pr_info(FEE a); } 187 | #define PR_V2(a...) { if (verbose > 1) pr_info(FEE a); } 188 | #define PR_V3(a...) { if (verbose > 2) pr_info(FEE a); } 189 | #else 190 | #define PR_V1(a...) 191 | #define PR_V2(a...) 192 | #define PR_V3(a...) 193 | #endif 194 | #endif 195 | 196 | #define _F_ __FUNCTION__ 197 | #define PR_ENTER(a...) { if (verbose) { \ 198 | pr_info(FEE "enter %s: ", _F_); pr_cont(a); }} 199 | #define PR_EXIT(a...) { if (verbose) { \ 200 | pr_info(FEE "exit %s: ", _F_); pr_cont(a); }} 201 | 202 | #define PR_SLEEPMS(_txt, _ms) { pr_info(FEE " " _txt); msleep(_ms); } 203 | 204 | #endif 205 | -------------------------------------------------------------------------------- /shim_bridge/fee_IVSHMSG.c: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Hewlett Packard Enterprise Development LP. 3 | * All rights reserved. 4 | * 5 | * This source code file is part of the EmerGen-Z project. 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, version 2 of the License, or 10 | * (at your option) any later version. 11 | 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | // Implement the mailbox/mailslot protocol of IVSHMSG. 22 | 23 | #include // usleep_range, wait_event* 24 | #include 25 | #include // jiffies 26 | 27 | #include "fee.h" 28 | 29 | //------------------------------------------------------------------------- 30 | // Return positive (bytecount) on success, negative on error, never 0. 31 | // The synchronous rate seems to be determined mostly by the sleep 32 | // duration. I tried a 3x timeout whose success varied from 2 minutes to 33 | // three hours before it popped. 4x was better, lasted until I did a 34 | // compile, so...use a slightly adaptive timeout to reach the LOOP_MAX. 35 | // CID,SID is the order used in the spec. 36 | 37 | #define PRIOR_RESP_WAIT (5 * HZ) // 5x 38 | #define DELAY_MS_LOOP_MAX 10 // or about 100 writes/second 39 | 40 | static unsigned long longest = PRIOR_RESP_WAIT/2; 41 | 42 | int FEE_create_outgoing(int CID, int SID, char *buf, size_t buflen, 43 | struct FEE_adapter *adapter) 44 | { 45 | uint32_t peer_id; 46 | unsigned long now = 0, this_delay, 47 | hw_timeout = get_jiffies_64() + PRIOR_RESP_WAIT; 48 | 49 | // The IVSHMEM "vector" will map to an MSI-X "entry" value. "vector" 50 | // is the lower 16 bits and the combo must be assigned atomically. 51 | union __attribute__ ((packed)) { 52 | struct { uint16_t vector, peer; }; 53 | uint32_t Doorbell; 54 | } ringer; 55 | 56 | peer_id = SID == GENZ_FEE_SID_CID_IS_PEER_ID ? CID : CID / 100; 57 | 58 | // Might NOT be printable C string. 59 | PR_V1("%s(%lu bytes) to %d:%d -> %d\n", 60 | __FUNCTION__, buflen, SID, CID, peer_id); 61 | 62 | // FIXME: integrate with Link RFC results 63 | if (SID != 27 && SID != GENZ_FEE_SID_CID_IS_PEER_ID) 64 | return -ENETUNREACH; 65 | 66 | if (peer_id < 1 || peer_id > adapter->globals->server_id) 67 | return -EBADSLT; 68 | if (buflen >= adapter->max_buflen) 69 | return -E2BIG; 70 | if (!buflen) 71 | return -ENODATA; // FIXME: is there value to a "silent kick"? 72 | 73 | // Pseudo-"HW ready": wait until my_slot has pushed a previous write 74 | // through. In truth it's the previous responder clearing my buflen. 75 | // The macro makes many references to its parameters, so... 76 | this_delay = 1; 77 | while (adapter->my_slot->buflen && time_before(now, hw_timeout)) { 78 | if (in_interrupt()) 79 | mdelay(this_delay); // (25k) leads to compiler error 80 | else 81 | msleep(this_delay); 82 | if (this_delay < DELAY_MS_LOOP_MAX) 83 | this_delay += 2; 84 | now = get_jiffies_64(); 85 | } 86 | if ((hw_timeout -= now) > longest) { 87 | // pr_warn(FZ "%s() biggest TO goes from %lu to %lu\n", 88 | // __FUNCTION__, longest, hw_timeout); 89 | longest = hw_timeout; 90 | } 91 | 92 | // FIXME: add stompcounter tracker, return -EXXXX. To start with, just 93 | // emit an error on first occurrence and see what falls out. 94 | if (adapter->my_slot->buflen) { 95 | pr_err("%s() would stomp previous message to %llu\n", 96 | __FUNCTION__, adapter->my_slot->last_responder); 97 | return -ERESTARTSYS; 98 | } 99 | // Keep nodename and buf pointer; update buflen and buf contents. 100 | // buflen is the handshake out to the world that I'm busy. 101 | adapter->my_slot->buflen = buflen; 102 | adapter->my_slot->buf[buflen] = '\0'; // ASCII strings paranoia 103 | adapter->my_slot->last_responder = peer_id; 104 | memcpy(adapter->my_slot->buf, buf, buflen); 105 | 106 | // Choose the correct vector set from all sent to me via the peer. 107 | // Trigger the vector corresponding to me with the vector. 108 | ringer.peer = peer_id; 109 | ringer.vector = adapter->my_id; 110 | adapter->regs->Doorbell = ringer.Doorbell; 111 | return buflen; 112 | } 113 | EXPORT_SYMBOL(FEE_create_outgoing); 114 | 115 | //------------------------------------------------------------------------- 116 | // Return a pointer to the data structure or ERRPTR, rather than an integer 117 | // ret, so the caller doesn't need to understand the adapter structure to 118 | // look it up. Intermix locking with that in msix_all(). 119 | 120 | struct FEE_mailslot *FEE_await_incoming(struct FEE_adapter *adapter, 121 | int nonblocking) 122 | { 123 | int ret = 0; 124 | 125 | if (adapter->incoming_slot) 126 | return adapter->incoming_slot; 127 | if (nonblocking) 128 | return ERR_PTR(-EAGAIN); 129 | PR_V2("%s() waiting...\n", __FUNCTION__); 130 | 131 | // wait_event_xxx checks the the condition BEFORE waiting but 132 | // does modify the run state. Does that side effect matter? 133 | // FIXME: wait_event_interruptible_locked? 134 | if ((ret = wait_event_interruptible(adapter->incoming_slot_wqh, 135 | adapter->incoming_slot))) 136 | return ERR_PTR(ret); 137 | return adapter->incoming_slot; 138 | } 139 | EXPORT_SYMBOL(FEE_await_incoming); 140 | 141 | //------------------------------------------------------------------------- 142 | 143 | void FEE_release_incoming(struct FEE_adapter *adapter) 144 | { 145 | spin_lock(&adapter->incoming_slot_lock); 146 | adapter->incoming_slot->buflen = 0; // The slot of the sender. 147 | adapter->incoming_slot = NULL; // The local MSI-X handler. 148 | spin_unlock(&adapter->incoming_slot_lock); 149 | } 150 | EXPORT_SYMBOL(FEE_release_incoming); 151 | -------------------------------------------------------------------------------- /shim_bridge/fee_MSI-X.c: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Hewlett Packard Enterprise Development LP. 3 | * All rights reserved. 4 | * 5 | * This source code file is part of the EmerGen-Z project. 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, version 2 of the License, or 10 | * (at your option) any later version. 11 | 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | // Arch-specific ISR handler for x86_64: configure and handle MSI-X interrupts 22 | // from IVSHMEM device. 23 | 24 | #include // irq_enable, etc 25 | 26 | #include "fee.h" 27 | 28 | //------------------------------------------------------------------------- 29 | // FIXME: can a spurious interrupt get me here "too fast" so that I'm 30 | // overrunning the incoming slot during a tight loop client? 31 | 32 | static irqreturn_t all_msix(int vector, void *data) { 33 | struct FEE_adapter *adapter = data; 34 | struct msix_entry *msix_entries = adapter->IRQ_private; 35 | int slotnum, stomped = 0; 36 | uint16_t incoming_id = 0; // see pci.h for msix_entry 37 | struct FEE_mailslot __iomem *incoming_slot; 38 | 39 | spin_lock(&(adapter->incoming_slot_lock)); 40 | 41 | // Match the IRQ vector to entry/vector pair which yields the sender. 42 | // Turns out i and msix_entries[i].entry are identical in famez. 43 | // FIXME: preload a lookup table if I ever care about speed. 44 | for (slotnum = 1; slotnum < adapter->globals->nEvents; slotnum++) { 45 | if (vector == msix_entries[slotnum].vector) 46 | break; 47 | } 48 | if (slotnum >= adapter->globals->nEvents) { 49 | spin_unlock(&(adapter->incoming_slot_lock)); 50 | pr_err(FEE "IRQ handler could not match vector %d\n", vector); 51 | return IRQ_NONE; 52 | } 53 | // All returns from here are IRQ_HANDLED 54 | 55 | incoming_id = msix_entries[slotnum].entry; 56 | if (!(incoming_slot = calculate_mailslot(adapter, incoming_id))) { 57 | spin_unlock(&(adapter->incoming_slot_lock)); 58 | pr_err(FEE "Could not match peer %u\n", incoming_id); 59 | return IRQ_HANDLED; 60 | } 61 | 62 | // This may do weird things with the spinlock held. 63 | PR_V2("IRQ %d == sender %u -> \"%s\"\n", 64 | vector, incoming_id, incoming_slot->buf); 65 | 66 | // Link layer management can be fully processed here, otherwise 67 | // deal with a "normal" message. 68 | if (FEE_link_request(incoming_slot, adapter) == IRQ_HANDLED) 69 | return IRQ_HANDLED; 70 | 71 | if (adapter->incoming_slot) // print outside the spinlock 72 | stomped = adapter->incoming_slot->peer_id; 73 | adapter->incoming_slot = incoming_slot; 74 | spin_unlock(&(adapter->incoming_slot_lock)); 75 | 76 | wake_up(&(adapter->incoming_slot_wqh)); 77 | if (stomped) 78 | pr_warn(FEE "%s() stomped incoming slot for reader %d\n", 79 | __FUNCTION__, adapter->my_id); 80 | return IRQ_HANDLED; 81 | } 82 | 83 | //------------------------------------------------------------------------- 84 | // As there are only nClients actual clients (because mailslot 0 is globals 85 | // and server @ nslots-1) I SHOULDN'T actually activate those two IRQs. 86 | 87 | int FEE_ISR_setup(struct pci_dev *pdev) 88 | { 89 | struct FEE_adapter *adapter = pci_get_drvdata(pdev); 90 | int ret, i, nvectors = 0, last_irq_index; 91 | struct msix_entry *msix_entries; // pci.h, will be an array 92 | 93 | // How many vectors are provided versus neeed? Slot 0 doesn't need 94 | // one but all others do. 95 | if ((nvectors = pci_msix_vec_count(pdev)) < 0) { 96 | pr_err(FEE "Error retrieving MSI-X vector count\n"); 97 | return nvectors; 98 | } 99 | pr_info(FEESP "%2d MSI-X vectors available (%sabled)\n", 100 | nvectors, pdev->msix_enabled ? "en" : "dis"); 101 | if (nvectors < 16) { // Convention in FAME emulation_configure.sh 102 | pr_err(FEE "QEMU must provide >= 16 MSI-X vectors; only %d\n", nvectors); 103 | return -EINVAL; 104 | } 105 | if (adapter->globals->nEvents > nvectors) { 106 | pr_err(FEE "need %llu MSI-X vectors, only %d available\n", 107 | adapter->globals->nEvents, nvectors); 108 | return -ENOSPC; 109 | } 110 | nvectors = adapter->globals->nEvents; // legibility below 111 | 112 | ret = -ENOMEM; 113 | if (!(msix_entries = kzalloc( 114 | nvectors * sizeof(struct msix_entry), GFP_KERNEL))) { 115 | pr_err(FEE "Can't allocate MSI-X entries table\n"); 116 | goto err_kfree_msix_entries; 117 | } 118 | adapter->IRQ_private = msix_entries; 119 | 120 | // .vector was zeroed by kzalloc 121 | for (i = 0; i < nvectors; i++) 122 | msix_entries[i].entry = i; 123 | 124 | // There used to be a direct call for "exact match". Re-create it. 125 | if ((ret = pci_alloc_irq_vectors( 126 | pdev, nvectors, nvectors, PCI_IRQ_MSIX)) < 0) { 127 | pr_err(FEE "Can't allocate MSI-X IRQ vectors\n"); 128 | goto err_kfree_msix_entries; 129 | } 130 | pr_info(FEESP "%2d MSI-X vectors used (%sabled)\n", 131 | ret, pdev->msix_enabled ? "en" : "dis"); 132 | if (ret < nvectors) { 133 | pr_err(FEE "%d vectors are not enough\n", ret); 134 | ret = -ENOSPC; // Akin to pci_alloc_irq_vectors 135 | goto err_pci_free_irq_vectors; 136 | } 137 | 138 | // Attach each IRQ to the same handler. pci_irq_vector() walks a 139 | // list and returns info on a match. Success is merely a lookup, 140 | // not an allocation, so there's nothing to clean up from this step. 141 | // Reuse the table from the old pci_msix_xxx calls. Note that 142 | // requested vectors are still option base 0. 143 | for (i = 0; i < nvectors; i++) { 144 | if ((ret = pci_irq_vector(pdev, i)) < 0) { 145 | pr_err("pci_irq_vector(%d) failed: %d\n", i, ret); 146 | goto err_pci_free_irq_vectors; 147 | } 148 | msix_entries[i].vector = ret; 149 | } 150 | 151 | // Now that they're all batched, assign them. Each successful request 152 | // must be matched by a free_irq() someday. No, the return value 153 | // is not stored anywhere. 154 | for (last_irq_index = 0; 155 | last_irq_index < nvectors; 156 | last_irq_index++) { 157 | if ((ret = request_irq( 158 | msix_entries[last_irq_index].vector, 159 | all_msix, 160 | 0, 161 | FEE_NAME, 162 | adapter))) { 163 | pr_err(FEE "request_irq(%d) failed: %d\n", 164 | last_irq_index, ret); 165 | goto err_free_completed_irqs; 166 | } 167 | PR_V1(FEESP "%d = %d\n", 168 | last_irq_index, 169 | msix_entries[last_irq_index].vector); 170 | } 171 | return 0; 172 | 173 | err_free_completed_irqs: 174 | for (i = 0; i < last_irq_index; i++) 175 | free_irq(msix_entries[i].vector, adapter); 176 | 177 | err_pci_free_irq_vectors: 178 | pci_free_irq_vectors(pdev); 179 | 180 | err_kfree_msix_entries: 181 | kfree(msix_entries); 182 | adapter->IRQ_private = NULL; // sentinel for teardown 183 | return ret; 184 | } 185 | 186 | //------------------------------------------------------------------------- 187 | // There is no disable control on this "device", hope one doesn't fire... 188 | // Can be called from setup() above so account for partial completion. 189 | 190 | void FEE_ISR_teardown(struct pci_dev *pdev) 191 | { 192 | struct FEE_adapter *adapter = pci_get_drvdata(pdev); 193 | struct msix_entry *msix_entries = adapter->IRQ_private; 194 | int i; 195 | 196 | if (!msix_entries) // Been there, done that 197 | return; 198 | 199 | for (i = 0; i < adapter->globals->nClients + 2; i++) 200 | free_irq(msix_entries[i].vector, adapter); 201 | pci_free_irq_vectors(pdev); 202 | kfree(msix_entries); 203 | adapter->IRQ_private = NULL; 204 | } 205 | -------------------------------------------------------------------------------- /shim_bridge/fee_adapter.c: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Hewlett Packard Enterprise Development LP. 3 | * All rights reserved. 4 | * 5 | * This source code file is part of the EmerGen-Z project. 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, version 2 of the License, or 10 | * (at your option) any later version. 11 | 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #include 22 | 23 | #include "fee.h" 24 | #include "genz_device.h" // core structure APIs FIXME move them 25 | 26 | //------------------------------------------------------------------------- 27 | // Slot 0 is the globals data, so disallow its use. Server id currently 28 | // follows last client but in general could be discontiguous. 29 | 30 | struct FEE_mailslot __iomem *calculate_mailslot( 31 | struct FEE_adapter *adapter, 32 | unsigned slotnum) 33 | { 34 | struct FEE_mailslot __iomem *slot; 35 | 36 | if ((slotnum < 1 || slotnum > adapter->globals->nClients) && 37 | slotnum != adapter->globals->server_id) { 38 | pr_err(FEE "mailslot %u is out of range\n", slotnum); 39 | return NULL; 40 | } 41 | slot = (void *)( 42 | (uint64_t)adapter->globals + slotnum * adapter->globals->slotsize); 43 | return slot; 44 | } 45 | 46 | //------------------------------------------------------------------------- 47 | 48 | static void unmapBARs(struct pci_dev *pdev) 49 | { 50 | struct FEE_adapter *adapter = pci_get_drvdata(pdev); 51 | 52 | if (adapter->regs) pci_iounmap(pdev, adapter->regs); // else whine 53 | adapter->regs = NULL; 54 | if (adapter->globals) pci_iounmap(pdev, adapter->globals); 55 | adapter->globals = NULL; 56 | pci_release_regions(pdev); 57 | } 58 | 59 | //------------------------------------------------------------------------- 60 | // Map the regions and overlay data structures. Since it's QEMU, ioremap 61 | // (uncached) for BAR0/1 and ioremap_cached(BAR2) would be fine. However, 62 | // the proscribed calls do the start/end/length math so use them. 63 | 64 | static int mapBARs(struct pci_dev *pdev) 65 | { 66 | struct FEE_adapter *adapter = pci_get_drvdata(pdev); 67 | int ret; 68 | 69 | // "cat /proc/iomem" seems to be very finicky about spaces and 70 | // punctuation even if there are other things in there with it. 71 | if ((ret = pci_request_regions(pdev, FEE_NAME)) < 0) { 72 | pr_err(FEESP "pci_request_regions failed: %d\n", ret); 73 | return ret; 74 | } 75 | 76 | PR_V1(FEESP "Mapping BAR0 regs (%llu bytes)\n", 77 | pci_resource_len(pdev, 0)); 78 | if (!(adapter->regs = pci_iomap(pdev, 0, 0))) 79 | goto err_unmap; 80 | 81 | PR_V1(FEESP "Mapping BAR2 globals/mailslots (%llu bytes)\n", 82 | pci_resource_len(pdev, 2)); 83 | if (!(adapter->globals = pci_iomap(pdev, 2, 0))) 84 | goto err_unmap; 85 | 86 | return 0; 87 | 88 | err_unmap: 89 | unmapBARs(pdev); 90 | return -ENOMEM; 91 | } 92 | 93 | //------------------------------------------------------------------------- 94 | 95 | void FEE_adapter_destroy(struct FEE_adapter *adapter) 96 | { 97 | struct pci_dev *pdev; 98 | 99 | if (!adapter) return; // probably not worth whining 100 | if (!(pdev = adapter->pdev)) { 101 | pr_err(FEE "destroy_adapter() has NULL pdev\n"); 102 | return; 103 | } 104 | 105 | unmapBARs(pdev); // May have be done, doesn't hurt 106 | 107 | dev_set_drvdata(&pdev->dev, NULL); 108 | pci_set_drvdata(pdev, NULL); 109 | adapter->pdev = NULL; 110 | 111 | if (adapter->IRQ_private) kfree(adapter->IRQ_private); 112 | adapter->IRQ_private = NULL; 113 | // Probably other memory leakage if this ever executes. 114 | if (adapter->outgoing) 115 | kfree(adapter->outgoing); 116 | adapter->outgoing = NULL; 117 | 118 | genz_core_structure_destroy(adapter->core); 119 | kfree(adapter); 120 | } 121 | 122 | //------------------------------------------------------------------------- 123 | // Set up more globals and mailbox references to realize dynamic padding. 124 | 125 | struct FEE_adapter *FEE_adapter_create(struct pci_dev *pdev) 126 | { 127 | struct FEE_adapter *adapter = NULL; 128 | int ret; 129 | 130 | if (!(adapter = kzalloc(sizeof(*adapter), GFP_KERNEL))) { 131 | pr_err(FEESP "Cannot kzalloc(adapter)\n"); 132 | return ERR_PTR(-ENOMEM); 133 | } 134 | 135 | // Lots of backpointers. 136 | pci_set_drvdata(pdev, adapter); // Just pass around pdev. 137 | dev_set_drvdata(&pdev->dev, adapter); // Never hurts to go deep. 138 | adapter->pdev = pdev; // Reverse pointers never hurt. 139 | adapter->slot = pdev->devfn >> 3; // Needed in a few places 140 | 141 | // Simple fields. 142 | init_waitqueue_head(&(adapter->incoming_slot_wqh)); 143 | spin_lock_init(&(adapter->incoming_slot_lock)); 144 | 145 | // Real work. 146 | if ((ret = mapBARs(pdev))) 147 | goto err_kfree; 148 | 149 | // Now that there's access to globals and registers...Docs for 150 | // pci_iomap() say to use io[read|write]32. Since this is QEMU, 151 | // direct memory references should work. The offset passed in 152 | // globals is handcrafted in Python, make sure it's all kosher. 153 | // If these fail, go back and add tests to Python, not here. 154 | ret = -EINVAL; 155 | if (offsetof(struct FEE_mailslot, buf) != adapter->globals->buf_offset) { 156 | pr_err(FEE "MSG_OFFSET global != C offset in here\n"); 157 | goto err_kfree; 158 | } 159 | if (adapter->globals->slotsize <= adapter->globals->buf_offset) { 160 | pr_err(FEE "MSG_OFFSET global is > SLOTSIZE global\n"); 161 | goto err_kfree; 162 | } 163 | adapter->max_buflen = adapter->globals->slotsize - 164 | adapter->globals->buf_offset; 165 | adapter->my_id = adapter->regs->IVPosition; 166 | 167 | // All the needed parameters are set to finish this off. 168 | if (!(adapter->my_slot = calculate_mailslot(adapter, adapter->my_id))) 169 | goto err_kfree; 170 | 171 | // Zap the slot but recover the peer_id set by server. 172 | if (adapter->my_id != adapter->my_slot->peer_id) { 173 | pr_err("Server-defined peer ID %llu is wrong\n", adapter->my_slot->peer_id); 174 | goto err_kfree; 175 | } 176 | memset(adapter->my_slot, 0, adapter->globals->slotsize); 177 | adapter->my_slot->peer_id = adapter->my_id; 178 | 179 | // Leave room for the NUL in strings. 180 | snprintf(adapter->my_slot->nodename, 181 | sizeof(adapter->my_slot->nodename) - 1, 182 | "%s.%02x", utsname()->nodename, adapter->pdev->devfn >> 3); 183 | strncpy(adapter->my_slot->cclass, DEFAULT_CCLASS, 184 | sizeof(adapter->my_slot->cclass) - 1); 185 | 186 | PR_V1(FEESP "mailslot size=%llu, buf offset=%llu, server=%llu\n", 187 | adapter->globals->slotsize, 188 | adapter->globals->buf_offset, 189 | adapter->globals->server_id); 190 | 191 | return adapter; 192 | 193 | err_kfree: 194 | FEE_adapter_destroy(adapter); 195 | return ERR_PTR(ret); 196 | } 197 | -------------------------------------------------------------------------------- /shim_bridge/fee_link.c: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Hewlett Packard Enterprise Development LP. 3 | * All rights reserved. 4 | * 5 | * This source code file is part of the EmerGen-Z project. 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, version 2 of the License, or 10 | * (at your option) any later version. 11 | 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | // Link-level messages, mostly from the switch (IVSHMSG server). 22 | // It may hijack and finish off the message. 23 | 24 | #include "fee.h" 25 | 26 | // See ivshmsg_requests.py:_Link_CTL(), etc for required formats. 27 | // I'm skipping the tracker EZT for now. 28 | 29 | #define LINK_CTL_PEER_ATTRIBUTE \ 30 | "Link CTL Peer-Attribute" 31 | 32 | #define LINK_CTL_ACK \ 33 | "Link CTL ACK C-Class=%s,CID0=%d,SID0=%d" 34 | 35 | #define CTL_WRITE_0_CID_SID \ 36 | "CTL-Write Space=0,PFMCID=%d,PFMSID=%d,CID=%d,SID=%d,Tag=%d" 37 | 38 | #define STANDALONE_ACKNOWLEDGMENT \ 39 | "Standalone Acknowledgment Tag=%d,Reason=OK" 40 | 41 | //------------------------------------------------------------------------- 42 | // This is called in interrupt context with the incoming_slot->lock held. 43 | 44 | irqreturn_t FEE_link_request(struct FEE_mailslot __iomem *incoming_slot, 45 | struct FEE_adapter *adapter) 46 | { 47 | uint32_t PFMSID, PFMCID, SID, CID, tag; 48 | char outbuf[128]; 49 | 50 | // These are all fixed values now, but someday... 51 | incoming_slot->peer_SID = GENZ_FEE_SID_DEFAULT; 52 | incoming_slot->peer_CID = incoming_slot->peer_id * 100; 53 | 54 | // Simple proof-of-life, must be an exact match. 55 | if (incoming_slot->buflen == 4 && 56 | STREQ_N(incoming_slot->buf, "ping", 4)) { 57 | incoming_slot->buflen = 0; // buf received 58 | spin_unlock(&(adapter->incoming_slot_lock)); 59 | FEE_create_outgoing( 60 | incoming_slot->peer_id, 61 | GENZ_FEE_SID_CID_IS_PEER_ID, 62 | "pong", 4, 63 | adapter); 64 | return IRQ_HANDLED; 65 | } 66 | 67 | if (STREQ_N(incoming_slot->buf, LINK_CTL_PEER_ATTRIBUTE, 68 | strlen(LINK_CTL_PEER_ATTRIBUTE))) { 69 | incoming_slot->buflen = 0; // buf received 70 | spin_unlock(&(adapter->incoming_slot_lock)); 71 | sprintf(outbuf, LINK_CTL_ACK, 72 | adapter->core->Base_C_Class_str, 73 | adapter->core->CID0, 74 | adapter->core->SID0); 75 | FEE_create_outgoing( 76 | incoming_slot->peer_id, 77 | GENZ_FEE_SID_CID_IS_PEER_ID, 78 | outbuf, strlen(outbuf), 79 | adapter); 80 | return IRQ_HANDLED; 81 | } 82 | 83 | if (sscanf(incoming_slot->buf, CTL_WRITE_0_CID_SID, 84 | &PFMCID, &PFMSID, &CID, &SID, &tag) == 5) { 85 | incoming_slot->buflen = 0; // buf received 86 | spin_unlock(&(adapter->incoming_slot_lock)); 87 | adapter->core->PFMCID = PFMCID; 88 | adapter->core->PFMSID = PFMSID; 89 | adapter->core->CID0 = CID; 90 | adapter->core->SID0 = SID; 91 | adapter->core->PMCID = -1; 92 | sprintf(outbuf, STANDALONE_ACKNOWLEDGMENT, tag); 93 | FEE_create_outgoing( 94 | incoming_slot->peer_id, 95 | GENZ_FEE_SID_CID_IS_PEER_ID, 96 | outbuf, strlen(outbuf), 97 | adapter); 98 | return IRQ_HANDLED; 99 | } 100 | 101 | return IRQ_NONE; 102 | } 103 | -------------------------------------------------------------------------------- /shim_bridge/fee_pci.c: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Hewlett Packard Enterprise Development LP. 3 | * All rights reserved. 4 | * 5 | * This source code file is part of the EmerGen-Z project. 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, version 2 of the License, or 10 | * (at your option) any later version. 11 | 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | // Initial discovery and setup of IVSHMEM/IVSHMSG devices 22 | // HP(E) lineage: res2hot from MMS PoC "mimosa" mms_base.c, flavored by zhpe. 23 | 24 | #include 25 | 26 | #include "fee.h" 27 | 28 | MODULE_LICENSE("GPL"); 29 | MODULE_VERSION(FEE_VERSION); 30 | MODULE_AUTHOR("Rocky Craig "); 31 | MODULE_DESCRIPTION("PCI shim for EmerGen-Z project on F.E.E."); 32 | 33 | // Find the one macro that does the right thing. Notice there is no "device" 34 | // for QEMU in the PCI ID database, just the sub* things. 35 | 36 | static struct pci_device_id FEE_PCI_ID_table[] = { 37 | { PCI_DEVICE_SUB( // vend, dev, subvend, subdev 38 | PCI_VENDOR_ID_REDHAT_QUMRANET, 39 | PCI_ANY_ID, 40 | PCI_SUBVENDOR_ID_REDHAT_QUMRANET, 41 | PCI_SUBDEVICE_ID_QEMU) 42 | }, 43 | { 0 }, 44 | }; 45 | 46 | MODULE_DEVICE_TABLE(pci, FEE_PCI_ID_table); // depmod, hotplug, modinfo 47 | 48 | // module parameters are global 49 | 50 | int verbose = 0; 51 | module_param(verbose, uint, 0644); 52 | MODULE_PARM_DESC(verbose, "increase amount of printk info (0)"); 53 | 54 | // Multiple bridge "devices" accepted by FEE_init_one(). PCI core might 55 | // do everything I need but I can't shake the feeling I want this for 56 | // something else...right now it just tracks insmod/rmmod. 57 | 58 | LIST_HEAD(FEE_adapter_list); 59 | DEFINE_SEMAPHORE(FEE_adapter_sema); 60 | 61 | //------------------------------------------------------------------------- 62 | // Called at insmod time and also at hotplug events (shouldn't be any). 63 | // Only take IVSHMEM (filtered by PCI core) with a BAR 1 and 64 vectors. 64 | 65 | static char get_peer_attributes[] = "Link CTL Peer-Attribute"; 66 | 67 | static int FEE_init_one( 68 | struct pci_dev *pdev, const struct pci_device_id *pdev_id) 69 | { 70 | struct FEE_adapter *adapter = NULL, *cur = NULL; 71 | int ret = -ENOTTY; 72 | 73 | PR_V1("%s(%s)\n", __FUNCTION__, CARDLOC(pdev)); 74 | 75 | if (pci_get_drvdata(pdev)) { // Is this possible? 76 | pr_err(FEESP "This device is already configured\n"); 77 | return -EALREADY; 78 | } 79 | 80 | // Enable it to discriminate values and create a configuration for 81 | // this instance. 82 | 83 | if ((ret = pci_enable_device(pdev)) < 0) { 84 | pr_err(FEESP "pci_enable_device failed: %d\n", ret); 85 | return ret; 86 | } 87 | if (pdev->revision != 1 || 88 | !pdev->msix_cap || 89 | !pci_resource_start(pdev, 1)) { 90 | PR_V1("IVSHMEM @ %s is missing IVSHMSG features\n", CARDLOC(pdev)); 91 | ret = -ENODEV; 92 | goto err_pci_disable_device; 93 | } 94 | pr_info(FEE "IVSHMEM @ %s has IVSHMSG features\n", CARDLOC(pdev)); 95 | 96 | if (IS_ERR_VALUE((adapter = FEE_adapter_create(pdev)))) { 97 | ret = PTR_ERR(adapter); 98 | adapter = NULL; 99 | goto err_pci_disable_device; 100 | } 101 | 102 | if ((ret = FEE_ISR_setup(pdev))) 103 | goto err_pci_disable_device; 104 | 105 | // It's a keeper...unless it's already there. Unlikely, but it's 106 | // not paranoia when in the kernel. 107 | ret = down_interruptible(&FEE_adapter_sema); // FIXME: deal with ret 108 | ret = 0; 109 | list_for_each_entry(cur, &FEE_adapter_list, lister) { 110 | if (STREQ(CARDLOC(pdev), pci_resource_name(cur->pdev, 1))) { 111 | ret = -EALREADY; 112 | break; 113 | } 114 | } 115 | if (!ret) { 116 | if (pdev->slot) { // See lscpi -v 117 | char newname[32]; 118 | 119 | // Originally slot number %d 120 | // pr_info("Slot name = %s\n", pci_slot_name(pdev->slot)); 121 | sprintf(newname, "%s.%02x", 122 | FEE_NAME, (unsigned)(pdev->slot->number)); 123 | ret = kobject_rename(&pdev->slot->kobj, newname); 124 | ret = 0; // __must_check, but __dont_care 125 | } 126 | list_add_tail(&adapter->lister, &FEE_adapter_list); 127 | } 128 | up(&FEE_adapter_sema); 129 | if (ret) { 130 | pr_err(FEESP "This device is already in active list\n"); 131 | goto err_MSIX_teardown; 132 | } 133 | 134 | 135 | // Get peer-attributes from ivshmsg_server; response processed inline 136 | ret = FEE_create_outgoing( 137 | adapter->globals->server_id, 138 | GENZ_FEE_SID_CID_IS_PEER_ID, 139 | get_peer_attributes, strlen(get_peer_attributes), adapter); 140 | if (ret > 0) 141 | ret = ret == strlen(get_peer_attributes) ? 0 : -EIO; 142 | if (!ret) { 143 | UPDATE_SWITCH(adapter); 144 | return ret; // else fall through 145 | } 146 | 147 | err_MSIX_teardown: 148 | PR_V1("tearing down MSI-X %s\n", CARDLOC(pdev)); 149 | FEE_ISR_teardown(pdev); 150 | 151 | err_pci_disable_device: 152 | PR_V1("disabling device %s\n", CARDLOC(pdev)); 153 | pci_disable_device(pdev); 154 | 155 | // err_destroy_adapter: 156 | FEE_adapter_destroy(adapter); 157 | return ret; 158 | } 159 | 160 | //------------------------------------------------------------------------- 161 | 162 | static void FEE_remove_one(struct pci_dev *pdev) 163 | { 164 | struct FEE_adapter *cur, *next, *adapter = pci_get_drvdata(pdev); 165 | char oldname[8]; 166 | int ret; 167 | 168 | pr_info(FEE "%s(%s): ", __FUNCTION__, CARDLOC(pdev)); 169 | if (!adapter) { 170 | pr_cont("still not my circus\n"); 171 | return; 172 | } 173 | pr_cont("disabling/removing/freeing resources\n"); 174 | 175 | // Fix lspci -v 176 | sprintf(oldname, "%u", (unsigned)(pdev->slot->number)); 177 | ret = kobject_rename(&pdev->slot->kobj, oldname); 178 | ret = 0; // __must_check, but __dont_care 179 | 180 | strcpy(adapter->my_slot->cclass, "Driverless QEMU"); 181 | UPDATE_SWITCH(adapter); 182 | 183 | FEE_ISR_teardown(pdev); 184 | 185 | pci_disable_device(pdev); 186 | 187 | if (atomic_read(&adapter->nr_users)) 188 | pr_err(FEESP "# users is non-zero, very interesting\n"); 189 | 190 | ret = down_interruptible(&FEE_adapter_sema); // FIXME: deal with ret 191 | list_for_each_entry_safe(cur, next, &FEE_adapter_list, lister) { 192 | if (STREQ(CARDLOC(cur->pdev), CARDLOC(pdev))) 193 | list_del(&(cur->lister)); 194 | } 195 | up(&FEE_adapter_sema); 196 | 197 | FEE_adapter_destroy(adapter); 198 | } 199 | 200 | //------------------------------------------------------------------------- 201 | 202 | static struct pci_driver FEE_driver = { 203 | .name = FEE_NAME, 204 | .id_table = FEE_PCI_ID_table, 205 | .probe = FEE_init_one, 206 | .remove = FEE_remove_one 207 | }; 208 | 209 | int __init FEE_init(void) 210 | { 211 | int ret; 212 | 213 | pr_info("-------------------------------------------------------"); 214 | pr_info(FEE FEE_VERSION "; parms:\n"); 215 | pr_info(FEESP "verbose = %d\n", verbose); 216 | 217 | if ((ret = pci_register_driver(&FEE_driver))) 218 | pr_err(FEE "pci_register_driver() = %d\n", ret); 219 | 220 | return ret; 221 | } 222 | 223 | module_init(FEE_init); 224 | 225 | //------------------------------------------------------------------------- 226 | // Called from rmmod. 227 | 228 | void FEE_exit(void) 229 | { 230 | pci_unregister_driver(&FEE_driver); 231 | } 232 | 233 | module_exit(FEE_exit); 234 | -------------------------------------------------------------------------------- /shim_bridge/fee_register.c: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Hewlett Packard Enterprise Development LP. 3 | * All rights reserved. 4 | * 5 | * This source code file is part of the EmerGen-Z project. 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, version 2 of the License, or 10 | * (at your option) any later version. 11 | 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include // fops->owner 25 | 26 | #include "fee.h" 27 | #include "genz_device.h" 28 | 29 | //------------------------------------------------------------------------- 30 | 31 | int FEE_register(const struct genz_core_structure *core, 32 | const struct file_operations *fops, 33 | const struct bin_attribute *attr, 34 | int onlySlot) 35 | { 36 | struct FEE_adapter *adapter; 37 | char *ownername; 38 | int ret, nbindings; 39 | 40 | if ((ret = down_interruptible(&FEE_adapter_sema))) 41 | return ret; 42 | ownername = fops->owner->name; 43 | nbindings = 0; 44 | list_for_each_entry(adapter, &FEE_adapter_list, lister) { 45 | 46 | if (onlySlot && onlySlot != adapter->slot) { 47 | pr_info(FEE "skipping slot %d\n", adapter->slot); 48 | continue; 49 | } 50 | 51 | // Device file name is meant to be reminiscent of lspci output. 52 | pr_info(FEE "binding %s to %s:\n", 53 | ownername, pci_resource_name(adapter->pdev, 1)); 54 | 55 | adapter->genz_chrdev = genz_register_char_device( 56 | core, fops, adapter, attr, adapter->slot); 57 | if (IS_ERR(adapter->genz_chrdev)) { 58 | pr_err("binding failed\n"); 59 | ret = PTR_ERR(adapter->genz_chrdev); 60 | goto up_and_out; 61 | } 62 | adapter->core = core; 63 | 64 | // Now that all allocs have worked, change adapter. Yes it's 65 | // slightly after the "live" activation, get over it. 66 | strncpy(adapter->core->Base_C_Class_str, 67 | adapter->genz_chrdev->cclass, 68 | sizeof(adapter->core->Base_C_Class_str) - 1); 69 | strncpy(adapter->my_slot->cclass, 70 | adapter->genz_chrdev->cclass, 71 | sizeof(adapter->my_slot->cclass) - 1); 72 | 73 | UPDATE_SWITCH(adapter) 74 | 75 | nbindings++; 76 | } 77 | ret = nbindings; 78 | 79 | up_and_out: 80 | up(&FEE_adapter_sema); 81 | return ret; 82 | } 83 | EXPORT_SYMBOL(FEE_register); 84 | 85 | //------------------------------------------------------------------------- 86 | // Return the count of un-bindings or -ERRNO. 87 | 88 | int FEE_unregister(const struct file_operations *fops) 89 | { 90 | struct FEE_adapter *adapter; 91 | int ret; 92 | 93 | if ((ret = down_interruptible(&FEE_adapter_sema))) 94 | return ret; 95 | 96 | ret = 0; 97 | list_for_each_entry(adapter, &FEE_adapter_list, lister) { 98 | 99 | pr_info(FEE "UNbind %s from %s: ", 100 | fops->owner->name, pci_resource_name(adapter->pdev, 0)); 101 | 102 | if (adapter->genz_chrdev && 103 | adapter->genz_chrdev->cdev.ops == fops) { 104 | genz_unregister_char_device(adapter->genz_chrdev); 105 | adapter->genz_chrdev = NULL; 106 | strncpy(adapter->my_slot->cclass, 107 | DEFAULT_CCLASS, 108 | sizeof(adapter->my_slot->cclass) - 1); 109 | strncpy(adapter->core->Base_C_Class_str, 110 | DEFAULT_CCLASS, 111 | sizeof(adapter->core->Base_C_Class_str) - 1); 112 | UPDATE_SWITCH(adapter) 113 | ret++; 114 | pr_cont("success\n"); 115 | } else { 116 | pr_cont("not actually bound\n"); 117 | pr_info("Lookup == 0x%p\n", adapter->genz_chrdev); 118 | } 119 | } 120 | up(&FEE_adapter_sema); 121 | return ret; 122 | } 123 | EXPORT_SYMBOL(FEE_unregister); 124 | -------------------------------------------------------------------------------- /shim_bridge/genz_fee-stop.service: -------------------------------------------------------------------------------- 1 | # Systemd unit file to remove modules on shutdown. This gives 2 | # them a chance to cleanly rewrite their state for monitoring 3 | # tools like Executive Cardboard and the switch "dump". 4 | 5 | # Location: /etc/systemd/system/genzfee-stop.service 6 | # After copying, register it for the first time: 7 | # sudo systemctl daemon-reload 8 | # sudo systemctl enable genzfee-stop 9 | # sudo systemctl start genzfee-stop 10 | 11 | [Unit] 12 | Description=Unload Gen-Z Fabric Emulation Environment 13 | 14 | [Service] 15 | Type=oneshot 16 | ExecStart=/bin/true 17 | RemainAfterExit=true 18 | ExecStop=/bin/bash -c 'rmmod genzfee_bridge; rmmod genzfee; rmmod genz' 19 | StandardOutput=journal 20 | 21 | [Install] 22 | WantedBy=multi-user.target 23 | -------------------------------------------------------------------------------- /shim_bridge/gf_bridge.c: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Hewlett Packard Enterprise Development LP. 3 | * All rights reserved. 4 | * 5 | * This source code file is part of the EmerGen-Z project. 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, version 2 of the License, or 10 | * (at your option) any later version. 11 | 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include // yes after the others 32 | 33 | #include "genz_class.h" 34 | #include "genz_device.h" 35 | 36 | #include "fee.h" 37 | #include "gf_bridge.h" 38 | 39 | #define __UNUSED__ __attribute__ ((unused)) 40 | 41 | MODULE_LICENSE("GPL"); 42 | MODULE_VERSION(GFBRIDGE_VERSION); 43 | MODULE_AUTHOR("Rocky Craig "); 44 | MODULE_DESCRIPTION("Soft-bridge driver for EmerGen-Z on F.E.E."); 45 | 46 | // module parameters are global 47 | 48 | int verbose = 0; 49 | module_param(verbose, uint, 0644); 50 | MODULE_PARM_DESC(verbose, "increase amount of printk info (0)"); 51 | 52 | int onlySlot = 0; // 0 == all 53 | module_param(onlySlot, uint, 0644); 54 | MODULE_PARM_DESC(onlySlot, "bind driver to this slot (0 == all)"); 55 | 56 | DECLARE_WAIT_QUEUE_HEAD(bridge_reader_wait); 57 | 58 | //------------------------------------------------------------------------- 59 | // misc_register sets up a "hooking" fops for the first open call. It 60 | // extracts its misdevice, puts it in file->private, then install the real 61 | // fops and calls open. Duplicate the private extraction here. 62 | 63 | static int gf_bridge_open(struct inode *inode, struct file *file) 64 | { 65 | struct FEE_adapter *adapter; 66 | int n, ret; 67 | 68 | // FEE drivers must do this during open() whether they use 69 | // the return value or not. Later APIs need it. 70 | adapter = file->private_data = 71 | genz_char_drv_1stopen_private_data(file); 72 | 73 | // pr_info("bridge_open() file->private_data @ 0x%p\n", adapter); 74 | 75 | // FIXME: got to come up with more 'local module' support for this. 76 | // Just keep it single user for now. This code was from an earlier 77 | // incantation, the "file->private_data" voodoo is another way to 78 | // do single open. I may need this later. 79 | 80 | ret = 0; 81 | if ((n = atomic_add_return(1, &adapter->nr_users) == 1)) { 82 | struct bridge_buffers *buffers; 83 | 84 | if (!(buffers = kzalloc(sizeof(*buffers), GFP_KERNEL))) { 85 | ret = -ENOMEM; 86 | goto alldone; 87 | } 88 | if (!(buffers->wbuf = kzalloc(adapter->max_buflen, GFP_KERNEL))) { 89 | kfree(buffers); 90 | ret = -ENOMEM; 91 | goto alldone; 92 | } 93 | mutex_init(&(buffers->wbuf_mutex)); 94 | adapter->outgoing = buffers; 95 | } else { 96 | pr_warn(GFBRSP "Sorry, just exclusive open() for now\n"); 97 | ret = -EBUSY; 98 | goto alldone; 99 | } 100 | 101 | PR_V1("open: %d users\n", atomic_read(&adapter->nr_users)); 102 | 103 | alldone: 104 | if (ret) 105 | atomic_dec(&adapter->nr_users); 106 | return ret; 107 | } 108 | 109 | //------------------------------------------------------------------------- 110 | // At any close of a process fd 111 | 112 | static int gf_bridge_flush(struct file *file, fl_owner_t id) 113 | { 114 | struct FEE_adapter *adapter = file->private_data; 115 | int nr_users, f_count; 116 | 117 | spin_lock(&file->f_lock); 118 | nr_users = atomic_read(&adapter->nr_users); 119 | f_count = atomic_long_read(&file->f_count); 120 | spin_unlock(&file->f_lock); 121 | if (f_count == 1) { 122 | atomic_dec(&adapter->nr_users); 123 | nr_users--; 124 | } 125 | 126 | PR_V1("flush: after (optional) dec: %d users, file count = %d\n", 127 | nr_users, f_count); 128 | 129 | return 0; 130 | } 131 | 132 | //------------------------------------------------------------------------- 133 | // Only at the final close of the last process fd 134 | 135 | static int gf_bridge_release(struct inode *inode, struct file *file) 136 | { 137 | struct FEE_adapter *adapter = file->private_data; 138 | struct bridge_buffers *buffers = adapter->outgoing; 139 | int nr_users, f_count; 140 | 141 | spin_lock(&file->f_lock); 142 | nr_users = atomic_read(&adapter->nr_users); 143 | f_count = atomic_long_read(&file->f_count); 144 | spin_unlock(&file->f_lock); 145 | PR_V1("release: %d users, file count = %d\n", nr_users, f_count); 146 | BUG_ON(nr_users); 147 | kfree(buffers->wbuf); 148 | kfree(buffers); 149 | adapter->outgoing = NULL; 150 | return 0; 151 | } 152 | 153 | //------------------------------------------------------------------------- 154 | // Prepend the sender id as a field separated by a colon, realized by two 155 | // calls to copy_to_user and avoiding a temporary buffer here. copy_to_user 156 | // can sleep and returns the number of bytes that could NOT be copied or 157 | // -ERRNO. Require both copies to work all the way. 158 | 159 | static ssize_t gf_bridge_read(struct file *file, char __user *buf, 160 | size_t buflen, loff_t *ppos) 161 | { 162 | struct FEE_adapter *adapter = file->private_data; 163 | struct FEE_mailslot *sender; 164 | int ret, n; 165 | // SID is 28 bits or 10 decimal digits; CID is 16 bits or 5 digits 166 | // so make the buffer big enough. 167 | char sidcidstr[32]; 168 | 169 | // A successful return needs cleanup via FEE_release_incoming(). 170 | sender = FEE_await_incoming(adapter, file->f_flags & O_NONBLOCK); 171 | if (IS_ERR(sender)) 172 | return PTR_ERR(sender); 173 | PR_V2(GFBRSP "wait finished, %llu bytes to read\n", sender->buflen); 174 | 175 | // Two parts to the response: first is the sender "CID,SID:". 176 | // Omit the [] brackets commonly seen in the spec, ala [CID,SID]. 177 | n = snprintf(sidcidstr, sizeof(sidcidstr) - 1, 178 | "%llu,%llu:", sender->peer_CID, sender->peer_SID); 179 | 180 | if (n >= sizeof(sidcidstr) || buflen < sender->buflen + n - 1) { 181 | ret = -E2BIG; 182 | goto read_complete; 183 | } 184 | if ((ret = copy_to_user(buf, sidcidstr, n))) { 185 | if (ret > 0) ret= -EFAULT; // partial transfer 186 | goto read_complete; 187 | } 188 | 189 | // The message body follows the colon of the previous snippet. 190 | ret = copy_to_user(buf + (uint64_t)n, sender->buf, sender->buflen); 191 | ret = !ret ? sender->buflen + n : 192 | (ret > 0 ? -EFAULT : ret); 193 | // Now it's either the length of the full responose or -ESOMETHING 194 | if (ret > 0) 195 | *ppos = 0; 196 | 197 | read_complete: // Whether I used it or not, let everything go 198 | FEE_release_incoming(adapter); 199 | return ret; 200 | } 201 | 202 | //------------------------------------------------------------------------- 203 | // Use many idiot checks. Performance is not the issue here. The data 204 | // might be binary (including unprintables and NULs), not just a C string. 205 | 206 | static ssize_t gf_bridge_write(struct file *file, const char __user *buf, 207 | size_t buflen, loff_t *ppos) 208 | { 209 | struct FEE_adapter *adapter = file->private_data; 210 | struct bridge_buffers *buffers = adapter->outgoing; 211 | ssize_t successlen = buflen; 212 | char *bufbody; 213 | int ret, restarts, SID, CID; 214 | 215 | if (buflen >= adapter->max_buflen - 1) { // Paranoia on term NUL 216 | PR_V1("buflen of %lu is too big\n", buflen); 217 | return -E2BIG; 218 | } 219 | mutex_lock(&buffers->wbuf_mutex); // Multiuse of *file 220 | if ((ret = copy_from_user(buffers->wbuf, buf, buflen))) { 221 | if (ret > 0) 222 | ret = -EFAULT; 223 | goto unlock_return; 224 | } 225 | // Even if it's not a string, this puts a bound on the strchr(':') 226 | buffers->wbuf[buflen] = '\0'; 227 | 228 | // Split body into two pieces around the first colon: a proper string 229 | // and whatever the real payload is (string or binary). 230 | if (!(bufbody = strchr(buffers->wbuf, ':'))) { 231 | pr_err(GFBR "no colon in \"%s\"\n", buffers->wbuf); 232 | ret = -EBADMSG; 233 | goto unlock_return; 234 | } 235 | *bufbody = '\0'; // chomp ':', now two NUL-terminated sections 236 | bufbody++; 237 | buflen -= (uint64_t)bufbody - (uint64_t)buffers->wbuf; 238 | 239 | // SID and CID from varying input, including "expert use" of a peer id. 240 | 241 | SID = GENZ_FEE_SID_CID_IS_PEER_ID; 242 | if (STREQ(buffers->wbuf, "server") || STREQ(buffers->wbuf, "switch") || 243 | STREQ(buffers->wbuf, "link") || STREQ(buffers->wbuf, "interface")) 244 | CID = adapter->globals->server_id; 245 | else { 246 | char *comma = strchr(buffers->wbuf, ','); // Want CID,SID 247 | 248 | if (comma) { 249 | *comma = '\0'; 250 | if ((ret = kstrtoint(buffers->wbuf, 0, &CID))) 251 | goto unlock_return; 252 | if ((ret = kstrtoint(comma + 1, 0, &SID))) 253 | goto unlock_return; 254 | } else { // Direct use of an IVSHMSG peer id 255 | if ((ret = kstrtoint(buffers->wbuf, 0, &CID))) 256 | goto unlock_return; 257 | } 258 | } 259 | 260 | // Length or -ERRNO. If length matched, then all is well, but 261 | // this final len is always shorter than the original length. Some 262 | // code (ie, "echo") will resubmit the partial if the count is 263 | // short. So lie about it to the caller. 264 | 265 | restarts = 0; 266 | restart: 267 | ret = FEE_create_outgoing(CID, SID, bufbody, buflen, adapter); 268 | if (ret == -ERESTARTSYS) { // spurious timeout 269 | if (restarts++ < 2) 270 | goto restart; 271 | ret = -ETIMEDOUT; 272 | } else if (ret == buflen) 273 | ret = successlen; 274 | else if (ret >= 0) 275 | ret = -EIO; // partial transfer paranoia 276 | 277 | unlock_return: 278 | mutex_unlock(&buffers->wbuf_mutex); 279 | return ret; 280 | } 281 | 282 | //------------------------------------------------------------------------- 283 | // Callbacks on activity against /sys/devices/..../thisdev. Note the brdev 284 | // has "core" field. 285 | 286 | static ssize_t gf_bridge_sysfs_read( 287 | struct file *file, struct kobject *kobj, struct bin_attribute *bin_attr, 288 | char *buf, loff_t offset, size_t size) 289 | { 290 | struct genz_char_device *brdev = bin_attr->private; 291 | 292 | pr_info("%s(%s->%s, %lu bytes @ %lld)\n", 293 | __FUNCTION__, kobj->name, bin_attr->attr.name, size, offset); 294 | memset(buf, 0, size); 295 | if (strcmp(bin_attr->attr.name, "core")) 296 | snprintf(buf, size - 1, "You are in the bridge driver.\n"); 297 | else 298 | snprintf(buf, size - 1, 299 | "SID0=%d\nCID0=%d\nMaxInterface=%d\n", 300 | brdev->core->SID0, 301 | brdev->core->CID0, 302 | brdev->core->MaxInterface 303 | ); 304 | return size; 305 | } 306 | 307 | static ssize_t gf_bridge_sysfs_write( 308 | struct file *file, struct kobject *kobj, struct bin_attribute *bin_attr, 309 | char *buf, loff_t offset, size_t size) 310 | { 311 | __UNUSED__ struct genz_char_device *brdev = bin_attr->private; 312 | 313 | pr_info("%s(%s->%s, %lu bytes @ %lld)\n", 314 | __FUNCTION__, kobj->name, bin_attr->attr.name, size, offset); 315 | buf[size - 1] = '\0'; 316 | if (size < 128) 317 | pr_cont(" = %s", buf); 318 | pr_cont("\n"); 319 | return size; 320 | } 321 | 322 | 323 | //------------------------------------------------------------------------- 324 | // Returning 0 will cause the caller (epoll/poll/select) to sleep. 325 | 326 | static uint gf_bridge_poll(struct file *file, struct poll_table_struct *wait) 327 | { 328 | struct FEE_adapter *adapter = file->private_data; 329 | uint ret = 0; 330 | 331 | poll_wait(file, &bridge_reader_wait, wait); 332 | ret |= POLLIN | POLLRDNORM; 333 | // FIXME encapsulate this better, it's really the purview of sendstring 334 | if (!adapter->my_slot->buflen) 335 | ret |= POLLOUT | POLLWRNORM; 336 | return ret; 337 | } 338 | 339 | // Symbols show up in /proc/kallsyms so spell them out. 340 | static const struct file_operations gf_bridge_fops = { 341 | .owner = THIS_MODULE, 342 | .open = gf_bridge_open, 343 | .flush = gf_bridge_flush, 344 | .release = gf_bridge_release, 345 | .read = gf_bridge_read, 346 | .write = gf_bridge_write, 347 | .poll = gf_bridge_poll, 348 | }; 349 | 350 | static const struct bin_attribute gf_bridge_sysfs_helper = { 351 | .read = gf_bridge_sysfs_read, 352 | .write = gf_bridge_sysfs_write, 353 | .mmap = NULL, // Lest there be any uncertainty. 354 | .private = NULL, // Gets chrdev unless overridden. 355 | }; 356 | 357 | //------------------------------------------------------------------------- 358 | // Called from insmod. Bind the driver set to all available FEE devices. 359 | 360 | static int _nbindings = 0; 361 | 362 | int __init gf_bridge_init(void) 363 | { 364 | int ret; 365 | struct genz_core_structure *core; 366 | 367 | pr_info("-------------------------------------------------------"); 368 | pr_info(GFBR GFBRIDGE_VERSION "; parms:\n"); 369 | pr_info(GFBRSP "verbose = %d\n", verbose); 370 | 371 | if (IS_ERR_OR_NULL( 372 | (core = genz_core_structure_create(GENZ_CCE_DISCRETE_BRIDGE)))) 373 | return -ENOMEM; 374 | core->MaxInterface = 2; 375 | core->MaxCTL = 8192; // Non-zero 376 | 377 | _nbindings = 0; 378 | if ((ret = FEE_register(core, &gf_bridge_fops, &gf_bridge_sysfs_helper, onlySlot)) < 0) 379 | return ret; 380 | _nbindings = ret; 381 | pr_info(GFBR "%d bindings made\n", _nbindings); 382 | return _nbindings ? 0 : -ENODEV; 383 | } 384 | 385 | module_init(gf_bridge_init); 386 | 387 | //------------------------------------------------------------------------- 388 | // Called from rmmod. Unbind this driver set from any registered bindings. 389 | 390 | void gf_bridge_exit(void) 391 | { 392 | int ret = FEE_unregister(&gf_bridge_fops); 393 | if (ret >= 0) 394 | pr_info(GFBR "%d/%d bindings released\n", ret, _nbindings); 395 | else 396 | pr_err(GFBR "module exit errno %d\n", -ret); 397 | } 398 | 399 | module_exit(gf_bridge_exit); 400 | -------------------------------------------------------------------------------- /shim_bridge/gf_bridge.h: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Hewlett Packard Enterprise Development LP. 3 | * All rights reserved. 4 | * 5 | * This source code file is part of the EmerGen-Z project. 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, version 2 of the License, or 10 | * (at your option) any later version. 11 | 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | // Bridge structures 22 | 23 | #ifndef GENZFEE_BRIDGE_DOT_H 24 | #define GENZFEE_BRIDGE_DOT_H 25 | 26 | #include 27 | #include 28 | 29 | #define GFBRIDGE_DEBUG // See "Debug assistance" below 30 | 31 | #define GFBRIDGE_NAME "gfbridge" 32 | #define GFBR "gfbr: " // pr_xxxx header 33 | #define GFBRSP " " // pr_xxxx header same length indent 34 | 35 | #define GFBRIDGE_VERSION GFBRIDGE_NAME " v0.1.0: gotta start somewhere" 36 | 37 | // Just write support for now. 38 | struct bridge_buffers { 39 | char *wbuf; // kmalloc(max_msglen) 40 | struct mutex wbuf_mutex; 41 | }; 42 | 43 | //------------------------------------------------------------------------- 44 | // Debug support 45 | 46 | #ifndef PR_V1 // Avoid "redefine" errors 47 | #ifdef GFBRIDGE_DEBUG 48 | #define PR_V1(a...) { if (verbose) pr_info(GFBR a); } 49 | #define PR_V2(a...) { if (verbose > 1) pr_info(GFBR a); } 50 | #define PR_V3(a...) { if (verbose > 2) pr_info(GFBR a); } 51 | #else 52 | #define PR_V1(a...) 53 | #define PR_V2(a...) 54 | #define PR_V3(a...) 55 | #endif 56 | #endif 57 | 58 | #ifndef PR_ENTER 59 | #define _F_ __FUNCTION__ 60 | #define PR_ENTER(a...) { if (verbose) { \ 61 | pr_info(GFBR "enter %s: ", _F_); pr_cont(a); }} 62 | #define PR_EXIT(a...) { if (verbose) { \ 63 | pr_info(GFBR "exit %s: ", _F_); pr_cont(a); }} 64 | #undefine _F_ 65 | #endif 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /subsystem/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for prototype Gen-Z subsystem 2 | 3 | VERBOSE=0 4 | 5 | CONFIG_GENZ ?= m 6 | 7 | KERNELBASE = /lib/modules/$(shell uname -r) 8 | KERNELDIR ?= $(KERNELBASE)/build 9 | SHELL=/bin/bash 10 | PWD:=$(shell /bin/pwd) 11 | 12 | # struct wait_queue_head, among others, didn't show up until 4.13, but 13 | # was backported into SLES15 (kernel 4.12). It's a gross test but 14 | # better than nothing. 15 | VMIN:=4 16 | PMIN:=13 17 | V:=$(shell make --no-print-directory -C ${KERNELDIR} kernelversion | cut -d. -f1) 18 | P:=$(shell make --no-print-directory -C ${KERNELDIR} kernelversion | cut -d. -f2) 19 | VFAIL:=Kernel headers are $V.$P, need \>= ${VMIN}.${PMIN} 20 | VFAILNOBACK:=Kernel headers are $V.$P, no backport from \>= ${VMIN}.${PMIN} 21 | 22 | obj-$(CONFIG_GENZ) += genz.o 23 | 24 | genz-objs := genz_bus.o genz_class.o genz_device.o 25 | 26 | RUNNING_ARCH := $(shell dpkg-architecture -qDEB_BUILD_ARCH_CPU 2>/dev/null) 27 | 28 | all: versioncheck 29 | ifeq "$(RUNNING_ARCH)" "amd64" 30 | make $(CFLAGS) V=$(VERBOSE) -C $(KERNELDIR) M=$(PWD) ARCH=x86 modules 31 | else 32 | make $(CFLAGS) V=$(VERBOSE) -C $(KERNELDIR) M=$(PWD) modules 33 | endif 34 | 35 | modules_install: all 36 | INSTALL_MOD_DIR=genz sudo -E make V=$(VERBOSE) -C $(KERNELDIR) M=$(PWD) modules_install 37 | sudo -E depmod -a 38 | 39 | clean: 40 | ifeq "$(architecture)" "amd64" 41 | make -C $(KERNELDIR) M=$(PWD) ARCH=x86 clean 42 | else 43 | make -C $(KERNELDIR) M=$(PWD) clean 44 | endif 45 | 46 | # Kernel 3 is bad, 5 is good, 4 needs a closer look. 47 | versioncheck: 48 | @[ $V -lt ${VMIN} ] && echo ${VFAIL} && exit 1; \ 49 | [ $V -gt ${VMIN} ] && exit 0; \ 50 | [ $P -ge ${PMIN} ] && exit 0; \ 51 | grep -q 'struct wait_queue_head' ${KERNELDIR}/include/linux/wait.h && exit 0; \ 52 | echo ${VFAILNOBACK}; exit 1 53 | 54 | -------------------------------------------------------------------------------- /subsystem/drivers.base.base.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | #include 3 | 4 | /** 5 | * struct subsys_private - structure to hold the private to the driver core portions of the bus_type/class structure. 6 | * 7 | * @subsys - the struct kset that defines this subsystem 8 | * @devices_kset - the subsystem's 'devices' directory 9 | * @interfaces - list of subsystem interfaces associated 10 | * @mutex - protect the devices, and interfaces lists. 11 | * 12 | * @drivers_kset - the list of drivers associated 13 | * @klist_devices - the klist to iterate over the @devices_kset 14 | * @klist_drivers - the klist to iterate over the @drivers_kset 15 | * @bus_notifier - the bus notifier list for anything that cares about things 16 | * on this bus. 17 | * @bus - pointer back to the struct bus_type that this structure is associated 18 | * with. 19 | * 20 | * @glue_dirs - "glue" directory to put in-between the parent device to 21 | * avoid namespace conflicts 22 | * @class - pointer back to the struct class that this structure is associated 23 | * with. 24 | * 25 | * This structure is the one that is the actual kobject allowing struct 26 | * bus_type/class to be statically allocated safely. Nothing outside of the 27 | * driver core should ever touch these fields. 28 | */ 29 | struct subsys_private { 30 | struct kset subsys; 31 | struct kset *devices_kset; 32 | struct list_head interfaces; 33 | struct mutex mutex; 34 | 35 | struct kset *drivers_kset; 36 | struct klist klist_devices; 37 | struct klist klist_drivers; 38 | struct blocking_notifier_head bus_notifier; 39 | unsigned int drivers_autoprobe:1; 40 | struct bus_type *bus; 41 | 42 | struct kset glue_dirs; 43 | struct class *class; 44 | }; 45 | #define to_subsys_private(obj) container_of(obj, struct subsys_private, subsys.kobj) 46 | 47 | struct driver_private { 48 | struct kobject kobj; 49 | struct klist klist_devices; 50 | struct klist_node knode_bus; 51 | struct module_kobject *mkobj; 52 | struct device_driver *driver; 53 | }; 54 | #define to_driver(obj) container_of(obj, struct driver_private, kobj) 55 | 56 | /** 57 | * struct device_private - structure to hold the private to the driver core portions of the device structure. 58 | * 59 | * @klist_children - klist containing all children of this device 60 | * @knode_parent - node in sibling list 61 | * @knode_driver - node in driver list 62 | * @knode_bus - node in bus list 63 | * @deferred_probe - entry in deferred_probe_list which is used to retry the 64 | * binding of drivers which were unable to get all the resources needed by 65 | * the device; typically because it depends on another driver getting 66 | * probed first. 67 | * @device - pointer back to the struct device that this structure is 68 | * associated with. 69 | * 70 | * Nothing outside of the driver core should ever touch these fields. 71 | */ 72 | struct device_private { 73 | struct klist klist_children; 74 | struct klist_node knode_parent; 75 | struct klist_node knode_driver; 76 | struct klist_node knode_bus; 77 | struct list_head deferred_probe; 78 | struct device *device; 79 | }; 80 | #define to_device_private_parent(obj) \ 81 | container_of(obj, struct device_private, knode_parent) 82 | #define to_device_private_driver(obj) \ 83 | container_of(obj, struct device_private, knode_driver) 84 | #define to_device_private_bus(obj) \ 85 | container_of(obj, struct device_private, knode_bus) 86 | 87 | /* initialisation functions */ 88 | extern int devices_init(void); 89 | extern int buses_init(void); 90 | extern int classes_init(void); 91 | extern int firmware_init(void); 92 | #ifdef CONFIG_SYS_HYPERVISOR 93 | extern int hypervisor_init(void); 94 | #else 95 | static inline int hypervisor_init(void) { return 0; } 96 | #endif 97 | extern int platform_bus_init(void); 98 | extern void cpu_dev_init(void); 99 | extern void container_dev_init(void); 100 | 101 | struct kobject *virtual_device_parent(struct device *dev); 102 | 103 | extern int bus_add_device(struct device *dev); 104 | extern void bus_probe_device(struct device *dev); 105 | extern void bus_remove_device(struct device *dev); 106 | 107 | extern int bus_add_driver(struct device_driver *drv); 108 | extern void bus_remove_driver(struct device_driver *drv); 109 | extern void device_release_driver_internal(struct device *dev, 110 | struct device_driver *drv, 111 | struct device *parent); 112 | 113 | extern void driver_detach(struct device_driver *drv); 114 | extern int driver_probe_device(struct device_driver *drv, struct device *dev); 115 | extern void driver_deferred_probe_del(struct device *dev); 116 | static inline int driver_match_device(struct device_driver *drv, 117 | struct device *dev) 118 | { 119 | return drv->bus->match ? drv->bus->match(dev, drv) : 1; 120 | } 121 | extern bool driver_allows_async_probing(struct device_driver *drv); 122 | 123 | extern int driver_add_groups(struct device_driver *drv, 124 | const struct attribute_group **groups); 125 | extern void driver_remove_groups(struct device_driver *drv, 126 | const struct attribute_group **groups); 127 | 128 | extern char *make_class_name(const char *name, struct kobject *kobj); 129 | 130 | extern int devres_release_all(struct device *dev); 131 | extern void device_block_probing(void); 132 | extern void device_unblock_probing(void); 133 | 134 | /* /sys/devices directory */ 135 | extern struct kset *devices_kset; 136 | extern void devices_kset_move_last(struct device *dev); 137 | 138 | #if defined(CONFIG_MODULES) && defined(CONFIG_SYSFS) 139 | extern void module_add_driver(struct module *mod, struct device_driver *drv); 140 | extern void module_remove_driver(struct device_driver *drv); 141 | #else 142 | static inline void module_add_driver(struct module *mod, 143 | struct device_driver *drv) { } 144 | static inline void module_remove_driver(struct device_driver *drv) { } 145 | #endif 146 | 147 | #ifdef CONFIG_DEVTMPFS 148 | extern int devtmpfs_init(void); 149 | #else 150 | static inline int devtmpfs_init(void) { return 0; } 151 | #endif 152 | 153 | /* Device links support */ 154 | extern int device_links_read_lock(void); 155 | extern void device_links_read_unlock(int idx); 156 | extern int device_links_check_suppliers(struct device *dev); 157 | extern void device_links_driver_bound(struct device *dev); 158 | extern void device_links_driver_cleanup(struct device *dev); 159 | extern void device_links_no_driver(struct device *dev); 160 | extern bool device_links_busy(struct device *dev); 161 | extern void device_links_unbind_consumers(struct device *dev); 162 | 163 | /* device pm support */ 164 | void device_pm_move_to_tail(struct device *dev); 165 | 166 | -------------------------------------------------------------------------------- /subsystem/genz_bus.c: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018-2019 Hewlett Packard Enterprise Development LP. 3 | * All rights reserved. 4 | * 5 | * This source code file is part of the EmerGen-Z project. 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, version 2 of the License, or 10 | * (at your option) any later version. 11 | 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "genz_bus.h" 28 | #include "genz_class.h" 29 | #include "genz_subsystem.h" 30 | 31 | #include "drivers.base.base.h" // Copied from 4.19 32 | 33 | MODULE_LICENSE("GPL"); 34 | MODULE_VERSION(DRV_VERSION); 35 | 36 | int verbose = 0; 37 | module_param(verbose, uint, 0644); 38 | MODULE_PARM_DESC(verbose, "increase amount of printk info (0)"); 39 | 40 | //------------------------------------------------------------------------- 41 | // Boolean 42 | 43 | static int genz_match(struct device *dev, struct device_driver *drv) 44 | { 45 | struct genz_device __unused *genz_dev = to_genz_dev(dev); 46 | 47 | pr_info("%s()\n", __FUNCTION__); 48 | return 1; 49 | }; 50 | 51 | //------------------------------------------------------------------------- 52 | 53 | static int genz_num_vf(struct device *dev) 54 | { 55 | pr_info("%s()\n", __FUNCTION__); 56 | return 1; 57 | } 58 | 59 | //------------------------------------------------------------------------- 60 | // Kernel bitches at you without one of these. 61 | 62 | static int genz_remove_one_device(struct device *dev) 63 | { 64 | struct genz_device __unused *genz_dev = to_genz_dev(dev); 65 | // struct genz_driver *drv = genz_dev->driver; 66 | 67 | pr_info("%s()\n", __FUNCTION__); 68 | return 0; 69 | } 70 | 71 | //----------------------------------------------------------------------- 72 | // FIXME: how is this called? 73 | 74 | static int genz_dev_init(struct genz_device *dev) 75 | { 76 | pr_info("%s()\n", __FUNCTION__); 77 | 78 | return 0; 79 | } 80 | 81 | static void genz_dev_uninit(struct genz_device *dev) 82 | { 83 | pr_info("%s()\n", __FUNCTION__); 84 | } 85 | 86 | static const struct genz_device_ops devops = { 87 | .init = genz_dev_init, 88 | .uninit = genz_dev_uninit, 89 | }; 90 | 91 | //----------------------------------------------------------------------- 92 | // This is a global common setup for all genz_devs, followed by a "personal" 93 | // customization callback. See the dummy driver and alloc_netdev. 94 | 95 | struct genz_device *alloc_genzdev(const char *namefmt, 96 | void (*customize_cb)(struct genz_device *)) 97 | { 98 | struct genz_device *genz_dev; 99 | 100 | pr_info("%s()\n", __FUNCTION__); 101 | 102 | if (!(genz_dev = kzalloc(sizeof(struct genz_device), GFP_KERNEL))) { 103 | PR_ERR("failed kzalloc\n"); 104 | return NULL; 105 | } 106 | // FIXME: does it need to be 32-byte aligned like alloc_netdev_mqs? 107 | strcpy(genz_dev->namefmt, namefmt); 108 | customize_cb(genz_dev); 109 | return genz_dev; 110 | } 111 | EXPORT_SYMBOL(alloc_genzdev); 112 | 113 | //----------------------------------------------------------------------- 114 | 115 | static struct genz_device *the_one = NULL; 116 | 117 | int register_genzdev(struct genz_device *genz_dev) { 118 | pr_info("%s()\n", __FUNCTION__); 119 | if (the_one) 120 | return -EALREADY; 121 | the_one = genz_dev; 122 | return 0; 123 | } 124 | EXPORT_SYMBOL(register_genzdev); 125 | 126 | //----------------------------------------------------------------------- 127 | 128 | void unregister_genzdev(struct genz_device *genz_dev) { 129 | pr_info("%s()\n", __FUNCTION__); 130 | if (the_one && the_one == genz_dev) 131 | the_one = NULL; 132 | } 133 | EXPORT_SYMBOL(unregister_genzdev); 134 | 135 | //----------------------------------------------------------------------- 136 | // A callback for the global alloc_genzdev() 137 | 138 | static void genz_device_customize(struct genz_device *genz_dev) 139 | { 140 | pr_info("%s()\n", __FUNCTION__); 141 | } 142 | 143 | int genz_init_one(struct device *dev) 144 | { 145 | struct genz_device *genz_dev; 146 | int ret = 0; 147 | 148 | pr_info("%s()\n", __FUNCTION__); 149 | 150 | if (!(genz_dev = alloc_genzdev("genz%02d", genz_device_customize))) { 151 | PR_ERR("alloc_genzdev() failed\n"); 152 | return -ENOMEM; // It had ONE job... 153 | } 154 | 155 | if ((ret = register_genzdev(genz_dev))) 156 | PR_ERR("register_genzdev() failed\n"); 157 | 158 | return ret; 159 | } 160 | 161 | struct bus_type genz_bus_type = { // directly in /sys/bus 162 | .name = "genz_fabric", 163 | .dev_name = "genz%u", // "subsystem enumeration" 164 | .dev_root = NULL, // "Default parent device" 165 | .match = genz_match, // Verify driver<->device permutations 166 | .probe = genz_init_one, // if match() call drv.probe 167 | .remove = genz_remove_one_device, 168 | .num_vf = genz_num_vf, 169 | }; 170 | 171 | static struct kset *fabrics_kset; 172 | 173 | #define MAXBUSES 254 // Fit into "0x%02x" format 174 | #define POTPOURRID (MAXBUSES + 1) // The "pick it for me" bus 175 | 176 | static DEFINE_MUTEX(genz_bus_mutex); 177 | static LIST_HEAD(genz_bus_list); 178 | 179 | struct device *genz_root_device = NULL; // create a directory in /sys/devices 180 | 181 | static void pleeeeeeaseReleaseMeLetMeGo(struct device *pdev) { 182 | pr_info("%s(%s)\n", __FUNCTION__, dev_name(pdev)); // initname? 183 | } 184 | 185 | /* 186 | * Yes this needs to be extended to more than PCI slot number 187 | * but it will do for now. 188 | */ 189 | 190 | struct device *genz_find_bus_by_PCIslotnum(int desired) 191 | { 192 | struct genz_bus_instance *tmp, *match = NULL; 193 | 194 | if (desired > MAXBUSES) 195 | return NULL; 196 | if (desired < 0) 197 | desired = POTPOURRID; 198 | 199 | mutex_lock(&genz_bus_mutex); 200 | 201 | match = NULL; 202 | list_for_each_entry(tmp, &genz_bus_list, bus_lister) { 203 | if (desired == tmp->id) { 204 | match = tmp; 205 | break; 206 | } 207 | } 208 | if (!match) { // Per comments in source, zero it first. 209 | if (!(match = kzalloc(sizeof(struct genz_bus_instance), GFP_KERNEL))) 210 | goto all_done; 211 | match->id = desired; 212 | INIT_LIST_HEAD(&match->bus_lister); 213 | 214 | // LDD3:14 Device Model -> "Device Registration"; see also source for 215 | // "subsys_register()". Need a separate object from bus to form an 216 | // anchor point; it's not a fully fleshed-out struct device but serves 217 | // the anchor purpose. device_register() does both of these but I 218 | // want to change the bus and name, so do it in two pieces. 219 | // .parent = NULL (ie, after kzalloc) lands at the top of /sys/devices 220 | // which seems good. Start with the lists/mutex/kobj of struct device. 221 | 222 | device_initialize(&(match->bus_dev)); 223 | match->bus_dev.bus = &genz_bus_type; 224 | match->bus_dev.release = pleeeeeeaseReleaseMeLetMeGo; 225 | dev_set_name(&match->bus_dev, "genz%02x", match->id); // kobj 226 | if (device_add(&match->bus_dev)) { 227 | PR_ERR("device_add(0x%02x) failed\n", match->id); 228 | kfree(match); 229 | match = NULL; 230 | goto all_done; 231 | } 232 | list_add_tail(&match->bus_lister, &genz_bus_list); 233 | 234 | } 235 | 236 | all_done: 237 | mutex_unlock(&genz_bus_mutex); 238 | 239 | return match ? &(match->bus_dev) : NULL; 240 | } 241 | 242 | void genz_subsystem_exit(void) 243 | { 244 | struct genz_bus_instance *bus, *tmp; 245 | 246 | pr_info("%s()\n", __FUNCTION__); 247 | list_for_each_entry_safe(bus, tmp, &genz_bus_list, bus_lister) { 248 | // if device_add() was called, must use this 249 | device_del(&bus->bus_dev); 250 | put_device(&bus->bus_dev); // FIXME: better in release()? 251 | kfree(bus); 252 | } 253 | kset_unregister(fabrics_kset); 254 | root_device_unregister(genz_root_device); 255 | bus_unregister(&genz_bus_type); 256 | genz_classes_destroy(); 257 | } 258 | 259 | int __init genz_subsystem_init(void) 260 | { 261 | int ret = 0; 262 | 263 | pr_info("%s()\n", __FUNCTION__); 264 | 265 | if ((ret = genz_classes_init())) { 266 | PR_ERR("genz_classes_init() failed\n"); 267 | return ret; 268 | } 269 | if (!(genz_root_device = root_device_register("genz"))) { 270 | genz_classes_destroy(); 271 | return ret; 272 | } 273 | if ((ret = bus_register(&genz_bus_type))) { 274 | PR_ERR("bus_register() failed\n"); 275 | root_device_unregister(genz_root_device); 276 | genz_classes_destroy(); 277 | return ret; 278 | } 279 | // Add a fabrics directory. This is so documented against, 280 | // but stolen from bus_register(). 281 | if (!(fabrics_kset = kset_create_and_add( 282 | "fabrics", NULL, &genz_bus_type.p->subsys.kobj))) { 283 | PR_ERR("couldn't create fabrics directory\n"); 284 | bus_unregister(&genz_bus_type); 285 | root_device_unregister(genz_root_device); 286 | genz_classes_destroy(); 287 | return -ENOMEM; 288 | } 289 | return 0; 290 | } 291 | 292 | module_init(genz_subsystem_init); 293 | module_exit(genz_subsystem_exit); 294 | -------------------------------------------------------------------------------- /subsystem/genz_bus.h: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018-2019 Hewlett Packard Enterprise Development LP. 3 | * All rights reserved. 4 | * 5 | * This source code file is part of the EmerGen-Z project. 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, version 2 of the License, or 10 | * (at your option) any later version. 11 | 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #ifndef GENZ_BUS_DOT_H 22 | #define GENZ_BUS_DOT_H 23 | 24 | #include 25 | #include 26 | 27 | #include "genz_device.h" 28 | 29 | struct genz_bus_instance { 30 | struct list_head bus_lister; 31 | unsigned id; // from hints to genz_find_bus_by_xxx 32 | struct device bus_dev; 33 | struct kobject *sysFabric; // Topology reflected under /sys/bus/genz/genzXX/fabric 34 | }; 35 | #define to_genz_bus(pDeV) container_of(pDeV, struct genz_bus_instance, bus_dev); 36 | 37 | struct genz_device_ops { 38 | int (*init)(struct genz_device *genz_dev); 39 | void (*uninit)(struct genz_device *genz_dev); 40 | }; 41 | 42 | //------------------------------------------------------------------------- 43 | // genz_bus.c 44 | 45 | struct device *genz_find_bus_by_PCIslotnum(int); 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /subsystem/genz_class.c: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018-2019 Hewlett Packard Enterprise Development LP. 3 | * All rights reserved. 4 | * 5 | * This source code file is part of the EmerGen-Z project. 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, version 2 of the License, or 10 | * (at your option) any later version. 11 | 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "genz_class.h" 26 | #include "genz_subsystem.h" 27 | 28 | //------------------------------------------------------------------------- 29 | // Component Class Encodings are the array index. 30 | // Some names are tweaked to facilitate alphabetical ordering. 31 | 32 | static struct class genz_classes[] = { 33 | { .name = "genz_fabric" }, // Reparented by... 34 | { .name = "genz_memory_p2p" }, // 0x1 35 | { .name = "genz_memory_explicit" }, 36 | { .name = "genz_switch_integrated" }, 37 | { .name = "genz_switch_enclosure" }, 38 | { .name = "genz_switch_fabric" }, // 0x5 39 | { .name = "genz_processor" }, 40 | { .name = "genz_processor_nb" }, 41 | { .name = "genz_accelerator_nb_nc" }, 42 | { .name = "genz_accelerator_nb" }, 43 | { .name = "genz_accelerator_nc" }, // 0xA 44 | { .name = "genz_accelerator" }, 45 | { .name = "genz_io_nb_nc" }, 46 | { .name = "genz_io_nb" }, 47 | { .name = "genz_io_nc" }, 48 | { .name = "genz_io" }, // 0xF 49 | { .name = "genz_block" }, // 0x10 50 | { .name = "genz_block_nb" }, 51 | { .name = "genz_tr" }, 52 | { .name = "genz_multiclass" }, 53 | { .name = "genz_bridge_discrete" }, 54 | { .name = "genz_bridge_integrated" }, // 0x15 55 | { .name = "genz_test_board" }, 56 | { .name = "genz_lph" }, 57 | {} // NULL == EOL sentinel 58 | }; 59 | 60 | //------------------------------------------------------------------------- 61 | // It's actually all pointers so the math is good. 62 | 63 | static unsigned maxindex = (sizeof(genz_classes)/sizeof(genz_classes[0])) - 2; 64 | 65 | struct class *genz_class_getter(unsigned index) 66 | { 67 | return (index && index <= maxindex) ? &genz_classes[index] : NULL; 68 | } 69 | EXPORT_SYMBOL(genz_class_getter); 70 | 71 | //------------------------------------------------------------------------- 72 | // 0 or error 73 | 74 | int genz_classes_init() 75 | { 76 | int i, ret = 0; 77 | 78 | pr_info("%s() max index = 0x%x\n", __FUNCTION__, maxindex); 79 | 80 | // class_register() defaults to a kobj of "sysfs_dev_char_kobj". It's 81 | // possible to set kobj to something else first. Or use create_class() 82 | // which does kzalloc behind the scenes along with class_register. 83 | // Thus things that piggyback off cls->kobj go under dev, see 84 | // devices_init() in bootlin. No .release is needed cuz there's 85 | // nothing to stop() or kfree(). 86 | 87 | // for (i = 0; genz_classes[i].name; i++) { 88 | for (i = 0; !i; i++) { // Just the one for now... 89 | struct class *this; 90 | 91 | this = &genz_classes[i]; 92 | this->owner = THIS_MODULE; 93 | if ((ret = class_register(this))) { 94 | PR_ERR("class_register(%s) failed\n", this->name); 95 | break; 96 | } 97 | // Reparent all "real" classes to index 0. 98 | // 1. Take it out of current kset (static global class_kset) 99 | // 2. Repoint kset to what I want (see kobject_add) 100 | // 3. kobject_add() 101 | if (i & 0) { 102 | pr_info("Reparenting %s\n", this->name); 103 | kobject_del(this->dev_kobj); 104 | // this->dev_kobj->kset = genz_classes[0].dev_kobj->kset; 105 | // ret = kobject_add(this->dev_kobj, NULL, "%s", this->name); 106 | if (ret) { 107 | PR_ERR("reparent(%s) failed\n", this->name); 108 | break; 109 | } 110 | } 111 | } 112 | if (ret) // remove the ones that worked 113 | while (--i >= 0) 114 | class_unregister(&genz_classes[i]); 115 | return ret; 116 | }; 117 | 118 | //------------------------------------------------------------------------- 119 | 120 | void genz_classes_destroy() 121 | { 122 | int i; 123 | 124 | pr_info("%s()\n", __FUNCTION__); 125 | // for (i = 0; genz_classes[i].name; i++) { 126 | for (i = 0; !i; i++) // Just the one for now... 127 | class_unregister(&genz_classes[i]); 128 | } 129 | -------------------------------------------------------------------------------- /subsystem/genz_class.h: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018-2019 Hewlett Packard Enterprise Development LP. 3 | * All rights reserved. 4 | * 5 | * This source code file is part of the EmerGen-Z project. 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, version 2 of the License, or 10 | * (at your option) any later version. 11 | 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #ifndef GENZ_CLASS_DOT_H 22 | #define GENZ_CLASS_DOT_H 23 | 24 | /** 25 | * Gen-Z 1.0 Appendix C, Component Class Encodings. 26 | * NB == Non-Bootable 27 | * NC == Non-Coherent 28 | */ 29 | 30 | enum genz_component_class_encodings { 31 | GENZ_CCE_RESERVED_SHALL_NOT_BE_USED = 0x0, 32 | GENZ_CCE_MEMORY_P2P_CORE, 33 | GENZ_CCE_MEMORY_EXPLICIT_OPCLASS, 34 | GENZ_CCE_INTEGRATED_SWITCH, 35 | GENZ_CCE_ENC_EXP_SWITCH, 36 | GENZ_CCE_FABRIC_SWITCH, 37 | GENZ_CCE_PROCESSOR, 38 | GENZ_CCE_PROCESSOR_NB, 39 | GENZ_CCE_ACCELERATOR_NB_NC = 0x8, 40 | GENZ_CCE_ACCELERATOR_NB, 41 | GENZ_CCE_ACCELERATOR_NC, 42 | GENZ_CCE_ACCELERATOR, 43 | GENZ_CCE_IO_NB_NC, 44 | GENZ_CCE_IO_NB, 45 | GENZ_CCE_IO_NC, 46 | GENZ_CCE_IO, 47 | GENZ_CCE_BLOCK_STORAGE = 0x10, 48 | GENZ_CCE_BLOCK_STORAGE_NB, 49 | GENZ_CCE_TRANSPARENT_ROUTER, 50 | GENZ_CCE_MULTI_CLASS, 51 | GENZ_CCE_DISCRETE_BRIDGE, 52 | GENZ_CCE_INTEGRATED_BRIDGE, 53 | GENZ_CCE_COMPLIANCE_TEST_BOARD, 54 | GENZ_CCE_LPH = 0x17, 55 | GENZ_CCE_TOO_BIG, 56 | }; 57 | 58 | int genz_classes_init(void); 59 | 60 | void genz_classes_destroy(void); 61 | 62 | struct class *genz_class_getter(unsigned); 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /subsystem/genz_control.h: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018-2019 Hewlett Packard Enterprise Development LP. 3 | * All rights reserved. 4 | * 5 | * This source code file is part of the EmerGen-Z project. 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, version 2 of the License, or 10 | * (at your option) any later version. 11 | 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | 22 | /* The Control Space structures. */ 23 | 24 | #ifndef GENZ_CONTROL_DOT_H 25 | #define GENZ_CONTROL_DOT_H 26 | 27 | #include 28 | 29 | // Minimum proscribed data structures are listed in 30 | // Gen-Z 1.0 "8.13.1 Grouping: Baseline Structures" and 31 | // Gen-Z 1.0 "8.13.2 Grouping: Routing/Fabric Structures" 32 | // Definitions below ending in "_structure" are merely pertinent fields. 33 | // Those ending in "_format" are the packed binary layout. 34 | 35 | // Gen-Z 1.0 "8.14 Core Structure" 36 | 37 | enum genz_core_structure_optional_substructures { 38 | GENZ_CORE_STRUCTURE_ALLOC_COMP_DEST_TABLE = 1 << 0, 39 | GENZ_CORE_STRUCTURE_ALLOC_XYZZY_TABLE = 1 << 1, 40 | GENZ_CORE_STRUCTURE_ALLOC_ALL = (1 << 2) - 1 41 | }; 42 | 43 | struct genz_core_structure { 44 | unsigned CCE; 45 | char Base_C_Class_str[32]; 46 | uint32_t MaxInterface, MaxData, MaxCTL; 47 | int32_t CID0, SID0, // 0 if unassigned, -1 if unused 48 | PMCID, // If I am the primary manager 49 | PFMCID, PFMSID, // If someone else is the fabric manager 50 | SFMCID, SFMSID; 51 | struct genz_component_destination_table_structure *comp_dest_table; 52 | }; 53 | 54 | // Gen-Z 1.0 "8.15 Opcode Set Structure" 55 | struct genz_opcode_set_structure { 56 | int HiMom; 57 | }; 58 | 59 | // Gen-Z 1.0 "8.16 Interface Structure" 60 | struct genz_interface_structure { 61 | uint32_t Version, InterfaceID, 62 | HVS, HVE, 63 | I_Status, 64 | PeerIntefaceID, 65 | PeerBaseC_Class, 66 | PeerCID, PeerSID, 67 | PeerState; 68 | }; 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /subsystem/genz_device.c: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018-2019 Hewlett Packard Enterprise Development LP. 3 | * All rights reserved. 4 | * 5 | * This source code file is part of the FAME-Z project. 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, version 2 of the License, or 10 | * (at your option) any later version. 11 | 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "genz_bus.h" 29 | #include "genz_class.h" 30 | #include "genz_control.h" 31 | #include "genz_device.h" 32 | #include "genz_routing_fabric.h" 33 | #include "genz_subsystem.h" 34 | 35 | /* 36 | * MINORBITS is 20, which is 1M components, which is cool, but it's 16k longs 37 | * in the bitmap, or 128k, which seems like uncool overkill. 38 | */ 39 | 40 | #define GENZ_MINORBITS 14 /* 16k components per class */ 41 | #define MAXMINORS (1 << GENZ_MINORBITS) /* 2k space per bitmap */ 42 | 43 | const char * const genz_component_class_str[] = { 44 | "BAD HACKER. BAD!", 45 | "MemoryP2PCore", 46 | "MemoryExplicitOpClass", 47 | "IntegratedSwitch", 48 | "EnclosureExpansionSwitch", 49 | "FabricSwitch", 50 | "Processor", 51 | "Processor_NB", 52 | "Accelerator_NB_NC", 53 | "Accelerator_NB", 54 | "Accelerator_NC", 55 | "Accelerator", 56 | "IO_NB_NC", 57 | "IO_NB", 58 | "IO_NC", 59 | "IO", 60 | "BlockStorage", 61 | "BlockStorage_NB", 62 | "TransparentRouter", 63 | "MultiClass", 64 | "DiscreteBridge", 65 | "IntegratedBridge", 66 | "ComplianceTestBoard", // 1.0a 67 | "LPH", 68 | NULL 69 | }; 70 | 71 | static DEFINE_MUTEX(bridge_mutex); 72 | static DECLARE_BITMAP(bridge_minor_bitmap, MAXMINORS) = { 0 }; 73 | static LIST_HEAD(bridge_list); 74 | 75 | static uint64_t bridge_major = 0; /* Until first allocation */ 76 | 77 | /** 78 | * genz_core_structure_create - allocate and populate a Gen-Z Core Structure 79 | * @alloc: a bitfield directing which sub-structures to allocate. 80 | * 81 | * Create a semantically-complete Core Structure (not binary field-precise). 82 | */ 83 | 84 | struct genz_core_structure *genz_core_structure_create(unsigned CCE) 85 | { 86 | uint64_t alloc = 0; 87 | struct genz_core_structure *core; 88 | 89 | switch (CCE) { 90 | case GENZ_CCE_DISCRETE_BRIDGE: 91 | case GENZ_CCE_INTEGRATED_BRIDGE: 92 | alloc = GENZ_CORE_STRUCTURE_ALLOC_COMP_DEST_TABLE; 93 | break; 94 | default: 95 | return ERR_PTR(-EINVAL); 96 | } 97 | if (!(core = kzalloc(sizeof(*core), GFP_KERNEL))) 98 | return ERR_PTR(-ENOMEM); 99 | core->CCE = CCE; 100 | 101 | if ((alloc & GENZ_CORE_STRUCTURE_ALLOC_COMP_DEST_TABLE) && 102 | !(core->comp_dest_table = 103 | kzalloc(sizeof(*core->comp_dest_table), GFP_KERNEL))) { 104 | genz_core_structure_destroy(core); 105 | return ERR_PTR(-ENOMEM); 106 | } 107 | return core; 108 | } 109 | EXPORT_SYMBOL(genz_core_structure_create); 110 | 111 | void genz_core_structure_destroy(struct genz_core_structure *core) 112 | { 113 | if (!core) 114 | return; 115 | if (core->comp_dest_table) { 116 | kfree(core->comp_dest_table); 117 | core->comp_dest_table = NULL; 118 | } 119 | kfree(core); 120 | } 121 | EXPORT_SYMBOL(genz_core_structure_destroy); 122 | 123 | static ssize_t chrdev_bin_read( 124 | struct file *file, struct kobject *kobj, struct bin_attribute *bin_attr, 125 | char *buf, loff_t offset, size_t size) 126 | { 127 | pr_info("%s(%s::%s, %lu bytes @ %lld)\n", 128 | __FUNCTION__, kobj->name, bin_attr->attr.name, size, offset); 129 | memset(buf, 0, size); 130 | snprintf(buf, size - 1, "You're in a maze of twisty little passages, all alike.\n"); 131 | return size; 132 | } 133 | 134 | static ssize_t chrdev_bin_write( 135 | struct file *file, struct kobject *kobj, struct bin_attribute *bin_attr, 136 | char *buf, loff_t offset, size_t size) 137 | { 138 | pr_info("%s(%s::%s, %lu bytes @ %lld)\n", 139 | __FUNCTION__, kobj->name, bin_attr->attr.name, size, offset); 140 | buf[size - 1] = '\0'; 141 | if (size < 128) 142 | pr_cont(" = %s", buf); 143 | pr_cont("\n"); 144 | return size; 145 | } 146 | 147 | /** 148 | * genz_register_char_device - add a new character device and driver 149 | * @core: Core structure with CCE set appropriately 150 | * @fops: driver set for the device 151 | * @file_private_data: to be attached as file->private_data in all fops 152 | * @bin_attr: optional private routines/data for setting up sysfs binary files 153 | * @PCIslotnum: just what it says FIXME use dev_name() from caller 154 | * Based on misc_register(). Returns pointer to new structure on success 155 | * or ERR_PTR(-ESOMETHING). 156 | */ 157 | 158 | struct genz_char_device *genz_register_char_device( 159 | const struct genz_core_structure *core, 160 | const struct file_operations *fops, 161 | void *file_private_data, 162 | const struct bin_attribute *attr_custom, 163 | int PCIslotnum) 164 | { 165 | struct bin_attribute attr_final; 166 | int i, ret = 0; 167 | char *ownername = NULL; 168 | struct genz_char_device *genz_chrdev = NULL; 169 | dev_t base_dev_t = 0; 170 | uint64_t minor; 171 | struct mutex *themutex; 172 | unsigned long *thebitmap; // See types.h DECLARE_BITMAP 173 | uint64_t *themajor; 174 | 175 | switch (core->CCE) { 176 | case GENZ_CCE_DISCRETE_BRIDGE: 177 | case GENZ_CCE_INTEGRATED_BRIDGE: 178 | themutex = &bridge_mutex; 179 | thebitmap = bridge_minor_bitmap; // It's an array 180 | themajor = &bridge_major; 181 | break; 182 | default: 183 | PR_ERR("unhandled Component Encoding %d\n", core->CCE); 184 | return ERR_PTR(-EDOM); 185 | } 186 | ownername = fops->owner->name; 187 | 188 | // Idiot checking. Some day check offset limits, etc. 189 | if (!core->MaxInterface || core->MaxInterface > 1024) { 190 | PR_ERR("core->MaxInterface=%d is out of range\n", 191 | core->MaxInterface); 192 | return ERR_PTR(-EINVAL); 193 | } 194 | if (core->MaxCTL < 8192) { 195 | PR_ERR("core->MaxCTL=%d is too small\n", 196 | core->MaxCTL); 197 | return ERR_PTR(-EINVAL); 198 | } 199 | 200 | // Memory allocation before the mutex lock (easier cleanup). 201 | 202 | if (!(genz_chrdev = kzalloc(sizeof(*genz_chrdev), GFP_KERNEL))) 203 | return ERR_PTR(-ENOMEM); 204 | 205 | if (!(genz_chrdev->iface_attrs = kzalloc( 206 | sizeof(struct bin_attribute) * core->MaxInterface, 207 | GFP_KERNEL))) 208 | return ERR_PTR(-ENOMEM); 209 | 210 | // Do this math once. 211 | sysfs_bin_attr_init(&attr_final); 212 | attr_final.private = attr_custom->private ? 213 | attr_custom->private : genz_chrdev; 214 | attr_final.read = attr_custom->read ? 215 | attr_custom->read : chrdev_bin_read; 216 | attr_final.write = attr_custom->write ? 217 | attr_custom->write : chrdev_bin_write; 218 | attr_final.mmap = attr_custom->mmap ? attr_custom->mmap : NULL; 219 | 220 | for (i = 0; i < core->MaxInterface; i++) { 221 | struct bin_attribute *this; 222 | char *name; 223 | 224 | this = &(genz_chrdev->iface_attrs[i]); 225 | sysfs_bin_attr_init(this); 226 | if (!(name = kzalloc(8, GFP_KERNEL))) { 227 | PR_ERR("Cannot allocate space for iface %d\n", i); 228 | while (--i >= 0) 229 | kfree(genz_chrdev->iface_attrs[i].attr.name); 230 | return ERR_PTR(-ENOMEM); 231 | } 232 | sprintf(name, "%04d", i); 233 | this->attr.name = name; 234 | this->attr.mode = S_IRUSR | S_IWUSR; 235 | this->size = 4096; 236 | this->private = attr_final.private; 237 | this->read = attr_final.read; 238 | this->write = attr_final.write; 239 | this->mmap = attr_final.mmap; 240 | } 241 | 242 | mutex_lock(themutex); 243 | minor = find_first_zero_bit(thebitmap, GENZ_MINORBITS); 244 | if (minor >= GENZ_MINORBITS) { 245 | PR_ERR("exhausted minor numbers for major %llu (%s)\n", 246 | *themajor, ownername); 247 | ret = -EDOM; 248 | goto up_and_out; 249 | } 250 | if (*themajor) { 251 | base_dev_t = MKDEV(*themajor, minor); 252 | ret = register_chrdev_region(base_dev_t, 1, ownername); 253 | } else { 254 | if (!(ret = alloc_chrdev_region(&base_dev_t, minor, 1, ownername))) 255 | *themajor = MAJOR(base_dev_t); 256 | } 257 | if (ret) { 258 | PR_ERR("can't allocate chrdev_region: %d\n", ret); 259 | goto up_and_out; 260 | } 261 | set_bit(minor, thebitmap); 262 | pr_info("%s(%s) dev_t = %llu:%llu\n", __FUNCTION__, ownername, 263 | *themajor, minor); 264 | 265 | if (!(genz_chrdev->parent = genz_find_bus_by_PCIslotnum(PCIslotnum))) { 266 | ret = -ENODEV; 267 | goto up_and_out; 268 | } 269 | genz_chrdev->core = core; 270 | genz_chrdev->file_private_data = file_private_data; 271 | genz_chrdev->genz_class = genz_class_getter(core->CCE); 272 | genz_chrdev->cclass = genz_component_class_str[core->CCE]; 273 | genz_chrdev->mode = 0666; 274 | // FIXME: managed in here (0, 1, 2, ...) when PCIslotnum becomes 275 | // generic dev_name. 276 | genz_chrdev->instance = PCIslotnum; 277 | 278 | // This sets .fops, .list, and .kobj == ktype_cdev_default. 279 | // Then add anything else. 280 | cdev_init(&genz_chrdev->cdev, fops); 281 | genz_chrdev->cdev.dev = MKDEV(*themajor, minor); 282 | genz_chrdev->cdev.count = 1; 283 | if ((ret = kobject_set_name(&genz_chrdev->cdev.kobj, 284 | "%s_%02x", ownername, genz_chrdev->instance))) { 285 | PR_ERR("kobject_set_name(%s) failed\n", ownername); 286 | goto up_and_out; 287 | } 288 | if ((ret = cdev_add(&genz_chrdev->cdev, 289 | genz_chrdev->cdev.dev, 290 | genz_chrdev->cdev.count))) { 291 | PR_ERR("cdev_add() failed\n"); 292 | goto up_and_out; 293 | } 294 | 295 | // Driver becomes "live" on success so insure data is ready. 296 | genz_chrdev->this_device = device_create( 297 | genz_chrdev->genz_class, 298 | genz_chrdev->parent, // ugly croakage if this is NULL 299 | genz_chrdev->cdev.dev, 300 | genz_chrdev, // drvdata: not sure where this goes 301 | "%s_%02x", 302 | ownername, genz_chrdev->instance); 303 | if (IS_ERR(genz_chrdev->this_device)) { 304 | ret = PTR_ERR(genz_chrdev->this_device); 305 | PR_ERR("device_create_with_groups() failed\n"); 306 | goto up_and_out; 307 | } 308 | 309 | // Section 8.14 310 | sysfs_bin_attr_init(&(genz_chrdev->sysCoreStructure)); 311 | genz_chrdev->sysCoreStructure.attr.name = "core"; 312 | genz_chrdev->sysCoreStructure.attr.mode = S_IRUSR | S_IWUSR; 313 | genz_chrdev->sysCoreStructure.size = 4096; // really 512 314 | genz_chrdev->sysCoreStructure.private = attr_final.private; 315 | genz_chrdev->sysCoreStructure.read = attr_final.read; 316 | genz_chrdev->sysCoreStructure.write = attr_final.write; 317 | genz_chrdev->sysCoreStructure.mmap = attr_final.mmap; 318 | 319 | if ((ret = device_create_bin_file( 320 | genz_chrdev->this_device, 321 | &genz_chrdev->sysCoreStructure))) { 322 | PR_ERR("couldn't create sys core structure file: %d\n", ret); 323 | goto up_and_out; 324 | } 325 | 326 | if (!(genz_chrdev->sysInterfaces = kobject_create_and_add( 327 | "interfaces", &genz_chrdev->this_device->kobj))) { 328 | PR_ERR("couldn't create interfaces directory\n"); 329 | ret = -EBADF; 330 | goto up_and_out; 331 | } 332 | 333 | for (i = 0; i < core->MaxInterface; i++) 334 | if ((ret = sysfs_create_bin_file( 335 | genz_chrdev->sysInterfaces, 336 | &(genz_chrdev->iface_attrs[i])))) { 337 | PR_ERR("couldn't create interface %d: %d\n", i, ret); 338 | goto up_and_out; 339 | } 340 | 341 | up_and_out: 342 | mutex_unlock(themutex); 343 | if (ret) { 344 | genz_unregister_char_device(genz_chrdev); 345 | return ERR_PTR(ret); 346 | } 347 | return genz_chrdev; 348 | } 349 | EXPORT_SYMBOL(genz_register_char_device); 350 | 351 | void genz_unregister_char_device(struct genz_char_device *genz_chrdev) 352 | { 353 | // FIXME: review for memory leaks 354 | if (!genz_chrdev) 355 | return; 356 | if (genz_chrdev->sysInterfaces) { 357 | int i; 358 | 359 | for (i = 0; i < genz_chrdev->core->MaxInterface; i++) { 360 | struct bin_attribute *this; 361 | 362 | this = &(genz_chrdev->iface_attrs[i]); 363 | sysfs_remove_bin_file(genz_chrdev->sysInterfaces, this); 364 | kfree(this->attr.name); 365 | } 366 | kfree(genz_chrdev->iface_attrs); 367 | genz_chrdev->iface_attrs = NULL; 368 | kobject_del(genz_chrdev->sysInterfaces); 369 | genz_chrdev->sysInterfaces = NULL; 370 | } 371 | device_remove_bin_file( 372 | genz_chrdev->this_device, 373 | &genz_chrdev->sysCoreStructure); 374 | memset(&genz_chrdev->sysCoreStructure, 0, sizeof(struct bin_attribute)); 375 | device_destroy(genz_chrdev->genz_class, genz_chrdev->cdev.dev); 376 | kfree(genz_chrdev); 377 | } 378 | EXPORT_SYMBOL(genz_unregister_char_device); 379 | -------------------------------------------------------------------------------- /subsystem/genz_device.h: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018-2019 Hewlett Packard Enterprise Development LP. 3 | * All rights reserved. 4 | * 5 | * This source code file is part of the FAME-Z project. 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, version 2 of the License, or 10 | * (at your option) any later version. 11 | 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | #ifndef GENZ_DEVICE_DOT_H 22 | #define GENZ_DEVICE_DOT_H 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include "genz_subsystem.h" 29 | 30 | #define GZNAMFMTSIZ 64 31 | 32 | struct genz_device { 33 | char namefmt[GZNAMFMTSIZ]; 34 | struct list_head lister; 35 | uint64_t flags; 36 | struct device dev; 37 | void *private_data; 38 | }; 39 | #define to_genz_dev(pDeV) container_of(pDeV, struct genz_device, dev) 40 | 41 | // Composition pattern to realize all data needed to represent a device. 42 | // "misc" class devices get it all clearly spelled out in struct miscdevice. 43 | // and it's all populated by misc_register() in the core. cdev is kept 44 | // as a full structure; it can be be pulled from the filp->f_inode->i_cdev 45 | // and used as anchor in to_xxxx lookups. 46 | 47 | struct genz_char_device { 48 | struct genz_core_structure *core; 49 | int instance; // Currently external control 50 | const char *cclass; // genz_component_class_str[CCE] 51 | void *file_private_data; // Extracted at first fops->open() 52 | struct class *genz_class; // Multi-purpose struct 53 | struct cdev cdev; // full structure, has 54 | // kobject 55 | // owner 56 | // ops (fops) 57 | // list_head 58 | // dev_t (base maj/min) 59 | // count (of minors) 60 | 61 | // Copied from miscdevice, in active use 62 | struct device *parent; // set by caller, now to figure out WTF? 63 | struct device *this_device; // created on the fly. 64 | 65 | // Copied from miscdevice, not used yet 66 | umode_t mode; 67 | const struct attribute_group **attr_groups; // Null-term array 68 | const char *name; // used in device_create[_with_groups] 69 | const char *nodename; // used in misc_class->devnode() 70 | // callback to name... 71 | // NOT copied from miscdevice 72 | // minor, because cdev has a dev_t 73 | // list_head, because cdev has one 74 | 75 | // Additional items under /sys/devices/.../one_device 76 | struct bin_attribute sysCoreStructure; // file 77 | struct kobject *sysInterfaces; // directory 78 | struct bin_attribute *iface_attrs; // file for each interface 79 | }; 80 | 81 | static inline void *genz_char_drv_1stopen_private_data(struct file *file) 82 | { 83 | struct genz_char_device *container = container_of( 84 | file->f_inode->i_cdev, // member address 85 | struct genz_char_device, // container type 86 | cdev); // container member 87 | return container->file_private_data; 88 | } 89 | 90 | extern const char * const genz_component_class_str[]; 91 | 92 | // EXPORTed 93 | 94 | extern struct genz_core_structure *genz_core_structure_create(unsigned); 95 | extern void genz_core_structure_destroy(struct genz_core_structure *); 96 | 97 | extern struct genz_char_device *genz_register_char_device( 98 | const struct genz_core_structure *, 99 | const struct file_operations *, 100 | void *file_private_data, 101 | const struct bin_attribute *, 102 | int instance); 103 | 104 | extern void genz_unregister_char_device(struct genz_char_device *); 105 | #endif 106 | -------------------------------------------------------------------------------- /subsystem/genz_routing_fabric.h: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018-2019 Hewlett Packard Enterprise Development LP. 3 | * All rights reserved. 4 | * 5 | * This source code file is part of the FAME-Z project. 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, version 2 of the License, or 10 | * (at your option) any later version. 11 | 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | // Only the beginning 22 | 23 | #ifndef GENZ_ROUTING_FABRIC_DOT_H 24 | #define GENZ_ROUTING_FABRIC_DOT_H 25 | 26 | #include 27 | #include 28 | 29 | // Definitions below ending in "_structure" are merely pertinent fields. 30 | // Those ending in "_format" are the packed binary layout. 31 | 32 | // Gen-Z 1.0 "8.29 Component Destination Table Structure" 33 | struct genz_component_destination_table_structure { 34 | int HiMom; 35 | }; 36 | 37 | // Gen-Z 1.0 "8.29 Single-Subnet Destination Table Structure" 38 | struct genz_single_subnet_destination_table_structure { 39 | int HiMom; 40 | }; 41 | 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /subsystem/genz_subsystem.h: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018-2019 Hewlett Packard Enterprise Development LP. 3 | * All rights reserved. 4 | * 5 | * This source code file is part of the EmerGen-Z project. 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, version 2 of the License, or 10 | * (at your option) any later version. 11 | 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | /* Versioning and debug/trace support. */ 22 | 23 | #ifndef GENZ_BASELINE_DOT_H 24 | #define GENZ_BASELINE_DOT_H 25 | 26 | #include 27 | 28 | #define GENZ_DEBUG 29 | 30 | #define DRV_NAME "Gen-Z" 31 | #define DRV_VERSION "0.1" 32 | 33 | #define __unused __attribute__ ((unused)) 34 | 35 | extern int verbose; 36 | 37 | #define __PRE "genz:" 38 | 39 | #define PR_ERR(a...) { pr_err("%s(): ", __FUNCTION__); pr_cont(a); } 40 | 41 | #ifndef PR_V1 42 | #ifdef GENZ_DEBUG 43 | #define PR_V1(a...) { if (verbose) pr_info(__PRE a); } 44 | #define PR_V2(a...) { if (verbose > 1) pr_info(__PRE a); } 45 | #define PR_V3(a...) { if (verbose > 2) pr_info(__PRE a); } 46 | // #undef __PRE 47 | #else 48 | #define PR_V1(a...) 49 | #define PR_V2(a...) 50 | #define PR_V3(a...) 51 | #endif 52 | #endif 53 | 54 | #endif 55 | --------------------------------------------------------------------------------