@@ -1,160 +1,78 @@
#include <hal/mmu.h>

#define PAGE_SIZE (4096)

#define MAX_MMU_SLOTS 10
#define NO_EMPTY_SLOT (MAX_MMU_SLOTS + 1)

#define A_BAD_ADDR (0)

#define NULL (0)

#define get_pfn(x) (x & ~(PAGE_SIZE - 1))

struct mmu_mapping {

unsigned int vaddr;
unsigned int paddr;
char valid;

} mappings[10];

/*
* This records in a global structure all MMU mappings
* If such a mapping already exists the function returns immediately.
* If such a mapping does not exist yet, vaddr is mapped to paddr and
* the mapping is recorded in the mappings[] global structure array in
* an empty slot.
* If there is no empty slot anymore then we fail
* Milkymist SoC (Software)
* Copyright (C) 2012 Yann Sionneau <yann.sionneau@gmail.com>
*
* 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, version 3 of the License.
*
* 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 <http://www.gnu.org/licenses/>.
*/

unsigned int mmu_map(unsigned int vaddr, unsigned int paddr) {
int i;
int empty_slot = NO_EMPTY_SLOT;
vaddr = get_pfn(vaddr);

for (i = 0 ; i < MAX_MMU_SLOTS ; ++i)
{
if (!mappings[i].valid)
empty_slot = i;
if (vaddr == mappings[i].vaddr && paddr == mappings[i].paddr)
return 1;
}

if (empty_slot == NO_EMPTY_SLOT)
return empty_slot;

mappings[empty_slot].vaddr = vaddr;
mappings[empty_slot].paddr = paddr;
mappings[empty_slot].valid = 1;
mmu_dtlb_map(vaddr, paddr);

return 1;
}
#include <hal/mmu.h>
#include <base/mmu.h>
#include <base/stdio.h>

unsigned int get_mmu_mapping_for(unsigned int vaddr) {
int i;
vaddr = get_pfn(vaddr);
void dtlb_exception_handling_tests() {

for (i = 0 ; i < MAX_MMU_SLOTS ; ++i)
if (mappings[i].valid && vaddr == mappings[i].vaddr)
return mappings[i].paddr;
register unsigned int stack, addr;
unsigned int data;
int ret;

return A_BAD_ADDR;
}
asm volatile("mv %0, sp" : "=r"(stack) :: );

unsigned int invalidate_mmu_mapping(unsigned int vaddr) {
int i;
vaddr = get_pfn(vaddr);
for (i = 0 ; i < MAX_MMU_SLOTS ; ++i)
{
if (mappings[i].valid && vaddr == mappings[i].vaddr) {
mmu_dtlb_invalidate(vaddr);
mappings[i].valid = 0;
return 1;
}
}
return 0;
}
ret = mmu_map(stack, stack);
check_for_error(ret);

static void panic(void) {
puts("PANIC !");
while(1)
asm volatile("nop");
}
ret = mmu_map(stack-0x1000, stack-0x1000);
check_for_error(ret);

static void check_for_error(int ret) {
if (ret)
return;
printf("stack == 0x%08X\n", stack);

if (ret == NO_EMPTY_SLOT) {
puts("No empty slot in MMU mappings structure anymore");
panic();
}
addr = 0x44004004;

if ( !ret ) {
puts("Unknown issue");
panic();
}
}
printf("\n=> Mapping 0x%08X to 0x%08X\n", addr, addr);
ret = mmu_map(addr, addr);
check_for_error(ret);

void dtlb_exception_handling_tests() {
data = 42;
printf("=> Writing %d to physical address 0x%08X\n", data, addr);
*(unsigned int *)addr = data;

register unsigned int stack, addr, data;
int ret;
printf("=> Activating the MMU and reading form virtual address 0x%08X\n", addr);
data = read_word_with_mmu_enabled(addr);
printf("\n<= Reading %d from virtual address 0x%08X\n\n", data, addr);

asm volatile("mv %0, sp" : "=r"(stack) :: );
printf("=> Invalidating the mapping of virtual address 0x%08X in the TLB\n", addr);
mmu_dtlb_invalidate(addr);

ret = mmu_map(stack, stack);
check_for_error(ret);
data = 43;
printf("=> Writing %d to physical address 0x%08X\n", data, addr);
*(unsigned int *)addr = data;

ret = mmu_map(stack-0x1000, stack-0x1000);
printf("=> Activating the MMU and reading form virtual address 0x%08X\n", addr);
data = read_word_with_mmu_enabled(addr);
printf("\n<= Reading %d from virtual address 0x%08X\n\n", data, addr);

printf("=> Mapping 0x%08X to 0%08X\n", addr, addr+0x1000);
ret = mmu_map(addr, addr+0x1000); // Map to something else
check_for_error(ret);

printf("stack == 0x%08X\n", stack);
printf("=> Invalidating the mapping of virtual address 0x%08X in the TLB\n", addr);
mmu_dtlb_invalidate(addr); // AND invalidate the mapping

data = 44;
printf("=> Writting %d to physical address 0x%08X\n", data, addr+0x1000);
*(unsigned int *)(addr + 0x1000) = data;

addr = 0x44002342; // Random address
*(unsigned int *)addr = 42;
// mmu_map(addr, addr);

printf("Address 0x%08X mapped to itself, value : ", addr);

asm volatile(
"xor r11, r11, r11\n\t"
"ori r11, r11, 0x11\n\t"
"wcsr tlbctrl, r11\n\t" // this activates the mmu
"xor r0, r0, r0\n\t"
"xor r11, r11, r11\n\t"
"or r11, r11, %1\n\t"
"lw %0, (r11+0)\n\t"
"xor r11, r11, r11\n\t"
"ori r11, r11, 0x9\n\t"
"wcsr tlbctrl, r11\n\t" // this disactivates the mmu
"xor r0, r0, r0" : "=&r"(data) : "r"(addr) : "r11"
);

printf("%d\n", data);

invalidate_mmu_mapping(addr);

printf("DTLB has just been invalidated, next access to 0x%08X should trigger a DTLB exception\n", addr);

printf("Address 0x%08X not mapped, value : ", addr);

asm volatile(
"xor r11, r11, r11\n\t"
"ori r11, r11, 0x11\n\t"
"wcsr tlbctrl, r11\n\t" // this activates the mmu
"xor r0, r0, r0\n\t"
"xor r11, r11, r11\n\t"
"or r11, r11, %1\n\t"
"lw %0, (r11+0)\n\t"
"xor r11, r11, r11\n\t"
"ori r11, r11, 0x9\n\t"
"wcsr tlbctrl, r11\n\t" // this disactivates the mmu
"xor r0, r0, r0" : "=&r"(data) : "r"(addr) : "r11"
);

printf("%d\n", data);
printf("=> Activating the MMU and reading form virtual address 0x%08X\n", addr);
data = read_word_with_mmu_enabled(addr);
printf("\n<= Reading %d from virtual address 0x%08X\n\n", data, addr);

}
@@ -1,8 +1,43 @@
/*
* Milkymist SoC (Software)
* Copyright (C) 2012 Yann Sionneau <yann.sionneau@gmail.com>
*
* 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, version 3 of the License.
*
* 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 <http://www.gnu.org/licenses/>.
*/

#include <hal/mmu.h>
#include <base/mmu.h>

void dtlb_miss_handler(void)
{
unsigned int vaddr, paddr;

// retrieve virtual address which caused the page fault
asm volatile("rcsr %0, dtlbma" : "=r"(vaddr) :: );

void dtlb_miss_handler(void) {
/*
* check if there is an existing mapping for that virtual address
* if yes: refill the DTLB with it
* if not: we panic() !
*/
paddr = get_mmu_mapping_for(vaddr);
if (paddr == A_BAD_ADDR)
{
puts("[TLB miss handler] Unrecoverable page fault !");
panic();
}

disable_dtlb();
printf("TOTO");
printf("[TLB miss handler] Refilling DTLB with mapping 0x%08X->0x%08X\n", vaddr, paddr);
mmu_dtlb_map(vaddr, paddr);

}
@@ -0,0 +1,29 @@
/*
* Milkymist SoC (Software)
* Copyright (C) 2012 Yann Sionneau <yann.sionneau@gmail.com>
*
* 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, version 3 of the License.
*
* 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 <http://www.gnu.org/licenses/>.
*/

#ifndef __BASE_MMU_H__
#define __BASE_MMU_H__

#include <hal/mmu.h>

unsigned int mmu_map(unsigned int vaddr, unsigned int paddr);
unsigned int get_mmu_mapping_for(unsigned int vaddr);
unsigned char remove_mmu_mapping_for(unsigned int vaddr);
void panic(void);
void check_for_error(int ret);

#endif
@@ -15,6 +15,22 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef __MMU_H__
#define __MMU_H__

#define PAGE_SIZE (4096)
#define MAX_MMU_SLOTS 10
#define NO_EMPTY_SLOT (MAX_MMU_SLOTS + 1)
#define A_BAD_ADDR 0xffffffff
#define get_pfn(x) (x & ~(PAGE_SIZE - 1))

struct mmu_mapping {

unsigned int vaddr;
unsigned int paddr;
char valid;

};

#define enable_dtlb() do { \
asm volatile ("xor r11, r11, r11\n\t" \
@@ -33,3 +49,7 @@
} while(0);

void mmu_dtlb_map(unsigned int vpfn, unsigned int pfn);
unsigned int read_word_with_mmu_enabled(unsigned int vaddr);
void mmu_dtlb_invalidate(unsigned int vaddr);

#endif
@@ -1,7 +1,7 @@
MMDIR=../..
include $(MMDIR)/software/include.mak

OBJECTS_ALL=divsi3.o libc.o crc16.o crc32.o console.o blockdev.o fatfs.o system.o board.o uart.o
OBJECTS_ALL=divsi3.o libc.o crc16.o crc32.o console.o blockdev.o fatfs.o system.o board.o uart.o mmu.o
OBJECTS=$(OBJECTS_ALL) softfloat.o softfloat-glue.o vsnprintf.o atof.o malloc.o
OBJECTS_LIGHT=$(OBJECTS_ALL) vsnprintf-nofloat.o

@@ -0,0 +1,116 @@
/*
* Milkymist SoC (Software)
* Copyright (C) 2012 Yann Sionneau <yann.sionneau@gmail.com>
*
* 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, version 3 of the License.
*
* 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 <http://www.gnu.org/licenses/>.
*/

/* @vpfn : virtual page frame number
* @pfn : physical page frame number
*/

#include <hal/mmu.h>
#include <base/mmu.h>

struct mmu_mapping mappings[MAX_MMU_SLOTS];

/*
* This records in a global structure all MMU mappings
* If such a mapping already exists the function returns immediately.
* If such a mapping does not exist yet, vaddr is mapped to paddr and
* the mapping is recorded in the mappings[] global structure array in
* an empty slot.
* If there is no empty slot anymore then we fail
*/

unsigned int mmu_map(unsigned int vaddr, unsigned int paddr) {
int i;
int empty_slot = NO_EMPTY_SLOT;
vaddr = get_pfn(vaddr);
paddr = get_pfn(paddr);

for (i = 0 ; i < MAX_MMU_SLOTS ; ++i)
{
if (!mappings[i].valid)
empty_slot = i;
if ((vaddr == mappings[i].vaddr) && (paddr == mappings[i].paddr) && mappings[i].valid)
{
puts("Already mapped !");
return 1;
}
}

if (empty_slot == NO_EMPTY_SLOT)
{
puts("No more slots !");
return empty_slot;
}

mappings[empty_slot].vaddr = vaddr;
mappings[empty_slot].paddr = paddr;
mappings[empty_slot].valid = 1;
mmu_dtlb_map(vaddr, paddr);
printf("mapping 0x%08X->0x%08X in slot %d [0x%p]\n", vaddr, paddr, empty_slot, &mappings[empty_slot]);

return 1;
}

unsigned int get_mmu_mapping_for(unsigned int vaddr) {
int i;
vaddr = get_pfn(vaddr);

for (i = 0 ; i < MAX_MMU_SLOTS ; ++i)
if (mappings[i].valid && (vaddr == mappings[i].vaddr))
return mappings[i].paddr;

return A_BAD_ADDR;
}

unsigned char remove_mmu_mapping_for(unsigned int vaddr) {
int i;
vaddr = get_pfn(vaddr);

for (i = 0 ; i < MAX_MMU_SLOTS ; ++i)
{
if (mappings[i].valid && (vaddr == mappings[i].vaddr))
{
mmu_dtlb_invalidate(vaddr);
mappings[i].valid = 0;
return 1;
}
}
return 0;
}

void panic(void) {
puts("PANIC !");
while(1)
asm volatile("nop");
}

void check_for_error(int ret) {
if (ret == 1)
return;

if (ret == NO_EMPTY_SLOT)
{
puts("No empty slot in MMU mappings structure anymore");
panic();
}

if ( !ret )
{
puts("Unknown issue");
panic();
}
}
@@ -15,6 +15,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <hal/mmu.h>
#include <base/mmu.h>

/* @vpfn : virtual page frame number
* @pfn : physical page frame number
*/
@@ -42,3 +45,30 @@ inline void mmu_dtlb_invalidate(unsigned int vaddr)
"ori r11, r11, 0x21\n\t"
"wcsr tlbctrl, r11":::"r11");
}

/* This function activates the MMU
* then reads from virtual address "vaddr"
* and store the result in temporary variable "data".
* Then MMU is disactivated and the content of "data"
* is returned.
*/

unsigned int read_word_with_mmu_enabled(unsigned int vaddr)
{
register unsigned int data;
asm volatile(
"xor r11, r11, r11\n\t"
"ori r11, r11, 0x11\n\t"
"wcsr tlbctrl, r11\n\t" // Activates the MMU
"xor r0, r0, r0\n\t"
"xor r11, r11, r11\n\t"
"or r11, r11, %1\n\t"
"lw %0, (r11+0)\n\t" // Reads from virtual address "addr"
"xor r11, r11, r11\n\t"
"ori r11, r11, 0x9\n\t"
"wcsr tlbctrl, r11\n\t" // Disactivates the MMU
"xor r0, r0, r0\n\t" : "=&r"(data) : "r"(vaddr) : "r11"
);

return data;
}