AEMB is a family of highly-rated open-source embedded microprocessor core. All the programs for AEMB have to run in a simulation environment, because the core has not yet been implemented in silicone chip. Recently, one of the interns is assigned with the porting of AEMB for QEMU. With the QEMU ported for AEMB, it serves as a platform for application to run the embedded microprocessor.
QEMU is a processor emulator that relies on dynamic binary translation to achieve a reasonable speed. The core of QEMU is Tiny Code Generator (TCG). It is a code generator which translates code fragments (basic blocks) from target code to a code representation which can be run on a host. In our case where x86 platform is used for the emulation, the code generator is responsible for translating Microblaze’s code to code representation which can be run on x86 architecture.
Since linux kernel is to be executed in QEMU, it is therefore crucial to verify the correctness of AEMB behavior in QEMU. A test program is compiled with the aim to investigate the behavior in AEMB ported QEMU.
#include <stdio.h>
#include <stdlib.h>
#include "aemb/core.hh"
#include "simboard.hh"
#define aemb_write_msr(flags)
do {
asm volatile ("# aemb_write_msr nt"
"mts rmsr, %0 nt"
"nop nt"
:
: "r"(flags)
: "memory");
} while(0)
#define aemb_read_msr(flags)
do {
asm volatile ("# aemb_read_msr nt"
"mfs %0, rmsr nt"
"nop nt"
: "=r"(flags)
:
: "memory");
} while (0)
#define aemb_set_carry()
do {
asm volatile ("# aemb_set_carry nt"
"msrset r0, %0 nt"
"nop nt"
:
: "i"(AEMB_MSR_C)
: "memory");
} while(0)
#define aemb_clear_carry()
do {
asm volatile ("# aemb_clear_carry nt"
"msrclr r0, %0 nt"
"nop nt"
:
: "i"(AEMB_MSR_C)
: "memory");
} while(0)
#define aemb_enable_int()
do {
asm volatile ("# aemb_enable_int nt"
"msrset r0, %0 nt"
"nop nt"
:
: "i"(AEMB_MSR_IE)
: "memory");
} while(0)
#define aemb_disable_int()
do {
asm volatile ("#aemb_disable_int nt"
"msrclr r0, %0 nt"
"nop nt"
:
: "i"(AEMB_MSR_IE)
: "memory");
} while(0)
void __attribute__ ((interrupt_handler)) interruptHandler()
{
int *toggle = (int *)0xFFFFFFE0;
aemb_clear_carry();
*toggle = -1;
}
int main()
{
int beforeInt, afterInt;
aemb_set_carry();
aemb_read_msr(beforeInt);
aemb_enable_int();
for (int timer=0; timer < 300; ++timer)
asm volatile ("nop");
aemb_disable_int();
aemb_read_msr(afterInt);
iprintf("MSR before interruptt: %xnMSR after interruptt: %xn", beforeInt, afterInt);
return 0;
}
aemb/core.hh and simboard.hh are AEMB library. They are mainly used for thread locking and defining stdio addresses. MSR and interrupt operations are defined by preprocessor macro. The program is started by setting carry bit, followed reading and storing MSR in variable beforeInt. Interrupt is enabled, and it is disabled after some delay implemented using for loop.
When interrupt is enabled, the program jumps to interrupt handler, which clearing the carry bit and acknowledge the interrupt. After interrupt is disabled, MSR is read and stored in afterInt. The MSR before and after interrupt are printed out. The output of QEMU for the test program:
MSR before interrupt : 20000404
MSR after interrupt : 20000404
The test program shows that CC bit is not set when C bit is set. This problem must be solved, because many operations depend on CC bit in MSR. As the result, the codes in QEMU that perform operation on MSR have to be modified, so that QEMU is mimicking the correct behavior of AEMB.
After much investigation, the function that causing CC bit not set is found to be dec_msr, where the decoding of MSR happens. In dec_msr, the operation of setting or clearing a bit is determined by decoding the op-code, where a ‘1’ in bit 15 is msrclr, and ‘0’ is msrset operation. The most important operations are done by following codes:
if (clr) {
tcg_gen_not_tl(t1, t1);
tcg_gen_and_tl(t0, t0, t1);
} else
tcg_gen_or_tl(t0, t0, t1);
msr_write(dc, t0);
The bit location is determined by immediate value encoded in the op-code. The immediate value and MSR are first stored in t1 and t0, respectively. clr determines whether the op-code is msrset or msrcls. If the op-code is msrcls operation, if (clr) statement will be executed. The immediate value is inverted, followed by AND operation with MSR to mask the cleared bit. On the other hand, if op-code is msrset, the immediate value is OR-ed with MSR, and the corresponding bit in MSR is set. However, in the case of setting carry, i.e. msrset r0, 4, only carry bit is set, and CC bit in MSR remained untouched.
The problem can be solved by first checking the immediate value for msrset operation. If the immediate value is 4 (C bit in MSR), MSR is OR-ed with 0x80000004, which is the mask for setting C and CC bit. If the operation is not setting carry bit, the immediate value is just OR-ed with MSR so that the corresponding bit is set in the MSR.
if (clr) {
tcg_gen_not_tl(t1, t1);
tcg_gen_and_tl(t0, t0, t1);
} else {
if (sr==4)
tcg_gen_ori_tl(t0,t0,MSR_CCC_mask);
else
tcg_gen_or_tl(t0, t0, t1); }
msr_write(dc, t0);
Having the modification, the output of QEMU for the test program now becomes:
MSR before interrupt : A0000404
MSR after interrupt : A0000404
where CC and C bit are both set, and MSR before and after interrupt are the same.
0 Comments