[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Minios-devel] [PATCH v3 06/43] arm64: add the boot code



On Mon, Apr 16, 2018 at 06:13:31PM +0100, Julien Grall wrote:
> Hi,
> 
> On 16/04/18 07:31, Huang Shijie wrote:
> >This patch adds the boot code for arm64:
> >     0.) add the header which contains all the macros to setup the page table
> >     1.) init the MAIR/TCR for 48 bit virtual address.
> >     2.) setup the page table for the code section.
> >     3.) enable the MMU
> >
> >This patch refers to Chen Baozi's patch:
> >      "Initial codes for arm64"
> >
> >Signed-off-by: Huang Shijie <shijie.huang@xxxxxxx>
> >---
> >  arch/arm/arm64/arm64.S        | 286 
> > ++++++++++++++++++++++++++++++++++++++++++
> >  arch/arm/arm64/asm.h          |  18 +++
> >  include/arm/arm64/pagetable.h | 108 ++++++++++++++++
> >  3 files changed, 412 insertions(+)
> >  create mode 100644 arch/arm/arm64/arm64.S
> >  create mode 100644 arch/arm/arm64/asm.h
> >  create mode 100644 include/arm/arm64/pagetable.h
> >
> >diff --git a/arch/arm/arm64/arm64.S b/arch/arm/arm64/arm64.S
> >new file mode 100644
> >index 0000000..b454cc6
> >--- /dev/null
> >+++ b/arch/arm/arm64/arm64.S
> >@@ -0,0 +1,286 @@
> >+#include "asm.h"
> >+#include <arch_limits.h>
> >+#include <arm64/pagetable.h>
> >+#include <xen/xen.h>
> >+
> >+/* This macro will use the x0/x1/x2/x16 */
> >+#define PRINT(_s)                              \
> >+    adr     x2, 97f;                           \
> >+    adr     x1, 98f;                           \
> >+    sub     x1, x1, x2;                        \
> >+    mov     x0, #CONSOLEIO_write;              \
> >+    mov     x16, #__HYPERVISOR_console_io;     \
> >+    hvc     #XEN_HYPERCALL_TAG;                \
> >+    b       99f;                               \
> 
> I see you use the PRINT(...) before enabling the MMU. Per
> include/xen/arch-arm.h:
> 
> "All memory which is shared with other entities in the system
> (including the hypervisor and other guests) must reside in memory
> which is mapped as Normal Inner Write-Back Outer Write-Back Inner-Shareable.
> This applies to:
>  - hypercall arguments passed via a pointer to guest memory.
>  - memory shared via the grant table mechanism (including PV I/O
>    rings etc).
>  - memory shared with the hypervisor (struct shared_info, struct
>    vcpu_info, the grant table, etc)."
> 
> This means that your PRINT may not always appear and provoking other funny
> problem as the cache may have an inconsistent state. So I think you want to
> avoid using those print before MMU is enabled.
okay, I will remove the PRINT.
It is really not necessary.
> 
> >+97: .asciz _s;                                 \
> 
> Don't you want to put the string into the text section?
Ditto.
> 
> >+98: ;                                          \
> >+    .align  2;                                 \
> >+99:                                            \
> >+
> >+    .data
> >+    .globl _boot_stack
> >+    .globl boot_l0_pgtable
> >+    .globl boot_l1_pgtable
> >+    .globl boot_l2_pgtable
> >+    .globl idmap_l0_pgtable
> >+    .globl idmap_l1_pgtable
> 
> Please put the .globl label before each declaration. But I am not sure why
> you need to export most of them.
The code setup the page table may need them, so I should export them.

> 
> >+
> >+    .align 12
> >+boot_l0_pgtable:
> >+    .fill  PAGE_SIZE,1,0
> >+boot_l1_pgtable:
> >+    .fill  PAGE_SIZE,1,0
> >+boot_l2_pgtable:
> >+    .fill  PAGE_SIZE,1,0
> >+idmap_l0_pgtable:
> >+    .fill  PAGE_SIZE,1,0
> >+idmap_l1_pgtable:
> >+    .fill  PAGE_SIZE,1,0
> >+
> >+    .align 12
> >+_boot_stack:
> >+    .fill  __STACK_SIZE,1,0
> 
> Can we please use STACK_SIZE and not __STACK_SIZE.
I will try...
But I remember I met something wrong in compiling when I used STACK_SIZE.

> 
> >+stack_end:
> >+
> >+/*
> >+ * Kernel startup entry point.
> >+ *
> >+ * Please refer to linux kernel file Documentation/arm64/booting.txt
> >+ * for the header format.
> >+ */
> >+    .text
> >+
> >+    b       _start                  /* branch to kernel start, magic */
> >+    .long   0                       /* reserved */
> >+    .quad   0x0                     /* Image load offset from start of RAM 
> >*/
> >+    .quad   _end - _start           /* Effective Image size */
> >+    .quad   2                       /* kernel flags: LE, 4K page size */
> 
> Again, do we really care to have the kernel placed as close as possible to
> the base address of the DRAM?
Do you have a better place to place the kernel?

> 
> For reminder, the documentation of that flag is:
> 
> Bit 3:        Kernel physical placement
>                         0 - 2MB aligned base should be as close as possible
> to the base of DRAM, since memory below it is not accessible via the linear
> mapping
>                         1 - 2MB aligned base may be anywhere in physical
> memory
> 
> How does mini-OS on Arm64 deal with memory below the loaded address of the
> kernel?
We do not use the memory below the loaded address of kernel.
We put kernel at 0xffff000000000000 (48bit).

And we setup the page table for address >= 0xffff000000000000,
the address below 0xffff000000000000 is not accessed.    

> 
> >+    .quad   0                       /* reserved */
> >+    .quad   0                       /* reserved */
> >+    .quad   0                       /* reserved */
> >+    .byte   0x41                    /* Magic number, "ARM\x64" */
> >+    .byte   0x52
> >+    .byte   0x4d
> >+    .byte   0x64
> >+    .long   0                       /* reserved */
> >+
> >+/*
> >+ * Primary CPU general-purpose register settings
> 
> This is fairly unclear to me what you mean by primary. Did you mean,
> "Initial state of the GPR when _start is called?"
yes, We can remove the Primary here, there is only one cpu for mini-os...
> 
> >+ * x0 = physical address of device tree blob (dtb) in system RAM.
> >+ * x1 = 0 (reserved for future use)
> >+ * x2 = 0 (reserved for future use)
> >+ * x3 = 0 (reserved for future use)
> >+ *
> >+ * The registers used by _start:
> >+ * x20 - FDT pointer
> >+ * x22 - offset between PA and VA
> >+ */
> >+ENTRY(_start)
> >+    /* Save the FDT pointer */
> >+    mov     x20, x0
> >+
> >+    /* Calculate where we are */
> >+    bl      _calc_offset
> >+
> >+    PRINT("- Mini-OS booting -\n")
> >+
> >+    PRINT("- Setup CPU -\n")
> >+    /* Setup CPU for turning on the MMU. */
> >+    bl      _setup_cpu
> >+
> >+    PRINT("- Setup booting pagetable -\n")
> 
> I am pretty sure you need to clean the cache for the page-table region here
> to avoid potential dirty lines being evicted and ...
I am not clear about this.
is it possible that there is cache for the paget able region?

Sorry, I will read more document and reply your following comments...

Thanks
Huang Shijie
> 
> >+    /* Setup the initial page table. */
> >+    bl      _setup_initial_pgtable
> >+    mov     x19, x0
> >+
> >+    /* Setup the identity mapping */
> >+    bl      _setup_idmap_pgtable
> 
> Also here because you populated the page-tables with non-cacheable accesses
> (MMU disabled), so you want to remove speculatively loaded cache lines.
> 
> >+
> >+    /* Load TTBRx */
> >+    msr     ttbr1_el1, x19
> >+    msr     ttbr0_el1, x0
> >+    isb
> >+
> >+    /* Turning on MMU */
> >+    tlbi    vmalle1
> 
> Can you please document why the TLB here. In that case it remove stall TLB
> entries.
> 
> >+    dsb     nsh
> >+    isb
> 
> The isb here is not necessary. You can rely on the one after the MMU is
> enabled below.
> 
> >+    ldr     x1, =(SCTLR_M | SCTLR_C | SCTLR_I)
> 
> If you fully overwrite SCTLR_EL1 value, then you need to make sure all RES1
> are set to 1 in ARMv8.0. Otherwise, you are going to have some surprise when
> those bits are defined differently (such as moving to ARMv8.2).
> 
> >+    msr     sctlr_el1, x1
> >+    isb
> >+
> >+    PRINT("- MMU on -\n")
> >+    ldr     x0, =mmu_on
> >+    br      x0
> >+
> >+mmu_on:
> >+    /* Do not use the TTBR0_EL1 any more */
> >+    mrs     x19, tcr_el1
> >+    add     x19, x19, TCR_EPD0
> >+    msr     tcr_el1, x19
> 
> You probably want to flush the TLB here. So you don't end up to use stall
> data.
> 
> >+
> >+    /* Setup stack */
> >+    PRINT("- Setup stack -\n")
> >+    ldr     x1, =stack_end
> >+    mov     sp, x1
> >+
> >+    PRINT("- Jumping to C entry -\n")
> >+    mov     x0, x20                  /* x0 <- device tree (physical 
> >address) */
> >+    mov     x1, x22                  /* x1 <- phys_offset */
> >+
> >+    b      arch_init
> >+ENDPROC(_start)
> >+
> >+/*
> >+ * Get the phys-offset, and save it in x22
> >+ */
> >+_calc_offset:
> >+    ldr     x22, =_start             /* x0 := vaddr(_start)  */
> >+    adr     x21, _start              /* x21 := paddr(_start) */
> >+    sub     x22, x21, x22            /* x22 := phys-offset (paddr - vaddr) 
> >*/
> >+    ret
> >+
> >+/*
> >+ * Setup the memory region attribute;
> >+ * Setup the TCR.
> >+ */
> >+_setup_cpu:
> >+    /*
> >+     * Setup memory attribute type tables
> >+     *
> >+     * Memory region attributes for LPAE:
> >+     *
> >+     *   n = AttrIndx[2:0]
> >+     *                      n       MAIR
> >+     *   DEVICE_nGnRnE      000     00000000 (0x00)
> >+     *   DEVICE_nGnRE       001     00000100 (0x04)
> >+     *   DEVICE_GRE         010     00001100 (0x0c)
> >+     *   NORMAL_NC          011     01000100 (0x44)
> >+     *   NORMAL             100     11111111 (0xff)
> >+     */
> >+    ldr     x0, =(SET_MAIR(0x00, MEM_DEVICE_nGnRnE) | \
> >+                  SET_MAIR(0x04, MEM_DEVICE_nGnRE)  | \
> >+                  SET_MAIR(0x0c, MEM_DEVICE_GRE)    | \
> >+                  SET_MAIR(0x44, MEM_NORMAL_NC)     | \
> >+                  SET_MAIR(0xff, MEM_NORMAL))
> >+    msr     mair_el1, x0
> >+
> >+    /*
> >+     * Setup translation control register (TCR)
> >+     */
> >+    ldr     x0, =(TCR_TxSZ(VA_BITS) | TCR_ASID16 | TCR_TG1_4K | \
> >+                  TCR_IRGN_WBWA | TCR_ORGN_WBWA | TCR_SHARED | 
> >TCR_IPS_48BIT)
> >+    msr     tcr_el1, x0
> >+
> >+    ret
> >+
> >+/*
> >+ * Setup the page table mapping for @addr at @level with @prot.
> >+ *
> >+ * Note: x22 stores the offset between virtual address and physical address.
> >+ */
> >+.macro set_page_table, addr, level, prot
> >+    /* Find the table index in @level, save it in x3  */
> >+.if \level == 0
> >+    lsr     x3, \addr, #L0_SHIFT
> >+    adr     x8, boot_l1_pgtable
> >+    adr     x11, boot_l0_pgtable
> >+.endif
> >+
> >+.if \level == 1
> >+    lsr     x3, \addr, #L1_SHIFT
> >+    adr     x8, boot_l2_pgtable
> >+    adr     x11, boot_l1_pgtable
> >+.endif
> >+
> >+.if \level == 2
> >+    lsr     x3, \addr, #L2_SHIFT
> >+    adr     x11, boot_l2_pgtable
> >+    /* Get the physical address, the @addr should be 2M aligned. */
> >+    add     x8, \addr, x22
> >+.endif
> >+
> >+    and     x3, x3, #Ln_ADDR_MASK
> >+
> >+    /* Build the page table entry */
> >+    ldr     x7, = \prot
> >+    lsr     x9, x8, #PAGE_SHIFT
> >+    orr     x7, x7, x9, lsl #PAGE_SHIFT
> >+
> >+    /* Store entry */
> >+    str     x7, [x11, x3, lsl #3]
> >+.endm
> >+
> >+/*
> >+ * Setup the mapping for code section
> >+ *
> >+ * => null
> >+ * <= x0 -> for TTBR1_EL1
> >+ */
> >+_setup_initial_pgtable:
> >+    /* Start to map the code */
> >+    ldr     x0, =_text                 /* x0 := vaddr(_text)            */
> >+    ldr     x1, =_end                  /* x1 := vaddr(_end)             */
> >+
> >+    set_page_table x0, 0, PT_PT
> >+    set_page_table x0, 1, PT_PT
> >+1:
> >+    set_page_table x0, 2, PT_MEM
> >+
> >+    add     x0, x0, L2_SIZE
> >+    cmp     x1, x0
> >+    b.gt    1b
> >+
> >+    adr     x0, boot_l0_pgtable
> >+    dsb     sy
> >+    ret
> >+
> >+/*
> >+ * Setup the page table mapping for @addr at @level with @prot.
> >+ *
> >+ * Only used for identity mapping.
> >+ */
> >+.macro set_ident_page_table, addr, level, prot
> >+    /* Find the table index in @level, save it in x3  */
> >+.if \level == 0
> >+    lsr     x3, \addr, #L0_SHIFT
> >+    adr     x8, idmap_l1_pgtable
> >+    adr     x11, idmap_l0_pgtable
> >+.endif
> >+
> >+.if \level == 1
> >+    lsr     x3, \addr, #L1_SHIFT
> >+    mov     x8, \addr
> >+    adr     x11, idmap_l1_pgtable
> >+.endif
> >+
> >+    and     x3, x3, #Ln_ADDR_MASK
> >+
> >+    /* Build the page table entry */
> >+    ldr     x7, = \prot
> >+    lsr     x9, x8, #PAGE_SHIFT
> >+    orr     x7, x7, x9, lsl #PAGE_SHIFT
> >+
> >+    /* Store entry */
> >+    str     x7, [x11, x3, lsl #3]
> >+.endm
> >+
> >+/*
> >+ * Setup the page table for TTBR0_EL1:
> >+ *   Mapping the page table for the code section.
> >+ *   We use 48bit address, and just use level 0/1
> >+ *   for the mapping (we do not use level 2 and level 3).
> >+ *
> >+ * => none
> >+ * <= x0 : save the page table pointer for TTBR0_EL1.
> >+ */
> >+_setup_idmap_pgtable:
> >+    /* Create the VA = PA map */
> >+    adr     x0, _text
> >+
> >+    set_ident_page_table x0, 0, PT_PT
> >+    set_ident_page_table x0, 1, PT_MEM
> >+
> >+    adr     x0, idmap_l0_pgtable
> >+    dsb     sy
> >+    ret
> >diff --git a/arch/arm/arm64/asm.h b/arch/arm/arm64/asm.h
> >new file mode 100644
> >index 0000000..3a498c4
> >--- /dev/null
> >+++ b/arch/arm/arm64/asm.h
> 
> Again, nothing in this header look arm64 specific.
> 
> >@@ -0,0 +1,18 @@
> >+#ifndef __ASM_H__
> >+#define __ASM_H__
> >+
> >+#define ALIGN   .align 4
> >+
> >+#define ENTRY(name) \
> >+    .globl name; \
> >+    ALIGN; \
> >+    name:
> >+
> >+#define END(name) \
> >+    .size name, .-name
> >+
> >+#define ENDPROC(name) \
> >+    .type name, @function; \
> >+    END(name)
> >+
> >+#endif /* __ASM_H__ */
> >diff --git a/include/arm/arm64/pagetable.h b/include/arm/arm64/pagetable.h
> >new file mode 100644
> >index 0000000..8e2384f
> >--- /dev/null
> >+++ b/include/arm/arm64/pagetable.h
> >@@ -0,0 +1,108 @@
> >+#ifndef __ARM64_PAGE_TABLE__
> >+
> >+#define __ARM64_PAGE_TABLE__
> >+
> >+/* TCR flags */
> >+#define TCR_TxSZ(x)         ((((64) - (x)) << 16) | (((64) - (x)) << 0))
> >+#define TCR_IRGN_WBWA       (((1) << 8) | ((1) << 24))
> >+#define TCR_ORGN_WBWA       (((1) << 10) | ((1) << 26))
> >+#define TCR_SHARED          (((3) << 12) | ((3) << 28))
> >+#define TCR_ASID16          ((1) << 36)
> >+#define TCR_IPS_48BIT       ((5) << 32)
> >+#define TCR_TG1_4K          ((2) << 30)
> 
> Those flags are only going to work well in assembly. In C, you will have
> truncation problem as the shift is based on integer (aka 32-bit).
> 
> Also can you remove parenthese around all plain values. I.e s/(2)/2/. This
> will make the code more readable.
> 
> >+#define TCR_EPD0            (1 << 7)
> >+
> >+/* Max virtual address */
> >+#define     VM_MAX_ADDRESS      (0xffffffffffffffff)
> >+
> >+/* Number of virtual address bits */
> >+#define VA_BITS             48
> >+
> >+/*
> >+ * Memory types available.
> >+ */
> >+#define MEM_DEVICE_nGnRnE    0
> >+#define MEM_DEVICE_nGnRE     1
> >+#define MEM_DEVICE_GRE       2
> >+#define MEM_NORMAL_NC        3
> >+#define MEM_NORMAL           4
> >+
> >+#define SET_MAIR(attr, mt)  ((attr) << ((mt) * 8))
> >+
> >+/* SCTLR_EL1 - System Control Register */
> >+#define SCTLR_M             (1 << 0)
> >+#define SCTLR_C             (1 << 2)
> >+#define SCTLR_I             (1 << 12)
> >+
> >+/* Level 0 table, 512GiB per entry */
> >+#define L0_SHIFT            39
> >+#define L0_SIZE             (1UL << L0_SHIFT)
> >+#define L0_INVAL            0x0 /* An invalid address */
> >+#define L0_TABLE            0x3 /* A next-level table */
> >+
> >+/* Level 1 table, 1GiB per entry */
> >+#define L1_SHIFT            30
> >+#define L1_SIZE             (1 << L1_SHIFT)
> 
> Please try to be consistent to use 1UL everywhere for *_SIZE.
> 
> >+#define L1_OFFSET           (L1_SIZE - 1)
> >+#define L1_INVAL            L0_INVAL
> >+#define L1_BLOCK            0x1
> >+#define L1_TABLE            L0_TABLE
> >+#define L1_MASK             (~(L1_SIZE-1))
> >+
> >+/* Level 2 table, 2MiB per entry */
> >+#define L2_SHIFT            21
> >+#define L2_SIZE             (1 << L2_SHIFT)
> >+#define L2_OFFSET           (L2_SIZE - 1)
> >+#define L2_INVAL            L0_INVAL
> >+#define L2_BLOCK            L1_BLOCK
> >+#define L2_TABLE            L0_TABLE
> >+#define L2_MASK             (~(L2_SIZE-1))
> >+
> >+/* Level 3 table, 4KiB per entry */
> >+#define L3_SHIFT            12
> >+#define L3_SIZE             (1 << L3_SHIFT)
> >+#define L3_OFFSET           (L3_SIZE - 1)
> >+#define L3_INVAL            0x0
> >+#define L3_PAGE             0x3
> >+#define L3_MASK             (~(L3_SIZE-1))
> >+
> >+#define Ln_ENTRIES          (1 << 9)
> >+#define Ln_ADDR_MASK        (Ln_ENTRIES - 1)
> >+
> >+#define ATTR_MASK_L         0xfff
> >+
> >+#define l0_pgt_idx(va)      (((va) >> L0_SHIFT) & Ln_ADDR_MASK)
> >+#define l1_pgt_idx(va)      (((va) >> L1_SHIFT) & Ln_ADDR_MASK)
> >+#define l2_pgt_idx(va)      (((va) >> L2_SHIFT) & Ln_ADDR_MASK)
> >+#define l3_pgt_idx(va)      (((va) >> L3_SHIFT) & Ln_ADDR_MASK)
> >+
> >+/*
> >+ * Lower attributes fields in Stage 1 VMSAv8-A Block and Page descriptor
> >+ */
> >+#define ATTR_nG            (1 << 11)
> >+#define ATTR_AF            (1 << 10)
> >+#define ATTR_SH(x)         ((x) << 8)
> >+#define ATTR_SH_MASK       ATTR_SH(3)
> >+#define ATTR_SH_NS         0               /* Non-shareable */
> >+#define ATTR_SH_OS         2               /* Outer-shareable */
> >+#define ATTR_SH_IS         3               /* Inner-shareable */
> >+#define ATTR_AP_RW_BIT     (1 << 7)
> >+#define ATTR_AP(x)         ((x) << 6)
> >+#define ATTR_AP_MASK       ATTR_AP(3)
> >+#define ATTR_AP_RW         (0 << 1)
> >+#define ATTR_AP_RO         (1 << 1)
> >+#define ATTR_AP_USER       (1 << 0)
> >+#define ATTR_NS            (1 << 5)
> >+#define ATTR_IDX(x)        ((x) << 2)
> >+#define ATTR_IDX_MASK      (7 << 2)
> >+
> >+#define MEM_DEF_ATTR       
> >(ATTR_AF|ATTR_SH(ATTR_SH_IS)|ATTR_IDX(MEM_NORMAL))
> >+#define MEM_NC_ATTR        
> >(ATTR_AF|ATTR_SH(ATTR_SH_IS)|ATTR_IDX(MEM_NORMAL_NC))
> >+#define MEM_DEV_ATTR       
> >(ATTR_AF|ATTR_SH(ATTR_SH_IS)|ATTR_IDX(MEM_DEVICE_nGnRnE))
> >+
> >+#define MEM_RO_ATTR        (MEM_DEF_ATTR|ATTR_AP(ATTR_AP_RO))
> >+
> >+#define PT_PT              (L0_TABLE)
> >+#define PT_MEM             (MEM_DEF_ATTR | L1_BLOCK)
> 
> The naming for those 2 macros don't make sense based on the value. For
> instance, PT_PT gives the impression to be attribute for table and does not
> necessarily need to be level-0.
> 
> But I just notice that you define L1_TABLE as L0_TABLE. This is so
> confusing, you probably want to do some renaming some where. Same for
> L1_BLOCK.
> 
> Cheers,
> 
> -- 
> Julien Grall

_______________________________________________
Minios-devel mailing list
Minios-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/minios-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.