blob: 1c39113e93bdc1fb6dca12a4b24ccea25cf9cbdb [file] [log] [blame]
.. SPDX-License-Identifier: BSD-3-Clause
.. SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
Coding Standard
===============
This document describes the coding rules to follow to contribute to the project.
General
-------
The following coding standard is derived from `MISRA C:2012 Guidelines`_,
`TF-A coding style`_ and `Linux kernel coding style`_ coding standards.
File Encoding
-------------
The source code must use the **UTF-8** character encoding. Comments and
documentation may use non-ASCII characters when required (e.g. Greek letters
used for units) but code itself is still limited to ASCII characters.
Language
--------
The primary language for comments and naming must be International English. In
cases where there is a conflict between the American English and British English
spellings of a word, the American English spelling is used.
Exceptions are made when referring directly to something that does not use
international style, such as the name of a company. In these cases the existing
name should be used as-is.
C Language Standard
-------------------
The C language mode used for |RMM| is *GNU11*. This is the "GNU dialect of ISO
C11", which implies the *ISO C11* standard with GNU extensions.
Both GCC and Clang compilers have support for *GNU11* mode, though
Clang does lack support for a small number of GNU extensions. These
missing extensions are rarely used, however, and should not pose a problem.
Length
------
- Each file, function and scopes should have a logical uniting theme.
No length limit is set for a file.
- A function should be 24 lines maximum.
This will not be enforced, any function being longer should trigger a
discussion during the review process.
- The recommended maximum line length is 80 characters, except for string literals
as it would make any search for it more difficult. A maximum length of 100
characters is enforced by the coding guidelines static check.
- A variable should not be longer than 31 characters.
Although the `C11 specification`_ specifies that the number of signitificant
characters in an identifier is implementation defined it sets the translation
limit to the 31 initial characters.
+--------------+-----------------------------------+
| TYPE | LIMIT |
+==============+===================================+
| function | 24 lines (not enforced) |
+--------------+-----------------------------------+
| line | 100 characters |
+--------------+-----------------------------------+
| identifier | 31 characters |
+--------------+-----------------------------------+
Headers/Footers
---------------
- Include guards:
.. code:: c
#ifndef FILE_NAME_H
#define FILE_NAME_H
<header content>
#endif /* FILE_NAME_H */
- Include statement variant is <>:
.. code:: c
#include <file.h>
- Include files should be alphabetically ordered:
.. code:: c
#include <axxxx.h>
#include <bxxxx.h>
[...]
#include <zxxxx.h>
- If possible, use forward declaration of struct types in public headers.
This will reduce interdependence of header file inclusion.
.. code:: c
#include <axxxx.h>
#include <bxxxx.h>
[...]
/* forward declaration */
struct x;
void foo(struct *x);
Naming conventions
------------------
- Case:
Functions and variables must be in Snake Case
.. code:: c
unsigned int my_snake_case_variable = 0U;
void my_snake_case_function(void)
{
[...]
}
- Local variables should be declared at the top of the closest opening scope
and should be short.
We won't enforce a length, and defining short is difficult, this motto
(from Linux) catches the spirit
+---------------------------------------------------------------------------+
| LOCAL variable names should be short, and to the point. |
| |
| If you have some random integer loop counter, |
| it should probably be called i. |
| |
| Calling it loop_counter is non-productive, |
| if there is no chance of it being mis-understood. |
| |
| Similarly, tmp can be just about any type of variable that is |
| used to hold a temporary value. |
| |
| If you are afraid to mix up your local variable names, |
| you have another problem. |
+---------------------------------------------------------------------------+
.. code:: c
int foo(const int a)
{
int c; /* needed in the function */
c = a; /* MISRA-C rules recommend to not modify arguments variables */
if (c == 42) {
int b; /* needed only in this "if" statment */
b = bar(); /* bar will return an int */
if (b != -1) {
c += b;
}
}
return c;
}
- Use an appropraite prefix for public API of a component. For example,
if the component name is `bar`, then the init API of the component
should be called `bar_init()`.
Indentation
-----------
Use **tabs** for indentation. The use of spaces for indentation is forbidden
except in the case where a term is being indented to a boundary that cannot be
achieved using tabs alone.
Tab spacing should be set to **8 characters**.
Trailing whitespaces or tabulations are not allowed and must be trimmed.
Spacing
-------
Single spacing should be used around most operators, including:
- Arithmetic operators (``+``, ``-``, ``/``, ``*``, ``%``)
- Assignment operators (``=``, ``+=``, etc)
- Boolean operators (``&&``, ``||``)
- Comparison operators (``<``, ``>``, ``==``, etc)
- Shift operators (``>>``, ``<<``)
- Logical operators (``&``, ``|``, etc)
- Flow control (``if``, ``else``, ``switch``, ``while``, ``return``, etc)
No spacing should be used around the following operators
- Cast (``()``)
- Indirection (``*``)
Braces
------
- Use K&R style for statements.
- Function opening braces are on a new line.
- Use braces even for singled line.
.. code:: c
void function(void)
{
/* if statement */
if (my_test) {
do_this();
do_that();
}
/* if/else statement */
if (my_Test) {
do_this();
do_that();
} else {
do_other_this();
}
}
Commenting
----------
Double-slash style of comments (//) is not allowed, below are examples of
correct commenting.
.. code:: c
/*
* This example illustrates the first allowed style for multi-line comments.
*
* Blank lines within multi-lines are allowed when they add clarity or when
* they separate multiple contexts.
*/
.. code:: c
/**************************************************************************
* This is the second allowed style for multi-line comments.
*
* In this style, the first and last lines use asterisks that run the full
* width of the comment at its widest point.
*
* This style can be used for additional emphasis.
*************************************************************************/
.. code:: c
/* Single line comments can use this format */
.. code:: c
/***************************************************************************
* This alternative single-line comment style can also be used for emphasis.
**************************************************************************/
Error return values and Exception handling
------------------------------------------
- Function return type must be explicitly defined.
- Unless specifed otherwise by an official specification, return values must be
used to return success or failure (Standard Posix error codes).
Return an integer if the function is an action or imperative command
Failure: -Exxx (STD posix error codes, unless specified otherwise)
Success: 0
Return a boolean if the function is as predicate
Failure: false
Success: true
- If a function returns error information, then that error information shall
be tested.
Exceptions are allowed for STDLIB functions (memcpy/printf/...) in which case
it must be void casted.
.. code:: c
#define MY_TRANSFORMED_ERROR (-1)
void my_print_function(struct my_struct in_mystruct)
{
long long transformed_a = my_transform_a(in_mystruct.a);
if (transform_a != MY_TRANSFORMED_ERROR) {
(void)printf("STRUCT\n\tfield(a): %ll\n", transformed_a);
} else {
(void)printf("STRUCT\n\tERROR %ll\n", transformed_a);
}
}
Use of asserts and panic
------------------------
Assertions, as a general rule, are only used to catch errors during
development cycles and are removed from production binaries. They are
useful to document pre-conditions for a function or impossible conditions
in code. They are not substitutes for proper error checking and any
expression used to test an assertion must not have a side-effect.
For example,
.. code:: c
assert(--i == 0);
should not be used in code.
Assertions can be used to validate input arguments to an API as long as
the caller and callee are within the same trust boundary.
``panic()`` is used in places wherein it is not possible to continue the
execution of program sensibly. It should be used sparingly within code
and, if possible, instead of panic(), components should return error
back to the caller and the caller can decide on the appropriate action.
This is particularly useful to build resilence to the program wherein
non-functional part of the program can be disabled and, if possible,
other functional aspects of the program can be kept running.
Using COMPILER_ASSERT to check for compile time data errors
-----------------------------------------------------------
Where possible, use the ``COMPILER_ASSERT`` macro to check the validity of
data known at compile time instead of checking validity at runtime, to avoid
unnecessary runtime code.
For example, this can be used to check that the assembler's and compiler's views
of the size of an array is the same.
.. code:: c
#include <utils_def.h>
define MY_STRUCT_SIZE 8 /* Used by assembler source files */
struct my_struct {
uint32_t arg1;
uint32_t arg2;
};
COMPILER_ASSERT(MY_STRUCT_SIZE == sizeof(struct my_struct));
If ``MY_STRUCT_SIZE`` in the above example were wrong then the compiler would
emit an error like this:
::
my_struct.h:10:1: note: in expansion of macro 'COMPILER_ASSERT'
10 | COMPILER_ASSERT(MY_STRUCT_SIZE == sizeof(struct my_struct));
| ^~~~~~~~~~~~~~~
.. note::
For CBMC analysis some of the compile assertions are not valid (for example
due to the missing padding from certain structures, or due to smaller
``GRANULE_SIZE``). In this case the macro ``COMPILER_ASSERT_NO_CBMC`` should
be used which behaves as ``COMPILER_ASSERT`` for regular builds, and always
passes for CBMC build.
Data types, structures and typedefs
-----------------------------------
- Data Types:
The |RMM| codebase should be kept as portable as possible for 64-bits platforms.
To help with this, the following data type usage guidelines should be followed:
- Where possible, use the built-in *C* data types for variable storage (for
example, ``char``, ``int``, ``long long``, etc) instead of the standard *C11*
types. Most code is typically only concerned with the minimum size of the
data stored, which the built-in *C* types guarantee.
- Avoid using the exact-size standard *C11* types in general (for example,
``uint16_t``, ``uint32_t``, ``uint64_t``, etc) since they can prevent the
compiler from making optimizations. There are legitimate uses for them,
for example to represent data of a known structure. When using them in a
structure definition, consider how padding in the structure will work across
architectures.
- Use ``int`` as the default integer type - it's likely to be the fastest on all
systems. Also this can be assumed to be 32-bit as a consequence of the
`Procedure Call Standard for the Arm 64-bit Architecture`_ .
- Avoid use of ``short`` as this may end up being slower than ``int`` in some
systems. If a variable must be exactly 16-bit, use ``int16_t`` or
``uint16_t``.
- ``long`` are defined as LP64 (64-bit), this is guaranteed to be 64-bit.
- Use ``char`` for storing text. Use ``uint8_t`` for storing other 8-bit data.
- Use ``unsigned`` for integers that can never be negative (counts,
indices, sizes, etc). |RMM| intends to comply with MISRA "essential type"
coding rules (10.X), where signed and unsigned types are considered different
essential types. Choosing the correct type will aid this. MISRA static
analysers will pick up any implicit signed/unsigned conversions that may lead
to unexpected behaviour.
- For pointer types:
- If an argument in a function declaration is pointing to a known type then
simply use a pointer to that type (for example: ``struct my_struct *``).
- If a variable (including an argument in a function declaration) is pointing
to a general, memory-mapped address, an array of pointers or another
structure that is likely to require pointer arithmetic then use
``uintptr_t``. This will reduce the amount of casting required in the code.
Avoid using ``unsigned long`` or ``unsigned long long`` for this purpose;
it may work but is less portable.
- Use of ``void *`` is generally discouraged. Although it is useful to
represent pointers to types that are abstracted away from the callers and
has useful implicit cast properties, for the sake of a more uniform code
base, we encourage use of ``uintptr_t`` where possible.
- Avoid pointer arithmetic generally (as this violates MISRA C 2012 rule
18.4) and especially on void pointers (as this is only supported via
language extensions and is considered non-standard). In |RMM|, setting the
``W`` build flag to ``W=3`` enables the *-Wpointer-arith* compiler flag and
this will emit warnings where pointer arithmetic is used.
- Use ``ptrdiff_t`` to compare the difference between 2 pointers.
- Use ``size_t`` when storing the ``sizeof()`` something.
- Use ``ssize_t`` when returning the ``sizeof()`` something from a function
that can also return an error code; the signed type allows for a negative
return code in case of error. This practice should be used sparingly.
- Use ``uint64_t`` to store the contents of an AArch64 register or
represent a 64-bit value. Use of ``unsigned long`` or ``u_register_t``
for these purposes is discouraged.
These guidelines should be updated if additional types are needed.
- Typedefs:
Typedef should be avoided and used only to create opaque types.
An opaque data type is one whose concrete data structure is not publicly
defined. Opaque data types can be used on handles to resources that the caller
is not expected to address directly.
.. code:: c
/* File main.c */
#include <my_lib.h>
int main(void)
{
context_t *context;
int res;
context = my_lib_init();
res = my_lib_compute(context, "2x2");
if (res == -EMYLIB_ERROR) {
return -1
}
return res;
}
.. code:: c
/* File my_lib.h */
#ifndef MY_LIB_H
#define MY_LIB_H
typedef struct my_lib_context {
[...] /* whatever internal private variables you need in my_lib */
} context_t;
#endif /* MY_LIB_H */
Macros and Enums
----------------
- Favor functions over macros.
- Preprocessor macros and enums values are written in all uppercase text.
- A numerical value shall be typed.
.. code:: c
/* Common C usage​ */
#define MY_MACRO 4UL
/* If used in C and ASM (included from a .S file)​ */
#define MY_MACRO UL(4)
- Expressions resulting from the expansion of macro parameters must be enclosed
in parentheses.
- A macro parameter immediately following a # operator mustn't be immediately
followed by a ## operator.
.. code:: c
#define SINGLE_HASH_OP(x) (#x) /* allowed */
#define SINGLE_DOUBLE_HASH_OP(x, y) (x ## y) /* allowed */
#define MIXED_HASH_OP(x, y) (#x ## y) /* not allowed */
- Avoid defining macros that affect the control flow (i.e. avoid using
return/goto in a macro).
- Macro with multiple statements can be enclosed in a do-while block or in a
expression statement.
.. code:: c
int foo(char **b);
#define M1(a, b) \
do { \
if ((a) == 5) { \
foo((b)); \
} \
} while (false)
#define M2(a, b) \
({ \
if ((a) == 5) { \
foo((b)); \
} \
})
int foo(char **b)
{
return 42;
}
int main(int ac, char **av)
{
if (ac == 1) {
M1(ac, av);
} else if (ac == 2) {
M2(ac, av);
} else {
return -1;
}
return ac;
}
Switch statements
-----------------
- Return in a *case* are allowed.
- Fallthrough are allowed as long as they are commented.
- Do not rely on type promotion between the switch type and the case type.
Inline assembly
---------------
- Favor C language over assembly language.
- Document all usage of assembly.
- Do not mix C and ASM in the same file.
Libc functions that are banned or to be used with caution
---------------------------------------------------------
Below is a list of functions that present security risks.
+------------------------+--------------------------------------+
| libc function | Comments |
+========================+======================================+
| ``strcpy, wcscpy``, | use strlcpy instead |
| ``strncpy`` | |
+------------------------+--------------------------------------+
| ``strcat, wcscat``, | use strlcat instead |
| ``strncat`` | |
+------------------------+--------------------------------------+
| ``sprintf, vsprintf`` | use snprintf, vsnprintf |
| | instead |
+------------------------+--------------------------------------+
| ``snprintf`` | if used, ensure result fits in buffer|
| | i.e : snprintf(buf,size...) < size |
+------------------------+--------------------------------------+
| ``vsnprintf`` | if used, inspect va_list match types |
| | specified in format string |
+------------------------+--------------------------------------+
| ``strtok, strtok_r``, | Should not be used |
| ``strsep`` | |
+------------------------+--------------------------------------+
| ``ato*`` | Should not be used |
+------------------------+--------------------------------------+
| ``*toa`` | Should not be used |
+------------------------+--------------------------------------+
The use of above functions are discouraged and will only be allowed
in justified cases after a discussion has been held either on the mailing
list or during patch review and it is agreed that no alternative to their
use is available. The code containing the banned APIs must properly justify
their usage in the comments.
The above restriction does not apply to Third Party IP code inside the ``ext/``
directory.
-----------
.. _`Procedure Call Standard for the Arm 64-bit Architecture`: https://developer.arm.com/docs/ihi0055/latest/
.. _`Linux kernel coding style`: https://www.kernel.org/doc/html/latest/process/coding-style.html
.. _`MISRA C:2012 Guidelines`: https://www.misra.org.uk/Activities/MISRAC/tabid/160/Default.aspx
.. _`TF-A coding style`: https://trustedfirmware-a.readthedocs.io/en/latest/process/coding-style.html
.. _`C11 specification`: https://en.wikipedia.org/wiki/C11_(C_standard_revision)