360 lines
8.7 KiB
ArmAsm
360 lines
8.7 KiB
ArmAsm
/*
|
|
* linux/arch/arm26/mm/proc-arm2,3.S
|
|
*
|
|
* Copyright (C) 1997-1999 Russell King
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* MMU functions for ARM2,3
|
|
*
|
|
* These are the low level assembler for performing cache
|
|
* and memory functions on ARM2, ARM250 and ARM3 processors.
|
|
*/
|
|
#include <linux/linkage.h>
|
|
#include <asm/assembler.h>
|
|
#include <asm/asm-offsets.h>
|
|
#include <asm/procinfo.h>
|
|
#include <asm/ptrace.h>
|
|
|
|
/*
|
|
* MEMC workhorse code. It's both a horse which things it's a pig.
|
|
*/
|
|
/*
|
|
* Function: cpu_memc_update_entry(pgd_t *pgd, unsigned long phys_pte, unsigned long addr)
|
|
* Params : pgd Page tables/MEMC mapping
|
|
* : phys_pte physical address, or PTE
|
|
* : addr virtual address
|
|
*/
|
|
ENTRY(cpu_memc_update_entry)
|
|
tst r1, #PAGE_PRESENT @ is the page present
|
|
orreq r1, r1, #PAGE_OLD | PAGE_CLEAN
|
|
moveq r2, #0x01f00000
|
|
mov r3, r1, lsr #13 @ convert to physical page nr
|
|
and r3, r3, #0x3fc
|
|
adr ip, memc_phys_table_32
|
|
ldr r3, [ip, r3]
|
|
tst r1, #PAGE_OLD | PAGE_NOT_USER
|
|
biceq r3, r3, #0x200
|
|
tsteq r1, #PAGE_READONLY | PAGE_CLEAN
|
|
biceq r3, r3, #0x300
|
|
mov r2, r2, lsr #15 @ virtual -> nr
|
|
orr r3, r3, r2, lsl #15
|
|
and r2, r2, #0x300
|
|
orr r3, r3, r2, lsl #2
|
|
and r2, r3, #255
|
|
sub r0, r0, #256 * 4
|
|
str r3, [r0, r2, lsl #2]
|
|
strb r3, [r3]
|
|
movs pc, lr
|
|
/*
|
|
* Params : r0 = preserved
|
|
* : r1 = memc table base (preserved)
|
|
* : r2 = page table entry
|
|
* : r3 = preserved
|
|
* : r4 = unused
|
|
* : r5 = memc physical address translation table
|
|
* : ip = virtual address (preserved)
|
|
*/
|
|
update_pte:
|
|
mov r4, r2, lsr #13
|
|
and r4, r4, #0x3fc
|
|
ldr r4, [r5, r4] @ covert to MEMC page
|
|
|
|
tst r2, #PAGE_OLD | PAGE_NOT_USER @ check for MEMC read
|
|
biceq r4, r4, #0x200
|
|
tsteq r2, #PAGE_READONLY | PAGE_CLEAN @ check for MEMC write
|
|
biceq r4, r4, #0x300
|
|
|
|
orr r4, r4, ip
|
|
and r2, ip, #0x01800000
|
|
orr r4, r4, r2, lsr #13
|
|
|
|
and r2, r4, #255
|
|
str r4, [r1, r2, lsl #2]
|
|
movs pc, lr
|
|
|
|
/*
|
|
* Params : r0 = preserved
|
|
* : r1 = memc table base (preserved)
|
|
* : r2 = page table base
|
|
* : r3 = preserved
|
|
* : r4 = unused
|
|
* : r5 = memc physical address translation table
|
|
* : ip = virtual address (updated)
|
|
*/
|
|
update_pte_table:
|
|
stmfd sp!, {r0, lr}
|
|
bic r0, r2, #3
|
|
1: ldr r2, [r0], #4 @ get entry
|
|
tst r2, #PAGE_PRESENT @ page present
|
|
blne update_pte @ process pte
|
|
add ip, ip, #32768 @ increment virt addr
|
|
ldr r2, [r0], #4 @ get entry
|
|
tst r2, #PAGE_PRESENT @ page present
|
|
blne update_pte @ process pte
|
|
add ip, ip, #32768 @ increment virt addr
|
|
ldr r2, [r0], #4 @ get entry
|
|
tst r2, #PAGE_PRESENT @ page present
|
|
blne update_pte @ process pte
|
|
add ip, ip, #32768 @ increment virt addr
|
|
ldr r2, [r0], #4 @ get entry
|
|
tst r2, #PAGE_PRESENT @ page present
|
|
blne update_pte @ process pte
|
|
add ip, ip, #32768 @ increment virt addr
|
|
tst ip, #32768 * 31 @ finished?
|
|
bne 1b
|
|
ldmfd sp!, {r0, pc}^
|
|
|
|
/*
|
|
* Function: cpu_memc_update_all(pgd_t *pgd)
|
|
* Params : pgd Page tables/MEMC mapping
|
|
* Notes : this is optimised for 32k pages
|
|
*/
|
|
ENTRY(cpu_memc_update_all)
|
|
stmfd sp!, {r4, r5, lr}
|
|
bl clear_tables
|
|
sub r1, r0, #256 * 4 @ start of MEMC tables
|
|
adr r5, memc_phys_table_32 @ Convert to logical page number
|
|
mov ip, #0 @ virtual address
|
|
1: ldmia r0!, {r2, r3} @ load two pgd entries
|
|
tst r2, #PAGE_PRESENT @ is pgd entry present?
|
|
addeq ip, ip, #1048576 @FIXME - PAGE_PRESENT is for PTEs technically...
|
|
blne update_pte_table
|
|
mov r2, r3
|
|
tst r2, #PAGE_PRESENT @ is pgd entry present?
|
|
addeq ip, ip, #1048576
|
|
blne update_pte_table
|
|
teq ip, #32 * 1048576
|
|
bne 1b
|
|
ldmfd sp!, {r4, r5, pc}^
|
|
|
|
/*
|
|
* Build the table to map from physical page number to memc page number
|
|
*/
|
|
.type memc_phys_table_32, #object
|
|
memc_phys_table_32:
|
|
.irp b7, 0x00, 0x80
|
|
.irp b6, 0x00, 0x02
|
|
.irp b5, 0x00, 0x04
|
|
.irp b4, 0x00, 0x01
|
|
|
|
.irp b3, 0x00, 0x40
|
|
.irp b2, 0x00, 0x20
|
|
.irp b1, 0x00, 0x10
|
|
.irp b0, 0x00, 0x08
|
|
.long 0x03800300 + \b7 + \b6 + \b5 + \b4 + \b3 + \b2 + \b1 + \b0
|
|
.endr
|
|
.endr
|
|
.endr
|
|
.endr
|
|
|
|
.endr
|
|
.endr
|
|
.endr
|
|
.endr
|
|
.size memc_phys_table_32, . - memc_phys_table_32
|
|
|
|
/*
|
|
* helper for cpu_memc_update_all, this clears out all
|
|
* mappings, setting them close to the top of memory,
|
|
* and inaccessible (0x01f00000).
|
|
* Params : r0 = page table pointer
|
|
*/
|
|
clear_tables: ldr r1, _arm3_set_pgd - 4
|
|
ldr r2, [r1]
|
|
sub r1, r0, #256 * 4 @ start of MEMC tables
|
|
add r2, r1, r2, lsl #2 @ end of tables
|
|
mov r3, #0x03f00000 @ Default mapping (null mapping)
|
|
orr r3, r3, #0x00000f00
|
|
orr r4, r3, #1
|
|
orr r5, r3, #2
|
|
orr ip, r3, #3
|
|
1: stmia r1!, {r3, r4, r5, ip}
|
|
add r3, r3, #4
|
|
add r4, r4, #4
|
|
add r5, r5, #4
|
|
add ip, ip, #4
|
|
stmia r1!, {r3, r4, r5, ip}
|
|
add r3, r3, #4
|
|
add r4, r4, #4
|
|
add r5, r5, #4
|
|
add ip, ip, #4
|
|
teq r1, r2
|
|
bne 1b
|
|
mov pc, lr
|
|
|
|
/*
|
|
* Function: *_set_pgd(pgd_t *pgd)
|
|
* Params : pgd New page tables/MEMC mapping
|
|
* Purpose : update MEMC hardware with new mapping
|
|
*/
|
|
.word page_nr @ extern - declared in mm-memc.c
|
|
_arm3_set_pgd: mcr p15, 0, r1, c1, c0, 0 @ flush cache
|
|
_arm2_set_pgd: stmfd sp!, {lr}
|
|
ldr r1, _arm3_set_pgd - 4
|
|
ldr r2, [r1]
|
|
sub r0, r0, #256 * 4 @ start of MEMC tables
|
|
add r1, r0, r2, lsl #2 @ end of tables
|
|
1: ldmia r0!, {r2, r3, ip, lr}
|
|
strb r2, [r2]
|
|
strb r3, [r3]
|
|
strb ip, [ip]
|
|
strb lr, [lr]
|
|
ldmia r0!, {r2, r3, ip, lr}
|
|
strb r2, [r2]
|
|
strb r3, [r3]
|
|
strb ip, [ip]
|
|
strb lr, [lr]
|
|
teq r0, r1
|
|
bne 1b
|
|
ldmfd sp!, {pc}^
|
|
|
|
/*
|
|
* Function: *_proc_init (void)
|
|
* Purpose : Initialise the cache control registers
|
|
*/
|
|
_arm3_proc_init:
|
|
mov r0, #0x001f0000
|
|
orr r0, r0, #0x0000ff00
|
|
orr r0, r0, #0x000000ff
|
|
mcr p15, 0, r0, c3, c0 @ ARM3 Cacheable
|
|
mcr p15, 0, r0, c4, c0 @ ARM3 Updateable
|
|
mov r0, #0
|
|
mcr p15, 0, r0, c5, c0 @ ARM3 Disruptive
|
|
mcr p15, 0, r0, c1, c0 @ ARM3 Flush
|
|
mov r0, #3
|
|
mcr p15, 0, r0, c2, c0 @ ARM3 Control
|
|
_arm2_proc_init:
|
|
movs pc, lr
|
|
|
|
/*
|
|
* Function: *_proc_fin (void)
|
|
* Purpose : Finalise processor (disable caches)
|
|
*/
|
|
_arm3_proc_fin: mov r0, #2
|
|
mcr p15, 0, r0, c2, c0
|
|
_arm2_proc_fin: orrs pc, lr, #PSR_I_BIT|PSR_F_BIT
|
|
|
|
/*
|
|
* Function: *_xchg_1 (int new, volatile void *ptr)
|
|
* Params : new New value to store at...
|
|
* : ptr pointer to byte-wide location
|
|
* Purpose : Performs an exchange operation
|
|
* Returns : Original byte data at 'ptr'
|
|
*/
|
|
_arm2_xchg_1: mov r2, pc
|
|
orr r2, r2, #PSR_I_BIT
|
|
teqp r2, #0
|
|
ldrb r2, [r1]
|
|
strb r0, [r1]
|
|
mov r0, r2
|
|
movs pc, lr
|
|
|
|
_arm3_xchg_1: swpb r0, r0, [r1]
|
|
movs pc, lr
|
|
|
|
/*
|
|
* Function: *_xchg_4 (int new, volatile void *ptr)
|
|
* Params : new New value to store at...
|
|
* : ptr pointer to word-wide location
|
|
* Purpose : Performs an exchange operation
|
|
* Returns : Original word data at 'ptr'
|
|
*/
|
|
_arm2_xchg_4: mov r2, pc
|
|
orr r2, r2, #PSR_I_BIT
|
|
teqp r2, #0
|
|
ldr r2, [r1]
|
|
str r0, [r1]
|
|
mov r0, r2
|
|
movs pc, lr
|
|
|
|
_arm3_xchg_4: swp r0, r0, [r1]
|
|
movs pc, lr
|
|
|
|
_arm2_3_check_bugs:
|
|
bics pc, lr, #PSR_F_BIT @ Clear FIQ disable bit
|
|
|
|
armvlsi_name: .asciz "ARM/VLSI"
|
|
_arm2_name: .asciz "ARM 2"
|
|
_arm250_name: .asciz "ARM 250"
|
|
_arm3_name: .asciz "ARM 3"
|
|
|
|
.section ".init.text", #alloc, #execinstr
|
|
/*
|
|
* Purpose : Function pointers used to access above functions - all calls
|
|
* come through these
|
|
*/
|
|
.globl arm2_processor_functions
|
|
arm2_processor_functions:
|
|
.word _arm2_3_check_bugs
|
|
.word _arm2_proc_init
|
|
.word _arm2_proc_fin
|
|
.word _arm2_set_pgd
|
|
.word _arm2_xchg_1
|
|
.word _arm2_xchg_4
|
|
|
|
cpu_arm2_info:
|
|
.long armvlsi_name
|
|
.long _arm2_name
|
|
|
|
.globl arm250_processor_functions
|
|
arm250_processor_functions:
|
|
.word _arm2_3_check_bugs
|
|
.word _arm2_proc_init
|
|
.word _arm2_proc_fin
|
|
.word _arm2_set_pgd
|
|
.word _arm3_xchg_1
|
|
.word _arm3_xchg_4
|
|
|
|
cpu_arm250_info:
|
|
.long armvlsi_name
|
|
.long _arm250_name
|
|
|
|
.globl arm3_processor_functions
|
|
arm3_processor_functions:
|
|
.word _arm2_3_check_bugs
|
|
.word _arm3_proc_init
|
|
.word _arm3_proc_fin
|
|
.word _arm3_set_pgd
|
|
.word _arm3_xchg_1
|
|
.word _arm3_xchg_4
|
|
|
|
cpu_arm3_info:
|
|
.long armvlsi_name
|
|
.long _arm3_name
|
|
|
|
arm2_arch_name: .asciz "armv1"
|
|
arm3_arch_name: .asciz "armv2"
|
|
arm2_elf_name: .asciz "v1"
|
|
arm3_elf_name: .asciz "v2"
|
|
.align
|
|
|
|
.section ".proc.info", #alloc, #execinstr
|
|
|
|
.long 0x41560200
|
|
.long 0xfffffff0
|
|
.long arm2_arch_name
|
|
.long arm2_elf_name
|
|
.long 0
|
|
.long cpu_arm2_info
|
|
.long arm2_processor_functions
|
|
|
|
.long 0x41560250
|
|
.long 0xfffffff0
|
|
.long arm3_arch_name
|
|
.long arm3_elf_name
|
|
.long 0
|
|
.long cpu_arm250_info
|
|
.long arm250_processor_functions
|
|
|
|
.long 0x41560300
|
|
.long 0xfffffff0
|
|
.long arm3_arch_name
|
|
.long arm3_elf_name
|
|
.long 0
|
|
.long cpu_arm3_info
|
|
.long arm3_processor_functions
|
|
|