Commit c20709aa32045c79e21905c4c009aae53d008af5
1 parent
92cb7d54
initial user mode network support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@730 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
3 changed files
with
266 additions
and
84 deletions
configure
... | ... | @@ -71,6 +71,7 @@ bigendian="no" |
71 | 71 | mingw32="no" |
72 | 72 | EXESUF="" |
73 | 73 | gdbstub="yes" |
74 | +slirp="no" | |
74 | 75 | |
75 | 76 | # OS specific |
76 | 77 | targetos=`uname -s` |
... | ... | @@ -124,6 +125,8 @@ for opt do |
124 | 125 | ;; |
125 | 126 | --enable-mingw32) mingw32="yes" ; cross_prefix="i386-mingw32-" |
126 | 127 | ;; |
128 | + --enable-slirp) slirp="yes" | |
129 | + ;; | |
127 | 130 | esac |
128 | 131 | done |
129 | 132 | |
... | ... | @@ -378,6 +381,10 @@ if test "$sdl" = "yes" ; then |
378 | 381 | fi |
379 | 382 | echo "" >> $config_mak |
380 | 383 | fi |
384 | +if test "$slirp" = "yes" ; then | |
385 | + echo "CONFIG_SLIRP=yes" >> $config_mak | |
386 | + echo "#define CONFIG_SLIRP 1" >> $config_h | |
387 | +fi | |
381 | 388 | echo -n "VERSION=" >>$config_mak |
382 | 389 | head $source_path/VERSION >>$config_mak |
383 | 390 | echo "" >>$config_mak | ... | ... |
vl.c
... | ... | @@ -45,6 +45,10 @@ |
45 | 45 | #include <linux/if_tun.h> |
46 | 46 | #endif |
47 | 47 | |
48 | +#if defined(CONFIG_SLIRP) | |
49 | +#include "libslirp.h" | |
50 | +#endif | |
51 | + | |
48 | 52 | #ifdef _WIN32 |
49 | 53 | #include <sys/timeb.h> |
50 | 54 | #include <windows.h> |
... | ... | @@ -750,20 +754,121 @@ int serial_open_device(void) |
750 | 754 | #endif |
751 | 755 | |
752 | 756 | /***********************************************************/ |
753 | -/* Linux network device redirector */ | |
757 | +/* Linux network device redirectors */ | |
754 | 758 | |
755 | -#ifdef _WIN32 | |
759 | +void hex_dump(FILE *f, const uint8_t *buf, int size) | |
760 | +{ | |
761 | + int len, i, j, c; | |
762 | + | |
763 | + for(i=0;i<size;i+=16) { | |
764 | + len = size - i; | |
765 | + if (len > 16) | |
766 | + len = 16; | |
767 | + fprintf(f, "%08x ", i); | |
768 | + for(j=0;j<16;j++) { | |
769 | + if (j < len) | |
770 | + fprintf(f, " %02x", buf[i+j]); | |
771 | + else | |
772 | + fprintf(f, " "); | |
773 | + } | |
774 | + fprintf(f, " "); | |
775 | + for(j=0;j<len;j++) { | |
776 | + c = buf[i+j]; | |
777 | + if (c < ' ' || c > '~') | |
778 | + c = '.'; | |
779 | + fprintf(f, "%c", c); | |
780 | + } | |
781 | + fprintf(f, "\n"); | |
782 | + } | |
783 | +} | |
784 | + | |
785 | +void qemu_send_packet(NetDriverState *nd, const uint8_t *buf, int size) | |
786 | +{ | |
787 | + nd->send_packet(nd, buf, size); | |
788 | +} | |
756 | 789 | |
757 | -static int net_init(void) | |
790 | +void qemu_add_read_packet(NetDriverState *nd, IOCanRWHandler *fd_can_read, | |
791 | + IOReadHandler *fd_read, void *opaque) | |
758 | 792 | { |
793 | + nd->add_read_packet(nd, fd_can_read, fd_read, opaque); | |
794 | +} | |
795 | + | |
796 | +/* dummy network adapter */ | |
797 | + | |
798 | +static void dummy_send_packet(NetDriverState *nd, const uint8_t *buf, int size) | |
799 | +{ | |
800 | +} | |
801 | + | |
802 | +static void dummy_add_read_packet(NetDriverState *nd, | |
803 | + IOCanRWHandler *fd_can_read, | |
804 | + IOReadHandler *fd_read, void *opaque) | |
805 | +{ | |
806 | +} | |
807 | + | |
808 | +static int net_dummy_init(NetDriverState *nd) | |
809 | +{ | |
810 | + nd->send_packet = dummy_send_packet; | |
811 | + nd->add_read_packet = dummy_add_read_packet; | |
812 | + pstrcpy(nd->ifname, sizeof(nd->ifname), "dummy"); | |
759 | 813 | return 0; |
760 | 814 | } |
761 | 815 | |
762 | -void net_send_packet(NetDriverState *nd, const uint8_t *buf, int size) | |
816 | +#if defined(CONFIG_SLIRP) | |
817 | + | |
818 | +/* slirp network adapter */ | |
819 | + | |
820 | +static void *slirp_fd_opaque; | |
821 | +static IOCanRWHandler *slirp_fd_can_read; | |
822 | +static IOReadHandler *slirp_fd_read; | |
823 | +static int slirp_inited; | |
824 | + | |
825 | +int slirp_can_output(void) | |
826 | +{ | |
827 | + return slirp_fd_can_read(slirp_fd_opaque); | |
828 | +} | |
829 | + | |
830 | +void slirp_output(const uint8_t *pkt, int pkt_len) | |
763 | 831 | { |
832 | +#if 0 | |
833 | + printf("output:\n"); | |
834 | + hex_dump(stdout, pkt, pkt_len); | |
835 | +#endif | |
836 | + slirp_fd_read(slirp_fd_opaque, pkt, pkt_len); | |
764 | 837 | } |
765 | 838 | |
766 | -#else | |
839 | +static void slirp_send_packet(NetDriverState *nd, const uint8_t *buf, int size) | |
840 | +{ | |
841 | +#if 0 | |
842 | + printf("input:\n"); | |
843 | + hex_dump(stdout, buf, size); | |
844 | +#endif | |
845 | + slirp_input(buf, size); | |
846 | +} | |
847 | + | |
848 | +static void slirp_add_read_packet(NetDriverState *nd, | |
849 | + IOCanRWHandler *fd_can_read, | |
850 | + IOReadHandler *fd_read, void *opaque) | |
851 | +{ | |
852 | + slirp_fd_opaque = opaque; | |
853 | + slirp_fd_can_read = fd_can_read; | |
854 | + slirp_fd_read = fd_read; | |
855 | +} | |
856 | + | |
857 | +static int net_slirp_init(NetDriverState *nd) | |
858 | +{ | |
859 | + if (!slirp_inited) { | |
860 | + slirp_inited = 1; | |
861 | + slirp_init(); | |
862 | + } | |
863 | + nd->send_packet = slirp_send_packet; | |
864 | + nd->add_read_packet = slirp_add_read_packet; | |
865 | + pstrcpy(nd->ifname, sizeof(nd->ifname), "slirp"); | |
866 | + return 0; | |
867 | +} | |
868 | + | |
869 | +#endif /* CONFIG_SLIRP */ | |
870 | + | |
871 | +#if !defined(_WIN32) | |
767 | 872 | |
768 | 873 | static int tun_open(char *ifname, int ifname_size) |
769 | 874 | { |
... | ... | @@ -790,60 +895,61 @@ static int tun_open(char *ifname, int ifname_size) |
790 | 895 | return fd; |
791 | 896 | } |
792 | 897 | |
793 | -static int net_init(void) | |
898 | +static void tun_send_packet(NetDriverState *nd, const uint8_t *buf, int size) | |
899 | +{ | |
900 | + write(nd->fd, buf, size); | |
901 | +} | |
902 | + | |
903 | +static void tun_add_read_packet(NetDriverState *nd, | |
904 | + IOCanRWHandler *fd_can_read, | |
905 | + IOReadHandler *fd_read, void *opaque) | |
794 | 906 | { |
795 | - int pid, status, launch_script, i; | |
796 | - NetDriverState *nd; | |
797 | - char *args[MAX_NICS + 2]; | |
907 | + qemu_add_fd_read_handler(nd->fd, fd_can_read, fd_read, opaque); | |
908 | +} | |
909 | + | |
910 | +static int net_tun_init(NetDriverState *nd) | |
911 | +{ | |
912 | + int pid, status; | |
913 | + char *args[3]; | |
798 | 914 | char **parg; |
799 | 915 | |
800 | - launch_script = 0; | |
801 | - for(i = 0; i < nb_nics; i++) { | |
802 | - nd = &nd_table[i]; | |
803 | - if (nd->fd < 0) { | |
804 | - nd->fd = tun_open(nd->ifname, sizeof(nd->ifname)); | |
805 | - if (nd->fd >= 0) | |
806 | - launch_script = 1; | |
807 | - } | |
808 | - } | |
916 | + nd->fd = tun_open(nd->ifname, sizeof(nd->ifname)); | |
917 | + if (nd->fd < 0) | |
918 | + return -1; | |
809 | 919 | |
810 | - if (launch_script) { | |
811 | - /* try to launch network init script */ | |
812 | - pid = fork(); | |
813 | - if (pid >= 0) { | |
814 | - if (pid == 0) { | |
815 | - parg = args; | |
816 | - *parg++ = network_script; | |
817 | - for(i = 0; i < nb_nics; i++) { | |
818 | - nd = &nd_table[i]; | |
819 | - if (nd->fd >= 0) { | |
820 | - *parg++ = nd->ifname; | |
821 | - } | |
822 | - } | |
823 | - *parg++ = NULL; | |
824 | - execv(network_script, args); | |
825 | - exit(1); | |
826 | - } | |
827 | - while (waitpid(pid, &status, 0) != pid); | |
828 | - if (!WIFEXITED(status) || | |
829 | - WEXITSTATUS(status) != 0) { | |
830 | - fprintf(stderr, "%s: could not launch network script\n", | |
831 | - network_script); | |
832 | - } | |
920 | + /* try to launch network init script */ | |
921 | + pid = fork(); | |
922 | + if (pid >= 0) { | |
923 | + if (pid == 0) { | |
924 | + parg = args; | |
925 | + *parg++ = network_script; | |
926 | + *parg++ = nd->ifname; | |
927 | + *parg++ = NULL; | |
928 | + execv(network_script, args); | |
929 | + exit(1); | |
930 | + } | |
931 | + while (waitpid(pid, &status, 0) != pid); | |
932 | + if (!WIFEXITED(status) || | |
933 | + WEXITSTATUS(status) != 0) { | |
934 | + fprintf(stderr, "%s: could not launch network script\n", | |
935 | + network_script); | |
833 | 936 | } |
834 | 937 | } |
938 | + nd->send_packet = tun_send_packet; | |
939 | + nd->add_read_packet = tun_add_read_packet; | |
835 | 940 | return 0; |
836 | 941 | } |
837 | 942 | |
838 | -void net_send_packet(NetDriverState *nd, const uint8_t *buf, int size) | |
943 | +static int net_fd_init(NetDriverState *nd, int fd) | |
839 | 944 | { |
840 | -#ifdef DEBUG_NE2000 | |
841 | - printf("NE2000: sending packet size=%d\n", size); | |
842 | -#endif | |
843 | - write(nd->fd, buf, size); | |
945 | + nd->fd = fd; | |
946 | + nd->send_packet = tun_send_packet; | |
947 | + nd->add_read_packet = tun_add_read_packet; | |
948 | + pstrcpy(nd->ifname, sizeof(nd->ifname), "tunfd"); | |
949 | + return 0; | |
844 | 950 | } |
845 | 951 | |
846 | -#endif | |
952 | +#endif /* !_WIN32 */ | |
847 | 953 | |
848 | 954 | /***********************************************************/ |
849 | 955 | /* dumb display */ |
... | ... | @@ -1597,6 +1703,28 @@ int main_loop(void) |
1597 | 1703 | } |
1598 | 1704 | } |
1599 | 1705 | } |
1706 | + | |
1707 | +#if defined(CONFIG_SLIRP) | |
1708 | + /* XXX: merge with poll() */ | |
1709 | + if (slirp_inited) { | |
1710 | + fd_set rfds, wfds, xfds; | |
1711 | + int nfds; | |
1712 | + struct timeval tv; | |
1713 | + | |
1714 | + nfds = -1; | |
1715 | + FD_ZERO(&rfds); | |
1716 | + FD_ZERO(&wfds); | |
1717 | + FD_ZERO(&xfds); | |
1718 | + slirp_select_fill(&nfds, &rfds, &wfds, &xfds); | |
1719 | + tv.tv_sec = 0; | |
1720 | + tv.tv_usec = 0; | |
1721 | + ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv); | |
1722 | + if (ret >= 0) { | |
1723 | + slirp_select_poll(&rfds, &wfds, &xfds); | |
1724 | + } | |
1725 | + } | |
1726 | +#endif | |
1727 | + | |
1600 | 1728 | #endif |
1601 | 1729 | |
1602 | 1730 | if (vm_running) { |
... | ... | @@ -1636,10 +1764,14 @@ void help(void) |
1636 | 1764 | "-nographic disable graphical output and redirect serial I/Os to console\n" |
1637 | 1765 | "\n" |
1638 | 1766 | "Network options:\n" |
1639 | - "-n script set network init script [default=%s]\n" | |
1640 | - "-nics n simulate 'n' network interfaces [default=1]\n" | |
1767 | + "-nics n simulate 'n' network cards [default=1]\n" | |
1641 | 1768 | "-macaddr addr set the mac address of the first interface\n" |
1642 | - "-tun-fd fd0[,...] use these fds as already opened tap/tun interfaces\n" | |
1769 | + "-n script set tap/tun network init script [default=%s]\n" | |
1770 | + "-tun-fd fd use this fd as already opened tap/tun interface\n" | |
1771 | +#ifdef CONFIG_SLIRP | |
1772 | + "-user-net use user mode network stack [default if no tap/tun script]\n" | |
1773 | +#endif | |
1774 | + "-dummy-net use dummy network stack\n" | |
1643 | 1775 | "\n" |
1644 | 1776 | "Linux boot specific:\n" |
1645 | 1777 | "-kernel bzImage use 'bzImage' as kernel image\n" |
... | ... | @@ -1695,6 +1827,8 @@ struct option long_options[] = { |
1695 | 1827 | { "no-code-copy", 0, NULL, 0 }, |
1696 | 1828 | { "nics", 1, NULL, 0 }, |
1697 | 1829 | { "macaddr", 1, NULL, 0 }, |
1830 | + { "user-net", 1, NULL, 0 }, | |
1831 | + { "dummy-net", 1, NULL, 0 }, | |
1698 | 1832 | { NULL, 0, NULL, 0 }, |
1699 | 1833 | }; |
1700 | 1834 | |
... | ... | @@ -1707,6 +1841,10 @@ static uint8_t *signal_stack; |
1707 | 1841 | |
1708 | 1842 | #endif |
1709 | 1843 | |
1844 | +#define NET_IF_TUN 0 | |
1845 | +#define NET_IF_USER 1 | |
1846 | +#define NET_IF_DUMMY 2 | |
1847 | + | |
1710 | 1848 | int main(int argc, char **argv) |
1711 | 1849 | { |
1712 | 1850 | #ifdef CONFIG_GDBSTUB |
... | ... | @@ -1722,7 +1860,8 @@ int main(int argc, char **argv) |
1722 | 1860 | int cyls, heads, secs; |
1723 | 1861 | int start_emulation = 1; |
1724 | 1862 | uint8_t macaddr[6]; |
1725 | - | |
1863 | + int net_if_type, nb_tun_fds, tun_fds[MAX_NICS]; | |
1864 | + | |
1726 | 1865 | #if !defined(CONFIG_SOFTMMU) |
1727 | 1866 | /* we never want that malloc() uses mmap() */ |
1728 | 1867 | mallopt(M_MMAP_THRESHOLD, 4096 * 1024); |
... | ... | @@ -1746,6 +1885,8 @@ int main(int argc, char **argv) |
1746 | 1885 | has_cdrom = 1; |
1747 | 1886 | cyls = heads = secs = 0; |
1748 | 1887 | |
1888 | + nb_tun_fds = 0; | |
1889 | + net_if_type = -1; | |
1749 | 1890 | nb_nics = 1; |
1750 | 1891 | /* default mac address of the first network interface */ |
1751 | 1892 | macaddr[0] = 0x52; |
... | ... | @@ -1754,10 +1895,8 @@ int main(int argc, char **argv) |
1754 | 1895 | macaddr[3] = 0x12; |
1755 | 1896 | macaddr[4] = 0x34; |
1756 | 1897 | macaddr[5] = 0x56; |
1757 | - | |
1758 | - for(i = 0; i < MAX_NICS; i++) | |
1759 | - nd_table[i].fd = -1; | |
1760 | - | |
1898 | + | |
1899 | + | |
1761 | 1900 | for(;;) { |
1762 | 1901 | c = getopt_long_only(argc, argv, "hm:d:n:sp:L:S", long_options, &long_index); |
1763 | 1902 | if (c == -1) |
... | ... | @@ -1809,23 +1948,13 @@ int main(int argc, char **argv) |
1809 | 1948 | { |
1810 | 1949 | const char *p; |
1811 | 1950 | int fd; |
1812 | - p = optarg; | |
1813 | - nb_nics = 0; | |
1814 | - for(;;) { | |
1815 | - fd = strtol(p, (char **)&p, 0); | |
1816 | - nd_table[nb_nics].fd = fd; | |
1817 | - snprintf(nd_table[nb_nics].ifname, | |
1818 | - sizeof(nd_table[nb_nics].ifname), | |
1819 | - "fd%d", nb_nics); | |
1820 | - nb_nics++; | |
1821 | - if (*p == ',') { | |
1822 | - p++; | |
1823 | - } else if (*p != '\0') { | |
1824 | - fprintf(stderr, "qemu: invalid fd for network interface %d\n", nb_nics); | |
1951 | + if (nb_tun_fds < MAX_NICS) { | |
1952 | + fd = strtol(optarg, (char **)&p, 0); | |
1953 | + if (*p != '\0') { | |
1954 | + fprintf(stderr, "qemu: invalid fd for network interface %d\n", nb_tun_fds); | |
1825 | 1955 | exit(1); |
1826 | - } else { | |
1827 | - break; | |
1828 | 1956 | } |
1957 | + tun_fds[nb_tun_fds++] = fd; | |
1829 | 1958 | } |
1830 | 1959 | } |
1831 | 1960 | break; |
... | ... | @@ -1885,6 +2014,12 @@ int main(int argc, char **argv) |
1885 | 2014 | } |
1886 | 2015 | } |
1887 | 2016 | break; |
2017 | + case 18: | |
2018 | + net_if_type = NET_IF_USER; | |
2019 | + break; | |
2020 | + case 19: | |
2021 | + net_if_type = NET_IF_DUMMY; | |
2022 | + break; | |
1888 | 2023 | } |
1889 | 2024 | break; |
1890 | 2025 | case 'h': |
... | ... | @@ -1965,8 +2100,18 @@ int main(int argc, char **argv) |
1965 | 2100 | #endif |
1966 | 2101 | |
1967 | 2102 | /* init host network redirectors */ |
1968 | - for(i = 0; i < MAX_NICS; i++) { | |
2103 | + if (net_if_type == -1) { | |
2104 | + net_if_type = NET_IF_TUN; | |
2105 | +#if defined(CONFIG_SLIRP) | |
2106 | + if (access(network_script, R_OK) < 0) { | |
2107 | + net_if_type = NET_IF_USER; | |
2108 | + } | |
2109 | +#endif | |
2110 | + } | |
2111 | + | |
2112 | + for(i = 0; i < nb_nics; i++) { | |
1969 | 2113 | NetDriverState *nd = &nd_table[i]; |
2114 | + nd->index = i; | |
1970 | 2115 | /* init virtual mac address */ |
1971 | 2116 | nd->macaddr[0] = macaddr[0]; |
1972 | 2117 | nd->macaddr[1] = macaddr[1]; |
... | ... | @@ -1974,8 +2119,27 @@ int main(int argc, char **argv) |
1974 | 2119 | nd->macaddr[3] = macaddr[3]; |
1975 | 2120 | nd->macaddr[4] = macaddr[4]; |
1976 | 2121 | nd->macaddr[5] = macaddr[5] + i; |
2122 | + switch(net_if_type) { | |
2123 | +#if defined(CONFIG_SLIRP) | |
2124 | + case NET_IF_USER: | |
2125 | + net_slirp_init(nd); | |
2126 | + break; | |
2127 | +#endif | |
2128 | +#if !defined(_WIN32) | |
2129 | + case NET_IF_TUN: | |
2130 | + if (i < nb_tun_fds) { | |
2131 | + net_fd_init(nd, tun_fds[i]); | |
2132 | + } else { | |
2133 | + net_tun_init(nd); | |
2134 | + } | |
2135 | + break; | |
2136 | +#endif | |
2137 | + case NET_IF_DUMMY: | |
2138 | + default: | |
2139 | + net_dummy_init(nd); | |
2140 | + break; | |
2141 | + } | |
1977 | 2142 | } |
1978 | - net_init(); | |
1979 | 2143 | |
1980 | 2144 | /* init the memory */ |
1981 | 2145 | phys_ram_size = ram_size + vga_ram_size; |
... | ... | @@ -2058,7 +2222,7 @@ int main(int argc, char **argv) |
2058 | 2222 | } |
2059 | 2223 | if (fd_filename[i] != '\0') { |
2060 | 2224 | if (bdrv_open(fd_table[i], fd_filename[i], snapshot) < 0) { |
2061 | - fprintf(stderr, "qemu: could not open floppy disk image '%s\n", | |
2225 | + fprintf(stderr, "qemu: could not open floppy disk image '%s'\n", | |
2062 | 2226 | fd_filename[i]); |
2063 | 2227 | exit(1); |
2064 | 2228 | } | ... | ... |
vl.h
... | ... | @@ -132,29 +132,39 @@ void qemu_del_vm_stop_handler(VMStopHandler *cb, void *opaque); |
132 | 132 | void vm_start(void); |
133 | 133 | void vm_stop(int reason); |
134 | 134 | |
135 | +/* async I/O support */ | |
136 | + | |
137 | +typedef void IOReadHandler(void *opaque, const uint8_t *buf, int size); | |
138 | +typedef int IOCanRWHandler(void *opaque); | |
139 | + | |
140 | +int qemu_add_fd_read_handler(int fd, IOCanRWHandler *fd_can_read, | |
141 | + IOReadHandler *fd_read, void *opaque); | |
142 | +void qemu_del_fd_read_handler(int fd); | |
143 | + | |
135 | 144 | /* network redirectors support */ |
136 | 145 | |
137 | 146 | #define MAX_NICS 8 |
138 | 147 | |
139 | 148 | typedef struct NetDriverState { |
140 | - int fd; | |
149 | + int index; /* index number in QEMU */ | |
141 | 150 | uint8_t macaddr[6]; |
142 | 151 | char ifname[16]; |
152 | + void (*send_packet)(struct NetDriverState *nd, | |
153 | + const uint8_t *buf, int size); | |
154 | + void (*add_read_packet)(struct NetDriverState *nd, | |
155 | + IOCanRWHandler *fd_can_read, | |
156 | + IOReadHandler *fd_read, void *opaque); | |
157 | + /* tun specific data */ | |
158 | + int fd; | |
159 | + /* slirp specific data */ | |
143 | 160 | } NetDriverState; |
144 | 161 | |
145 | 162 | extern int nb_nics; |
146 | 163 | extern NetDriverState nd_table[MAX_NICS]; |
147 | 164 | |
148 | -void net_send_packet(NetDriverState *nd, const uint8_t *buf, int size); | |
149 | - | |
150 | -/* async I/O support */ | |
151 | - | |
152 | -typedef void IOReadHandler(void *opaque, const uint8_t *buf, int size); | |
153 | -typedef int IOCanRWHandler(void *opaque); | |
154 | - | |
155 | -int qemu_add_fd_read_handler(int fd, IOCanRWHandler *fd_can_read, | |
156 | - IOReadHandler *fd_read, void *opaque); | |
157 | -void qemu_del_fd_read_handler(int fd); | |
165 | +void qemu_send_packet(NetDriverState *nd, const uint8_t *buf, int size); | |
166 | +void qemu_add_read_packet(NetDriverState *nd, IOCanRWHandler *fd_can_read, | |
167 | + IOReadHandler *fd_read, void *opaque); | |
158 | 168 | |
159 | 169 | /* timers */ |
160 | 170 | |
... | ... | @@ -417,6 +427,7 @@ void serial_receive_break(SerialState *s); |
417 | 427 | void pic_set_irq(int irq, int level); |
418 | 428 | void pic_init(void); |
419 | 429 | uint32_t pic_intack_read(CPUState *env); |
430 | +void pic_info(void); | |
420 | 431 | |
421 | 432 | /* i8254.c */ |
422 | 433 | ... | ... |