Portando o SO para Aarch64

Aarch64 é uma arquitetura de 64 bits do ARM (às vezes chamada arm64). Neste artigo, mostrarei como ele difere do ARM "regular" (32 bits) e como é difícil portar seu sistema para ele.


Este artigo não é um guia detalhado, mas uma visão geral dos módulos do sistema que precisarão ser refeitos e quanto a arquitetura como um todo difere dos ARMs comuns de 32 bits; tudo isso da minha experiência pessoal ao portar a Embox nessa arquitetura. Para a transferência direta de um sistema específico, de uma forma ou de outra, você terá que lidar com a documentação. No final do artigo, deixei links para alguns documentos que podem ser úteis.


De fato, existem mais diferenças do que semelhanças, e o Aarch64 é mais uma nova arquitetura do que uma extensão de 64 bits do familiar ARM. O antecessor do Aarch64 é amplamente o Aarch32 (essa é uma extensão do ARM de 32 bits usual), mas como eu não tinha experiência com ele, não escreverei sobre isso :)


Ainda neste artigo, se eu escrever sobre o ARM "antigo" ou "antigo", quero dizer ARM de 32 bits (com um conjunto de comandos ARM).


Analisarei brevemente a lista de alterações em comparação com o ARM de 32 bits e as analisarei mais detalhadamente.


  • Os registros de uso geral tornaram-se duas vezes mais amplos (agora são 64 bits cada) e seu número dobrou (ou seja, agora não existem 16, mas 32 deles).
  • Recusa do conceito de registrador de coprocessador, agora eles podem ser acessados ​​simplesmente pelo nome, por exemplo, msr vbar_el1, x0 (contra o mcr p15, 0, %0, c1, c1, 2 anterior mcr p15, 0, %0, c1, c1, 2 )
  • O novo modelo da MMU (não está conectado ao antigo, de qualquer forma, você deve escrever novamente).
  • Anteriormente, havia dois níveis de privilégio: usuário (correspondente ao modo de processador USR) e sistema (correspondente aos modos SYS, IRQ, FIQ, ABT, ...), agora tudo é mais fácil e mais complicado ao mesmo tempo - agora existem 4 modos.
  • AdvSIMD substituiu NEON, operações de ponto flutuante são feitas através dele.

Agora mais sobre os pontos.


Registradores e conjunto de instruções


Os registros de uso geral são r0-r30, enquanto você pode acessá-los como 64 bits (x0-x30) ou 32 bits (w0-w30, acesso aos 32 bits inferiores).


O conjunto de instruções para Aarch64 é chamado A64. Leia a descrição das instruções aqui . A aritmética básica e alguns outros comandos na linguagem assembly permaneceram os mesmos:


  mov w0, w1 /*    w1  w0 */ add x0, x1, 13 /*   x0  x1   13 */ b label /* ""   "label" bl label /* ""   "label",     x30 */ ldr x3, [x1, 0] /*   x3 ,    x1 */ str x3, [x0, 0] /*   x3  ,    x0 */ 

Agora um pouco sobre as diferenças:


  • Um registro especial “zero” rzr/xzr/wzr , que é zero durante a leitura (você pode usar a gravação no registro, mas o resultado do cálculo não será gravado em nenhum lugar).

 subs xzr, x1, x2 /*  x1  x2    NZCV,       */ 

  • Você não pode empilhar muitos registradores ( stmfd sp!, {r0-r3} ) na pilha de uma só vez; você deve fazer isso em pares:

  stp x0, x1, [sp, 16]! stp x2, x3, [sp, 16]! 

  • O registro do PC (contador de programa, um ponteiro para a instrução de execução atual) agora não é um registro geral (costumava ser R15), portanto, não pode ser acessado com comandos comuns ( mov , ldr ), apenas através de ret , bl e assim por diante.


  • O status do programa agora não exibe CPSR (esse registro simplesmente não existe), mas os registros DAIF (contém o IRQ, máscara FIQ etc.), AIF - os mesmos bits A, I, F do CPSR), NZCV (bits negativos, zero, transporte , oVerflow - de repente, o mesmo NZCV do CPSR) e o System Control Register (SCTLR, para permitir cache, MMU, endianness e assim por diante).



Parece que esses comandos são suficientes para escrever um carregador de inicialização simples que pode transferir o controle para um código independente de plataforma :)


Modos de desempenho e alternância entre eles


Os modos de desempenho estão bem escritos em Fundamentos do ARMv8-A . Vou relatar brevemente a essência deste documento aqui.


O Aarch64 possui 4 níveis de privilégio (nível de execução, a seguir denominado EL).


  • EL3 - Secure Monitor (presume-se que o firmware esteja sendo executado nesse nível)
  • EL2 - Hipervisor
  • EL1 - OS
  • EL0 - Aplicações

Em um sistema operacional de 64 bits, você pode executar aplicativos de 32 e 64 bits; em um sistema operacional de 32 bits, apenas aplicativos de 32 bits podem ser executados.



As transições entre ELs são feitas com a ajuda de exceções (chamadas do sistema, interrupções, erro de acesso à memória) ou com a ajuda do eret return from exception ( eret ).


Cada EL tem seus próprios registros SPSR, ELR, SP (ou seja, são "registros bancários").


Muitos registros do sistema também são divididos por EL - por exemplo, o registro de contexto MMU ttbr0 - existe ttbr0_el2 , ttbr0_el1 e você precisa acessar seu registro no EL correspondente. O mesmo se aplica aos registros de status do programa - DAIF, NZCV, SCTLR, SPSR, ELR ...


MMU


O Armv8-A suporta o MMU ARMv8.2 LPA, mais sobre isso pode ser encontrado no capítulo D5 do Manual de referência da arquitetura do ARM para o Armv8, Armv8-A .


Em resumo, esta MMU suporta páginas 4KiB (4 níveis de tabelas de memória virtual), 16KiB (4 níveis) e 64KiB (3 níveis). Em qualquer um dos níveis intermediários, você pode especificar um bloco de memória, indicando assim não o próximo nível da tabela, mas toda uma memória do tamanho que a próxima tabela de nível deve "cobrir". Eu tenho um artigo de longa data sobre memória virtual, onde você pode ler sobre tabelas, níveis de tradução e isso é tudo.


Das pequenas mudanças, eles recusaram os domínios, mas adicionaram sinalizadores como bit sujo.


Em geral, exceto por "blocos" em vez de tabelas de conversão intermediárias, nenhuma alteração conceitual especial foi observada, MMU como MMU.


Simd avançado


Existem diferenças significativas de AdvSIMD no NEON antigo, tanto ao trabalhar com ponto flutuante quanto com operações vetoriais (SIMD). Por exemplo, se D0 anterior consistia em S0 e S1 e Q0 - de D0 e D1, então agora não é assim: Q0 corresponde a D0 e S0, para Q1 - D1 e S0 e assim por diante. Ao mesmo tempo, o suporte ao VFP / SIMD é obrigatório. Ao fazer um acordo, não há transferência programática de parâmetros (o que costumava ser chamado de "soft float ABI", no GCC - o sinalizador -mfloat-abi=softfp ), para que você precise implementar o suporte de hardware para o ponto flutuante .


Havia 16 registros de 128 bits:



Existem 32 registros de 128 bits cada:



Você pode ler mais sobre o NEON neste artigo ; uma lista de comandos disponíveis para o Aarch64 pode ser encontrada aqui .


Operações básicas com registros de ponto flutuante:


  fadd s0, s1, s2 /* s0 = s1 + s2 */ fmul d0, d1, d2 /* d0 = d1 * d2 */ 

Operações básicas do SIMD:


  /*  , : NEON,    */ /* q0 = q1 + q2,   --   4     */ vadd.s32 q0, q1, q2 /* : AdvSIMD,    */ /* v0 = v1 + v2,   --   4     */ add v0.4s, v1.4s, v2.4s /*   v1 (  2 64- )    d1 */ addv d1, v1.ds /*     4   0 */ movi v1.4s, 0x0 

Plataformas


QEMU


QEMU tem suporte para Aarch64. Uma das plataformas é virt , para que inicie no modo de 64 bits, você também precisa passar o -cpu cortex-a53 , algo como isto:


 qemu-system-aarch64 -M virt -cpu cortex-a53 -kernel ./embox -m 1024 -nographic # ./embox -- ELF-  

O que é legal, são usados ​​muitos periféricos para esta plataforma, drivers para os quais já estavam na Embox - por exemplo, PL011 para o console, ARM Generic Interrupt Controller, etc. É claro que esses dispositivos têm endereços de registro base diferentes e outros números de interrupção, mas o principal é O código do driver funciona inalterado na nova arquitetura. Quando o sistema inicia, o controle está no EL1.


i.MX8


Devido a esse pedaço de ferro, foi iniciada a portabilidade para o Aarch64 - i.MX8MQ Nitrogen8M.



Diferentemente do QEMU, o u-boot transfere o controle para a imagem no EL2 e, além disso, por algum motivo, inclui o MMU (toda a memória é mapeada de 1 para 1), o que cria alguns problemas adicionais durante a inicialização.


A Embox já suportava o i.MX6 e, bem, no i.MX8 a parte da periferia é a mesma - por exemplo, UART e Ethernet, que também funcionavam (eu tive que consertar alguns lugares onde havia uma ligação restrita aos endereços de 32 bits). Por outro lado, o controlador de interrupção é diferente lá - ARM GICv3, que é bem diferente da primeira versão.


Conclusão


No momento, o suporte ao Aarch64 no Embox não está completo, mas já existe uma funcionalidade mínima - interrupções, MMU, entrada e saída via UART. Ainda há muito a ser finalizado, mas os primeiros passos foram mais fáceis de dar do que pareciam desde o início. Há muito menos documentação e artigos do que no ARM, mas há informações mais que suficientes para lidar com tudo.


Em geral, se você tem experiência com o ARM, portar para o Aarch64 é uma tarefa viável. Embora, como sempre, você possa tropeçar em algumas coisinhas :)


Você pode fazer o download do projeto para colocá-lo no QEMU em nosso repositório , se tiver alguma dúvida - escreva nos comentários, no boletim informativo ou no bate-papo no Telegram (também há um canal ).


Links úteis



PS


De 24 a 25 de agosto, falaremos no TechTrain , ouviremos nossas apresentações duas ou três vezes , chegaremos ao stand - responderemos às suas perguntas :)

Source: https://habr.com/ru/post/pt463417/


All Articles