Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
d5a0b50c6f | ||
|
87858c89ca | ||
|
a6f816d697 | ||
|
0ad041d476 |
@@ -1,3 +1,10 @@
|
||||
version 0.4.1:
|
||||
|
||||
- more accurate timer support in vl.
|
||||
- more reliable NE2000 probe in vl.
|
||||
- added 2.5.66 kernel in vl-test.
|
||||
- added VLTMPDIR environment variable in vl.
|
||||
|
||||
version 0.4:
|
||||
|
||||
- initial support for ring 0 x86 processor emulation
|
||||
|
1
Makefile
1
Makefile
@@ -189,6 +189,7 @@ distclean: clean
|
||||
rm -f config.mak config.h
|
||||
|
||||
install: $(PROGS)
|
||||
mkdir -p $(prefix)/bin
|
||||
install -m 755 -s $(PROGS) $(prefix)/bin
|
||||
|
||||
# various test targets
|
||||
|
1
dyngen.c
1
dyngen.c
@@ -108,6 +108,7 @@ typedef uint64_t host_ulong;
|
||||
#define SHT_RELOC SHT_REL
|
||||
#endif
|
||||
|
||||
#define NO_THUNK_TYPE_SIZE
|
||||
#include "thunk.h"
|
||||
|
||||
enum {
|
||||
|
@@ -3074,7 +3074,8 @@ static int print_insn_powerpc(FILE *, unsigned long insn, unsigned memaddr, int
|
||||
|
||||
int print_insn_ppc (bfd_vma pc, disassemble_info *info)
|
||||
{
|
||||
return print_insn_powerpc (info->stream, *(unsigned *)(long)pc, pc,
|
||||
return print_insn_powerpc (info->stream,
|
||||
(unsigned int)bfd_getb32((bfd_byte *)pc), pc,
|
||||
PPC_OPCODE_PPC | PPC_OPCODE_601);
|
||||
}
|
||||
|
||||
|
@@ -47,7 +47,7 @@ QEMU generic features:
|
||||
|
||||
@item Self-modifying code support.
|
||||
|
||||
@item Precise exception support.
|
||||
@item Precise exceptions support.
|
||||
|
||||
@item The virtual CPU is a library (@code{libqemu}) which can be used
|
||||
in other projects.
|
||||
@@ -128,7 +128,7 @@ generic dynamic code generation architecture of QEMU.
|
||||
|
||||
@end itemize
|
||||
|
||||
@chapter QEMU User space emulation invocation
|
||||
@chapter QEMU User space emulator invocation
|
||||
|
||||
@section Quick Start
|
||||
|
||||
@@ -315,7 +315,8 @@ sh: can't access tty; job control turned off
|
||||
Then you can play with the kernel inside the virtual serial console. You
|
||||
can launch @code{ls} for example. Type @key{Ctrl-a h} to have an help
|
||||
about the keys you can type inside the virtual serial console. In
|
||||
particular @key{Ctrl-a b} is the Magic SysRq key.
|
||||
particular, use @key{Ctrl-a x} to exit QEMU and use @key{Ctrl-a b} as
|
||||
the Magic SysRq key.
|
||||
|
||||
@item
|
||||
If the network is enabled, launch the script @file{/etc/linuxrc} in the
|
||||
@@ -334,9 +335,24 @@ a real Virtual Linux system !
|
||||
|
||||
@end enumerate
|
||||
|
||||
NOTE: the example initrd is a modified version of the one made by Kevin
|
||||
NOTES:
|
||||
@enumerate
|
||||
@item
|
||||
A 2.5.66 kernel is also included in the vl-test archive. Just
|
||||
replace the bzImage in vl.sh to try it.
|
||||
|
||||
@item
|
||||
vl creates a temporary file in @var{$VLTMPDIR} (@file{/tmp} is the
|
||||
default) containing all the simulated PC memory. If possible, try to use
|
||||
a temporary directory using the tmpfs filesystem to avoid too many
|
||||
unnecessary disk accesses.
|
||||
|
||||
@item
|
||||
The example initrd is a modified version of the one made by Kevin
|
||||
Lawton for the plex86 Project (@url{www.plex86.org}).
|
||||
|
||||
@end enumerate
|
||||
|
||||
@section Kernel Compilation
|
||||
|
||||
You can use any Linux kernel within QEMU provided it is mapped at
|
||||
@@ -372,6 +388,20 @@ As you would do to make a real kernel. Then you can use with QEMU
|
||||
exactly the same kernel as you would boot on your PC (in
|
||||
@file{arch/i386/boot/bzImage}).
|
||||
|
||||
If you are not using a 2.5 kernel as host kernel but if you use a target
|
||||
2.5 kernel, you must also ensure that the 'HZ' define is set to 100
|
||||
(1000 is the default) as QEMU cannot currently emulate timers at
|
||||
frequencies greater than 100 Hz on host Linux systems < 2.5. In
|
||||
asm/param.h, replace:
|
||||
|
||||
@example
|
||||
# define HZ 1000 /* Internal kernel timer frequency */
|
||||
@end example
|
||||
by
|
||||
@example
|
||||
# define HZ 100 /* Internal kernel timer frequency */
|
||||
@end example
|
||||
|
||||
@section PC Emulation
|
||||
|
||||
QEMU emulates the following PC peripherials:
|
||||
@@ -388,7 +418,7 @@ Serial port (port=0x3f8, irq=4)
|
||||
@item
|
||||
NE2000 network adapter (port=0x300, irq=9)
|
||||
@item
|
||||
Dumb VGA (to print the @code{uncompressing Linux kernel} message)
|
||||
Dumb VGA (to print the @code{Uncompressing Linux} message)
|
||||
@end itemize
|
||||
|
||||
@chapter QEMU Internals
|
||||
@@ -405,9 +435,9 @@ Like Valgrind [2], QEMU does user space emulation and dynamic
|
||||
translation. Valgrind is mainly a memory debugger while QEMU has no
|
||||
support for it (QEMU could be used to detect out of bound memory
|
||||
accesses as Valgrind, but it has no support to track uninitialised data
|
||||
as Valgrind does). Valgrind dynamic translator generates better code
|
||||
as Valgrind does). The Valgrind dynamic translator generates better code
|
||||
than QEMU (in particular it does register allocation) but it is closely
|
||||
tied to an x86 host and target and has no support for precise exception
|
||||
tied to an x86 host and target and has no support for precise exceptions
|
||||
and system emulation.
|
||||
|
||||
EM86 [4] is the closest project to user space QEMU (and QEMU still uses
|
||||
@@ -433,8 +463,8 @@ system emulator. It requires a patched Linux kernel to work (you cannot
|
||||
launch the same kernel on your PC), but the patches are really small. As
|
||||
it is a PC virtualizer (no emulation is done except for some priveledged
|
||||
instructions), it has the potential of being faster than QEMU. The
|
||||
downside is that a complicated (and potentially unsafe) kernel patch is
|
||||
needed.
|
||||
downside is that a complicated (and potentially unsafe) host kernel
|
||||
patch is needed.
|
||||
|
||||
@section Portable dynamic translation
|
||||
|
||||
|
3
thunk.h
3
thunk.h
@@ -236,6 +236,7 @@ void thunk_register_struct(int id, const char *name, const argtype *types);
|
||||
void thunk_register_struct_direct(int id, const char *name, StructEntry *se1);
|
||||
const argtype *thunk_convert(void *dst, const void *src,
|
||||
const argtype *type_ptr, int to_host);
|
||||
#ifndef NO_THUNK_TYPE_SIZE
|
||||
|
||||
extern StructEntry struct_entries[];
|
||||
|
||||
@@ -312,6 +313,8 @@ static inline int thunk_type_align(const argtype *type_ptr, int is_host)
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* NO_THUNK_TYPE_SIZE */
|
||||
|
||||
unsigned int target_to_host_bitmask(unsigned int x86_mask,
|
||||
bitmask_transtbl * trans_tbl);
|
||||
unsigned int host_to_target_bitmask(unsigned int alpha_mask,
|
||||
|
172
vl.c
172
vl.c
@@ -745,17 +745,19 @@ void pic_init(void)
|
||||
#define RW_STATE_LATCHED_WORD1 5
|
||||
|
||||
typedef struct PITChannelState {
|
||||
uint16_t count;
|
||||
int count; /* can be 65536 */
|
||||
uint16_t latched_count;
|
||||
uint8_t rw_state;
|
||||
uint8_t mode;
|
||||
uint8_t bcd; /* not supported */
|
||||
uint8_t gate; /* timer start */
|
||||
int64_t count_load_time;
|
||||
int64_t count_last_edge_check_time;
|
||||
} PITChannelState;
|
||||
|
||||
PITChannelState pit_channels[3];
|
||||
int speaker_data_on;
|
||||
int pit_min_timer_count = 0;
|
||||
|
||||
int64_t ticks_per_sec;
|
||||
|
||||
@@ -785,13 +787,36 @@ void cpu_calibrate_ticks(void)
|
||||
ticks_per_sec = (ticks * 1000000LL + (usec >> 1)) / usec;
|
||||
}
|
||||
|
||||
/* compute with 96 bit intermediate result: (a*b)/c */
|
||||
static uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c)
|
||||
{
|
||||
union {
|
||||
uint64_t ll;
|
||||
struct {
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
uint32_t high, low;
|
||||
#else
|
||||
uint32_t low, high;
|
||||
#endif
|
||||
} l;
|
||||
} u, res;
|
||||
uint64_t rl, rh;
|
||||
|
||||
u.ll = a;
|
||||
rl = (uint64_t)u.l.low * (uint64_t)b;
|
||||
rh = (uint64_t)u.l.high * (uint64_t)b;
|
||||
rh += (rl >> 32);
|
||||
res.l.high = rh / c;
|
||||
res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c;
|
||||
return res.ll;
|
||||
}
|
||||
|
||||
static int pit_get_count(PITChannelState *s)
|
||||
{
|
||||
int64_t d;
|
||||
uint64_t d;
|
||||
int counter;
|
||||
|
||||
d = ((cpu_get_ticks() - s->count_load_time) * PIT_FREQ) /
|
||||
ticks_per_sec;
|
||||
d = muldiv64(cpu_get_ticks() - s->count_load_time, PIT_FREQ, ticks_per_sec);
|
||||
switch(s->mode) {
|
||||
case 0:
|
||||
case 1:
|
||||
@@ -809,11 +834,10 @@ static int pit_get_count(PITChannelState *s)
|
||||
/* get pit output bit */
|
||||
static int pit_get_out(PITChannelState *s)
|
||||
{
|
||||
int64_t d;
|
||||
uint64_t d;
|
||||
int out;
|
||||
|
||||
d = ((cpu_get_ticks() - s->count_load_time) * PIT_FREQ) /
|
||||
ticks_per_sec;
|
||||
d = muldiv64(cpu_get_ticks() - s->count_load_time, PIT_FREQ, ticks_per_sec);
|
||||
switch(s->mode) {
|
||||
default:
|
||||
case 0:
|
||||
@@ -839,11 +863,74 @@ static int pit_get_out(PITChannelState *s)
|
||||
return out;
|
||||
}
|
||||
|
||||
/* get the number of 0 to 1 transitions we had since we call this
|
||||
function */
|
||||
/* XXX: maybe better to use ticks precision to avoid getting edges
|
||||
twice if checks are done at very small intervals */
|
||||
static int pit_get_out_edges(PITChannelState *s)
|
||||
{
|
||||
uint64_t d1, d2;
|
||||
int64_t ticks;
|
||||
int ret, v;
|
||||
|
||||
ticks = cpu_get_ticks();
|
||||
d1 = muldiv64(s->count_last_edge_check_time - s->count_load_time,
|
||||
PIT_FREQ, ticks_per_sec);
|
||||
d2 = muldiv64(ticks - s->count_load_time,
|
||||
PIT_FREQ, ticks_per_sec);
|
||||
s->count_last_edge_check_time = ticks;
|
||||
switch(s->mode) {
|
||||
default:
|
||||
case 0:
|
||||
if (d1 < s->count && d2 >= s->count)
|
||||
ret = 1;
|
||||
else
|
||||
ret = 0;
|
||||
break;
|
||||
case 1:
|
||||
ret = 0;
|
||||
break;
|
||||
case 2:
|
||||
d1 /= s->count;
|
||||
d2 /= s->count;
|
||||
ret = d2 - d1;
|
||||
break;
|
||||
case 3:
|
||||
v = s->count - (s->count >> 1);
|
||||
d1 = (d1 + v) / s->count;
|
||||
d2 = (d2 + v) / s->count;
|
||||
ret = d2 - d1;
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
if (d1 < s->count && d2 >= s->count)
|
||||
ret = 1;
|
||||
else
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void pit_load_count(PITChannelState *s, int val)
|
||||
{
|
||||
if (val == 0)
|
||||
val = 0x10000;
|
||||
s->count_load_time = cpu_get_ticks();
|
||||
s->count_last_edge_check_time = s->count_load_time;
|
||||
s->count = val;
|
||||
if (s == &pit_channels[0] && val <= pit_min_timer_count) {
|
||||
fprintf(stderr,
|
||||
"\nWARNING: vl: on your system, accurate timer emulation is impossible if its frequency is more than %d Hz. If using a 2.5.xx Linux kernel, you must patch asm/param.h to change HZ from 1000 to 100.\n\n",
|
||||
PIT_FREQ / pit_min_timer_count);
|
||||
}
|
||||
}
|
||||
|
||||
void pit_ioport_write(CPUX86State *env, uint32_t addr, uint32_t val)
|
||||
{
|
||||
int channel, access;
|
||||
PITChannelState *s;
|
||||
|
||||
|
||||
addr &= 3;
|
||||
if (addr == 3) {
|
||||
channel = val >> 6;
|
||||
@@ -857,27 +944,24 @@ void pit_ioport_write(CPUX86State *env, uint32_t addr, uint32_t val)
|
||||
s->rw_state = RW_STATE_LATCHED_WORD0;
|
||||
break;
|
||||
default:
|
||||
s->mode = (val >> 1) & 7;
|
||||
s->bcd = val & 1;
|
||||
s->rw_state = access - 1 + RW_STATE_LSB;
|
||||
break;
|
||||
}
|
||||
s->mode = (val >> 1) & 7;
|
||||
s->bcd = val & 1;
|
||||
} else {
|
||||
s = &pit_channels[addr];
|
||||
switch(s->rw_state) {
|
||||
case RW_STATE_LSB:
|
||||
s->count_load_time = cpu_get_ticks();
|
||||
s->count = val;
|
||||
pit_load_count(s, val);
|
||||
break;
|
||||
case RW_STATE_MSB:
|
||||
s->count_load_time = cpu_get_ticks();
|
||||
s->count = (val << 8);
|
||||
pit_load_count(s, val << 8);
|
||||
break;
|
||||
case RW_STATE_WORD0:
|
||||
case RW_STATE_WORD1:
|
||||
if (s->rw_state & 1) {
|
||||
s->count_load_time = cpu_get_ticks();
|
||||
s->count = (s->latched_count & 0xff) | (val << 8);
|
||||
pit_load_count(s, (s->latched_count & 0xff) | (val << 8));
|
||||
} else {
|
||||
s->latched_count = val;
|
||||
}
|
||||
@@ -935,16 +1019,23 @@ uint32_t speaker_ioport_read(CPUX86State *env, uint32_t addr)
|
||||
|
||||
void pit_init(void)
|
||||
{
|
||||
pit_channels[0].gate = 1;
|
||||
pit_channels[1].gate = 1;
|
||||
pit_channels[2].gate = 0;
|
||||
|
||||
PITChannelState *s;
|
||||
int i;
|
||||
|
||||
cpu_calibrate_ticks();
|
||||
|
||||
for(i = 0;i < 3; i++) {
|
||||
s = &pit_channels[i];
|
||||
s->mode = 3;
|
||||
s->gate = (i != 2);
|
||||
pit_load_count(s, 0);
|
||||
}
|
||||
|
||||
register_ioport_writeb(0x40, 4, pit_ioport_write);
|
||||
register_ioport_readb(0x40, 3, pit_ioport_read);
|
||||
|
||||
register_ioport_readb(0x61, 1, speaker_ioport_read);
|
||||
register_ioport_writeb(0x61, 1, speaker_ioport_write);
|
||||
cpu_calibrate_ticks();
|
||||
}
|
||||
|
||||
/***********************************************************/
|
||||
@@ -1462,6 +1553,8 @@ void ne2000_ioport_write(CPUX86State *env, uint32_t addr, uint32_t val)
|
||||
s->rcnt == 0) {
|
||||
s->isr |= ENISR_RDC;
|
||||
ne2000_update_irq(s);
|
||||
/* XXX: find a better solution for irqs */
|
||||
cpu_x86_interrupt(global_env);
|
||||
}
|
||||
if (val & E8390_TRANS) {
|
||||
net_send_packet(s, s->mem + (s->tpsr << 8), s->tcnt);
|
||||
@@ -1671,13 +1764,23 @@ static void host_segv_handler(int host_signum, siginfo_t *info,
|
||||
}
|
||||
|
||||
static int timer_irq_pending;
|
||||
static int timer_irq_count;
|
||||
|
||||
static void host_alarm_handler(int host_signum, siginfo_t *info,
|
||||
void *puc)
|
||||
{
|
||||
/* just exit from the cpu to have a chance to handle timers */
|
||||
cpu_x86_interrupt(global_env);
|
||||
timer_irq_pending = 1;
|
||||
/* NOTE: since usually the OS asks a 100 Hz clock, there can be
|
||||
some drift between cpu_get_ticks() and the interrupt time. So
|
||||
we queue some interrupts to avoid missing some */
|
||||
timer_irq_count += pit_get_out_edges(&pit_channels[0]);
|
||||
if (timer_irq_count) {
|
||||
if (timer_irq_count > 2)
|
||||
timer_irq_count = 2;
|
||||
timer_irq_count--;
|
||||
/* just exit from the cpu to have a chance to handle timers */
|
||||
cpu_x86_interrupt(global_env);
|
||||
timer_irq_pending = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void help(void)
|
||||
@@ -1705,7 +1808,8 @@ int main(int argc, char **argv)
|
||||
struct sigaction act;
|
||||
struct itimerval itv;
|
||||
CPUX86State *env;
|
||||
|
||||
const char *tmpdir;
|
||||
|
||||
/* we never want that malloc() uses mmap() */
|
||||
mallopt(M_MMAP_THRESHOLD, 4096 * 1024);
|
||||
|
||||
@@ -1749,14 +1853,19 @@ int main(int argc, char **argv)
|
||||
net_init();
|
||||
|
||||
/* init the memory */
|
||||
strcpy(phys_ram_file, "/tmp/vlXXXXXX");
|
||||
tmpdir = getenv("VLTMPDIR");
|
||||
if (!tmpdir)
|
||||
tmpdir = "/tmp";
|
||||
snprintf(phys_ram_file, sizeof(phys_ram_file), "%s/vlXXXXXX", tmpdir);
|
||||
if (mkstemp(phys_ram_file) < 0) {
|
||||
fprintf(stderr, "Could not create temporary memory file\n");
|
||||
fprintf(stderr, "Could not create temporary memory file '%s'\n",
|
||||
phys_ram_file);
|
||||
exit(1);
|
||||
}
|
||||
phys_ram_fd = open(phys_ram_file, O_CREAT | O_TRUNC | O_RDWR, 0600);
|
||||
if (phys_ram_fd < 0) {
|
||||
fprintf(stderr, "Could not open temporary memory file\n");
|
||||
fprintf(stderr, "Could not open temporary memory file '%s'\n",
|
||||
phys_ram_file);
|
||||
exit(1);
|
||||
}
|
||||
ftruncate(phys_ram_fd, phys_ram_size);
|
||||
@@ -1856,10 +1965,15 @@ int main(int argc, char **argv)
|
||||
env->eflags = 0x2;
|
||||
|
||||
itv.it_interval.tv_sec = 0;
|
||||
itv.it_interval.tv_usec = 10 * 1000;
|
||||
itv.it_interval.tv_usec = 1000;
|
||||
itv.it_value.tv_sec = 0;
|
||||
itv.it_value.tv_usec = 10 * 1000;
|
||||
setitimer(ITIMER_REAL, &itv, NULL);
|
||||
/* we probe the tick duration of the kernel to inform the user if
|
||||
the emulated kernel requested a too high timer frequency */
|
||||
getitimer(ITIMER_REAL, &itv);
|
||||
pit_min_timer_count = ((uint64_t)itv.it_interval.tv_usec * PIT_FREQ) /
|
||||
1000000;
|
||||
|
||||
for(;;) {
|
||||
struct pollfd ufds[2], *pf, *serial_ufd, *net_ufd;
|
||||
|
Reference in New Issue
Block a user