diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/um | |
download | mrst-s0i3-test-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.gz mrst-s0i3-test-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.xz mrst-s0i3-test-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.zip |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/um')
295 files changed, 35908 insertions, 0 deletions
diff --git a/arch/um/Kconfig b/arch/um/Kconfig new file mode 100644 index 00000000000..9a23df18212 --- /dev/null +++ b/arch/um/Kconfig @@ -0,0 +1,320 @@ +# UML uses the generic IRQ sugsystem +config GENERIC_HARDIRQS + bool + default y + +config UML + bool + default y + +# XXX: does UM have a mmu/swap? +config MMU + bool + default y + +mainmenu "Linux/Usermode Kernel Configuration" + +config ISA + bool + +config SBUS + bool + +config PCI + bool + +config UID16 + bool + default y + +config RWSEM_GENERIC_SPINLOCK + bool + default y + +config GENERIC_CALIBRATE_DELAY + bool + default y + +menu "UML-specific options" + +config MODE_TT + bool "Tracing thread support" + default y + help + This option controls whether tracing thread support is compiled + into UML. Normally, this should be set to Y. If you intend to + use only skas mode (and the host has the skas patch applied to it), + then it is OK to say N here. + +config STATIC_LINK + bool "Force a static link" + default n + depends on !MODE_TT + help + If CONFIG_MODE_TT is disabled, then this option gives you the ability + to force a static link of UML. Normally, if only skas mode is built + in to UML, it will be linked as a shared binary. This is inconvenient + for use in a chroot jail. So, if you intend to run UML inside a + chroot, and you disable CONFIG_MODE_TT, you probably want to say Y + here. + +config MODE_SKAS + bool "Separate Kernel Address Space support" + default y + help + This option controls whether skas (separate kernel address space) + support is compiled in. If you have applied the skas patch to the + host, then you certainly want to say Y here (and consider saying N + to CONFIG_MODE_TT). Otherwise, it is safe to say Y. Disabling this + option will shrink the UML binary slightly. + +source "arch/um/Kconfig_arch" + +config LD_SCRIPT_STATIC + bool + default y + depends on MODE_TT || STATIC_LINK + +config LD_SCRIPT_DYN + bool + default y + depends on !LD_SCRIPT_STATIC + +config NET + bool "Networking support" + help + Unless you really know what you are doing, you should say Y here. + The reason is that some programs need kernel networking support even + when running on a stand-alone machine that isn't connected to any + other computer. If you are upgrading from an older kernel, you + should consider updating your networking tools too because changes + in the kernel and the tools often go hand in hand. The tools are + contained in the package net-tools, the location and version number + of which are given in <file:Documentation/Changes>. + + For a general introduction to Linux networking, it is highly + recommended to read the NET-HOWTO, available from + <http://www.tldp.org/docs.html#howto>. + + +source "fs/Kconfig.binfmt" + +config HOSTFS + tristate "Host filesystem" + help + While the User-Mode Linux port uses its own root file system for + booting and normal file access, this module lets the UML user + access files stored on the host. It does not require any + network connection between the Host and UML. An example use of + this might be: + + mount none /tmp/fromhost -t hostfs -o /tmp/umlshare + + where /tmp/fromhost is an empty directory inside UML and + /tmp/umlshare is a directory on the host with files the UML user + wishes to access. + + For more information, see + <http://user-mode-linux.sourceforge.net/hostfs.html>. + + If you'd like to be able to work with files stored on the host, + say Y or M here; otherwise say N. + +config HPPFS + tristate "HoneyPot ProcFS (EXPERIMENTAL)" + depends on BROKEN + help + hppfs (HoneyPot ProcFS) is a filesystem which allows UML /proc + entries to be overridden, removed, or fabricated from the host. + Its purpose is to allow a UML to appear to be a physical machine + by removing or changing anything in /proc which gives away the + identity of a UML. + + See <http://user-mode-linux.sf.net/hppfs.html> for more information. + + You only need this if you are setting up a UML honeypot. Otherwise, + it is safe to say 'N' here. + + If you are actively using it, please ask for it to be fixed. In this + moment, it does not work on 2.6 (it works somehow on 2.4). + +config MCONSOLE + bool "Management console" + default y + help + The user mode linux management console is a low-level interface to + the kernel, somewhat like the i386 SysRq interface. Since there is + a full-blown operating system running under every user mode linux + instance, there is much greater flexibility possible than with the + SysRq mechanism. + + If you answer 'Y' to this option, to use this feature, you need the + mconsole client (called uml_mconsole) which is present in CVS in + 2.4.5-9um and later (path /tools/mconsole), and is also in the + distribution RPM package in 2.4.6 and later. + + It is safe to say 'Y' here. + +config MAGIC_SYSRQ + bool "Magic SysRq key" + depends on MCONSOLE + ---help--- + If you say Y here, you will have some control over the system even + if the system crashes for example during kernel debugging (e.g., you + will be able to flush the buffer cache to disk, reboot the system + immediately or dump some status information). A key for each of the + possible requests is provided. + + This is the feature normally accomplished by pressing a key + while holding SysRq (Alt+PrintScreen). + + On UML, this is accomplished by sending a "sysrq" command with + mconsole, followed by the letter for the requested command. + + The keys are documented in <file:Documentation/sysrq.txt>. Don't say Y + unless you really know what this hack does. + +config HOST_2G_2G + bool "2G/2G host address space split" + default n + help + This is needed when the host on which you run has a 2G/2G memory + split, instead of the customary 3G/1G. + + Note that to enable such a host + configuration, which makes sense only in some cases, you need special + host patches. + + So, if you do not know what to do here, say 'N'. + +config SMP + bool "Symmetric multi-processing support (EXPERIMENTAL)" + default n + depends on MODE_TT && EXPERIMENTAL + help + This option enables UML SMP support. + It is NOT related to having a real SMP box. Not directly, at least. + + UML implements virtual SMP by allowing as many processes to run + simultaneously on the host as there are virtual processors configured. + + Obviously, if the host is a uniprocessor, those processes will + timeshare, but, inside UML, will appear to be running simultaneously. + If the host is a multiprocessor, then UML processes may run + simultaneously, depending on the host scheduler. + + This, however, is supported only in TT mode. So, if you use the SKAS + patch on your host, switching to TT mode and enabling SMP usually gives + you worse performances. + Also, since the support for SMP has been under-developed, there could + be some bugs being exposed by enabling SMP. + + If you don't know what to do, say N. + +config NR_CPUS + int "Maximum number of CPUs (2-32)" + range 2 32 + depends on SMP + default "32" + +config NEST_LEVEL + int "Nesting level" + default "0" + help + This is set to the number of layers of UMLs that this UML will be run + in. Normally, this is zero, meaning that it will run directly on the + host. Setting it to one will build a UML that can run inside a UML + that is running on the host. Generally, if you intend this UML to run + inside another UML, set CONFIG_NEST_LEVEL to one more than the host + UML. + + Note that if the hosting UML has its CONFIG_KERNEL_HALF_GIGS set to + greater than one, then the guest UML should have its CONFIG_NEST_LEVEL + set to the host's CONFIG_NEST_LEVEL + CONFIG_KERNEL_HALF_GIGS. + Only change this if you are running nested UMLs. + +config KERNEL_HALF_GIGS + int "Kernel address space size (in .5G units)" + default "1" + help + This determines the amount of address space that UML will allocate for + its own, measured in half Gigabyte units. The default is 1. + Change this only if you need to boot UML with an unusually large amount + of physical memory. + +config HIGHMEM + bool "Highmem support" + +config KERNEL_STACK_ORDER + int "Kernel stack size order" + default 2 + help + This option determines the size of UML kernel stacks. They will + be 1 << order pages. The default is OK unless you're running Valgrind + on UML, in which case, set this to 3. + +config UML_REAL_TIME_CLOCK + bool "Real-time Clock" + default y + help + This option makes UML time deltas match wall clock deltas. This should + normally be enabled. The exception would be if you are debugging with + UML and spend long times with UML stopped at a breakpoint. In this + case, when UML is restarted, it will call the timer enough times to make + up for the time spent at the breakpoint. This could result in a + noticable lag. If this is a problem, then disable this option. + +endmenu + +source "init/Kconfig" + +source "drivers/base/Kconfig" + +source "arch/um/Kconfig_char" + +source "drivers/block/Kconfig" + +config NETDEVICES + bool + default NET + +source "arch/um/Kconfig_net" + +source "net/Kconfig" + +source "fs/Kconfig" + +source "security/Kconfig" + +source "crypto/Kconfig" + +source "lib/Kconfig" + +menu "SCSI support" +depends on BROKEN + +config SCSI + tristate "SCSI support" + +# This gives us free_dma, which scsi.c wants. +config GENERIC_ISA_DMA + bool + depends on SCSI + default y + +source "arch/um/Kconfig_scsi" + +endmenu + +source "drivers/md/Kconfig" + +if BROKEN + source "drivers/mtd/Kconfig" +endif + +#This is just to shut up some Kconfig warnings, so no prompt. +config INPUT + bool + default n + +source "arch/um/Kconfig.debug" diff --git a/arch/um/Kconfig.debug b/arch/um/Kconfig.debug new file mode 100644 index 00000000000..b89989de364 --- /dev/null +++ b/arch/um/Kconfig.debug @@ -0,0 +1,53 @@ +menu "Kernel hacking" + +source "lib/Kconfig.debug" + +config FRAME_POINTER + bool + default y if DEBUG_INFO + +config PT_PROXY + bool "Enable ptrace proxy" + depends on XTERM_CHAN && DEBUG_INFO && MODE_TT + help + This option enables a debugging interface which allows gdb to debug + the kernel without needing to actually attach to kernel threads. + If you want to do kernel debugging, say Y here; otherwise say N. + +config GPROF + bool "Enable gprof support" + depends on DEBUG_INFO && MODE_SKAS && !MODE_TT + help + This allows profiling of a User-Mode Linux kernel with the gprof + utility. + + See <http://user-mode-linux.sourceforge.net/gprof.html> for more + details. + + If you're involved in UML kernel development and want to use gprof, + say Y. If you're unsure, say N. + +config GCOV + bool "Enable gcov support" + depends on DEBUG_INFO && MODE_SKAS + help + This option allows developers to retrieve coverage data from a UML + session. + + See <http://user-mode-linux.sourceforge.net/gprof.html> for more + details. + + If you're involved in UML kernel development and want to use gcov, + say Y. If you're unsure, say N. + +config SYSCALL_DEBUG + bool "Enable system call debugging" + default N + depends on DEBUG_INFO + help + This adds some system debugging to UML, including keeping a ring buffer + with recent system calls and some global and per-task statistics. + + If unsure, say N + +endmenu diff --git a/arch/um/Kconfig_char b/arch/um/Kconfig_char new file mode 100644 index 00000000000..3e50fdb6762 --- /dev/null +++ b/arch/um/Kconfig_char @@ -0,0 +1,208 @@ + +menu "Character Devices" + +config STDERR_CONSOLE + bool "stderr console" + default y + help + console driver which dumps all printk messages to stderr. + +config STDIO_CONSOLE + bool + default y + +config SSL + bool "Virtual serial line" + help + The User-Mode Linux environment allows you to create virtual serial + lines on the UML that are usually made to show up on the host as + ttys or ptys. + + See <http://user-mode-linux.sourceforge.net/input.html> for more + information and command line examples of how to use this facility. + + Unless you have a specific reason for disabling this, say Y. + +config NULL_CHAN + bool "null channel support" + help + This option enables support for attaching UML consoles and serial + lines to a device similar to /dev/null. Data written to it disappears + and there is never any data to be read. + +config PORT_CHAN + bool "port channel support" + help + This option enables support for attaching UML consoles and serial + lines to host portals. They may be accessed with 'telnet <host> + <port number>'. Any number of consoles and serial lines may be + attached to a single portal, although what UML device you get when + you telnet to that portal will be unpredictable. + It is safe to say 'Y' here. + +config PTY_CHAN + bool "pty channel support" + help + This option enables support for attaching UML consoles and serial + lines to host pseudo-terminals. Access to both traditional + pseudo-terminals (/dev/pty*) and pts pseudo-terminals are controlled + with this option. The assignment of UML devices to host devices + will be announced in the kernel message log. + It is safe to say 'Y' here. + +config TTY_CHAN + bool "tty channel support" + help + This option enables support for attaching UML consoles and serial + lines to host terminals. Access to both virtual consoles + (/dev/tty*) and the slave side of pseudo-terminals (/dev/ttyp* and + /dev/pts/*) are controlled by this option. + It is safe to say 'Y' here. + +config XTERM_CHAN + bool "xterm channel support" + help + This option enables support for attaching UML consoles and serial + lines to xterms. Each UML device so assigned will be brought up in + its own xterm. + If you disable this option, then CONFIG_PT_PROXY will be disabled as + well, since UML's gdb currently requires an xterm. + It is safe to say 'Y' here. + +config NOCONFIG_CHAN + bool + default !(XTERM_CHAN && TTY_CHAN && PTY_CHAN && PORT_CHAN && NULL_CHAN) + +config CON_ZERO_CHAN + string "Default main console channel initialization" + default "fd:0,fd:1" + help + This is the string describing the channel to which the main console + will be attached by default. This value can be overridden from the + command line. The default value is "fd:0,fd:1", which attaches the + main console to stdin and stdout. + It is safe to leave this unchanged. + +config CON_CHAN + string "Default console channel initialization" + default "xterm" + help + This is the string describing the channel to which all consoles + except the main console will be attached by default. This value can + be overridden from the command line. The default value is "xterm", + which brings them up in xterms. + It is safe to leave this unchanged, although you may wish to change + this if you expect the UML that you build to be run in environments + which don't have X or xterm available. + +config SSL_CHAN + string "Default serial line channel initialization" + default "pty" + help + This is the string describing the channel to which the serial lines + will be attached by default. This value can be overridden from the + command line. The default value is "pty", which attaches them to + traditional pseudo-terminals. + It is safe to leave this unchanged, although you may wish to change + this if you expect the UML that you build to be run in environments + which don't have a set of /dev/pty* devices. + +config UNIX98_PTYS + bool "Unix98 PTY support" + ---help--- + A pseudo terminal (PTY) is a software device consisting of two + halves: a master and a slave. The slave device behaves identical to + a physical terminal; the master device is used by a process to + read data from and write data to the slave, thereby emulating a + terminal. Typical programs for the master side are telnet servers + and xterms. + + Linux has traditionally used the BSD-like names /dev/ptyxx for + masters and /dev/ttyxx for slaves of pseudo terminals. This scheme + has a number of problems. The GNU C library glibc 2.1 and later, + however, supports the Unix98 naming standard: in order to acquire a + pseudo terminal, a process opens /dev/ptmx; the number of the pseudo + terminal is then made available to the process and the pseudo + terminal slave can be accessed as /dev/pts/<number>. What was + traditionally /dev/ttyp2 will then be /dev/pts/2, for example. + + All modern Linux systems use the Unix98 ptys. Say Y unless + you're on an embedded system and want to conserve memory. + +config LEGACY_PTYS + bool "Legacy (BSD) PTY support" + default y + ---help--- + A pseudo terminal (PTY) is a software device consisting of two + halves: a master and a slave. The slave device behaves identical to + a physical terminal; the master device is used by a process to + read data from and write data to the slave, thereby emulating a + terminal. Typical programs for the master side are telnet servers + and xterms. + + Linux has traditionally used the BSD-like names /dev/ptyxx + for masters and /dev/ttyxx for slaves of pseudo + terminals. This scheme has a number of problems, including + security. This option enables these legacy devices; on most + systems, it is safe to say N. + + +config LEGACY_PTY_COUNT + int "Maximum number of legacy PTY in use" + depends on LEGACY_PTYS + default "256" + ---help--- + The maximum number of legacy PTYs that can be used at any one time. + The default is 256, and should be more than enough. Embedded + systems may want to reduce this to save memory. + + When not in use, each legacy PTY occupies 12 bytes on 32-bit + architectures and 24 bytes on 64-bit architectures. + +config WATCHDOG + bool "Watchdog Timer Support" + +config WATCHDOG_NOWAYOUT + bool "Disable watchdog shutdown on close" + depends on WATCHDOG + +config SOFT_WATCHDOG + tristate "Software Watchdog" + depends on WATCHDOG + +config UML_WATCHDOG + tristate "UML watchdog" + depends on WATCHDOG + +config UML_SOUND + tristate "Sound support" + help + This option enables UML sound support. If enabled, it will pull in + soundcore and the UML hostaudio relay, which acts as a intermediary + between the host's dsp and mixer devices and the UML sound system. + It is safe to say 'Y' here. + +config SOUND + tristate + default UML_SOUND + +config HOSTAUDIO + tristate + default UML_SOUND + +config UML_RANDOM + tristate "Hardware random number generator" + help + This option enables UML's "hardware" random number generator. It + attaches itself to the host's /dev/random, supplying as much entropy + as the host has, rather than the small amount the UML gets from its + own drivers. It registers itself as a standard hardware random number + generator, major 10, minor 183, and the canonical device name is + /dev/hwrng. + The way to make use of this is to install the rng-tools package + (check your distro, or download from + http://sourceforge.net/projects/gkernel/). rngd periodically reads + /dev/hwrng and injects the entropy into /dev/random. + +endmenu + diff --git a/arch/um/Kconfig_i386 b/arch/um/Kconfig_i386 new file mode 100644 index 00000000000..203c242201b --- /dev/null +++ b/arch/um/Kconfig_i386 @@ -0,0 +1,24 @@ +config 64_BIT + bool + default n + +config TOP_ADDR + hex + default 0xc0000000 if !HOST_2G_2G + default 0x80000000 if HOST_2G_2G + +config 3_LEVEL_PGTABLES + bool "Three-level pagetables" + default n + help + Three-level pagetables will let UML have more than 4G of physical + memory. All the memory that can't be mapped directly will be treated + as high memory. + +config ARCH_HAS_SC_SIGNALS + bool + default y + +config ARCH_REUSE_HOST_VSYSCALL_AREA + bool + default y diff --git a/arch/um/Kconfig_net b/arch/um/Kconfig_net new file mode 100644 index 00000000000..1c2f9a70d91 --- /dev/null +++ b/arch/um/Kconfig_net @@ -0,0 +1,180 @@ + +menu "UML Network Devices" + depends on NET + +# UML virtual driver +config UML_NET + bool "Virtual network device" + help + While the User-Mode port cannot directly talk to any physical + hardware devices, this choice and the following transport options + provide one or more virtual network devices through which the UML + kernels can talk to each other, the host, and with the host's help, + machines on the outside world. + + For more information, including explanations of the networking and + sample configurations, see + <http://user-mode-linux.sourceforge.net/networking.html>. + + If you'd like to be able to enable networking in the User-Mode + linux environment, say Y; otherwise say N. Note that you must + enable at least one of the following transport options to actually + make use of UML networking. + +config UML_NET_ETHERTAP + bool "Ethertap transport" + depends on UML_NET + help + The Ethertap User-Mode Linux network transport allows a single + running UML to exchange packets with its host over one of the + host's Ethertap devices, such as /dev/tap0. Additional running + UMLs can use additional Ethertap devices, one per running UML. + While the UML believes it's on a (multi-device, broadcast) virtual + Ethernet network, it's in fact communicating over a point-to-point + link with the host. + + To use this, your host kernel must have support for Ethertap + devices. Also, if your host kernel is 2.4.x, it must have + CONFIG_NETLINK_DEV configured as Y or M. + + For more information, see + <http://user-mode-linux.sourceforge.net/networking.html> That site + has examples of the UML command line to use to enable Ethertap + networking. + + If you'd like to set up an IP network with the host and/or the + outside world, say Y to this, the Daemon Transport and/or the + Slip Transport. You'll need at least one of them, but may choose + more than one without conflict. If you don't need UML networking, + say N. + +config UML_NET_TUNTAP + bool "TUN/TAP transport" + depends on UML_NET + help + The UML TUN/TAP network transport allows a UML instance to exchange + packets with the host over a TUN/TAP device. This option will only + work with a 2.4 host, unless you've applied the TUN/TAP patch to + your 2.2 host kernel. + + To use this transport, your host kernel must have support for TUN/TAP + devices, either built-in or as a module. + +config UML_NET_SLIP + bool "SLIP transport" + depends on UML_NET + help + The slip User-Mode Linux network transport allows a running UML to + network with its host over a point-to-point link. Unlike Ethertap, + which can carry any Ethernet frame (and hence even non-IP packets), + the slip transport can only carry IP packets. + + To use this, your host must support slip devices. + + For more information, see + <http://user-mode-linux.sourceforge.net/networking.html>. That site + has examples of the UML command line to use to enable slip + networking, and details of a few quirks with it. + + The Ethertap Transport is preferred over slip because of its + limitations. If you prefer slip, however, say Y here. Otherwise + choose the Multicast transport (to network multiple UMLs on + multiple hosts), Ethertap (to network with the host and the + outside world), and/or the Daemon transport (to network multiple + UMLs on a single host). You may choose more than one without + conflict. If you don't need UML networking, say N. + +config UML_NET_DAEMON + bool "Daemon transport" + depends on UML_NET + help + This User-Mode Linux network transport allows one or more running + UMLs on a single host to communicate with each other, but not to + the host. + + To use this form of networking, you'll need to run the UML + networking daemon on the host. + + For more information, see + <http://user-mode-linux.sourceforge.net/networking.html> That site + has examples of the UML command line to use to enable Daemon + networking. + + If you'd like to set up a network with other UMLs on a single host, + say Y. If you need a network between UMLs on multiple physical + hosts, choose the Multicast Transport. To set up a network with + the host and/or other IP machines, say Y to the Ethertap or Slip + transports. You'll need at least one of them, but may choose + more than one without conflict. If you don't need UML networking, + say N. + +config UML_NET_MCAST + bool "Multicast transport" + depends on UML_NET + help + This Multicast User-Mode Linux network transport allows multiple + UMLs (even ones running on different host machines!) to talk to + each other over a virtual ethernet network. However, it requires + at least one UML with one of the other transports to act as a + bridge if any of them need to be able to talk to their hosts or any + other IP machines. + + To use this, your host kernel(s) must support IP Multicasting. + + For more information, see + <http://user-mode-linux.sourceforge.net/networking.html> That site + has examples of the UML command line to use to enable Multicast + networking, and notes about the security of this approach. + + If you need UMLs on multiple physical hosts to communicate as if + they shared an Ethernet network, say Y. If you need to communicate + with other IP machines, make sure you select one of the other + transports (possibly in addition to Multicast; they're not + exclusive). If you don't need to network UMLs say N to each of + the transports. + +config UML_NET_PCAP + bool "pcap transport" + depends on UML_NET && BROKEN + help + The pcap transport makes a pcap packet stream on the host look + like an ethernet device inside UML. This is useful for making + UML act as a network monitor for the host. You must have libcap + installed in order to build the pcap transport into UML. + + For more information, see + <http://user-mode-linux.sourceforge.net/networking.html> That site + has examples of the UML command line to use to enable this option. + + If you intend to use UML as a network monitor for the host, say + Y here. Otherwise, say N. + +config UML_NET_SLIRP + bool "SLiRP transport" + depends on UML_NET + help + The SLiRP User-Mode Linux network transport allows a running UML + to network by invoking a program that can handle SLIP encapsulated + packets. This is commonly (but not limited to) the application + known as SLiRP, a program that can re-socket IP packets back onto + the host on which it is run. Only IP packets are supported, + unlike other network transports that can handle all Ethernet + frames. In general, slirp allows the UML the same IP connectivity + to the outside world that the host user is permitted, and unlike + other transports, SLiRP works without the need of root level + privleges, setuid binaries, or SLIP devices on the host. This + also means not every type of connection is possible, but most + situations can be accomodated with carefully crafted slirp + commands that can be passed along as part of the network device's + setup string. The effect of this transport on the UML is similar + that of a host behind a firewall that masquerades all network + connections passing through it (but is less secure). + + To use this you should first have slirp compiled somewhere + accessible on the host, and have read its documentation. If you + don't need UML networking, say N. + + Startup example: "eth0=slirp,FE:FD:01:02:03:04,/usr/local/bin/slirp" + +endmenu + diff --git a/arch/um/Kconfig_scsi b/arch/um/Kconfig_scsi new file mode 100644 index 00000000000..c291c942b1a --- /dev/null +++ b/arch/um/Kconfig_scsi @@ -0,0 +1,58 @@ +comment "SCSI support type (disk, tape, CD-ROM)" + depends on SCSI + +config BLK_DEV_SD + tristate "SCSI disk support" + depends on SCSI + +config SD_EXTRA_DEVS + int "Maximum number of SCSI disks that can be loaded as modules" + depends on BLK_DEV_SD + default "40" + +config CHR_DEV_ST + tristate "SCSI tape support" + depends on SCSI + +config BLK_DEV_SR + tristate "SCSI CD-ROM support" + depends on SCSI + +config BLK_DEV_SR_VENDOR + bool "Enable vendor-specific extensions (for SCSI CDROM)" + depends on BLK_DEV_SR + +config SR_EXTRA_DEVS + int "Maximum number of CDROM devices that can be loaded as modules" + depends on BLK_DEV_SR + default "2" + +config CHR_DEV_SG + tristate "SCSI generic support" + depends on SCSI + +comment "Some SCSI devices (e.g. CD jukebox) support multiple LUNs" + depends on SCSI + +#if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then +config SCSI_DEBUG_QUEUES + bool "Enable extra checks in new queueing code" + depends on SCSI + +#fi +config SCSI_MULTI_LUN + bool "Probe all LUNs on each SCSI device" + depends on SCSI + +config SCSI_CONSTANTS + bool "Verbose SCSI error reporting (kernel size +=12K)" + depends on SCSI + +config SCSI_LOGGING + bool "SCSI logging facility" + depends on SCSI + +config SCSI_DEBUG + tristate "SCSI debugging host simulator (EXPERIMENTAL)" + depends on SCSI + diff --git a/arch/um/Kconfig_x86_64 b/arch/um/Kconfig_x86_64 new file mode 100644 index 00000000000..768dc6626a8 --- /dev/null +++ b/arch/um/Kconfig_x86_64 @@ -0,0 +1,15 @@ +config 64_BIT + bool + default y + +config 3_LEVEL_PGTABLES + bool + default y + +config ARCH_HAS_SC_SIGNALS + bool + default n + +config ARCH_REUSE_HOST_VSYSCALL_AREA + bool + default n diff --git a/arch/um/Makefile b/arch/um/Makefile new file mode 100644 index 00000000000..97bca6b5ca9 --- /dev/null +++ b/arch/um/Makefile @@ -0,0 +1,210 @@ +# +# Copyright (C) 2002 Jeff Dike (jdike@karaya.com) +# Licensed under the GPL +# + +ARCH_DIR := arch/um +OS := $(shell uname -s) +# We require bash because the vmlinux link and loader script cpp use bash +# features. +SHELL := /bin/bash + +filechk_gen_header = $< + +core-y += $(ARCH_DIR)/kernel/ \ + $(ARCH_DIR)/drivers/ \ + $(ARCH_DIR)/os-$(OS)/ + +# Have to precede the include because the included Makefiles reference them. +SYMLINK_HEADERS := archparam.h system.h sigcontext.h processor.h ptrace.h \ + arch-signal.h module.h vm-flags.h +SYMLINK_HEADERS := $(foreach header,$(SYMLINK_HEADERS),include/asm-um/$(header)) + +# XXX: The "os" symlink is only used by arch/um/include/os.h, which includes +# ../os/include/file.h +# +# These are cleaned up during mrproper. Please DO NOT fix it again, this is +# the Correct Thing(tm) to do! +ARCH_SYMLINKS = include/asm-um/arch $(ARCH_DIR)/include/sysdep $(ARCH_DIR)/os \ + $(SYMLINK_HEADERS) $(ARCH_DIR)/include/uml-config.h + +GEN_HEADERS += $(ARCH_DIR)/include/task.h $(ARCH_DIR)/include/kern_constants.h + +um-modes-$(CONFIG_MODE_TT) += tt +um-modes-$(CONFIG_MODE_SKAS) += skas + +MODE_INCLUDE += $(foreach mode,$(um-modes-y),\ + -I$(srctree)/$(ARCH_DIR)/kernel/$(mode)/include) + +MAKEFILES-INCL += $(foreach mode,$(um-modes-y),\ + $(srctree)/$(ARCH_DIR)/Makefile-$(mode)) + +ifneq ($(MAKEFILES-INCL),) + include $(MAKEFILES-INCL) +endif + +ARCH_INCLUDE := -I$(ARCH_DIR)/include +SYS_DIR := $(ARCH_DIR)/include/sysdep-$(SUBARCH) + +include $(srctree)/$(ARCH_DIR)/Makefile-$(SUBARCH) + +core-y += $(SUBARCH_CORE) +libs-y += $(SUBARCH_LIBS) + +# -Derrno=kernel_errno - This turns all kernel references to errno into +# kernel_errno to separate them from the libc errno. This allows -fno-common +# in CFLAGS. Otherwise, it would cause ld to complain about the two different +# errnos. + +CFLAGS += $(CFLAGS-y) -D__arch_um__ -DSUBARCH=\"$(SUBARCH)\" \ + $(ARCH_INCLUDE) $(MODE_INCLUDE) + +USER_CFLAGS := $(patsubst -I%,,$(CFLAGS)) +USER_CFLAGS := $(patsubst -D__KERNEL__,,$(USER_CFLAGS)) $(ARCH_INCLUDE) \ + $(MODE_INCLUDE) $(ARCH_USER_CFLAGS) +CFLAGS += -Derrno=kernel_errno -Dsigprocmask=kernel_sigprocmask +CFLAGS += $(call cc-option,-fno-unit-at-a-time,) + +#This will adjust *FLAGS accordingly to the platform. +include $(srctree)/$(ARCH_DIR)/Makefile-os-$(OS) + +# These are needed for clean and mrproper, since in that case .config is not +# included; the values here are meaningless + +CONFIG_NEST_LEVEL ?= 0 +CONFIG_KERNEL_HALF_GIGS ?= 0 + +SIZE = (($(CONFIG_NEST_LEVEL) + $(CONFIG_KERNEL_HALF_GIGS)) * 0x20000000) + +ifeq ($(CONFIG_MODE_SKAS), y) +$(SYS_HEADERS) : $(ARCH_DIR)/include/skas_ptregs.h +endif + +.PHONY: linux + +all: linux + +linux: vmlinux + ln -f $< $@ + +define archhelp + echo '* linux - Binary kernel image (./linux) - for backward' + echo ' compatibility only, this creates a hard link to the' + echo ' real kernel binary, the the "vmlinux" binary you' + echo ' find in the kernel root.' +endef + +$(shell cd $(ARCH_DIR) && ln -sf Kconfig_$(SUBARCH) Kconfig_arch) + +prepare: $(ARCH_SYMLINKS) $(SYS_HEADERS) $(GEN_HEADERS) \ + $(ARCH_DIR)/kernel/vmlinux.lds.S + +LINK-$(CONFIG_LD_SCRIPT_STATIC) += -static +LINK-$(CONFIG_LD_SCRIPT_DYN) += -Wl,-rpath,/lib + +LD_SCRIPT-$(CONFIG_LD_SCRIPT_STATIC) := uml.lds.S +LD_SCRIPT-$(CONFIG_LD_SCRIPT_DYN) := dyn.lds.S + +CPP_MODE-$(CONFIG_MODE_TT) := -DMODE_TT +CONFIG_KERNEL_STACK_ORDER ?= 2 +STACK_SIZE := $(shell echo $$[ 4096 * (1 << $(CONFIG_KERNEL_STACK_ORDER)) ] ) + +ifndef START + START = $$(($(TOP_ADDR) - $(SIZE))) +endif + +CPPFLAGS_vmlinux.lds = $(shell echo -U$(SUBARCH) \ + -DSTART=$(START) -DELF_ARCH=$(ELF_ARCH) \ + -DELF_FORMAT=\"$(ELF_FORMAT)\" $(CPP_MODE-y) \ + -DKERNEL_STACK_SIZE=$(STACK_SIZE)) + +#The wrappers will select whether using "malloc" or the kernel allocator. +LINK_WRAPS = -Wl,--wrap,malloc -Wl,--wrap,free -Wl,--wrap,calloc + +CFLAGS_vmlinux = $(LINK-y) $(LINK_WRAPS) +define cmd_vmlinux__ + $(CC) $(CFLAGS_vmlinux) -o $@ \ + -Wl,-T,$(vmlinux-lds) $(vmlinux-init) \ + -Wl,--start-group $(vmlinux-main) -Wl,--end-group \ + -L/usr/lib -lutil \ + $(filter-out $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) \ + FORCE ,$^) ; rm -f linux +endef + +#When cleaning we don't include .config, so we don't include +#TT or skas makefiles and don't clean skas_ptregs.h. +CLEAN_FILES += linux x.i gmon.out $(ARCH_DIR)/include/uml-config.h \ + $(GEN_HEADERS) $(ARCH_DIR)/include/skas_ptregs.h + +MRPROPER_FILES += $(SYMLINK_HEADERS) $(ARCH_SYMLINKS) \ + $(addprefix $(ARCH_DIR)/kernel/,$(KERN_SYMLINKS)) $(ARCH_DIR)/os \ + $(ARCH_DIR)/Kconfig_arch + +archclean: + $(Q)$(MAKE) $(clean)=$(ARCH_DIR)/util + @find . \( -name '*.bb' -o -name '*.bbg' -o -name '*.da' \ + -o -name '*.gcov' \) -type f -print | xargs rm -f + +#We need to re-preprocess this when the symlink dest changes. +#So we touch it when needed. +$(ARCH_DIR)/kernel/vmlinux.lds.S: FORCE + $(Q)if [ "$(shell readlink $@)" != "$(LD_SCRIPT-y)" ]; then \ + echo ' SYMLINK $@'; \ + ln -sf $(LD_SCRIPT-y) $@; \ + touch $@; \ + fi; + +$(SYMLINK_HEADERS): + @echo ' SYMLINK $@' + $(Q)cd $(TOPDIR)/$(dir $@) ; \ + ln -sf $(basename $(notdir $@))-$(SUBARCH)$(suffix $@) $(notdir $@) + +include/asm-um/arch: + @echo ' SYMLINK $@' + $(Q)cd $(TOPDIR)/include/asm-um && ln -sf ../asm-$(SUBARCH) arch + +$(ARCH_DIR)/include/sysdep: + @echo ' SYMLINK $@' + $(Q)cd $(ARCH_DIR)/include && ln -sf sysdep-$(SUBARCH) sysdep + +$(ARCH_DIR)/os: + @echo ' SYMLINK $@' + $(Q)cd $(ARCH_DIR) && ln -sf os-$(OS) os + +# Generated files +define filechk_umlconfig + sed 's/ CONFIG/ UML_CONFIG/' +endef + +$(ARCH_DIR)/include/uml-config.h : include/linux/autoconf.h + $(call filechk,umlconfig) + +$(ARCH_DIR)/include/task.h: $(ARCH_DIR)/util/mk_task + $(call filechk,gen_header) + +$(ARCH_DIR)/include/user_constants.h: $(ARCH_DIR)/os/util/mk_user_constants + $(call filechk,gen_header) + +$(ARCH_DIR)/include/kern_constants.h: $(ARCH_DIR)/util/mk_constants + $(call filechk,gen_header) + +$(ARCH_DIR)/include/skas_ptregs.h: $(ARCH_DIR)/kernel/skas/util/mk_ptregs + $(call filechk,gen_header) + +$(ARCH_DIR)/os/util/mk_user_constants: $(ARCH_DIR)/os/util FORCE ; + +$(ARCH_DIR)/util/mk_task $(ARCH_DIR)/util/mk_constants: $(ARCH_DIR)/include/user_constants.h $(ARCH_DIR)/util \ + FORCE ; + +$(ARCH_DIR)/kernel/skas/util/mk_ptregs: $(ARCH_DIR)/kernel/skas/util FORCE ; + +$(ARCH_DIR)/util: scripts_basic $(SYS_DIR)/sc.h FORCE + $(Q)$(MAKE) $(build)=$@ + +$(ARCH_DIR)/kernel/skas/util: scripts_basic FORCE + $(Q)$(MAKE) $(build)=$@ + +$(ARCH_DIR)/os/util: scripts_basic FORCE + $(Q)$(MAKE) $(build)=$@ + +export SUBARCH USER_CFLAGS OS diff --git a/arch/um/Makefile-i386 b/arch/um/Makefile-i386 new file mode 100644 index 00000000000..97b223bfa78 --- /dev/null +++ b/arch/um/Makefile-i386 @@ -0,0 +1,44 @@ +SUBARCH_CORE := arch/um/sys-i386/ + +TOP_ADDR := $(CONFIG_TOP_ADDR) + +ifeq ($(CONFIG_MODE_SKAS),y) + ifneq ($(CONFIG_MODE_TT),y) + START := 0x8048000 + endif +endif + +CFLAGS += -U__$(SUBARCH)__ -U$(SUBARCH) +ARCH_USER_CFLAGS := + +ifneq ($(CONFIG_GPROF),y) +ARCH_CFLAGS += -DUM_FASTCALL +endif + +ELF_ARCH := $(SUBARCH) +ELF_FORMAT := elf32-$(SUBARCH) + +OBJCOPYFLAGS := -O binary -R .note -R .comment -S + +SYS_UTIL_DIR := $(ARCH_DIR)/sys-i386/util + +SYS_HEADERS := $(SYS_DIR)/sc.h $(SYS_DIR)/thread.h + +prepare: $(SYS_HEADERS) + +$(SYS_DIR)/sc.h: $(SYS_UTIL_DIR)/mk_sc + $(call filechk,gen_header) + +$(SYS_DIR)/thread.h: $(SYS_UTIL_DIR)/mk_thread + $(call filechk,gen_header) + +$(SYS_UTIL_DIR)/mk_sc: scripts_basic FORCE + $(Q)$(MAKE) $(build)=$(SYS_UTIL_DIR) $@ + +$(SYS_UTIL_DIR)/mk_thread: scripts_basic $(ARCH_SYMLINKS) $(GEN_HEADERS) FORCE + $(Q)$(MAKE) $(build)=$(SYS_UTIL_DIR) $@ + +$(SYS_UTIL_DIR): scripts_basic include/asm FORCE + $(Q)$(MAKE) $(build)=$(SYS_UTIL_DIR) + +CLEAN_FILES += $(SYS_HEADERS) diff --git a/arch/um/Makefile-ia64 b/arch/um/Makefile-ia64 new file mode 100644 index 00000000000..f84dc23b0f6 --- /dev/null +++ b/arch/um/Makefile-ia64 @@ -0,0 +1 @@ +START_ADDR = 0x1000000000000000 diff --git a/arch/um/Makefile-os-Linux b/arch/um/Makefile-os-Linux new file mode 100644 index 00000000000..0c0f9a1cbba --- /dev/null +++ b/arch/um/Makefile-os-Linux @@ -0,0 +1,8 @@ +# +# Copyright (C) 2000 Jeff Dike (jdike@karaya.com) +# Licensed under the GPL +# + +# To get a definition of F_SETSIG +USER_CFLAGS += -D_GNU_SOURCE -D_LARGEFILE64_SOURCE +CFLAGS += -D_LARGEFILE64_SOURCE diff --git a/arch/um/Makefile-ppc b/arch/um/Makefile-ppc new file mode 100644 index 00000000000..66fd2003e16 --- /dev/null +++ b/arch/um/Makefile-ppc @@ -0,0 +1,9 @@ +ifeq ($(CONFIG_HOST_2G_2G), y) +START_ADDR = 0x80000000 +else +START_ADDR = 0xc0000000 +endif +ARCH_CFLAGS = -U__powerpc__ -D__UM_PPC__ + +# The arch is ppc, but the elf32 name is powerpc +ELF_SUBARCH = powerpc diff --git a/arch/um/Makefile-skas b/arch/um/Makefile-skas new file mode 100644 index 00000000000..fd18ec57227 --- /dev/null +++ b/arch/um/Makefile-skas @@ -0,0 +1,14 @@ +# +# Copyright (C) 2002 Jeff Dike (jdike@karaya.com) +# Licensed under the GPL +# + +GPROF_OPT += -pg +GCOV_OPT += -fprofile-arcs -ftest-coverage + +CFLAGS-$(CONFIG_GCOV) += $(GCOV_OPT) +CFLAGS-$(CONFIG_GPROF) += $(GPROF_OPT) +LINK-$(CONFIG_GCOV) += $(GCOV_OPT) +LINK-$(CONFIG_GPROF) += $(GPROF_OPT) + +GEN_HEADERS += $(ARCH_DIR)/include/skas_ptregs.h diff --git a/arch/um/Makefile-tt b/arch/um/Makefile-tt new file mode 100644 index 00000000000..03f7b10cfd0 --- /dev/null +++ b/arch/um/Makefile-tt @@ -0,0 +1,5 @@ +# +# Copyright (C) 2002 Jeff Dike (jdike@karaya.com) +# Licensed under the GPL +# + diff --git a/arch/um/Makefile-x86_64 b/arch/um/Makefile-x86_64 new file mode 100644 index 00000000000..a77971133e9 --- /dev/null +++ b/arch/um/Makefile-x86_64 @@ -0,0 +1,32 @@ +# Copyright 2003 - 2004 Pathscale, Inc +# Released under the GPL + +SUBARCH_LIBS := arch/um/sys-x86_64/ +START := 0x60000000 + +CFLAGS += -U__$(SUBARCH)__ -fno-builtin +ARCH_USER_CFLAGS := -D__x86_64__ + +ELF_ARCH := i386:x86-64 +ELF_FORMAT := elf64-x86-64 + +SYS_UTIL_DIR := $(ARCH_DIR)/sys-x86_64/util +SYS_DIR := $(ARCH_DIR)/include/sysdep-x86_64 + +SYS_HEADERS = $(SYS_DIR)/sc.h $(SYS_DIR)/thread.h + +prepare: $(SYS_HEADERS) + +$(SYS_DIR)/sc.h: $(SYS_UTIL_DIR)/mk_sc + $(call filechk,gen_header) + +$(SYS_DIR)/thread.h: $(SYS_UTIL_DIR)/mk_thread + $(call filechk,gen_header) + +$(SYS_UTIL_DIR)/mk_sc: scripts_basic FORCE + $(Q)$(MAKE) $(build)=$(SYS_UTIL_DIR) $@ + +$(SYS_UTIL_DIR)/mk_thread: scripts_basic $(ARCH_SYMLINKS) $(GEN_HEADERS) FORCE + $(Q)$(MAKE) $(build)=$(SYS_UTIL_DIR) $@ + +CLEAN_FILES += $(SYS_HEADERS) diff --git a/arch/um/config.release b/arch/um/config.release new file mode 100644 index 00000000000..fc68bcb9294 --- /dev/null +++ b/arch/um/config.release @@ -0,0 +1,333 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_USERMODE=y +# CONFIG_ISA is not set +# CONFIG_SBUS is not set +# CONFIG_PCI is not set +CONFIG_UID16=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# General Setup +# +CONFIG_STDIO_CONSOLE=y +CONFIG_NET=y +CONFIG_SYSVIPC=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_SYSCTL=y +CONFIG_BINFMT_AOUT=y +CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_MISC=y +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 +CONFIG_SSL=y +CONFIG_HOSTFS=y +CONFIG_MCONSOLE=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_HOST_2G_2G is not set +# CONFIG_UML_SMP is not set +# CONFIG_SMP is not set +CONFIG_CON_ZERO_CHAN="fd:0,fd:1" +CONFIG_CON_CHAN="xterm" +CONFIG_SSL_CHAN="pty" +CONFIG_NEST_LEVEL=0 +CONFIG_KERNEL_HALF_GIGS=1 + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_KMOD=y + +# +# Devices +# +CONFIG_BLK_DEV_UBD=y +# CONFIG_BLK_DEV_UBD_SYNC is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_NBD=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_INITRD=y +# CONFIG_MMAPPER is not set +CONFIG_UML_SOUND=y +CONFIG_SOUND=y +CONFIG_HOSTAUDIO=y +# CONFIG_UML_WATCHDOG is not set +# CONFIG_TTY_LOG is not set +CONFIG_FD_CHAN=y +# CONFIG_NULL_CHAN is not set +CONFIG_PORT_CHAN=y +CONFIG_PTY_CHAN=y +CONFIG_TTY_CHAN=y +CONFIG_XTERM_CHAN=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_PACKET_MMAP=y +# CONFIG_NETLINK_DEV is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set +# CONFIG_VLAN_8021Q is not set + +# +# +# +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# Network device support +# +CONFIG_UML_NET=y +CONFIG_UML_NET_ETHERTAP=y +CONFIG_UML_NET_TUNTAP=y +CONFIG_UML_NET_SLIP=y +CONFIG_UML_NET_DAEMON=y +CONFIG_UML_NET_MCAST=y +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +CONFIG_DUMMY=y +CONFIG_BONDING=m +CONFIG_EQUALIZER=m +CONFIG_TUN=y +# CONFIG_ETHERTAP is not set + +# +# Ethernet (10 or 100Mbit) +# +# CONFIG_NET_ETHERNET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +CONFIG_PLIP=m +CONFIG_PPP=m +CONFIG_PPP_MULTILINK=y +# CONFIG_PPP_FILTER is not set +# CONFIG_PPP_ASYNC is not set +CONFIG_PPP_SYNC_TTY=m +CONFIG_PPP_DEFLATE=m +CONFIG_PPP_BSDCOMP=m +CONFIG_PPPOE=m +CONFIG_SLIP=m +CONFIG_SLIP_COMPRESSED=y +CONFIG_SLIP_SMART=y +# CONFIG_SLIP_MODE_SLIP6 is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +CONFIG_SHAPER=m + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# File systems +# +CONFIG_QUOTA=y +CONFIG_AUTOFS_FS=m +CONFIG_AUTOFS4_FS=m +CONFIG_REISERFS_FS=m +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +CONFIG_ADFS_FS=m +# CONFIG_ADFS_FS_RW is not set +CONFIG_AFFS_FS=m +CONFIG_HFS_FS=m +CONFIG_BFS_FS=m +CONFIG_EXT3_FS=y +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_UMSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_EFS_FS=m +# CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set +CONFIG_CRAMFS=m +CONFIG_TMPFS=y +CONFIG_RAMFS=m +CONFIG_ISO9660_FS=y +# CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set +CONFIG_MINIX_FS=m +CONFIG_VXFS_FS=m +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +CONFIG_HPFS_FS=m +CONFIG_PROC_FS=y +CONFIG_DEVFS_FS=y +CONFIG_DEVFS_MOUNT=y +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +CONFIG_QNX4FS_FS=m +# CONFIG_QNX4FS_RW is not set +CONFIG_ROMFS_FS=m +CONFIG_EXT2_FS=y +CONFIG_SYSV_FS=m +CONFIG_UDF_FS=m +CONFIG_UFS_FS=m +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_ROOT_NFS is not set +CONFIG_NFSD=y +CONFIG_NFSD_V3=y +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set +# CONFIG_ZISOFS_FS is not set +CONFIG_ZLIB_FS_INFLATE=m + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +CONFIG_NLS=y + +# +# Native Language Support +# +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_MD_MULTIPATH is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Kernel hacking +# +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_PT_PROXY is not set +# CONFIG_GPROF is not set +# CONFIG_GCOV is not set diff --git a/arch/um/defconfig b/arch/um/defconfig new file mode 100644 index 00000000000..fc3075c589d --- /dev/null +++ b/arch/um/defconfig @@ -0,0 +1,417 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.12-rc1-bk1 +# Sun Mar 20 16:53:00 2005 +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_UML=y +CONFIG_MMU=y +CONFIG_UID16=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_CALIBRATE_DELAY=y + +# +# UML-specific options +# +CONFIG_MODE_TT=y +CONFIG_MODE_SKAS=y +# CONFIG_64_BIT is not set +CONFIG_TOP_ADDR=0xc0000000 +# CONFIG_3_LEVEL_PGTABLES is not set +CONFIG_ARCH_HAS_SC_SIGNALS=y +CONFIG_ARCH_REUSE_HOST_VSYSCALL_AREA=y +CONFIG_LD_SCRIPT_STATIC=y +CONFIG_NET=y +CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_MISC=m +CONFIG_HOSTFS=y +CONFIG_MCONSOLE=y +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_HOST_2G_2G is not set +# CONFIG_SMP is not set +CONFIG_NEST_LEVEL=0 +CONFIG_KERNEL_HALF_GIGS=1 +# CONFIG_HIGHMEM is not set +CONFIG_KERNEL_STACK_ORDER=2 +CONFIG_UML_REAL_TIME_CLOCK=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_BROKEN_ON_SMP=y + +# +# General setup +# +CONFIG_LOCALVERSION="" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +# CONFIG_HOTPLUG is not set +CONFIG_KOBJECT_UEVENT=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +# CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_KALLSYMS_EXTRA_PASS=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SHMEM=y +CONFIG_CC_ALIGN_FUNCTIONS=0 +CONFIG_CC_ALIGN_LABELS=0 +CONFIG_CC_ALIGN_LOOPS=0 +CONFIG_CC_ALIGN_JUMPS=0 +# CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=0 + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_KMOD=y + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set +# CONFIG_DEBUG_DRIVER is not set + +# +# Character Devices +# +CONFIG_STDERR_CONSOLE=y +CONFIG_STDIO_CONSOLE=y +CONFIG_SSL=y +CONFIG_NULL_CHAN=y +CONFIG_PORT_CHAN=y +CONFIG_PTY_CHAN=y +CONFIG_TTY_CHAN=y +CONFIG_XTERM_CHAN=y +# CONFIG_NOCONFIG_CHAN is not set +CONFIG_CON_ZERO_CHAN="fd:0,fd:1" +CONFIG_CON_CHAN="xterm" +CONFIG_SSL_CHAN="pty" +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_WATCHDOG is not set +CONFIG_UML_SOUND=m +CONFIG_SOUND=m +CONFIG_HOSTAUDIO=m +CONFIG_UML_RANDOM=y + +# +# Block devices +# +CONFIG_BLK_DEV_UBD=y +CONFIG_BLK_DEV_UBD_SYNC=y +CONFIG_BLK_DEV_COW_COMMON=y +CONFIG_BLK_DEV_LOOP=m +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +CONFIG_BLK_DEV_NBD=m +# CONFIG_BLK_DEV_RAM is not set +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_INITRAMFS_SOURCE="" +# CONFIG_LBD is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_ATA_OVER_ETH is not set +CONFIG_NETDEVICES=y + +# +# UML Network Devices +# +CONFIG_UML_NET=y +CONFIG_UML_NET_ETHERTAP=y +CONFIG_UML_NET_TUNTAP=y +CONFIG_UML_NET_SLIP=y +CONFIG_UML_NET_DAEMON=y +CONFIG_UML_NET_MCAST=y +CONFIG_UML_NET_SLIRP=y + +# +# Networking support +# + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_PACKET_MMAP=y +# CONFIG_NETLINK_DEV is not set +CONFIG_UNIX=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_TUNNEL is not set +CONFIG_IP_TCPDIAG=y +# CONFIG_IP_TCPDIAG_IPV6 is not set +# CONFIG_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_DUMMY=m +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +CONFIG_TUN=m + +# +# Wan interfaces +# +# CONFIG_WAN is not set +CONFIG_PPP=m +# CONFIG_PPP_MULTILINK is not set +# CONFIG_PPP_FILTER is not set +# CONFIG_PPP_ASYNC is not set +# CONFIG_PPP_SYNC_TTY is not set +# CONFIG_PPP_DEFLATE is not set +# CONFIG_PPP_BSDCOMP is not set +# CONFIG_PPPOE is not set +CONFIG_SLIP=m +# CONFIG_SLIP_COMPRESSED is not set +# CONFIG_SLIP_SMART is not set +# CONFIG_SLIP_MODE_SLIP6 is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +CONFIG_EXT3_FS=y +# CONFIG_EXT3_FS_XATTR is not set +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +CONFIG_REISERFS_FS=y +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_REISERFS_FS_XATTR is not set +# CONFIG_JFS_FS is not set + +# +# XFS support +# +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_QUOTA=y +# CONFIG_QFMT_V1 is not set +# CONFIG_QFMT_V2 is not set +CONFIG_QUOTACTL=y +CONFIG_DNOTIFY=y +CONFIG_AUTOFS_FS=m +CONFIG_AUTOFS4_FS=m + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +# CONFIG_ZISOFS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +# CONFIG_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVPTS_FS_XATTR is not set +CONFIG_TMPFS=y +# CONFIG_TMPFS_XATTR is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +# CONFIG_NFS_FS is not set +# CONFIG_NFSD is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# Native Language Support +# +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Hardware crypto devices +# + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +CONFIG_CRC32=m +# CONFIG_LIBCRC32C is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_INPUT is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_DEBUG_KERNEL=y +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_SCHEDSTATS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_FS is not set +CONFIG_FRAME_POINTER=y +CONFIG_PT_PROXY=y +# CONFIG_GPROF is not set +# CONFIG_GCOV is not set +# CONFIG_SYSCALL_DEBUG is not set diff --git a/arch/um/drivers/Makefile b/arch/um/drivers/Makefile new file mode 100644 index 00000000000..323f72c64cd --- /dev/null +++ b/arch/um/drivers/Makefile @@ -0,0 +1,46 @@ +# +# Copyright (C) 2000, 2002, 2003 Jeff Dike (jdike@karaya.com) +# Licensed under the GPL +# + +# pcap is broken in 2.5 because kbuild doesn't allow pcap.a to be linked +# in to pcap.o + +slip-objs := slip_kern.o slip_user.o +slirp-objs := slirp_kern.o slirp_user.o +daemon-objs := daemon_kern.o daemon_user.o +mcast-objs := mcast_kern.o mcast_user.o +#pcap-objs := pcap_kern.o pcap_user.o $(PCAP) +net-objs := net_kern.o net_user.o +mconsole-objs := mconsole_kern.o mconsole_user.o +hostaudio-objs := hostaudio_kern.o +ubd-objs := ubd_kern.o ubd_user.o +port-objs := port_kern.o port_user.o +harddog-objs := harddog_kern.o harddog_user.o + +obj-y := stdio_console.o fd.o chan_kern.o chan_user.o line.o +obj-$(CONFIG_SSL) += ssl.o +obj-$(CONFIG_STDERR_CONSOLE) += stderr_console.o + +obj-$(CONFIG_UML_NET_SLIP) += slip.o +obj-$(CONFIG_UML_NET_SLIRP) += slirp.o +obj-$(CONFIG_UML_NET_DAEMON) += daemon.o +obj-$(CONFIG_UML_NET_MCAST) += mcast.o +#obj-$(CONFIG_UML_NET_PCAP) += pcap.o $(PCAP) +obj-$(CONFIG_UML_NET) += net.o +obj-$(CONFIG_MCONSOLE) += mconsole.o +obj-$(CONFIG_MMAPPER) += mmapper_kern.o +obj-$(CONFIG_BLK_DEV_UBD) += ubd.o +obj-$(CONFIG_HOSTAUDIO) += hostaudio.o +obj-$(CONFIG_NULL_CHAN) += null.o +obj-$(CONFIG_PORT_CHAN) += port.o +obj-$(CONFIG_PTY_CHAN) += pty.o +obj-$(CONFIG_TTY_CHAN) += tty.o +obj-$(CONFIG_XTERM_CHAN) += xterm.o xterm_kern.o +obj-$(CONFIG_UML_WATCHDOG) += harddog.o +obj-$(CONFIG_BLK_DEV_COW_COMMON) += cow_user.o +obj-$(CONFIG_UML_RANDOM) += random.o + +USER_OBJS := fd.o null.o pty.o tty.o xterm.o + +include arch/um/scripts/Makefile.rules diff --git a/arch/um/drivers/chan_kern.c b/arch/um/drivers/chan_kern.c new file mode 100644 index 00000000000..1f77deb3fd2 --- /dev/null +++ b/arch/um/drivers/chan_kern.c @@ -0,0 +1,577 @@ +/* + * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <linux/stddef.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/tty.h> +#include <linux/string.h> +#include <linux/tty_flip.h> +#include <asm/irq.h> +#include "chan_kern.h" +#include "user_util.h" +#include "kern.h" +#include "irq_user.h" +#include "sigio.h" +#include "line.h" +#include "os.h" + +#ifdef CONFIG_NOCONFIG_CHAN +static void *not_configged_init(char *str, int device, struct chan_opts *opts) +{ + printk(KERN_ERR "Using a channel type which is configured out of " + "UML\n"); + return(NULL); +} + +static int not_configged_open(int input, int output, int primary, void *data, + char **dev_out) +{ + printk(KERN_ERR "Using a channel type which is configured out of " + "UML\n"); + return(-ENODEV); +} + +static void not_configged_close(int fd, void *data) +{ + printk(KERN_ERR "Using a channel type which is configured out of " + "UML\n"); +} + +static int not_configged_read(int fd, char *c_out, void *data) +{ + printk(KERN_ERR "Using a channel type which is configured out of " + "UML\n"); + return(-EIO); +} + +static int not_configged_write(int fd, const char *buf, int len, void *data) +{ + printk(KERN_ERR "Using a channel type which is configured out of " + "UML\n"); + return(-EIO); +} + +static int not_configged_console_write(int fd, const char *buf, int len, + void *data) +{ + printk(KERN_ERR "Using a channel type which is configured out of " + "UML\n"); + return(-EIO); +} + +static int not_configged_window_size(int fd, void *data, unsigned short *rows, + unsigned short *cols) +{ + printk(KERN_ERR "Using a channel type which is configured out of " + "UML\n"); + return(-ENODEV); +} + +static void not_configged_free(void *data) +{ + printk(KERN_ERR "Using a channel type which is configured out of " + "UML\n"); +} + +static struct chan_ops not_configged_ops = { + .init = not_configged_init, + .open = not_configged_open, + .close = not_configged_close, + .read = not_configged_read, + .write = not_configged_write, + .console_write = not_configged_console_write, + .window_size = not_configged_window_size, + .free = not_configged_free, + .winch = 0, +}; +#endif /* CONFIG_NOCONFIG_CHAN */ + +void generic_close(int fd, void *unused) +{ + os_close_file(fd); +} + +int generic_read(int fd, char *c_out, void *unused) +{ + int n; + + n = os_read_file(fd, c_out, sizeof(*c_out)); + + if(n == -EAGAIN) + return(0); + else if(n == 0) + return(-EIO); + return(n); +} + +/* XXX Trivial wrapper around os_write_file */ + +int generic_write(int fd, const char *buf, int n, void *unused) +{ + return(os_write_file(fd, buf, n)); +} + +int generic_window_size(int fd, void *unused, unsigned short *rows_out, + unsigned short *cols_out) +{ + int rows, cols; + int ret; + + ret = os_window_size(fd, &rows, &cols); + if(ret < 0) + return(ret); + + ret = ((*rows_out != rows) || (*cols_out != cols)); + + *rows_out = rows; + *cols_out = cols; + + return(ret); +} + +void generic_free(void *data) +{ + kfree(data); +} + +static void tty_receive_char(struct tty_struct *tty, char ch) +{ + if(tty == NULL) return; + + if(I_IXON(tty) && !I_IXOFF(tty) && !tty->raw) { + if(ch == STOP_CHAR(tty)){ + stop_tty(tty); + return; + } + else if(ch == START_CHAR(tty)){ + start_tty(tty); + return; + } + } + + if((tty->flip.flag_buf_ptr == NULL) || + (tty->flip.char_buf_ptr == NULL)) + return; + tty_insert_flip_char(tty, ch, TTY_NORMAL); +} + +static int open_one_chan(struct chan *chan, int input, int output, int primary) +{ + int fd; + + if(chan->opened) return(0); + if(chan->ops->open == NULL) fd = 0; + else fd = (*chan->ops->open)(input, output, primary, chan->data, + &chan->dev); + if(fd < 0) return(fd); + chan->fd = fd; + + chan->opened = 1; + return(0); +} + +int open_chan(struct list_head *chans) +{ + struct list_head *ele; + struct chan *chan; + int ret, err = 0; + + list_for_each(ele, chans){ + chan = list_entry(ele, struct chan, list); + ret = open_one_chan(chan, chan->input, chan->output, + chan->primary); + if(chan->primary) err = ret; + } + return(err); +} + +void chan_enable_winch(struct list_head *chans, struct tty_struct *tty) +{ + struct list_head *ele; + struct chan *chan; + + list_for_each(ele, chans){ + chan = list_entry(ele, struct chan, list); + if(chan->primary && chan->output && chan->ops->winch){ + register_winch(chan->fd, tty); + return; + } + } +} + +void enable_chan(struct list_head *chans, struct tty_struct *tty) +{ + struct list_head *ele; + struct chan *chan; + + list_for_each(ele, chans){ + chan = list_entry(ele, struct chan, list); + if(!chan->opened) continue; + + line_setup_irq(chan->fd, chan->input, chan->output, tty); + } +} + +void close_chan(struct list_head *chans) +{ + struct chan *chan; + + /* Close in reverse order as open in case more than one of them + * refers to the same device and they save and restore that device's + * state. Then, the first one opened will have the original state, + * so it must be the last closed. + */ + list_for_each_entry_reverse(chan, chans, list) { + if(!chan->opened) continue; + if(chan->ops->close != NULL) + (*chan->ops->close)(chan->fd, chan->data); + chan->opened = 0; + chan->fd = -1; + } +} + +int write_chan(struct list_head *chans, const char *buf, int len, + int write_irq) +{ + struct list_head *ele; + struct chan *chan = NULL; + int n, ret = 0; + + list_for_each(ele, chans) { + chan = list_entry(ele, struct chan, list); + if (!chan->output || (chan->ops->write == NULL)) + continue; + n = chan->ops->write(chan->fd, buf, len, chan->data); + if (chan->primary) { + ret = n; + if ((ret == -EAGAIN) || ((ret >= 0) && (ret < len))) + reactivate_fd(chan->fd, write_irq); + } + } + return(ret); +} + +int console_write_chan(struct list_head *chans, const char *buf, int len) +{ + struct list_head *ele; + struct chan *chan; + int n, ret = 0; + + list_for_each(ele, chans){ + chan = list_entry(ele, struct chan, list); + if(!chan->output || (chan->ops->console_write == NULL)) + continue; + n = chan->ops->console_write(chan->fd, buf, len, chan->data); + if(chan->primary) ret = n; + } + return(ret); +} + +int console_open_chan(struct line *line, struct console *co, struct chan_opts *opts) +{ + if (!list_empty(&line->chan_list)) + return 0; + + if (0 != parse_chan_pair(line->init_str, &line->chan_list, + line->init_pri, co->index, opts)) + return -1; + if (0 != open_chan(&line->chan_list)) + return -1; + printk("Console initialized on /dev/%s%d\n",co->name,co->index); + return 0; +} + +int chan_window_size(struct list_head *chans, unsigned short *rows_out, + unsigned short *cols_out) +{ + struct list_head *ele; + struct chan *chan; + + list_for_each(ele, chans){ + chan = list_entry(ele, struct chan, list); + if(chan->primary){ + if(chan->ops->window_size == NULL) return(0); + return(chan->ops->window_size(chan->fd, chan->data, + rows_out, cols_out)); + } + } + return(0); +} + +void free_one_chan(struct chan *chan) +{ + list_del(&chan->list); + if(chan->ops->free != NULL) + (*chan->ops->free)(chan->data); + free_irq_by_fd(chan->fd); + if(chan->primary && chan->output) ignore_sigio_fd(chan->fd); + kfree(chan); +} + +void free_chan(struct list_head *chans) +{ + struct list_head *ele, *next; + struct chan *chan; + + list_for_each_safe(ele, next, chans){ + chan = list_entry(ele, struct chan, list); + free_one_chan(chan); + } +} + +static int one_chan_config_string(struct chan *chan, char *str, int size, + char **error_out) +{ + int n = 0; + + if(chan == NULL){ + CONFIG_CHUNK(str, size, n, "none", 1); + return(n); + } + + CONFIG_CHUNK(str, size, n, chan->ops->type, 0); + + if(chan->dev == NULL){ + CONFIG_CHUNK(str, size, n, "", 1); + return(n); + } + + CONFIG_CHUNK(str, size, n, ":", 0); + CONFIG_CHUNK(str, size, n, chan->dev, 0); + + return(n); +} + +static int chan_pair_config_string(struct chan *in, struct chan *out, + char *str, int size, char **error_out) +{ + int n; + + n = one_chan_config_string(in, str, size, error_out); + str += n; + size -= n; + + if(in == out){ + CONFIG_CHUNK(str, size, n, "", 1); + return(n); + } + + CONFIG_CHUNK(str, size, n, ",", 1); + n = one_chan_config_string(out, str, size, error_out); + str += n; + size -= n; + CONFIG_CHUNK(str, size, n, "", 1); + + return(n); +} + +int chan_config_string(struct list_head *chans, char *str, int size, + char **error_out) +{ + struct list_head *ele; + struct chan *chan, *in = NULL, *out = NULL; + + list_for_each(ele, chans){ + chan = list_entry(ele, struct chan, list); + if(!chan->primary) + continue; + if(chan->input) + in = chan; + if(chan->output) + out = chan; + } + + return(chan_pair_config_string(in, out, str, size, error_out)); +} + +struct chan_type { + char *key; + struct chan_ops *ops; +}; + +struct chan_type chan_table[] = { + { "fd", &fd_ops }, + +#ifdef CONFIG_NULL_CHAN + { "null", &null_ops }, +#else + { "null", ¬_configged_ops }, +#endif + +#ifdef CONFIG_PORT_CHAN + { "port", &port_ops }, +#else + { "port", ¬_configged_ops }, +#endif + +#ifdef CONFIG_PTY_CHAN + { "pty", &pty_ops }, + { "pts", &pts_ops }, +#else + { "pty", ¬_configged_ops }, + { "pts", ¬_configged_ops }, +#endif + +#ifdef CONFIG_TTY_CHAN + { "tty", &tty_ops }, +#else + { "tty", ¬_configged_ops }, +#endif + +#ifdef CONFIG_XTERM_CHAN + { "xterm", &xterm_ops }, +#else + { "xterm", ¬_configged_ops }, +#endif +}; + +static struct chan *parse_chan(char *str, int pri, int device, + struct chan_opts *opts) +{ + struct chan_type *entry; + struct chan_ops *ops; + struct chan *chan; + void *data; + int i; + + ops = NULL; + data = NULL; + for(i = 0; i < sizeof(chan_table)/sizeof(chan_table[0]); i++){ + entry = &chan_table[i]; + if(!strncmp(str, entry->key, strlen(entry->key))){ + ops = entry->ops; + str += strlen(entry->key); + break; + } + } + if(ops == NULL){ + printk(KERN_ERR "parse_chan couldn't parse \"%s\"\n", + str); + return(NULL); + } + if(ops->init == NULL) return(NULL); + data = (*ops->init)(str, device, opts); + if(data == NULL) return(NULL); + + chan = kmalloc(sizeof(*chan), GFP_KERNEL); + if(chan == NULL) return(NULL); + *chan = ((struct chan) { .list = LIST_HEAD_INIT(chan->list), + .primary = 1, + .input = 0, + .output = 0, + .opened = 0, + .fd = -1, + .pri = pri, + .ops = ops, + .data = data }); + return(chan); +} + +int parse_chan_pair(char *str, struct list_head *chans, int pri, int device, + struct chan_opts *opts) +{ + struct chan *new, *chan; + char *in, *out; + + if(!list_empty(chans)){ + chan = list_entry(chans->next, struct chan, list); + if(chan->pri >= pri) return(0); + free_chan(chans); + INIT_LIST_HEAD(chans); + } + + out = strchr(str, ','); + if(out != NULL){ + in = str; + *out = '\0'; + out++; + new = parse_chan(in, pri, device, opts); + if(new == NULL) return(-1); + new->input = 1; + list_add(&new->list, chans); + + new = parse_chan(out, pri, device, opts); + if(new == NULL) return(-1); + list_add(&new->list, chans); + new->output = 1; + } + else { + new = parse_chan(str, pri, device, opts); + if(new == NULL) return(-1); + list_add(&new->list, chans); + new->input = 1; + new->output = 1; + } + return(0); +} + +int chan_out_fd(struct list_head *chans) +{ + struct list_head *ele; + struct chan *chan; + + list_for_each(ele, chans){ + chan = list_entry(ele, struct chan, list); + if(chan->primary && chan->output) + return(chan->fd); + } + return(-1); +} + +void chan_interrupt(struct list_head *chans, struct work_struct *task, + struct tty_struct *tty, int irq) +{ + struct list_head *ele, *next; + struct chan *chan; + int err; + char c; + + list_for_each_safe(ele, next, chans){ + chan = list_entry(ele, struct chan, list); + if(!chan->input || (chan->ops->read == NULL)) continue; + do { + if((tty != NULL) && + (tty->flip.count >= TTY_FLIPBUF_SIZE)){ + schedule_work(task); + goto out; + } + err = chan->ops->read(chan->fd, &c, chan->data); + if(err > 0) + tty_receive_char(tty, c); + } while(err > 0); + + if(err == 0) reactivate_fd(chan->fd, irq); + if(err == -EIO){ + if(chan->primary){ + if(tty != NULL) + tty_hangup(tty); + line_disable(tty, irq); + close_chan(chans); + free_chan(chans); + return; + } + else { + if(chan->ops->close != NULL) + chan->ops->close(chan->fd, chan->data); + free_one_chan(chan); + } + } + } + out: + if(tty) tty_flip_buffer_push(tty); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/chan_user.c b/arch/um/drivers/chan_user.c new file mode 100644 index 00000000000..583b8e137c3 --- /dev/null +++ b/arch/um/drivers/chan_user.c @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <termios.h> +#include <string.h> +#include <signal.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include "kern_util.h" +#include "user_util.h" +#include "chan_user.h" +#include "user.h" +#include "helper.h" +#include "os.h" +#include "choose-mode.h" +#include "mode.h" + +int generic_console_write(int fd, const char *buf, int n, void *unused) +{ + struct termios save, new; + int err; + + if(isatty(fd)){ + CATCH_EINTR(err = tcgetattr(fd, &save)); + if (err) + goto error; + new = save; + /* The terminal becomes a bit less raw, to handle \n also as + * "Carriage Return", not only as "New Line". Otherwise, the new + * line won't start at the first column.*/ + new.c_oflag |= OPOST; + CATCH_EINTR(err = tcsetattr(fd, TCSAFLUSH, &new)); + if (err) + goto error; + } + err = generic_write(fd, buf, n, NULL); + /* Restore raw mode, in any case; we *must* ignore any error apart + * EINTR, except for debug.*/ + if(isatty(fd)) + CATCH_EINTR(tcsetattr(fd, TCSAFLUSH, &save)); + return(err); +error: + return(-errno); +} + +/* + * UML SIGWINCH handling + * + * The point of this is to handle SIGWINCH on consoles which have host ttys and + * relay them inside UML to whatever might be running on the console and cares + * about the window size (since SIGWINCH notifies about terminal size changes). + * + * So, we have a separate thread for each host tty attached to a UML device + * (side-issue - I'm annoyed that one thread can't have multiple controlling + * ttys for purposed of handling SIGWINCH, but I imagine there are other reasons + * that doesn't make any sense). + * + * SIGWINCH can't be received synchronously, so you have to set up to receive it + * as a signal. That being the case, if you are going to wait for it, it is + * convenient to sit in a pause() and wait for the signal to bounce you out of + * it (see below for how we make sure to exit only on SIGWINCH). + */ + +static void winch_handler(int sig) +{ +} + +struct winch_data { + int pty_fd; + int pipe_fd; + int close_me; +}; + +static int winch_thread(void *arg) +{ + struct winch_data *data = arg; + sigset_t sigs; + int pty_fd, pipe_fd; + int count, err; + char c = 1; + + os_close_file(data->close_me); + pty_fd = data->pty_fd; + pipe_fd = data->pipe_fd; + count = os_write_file(pipe_fd, &c, sizeof(c)); + if(count != sizeof(c)) + printk("winch_thread : failed to write synchronization " + "byte, err = %d\n", -count); + + /* We are not using SIG_IGN on purpose, so don't fix it as I thought to + * do! If using SIG_IGN, the pause() call below would not stop on + * SIGWINCH. */ + + signal(SIGWINCH, winch_handler); + sigfillset(&sigs); + sigdelset(&sigs, SIGWINCH); + /* Block anything else than SIGWINCH. */ + if(sigprocmask(SIG_SETMASK, &sigs, NULL) < 0){ + printk("winch_thread : sigprocmask failed, errno = %d\n", + errno); + exit(1); + } + + if(setsid() < 0){ + printk("winch_thread : setsid failed, errno = %d\n", errno); + exit(1); + } + + err = os_new_tty_pgrp(pty_fd, os_getpid()); + if(err < 0){ + printk("winch_thread : new_tty_pgrp failed, err = %d\n", -err); + exit(1); + } + + /* These are synchronization calls between various UML threads on the + * host - since they are not different kernel threads, we cannot use + * kernel semaphores. We don't use SysV semaphores because they are + * persistant. */ + count = os_read_file(pipe_fd, &c, sizeof(c)); + if(count != sizeof(c)) + printk("winch_thread : failed to read synchronization byte, " + "err = %d\n", -count); + + while(1){ + /* This will be interrupted by SIGWINCH only, since other signals + * are blocked.*/ + pause(); + + count = os_write_file(pipe_fd, &c, sizeof(c)); + if(count != sizeof(c)) + printk("winch_thread : write failed, err = %d\n", + -count); + } +} + +static int winch_tramp(int fd, struct tty_struct *tty, int *fd_out) +{ + struct winch_data data; + unsigned long stack; + int fds[2], pid, n, err; + char c; + + err = os_pipe(fds, 1, 1); + if(err < 0){ + printk("winch_tramp : os_pipe failed, err = %d\n", -err); + return(err); + } + + data = ((struct winch_data) { .pty_fd = fd, + .pipe_fd = fds[1], + .close_me = fds[0] } ); + pid = run_helper_thread(winch_thread, &data, 0, &stack, 0); + if(pid < 0){ + printk("fork of winch_thread failed - errno = %d\n", errno); + return(pid); + } + + os_close_file(fds[1]); + *fd_out = fds[0]; + n = os_read_file(fds[0], &c, sizeof(c)); + if(n != sizeof(c)){ + printk("winch_tramp : failed to read synchronization byte\n"); + printk("read failed, err = %d\n", -n); + printk("fd %d will not support SIGWINCH\n", fd); + *fd_out = -1; + } + return(pid); +} + +void register_winch(int fd, struct tty_struct *tty) +{ + int pid, thread, thread_fd; + int count; + char c = 1; + + if(!isatty(fd)) + return; + + pid = tcgetpgrp(fd); + if(!CHOOSE_MODE_PROC(is_tracer_winch, is_skas_winch, pid, fd, + tty) && (pid == -1)){ + thread = winch_tramp(fd, tty, &thread_fd); + if(fd != -1){ + register_winch_irq(thread_fd, fd, thread, tty); + + count = os_write_file(thread_fd, &c, sizeof(c)); + if(count != sizeof(c)) + printk("register_winch : failed to write " + "synchronization byte, err = %d\n", + -count); + } + } +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/cow.h b/arch/um/drivers/cow.h new file mode 100644 index 00000000000..4fcbe8b1b77 --- /dev/null +++ b/arch/um/drivers/cow.h @@ -0,0 +1,42 @@ +#ifndef __COW_H__ +#define __COW_H__ + +#include <asm/types.h> + +#if __BYTE_ORDER == __BIG_ENDIAN +# define ntohll(x) (x) +# define htonll(x) (x) +#elif __BYTE_ORDER == __LITTLE_ENDIAN +# define ntohll(x) bswap_64(x) +# define htonll(x) bswap_64(x) +#else +#error "__BYTE_ORDER not defined" +#endif + +extern int init_cow_file(int fd, char *cow_file, char *backing_file, + int sectorsize, int alignment, int *bitmap_offset_out, + unsigned long *bitmap_len_out, int *data_offset_out); + +extern int file_reader(__u64 offset, char *buf, int len, void *arg); +extern int read_cow_header(int (*reader)(__u64, char *, int, void *), + void *arg, __u32 *version_out, + char **backing_file_out, time_t *mtime_out, + unsigned long long *size_out, int *sectorsize_out, + __u32 *align_out, int *bitmap_offset_out); + +extern int write_cow_header(char *cow_file, int fd, char *backing_file, + int sectorsize, int alignment, + unsigned long long *size); + +extern void cow_sizes(int version, __u64 size, int sectorsize, int align, + int bitmap_offset, unsigned long *bitmap_len_out, + int *data_offset_out); + +#endif + +/* + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/cow_sys.h b/arch/um/drivers/cow_sys.h new file mode 100644 index 00000000000..c83fc5d6893 --- /dev/null +++ b/arch/um/drivers/cow_sys.h @@ -0,0 +1,48 @@ +#ifndef __COW_SYS_H__ +#define __COW_SYS_H__ + +#include "kern_util.h" +#include "user_util.h" +#include "os.h" +#include "user.h" + +static inline void *cow_malloc(int size) +{ + return(um_kmalloc(size)); +} + +static inline void cow_free(void *ptr) +{ + kfree(ptr); +} + +#define cow_printf printk + +static inline char *cow_strdup(char *str) +{ + return(uml_strdup(str)); +} + +static inline int cow_seek_file(int fd, unsigned long long offset) +{ + return(os_seek_file(fd, offset)); +} + +static inline int cow_file_size(char *file, unsigned long long *size_out) +{ + return(os_file_size(file, size_out)); +} + +static inline int cow_write_file(int fd, char *buf, int size) +{ + return(os_write_file(fd, buf, size)); +} + +#endif + +/* + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/cow_user.c b/arch/um/drivers/cow_user.c new file mode 100644 index 00000000000..a8ce6fc3ef2 --- /dev/null +++ b/arch/um/drivers/cow_user.c @@ -0,0 +1,378 @@ +#include <stddef.h> +#include <string.h> +#include <errno.h> +/* _XOPEN_SOURCE is needed for pread, but we define _GNU_SOURCE, which defines + * that. + */ +#include <unistd.h> +#include <byteswap.h> +#include <sys/time.h> +#include <sys/param.h> +#include <sys/user.h> +#include <netinet/in.h> + +#include "os.h" + +#include "cow.h" +#include "cow_sys.h" + +#define PATH_LEN_V1 256 + +struct cow_header_v1 { + int magic; + int version; + char backing_file[PATH_LEN_V1]; + time_t mtime; + __u64 size; + int sectorsize; +}; + +#define PATH_LEN_V2 MAXPATHLEN + +struct cow_header_v2 { + __u32 magic; + __u32 version; + char backing_file[PATH_LEN_V2]; + time_t mtime; + __u64 size; + int sectorsize; +}; + +/* Define PATH_LEN_V3 as the usual value of MAXPATHLEN, just hard-code it in + * case other systems have different values for MAXPATHLEN + */ +#define PATH_LEN_V3 4096 + +/* Changes from V2 - + * PATH_LEN_V3 as described above + * Explicitly specify field bit lengths for systems with different + * lengths for the usual C types. Not sure whether char or + * time_t should be changed, this can be changed later without + * breaking compatibility + * Add alignment field so that different alignments can be used for the + * bitmap and data + * Add cow_format field to allow for the possibility of different ways + * of specifying the COW blocks. For now, the only value is 0, + * for the traditional COW bitmap. + * Move the backing_file field to the end of the header. This allows + * for the possibility of expanding it into the padding required + * by the bitmap alignment. + * The bitmap and data portions of the file will be aligned as specified + * by the alignment field. This is to allow COW files to be + * put on devices with restrictions on access alignments, such as + * /dev/raw, with a 512 byte alignment restriction. This also + * allows the data to be more aligned more strictly than on + * sector boundaries. This is needed for ubd-mmap, which needs + * the data to be page aligned. + * Fixed (finally!) the rounding bug + */ + +struct cow_header_v3 { + __u32 magic; + __u32 version; + __u32 mtime; + __u64 size; + __u32 sectorsize; + __u32 alignment; + __u32 cow_format; + char backing_file[PATH_LEN_V3]; +}; + +/* COW format definitions - for now, we have only the usual COW bitmap */ +#define COW_BITMAP 0 + +union cow_header { + struct cow_header_v1 v1; + struct cow_header_v2 v2; + struct cow_header_v3 v3; +}; + +#define COW_MAGIC 0x4f4f4f4d /* MOOO */ +#define COW_VERSION 3 + +#define DIV_ROUND(x, len) (((x) + (len) - 1) / (len)) +#define ROUND_UP(x, align) DIV_ROUND(x, align) * (align) + +void cow_sizes(int version, __u64 size, int sectorsize, int align, + int bitmap_offset, unsigned long *bitmap_len_out, + int *data_offset_out) +{ + if(version < 3){ + *bitmap_len_out = (size + sectorsize - 1) / (8 * sectorsize); + + *data_offset_out = bitmap_offset + *bitmap_len_out; + *data_offset_out = (*data_offset_out + sectorsize - 1) / + sectorsize; + *data_offset_out *= sectorsize; + } + else { + *bitmap_len_out = DIV_ROUND(size, sectorsize); + *bitmap_len_out = DIV_ROUND(*bitmap_len_out, 8); + + *data_offset_out = bitmap_offset + *bitmap_len_out; + *data_offset_out = ROUND_UP(*data_offset_out, align); + } +} + +static int absolutize(char *to, int size, char *from) +{ + char save_cwd[256], *slash; + int remaining; + + if(getcwd(save_cwd, sizeof(save_cwd)) == NULL) { + cow_printf("absolutize : unable to get cwd - errno = %d\n", + errno); + return(-1); + } + slash = strrchr(from, '/'); + if(slash != NULL){ + *slash = '\0'; + if(chdir(from)){ + *slash = '/'; + cow_printf("absolutize : Can't cd to '%s' - " + "errno = %d\n", from, errno); + return(-1); + } + *slash = '/'; + if(getcwd(to, size) == NULL){ + cow_printf("absolutize : unable to get cwd of '%s' - " + "errno = %d\n", from, errno); + return(-1); + } + remaining = size - strlen(to); + if(strlen(slash) + 1 > remaining){ + cow_printf("absolutize : unable to fit '%s' into %d " + "chars\n", from, size); + return(-1); + } + strcat(to, slash); + } + else { + if(strlen(save_cwd) + 1 + strlen(from) + 1 > size){ + cow_printf("absolutize : unable to fit '%s' into %d " + "chars\n", from, size); + return(-1); + } + strcpy(to, save_cwd); + strcat(to, "/"); + strcat(to, from); + } + chdir(save_cwd); + return(0); +} + +int write_cow_header(char *cow_file, int fd, char *backing_file, + int sectorsize, int alignment, unsigned long long *size) +{ + struct cow_header_v3 *header; + unsigned long modtime; + int err; + + err = cow_seek_file(fd, 0); + if(err < 0){ + cow_printf("write_cow_header - lseek failed, err = %d\n", -err); + goto out; + } + + err = -ENOMEM; + header = cow_malloc(sizeof(*header)); + if(header == NULL){ + cow_printf("Failed to allocate COW V3 header\n"); + goto out; + } + header->magic = htonl(COW_MAGIC); + header->version = htonl(COW_VERSION); + + err = -EINVAL; + if(strlen(backing_file) > sizeof(header->backing_file) - 1){ + cow_printf("Backing file name \"%s\" is too long - names are " + "limited to %d characters\n", backing_file, + sizeof(header->backing_file) - 1); + goto out_free; + } + + if(absolutize(header->backing_file, sizeof(header->backing_file), + backing_file)) + goto out_free; + + err = os_file_modtime(header->backing_file, &modtime); + if(err < 0){ + cow_printf("Backing file '%s' mtime request failed, " + "err = %d\n", header->backing_file, -err); + goto out_free; + } + + err = cow_file_size(header->backing_file, size); + if(err < 0){ + cow_printf("Couldn't get size of backing file '%s', " + "err = %d\n", header->backing_file, -err); + goto out_free; + } + + header->mtime = htonl(modtime); + header->size = htonll(*size); + header->sectorsize = htonl(sectorsize); + header->alignment = htonl(alignment); + header->cow_format = COW_BITMAP; + + err = os_write_file(fd, header, sizeof(*header)); + if(err != sizeof(*header)){ + cow_printf("Write of header to new COW file '%s' failed, " + "err = %d\n", cow_file, -err); + goto out_free; + } + err = 0; + out_free: + cow_free(header); + out: + return(err); +} + +int file_reader(__u64 offset, char *buf, int len, void *arg) +{ + int fd = *((int *) arg); + + return(pread(fd, buf, len, offset)); +} + +/* XXX Need to sanity-check the values read from the header */ + +int read_cow_header(int (*reader)(__u64, char *, int, void *), void *arg, + __u32 *version_out, char **backing_file_out, + time_t *mtime_out, unsigned long long *size_out, + int *sectorsize_out, __u32 *align_out, + int *bitmap_offset_out) +{ + union cow_header *header; + char *file; + int err, n; + unsigned long version, magic; + + header = cow_malloc(sizeof(*header)); + if(header == NULL){ + cow_printf("read_cow_header - Failed to allocate header\n"); + return(-ENOMEM); + } + err = -EINVAL; + n = (*reader)(0, (char *) header, sizeof(*header), arg); + if(n < offsetof(typeof(header->v1), backing_file)){ + cow_printf("read_cow_header - short header\n"); + goto out; + } + + magic = header->v1.magic; + if(magic == COW_MAGIC) { + version = header->v1.version; + } + else if(magic == ntohl(COW_MAGIC)){ + version = ntohl(header->v1.version); + } + /* No error printed because the non-COW case comes through here */ + else goto out; + + *version_out = version; + + if(version == 1){ + if(n < sizeof(header->v1)){ + cow_printf("read_cow_header - failed to read V1 " + "header\n"); + goto out; + } + *mtime_out = header->v1.mtime; + *size_out = header->v1.size; + *sectorsize_out = header->v1.sectorsize; + *bitmap_offset_out = sizeof(header->v1); + *align_out = *sectorsize_out; + file = header->v1.backing_file; + } + else if(version == 2){ + if(n < sizeof(header->v2)){ + cow_printf("read_cow_header - failed to read V2 " + "header\n"); + goto out; + } + *mtime_out = ntohl(header->v2.mtime); + *size_out = ntohll(header->v2.size); + *sectorsize_out = ntohl(header->v2.sectorsize); + *bitmap_offset_out = sizeof(header->v2); + *align_out = *sectorsize_out; + file = header->v2.backing_file; + } + else if(version == 3){ + if(n < sizeof(header->v3)){ + cow_printf("read_cow_header - failed to read V2 " + "header\n"); + goto out; + } + *mtime_out = ntohl(header->v3.mtime); + *size_out = ntohll(header->v3.size); + *sectorsize_out = ntohl(header->v3.sectorsize); + *align_out = ntohl(header->v3.alignment); + *bitmap_offset_out = ROUND_UP(sizeof(header->v3), *align_out); + file = header->v3.backing_file; + } + else { + cow_printf("read_cow_header - invalid COW version\n"); + goto out; + } + err = -ENOMEM; + *backing_file_out = cow_strdup(file); + if(*backing_file_out == NULL){ + cow_printf("read_cow_header - failed to allocate backing " + "file\n"); + goto out; + } + err = 0; + out: + cow_free(header); + return(err); +} + +int init_cow_file(int fd, char *cow_file, char *backing_file, int sectorsize, + int alignment, int *bitmap_offset_out, + unsigned long *bitmap_len_out, int *data_offset_out) +{ + unsigned long long size, offset; + char zero = 0; + int err; + + err = write_cow_header(cow_file, fd, backing_file, sectorsize, + alignment, &size); + if(err) + goto out; + + *bitmap_offset_out = ROUND_UP(sizeof(struct cow_header_v3), alignment); + cow_sizes(COW_VERSION, size, sectorsize, alignment, *bitmap_offset_out, + bitmap_len_out, data_offset_out); + + offset = *data_offset_out + size - sizeof(zero); + err = cow_seek_file(fd, offset); + if(err < 0){ + cow_printf("cow bitmap lseek failed : err = %d\n", -err); + goto out; + } + + /* does not really matter how much we write it is just to set EOF + * this also sets the entire COW bitmap + * to zero without having to allocate it + */ + err = cow_write_file(fd, &zero, sizeof(zero)); + if(err != sizeof(zero)){ + cow_printf("Write of bitmap to new COW file '%s' failed, " + "err = %d\n", cow_file, -err); + err = -EINVAL; + goto out; + } + + return(0); + + out: + return(err); +} + +/* + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/daemon.h b/arch/um/drivers/daemon.h new file mode 100644 index 00000000000..7326c42f7ef --- /dev/null +++ b/arch/um/drivers/daemon.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "net_user.h" + +#define SWITCH_VERSION 3 + +struct daemon_data { + char *sock_type; + char *ctl_sock; + void *ctl_addr; + void *data_addr; + void *local_addr; + int fd; + int control; + void *dev; +}; + +extern struct net_user_info daemon_user_info; + +extern int daemon_user_write(int fd, void *buf, int len, + struct daemon_data *pri); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/daemon_kern.c b/arch/um/drivers/daemon_kern.c new file mode 100644 index 00000000000..30d285b266a --- /dev/null +++ b/arch/um/drivers/daemon_kern.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and + * James Leu (jleu@mindspring.net). + * Copyright (C) 2001 by various other people who didn't put their name here. + * Licensed under the GPL. + */ + +#include "linux/kernel.h" +#include "linux/init.h" +#include "linux/netdevice.h" +#include "linux/etherdevice.h" +#include "net_kern.h" +#include "net_user.h" +#include "daemon.h" + +struct daemon_init { + char *sock_type; + char *ctl_sock; +}; + +void daemon_init(struct net_device *dev, void *data) +{ + struct uml_net_private *pri; + struct daemon_data *dpri; + struct daemon_init *init = data; + + pri = dev->priv; + dpri = (struct daemon_data *) pri->user; + dpri->sock_type = init->sock_type; + dpri->ctl_sock = init->ctl_sock; + dpri->fd = -1; + dpri->control = -1; + dpri->dev = dev; + + printk("daemon backend (uml_switch version %d) - %s:%s", + SWITCH_VERSION, dpri->sock_type, dpri->ctl_sock); + printk("\n"); +} + +static int daemon_read(int fd, struct sk_buff **skb, + struct uml_net_private *lp) +{ + *skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER); + if(*skb == NULL) return(-ENOMEM); + return(net_recvfrom(fd, (*skb)->mac.raw, + (*skb)->dev->mtu + ETH_HEADER_OTHER)); +} + +static int daemon_write(int fd, struct sk_buff **skb, + struct uml_net_private *lp) +{ + return(daemon_user_write(fd, (*skb)->data, (*skb)->len, + (struct daemon_data *) &lp->user)); +} + +static struct net_kern_info daemon_kern_info = { + .init = daemon_init, + .protocol = eth_protocol, + .read = daemon_read, + .write = daemon_write, +}; + +int daemon_setup(char *str, char **mac_out, void *data) +{ + struct daemon_init *init = data; + char *remain; + + *init = ((struct daemon_init) + { .sock_type = "unix", + .ctl_sock = "/tmp/uml.ctl" }); + + remain = split_if_spec(str, mac_out, &init->sock_type, &init->ctl_sock, + NULL); + if(remain != NULL) + printk(KERN_WARNING "daemon_setup : Ignoring data socket " + "specification\n"); + + return(1); +} + +static struct transport daemon_transport = { + .list = LIST_HEAD_INIT(daemon_transport.list), + .name = "daemon", + .setup = daemon_setup, + .user = &daemon_user_info, + .kern = &daemon_kern_info, + .private_size = sizeof(struct daemon_data), + .setup_size = sizeof(struct daemon_init), +}; + +static int register_daemon(void) +{ + register_transport(&daemon_transport); + return(1); +} + +__initcall(register_daemon); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/daemon_user.c b/arch/um/drivers/daemon_user.c new file mode 100644 index 00000000000..cf15b4a8b51 --- /dev/null +++ b/arch/um/drivers/daemon_user.c @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and + * James Leu (jleu@mindspring.net). + * Copyright (C) 2001 by various other people who didn't put their name here. + * Licensed under the GPL. + */ + +#include <errno.h> +#include <unistd.h> +#include <stdint.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/time.h> +#include "net_user.h" +#include "daemon.h" +#include "kern_util.h" +#include "user_util.h" +#include "user.h" +#include "os.h" + +#define MAX_PACKET (ETH_MAX_PACKET + ETH_HEADER_OTHER) + +enum request_type { REQ_NEW_CONTROL }; + +#define SWITCH_MAGIC 0xfeedface + +struct request_v3 { + uint32_t magic; + uint32_t version; + enum request_type type; + struct sockaddr_un sock; +}; + +static struct sockaddr_un *new_addr(void *name, int len) +{ + struct sockaddr_un *sun; + + sun = um_kmalloc(sizeof(struct sockaddr_un)); + if(sun == NULL){ + printk("new_addr: allocation of sockaddr_un failed\n"); + return(NULL); + } + sun->sun_family = AF_UNIX; + memcpy(sun->sun_path, name, len); + return(sun); +} + +static int connect_to_switch(struct daemon_data *pri) +{ + struct sockaddr_un *ctl_addr = pri->ctl_addr; + struct sockaddr_un *local_addr = pri->local_addr; + struct sockaddr_un *sun; + struct request_v3 req; + int fd, n, err; + + pri->control = socket(AF_UNIX, SOCK_STREAM, 0); + if(pri->control < 0){ + printk("daemon_open : control socket failed, errno = %d\n", + errno); + return(-errno); + } + + if(connect(pri->control, (struct sockaddr *) ctl_addr, + sizeof(*ctl_addr)) < 0){ + printk("daemon_open : control connect failed, errno = %d\n", + errno); + err = -errno; + goto out; + } + + fd = socket(AF_UNIX, SOCK_DGRAM, 0); + if(fd < 0){ + printk("daemon_open : data socket failed, errno = %d\n", + errno); + err = -errno; + goto out; + } + if(bind(fd, (struct sockaddr *) local_addr, sizeof(*local_addr)) < 0){ + printk("daemon_open : data bind failed, errno = %d\n", + errno); + err = -errno; + goto out_close; + } + + sun = um_kmalloc(sizeof(struct sockaddr_un)); + if(sun == NULL){ + printk("new_addr: allocation of sockaddr_un failed\n"); + err = -ENOMEM; + goto out_close; + } + + req.magic = SWITCH_MAGIC; + req.version = SWITCH_VERSION; + req.type = REQ_NEW_CONTROL; + req.sock = *local_addr; + n = os_write_file(pri->control, &req, sizeof(req)); + if(n != sizeof(req)){ + printk("daemon_open : control setup request failed, err = %d\n", + -n); + err = -ENOTCONN; + goto out; + } + + n = os_read_file(pri->control, sun, sizeof(*sun)); + if(n != sizeof(*sun)){ + printk("daemon_open : read of data socket failed, err = %d\n", + -n); + err = -ENOTCONN; + goto out_close; + } + + pri->data_addr = sun; + return(fd); + + out_close: + os_close_file(fd); + out: + os_close_file(pri->control); + return(err); +} + +static void daemon_user_init(void *data, void *dev) +{ + struct daemon_data *pri = data; + struct timeval tv; + struct { + char zero; + int pid; + int usecs; + } name; + + if(!strcmp(pri->sock_type, "unix")) + pri->ctl_addr = new_addr(pri->ctl_sock, + strlen(pri->ctl_sock) + 1); + name.zero = 0; + name.pid = os_getpid(); + gettimeofday(&tv, NULL); + name.usecs = tv.tv_usec; + pri->local_addr = new_addr(&name, sizeof(name)); + pri->dev = dev; + pri->fd = connect_to_switch(pri); + if(pri->fd < 0){ + kfree(pri->local_addr); + pri->local_addr = NULL; + } +} + +static int daemon_open(void *data) +{ + struct daemon_data *pri = data; + return(pri->fd); +} + +static void daemon_remove(void *data) +{ + struct daemon_data *pri = data; + + os_close_file(pri->fd); + os_close_file(pri->control); + if(pri->data_addr != NULL) kfree(pri->data_addr); + if(pri->ctl_addr != NULL) kfree(pri->ctl_addr); + if(pri->local_addr != NULL) kfree(pri->local_addr); +} + +int daemon_user_write(int fd, void *buf, int len, struct daemon_data *pri) +{ + struct sockaddr_un *data_addr = pri->data_addr; + + return(net_sendto(fd, buf, len, data_addr, sizeof(*data_addr))); +} + +static int daemon_set_mtu(int mtu, void *data) +{ + return(mtu); +} + +struct net_user_info daemon_user_info = { + .init = daemon_user_init, + .open = daemon_open, + .close = NULL, + .remove = daemon_remove, + .set_mtu = daemon_set_mtu, + .add_address = NULL, + .delete_address = NULL, + .max_packet = MAX_PACKET - ETH_HEADER_OTHER +}; + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/fd.c b/arch/um/drivers/fd.c new file mode 100644 index 00000000000..f0b888f66e0 --- /dev/null +++ b/arch/um/drivers/fd.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <termios.h> +#include <errno.h> +#include "user.h" +#include "user_util.h" +#include "chan_user.h" + +struct fd_chan { + int fd; + int raw; + struct termios tt; + char str[sizeof("1234567890\0")]; +}; + +static void *fd_init(char *str, int device, struct chan_opts *opts) +{ + struct fd_chan *data; + char *end; + int n; + + if(*str != ':'){ + printk("fd_init : channel type 'fd' must specify a file " + "descriptor\n"); + return(NULL); + } + str++; + n = strtoul(str, &end, 0); + if((*end != '\0') || (end == str)){ + printk("fd_init : couldn't parse file descriptor '%s'\n", str); + return(NULL); + } + data = um_kmalloc(sizeof(*data)); + if(data == NULL) return(NULL); + *data = ((struct fd_chan) { .fd = n, + .raw = opts->raw }); + return(data); +} + +static int fd_open(int input, int output, int primary, void *d, char **dev_out) +{ + struct fd_chan *data = d; + int err; + + if(data->raw && isatty(data->fd)){ + CATCH_EINTR(err = tcgetattr(data->fd, &data->tt)); + if(err) + return(err); + + err = raw(data->fd); + if(err) + return(err); + } + sprintf(data->str, "%d", data->fd); + *dev_out = data->str; + return(data->fd); +} + +static void fd_close(int fd, void *d) +{ + struct fd_chan *data = d; + int err; + + if(data->raw && isatty(fd)){ + CATCH_EINTR(err = tcsetattr(fd, TCSAFLUSH, &data->tt)); + if(err) + printk("Failed to restore terminal state - " + "errno = %d\n", -err); + data->raw = 0; + } +} + +static int fd_console_write(int fd, const char *buf, int n, void *d) +{ + struct fd_chan *data = d; + + return(generic_console_write(fd, buf, n, &data->tt)); +} + +struct chan_ops fd_ops = { + .type = "fd", + .init = fd_init, + .open = fd_open, + .close = fd_close, + .read = generic_read, + .write = generic_write, + .console_write = fd_console_write, + .window_size = generic_window_size, + .free = generic_free, + .winch = 1, +}; + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/harddog_kern.c b/arch/um/drivers/harddog_kern.c new file mode 100644 index 00000000000..147ec19f6bb --- /dev/null +++ b/arch/um/drivers/harddog_kern.c @@ -0,0 +1,190 @@ +/* UML hardware watchdog, shamelessly stolen from: + * + * SoftDog 0.05: A Software Watchdog Device + * + * (c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved. + * http://www.redhat.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide + * warranty for any of this software. This material is provided + * "AS-IS" and at no charge. + * + * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk> + * + * Software only watchdog driver. Unlike its big brother the WDT501P + * driver this won't always recover a failed machine. + * + * 03/96: Angelo Haritsis <ah@doc.ic.ac.uk> : + * Modularised. + * Added soft_margin; use upon insmod to change the timer delay. + * NB: uses same minor as wdt (WATCHDOG_MINOR); we could use separate + * minors. + * + * 19980911 Alan Cox + * Made SMP safe for 2.3.x + * + * 20011127 Joel Becker (jlbec@evilplan.org> + * Added soft_noboot; Allows testing the softdog trigger without + * requiring a recompile. + * Added WDIOC_GETTIMEOUT and WDIOC_SETTIMOUT. + */ + +#include <linux/module.h> +#include <linux/config.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/miscdevice.h> +#include <linux/watchdog.h> +#include <linux/reboot.h> +#include <linux/smp_lock.h> +#include <linux/init.h> +#include <asm/uaccess.h> +#include "helper.h" +#include "mconsole.h" + +MODULE_LICENSE("GPL"); + +/* Locked by the BKL in harddog_open and harddog_release */ +static int timer_alive; +static int harddog_in_fd = -1; +static int harddog_out_fd = -1; + +/* + * Allow only one person to hold it open + */ + +extern int start_watchdog(int *in_fd_ret, int *out_fd_ret, char *sock); + +static int harddog_open(struct inode *inode, struct file *file) +{ + int err; + char *sock = NULL; + + lock_kernel(); + if(timer_alive) + return -EBUSY; +#ifdef CONFIG_HARDDOG_NOWAYOUT + __module_get(THIS_MODULE); +#endif + +#ifdef CONFIG_MCONSOLE + sock = mconsole_notify_socket(); +#endif + err = start_watchdog(&harddog_in_fd, &harddog_out_fd, sock); + if(err) return(err); + + timer_alive = 1; + unlock_kernel(); + return nonseekable_open(inode, file); +} + +extern void stop_watchdog(int in_fd, int out_fd); + +static int harddog_release(struct inode *inode, struct file *file) +{ + /* + * Shut off the timer. + */ + lock_kernel(); + + stop_watchdog(harddog_in_fd, harddog_out_fd); + harddog_in_fd = -1; + harddog_out_fd = -1; + + timer_alive=0; + unlock_kernel(); + return 0; +} + +extern int ping_watchdog(int fd); + +static ssize_t harddog_write(struct file *file, const char *data, size_t len, + loff_t *ppos) +{ + /* + * Refresh the timer. + */ + if(len) + return(ping_watchdog(harddog_out_fd)); + return 0; +} + +static int harddog_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + static struct watchdog_info ident = { + WDIOC_SETTIMEOUT, + 0, + "UML Hardware Watchdog" + }; + switch (cmd) { + default: + return -ENOTTY; + case WDIOC_GETSUPPORT: + if(copy_to_user((struct harddog_info *)arg, &ident, + sizeof(ident))) + return -EFAULT; + return 0; + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0,(int *)arg); + case WDIOC_KEEPALIVE: + return(ping_watchdog(harddog_out_fd)); + } +} + +static struct file_operations harddog_fops = { + .owner = THIS_MODULE, + .write = harddog_write, + .ioctl = harddog_ioctl, + .open = harddog_open, + .release = harddog_release, +}; + +static struct miscdevice harddog_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &harddog_fops, +}; + +static char banner[] __initdata = KERN_INFO "UML Watchdog Timer\n"; + +static int __init harddog_init(void) +{ + int ret; + + ret = misc_register(&harddog_miscdev); + + if (ret) + return ret; + + printk(banner); + + return(0); +} + +static void __exit harddog_exit(void) +{ + misc_deregister(&harddog_miscdev); +} + +module_init(harddog_init); +module_exit(harddog_exit); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/harddog_user.c b/arch/um/drivers/harddog_user.c new file mode 100644 index 00000000000..d934181b8d4 --- /dev/null +++ b/arch/um/drivers/harddog_user.c @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include "user_util.h" +#include "user.h" +#include "helper.h" +#include "mconsole.h" +#include "os.h" +#include "choose-mode.h" +#include "mode.h" + +struct dog_data { + int stdin; + int stdout; + int close_me[2]; +}; + +static void pre_exec(void *d) +{ + struct dog_data *data = d; + + dup2(data->stdin, 0); + dup2(data->stdout, 1); + dup2(data->stdout, 2); + os_close_file(data->stdin); + os_close_file(data->stdout); + os_close_file(data->close_me[0]); + os_close_file(data->close_me[1]); +} + +int start_watchdog(int *in_fd_ret, int *out_fd_ret, char *sock) +{ + struct dog_data data; + int in_fds[2], out_fds[2], pid, n, err; + char pid_buf[sizeof("nnnnn\0")], c; + char *pid_args[] = { "/usr/bin/uml_watchdog", "-pid", pid_buf, NULL }; + char *mconsole_args[] = { "/usr/bin/uml_watchdog", "-mconsole", NULL, + NULL }; + char **args = NULL; + + err = os_pipe(in_fds, 1, 0); + if(err < 0){ + printk("harddog_open - os_pipe failed, err = %d\n", -err); + goto out; + } + + err = os_pipe(out_fds, 1, 0); + if(err < 0){ + printk("harddog_open - os_pipe failed, err = %d\n", -err); + goto out_close_in; + } + + data.stdin = out_fds[0]; + data.stdout = in_fds[1]; + data.close_me[0] = out_fds[1]; + data.close_me[1] = in_fds[0]; + + if(sock != NULL){ + mconsole_args[2] = sock; + args = mconsole_args; + } + else { + /* XXX The os_getpid() is not SMP correct */ + sprintf(pid_buf, "%d", CHOOSE_MODE(tracing_pid, os_getpid())); + args = pid_args; + } + + pid = run_helper(pre_exec, &data, args, NULL); + + os_close_file(out_fds[0]); + os_close_file(in_fds[1]); + + if(pid < 0){ + err = -pid; + printk("harddog_open - run_helper failed, errno = %d\n", -err); + goto out_close_out; + } + + n = os_read_file(in_fds[0], &c, sizeof(c)); + if(n == 0){ + printk("harddog_open - EOF on watchdog pipe\n"); + helper_wait(pid); + err = -EIO; + goto out_close_out; + } + else if(n < 0){ + printk("harddog_open - read of watchdog pipe failed, " + "err = %d\n", -n); + helper_wait(pid); + err = n; + goto out_close_out; + } + *in_fd_ret = in_fds[0]; + *out_fd_ret = out_fds[1]; + return(0); + + out_close_in: + os_close_file(in_fds[0]); + os_close_file(in_fds[1]); + out_close_out: + os_close_file(out_fds[0]); + os_close_file(out_fds[1]); + out: + return(err); +} + +void stop_watchdog(int in_fd, int out_fd) +{ + os_close_file(in_fd); + os_close_file(out_fd); +} + +int ping_watchdog(int fd) +{ + int n; + char c = '\n'; + + n = os_write_file(fd, &c, sizeof(c)); + if(n != sizeof(c)){ + printk("ping_watchdog - write failed, err = %d\n", -n); + if(n < 0) + return(n); + return(-EIO); + } + return 1; + +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/hostaudio_kern.c b/arch/um/drivers/hostaudio_kern.c new file mode 100644 index 00000000000..d5742783e19 --- /dev/null +++ b/arch/um/drivers/hostaudio_kern.c @@ -0,0 +1,352 @@ +/* + * Copyright (C) 2002 Steve Schmidtke + * Licensed under the GPL + */ + +#include "linux/config.h" +#include "linux/module.h" +#include "linux/init.h" +#include "linux/slab.h" +#include "linux/fs.h" +#include "linux/sound.h" +#include "linux/soundcard.h" +#include "asm/uaccess.h" +#include "kern_util.h" +#include "init.h" +#include "os.h" + +struct hostaudio_state { + int fd; +}; + +struct hostmixer_state { + int fd; +}; + +#define HOSTAUDIO_DEV_DSP "/dev/sound/dsp" +#define HOSTAUDIO_DEV_MIXER "/dev/sound/mixer" + +/* Only changed from linux_main at boot time */ +char *dsp = HOSTAUDIO_DEV_DSP; +char *mixer = HOSTAUDIO_DEV_MIXER; + +#define DSP_HELP \ +" This is used to specify the host dsp device to the hostaudio driver.\n" \ +" The default is \"" HOSTAUDIO_DEV_DSP "\".\n\n" + +#define MIXER_HELP \ +" This is used to specify the host mixer device to the hostaudio driver.\n"\ +" The default is \"" HOSTAUDIO_DEV_MIXER "\".\n\n" + +#ifndef MODULE +static int set_dsp(char *name, int *add) +{ + dsp = name; + return(0); +} + +__uml_setup("dsp=", set_dsp, "dsp=<dsp device>\n" DSP_HELP); + +static int set_mixer(char *name, int *add) +{ + mixer = name; + return(0); +} + +__uml_setup("mixer=", set_mixer, "mixer=<mixer device>\n" MIXER_HELP); + +#else /*MODULE*/ + +MODULE_PARM(dsp, "s"); +MODULE_PARM_DESC(dsp, DSP_HELP); + +MODULE_PARM(mixer, "s"); +MODULE_PARM_DESC(mixer, MIXER_HELP); + +#endif + +/* /dev/dsp file operations */ + +static ssize_t hostaudio_read(struct file *file, char *buffer, size_t count, + loff_t *ppos) +{ + struct hostaudio_state *state = file->private_data; + void *kbuf; + int err; + +#ifdef DEBUG + printk("hostaudio: read called, count = %d\n", count); +#endif + + kbuf = kmalloc(count, GFP_KERNEL); + if(kbuf == NULL) + return(-ENOMEM); + + err = os_read_file(state->fd, kbuf, count); + if(err < 0) + goto out; + + if(copy_to_user(buffer, kbuf, err)) + err = -EFAULT; + + out: + kfree(kbuf); + return(err); +} + +static ssize_t hostaudio_write(struct file *file, const char *buffer, + size_t count, loff_t *ppos) +{ + struct hostaudio_state *state = file->private_data; + void *kbuf; + int err; + +#ifdef DEBUG + printk("hostaudio: write called, count = %d\n", count); +#endif + + kbuf = kmalloc(count, GFP_KERNEL); + if(kbuf == NULL) + return(-ENOMEM); + + err = -EFAULT; + if(copy_from_user(kbuf, buffer, count)) + goto out; + + err = os_write_file(state->fd, kbuf, count); + if(err < 0) + goto out; + *ppos += err; + + out: + kfree(kbuf); + return(err); +} + +static unsigned int hostaudio_poll(struct file *file, + struct poll_table_struct *wait) +{ + unsigned int mask = 0; + +#ifdef DEBUG + printk("hostaudio: poll called (unimplemented)\n"); +#endif + + return(mask); +} + +static int hostaudio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct hostaudio_state *state = file->private_data; + unsigned long data = 0; + int err; + +#ifdef DEBUG + printk("hostaudio: ioctl called, cmd = %u\n", cmd); +#endif + switch(cmd){ + case SNDCTL_DSP_SPEED: + case SNDCTL_DSP_STEREO: + case SNDCTL_DSP_GETBLKSIZE: + case SNDCTL_DSP_CHANNELS: + case SNDCTL_DSP_SUBDIVIDE: + case SNDCTL_DSP_SETFRAGMENT: + if(get_user(data, (int *) arg)) + return(-EFAULT); + break; + default: + break; + } + + err = os_ioctl_generic(state->fd, cmd, (unsigned long) &data); + + switch(cmd){ + case SNDCTL_DSP_SPEED: + case SNDCTL_DSP_STEREO: + case SNDCTL_DSP_GETBLKSIZE: + case SNDCTL_DSP_CHANNELS: + case SNDCTL_DSP_SUBDIVIDE: + case SNDCTL_DSP_SETFRAGMENT: + if(put_user(data, (int *) arg)) + return(-EFAULT); + break; + default: + break; + } + + return(err); +} + +static int hostaudio_open(struct inode *inode, struct file *file) +{ + struct hostaudio_state *state; + int r = 0, w = 0; + int ret; + +#ifdef DEBUG + printk("hostaudio: open called (host: %s)\n", dsp); +#endif + + state = kmalloc(sizeof(struct hostaudio_state), GFP_KERNEL); + if(state == NULL) + return(-ENOMEM); + + if(file->f_mode & FMODE_READ) r = 1; + if(file->f_mode & FMODE_WRITE) w = 1; + + ret = os_open_file(dsp, of_set_rw(OPENFLAGS(), r, w), 0); + if(ret < 0){ + kfree(state); + return(ret); + } + + state->fd = ret; + file->private_data = state; + return(0); +} + +static int hostaudio_release(struct inode *inode, struct file *file) +{ + struct hostaudio_state *state = file->private_data; + +#ifdef DEBUG + printk("hostaudio: release called\n"); +#endif + + os_close_file(state->fd); + kfree(state); + + return(0); +} + +/* /dev/mixer file operations */ + +static int hostmixer_ioctl_mixdev(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct hostmixer_state *state = file->private_data; + +#ifdef DEBUG + printk("hostmixer: ioctl called\n"); +#endif + + return(os_ioctl_generic(state->fd, cmd, arg)); +} + +static int hostmixer_open_mixdev(struct inode *inode, struct file *file) +{ + struct hostmixer_state *state; + int r = 0, w = 0; + int ret; + +#ifdef DEBUG + printk("hostmixer: open called (host: %s)\n", mixer); +#endif + + state = kmalloc(sizeof(struct hostmixer_state), GFP_KERNEL); + if(state == NULL) return(-ENOMEM); + + if(file->f_mode & FMODE_READ) r = 1; + if(file->f_mode & FMODE_WRITE) w = 1; + + ret = os_open_file(mixer, of_set_rw(OPENFLAGS(), r, w), 0); + + if(ret < 0){ + printk("hostaudio_open_mixdev failed to open '%s', err = %d\n", + dsp, -ret); + kfree(state); + return(ret); + } + + file->private_data = state; + return(0); +} + +static int hostmixer_release(struct inode *inode, struct file *file) +{ + struct hostmixer_state *state = file->private_data; + +#ifdef DEBUG + printk("hostmixer: release called\n"); +#endif + + os_close_file(state->fd); + kfree(state); + + return(0); +} + + +/* kernel module operations */ + +static struct file_operations hostaudio_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = hostaudio_read, + .write = hostaudio_write, + .poll = hostaudio_poll, + .ioctl = hostaudio_ioctl, + .mmap = NULL, + .open = hostaudio_open, + .release = hostaudio_release, +}; + +static struct file_operations hostmixer_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .ioctl = hostmixer_ioctl_mixdev, + .open = hostmixer_open_mixdev, + .release = hostmixer_release, +}; + +struct { + int dev_audio; + int dev_mixer; +} module_data; + +MODULE_AUTHOR("Steve Schmidtke"); +MODULE_DESCRIPTION("UML Audio Relay"); +MODULE_LICENSE("GPL"); + +static int __init hostaudio_init_module(void) +{ + printk(KERN_INFO "UML Audio Relay (host dsp = %s, host mixer = %s)\n", + dsp, mixer); + + module_data.dev_audio = register_sound_dsp(&hostaudio_fops, -1); + if(module_data.dev_audio < 0){ + printk(KERN_ERR "hostaudio: couldn't register DSP device!\n"); + return -ENODEV; + } + + module_data.dev_mixer = register_sound_mixer(&hostmixer_fops, -1); + if(module_data.dev_mixer < 0){ + printk(KERN_ERR "hostmixer: couldn't register mixer " + "device!\n"); + unregister_sound_dsp(module_data.dev_audio); + return -ENODEV; + } + + return 0; +} + +static void __exit hostaudio_cleanup_module (void) +{ + unregister_sound_mixer(module_data.dev_mixer); + unregister_sound_dsp(module_data.dev_audio); +} + +module_init(hostaudio_init_module); +module_exit(hostaudio_cleanup_module); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c new file mode 100644 index 00000000000..6924f273ced --- /dev/null +++ b/arch/um/drivers/line.c @@ -0,0 +1,681 @@ +/* + * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/sched.h" +#include "linux/slab.h" +#include "linux/list.h" +#include "linux/kd.h" +#include "linux/interrupt.h" +#include "linux/devfs_fs_kernel.h" +#include "asm/uaccess.h" +#include "chan_kern.h" +#include "irq_user.h" +#include "line.h" +#include "kern.h" +#include "user_util.h" +#include "kern_util.h" +#include "os.h" +#include "irq_kern.h" + +#define LINE_BUFSIZE 4096 + +static irqreturn_t line_interrupt(int irq, void *data, struct pt_regs *unused) +{ + struct tty_struct *tty = data; + struct line *line = tty->driver_data; + + if (line) + chan_interrupt(&line->chan_list, &line->task, tty, irq); + return IRQ_HANDLED; +} + +static void line_timer_cb(void *arg) +{ + struct tty_struct *tty = arg; + struct line *line = tty->driver_data; + + line_interrupt(line->driver->read_irq, arg, NULL); +} + +static int write_room(struct line *dev) +{ + int n; + + if (dev->buffer == NULL) + return (LINE_BUFSIZE - 1); + + n = dev->head - dev->tail; + if (n <= 0) + n = LINE_BUFSIZE + n; + return (n - 1); +} + +static int buffer_data(struct line *line, const char *buf, int len) +{ + int end, room; + + if(line->buffer == NULL){ + line->buffer = kmalloc(LINE_BUFSIZE, GFP_ATOMIC); + if (line->buffer == NULL) { + printk("buffer_data - atomic allocation failed\n"); + return(0); + } + line->head = line->buffer; + line->tail = line->buffer; + } + + room = write_room(line); + len = (len > room) ? room : len; + + end = line->buffer + LINE_BUFSIZE - line->tail; + if(len < end){ + memcpy(line->tail, buf, len); + line->tail += len; + } + else { + memcpy(line->tail, buf, end); + buf += end; + memcpy(line->buffer, buf, len - end); + line->tail = line->buffer + len - end; + } + + return(len); +} + +static int flush_buffer(struct line *line) +{ + int n, count; + + if ((line->buffer == NULL) || (line->head == line->tail)) + return(1); + + if (line->tail < line->head) { + count = line->buffer + LINE_BUFSIZE - line->head; + n = write_chan(&line->chan_list, line->head, count, + line->driver->write_irq); + if (n < 0) + return(n); + if (n == count) + line->head = line->buffer; + else { + line->head += n; + return(0); + } + } + + count = line->tail - line->head; + n = write_chan(&line->chan_list, line->head, count, + line->driver->write_irq); + if(n < 0) return(n); + + line->head += n; + return(line->head == line->tail); +} + +int line_write(struct tty_struct *tty, const unsigned char *buf, int len) +{ + struct line *line = tty->driver_data; + unsigned long flags; + int n, err, ret = 0; + + if(tty->stopped) return 0; + + down(&line->sem); + if(line->head != line->tail){ + local_irq_save(flags); + ret = buffer_data(line, buf, len); + err = flush_buffer(line); + local_irq_restore(flags); + if(err <= 0 && (err != -EAGAIN || !ret)) + ret = err; + } + else { + n = write_chan(&line->chan_list, buf, len, + line->driver->write_irq); + if(n < 0){ + ret = n; + goto out_up; + } + + len -= n; + ret += n; + if(len > 0) + ret += buffer_data(line, buf + n, len); + } + out_up: + up(&line->sem); + return(ret); +} + +void line_put_char(struct tty_struct *tty, unsigned char ch) +{ + line_write(tty, &ch, sizeof(ch)); +} + +void line_set_termios(struct tty_struct *tty, struct termios * old) +{ + /* nothing */ +} + +int line_chars_in_buffer(struct tty_struct *tty) +{ + return 0; +} + +static struct { + int cmd; + char *level; + char *name; +} tty_ioctls[] = { + /* don't print these, they flood the log ... */ + { TCGETS, NULL, "TCGETS" }, + { TCSETS, NULL, "TCSETS" }, + { TCSETSW, NULL, "TCSETSW" }, + { TCFLSH, NULL, "TCFLSH" }, + { TCSBRK, NULL, "TCSBRK" }, + + /* general tty stuff */ + { TCSETSF, KERN_DEBUG, "TCSETSF" }, + { TCGETA, KERN_DEBUG, "TCGETA" }, + { TIOCMGET, KERN_DEBUG, "TIOCMGET" }, + { TCSBRKP, KERN_DEBUG, "TCSBRKP" }, + { TIOCMSET, KERN_DEBUG, "TIOCMSET" }, + + /* linux-specific ones */ + { TIOCLINUX, KERN_INFO, "TIOCLINUX" }, + { KDGKBMODE, KERN_INFO, "KDGKBMODE" }, + { KDGKBTYPE, KERN_INFO, "KDGKBTYPE" }, + { KDSIGACCEPT, KERN_INFO, "KDSIGACCEPT" }, +}; + +int line_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + int ret; + int i; + + ret = 0; + switch(cmd) { +#ifdef TIOCGETP + case TIOCGETP: + case TIOCSETP: + case TIOCSETN: +#endif +#ifdef TIOCGETC + case TIOCGETC: + case TIOCSETC: +#endif +#ifdef TIOCGLTC + case TIOCGLTC: + case TIOCSLTC: +#endif + case TCGETS: + case TCSETSF: + case TCSETSW: + case TCSETS: + case TCGETA: + case TCSETAF: + case TCSETAW: + case TCSETA: + case TCXONC: + case TCFLSH: + case TIOCOUTQ: + case TIOCINQ: + case TIOCGLCKTRMIOS: + case TIOCSLCKTRMIOS: + case TIOCPKT: + case TIOCGSOFTCAR: + case TIOCSSOFTCAR: + return -ENOIOCTLCMD; +#if 0 + case TCwhatever: + /* do something */ + break; +#endif + default: + for (i = 0; i < ARRAY_SIZE(tty_ioctls); i++) + if (cmd == tty_ioctls[i].cmd) + break; + if (i < ARRAY_SIZE(tty_ioctls)) { + if (NULL != tty_ioctls[i].level) + printk("%s%s: %s: ioctl %s called\n", + tty_ioctls[i].level, __FUNCTION__, + tty->name, tty_ioctls[i].name); + } else { + printk(KERN_ERR "%s: %s: unknown ioctl: 0x%x\n", + __FUNCTION__, tty->name, cmd); + } + ret = -ENOIOCTLCMD; + break; + } + return(ret); +} + +static irqreturn_t line_write_interrupt(int irq, void *data, + struct pt_regs *unused) +{ + struct tty_struct *tty = data; + struct line *line = tty->driver_data; + int err; + + err = flush_buffer(line); + if(err == 0) + return(IRQ_NONE); + else if(err < 0){ + line->head = line->buffer; + line->tail = line->buffer; + } + + if(tty == NULL) + return(IRQ_NONE); + + if(test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && + (tty->ldisc.write_wakeup != NULL)) + (tty->ldisc.write_wakeup)(tty); + + /* BLOCKING mode + * In blocking mode, everything sleeps on tty->write_wait. + * Sleeping in the console driver would break non-blocking + * writes. + */ + + if(waitqueue_active(&tty->write_wait)) + wake_up_interruptible(&tty->write_wait); + return(IRQ_HANDLED); +} + +int line_setup_irq(int fd, int input, int output, struct tty_struct *tty) +{ + struct line *line = tty->driver_data; + struct line_driver *driver = line->driver; + int err = 0, flags = SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM; + + if(input) err = um_request_irq(driver->read_irq, fd, IRQ_READ, + line_interrupt, flags, + driver->read_irq_name, tty); + if(err) return(err); + if(output) err = um_request_irq(driver->write_irq, fd, IRQ_WRITE, + line_write_interrupt, flags, + driver->write_irq_name, tty); + line->have_irq = 1; + return(err); +} + +void line_disable(struct tty_struct *tty, int current_irq) +{ + struct line *line = tty->driver_data; + + if(!line->have_irq) + return; + + if(line->driver->read_irq == current_irq) + free_irq_later(line->driver->read_irq, tty); + else { + free_irq_by_irq_and_dev(line->driver->read_irq, tty); + free_irq(line->driver->read_irq, tty); + } + + if(line->driver->write_irq == current_irq) + free_irq_later(line->driver->write_irq, tty); + else { + free_irq_by_irq_and_dev(line->driver->write_irq, tty); + free_irq(line->driver->write_irq, tty); + } + + line->have_irq = 0; +} + +int line_open(struct line *lines, struct tty_struct *tty, + struct chan_opts *opts) +{ + struct line *line; + int err = 0; + + line = &lines[tty->index]; + tty->driver_data = line; + + down(&line->sem); + if (tty->count == 1) { + if (!line->valid) { + err = -ENODEV; + goto out; + } + if (list_empty(&line->chan_list)) { + err = parse_chan_pair(line->init_str, &line->chan_list, + line->init_pri, tty->index, opts); + if(err) goto out; + err = open_chan(&line->chan_list); + if(err) goto out; + } + enable_chan(&line->chan_list, tty); + INIT_WORK(&line->task, line_timer_cb, tty); + } + + if(!line->sigio){ + chan_enable_winch(&line->chan_list, tty); + line->sigio = 1; + } + chan_window_size(&line->chan_list, &tty->winsize.ws_row, + &tty->winsize.ws_col); + line->count++; + +out: + up(&line->sem); + return(err); +} + +void line_close(struct tty_struct *tty, struct file * filp) +{ + struct line *line = tty->driver_data; + + down(&line->sem); + line->count--; + if (tty->count == 1) { + line_disable(tty, -1); + tty->driver_data = NULL; + } + up(&line->sem); +} + +void close_lines(struct line *lines, int nlines) +{ + int i; + + for(i = 0; i < nlines; i++) + close_chan(&lines[i].chan_list); +} + +int line_setup(struct line *lines, int num, char *init, int all_allowed) +{ + int i, n; + char *end; + + if(*init == '=') n = -1; + else { + n = simple_strtoul(init, &end, 0); + if(*end != '='){ + printk(KERN_ERR "line_setup failed to parse \"%s\"\n", + init); + return(0); + } + init = end; + } + init++; + if((n >= 0) && (n >= num)){ + printk("line_setup - %d out of range ((0 ... %d) allowed)\n", + n, num - 1); + return(0); + } + else if (n >= 0){ + if (lines[n].count > 0) { + printk("line_setup - device %d is open\n", n); + return(0); + } + if (lines[n].init_pri <= INIT_ONE){ + lines[n].init_pri = INIT_ONE; + if (!strcmp(init, "none")) + lines[n].valid = 0; + else { + lines[n].init_str = init; + lines[n].valid = 1; + } + } + } + else if(!all_allowed){ + printk("line_setup - can't configure all devices from " + "mconsole\n"); + return(0); + } + else { + for(i = 0; i < num; i++){ + if(lines[i].init_pri <= INIT_ALL){ + lines[i].init_pri = INIT_ALL; + if(!strcmp(init, "none")) lines[i].valid = 0; + else { + lines[i].init_str = init; + lines[i].valid = 1; + } + } + } + } + return(1); +} + +int line_config(struct line *lines, int num, char *str) +{ + char *new = uml_strdup(str); + + if(new == NULL){ + printk("line_config - uml_strdup failed\n"); + return(-ENOMEM); + } + return(!line_setup(lines, num, new, 0)); +} + +int line_get_config(char *name, struct line *lines, int num, char *str, + int size, char **error_out) +{ + struct line *line; + char *end; + int dev, n = 0; + + dev = simple_strtoul(name, &end, 0); + if((*end != '\0') || (end == name)){ + *error_out = "line_get_config failed to parse device number"; + return(0); + } + + if((dev < 0) || (dev >= num)){ + *error_out = "device number of of range"; + return(0); + } + + line = &lines[dev]; + + down(&line->sem); + if(!line->valid) + CONFIG_CHUNK(str, size, n, "none", 1); + else if(line->count == 0) + CONFIG_CHUNK(str, size, n, line->init_str, 1); + else n = chan_config_string(&line->chan_list, str, size, error_out); + up(&line->sem); + + return(n); +} + +int line_remove(struct line *lines, int num, char *str) +{ + char config[sizeof("conxxxx=none\0")]; + + sprintf(config, "%s=none", str); + return(!line_setup(lines, num, config, 0)); +} + +int line_write_room(struct tty_struct *tty) +{ + struct line *dev = tty->driver_data; + int room; + + if (tty->stopped) + return 0; + room = write_room(dev); + if (0 == room) + printk(KERN_DEBUG "%s: %s: no room left in buffer\n", + __FUNCTION__,tty->name); + return room; +} + +struct tty_driver *line_register_devfs(struct lines *set, + struct line_driver *line_driver, + struct tty_operations *ops, struct line *lines, + int nlines) +{ + int i; + struct tty_driver *driver = alloc_tty_driver(nlines); + + if (!driver) + return NULL; + + driver->driver_name = line_driver->name; + driver->name = line_driver->device_name; + driver->devfs_name = line_driver->devfs_name; + driver->major = line_driver->major; + driver->minor_start = line_driver->minor_start; + driver->type = line_driver->type; + driver->subtype = line_driver->subtype; + driver->flags = TTY_DRIVER_REAL_RAW; + driver->init_termios = tty_std_termios; + tty_set_operations(driver, ops); + + if (tty_register_driver(driver)) { + printk("%s: can't register %s driver\n", + __FUNCTION__,line_driver->name); + put_tty_driver(driver); + return NULL; + } + + for(i = 0; i < nlines; i++){ + if(!lines[i].valid) + tty_unregister_device(driver, i); + } + + mconsole_register_dev(&line_driver->mc); + return driver; +} + +void lines_init(struct line *lines, int nlines) +{ + struct line *line; + int i; + + for(i = 0; i < nlines; i++){ + line = &lines[i]; + INIT_LIST_HEAD(&line->chan_list); + sema_init(&line->sem, 1); + if(line->init_str != NULL){ + line->init_str = uml_strdup(line->init_str); + if(line->init_str == NULL) + printk("lines_init - uml_strdup returned " + "NULL\n"); + } + } +} + +struct winch { + struct list_head list; + int fd; + int tty_fd; + int pid; + struct tty_struct *tty; +}; + +irqreturn_t winch_interrupt(int irq, void *data, struct pt_regs *unused) +{ + struct winch *winch = data; + struct tty_struct *tty; + struct line *line; + int err; + char c; + + if(winch->fd != -1){ + err = generic_read(winch->fd, &c, NULL); + if(err < 0){ + if(err != -EAGAIN){ + printk("winch_interrupt : read failed, " + "errno = %d\n", -err); + printk("fd %d is losing SIGWINCH support\n", + winch->tty_fd); + return(IRQ_HANDLED); + } + goto out; + } + } + tty = winch->tty; + if (tty != NULL) { + line = tty->driver_data; + chan_window_size(&line->chan_list, + &tty->winsize.ws_row, + &tty->winsize.ws_col); + kill_pg(tty->pgrp, SIGWINCH, 1); + } + out: + if(winch->fd != -1) + reactivate_fd(winch->fd, WINCH_IRQ); + return(IRQ_HANDLED); +} + +DECLARE_MUTEX(winch_handler_sem); +LIST_HEAD(winch_handlers); + +void register_winch_irq(int fd, int tty_fd, int pid, struct tty_struct *tty) +{ + struct winch *winch; + + down(&winch_handler_sem); + winch = kmalloc(sizeof(*winch), GFP_KERNEL); + if (winch == NULL) { + printk("register_winch_irq - kmalloc failed\n"); + goto out; + } + *winch = ((struct winch) { .list = LIST_HEAD_INIT(winch->list), + .fd = fd, + .tty_fd = tty_fd, + .pid = pid, + .tty = tty }); + list_add(&winch->list, &winch_handlers); + if(um_request_irq(WINCH_IRQ, fd, IRQ_READ, winch_interrupt, + SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM, + "winch", winch) < 0) + printk("register_winch_irq - failed to register IRQ\n"); + out: + up(&winch_handler_sem); +} + +static void winch_cleanup(void) +{ + struct list_head *ele; + struct winch *winch; + + list_for_each(ele, &winch_handlers){ + winch = list_entry(ele, struct winch, list); + if(winch->fd != -1){ + deactivate_fd(winch->fd, WINCH_IRQ); + os_close_file(winch->fd); + } + if(winch->pid != -1) + os_kill_process(winch->pid, 1); + } +} +__uml_exitcall(winch_cleanup); + +char *add_xterm_umid(char *base) +{ + char *umid, *title; + int len; + + umid = get_umid(1); + if(umid == NULL) return(base); + + len = strlen(base) + strlen(" ()") + strlen(umid) + 1; + title = kmalloc(len, GFP_KERNEL); + if(title == NULL){ + printk("Failed to allocate buffer for xterm title\n"); + return(base); + } + + snprintf(title, len, "%s (%s)", base, umid); + return(title); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/mcast.h b/arch/um/drivers/mcast.h new file mode 100644 index 00000000000..a2c6db24345 --- /dev/null +++ b/arch/um/drivers/mcast.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "net_user.h" + +struct mcast_data { + char *addr; + unsigned short port; + void *mcast_addr; + int ttl; + void *dev; +}; + +extern struct net_user_info mcast_user_info; + +extern int mcast_user_write(int fd, void *buf, int len, + struct mcast_data *pri); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/mcast_kern.c b/arch/um/drivers/mcast_kern.c new file mode 100644 index 00000000000..faf714e87b5 --- /dev/null +++ b/arch/um/drivers/mcast_kern.c @@ -0,0 +1,143 @@ +/* + * user-mode-linux networking multicast transport + * Copyright (C) 2001 by Harald Welte <laforge@gnumonks.org> + * + * based on the existing uml-networking code, which is + * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and + * James Leu (jleu@mindspring.net). + * Copyright (C) 2001 by various other people who didn't put their name here. + * + * Licensed under the GPL. + */ + +#include "linux/kernel.h" +#include "linux/init.h" +#include "linux/netdevice.h" +#include "linux/etherdevice.h" +#include "linux/in.h" +#include "linux/inet.h" +#include "net_kern.h" +#include "net_user.h" +#include "mcast.h" + +struct mcast_init { + char *addr; + int port; + int ttl; +}; + +void mcast_init(struct net_device *dev, void *data) +{ + struct uml_net_private *pri; + struct mcast_data *dpri; + struct mcast_init *init = data; + + pri = dev->priv; + dpri = (struct mcast_data *) pri->user; + dpri->addr = init->addr; + dpri->port = init->port; + dpri->ttl = init->ttl; + dpri->dev = dev; + + printk("mcast backend "); + printk("multicast adddress: %s:%u, TTL:%u ", + dpri->addr, dpri->port, dpri->ttl); + + printk("\n"); +} + +static int mcast_read(int fd, struct sk_buff **skb, struct uml_net_private *lp) +{ + *skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER); + if(*skb == NULL) return(-ENOMEM); + return(net_recvfrom(fd, (*skb)->mac.raw, + (*skb)->dev->mtu + ETH_HEADER_OTHER)); +} + +static int mcast_write(int fd, struct sk_buff **skb, + struct uml_net_private *lp) +{ + return mcast_user_write(fd, (*skb)->data, (*skb)->len, + (struct mcast_data *) &lp->user); +} + +static struct net_kern_info mcast_kern_info = { + .init = mcast_init, + .protocol = eth_protocol, + .read = mcast_read, + .write = mcast_write, +}; + +int mcast_setup(char *str, char **mac_out, void *data) +{ + struct mcast_init *init = data; + char *port_str = NULL, *ttl_str = NULL, *remain; + char *last; + int n; + + *init = ((struct mcast_init) + { .addr = "239.192.168.1", + .port = 1102, + .ttl = 1 }); + + remain = split_if_spec(str, mac_out, &init->addr, &port_str, &ttl_str, + NULL); + if(remain != NULL){ + printk(KERN_ERR "mcast_setup - Extra garbage on " + "specification : '%s'\n", remain); + return(0); + } + + if(port_str != NULL){ + n = simple_strtoul(port_str, &last, 10); + if((*last != '\0') || (last == port_str)){ + printk(KERN_ERR "mcast_setup - Bad port : '%s'\n", + port_str); + return(0); + } + init->port = htons(n); + } + + if(ttl_str != NULL){ + init->ttl = simple_strtoul(ttl_str, &last, 10); + if((*last != '\0') || (last == ttl_str)){ + printk(KERN_ERR "mcast_setup - Bad ttl : '%s'\n", + ttl_str); + return(0); + } + } + + printk(KERN_INFO "Configured mcast device: %s:%u-%u\n", init->addr, + init->port, init->ttl); + + return(1); +} + +static struct transport mcast_transport = { + .list = LIST_HEAD_INIT(mcast_transport.list), + .name = "mcast", + .setup = mcast_setup, + .user = &mcast_user_info, + .kern = &mcast_kern_info, + .private_size = sizeof(struct mcast_data), + .setup_size = sizeof(struct mcast_init), +}; + +static int register_mcast(void) +{ + register_transport(&mcast_transport); + return(1); +} + +__initcall(register_mcast); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/mcast_user.c b/arch/um/drivers/mcast_user.c new file mode 100644 index 00000000000..0fe1d9fa913 --- /dev/null +++ b/arch/um/drivers/mcast_user.c @@ -0,0 +1,177 @@ +/* + * user-mode-linux networking multicast transport + * Copyright (C) 2001 by Harald Welte <laforge@gnumonks.org> + * + * based on the existing uml-networking code, which is + * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and + * James Leu (jleu@mindspring.net). + * Copyright (C) 2001 by various other people who didn't put their name here. + * + * Licensed under the GPL. + * + */ + +#include <errno.h> +#include <unistd.h> +#include <linux/inet.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/time.h> +#include <netinet/in.h> +#include "net_user.h" +#include "mcast.h" +#include "kern_util.h" +#include "user_util.h" +#include "user.h" +#include "os.h" + +#define MAX_PACKET (ETH_MAX_PACKET + ETH_HEADER_OTHER) + +static struct sockaddr_in *new_addr(char *addr, unsigned short port) +{ + struct sockaddr_in *sin; + + sin = um_kmalloc(sizeof(struct sockaddr_in)); + if(sin == NULL){ + printk("new_addr: allocation of sockaddr_in failed\n"); + return(NULL); + } + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = in_aton(addr); + sin->sin_port = port; + return(sin); +} + +static void mcast_user_init(void *data, void *dev) +{ + struct mcast_data *pri = data; + + pri->mcast_addr = new_addr(pri->addr, pri->port); + pri->dev = dev; +} + +static int mcast_open(void *data) +{ + struct mcast_data *pri = data; + struct sockaddr_in *sin = pri->mcast_addr; + struct ip_mreq mreq; + int fd, yes = 1; + + + if ((sin->sin_addr.s_addr == 0) || (sin->sin_port == 0)) { + fd = -EINVAL; + goto out; + } + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0){ + printk("mcast_open : data socket failed, errno = %d\n", + errno); + fd = -ENOMEM; + goto out; + } + + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) { + printk("mcast_open: SO_REUSEADDR failed, errno = %d\n", + errno); + os_close_file(fd); + fd = -EINVAL; + goto out; + } + + /* set ttl according to config */ + if (setsockopt(fd, SOL_IP, IP_MULTICAST_TTL, &pri->ttl, + sizeof(pri->ttl)) < 0) { + printk("mcast_open: IP_MULTICAST_TTL failed, error = %d\n", + errno); + os_close_file(fd); + fd = -EINVAL; + goto out; + } + + /* set LOOP, so data does get fed back to local sockets */ + if (setsockopt(fd, SOL_IP, IP_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) { + printk("mcast_open: IP_MULTICAST_LOOP failed, error = %d\n", + errno); + os_close_file(fd); + fd = -EINVAL; + goto out; + } + + /* bind socket to mcast address */ + if (bind(fd, (struct sockaddr *) sin, sizeof(*sin)) < 0) { + printk("mcast_open : data bind failed, errno = %d\n", errno); + os_close_file(fd); + fd = -EINVAL; + goto out; + } + + /* subscribe to the multicast group */ + mreq.imr_multiaddr.s_addr = sin->sin_addr.s_addr; + mreq.imr_interface.s_addr = 0; + if (setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP, + &mreq, sizeof(mreq)) < 0) { + printk("mcast_open: IP_ADD_MEMBERSHIP failed, error = %d\n", + errno); + printk("There appears not to be a multicast-capable network " + "interface on the host.\n"); + printk("eth0 should be configured in order to use the " + "multicast transport.\n"); + os_close_file(fd); + fd = -EINVAL; + } + + out: + return(fd); +} + +static void mcast_close(int fd, void *data) +{ + struct ip_mreq mreq; + struct mcast_data *pri = data; + struct sockaddr_in *sin = pri->mcast_addr; + + mreq.imr_multiaddr.s_addr = sin->sin_addr.s_addr; + mreq.imr_interface.s_addr = 0; + if (setsockopt(fd, SOL_IP, IP_DROP_MEMBERSHIP, + &mreq, sizeof(mreq)) < 0) { + printk("mcast_open: IP_DROP_MEMBERSHIP failed, error = %d\n", + errno); + } + + os_close_file(fd); +} + +int mcast_user_write(int fd, void *buf, int len, struct mcast_data *pri) +{ + struct sockaddr_in *data_addr = pri->mcast_addr; + + return(net_sendto(fd, buf, len, data_addr, sizeof(*data_addr))); +} + +static int mcast_set_mtu(int mtu, void *data) +{ + return(mtu); +} + +struct net_user_info mcast_user_info = { + .init = mcast_user_init, + .open = mcast_open, + .close = mcast_close, + .remove = NULL, + .set_mtu = mcast_set_mtu, + .add_address = NULL, + .delete_address = NULL, + .max_packet = MAX_PACKET - ETH_HEADER_OTHER +}; + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/mconsole_kern.c b/arch/um/drivers/mconsole_kern.c new file mode 100644 index 00000000000..d7c7adcc0a6 --- /dev/null +++ b/arch/um/drivers/mconsole_kern.c @@ -0,0 +1,619 @@ +/* + * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) + * Copyright (C) 2001 - 2003 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#include "linux/kernel.h" +#include "linux/slab.h" +#include "linux/init.h" +#include "linux/notifier.h" +#include "linux/reboot.h" +#include "linux/utsname.h" +#include "linux/ctype.h" +#include "linux/interrupt.h" +#include "linux/sysrq.h" +#include "linux/workqueue.h" +#include "linux/module.h" +#include "linux/file.h" +#include "linux/fs.h" +#include "linux/namei.h" +#include "linux/proc_fs.h" +#include "linux/syscalls.h" +#include "asm/irq.h" +#include "asm/uaccess.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" +#include "mconsole.h" +#include "mconsole_kern.h" +#include "irq_user.h" +#include "init.h" +#include "os.h" +#include "umid.h" +#include "irq_kern.h" + +static int do_unlink_socket(struct notifier_block *notifier, + unsigned long what, void *data) +{ + return(mconsole_unlink_socket()); +} + + +static struct notifier_block reboot_notifier = { + .notifier_call = do_unlink_socket, + .priority = 0, +}; + +/* Safe without explicit locking for now. Tasklets provide their own + * locking, and the interrupt handler is safe because it can't interrupt + * itself and it can only happen on CPU 0. + */ + +LIST_HEAD(mc_requests); + +static void mc_work_proc(void *unused) +{ + struct mconsole_entry *req; + unsigned long flags; + + while(!list_empty(&mc_requests)){ + local_save_flags(flags); + req = list_entry(mc_requests.next, struct mconsole_entry, + list); + list_del(&req->list); + local_irq_restore(flags); + req->request.cmd->handler(&req->request); + kfree(req); + } +} + +DECLARE_WORK(mconsole_work, mc_work_proc, NULL); + +static irqreturn_t mconsole_interrupt(int irq, void *dev_id, + struct pt_regs *regs) +{ + /* long to avoid size mismatch warnings from gcc */ + long fd; + struct mconsole_entry *new; + struct mc_request req; + + fd = (long) dev_id; + while (mconsole_get_request(fd, &req)){ + if(req.cmd->context == MCONSOLE_INTR) + (*req.cmd->handler)(&req); + else { + new = kmalloc(sizeof(*new), GFP_ATOMIC); + if(new == NULL) + mconsole_reply(&req, "Out of memory", 1, 0); + else { + new->request = req; + list_add(&new->list, &mc_requests); + } + } + } + if(!list_empty(&mc_requests)) + schedule_work(&mconsole_work); + reactivate_fd(fd, MCONSOLE_IRQ); + return(IRQ_HANDLED); +} + +void mconsole_version(struct mc_request *req) +{ + char version[256]; + + sprintf(version, "%s %s %s %s %s", system_utsname.sysname, + system_utsname.nodename, system_utsname.release, + system_utsname.version, system_utsname.machine); + mconsole_reply(req, version, 0, 0); +} + +void mconsole_log(struct mc_request *req) +{ + int len; + char *ptr = req->request.data; + + ptr += strlen("log "); + + len = req->len - (ptr - req->request.data); + printk("%.*s", len, ptr); + mconsole_reply(req, "", 0, 0); +} + +/* This is a more convoluted version of mconsole_proc, which has some stability + * problems; however, we need it fixed, because it is expected that UML users + * mount HPPFS instead of procfs on /proc. And we want mconsole_proc to still + * show the real procfs content, not the ones from hppfs.*/ +#if 0 +void mconsole_proc(struct mc_request *req) +{ + struct nameidata nd; + struct file_system_type *proc; + struct super_block *super; + struct file *file; + int n, err; + char *ptr = req->request.data, *buf; + + ptr += strlen("proc"); + while(isspace(*ptr)) ptr++; + + proc = get_fs_type("proc"); + if(proc == NULL){ + mconsole_reply(req, "procfs not registered", 1, 0); + goto out; + } + + super = (*proc->get_sb)(proc, 0, NULL, NULL); + put_filesystem(proc); + if(super == NULL){ + mconsole_reply(req, "Failed to get procfs superblock", 1, 0); + goto out; + } + up_write(&super->s_umount); + + nd.dentry = super->s_root; + nd.mnt = NULL; + nd.flags = O_RDONLY + 1; + nd.last_type = LAST_ROOT; + + /* START: it was experienced that the stability problems are closed + * if commenting out these two calls + the below read cycle. To + * make UML crash again, it was enough to readd either one.*/ + err = link_path_walk(ptr, &nd); + if(err){ + mconsole_reply(req, "Failed to look up file", 1, 0); + goto out_kill; + } + + file = dentry_open(nd.dentry, nd.mnt, O_RDONLY); + if(IS_ERR(file)){ + mconsole_reply(req, "Failed to open file", 1, 0); + goto out_kill; + } + /*END*/ + + buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if(buf == NULL){ + mconsole_reply(req, "Failed to allocate buffer", 1, 0); + goto out_fput; + } + + if((file->f_op != NULL) && (file->f_op->read != NULL)){ + do { + n = (*file->f_op->read)(file, buf, PAGE_SIZE - 1, + &file->f_pos); + if(n >= 0){ + buf[n] = '\0'; + mconsole_reply(req, buf, 0, (n > 0)); + } + else { + mconsole_reply(req, "Read of file failed", + 1, 0); + goto out_free; + } + } while(n > 0); + } + else mconsole_reply(req, "", 0, 0); + + out_free: + kfree(buf); + out_fput: + fput(file); + out_kill: + deactivate_super(super); + out: ; +} +#endif + +void mconsole_proc(struct mc_request *req) +{ + char path[64]; + char *buf; + int len; + int fd; + int first_chunk = 1; + char *ptr = req->request.data; + + ptr += strlen("proc"); + while(isspace(*ptr)) ptr++; + snprintf(path, sizeof(path), "/proc/%s", ptr); + + fd = sys_open(path, 0, 0); + if (fd < 0) { + mconsole_reply(req, "Failed to open file", 1, 0); + printk("open %s: %d\n",path,fd); + goto out; + } + + buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if(buf == NULL){ + mconsole_reply(req, "Failed to allocate buffer", 1, 0); + goto out_close; + } + + for (;;) { + len = sys_read(fd, buf, PAGE_SIZE-1); + if (len < 0) { + mconsole_reply(req, "Read of file failed", 1, 0); + goto out_free; + } + /*Begin the file content on his own line.*/ + if (first_chunk) { + mconsole_reply(req, "\n", 0, 1); + first_chunk = 0; + } + if (len == PAGE_SIZE-1) { + buf[len] = '\0'; + mconsole_reply(req, buf, 0, 1); + } else { + buf[len] = '\0'; + mconsole_reply(req, buf, 0, 0); + break; + } + } + + out_free: + kfree(buf); + out_close: + sys_close(fd); + out: + /* nothing */; +} + +#define UML_MCONSOLE_HELPTEXT \ +"Commands: \n\ + version - Get kernel version \n\ + help - Print this message \n\ + halt - Halt UML \n\ + reboot - Reboot UML \n\ + config <dev>=<config> - Add a new device to UML; \n\ + same syntax as command line \n\ + config <dev> - Query the configuration of a device \n\ + remove <dev> - Remove a device from UML \n\ + sysrq <letter> - Performs the SysRq action controlled by the letter \n\ + cad - invoke the Ctl-Alt-Del handler \n\ + stop - pause the UML; it will do nothing until it receives a 'go' \n\ + go - continue the UML after a 'stop' \n\ + log <string> - make UML enter <string> into the kernel log\n\ + proc <file> - returns the contents of the UML's /proc/<file>\n\ +" + +void mconsole_help(struct mc_request *req) +{ + mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0); +} + +void mconsole_halt(struct mc_request *req) +{ + mconsole_reply(req, "", 0, 0); + machine_halt(); +} + +void mconsole_reboot(struct mc_request *req) +{ + mconsole_reply(req, "", 0, 0); + machine_restart(NULL); +} + +extern void ctrl_alt_del(void); + +void mconsole_cad(struct mc_request *req) +{ + mconsole_reply(req, "", 0, 0); + ctrl_alt_del(); +} + +void mconsole_go(struct mc_request *req) +{ + mconsole_reply(req, "Not stopped", 1, 0); +} + +void mconsole_stop(struct mc_request *req) +{ + deactivate_fd(req->originating_fd, MCONSOLE_IRQ); + os_set_fd_block(req->originating_fd, 1); + mconsole_reply(req, "", 0, 0); + while(mconsole_get_request(req->originating_fd, req)){ + if(req->cmd->handler == mconsole_go) break; + (*req->cmd->handler)(req); + } + os_set_fd_block(req->originating_fd, 0); + reactivate_fd(req->originating_fd, MCONSOLE_IRQ); + mconsole_reply(req, "", 0, 0); +} + +/* This list is populated by __initcall routines. */ + +LIST_HEAD(mconsole_devices); + +void mconsole_register_dev(struct mc_device *new) +{ + list_add(&new->list, &mconsole_devices); +} + +static struct mc_device *mconsole_find_dev(char *name) +{ + struct list_head *ele; + struct mc_device *dev; + + list_for_each(ele, &mconsole_devices){ + dev = list_entry(ele, struct mc_device, list); + if(!strncmp(name, dev->name, strlen(dev->name))) + return(dev); + } + return(NULL); +} + +#define CONFIG_BUF_SIZE 64 + +static void mconsole_get_config(int (*get_config)(char *, char *, int, + char **), + struct mc_request *req, char *name) +{ + char default_buf[CONFIG_BUF_SIZE], *error, *buf; + int n, size; + + if(get_config == NULL){ + mconsole_reply(req, "No get_config routine defined", 1, 0); + return; + } + + error = NULL; + size = sizeof(default_buf)/sizeof(default_buf[0]); + buf = default_buf; + + while(1){ + n = (*get_config)(name, buf, size, &error); + if(error != NULL){ + mconsole_reply(req, error, 1, 0); + goto out; + } + + if(n <= size){ + mconsole_reply(req, buf, 0, 0); + goto out; + } + + if(buf != default_buf) + kfree(buf); + + size = n; + buf = kmalloc(size, GFP_KERNEL); + if(buf == NULL){ + mconsole_reply(req, "Failed to allocate buffer", 1, 0); + return; + } + } + out: + if(buf != default_buf) + kfree(buf); + +} + +void mconsole_config(struct mc_request *req) +{ + struct mc_device *dev; + char *ptr = req->request.data, *name; + int err; + + ptr += strlen("config"); + while(isspace(*ptr)) ptr++; + dev = mconsole_find_dev(ptr); + if(dev == NULL){ + mconsole_reply(req, "Bad configuration option", 1, 0); + return; + } + + name = &ptr[strlen(dev->name)]; + ptr = name; + while((*ptr != '=') && (*ptr != '\0')) + ptr++; + + if(*ptr == '='){ + err = (*dev->config)(name); + mconsole_reply(req, "", err, 0); + } + else mconsole_get_config(dev->get_config, req, name); +} + +void mconsole_remove(struct mc_request *req) +{ + struct mc_device *dev; + char *ptr = req->request.data; + int err; + + ptr += strlen("remove"); + while(isspace(*ptr)) ptr++; + dev = mconsole_find_dev(ptr); + if(dev == NULL){ + mconsole_reply(req, "Bad remove option", 1, 0); + return; + } + err = (*dev->remove)(&ptr[strlen(dev->name)]); + mconsole_reply(req, "", err, 0); +} + +#ifdef CONFIG_MAGIC_SYSRQ +void mconsole_sysrq(struct mc_request *req) +{ + char *ptr = req->request.data; + + ptr += strlen("sysrq"); + while(isspace(*ptr)) ptr++; + + mconsole_reply(req, "", 0, 0); + handle_sysrq(*ptr, ¤t->thread.regs, NULL); +} +#else +void mconsole_sysrq(struct mc_request *req) +{ + mconsole_reply(req, "Sysrq not compiled in", 1, 0); +} +#endif + +/* Changed by mconsole_setup, which is __setup, and called before SMP is + * active. + */ +static char *notify_socket = NULL; + +int mconsole_init(void) +{ + /* long to avoid size mismatch warnings from gcc */ + long sock; + int err; + char file[256]; + + if(umid_file_name("mconsole", file, sizeof(file))) return(-1); + snprintf(mconsole_socket_name, sizeof(file), "%s", file); + + sock = os_create_unix_socket(file, sizeof(file), 1); + if (sock < 0){ + printk("Failed to initialize management console\n"); + return(1); + } + + register_reboot_notifier(&reboot_notifier); + + err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt, + SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM, + "mconsole", (void *)sock); + if (err){ + printk("Failed to get IRQ for management console\n"); + return(1); + } + + if(notify_socket != NULL){ + notify_socket = uml_strdup(notify_socket); + if(notify_socket != NULL) + mconsole_notify(notify_socket, MCONSOLE_SOCKET, + mconsole_socket_name, + strlen(mconsole_socket_name) + 1); + else printk(KERN_ERR "mconsole_setup failed to strdup " + "string\n"); + } + + printk("mconsole (version %d) initialized on %s\n", + MCONSOLE_VERSION, mconsole_socket_name); + return(0); +} + +__initcall(mconsole_init); + +static int write_proc_mconsole(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + char *buf; + + buf = kmalloc(count + 1, GFP_KERNEL); + if(buf == NULL) + return(-ENOMEM); + + if(copy_from_user(buf, buffer, count)){ + count = -EFAULT; + goto out; + } + + buf[count] = '\0'; + + mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count); + out: + kfree(buf); + return(count); +} + +static int create_proc_mconsole(void) +{ + struct proc_dir_entry *ent; + + if(notify_socket == NULL) return(0); + + ent = create_proc_entry("mconsole", S_IFREG | 0200, NULL); + if(ent == NULL){ + printk("create_proc_mconsole : create_proc_entry failed\n"); + return(0); + } + + ent->read_proc = NULL; + ent->write_proc = write_proc_mconsole; + return(0); +} + +static DEFINE_SPINLOCK(notify_spinlock); + +void lock_notify(void) +{ + spin_lock(¬ify_spinlock); +} + +void unlock_notify(void) +{ + spin_unlock(¬ify_spinlock); +} + +__initcall(create_proc_mconsole); + +#define NOTIFY "=notify:" + +static int mconsole_setup(char *str) +{ + if(!strncmp(str, NOTIFY, strlen(NOTIFY))){ + str += strlen(NOTIFY); + notify_socket = str; + } + else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str); + return(1); +} + +__setup("mconsole", mconsole_setup); + +__uml_help(mconsole_setup, +"mconsole=notify:<socket>\n" +" Requests that the mconsole driver send a message to the named Unix\n" +" socket containing the name of the mconsole socket. This also serves\n" +" to notify outside processes when UML has booted far enough to respond\n" +" to mconsole requests.\n\n" +); + +static int notify_panic(struct notifier_block *self, unsigned long unused1, + void *ptr) +{ + char *message = ptr; + + if(notify_socket == NULL) return(0); + + mconsole_notify(notify_socket, MCONSOLE_PANIC, message, + strlen(message) + 1); + return(0); +} + +static struct notifier_block panic_exit_notifier = { + .notifier_call = notify_panic, + .next = NULL, + .priority = 1 +}; + +static int add_notifier(void) +{ + notifier_chain_register(&panic_notifier_list, &panic_exit_notifier); + return(0); +} + +__initcall(add_notifier); + +char *mconsole_notify_socket(void) +{ + return(notify_socket); +} + +EXPORT_SYMBOL(mconsole_notify_socket); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/mconsole_user.c b/arch/um/drivers/mconsole_user.c new file mode 100644 index 00000000000..fe5afb13252 --- /dev/null +++ b/arch/um/drivers/mconsole_user.c @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) + * Copyright (C) 2001 - 2003 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <signal.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/uio.h> +#include <sys/un.h> +#include <unistd.h> +#include "user.h" +#include "mconsole.h" +#include "umid.h" + +static struct mconsole_command commands[] = { + { "version", mconsole_version, MCONSOLE_INTR }, + { "halt", mconsole_halt, MCONSOLE_PROC }, + { "reboot", mconsole_reboot, MCONSOLE_PROC }, + { "config", mconsole_config, MCONSOLE_PROC }, + { "remove", mconsole_remove, MCONSOLE_PROC }, + { "sysrq", mconsole_sysrq, MCONSOLE_INTR }, + { "help", mconsole_help, MCONSOLE_INTR }, + { "cad", mconsole_cad, MCONSOLE_INTR }, + { "stop", mconsole_stop, MCONSOLE_PROC }, + { "go", mconsole_go, MCONSOLE_INTR }, + { "log", mconsole_log, MCONSOLE_INTR }, + { "proc", mconsole_proc, MCONSOLE_PROC }, +}; + +/* Initialized in mconsole_init, which is an initcall */ +char mconsole_socket_name[256]; + +int mconsole_reply_v0(struct mc_request *req, char *reply) +{ + struct iovec iov; + struct msghdr msg; + + iov.iov_base = reply; + iov.iov_len = strlen(reply); + + msg.msg_name = &(req->origin); + msg.msg_namelen = req->originlen; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + return sendmsg(req->originating_fd, &msg, 0); +} + +static struct mconsole_command *mconsole_parse(struct mc_request *req) +{ + struct mconsole_command *cmd; + int i; + + for(i=0;i<sizeof(commands)/sizeof(commands[0]);i++){ + cmd = &commands[i]; + if(!strncmp(req->request.data, cmd->command, + strlen(cmd->command))){ + return(cmd); + } + } + return(NULL); +} + +#define MIN(a,b) ((a)<(b) ? (a):(b)) + +#define STRINGX(x) #x +#define STRING(x) STRINGX(x) + +int mconsole_get_request(int fd, struct mc_request *req) +{ + int len; + + req->originlen = sizeof(req->origin); + req->len = recvfrom(fd, &req->request, sizeof(req->request), 0, + (struct sockaddr *) req->origin, &req->originlen); + if (req->len < 0) + return 0; + + req->originating_fd = fd; + + if(req->request.magic != MCONSOLE_MAGIC){ + /* Unversioned request */ + len = MIN(sizeof(req->request.data) - 1, + strlen((char *) &req->request)); + memmove(req->request.data, &req->request, len); + req->request.data[len] = '\0'; + + req->request.magic = MCONSOLE_MAGIC; + req->request.version = 0; + req->request.len = len; + + mconsole_reply_v0(req, "ERR Version 0 mconsole clients are " + "not supported by this driver"); + return(0); + } + + if(req->request.len >= MCONSOLE_MAX_DATA){ + mconsole_reply(req, "Request too large", 1, 0); + return(0); + } + if(req->request.version != MCONSOLE_VERSION){ + mconsole_reply(req, "This driver only supports version " + STRING(MCONSOLE_VERSION) " clients", 1, 0); + } + + req->request.data[req->request.len] = '\0'; + req->cmd = mconsole_parse(req); + if(req->cmd == NULL){ + mconsole_reply(req, "Unknown command", 1, 0); + return(0); + } + + return(1); +} + +int mconsole_reply(struct mc_request *req, char *str, int err, int more) +{ + struct mconsole_reply reply; + int total, len, n; + + total = strlen(str); + do { + reply.err = err; + + /* err can only be true on the first packet */ + err = 0; + + len = MIN(total, MCONSOLE_MAX_DATA - 1); + + if(len == total) reply.more = more; + else reply.more = 1; + + memcpy(reply.data, str, len); + reply.data[len] = '\0'; + total -= len; + str += len; + reply.len = len + 1; + + len = sizeof(reply) + reply.len - sizeof(reply.data); + + n = sendto(req->originating_fd, &reply, len, 0, + (struct sockaddr *) req->origin, req->originlen); + + if(n < 0) return(-errno); + } while(total > 0); + return(0); +} + +int mconsole_unlink_socket(void) +{ + unlink(mconsole_socket_name); + return 0; +} + +static int notify_sock = -1; + +int mconsole_notify(char *sock_name, int type, const void *data, int len) +{ + struct sockaddr_un target; + struct mconsole_notify packet; + int n, err = 0; + + lock_notify(); + if(notify_sock < 0){ + notify_sock = socket(PF_UNIX, SOCK_DGRAM, 0); + if(notify_sock < 0){ + printk("mconsole_notify - socket failed, errno = %d\n", + errno); + err = -errno; + } + } + unlock_notify(); + + if(err) + return(err); + + target.sun_family = AF_UNIX; + strcpy(target.sun_path, sock_name); + + packet.magic = MCONSOLE_MAGIC; + packet.version = MCONSOLE_VERSION; + packet.type = type; + len = (len > sizeof(packet.data)) ? sizeof(packet.data) : len; + packet.len = len; + memcpy(packet.data, data, len); + + err = 0; + len = sizeof(packet) + packet.len - sizeof(packet.data); + n = sendto(notify_sock, &packet, len, 0, (struct sockaddr *) &target, + sizeof(target)); + if(n < 0){ + printk("mconsole_notify - sendto failed, errno = %d\n", errno); + err = -errno; + } + return(err); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/mmapper_kern.c b/arch/um/drivers/mmapper_kern.c new file mode 100644 index 00000000000..a63231dffe0 --- /dev/null +++ b/arch/um/drivers/mmapper_kern.c @@ -0,0 +1,150 @@ +/* + * arch/um/drivers/mmapper_kern.c + * + * BRIEF MODULE DESCRIPTION + * + * Copyright (C) 2000 RidgeRun, Inc. + * Author: RidgeRun, Inc. + * Greg Lonnon glonnon@ridgerun.com or info@ridgerun.com + * + */ + +#include <linux/types.h> +#include <linux/kdev_t.h> +#include <linux/time.h> +#include <linux/devfs_fs_kernel.h> +#include <linux/module.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/smp_lock.h> +#include <asm/uaccess.h> +#include <asm/irq.h> +#include <asm/pgtable.h> +#include "mem_user.h" +#include "user_util.h" + +/* These are set in mmapper_init, which is called at boot time */ +static unsigned long mmapper_size; +static unsigned long p_buf = 0; +static char *v_buf = NULL; + +static ssize_t +mmapper_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + if(*ppos > mmapper_size) + return -EINVAL; + + if(count + *ppos > mmapper_size) + count = count + *ppos - mmapper_size; + + if(count < 0) + return -EINVAL; + + copy_to_user(buf,&v_buf[*ppos],count); + + return count; +} + +static ssize_t +mmapper_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + if(*ppos > mmapper_size) + return -EINVAL; + + if(count + *ppos > mmapper_size) + count = count + *ppos - mmapper_size; + + if(count < 0) + return -EINVAL; + + copy_from_user(&v_buf[*ppos],buf,count); + + return count; +} + +static int +mmapper_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + return(-ENOIOCTLCMD); +} + +static int +mmapper_mmap(struct file *file, struct vm_area_struct * vma) +{ + int ret = -EINVAL; + int size; + + lock_kernel(); + if (vma->vm_pgoff != 0) + goto out; + + size = vma->vm_end - vma->vm_start; + if(size > mmapper_size) return(-EFAULT); + + /* XXX A comment above remap_pfn_range says it should only be + * called when the mm semaphore is held + */ + if (remap_pfn_range(vma, vma->vm_start, p_buf >> PAGE_SHIFT, size, + vma->vm_page_prot)) + goto out; + ret = 0; +out: + unlock_kernel(); + return ret; +} + +static int +mmapper_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int +mmapper_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static struct file_operations mmapper_fops = { + .owner = THIS_MODULE, + .read = mmapper_read, + .write = mmapper_write, + .ioctl = mmapper_ioctl, + .mmap = mmapper_mmap, + .open = mmapper_open, + .release = mmapper_release, +}; + +static int __init mmapper_init(void) +{ + printk(KERN_INFO "Mapper v0.1\n"); + + v_buf = (char *) find_iomem("mmapper", &mmapper_size); + if(mmapper_size == 0){ + printk(KERN_ERR "mmapper_init - find_iomem failed\n"); + return(0); + } + + p_buf = __pa(v_buf); + + devfs_mk_cdev(MKDEV(30, 0), S_IFCHR|S_IRUGO|S_IWUGO, "mmapper"); + return(0); +} + +static void mmapper_exit(void) +{ +} + +module_init(mmapper_init); +module_exit(mmapper_exit); + +MODULE_AUTHOR("Greg Lonnon <glonnon@ridgerun.com>"); +MODULE_DESCRIPTION("DSPLinux simulator mmapper driver"); +/* + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/net_kern.c b/arch/um/drivers/net_kern.c new file mode 100644 index 00000000000..4eeaf88c1e9 --- /dev/null +++ b/arch/um/drivers/net_kern.c @@ -0,0 +1,896 @@ +/* + * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) and + * James Leu (jleu@mindspring.net). + * Copyright (C) 2001 by various other people who didn't put their name here. + * Licensed under the GPL. + */ + +#include "linux/config.h" +#include "linux/kernel.h" +#include "linux/netdevice.h" +#include "linux/rtnetlink.h" +#include "linux/skbuff.h" +#include "linux/socket.h" +#include "linux/spinlock.h" +#include "linux/module.h" +#include "linux/init.h" +#include "linux/etherdevice.h" +#include "linux/list.h" +#include "linux/inetdevice.h" +#include "linux/ctype.h" +#include "linux/bootmem.h" +#include "linux/ethtool.h" +#include "asm/uaccess.h" +#include "user_util.h" +#include "kern_util.h" +#include "net_kern.h" +#include "net_user.h" +#include "mconsole_kern.h" +#include "init.h" +#include "irq_user.h" +#include "irq_kern.h" + +#define DRIVER_NAME "uml-netdev" + +static DEFINE_SPINLOCK(opened_lock); +LIST_HEAD(opened); + +static int uml_net_rx(struct net_device *dev) +{ + struct uml_net_private *lp = dev->priv; + int pkt_len; + struct sk_buff *skb; + + /* If we can't allocate memory, try again next round. */ + skb = dev_alloc_skb(dev->mtu); + if (skb == NULL) { + lp->stats.rx_dropped++; + return 0; + } + + skb->dev = dev; + skb_put(skb, dev->mtu); + skb->mac.raw = skb->data; + pkt_len = (*lp->read)(lp->fd, &skb, lp); + + if (pkt_len > 0) { + skb_trim(skb, pkt_len); + skb->protocol = (*lp->protocol)(skb); + netif_rx(skb); + + lp->stats.rx_bytes += skb->len; + lp->stats.rx_packets++; + return pkt_len; + } + + kfree_skb(skb); + return pkt_len; +} + +irqreturn_t uml_net_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = dev_id; + struct uml_net_private *lp = dev->priv; + int err; + + if(!netif_running(dev)) + return(IRQ_NONE); + + spin_lock(&lp->lock); + while((err = uml_net_rx(dev)) > 0) ; + if(err < 0) { + printk(KERN_ERR + "Device '%s' read returned %d, shutting it down\n", + dev->name, err); + dev_close(dev); + goto out; + } + reactivate_fd(lp->fd, UM_ETH_IRQ); + + out: + spin_unlock(&lp->lock); + return(IRQ_HANDLED); +} + +static int uml_net_open(struct net_device *dev) +{ + struct uml_net_private *lp = dev->priv; + char addr[sizeof("255.255.255.255\0")]; + int err; + + spin_lock(&lp->lock); + + if(lp->fd >= 0){ + err = -ENXIO; + goto out; + } + + if(!lp->have_mac){ + dev_ip_addr(dev, addr, &lp->mac[2]); + set_ether_mac(dev, lp->mac); + } + + lp->fd = (*lp->open)(&lp->user); + if(lp->fd < 0){ + err = lp->fd; + goto out; + } + + err = um_request_irq(dev->irq, lp->fd, IRQ_READ, uml_net_interrupt, + SA_INTERRUPT | SA_SHIRQ, dev->name, dev); + if(err != 0){ + printk(KERN_ERR "uml_net_open: failed to get irq(%d)\n", err); + if(lp->close != NULL) (*lp->close)(lp->fd, &lp->user); + lp->fd = -1; + err = -ENETUNREACH; + } + + lp->tl.data = (unsigned long) &lp->user; + netif_start_queue(dev); + + /* clear buffer - it can happen that the host side of the interface + * is full when we get here. In this case, new data is never queued, + * SIGIOs never arrive, and the net never works. + */ + while((err = uml_net_rx(dev)) > 0) ; + + out: + spin_unlock(&lp->lock); + return(err); +} + +static int uml_net_close(struct net_device *dev) +{ + struct uml_net_private *lp = dev->priv; + + netif_stop_queue(dev); + spin_lock(&lp->lock); + + free_irq_by_irq_and_dev(dev->irq, dev); + free_irq(dev->irq, dev); + if(lp->close != NULL) + (*lp->close)(lp->fd, &lp->user); + lp->fd = -1; + + spin_unlock(&lp->lock); + return 0; +} + +static int uml_net_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct uml_net_private *lp = dev->priv; + unsigned long flags; + int len; + + netif_stop_queue(dev); + + spin_lock_irqsave(&lp->lock, flags); + + len = (*lp->write)(lp->fd, &skb, lp); + + if(len == skb->len) { + lp->stats.tx_packets++; + lp->stats.tx_bytes += skb->len; + dev->trans_start = jiffies; + netif_start_queue(dev); + + /* this is normally done in the interrupt when tx finishes */ + netif_wake_queue(dev); + } + else if(len == 0){ + netif_start_queue(dev); + lp->stats.tx_dropped++; + } + else { + netif_start_queue(dev); + printk(KERN_ERR "uml_net_start_xmit: failed(%d)\n", len); + } + + spin_unlock_irqrestore(&lp->lock, flags); + + dev_kfree_skb(skb); + + return 0; +} + +static struct net_device_stats *uml_net_get_stats(struct net_device *dev) +{ + struct uml_net_private *lp = dev->priv; + return &lp->stats; +} + +static void uml_net_set_multicast_list(struct net_device *dev) +{ + if (dev->flags & IFF_PROMISC) return; + else if (dev->mc_count) dev->flags |= IFF_ALLMULTI; + else dev->flags &= ~IFF_ALLMULTI; +} + +static void uml_net_tx_timeout(struct net_device *dev) +{ + dev->trans_start = jiffies; + netif_wake_queue(dev); +} + +static int uml_net_set_mac(struct net_device *dev, void *addr) +{ + struct uml_net_private *lp = dev->priv; + struct sockaddr *hwaddr = addr; + + spin_lock(&lp->lock); + memcpy(dev->dev_addr, hwaddr->sa_data, ETH_ALEN); + spin_unlock(&lp->lock); + + return(0); +} + +static int uml_net_change_mtu(struct net_device *dev, int new_mtu) +{ + struct uml_net_private *lp = dev->priv; + int err = 0; + + spin_lock(&lp->lock); + + new_mtu = (*lp->set_mtu)(new_mtu, &lp->user); + if(new_mtu < 0){ + err = new_mtu; + goto out; + } + + dev->mtu = new_mtu; + + out: + spin_unlock(&lp->lock); + return err; +} + +static int uml_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + static const struct ethtool_drvinfo info = { + .cmd = ETHTOOL_GDRVINFO, + .driver = DRIVER_NAME, + .version = "42", + }; + void *useraddr; + u32 ethcmd; + + switch (cmd) { + case SIOCETHTOOL: + useraddr = ifr->ifr_data; + if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) + return -EFAULT; + switch (ethcmd) { + case ETHTOOL_GDRVINFO: + if (copy_to_user(useraddr, &info, sizeof(info))) + return -EFAULT; + return 0; + default: + return -EOPNOTSUPP; + } + default: + return -EINVAL; + } +} + +void uml_net_user_timer_expire(unsigned long _conn) +{ +#ifdef undef + struct connection *conn = (struct connection *)_conn; + + dprintk(KERN_INFO "uml_net_user_timer_expire [%p]\n", conn); + do_connect(conn); +#endif +} + +static DEFINE_SPINLOCK(devices_lock); +static struct list_head devices = LIST_HEAD_INIT(devices); + +static struct device_driver uml_net_driver = { + .name = DRIVER_NAME, + .bus = &platform_bus_type, +}; +static int driver_registered; + +static int eth_configure(int n, void *init, char *mac, + struct transport *transport) +{ + struct uml_net *device; + struct net_device *dev; + struct uml_net_private *lp; + int save, err, size; + + size = transport->private_size + sizeof(struct uml_net_private) + + sizeof(((struct uml_net_private *) 0)->user); + + device = kmalloc(sizeof(*device), GFP_KERNEL); + if (device == NULL) { + printk(KERN_ERR "eth_configure failed to allocate uml_net\n"); + return(1); + } + + memset(device, 0, sizeof(*device)); + INIT_LIST_HEAD(&device->list); + device->index = n; + + spin_lock(&devices_lock); + list_add(&device->list, &devices); + spin_unlock(&devices_lock); + + if (setup_etheraddr(mac, device->mac)) + device->have_mac = 1; + + printk(KERN_INFO "Netdevice %d ", n); + if (device->have_mac) + printk("(%02x:%02x:%02x:%02x:%02x:%02x) ", + device->mac[0], device->mac[1], + device->mac[2], device->mac[3], + device->mac[4], device->mac[5]); + printk(": "); + dev = alloc_etherdev(size); + if (dev == NULL) { + printk(KERN_ERR "eth_configure: failed to allocate device\n"); + return 1; + } + + /* sysfs register */ + if (!driver_registered) { + driver_register(¨_net_driver); + driver_registered = 1; + } + device->pdev.id = n; + device->pdev.name = DRIVER_NAME; + platform_device_register(&device->pdev); + SET_NETDEV_DEV(dev,&device->pdev.dev); + + /* If this name ends up conflicting with an existing registered + * netdevice, that is OK, register_netdev{,ice}() will notice this + * and fail. + */ + snprintf(dev->name, sizeof(dev->name), "eth%d", n); + device->dev = dev; + + (*transport->kern->init)(dev, init); + + dev->mtu = transport->user->max_packet; + dev->open = uml_net_open; + dev->hard_start_xmit = uml_net_start_xmit; + dev->stop = uml_net_close; + dev->get_stats = uml_net_get_stats; + dev->set_multicast_list = uml_net_set_multicast_list; + dev->tx_timeout = uml_net_tx_timeout; + dev->set_mac_address = uml_net_set_mac; + dev->change_mtu = uml_net_change_mtu; + dev->do_ioctl = uml_net_ioctl; + dev->watchdog_timeo = (HZ >> 1); + dev->irq = UM_ETH_IRQ; + + rtnl_lock(); + err = register_netdevice(dev); + rtnl_unlock(); + if (err) { + device->dev = NULL; + /* XXX: should we call ->remove() here? */ + free_netdev(dev); + return 1; + } + lp = dev->priv; + + /* lp.user is the first four bytes of the transport data, which + * has already been initialized. This structure assignment will + * overwrite that, so we make sure that .user gets overwritten with + * what it already has. + */ + save = lp->user[0]; + *lp = ((struct uml_net_private) + { .list = LIST_HEAD_INIT(lp->list), + .dev = dev, + .fd = -1, + .mac = { 0xfe, 0xfd, 0x0, 0x0, 0x0, 0x0}, + .have_mac = device->have_mac, + .protocol = transport->kern->protocol, + .open = transport->user->open, + .close = transport->user->close, + .remove = transport->user->remove, + .read = transport->kern->read, + .write = transport->kern->write, + .add_address = transport->user->add_address, + .delete_address = transport->user->delete_address, + .set_mtu = transport->user->set_mtu, + .user = { save } }); + + init_timer(&lp->tl); + spin_lock_init(&lp->lock); + lp->tl.function = uml_net_user_timer_expire; + if (lp->have_mac) + memcpy(lp->mac, device->mac, sizeof(lp->mac)); + + if (transport->user->init) + (*transport->user->init)(&lp->user, dev); + + if (device->have_mac) + set_ether_mac(dev, device->mac); + + spin_lock(&opened_lock); + list_add(&lp->list, &opened); + spin_unlock(&opened_lock); + + return(0); +} + +static struct uml_net *find_device(int n) +{ + struct uml_net *device; + struct list_head *ele; + + spin_lock(&devices_lock); + list_for_each(ele, &devices){ + device = list_entry(ele, struct uml_net, list); + if(device->index == n) + goto out; + } + device = NULL; + out: + spin_unlock(&devices_lock); + return(device); +} + +static int eth_parse(char *str, int *index_out, char **str_out) +{ + char *end; + int n; + + n = simple_strtoul(str, &end, 0); + if(end == str){ + printk(KERN_ERR "eth_setup: Failed to parse '%s'\n", str); + return(1); + } + if(n < 0){ + printk(KERN_ERR "eth_setup: device %d is negative\n", n); + return(1); + } + str = end; + if(*str != '='){ + printk(KERN_ERR + "eth_setup: expected '=' after device number\n"); + return(1); + } + str++; + if(find_device(n)){ + printk(KERN_ERR "eth_setup: Device %d already configured\n", + n); + return(1); + } + if(index_out) *index_out = n; + *str_out = str; + return(0); +} + +struct eth_init { + struct list_head list; + char *init; + int index; +}; + +/* Filled in at boot time. Will need locking if the transports become + * modular. + */ +struct list_head transports = LIST_HEAD_INIT(transports); + +/* Filled in during early boot */ +struct list_head eth_cmd_line = LIST_HEAD_INIT(eth_cmd_line); + +static int check_transport(struct transport *transport, char *eth, int n, + void **init_out, char **mac_out) +{ + int len; + + len = strlen(transport->name); + if(strncmp(eth, transport->name, len)) + return(0); + + eth += len; + if(*eth == ',') + eth++; + else if(*eth != '\0') + return(0); + + *init_out = kmalloc(transport->setup_size, GFP_KERNEL); + if(*init_out == NULL) + return(1); + + if(!transport->setup(eth, mac_out, *init_out)){ + kfree(*init_out); + *init_out = NULL; + } + return(1); +} + +void register_transport(struct transport *new) +{ + struct list_head *ele, *next; + struct eth_init *eth; + void *init; + char *mac = NULL; + int match; + + list_add(&new->list, &transports); + + list_for_each_safe(ele, next, ð_cmd_line){ + eth = list_entry(ele, struct eth_init, list); + match = check_transport(new, eth->init, eth->index, &init, + &mac); + if(!match) + continue; + else if(init != NULL){ + eth_configure(eth->index, init, mac, new); + kfree(init); + } + list_del(ð->list); + } +} + +static int eth_setup_common(char *str, int index) +{ + struct list_head *ele; + struct transport *transport; + void *init; + char *mac = NULL; + + list_for_each(ele, &transports){ + transport = list_entry(ele, struct transport, list); + if(!check_transport(transport, str, index, &init, &mac)) + continue; + if(init != NULL){ + eth_configure(index, init, mac, transport); + kfree(init); + } + return(1); + } + return(0); +} + +static int eth_setup(char *str) +{ + struct eth_init *new; + int n, err; + + err = eth_parse(str, &n, &str); + if(err) return(1); + + new = alloc_bootmem(sizeof(new)); + if (new == NULL){ + printk("eth_init : alloc_bootmem failed\n"); + return(1); + } + + INIT_LIST_HEAD(&new->list); + new->index = n; + new->init = str; + + list_add_tail(&new->list, ð_cmd_line); + return(1); +} + +__setup("eth", eth_setup); +__uml_help(eth_setup, +"eth[0-9]+=<transport>,<options>\n" +" Configure a network device.\n\n" +); + +#if 0 +static int eth_init(void) +{ + struct list_head *ele, *next; + struct eth_init *eth; + + list_for_each_safe(ele, next, ð_cmd_line){ + eth = list_entry(ele, struct eth_init, list); + + if(eth_setup_common(eth->init, eth->index)) + list_del(ð->list); + } + + return(1); +} +__initcall(eth_init); +#endif + +static int net_config(char *str) +{ + int n, err; + + err = eth_parse(str, &n, &str); + if(err) return(err); + + str = uml_strdup(str); + if(str == NULL){ + printk(KERN_ERR "net_config failed to strdup string\n"); + return(-1); + } + err = !eth_setup_common(str, n); + if(err) + kfree(str); + return(err); +} + +static int net_remove(char *str) +{ + struct uml_net *device; + struct net_device *dev; + struct uml_net_private *lp; + char *end; + int n; + + n = simple_strtoul(str, &end, 0); + if((*end != '\0') || (end == str)) + return(-1); + + device = find_device(n); + if(device == NULL) + return(0); + + dev = device->dev; + lp = dev->priv; + if(lp->fd > 0) return(-1); + if(lp->remove != NULL) (*lp->remove)(&lp->user); + unregister_netdev(dev); + platform_device_unregister(&device->pdev); + + list_del(&device->list); + kfree(device); + free_netdev(dev); + return(0); +} + +static struct mc_device net_mc = { + .name = "eth", + .config = net_config, + .get_config = NULL, + .remove = net_remove, +}; + +static int uml_inetaddr_event(struct notifier_block *this, unsigned long event, + void *ptr) +{ + struct in_ifaddr *ifa = ptr; + u32 addr = ifa->ifa_address; + u32 netmask = ifa->ifa_mask; + struct net_device *dev = ifa->ifa_dev->dev; + struct uml_net_private *lp; + void (*proc)(unsigned char *, unsigned char *, void *); + unsigned char addr_buf[4], netmask_buf[4]; + + if(dev->open != uml_net_open) return(NOTIFY_DONE); + + lp = dev->priv; + + proc = NULL; + switch (event){ + case NETDEV_UP: + proc = lp->add_address; + break; + case NETDEV_DOWN: + proc = lp->delete_address; + break; + } + if(proc != NULL){ + addr_buf[0] = addr & 0xff; + addr_buf[1] = (addr >> 8) & 0xff; + addr_buf[2] = (addr >> 16) & 0xff; + addr_buf[3] = addr >> 24; + netmask_buf[0] = netmask & 0xff; + netmask_buf[1] = (netmask >> 8) & 0xff; + netmask_buf[2] = (netmask >> 16) & 0xff; + netmask_buf[3] = netmask >> 24; + (*proc)(addr_buf, netmask_buf, &lp->user); + } + return(NOTIFY_DONE); +} + +struct notifier_block uml_inetaddr_notifier = { + .notifier_call = uml_inetaddr_event, +}; + +static int uml_net_init(void) +{ + struct list_head *ele; + struct uml_net_private *lp; + struct in_device *ip; + struct in_ifaddr *in; + + mconsole_register_dev(&net_mc); + register_inetaddr_notifier(¨_inetaddr_notifier); + + /* Devices may have been opened already, so the uml_inetaddr_notifier + * didn't get a chance to run for them. This fakes it so that + * addresses which have already been set up get handled properly. + */ + list_for_each(ele, &opened){ + lp = list_entry(ele, struct uml_net_private, list); + ip = lp->dev->ip_ptr; + if(ip == NULL) continue; + in = ip->ifa_list; + while(in != NULL){ + uml_inetaddr_event(NULL, NETDEV_UP, in); + in = in->ifa_next; + } + } + + return(0); +} + +__initcall(uml_net_init); + +static void close_devices(void) +{ + struct list_head *ele; + struct uml_net_private *lp; + + list_for_each(ele, &opened){ + lp = list_entry(ele, struct uml_net_private, list); + if((lp->close != NULL) && (lp->fd >= 0)) + (*lp->close)(lp->fd, &lp->user); + if(lp->remove != NULL) (*lp->remove)(&lp->user); + } +} + +__uml_exitcall(close_devices); + +int setup_etheraddr(char *str, unsigned char *addr) +{ + char *end; + int i; + + if(str == NULL) + return(0); + for(i=0;i<6;i++){ + addr[i] = simple_strtoul(str, &end, 16); + if((end == str) || + ((*end != ':') && (*end != ',') && (*end != '\0'))){ + printk(KERN_ERR + "setup_etheraddr: failed to parse '%s' " + "as an ethernet address\n", str); + return(0); + } + str = end + 1; + } + if(addr[0] & 1){ + printk(KERN_ERR + "Attempt to assign a broadcast ethernet address to a " + "device disallowed\n"); + return(0); + } + return(1); +} + +void dev_ip_addr(void *d, char *buf, char *bin_buf) +{ + struct net_device *dev = d; + struct in_device *ip = dev->ip_ptr; + struct in_ifaddr *in; + u32 addr; + + if((ip == NULL) || ((in = ip->ifa_list) == NULL)){ + printk(KERN_WARNING "dev_ip_addr - device not assigned an " + "IP address\n"); + return; + } + addr = in->ifa_address; + sprintf(buf, "%d.%d.%d.%d", addr & 0xff, (addr >> 8) & 0xff, + (addr >> 16) & 0xff, addr >> 24); + if(bin_buf){ + bin_buf[0] = addr & 0xff; + bin_buf[1] = (addr >> 8) & 0xff; + bin_buf[2] = (addr >> 16) & 0xff; + bin_buf[3] = addr >> 24; + } +} + +void set_ether_mac(void *d, unsigned char *addr) +{ + struct net_device *dev = d; + + memcpy(dev->dev_addr, addr, ETH_ALEN); +} + +struct sk_buff *ether_adjust_skb(struct sk_buff *skb, int extra) +{ + if((skb != NULL) && (skb_tailroom(skb) < extra)){ + struct sk_buff *skb2; + + skb2 = skb_copy_expand(skb, 0, extra, GFP_ATOMIC); + dev_kfree_skb(skb); + skb = skb2; + } + if(skb != NULL) skb_put(skb, extra); + return(skb); +} + +void iter_addresses(void *d, void (*cb)(unsigned char *, unsigned char *, + void *), + void *arg) +{ + struct net_device *dev = d; + struct in_device *ip = dev->ip_ptr; + struct in_ifaddr *in; + unsigned char address[4], netmask[4]; + + if(ip == NULL) return; + in = ip->ifa_list; + while(in != NULL){ + address[0] = in->ifa_address & 0xff; + address[1] = (in->ifa_address >> 8) & 0xff; + address[2] = (in->ifa_address >> 16) & 0xff; + address[3] = in->ifa_address >> 24; + netmask[0] = in->ifa_mask & 0xff; + netmask[1] = (in->ifa_mask >> 8) & 0xff; + netmask[2] = (in->ifa_mask >> 16) & 0xff; + netmask[3] = in->ifa_mask >> 24; + (*cb)(address, netmask, arg); + in = in->ifa_next; + } +} + +int dev_netmask(void *d, void *m) +{ + struct net_device *dev = d; + struct in_device *ip = dev->ip_ptr; + struct in_ifaddr *in; + __u32 *mask_out = m; + + if(ip == NULL) + return(1); + + in = ip->ifa_list; + if(in == NULL) + return(1); + + *mask_out = in->ifa_mask; + return(0); +} + +void *get_output_buffer(int *len_out) +{ + void *ret; + + ret = (void *) __get_free_pages(GFP_KERNEL, 0); + if(ret) *len_out = PAGE_SIZE; + else *len_out = 0; + return(ret); +} + +void free_output_buffer(void *buffer) +{ + free_pages((unsigned long) buffer, 0); +} + +int tap_setup_common(char *str, char *type, char **dev_name, char **mac_out, + char **gate_addr) +{ + char *remain; + + remain = split_if_spec(str, dev_name, mac_out, gate_addr, NULL); + if(remain != NULL){ + printk("tap_setup_common - Extra garbage on specification : " + "'%s'\n", remain); + return(1); + } + + return(0); +} + +unsigned short eth_protocol(struct sk_buff *skb) +{ + return(eth_type_trans(skb, skb->dev)); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/net_user.c b/arch/um/drivers/net_user.c new file mode 100644 index 00000000000..47229fe4a81 --- /dev/null +++ b/arch/um/drivers/net_user.c @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <stddef.h> +#include <stdarg.h> +#include <unistd.h> +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include "user.h" +#include "user_util.h" +#include "kern_util.h" +#include "net_user.h" +#include "helper.h" +#include "os.h" + +int tap_open_common(void *dev, char *gate_addr) +{ + int tap_addr[4]; + + if(gate_addr == NULL) return(0); + if(sscanf(gate_addr, "%d.%d.%d.%d", &tap_addr[0], + &tap_addr[1], &tap_addr[2], &tap_addr[3]) != 4){ + printk("Invalid tap IP address - '%s'\n", gate_addr); + return(-EINVAL); + } + return(0); +} + +void tap_check_ips(char *gate_addr, char *eth_addr) +{ + int tap_addr[4]; + + if((gate_addr != NULL) && + (sscanf(gate_addr, "%d.%d.%d.%d", &tap_addr[0], + &tap_addr[1], &tap_addr[2], &tap_addr[3]) == 4) && + (eth_addr[0] == tap_addr[0]) && + (eth_addr[1] == tap_addr[1]) && + (eth_addr[2] == tap_addr[2]) && + (eth_addr[3] == tap_addr[3])){ + printk("The tap IP address and the UML eth IP address" + " must be different\n"); + } +} + +void read_output(int fd, char *output, int len) +{ + int remain, n, actual; + char c; + + if(output == NULL){ + output = &c; + len = sizeof(c); + } + + *output = '\0'; + n = os_read_file(fd, &remain, sizeof(remain)); + if(n != sizeof(remain)){ + printk("read_output - read of length failed, err = %d\n", -n); + return; + } + + while(remain != 0){ + n = (remain < len) ? remain : len; + actual = os_read_file(fd, output, n); + if(actual != n){ + printk("read_output - read of data failed, " + "err = %d\n", -actual); + return; + } + remain -= actual; + } + return; +} + +int net_read(int fd, void *buf, int len) +{ + int n; + + n = os_read_file(fd, buf, len); + + if(n == -EAGAIN) + return(0); + else if(n == 0) + return(-ENOTCONN); + return(n); +} + +int net_recvfrom(int fd, void *buf, int len) +{ + int n; + + while(((n = recvfrom(fd, buf, len, 0, NULL, NULL)) < 0) && + (errno == EINTR)) ; + + if(n < 0){ + if(errno == EAGAIN) return(0); + return(-errno); + } + else if(n == 0) return(-ENOTCONN); + return(n); +} + +int net_write(int fd, void *buf, int len) +{ + int n; + + n = os_write_file(fd, buf, len); + + if(n == -EAGAIN) + return(0); + else if(n == 0) + return(-ENOTCONN); + return(n); +} + +int net_send(int fd, void *buf, int len) +{ + int n; + + while(((n = send(fd, buf, len, 0)) < 0) && (errno == EINTR)) ; + if(n < 0){ + if(errno == EAGAIN) return(0); + return(-errno); + } + else if(n == 0) return(-ENOTCONN); + return(n); +} + +int net_sendto(int fd, void *buf, int len, void *to, int sock_len) +{ + int n; + + while(((n = sendto(fd, buf, len, 0, (struct sockaddr *) to, + sock_len)) < 0) && (errno == EINTR)) ; + if(n < 0){ + if(errno == EAGAIN) return(0); + return(-errno); + } + else if(n == 0) return(-ENOTCONN); + return(n); +} + +struct change_pre_exec_data { + int close_me; + int stdout; +}; + +static void change_pre_exec(void *arg) +{ + struct change_pre_exec_data *data = arg; + + os_close_file(data->close_me); + dup2(data->stdout, 1); +} + +static int change_tramp(char **argv, char *output, int output_len) +{ + int pid, fds[2], err; + struct change_pre_exec_data pe_data; + + err = os_pipe(fds, 1, 0); + if(err < 0){ + printk("change_tramp - pipe failed, err = %d\n", -err); + return(err); + } + pe_data.close_me = fds[0]; + pe_data.stdout = fds[1]; + pid = run_helper(change_pre_exec, &pe_data, argv, NULL); + + read_output(fds[0], output, output_len); + os_close_file(fds[0]); + os_close_file(fds[1]); + + if (pid > 0) + CATCH_EINTR(err = waitpid(pid, NULL, 0)); + return(pid); +} + +static void change(char *dev, char *what, unsigned char *addr, + unsigned char *netmask) +{ + char addr_buf[sizeof("255.255.255.255\0")]; + char netmask_buf[sizeof("255.255.255.255\0")]; + char version[sizeof("nnnnn\0")]; + char *argv[] = { "uml_net", version, what, dev, addr_buf, + netmask_buf, NULL }; + char *output; + int output_len, pid; + + sprintf(version, "%d", UML_NET_VERSION); + sprintf(addr_buf, "%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]); + sprintf(netmask_buf, "%d.%d.%d.%d", netmask[0], netmask[1], + netmask[2], netmask[3]); + + output_len = page_size(); + output = um_kmalloc(output_len); + if(output == NULL) + printk("change : failed to allocate output buffer\n"); + + pid = change_tramp(argv, output, output_len); + if(pid < 0) return; + + if(output != NULL){ + printk("%s", output); + kfree(output); + } +} + +void open_addr(unsigned char *addr, unsigned char *netmask, void *arg) +{ + change(arg, "add", addr, netmask); +} + +void close_addr(unsigned char *addr, unsigned char *netmask, void *arg) +{ + change(arg, "del", addr, netmask); +} + +char *split_if_spec(char *str, ...) +{ + char **arg, *end; + va_list ap; + + va_start(ap, str); + while((arg = va_arg(ap, char **)) != NULL){ + if(*str == '\0') + return(NULL); + end = strchr(str, ','); + if(end != str) + *arg = str; + if(end == NULL) + return(NULL); + *end++ = '\0'; + str = end; + } + va_end(ap); + return(str); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/null.c b/arch/um/drivers/null.c new file mode 100644 index 00000000000..14cc5f78398 --- /dev/null +++ b/arch/um/drivers/null.c @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <stdlib.h> +#include <errno.h> +#include "chan_user.h" +#include "os.h" + +static int null_chan; + +static void *null_init(char *str, int device, struct chan_opts *opts) +{ + return(&null_chan); +} + +static int null_open(int input, int output, int primary, void *d, + char **dev_out) +{ + *dev_out = NULL; + return(os_open_file(DEV_NULL, of_rdwr(OPENFLAGS()), 0)); +} + +static int null_read(int fd, char *c_out, void *unused) +{ + return(-ENODEV); +} + +static void null_free(void *data) +{ +} + +struct chan_ops null_ops = { + .type = "null", + .init = null_init, + .open = null_open, + .close = generic_close, + .read = null_read, + .write = generic_write, + .console_write = generic_console_write, + .window_size = generic_window_size, + .free = null_free, + .winch = 0, +}; + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/pcap_kern.c b/arch/um/drivers/pcap_kern.c new file mode 100644 index 00000000000..07c80f2156e --- /dev/null +++ b/arch/um/drivers/pcap_kern.c @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2002 Jeff Dike <jdike@karaya.com> + * Licensed under the GPL. + */ + +#include "linux/init.h" +#include "linux/netdevice.h" +#include "linux/etherdevice.h" +#include "net_kern.h" +#include "net_user.h" +#include "pcap_user.h" + +struct pcap_init { + char *host_if; + int promisc; + int optimize; + char *filter; +}; + +void pcap_init(struct net_device *dev, void *data) +{ + struct uml_net_private *pri; + struct pcap_data *ppri; + struct pcap_init *init = data; + + pri = dev->priv; + ppri = (struct pcap_data *) pri->user; + ppri->host_if = init->host_if; + ppri->promisc = init->promisc; + ppri->optimize = init->optimize; + ppri->filter = init->filter; +} + +static int pcap_read(int fd, struct sk_buff **skb, + struct uml_net_private *lp) +{ + *skb = ether_adjust_skb(*skb, ETH_HEADER_OTHER); + if(*skb == NULL) return(-ENOMEM); + return(pcap_user_read(fd, (*skb)->mac.raw, + (*skb)->dev->mtu + ETH_HEADER_OTHER, + (struct pcap_data *) &lp->user)); +} + +static int pcap_write(int fd, struct sk_buff **skb, struct uml_net_private *lp) +{ + return(-EPERM); +} + +static struct net_kern_info pcap_kern_info = { + .init = pcap_init, + .protocol = eth_protocol, + .read = pcap_read, + .write = pcap_write, +}; + +int pcap_setup(char *str, char **mac_out, void *data) +{ + struct pcap_init *init = data; + char *remain, *host_if = NULL, *options[2] = { NULL, NULL }; + int i; + + *init = ((struct pcap_init) + { .host_if = "eth0", + .promisc = 1, + .optimize = 0, + .filter = NULL }); + + remain = split_if_spec(str, &host_if, &init->filter, + &options[0], &options[1], NULL); + if(remain != NULL){ + printk(KERN_ERR "pcap_setup - Extra garbage on " + "specification : '%s'\n", remain); + return(0); + } + + if(host_if != NULL) + init->host_if = host_if; + + for(i = 0; i < sizeof(options)/sizeof(options[0]); i++){ + if(options[i] == NULL) + continue; + if(!strcmp(options[i], "promisc")) + init->promisc = 1; + else if(!strcmp(options[i], "nopromisc")) + init->promisc = 0; + else if(!strcmp(options[i], "optimize")) + init->optimize = 1; + else if(!strcmp(options[i], "nooptimize")) + init->optimize = 0; + else printk("pcap_setup : bad option - '%s'\n", options[i]); + } + + return(1); +} + +static struct transport pcap_transport = { + .list = LIST_HEAD_INIT(pcap_transport.list), + .name = "pcap", + .setup = pcap_setup, + .user = &pcap_user_info, + .kern = &pcap_kern_info, + .private_size = sizeof(struct pcap_data), + .setup_size = sizeof(struct pcap_init), +}; + +static int register_pcap(void) +{ + register_transport(&pcap_transport); + return(1); +} + +__initcall(register_pcap); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/pcap_user.c b/arch/um/drivers/pcap_user.c new file mode 100644 index 00000000000..edfcb29273e --- /dev/null +++ b/arch/um/drivers/pcap_user.c @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2002 Jeff Dike <jdike@karaya.com> + * Licensed under the GPL. + */ + +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <pcap.h> +#include <asm/types.h> +#include "net_user.h" +#include "pcap_user.h" +#include "user.h" + +#define MAX_PACKET (ETH_MAX_PACKET + ETH_HEADER_OTHER) + +#define PCAP_FD(p) (*(int *)(p)) + +static void pcap_user_init(void *data, void *dev) +{ + struct pcap_data *pri = data; + pcap_t *p; + char errors[PCAP_ERRBUF_SIZE]; + + p = pcap_open_live(pri->host_if, MAX_PACKET, pri->promisc, 0, errors); + if(p == NULL){ + printk("pcap_user_init : pcap_open_live failed - '%s'\n", + errors); + return; + } + + pri->dev = dev; + pri->pcap = p; +} + +static int pcap_open(void *data) +{ + struct pcap_data *pri = data; + __u32 netmask; + int err; + + if(pri->pcap == NULL) + return(-ENODEV); + + if(pri->filter != NULL){ + err = dev_netmask(pri->dev, &netmask); + if(err < 0){ + printk("pcap_open : dev_netmask failed\n"); + return(-EIO); + } + + pri->compiled = um_kmalloc(sizeof(struct bpf_program)); + if(pri->compiled == NULL){ + printk("pcap_open : kmalloc failed\n"); + return(-ENOMEM); + } + + err = pcap_compile(pri->pcap, + (struct bpf_program *) pri->compiled, + pri->filter, pri->optimize, netmask); + if(err < 0){ + printk("pcap_open : pcap_compile failed - '%s'\n", + pcap_geterr(pri->pcap)); + return(-EIO); + } + + err = pcap_setfilter(pri->pcap, pri->compiled); + if(err < 0){ + printk("pcap_open : pcap_setfilter failed - '%s'\n", + pcap_geterr(pri->pcap)); + return(-EIO); + } + } + + return(PCAP_FD(pri->pcap)); +} + +static void pcap_remove(void *data) +{ + struct pcap_data *pri = data; + + if(pri->compiled != NULL) + pcap_freecode(pri->compiled); + + pcap_close(pri->pcap); +} + +struct pcap_handler_data { + char *buffer; + int len; +}; + +static void handler(u_char *data, const struct pcap_pkthdr *header, + const u_char *packet) +{ + int len; + + struct pcap_handler_data *hdata = (struct pcap_handler_data *) data; + + len = hdata->len < header->caplen ? hdata->len : header->caplen; + memcpy(hdata->buffer, packet, len); + hdata->len = len; +} + +int pcap_user_read(int fd, void *buffer, int len, struct pcap_data *pri) +{ + struct pcap_handler_data hdata = ((struct pcap_handler_data) + { .buffer = buffer, + .len = len }); + int n; + + n = pcap_dispatch(pri->pcap, 1, handler, (u_char *) &hdata); + if(n < 0){ + printk("pcap_dispatch failed - %s\n", pcap_geterr(pri->pcap)); + return(-EIO); + } + else if(n == 0) + return(0); + return(hdata.len); +} + +struct net_user_info pcap_user_info = { + .init = pcap_user_init, + .open = pcap_open, + .close = NULL, + .remove = pcap_remove, + .set_mtu = NULL, + .add_address = NULL, + .delete_address = NULL, + .max_packet = MAX_PACKET - ETH_HEADER_OTHER +}; + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/pcap_user.h b/arch/um/drivers/pcap_user.h new file mode 100644 index 00000000000..58f9f6a1420 --- /dev/null +++ b/arch/um/drivers/pcap_user.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "net_user.h" + +struct pcap_data { + char *host_if; + int promisc; + int optimize; + char *filter; + void *compiled; + void *pcap; + void *dev; +}; + +extern struct net_user_info pcap_user_info; + +extern int pcap_user_read(int fd, void *buf, int len, struct pcap_data *pri); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/port.h b/arch/um/drivers/port.h new file mode 100644 index 00000000000..9117609a575 --- /dev/null +++ b/arch/um/drivers/port.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __PORT_H__ +#define __PORT_H__ + +extern void *port_data(int port); +extern int port_wait(void *data); +extern void port_kern_close(void *d); +extern int port_connection(int fd, int *socket_out, int *pid_out); +extern int port_listen_fd(int port); +extern void port_read(int fd, void *data); +extern void port_kern_free(void *d); +extern int port_rcv_fd(int fd); +extern void port_remove_dev(void *d); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/port_kern.c b/arch/um/drivers/port_kern.c new file mode 100644 index 00000000000..b5ee07472f7 --- /dev/null +++ b/arch/um/drivers/port_kern.c @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/list.h" +#include "linux/sched.h" +#include "linux/slab.h" +#include "linux/interrupt.h" +#include "linux/irq.h" +#include "linux/spinlock.h" +#include "linux/errno.h" +#include "asm/atomic.h" +#include "asm/semaphore.h" +#include "asm/errno.h" +#include "kern_util.h" +#include "kern.h" +#include "irq_user.h" +#include "irq_kern.h" +#include "port.h" +#include "init.h" +#include "os.h" + +struct port_list { + struct list_head list; + atomic_t wait_count; + int has_connection; + struct completion done; + int port; + int fd; + spinlock_t lock; + struct list_head pending; + struct list_head connections; +}; + +struct port_dev { + struct port_list *port; + int helper_pid; + int telnetd_pid; +}; + +struct connection { + struct list_head list; + int fd; + int helper_pid; + int socket[2]; + int telnetd_pid; + struct port_list *port; +}; + +static irqreturn_t pipe_interrupt(int irq, void *data, struct pt_regs *regs) +{ + struct connection *conn = data; + int fd; + + fd = os_rcv_fd(conn->socket[0], &conn->helper_pid); + if(fd < 0){ + if(fd == -EAGAIN) + return(IRQ_NONE); + + printk(KERN_ERR "pipe_interrupt : os_rcv_fd returned %d\n", + -fd); + os_close_file(conn->fd); + } + + list_del(&conn->list); + + conn->fd = fd; + list_add(&conn->list, &conn->port->connections); + + complete(&conn->port->done); + return(IRQ_HANDLED); +} + +#define NO_WAITER_MSG \ + "****\n" \ + "There are currently no UML consoles waiting for port connections.\n" \ + "Either disconnect from one to make it available or activate some more\n" \ + "by enabling more consoles in the UML /etc/inittab.\n" \ + "****\n" + +static int port_accept(struct port_list *port) +{ + struct connection *conn; + int fd, socket[2], pid, ret = 0; + + fd = port_connection(port->fd, socket, &pid); + if(fd < 0){ + if(fd != -EAGAIN) + printk(KERN_ERR "port_accept : port_connection " + "returned %d\n", -fd); + goto out; + } + + conn = kmalloc(sizeof(*conn), GFP_ATOMIC); + if(conn == NULL){ + printk(KERN_ERR "port_accept : failed to allocate " + "connection\n"); + goto out_close; + } + *conn = ((struct connection) + { .list = LIST_HEAD_INIT(conn->list), + .fd = fd, + .socket = { socket[0], socket[1] }, + .telnetd_pid = pid, + .port = port }); + + if(um_request_irq(TELNETD_IRQ, socket[0], IRQ_READ, pipe_interrupt, + SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM, + "telnetd", conn)){ + printk(KERN_ERR "port_accept : failed to get IRQ for " + "telnetd\n"); + goto out_free; + } + + if(atomic_read(&port->wait_count) == 0){ + os_write_file(fd, NO_WAITER_MSG, sizeof(NO_WAITER_MSG)); + printk("No one waiting for port\n"); + } + list_add(&conn->list, &port->pending); + return(1); + + out_free: + kfree(conn); + out_close: + os_close_file(fd); + if(pid != -1) + os_kill_process(pid, 1); + out: + return(ret); +} + +DECLARE_MUTEX(ports_sem); +struct list_head ports = LIST_HEAD_INIT(ports); + +void port_work_proc(void *unused) +{ + struct port_list *port; + struct list_head *ele; + unsigned long flags; + + local_irq_save(flags); + list_for_each(ele, &ports){ + port = list_entry(ele, struct port_list, list); + if(!port->has_connection) + continue; + reactivate_fd(port->fd, ACCEPT_IRQ); + while(port_accept(port)) ; + port->has_connection = 0; + } + local_irq_restore(flags); +} + +DECLARE_WORK(port_work, port_work_proc, NULL); + +static irqreturn_t port_interrupt(int irq, void *data, struct pt_regs *regs) +{ + struct port_list *port = data; + + port->has_connection = 1; + schedule_work(&port_work); + return(IRQ_HANDLED); +} + +void *port_data(int port_num) +{ + struct list_head *ele; + struct port_list *port; + struct port_dev *dev = NULL; + int fd; + + down(&ports_sem); + list_for_each(ele, &ports){ + port = list_entry(ele, struct port_list, list); + if(port->port == port_num) goto found; + } + port = kmalloc(sizeof(struct port_list), GFP_KERNEL); + if(port == NULL){ + printk(KERN_ERR "Allocation of port list failed\n"); + goto out; + } + + fd = port_listen_fd(port_num); + if(fd < 0){ + printk(KERN_ERR "binding to port %d failed, errno = %d\n", + port_num, -fd); + goto out_free; + } + if(um_request_irq(ACCEPT_IRQ, fd, IRQ_READ, port_interrupt, + SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM, "port", + port)){ + printk(KERN_ERR "Failed to get IRQ for port %d\n", port_num); + goto out_close; + } + + *port = ((struct port_list) + { .list = LIST_HEAD_INIT(port->list), + .wait_count = ATOMIC_INIT(0), + .has_connection = 0, + .port = port_num, + .fd = fd, + .pending = LIST_HEAD_INIT(port->pending), + .connections = LIST_HEAD_INIT(port->connections) }); + spin_lock_init(&port->lock); + init_completion(&port->done); + list_add(&port->list, &ports); + + found: + dev = kmalloc(sizeof(struct port_dev), GFP_KERNEL); + if(dev == NULL){ + printk(KERN_ERR "Allocation of port device entry failed\n"); + goto out; + } + + *dev = ((struct port_dev) { .port = port, + .helper_pid = -1, + .telnetd_pid = -1 }); + goto out; + + out_free: + kfree(port); + out_close: + os_close_file(fd); + out: + up(&ports_sem); + return(dev); +} + +int port_wait(void *data) +{ + struct port_dev *dev = data; + struct connection *conn; + struct port_list *port = dev->port; + int fd; + + atomic_inc(&port->wait_count); + while(1){ + fd = -ERESTARTSYS; + if(wait_for_completion_interruptible(&port->done)) + goto out; + + spin_lock(&port->lock); + + conn = list_entry(port->connections.next, struct connection, + list); + list_del(&conn->list); + spin_unlock(&port->lock); + + os_shutdown_socket(conn->socket[0], 1, 1); + os_close_file(conn->socket[0]); + os_shutdown_socket(conn->socket[1], 1, 1); + os_close_file(conn->socket[1]); + + /* This is done here because freeing an IRQ can't be done + * within the IRQ handler. So, pipe_interrupt always ups + * the semaphore regardless of whether it got a successful + * connection. Then we loop here throwing out failed + * connections until a good one is found. + */ + free_irq_by_irq_and_dev(TELNETD_IRQ, conn); + free_irq(TELNETD_IRQ, conn); + + if(conn->fd >= 0) break; + os_close_file(conn->fd); + kfree(conn); + } + + fd = conn->fd; + dev->helper_pid = conn->helper_pid; + dev->telnetd_pid = conn->telnetd_pid; + kfree(conn); + out: + atomic_dec(&port->wait_count); + return fd; +} + +void port_remove_dev(void *d) +{ + struct port_dev *dev = d; + + if(dev->helper_pid != -1) + os_kill_process(dev->helper_pid, 0); + if(dev->telnetd_pid != -1) + os_kill_process(dev->telnetd_pid, 1); + dev->helper_pid = -1; + dev->telnetd_pid = -1; +} + +void port_kern_free(void *d) +{ + struct port_dev *dev = d; + + port_remove_dev(dev); + kfree(dev); +} + +static void free_port(void) +{ + struct list_head *ele; + struct port_list *port; + + list_for_each(ele, &ports){ + port = list_entry(ele, struct port_list, list); + free_irq_by_fd(port->fd); + os_close_file(port->fd); + } +} + +__uml_exitcall(free_port); diff --git a/arch/um/drivers/port_user.c b/arch/um/drivers/port_user.c new file mode 100644 index 00000000000..14dd2002d2d --- /dev/null +++ b/arch/um/drivers/port_user.c @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <stdio.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <termios.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <netinet/in.h> +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "chan_user.h" +#include "port.h" +#include "helper.h" +#include "os.h" + +struct port_chan { + int raw; + struct termios tt; + void *kernel_data; + char dev[sizeof("32768\0")]; +}; + +static void *port_init(char *str, int device, struct chan_opts *opts) +{ + struct port_chan *data; + void *kern_data; + char *end; + int port; + + if(*str != ':'){ + printk("port_init : channel type 'port' must specify a " + "port number\n"); + return(NULL); + } + str++; + port = strtoul(str, &end, 0); + if((*end != '\0') || (end == str)){ + printk("port_init : couldn't parse port '%s'\n", str); + return(NULL); + } + + kern_data = port_data(port); + if(kern_data == NULL) + return(NULL); + + data = um_kmalloc(sizeof(*data)); + if(data == NULL) + goto err; + + *data = ((struct port_chan) { .raw = opts->raw, + .kernel_data = kern_data }); + sprintf(data->dev, "%d", port); + + return(data); + err: + port_kern_free(kern_data); + return(NULL); +} + +static void port_free(void *d) +{ + struct port_chan *data = d; + + port_kern_free(data->kernel_data); + kfree(data); +} + +static int port_open(int input, int output, int primary, void *d, + char **dev_out) +{ + struct port_chan *data = d; + int fd, err; + + fd = port_wait(data->kernel_data); + if((fd >= 0) && data->raw){ + CATCH_EINTR(err = tcgetattr(fd, &data->tt)); + if(err) + return(err); + + err = raw(fd); + if(err) + return(err); + } + *dev_out = data->dev; + return(fd); +} + +static void port_close(int fd, void *d) +{ + struct port_chan *data = d; + + port_remove_dev(data->kernel_data); + os_close_file(fd); +} + +static int port_console_write(int fd, const char *buf, int n, void *d) +{ + struct port_chan *data = d; + + return(generic_console_write(fd, buf, n, &data->tt)); +} + +struct chan_ops port_ops = { + .type = "port", + .init = port_init, + .open = port_open, + .close = port_close, + .read = generic_read, + .write = generic_write, + .console_write = port_console_write, + .window_size = generic_window_size, + .free = port_free, + .winch = 1, +}; + +int port_listen_fd(int port) +{ + struct sockaddr_in addr; + int fd, err, arg; + + fd = socket(PF_INET, SOCK_STREAM, 0); + if(fd == -1) + return(-errno); + + arg = 1; + if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &arg, sizeof(arg)) < 0){ + err = -errno; + goto out; + } + + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + if(bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0){ + err = -errno; + goto out; + } + + if(listen(fd, 1) < 0){ + err = -errno; + goto out; + } + + err = os_set_fd_block(fd, 0); + if(err < 0) + goto out; + + return(fd); + out: + os_close_file(fd); + return(err); +} + +struct port_pre_exec_data { + int sock_fd; + int pipe_fd; +}; + +void port_pre_exec(void *arg) +{ + struct port_pre_exec_data *data = arg; + + dup2(data->sock_fd, 0); + dup2(data->sock_fd, 1); + dup2(data->sock_fd, 2); + os_close_file(data->sock_fd); + dup2(data->pipe_fd, 3); + os_shutdown_socket(3, 1, 0); + os_close_file(data->pipe_fd); +} + +int port_connection(int fd, int *socket, int *pid_out) +{ + int new, err; + char *argv[] = { "/usr/sbin/in.telnetd", "-L", + "/usr/lib/uml/port-helper", NULL }; + struct port_pre_exec_data data; + + new = os_accept_connection(fd); + if(new < 0) + return(new); + + err = os_pipe(socket, 0, 0); + if(err < 0) + goto out_close; + + data = ((struct port_pre_exec_data) + { .sock_fd = new, + .pipe_fd = socket[1] }); + + err = run_helper(port_pre_exec, &data, argv, NULL); + if(err < 0) + goto out_shutdown; + + *pid_out = err; + return(new); + + out_shutdown: + os_shutdown_socket(socket[0], 1, 1); + os_close_file(socket[0]); + os_shutdown_socket(socket[1], 1, 1); + os_close_file(socket[1]); + out_close: + os_close_file(new); + return(err); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/pty.c b/arch/um/drivers/pty.c new file mode 100644 index 00000000000..ed84d01df6c --- /dev/null +++ b/arch/um/drivers/pty.c @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <termios.h> +#include "chan_user.h" +#include "user.h" +#include "user_util.h" +#include "kern_util.h" +#include "os.h" + +struct pty_chan { + void (*announce)(char *dev_name, int dev); + int dev; + int raw; + struct termios tt; + char dev_name[sizeof("/dev/pts/0123456\0")]; +}; + +static void *pty_chan_init(char *str, int device, struct chan_opts *opts) +{ + struct pty_chan *data; + + data = um_kmalloc(sizeof(*data)); + if(data == NULL) return(NULL); + *data = ((struct pty_chan) { .announce = opts->announce, + .dev = device, + .raw = opts->raw }); + return(data); +} + +static int pts_open(int input, int output, int primary, void *d, + char **dev_out) +{ + struct pty_chan *data = d; + char *dev; + int fd, err; + + fd = get_pty(); + if(fd < 0){ + printk("open_pts : Failed to open pts\n"); + return(-errno); + } + if(data->raw){ + CATCH_EINTR(err = tcgetattr(fd, &data->tt)); + if(err) + return(err); + + err = raw(fd); + if(err) + return(err); + } + + dev = ptsname(fd); + sprintf(data->dev_name, "%s", dev); + *dev_out = data->dev_name; + if (data->announce) + (*data->announce)(dev, data->dev); + return(fd); +} + +static int getmaster(char *line) +{ + char *pty, *bank, *cp; + int master, err; + + pty = &line[strlen("/dev/ptyp")]; + for (bank = "pqrs"; *bank; bank++) { + line[strlen("/dev/pty")] = *bank; + *pty = '0'; + if (os_stat_file(line, NULL) < 0) + break; + for (cp = "0123456789abcdef"; *cp; cp++) { + *pty = *cp; + master = os_open_file(line, of_rdwr(OPENFLAGS()), 0); + if (master >= 0) { + char *tp = &line[strlen("/dev/")]; + + /* verify slave side is usable */ + *tp = 't'; + err = os_access(line, OS_ACC_RW_OK); + *tp = 'p'; + if(err == 0) return(master); + (void) os_close_file(master); + } + } + } + return(-1); +} + +static int pty_open(int input, int output, int primary, void *d, + char **dev_out) +{ + struct pty_chan *data = d; + int fd, err; + char dev[sizeof("/dev/ptyxx\0")] = "/dev/ptyxx"; + + fd = getmaster(dev); + if(fd < 0) + return(-errno); + + if(data->raw){ + err = raw(fd); + if(err) + return(err); + } + + if(data->announce) (*data->announce)(dev, data->dev); + + sprintf(data->dev_name, "%s", dev); + *dev_out = data->dev_name; + return(fd); +} + +static int pty_console_write(int fd, const char *buf, int n, void *d) +{ + struct pty_chan *data = d; + + return(generic_console_write(fd, buf, n, &data->tt)); +} + +struct chan_ops pty_ops = { + .type = "pty", + .init = pty_chan_init, + .open = pty_open, + .close = generic_close, + .read = generic_read, + .write = generic_write, + .console_write = pty_console_write, + .window_size = generic_window_size, + .free = generic_free, + .winch = 0, +}; + +struct chan_ops pts_ops = { + .type = "pts", + .init = pty_chan_init, + .open = pts_open, + .close = generic_close, + .read = generic_read, + .write = generic_write, + .console_write = pty_console_write, + .window_size = generic_window_size, + .free = generic_free, + .winch = 0, +}; + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/random.c b/arch/um/drivers/random.c new file mode 100644 index 00000000000..d43e9fab05a --- /dev/null +++ b/arch/um/drivers/random.c @@ -0,0 +1,122 @@ +/* Much of this ripped from hw_random.c */ + +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> +#include <linux/delay.h> +#include <asm/uaccess.h> +#include "os.h" + +/* + * core module and version information + */ +#define RNG_VERSION "1.0.0" +#define RNG_MODULE_NAME "random" +#define RNG_DRIVER_NAME RNG_MODULE_NAME " virtual driver " RNG_VERSION +#define PFX RNG_MODULE_NAME ": " + +#define RNG_MISCDEV_MINOR 183 /* official */ + +static int random_fd = -1; + +static int rng_dev_open (struct inode *inode, struct file *filp) +{ + /* enforce read-only access to this chrdev */ + if ((filp->f_mode & FMODE_READ) == 0) + return -EINVAL; + if (filp->f_mode & FMODE_WRITE) + return -EINVAL; + + return 0; +} + +static ssize_t rng_dev_read (struct file *filp, char __user *buf, size_t size, + loff_t * offp) +{ + u32 data; + int n, ret = 0, have_data; + + while(size){ + n = os_read_file(random_fd, &data, sizeof(data)); + if(n > 0){ + have_data = n; + while (have_data && size) { + if (put_user((u8)data, buf++)) { + ret = ret ? : -EFAULT; + break; + } + size--; + ret++; + have_data--; + data>>=8; + } + } + else if(n == -EAGAIN){ + if (filp->f_flags & O_NONBLOCK) + return ret ? : -EAGAIN; + + if(need_resched()){ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + } + else return n; + if (signal_pending (current)) + return ret ? : -ERESTARTSYS; + } + return ret; +} + +static struct file_operations rng_chrdev_ops = { + .owner = THIS_MODULE, + .open = rng_dev_open, + .read = rng_dev_read, +}; + +static struct miscdevice rng_miscdev = { + RNG_MISCDEV_MINOR, + RNG_MODULE_NAME, + &rng_chrdev_ops, +}; + +/* + * rng_init - initialize RNG module + */ +static int __init rng_init (void) +{ + int err; + + err = os_open_file("/dev/random", of_read(OPENFLAGS()), 0); + if(err < 0) + goto out; + + random_fd = err; + + err = os_set_fd_block(random_fd, 0); + if(err) + goto err_out_cleanup_hw; + + err = misc_register (&rng_miscdev); + if (err) { + printk (KERN_ERR PFX "misc device register failed\n"); + goto err_out_cleanup_hw; + } + + out: + return err; + + err_out_cleanup_hw: + random_fd = -1; + goto out; +} + +/* + * rng_cleanup - shutdown RNG module + */ +static void __exit rng_cleanup (void) +{ + misc_deregister (&rng_miscdev); +} + +module_init (rng_init); +module_exit (rng_cleanup); diff --git a/arch/um/drivers/slip.h b/arch/um/drivers/slip.h new file mode 100644 index 00000000000..495f2f1b142 --- /dev/null +++ b/arch/um/drivers/slip.h @@ -0,0 +1,39 @@ +#ifndef __UM_SLIP_H +#define __UM_SLIP_H + +#define BUF_SIZE 1500 + /* two bytes each for a (pathological) max packet of escaped chars + * + * terminating END char + initial END char */ +#define ENC_BUF_SIZE (2 * BUF_SIZE + 2) + +struct slip_data { + void *dev; + char name[sizeof("slnnnnn\0")]; + char *addr; + char *gate_addr; + int slave; + char ibuf[ENC_BUF_SIZE]; + char obuf[ENC_BUF_SIZE]; + int more; /* more data: do not read fd until ibuf has been drained */ + int pos; + int esc; +}; + +extern struct net_user_info slip_user_info; + +extern int set_umn_addr(int fd, char *addr, char *ptp_addr); +extern int slip_user_read(int fd, void *buf, int len, struct slip_data *pri); +extern int slip_user_write(int fd, void *buf, int len, struct slip_data *pri); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/slip_kern.c b/arch/um/drivers/slip_kern.c new file mode 100644 index 00000000000..0886eedba21 --- /dev/null +++ b/arch/um/drivers/slip_kern.c @@ -0,0 +1,109 @@ +#include "linux/config.h" +#include "linux/kernel.h" +#include "linux/stddef.h" +#include "linux/init.h" +#include "linux/netdevice.h" +#include "linux/if_arp.h" +#include "net_kern.h" +#include "net_user.h" +#include "kern.h" +#include "slip.h" + +struct slip_init { + char *gate_addr; +}; + +void slip_init(struct net_device *dev, void *data) +{ + struct uml_net_private *private; + struct slip_data *spri; + struct slip_init *init = data; + + private = dev->priv; + spri = (struct slip_data *) private->user; + *spri = ((struct slip_data) + { .name = { '\0' }, + .addr = NULL, + .gate_addr = init->gate_addr, + .slave = -1, + .ibuf = { '\0' }, + .obuf = { '\0' }, + .pos = 0, + .esc = 0, + .dev = dev }); + + dev->init = NULL; + dev->hard_header_len = 0; + dev->addr_len = 4; + dev->type = ARPHRD_ETHER; + dev->tx_queue_len = 256; + dev->flags = IFF_NOARP; + printk("SLIP backend - SLIP IP = %s\n", spri->gate_addr); +} + +static unsigned short slip_protocol(struct sk_buff *skbuff) +{ + return(htons(ETH_P_IP)); +} + +static int slip_read(int fd, struct sk_buff **skb, + struct uml_net_private *lp) +{ + return(slip_user_read(fd, (*skb)->mac.raw, (*skb)->dev->mtu, + (struct slip_data *) &lp->user)); +} + +static int slip_write(int fd, struct sk_buff **skb, + struct uml_net_private *lp) +{ + return(slip_user_write(fd, (*skb)->data, (*skb)->len, + (struct slip_data *) &lp->user)); +} + +struct net_kern_info slip_kern_info = { + .init = slip_init, + .protocol = slip_protocol, + .read = slip_read, + .write = slip_write, +}; + +static int slip_setup(char *str, char **mac_out, void *data) +{ + struct slip_init *init = data; + + *init = ((struct slip_init) + { .gate_addr = NULL }); + + if(str[0] != '\0') + init->gate_addr = str; + return(1); +} + +static struct transport slip_transport = { + .list = LIST_HEAD_INIT(slip_transport.list), + .name = "slip", + .setup = slip_setup, + .user = &slip_user_info, + .kern = &slip_kern_info, + .private_size = sizeof(struct slip_data), + .setup_size = sizeof(struct slip_init), +}; + +static int register_slip(void) +{ + register_transport(&slip_transport); + return(1); +} + +__initcall(register_slip); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/slip_proto.h b/arch/um/drivers/slip_proto.h new file mode 100644 index 00000000000..7206361ace4 --- /dev/null +++ b/arch/um/drivers/slip_proto.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __UM_SLIP_PROTO_H__ +#define __UM_SLIP_PROTO_H__ + +/* SLIP protocol characters. */ +#define SLIP_END 0300 /* indicates end of frame */ +#define SLIP_ESC 0333 /* indicates byte stuffing */ +#define SLIP_ESC_END 0334 /* ESC ESC_END means END 'data' */ +#define SLIP_ESC_ESC 0335 /* ESC ESC_ESC means ESC 'data' */ + +static inline int slip_unesc(unsigned char c,char *buf,int *pos, int *esc) +{ + int ret; + + switch(c){ + case SLIP_END: + *esc = 0; + ret=*pos; + *pos=0; + return(ret); + case SLIP_ESC: + *esc = 1; + return(0); + case SLIP_ESC_ESC: + if(*esc){ + *esc = 0; + c = SLIP_ESC; + } + break; + case SLIP_ESC_END: + if(*esc){ + *esc = 0; + c = SLIP_END; + } + break; + } + buf[(*pos)++] = c; + return(0); +} + +static inline int slip_esc(unsigned char *s, unsigned char *d, int len) +{ + unsigned char *ptr = d; + unsigned char c; + + /* + * Send an initial END character to flush out any + * data that may have accumulated in the receiver + * due to line noise. + */ + + *ptr++ = SLIP_END; + + /* + * For each byte in the packet, send the appropriate + * character sequence, according to the SLIP protocol. + */ + + while (len-- > 0) { + switch(c = *s++) { + case SLIP_END: + *ptr++ = SLIP_ESC; + *ptr++ = SLIP_ESC_END; + break; + case SLIP_ESC: + *ptr++ = SLIP_ESC; + *ptr++ = SLIP_ESC_ESC; + break; + default: + *ptr++ = c; + break; + } + } + *ptr++ = SLIP_END; + return (ptr - d); +} + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/slip_user.c b/arch/um/drivers/slip_user.c new file mode 100644 index 00000000000..d94846b1b4c --- /dev/null +++ b/arch/um/drivers/slip_user.c @@ -0,0 +1,280 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <stddef.h> +#include <sched.h> +#include <string.h> +#include <errno.h> +#include <sys/termios.h> +#include <sys/wait.h> +#include <sys/signal.h> +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "net_user.h" +#include "slip.h" +#include "slip_proto.h" +#include "helper.h" +#include "os.h" + +void slip_user_init(void *data, void *dev) +{ + struct slip_data *pri = data; + + pri->dev = dev; +} + +static int set_up_tty(int fd) +{ + int i; + struct termios tios; + + if (tcgetattr(fd, &tios) < 0) { + printk("could not get initial terminal attributes\n"); + return(-1); + } + + tios.c_cflag = CS8 | CREAD | HUPCL | CLOCAL; + tios.c_iflag = IGNBRK | IGNPAR; + tios.c_oflag = 0; + tios.c_lflag = 0; + for (i = 0; i < NCCS; i++) + tios.c_cc[i] = 0; + tios.c_cc[VMIN] = 1; + tios.c_cc[VTIME] = 0; + + cfsetospeed(&tios, B38400); + cfsetispeed(&tios, B38400); + + if (tcsetattr(fd, TCSAFLUSH, &tios) < 0) { + printk("failed to set terminal attributes\n"); + return(-1); + } + return(0); +} + +struct slip_pre_exec_data { + int stdin; + int stdout; + int close_me; +}; + +static void slip_pre_exec(void *arg) +{ + struct slip_pre_exec_data *data = arg; + + if(data->stdin >= 0) dup2(data->stdin, 0); + dup2(data->stdout, 1); + if(data->close_me >= 0) os_close_file(data->close_me); +} + +static int slip_tramp(char **argv, int fd) +{ + struct slip_pre_exec_data pe_data; + char *output; + int status, pid, fds[2], err, output_len; + + err = os_pipe(fds, 1, 0); + if(err < 0){ + printk("slip_tramp : pipe failed, err = %d\n", -err); + return(err); + } + + err = 0; + pe_data.stdin = fd; + pe_data.stdout = fds[1]; + pe_data.close_me = fds[0]; + pid = run_helper(slip_pre_exec, &pe_data, argv, NULL); + + if(pid < 0) err = pid; + else { + output_len = page_size(); + output = um_kmalloc(output_len); + if(output == NULL) + printk("slip_tramp : failed to allocate output " + "buffer\n"); + + os_close_file(fds[1]); + read_output(fds[0], output, output_len); + if(output != NULL){ + printk("%s", output); + kfree(output); + } + CATCH_EINTR(err = waitpid(pid, &status, 0)); + if(err < 0) + err = errno; + else if(!WIFEXITED(status) || (WEXITSTATUS(status) != 0)){ + printk("'%s' didn't exit with status 0\n", argv[0]); + err = -EINVAL; + } + } + + os_close_file(fds[0]); + + return(err); +} + +static int slip_open(void *data) +{ + struct slip_data *pri = data; + char version_buf[sizeof("nnnnn\0")]; + char gate_buf[sizeof("nnn.nnn.nnn.nnn\0")]; + char *argv[] = { "uml_net", version_buf, "slip", "up", gate_buf, + NULL }; + int sfd, mfd, err; + + mfd = get_pty(); + if(mfd < 0){ + printk("umn : Failed to open pty, err = %d\n", -mfd); + return(mfd); + } + sfd = os_open_file(ptsname(mfd), of_rdwr(OPENFLAGS()), 0); + if(sfd < 0){ + printk("Couldn't open tty for slip line, err = %d\n", -sfd); + os_close_file(mfd); + return(sfd); + } + if(set_up_tty(sfd)) return(-1); + pri->slave = sfd; + pri->pos = 0; + pri->esc = 0; + if(pri->gate_addr != NULL){ + sprintf(version_buf, "%d", UML_NET_VERSION); + strcpy(gate_buf, pri->gate_addr); + + err = slip_tramp(argv, sfd); + + if(err < 0){ + printk("slip_tramp failed - err = %d\n", -err); + return(err); + } + err = os_get_ifname(pri->slave, pri->name); + if(err < 0){ + printk("get_ifname failed, err = %d\n", -err); + return(err); + } + iter_addresses(pri->dev, open_addr, pri->name); + } + else { + err = os_set_slip(sfd); + if(err < 0){ + printk("Failed to set slip discipline encapsulation - " + "err = %d\n", -err); + return(err); + } + } + return(mfd); +} + +static void slip_close(int fd, void *data) +{ + struct slip_data *pri = data; + char version_buf[sizeof("nnnnn\0")]; + char *argv[] = { "uml_net", version_buf, "slip", "down", pri->name, + NULL }; + int err; + + if(pri->gate_addr != NULL) + iter_addresses(pri->dev, close_addr, pri->name); + + sprintf(version_buf, "%d", UML_NET_VERSION); + + err = slip_tramp(argv, pri->slave); + + if(err != 0) + printk("slip_tramp failed - errno = %d\n", -err); + os_close_file(fd); + os_close_file(pri->slave); + pri->slave = -1; +} + +int slip_user_read(int fd, void *buf, int len, struct slip_data *pri) +{ + int i, n, size, start; + + if(pri->more>0) { + i = 0; + while(i < pri->more) { + size = slip_unesc(pri->ibuf[i++], + pri->ibuf, &pri->pos, &pri->esc); + if(size){ + memcpy(buf, pri->ibuf, size); + memmove(pri->ibuf, &pri->ibuf[i], pri->more-i); + pri->more=pri->more-i; + return(size); + } + } + pri->more=0; + } + + n = net_read(fd, &pri->ibuf[pri->pos], sizeof(pri->ibuf) - pri->pos); + if(n <= 0) return(n); + + start = pri->pos; + for(i = 0; i < n; i++){ + size = slip_unesc(pri->ibuf[start + i], + pri->ibuf, &pri->pos, &pri->esc); + if(size){ + memcpy(buf, pri->ibuf, size); + memmove(pri->ibuf, &pri->ibuf[start+i+1], n-(i+1)); + pri->more=n-(i+1); + return(size); + } + } + return(0); +} + +int slip_user_write(int fd, void *buf, int len, struct slip_data *pri) +{ + int actual, n; + + actual = slip_esc(buf, pri->obuf, len); + n = net_write(fd, pri->obuf, actual); + if(n < 0) return(n); + else return(len); +} + +static int slip_set_mtu(int mtu, void *data) +{ + return(mtu); +} + +static void slip_add_addr(unsigned char *addr, unsigned char *netmask, + void *data) +{ + struct slip_data *pri = data; + + if(pri->slave < 0) return; + open_addr(addr, netmask, pri->name); +} + +static void slip_del_addr(unsigned char *addr, unsigned char *netmask, + void *data) +{ + struct slip_data *pri = data; + + if(pri->slave < 0) return; + close_addr(addr, netmask, pri->name); +} + +struct net_user_info slip_user_info = { + .init = slip_user_init, + .open = slip_open, + .close = slip_close, + .remove = NULL, + .set_mtu = slip_set_mtu, + .add_address = slip_add_addr, + .delete_address = slip_del_addr, + .max_packet = BUF_SIZE +}; + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/slirp.h b/arch/um/drivers/slirp.h new file mode 100644 index 00000000000..04e407d1e44 --- /dev/null +++ b/arch/um/drivers/slirp.h @@ -0,0 +1,51 @@ +#ifndef __UM_SLIRP_H +#define __UM_SLIRP_H + +#define BUF_SIZE 1500 + /* two bytes each for a (pathological) max packet of escaped chars + * + * terminating END char + initial END char */ +#define ENC_BUF_SIZE (2 * BUF_SIZE + 2) + +#define SLIRP_MAX_ARGS 100 +/* + * XXX this next definition is here because I don't understand why this + * initializer doesn't work in slirp_kern.c: + * + * argv : { init->argv[ 0 ... SLIRP_MAX_ARGS-1 ] }, + * + * or why I can't typecast like this: + * + * argv : (char* [SLIRP_MAX_ARGS])(init->argv), + */ +struct arg_list_dummy_wrapper { char *argv[SLIRP_MAX_ARGS]; }; + +struct slirp_data { + void *dev; + struct arg_list_dummy_wrapper argw; + int pid; + int slave; + char ibuf[ENC_BUF_SIZE]; + char obuf[ENC_BUF_SIZE]; + int more; /* more data: do not read fd until ibuf has been drained */ + int pos; + int esc; +}; + +extern struct net_user_info slirp_user_info; + +extern int set_umn_addr(int fd, char *addr, char *ptp_addr); +extern int slirp_user_read(int fd, void *buf, int len, struct slirp_data *pri); +extern int slirp_user_write(int fd, void *buf, int len, struct slirp_data *pri); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/slirp_kern.c b/arch/um/drivers/slirp_kern.c new file mode 100644 index 00000000000..c9d6b52a831 --- /dev/null +++ b/arch/um/drivers/slirp_kern.c @@ -0,0 +1,135 @@ +#include "linux/kernel.h" +#include "linux/stddef.h" +#include "linux/init.h" +#include "linux/netdevice.h" +#include "linux/if_arp.h" +#include "net_kern.h" +#include "net_user.h" +#include "kern.h" +#include "slirp.h" + +struct slirp_init { + struct arg_list_dummy_wrapper argw; /* XXX should be simpler... */ +}; + +void slirp_init(struct net_device *dev, void *data) +{ + struct uml_net_private *private; + struct slirp_data *spri; + struct slirp_init *init = data; + int i; + + private = dev->priv; + spri = (struct slirp_data *) private->user; + *spri = ((struct slirp_data) + { .argw = init->argw, + .pid = -1, + .slave = -1, + .ibuf = { '\0' }, + .obuf = { '\0' }, + .pos = 0, + .esc = 0, + .dev = dev }); + + dev->init = NULL; + dev->hard_header_len = 0; + dev->header_cache_update = NULL; + dev->hard_header_cache = NULL; + dev->hard_header = NULL; + dev->addr_len = 0; + dev->type = ARPHRD_SLIP; + dev->tx_queue_len = 256; + dev->flags = IFF_NOARP; + printk("SLIRP backend - command line:"); + for(i=0;spri->argw.argv[i]!=NULL;i++) { + printk(" '%s'",spri->argw.argv[i]); + } + printk("\n"); +} + +static unsigned short slirp_protocol(struct sk_buff *skbuff) +{ + return(htons(ETH_P_IP)); +} + +static int slirp_read(int fd, struct sk_buff **skb, + struct uml_net_private *lp) +{ + return(slirp_user_read(fd, (*skb)->mac.raw, (*skb)->dev->mtu, + (struct slirp_data *) &lp->user)); +} + +static int slirp_write(int fd, struct sk_buff **skb, + struct uml_net_private *lp) +{ + return(slirp_user_write(fd, (*skb)->data, (*skb)->len, + (struct slirp_data *) &lp->user)); +} + +struct net_kern_info slirp_kern_info = { + .init = slirp_init, + .protocol = slirp_protocol, + .read = slirp_read, + .write = slirp_write, +}; + +static int slirp_setup(char *str, char **mac_out, void *data) +{ + struct slirp_init *init = data; + int i=0; + + *init = ((struct slirp_init) + { argw : { { "slirp", NULL } } }); + + str = split_if_spec(str, mac_out, NULL); + + if(str == NULL) { /* no command line given after MAC addr */ + return(1); + } + + do { + if(i>=SLIRP_MAX_ARGS-1) { + printk("slirp_setup: truncating slirp arguments\n"); + break; + } + init->argw.argv[i++] = str; + while(*str && *str!=',') { + if(*str=='_') *str=' '; + str++; + } + if(*str!=',') + break; + *str++='\0'; + } while(1); + init->argw.argv[i]=NULL; + return(1); +} + +static struct transport slirp_transport = { + .list = LIST_HEAD_INIT(slirp_transport.list), + .name = "slirp", + .setup = slirp_setup, + .user = &slirp_user_info, + .kern = &slirp_kern_info, + .private_size = sizeof(struct slirp_data), + .setup_size = sizeof(struct slirp_init), +}; + +static int register_slirp(void) +{ + register_transport(&slirp_transport); + return(1); +} + +__initcall(register_slirp); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/slirp_user.c b/arch/um/drivers/slirp_user.c new file mode 100644 index 00000000000..c322515c71c --- /dev/null +++ b/arch/um/drivers/slirp_user.c @@ -0,0 +1,201 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <stddef.h> +#include <sched.h> +#include <string.h> +#include <errno.h> +#include <sys/wait.h> +#include <sys/signal.h> +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "net_user.h" +#include "slirp.h" +#include "slip_proto.h" +#include "helper.h" +#include "os.h" + +void slirp_user_init(void *data, void *dev) +{ + struct slirp_data *pri = data; + + pri->dev = dev; +} + +struct slirp_pre_exec_data { + int stdin; + int stdout; +}; + +static void slirp_pre_exec(void *arg) +{ + struct slirp_pre_exec_data *data = arg; + + if(data->stdin != -1) dup2(data->stdin, 0); + if(data->stdout != -1) dup2(data->stdout, 1); +} + +static int slirp_tramp(char **argv, int fd) +{ + struct slirp_pre_exec_data pe_data; + int pid; + + pe_data.stdin = fd; + pe_data.stdout = fd; + pid = run_helper(slirp_pre_exec, &pe_data, argv, NULL); + + return(pid); +} + +/* XXX This is just a trivial wrapper around os_pipe */ +static int slirp_datachan(int *mfd, int *sfd) +{ + int fds[2], err; + + err = os_pipe(fds, 1, 1); + if(err < 0){ + printk("slirp_datachan: Failed to open pipe, err = %d\n", -err); + return(err); + } + + *mfd = fds[0]; + *sfd = fds[1]; + return(0); +} + +static int slirp_open(void *data) +{ + struct slirp_data *pri = data; + int sfd, mfd, pid, err; + + err = slirp_datachan(&mfd, &sfd); + if(err) + return(err); + + pid = slirp_tramp(pri->argw.argv, sfd); + + if(pid < 0){ + printk("slirp_tramp failed - errno = %d\n", -pid); + os_close_file(sfd); + os_close_file(mfd); + return(pid); + } + + pri->slave = sfd; + pri->pos = 0; + pri->esc = 0; + + pri->pid = pid; + + return(mfd); +} + +static void slirp_close(int fd, void *data) +{ + struct slirp_data *pri = data; + int status,err; + + os_close_file(fd); + os_close_file(pri->slave); + + pri->slave = -1; + + if(pri->pid<1) { + printk("slirp_close: no child process to shut down\n"); + return; + } + +#if 0 + if(kill(pri->pid, SIGHUP)<0) { + printk("slirp_close: sending hangup to %d failed (%d)\n", + pri->pid, errno); + } +#endif + + CATCH_EINTR(err = waitpid(pri->pid, &status, WNOHANG)); + if(err < 0) { + printk("slirp_close: waitpid returned %d\n", errno); + return; + } + + if(err == 0) { + printk("slirp_close: process %d has not exited\n"); + return; + } + + pri->pid = -1; +} + +int slirp_user_read(int fd, void *buf, int len, struct slirp_data *pri) +{ + int i, n, size, start; + + if(pri->more>0) { + i = 0; + while(i < pri->more) { + size = slip_unesc(pri->ibuf[i++], + pri->ibuf,&pri->pos,&pri->esc); + if(size){ + memcpy(buf, pri->ibuf, size); + memmove(pri->ibuf, &pri->ibuf[i], pri->more-i); + pri->more=pri->more-i; + return(size); + } + } + pri->more=0; + } + + n = net_read(fd, &pri->ibuf[pri->pos], sizeof(pri->ibuf) - pri->pos); + if(n <= 0) return(n); + + start = pri->pos; + for(i = 0; i < n; i++){ + size = slip_unesc(pri->ibuf[start + i], + pri->ibuf,&pri->pos,&pri->esc); + if(size){ + memcpy(buf, pri->ibuf, size); + memmove(pri->ibuf, &pri->ibuf[start+i+1], n-(i+1)); + pri->more=n-(i+1); + return(size); + } + } + return(0); +} + +int slirp_user_write(int fd, void *buf, int len, struct slirp_data *pri) +{ + int actual, n; + + actual = slip_esc(buf, pri->obuf, len); + n = net_write(fd, pri->obuf, actual); + if(n < 0) return(n); + else return(len); +} + +static int slirp_set_mtu(int mtu, void *data) +{ + return(mtu); +} + +struct net_user_info slirp_user_info = { + .init = slirp_user_init, + .open = slirp_open, + .close = slirp_close, + .remove = NULL, + .set_mtu = slirp_set_mtu, + .add_address = NULL, + .delete_address = NULL, + .max_packet = BUF_SIZE +}; + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/ssl.c b/arch/um/drivers/ssl.c new file mode 100644 index 00000000000..c5839c3141f --- /dev/null +++ b/arch/um/drivers/ssl.c @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2000, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/config.h" +#include "linux/fs.h" +#include "linux/tty.h" +#include "linux/tty_driver.h" +#include "linux/major.h" +#include "linux/mm.h" +#include "linux/init.h" +#include "linux/console.h" +#include "asm/termbits.h" +#include "asm/irq.h" +#include "line.h" +#include "ssl.h" +#include "chan_kern.h" +#include "user_util.h" +#include "kern_util.h" +#include "kern.h" +#include "init.h" +#include "irq_user.h" +#include "mconsole_kern.h" +#include "2_5compat.h" + +static int ssl_version = 1; + +/* Referenced only by tty_driver below - presumably it's locked correctly + * by the tty driver. + */ + +static struct tty_driver *ssl_driver; + +#define NR_PORTS 64 + +void ssl_announce(char *dev_name, int dev) +{ + printk(KERN_INFO "Serial line %d assigned device '%s'\n", dev, + dev_name); +} + +static struct chan_opts opts = { + .announce = ssl_announce, + .xterm_title = "Serial Line #%d", + .raw = 1, + .tramp_stack = 0, + .in_kernel = 1, +}; + +static int ssl_config(char *str); +static int ssl_get_config(char *dev, char *str, int size, char **error_out); +static int ssl_remove(char *str); + +static struct line_driver driver = { + .name = "UML serial line", + .device_name = "ttyS", + .devfs_name = "tts/", + .major = TTY_MAJOR, + .minor_start = 64, + .type = TTY_DRIVER_TYPE_SERIAL, + .subtype = 0, + .read_irq = SSL_IRQ, + .read_irq_name = "ssl", + .write_irq = SSL_WRITE_IRQ, + .write_irq_name = "ssl-write", + .symlink_from = "serial", + .symlink_to = "tts", + .mc = { + .name = "ssl", + .config = ssl_config, + .get_config = ssl_get_config, + .remove = ssl_remove, + }, +}; + +/* The array is initialized by line_init, which is an initcall. The + * individual elements are protected by individual semaphores. + */ +static struct line serial_lines[NR_PORTS] = + { [0 ... NR_PORTS - 1] = LINE_INIT(CONFIG_SSL_CHAN, &driver) }; + +static struct lines lines = LINES_INIT(NR_PORTS); + +static int ssl_config(char *str) +{ + return(line_config(serial_lines, + sizeof(serial_lines)/sizeof(serial_lines[0]), str)); +} + +static int ssl_get_config(char *dev, char *str, int size, char **error_out) +{ + return(line_get_config(dev, serial_lines, + sizeof(serial_lines)/sizeof(serial_lines[0]), + str, size, error_out)); +} + +static int ssl_remove(char *str) +{ + return(line_remove(serial_lines, + sizeof(serial_lines)/sizeof(serial_lines[0]), str)); +} + +int ssl_open(struct tty_struct *tty, struct file *filp) +{ + return line_open(serial_lines, tty, &opts); +} + +#if 0 +static int ssl_chars_in_buffer(struct tty_struct *tty) +{ + return(0); +} + +static void ssl_flush_buffer(struct tty_struct *tty) +{ + return; +} + +static void ssl_throttle(struct tty_struct * tty) +{ + printk(KERN_ERR "Someone should implement ssl_throttle\n"); +} + +static void ssl_unthrottle(struct tty_struct * tty) +{ + printk(KERN_ERR "Someone should implement ssl_unthrottle\n"); +} + +static void ssl_stop(struct tty_struct *tty) +{ + printk(KERN_ERR "Someone should implement ssl_stop\n"); +} + +static void ssl_start(struct tty_struct *tty) +{ + printk(KERN_ERR "Someone should implement ssl_start\n"); +} + +void ssl_hangup(struct tty_struct *tty) +{ +} +#endif + +static struct tty_operations ssl_ops = { + .open = ssl_open, + .close = line_close, + .write = line_write, + .put_char = line_put_char, + .write_room = line_write_room, + .chars_in_buffer = line_chars_in_buffer, + .set_termios = line_set_termios, + .ioctl = line_ioctl, +#if 0 + .flush_chars = ssl_flush_chars, + .flush_buffer = ssl_flush_buffer, + .throttle = ssl_throttle, + .unthrottle = ssl_unthrottle, + .stop = ssl_stop, + .start = ssl_start, + .hangup = ssl_hangup, +#endif +}; + +/* Changed by ssl_init and referenced by ssl_exit, which are both serialized + * by being an initcall and exitcall, respectively. + */ +static int ssl_init_done = 0; + +static void ssl_console_write(struct console *c, const char *string, + unsigned len) +{ + struct line *line = &serial_lines[c->index]; + + down(&line->sem); + console_write_chan(&line->chan_list, string, len); + up(&line->sem); +} + +static struct tty_driver *ssl_console_device(struct console *c, int *index) +{ + *index = c->index; + return ssl_driver; +} + +static int ssl_console_setup(struct console *co, char *options) +{ + struct line *line = &serial_lines[co->index]; + + return console_open_chan(line,co,&opts); +} + +static struct console ssl_cons = { + .name = "ttyS", + .write = ssl_console_write, + .device = ssl_console_device, + .setup = ssl_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + +int ssl_init(void) +{ + char *new_title; + + printk(KERN_INFO "Initializing software serial port version %d\n", + ssl_version); + ssl_driver = line_register_devfs(&lines, &driver, &ssl_ops, + serial_lines, ARRAY_SIZE(serial_lines)); + + lines_init(serial_lines, sizeof(serial_lines)/sizeof(serial_lines[0])); + + new_title = add_xterm_umid(opts.xterm_title); + if (new_title != NULL) + opts.xterm_title = new_title; + + ssl_init_done = 1; + register_console(&ssl_cons); + return(0); +} +late_initcall(ssl_init); + +static void ssl_exit(void) +{ + if (!ssl_init_done) + return; + close_lines(serial_lines, + sizeof(serial_lines)/sizeof(serial_lines[0])); +} +__uml_exitcall(ssl_exit); + +static int ssl_chan_setup(char *str) +{ + return(line_setup(serial_lines, + sizeof(serial_lines)/sizeof(serial_lines[0]), + str, 1)); +} + +__setup("ssl", ssl_chan_setup); +__channel_help(ssl_chan_setup, "ssl"); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/ssl.h b/arch/um/drivers/ssl.h new file mode 100644 index 00000000000..98412aa6660 --- /dev/null +++ b/arch/um/drivers/ssl.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SSL_H__ +#define __SSL_H__ + +extern int ssl_read(int fd, int line); +extern void ssl_receive_char(int line, char ch); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/stderr_console.c b/arch/um/drivers/stderr_console.c new file mode 100644 index 00000000000..98565b53d17 --- /dev/null +++ b/arch/um/drivers/stderr_console.c @@ -0,0 +1,45 @@ +#include <linux/init.h> +#include <linux/console.h> + +#include "chan_user.h" + +/* ----------------------------------------------------------------------------- */ +/* trivial console driver -- simply dump everything to stderr */ + +/* + * Don't register by default -- as this registeres very early in the + * boot process it becomes the default console. And as this isn't a + * real tty driver init isn't able to open /dev/console then. + * + * In most cases this isn't what you want ... + */ +static int use_stderr_console = 0; + +static void stderr_console_write(struct console *console, const char *string, + unsigned len) +{ + generic_write(2 /* stderr */, string, len, NULL); +} + +static struct console stderr_console = { + .name "stderr", + .write stderr_console_write, + .flags CON_PRINTBUFFER, +}; + +static int __init stderr_console_init(void) +{ + if (use_stderr_console) + register_console(&stderr_console); + return 0; +} +console_initcall(stderr_console_init); + +static int stderr_setup(char *str) +{ + if (!str) + return 0; + use_stderr_console = simple_strtoul(str,&str,0); + return 1; +} +__setup("stderr=", stderr_setup); diff --git a/arch/um/drivers/stdio_console.c b/arch/um/drivers/stdio_console.c new file mode 100644 index 00000000000..e604d7c8769 --- /dev/null +++ b/arch/um/drivers/stdio_console.c @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/config.h" +#include "linux/posix_types.h" +#include "linux/tty.h" +#include "linux/tty_flip.h" +#include "linux/types.h" +#include "linux/major.h" +#include "linux/kdev_t.h" +#include "linux/console.h" +#include "linux/string.h" +#include "linux/sched.h" +#include "linux/list.h" +#include "linux/init.h" +#include "linux/interrupt.h" +#include "linux/slab.h" +#include "linux/hardirq.h" +#include "asm/current.h" +#include "asm/irq.h" +#include "stdio_console.h" +#include "line.h" +#include "chan_kern.h" +#include "user_util.h" +#include "kern_util.h" +#include "irq_user.h" +#include "mconsole_kern.h" +#include "init.h" +#include "2_5compat.h" + +#define MAX_TTYS (16) + +/* ----------------------------------------------------------------------------- */ + +/* Referenced only by tty_driver below - presumably it's locked correctly + * by the tty driver. + */ + +static struct tty_driver *console_driver; + +void stdio_announce(char *dev_name, int dev) +{ + printk(KERN_INFO "Virtual console %d assigned device '%s'\n", dev, + dev_name); +} + +static struct chan_opts opts = { + .announce = stdio_announce, + .xterm_title = "Virtual Console #%d", + .raw = 1, + .tramp_stack = 0, + .in_kernel = 1, +}; + +static int con_config(char *str); +static int con_get_config(char *dev, char *str, int size, char **error_out); +static int con_remove(char *str); + +static struct line_driver driver = { + .name = "UML console", + .device_name = "tty", + .devfs_name = "vc/", + .major = TTY_MAJOR, + .minor_start = 0, + .type = TTY_DRIVER_TYPE_CONSOLE, + .subtype = SYSTEM_TYPE_CONSOLE, + .read_irq = CONSOLE_IRQ, + .read_irq_name = "console", + .write_irq = CONSOLE_WRITE_IRQ, + .write_irq_name = "console-write", + .symlink_from = "ttys", + .symlink_to = "vc", + .mc = { + .name = "con", + .config = con_config, + .get_config = con_get_config, + .remove = con_remove, + }, +}; + +static struct lines console_lines = LINES_INIT(MAX_TTYS); + +/* The array is initialized by line_init, which is an initcall. The + * individual elements are protected by individual semaphores. + */ +struct line vts[MAX_TTYS] = { LINE_INIT(CONFIG_CON_ZERO_CHAN, &driver), + [ 1 ... MAX_TTYS - 1 ] = + LINE_INIT(CONFIG_CON_CHAN, &driver) }; + +static int con_config(char *str) +{ + return(line_config(vts, sizeof(vts)/sizeof(vts[0]), str)); +} + +static int con_get_config(char *dev, char *str, int size, char **error_out) +{ + return(line_get_config(dev, vts, sizeof(vts)/sizeof(vts[0]), str, + size, error_out)); +} + +static int con_remove(char *str) +{ + return(line_remove(vts, sizeof(vts)/sizeof(vts[0]), str)); +} + +static int con_open(struct tty_struct *tty, struct file *filp) +{ + return line_open(vts, tty, &opts); +} + +static int con_init_done = 0; + +static struct tty_operations console_ops = { + .open = con_open, + .close = line_close, + .write = line_write, + .write_room = line_write_room, + .chars_in_buffer = line_chars_in_buffer, + .set_termios = line_set_termios, + .ioctl = line_ioctl, +}; + +static void uml_console_write(struct console *console, const char *string, + unsigned len) +{ + struct line *line = &vts[console->index]; + + down(&line->sem); + console_write_chan(&line->chan_list, string, len); + up(&line->sem); +} + +static struct tty_driver *uml_console_device(struct console *c, int *index) +{ + *index = c->index; + return console_driver; +} + +static int uml_console_setup(struct console *co, char *options) +{ + struct line *line = &vts[co->index]; + + return console_open_chan(line,co,&opts); +} + +static struct console stdiocons = { + .name = "tty", + .write = uml_console_write, + .device = uml_console_device, + .setup = uml_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &vts, +}; + +int stdio_init(void) +{ + char *new_title; + + console_driver = line_register_devfs(&console_lines, &driver, + &console_ops, vts, + ARRAY_SIZE(vts)); + if (NULL == console_driver) + return -1; + printk(KERN_INFO "Initialized stdio console driver\n"); + + lines_init(vts, sizeof(vts)/sizeof(vts[0])); + + new_title = add_xterm_umid(opts.xterm_title); + if(new_title != NULL) + opts.xterm_title = new_title; + + con_init_done = 1; + register_console(&stdiocons); + return(0); +} +late_initcall(stdio_init); + +static void console_exit(void) +{ + if (!con_init_done) + return; + close_lines(vts, sizeof(vts)/sizeof(vts[0])); +} +__uml_exitcall(console_exit); + +static int console_chan_setup(char *str) +{ + return(line_setup(vts, sizeof(vts)/sizeof(vts[0]), str, 1)); +} +__setup("con", console_chan_setup); +__channel_help(console_chan_setup, "con"); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/stdio_console.h b/arch/um/drivers/stdio_console.h new file mode 100644 index 00000000000..505a3d5bea5 --- /dev/null +++ b/arch/um/drivers/stdio_console.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __STDIO_CONSOLE_H +#define __STDIO_CONSOLE_H + +extern void save_console_flags(void); +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/tty.c b/arch/um/drivers/tty.c new file mode 100644 index 00000000000..6fbb670ee27 --- /dev/null +++ b/arch/um/drivers/tty.c @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <stdio.h> +#include <termios.h> +#include <errno.h> +#include <unistd.h> +#include "chan_user.h" +#include "user_util.h" +#include "user.h" +#include "os.h" + +struct tty_chan { + char *dev; + int raw; + struct termios tt; +}; + +static void *tty_chan_init(char *str, int device, struct chan_opts *opts) +{ + struct tty_chan *data; + + if(*str != ':'){ + printk("tty_init : channel type 'tty' must specify " + "a device\n"); + return(NULL); + } + str++; + + data = um_kmalloc(sizeof(*data)); + if(data == NULL) + return(NULL); + *data = ((struct tty_chan) { .dev = str, + .raw = opts->raw }); + + return(data); +} + +static int tty_open(int input, int output, int primary, void *d, + char **dev_out) +{ + struct tty_chan *data = d; + int fd, err; + + fd = os_open_file(data->dev, of_set_rw(OPENFLAGS(), input, output), 0); + if(fd < 0) return(fd); + if(data->raw){ + CATCH_EINTR(err = tcgetattr(fd, &data->tt)); + if(err) + return(err); + + err = raw(fd); + if(err) + return(err); + } + + *dev_out = data->dev; + return(fd); +} + +static int tty_console_write(int fd, const char *buf, int n, void *d) +{ + struct tty_chan *data = d; + + return(generic_console_write(fd, buf, n, &data->tt)); +} + +struct chan_ops tty_ops = { + .type = "tty", + .init = tty_chan_init, + .open = tty_open, + .close = generic_close, + .read = generic_read, + .write = generic_write, + .console_write = tty_console_write, + .window_size = generic_window_size, + .free = generic_free, + .winch = 0, +}; + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/ubd_kern.c b/arch/um/drivers/ubd_kern.c new file mode 100644 index 00000000000..4d8b165bfa4 --- /dev/null +++ b/arch/um/drivers/ubd_kern.c @@ -0,0 +1,1669 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +/* 2001-09-28...2002-04-17 + * Partition stuff by James_McMechan@hotmail.com + * old style ubd by setting UBD_SHIFT to 0 + * 2002-09-27...2002-10-18 massive tinkering for 2.5 + * partitions have changed in 2.5 + * 2003-01-29 more tinkering for 2.5.59-1 + * This should now address the sysfs problems and has + * the symlink for devfs to allow for booting with + * the common /dev/ubd/discX/... names rather than + * only /dev/ubdN/discN this version also has lots of + * clean ups preparing for ubd-many. + * James McMechan + */ + +#define MAJOR_NR UBD_MAJOR +#define UBD_SHIFT 4 + +#include "linux/config.h" +#include "linux/module.h" +#include "linux/blkdev.h" +#include "linux/hdreg.h" +#include "linux/init.h" +#include "linux/devfs_fs_kernel.h" +#include "linux/cdrom.h" +#include "linux/proc_fs.h" +#include "linux/ctype.h" +#include "linux/capability.h" +#include "linux/mm.h" +#include "linux/vmalloc.h" +#include "linux/blkpg.h" +#include "linux/genhd.h" +#include "linux/spinlock.h" +#include "asm/segment.h" +#include "asm/uaccess.h" +#include "asm/irq.h" +#include "asm/types.h" +#include "asm/tlbflush.h" +#include "user_util.h" +#include "mem_user.h" +#include "kern_util.h" +#include "kern.h" +#include "mconsole_kern.h" +#include "init.h" +#include "irq_user.h" +#include "irq_kern.h" +#include "ubd_user.h" +#include "2_5compat.h" +#include "os.h" +#include "mem.h" +#include "mem_kern.h" +#include "cow.h" + +enum ubd_req { UBD_READ, UBD_WRITE, UBD_MMAP }; + +struct io_thread_req { + enum ubd_req op; + int fds[2]; + unsigned long offsets[2]; + unsigned long long offset; + unsigned long length; + char *buffer; + int sectorsize; + unsigned long sector_mask; + unsigned long long cow_offset; + unsigned long bitmap_words[2]; + int map_fd; + unsigned long long map_offset; + int error; +}; + +extern int open_ubd_file(char *file, struct openflags *openflags, + char **backing_file_out, int *bitmap_offset_out, + unsigned long *bitmap_len_out, int *data_offset_out, + int *create_cow_out); +extern int create_cow_file(char *cow_file, char *backing_file, + struct openflags flags, int sectorsize, + int alignment, int *bitmap_offset_out, + unsigned long *bitmap_len_out, + int *data_offset_out); +extern int read_cow_bitmap(int fd, void *buf, int offset, int len); +extern void do_io(struct io_thread_req *req); + +static inline int ubd_test_bit(__u64 bit, unsigned char *data) +{ + __u64 n; + int bits, off; + + bits = sizeof(data[0]) * 8; + n = bit / bits; + off = bit % bits; + return((data[n] & (1 << off)) != 0); +} + +static inline void ubd_set_bit(__u64 bit, unsigned char *data) +{ + __u64 n; + int bits, off; + + bits = sizeof(data[0]) * 8; + n = bit / bits; + off = bit % bits; + data[n] |= (1 << off); +} +/*End stuff from ubd_user.h*/ + +#define DRIVER_NAME "uml-blkdev" + +static DEFINE_SPINLOCK(ubd_io_lock); +static DEFINE_SPINLOCK(ubd_lock); + +static void (*do_ubd)(void); + +static int ubd_open(struct inode * inode, struct file * filp); +static int ubd_release(struct inode * inode, struct file * file); +static int ubd_ioctl(struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg); + +#define MAX_DEV (8) + +/* Changed in early boot */ +static int ubd_do_mmap = 0; +#define UBD_MMAP_BLOCK_SIZE PAGE_SIZE + +static struct block_device_operations ubd_blops = { + .owner = THIS_MODULE, + .open = ubd_open, + .release = ubd_release, + .ioctl = ubd_ioctl, +}; + +/* Protected by the queue_lock */ +static request_queue_t *ubd_queue; + +/* Protected by ubd_lock */ +static int fake_major = MAJOR_NR; + +static struct gendisk *ubd_gendisk[MAX_DEV]; +static struct gendisk *fake_gendisk[MAX_DEV]; + +#ifdef CONFIG_BLK_DEV_UBD_SYNC +#define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 1, .c = 0, \ + .cl = 1 }) +#else +#define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 0, .c = 0, \ + .cl = 1 }) +#endif + +/* Not protected - changed only in ubd_setup_common and then only to + * to enable O_SYNC. + */ +static struct openflags global_openflags = OPEN_FLAGS; + +struct cow { + char *file; + int fd; + unsigned long *bitmap; + unsigned long bitmap_len; + int bitmap_offset; + int data_offset; +}; + +struct ubd { + char *file; + int count; + int fd; + __u64 size; + struct openflags boot_openflags; + struct openflags openflags; + int no_cow; + struct cow cow; + struct platform_device pdev; + + int map_writes; + int map_reads; + int nomap_writes; + int nomap_reads; + int write_maps; +}; + +#define DEFAULT_COW { \ + .file = NULL, \ + .fd = -1, \ + .bitmap = NULL, \ + .bitmap_offset = 0, \ + .data_offset = 0, \ +} + +#define DEFAULT_UBD { \ + .file = NULL, \ + .count = 0, \ + .fd = -1, \ + .size = -1, \ + .boot_openflags = OPEN_FLAGS, \ + .openflags = OPEN_FLAGS, \ + .no_cow = 0, \ + .cow = DEFAULT_COW, \ + .map_writes = 0, \ + .map_reads = 0, \ + .nomap_writes = 0, \ + .nomap_reads = 0, \ + .write_maps = 0, \ +} + +struct ubd ubd_dev[MAX_DEV] = { [ 0 ... MAX_DEV - 1 ] = DEFAULT_UBD }; + +static int ubd0_init(void) +{ + struct ubd *dev = &ubd_dev[0]; + + if(dev->file == NULL) + dev->file = "root_fs"; + return(0); +} + +__initcall(ubd0_init); + +/* Only changed by fake_ide_setup which is a setup */ +static int fake_ide = 0; +static struct proc_dir_entry *proc_ide_root = NULL; +static struct proc_dir_entry *proc_ide = NULL; + +static void make_proc_ide(void) +{ + proc_ide_root = proc_mkdir("ide", NULL); + proc_ide = proc_mkdir("ide0", proc_ide_root); +} + +static int proc_ide_read_media(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + int len; + + strcpy(page, "disk\n"); + len = strlen("disk\n"); + len -= off; + if (len < count){ + *eof = 1; + if (len <= 0) return 0; + } + else len = count; + *start = page + off; + return len; +} + +static void make_ide_entries(char *dev_name) +{ + struct proc_dir_entry *dir, *ent; + char name[64]; + + if(proc_ide_root == NULL) make_proc_ide(); + + dir = proc_mkdir(dev_name, proc_ide); + if(!dir) return; + + ent = create_proc_entry("media", S_IFREG|S_IRUGO, dir); + if(!ent) return; + ent->nlink = 1; + ent->data = NULL; + ent->read_proc = proc_ide_read_media; + ent->write_proc = NULL; + sprintf(name,"ide0/%s", dev_name); + proc_symlink(dev_name, proc_ide_root, name); +} + +static int fake_ide_setup(char *str) +{ + fake_ide = 1; + return(1); +} + +__setup("fake_ide", fake_ide_setup); + +__uml_help(fake_ide_setup, +"fake_ide\n" +" Create ide0 entries that map onto ubd devices.\n\n" +); + +static int parse_unit(char **ptr) +{ + char *str = *ptr, *end; + int n = -1; + + if(isdigit(*str)) { + n = simple_strtoul(str, &end, 0); + if(end == str) + return(-1); + *ptr = end; + } + else if (('a' <= *str) && (*str <= 'h')) { + n = *str - 'a'; + str++; + *ptr = str; + } + return(n); +} + +static int ubd_setup_common(char *str, int *index_out) +{ + struct ubd *dev; + struct openflags flags = global_openflags; + char *backing_file; + int n, err, i; + + if(index_out) *index_out = -1; + n = *str; + if(n == '='){ + char *end; + int major; + + str++; + if(!strcmp(str, "mmap")){ + CHOOSE_MODE(printk("mmap not supported by the ubd " + "driver in tt mode\n"), + ubd_do_mmap = 1); + return(0); + } + + if(!strcmp(str, "sync")){ + global_openflags = of_sync(global_openflags); + return(0); + } + major = simple_strtoul(str, &end, 0); + if((*end != '\0') || (end == str)){ + printk(KERN_ERR + "ubd_setup : didn't parse major number\n"); + return(1); + } + + err = 1; + spin_lock(&ubd_lock); + if(fake_major != MAJOR_NR){ + printk(KERN_ERR "Can't assign a fake major twice\n"); + goto out1; + } + + fake_major = major; + + printk(KERN_INFO "Setting extra ubd major number to %d\n", + major); + err = 0; + out1: + spin_unlock(&ubd_lock); + return(err); + } + + n = parse_unit(&str); + if(n < 0){ + printk(KERN_ERR "ubd_setup : couldn't parse unit number " + "'%s'\n", str); + return(1); + } + if(n >= MAX_DEV){ + printk(KERN_ERR "ubd_setup : index %d out of range " + "(%d devices, from 0 to %d)\n", n, MAX_DEV, MAX_DEV - 1); + return(1); + } + + err = 1; + spin_lock(&ubd_lock); + + dev = &ubd_dev[n]; + if(dev->file != NULL){ + printk(KERN_ERR "ubd_setup : device already configured\n"); + goto out; + } + + if (index_out) + *index_out = n; + + for (i = 0; i < 4; i++) { + switch (*str) { + case 'r': + flags.w = 0; + break; + case 's': + flags.s = 1; + break; + case 'd': + dev->no_cow = 1; + break; + case '=': + str++; + goto break_loop; + default: + printk(KERN_ERR "ubd_setup : Expected '=' or flag letter (r,s or d)\n"); + goto out; + } + str++; + } + + if (*str == '=') + printk(KERN_ERR "ubd_setup : Too many flags specified\n"); + else + printk(KERN_ERR "ubd_setup : Expected '='\n"); + goto out; + +break_loop: + err = 0; + backing_file = strchr(str, ','); + + if (!backing_file) { + backing_file = strchr(str, ':'); + } + + if(backing_file){ + if(dev->no_cow) + printk(KERN_ERR "Can't specify both 'd' and a " + "cow file\n"); + else { + *backing_file = '\0'; + backing_file++; + } + } + dev->file = str; + dev->cow.file = backing_file; + dev->boot_openflags = flags; +out: + spin_unlock(&ubd_lock); + return(err); +} + +static int ubd_setup(char *str) +{ + ubd_setup_common(str, NULL); + return(1); +} + +__setup("ubd", ubd_setup); +__uml_help(ubd_setup, +"ubd<n><flags>=<filename>[(:|,)<filename2>]\n" +" This is used to associate a device with a file in the underlying\n" +" filesystem. When specifying two filenames, the first one is the\n" +" COW name and the second is the backing file name. As separator you can\n" +" use either a ':' or a ',': the first one allows writing things like;\n" +" ubd0=~/Uml/root_cow:~/Uml/root_backing_file\n" +" while with a ',' the shell would not expand the 2nd '~'.\n" +" When using only one filename, UML will detect whether to thread it like\n" +" a COW file or a backing file. To override this detection, add the 'd'\n" +" flag:\n" +" ubd0d=BackingFile\n" +" Usually, there is a filesystem in the file, but \n" +" that's not required. Swap devices containing swap files can be\n" +" specified like this. Also, a file which doesn't contain a\n" +" filesystem can have its contents read in the virtual \n" +" machine by running 'dd' on the device. <n> must be in the range\n" +" 0 to 7. Appending an 'r' to the number will cause that device\n" +" to be mounted read-only. For example ubd1r=./ext_fs. Appending\n" +" an 's' will cause data to be written to disk on the host immediately.\n\n" +); + +static int udb_setup(char *str) +{ + printk("udb%s specified on command line is almost certainly a ubd -> " + "udb TYPO\n", str); + return(1); +} + +__setup("udb", udb_setup); +__uml_help(udb_setup, +"udb\n" +" This option is here solely to catch ubd -> udb typos, which can be\n\n" +" to impossible to catch visually unless you specifically look for\n\n" +" them. The only result of any option starting with 'udb' is an error\n\n" +" in the boot output.\n\n" +); + +static int fakehd_set = 0; +static int fakehd(char *str) +{ + printk(KERN_INFO "fakehd : Changing ubd name to \"hd\".\n"); + fakehd_set = 1; + return 1; +} + +__setup("fakehd", fakehd); +__uml_help(fakehd, +"fakehd\n" +" Change the ubd device name to \"hd\".\n\n" +); + +static void do_ubd_request(request_queue_t * q); + +/* Only changed by ubd_init, which is an initcall. */ +int thread_fd = -1; + +/* Changed by ubd_handler, which is serialized because interrupts only + * happen on CPU 0. + */ +int intr_count = 0; + +/* call ubd_finish if you need to serialize */ +static void __ubd_finish(struct request *req, int error) +{ + int nsect; + + if(error){ + end_request(req, 0); + return; + } + nsect = req->current_nr_sectors; + req->sector += nsect; + req->buffer += nsect << 9; + req->errors = 0; + req->nr_sectors -= nsect; + req->current_nr_sectors = 0; + end_request(req, 1); +} + +static inline void ubd_finish(struct request *req, int error) +{ + spin_lock(&ubd_io_lock); + __ubd_finish(req, error); + spin_unlock(&ubd_io_lock); +} + +/* Called without ubd_io_lock held */ +static void ubd_handler(void) +{ + struct io_thread_req req; + struct request *rq = elv_next_request(ubd_queue); + int n, err; + + do_ubd = NULL; + intr_count++; + n = os_read_file(thread_fd, &req, sizeof(req)); + if(n != sizeof(req)){ + printk(KERN_ERR "Pid %d - spurious interrupt in ubd_handler, " + "err = %d\n", os_getpid(), -n); + spin_lock(&ubd_io_lock); + end_request(rq, 0); + spin_unlock(&ubd_io_lock); + return; + } + + if((req.op != UBD_MMAP) && + ((req.offset != ((__u64) (rq->sector)) << 9) || + (req.length != (rq->current_nr_sectors) << 9))) + panic("I/O op mismatch"); + + if(req.map_fd != -1){ + err = physmem_subst_mapping(req.buffer, req.map_fd, + req.map_offset, 1); + if(err) + printk("ubd_handler - physmem_subst_mapping failed, " + "err = %d\n", -err); + } + + ubd_finish(rq, req.error); + reactivate_fd(thread_fd, UBD_IRQ); + do_ubd_request(ubd_queue); +} + +static irqreturn_t ubd_intr(int irq, void *dev, struct pt_regs *unused) +{ + ubd_handler(); + return(IRQ_HANDLED); +} + +/* Only changed by ubd_init, which is an initcall. */ +static int io_pid = -1; + +void kill_io_thread(void) +{ + if(io_pid != -1) + os_kill_process(io_pid, 1); +} + +__uml_exitcall(kill_io_thread); + +static int ubd_file_size(struct ubd *dev, __u64 *size_out) +{ + char *file; + + file = dev->cow.file ? dev->cow.file : dev->file; + return(os_file_size(file, size_out)); +} + +static void ubd_close(struct ubd *dev) +{ + if(ubd_do_mmap) + physmem_forget_descriptor(dev->fd); + os_close_file(dev->fd); + if(dev->cow.file == NULL) + return; + + if(ubd_do_mmap) + physmem_forget_descriptor(dev->cow.fd); + os_close_file(dev->cow.fd); + vfree(dev->cow.bitmap); + dev->cow.bitmap = NULL; +} + +static int ubd_open_dev(struct ubd *dev) +{ + struct openflags flags; + char **back_ptr; + int err, create_cow, *create_ptr; + + dev->openflags = dev->boot_openflags; + create_cow = 0; + create_ptr = (dev->cow.file != NULL) ? &create_cow : NULL; + back_ptr = dev->no_cow ? NULL : &dev->cow.file; + dev->fd = open_ubd_file(dev->file, &dev->openflags, back_ptr, + &dev->cow.bitmap_offset, &dev->cow.bitmap_len, + &dev->cow.data_offset, create_ptr); + + if((dev->fd == -ENOENT) && create_cow){ + dev->fd = create_cow_file(dev->file, dev->cow.file, + dev->openflags, 1 << 9, PAGE_SIZE, + &dev->cow.bitmap_offset, + &dev->cow.bitmap_len, + &dev->cow.data_offset); + if(dev->fd >= 0){ + printk(KERN_INFO "Creating \"%s\" as COW file for " + "\"%s\"\n", dev->file, dev->cow.file); + } + } + + if(dev->fd < 0){ + printk("Failed to open '%s', errno = %d\n", dev->file, + -dev->fd); + return(dev->fd); + } + + if(dev->cow.file != NULL){ + err = -ENOMEM; + dev->cow.bitmap = (void *) vmalloc(dev->cow.bitmap_len); + if(dev->cow.bitmap == NULL){ + printk(KERN_ERR "Failed to vmalloc COW bitmap\n"); + goto error; + } + flush_tlb_kernel_vm(); + + err = read_cow_bitmap(dev->fd, dev->cow.bitmap, + dev->cow.bitmap_offset, + dev->cow.bitmap_len); + if(err < 0) + goto error; + + flags = dev->openflags; + flags.w = 0; + err = open_ubd_file(dev->cow.file, &flags, NULL, NULL, NULL, + NULL, NULL); + if(err < 0) goto error; + dev->cow.fd = err; + } + return(0); + error: + os_close_file(dev->fd); + return(err); +} + +static int ubd_new_disk(int major, u64 size, int unit, + struct gendisk **disk_out) + +{ + struct gendisk *disk; + char from[sizeof("ubd/nnnnn\0")], to[sizeof("discnnnnn/disc\0")]; + int err; + + disk = alloc_disk(1 << UBD_SHIFT); + if(disk == NULL) + return(-ENOMEM); + + disk->major = major; + disk->first_minor = unit << UBD_SHIFT; + disk->fops = &ubd_blops; + set_capacity(disk, size / 512); + if(major == MAJOR_NR){ + sprintf(disk->disk_name, "ubd%c", 'a' + unit); + sprintf(disk->devfs_name, "ubd/disc%d", unit); + sprintf(from, "ubd/%d", unit); + sprintf(to, "disc%d/disc", unit); + err = devfs_mk_symlink(from, to); + if(err) + printk("ubd_new_disk failed to make link from %s to " + "%s, error = %d\n", from, to, err); + } + else { + sprintf(disk->disk_name, "ubd_fake%d", unit); + sprintf(disk->devfs_name, "ubd_fake/disc%d", unit); + } + + /* sysfs register (not for ide fake devices) */ + if (major == MAJOR_NR) { + ubd_dev[unit].pdev.id = unit; + ubd_dev[unit].pdev.name = DRIVER_NAME; + platform_device_register(&ubd_dev[unit].pdev); + disk->driverfs_dev = &ubd_dev[unit].pdev.dev; + } + + disk->private_data = &ubd_dev[unit]; + disk->queue = ubd_queue; + add_disk(disk); + + *disk_out = disk; + return 0; +} + +#define ROUND_BLOCK(n) ((n + ((1 << 9) - 1)) & (-1 << 9)) + +static int ubd_add(int n) +{ + struct ubd *dev = &ubd_dev[n]; + int err; + + if(dev->file == NULL) + return(-ENODEV); + + if (ubd_open_dev(dev)) + return(-ENODEV); + + err = ubd_file_size(dev, &dev->size); + if(err < 0) + return(err); + + dev->size = ROUND_BLOCK(dev->size); + + err = ubd_new_disk(MAJOR_NR, dev->size, n, &ubd_gendisk[n]); + if(err) + return(err); + + if(fake_major != MAJOR_NR) + ubd_new_disk(fake_major, dev->size, n, + &fake_gendisk[n]); + + /* perhaps this should also be under the "if (fake_major)" above */ + /* using the fake_disk->disk_name and also the fakehd_set name */ + if (fake_ide) + make_ide_entries(ubd_gendisk[n]->disk_name); + + ubd_close(dev); + return 0; +} + +static int ubd_config(char *str) +{ + int n, err; + + str = uml_strdup(str); + if(str == NULL){ + printk(KERN_ERR "ubd_config failed to strdup string\n"); + return(1); + } + err = ubd_setup_common(str, &n); + if(err){ + kfree(str); + return(-1); + } + if(n == -1) return(0); + + spin_lock(&ubd_lock); + err = ubd_add(n); + if(err) + ubd_dev[n].file = NULL; + spin_unlock(&ubd_lock); + + return(err); +} + +static int ubd_get_config(char *name, char *str, int size, char **error_out) +{ + struct ubd *dev; + int n, len = 0; + + n = parse_unit(&name); + if((n >= MAX_DEV) || (n < 0)){ + *error_out = "ubd_get_config : device number out of range"; + return(-1); + } + + dev = &ubd_dev[n]; + spin_lock(&ubd_lock); + + if(dev->file == NULL){ + CONFIG_CHUNK(str, size, len, "", 1); + goto out; + } + + CONFIG_CHUNK(str, size, len, dev->file, 0); + + if(dev->cow.file != NULL){ + CONFIG_CHUNK(str, size, len, ",", 0); + CONFIG_CHUNK(str, size, len, dev->cow.file, 1); + } + else CONFIG_CHUNK(str, size, len, "", 1); + + out: + spin_unlock(&ubd_lock); + return(len); +} + +static int ubd_remove(char *str) +{ + struct ubd *dev; + int n, err = -ENODEV; + + n = parse_unit(&str); + + if((n < 0) || (n >= MAX_DEV)) + return(err); + + dev = &ubd_dev[n]; + if(dev->count > 0) + return(-EBUSY); /* you cannot remove a open disk */ + + err = 0; + spin_lock(&ubd_lock); + + if(ubd_gendisk[n] == NULL) + goto out; + + del_gendisk(ubd_gendisk[n]); + put_disk(ubd_gendisk[n]); + ubd_gendisk[n] = NULL; + + if(fake_gendisk[n] != NULL){ + del_gendisk(fake_gendisk[n]); + put_disk(fake_gendisk[n]); + fake_gendisk[n] = NULL; + } + + platform_device_unregister(&dev->pdev); + *dev = ((struct ubd) DEFAULT_UBD); + err = 0; + out: + spin_unlock(&ubd_lock); + return(err); +} + +static struct mc_device ubd_mc = { + .name = "ubd", + .config = ubd_config, + .get_config = ubd_get_config, + .remove = ubd_remove, +}; + +static int ubd_mc_init(void) +{ + mconsole_register_dev(&ubd_mc); + return 0; +} + +__initcall(ubd_mc_init); + +static struct device_driver ubd_driver = { + .name = DRIVER_NAME, + .bus = &platform_bus_type, +}; + +int ubd_init(void) +{ + int i; + + devfs_mk_dir("ubd"); + if (register_blkdev(MAJOR_NR, "ubd")) + return -1; + + ubd_queue = blk_init_queue(do_ubd_request, &ubd_io_lock); + if (!ubd_queue) { + unregister_blkdev(MAJOR_NR, "ubd"); + return -1; + } + + if (fake_major != MAJOR_NR) { + char name[sizeof("ubd_nnn\0")]; + + snprintf(name, sizeof(name), "ubd_%d", fake_major); + devfs_mk_dir(name); + if (register_blkdev(fake_major, "ubd")) + return -1; + } + driver_register(&ubd_driver); + for (i = 0; i < MAX_DEV; i++) + ubd_add(i); + return 0; +} + +late_initcall(ubd_init); + +int ubd_driver_init(void){ + unsigned long stack; + int err; + + /* Set by CONFIG_BLK_DEV_UBD_SYNC or ubd=sync.*/ + if(global_openflags.s){ + printk(KERN_INFO "ubd: Synchronous mode\n"); + /* Letting ubd=sync be like using ubd#s= instead of ubd#= is + * enough. So use anyway the io thread. */ + } + stack = alloc_stack(0, 0); + io_pid = start_io_thread(stack + PAGE_SIZE - sizeof(void *), + &thread_fd); + if(io_pid < 0){ + printk(KERN_ERR + "ubd : Failed to start I/O thread (errno = %d) - " + "falling back to synchronous I/O\n", -io_pid); + io_pid = -1; + return(0); + } + err = um_request_irq(UBD_IRQ, thread_fd, IRQ_READ, ubd_intr, + SA_INTERRUPT, "ubd", ubd_dev); + if(err != 0) + printk(KERN_ERR "um_request_irq failed - errno = %d\n", -err); + return(err); +} + +device_initcall(ubd_driver_init); + +static int ubd_open(struct inode *inode, struct file *filp) +{ + struct gendisk *disk = inode->i_bdev->bd_disk; + struct ubd *dev = disk->private_data; + int err = 0; + + if(dev->count == 0){ + err = ubd_open_dev(dev); + if(err){ + printk(KERN_ERR "%s: Can't open \"%s\": errno = %d\n", + disk->disk_name, dev->file, -err); + goto out; + } + } + dev->count++; + if((filp->f_mode & FMODE_WRITE) && !dev->openflags.w){ + if(--dev->count == 0) ubd_close(dev); + err = -EROFS; + } + out: + return(err); +} + +static int ubd_release(struct inode * inode, struct file * file) +{ + struct gendisk *disk = inode->i_bdev->bd_disk; + struct ubd *dev = disk->private_data; + + if(--dev->count == 0) + ubd_close(dev); + return(0); +} + +static void cowify_bitmap(__u64 io_offset, int length, unsigned long *cow_mask, + __u64 *cow_offset, unsigned long *bitmap, + __u64 bitmap_offset, unsigned long *bitmap_words, + __u64 bitmap_len) +{ + __u64 sector = io_offset >> 9; + int i, update_bitmap = 0; + + for(i = 0; i < length >> 9; i++){ + if(cow_mask != NULL) + ubd_set_bit(i, (unsigned char *) cow_mask); + if(ubd_test_bit(sector + i, (unsigned char *) bitmap)) + continue; + + update_bitmap = 1; + ubd_set_bit(sector + i, (unsigned char *) bitmap); + } + + if(!update_bitmap) + return; + + *cow_offset = sector / (sizeof(unsigned long) * 8); + + /* This takes care of the case where we're exactly at the end of the + * device, and *cow_offset + 1 is off the end. So, just back it up + * by one word. Thanks to Lynn Kerby for the fix and James McMechan + * for the original diagnosis. + */ + if(*cow_offset == ((bitmap_len + sizeof(unsigned long) - 1) / + sizeof(unsigned long) - 1)) + (*cow_offset)--; + + bitmap_words[0] = bitmap[*cow_offset]; + bitmap_words[1] = bitmap[*cow_offset + 1]; + + *cow_offset *= sizeof(unsigned long); + *cow_offset += bitmap_offset; +} + +static void cowify_req(struct io_thread_req *req, unsigned long *bitmap, + __u64 bitmap_offset, __u64 bitmap_len) +{ + __u64 sector = req->offset >> 9; + int i; + + if(req->length > (sizeof(req->sector_mask) * 8) << 9) + panic("Operation too long"); + + if(req->op == UBD_READ) { + for(i = 0; i < req->length >> 9; i++){ + if(ubd_test_bit(sector + i, (unsigned char *) bitmap)) + ubd_set_bit(i, (unsigned char *) + &req->sector_mask); + } + } + else cowify_bitmap(req->offset, req->length, &req->sector_mask, + &req->cow_offset, bitmap, bitmap_offset, + req->bitmap_words, bitmap_len); +} + +static int mmap_fd(struct request *req, struct ubd *dev, __u64 offset) +{ + __u64 sector; + unsigned char *bitmap; + int bit, i; + + /* mmap must have been requested on the command line */ + if(!ubd_do_mmap) + return(-1); + + /* The buffer must be page aligned */ + if(((unsigned long) req->buffer % UBD_MMAP_BLOCK_SIZE) != 0) + return(-1); + + /* The request must be a page long */ + if((req->current_nr_sectors << 9) != PAGE_SIZE) + return(-1); + + if(dev->cow.file == NULL) + return(dev->fd); + + sector = offset >> 9; + bitmap = (unsigned char *) dev->cow.bitmap; + bit = ubd_test_bit(sector, bitmap); + + for(i = 1; i < req->current_nr_sectors; i++){ + if(ubd_test_bit(sector + i, bitmap) != bit) + return(-1); + } + + if(bit || (rq_data_dir(req) == WRITE)) + offset += dev->cow.data_offset; + + /* The data on disk must be page aligned */ + if((offset % UBD_MMAP_BLOCK_SIZE) != 0) + return(-1); + + return(bit ? dev->fd : dev->cow.fd); +} + +static int prepare_mmap_request(struct ubd *dev, int fd, __u64 offset, + struct request *req, + struct io_thread_req *io_req) +{ + int err; + + if(rq_data_dir(req) == WRITE){ + /* Writes are almost no-ops since the new data is already in the + * host page cache + */ + dev->map_writes++; + if(dev->cow.file != NULL) + cowify_bitmap(io_req->offset, io_req->length, + &io_req->sector_mask, &io_req->cow_offset, + dev->cow.bitmap, dev->cow.bitmap_offset, + io_req->bitmap_words, + dev->cow.bitmap_len); + } + else { + int w; + + if((dev->cow.file != NULL) && (fd == dev->cow.fd)) + w = 0; + else w = dev->openflags.w; + + if((dev->cow.file != NULL) && (fd == dev->fd)) + offset += dev->cow.data_offset; + + err = physmem_subst_mapping(req->buffer, fd, offset, w); + if(err){ + printk("physmem_subst_mapping failed, err = %d\n", + -err); + return(1); + } + dev->map_reads++; + } + io_req->op = UBD_MMAP; + io_req->buffer = req->buffer; + return(0); +} + +/* Called with ubd_io_lock held */ +static int prepare_request(struct request *req, struct io_thread_req *io_req) +{ + struct gendisk *disk = req->rq_disk; + struct ubd *dev = disk->private_data; + __u64 offset; + int len, fd; + + if(req->rq_status == RQ_INACTIVE) return(1); + + if((rq_data_dir(req) == WRITE) && !dev->openflags.w){ + printk("Write attempted on readonly ubd device %s\n", + disk->disk_name); + end_request(req, 0); + return(1); + } + + offset = ((__u64) req->sector) << 9; + len = req->current_nr_sectors << 9; + + io_req->fds[0] = (dev->cow.file != NULL) ? dev->cow.fd : dev->fd; + io_req->fds[1] = dev->fd; + io_req->map_fd = -1; + io_req->cow_offset = -1; + io_req->offset = offset; + io_req->length = len; + io_req->error = 0; + io_req->sector_mask = 0; + + fd = mmap_fd(req, dev, io_req->offset); + if(fd > 0){ + /* If mmapping is otherwise OK, but the first access to the + * page is a write, then it's not mapped in yet. So we have + * to write the data to disk first, then we can map the disk + * page in and continue normally from there. + */ + if((rq_data_dir(req) == WRITE) && !is_remapped(req->buffer)){ + io_req->map_fd = dev->fd; + io_req->map_offset = io_req->offset + + dev->cow.data_offset; + dev->write_maps++; + } + else return(prepare_mmap_request(dev, fd, io_req->offset, req, + io_req)); + } + + if(rq_data_dir(req) == READ) + dev->nomap_reads++; + else dev->nomap_writes++; + + io_req->op = (rq_data_dir(req) == READ) ? UBD_READ : UBD_WRITE; + io_req->offsets[0] = 0; + io_req->offsets[1] = dev->cow.data_offset; + io_req->buffer = req->buffer; + io_req->sectorsize = 1 << 9; + + if(dev->cow.file != NULL) + cowify_req(io_req, dev->cow.bitmap, dev->cow.bitmap_offset, + dev->cow.bitmap_len); + + return(0); +} + +/* Called with ubd_io_lock held */ +static void do_ubd_request(request_queue_t *q) +{ + struct io_thread_req io_req; + struct request *req; + int err, n; + + if(thread_fd == -1){ + while((req = elv_next_request(q)) != NULL){ + err = prepare_request(req, &io_req); + if(!err){ + do_io(&io_req); + __ubd_finish(req, io_req.error); + } + } + } + else { + if(do_ubd || (req = elv_next_request(q)) == NULL) + return; + err = prepare_request(req, &io_req); + if(!err){ + do_ubd = ubd_handler; + n = os_write_file(thread_fd, (char *) &io_req, + sizeof(io_req)); + if(n != sizeof(io_req)) + printk("write to io thread failed, " + "errno = %d\n", -n); + } + } +} + +static int ubd_ioctl(struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg) +{ + struct hd_geometry __user *loc = (struct hd_geometry __user *) arg; + struct ubd *dev = inode->i_bdev->bd_disk->private_data; + struct hd_driveid ubd_id = { + .cyls = 0, + .heads = 128, + .sectors = 32, + }; + + switch (cmd) { + struct hd_geometry g; + struct cdrom_volctrl volume; + case HDIO_GETGEO: + if(!loc) return(-EINVAL); + g.heads = 128; + g.sectors = 32; + g.cylinders = dev->size / (128 * 32 * 512); + g.start = get_start_sect(inode->i_bdev); + return(copy_to_user(loc, &g, sizeof(g)) ? -EFAULT : 0); + + case HDIO_GET_IDENTITY: + ubd_id.cyls = dev->size / (128 * 32 * 512); + if(copy_to_user((char __user *) arg, (char *) &ubd_id, + sizeof(ubd_id))) + return(-EFAULT); + return(0); + + case CDROMVOLREAD: + if(copy_from_user(&volume, (char __user *) arg, sizeof(volume))) + return(-EFAULT); + volume.channel0 = 255; + volume.channel1 = 255; + volume.channel2 = 255; + volume.channel3 = 255; + if(copy_to_user((char __user *) arg, &volume, sizeof(volume))) + return(-EFAULT); + return(0); + } + return(-EINVAL); +} + +static int ubd_check_remapped(int fd, unsigned long address, int is_write, + __u64 offset) +{ + __u64 bitmap_offset; + unsigned long new_bitmap[2]; + int i, err, n; + + /* If it's not a write access, we can't do anything about it */ + if(!is_write) + return(0); + + /* We have a write */ + for(i = 0; i < sizeof(ubd_dev) / sizeof(ubd_dev[0]); i++){ + struct ubd *dev = &ubd_dev[i]; + + if((dev->fd != fd) && (dev->cow.fd != fd)) + continue; + + /* It's a write to a ubd device */ + + if(!dev->openflags.w){ + /* It's a write access on a read-only device - probably + * shouldn't happen. If the kernel is trying to change + * something with no intention of writing it back out, + * then this message will clue us in that this needs + * fixing + */ + printk("Write access to mapped page from readonly ubd " + "device %d\n", i); + return(0); + } + + /* It's a write to a writeable ubd device - it must be COWed + * because, otherwise, the page would have been mapped in + * writeable + */ + + if(!dev->cow.file) + panic("Write fault on writeable non-COW ubd device %d", + i); + + /* It should also be an access to the backing file since the + * COW pages should be mapped in read-write + */ + + if(fd == dev->fd) + panic("Write fault on a backing page of ubd " + "device %d\n", i); + + /* So, we do the write, copying the backing data to the COW + * file... + */ + + err = os_seek_file(dev->fd, offset + dev->cow.data_offset); + if(err < 0) + panic("Couldn't seek to %lld in COW file of ubd " + "device %d, err = %d", + offset + dev->cow.data_offset, i, -err); + + n = os_write_file(dev->fd, (void *) address, PAGE_SIZE); + if(n != PAGE_SIZE) + panic("Couldn't copy data to COW file of ubd " + "device %d, err = %d", i, -n); + + /* ... updating the COW bitmap... */ + + cowify_bitmap(offset, PAGE_SIZE, NULL, &bitmap_offset, + dev->cow.bitmap, dev->cow.bitmap_offset, + new_bitmap, dev->cow.bitmap_len); + + err = os_seek_file(dev->fd, bitmap_offset); + if(err < 0) + panic("Couldn't seek to %lld in COW file of ubd " + "device %d, err = %d", bitmap_offset, i, -err); + + n = os_write_file(dev->fd, new_bitmap, sizeof(new_bitmap)); + if(n != sizeof(new_bitmap)) + panic("Couldn't update bitmap of ubd device %d, " + "err = %d", i, -n); + + /* Maybe we can map the COW page in, and maybe we can't. If + * it is a pre-V3 COW file, we can't, since the alignment will + * be wrong. If it is a V3 or later COW file which has been + * moved to a system with a larger page size, then maybe we + * can't, depending on the exact location of the page. + */ + + offset += dev->cow.data_offset; + + /* Remove the remapping, putting the original anonymous page + * back. If the COW file can be mapped in, that is done. + * Otherwise, the COW page is read in. + */ + + if(!physmem_remove_mapping((void *) address)) + panic("Address 0x%lx not remapped by ubd device %d", + address, i); + if((offset % UBD_MMAP_BLOCK_SIZE) == 0) + physmem_subst_mapping((void *) address, dev->fd, + offset, 1); + else { + err = os_seek_file(dev->fd, offset); + if(err < 0) + panic("Couldn't seek to %lld in COW file of " + "ubd device %d, err = %d", offset, i, + -err); + + n = os_read_file(dev->fd, (void *) address, PAGE_SIZE); + if(n != PAGE_SIZE) + panic("Failed to read page from offset %llx of " + "COW file of ubd device %d, err = %d", + offset, i, -n); + } + + return(1); + } + + /* It's not a write on a ubd device */ + return(0); +} + +static struct remapper ubd_remapper = { + .list = LIST_HEAD_INIT(ubd_remapper.list), + .proc = ubd_check_remapped, +}; + +static int ubd_remapper_setup(void) +{ + if(ubd_do_mmap) + register_remapper(&ubd_remapper); + + return(0); +} + +__initcall(ubd_remapper_setup); + +static int same_backing_files(char *from_cmdline, char *from_cow, char *cow) +{ + struct uml_stat buf1, buf2; + int err; + + if(from_cmdline == NULL) return(1); + if(!strcmp(from_cmdline, from_cow)) return(1); + + err = os_stat_file(from_cmdline, &buf1); + if(err < 0){ + printk("Couldn't stat '%s', err = %d\n", from_cmdline, -err); + return(1); + } + err = os_stat_file(from_cow, &buf2); + if(err < 0){ + printk("Couldn't stat '%s', err = %d\n", from_cow, -err); + return(1); + } + if((buf1.ust_dev == buf2.ust_dev) && (buf1.ust_ino == buf2.ust_ino)) + return(1); + + printk("Backing file mismatch - \"%s\" requested,\n" + "\"%s\" specified in COW header of \"%s\"\n", + from_cmdline, from_cow, cow); + return(0); +} + +static int backing_file_mismatch(char *file, __u64 size, time_t mtime) +{ + unsigned long modtime; + long long actual; + int err; + + err = os_file_modtime(file, &modtime); + if(err < 0){ + printk("Failed to get modification time of backing file " + "\"%s\", err = %d\n", file, -err); + return(err); + } + + err = os_file_size(file, &actual); + if(err < 0){ + printk("Failed to get size of backing file \"%s\", " + "err = %d\n", file, -err); + return(err); + } + + if(actual != size){ + /*__u64 can be a long on AMD64 and with %lu GCC complains; so + * the typecast.*/ + printk("Size mismatch (%llu vs %llu) of COW header vs backing " + "file\n", (unsigned long long) size, actual); + return(-EINVAL); + } + if(modtime != mtime){ + printk("mtime mismatch (%ld vs %ld) of COW header vs backing " + "file\n", mtime, modtime); + return(-EINVAL); + } + return(0); +} + +int read_cow_bitmap(int fd, void *buf, int offset, int len) +{ + int err; + + err = os_seek_file(fd, offset); + if(err < 0) + return(err); + + err = os_read_file(fd, buf, len); + if(err < 0) + return(err); + + return(0); +} + +int open_ubd_file(char *file, struct openflags *openflags, + char **backing_file_out, int *bitmap_offset_out, + unsigned long *bitmap_len_out, int *data_offset_out, + int *create_cow_out) +{ + time_t mtime; + unsigned long long size; + __u32 version, align; + char *backing_file; + int fd, err, sectorsize, same, mode = 0644; + + fd = os_open_file(file, *openflags, mode); + if(fd < 0){ + if((fd == -ENOENT) && (create_cow_out != NULL)) + *create_cow_out = 1; + if(!openflags->w || + ((fd != -EROFS) && (fd != -EACCES))) return(fd); + openflags->w = 0; + fd = os_open_file(file, *openflags, mode); + if(fd < 0) + return(fd); + } + + err = os_lock_file(fd, openflags->w); + if(err < 0){ + printk("Failed to lock '%s', err = %d\n", file, -err); + goto out_close; + } + + if(backing_file_out == NULL) return(fd); + + err = read_cow_header(file_reader, &fd, &version, &backing_file, &mtime, + &size, §orsize, &align, bitmap_offset_out); + if(err && (*backing_file_out != NULL)){ + printk("Failed to read COW header from COW file \"%s\", " + "errno = %d\n", file, -err); + goto out_close; + } + if(err) return(fd); + + if(backing_file_out == NULL) return(fd); + + same = same_backing_files(*backing_file_out, backing_file, file); + + if(!same && !backing_file_mismatch(*backing_file_out, size, mtime)){ + printk("Switching backing file to '%s'\n", *backing_file_out); + err = write_cow_header(file, fd, *backing_file_out, + sectorsize, align, &size); + if(err){ + printk("Switch failed, errno = %d\n", -err); + return(err); + } + } + else { + *backing_file_out = backing_file; + err = backing_file_mismatch(*backing_file_out, size, mtime); + if(err) goto out_close; + } + + cow_sizes(version, size, sectorsize, align, *bitmap_offset_out, + bitmap_len_out, data_offset_out); + + return(fd); + out_close: + os_close_file(fd); + return(err); +} + +int create_cow_file(char *cow_file, char *backing_file, struct openflags flags, + int sectorsize, int alignment, int *bitmap_offset_out, + unsigned long *bitmap_len_out, int *data_offset_out) +{ + int err, fd; + + flags.c = 1; + fd = open_ubd_file(cow_file, &flags, NULL, NULL, NULL, NULL, NULL); + if(fd < 0){ + err = fd; + printk("Open of COW file '%s' failed, errno = %d\n", cow_file, + -err); + goto out; + } + + err = init_cow_file(fd, cow_file, backing_file, sectorsize, alignment, + bitmap_offset_out, bitmap_len_out, + data_offset_out); + if(!err) + return(fd); + os_close_file(fd); + out: + return(err); +} + +static int update_bitmap(struct io_thread_req *req) +{ + int n; + + if(req->cow_offset == -1) + return(0); + + n = os_seek_file(req->fds[1], req->cow_offset); + if(n < 0){ + printk("do_io - bitmap lseek failed : err = %d\n", -n); + return(1); + } + + n = os_write_file(req->fds[1], &req->bitmap_words, + sizeof(req->bitmap_words)); + if(n != sizeof(req->bitmap_words)){ + printk("do_io - bitmap update failed, err = %d fd = %d\n", -n, + req->fds[1]); + return(1); + } + + return(0); +} + +void do_io(struct io_thread_req *req) +{ + char *buf; + unsigned long len; + int n, nsectors, start, end, bit; + int err; + __u64 off; + + if(req->op == UBD_MMAP){ + /* Touch the page to force the host to do any necessary IO to + * get it into memory + */ + n = *((volatile int *) req->buffer); + req->error = update_bitmap(req); + return; + } + + nsectors = req->length / req->sectorsize; + start = 0; + do { + bit = ubd_test_bit(start, (unsigned char *) &req->sector_mask); + end = start; + while((end < nsectors) && + (ubd_test_bit(end, (unsigned char *) + &req->sector_mask) == bit)) + end++; + + off = req->offset + req->offsets[bit] + + start * req->sectorsize; + len = (end - start) * req->sectorsize; + buf = &req->buffer[start * req->sectorsize]; + + err = os_seek_file(req->fds[bit], off); + if(err < 0){ + printk("do_io - lseek failed : err = %d\n", -err); + req->error = 1; + return; + } + if(req->op == UBD_READ){ + n = 0; + do { + buf = &buf[n]; + len -= n; + n = os_read_file(req->fds[bit], buf, len); + if (n < 0) { + printk("do_io - read failed, err = %d " + "fd = %d\n", -n, req->fds[bit]); + req->error = 1; + return; + } + } while((n < len) && (n != 0)); + if (n < len) memset(&buf[n], 0, len - n); + } + else { + n = os_write_file(req->fds[bit], buf, len); + if(n != len){ + printk("do_io - write failed err = %d " + "fd = %d\n", -n, req->fds[bit]); + req->error = 1; + return; + } + } + + start = end; + } while(start < nsectors); + + req->error = update_bitmap(req); +} + +/* Changed in start_io_thread, which is serialized by being called only + * from ubd_init, which is an initcall. + */ +int kernel_fd = -1; + +/* Only changed by the io thread */ +int io_count = 0; + +int io_thread(void *arg) +{ + struct io_thread_req req; + int n; + + ignore_sigwinch_sig(); + while(1){ + n = os_read_file(kernel_fd, &req, sizeof(req)); + if(n != sizeof(req)){ + if(n < 0) + printk("io_thread - read failed, fd = %d, " + "err = %d\n", kernel_fd, -n); + else { + printk("io_thread - short read, fd = %d, " + "length = %d\n", kernel_fd, n); + } + continue; + } + io_count++; + do_io(&req); + n = os_write_file(kernel_fd, &req, sizeof(req)); + if(n != sizeof(req)) + printk("io_thread - write failed, fd = %d, err = %d\n", + kernel_fd, -n); + } +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/ubd_user.c b/arch/um/drivers/ubd_user.c new file mode 100644 index 00000000000..b94d2bc4fe0 --- /dev/null +++ b/arch/um/drivers/ubd_user.c @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2001 Ridgerun,Inc (glonnon@ridgerun.com) + * Licensed under the GPL + */ + +#include <stddef.h> +#include <unistd.h> +#include <errno.h> +#include <sched.h> +#include <signal.h> +#include <string.h> +#include <netinet/in.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <sys/mman.h> +#include <sys/param.h> +#include "asm/types.h" +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "ubd_user.h" +#include "os.h" +#include "cow.h" + +#include <endian.h> +#include <byteswap.h> + +void ignore_sigwinch_sig(void) +{ + signal(SIGWINCH, SIG_IGN); +} + +int start_io_thread(unsigned long sp, int *fd_out) +{ + int pid, fds[2], err; + + err = os_pipe(fds, 1, 1); + if(err < 0){ + printk("start_io_thread - os_pipe failed, err = %d\n", -err); + goto out; + } + + kernel_fd = fds[0]; + *fd_out = fds[1]; + + pid = clone(io_thread, (void *) sp, CLONE_FILES | CLONE_VM | SIGCHLD, + NULL); + if(pid < 0){ + printk("start_io_thread - clone failed : errno = %d\n", errno); + err = -errno; + goto out_close; + } + + return(pid); + + out_close: + os_close_file(fds[0]); + os_close_file(fds[1]); + kernel_fd = -1; + *fd_out = -1; + out: + return(err); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/xterm.c b/arch/um/drivers/xterm.c new file mode 100644 index 00000000000..93dc1911363 --- /dev/null +++ b/arch/um/drivers/xterm.c @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <termios.h> +#include <signal.h> +#include <sched.h> +#include <sys/socket.h> +#include "kern_util.h" +#include "chan_user.h" +#include "helper.h" +#include "user_util.h" +#include "user.h" +#include "os.h" +#include "xterm.h" + +struct xterm_chan { + int pid; + int helper_pid; + char *title; + int device; + int raw; + struct termios tt; + unsigned long stack; + int direct_rcv; +}; + +/* Not static because it's called directly by the tt mode gdb code */ +void *xterm_init(char *str, int device, struct chan_opts *opts) +{ + struct xterm_chan *data; + + data = malloc(sizeof(*data)); + if(data == NULL) return(NULL); + *data = ((struct xterm_chan) { .pid = -1, + .helper_pid = -1, + .device = device, + .title = opts->xterm_title, + .raw = opts->raw, + .stack = opts->tramp_stack, + .direct_rcv = !opts->in_kernel } ); + return(data); +} + +/* Only changed by xterm_setup, which is a setup */ +static char *terminal_emulator = "xterm"; +static char *title_switch = "-T"; +static char *exec_switch = "-e"; + +static int __init xterm_setup(char *line, int *add) +{ + *add = 0; + terminal_emulator = line; + + line = strchr(line, ','); + if(line == NULL) return(0); + *line++ = '\0'; + if(*line) title_switch = line; + + line = strchr(line, ','); + if(line == NULL) return(0); + *line++ = '\0'; + if(*line) exec_switch = line; + + return(0); +} + +__uml_setup("xterm=", xterm_setup, +"xterm=<terminal emulator>,<title switch>,<exec switch>\n" +" Specifies an alternate terminal emulator to use for the debugger,\n" +" consoles, and serial lines when they are attached to the xterm channel.\n" +" The values are the terminal emulator binary, the switch it uses to set\n" +" its title, and the switch it uses to execute a subprocess,\n" +" respectively. The title switch must have the form '<switch> title',\n" +" not '<switch>=title'. Similarly, the exec switch must have the form\n" +" '<switch> command arg1 arg2 ...'.\n" +" The default values are 'xterm=xterm,-T,-e'. Values for gnome-terminal\n" +" are 'xterm=gnome-terminal,-t,-x'.\n\n" +); + +/* XXX This badly needs some cleaning up in the error paths + * Not static because it's called directly by the tt mode gdb code + */ +int xterm_open(int input, int output, int primary, void *d, + char **dev_out) +{ + struct xterm_chan *data = d; + unsigned long stack; + int pid, fd, new, err; + char title[256], file[] = "/tmp/xterm-pipeXXXXXX"; + char *argv[] = { terminal_emulator, title_switch, title, exec_switch, + "/usr/lib/uml/port-helper", "-uml-socket", + file, NULL }; + + if(os_access(argv[4], OS_ACC_X_OK) < 0) + argv[4] = "port-helper"; + + /* Check that DISPLAY is set, this doesn't guarantee the xterm + * will work but w/o it we can be pretty sure it won't. */ + if (!getenv("DISPLAY")) { + printk("xterm_open: $DISPLAY not set.\n"); + return -ENODEV; + } + + fd = mkstemp(file); + if(fd < 0){ + printk("xterm_open : mkstemp failed, errno = %d\n", errno); + return(-errno); + } + + if(unlink(file)){ + printk("xterm_open : unlink failed, errno = %d\n", errno); + return(-errno); + } + os_close_file(fd); + + fd = os_create_unix_socket(file, sizeof(file), 1); + if(fd < 0){ + printk("xterm_open : create_unix_socket failed, errno = %d\n", + -fd); + return(fd); + } + + sprintf(title, data->title, data->device); + stack = data->stack; + pid = run_helper(NULL, NULL, argv, &stack); + if(pid < 0){ + printk("xterm_open : run_helper failed, errno = %d\n", -pid); + return(pid); + } + + if(data->stack == 0) free_stack(stack, 0); + + if (data->direct_rcv) { + new = os_rcv_fd(fd, &data->helper_pid); + } else { + err = os_set_fd_block(fd, 0); + if(err < 0){ + printk("xterm_open : failed to set descriptor " + "non-blocking, err = %d\n", -err); + return(err); + } + new = xterm_fd(fd, &data->helper_pid); + } + if(new < 0){ + printk("xterm_open : os_rcv_fd failed, err = %d\n", -new); + goto out; + } + + CATCH_EINTR(err = tcgetattr(new, &data->tt)); + if(err){ + new = err; + goto out; + } + + if(data->raw){ + err = raw(new); + if(err){ + new = err; + goto out; + } + } + + data->pid = pid; + *dev_out = NULL; + out: + unlink(file); + return(new); +} + +/* Not static because it's called directly by the tt mode gdb code */ +void xterm_close(int fd, void *d) +{ + struct xterm_chan *data = d; + + if(data->pid != -1) + os_kill_process(data->pid, 1); + data->pid = -1; + if(data->helper_pid != -1) + os_kill_process(data->helper_pid, 0); + data->helper_pid = -1; + os_close_file(fd); +} + +static void xterm_free(void *d) +{ + free(d); +} + +static int xterm_console_write(int fd, const char *buf, int n, void *d) +{ + struct xterm_chan *data = d; + + return(generic_console_write(fd, buf, n, &data->tt)); +} + +struct chan_ops xterm_ops = { + .type = "xterm", + .init = xterm_init, + .open = xterm_open, + .close = xterm_close, + .read = generic_read, + .write = generic_write, + .console_write = xterm_console_write, + .window_size = generic_window_size, + .free = xterm_free, + .winch = 1, +}; + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/xterm.h b/arch/um/drivers/xterm.h new file mode 100644 index 00000000000..f33a6e77b18 --- /dev/null +++ b/arch/um/drivers/xterm.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __XTERM_H__ +#define __XTERM_H__ + +extern int xterm_fd(int socket, int *pid_out); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/drivers/xterm_kern.c b/arch/um/drivers/xterm_kern.c new file mode 100644 index 00000000000..7917b9d1cec --- /dev/null +++ b/arch/um/drivers/xterm_kern.c @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/errno.h" +#include "linux/slab.h" +#include "linux/signal.h" +#include "linux/interrupt.h" +#include "asm/semaphore.h" +#include "asm/irq.h" +#include "irq_user.h" +#include "irq_kern.h" +#include "kern_util.h" +#include "os.h" +#include "xterm.h" + +struct xterm_wait { + struct completion ready; + int fd; + int pid; + int new_fd; +}; + +static irqreturn_t xterm_interrupt(int irq, void *data, struct pt_regs *regs) +{ + struct xterm_wait *xterm = data; + int fd; + + fd = os_rcv_fd(xterm->fd, &xterm->pid); + if(fd == -EAGAIN) + return(IRQ_NONE); + + xterm->new_fd = fd; + complete(&xterm->ready); + return(IRQ_HANDLED); +} + +int xterm_fd(int socket, int *pid_out) +{ + struct xterm_wait *data; + int err, ret; + + data = kmalloc(sizeof(*data), GFP_KERNEL); + if(data == NULL){ + printk(KERN_ERR "xterm_fd : failed to allocate xterm_wait\n"); + return(-ENOMEM); + } + + /* This is a locked semaphore... */ + *data = ((struct xterm_wait) + { .fd = socket, + .pid = -1, + .new_fd = -1 }); + init_completion(&data->ready); + + err = um_request_irq(XTERM_IRQ, socket, IRQ_READ, xterm_interrupt, + SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM, + "xterm", data); + if (err){ + printk(KERN_ERR "xterm_fd : failed to get IRQ for xterm, " + "err = %d\n", err); + ret = err; + goto out; + } + + /* ... so here we wait for an xterm interrupt. + * + * XXX Note, if the xterm doesn't work for some reason (eg. DISPLAY + * isn't set) this will hang... */ + wait_for_completion(&data->ready); + + free_irq_by_irq_and_dev(XTERM_IRQ, data); + free_irq(XTERM_IRQ, data); + + ret = data->new_fd; + *pid_out = data->pid; + out: + kfree(data); + + return(ret); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/2_5compat.h b/arch/um/include/2_5compat.h new file mode 100644 index 00000000000..abdb015a4d7 --- /dev/null +++ b/arch/um/include/2_5compat.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __2_5_COMPAT_H__ +#define __2_5_COMPAT_H__ + +#define INIT_HARDSECT(arr, maj, sizes) + +#define SET_PRI(task) do ; while(0) + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/chan_kern.h b/arch/um/include/chan_kern.h new file mode 100644 index 00000000000..da9a6717e7a --- /dev/null +++ b/arch/um/include/chan_kern.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __CHAN_KERN_H__ +#define __CHAN_KERN_H__ + +#include "linux/tty.h" +#include "linux/list.h" +#include "linux/console.h" +#include "chan_user.h" +#include "line.h" + +struct chan { + struct list_head list; + char *dev; + unsigned int primary:1; + unsigned int input:1; + unsigned int output:1; + unsigned int opened:1; + int fd; + enum chan_init_pri pri; + struct chan_ops *ops; + void *data; +}; + +extern void chan_interrupt(struct list_head *chans, struct work_struct *task, + struct tty_struct *tty, int irq); +extern int parse_chan_pair(char *str, struct list_head *chans, int pri, + int device, struct chan_opts *opts); +extern int open_chan(struct list_head *chans); +extern int write_chan(struct list_head *chans, const char *buf, int len, + int write_irq); +extern int console_write_chan(struct list_head *chans, const char *buf, + int len); +extern int console_open_chan(struct line *line, struct console *co, + struct chan_opts *opts); +extern void close_chan(struct list_head *chans); +extern void chan_enable_winch(struct list_head *chans, struct tty_struct *tty); +extern void enable_chan(struct list_head *chans, struct tty_struct *tty); +extern int chan_window_size(struct list_head *chans, + unsigned short *rows_out, + unsigned short *cols_out); +extern int chan_out_fd(struct list_head *chans); +extern int chan_config_string(struct list_head *chans, char *str, int size, + char **error_out); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/chan_user.h b/arch/um/include/chan_user.h new file mode 100644 index 00000000000..f77d9aa4c16 --- /dev/null +++ b/arch/um/include/chan_user.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __CHAN_USER_H__ +#define __CHAN_USER_H__ + +#include "init.h" + +struct chan_opts { + void (*announce)(char *dev_name, int dev); + char *xterm_title; + int raw; + unsigned long tramp_stack; + int in_kernel; +}; + +enum chan_init_pri { INIT_STATIC, INIT_ALL, INIT_ONE }; + +struct chan_ops { + char *type; + void *(*init)(char *, int, struct chan_opts *); + int (*open)(int, int, int, void *, char **); + void (*close)(int, void *); + int (*read)(int, char *, void *); + int (*write)(int, const char *, int, void *); + int (*console_write)(int, const char *, int, void *); + int (*window_size)(int, void *, unsigned short *, unsigned short *); + void (*free)(void *); + int winch; +}; + +extern struct chan_ops fd_ops, null_ops, port_ops, pts_ops, pty_ops, tty_ops, + xterm_ops; + +extern void generic_close(int fd, void *unused); +extern int generic_read(int fd, char *c_out, void *unused); +extern int generic_write(int fd, const char *buf, int n, void *unused); +extern int generic_console_write(int fd, const char *buf, int n, void *state); +extern int generic_window_size(int fd, void *unused, unsigned short *rows_out, + unsigned short *cols_out); +extern void generic_free(void *data); + +struct tty_struct; +extern void register_winch(int fd, struct tty_struct *tty); +extern void register_winch_irq(int fd, int tty_fd, int pid, struct tty_struct *tty); + +#define __channel_help(fn, prefix) \ +__uml_help(fn, prefix "[0-9]*=<channel description>\n" \ +" Attach a console or serial line to a host channel. See\n" \ +" http://user-mode-linux.sourceforge.net/input.html for a complete\n" \ +" description of this switch.\n\n" \ +); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/choose-mode.h b/arch/um/include/choose-mode.h new file mode 100644 index 00000000000..8e6b62f5e9a --- /dev/null +++ b/arch/um/include/choose-mode.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __CHOOSE_MODE_H__ +#define __CHOOSE_MODE_H__ + +#include "uml-config.h" + +#if defined(UML_CONFIG_MODE_TT) && defined(UML_CONFIG_MODE_SKAS) +#define CHOOSE_MODE(tt, skas) (mode_tt ? (tt) : (skas)) + +#elif defined(UML_CONFIG_MODE_SKAS) +#define CHOOSE_MODE(tt, skas) (skas) + +#elif defined(UML_CONFIG_MODE_TT) +#define CHOOSE_MODE(tt, skas) (tt) +#endif + +#define CHOOSE_MODE_PROC(tt, skas, args...) \ + CHOOSE_MODE(tt(args), skas(args)) + +extern int mode_tt; +static inline void *__choose_mode(void *tt, void *skas) { + return mode_tt ? tt : skas; +} + +#define __CHOOSE_MODE(tt, skas) (*( (typeof(tt) *) __choose_mode(&(tt), &(skas)))) + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/elf_user.h b/arch/um/include/elf_user.h new file mode 100644 index 00000000000..53516b63727 --- /dev/null +++ b/arch/um/include/elf_user.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2004 Fujitsu Siemens Computers GmbH + * Author: Bodo Stroesser <bstroesser@fujitsu-siemens.com> + * Licensed under the GPL + */ + +#ifndef __ELF_USER_H__ +#define __ELF_USER_H__ + +/* For compilation on a host that doesn't support AT_SYSINFO (Linux 2.4) */ + +#ifndef AT_SYSINFO +#define AT_SYSINFO 32 +#endif +#ifndef AT_SYSINFO_EHDR +#define AT_SYSINFO_EHDR 33 +#endif + +#endif diff --git a/arch/um/include/frame_kern.h b/arch/um/include/frame_kern.h new file mode 100644 index 00000000000..ce9514f5721 --- /dev/null +++ b/arch/um/include/frame_kern.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __FRAME_KERN_H_ +#define __FRAME_KERN_H_ + +#define _S(nr) (1<<((nr)-1)) +#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) + +extern int setup_signal_stack_sc(unsigned long stack_top, int sig, + struct k_sigaction *ka, + struct pt_regs *regs, + sigset_t *mask); +extern int setup_signal_stack_si(unsigned long stack_top, int sig, + struct k_sigaction *ka, + struct pt_regs *regs, siginfo_t *info, + sigset_t *mask); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/helper.h b/arch/um/include/helper.h new file mode 100644 index 00000000000..162ac31192f --- /dev/null +++ b/arch/um/include/helper.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __HELPER_H__ +#define __HELPER_H__ + +extern int run_helper(void (*pre_exec)(void *), void *pre_data, char **argv, + unsigned long *stack_out); +extern int run_helper_thread(int (*proc)(void *), void *arg, + unsigned int flags, unsigned long *stack_out, + int stack_order); +extern int helper_wait(int pid); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/init.h b/arch/um/include/init.h new file mode 100644 index 00000000000..55c2693f877 --- /dev/null +++ b/arch/um/include/init.h @@ -0,0 +1,132 @@ +#ifndef _LINUX_UML_INIT_H +#define _LINUX_UML_INIT_H + +/* These macros are used to mark some functions or + * initialized data (doesn't apply to uninitialized data) + * as `initialization' functions. The kernel can take this + * as hint that the function is used only during the initialization + * phase and free up used memory resources after + * + * Usage: + * For functions: + * + * You should add __init immediately before the function name, like: + * + * static void __init initme(int x, int y) + * { + * extern int z; z = x * y; + * } + * + * If the function has a prototype somewhere, you can also add + * __init between closing brace of the prototype and semicolon: + * + * extern int initialize_foobar_device(int, int, int) __init; + * + * For initialized data: + * You should insert __initdata between the variable name and equal + * sign followed by value, e.g.: + * + * static int init_variable __initdata = 0; + * static char linux_logo[] __initdata = { 0x32, 0x36, ... }; + * + * Don't forget to initialize data not at file scope, i.e. within a function, + * as gcc otherwise puts the data into the bss section and not into the init + * section. + * + * Also note, that this data cannot be "const". + */ + +#ifndef _LINUX_INIT_H +typedef int (*initcall_t)(void); +typedef void (*exitcall_t)(void); + +/* These are for everybody (although not all archs will actually + discard it in modules) */ +#define __init __attribute__ ((__section__ (".init.text"))) +#define __initdata __attribute__ ((__section__ (".init.data"))) +#define __exitdata __attribute__ ((__section__(".exit.data"))) +#define __exit_call __attribute_used__ __attribute__ ((__section__ (".exitcall.exit"))) + +#ifdef MODULE +#define __exit __attribute__ ((__section__(".exit.text"))) +#else +#define __exit __attribute_used__ __attribute__ ((__section__(".exit.text"))) +#endif + +#endif + +#ifndef MODULE +struct uml_param { + const char *str; + int (*setup_func)(char *, int *); +}; + +extern initcall_t __uml_initcall_start, __uml_initcall_end; +extern initcall_t __uml_postsetup_start, __uml_postsetup_end; +extern const char *__uml_help_start, *__uml_help_end; +#endif + +#define __uml_initcall(fn) \ + static initcall_t __uml_initcall_##fn __uml_init_call = fn + +#define __uml_exitcall(fn) \ + static exitcall_t __uml_exitcall_##fn __uml_exit_call = fn + +extern struct uml_param __uml_setup_start, __uml_setup_end; + +#define __uml_postsetup(fn) \ + static initcall_t __uml_postsetup_##fn __uml_postsetup_call = fn + +#define __non_empty_string(dummyname,string) \ + struct __uml_non_empty_string_struct_##dummyname \ + { \ + char _string[sizeof(string)-2]; \ + } + +#ifndef MODULE +#define __uml_setup(str, fn, help...) \ + __non_empty_string(fn ##_setup, str); \ + __uml_help(fn, help); \ + static char __uml_setup_str_##fn[] __initdata = str; \ + static struct uml_param __uml_setup_##fn __uml_init_setup = { __uml_setup_str_##fn, fn } +#else +#define __uml_setup(str, fn, help...) \ + +#endif + +#define __uml_help(fn, help...) \ + __non_empty_string(fn ##__help, help); \ + static char __uml_help_str_##fn[] __initdata = help; \ + static const char *__uml_help_##fn __uml_setup_help = __uml_help_str_##fn + +/* + * Mark functions and data as being only used at initialization + * or exit time. + */ +#define __uml_init_setup __attribute_used__ __attribute__ ((__section__ (".uml.setup.init"))) +#define __uml_setup_help __attribute_used__ __attribute__ ((__section__ (".uml.help.init"))) +#define __uml_init_call __attribute_used__ __attribute__ ((__section__ (".uml.initcall.init"))) +#define __uml_postsetup_call __attribute_used__ __attribute__ ((__section__ (".uml.postsetup.init"))) +#define __uml_exit_call __attribute_used__ __attribute__ ((__section__ (".uml.exitcall.exit"))) + +#ifndef __KERNEL__ + +#define __initcall(fn) static initcall_t __initcall_##fn __init_call = fn +#define __exitcall(fn) static exitcall_t __exitcall_##fn __exit_call = fn + +#define __init_call __attribute__ ((unused,__section__ (".initcall.init"))) + +#endif + +#endif /* _LINUX_UML_INIT_H */ + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/initrd.h b/arch/um/include/initrd.h new file mode 100644 index 00000000000..439b9a81498 --- /dev/null +++ b/arch/um/include/initrd.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __INITRD_USER_H__ +#define __INITRD_USER_H__ + +extern int load_initrd(char *filename, void *buf, int size); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/irq_kern.h b/arch/um/include/irq_kern.h new file mode 100644 index 00000000000..3af52a634c4 --- /dev/null +++ b/arch/um/include/irq_kern.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __IRQ_KERN_H__ +#define __IRQ_KERN_H__ + +#include "linux/interrupt.h" + +extern int um_request_irq(unsigned int irq, int fd, int type, + irqreturn_t (*handler)(int, void *, + struct pt_regs *), + unsigned long irqflags, const char * devname, + void *dev_id); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/irq_user.h b/arch/um/include/irq_user.h new file mode 100644 index 00000000000..f724b717213 --- /dev/null +++ b/arch/um/include/irq_user.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __IRQ_USER_H__ +#define __IRQ_USER_H__ + +enum { IRQ_READ, IRQ_WRITE }; + +extern void sigio_handler(int sig, union uml_pt_regs *regs); +extern int activate_fd(int irq, int fd, int type, void *dev_id); +extern void free_irq_by_irq_and_dev(unsigned int irq, void *dev_id); +extern void free_irq_by_fd(int fd); +extern void reactivate_fd(int fd, int irqnum); +extern void deactivate_fd(int fd, int irqnum); +extern int deactivate_all_fds(void); +extern void forward_interrupts(int pid); +extern void init_irq_signals(int on_sigstack); +extern void forward_ipi(int fd, int pid); +extern void free_irq_later(int irq, void *dev_id); +extern int activate_ipi(int fd, int pid); +extern unsigned long irq_lock(void); +extern void irq_unlock(unsigned long flags); +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/kern.h b/arch/um/include/kern.h new file mode 100644 index 00000000000..1e3170768b5 --- /dev/null +++ b/arch/um/include/kern.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __KERN_H__ +#define __KERN_H__ + +/* These are all user-mode things which are convenient to call directly + * from kernel code and for which writing a wrapper is too much of a pain. + * The regular include files can't be included because this file is included + * only into kernel code, and user-space includes conflict with kernel + * includes. + */ + +extern int errno; + +extern int clone(int (*proc)(void *), void *sp, int flags, void *data); +extern int sleep(int); +extern int printf(char *fmt, ...); +extern char *strerror(int errnum); +extern char *ptsname(int __fd); +extern int munmap(void *, int); +extern void *sbrk(int increment); +extern void *malloc(int size); +extern void perror(char *err); +extern int kill(int pid, int sig); +extern int getuid(void); +extern int getgid(void); +extern int pause(void); +extern int write(int, const void *, int); +extern int exit(int); +extern int close(int); +extern int read(unsigned int, char *, int); +extern int pipe(int *); +extern int sched_yield(void); +extern int ptrace(int op, int pid, long addr, long data); +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/kern_util.h b/arch/um/include/kern_util.h new file mode 100644 index 00000000000..15389c886b4 --- /dev/null +++ b/arch/um/include/kern_util.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __KERN_UTIL_H__ +#define __KERN_UTIL_H__ + +#include "linux/threads.h" +#include "sysdep/ptrace.h" + +extern int ncpus; +extern char *linux_prog; +extern char *gdb_init; +extern int kmalloc_ok; +extern int timer_irq_inited; +extern int jail; +extern int nsyscalls; + +extern struct task_struct *idle_threads[NR_CPUS]; + +#define UML_ROUND_DOWN(addr) ((void *)(((unsigned long) addr) & PAGE_MASK)) +#define UML_ROUND_UP(addr) \ + UML_ROUND_DOWN(((unsigned long) addr) + PAGE_SIZE - 1) + +extern int kernel_fork(unsigned long flags, int (*fn)(void *), void * arg); +extern unsigned long stack_sp(unsigned long page); +extern int kernel_thread_proc(void *data); +extern void syscall_segv(int sig); +extern int current_pid(void); +extern unsigned long alloc_stack(int order, int atomic); +extern int do_signal(void); +extern int is_stack_fault(unsigned long sp); +extern unsigned long segv(unsigned long address, unsigned long ip, + int is_write, int is_user, void *sc); +extern int handle_page_fault(unsigned long address, unsigned long ip, + int is_write, int is_user, int *code_out); +extern void syscall_ready(void); +extern void set_tracing(void *t, int tracing); +extern int is_tracing(void *task); +extern int segv_syscall(void); +extern void kern_finish_exec(void *task, int new_pid, unsigned long stack); +extern int page_size(void); +extern unsigned long page_mask(void); +extern int need_finish_fork(void); +extern void free_stack(unsigned long stack, int order); +extern void add_input_request(int op, void (*proc)(int), void *arg); +extern char *current_cmd(void); +extern void timer_handler(int sig, union uml_pt_regs *regs); +extern int set_signals(int enable); +extern void force_sigbus(void); +extern int pid_to_processor_id(int pid); +extern void block_signals(void); +extern void unblock_signals(void); +extern void deliver_signals(void *t); +extern int next_syscall_index(int max); +extern int next_trap_index(int max); +extern void default_idle(void); +extern void finish_fork(void); +extern void paging_init(void); +extern void init_flush_vm(void); +extern void *syscall_sp(void *t); +extern void syscall_trace(union uml_pt_regs *regs, int entryexit); +extern int hz(void); +extern void uml_idle_timer(void); +extern unsigned int do_IRQ(int irq, union uml_pt_regs *regs); +extern int external_pid(void *t); +extern void boot_timer_handler(int sig); +extern void interrupt_end(void); +extern void initial_thread_cb(void (*proc)(void *), void *arg); +extern int debugger_signal(int status, int pid); +extern void debugger_parent_signal(int status, int pid); +extern void child_signal(int pid, int status); +extern int init_ptrace_proxy(int idle_pid, int startup, int stop); +extern int init_parent_proxy(int pid); +extern int singlestepping(void *t); +extern void check_stack_overflow(void *ptr); +extern void relay_signal(int sig, union uml_pt_regs *regs); +extern void not_implemented(void); +extern int user_context(unsigned long sp); +extern void timer_irq(union uml_pt_regs *regs); +extern void unprotect_stack(unsigned long stack); +extern void do_uml_exitcalls(void); +extern int attach_debugger(int idle_pid, int pid, int stop); +extern void bad_segv(unsigned long address, unsigned long ip, int is_write); +extern int config_gdb(char *str); +extern int remove_gdb(void); +extern char *uml_strdup(char *string); +extern void unprotect_kernel_mem(void); +extern void protect_kernel_mem(void); +extern void uml_cleanup(void); +extern void set_current(void *t); +extern void lock_signalled_task(void *t); +extern void IPI_handler(int cpu); +extern int jail_setup(char *line, int *add); +extern void *get_init_task(void); +extern int clear_user_proc(void *buf, int size); +extern int copy_to_user_proc(void *to, void *from, int size); +extern int copy_from_user_proc(void *to, void *from, int size); +extern int strlen_user_proc(char *str); +extern void bus_handler(int sig, union uml_pt_regs *regs); +extern void winch(int sig, union uml_pt_regs *regs); +extern long execute_syscall(void *r); +extern int smp_sigio_handler(void); +extern void *get_current(void); +extern struct task_struct *get_task(int pid, int require); +extern void machine_halt(void); +extern int is_syscall(unsigned long addr); +extern void arch_switch(void); +extern void free_irq(unsigned int, void *); +extern int um_in_interrupt(void); +extern int cpu(void); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/line.h b/arch/um/include/line.h new file mode 100644 index 00000000000..6d81ecc17be --- /dev/null +++ b/arch/um/include/line.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __LINE_H__ +#define __LINE_H__ + +#include "linux/list.h" +#include "linux/workqueue.h" +#include "linux/tty.h" +#include "linux/interrupt.h" +#include "asm/semaphore.h" +#include "chan_user.h" +#include "mconsole_kern.h" + +struct line_driver { + char *name; + char *device_name; + char *devfs_name; + short major; + short minor_start; + short type; + short subtype; + int read_irq; + char *read_irq_name; + int write_irq; + char *write_irq_name; + char *symlink_from; + char *symlink_to; + struct mc_device mc; +}; + +struct line { + char *init_str; + int init_pri; + struct list_head chan_list; + int valid; + int count; + struct semaphore sem; + char *buffer; + char *head; + char *tail; + int sigio; + struct work_struct task; + struct line_driver *driver; + int have_irq; +}; + +#define LINE_INIT(str, d) \ + { init_str : str, \ + init_pri : INIT_STATIC, \ + chan_list : { }, \ + valid : 1, \ + sem : { }, \ + buffer : NULL, \ + head : NULL, \ + tail : NULL, \ + sigio : 0, \ + driver : d, \ + have_irq : 0 } + +struct lines { + int num; +}; + +#define LINES_INIT(n) { num : n } + +extern void line_close(struct tty_struct *tty, struct file * filp); +extern int line_open(struct line *lines, struct tty_struct *tty, + struct chan_opts *opts); +extern int line_setup(struct line *lines, int num, char *init, + int all_allowed); +extern int line_write(struct tty_struct *tty, const unsigned char *buf, int len); +extern void line_put_char(struct tty_struct *tty, unsigned char ch); +extern void line_set_termios(struct tty_struct *tty, struct termios * old); +extern int line_chars_in_buffer(struct tty_struct *tty); +extern int line_write_room(struct tty_struct *tty); +extern int line_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg); +extern char *add_xterm_umid(char *base); +extern int line_setup_irq(int fd, int input, int output, struct tty_struct *tty); +extern void line_close_chan(struct line *line); +extern void line_disable(struct tty_struct *tty, int current_irq); +extern struct tty_driver * line_register_devfs(struct lines *set, + struct line_driver *line_driver, + struct tty_operations *driver, + struct line *lines, + int nlines); +extern void lines_init(struct line *lines, int nlines); +extern void close_lines(struct line *lines, int nlines); +extern int line_config(struct line *lines, int num, char *str); +extern int line_remove(struct line *lines, int num, char *str); +extern int line_get_config(char *dev, struct line *lines, int num, char *str, + int size, char **error_out); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/mconsole.h b/arch/um/include/mconsole.h new file mode 100644 index 00000000000..9fbe3083fdd --- /dev/null +++ b/arch/um/include/mconsole.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) + * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __MCONSOLE_H__ +#define __MCONSOLE_H__ + +#ifndef __KERNEL__ +#include <stdint.h> +#define u32 uint32_t +#endif + +#define MCONSOLE_MAGIC (0xcafebabe) +#define MCONSOLE_MAX_DATA (512) +#define MCONSOLE_VERSION 2 + +struct mconsole_request { + u32 magic; + u32 version; + u32 len; + char data[MCONSOLE_MAX_DATA]; +}; + +struct mconsole_reply { + u32 err; + u32 more; + u32 len; + char data[MCONSOLE_MAX_DATA]; +}; + +struct mconsole_notify { + u32 magic; + u32 version; + enum { MCONSOLE_SOCKET, MCONSOLE_PANIC, MCONSOLE_HANG, + MCONSOLE_USER_NOTIFY } type; + u32 len; + char data[MCONSOLE_MAX_DATA]; +}; + +struct mc_request; + +enum mc_context { MCONSOLE_INTR, MCONSOLE_PROC }; + +struct mconsole_command +{ + char *command; + void (*handler)(struct mc_request *req); + enum mc_context context; +}; + +struct mc_request +{ + int len; + int as_interrupt; + + int originating_fd; + int originlen; + unsigned char origin[128]; /* sockaddr_un */ + + struct mconsole_request request; + struct mconsole_command *cmd; +}; + +extern char mconsole_socket_name[]; + +extern int mconsole_unlink_socket(void); +extern int mconsole_reply(struct mc_request *req, char *reply, int err, + int more); + +extern void mconsole_version(struct mc_request *req); +extern void mconsole_help(struct mc_request *req); +extern void mconsole_halt(struct mc_request *req); +extern void mconsole_reboot(struct mc_request *req); +extern void mconsole_config(struct mc_request *req); +extern void mconsole_remove(struct mc_request *req); +extern void mconsole_sysrq(struct mc_request *req); +extern void mconsole_cad(struct mc_request *req); +extern void mconsole_stop(struct mc_request *req); +extern void mconsole_go(struct mc_request *req); +extern void mconsole_log(struct mc_request *req); +extern void mconsole_proc(struct mc_request *req); + +extern int mconsole_get_request(int fd, struct mc_request *req); +extern int mconsole_notify(char *sock_name, int type, const void *data, + int len); +extern char *mconsole_notify_socket(void); +extern void lock_notify(void); +extern void unlock_notify(void); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/mconsole_kern.h b/arch/um/include/mconsole_kern.h new file mode 100644 index 00000000000..61c274fcee5 --- /dev/null +++ b/arch/um/include/mconsole_kern.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __MCONSOLE_KERN_H__ +#define __MCONSOLE_KERN_H__ + +#include "linux/config.h" +#include "linux/list.h" +#include "mconsole.h" + +struct mconsole_entry { + struct list_head list; + struct mc_request request; +}; + +struct mc_device { + struct list_head list; + char *name; + int (*config)(char *); + int (*get_config)(char *, char *, int, char **); + int (*remove)(char *); +}; + +#define CONFIG_CHUNK(str, size, current, chunk, end) \ +do { \ + current += strlen(chunk); \ + if(current >= size) \ + str = NULL; \ + if(str != NULL){ \ + strcpy(str, chunk); \ + str += strlen(chunk); \ + } \ + if(end) \ + current++; \ +} while(0) + +#ifdef CONFIG_MCONSOLE + +extern void mconsole_register_dev(struct mc_device *new); + +#else + +static inline void mconsole_register_dev(struct mc_device *new) +{ +} + +#endif + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/mem.h b/arch/um/include/mem.h new file mode 100644 index 00000000000..10c46c38949 --- /dev/null +++ b/arch/um/include/mem.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2002, 2003 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#ifndef __MEM_H__ +#define __MEM_H__ + +#include "linux/types.h" + +extern int phys_mapping(unsigned long phys, __u64 *offset_out); +extern int physmem_subst_mapping(void *virt, int fd, __u64 offset, int w); +extern int is_remapped(void *virt); +extern int physmem_remove_mapping(void *virt); +extern void physmem_forget_descriptor(int fd); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/mem_kern.h b/arch/um/include/mem_kern.h new file mode 100644 index 00000000000..cb7e196d366 --- /dev/null +++ b/arch/um/include/mem_kern.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2003 Jeff Dike (jdike@addtoit.com) + * Licensed under the GPL + */ + +#ifndef __MEM_KERN_H__ +#define __MEM_KERN_H__ + +#include "linux/list.h" +#include "linux/types.h" + +struct remapper { + struct list_head list; + int (*proc)(int, unsigned long, int, __u64); +}; + +extern void register_remapper(struct remapper *info); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/mem_user.h b/arch/um/include/mem_user.h new file mode 100644 index 00000000000..d6404bb6466 --- /dev/null +++ b/arch/um/include/mem_user.h @@ -0,0 +1,83 @@ +/* + * arch/um/include/mem_user.h + * + * BRIEF MODULE DESCRIPTION + * user side memory interface for support IO memory inside user mode linux + * + * Copyright (C) 2001 RidgeRun, Inc. + * Author: RidgeRun, Inc. + * Greg Lonnon glonnon@ridgerun.com or info@ridgerun.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _MEM_USER_H +#define _MEM_USER_H + +struct iomem_region { + struct iomem_region *next; + char *driver; + int fd; + int size; + unsigned long phys; + unsigned long virt; +}; + +extern struct iomem_region *iomem_regions; +extern int iomem_size; + +#define ROUND_4M(n) ((((unsigned long) (n)) + (1 << 22)) & ~((1 << 22) - 1)) + +extern unsigned long host_task_size; +extern unsigned long task_size; + +extern void check_devanon(void); +extern int init_mem_user(void); +extern int create_mem_file(unsigned long len); +extern void setup_memory(void *entry); +extern unsigned long find_iomem(char *driver, unsigned long *len_out); +extern int init_maps(unsigned long physmem, unsigned long iomem, + unsigned long highmem); +extern unsigned long get_vm(unsigned long len); +extern void setup_physmem(unsigned long start, unsigned long usable, + unsigned long len, unsigned long highmem); +extern void add_iomem(char *name, int fd, unsigned long size); +extern unsigned long phys_offset(unsigned long phys); +extern void unmap_physmem(void); +extern void map_memory(unsigned long virt, unsigned long phys, + unsigned long len, int r, int w, int x); +extern int protect_memory(unsigned long addr, unsigned long len, + int r, int w, int x, int must_succeed); +extern unsigned long get_kmem_end(void); +extern void check_tmpexec(void); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/mode.h b/arch/um/include/mode.h new file mode 100644 index 00000000000..786cf563eb0 --- /dev/null +++ b/arch/um/include/mode.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __MODE_H__ +#define __MODE_H__ + +#include "uml-config.h" + +#ifdef UML_CONFIG_MODE_TT +#include "mode-tt.h" +#endif + +#ifdef UML_CONFIG_MODE_SKAS +#include "mode-skas.h" +#endif + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/mode_kern.h b/arch/um/include/mode_kern.h new file mode 100644 index 00000000000..2d88afd0cf1 --- /dev/null +++ b/arch/um/include/mode_kern.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __MODE_KERN_H__ +#define __MODE_KERN_H__ + +#include "linux/config.h" + +#ifdef CONFIG_MODE_TT +#include "mode_kern-tt.h" +#endif + +#ifdef CONFIG_MODE_SKAS +#include "mode_kern-skas.h" +#endif + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/net_kern.h b/arch/um/include/net_kern.h new file mode 100644 index 00000000000..1c07949a13d --- /dev/null +++ b/arch/um/include/net_kern.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __UM_NET_KERN_H +#define __UM_NET_KERN_H + +#include "linux/netdevice.h" +#include "linux/skbuff.h" +#include "linux/socket.h" +#include "linux/list.h" + +struct uml_net { + struct list_head list; + struct net_device *dev; + struct platform_device pdev; + int index; + unsigned char mac[ETH_ALEN]; + int have_mac; +}; + +struct uml_net_private { + struct list_head list; + spinlock_t lock; + struct net_device *dev; + struct timer_list tl; + struct net_device_stats stats; + int fd; + unsigned char mac[ETH_ALEN]; + int have_mac; + unsigned short (*protocol)(struct sk_buff *); + int (*open)(void *); + void (*close)(int, void *); + void (*remove)(void *); + int (*read)(int, struct sk_buff **skb, struct uml_net_private *); + int (*write)(int, struct sk_buff **skb, struct uml_net_private *); + + void (*add_address)(unsigned char *, unsigned char *, void *); + void (*delete_address)(unsigned char *, unsigned char *, void *); + int (*set_mtu)(int mtu, void *); + int user[1]; +}; + +struct net_kern_info { + void (*init)(struct net_device *, void *); + unsigned short (*protocol)(struct sk_buff *); + int (*read)(int, struct sk_buff **skb, struct uml_net_private *); + int (*write)(int, struct sk_buff **skb, struct uml_net_private *); +}; + +struct transport { + struct list_head list; + char *name; + int (*setup)(char *, char **, void *); + struct net_user_info *user; + struct net_kern_info *kern; + int private_size; + int setup_size; +}; + +extern struct net_device *ether_init(int); +extern unsigned short ether_protocol(struct sk_buff *); +extern int setup_etheraddr(char *str, unsigned char *addr); +extern struct sk_buff *ether_adjust_skb(struct sk_buff *skb, int extra); +extern int tap_setup_common(char *str, char *type, char **dev_name, + char **mac_out, char **gate_addr); +extern void register_transport(struct transport *new); +extern unsigned short eth_protocol(struct sk_buff *skb); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/net_user.h b/arch/um/include/net_user.h new file mode 100644 index 00000000000..36807b796e9 --- /dev/null +++ b/arch/um/include/net_user.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __UM_NET_USER_H__ +#define __UM_NET_USER_H__ + +#define ETH_ADDR_LEN (6) +#define ETH_HEADER_ETHERTAP (16) +#define ETH_HEADER_OTHER (14) +#define ETH_MAX_PACKET (1500) + +#define UML_NET_VERSION (4) + +struct net_user_info { + void (*init)(void *, void *); + int (*open)(void *); + void (*close)(int, void *); + void (*remove)(void *); + int (*set_mtu)(int mtu, void *); + void (*add_address)(unsigned char *, unsigned char *, void *); + void (*delete_address)(unsigned char *, unsigned char *, void *); + int max_packet; +}; + +extern void ether_user_init(void *data, void *dev); +extern void dev_ip_addr(void *d, char *buf, char *bin_buf); +extern void set_ether_mac(void *d, unsigned char *addr); +extern void iter_addresses(void *d, void (*cb)(unsigned char *, + unsigned char *, void *), + void *arg); + +extern void *get_output_buffer(int *len_out); +extern void free_output_buffer(void *buffer); + +extern int tap_open_common(void *dev, char *gate_addr); +extern void tap_check_ips(char *gate_addr, char *eth_addr); + +extern void read_output(int fd, char *output_out, int len); + +extern int net_read(int fd, void *buf, int len); +extern int net_recvfrom(int fd, void *buf, int len); +extern int net_write(int fd, void *buf, int len); +extern int net_send(int fd, void *buf, int len); +extern int net_sendto(int fd, void *buf, int len, void *to, int sock_len); + +extern void open_addr(unsigned char *addr, unsigned char *netmask, void *arg); +extern void close_addr(unsigned char *addr, unsigned char *netmask, void *arg); + +extern char *split_if_spec(char *str, ...); + +extern int dev_netmask(void *d, void *m); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/os.h b/arch/um/include/os.h new file mode 100644 index 00000000000..07340c8cf20 --- /dev/null +++ b/arch/um/include/os.h @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __OS_H__ +#define __OS_H__ + +#include "asm/types.h" +#include "../os/include/file.h" + +#define OS_TYPE_FILE 1 +#define OS_TYPE_DIR 2 +#define OS_TYPE_SYMLINK 3 +#define OS_TYPE_CHARDEV 4 +#define OS_TYPE_BLOCKDEV 5 +#define OS_TYPE_FIFO 6 +#define OS_TYPE_SOCK 7 + +/* os_access() flags */ +#define OS_ACC_F_OK 0 /* Test for existence. */ +#define OS_ACC_X_OK 1 /* Test for execute permission. */ +#define OS_ACC_W_OK 2 /* Test for write permission. */ +#define OS_ACC_R_OK 4 /* Test for read permission. */ +#define OS_ACC_RW_OK (OS_ACC_W_OK | OS_ACC_R_OK) /* Test for RW permission */ + +/* + * types taken from stat_file() in hostfs_user.c + * (if they are wrong here, they are wrong there...). + */ +struct uml_stat { + int ust_dev; /* device */ + unsigned long long ust_ino; /* inode */ + int ust_mode; /* protection */ + int ust_nlink; /* number of hard links */ + int ust_uid; /* user ID of owner */ + int ust_gid; /* group ID of owner */ + unsigned long long ust_size; /* total size, in bytes */ + int ust_blksize; /* blocksize for filesystem I/O */ + unsigned long long ust_blocks; /* number of blocks allocated */ + unsigned long ust_atime; /* time of last access */ + unsigned long ust_mtime; /* time of last modification */ + unsigned long ust_ctime; /* time of last change */ +}; + +struct openflags { + unsigned int r : 1; + unsigned int w : 1; + unsigned int s : 1; /* O_SYNC */ + unsigned int c : 1; /* O_CREAT */ + unsigned int t : 1; /* O_TRUNC */ + unsigned int a : 1; /* O_APPEND */ + unsigned int e : 1; /* O_EXCL */ + unsigned int cl : 1; /* FD_CLOEXEC */ +}; + +#define OPENFLAGS() ((struct openflags) { .r = 0, .w = 0, .s = 0, .c = 0, \ + .t = 0, .a = 0, .e = 0, .cl = 0 }) + +static inline struct openflags of_read(struct openflags flags) +{ + flags.r = 1; + return(flags); +} + +static inline struct openflags of_write(struct openflags flags) +{ + flags.w = 1; + return(flags); +} + +static inline struct openflags of_rdwr(struct openflags flags) +{ + return(of_read(of_write(flags))); +} + +static inline struct openflags of_set_rw(struct openflags flags, int r, int w) +{ + flags.r = r; + flags.w = w; + return(flags); +} + +static inline struct openflags of_sync(struct openflags flags) +{ + flags.s = 1; + return(flags); +} + +static inline struct openflags of_create(struct openflags flags) +{ + flags.c = 1; + return(flags); +} + +static inline struct openflags of_trunc(struct openflags flags) +{ + flags.t = 1; + return(flags); +} + +static inline struct openflags of_append(struct openflags flags) +{ + flags.a = 1; + return(flags); +} + +static inline struct openflags of_excl(struct openflags flags) +{ + flags.e = 1; + return(flags); +} + +static inline struct openflags of_cloexec(struct openflags flags) +{ + flags.cl = 1; + return(flags); +} + +extern int os_stat_file(const char *file_name, struct uml_stat *buf); +extern int os_stat_fd(const int fd, struct uml_stat *buf); +extern int os_access(const char *file, int mode); +extern void os_print_error(int error, const char* str); +extern int os_get_exec_close(int fd, int *close_on_exec); +extern int os_set_exec_close(int fd, int close_on_exec); +extern int os_ioctl_generic(int fd, unsigned int cmd, unsigned long arg); +extern int os_window_size(int fd, int *rows, int *cols); +extern int os_new_tty_pgrp(int fd, int pid); +extern int os_get_ifname(int fd, char *namebuf); +extern int os_set_slip(int fd); +extern int os_set_owner(int fd, int pid); +extern int os_sigio_async(int master, int slave); +extern int os_mode_fd(int fd, int mode); + +extern int os_seek_file(int fd, __u64 offset); +extern int os_open_file(char *file, struct openflags flags, int mode); +extern int os_read_file(int fd, void *buf, int len); +extern int os_write_file(int fd, const void *buf, int count); +extern int os_file_size(char *file, long long *size_out); +extern int os_file_modtime(char *file, unsigned long *modtime); +extern int os_pipe(int *fd, int stream, int close_on_exec); +extern int os_set_fd_async(int fd, int owner); +extern int os_clear_fd_async(int fd); +extern int os_set_fd_block(int fd, int blocking); +extern int os_accept_connection(int fd); +extern int os_create_unix_socket(char *file, int len, int close_on_exec); +extern int os_shutdown_socket(int fd, int r, int w); +extern void os_close_file(int fd); +extern int os_rcv_fd(int fd, int *helper_pid_out); +extern int create_unix_socket(char *file, int len, int close_on_exec); +extern int os_connect_socket(char *name); +extern int os_file_type(char *file); +extern int os_file_mode(char *file, struct openflags *mode_out); +extern int os_lock_file(int fd, int excl); + +extern unsigned long os_process_pc(int pid); +extern int os_process_parent(int pid); +extern void os_stop_process(int pid); +extern void os_kill_process(int pid, int reap_child); +extern void os_kill_ptraced_process(int pid, int reap_child); +extern void os_usr1_process(int pid); +extern int os_getpid(void); + +extern int os_map_memory(void *virt, int fd, unsigned long long off, + unsigned long len, int r, int w, int x); +extern int os_protect_memory(void *addr, unsigned long len, + int r, int w, int x); +extern int os_unmap_memory(void *addr, int len); +extern void os_flush_stdout(void); +extern unsigned long long os_usecs(void); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/process.h b/arch/um/include/process.h new file mode 100644 index 00000000000..5af9157ff54 --- /dev/null +++ b/arch/um/include/process.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __PROCESS_H__ +#define __PROCESS_H__ + +#include <signal.h> + +extern void sig_handler(int sig, struct sigcontext sc); +extern void alarm_handler(int sig, struct sigcontext sc); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/ptrace_user.h b/arch/um/include/ptrace_user.h new file mode 100644 index 00000000000..f3450e6bc18 --- /dev/null +++ b/arch/um/include/ptrace_user.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __PTRACE_USER_H__ +#define __PTRACE_USER_H__ + +#include "sysdep/ptrace_user.h" + +extern int ptrace_getregs(long pid, unsigned long *regs_out); +extern int ptrace_setregs(long pid, unsigned long *regs_in); +extern int ptrace_getfpregs(long pid, unsigned long *regs_out); +extern int ptrace_setfpregs(long pid, unsigned long *regs); +extern void arch_enter_kernel(void *task, int pid); +extern void arch_leave_kernel(void *task, int pid); +extern void ptrace_pokeuser(unsigned long addr, unsigned long data); + + +/* syscall emulation path in ptrace */ + +#ifndef PTRACE_SYSEMU +#define PTRACE_SYSEMU 31 +#endif +#ifndef PTRACE_SYSEMU_SINGLESTEP +#define PTRACE_SYSEMU_SINGLESTEP 32 +#endif + +/* On architectures, that started to support PTRACE_O_TRACESYSGOOD + * in linux 2.4, there are two different definitions of + * PTRACE_SETOPTIONS: linux 2.4 uses 21 while linux 2.6 uses 0x4200. + * For binary compatibility, 2.6 also supports the old "21", named + * PTRACE_OLDSETOPTION. On these architectures, UML always must use + * "21", to ensure the kernel runs on 2.4 and 2.6 host without + * recompilation. So, we use PTRACE_OLDSETOPTIONS in UML. + * We also want to be able to build the kernel on 2.4, which doesn't + * have PTRACE_OLDSETOPTIONS. So, if it is missing, we declare + * PTRACE_OLDSETOPTIONS to to be the same as PTRACE_SETOPTIONS. + * + * On architectures, that start to support PTRACE_O_TRACESYSGOOD on + * linux 2.6, PTRACE_OLDSETOPTIONS never is defined, and also isn't + * supported by the host kernel. In that case, our trick lets us use + * the new 0x4200 with the name PTRACE_OLDSETOPTIONS. + */ +#ifndef PTRACE_OLDSETOPTIONS +#define PTRACE_OLDSETOPTIONS PTRACE_SETOPTIONS +#endif + +void set_using_sysemu(int value); +int get_using_sysemu(void); +extern int sysemu_supported; + +#define SELECT_PTRACE_OPERATION(sysemu_mode, singlestep_mode) \ + (((int[3][3] ) { \ + { PTRACE_SYSCALL, PTRACE_SYSCALL, PTRACE_SINGLESTEP }, \ + { PTRACE_SYSEMU, PTRACE_SYSEMU, PTRACE_SINGLESTEP }, \ + { PTRACE_SYSEMU, PTRACE_SYSEMU_SINGLESTEP, PTRACE_SYSEMU_SINGLESTEP }}) \ + [sysemu_mode][singlestep_mode]) + +#endif diff --git a/arch/um/include/registers.h b/arch/um/include/registers.h new file mode 100644 index 00000000000..8744abb5224 --- /dev/null +++ b/arch/um/include/registers.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2004 PathScale, Inc + * Licensed under the GPL + */ + +#ifndef __REGISTERS_H +#define __REGISTERS_H + +#include "sysdep/ptrace.h" + +extern void init_thread_registers(union uml_pt_regs *to); +extern int save_fp_registers(int pid, unsigned long *fp_regs); +extern int restore_fp_registers(int pid, unsigned long *fp_regs); +extern void save_registers(int pid, union uml_pt_regs *regs); +extern void restore_registers(int pid, union uml_pt_regs *regs); +extern void init_registers(int pid); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/sigcontext.h b/arch/um/include/sigcontext.h new file mode 100644 index 00000000000..59816ca7a8d --- /dev/null +++ b/arch/um/include/sigcontext.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __UML_SIGCONTEXT_H__ +#define __UML_SIGCONTEXT_H__ + +#include "sysdep/sigcontext.h" + +extern int sc_size(void *data); +extern void sc_to_sc(void *to_ptr, void *from_ptr); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/sigio.h b/arch/um/include/sigio.h new file mode 100644 index 00000000000..37d76e29a14 --- /dev/null +++ b/arch/um/include/sigio.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SIGIO_H__ +#define __SIGIO_H__ + +extern int write_sigio_irq(int fd); +extern int register_sigio_fd(int fd); +extern int read_sigio_fd(int fd); +extern int add_sigio_fd(int fd, int read); +extern int ignore_sigio_fd(int fd); +extern void sigio_lock(void); +extern void sigio_unlock(void); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/signal_kern.h b/arch/um/include/signal_kern.h new file mode 100644 index 00000000000..aeb5d5ab1df --- /dev/null +++ b/arch/um/include/signal_kern.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SIGNAL_KERN_H__ +#define __SIGNAL_KERN_H__ + +extern int have_signals(void *t); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/signal_user.h b/arch/um/include/signal_user.h new file mode 100644 index 00000000000..b075e543d86 --- /dev/null +++ b/arch/um/include/signal_user.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SIGNAL_USER_H__ +#define __SIGNAL_USER_H__ + +extern int signal_stack_size; + +extern int change_sig(int signal, int on); +extern void set_sigstack(void *stack, int size); +extern void set_handler(int sig, void (*handler)(int), int flags, ...); +extern int set_signals(int enable); +extern int get_signals(void); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/skas_ptrace.h b/arch/um/include/skas_ptrace.h new file mode 100644 index 00000000000..cfb5fb4f5b9 --- /dev/null +++ b/arch/um/include/skas_ptrace.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SKAS_PTRACE_H +#define __SKAS_PTRACE_H + +struct ptrace_faultinfo { + int is_write; + unsigned long addr; +}; + +struct ptrace_ldt { + int func; + void *ptr; + unsigned long bytecount; +}; + +#define PTRACE_FAULTINFO 52 +#define PTRACE_SIGPENDING 53 +#define PTRACE_LDT 54 +#define PTRACE_SWITCH_MM 55 + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/syscall_user.h b/arch/um/include/syscall_user.h new file mode 100644 index 00000000000..811d0ec2445 --- /dev/null +++ b/arch/um/include/syscall_user.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SYSCALL_USER_H +#define __SYSCALL_USER_H + +extern int record_syscall_start(int syscall); +extern void record_syscall_end(int index, long result); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/sysdep-i386/checksum.h b/arch/um/include/sysdep-i386/checksum.h new file mode 100644 index 00000000000..3a2a45811aa --- /dev/null +++ b/arch/um/include/sysdep-i386/checksum.h @@ -0,0 +1,219 @@ +/* + * Licensed under the GPL + */ + +#ifndef __UM_SYSDEP_CHECKSUM_H +#define __UM_SYSDEP_CHECKSUM_H + +#include "linux/in6.h" +#include "linux/string.h" + +/* + * computes the checksum of a memory block at buff, length len, + * and adds in "sum" (32-bit) + * + * returns a 32-bit number suitable for feeding into itself + * or csum_tcpudp_magic + * + * this function must be called with even lengths, except + * for the last fragment, which may be odd + * + * it's best to have buff aligned on a 32-bit boundary + */ +unsigned int csum_partial(const unsigned char * buff, int len, + unsigned int sum); + +/* + * the same as csum_partial, but copies from src while it + * checksums, and handles user-space pointer exceptions correctly, when needed. + * + * here even more important to align src and dst on a 32-bit (or even + * better 64-bit) boundary + */ + +unsigned int csum_partial_copy_to(const unsigned char *src, unsigned char *dst, + int len, int sum, int *err_ptr); +unsigned int csum_partial_copy_from(const unsigned char *src, unsigned char *dst, + int len, int sum, int *err_ptr); + +/* + * Note: when you get a NULL pointer exception here this means someone + * passed in an incorrect kernel address to one of these functions. + * + * If you use these functions directly please don't forget the + * access_ok(). + */ + +static __inline__ +unsigned int csum_partial_copy_nocheck(const unsigned char *src, unsigned char *dst, + int len, int sum) +{ + memcpy(dst, src, len); + return(csum_partial(dst, len, sum)); +} + +static __inline__ +unsigned int csum_partial_copy_from_user(const unsigned char *src, unsigned char *dst, + int len, int sum, int *err_ptr) +{ + return csum_partial_copy_from(src, dst, len, sum, err_ptr); +} + +/* + * These are the old (and unsafe) way of doing checksums, a warning message + * will be printed if they are used and an exception occurs. + * + * these functions should go away after some time. + */ + +#define csum_partial_copy_fromuser csum_partial_copy_from_user +unsigned int csum_partial_copy(const unsigned char *src, unsigned char *dst, int len, int sum); + +/* + * This is a version of ip_compute_csum() optimized for IP headers, + * which always checksum on 4 octet boundaries. + * + * By Jorge Cwik <jorge@laser.satlink.net>, adapted for linux by + * Arnt Gulbrandsen. + */ +static inline unsigned short ip_fast_csum(unsigned char * iph, + unsigned int ihl) +{ + unsigned int sum; + + __asm__ __volatile__( + "movl (%1), %0 ;\n" + "subl $4, %2 ;\n" + "jbe 2f ;\n" + "addl 4(%1), %0 ;\n" + "adcl 8(%1), %0 ;\n" + "adcl 12(%1), %0 ;\n" +"1: adcl 16(%1), %0 ;\n" + "lea 4(%1), %1 ;\n" + "decl %2 ;\n" + "jne 1b ;\n" + "adcl $0, %0 ;\n" + "movl %0, %2 ;\n" + "shrl $16, %0 ;\n" + "addw %w2, %w0 ;\n" + "adcl $0, %0 ;\n" + "notl %0 ;\n" +"2: ;\n" + /* Since the input registers which are loaded with iph and ipl + are modified, we must also specify them as outputs, or gcc + will assume they contain their original values. */ + : "=r" (sum), "=r" (iph), "=r" (ihl) + : "1" (iph), "2" (ihl) + : "memory"); + return(sum); +} + +/* + * Fold a partial checksum + */ + +static inline unsigned int csum_fold(unsigned int sum) +{ + __asm__( + "addl %1, %0 ;\n" + "adcl $0xffff, %0 ;\n" + : "=r" (sum) + : "r" (sum << 16), "0" (sum & 0xffff0000) + ); + return (~sum) >> 16; +} + +static inline unsigned long csum_tcpudp_nofold(unsigned long saddr, + unsigned long daddr, + unsigned short len, + unsigned short proto, + unsigned int sum) +{ + __asm__( + "addl %1, %0 ;\n" + "adcl %2, %0 ;\n" + "adcl %3, %0 ;\n" + "adcl $0, %0 ;\n" + : "=r" (sum) + : "g" (daddr), "g"(saddr), "g"((ntohs(len)<<16)+proto*256), "0"(sum)); + return sum; +} + +/* + * computes the checksum of the TCP/UDP pseudo-header + * returns a 16-bit checksum, already complemented + */ +static inline unsigned short int csum_tcpudp_magic(unsigned long saddr, + unsigned long daddr, + unsigned short len, + unsigned short proto, + unsigned int sum) +{ + return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum)); +} + +/* + * this routine is used for miscellaneous IP-like checksums, mainly + * in icmp.c + */ + +static inline unsigned short ip_compute_csum(unsigned char * buff, int len) +{ + return csum_fold (csum_partial(buff, len, 0)); +} + +#define _HAVE_ARCH_IPV6_CSUM +static __inline__ unsigned short int csum_ipv6_magic(struct in6_addr *saddr, + struct in6_addr *daddr, + __u32 len, + unsigned short proto, + unsigned int sum) +{ + __asm__( + "addl 0(%1), %0 ;\n" + "adcl 4(%1), %0 ;\n" + "adcl 8(%1), %0 ;\n" + "adcl 12(%1), %0 ;\n" + "adcl 0(%2), %0 ;\n" + "adcl 4(%2), %0 ;\n" + "adcl 8(%2), %0 ;\n" + "adcl 12(%2), %0 ;\n" + "adcl %3, %0 ;\n" + "adcl %4, %0 ;\n" + "adcl $0, %0 ;\n" + : "=&r" (sum) + : "r" (saddr), "r" (daddr), + "r"(htonl(len)), "r"(htonl(proto)), "0"(sum)); + + return csum_fold(sum); +} + +/* + * Copy and checksum to user + */ +#define HAVE_CSUM_COPY_USER +static __inline__ unsigned int csum_and_copy_to_user(const unsigned char *src, + unsigned char *dst, + int len, int sum, int *err_ptr) +{ + if (access_ok(VERIFY_WRITE, dst, len)) + return(csum_partial_copy_to(src, dst, len, sum, err_ptr)); + + if (len) + *err_ptr = -EFAULT; + + return -1; /* invalid checksum */ +} + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/sysdep-i386/ptrace.h b/arch/um/include/sysdep-i386/ptrace.h new file mode 100644 index 00000000000..661d495e204 --- /dev/null +++ b/arch/um/include/sysdep-i386/ptrace.h @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SYSDEP_I386_PTRACE_H +#define __SYSDEP_I386_PTRACE_H + +#include "uml-config.h" +#include "user_constants.h" + +#define MAX_REG_NR (UM_FRAME_SIZE / sizeof(unsigned long)) +#define MAX_REG_OFFSET (UM_FRAME_SIZE) + +extern void update_debugregs(int seq); + +/* syscall emulation path in ptrace */ + +#ifndef PTRACE_SYSEMU +#define PTRACE_SYSEMU 31 +#endif + +void set_using_sysemu(int value); +int get_using_sysemu(void); +extern int sysemu_supported; + +#ifdef UML_CONFIG_MODE_TT +#include "sysdep/sc.h" +#endif + +#ifdef UML_CONFIG_MODE_SKAS + +#include "skas_ptregs.h" + +#define REGS_IP(r) ((r)[HOST_IP]) +#define REGS_SP(r) ((r)[HOST_SP]) +#define REGS_EFLAGS(r) ((r)[HOST_EFLAGS]) +#define REGS_EAX(r) ((r)[HOST_EAX]) +#define REGS_EBX(r) ((r)[HOST_EBX]) +#define REGS_ECX(r) ((r)[HOST_ECX]) +#define REGS_EDX(r) ((r)[HOST_EDX]) +#define REGS_ESI(r) ((r)[HOST_ESI]) +#define REGS_EDI(r) ((r)[HOST_EDI]) +#define REGS_EBP(r) ((r)[HOST_EBP]) +#define REGS_CS(r) ((r)[HOST_CS]) +#define REGS_SS(r) ((r)[HOST_SS]) +#define REGS_DS(r) ((r)[HOST_DS]) +#define REGS_ES(r) ((r)[HOST_ES]) +#define REGS_FS(r) ((r)[HOST_FS]) +#define REGS_GS(r) ((r)[HOST_GS]) + +#define REGS_SET_SYSCALL_RETURN(r, res) REGS_EAX(r) = (res) + +#define REGS_RESTART_SYSCALL(r) IP_RESTART_SYSCALL(REGS_IP(r)) + +#define REGS_SEGV_IS_FIXABLE(r) SEGV_IS_FIXABLE((r)->trap_type) + +#define REGS_FAULT_ADDR(r) ((r)->fault_addr) + +#define REGS_FAULT_WRITE(r) FAULT_WRITE((r)->fault_type) + +#endif +#ifndef PTRACE_SYSEMU_SINGLESTEP +#define PTRACE_SYSEMU_SINGLESTEP 32 +#endif + +#include "choose-mode.h" + +union uml_pt_regs { +#ifdef UML_CONFIG_MODE_TT + struct tt_regs { + long syscall; + void *sc; + } tt; +#endif +#ifdef UML_CONFIG_MODE_SKAS + struct skas_regs { + unsigned long regs[HOST_FRAME_SIZE]; + unsigned long fp[HOST_FP_SIZE]; + unsigned long xfp[HOST_XFP_SIZE]; + unsigned long fault_addr; + unsigned long fault_type; + unsigned long trap_type; + long syscall; + int is_user; + } skas; +#endif +}; + +#define EMPTY_UML_PT_REGS { } + +extern int mode_tt; + +#define UPT_SC(r) ((r)->tt.sc) +#define UPT_IP(r) \ + __CHOOSE_MODE(SC_IP(UPT_SC(r)), REGS_IP((r)->skas.regs)) +#define UPT_SP(r) \ + __CHOOSE_MODE(SC_SP(UPT_SC(r)), REGS_SP((r)->skas.regs)) +#define UPT_EFLAGS(r) \ + __CHOOSE_MODE(SC_EFLAGS(UPT_SC(r)), REGS_EFLAGS((r)->skas.regs)) +#define UPT_EAX(r) \ + __CHOOSE_MODE(SC_EAX(UPT_SC(r)), REGS_EAX((r)->skas.regs)) +#define UPT_EBX(r) \ + __CHOOSE_MODE(SC_EBX(UPT_SC(r)), REGS_EBX((r)->skas.regs)) +#define UPT_ECX(r) \ + __CHOOSE_MODE(SC_ECX(UPT_SC(r)), REGS_ECX((r)->skas.regs)) +#define UPT_EDX(r) \ + __CHOOSE_MODE(SC_EDX(UPT_SC(r)), REGS_EDX((r)->skas.regs)) +#define UPT_ESI(r) \ + __CHOOSE_MODE(SC_ESI(UPT_SC(r)), REGS_ESI((r)->skas.regs)) +#define UPT_EDI(r) \ + __CHOOSE_MODE(SC_EDI(UPT_SC(r)), REGS_EDI((r)->skas.regs)) +#define UPT_EBP(r) \ + __CHOOSE_MODE(SC_EBP(UPT_SC(r)), REGS_EBP((r)->skas.regs)) +#define UPT_ORIG_EAX(r) \ + __CHOOSE_MODE((r)->tt.syscall, (r)->skas.syscall) +#define UPT_CS(r) \ + __CHOOSE_MODE(SC_CS(UPT_SC(r)), REGS_CS((r)->skas.regs)) +#define UPT_SS(r) \ + __CHOOSE_MODE(SC_SS(UPT_SC(r)), REGS_SS((r)->skas.regs)) +#define UPT_DS(r) \ + __CHOOSE_MODE(SC_DS(UPT_SC(r)), REGS_DS((r)->skas.regs)) +#define UPT_ES(r) \ + __CHOOSE_MODE(SC_ES(UPT_SC(r)), REGS_ES((r)->skas.regs)) +#define UPT_FS(r) \ + __CHOOSE_MODE(SC_FS(UPT_SC(r)), REGS_FS((r)->skas.regs)) +#define UPT_GS(r) \ + __CHOOSE_MODE(SC_GS(UPT_SC(r)), REGS_GS((r)->skas.regs)) + +#define UPT_SYSCALL_ARG1(r) UPT_EBX(r) +#define UPT_SYSCALL_ARG2(r) UPT_ECX(r) +#define UPT_SYSCALL_ARG3(r) UPT_EDX(r) +#define UPT_SYSCALL_ARG4(r) UPT_ESI(r) +#define UPT_SYSCALL_ARG5(r) UPT_EDI(r) +#define UPT_SYSCALL_ARG6(r) UPT_EBP(r) + +extern int user_context(unsigned long sp); + +#define UPT_IS_USER(r) \ + CHOOSE_MODE(user_context(UPT_SP(r)), (r)->skas.is_user) + +struct syscall_args { + unsigned long args[6]; +}; + +#define SYSCALL_ARGS(r) ((struct syscall_args) \ + { .args = { UPT_SYSCALL_ARG1(r), \ + UPT_SYSCALL_ARG2(r), \ + UPT_SYSCALL_ARG3(r), \ + UPT_SYSCALL_ARG4(r), \ + UPT_SYSCALL_ARG5(r), \ + UPT_SYSCALL_ARG6(r) } } ) + +#define UPT_REG(regs, reg) \ + ({ unsigned long val; \ + switch(reg){ \ + case EIP: val = UPT_IP(regs); break; \ + case UESP: val = UPT_SP(regs); break; \ + case EAX: val = UPT_EAX(regs); break; \ + case EBX: val = UPT_EBX(regs); break; \ + case ECX: val = UPT_ECX(regs); break; \ + case EDX: val = UPT_EDX(regs); break; \ + case ESI: val = UPT_ESI(regs); break; \ + case EDI: val = UPT_EDI(regs); break; \ + case EBP: val = UPT_EBP(regs); break; \ + case ORIG_EAX: val = UPT_ORIG_EAX(regs); break; \ + case CS: val = UPT_CS(regs); break; \ + case SS: val = UPT_SS(regs); break; \ + case DS: val = UPT_DS(regs); break; \ + case ES: val = UPT_ES(regs); break; \ + case FS: val = UPT_FS(regs); break; \ + case GS: val = UPT_GS(regs); break; \ + case EFL: val = UPT_EFLAGS(regs); break; \ + default : \ + panic("Bad register in UPT_REG : %d\n", reg); \ + val = -1; \ + } \ + val; \ + }) + + +#define UPT_SET(regs, reg, val) \ + do { \ + switch(reg){ \ + case EIP: UPT_IP(regs) = val; break; \ + case UESP: UPT_SP(regs) = val; break; \ + case EAX: UPT_EAX(regs) = val; break; \ + case EBX: UPT_EBX(regs) = val; break; \ + case ECX: UPT_ECX(regs) = val; break; \ + case EDX: UPT_EDX(regs) = val; break; \ + case ESI: UPT_ESI(regs) = val; break; \ + case EDI: UPT_EDI(regs) = val; break; \ + case EBP: UPT_EBP(regs) = val; break; \ + case ORIG_EAX: UPT_ORIG_EAX(regs) = val; break; \ + case CS: UPT_CS(regs) = val; break; \ + case SS: UPT_SS(regs) = val; break; \ + case DS: UPT_DS(regs) = val; break; \ + case ES: UPT_ES(regs) = val; break; \ + case FS: UPT_FS(regs) = val; break; \ + case GS: UPT_GS(regs) = val; break; \ + case EFL: UPT_EFLAGS(regs) = val; break; \ + default : \ + panic("Bad register in UPT_SET : %d\n", reg); \ + break; \ + } \ + } while (0) + +#define UPT_SET_SYSCALL_RETURN(r, res) \ + CHOOSE_MODE(SC_SET_SYSCALL_RETURN(UPT_SC(r), (res)), \ + REGS_SET_SYSCALL_RETURN((r)->skas.regs, (res))) + +#define UPT_RESTART_SYSCALL(r) \ + CHOOSE_MODE(SC_RESTART_SYSCALL(UPT_SC(r)), \ + REGS_RESTART_SYSCALL((r)->skas.regs)) + +#define UPT_ORIG_SYSCALL(r) UPT_EAX(r) +#define UPT_SYSCALL_NR(r) UPT_ORIG_EAX(r) +#define UPT_SYSCALL_RET(r) UPT_EAX(r) + +#define UPT_SEGV_IS_FIXABLE(r) \ + CHOOSE_MODE(SC_SEGV_IS_FIXABLE(UPT_SC(r)), \ + REGS_SEGV_IS_FIXABLE(&r->skas)) + +#define UPT_FAULT_ADDR(r) \ + __CHOOSE_MODE(SC_FAULT_ADDR(UPT_SC(r)), REGS_FAULT_ADDR(&r->skas)) + +#define UPT_FAULT_WRITE(r) \ + CHOOSE_MODE(SC_FAULT_WRITE(UPT_SC(r)), REGS_FAULT_WRITE(&r->skas)) + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/sysdep-i386/ptrace_user.h b/arch/um/include/sysdep-i386/ptrace_user.h new file mode 100644 index 00000000000..eca8066e7a4 --- /dev/null +++ b/arch/um/include/sysdep-i386/ptrace_user.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SYSDEP_I386_PTRACE_USER_H__ +#define __SYSDEP_I386_PTRACE_USER_H__ + +#include <sys/ptrace.h> +#include <linux/ptrace.h> +#include <asm/ptrace.h> + +#define PT_OFFSET(r) ((r) * sizeof(long)) + +#define PT_SYSCALL_NR(regs) ((regs)[ORIG_EAX]) +#define PT_SYSCALL_NR_OFFSET PT_OFFSET(ORIG_EAX) + +#define PT_SYSCALL_ARG1_OFFSET PT_OFFSET(EBX) +#define PT_SYSCALL_ARG2_OFFSET PT_OFFSET(ECX) +#define PT_SYSCALL_ARG3_OFFSET PT_OFFSET(EDX) +#define PT_SYSCALL_ARG4_OFFSET PT_OFFSET(ESI) +#define PT_SYSCALL_ARG5_OFFSET PT_OFFSET(EDI) + +#define PT_SYSCALL_RET_OFFSET PT_OFFSET(EAX) + +#define PT_IP_OFFSET PT_OFFSET(EIP) +#define PT_IP(regs) ((regs)[EIP]) +#define PT_SP(regs) ((regs)[UESP]) + +#ifndef FRAME_SIZE +#define FRAME_SIZE (17) +#endif +#define FRAME_SIZE_OFFSET (FRAME_SIZE * sizeof(unsigned long)) + +#define FP_FRAME_SIZE (27) +#define FPX_FRAME_SIZE (128) + +#ifdef PTRACE_GETREGS +#define UM_HAVE_GETREGS +#endif + +#ifdef PTRACE_SETREGS +#define UM_HAVE_SETREGS +#endif + +#ifdef PTRACE_GETFPREGS +#define UM_HAVE_GETFPREGS +#endif + +#ifdef PTRACE_SETFPREGS +#define UM_HAVE_SETFPREGS +#endif + +#ifdef PTRACE_GETFPXREGS +#define UM_HAVE_GETFPXREGS +#endif + +#ifdef PTRACE_SETFPXREGS +#define UM_HAVE_SETFPXREGS +#endif + +#endif diff --git a/arch/um/include/sysdep-i386/sigcontext.h b/arch/um/include/sysdep-i386/sigcontext.h new file mode 100644 index 00000000000..dfee589de36 --- /dev/null +++ b/arch/um/include/sysdep-i386/sigcontext.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SYS_SIGCONTEXT_I386_H +#define __SYS_SIGCONTEXT_I386_H + +#include <sysdep/sc.h> + +#define IP_RESTART_SYSCALL(ip) ((ip) -= 2) + +#define SC_RESTART_SYSCALL(sc) IP_RESTART_SYSCALL(SC_IP(sc)) +#define SC_SET_SYSCALL_RETURN(sc, result) SC_EAX(sc) = (result) + +#define SC_FAULT_ADDR(sc) SC_CR2(sc) +#define SC_FAULT_TYPE(sc) SC_ERR(sc) + +#define FAULT_WRITE(err) (err & 2) +#define TO_SC_ERR(is_write) ((is_write) ? 2 : 0) + +#define SC_FAULT_WRITE(sc) (FAULT_WRITE(SC_ERR(sc))) + +#define SC_TRAP_TYPE(sc) SC_TRAPNO(sc) + +/* ptrace expects that, at the start of a system call, %eax contains + * -ENOSYS, so this makes it so. + */ +#define SC_START_SYSCALL(sc) do SC_EAX(sc) = -ENOSYS; while(0) + +/* This is Page Fault */ +#define SEGV_IS_FIXABLE(trap) (trap == 14) + +#define SC_SEGV_IS_FIXABLE(sc) (SEGV_IS_FIXABLE(SC_TRAPNO(sc))) + +extern unsigned long *sc_sigmask(void *sc_ptr); +extern int sc_get_fpregs(unsigned long buf, void *sc_ptr); + +#endif +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/sysdep-i386/signal.h b/arch/um/include/sysdep-i386/signal.h new file mode 100644 index 00000000000..b1e1f7a7749 --- /dev/null +++ b/arch/um/include/sysdep-i386/signal.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2004 PathScale, Inc + * Licensed under the GPL + */ + +#ifndef __I386_SIGNAL_H_ +#define __I386_SIGNAL_H_ + +#include <signal.h> + +#define ARCH_GET_SIGCONTEXT(sc, sig) \ + do sc = (struct sigcontext *) (&sig + 1); while(0) + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/sysdep-i386/syscalls.h b/arch/um/include/sysdep-i386/syscalls.h new file mode 100644 index 00000000000..5db81ec9087 --- /dev/null +++ b/arch/um/include/sysdep-i386/syscalls.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "asm/unistd.h" +#include "sysdep/ptrace.h" + +typedef long syscall_handler_t(struct pt_regs); + +/* Not declared on x86, incompatible declarations on x86_64, so these have + * to go here rather than in sys_call_table.c + */ +extern syscall_handler_t sys_ptrace; +extern syscall_handler_t sys_rt_sigaction; + +extern syscall_handler_t old_mmap_i386; + +#define EXECUTE_SYSCALL(syscall, regs) \ + ((long (*)(struct syscall_args)) (*sys_call_table[syscall]))(SYSCALL_ARGS(®s->regs)) + +extern long sys_mmap2(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long pgoff); + +/* On i386 they choose a meaningless naming.*/ +#define __NR_kexec_load __NR_sys_kexec_load + +#define ARCH_SYSCALLS \ + [ __NR_waitpid ] = (syscall_handler_t *) sys_waitpid, \ + [ __NR_break ] = (syscall_handler_t *) sys_ni_syscall, \ + [ __NR_oldstat ] = (syscall_handler_t *) sys_stat, \ + [ __NR_umount ] = (syscall_handler_t *) sys_oldumount, \ + [ __NR_stime ] = um_stime, \ + [ __NR_oldfstat ] = (syscall_handler_t *) sys_fstat, \ + [ __NR_stty ] = (syscall_handler_t *) sys_ni_syscall, \ + [ __NR_gtty ] = (syscall_handler_t *) sys_ni_syscall, \ + [ __NR_nice ] = (syscall_handler_t *) sys_nice, \ + [ __NR_ftime ] = (syscall_handler_t *) sys_ni_syscall, \ + [ __NR_prof ] = (syscall_handler_t *) sys_ni_syscall, \ + [ __NR_signal ] = (syscall_handler_t *) sys_signal, \ + [ __NR_lock ] = (syscall_handler_t *) sys_ni_syscall, \ + [ __NR_mpx ] = (syscall_handler_t *) sys_ni_syscall, \ + [ __NR_ulimit ] = (syscall_handler_t *) sys_ni_syscall, \ + [ __NR_oldolduname ] = (syscall_handler_t *) sys_olduname, \ + [ __NR_sigaction ] = (syscall_handler_t *) sys_sigaction, \ + [ __NR_sgetmask ] = (syscall_handler_t *) sys_sgetmask, \ + [ __NR_ssetmask ] = (syscall_handler_t *) sys_ssetmask, \ + [ __NR_sigsuspend ] = (syscall_handler_t *) sys_sigsuspend, \ + [ __NR_sigpending ] = (syscall_handler_t *) sys_sigpending, \ + [ __NR_oldlstat ] = (syscall_handler_t *) sys_lstat, \ + [ __NR_readdir ] = old_readdir, \ + [ __NR_profil ] = (syscall_handler_t *) sys_ni_syscall, \ + [ __NR_socketcall ] = (syscall_handler_t *) sys_socketcall, \ + [ __NR_olduname ] = (syscall_handler_t *) sys_uname, \ + [ __NR_iopl ] = (syscall_handler_t *) sys_ni_syscall, \ + [ __NR_idle ] = (syscall_handler_t *) sys_ni_syscall, \ + [ __NR_ipc ] = (syscall_handler_t *) sys_ipc, \ + [ __NR_sigreturn ] = (syscall_handler_t *) sys_sigreturn, \ + [ __NR_sigprocmask ] = (syscall_handler_t *) sys_sigprocmask, \ + [ __NR_bdflush ] = (syscall_handler_t *) sys_bdflush, \ + [ __NR__llseek ] = (syscall_handler_t *) sys_llseek, \ + [ __NR__newselect ] = (syscall_handler_t *) sys_select, \ + [ __NR_vm86 ] = (syscall_handler_t *) sys_ni_syscall, \ + [ __NR_mmap ] = (syscall_handler_t *) old_mmap_i386, \ + [ __NR_ugetrlimit ] = (syscall_handler_t *) sys_getrlimit, \ + [ __NR_mmap2 ] = (syscall_handler_t *) sys_mmap2, \ + [ __NR_truncate64 ] = (syscall_handler_t *) sys_truncate64, \ + [ __NR_ftruncate64 ] = (syscall_handler_t *) sys_ftruncate64, \ + [ __NR_stat64 ] = (syscall_handler_t *) sys_stat64, \ + [ __NR_lstat64 ] = (syscall_handler_t *) sys_lstat64, \ + [ __NR_fstat64 ] = (syscall_handler_t *) sys_fstat64, \ + [ __NR_fcntl64 ] = (syscall_handler_t *) sys_fcntl64, \ + [ __NR_sendfile64 ] = (syscall_handler_t *) sys_sendfile64, \ + [ __NR_statfs64 ] = (syscall_handler_t *) sys_statfs64, \ + [ __NR_fstatfs64 ] = (syscall_handler_t *) sys_fstatfs64, \ + [ __NR_fadvise64_64 ] = (syscall_handler_t *) sys_fadvise64_64, \ + [ __NR_select ] = (syscall_handler_t *) old_select, \ + [ __NR_vm86old ] = (syscall_handler_t *) sys_ni_syscall, \ + [ __NR_modify_ldt ] = (syscall_handler_t *) sys_modify_ldt, \ + [ __NR_lchown32 ] = (syscall_handler_t *) sys_lchown, \ + [ __NR_getuid32 ] = (syscall_handler_t *) sys_getuid, \ + [ __NR_getgid32 ] = (syscall_handler_t *) sys_getgid, \ + [ __NR_geteuid32 ] = (syscall_handler_t *) sys_geteuid, \ + [ __NR_getegid32 ] = (syscall_handler_t *) sys_getegid, \ + [ __NR_setreuid32 ] = (syscall_handler_t *) sys_setreuid, \ + [ __NR_setregid32 ] = (syscall_handler_t *) sys_setregid, \ + [ __NR_getgroups32 ] = (syscall_handler_t *) sys_getgroups, \ + [ __NR_setgroups32 ] = (syscall_handler_t *) sys_setgroups, \ + [ __NR_fchown32 ] = (syscall_handler_t *) sys_fchown, \ + [ __NR_setresuid32 ] = (syscall_handler_t *) sys_setresuid, \ + [ __NR_getresuid32 ] = (syscall_handler_t *) sys_getresuid, \ + [ __NR_setresgid32 ] = (syscall_handler_t *) sys_setresgid, \ + [ __NR_getresgid32 ] = (syscall_handler_t *) sys_getresgid, \ + [ __NR_chown32 ] = (syscall_handler_t *) sys_chown, \ + [ __NR_setuid32 ] = (syscall_handler_t *) sys_setuid, \ + [ __NR_setgid32 ] = (syscall_handler_t *) sys_setgid, \ + [ __NR_setfsuid32 ] = (syscall_handler_t *) sys_setfsuid, \ + [ __NR_setfsgid32 ] = (syscall_handler_t *) sys_setfsgid, \ + [ __NR_pivot_root ] = (syscall_handler_t *) sys_pivot_root, \ + [ __NR_mincore ] = (syscall_handler_t *) sys_mincore, \ + [ __NR_madvise ] = (syscall_handler_t *) sys_madvise, \ + [ 222 ] = (syscall_handler_t *) sys_ni_syscall, \ + [ 223 ] = (syscall_handler_t *) sys_ni_syscall, \ + [ __NR_set_thread_area ] = (syscall_handler_t *) sys_ni_syscall, \ + [ __NR_get_thread_area ] = (syscall_handler_t *) sys_ni_syscall, \ + [ 251 ] = (syscall_handler_t *) sys_ni_syscall, \ + [ 285 ] = (syscall_handler_t *) sys_ni_syscall, + +/* 222 doesn't yet have a name in include/asm-i386/unistd.h */ + +#define LAST_ARCH_SYSCALL 285 + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/sysdep-ia64/ptrace.h b/arch/um/include/sysdep-ia64/ptrace.h new file mode 100644 index 00000000000..42dd8fb6f2f --- /dev/null +++ b/arch/um/include/sysdep-ia64/ptrace.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SYSDEP_IA64_PTRACE_H +#define __SYSDEP_IA64_PTRACE_H + +struct sys_pt_regs { + int foo; +}; + +#define EMPTY_REGS { 0 } + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/sysdep-ia64/sigcontext.h b/arch/um/include/sysdep-ia64/sigcontext.h new file mode 100644 index 00000000000..f15fb25260b --- /dev/null +++ b/arch/um/include/sysdep-ia64/sigcontext.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SYSDEP_IA64_SIGCONTEXT_H +#define __SYSDEP_IA64_SIGCONTEXT_H + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/sysdep-ia64/syscalls.h b/arch/um/include/sysdep-ia64/syscalls.h new file mode 100644 index 00000000000..4a1f46ef1eb --- /dev/null +++ b/arch/um/include/sysdep-ia64/syscalls.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SYSDEP_IA64_SYSCALLS_H +#define __SYSDEP_IA64_SYSCALLS_H + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/sysdep-ppc/ptrace.h b/arch/um/include/sysdep-ppc/ptrace.h new file mode 100644 index 00000000000..8a27353733a --- /dev/null +++ b/arch/um/include/sysdep-ppc/ptrace.h @@ -0,0 +1,104 @@ +/* + * Licensed under the GPL + */ + +#ifndef __SYS_PTRACE_PPC_H +#define __SYS_PTRACE_PPC_H + +#include "linux/config.h" +#include "linux/types.h" + +/* the following taken from <asm-ppc/ptrace.h> */ + +#ifdef CONFIG_PPC64 +#define PPC_REG unsigned long /*long*/ +#else +#define PPC_REG unsigned long +#endif +struct sys_pt_regs_s { + PPC_REG gpr[32]; + PPC_REG nip; + PPC_REG msr; + PPC_REG orig_gpr3; /* Used for restarting system calls */ + PPC_REG ctr; + PPC_REG link; + PPC_REG xer; + PPC_REG ccr; + PPC_REG mq; /* 601 only (not used at present) */ + /* Used on APUS to hold IPL value. */ + PPC_REG trap; /* Reason for being here */ + PPC_REG dar; /* Fault registers */ + PPC_REG dsisr; + PPC_REG result; /* Result of a system call */ +}; + +#define NUM_REGS (sizeof(struct sys_pt_regs_s) / sizeof(PPC_REG)) + +struct sys_pt_regs { + PPC_REG regs[sizeof(struct sys_pt_regs_s) / sizeof(PPC_REG)]; +}; + +#define UM_MAX_REG (PT_FPR0) +#define UM_MAX_REG_OFFSET (UM_MAX_REG * sizeof(PPC_REG)) + +#define EMPTY_REGS { { [ 0 ... NUM_REGS - 1] = 0 } } + +#define UM_REG(r, n) ((r)->regs[n]) + +#define UM_SYSCALL_RET(r) UM_REG(r, PT_R3) +#define UM_SP(r) UM_REG(r, PT_R1) +#define UM_IP(r) UM_REG(r, PT_NIP) +#define UM_ELF_ZERO(r) UM_REG(r, PT_FPSCR) +#define UM_SYSCALL_NR(r) UM_REG(r, PT_R0) +#define UM_SYSCALL_ARG1(r) UM_REG(r, PT_ORIG_R3) +#define UM_SYSCALL_ARG2(r) UM_REG(r, PT_R4) +#define UM_SYSCALL_ARG3(r) UM_REG(r, PT_R5) +#define UM_SYSCALL_ARG4(r) UM_REG(r, PT_R6) +#define UM_SYSCALL_ARG5(r) UM_REG(r, PT_R7) +#define UM_SYSCALL_ARG6(r) UM_REG(r, PT_R8) + +#define UM_SYSCALL_NR_OFFSET (PT_R0 * sizeof(PPC_REG)) +#define UM_SYSCALL_RET_OFFSET (PT_R3 * sizeof(PPC_REG)) +#define UM_SYSCALL_ARG1_OFFSET (PT_R3 * sizeof(PPC_REG)) +#define UM_SYSCALL_ARG2_OFFSET (PT_R4 * sizeof(PPC_REG)) +#define UM_SYSCALL_ARG3_OFFSET (PT_R5 * sizeof(PPC_REG)) +#define UM_SYSCALL_ARG4_OFFSET (PT_R6 * sizeof(PPC_REG)) +#define UM_SYSCALL_ARG5_OFFSET (PT_R7 * sizeof(PPC_REG)) +#define UM_SYSCALL_ARG6_OFFSET (PT_R8 * sizeof(PPC_REG)) +#define UM_SP_OFFSET (PT_R1 * sizeof(PPC_REG)) +#define UM_IP_OFFSET (PT_NIP * sizeof(PPC_REG)) +#define UM_ELF_ZERO_OFFSET (PT_R3 * sizeof(PPC_REG)) + +#define UM_SET_SYSCALL_RETURN(_regs, result) \ +do { \ + if (result < 0) { \ + (_regs)->regs[PT_CCR] |= 0x10000000; \ + UM_SYSCALL_RET((_regs)) = -result; \ + } else { \ + UM_SYSCALL_RET((_regs)) = result; \ + } \ +} while(0) + +extern void shove_aux_table(unsigned long sp); +#define UM_FIX_EXEC_STACK(sp) shove_aux_table(sp); + +/* These aren't actually defined. The undefs are just to make sure + * everyone's clear on the concept. + */ +#undef UML_HAVE_GETREGS +#undef UML_HAVE_GETFPREGS +#undef UML_HAVE_SETREGS +#undef UML_HAVE_SETFPREGS + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/sysdep-ppc/sigcontext.h b/arch/um/include/sysdep-ppc/sigcontext.h new file mode 100644 index 00000000000..f20d965de9c --- /dev/null +++ b/arch/um/include/sysdep-ppc/sigcontext.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __SYS_SIGCONTEXT_PPC_H +#define __SYS_SIGCONTEXT_PPC_H + +#define DSISR_WRITE 0x02000000 + +#define SC_FAULT_ADDR(sc) ({ \ + struct sigcontext *_sc = (sc); \ + long retval = -1; \ + switch (_sc->regs->trap) { \ + case 0x300: \ + /* data exception */ \ + retval = _sc->regs->dar; \ + break; \ + case 0x400: \ + /* instruction exception */ \ + retval = _sc->regs->nip; \ + break; \ + default: \ + panic("SC_FAULT_ADDR: unhandled trap type\n"); \ + } \ + retval; \ + }) + +#define SC_FAULT_WRITE(sc) ({ \ + struct sigcontext *_sc = (sc); \ + long retval = -1; \ + switch (_sc->regs->trap) { \ + case 0x300: \ + /* data exception */ \ + retval = !!(_sc->regs->dsisr & DSISR_WRITE); \ + break; \ + case 0x400: \ + /* instruction exception: not a write */ \ + retval = 0; \ + break; \ + default: \ + panic("SC_FAULT_ADDR: unhandled trap type\n"); \ + } \ + retval; \ + }) + +#define SC_IP(sc) ((sc)->regs->nip) +#define SC_SP(sc) ((sc)->regs->gpr[1]) +#define SEGV_IS_FIXABLE(sc) (1) + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/sysdep-ppc/syscalls.h b/arch/um/include/sysdep-ppc/syscalls.h new file mode 100644 index 00000000000..679df351e19 --- /dev/null +++ b/arch/um/include/sysdep-ppc/syscalls.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +typedef long syscall_handler_t(unsigned long arg1, unsigned long arg2, + unsigned long arg3, unsigned long arg4, + unsigned long arg5, unsigned long arg6); + +#define EXECUTE_SYSCALL(syscall, regs) \ + (*sys_call_table[syscall])(UM_SYSCALL_ARG1(®s), \ + UM_SYSCALL_ARG2(®s), \ + UM_SYSCALL_ARG3(®s), \ + UM_SYSCALL_ARG4(®s), \ + UM_SYSCALL_ARG5(®s), \ + UM_SYSCALL_ARG6(®s)) + +extern syscall_handler_t sys_mincore; +extern syscall_handler_t sys_madvise; + +/* old_mmap needs the correct prototype since syscall_kern.c includes + * this file. + */ +int old_mmap(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long offset); + +#define ARCH_SYSCALLS \ + [ __NR_modify_ldt ] = sys_ni_syscall, \ + [ __NR_pciconfig_read ] = sys_ni_syscall, \ + [ __NR_pciconfig_write ] = sys_ni_syscall, \ + [ __NR_pciconfig_iobase ] = sys_ni_syscall, \ + [ __NR_pivot_root ] = sys_ni_syscall, \ + [ __NR_multiplexer ] = sys_ni_syscall, \ + [ __NR_mmap ] = old_mmap, \ + [ __NR_madvise ] = sys_madvise, \ + [ __NR_mincore ] = sys_mincore, \ + [ __NR_iopl ] = (syscall_handler_t *) sys_ni_syscall, \ + [ __NR_utimes ] = (syscall_handler_t *) sys_utimes, \ + [ __NR_fadvise64 ] = (syscall_handler_t *) sys_fadvise64, + +#define LAST_ARCH_SYSCALL __NR_fadvise64 + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/sysdep-x86_64/checksum.h b/arch/um/include/sysdep-x86_64/checksum.h new file mode 100644 index 00000000000..572c6c19be3 --- /dev/null +++ b/arch/um/include/sysdep-x86_64/checksum.h @@ -0,0 +1,151 @@ +/* + * Licensed under the GPL + */ + +#ifndef __UM_SYSDEP_CHECKSUM_H +#define __UM_SYSDEP_CHECKSUM_H + +#include "linux/string.h" +#include "linux/in6.h" +#include "asm/uaccess.h" + +extern unsigned int csum_partial_copy_from(const unsigned char *src, unsigned char *dst, int len, + int sum, int *err_ptr); +extern unsigned csum_partial(const unsigned char *buff, unsigned len, + unsigned sum); + +/* + * Note: when you get a NULL pointer exception here this means someone + * passed in an incorrect kernel address to one of these functions. + * + * If you use these functions directly please don't forget the + * access_ok(). + */ + +static __inline__ +unsigned int csum_partial_copy_nocheck(const unsigned char *src, unsigned char *dst, + int len, int sum) +{ + memcpy(dst, src, len); + return(csum_partial(dst, len, sum)); +} + +static __inline__ +unsigned int csum_partial_copy_from_user(const unsigned char *src, unsigned char *dst, + int len, int sum, int *err_ptr) +{ + return csum_partial_copy_from(src, dst, len, sum, err_ptr); +} + +/** + * csum_fold - Fold and invert a 32bit checksum. + * sum: 32bit unfolded sum + * + * Fold a 32bit running checksum to 16bit and invert it. This is usually + * the last step before putting a checksum into a packet. + * Make sure not to mix with 64bit checksums. + */ +static inline unsigned int csum_fold(unsigned int sum) +{ + __asm__( + " addl %1,%0\n" + " adcl $0xffff,%0" + : "=r" (sum) + : "r" (sum << 16), "0" (sum & 0xffff0000) + ); + return (~sum) >> 16; +} + +/** + * csum_tcpup_nofold - Compute an IPv4 pseudo header checksum. + * @saddr: source address + * @daddr: destination address + * @len: length of packet + * @proto: ip protocol of packet + * @sum: initial sum to be added in (32bit unfolded) + * + * Returns the pseudo header checksum the input data. Result is + * 32bit unfolded. + */ +static inline unsigned long +csum_tcpudp_nofold(unsigned saddr, unsigned daddr, unsigned short len, + unsigned short proto, unsigned int sum) +{ + asm(" addl %1, %0\n" + " adcl %2, %0\n" + " adcl %3, %0\n" + " adcl $0, %0\n" + : "=r" (sum) + : "g" (daddr), "g" (saddr), "g" ((ntohs(len)<<16)+proto*256), "0" (sum)); + return sum; +} + +/* + * computes the checksum of the TCP/UDP pseudo-header + * returns a 16-bit checksum, already complemented + */ +static inline unsigned short int csum_tcpudp_magic(unsigned long saddr, + unsigned long daddr, + unsigned short len, + unsigned short proto, + unsigned int sum) +{ + return csum_fold(csum_tcpudp_nofold(saddr,daddr,len,proto,sum)); +} + +/** + * ip_fast_csum - Compute the IPv4 header checksum efficiently. + * iph: ipv4 header + * ihl: length of header / 4 + */ +static inline unsigned short ip_fast_csum(unsigned char *iph, unsigned int ihl) +{ + unsigned int sum; + + asm( " movl (%1), %0\n" + " subl $4, %2\n" + " jbe 2f\n" + " addl 4(%1), %0\n" + " adcl 8(%1), %0\n" + " adcl 12(%1), %0\n" + "1: adcl 16(%1), %0\n" + " lea 4(%1), %1\n" + " decl %2\n" + " jne 1b\n" + " adcl $0, %0\n" + " movl %0, %2\n" + " shrl $16, %0\n" + " addw %w2, %w0\n" + " adcl $0, %0\n" + " notl %0\n" + "2:" + /* Since the input registers which are loaded with iph and ipl + are modified, we must also specify them as outputs, or gcc + will assume they contain their original values. */ + : "=r" (sum), "=r" (iph), "=r" (ihl) + : "1" (iph), "2" (ihl) + : "memory"); + return(sum); +} + +static inline unsigned add32_with_carry(unsigned a, unsigned b) +{ + asm("addl %2,%0\n\t" + "adcl $0,%0" + : "=r" (a) + : "0" (a), "r" (b)); + return a; +} + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/sysdep-x86_64/ptrace.h b/arch/um/include/sysdep-x86_64/ptrace.h new file mode 100644 index 00000000000..915c82daffb --- /dev/null +++ b/arch/um/include/sysdep-x86_64/ptrace.h @@ -0,0 +1,264 @@ +/* + * Copyright 2003 PathScale, Inc. + * + * Licensed under the GPL + */ + +#ifndef __SYSDEP_X86_64_PTRACE_H +#define __SYSDEP_X86_64_PTRACE_H + +#include "uml-config.h" +#include "user_constants.h" + +#define MAX_REG_OFFSET (UM_FRAME_SIZE) +#define MAX_REG_NR ((MAX_REG_OFFSET) / sizeof(unsigned long)) + +#ifdef UML_CONFIG_MODE_TT +#include "sysdep/sc.h" +#endif + +#ifdef UML_CONFIG_MODE_SKAS +#include "skas_ptregs.h" + +#define REGS_IP(r) ((r)[HOST_IP]) +#define REGS_SP(r) ((r)[HOST_SP]) + +#define REGS_RBX(r) ((r)[HOST_RBX]) +#define REGS_RCX(r) ((r)[HOST_RCX]) +#define REGS_RDX(r) ((r)[HOST_RDX]) +#define REGS_RSI(r) ((r)[HOST_RSI]) +#define REGS_RDI(r) ((r)[HOST_RDI]) +#define REGS_RBP(r) ((r)[HOST_RBP]) +#define REGS_RAX(r) ((r)[HOST_RAX]) +#define REGS_R8(r) ((r)[HOST_R8]) +#define REGS_R9(r) ((r)[HOST_R9]) +#define REGS_R10(r) ((r)[HOST_R10]) +#define REGS_R11(r) ((r)[HOST_R11]) +#define REGS_R12(r) ((r)[HOST_R12]) +#define REGS_R13(r) ((r)[HOST_R13]) +#define REGS_R14(r) ((r)[HOST_R14]) +#define REGS_R15(r) ((r)[HOST_R15]) +#define REGS_CS(r) ((r)[HOST_CS]) +#define REGS_EFLAGS(r) ((r)[HOST_EFLAGS]) +#define REGS_SS(r) ((r)[HOST_SS]) + +#define HOST_FS_BASE 21 +#define HOST_GS_BASE 22 +#define HOST_DS 23 +#define HOST_ES 24 +#define HOST_FS 25 +#define HOST_GS 26 + +#define REGS_FS_BASE(r) ((r)[HOST_FS_BASE]) +#define REGS_GS_BASE(r) ((r)[HOST_GS_BASE]) +#define REGS_DS(r) ((r)[HOST_DS]) +#define REGS_ES(r) ((r)[HOST_ES]) +#define REGS_FS(r) ((r)[HOST_FS]) +#define REGS_GS(r) ((r)[HOST_GS]) + +#define REGS_ORIG_RAX(r) ((r)[HOST_ORIG_RAX]) + +#define REGS_SET_SYSCALL_RETURN(r, res) REGS_RAX(r) = (res) + +#define REGS_RESTART_SYSCALL(r) IP_RESTART_SYSCALL(REGS_IP(r)) + +#define REGS_SEGV_IS_FIXABLE(r) SEGV_IS_FIXABLE((r)->trap_type) + +#define REGS_FAULT_ADDR(r) ((r)->fault_addr) + +#define REGS_FAULT_WRITE(r) FAULT_WRITE((r)->fault_type) + +#define REGS_TRAP(r) ((r)->trap_type) + +#define REGS_ERR(r) ((r)->fault_type) + +#endif + +#include "choose-mode.h" + +/* XXX */ +union uml_pt_regs { +#ifdef UML_CONFIG_MODE_TT + struct tt_regs { + long syscall; + unsigned long orig_rax; + void *sc; + } tt; +#endif +#ifdef UML_CONFIG_MODE_SKAS + struct skas_regs { + /* XXX */ + unsigned long regs[27]; + unsigned long fp[65]; + unsigned long fault_addr; + unsigned long fault_type; + unsigned long trap_type; + long syscall; + int is_user; + } skas; +#endif +}; + +#define EMPTY_UML_PT_REGS { } + +/* XXX */ +extern int mode_tt; + +#define UPT_RBX(r) __CHOOSE_MODE(SC_RBX(UPT_SC(r)), REGS_RBX((r)->skas.regs)) +#define UPT_RCX(r) __CHOOSE_MODE(SC_RCX(UPT_SC(r)), REGS_RCX((r)->skas.regs)) +#define UPT_RDX(r) __CHOOSE_MODE(SC_RDX(UPT_SC(r)), REGS_RDX((r)->skas.regs)) +#define UPT_RSI(r) __CHOOSE_MODE(SC_RSI(UPT_SC(r)), REGS_RSI((r)->skas.regs)) +#define UPT_RDI(r) __CHOOSE_MODE(SC_RDI(UPT_SC(r)), REGS_RDI((r)->skas.regs)) +#define UPT_RBP(r) __CHOOSE_MODE(SC_RBP(UPT_SC(r)), REGS_RBP((r)->skas.regs)) +#define UPT_RAX(r) __CHOOSE_MODE(SC_RAX(UPT_SC(r)), REGS_RAX((r)->skas.regs)) +#define UPT_R8(r) __CHOOSE_MODE(SC_R8(UPT_SC(r)), REGS_R8((r)->skas.regs)) +#define UPT_R9(r) __CHOOSE_MODE(SC_R9(UPT_SC(r)), REGS_R9((r)->skas.regs)) +#define UPT_R10(r) __CHOOSE_MODE(SC_R10(UPT_SC(r)), REGS_R10((r)->skas.regs)) +#define UPT_R11(r) __CHOOSE_MODE(SC_R11(UPT_SC(r)), REGS_R11((r)->skas.regs)) +#define UPT_R12(r) __CHOOSE_MODE(SC_R12(UPT_SC(r)), REGS_R12((r)->skas.regs)) +#define UPT_R13(r) __CHOOSE_MODE(SC_R13(UPT_SC(r)), REGS_R13((r)->skas.regs)) +#define UPT_R14(r) __CHOOSE_MODE(SC_R14(UPT_SC(r)), REGS_R14((r)->skas.regs)) +#define UPT_R15(r) __CHOOSE_MODE(SC_R15(UPT_SC(r)), REGS_R15((r)->skas.regs)) +#define UPT_CS(r) __CHOOSE_MODE(SC_CS(UPT_SC(r)), REGS_CS((r)->skas.regs)) +#define UPT_FS(r) __CHOOSE_MODE(SC_FS(UPT_SC(r)), REGS_FS((r)->skas.regs)) +#define UPT_GS(r) __CHOOSE_MODE(SC_GS(UPT_SC(r)), REGS_GS((r)->skas.regs)) +#define UPT_DS(r) __CHOOSE_MODE(SC_DS(UPT_SC(r)), REGS_DS((r)->skas.regs)) +#define UPT_ES(r) __CHOOSE_MODE(SC_ES(UPT_SC(r)), REGS_ES((r)->skas.regs)) +#define UPT_CS(r) __CHOOSE_MODE(SC_CS(UPT_SC(r)), REGS_CS((r)->skas.regs)) +#define UPT_ORIG_RAX(r) \ + __CHOOSE_MODE((r)->tt.orig_rax, REGS_ORIG_RAX((r)->skas.regs)) + +#define UPT_IP(r) __CHOOSE_MODE(SC_IP(UPT_SC(r)), REGS_IP((r)->skas.regs)) +#define UPT_SP(r) __CHOOSE_MODE(SC_SP(UPT_SC(r)), REGS_SP((r)->skas.regs)) + +#define UPT_EFLAGS(r) \ + __CHOOSE_MODE(SC_EFLAGS(UPT_SC(r)), REGS_EFLAGS((r)->skas.regs)) +#define UPT_SC(r) ((r)->tt.sc) +#define UPT_SYSCALL_NR(r) __CHOOSE_MODE((r)->tt.syscall, (r)->skas.syscall) + +extern int user_context(unsigned long sp); + +#define UPT_IS_USER(r) \ + CHOOSE_MODE(user_context(UPT_SP(r)), (r)->skas.is_user) + +#define UPT_SYSCALL_ARG1(r) UPT_RDI(r) +#define UPT_SYSCALL_ARG2(r) UPT_RSI(r) +#define UPT_SYSCALL_ARG3(r) UPT_RDX(r) +#define UPT_SYSCALL_ARG4(r) UPT_R10(r) +#define UPT_SYSCALL_ARG5(r) UPT_R8(r) +#define UPT_SYSCALL_ARG6(r) UPT_R9(r) + +struct syscall_args { + unsigned long args[6]; +}; + +#define SYSCALL_ARGS(r) ((struct syscall_args) \ + { .args = { UPT_SYSCALL_ARG1(r), \ + UPT_SYSCALL_ARG2(r), \ + UPT_SYSCALL_ARG3(r), \ + UPT_SYSCALL_ARG4(r), \ + UPT_SYSCALL_ARG5(r), \ + UPT_SYSCALL_ARG6(r) } } ) + +#define UPT_REG(regs, reg) \ + ({ unsigned long val; \ + switch(reg){ \ + case R8: val = UPT_R8(regs); break; \ + case R9: val = UPT_R9(regs); break; \ + case R10: val = UPT_R10(regs); break; \ + case R11: val = UPT_R11(regs); break; \ + case R12: val = UPT_R12(regs); break; \ + case R13: val = UPT_R13(regs); break; \ + case R14: val = UPT_R14(regs); break; \ + case R15: val = UPT_R15(regs); break; \ + case RIP: val = UPT_IP(regs); break; \ + case RSP: val = UPT_SP(regs); break; \ + case RAX: val = UPT_RAX(regs); break; \ + case RBX: val = UPT_RBX(regs); break; \ + case RCX: val = UPT_RCX(regs); break; \ + case RDX: val = UPT_RDX(regs); break; \ + case RSI: val = UPT_RSI(regs); break; \ + case RDI: val = UPT_RDI(regs); break; \ + case RBP: val = UPT_RBP(regs); break; \ + case ORIG_RAX: val = UPT_ORIG_RAX(regs); break; \ + case CS: val = UPT_CS(regs); break; \ + case DS: val = UPT_DS(regs); break; \ + case ES: val = UPT_ES(regs); break; \ + case FS: val = UPT_FS(regs); break; \ + case GS: val = UPT_GS(regs); break; \ + case EFLAGS: val = UPT_EFLAGS(regs); break; \ + default : \ + panic("Bad register in UPT_REG : %d\n", reg); \ + val = -1; \ + } \ + val; \ + }) + + +#define UPT_SET(regs, reg, val) \ + ({ unsigned long val; \ + switch(reg){ \ + case R8: UPT_R8(regs) = val; break; \ + case R9: UPT_R9(regs) = val; break; \ + case R10: UPT_R10(regs) = val; break; \ + case R11: UPT_R11(regs) = val; break; \ + case R12: UPT_R12(regs) = val; break; \ + case R13: UPT_R13(regs) = val; break; \ + case R14: UPT_R14(regs) = val; break; \ + case R15: UPT_R15(regs) = val; break; \ + case RIP: UPT_IP(regs) = val; break; \ + case RSP: UPT_SP(regs) = val; break; \ + case RAX: UPT_RAX(regs) = val; break; \ + case RBX: UPT_RBX(regs) = val; break; \ + case RCX: UPT_RCX(regs) = val; break; \ + case RDX: UPT_RDX(regs) = val; break; \ + case RSI: UPT_RSI(regs) = val; break; \ + case RDI: UPT_RDI(regs) = val; break; \ + case RBP: UPT_RBP(regs) = val; break; \ + case ORIG_RAX: UPT_ORIG_RAX(regs) = val; break; \ + case CS: UPT_CS(regs) = val; break; \ + case DS: UPT_DS(regs) = val; break; \ + case ES: UPT_ES(regs) = val; break; \ + case FS: UPT_FS(regs) = val; break; \ + case GS: UPT_GS(regs) = val; break; \ + case EFLAGS: UPT_EFLAGS(regs) = val; break; \ + default : \ + panic("Bad register in UPT_SET : %d\n", reg); \ + break; \ + } \ + val; \ + }) + +#define UPT_SET_SYSCALL_RETURN(r, res) \ + CHOOSE_MODE(SC_SET_SYSCALL_RETURN(UPT_SC(r), (res)), \ + REGS_SET_SYSCALL_RETURN((r)->skas.regs, (res))) + +#define UPT_RESTART_SYSCALL(r) \ + CHOOSE_MODE(SC_RESTART_SYSCALL(UPT_SC(r)), \ + REGS_RESTART_SYSCALL((r)->skas.regs)) + +#define UPT_SEGV_IS_FIXABLE(r) \ + CHOOSE_MODE(SC_SEGV_IS_FIXABLE(UPT_SC(r)), \ + REGS_SEGV_IS_FIXABLE(&r->skas)) + +#define UPT_FAULT_ADDR(r) \ + __CHOOSE_MODE(SC_FAULT_ADDR(UPT_SC(r)), REGS_FAULT_ADDR(&r->skas)) + +#define UPT_FAULT_WRITE(r) \ + CHOOSE_MODE(SC_FAULT_WRITE(UPT_SC(r)), REGS_FAULT_WRITE(&r->skas)) + +#define UPT_TRAP(r) __CHOOSE_MODE(SC_TRAP_TYPE(UPT_SC(r)), REGS_TRAP(&r->skas)) +#define UPT_ERR(r) __CHOOSE_MODE(SC_FAULT_TYPE(UPT_SC(r)), REGS_ERR(&r->skas)) + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/sysdep-x86_64/ptrace_user.h b/arch/um/include/sysdep-x86_64/ptrace_user.h new file mode 100644 index 00000000000..31729973fb1 --- /dev/null +++ b/arch/um/include/sysdep-x86_64/ptrace_user.h @@ -0,0 +1,69 @@ +/* + * Copyright 2003 PathScale, Inc. + * + * Licensed under the GPL + */ + +#ifndef __SYSDEP_X86_64_PTRACE_USER_H__ +#define __SYSDEP_X86_64_PTRACE_USER_H__ + +#define __FRAME_OFFSETS +#include <sys/ptrace.h> +#include <linux/ptrace.h> +#include <asm/ptrace.h> +#undef __FRAME_OFFSETS + +#define PT_INDEX(off) ((off) / sizeof(unsigned long)) + +#define PT_SYSCALL_NR(regs) ((regs)[PT_INDEX(ORIG_RAX)]) +#define PT_SYSCALL_NR_OFFSET (ORIG_RAX) + +#define PT_SYSCALL_ARG1(regs) (((unsigned long *) (regs))[PT_INDEX(RDI)]) +#define PT_SYSCALL_ARG1_OFFSET (RDI) + +#define PT_SYSCALL_ARG2(regs) (((unsigned long *) (regs))[PT_INDEX(RSI)]) +#define PT_SYSCALL_ARG2_OFFSET (RSI) + +#define PT_SYSCALL_ARG3(regs) (((unsigned long *) (regs))[PT_INDEX(RDX)]) +#define PT_SYSCALL_ARG3_OFFSET (RDX) + +#define PT_SYSCALL_ARG4(regs) (((unsigned long *) (regs))[PT_INDEX(RCX)]) +#define PT_SYSCALL_ARG4_OFFSET (RCX) + +#define PT_SYSCALL_ARG5(regs) (((unsigned long *) (regs))[PT_INDEX(R8)]) +#define PT_SYSCALL_ARG5_OFFSET (R8) + +#define PT_SYSCALL_ARG6(regs) (((unsigned long *) (regs))[PT_INDEX(R9)]) +#define PT_SYSCALL_ARG6_OFFSET (R9) + +#define PT_SYSCALL_RET_OFFSET (RAX) + +#define PT_IP_OFFSET (RIP) +#define PT_IP(regs) ((regs)[PT_INDEX(RIP)]) + +#define PT_SP_OFFSET (RSP) +#define PT_SP(regs) ((regs)[PT_INDEX(RSP)]) + +#define PT_ORIG_RAX_OFFSET (ORIG_RAX) +#define PT_ORIG_RAX(regs) ((regs)[PT_INDEX(ORIG_RAX)]) + +/* x86_64 FC3 doesn't define this in /usr/include/linux/ptrace.h even though + * it's defined in the kernel's include/linux/ptrace.h. Additionally, use the + * 2.4 name and value for 2.4 host compatibility. + */ +#ifndef PTRACE_OLDSETOPTIONS +#define PTRACE_OLDSETOPTIONS 21 +#endif + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/sysdep-x86_64/sigcontext.h b/arch/um/include/sysdep-x86_64/sigcontext.h new file mode 100644 index 00000000000..1e38a54ff4c --- /dev/null +++ b/arch/um/include/sysdep-x86_64/sigcontext.h @@ -0,0 +1,49 @@ +/* + * Copyright 2003 PathScale, Inc. + * + * Licensed under the GPL + */ + +#ifndef __SYSDEP_X86_64_SIGCONTEXT_H +#define __SYSDEP_X86_64_SIGCONTEXT_H + +#include <sysdep/sc.h> + +#define IP_RESTART_SYSCALL(ip) ((ip) -= 2) + +#define SC_RESTART_SYSCALL(sc) IP_RESTART_SYSCALL(SC_IP(sc)) +#define SC_SET_SYSCALL_RETURN(sc, result) SC_RAX(sc) = (result) + +#define SC_FAULT_ADDR(sc) SC_CR2(sc) +#define SC_FAULT_TYPE(sc) SC_ERR(sc) + +#define FAULT_WRITE(err) ((err) & 2) + +#define SC_FAULT_WRITE(sc) FAULT_WRITE(SC_FAULT_TYPE(sc)) + +#define SC_TRAP_TYPE(sc) SC_TRAPNO(sc) + +/* ptrace expects that, at the start of a system call, %eax contains + * -ENOSYS, so this makes it so. + */ + +#define SC_START_SYSCALL(sc) do SC_RAX(sc) = -ENOSYS; while(0) + +#define SEGV_IS_FIXABLE(trap) ((trap) == 14) +#define SC_SEGV_IS_FIXABLE(sc) SEGV_IS_FIXABLE(SC_TRAP_TYPE(sc)) + +extern unsigned long *sc_sigmask(void *sc_ptr); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ + diff --git a/arch/um/include/sysdep-x86_64/signal.h b/arch/um/include/sysdep-x86_64/signal.h new file mode 100644 index 00000000000..e5e52756fab --- /dev/null +++ b/arch/um/include/sysdep-x86_64/signal.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2004 PathScale, Inc + * Licensed under the GPL + */ + +#ifndef __X86_64_SIGNAL_H_ +#define __X86_64_SIGNAL_H_ + +#define ARCH_GET_SIGCONTEXT(sc, sig_addr) \ + do { \ + struct ucontext *__uc; \ + asm("movq %%rdx, %0" : "=r" (__uc)); \ + sc = (struct sigcontext *) &__uc->uc_mcontext; \ + } while(0) + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/sysdep-x86_64/syscalls.h b/arch/um/include/sysdep-x86_64/syscalls.h new file mode 100644 index 00000000000..b187a4157ff --- /dev/null +++ b/arch/um/include/sysdep-x86_64/syscalls.h @@ -0,0 +1,91 @@ +/* + * Copyright 2003 PathScale, Inc. + * + * Licensed under the GPL + */ + +#ifndef __SYSDEP_X86_64_SYSCALLS_H__ +#define __SYSDEP_X86_64_SYSCALLS_H__ + +#include <linux/msg.h> +#include <linux/shm.h> + +typedef long syscall_handler_t(void); + +extern syscall_handler_t *ia32_sys_call_table[]; + +#define EXECUTE_SYSCALL(syscall, regs) \ + (((long (*)(long, long, long, long, long, long)) \ + (*sys_call_table[syscall]))(UPT_SYSCALL_ARG1(®s->regs), \ + UPT_SYSCALL_ARG2(®s->regs), \ + UPT_SYSCALL_ARG3(®s->regs), \ + UPT_SYSCALL_ARG4(®s->regs), \ + UPT_SYSCALL_ARG5(®s->regs), \ + UPT_SYSCALL_ARG6(®s->regs))) + +extern long old_mmap(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long pgoff); +extern syscall_handler_t wrap_sys_shmat; +extern syscall_handler_t sys_modify_ldt; +extern syscall_handler_t sys_arch_prctl; + +#define ARCH_SYSCALLS \ + [ __NR_mmap ] = (syscall_handler_t *) old_mmap, \ + [ __NR_select ] = (syscall_handler_t *) sys_select, \ + [ __NR_mincore ] = (syscall_handler_t *) sys_mincore, \ + [ __NR_madvise ] = (syscall_handler_t *) sys_madvise, \ + [ __NR_shmget ] = (syscall_handler_t *) sys_shmget, \ + [ __NR_shmat ] = (syscall_handler_t *) wrap_sys_shmat, \ + [ __NR_shmctl ] = (syscall_handler_t *) sys_shmctl, \ + [ __NR_semop ] = (syscall_handler_t *) sys_semop, \ + [ __NR_semget ] = (syscall_handler_t *) sys_semget, \ + [ __NR_semctl ] = (syscall_handler_t *) sys_semctl, \ + [ __NR_shmdt ] = (syscall_handler_t *) sys_shmdt, \ + [ __NR_msgget ] = (syscall_handler_t *) sys_msgget, \ + [ __NR_msgsnd ] = (syscall_handler_t *) sys_msgsnd, \ + [ __NR_msgrcv ] = (syscall_handler_t *) sys_msgrcv, \ + [ __NR_msgctl ] = (syscall_handler_t *) sys_msgctl, \ + [ __NR_pivot_root ] = (syscall_handler_t *) sys_pivot_root, \ + [ __NR_tuxcall ] = (syscall_handler_t *) sys_ni_syscall, \ + [ __NR_security ] = (syscall_handler_t *) sys_ni_syscall, \ + [ __NR_epoll_ctl_old ] = (syscall_handler_t *) sys_ni_syscall, \ + [ __NR_epoll_wait_old ] = (syscall_handler_t *) sys_ni_syscall, \ + [ __NR_modify_ldt ] = (syscall_handler_t *) sys_modify_ldt, \ + [ __NR_arch_prctl ] = (syscall_handler_t *) sys_arch_prctl, \ + [ __NR_socket ] = (syscall_handler_t *) sys_socket, \ + [ __NR_connect ] = (syscall_handler_t *) sys_connect, \ + [ __NR_accept ] = (syscall_handler_t *) sys_accept, \ + [ __NR_recvfrom ] = (syscall_handler_t *) sys_recvfrom, \ + [ __NR_recvmsg ] = (syscall_handler_t *) sys_recvmsg, \ + [ __NR_sendmsg ] = (syscall_handler_t *) sys_sendmsg, \ + [ __NR_bind ] = (syscall_handler_t *) sys_bind, \ + [ __NR_listen ] = (syscall_handler_t *) sys_listen, \ + [ __NR_getsockname ] = (syscall_handler_t *) sys_getsockname, \ + [ __NR_getpeername ] = (syscall_handler_t *) sys_getpeername, \ + [ __NR_socketpair ] = (syscall_handler_t *) sys_socketpair, \ + [ __NR_sendto ] = (syscall_handler_t *) sys_sendto, \ + [ __NR_shutdown ] = (syscall_handler_t *) sys_shutdown, \ + [ __NR_setsockopt ] = (syscall_handler_t *) sys_setsockopt, \ + [ __NR_getsockopt ] = (syscall_handler_t *) sys_getsockopt, \ + [ __NR_iopl ] = (syscall_handler_t *) sys_ni_syscall, \ + [ __NR_set_thread_area ] = (syscall_handler_t *) sys_ni_syscall, \ + [ __NR_get_thread_area ] = (syscall_handler_t *) sys_ni_syscall, \ + [ __NR_semtimedop ] = (syscall_handler_t *) sys_semtimedop, \ + [ 251 ] = (syscall_handler_t *) sys_ni_syscall, + +#define LAST_ARCH_SYSCALL 251 +#define NR_syscalls 1024 + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/sysrq.h b/arch/um/include/sysrq.h new file mode 100644 index 00000000000..2ce9423460b --- /dev/null +++ b/arch/um/include/sysrq.h @@ -0,0 +1,6 @@ +#ifndef __UM_SYSRQ_H +#define __UM_SYSRQ_H + +extern void show_trace(unsigned long *stack); + +#endif diff --git a/arch/um/include/tempfile.h b/arch/um/include/tempfile.h new file mode 100644 index 00000000000..e36d9e0f510 --- /dev/null +++ b/arch/um/include/tempfile.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __TEMPFILE_H__ +#define __TEMPFILE_H__ + +extern int make_tempfile(const char *template, char **tempname, int do_unlink); + +#endif +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/time_user.h b/arch/um/include/time_user.h new file mode 100644 index 00000000000..6793a2fcd0a --- /dev/null +++ b/arch/um/include/time_user.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __TIME_USER_H__ +#define __TIME_USER_H__ + +extern void timer(void); +extern void switch_timers(int to_real); +extern void set_interval(int timer_type); +extern void idle_sleep(int secs); +extern void enable_timer(void); +extern void disable_timer(void); +extern unsigned long time_lock(void); +extern void time_unlock(unsigned long); + +#endif diff --git a/arch/um/include/tlb.h b/arch/um/include/tlb.h new file mode 100644 index 00000000000..da1097285b8 --- /dev/null +++ b/arch/um/include/tlb.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __TLB_H__ +#define __TLB_H__ + +#include "um_mmu.h" + +struct host_vm_op { + enum { MMAP, MUNMAP, MPROTECT } type; + union { + struct { + unsigned long addr; + unsigned long len; + unsigned int r:1; + unsigned int w:1; + unsigned int x:1; + int fd; + __u64 offset; + } mmap; + struct { + unsigned long addr; + unsigned long len; + } munmap; + struct { + unsigned long addr; + unsigned long len; + unsigned int r:1; + unsigned int w:1; + unsigned int x:1; + } mprotect; + } u; +}; + +extern void mprotect_kernel_vm(int w); +extern void force_flush_all(void); +extern void fix_range_common(struct mm_struct *mm, unsigned long start_addr, + unsigned long end_addr, int force, int data, + void (*do_ops)(int, struct host_vm_op *, int)); +extern int flush_tlb_kernel_range_common(unsigned long start, + unsigned long end); + +extern int add_mmap(unsigned long virt, unsigned long phys, unsigned long len, + int r, int w, int x, struct host_vm_op *ops, int index, + int last_filled, int data, + void (*do_ops)(int, struct host_vm_op *, int)); +extern int add_munmap(unsigned long addr, unsigned long len, + struct host_vm_op *ops, int index, int last_filled, + int data, void (*do_ops)(int, struct host_vm_op *, int)); +extern int add_mprotect(unsigned long addr, unsigned long len, int r, int w, + int x, struct host_vm_op *ops, int index, + int last_filled, int data, + void (*do_ops)(int, struct host_vm_op *, int)); +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/ubd_user.h b/arch/um/include/ubd_user.h new file mode 100644 index 00000000000..bb66517f073 --- /dev/null +++ b/arch/um/include/ubd_user.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Copyright (C) 2001 RidgeRun, Inc (glonnon@ridgerun.com) + * Licensed under the GPL + */ + +#ifndef __UM_UBD_USER_H +#define __UM_UBD_USER_H + +extern void ignore_sigwinch_sig(void); +extern int start_io_thread(unsigned long sp, int *fds_out); +extern int io_thread(void *arg); +extern int kernel_fd; + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/um_mmu.h b/arch/um/include/um_mmu.h new file mode 100644 index 00000000000..0fa64323830 --- /dev/null +++ b/arch/um/include/um_mmu.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __ARCH_UM_MMU_H +#define __ARCH_UM_MMU_H + +#include "uml-config.h" +#include "choose-mode.h" + +#ifdef UML_CONFIG_MODE_TT +#include "mmu-tt.h" +#endif + +#ifdef UML_CONFIG_MODE_SKAS +#include "mmu-skas.h" +#endif + +typedef union mm_context { +#ifdef UML_CONFIG_MODE_TT + struct mmu_context_tt tt; +#endif +#ifdef UML_CONFIG_MODE_SKAS + struct mmu_context_skas skas; +#endif +} mm_context_t; + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/um_uaccess.h b/arch/um/include/um_uaccess.h new file mode 100644 index 00000000000..6e348cb6de2 --- /dev/null +++ b/arch/um/include/um_uaccess.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __ARCH_UM_UACCESS_H +#define __ARCH_UM_UACCESS_H + +#include "linux/config.h" +#include "choose-mode.h" + +#ifdef CONFIG_MODE_TT +#include "uaccess-tt.h" +#endif + +#ifdef CONFIG_MODE_SKAS +#include "uaccess-skas.h" +#endif + +#define access_ok(type, addr, size) \ + CHOOSE_MODE_PROC(access_ok_tt, access_ok_skas, type, addr, size) + +/* this function will go away soon - use access_ok() instead */ +static inline int __deprecated verify_area(int type, const void __user *addr, unsigned long size) +{ + return (CHOOSE_MODE_PROC(verify_area_tt, verify_area_skas, type, addr, + size)); +} + +static inline int copy_from_user(void *to, const void __user *from, int n) +{ + return(CHOOSE_MODE_PROC(copy_from_user_tt, copy_from_user_skas, to, + from, n)); +} + +static inline int copy_to_user(void __user *to, const void *from, int n) +{ + return(CHOOSE_MODE_PROC(copy_to_user_tt, copy_to_user_skas, to, + from, n)); +} + +/* + * strncpy_from_user: - Copy a NUL terminated string from userspace. + * @dst: Destination address, in kernel space. This buffer must be at + * least @count bytes long. + * @src: Source address, in user space. + * @count: Maximum number of bytes to copy, including the trailing NUL. + * + * Copies a NUL-terminated string from userspace to kernel space. + * + * On success, returns the length of the string (not including the trailing + * NUL). + * + * If access to userspace fails, returns -EFAULT (some data may have been + * copied). + * + * If @count is smaller than the length of the string, copies @count bytes + * and returns @count. + */ + +static inline int strncpy_from_user(char *dst, const char __user *src, int count) +{ + return(CHOOSE_MODE_PROC(strncpy_from_user_tt, strncpy_from_user_skas, + dst, src, count)); +} + +/* + * __clear_user: - Zero a block of memory in user space, with less checking. + * @to: Destination address, in user space. + * @n: Number of bytes to zero. + * + * Zero a block of memory in user space. Caller must check + * the specified block with access_ok() before calling this function. + * + * Returns number of bytes that could not be cleared. + * On success, this will be zero. + */ +static inline int __clear_user(void *mem, int len) +{ + return(CHOOSE_MODE_PROC(__clear_user_tt, __clear_user_skas, mem, len)); +} + +/* + * clear_user: - Zero a block of memory in user space. + * @to: Destination address, in user space. + * @n: Number of bytes to zero. + * + * Zero a block of memory in user space. + * + * Returns number of bytes that could not be cleared. + * On success, this will be zero. + */ +static inline int clear_user(void __user *mem, int len) +{ + return(CHOOSE_MODE_PROC(clear_user_tt, clear_user_skas, mem, len)); +} + +/* + * strlen_user: - Get the size of a string in user space. + * @str: The string to measure. + * @n: The maximum valid length + * + * Get the size of a NUL-terminated string in user space. + * + * Returns the size of the string INCLUDING the terminating NUL. + * On exception, returns 0. + * If the string is too long, returns a value greater than @n. + */ +static inline int strnlen_user(const void __user *str, long len) +{ + return(CHOOSE_MODE_PROC(strnlen_user_tt, strnlen_user_skas, str, len)); +} + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/umid.h b/arch/um/include/umid.h new file mode 100644 index 00000000000..11373c851f1 --- /dev/null +++ b/arch/um/include/umid.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __UMID_H__ +#define __UMID_H__ + +extern int umid_file_name(char *name, char *buf, int len); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/uml_uaccess.h b/arch/um/include/uml_uaccess.h new file mode 100644 index 00000000000..f77eb642845 --- /dev/null +++ b/arch/um/include/uml_uaccess.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __UML_UACCESS_H__ +#define __UML_UACCESS_H__ + +extern int __do_copy_to_user(void *to, const void *from, int n, + void **fault_addr, void **fault_catcher); +extern unsigned long __do_user_copy(void *to, const void *from, int n, + void **fault_addr, void **fault_catcher, + void (*op)(void *to, const void *from, + int n), int *faulted_out); +void __do_copy(void *to, const void *from, int n); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/user.h b/arch/um/include/user.h new file mode 100644 index 00000000000..57ee9e26122 --- /dev/null +++ b/arch/um/include/user.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __USER_H__ +#define __USER_H__ + +extern void panic(const char *fmt, ...); +extern int printk(const char *fmt, ...); +extern void schedule(void); +extern void *um_kmalloc(int size); +extern void *um_kmalloc_atomic(int size); +extern void kfree(void *ptr); +extern int in_aton(char *str); +extern int open_gdb_chan(void); +extern int strlcpy(char *, const char *, int); +extern void *um_vmalloc(int size); +extern void vfree(void *ptr); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/include/user_util.h b/arch/um/include/user_util.h new file mode 100644 index 00000000000..103cd320386 --- /dev/null +++ b/arch/um/include/user_util.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#ifndef __USER_UTIL_H__ +#define __USER_UTIL_H__ + +#include "sysdep/ptrace.h" + +#define CATCH_EINTR(expr) while ((errno = 0, ((expr) < 0)) && (errno == EINTR)) + +extern int mode_tt; + +extern int grantpt(int __fd); +extern int unlockpt(int __fd); +extern char *ptsname(int __fd); + +struct cpu_task { + int pid; + void *task; +}; + +extern struct cpu_task cpu_tasks[]; + +struct signal_info { + void (*handler)(int, union uml_pt_regs *); + int is_irq; +}; + +extern struct signal_info sig_info[]; + +extern unsigned long low_physmem; +extern unsigned long high_physmem; +extern unsigned long uml_physmem; +extern unsigned long uml_reserved; +extern unsigned long end_vm; +extern unsigned long start_vm; +extern unsigned long highmem; + +extern char host_info[]; + +extern char saved_command_line[]; +extern char command_line[]; + +extern char *tempdir; + +extern unsigned long _stext, _etext, _sdata, _edata, __bss_start, _end; +extern unsigned long _unprotected_end; +extern unsigned long brk_start; + +extern int pty_output_sigio; +extern int pty_close_sigio; + +extern void stop(void); +extern void stack_protections(unsigned long address); +extern void task_protections(unsigned long address); +extern int wait_for_stop(int pid, int sig, int cont_type, void *relay); +extern void *add_signal_handler(int sig, void (*handler)(int)); +extern int start_fork_tramp(void *arg, unsigned long temp_stack, + int clone_flags, int (*tramp)(void *)); +extern int linux_main(int argc, char **argv); +extern void set_cmdline(char *cmd); +extern void input_cb(void (*proc)(void *), void *arg, int arg_len); +extern int get_pty(void); +extern void *um_kmalloc(int size); +extern int switcheroo(int fd, int prot, void *from, void *to, int size); +extern void setup_machinename(char *machine_out); +extern void setup_hostinfo(void); +extern void add_arg(char *arg); +extern void init_new_thread_stack(void *sig_stack, void (*usr1_handler)(int)); +extern void init_new_thread_signals(int altstack); +extern void do_exec(int old_pid, int new_pid); +extern void tracer_panic(char *msg, ...); +extern char *get_umid(int only_if_set); +extern void do_longjmp(void *p, int val); +extern int detach(int pid, int sig); +extern int attach(int pid); +extern void kill_child_dead(int pid); +extern int cont(int pid); +extern void check_ptrace(void); +extern void check_sigio(void); +extern int run_kernel_thread(int (*fn)(void *), void *arg, void **jmp_ptr); +extern void write_sigio_workaround(void); +extern void arch_check_bugs(void); +extern int cpu_feature(char *what, char *buf, int len); +extern int arch_handle_signal(int sig, union uml_pt_regs *regs); +extern int arch_fixup(unsigned long address, void *sc_ptr); +extern void forward_pending_sigio(int target); +extern int can_do_skas(void); +extern void arch_init_thread(void); +extern int setjmp_wrapper(void (*proc)(void *, void *), ...); +extern int raw(int fd); + +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/kernel/Makefile b/arch/um/kernel/Makefile new file mode 100644 index 00000000000..dc796c1bf39 --- /dev/null +++ b/arch/um/kernel/Makefile @@ -0,0 +1,58 @@ +# +# Copyright (C) 2002 Jeff Dike (jdike@karaya.com) +# Licensed under the GPL +# + +extra-y := vmlinux.lds +clean-files := vmlinux.lds.S config.tmp + +obj-y = checksum.o config.o exec_kern.o exitcode.o \ + helper.o init_task.o irq.o irq_user.o ksyms.o main.o mem.o mem_user.o \ + physmem.o process.o process_kern.o ptrace.o reboot.o resource.o \ + sigio_user.o sigio_kern.o signal_kern.o signal_user.o smp.o \ + syscall_kern.o sysrq.o sys_call_table.o tempfile.o time.o time_kern.o \ + tlb.o trap_kern.o trap_user.o uaccess_user.o um_arch.o umid.o \ + user_util.o + +obj-$(CONFIG_BLK_DEV_INITRD) += initrd_kern.o initrd_user.o +obj-$(CONFIG_GPROF) += gprof_syms.o +obj-$(CONFIG_GCOV) += gmon_syms.o +obj-$(CONFIG_TTY_LOG) += tty_log.o +obj-$(CONFIG_SYSCALL_DEBUG) += syscall_user.o + +obj-$(CONFIG_MODE_TT) += tt/ +obj-$(CONFIG_MODE_SKAS) += skas/ + +# This needs be compiled with frame pointers regardless of how the rest of the +# kernel is built. +CFLAGS_frame.o := -fno-omit-frame-pointer + +user-objs-$(CONFIG_TTY_LOG) += tty_log.o + +USER_OBJS := $(user-objs-y) config.o helper.o main.o process.o tempfile.o \ + time.o tty_log.o umid.o user_util.o frame.o + +include arch/um/scripts/Makefile.rules + +targets += config.c + +# Be careful with the below Sed code - sed is pitfall-rich! +# We use sed to lower build requirements, for "embedded" builders for instance. + +$(obj)/config.tmp: $(objtree)/.config FORCE + $(call if_changed,quote1) + +quiet_cmd_quote1 = QUOTE $@ + cmd_quote1 = sed -e 's/"/\\"/g' -e 's/^/"/' -e 's/$$/\\n"/' \ + $< > $@ + +$(obj)/config.c: $(src)/config.c.in $(obj)/config.tmp FORCE + $(call if_changed,quote2) + +quiet_cmd_quote2 = QUOTE $@ + cmd_quote2 = sed -e '/CONFIG/{' \ + -e 's/"CONFIG"\;/""/' \ + -e 'r $(obj)/config.tmp' \ + -e 'a""\;' \ + -e '}' \ + $< > $@ diff --git a/arch/um/kernel/checksum.c b/arch/um/kernel/checksum.c new file mode 100644 index 00000000000..e69b2be951d --- /dev/null +++ b/arch/um/kernel/checksum.c @@ -0,0 +1,36 @@ +#include "asm/uaccess.h" +#include "linux/errno.h" +#include "linux/module.h" + +unsigned int arch_csum_partial(const unsigned char *buff, int len, int sum); + +unsigned int csum_partial(unsigned char *buff, int len, int sum) +{ + return arch_csum_partial(buff, len, sum); +} + +EXPORT_SYMBOL(csum_partial); + +unsigned int csum_partial_copy_to(const unsigned char *src, + unsigned char __user *dst, int len, int sum, + int *err_ptr) +{ + if(copy_to_user(dst, src, len)){ + *err_ptr = -EFAULT; + return(-1); + } + + return(arch_csum_partial(src, len, sum)); +} + +unsigned int csum_partial_copy_from(const unsigned char __user *src, + unsigned char *dst, int len, int sum, + int *err_ptr) +{ + if(copy_from_user(dst, src, len)){ + *err_ptr = -EFAULT; + return(-1); + } + + return arch_csum_partial(dst, len, sum); +} diff --git a/arch/um/kernel/config.c.in b/arch/um/kernel/config.c.in new file mode 100644 index 00000000000..c062cbfe386 --- /dev/null +++ b/arch/um/kernel/config.c.in @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <stdio.h> +#include <stdlib.h> +#include "init.h" + +static __initdata char *config = "CONFIG"; + +static int __init print_config(char *line, int *add) +{ + printf("%s", config); + exit(0); +} + +__uml_setup("--showconfig", print_config, +"--showconfig\n" +" Prints the config file that this UML binary was generated from.\n\n" +); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/kernel/dyn.lds.S b/arch/um/kernel/dyn.lds.S new file mode 100644 index 00000000000..715b0838a68 --- /dev/null +++ b/arch/um/kernel/dyn.lds.S @@ -0,0 +1,176 @@ +#include <asm-generic/vmlinux.lds.h> + +OUTPUT_FORMAT(ELF_FORMAT) +OUTPUT_ARCH(ELF_ARCH) +ENTRY(_start) +jiffies = jiffies_64; + +SECTIONS +{ + PROVIDE (__executable_start = START); + . = START + SIZEOF_HEADERS; + .interp : { *(.interp) } + /* Used in arch/um/kernel/mem.c. Any memory between START and __binary_start + * is remapped.*/ + __binary_start = .; + . = ALIGN(4096); /* Init code and data */ + _stext = .; + __init_begin = .; + .init.text : { + _sinittext = .; + *(.init.text) + _einittext = .; + } + + . = ALIGN(4096); + + /* Read-only sections, merged into text segment: */ + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .rel.init : { *(.rel.init) } + .rela.init : { *(.rela.init) } + .rel.text : { *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) } + .rela.text : { *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) } + .rel.fini : { *(.rel.fini) } + .rela.fini : { *(.rela.fini) } + .rel.rodata : { *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) } + .rela.rodata : { *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) } + .rel.data : { *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) } + .rela.data : { *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) } + .rel.tdata : { *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) } + .rela.tdata : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) } + .rel.tbss : { *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) } + .rela.tbss : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.bss : { *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) } + .rela.bss : { *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + .init : { + KEEP (*(.init)) + } =0x90909090 + .plt : { *(.plt) } + .text : { + *(.text) + SCHED_TEXT + LOCK_TEXT + *(.fixup) + *(.stub .text.* .gnu.linkonce.t.*) + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + } =0x90909090 + .fini : { + KEEP (*(.fini)) + } =0x90909090 + + .kstrtab : { *(.kstrtab) } + + #include "asm/common.lds.S" + + init.data : { *(.init.data) } + + /* Ensure the __preinit_array_start label is properly aligned. We + could instead move the label definition inside the section, but + the linker would then create the section even if it turns out to + be empty, which isn't pretty. */ + . = ALIGN(32 / 8); + .preinit_array : { *(.preinit_array) } + .init_array : { *(.init_array) } + .fini_array : { *(.fini_array) } + .data : { + . = ALIGN(KERNEL_STACK_SIZE); /* init_task */ + *(.data.init_task) + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + .data1 : { *(.data1) } + .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) } + .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } + .eh_frame : { KEEP (*(.eh_frame)) } + .gcc_except_table : { *(.gcc_except_table) } + .dynamic : { *(.dynamic) } + .ctors : { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + /* We don't want to include the .ctor section from + from the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + .dtors : { + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + .jcr : { KEEP (*(.jcr)) } + .got : { *(.got.plt) *(.got) } + _edata = .; + PROVIDE (edata = .); + __bss_start = .; + .bss : { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + /* Align here to ensure that the .bss section occupies space up to + _end. Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. */ + . = ALIGN(32 / 8); + . = ALIGN(32 / 8); + } + _end = .; + PROVIDE (end = .); + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } +} diff --git a/arch/um/kernel/exec_kern.c b/arch/um/kernel/exec_kern.c new file mode 100644 index 00000000000..49ddabe69be --- /dev/null +++ b/arch/um/kernel/exec_kern.c @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/slab.h" +#include "linux/smp_lock.h" +#include "linux/ptrace.h" +#include "asm/ptrace.h" +#include "asm/pgtable.h" +#include "asm/tlbflush.h" +#include "asm/uaccess.h" +#include "user_util.h" +#include "kern_util.h" +#include "mem_user.h" +#include "kern.h" +#include "irq_user.h" +#include "tlb.h" +#include "2_5compat.h" +#include "os.h" +#include "time_user.h" +#include "choose-mode.h" +#include "mode_kern.h" + +void flush_thread(void) +{ + CHOOSE_MODE(flush_thread_tt(), flush_thread_skas()); +} + +void start_thread(struct pt_regs *regs, unsigned long eip, unsigned long esp) +{ + CHOOSE_MODE_PROC(start_thread_tt, start_thread_skas, regs, eip, esp); +} + +extern void log_exec(char **argv, void *tty); + +static long execve1(char *file, char __user * __user *argv, + char *__user __user *env) +{ + long error; + +#ifdef CONFIG_TTY_LOG + log_exec(argv, current->tty); +#endif + error = do_execve(file, argv, env, ¤t->thread.regs); + if (error == 0){ + task_lock(current); + current->ptrace &= ~PT_DTRACE; + task_unlock(current); + set_cmdline(current_cmd()); + } + return(error); +} + +long um_execve(char *file, char __user *__user *argv, char __user *__user *env) +{ + long err; + + err = execve1(file, argv, env); + if(!err) + do_longjmp(current->thread.exec_buf, 1); + return(err); +} + +long sys_execve(char *file, char __user *__user *argv, + char __user *__user *env) +{ + long error; + char *filename; + + lock_kernel(); + filename = getname((char __user *) file); + error = PTR_ERR(filename); + if (IS_ERR(filename)) goto out; + error = execve1(filename, argv, env); + putname(filename); + out: + unlock_kernel(); + return(error); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/kernel/exitcode.c b/arch/um/kernel/exitcode.c new file mode 100644 index 00000000000..0ea87f24b36 --- /dev/null +++ b/arch/um/kernel/exitcode.c @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/init.h" +#include "linux/ctype.h" +#include "linux/proc_fs.h" +#include "asm/uaccess.h" + +/* If read and write race, the read will still atomically read a valid + * value. + */ +int uml_exitcode = 0; + +static int read_proc_exitcode(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + + len = sprintf(page, "%d\n", uml_exitcode); + len -= off; + if(len <= off+count) *eof = 1; + *start = page + off; + if(len > count) len = count; + if(len < 0) len = 0; + return(len); +} + +static int write_proc_exitcode(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + char *end, buf[sizeof("nnnnn\0")]; + int tmp; + + if(copy_from_user(buf, buffer, count)) + return(-EFAULT); + tmp = simple_strtol(buf, &end, 0); + if((*end != '\0') && !isspace(*end)) + return(-EINVAL); + uml_exitcode = tmp; + return(count); +} + +static int make_proc_exitcode(void) +{ + struct proc_dir_entry *ent; + + ent = create_proc_entry("exitcode", 0600, &proc_root); + if(ent == NULL){ + printk("make_proc_exitcode : Failed to register " + "/proc/exitcode\n"); + return(0); + } + + ent->read_proc = read_proc_exitcode; + ent->write_proc = write_proc_exitcode; + + return(0); +} + +__initcall(make_proc_exitcode); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/kernel/gmon_syms.c b/arch/um/kernel/gmon_syms.c new file mode 100644 index 00000000000..2c86e7fdb01 --- /dev/null +++ b/arch/um/kernel/gmon_syms.c @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/module.h" + +extern void __bb_init_func(void *); +EXPORT_SYMBOL(__bb_init_func); + +/* This is defined (and referred to in profiling stub code) only by some GCC + * versions in libgcov. + * + * Since SuSE backported the fix, we cannot handle it depending on GCC version. + * So, unconditinally export it. But also give it a weak declaration, which will + * be overriden by any other one. + */ + +extern void __gcov_init(void *) __attribute__((weak)); +EXPORT_SYMBOL(__gcov_init); + +extern void __gcov_merge_add(void *) __attribute__((weak)); +EXPORT_SYMBOL(__gcov_merge_add); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/kernel/gprof_syms.c b/arch/um/kernel/gprof_syms.c new file mode 100644 index 00000000000..9244f018d44 --- /dev/null +++ b/arch/um/kernel/gprof_syms.c @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/module.h" + +extern void mcount(void); +EXPORT_SYMBOL(mcount); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/kernel/helper.c b/arch/um/kernel/helper.c new file mode 100644 index 00000000000..13b1f5c2f7e --- /dev/null +++ b/arch/um/kernel/helper.c @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <sched.h> +#include <sys/signal.h> +#include <sys/wait.h> +#include "user.h" +#include "kern_util.h" +#include "user_util.h" +#include "os.h" + +struct helper_data { + void (*pre_exec)(void*); + void *pre_data; + char **argv; + int fd; +}; + +/* Debugging aid, changed only from gdb */ +int helper_pause = 0; + +static void helper_hup(int sig) +{ +} + +static int helper_child(void *arg) +{ + struct helper_data *data = arg; + char **argv = data->argv; + int errval; + + if(helper_pause){ + signal(SIGHUP, helper_hup); + pause(); + } + if(data->pre_exec != NULL) + (*data->pre_exec)(data->pre_data); + execvp(argv[0], argv); + errval = errno; + printk("execvp of '%s' failed - errno = %d\n", argv[0], errno); + os_write_file(data->fd, &errval, sizeof(errval)); + os_kill_process(os_getpid(), 0); + return(0); +} + +/* Returns either the pid of the child process we run or -E* on failure. + * XXX The alloc_stack here breaks if this is called in the tracing thread */ +int run_helper(void (*pre_exec)(void *), void *pre_data, char **argv, + unsigned long *stack_out) +{ + struct helper_data data; + unsigned long stack, sp; + int pid, fds[2], ret, n; + + if((stack_out != NULL) && (*stack_out != 0)) + stack = *stack_out; + else stack = alloc_stack(0, um_in_interrupt()); + if(stack == 0) + return(-ENOMEM); + + ret = os_pipe(fds, 1, 0); + if(ret < 0){ + printk("run_helper : pipe failed, ret = %d\n", -ret); + goto out_free; + } + + ret = os_set_exec_close(fds[1], 1); + if(ret < 0){ + printk("run_helper : setting FD_CLOEXEC failed, ret = %d\n", + -ret); + goto out_close; + } + + sp = stack + page_size() - sizeof(void *); + data.pre_exec = pre_exec; + data.pre_data = pre_data; + data.argv = argv; + data.fd = fds[1]; + pid = clone(helper_child, (void *) sp, CLONE_VM | SIGCHLD, &data); + if(pid < 0){ + printk("run_helper : clone failed, errno = %d\n", errno); + ret = -errno; + goto out_close; + } + + os_close_file(fds[1]); + fds[1] = -1; + + /*Read the errno value from the child.*/ + n = os_read_file(fds[0], &ret, sizeof(ret)); + if(n < 0){ + printk("run_helper : read on pipe failed, ret = %d\n", -n); + ret = n; + os_kill_process(pid, 1); + } + else if(n != 0){ + CATCH_EINTR(n = waitpid(pid, NULL, 0)); + ret = -errno; + } else { + ret = pid; + } + +out_close: + if (fds[1] != -1) + os_close_file(fds[1]); + os_close_file(fds[0]); +out_free: + if(stack_out == NULL) + free_stack(stack, 0); + else *stack_out = stack; + return(ret); +} + +int run_helper_thread(int (*proc)(void *), void *arg, unsigned int flags, + unsigned long *stack_out, int stack_order) +{ + unsigned long stack, sp; + int pid, status; + + stack = alloc_stack(stack_order, um_in_interrupt()); + if(stack == 0) return(-ENOMEM); + + sp = stack + (page_size() << stack_order) - sizeof(void *); + pid = clone(proc, (void *) sp, flags | SIGCHLD, arg); + if(pid < 0){ + printk("run_helper_thread : clone failed, errno = %d\n", + errno); + return(-errno); + } + if(stack_out == NULL){ + CATCH_EINTR(pid = waitpid(pid, &status, 0)); + if(pid < 0){ + printk("run_helper_thread - wait failed, errno = %d\n", + errno); + pid = -errno; + } + if(!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) + printk("run_helper_thread - thread returned status " + "0x%x\n", status); + free_stack(stack, stack_order); + } + else *stack_out = stack; + return(pid); +} + +int helper_wait(int pid, int block) +{ + int ret; + + CATCH_EINTR(ret = waitpid(pid, NULL, WNOHANG)); + if(ret < 0){ + printk("helper_wait : waitpid failed, errno = %d\n", errno); + return(-errno); + } + return(ret); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/kernel/init_task.c b/arch/um/kernel/init_task.c new file mode 100644 index 00000000000..cd7c85be0a1 --- /dev/null +++ b/arch/um/kernel/init_task.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/config.h" +#include "linux/mm.h" +#include "linux/module.h" +#include "linux/sched.h" +#include "linux/init_task.h" +#include "linux/mqueue.h" +#include "asm/uaccess.h" +#include "asm/pgtable.h" +#include "user_util.h" +#include "mem_user.h" + +static struct fs_struct init_fs = INIT_FS; +struct mm_struct init_mm = INIT_MM(init_mm); +static struct files_struct init_files = INIT_FILES; +static struct signal_struct init_signals = INIT_SIGNALS(init_signals); +static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); +EXPORT_SYMBOL(init_mm); + +/* + * Initial task structure. + * + * All other task structs will be allocated on slabs in fork.c + */ + +struct task_struct init_task = INIT_TASK(init_task); + +EXPORT_SYMBOL(init_task); + +/* + * Initial thread structure. + * + * We need to make sure that this is 16384-byte aligned due to the + * way process stacks are handled. This is done by having a special + * "init_task" linker map entry.. + */ + +union thread_union init_thread_union +__attribute__((__section__(".data.init_task"))) = +{ INIT_THREAD_INFO(init_task) }; + +void unprotect_stack(unsigned long stack) +{ + protect_memory(stack, (1 << CONFIG_KERNEL_STACK_ORDER) * PAGE_SIZE, + 1, 1, 0, 1); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/kernel/initrd_kern.c b/arch/um/kernel/initrd_kern.c new file mode 100644 index 00000000000..fc568af468b --- /dev/null +++ b/arch/um/kernel/initrd_kern.c @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/init.h" +#include "linux/bootmem.h" +#include "linux/initrd.h" +#include "asm/types.h" +#include "user_util.h" +#include "kern_util.h" +#include "initrd.h" +#include "init.h" +#include "os.h" + +/* Changed by uml_initrd_setup, which is a setup */ +static char *initrd __initdata = NULL; + +static int __init read_initrd(void) +{ + void *area; + long long size; + int err; + + if(initrd == NULL) return 0; + err = os_file_size(initrd, &size); + if(err) return 0; + area = alloc_bootmem(size); + if(area == NULL) return 0; + if(load_initrd(initrd, area, size) == -1) return 0; + initrd_start = (unsigned long) area; + initrd_end = initrd_start + size; + return 0; +} + +__uml_postsetup(read_initrd); + +static int __init uml_initrd_setup(char *line, int *add) +{ + initrd = line; + return 0; +} + +__uml_setup("initrd=", uml_initrd_setup, +"initrd=<initrd image>\n" +" This is used to boot UML from an initrd image. The argument is the\n" +" name of the file containing the image.\n\n" +); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/kernel/initrd_user.c b/arch/um/kernel/initrd_user.c new file mode 100644 index 00000000000..cb90681e151 --- /dev/null +++ b/arch/um/kernel/initrd_user.c @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> + +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "initrd.h" +#include "os.h" + +int load_initrd(char *filename, void *buf, int size) +{ + int fd, n; + + fd = os_open_file(filename, of_read(OPENFLAGS()), 0); + if(fd < 0){ + printk("Opening '%s' failed - err = %d\n", filename, -fd); + return(-1); + } + n = os_read_file(fd, buf, size); + if(n != size){ + printk("Read of %d bytes from '%s' failed, err = %d\n", size, + filename, -n); + return(-1); + } + + os_close_file(fd); + return(0); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/kernel/irq.c b/arch/um/kernel/irq.c new file mode 100644 index 00000000000..d71e8f00810 --- /dev/null +++ b/arch/um/kernel/irq.c @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + * Derived (i.e. mostly copied) from arch/i386/kernel/irq.c: + * Copyright (C) 1992, 1998 Linus Torvalds, Ingo Molnar + */ + +#include "linux/config.h" +#include "linux/kernel.h" +#include "linux/module.h" +#include "linux/smp.h" +#include "linux/irq.h" +#include "linux/kernel_stat.h" +#include "linux/interrupt.h" +#include "linux/random.h" +#include "linux/slab.h" +#include "linux/file.h" +#include "linux/proc_fs.h" +#include "linux/init.h" +#include "linux/seq_file.h" +#include "linux/profile.h" +#include "linux/hardirq.h" +#include "asm/irq.h" +#include "asm/hw_irq.h" +#include "asm/atomic.h" +#include "asm/signal.h" +#include "asm/system.h" +#include "asm/errno.h" +#include "asm/uaccess.h" +#include "user_util.h" +#include "kern_util.h" +#include "irq_user.h" +#include "irq_kern.h" + + +/* + * Generic, controller-independent functions: + */ + +int show_interrupts(struct seq_file *p, void *v) +{ + int i = *(loff_t *) v, j; + struct irqaction * action; + unsigned long flags; + + if (i == 0) { + seq_printf(p, " "); + for_each_online_cpu(j) + seq_printf(p, "CPU%d ",j); + seq_putc(p, '\n'); + } + + if (i < NR_IRQS) { + spin_lock_irqsave(&irq_desc[i].lock, flags); + action = irq_desc[i].action; + if (!action) + goto skip; + seq_printf(p, "%3d: ",i); +#ifndef CONFIG_SMP + seq_printf(p, "%10u ", kstat_irqs(i)); +#else + for_each_online_cpu(j) + seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]); +#endif + seq_printf(p, " %14s", irq_desc[i].handler->typename); + seq_printf(p, " %s", action->name); + + for (action=action->next; action; action = action->next) + seq_printf(p, ", %s", action->name); + + seq_putc(p, '\n'); +skip: + spin_unlock_irqrestore(&irq_desc[i].lock, flags); + } else if (i == NR_IRQS) { + seq_putc(p, '\n'); + } + + return 0; +} + +/* + * do_IRQ handles all normal device IRQ's (the special + * SMP cross-CPU interrupts have their own specific + * handlers). + */ +unsigned int do_IRQ(int irq, union uml_pt_regs *regs) +{ + irq_enter(); + __do_IRQ(irq, (struct pt_regs *) regs); + irq_exit(); + return 1; +} + +int um_request_irq(unsigned int irq, int fd, int type, + irqreturn_t (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, const char * devname, + void *dev_id) +{ + int err; + + err = request_irq(irq, handler, irqflags, devname, dev_id); + if(err) + return(err); + + if(fd != -1) + err = activate_fd(irq, fd, type, dev_id); + return(err); +} +EXPORT_SYMBOL(um_request_irq); +EXPORT_SYMBOL(reactivate_fd); + +static DEFINE_SPINLOCK(irq_spinlock); + +unsigned long irq_lock(void) +{ + unsigned long flags; + + spin_lock_irqsave(&irq_spinlock, flags); + return(flags); +} + +void irq_unlock(unsigned long flags) +{ + spin_unlock_irqrestore(&irq_spinlock, flags); +} + +/* presently hw_interrupt_type must define (startup || enable) && + * disable && end */ +static void dummy(unsigned int irq) +{ +} + +static struct hw_interrupt_type SIGIO_irq_type = { + .typename = "SIGIO", + .disable = dummy, + .enable = dummy, + .ack = dummy, + .end = dummy +}; + +static struct hw_interrupt_type SIGVTALRM_irq_type = { + .typename = "SIGVTALRM", + .shutdown = dummy, /* never called */ + .disable = dummy, + .enable = dummy, + .ack = dummy, + .end = dummy +}; + +void __init init_IRQ(void) +{ + int i; + + irq_desc[TIMER_IRQ].status = IRQ_DISABLED; + irq_desc[TIMER_IRQ].action = NULL; + irq_desc[TIMER_IRQ].depth = 1; + irq_desc[TIMER_IRQ].handler = &SIGVTALRM_irq_type; + enable_irq(TIMER_IRQ); + for(i=1;i<NR_IRQS;i++){ + irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].action = NULL; + irq_desc[i].depth = 1; + irq_desc[i].handler = &SIGIO_irq_type; + enable_irq(i); + } + init_irq_signals(0); +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-file-style: "linux" + * End: + */ diff --git a/arch/um/kernel/irq_user.c b/arch/um/kernel/irq_user.c new file mode 100644 index 00000000000..6d6f9484b88 --- /dev/null +++ b/arch/um/kernel/irq_user.c @@ -0,0 +1,443 @@ +/* + * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <signal.h> +#include <string.h> +#include <sys/poll.h> +#include <sys/types.h> +#include <sys/time.h> +#include "user_util.h" +#include "kern_util.h" +#include "user.h" +#include "process.h" +#include "signal_user.h" +#include "sigio.h" +#include "irq_user.h" +#include "os.h" + +struct irq_fd { + struct irq_fd *next; + void *id; + int fd; + int type; + int irq; + int pid; + int events; + int current_events; + int freed; +}; + +static struct irq_fd *active_fds = NULL; +static struct irq_fd **last_irq_ptr = &active_fds; + +static struct pollfd *pollfds = NULL; +static int pollfds_num = 0; +static int pollfds_size = 0; + +extern int io_count, intr_count; + +void sigio_handler(int sig, union uml_pt_regs *regs) +{ + struct irq_fd *irq_fd, *next; + int i, n; + + if(smp_sigio_handler()) return; + while(1){ + n = poll(pollfds, pollfds_num, 0); + if(n < 0){ + if(errno == EINTR) continue; + printk("sigio_handler : poll returned %d, " + "errno = %d\n", n, errno); + break; + } + if(n == 0) break; + + irq_fd = active_fds; + for(i = 0; i < pollfds_num; i++){ + if(pollfds[i].revents != 0){ + irq_fd->current_events = pollfds[i].revents; + pollfds[i].fd = -1; + } + irq_fd = irq_fd->next; + } + + for(irq_fd = active_fds; irq_fd != NULL; irq_fd = next){ + next = irq_fd->next; + if(irq_fd->current_events != 0){ + irq_fd->current_events = 0; + do_IRQ(irq_fd->irq, regs); + + /* This is here because the next irq may be + * freed in the handler. If a console goes + * away, both the read and write irqs will be + * freed. After do_IRQ, ->next will point to + * a good IRQ. + * Irqs can't be freed inside their handlers, + * so the next best thing is to have them + * marked as needing freeing, so that they + * can be freed here. + */ + next = irq_fd->next; + if(irq_fd->freed){ + free_irq(irq_fd->irq, irq_fd->id); + free_irq_by_irq_and_dev(irq_fd->irq, + irq_fd->id); + } + } + } + } +} + +int activate_ipi(int fd, int pid) +{ + return(os_set_fd_async(fd, pid)); +} + +static void maybe_sigio_broken(int fd, int type) +{ + if(isatty(fd)){ + if((type == IRQ_WRITE) && !pty_output_sigio){ + write_sigio_workaround(); + add_sigio_fd(fd, 0); + } + else if((type == IRQ_READ) && !pty_close_sigio){ + write_sigio_workaround(); + add_sigio_fd(fd, 1); + } + } +} + +int activate_fd(int irq, int fd, int type, void *dev_id) +{ + struct pollfd *tmp_pfd; + struct irq_fd *new_fd, *irq_fd; + unsigned long flags; + int pid, events, err, n, size; + + pid = os_getpid(); + err = os_set_fd_async(fd, pid); + if(err < 0) + goto out; + + new_fd = um_kmalloc(sizeof(*new_fd)); + err = -ENOMEM; + if(new_fd == NULL) + goto out; + + if(type == IRQ_READ) events = POLLIN | POLLPRI; + else events = POLLOUT; + *new_fd = ((struct irq_fd) { .next = NULL, + .id = dev_id, + .fd = fd, + .type = type, + .irq = irq, + .pid = pid, + .events = events, + .current_events = 0, + .freed = 0 } ); + + /* Critical section - locked by a spinlock because this stuff can + * be changed from interrupt handlers. The stuff above is done + * outside the lock because it allocates memory. + */ + + /* Actually, it only looks like it can be called from interrupt + * context. The culprit is reactivate_fd, which calls + * maybe_sigio_broken, which calls write_sigio_workaround, + * which calls activate_fd. However, write_sigio_workaround should + * only be called once, at boot time. That would make it clear that + * this is called only from process context, and can be locked with + * a semaphore. + */ + flags = irq_lock(); + for(irq_fd = active_fds; irq_fd != NULL; irq_fd = irq_fd->next){ + if((irq_fd->fd == fd) && (irq_fd->type == type)){ + printk("Registering fd %d twice\n", fd); + printk("Irqs : %d, %d\n", irq_fd->irq, irq); + printk("Ids : 0x%x, 0x%x\n", irq_fd->id, dev_id); + goto out_unlock; + } + } + + n = pollfds_num; + if(n == pollfds_size){ + while(1){ + /* Here we have to drop the lock in order to call + * kmalloc, which might sleep. If something else + * came in and changed the pollfds array, we free + * the buffer and try again. + */ + irq_unlock(flags); + size = (pollfds_num + 1) * sizeof(pollfds[0]); + tmp_pfd = um_kmalloc(size); + flags = irq_lock(); + if(tmp_pfd == NULL) + goto out_unlock; + if(n == pollfds_size) + break; + kfree(tmp_pfd); + } + if(pollfds != NULL){ + memcpy(tmp_pfd, pollfds, + sizeof(pollfds[0]) * pollfds_size); + kfree(pollfds); + } + pollfds = tmp_pfd; + pollfds_size++; + } + + if(type == IRQ_WRITE) + fd = -1; + + pollfds[pollfds_num] = ((struct pollfd) { .fd = fd, + .events = events, + .revents = 0 }); + pollfds_num++; + + *last_irq_ptr = new_fd; + last_irq_ptr = &new_fd->next; + + irq_unlock(flags); + + /* This calls activate_fd, so it has to be outside the critical + * section. + */ + maybe_sigio_broken(fd, type); + + return(0); + + out_unlock: + irq_unlock(flags); + kfree(new_fd); + out: + return(err); +} + +static void free_irq_by_cb(int (*test)(struct irq_fd *, void *), void *arg) +{ + struct irq_fd **prev; + unsigned long flags; + int i = 0; + + flags = irq_lock(); + prev = &active_fds; + while(*prev != NULL){ + if((*test)(*prev, arg)){ + struct irq_fd *old_fd = *prev; + if((pollfds[i].fd != -1) && + (pollfds[i].fd != (*prev)->fd)){ + printk("free_irq_by_cb - mismatch between " + "active_fds and pollfds, fd %d vs %d\n", + (*prev)->fd, pollfds[i].fd); + goto out; + } + memcpy(&pollfds[i], &pollfds[i + 1], + (pollfds_num - i - 1) * sizeof(pollfds[0])); + pollfds_num--; + if(last_irq_ptr == &old_fd->next) + last_irq_ptr = prev; + *prev = (*prev)->next; + if(old_fd->type == IRQ_WRITE) + ignore_sigio_fd(old_fd->fd); + kfree(old_fd); + continue; + } + prev = &(*prev)->next; + i++; + } + out: + irq_unlock(flags); +} + +struct irq_and_dev { + int irq; + void *dev; +}; + +static int same_irq_and_dev(struct irq_fd *irq, void *d) +{ + struct irq_and_dev *data = d; + + return((irq->irq == data->irq) && (irq->id == data->dev)); +} + +void free_irq_by_irq_and_dev(unsigned int irq, void *dev) +{ + struct irq_and_dev data = ((struct irq_and_dev) { .irq = irq, + .dev = dev }); + + free_irq_by_cb(same_irq_and_dev, &data); +} + +static int same_fd(struct irq_fd *irq, void *fd) +{ + return(irq->fd == *((int *) fd)); +} + +void free_irq_by_fd(int fd) +{ + free_irq_by_cb(same_fd, &fd); +} + +static struct irq_fd *find_irq_by_fd(int fd, int irqnum, int *index_out) +{ + struct irq_fd *irq; + int i = 0; + + for(irq=active_fds; irq != NULL; irq = irq->next){ + if((irq->fd == fd) && (irq->irq == irqnum)) break; + i++; + } + if(irq == NULL){ + printk("find_irq_by_fd doesn't have descriptor %d\n", fd); + goto out; + } + if((pollfds[i].fd != -1) && (pollfds[i].fd != fd)){ + printk("find_irq_by_fd - mismatch between active_fds and " + "pollfds, fd %d vs %d, need %d\n", irq->fd, + pollfds[i].fd, fd); + irq = NULL; + goto out; + } + *index_out = i; + out: + return(irq); +} + +void free_irq_later(int irq, void *dev_id) +{ + struct irq_fd *irq_fd; + unsigned long flags; + + flags = irq_lock(); + for(irq_fd = active_fds; irq_fd != NULL; irq_fd = irq_fd->next){ + if((irq_fd->irq == irq) && (irq_fd->id == dev_id)) + break; + } + if(irq_fd == NULL){ + printk("free_irq_later found no irq, irq = %d, " + "dev_id = 0x%p\n", irq, dev_id); + goto out; + } + irq_fd->freed = 1; + out: + irq_unlock(flags); +} + +void reactivate_fd(int fd, int irqnum) +{ + struct irq_fd *irq; + unsigned long flags; + int i; + + flags = irq_lock(); + irq = find_irq_by_fd(fd, irqnum, &i); + if(irq == NULL){ + irq_unlock(flags); + return; + } + + pollfds[i].fd = irq->fd; + + irq_unlock(flags); + + /* This calls activate_fd, so it has to be outside the critical + * section. + */ + maybe_sigio_broken(fd, irq->type); +} + +void deactivate_fd(int fd, int irqnum) +{ + struct irq_fd *irq; + unsigned long flags; + int i; + + flags = irq_lock(); + irq = find_irq_by_fd(fd, irqnum, &i); + if(irq == NULL) + goto out; + pollfds[i].fd = -1; + out: + irq_unlock(flags); +} + +int deactivate_all_fds(void) +{ + struct irq_fd *irq; + int err; + + for(irq=active_fds;irq != NULL;irq = irq->next){ + err = os_clear_fd_async(irq->fd); + if(err) + return(err); + } + /* If there is a signal already queued, after unblocking ignore it */ |