Skip to content

Commit

Permalink
elfloader: setup pagetables as needed
Browse files Browse the repository at this point in the history
This change sets up pagetables individually for:
- The ELFloader image (Normal memory)
- The DTB, whether supplied by EFI, cpio or u-boot (Normal mem)
- The UART MMIO range (Strongly-Ordered mem)

Thus, it removes the bulk 512 GiB 1:1 mapping that was there before.
This resulted in problems, since the kernel image was mapped with
Normal memory, but the same physical memory was part of the
1:1 Strongly-Ordered mapping.

This fulfills the definition of "Mismatched memory attributes"
from the ARM Architecture specification (ARM DDI 0487I.a, section
B2.8). Even though I am currently unable to see where there
would *occur* such a mismatched access, having such a mapping
situation is certainly not desirable and should be avoided.

Moreover, it is unclear whether there could arise problems from
establishing the (Strongly-ordered) mapping if there is nothing
behind a physical address (which is certainly true for some
parts of the 512 GiB range).

This commit solves the sporadics hangs while booting after the
"Enabling MMU and ..." message. Tests on several different Orins
(Muc and SJ) show promising results, i.e. no "hangs" occurred
anymore.

Note: The code in arm_switch_to_hyp_tables() still disables and
re-enables both MMU & caches, but there are no memory accesses in
between. That section has been engineered to be as short as possible
and no memory accesses happen in between.

Several barriers and code to invalidate instruction caches have
been added, too, in order to be on the safe side. However, tests
with just adding *that* code still showed the problem being present.
The only change that showed behavior change was the change of
translation tables. Thus, this *is* the actual solution to the
instability problems.

Moreover, we need to support crossing a 1 GiB page for placement
of the ELFloader. This is due to the latest firmware on Orin0 in
MUC, named "Jetson UEFI firmware (version 4.1-33958178)", which puts
our image closely below a 1 GiB boundary. Only for tiny image sizes
the boundary will not be crossed. Thus, we do not hard-code the
writing of tables, because the logic for doing so while crossing a
1 GiB boundary is too complicated. Instead, we use a fully dynamic
approach that walks the pagetables in software for a given VA and
inserts missing levels on demand from a preallocated pool of pages.

Only the two top-level pagetables are fixed. This allows for re-use
of all pagetable code, where we only need to distinguish in one (!)
place between hypervisor and non-hyp (or VHE).

Signed-off-by: Matthias Rosenfelder <[email protected]>
  • Loading branch information
mro-github-12345 authored and Andy Bui committed Feb 25, 2024
1 parent 33f00c8 commit 4996de7
Show file tree
Hide file tree
Showing 9 changed files with 444 additions and 95 deletions.
10 changes: 6 additions & 4 deletions elfloader-tool/include/arch-arm/64/mode/structures.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@

#pragma once

/* ARM VMSAv8-64 (with a fully populated last level) has the same number of PTEs
* in all levels (we don't use concatenated pagetables in ELFloader) and each
* table entry is always eight bytes large.
*/
#define BITS_PER_LEVEL (PAGE_BITS - 3)

#define ARM_1GB_BLOCK_BITS 30
#define ARM_2MB_BLOCK_BITS 21

Expand All @@ -26,9 +32,5 @@
#define GET_PMD_INDEX(x) (((word_t)(x) >> (ARM_2MB_BLOCK_BITS)) & MASK(PMD_BITS))

extern uint64_t _boot_pgd_up[BIT(PGD_BITS)];
extern uint64_t _boot_pud_up[BIT(PUD_BITS)];
extern uint64_t _boot_pmd_up[BIT(PMD_BITS)];

extern uint64_t _boot_pgd_down[BIT(PGD_BITS)];
extern uint64_t _boot_pud_down[BIT(PUD_BITS)];

15 changes: 15 additions & 0 deletions elfloader-tool/include/arch-arm/elfloader.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,22 @@ typedef void (*init_arm_kernel_t)(word_t ui_p_reg_start,

/* Enable the mmu. */
extern void arm_enable_mmu(void);

/* These functions are very similar however, there are some small differences
* between the ARMv8 and legacy implementation.
*
* New ARMv8 implementation:
* - Does the MMU disabling. This is to keep the time spent with MMU off low.
* - Is only meant if seL4 runs in EL2.
*/
#if defined(CONFIG_ARCH_AARCH64)
/* Switches MMU-related stuff: pagetables, MAIR & TCR etc. Works also if the MMU
* was off initially. EL2 translation regime only.
*/
extern void arm_switch_to_hyp_tables(void);
#else
extern void arm_enable_hyp_mmu(void);
#endif


/* Setup boot VSpace. */
Expand Down
5 changes: 5 additions & 0 deletions elfloader-tool/include/drivers/uart.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#pragma once

#include <autoconf.h>
#include <drivers/common.h>

#define dev_get_uart(dev) ((struct elfloader_uart_ops *)(dev->drv->ops))
Expand All @@ -16,3 +17,7 @@ struct elfloader_uart_ops {

volatile void *uart_get_mmio(void);
void uart_set_out(struct elfloader_device *out);
#if defined(CONFIG_ARCH_AARCH64)
/* Implemented in mmu.c */
void mmu_set_uart_base(volatile void *base);
#endif
Loading

1 comment on commit 4996de7

@Indanz
Copy link

@Indanz Indanz commented on 4996de7 Feb 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not familiar with Elfloader code, but my impression is that it is getting much more complicated than you would want a simple loader to be.

  • There is nothing wrong with mismatched memory attributes, it can only causes problems if you access the same cache line area via two mismatched mappings.
  • Mapping non-existing physical memory is not a problem in itself, but accessing that memory might (usually it would give a fault or SError for cached writes, but it can also cause the whole bus to hang if it is an FPGA).
  • The main thing mapping device memory cacheable can cause is speculative reads, but in general those shouldn't have side effects that cause hangs.

Changing the code so much changes timing enough that it can mask timing sensitive bugs, so until the root cause of the hangs is found, I am not convinced this actually fixes it.

Have you tried synchronising secondary cores to a known state before proceeding bootup?

Please sign in to comment.