Compare commits

..

4 Commits

Author SHA1 Message Date
bellard
d5a0b50c6f update
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@287 c046a42c-6fe2-441c-8c8c-71466251a162
2003-06-27 12:02:03 +00:00
bellard
87858c89ca more precise timer emulation - fixed NE2000 probe problems - added VLTMPDIR support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@286 c046a42c-6fe2-441c-8c8c-71466251a162
2003-06-27 12:01:39 +00:00
bellard
a6f816d697 fixed endianness (Jocelyn Mayer)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@285 c046a42c-6fe2-441c-8c8c-71466251a162
2003-06-26 17:09:07 +00:00
bellard
0ad041d476 fixed inline pb
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@284 c046a42c-6fe2-441c-8c8c-71466251a162
2003-06-25 22:11:41 +00:00
8 changed files with 197 additions and 40 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -1 +1 @@
0.4
0.4.1

View File

@@ -108,6 +108,7 @@ typedef uint64_t host_ulong;
#define SHT_RELOC SHT_REL
#endif
#define NO_THUNK_TYPE_SIZE
#include "thunk.h"
enum {

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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
View File

@@ -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;