Portuguese (Brazilian)

Adicionando uma Nova Chamada de Sistema

Este documento descreve o que está envolvido na adição de uma nova chamada de sistema (system call) ao kernel Linux, indo além dos conselhos normais de submissão em Documentation/process/submitting-patches.rst.

Alternativas às Chamadas de Sistema

A primeira coisa a se considerar ao adicionar uma nova chamada de sistema é se uma das alternativas poderia ser mais adequada. Embora as chamadas de sistema sejam os pontos de interação mais tradicionais e óbvios entre o espaço do usuário (userspace) e o kernel, existem outras possibilidades -- escolha o que melhor se adapta à sua interface.

  • Se as operações envolvidas puderem ser moldadas para se parecerem com um objeto do tipo arquivo, pode fazer mais sentido criar um novo sistema de arquivos ou dispositivo. Isso também torna mais fácil encapsular a nova funcionalidade em um módulo de kernel, em vez de exigir que ela seja incorporada ao kernel principal.

    • Se a nova funcionalidade envolver operações em que o kernel notifica o espaço do usuário de que algo aconteceu, retornar um novo descritor de arquivo (file descriptor) para o objeto relevante permite que o espaço do usuário use poll/select/epoll para receber essa notificação.

    • No entanto, as operações que não se mapeiam para operações do tipo read(2)/write(2) precisam ser implementadas como requisições ioctl(2), o que pode levar a uma API um tanto quanto opaca.

  • Se você estiver apenas expondo informações do sistema em tempo de execução, um novo nó no sysfs (veja Documentation/filesystems/sysfs.rst) ou no sistema de arquivos /proc pode ser mais apropriado. No entanto, o acesso a esses mecanismos exige que o sistema de arquivos relevante esteja montado, o que pode não ser sempre o caso (por exemplo, em um ambiente com namespaces, sandboxed ou chrooted). Evite adicionar qualquer API ao debugfs, pois este não é considerado uma interface de “produção” para o espaço do usuário.

  • Se a operação for específica para um arquivo ou descritor de arquivo de um determinado objeto, então uma opção de comando adicional para fcntl(2) pode ser mais adequada. Contudo, o fcntl(2) é uma chamada de sistema de multiplexação que oculta muita complexidade, portanto, esta opção é melhor para quando a nova função for intimamente análoga à funcionalidade existente do fcntl(2), ou se a nova funcionalidade for muito simples (por exemplo, obter/definir uma flag simples relacionada a um descritor de arquivo).

  • Se a operação for específica para uma tarefa (task) ou processo específico, então uma opção de comando adicional para prctl(2) pode ser mais apropriada. Assim como no caso do fcntl(2), esta chamada de sistema é um multiplexador complicado, sendo melhor reservá-la para análogos próximos de comandos prctl() existentes ou para obter/definir uma flag simples relacionada a um processo.

Projetando a API: Planejando a Extensibilidade

Uma nova chamada de sistema faz parte da API do kernel e deve ser suportada indefinidamente. Sendo assim, é uma excelente ideia discutir explicitamente a interface na lista de discussão do kernel (LKML), e é crucial planejar extensões futuras para essa interface.

(A tabela de chamadas de sistema está repleta de exemplos históricos onde isso não foi feito, juntamente com as respectivas chamadas de sistema de acompanhamento -- eventfd/eventfd2, dup2/dup3, inotify_init/inotify_init1, pipe/pipe2, renameat/renameat2 -- portanto, aprenda com a história do kernel e planeje as extensões desde o início.)

Para chamadas de sistema mais simples que recebem apenas alguns argumentos, a maneira preferencial de permitir extensibilidade futura é incluir um argumento de flags na chamada de sistema. Para garantir que os programas do espaço do usuário possam usar flags de forma segura entre diferentes versões do kernel, verifique se o valor de flags contém qualquer flag desconhecida e rejeite a chamada de sistema (com EINVAL) se contiver:

if (flags & ~(THING_FLAG1 | THING_FLAG2 | THING_FLAG3))
    return -EINVAL;

(Se nenhum valor de flag for utilizado ainda, verifique se o argumento de flags é zero.)

Para chamadas de sistema mais sofisticadas que envolvem um número maior de argumentos, prefere-se encapsular a maioria dos argumentos em uma estrutura (struct) que é passada por meio de um ponteiro. Esse tipo de estrutura pode lidar com extensões futuras incluindo um argumento de tamanho (size) na própria estrutura:

struct xyzzy_params {
    u32 size; /* o espaço do usuário define p->size = sizeof(struct xyzzy_params) */
    u32 param_1;
    u64 param_2;
    u64 param_3;
};

Desde que qualquer campo adicionado subsequentemente, digamos param_4, seja projetado de forma que um valor zero mantenha o comportamento anterior, isso permitirá lidar com a divergência de versões em ambas as direções:

  • Para lidar com um programa de espaço do usuário mais novo chamando um kernel mais antigo, o código do kernel deve verificar se qualquer memória além do tamanho da estrutura que ele espera está zerada (efetivamente verificando se param_4 == 0).

  • Para lidar com um programa de espaço do usuário mais antigo chamando um kernel mais novo, o código do kernel pode preencher com zero (zero-extend) a instância menor da estrutura (efetivamente definindo param_4 = 0).

Veja perf_event_open(2) e a função perf_copy_attr() (em kernel/events/core.c) para um exemplo desta abordagem.

Projetando a API: Outras Considerações

Se a sua nova chamada de sistema permitir que o espaço do usuário se refira a um objeto do kernel, ela deve usar um descritor de arquivo (file descriptor) como o handle (identificador) para esse objeto -- não invente um novo tipo de handle de objeto para o espaço do usuário quando o kernel já possui mecanismos e semânticas bem definidas para o uso de descritores de arquivo.

Se a sua nova chamada de sistema (2) de fato retornar un novo descritor de arquivo, então o argumento de flags deve incluir um valor que seja equivalente a definir O_CLOEXEC no novo FD. Isso torna possível para o espaço do usuário fechar a janela de tempo entre a chamada () e a execução de fcntl(fd, F_SETFD, FD_CLOEXEC), onde um fork() e execve() inesperados em outra thread poderiam vazar um descritor para o programa executado. (Contudo, resista à tentação de reutilizar o valor real da constante O_CLOEXEC, pois ela é específica de cada arquitetura e faz parte de um espaço de numeração de flags O_* que está bastante cheio.)

Se a sua chamada de sistema retornar um novo descritor de arquivo, você também deve considerar o que significa usar a família de chamadas de sistema poll(2) nesse descritor de arquivo. Tornar um descritor de arquivo pronto para leitura ou escrita é a maneira normal de o kernel indicar ao espaço do usuário que um evento ocorreu no objeto correspondente do kernel.

Se a sua nova chamada de sistema (2) envolver um argumento de nome de arquivo (filename):

int sys_xyzzy(const char __user *path, ..., unsigned int flags);

você também deve considerar se uma versão xyzzyat(2) seria mais apropriada:

int sys_xyzzyat(int dfd, const char __user *path, ..., unsigned int flags);

Isso permite maior flexibilidade para a forma como o espaço do usuário especifica o arquivo em questão; em particular, permite que o espaço do usuário solicite a funcionalidade para um descritor de arquivo já aberto usando a flag AT_EMPTY_PATH, fornecendo efetivamente uma operação fxyzzy(3) de graça:

- xyzzyat(AT_FDCWD, path, ..., 0) é equivalente a (path,...)
- xyzzyat(fd, "", ..., AT_EMPTY_PATH) é equivalente a fxyzzy(fd, ...)

(Para mais detalhes sobre a justificativa das chamadas *at(), veja a página de manual openat(2); para um exemplo de AT_EMPTY_PATH, veja a página de manual fstatat(2).)

Se a sua nova chamada de sistema (2) envolver um parâmetro que descreve um deslocamento (offset) dentro de um arquivo, mude o seu tipo para loff_t para que offsets de 64 bits possam ser suportados mesmo em arquiteturas de 32 bits.

Se a sua nova chamada de sistema (2) envolver funcionalidades privilegiadas, ela precisa ser governada pelo bit de capacidade (capability) do Linux apropriado (verificado com uma chamada a capable()), conforme descrito na página de manual capabilities(7). Escolha um bit de capacidade existente que governe funcionalidades relacionadas, mas tente evitar combinar muitas funções que tenham apenas uma vaga relação sob o mesmo bit, pois isso vai contra o propósito das capabilities de dividir o poder do root. Em particular, evite adicionar novos usos para a capacidade CAP_SYS_ADMIN, que já é excessivamente generalista.

Se a sua nova chamada de sistema (2) manipular um processo diferente do processo que a chamou, ela deve ser restrita (usando uma chamada a ptrace_may_access()) para que apenas um processo chamador com as mesmas permissões do processo alvo, ou com as capacidades necessárias, possa manipular o processo alvo.

Finalmente, esteja ciente de que algumas arquiteturas não-x86 lidam melhor se os parâmetros da chamada de sistema que são explicitamente de 64 bits caírem em argumentos de numeração ímpar (ou seja, parâmetro 1, 3, 5), para permitir o uso de pares contíguos de registradores de 32 bits. (Esta preocupação não se aplica se os argumentos fizerem parte de uma estrutura que é passada por meio de um ponteiro.)

Propondo a API

Para tornar as novas chamadas de sistema fáceis de revisar, é melhor dividir o conjunto de patches (patchset) em blocos separados. Estes devem incluir, pelo menos, os seguintes itens como commits distintos (cada um dos quais é descrito mais adiante):

  • A implementação central da chamada de sistema, juntamente com protótipos, numeração genérica, alterações no Kconfig e a implementação de stub de realinhamento (fallback stub).

  • A fiação (wiring up) da nova chamada de sistema para uma arquitetura em particular, geralmente x86 (incluindo todas as variantes x86_64, x86_32 e x32).

  • Uma demonstração do uso da nova chamada de sistema no espaço do usuário por meio de um selftest em tools/testing/selftests/.

  • Um rascunho da página de manual (man-page) para a nova chamada de sistema, seja como texto simples na carta de apresentação (cover letter) ou como um patch para o repositório (separado) de man-pages.

Novas propostas de chamadas de sistema, como qualquer alteração na API do kernel, devem sempre ser enviadas com cópia (cc’ed) para linux-api@vger.kernel.org.

Implementação Genérica de Chamadas de Sistema

O ponto de entrada principal para a sua nova chamada de sistema (2) será chamado de sys_xyzzy(), mas você deve adicionar esse ponto de entrada com a macro SYSCALL_DEFINEn() apropriada, em vez de fazer isso explicitamente. O ‘n’ indica o número de argumentos da chamada de sistema, e a macro recebe o nome da chamada de sistema seguido pelos pares (tipo, nome) para os parâmetros como argumentos. O uso dessa macro permite que os metadados sobre a nova chamada de sistema fiquem disponíveis para outras ferramentas.

O novo ponto de entrada também precisa de um protótipo de função correspondente em include/linux/syscalls.h, marcado como asmlinkage para corresponder à maneira como as chamadas de sistema são invocadas:

asmlinkage long sys_xyzzy(...);

Algumas arquiteturas (por exemplo, x86) possuem suas próprias tabelas de syscall específicas da arquitetura, mas várias outras arquiteturas compartilham uma tabela de syscall genérica. Adicione a sua nova chamada de sistema à lista genérica adicionando uma entrada na lista em include/uapi/asm-generic/unistd.h:

#define __NR_xyzzy 292
__SYSCALL(__NR_xyzzy, sys_xyzzy)

Atualize também a contagem de __NR_syscalls para refletir a chamada de sistema adicional, e observe que se múltiplas novas chamadas de sistema forem adicionadas na mesma janela de mesclagem (merge window), o número da sua nova syscall poderá ser ajustado para resolver conflitos.

O arquivo kernel/sys_ni.c fornece uma implementação de stub de fallback para cada chamada de sistema, retornando -ENOSYS. Adicione a sua nova chamada de sistema aqui também:

COND_SYSCALL(sys_xyzzy);

A sua nova funcionalidade de kernel, e a chamada de sistema que a controla, deve normalmente ser opcional, portanto adicione uma opção CONFIG (tipicamente em init/Kconfig) para ela. Como de costume para novas opções CONFIG:

  • Inclua uma descrição da nova funcionalidade e da chamada de sistema controlada pela opção.

  • Faça a opção depender de EXPERT se ela deve ser ocultada dos usuários normais.

  • Faça com que quaisquer novos arquivos de código-fonte que implementem a função sejam dependentes da opção CONFIG no Makefile (por exemplo, obj-$(CONFIG_XYZZY_SYSCALL) += xyzzy.o).

  • Verifique duas vezes se o kernel ainda compila com a nova opção CONFIG desativada.

Para resumir, você precisa de um commit que inclua:

  • Opção CONFIG para a nova função, normalmente em init/Kconfig

  • SYSCALL_DEFINEn(, ...) para o ponto de entrada

  • Protótipo correspondente em include/linux/syscalls.h

  • Entrada na tabela genérica em include/uapi/asm-generic/unistd.h

  • Stub de fallback em kernel/sys_ni.c

Desde a versão 6.11

A partir da versão 6.11 do kernel, a implementação de chamadas de sistema genéricas para as seguintes arquiteturas não requer mais modificações em include/uapi/asm-generic/unistd.h:

  • arc

  • arm64

  • csky

  • hexagon

  • loongarch

  • nios2

  • openrisc

  • riscv

Em vez disso, você precisa atualizar scripts/syscall.tbl e, se aplicável, ajustar arch/*/kernel/Makefile.syscalls.

Como o scripts/syscall.tbl serve como uma tabela de syscall comum para múltiplas arquiteturas, uma nova entrada é necessária nesta tabela:

468   common        sys_xyzzy

Note que adicionar uma entrada ao scripts/syscall.tbl com a ABI “common” também afeta todas as arquiteturas que compartilham essa tabela. Para alterações mais limitadas ou específicas de uma arquitetura, considere usar uma ABI específica da arquitetura ou definir uma nova.

Se uma nova ABI, digamos xyz, for introduzida, as atualizações correspondentes também devem ser feitas em arch/*/kernel/Makefile.syscalls:

syscall_abis_{32,64} += xyz (...)

Para resumir, você precisa de um commit que inclua:

  • Opção CONFIG para a nova função, normalmente em init/Kconfig

  • SYSCALL_DEFINEn(, ...) para o ponto de entrada

  • Protótipo correspondente em include/linux/syscalls.h

  • Nova entrada em scripts/syscall.tbl

  • (Se necessário) Atualizações de Makefile em arch/*/kernel/Makefile.syscalls

  • Stub de fallback em kernel/sys_ni.c

Implementação de Chamadas de Sistema em x86

Para interligar (wire up) a sua nova chamada de sistema nas plataformas x86, você precisa atualizar as tabelas mestras de syscall. Assumindo que a sua nova chamada de sistema não seja especial de alguma forma (veja abaixo), isso envolve uma entrada “common” (para x86_64 e x32) em arch/x86/entry/syscalls/syscall_64.tbl:

333   common        sys_xyzzy

e uma entrada “i386” em arch/x86/entry/syscalls/syscall_32.tbl:

380   i386          sys_xyzzy

Novamente, esses números estão sujeitos a alterações caso ocorram conflitos na janela de mesclagem (merge window) relevante.

Chamadas de Sistema de Compatibilidade (Genéricas)

Para a maioria das chamadas de sistema, a mesma implementação de 64 bits pode ser invocada mesmo quando o programa do espaço do usuário é, ele próprio, de 32 bits; mesmo se os parâmetros da chamada de sistema incluírem um ponteiro explícito, isso é tratado de forma transparente.

No entanto, existem algumas situações em que uma camada de compatibilidade (compatibility layer) é necessária para lidar com as diferenças de tamanho entre 32 bits e 64 bits.

A primeira é se o kernel de 64 bits também suportar programas de espaço do usuário de 32 bits e, portanto, precisar analisar áreas de memória (__user) que poderiam conter valores de 32 bits ou 64 bits. Em particular, isso é necessário sempre que um argumento de chamada de sistema for:

  • um ponteiro para um ponteiro

  • um ponteiro para uma struct que contém um ponteiro (por exemplo, struct iovec __user *)

  • um ponteiro para um tipo integral de tamanho variável (time_t, off_t, long, ...)

  • um ponteiro para uma struct que contém um tipo integral de tamanho variável.

A segunda situação que requer uma camada de compatibilidade é se um dos argumentos da chamada de sistema tiver um tipo que é explicitamente de 64 bits, mesmo em uma arquitetura de 32 bits, por exemplo, loff_t ou __u64. Neste caso, um valor que chega ao kernel de 64 bits vindo de uma aplicação de 32 bits será dividido em dois valores de 32 bits, que precisarão ser remontados na camada de compatibilidade.

(Note que um argumento de chamada de sistema que seja um ponteiro para um tipo explícito de 64 bits não precisa de uma camada de compatibilidade; por exemplo, os argumentos do splice(2) do tipo loff_t __user * não disparam a necessidade de uma chamada de sistema compat_.)

A versão de compatibilidade da chamada de sistema é chamada de compat_sys_xyzzy() e é adicionada com a macro COMPAT_SYSCALL_DEFINEn(), de forma análoga à macro SYSCALL_DEFINEn. Esta versão da implementação roda como parte de um kernel de 64 bits, mas espera receber valores de parâmetros de 32 bits e faz o que for necessário para lidar com eles. (Tipicamente, a versão compat_sys_ converte os valores para versões de 64 bits e chama a versão sys_, ou ambas chamam uma função interna comum de implementação).

O ponto de entrada compat também precisa de um protótipo de função correspondente em include/linux/compat.h, marcado como asmlinkage para corresponder à maneira como as chamadas de sistema são invocadas:

asmlinkage long compat_sys_xyzzy(...);

Se a chamada de sistema envolver uma estrutura cujo layout seja diferente em sistemas de 32 bits e 64 bits, digamos struct xyzzy_args, então o arquivo de cabeçalho include/linux/compat.h também deve incluir uma versão compat da estrutura (struct compat_xyzzy_args), onde cada campo de tamanho variável tenha o tipo compat_ correspondente ao tipo na struct xyzzy_args. A rotina compat_sys_xyzzy() pode então usar essa estrutura compat_ para analisar os argumentos vindos de uma invocação de 32 bits.

Por exemplo, se existirem os campos:

struct xyzzy_args {
    const char __user *ptr;
    __kernel_long_t varying_val;
    u64 fixed_val;
    /* ... */
};

na struct xyzzy_args, então a struct compat_xyzzy_args teria:

struct compat_xyzzy_args {
    compat_uptr_t ptr;
    compat_long_t varying_val;
    u64 fixed_val;
    /* ... */
};

A lista genérica de chamadas de sistema também precisa de ajustes para permitir a versão compat; a entrada em include/uapi/asm-generic/unistd.h deve usar __SC_COMP em vez de __SYSCALL:

#define __NR_xyzzy 292
__SC_COMP(__NR_xyzzy, sys_xyzzy, compat_sys_xyzzy)

Para resumir, você precisa de:

  • uma macro COMPAT_SYSCALL_DEFINEn(, ...) para o ponto de entrada compat

  • protótipo correspondente em include/linux/compat.h

  • (se necessário) struct de mapeamento de 32 bits em include/linux/compat.h

  • instância de __SC_COMP, e não de __SYSCALL, em include/uapi/asm-generic/unistd.h

Desde a versão 6.11

Isso se aplica a todas as arquiteturas listadas em Desde a versão 6.11 sob “Implementação Genérica de Chamadas de Sistema”, exceto arm64. Veja Chamadas de Sistema de Compatibilidade (arm64) para mais informações.

Você precisa estender a entrada em scripts/syscall.tbl com uma coluna extra para indicar que um programa de espaço do usuário de 32 bits rodando em um kernel de 64 bits deve atingir o ponto de entrada compat:

468   common          sys_xyzzy    compat_sys_xyzzy

Para resumir, você precisa de:

  • COMPAT_SYSCALL_DEFINEn(, ...) para o ponto de entrada compat

  • Protótipo correspondente em include/linux/compat.h

  • Modificação da entrada em scripts/syscall.tbl para incluir uma coluna “compat” extra

  • (Se necessário) Struct de mapeamento de 32 bits em include/linux/compat.h

Chamadas de Sistema de Compatibilidade (arm64)

No arm64, existe uma tabela de syscall dedicada para chamadas de sistema de compatibilidade voltadas para o espaço do usuário de 32 bits (AArch32): arch/arm64/tools/syscall_32.tbl. Você precisa adicionar uma linha adicional a esta tabela especificando o ponto de entrada compat:

468   common          sys_xyzzy    compat_sys_xyzzy

Chamadas de Sistema de Compatibilidade (x86)

Para interligar a arquitetura x86 de uma chamada de sistema com uma versão de compatibilidade, as entradas nas tabelas de syscall precisam ser ajustadas.

Primeiro, a entrada em arch/x86/entry/syscalls/syscall_32.tbl ganha uma coluna extra para indicar que um programa de espaço do usuário de 32 bits rodando em um kernel de 64 bits deve atingir o ponto de entrada compat:

380   i386          sys_xyzzy    __ia32_compat_sys_xyzzy

Segundo, você precisa definir o que deve acontecer para a versão da ABI x32 da nova chamada de sistema. Há uma escolha aqui: o layout dos argumentos deve corresponder à versão de 64 bits ou à versão de 32 bits.

Se houver um ponteiro para um ponteiro envolvido, a decisão é fácil: x32 é ILP32 (inteiro, long e ponteiro possuem 32 bits), portanto o layout deve corresponder à versão de 32 bits, e a entrada em arch/x86/entry/syscalls/syscall_64.tbl é dividida para que os programas x32 atinjam o wrapper de compatibilidade:

333   64            sys_xyzzy
...
555   x32           __x32_compat_sys_xyzzy

Se não houver ponteiros envolvidos, então é preferível reutilizar a chamada de sistema de 64 bits para a ABI x32 (e, consequentemente, a entrada em arch/x86/entry/syscalls/syscall_64.tbl permanece inalterada).

Em qualquer um dos casos, você deve verificar se os tipos envolvidos no layout dos seus argumentos de fato se mapeiam exatamente do x32 (-mx32) para os seus equivalentes de 32 bits (-m32) ou 64 bits (-m64).

Chamadas de Sistema com Retorno para Outro Local

Para a maioria das chamadas de sistema (syscalls), assim que a execução é concluída, o programa do usuário continua exatamente de onde parou -- na próxima instrução, com a pilha idêntica e a maior parte dos registradores no mesmo estado de antes da chamada, além do mesmo espaço de memória virtual.

No entanto, algumas poucas chamadas de sistema agem de forma diferente. Elas podem retornar para um local distinto (rt_sigreturn), alterar o espaço de memória (fork/vfork/clone) ou até mesmo modificar a arquitetura (execve/execveat) do programa.

Para permitir isso, a implementação da chamada de sistema no kernel pode precisar salvar e restaurar registradores adicionais na pilha do kernel, garantindo controle total de onde e como a execução continuará após a syscall.

Isso é específico de cada arquitetura (arch-specific), mas tipicamente envolve a definição de pontos de entrada em assembly que salvam/restauram esses registradores adicionais e invocam o ponto de entrada real da chamada de sistema.

Para x86_64, isso é implementado como um ponto de entrada stub_xyzzy em arch/x86/entry/entry_64.S, e a entrada correspondente na tabela de syscalls (arch/x86/entry/syscalls/syscall_64.tbl) é ajustada para refletir:

333   common        stub_xyzzy

O equivalente para programas de 32 bits executados em um kernel de 64 bits é normalmente chamado de stub32_xyzzy e implementado em arch/x86/entry/entry_64_compat.S, com o respectivo ajuste na tabela de syscalls em arch/x86/entry/syscalls/syscall_32.tbl:

380   i386          sys_xyzzy    stub32_xyzzy

Se a chamada de sistema precisar de uma camada de compatibilidade (como na seção anterior), a versão stub32_ precisará chamar a versão compat_sys_ da chamada de sistema em vez da versão nativa de 64 bits. Além disso, se a implementação da ABI x32 não for compartilhada com a versão x86_64, sua tabela de syscalls também precisará invocar um stub que direcione para a versão compat_sys_.

Por questões de integridade, também é recomendado configurar um mapeamento para que o User-Mode Linux (UML) continue funcionando -- sua tabela de syscalls fará referência a stub_xyzzy, mas o build do UML não inclui a implementação de arch/x86/entry/entry_64.S (já que o UML simula registradores, etc.). Corrigir isso é tão simples quanto adicionar um #define em arch/x86/um/sys_call_table_64.c:

#define stub_xyzzy sys_xyzzy

Outros Detalhes

A maior parte do kernel trata as chamadas de sistema de maneira genérica, mas há exceções ocasionais que podem precisar de atualização para a sua chamada de sistema específica.

O subsistema de auditoria (audit) é um desses casos especiais; ele inclui funções (específicas de cada arquitetura) que classificam alguns tipos especiais de chamada de sistema -- especificamente operações de abertura de arquivo (open/openat), execução de programa (execve/exeveat) ou multiplexador de socket (socketcall). Se a sua nova chamada de sistema for análoga a uma dessas, o sistema de auditoria deverá ser atualizado.

De forma mais geral, se existir uma chamada de sistema atual que seja análoga à sua nova chamada de sistema, vale a pena fazer um grep em todo o kernel pela chamada existente para verificar se não há outros casos especiais.

Testes

Uma nova chamada de sistema deve, obviamente, ser testada; também é útil fornecer aos revisores uma demonstração de como os programas do espaço do usuário (user space) usarão a chamada de sistema. Uma boa maneira de combinar esses objetivos é incluir um programa simples de autoteste em um novo diretório sob tools/testing/selftests/.

Para uma nova chamada de sistema, obviamente não haverá uma função de wrapper na libc e, portanto, o teste precisará invocá-la usando syscall(); além disso, se a chamada de sistema envolver uma nova estrutura visível para o espaço do usuário, o cabeçalho correspondente precisará ser instalado para compilar o teste.

Certifique-se de que o autoteste seja executado com sucesso em todas as arquiteturas suportadas. Por exemplo, verifique se ele funciona quando compitado como um programa ABI x86_64 (-m64), x86_32 (-m32) e x32 (-mx32).

Para testes mais extensos e minuciosos de novas funcionalidades, você também deve considerar a adição de testes ao Linux Test Project ou ao projeto xfstests para alterações relacionadas

Página de Manual (Man Page)

Todas as novas chamadas de sistema devem vir acompanhadas de uma página de manual completa, idealmente usando a marcação groff, mas texto simples também é aceitável. Se o groff for utilizado, é útil incluir uma versão ASCII pré- renderizada da página de manual no e-mail de apresentação (cover letter) do conjunto de patches (patchset), para a conveniência dos revisores.

A página de manual deve ser enviada com cópia (cc) para linux-man@vger.kernel.org. Para mais detalhes, consulte https://www.kernel.org/doc/man-pages/patches.html

Não invoque Chamadas de Sistema dentro do Kernel

As chamadas de sistema são, como mencionado acima, pontos de interação entre o espaço do usuário (userspace) e o kernel. Portanto, funções de chamada de sistema como sys_xyzzy() ou compat_sys_xyzzy() só devem ser chamadas a partir do espaço do usuário por meio da tabela de syscalls, e não de outros lugares do kernel. Se a funcionalidade da syscall for útil para ser utilizada dentro do kernel, precisar ser compartilhada entre uma syscall antiga e uma nova, ou precisar ser compartilhada entre uma syscall e sua variante de compatibilidade, ela deve ser implementada por meio de uma função auxiliadora (“helper”, como ksys_xyzzy()). Essa função do kernel poderá então ser chamada dentro do stub da syscall (sys_xyzzy()), do stub da syscall de compatibilidade (compat_sys_xyzzy()) e/ou de outro código do kernel.

Pelo menos em x86 de 64 bits, será um requisito rígido a partir da versão v4.17 em diante não chamar funções de chamadas de sistema no kernel. Essa arquitetura utiliza uma convenção de chamada diferente para chamadas de sistema na qual a struct pt_regs é decodificada dinamicamente em um wrapper de syscall, que então repassa o processamento para a função real da syscall. Isso significa que apenas os parâmetros realmente necessários para uma syscall específica são passados durante a entrada da syscall, em vez de preencher seis registradores da CPU com conteúdos aleatórios do espaço do usuário o tempo todo (o que poderia causar problemas sérios no decorrer da cadeia de chamadas).

Além disso, as regras sobre como os dados podem ser acessados diferem entre os dados do kernel e os dados do usuário. Essa é outra razão pela qual chamar sys_xyzzy() geralmente é uma má ideia.

Exceções a essa regra são permitidas apenas em substituições (overrides) específicas de cada arquitetura, wrappers de compatibilidade específicos de cada arquitetura ou outros códigos dentro do diretório arch/.

Referências e Fontes