arm64: Fixes for booting in EL2 w/ E2H

* E2H (EL2 Host) is enabled by default and usually can't be disabled
  on CPUs with FEAT_VHE. Since EL2 becomes a superset of EL1 with
  E2H, we can simply do nothing and everything that expects EL1
  will Just Work
* Changed some register bits numbers to avoid C integer promotion
  issues
* sEmptyTable needs to be filled in earler, before the kernel team
  is created. Otherwise, we put the uninitialized value of sEmptyTable
  in TTBR0, which immediately pends SError on Apple M1

Change-Id: I84ff7fd9134448f27315dadfd12bdd9df8c75b10
Reviewed-on: https://review.haiku-os.org/c/haiku/+/10547
Reviewed-by: waddlesplash <[email protected]>
Tested-by: Commit checker robot <[email protected]>
This commit is contained in:
Sam Roberts
2026-03-19 15:33:11 +00:00
committed by waddlesplash
parent 13abea7517
commit 593f25d6f1
5 changed files with 40 additions and 40 deletions
@@ -80,6 +80,7 @@
#define HCR_RW 0x0000000080000000
#define HCR_CD 0x0000000100000000
#define HCR_ID 0x0000000200000000
#define HCR_E2H 0x0000000400000000
#endif
@@ -597,27 +597,27 @@
#define PSR_FLAGS 0xf0000000
/* TCR_EL1 - Translation Control Register */
#define TCR_ASID_16 (1 << 36)
#define TCR_ASID_16 (1UL << 36)
#define TCR_IPS_SHIFT 32
#define TCR_IPS_32BIT (0 << TCR_IPS_SHIFT)
#define TCR_IPS_36BIT (1 << TCR_IPS_SHIFT)
#define TCR_IPS_40BIT (2 << TCR_IPS_SHIFT)
#define TCR_IPS_42BIT (3 << TCR_IPS_SHIFT)
#define TCR_IPS_44BIT (4 << TCR_IPS_SHIFT)
#define TCR_IPS_48BIT (5 << TCR_IPS_SHIFT)
#define TCR_IPS_32BIT (0UL << TCR_IPS_SHIFT)
#define TCR_IPS_36BIT (1UL << TCR_IPS_SHIFT)
#define TCR_IPS_40BIT (2UL << TCR_IPS_SHIFT)
#define TCR_IPS_42BIT (3UL << TCR_IPS_SHIFT)
#define TCR_IPS_44BIT (4UL << TCR_IPS_SHIFT)
#define TCR_IPS_48BIT (5UL << TCR_IPS_SHIFT)
#define TCR_TG0_SHIFT 14
#define TCR_TG0_4K (0 << TCR_TG0_SHIFT)
#define TCR_TG0_64K (1 << TCR_TG0_SHIFT)
#define TCR_TG0_16K (2 << TCR_TG0_SHIFT)
#define TCR_TG0_MASK (3 << TCR_TG0_SHIFT)
#define TCR_TG0_4K (0UL << TCR_TG0_SHIFT)
#define TCR_TG0_64K (1UL << TCR_TG0_SHIFT)
#define TCR_TG0_16K (2UL << TCR_TG0_SHIFT)
#define TCR_TG0_MASK (3UL << TCR_TG0_SHIFT)
#define TCR_TG1_SHIFT 30
#define TCR_TG1_16K (1 << TCR_TG1_SHIFT)
#define TCR_TG1_4K (2 << TCR_TG1_SHIFT)
#define TCR_TG1_64K (3 << TCR_TG1_SHIFT)
#define TCR_TG1_MASK (3 << TCR_TG1_SHIFT)
#define TCR_TG1_16K (1UL << TCR_TG1_SHIFT)
#define TCR_TG1_4K (2UL << TCR_TG1_SHIFT)
#define TCR_TG1_64K (3UL << TCR_TG1_SHIFT)
#define TCR_TG1_MASK (3UL << TCR_TG1_SHIFT)
#define TCR_SH1_SHIFT 28
#define TCR_SH1_IS (0x3UL << TCR_SH1_SHIFT)
@@ -6,6 +6,7 @@
#include <efi/types.h>
#include <kernel/arch/arm64/arm_registers.h>
#include <kernel/arch/arm64/arch_hypervisor.h>
#include <kernel/arch/arm64/arch_pte.h>
#include <arch_kernel.h>
@@ -167,18 +167,26 @@ arch_start_kernel(addr_t kernelEntry)
serial_init();
serial_enable();
switch (el) {
case 1:
arch_mmu_setup_EL1(READ_SPECIALREG(TCR_EL1));
break;
case 2:
arch_mmu_setup_EL1(READ_SPECIALREG(TCR_EL2));
arch_cache_disable();
_arch_transition_EL2_EL1();
break;
default:
panic("Unexpected Exception Level\n");
break;
// If we have E2H available, we want to also enable TGE
// so exceptions don't get taken to EL1
bool e2h = false;
if (el == 2) {
uint64 hcr = READ_SPECIALREG(HCR_EL2);
if ((hcr & HCR_E2H) != 0) {
e2h = true;
WRITE_SPECIALREG(HCR_EL2, hcr | HCR_TGE);
}
}
// EL2 with E2H enabled behaves as a superset of EL1
//
// EL2 without E2H enabled does not, so we need to drop to EL1
if (el == 1 || e2h) {
arch_mmu_setup_EL1(READ_SPECIALREG(TCR_EL1));
} else {
arch_mmu_setup_EL1(READ_SPECIALREG(TCR_EL2));
arch_cache_disable();
_arch_transition_EL2_EL1();
}
arch_cache_enable();
@@ -20,16 +20,6 @@ static char sPhysicalPageMapperData[sizeof(PMAPPhysicalPageMapper)];
static phys_addr_t sEmptyTable;
static void
arch_vm_alloc_empty_table(void)
{
vm_page_reservation reservation;
vm_page_reserve_pages(&reservation, 1, VM_PRIORITY_SYSTEM);
vm_page* page = vm_page_allocate_page(&reservation, PAGE_STATE_WIRED | VM_PAGE_ALLOC_CLEAR);
DEBUG_PAGE_ACCESS_END(page);
sEmptyTable = page->physical_page_number << PAGE_SHIFT;
}
void
arch_vm_install_empty_table_ttbr0(void)
@@ -103,6 +93,9 @@ arch_vm_translation_map_init(kernel_args* args, VMPhysicalPageMapper** _physical
*_physicalPageMapper = new (&sPhysicalPageMapperData) PMAPPhysicalPageMapper();
// Create an empty page table for use when we don't want a userspace page table.
sEmptyTable = vm_allocate_early_physical_page(args) << PAGE_SHIFT;
return B_OK;
}
@@ -112,9 +105,6 @@ arch_vm_translation_map_init_post_sem(kernel_args* args)
{
dprintf("arch_vm_translation_map_init_post_sem\n");
// Create an empty page table for use when we don't want a userspace page table.
arch_vm_alloc_empty_table();
return B_OK;
}