Preparando-se para o Rust

Antes de podermos começar a executar o código Rust, precisamos fazer alguma

.section .init.entry, "ax"
.global entry
entry:
    /*
     * Carregue e aplique a configuração de gerenciamento de memória, pronto para
     * habilitar MMU e caches.
    adrp x30, idmap
    msr ttbr0_el1, x30

    mov_i x30, .Lmairval
    msr mair_el1, x30

    mov_i x30, .Ltcrval
    /* Copie o intervalo de PA suportado para TCR_EL1.IPS. */
    mrs x29, id_aa64mmfr0_el1
    bfi x30, x29, #32, #4

    msr tcr_el1, x30

    mov_i x30, .Lsctlrval

    /*
     * Garanta que tudo antes deste ponto tenha sido concluĂ­do, entĂŁo invalida
     * quaisquer entradas locais de TLB potencialmente obsoletas antes que elas
     */
    isb
    tlbi vmalle1
    ic iallu
    dsb nsh
    isb

    /*
     * Configure sctlr_el1 para habilitar MMU e cache e não prossiga até isto
     * tenha sido concluĂ­do.
     */
    msr sctlr_el1, x30
    isb

    /* Desative a captura de acesso de ponto flutuante em EL1. */
    mrs x30, cpacr_el1
    orr x30, x30, #(0x3 << 20)
    msr cpacr_el1, x30
    isb

    /* Zere a seção bss. */
    adr_l x29, bss_begin
    adr_l x30, bss_end
0:  cmp x29, x30
    b.hs 1f
    stp xzr, xzr, [x29], #16
    b 0b

1:  /* Prepare a pilha. */
    adr_l x30, boot_stack_end
    mov sp, x30

    /* Configure o vetor de exceção. */
    adr x30, vector_table_el1
    msr vbar_el1, x30

    /* Chame o cĂłdigo Rust. */
    bl main

    /* Loop infinito esperando por interrupçÔes. */
2:  wfi
    b 2b
  • Isso Ă© o mesmo que seria para C: inicializando o estado do processador, zerando o BSS e configurando o ponteiro da pilha.
    • O BSS (bloco de sĂ­mbolo inicial, por razĂ”es histĂłricas) Ă© a parte do arquivo objeto que contĂ©m variĂĄveis alocadas estaticamente que sĂŁo inicializadas como zero. Eles sĂŁo omitidos da imagem, para evitar desperdĂ­cio de espaço em zeros. O compilador assume que o carregador cuidarĂĄ de zerĂĄ-los.
  • O BSS pode jĂĄ estar zerado, dependendo de como a memĂłria Ă© inicializada e a imagem Ă© carregada, mas o zeramos para ter certeza.
  • É necessĂĄrio habilitar a MMU e o cache antes de ler ou gravar qualquer memĂłria. Se nĂŁo fizermos isso:
    • Os acessos nĂŁo alinhados falharĂŁo. ConstruĂ­mos o cĂłdigo Rust para o alvo aarch64-unknown-none que define +strict-align para evitar que o compilador gere acessos nĂŁo alinhados, portanto, deve estar tudo bem neste caso, mas este nĂŁo Ă© necessariamente o caso em geral.
    • Se estivesse sendo executado em uma VM, isso pode levar a problemas de coerĂȘncia de cache. O problema Ă© que a VM estĂĄ acessando a memĂłria diretamente com o cache desabilitado, enquanto o host tem aliases cacheĂĄveis ​​para a mesma memĂłria. Mesmo que o host nĂŁo acesse explicitamente a memĂłria, acessos especulativos podem levar a preenchimentos de cache e, em seguida, alteraçÔes de um ou de outro serĂŁo perdidas quando o cache for limpo ou a VM habilitar o cache. (O cache Ă© indexado pelo endereço fĂ­sico, nĂŁo VA ou IPA.)
  • Para simplificar, usamos apenas uma tabela de pĂĄginas codificada (consulte idmap.S) que mapeia a identidade dos primeiros 1 GiB do espaço de endereços para dispositivos, os prĂłximos 1 GiB para DRAM e mais 1 GiB mais acima para mais dispositivos. Isso corresponde ao layout de memĂłria que o QEMU usa.
  • TambĂ©m configuramos o vetor de exceção (vbar_el1), que veremos mais tarde.
  • Todos os exemplos desta tarde assumem que estaremos executando no nĂ­vel de exceção 1 (EL1). Se vocĂȘ precisar executar em um nĂ­vel de exceção diferente, vocĂȘ precisarĂĄ modificar entry.S de acordo.