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

[Minios-devel] [PATCH v2 29/47] arm64: init the memory system



This patch do followings to initialize the memory system:
    0.) Map extra 2M for the first_free_pfn.

    1.) add arch_mm_preinit() to setup the page table for Device Tree.

    2.) add functions to setup the page table, such as
        early_alloc_page()/build_pagetable()/build_pud/build_pmd.

    3.) Just as the x86 does, limits the max memory size to MAX_MEM_SIZE,
        the min memory size to MIN_MEM_SIZE,

    4.) and setup the page allocator in arch_init_mm().
        The init_pagetable() will find the best block mapping level to setup
        the page table.

Signed-off-by: Huang Shijie <shijie.huang@xxxxxxx>
---
 arch/arm/arm64/arm64.S |   3 +
 arch/arm/mm.c          | 253 +++++++++++++++++++++++++++++++++++++++++++++++++
 arch/arm/setup.c       |   3 +
 include/arm/arch_mm.h  |   7 ++
 4 files changed, 266 insertions(+)

diff --git a/arch/arm/arm64/arm64.S b/arch/arm/arm64/arm64.S
index 93ffc89..4e9c042 100644
--- a/arch/arm/arm64/arm64.S
+++ b/arch/arm/arm64/arm64.S
@@ -232,6 +232,9 @@ _setup_initial_pgtable:
     ldr     x0, =_text                 /* x0 := vaddr(_text)            */
     ldr     x1, =_end                  /* x1 := vaddr(_end)             */
 
+    /* Map extra 2M for first_free_pfn */
+    add     x1, x1, L2_SIZE
+
     set_page_table x0, 0, PT_PT
     set_page_table x0, 1, PT_PT
 1:
diff --git a/arch/arm/mm.c b/arch/arm/mm.c
index d98fad8..23a23e1 100644
--- a/arch/arm/mm.c
+++ b/arch/arm/mm.c
@@ -23,6 +23,252 @@ unsigned long allocate_ondemand(unsigned long n, unsigned 
long alignment)
     BUG();
 }
 
+#if defined(__aarch64__)
+
+#include <arm64/pagetable.h>
+
+extern lpae_t boot_l0_pgtable[512];
+
+static inline void set_pgt_entry(lpae_t *ptr, lpae_t val)
+{
+    *ptr = val;
+    dsb(ishst);
+    isb();
+}
+
+static void build_pte(lpae_t *pmd, unsigned long vaddr, unsigned long vend,
+                      paddr_t phys, uint64_t mem_type)
+{
+    lpae_t *pte;
+
+    pte = (lpae_t *)to_virt((*pmd) & ~ATTR_MASK_L) + l3_pgt_idx(vaddr);
+    do {
+        set_pgt_entry(pte, (phys & L3_MASK) | mem_type | L3_PAGE);
+
+        vaddr += L3_SIZE;
+        phys += L3_SIZE;
+        pte++;
+    } while (vaddr < vend);
+}
+
+static int build_pmd(lpae_t *pud, unsigned long vaddr, unsigned long vend,
+                      paddr_t phys, uint64_t mem_type,
+                      paddr_t (*new_page)(void), int level)
+{
+    lpae_t *pmd;
+    unsigned long next;
+
+    pmd = (lpae_t *)to_virt((*pud) & ~ATTR_MASK_L) + l2_pgt_idx(vaddr);
+    do {
+        if (level == 2) {
+             set_pgt_entry(pmd, (phys & L2_MASK) | mem_type | L2_BLOCK);
+        } else {
+             next = vaddr + L2_SIZE;
+             if (next > vend)
+                 next = vend;
+
+             if ((*pmd) == L2_INVAL) {
+                 paddr_t newpage = new_page();
+                 if (!newpage)
+                         return -ENOMEM;
+                 set_pgt_entry(pmd, newpage | PT_PT);
+             }
+
+             build_pte(pmd, vaddr, next, phys, mem_type);
+        }
+
+        vaddr += L2_SIZE;
+        phys += L2_SIZE;
+        pmd++;
+    } while (vaddr < vend);
+
+    return 0;
+}
+
+static int build_pud(lpae_t *pgd, unsigned long vaddr, unsigned long vend,
+                      paddr_t phys, uint64_t mem_type,
+                      paddr_t (*new_page)(void), int level)
+{
+    lpae_t *pud;
+    unsigned long next;
+    int ret;
+
+    pud = (lpae_t *)to_virt((*pgd) & ~ATTR_MASK_L) + l1_pgt_idx(vaddr);
+    do {
+        if (level == 1) {
+             set_pgt_entry(pud, (phys & L1_MASK) | mem_type | L1_BLOCK);
+        } else {
+             next = vaddr + L1_SIZE;
+             if (next > vend)
+                 next = vend;
+
+             if ((*pud) == L1_INVAL) {
+                 paddr_t newpage = new_page();
+                 if (!newpage)
+                     return -ENOMEM;
+                 set_pgt_entry(pud, newpage | PT_PT);
+             }
+
+             ret = build_pmd(pud, vaddr, next, phys, mem_type, new_page, 
level);
+             if (ret)
+                 return ret;
+        }
+
+        vaddr += L1_SIZE;
+        phys += L1_SIZE;
+        pud++;
+    } while (vaddr < vend);
+
+    return 0;
+}
+
+static int build_pagetable(unsigned long vaddr, unsigned long start_pfn,
+                     unsigned long max_pfn, uint64_t mem_type,
+                     paddr_t (*new_page)(void), int level)
+{
+    paddr_t p_start;
+    unsigned long v_end, next;
+    lpae_t *pgd;
+    int ret;
+
+    v_end = vaddr + max_pfn * PAGE_SIZE;
+    p_start = PFN_PHYS(start_pfn);
+
+    pgd = &boot_l0_pgtable[l0_pgt_idx(vaddr)];
+
+    do {
+        next = (vaddr + L0_SIZE);
+        if (next > v_end)
+            next = v_end;
+
+        if ((*pgd) == L0_INVAL) {
+            paddr_t newpage = new_page();
+            if (!newpage)
+                return -ENOMEM;
+            set_pgt_entry(pgd, newpage | PT_PT);
+        }
+
+        ret = build_pud(pgd, vaddr, next, p_start, mem_type, new_page, level);
+        if (ret)
+            return ret;
+
+        p_start += next - vaddr;
+        vaddr = next;
+        pgd++;
+    } while (vaddr != v_end);
+
+    return 0;
+}
+
+/*
+ * Before the page allocator is ready, we use first_free_pfn to record
+ * the first free page. The first_free_pfn will be increased by
+ * early_alloc_page().
+ */
+static unsigned long first_free_pfn;
+
+/* The pfn for MIN_MEM_SIZE */
+static unsigned long min_mem_pfn;
+
+static paddr_t early_alloc_page(void)
+{
+    paddr_t new_page;
+
+    memset(pfn_to_virt(first_free_pfn), 0, PAGE_SIZE);
+    dsb(ishst);
+
+    new_page = PFN_PHYS(first_free_pfn);
+    first_free_pfn++;
+    ASSERT(first_free_pfn < min_mem_pfn);
+    return new_page;
+}
+
+static int init_pagetable_ok;
+/*
+ * This function will setup the page table for the memory system.
+ */
+void init_pagetable(unsigned long *start_pfn, unsigned long base,
+                    unsigned long size)
+{
+    unsigned long vaddr = (unsigned long)to_virt(base);
+    paddr_t phys = base;
+    paddr_t sz = L1_SIZE;
+    lpae_t *pgd;
+    lpae_t *pud;
+    int level;
+
+    do {
+        /*
+         * We cannot set block mapping for PGD(level 0),
+         * but we can set block mapping for PUD(level 1) and PMD(level 2).
+         * Get the proper level for build_pagetable().
+         */
+        if (size >= L1_SIZE) {
+            pgd = &boot_l0_pgtable[l0_pgt_idx(vaddr)];
+            if ((*pgd) == L0_INVAL) {
+                 level = 1;
+            } else {
+                 pud = (lpae_t *)to_virt((*pgd) & ~ATTR_MASK_L) + 
l1_pgt_idx(vaddr);
+                 if ((*pud) == L1_INVAL)
+                     level = 1;
+                 else
+                     level = 2;
+            }
+        } else {
+             sz = size & L2_MASK;
+             level = 2;
+        }
+
+        build_pagetable(vaddr, PHYS_PFN(phys), PFN_UP(sz),
+                        MEM_DEF_ATTR, early_alloc_page, level);
+
+        vaddr += sz;
+        phys  += sz;
+        size -= sz;
+    } while (size > L2_SIZE);
+
+    /* Use the page mapping (level 3) for the left */
+    if (size)
+        build_pagetable(vaddr, PHYS_PFN(phys), PFN_UP(size),
+                        MEM_DEF_ATTR, early_alloc_page, 3);
+
+    *start_pfn = first_free_pfn;
+    init_pagetable_ok = 1;
+}
+
+void arch_mm_preinit(void *dtb_pointer)
+{
+    paddr_t **dtb_p = dtb_pointer;
+    paddr_t *dtb = *dtb_p;
+    uintptr_t end = (uintptr_t) &_end;
+
+    dtb = to_virt(((paddr_t)dtb));
+    first_free_pfn = PFN_UP(to_phys(end));
+    min_mem_pfn = PFN_UP(to_phys(_text) + MIN_MEM_SIZE);
+
+    /*
+     * Setup the mapping for Device Tree, only map 2M(L2_SIZE) size.
+     *
+     * Note: The early_alloc_page() will increase @first_free_pfn.
+     */
+    build_pagetable((unsigned long)dtb, virt_to_pfn((unsigned long)dtb),
+                    PHYS_PFN(L2_SIZE), MEM_DEF_ATTR, early_alloc_page, 2);
+
+    *dtb_p = dtb;
+}
+
+#else
+void arch_mm_preinit(void *dtb_pointer)
+{
+}
+
+void init_pagetable(unsigned long *start_pfn, unsigned long base,
+                    unsigned long size)
+{
+}
+
+#endif
+
 void arch_init_mm(unsigned long *start_pfn_p, unsigned long *max_pfn_p)
 {
     int memory;
@@ -65,6 +311,11 @@ void arch_init_mm(unsigned long *start_pfn_p, unsigned long 
*max_pfn_p)
     end = (uintptr_t) &_end;
     mem_base = fdt64_to_cpu(regs[0]);
     mem_size = fdt64_to_cpu(regs[1]);
+
+    BUG_ON(mem_size < MIN_MEM_SIZE);
+    if (mem_size > MAX_MEM_SIZE)
+        mem_size = MAX_MEM_SIZE;
+
     printk("Found memory at 0x%llx (len 0x%llx)\n",
             (unsigned long long) mem_base, (unsigned long long) mem_size);
 
@@ -73,6 +324,8 @@ void arch_init_mm(unsigned long *start_pfn_p, unsigned long 
*max_pfn_p)
     heap_len = mem_size - (PFN_PHYS(*start_pfn_p) - mem_base);
     *max_pfn_p = *start_pfn_p + PFN_DOWN(heap_len);
 
+    init_pagetable(start_pfn_p, mem_base, mem_size);
+
     printk("Using pages %lu to %lu as free space for heap.\n", *start_pfn_p, 
*max_pfn_p);
 
     /* The device tree is probably in memory that we're about to hand over to 
the page
diff --git a/arch/arm/setup.c b/arch/arm/setup.c
index 27bea4a..ab82eda 100644
--- a/arch/arm/setup.c
+++ b/arch/arm/setup.c
@@ -29,6 +29,9 @@ void arch_init(void *dtb_pointer, paddr_t physical_offset)
 
     xprintk("Virtual -> physical offset = %"PRIpaddr" \n", 
physical_address_offset);
 
+    /* Do the preparations */
+    arch_mm_preinit(&dtb_pointer);
+
     xprintk("Checking DTB at %p...\n", dtb_pointer);
 
     if ((r = fdt_check_header(dtb_pointer))) {
diff --git a/include/arm/arch_mm.h b/include/arm/arch_mm.h
index 231247c..7bfb942 100644
--- a/include/arm/arch_mm.h
+++ b/include/arm/arch_mm.h
@@ -4,11 +4,17 @@
 #if defined(__arm__)
 typedef uint32_t paddr_t;
 #define PRIpaddr "x"
+#define MIN_MEM_SIZE            (0x400000)
+#define MAX_MEM_SIZE            (~0UL)
 #else
 typedef uint64_t paddr_t;
 #define PRIpaddr "lx"
+#define MIN_MEM_SIZE            (0x400000)
+#define MAX_MEM_SIZE            (1UL << 39)
 #endif
 
+typedef uint64_t lpae_t;
+
 extern char _text, _etext, _erodata, _edata, _end, __bss_start;
 extern int _boot_stack[];
 extern int _boot_stack_end[];
@@ -35,6 +41,7 @@ extern paddr_t physical_address_offset;
 
 #define virtual_to_mfn(_virt)     virt_to_mfn(_virt)
 
+void arch_mm_preinit(void *dtb_pointer);
 // FIXME
 #define map_frames(f, n) (NULL)
 
-- 
2.7.4


_______________________________________________
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®.