123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- /*
- * guest context on VMM
- *
- * COPYRIGHT (C) 2013-2014, Shanghai Real-Thread Technology Co., Ltd
- *
- * This file is part of RT-Thread (http://www.rt-thread.org)
- *
- * All rights reserved.
- *
- * 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, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Change Logs:
- * Date Author Notes
- * 2013-11-04 Grissiom add comment
- */
- #include <rthw.h>
- #include <rtthread.h>
- #include <interrupt.h>
- #include <log_trace.h>
- #include <vmm.h>
- #include "vmm_context.h"
- struct rt_vmm_share_layout rt_vmm_share SECTION(".vmm.share");
- volatile struct vmm_context *_vmm_context = RT_NULL;
- void vmm_context_init(void *context_addr)
- {
- _vmm_context = (struct vmm_context *)context_addr;
- rt_memset((void *)_vmm_context, 0x00, sizeof(struct vmm_context));
- /* When loading RT-Thread, the IRQ on the guest should be disabled. */
- _vmm_context->virq_status = 1;
- }
- #ifdef RT_VMM_USING_DOMAIN
- unsigned long guest_domain_val SECTION(".bss.share");
- unsigned long vmm_domain_val SECTION(".bss.share");
- /* some RT-Thread code need to be called in the guest
- * context(rt_thread_idle_excute for example). To simplify the code, we need a
- * "super" domain mode to have access of both side. The code executed in super
- * domain mode is restricted and should be harmless. */
- unsigned long super_domain_val SECTION(".bss.share");
- void vmm_context_init_domain(struct vmm_domain *domain)
- {
- asm volatile ("mrc p15, 0, %0, c3, c0\n" : "=r" (guest_domain_val));
- rt_kprintf("Linux domain: kernel: %d, user: %d, io: %d\n"
- "VMM domain: vmm: %d, share: %d\n",
- domain->kernel, domain->user, domain->io,
- domain->vmm, domain->vmm_share);
- if (domain->kernel == domain->vmm ||
- domain->io == domain->vmm)
- {
- rt_kprintf("VMM and the guest share the same domain\n");
- super_domain_val = vmm_domain_val = guest_domain_val;
- return;
- }
- vmm_domain_val = guest_domain_val;
- /* become client to our own territory */
- vmm_domain_val |= (1 << (domain->vmm * 2)) | (1 << (domain->vmm_share * 2));
- super_domain_val = vmm_domain_val;
- /* super domain has access to both side */
- super_domain_val |= (1 << (domain->kernel * 2)) | (1 << (domain->user * 2));
- rt_kprintf("Original DAC: 0x%08x\n", guest_domain_val);
- }
- unsigned long vmm_context_enter_domain(unsigned long domain_val)
- {
- unsigned long old_domain;
- asm volatile ("mrc p15, 0, %0, c3, c0\n" : "=r" (old_domain));
- asm volatile ("mcr p15, 0, %0, c3, c0\n" : :"r" (domain_val) : "memory");
- return old_domain;
- }
- void vmm_context_restore_domain(unsigned long domain_val)
- {
- asm volatile ("mcr p15, 0, %0, c3, c0\n" : :"r" (domain_val) : "memory");
- }
- #endif
- void vmm_virq_pending(int irq)
- {
- /* when running this piece of code, the guest is already suspended. So it's
- * safe to set the bits without locks. */
- _vmm_context->virq_pending[irq / 32] |= (1 << (irq % 32));
- _vmm_context->virq_pended = 1;
- /* mask this IRQ in host */
- rt_hw_interrupt_mask(irq);
- }
- void vmm_virq_update(void)
- {
- if ((!_vmm_context->virq_status) &&
- ( _vmm_context->virq_pended))
- {
- rt_hw_interrupt_trigger(RT_VMM_VIRQ_TRIGGER);
- }
- }
- /** check the guest IRQ status
- *
- * @return 0 on guest should handle IRQ, -1 on should restore the guest context
- * normally.
- */
- int vmm_virq_check(void)
- {
- if ((!_vmm_context->virq_status) &&
- ( _vmm_context->virq_pended))
- {
- return 0;
- }
- return -1;
- }
- /* 10 = len("%08x, ") */
- static char _vmbuf[10*ARRAY_SIZE(_vmm_context->virq_pending)];
- void vmm_dump_virq(void)
- {
- int i, s;
- vmm_info("---- virtual IRQ ----\n");
- vmm_info(" status: %08x, pended: %08x, pending:\n",
- _vmm_context->virq_status, _vmm_context->virq_pended);
- for (s = 0, i = 0; i < ARRAY_SIZE(_vmm_context->virq_pending); i++)
- {
- s += rt_snprintf(_vmbuf+s, sizeof(_vmbuf)-s,
- "%08x, ", _vmm_context->virq_pending[i]);
- }
- vmm_info("%.*s\n", sizeof(_vmbuf), _vmbuf);
- vmm_info("---- virtual IRQ ----\n");
- }
- int vmm_virq_coherence_ok(void)
- {
- int i, res;
- int should_pend = 0;
- for (i = 0; i < ARRAY_SIZE(_vmm_context->virq_pending); i++)
- {
- should_pend |= _vmm_context->virq_pending[i];
- }
- res = (_vmm_context->virq_pended == !!should_pend);
- if (!res)
- {
- vmm_info("--- %x %x, %x\n",
- _vmm_context->virq_pended, should_pend, !!should_pend);
- }
- return res;
- }
- extern struct rt_thread vmm_thread;
- void vmm_show_guest_reg(void)
- {
- struct rt_hw_stack *sp = vmm_thread.sp;
- #ifdef RT_VMM_USING_DOMAIN
- unsigned long old_domain;
- old_domain = vmm_context_enter_domain(super_domain_val);
- #endif
- vmm_info("CPSR: %08x, PC: %08x, LR: %08x, SP: %08x\n",
- sp->cpsr, sp->pc, sp->lr, sp+1);
- #ifdef RT_VMM_USING_DOMAIN
- vmm_context_restore_domain(old_domain);
- #endif
- }
- void vmm_dump_domain(void)
- {
- unsigned long dac;
- asm volatile ("mrc p15, 0, %0, c3, c0\n" : "=r" (dac));
- vmm_info("current DAC: %08x\n", dac);
- #ifdef RT_VMM_USING_DOMAIN
- vmm_info("guest DAC: %08x, RTT DAC: %08x, super DAC: %08x\n",
- guest_domain_val, vmm_domain_val, super_domain_val);
- #endif
- }
- void vmm_show_guest(void)
- {
- vmm_show_guest_reg();
- vmm_dump_virq();
- vmm_dump_domain();
- }
- #ifdef RT_USING_FINSH
- #include <finsh.h>
- FINSH_FUNCTION_EXPORT_ALIAS(vmm_show_guest, vmm, show vmm status);
- #endif
- static int _bad_cpsr(unsigned long cpsr)
- {
- int bad = 1;
- switch (cpsr & MODEMASK)
- {
- case USERMODE:
- case FIQMODE:
- case IRQMODE:
- case SVCMODE:
- #ifdef CPU_HAS_MONITOR_MODE
- case MONITORMODE:
- #endif
- case ABORTMODE:
- #ifdef CPU_HAS_HYP_MODE
- case HYPMODE:
- #endif
- case UNDEFMODE:
- case MODEMASK:
- bad = 0;
- break;
- };
- return bad;
- }
- void vmm_verify_guest_status(struct rt_hw_stack *sp)
- {
- int dump_vmm = 0;
- unsigned long cpsr;
- #ifdef RT_VMM_USING_DOMAIN
- unsigned long old_domain;
- old_domain = vmm_context_enter_domain(super_domain_val);
- #endif
- cpsr = sp->cpsr;
- if (_bad_cpsr(cpsr))
- {
- vmm_info("=================================\n");
- vmm_info("VMM WARING: bad CPSR in guest\n");
- dump_vmm = 1;
- }
- else
- {
- if (cpsr & A_Bit && 0)
- {
- vmm_info("=================================\n");
- vmm_info("VMM WARING: A bit is set in guest\n");
- dump_vmm = 1;
- }
- if ((cpsr & I_Bit) && (sp->pc <= VMM_BEGIN))
- {
- vmm_info("=================================\n");
- vmm_info("VMM WARING: IRQ disabled in guest\n");
- dump_vmm = 1;
- }
- if (cpsr & F_Bit)
- {
- vmm_info("=================================\n");
- vmm_info("VMM WARING: FIQ disabled in guest\n");
- dump_vmm = 1;
- }
- if ((cpsr & MODEMASK) == USERMODE)
- {
- if (_vmm_context->virq_status & 1)
- {
- vmm_info("=================================\n");
- vmm_info("VMM WARING: VIRQ disabled in user mode\n");
- dump_vmm = 1;
- }
- if ((sp->pc > 0xbf000000) && (sp->pc < 0xffff0000))
- {
- vmm_info("=================================\n");
- vmm_info("VMM WARING: executing kernel code in usr mode\n");
- dump_vmm = 1;
- }
- /* FIXME: when the guest is suspended in user mode and its
- * interrupts come, this can be misleading. */
- #if 0
- if (_vmm_context->virq_pended)
- {
- vmm_info("=================================\n");
- vmm_info("VMM WARING: VIRQ pended in user mode\n");
- dump_vmm = 1;
- }
- #endif
- }
- else if ((cpsr & MODEMASK) == SVCMODE && sp->pc < 0xbf000000)
- {
- vmm_info("=================================\n");
- vmm_info("VMM WARING: executing usr code in svc mode\n");
- dump_vmm = 1;
- }
- }
- #if 0
- if (!vmm_virq_coherence_ok())
- {
- vmm_info("=================================\n");
- vmm_info("VMM WARING: bad VIRQ status\n");
- dump_vmm = 1;
- }
- #endif
- if (dump_vmm)
- {
- vmm_show_guest();
- vmm_info("=================================\n");
- }
- #ifdef RT_VMM_USING_DOMAIN
- vmm_context_restore_domain(old_domain);
- #endif
- }
|