[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>
This commit is contained in:
parent
2047e40d72
commit
b97b8a9983
15 changed files with 822 additions and 8 deletions
|
@ -765,6 +765,15 @@ config L1_MAX_PIECE
|
|||
Set the max memory pieces for the L1 SRAM allocation algorithm.
|
||||
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"
|
||||
|
||||
menu "EBIU_AMGCTL Global Control"
|
||||
|
|
|
@ -82,7 +82,11 @@ core-y += arch/$(ARCH)/mach-$(MACHINE)/
|
|||
core-y += arch/$(ARCH)/mach-$(MACHINE)/boards/
|
||||
endif
|
||||
|
||||
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/
|
||||
|
||||
|
|
8
arch/blackfin/kernel/cplb-mpu/Makefile
Normal file
8
arch/blackfin/kernel/cplb-mpu/Makefile
Normal file
|
@ -0,0 +1,8 @@
|
|||
#
|
||||
# arch/blackfin/kernel/cplb-nompu/Makefile
|
||||
#
|
||||
|
||||
obj-y := cplbinit.o cacheinit.o cplbmgr.o
|
||||
|
||||
obj-$(CONFIG_CPLB_INFO) += cplbinfo.o
|
||||
|
62
arch/blackfin/kernel/cplb-mpu/cacheinit.c
Normal file
62
arch/blackfin/kernel/cplb-mpu/cacheinit.c
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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
|
144
arch/blackfin/kernel/cplb-mpu/cplbinfo.c
Normal file
144
arch/blackfin/kernel/cplb-mpu/cplbinfo.c
Normal file
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* 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);
|
91
arch/blackfin/kernel/cplb-mpu/cplbinit.c
Normal file
91
arch/blackfin/kernel/cplb-mpu/cplbinit.c
Normal file
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
338
arch/blackfin/kernel/cplb-mpu/cplbmgr.c
Normal file
338
arch/blackfin/kernel/cplb-mpu/cplbmgr.c
Normal file
|
@ -0,0 +1,338 @@
|
|||
/*
|
||||
* 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)
|
|||
memory_end = _ramend - DMA_UNCACHED_REGION;
|
||||
|
||||
_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);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_MTD_UCLINUX)
|
||||
/* generic memory mapped MTD driver */
|
||||
|
@ -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);
|
||||
#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)
|
||||
memory_end -= SIZE_4K; /*In case there is no valid CPLB behind memory_end make sure we don't get to close*/
|
||||
#endif
|
||||
|
|
|
@ -95,24 +95,43 @@ ENTRY(_ex_workaround_261)
|
|||
R6 = 0x26; /* Data CPLB Miss */
|
||||
cc = R6 == R7;
|
||||
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
|
||||
* and Data CPLB Multiple Hits - Linux Trap Zero
|
||||
*/
|
||||
jump _ex_trap_c;
|
||||
ENDPROC(_ex_workaround_261)
|
||||
|
||||
#else
|
||||
#ifdef CONFIG_MPU
|
||||
#define _ex_dviol _ex_dcplb_viol
|
||||
#else
|
||||
#define _ex_dviol _ex_trap_c
|
||||
#endif
|
||||
#define _ex_dmiss _ex_dcplb_miss
|
||||
#define _ex_dmult _ex_trap_c
|
||||
#endif
|
||||
|
||||
|
||||
ENTRY(_ex_dcplb_viol)
|
||||
ENTRY(_ex_dcplb_miss)
|
||||
ENTRY(_ex_icplb_miss)
|
||||
(R7:6,P5:4) = [sp++];
|
||||
ASTAT = [sp++];
|
||||
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;
|
||||
#endif
|
||||
DEBUG_START_HWTRACE(p5, r7)
|
||||
RESTORE_ALL_SYS
|
||||
SP = EX_SCRATCH_REG;
|
||||
|
|
|
@ -184,13 +184,15 @@ static __init void free_init_pages(const char *what, unsigned long begin, unsign
|
|||
#ifdef CONFIG_BLK_DEV_INITRD
|
||||
void __init free_initrd_mem(unsigned long start, unsigned long end)
|
||||
{
|
||||
#ifndef CONFIG_MPU
|
||||
free_init_pages("initrd memory", start, end);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
void __init free_initmem(void)
|
||||
{
|
||||
#ifdef CONFIG_RAMKERNEL
|
||||
#if defined CONFIG_RAMKERNEL && !defined CONFIG_MPU
|
||||
free_init_pages("unused kernel memory",
|
||||
(unsigned long)(&__init_begin),
|
||||
(unsigned long)(&__init_end));
|
||||
|
|
61
include/asm-blackfin/cplb-mpu.h
Normal file
61
include/asm-blackfin/cplb-mpu.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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 @@
|
|||
#define SIZE_1M 0x00100000 /* 1M */
|
||||
#define SIZE_4M 0x00400000 /* 4M */
|
||||
|
||||
#ifdef CONFIG_MPU
|
||||
#define MAX_CPLBS 16
|
||||
#else
|
||||
#define MAX_CPLBS (16 * 2)
|
||||
#endif
|
||||
|
||||
#define ASYNC_MEMORY_CPLB_COVERAGE ((ASYNC_BANK0_SIZE + ASYNC_BANK1_SIZE + \
|
||||
ASYNC_BANK2_SIZE + ASYNC_BANK3_SIZE) / SIZE_4M)
|
||||
|
|
|
@ -33,6 +33,12 @@
|
|||
#include <asm/blackfin.h>
|
||||
#include <asm/cplb.h>
|
||||
|
||||
#ifdef CONFIG_MPU
|
||||
|
||||
#include <asm/cplb-mpu.h>
|
||||
|
||||
#else
|
||||
|
||||
#define INITIAL_T 0x1
|
||||
#define SWITCH_T 0x2
|
||||
#define I_CPLB 0x4
|
||||
|
@ -79,6 +85,8 @@ extern u_long ipdt_swapcount_table[];
|
|||
extern u_long dpdt_swapcount_table[];
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_MPU */
|
||||
|
||||
extern unsigned long reserved_mem_dcache_on;
|
||||
extern unsigned long reserved_mem_icache_on;
|
||||
|
||||
|
|
|
@ -24,7 +24,9 @@ typedef struct {
|
|||
unsigned long exec_fdpic_loadmap;
|
||||
unsigned long interp_fdpic_loadmap;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MPU
|
||||
unsigned long *page_rwx_mask;
|
||||
#endif
|
||||
} mm_context_t;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -30,9 +30,12 @@
|
|||
#ifndef __BLACKFIN_MMU_CONTEXT_H__
|
||||
#define __BLACKFIN_MMU_CONTEXT_H__
|
||||
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/sched.h>
|
||||
#include <asm/setup.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/pgalloc.h>
|
||||
#include <asm/cplbinit.h>
|
||||
|
||||
extern void *current_l1_stack_save;
|
||||
extern int nr_l1stack_tasks;
|
||||
|
@ -50,6 +53,12 @@ static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk)
|
|||
static inline int
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -73,6 +82,11 @@ static inline void destroy_context(struct mm_struct *mm)
|
|||
sram_free(tmp->addr);
|
||||
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
|
||||
|
@ -106,9 +120,21 @@ activate_l1stack(struct mm_struct *mm, unsigned long sp_base)
|
|||
|
||||
#define deactivate_mm(tsk,mm) do { } while (0)
|
||||
|
||||
static inline void activate_mm(struct mm_struct *prev_mm,
|
||||
struct mm_struct *next_mm)
|
||||
#define activate_mm(prev, next) switch_mm(prev, next, NULL)
|
||||
|
||||
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)
|
||||
return;
|
||||
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,
|
|||
memcpy(l1_stack_base, current_l1_stack_save, l1_stack_len);
|
||||
}
|
||||
|
||||
static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next,
|
||||
struct task_struct *tsk)
|
||||
#ifdef CONFIG_MPU
|
||||
static inline void protect_page(struct mm_struct *mm, unsigned long addr,
|
||||
unsigned long flags)
|
||||
{
|
||||
activate_mm(prev, next);
|
||||
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)
|
||||
{
|
||||
flush_switched_cplbs();
|
||||
set_mask_dcplbs(mm->context.page_rwx_mask);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue