xref: /openbmc/linux/scripts/bpf_doc.py (revision 86aa961bb4619a68077ebeba21c52e9ba0eab43d)
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    """
40923a932cSJoe Stringer    def __init__(self, proto='', desc='', ret=''):
41923a932cSJoe Stringer        self.proto = proto
42923a932cSJoe Stringer        self.desc = desc
43923a932cSJoe Stringer        self.ret = ret
44923a932cSJoe Stringer
45a67882a2SJoe Stringer
46a67882a2SJoe Stringerclass Helper(APIElement):
47a67882a2SJoe Stringer    """
48a67882a2SJoe Stringer    An object representing the description of an eBPF helper function.
49a67882a2SJoe Stringer    @proto: function prototype of the helper function
50a67882a2SJoe Stringer    @desc: textual description of the helper function
51a67882a2SJoe Stringer    @ret: description of the return value of the helper function
52a67882a2SJoe Stringer    """
530a0d55efSEyal Birger    def __init__(self, *args, **kwargs):
540a0d55efSEyal Birger        super().__init__(*args, **kwargs)
550a0d55efSEyal Birger        self.enum_val = None
560a0d55efSEyal Birger
57923a932cSJoe Stringer    def proto_break_down(self):
58923a932cSJoe Stringer        """
59923a932cSJoe Stringer        Break down helper function protocol into smaller chunks: return type,
60923a932cSJoe Stringer        name, distincts arguments.
61923a932cSJoe Stringer        """
62121fd33bSVishal Chourasia        arg_re = re.compile(r'((\w+ )*?(\w+|...))( (\**)(\w+))?$')
63923a932cSJoe Stringer        res = {}
64121fd33bSVishal Chourasia        proto_re = re.compile(r'(.+) (\**)(\w+)\(((([^,]+)(, )?){1,5})\)$')
65923a932cSJoe Stringer
66923a932cSJoe Stringer        capture = proto_re.match(self.proto)
67923a932cSJoe Stringer        res['ret_type'] = capture.group(1)
68923a932cSJoe Stringer        res['ret_star'] = capture.group(2)
69923a932cSJoe Stringer        res['name']     = capture.group(3)
70923a932cSJoe Stringer        res['args'] = []
71923a932cSJoe Stringer
72923a932cSJoe Stringer        args    = capture.group(4).split(', ')
73923a932cSJoe Stringer        for a in args:
74923a932cSJoe Stringer            capture = arg_re.match(a)
75923a932cSJoe Stringer            res['args'].append({
76923a932cSJoe Stringer                'type' : capture.group(1),
77923a932cSJoe Stringer                'star' : capture.group(5),
78923a932cSJoe Stringer                'name' : capture.group(6)
79923a932cSJoe Stringer            })
80923a932cSJoe Stringer
81923a932cSJoe Stringer        return res
82923a932cSJoe Stringer
83a67882a2SJoe Stringer
84923a932cSJoe Stringerclass HeaderParser(object):
85923a932cSJoe Stringer    """
86923a932cSJoe Stringer    An object used to parse a file in order to extract the documentation of a
87923a932cSJoe Stringer    list of eBPF helper functions. All the helpers that can be retrieved are
88923a932cSJoe Stringer    stored as Helper object, in the self.helpers() array.
89923a932cSJoe Stringer    @filename: name of file to parse, usually include/uapi/linux/bpf.h in the
90923a932cSJoe Stringer               kernel tree
91923a932cSJoe Stringer    """
92923a932cSJoe Stringer    def __init__(self, filename):
93923a932cSJoe Stringer        self.reader = open(filename, 'r')
94923a932cSJoe Stringer        self.line = ''
95923a932cSJoe Stringer        self.helpers = []
96a67882a2SJoe Stringer        self.commands = []
9771a3cdf8SUsama Arif        self.desc_unique_helpers = set()
9871a3cdf8SUsama Arif        self.define_unique_helpers = []
990a0d55efSEyal Birger        self.helper_enum_vals = {}
100ce3e44a0SAndrii Nakryiko        self.helper_enum_pos = {}
1010ba3929eSUsama Arif        self.desc_syscalls = []
1020ba3929eSUsama Arif        self.enum_syscalls = []
103a67882a2SJoe Stringer
104a67882a2SJoe Stringer    def parse_element(self):
105a67882a2SJoe Stringer        proto    = self.parse_symbol()
106f1f3f67fSUsama Arif        desc     = self.parse_desc(proto)
107f1f3f67fSUsama Arif        ret      = self.parse_ret(proto)
108a67882a2SJoe Stringer        return APIElement(proto=proto, desc=desc, ret=ret)
109923a932cSJoe Stringer
110923a932cSJoe Stringer    def parse_helper(self):
111923a932cSJoe Stringer        proto    = self.parse_proto()
112f1f3f67fSUsama Arif        desc     = self.parse_desc(proto)
113f1f3f67fSUsama Arif        ret      = self.parse_ret(proto)
114923a932cSJoe Stringer        return Helper(proto=proto, desc=desc, ret=ret)
115923a932cSJoe Stringer
116a67882a2SJoe Stringer    def parse_symbol(self):
117121fd33bSVishal Chourasia        p = re.compile(r' \* ?(BPF\w+)$')
118a67882a2SJoe Stringer        capture = p.match(self.line)
119a67882a2SJoe Stringer        if not capture:
120a67882a2SJoe Stringer            raise NoSyscallCommandFound
121121fd33bSVishal Chourasia        end_re = re.compile(r' \* ?NOTES$')
122a67882a2SJoe Stringer        end = end_re.match(self.line)
123a67882a2SJoe Stringer        if end:
124a67882a2SJoe Stringer            raise NoSyscallCommandFound
125a67882a2SJoe Stringer        self.line = self.reader.readline()
126a67882a2SJoe Stringer        return capture.group(1)
127a67882a2SJoe Stringer
128923a932cSJoe Stringer    def parse_proto(self):
129923a932cSJoe Stringer        # Argument can be of shape:
130923a932cSJoe Stringer        #   - "void"
131923a932cSJoe Stringer        #   - "type  name"
132923a932cSJoe Stringer        #   - "type *name"
133923a932cSJoe Stringer        #   - Same as above, with "const" and/or "struct" in front of type
134923a932cSJoe Stringer        #   - "..." (undefined number of arguments, for bpf_trace_printk())
135923a932cSJoe Stringer        # There is at least one term ("void"), and at most five arguments.
136121fd33bSVishal Chourasia        p = re.compile(r' \* ?((.+) \**\w+\((((const )?(struct )?(\w+|\.\.\.)( \**\w+)?)(, )?){1,5}\))$')
137923a932cSJoe Stringer        capture = p.match(self.line)
138923a932cSJoe Stringer        if not capture:
139923a932cSJoe Stringer            raise NoHelperFound
140923a932cSJoe Stringer        self.line = self.reader.readline()
141923a932cSJoe Stringer        return capture.group(1)
142923a932cSJoe Stringer
143f1f3f67fSUsama Arif    def parse_desc(self, proto):
144121fd33bSVishal Chourasia        p = re.compile(r' \* ?(?:\t| {5,8})Description$')
145923a932cSJoe Stringer        capture = p.match(self.line)
146923a932cSJoe Stringer        if not capture:
147f1f3f67fSUsama Arif            raise Exception("No description section found for " + proto)
148923a932cSJoe Stringer        # Description can be several lines, some of them possibly empty, and it
149923a932cSJoe Stringer        # stops when another subsection title is met.
150923a932cSJoe Stringer        desc = ''
151f1f3f67fSUsama Arif        desc_present = False
152923a932cSJoe Stringer        while True:
153923a932cSJoe Stringer            self.line = self.reader.readline()
154923a932cSJoe Stringer            if self.line == ' *\n':
155923a932cSJoe Stringer                desc += '\n'
156923a932cSJoe Stringer            else:
157121fd33bSVishal Chourasia                p = re.compile(r' \* ?(?:\t| {5,8})(?:\t| {8})(.*)')
158923a932cSJoe Stringer                capture = p.match(self.line)
159923a932cSJoe Stringer                if capture:
160f1f3f67fSUsama Arif                    desc_present = True
161923a932cSJoe Stringer                    desc += capture.group(1) + '\n'
162923a932cSJoe Stringer                else:
163923a932cSJoe Stringer                    break
164f1f3f67fSUsama Arif
165f1f3f67fSUsama Arif        if not desc_present:
166f1f3f67fSUsama Arif            raise Exception("No description found for " + proto)
167923a932cSJoe Stringer        return desc
168923a932cSJoe Stringer
169f1f3f67fSUsama Arif    def parse_ret(self, proto):
170121fd33bSVishal Chourasia        p = re.compile(r' \* ?(?:\t| {5,8})Return$')
171923a932cSJoe Stringer        capture = p.match(self.line)
172923a932cSJoe Stringer        if not capture:
173f1f3f67fSUsama Arif            raise Exception("No return section found for " + proto)
174923a932cSJoe Stringer        # Return value description can be several lines, some of them possibly
175923a932cSJoe Stringer        # empty, and it stops when another subsection title is met.
176923a932cSJoe Stringer        ret = ''
177f1f3f67fSUsama Arif        ret_present = False
178923a932cSJoe Stringer        while True:
179923a932cSJoe Stringer            self.line = self.reader.readline()
180923a932cSJoe Stringer            if self.line == ' *\n':
181923a932cSJoe Stringer                ret += '\n'
182923a932cSJoe Stringer            else:
183121fd33bSVishal Chourasia                p = re.compile(r' \* ?(?:\t| {5,8})(?:\t| {8})(.*)')
184923a932cSJoe Stringer                capture = p.match(self.line)
185923a932cSJoe Stringer                if capture:
186f1f3f67fSUsama Arif                    ret_present = True
187923a932cSJoe Stringer                    ret += capture.group(1) + '\n'
188923a932cSJoe Stringer                else:
189923a932cSJoe Stringer                    break
190f1f3f67fSUsama Arif
191f1f3f67fSUsama Arif        if not ret_present:
192f1f3f67fSUsama Arif            raise Exception("No return found for " + proto)
193923a932cSJoe Stringer        return ret
194923a932cSJoe Stringer
1950ba3929eSUsama Arif    def seek_to(self, target, help_message, discard_lines = 1):
196a67882a2SJoe Stringer        self.reader.seek(0)
197a67882a2SJoe Stringer        offset = self.reader.read().find(target)
198923a932cSJoe Stringer        if offset == -1:
199a67882a2SJoe Stringer            raise Exception(help_message)
200923a932cSJoe Stringer        self.reader.seek(offset)
201923a932cSJoe Stringer        self.reader.readline()
2020ba3929eSUsama Arif        for _ in range(discard_lines):
203923a932cSJoe Stringer            self.reader.readline()
204923a932cSJoe Stringer        self.line = self.reader.readline()
205923a932cSJoe Stringer
2060ba3929eSUsama Arif    def parse_desc_syscall(self):
207a67882a2SJoe Stringer        self.seek_to('* DOC: eBPF Syscall Commands',
208a67882a2SJoe Stringer                     'Could not find start of eBPF syscall descriptions list')
209a67882a2SJoe Stringer        while True:
210a67882a2SJoe Stringer            try:
211a67882a2SJoe Stringer                command = self.parse_element()
212a67882a2SJoe Stringer                self.commands.append(command)
2130ba3929eSUsama Arif                self.desc_syscalls.append(command.proto)
2140ba3929eSUsama Arif
215a67882a2SJoe Stringer            except NoSyscallCommandFound:
216a67882a2SJoe Stringer                break
217a67882a2SJoe Stringer
2180ba3929eSUsama Arif    def parse_enum_syscall(self):
2190ba3929eSUsama Arif        self.seek_to('enum bpf_cmd {',
2200ba3929eSUsama Arif                     'Could not find start of bpf_cmd enum', 0)
2210ba3929eSUsama Arif        # Searches for either one or more BPF\w+ enums
222121fd33bSVishal Chourasia        bpf_p = re.compile(r'\s*(BPF\w+)+')
2230ba3929eSUsama Arif        # Searches for an enum entry assigned to another entry,
2240ba3929eSUsama Arif        # for e.g. BPF_PROG_RUN = BPF_PROG_TEST_RUN, which is
2250ba3929eSUsama Arif        # not documented hence should be skipped in check to
2260ba3929eSUsama Arif        # determine if the right number of syscalls are documented
227121fd33bSVishal Chourasia        assign_p = re.compile(r'\s*(BPF\w+)\s*=\s*(BPF\w+)')
2280ba3929eSUsama Arif        bpf_cmd_str = ''
2290ba3929eSUsama Arif        while True:
2300ba3929eSUsama Arif            capture = assign_p.match(self.line)
2310ba3929eSUsama Arif            if capture:
2320ba3929eSUsama Arif                # Skip line if an enum entry is assigned to another entry
2330ba3929eSUsama Arif                self.line = self.reader.readline()
2340ba3929eSUsama Arif                continue
2350ba3929eSUsama Arif            capture = bpf_p.match(self.line)
2360ba3929eSUsama Arif            if capture:
2370ba3929eSUsama Arif                bpf_cmd_str += self.line
2380ba3929eSUsama Arif            else:
2390ba3929eSUsama Arif                break
2400ba3929eSUsama Arif            self.line = self.reader.readline()
2410ba3929eSUsama Arif        # Find the number of occurences of BPF\w+
242121fd33bSVishal Chourasia        self.enum_syscalls = re.findall(r'(BPF\w+)+', bpf_cmd_str)
2430ba3929eSUsama Arif
24471a3cdf8SUsama Arif    def parse_desc_helpers(self):
24592ec1cc3SQuentin Monnet        self.seek_to(helpersDocStart,
246a67882a2SJoe Stringer                     'Could not find start of eBPF helper descriptions list')
247923a932cSJoe Stringer        while True:
248923a932cSJoe Stringer            try:
249923a932cSJoe Stringer                helper = self.parse_helper()
250923a932cSJoe Stringer                self.helpers.append(helper)
25171a3cdf8SUsama Arif                proto = helper.proto_break_down()
25271a3cdf8SUsama Arif                self.desc_unique_helpers.add(proto['name'])
253923a932cSJoe Stringer            except NoHelperFound:
254923a932cSJoe Stringer                break
255923a932cSJoe Stringer
25671a3cdf8SUsama Arif    def parse_define_helpers(self):
2578a76145aSAndrii Nakryiko        # Parse FN(...) in #define ___BPF_FUNC_MAPPER to compare later with the
2580a0d55efSEyal Birger        # number of unique function names present in description and use the
2590a0d55efSEyal Birger        # correct enumeration value.
26071a3cdf8SUsama Arif        # Note: seek_to(..) discards the first line below the target search text,
2618a76145aSAndrii Nakryiko        # resulting in FN(unspec, 0, ##ctx) being skipped and not added to
2628a76145aSAndrii Nakryiko        # self.define_unique_helpers.
2638a76145aSAndrii Nakryiko        self.seek_to('#define ___BPF_FUNC_MAPPER(FN, ctx...)',
26471a3cdf8SUsama Arif                     'Could not find start of eBPF helper definition list')
2650a0d55efSEyal Birger        # Searches for one FN(\w+) define or a backslash for newline
266121fd33bSVishal Chourasia        p = re.compile(r'\s*FN\((\w+), (\d+), ##ctx\)|\\\\')
26771a3cdf8SUsama Arif        fn_defines_str = ''
268ce3e44a0SAndrii Nakryiko        i = 0
26971a3cdf8SUsama Arif        while True:
27071a3cdf8SUsama Arif            capture = p.match(self.line)
27171a3cdf8SUsama Arif            if capture:
27271a3cdf8SUsama Arif                fn_defines_str += self.line
273ce3e44a0SAndrii Nakryiko                helper_name = capture.expand(r'bpf_\1')
2745fbea423SMichal Suchanek                self.helper_enum_vals[helper_name] = int(capture.group(2))
275ce3e44a0SAndrii Nakryiko                self.helper_enum_pos[helper_name] = i
276ce3e44a0SAndrii Nakryiko                i += 1
27771a3cdf8SUsama Arif            else:
27871a3cdf8SUsama Arif                break
27971a3cdf8SUsama Arif            self.line = self.reader.readline()
28071a3cdf8SUsama Arif        # Find the number of occurences of FN(\w+)
281121fd33bSVishal Chourasia        self.define_unique_helpers = re.findall(r'FN\(\w+, \d+, ##ctx\)', fn_defines_str)
28271a3cdf8SUsama Arif
283ce3e44a0SAndrii Nakryiko    def validate_helpers(self):
284ce3e44a0SAndrii Nakryiko        last_helper = ''
2850a0d55efSEyal Birger        seen_helpers = set()
286ce3e44a0SAndrii Nakryiko        seen_enum_vals = set()
287ce3e44a0SAndrii Nakryiko        i = 0
2880a0d55efSEyal Birger        for helper in self.helpers:
2890a0d55efSEyal Birger            proto = helper.proto_break_down()
2900a0d55efSEyal Birger            name = proto['name']
2910a0d55efSEyal Birger            try:
2920a0d55efSEyal Birger                enum_val = self.helper_enum_vals[name]
293ce3e44a0SAndrii Nakryiko                enum_pos = self.helper_enum_pos[name]
2940a0d55efSEyal Birger            except KeyError:
2950a0d55efSEyal Birger                raise Exception("Helper %s is missing from enum bpf_func_id" % name)
2960a0d55efSEyal Birger
297ce3e44a0SAndrii Nakryiko            if name in seen_helpers:
298ce3e44a0SAndrii Nakryiko                if last_helper != name:
299ce3e44a0SAndrii Nakryiko                    raise Exception("Helper %s has multiple descriptions which are not grouped together" % name)
300ce3e44a0SAndrii Nakryiko                continue
301ce3e44a0SAndrii Nakryiko
3020a0d55efSEyal Birger            # Enforce current practice of having the descriptions ordered
3030a0d55efSEyal Birger            # by enum value.
304ce3e44a0SAndrii Nakryiko            if enum_pos != i:
305ce3e44a0SAndrii 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))
306ce3e44a0SAndrii Nakryiko            if enum_val in seen_enum_vals:
307ce3e44a0SAndrii Nakryiko                raise Exception("Helper %s has duplicated value %d" % (name, enum_val))
308ce3e44a0SAndrii Nakryiko
3090a0d55efSEyal Birger            seen_helpers.add(name)
310ce3e44a0SAndrii Nakryiko            last_helper = name
311ce3e44a0SAndrii Nakryiko            seen_enum_vals.add(enum_val)
3120a0d55efSEyal Birger
3130a0d55efSEyal Birger            helper.enum_val = enum_val
314ce3e44a0SAndrii Nakryiko            i += 1
3150a0d55efSEyal Birger
316a67882a2SJoe Stringer    def run(self):
3170ba3929eSUsama Arif        self.parse_desc_syscall()
3180ba3929eSUsama Arif        self.parse_enum_syscall()
31971a3cdf8SUsama Arif        self.parse_desc_helpers()
32071a3cdf8SUsama Arif        self.parse_define_helpers()
321ce3e44a0SAndrii Nakryiko        self.validate_helpers()
322923a932cSJoe Stringer        self.reader.close()
323923a932cSJoe Stringer
324923a932cSJoe Stringer###############################################################################
325923a932cSJoe Stringer
326923a932cSJoe Stringerclass Printer(object):
327923a932cSJoe Stringer    """
328923a932cSJoe Stringer    A generic class for printers. Printers should be created with an array of
329923a932cSJoe Stringer    Helper objects, and implement a way to print them in the desired fashion.
330923a932cSJoe Stringer    @parser: A HeaderParser with objects to print to standard output
331923a932cSJoe Stringer    """
332923a932cSJoe Stringer    def __init__(self, parser):
333923a932cSJoe Stringer        self.parser = parser
334923a932cSJoe Stringer        self.elements = []
335923a932cSJoe Stringer
336923a932cSJoe Stringer    def print_header(self):
337923a932cSJoe Stringer        pass
338923a932cSJoe Stringer
339923a932cSJoe Stringer    def print_footer(self):
340923a932cSJoe Stringer        pass
341923a932cSJoe Stringer
342923a932cSJoe Stringer    def print_one(self, helper):
343923a932cSJoe Stringer        pass
344923a932cSJoe Stringer
345923a932cSJoe Stringer    def print_all(self):
346923a932cSJoe Stringer        self.print_header()
347923a932cSJoe Stringer        for elem in self.elements:
348923a932cSJoe Stringer            self.print_one(elem)
349923a932cSJoe Stringer        self.print_footer()
350923a932cSJoe Stringer
3510ba3929eSUsama Arif    def elem_number_check(self, desc_unique_elem, define_unique_elem, type, instance):
3520ba3929eSUsama Arif        """
3530ba3929eSUsama Arif        Checks the number of helpers/syscalls documented within the header file
3540ba3929eSUsama Arif        description with those defined as part of enum/macro and raise an
3550ba3929eSUsama Arif        Exception if they don't match.
3560ba3929eSUsama Arif        """
3570ba3929eSUsama Arif        nr_desc_unique_elem = len(desc_unique_elem)
3580ba3929eSUsama Arif        nr_define_unique_elem = len(define_unique_elem)
3590ba3929eSUsama Arif        if nr_desc_unique_elem != nr_define_unique_elem:
3600ba3929eSUsama Arif            exception_msg = '''
3610ba3929eSUsama ArifThe number of unique %s in description (%d) doesn\'t match the number of unique %s defined in %s (%d)
3620ba3929eSUsama Arif''' % (type, nr_desc_unique_elem, type, instance, nr_define_unique_elem)
3630ba3929eSUsama Arif            if nr_desc_unique_elem < nr_define_unique_elem:
3640ba3929eSUsama Arif                # Function description is parsed until no helper is found (which can be due to
3650ba3929eSUsama Arif                # misformatting). Hence, only print the first missing/misformatted helper/enum.
3660ba3929eSUsama Arif                exception_msg += '''
3670ba3929eSUsama ArifThe description for %s is not present or formatted correctly.
3680ba3929eSUsama Arif''' % (define_unique_elem[nr_desc_unique_elem])
3690ba3929eSUsama Arif            raise Exception(exception_msg)
370923a932cSJoe Stringer
371923a932cSJoe Stringerclass PrinterRST(Printer):
372923a932cSJoe Stringer    """
373923a932cSJoe Stringer    A generic class for printers that print ReStructured Text. Printers should
374923a932cSJoe Stringer    be created with a HeaderParser object, and implement a way to print API
375923a932cSJoe Stringer    elements in the desired fashion.
376923a932cSJoe Stringer    @parser: A HeaderParser with objects to print to standard output
377923a932cSJoe Stringer    """
378923a932cSJoe Stringer    def __init__(self, parser):
379923a932cSJoe Stringer        self.parser = parser
380923a932cSJoe Stringer
381923a932cSJoe Stringer    def print_license(self):
382923a932cSJoe Stringer        license = '''\
383923a932cSJoe Stringer.. Copyright (C) All BPF authors and contributors from 2014 to present.
384923a932cSJoe Stringer.. See git log include/uapi/linux/bpf.h in kernel tree for details.
385923a932cSJoe Stringer..
3865cb62b75SAlejandro Colomar.. SPDX-License-Identifier: Linux-man-pages-copyleft
387923a932cSJoe Stringer..
388923a932cSJoe Stringer.. Please do not edit this file. It was generated from the documentation
389923a932cSJoe Stringer.. located in file include/uapi/linux/bpf.h of the Linux kernel sources
390923a932cSJoe Stringer.. (helpers description), and from scripts/bpf_doc.py in the same
391923a932cSJoe Stringer.. repository (header and footer).
392923a932cSJoe Stringer'''
393923a932cSJoe Stringer        print(license)
394923a932cSJoe Stringer
395923a932cSJoe Stringer    def print_elem(self, elem):
396923a932cSJoe Stringer        if (elem.desc):
397923a932cSJoe Stringer            print('\tDescription')
398923a932cSJoe Stringer            # Do not strip all newline characters: formatted code at the end of
399923a932cSJoe Stringer            # a section must be followed by a blank line.
400923a932cSJoe Stringer            for line in re.sub('\n$', '', elem.desc, count=1).split('\n'):
401923a932cSJoe Stringer                print('{}{}'.format('\t\t' if line else '', line))
402923a932cSJoe Stringer
403923a932cSJoe Stringer        if (elem.ret):
404923a932cSJoe Stringer            print('\tReturn')
405923a932cSJoe Stringer            for line in elem.ret.rstrip().split('\n'):
406923a932cSJoe Stringer                print('{}{}'.format('\t\t' if line else '', line))
407923a932cSJoe Stringer
408923a932cSJoe Stringer        print('')
409923a932cSJoe Stringer
410fd0a38f9SQuentin Monnet    def get_kernel_version(self):
411fd0a38f9SQuentin Monnet        try:
412fd0a38f9SQuentin Monnet            version = subprocess.run(['git', 'describe'], cwd=linuxRoot,
413fd0a38f9SQuentin Monnet                                     capture_output=True, check=True)
414fd0a38f9SQuentin Monnet            version = version.stdout.decode().rstrip()
415fd0a38f9SQuentin Monnet        except:
416fd0a38f9SQuentin Monnet            try:
417*54d38a5cSHangbin Liu                version = subprocess.run(['make', '-s', '--no-print-directory', 'kernelversion'],
418*54d38a5cSHangbin Liu                                         cwd=linuxRoot, capture_output=True, check=True)
419fd0a38f9SQuentin Monnet                version = version.stdout.decode().rstrip()
420fd0a38f9SQuentin Monnet            except:
421fd0a38f9SQuentin Monnet                return 'Linux'
422fd0a38f9SQuentin Monnet        return 'Linux {version}'.format(version=version)
423fd0a38f9SQuentin Monnet
42492ec1cc3SQuentin Monnet    def get_last_doc_update(self, delimiter):
42592ec1cc3SQuentin Monnet        try:
42692ec1cc3SQuentin Monnet            cmd = ['git', 'log', '-1', '--pretty=format:%cs', '--no-patch',
42792ec1cc3SQuentin Monnet                   '-L',
428121fd33bSVishal Chourasia                   '/{}/,/\\*\\//:include/uapi/linux/bpf.h'.format(delimiter)]
42992ec1cc3SQuentin Monnet            date = subprocess.run(cmd, cwd=linuxRoot,
43092ec1cc3SQuentin Monnet                                  capture_output=True, check=True)
43192ec1cc3SQuentin Monnet            return date.stdout.decode().rstrip()
43292ec1cc3SQuentin Monnet        except:
43392ec1cc3SQuentin Monnet            return ''
43492ec1cc3SQuentin Monnet
435923a932cSJoe Stringerclass PrinterHelpersRST(PrinterRST):
436923a932cSJoe Stringer    """
437923a932cSJoe Stringer    A printer for dumping collected information about helpers as a ReStructured
438923a932cSJoe Stringer    Text page compatible with the rst2man program, which can be used to
439923a932cSJoe Stringer    generate a manual page for the helpers.
440923a932cSJoe Stringer    @parser: A HeaderParser with Helper objects to print to standard output
441923a932cSJoe Stringer    """
442923a932cSJoe Stringer    def __init__(self, parser):
443923a932cSJoe Stringer        self.elements = parser.helpers
4448a76145aSAndrii Nakryiko        self.elem_number_check(parser.desc_unique_helpers, parser.define_unique_helpers, 'helper', '___BPF_FUNC_MAPPER')
445923a932cSJoe Stringer
446923a932cSJoe Stringer    def print_header(self):
447923a932cSJoe Stringer        header = '''\
448923a932cSJoe Stringer===========
449923a932cSJoe StringerBPF-HELPERS
450923a932cSJoe Stringer===========
451923a932cSJoe Stringer-------------------------------------------------------------------------------
452923a932cSJoe Stringerlist of eBPF helper functions
453923a932cSJoe Stringer-------------------------------------------------------------------------------
454923a932cSJoe Stringer
455923a932cSJoe Stringer:Manual section: 7
456fd0a38f9SQuentin Monnet:Version: {version}
45792ec1cc3SQuentin Monnet{date_field}{date}
458923a932cSJoe Stringer
459923a932cSJoe StringerDESCRIPTION
460923a932cSJoe Stringer===========
461923a932cSJoe Stringer
462923a932cSJoe StringerThe extended Berkeley Packet Filter (eBPF) subsystem consists in programs
463923a932cSJoe Stringerwritten in a pseudo-assembly language, then attached to one of the several
464923a932cSJoe Stringerkernel hooks and run in reaction of specific events. This framework differs
465923a932cSJoe Stringerfrom the older, "classic" BPF (or "cBPF") in several aspects, one of them being
466923a932cSJoe Stringerthe ability to call special functions (or "helpers") from within a program.
467923a932cSJoe StringerThese functions are restricted to a white-list of helpers defined in the
468923a932cSJoe Stringerkernel.
469923a932cSJoe Stringer
470923a932cSJoe StringerThese helpers are used by eBPF programs to interact with the system, or with
471923a932cSJoe Stringerthe context in which they work. For instance, they can be used to print
472923a932cSJoe Stringerdebugging messages, to get the time since the system was booted, to interact
473923a932cSJoe Stringerwith eBPF maps, or to manipulate network packets. Since there are several eBPF
474923a932cSJoe Stringerprogram types, and that they do not run in the same context, each program type
475923a932cSJoe Stringercan only call a subset of those helpers.
476923a932cSJoe Stringer
477923a932cSJoe StringerDue to eBPF conventions, a helper can not have more than five arguments.
478923a932cSJoe Stringer
479923a932cSJoe StringerInternally, eBPF programs call directly into the compiled helper functions
480923a932cSJoe Stringerwithout requiring any foreign-function interface. As a result, calling helpers
481923a932cSJoe Stringerintroduces no overhead, thus offering excellent performance.
482923a932cSJoe Stringer
483923a932cSJoe StringerThis document is an attempt to list and document the helpers available to eBPF
484923a932cSJoe Stringerdevelopers. They are sorted by chronological order (the oldest helpers in the
485923a932cSJoe Stringerkernel at the top).
486923a932cSJoe Stringer
487923a932cSJoe StringerHELPERS
488923a932cSJoe Stringer=======
489923a932cSJoe Stringer'''
490fd0a38f9SQuentin Monnet        kernelVersion = self.get_kernel_version()
49192ec1cc3SQuentin Monnet        lastUpdate = self.get_last_doc_update(helpersDocStart)
492fd0a38f9SQuentin Monnet
493923a932cSJoe Stringer        PrinterRST.print_license(self)
49492ec1cc3SQuentin Monnet        print(header.format(version=kernelVersion,
49592ec1cc3SQuentin Monnet                            date_field = ':Date: ' if lastUpdate else '',
49692ec1cc3SQuentin Monnet                            date=lastUpdate))
497923a932cSJoe Stringer
498923a932cSJoe Stringer    def print_footer(self):
499923a932cSJoe Stringer        footer = '''
500923a932cSJoe StringerEXAMPLES
501923a932cSJoe Stringer========
502923a932cSJoe Stringer
503923a932cSJoe StringerExample usage for most of the eBPF helpers listed in this manual page are
504923a932cSJoe Stringeravailable within the Linux kernel sources, at the following locations:
505923a932cSJoe Stringer
506923a932cSJoe Stringer* *samples/bpf/*
507923a932cSJoe Stringer* *tools/testing/selftests/bpf/*
508923a932cSJoe Stringer
509923a932cSJoe StringerLICENSE
510923a932cSJoe Stringer=======
511923a932cSJoe Stringer
512923a932cSJoe StringereBPF programs can have an associated license, passed along with the bytecode
513923a932cSJoe Stringerinstructions to the kernel when the programs are loaded. The format for that
514923a932cSJoe Stringerstring is identical to the one in use for kernel modules (Dual licenses, such
515923a932cSJoe Stringeras "Dual BSD/GPL", may be used). Some helper functions are only accessible to
516239b85a9SGianmarco Lusvardiprograms that are compatible with the GNU General Public License (GNU GPL).
517923a932cSJoe Stringer
518923a932cSJoe StringerIn order to use such helpers, the eBPF program must be loaded with the correct
519121fd33bSVishal Chourasialicense string passed (via **attr**) to the **bpf**\\ () system call, and this
520923a932cSJoe Stringergenerally translates into the C source code of the program containing a line
521923a932cSJoe Stringersimilar to the following:
522923a932cSJoe Stringer
523923a932cSJoe Stringer::
524923a932cSJoe Stringer
525923a932cSJoe Stringer	char ____license[] __attribute__((section("license"), used)) = "GPL";
526923a932cSJoe Stringer
527923a932cSJoe StringerIMPLEMENTATION
528923a932cSJoe Stringer==============
529923a932cSJoe Stringer
530923a932cSJoe StringerThis manual page is an effort to document the existing eBPF helper functions.
531923a932cSJoe StringerBut as of this writing, the BPF sub-system is under heavy development. New eBPF
532923a932cSJoe Stringerprogram or map types are added, along with new helper functions. Some helpers
533923a932cSJoe Stringerare occasionally made available for additional program types. So in spite of
534923a932cSJoe Stringerthe efforts of the community, this page might not be up-to-date. If you want to
535923a932cSJoe Stringercheck by yourself what helper functions exist in your kernel, or what types of
536923a932cSJoe Stringerprograms they can support, here are some files among the kernel tree that you
537923a932cSJoe Stringermay be interested in:
538923a932cSJoe Stringer
539923a932cSJoe Stringer* *include/uapi/linux/bpf.h* is the main BPF header. It contains the full list
540923a932cSJoe Stringer  of all helper functions, as well as many other BPF definitions including most
541923a932cSJoe Stringer  of the flags, structs or constants used by the helpers.
542923a932cSJoe Stringer* *net/core/filter.c* contains the definition of most network-related helper
543923a932cSJoe Stringer  functions, and the list of program types from which they can be used.
544923a932cSJoe Stringer* *kernel/trace/bpf_trace.c* is the equivalent for most tracing program-related
545923a932cSJoe Stringer  helpers.
546923a932cSJoe Stringer* *kernel/bpf/verifier.c* contains the functions used to check that valid types
547923a932cSJoe Stringer  of eBPF maps are used with a given helper function.
548923a932cSJoe Stringer* *kernel/bpf/* directory contains other files in which additional helpers are
549923a932cSJoe Stringer  defined (for cgroups, sockmaps, etc.).
550923a932cSJoe Stringer* The bpftool utility can be used to probe the availability of helper functions
551923a932cSJoe Stringer  on the system (as well as supported program and map types, and a number of
552923a932cSJoe Stringer  other parameters). To do so, run **bpftool feature probe** (see
553121fd33bSVishal Chourasia  **bpftool-feature**\\ (8) for details). Add the **unprivileged** keyword to
554923a932cSJoe Stringer  list features available to unprivileged users.
555923a932cSJoe Stringer
556923a932cSJoe StringerCompatibility between helper functions and program types can generally be found
557923a932cSJoe Stringerin the files where helper functions are defined. Look for the **struct
558923a932cSJoe Stringerbpf_func_proto** objects and for functions returning them: these functions
559923a932cSJoe Stringercontain a list of helpers that a given program type can call. Note that the
560923a932cSJoe Stringer**default:** label of the **switch ... case** used to filter helpers can call
561923a932cSJoe Stringerother functions, themselves allowing access to additional helpers. The
562923a932cSJoe Stringerrequirement for GPL license is also in those **struct bpf_func_proto**.
563923a932cSJoe Stringer
564923a932cSJoe StringerCompatibility between helper functions and map types can be found in the
565121fd33bSVishal Chourasia**check_map_func_compatibility**\\ () function in file *kernel/bpf/verifier.c*.
566923a932cSJoe Stringer
567923a932cSJoe StringerHelper functions that invalidate the checks on **data** and **data_end**
568923a932cSJoe Stringerpointers for network processing are listed in function
569121fd33bSVishal Chourasia**bpf_helper_changes_pkt_data**\\ () in file *net/core/filter.c*.
570923a932cSJoe Stringer
571923a932cSJoe StringerSEE ALSO
572923a932cSJoe Stringer========
573923a932cSJoe Stringer
574121fd33bSVishal Chourasia**bpf**\\ (2),
575121fd33bSVishal Chourasia**bpftool**\\ (8),
576121fd33bSVishal Chourasia**cgroups**\\ (7),
577121fd33bSVishal Chourasia**ip**\\ (8),
578121fd33bSVishal Chourasia**perf_event_open**\\ (2),
579121fd33bSVishal Chourasia**sendmsg**\\ (2),
580121fd33bSVishal Chourasia**socket**\\ (7),
581121fd33bSVishal Chourasia**tc-bpf**\\ (8)'''
582923a932cSJoe Stringer        print(footer)
583923a932cSJoe Stringer
584923a932cSJoe Stringer    def print_proto(self, helper):
585923a932cSJoe Stringer        """
586923a932cSJoe Stringer        Format function protocol with bold and italics markers. This makes RST
587923a932cSJoe Stringer        file less readable, but gives nice results in the manual page.
588923a932cSJoe Stringer        """
589923a932cSJoe Stringer        proto = helper.proto_break_down()
590923a932cSJoe Stringer
591923a932cSJoe Stringer        print('**%s %s%s(' % (proto['ret_type'],
592923a932cSJoe Stringer                              proto['ret_star'].replace('*', '\\*'),
593923a932cSJoe Stringer                              proto['name']),
594923a932cSJoe Stringer              end='')
595923a932cSJoe Stringer
596923a932cSJoe Stringer        comma = ''
597923a932cSJoe Stringer        for a in proto['args']:
598923a932cSJoe Stringer            one_arg = '{}{}'.format(comma, a['type'])
599923a932cSJoe Stringer            if a['name']:
600923a932cSJoe Stringer                if a['star']:
601121fd33bSVishal Chourasia                    one_arg += ' {}**\\ '.format(a['star'].replace('*', '\\*'))
602923a932cSJoe Stringer                else:
603923a932cSJoe Stringer                    one_arg += '** '
604923a932cSJoe Stringer                one_arg += '*{}*\\ **'.format(a['name'])
605923a932cSJoe Stringer            comma = ', '
606923a932cSJoe Stringer            print(one_arg, end='')
607923a932cSJoe Stringer
608923a932cSJoe Stringer        print(')**')
609923a932cSJoe Stringer
610923a932cSJoe Stringer    def print_one(self, helper):
611923a932cSJoe Stringer        self.print_proto(helper)
612923a932cSJoe Stringer        self.print_elem(helper)
613923a932cSJoe Stringer
614923a932cSJoe Stringer
615a67882a2SJoe Stringerclass PrinterSyscallRST(PrinterRST):
616a67882a2SJoe Stringer    """
617a67882a2SJoe Stringer    A printer for dumping collected information about the syscall API as a
618a67882a2SJoe Stringer    ReStructured Text page compatible with the rst2man program, which can be
619a67882a2SJoe Stringer    used to generate a manual page for the syscall.
620a67882a2SJoe Stringer    @parser: A HeaderParser with APIElement objects to print to standard
621a67882a2SJoe Stringer             output
622a67882a2SJoe Stringer    """
623a67882a2SJoe Stringer    def __init__(self, parser):
624a67882a2SJoe Stringer        self.elements = parser.commands
6250ba3929eSUsama Arif        self.elem_number_check(parser.desc_syscalls, parser.enum_syscalls, 'syscall', 'bpf_cmd')
626a67882a2SJoe Stringer
627a67882a2SJoe Stringer    def print_header(self):
628a67882a2SJoe Stringer        header = '''\
629a67882a2SJoe Stringer===
630a67882a2SJoe Stringerbpf
631a67882a2SJoe Stringer===
632a67882a2SJoe Stringer-------------------------------------------------------------------------------
633a67882a2SJoe StringerPerform a command on an extended BPF object
634a67882a2SJoe Stringer-------------------------------------------------------------------------------
635a67882a2SJoe Stringer
636a67882a2SJoe Stringer:Manual section: 2
637a67882a2SJoe Stringer
638a67882a2SJoe StringerCOMMANDS
639a67882a2SJoe Stringer========
640a67882a2SJoe Stringer'''
641a67882a2SJoe Stringer        PrinterRST.print_license(self)
642a67882a2SJoe Stringer        print(header)
643a67882a2SJoe Stringer
644a67882a2SJoe Stringer    def print_one(self, command):
645a67882a2SJoe Stringer        print('**%s**' % (command.proto))
646a67882a2SJoe Stringer        self.print_elem(command)
647923a932cSJoe Stringer
648923a932cSJoe Stringer
649923a932cSJoe Stringerclass PrinterHelpers(Printer):
650923a932cSJoe Stringer    """
651923a932cSJoe Stringer    A printer for dumping collected information about helpers as C header to
652923a932cSJoe Stringer    be included from BPF program.
653923a932cSJoe Stringer    @parser: A HeaderParser with Helper objects to print to standard output
654923a932cSJoe Stringer    """
655923a932cSJoe Stringer    def __init__(self, parser):
656923a932cSJoe Stringer        self.elements = parser.helpers
6578a76145aSAndrii Nakryiko        self.elem_number_check(parser.desc_unique_helpers, parser.define_unique_helpers, 'helper', '___BPF_FUNC_MAPPER')
658923a932cSJoe Stringer
659923a932cSJoe Stringer    type_fwds = [
660923a932cSJoe Stringer            'struct bpf_fib_lookup',
661923a932cSJoe Stringer            'struct bpf_sk_lookup',
662923a932cSJoe Stringer            'struct bpf_perf_event_data',
663923a932cSJoe Stringer            'struct bpf_perf_event_value',
664923a932cSJoe Stringer            'struct bpf_pidns_info',
665923a932cSJoe Stringer            'struct bpf_redir_neigh',
666923a932cSJoe Stringer            'struct bpf_sock',
667923a932cSJoe Stringer            'struct bpf_sock_addr',
668923a932cSJoe Stringer            'struct bpf_sock_ops',
669923a932cSJoe Stringer            'struct bpf_sock_tuple',
670923a932cSJoe Stringer            'struct bpf_spin_lock',
671923a932cSJoe Stringer            'struct bpf_sysctl',
672923a932cSJoe Stringer            'struct bpf_tcp_sock',
673923a932cSJoe Stringer            'struct bpf_tunnel_key',
674923a932cSJoe Stringer            'struct bpf_xfrm_state',
675923a932cSJoe Stringer            'struct linux_binprm',
676923a932cSJoe Stringer            'struct pt_regs',
677923a932cSJoe Stringer            'struct sk_reuseport_md',
678923a932cSJoe Stringer            'struct sockaddr',
679923a932cSJoe Stringer            'struct tcphdr',
680923a932cSJoe Stringer            'struct seq_file',
681923a932cSJoe Stringer            'struct tcp6_sock',
682923a932cSJoe Stringer            'struct tcp_sock',
683923a932cSJoe Stringer            'struct tcp_timewait_sock',
684923a932cSJoe Stringer            'struct tcp_request_sock',
685923a932cSJoe Stringer            'struct udp6_sock',
6869eeb3aa3SHengqi Chen            'struct unix_sock',
687923a932cSJoe Stringer            'struct task_struct',
688c4bcfb38SYonghong Song            'struct cgroup',
689923a932cSJoe Stringer
690923a932cSJoe Stringer            'struct __sk_buff',
691923a932cSJoe Stringer            'struct sk_msg_md',
692923a932cSJoe Stringer            'struct xdp_md',
693923a932cSJoe Stringer            'struct path',
694923a932cSJoe Stringer            'struct btf_ptr',
695923a932cSJoe Stringer            'struct inode',
696923a932cSJoe Stringer            'struct socket',
697923a932cSJoe Stringer            'struct file',
698b00628b1SAlexei Starovoitov            'struct bpf_timer',
6993bc253c2SGeliang Tang            'struct mptcp_sock',
70097e03f52SJoanne Koong            'struct bpf_dynptr',
70133bf9885SMaxim Mikityanskiy            'struct iphdr',
70233bf9885SMaxim Mikityanskiy            'struct ipv6hdr',
703923a932cSJoe Stringer    ]
704923a932cSJoe Stringer    known_types = {
705923a932cSJoe Stringer            '...',
706923a932cSJoe Stringer            'void',
707923a932cSJoe Stringer            'const void',
708923a932cSJoe Stringer            'char',
709923a932cSJoe Stringer            'const char',
710923a932cSJoe Stringer            'int',
711923a932cSJoe Stringer            'long',
712923a932cSJoe Stringer            'unsigned long',
713923a932cSJoe Stringer
714923a932cSJoe Stringer            '__be16',
715923a932cSJoe Stringer            '__be32',
716923a932cSJoe Stringer            '__wsum',
717923a932cSJoe Stringer
718923a932cSJoe Stringer            'struct bpf_fib_lookup',
719923a932cSJoe Stringer            'struct bpf_perf_event_data',
720923a932cSJoe Stringer            'struct bpf_perf_event_value',
721923a932cSJoe Stringer            'struct bpf_pidns_info',
722923a932cSJoe Stringer            'struct bpf_redir_neigh',
723923a932cSJoe Stringer            'struct bpf_sk_lookup',
724923a932cSJoe Stringer            'struct bpf_sock',
725923a932cSJoe Stringer            'struct bpf_sock_addr',
726923a932cSJoe Stringer            'struct bpf_sock_ops',
727923a932cSJoe Stringer            'struct bpf_sock_tuple',
728923a932cSJoe Stringer            'struct bpf_spin_lock',
729923a932cSJoe Stringer            'struct bpf_sysctl',
730923a932cSJoe Stringer            'struct bpf_tcp_sock',
731923a932cSJoe Stringer            'struct bpf_tunnel_key',
732923a932cSJoe Stringer            'struct bpf_xfrm_state',
733923a932cSJoe Stringer            'struct linux_binprm',
734923a932cSJoe Stringer            'struct pt_regs',
735923a932cSJoe Stringer            'struct sk_reuseport_md',
736923a932cSJoe Stringer            'struct sockaddr',
737923a932cSJoe Stringer            'struct tcphdr',
738923a932cSJoe Stringer            'struct seq_file',
739923a932cSJoe Stringer            'struct tcp6_sock',
740923a932cSJoe Stringer            'struct tcp_sock',
741923a932cSJoe Stringer            'struct tcp_timewait_sock',
742923a932cSJoe Stringer            'struct tcp_request_sock',
743923a932cSJoe Stringer            'struct udp6_sock',
7449eeb3aa3SHengqi Chen            'struct unix_sock',
745923a932cSJoe Stringer            'struct task_struct',
746c4bcfb38SYonghong Song            'struct cgroup',
747923a932cSJoe Stringer            'struct path',
748923a932cSJoe Stringer            'struct btf_ptr',
749923a932cSJoe Stringer            'struct inode',
750923a932cSJoe Stringer            'struct socket',
751923a932cSJoe Stringer            'struct file',
752b00628b1SAlexei Starovoitov            'struct bpf_timer',
7533bc253c2SGeliang Tang            'struct mptcp_sock',
75497e03f52SJoanne Koong            'struct bpf_dynptr',
75527060531SKumar Kartikeya Dwivedi            'const struct bpf_dynptr',
75633bf9885SMaxim Mikityanskiy            'struct iphdr',
75733bf9885SMaxim Mikityanskiy            'struct ipv6hdr',
758923a932cSJoe Stringer    }
759923a932cSJoe Stringer    mapped_types = {
760923a932cSJoe Stringer            'u8': '__u8',
761923a932cSJoe Stringer            'u16': '__u16',
762923a932cSJoe Stringer            'u32': '__u32',
763923a932cSJoe Stringer            'u64': '__u64',
764923a932cSJoe Stringer            's8': '__s8',
765923a932cSJoe Stringer            's16': '__s16',
766923a932cSJoe Stringer            's32': '__s32',
767923a932cSJoe Stringer            's64': '__s64',
768923a932cSJoe Stringer            'size_t': 'unsigned long',
769923a932cSJoe Stringer            'struct bpf_map': 'void',
770923a932cSJoe Stringer            'struct sk_buff': 'struct __sk_buff',
771923a932cSJoe Stringer            'const struct sk_buff': 'const struct __sk_buff',
772923a932cSJoe Stringer            'struct sk_msg_buff': 'struct sk_msg_md',
773923a932cSJoe Stringer            'struct xdp_buff': 'struct xdp_md',
774923a932cSJoe Stringer    }
775923a932cSJoe Stringer    # Helpers overloaded for different context types.
776923a932cSJoe Stringer    overloaded_helpers = [
777923a932cSJoe Stringer        'bpf_get_socket_cookie',
778923a932cSJoe Stringer        'bpf_sk_assign',
779923a932cSJoe Stringer    ]
780923a932cSJoe Stringer
781923a932cSJoe Stringer    def print_header(self):
782923a932cSJoe Stringer        header = '''\
783923a932cSJoe Stringer/* This is auto-generated file. See bpf_doc.py for details. */
784923a932cSJoe Stringer
785923a932cSJoe Stringer/* Forward declarations of BPF structs */'''
786923a932cSJoe Stringer
787923a932cSJoe Stringer        print(header)
788923a932cSJoe Stringer        for fwd in self.type_fwds:
789923a932cSJoe Stringer            print('%s;' % fwd)
790923a932cSJoe Stringer        print('')
791923a932cSJoe Stringer
792923a932cSJoe Stringer    def print_footer(self):
793923a932cSJoe Stringer        footer = ''
794923a932cSJoe Stringer        print(footer)
795923a932cSJoe Stringer
796923a932cSJoe Stringer    def map_type(self, t):
797923a932cSJoe Stringer        if t in self.known_types:
798923a932cSJoe Stringer            return t
799923a932cSJoe Stringer        if t in self.mapped_types:
800923a932cSJoe Stringer            return self.mapped_types[t]
801923a932cSJoe Stringer        print("Unrecognized type '%s', please add it to known types!" % t,
802923a932cSJoe Stringer              file=sys.stderr)
803923a932cSJoe Stringer        sys.exit(1)
804923a932cSJoe Stringer
805923a932cSJoe Stringer    seen_helpers = set()
806923a932cSJoe Stringer
807923a932cSJoe Stringer    def print_one(self, helper):
808923a932cSJoe Stringer        proto = helper.proto_break_down()
809923a932cSJoe Stringer
810923a932cSJoe Stringer        if proto['name'] in self.seen_helpers:
811923a932cSJoe Stringer            return
812923a932cSJoe Stringer        self.seen_helpers.add(proto['name'])
813923a932cSJoe Stringer
814923a932cSJoe Stringer        print('/*')
815923a932cSJoe Stringer        print(" * %s" % proto['name'])
816923a932cSJoe Stringer        print(" *")
817923a932cSJoe Stringer        if (helper.desc):
818923a932cSJoe Stringer            # Do not strip all newline characters: formatted code at the end of
819923a932cSJoe Stringer            # a section must be followed by a blank line.
820923a932cSJoe Stringer            for line in re.sub('\n$', '', helper.desc, count=1).split('\n'):
821923a932cSJoe Stringer                print(' *{}{}'.format(' \t' if line else '', line))
822923a932cSJoe Stringer
823923a932cSJoe Stringer        if (helper.ret):
824923a932cSJoe Stringer            print(' *')
825923a932cSJoe Stringer            print(' * Returns')
826923a932cSJoe Stringer            for line in helper.ret.rstrip().split('\n'):
827923a932cSJoe Stringer                print(' *{}{}'.format(' \t' if line else '', line))
828923a932cSJoe Stringer
829923a932cSJoe Stringer        print(' */')
830923a932cSJoe Stringer        print('static %s %s(*%s)(' % (self.map_type(proto['ret_type']),
831923a932cSJoe Stringer                                      proto['ret_star'], proto['name']), end='')
832923a932cSJoe Stringer        comma = ''
833923a932cSJoe Stringer        for i, a in enumerate(proto['args']):
834923a932cSJoe Stringer            t = a['type']
835923a932cSJoe Stringer            n = a['name']
836923a932cSJoe Stringer            if proto['name'] in self.overloaded_helpers and i == 0:
837923a932cSJoe Stringer                    t = 'void'
838923a932cSJoe Stringer                    n = 'ctx'
839923a932cSJoe Stringer            one_arg = '{}{}'.format(comma, self.map_type(t))
840923a932cSJoe Stringer            if n:
841923a932cSJoe Stringer                if a['star']:
842923a932cSJoe Stringer                    one_arg += ' {}'.format(a['star'])
843923a932cSJoe Stringer                else:
844923a932cSJoe Stringer                    one_arg += ' '
845923a932cSJoe Stringer                one_arg += '{}'.format(n)
846923a932cSJoe Stringer            comma = ', '
847923a932cSJoe Stringer            print(one_arg, end='')
848923a932cSJoe Stringer
8490a0d55efSEyal Birger        print(') = (void *) %d;' % helper.enum_val)
850923a932cSJoe Stringer        print('')
851923a932cSJoe Stringer
852923a932cSJoe Stringer###############################################################################
853923a932cSJoe Stringer
854923a932cSJoe Stringer# If script is launched from scripts/ from kernel tree and can access
855923a932cSJoe Stringer# ../include/uapi/linux/bpf.h, use it as a default name for the file to parse,
856923a932cSJoe Stringer# otherwise the --filename argument will be required from the command line.
857923a932cSJoe Stringerscript = os.path.abspath(sys.argv[0])
858923a932cSJoe StringerlinuxRoot = os.path.dirname(os.path.dirname(script))
859923a932cSJoe Stringerbpfh = os.path.join(linuxRoot, 'include/uapi/linux/bpf.h')
860923a932cSJoe Stringer
861923a932cSJoe Stringerprinters = {
862923a932cSJoe Stringer        'helpers': PrinterHelpersRST,
863a67882a2SJoe Stringer        'syscall': PrinterSyscallRST,
864923a932cSJoe Stringer}
865923a932cSJoe Stringer
866923a932cSJoe StringerargParser = argparse.ArgumentParser(description="""
867923a932cSJoe StringerParse eBPF header file and generate documentation for the eBPF API.
868923a932cSJoe StringerThe RST-formatted output produced can be turned into a manual page with the
869923a932cSJoe Stringerrst2man utility.
870923a932cSJoe Stringer""")
871923a932cSJoe StringerargParser.add_argument('--header', action='store_true',
872923a932cSJoe Stringer                       help='generate C header file')
873923a932cSJoe Stringerif (os.path.isfile(bpfh)):
874923a932cSJoe Stringer    argParser.add_argument('--filename', help='path to include/uapi/linux/bpf.h',
875923a932cSJoe Stringer                           default=bpfh)
876923a932cSJoe Stringerelse:
877923a932cSJoe Stringer    argParser.add_argument('--filename', help='path to include/uapi/linux/bpf.h')
878923a932cSJoe StringerargParser.add_argument('target', nargs='?', default='helpers',
879923a932cSJoe Stringer                       choices=printers.keys(), help='eBPF API target')
880923a932cSJoe Stringerargs = argParser.parse_args()
881923a932cSJoe Stringer
882923a932cSJoe Stringer# Parse file.
883923a932cSJoe StringerheaderParser = HeaderParser(args.filename)
884923a932cSJoe StringerheaderParser.run()
885923a932cSJoe Stringer
886923a932cSJoe Stringer# Print formatted output to standard output.
887923a932cSJoe Stringerif args.header:
888a67882a2SJoe Stringer    if args.target != 'helpers':
889a67882a2SJoe Stringer        raise NotImplementedError('Only helpers header generation is supported')
890923a932cSJoe Stringer    printer = PrinterHelpers(headerParser)
891923a932cSJoe Stringerelse:
892923a932cSJoe Stringer    printer = printers[args.target](headerParser)
893923a932cSJoe Stringerprinter.print_all()
894