258 lines
5.7 KiB
C
258 lines
5.7 KiB
C
#include <linux/module.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/io.h>
|
|
#include <linux/memblock.h>
|
|
|
|
#include <asm/cacheflush.h>
|
|
#include <mach/hardware.h>
|
|
|
|
#include "vmm_linux.h"
|
|
|
|
#define IOMAP_NUM 3
|
|
#define BUFF_SZ (4 * 1024)
|
|
|
|
struct vmm_iomap *_linux_iomap = NULL;
|
|
|
|
const char *uart_name = "uart";
|
|
|
|
/* some exported Linux Kernel patch */
|
|
extern void vmm_set_status(int status);
|
|
extern void vmm_context_init(void *context_addr);
|
|
extern unsigned long vmm_save_irq(void);
|
|
extern void vmm_restore_irq(unsigned long flags);
|
|
|
|
static struct vmm_domain domain =
|
|
{
|
|
.kernel = DOMAIN_KERNEL,
|
|
.user = DOMAIN_USER,
|
|
.io = DOMAIN_IO,
|
|
.vmm = DOMAIN_RTVMM,
|
|
.vmm_share = DOMAIN_RTVMM_SHR,
|
|
};
|
|
|
|
static struct vmm_iomap iomap[RT_VMM_IOMAP_MAXNR] =
|
|
{
|
|
{.name = "UART1", .pa = 0x1000A000, .size = 4096},
|
|
{.name = "TIMER", .pa = 0x10012000, .size = 4096},
|
|
{.name = "GIC_CPU", .pa = 0x1E000000, .size = 4096},
|
|
{.name = "GIC_DIST", .pa = 0x1E001000, .size = 4096},
|
|
{.name = "SYS_CTRL", .pa = 0x1001A000, .size = 4096},
|
|
{.pa = 0},
|
|
};
|
|
|
|
void vmm_iomap_init(void)
|
|
{
|
|
int index;
|
|
|
|
_linux_iomap = &iomap[0];
|
|
|
|
BUILD_BUG_ON(ARRAY_SIZE(iomap) > RT_VMM_IOMAP_MAXNR);
|
|
|
|
for (index = 0; index < ARRAY_SIZE(iomap); index++) {
|
|
if (_linux_iomap[index].pa == 0)
|
|
break;
|
|
|
|
if (_linux_iomap[index].size != 0)
|
|
_linux_iomap[index].va =
|
|
ioremap_nocache(_linux_iomap[index].pa,
|
|
_linux_iomap[index].size);
|
|
|
|
printk("%s: 0x%08lx --> 0x%p, size %u\n",
|
|
_linux_iomap[index].name,
|
|
_linux_iomap[index].pa,
|
|
_linux_iomap[index].va,
|
|
_linux_iomap[index].size);
|
|
}
|
|
|
|
printk("vmm: init iomap done!\n");
|
|
}
|
|
|
|
#if 0
|
|
void trap_set_vector(unsigned long start, unsigned int length)
|
|
{
|
|
int sctrl;
|
|
|
|
/* C12-C0 is only active when SCTLR.V = 0 */
|
|
asm volatile ("mrc p15, #0, %0, c1, c0, #0"
|
|
:"=r" (sctrl));
|
|
sctrl &= ~(1 << 13);
|
|
asm volatile ("mcr p15, #0, %0, c1, c0, #0"
|
|
:
|
|
:"r" (sctrl));
|
|
|
|
asm volatile ("mcr p15, #0, %0, c12, c0, #0"
|
|
:
|
|
:"r" (start));
|
|
rmb();
|
|
}
|
|
#else
|
|
extern void trap_set_vector(unsigned long start, unsigned int length);
|
|
#endif
|
|
|
|
static void vmm_open_domain(void)
|
|
{
|
|
unsigned long dval;
|
|
asm volatile ("mrc p15, 0, %0, c3, c0\n"
|
|
: "=r" (dval));
|
|
dval |= (0x1 << (DOMAIN_RTVMM * 2)) |
|
|
(0x1 << (DOMAIN_RTVMM_SHR * 2));
|
|
asm volatile ("mcr p15, 0, %0, c3, c0\n"
|
|
: : "r" (dval));
|
|
}
|
|
|
|
static void vmm_close_domain(void)
|
|
{
|
|
unsigned long dval;
|
|
asm volatile ("mrc p15, 0, %0, c3, c0\n"
|
|
: "=r" (dval));
|
|
/* we still need access tp DOMAIN_RTVMM_SHR because the IRQ stack is
|
|
* there. */
|
|
dval &= ~(0x3 << (DOMAIN_RTVMM * 2));
|
|
asm volatile ("mcr p15, 0, %0, c3, c0\n"
|
|
: : "r" (dval));
|
|
}
|
|
|
|
static DEFINE_SPINLOCK(init_lock);
|
|
void vmm_entry(void)
|
|
{
|
|
vmm_entry_t entry;
|
|
unsigned long flags;
|
|
struct vmm_entry_param eparam = {
|
|
.iomap = &iomap[0],
|
|
.domain = &domain,
|
|
};
|
|
|
|
printk("Entry VMM:0x%08x with iomap 0x%p\n", VMM_BEGIN, _linux_iomap);
|
|
|
|
spin_lock_irqsave(&init_lock, flags);
|
|
|
|
memcpy((void*)(LINUX_VECTOR_POS), (void*)0xFFFF0000,
|
|
LINUX_VECTOR_PGSZ);
|
|
flush_icache_range(LINUX_VECTOR_POS,
|
|
LINUX_VECTOR_POS + LINUX_VECTOR_PGSZ);
|
|
|
|
/*dump_vector(0xFFFF0000);*/
|
|
/* set the interrupt vector to RTT */
|
|
trap_set_vector(VMM_BEGIN, 16 * 4);
|
|
/*dump_vector(VMM_END-LINUX_VECTOR_PGSZ);*/
|
|
|
|
entry = (vmm_entry_t)VMM_BEGIN;
|
|
|
|
vmm_context_init(&RT_VMM_SHARE->ctx);
|
|
vmm_set_status(0x01);
|
|
|
|
pr_info("Linux domain: kernel: %d, user: %d, io: %d\n",
|
|
DOMAIN_KERNEL, DOMAIN_USER, DOMAIN_IO);
|
|
|
|
/* switch to RTT and Good Luck */
|
|
entry(&eparam);
|
|
|
|
spin_unlock_irqrestore(&init_lock, flags);
|
|
|
|
/* we now switched to virtual IRQ but the hardware IRQ is disabled
|
|
* before entering RT-Thread. So we have to enabled it by hand. */
|
|
{
|
|
asm volatile ("cpsie i":::"memory", "cc");
|
|
}
|
|
|
|
printk("come back to Linux.\n");
|
|
|
|
}
|
|
|
|
int vmm_load_fw(const char* filename)
|
|
{
|
|
mm_segment_t oldfs = {0};
|
|
unsigned long len;
|
|
unsigned long file_sz;
|
|
loff_t pos = 0;
|
|
struct file *flp = NULL;
|
|
char *buf_ptr = (char*)VMM_BEGIN;
|
|
|
|
printk("loading RT-Thread:%s ....", filename);
|
|
/* FIXME: we should not need this actually. But currently Linux would
|
|
* hang without this. Let's just proceed and I will go back to handle
|
|
* this in the future. */
|
|
memset((void*)VMM_BEGIN, 0, VMM_SIZE);
|
|
|
|
flp = filp_open(filename, O_RDONLY, S_IRWXU);
|
|
if (IS_ERR(flp))
|
|
{
|
|
printk("vmm loader: open file failed. "
|
|
"Return 0x%p\n", flp);
|
|
return -1;
|
|
}
|
|
|
|
/* get file size */
|
|
file_sz = vfs_llseek(flp, 0, SEEK_END);
|
|
vfs_llseek(flp, 0, SEEK_SET);
|
|
|
|
oldfs = get_fs();
|
|
set_fs(get_ds());
|
|
while (file_sz > 0)
|
|
{
|
|
// len = vfs_read(flp, (void __user __force *)buff, BUFF_SZ, &pos);
|
|
len = vfs_read(flp, (void __user __force*)buf_ptr, BUFF_SZ, &pos);
|
|
file_sz -= len;
|
|
buf_ptr += len;
|
|
}
|
|
set_fs(oldfs);
|
|
|
|
filp_close(flp, NULL);
|
|
|
|
printk("done!\n");
|
|
|
|
/* flush RT-Thread memory */
|
|
flush_cache_vmap(VMM_BEGIN, VMM_END);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __init vmm_init(void)
|
|
{
|
|
printk("VMM started.\n");
|
|
|
|
vmm_iomap_init();
|
|
/* Open the domain permission so we could write firmware to it */
|
|
vmm_open_domain();
|
|
if (vmm_load_fw("/vmm/rtthread.bin") == 0)
|
|
vmm_entry();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void __exit vmm_exit(void)
|
|
{
|
|
int i;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&init_lock, flags);
|
|
vmm_set_status(0x00);
|
|
trap_set_vector(LINUX_VECTOR_POS, 16 * 4);
|
|
spin_unlock_irqrestore(&init_lock, flags);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(iomap); i++)
|
|
{
|
|
if (iomap[i].pa == 0)
|
|
break;
|
|
|
|
printk("iounmap %s(0x%p)\n",
|
|
iomap[i].name,
|
|
iomap[i].va);
|
|
iounmap(iomap[i].va);
|
|
}
|
|
|
|
vmm_close_domain();
|
|
|
|
printk("vmm exit\n");
|
|
}
|
|
|
|
module_init(vmm_init);
|
|
module_exit(vmm_exit);
|
|
|
|
MODULE_AUTHOR("bernard.xiong <bernard.xiong@gmail.com>");
|
|
MODULE_DESCRIPTION("RT-VMM");
|
|
MODULE_LICENSE("GPL");
|