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