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.)
- Os acessos nĂŁo alinhados falharĂŁo. ConstruĂmos o cĂłdigo Rust para o alvo
- 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.