| /* SPDX-License-Identifier: GPL-2.0-only */ |
| /* |
| * linux/arch/unicore32/kernel/head.S |
| * |
| * Code specific to PKUnity SoC and UniCore ISA |
| * |
| * Copyright (C) 2001-2010 GUAN Xue-tao |
| */ |
| #include <linux/linkage.h> |
| #include <linux/init.h> |
| |
| #include <asm/assembler.h> |
| #include <asm/ptrace.h> |
| #include <generated/asm-offsets.h> |
| #include <asm/memory.h> |
| #include <asm/thread_info.h> |
| #include <asm/hwdef-copro.h> |
| #include <asm/pgtable-hwdef.h> |
| |
| #if (PHYS_OFFSET & 0x003fffff) |
| #error "PHYS_OFFSET must be at an even 4MiB boundary!" |
| #endif |
| |
| #define KERNEL_RAM_VADDR (PAGE_OFFSET + KERNEL_IMAGE_START) |
| #define KERNEL_RAM_PADDR (PHYS_OFFSET + KERNEL_IMAGE_START) |
| |
| #define KERNEL_PGD_PADDR (KERNEL_RAM_PADDR - 0x1000) |
| #define KERNEL_PGD_VADDR (KERNEL_RAM_VADDR - 0x1000) |
| |
| #define KERNEL_START KERNEL_RAM_VADDR |
| #define KERNEL_END _end |
| |
| /* |
| * swapper_pg_dir is the virtual address of the initial page table. |
| * We place the page tables 4K below KERNEL_RAM_VADDR. Therefore, we must |
| * make sure that KERNEL_RAM_VADDR is correctly set. Currently, we expect |
| * the least significant 16 bits to be 0x8000, but we could probably |
| * relax this restriction to KERNEL_RAM_VADDR >= PAGE_OFFSET + 0x1000. |
| */ |
| #if (KERNEL_RAM_VADDR & 0xffff) != 0x8000 |
| #error KERNEL_RAM_VADDR must start at 0xXXXX8000 |
| #endif |
| |
| .globl swapper_pg_dir |
| .equ swapper_pg_dir, KERNEL_RAM_VADDR - 0x1000 |
| |
| /* |
| * Kernel startup entry point. |
| * --------------------------- |
| * |
| * This is normally called from the decompressor code. The requirements |
| * are: MMU = off, D-cache = off, I-cache = dont care |
| * |
| * This code is mostly position independent, so if you link the kernel at |
| * 0xc0008000, you call this at __pa(0xc0008000). |
| */ |
| __HEAD |
| ENTRY(stext) |
| @ set asr |
| mov r0, #PRIV_MODE @ ensure priv mode |
| or r0, #PSR_R_BIT | PSR_I_BIT @ disable irqs |
| mov.a asr, r0 |
| |
| @ process identify |
| movc r0, p0.c0, #0 @ cpuid |
| movl r1, 0xff00ffff @ mask |
| movl r2, 0x4d000863 @ value |
| and r0, r1, r0 |
| cxor.a r0, r2 |
| bne __error_p @ invalid processor id |
| |
| /* |
| * Clear the 4K level 1 swapper page table |
| */ |
| movl r0, #KERNEL_PGD_PADDR @ page table address |
| mov r1, #0 |
| add r2, r0, #0x1000 |
| 101: stw.w r1, [r0]+, #4 |
| stw.w r1, [r0]+, #4 |
| stw.w r1, [r0]+, #4 |
| stw.w r1, [r0]+, #4 |
| cxor.a r0, r2 |
| bne 101b |
| |
| movl r4, #KERNEL_PGD_PADDR @ page table address |
| mov r7, #PMD_TYPE_SECT | PMD_PRESENT @ page size: section |
| or r7, r7, #PMD_SECT_CACHEABLE @ cacheable |
| or r7, r7, #PMD_SECT_READ | PMD_SECT_WRITE | PMD_SECT_EXEC |
| |
| /* |
| * Create identity mapping for first 4MB of kernel to |
| * cater for the MMU enable. This identity mapping |
| * will be removed by paging_init(). We use our current program |
| * counter to determine corresponding section base address. |
| */ |
| mov r6, pc |
| mov r6, r6 >> #22 @ start of kernel section |
| or r1, r7, r6 << #22 @ flags + kernel base |
| stw r1, [r4+], r6 << #2 @ identity mapping |
| |
| /* |
| * Now setup the pagetables for our kernel direct |
| * mapped region. |
| */ |
| add r0, r4, #(KERNEL_START & 0xff000000) >> 20 |
| stw.w r1, [r0+], #(KERNEL_START & 0x00c00000) >> 20 |
| movl r6, #(KERNEL_END - 1) |
| add r0, r0, #4 |
| add r6, r4, r6 >> #20 |
| 102: csub.a r0, r6 |
| add r1, r1, #1 << 22 |
| bua 103f |
| stw.w r1, [r0]+, #4 |
| b 102b |
| 103: |
| /* |
| * Then map first 4MB of ram in case it contains our boot params. |
| */ |
| add r0, r4, #PAGE_OFFSET >> 20 |
| or r6, r7, #(PHYS_OFFSET & 0xffc00000) |
| stw r6, [r0] |
| |
| ldw r15, __switch_data @ address to jump to after |
| |
| /* |
| * Initialise TLB, Caches, and MMU state ready to switch the MMU |
| * on. |
| */ |
| mov r0, #0 |
| movc p0.c5, r0, #28 @ cache invalidate all |
| nop8 |
| movc p0.c6, r0, #6 @ TLB invalidate all |
| nop8 |
| |
| /* |
| * ..V. .... ..TB IDAM |
| * ..1. .... ..01 1111 |
| */ |
| movl r0, #0x201f @ control register setting |
| |
| /* |
| * Setup common bits before finally enabling the MMU. Essentially |
| * this is just loading the page table pointer and domain access |
| * registers. |
| */ |
| #ifndef CONFIG_ALIGNMENT_TRAP |
| andn r0, r0, #CR_A |
| #endif |
| #ifdef CONFIG_CPU_DCACHE_DISABLE |
| andn r0, r0, #CR_D |
| #endif |
| #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH |
| andn r0, r0, #CR_B |
| #endif |
| #ifdef CONFIG_CPU_ICACHE_DISABLE |
| andn r0, r0, #CR_I |
| #endif |
| |
| movc p0.c2, r4, #0 @ set pgd |
| b __turn_mmu_on |
| ENDPROC(stext) |
| |
| /* |
| * Enable the MMU. This completely changes the structure of the visible |
| * memory space. You will not be able to trace execution through this. |
| * |
| * r0 = cp#0 control register |
| * r15 = *virtual* address to jump to upon completion |
| */ |
| .align 5 |
| __turn_mmu_on: |
| mov r0, r0 |
| movc p0.c1, r0, #0 @ write control reg |
| nop @ fetch inst by phys addr |
| mov pc, r15 |
| nop8 @ fetch inst by phys addr |
| ENDPROC(__turn_mmu_on) |
| |
| /* |
| * Setup the initial page tables. We only setup the barest |
| * amount which are required to get the kernel running, which |
| * generally means mapping in the kernel code. |
| * |
| * r9 = cpuid |
| * r10 = procinfo |
| * |
| * Returns: |
| * r0, r3, r6, r7 corrupted |
| * r4 = physical page table address |
| */ |
| .ltorg |
| |
| .align 2 |
| .type __switch_data, %object |
| __switch_data: |
| .long __mmap_switched |
| .long __bss_start @ r6 |
| .long _end @ r7 |
| .long cr_alignment @ r8 |
| .long init_thread_union + THREAD_START_SP @ sp |
| |
| /* |
| * The following fragment of code is executed with the MMU on in MMU mode, |
| * and uses absolute addresses; this is not position independent. |
| * |
| * r0 = cp#0 control register |
| */ |
| __mmap_switched: |
| adr r3, __switch_data + 4 |
| |
| ldm.w (r6, r7, r8), [r3]+ |
| ldw sp, [r3] |
| |
| mov fp, #0 @ Clear BSS (and zero fp) |
| 203: csub.a r6, r7 |
| bea 204f |
| stw.w fp, [r6]+,#4 |
| b 203b |
| 204: |
| andn r1, r0, #CR_A @ Clear 'A' bit |
| stm (r0, r1), [r8]+ @ Save control register values |
| b start_kernel |
| ENDPROC(__mmap_switched) |
| |
| /* |
| * Exception handling. Something went wrong and we can't proceed. We |
| * ought to tell the user, but since we don't have any guarantee that |
| * we're even running on the right architecture, we do virtually nothing. |
| * |
| * If CONFIG_DEBUG_LL is set we try to print out something about the error |
| * and hope for the best (useful if bootloader fails to pass a proper |
| * machine ID for example). |
| */ |
| __error_p: |
| #ifdef CONFIG_DEBUG_LL |
| adr r0, str_p1 |
| b.l printascii |
| mov r0, r9 |
| b.l printhex8 |
| adr r0, str_p2 |
| b.l printascii |
| 901: nop8 |
| b 901b |
| str_p1: .asciz "\nError: unrecognized processor variant (0x" |
| str_p2: .asciz ").\n" |
| .align |
| #endif |
| ENDPROC(__error_p) |
| |