Commit b97b8a99 authored by Bernd Schmidt's avatar Bernd Schmidt Committed by Bryan Wu

[Blackfin] arch: Initial checkin of the memory protection support.

Enable it with CONFIG_MPU.
Signed-off-by: default avatarBernd Schmidt <bernd.schmidt@analog.com>
Signed-off-by: default avatarBryan Wu <bryan.wu@analog.com>
parent 2047e40d
...@@ -765,6 +765,15 @@ config L1_MAX_PIECE ...@@ -765,6 +765,15 @@ config L1_MAX_PIECE
Set the max memory pieces for the L1 SRAM allocation algorithm. Set the max memory pieces for the L1 SRAM allocation algorithm.
Min value is 16. Max value is 1024. Min value is 16. Max value is 1024.
config MPU
bool "Enable the memory protection unit (EXPERIMENTAL)"
default n
help
Use the processor's MPU to protect applications from accessing
memory they do not own. This comes at a performance penalty
and is recommended only for debugging.
comment "Asynchonous Memory Configuration" comment "Asynchonous Memory Configuration"
menu "EBIU_AMGCTL Global Control" menu "EBIU_AMGCTL Global Control"
......
...@@ -82,7 +82,11 @@ core-y += arch/$(ARCH)/mach-$(MACHINE)/ ...@@ -82,7 +82,11 @@ core-y += arch/$(ARCH)/mach-$(MACHINE)/
core-y += arch/$(ARCH)/mach-$(MACHINE)/boards/ core-y += arch/$(ARCH)/mach-$(MACHINE)/boards/
endif endif
core-y += arch/$(ARCH)/kernel/cplb-nompu/ ifeq ($(CONFIG_MPU),y)
core-y += arch/$(ARCH)/kernel/cplb-mpu/
else
core-y += arch/$(ARCH)/kernel/cplb-nompu/
endif
libs-y += arch/$(ARCH)/lib/ libs-y += arch/$(ARCH)/lib/
......
#
# arch/blackfin/kernel/cplb-nompu/Makefile
#
obj-y := cplbinit.o cacheinit.o cplbmgr.o
obj-$(CONFIG_CPLB_INFO) += cplbinfo.o
/*
* Copyright 2004-2007 Analog Devices Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see the file COPYING, or write
* to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <linux/cpu.h>
#include <asm/cacheflush.h>
#include <asm/blackfin.h>
#include <asm/cplb.h>
#include <asm/cplbinit.h>
#if defined(CONFIG_BFIN_ICACHE)
void bfin_icache_init(void)
{
unsigned long ctrl;
int i;
SSYNC();
for (i = 0; i < MAX_CPLBS; i++) {
bfin_write32(ICPLB_ADDR0 + i * 4, icplb_tbl[i].addr);
bfin_write32(ICPLB_DATA0 + i * 4, icplb_tbl[i].data);
}
ctrl = bfin_read_IMEM_CONTROL();
ctrl |= IMC | ENICPLB;
bfin_write_IMEM_CONTROL(ctrl);
SSYNC();
}
#endif
#if defined(CONFIG_BFIN_DCACHE)
void bfin_dcache_init(void)
{
unsigned long ctrl;
int i;
SSYNC();
for (i = 0; i < MAX_CPLBS; i++) {
bfin_write32(DCPLB_ADDR0 + i * 4, dcplb_tbl[i].addr);
bfin_write32(DCPLB_DATA0 + i * 4, dcplb_tbl[i].data);
}
ctrl = bfin_read_DMEM_CONTROL();
ctrl |= DMEM_CNTR;
bfin_write_DMEM_CONTROL(ctrl);
SSYNC();
}
#endif
/*
* File: arch/blackfin/mach-common/cplbinfo.c
* Based on:
* Author: Sonic Zhang <sonic.zhang@analog.com>
*
* Created: Jan. 2005
* Description: Display CPLB status
*
* Modified:
* Copyright 2004-2006 Analog Devices Inc.
*
* Bugs: Enter bugs at http://blackfin.uclinux.org/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see the file COPYING, or write
* to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include <asm/current.h>
#include <asm/system.h>
#include <asm/cplb.h>
#include <asm/cplbinit.h>
#include <asm/blackfin.h>
#define CPLB_I 1
#define CPLB_D 2
#define SYNC_SYS SSYNC()
#define SYNC_CORE CSYNC()
#define CPLB_BIT_PAGESIZE 0x30000
static char page_size_string_table[][4] = { "1K", "4K", "1M", "4M" };
static char *cplb_print_entry(char *buf, struct cplb_entry *tbl, int switched)
{
int i;
buf += sprintf(buf, "Index\tAddress\t\tData\tSize\tU/RD\tU/WR\tS/WR\tSwitch\n");
for (i = 0; i < MAX_CPLBS; i++) {
unsigned long data = tbl[i].data;
unsigned long addr = tbl[i].addr;
if (!(data & CPLB_VALID))
continue;
buf +=
sprintf(buf,
"%d\t0x%08lx\t%06lx\t%s\t%c\t%c\t%c\t%c\n",
i, addr, data,
page_size_string_table[(data & 0x30000) >> 16],
(data & CPLB_USER_RD) ? 'Y' : 'N',
(data & CPLB_USER_WR) ? 'Y' : 'N',
(data & CPLB_SUPV_WR) ? 'Y' : 'N',
i < switched ? 'N' : 'Y');
}
buf += sprintf(buf, "\n");
return buf;
}
int cplbinfo_proc_output(char *buf)
{
char *p;
p = buf;
p += sprintf(p, "------------------ CPLB Information ------------------\n\n");
if (bfin_read_IMEM_CONTROL() & ENICPLB) {
p += sprintf(p, "Instruction CPLB entry:\n");
p = cplb_print_entry(p, icplb_tbl, first_switched_icplb);
} else
p += sprintf(p, "Instruction CPLB is disabled.\n\n");
if (1 || bfin_read_DMEM_CONTROL() & ENDCPLB) {
p += sprintf(p, "Data CPLB entry:\n");
p = cplb_print_entry(p, dcplb_tbl, first_switched_dcplb);
} else
p += sprintf(p, "Data CPLB is disabled.\n");
p += sprintf(p, "ICPLB miss: %d\nICPLB supervisor miss: %d\n",
nr_icplb_miss, nr_icplb_supv_miss);
p += sprintf(p, "DCPLB miss: %d\nDCPLB protection fault:%d\n",
nr_dcplb_miss, nr_dcplb_prot);
p += sprintf(p, "CPLB flushes: %d\n",
nr_cplb_flush);
return p - buf;
}
static int cplbinfo_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
int len;
len = cplbinfo_proc_output(page);
if (len <= off + count)
*eof = 1;
*start = page + off;
len -= off;
if (len > count)
len = count;
if (len < 0)
len = 0;
return len;
}
static int __init cplbinfo_init(void)
{
struct proc_dir_entry *entry;
entry = create_proc_entry("cplbinfo", 0, NULL);
if (!entry)
return -ENOMEM;
entry->read_proc = cplbinfo_read_proc;
entry->data = NULL;
return 0;
}
static void __exit cplbinfo_exit(void)
{
remove_proc_entry("cplbinfo", NULL);
}
module_init(cplbinfo_init);
module_exit(cplbinfo_exit);
/*
* Blackfin CPLB initialization
*
* Copyright 2004-2007 Analog Devices Inc.
*
* Bugs: Enter bugs at http://blackfin.uclinux.org/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see the file COPYING, or write
* to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <linux/module.h>
#include <asm/blackfin.h>
#include <asm/cplb.h>
#include <asm/cplbinit.h>
struct cplb_entry icplb_tbl[MAX_CPLBS];
struct cplb_entry dcplb_tbl[MAX_CPLBS];
int first_switched_icplb, first_switched_dcplb;
int first_mask_dcplb;
void __init generate_cpl_tables(void)
{
int i_d, i_i;
unsigned long addr;
unsigned long d_data, i_data;
unsigned long d_cache = 0, i_cache = 0;
#ifdef CONFIG_BFIN_ICACHE
i_cache = CPLB_L1_CHBL | ANOMALY_05000158_WORKAROUND;
#endif
#ifdef CONFIG_BFIN_DCACHE
d_cache = CPLB_L1_CHBL;
#ifdef CONFIG_BLKFIN_WT
d_cache |= CPLB_L1_AOW | CPLB_WT;
#endif
#endif
i_d = i_i = 0;
/* Set up the zero page. */
dcplb_tbl[i_d].addr = 0;
dcplb_tbl[i_d++].data = SDRAM_OOPS | PAGE_SIZE_1KB;
#if 0
icplb_tbl[i_i].addr = 0;
icplb_tbl[i_i++].data = i_cache | CPLB_USER_RD | PAGE_SIZE_4KB;
#endif
/* Cover kernel memory with 4M pages. */
addr = 0;
d_data = d_cache | CPLB_SUPV_WR | CPLB_VALID | PAGE_SIZE_4MB | CPLB_DIRTY;
i_data = i_cache | CPLB_VALID | CPLB_PORTPRIO | PAGE_SIZE_4MB;
for (; addr < memory_start; addr += 4 * 1024 * 1024) {
dcplb_tbl[i_d].addr = addr;
dcplb_tbl[i_d++].data = d_data;
icplb_tbl[i_i].addr = addr;
icplb_tbl[i_i++].data = i_data | (addr == 0 ? CPLB_USER_RD : 0);
}
/* Cover L1 memory. One 4M area for code and data each is enough. */
#if L1_DATA_A_LENGTH > 0 || L1_DATA_B_LENGTH > 0
dcplb_tbl[i_d].addr = L1_DATA_A_START;
dcplb_tbl[i_d++].data = L1_DMEMORY | PAGE_SIZE_4MB;
#endif
icplb_tbl[i_i].addr = L1_CODE_START;
icplb_tbl[i_i++].data = L1_IMEMORY | PAGE_SIZE_4MB;
first_mask_dcplb = i_d;
first_switched_dcplb = i_d + (1 << page_mask_order);
first_switched_icplb = i_i;
while (i_d < MAX_CPLBS)
dcplb_tbl[i_d++].data = 0;
while (i_i < MAX_CPLBS)
icplb_tbl[i_i++].data = 0;
}
/*
* Blackfin CPLB exception handling.
* Copyright 2004-2007 Analog Devices Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see the file COPYING, or write
* to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <linux/module.h>
#include <linux/mm.h>
#include <asm/blackfin.h>
#include <asm/cplbinit.h>
#include <asm/mmu_context.h>
#ifdef CONFIG_BFIN_ICACHE
#define FAULT_RW (1 << 16)
#define FAULT_USERSUPV (1 << 17)
int page_mask_nelts;
int page_mask_order;
unsigned long *current_rwx_mask;
int nr_dcplb_miss, nr_icplb_miss, nr_icplb_supv_miss, nr_dcplb_prot;
int nr_cplb_flush;
static inline void disable_dcplb(void)
{
unsigned long ctrl;
SSYNC();
ctrl = bfin_read_DMEM_CONTROL();
ctrl &= ~ENDCPLB;
bfin_write_DMEM_CONTROL(ctrl);
SSYNC();
}
static inline void enable_dcplb(void)
{
unsigned long ctrl;
SSYNC();
ctrl = bfin_read_DMEM_CONTROL();
ctrl |= ENDCPLB;
bfin_write_DMEM_CONTROL(ctrl);
SSYNC();
}
static inline void disable_icplb(void)
{
unsigned long ctrl;
SSYNC();
ctrl = bfin_read_IMEM_CONTROL();
ctrl &= ~ENICPLB;
bfin_write_IMEM_CONTROL(ctrl);
SSYNC();
}
static inline void enable_icplb(void)
{
unsigned long ctrl;
SSYNC();
ctrl = bfin_read_IMEM_CONTROL();
ctrl |= ENICPLB;
bfin_write_IMEM_CONTROL(ctrl);
SSYNC();
}
/*
* Given the contents of the status register, return the index of the
* CPLB that caused the fault.
*/
static inline int faulting_cplb_index(int status)
{
int signbits = __builtin_bfin_norm_fr1x32(status & 0xFFFF);
return 30 - signbits;
}
/*
* Given the contents of the status register and the DCPLB_DATA contents,
* return true if a write access should be permitted.
*/
static inline int write_permitted(int status, unsigned long data)
{
if (status & FAULT_USERSUPV)
return !!(data & CPLB_SUPV_WR);
else
return !!(data & CPLB_USER_WR);
}
/* Counters to implement round-robin replacement. */
static int icplb_rr_index, dcplb_rr_index;
/*
* Find an ICPLB entry to be evicted and return its index.
*/
static int evict_one_icplb(void)
{
int i;
for (i = first_switched_icplb; i < MAX_CPLBS; i++)
if ((icplb_tbl[i].data & CPLB_VALID) == 0)
return i;
i = first_switched_icplb + icplb_rr_index;
if (i >= MAX_CPLBS) {
i -= MAX_CPLBS - first_switched_icplb;
icplb_rr_index -= MAX_CPLBS - first_switched_icplb;
}
icplb_rr_index++;
return i;
}
static int evict_one_dcplb(void)
{
int i;
for (i = first_switched_dcplb; i < MAX_CPLBS; i++)
if ((dcplb_tbl[i].data & CPLB_VALID) == 0)
return i;
i = first_switched_dcplb + dcplb_rr_index;
if (i >= MAX_CPLBS) {
i -= MAX_CPLBS - first_switched_dcplb;
dcplb_rr_index -= MAX_CPLBS - first_switched_dcplb;
}
dcplb_rr_index++;
return i;
}
static noinline int dcplb_miss(void)
{
unsigned long addr = bfin_read_DCPLB_FAULT_ADDR();
int status = bfin_read_DCPLB_STATUS();
unsigned long *mask;
int idx;
unsigned long d_data;
nr_dcplb_miss++;
if (addr >= _ramend)
return CPLB_PROT_VIOL;
d_data = CPLB_SUPV_WR | CPLB_VALID | CPLB_DIRTY | PAGE_SIZE_4KB;
#ifdef CONFIG_BFIN_DCACHE
d_data |= CPLB_L1_CHBL | ANOMALY_05000158_WORKAROUND;
#ifdef CONFIG_BLKFIN_WT
d_data |= CPLB_L1_AOW | CPLB_WT;
#endif
#endif
mask = current_rwx_mask;
if (mask) {
int page = addr >> PAGE_SHIFT;
int offs = page >> 5;
int bit = 1 << (page & 31);
if (mask[offs] & bit)
d_data |= CPLB_USER_RD;
mask += page_mask_nelts;
if (mask[offs] & bit)
d_data |= CPLB_USER_WR;
}
idx = evict_one_dcplb();
addr &= PAGE_MASK;
dcplb_tbl[idx].addr = addr;
dcplb_tbl[idx].data = d_data;
disable_dcplb();
bfin_write32(DCPLB_DATA0 + idx * 4, d_data);
bfin_write32(DCPLB_ADDR0 + idx * 4, addr);
enable_dcplb();
return 0;
}
static noinline int icplb_miss(void)
{
unsigned long addr = bfin_read_ICPLB_FAULT_ADDR();
int status = bfin_read_ICPLB_STATUS();
int idx;
unsigned long i_data;
nr_icplb_miss++;
if (status & FAULT_USERSUPV)
nr_icplb_supv_miss++;
if (addr >= _ramend)
return CPLB_PROT_VIOL;
/*
* First, try to find a CPLB that matches this address. If we
* find one, then the fact that we're in the miss handler means
* that the instruction crosses a page boundary.
*/
for (idx = first_switched_icplb; idx < MAX_CPLBS; idx++) {
if (icplb_tbl[idx].data & CPLB_VALID) {
unsigned long this_addr = icplb_tbl[idx].addr;
if (this_addr <= addr && this_addr + PAGE_SIZE > addr) {
addr += PAGE_SIZE;
break;
}
}
}
i_data = CPLB_VALID | CPLB_PORTPRIO | PAGE_SIZE_4KB;
#ifdef CONFIG_BFIN_ICACHE
i_data |= CPLB_L1_CHBL | ANOMALY_05000158_WORKAROUND;
#endif
/*
* Two cases to distinguish - a supervisor access must necessarily
* be for a module page; we grant it unconditionally (could do better
* here in the future). Otherwise, check the x bitmap of the current
* process.
*/
if (!(status & FAULT_USERSUPV)) {
unsigned long *mask = current_rwx_mask;
if (mask) {
int page = addr >> PAGE_SHIFT;
int offs = page >> 5;
int bit = 1 << (page & 31);
mask += 2 * page_mask_nelts;
if (mask[offs] & bit)
i_data |= CPLB_USER_RD;
}
}
idx = evict_one_icplb();
addr &= PAGE_MASK;
icplb_tbl[idx].addr = addr;
icplb_tbl[idx].data = i_data;
disable_icplb();
bfin_write32(ICPLB_DATA0 + idx * 4, i_data);
bfin_write32(ICPLB_ADDR0 + idx * 4, addr);
enable_icplb();
return 0;
}
static noinline int dcplb_protection_fault(void)
{
unsigned long addr = bfin_read_DCPLB_FAULT_ADDR();
int status = bfin_read_DCPLB_STATUS();
nr_dcplb_prot++;
if (status & FAULT_RW) {
int idx = faulting_cplb_index(status);
unsigned long data = dcplb_tbl[idx].data;
if (!(data & CPLB_WT) && !(data & CPLB_DIRTY) &&
write_permitted(status, data)) {
data |= CPLB_DIRTY;
dcplb_tbl[idx].data = data;
bfin_write32(DCPLB_DATA0 + idx * 4, data);
return 0;
}
}
return CPLB_PROT_VIOL;
}
int cplb_hdr(int seqstat, struct pt_regs *regs)
{
int cause = seqstat & 0x3f;
switch (cause) {
case 0x23:
return dcplb_protection_fault();
case 0x2C:
return icplb_miss();
case 0x26:
return dcplb_miss();
default:
return 1;
panic_cplb_error(seqstat, regs);
}
}
void flush_switched_cplbs(void)
{
int i;
nr_cplb_flush++;
disable_icplb();
for (i = first_switched_icplb; i < MAX_CPLBS; i++) {
icplb_tbl[i].data = 0;
bfin_write32(ICPLB_DATA0 + i * 4, 0);
}
enable_icplb();
disable_dcplb();
for (i = first_mask_dcplb; i < MAX_CPLBS; i++) {
dcplb_tbl[i].data = 0;
bfin_write32(DCPLB_DATA0 + i * 4, 0);
}
enable_dcplb();
}
void set_mask_dcplbs(unsigned long *masks)
{
int i;
unsigned long addr = (unsigned long)masks;
unsigned long d_data;
current_rwx_mask = masks;
if (!masks)
return;
d_data = CPLB_SUPV_WR | CPLB_VALID | CPLB_DIRTY | PAGE_SIZE_4KB;
#ifdef CONFIG_BFIN_DCACHE
d_data |= CPLB_L1_CHBL;
#ifdef CONFIG_BLKFIN_WT
d_data |= CPLB_L1_AOW | CPLB_WT;
#endif
#endif
disable_dcplb();
for (i = first_mask_dcplb; i < first_switched_dcplb; i++) {
dcplb_tbl[i].addr = addr;
dcplb_tbl[i].data = d_data;
bfin_write32(DCPLB_DATA0 + i * 4, d_data);
bfin_write32(DCPLB_ADDR0 + i * 4, addr);
addr += PAGE_SIZE;
}
enable_dcplb();
}
#endif
...@@ -238,7 +238,12 @@ void __init setup_arch(char **cmdline_p) ...@@ -238,7 +238,12 @@ void __init setup_arch(char **cmdline_p)
memory_end = _ramend - DMA_UNCACHED_REGION; memory_end = _ramend - DMA_UNCACHED_REGION;
_ramstart = (unsigned long)__bss_stop; _ramstart = (unsigned long)__bss_stop;
#ifdef CONFIG_MPU
/* Round up to multiple of 4MB. */
memory_start = (_ramstart + 0x3fffff) & ~0x3fffff;
#else
memory_start = PAGE_ALIGN(_ramstart); memory_start = PAGE_ALIGN(_ramstart);
#endif
#if defined(CONFIG_MTD_UCLINUX) #if defined(CONFIG_MTD_UCLINUX)
/* generic memory mapped MTD driver */ /* generic memory mapped MTD driver */
...@@ -307,6 +312,11 @@ void __init setup_arch(char **cmdline_p) ...@@ -307,6 +312,11 @@ void __init setup_arch(char **cmdline_p)
printk(KERN_NOTICE "Warning: limiting memory to %liMB due to hardware anomaly 05000263\n", memory_end >> 20); printk(KERN_NOTICE "Warning: limiting memory to %liMB due to hardware anomaly 05000263\n", memory_end >> 20);
#endif /* ANOMALY_05000263 */ #endif /* ANOMALY_05000263 */
#ifdef CONFIG_MPU
page_mask_nelts = ((_ramend >> PAGE_SHIFT) + 31) / 32;
page_mask_order = get_order(3 * page_mask_nelts * sizeof(long));
#endif
#if !defined(CONFIG_MTD_UCLINUX) #if !defined(CONFIG_MTD_UCLINUX)
memory_end -= SIZE_4K; /*In case there is no valid CPLB behind memory_end make sure we don't get to close*/ memory_end -= SIZE_4K; /*In case there is no valid CPLB behind memory_end make sure we don't get to close*/
#endif #endif
......
...@@ -95,24 +95,43 @@ ENTRY(_ex_workaround_261) ...@@ -95,24 +95,43 @@ ENTRY(_ex_workaround_261)
R6 = 0x26; /* Data CPLB Miss */ R6 = 0x26; /* Data CPLB Miss */
cc = R6 == R7; cc = R6 == R7;
if cc jump _ex_dcplb_miss (BP); if cc jump _ex_dcplb_miss (BP);
R6 = 0x23; /* Data CPLB Miss */
cc = R6 == R7;
if cc jump _ex_dcplb_viol (BP);
/* Handle 0x23 Data CPLB Protection Violation /* Handle 0x23 Data CPLB Protection Violation
* and Data CPLB Multiple Hits - Linux Trap Zero * and Data CPLB Multiple Hits - Linux Trap Zero
*/ */
jump _ex_trap_c; jump _ex_trap_c;
ENDPROC(_ex_workaround_261) ENDPROC(_ex_workaround_261)
#else
#ifdef CONFIG_MPU
#define _ex_dviol _ex_dcplb_viol
#else #else
#define _ex_dviol _ex_trap_c #define _ex_dviol _ex_trap_c
#endif
#define _ex_dmiss _ex_dcplb_miss #define _ex_dmiss _ex_dcplb_miss
#define _ex_dmult _ex_trap_c #define _ex_dmult _ex_trap_c
#endif #endif
ENTRY(_ex_dcplb_viol)
ENTRY(_ex_dcplb_miss) ENTRY(_ex_dcplb_miss)
ENTRY(_ex_icplb_miss) ENTRY(_ex_icplb_miss)
(R7:6,P5:4) = [sp++]; (R7:6,P5:4) = [sp++];
ASTAT = [sp++]; ASTAT = [sp++];
SAVE_ALL_SYS SAVE_ALL_SYS
#ifdef CONFIG_MPU
R0 = SEQSTAT;
R1 = SP;
sp += -12;
call _cplb_hdr;
sp += 12;
CC = R0 == 0;
IF !CC JUMP _handle_bad_cplb;
#else
call __cplb_hdr; call __cplb_hdr;
#endif
DEBUG_START_HWTRACE(p5, r7) DEBUG_START_HWTRACE(p5, r7)
RESTORE_ALL_SYS RESTORE_ALL_SYS
SP = EX_SCRATCH_REG; SP = EX_SCRATCH_REG;
......
...@@ -184,13 +184,15 @@ static __init void free_init_pages(const char *what, unsigned long begin, unsign ...@@ -184,13 +184,15 @@ static __init void free_init_pages(const char *what, unsigned long begin, unsign
#ifdef CONFIG_BLK_DEV_INITRD #ifdef CONFIG_BLK_DEV_INITRD
void __init free_initrd_mem(unsigned long start, unsigned long end) void __init free_initrd_mem(unsigned long start, unsigned long end)
{ {
#ifndef CONFIG_MPU
free_init_pages("initrd memory", start, end); free_init_pages("initrd memory", start, end);
#endif
} }
#endif #endif
void __init free_initmem(void) void __init free_initmem(void)
{ {
#ifdef CONFIG_RAMKERNEL #if defined CONFIG_RAMKERNEL && !defined CONFIG_MPU
free_init_pages("unused kernel memory", free_init_pages("unused kernel memory",
(unsigned long)(&__init_begin), (unsigned long)(&__init_begin),
(unsigned long)(&__init_end)); (unsigned long)(&__init_end));
......
/*
* File: include/asm-blackfin/cplbinit.h
* Based on:
* Author:
*
* Created:
* Description:
*
* Modified:
* Copyright 2004-2006 Analog Devices Inc.
*
* Bugs: Enter bugs at http://blackfin.uclinux.org/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see the file COPYING, or write
* to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __ASM_BFIN_CPLB_MPU_H
#define __ASM_BFIN_CPLB_MPU_H
struct cplb_entry {
unsigned long data, addr;
};
struct mem_region {
unsigned long start, end;
unsigned long dcplb_data;
unsigned long icplb_data;
};
extern struct cplb_entry dcplb_tbl[MAX_CPLBS];
extern struct cplb_entry icplb_tbl[MAX_CPLBS];
extern int first_switched_icplb;
extern int first_mask_dcplb;
extern int first_switched_dcplb;
extern int nr_dcplb_miss, nr_icplb_miss, nr_icplb_supv_miss, nr_dcplb_prot;
extern int nr_cplb_flush;
extern int page_mask_order;
extern int page_mask_nelts;
extern unsigned long *current_rwx_mask;
extern void flush_switched_cplbs(void);
extern void set_mask_dcplbs(unsigned long *);
extern void __noreturn panic_cplb_error(int seqstat, struct pt_regs *);
#endif /* __ASM_BFIN_CPLB_MPU_H */
...@@ -65,7 +65,11 @@ ...@@ -65,7 +65,11 @@
#define SIZE_1M 0x00100000 /* 1M */ #define SIZE_1M 0x00100000 /* 1M */
#define SIZE_4M 0x00400000 /* 4M */ #define SIZE_4M 0x00400000 /* 4M */
#ifdef CONFIG_MPU
#define MAX_CPLBS 16
#else
#define MAX_CPLBS (16 * 2) #define MAX_CPLBS (16 * 2)
#endif
#define ASYNC_MEMORY_CPLB_COVERAGE ((ASYNC_BANK0_SIZE + ASYNC_BANK1_SIZE + \ #define ASYNC_MEMORY_CPLB_COVERAGE ((ASYNC_BANK0_SIZE + ASYNC_BANK1_SIZE + \
ASYNC_BANK2_SIZE + ASYNC_BANK3_SIZE) / SIZE_4M) ASYNC_BANK2_SIZE + ASYNC_BANK3_SIZE) / SIZE_4M)
......
...@@ -33,6 +33,12 @@ ...@@ -33,6 +33,12 @@
#include <asm/blackfin.h> #include <asm/blackfin.h>
#include <asm/cplb.h> #include <asm/cplb.h>
#ifdef CONFIG_MPU
#include <asm/cplb-mpu.h>
#else
#define INITIAL_T 0x1 #define INITIAL_T 0x1
#define SWITCH_T 0x2 #define SWITCH_T 0x2
#define I_CPLB 0x4 #define I_CPLB 0x4
...@@ -79,6 +85,8 @@ extern u_long ipdt_swapcount_table[]; ...@@ -79,6 +85,8 @@ extern u_long ipdt_swapcount_table[];
extern u_long dpdt_swapcount_table[]; extern u_long dpdt_swapcount_table[];
#endif #endif
#endif /* CONFIG_MPU */
extern unsigned long reserved_mem_dcache_on; extern unsigned long reserved_mem_dcache_on;
extern unsigned long reserved_mem_icache_on; extern unsigned long reserved_mem_icache_on;
......
...@@ -24,7 +24,9 @@ typedef struct { ...@@ -24,7 +24,9 @@ typedef struct {
unsigned long exec_fdpic_loadmap; unsigned long exec_fdpic_loadmap;
unsigned long interp_fdpic_loadmap; unsigned long interp_fdpic_loadmap;
#endif #endif
#ifdef CONFIG_MPU
unsigned long *page_rwx_mask;
#endif
} mm_context_t; } mm_context_t;
#endif #endif
...@@ -30,9 +30,12 @@ ...@@ -30,9 +30,12 @@
#ifndef __BLACKFIN_MMU_CONTEXT_H__ #ifndef __BLACKFIN_MMU_CONTEXT_H__
#define __BLACKFIN_MMU_CONTEXT_H__ #define __BLACKFIN_MMU_CONTEXT_H__
#include <linux/gfp.h>
#include <linux/sched.h>
#include <asm/setup.h> #include <asm/setup.h>
#include <asm/page.h> #include <asm/page.h>
#include <asm/pgalloc.h> #include <asm/pgalloc.h>
#include <asm/cplbinit.h>
extern void *current_l1_stack_save; extern void *current_l1_stack_save;
extern int nr_l1stack_tasks; extern int nr_l1stack_tasks;
...@@ -50,6 +53,12 @@ static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk) ...@@ -50,6 +53,12 @@ static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk)
static inline int static inline int
init_new_context(struct task_struct *tsk, struct mm_struct *mm) init_new_context(struct task_struct *tsk, struct mm_struct *mm)
{ {
#ifdef CONFIG_MPU
unsigned long p = __get_free_pages(GFP_KERNEL, page_mask_order);
mm->context.page_rwx_mask = (unsigned long *)p;
memset(mm->context.page_rwx_mask, 0,
page_mask_nelts * 3 * sizeof(long));
#endif
return 0; return 0;
} }
...@@ -73,6 +82,11 @@ static inline void destroy_context(struct mm_struct *mm) ...@@ -73,6 +82,11 @@ static inline void destroy_context(struct mm_struct *mm)
sram_free(tmp->addr); sram_free(tmp->addr);
kfree(tmp); kfree(tmp);
} }
#ifdef CONFIG_MPU
if (current_rwx_mask == mm->context.page_rwx_mask)
current_rwx_mask = NULL;
free_pages((unsigned long)mm->context.page_rwx_mask, page_mask_order);
#endif
} }
static inline unsigned long static inline unsigned long
...@@ -106,9 +120,21 @@ activate_l1stack(struct mm_struct *mm, unsigned long sp_base) ...@@ -106,9 +120,21 @@ activate_l1stack(struct mm_struct *mm, unsigned long sp_base)
#define deactivate_mm(tsk,mm) do { } while (0) #define deactivate_mm(tsk,mm) do { } while (0)
static inline void activate_mm(struct mm_struct *prev_mm, #define activate_mm(prev, next) switch_mm(prev, next, NULL)
struct mm_struct *next_mm)
static inline void switch_mm(struct mm_struct *prev_mm, struct mm_struct *next_mm,
struct task_struct *tsk)
{ {
if (prev_mm == next_mm)
return;
#ifdef CONFIG_MPU
if (prev_mm->context.page_rwx_mask == current_rwx_mask) {
flush_switched_cplbs();
set_mask_dcplbs(next_mm->context.page_rwx_mask);
}
#endif
/* L1 stack switching. */
if (!next_mm->context.l1_stack_save) if (!next_mm->context.l1_stack_save)
return; return;
if (next_mm->context.l1_stack_save == current_l1_stack_save) 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,10 +146,36 @@ static inline void activate_mm(struct mm_struct *prev_mm,
memcpy(l1_stack_base, current_l1_stack_save, l1_stack_len); memcpy(l1_stack_base, current_l1_stack_save, l1_stack_len);
} }
static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, #ifdef CONFIG_MPU
struct task_struct *tsk) static inline void protect_page(struct mm_struct *mm, unsigned long addr,
unsigned long flags)
{
unsigned long *mask = mm->context.page_rwx_mask;
unsigned long page = addr >> 12;
unsigned long idx = page >> 5;
unsigned long bit = 1 << (page & 31);
if (flags & VM_MAYREAD)
mask[idx] |= bit;
else
mask[idx] &= ~bit;
mask += page_mask_nelts;
if (flags & VM_MAYWRITE)
mask[idx] |= bit;
else
mask[idx] &= ~bit;
mask += page_mask_nelts;
if (flags & VM_MAYEXEC)
mask[idx] |= bit;
else
mask[idx] &= ~bit;
}
static inline void update_protections(struct mm_struct *mm)
{ {
activate_mm(prev, next); flush_switched_cplbs();
set_mask_dcplbs(mm->context.page_rwx_mask);
} }
#endif
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment