[PATCH v2 2/8] doc: support kernel-doc for asm functions

Robert Elliott
2022-12-19T18:56:30Z

Support kernel-doc comments in assembly language files for functions
called by C functions.

The comment must include a line containing:
* Prototype: asmlinkage ... rest of C prototype...

and that function name must match the name used in line like:
SYM_FUNC_START(name)

or
SOMETHING name

which is used in a few places in which SYM_FUNC_START is nested.

Signed-off-by: Robert Elliott <elliott@hpe.com>

---
v2 Add documentation for the new kernel-doc Prototype line.
Rebased onto 6.1.0.
Support new SYM_TYPED_FUNC_START macro.
---
Documentation/doc-guide/kernel-doc.rst | 79 ++++++++++++++++++++++++++
scripts/kernel-doc | 49 +++++++++++++++-
2 files changed, 126 insertions(+), 2 deletions(-)

diff --git a/Documentation/doc-guide/kernel-doc.rst b/Documentation/doc-guide/kernel-doc.rst
index 1dcbd7332476..554694a15586 100644
--- a/Documentation/doc-guide/kernel-doc.rst
+++ b/Documentation/doc-guide/kernel-doc.rst
@@ -93,6 +93,9 @@ The brief description following the function name may span multiple lines, and
ends with an argument description, a blank comment line, or the end of the
comment block.

+This may also be used to describe a functions in an assembly language file,
+provided that a Prototype line is also present (see below).
+
Function parameters
~~~~~~~~~~~~~~~~~~~

@@ -171,6 +174,82 @@ named ``Return``.
as a new section heading, which probably won't produce the desired
effect.

+Prototypes
+~~~~~~~~~~
+
+In assembly language files (.S files), functions callable by
+C code are defined with::
+
+ SYM_FUNCTION_START(function_name)
+ assembly language code ...
+
+This does not list the arguments like a C function definition; that
+information is implicit in the assembly language instructions that follow.
+
+To document that usage and how the function should be referenced by
+C code, include the recommended Prototype like this::
+
+ /**
+ * crc_pcl - Calculate CRC32C using x86 CRC32 and PCLMULQDQ instructions
+ * @buffer: address of data (%rdi, bufp macro)
+ * @len: data size (%rsi, len macro)
+ * @crc_init: initial CRC32C value (%rdx, crc_init_arg macro);
+ * only using lower 32 bits
+ *
+ * This function supports 64-bit CPUs.
+ * It loops on 8-byte aligned QWORDs, but also supports unaligned
+ * addresses and all length values.
+ *
+ * Return: CRC32C value (upper 32 bits zero)(%rax)
+ * Prototype: asmlinkage unsigned int crc_pcl(const u8 *buffer,
+ * unsigned int len,
+ * unsigned int crc_init);
+ */
+ SYM_FUNC_START(crc_pcl)
+ assembly language code ...
+
+scripts/kernel-doc ensures that the arguments match those in the
+prototype and that the function name matches everywhere.
+
+Variants of SYM_FUNC_START like SYM_TYPED_FUNC_START and
+SYM_FUNC_START_WEAK are also supprted.
+
+In a few cases, a macro is defined that contains the SYM_FUNC_START()
+macro and code. scripts/kernel-doc recognizes that format as well::
+
+ .macro SHA1_VECTOR_ASM name
+ SYM_FUNC_START(\name)
+ assembly language code ...
+
+ /**
+ * sha1_transform_ssse3 - Calculate SHA1 hash using the x86 SSSE3 feature set
+ * @digest: address of current 20-byte hash value (%rdi, CTX macro)
+ * @data: address of data (%rsi, BUF macro);
+ * data size must be a multiple of 64 bytes
+ * @blocks: number of 64-byte blocks (%rdx, CNT macro)
+ *
+ * This function supports 64-bit CPUs.
+ *
+ * Return: none
+ * Prototype: asmlinkage void sha1_transform_ssse3(u32 *digest, const u8 *data, int blocks)
+ */
+ SHA1_VECTOR_ASM sha1_transform_ssse3
+
+ /**
+ * sha1_transform_avx - Calculate SHA1 hash using the x86 AVX feature set
+ * @digest: address of current 20-byte hash value (%rdi, CTX macro)
+ * @data: address of data (%rsi, BUF macro);
+ * data size must be a multiple of 64 bytes
+ * @blocks: number of 64-byte blocks (%rdx, CNT macro)
+ *
+ * This function supports 64-bit CPUs.
+ *
+ * Return: none
+ * Prototype: asmlinkage void sha1_transform_avx(u32 *digest, const u8 *data, int blocks)
+ */
+ SHA1_VECTOR_ASM sha1_transform_avx
+
+
Structure, union, and enumeration documentation
-----------------------------------------------

diff --git a/scripts/kernel-doc b/scripts/kernel-doc
index 54b0893cae66..e23591d3c78c 100755
--- a/scripts/kernel-doc
+++ b/scripts/kernel-doc
@@ -174,6 +174,7 @@ my %nosymbol_table = ();
my $declaration_start_line;
my ($type, $declaration_name, $return_type);
my ($newsection, $newcontents, $prototype, $brcount, %source_map);
+my %asmprototypes;

if (defined($ENV{'KBUILD_VERBOSE'})) {
$verbose = "$ENV{'KBUILD_VERBOSE'}";
@@ -248,7 +249,7 @@ my $doc_decl = $doc_com . '(\w+)';
# while trying to not match literal block starts like "example::"
#
my $doc_sect = $doc_com .
- '\s*(\@[.\w]+|\@\.\.\.|description|context|returns?|notes?|examples?)\s*:([^:].*)?$';
+ '\s*(\@[.\w]+|\@\.\.\.|description|context|returns?|prototype|notes?|examples?)\s*:([^:].*)?$';
my $doc_content = $doc_com_body . '(.*)';
my $doc_block = $doc_com . 'DOC:\s*(.*)?';
my $doc_inline_start = '^\s*/\*\*\s*$';
@@ -278,6 +279,7 @@ my $section_intro = "Introduction";
my $section = $section_default;
my $section_context = "Context";
my $section_return = "Return";
+my $section_asmprototype = "Prototype";

my $undescribed = "-- undescribed --";

@@ -469,6 +471,13 @@ sub dump_section {
$new_start_line = 0;
}
}
+
+ if ($name eq $section_asmprototype) {
+ # extract the function name for future matching to SYM.*FUNC_START.*(name)
+ # since that doesn't include arguments like a C function call
+ my ($func) = ($contents =~ /^.*\s+(\S+)\(/);
+ $asmprototypes{$func} = $contents;
+ }
}

##
@@ -1865,9 +1874,32 @@ sub syscall_munge() {
sub process_proto_function($$) {
my $x = shift;
my $file = shift;
+ my $funcname;

$x =~ s@\/\/.*$@@gos; # strip C99-style comments to end of line

+ # support asm functions declared with one of these starting in
+ # the first column:
+ # SYM_FUNC_START(name)
+ # SYM_FUNC_START_LOCAL(name)
+ # SYM_FUNC_START_WEAK(name)
+ # SYM_TYPED_FUNC_START(name)
+ # or for nested macros:
+ # SOMESTRING<whitespace>name
+ if ($file =~ /\.S$/) {
+ if ($x =~ /^SYM.*FUNC_START/) {
+ ($funcname) = ($x =~ /^SYM.*FUNC_START.*\((.*)\)/);
+ } elsif ($x =~ /^[A-Za-z0-9_]+\s+[A-Za-z0-9_]+/) {
+ ($funcname) = ($x =~ /^[A-Za-z0-9_]+\s+([A-Za-z0-9_]+)/);
+ }
+ }
+ if (defined $funcname) {
+ $prototype = $asmprototypes{$funcname};
+ dump_function($asmprototypes{$funcname}, $file);
+ reset_state();
+ return;
+ }
+
if ($x =~ m#\s*/\*\s+MACDOC\s*#io || ($x =~ /^#/ && $x !~ /^#\s*define/)) {
# do nothing
}
@@ -2106,6 +2138,8 @@ sub process_body($$) {
$newsection = $section_default;
} elsif ($newsection =~ m/^context$/i) {
$newsection = $section_context;
+ } elsif ($newsection =~ m/^prototype$/i) {
+ $newsection = $section_asmprototype;
} elsif ($newsection =~ m/^returns?$/i) {
$newsection = $section_return;
} elsif ($newsection =~ m/^\@return$/) {
@@ -2156,6 +2190,16 @@ sub process_body($$) {
$contents = "";
$new_start_line = $.;
$state = STATE_BODY;
+ } elsif ($section eq $section_asmprototype) {
+ my ($protoline) = /Prototype:\s+(.+)$/;
+ my ($funcname) = $protoline =~ /Prototype\.*\s+(\S+)\(/;
+
+ $asmprototypes{$funcname} = $protoline;
+ dump_section($file, $section, $contents);
+ $section = $section_default;
+ $contents = "";
+ $new_start_line = $.;
+ $state = STATE_BODY;
} else {
if ($section ne $section_default) {
$state = STATE_BODY_WITH_BLANK_LINE;
@@ -2171,7 +2215,7 @@ sub process_body($$) {
$declaration_purpose =~ s/\s+/ /g;
} else {
my $cont = $1;
- if ($section =~ m/^@/ || $section eq $section_context) {
+ if ($section =~ m/^@/ || $section eq $section_context || $section eq $section_asmprototype) {
if (!defined $leading_space) {
if ($cont =~ m/^(\s+)/) {
$leading_space = $1;
@@ -2307,6 +2351,7 @@ sub process_file($) {
}
# Replace tabs by spaces
while ($_ =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e) {};
+
# Hand this line to the appropriate state handler
if ($state == STATE_NORMAL) {
process_normal();
--
2.38.1