| 
									
										
										
										
											2013-11-07 18:43:09 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * QTest testcase for Realtek 8139 NIC | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (c) 2013-2014 SUSE LINUX Products GmbH | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This work is licensed under the terms of the GNU GPL, version 2 or later. | 
					
						
							|  |  |  |  * See the COPYING file in the top-level directory. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-08 18:08:51 +00:00
										 |  |  | #include "qemu/osdep.h"
 | 
					
						
							| 
									
										
										
										
											2019-09-03 07:50:26 +02:00
										 |  |  | #include "libqtest-single.h"
 | 
					
						
							| 
									
										
										
										
											2015-01-08 18:38:23 +00:00
										 |  |  | #include "libqos/pci-pc.h"
 | 
					
						
							| 
									
										
										
										
											2015-06-12 16:01:30 +03:00
										 |  |  | #include "qemu/timer.h"
 | 
					
						
							| 
									
										
										
										
											2013-11-07 18:43:09 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-15 13:41:22 +01:00
										 |  |  | static int verbosity_level; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-07 18:43:09 +01:00
										 |  |  | /* Tests only initialization so far. TODO: Replace with functional tests */ | 
					
						
							|  |  |  | static void nop(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-24 19:29:45 +02:00
										 |  |  | #define CLK 33333333
 | 
					
						
							| 
									
										
										
										
											2015-01-08 18:38:23 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | static QPCIBus *pcibus; | 
					
						
							| 
									
										
										
										
											2023-10-09 12:02:47 +02:00
										 |  |  | static QPCIDevice *pcidev; | 
					
						
							| 
									
										
										
										
											2016-10-24 15:52:06 +11:00
										 |  |  | static QPCIBar dev_bar; | 
					
						
							| 
									
										
										
										
											2015-01-08 18:38:23 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | static void save_fn(QPCIDevice *dev, int devfn, void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     QPCIDevice **pdev = (QPCIDevice **) data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     *pdev = dev; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static QPCIDevice *get_device(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     QPCIDevice *dev; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-19 13:50:27 +02:00
										 |  |  |     pcibus = qpci_new_pc(global_qtest, NULL); | 
					
						
							| 
									
										
										
										
											2015-01-08 18:38:23 +00:00
										 |  |  |     qpci_device_foreach(pcibus, 0x10ec, 0x8139, save_fn, &dev); | 
					
						
							|  |  |  |     g_assert(dev != NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return dev; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define PORT(name, len, val) \
 | 
					
						
							|  |  |  | static unsigned __attribute__((unused)) in_##name(void) \ | 
					
						
							|  |  |  | { \ | 
					
						
							| 
									
										
										
										
											2023-10-09 12:02:47 +02:00
										 |  |  |     unsigned res = qpci_io_read##len(pcidev, dev_bar, (val));     \ | 
					
						
							| 
									
										
										
										
											2023-02-15 13:41:22 +01:00
										 |  |  |     if (verbosity_level >= 2) { \ | 
					
						
							|  |  |  |         g_test_message("*%s -> %x", #name, res); \ | 
					
						
							|  |  |  |     } \ | 
					
						
							| 
									
										
										
										
											2015-01-08 18:38:23 +00:00
										 |  |  |     return res; \ | 
					
						
							|  |  |  | } \ | 
					
						
							|  |  |  | static void out_##name(unsigned v) \ | 
					
						
							|  |  |  | { \ | 
					
						
							| 
									
										
										
										
											2023-02-15 13:41:22 +01:00
										 |  |  |     if (verbosity_level >= 2) { \ | 
					
						
							|  |  |  |         g_test_message("%x -> *%s", v, #name); \ | 
					
						
							|  |  |  |     } \ | 
					
						
							| 
									
										
										
										
											2023-10-09 12:02:47 +02:00
										 |  |  |     qpci_io_write##len(pcidev, dev_bar, (val), v);        \ | 
					
						
							| 
									
										
										
										
											2015-01-08 18:38:23 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PORT(Timer, l, 0x48) | 
					
						
							|  |  |  | PORT(IntrMask, w, 0x3c) | 
					
						
							|  |  |  | PORT(IntrStatus, w, 0x3E) | 
					
						
							|  |  |  | PORT(TimerInt, l, 0x54) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-12 00:38:45 -07:00
										 |  |  | #define fatal(...) do { g_test_message(__VA_ARGS__); g_assert_not_reached(); } while (0)
 | 
					
						
							| 
									
										
										
										
											2015-01-08 18:38:23 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | static void test_timer(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     const unsigned from = 0.95 * CLK; | 
					
						
							|  |  |  |     const unsigned to = 1.6 * CLK; | 
					
						
							|  |  |  |     unsigned prev, curr, next; | 
					
						
							|  |  |  |     unsigned cnt, diff; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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 */ | 
					
						
							|  |  |  |     curr = in_Timer(); | 
					
						
							|  |  |  |     if (curr > 0.1 * CLK) { | 
					
						
							|  |  |  |         fatal("time too big %u\n", curr); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     for (cnt = 0; ; ) { | 
					
						
							| 
									
										
										
										
											2015-07-08 15:10:09 +01:00
										 |  |  |         clock_step(1 * NANOSECONDS_PER_SECOND); | 
					
						
							| 
									
										
										
										
											2015-01-08 18:38:23 +00:00
										 |  |  |         prev = curr; | 
					
						
							|  |  |  |         curr = in_Timer(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* test skip is in a specific range */ | 
					
						
							|  |  |  |         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); | 
					
						
							|  |  |  |     clock_step(40); | 
					
						
							|  |  |  |     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); | 
					
						
							| 
									
										
										
										
											2015-07-08 15:10:09 +01:00
										 |  |  |     clock_step(1 * NANOSECONDS_PER_SECOND); | 
					
						
							| 
									
										
										
										
											2015-01-08 18:38:23 +00:00
										 |  |  |     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); | 
					
						
							| 
									
										
										
										
											2015-07-08 15:10:09 +01:00
										 |  |  |     clock_step(1 * NANOSECONDS_PER_SECOND); | 
					
						
							| 
									
										
										
										
											2015-01-08 18:38:23 +00:00
										 |  |  |     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(); | 
					
						
							|  |  |  |     next = curr + 5.0 * CLK; | 
					
						
							|  |  |  |     out_TimerInt(next); | 
					
						
							|  |  |  |     for (cnt = 0; ; ) { | 
					
						
							| 
									
										
										
										
											2015-07-08 15:10:09 +01:00
										 |  |  |         clock_step(1 * NANOSECONDS_PER_SECOND); | 
					
						
							| 
									
										
										
										
											2015-01-08 18:38:23 +00:00
										 |  |  |         prev = curr; | 
					
						
							|  |  |  |         curr = in_Timer(); | 
					
						
							|  |  |  |         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; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-01 16:10:18 +01:00
										 |  |  |     g_test_message("Everythink is ok!"); | 
					
						
							| 
									
										
										
										
											2015-01-08 18:38:23 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void test_init(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     uint64_t barsize; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-09 12:02:47 +02:00
										 |  |  |     pcidev = get_device(); | 
					
						
							| 
									
										
										
										
											2015-01-08 18:38:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-09 12:02:47 +02:00
										 |  |  |     dev_bar = qpci_iomap(pcidev, 0, &barsize); | 
					
						
							| 
									
										
										
										
											2015-01-08 18:38:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-09 12:02:47 +02:00
										 |  |  |     qpci_device_enable(pcidev); | 
					
						
							| 
									
										
										
										
											2015-01-08 18:38:23 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     test_timer(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-07 18:43:09 +01:00
										 |  |  | int main(int argc, char **argv) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int ret; | 
					
						
							| 
									
										
										
										
											2023-02-15 13:41:22 +01:00
										 |  |  |     char *v_env = getenv("V"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (v_env) { | 
					
						
							|  |  |  |         verbosity_level = atoi(v_env); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2013-11-07 18:43:09 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-28 19:06:45 +00:00
										 |  |  |     g_test_init(&argc, &argv, NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-25 10:10:15 +02:00
										 |  |  |     if (!qtest_has_device("rtl8139")) { | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-11 12:19:52 -05:00
										 |  |  |     qtest_start("-device rtl8139"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-07 18:43:09 +01:00
										 |  |  |     qtest_add_func("/rtl8139/nop", nop); | 
					
						
							| 
									
										
										
										
											2015-01-08 18:38:23 +00:00
										 |  |  |     qtest_add_func("/rtl8139/timer", test_init); | 
					
						
							| 
									
										
										
										
											2013-11-07 18:43:09 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     ret = g_test_run(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     qtest_end(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return ret; | 
					
						
							|  |  |  | } |