xref: /linux-6.15/scripts/bpf_doc.py (revision 48b13cab)
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