1923a932cSJoe Stringer#!/usr/bin/env python3 2923a932cSJoe Stringer# SPDX-License-Identifier: GPL-2.0-only 3923a932cSJoe Stringer# 4923a932cSJoe Stringer# Copyright (C) 2018-2019 Netronome Systems, Inc. 5923a932cSJoe Stringer# Copyright (C) 2021 Isovalent, Inc. 6923a932cSJoe Stringer 7923a932cSJoe Stringer# In case user attempts to run with Python 2. 8923a932cSJoe Stringerfrom __future__ import print_function 9923a932cSJoe Stringer 10923a932cSJoe Stringerimport argparse 11923a932cSJoe Stringerimport re 12923a932cSJoe Stringerimport sys, os 13fd0a38f9SQuentin Monnetimport subprocess 14fd0a38f9SQuentin Monnet 1592ec1cc3SQuentin MonnethelpersDocStart = 'Start of BPF helper function descriptions:' 16923a932cSJoe Stringer 17923a932cSJoe Stringerclass NoHelperFound(BaseException): 18923a932cSJoe Stringer pass 19923a932cSJoe Stringer 20a67882a2SJoe Stringerclass NoSyscallCommandFound(BaseException): 21a67882a2SJoe Stringer pass 22a67882a2SJoe Stringer 23923a932cSJoe Stringerclass ParsingError(BaseException): 24923a932cSJoe Stringer def __init__(self, line='<line not provided>', reader=None): 25923a932cSJoe Stringer if reader: 26923a932cSJoe Stringer BaseException.__init__(self, 27923a932cSJoe Stringer 'Error at file offset %d, parsing line: %s' % 28923a932cSJoe Stringer (reader.tell(), line)) 29923a932cSJoe Stringer else: 30923a932cSJoe Stringer BaseException.__init__(self, 'Error parsing line: %s' % line) 31923a932cSJoe Stringer 32a67882a2SJoe Stringer 33a67882a2SJoe Stringerclass APIElement(object): 34923a932cSJoe Stringer """ 35a67882a2SJoe Stringer An object representing the description of an aspect of the eBPF API. 36a67882a2SJoe Stringer @proto: prototype of the API symbol 37a67882a2SJoe Stringer @desc: textual description of the symbol 38a67882a2SJoe Stringer @ret: (optional) description of any associated return value 39923a932cSJoe Stringer """ 40*48b13cabSEduard Zingerman def __init__(self, proto='', desc='', ret='', attrs=[]): 41923a932cSJoe Stringer self.proto = proto 42923a932cSJoe Stringer self.desc = desc 43923a932cSJoe Stringer self.ret = ret 44*48b13cabSEduard Zingerman self.attrs = attrs 45923a932cSJoe Stringer 46a67882a2SJoe Stringer 47a67882a2SJoe Stringerclass Helper(APIElement): 48a67882a2SJoe Stringer """ 49a67882a2SJoe Stringer An object representing the description of an eBPF helper function. 50a67882a2SJoe Stringer @proto: function prototype of the helper function 51a67882a2SJoe Stringer @desc: textual description of the helper function 52a67882a2SJoe Stringer @ret: description of the return value of the helper function 53a67882a2SJoe Stringer """ 540a0d55efSEyal Birger def __init__(self, *args, **kwargs): 550a0d55efSEyal Birger super().__init__(*args, **kwargs) 560a0d55efSEyal Birger self.enum_val = None 570a0d55efSEyal Birger 58923a932cSJoe Stringer def proto_break_down(self): 59923a932cSJoe Stringer """ 60923a932cSJoe Stringer Break down helper function protocol into smaller chunks: return type, 61923a932cSJoe Stringer name, distincts arguments. 62923a932cSJoe Stringer """ 63121fd33bSVishal Chourasia arg_re = re.compile(r'((\w+ )*?(\w+|...))( (\**)(\w+))?$') 64923a932cSJoe Stringer res = {} 65121fd33bSVishal Chourasia proto_re = re.compile(r'(.+) (\**)(\w+)\(((([^,]+)(, )?){1,5})\)$') 66923a932cSJoe Stringer 67923a932cSJoe Stringer capture = proto_re.match(self.proto) 68923a932cSJoe Stringer res['ret_type'] = capture.group(1) 69923a932cSJoe Stringer res['ret_star'] = capture.group(2) 70923a932cSJoe Stringer res['name'] = capture.group(3) 71923a932cSJoe Stringer res['args'] = [] 72923a932cSJoe Stringer 73923a932cSJoe Stringer args = capture.group(4).split(', ') 74923a932cSJoe Stringer for a in args: 75923a932cSJoe Stringer capture = arg_re.match(a) 76923a932cSJoe Stringer res['args'].append({ 77923a932cSJoe Stringer 'type' : capture.group(1), 78923a932cSJoe Stringer 'star' : capture.group(5), 79923a932cSJoe Stringer 'name' : capture.group(6) 80923a932cSJoe Stringer }) 81923a932cSJoe Stringer 82923a932cSJoe Stringer return res 83923a932cSJoe Stringer 84a67882a2SJoe Stringer 85*48b13cabSEduard ZingermanATTRS = { 86*48b13cabSEduard Zingerman '__bpf_fastcall': 'bpf_fastcall' 87*48b13cabSEduard Zingerman} 88*48b13cabSEduard Zingerman 89*48b13cabSEduard Zingerman 90923a932cSJoe Stringerclass HeaderParser(object): 91923a932cSJoe Stringer """ 92923a932cSJoe Stringer An object used to parse a file in order to extract the documentation of a 93923a932cSJoe Stringer list of eBPF helper functions. All the helpers that can be retrieved are 94923a932cSJoe Stringer stored as Helper object, in the self.helpers() array. 95923a932cSJoe Stringer @filename: name of file to parse, usually include/uapi/linux/bpf.h in the 96923a932cSJoe Stringer kernel tree 97923a932cSJoe Stringer """ 98923a932cSJoe Stringer def __init__(self, filename): 99923a932cSJoe Stringer self.reader = open(filename, 'r') 100923a932cSJoe Stringer self.line = '' 101923a932cSJoe Stringer self.helpers = [] 102a67882a2SJoe Stringer self.commands = [] 10371a3cdf8SUsama Arif self.desc_unique_helpers = set() 10471a3cdf8SUsama Arif self.define_unique_helpers = [] 1050a0d55efSEyal Birger self.helper_enum_vals = {} 106ce3e44a0SAndrii Nakryiko self.helper_enum_pos = {} 1070ba3929eSUsama Arif self.desc_syscalls = [] 1080ba3929eSUsama Arif self.enum_syscalls = [] 109a67882a2SJoe Stringer 110a67882a2SJoe Stringer def parse_element(self): 111a67882a2SJoe Stringer proto = self.parse_symbol() 112f1f3f67fSUsama Arif desc = self.parse_desc(proto) 113f1f3f67fSUsama Arif ret = self.parse_ret(proto) 114a67882a2SJoe Stringer return APIElement(proto=proto, desc=desc, ret=ret) 115923a932cSJoe Stringer 116923a932cSJoe Stringer def parse_helper(self): 117923a932cSJoe Stringer proto = self.parse_proto() 118f1f3f67fSUsama Arif desc = self.parse_desc(proto) 119f1f3f67fSUsama Arif ret = self.parse_ret(proto) 120*48b13cabSEduard Zingerman attrs = self.parse_attrs(proto) 121*48b13cabSEduard Zingerman return Helper(proto=proto, desc=desc, ret=ret, attrs=attrs) 122923a932cSJoe Stringer 123a67882a2SJoe Stringer def parse_symbol(self): 124121fd33bSVishal Chourasia p = re.compile(r' \* ?(BPF\w+)$') 125a67882a2SJoe Stringer capture = p.match(self.line) 126a67882a2SJoe Stringer if not capture: 127a67882a2SJoe Stringer raise NoSyscallCommandFound 128121fd33bSVishal Chourasia end_re = re.compile(r' \* ?NOTES$') 129a67882a2SJoe Stringer end = end_re.match(self.line) 130a67882a2SJoe Stringer if end: 131a67882a2SJoe Stringer raise NoSyscallCommandFound 132a67882a2SJoe Stringer self.line = self.reader.readline() 133a67882a2SJoe Stringer return capture.group(1) 134a67882a2SJoe Stringer 135923a932cSJoe Stringer def parse_proto(self): 136923a932cSJoe Stringer # Argument can be of shape: 137923a932cSJoe Stringer # - "void" 138923a932cSJoe Stringer # - "type name" 139923a932cSJoe Stringer # - "type *name" 140923a932cSJoe Stringer # - Same as above, with "const" and/or "struct" in front of type 141923a932cSJoe Stringer # - "..." (undefined number of arguments, for bpf_trace_printk()) 142923a932cSJoe Stringer # There is at least one term ("void"), and at most five arguments. 143121fd33bSVishal Chourasia p = re.compile(r' \* ?((.+) \**\w+\((((const )?(struct )?(\w+|\.\.\.)( \**\w+)?)(, )?){1,5}\))$') 144923a932cSJoe Stringer capture = p.match(self.line) 145923a932cSJoe Stringer if not capture: 146923a932cSJoe Stringer raise NoHelperFound 147923a932cSJoe Stringer self.line = self.reader.readline() 148923a932cSJoe Stringer return capture.group(1) 149923a932cSJoe Stringer 150f1f3f67fSUsama Arif def parse_desc(self, proto): 151121fd33bSVishal Chourasia p = re.compile(r' \* ?(?:\t| {5,8})Description$') 152923a932cSJoe Stringer capture = p.match(self.line) 153923a932cSJoe Stringer if not capture: 154f1f3f67fSUsama Arif raise Exception("No description section found for " + proto) 155923a932cSJoe Stringer # Description can be several lines, some of them possibly empty, and it 156923a932cSJoe Stringer # stops when another subsection title is met. 157923a932cSJoe Stringer desc = '' 158f1f3f67fSUsama Arif desc_present = False 159923a932cSJoe Stringer while True: 160923a932cSJoe Stringer self.line = self.reader.readline() 161923a932cSJoe Stringer if self.line == ' *\n': 162923a932cSJoe Stringer desc += '\n' 163923a932cSJoe Stringer else: 164121fd33bSVishal Chourasia p = re.compile(r' \* ?(?:\t| {5,8})(?:\t| {8})(.*)') 165923a932cSJoe Stringer capture = p.match(self.line) 166923a932cSJoe Stringer if capture: 167f1f3f67fSUsama Arif desc_present = True 168923a932cSJoe Stringer desc += capture.group(1) + '\n' 169923a932cSJoe Stringer else: 170923a932cSJoe Stringer break 171f1f3f67fSUsama Arif 172f1f3f67fSUsama Arif if not desc_present: 173f1f3f67fSUsama Arif raise Exception("No description found for " + proto) 174923a932cSJoe Stringer return desc 175923a932cSJoe Stringer 176f1f3f67fSUsama Arif def parse_ret(self, proto): 177121fd33bSVishal Chourasia p = re.compile(r' \* ?(?:\t| {5,8})Return$') 178923a932cSJoe Stringer capture = p.match(self.line) 179923a932cSJoe Stringer if not capture: 180f1f3f67fSUsama Arif raise Exception("No return section found for " + proto) 181923a932cSJoe Stringer # Return value description can be several lines, some of them possibly 182923a932cSJoe Stringer # empty, and it stops when another subsection title is met. 183923a932cSJoe Stringer ret = '' 184f1f3f67fSUsama Arif ret_present = False 185923a932cSJoe Stringer while True: 186923a932cSJoe Stringer self.line = self.reader.readline() 187923a932cSJoe Stringer if self.line == ' *\n': 188923a932cSJoe Stringer ret += '\n' 189923a932cSJoe Stringer else: 190121fd33bSVishal Chourasia p = re.compile(r' \* ?(?:\t| {5,8})(?:\t| {8})(.*)') 191923a932cSJoe Stringer capture = p.match(self.line) 192923a932cSJoe Stringer if capture: 193f1f3f67fSUsama Arif ret_present = True 194923a932cSJoe Stringer ret += capture.group(1) + '\n' 195923a932cSJoe Stringer else: 196923a932cSJoe Stringer break 197f1f3f67fSUsama Arif 198f1f3f67fSUsama Arif if not ret_present: 199f1f3f67fSUsama Arif raise Exception("No return found for " + proto) 200923a932cSJoe Stringer return ret 201923a932cSJoe Stringer 202*48b13cabSEduard Zingerman def parse_attrs(self, proto): 203*48b13cabSEduard Zingerman p = re.compile(r' \* ?(?:\t| {5,8})Attributes$') 204*48b13cabSEduard Zingerman capture = p.match(self.line) 205*48b13cabSEduard Zingerman if not capture: 206*48b13cabSEduard Zingerman return [] 207*48b13cabSEduard Zingerman # Expect a single line with mnemonics for attributes separated by spaces 208*48b13cabSEduard Zingerman self.line = self.reader.readline() 209*48b13cabSEduard Zingerman p = re.compile(r' \* ?(?:\t| {5,8})(?:\t| {8})(.*)') 210*48b13cabSEduard Zingerman capture = p.match(self.line) 211*48b13cabSEduard Zingerman if not capture: 212*48b13cabSEduard Zingerman raise Exception("Incomplete 'Attributes' section for " + proto) 213*48b13cabSEduard Zingerman attrs = capture.group(1).split(' ') 214*48b13cabSEduard Zingerman for attr in attrs: 215*48b13cabSEduard Zingerman if attr not in ATTRS: 216*48b13cabSEduard Zingerman raise Exception("Unexpected attribute '" + attr + "' specified for " + proto) 217*48b13cabSEduard Zingerman self.line = self.reader.readline() 218*48b13cabSEduard Zingerman if self.line != ' *\n': 219*48b13cabSEduard Zingerman raise Exception("Expecting empty line after 'Attributes' section for " + proto) 220*48b13cabSEduard Zingerman # Prepare a line for next self.parse_* to consume 221*48b13cabSEduard Zingerman self.line = self.reader.readline() 222*48b13cabSEduard Zingerman return attrs 223*48b13cabSEduard Zingerman 2240ba3929eSUsama Arif def seek_to(self, target, help_message, discard_lines = 1): 225a67882a2SJoe Stringer self.reader.seek(0) 226a67882a2SJoe Stringer offset = self.reader.read().find(target) 227923a932cSJoe Stringer if offset == -1: 228a67882a2SJoe Stringer raise Exception(help_message) 229923a932cSJoe Stringer self.reader.seek(offset) 230923a932cSJoe Stringer self.reader.readline() 2310ba3929eSUsama Arif for _ in range(discard_lines): 232923a932cSJoe Stringer self.reader.readline() 233923a932cSJoe Stringer self.line = self.reader.readline() 234923a932cSJoe Stringer 2350ba3929eSUsama Arif def parse_desc_syscall(self): 236a67882a2SJoe Stringer self.seek_to('* DOC: eBPF Syscall Commands', 237a67882a2SJoe Stringer 'Could not find start of eBPF syscall descriptions list') 238a67882a2SJoe Stringer while True: 239a67882a2SJoe Stringer try: 240a67882a2SJoe Stringer command = self.parse_element() 241a67882a2SJoe Stringer self.commands.append(command) 2420ba3929eSUsama Arif self.desc_syscalls.append(command.proto) 2430ba3929eSUsama Arif 244a67882a2SJoe Stringer except NoSyscallCommandFound: 245a67882a2SJoe Stringer break 246a67882a2SJoe Stringer 2470ba3929eSUsama Arif def parse_enum_syscall(self): 2480ba3929eSUsama Arif self.seek_to('enum bpf_cmd {', 2490ba3929eSUsama Arif 'Could not find start of bpf_cmd enum', 0) 2500ba3929eSUsama Arif # Searches for either one or more BPF\w+ enums 251121fd33bSVishal Chourasia bpf_p = re.compile(r'\s*(BPF\w+)+') 2520ba3929eSUsama Arif # Searches for an enum entry assigned to another entry, 2530ba3929eSUsama Arif # for e.g. BPF_PROG_RUN = BPF_PROG_TEST_RUN, which is 2540ba3929eSUsama Arif # not documented hence should be skipped in check to 2550ba3929eSUsama Arif # determine if the right number of syscalls are documented 256121fd33bSVishal Chourasia assign_p = re.compile(r'\s*(BPF\w+)\s*=\s*(BPF\w+)') 2570ba3929eSUsama Arif bpf_cmd_str = '' 2580ba3929eSUsama Arif while True: 2590ba3929eSUsama Arif capture = assign_p.match(self.line) 2600ba3929eSUsama Arif if capture: 2610ba3929eSUsama Arif # Skip line if an enum entry is assigned to another entry 2620ba3929eSUsama Arif self.line = self.reader.readline() 2630ba3929eSUsama Arif continue 2640ba3929eSUsama Arif capture = bpf_p.match(self.line) 2650ba3929eSUsama Arif if capture: 2660ba3929eSUsama Arif bpf_cmd_str += self.line 2670ba3929eSUsama Arif else: 2680ba3929eSUsama Arif break 2690ba3929eSUsama Arif self.line = self.reader.readline() 2700ba3929eSUsama Arif # Find the number of occurences of BPF\w+ 271121fd33bSVishal Chourasia self.enum_syscalls = re.findall(r'(BPF\w+)+', bpf_cmd_str) 2720ba3929eSUsama Arif 27371a3cdf8SUsama Arif def parse_desc_helpers(self): 27492ec1cc3SQuentin Monnet self.seek_to(helpersDocStart, 275a67882a2SJoe Stringer 'Could not find start of eBPF helper descriptions list') 276923a932cSJoe Stringer while True: 277923a932cSJoe Stringer try: 278923a932cSJoe Stringer helper = self.parse_helper() 279923a932cSJoe Stringer self.helpers.append(helper) 28071a3cdf8SUsama Arif proto = helper.proto_break_down() 28171a3cdf8SUsama Arif self.desc_unique_helpers.add(proto['name']) 282923a932cSJoe Stringer except NoHelperFound: 283923a932cSJoe Stringer break 284923a932cSJoe Stringer 28571a3cdf8SUsama Arif def parse_define_helpers(self): 2868a76145aSAndrii Nakryiko # Parse FN(...) in #define ___BPF_FUNC_MAPPER to compare later with the 2870a0d55efSEyal Birger # number of unique function names present in description and use the 2880a0d55efSEyal Birger # correct enumeration value. 28971a3cdf8SUsama Arif # Note: seek_to(..) discards the first line below the target search text, 2908a76145aSAndrii Nakryiko # resulting in FN(unspec, 0, ##ctx) being skipped and not added to 2918a76145aSAndrii Nakryiko # self.define_unique_helpers. 2928a76145aSAndrii Nakryiko self.seek_to('#define ___BPF_FUNC_MAPPER(FN, ctx...)', 29371a3cdf8SUsama Arif 'Could not find start of eBPF helper definition list') 2940a0d55efSEyal Birger # Searches for one FN(\w+) define or a backslash for newline 295121fd33bSVishal Chourasia p = re.compile(r'\s*FN\((\w+), (\d+), ##ctx\)|\\\\') 29671a3cdf8SUsama Arif fn_defines_str = '' 297ce3e44a0SAndrii Nakryiko i = 0 29871a3cdf8SUsama Arif while True: 29971a3cdf8SUsama Arif capture = p.match(self.line) 30071a3cdf8SUsama Arif if capture: 30171a3cdf8SUsama Arif fn_defines_str += self.line 302ce3e44a0SAndrii Nakryiko helper_name = capture.expand(r'bpf_\1') 3035fbea423SMichal Suchanek self.helper_enum_vals[helper_name] = int(capture.group(2)) 304ce3e44a0SAndrii Nakryiko self.helper_enum_pos[helper_name] = i 305ce3e44a0SAndrii Nakryiko i += 1 30671a3cdf8SUsama Arif else: 30771a3cdf8SUsama Arif break 30871a3cdf8SUsama Arif self.line = self.reader.readline() 30971a3cdf8SUsama Arif # Find the number of occurences of FN(\w+) 310121fd33bSVishal Chourasia self.define_unique_helpers = re.findall(r'FN\(\w+, \d+, ##ctx\)', fn_defines_str) 31171a3cdf8SUsama Arif 312ce3e44a0SAndrii Nakryiko def validate_helpers(self): 313ce3e44a0SAndrii Nakryiko last_helper = '' 3140a0d55efSEyal Birger seen_helpers = set() 315ce3e44a0SAndrii Nakryiko seen_enum_vals = set() 316ce3e44a0SAndrii Nakryiko i = 0 3170a0d55efSEyal Birger for helper in self.helpers: 3180a0d55efSEyal Birger proto = helper.proto_break_down() 3190a0d55efSEyal Birger name = proto['name'] 3200a0d55efSEyal Birger try: 3210a0d55efSEyal Birger enum_val = self.helper_enum_vals[name] 322ce3e44a0SAndrii Nakryiko enum_pos = self.helper_enum_pos[name] 3230a0d55efSEyal Birger except KeyError: 3240a0d55efSEyal Birger raise Exception("Helper %s is missing from enum bpf_func_id" % name) 3250a0d55efSEyal Birger 326ce3e44a0SAndrii Nakryiko if name in seen_helpers: 327ce3e44a0SAndrii Nakryiko if last_helper != name: 328ce3e44a0SAndrii Nakryiko raise Exception("Helper %s has multiple descriptions which are not grouped together" % name) 329ce3e44a0SAndrii Nakryiko continue 330ce3e44a0SAndrii Nakryiko 3310a0d55efSEyal Birger # Enforce current practice of having the descriptions ordered 3320a0d55efSEyal Birger # by enum value. 333ce3e44a0SAndrii Nakryiko if enum_pos != i: 334ce3e44a0SAndrii Nakryiko raise Exception("Helper %s (ID %d) comment order (#%d) must be aligned with its position (#%d) in enum bpf_func_id" % (name, enum_val, i + 1, enum_pos + 1)) 335ce3e44a0SAndrii Nakryiko if enum_val in seen_enum_vals: 336ce3e44a0SAndrii Nakryiko raise Exception("Helper %s has duplicated value %d" % (name, enum_val)) 337ce3e44a0SAndrii Nakryiko 3380a0d55efSEyal Birger seen_helpers.add(name) 339ce3e44a0SAndrii Nakryiko last_helper = name 340ce3e44a0SAndrii Nakryiko seen_enum_vals.add(enum_val) 3410a0d55efSEyal Birger 3420a0d55efSEyal Birger helper.enum_val = enum_val 343ce3e44a0SAndrii Nakryiko i += 1 3440a0d55efSEyal Birger 345a67882a2SJoe Stringer def run(self): 3460ba3929eSUsama Arif self.parse_desc_syscall() 3470ba3929eSUsama Arif self.parse_enum_syscall() 34871a3cdf8SUsama Arif self.parse_desc_helpers() 34971a3cdf8SUsama Arif self.parse_define_helpers() 350ce3e44a0SAndrii Nakryiko self.validate_helpers() 351923a932cSJoe Stringer self.reader.close() 352923a932cSJoe Stringer 353923a932cSJoe Stringer############################################################################### 354923a932cSJoe Stringer 355923a932cSJoe Stringerclass Printer(object): 356923a932cSJoe Stringer """ 357923a932cSJoe Stringer A generic class for printers. Printers should be created with an array of 358923a932cSJoe Stringer Helper objects, and implement a way to print them in the desired fashion. 359923a932cSJoe Stringer @parser: A HeaderParser with objects to print to standard output 360923a932cSJoe Stringer """ 361923a932cSJoe Stringer def __init__(self, parser): 362923a932cSJoe Stringer self.parser = parser 363923a932cSJoe Stringer self.elements = [] 364923a932cSJoe Stringer 365923a932cSJoe Stringer def print_header(self): 366923a932cSJoe Stringer pass 367923a932cSJoe Stringer 368923a932cSJoe Stringer def print_footer(self): 369923a932cSJoe Stringer pass 370923a932cSJoe Stringer 371923a932cSJoe Stringer def print_one(self, helper): 372923a932cSJoe Stringer pass 373923a932cSJoe Stringer 374923a932cSJoe Stringer def print_all(self): 375923a932cSJoe Stringer self.print_header() 376923a932cSJoe Stringer for elem in self.elements: 377923a932cSJoe Stringer self.print_one(elem) 378923a932cSJoe Stringer self.print_footer() 379923a932cSJoe Stringer 3800ba3929eSUsama Arif def elem_number_check(self, desc_unique_elem, define_unique_elem, type, instance): 3810ba3929eSUsama Arif """ 3820ba3929eSUsama Arif Checks the number of helpers/syscalls documented within the header file 3830ba3929eSUsama Arif description with those defined as part of enum/macro and raise an 3840ba3929eSUsama Arif Exception if they don't match. 3850ba3929eSUsama Arif """ 3860ba3929eSUsama Arif nr_desc_unique_elem = len(desc_unique_elem) 3870ba3929eSUsama Arif nr_define_unique_elem = len(define_unique_elem) 3880ba3929eSUsama Arif if nr_desc_unique_elem != nr_define_unique_elem: 3890ba3929eSUsama Arif exception_msg = ''' 3900ba3929eSUsama ArifThe number of unique %s in description (%d) doesn\'t match the number of unique %s defined in %s (%d) 3910ba3929eSUsama Arif''' % (type, nr_desc_unique_elem, type, instance, nr_define_unique_elem) 3920ba3929eSUsama Arif if nr_desc_unique_elem < nr_define_unique_elem: 3930ba3929eSUsama Arif # Function description is parsed until no helper is found (which can be due to 3940ba3929eSUsama Arif # misformatting). Hence, only print the first missing/misformatted helper/enum. 3950ba3929eSUsama Arif exception_msg += ''' 3960ba3929eSUsama ArifThe description for %s is not present or formatted correctly. 3970ba3929eSUsama Arif''' % (define_unique_elem[nr_desc_unique_elem]) 3980ba3929eSUsama Arif raise Exception(exception_msg) 399923a932cSJoe Stringer 400923a932cSJoe Stringerclass PrinterRST(Printer): 401923a932cSJoe Stringer """ 402923a932cSJoe Stringer A generic class for printers that print ReStructured Text. Printers should 403923a932cSJoe Stringer be created with a HeaderParser object, and implement a way to print API 404923a932cSJoe Stringer elements in the desired fashion. 405923a932cSJoe Stringer @parser: A HeaderParser with objects to print to standard output 406923a932cSJoe Stringer """ 407923a932cSJoe Stringer def __init__(self, parser): 408923a932cSJoe Stringer self.parser = parser 409923a932cSJoe Stringer 410923a932cSJoe Stringer def print_license(self): 411923a932cSJoe Stringer license = '''\ 412923a932cSJoe Stringer.. Copyright (C) All BPF authors and contributors from 2014 to present. 413923a932cSJoe Stringer.. See git log include/uapi/linux/bpf.h in kernel tree for details. 414923a932cSJoe Stringer.. 4155cb62b75SAlejandro Colomar.. SPDX-License-Identifier: Linux-man-pages-copyleft 416923a932cSJoe Stringer.. 417923a932cSJoe Stringer.. Please do not edit this file. It was generated from the documentation 418923a932cSJoe Stringer.. located in file include/uapi/linux/bpf.h of the Linux kernel sources 419923a932cSJoe Stringer.. (helpers description), and from scripts/bpf_doc.py in the same 420923a932cSJoe Stringer.. repository (header and footer). 421923a932cSJoe Stringer''' 422923a932cSJoe Stringer print(license) 423923a932cSJoe Stringer 424923a932cSJoe Stringer def print_elem(self, elem): 425923a932cSJoe Stringer if (elem.desc): 426923a932cSJoe Stringer print('\tDescription') 427923a932cSJoe Stringer # Do not strip all newline characters: formatted code at the end of 428923a932cSJoe Stringer # a section must be followed by a blank line. 429923a932cSJoe Stringer for line in re.sub('\n$', '', elem.desc, count=1).split('\n'): 430923a932cSJoe Stringer print('{}{}'.format('\t\t' if line else '', line)) 431923a932cSJoe Stringer 432923a932cSJoe Stringer if (elem.ret): 433923a932cSJoe Stringer print('\tReturn') 434923a932cSJoe Stringer for line in elem.ret.rstrip().split('\n'): 435923a932cSJoe Stringer print('{}{}'.format('\t\t' if line else '', line)) 436923a932cSJoe Stringer 437923a932cSJoe Stringer print('') 438923a932cSJoe Stringer 439fd0a38f9SQuentin Monnet def get_kernel_version(self): 440fd0a38f9SQuentin Monnet try: 441fd0a38f9SQuentin Monnet version = subprocess.run(['git', 'describe'], cwd=linuxRoot, 442fd0a38f9SQuentin Monnet capture_output=True, check=True) 443fd0a38f9SQuentin Monnet version = version.stdout.decode().rstrip() 444fd0a38f9SQuentin Monnet except: 445fd0a38f9SQuentin Monnet try: 4465384cc0dSHangbin Liu version = subprocess.run(['make', '-s', '--no-print-directory', 'kernelversion'], 4475384cc0dSHangbin Liu cwd=linuxRoot, capture_output=True, check=True) 448fd0a38f9SQuentin Monnet version = version.stdout.decode().rstrip() 449fd0a38f9SQuentin Monnet except: 450fd0a38f9SQuentin Monnet return 'Linux' 451fd0a38f9SQuentin Monnet return 'Linux {version}'.format(version=version) 452fd0a38f9SQuentin Monnet 45392ec1cc3SQuentin Monnet def get_last_doc_update(self, delimiter): 45492ec1cc3SQuentin Monnet try: 45592ec1cc3SQuentin Monnet cmd = ['git', 'log', '-1', '--pretty=format:%cs', '--no-patch', 45692ec1cc3SQuentin Monnet '-L', 457121fd33bSVishal Chourasia '/{}/,/\\*\\//:include/uapi/linux/bpf.h'.format(delimiter)] 45892ec1cc3SQuentin Monnet date = subprocess.run(cmd, cwd=linuxRoot, 45992ec1cc3SQuentin Monnet capture_output=True, check=True) 46092ec1cc3SQuentin Monnet return date.stdout.decode().rstrip() 46192ec1cc3SQuentin Monnet except: 46292ec1cc3SQuentin Monnet return '' 46392ec1cc3SQuentin Monnet 464923a932cSJoe Stringerclass PrinterHelpersRST(PrinterRST): 465923a932cSJoe Stringer """ 466923a932cSJoe Stringer A printer for dumping collected information about helpers as a ReStructured 467923a932cSJoe Stringer Text page compatible with the rst2man program, which can be used to 468923a932cSJoe Stringer generate a manual page for the helpers. 469923a932cSJoe Stringer @parser: A HeaderParser with Helper objects to print to standard output 470923a932cSJoe Stringer """ 471923a932cSJoe Stringer def __init__(self, parser): 472923a932cSJoe Stringer self.elements = parser.helpers 4738a76145aSAndrii Nakryiko self.elem_number_check(parser.desc_unique_helpers, parser.define_unique_helpers, 'helper', '___BPF_FUNC_MAPPER') 474923a932cSJoe Stringer 475923a932cSJoe Stringer def print_header(self): 476923a932cSJoe Stringer header = '''\ 477923a932cSJoe Stringer=========== 478923a932cSJoe StringerBPF-HELPERS 479923a932cSJoe Stringer=========== 480923a932cSJoe Stringer------------------------------------------------------------------------------- 481923a932cSJoe Stringerlist of eBPF helper functions 482923a932cSJoe Stringer------------------------------------------------------------------------------- 483923a932cSJoe Stringer 484923a932cSJoe Stringer:Manual section: 7 485fd0a38f9SQuentin Monnet:Version: {version} 48692ec1cc3SQuentin Monnet{date_field}{date} 487923a932cSJoe Stringer 488923a932cSJoe StringerDESCRIPTION 489923a932cSJoe Stringer=========== 490923a932cSJoe Stringer 491923a932cSJoe StringerThe extended Berkeley Packet Filter (eBPF) subsystem consists in programs 492923a932cSJoe Stringerwritten in a pseudo-assembly language, then attached to one of the several 493923a932cSJoe Stringerkernel hooks and run in reaction of specific events. This framework differs 494923a932cSJoe Stringerfrom the older, "classic" BPF (or "cBPF") in several aspects, one of them being 495923a932cSJoe Stringerthe ability to call special functions (or "helpers") from within a program. 496923a932cSJoe StringerThese functions are restricted to a white-list of helpers defined in the 497923a932cSJoe Stringerkernel. 498923a932cSJoe Stringer 499923a932cSJoe StringerThese helpers are used by eBPF programs to interact with the system, or with 500923a932cSJoe Stringerthe context in which they work. For instance, they can be used to print 501923a932cSJoe Stringerdebugging messages, to get the time since the system was booted, to interact 502923a932cSJoe Stringerwith eBPF maps, or to manipulate network packets. Since there are several eBPF 503923a932cSJoe Stringerprogram types, and that they do not run in the same context, each program type 504923a932cSJoe Stringercan only call a subset of those helpers. 505923a932cSJoe Stringer 506923a932cSJoe StringerDue to eBPF conventions, a helper can not have more than five arguments. 507923a932cSJoe Stringer 508923a932cSJoe StringerInternally, eBPF programs call directly into the compiled helper functions 509923a932cSJoe Stringerwithout requiring any foreign-function interface. As a result, calling helpers 510923a932cSJoe Stringerintroduces no overhead, thus offering excellent performance. 511923a932cSJoe Stringer 512923a932cSJoe StringerThis document is an attempt to list and document the helpers available to eBPF 513923a932cSJoe Stringerdevelopers. They are sorted by chronological order (the oldest helpers in the 514923a932cSJoe Stringerkernel at the top). 515923a932cSJoe Stringer 516923a932cSJoe StringerHELPERS 517923a932cSJoe Stringer======= 518923a932cSJoe Stringer''' 519fd0a38f9SQuentin Monnet kernelVersion = self.get_kernel_version() 52092ec1cc3SQuentin Monnet lastUpdate = self.get_last_doc_update(helpersDocStart) 521fd0a38f9SQuentin Monnet 522923a932cSJoe Stringer PrinterRST.print_license(self) 52392ec1cc3SQuentin Monnet print(header.format(version=kernelVersion, 52492ec1cc3SQuentin Monnet date_field = ':Date: ' if lastUpdate else '', 52592ec1cc3SQuentin Monnet date=lastUpdate)) 526923a932cSJoe Stringer 527923a932cSJoe Stringer def print_footer(self): 528923a932cSJoe Stringer footer = ''' 529923a932cSJoe StringerEXAMPLES 530923a932cSJoe Stringer======== 531923a932cSJoe Stringer 532923a932cSJoe StringerExample usage for most of the eBPF helpers listed in this manual page are 533923a932cSJoe Stringeravailable within the Linux kernel sources, at the following locations: 534923a932cSJoe Stringer 535923a932cSJoe Stringer* *samples/bpf/* 536923a932cSJoe Stringer* *tools/testing/selftests/bpf/* 537923a932cSJoe Stringer 538923a932cSJoe StringerLICENSE 539923a932cSJoe Stringer======= 540923a932cSJoe Stringer 541923a932cSJoe StringereBPF programs can have an associated license, passed along with the bytecode 542923a932cSJoe Stringerinstructions to the kernel when the programs are loaded. The format for that 543923a932cSJoe Stringerstring is identical to the one in use for kernel modules (Dual licenses, such 544923a932cSJoe Stringeras "Dual BSD/GPL", may be used). Some helper functions are only accessible to 545e37243b6SGianmarco Lusvardiprograms that are compatible with the GNU General Public License (GNU GPL). 546923a932cSJoe Stringer 547923a932cSJoe StringerIn order to use such helpers, the eBPF program must be loaded with the correct 548121fd33bSVishal Chourasialicense string passed (via **attr**) to the **bpf**\\ () system call, and this 549923a932cSJoe Stringergenerally translates into the C source code of the program containing a line 550923a932cSJoe Stringersimilar to the following: 551923a932cSJoe Stringer 552923a932cSJoe Stringer:: 553923a932cSJoe Stringer 554923a932cSJoe Stringer char ____license[] __attribute__((section("license"), used)) = "GPL"; 555923a932cSJoe Stringer 556923a932cSJoe StringerIMPLEMENTATION 557923a932cSJoe Stringer============== 558923a932cSJoe Stringer 559923a932cSJoe StringerThis manual page is an effort to document the existing eBPF helper functions. 560923a932cSJoe StringerBut as of this writing, the BPF sub-system is under heavy development. New eBPF 561923a932cSJoe Stringerprogram or map types are added, along with new helper functions. Some helpers 562923a932cSJoe Stringerare occasionally made available for additional program types. So in spite of 563923a932cSJoe Stringerthe efforts of the community, this page might not be up-to-date. If you want to 564923a932cSJoe Stringercheck by yourself what helper functions exist in your kernel, or what types of 565923a932cSJoe Stringerprograms they can support, here are some files among the kernel tree that you 566923a932cSJoe Stringermay be interested in: 567923a932cSJoe Stringer 568923a932cSJoe Stringer* *include/uapi/linux/bpf.h* is the main BPF header. It contains the full list 569923a932cSJoe Stringer of all helper functions, as well as many other BPF definitions including most 570923a932cSJoe Stringer of the flags, structs or constants used by the helpers. 571923a932cSJoe Stringer* *net/core/filter.c* contains the definition of most network-related helper 572923a932cSJoe Stringer functions, and the list of program types from which they can be used. 573923a932cSJoe Stringer* *kernel/trace/bpf_trace.c* is the equivalent for most tracing program-related 574923a932cSJoe Stringer helpers. 575923a932cSJoe Stringer* *kernel/bpf/verifier.c* contains the functions used to check that valid types 576923a932cSJoe Stringer of eBPF maps are used with a given helper function. 577923a932cSJoe Stringer* *kernel/bpf/* directory contains other files in which additional helpers are 578923a932cSJoe Stringer defined (for cgroups, sockmaps, etc.). 579923a932cSJoe Stringer* The bpftool utility can be used to probe the availability of helper functions 580923a932cSJoe Stringer on the system (as well as supported program and map types, and a number of 581923a932cSJoe Stringer other parameters). To do so, run **bpftool feature probe** (see 582121fd33bSVishal Chourasia **bpftool-feature**\\ (8) for details). Add the **unprivileged** keyword to 583923a932cSJoe Stringer list features available to unprivileged users. 584923a932cSJoe Stringer 585923a932cSJoe StringerCompatibility between helper functions and program types can generally be found 586923a932cSJoe Stringerin the files where helper functions are defined. Look for the **struct 587923a932cSJoe Stringerbpf_func_proto** objects and for functions returning them: these functions 588923a932cSJoe Stringercontain a list of helpers that a given program type can call. Note that the 589923a932cSJoe Stringer**default:** label of the **switch ... case** used to filter helpers can call 590923a932cSJoe Stringerother functions, themselves allowing access to additional helpers. The 591923a932cSJoe Stringerrequirement for GPL license is also in those **struct bpf_func_proto**. 592923a932cSJoe Stringer 593923a932cSJoe StringerCompatibility between helper functions and map types can be found in the 594121fd33bSVishal Chourasia**check_map_func_compatibility**\\ () function in file *kernel/bpf/verifier.c*. 595923a932cSJoe Stringer 596923a932cSJoe StringerHelper functions that invalidate the checks on **data** and **data_end** 597923a932cSJoe Stringerpointers for network processing are listed in function 598121fd33bSVishal Chourasia**bpf_helper_changes_pkt_data**\\ () in file *net/core/filter.c*. 599923a932cSJoe Stringer 600923a932cSJoe StringerSEE ALSO 601923a932cSJoe Stringer======== 602923a932cSJoe Stringer 603121fd33bSVishal Chourasia**bpf**\\ (2), 604121fd33bSVishal Chourasia**bpftool**\\ (8), 605121fd33bSVishal Chourasia**cgroups**\\ (7), 606121fd33bSVishal Chourasia**ip**\\ (8), 607121fd33bSVishal Chourasia**perf_event_open**\\ (2), 608121fd33bSVishal Chourasia**sendmsg**\\ (2), 609121fd33bSVishal Chourasia**socket**\\ (7), 610121fd33bSVishal Chourasia**tc-bpf**\\ (8)''' 611923a932cSJoe Stringer print(footer) 612923a932cSJoe Stringer 613923a932cSJoe Stringer def print_proto(self, helper): 614923a932cSJoe Stringer """ 615923a932cSJoe Stringer Format function protocol with bold and italics markers. This makes RST 616923a932cSJoe Stringer file less readable, but gives nice results in the manual page. 617923a932cSJoe Stringer """ 618923a932cSJoe Stringer proto = helper.proto_break_down() 619923a932cSJoe Stringer 620923a932cSJoe Stringer print('**%s %s%s(' % (proto['ret_type'], 621923a932cSJoe Stringer proto['ret_star'].replace('*', '\\*'), 622923a932cSJoe Stringer proto['name']), 623923a932cSJoe Stringer end='') 624923a932cSJoe Stringer 625923a932cSJoe Stringer comma = '' 626923a932cSJoe Stringer for a in proto['args']: 627923a932cSJoe Stringer one_arg = '{}{}'.format(comma, a['type']) 628923a932cSJoe Stringer if a['name']: 629923a932cSJoe Stringer if a['star']: 630121fd33bSVishal Chourasia one_arg += ' {}**\\ '.format(a['star'].replace('*', '\\*')) 631923a932cSJoe Stringer else: 632923a932cSJoe Stringer one_arg += '** ' 633923a932cSJoe Stringer one_arg += '*{}*\\ **'.format(a['name']) 634923a932cSJoe Stringer comma = ', ' 635923a932cSJoe Stringer print(one_arg, end='') 636923a932cSJoe Stringer 637923a932cSJoe Stringer print(')**') 638923a932cSJoe Stringer 639923a932cSJoe Stringer def print_one(self, helper): 640923a932cSJoe Stringer self.print_proto(helper) 641923a932cSJoe Stringer self.print_elem(helper) 642923a932cSJoe Stringer 643923a932cSJoe Stringer 644a67882a2SJoe Stringerclass PrinterSyscallRST(PrinterRST): 645a67882a2SJoe Stringer """ 646a67882a2SJoe Stringer A printer for dumping collected information about the syscall API as a 647a67882a2SJoe Stringer ReStructured Text page compatible with the rst2man program, which can be 648a67882a2SJoe Stringer used to generate a manual page for the syscall. 649a67882a2SJoe Stringer @parser: A HeaderParser with APIElement objects to print to standard 650a67882a2SJoe Stringer output 651a67882a2SJoe Stringer """ 652a67882a2SJoe Stringer def __init__(self, parser): 653a67882a2SJoe Stringer self.elements = parser.commands 6540ba3929eSUsama Arif self.elem_number_check(parser.desc_syscalls, parser.enum_syscalls, 'syscall', 'bpf_cmd') 655a67882a2SJoe Stringer 656a67882a2SJoe Stringer def print_header(self): 657a67882a2SJoe Stringer header = '''\ 658a67882a2SJoe Stringer=== 659a67882a2SJoe Stringerbpf 660a67882a2SJoe Stringer=== 661a67882a2SJoe Stringer------------------------------------------------------------------------------- 662a67882a2SJoe StringerPerform a command on an extended BPF object 663a67882a2SJoe Stringer------------------------------------------------------------------------------- 664a67882a2SJoe Stringer 665a67882a2SJoe Stringer:Manual section: 2 666a67882a2SJoe Stringer 667a67882a2SJoe StringerCOMMANDS 668a67882a2SJoe Stringer======== 669a67882a2SJoe Stringer''' 670a67882a2SJoe Stringer PrinterRST.print_license(self) 671a67882a2SJoe Stringer print(header) 672a67882a2SJoe Stringer 673a67882a2SJoe Stringer def print_one(self, command): 674a67882a2SJoe Stringer print('**%s**' % (command.proto)) 675a67882a2SJoe Stringer self.print_elem(command) 676923a932cSJoe Stringer 677923a932cSJoe Stringer 678923a932cSJoe Stringerclass PrinterHelpers(Printer): 679923a932cSJoe Stringer """ 680923a932cSJoe Stringer A printer for dumping collected information about helpers as C header to 681923a932cSJoe Stringer be included from BPF program. 682923a932cSJoe Stringer @parser: A HeaderParser with Helper objects to print to standard output 683923a932cSJoe Stringer """ 684923a932cSJoe Stringer def __init__(self, parser): 685923a932cSJoe Stringer self.elements = parser.helpers 6868a76145aSAndrii Nakryiko self.elem_number_check(parser.desc_unique_helpers, parser.define_unique_helpers, 'helper', '___BPF_FUNC_MAPPER') 687923a932cSJoe Stringer 688923a932cSJoe Stringer type_fwds = [ 689923a932cSJoe Stringer 'struct bpf_fib_lookup', 690923a932cSJoe Stringer 'struct bpf_sk_lookup', 691923a932cSJoe Stringer 'struct bpf_perf_event_data', 692923a932cSJoe Stringer 'struct bpf_perf_event_value', 693923a932cSJoe Stringer 'struct bpf_pidns_info', 694923a932cSJoe Stringer 'struct bpf_redir_neigh', 695923a932cSJoe Stringer 'struct bpf_sock', 696923a932cSJoe Stringer 'struct bpf_sock_addr', 697923a932cSJoe Stringer 'struct bpf_sock_ops', 698923a932cSJoe Stringer 'struct bpf_sock_tuple', 699923a932cSJoe Stringer 'struct bpf_spin_lock', 700923a932cSJoe Stringer 'struct bpf_sysctl', 701923a932cSJoe Stringer 'struct bpf_tcp_sock', 702923a932cSJoe Stringer 'struct bpf_tunnel_key', 703923a932cSJoe Stringer 'struct bpf_xfrm_state', 704923a932cSJoe Stringer 'struct linux_binprm', 705923a932cSJoe Stringer 'struct pt_regs', 706923a932cSJoe Stringer 'struct sk_reuseport_md', 707923a932cSJoe Stringer 'struct sockaddr', 708923a932cSJoe Stringer 'struct tcphdr', 709923a932cSJoe Stringer 'struct seq_file', 710923a932cSJoe Stringer 'struct tcp6_sock', 711923a932cSJoe Stringer 'struct tcp_sock', 712923a932cSJoe Stringer 'struct tcp_timewait_sock', 713923a932cSJoe Stringer 'struct tcp_request_sock', 714923a932cSJoe Stringer 'struct udp6_sock', 7159eeb3aa3SHengqi Chen 'struct unix_sock', 716923a932cSJoe Stringer 'struct task_struct', 717c4bcfb38SYonghong Song 'struct cgroup', 718923a932cSJoe Stringer 719923a932cSJoe Stringer 'struct __sk_buff', 720923a932cSJoe Stringer 'struct sk_msg_md', 721923a932cSJoe Stringer 'struct xdp_md', 722923a932cSJoe Stringer 'struct path', 723923a932cSJoe Stringer 'struct btf_ptr', 724923a932cSJoe Stringer 'struct inode', 725923a932cSJoe Stringer 'struct socket', 726923a932cSJoe Stringer 'struct file', 727b00628b1SAlexei Starovoitov 'struct bpf_timer', 7283bc253c2SGeliang Tang 'struct mptcp_sock', 72997e03f52SJoanne Koong 'struct bpf_dynptr', 73033bf9885SMaxim Mikityanskiy 'struct iphdr', 73133bf9885SMaxim Mikityanskiy 'struct ipv6hdr', 732923a932cSJoe Stringer ] 733923a932cSJoe Stringer known_types = { 734923a932cSJoe Stringer '...', 735923a932cSJoe Stringer 'void', 736923a932cSJoe Stringer 'const void', 737923a932cSJoe Stringer 'char', 738923a932cSJoe Stringer 'const char', 739923a932cSJoe Stringer 'int', 740923a932cSJoe Stringer 'long', 741923a932cSJoe Stringer 'unsigned long', 742923a932cSJoe Stringer 743923a932cSJoe Stringer '__be16', 744923a932cSJoe Stringer '__be32', 745923a932cSJoe Stringer '__wsum', 746923a932cSJoe Stringer 747923a932cSJoe Stringer 'struct bpf_fib_lookup', 748923a932cSJoe Stringer 'struct bpf_perf_event_data', 749923a932cSJoe Stringer 'struct bpf_perf_event_value', 750923a932cSJoe Stringer 'struct bpf_pidns_info', 751923a932cSJoe Stringer 'struct bpf_redir_neigh', 752923a932cSJoe Stringer 'struct bpf_sk_lookup', 753923a932cSJoe Stringer 'struct bpf_sock', 754923a932cSJoe Stringer 'struct bpf_sock_addr', 755923a932cSJoe Stringer 'struct bpf_sock_ops', 756923a932cSJoe Stringer 'struct bpf_sock_tuple', 757923a932cSJoe Stringer 'struct bpf_spin_lock', 758923a932cSJoe Stringer 'struct bpf_sysctl', 759923a932cSJoe Stringer 'struct bpf_tcp_sock', 760923a932cSJoe Stringer 'struct bpf_tunnel_key', 761923a932cSJoe Stringer 'struct bpf_xfrm_state', 762923a932cSJoe Stringer 'struct linux_binprm', 763923a932cSJoe Stringer 'struct pt_regs', 764923a932cSJoe Stringer 'struct sk_reuseport_md', 765923a932cSJoe Stringer 'struct sockaddr', 766923a932cSJoe Stringer 'struct tcphdr', 767923a932cSJoe Stringer 'struct seq_file', 768923a932cSJoe Stringer 'struct tcp6_sock', 769923a932cSJoe Stringer 'struct tcp_sock', 770923a932cSJoe Stringer 'struct tcp_timewait_sock', 771923a932cSJoe Stringer 'struct tcp_request_sock', 772923a932cSJoe Stringer 'struct udp6_sock', 7739eeb3aa3SHengqi Chen 'struct unix_sock', 774923a932cSJoe Stringer 'struct task_struct', 775c4bcfb38SYonghong Song 'struct cgroup', 776923a932cSJoe Stringer 'struct path', 777923a932cSJoe Stringer 'struct btf_ptr', 778923a932cSJoe Stringer 'struct inode', 779923a932cSJoe Stringer 'struct socket', 780923a932cSJoe Stringer 'struct file', 781b00628b1SAlexei Starovoitov 'struct bpf_timer', 7823bc253c2SGeliang Tang 'struct mptcp_sock', 78397e03f52SJoanne Koong 'struct bpf_dynptr', 78427060531SKumar Kartikeya Dwivedi 'const struct bpf_dynptr', 78533bf9885SMaxim Mikityanskiy 'struct iphdr', 78633bf9885SMaxim Mikityanskiy 'struct ipv6hdr', 787923a932cSJoe Stringer } 788923a932cSJoe Stringer mapped_types = { 789923a932cSJoe Stringer 'u8': '__u8', 790923a932cSJoe Stringer 'u16': '__u16', 791923a932cSJoe Stringer 'u32': '__u32', 792923a932cSJoe Stringer 'u64': '__u64', 793923a932cSJoe Stringer 's8': '__s8', 794923a932cSJoe Stringer 's16': '__s16', 795923a932cSJoe Stringer 's32': '__s32', 796923a932cSJoe Stringer 's64': '__s64', 797923a932cSJoe Stringer 'size_t': 'unsigned long', 798923a932cSJoe Stringer 'struct bpf_map': 'void', 799923a932cSJoe Stringer 'struct sk_buff': 'struct __sk_buff', 800923a932cSJoe Stringer 'const struct sk_buff': 'const struct __sk_buff', 801923a932cSJoe Stringer 'struct sk_msg_buff': 'struct sk_msg_md', 802923a932cSJoe Stringer 'struct xdp_buff': 'struct xdp_md', 803923a932cSJoe Stringer } 804923a932cSJoe Stringer # Helpers overloaded for different context types. 805923a932cSJoe Stringer overloaded_helpers = [ 806923a932cSJoe Stringer 'bpf_get_socket_cookie', 807923a932cSJoe Stringer 'bpf_sk_assign', 808923a932cSJoe Stringer ] 809923a932cSJoe Stringer 810923a932cSJoe Stringer def print_header(self): 811923a932cSJoe Stringer header = '''\ 812923a932cSJoe Stringer/* This is auto-generated file. See bpf_doc.py for details. */ 813923a932cSJoe Stringer 814923a932cSJoe Stringer/* Forward declarations of BPF structs */''' 815923a932cSJoe Stringer 816923a932cSJoe Stringer print(header) 817923a932cSJoe Stringer for fwd in self.type_fwds: 818923a932cSJoe Stringer print('%s;' % fwd) 819923a932cSJoe Stringer print('') 820923a932cSJoe Stringer 821*48b13cabSEduard Zingerman used_attrs = set() 822*48b13cabSEduard Zingerman for helper in self.elements: 823*48b13cabSEduard Zingerman for attr in helper.attrs: 824*48b13cabSEduard Zingerman used_attrs.add(attr) 825*48b13cabSEduard Zingerman for attr in sorted(used_attrs): 826*48b13cabSEduard Zingerman print('#ifndef %s' % attr) 827*48b13cabSEduard Zingerman print('#if __has_attribute(%s)' % ATTRS[attr]) 828*48b13cabSEduard Zingerman print('#define %s __attribute__((%s))' % (attr, ATTRS[attr])) 829*48b13cabSEduard Zingerman print('#else') 830*48b13cabSEduard Zingerman print('#define %s' % attr) 831*48b13cabSEduard Zingerman print('#endif') 832*48b13cabSEduard Zingerman print('#endif') 833*48b13cabSEduard Zingerman if used_attrs: 834*48b13cabSEduard Zingerman print('') 835*48b13cabSEduard Zingerman 836923a932cSJoe Stringer def print_footer(self): 837923a932cSJoe Stringer footer = '' 838923a932cSJoe Stringer print(footer) 839923a932cSJoe Stringer 840923a932cSJoe Stringer def map_type(self, t): 841923a932cSJoe Stringer if t in self.known_types: 842923a932cSJoe Stringer return t 843923a932cSJoe Stringer if t in self.mapped_types: 844923a932cSJoe Stringer return self.mapped_types[t] 845923a932cSJoe Stringer print("Unrecognized type '%s', please add it to known types!" % t, 846923a932cSJoe Stringer file=sys.stderr) 847923a932cSJoe Stringer sys.exit(1) 848923a932cSJoe Stringer 849923a932cSJoe Stringer seen_helpers = set() 850923a932cSJoe Stringer 851923a932cSJoe Stringer def print_one(self, helper): 852923a932cSJoe Stringer proto = helper.proto_break_down() 853923a932cSJoe Stringer 854923a932cSJoe Stringer if proto['name'] in self.seen_helpers: 855923a932cSJoe Stringer return 856923a932cSJoe Stringer self.seen_helpers.add(proto['name']) 857923a932cSJoe Stringer 858923a932cSJoe Stringer print('/*') 859923a932cSJoe Stringer print(" * %s" % proto['name']) 860923a932cSJoe Stringer print(" *") 861923a932cSJoe Stringer if (helper.desc): 862923a932cSJoe Stringer # Do not strip all newline characters: formatted code at the end of 863923a932cSJoe Stringer # a section must be followed by a blank line. 864923a932cSJoe Stringer for line in re.sub('\n$', '', helper.desc, count=1).split('\n'): 865923a932cSJoe Stringer print(' *{}{}'.format(' \t' if line else '', line)) 866923a932cSJoe Stringer 867923a932cSJoe Stringer if (helper.ret): 868923a932cSJoe Stringer print(' *') 869923a932cSJoe Stringer print(' * Returns') 870923a932cSJoe Stringer for line in helper.ret.rstrip().split('\n'): 871923a932cSJoe Stringer print(' *{}{}'.format(' \t' if line else '', line)) 872923a932cSJoe Stringer 873923a932cSJoe Stringer print(' */') 874*48b13cabSEduard Zingerman print('static ', end='') 875*48b13cabSEduard Zingerman if helper.attrs: 876*48b13cabSEduard Zingerman print('%s ' % (" ".join(helper.attrs)), end='') 877*48b13cabSEduard Zingerman print('%s %s(* const %s)(' % (self.map_type(proto['ret_type']), 878923a932cSJoe Stringer proto['ret_star'], proto['name']), end='') 879923a932cSJoe Stringer comma = '' 880923a932cSJoe Stringer for i, a in enumerate(proto['args']): 881923a932cSJoe Stringer t = a['type'] 882923a932cSJoe Stringer n = a['name'] 883923a932cSJoe Stringer if proto['name'] in self.overloaded_helpers and i == 0: 884923a932cSJoe Stringer t = 'void' 885923a932cSJoe Stringer n = 'ctx' 886923a932cSJoe Stringer one_arg = '{}{}'.format(comma, self.map_type(t)) 887923a932cSJoe Stringer if n: 888923a932cSJoe Stringer if a['star']: 889923a932cSJoe Stringer one_arg += ' {}'.format(a['star']) 890923a932cSJoe Stringer else: 891923a932cSJoe Stringer one_arg += ' ' 892923a932cSJoe Stringer one_arg += '{}'.format(n) 893923a932cSJoe Stringer comma = ', ' 894923a932cSJoe Stringer print(one_arg, end='') 895923a932cSJoe Stringer 8960a0d55efSEyal Birger print(') = (void *) %d;' % helper.enum_val) 897923a932cSJoe Stringer print('') 898923a932cSJoe Stringer 899923a932cSJoe Stringer############################################################################### 900923a932cSJoe Stringer 901923a932cSJoe Stringer# If script is launched from scripts/ from kernel tree and can access 902923a932cSJoe Stringer# ../include/uapi/linux/bpf.h, use it as a default name for the file to parse, 903923a932cSJoe Stringer# otherwise the --filename argument will be required from the command line. 904923a932cSJoe Stringerscript = os.path.abspath(sys.argv[0]) 905923a932cSJoe StringerlinuxRoot = os.path.dirname(os.path.dirname(script)) 906923a932cSJoe Stringerbpfh = os.path.join(linuxRoot, 'include/uapi/linux/bpf.h') 907923a932cSJoe Stringer 908923a932cSJoe Stringerprinters = { 909923a932cSJoe Stringer 'helpers': PrinterHelpersRST, 910a67882a2SJoe Stringer 'syscall': PrinterSyscallRST, 911923a932cSJoe Stringer} 912923a932cSJoe Stringer 913923a932cSJoe StringerargParser = argparse.ArgumentParser(description=""" 914923a932cSJoe StringerParse eBPF header file and generate documentation for the eBPF API. 915923a932cSJoe StringerThe RST-formatted output produced can be turned into a manual page with the 916923a932cSJoe Stringerrst2man utility. 917923a932cSJoe Stringer""") 918923a932cSJoe StringerargParser.add_argument('--header', action='store_true', 919923a932cSJoe Stringer help='generate C header file') 920923a932cSJoe Stringerif (os.path.isfile(bpfh)): 921923a932cSJoe Stringer argParser.add_argument('--filename', help='path to include/uapi/linux/bpf.h', 922923a932cSJoe Stringer default=bpfh) 923923a932cSJoe Stringerelse: 924923a932cSJoe Stringer argParser.add_argument('--filename', help='path to include/uapi/linux/bpf.h') 925923a932cSJoe StringerargParser.add_argument('target', nargs='?', default='helpers', 926923a932cSJoe Stringer choices=printers.keys(), help='eBPF API target') 927923a932cSJoe Stringerargs = argParser.parse_args() 928923a932cSJoe Stringer 929923a932cSJoe Stringer# Parse file. 930923a932cSJoe StringerheaderParser = HeaderParser(args.filename) 931923a932cSJoe StringerheaderParser.run() 932923a932cSJoe Stringer 933923a932cSJoe Stringer# Print formatted output to standard output. 934923a932cSJoe Stringerif args.header: 935a67882a2SJoe Stringer if args.target != 'helpers': 936a67882a2SJoe Stringer raise NotImplementedError('Only helpers header generation is supported') 937923a932cSJoe Stringer printer = PrinterHelpers(headerParser) 938923a932cSJoe Stringerelse: 939923a932cSJoe Stringer printer = printers[args.target](headerParser) 940923a932cSJoe Stringerprinter.print_all() 941