aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBernd Schmidt <bernd.schmidt@analog.com>2008-01-27 05:39:16 -0500
committerBryan Wu <bryan.wu@analog.com>2008-01-27 05:39:16 -0500
commitb97b8a998397e8c64699559099fa9febffae2b4d (patch)
tree689188b6336cf45b4391f5bc764878e342b9ac90
parent2047e40d724d42928c0b5994a1568c1b738efdb7 (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/Kconfig9
-rw-r--r--arch/blackfin/Makefile6
-rw-r--r--arch/blackfin/kernel/cplb-mpu/Makefile8
-rw-r--r--arch/blackfin/kernel/cplb-mpu/cacheinit.c62
-rw-r--r--arch/blackfin/kernel/cplb-mpu/cplbinfo.c144
-rw-r--r--arch/blackfin/kernel/cplb-mpu/cplbinit.c91
-rw-r--r--arch/blackfin/kernel/cplb-mpu/cplbmgr.c338
-rw-r--r--arch/blackfin/kernel/setup.c10
-rw-r--r--arch/blackfin/mach-common/entry.S19
-rw-r--r--arch/blackfin/mm/init.c4
-rw-r--r--include/asm-blackfin/cplb-mpu.h61
-rw-r--r--include/asm-blackfin/cplb.h4
-rw-r--r--include/asm-blackfin/cplbinit.h8
-rw-r--r--include/asm-blackfin/mmu.h4
-rw-r--r--include/asm-blackfin/mmu_context.h62
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
769config 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
768comment "Asynchonous Memory Configuration" 777comment "Asynchonous Memory Configuration"
769 778
770menu "EBIU_AMGCTL Global Control" 779menu "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)/
82core-y += arch/$(ARCH)/mach-$(MACHINE)/boards/ 82core-y += arch/$(ARCH)/mach-$(MACHINE)/boards/
83endif 83endif
84 84
85core-y += arch/$(ARCH)/kernel/cplb-nompu/ 85ifeq ($(CONFIG_MPU),y)
86core-y += arch/$(ARCH)/kernel/cplb-mpu/
87else
88core-y += arch/$(ARCH)/kernel/cplb-nompu/
89endif
86 90
87libs-y += arch/$(ARCH)/lib/ 91libs-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
5obj-y := cplbinit.o cacheinit.o cplbmgr.o
6
7obj-$(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)
28void 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)
46void 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
50static char page_size_string_table[][4] = { "1K", "4K", "1M", "4M" };
51
52static 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
77int 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
107static 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
124static 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
138static void __exit cplbinfo_exit(void)
139{
140 remove_proc_entry("cplbinfo", NULL);
141}
142
143module_init(cplbinfo_init);
144module_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
29struct cplb_entry icplb_tbl[MAX_CPLBS];
30struct cplb_entry dcplb_tbl[MAX_CPLBS];
31
32int first_switched_icplb, first_switched_dcplb;
33int first_mask_dcplb;
34
35void __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
32int page_mask_nelts;
33int page_mask_order;
34unsigned long *current_rwx_mask;
35
36int nr_dcplb_miss, nr_icplb_miss, nr_icplb_supv_miss, nr_dcplb_prot;
37int nr_cplb_flush;
38
39static 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
49static 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
59static 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
69static 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 */
83static 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 */
93static 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. */
102static int icplb_rr_index, dcplb_rr_index;
103
104/*
105 * Find an ICPLB entry to be evicted and return its index.
106 */
107static 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
122static 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
137static 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
184static 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
251static 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
272int 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
288void 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
309void 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)
102ENDPROC(_ex_workaround_261) 105ENDPROC(_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
118ENTRY(_ex_dcplb_viol)
110ENTRY(_ex_dcplb_miss) 119ENTRY(_ex_dcplb_miss)
111ENTRY(_ex_icplb_miss) 120ENTRY(_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
185void __init free_initrd_mem(unsigned long start, unsigned long end) 185void __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
191void __init free_initmem(void) 193void __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
32struct cplb_entry {
33 unsigned long data, addr;
34};
35
36struct mem_region {
37 unsigned long start, end;
38 unsigned long dcplb_data;
39 unsigned long icplb_data;
40};
41
42extern struct cplb_entry dcplb_tbl[MAX_CPLBS];
43extern struct cplb_entry icplb_tbl[MAX_CPLBS];
44extern int first_switched_icplb;
45extern int first_mask_dcplb;
46extern int first_switched_dcplb;
47
48extern int nr_dcplb_miss, nr_icplb_miss, nr_icplb_supv_miss, nr_dcplb_prot;
49extern int nr_cplb_flush;
50
51extern int page_mask_order;
52extern int page_mask_nelts;
53
54extern unsigned long *current_rwx_mask;
55
56extern void flush_switched_cplbs(void);
57extern void set_mask_dcplbs(unsigned long *);
58
59extern 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[];
79extern u_long dpdt_swapcount_table[]; 85extern u_long dpdt_swapcount_table[];
80#endif 86#endif
81 87
88#endif /* CONFIG_MPU */
89
82extern unsigned long reserved_mem_dcache_on; 90extern unsigned long reserved_mem_dcache_on;
83extern unsigned long reserved_mem_icache_on; 91extern 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
37extern void *current_l1_stack_save; 40extern void *current_l1_stack_save;
38extern int nr_l1stack_tasks; 41extern int nr_l1stack_tasks;
@@ -50,6 +53,12 @@ static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk)
50static inline int 53static inline int
51init_new_context(struct task_struct *tsk, struct mm_struct *mm) 54init_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
78static inline unsigned long 92static 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
109static 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
125static 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
123static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, 149#ifdef CONFIG_MPU
124 struct task_struct *tsk) 150static 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
174static 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