diff options
| author | Bernd Schmidt <bernd.schmidt@analog.com> | 2008-01-27 05:39:16 -0500 |
|---|---|---|
| committer | Bryan Wu <bryan.wu@analog.com> | 2008-01-27 05:39:16 -0500 |
| commit | b97b8a998397e8c64699559099fa9febffae2b4d (patch) | |
| tree | 689188b6336cf45b4391f5bc764878e342b9ac90 | |
| parent | 2047e40d724d42928c0b5994a1568c1b738efdb7 (diff) | |
[Blackfin] arch: Initial checkin of the memory protection support.
Enable it with CONFIG_MPU.
Signed-off-by: Bernd Schmidt <bernd.schmidt@analog.com>
Signed-off-by: Bryan Wu <bryan.wu@analog.com>
| -rw-r--r-- | arch/blackfin/Kconfig | 9 | ||||
| -rw-r--r-- | arch/blackfin/Makefile | 6 | ||||
| -rw-r--r-- | arch/blackfin/kernel/cplb-mpu/Makefile | 8 | ||||
| -rw-r--r-- | arch/blackfin/kernel/cplb-mpu/cacheinit.c | 62 | ||||
| -rw-r--r-- | arch/blackfin/kernel/cplb-mpu/cplbinfo.c | 144 | ||||
| -rw-r--r-- | arch/blackfin/kernel/cplb-mpu/cplbinit.c | 91 | ||||
| -rw-r--r-- | arch/blackfin/kernel/cplb-mpu/cplbmgr.c | 338 | ||||
| -rw-r--r-- | arch/blackfin/kernel/setup.c | 10 | ||||
| -rw-r--r-- | arch/blackfin/mach-common/entry.S | 19 | ||||
| -rw-r--r-- | arch/blackfin/mm/init.c | 4 | ||||
| -rw-r--r-- | include/asm-blackfin/cplb-mpu.h | 61 | ||||
| -rw-r--r-- | include/asm-blackfin/cplb.h | 4 | ||||
| -rw-r--r-- | include/asm-blackfin/cplbinit.h | 8 | ||||
| -rw-r--r-- | include/asm-blackfin/mmu.h | 4 | ||||
| -rw-r--r-- | include/asm-blackfin/mmu_context.h | 62 |
15 files changed, 822 insertions, 8 deletions
diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig index ce521a921bee..6b96d7d21aea 100644 --- a/arch/blackfin/Kconfig +++ b/arch/blackfin/Kconfig | |||
| @@ -765,6 +765,15 @@ config L1_MAX_PIECE | |||
| 765 | Set the max memory pieces for the L1 SRAM allocation algorithm. | 765 | Set the max memory pieces for the L1 SRAM allocation algorithm. |
| 766 | Min value is 16. Max value is 1024. | 766 | Min value is 16. Max value is 1024. |
| 767 | 767 | ||
| 768 | |||
| 769 | config MPU | ||
| 770 | bool "Enable the memory protection unit (EXPERIMENTAL)" | ||
| 771 | default n | ||
| 772 | help | ||
| 773 | Use the processor's MPU to protect applications from accessing | ||
| 774 | memory they do not own. This comes at a performance penalty | ||
| 775 | and is recommended only for debugging. | ||
| 776 | |||
| 768 | comment "Asynchonous Memory Configuration" | 777 | comment "Asynchonous Memory Configuration" |
| 769 | 778 | ||
| 770 | menu "EBIU_AMGCTL Global Control" | 779 | menu "EBIU_AMGCTL Global Control" |
diff --git a/arch/blackfin/Makefile b/arch/blackfin/Makefile index 2fc899c5e45f..0edc402fef54 100644 --- a/arch/blackfin/Makefile +++ b/arch/blackfin/Makefile | |||
| @@ -82,7 +82,11 @@ core-y += arch/$(ARCH)/mach-$(MACHINE)/ | |||
| 82 | core-y += arch/$(ARCH)/mach-$(MACHINE)/boards/ | 82 | core-y += arch/$(ARCH)/mach-$(MACHINE)/boards/ |
| 83 | endif | 83 | endif |
| 84 | 84 | ||
| 85 | core-y += arch/$(ARCH)/kernel/cplb-nompu/ | 85 | ifeq ($(CONFIG_MPU),y) |
| 86 | core-y += arch/$(ARCH)/kernel/cplb-mpu/ | ||
| 87 | else | ||
| 88 | core-y += arch/$(ARCH)/kernel/cplb-nompu/ | ||
| 89 | endif | ||
| 86 | 90 | ||
| 87 | libs-y += arch/$(ARCH)/lib/ | 91 | libs-y += arch/$(ARCH)/lib/ |
| 88 | 92 | ||
diff --git a/arch/blackfin/kernel/cplb-mpu/Makefile b/arch/blackfin/kernel/cplb-mpu/Makefile new file mode 100644 index 000000000000..286b69357f97 --- /dev/null +++ b/arch/blackfin/kernel/cplb-mpu/Makefile | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | # | ||
| 2 | # arch/blackfin/kernel/cplb-nompu/Makefile | ||
| 3 | # | ||
| 4 | |||
| 5 | obj-y := cplbinit.o cacheinit.o cplbmgr.o | ||
| 6 | |||
| 7 | obj-$(CONFIG_CPLB_INFO) += cplbinfo.o | ||
| 8 | |||
diff --git a/arch/blackfin/kernel/cplb-mpu/cacheinit.c b/arch/blackfin/kernel/cplb-mpu/cacheinit.c new file mode 100644 index 000000000000..9eecfa403187 --- /dev/null +++ b/arch/blackfin/kernel/cplb-mpu/cacheinit.c | |||
| @@ -0,0 +1,62 @@ | |||
| 1 | /* | ||
| 2 | * Copyright 2004-2007 Analog Devices Inc. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License as published by | ||
| 6 | * the Free Software Foundation; either version 2 of the License, or | ||
| 7 | * (at your option) any later version. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope that it will be useful, | ||
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 12 | * GNU General Public License for more details. | ||
| 13 | * | ||
| 14 | * You should have received a copy of the GNU General Public License | ||
| 15 | * along with this program; if not, see the file COPYING, or write | ||
| 16 | * to the Free Software Foundation, Inc., | ||
| 17 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <linux/cpu.h> | ||
| 21 | |||
| 22 | #include <asm/cacheflush.h> | ||
| 23 | #include <asm/blackfin.h> | ||
| 24 | #include <asm/cplb.h> | ||
| 25 | #include <asm/cplbinit.h> | ||
| 26 | |||
| 27 | #if defined(CONFIG_BFIN_ICACHE) | ||
| 28 | void bfin_icache_init(void) | ||
| 29 | { | ||
| 30 | unsigned long ctrl; | ||
| 31 | int i; | ||
| 32 | |||
| 33 | SSYNC(); | ||
| 34 | for (i = 0; i < MAX_CPLBS; i++) { | ||
| 35 | bfin_write32(ICPLB_ADDR0 + i * 4, icplb_tbl[i].addr); | ||
| 36 | bfin_write32(ICPLB_DATA0 + i * 4, icplb_tbl[i].data); | ||
| 37 | } | ||
| 38 | ctrl = bfin_read_IMEM_CONTROL(); | ||
| 39 | ctrl |= IMC | ENICPLB; | ||
| 40 | bfin_write_IMEM_CONTROL(ctrl); | ||
| 41 | SSYNC(); | ||
| 42 | } | ||
| 43 | #endif | ||
| 44 | |||
| 45 | #if defined(CONFIG_BFIN_DCACHE) | ||
| 46 | void bfin_dcache_init(void) | ||
| 47 | { | ||
| 48 | unsigned long ctrl; | ||
| 49 | int i; | ||
| 50 | |||
| 51 | SSYNC(); | ||
| 52 | for (i = 0; i < MAX_CPLBS; i++) { | ||
| 53 | bfin_write32(DCPLB_ADDR0 + i * 4, dcplb_tbl[i].addr); | ||
| 54 | bfin_write32(DCPLB_DATA0 + i * 4, dcplb_tbl[i].data); | ||
| 55 | } | ||
| 56 | |||
| 57 | ctrl = bfin_read_DMEM_CONTROL(); | ||
| 58 | ctrl |= DMEM_CNTR; | ||
| 59 | bfin_write_DMEM_CONTROL(ctrl); | ||
| 60 | SSYNC(); | ||
| 61 | } | ||
| 62 | #endif | ||
diff --git a/arch/blackfin/kernel/cplb-mpu/cplbinfo.c b/arch/blackfin/kernel/cplb-mpu/cplbinfo.c new file mode 100644 index 000000000000..bd072299f7f2 --- /dev/null +++ b/arch/blackfin/kernel/cplb-mpu/cplbinfo.c | |||
| @@ -0,0 +1,144 @@ | |||
| 1 | /* | ||
| 2 | * File: arch/blackfin/mach-common/cplbinfo.c | ||
| 3 | * Based on: | ||
| 4 | * Author: Sonic Zhang <sonic.zhang@analog.com> | ||
| 5 | * | ||
| 6 | * Created: Jan. 2005 | ||
| 7 | * Description: Display CPLB status | ||
| 8 | * | ||
| 9 | * Modified: | ||
| 10 | * Copyright 2004-2006 Analog Devices Inc. | ||
| 11 | * | ||
| 12 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | ||
| 13 | * | ||
| 14 | * This program is free software; you can redistribute it and/or modify | ||
| 15 | * it under the terms of the GNU General Public License as published by | ||
| 16 | * the Free Software Foundation; either version 2 of the License, or | ||
| 17 | * (at your option) any later version. | ||
| 18 | * | ||
| 19 | * This program is distributed in the hope that it will be useful, | ||
| 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 22 | * GNU General Public License for more details. | ||
| 23 | * | ||
| 24 | * You should have received a copy of the GNU General Public License | ||
| 25 | * along with this program; if not, see the file COPYING, or write | ||
| 26 | * to the Free Software Foundation, Inc., | ||
| 27 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 28 | */ | ||
| 29 | |||
| 30 | #include <linux/module.h> | ||
| 31 | #include <linux/kernel.h> | ||
| 32 | #include <linux/init.h> | ||
| 33 | #include <linux/proc_fs.h> | ||
| 34 | #include <linux/uaccess.h> | ||
| 35 | |||
| 36 | #include <asm/current.h> | ||
| 37 | #include <asm/system.h> | ||
| 38 | #include <asm/cplb.h> | ||
| 39 | #include <asm/cplbinit.h> | ||
| 40 | #include <asm/blackfin.h> | ||
| 41 | |||
| 42 | #define CPLB_I 1 | ||
| 43 | #define CPLB_D 2 | ||
| 44 | |||
| 45 | #define SYNC_SYS SSYNC() | ||
| 46 | #define SYNC_CORE CSYNC() | ||
| 47 | |||
| 48 | #define CPLB_BIT_PAGESIZE 0x30000 | ||
| 49 | |||
| 50 | static char page_size_string_table[][4] = { "1K", "4K", "1M", "4M" }; | ||
| 51 | |||
| 52 | static char *cplb_print_entry(char *buf, struct cplb_entry *tbl, int switched) | ||
| 53 | { | ||
| 54 | int i; | ||
| 55 | buf += sprintf(buf, "Index\tAddress\t\tData\tSize\tU/RD\tU/WR\tS/WR\tSwitch\n"); | ||
| 56 | for (i = 0; i < MAX_CPLBS; i++) { | ||
| 57 | unsigned long data = tbl[i].data; | ||
| 58 | unsigned long addr = tbl[i].addr; | ||
| 59 | if (!(data & CPLB_VALID)) | ||
| 60 | continue; | ||
| 61 | |||
| 62 | buf += | ||
| 63 | sprintf(buf, | ||
| 64 | "%d\t0x%08lx\t%06lx\t%s\t%c\t%c\t%c\t%c\n", | ||
| 65 | i, addr, data, | ||
| 66 | page_size_string_table[(data & 0x30000) >> 16], | ||
| 67 | (data & CPLB_USER_RD) ? 'Y' : 'N', | ||
| 68 | (data & CPLB_USER_WR) ? 'Y' : 'N', | ||
| 69 | (data & CPLB_SUPV_WR) ? 'Y' : 'N', | ||
| 70 | i < switched ? 'N' : 'Y'); | ||
| 71 | } | ||
| 72 | buf += sprintf(buf, "\n"); | ||
| 73 | |||
| 74 | return buf; | ||
| 75 | } | ||
| 76 | |||
| 77 | int cplbinfo_proc_output(char *buf) | ||
| 78 | { | ||
| 79 | char *p; | ||
| 80 | |||
| 81 | p = buf; | ||
| 82 | |||
| 83 | p += sprintf(p, "------------------ CPLB Information ------------------\n\n"); | ||
| 84 | |||
| 85 | if (bfin_read_IMEM_CONTROL() & ENICPLB) { | ||
| 86 | p += sprintf(p, "Instruction CPLB entry:\n"); | ||
| 87 | p = cplb_print_entry(p, icplb_tbl, first_switched_icplb); | ||
| 88 | } else | ||
| 89 | p += sprintf(p, "Instruction CPLB is disabled.\n\n"); | ||
| 90 | |||
| 91 | if (1 || bfin_read_DMEM_CONTROL() & ENDCPLB) { | ||
| 92 | p += sprintf(p, "Data CPLB entry:\n"); | ||
| 93 | p = cplb_print_entry(p, dcplb_tbl, first_switched_dcplb); | ||
| 94 | } else | ||
| 95 | p += sprintf(p, "Data CPLB is disabled.\n"); | ||
| 96 | |||
| 97 | p += sprintf(p, "ICPLB miss: %d\nICPLB supervisor miss: %d\n", | ||
| 98 | nr_icplb_miss, nr_icplb_supv_miss); | ||
| 99 | p += sprintf(p, "DCPLB miss: %d\nDCPLB protection fault:%d\n", | ||
| 100 | nr_dcplb_miss, nr_dcplb_prot); | ||
| 101 | p += sprintf(p, "CPLB flushes: %d\n", | ||
| 102 | nr_cplb_flush); | ||
| 103 | |||
| 104 | return p - buf; | ||
| 105 | } | ||
| 106 | |||
| 107 | static int cplbinfo_read_proc(char *page, char **start, off_t off, | ||
| 108 | int count, int *eof, void *data) | ||
| 109 | { | ||
| 110 | int len; | ||
| 111 | |||
| 112 | len = cplbinfo_proc_output(page); | ||
| 113 | if (len <= off + count) | ||
| 114 | *eof = 1; | ||
| 115 | *start = page + off; | ||
| 116 | len -= off; | ||
| 117 | if (len > count) | ||
| 118 | len = count; | ||
| 119 | if (len < 0) | ||
| 120 | len = 0; | ||
| 121 | return len; | ||
| 122 | } | ||
| 123 | |||
| 124 | static int __init cplbinfo_init(void) | ||
| 125 | { | ||
| 126 | struct proc_dir_entry *entry; | ||
| 127 | |||
| 128 | entry = create_proc_entry("cplbinfo", 0, NULL); | ||
| 129 | if (!entry) | ||
| 130 | return -ENOMEM; | ||
| 131 | |||
| 132 | entry->read_proc = cplbinfo_read_proc; | ||
| 133 | entry->data = NULL; | ||
| 134 | |||
| 135 | return 0; | ||
| 136 | } | ||
| 137 | |||
| 138 | static void __exit cplbinfo_exit(void) | ||
| 139 | { | ||
| 140 | remove_proc_entry("cplbinfo", NULL); | ||
| 141 | } | ||
| 142 | |||
| 143 | module_init(cplbinfo_init); | ||
| 144 | module_exit(cplbinfo_exit); | ||
diff --git a/arch/blackfin/kernel/cplb-mpu/cplbinit.c b/arch/blackfin/kernel/cplb-mpu/cplbinit.c new file mode 100644 index 000000000000..e2e2b5079f5b --- /dev/null +++ b/arch/blackfin/kernel/cplb-mpu/cplbinit.c | |||
| @@ -0,0 +1,91 @@ | |||
| 1 | /* | ||
| 2 | * Blackfin CPLB initialization | ||
| 3 | * | ||
| 4 | * Copyright 2004-2007 Analog Devices Inc. | ||
| 5 | * | ||
| 6 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2 of the License, or | ||
| 11 | * (at your option) any later version. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, | ||
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | * GNU General Public License for more details. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the GNU General Public License | ||
| 19 | * along with this program; if not, see the file COPYING, or write | ||
| 20 | * to the Free Software Foundation, Inc., | ||
| 21 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 22 | */ | ||
| 23 | #include <linux/module.h> | ||
| 24 | |||
| 25 | #include <asm/blackfin.h> | ||
| 26 | #include <asm/cplb.h> | ||
| 27 | #include <asm/cplbinit.h> | ||
| 28 | |||
| 29 | struct cplb_entry icplb_tbl[MAX_CPLBS]; | ||
| 30 | struct cplb_entry dcplb_tbl[MAX_CPLBS]; | ||
| 31 | |||
| 32 | int first_switched_icplb, first_switched_dcplb; | ||
| 33 | int first_mask_dcplb; | ||
| 34 | |||
| 35 | void __init generate_cpl_tables(void) | ||
| 36 | { | ||
| 37 | int i_d, i_i; | ||
| 38 | unsigned long addr; | ||
| 39 | unsigned long d_data, i_data; | ||
| 40 | unsigned long d_cache = 0, i_cache = 0; | ||
| 41 | |||
| 42 | #ifdef CONFIG_BFIN_ICACHE | ||
| 43 | i_cache = CPLB_L1_CHBL | ANOMALY_05000158_WORKAROUND; | ||
| 44 | #endif | ||
| 45 | |||
| 46 | #ifdef CONFIG_BFIN_DCACHE | ||
| 47 | d_cache = CPLB_L1_CHBL; | ||
| 48 | #ifdef CONFIG_BLKFIN_WT | ||
| 49 | d_cache |= CPLB_L1_AOW | CPLB_WT; | ||
| 50 | #endif | ||
| 51 | #endif | ||
| 52 | i_d = i_i = 0; | ||
| 53 | |||
| 54 | /* Set up the zero page. */ | ||
| 55 | dcplb_tbl[i_d].addr = 0; | ||
| 56 | dcplb_tbl[i_d++].data = SDRAM_OOPS | PAGE_SIZE_1KB; | ||
| 57 | |||
| 58 | #if 0 | ||
| 59 | icplb_tbl[i_i].addr = 0; | ||
| 60 | icplb_tbl[i_i++].data = i_cache | CPLB_USER_RD | PAGE_SIZE_4KB; | ||
| 61 | #endif | ||
| 62 | |||
| 63 | /* Cover kernel memory with 4M pages. */ | ||
| 64 | addr = 0; | ||
| 65 | d_data = d_cache | CPLB_SUPV_WR | CPLB_VALID | PAGE_SIZE_4MB | CPLB_DIRTY; | ||
| 66 | i_data = i_cache | CPLB_VALID | CPLB_PORTPRIO | PAGE_SIZE_4MB; | ||
| 67 | |||
| 68 | for (; addr < memory_start; addr += 4 * 1024 * 1024) { | ||
| 69 | dcplb_tbl[i_d].addr = addr; | ||
| 70 | dcplb_tbl[i_d++].data = d_data; | ||
| 71 | icplb_tbl[i_i].addr = addr; | ||
| 72 | icplb_tbl[i_i++].data = i_data | (addr == 0 ? CPLB_USER_RD : 0); | ||
| 73 | } | ||
| 74 | |||
| 75 | /* Cover L1 memory. One 4M area for code and data each is enough. */ | ||
| 76 | #if L1_DATA_A_LENGTH > 0 || L1_DATA_B_LENGTH > 0 | ||
| 77 | dcplb_tbl[i_d].addr = L1_DATA_A_START; | ||
| 78 | dcplb_tbl[i_d++].data = L1_DMEMORY | PAGE_SIZE_4MB; | ||
| 79 | #endif | ||
| 80 | icplb_tbl[i_i].addr = L1_CODE_START; | ||
| 81 | icplb_tbl[i_i++].data = L1_IMEMORY | PAGE_SIZE_4MB; | ||
| 82 | |||
| 83 | first_mask_dcplb = i_d; | ||
| 84 | first_switched_dcplb = i_d + (1 << page_mask_order); | ||
| 85 | first_switched_icplb = i_i; | ||
| 86 | |||
| 87 | while (i_d < MAX_CPLBS) | ||
| 88 | dcplb_tbl[i_d++].data = 0; | ||
| 89 | while (i_i < MAX_CPLBS) | ||
| 90 | icplb_tbl[i_i++].data = 0; | ||
| 91 | } | ||
diff --git a/arch/blackfin/kernel/cplb-mpu/cplbmgr.c b/arch/blackfin/kernel/cplb-mpu/cplbmgr.c new file mode 100644 index 000000000000..c426a22f9907 --- /dev/null +++ b/arch/blackfin/kernel/cplb-mpu/cplbmgr.c | |||
| @@ -0,0 +1,338 @@ | |||
| 1 | /* | ||
| 2 | * Blackfin CPLB exception handling. | ||
| 3 | * Copyright 2004-2007 Analog Devices Inc. | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License as published by | ||
| 7 | * the Free Software Foundation; either version 2 of the License, or | ||
| 8 | * (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License | ||
| 16 | * along with this program; if not, see the file COPYING, or write | ||
| 17 | * to the Free Software Foundation, Inc., | ||
| 18 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 19 | */ | ||
| 20 | #include <linux/module.h> | ||
| 21 | #include <linux/mm.h> | ||
| 22 | |||
| 23 | #include <asm/blackfin.h> | ||
| 24 | #include <asm/cplbinit.h> | ||
| 25 | #include <asm/mmu_context.h> | ||
| 26 | |||
| 27 | #ifdef CONFIG_BFIN_ICACHE | ||
| 28 | |||
| 29 | #define FAULT_RW (1 << 16) | ||
| 30 | #define FAULT_USERSUPV (1 << 17) | ||
| 31 | |||
| 32 | int page_mask_nelts; | ||
| 33 | int page_mask_order; | ||
| 34 | unsigned long *current_rwx_mask; | ||
| 35 | |||
| 36 | int nr_dcplb_miss, nr_icplb_miss, nr_icplb_supv_miss, nr_dcplb_prot; | ||
| 37 | int nr_cplb_flush; | ||
| 38 | |||
| 39 | static inline void disable_dcplb(void) | ||
| 40 | { | ||
| 41 | unsigned long ctrl; | ||
| 42 | SSYNC(); | ||
| 43 | ctrl = bfin_read_DMEM_CONTROL(); | ||
| 44 | ctrl &= ~ENDCPLB; | ||
| 45 | bfin_write_DMEM_CONTROL(ctrl); | ||
| 46 | SSYNC(); | ||
| 47 | } | ||
| 48 | |||
| 49 | static inline void enable_dcplb(void) | ||
| 50 | { | ||
| 51 | unsigned long ctrl; | ||
| 52 | SSYNC(); | ||
| 53 | ctrl = bfin_read_DMEM_CONTROL(); | ||
| 54 | ctrl |= ENDCPLB; | ||
| 55 | bfin_write_DMEM_CONTROL(ctrl); | ||
| 56 | SSYNC(); | ||
| 57 | } | ||
| 58 | |||
| 59 | static inline void disable_icplb(void) | ||
| 60 | { | ||
| 61 | unsigned long ctrl; | ||
| 62 | SSYNC(); | ||
| 63 | ctrl = bfin_read_IMEM_CONTROL(); | ||
| 64 | ctrl &= ~ENICPLB; | ||
| 65 | bfin_write_IMEM_CONTROL(ctrl); | ||
| 66 | SSYNC(); | ||
| 67 | } | ||
| 68 | |||
| 69 | static inline void enable_icplb(void) | ||
| 70 | { | ||
| 71 | unsigned long ctrl; | ||
| 72 | SSYNC(); | ||
| 73 | ctrl = bfin_read_IMEM_CONTROL(); | ||
| 74 | ctrl |= ENICPLB; | ||
| 75 | bfin_write_IMEM_CONTROL(ctrl); | ||
| 76 | SSYNC(); | ||
| 77 | } | ||
| 78 | |||
| 79 | /* | ||
| 80 | * Given the contents of the status register, return the index of the | ||
| 81 | * CPLB that caused the fault. | ||
| 82 | */ | ||
| 83 | static inline int faulting_cplb_index(int status) | ||
| 84 | { | ||
| 85 | int signbits = __builtin_bfin_norm_fr1x32(status & 0xFFFF); | ||
| 86 | return 30 - signbits; | ||
| 87 | } | ||
| 88 | |||
| 89 | /* | ||
| 90 | * Given the contents of the status register and the DCPLB_DATA contents, | ||
| 91 | * return true if a write access should be permitted. | ||
| 92 | */ | ||
| 93 | static inline int write_permitted(int status, unsigned long data) | ||
| 94 | { | ||
| 95 | if (status & FAULT_USERSUPV) | ||
| 96 | return !!(data & CPLB_SUPV_WR); | ||
| 97 | else | ||
| 98 | return !!(data & CPLB_USER_WR); | ||
| 99 | } | ||
| 100 | |||
| 101 | /* Counters to implement round-robin replacement. */ | ||
| 102 | static int icplb_rr_index, dcplb_rr_index; | ||
| 103 | |||
| 104 | /* | ||
| 105 | * Find an ICPLB entry to be evicted and return its index. | ||
| 106 | */ | ||
| 107 | static int evict_one_icplb(void) | ||
| 108 | { | ||
| 109 | int i; | ||
| 110 | for (i = first_switched_icplb; i < MAX_CPLBS; i++) | ||
| 111 | if ((icplb_tbl[i].data & CPLB_VALID) == 0) | ||
| 112 | return i; | ||
| 113 | i = first_switched_icplb + icplb_rr_index; | ||
| 114 | if (i >= MAX_CPLBS) { | ||
| 115 | i -= MAX_CPLBS - first_switched_icplb; | ||
| 116 | icplb_rr_index -= MAX_CPLBS - first_switched_icplb; | ||
| 117 | } | ||
| 118 | icplb_rr_index++; | ||
| 119 | return i; | ||
| 120 | } | ||
| 121 | |||
| 122 | static int evict_one_dcplb(void) | ||
| 123 | { | ||
| 124 | int i; | ||
| 125 | for (i = first_switched_dcplb; i < MAX_CPLBS; i++) | ||
| 126 | if ((dcplb_tbl[i].data & CPLB_VALID) == 0) | ||
| 127 | return i; | ||
| 128 | i = first_switched_dcplb + dcplb_rr_index; | ||
| 129 | if (i >= MAX_CPLBS) { | ||
| 130 | i -= MAX_CPLBS - first_switched_dcplb; | ||
| 131 | dcplb_rr_index -= MAX_CPLBS - first_switched_dcplb; | ||
| 132 | } | ||
| 133 | dcplb_rr_index++; | ||
| 134 | return i; | ||
| 135 | } | ||
| 136 | |||
| 137 | static noinline int dcplb_miss(void) | ||
| 138 | { | ||
| 139 | unsigned long addr = bfin_read_DCPLB_FAULT_ADDR(); | ||
| 140 | int status = bfin_read_DCPLB_STATUS(); | ||
| 141 | unsigned long *mask; | ||
| 142 | int idx; | ||
| 143 | unsigned long d_data; | ||
| 144 | |||
| 145 | nr_dcplb_miss++; | ||
| 146 | if (addr >= _ramend) | ||
| 147 | return CPLB_PROT_VIOL; | ||
| 148 | |||
| 149 | d_data = CPLB_SUPV_WR | CPLB_VALID | CPLB_DIRTY | PAGE_SIZE_4KB; | ||
| 150 | #ifdef CONFIG_BFIN_DCACHE | ||
| 151 | d_data |= CPLB_L1_CHBL | ANOMALY_05000158_WORKAROUND; | ||
| 152 | #ifdef CONFIG_BLKFIN_WT | ||
| 153 | d_data |= CPLB_L1_AOW | CPLB_WT; | ||
| 154 | #endif | ||
| 155 | #endif | ||
| 156 | mask = current_rwx_mask; | ||
| 157 | if (mask) { | ||
| 158 | int page = addr >> PAGE_SHIFT; | ||
| 159 | int offs = page >> 5; | ||
| 160 | int bit = 1 << (page & 31); | ||
| 161 | |||
| 162 | if (mask[offs] & bit) | ||
| 163 | d_data |= CPLB_USER_RD; | ||
| 164 | |||
| 165 | mask += page_mask_nelts; | ||
| 166 | if (mask[offs] & bit) | ||
| 167 | d_data |= CPLB_USER_WR; | ||
| 168 | } | ||
| 169 | |||
| 170 | idx = evict_one_dcplb(); | ||
| 171 | |||
| 172 | addr &= PAGE_MASK; | ||
| 173 | dcplb_tbl[idx].addr = addr; | ||
| 174 | dcplb_tbl[idx].data = d_data; | ||
| 175 | |||
| 176 | disable_dcplb(); | ||
| 177 | bfin_write32(DCPLB_DATA0 + idx * 4, d_data); | ||
| 178 | bfin_write32(DCPLB_ADDR0 + idx * 4, addr); | ||
| 179 | enable_dcplb(); | ||
| 180 | |||
| 181 | return 0; | ||
| 182 | } | ||
| 183 | |||
| 184 | static noinline int icplb_miss(void) | ||
| 185 | { | ||
| 186 | unsigned long addr = bfin_read_ICPLB_FAULT_ADDR(); | ||
| 187 | int status = bfin_read_ICPLB_STATUS(); | ||
| 188 | int idx; | ||
| 189 | unsigned long i_data; | ||
| 190 | |||
| 191 | nr_icplb_miss++; | ||
| 192 | if (status & FAULT_USERSUPV) | ||
| 193 | nr_icplb_supv_miss++; | ||
| 194 | |||
| 195 | if (addr >= _ramend) | ||
| 196 | return CPLB_PROT_VIOL; | ||
| 197 | |||
| 198 | /* | ||
| 199 | * First, try to find a CPLB that matches this address. If we | ||
| 200 | * find one, then the fact that we're in the miss handler means | ||
| 201 | * that the instruction crosses a page boundary. | ||
| 202 | */ | ||
| 203 | for (idx = first_switched_icplb; idx < MAX_CPLBS; idx++) { | ||
| 204 | if (icplb_tbl[idx].data & CPLB_VALID) { | ||
| 205 | unsigned long this_addr = icplb_tbl[idx].addr; | ||
| 206 | if (this_addr <= addr && this_addr + PAGE_SIZE > addr) { | ||
| 207 | addr += PAGE_SIZE; | ||
| 208 | break; | ||
| 209 | } | ||
| 210 | } | ||
| 211 | } | ||
| 212 | |||
| 213 | i_data = CPLB_VALID | CPLB_PORTPRIO | PAGE_SIZE_4KB; | ||
| 214 | #ifdef CONFIG_BFIN_ICACHE | ||
| 215 | i_data |= CPLB_L1_CHBL | ANOMALY_05000158_WORKAROUND; | ||
| 216 | #endif | ||
| 217 | |||
| 218 | /* | ||
| 219 | * Two cases to distinguish - a supervisor access must necessarily | ||
| 220 | * be for a module page; we grant it unconditionally (could do better | ||
| 221 | * here in the future). Otherwise, check the x bitmap of the current | ||
| 222 | * process. | ||
| 223 | */ | ||
| 224 | if (!(status & FAULT_USERSUPV)) { | ||
| 225 | unsigned long *mask = current_rwx_mask; | ||
| 226 | |||
| 227 | if (mask) { | ||
| 228 | int page = addr >> PAGE_SHIFT; | ||
| 229 | int offs = page >> 5; | ||
| 230 | int bit = 1 << (page & 31); | ||
| 231 | |||
| 232 | mask += 2 * page_mask_nelts; | ||
| 233 | if (mask[offs] & bit) | ||
| 234 | i_data |= CPLB_USER_RD; | ||
| 235 | } | ||
| 236 | } | ||
| 237 | |||
| 238 | idx = evict_one_icplb(); | ||
| 239 | addr &= PAGE_MASK; | ||
| 240 | icplb_tbl[idx].addr = addr; | ||
| 241 | icplb_tbl[idx].data = i_data; | ||
| 242 | |||
| 243 | disable_icplb(); | ||
| 244 | bfin_write32(ICPLB_DATA0 + idx * 4, i_data); | ||
| 245 | bfin_write32(ICPLB_ADDR0 + idx * 4, addr); | ||
| 246 | enable_icplb(); | ||
| 247 | |||
| 248 | return 0; | ||
| 249 | } | ||
| 250 | |||
| 251 | static noinline int dcplb_protection_fault(void) | ||
| 252 | { | ||
| 253 | unsigned long addr = bfin_read_DCPLB_FAULT_ADDR(); | ||
| 254 | int status = bfin_read_DCPLB_STATUS(); | ||
| 255 | |||
| 256 | nr_dcplb_prot++; | ||
| 257 | |||
| 258 | if (status & FAULT_RW) { | ||
| 259 | int idx = faulting_cplb_index(status); | ||
| 260 | unsigned long data = dcplb_tbl[idx].data; | ||
| 261 | if (!(data & CPLB_WT) && !(data & CPLB_DIRTY) && | ||
| 262 | write_permitted(status, data)) { | ||
| 263 | data |= CPLB_DIRTY; | ||
| 264 | dcplb_tbl[idx].data = data; | ||
| 265 | bfin_write32(DCPLB_DATA0 + idx * 4, data); | ||
| 266 | return 0; | ||
| 267 | } | ||
| 268 | } | ||
| 269 | return CPLB_PROT_VIOL; | ||
| 270 | } | ||
| 271 | |||
| 272 | int cplb_hdr(int seqstat, struct pt_regs *regs) | ||
| 273 | { | ||
| 274 | int cause = seqstat & 0x3f; | ||
| 275 | switch (cause) { | ||
| 276 | case 0x23: | ||
| 277 | return dcplb_protection_fault(); | ||
| 278 | case 0x2C: | ||
| 279 | return icplb_miss(); | ||
| 280 | case 0x26: | ||
| 281 | return dcplb_miss(); | ||
| 282 | default: | ||
| 283 | return 1; | ||
| 284 | panic_cplb_error(seqstat, regs); | ||
| 285 | } | ||
| 286 | } | ||
| 287 | |||
| 288 | void flush_switched_cplbs(void) | ||
| 289 | { | ||
| 290 | int i; | ||
| 291 | |||
| 292 | nr_cplb_flush++; | ||
| 293 | |||
| 294 | disable_icplb(); | ||
| 295 | for (i = first_switched_icplb; i < MAX_CPLBS; i++) { | ||
| 296 | icplb_tbl[i].data = 0; | ||
| 297 | bfin_write32(ICPLB_DATA0 + i * 4, 0); | ||
| 298 | } | ||
| 299 | enable_icplb(); | ||
| 300 | |||
| 301 | disable_dcplb(); | ||
| 302 | for (i = first_mask_dcplb; i < MAX_CPLBS; i++) { | ||
| 303 | dcplb_tbl[i].data = 0; | ||
| 304 | bfin_write32(DCPLB_DATA0 + i * 4, 0); | ||
| 305 | } | ||
| 306 | enable_dcplb(); | ||
| 307 | } | ||
| 308 | |||
| 309 | void set_mask_dcplbs(unsigned long *masks) | ||
| 310 | { | ||
| 311 | int i; | ||
| 312 | unsigned long addr = (unsigned long)masks; | ||
| 313 | unsigned long d_data; | ||
| 314 | current_rwx_mask = masks; | ||
| 315 | |||
| 316 | if (!masks) | ||
| 317 | return; | ||
| 318 | |||
| 319 | d_data = CPLB_SUPV_WR | CPLB_VALID | CPLB_DIRTY | PAGE_SIZE_4KB; | ||
| 320 | #ifdef CONFIG_BFIN_DCACHE | ||
| 321 | d_data |= CPLB_L1_CHBL; | ||
| 322 | #ifdef CONFIG_BLKFIN_WT | ||
| 323 | d_data |= CPLB_L1_AOW | CPLB_WT; | ||
| 324 | #endif | ||
| 325 | #endif | ||
| 326 | |||
| 327 | disable_dcplb(); | ||
| 328 | for (i = first_mask_dcplb; i < first_switched_dcplb; i++) { | ||
| 329 | dcplb_tbl[i].addr = addr; | ||
| 330 | dcplb_tbl[i].data = d_data; | ||
| 331 | bfin_write32(DCPLB_DATA0 + i * 4, d_data); | ||
| 332 | bfin_write32(DCPLB_ADDR0 + i * 4, addr); | ||
| 333 | addr += PAGE_SIZE; | ||
| 334 | } | ||
| 335 | enable_dcplb(); | ||
| 336 | } | ||
| 337 | |||
| 338 | #endif | ||
diff --git a/arch/blackfin/kernel/setup.c b/arch/blackfin/kernel/setup.c index a03c2dfff4a3..1a942a721d51 100644 --- a/arch/blackfin/kernel/setup.c +++ b/arch/blackfin/kernel/setup.c | |||
| @@ -238,7 +238,12 @@ void __init setup_arch(char **cmdline_p) | |||
| 238 | memory_end = _ramend - DMA_UNCACHED_REGION; | 238 | memory_end = _ramend - DMA_UNCACHED_REGION; |
| 239 | 239 | ||
| 240 | _ramstart = (unsigned long)__bss_stop; | 240 | _ramstart = (unsigned long)__bss_stop; |
| 241 | #ifdef CONFIG_MPU | ||
| 242 | /* Round up to multiple of 4MB. */ | ||
| 243 | memory_start = (_ramstart + 0x3fffff) & ~0x3fffff; | ||
| 244 | #else | ||
| 241 | memory_start = PAGE_ALIGN(_ramstart); | 245 | memory_start = PAGE_ALIGN(_ramstart); |
| 246 | #endif | ||
| 242 | 247 | ||
| 243 | #if defined(CONFIG_MTD_UCLINUX) | 248 | #if defined(CONFIG_MTD_UCLINUX) |
| 244 | /* generic memory mapped MTD driver */ | 249 | /* generic memory mapped MTD driver */ |
| @@ -307,6 +312,11 @@ void __init setup_arch(char **cmdline_p) | |||
| 307 | printk(KERN_NOTICE "Warning: limiting memory to %liMB due to hardware anomaly 05000263\n", memory_end >> 20); | 312 | printk(KERN_NOTICE "Warning: limiting memory to %liMB due to hardware anomaly 05000263\n", memory_end >> 20); |
| 308 | #endif /* ANOMALY_05000263 */ | 313 | #endif /* ANOMALY_05000263 */ |
| 309 | 314 | ||
| 315 | #ifdef CONFIG_MPU | ||
| 316 | page_mask_nelts = ((_ramend >> PAGE_SHIFT) + 31) / 32; | ||
| 317 | page_mask_order = get_order(3 * page_mask_nelts * sizeof(long)); | ||
| 318 | #endif | ||
| 319 | |||
| 310 | #if !defined(CONFIG_MTD_UCLINUX) | 320 | #if !defined(CONFIG_MTD_UCLINUX) |
| 311 | memory_end -= SIZE_4K; /*In case there is no valid CPLB behind memory_end make sure we don't get to close*/ | 321 | memory_end -= SIZE_4K; /*In case there is no valid CPLB behind memory_end make sure we don't get to close*/ |
| 312 | #endif | 322 | #endif |
diff --git a/arch/blackfin/mach-common/entry.S b/arch/blackfin/mach-common/entry.S index 58f7ad617992..c2e81a10c47b 100644 --- a/arch/blackfin/mach-common/entry.S +++ b/arch/blackfin/mach-common/entry.S | |||
| @@ -95,6 +95,9 @@ ENTRY(_ex_workaround_261) | |||
| 95 | R6 = 0x26; /* Data CPLB Miss */ | 95 | R6 = 0x26; /* Data CPLB Miss */ |
| 96 | cc = R6 == R7; | 96 | cc = R6 == R7; |
| 97 | if cc jump _ex_dcplb_miss (BP); | 97 | if cc jump _ex_dcplb_miss (BP); |
| 98 | R6 = 0x23; /* Data CPLB Miss */ | ||
| 99 | cc = R6 == R7; | ||
| 100 | if cc jump _ex_dcplb_viol (BP); | ||
| 98 | /* Handle 0x23 Data CPLB Protection Violation | 101 | /* Handle 0x23 Data CPLB Protection Violation |
| 99 | * and Data CPLB Multiple Hits - Linux Trap Zero | 102 | * and Data CPLB Multiple Hits - Linux Trap Zero |
| 100 | */ | 103 | */ |
| @@ -102,17 +105,33 @@ ENTRY(_ex_workaround_261) | |||
| 102 | ENDPROC(_ex_workaround_261) | 105 | ENDPROC(_ex_workaround_261) |
| 103 | 106 | ||
| 104 | #else | 107 | #else |
| 108 | #ifdef CONFIG_MPU | ||
| 109 | #define _ex_dviol _ex_dcplb_viol | ||
| 110 | #else | ||
| 105 | #define _ex_dviol _ex_trap_c | 111 | #define _ex_dviol _ex_trap_c |
| 112 | #endif | ||
| 106 | #define _ex_dmiss _ex_dcplb_miss | 113 | #define _ex_dmiss _ex_dcplb_miss |
| 107 | #define _ex_dmult _ex_trap_c | 114 | #define _ex_dmult _ex_trap_c |
| 108 | #endif | 115 | #endif |
| 109 | 116 | ||
| 117 | |||
| 118 | ENTRY(_ex_dcplb_viol) | ||
| 110 | ENTRY(_ex_dcplb_miss) | 119 | ENTRY(_ex_dcplb_miss) |
| 111 | ENTRY(_ex_icplb_miss) | 120 | ENTRY(_ex_icplb_miss) |
| 112 | (R7:6,P5:4) = [sp++]; | 121 | (R7:6,P5:4) = [sp++]; |
| 113 | ASTAT = [sp++]; | 122 | ASTAT = [sp++]; |
| 114 | SAVE_ALL_SYS | 123 | SAVE_ALL_SYS |
| 124 | #ifdef CONFIG_MPU | ||
| 125 | R0 = SEQSTAT; | ||
| 126 | R1 = SP; | ||
| 127 | sp += -12; | ||
| 128 | call _cplb_hdr; | ||
| 129 | sp += 12; | ||
| 130 | CC = R0 == 0; | ||
| 131 | IF !CC JUMP _handle_bad_cplb; | ||
| 132 | #else | ||
| 115 | call __cplb_hdr; | 133 | call __cplb_hdr; |
| 134 | #endif | ||
| 116 | DEBUG_START_HWTRACE(p5, r7) | 135 | DEBUG_START_HWTRACE(p5, r7) |
| 117 | RESTORE_ALL_SYS | 136 | RESTORE_ALL_SYS |
| 118 | SP = EX_SCRATCH_REG; | 137 | SP = EX_SCRATCH_REG; |
diff --git a/arch/blackfin/mm/init.c b/arch/blackfin/mm/init.c index e97ea8fc8dc4..9f007cace6e6 100644 --- a/arch/blackfin/mm/init.c +++ b/arch/blackfin/mm/init.c | |||
| @@ -184,13 +184,15 @@ static __init void free_init_pages(const char *what, unsigned long begin, unsign | |||
| 184 | #ifdef CONFIG_BLK_DEV_INITRD | 184 | #ifdef CONFIG_BLK_DEV_INITRD |
| 185 | void __init free_initrd_mem(unsigned long start, unsigned long end) | 185 | void __init free_initrd_mem(unsigned long start, unsigned long end) |
| 186 | { | 186 | { |
| 187 | #ifndef CONFIG_MPU | ||
| 187 | free_init_pages("initrd memory", start, end); | 188 | free_init_pages("initrd memory", start, end); |
| 189 | #endif | ||
| 188 | } | 190 | } |
| 189 | #endif | 191 | #endif |
| 190 | 192 | ||
| 191 | void __init free_initmem(void) | 193 | void __init free_initmem(void) |
| 192 | { | 194 | { |
| 193 | #ifdef CONFIG_RAMKERNEL | 195 | #if defined CONFIG_RAMKERNEL && !defined CONFIG_MPU |
| 194 | free_init_pages("unused kernel memory", | 196 | free_init_pages("unused kernel memory", |
| 195 | (unsigned long)(&__init_begin), | 197 | (unsigned long)(&__init_begin), |
| 196 | (unsigned long)(&__init_end)); | 198 | (unsigned long)(&__init_end)); |
diff --git a/include/asm-blackfin/cplb-mpu.h b/include/asm-blackfin/cplb-mpu.h new file mode 100644 index 000000000000..75c67b99d607 --- /dev/null +++ b/include/asm-blackfin/cplb-mpu.h | |||
| @@ -0,0 +1,61 @@ | |||
| 1 | /* | ||
| 2 | * File: include/asm-blackfin/cplbinit.h | ||
| 3 | * Based on: | ||
| 4 | * Author: | ||
| 5 | * | ||
| 6 | * Created: | ||
| 7 | * Description: | ||
| 8 | * | ||
| 9 | * Modified: | ||
| 10 | * Copyright 2004-2006 Analog Devices Inc. | ||
| 11 | * | ||
| 12 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | ||
| 13 | * | ||
| 14 | * This program is free software; you can redistribute it and/or modify | ||
| 15 | * it under the terms of the GNU General Public License as published by | ||
| 16 | * the Free Software Foundation; either version 2 of the License, or | ||
| 17 | * (at your option) any later version. | ||
| 18 | * | ||
| 19 | * This program is distributed in the hope that it will be useful, | ||
| 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 22 | * GNU General Public License for more details. | ||
| 23 | * | ||
| 24 | * You should have received a copy of the GNU General Public License | ||
| 25 | * along with this program; if not, see the file COPYING, or write | ||
| 26 | * to the Free Software Foundation, Inc., | ||
| 27 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 28 | */ | ||
| 29 | #ifndef __ASM_BFIN_CPLB_MPU_H | ||
| 30 | #define __ASM_BFIN_CPLB_MPU_H | ||
| 31 | |||
| 32 | struct cplb_entry { | ||
| 33 | unsigned long data, addr; | ||
| 34 | }; | ||
| 35 | |||
| 36 | struct mem_region { | ||
| 37 | unsigned long start, end; | ||
| 38 | unsigned long dcplb_data; | ||
| 39 | unsigned long icplb_data; | ||
| 40 | }; | ||
| 41 | |||
| 42 | extern struct cplb_entry dcplb_tbl[MAX_CPLBS]; | ||
| 43 | extern struct cplb_entry icplb_tbl[MAX_CPLBS]; | ||
| 44 | extern int first_switched_icplb; | ||
| 45 | extern int first_mask_dcplb; | ||
| 46 | extern int first_switched_dcplb; | ||
| 47 | |||
| 48 | extern int nr_dcplb_miss, nr_icplb_miss, nr_icplb_supv_miss, nr_dcplb_prot; | ||
| 49 | extern int nr_cplb_flush; | ||
| 50 | |||
| 51 | extern int page_mask_order; | ||
| 52 | extern int page_mask_nelts; | ||
| 53 | |||
| 54 | extern unsigned long *current_rwx_mask; | ||
| 55 | |||
| 56 | extern void flush_switched_cplbs(void); | ||
| 57 | extern void set_mask_dcplbs(unsigned long *); | ||
| 58 | |||
| 59 | extern void __noreturn panic_cplb_error(int seqstat, struct pt_regs *); | ||
| 60 | |||
| 61 | #endif /* __ASM_BFIN_CPLB_MPU_H */ | ||
diff --git a/include/asm-blackfin/cplb.h b/include/asm-blackfin/cplb.h index 06828d77a58f..654375c2b746 100644 --- a/include/asm-blackfin/cplb.h +++ b/include/asm-blackfin/cplb.h | |||
| @@ -65,7 +65,11 @@ | |||
| 65 | #define SIZE_1M 0x00100000 /* 1M */ | 65 | #define SIZE_1M 0x00100000 /* 1M */ |
| 66 | #define SIZE_4M 0x00400000 /* 4M */ | 66 | #define SIZE_4M 0x00400000 /* 4M */ |
| 67 | 67 | ||
| 68 | #ifdef CONFIG_MPU | ||
| 69 | #define MAX_CPLBS 16 | ||
| 70 | #else | ||
| 68 | #define MAX_CPLBS (16 * 2) | 71 | #define MAX_CPLBS (16 * 2) |
| 72 | #endif | ||
| 69 | 73 | ||
| 70 | #define ASYNC_MEMORY_CPLB_COVERAGE ((ASYNC_BANK0_SIZE + ASYNC_BANK1_SIZE + \ | 74 | #define ASYNC_MEMORY_CPLB_COVERAGE ((ASYNC_BANK0_SIZE + ASYNC_BANK1_SIZE + \ |
| 71 | ASYNC_BANK2_SIZE + ASYNC_BANK3_SIZE) / SIZE_4M) | 75 | ASYNC_BANK2_SIZE + ASYNC_BANK3_SIZE) / SIZE_4M) |
diff --git a/include/asm-blackfin/cplbinit.h b/include/asm-blackfin/cplbinit.h index c4d0596e8e9f..0eb1c1b685a7 100644 --- a/include/asm-blackfin/cplbinit.h +++ b/include/asm-blackfin/cplbinit.h | |||
| @@ -33,6 +33,12 @@ | |||
| 33 | #include <asm/blackfin.h> | 33 | #include <asm/blackfin.h> |
| 34 | #include <asm/cplb.h> | 34 | #include <asm/cplb.h> |
| 35 | 35 | ||
| 36 | #ifdef CONFIG_MPU | ||
| 37 | |||
| 38 | #include <asm/cplb-mpu.h> | ||
| 39 | |||
| 40 | #else | ||
| 41 | |||
| 36 | #define INITIAL_T 0x1 | 42 | #define INITIAL_T 0x1 |
| 37 | #define SWITCH_T 0x2 | 43 | #define SWITCH_T 0x2 |
| 38 | #define I_CPLB 0x4 | 44 | #define I_CPLB 0x4 |
| @@ -79,6 +85,8 @@ extern u_long ipdt_swapcount_table[]; | |||
| 79 | extern u_long dpdt_swapcount_table[]; | 85 | extern u_long dpdt_swapcount_table[]; |
| 80 | #endif | 86 | #endif |
| 81 | 87 | ||
| 88 | #endif /* CONFIG_MPU */ | ||
| 89 | |||
| 82 | extern unsigned long reserved_mem_dcache_on; | 90 | extern unsigned long reserved_mem_dcache_on; |
| 83 | extern unsigned long reserved_mem_icache_on; | 91 | extern unsigned long reserved_mem_icache_on; |
| 84 | 92 | ||
diff --git a/include/asm-blackfin/mmu.h b/include/asm-blackfin/mmu.h index 11d52f1167d0..757e43906ed4 100644 --- a/include/asm-blackfin/mmu.h +++ b/include/asm-blackfin/mmu.h | |||
| @@ -24,7 +24,9 @@ typedef struct { | |||
| 24 | unsigned long exec_fdpic_loadmap; | 24 | unsigned long exec_fdpic_loadmap; |
| 25 | unsigned long interp_fdpic_loadmap; | 25 | unsigned long interp_fdpic_loadmap; |
| 26 | #endif | 26 | #endif |
| 27 | 27 | #ifdef CONFIG_MPU | |
| 28 | unsigned long *page_rwx_mask; | ||
| 29 | #endif | ||
| 28 | } mm_context_t; | 30 | } mm_context_t; |
| 29 | 31 | ||
| 30 | #endif | 32 | #endif |
diff --git a/include/asm-blackfin/mmu_context.h b/include/asm-blackfin/mmu_context.h index c5c71a6aaf19..b5eb67596ad5 100644 --- a/include/asm-blackfin/mmu_context.h +++ b/include/asm-blackfin/mmu_context.h | |||
| @@ -30,9 +30,12 @@ | |||
| 30 | #ifndef __BLACKFIN_MMU_CONTEXT_H__ | 30 | #ifndef __BLACKFIN_MMU_CONTEXT_H__ |
| 31 | #define __BLACKFIN_MMU_CONTEXT_H__ | 31 | #define __BLACKFIN_MMU_CONTEXT_H__ |
| 32 | 32 | ||
| 33 | #include <linux/gfp.h> | ||
| 34 | #include <linux/sched.h> | ||
| 33 | #include <asm/setup.h> | 35 | #include <asm/setup.h> |
| 34 | #include <asm/page.h> | 36 | #include <asm/page.h> |
| 35 | #include <asm/pgalloc.h> | 37 | #include <asm/pgalloc.h> |
| 38 | #include <asm/cplbinit.h> | ||
| 36 | 39 | ||
| 37 | extern void *current_l1_stack_save; | 40 | extern void *current_l1_stack_save; |
| 38 | extern int nr_l1stack_tasks; | 41 | extern int nr_l1stack_tasks; |
| @@ -50,6 +53,12 @@ static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk) | |||
| 50 | static inline int | 53 | static inline int |
| 51 | init_new_context(struct task_struct *tsk, struct mm_struct *mm) | 54 | init_new_context(struct task_struct *tsk, struct mm_struct *mm) |
| 52 | { | 55 | { |
| 56 | #ifdef CONFIG_MPU | ||
| 57 | unsigned long p = __get_free_pages(GFP_KERNEL, page_mask_order); | ||
| 58 | mm->context.page_rwx_mask = (unsigned long *)p; | ||
| 59 | memset(mm->context.page_rwx_mask, 0, | ||
| 60 | page_mask_nelts * 3 * sizeof(long)); | ||
| 61 | #endif | ||
| 53 | return 0; | 62 | return 0; |
| 54 | } | 63 | } |
| 55 | 64 | ||
| @@ -73,6 +82,11 @@ static inline void destroy_context(struct mm_struct *mm) | |||
| 73 | sram_free(tmp->addr); | 82 | sram_free(tmp->addr); |
| 74 | kfree(tmp); | 83 | kfree(tmp); |
| 75 | } | 84 | } |
| 85 | #ifdef CONFIG_MPU | ||
| 86 | if (current_rwx_mask == mm->context.page_rwx_mask) | ||
| 87 | current_rwx_mask = NULL; | ||
| 88 | free_pages((unsigned long)mm->context.page_rwx_mask, page_mask_order); | ||
| 89 | #endif | ||
| 76 | } | 90 | } |
| 77 | 91 | ||
| 78 | static inline unsigned long | 92 | static inline unsigned long |
| @@ -106,9 +120,21 @@ activate_l1stack(struct mm_struct *mm, unsigned long sp_base) | |||
| 106 | 120 | ||
| 107 | #define deactivate_mm(tsk,mm) do { } while (0) | 121 | #define deactivate_mm(tsk,mm) do { } while (0) |
| 108 | 122 | ||
| 109 | static inline void activate_mm(struct mm_struct *prev_mm, | 123 | #define activate_mm(prev, next) switch_mm(prev, next, NULL) |
| 110 | struct mm_struct *next_mm) | 124 | |
| 125 | static inline void switch_mm(struct mm_struct *prev_mm, struct mm_struct *next_mm, | ||
| 126 | struct task_struct *tsk) | ||
| 111 | { | 127 | { |
| 128 | if (prev_mm == next_mm) | ||
| 129 | return; | ||
| 130 | #ifdef CONFIG_MPU | ||
| 131 | if (prev_mm->context.page_rwx_mask == current_rwx_mask) { | ||
| 132 | flush_switched_cplbs(); | ||
| 133 | set_mask_dcplbs(next_mm->context.page_rwx_mask); | ||
| 134 | } | ||
| 135 | #endif | ||
| 136 | |||
| 137 | /* L1 stack switching. */ | ||
| 112 | if (!next_mm->context.l1_stack_save) | 138 | if (!next_mm->context.l1_stack_save) |
| 113 | return; | 139 | return; |
| 114 | if (next_mm->context.l1_stack_save == current_l1_stack_save) | 140 | if (next_mm->context.l1_stack_save == current_l1_stack_save) |
| @@ -120,10 +146,36 @@ static inline void activate_mm(struct mm_struct *prev_mm, | |||
| 120 | memcpy(l1_stack_base, current_l1_stack_save, l1_stack_len); | 146 | memcpy(l1_stack_base, current_l1_stack_save, l1_stack_len); |
| 121 | } | 147 | } |
| 122 | 148 | ||
| 123 | static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, | 149 | #ifdef CONFIG_MPU |
| 124 | struct task_struct *tsk) | 150 | static inline void protect_page(struct mm_struct *mm, unsigned long addr, |
| 151 | unsigned long flags) | ||
| 152 | { | ||
| 153 | unsigned long *mask = mm->context.page_rwx_mask; | ||
| 154 | unsigned long page = addr >> 12; | ||
| 155 | unsigned long idx = page >> 5; | ||
| 156 | unsigned long bit = 1 << (page & 31); | ||
| 157 | |||
| 158 | if (flags & VM_MAYREAD) | ||
| 159 | mask[idx] |= bit; | ||
| 160 | else | ||
| 161 | mask[idx] &= ~bit; | ||
| 162 | mask += page_mask_nelts; | ||
| 163 | if (flags & VM_MAYWRITE) | ||
| 164 | mask[idx] |= bit; | ||
| 165 | else | ||
| 166 | mask[idx] &= ~bit; | ||
| 167 | mask += page_mask_nelts; | ||
| 168 | if (flags & VM_MAYEXEC) | ||
| 169 | mask[idx] |= bit; | ||
| 170 | else | ||
| 171 | mask[idx] &= ~bit; | ||
| 172 | } | ||
| 173 | |||
| 174 | static inline void update_protections(struct mm_struct *mm) | ||
| 125 | { | 175 | { |
| 126 | activate_mm(prev, next); | 176 | flush_switched_cplbs(); |
| 177 | set_mask_dcplbs(mm->context.page_rwx_mask); | ||
| 127 | } | 178 | } |
| 179 | #endif | ||
| 128 | 180 | ||
| 129 | #endif | 181 | #endif |
