mmu.c 18.0 KB
Newer Older
1
/*
S
Shell 已提交
2
 * Copyright (c) 2006-2018, RT-Thread Development Team
3
 *
B
bigmagic 已提交
4
 * SPDX-License-Identifier: Apache-2.0
5 6
 *
 * Change Logs:
G
GuEe-GUI 已提交
7
 * Date           Author       Notes
S
Shell 已提交
8
 * 2012-01-10     bernard      porting to AM1808
9
 */
B
bigmagic 已提交
10

S
Shell 已提交
11
#include <board.h>
12 13 14 15 16
#include <rthw.h>
#include <rtthread.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
B
bigmagic 已提交
17

18 19
#include "mm_aspace.h"
#include "mm_page.h"
S
Shell 已提交
20
#include "mmu.h"
21
#include "tlb.h"
S
Shell 已提交
22 23

#ifdef RT_USING_SMART
24
#include "ioremap.h"
S
Shell 已提交
25 26 27
#include <lwp_mm.h>
#endif

28 29 30
#define DBG_TAG "hw.mmu"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
S
Shell 已提交
31

32 33 34 35 36 37 38 39 40 41 42
#define MMU_LEVEL_MASK   0x1ffUL
#define MMU_LEVEL_SHIFT  9
#define MMU_ADDRESS_BITS 39
#define MMU_ADDRESS_MASK 0x0000fffffffff000UL
#define MMU_ATTRIB_MASK  0xfff0000000000ffcUL

#define MMU_TYPE_MASK  3UL
#define MMU_TYPE_USED  1UL
#define MMU_TYPE_BLOCK 1UL
#define MMU_TYPE_TABLE 3UL
#define MMU_TYPE_PAGE  3UL
S
Shell 已提交
43 44 45 46 47

#define MMU_TBL_BLOCK_2M_LEVEL 2
#define MMU_TBL_PAGE_4k_LEVEL  3
#define MMU_TBL_LEVEL_NR       4

48
volatile unsigned long MMUTable[512] __attribute__((aligned(4 * 1024)));
49

S
Shell 已提交
50
struct mmu_level_info
51
{
S
Shell 已提交
52 53 54
    unsigned long *pos;
    void *page;
};
55

S
Shell 已提交
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
static void _kenrel_unmap_4K(unsigned long *lv0_tbl, void *v_addr)
{
    int level;
    unsigned long va = (unsigned long)v_addr;
    unsigned long *cur_lv_tbl = lv0_tbl;
    unsigned long page;
    unsigned long off;
    struct mmu_level_info level_info[4];
    int ref;
    int level_shift = MMU_ADDRESS_BITS;
    unsigned long *pos;

    rt_memset(level_info, 0, sizeof level_info);
    for (level = 0; level < MMU_TBL_LEVEL_NR; level++)
    {
        off = (va >> level_shift);
        off &= MMU_LEVEL_MASK;
        page = cur_lv_tbl[off];
        if (!(page & MMU_TYPE_USED))
        {
            break;
        }
        if ((page & MMU_TYPE_MASK) == MMU_TYPE_BLOCK)
        {
            break;
        }
        level_info[level].pos = cur_lv_tbl + off;
        cur_lv_tbl = (unsigned long *)(page & MMU_ADDRESS_MASK);
        cur_lv_tbl = (unsigned long *)((unsigned long)cur_lv_tbl - PV_OFFSET);
        level_info[level].page = cur_lv_tbl;
        level_shift -= MMU_LEVEL_SHIFT;
    }

    level = MMU_TBL_PAGE_4k_LEVEL;
    pos = level_info[level].pos;
    if (pos)
G
GuEe-GUI 已提交
92
    {
S
Shell 已提交
93 94
        *pos = (unsigned long)RT_NULL;
        rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, pos, sizeof(void *));
G
GuEe-GUI 已提交
95
    }
S
Shell 已提交
96 97 98
    level--;

    while (level >= 0)
G
GuEe-GUI 已提交
99
    {
S
Shell 已提交
100 101 102 103 104 105 106 107 108 109 110 111 112
        pos = level_info[level].pos;
        if (pos)
        {
            void *cur_page = level_info[level].page;
            ref = rt_page_ref_get(cur_page, 0);
            if (ref == 1)
            {
                *pos = (unsigned long)RT_NULL;
                rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, pos, sizeof(void *));
            }
            rt_pages_free(cur_page, 0);
        }
        level--;
G
GuEe-GUI 已提交
113
    }
114

S
Shell 已提交
115 116 117
    return;
}

118 119
static int _kenrel_map_4K(unsigned long *lv0_tbl, void *vaddr, void *paddr,
                          unsigned long attr)
S
Shell 已提交
120 121 122 123 124 125
{
    int ret = 0;
    int level;
    unsigned long *cur_lv_tbl = lv0_tbl;
    unsigned long page;
    unsigned long off;
126 127
    intptr_t va = (intptr_t)vaddr;
    intptr_t pa = (intptr_t)paddr;
S
Shell 已提交
128 129 130
    int level_shift = MMU_ADDRESS_BITS;

    if (va & ARCH_PAGE_MASK)
131
    {
S
Shell 已提交
132
        return MMU_MAP_ERROR_VANOTALIGN;
B
bigmagic 已提交
133
    }
S
Shell 已提交
134
    if (pa & ARCH_PAGE_MASK)
B
bigmagic 已提交
135
    {
S
Shell 已提交
136
        return MMU_MAP_ERROR_PANOTALIGN;
B
bigmagic 已提交
137
    }
S
Shell 已提交
138 139 140 141 142 143 144 145 146 147 148 149 150
    for (level = 0; level < MMU_TBL_PAGE_4k_LEVEL; level++)
    {
        off = (va >> level_shift);
        off &= MMU_LEVEL_MASK;
        if (!(cur_lv_tbl[off] & MMU_TYPE_USED))
        {
            page = (unsigned long)rt_pages_alloc(0);
            if (!page)
            {
                ret = MMU_MAP_ERROR_NOPAGE;
                goto err;
            }
            rt_memset((void *)page, 0, ARCH_PAGE_SIZE);
W
wangxiaoyao 已提交
151
            rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, (void *)page, ARCH_PAGE_SIZE);
S
Shell 已提交
152
            cur_lv_tbl[off] = (page + PV_OFFSET) | MMU_TYPE_TABLE;
W
wangxiaoyao 已提交
153
            rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, cur_lv_tbl + off, sizeof(void *));
S
Shell 已提交
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
        }
        else
        {
            page = cur_lv_tbl[off];
            page &= MMU_ADDRESS_MASK;
            /* page to va */
            page -= PV_OFFSET;
            rt_page_ref_inc((void *)page, 0);
        }
        page = cur_lv_tbl[off];
        if ((page & MMU_TYPE_MASK) == MMU_TYPE_BLOCK)
        {
            /* is block! error! */
            ret = MMU_MAP_ERROR_CONFLICT;
            goto err;
        }
        cur_lv_tbl = (unsigned long *)(page & MMU_ADDRESS_MASK);
        cur_lv_tbl = (unsigned long *)((unsigned long)cur_lv_tbl - PV_OFFSET);
        level_shift -= MMU_LEVEL_SHIFT;
    }
    /* now is level page */
    attr &= MMU_ATTRIB_MASK;
    pa |= (attr | MMU_TYPE_PAGE); /* page */
    off = (va >> ARCH_PAGE_SHIFT);
    off &= MMU_LEVEL_MASK;
    cur_lv_tbl[off] = pa; /* page */
    rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, cur_lv_tbl + off, sizeof(void *));
    return ret;
err:
    _kenrel_unmap_4K(lv0_tbl, (void *)va);
    return ret;
}
G
GuEe-GUI 已提交
186

187 188
void *rt_hw_mmu_map(rt_aspace_t aspace, void *v_addr, void *p_addr, size_t size,
                    size_t attr)
S
Shell 已提交
189
{
190
    int ret = -1;
G
GuEe-GUI 已提交
191

192 193 194 195 196
    void *unmap_va = v_addr;
    size_t npages = size >> ARCH_PAGE_SHIFT;

    // TODO trying with HUGEPAGE here
    while (npages--)
B
bigmagic 已提交
197
    {
W
wangxiaoyao 已提交
198
        MM_PGTBL_LOCK(aspace);
199
        ret = _kenrel_map_4K(aspace->page_table, v_addr, p_addr, attr);
W
wangxiaoyao 已提交
200 201
        MM_PGTBL_UNLOCK(aspace);

B
bigmagic 已提交
202
        if (ret != 0)
203
        {
204 205 206
            /* error, undo map */
            while (unmap_va != v_addr)
            {
W
wangxiaoyao 已提交
207
                MM_PGTBL_LOCK(aspace);
208
                _kenrel_unmap_4K(aspace->page_table, (void *)unmap_va);
W
wangxiaoyao 已提交
209
                MM_PGTBL_UNLOCK(aspace);
W
wangxiaoyao 已提交
210
                unmap_va += ARCH_PAGE_SIZE;
211 212
            }
            break;
S
Shell 已提交
213
        }
214 215
        v_addr += ARCH_PAGE_SIZE;
        p_addr += ARCH_PAGE_SIZE;
S
Shell 已提交
216
    }
217 218 219 220 221 222 223

    if (ret == 0)
    {
        return unmap_va;
    }

    return NULL;
S
Shell 已提交
224 225
}

226
void rt_hw_mmu_unmap(rt_aspace_t aspace, void *v_addr, size_t size)
S
Shell 已提交
227
{
228 229
    // caller guarantee that v_addr & size are page aligned
    size_t npages = size >> ARCH_PAGE_SHIFT;
S
Shell 已提交
230

231 232 233 234
    if (!aspace->page_table)
    {
        return;
    }
S
Shell 已提交
235

236 237
    while (npages--)
    {
W
wangxiaoyao 已提交
238
        MM_PGTBL_LOCK(aspace);
239
        _kenrel_unmap_4K(aspace->page_table, v_addr);
W
wangxiaoyao 已提交
240
        MM_PGTBL_UNLOCK(aspace);
241 242
        v_addr += ARCH_PAGE_SIZE;
    }
S
Shell 已提交
243 244
}

245
void rt_hw_aspace_switch(rt_aspace_t aspace)
S
Shell 已提交
246
{
247 248 249
    if (aspace != &rt_kernel_space)
    {
        void *pgtbl = aspace->page_table;
W
wangxiaoyao 已提交
250
        pgtbl = rt_kmem_v2p(pgtbl);
251 252 253 254 255 256 257 258 259 260 261 262
        uintptr_t tcr;

        __asm__ volatile("msr ttbr0_el1, %0" ::"r"(pgtbl) : "memory");

        __asm__ volatile("mrs %0, tcr_el1" : "=r"(tcr));
        tcr &= ~(1ul << 7);
        __asm__ volatile("msr tcr_el1, %0\n"
                         "isb" ::"r"(tcr)
                         : "memory");

        rt_hw_tlb_invalidate_all_local();
    }
S
Shell 已提交
263 264
}

265
void rt_hw_mmu_ktbl_set(unsigned long tbl)
S
Shell 已提交
266
{
267 268 269 270 271 272 273 274
#ifdef RT_USING_SMART
    tbl += PV_OFFSET;
    __asm__ volatile("msr TTBR1_EL1, %0\n dsb sy\nisb" ::"r"(tbl) : "memory");
#else
    __asm__ volatile("msr TTBR0_EL1, %0\n dsb sy\nisb" ::"r"(tbl) : "memory");
#endif
    __asm__ volatile("tlbi vmalle1\n dsb sy\nisb" ::: "memory");
    __asm__ volatile("ic ialluis\n dsb sy\nisb" ::: "memory");
S
Shell 已提交
275 276
}

277 278 279 280 281 282 283 284 285 286 287 288 289
/**
 * @brief setup Page Table for kernel space. It's a fixed map
 * and all mappings cannot be changed after initialization.
 *
 * Memory region in struct mem_desc must be page aligned,
 * otherwise is a failure and no report will be
 * returned.
 *
 * @param mmu_info
 * @param mdesc
 * @param desc_nr
 */
void rt_hw_mmu_setup(rt_aspace_t aspace, struct mem_desc *mdesc, int desc_nr)
S
Shell 已提交
290
{
291 292
    void *err;
    for (size_t i = 0; i < desc_nr; i++)
S
Shell 已提交
293
    {
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
        size_t attr;
        switch (mdesc->attr)
        {
        case NORMAL_MEM:
            attr = MMU_MAP_K_RWCB;
            break;
        case NORMAL_NOCACHE_MEM:
            attr = MMU_MAP_K_RWCB;
            break;
        case DEVICE_MEM:
            attr = MMU_MAP_K_DEVICE;
            break;
        default:
            attr = MMU_MAP_K_DEVICE;
        }

        struct rt_mm_va_hint hint = {.flags = MMF_MAP_FIXED,
                                    .limit_start = aspace->start,
                                    .limit_range_size = aspace->size,
                                    .map_size = mdesc->vaddr_end -
                                                mdesc->vaddr_start + 1,
                                    .prefer = (void *)mdesc->vaddr_start};

317 318
        if (mdesc->paddr_start == (rt_size_t)ARCH_MAP_FAILED)
            mdesc->paddr_start = mdesc->vaddr_start + PV_OFFSET;
W
wangxiaoyao 已提交
319 320
        int retval;
        retval = rt_aspace_map_phy_static(aspace, &mdesc->varea, &hint, attr,
321
                                 mdesc->paddr_start >> MM_PAGE_SHIFT, &err);
W
wangxiaoyao 已提交
322 323 324 325 326
        if (retval)
        {
            LOG_E("%s: map failed with code %d", retval);
            RT_ASSERT(0);
        }
327
        mdesc++;
S
Shell 已提交
328
    }
329 330 331

    rt_hw_mmu_ktbl_set((unsigned long)rt_kernel_space.page_table);
    rt_page_cleanup();
S
Shell 已提交
332 333
}

334

S
Shell 已提交
335
#ifdef RT_USING_SMART
336 337 338 339 340
static inline void _init_region(void *vaddr, size_t size)
{
    rt_ioremap_start = vaddr;
    rt_ioremap_size = size;
    rt_mpr_start = rt_ioremap_start - rt_mpr_size;
S
Shell 已提交
341
}
342
#else
S
Shell 已提交
343

344 345
#define RTOS_VEND ((void *)0xfffffffff000UL)
static inline void _init_region(void *vaddr, size_t size)
S
Shell 已提交
346
{
347
    rt_mpr_start = RTOS_VEND - rt_mpr_size;
S
Shell 已提交
348
}
349
#endif
S
Shell 已提交
350 351 352 353 354 355 356 357 358 359 360 361

/**
 * This function will initialize rt_mmu_info structure.
 *
 * @param mmu_info   rt_mmu_info structure
 * @param v_address  virtual address
 * @param size       map size
 * @param vtable     mmu table
 * @param pv_off     pv offset in kernel space
 *
 * @return 0 on successful and -1 for fail
 */
362 363
int rt_hw_mmu_map_init(rt_aspace_t aspace, void *v_address, size_t size,
                       size_t *vtable, size_t pv_off)
S
Shell 已提交
364 365 366
{
    size_t va_s, va_e;

367
    if (!aspace || !vtable)
S
Shell 已提交
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
    {
        return -1;
    }

    va_s = (size_t)v_address;
    va_e = (size_t)v_address + size - 1;

    if (va_e < va_s)
    {
        return -1;
    }

    va_s >>= ARCH_SECTION_SHIFT;
    va_e >>= ARCH_SECTION_SHIFT;

    if (va_s == 0)
    {
        return -1;
    }

388 389 390 391 392 393 394 395
#ifdef RT_USING_SMART
    rt_aspace_init(aspace, (void *)KERNEL_VADDR_START, 0 - KERNEL_VADDR_START,
                   vtable);
#else
    rt_aspace_init(aspace, (void *)0x1000, RTOS_VEND - (void *)0x1000, vtable);
#endif

    _init_region(v_address, size);
S
Shell 已提交
396 397 398 399

    return 0;
}

400 401 402 403 404 405 406
/************ setting el1 mmu register**************
  MAIR_EL1
  index 0 : memory outer writeback, write/read alloc
  index 1 : memory nocache
  index 2 : device nGnRnE
 *****************************************************/
void mmu_tcr_init(void)
S
Shell 已提交
407
{
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433
    unsigned long val64;

    val64 = 0x00447fUL;
    __asm__ volatile("msr MAIR_EL1, %0\n dsb sy\n" ::"r"(val64));

    /* TCR_EL1 */
    val64 = (16UL << 0)      /* t0sz 48bit */
            | (0x0UL << 6)   /* reserved */
            | (0x0UL << 7)   /* epd0 */
            | (0x3UL << 8)   /* t0 wb cacheable */
            | (0x3UL << 10)  /* inner shareable */
            | (0x2UL << 12)  /* t0 outer shareable */
            | (0x0UL << 14)  /* t0 4K */
            | (16UL << 16)   /* t1sz 48bit */
            | (0x0UL << 22)  /* define asid use ttbr0.asid */
            | (0x0UL << 23)  /* epd1 */
            | (0x3UL << 24)  /* t1 inner wb cacheable */
            | (0x3UL << 26)  /* t1 outer wb cacheable */
            | (0x2UL << 28)  /* t1 outer shareable */
            | (0x2UL << 30)  /* t1 4k */
            | (0x1UL << 32)  /* 001b 64GB PA */
            | (0x0UL << 35)  /* reserved */
            | (0x1UL << 36)  /* as: 0:8bit 1:16bit */
            | (0x0UL << 37)  /* tbi0 */
            | (0x0UL << 38); /* tbi1 */
    __asm__ volatile("msr TCR_EL1, %0\n" ::"r"(val64));
S
Shell 已提交
434 435
}

436
struct page_table
S
Shell 已提交
437
{
438 439
    unsigned long page[512];
};
S
Shell 已提交
440

441 442 443 444 445
static struct page_table *__init_page_array;
static unsigned long __page_off = 0UL;
unsigned long get_free_page(void)
{
    if (!__init_page_array)
S
Shell 已提交
446
    {
447 448 449 450 451
        unsigned long temp_page_start;
        asm volatile("mov %0, sp" : "=r"(temp_page_start));
        __init_page_array =
            (struct page_table *)(temp_page_start & ~(ARCH_SECTION_MASK));
        __page_off = 2; /* 0, 1 for ttbr0, ttrb1 */
S
Shell 已提交
452
    }
453 454 455 456 457 458 459 460 461 462 463 464
    __page_off++;
    return (unsigned long)(__init_page_array[__page_off - 1].page);
}

static int _map_single_page_2M(unsigned long *lv0_tbl, unsigned long va,
                               unsigned long pa, unsigned long attr)
{
    int level;
    unsigned long *cur_lv_tbl = lv0_tbl;
    unsigned long page;
    unsigned long off;
    int level_shift = MMU_ADDRESS_BITS;
S
Shell 已提交
465

466
    if (va & ARCH_SECTION_MASK)
S
Shell 已提交
467
    {
468
        return MMU_MAP_ERROR_VANOTALIGN;
S
Shell 已提交
469
    }
470
    if (pa & ARCH_SECTION_MASK)
S
Shell 已提交
471
    {
472 473 474 475 476 477 478
        return MMU_MAP_ERROR_PANOTALIGN;
    }
    for (level = 0; level < MMU_TBL_BLOCK_2M_LEVEL; level++)
    {
        off = (va >> level_shift);
        off &= MMU_LEVEL_MASK;
        if (!(cur_lv_tbl[off] & MMU_TYPE_USED))
S
Shell 已提交
479
        {
480 481 482 483 484 485 486
            page = get_free_page();
            if (!page)
            {
                return MMU_MAP_ERROR_NOPAGE;
            }
            rt_memset((char *)page, 0, ARCH_PAGE_SIZE);
            cur_lv_tbl[off] = page | MMU_TYPE_TABLE;
S
Shell 已提交
487
        }
488 489
        page = cur_lv_tbl[off];
        if ((page & MMU_TYPE_MASK) == MMU_TYPE_BLOCK)
S
Shell 已提交
490
        {
491 492
            /* is block! error! */
            return MMU_MAP_ERROR_CONFLICT;
S
Shell 已提交
493
        }
494 495
        cur_lv_tbl = (unsigned long *)(page & MMU_ADDRESS_MASK);
        level_shift -= MMU_LEVEL_SHIFT;
S
Shell 已提交
496
    }
497 498 499 500 501
    attr &= MMU_ATTRIB_MASK;
    pa |= (attr | MMU_TYPE_BLOCK); /* block */
    off = (va >> ARCH_SECTION_SHIFT);
    off &= MMU_LEVEL_MASK;
    cur_lv_tbl[off] = pa;
S
Shell 已提交
502 503 504
    return 0;
}

505 506 507
static int _init_map_2M(unsigned long *lv0_tbl, unsigned long va,
                        unsigned long pa, unsigned long count,
                        unsigned long attr)
S
Shell 已提交
508
{
509 510
    unsigned long i;
    int ret;
S
Shell 已提交
511

512
    if (va & ARCH_SECTION_MASK)
S
Shell 已提交
513 514 515
    {
        return -1;
    }
516
    if (pa & ARCH_SECTION_MASK)
S
Shell 已提交
517 518 519
    {
        return -1;
    }
520
    for (i = 0; i < count; i++)
S
Shell 已提交
521
    {
522 523 524 525
        ret = _map_single_page_2M(lv0_tbl, va, pa, attr);
        va += ARCH_SECTION_SIZE;
        pa += ARCH_SECTION_SIZE;
        if (ret != 0)
S
Shell 已提交
526
        {
527
            return ret;
528 529
        }
    }
S
Shell 已提交
530 531 532
    return 0;
}

533
static unsigned long *_query(rt_aspace_t aspace, void *vaddr, int *plvl_shf)
S
Shell 已提交
534
{
535 536 537 538 539 540
    int level;
    unsigned long va = (unsigned long)vaddr;
    unsigned long *cur_lv_tbl;
    unsigned long page;
    unsigned long off;
    int level_shift = MMU_ADDRESS_BITS;
S
Shell 已提交
541

542 543
    cur_lv_tbl = aspace->page_table;
    RT_ASSERT(cur_lv_tbl);
S
Shell 已提交
544

545
    for (level = 0; level < MMU_TBL_PAGE_4k_LEVEL; level++)
S
Shell 已提交
546
    {
547 548
        off = (va >> level_shift);
        off &= MMU_LEVEL_MASK;
S
Shell 已提交
549

550 551 552 553
        if (!(cur_lv_tbl[off] & MMU_TYPE_USED))
        {
            return (void *)0;
        }
B
bigmagic 已提交
554

555 556
        page = cur_lv_tbl[off];
        if ((page & MMU_TYPE_MASK) == MMU_TYPE_BLOCK)
S
Shell 已提交
557
        {
558 559
            *plvl_shf = level_shift;
            return &cur_lv_tbl[off];
S
Shell 已提交
560
        }
561 562 563 564

        cur_lv_tbl = (unsigned long *)(page & MMU_ADDRESS_MASK);
        cur_lv_tbl = (unsigned long *)((unsigned long)cur_lv_tbl - PV_OFFSET);
        level_shift -= MMU_LEVEL_SHIFT;
S
Shell 已提交
565
    }
566 567 568 569
    /* now is level MMU_TBL_PAGE_4k_LEVEL */
    off = (va >> ARCH_PAGE_SHIFT);
    off &= MMU_LEVEL_MASK;
    page = cur_lv_tbl[off];
S
Shell 已提交
570

571 572 573 574 575 576
    if (!(page & MMU_TYPE_USED))
    {
        return (void *)0;
    }
    *plvl_shf = level_shift;
    return &cur_lv_tbl[off];
S
Shell 已提交
577 578
}

579
void *rt_hw_mmu_v2p(rt_aspace_t aspace, void *v_addr)
S
Shell 已提交
580
{
581 582 583
    int level_shift;
    unsigned long paddr;
    unsigned long *pte = _query(aspace, v_addr, &level_shift);
S
Shell 已提交
584

585
    if (pte)
S
Shell 已提交
586
    {
587 588
        paddr = *pte & MMU_ADDRESS_MASK;
        paddr |= (uintptr_t)v_addr & ((1ul << level_shift) - 1);
S
Shell 已提交
589 590 591
    }
    else
    {
592
        paddr = (unsigned long)ARCH_MAP_FAILED;
S
Shell 已提交
593
    }
594
    return (void *)paddr;
S
Shell 已提交
595 596
}

597
static int _noncache(uintptr_t *pte)
S
Shell 已提交
598
{
599 600 601 602 603
    int err = 0;
    const uintptr_t idx_shift = 2;
    const uintptr_t idx_mask = 0x7 << idx_shift;
    uintptr_t entry = *pte;
    if ((entry & idx_mask) == (NORMAL_MEM << idx_shift))
S
Shell 已提交
604
    {
605
        *pte = (entry & ~idx_mask) | (NORMAL_NOCACHE_MEM << idx_shift);
S
Shell 已提交
606
    }
607
    else
S
Shell 已提交
608
    {
609 610
        // do not support other type to be noncache
        err = RT_ENOSYS;
S
Shell 已提交
611
    }
612
    return err;
S
Shell 已提交
613 614
}

615
static int _cache(uintptr_t *pte)
S
Shell 已提交
616
{
617 618 619 620 621
    int err = 0;
    const uintptr_t idx_shift = 2;
    const uintptr_t idx_mask = 0x7 << idx_shift;
    uintptr_t entry = *pte;
    if ((entry & idx_mask) == (NORMAL_NOCACHE_MEM << idx_shift))
S
Shell 已提交
622
    {
623
        *pte = (entry & ~idx_mask) | (NORMAL_MEM << idx_shift);
S
Shell 已提交
624 625 626
    }
    else
    {
627 628
        // do not support other type to be cache
        err = -RT_ENOSYS;
S
Shell 已提交
629
    }
630
    return err;
S
Shell 已提交
631 632
}

633 634 635 636
static int (*control_handler[MMU_CNTL_DUMMY_END])(uintptr_t *pte) = {
    [MMU_CNTL_CACHE] = _cache,
    [MMU_CNTL_NONCACHE] = _noncache,
};
637

638 639
int rt_hw_mmu_control(struct rt_aspace *aspace, void *vaddr, size_t size,
                      enum rt_mmu_cntl cmd)
640
{
641 642 643
    int level_shift;
    int err = -RT_EINVAL;
    void *vend = vaddr + size;
G
GuEe-GUI 已提交
644

645 646
    int (*handler)(uintptr_t * pte);
    if (cmd >= 0 && cmd < MMU_CNTL_DUMMY_END)
647
    {
648
        handler = control_handler[cmd];
B
bigmagic 已提交
649

650
        while (vaddr < vend)
S
Shell 已提交
651
        {
652 653 654 655 656 657 658 659 660 661
            uintptr_t *pte = _query(aspace, vaddr, &level_shift);
            void *range_end = vaddr + (1ul << level_shift);
            RT_ASSERT(range_end <= vend);

            if (pte)
            {
                err = handler(pte);
                RT_ASSERT(err == RT_EOK);
            }
            vaddr = range_end;
S
Shell 已提交
662 663
        }
    }
664
    else
S
Shell 已提交
665
    {
666
        err = -RT_ENOSYS;
S
Shell 已提交
667
    }
B
bigmagic 已提交
668

669
    return err;
mysterywolf's avatar
mysterywolf 已提交
670
}
S
Shell 已提交
671

672
void rt_hw_mem_setup_early(unsigned long *tbl0, unsigned long *tbl1,
673
                           unsigned long size, unsigned long pv_off)
S
Shell 已提交
674 675
{
    int ret;
676 677 678 679

    /* setup pv off */
    rt_kmem_pvoff_set(pv_off);

S
Shell 已提交
680 681 682 683 684
    unsigned long va = KERNEL_VADDR_START;
    unsigned long count = (size + ARCH_SECTION_MASK) >> ARCH_SECTION_SHIFT;
    unsigned long normal_attr = MMU_MAP_CUSTOM(MMU_AP_KAUN, NORMAL_MEM);

    /* clean the first two pages */
685 686
    rt_memset((char *)tbl0, 0, ARCH_PAGE_SIZE);
    rt_memset((char *)tbl1, 0, ARCH_PAGE_SIZE);
S
Shell 已提交
687

688
    ret = _init_map_2M(tbl1, va, va + pv_off, count, normal_attr);
S
Shell 已提交
689 690 691 692
    if (ret != 0)
    {
        while (1);
    }
693
    ret = _init_map_2M(tbl0, va + pv_off, va + pv_off, count, normal_attr);
S
Shell 已提交
694 695 696 697 698
    if (ret != 0)
    {
        while (1);
    }
}