1 /* 2 * QTest testcase for Realtek 8139 NIC 3 * 4 * Copyright (c) 2013-2014 SUSE LINUX Products GmbH 5 * 6 * This work is licensed under the terms of the GNU GPL, version 2 or later. 7 * See the COPYING file in the top-level directory. 8 */ 9 10 #include "qemu/osdep.h" 11 #include "libqtest-single.h" 12 #include "libqos/pci-pc.h" 13 #include "qemu/timer.h" 14 #include "qemu-common.h" 15 16 /* Tests only initialization so far. TODO: Replace with functional tests */ 17 static void nop(void) 18 { 19 } 20 21 #define CLK 33333333 22 23 static QPCIBus *pcibus; 24 static QPCIDevice *dev; 25 static QPCIBar dev_bar; 26 27 static void save_fn(QPCIDevice *dev, int devfn, void *data) 28 { 29 QPCIDevice **pdev = (QPCIDevice **) data; 30 31 *pdev = dev; 32 } 33 34 static QPCIDevice *get_device(void) 35 { 36 QPCIDevice *dev; 37 38 pcibus = qpci_new_pc(global_qtest, NULL); 39 qpci_device_foreach(pcibus, 0x10ec, 0x8139, save_fn, &dev); 40 g_assert(dev != NULL); 41 42 return dev; 43 } 44 45 #define PORT(name, len, val) \ 46 static unsigned __attribute__((unused)) in_##name(void) \ 47 { \ 48 unsigned res = qpci_io_read##len(dev, dev_bar, (val)); \ 49 g_test_message("*%s -> %x", #name, res); \ 50 return res; \ 51 } \ 52 static void out_##name(unsigned v) \ 53 { \ 54 g_test_message("%x -> *%s", v, #name); \ 55 qpci_io_write##len(dev, dev_bar, (val), v); \ 56 } 57 58 PORT(Timer, l, 0x48) 59 PORT(IntrMask, w, 0x3c) 60 PORT(IntrStatus, w, 0x3E) 61 PORT(TimerInt, l, 0x54) 62 63 #define fatal(...) do { g_test_message(__VA_ARGS__); g_assert(0); } while (0) 64 65 static void test_timer(void) 66 { 67 const unsigned from = 0.95 * CLK; 68 const unsigned to = 1.6 * CLK; 69 unsigned prev, curr, next; 70 unsigned cnt, diff; 71 72 out_IntrMask(0); 73 74 in_IntrStatus(); 75 in_Timer(); 76 in_Timer(); 77 78 /* Test 1. test counter continue and continue */ 79 out_TimerInt(0); /* disable timer */ 80 out_IntrStatus(0x4000); 81 out_Timer(12345); /* reset timer to 0 */ 82 curr = in_Timer(); 83 if (curr > 0.1 * CLK) { 84 fatal("time too big %u\n", curr); 85 } 86 for (cnt = 0; ; ) { 87 clock_step(1 * NANOSECONDS_PER_SECOND); 88 prev = curr; 89 curr = in_Timer(); 90 91 /* test skip is in a specific range */ 92 diff = (curr-prev) & 0xffffffffu; 93 if (diff < from || diff > to) { 94 fatal("Invalid diff %u (%u-%u)\n", diff, from, to); 95 } 96 if (curr < prev && ++cnt == 3) { 97 break; 98 } 99 } 100 101 /* Test 2. Check we didn't get an interrupt with TimerInt == 0 */ 102 if (in_IntrStatus() & 0x4000) { 103 fatal("got an interrupt\n"); 104 } 105 106 /* Test 3. Setting TimerInt to 1 and Timer to 0 get interrupt */ 107 out_TimerInt(1); 108 out_Timer(0); 109 clock_step(40); 110 if ((in_IntrStatus() & 0x4000) == 0) { 111 fatal("we should have an interrupt here!\n"); 112 } 113 114 /* Test 3. Check acknowledge */ 115 out_IntrStatus(0x4000); 116 if (in_IntrStatus() & 0x4000) { 117 fatal("got an interrupt\n"); 118 } 119 120 /* Test. Status set after Timer reset */ 121 out_Timer(0); 122 out_TimerInt(0); 123 out_IntrStatus(0x4000); 124 curr = in_Timer(); 125 out_TimerInt(curr + 0.5 * CLK); 126 clock_step(1 * NANOSECONDS_PER_SECOND); 127 out_Timer(0); 128 if ((in_IntrStatus() & 0x4000) == 0) { 129 fatal("we should have an interrupt here!\n"); 130 } 131 132 /* Test. Status set after TimerInt reset */ 133 out_Timer(0); 134 out_TimerInt(0); 135 out_IntrStatus(0x4000); 136 curr = in_Timer(); 137 out_TimerInt(curr + 0.5 * CLK); 138 clock_step(1 * NANOSECONDS_PER_SECOND); 139 out_TimerInt(0); 140 if ((in_IntrStatus() & 0x4000) == 0) { 141 fatal("we should have an interrupt here!\n"); 142 } 143 144 /* Test 4. Increment TimerInt we should see an interrupt */ 145 curr = in_Timer(); 146 next = curr + 5.0 * CLK; 147 out_TimerInt(next); 148 for (cnt = 0; ; ) { 149 clock_step(1 * NANOSECONDS_PER_SECOND); 150 prev = curr; 151 curr = in_Timer(); 152 diff = (curr-prev) & 0xffffffffu; 153 if (diff < from || diff > to) { 154 fatal("Invalid diff %u (%u-%u)\n", diff, from, to); 155 } 156 if (cnt < 3 && curr > next) { 157 if ((in_IntrStatus() & 0x4000) == 0) { 158 fatal("we should have an interrupt here!\n"); 159 } 160 out_IntrStatus(0x4000); 161 next = curr + 5.0 * CLK; 162 out_TimerInt(next); 163 if (++cnt == 3) { 164 out_TimerInt(1); 165 } 166 /* Test 5. Second time we pass from 0 should see an interrupt */ 167 } else if (cnt >= 3 && curr < prev) { 168 /* here we should have an interrupt */ 169 if ((in_IntrStatus() & 0x4000) == 0) { 170 fatal("we should have an interrupt here!\n"); 171 } 172 out_IntrStatus(0x4000); 173 if (++cnt == 5) { 174 break; 175 } 176 } 177 } 178 179 g_test_message("Everythink is ok!"); 180 } 181 182 183 static void test_init(void) 184 { 185 uint64_t barsize; 186 187 dev = get_device(); 188 189 dev_bar = qpci_iomap(dev, 0, &barsize); 190 191 qpci_device_enable(dev); 192 193 test_timer(); 194 } 195 196 int main(int argc, char **argv) 197 { 198 int ret; 199 200 qtest_start("-device rtl8139"); 201 202 g_test_init(&argc, &argv, NULL); 203 qtest_add_func("/rtl8139/nop", nop); 204 qtest_add_func("/rtl8139/timer", test_init); 205 206 ret = g_test_run(); 207 208 qtest_end(); 209 210 return ret; 211 } 212