* [Qemu-devel] rtl8139 timer interrupt rewrote
@ 2010-02-05 21:02 Frediano Ziglio
2010-02-08 8:28 ` Markus Armbruster
0 siblings, 1 reply; 2+ messages in thread
From: Frediano Ziglio @ 2010-02-05 21:02 UTC (permalink / raw)
To: qemu-devel
[-- Attachment #1: Type: text/plain, Size: 375 bytes --]
Hi,
I rewrote timer implementation for this card. I wrote even a small
Linux guest test program (attached as main.c). This patch add a QEMU
timer only when needed (timeout status not set, timeout irq wanted and
timer set). I tested this patch with a Darwin system and with my test
program (I bought a compatible physical card and it works too).
Regards
Frediano Ziglio
[-- Attachment #2: qemu-0.12.2.diff --]
[-- Type: text/x-patch, Size: 12575 bytes --]
--- hw/rtl8139.c.orig 2010-01-14 23:17:59.000000000 +0100
+++ hw/rtl8139.c 2010-02-05 20:25:31.711462995 +0100
@@ -34,41 +34,44 @@
* Implemented Tally Counters, increased VM load/save version
* Implemented IP/TCP/UDP checksum task offloading
*
* 2006-Jul-04 Igor Kovalenko : Implemented TCP segmentation offloading
* Fixed MTU=1500 for produced ethernet frames
*
* 2006-Jul-09 Igor Kovalenko : Fixed TCP header length calculation while processing
* segmentation offloading
* Removed slirp.h dependency
* Added rx/tx buffer reset when enabling rx/tx operation
+ *
+ * 2009-Feb-04 Frediano Ziglio: Rewrote timer support using QEMU timer only when strictly
+ * needed (required for for Darwin)
*/
#include "hw.h"
#include "pci.h"
#include "qemu-timer.h"
#include "net.h"
#include "loader.h"
/* debug RTL8139 card */
//#define DEBUG_RTL8139 1
#define PCI_FREQUENCY 33000000L
/* debug RTL8139 card C+ mode only */
//#define DEBUG_RTL8139CP 1
/* Calculate CRCs properly on Rx packets */
#define RTL8139_CALCULATE_RXCRC 1
/* Uncomment to enable on-board timer interrupts */
-//#define RTL8139_ONBOARD_TIMER 1
+#define RTL8139_ONBOARD_TIMER 1
#if defined(RTL8139_CALCULATE_RXCRC)
/* For crc32 */
#include <zlib.h>
#endif
#define SET_MASKED(input, mask, curr) \
( ( (input) & ~(mask) ) | ( (curr) & (mask) ) )
/* arg % size for size which is a power of 2 */
@@ -482,25 +485,34 @@ typedef struct RTL8139State {
int64_t TCTR_base;
/* Tally counters */
RTL8139TallyCounters tally_counters;
/* Non-persistent data */
uint8_t *cplus_txbuffer;
int cplus_txbuffer_len;
int cplus_txbuffer_offset;
+#ifdef RTL8139_ONBOARD_TIMER
/* PCI interrupt timer */
QEMUTimer *timer;
+ int64_t TimerExpire;
+#endif
} RTL8139State;
+#ifdef RTL8139_ONBOARD_TIMER
+static void rtl8139_set_next_tctr_time(RTL8139State *s, int64_t current_time);
+#else
+#define rtl8139_set_next_tctr_time(s,t) do { ; } while(0)
+#endif
+
static void prom9346_decode_command(EEprom9346 *eeprom, uint8_t command)
{
DEBUG_PRINT(("RTL8139: eeprom command 0x%02x\n", command));
switch (command & Chip9346_op_mask)
{
case Chip9346_op_read:
{
eeprom->address = command & EEPROM_9346_ADDR_MASK;
eeprom->output = eeprom->contents[eeprom->address];
@@ -2510,21 +2522,23 @@ static uint32_t rtl8139_RxBuf_read(RTL81
static void rtl8139_IntrMask_write(RTL8139State *s, uint32_t val)
{
DEBUG_PRINT(("RTL8139: IntrMask write(w) val=0x%04x\n", val));
/* mask unwriteable bits */
val = SET_MASKED(val, 0x1e00, s->IntrMask);
s->IntrMask = val;
+ rtl8139_set_next_tctr_time(s, qemu_get_clock(vm_clock));
rtl8139_update_irq(s);
+
}
static uint32_t rtl8139_IntrMask_read(RTL8139State *s)
{
uint32_t ret = s->IntrMask;
DEBUG_PRINT(("RTL8139: IntrMask read(w) val=0x%04x\n", ret));
return ret;
}
@@ -2543,26 +2557,36 @@ static void rtl8139_IntrStatus_write(RTL
uint16_t newStatus = s->IntrStatus & ~val;
/* mask unwriteable bits */
newStatus = SET_MASKED(newStatus, 0x1e00, s->IntrStatus);
/* writing 1 to interrupt status register bit clears it */
s->IntrStatus = 0;
rtl8139_update_irq(s);
s->IntrStatus = newStatus;
+ /*
+ * Computing if we miss an interrupt here is not that correct but
+ * considered that we should have had already an interrupt
+ * and probably emulated is slower is better to assume this resetting was done before
+ * testing on previous rtl8139_update_irq lead to IRQ loosing
+ */
+ rtl8139_set_next_tctr_time(s, qemu_get_clock(vm_clock));
rtl8139_update_irq(s);
+
#endif
}
static uint32_t rtl8139_IntrStatus_read(RTL8139State *s)
{
+ rtl8139_set_next_tctr_time(s, qemu_get_clock(vm_clock));
+
uint32_t ret = s->IntrStatus;
DEBUG_PRINT(("RTL8139: IntrStatus read(w) val=0x%04x\n", ret));
#if 0
/* reading ISR clears all interrupts */
s->IntrStatus = 0;
rtl8139_update_irq(s);
@@ -2727,20 +2751,56 @@ static void rtl8139_io_writew(void *opaq
default:
DEBUG_PRINT(("RTL8139: ioport write(w) addr=0x%x val=0x%04x via write(b)\n", addr, val));
rtl8139_io_writeb(opaque, addr, val & 0xff);
rtl8139_io_writeb(opaque, addr + 1, (val >> 8) & 0xff);
break;
}
}
+#ifdef RTL8139_ONBOARD_TIMER
+static void rtl8139_set_next_tctr_time(RTL8139State *s, int64_t current_time)
+{
+ DEBUG_PRINT(("RTL8139: entered rtl8139_set_next_tctr_time\n"));
+
+ if (s->TimerExpire && current_time >= s->TimerExpire) {
+ s->IntrStatus |= PCSTimeout;
+ rtl8139_update_irq(s);
+ }
+
+ /* Set QEMU timer only if needed that is
+ * - TimerInt <> 0 (we have a timer)
+ * - mask = 1 (we want an interrupt timer)
+ * - irq = 0 (irq is not already active)
+ * If any of above change we need to compute timer again
+ * Also we must check if timer is passed without QEMU timer
+ */
+ s->TimerExpire = 0;
+ if (!s->TimerInt)
+ return;
+
+ int64_t pci_time = muldiv64(current_time - s->TCTR_base, PCI_FREQUENCY, get_ticks_per_sec());
+ uint32_t low_pci = pci_time & 0xffffffff;
+ pci_time = pci_time - low_pci + s->TimerInt;
+ if (low_pci >= s->TimerInt)
+ pci_time += 0x100000000LL;
+ int64_t next_time = s->TCTR_base + muldiv64(pci_time, get_ticks_per_sec(), PCI_FREQUENCY);
+ s->TimerExpire = next_time;
+
+ if ((s->IntrMask & PCSTimeout) == 0 || (s->IntrStatus & PCSTimeout) != 0)
+ return;
+
+ qemu_mod_timer(s->timer, next_time);
+}
+#endif
+
static void rtl8139_io_writel(void *opaque, uint8_t addr, uint32_t val)
{
RTL8139State *s = opaque;
addr &= 0xfc;
switch (addr)
{
case RxMissed:
DEBUG_PRINT(("RTL8139: RxMissed clearing on write\n"));
@@ -2772,27 +2832,29 @@ static void rtl8139_io_writel(void *opaq
s->RxRingAddrLO = val;
break;
case RxRingAddrHI:
DEBUG_PRINT(("RTL8139: C+ RxRing high bits write val=0x%08x\n", val));
s->RxRingAddrHI = val;
break;
case Timer:
DEBUG_PRINT(("RTL8139: TCTR Timer reset on write\n"));
- s->TCTR = 0;
s->TCTR_base = qemu_get_clock(vm_clock);
+ rtl8139_set_next_tctr_time(s, s->TCTR_base);
break;
case FlashReg:
DEBUG_PRINT(("RTL8139: FlashReg TimerInt write val=0x%08x\n", val));
+ if (s->TimerInt == val) break;
s->TimerInt = val;
+ rtl8139_set_next_tctr_time(s, qemu_get_clock(vm_clock));
break;
default:
DEBUG_PRINT(("RTL8139: ioport write(l) addr=0x%x val=0x%08x via write(b)\n", addr, val));
rtl8139_io_writeb(opaque, addr, val & 0xff);
rtl8139_io_writeb(opaque, addr + 1, (val >> 8) & 0xff);
rtl8139_io_writeb(opaque, addr + 2, (val >> 16) & 0xff);
rtl8139_io_writeb(opaque, addr + 3, (val >> 24) & 0xff);
break;
}
@@ -2988,21 +3050,21 @@ static uint32_t rtl8139_io_readl(void *o
ret = s->RxRingAddrLO;
DEBUG_PRINT(("RTL8139: C+ RxRing low bits read val=0x%08x\n", ret));
break;
case RxRingAddrHI:
ret = s->RxRingAddrHI;
DEBUG_PRINT(("RTL8139: C+ RxRing high bits read val=0x%08x\n", ret));
break;
case Timer:
- ret = s->TCTR;
+ ret = muldiv64(qemu_get_clock(vm_clock) - s->TCTR_base, PCI_FREQUENCY, get_ticks_per_sec());
DEBUG_PRINT(("RTL8139: TCTR Timer read val=0x%08x\n", ret));
break;
case FlashReg:
ret = s->TimerInt;
DEBUG_PRINT(("RTL8139: FlashReg TimerInt read val=0x%08x\n", ret));
break;
default:
DEBUG_PRINT(("RTL8139: ioport read(l) addr=0x%x via read(b)\n", addr));
@@ -3093,33 +3155,45 @@ static uint32_t rtl8139_mmio_readl(void
uint32_t val = rtl8139_io_readl(opaque, addr & 0xFF);
#ifdef TARGET_WORDS_BIGENDIAN
val = bswap32(val);
#endif
return val;
}
static int rtl8139_post_load(void *opaque, int version_id)
{
RTL8139State* s = opaque;
+ rtl8139_set_next_tctr_time(s, qemu_get_clock(vm_clock));
if (version_id < 4) {
s->cplus_enabled = s->CpCmd != 0;
}
return 0;
}
+static void rtl8139_pre_save(void *opaque)
+{
+ RTL8139State* s = opaque;
+
+ // set IntrStatus correctly
+ int64_t current_time = qemu_get_clock(vm_clock);
+ rtl8139_set_next_tctr_time(s, current_time);
+ s->TCTR = muldiv64(current_time - s->TCTR_base, PCI_FREQUENCY, get_ticks_per_sec());
+}
+
static const VMStateDescription vmstate_rtl8139 = {
.name = "rtl8139",
.version_id = 4,
.minimum_version_id = 3,
.minimum_version_id_old = 3,
.post_load = rtl8139_post_load,
+ .pre_save = rtl8139_pre_save,
.fields = (VMStateField []) {
VMSTATE_PCI_DEVICE(dev, RTL8139State),
VMSTATE_PARTIAL_BUFFER(phys, RTL8139State, 6),
VMSTATE_BUFFER(mult, RTL8139State),
VMSTATE_UINT32_ARRAY(TxStatus, RTL8139State, 4),
VMSTATE_UINT32_ARRAY(TxAddr, RTL8139State, 4),
VMSTATE_UINT32(RxBuf, RTL8139State),
VMSTATE_UINT32(RxBufferSize, RTL8139State),
VMSTATE_UINT32(RxBufPtr, RTL8139State),
@@ -3219,71 +3293,34 @@ static CPUReadMemoryFunc * const rtl8139
rtl8139_mmio_readw,
rtl8139_mmio_readl,
};
static CPUWriteMemoryFunc * const rtl8139_mmio_write[3] = {
rtl8139_mmio_writeb,
rtl8139_mmio_writew,
rtl8139_mmio_writel,
};
-static inline int64_t rtl8139_get_next_tctr_time(RTL8139State *s, int64_t current_time)
-{
- int64_t next_time = current_time +
- muldiv64(1, get_ticks_per_sec(), PCI_FREQUENCY);
- if (next_time <= current_time)
- next_time = current_time + 1;
- return next_time;
-}
-
#ifdef RTL8139_ONBOARD_TIMER
static void rtl8139_timer(void *opaque)
{
RTL8139State *s = opaque;
- int is_timeout = 0;
-
- int64_t curr_time;
- uint32_t curr_tick;
-
if (!s->clock_enabled)
{
DEBUG_PRINT(("RTL8139: >>> timer: clock is not running\n"));
return;
}
- curr_time = qemu_get_clock(vm_clock);
-
- curr_tick = muldiv64(curr_time - s->TCTR_base, PCI_FREQUENCY,
- get_ticks_per_sec());
-
- if (s->TimerInt && curr_tick >= s->TimerInt)
- {
- if (s->TCTR < s->TimerInt || curr_tick < s->TCTR)
- {
- is_timeout = 1;
- }
- }
-
- s->TCTR = curr_tick;
-
-// DEBUG_PRINT(("RTL8139: >>> timer: tick=%08u\n", s->TCTR));
-
- if (is_timeout)
- {
- DEBUG_PRINT(("RTL8139: >>> timer: timeout tick=%08u\n", s->TCTR));
- s->IntrStatus |= PCSTimeout;
- rtl8139_update_irq(s);
- }
-
- qemu_mod_timer(s->timer,
- rtl8139_get_next_tctr_time(s,curr_time));
+ s->IntrStatus |= PCSTimeout;
+ rtl8139_update_irq(s);
+ rtl8139_set_next_tctr_time(s, qemu_get_clock(vm_clock));
}
#endif /* RTL8139_ONBOARD_TIMER */
static void rtl8139_cleanup(VLANClientState *nc)
{
RTL8139State *s = DO_UPCAST(NICState, nc, nc)->opaque;
s->nic = NULL;
}
@@ -3341,24 +3378,23 @@ static int pci_rtl8139_init(PCIDevice *d
s->nic = qemu_new_nic(&net_rtl8139_info, &s->conf,
dev->qdev.info->name, dev->qdev.id, s);
qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a);
s->cplus_txbuffer = NULL;
s->cplus_txbuffer_len = 0;
s->cplus_txbuffer_offset = 0;
#ifdef RTL8139_ONBOARD_TIMER
+ s->TimerExpire = 0;
s->timer = qemu_new_timer(vm_clock, rtl8139_timer, s);
-
- qemu_mod_timer(s->timer,
- rtl8139_get_next_tctr_time(s,qemu_get_clock(vm_clock)));
+ rtl8139_set_next_tctr_time(s, qemu_get_clock(vm_clock));
#endif /* RTL8139_ONBOARD_TIMER */
return 0;
}
static PCIDeviceInfo rtl8139_info = {
.qdev.name = "rtl8139",
.qdev.size = sizeof(RTL8139State),
.qdev.reset = rtl8139_reset,
.qdev.vmsd = &vmstate_rtl8139,
.init = pci_rtl8139_init,
[-- Attachment #3: main.c --]
[-- Type: text/x-csrc, Size: 5037 bytes --]
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <ctype.h>
#include <string.h>
#include <sys/io.h>
static void restore(void);
static void __attribute__ ((format (printf, 1, 2)))
fatal(const char *msg, ...)
{
va_list ap;
fprintf(stderr, "fatal error: ");
va_start(ap, msg);
vfprintf (stderr, msg, ap);
va_end(ap);
restore();
exit(1);
}
static int port_start = -1;
static int verbose = 1;
#define PORT(name,len,val) \
static unsigned in_##name() { \
unsigned res = in##len(port_start+(val)); \
if (verbose) printf("*%s -> %x\n", #name, res); \
return res; \
} \
static void out_##name(unsigned v) { \
if (verbose) printf("%x -> *%s\n", v, #name); \
out##len(v,port_start+(val)); \
}
PORT(Timer,l,0x48)
PORT(IntrMask,w,0x3c)
PORT(IntrStatus,w,0x3E)
PORT(TimerInt,l,0x54)
static void my_sleep(int n)
{
if (verbose) {
printf("sleeping %d seconds...", n);
fflush(stdout);
}
sleep(n);
if (verbose) printf("\n\n");
}
#define sleep(n) my_sleep(n)
static int saved_mask = -1;
static int saved_tmr = -1;
static void restore(void)
{
if (saved_mask >= 0)
out_IntrMask(saved_mask);
if (saved_tmr >= 0)
out_TimerInt(saved_tmr);
out_Timer(0);
}
static void stop(int sig)
{
restore();
printf("bye bye\n");
exit(1);
}
#define CLK 33000000
int main()
{
// find port address from lspci command
FILE *f = popen("lspci -vn", "r");
if (!f)
fatal("executing lspci...\n");
char line[128];
int in_card = 0;
while(fgets(line, sizeof(line), f)) {
// parse header, select our card
if (!isspace(line[0])) {
in_card = 0;
if (strstr(line, " 10ec:8139 ") != NULL)
in_card = 1;
continue;
}
// skip other cards
if (!in_card) continue;
if (sscanf(line, " I/O ports at %x [size", &port_start) == 1)
break;
}
pclose(f);
if (port_start < 0)
fatal("port not found!\n");
printf("found card at address %x\n", port_start);
// enable direct port access
if (iopl(3))
fatal("iopl...\n");
signal(SIGTERM, stop);
signal(SIGINT, stop);
saved_mask = in_IntrMask();
saved_tmr = in_TimerInt();
const unsigned from = 0.95 * CLK;
const unsigned to = 1.6 * CLK;
out_IntrMask(0);
in_IntrStatus();
in_Timer();
in_Timer();
// Test 1. test counter continue and continue
out_TimerInt(0); // disable timer
out_IntrStatus(0x4000);
out_Timer(12345); // reset timer to 0
unsigned curr = in_Timer();
unsigned cnt;
if (curr > 0.1 * CLK)
fatal("time too big %u\n", curr);
for (cnt=0;;) {
sleep(1);
unsigned prev = curr;
curr = in_Timer();
// test skep is in a specific range
unsigned diff = (curr-prev) & 0xffffffffu;
if (diff < from || diff > to)
fatal("Invalid diff %u (%u-%u)\n", diff, from,to);
if (curr < prev && ++cnt == 3)
break;
}
// Test 2. Check we didn't get an interrupt with TimerInt == 0
if (in_IntrStatus() & 0x4000)
fatal("got an interrupt\n");
// Test 3. Setting TimerInt to 1 and Timer to 0 get interrupt
out_TimerInt(1);
out_Timer(0);
if ((in_IntrStatus() & 0x4000) == 0)
fatal("we should have an interrupt here!\n");
// Test 3. Check acknowledge
out_IntrStatus(0x4000);
if (in_IntrStatus() & 0x4000)
fatal("got an interrupt\n");
// Test. Status set after Timer reset
out_Timer(0);
out_TimerInt(0);
out_IntrStatus(0x4000);
curr = in_Timer();
out_TimerInt(curr + 0.5 * CLK);
sleep(1);
out_Timer(0);
if ((in_IntrStatus() & 0x4000) == 0)
fatal("we should have an interrupt here!\n");
// Test. Status set after TimerInt reset
out_Timer(0);
out_TimerInt(0);
out_IntrStatus(0x4000);
curr = in_Timer();
out_TimerInt(curr + 0.5 * CLK);
sleep(1);
out_TimerInt(0);
if ((in_IntrStatus() & 0x4000) == 0)
fatal("we should have an interrupt here!\n");
// Test 4. Increment TimerInt we should see an interrupt
curr = in_Timer();
unsigned next = curr + 5.0 * CLK;
out_TimerInt(next);
for (cnt=0;;) {
sleep(1);
unsigned prev = curr;
curr = in_Timer();
unsigned diff = (curr-prev) & 0xffffffffu;
if (diff < from || diff > to)
fatal("Invalid diff %u (%u-%u)\n", diff, from,to);
if (cnt < 3 && curr > next) {
if ((in_IntrStatus() & 0x4000) == 0)
fatal("we should have an interrupt here!\n");
out_IntrStatus(0x4000);
next = curr + 5.0 * CLK;
out_TimerInt(next);
if (++cnt == 3)
out_TimerInt(1);
// Test 5. Second time we pass from 0 should see an interrupt
} else if (cnt >= 3 && curr < prev) {
// here we should have an interrupt
if ((in_IntrStatus() & 0x4000) == 0)
fatal("we should have an interrupt here!\n");
out_IntrStatus(0x4000);
if (++cnt == 5) break;
}
}
printf("Everythink is ok!\n");
verbose = 0;
restore();
return 0;
/* irq is set when Timer == TimerInt
* TimerInt == 0 disable timer
* Timer keep counting always
* If you set ANY value to Timer Timer is set to 0
*/
}
/*
test
- 0 TimerInt non setta mai irq
- settanto a 1 TimerInt e poi resettando Timer subito irq
- passando seconda volta setta comunque
- scrivo qualsiasi in Timer valore e va a 0 comunque
*/
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [Qemu-devel] rtl8139 timer interrupt rewrote
2010-02-05 21:02 [Qemu-devel] rtl8139 timer interrupt rewrote Frediano Ziglio
@ 2010-02-08 8:28 ` Markus Armbruster
0 siblings, 0 replies; 2+ messages in thread
From: Markus Armbruster @ 2010-02-08 8:28 UTC (permalink / raw)
To: Frediano Ziglio; +Cc: qemu-devel
You can improve your patch's chances for getting noticed, reviewed and
merged by putting [PATCH] in your subject. Consider using
git-format-patch and git-send-email.
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2010-02-08 8:29 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-02-05 21:02 [Qemu-devel] rtl8139 timer interrupt rewrote Frediano Ziglio
2010-02-08 8:28 ` Markus Armbruster
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).