Merge latest xen-unstable into xen-ia64-unstable

This commit is contained in:
djm@kirby.fc.hp.com committed 2005-09-22 11:42:01 -06:00
commit 46d993a5ca
235 files changed
+13465 -18367

No files matched your search

+8 -3
View File
@@ -86,6 +86,9 @@
^tools/check/\..*$
^tools/console/xenconsoled$
^tools/console/xenconsole$
^tools/debugger/gdb/gdb-6\.2\.1\.tar\.bz2$
^tools/debugger/gdb/gdb-6\.2\.1/.*$
^tools/debugger/gdb/gdb-6\.2\.1-linux-i386-xen/.*$
^tools/debugger/pdb/pdb$
^tools/debugger/pdb/linux-[0-9.]*-module/.*\.ko$
^tools/debugger/pdb/linux-[0-9.]*-module/.*\.mod.c$
@@ -136,9 +139,10 @@
^tools/vnet/vnet-module/\..*\.cmd$
^tools/vnet/vnet-module/\.tmp_versions/.*$
^tools/vnet/vnet-module/vnet_module\.mod\..*$
^tools/vtpm/vtpm*
^tools/vtpm/tpm_emulator-*
^tools/vtpm_manager/manager/vtpm_managerd
^tools/vtpm/tpm_emulator/.*$
^tools/vtpm/tpm_emulator-.*\.tar\.gz$
^tools/vtpm/vtpm/.*$
^tools/vtpm_manager/manager/vtpm_managerd$
^tools/xcutils/xc_restore$
^tools/xcutils/xc_save$
^tools/xenstat/xentop/xentop$
@@ -156,6 +160,7 @@
^tools/xenstore/xs_stress$
^tools/xenstore/xs_test$
^tools/xenstore/xs_watch_stress$
^tools/xentrace/xenctx$
^tools/xentrace/xentrace$
^xen/BLOG$
^xen/TAGS$
+18 -17
View File
@@ -98,12 +98,15 @@ clean::
$(MAKE) -C tools clean
$(MAKE) -C docs clean
# clean, but blow away kernel build tree plus tar balls
mrproper: clean
# clean, but blow away kernel build tree plus tarballs
distclean: clean
rm -rf dist patches/tmp
for i in $(ALLKERNELS) ; do $(MAKE) $$i-delete ; done
for i in $(ALLSPARSETREES) ; do $(MAKE) $$i-mrproper ; done
# Linux name for GNU distclean
mrproper: distclean
install-logging: LOGGING=logging-0.4.9.2
install-logging:
[ -f $(LOGGING).tar.gz ] || wget http://www.red-dove.com/$(LOGGING).tar.gz
@@ -142,7 +145,7 @@ help:
@echo 'Cleaning targets:'
@echo ' clean - clean the Xen, tools and docs (but not'
@echo ' guest kernel) trees'
@echo ' mrproper - clean plus delete kernel tarballs and kernel'
@echo ' distclean - clean plus delete kernel tarballs and kernel'
@echo ' build trees'
@echo ' kdelete - delete guest kernel build trees'
@echo ' kclean - clean guest kernel build trees'
@@ -163,27 +166,25 @@ uninstall: D=$(DESTDIR)
uninstall:
[ -d $(D)/etc/xen ] && mv -f $(D)/etc/xen $(D)/etc/xen.old-`date +%s`
rm -rf $(D)/etc/init.d/xend*
rm -rf $(D)/usr/$(LIBDIR)/libxc* $(D)/usr/$(LIBDIR)/libxutil*
rm -rf $(D)/usr/$(LIBDIR)/python/xen $(D)/usr/include/xen
rm -rf $(D)/usr/$(LIBDIR)/share/xen $(D)/usr/$(LIBDIR)/libxenstore*
rm -rf $(D)/etc/hotplug/xen-backend.agent
rm -rf $(D)/var/run/xen* $(D)/var/lib/xen*
rm -rf $(D)/usr/include/xcs_proto.h $(D)/usr/include/xc.h
rm -rf $(D)/usr/include/xs_lib.h $(D)/usr/include/xs.h
rm -rf $(D)/usr/sbin/xcs $(D)/usr/sbin/xcsdump $(D)/usr/sbin/xen*
rm -rf $(D)/usr/sbin/netfix
rm -rf $(D)/usr/sbin/xfrd $(D)/usr/sbin/xm
rm -rf $(D)/usr/share/doc/xen $(D)/usr/man/man*/xentrace*
rm -rf $(D)/usr/bin/xen* $(D)/usr/bin/miniterm
rm -rf $(D)/boot/*xen*
rm -rf $(D)/lib/modules/*xen*
rm -rf $(D)/usr/bin/xen* $(D)/usr/bin/lomount
rm -rf $(D)/usr/bin/cpuperf-perfcntr $(D)/usr/bin/cpuperf-xen
rm -rf $(D)/usr/bin/xc_shadow
rm -rf $(D)/usr/share/xen $(D)/usr/libexec/xen
rm -rf $(D)/usr/include/xenctrl.h
rm -rf $(D)/usr/include/xs_lib.h $(D)/usr/include/xs.h
rm -rf $(D)/usr/include/xen
rm -rf $(D)/usr/$(LIBDIR)/libxenctrl* $(D)/usr/$(LIBDIR)/libxenguest*
rm -rf $(D)/usr/$(LIBDIR)/libxenstore*
rm -rf $(D)/usr/$(LIBDIR)/python/xen $(D)/usr/$(LIBDIR)/xen
rm -rf $(D)/usr/libexec/xen
rm -rf $(D)/usr/sbin/xen* $(D)/usr/sbin/netfix $(D)/usr/sbin/xm
rm -rf $(D)/usr/share/doc/xen
rm -rf $(D)/usr/share/xen
rm -rf $(D)/usr/share/man/man1/xen*
rm -rf $(D)/usr/share/man/man8/xen*
rm -rf $(D)/usr/lib/xen
rm -rf $(D)/etc/hotplug.d/xen-backend
rm -rf $(D)/etc/hotplug/xen-backend.agent
# Legacy targets for compatibility
linux24:
+4 -3
View File
@@ -12,7 +12,7 @@ DOXYGEN := doxygen
pkgdocdir := /usr/share/doc/xen
DOC_TEX := $(wildcard src/*.tex)
DOC_TEX := src/user.tex src/interface.tex
DOC_PS := $(patsubst src/%.tex,ps/%.ps,$(DOC_TEX))
DOC_PDF := $(patsubst src/%.tex,pdf/%.pdf,$(DOC_TEX))
DOC_HTML := $(patsubst src/%.tex,html/%/index.html,$(DOC_TEX))
@@ -36,11 +36,12 @@ html:
$(MAKE) $(DOC_HTML); fi
python-dev-docs:
mkdir -p api/tools/python
@mkdir -v -p api/tools/python
@if which $(DOXYGEN) 1>/dev/null 2>/dev/null; then \
echo "Running doxygen to generate Python tools APIs ... "; \
$(DOXYGEN) Doxyfile; \
$(MAKE) -C api/tools/python/latex ; fi
$(MAKE) -C api/tools/python/latex ; else \
echo "Doxygen not installed; skipping python-dev-docs."; fi
clean:
rm -rf .word_count *.aux *.dvi *.bbl *.blg *.glo *.idx *~
+16 -1412
View File
File diff suppressed because it is too large. Load diff
+140
View File
@@ -0,0 +1,140 @@
\chapter{Virtual Architecture}
On a Xen-based system, the hypervisor itself runs in {\it ring 0}. It
has full access to the physical memory available in the system and is
responsible for allocating portions of it to the domains. Guest
operating systems run in and use {\it rings 1}, {\it 2} and {\it 3} as
they see fit. Segmentation is used to prevent the guest OS from
accessing the portion of the address space that is reserved for Xen.
We expect most guest operating systems will use ring 1 for their own
operation and place applications in ring 3.
In this chapter we consider the basic virtual architecture provided by
Xen: the basic CPU state, exception and interrupt handling, and time.
Other aspects such as memory and device access are discussed in later
chapters.
\section{CPU state}
All privileged state must be handled by Xen. The guest OS has no
direct access to CR3 and is not permitted to update privileged bits in
EFLAGS. Guest OSes use \emph{hypercalls} to invoke operations in Xen;
these are analogous to system calls but occur from ring 1 to ring 0.
A list of all hypercalls is given in Appendix~\ref{a:hypercalls}.
\section{Exceptions}
A virtual IDT is provided --- a domain can submit a table of trap
handlers to Xen via the {\tt set\_trap\_table()} hypercall. Most trap
handlers are identical to native x86 handlers, although the page-fault
handler is somewhat different.
\section{Interrupts and events}
Interrupts are virtualized by mapping them to \emph{events}, which are
delivered asynchronously to the target domain using a callback
supplied via the {\tt set\_callbacks()} hypercall. A guest OS can map
these events onto its standard interrupt dispatch mechanisms. Xen is
responsible for determining the target domain that will handle each
physical interrupt source. For more details on the binding of event
sources to events, see Chapter~\ref{c:devices}.
\section{Time}
Guest operating systems need to be aware of the passage of both real
(or wallclock) time and their own `virtual time' (the time for which
they have been executing). Furthermore, Xen has a notion of time which
is used for scheduling. The following notions of time are provided:
\begin{description}
\item[Cycle counter time.]
This provides a fine-grained time reference. The cycle counter time
is used to accurately extrapolate the other time references. On SMP
machines it is currently assumed that the cycle counter time is
synchronized between CPUs. The current x86-based implementation
achieves this within inter-CPU communication latencies.
\item[System time.]
This is a 64-bit counter which holds the number of nanoseconds that
have elapsed since system boot.
\item[Wall clock time.]
This is the time of day in a Unix-style {\tt struct timeval}
(seconds and microseconds since 1 January 1970, adjusted by leap
seconds). An NTP client hosted by {\it domain 0} can keep this
value accurate.
\item[Domain virtual time.]
This progresses at the same pace as system time, but only while a
domain is executing --- it stops while a domain is de-scheduled.
Therefore the share of the CPU that a domain receives is indicated
by the rate at which its virtual time increases.
\end{description}
Xen exports timestamps for system time and wall-clock time to guest
operating systems through a shared page of memory. Xen also provides
the cycle counter time at the instant the timestamps were calculated,
and the CPU frequency in Hertz. This allows the guest to extrapolate
system and wall-clock times accurately based on the current cycle
counter time.
Since all time stamps need to be updated and read \emph{atomically}
two version numbers are also stored in the shared info page. The first
is incremented prior to an update, while the second is only
incremented afterwards. Thus a guest can be sure that it read a
consistent state by checking the two version numbers are equal.
Xen includes a periodic ticker which sends a timer event to the
currently executing domain every 10ms. The Xen scheduler also sends a
timer event whenever a domain is scheduled; this allows the guest OS
to adjust for the time that has passed while it has been inactive. In
addition, Xen allows each domain to request that they receive a timer
event sent at a specified system time by using the {\tt
set\_timer\_op()} hypercall. Guest OSes may use this timer to
implement timeout values when they block.
%% % akw: demoting this to a section -- not sure if there is any point
%% % though, maybe just remove it.
\section{Xen CPU Scheduling}
Xen offers a uniform API for CPU schedulers. It is possible to choose
from a number of schedulers at boot and it should be easy to add more.
The BVT, Atropos and Round Robin schedulers are part of the normal Xen
distribution. BVT provides proportional fair shares of the CPU to the
running domains. Atropos can be used to reserve absolute shares of
the CPU for each domain. Round-robin is provided as an example of
Xen's internal scheduler API.
\paragraph*{Note: SMP host support}
Xen has always supported SMP host systems. Domains are statically
assigned to CPUs, either at creation time or when manually pinning to
a particular CPU. The current schedulers then run locally on each CPU
to decide which of the assigned domains should be run there. The
user-level control software can be used to perform coarse-grain
load-balancing between CPUs.
%% More information on the characteristics and use of these schedulers
%% is available in {\tt Sched-HOWTO.txt}.
\section{Privileged operations}
Xen exports an extended interface to privileged domains (viz.\ {\it
Domain 0}). This allows such domains to build and boot other domains
on the server, and provides control interfaces for managing
scheduling, memory, networking, and block devices.
+62
View File
@@ -0,0 +1,62 @@
\chapter{Debugging}
Xen provides tools for debugging both Xen and guest OSes. Currently, the
Pervasive Debugger provides a GDB stub, which provides facilities for symbolic
debugging of Xen itself and of OS kernels running on top of Xen. The Trace
Buffer provides a lightweight means to log data about Xen's internal state and
behaviour at runtime, for later analysis.
\section{Pervasive Debugger}
Information on using the pervasive debugger is available in pdb.txt.
\section{Trace Buffer}
The trace buffer provides a means to observe Xen's operation from domain 0.
Trace events, inserted at key points in Xen's code, record data that can be
read by the {\tt xentrace} tool. Recording these events has a low overhead
and hence the trace buffer may be useful for debugging timing-sensitive
behaviours.
\subsection{Internal API}
To use the trace buffer functionality from within Xen, you must {\tt \#include
<xen/trace.h>}, which contains definitions related to the trace buffer. Trace
events are inserted into the buffer using the {\tt TRACE\_xD} ({\tt x} = 0, 1,
2, 3, 4 or 5) macros. These all take an event number, plus {\tt x} additional
(32-bit) data as their arguments. For trace buffer-enabled builds of Xen these
will insert the event ID and data into the trace buffer, along with the current
value of the CPU cycle-counter. For builds without the trace buffer enabled,
the macros expand to no-ops and thus can be left in place without incurring
overheads.
\subsection{Trace-enabled builds}
By default, the trace buffer is enabled only in debug builds (i.e. {\tt NDEBUG}
is not defined). It can be enabled separately by defining {\tt TRACE\_BUFFER},
either in {\tt <xen/config.h>} or on the gcc command line.
The size (in pages) of the per-CPU trace buffers can be specified using the
{\tt tbuf\_size=n } boot parameter to Xen. If the size is set to 0, the trace
buffers will be disabled.
\subsection{Dumping trace data}
When running a trace buffer build of Xen, trace data are written continuously
into the buffer data areas, with newer data overwriting older data. This data
can be captured using the {\tt xentrace} program in domain 0.
The {\tt xentrace} tool uses {\tt /dev/mem} in domain 0 to map the trace
buffers into its address space. It then periodically polls all the buffers for
new data, dumping out any new records from each buffer in turn. As a result,
for machines with multiple (logical) CPUs, the trace buffer output will not be
in overall chronological order.
The output from {\tt xentrace} can be post-processed using {\tt
xentrace\_cpusplit} (used to split trace data out into per-cpu log files) and
{\tt xentrace\_format} (used to pretty-print trace data). For the predefined
trace points, there is an example format file in {\tt tools/xentrace/formats }.
For more information, see the manual pages for {\tt xentrace}, {\tt
xentrace\_format} and {\tt xentrace\_cpusplit}.
+178
View File
@@ -0,0 +1,178 @@
\chapter{Devices}
\label{c:devices}
Devices such as network and disk are exported to guests using a split
device driver. The device driver domain, which accesses the physical
device directly also runs a \emph{backend} driver, serving requests to
that device from guests. Each guest will use a simple \emph{frontend}
driver, to access the backend. Communication between these domains is
composed of two parts: First, data is placed onto a shared memory page
between the domains. Second, an event channel between the two domains
is used to pass notification that data is outstanding. This
separation of notification from data transfer allows message batching,
and results in very efficient device access.
Event channels are used extensively in device virtualization; each
domain has a number of end-points or \emph{ports} each of which may be
bound to one of the following \emph{event sources}:
\begin{itemize}
\item a physical interrupt from a real device,
\item a virtual interrupt (callback) from Xen, or
\item a signal from another domain
\end{itemize}
Events are lightweight and do not carry much information beyond the
source of the notification. Hence when performing bulk data transfer,
events are typically used as synchronization primitives over a shared
memory transport. Event channels are managed via the {\tt
event\_channel\_op()} hypercall; for more details see
Section~\ref{s:idc}.
This chapter focuses on some individual device interfaces available to
Xen guests.
\section{Network I/O}
Virtual network device services are provided by shared memory
communication with a backend domain. From the point of view of other
domains, the backend may be viewed as a virtual ethernet switch
element with each domain having one or more virtual network interfaces
connected to it.
\subsection{Backend Packet Handling}
The backend driver is responsible for a variety of actions relating to
the transmission and reception of packets from the physical device.
With regard to transmission, the backend performs these key actions:
\begin{itemize}
\item {\bf Validation:} To ensure that domains do not attempt to
generate invalid (e.g. spoofed) traffic, the backend driver may
validate headers ensuring that source MAC and IP addresses match the
interface that they have been sent from.
Validation functions can be configured using standard firewall rules
({\small{\tt iptables}} in the case of Linux).
\item {\bf Scheduling:} Since a number of domains can share a single
physical network interface, the backend must mediate access when
several domains each have packets queued for transmission. This
general scheduling function subsumes basic shaping or rate-limiting
schemes.
\item {\bf Logging and Accounting:} The backend domain can be
configured with classifier rules that control how packets are
accounted or logged. For example, log messages might be generated
whenever a domain attempts to send a TCP packet containing a SYN.
\end{itemize}
On receipt of incoming packets, the backend acts as a simple
demultiplexer: Packets are passed to the appropriate virtual interface
after any necessary logging and accounting have been carried out.
\subsection{Data Transfer}
Each virtual interface uses two ``descriptor rings'', one for
transmit, the other for receive. Each descriptor identifies a block
of contiguous physical memory allocated to the domain.
The transmit ring carries packets to transmit from the guest to the
backend domain. The return path of the transmit ring carries messages
indicating that the contents have been physically transmitted and the
backend no longer requires the associated pages of memory.
To receive packets, the guest places descriptors of unused pages on
the receive ring. The backend will return received packets by
exchanging these pages in the domain's memory with new pages
containing the received data, and passing back descriptors regarding
the new packets on the ring. This zero-copy approach allows the
backend to maintain a pool of free pages to receive packets into, and
then deliver them to appropriate domains after examining their
headers.
% Real physical addresses are used throughout, with the domain
% performing translation from pseudo-physical addresses if that is
% necessary.
If a domain does not keep its receive ring stocked with empty buffers
then packets destined to it may be dropped. This provides some
defence against receive livelock problems because an overload domain
will cease to receive further data. Similarly, on the transmit path,
it provides the application with feedback on the rate at which packets
are able to leave the system.
Flow control on rings is achieved by including a pair of producer
indexes on the shared ring page. Each side will maintain a private
consumer index indicating the next outstanding message. In this
manner, the domains cooperate to divide the ring into two message
lists, one in each direction. Notification is decoupled from the
immediate placement of new messages on the ring; the event channel
will be used to generate notification when {\em either} a certain
number of outstanding messages are queued, {\em or} a specified number
of nanoseconds have elapsed since the oldest message was placed on the
ring.
%% Not sure if my version is any better -- here is what was here
%% before: Synchronization between the backend domain and the guest is
%% achieved using counters held in shared memory that is accessible to
%% both. Each ring has associated producer and consumer indices
%% indicating the area in the ring that holds descriptors that contain
%% data. After receiving {\it n} packets or {\t nanoseconds} after
%% receiving the first packet, the hypervisor sends an event to the
%% domain.
\section{Block I/O}
All guest OS disk access goes through the virtual block device VBD
interface. This interface allows domains access to portions of block
storage devices visible to the the block backend device. The VBD
interface is a split driver, similar to the network interface
described above. A single shared memory ring is used between the
frontend and backend drivers, across which read and write messages are
sent.
Any block device accessible to the backend domain, including
network-based block (iSCSI, *NBD, etc), loopback and LVM/MD devices,
can be exported as a VBD. Each VBD is mapped to a device node in the
guest, specified in the guest's startup configuration.
Old (Xen 1.2) virtual disks are not supported under Xen 2.0, since
similar functionality can be achieved using the more complete LVM
system, which is already in widespread use.
\subsection{Data Transfer}
The single ring between the guest and the block backend supports three
messages:
\begin{description}
\item [{\small {\tt PROBE}}:] Return a list of the VBDs available to
this guest from the backend. The request includes a descriptor of a
free page into which the reply will be written by the backend.
\item [{\small {\tt READ}}:] Read data from the specified block
device. The front end identifies the device and location to read
from and attaches pages for the data to be copied to (typically via
DMA from the device). The backend acknowledges completed read
requests as they finish.
\item [{\small {\tt WRITE}}:] Write data to the specified block
device. This functions essentially as {\small {\tt READ}}, except
that the data moves to the device instead of from it.
\end{description}
%% um... some old text: In overview, the same style of descriptor-ring
%% that is used for network packets is used here. Each domain has one
%% ring that carries operation requests to the hypervisor and carries
%% the results back again.
%% Rather than copying data, the backend simply maps the domain's
%% buffers in order to enable direct DMA to them. The act of mapping
%% the buffers also increases the reference counts of the underlying
%% pages, so that the unprivileged domain cannot try to return them to
%% the hypervisor, install them as page tables, or any other unsafe
%% behaviour.
%%
%% % block API here
+49
View File
@@ -0,0 +1,49 @@
\chapter{Further Information}
If you have questions that are not answered by this manual, the
sources of information listed below may be of interest to you. Note
that bug reports, suggestions and contributions related to the
software (or the documentation) should be sent to the Xen developers'
mailing list (address below).
\section{Other documentation}
If you are mainly interested in using (rather than developing for)
Xen, the \emph{Xen Users' Manual} is distributed in the {\tt docs/}
directory of the Xen source distribution.
% Various HOWTOs are also available in {\tt docs/HOWTOS}.
\section{Online references}
The official Xen web site is found at:
\begin{quote}
{\tt http://www.cl.cam.ac.uk/Research/SRG/netos/xen/}
\end{quote}
This contains links to the latest versions of all on-line
documentation.
\section{Mailing lists}
There are currently four official Xen mailing lists:
\begin{description}
\item[xen-devel@lists.xensource.com] Used for development
discussions and bug reports. Subscribe at: \\
{\small {\tt http://lists.xensource.com/xen-devel}}
\item[xen-users@lists.xensource.com] Used for installation and usage
discussions and requests for help. Subscribe at: \\
{\small {\tt http://lists.xensource.com/xen-users}}
\item[xen-announce@lists.xensource.com] Used for announcements only.
Subscribe at: \\
{\small {\tt http://lists.xensource.com/xen-announce}}
\item[xen-changelog@lists.xensource.com] Changelog feed
from the unstable and 2.0 trees - developer oriented. Subscribe at: \\
{\small {\tt http://lists.xensource.com/xen-changelog}}
\end{description}
Of these, xen-devel is the most active.
+524
View File
@@ -0,0 +1,524 @@
\newcommand{\hypercall}[1]{\vspace{2mm}{\sf #1}}
\chapter{Xen Hypercalls}
\label{a:hypercalls}
Hypercalls represent the procedural interface to Xen; this appendix
categorizes and describes the current set of hypercalls.
\section{Invoking Hypercalls}
Hypercalls are invoked in a manner analogous to system calls in a
conventional operating system; a software interrupt is issued which
vectors to an entry point within Xen. On x86\_32 machines the
instruction required is {\tt int \$82}; the (real) IDT is setup so
that this may only be issued from within ring 1. The particular
hypercall to be invoked is contained in {\tt EAX} --- a list
mapping these values to symbolic hypercall names can be found
in {\tt xen/include/public/xen.h}.
On some occasions a set of hypercalls will be required to carry
out a higher-level function; a good example is when a guest
operating wishes to context switch to a new process which
requires updating various privileged CPU state. As an optimization
for these cases, there is a generic mechanism to issue a set of
hypercalls as a batch:
\begin{quote}
\hypercall{multicall(void *call\_list, int nr\_calls)}
Execute a series of hypervisor calls; {\tt nr\_calls} is the length of
the array of {\tt multicall\_entry\_t} structures pointed to be {\tt
call\_list}. Each entry contains the hypercall operation code followed
by up to 7 word-sized arguments.
\end{quote}
Note that multicalls are provided purely as an optimization; there is
no requirement to use them when first porting a guest operating
system.
\section{Virtual CPU Setup}
At start of day, a guest operating system needs to setup the virtual
CPU it is executing on. This includes installing vectors for the
virtual IDT so that the guest OS can handle interrupts, page faults,
etc. However the very first thing a guest OS must setup is a pair
of hypervisor callbacks: these are the entry points which Xen will
use when it wishes to notify the guest OS of an occurrence.
\begin{quote}
\hypercall{set\_callbacks(unsigned long event\_selector, unsigned long
event\_address, unsigned long failsafe\_selector, unsigned long
failsafe\_address) }
Register the normal (``event'') and failsafe callbacks for
event processing. In each case the code segment selector and
address within that segment are provided. The selectors must
have RPL 1; in XenLinux we simply use the kernel's CS for both
{\tt event\_selector} and {\tt failsafe\_selector}.
The value {\tt event\_address} specifies the address of the guest OSes
event handling and dispatch routine; the {\tt failsafe\_address}
specifies a separate entry point which is used only if a fault occurs
when Xen attempts to use the normal callback.
\end{quote}
After installing the hypervisor callbacks, the guest OS can
install a `virtual IDT' by using the following hypercall:
\begin{quote}
\hypercall{set\_trap\_table(trap\_info\_t *table)}
Install one or more entries into the per-domain
trap handler table (essentially a software version of the IDT).
Each entry in the array pointed to by {\tt table} includes the
exception vector number with the corresponding segment selector
and entry point. Most guest OSes can use the same handlers on
Xen as when running on the real hardware; an exception is the
page fault handler (exception vector 14) where a modified
stack-frame layout is used.
\end{quote}
\section{Scheduling and Timer}
Domains are preemptively scheduled by Xen according to the
parameters installed by domain 0 (see Section~\ref{s:dom0ops}).
In addition, however, a domain may choose to explicitly
control certain behavior with the following hypercall:
\begin{quote}
\hypercall{sched\_op(unsigned long op)}
Request scheduling operation from hypervisor. The options are: {\it
yield}, {\it block}, and {\it shutdown}. {\it yield} keeps the
calling domain runnable but may cause a reschedule if other domains
are runnable. {\it block} removes the calling domain from the run
queue and cause is to sleeps until an event is delivered to it. {\it
shutdown} is used to end the domain's execution; the caller can
additionally specify whether the domain should reboot, halt or
suspend.
\end{quote}
To aid the implementation of a process scheduler within a guest OS,
Xen provides a virtual programmable timer:
\begin{quote}
\hypercall{set\_timer\_op(uint64\_t timeout)}
Request a timer event to be sent at the specified system time (time
in nanoseconds since system boot). The hypercall actually passes the
64-bit timeout value as a pair of 32-bit values.
\end{quote}
Note that calling {\tt set\_timer\_op()} prior to {\tt sched\_op}
allows block-with-timeout semantics.
\section{Page Table Management}
Since guest operating systems have read-only access to their page
tables, Xen must be involved when making any changes. The following
multi-purpose hypercall can be used to modify page-table entries,
update the machine-to-physical mapping table, flush the TLB, install
a new page-table base pointer, and more.
\begin{quote}
\hypercall{mmu\_update(mmu\_update\_t *req, int count, int *success\_count)}
Update the page table for the domain; a set of {\tt count} updates are
submitted for processing in a batch, with {\tt success\_count} being
updated to report the number of successful updates.
Each element of {\tt req[]} contains a pointer (address) and value;
the least significant 2-bits of the pointer are used to distinguish
the type of update requested as follows:
\begin{description}
\item[\it MMU\_NORMAL\_PT\_UPDATE:] update a page directory entry or
page table entry to the associated value; Xen will check that the
update is safe, as described in Chapter~\ref{c:memory}.
\item[\it MMU\_MACHPHYS\_UPDATE:] update an entry in the
machine-to-physical table. The calling domain must own the machine
page in question (or be privileged).
\item[\it MMU\_EXTENDED\_COMMAND:] perform additional MMU operations.
The set of additional MMU operations is considerable, and includes
updating {\tt cr3} (or just re-installing it for a TLB flush),
flushing the cache, installing a new LDT, or pinning \& unpinning
page-table pages (to ensure their reference count doesn't drop to zero
which would require a revalidation of all entries).
Further extended commands are used to deal with granting and
acquiring page ownership; see Section~\ref{s:idc}.
\end{description}
More details on the precise format of all commands can be
found in {\tt xen/include/public/xen.h}.
\end{quote}
Explicitly updating batches of page table entries is extremely
efficient, but can require a number of alterations to the guest
OS. Using the writable page table mode (Chapter~\ref{c:memory}) is
recommended for new OS ports.
Regardless of which page table update mode is being used, however,
there are some occasions (notably handling a demand page fault) where
a guest OS will wish to modify exactly one PTE rather than a
batch. This is catered for by the following:
\begin{quote}
\hypercall{update\_va\_mapping(unsigned long page\_nr, unsigned long
val, \\ unsigned long flags)}
Update the currently installed PTE for the page {\tt page\_nr} to
{\tt val}. As with {\tt mmu\_update()}, Xen checks the modification
is safe before applying it. The {\tt flags} determine which kind
of TLB flush, if any, should follow the update.
\end{quote}
Finally, sufficiently privileged domains may occasionally wish to manipulate
the pages of others:
\begin{quote}
\hypercall{update\_va\_mapping\_otherdomain(unsigned long page\_nr,
unsigned long val, unsigned long flags, uint16\_t domid)}
Identical to {\tt update\_va\_mapping()} save that the pages being
mapped must belong to the domain {\tt domid}.
\end{quote}
This privileged operation is currently used by backend virtual device
drivers to safely map pages containing I/O data.
\section{Segmentation Support}
Xen allows guest OSes to install a custom GDT if they require it;
this is context switched transparently whenever a domain is
[de]scheduled. The following hypercall is effectively a
`safe' version of {\tt lgdt}:
\begin{quote}
\hypercall{set\_gdt(unsigned long *frame\_list, int entries)}
Install a global descriptor table for a domain; {\tt frame\_list} is
an array of up to 16 machine page frames within which the GDT resides,
with {\tt entries} being the actual number of descriptor-entry
slots. All page frames must be mapped read-only within the guest's
address space, and the table must be large enough to contain Xen's
reserved entries (see {\tt xen/include/public/arch-x86\_32.h}).
\end{quote}
Many guest OSes will also wish to install LDTs; this is achieved by
using {\tt mmu\_update()} with an extended command, passing the
linear address of the LDT base along with the number of entries. No
special safety checks are required; Xen needs to perform this task
simply since {\tt lldt} requires CPL 0.
Xen also allows guest operating systems to update just an
individual segment descriptor in the GDT or LDT:
\begin{quote}
\hypercall{update\_descriptor(unsigned long ma, unsigned long word1,
unsigned long word2)}
Update the GDT/LDT entry at machine address {\tt ma}; the new
8-byte descriptor is stored in {\tt word1} and {\tt word2}.
Xen performs a number of checks to ensure the descriptor is
valid.
\end{quote}
Guest OSes can use the above in place of context switching entire
LDTs (or the GDT) when the number of changing descriptors is small.
\section{Context Switching}
When a guest OS wishes to context switch between two processes,
it can use the page table and segmentation hypercalls described
above to perform the the bulk of the privileged work. In addition,
however, it will need to invoke Xen to switch the kernel (ring 1)
stack pointer:
\begin{quote}
\hypercall{stack\_switch(unsigned long ss, unsigned long esp)}
Request kernel stack switch from hypervisor; {\tt ss} is the new
stack segment, which {\tt esp} is the new stack pointer.
\end{quote}
A final useful hypercall for context switching allows ``lazy''
save and restore of floating point state:
\begin{quote}
\hypercall{fpu\_taskswitch(void)}
This call instructs Xen to set the {\tt TS} bit in the {\tt cr0}
control register; this means that the next attempt to use floating
point will cause a trap which the guest OS can trap. Typically it will
then save/restore the FP state, and clear the {\tt TS} bit.
\end{quote}
This is provided as an optimization only; guest OSes can also choose
to save and restore FP state on all context switches for simplicity.
\section{Physical Memory Management}
As mentioned previously, each domain has a maximum and current
memory allocation. The maximum allocation, set at domain creation
time, cannot be modified. However a domain can choose to reduce
and subsequently grow its current allocation by using the
following call:
\begin{quote}
\hypercall{dom\_mem\_op(unsigned int op, unsigned long *extent\_list,
unsigned long nr\_extents, unsigned int extent\_order)}
Increase or decrease current memory allocation (as determined by
the value of {\tt op}). Each invocation provides a list of
extents each of which is $2^s$ pages in size,
where $s$ is the value of {\tt extent\_order}.
\end{quote}
In addition to simply reducing or increasing the current memory
allocation via a `balloon driver', this call is also useful for
obtaining contiguous regions of machine memory when required (e.g.
for certain PCI devices, or if using superpages).
\section{Inter-Domain Communication}
\label{s:idc}
Xen provides a simple asynchronous notification mechanism via
\emph{event channels}. Each domain has a set of end-points (or
\emph{ports}) which may be bound to an event source (e.g. a physical
IRQ, a virtual IRQ, or an port in another domain). When a pair of
end-points in two different domains are bound together, then a `send'
operation on one will cause an event to be received by the destination
domain.
The control and use of event channels involves the following hypercall:
\begin{quote}
\hypercall{event\_channel\_op(evtchn\_op\_t *op)}
Inter-domain event-channel management; {\tt op} is a discriminated
union which allows the following 7 operations:
\begin{description}
\item[\it alloc\_unbound:] allocate a free (unbound) local
port and prepare for connection from a specified domain.
\item[\it bind\_virq:] bind a local port to a virtual
IRQ; any particular VIRQ can be bound to at most one port per domain.
\item[\it bind\_pirq:] bind a local port to a physical IRQ;
once more, a given pIRQ can be bound to at most one port per
domain. Furthermore the calling domain must be sufficiently
privileged.
\item[\it bind\_interdomain:] construct an interdomain event
channel; in general, the target domain must have previously allocated
an unbound port for this channel, although this can be bypassed by
privileged domains during domain setup.
\item[\it close:] close an interdomain event channel.
\item[\it send:] send an event to the remote end of a
interdomain event channel.
\item[\it status:] determine the current status of a local port.
\end{description}
For more details see
{\tt xen/include/public/event\_channel.h}.
\end{quote}
Event channels are the fundamental communication primitive between
Xen domains and seamlessly support SMP. However they provide little
bandwidth for communication {\sl per se}, and hence are typically
married with a piece of shared memory to produce effective and
high-performance inter-domain communication.
Safe sharing of memory pages between guest OSes is carried out by
granting access on a per page basis to individual domains. This is
achieved by using the {\tt grant\_table\_op()} hypercall.
\begin{quote}
\hypercall{grant\_table\_op(unsigned int cmd, void *uop, unsigned int count)}
Grant or remove access to a particular page to a particular domain.
\end{quote}
This is not currently widely in use by guest operating systems, but
we intend to integrate support more fully in the near future.
\section{PCI Configuration}
Domains with physical device access (i.e.\ driver domains) receive
limited access to certain PCI devices (bus address space and
interrupts). However many guest operating systems attempt to
determine the PCI configuration by directly access the PCI BIOS,
which cannot be allowed for safety.
Instead, Xen provides the following hypercall:
\begin{quote}
\hypercall{physdev\_op(void *physdev\_op)}
Perform a PCI configuration option; depending on the value
of {\tt physdev\_op} this can be a PCI config read, a PCI config
write, or a small number of other queries.
\end{quote}
For examples of using {\tt physdev\_op()}, see the
Xen-specific PCI code in the linux sparse tree.
\section{Administrative Operations}
\label{s:dom0ops}
A large number of control operations are available to a sufficiently
privileged domain (typically domain 0). These allow the creation and
management of new domains, for example. A complete list is given
below: for more details on any or all of these, please see
{\tt xen/include/public/dom0\_ops.h}
\begin{quote}
\hypercall{dom0\_op(dom0\_op\_t *op)}
Administrative domain operations for domain management. The options are:
\begin{description}
\item [\it DOM0\_CREATEDOMAIN:] create a new domain
\item [\it DOM0\_PAUSEDOMAIN:] remove a domain from the scheduler run
queue.
\item [\it DOM0\_UNPAUSEDOMAIN:] mark a paused domain as schedulable
once again.
\item [\it DOM0\_DESTROYDOMAIN:] deallocate all resources associated
with a domain
\item [\it DOM0\_GETMEMLIST:] get list of pages used by the domain
\item [\it DOM0\_SCHEDCTL:]
\item [\it DOM0\_ADJUSTDOM:] adjust scheduling priorities for domain
\item [\it DOM0\_BUILDDOMAIN:] do final guest OS setup for domain
\item [\it DOM0\_GETDOMAINFO:] get statistics about the domain
\item [\it DOM0\_GETPAGEFRAMEINFO:]
\item [\it DOM0\_GETPAGEFRAMEINFO2:]
\item [\it DOM0\_IOPL:] set I/O privilege level
\item [\it DOM0\_MSR:] read or write model specific registers
\item [\it DOM0\_DEBUG:] interactively invoke the debugger
\item [\it DOM0\_SETTIME:] set system time
\item [\it DOM0\_READCONSOLE:] read console content from hypervisor buffer ring
\item [\it DOM0\_PINCPUDOMAIN:] pin domain to a particular CPU
\item [\it DOM0\_GETTBUFS:] get information about the size and location of
the trace buffers (only on trace-buffer enabled builds)
\item [\it DOM0\_PHYSINFO:] get information about the host machine
\item [\it DOM0\_PCIDEV\_ACCESS:] modify PCI device access permissions
\item [\it DOM0\_SCHED\_ID:] get the ID of the current Xen scheduler
\item [\it DOM0\_SHADOW\_CONTROL:] switch between shadow page-table modes
\item [\it DOM0\_SETDOMAININITIALMEM:] set initial memory allocation of a domain
\item [\it DOM0\_SETDOMAINMAXMEM:] set maximum memory allocation of a domain
\item [\it DOM0\_SETDOMAINVMASSIST:] set domain VM assist options
\end{description}
\end{quote}
Most of the above are best understood by looking at the code
implementing them (in {\tt xen/common/dom0\_ops.c}) and in
the user-space tools that use them (mostly in {\tt tools/libxc}).
\section{Debugging Hypercalls}
A few additional hypercalls are mainly useful for debugging:
\begin{quote}
\hypercall{console\_io(int cmd, int count, char *str)}
Use Xen to interact with the console; operations are:
{\it CONSOLEIO\_write}: Output count characters from buffer str.
{\it CONSOLEIO\_read}: Input at most count characters into buffer str.
\end{quote}
A pair of hypercalls allows access to the underlying debug registers:
\begin{quote}
\hypercall{set\_debugreg(int reg, unsigned long value)}
Set debug register {\tt reg} to {\tt value}
\hypercall{get\_debugreg(int reg)}
Return the contents of the debug register {\tt reg}
\end{quote}
And finally:
\begin{quote}
\hypercall{xen\_version(int cmd)}
Request Xen version number.
\end{quote}
This is useful to ensure that user-space tools are in sync
with the underlying hypervisor.
\section{Deprecated Hypercalls}
Xen is under constant development and refinement; as such there
are plans to improve the way in which various pieces of functionality
are exposed to guest OSes.
\begin{quote}
\hypercall{vm\_assist(unsigned int cmd, unsigned int type)}
Toggle various memory management modes (in particular wrritable page
tables and superpage support).
\end{quote}
This is likely to be replaced with mode values in the shared
information page since this is more resilient for resumption
after migration or checkpoint.
+162
View File
@@ -0,0 +1,162 @@
\chapter{Memory}
\label{c:memory}
Xen is responsible for managing the allocation of physical memory to
domains, and for ensuring safe use of the paging and segmentation
hardware.
\section{Memory Allocation}
Xen resides within a small fixed portion of physical memory; it also
reserves the top 64MB of every virtual address space. The remaining
physical memory is available for allocation to domains at a page
granularity. Xen tracks the ownership and use of each page, which
allows it to enforce secure partitioning between domains.
Each domain has a maximum and current physical memory allocation. A
guest OS may run a `balloon driver' to dynamically adjust its current
memory allocation up to its limit.
%% XXX SMH: I use machine and physical in the next section (which is
%% kinda required for consistency with code); wonder if this section
%% should use same terms?
%%
%% Probably.
%%
%% Merging this and below section at some point prob makes sense.
\section{Pseudo-Physical Memory}
Since physical memory is allocated and freed on a page granularity,
there is no guarantee that a domain will receive a contiguous stretch
of physical memory. However most operating systems do not have good
support for operating in a fragmented physical address space. To aid
porting such operating systems to run on top of Xen, we make a
distinction between \emph{machine memory} and \emph{pseudo-physical
memory}.
Put simply, machine memory refers to the entire amount of memory
installed in the machine, including that reserved by Xen, in use by
various domains, or currently unallocated. We consider machine memory
to comprise a set of 4K \emph{machine page frames} numbered
consecutively starting from 0. Machine frame numbers mean the same
within Xen or any domain.
Pseudo-physical memory, on the other hand, is a per-domain
abstraction. It allows a guest operating system to consider its memory
allocation to consist of a contiguous range of physical page frames
starting at physical frame 0, despite the fact that the underlying
machine page frames may be sparsely allocated and in any order.
To achieve this, Xen maintains a globally readable {\it
machine-to-physical} table which records the mapping from machine
page frames to pseudo-physical ones. In addition, each domain is
supplied with a {\it physical-to-machine} table which performs the
inverse mapping. Clearly the machine-to-physical table has size
proportional to the amount of RAM installed in the machine, while each
physical-to-machine table has size proportional to the memory
allocation of the given domain.
Architecture dependent code in guest operating systems can then use
the two tables to provide the abstraction of pseudo-physical memory.
In general, only certain specialized parts of the operating system
(such as page table management) needs to understand the difference
between machine and pseudo-physical addresses.
\section{Page Table Updates}
In the default mode of operation, Xen enforces read-only access to
page tables and requires guest operating systems to explicitly request
any modifications. Xen validates all such requests and only applies
updates that it deems safe. This is necessary to prevent domains from
adding arbitrary mappings to their page tables.
To aid validation, Xen associates a type and reference count with each
memory page. A page has one of the following mutually-exclusive types
at any point in time: page directory ({\sf PD}), page table ({\sf
PT}), local descriptor table ({\sf LDT}), global descriptor table
({\sf GDT}), or writable ({\sf RW}). Note that a guest OS may always
create readable mappings of its own memory regardless of its current
type.
%%% XXX: possibly explain more about ref count 'lifecyle' here?
This mechanism is used to maintain the invariants required for safety;
for example, a domain cannot have a writable mapping to any part of a
page table as this would require the page concerned to simultaneously
be of types {\sf PT} and {\sf RW}.
% \section{Writable Page Tables}
Xen also provides an alternative mode of operation in which guests be
have the illusion that their page tables are directly writable. Of
course this is not really the case, since Xen must still validate
modifications to ensure secure partitioning. To this end, Xen traps
any write attempt to a memory page of type {\sf PT} (i.e., that is
currently part of a page table). If such an access occurs, Xen
temporarily allows write access to that page while at the same time
\emph{disconnecting} it from the page table that is currently in use.
This allows the guest to safely make updates to the page because the
newly-updated entries cannot be used by the MMU until Xen revalidates
and reconnects the page. Reconnection occurs automatically in a
number of situations: for example, when the guest modifies a different
page-table page, when the domain is preempted, or whenever the guest
uses Xen's explicit page-table update interfaces.
Finally, Xen also supports a form of \emph{shadow page tables} in
which the guest OS uses a independent copy of page tables which are
unknown to the hardware (i.e.\ which are never pointed to by {\tt
cr3}). Instead Xen propagates changes made to the guest's tables to
the real ones, and vice versa. This is useful for logging page writes
(e.g.\ for live migration or checkpoint). A full version of the shadow
page tables also allows guest OS porting with less effort.
\section{Segment Descriptor Tables}
On boot a guest is supplied with a default GDT, which does not reside
within its own memory allocation. If the guest wishes to use other
than the default `flat' ring-1 and ring-3 segments that this GDT
provides, it must register a custom GDT and/or LDT with Xen, allocated
from its own memory. Note that a number of GDT entries are reserved by
Xen -- any custom GDT must also include sufficient space for these
entries.
For example, the following hypercall is used to specify a new GDT:
\begin{quote}
int {\bf set\_gdt}(unsigned long *{\em frame\_list}, int {\em
entries})
\emph{frame\_list}: An array of up to 16 machine page frames within
which the GDT resides. Any frame registered as a GDT frame may only
be mapped read-only within the guest's address space (e.g., no
writable mappings, no use as a page-table page, and so on).
\emph{entries}: The number of descriptor-entry slots in the GDT.
Note that the table must be large enough to contain Xen's reserved
entries; thus we must have `{\em entries $>$
LAST\_RESERVED\_GDT\_ENTRY}\ '. Note also that, after registering
the GDT, slots \emph{FIRST\_} through
\emph{LAST\_RESERVED\_GDT\_ENTRY} are no longer usable by the guest
and may be overwritten by Xen.
\end{quote}
The LDT is updated via the generic MMU update mechanism (i.e., via the
{\tt mmu\_update()} hypercall.
\section{Start of Day}
The start-of-day environment for guest operating systems is rather
different to that provided by the underlying hardware. In particular,
the processor is already executing in protected mode with paging
enabled.
{\it Domain 0} is created and booted by Xen itself. For all subsequent
domains, the analogue of the boot-loader is the {\it domain builder},
user-space software running in {\it domain 0}. The domain builder is
responsible for building the initial page tables for a domain and
loading its kernel image at the appropriate virtual address.
+268
View File
@@ -0,0 +1,268 @@
\chapter{Scheduling API}
The scheduling API is used by both the schedulers described above and should
also be used by any new schedulers. It provides a generic interface and also
implements much of the ``boilerplate'' code.
Schedulers conforming to this API are described by the following
structure:
\begin{verbatim}
struct scheduler
{
char *name; /* full name for this scheduler */
char *opt_name; /* option name for this scheduler */
unsigned int sched_id; /* ID for this scheduler */
int (*init_scheduler) ();
int (*alloc_task) (struct task_struct *);
void (*add_task) (struct task_struct *);
void (*free_task) (struct task_struct *);
void (*rem_task) (struct task_struct *);
void (*wake_up) (struct task_struct *);
void (*do_block) (struct task_struct *);
task_slice_t (*do_schedule) (s_time_t);
int (*control) (struct sched_ctl_cmd *);
int (*adjdom) (struct task_struct *,
struct sched_adjdom_cmd *);
s32 (*reschedule) (struct task_struct *);
void (*dump_settings) (void);
void (*dump_cpu_state) (int);
void (*dump_runq_el) (struct task_struct *);
};
\end{verbatim}
The only method that {\em must} be implemented is
{\tt do\_schedule()}. However, if there is not some implementation for the
{\tt wake\_up()} method then waking tasks will not get put on the runqueue!
The fields of the above structure are described in more detail below.
\subsubsection{name}
The name field should point to a descriptive ASCII string.
\subsubsection{opt\_name}
This field is the value of the {\tt sched=} boot-time option that will select
this scheduler.
\subsubsection{sched\_id}
This is an integer that uniquely identifies this scheduler. There should be a
macro corrsponding to this scheduler ID in {\tt <xen/sched-if.h>}.
\subsubsection{init\_scheduler}
\paragraph*{Purpose}
This is a function for performing any scheduler-specific initialisation. For
instance, it might allocate memory for per-CPU scheduler data and initialise it
appropriately.
\paragraph*{Call environment}
This function is called after the initialisation performed by the generic
layer. The function is called exactly once, for the scheduler that has been
selected.
\paragraph*{Return values}
This should return negative on failure --- this will cause an
immediate panic and the system will fail to boot.
\subsubsection{alloc\_task}
\paragraph*{Purpose}
Called when a {\tt task\_struct} is allocated by the generic scheduler
layer. A particular scheduler implementation may use this method to
allocate per-task data for this task. It may use the {\tt
sched\_priv} pointer in the {\tt task\_struct} to point to this data.
\paragraph*{Call environment}
The generic layer guarantees that the {\tt sched\_priv} field will
remain intact from the time this method is called until the task is
deallocated (so long as the scheduler implementation does not change
it explicitly!).
\paragraph*{Return values}
Negative on failure.
\subsubsection{add\_task}
\paragraph*{Purpose}
Called when a task is initially added by the generic layer.
\paragraph*{Call environment}
The fields in the {\tt task\_struct} are now filled out and available for use.
Schedulers should implement appropriate initialisation of any per-task private
information in this method.
\subsubsection{free\_task}
\paragraph*{Purpose}
Schedulers should free the space used by any associated private data
structures.
\paragraph*{Call environment}
This is called when a {\tt task\_struct} is about to be deallocated.
The generic layer will have done generic task removal operations and
(if implemented) called the scheduler's {\tt rem\_task} method before
this method is called.
\subsubsection{rem\_task}
\paragraph*{Purpose}
This is called when a task is being removed from scheduling (but is
not yet being freed).
\subsubsection{wake\_up}
\paragraph*{Purpose}
Called when a task is woken up, this method should put the task on the runqueue
(or do the scheduler-specific equivalent action).
\paragraph*{Call environment}
The task is already set to state RUNNING.
\subsubsection{do\_block}
\paragraph*{Purpose}
This function is called when a task is blocked. This function should
not remove the task from the runqueue.
\paragraph*{Call environment}
The EVENTS\_MASTER\_ENABLE\_BIT is already set and the task state changed to
TASK\_INTERRUPTIBLE on entry to this method. A call to the {\tt
do\_schedule} method will be made after this method returns, in
order to select the next task to run.
\subsubsection{do\_schedule}
This method must be implemented.
\paragraph*{Purpose}
The method is called each time a new task must be chosen for scheduling on the
current CPU. The current time as passed as the single argument (the current
task can be found using the {\tt current} macro).
This method should select the next task to run on this CPU and set it's minimum
time to run as well as returning the data described below.
This method should also take the appropriate action if the previous
task has blocked, e.g. removing it from the runqueue.
\paragraph*{Call environment}
The other fields in the {\tt task\_struct} are updated by the generic layer,
which also performs all Xen-specific tasks and performs the actual task switch
(unless the previous task has been chosen again).
This method is called with the {\tt schedule\_lock} held for the current CPU
and local interrupts disabled.
\paragraph*{Return values}
Must return a {\tt struct task\_slice} describing what task to run and how long
for (at maximum).
\subsubsection{control}
\paragraph*{Purpose}
This method is called for global scheduler control operations. It takes a
pointer to a {\tt struct sched\_ctl\_cmd}, which it should either
source data from or populate with data, depending on the value of the
{\tt direction} field.
\paragraph*{Call environment}
The generic layer guarantees that when this method is called, the
caller selected the correct scheduler ID, hence the scheduler's
implementation does not need to sanity-check these parts of the call.
\paragraph*{Return values}
This function should return the value to be passed back to user space, hence it
should either be 0 or an appropriate errno value.
\subsubsection{sched\_adjdom}
\paragraph*{Purpose}
This method is called to adjust the scheduling parameters of a particular
domain, or to query their current values. The function should check
the {\tt direction} field of the {\tt sched\_adjdom\_cmd} it receives in
order to determine which of these operations is being performed.
\paragraph*{Call environment}
The generic layer guarantees that the caller has specified the correct
control interface version and scheduler ID and that the supplied {\tt
task\_struct} will not be deallocated during the call (hence it is not
necessary to {\tt get\_task\_struct}).
\paragraph*{Return values}
This function should return the value to be passed back to user space, hence it
should either be 0 or an appropriate errno value.
\subsubsection{reschedule}
\paragraph*{Purpose}
This method is called to determine if a reschedule is required as a result of a
particular task.
\paragraph*{Call environment}
The generic layer will cause a reschedule if the current domain is the idle
task or it has exceeded its minimum time slice before a reschedule. The
generic layer guarantees that the task passed is not currently running but is
on the runqueue.
\paragraph*{Return values}
Should return a mask of CPUs to cause a reschedule on.
\subsubsection{dump\_settings}
\paragraph*{Purpose}
If implemented, this should dump any private global settings for this
scheduler to the console.
\paragraph*{Call environment}
This function is called with interrupts enabled.
\subsubsection{dump\_cpu\_state}
\paragraph*{Purpose}
This method should dump any private settings for the specified CPU.
\paragraph*{Call environment}
This function is called with interrupts disabled and the {\tt schedule\_lock}
for the specified CPU held.
\subsubsection{dump\_runq\_el}
\paragraph*{Purpose}
This method should dump any private settings for the specified task.
\paragraph*{Call environment}
This function is called with interrupts disabled and the {\tt schedule\_lock}
for the task's CPU held.
+26 -2099
View File
File diff suppressed because it is too large. Load diff
+170
View File
@@ -0,0 +1,170 @@
\chapter{Build, Boot and Debug Options}
This chapter describes the build- and boot-time options which may be
used to tailor your Xen system.
\section{Xen Build Options}
Xen provides a number of build-time options which should be set as
environment variables or passed on make's command-line.
\begin{description}
\item[verbose=y] Enable debugging messages when Xen detects an
unexpected condition. Also enables console output from all domains.
\item[debug=y] Enable debug assertions. Implies {\bf verbose=y}.
(Primarily useful for tracing bugs in Xen).
\item[debugger=y] Enable the in-Xen debugger. This can be used to
debug Xen, guest OSes, and applications.
\item[perfc=y] Enable performance counters for significant events
within Xen. The counts can be reset or displayed on Xen's console
via console control keys.
\item[trace=y] Enable per-cpu trace buffers which log a range of
events within Xen for collection by control software.
\end{description}
\section{Xen Boot Options}
\label{s:xboot}
These options are used to configure Xen's behaviour at runtime. They
should be appended to Xen's command line, either manually or by
editing \path{grub.conf}.
\begin{description}
\item [ noreboot ] Don't reboot the machine automatically on errors.
This is useful to catch debug output if you aren't catching console
messages via the serial line.
\item [ nosmp ] Disable SMP support. This option is implied by
`ignorebiostables'.
\item [ watchdog ] Enable NMI watchdog which can report certain
failures.
\item [ noirqbalance ] Disable software IRQ balancing and affinity.
This can be used on systems such as Dell 1850/2850 that have
workarounds in hardware for IRQ-routing issues.
\item [ badpage=$<$page number$>$,$<$page number$>$, \ldots ] Specify
a list of pages not to be allocated for use because they contain bad
bytes. For example, if your memory tester says that byte 0x12345678
is bad, you would place `badpage=0x12345' on Xen's command line.
\item [ com1=$<$baud$>$,DPS,$<$io\_base$>$,$<$irq$>$
com2=$<$baud$>$,DPS,$<$io\_base$>$,$<$irq$>$ ] \mbox{}\\
Xen supports up to two 16550-compatible serial ports. For example:
`com1=9600, 8n1, 0x408, 5' maps COM1 to a 9600-baud port, 8 data
bits, no parity, 1 stop bit, I/O port base 0x408, IRQ 5. If some
configuration options are standard (e.g., I/O base and IRQ), then
only a prefix of the full configuration string need be specified. If
the baud rate is pre-configured (e.g., by the bootloader) then you
can specify `auto' in place of a numeric baud rate.
\item [ console=$<$specifier list$>$ ] Specify the destination for Xen
console I/O. This is a comma-separated list of, for example:
\begin{description}
\item[ vga ] Use VGA console and allow keyboard input.
\item[ com1 ] Use serial port com1.
\item[ com2H ] Use serial port com2. Transmitted chars will have the
MSB set. Received chars must have MSB set.
\item[ com2L] Use serial port com2. Transmitted chars will have the
MSB cleared. Received chars must have MSB cleared.
\end{description}
The latter two examples allow a single port to be shared by two
subsystems (e.g.\ console and debugger). Sharing is controlled by
MSB of each transmitted/received character. [NB. Default for this
option is `com1,vga']
\item [ sync\_console ] Force synchronous console output. This is
useful if you system fails unexpectedly before it has sent all
available output to the console. In most cases Xen will
automatically enter synchronous mode when an exceptional event
occurs, but this option provides a manual fallback.
\item [ conswitch=$<$switch-char$><$auto-switch-char$>$ ] Specify how
to switch serial-console input between Xen and DOM0. The required
sequence is CTRL-$<$switch-char$>$ pressed three times. Specifying
the backtick character disables switching. The
$<$auto-switch-char$>$ specifies whether Xen should auto-switch
input to DOM0 when it boots --- if it is `x' then auto-switching is
disabled. Any other value, or omitting the character, enables
auto-switching. [NB. Default switch-char is `a'.]
\item [ nmi=xxx ]
Specify what to do with an NMI parity or I/O error. \\
`nmi=fatal': Xen prints a diagnostic and then hangs. \\
`nmi=dom0': Inform DOM0 of the NMI. \\
`nmi=ignore': Ignore the NMI.
\item [ mem=xxx ] Set the physical RAM address limit. Any RAM
appearing beyond this physical address in the memory map will be
ignored. This parameter may be specified with a B, K, M or G suffix,
representing bytes, kilobytes, megabytes and gigabytes respectively.
The default unit, if no suffix is specified, is kilobytes.
\item [ dom0\_mem=xxx ] Set the amount of memory to be allocated to
domain0. In Xen 3.x the parameter may be specified with a B, K, M or
G suffix, representing bytes, kilobytes, megabytes and gigabytes
respectively; if no suffix is specified, the parameter defaults to
kilobytes. In previous versions of Xen, suffixes were not supported
and the value is always interpreted as kilobytes.
\item [ tbuf\_size=xxx ] Set the size of the per-cpu trace buffers, in
pages (default 1). Note that the trace buffers are only enabled in
debug builds. Most users can ignore this feature completely.
\item [ sched=xxx ] Select the CPU scheduler Xen should use. The
current possibilities are `bvt' (default), `atropos' and `rrobin'.
For more information see Section~\ref{s:sched}.
\item [ apic\_verbosity=debug,verbose ] Print more detailed
information about local APIC and IOAPIC configuration.
\item [ lapic ] Force use of local APIC even when left disabled by
uniprocessor BIOS.
\item [ nolapic ] Ignore local APIC in a uniprocessor system, even if
enabled by the BIOS.
\item [ apic=bigsmp,default,es7000,summit ] Specify NUMA platform.
This can usually be probed automatically.
\end{description}
In addition, the following options may be specified on the Xen command
line. Since domain 0 shares responsibility for booting the platform,
Xen will automatically propagate these options to its command line.
These options are taken from Linux's command-line syntax with
unchanged semantics.
\begin{description}
\item [ acpi=off,force,strict,ht,noirq,\ldots ] Modify how Xen (and
domain 0) parses the BIOS ACPI tables.
\item [ acpi\_skip\_timer\_override ] Instruct Xen (and domain~0) to
ignore timer-interrupt override instructions specified by the BIOS
ACPI tables.
\item [ noapic ] Instruct Xen (and domain~0) to ignore any IOAPICs
that are present in the system, and instead continue to use the
legacy PIC.
\end{description}
\section{XenLinux Boot Options}
In addition to the standard Linux kernel boot options, we support:
\begin{description}
\item[ xencons=xxx ] Specify the device node to which the Xen virtual
console driver is attached. The following options are supported:
\begin{center}
\begin{tabular}{l}
`xencons=off': disable virtual console \\
`xencons=tty': attach console to /dev/tty1 (tty0 at boot-time) \\
`xencons=ttyS': attach console to /dev/ttyS0
\end{tabular}
\end{center}
The default is ttyS for dom0 and tty for all other domains.
\end{description}
\section{Debugging}
\label{s:keys}
Xen has a set of debugging features that can be useful to try and
figure out what's going on. Hit `h' on the serial line (if you
specified a baud rate on the Xen command line) or ScrollLock-h on the
keyboard to get a list of supported commands.
If you have a crash you'll likely get a crash dump containing an EIP
(PC) which, along with an \path{objdump -d image}, can be useful in
figuring out what's happened. Debug a Xenlinux image just as you
would any other Linux kernel.
%% We supply a handy debug terminal program which you can find in
%% \path{/usr/local/src/xen-2.0.bk/tools/misc/miniterm/} This should
%% be built and executed on another machine that is connected via a
%% null modem cable. Documentation is included. Alternatively, if the
%% Xen machine is connected to a serial-port server then we supply a
%% dumb TCP terminal client, {\tt xencons}.
+115
View File
@@ -0,0 +1,115 @@
\chapter{Control Software}
The Xen control software includes the \xend\ node control daemon
(which must be running), the xm command line tools, and the prototype
xensv web interface.
\section{\Xend\ (node control daemon)}
\label{s:xend}
The Xen Daemon (\Xend) performs system management functions related to
virtual machines. It forms a central point of control for a machine
and can be controlled using an HTTP-based protocol. \Xend\ must be
running in order to start and manage virtual machines.
\Xend\ must be run as root because it needs access to privileged
system management functions. A small set of commands may be issued on
the \xend\ command line:
\begin{tabular}{ll}
\verb!# xend start! & start \xend, if not already running \\
\verb!# xend stop! & stop \xend\ if already running \\
\verb!# xend restart! & restart \xend\ if running, otherwise start it \\
% \verb!# xend trace_start! & start \xend, with very detailed debug logging \\
\verb!# xend status! & indicates \xend\ status by its return code
\end{tabular}
A SysV init script called {\tt xend} is provided to start \xend\ at
boot time. {\tt make install} installs this script in
\path{/etc/init.d}. To enable it, you have to make symbolic links in
the appropriate runlevel directories or use the {\tt chkconfig} tool,
where available.
Once \xend\ is running, more sophisticated administration can be done
using the xm tool (see Section~\ref{s:xm}) and the experimental Xensv
web interface (see Section~\ref{s:xensv}).
As \xend\ runs, events will be logged to \path{/var/log/xend.log} and,
if the migration assistant daemon (\path{xfrd}) has been started,
\path{/var/log/xfrd.log}. These may be of use for troubleshooting
problems.
\section{Xm (command line interface)}
\label{s:xm}
The xm tool is the primary tool for managing Xen from the console.
The general format of an xm command line is:
\begin{verbatim}
# xm command [switches] [arguments] [variables]
\end{verbatim}
The available \emph{switches} and \emph{arguments} are dependent on
the \emph{command} chosen. The \emph{variables} may be set using
declarations of the form {\tt variable=value} and command line
declarations override any of the values in the configuration file
being used, including the standard variables described above and any
custom variables (for instance, the \path{xmdefconfig} file uses a
{\tt vmid} variable).
The available commands are as follows:
\begin{description}
\item[set-mem] Request a domain to adjust its memory footprint.
\item[create] Create a new domain.
\item[destroy] Kill a domain immediately.
\item[list] List running domains.
\item[shutdown] Ask a domain to shutdown.
\item[dmesg] Fetch the Xen (not Linux!) boot output.
\item[consoles] Lists the available consoles.
\item[console] Connect to the console for a domain.
\item[help] Get help on xm commands.
\item[save] Suspend a domain to disk.
\item[restore] Restore a domain from disk.
\item[pause] Pause a domain's execution.
\item[unpause] Un-pause a domain.
\item[pincpu] Pin a domain to a CPU.
\item[bvt] Set BVT scheduler parameters for a domain.
\item[bvt\_ctxallow] Set the BVT context switching allowance for the
system.
\item[atropos] Set the atropos parameters for a domain.
\item[rrobin] Set the round robin time slice for the system.
\item[info] Get information about the Xen host.
\item[call] Call a \xend\ HTTP API function directly.
\end{description}
For a detailed overview of switches, arguments and variables to each
command try
\begin{quote}
\begin{verbatim}
# xm help command
\end{verbatim}
\end{quote}
\section{Xensv (web control interface)}
\label{s:xensv}
Xensv is the experimental web control interface for managing a Xen
machine. It can be used to perform some (but not yet all) of the
management tasks that can be done using the xm tool.
It can be started using:
\begin{quote}
\verb_# xensv start_
\end{quote}
and stopped using:
\begin{quote}
\verb_# xensv stop_
\end{quote}
By default, Xensv will serve out the web interface on port 8080. This
can be changed by editing
\path{/usr/lib/python2.3/site-packages/xen/sv/params.py}.
Once Xensv is running, the web interface can be used to create and
manage running domains.
+154
View File
@@ -0,0 +1,154 @@
\chapter{Installing Xen / XenLinux on Debian}
The Debian project provides a tool called \path{debootstrap} which
allows a base Debian system to be installed into a filesystem without
requiring the host system to have any Debian-specific software (such
as \path{apt}).
Here's some info how to install Debian 3.1 (Sarge) for an unprivileged
Xen domain:
\begin{enumerate}
\item Set up Xen and test that it's working, as described earlier in
this manual.
\item Create disk images for rootfs and swap. Alternatively, you might
create dedicated partitions, LVM logical volumes, etc.\ if that
suits your setup.
\begin{verbatim}
dd if=/dev/zero of=/path/diskimage bs=1024k count=size_in_mbytes
dd if=/dev/zero of=/path/swapimage bs=1024k count=size_in_mbytes
\end{verbatim}
If you're going to use this filesystem / disk image only as a
`template' for other vm disk images, something like 300 MB should be
enough. (of course it depends what kind of packages you are planning
to install to the template)
\item Create the filesystem and initialise the swap image
\begin{verbatim}
mkfs.ext3 /path/diskimage
mkswap /path/swapimage
\end{verbatim}
\item Mount the disk image for installation
\begin{verbatim}
mount -o loop /path/diskimage /mnt/disk
\end{verbatim}
\item Install \path{debootstrap}. Make sure you have debootstrap
installed on the host. If you are running Debian Sarge (3.1 /
testing) or unstable you can install it by running \path{apt-get
install debootstrap}. Otherwise, it can be downloaded from the
Debian project website.
\item Install Debian base to the disk image:
\begin{verbatim}
debootstrap --arch i386 sarge /mnt/disk \
http://ftp.<countrycode>.debian.org/debian
\end{verbatim}
You can use any other Debian http/ftp mirror you want.
\item When debootstrap completes successfully, modify settings:
\begin{verbatim}
chroot /mnt/disk /bin/bash
\end{verbatim}
Edit the following files using vi or nano and make needed changes:
\begin{verbatim}
/etc/hostname
/etc/hosts
/etc/resolv.conf
/etc/network/interfaces
/etc/networks
\end{verbatim}
Set up access to the services, edit:
\begin{verbatim}
/etc/hosts.deny
/etc/hosts.allow
/etc/inetd.conf
\end{verbatim}
Add Debian mirror to:
\begin{verbatim}
/etc/apt/sources.list
\end{verbatim}
Create fstab like this:
\begin{verbatim}
/dev/sda1 / ext3 errors=remount-ro 0 1
/dev/sda2 none swap sw 0 0
proc /proc proc defaults 0 0
\end{verbatim}
Logout
\item Unmount the disk image
\begin{verbatim}
umount /mnt/disk
\end{verbatim}
\item Create Xen 2.0 configuration file for the new domain. You can
use the example-configurations coming with Xen as a template.
Make sure you have the following set up:
\begin{verbatim}
disk = [ 'file:/path/diskimage,sda1,w', 'file:/path/swapimage,sda2,w' ]
root = "/dev/sda1 ro"
\end{verbatim}
\item Start the new domain
\begin{verbatim}
xm create -f domain_config_file
\end{verbatim}
Check that the new domain is running:
\begin{verbatim}
xm list
\end{verbatim}
\item Attach to the console of the new domain. You should see
something like this when starting the new domain:
\begin{verbatim}
Started domain testdomain2, console on port 9626
\end{verbatim}
There you can see the ID of the console: 26. You can also list the
consoles with \path{xm consoles} (ID is the last two digits of the
port number.)
Attach to the console:
\begin{verbatim}
xm console 26
\end{verbatim}
or by telnetting to the port 9626 of localhost (the xm console
program works better).
\item Log in and run base-config
As a default there's no password for the root.
Check that everything looks OK, and the system started without
errors. Check that the swap is active, and the network settings are
correct.
Run \path{/usr/sbin/base-config} to set up the Debian settings.
Set up the password for root using passwd.
\item Done. You can exit the console by pressing {\path{Ctrl + ]}}
\end{enumerate}
If you need to create new domains, you can just copy the contents of
the `template'-image to the new disk images, either by mounting the
template and the new image, and using \path{cp -a} or \path{tar} or by
simply copying the image file. Once this is done, modify the
image-specific settings (hostname, network settings, etc).
+281
View File
@@ -0,0 +1,281 @@
\chapter{Domain Configuration}
\label{cha:config}
The following contains the syntax of the domain configuration files
and description of how to further specify networking, driver domain
and general scheduling behavior.
\section{Configuration Files}
\label{s:cfiles}
Xen configuration files contain the following standard variables.
Unless otherwise stated, configuration items should be enclosed in
quotes: see \path{/etc/xen/xmexample1} and \path{/etc/xen/xmexample2}
for concrete examples of the syntax.
\begin{description}
\item[kernel] Path to the kernel image.
\item[ramdisk] Path to a ramdisk image (optional).
% \item[builder] The name of the domain build function (e.g.
% {\tt'linux'} or {\tt'netbsd'}.
\item[memory] Memory size in megabytes.
\item[cpu] CPU to run this domain on, or {\tt -1} for auto-allocation.
\item[console] Port to export the domain console on (default 9600 +
domain ID).
\item[nics] Number of virtual network interfaces.
\item[vif] List of MAC addresses (random addresses are assigned if not
given) and bridges to use for the domain's network interfaces, e.g.\
\begin{verbatim}
vif = [ 'mac=aa:00:00:00:00:11, bridge=xen-br0',
'bridge=xen-br1' ]
\end{verbatim}
to assign a MAC address and bridge to the first interface and assign
a different bridge to the second interface, leaving \xend\ to choose
the MAC address.
\item[disk] List of block devices to export to the domain, e.g.\ \\
\verb_disk = [ 'phy:hda1,sda1,r' ]_ \\
exports physical device \path{/dev/hda1} to the domain as
\path{/dev/sda1} with read-only access. Exporting a disk read-write
which is currently mounted is dangerous -- if you are \emph{certain}
you wish to do this, you can specify \path{w!} as the mode.
\item[dhcp] Set to {\tt `dhcp'} if you want to use DHCP to configure
networking.
\item[netmask] Manually configured IP netmask.
\item[gateway] Manually configured IP gateway.
\item[hostname] Set the hostname for the virtual machine.
\item[root] Specify the root device parameter on the kernel command
line.
\item[nfs\_server] IP address for the NFS server (if any).
\item[nfs\_root] Path of the root filesystem on the NFS server (if
any).
\item[extra] Extra string to append to the kernel command line (if
any)
\item[restart] Three possible options:
\begin{description}
\item[always] Always restart the domain, no matter what its exit
code is.
\item[never] Never restart the domain.
\item[onreboot] Restart the domain iff it requests reboot.
\end{description}
\end{description}
For additional flexibility, it is also possible to include Python
scripting commands in configuration files. An example of this is the
\path{xmexample2} file, which uses Python code to handle the
\path{vmid} variable.
%\part{Advanced Topics}
\section{Network Configuration}
For many users, the default installation should work ``out of the
box''. More complicated network setups, for instance with multiple
Ethernet interfaces and/or existing bridging setups will require some
special configuration.
The purpose of this section is to describe the mechanisms provided by
\xend\ to allow a flexible configuration for Xen's virtual networking.
\subsection{Xen virtual network topology}
Each domain network interface is connected to a virtual network
interface in dom0 by a point to point link (effectively a ``virtual
crossover cable''). These devices are named {\tt
vif$<$domid$>$.$<$vifid$>$} (e.g.\ {\tt vif1.0} for the first
interface in domain~1, {\tt vif3.1} for the second interface in
domain~3).
Traffic on these virtual interfaces is handled in domain~0 using
standard Linux mechanisms for bridging, routing, rate limiting, etc.
Xend calls on two shell scripts to perform initial configuration of
the network and configuration of new virtual interfaces. By default,
these scripts configure a single bridge for all the virtual
interfaces. Arbitrary routing / bridging configurations can be
configured by customizing the scripts, as described in the following
section.
\subsection{Xen networking scripts}
Xen's virtual networking is configured by two shell scripts (by
default \path{network} and \path{vif-bridge}). These are called
automatically by \xend\ when certain events occur, with arguments to
the scripts providing further contextual information. These scripts
are found by default in \path{/etc/xen/scripts}. The names and
locations of the scripts can be configured in
\path{/etc/xen/xend-config.sxp}.
\begin{description}
\item[network:] This script is called whenever \xend\ is started or
stopped to respectively initialize or tear down the Xen virtual
network. In the default configuration initialization creates the
bridge `xen-br0' and moves eth0 onto that bridge, modifying the
routing accordingly. When \xend\ exits, it deletes the Xen bridge
and removes eth0, restoring the normal IP and routing configuration.
%% In configurations where the bridge already exists, this script
%% could be replaced with a link to \path{/bin/true} (for instance).
\item[vif-bridge:] This script is called for every domain virtual
interface and can configure firewalling rules and add the vif to the
appropriate bridge. By default, this adds and removes VIFs on the
default Xen bridge.
\end{description}
For more complex network setups (e.g.\ where routing is required or
integrate with existing bridges) these scripts may be replaced with
customized variants for your site's preferred configuration.
%% There are two possible types of privileges: IO privileges and
%% administration privileges.
\section{Driver Domain Configuration}
I/O privileges can be assigned to allow a domain to directly access
PCI devices itself. This is used to support driver domains.
Setting back-end privileges is currently only supported in SXP format
config files. To allow a domain to function as a back-end for others,
somewhere within the {\tt vm} element of its configuration file must
be a {\tt back-end} element of the form {\tt (back-end ({\em type}))}
where {\tt \em type} may be either {\tt netif} or {\tt blkif},
according to the type of virtual device this domain will service.
%% After this domain has been built, \xend will connect all new and
%% existing {\em virtual} devices (of the appropriate type) to that
%% back-end.
Note that a block back-end cannot currently import virtual block
devices from other domains, and a network back-end cannot import
virtual network devices from other domains. Thus (particularly in the
case of block back-ends, which cannot import a virtual block device as
their root filesystem), you may need to boot a back-end domain from a
ramdisk or a network device.
Access to PCI devices may be configured on a per-device basis. Xen
will assign the minimal set of hardware privileges to a domain that
are required to control its devices. This can be configured in either
format of configuration file:
\begin{itemize}
\item SXP Format: Include device elements of the form: \\
\centerline{ {\tt (device (pci (bus {\em x}) (dev {\em y}) (func {\em z})))}} \\
inside the top-level {\tt vm} element. Each one specifies the
address of a device this domain is allowed to access --- the numbers
\emph{x},\emph{y} and \emph{z} may be in either decimal or
hexadecimal format.
\item Flat Format: Include a list of PCI device addresses of the
format: \\
\centerline{{\tt pci = ['x,y,z', \ldots]}} \\
where each element in the list is a string specifying the components
of the PCI device address, separated by commas. The components
({\tt \em x}, {\tt \em y} and {\tt \em z}) of the list may be
formatted as either decimal or hexadecimal.
\end{itemize}
%% \section{Administration Domains}
%% Administration privileges allow a domain to use the `dom0
%% operations' (so called because they are usually available only to
%% domain 0). A privileged domain can build other domains, set
%% scheduling parameters, etc.
% Support for other administrative domains is not yet available...
% perhaps we should plumb it in some time
\section{Scheduler Configuration}
\label{s:sched}
Xen offers a boot time choice between multiple schedulers. To select
a scheduler, pass the boot parameter \emph{sched=sched\_name} to Xen,
substituting the appropriate scheduler name. Details of the
schedulers and their parameters are included below; future versions of
the tools will provide a higher-level interface to these tools.
It is expected that system administrators configure their system to
use the scheduler most appropriate to their needs. Currently, the BVT
scheduler is the recommended choice.
\subsection{Borrowed Virtual Time}
{\tt sched=bvt} (the default) \\
BVT provides proportional fair shares of the CPU time. It has been
observed to penalize domains that block frequently (e.g.\ I/O
intensive domains), but this can be compensated for by using warping.
\subsubsection{Global Parameters}
\begin{description}
\item[ctx\_allow] The context switch allowance is similar to the
``quantum'' in traditional schedulers. It is the minimum time that
a scheduled domain will be allowed to run before being preempted.
\end{description}
\subsubsection{Per-domain parameters}
\begin{description}
\item[mcuadv] The MCU (Minimum Charging Unit) advance determines the
proportional share of the CPU that a domain receives. It is set
inversely proportionally to a domain's sharing weight.
\item[warp] The amount of ``virtual time'' the domain is allowed to
warp backwards.
\item[warpl] The warp limit is the maximum time a domain can run
warped for.
\item[warpu] The unwarp requirement is the minimum time a domain must
run unwarped for before it can warp again.
\end{description}
\subsection{Atropos}
{\tt sched=atropos} \\
Atropos is a soft real time scheduler. It provides guarantees about
absolute shares of the CPU, with a facility for sharing slack CPU time
on a best-effort basis. It can provide timeliness guarantees for
latency-sensitive domains.
Every domain has an associated period and slice. The domain should
receive `slice' nanoseconds every `period' nanoseconds. This allows
the administrator to configure both the absolute share of the CPU a
domain receives and the frequency with which it is scheduled.
%% When domains unblock, their period is reduced to the value of the
%% latency hint (the slice is scaled accordingly so that they still
%% get the same proportion of the CPU). For each subsequent period,
%% the slice and period times are doubled until they reach their
%% original values.
Note: don't over-commit the CPU when using Atropos (i.e.\ don't reserve
more CPU than is available --- the utilization should be kept to
slightly less than 100\% in order to ensure predictable behavior).
\subsubsection{Per-domain parameters}
\begin{description}
\item[period] The regular time interval during which a domain is
guaranteed to receive its allocation of CPU time.
\item[slice] The length of time per period that a domain is guaranteed
to run for (in the absence of voluntary yielding of the CPU).
\item[latency] The latency hint is used to control how soon after
waking up a domain it should be scheduled.
\item[xtratime] This is a boolean flag that specifies whether a domain
should be allowed a share of the system slack time.
\end{description}
\subsection{Round Robin}
{\tt sched=rrobin} \\
The round robin scheduler is included as a simple demonstration of
Xen's internal scheduler API. It is not intended for production use.
\subsubsection{Global Parameters}
\begin{description}
\item[rr\_slice] The maximum time each domain runs before the next
scheduling decision is made.
\end{description}
+243
View File
@@ -0,0 +1,243 @@
\chapter{Domain Filesystem Storage}
It is possible to directly export any Linux block device in dom0 to
another domain, or to export filesystems / devices to virtual machines
using standard network protocols (e.g.\ NBD, iSCSI, NFS, etc.). This
chapter covers some of the possibilities.
\section{Exporting Physical Devices as VBDs}
\label{s:exporting-physical-devices-as-vbds}
One of the simplest configurations is to directly export individual
partitions from domain~0 to other domains. To achieve this use the
\path{phy:} specifier in your domain configuration file. For example a
line like
\begin{quote}
\verb_disk = ['phy:hda3,sda1,w']_
\end{quote}
specifies that the partition \path{/dev/hda3} in domain~0 should be
exported read-write to the new domain as \path{/dev/sda1}; one could
equally well export it as \path{/dev/hda} or \path{/dev/sdb5} should
one wish.
In addition to local disks and partitions, it is possible to export
any device that Linux considers to be ``a disk'' in the same manner.
For example, if you have iSCSI disks or GNBD volumes imported into
domain~0 you can export these to other domains using the \path{phy:}
disk syntax. E.g.:
\begin{quote}
\verb_disk = ['phy:vg/lvm1,sda2,w']_
\end{quote}
\begin{center}
\framebox{\bf Warning: Block device sharing}
\end{center}
\begin{quote}
Block devices should typically only be shared between domains in a
read-only fashion otherwise the Linux kernel's file systems will get
very confused as the file system structure may change underneath
them (having the same ext3 partition mounted \path{rw} twice is a
sure fire way to cause irreparable damage)! \Xend\ will attempt to
prevent you from doing this by checking that the device is not
mounted read-write in domain~0, and hasn't already been exported
read-write to another domain. If you want read-write sharing,
export the directory to other domains via NFS from domain~0 (or use
a cluster file system such as GFS or ocfs2).
\end{quote}
\section{Using File-backed VBDs}
It is also possible to use a file in Domain~0 as the primary storage
for a virtual machine. As well as being convenient, this also has the
advantage that the virtual block device will be \emph{sparse} ---
space will only really be allocated as parts of the file are used. So
if a virtual machine uses only half of its disk space then the file
really takes up half of the size allocated.
For example, to create a 2GB sparse file-backed virtual block device
(actually only consumes 1KB of disk):
\begin{quote}
\verb_# dd if=/dev/zero of=vm1disk bs=1k seek=2048k count=1_
\end{quote}
Make a file system in the disk file:
\begin{quote}
\verb_# mkfs -t ext3 vm1disk_
\end{quote}
(when the tool asks for confirmation, answer `y')
Populate the file system e.g.\ by copying from the current root:
\begin{quote}
\begin{verbatim}
# mount -o loop vm1disk /mnt
# cp -ax /{root,dev,var,etc,usr,bin,sbin,lib} /mnt
# mkdir /mnt/{proc,sys,home,tmp}
\end{verbatim}
\end{quote}
Tailor the file system by editing \path{/etc/fstab},
\path{/etc/hostname}, etc.\ Don't forget to edit the files in the
mounted file system, instead of your domain~0 filesystem, e.g.\ you
would edit \path{/mnt/etc/fstab} instead of \path{/etc/fstab}. For
this example put \path{/dev/sda1} to root in fstab.
Now unmount (this is important!):
\begin{quote}
\verb_# umount /mnt_
\end{quote}
In the configuration file set:
\begin{quote}
\verb_disk = ['file:/full/path/to/vm1disk,sda1,w']_
\end{quote}
As the virtual machine writes to its `disk', the sparse file will be
filled in and consume more space up to the original 2GB.
{\bf Note that file-backed VBDs may not be appropriate for backing
I/O-intensive domains.} File-backed VBDs are known to experience
substantial slowdowns under heavy I/O workloads, due to the I/O
handling by the loopback block device used to support file-backed VBDs
in dom0. Better I/O performance can be achieved by using either
LVM-backed VBDs (Section~\ref{s:using-lvm-backed-vbds}) or physical
devices as VBDs (Section~\ref{s:exporting-physical-devices-as-vbds}).
Linux supports a maximum of eight file-backed VBDs across all domains
by default. This limit can be statically increased by using the
\emph{max\_loop} module parameter if CONFIG\_BLK\_DEV\_LOOP is
compiled as a module in the dom0 kernel, or by using the
\emph{max\_loop=n} boot option if CONFIG\_BLK\_DEV\_LOOP is compiled
directly into the dom0 kernel.
\section{Using LVM-backed VBDs}
\label{s:using-lvm-backed-vbds}
A particularly appealing solution is to use LVM volumes as backing for
domain file-systems since this allows dynamic growing/shrinking of
volumes as well as snapshot and other features.
To initialize a partition to support LVM volumes:
\begin{quote}
\begin{verbatim}
# pvcreate /dev/sda10
\end{verbatim}
\end{quote}
Create a volume group named `vg' on the physical partition:
\begin{quote}
\begin{verbatim}
# vgcreate vg /dev/sda10
\end{verbatim}
\end{quote}
Create a logical volume of size 4GB named `myvmdisk1':
\begin{quote}
\begin{verbatim}
# lvcreate -L4096M -n myvmdisk1 vg
\end{verbatim}
\end{quote}
You should now see that you have a \path{/dev/vg/myvmdisk1} Make a
filesystem, mount it and populate it, e.g.:
\begin{quote}
\begin{verbatim}
# mkfs -t ext3 /dev/vg/myvmdisk1
# mount /dev/vg/myvmdisk1 /mnt
# cp -ax / /mnt
# umount /mnt
\end{verbatim}
\end{quote}
Now configure your VM with the following disk configuration:
\begin{quote}
\begin{verbatim}
disk = [ 'phy:vg/myvmdisk1,sda1,w' ]
\end{verbatim}
\end{quote}
LVM enables you to grow the size of logical volumes, but you'll need
to resize the corresponding file system to make use of the new space.
Some file systems (e.g.\ ext3) now support online resize. See the LVM
manuals for more details.
You can also use LVM for creating copy-on-write (CoW) clones of LVM
volumes (known as writable persistent snapshots in LVM terminology).
This facility is new in Linux 2.6.8, so isn't as stable as one might
hope. In particular, using lots of CoW LVM disks consumes a lot of
dom0 memory, and error conditions such as running out of disk space
are not handled well. Hopefully this will improve in future.
To create two copy-on-write clone of the above file system you would
use the following commands:
\begin{quote}
\begin{verbatim}
# lvcreate -s -L1024M -n myclonedisk1 /dev/vg/myvmdisk1
# lvcreate -s -L1024M -n myclonedisk2 /dev/vg/myvmdisk1
\end{verbatim}
\end{quote}
Each of these can grow to have 1GB of differences from the master
volume. You can grow the amount of space for storing the differences
using the lvextend command, e.g.:
\begin{quote}
\begin{verbatim}
# lvextend +100M /dev/vg/myclonedisk1
\end{verbatim}
\end{quote}
Don't let the `differences volume' ever fill up otherwise LVM gets
rather confused. It may be possible to automate the growing process by
using \path{dmsetup wait} to spot the volume getting full and then
issue an \path{lvextend}.
In principle, it is possible to continue writing to the volume that
has been cloned (the changes will not be visible to the clones), but
we wouldn't recommend this: have the cloned volume as a `pristine'
file system install that isn't mounted directly by any of the virtual
machines.
\section{Using NFS Root}
First, populate a root filesystem in a directory on the server
machine. This can be on a distinct physical machine, or simply run
within a virtual machine on the same node.
Now configure the NFS server to export this filesystem over the
network by adding a line to \path{/etc/exports}, for instance:
\begin{quote}
\begin{small}
\begin{verbatim}
/export/vm1root 1.2.3.4/24 (rw,sync,no_root_squash)
\end{verbatim}
\end{small}
\end{quote}
Finally, configure the domain to use NFS root. In addition to the
normal variables, you should make sure to set the following values in
the domain's configuration file:
\begin{quote}
\begin{small}
\begin{verbatim}
root = '/dev/nfs'
nfs_server = '2.3.4.5' # substitute IP address of server
nfs_root = '/path/to/root' # path to root FS on the server
\end{verbatim}
\end{small}
\end{quote}
The domain will need network access at boot time, so either statically
configure an IP address using the config variables \path{ip},
\path{netmask}, \path{gateway}, \path{hostname}; or enable DHCP
(\path{dhcp='dhcp'}).
Note that the Linux NFS root implementation is known to have stability
problems under high load (this is not a Xen-specific problem), so this
configuration may not be appropriate for critical servers.
+203
View File
@@ -0,0 +1,203 @@
\chapter{Domain Management Tools}
The previous chapter described a simple example of how to configure
and start a domain. This chapter summarises the tools available to
manage running domains.
\section{Command-line Management}
Command line management tasks are also performed using the \path{xm}
tool. For online help for the commands available, type:
\begin{quote}
\verb_# xm help_
\end{quote}
You can also type \path{xm help $<$command$>$} for more information on
a given command.
\subsection{Basic Management Commands}
The most important \path{xm} commands are:
\begin{quote}
\verb_# xm list_: Lists all domains running.\\
\verb_# xm consoles_: Gives information about the domain consoles.\\
\verb_# xm console_: Opens a console to a domain (e.g.\
\verb_# xm console myVM_)
\end{quote}
\subsection{\tt xm list}
The output of \path{xm list} is in rows of the following format:
\begin{center} {\tt name domid memory cpu state cputime console}
\end{center}
\begin{quote}
\begin{description}
\item[name] The descriptive name of the virtual machine.
\item[domid] The number of the domain ID this virtual machine is
running in.
\item[memory] Memory size in megabytes.
\item[cpu] The CPU this domain is running on.
\item[state] Domain state consists of 5 fields:
\begin{description}
\item[r] running
\item[b] blocked
\item[p] paused
\item[s] shutdown
\item[c] crashed
\end{description}
\item[cputime] How much CPU time (in seconds) the domain has used so
far.
\item[console] TCP port accepting connections to the domain's
console.
\end{description}
\end{quote}
The \path{xm list} command also supports a long output format when the
\path{-l} switch is used. This outputs the fulls details of the
running domains in \xend's SXP configuration format.
For example, suppose the system is running the ttylinux domain as
described earlier. The list command should produce output somewhat
like the following:
\begin{verbatim}
# xm list
Name Id Mem(MB) CPU State Time(s) Console
Domain-0 0 251 0 r---- 172.2
ttylinux 5 63 0 -b--- 3.0 9605
\end{verbatim}
Here we can see the details for the ttylinux domain, as well as for
domain~0 (which, of course, is always running). Note that the console
port for the ttylinux domain is 9605. This can be connected to by TCP
using a terminal program (e.g. \path{telnet} or, better,
\path{xencons}). The simplest way to connect is to use the
\path{xm~console} command, specifying the domain name or ID. To
connect to the console of the ttylinux domain, we could use any of the
following:
\begin{verbatim}
# xm console ttylinux
# xm console 5
# xencons localhost 9605
\end{verbatim}
\section{Domain Save and Restore}
The administrator of a Xen system may suspend a virtual machine's
current state into a disk file in domain~0, allowing it to be resumed
at a later time.
The ttylinux domain described earlier can be suspended to disk using
the command:
\begin{verbatim}
# xm save ttylinux ttylinux.xen
\end{verbatim}
This will stop the domain named `ttylinux' and save its current state
into a file called \path{ttylinux.xen}.
To resume execution of this domain, use the \path{xm restore} command:
\begin{verbatim}
# xm restore ttylinux.xen
\end{verbatim}
This will restore the state of the domain and restart it. The domain
will carry on as before and the console may be reconnected using the
\path{xm console} command, as above.
\section{Live Migration}
Live migration is used to transfer a domain between physical hosts
whilst that domain continues to perform its usual activities --- from
the user's perspective, the migration should be imperceptible.
To perform a live migration, both hosts must be running Xen / \xend\
and the destination host must have sufficient resources (e.g.\ memory
capacity) to accommodate the domain after the move. Furthermore we
currently require both source and destination machines to be on the
same L2 subnet.
Currently, there is no support for providing automatic remote access
to filesystems stored on local disk when a domain is migrated.
Administrators should choose an appropriate storage solution (i.e.\
SAN, NAS, etc.) to ensure that domain filesystems are also available
on their destination node. GNBD is a good method for exporting a
volume from one machine to another. iSCSI can do a similar job, but is
more complex to set up.
When a domain migrates, it's MAC and IP address move with it, thus it
is only possible to migrate VMs within the same layer-2 network and IP
subnet. If the destination node is on a different subnet, the
administrator would need to manually configure a suitable etherip or
IP tunnel in the domain~0 of the remote node.
A domain may be migrated using the \path{xm migrate} command. To live
migrate a domain to another machine, we would use the command:
\begin{verbatim}
# xm migrate --live mydomain destination.ournetwork.com
\end{verbatim}
Without the \path{--live} flag, \xend\ simply stops the domain and
copies the memory image over to the new node and restarts it. Since
domains can have large allocations this can be quite time consuming,
even on a Gigabit network. With the \path{--live} flag \xend\ attempts
to keep the domain running while the migration is in progress,
resulting in typical `downtimes' of just 60--300ms.
For now it will be necessary to reconnect to the domain's console on
the new machine using the \path{xm console} command. If a migrated
domain has any open network connections then they will be preserved,
so SSH connections do not have this limitation.
\section{Managing Domain Memory}
XenLinux domains have the ability to relinquish / reclaim machine
memory at the request of the administrator or the user of the domain.
\subsection{Setting memory footprints from dom0}
The machine administrator can request that a domain alter its memory
footprint using the \path{xm set-mem} command. For instance, we can
request that our example ttylinux domain reduce its memory footprint
to 32 megabytes.
\begin{verbatim}
# xm set-mem ttylinux 32
\end{verbatim}
We can now see the result of this in the output of \path{xm list}:
\begin{verbatim}
# xm list
Name Id Mem(MB) CPU State Time(s) Console
Domain-0 0 251 0 r---- 172.2
ttylinux 5 31 0 -b--- 4.3 9605
\end{verbatim}
The domain has responded to the request by returning memory to Xen. We
can restore the domain to its original size using the command line:
\begin{verbatim}
# xm set-mem ttylinux 64
\end{verbatim}
\subsection{Setting memory footprints from within a domain}
The virtual file \path{/proc/xen/balloon} allows the owner of a domain
to adjust their own memory footprint. Reading the file (e.g.\
\path{cat /proc/xen/balloon}) prints out the current memory footprint
of the domain. Writing the file (e.g.\ \path{echo new\_target >
/proc/xen/balloon}) requests that the kernel adjust the domain's
memory footprint to a new value.
\subsection{Setting memory limits}
Xen associates a memory size limit with each domain. By default, this
is the amount of memory the domain is originally started with,
preventing the domain from ever growing beyond this size. To permit a
domain to grow beyond its original allocation or to prevent a domain
you've shrunk from reclaiming the memory it relinquished, use the
\path{xm maxmem} command.
+79
View File
@@ -0,0 +1,79 @@
\chapter{Glossary of Terms}
\begin{description}
\item[Atropos] One of the CPU schedulers provided by Xen. Atropos
provides domains with absolute shares of the CPU, with timeliness
guarantees and a mechanism for sharing out `slack time'.
\item[BVT] The BVT scheduler is used to give proportional fair shares
of the CPU to domains.
\item[Exokernel] A minimal piece of privileged code, similar to a {\bf
microkernel} but providing a more `hardware-like' interface to the
tasks it manages. This is similar to a paravirtualising VMM like
{\bf Xen} but was designed as a new operating system structure,
rather than specifically to run multiple conventional OSs.
\item[Domain] A domain is the execution context that contains a
running {\bf virtual machine}. The relationship between virtual
machines and domains on Xen is similar to that between programs and
processes in an operating system: a virtual machine is a persistent
entity that resides on disk (somewhat like a program). When it is
loaded for execution, it runs in a domain. Each domain has a {\bf
domain ID}.
\item[Domain 0] The first domain to be started on a Xen machine.
Domain 0 is responsible for managing the system.
\item[Domain ID] A unique identifier for a {\bf domain}, analogous to
a process ID in an operating system.
\item[Full virtualisation] An approach to virtualisation which
requires no modifications to the hosted operating system, providing
the illusion of a complete system of real hardware devices.
\item[Hypervisor] An alternative term for {\bf VMM}, used because it
means `beyond supervisor', since it is responsible for managing
multiple `supervisor' kernels.
\item[Live migration] A technique for moving a running virtual machine
to another physical host, without stopping it or the services
running on it.
\item[Microkernel] A small base of code running at the highest
hardware privilege level. A microkernel is responsible for sharing
CPU and memory (and sometimes other devices) between less privileged
tasks running on the system. This is similar to a VMM, particularly
a {\bf paravirtualising} VMM but typically addressing a different
problem space and providing different kind of interface.
\item[NetBSD/Xen] A port of NetBSD to the Xen architecture.
\item[Paravirtualisation] An approach to virtualisation which requires
modifications to the operating system in order to run in a virtual
machine. Xen uses paravirtualisation but preserves binary
compatibility for user space applications.
\item[Shadow pagetables] A technique for hiding the layout of machine
memory from a virtual machine's operating system. Used in some {\bf
VMMs} to provide the illusion of contiguous physical memory, in
Xen this is used during {\bf live migration}.
\item[Virtual Machine] The environment in which a hosted operating
system runs, providing the abstraction of a dedicated machine. A
virtual machine may be identical to the underlying hardware (as in
{\bf full virtualisation}, or it may differ, as in {\bf
paravirtualisation}).
\item[VMM] Virtual Machine Monitor - the software that allows multiple
virtual machines to be multiplexed on a single physical machine.
\item[Xen] Xen is a paravirtualising virtual machine monitor,
developed primarily by the Systems Research Group at the University
of Cambridge Computer Laboratory.
\item[XenLinux] Official name for the port of the Linux kernel that
runs on Xen.
\end{description}
+394
View File
@@ -0,0 +1,394 @@
\chapter{Installation}
The Xen distribution includes three main components: Xen itself, ports
of Linux 2.4 and 2.6 and NetBSD to run on Xen, and the userspace
tools required to manage a Xen-based system. This chapter describes
how to install the Xen~2.0 distribution from source. Alternatively,
there may be pre-built packages available as part of your operating
system distribution.
\section{Prerequisites}
\label{sec:prerequisites}
The following is a full list of prerequisites. Items marked `$\dag$'
are required by the \xend\ control tools, and hence required if you
want to run more than one virtual machine; items marked `$*$' are only
required if you wish to build from source.
\begin{itemize}
\item A working Linux distribution using the GRUB bootloader and
running on a P6-class (or newer) CPU.
\item [$\dag$] The \path{iproute2} package.
\item [$\dag$] The Linux bridge-utils\footnote{Available from {\tt
http://bridge.sourceforge.net}} (e.g., \path{/sbin/brctl})
\item [$\dag$] An installation of Twisted~v1.3 or
above\footnote{Available from {\tt http://www.twistedmatrix.com}}.
There may be a binary package available for your distribution;
alternatively it can be installed by running `{\sl make
install-twisted}' in the root of the Xen source tree.
\item [$*$] Build tools (gcc v3.2.x or v3.3.x, binutils, GNU make).
\item [$*$] Development installation of libcurl (e.g., libcurl-devel)
\item [$*$] Development installation of zlib (e.g., zlib-dev).
\item [$*$] Development installation of Python v2.2 or later (e.g.,
python-dev).
\item [$*$] \LaTeX\ and transfig are required to build the
documentation.
\end{itemize}
Once you have satisfied the relevant prerequisites, you can now
install either a binary or source distribution of Xen.
\section{Installing from Binary Tarball}
Pre-built tarballs are available for download from the Xen download
page
\begin{quote} {\tt http://xen.sf.net}
\end{quote}
Once you've downloaded the tarball, simply unpack and install:
\begin{verbatim}
# tar zxvf xen-2.0-install.tgz
# cd xen-2.0-install
# sh ./install.sh
\end{verbatim}
Once you've installed the binaries you need to configure your system
as described in Section~\ref{s:configure}.
\section{Installing from Source}
This section describes how to obtain, build, and install Xen from
source.
\subsection{Obtaining the Source}
The Xen source tree is available as either a compressed source tar
ball or as a clone of our master BitKeeper repository.
\begin{description}
\item[Obtaining the Source Tarball]\mbox{} \\
Stable versions (and daily snapshots) of the Xen source tree are
available as compressed tarballs from the Xen download page
\begin{quote} {\tt http://xen.sf.net}
\end{quote}
\item[Using BitKeeper]\mbox{} \\
If you wish to install Xen from a clone of our latest BitKeeper
repository then you will need to install the BitKeeper tools.
Download instructions for BitKeeper can be obtained by filling out
the form at:
\begin{quote} {\tt http://www.bitmover.com/cgi-bin/download.cgi}
\end{quote}
The public master BK repository for the 2.0 release lives at:
\begin{quote} {\tt bk://xen.bkbits.net/xen-2.0.bk}
\end{quote}
You can use BitKeeper to download it and keep it updated with the
latest features and fixes.
Change to the directory in which you want to put the source code, then
run:
\begin{verbatim}
# bk clone bk://xen.bkbits.net/xen-2.0.bk
\end{verbatim}
Under your current directory, a new directory named \path{xen-2.0.bk}
has been created, which contains all the source code for Xen, the OS
ports, and the control tools. You can update your repository with the
latest changes at any time by running:
\begin{verbatim}
# cd xen-2.0.bk # to change into the local repository
# bk pull # to update the repository
\end{verbatim}
\end{description}
% \section{The distribution}
%
% The Xen source code repository is structured as follows:
%
% \begin{description}
% \item[\path{tools/}] Xen node controller daemon (Xend), command line
% tools, control libraries
% \item[\path{xen/}] The Xen VMM.
% \item[\path{linux-*-xen-sparse/}] Xen support for Linux.
% \item[\path{linux-*-patches/}] Experimental patches for Linux.
% \item[\path{netbsd-*-xen-sparse/}] Xen support for NetBSD.
% \item[\path{docs/}] Various documentation files for users and
% developers.
% \item[\path{extras/}] Bonus extras.
% \end{description}
\subsection{Building from Source}
The top-level Xen Makefile includes a target `world' that will do the
following:
\begin{itemize}
\item Build Xen.
\item Build the control tools, including \xend.
\item Download (if necessary) and unpack the Linux 2.6 source code,
and patch it for use with Xen.
\item Build a Linux kernel to use in domain 0 and a smaller
unprivileged kernel, which can optionally be used for unprivileged
virtual machines.
\end{itemize}
After the build has completed you should have a top-level directory
called \path{dist/} in which all resulting targets will be placed; of
particular interest are the two kernels XenLinux kernel images, one
with a `-xen0' extension which contains hardware device drivers and
drivers for Xen's virtual devices, and one with a `-xenU' extension
that just contains the virtual ones. These are found in
\path{dist/install/boot/} along with the image for Xen itself and the
configuration files used during the build.
The NetBSD port can be built using:
\begin{quote}
\begin{verbatim}
# make netbsd20
\end{verbatim}
\end{quote}
NetBSD port is built using a snapshot of the netbsd-2-0 cvs branch.
The snapshot is downloaded as part of the build process, if it is not
yet present in the \path{NETBSD\_SRC\_PATH} search path. The build
process also downloads a toolchain which includes all the tools
necessary to build the NetBSD kernel under Linux.
To customize further the set of kernels built you need to edit the
top-level Makefile. Look for the line:
\begin{quote}
\begin{verbatim}
KERNELS ?= mk.linux-2.6-xen0 mk.linux-2.6-xenU
\end{verbatim}
\end{quote}
You can edit this line to include any set of operating system kernels
which have configurations in the top-level \path{buildconfigs/}
directory, for example \path{mk.linux-2.4-xenU} to build a Linux 2.4
kernel containing only virtual device drivers.
%% Inspect the Makefile if you want to see what goes on during a
%% build. Building Xen and the tools is straightforward, but XenLinux
%% is more complicated. The makefile needs a `pristine' Linux kernel
%% tree to which it will then add the Xen architecture files. You can
%% tell the makefile the location of the appropriate Linux compressed
%% tar file by
%% setting the LINUX\_SRC environment variable, e.g. \\
%% \verb!# LINUX_SRC=/tmp/linux-2.6.11.tar.bz2 make world! \\ or by
%% placing the tar file somewhere in the search path of {\tt
%% LINUX\_SRC\_PATH} which defaults to `{\tt .:..}'. If the
%% makefile can't find a suitable kernel tar file it attempts to
%% download it from kernel.org (this won't work if you're behind a
%% firewall).
%% After untaring the pristine kernel tree, the makefile uses the {\tt
%% mkbuildtree} script to add the Xen patches to the kernel.
%% The procedure is similar to build the Linux 2.4 port: \\
%% \verb!# LINUX_SRC=/path/to/linux2.4/source make linux24!
%% \framebox{\parbox{5in}{
%% {\bf Distro specific:} \\
%% {\it Gentoo} --- if not using udev (most installations,
%% currently), you'll need to enable devfs and devfs mount at boot
%% time in the xen0 config. }}
\subsection{Custom XenLinux Builds}
% If you have an SMP machine you may wish to give the {\tt '-j4'}
% argument to make to get a parallel build.
If you wish to build a customized XenLinux kernel (e.g. to support
additional devices or enable distribution-required features), you can
use the standard Linux configuration mechanisms, specifying that the
architecture being built for is \path{xen}, e.g:
\begin{quote}
\begin{verbatim}
# cd linux-2.6.11-xen0
# make ARCH=xen xconfig
# cd ..
# make
\end{verbatim}
\end{quote}
You can also copy an existing Linux configuration (\path{.config})
into \path{linux-2.6.11-xen0} and execute:
\begin{quote}
\begin{verbatim}
# make ARCH=xen oldconfig
\end{verbatim}
\end{quote}
You may be prompted with some Xen-specific options; we advise
accepting the defaults for these options.
Note that the only difference between the two types of Linux kernel
that are built is the configuration file used for each. The `U'
suffixed (unprivileged) versions don't contain any of the physical
hardware device drivers, leading to a 30\% reduction in size; hence
you may prefer these for your non-privileged domains. The `0'
suffixed privileged versions can be used to boot the system, as well
as in driver domains and unprivileged domains.
\subsection{Installing the Binaries}
The files produced by the build process are stored under the
\path{dist/install/} directory. To install them in their default
locations, do:
\begin{quote}
\begin{verbatim}
# make install
\end{verbatim}
\end{quote}
Alternatively, users with special installation requirements may wish
to install them manually by copying the files to their appropriate
destinations.
%% Files in \path{install/boot/} include:
%% \begin{itemize}
%% \item \path{install/boot/xen-2.0.gz} Link to the Xen 'kernel'
%% \item \path{install/boot/vmlinuz-2.6-xen0} Link to domain 0
%% XenLinux kernel
%% \item \path{install/boot/vmlinuz-2.6-xenU} Link to unprivileged
%% XenLinux kernel
%% \end{itemize}
The \path{dist/install/boot} directory will also contain the config
files used for building the XenLinux kernels, and also versions of Xen
and XenLinux kernels that contain debug symbols (\path{xen-syms-2.0.6}
and \path{vmlinux-syms-2.6.11.11-xen0}) which are essential for
interpreting crash dumps. Retain these files as the developers may
wish to see them if you post on the mailing list.
\section{Configuration}
\label{s:configure}
Once you have built and installed the Xen distribution, it is simple
to prepare the machine for booting and running Xen.
\subsection{GRUB Configuration}
An entry should be added to \path{grub.conf} (often found under
\path{/boot/} or \path{/boot/grub/}) to allow Xen / XenLinux to boot.
This file is sometimes called \path{menu.lst}, depending on your
distribution. The entry should look something like the following:
{\small
\begin{verbatim}
title Xen 2.0 / XenLinux 2.6
kernel /boot/xen-2.0.gz dom0_mem=131072
module /boot/vmlinuz-2.6-xen0 root=/dev/sda4 ro console=tty0
\end{verbatim}
}
The kernel line tells GRUB where to find Xen itself and what boot
parameters should be passed to it (in this case, setting domain 0's
memory allocation in kilobytes and the settings for the serial port).
For more details on the various Xen boot parameters see
Section~\ref{s:xboot}.
The module line of the configuration describes the location of the
XenLinux kernel that Xen should start and the parameters that should
be passed to it (these are standard Linux parameters, identifying the
root device and specifying it be initially mounted read only and
instructing that console output be sent to the screen). Some
distributions such as SuSE do not require the \path{ro} parameter.
%% \framebox{\parbox{5in}{
%% {\bf Distro specific:} \\
%% {\it SuSE} --- Omit the {\tt ro} option from the XenLinux
%% kernel command line, since the partition won't be remounted rw
%% during boot. }}
If you want to use an initrd, just add another \path{module} line to
the configuration, as usual:
{\small
\begin{verbatim}
module /boot/my_initrd.gz
\end{verbatim}
}
As always when installing a new kernel, it is recommended that you do
not delete existing menu options from \path{menu.lst} --- you may want
to boot your old Linux kernel in future, particularly if you have
problems.
\subsection{Serial Console (optional)}
%% kernel /boot/xen-2.0.gz dom0_mem=131072 com1=115200,8n1
%% module /boot/vmlinuz-2.6-xen0 root=/dev/sda4 ro
In order to configure Xen serial console output, it is necessary to
add an boot option to your GRUB config; e.g.\ replace the above kernel
line with:
\begin{quote}
{\small
\begin{verbatim}
kernel /boot/xen.gz dom0_mem=131072 com1=115200,8n1
\end{verbatim}}
\end{quote}
This configures Xen to output on COM1 at 115,200 baud, 8 data bits, 1
stop bit and no parity. Modify these parameters for your set up.
One can also configure XenLinux to share the serial console; to
achieve this append ``\path{console=ttyS0}'' to your module line.
If you wish to be able to log in over the XenLinux serial console it
is necessary to add a line into \path{/etc/inittab}, just as per
regular Linux. Simply add the line:
\begin{quote} {\small {\tt c:2345:respawn:/sbin/mingetty ttyS0}}
\end{quote}
and you should be able to log in. Note that to successfully log in as
root over the serial line will require adding \path{ttyS0} to
\path{/etc/securetty} in most modern distributions.
\subsection{TLS Libraries}
Users of the XenLinux 2.6 kernel should disable Thread Local Storage
(e.g.\ by doing a \path{mv /lib/tls /lib/tls.disabled}) before
attempting to run with a XenLinux kernel\footnote{If you boot without
first disabling TLS, you will get a warning message during the boot
process. In this case, simply perform the rename after the machine
is up and then run \texttt{/sbin/ldconfig} to make it take effect.}.
You can always reenable it by restoring the directory to its original
location (i.e.\ \path{mv /lib/tls.disabled /lib/tls}).
The reason for this is that the current TLS implementation uses
segmentation in a way that is not permissible under Xen. If TLS is
not disabled, an emulation mode is used within Xen which reduces
performance substantially.
We hope that this issue can be resolved by working with Linux
distribution vendors to implement a minor backward-compatible change
to the TLS library.
\section{Booting Xen}
It should now be possible to restart the system and use Xen. Reboot
as usual but choose the new Xen option when the Grub screen appears.
What follows should look much like a conventional Linux boot. The
first portion of the output comes from Xen itself, supplying low level
information about itself and the machine it is running on. The
following portion of the output comes from XenLinux.
You may see some errors during the XenLinux boot. These are not
necessarily anything to worry about --- they may result from kernel
configuration differences between your XenLinux kernel and the one you
usually use.
When the boot completes, you should be able to log into your system as
usual. If you are unable to log in to your system running Xen, you
should still be able to reboot with your normal Linux kernel.
+143
View File
@@ -0,0 +1,143 @@
\chapter{Introduction}
Xen is a \emph{paravirtualising} virtual machine monitor (VMM), or
`hypervisor', for the x86 processor architecture. Xen can securely
execute multiple virtual machines on a single physical system with
close-to-native performance. The virtual machine technology
facilitates enterprise-grade functionality, including:
\begin{itemize}
\item Virtual machines with performance close to native hardware.
\item Live migration of running virtual machines between physical
hosts.
\item Excellent hardware support (supports most Linux device drivers).
\item Sandboxed, re-startable device drivers.
\end{itemize}
Paravirtualisation permits very high performance virtualisation, even
on architectures like x86 that are traditionally very hard to
virtualise.
The drawback of this approach is that it requires operating systems to
be \emph{ported} to run on Xen. Porting an OS to run on Xen is
similar to supporting a new hardware platform, however the process is
simplified because the paravirtual machine architecture is very
similar to the underlying native hardware. Even though operating
system kernels must explicitly support Xen, a key feature is that user
space applications and libraries \emph{do not} require modification.
Xen support is available for increasingly many operating systems:
right now, Linux 2.4, Linux 2.6 and NetBSD are available for Xen 2.0.
A FreeBSD port is undergoing testing and will be incorporated into the
release soon. Other OS ports, including Plan 9, are in progress. We
hope that that arch-xen patches will be incorporated into the
mainstream releases of these operating systems in due course (as has
already happened for NetBSD).
Possible usage scenarios for Xen include:
\begin{description}
\item [Kernel development.] Test and debug kernel modifications in a
sandboxed virtual machine --- no need for a separate test machine.
\item [Multiple OS configurations.] Run multiple operating systems
simultaneously, for instance for compatibility or QA purposes.
\item [Server consolidation.] Move multiple servers onto a single
physical host with performance and fault isolation provided at
virtual machine boundaries.
\item [Cluster computing.] Management at VM granularity provides more
flexibility than separately managing each physical host, but better
control and isolation than single-system image solutions,
particularly by using live migration for load balancing.
\item [Hardware support for custom OSes.] Allow development of new
OSes while benefiting from the wide-ranging hardware support of
existing OSes such as Linux.
\end{description}
\section{Structure of a Xen-Based System}
A Xen system has multiple layers, the lowest and most privileged of
which is Xen itself.
Xen in turn may host multiple \emph{guest} operating systems, each of
which is executed within a secure virtual machine (in Xen terminology,
a \emph{domain}). Domains are scheduled by Xen to make effective use
of the available physical CPUs. Each guest OS manages its own
applications, which includes responsibility for scheduling each
application within the time allotted to the VM by Xen.
The first domain, \emph{domain 0}, is created automatically when the
system boots and has special management privileges. Domain 0 builds
other domains and manages their virtual devices. It also performs
administrative tasks such as suspending, resuming and migrating other
virtual machines.
Within domain 0, a process called \emph{xend} runs to manage the
system. \Xend is responsible for managing virtual machines and
providing access to their consoles. Commands are issued to \xend over
an HTTP interface, either from a command-line tool or from a web
browser.
\section{Hardware Support}
Xen currently runs only on the x86 architecture, requiring a `P6' or
newer processor (e.g. Pentium Pro, Celeron, Pentium II, Pentium III,
Pentium IV, Xeon, AMD Athlon, AMD Duron). Multiprocessor machines are
supported, and we also have basic support for HyperThreading (SMT),
although this remains a topic for ongoing research. A port
specifically for x86/64 is in progress, although Xen already runs on
such systems in 32-bit legacy mode. In addition a port to the IA64
architecture is approaching completion. We hope to add other
architectures such as PPC and ARM in due course.
Xen can currently use up to 4GB of memory. It is possible for x86
machines to address up to 64GB of physical memory but there are no
current plans to support these systems: The x86/64 port is the planned
route to supporting larger memory sizes.
Xen offloads most of the hardware support issues to the guest OS
running in Domain~0. Xen itself contains only the code required to
detect and start secondary processors, set up interrupt routing, and
perform PCI bus enumeration. Device drivers run within a privileged
guest OS rather than within Xen itself. This approach provides
compatibility with the majority of device hardware supported by Linux.
The default XenLinux build contains support for relatively modern
server-class network and disk hardware, but you can add support for
other hardware by configuring your XenLinux kernel in the normal way.
\section{History}
Xen was originally developed by the Systems Research Group at the
University of Cambridge Computer Laboratory as part of the XenoServers
project, funded by the UK-EPSRC.
XenoServers aim to provide a `public infrastructure for global
distributed computing', and Xen plays a key part in that, allowing us
to efficiently partition a single machine to enable multiple
independent clients to run their operating systems and applications in
an environment providing protection, resource isolation and
accounting. The project web page contains further information along
with pointers to papers and technical reports:
\path{http://www.cl.cam.ac.uk/xeno}
Xen has since grown into a fully-fledged project in its own right,
enabling us to investigate interesting research issues regarding the
best techniques for virtualising resources such as the CPU, memory,
disk and network. The project has been bolstered by support from
Intel Research Cambridge, and HP Labs, who are now working closely
with us.
Xen was first described in a paper presented at SOSP in
2003\footnote{\tt
http://www.cl.cam.ac.uk/netos/papers/2003-xensosp.pdf}, and the
first public release (1.0) was made that October. Since then, Xen has
significantly matured and is now used in production scenarios on many
sites.
Xen 2.0 features greatly enhanced hardware support, configuration
flexibility, usability and a larger complement of supported operating
systems. This latest release takes Xen a step closer to becoming the
definitive open source solution for virtualisation.
+61
View File
@@ -0,0 +1,61 @@
\chapter{Installing Xen / XenLinux on Red~Hat or Fedora Core}
When using Xen / XenLinux on a standard Linux distribution there are a
couple of things to watch out for:
Note that, because domains greater than 0 don't have any privileged
access at all, certain commands in the default boot sequence will fail
e.g.\ attempts to update the hwclock, change the console font, update
the keytable map, start apmd (power management), or gpm (mouse
cursor). Either ignore the errors (they should be harmless), or
remove them from the startup scripts. Deleting the following links
are a good start: {\path{S24pcmcia}}, {\path{S09isdn}},
{\path{S17keytable}}, {\path{S26apmd}}, {\path{S85gpm}}.
If you want to use a single root file system that works cleanly for
both domain~0 and unprivileged domains, a useful trick is to use
different `init' run levels. For example, use run level 3 for
domain~0, and run level 4 for other domains. This enables different
startup scripts to be run in depending on the run level number passed
on the kernel command line.
If using NFS root files systems mounted either from an external server
or from domain0 there are a couple of other gotchas. The default
{\path{/etc/sysconfig/iptables}} rules block NFS, so part way through
the boot sequence things will suddenly go dead.
If you're planning on having a separate NFS {\path{/usr}} partition,
the RH9 boot scripts don't make life easy - they attempt to mount NFS
file systems way to late in the boot process. The easiest way I found
to do this was to have a {\path{/linuxrc}} script run ahead of
{\path{/sbin/init}} that mounts {\path{/usr}}:
\begin{quote}
\begin{small}\begin{verbatim}
#!/bin/bash
/sbin/ipconfig lo 127.0.0.1
/sbin/portmap
/bin/mount /usr
exec /sbin/init "$@" <>/dev/console 2>&1
\end{verbatim}\end{small}
\end{quote}
%% $ XXX SMH: font lock fix :-)
The one slight complication with the above is that
{\path{/sbin/portmap}} is dynamically linked against
{\path{/usr/lib/libwrap.so.0}} Since this is in {\path{/usr}}, it
won't work. This can be solved by copying the file (and link) below
the {\path{/usr}} mount point, and just let the file be `covered' when
the mount happens.
In some installations, where a shared read-only {\path{/usr}} is being
used, it may be desirable to move other large directories over into
the read-only {\path{/usr}}. For example, you might replace
{\path{/bin}}, {\path{/lib}} and {\path{/sbin}} with links into
{\path{/usr/root/bin}}, {\path{/usr/root/lib}} and
{\path{/usr/root/sbin}} respectively. This creates other problems for
running the {\path{/linuxrc}} script, requiring bash, portmap, mount,
ifconfig, and a handful of other shared libraries to be copied below
the mount point --- a simple statically-linked C program would solve
this problem.
+172
View File
@@ -0,0 +1,172 @@
\chapter{Starting Additional Domains}
The first step in creating a new domain is to prepare a root
filesystem for it to boot from. Typically, this might be stored in a
normal partition, an LVM or other volume manager partition, a disk
file or on an NFS server. A simple way to do this is simply to boot
from your standard OS install CD and install the distribution into
another partition on your hard drive.
To start the \xend\ control daemon, type
\begin{quote}
\verb!# xend start!
\end{quote}
If you wish the daemon to start automatically, see the instructions in
Section~\ref{s:xend}. Once the daemon is running, you can use the
\path{xm} tool to monitor and maintain the domains running on your
system. This chapter provides only a brief tutorial. We provide full
details of the \path{xm} tool in the next chapter.
% \section{From the web interface}
%
% Boot the Xen machine and start Xensv (see Chapter~\ref{cha:xensv}
% for more details) using the command: \\
% \verb_# xensv start_ \\
% This will also start Xend (see Chapter~\ref{cha:xend} for more
% information).
%
% The domain management interface will then be available at {\tt
% http://your\_machine:8080/}. This provides a user friendly wizard
% for starting domains and functions for managing running domains.
%
% \section{From the command line}
\section{Creating a Domain Configuration File}
Before you can start an additional domain, you must create a
configuration file. We provide two example files which you can use as
a starting point:
\begin{itemize}
\item \path{/etc/xen/xmexample1} is a simple template configuration
file for describing a single VM.
\item \path{/etc/xen/xmexample2} file is a template description that
is intended to be reused for multiple virtual machines. Setting the
value of the \path{vmid} variable on the \path{xm} command line
fills in parts of this template.
\end{itemize}
Copy one of these files and edit it as appropriate. Typical values
you may wish to edit include:
\begin{quote}
\begin{description}
\item[kernel] Set this to the path of the kernel you compiled for use
with Xen (e.g.\ \path{kernel = `/boot/vmlinuz-2.6-xenU'})
\item[memory] Set this to the size of the domain's memory in megabytes
(e.g.\ \path{memory = 64})
\item[disk] Set the first entry in this list to calculate the offset
of the domain's root partition, based on the domain ID. Set the
second to the location of \path{/usr} if you are sharing it between
domains (e.g.\ \path{disk = [`phy:your\_hard\_drive\%d,sda1,w' \%
(base\_partition\_number + vmid),
`phy:your\_usr\_partition,sda6,r' ]}
\item[dhcp] Uncomment the dhcp variable, so that the domain will
receive its IP address from a DHCP server (e.g.\ \path{dhcp=`dhcp'})
\end{description}
\end{quote}
You may also want to edit the {\bf vif} variable in order to choose
the MAC address of the virtual ethernet interface yourself. For
example:
\begin{quote}
\verb_vif = [`mac=00:06:AA:F6:BB:B3']_
\end{quote}
If you do not set this variable, \xend\ will automatically generate a
random MAC address from an unused range.
\section{Booting the Domain}
The \path{xm} tool provides a variety of commands for managing
domains. Use the \path{create} command to start new domains. Assuming
you've created a configuration file \path{myvmconf} based around
\path{/etc/xen/xmexample2}, to start a domain with virtual machine
ID~1 you should type:
\begin{quote}
\begin{verbatim}
# xm create -c myvmconf vmid=1
\end{verbatim}
\end{quote}
The \path{-c} switch causes \path{xm} to turn into the domain's
console after creation. The \path{vmid=1} sets the \path{vmid}
variable used in the \path{myvmconf} file.
You should see the console boot messages from the new domain appearing
in the terminal in which you typed the command, culminating in a login
prompt.
\section{Example: ttylinux}
Ttylinux is a very small Linux distribution, designed to require very
few resources. We will use it as a concrete example of how to start a
Xen domain. Most users will probably want to install a full-featured
distribution once they have mastered the basics\footnote{ttylinux is
maintained by Pascal Schmidt. You can download source packages from
the distribution's home page: {\tt
http://www.minimalinux.org/ttylinux/}}.
\begin{enumerate}
\item Download and extract the ttylinux disk image from the Files
section of the project's SourceForge site (see
\path{http://sf.net/projects/xen/}).
\item Create a configuration file like the following:
\begin{verbatim}
kernel = "/boot/vmlinuz-2.6-xenU"
memory = 64
name = "ttylinux"
nics = 1
ip = "1.2.3.4"
disk = ['file:/path/to/ttylinux/rootfs,sda1,w']
root = "/dev/sda1 ro"
\end{verbatim}
\item Now start the domain and connect to its console:
\begin{verbatim}
xm create configfile -c
\end{verbatim}
\item Login as root, password root.
\end{enumerate}
\section{Starting / Stopping Domains Automatically}
It is possible to have certain domains start automatically at boot
time and to have dom0 wait for all running domains to shutdown before
it shuts down the system.
To specify a domain is to start at boot-time, place its configuration
file (or a link to it) under \path{/etc/xen/auto/}.
A Sys-V style init script for Red Hat and LSB-compliant systems is
provided and will be automatically copied to \path{/etc/init.d/}
during install. You can then enable it in the appropriate way for
your distribution.
For instance, on Red Hat:
\begin{quote}
\verb_# chkconfig --add xendomains_
\end{quote}
By default, this will start the boot-time domains in runlevels 3, 4
and 5.
You can also use the \path{service} command to run this script
manually, e.g:
\begin{quote}
\verb_# service xendomains start_
Starts all the domains with config files under /etc/xen/auto/.
\end{quote}
\begin{quote}
\verb_# service xendomains stop_
Shuts down ALL running Xen domains.
\end{quote}
+1 -1
View File
@@ -127,7 +127,7 @@ static void *xs_talkv(enum xsd_sockmsg_type type,
return ERR_PTR(err);
for (i = 0; i < num_vecs; i++) {
err = xb_write(iovec[i].iov_base, iovec[i].iov_len);;
err = xb_write(iovec[i].iov_base, iovec[i].iov_len);
if (err)
return ERR_PTR(err);
}
+2 -7
View File
@@ -73,6 +73,8 @@ config XEN_NETDEV_BACKEND
config XEN_TPMDEV_FRONTEND
bool "TPM-device frontend driver"
default n
select TCG_TPM
select TCG_XEN
help
The TPM-device frontend driver.
@@ -109,13 +111,6 @@ config XEN_NETDEV_FRONTEND
dedicated device-driver domain, or your master control domain
(domain 0), then you almost certainly want to say Y here.
config XEN_NETDEV_GRANT
bool "Grant table substrate for network drivers (DANGEROUS)"
default n
help
This introduces the use of grant tables as a data exhange mechanism
between the frontend and backend network drivers.
config XEN_NETDEV_FRONTEND_PIPELINED_TRANSMITTER
bool "Pipelined transmitter (DANGEROUS)"
depends on XEN_NETDEV_FRONTEND
@@ -19,7 +19,6 @@ CONFIG_XEN_NETDEV_BACKEND=y
# CONFIG_XEN_TPMDEV_BACKEND is not set
CONFIG_XEN_BLKDEV_FRONTEND=y
CONFIG_XEN_NETDEV_FRONTEND=y
CONFIG_XEN_NETDEV_GRANT=y
# CONFIG_XEN_NETDEV_FRONTEND_PIPELINED_TRANSMITTER is not set
# CONFIG_XEN_BLKDEV_TAP is not set
# CONFIG_XEN_SHADOW_MODE is not set
@@ -19,7 +19,6 @@ CONFIG_XEN_NETDEV_BACKEND=y
# CONFIG_XEN_TPMDEV_BACKEND is not set
CONFIG_XEN_BLKDEV_FRONTEND=y
CONFIG_XEN_NETDEV_FRONTEND=y
CONFIG_XEN_NETDEV_GRANT=y
# CONFIG_XEN_NETDEV_FRONTEND_PIPELINED_TRANSMITTER is not set
# CONFIG_XEN_BLKDEV_TAP is not set
# CONFIG_XEN_SHADOW_MODE is not set
@@ -16,7 +16,6 @@ CONFIG_NO_IDLE_HZ=y
# CONFIG_XEN_TPMDEV_BACKEND is not set
CONFIG_XEN_BLKDEV_FRONTEND=y
CONFIG_XEN_NETDEV_FRONTEND=y
CONFIG_XEN_NETDEV_GRANT=y
# CONFIG_XEN_NETDEV_FRONTEND_PIPELINED_TRANSMITTER is not set
# CONFIG_XEN_BLKDEV_TAP is not set
# CONFIG_XEN_SHADOW_MODE is not set
@@ -16,7 +16,6 @@ CONFIG_NO_IDLE_HZ=y
# CONFIG_XEN_TPMDEV_BACKEND is not set
CONFIG_XEN_BLKDEV_FRONTEND=y
CONFIG_XEN_NETDEV_FRONTEND=y
CONFIG_XEN_NETDEV_GRANT=y
# CONFIG_XEN_NETDEV_FRONTEND_PIPELINED_TRANSMITTER is not set
# CONFIG_XEN_BLKDEV_TAP is not set
# CONFIG_XEN_SHADOW_MODE is not set
@@ -19,7 +19,6 @@ CONFIG_XEN_NETDEV_BACKEND=y
# CONFIG_XEN_TPMDEV_BACKEND is not set
CONFIG_XEN_BLKDEV_FRONTEND=y
CONFIG_XEN_NETDEV_FRONTEND=y
CONFIG_XEN_NETDEV_GRANT=y
# CONFIG_XEN_NETDEV_FRONTEND_PIPELINED_TRANSMITTER is not set
# CONFIG_XEN_BLKDEV_TAP is not set
# CONFIG_XEN_SHADOW_MODE is not set
@@ -372,7 +371,7 @@ CONFIG_PNP=y
#
CONFIG_ISAPNP=y
# CONFIG_PNPBIOS is not set
CONFIG_PNPACPI=y
# CONFIG_PNPACPI is not set
#
# Block devices
@@ -19,7 +19,6 @@ CONFIG_XEN_NETDEV_BACKEND=y
# CONFIG_XEN_TPMDEV_BACKEND is not set
CONFIG_XEN_BLKDEV_FRONTEND=y
CONFIG_XEN_NETDEV_FRONTEND=y
CONFIG_XEN_NETDEV_GRANT=y
# CONFIG_XEN_NETDEV_FRONTEND_PIPELINED_TRANSMITTER is not set
# CONFIG_XEN_BLKDEV_TAP is not set
# CONFIG_XEN_SHADOW_MODE is not set
@@ -45,12 +45,12 @@ static int direct_remap_area_pte_fn(pte_t *pte,
return 0;
}
int direct_remap_pfn_range(struct mm_struct *mm,
unsigned long address,
unsigned long mfn,
unsigned long size,
pgprot_t prot,
domid_t domid)
static int __direct_remap_pfn_range(struct mm_struct *mm,
unsigned long address,
unsigned long mfn,
unsigned long size,
pgprot_t prot,
domid_t domid)
{
int i;
unsigned long start_address;
@@ -98,6 +98,20 @@ int direct_remap_pfn_range(struct mm_struct *mm,
return 0;
}
int direct_remap_pfn_range(struct vm_area_struct *vma,
unsigned long address,
unsigned long mfn,
unsigned long size,
pgprot_t prot,
domid_t domid)
{
/* Same as remap_pfn_range(). */
vma->vm_flags |= VM_IO | VM_RESERVED;
return __direct_remap_pfn_range(
vma->vm_mm, address, mfn, size, prot, domid);
}
EXPORT_SYMBOL(direct_remap_pfn_range);
@@ -221,8 +235,9 @@ void __iomem * __ioremap(unsigned long phys_addr, unsigned long size, unsigned l
#ifdef __x86_64__
flags |= _PAGE_USER;
#endif
if (direct_remap_pfn_range(&init_mm, (unsigned long) addr, phys_addr>>PAGE_SHIFT,
size, __pgprot(flags), domid)) {
if (__direct_remap_pfn_range(&init_mm, (unsigned long)addr,
phys_addr>>PAGE_SHIFT,
size, __pgprot(flags), domid)) {
vunmap((void __force *) addr);
return NULL;
}
@@ -295,7 +295,7 @@ int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
/* Write-combine setting is ignored, it is changed via the mtrr
* interfaces on this platform.
*/
if (direct_remap_pfn_range(vma->vm_mm, vma->vm_start, vma->vm_pgoff,
if (direct_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
vma->vm_end - vma->vm_start,
vma->vm_page_prot, DOMID_IO))
return -EAGAIN;
+2 -14
View File
@@ -90,22 +90,10 @@ out:
static int mmap_mem(struct file * file, struct vm_area_struct * vma)
{
int uncached;
uncached = uncached_access(file);
if (uncached)
if (uncached_access(file))
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
/* Don't try to swap out physical pages.. */
vma->vm_flags |= VM_RESERVED;
/*
* Don't dump addresses that are not real memory to a core file.
*/
if (uncached)
vma->vm_flags |= VM_IO;
if (direct_remap_pfn_range(vma->vm_mm, vma->vm_start, vma->vm_pgoff,
if (direct_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
vma->vm_end - vma->vm_start,
vma->vm_page_prot, DOMID_IO))
return -EAGAIN;
@@ -182,14 +182,14 @@ gnttab_end_foreign_access(grant_ref_t ref, int readonly)
}
int
gnttab_grant_foreign_transfer(domid_t domid, unsigned long pfn)
gnttab_grant_foreign_transfer(domid_t domid)
{
int ref;
if ( unlikely((ref = get_free_entry()) == -1) )
return -ENOSPC;
shared[ref].frame = pfn;
shared[ref].frame = 0;
shared[ref].domid = domid;
wmb();
shared[ref].flags = GTF_accept_transfer;
@@ -198,10 +198,9 @@ gnttab_grant_foreign_transfer(domid_t domid, unsigned long pfn)
}
void
gnttab_grant_foreign_transfer_ref(grant_ref_t ref, domid_t domid,
unsigned long pfn)
gnttab_grant_foreign_transfer_ref(grant_ref_t ref, domid_t domid)
{
shared[ref].frame = pfn;
shared[ref].frame = 0;
shared[ref].domid = domid;
wmb();
shared[ref].flags = GTF_accept_transfer;
@@ -334,7 +334,7 @@ static void shutdown_handler(struct xenbus_watch *watch, const char *node)
return;
}
xenbus_write("control", "shutdown", "", O_CREAT);
xenbus_write("control", "shutdown", "");
err = xenbus_transaction_end(0);
if (err == -ETIMEDOUT) {
-627
View File
@@ -1,627 +0,0 @@
/*
* Copyright (C) 2004 IBM Corporation
*
* Authors:
* Leendert van Doorn <leendert@watson.ibm.com>
* Dave Safford <safford@watson.ibm.com>
* Reiner Sailer <sailer@watson.ibm.com>
* Kylene Hall <kjhall@us.ibm.com>
*
* Maintained by: <tpmdd_devel@lists.sourceforge.net>
*
* Device driver for TCG/TCPA TPM (trusted platform module).
* Specifications at www.trustedcomputinggroup.org
*
* 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, version 2 of the
* License.
*
* Note, the TPM chip is not interrupt driven (only polling)
* and can have very long timeouts (minutes!). Hence the unusual
* calls to schedule_timeout.
*
*/
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/spinlock.h>
#include "tpm.h"
#define TPM_MINOR 224 /* officially assigned */
#define TPM_BUFSIZE 2048
static LIST_HEAD(tpm_chip_list);
static DEFINE_SPINLOCK(driver_lock);
static int dev_mask[32];
static void user_reader_timeout(unsigned long ptr)
{
struct tpm_chip *chip = (struct tpm_chip *) ptr;
down(&chip->buffer_mutex);
atomic_set(&chip->data_pending, 0);
memset(chip->data_buffer, 0, TPM_BUFSIZE);
up(&chip->buffer_mutex);
}
void tpm_time_expired(unsigned long ptr)
{
int *exp = (int *) ptr;
*exp = 1;
}
EXPORT_SYMBOL_GPL(tpm_time_expired);
/*
* Internal kernel interface to transmit TPM commands
*/
static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
size_t bufsiz)
{
ssize_t len;
u32 count;
__be32 *native_size;
native_size = (__force __be32 *) (buf + 2);
count = be32_to_cpu(*native_size);
if (count == 0)
return -ENODATA;
if (count > bufsiz) {
dev_err(&chip->pci_dev->dev,
"invalid count value %x %zx \n", count, bufsiz);
return -E2BIG;
}
down(&chip->tpm_mutex);
if ((len = chip->vendor->send(chip, (u8 *) buf, count)) < 0) {
dev_err(&chip->pci_dev->dev,
"tpm_transmit: tpm_send: error %zd\n", len);
return len;
}
down(&chip->timer_manipulation_mutex);
chip->time_expired = 0;
init_timer(&chip->device_timer);
chip->device_timer.function = tpm_time_expired;
chip->device_timer.expires = jiffies + 2 * 60 * HZ;
chip->device_timer.data = (unsigned long) &chip->time_expired;
add_timer(&chip->device_timer);
up(&chip->timer_manipulation_mutex);
do {
u8 status = inb(chip->vendor->base + 1);
if ((status & chip->vendor->req_complete_mask) ==
chip->vendor->req_complete_val) {
down(&chip->timer_manipulation_mutex);
del_singleshot_timer_sync(&chip->device_timer);
up(&chip->timer_manipulation_mutex);
goto out_recv;
}
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(TPM_TIMEOUT);
rmb();
} while (!chip->time_expired);
chip->vendor->cancel(chip);
dev_err(&chip->pci_dev->dev, "Time expired\n");
up(&chip->tpm_mutex);
return -EIO;
out_recv:
len = chip->vendor->recv(chip, (u8 *) buf, bufsiz);
if (len < 0)
dev_err(&chip->pci_dev->dev,
"tpm_transmit: tpm_recv: error %zd\n", len);
up(&chip->tpm_mutex);
return len;
}
#define TPM_DIGEST_SIZE 20
#define CAP_PCR_RESULT_SIZE 18
static u8 cap_pcr[] = {
0, 193, /* TPM_TAG_RQU_COMMAND */
0, 0, 0, 22, /* length */
0, 0, 0, 101, /* TPM_ORD_GetCapability */
0, 0, 0, 5,
0, 0, 0, 4,
0, 0, 1, 1
};
#define READ_PCR_RESULT_SIZE 30
static u8 pcrread[] = {
0, 193, /* TPM_TAG_RQU_COMMAND */
0, 0, 0, 14, /* length */
0, 0, 0, 21, /* TPM_ORD_PcrRead */
0, 0, 0, 0 /* PCR index */
};
static ssize_t show_pcrs(struct device *dev, char *buf)
{
u8 data[READ_PCR_RESULT_SIZE];
ssize_t len;
int i, j, index, num_pcrs;
char *str = buf;
struct tpm_chip *chip =
pci_get_drvdata(container_of(dev, struct pci_dev, dev));
if (chip == NULL)
return -ENODEV;
memcpy(data, cap_pcr, sizeof(cap_pcr));
if ((len = tpm_transmit(chip, data, sizeof(data)))
< CAP_PCR_RESULT_SIZE)
return len;
num_pcrs = be32_to_cpu(*((__force __be32 *) (data + 14)));
for (i = 0; i < num_pcrs; i++) {
memcpy(data, pcrread, sizeof(pcrread));
index = cpu_to_be32(i);
memcpy(data + 10, &index, 4);
if ((len = tpm_transmit(chip, data, sizeof(data)))
< READ_PCR_RESULT_SIZE)
return len;
str += sprintf(str, "PCR-%02d: ", i);
for (j = 0; j < TPM_DIGEST_SIZE; j++)
str += sprintf(str, "%02X ", *(data + 10 + j));
str += sprintf(str, "\n");
}
return str - buf;
}
static DEVICE_ATTR(pcrs, S_IRUGO, show_pcrs, NULL);
#define READ_PUBEK_RESULT_SIZE 314
static u8 readpubek[] = {
0, 193, /* TPM_TAG_RQU_COMMAND */
0, 0, 0, 30, /* length */
0, 0, 0, 124, /* TPM_ORD_ReadPubek */
};
static ssize_t show_pubek(struct device *dev, char *buf)
{
u8 data[READ_PUBEK_RESULT_SIZE];
ssize_t len;
__be32 *native_val;
int i;
char *str = buf;
struct tpm_chip *chip =
pci_get_drvdata(container_of(dev, struct pci_dev, dev));
if (chip == NULL)
return -ENODEV;
memcpy(data, readpubek, sizeof(readpubek));
memset(data + sizeof(readpubek), 0, 20); /* zero nonce */
if ((len = tpm_transmit(chip, data, sizeof(data))) <
READ_PUBEK_RESULT_SIZE)
return len;
/*
ignore header 10 bytes
algorithm 32 bits (1 == RSA )
encscheme 16 bits
sigscheme 16 bits
parameters (RSA 12->bytes: keybit, #primes, expbit)
keylenbytes 32 bits
256 byte modulus
ignore checksum 20 bytes
*/
native_val = (__force __be32 *) (data + 34);
str +=
sprintf(str,
"Algorithm: %02X %02X %02X %02X\nEncscheme: %02X %02X\n"
"Sigscheme: %02X %02X\nParameters: %02X %02X %02X %02X"
" %02X %02X %02X %02X %02X %02X %02X %02X\n"
"Modulus length: %d\nModulus: \n",
data[10], data[11], data[12], data[13], data[14],
data[15], data[16], data[17], data[22], data[23],
data[24], data[25], data[26], data[27], data[28],
data[29], data[30], data[31], data[32], data[33],
be32_to_cpu(*native_val)
);
for (i = 0; i < 256; i++) {
str += sprintf(str, "%02X ", data[i + 39]);
if ((i + 1) % 16 == 0)
str += sprintf(str, "\n");
}
return str - buf;
}
static DEVICE_ATTR(pubek, S_IRUGO, show_pubek, NULL);
#define CAP_VER_RESULT_SIZE 18
static u8 cap_version[] = {
0, 193, /* TPM_TAG_RQU_COMMAND */
0, 0, 0, 18, /* length */
0, 0, 0, 101, /* TPM_ORD_GetCapability */
0, 0, 0, 6,
0, 0, 0, 0
};
#define CAP_MANUFACTURER_RESULT_SIZE 18
static u8 cap_manufacturer[] = {
0, 193, /* TPM_TAG_RQU_COMMAND */
0, 0, 0, 22, /* length */
0, 0, 0, 101, /* TPM_ORD_GetCapability */
0, 0, 0, 5,
0, 0, 0, 4,
0, 0, 1, 3
};
static ssize_t show_caps(struct device *dev, char *buf)
{
u8 data[READ_PUBEK_RESULT_SIZE];
ssize_t len;
char *str = buf;
struct tpm_chip *chip =
pci_get_drvdata(container_of(dev, struct pci_dev, dev));
if (chip == NULL)
return -ENODEV;
memcpy(data, cap_manufacturer, sizeof(cap_manufacturer));
if ((len = tpm_transmit(chip, data, sizeof(data))) <
CAP_MANUFACTURER_RESULT_SIZE)
return len;
str += sprintf(str, "Manufacturer: 0x%x\n",
be32_to_cpu(*(data + 14)));
memcpy(data, cap_version, sizeof(cap_version));
if ((len = tpm_transmit(chip, data, sizeof(data))) <
CAP_VER_RESULT_SIZE)
return len;
str +=
sprintf(str, "TCG version: %d.%d\nFirmware version: %d.%d\n",
(int) data[14], (int) data[15], (int) data[16],
(int) data[17]);
return str - buf;
}
static DEVICE_ATTR(caps, S_IRUGO, show_caps, NULL);
/*
* Device file system interface to the TPM
*/
int tpm_open(struct inode *inode, struct file *file)
{
int rc = 0, minor = iminor(inode);
struct tpm_chip *chip = NULL, *pos;
spin_lock(&driver_lock);
list_for_each_entry(pos, &tpm_chip_list, list) {
if (pos->vendor->miscdev.minor == minor) {
chip = pos;
break;
}
}
if (chip == NULL) {
rc = -ENODEV;
goto err_out;
}
if (chip->num_opens) {
dev_dbg(&chip->pci_dev->dev,
"Another process owns this TPM\n");
rc = -EBUSY;
goto err_out;
}
chip->num_opens++;
pci_dev_get(chip->pci_dev);
spin_unlock(&driver_lock);
chip->data_buffer = kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL);
if (chip->data_buffer == NULL) {
chip->num_opens--;
pci_dev_put(chip->pci_dev);
return -ENOMEM;
}
atomic_set(&chip->data_pending, 0);
file->private_data = chip;
return 0;
err_out:
spin_unlock(&driver_lock);
return rc;
}
EXPORT_SYMBOL_GPL(tpm_open);
int tpm_release(struct inode *inode, struct file *file)
{
struct tpm_chip *chip = file->private_data;
file->private_data = NULL;
spin_lock(&driver_lock);
chip->num_opens--;
spin_unlock(&driver_lock);
down(&chip->timer_manipulation_mutex);
if (timer_pending(&chip->user_read_timer))
del_singleshot_timer_sync(&chip->user_read_timer);
else if (timer_pending(&chip->device_timer))
del_singleshot_timer_sync(&chip->device_timer);
up(&chip->timer_manipulation_mutex);
kfree(chip->data_buffer);
atomic_set(&chip->data_pending, 0);
pci_dev_put(chip->pci_dev);
return 0;
}
EXPORT_SYMBOL_GPL(tpm_release);
ssize_t tpm_write(struct file * file, const char __user * buf,
size_t size, loff_t * off)
{
struct tpm_chip *chip = file->private_data;
int in_size = size, out_size;
/* cannot perform a write until the read has cleared
either via tpm_read or a user_read_timer timeout */
while (atomic_read(&chip->data_pending) != 0) {
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(TPM_TIMEOUT);
}
down(&chip->buffer_mutex);
if (in_size > TPM_BUFSIZE)
in_size = TPM_BUFSIZE;
if (copy_from_user
(chip->data_buffer, (void __user *) buf, in_size)) {
up(&chip->buffer_mutex);
return -EFAULT;
}
/* atomic tpm command send and result receive */
out_size = tpm_transmit(chip, chip->data_buffer, TPM_BUFSIZE);
atomic_set(&chip->data_pending, out_size);
atomic_set(&chip->data_position, 0);
up(&chip->buffer_mutex);
/* Set a timeout by which the reader must come claim the result */
down(&chip->timer_manipulation_mutex);
init_timer(&chip->user_read_timer);
chip->user_read_timer.function = user_reader_timeout;
chip->user_read_timer.data = (unsigned long) chip;
chip->user_read_timer.expires = jiffies + (60 * HZ);
add_timer(&chip->user_read_timer);
up(&chip->timer_manipulation_mutex);
return in_size;
}
EXPORT_SYMBOL_GPL(tpm_write);
ssize_t tpm_read(struct file * file, char __user * buf,
size_t size, loff_t * off)
{
struct tpm_chip *chip = file->private_data;
int ret_size = -ENODATA;
int pos, pending = 0;
down(&chip->buffer_mutex);
ret_size = atomic_read(&chip->data_pending);
if ( ret_size > 0 ) { /* Result available */
if (size < ret_size)
ret_size = size;
pos = atomic_read(&chip->data_position);
if (copy_to_user((void __user *) buf,
&chip->data_buffer[pos], ret_size)) {
ret_size = -EFAULT;
} else {
pending = atomic_read(&chip->data_pending) - ret_size;
if ( pending ) {
atomic_set( &chip->data_pending, pending );
atomic_set( &chip->data_position, pos+ret_size );
}
}
}
up(&chip->buffer_mutex);
if ( ret_size <= 0 || pending == 0 ) {
atomic_set( &chip->data_pending, 0 );
down(&chip->timer_manipulation_mutex);
del_singleshot_timer_sync(&chip->user_read_timer);
up(&chip->timer_manipulation_mutex);
}
return ret_size;
}
EXPORT_SYMBOL_GPL(tpm_read);
void __devexit tpm_remove(struct pci_dev *pci_dev)
{
struct tpm_chip *chip = pci_get_drvdata(pci_dev);
if (chip == NULL) {
dev_err(&pci_dev->dev, "No device data found\n");
return;
}
spin_lock(&driver_lock);
list_del(&chip->list);
spin_unlock(&driver_lock);
pci_set_drvdata(pci_dev, NULL);
misc_deregister(&chip->vendor->miscdev);
device_remove_file(&pci_dev->dev, &dev_attr_pubek);
device_remove_file(&pci_dev->dev, &dev_attr_pcrs);
device_remove_file(&pci_dev->dev, &dev_attr_caps);
pci_disable_device(pci_dev);
dev_mask[chip->dev_num / 32] &= !(1 << (chip->dev_num % 32));
kfree(chip);
pci_dev_put(pci_dev);
}
EXPORT_SYMBOL_GPL(tpm_remove);
static u8 savestate[] = {
0, 193, /* TPM_TAG_RQU_COMMAND */
0, 0, 0, 10, /* blob length (in bytes) */
0, 0, 0, 152 /* TPM_ORD_SaveState */
};
/*
* We are about to suspend. Save the TPM state
* so that it can be restored.
*/
int tpm_pm_suspend(struct pci_dev *pci_dev, pm_message_t pm_state)
{
struct tpm_chip *chip = pci_get_drvdata(pci_dev);
if (chip == NULL)
return -ENODEV;
tpm_transmit(chip, savestate, sizeof(savestate));
return 0;
}
EXPORT_SYMBOL_GPL(tpm_pm_suspend);
/*
* Resume from a power safe. The BIOS already restored
* the TPM state.
*/
int tpm_pm_resume(struct pci_dev *pci_dev)
{
struct tpm_chip *chip = pci_get_drvdata(pci_dev);
if (chip == NULL)
return -ENODEV;
return 0;
}
EXPORT_SYMBOL_GPL(tpm_pm_resume);
/*
* Called from tpm_<specific>.c probe function only for devices
* the driver has determined it should claim. Prior to calling
* this function the specific probe function has called pci_enable_device
* upon errant exit from this function specific probe function should call
* pci_disable_device
*/
int tpm_register_hardware(struct pci_dev *pci_dev,
struct tpm_vendor_specific *entry)
{
char devname[7];
struct tpm_chip *chip;
int i, j;
/* Driver specific per-device data */
chip = kmalloc(sizeof(*chip), GFP_KERNEL);
if (chip == NULL)
return -ENOMEM;
memset(chip, 0, sizeof(struct tpm_chip));
init_MUTEX(&chip->buffer_mutex);
init_MUTEX(&chip->tpm_mutex);
init_MUTEX(&chip->timer_manipulation_mutex);
INIT_LIST_HEAD(&chip->list);
chip->vendor = entry;
chip->dev_num = -1;
for (i = 0; i < 32; i++)
for (j = 0; j < 8; j++)
if ((dev_mask[i] & (1 << j)) == 0) {
chip->dev_num = i * 32 + j;
dev_mask[i] |= 1 << j;
goto dev_num_search_complete;
}
dev_num_search_complete:
if (chip->dev_num < 0) {
dev_err(&pci_dev->dev,
"No available tpm device numbers\n");
kfree(chip);
return -ENODEV;
} else if (chip->dev_num == 0)
chip->vendor->miscdev.minor = TPM_MINOR;
else
chip->vendor->miscdev.minor = MISC_DYNAMIC_MINOR;
snprintf(devname, sizeof(devname), "%s%d", "tpm", chip->dev_num);
chip->vendor->miscdev.name = devname;
chip->vendor->miscdev.dev = &(pci_dev->dev);
chip->pci_dev = pci_dev_get(pci_dev);
if (misc_register(&chip->vendor->miscdev)) {
dev_err(&chip->pci_dev->dev,
"unable to misc_register %s, minor %d\n",
chip->vendor->miscdev.name,
chip->vendor->miscdev.minor);
pci_dev_put(pci_dev);
kfree(chip);
dev_mask[i] &= !(1 << j);
return -ENODEV;
}
pci_set_drvdata(pci_dev, chip);
list_add(&chip->list, &tpm_chip_list);
device_create_file(&pci_dev->dev, &dev_attr_pubek);
device_create_file(&pci_dev->dev, &dev_attr_pcrs);
device_create_file(&pci_dev->dev, &dev_attr_caps);
return 0;
}
EXPORT_SYMBOL_GPL(tpm_register_hardware);
static int __init init_tpm(void)
{
return 0;
}
static void __exit cleanup_tpm(void)
{
}
module_init(init_tpm);
module_exit(cleanup_tpm);
MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
MODULE_DESCRIPTION("TPM Driver");
MODULE_VERSION("2.0");
MODULE_LICENSE("GPL");
@@ -1,92 +0,0 @@
/*
* Copyright (C) 2004 IBM Corporation
*
* Authors:
* Leendert van Doorn <leendert@watson.ibm.com>
* Dave Safford <safford@watson.ibm.com>
* Reiner Sailer <sailer@watson.ibm.com>
* Kylene Hall <kjhall@us.ibm.com>
*
* Maintained by: <tpmdd_devel@lists.sourceforge.net>
*
* Device driver for TCG/TCPA TPM (trusted platform module).
* Specifications at www.trustedcomputinggroup.org
*
* 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, version 2 of the
* License.
*
*/
#include <linux/module.h>
#include <linux/version.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#define TPM_TIMEOUT msecs_to_jiffies(5)
/* TPM addresses */
#define TPM_ADDR 0x4E
#define TPM_DATA 0x4F
struct tpm_chip;
struct tpm_vendor_specific {
u8 req_complete_mask;
u8 req_complete_val;
u16 base; /* TPM base address */
int (*recv) (struct tpm_chip *, u8 *, size_t);
int (*send) (struct tpm_chip *, u8 *, size_t);
void (*cancel) (struct tpm_chip *);
struct miscdevice miscdev;
};
struct tpm_chip {
struct pci_dev *pci_dev; /* PCI device stuff */
int dev_num; /* /dev/tpm# */
int num_opens; /* only one allowed */
int time_expired;
/* Data passed to and from the tpm via the read/write calls */
u8 *data_buffer;
atomic_t data_pending;
atomic_t data_position;
struct semaphore buffer_mutex;
struct timer_list user_read_timer; /* user needs to claim result */
struct semaphore tpm_mutex; /* tpm is processing */
struct timer_list device_timer; /* tpm is processing */
struct semaphore timer_manipulation_mutex;
struct tpm_vendor_specific *vendor;
struct list_head list;
};
static inline int tpm_read_index(int index)
{
outb(index, TPM_ADDR);
return inb(TPM_DATA) & 0xFF;
}
static inline void tpm_write_index(int index, int value)
{
outb(index, TPM_ADDR);
outb(value & 0xFF, TPM_DATA);
}
extern void tpm_time_expired(unsigned long);
extern int tpm_register_hardware(struct pci_dev *,
struct tpm_vendor_specific *);
extern int tpm_open(struct inode *, struct file *);
extern int tpm_release(struct inode *, struct file *);
extern ssize_t tpm_write(struct file *, const char __user *, size_t,
loff_t *);
extern ssize_t tpm_read(struct file *, char __user *, size_t, loff_t *);
extern void __devexit tpm_remove(struct pci_dev *);
extern int tpm_pm_suspend(struct pci_dev *, pm_message_t);
extern int tpm_pm_resume(struct pci_dev *);
@@ -1,220 +0,0 @@
/*
* Copyright (C) 2004 IBM Corporation
*
* Authors:
* Leendert van Doorn <leendert@watson.ibm.com>
* Dave Safford <safford@watson.ibm.com>
* Reiner Sailer <sailer@watson.ibm.com>
* Kylene Hall <kjhall@us.ibm.com>
*
* Maintained by: <tpmdd_devel@lists.sourceforge.net>
*
* Device driver for TCG/TCPA TPM (trusted platform module).
* Specifications at www.trustedcomputinggroup.org
*
* 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, version 2 of the
* License.
*
*/
#include "tpm.h"
/* Atmel definitions */
enum tpm_atmel_addr {
TPM_ATMEL_BASE_ADDR_LO = 0x08,
TPM_ATMEL_BASE_ADDR_HI = 0x09
};
/* write status bits */
#define ATML_STATUS_ABORT 0x01
#define ATML_STATUS_LASTBYTE 0x04
/* read status bits */
#define ATML_STATUS_BUSY 0x01
#define ATML_STATUS_DATA_AVAIL 0x02
#define ATML_STATUS_REWRITE 0x04
static int tpm_atml_recv(struct tpm_chip *chip, u8 * buf, size_t count)
{
u8 status, *hdr = buf;
u32 size;
int i;
__be32 *native_size;
/* start reading header */
if (count < 6)
return -EIO;
for (i = 0; i < 6; i++) {
status = inb(chip->vendor->base + 1);
if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
dev_err(&chip->pci_dev->dev,
"error reading header\n");
return -EIO;
}
*buf++ = inb(chip->vendor->base);
}
/* size of the data received */
native_size = (__force __be32 *) (hdr + 2);
size = be32_to_cpu(*native_size);
if (count < size) {
dev_err(&chip->pci_dev->dev,
"Recv size(%d) less than available space\n", size);
for (; i < size; i++) { /* clear the waiting data anyway */
status = inb(chip->vendor->base + 1);
if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
dev_err(&chip->pci_dev->dev,
"error reading data\n");
return -EIO;
}
}
return -EIO;
}
/* read all the data available */
for (; i < size; i++) {
status = inb(chip->vendor->base + 1);
if ((status & ATML_STATUS_DATA_AVAIL) == 0) {
dev_err(&chip->pci_dev->dev,
"error reading data\n");
return -EIO;
}
*buf++ = inb(chip->vendor->base);
}
/* make sure data available is gone */
status = inb(chip->vendor->base + 1);
if (status & ATML_STATUS_DATA_AVAIL) {
dev_err(&chip->pci_dev->dev, "data available is stuck\n");
return -EIO;
}
return size;
}
static int tpm_atml_send(struct tpm_chip *chip, u8 * buf, size_t count)
{
int i;
dev_dbg(&chip->pci_dev->dev, "tpm_atml_send: ");
for (i = 0; i < count; i++) {
dev_dbg(&chip->pci_dev->dev, "0x%x(%d) ", buf[i], buf[i]);
outb(buf[i], chip->vendor->base);
}
return count;
}
static void tpm_atml_cancel(struct tpm_chip *chip)
{
outb(ATML_STATUS_ABORT, chip->vendor->base + 1);
}
static struct file_operations atmel_ops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.open = tpm_open,
.read = tpm_read,
.write = tpm_write,
.release = tpm_release,
};
static struct tpm_vendor_specific tpm_atmel = {
.recv = tpm_atml_recv,
.send = tpm_atml_send,
.cancel = tpm_atml_cancel,
.req_complete_mask = ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL,
.req_complete_val = ATML_STATUS_DATA_AVAIL,
.miscdev = { .fops = &atmel_ops, },
};
static int __devinit tpm_atml_init(struct pci_dev *pci_dev,
const struct pci_device_id *pci_id)
{
u8 version[4];
int rc = 0;
int lo, hi;
if (pci_enable_device(pci_dev))
return -EIO;
lo = tpm_read_index( TPM_ATMEL_BASE_ADDR_LO );
hi = tpm_read_index( TPM_ATMEL_BASE_ADDR_HI );
tpm_atmel.base = (hi<<8)|lo;
dev_dbg( &pci_dev->dev, "Operating with base: 0x%x\n", tpm_atmel.base);
/* verify that it is an Atmel part */
if (tpm_read_index(4) != 'A' || tpm_read_index(5) != 'T'
|| tpm_read_index(6) != 'M' || tpm_read_index(7) != 'L') {
rc = -ENODEV;
goto out_err;
}
/* query chip for its version number */
if ((version[0] = tpm_read_index(0x00)) != 0xFF) {
version[1] = tpm_read_index(0x01);
version[2] = tpm_read_index(0x02);
version[3] = tpm_read_index(0x03);
} else {
dev_info(&pci_dev->dev, "version query failed\n");
rc = -ENODEV;
goto out_err;
}
if ((rc = tpm_register_hardware(pci_dev, &tpm_atmel)) < 0)
goto out_err;
dev_info(&pci_dev->dev,
"Atmel TPM version %d.%d.%d.%d\n", version[0], version[1],
version[2], version[3]);
return 0;
out_err:
pci_disable_device(pci_dev);
return rc;
}
static struct pci_device_id tpm_pci_tbl[] __devinitdata = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0)},
{PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_LPC)},
{0,}
};
MODULE_DEVICE_TABLE(pci, tpm_pci_tbl);
static struct pci_driver atmel_pci_driver = {
.name = "tpm_atmel",
.id_table = tpm_pci_tbl,
.probe = tpm_atml_init,
.remove = __devexit_p(tpm_remove),
.suspend = tpm_pm_suspend,
.resume = tpm_pm_resume,
};
static int __init init_atmel(void)
{
return pci_register_driver(&atmel_pci_driver);
}
static void __exit cleanup_atmel(void)
{
pci_unregister_driver(&atmel_pci_driver);
}
module_init(init_atmel);
module_exit(cleanup_atmel);
MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
MODULE_DESCRIPTION("TPM Driver");
MODULE_VERSION("2.0");
MODULE_LICENSE("GPL");
@@ -1,377 +0,0 @@
/*
* Copyright (C) 2004 IBM Corporation
*
* Authors:
* Leendert van Doorn <leendert@watson.ibm.com>
* Dave Safford <safford@watson.ibm.com>
* Reiner Sailer <sailer@watson.ibm.com>
* Kylene Hall <kjhall@us.ibm.com>
*
* Maintained by: <tpmdd_devel@lists.sourceforge.net>
*
* Device driver for TCG/TCPA TPM (trusted platform module).
* Specifications at www.trustedcomputinggroup.org
*
* 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, version 2 of the
* License.
*
*/
#include "tpm.h"
/* National definitions */
#define TPM_NSC_BASE 0x360
#define TPM_NSC_IRQ 0x07
#define TPM_NSC_BASE0_HI 0x60
#define TPM_NSC_BASE0_LO 0x61
#define TPM_NSC_BASE1_HI 0x62
#define TPM_NSC_BASE1_LO 0x63
#define NSC_LDN_INDEX 0x07
#define NSC_SID_INDEX 0x20
#define NSC_LDC_INDEX 0x30
#define NSC_DIO_INDEX 0x60
#define NSC_CIO_INDEX 0x62
#define NSC_IRQ_INDEX 0x70
#define NSC_ITS_INDEX 0x71
#define NSC_STATUS 0x01
#define NSC_COMMAND 0x01
#define NSC_DATA 0x00
/* status bits */
#define NSC_STATUS_OBF 0x01 /* output buffer full */
#define NSC_STATUS_IBF 0x02 /* input buffer full */
#define NSC_STATUS_F0 0x04 /* F0 */
#define NSC_STATUS_A2 0x08 /* A2 */
#define NSC_STATUS_RDY 0x10 /* ready to receive command */
#define NSC_STATUS_IBR 0x20 /* ready to receive data */
/* command bits */
#define NSC_COMMAND_NORMAL 0x01 /* normal mode */
#define NSC_COMMAND_EOC 0x03
#define NSC_COMMAND_CANCEL 0x22
/*
* Wait for a certain status to appear
*/
static int wait_for_stat(struct tpm_chip *chip, u8 mask, u8 val, u8 * data)
{
int expired = 0;
struct timer_list status_timer =
TIMER_INITIALIZER(tpm_time_expired, jiffies + 10 * HZ,
(unsigned long) &expired);
/* status immediately available check */
*data = inb(chip->vendor->base + NSC_STATUS);
if ((*data & mask) == val)
return 0;
/* wait for status */
add_timer(&status_timer);
do {
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(TPM_TIMEOUT);
*data = inb(chip->vendor->base + 1);
if ((*data & mask) == val) {
del_singleshot_timer_sync(&status_timer);
return 0;
}
}
while (!expired);
return -EBUSY;
}
static int nsc_wait_for_ready(struct tpm_chip *chip)
{
int status;
int expired = 0;
struct timer_list status_timer =
TIMER_INITIALIZER(tpm_time_expired, jiffies + 100,
(unsigned long) &expired);
/* status immediately available check */
status = inb(chip->vendor->base + NSC_STATUS);
if (status & NSC_STATUS_OBF)
status = inb(chip->vendor->base + NSC_DATA);
if (status & NSC_STATUS_RDY)
return 0;
/* wait for status */
add_timer(&status_timer);
do {
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(TPM_TIMEOUT);
status = inb(chip->vendor->base + NSC_STATUS);
if (status & NSC_STATUS_OBF)
status = inb(chip->vendor->base + NSC_DATA);
if (status & NSC_STATUS_RDY) {
del_singleshot_timer_sync(&status_timer);
return 0;
}
}
while (!expired);
dev_info(&chip->pci_dev->dev, "wait for ready failed\n");
return -EBUSY;
}
static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count)
{
u8 *buffer = buf;
u8 data, *p;
u32 size;
__be32 *native_size;
if (count < 6)
return -EIO;
if (wait_for_stat(chip, NSC_STATUS_F0, NSC_STATUS_F0, &data) < 0) {
dev_err(&chip->pci_dev->dev, "F0 timeout\n");
return -EIO;
}
if ((data =
inb(chip->vendor->base + NSC_DATA)) != NSC_COMMAND_NORMAL) {
dev_err(&chip->pci_dev->dev, "not in normal mode (0x%x)\n",
data);
return -EIO;
}
/* read the whole packet */
for (p = buffer; p < &buffer[count]; p++) {
if (wait_for_stat
(chip, NSC_STATUS_OBF, NSC_STATUS_OBF, &data) < 0) {
dev_err(&chip->pci_dev->dev,
"OBF timeout (while reading data)\n");
return -EIO;
}
if (data & NSC_STATUS_F0)
break;
*p = inb(chip->vendor->base + NSC_DATA);
}
if ((data & NSC_STATUS_F0) == 0) {
dev_err(&chip->pci_dev->dev, "F0 not set\n");
return -EIO;
}
if ((data = inb(chip->vendor->base + NSC_DATA)) != NSC_COMMAND_EOC) {
dev_err(&chip->pci_dev->dev,
"expected end of command(0x%x)\n", data);
return -EIO;
}
native_size = (__force __be32 *) (buf + 2);
size = be32_to_cpu(*native_size);
if (count < size)
return -EIO;
return size;
}
static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count)
{
u8 data;
int i;
/*
* If we hit the chip with back to back commands it locks up
* and never set IBF. Hitting it with this "hammer" seems to
* fix it. Not sure why this is needed, we followed the flow
* chart in the manual to the letter.
*/
outb(NSC_COMMAND_CANCEL, chip->vendor->base + NSC_COMMAND);
if (nsc_wait_for_ready(chip) != 0)
return -EIO;
if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
dev_err(&chip->pci_dev->dev, "IBF timeout\n");
return -EIO;
}
outb(NSC_COMMAND_NORMAL, chip->vendor->base + NSC_COMMAND);
if (wait_for_stat(chip, NSC_STATUS_IBR, NSC_STATUS_IBR, &data) < 0) {
dev_err(&chip->pci_dev->dev, "IBR timeout\n");
return -EIO;
}
for (i = 0; i < count; i++) {
if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
dev_err(&chip->pci_dev->dev,
"IBF timeout (while writing data)\n");
return -EIO;
}
outb(buf[i], chip->vendor->base + NSC_DATA);
}
if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) {
dev_err(&chip->pci_dev->dev, "IBF timeout\n");
return -EIO;
}
outb(NSC_COMMAND_EOC, chip->vendor->base + NSC_COMMAND);
return count;
}
static void tpm_nsc_cancel(struct tpm_chip *chip)
{
outb(NSC_COMMAND_CANCEL, chip->vendor->base + NSC_COMMAND);
}
static struct file_operations nsc_ops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.open = tpm_open,
.read = tpm_read,
.write = tpm_write,
.release = tpm_release,
};
static struct tpm_vendor_specific tpm_nsc = {
.recv = tpm_nsc_recv,
.send = tpm_nsc_send,
.cancel = tpm_nsc_cancel,
.req_complete_mask = NSC_STATUS_OBF,
.req_complete_val = NSC_STATUS_OBF,
.miscdev = { .fops = &nsc_ops, },
};
static int __devinit tpm_nsc_init(struct pci_dev *pci_dev,
const struct pci_device_id *pci_id)
{
int rc = 0;
int lo, hi;
hi = tpm_read_index(TPM_NSC_BASE0_HI);
lo = tpm_read_index(TPM_NSC_BASE0_LO);
tpm_nsc.base = (hi<<8) | lo;
if (pci_enable_device(pci_dev))
return -EIO;
/* verify that it is a National part (SID) */
if (tpm_read_index(NSC_SID_INDEX) != 0xEF) {
rc = -ENODEV;
goto out_err;
}
dev_dbg(&pci_dev->dev, "NSC TPM detected\n");
dev_dbg(&pci_dev->dev,
"NSC LDN 0x%x, SID 0x%x, SRID 0x%x\n",
tpm_read_index(0x07), tpm_read_index(0x20),
tpm_read_index(0x27));
dev_dbg(&pci_dev->dev,
"NSC SIOCF1 0x%x SIOCF5 0x%x SIOCF6 0x%x SIOCF8 0x%x\n",
tpm_read_index(0x21), tpm_read_index(0x25),
tpm_read_index(0x26), tpm_read_index(0x28));
dev_dbg(&pci_dev->dev, "NSC IO Base0 0x%x\n",
(tpm_read_index(0x60) << 8) | tpm_read_index(0x61));
dev_dbg(&pci_dev->dev, "NSC IO Base1 0x%x\n",
(tpm_read_index(0x62) << 8) | tpm_read_index(0x63));
dev_dbg(&pci_dev->dev, "NSC Interrupt number and wakeup 0x%x\n",
tpm_read_index(0x70));
dev_dbg(&pci_dev->dev, "NSC IRQ type select 0x%x\n",
tpm_read_index(0x71));
dev_dbg(&pci_dev->dev,
"NSC DMA channel select0 0x%x, select1 0x%x\n",
tpm_read_index(0x74), tpm_read_index(0x75));
dev_dbg(&pci_dev->dev,
"NSC Config "
"0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
tpm_read_index(0xF0), tpm_read_index(0xF1),
tpm_read_index(0xF2), tpm_read_index(0xF3),
tpm_read_index(0xF4), tpm_read_index(0xF5),
tpm_read_index(0xF6), tpm_read_index(0xF7),
tpm_read_index(0xF8), tpm_read_index(0xF9));
dev_info(&pci_dev->dev,
"NSC PC21100 TPM revision %d\n",
tpm_read_index(0x27) & 0x1F);
if (tpm_read_index(NSC_LDC_INDEX) == 0)
dev_info(&pci_dev->dev, ": NSC TPM not active\n");
/* select PM channel 1 */
tpm_write_index(NSC_LDN_INDEX, 0x12);
tpm_read_index(NSC_LDN_INDEX);
/* disable the DPM module */
tpm_write_index(NSC_LDC_INDEX, 0);
tpm_read_index(NSC_LDC_INDEX);
/* set the data register base addresses */
tpm_write_index(NSC_DIO_INDEX, TPM_NSC_BASE >> 8);
tpm_write_index(NSC_DIO_INDEX + 1, TPM_NSC_BASE);
tpm_read_index(NSC_DIO_INDEX);
tpm_read_index(NSC_DIO_INDEX + 1);
/* set the command register base addresses */
tpm_write_index(NSC_CIO_INDEX, (TPM_NSC_BASE + 1) >> 8);
tpm_write_index(NSC_CIO_INDEX + 1, (TPM_NSC_BASE + 1));
tpm_read_index(NSC_DIO_INDEX);
tpm_read_index(NSC_DIO_INDEX + 1);
/* set the interrupt number to be used for the host interface */
tpm_write_index(NSC_IRQ_INDEX, TPM_NSC_IRQ);
tpm_write_index(NSC_ITS_INDEX, 0x00);
tpm_read_index(NSC_IRQ_INDEX);
/* enable the DPM module */
tpm_write_index(NSC_LDC_INDEX, 0x01);
tpm_read_index(NSC_LDC_INDEX);
if ((rc = tpm_register_hardware(pci_dev, &tpm_nsc)) < 0)
goto out_err;
return 0;
out_err:
pci_disable_device(pci_dev);
return rc;
}
static struct pci_device_id tpm_pci_tbl[] __devinitdata = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0)},
{PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_LPC)},
{0,}
};
MODULE_DEVICE_TABLE(pci, tpm_pci_tbl);
static struct pci_driver nsc_pci_driver = {
.name = "tpm_nsc",
.id_table = tpm_pci_tbl,
.probe = tpm_nsc_init,
.remove = __devexit_p(tpm_remove),
.suspend = tpm_pm_suspend,
.resume = tpm_pm_resume,
};
static int __init init_nsc(void)
{
return pci_register_driver(&nsc_pci_driver);
}
static void __exit cleanup_nsc(void)
{
pci_unregister_driver(&nsc_pci_driver);
}
module_init(init_nsc);
module_exit(cleanup_nsc);
MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
MODULE_DESCRIPTION("TPM Driver");
MODULE_VERSION("2.0");
MODULE_LICENSE("GPL");
@@ -1,4 +1,5 @@
obj-y += util.o
obj-y += console/
obj-y += evtchn/
@@ -362,7 +362,10 @@ static void watch_target(struct xenbus_watch *watch, const char *node)
return;
}
set_new_target(new_target >> PAGE_SHIFT);
/* The given memory/target value is in KiB, so it needs converting to
pages. PAGE_SHIFT converts bytes to pages, hence PAGE_SHIFT - 10.
*/
set_new_target(new_target >> (PAGE_SHIFT - 10));
}
+305 -310
View File
@@ -28,12 +28,12 @@
#define BATCH_PER_DOMAIN 16
static unsigned long mmap_vstart;
#define MMAP_PAGES \
(MAX_PENDING_REQS * BLKIF_MAX_SEGMENTS_PER_REQUEST)
#define MMAP_VADDR(_req,_seg) \
(mmap_vstart + \
((_req) * BLKIF_MAX_SEGMENTS_PER_REQUEST * PAGE_SIZE) + \
((_seg) * PAGE_SIZE))
#define MMAP_PAGES \
(MAX_PENDING_REQS * BLKIF_MAX_SEGMENTS_PER_REQUEST)
#define MMAP_VADDR(_req,_seg) \
(mmap_vstart + \
((_req) * BLKIF_MAX_SEGMENTS_PER_REQUEST * PAGE_SIZE) + \
((_seg) * PAGE_SIZE))
/*
* Each outstanding request that we've passed to the lower device layers has a
@@ -42,12 +42,12 @@ static unsigned long mmap_vstart;
* response queued for it, with the saved 'id' passed back.
*/
typedef struct {
blkif_t *blkif;
unsigned long id;
int nr_pages;
atomic_t pendcnt;
unsigned short operation;
int status;
blkif_t *blkif;
unsigned long id;
int nr_pages;
atomic_t pendcnt;
unsigned short operation;
int status;
} pending_req_t;
/*
@@ -68,14 +68,13 @@ static PEND_RING_IDX pending_prod, pending_cons;
static request_queue_t *plugged_queue;
static inline void flush_plugged_queue(void)
{
request_queue_t *q = plugged_queue;
if ( q != NULL )
{
if ( q->unplug_fn != NULL )
q->unplug_fn(q);
blk_put_queue(q);
plugged_queue = NULL;
}
request_queue_t *q = plugged_queue;
if (q != NULL) {
if ( q->unplug_fn != NULL )
q->unplug_fn(q);
blk_put_queue(q);
plugged_queue = NULL;
}
}
/* When using grant tables to map a frame for device access then the
@@ -106,24 +105,23 @@ static void make_response(blkif_t *blkif, unsigned long id,
static void fast_flush_area(int idx, int nr_pages)
{
struct gnttab_unmap_grant_ref unmap[BLKIF_MAX_SEGMENTS_PER_REQUEST];
unsigned int i, invcount = 0;
u16 handle;
struct gnttab_unmap_grant_ref unmap[BLKIF_MAX_SEGMENTS_PER_REQUEST];
unsigned int i, invcount = 0;
u16 handle;
for ( i = 0; i < nr_pages; i++ )
{
if ( BLKBACK_INVALID_HANDLE != ( handle = pending_handle(idx, i) ) )
{
unmap[i].host_addr = MMAP_VADDR(idx, i);
unmap[i].dev_bus_addr = 0;
unmap[i].handle = handle;
pending_handle(idx, i) = BLKBACK_INVALID_HANDLE;
invcount++;
}
}
if ( unlikely(HYPERVISOR_grant_table_op(
GNTTABOP_unmap_grant_ref, unmap, invcount)))
BUG();
for (i = 0; i < nr_pages; i++) {
handle = pending_handle(idx, i);
if (handle == BLKBACK_INVALID_HANDLE)
continue;
unmap[i].host_addr = MMAP_VADDR(idx, i);
unmap[i].dev_bus_addr = 0;
unmap[i].handle = handle;
pending_handle(idx, i) = BLKBACK_INVALID_HANDLE;
invcount++;
}
BUG_ON(HYPERVISOR_grant_table_op(
GNTTABOP_unmap_grant_ref, unmap, invcount));
}
@@ -136,34 +134,38 @@ static spinlock_t blkio_schedule_list_lock;
static int __on_blkdev_list(blkif_t *blkif)
{
return blkif->blkdev_list.next != NULL;
return blkif->blkdev_list.next != NULL;
}
static void remove_from_blkdev_list(blkif_t *blkif)
{
unsigned long flags;
if ( !__on_blkdev_list(blkif) ) return;
spin_lock_irqsave(&blkio_schedule_list_lock, flags);
if ( __on_blkdev_list(blkif) )
{
list_del(&blkif->blkdev_list);
blkif->blkdev_list.next = NULL;
blkif_put(blkif);
}
spin_unlock_irqrestore(&blkio_schedule_list_lock, flags);
unsigned long flags;
if (!__on_blkdev_list(blkif))
return;
spin_lock_irqsave(&blkio_schedule_list_lock, flags);
if (__on_blkdev_list(blkif)) {
list_del(&blkif->blkdev_list);
blkif->blkdev_list.next = NULL;
blkif_put(blkif);
}
spin_unlock_irqrestore(&blkio_schedule_list_lock, flags);
}
static void add_to_blkdev_list_tail(blkif_t *blkif)
{
unsigned long flags;
if ( __on_blkdev_list(blkif) ) return;
spin_lock_irqsave(&blkio_schedule_list_lock, flags);
if ( !__on_blkdev_list(blkif) && (blkif->status == CONNECTED) )
{
list_add_tail(&blkif->blkdev_list, &blkio_schedule_list);
blkif_get(blkif);
}
spin_unlock_irqrestore(&blkio_schedule_list_lock, flags);
unsigned long flags;
if (__on_blkdev_list(blkif))
return;
spin_lock_irqsave(&blkio_schedule_list_lock, flags);
if (!__on_blkdev_list(blkif) && (blkif->status == CONNECTED)) {
list_add_tail(&blkif->blkdev_list, &blkio_schedule_list);
blkif_get(blkif);
}
spin_unlock_irqrestore(&blkio_schedule_list_lock, flags);
}
@@ -175,54 +177,53 @@ static DECLARE_WAIT_QUEUE_HEAD(blkio_schedule_wait);
static int blkio_schedule(void *arg)
{
DECLARE_WAITQUEUE(wq, current);
DECLARE_WAITQUEUE(wq, current);
blkif_t *blkif;
struct list_head *ent;
blkif_t *blkif;
struct list_head *ent;
daemonize("xenblkd");
daemonize("xenblkd");
for ( ; ; )
{
/* Wait for work to do. */
add_wait_queue(&blkio_schedule_wait, &wq);
set_current_state(TASK_INTERRUPTIBLE);
if ( (NR_PENDING_REQS == MAX_PENDING_REQS) ||
list_empty(&blkio_schedule_list) )
schedule();
__set_current_state(TASK_RUNNING);
remove_wait_queue(&blkio_schedule_wait, &wq);
for (;;) {
/* Wait for work to do. */
add_wait_queue(&blkio_schedule_wait, &wq);
set_current_state(TASK_INTERRUPTIBLE);
if ( (NR_PENDING_REQS == MAX_PENDING_REQS) ||
list_empty(&blkio_schedule_list) )
schedule();
__set_current_state(TASK_RUNNING);
remove_wait_queue(&blkio_schedule_wait, &wq);
/* Queue up a batch of requests. */
while ( (NR_PENDING_REQS < MAX_PENDING_REQS) &&
!list_empty(&blkio_schedule_list) )
{
ent = blkio_schedule_list.next;
blkif = list_entry(ent, blkif_t, blkdev_list);
blkif_get(blkif);
remove_from_blkdev_list(blkif);
if ( do_block_io_op(blkif, BATCH_PER_DOMAIN) )
add_to_blkdev_list_tail(blkif);
blkif_put(blkif);
}
/* Queue up a batch of requests. */
while ((NR_PENDING_REQS < MAX_PENDING_REQS) &&
!list_empty(&blkio_schedule_list)) {
ent = blkio_schedule_list.next;
blkif = list_entry(ent, blkif_t, blkdev_list);
blkif_get(blkif);
remove_from_blkdev_list(blkif);
if (do_block_io_op(blkif, BATCH_PER_DOMAIN))
add_to_blkdev_list_tail(blkif);
blkif_put(blkif);
}
/* Push the batch through to disc. */
flush_plugged_queue();
}
/* Push the batch through to disc. */
flush_plugged_queue();
}
}
static void maybe_trigger_blkio_schedule(void)
{
/*
* Needed so that two processes, who together make the following predicate
* true, don't both read stale values and evaluate the predicate
* incorrectly. Incredibly unlikely to stall the scheduler on x86, but...
*/
smp_mb();
/*
* Needed so that two processes, which together make the following
* predicate true, don't both read stale values and evaluate the
* predicate incorrectly. Incredibly unlikely to stall the scheduler
* on x86, but...
*/
smp_mb();
if ( (NR_PENDING_REQS < (MAX_PENDING_REQS/2)) &&
!list_empty(&blkio_schedule_list) )
wake_up(&blkio_schedule_wait);
if ((NR_PENDING_REQS < (MAX_PENDING_REQS/2)) &&
!list_empty(&blkio_schedule_list))
wake_up(&blkio_schedule_wait);
}
@@ -233,36 +234,34 @@ static void maybe_trigger_blkio_schedule(void)
static void __end_block_io_op(pending_req_t *pending_req, int uptodate)
{
unsigned long flags;
unsigned long flags;
/* An error fails the entire request. */
if ( !uptodate )
{
DPRINTK("Buffer not up-to-date at end of operation\n");
pending_req->status = BLKIF_RSP_ERROR;
}
/* An error fails the entire request. */
if (!uptodate) {
DPRINTK("Buffer not up-to-date at end of operation\n");
pending_req->status = BLKIF_RSP_ERROR;
}
if ( atomic_dec_and_test(&pending_req->pendcnt) )
{
int pending_idx = pending_req - pending_reqs;
fast_flush_area(pending_idx, pending_req->nr_pages);
make_response(pending_req->blkif, pending_req->id,
pending_req->operation, pending_req->status);
blkif_put(pending_req->blkif);
spin_lock_irqsave(&pend_prod_lock, flags);
pending_ring[MASK_PEND_IDX(pending_prod++)] = pending_idx;
spin_unlock_irqrestore(&pend_prod_lock, flags);
maybe_trigger_blkio_schedule();
}
if (atomic_dec_and_test(&pending_req->pendcnt)) {
int pending_idx = pending_req - pending_reqs;
fast_flush_area(pending_idx, pending_req->nr_pages);
make_response(pending_req->blkif, pending_req->id,
pending_req->operation, pending_req->status);
blkif_put(pending_req->blkif);
spin_lock_irqsave(&pend_prod_lock, flags);
pending_ring[MASK_PEND_IDX(pending_prod++)] = pending_idx;
spin_unlock_irqrestore(&pend_prod_lock, flags);
maybe_trigger_blkio_schedule();
}
}
static int end_block_io_op(struct bio *bio, unsigned int done, int error)
{
if ( bio->bi_size != 0 )
return 1;
__end_block_io_op(bio->bi_private, !error);
bio_put(bio);
return error;
if (bio->bi_size != 0)
return 1;
__end_block_io_op(bio->bi_private, !error);
bio_put(bio);
return error;
}
@@ -272,10 +271,10 @@ static int end_block_io_op(struct bio *bio, unsigned int done, int error)
irqreturn_t blkif_be_int(int irq, void *dev_id, struct pt_regs *regs)
{
blkif_t *blkif = dev_id;
add_to_blkdev_list_tail(blkif);
maybe_trigger_blkio_schedule();
return IRQ_HANDLED;
blkif_t *blkif = dev_id;
add_to_blkdev_list_tail(blkif);
maybe_trigger_blkio_schedule();
return IRQ_HANDLED;
}
@@ -286,183 +285,174 @@ irqreturn_t blkif_be_int(int irq, void *dev_id, struct pt_regs *regs)
static int do_block_io_op(blkif_t *blkif, int max_to_do)
{
blkif_back_ring_t *blk_ring = &blkif->blk_ring;
blkif_request_t *req;
RING_IDX i, rp;
int more_to_do = 0;
blkif_back_ring_t *blk_ring = &blkif->blk_ring;
blkif_request_t *req;
RING_IDX i, rp;
int more_to_do = 0;
rp = blk_ring->sring->req_prod;
rmb(); /* Ensure we see queued requests up to 'rp'. */
rp = blk_ring->sring->req_prod;
rmb(); /* Ensure we see queued requests up to 'rp'. */
for ( i = blk_ring->req_cons;
(i != rp) && !RING_REQUEST_CONS_OVERFLOW(blk_ring, i);
i++ )
{
if ( (max_to_do-- == 0) || (NR_PENDING_REQS == MAX_PENDING_REQS) )
{
more_to_do = 1;
break;
}
for (i = blk_ring->req_cons;
(i != rp) && !RING_REQUEST_CONS_OVERFLOW(blk_ring, i);
i++) {
if ((max_to_do-- == 0) ||
(NR_PENDING_REQS == MAX_PENDING_REQS)) {
more_to_do = 1;
break;
}
req = RING_GET_REQUEST(blk_ring, i);
switch ( req->operation )
{
case BLKIF_OP_READ:
case BLKIF_OP_WRITE:
dispatch_rw_block_io(blkif, req);
break;
req = RING_GET_REQUEST(blk_ring, i);
switch (req->operation) {
case BLKIF_OP_READ:
case BLKIF_OP_WRITE:
dispatch_rw_block_io(blkif, req);
break;
default:
DPRINTK("error: unknown block io operation [%d]\n",
req->operation);
make_response(blkif, req->id, req->operation, BLKIF_RSP_ERROR);
break;
}
}
default:
DPRINTK("error: unknown block io operation [%d]\n",
req->operation);
make_response(blkif, req->id, req->operation,
BLKIF_RSP_ERROR);
break;
}
}
blk_ring->req_cons = i;
return more_to_do;
blk_ring->req_cons = i;
return more_to_do;
}
static void dispatch_rw_block_io(blkif_t *blkif, blkif_request_t *req)
{
extern void ll_rw_block(int rw, int nr, struct buffer_head * bhs[]);
int operation = (req->operation == BLKIF_OP_WRITE) ? WRITE : READ;
unsigned long fas = 0;
int i, pending_idx = pending_ring[MASK_PEND_IDX(pending_cons)];
pending_req_t *pending_req;
struct gnttab_map_grant_ref map[BLKIF_MAX_SEGMENTS_PER_REQUEST];
struct phys_req preq;
struct {
unsigned long buf; unsigned int nsec;
} seg[BLKIF_MAX_SEGMENTS_PER_REQUEST];
unsigned int nseg;
struct bio *bio = NULL, *biolist[BLKIF_MAX_SEGMENTS_PER_REQUEST];
int nbio = 0;
request_queue_t *q;
extern void ll_rw_block(int rw, int nr, struct buffer_head * bhs[]);
int operation = (req->operation == BLKIF_OP_WRITE) ? WRITE : READ;
unsigned long fas = 0;
int i, pending_idx = pending_ring[MASK_PEND_IDX(pending_cons)];
pending_req_t *pending_req;
struct gnttab_map_grant_ref map[BLKIF_MAX_SEGMENTS_PER_REQUEST];
struct phys_req preq;
struct {
unsigned long buf; unsigned int nsec;
} seg[BLKIF_MAX_SEGMENTS_PER_REQUEST];
unsigned int nseg;
struct bio *bio = NULL, *biolist[BLKIF_MAX_SEGMENTS_PER_REQUEST];
int nbio = 0;
request_queue_t *q;
/* Check that number of segments is sane. */
nseg = req->nr_segments;
if ( unlikely(nseg == 0) ||
unlikely(nseg > BLKIF_MAX_SEGMENTS_PER_REQUEST) )
{
DPRINTK("Bad number of segments in request (%d)\n", nseg);
goto bad_descriptor;
}
/* Check that number of segments is sane. */
nseg = req->nr_segments;
if (unlikely(nseg == 0) ||
unlikely(nseg > BLKIF_MAX_SEGMENTS_PER_REQUEST)) {
DPRINTK("Bad number of segments in request (%d)\n", nseg);
goto bad_descriptor;
}
preq.dev = req->handle;
preq.sector_number = req->sector_number;
preq.nr_sects = 0;
preq.dev = req->handle;
preq.sector_number = req->sector_number;
preq.nr_sects = 0;
for ( i = 0; i < nseg; i++ )
{
fas = req->frame_and_sects[i];
seg[i].nsec = blkif_last_sect(fas) - blkif_first_sect(fas) + 1;
for (i = 0; i < nseg; i++) {
fas = req->frame_and_sects[i];
seg[i].nsec = blkif_last_sect(fas) - blkif_first_sect(fas) + 1;
if ( seg[i].nsec <= 0 )
goto bad_descriptor;
preq.nr_sects += seg[i].nsec;
if (seg[i].nsec <= 0)
goto bad_descriptor;
preq.nr_sects += seg[i].nsec;
map[i].host_addr = MMAP_VADDR(pending_idx, i);
map[i].dom = blkif->domid;
map[i].ref = blkif_gref_from_fas(fas);
map[i].flags = GNTMAP_host_map;
if ( operation == WRITE )
map[i].flags |= GNTMAP_readonly;
}
map[i].host_addr = MMAP_VADDR(pending_idx, i);
map[i].dom = blkif->domid;
map[i].ref = blkif_gref_from_fas(fas);
map[i].flags = GNTMAP_host_map;
if ( operation == WRITE )
map[i].flags |= GNTMAP_readonly;
}
if ( unlikely(HYPERVISOR_grant_table_op(
GNTTABOP_map_grant_ref, map, nseg)))
BUG();
BUG_ON(HYPERVISOR_grant_table_op(
GNTTABOP_map_grant_ref, map, nseg));
for ( i = 0; i < nseg; i++ )
{
if ( unlikely(map[i].handle < 0) )
{
DPRINTK("invalid buffer -- could not remap it\n");
fast_flush_area(pending_idx, nseg);
goto bad_descriptor;
}
for (i = 0; i < nseg; i++) {
if (unlikely(map[i].handle < 0)) {
DPRINTK("invalid buffer -- could not remap it\n");
fast_flush_area(pending_idx, nseg);
goto bad_descriptor;
}
phys_to_machine_mapping[__pa(MMAP_VADDR(pending_idx, i))>>PAGE_SHIFT] =
FOREIGN_FRAME(map[i].dev_bus_addr >> PAGE_SHIFT);
phys_to_machine_mapping[__pa(MMAP_VADDR(
pending_idx, i)) >> PAGE_SHIFT] =
FOREIGN_FRAME(map[i].dev_bus_addr >> PAGE_SHIFT);
pending_handle(pending_idx, i) = map[i].handle;
}
pending_handle(pending_idx, i) = map[i].handle;
}
for ( i = 0; i < nseg; i++ )
{
fas = req->frame_and_sects[i];
seg[i].buf = map[i].dev_bus_addr | (blkif_first_sect(fas) << 9);
}
for (i = 0; i < nseg; i++) {
fas = req->frame_and_sects[i];
seg[i].buf = map[i].dev_bus_addr |
(blkif_first_sect(fas) << 9);
}
if ( vbd_translate(&preq, blkif, operation) != 0 )
{
DPRINTK("access denied: %s of [%llu,%llu] on dev=%04x\n",
operation == READ ? "read" : "write", preq.sector_number,
preq.sector_number + preq.nr_sects, preq.dev);
goto bad_descriptor;
}
if (vbd_translate(&preq, blkif, operation) != 0) {
DPRINTK("access denied: %s of [%llu,%llu] on dev=%04x\n",
operation == READ ? "read" : "write",
preq.sector_number,
preq.sector_number + preq.nr_sects, preq.dev);
goto bad_descriptor;
}
pending_req = &pending_reqs[pending_idx];
pending_req->blkif = blkif;
pending_req->id = req->id;
pending_req->operation = operation;
pending_req->status = BLKIF_RSP_OKAY;
pending_req->nr_pages = nseg;
pending_req = &pending_reqs[pending_idx];
pending_req->blkif = blkif;
pending_req->id = req->id;
pending_req->operation = operation;
pending_req->status = BLKIF_RSP_OKAY;
pending_req->nr_pages = nseg;
for ( i = 0; i < nseg; i++ )
{
if ( ((int)preq.sector_number|(int)seg[i].nsec) &
((bdev_hardsect_size(preq.bdev) >> 9) - 1) )
{
DPRINTK("Misaligned I/O request from domain %d", blkif->domid);
goto cleanup_and_fail;
}
for (i = 0; i < nseg; i++) {
if (((int)preq.sector_number|(int)seg[i].nsec) &
((bdev_hardsect_size(preq.bdev) >> 9) - 1)) {
DPRINTK("Misaligned I/O request from domain %d",
blkif->domid);
goto cleanup_and_fail;
}
while ( (bio == NULL) ||
(bio_add_page(bio,
virt_to_page(MMAP_VADDR(pending_idx, i)),
seg[i].nsec << 9,
seg[i].buf & ~PAGE_MASK) == 0) )
{
bio = biolist[nbio++] = bio_alloc(GFP_KERNEL, nseg-i);
if ( unlikely(bio == NULL) )
{
cleanup_and_fail:
for ( i = 0; i < (nbio-1); i++ )
bio_put(biolist[i]);
fast_flush_area(pending_idx, nseg);
goto bad_descriptor;
}
while ((bio == NULL) ||
(bio_add_page(bio,
virt_to_page(MMAP_VADDR(pending_idx, i)),
seg[i].nsec << 9,
seg[i].buf & ~PAGE_MASK) == 0)) {
bio = biolist[nbio++] = bio_alloc(GFP_KERNEL, nseg-i);
if (unlikely(bio == NULL)) {
cleanup_and_fail:
for (i = 0; i < (nbio-1); i++)
bio_put(biolist[i]);
fast_flush_area(pending_idx, nseg);
goto bad_descriptor;
}
bio->bi_bdev = preq.bdev;
bio->bi_private = pending_req;
bio->bi_end_io = end_block_io_op;
bio->bi_sector = preq.sector_number;
}
bio->bi_bdev = preq.bdev;
bio->bi_private = pending_req;
bio->bi_end_io = end_block_io_op;
bio->bi_sector = preq.sector_number;
}
preq.sector_number += seg[i].nsec;
}
preq.sector_number += seg[i].nsec;
}
if ( (q = bdev_get_queue(bio->bi_bdev)) != plugged_queue )
{
flush_plugged_queue();
blk_get_queue(q);
plugged_queue = q;
}
if ((q = bdev_get_queue(bio->bi_bdev)) != plugged_queue) {
flush_plugged_queue();
blk_get_queue(q);
plugged_queue = q;
}
atomic_set(&pending_req->pendcnt, nbio);
pending_cons++;
blkif_get(blkif);
atomic_set(&pending_req->pendcnt, nbio);
pending_cons++;
blkif_get(blkif);
for ( i = 0; i < nbio; i++ )
submit_bio(operation, biolist[i]);
for (i = 0; i < nbio; i++)
submit_bio(operation, biolist[i]);
return;
return;
bad_descriptor:
make_response(blkif, req->id, req->operation, BLKIF_RSP_ERROR);
make_response(blkif, req->id, req->operation, BLKIF_RSP_ERROR);
}
@@ -475,66 +465,71 @@ static void dispatch_rw_block_io(blkif_t *blkif, blkif_request_t *req)
static void make_response(blkif_t *blkif, unsigned long id,
unsigned short op, int st)
{
blkif_response_t *resp;
unsigned long flags;
blkif_back_ring_t *blk_ring = &blkif->blk_ring;
blkif_response_t *resp;
unsigned long flags;
blkif_back_ring_t *blk_ring = &blkif->blk_ring;
/* Place on the response ring for the relevant domain. */
spin_lock_irqsave(&blkif->blk_ring_lock, flags);
resp = RING_GET_RESPONSE(blk_ring, blk_ring->rsp_prod_pvt);
resp->id = id;
resp->operation = op;
resp->status = st;
wmb(); /* Ensure other side can see the response fields. */
blk_ring->rsp_prod_pvt++;
RING_PUSH_RESPONSES(blk_ring);
spin_unlock_irqrestore(&blkif->blk_ring_lock, flags);
/* Place on the response ring for the relevant domain. */
spin_lock_irqsave(&blkif->blk_ring_lock, flags);
resp = RING_GET_RESPONSE(blk_ring, blk_ring->rsp_prod_pvt);
resp->id = id;
resp->operation = op;
resp->status = st;
wmb(); /* Ensure other side can see the response fields. */
blk_ring->rsp_prod_pvt++;
RING_PUSH_RESPONSES(blk_ring);
spin_unlock_irqrestore(&blkif->blk_ring_lock, flags);
/* Kick the relevant domain. */
notify_via_evtchn(blkif->evtchn);
/* Kick the relevant domain. */
notify_via_evtchn(blkif->evtchn);
}
void blkif_deschedule(blkif_t *blkif)
{
remove_from_blkdev_list(blkif);
remove_from_blkdev_list(blkif);
}
static int __init blkif_init(void)
{
int i;
struct page *page;
int i;
struct page *page;
if ( !(xen_start_info->flags & SIF_INITDOMAIN) &&
!(xen_start_info->flags & SIF_BLK_BE_DOMAIN) )
return 0;
if (!(xen_start_info->flags & SIF_INITDOMAIN) &&
!(xen_start_info->flags & SIF_BLK_BE_DOMAIN))
return 0;
blkif_interface_init();
blkif_interface_init();
page = balloon_alloc_empty_page_range(MMAP_PAGES);
BUG_ON(page == NULL);
mmap_vstart = (unsigned long)pfn_to_kaddr(page_to_pfn(page));
page = balloon_alloc_empty_page_range(MMAP_PAGES);
BUG_ON(page == NULL);
mmap_vstart = (unsigned long)pfn_to_kaddr(page_to_pfn(page));
pending_cons = 0;
pending_prod = MAX_PENDING_REQS;
memset(pending_reqs, 0, sizeof(pending_reqs));
for ( i = 0; i < MAX_PENDING_REQS; i++ )
pending_ring[i] = i;
pending_cons = 0;
pending_prod = MAX_PENDING_REQS;
memset(pending_reqs, 0, sizeof(pending_reqs));
for (i = 0; i < MAX_PENDING_REQS; i++)
pending_ring[i] = i;
spin_lock_init(&blkio_schedule_list_lock);
INIT_LIST_HEAD(&blkio_schedule_list);
spin_lock_init(&blkio_schedule_list_lock);
INIT_LIST_HEAD(&blkio_schedule_list);
if ( kernel_thread(blkio_schedule, 0, CLONE_FS | CLONE_FILES) < 0 )
BUG();
BUG_ON(kernel_thread(blkio_schedule, 0, CLONE_FS | CLONE_FILES) < 0);
blkif_xenbus_init();
blkif_xenbus_init();
memset( pending_grant_handles, BLKBACK_INVALID_HANDLE, MMAP_PAGES );
memset(pending_grant_handles, BLKBACK_INVALID_HANDLE, MMAP_PAGES);
#ifdef CONFIG_XEN_BLKDEV_TAP_BE
printk(KERN_ALERT "NOTE: Blkif backend is running with tap support on!\n");
#endif
return 0;
return 0;
}
__initcall(blkif_init);
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/
@@ -17,6 +17,7 @@
#include <asm-xen/xen-public/io/blkif.h>
#include <asm-xen/xen-public/io/ring.h>
#include <asm-xen/gnttab.h>
#include <asm-xen/driver_util.h>
#if 0
#define ASSERT(_p) \
@@ -30,39 +31,39 @@
#endif
struct vbd {
blkif_vdev_t handle; /* what the domain refers to this vbd as */
unsigned char readonly; /* Non-zero -> read-only */
unsigned char type; /* VDISK_xxx */
u32 pdevice; /* phys device that this vbd maps to */
struct block_device *bdev;
blkif_vdev_t handle; /* what the domain refers to this vbd as */
unsigned char readonly; /* Non-zero -> read-only */
unsigned char type; /* VDISK_xxx */
u32 pdevice; /* phys device that this vbd maps to */
struct block_device *bdev;
};
typedef struct blkif_st {
/* Unique identifier for this interface. */
domid_t domid;
unsigned int handle;
/* Physical parameters of the comms window. */
unsigned long shmem_frame;
unsigned int evtchn;
unsigned int remote_evtchn;
/* Comms information. */
blkif_back_ring_t blk_ring;
/* VBDs attached to this interface. */
struct vbd vbd;
/* Private fields. */
enum { DISCONNECTED, CONNECTED } status;
/* Unique identifier for this interface. */
domid_t domid;
unsigned int handle;
/* Physical parameters of the comms window. */
unsigned int evtchn;
unsigned int remote_evtchn;
/* Comms information. */
blkif_back_ring_t blk_ring;
struct vm_struct *blk_ring_area;
/* VBDs attached to this interface. */
struct vbd vbd;
/* Private fields. */
enum { DISCONNECTED, CONNECTED } status;
#ifdef CONFIG_XEN_BLKDEV_TAP_BE
/* Is this a blktap frontend */
unsigned int is_blktap;
/* Is this a blktap frontend */
unsigned int is_blktap;
#endif
struct list_head blkdev_list;
spinlock_t blk_ring_lock;
atomic_t refcnt;
struct list_head blkdev_list;
spinlock_t blk_ring_lock;
atomic_t refcnt;
struct work_struct free_work;
u16 shmem_handle;
unsigned long shmem_vaddr;
grant_ref_t shmem_ref;
struct work_struct free_work;
u16 shmem_handle;
grant_ref_t shmem_ref;
} blkif_t;
blkif_t *alloc_blkif(domid_t domid);
@@ -70,11 +71,11 @@ void free_blkif_callback(blkif_t *blkif);
int blkif_map(blkif_t *blkif, unsigned long shared_page, unsigned int evtchn);
#define blkif_get(_b) (atomic_inc(&(_b)->refcnt))
#define blkif_put(_b) \
do { \
if ( atomic_dec_and_test(&(_b)->refcnt) ) \
free_blkif_callback(_b); \
} while (0)
#define blkif_put(_b) \
do { \
if (atomic_dec_and_test(&(_b)->refcnt)) \
free_blkif_callback(_b); \
} while (0)
/* Create a vbd. */
int vbd_create(blkif_t *blkif, blkif_vdev_t vdevice, u32 pdevice,
@@ -86,10 +87,10 @@ unsigned int vbd_info(struct vbd *vbd);
unsigned long vbd_secsize(struct vbd *vbd);
struct phys_req {
unsigned short dev;
unsigned short nr_sects;
struct block_device *bdev;
blkif_sector_t sector_number;
unsigned short dev;
unsigned short nr_sects;
struct block_device *bdev;
blkif_sector_t sector_number;
};
int vbd_translate(struct phys_req *req, blkif_t *blkif, int operation);
@@ -103,3 +104,13 @@ void blkif_xenbus_init(void);
irqreturn_t blkif_be_int(int irq, void *dev_id, struct pt_regs *regs);
#endif /* __BLKIF__BACKEND__COMMON_H__ */
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/
@@ -13,131 +13,144 @@ static kmem_cache_t *blkif_cachep;
blkif_t *alloc_blkif(domid_t domid)
{
blkif_t *blkif;
blkif_t *blkif;
blkif = kmem_cache_alloc(blkif_cachep, GFP_KERNEL);
if (!blkif)
return ERR_PTR(-ENOMEM);
blkif = kmem_cache_alloc(blkif_cachep, GFP_KERNEL);
if (!blkif)
return ERR_PTR(-ENOMEM);
memset(blkif, 0, sizeof(*blkif));
blkif->domid = domid;
blkif->status = DISCONNECTED;
spin_lock_init(&blkif->blk_ring_lock);
atomic_set(&blkif->refcnt, 1);
memset(blkif, 0, sizeof(*blkif));
blkif->domid = domid;
blkif->status = DISCONNECTED;
spin_lock_init(&blkif->blk_ring_lock);
atomic_set(&blkif->refcnt, 1);
return blkif;
return blkif;
}
static int map_frontend_page(blkif_t *blkif, unsigned long localaddr,
unsigned long shared_page)
static int map_frontend_page(blkif_t *blkif, unsigned long shared_page)
{
struct gnttab_map_grant_ref op;
op.host_addr = localaddr;
op.flags = GNTMAP_host_map;
op.ref = shared_page;
op.dom = blkif->domid;
struct gnttab_map_grant_ref op;
BUG_ON( HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1) );
op.host_addr = (unsigned long)blkif->blk_ring_area->addr;
op.flags = GNTMAP_host_map;
op.ref = shared_page;
op.dom = blkif->domid;
if (op.handle < 0) {
DPRINTK(" Grant table operation failure !\n");
return op.handle;
}
lock_vm_area(blkif->blk_ring_area);
BUG_ON(HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1));
unlock_vm_area(blkif->blk_ring_area);
blkif->shmem_ref = shared_page;
blkif->shmem_handle = op.handle;
blkif->shmem_vaddr = localaddr;
return 0;
if (op.handle < 0) {
DPRINTK(" Grant table operation failure !\n");
return op.handle;
}
blkif->shmem_ref = shared_page;
blkif->shmem_handle = op.handle;
return 0;
}
static void unmap_frontend_page(blkif_t *blkif)
{
struct gnttab_unmap_grant_ref op;
struct gnttab_unmap_grant_ref op;
op.host_addr = blkif->shmem_vaddr;
op.handle = blkif->shmem_handle;
op.dev_bus_addr = 0;
BUG_ON(HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1));
op.host_addr = (unsigned long)blkif->blk_ring_area->addr;
op.handle = blkif->shmem_handle;
op.dev_bus_addr = 0;
lock_vm_area(blkif->blk_ring_area);
BUG_ON(HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1));
unlock_vm_area(blkif->blk_ring_area);
}
int blkif_map(blkif_t *blkif, unsigned long shared_page, unsigned int evtchn)
{
struct vm_struct *vma;
blkif_sring_t *sring;
evtchn_op_t op = { .cmd = EVTCHNOP_bind_interdomain };
int err;
blkif_sring_t *sring;
evtchn_op_t op = { .cmd = EVTCHNOP_bind_interdomain };
int err;
BUG_ON(blkif->remote_evtchn);
BUG_ON(blkif->remote_evtchn);
if ( (vma = get_vm_area(PAGE_SIZE, VM_IOREMAP)) == NULL )
return -ENOMEM;
if ( (blkif->blk_ring_area = alloc_vm_area(PAGE_SIZE)) == NULL )
return -ENOMEM;
err = map_frontend_page(blkif, (unsigned long)vma->addr, shared_page);
if (err) {
vfree(vma->addr);
return err;
}
err = map_frontend_page(blkif, shared_page);
if (err) {
free_vm_area(blkif->blk_ring_area);
return err;
}
op.u.bind_interdomain.dom1 = DOMID_SELF;
op.u.bind_interdomain.dom2 = blkif->domid;
op.u.bind_interdomain.port1 = 0;
op.u.bind_interdomain.port2 = evtchn;
err = HYPERVISOR_event_channel_op(&op);
if (err) {
unmap_frontend_page(blkif);
vfree(vma->addr);
return err;
}
op.u.bind_interdomain.dom1 = DOMID_SELF;
op.u.bind_interdomain.dom2 = blkif->domid;
op.u.bind_interdomain.port1 = 0;
op.u.bind_interdomain.port2 = evtchn;
err = HYPERVISOR_event_channel_op(&op);
if (err) {
unmap_frontend_page(blkif);
free_vm_area(blkif->blk_ring_area);
return err;
}
blkif->evtchn = op.u.bind_interdomain.port1;
blkif->remote_evtchn = evtchn;
blkif->evtchn = op.u.bind_interdomain.port1;
blkif->remote_evtchn = evtchn;
sring = (blkif_sring_t *)vma->addr;
SHARED_RING_INIT(sring);
BACK_RING_INIT(&blkif->blk_ring, sring, PAGE_SIZE);
sring = (blkif_sring_t *)blkif->blk_ring_area->addr;
SHARED_RING_INIT(sring);
BACK_RING_INIT(&blkif->blk_ring, sring, PAGE_SIZE);
bind_evtchn_to_irqhandler(blkif->evtchn, blkif_be_int, 0, "blkif-backend",
blkif);
blkif->status = CONNECTED;
blkif->shmem_frame = shared_page;
bind_evtchn_to_irqhandler(
blkif->evtchn, blkif_be_int, 0, "blkif-backend", blkif);
blkif->status = CONNECTED;
return 0;
return 0;
}
static void free_blkif(void *arg)
{
evtchn_op_t op = { .cmd = EVTCHNOP_close };
blkif_t *blkif = (blkif_t *)arg;
evtchn_op_t op = { .cmd = EVTCHNOP_close };
blkif_t *blkif = (blkif_t *)arg;
op.u.close.port = blkif->evtchn;
op.u.close.dom = DOMID_SELF;
HYPERVISOR_event_channel_op(&op);
op.u.close.port = blkif->remote_evtchn;
op.u.close.dom = blkif->domid;
HYPERVISOR_event_channel_op(&op);
op.u.close.port = blkif->evtchn;
op.u.close.dom = DOMID_SELF;
HYPERVISOR_event_channel_op(&op);
op.u.close.port = blkif->remote_evtchn;
op.u.close.dom = blkif->domid;
HYPERVISOR_event_channel_op(&op);
vbd_free(&blkif->vbd);
vbd_free(&blkif->vbd);
if (blkif->evtchn)
unbind_evtchn_from_irqhandler(blkif->evtchn, blkif);
if (blkif->evtchn)
unbind_evtchn_from_irqhandler(blkif->evtchn, blkif);
if (blkif->blk_ring.sring) {
unmap_frontend_page(blkif);
vfree(blkif->blk_ring.sring);
blkif->blk_ring.sring = NULL;
}
if (blkif->blk_ring.sring) {
unmap_frontend_page(blkif);
free_vm_area(blkif->blk_ring_area);
blkif->blk_ring.sring = NULL;
}
kmem_cache_free(blkif_cachep, blkif);
kmem_cache_free(blkif_cachep, blkif);
}
void free_blkif_callback(blkif_t *blkif)
{
INIT_WORK(&blkif->free_work, free_blkif, (void *)blkif);
schedule_work(&blkif->free_work);
INIT_WORK(&blkif->free_work, free_blkif, (void *)blkif);
schedule_work(&blkif->free_work);
}
void __init blkif_interface_init(void)
{
blkif_cachep = kmem_cache_create("blkif_cache", sizeof(blkif_t),
0, 0, NULL, NULL);
blkif_cachep = kmem_cache_create("blkif_cache", sizeof(blkif_t),
0, 0, NULL, NULL);
}
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/
+53 -43
View File
@@ -11,10 +11,10 @@
static inline dev_t vbd_map_devnum(u32 cookie)
{
return MKDEV(BLKIF_MAJOR(cookie), BLKIF_MINOR(cookie));
return MKDEV(BLKIF_MAJOR(cookie), BLKIF_MINOR(cookie));
}
#define vbd_sz(_v) ((_v)->bdev->bd_part ? \
(_v)->bdev->bd_part->nr_sects : (_v)->bdev->bd_disk->capacity)
#define vbd_sz(_v) ((_v)->bdev->bd_part ? \
(_v)->bdev->bd_part->nr_sects : (_v)->bdev->bd_disk->capacity)
#define bdev_put(_b) blkdev_put(_b)
unsigned long vbd_size(struct vbd *vbd)
@@ -35,63 +35,73 @@ unsigned long vbd_secsize(struct vbd *vbd)
int vbd_create(blkif_t *blkif, blkif_vdev_t handle,
u32 pdevice, int readonly)
{
struct vbd *vbd;
struct vbd *vbd;
vbd = &blkif->vbd;
vbd->handle = handle;
vbd->readonly = readonly;
vbd->type = 0;
vbd = &blkif->vbd;
vbd->handle = handle;
vbd->readonly = readonly;
vbd->type = 0;
vbd->pdevice = pdevice;
vbd->pdevice = pdevice;
vbd->bdev = open_by_devnum(
vbd_map_devnum(vbd->pdevice),
vbd->readonly ? FMODE_READ : FMODE_WRITE);
if ( IS_ERR(vbd->bdev) )
{
DPRINTK("vbd_creat: device %08x doesn't exist.\n", vbd->pdevice);
return -ENOENT;
}
vbd->bdev = open_by_devnum(
vbd_map_devnum(vbd->pdevice),
vbd->readonly ? FMODE_READ : FMODE_WRITE);
if (IS_ERR(vbd->bdev)) {
DPRINTK("vbd_creat: device %08x doesn't exist.\n",
vbd->pdevice);
return -ENOENT;
}
if ( (vbd->bdev->bd_disk == NULL) )
{
DPRINTK("vbd_creat: device %08x doesn't exist.\n", vbd->pdevice);
vbd_free(vbd);
return -ENOENT;
}
if (vbd->bdev->bd_disk == NULL) {
DPRINTK("vbd_creat: device %08x doesn't exist.\n",
vbd->pdevice);
vbd_free(vbd);
return -ENOENT;
}
if ( vbd->bdev->bd_disk->flags & GENHD_FL_CD )
vbd->type |= VDISK_CDROM;
if ( vbd->bdev->bd_disk->flags & GENHD_FL_REMOVABLE )
vbd->type |= VDISK_REMOVABLE;
if (vbd->bdev->bd_disk->flags & GENHD_FL_CD)
vbd->type |= VDISK_CDROM;
if (vbd->bdev->bd_disk->flags & GENHD_FL_REMOVABLE)
vbd->type |= VDISK_REMOVABLE;
DPRINTK("Successful creation of handle=%04x (dom=%u)\n",
handle, blkif->domid);
return 0;
DPRINTK("Successful creation of handle=%04x (dom=%u)\n",
handle, blkif->domid);
return 0;
}
void vbd_free(struct vbd *vbd)
{
if (vbd->bdev)
bdev_put(vbd->bdev);
vbd->bdev = NULL;
if (vbd->bdev)
bdev_put(vbd->bdev);
vbd->bdev = NULL;
}
int vbd_translate(struct phys_req *req, blkif_t *blkif, int operation)
{
struct vbd *vbd = &blkif->vbd;
int rc = -EACCES;
struct vbd *vbd = &blkif->vbd;
int rc = -EACCES;
if ((operation == WRITE) && vbd->readonly)
goto out;
if ((operation == WRITE) && vbd->readonly)
goto out;
if (unlikely((req->sector_number + req->nr_sects) > vbd_sz(vbd)))
goto out;
if (unlikely((req->sector_number + req->nr_sects) > vbd_sz(vbd)))
goto out;
req->dev = vbd->pdevice;
req->bdev = vbd->bdev;
rc = 0;
req->dev = vbd->pdevice;
req->bdev = vbd->bdev;
rc = 0;
out:
return rc;
return rc;
}
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/
@@ -124,7 +124,7 @@ static void frontend_changed(struct xenbus_watch *watch, const char *node)
return;
abort:
abort:
xenbus_transaction_end(1);
}
@@ -228,6 +228,7 @@ static int blkback_probe(struct xenbus_device *dev,
be->dev = dev;
be->backend_watch.node = dev->nodename;
be->backend_watch.callback = backend_changed;
/* Will implicitly call backend_changed once. */
err = register_xenbus_watch(&be->backend_watch);
if (err) {
be->backend_watch.node = NULL;
@@ -249,8 +250,6 @@ static int blkback_probe(struct xenbus_device *dev,
}
dev->data = be;
backend_changed(&be->backend_watch, dev->nodename);
return 0;
free_be:
@@ -279,3 +278,13 @@ void blkif_xenbus_init(void)
{
xenbus_register_backend(&blkback);
}
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/
@@ -146,4 +146,15 @@ extern void do_blkif_request (request_queue_t *rq);
int xlvbd_add(blkif_sector_t capacity, int device,
u16 vdisk_info, u16 sector_size, struct blkfront_info *info);
void xlvbd_del(struct blkfront_info *info);
#endif /* __XEN_DRIVERS_BLOCK_H__ */
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/
@@ -65,7 +65,7 @@ static struct xlbd_type_info xlbd_vbd_type = {
};
static struct xlbd_major_info *major_info[NUM_IDE_MAJORS + NUM_SCSI_MAJORS +
NUM_VBD_MAJORS];
NUM_VBD_MAJORS];
#define XLBD_MAJOR_IDE_START 0
#define XLBD_MAJOR_SCSI_START (NUM_IDE_MAJORS)
@@ -309,3 +309,13 @@ xlvbd_del(struct blkfront_info *info)
bdput(bd);
}
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/
File diff suppressed because it is too large. Load diff
@@ -1,229 +0,0 @@
/*
* blktap.h
*
* Interfaces for the Xen block tap driver.
*
* (c) 2004, Andrew Warfield, University of Cambridge
*
*/
#ifndef __BLKTAP_H__
#define __BLKTAP_H__
#include <linux/version.h>
#include <linux/blkdev.h>
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/blkdev.h>
#include <asm/io.h>
#include <asm/setup.h>
#include <asm/pgalloc.h>
#include <asm/hypervisor.h>
#include <asm-xen/xen-public/io/blkif.h>
#include <asm-xen/xen-public/io/ring.h>
/* Used to signal to the backend that this is a tap domain. */
#define BLKTAP_COOKIE 0xbeadfeed
/* -------[ debug / pretty printing ]--------------------------------- */
#define PRINTK(_f, _a...) printk(KERN_ALERT "(file=%s, line=%d) " _f, \
__FILE__ , __LINE__ , ## _a )
#if 0
#define DPRINTK(_f, _a...) printk(KERN_ALERT "(file=%s, line=%d) " _f, \
__FILE__ , __LINE__ , ## _a )
#else
#define DPRINTK(_f, _a...) ((void)0)
#endif
#if 1
#define ASSERT(_p) \
if ( !(_p) ) { printk("Assertion '%s' failed, line %d, file %s", #_p , \
__LINE__, __FILE__); *(int*)0=0; }
#else
#define ASSERT(_p) ((void)0)
#endif
#define WPRINTK(fmt, args...) printk(KERN_WARNING "blk_tap: " fmt, ##args)
/* -------[ state descriptors ]--------------------------------------- */
#define BLKIF_STATE_CLOSED 0
#define BLKIF_STATE_DISCONNECTED 1
#define BLKIF_STATE_CONNECTED 2
/* -------[ connection tracking ]------------------------------------- */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
#define VMALLOC_VMADDR(x) ((unsigned long)(x))
#endif
extern spinlock_t blkif_io_lock;
typedef struct blkif_st {
/* Unique identifier for this interface. */
domid_t domid;
unsigned int handle;
/* Physical parameters of the comms window. */
unsigned long shmem_frame;
unsigned int evtchn;
/* Comms information. */
blkif_back_ring_t blk_ring;
enum { DISCONNECTED, DISCONNECTING, CONNECTED } status;
/*
* DISCONNECT response is deferred until pending requests are ack'ed.
* We therefore need to store the id from the original request.
*/
u8 disconnect_rspid;
struct blkif_st *hash_next;
struct list_head blkdev_list;
spinlock_t blk_ring_lock;
atomic_t refcnt;
struct work_struct work;
#ifdef CONFIG_XEN_BLKDEV_GRANT
u16 shmem_handle;
unsigned long shmem_vaddr;
grant_ref_t shmem_ref;
#endif
} blkif_t;
blkif_t *blkif_find_by_handle(domid_t domid, unsigned int handle);
void blkif_disconnect_complete(blkif_t *blkif);
#define blkif_get(_b) (atomic_inc(&(_b)->refcnt))
#define blkif_put(_b) \
do { \
if ( atomic_dec_and_test(&(_b)->refcnt) ) \
blkif_disconnect_complete(_b); \
} while (0)
/* -------[ active request tracking ]--------------------------------- */
typedef struct {
blkif_t *blkif;
unsigned long id;
int nr_pages;
int next_free;
} active_req_t;
typedef unsigned int ACTIVE_RING_IDX;
active_req_t *lookup_active_req(ACTIVE_RING_IDX idx);
extern inline unsigned int ID_TO_IDX(unsigned long id)
{
return ( id & 0x0000ffff );
}
extern inline domid_t ID_TO_DOM(unsigned long id)
{
return (id >> 16);
}
void active_reqs_init(void);
/* -------[ interposition -> character device interface ]------------- */
/* /dev/xen/blktap resides at device number major=10, minor=200 */
#define BLKTAP_MINOR 202
/* size of the extra VMA area to map in attached pages. */
#define BLKTAP_VMA_PAGES BLKIF_RING_SIZE
/* blktap IOCTLs: */
#define BLKTAP_IOCTL_KICK_FE 1
#define BLKTAP_IOCTL_KICK_BE 2
#define BLKTAP_IOCTL_SETMODE 3
#define BLKTAP_IOCTL_PRINT_IDXS 100
/* blktap switching modes: (Set with BLKTAP_IOCTL_SETMODE) */
#define BLKTAP_MODE_PASSTHROUGH 0x00000000 /* default */
#define BLKTAP_MODE_INTERCEPT_FE 0x00000001
#define BLKTAP_MODE_INTERCEPT_BE 0x00000002
#define BLKTAP_MODE_COPY_FE 0x00000004
#define BLKTAP_MODE_COPY_BE 0x00000008
#define BLKTAP_MODE_COPY_FE_PAGES 0x00000010
#define BLKTAP_MODE_COPY_BE_PAGES 0x00000020
#define BLKTAP_MODE_INTERPOSE \
(BLKTAP_MODE_INTERCEPT_FE | BLKTAP_MODE_INTERCEPT_BE)
#define BLKTAP_MODE_COPY_BOTH \
(BLKTAP_MODE_COPY_FE | BLKTAP_MODE_COPY_BE)
#define BLKTAP_MODE_COPY_BOTH_PAGES \
(BLKTAP_MODE_COPY_FE_PAGES | BLKTAP_MODE_COPY_BE_PAGES)
static inline int BLKTAP_MODE_VALID(unsigned long arg)
{
return (
( arg == BLKTAP_MODE_PASSTHROUGH ) ||
( arg == BLKTAP_MODE_INTERCEPT_FE ) ||
( arg == BLKTAP_MODE_INTERCEPT_BE ) ||
( arg == BLKTAP_MODE_INTERPOSE ) ||
( (arg & ~BLKTAP_MODE_COPY_FE_PAGES) == BLKTAP_MODE_COPY_FE ) ||
( (arg & ~BLKTAP_MODE_COPY_BE_PAGES) == BLKTAP_MODE_COPY_BE ) ||
( (arg & ~BLKTAP_MODE_COPY_BOTH_PAGES) == BLKTAP_MODE_COPY_BOTH )
);
}
/* -------[ Mappings to User VMA ]------------------------------------ */
#define BATCH_PER_DOMAIN 16
/* -------[ Here be globals ]----------------------------------------- */
extern unsigned long blktap_mode;
/* Connection to a single backend domain. */
extern blkif_front_ring_t blktap_be_ring;
extern unsigned int blktap_be_evtchn;
extern unsigned int blktap_be_state;
/* User ring status. */
extern unsigned long blktap_ring_ok;
/* -------[ ...and function prototypes. ]----------------------------- */
/* init function for character device interface. */
int blktap_init(void);
/* init function for the blkif cache. */
void __init blkif_interface_init(void);
void __init blkdev_schedule_init(void);
void blkif_deschedule(blkif_t *blkif);
/* interfaces to the char driver, passing messages to and from apps. */
void blktap_kick_user(void);
/* user ring access functions: */
int blktap_write_fe_ring(blkif_request_t *req);
int blktap_write_be_ring(blkif_response_t *rsp);
int blktap_write_ctrl_ring(ctrl_msg_t *msg);
/* fe/be ring access functions: */
int write_resp_to_fe_ring(blkif_t *blkif, blkif_response_t *rsp);
int write_req_to_be_ring(blkif_request_t *req);
/* event notification functions */
void kick_fe_domain(blkif_t *blkif);
void kick_be_domain(void);
/* Interrupt handlers. */
irqreturn_t blkif_ptbe_int(int irq, void *dev_id,
struct pt_regs *ptregs);
irqreturn_t blkif_ptfe_int(int irq, void *dev_id, struct pt_regs *regs);
/* Control message receiver. */
extern void blkif_ctrlif_rx(ctrl_msg_t *msg, unsigned long id);
/* debug */
void print_fe_ring_idxs(void);
void print_be_ring_idxs(void);
#define __BLKINT_H__
#endif
@@ -17,6 +17,7 @@
#include <asm-xen/xen-public/io/blkif.h>
#include <asm-xen/xen-public/io/ring.h>
#include <asm-xen/gnttab.h>
#include <asm-xen/driver_util.h>
#if 0
#define ASSERT(_p) \
@@ -32,39 +33,39 @@
#define WPRINTK(fmt, args...) printk(KERN_WARNING "blk_tap: " fmt, ##args)
struct vbd {
blkif_vdev_t handle; /* what the domain refers to this vbd as */
unsigned char readonly; /* Non-zero -> read-only */
unsigned char type; /* VDISK_xxx */
u32 pdevice; /* phys device that this vbd maps to */
struct block_device *bdev;
blkif_vdev_t handle; /* what the domain refers to this vbd as */
unsigned char readonly; /* Non-zero -> read-only */
unsigned char type; /* VDISK_xxx */
u32 pdevice; /* phys device that this vbd maps to */
struct block_device *bdev;
};
typedef struct blkif_st {
/* Unique identifier for this interface. */
domid_t domid;
unsigned int handle;
/* Physical parameters of the comms window. */
unsigned long shmem_frame;
unsigned int evtchn;
unsigned int remote_evtchn;
/* Comms information. */
blkif_back_ring_t blk_ring;
/* VBDs attached to this interface. */
struct vbd vbd;
/* Private fields. */
enum { DISCONNECTED, CONNECTED } status;
/* Unique identifier for this interface. */
domid_t domid;
unsigned int handle;
/* Physical parameters of the comms window. */
unsigned int evtchn;
unsigned int remote_evtchn;
/* Comms information. */
blkif_back_ring_t blk_ring;
struct vm_struct *blk_ring_area;
/* VBDs attached to this interface. */
struct vbd vbd;
/* Private fields. */
enum { DISCONNECTED, CONNECTED } status;
#ifdef CONFIG_XEN_BLKDEV_TAP_BE
/* Is this a blktap frontend */
unsigned int is_blktap;
/* Is this a blktap frontend */
unsigned int is_blktap;
#endif
struct list_head blkdev_list;
spinlock_t blk_ring_lock;
atomic_t refcnt;
struct list_head blkdev_list;
spinlock_t blk_ring_lock;
atomic_t refcnt;
struct work_struct free_work;
u16 shmem_handle;
unsigned long shmem_vaddr;
grant_ref_t shmem_ref;
struct work_struct free_work;
u16 shmem_handle;
grant_ref_t shmem_ref;
} blkif_t;
blkif_t *alloc_blkif(domid_t domid);
@@ -88,10 +89,10 @@ unsigned int vbd_info(struct vbd *vbd);
unsigned long vbd_secsize(struct vbd *vbd);
struct phys_req {
unsigned short dev;
unsigned short nr_sects;
struct block_device *bdev;
blkif_sector_t sector_number;
unsigned short dev;
unsigned short nr_sects;
struct block_device *bdev;
blkif_sector_t sector_number;
};
int vbd_translate(struct phys_req *req, blkif_t *blkif, int operation);
@@ -105,3 +106,13 @@ void blkif_xenbus_init(void);
irqreturn_t blkif_be_int(int irq, void *dev_id, struct pt_regs *regs);
#endif /* __BLKIF__BACKEND__COMMON_H__ */
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/
@@ -13,129 +13,143 @@ static kmem_cache_t *blkif_cachep;
blkif_t *alloc_blkif(domid_t domid)
{
blkif_t *blkif;
blkif_t *blkif;
blkif = kmem_cache_alloc(blkif_cachep, GFP_KERNEL);
if (!blkif)
return ERR_PTR(-ENOMEM);
blkif = kmem_cache_alloc(blkif_cachep, GFP_KERNEL);
if (!blkif)
return ERR_PTR(-ENOMEM);
memset(blkif, 0, sizeof(*blkif));
blkif->domid = domid;
blkif->status = DISCONNECTED;
spin_lock_init(&blkif->blk_ring_lock);
atomic_set(&blkif->refcnt, 1);
memset(blkif, 0, sizeof(*blkif));
blkif->domid = domid;
blkif->status = DISCONNECTED;
spin_lock_init(&blkif->blk_ring_lock);
atomic_set(&blkif->refcnt, 1);
return blkif;
return blkif;
}
static int map_frontend_page(blkif_t *blkif, unsigned long localaddr,
unsigned long shared_page)
static int map_frontend_page(blkif_t *blkif, unsigned long shared_page)
{
struct gnttab_map_grant_ref op;
op.host_addr = localaddr;
op.flags = GNTMAP_host_map;
op.ref = shared_page;
op.dom = blkif->domid;
struct gnttab_map_grant_ref op;
BUG_ON( HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1) );
op.host_addr = (unsigned long)blkif->blk_ring_area->addr;
op.flags = GNTMAP_host_map;
op.ref = shared_page;
op.dom = blkif->domid;
if (op.handle < 0) {
DPRINTK(" Grant table operation failure !\n");
return op.handle;
}
lock_vm_area(blkif->blk_ring_area);
BUG_ON(HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1));
unlock_vm_area(blkif->blk_ring_area);
blkif->shmem_ref = shared_page;
blkif->shmem_handle = op.handle;
blkif->shmem_vaddr = localaddr;
return 0;
if (op.handle < 0) {
DPRINTK(" Grant table operation failure !\n");
return op.handle;
}
blkif->shmem_ref = shared_page;
blkif->shmem_handle = op.handle;
return 0;
}
static void unmap_frontend_page(blkif_t *blkif)
{
struct gnttab_unmap_grant_ref op;
struct gnttab_unmap_grant_ref op;
op.host_addr = blkif->shmem_vaddr;
op.handle = blkif->shmem_handle;
op.dev_bus_addr = 0;
BUG_ON(HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1));
op.host_addr = (unsigned long)blkif->blk_ring_area->addr;
op.handle = blkif->shmem_handle;
op.dev_bus_addr = 0;
lock_vm_area(blkif->blk_ring_area);
BUG_ON(HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1));
unlock_vm_area(blkif->blk_ring_area);
}
int blkif_map(blkif_t *blkif, unsigned long shared_page, unsigned int evtchn)
{
struct vm_struct *vma;
blkif_sring_t *sring;
evtchn_op_t op = { .cmd = EVTCHNOP_bind_interdomain };
int err;
blkif_sring_t *sring;
evtchn_op_t op = { .cmd = EVTCHNOP_bind_interdomain };
int err;
BUG_ON(blkif->remote_evtchn);
BUG_ON(blkif->remote_evtchn);
if ( (vma = get_vm_area(PAGE_SIZE, VM_IOREMAP)) == NULL )
return -ENOMEM;
if ((blkif->blk_ring_area = alloc_vm_area(PAGE_SIZE)) == NULL)
return -ENOMEM;
err = map_frontend_page(blkif, (unsigned long)vma->addr, shared_page);
if (err) {
vfree(vma->addr);
return err;
}
err = map_frontend_page(blkif, shared_page);
if (err) {
free_vm_area(blkif->blk_ring_area);
return err;
}
op.u.bind_interdomain.dom1 = DOMID_SELF;
op.u.bind_interdomain.dom2 = blkif->domid;
op.u.bind_interdomain.port1 = 0;
op.u.bind_interdomain.port2 = evtchn;
err = HYPERVISOR_event_channel_op(&op);
if (err) {
unmap_frontend_page(blkif);
vfree(vma->addr);
return err;
}
op.u.bind_interdomain.dom1 = DOMID_SELF;
op.u.bind_interdomain.dom2 = blkif->domid;
op.u.bind_interdomain.port1 = 0;
op.u.bind_interdomain.port2 = evtchn;
err = HYPERVISOR_event_channel_op(&op);
if (err) {
unmap_frontend_page(blkif);
free_vm_area(blkif->blk_ring_area);
return err;
}
blkif->evtchn = op.u.bind_interdomain.port1;
blkif->remote_evtchn = evtchn;
sring = (blkif_sring_t *)vma->addr;
SHARED_RING_INIT(sring);
BACK_RING_INIT(&blkif->blk_ring, sring, PAGE_SIZE);
blkif->evtchn = op.u.bind_interdomain.port1;
blkif->remote_evtchn = evtchn;
bind_evtchn_to_irqhandler(blkif->evtchn, blkif_be_int, 0, "blkif-backend",
blkif);
blkif->status = CONNECTED;
blkif->shmem_frame = shared_page;
sring = (blkif_sring_t *)blkif->blk_ring_area->addr;
SHARED_RING_INIT(sring);
BACK_RING_INIT(&blkif->blk_ring, sring, PAGE_SIZE);
return 0;
bind_evtchn_to_irqhandler(
blkif->evtchn, blkif_be_int, 0, "blkif-backend", blkif);
blkif->status = CONNECTED;
return 0;
}
static void free_blkif(void *arg)
{
evtchn_op_t op = { .cmd = EVTCHNOP_close };
blkif_t *blkif = (blkif_t *)arg;
evtchn_op_t op = { .cmd = EVTCHNOP_close };
blkif_t *blkif = (blkif_t *)arg;
op.u.close.port = blkif->evtchn;
op.u.close.dom = DOMID_SELF;
HYPERVISOR_event_channel_op(&op);
op.u.close.port = blkif->remote_evtchn;
op.u.close.dom = blkif->domid;
HYPERVISOR_event_channel_op(&op);
op.u.close.port = blkif->evtchn;
op.u.close.dom = DOMID_SELF;
HYPERVISOR_event_channel_op(&op);
op.u.close.port = blkif->remote_evtchn;
op.u.close.dom = blkif->domid;
HYPERVISOR_event_channel_op(&op);
if (blkif->evtchn)
unbind_evtchn_from_irqhandler(blkif->evtchn, blkif);
if (blkif->evtchn)
unbind_evtchn_from_irqhandler(blkif->evtchn, blkif);
if (blkif->blk_ring.sring) {
unmap_frontend_page(blkif);
vfree(blkif->blk_ring.sring);
blkif->blk_ring.sring = NULL;
}
if (blkif->blk_ring.sring) {
unmap_frontend_page(blkif);
free_vm_area(blkif->blk_ring_area);
blkif->blk_ring.sring = NULL;
}
kmem_cache_free(blkif_cachep, blkif);
kmem_cache_free(blkif_cachep, blkif);
}
void free_blkif_callback(blkif_t *blkif)
{
INIT_WORK(&blkif->free_work, free_blkif, (void *)blkif);
schedule_work(&blkif->free_work);
INIT_WORK(&blkif->free_work, free_blkif, (void *)blkif);
schedule_work(&blkif->free_work);
}
void __init blkif_interface_init(void)
{
blkif_cachep = kmem_cache_create("blkif_cache", sizeof(blkif_t),
0, 0, NULL, NULL);
blkif_cachep = kmem_cache_create(
"blkif_cache", sizeof(blkif_t), 0, 0, NULL, NULL);
}
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/
@@ -172,6 +172,7 @@ static int blkback_probe(struct xenbus_device *dev,
be->dev = dev;
be->backend_watch.node = dev->nodename;
be->backend_watch.callback = backend_changed;
/* Registration implicitly fires backend_changed once */
err = register_xenbus_watch(&be->backend_watch);
if (err) {
be->backend_watch.node = NULL;
@@ -193,8 +194,6 @@ static int blkback_probe(struct xenbus_device *dev,
}
dev->data = be;
backend_changed(&be->backend_watch, dev->nodename);
return 0;
free_be:
@@ -223,3 +222,13 @@ void blkif_xenbus_init(void)
{
xenbus_register_backend(&blkback);
}
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/
File diff suppressed because it is too large. Load diff
@@ -36,13 +36,12 @@ struct ring_head
static inline struct ring_head *outring(void)
{
return machine_to_virt(xen_start_info->console_mfn << PAGE_SHIFT);
return mfn_to_virt(xen_start_info->console_mfn);
}
static inline struct ring_head *inring(void)
{
return machine_to_virt(xen_start_info->console_mfn << PAGE_SHIFT)
+ PAGE_SIZE/2;
return mfn_to_virt(xen_start_info->console_mfn) + PAGE_SIZE/2;
}
@@ -126,3 +125,13 @@ void xencons_resume(void)
(void)xencons_ring_init();
}
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/
@@ -3,12 +3,21 @@
asmlinkage int xprintk(const char *fmt, ...);
int xencons_ring_init(void);
int xencons_ring_send(const char *data, unsigned len);
typedef void (xencons_receiver_func)(char *buf, unsigned len,
struct pt_regs *regs);
typedef void (xencons_receiver_func)(
char *buf, unsigned len, struct pt_regs *regs);
void xencons_ring_register_receiver(xencons_receiver_func *f);
#endif /* _XENCONS_RING_H */
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/
+213 -270
View File
@@ -1,9 +1,9 @@
/******************************************************************************
* evtchn.c
*
* Xenolinux driver for receiving and demuxing event-channel signals.
* Driver for receiving and demuxing event-channel signals.
*
* Copyright (c) 2004, K A Fraser
* Copyright (c) 2004-2005, K A Fraser
* Multi-process extensions Copyright (c) 2004, Steven Smith
*
* This file may be distributed separately from the Linux kernel, or
@@ -46,29 +46,18 @@
#include <linux/init.h>
#define XEN_EVTCHN_MASK_OPS
#include <asm-xen/evtchn.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
#include <linux/devfs_fs_kernel.h>
#define OLD_DEVFS
#else
#include <linux/gfp.h>
#endif
#ifdef OLD_DEVFS
/* NB. This must be shared amongst drivers if more things go in /dev/xen */
static devfs_handle_t xen_dev_dir;
#endif
struct per_user_data {
/* Notification ring, accessed via /dev/xen/evtchn. */
# define EVTCHN_RING_SIZE 2048 /* 2048 16-bit entries */
# define EVTCHN_RING_MASK(_i) ((_i)&(EVTCHN_RING_SIZE-1))
u16 *ring;
unsigned int ring_cons, ring_prod, ring_overflow;
/* Notification ring, accessed via /dev/xen/evtchn. */
#define EVTCHN_RING_SIZE 2048 /* 2048 16-bit entries */
#define EVTCHN_RING_MASK(_i) ((_i)&(EVTCHN_RING_SIZE-1))
u16 *ring;
unsigned int ring_cons, ring_prod, ring_overflow;
/* Processes wait on this queue when ring is empty. */
wait_queue_head_t evtchn_wait;
struct fasync_struct *evtchn_async_queue;
/* Processes wait on this queue when ring is empty. */
wait_queue_head_t evtchn_wait;
struct fasync_struct *evtchn_async_queue;
};
/* Who's bound to each port? */
@@ -77,356 +66,310 @@ static spinlock_t port_user_lock;
void evtchn_device_upcall(int port)
{
struct per_user_data *u;
struct per_user_data *u;
spin_lock(&port_user_lock);
spin_lock(&port_user_lock);
mask_evtchn(port);
clear_evtchn(port);
mask_evtchn(port);
clear_evtchn(port);
if ( (u = port_user[port]) != NULL )
{
if ( (u->ring_prod - u->ring_cons) < EVTCHN_RING_SIZE )
{
u->ring[EVTCHN_RING_MASK(u->ring_prod)] = (u16)port;
if ( u->ring_cons == u->ring_prod++ )
{
wake_up_interruptible(&u->evtchn_wait);
kill_fasync(&u->evtchn_async_queue, SIGIO, POLL_IN);
}
}
else
{
u->ring_overflow = 1;
}
}
if ((u = port_user[port]) != NULL) {
if ((u->ring_prod - u->ring_cons) < EVTCHN_RING_SIZE) {
u->ring[EVTCHN_RING_MASK(u->ring_prod)] = (u16)port;
if (u->ring_cons == u->ring_prod++) {
wake_up_interruptible(&u->evtchn_wait);
kill_fasync(&u->evtchn_async_queue, SIGIO, POLL_IN);
}
} else {
u->ring_overflow = 1;
}
}
spin_unlock(&port_user_lock);
spin_unlock(&port_user_lock);
}
static ssize_t evtchn_read(struct file *file, char *buf,
size_t count, loff_t *ppos)
{
int rc;
unsigned int c, p, bytes1 = 0, bytes2 = 0;
DECLARE_WAITQUEUE(wait, current);
struct per_user_data *u = file->private_data;
int rc;
unsigned int c, p, bytes1 = 0, bytes2 = 0;
DECLARE_WAITQUEUE(wait, current);
struct per_user_data *u = file->private_data;
add_wait_queue(&u->evtchn_wait, &wait);
add_wait_queue(&u->evtchn_wait, &wait);
count &= ~1; /* even number of bytes */
count &= ~1; /* even number of bytes */
if ( count == 0 )
{
rc = 0;
goto out;
}
if (count == 0) {
rc = 0;
goto out;
}
if ( count > PAGE_SIZE )
count = PAGE_SIZE;
if (count > PAGE_SIZE)
count = PAGE_SIZE;
for ( ; ; )
{
set_current_state(TASK_INTERRUPTIBLE);
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
if ( (c = u->ring_cons) != (p = u->ring_prod) )
break;
if ((c = u->ring_cons) != (p = u->ring_prod))
break;
if ( u->ring_overflow )
{
rc = -EFBIG;
goto out;
}
if (u->ring_overflow) {
rc = -EFBIG;
goto out;
}
if ( file->f_flags & O_NONBLOCK )
{
rc = -EAGAIN;
goto out;
}
if (file->f_flags & O_NONBLOCK) {
rc = -EAGAIN;
goto out;
}
if ( signal_pending(current) )
{
rc = -ERESTARTSYS;
goto out;
}
if (signal_pending(current)) {
rc = -ERESTARTSYS;
goto out;
}
schedule();
}
schedule();
}
/* Byte lengths of two chunks. Chunk split (if any) is at ring wrap. */
if ( ((c ^ p) & EVTCHN_RING_SIZE) != 0 )
{
bytes1 = (EVTCHN_RING_SIZE - EVTCHN_RING_MASK(c)) * sizeof(u16);
bytes2 = EVTCHN_RING_MASK(p) * sizeof(u16);
}
else
{
bytes1 = (p - c) * sizeof(u16);
bytes2 = 0;
}
/* Byte lengths of two chunks. Chunk split (if any) is at ring wrap. */
if (((c ^ p) & EVTCHN_RING_SIZE) != 0) {
bytes1 = (EVTCHN_RING_SIZE - EVTCHN_RING_MASK(c)) *
sizeof(u16);
bytes2 = EVTCHN_RING_MASK(p) * sizeof(u16);
} else {
bytes1 = (p - c) * sizeof(u16);
bytes2 = 0;
}
/* Truncate chunks according to caller's maximum byte count. */
if ( bytes1 > count )
{
bytes1 = count;
bytes2 = 0;
}
else if ( (bytes1 + bytes2) > count )
{
bytes2 = count - bytes1;
}
/* Truncate chunks according to caller's maximum byte count. */
if (bytes1 > count) {
bytes1 = count;
bytes2 = 0;
} else if ((bytes1 + bytes2) > count) {
bytes2 = count - bytes1;
}
if ( copy_to_user(buf, &u->ring[EVTCHN_RING_MASK(c)], bytes1) ||
((bytes2 != 0) && copy_to_user(&buf[bytes1], &u->ring[0], bytes2)) )
{
rc = -EFAULT;
goto out;
}
if (copy_to_user(buf, &u->ring[EVTCHN_RING_MASK(c)], bytes1) ||
((bytes2 != 0) &&
copy_to_user(&buf[bytes1], &u->ring[0], bytes2))) {
rc = -EFAULT;
goto out;
}
u->ring_cons += (bytes1 + bytes2) / sizeof(u16);
u->ring_cons += (bytes1 + bytes2) / sizeof(u16);
rc = bytes1 + bytes2;
rc = bytes1 + bytes2;
out:
__set_current_state(TASK_RUNNING);
remove_wait_queue(&u->evtchn_wait, &wait);
return rc;
__set_current_state(TASK_RUNNING);
remove_wait_queue(&u->evtchn_wait, &wait);
return rc;
}
static ssize_t evtchn_write(struct file *file, const char *buf,
size_t count, loff_t *ppos)
{
int rc, i;
u16 *kbuf = (u16 *)__get_free_page(GFP_KERNEL);
struct per_user_data *u = file->private_data;
int rc, i;
u16 *kbuf = (u16 *)__get_free_page(GFP_KERNEL);
struct per_user_data *u = file->private_data;
if ( kbuf == NULL )
return -ENOMEM;
if (kbuf == NULL)
return -ENOMEM;
count &= ~1; /* even number of bytes */
count &= ~1; /* even number of bytes */
if ( count == 0 )
{
rc = 0;
goto out;
}
if (count == 0) {
rc = 0;
goto out;
}
if ( count > PAGE_SIZE )
count = PAGE_SIZE;
if (count > PAGE_SIZE)
count = PAGE_SIZE;
if ( copy_from_user(kbuf, buf, count) != 0 )
{
rc = -EFAULT;
goto out;
}
if (copy_from_user(kbuf, buf, count) != 0) {
rc = -EFAULT;
goto out;
}
spin_lock_irq(&port_user_lock);
for ( i = 0; i < (count/2); i++ )
if ( (kbuf[i] < NR_EVENT_CHANNELS) && (port_user[kbuf[i]] == u) )
unmask_evtchn(kbuf[i]);
spin_unlock_irq(&port_user_lock);
spin_lock_irq(&port_user_lock);
for (i = 0; i < (count/2); i++)
if ((kbuf[i] < NR_EVENT_CHANNELS) && (port_user[kbuf[i]] == u))
unmask_evtchn(kbuf[i]);
spin_unlock_irq(&port_user_lock);
rc = count;
rc = count;
out:
free_page((unsigned long)kbuf);
return rc;
free_page((unsigned long)kbuf);
return rc;
}
static int evtchn_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
int rc = 0;
struct per_user_data *u = file->private_data;
int rc = 0;
struct per_user_data *u = file->private_data;
spin_lock_irq(&port_user_lock);
spin_lock_irq(&port_user_lock);
switch ( cmd )
{
case EVTCHN_RESET:
/* Initialise the ring to empty. Clear errors. */
u->ring_cons = u->ring_prod = u->ring_overflow = 0;
break;
switch (cmd) {
case EVTCHN_RESET:
/* Initialise the ring to empty. Clear errors. */
u->ring_cons = u->ring_prod = u->ring_overflow = 0;
break;
case EVTCHN_BIND:
if ( arg >= NR_EVENT_CHANNELS )
{
rc = -EINVAL;
}
else if ( port_user[arg] != NULL )
{
rc = -EISCONN;
}
else
{
port_user[arg] = u;
unmask_evtchn(arg);
}
break;
case EVTCHN_BIND:
if (arg >= NR_EVENT_CHANNELS) {
rc = -EINVAL;
} else if (port_user[arg] != NULL) {
rc = -EISCONN;
} else {
port_user[arg] = u;
unmask_evtchn(arg);
}
break;
case EVTCHN_UNBIND:
if ( arg >= NR_EVENT_CHANNELS )
{
rc = -EINVAL;
}
else if ( port_user[arg] != u )
{
rc = -ENOTCONN;
}
else
{
port_user[arg] = NULL;
mask_evtchn(arg);
}
break;
case EVTCHN_UNBIND:
if (arg >= NR_EVENT_CHANNELS) {
rc = -EINVAL;
} else if (port_user[arg] != u) {
rc = -ENOTCONN;
} else {
port_user[arg] = NULL;
mask_evtchn(arg);
}
break;
default:
rc = -ENOSYS;
break;
}
default:
rc = -ENOSYS;
break;
}
spin_unlock_irq(&port_user_lock);
spin_unlock_irq(&port_user_lock);
return rc;
return rc;
}
static unsigned int evtchn_poll(struct file *file, poll_table *wait)
{
unsigned int mask = POLLOUT | POLLWRNORM;
struct per_user_data *u = file->private_data;
unsigned int mask = POLLOUT | POLLWRNORM;
struct per_user_data *u = file->private_data;
poll_wait(file, &u->evtchn_wait, wait);
if ( u->ring_cons != u->ring_prod )
mask |= POLLIN | POLLRDNORM;
if ( u->ring_overflow )
mask = POLLERR;
return mask;
poll_wait(file, &u->evtchn_wait, wait);
if (u->ring_cons != u->ring_prod)
mask |= POLLIN | POLLRDNORM;
if (u->ring_overflow)
mask = POLLERR;
return mask;
}
static int evtchn_fasync(int fd, struct file *filp, int on)
{
struct per_user_data *u = filp->private_data;
return fasync_helper(fd, filp, on, &u->evtchn_async_queue);
struct per_user_data *u = filp->private_data;
return fasync_helper(fd, filp, on, &u->evtchn_async_queue);
}
static int evtchn_open(struct inode *inode, struct file *filp)
{
struct per_user_data *u;
struct per_user_data *u;
if ( (u = kmalloc(sizeof(*u), GFP_KERNEL)) == NULL )
return -ENOMEM;
if ((u = kmalloc(sizeof(*u), GFP_KERNEL)) == NULL)
return -ENOMEM;
memset(u, 0, sizeof(*u));
init_waitqueue_head(&u->evtchn_wait);
memset(u, 0, sizeof(*u));
init_waitqueue_head(&u->evtchn_wait);
if ( (u->ring = (u16 *)__get_free_page(GFP_KERNEL)) == NULL )
{
kfree(u);
return -ENOMEM;
}
if ((u->ring = (u16 *)__get_free_page(GFP_KERNEL)) == NULL)
{
kfree(u);
return -ENOMEM;
}
filp->private_data = u;
filp->private_data = u;
return 0;
return 0;
}
static int evtchn_release(struct inode *inode, struct file *filp)
{
int i;
struct per_user_data *u = filp->private_data;
int i;
struct per_user_data *u = filp->private_data;
spin_lock_irq(&port_user_lock);
spin_lock_irq(&port_user_lock);
free_page((unsigned long)u->ring);
free_page((unsigned long)u->ring);
for ( i = 0; i < NR_EVENT_CHANNELS; i++ )
{
if ( port_user[i] == u )
{
port_user[i] = NULL;
mask_evtchn(i);
}
}
for (i = 0; i < NR_EVENT_CHANNELS; i++)
{
if (port_user[i] == u)
{
port_user[i] = NULL;
mask_evtchn(i);
}
}
spin_unlock_irq(&port_user_lock);
spin_unlock_irq(&port_user_lock);
kfree(u);
kfree(u);
return 0;
return 0;
}
static struct file_operations evtchn_fops = {
.owner = THIS_MODULE,
.read = evtchn_read,
.write = evtchn_write,
.ioctl = evtchn_ioctl,
.poll = evtchn_poll,
.fasync = evtchn_fasync,
.open = evtchn_open,
.release = evtchn_release,
.owner = THIS_MODULE,
.read = evtchn_read,
.write = evtchn_write,
.ioctl = evtchn_ioctl,
.poll = evtchn_poll,
.fasync = evtchn_fasync,
.open = evtchn_open,
.release = evtchn_release,
};
static struct miscdevice evtchn_miscdev = {
.minor = EVTCHN_MINOR,
.name = "evtchn",
.fops = &evtchn_fops,
.minor = EVTCHN_MINOR,
.name = "evtchn",
.fops = &evtchn_fops,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
.devfs_name = "misc/evtchn",
.devfs_name = "misc/evtchn",
#endif
};
static int __init evtchn_init(void)
{
#ifdef OLD_DEVFS
devfs_handle_t symlink_handle;
int pos;
char link_dest[64];
#endif
int err;
int err;
spin_lock_init(&port_user_lock);
memset(port_user, 0, sizeof(port_user));
spin_lock_init(&port_user_lock);
memset(port_user, 0, sizeof(port_user));
/* (DEVFS) create '/dev/misc/evtchn'. */
err = misc_register(&evtchn_miscdev);
if ( err != 0 )
{
printk(KERN_ALERT "Could not register /dev/misc/evtchn\n");
return err;
}
/* (DEVFS) create '/dev/misc/evtchn'. */
err = misc_register(&evtchn_miscdev);
if (err != 0)
{
printk(KERN_ALERT "Could not register /dev/misc/evtchn\n");
return err;
}
#ifdef OLD_DEVFS
/* (DEVFS) create directory '/dev/xen'. */
xen_dev_dir = devfs_mk_dir(NULL, "xen", NULL);
printk("Event-channel device installed.\n");
/* (DEVFS) &link_dest[pos] == '../misc/evtchn'. */
pos = devfs_generate_path(evtchn_miscdev.devfs_handle,
&link_dest[3],
sizeof(link_dest) - 3);
if ( pos >= 0 )
strncpy(&link_dest[pos], "../", 3);
/* (DEVFS) symlink '/dev/xen/evtchn' -> '../misc/evtchn'. */
(void)devfs_mk_symlink(xen_dev_dir,
"evtchn",
DEVFS_FL_DEFAULT,
&link_dest[pos],
&symlink_handle,
NULL);
/* (DEVFS) automatically destroy the symlink with its destination. */
devfs_auto_unregister(evtchn_miscdev.devfs_handle, symlink_handle);
#endif
printk("Event-channel device installed.\n");
return 0;
return 0;
}
static void evtchn_cleanup(void)
{
misc_deregister(&evtchn_miscdev);
misc_deregister(&evtchn_miscdev);
}
module_init(evtchn_init);
module_exit(evtchn_cleanup);
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/
@@ -18,17 +18,12 @@
#include <asm-xen/xen-public/io/netif.h>
#include <asm/io.h>
#include <asm/pgalloc.h>
#ifdef CONFIG_XEN_NETDEV_GRANT
#include <asm-xen/xen-public/grant_table.h>
#include <asm-xen/gnttab.h>
#include <asm-xen/driver_util.h>
#define GRANT_INVALID_REF (0xFFFF)
#endif
#if 0
#define ASSERT(_p) \
if ( !(_p) ) { printk("Assertion '%s' failed, line %d, file %s", #_p , \
@@ -44,74 +39,64 @@
#define WPRINTK(fmt, args...) \
printk(KERN_WARNING "xen_net: " fmt, ##args)
typedef struct netif_st {
/* Unique identifier for this interface. */
domid_t domid;
unsigned int handle;
/* Unique identifier for this interface. */
domid_t domid;
unsigned int handle;
u8 fe_dev_addr[6];
u8 fe_dev_addr[6];
/* Physical parameters of the comms window. */
unsigned long tx_shmem_frame;
#ifdef CONFIG_XEN_NETDEV_GRANT
u16 tx_shmem_handle;
unsigned long tx_shmem_vaddr;
grant_ref_t tx_shmem_ref;
#endif
unsigned long rx_shmem_frame;
#ifdef CONFIG_XEN_NETDEV_GRANT
u16 rx_shmem_handle;
unsigned long rx_shmem_vaddr;
grant_ref_t rx_shmem_ref;
#endif
unsigned int evtchn;
unsigned int remote_evtchn;
/* Physical parameters of the comms window. */
u16 tx_shmem_handle;
grant_ref_t tx_shmem_ref;
u16 rx_shmem_handle;
grant_ref_t rx_shmem_ref;
unsigned int evtchn;
unsigned int remote_evtchn;
/* The shared rings and indexes. */
netif_tx_interface_t *tx;
netif_rx_interface_t *rx;
/* The shared rings and indexes. */
netif_tx_interface_t *tx;
netif_rx_interface_t *rx;
struct vm_struct *comms_area;
/* Private indexes into shared ring. */
NETIF_RING_IDX rx_req_cons;
NETIF_RING_IDX rx_resp_prod; /* private version of shared variable */
#ifdef CONFIG_XEN_NETDEV_GRANT
NETIF_RING_IDX rx_resp_prod_copy; /* private version of shared variable */
#endif
NETIF_RING_IDX tx_req_cons;
NETIF_RING_IDX tx_resp_prod; /* private version of shared variable */
/* Private indexes into shared ring. */
NETIF_RING_IDX rx_req_cons;
NETIF_RING_IDX rx_resp_prod; /* private version of shared variable */
NETIF_RING_IDX rx_resp_prod_copy;
NETIF_RING_IDX tx_req_cons;
NETIF_RING_IDX tx_resp_prod; /* private version of shared variable */
/* Transmit shaping: allow 'credit_bytes' every 'credit_usec'. */
unsigned long credit_bytes;
unsigned long credit_usec;
unsigned long remaining_credit;
struct timer_list credit_timeout;
/* Transmit shaping: allow 'credit_bytes' every 'credit_usec'. */
unsigned long credit_bytes;
unsigned long credit_usec;
unsigned long remaining_credit;
struct timer_list credit_timeout;
/* Miscellaneous private stuff. */
enum { DISCONNECTED, DISCONNECTING, CONNECTED } status;
int active;
struct list_head list; /* scheduling list */
atomic_t refcnt;
struct net_device *dev;
struct net_device_stats stats;
/* Miscellaneous private stuff. */
enum { DISCONNECTED, DISCONNECTING, CONNECTED } status;
int active;
struct list_head list; /* scheduling list */
atomic_t refcnt;
struct net_device *dev;
struct net_device_stats stats;
struct work_struct free_work;
struct work_struct free_work;
} netif_t;
void netif_creditlimit(netif_t *netif);
int netif_disconnect(netif_t *netif);
netif_t *alloc_netif(domid_t domid, unsigned int handle, u8 be_mac[ETH_ALEN]);
void free_netif_callback(netif_t *netif);
void free_netif(netif_t *netif);
int netif_map(netif_t *netif, unsigned long tx_ring_ref,
unsigned long rx_ring_ref, unsigned int evtchn);
#define netif_get(_b) (atomic_inc(&(_b)->refcnt))
#define netif_put(_b) \
do { \
if ( atomic_dec_and_test(&(_b)->refcnt) ) \
free_netif_callback(_b); \
} while (0)
#define netif_put(_b) \
do { \
if ( atomic_dec_and_test(&(_b)->refcnt) ) \
free_netif(_b); \
} while (0)
void netif_xenbus_init(void);
@@ -123,3 +108,13 @@ struct net_device_stats *netif_be_get_stats(struct net_device *dev);
irqreturn_t netif_be_int(int irq, void *dev_id, struct pt_regs *regs);
#endif /* __NETIF__BACKEND__COMMON_H__ */
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/
@@ -11,296 +11,293 @@
static void __netif_up(netif_t *netif)
{
struct net_device *dev = netif->dev;
spin_lock_bh(&dev->xmit_lock);
netif->active = 1;
spin_unlock_bh(&dev->xmit_lock);
(void)bind_evtchn_to_irqhandler(
netif->evtchn, netif_be_int, 0, dev->name, netif);
netif_schedule_work(netif);
struct net_device *dev = netif->dev;
spin_lock_bh(&dev->xmit_lock);
netif->active = 1;
spin_unlock_bh(&dev->xmit_lock);
(void)bind_evtchn_to_irqhandler(
netif->evtchn, netif_be_int, 0, dev->name, netif);
netif_schedule_work(netif);
}
static void __netif_down(netif_t *netif)
{
struct net_device *dev = netif->dev;
spin_lock_bh(&dev->xmit_lock);
netif->active = 0;
spin_unlock_bh(&dev->xmit_lock);
unbind_evtchn_from_irqhandler(netif->evtchn, netif);
netif_deschedule_work(netif);
struct net_device *dev = netif->dev;
spin_lock_bh(&dev->xmit_lock);
netif->active = 0;
spin_unlock_bh(&dev->xmit_lock);
unbind_evtchn_from_irqhandler(netif->evtchn, netif);
netif_deschedule_work(netif);
}
static int net_open(struct net_device *dev)
{
netif_t *netif = netdev_priv(dev);
if (netif->status == CONNECTED)
__netif_up(netif);
netif_start_queue(dev);
return 0;
netif_t *netif = netdev_priv(dev);
if (netif->status == CONNECTED)
__netif_up(netif);
netif_start_queue(dev);
return 0;
}
static int net_close(struct net_device *dev)
{
netif_t *netif = netdev_priv(dev);
netif_stop_queue(dev);
if (netif->status == CONNECTED)
__netif_down(netif);
return 0;
netif_t *netif = netdev_priv(dev);
netif_stop_queue(dev);
if (netif->status == CONNECTED)
__netif_down(netif);
return 0;
}
netif_t *alloc_netif(domid_t domid, unsigned int handle, u8 be_mac[ETH_ALEN])
{
int err = 0, i;
struct net_device *dev;
netif_t *netif;
char name[IFNAMSIZ] = {};
int err = 0, i;
struct net_device *dev;
netif_t *netif;
char name[IFNAMSIZ] = {};
snprintf(name, IFNAMSIZ - 1, "vif%u.%u", domid, handle);
dev = alloc_netdev(sizeof(netif_t), name, ether_setup);
if (dev == NULL) {
DPRINTK("Could not create netif: out of memory\n");
return NULL;
}
snprintf(name, IFNAMSIZ - 1, "vif%u.%u", domid, handle);
dev = alloc_netdev(sizeof(netif_t), name, ether_setup);
if (dev == NULL) {
DPRINTK("Could not create netif: out of memory\n");
return NULL;
}
netif = netdev_priv(dev);
memset(netif, 0, sizeof(*netif));
netif->domid = domid;
netif->handle = handle;
netif->status = DISCONNECTED;
atomic_set(&netif->refcnt, 0);
netif->dev = dev;
netif = netdev_priv(dev);
memset(netif, 0, sizeof(*netif));
netif->domid = domid;
netif->handle = handle;
netif->status = DISCONNECTED;
atomic_set(&netif->refcnt, 0);
netif->dev = dev;
netif->credit_bytes = netif->remaining_credit = ~0UL;
netif->credit_usec = 0UL;
init_timer(&netif->credit_timeout);
netif->credit_bytes = netif->remaining_credit = ~0UL;
netif->credit_usec = 0UL;
init_timer(&netif->credit_timeout);
dev->hard_start_xmit = netif_be_start_xmit;
dev->get_stats = netif_be_get_stats;
dev->open = net_open;
dev->stop = net_close;
dev->features = NETIF_F_NO_CSUM;
dev->hard_start_xmit = netif_be_start_xmit;
dev->get_stats = netif_be_get_stats;
dev->open = net_open;
dev->stop = net_close;
dev->features = NETIF_F_NO_CSUM;
/* Disable queuing. */
dev->tx_queue_len = 0;
/* Disable queuing. */
dev->tx_queue_len = 0;
for (i = 0; i < ETH_ALEN; i++)
if (be_mac[i] != 0)
break;
if (i == ETH_ALEN) {
/*
* Initialise a dummy MAC address. We choose the numerically largest
* non-broadcast address to prevent the address getting stolen by an
* Ethernet bridge for STP purposes. (FE:FF:FF:FF:FF:FF)
*/
memset(dev->dev_addr, 0xFF, ETH_ALEN);
dev->dev_addr[0] &= ~0x01;
} else
memcpy(dev->dev_addr, be_mac, ETH_ALEN);
for (i = 0; i < ETH_ALEN; i++)
if (be_mac[i] != 0)
break;
if (i == ETH_ALEN) {
/*
* Initialise a dummy MAC address. We choose the numerically
* largest non-broadcast address to prevent the address getting
* stolen by an Ethernet bridge for STP purposes.
* (FE:FF:FF:FF:FF:FF)
*/
memset(dev->dev_addr, 0xFF, ETH_ALEN);
dev->dev_addr[0] &= ~0x01;
} else
memcpy(dev->dev_addr, be_mac, ETH_ALEN);
rtnl_lock();
err = register_netdevice(dev);
rtnl_unlock();
if (err) {
DPRINTK("Could not register new net device %s: err=%d\n",
dev->name, err);
free_netdev(dev);
return NULL;
}
rtnl_lock();
err = register_netdevice(dev);
rtnl_unlock();
if (err) {
DPRINTK("Could not register new net device %s: err=%d\n",
dev->name, err);
free_netdev(dev);
return NULL;
}
DPRINTK("Successfully created netif\n");
return netif;
DPRINTK("Successfully created netif\n");
return netif;
}
static int map_frontend_pages(netif_t *netif, unsigned long localaddr,
unsigned long tx_ring_ref,
unsigned long rx_ring_ref)
static int map_frontend_pages(
netif_t *netif, grant_ref_t tx_ring_ref, grant_ref_t rx_ring_ref)
{
#ifdef CONFIG_XEN_NETDEV_GRANT
struct gnttab_map_grant_ref op;
struct gnttab_map_grant_ref op;
/* Map: Use the Grant table reference */
op.host_addr = localaddr;
op.flags = GNTMAP_host_map;
op.ref = tx_ring_ref;
op.dom = netif->domid;
op.host_addr = (unsigned long)netif->comms_area->addr;
op.flags = GNTMAP_host_map;
op.ref = tx_ring_ref;
op.dom = netif->domid;
BUG_ON( HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1) );
if (op.handle < 0) {
DPRINTK(" Grant table operation failure mapping tx_ring_ref!\n");
return op.handle;
}
lock_vm_area(netif->comms_area);
BUG_ON(HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1));
unlock_vm_area(netif->comms_area);
netif->tx_shmem_ref = tx_ring_ref;
netif->tx_shmem_handle = op.handle;
netif->tx_shmem_vaddr = localaddr;
if (op.handle < 0) {
DPRINTK(" Gnttab failure mapping tx_ring_ref!\n");
return op.handle;
}
/* Map: Use the Grant table reference */
op.host_addr = localaddr + PAGE_SIZE;
op.flags = GNTMAP_host_map;
op.ref = rx_ring_ref;
op.dom = netif->domid;
netif->tx_shmem_ref = tx_ring_ref;
netif->tx_shmem_handle = op.handle;
BUG_ON( HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1) );
if (op.handle < 0) {
DPRINTK(" Grant table operation failure mapping rx_ring_ref!\n");
return op.handle;
}
op.host_addr = (unsigned long)netif->comms_area->addr + PAGE_SIZE;
op.flags = GNTMAP_host_map;
op.ref = rx_ring_ref;
op.dom = netif->domid;
netif->rx_shmem_ref = rx_ring_ref;
netif->rx_shmem_handle = op.handle;
netif->rx_shmem_vaddr = localaddr + PAGE_SIZE;
lock_vm_area(netif->comms_area);
BUG_ON(HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1));
unlock_vm_area(netif->comms_area);
#else
pgprot_t prot = __pgprot(_KERNPG_TABLE);
int err;
if (op.handle < 0) {
DPRINTK(" Gnttab failure mapping rx_ring_ref!\n");
return op.handle;
}
err = direct_remap_pfn_range(&init_mm, localaddr,
tx_ring_ref, PAGE_SIZE,
prot, netif->domid);
err |= direct_remap_pfn_range(&init_mm, localaddr + PAGE_SIZE,
rx_ring_ref, PAGE_SIZE,
prot, netif->domid);
netif->rx_shmem_ref = rx_ring_ref;
netif->rx_shmem_handle = op.handle;
if (err)
return err;
#endif
return 0;
return 0;
}
static void unmap_frontend_pages(netif_t *netif)
{
#ifdef CONFIG_XEN_NETDEV_GRANT
struct gnttab_unmap_grant_ref op;
struct gnttab_unmap_grant_ref op;
op.host_addr = netif->tx_shmem_vaddr;
op.handle = netif->tx_shmem_handle;
op.dev_bus_addr = 0;
BUG_ON(HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1));
op.host_addr = (unsigned long)netif->comms_area->addr;
op.handle = netif->tx_shmem_handle;
op.dev_bus_addr = 0;
op.host_addr = netif->rx_shmem_vaddr;
op.handle = netif->rx_shmem_handle;
op.dev_bus_addr = 0;
BUG_ON(HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1));
#endif
lock_vm_area(netif->comms_area);
BUG_ON(HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1));
unlock_vm_area(netif->comms_area);
return;
op.host_addr = (unsigned long)netif->comms_area->addr + PAGE_SIZE;
op.handle = netif->rx_shmem_handle;
op.dev_bus_addr = 0;
lock_vm_area(netif->comms_area);
BUG_ON(HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1));
unlock_vm_area(netif->comms_area);
}
int netif_map(netif_t *netif, unsigned long tx_ring_ref,
unsigned long rx_ring_ref, unsigned int evtchn)
{
struct vm_struct *vma;
evtchn_op_t op = { .cmd = EVTCHNOP_bind_interdomain };
int err;
evtchn_op_t op = { .cmd = EVTCHNOP_bind_interdomain };
int err;
vma = get_vm_area(2*PAGE_SIZE, VM_IOREMAP);
if (vma == NULL)
return -ENOMEM;
netif->comms_area = alloc_vm_area(2*PAGE_SIZE);
if (netif->comms_area == NULL)
return -ENOMEM;
err = map_frontend_pages(netif, (unsigned long)vma->addr, tx_ring_ref,
rx_ring_ref);
if (err) {
vfree(vma->addr);
return err;
}
err = map_frontend_pages(netif, tx_ring_ref, rx_ring_ref);
if (err) {
free_vm_area(netif->comms_area);
return err;
}
op.u.bind_interdomain.dom1 = DOMID_SELF;
op.u.bind_interdomain.dom2 = netif->domid;
op.u.bind_interdomain.port1 = 0;
op.u.bind_interdomain.port2 = evtchn;
err = HYPERVISOR_event_channel_op(&op);
if (err) {
unmap_frontend_pages(netif);
vfree(vma->addr);
return err;
}
op.u.bind_interdomain.dom1 = DOMID_SELF;
op.u.bind_interdomain.dom2 = netif->domid;
op.u.bind_interdomain.port1 = 0;
op.u.bind_interdomain.port2 = evtchn;
err = HYPERVISOR_event_channel_op(&op);
if (err) {
unmap_frontend_pages(netif);
free_vm_area(netif->comms_area);
return err;
}
netif->evtchn = op.u.bind_interdomain.port1;
netif->remote_evtchn = evtchn;
netif->evtchn = op.u.bind_interdomain.port1;
netif->remote_evtchn = evtchn;
netif->tx = (netif_tx_interface_t *)vma->addr;
netif->rx = (netif_rx_interface_t *)((char *)vma->addr + PAGE_SIZE);
netif->tx->resp_prod = netif->rx->resp_prod = 0;
netif_get(netif);
wmb(); /* Other CPUs see new state before interface is started. */
netif->tx = (netif_tx_interface_t *)netif->comms_area->addr;
netif->rx = (netif_rx_interface_t *)
((char *)netif->comms_area->addr + PAGE_SIZE);
netif->tx->resp_prod = netif->rx->resp_prod = 0;
netif_get(netif);
wmb(); /* Other CPUs see new state before interface is started. */
rtnl_lock();
netif->status = CONNECTED;
wmb();
if (netif_running(netif->dev))
__netif_up(netif);
rtnl_unlock();
rtnl_lock();
netif->status = CONNECTED;
wmb();
if (netif_running(netif->dev))
__netif_up(netif);
rtnl_unlock();
return 0;
return 0;
}
static void free_netif(void *arg)
static void free_netif_callback(void *arg)
{
evtchn_op_t op = { .cmd = EVTCHNOP_close };
netif_t *netif = (netif_t *)arg;
evtchn_op_t op = { .cmd = EVTCHNOP_close };
netif_t *netif = (netif_t *)arg;
/*
* These can't be done in netif_disconnect() because at that point there
* may be outstanding requests in the network stack whose asynchronous
* responses must still be notified to the remote driver.
*/
/*
* These can't be done in netif_disconnect() because at that point
* there may be outstanding requests in the network stack whose
* asynchronous responses must still be notified to the remote driver.
*/
op.u.close.port = netif->evtchn;
op.u.close.dom = DOMID_SELF;
HYPERVISOR_event_channel_op(&op);
op.u.close.port = netif->remote_evtchn;
op.u.close.dom = netif->domid;
HYPERVISOR_event_channel_op(&op);
op.u.close.port = netif->evtchn;
op.u.close.dom = DOMID_SELF;
HYPERVISOR_event_channel_op(&op);
op.u.close.port = netif->remote_evtchn;
op.u.close.dom = netif->domid;
HYPERVISOR_event_channel_op(&op);
unregister_netdev(netif->dev);
unregister_netdev(netif->dev);
if (netif->tx) {
unmap_frontend_pages(netif);
vfree(netif->tx); /* Frees netif->rx as well. */
}
if (netif->tx) {
unmap_frontend_pages(netif);
free_vm_area(netif->comms_area);
}
free_netdev(netif->dev);
free_netdev(netif->dev);
}
void free_netif_callback(netif_t *netif)
void free_netif(netif_t *netif)
{
INIT_WORK(&netif->free_work, free_netif, (void *)netif);
schedule_work(&netif->free_work);
INIT_WORK(&netif->free_work, free_netif_callback, (void *)netif);
schedule_work(&netif->free_work);
}
void netif_creditlimit(netif_t *netif)
{
#if 0
/* Set the credit limit (reset remaining credit to new limit). */
netif->credit_bytes = netif->remaining_credit = creditlimit->credit_bytes;
netif->credit_usec = creditlimit->period_usec;
/* Set the credit limit (reset remaining credit to new limit). */
netif->credit_bytes = creditlimit->credit_bytes;
netif->remaining_credit = creditlimit->credit_bytes;
netif->credit_usec = creditlimit->period_usec;
if (netif->status == CONNECTED) {
/*
* Schedule work so that any packets waiting under previous credit
* limit are dealt with (acts like a replenishment point).
*/
netif->credit_timeout.expires = jiffies;
netif_schedule_work(netif);
}
if (netif->status == CONNECTED) {
/*
* Schedule work so that any packets waiting under previous
* credit limit are dealt with (acts as a replenishment point).
*/
netif->credit_timeout.expires = jiffies;
netif_schedule_work(netif);
}
#endif
}
int netif_disconnect(netif_t *netif)
{
if (netif->status == CONNECTED) {
rtnl_lock();
netif->status = DISCONNECTING;
wmb();
if (netif_running(netif->dev))
__netif_down(netif);
rtnl_unlock();
netif_put(netif);
return 0; /* Caller should not send response message. */
}
if (netif->status == CONNECTED) {
rtnl_lock();
netif->status = DISCONNECTING;
wmb();
if (netif_running(netif->dev))
__netif_down(netif);
rtnl_unlock();
netif_put(netif);
return 0; /* Caller should not send response message. */
}
return 1;
return 1;
}
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/
File diff suppressed because it is too large. Load diff
@@ -242,6 +242,7 @@ static int netback_probe(struct xenbus_device *dev,
be->dev = dev;
be->backend_watch.node = dev->nodename;
be->backend_watch.callback = backend_changed;
/* Registration implicitly calls backend_changed. */
err = register_xenbus_watch(&be->backend_watch);
if (err) {
be->backend_watch.node = NULL;
@@ -263,8 +264,6 @@ static int netback_probe(struct xenbus_device *dev,
}
dev->data = be;
backend_changed(&be->backend_watch, dev->nodename);
return 0;
free_be:
@@ -294,3 +293,13 @@ void netif_xenbus_init(void)
{
xenbus_register_backend(&netback);
}
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/
File diff suppressed because it is too large. Load diff
+194 -173
View File
@@ -41,232 +41,253 @@ static struct proc_dir_entry *privcmd_intf;
static int privcmd_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long data)
{
int ret = -ENOSYS;
int ret = -ENOSYS;
switch ( cmd )
{
case IOCTL_PRIVCMD_HYPERCALL:
{
privcmd_hypercall_t hypercall;
switch (cmd) {
case IOCTL_PRIVCMD_HYPERCALL: {
privcmd_hypercall_t hypercall;
if ( copy_from_user(&hypercall, (void *)data, sizeof(hypercall)) )
return -EFAULT;
if (copy_from_user(&hypercall, (void *)data,
sizeof(hypercall)))
return -EFAULT;
#if defined(__i386__)
__asm__ __volatile__ (
"pushl %%ebx; pushl %%ecx; pushl %%edx; pushl %%esi; pushl %%edi; "
"movl 4(%%eax),%%ebx ;"
"movl 8(%%eax),%%ecx ;"
"movl 12(%%eax),%%edx ;"
"movl 16(%%eax),%%esi ;"
"movl 20(%%eax),%%edi ;"
"movl (%%eax),%%eax ;"
TRAP_INSTR "; "
"popl %%edi; popl %%esi; popl %%edx; popl %%ecx; popl %%ebx"
: "=a" (ret) : "0" (&hypercall) : "memory" );
__asm__ __volatile__ (
"pushl %%ebx; pushl %%ecx; pushl %%edx; "
"pushl %%esi; pushl %%edi; "
"movl 4(%%eax),%%ebx ;"
"movl 8(%%eax),%%ecx ;"
"movl 12(%%eax),%%edx ;"
"movl 16(%%eax),%%esi ;"
"movl 20(%%eax),%%edi ;"
"movl (%%eax),%%eax ;"
TRAP_INSTR "; "
"popl %%edi; popl %%esi; popl %%edx; "
"popl %%ecx; popl %%ebx"
: "=a" (ret) : "0" (&hypercall) : "memory" );
#elif defined (__x86_64__)
{
long ign1, ign2, ign3;
__asm__ __volatile__ (
"movq %8,%%r10; movq %9,%%r8;" TRAP_INSTR
: "=a" (ret), "=D" (ign1), "=S" (ign2), "=d" (ign3)
: "0" ((unsigned long)hypercall.op),
"1" ((unsigned long)hypercall.arg[0]),
"2" ((unsigned long)hypercall.arg[1]),
"3" ((unsigned long)hypercall.arg[2]),
"g" ((unsigned long)hypercall.arg[3]),
"g" ((unsigned long)hypercall.arg[4])
: "r11","rcx","r8","r10","memory");
}
{
long ign1, ign2, ign3;
__asm__ __volatile__ (
"movq %8,%%r10; movq %9,%%r8;" TRAP_INSTR
: "=a" (ret), "=D" (ign1),
"=S" (ign2), "=d" (ign3)
: "0" ((unsigned long)hypercall.op),
"1" ((unsigned long)hypercall.arg[0]),
"2" ((unsigned long)hypercall.arg[1]),
"3" ((unsigned long)hypercall.arg[2]),
"g" ((unsigned long)hypercall.arg[3]),
"g" ((unsigned long)hypercall.arg[4])
: "r11","rcx","r8","r10","memory");
}
#elif defined (__ia64__)
__asm__ __volatile__ (
";; mov r14=%2; mov r15=%3; mov r16=%4; mov r17=%5; mov r18=%6; mov
r2=%1; break 0x1000;; mov %0=r8 ;;"
: "=r" (ret)
: "r" (hypercall.op),
"r" (hypercall.arg[0]),
"r" (hypercall.arg[1]),
"r" (hypercall.arg[2]),
"r" (hypercall.arg[3]),
"r" (hypercall.arg[4])
: "r14","r15","r16","r17","r18","r2","r8","memory");
__asm__ __volatile__ (
";; mov r14=%2; mov r15=%3; "
"mov r16=%4; mov r17=%5; mov r18=%6;"
"mov r2=%1; break 0x1000;; mov %0=r8 ;;"
: "=r" (ret)
: "r" (hypercall.op),
"r" (hypercall.arg[0]),
"r" (hypercall.arg[1]),
"r" (hypercall.arg[2]),
"r" (hypercall.arg[3]),
"r" (hypercall.arg[4])
: "r14","r15","r16","r17","r18","r2","r8","memory");
#endif
}
break;
}
break;
#if defined(CONFIG_XEN_PRIVILEGED_GUEST)
case IOCTL_PRIVCMD_MMAP:
{
case IOCTL_PRIVCMD_MMAP: {
#define PRIVCMD_MMAP_SZ 32
privcmd_mmap_t mmapcmd;
privcmd_mmap_entry_t msg[PRIVCMD_MMAP_SZ], *p;
int i, rc;
privcmd_mmap_t mmapcmd;
privcmd_mmap_entry_t msg[PRIVCMD_MMAP_SZ], *p;
int i, rc;
if ( copy_from_user(&mmapcmd, (void *)data, sizeof(mmapcmd)) )
return -EFAULT;
if (copy_from_user(&mmapcmd, (void *)data, sizeof(mmapcmd)))
return -EFAULT;
p = mmapcmd.entry;
p = mmapcmd.entry;
for (i=0; i<mmapcmd.num; i+=PRIVCMD_MMAP_SZ, p+=PRIVCMD_MMAP_SZ)
{
int j, n = ((mmapcmd.num-i)>PRIVCMD_MMAP_SZ)?
PRIVCMD_MMAP_SZ:(mmapcmd.num-i);
for (i = 0; i < mmapcmd.num;
i += PRIVCMD_MMAP_SZ, p += PRIVCMD_MMAP_SZ) {
int j, n = ((mmapcmd.num-i)>PRIVCMD_MMAP_SZ)?
PRIVCMD_MMAP_SZ:(mmapcmd.num-i);
if ( copy_from_user(&msg, p, n*sizeof(privcmd_mmap_entry_t)) )
return -EFAULT;
if (copy_from_user(&msg, p,
n*sizeof(privcmd_mmap_entry_t)))
return -EFAULT;
for ( j = 0; j < n; j++ )
{
struct vm_area_struct *vma =
find_vma( current->mm, msg[j].va );
for (j = 0; j < n; j++) {
struct vm_area_struct *vma =
find_vma( current->mm, msg[j].va );
if ( !vma )
return -EINVAL;
if (!vma)
return -EINVAL;
if ( msg[j].va > PAGE_OFFSET )
return -EINVAL;
if (msg[j].va > PAGE_OFFSET)
return -EINVAL;
if ( (msg[j].va + (msg[j].npages<<PAGE_SHIFT)) > vma->vm_end )
return -EINVAL;
if ((msg[j].va + (msg[j].npages << PAGE_SHIFT))
> vma->vm_end )
return -EINVAL;
if ( (rc = direct_remap_pfn_range(vma->vm_mm,
msg[j].va&PAGE_MASK,
msg[j].mfn,
msg[j].npages<<PAGE_SHIFT,
vma->vm_page_prot,
mmapcmd.dom)) < 0 )
return rc;
}
}
ret = 0;
}
break;
if ((rc = direct_remap_pfn_range(
vma,
msg[j].va&PAGE_MASK,
msg[j].mfn,
msg[j].npages<<PAGE_SHIFT,
vma->vm_page_prot,
mmapcmd.dom)) < 0)
return rc;
}
}
ret = 0;
}
break;
case IOCTL_PRIVCMD_MMAPBATCH:
{
mmu_update_t u;
privcmd_mmapbatch_t m;
struct vm_area_struct *vma = NULL;
unsigned long *p, addr;
unsigned long mfn, ptep;
int i;
case IOCTL_PRIVCMD_MMAPBATCH: {
mmu_update_t u;
privcmd_mmapbatch_t m;
struct vm_area_struct *vma = NULL;
unsigned long *p, addr;
unsigned long mfn, ptep;
int i;
if ( copy_from_user(&m, (void *)data, sizeof(m)) )
{ ret = -EFAULT; goto batch_err; }
if (copy_from_user(&m, (void *)data, sizeof(m))) {
ret = -EFAULT;
goto batch_err;
}
vma = find_vma( current->mm, m.addr );
vma = find_vma( current->mm, m.addr );
if (!vma) {
ret = -EINVAL;
goto batch_err;
}
if ( !vma )
{ ret = -EINVAL; goto batch_err; }
if (m.addr > PAGE_OFFSET) {
ret = -EFAULT;
goto batch_err;
}
if ( m.addr > PAGE_OFFSET )
{ ret = -EFAULT; goto batch_err; }
if ((m.addr + (m.num<<PAGE_SHIFT)) > vma->vm_end) {
ret = -EFAULT;
goto batch_err;
}
if ( (m.addr + (m.num<<PAGE_SHIFT)) > vma->vm_end )
{ ret = -EFAULT; goto batch_err; }
p = m.arr;
addr = m.addr;
for (i = 0; i < m.num; i++, addr += PAGE_SIZE, p++) {
if (get_user(mfn, p))
return -EFAULT;
p = m.arr;
addr = m.addr;
for ( i = 0; i < m.num; i++, addr += PAGE_SIZE, p++ )
{
if ( get_user(mfn, p) )
return -EFAULT;
ret = create_lookup_pte_addr(vma->vm_mm, addr, &ptep);
if (ret)
goto batch_err;
ret = create_lookup_pte_addr(vma->vm_mm, addr, &ptep);
if (ret)
goto batch_err;
u.val = pte_val_ma(pfn_pte_ma(mfn, vma->vm_page_prot));
u.ptr = ptep;
u.val = pte_val_ma(pfn_pte_ma(mfn, vma->vm_page_prot));
u.ptr = ptep;
if (HYPERVISOR_mmu_update(&u, 1, NULL, m.dom) < 0)
put_user(0xF0000000 | mfn, p);
}
if ( unlikely(HYPERVISOR_mmu_update(&u, 1, NULL, m.dom) < 0) )
put_user(0xF0000000 | mfn, p);
}
ret = 0;
break;
ret = 0;
break;
batch_err:
printk("batch_err ret=%d vma=%p addr=%lx num=%d arr=%p %lx-%lx\n",
ret, vma, m.addr, m.num, m.arr,
vma ? vma->vm_start : 0, vma ? vma->vm_end : 0);
break;
}
break;
batch_err:
printk("batch_err ret=%d vma=%p addr=%lx "
"num=%d arr=%p %lx-%lx\n",
ret, vma, m.addr, m.num, m.arr,
vma ? vma->vm_start : 0, vma ? vma->vm_end : 0);
break;
}
break;
#endif
case IOCTL_PRIVCMD_GET_MACH2PHYS_START_MFN:
{
unsigned long m2pv = (unsigned long)machine_to_phys_mapping;
pgd_t *pgd = pgd_offset_k(m2pv);
pud_t *pud = pud_offset(pgd, m2pv);
pmd_t *pmd = pmd_offset(pud, m2pv);
unsigned long m2p_start_mfn = (*(unsigned long *)pmd) >> PAGE_SHIFT;
ret = put_user(m2p_start_mfn, (unsigned long *)data) ? -EFAULT: 0;
}
break;
case IOCTL_PRIVCMD_GET_MACH2PHYS_START_MFN: {
unsigned long m2pv = (unsigned long)machine_to_phys_mapping;
pgd_t *pgd = pgd_offset_k(m2pv);
pud_t *pud = pud_offset(pgd, m2pv);
pmd_t *pmd = pmd_offset(pud, m2pv);
unsigned long m2p_start_mfn =
(*(unsigned long *)pmd) >> PAGE_SHIFT;
ret = put_user(m2p_start_mfn, (unsigned long *)data) ?
-EFAULT: 0;
}
break;
case IOCTL_PRIVCMD_INITDOMAIN_STORE:
{
extern int do_xenbus_probe(void*);
unsigned long page;
case IOCTL_PRIVCMD_INITDOMAIN_STORE: {
extern int do_xenbus_probe(void*);
unsigned long page;
if (xen_start_info->store_evtchn != 0) {
ret = xen_start_info->store_mfn;
break;
}
if (xen_start_info->store_evtchn != 0) {
ret = xen_start_info->store_mfn;
break;
}
/* Allocate page. */
page = get_zeroed_page(GFP_KERNEL);
if (!page) {
ret = -ENOMEM;
break;
}
/* Allocate page. */
page = get_zeroed_page(GFP_KERNEL);
if (!page) {
ret = -ENOMEM;
break;
}
/* We don't refcnt properly, so set reserved on page.
* (this allocation is permanent) */
SetPageReserved(virt_to_page(page));
/* We don't refcnt properly, so set reserved on page.
* (this allocation is permanent) */
SetPageReserved(virt_to_page(page));
/* Initial connect. Setup channel and page. */
xen_start_info->store_evtchn = data;
xen_start_info->store_mfn = pfn_to_mfn(virt_to_phys((void *)page) >>
PAGE_SHIFT);
ret = xen_start_info->store_mfn;
/* Initial connect. Setup channel and page. */
xen_start_info->store_evtchn = data;
xen_start_info->store_mfn =
pfn_to_mfn(virt_to_phys((void *)page) >>
PAGE_SHIFT);
ret = xen_start_info->store_mfn;
/* We'll return then this will wait for daemon to answer */
kthread_run(do_xenbus_probe, NULL, "xenbus_probe");
}
break;
/* We'll return then this will wait for daemon to answer */
kthread_run(do_xenbus_probe, NULL, "xenbus_probe");
}
break;
default:
ret = -EINVAL;
break;
}
return ret;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int privcmd_mmap(struct file * file, struct vm_area_struct * vma)
{
/* DONTCOPY is essential for Xen as copy_page_range is broken. */
vma->vm_flags |= VM_RESERVED | VM_IO | VM_DONTCOPY;
/* DONTCOPY is essential for Xen as copy_page_range is broken. */
vma->vm_flags |= VM_RESERVED | VM_IO | VM_DONTCOPY;
return 0;
return 0;
}
static struct file_operations privcmd_file_ops = {
.ioctl = privcmd_ioctl,
.mmap = privcmd_mmap,
.ioctl = privcmd_ioctl,
.mmap = privcmd_mmap,
};
static int __init privcmd_init(void)
{
privcmd_intf = create_xen_proc_entry("privcmd", 0400);
if ( privcmd_intf != NULL )
privcmd_intf->proc_fops = &privcmd_file_ops;
privcmd_intf = create_xen_proc_entry("privcmd", 0400);
if (privcmd_intf != NULL)
privcmd_intf->proc_fops = &privcmd_file_ops;
return 0;
return 0;
}
__initcall(privcmd_init);
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/
@@ -11,10 +11,10 @@
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <asm-xen/evtchn.h>
#include <asm-xen/driver_util.h>
#include <asm-xen/xen-public/io/tpmif.h>
#include <asm/io.h>
#include <asm/pgalloc.h>
#include <asm-xen/xen-public/io/domain_controller.h>
#if 0
#define ASSERT(_p) \
@@ -34,12 +34,12 @@ typedef struct tpmif_st {
unsigned int handle;
/* Physical parameters of the comms window. */
unsigned long tx_shmem_frame;
unsigned int evtchn;
unsigned int remote_evtchn;
/* The shared rings and indexes. */
tpmif_tx_interface_t *tx;
struct vm_struct *tx_area;
/* Miscellaneous private stuff. */
enum { DISCONNECTED, DISCONNECTING, CONNECTED } status;
@@ -55,9 +55,7 @@ typedef struct tpmif_st {
struct work_struct work;
u16 shmem_handle;
unsigned long shmem_vaddr;
grant_ref_t shmem_ref;
} tpmif_t;
void tpmif_disconnect_complete(tpmif_t * tpmif);
@@ -86,3 +84,13 @@ extern int num_frontends;
#define MMAP_VADDR(t,_req) ((t)->mmap_vstart + ((_req) * PAGE_SIZE))
#endif /* __TPMIF__BACKEND__COMMON_H__ */
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/
@@ -1,4 +1,4 @@
/******************************************************************************
/*****************************************************************************
* drivers/xen/tpmback/interface.c
*
* Vritual TPM interface management.
@@ -14,187 +14,192 @@
#include "common.h"
#include <asm-xen/balloon.h>
#define VMALLOC_VMADDR(x) ((unsigned long)(x))
#define TPMIF_HASHSZ (2 << 5)
#define TPMIF_HASH(_d,_h) (((int)(_d)^(int)(_h))&(TPMIF_HASHSZ-1))
static kmem_cache_t *tpmif_cachep;
int num_frontends = 0;
LIST_HEAD(tpmif_list);
tpmif_t *alloc_tpmif(domid_t domid, long int instance)
tpmif_t *
alloc_tpmif(domid_t domid, long int instance)
{
struct page *page;
tpmif_t *tpmif;
struct page *page;
tpmif_t *tpmif;
tpmif = kmem_cache_alloc(tpmif_cachep, GFP_KERNEL);
if (!tpmif)
return ERR_PTR(-ENOMEM);
tpmif = kmem_cache_alloc(tpmif_cachep, GFP_KERNEL);
if (!tpmif)
return ERR_PTR(-ENOMEM);
memset(tpmif, 0, sizeof(*tpmif));
tpmif->domid = domid;
tpmif->status = DISCONNECTED;
tpmif->tpm_instance = instance;
atomic_set(&tpmif->refcnt, 1);
memset(tpmif, 0, sizeof (*tpmif));
tpmif->domid = domid;
tpmif->status = DISCONNECTED;
tpmif->tpm_instance = instance;
atomic_set(&tpmif->refcnt, 1);
page = balloon_alloc_empty_page_range(TPMIF_TX_RING_SIZE);
BUG_ON(page == NULL);
tpmif->mmap_vstart = (unsigned long)pfn_to_kaddr(page_to_pfn(page));
page = balloon_alloc_empty_page_range(TPMIF_TX_RING_SIZE);
BUG_ON(page == NULL);
tpmif->mmap_vstart = (unsigned long)pfn_to_kaddr(page_to_pfn(page));
list_add(&tpmif->tpmif_list, &tpmif_list);
num_frontends++;
list_add(&tpmif->tpmif_list, &tpmif_list);
num_frontends++;
return tpmif;
return tpmif;
}
void free_tpmif(tpmif_t *tpmif)
void
free_tpmif(tpmif_t * tpmif)
{
num_frontends--;
list_del(&tpmif->tpmif_list);
kmem_cache_free(tpmif_cachep, tpmif);
num_frontends--;
list_del(&tpmif->tpmif_list);
kmem_cache_free(tpmif_cachep, tpmif);
}
tpmif_t *tpmif_find(domid_t domid, long int instance)
tpmif_t *
tpmif_find(domid_t domid, long int instance)
{
tpmif_t *tpmif;
tpmif_t *tpmif;
list_for_each_entry(tpmif, &tpmif_list, tpmif_list) {
if (tpmif->tpm_instance == instance) {
if (tpmif->domid == domid) {
tpmif_get(tpmif);
return tpmif;
} else {
return NULL;
}
}
}
list_for_each_entry(tpmif, &tpmif_list, tpmif_list) {
if (tpmif->tpm_instance == instance) {
if (tpmif->domid == domid) {
tpmif_get(tpmif);
return tpmif;
} else {
return NULL;
}
}
}
return alloc_tpmif(domid, instance);
return alloc_tpmif(domid, instance);
}
static int map_frontend_page(tpmif_t *tpmif, unsigned long localaddr,
unsigned long shared_page)
static int
map_frontend_page(tpmif_t *tpmif, unsigned long shared_page)
{
struct gnttab_map_grant_ref op = {
.host_addr = localaddr,
.flags = GNTMAP_host_map,
.ref = shared_page,
.dom = tpmif->domid,
};
struct gnttab_map_grant_ref op = {
.host_addr = (unsigned long)tpmif->tx_area->addr,
.flags = GNTMAP_host_map,
.ref = shared_page,
.dom = tpmif->domid,
};
BUG_ON( HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1) );
lock_vm_area(tpmif->tx_area);
BUG_ON(HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1));
unlock_vm_area(tpmif->tx_area);
if (op.handle < 0) {
DPRINTK(" Grant table operation failure !\n");
return op.handle;
}
if (op.handle < 0) {
DPRINTK(" Grant table operation failure !\n");
return op.handle;
}
tpmif->shmem_ref = shared_page;
tpmif->shmem_handle = op.handle;
tpmif->shmem_vaddr = localaddr;
return 0;
tpmif->shmem_ref = shared_page;
tpmif->shmem_handle = op.handle;
return 0;
}
static void unmap_frontend_page(tpmif_t *tpmif)
static void
unmap_frontend_page(tpmif_t *tpmif)
{
struct gnttab_unmap_grant_ref op;
struct gnttab_unmap_grant_ref op;
op.host_addr = tpmif->shmem_vaddr;
op.handle = tpmif->shmem_handle;
op.dev_bus_addr = 0;
op.host_addr = (unsigned long)tpmif->tx_area->addr;
op.handle = tpmif->shmem_handle;
op.dev_bus_addr = 0;
BUG_ON(HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1));
lock_vm_area(tpmif->tx_area);
BUG_ON(HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1));
unlock_vm_area(tpmif->tx_area);
}
int tpmif_map(tpmif_t *tpmif,
unsigned long shared_page, unsigned int evtchn)
int
tpmif_map(tpmif_t *tpmif, unsigned long shared_page, unsigned int evtchn)
{
struct vm_struct *vma;
evtchn_op_t op = { .cmd = EVTCHNOP_bind_interdomain };
int err;
evtchn_op_t op = {.cmd = EVTCHNOP_bind_interdomain };
int err;
BUG_ON(tpmif->remote_evtchn);
BUG_ON(tpmif->remote_evtchn);
if ( (vma = get_vm_area(PAGE_SIZE, VM_IOREMAP)) == NULL )
return -ENOMEM;
if ((tpmif->tx_area = alloc_vm_area(PAGE_SIZE)) == NULL)
return -ENOMEM;
err = map_frontend_page(tpmif,
VMALLOC_VMADDR(vma->addr),
shared_page);
if (err) {
vfree(vma->addr);
return err;
}
err = map_frontend_page(tpmif, shared_page);
if (err) {
free_vm_area(tpmif->tx_area);
return err;
}
op.u.bind_interdomain.dom1 = DOMID_SELF;
op.u.bind_interdomain.dom2 = tpmif->domid;
op.u.bind_interdomain.port1 = 0;
op.u.bind_interdomain.port2 = evtchn;
err = HYPERVISOR_event_channel_op(&op);
if (err) {
unmap_frontend_page(tpmif);
vfree(vma->addr);
return err;
}
op.u.bind_interdomain.dom1 = DOMID_SELF;
op.u.bind_interdomain.dom2 = tpmif->domid;
op.u.bind_interdomain.port1 = 0;
op.u.bind_interdomain.port2 = evtchn;
err = HYPERVISOR_event_channel_op(&op);
if (err) {
unmap_frontend_page(tpmif);
free_vm_area(tpmif->tx_area);
return err;
}
tpmif->evtchn = op.u.bind_interdomain.port1;
tpmif->remote_evtchn = evtchn;
tpmif->evtchn = op.u.bind_interdomain.port1;
tpmif->remote_evtchn = evtchn;
tpmif->tx = (tpmif_tx_interface_t *) vma->addr;
tpmif->tx = (tpmif_tx_interface_t *)tpmif->tx_area->addr;
bind_evtchn_to_irqhandler(tpmif->evtchn,
tpmif_be_int,
0,
"tpmif-backend",
tpmif);
tpmif->status = CONNECTED;
tpmif->shmem_ref = shared_page;
tpmif->active = 1;
bind_evtchn_to_irqhandler(tpmif->evtchn,
tpmif_be_int, 0, "tpmif-backend", tpmif);
tpmif->status = CONNECTED;
tpmif->shmem_ref = shared_page;
tpmif->active = 1;
return 0;
return 0;
}
static void __tpmif_disconnect_complete(void *arg)
static void
__tpmif_disconnect_complete(void *arg)
{
evtchn_op_t op = { .cmd = EVTCHNOP_close };
tpmif_t *tpmif = (tpmif_t *) arg;
evtchn_op_t op = {.cmd = EVTCHNOP_close };
tpmif_t *tpmif = (tpmif_t *) arg;
op.u.close.port = tpmif->evtchn;
op.u.close.dom = DOMID_SELF;
HYPERVISOR_event_channel_op(&op);
op.u.close.port = tpmif->remote_evtchn;
op.u.close.dom = tpmif->domid;
HYPERVISOR_event_channel_op(&op);
op.u.close.port = tpmif->evtchn;
op.u.close.dom = DOMID_SELF;
HYPERVISOR_event_channel_op(&op);
op.u.close.port = tpmif->remote_evtchn;
op.u.close.dom = tpmif->domid;
HYPERVISOR_event_channel_op(&op);
if (tpmif->evtchn)
unbind_evtchn_from_irqhandler(tpmif->evtchn, tpmif);
if (tpmif->evtchn)
unbind_evtchn_from_irqhandler(tpmif->evtchn, tpmif);
if (tpmif->tx) {
unmap_frontend_page(tpmif);
vfree(tpmif->tx);
}
if (tpmif->tx) {
unmap_frontend_page(tpmif);
free_vm_area(tpmif->tx_area);
}
free_tpmif(tpmif);
free_tpmif(tpmif);
}
void tpmif_disconnect_complete(tpmif_t * tpmif)
void
tpmif_disconnect_complete(tpmif_t * tpmif)
{
INIT_WORK(&tpmif->work, __tpmif_disconnect_complete, (void *)tpmif);
schedule_work(&tpmif->work);
INIT_WORK(&tpmif->work, __tpmif_disconnect_complete, (void *)tpmif);
schedule_work(&tpmif->work);
}
void __init tpmif_interface_init(void)
void __init
tpmif_interface_init(void)
{
tpmif_cachep = kmem_cache_create("tpmif_cache", sizeof(tpmif_t),
0, 0, NULL, NULL);
tpmif_cachep = kmem_cache_create("tpmif_cache", sizeof (tpmif_t),
0, 0, NULL, NULL);
}
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/
@@ -566,7 +566,7 @@ vtpm_op_read(struct file *file,
* the more time we give the TPM to process the request.
*/
mod_timer(&pak->processing_timer,
jiffies + (num_frontends * 10 * HZ));
jiffies + (num_frontends * 60 * HZ));
dataex.copied_so_far = 0;
}
}
@@ -850,7 +850,7 @@ static int vtpm_queue_packet(struct packet *pak)
write_lock_irqsave(&dataex.pak_lock, flags);
list_add_tail(&pak->next, &dataex.pending_pak);
/* give the TPM some time to pick up the request */
mod_timer(&pak->processing_timer, jiffies + (10 * HZ));
mod_timer(&pak->processing_timer, jiffies + (30 * HZ));
write_unlock_irqrestore(&dataex.pak_lock,
flags);
@@ -1075,3 +1075,13 @@ tpmback_init(void)
}
__initcall(tpmback_init);
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/
@@ -213,6 +213,7 @@ static int tpmback_probe(struct xenbus_device *dev,
be->dev = dev;
be->backend_watch.node = dev->nodename;
/* Implicitly calls backend_changed() once. */
be->backend_watch.callback = backend_changed;
be->instance = -1;
err = register_xenbus_watch(&be->backend_watch);
@@ -236,8 +237,6 @@ static int tpmback_probe(struct xenbus_device *dev,
}
dev->data = be;
backend_changed(&be->backend_watch, dev->nodename);
return err;
free_be:
@@ -269,3 +268,13 @@ void tpmif_xenbus_init(void)
{
xenbus_register_backend(&tpmback);
}
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/
@@ -46,7 +46,6 @@
#include <asm-xen/xen-public/io/tpmif.h>
#include <asm/uaccess.h>
#include <asm-xen/xenbus.h>
#include <asm-xen/xen-public/io/domain_controller.h>
#include <asm-xen/xen-public/grant_table.h>
#include "tpmfront.h"
@@ -258,18 +257,24 @@ static int setup_tpmring(struct xenbus_device *dev,
tpm_allocate_buffers(tp);
info->ring_ref = gnttab_claim_grant_reference(&gref_head);
ASSERT(info->ring_ref != -ENOSPC);
gnttab_grant_foreign_access_ref(info->ring_ref,
backend_id,
(virt_to_machine(tp->tx) >> PAGE_SHIFT),
0);
err = gnttab_grant_foreign_access(backend_id,
(virt_to_machine(tp->tx) >> PAGE_SHIFT),
0);
if (err == -ENOSPC) {
free_page((unsigned long)sring);
tp->tx = NULL;
xenbus_dev_error(dev, err, "allocating grant reference");
return err;
}
info->ring_ref = err;
op.u.alloc_unbound.dom = backend_id;
err = HYPERVISOR_event_channel_op(&op);
if (err) {
gnttab_end_foreign_access(info->ring_ref, 0);
free_page((unsigned long)sring);
tp->tx = 0;
tp->tx = NULL;
xenbus_dev_error(dev, err, "allocating event channel");
return err;
}
@@ -283,6 +288,7 @@ static void destroy_tpmring(struct tpmfront_info *info, struct tpm_private *tp)
tpmif_set_connected_state(tp,0);
if ( tp->tx != NULL ) {
gnttab_end_foreign_access(info->ring_ref, 0);
free_page((unsigned long)tp->tx);
tp->tx = NULL;
}
@@ -412,7 +418,6 @@ static int tpmfront_probe(struct xenbus_device *dev,
return err;
}
watch_for_status(&info->watch, info->watch.node);
return 0;
}
@@ -736,3 +741,13 @@ tpmif_init(void)
}
__initcall(tpmif_init);
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/
@@ -2,7 +2,8 @@
#define TPM_FRONT_H
struct tpm_private {
struct tpm_private
{
tpmif_tx_interface_t *tx;
unsigned int evtchn;
int connected;
@@ -29,10 +30,21 @@ struct tpmfront_info
};
struct tx_buffer {
struct tx_buffer
{
unsigned int size; // available space in data
unsigned int len; // used space in data
unsigned char *data; // pointer to a page
};
#endif
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/
@@ -1,83 +0,0 @@
#ifndef __USBIF__BACKEND__COMMON_H__
#define __USBIF__BACKEND__COMMON_H__
#include <linux/config.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/rbtree.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/blkdev.h>
#include <asm/io.h>
#include <asm/setup.h>
#include <asm/pgalloc.h>
#include <asm/hypervisor.h>
#include <asm-xen/xen-public/io/usbif.h>
#if 0
#define ASSERT(_p) \
if ( !(_p) ) { printk("Assertion '%s' failed, line %d, file %s", #_p , \
__LINE__, __FILE__); *(int*)0=0; }
#define DPRINTK(_f, _a...) printk(KERN_ALERT "(file=%s, line=%d) " _f, \
__FILE__ , __LINE__ , ## _a )
#else
#define ASSERT(_p) ((void)0)
#define DPRINTK(_f, _a...) ((void)0)
#endif
typedef struct usbif_priv_st usbif_priv_t;
struct usbif_priv_st {
/* Unique identifier for this interface. */
domid_t domid;
unsigned int handle;
/* Physical parameters of the comms window. */
unsigned long shmem_frame;
unsigned int evtchn;
/* Comms Information */
usbif_back_ring_t usb_ring;
/* Private fields. */
enum { DISCONNECTED, DISCONNECTING, CONNECTED } status;
/*
* DISCONNECT response is deferred until pending requests are ack'ed.
* We therefore need to store the id from the original request.
*/
u8 disconnect_rspid;
usbif_priv_t *hash_next;
struct list_head usbif_list;
spinlock_t usb_ring_lock;
atomic_t refcnt;
struct work_struct work;
};
void usbif_create(usbif_be_create_t *create);
void usbif_destroy(usbif_be_destroy_t *destroy);
void usbif_connect(usbif_be_connect_t *connect);
int usbif_disconnect(usbif_be_disconnect_t *disconnect, u8 rsp_id);
void usbif_disconnect_complete(usbif_priv_t *up);
void usbif_release_port(usbif_be_release_port_t *msg);
int usbif_claim_port(usbif_be_claim_port_t *msg);
void usbif_release_ports(usbif_priv_t *up);
usbif_priv_t *usbif_find(domid_t domid);
#define usbif_get(_b) (atomic_inc(&(_b)->refcnt))
#define usbif_put(_b) \
do { \
if ( atomic_dec_and_test(&(_b)->refcnt) ) \
usbif_disconnect_complete(_b); \
} while (0)
void usbif_interface_init(void);
void usbif_ctrlif_init(void);
void usbif_deschedule(usbif_priv_t *up);
void remove_from_usbif_list(usbif_priv_t *up);
irqreturn_t usbif_be_int(int irq, void *dev_id, struct pt_regs *regs);
#endif /* __USBIF__BACKEND__COMMON_H__ */
@@ -1,61 +0,0 @@
/******************************************************************************
* arch/xen/drivers/usbif/backend/control.c
*
* Routines for interfacing with the control plane.
*
* Copyright (c) 2004, Keir Fraser
*/
#include "common.h"
static void usbif_ctrlif_rx(ctrl_msg_t *msg, unsigned long id)
{
DPRINTK("Received usbif backend message, subtype=%d\n", msg->subtype);
switch ( msg->subtype )
{
case CMSG_USBIF_BE_CREATE:
usbif_create((usbif_be_create_t *)&msg->msg[0]);
break;
case CMSG_USBIF_BE_DESTROY:
usbif_destroy((usbif_be_destroy_t *)&msg->msg[0]);
break;
case CMSG_USBIF_BE_CONNECT:
usbif_connect((usbif_be_connect_t *)&msg->msg[0]);
break;
case CMSG_USBIF_BE_DISCONNECT:
if ( !usbif_disconnect((usbif_be_disconnect_t *)&msg->msg[0],msg->id) )
return; /* Sending the response is deferred until later. */
break;
case CMSG_USBIF_BE_CLAIM_PORT:
usbif_claim_port((usbif_be_claim_port_t *)&msg->msg[0]);
break;
case CMSG_USBIF_BE_RELEASE_PORT:
usbif_release_port((usbif_be_release_port_t *)&msg->msg[0]);
break;
default:
DPRINTK("Parse error while reading message subtype %d, len %d\n",
msg->subtype, msg->length);
msg->length = 0;
break;
}
ctrl_if_send_response(msg);
}
void usbif_ctrlif_init(void)
{
ctrl_msg_t cmsg;
usbif_be_driver_status_changed_t st;
(void)ctrl_if_register_receiver(CMSG_USBIF_BE, usbif_ctrlif_rx,
CALLBACK_IN_BLOCKING_CONTEXT);
/* Send a driver-UP notification to the domain controller. */
cmsg.type = CMSG_USBIF_BE;
cmsg.subtype = CMSG_USBIF_BE_DRIVER_STATUS_CHANGED;
cmsg.length = sizeof(usbif_be_driver_status_changed_t);
st.status = USBIF_DRIVER_STATUS_UP;
memcpy(cmsg.msg, &st, sizeof(st));
ctrl_if_send_message_block(&cmsg, NULL, 0, TASK_UNINTERRUPTIBLE);
}
@@ -1,242 +0,0 @@
/******************************************************************************
* arch/xen/drivers/usbif/backend/interface.c
*
* USB device interface management.
*
* by Mark Williamson, Copyright (c) 2004
*/
#include "common.h"
#define USBIF_HASHSZ 1024
#define USBIF_HASH(_d) (((int)(_d))&(USBIF_HASHSZ-1))
static kmem_cache_t *usbif_priv_cachep;
static usbif_priv_t *usbif_priv_hash[USBIF_HASHSZ];
usbif_priv_t *usbif_find(domid_t domid)
{
usbif_priv_t *up = usbif_priv_hash[USBIF_HASH(domid)];
while ( (up != NULL ) && ( up->domid != domid ) )
up = up->hash_next;
return up;
}
static void __usbif_disconnect_complete(void *arg)
{
usbif_priv_t *usbif = (usbif_priv_t *)arg;
ctrl_msg_t cmsg;
usbif_be_disconnect_t disc;
/*
* These can't be done in usbif_disconnect() because at that point there
* may be outstanding requests at the device whose asynchronous responses
* must still be notified to the remote driver.
*/
vfree(usbif->usb_ring.sring);
/* Construct the deferred response message. */
cmsg.type = CMSG_USBIF_BE;
cmsg.subtype = CMSG_USBIF_BE_DISCONNECT;
cmsg.id = usbif->disconnect_rspid;
cmsg.length = sizeof(usbif_be_disconnect_t);
disc.domid = usbif->domid;
disc.status = USBIF_BE_STATUS_OKAY;
memcpy(cmsg.msg, &disc, sizeof(disc));
/*
* Make sure message is constructed /before/ status change, because
* after the status change the 'usbif' structure could be deallocated at
* any time. Also make sure we send the response /after/ status change,
* as otherwise a subsequent CONNECT request could spuriously fail if
* another CPU doesn't see the status change yet.
*/
mb();
if ( usbif->status != DISCONNECTING )
BUG();
usbif->status = DISCONNECTED;
mb();
/* Send the successful response. */
ctrl_if_send_response(&cmsg);
}
void usbif_disconnect_complete(usbif_priv_t *up)
{
INIT_WORK(&up->work, __usbif_disconnect_complete, (void *)up);
schedule_work(&up->work);
}
void usbif_create(usbif_be_create_t *create)
{
domid_t domid = create->domid;
usbif_priv_t **pup, *up;
if ( (up = kmem_cache_alloc(usbif_priv_cachep, GFP_KERNEL)) == NULL )
{
DPRINTK("Could not create usbif: out of memory\n");
create->status = USBIF_BE_STATUS_OUT_OF_MEMORY;
return;
}
memset(up, 0, sizeof(*up));
up->domid = domid;
up->status = DISCONNECTED;
spin_lock_init(&up->usb_ring_lock);
atomic_set(&up->refcnt, 0);
pup = &usbif_priv_hash[USBIF_HASH(domid)];
while ( *pup != NULL )
{
if ( (*pup)->domid == domid )
{
create->status = USBIF_BE_STATUS_INTERFACE_EXISTS;
kmem_cache_free(usbif_priv_cachep, up);
return;
}
pup = &(*pup)->hash_next;
}
up->hash_next = *pup;
*pup = up;
create->status = USBIF_BE_STATUS_OKAY;
}
void usbif_destroy(usbif_be_destroy_t *destroy)
{
domid_t domid = destroy->domid;
usbif_priv_t **pup, *up;
pup = &usbif_priv_hash[USBIF_HASH(domid)];
while ( (up = *pup) != NULL )
{
if ( up->domid == domid )
{
if ( up->status != DISCONNECTED )
goto still_connected;
goto destroy;
}
pup = &up->hash_next;
}
destroy->status = USBIF_BE_STATUS_INTERFACE_NOT_FOUND;
return;
still_connected:
destroy->status = USBIF_BE_STATUS_INTERFACE_CONNECTED;
return;
destroy:
*pup = up->hash_next;
usbif_release_ports(up);
kmem_cache_free(usbif_priv_cachep, up);
destroy->status = USBIF_BE_STATUS_OKAY;
}
void usbif_connect(usbif_be_connect_t *connect)
{
domid_t domid = connect->domid;
unsigned int evtchn = connect->evtchn;
unsigned long shmem_frame = connect->shmem_frame;
struct vm_struct *vma;
pgprot_t prot;
int error;
usbif_priv_t *up;
usbif_sring_t *sring;
up = usbif_find(domid);
if ( unlikely(up == NULL) )
{
DPRINTK("usbif_connect attempted for non-existent usbif (%u)\n",
connect->domid);
connect->status = USBIF_BE_STATUS_INTERFACE_NOT_FOUND;
return;
}
if ( (vma = get_vm_area(PAGE_SIZE, VM_IOREMAP)) == NULL )
{
connect->status = USBIF_BE_STATUS_OUT_OF_MEMORY;
return;
}
prot = __pgprot(_KERNPG_TABLE);
error = direct_remap_pfn_range(&init_mm, VMALLOC_VMADDR(vma->addr),
shmem_frame, PAGE_SIZE,
prot, domid);
if ( error != 0 )
{
if ( error == -ENOMEM )
connect->status = USBIF_BE_STATUS_OUT_OF_MEMORY;
else if ( error == -EFAULT )
connect->status = USBIF_BE_STATUS_MAPPING_ERROR;
else
connect->status = USBIF_BE_STATUS_ERROR;
vfree(vma->addr);
return;
}
if ( up->status != DISCONNECTED )
{
connect->status = USBIF_BE_STATUS_INTERFACE_CONNECTED;
vfree(vma->addr);
return;
}
sring = (usbif_sring_t *)vma->addr;
SHARED_RING_INIT(sring);
BACK_RING_INIT(&up->usb_ring, sring, PAGE_SIZE);
up->evtchn = evtchn;
up->shmem_frame = shmem_frame;
up->status = CONNECTED;
usbif_get(up);
(void)bind_evtchn_to_irqhandler(
evtchn, usbif_be_int, 0, "usbif-backend", up);
connect->status = USBIF_BE_STATUS_OKAY;
}
/* Remove URBs for this interface before destroying it. */
void usbif_deschedule(usbif_priv_t *up)
{
remove_from_usbif_list(up);
}
int usbif_disconnect(usbif_be_disconnect_t *disconnect, u8 rsp_id)
{
domid_t domid = disconnect->domid;
usbif_priv_t *up;
up = usbif_find(domid);
if ( unlikely(up == NULL) )
{
DPRINTK("usbif_disconnect attempted for non-existent usbif"
" (%u)\n", disconnect->domid);
disconnect->status = USBIF_BE_STATUS_INTERFACE_NOT_FOUND;
return 1; /* Caller will send response error message. */
}
if ( up->status == CONNECTED )
{
up->status = DISCONNECTING;
up->disconnect_rspid = rsp_id;
wmb(); /* Let other CPUs see the status change. */
unbind_evtchn_from_irqhandler(up->evtchn, up);
usbif_deschedule(up);
usbif_put(up);
return 0; /* Caller should not send response message. */
}
disconnect->status = USBIF_BE_STATUS_OKAY;
return 1;
}
void __init usbif_interface_init(void)
{
usbif_priv_cachep = kmem_cache_create("usbif_priv_cache",
sizeof(usbif_priv_t),
0, 0, NULL, NULL);
memset(usbif_priv_hash, 0, sizeof(usbif_priv_hash));
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -1,182 +0,0 @@
/******************************************************************************
* xhci.h
*
* Private definitions for the Xen Virtual USB Controller. Based on
* drivers/usb/host/uhci.h from Linux. Copyright for the imported content is
* retained by the original authors.
*
* Modifications are:
* Copyright (C) 2004 Intel Research Cambridge
* Copyright (C) 2004, 2005 Mark Williamson
*/
#ifndef __LINUX_XHCI_H
#define __LINUX_XHCI_H
#include <linux/list.h>
#include <linux/usb.h>
#include <asm-xen/xen-public/io/usbif.h>
#include <linux/spinlock.h>
/* xhci_port_t - current known state of a virtual hub ports */
typedef struct {
unsigned int cs :1; /* Connection status. */
unsigned int cs_chg :1; /* Connection status change. */
unsigned int pe :1; /* Port enable. */
unsigned int pe_chg :1; /* Port enable change. */
unsigned int susp :1; /* Suspended. */
unsigned int lsda :1; /* Low speed device attached. */
unsigned int pr :1; /* Port reset. */
} xhci_port_t;
/* struct virt_root_hub - state related to the virtual root hub */
struct virt_root_hub {
struct usb_device *dev;
int devnum; /* Address of Root Hub endpoint */
struct urb *urb;
void *int_addr;
int send;
int interval;
int numports;
int c_p_r[8];
struct timer_list rh_int_timer;
spinlock_t port_state_lock;
xhci_port_t *ports;
};
/* struct xhci - contains the state associated with a single USB interface */
struct xhci {
#ifdef CONFIG_PROC_FS
/* procfs */
int num;
struct proc_dir_entry *proc_entry;
#endif
int evtchn; /* Interdom channel to backend */
enum {
USBIF_STATE_CONNECTED = 2,
USBIF_STATE_DISCONNECTED = 1,
USBIF_STATE_CLOSED = 0
} state; /* State of this USB interface */
unsigned long recovery; /* boolean recovery in progress flag */
unsigned long bandwidth;
struct usb_bus *bus;
/* Main list of URB's currently controlled by this HC */
spinlock_t urb_list_lock;
struct list_head urb_list; /* P: xhci->urb_list_lock */
/* List of URB's awaiting completion callback */
spinlock_t complete_list_lock;
struct list_head complete_list; /* P: xhci->complete_list_lock */
struct virt_root_hub rh; /* private data of the virtual root hub */
spinlock_t ring_lock;
usbif_front_ring_t usb_ring;
int awaiting_reset;
};
/* per-URB private data structure for the host controller */
struct urb_priv {
struct urb *urb;
usbif_iso_t *schedule;
struct usb_device *dev;
int in_progress : 1; /* QH was queued (not linked in) */
int short_control_packet : 1; /* If we get a short packet during */
/* a control transfer, retrigger */
/* the status phase */
int status; /* Final status */
unsigned long inserttime; /* In jiffies */
struct list_head complete_list; /* P: xhci->complete_list_lock */
};
/*
* Locking in xhci.c
*
* spinlocks are used extensively to protect the many lists and data
* structures we have. It's not that pretty, but it's necessary. We
* need to be done with all of the locks (except complete_list_lock) when
* we call urb->complete. I've tried to make it simple enough so I don't
* have to spend hours racking my brain trying to figure out if the
* locking is safe.
*
* Here's the safe locking order to prevent deadlocks:
*
* #1 xhci->urb_list_lock
* #2 urb->lock
* #3 xhci->urb_remove_list_lock
* #4 xhci->complete_list_lock
*
* If you're going to grab 2 or more locks at once, ALWAYS grab the lock
* at the lowest level FIRST and NEVER grab locks at the same level at the
* same time.
*
* So, if you need xhci->urb_list_lock, grab it before you grab urb->lock
*/
/* -------------------------------------------------------------------------
Virtual Root HUB
------------------------------------------------------------------------- */
/* destination of request */
#define RH_DEVICE 0x00
#define RH_INTERFACE 0x01
#define RH_ENDPOINT 0x02
#define RH_OTHER 0x03
#define RH_CLASS 0x20
#define RH_VENDOR 0x40
/* Requests: bRequest << 8 | bmRequestType */
#define RH_GET_STATUS 0x0080
#define RH_CLEAR_FEATURE 0x0100
#define RH_SET_FEATURE 0x0300
#define RH_SET_ADDRESS 0x0500
#define RH_GET_DESCRIPTOR 0x0680
#define RH_SET_DESCRIPTOR 0x0700
#define RH_GET_CONFIGURATION 0x0880
#define RH_SET_CONFIGURATION 0x0900
#define RH_GET_STATE 0x0280
#define RH_GET_INTERFACE 0x0A80
#define RH_SET_INTERFACE 0x0B00
#define RH_SYNC_FRAME 0x0C80
/* Our Vendor Specific Request */
#define RH_SET_EP 0x2000
/* Hub port features */
#define RH_PORT_CONNECTION 0x00
#define RH_PORT_ENABLE 0x01
#define RH_PORT_SUSPEND 0x02
#define RH_PORT_OVER_CURRENT 0x03
#define RH_PORT_RESET 0x04
#define RH_PORT_POWER 0x08
#define RH_PORT_LOW_SPEED 0x09
#define RH_C_PORT_CONNECTION 0x10
#define RH_C_PORT_ENABLE 0x11
#define RH_C_PORT_SUSPEND 0x12
#define RH_C_PORT_OVER_CURRENT 0x13
#define RH_C_PORT_RESET 0x14
/* Hub features */
#define RH_C_HUB_LOCAL_POWER 0x00
#define RH_C_HUB_OVER_CURRENT 0x01
#define RH_DEVICE_REMOTE_WAKEUP 0x00
#define RH_ENDPOINT_STALL 0x01
/* Our Vendor Specific feature */
#define RH_REMOVE_EP 0x00
#define RH_ACK 0x01
#define RH_REQ_ERR -1
#define RH_NACK 0x00
#endif
+73
View File
@@ -0,0 +1,73 @@
#include <linux/config.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <asm/uaccess.h>
#include <asm-xen/driver_util.h>
static int f(pte_t *pte, struct page *pte_page, unsigned long addr, void *data)
{
/* generic_page_range() does all the hard work. */
return 0;
}
struct vm_struct *alloc_vm_area(unsigned long size)
{
struct vm_struct *area;
area = get_vm_area(size, VM_IOREMAP);
if (area == NULL)
return NULL;
/*
* This ensures that page tables are constructed for this region
* of kernel virtual address space and mapped into init_mm.
*/
if (generic_page_range(&init_mm, (unsigned long)area->addr,
area->size, f, NULL)) {
free_vm_area(area);
return NULL;
}
return area;
}
void free_vm_area(struct vm_struct *area)
{
BUG_ON(remove_vm_area(area->addr) != area);
kfree(area);
}
void lock_vm_area(struct vm_struct *area)
{
unsigned long i;
char c;
/*
* Prevent context switch to a lazy mm that doesn't have this area
* mapped into its page tables.
*/
preempt_disable();
/*
* Ensure that the page tables are mapped into the current mm. The
* page-fault path will copy the page directory pointers from init_mm.
*/
for (i = 0; i < area->size; i += PAGE_SIZE)
(void)__get_user(c, (char *)area->addr + i);
}
void unlock_vm_area(struct vm_struct *area)
{
preempt_enable();
}
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/
@@ -231,3 +231,13 @@ void xb_suspend_comms(void)
unbind_evtchn_from_irqhandler(xen_start_info->store_evtchn, &xb_waitq);
}
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/
@@ -39,3 +39,13 @@ int xs_input_avail(void);
extern wait_queue_head_t xb_waitq;
#endif /* _XENBUS_COMMS_H */
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/
@@ -186,3 +186,13 @@ xenbus_dev_init(void)
}
__initcall(xenbus_dev_init);
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/
@@ -687,3 +687,13 @@ static int __init xenbus_probe_init(void)
}
postcore_initcall(xenbus_probe_init);
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/
@@ -253,31 +253,19 @@ void *xenbus_read(const char *dir, const char *node, unsigned int *len)
EXPORT_SYMBOL(xenbus_read);
/* Write the value of a single file.
* Returns -err on failure. createflags can be 0, O_CREAT, or O_CREAT|O_EXCL.
* Returns -err on failure.
*/
int xenbus_write(const char *dir, const char *node,
const char *string, int createflags)
int xenbus_write(const char *dir, const char *node, const char *string)
{
const char *flags, *path;
struct kvec iovec[3];
const char *path;
struct kvec iovec[2];
path = join(dir, node);
/* Format: Flags (as string), path, data. */
if (createflags == 0)
flags = XS_WRITE_NONE;
else if (createflags == O_CREAT)
flags = XS_WRITE_CREATE;
else if (createflags == (O_CREAT|O_EXCL))
flags = XS_WRITE_CREATE_EXCL;
else
return -EINVAL;
iovec[0].iov_base = (void *)path;
iovec[0].iov_len = strlen(path) + 1;
iovec[1].iov_base = (void *)flags;
iovec[1].iov_len = strlen(flags) + 1;
iovec[2].iov_base = (void *)string;
iovec[2].iov_len = strlen(string);
iovec[1].iov_base = (void *)string;
iovec[1].iov_len = strlen(string);
return xs_error(xs_talkv(XS_WRITE, iovec, ARRAY_SIZE(iovec), NULL));
}
@@ -357,7 +345,7 @@ int xenbus_printf(const char *dir, const char *node, const char *fmt, ...)
va_end(ap);
BUG_ON(ret > sizeof(printf_buffer)-1);
return xenbus_write(dir, node, printf_buffer, O_CREAT);
return xenbus_write(dir, node, printf_buffer);
}
EXPORT_SYMBOL(xenbus_printf);
@@ -377,7 +365,7 @@ void xenbus_dev_error(struct xenbus_device *dev, int err, const char *fmt, ...)
BUG_ON(len + ret > sizeof(printf_buffer)-1);
dev->has_error = 1;
if (xenbus_write(dev->nodename, "error", printf_buffer, O_CREAT) != 0)
if (xenbus_write(dev->nodename, "error", printf_buffer) != 0)
printk("xenbus: failed to write error node for %s (%s)\n",
dev->nodename, printf_buffer);
}
@@ -578,3 +566,13 @@ int xs_init(void)
return PTR_ERR(watcher);
return 0;
}
/*
* Local variables:
* c-file-style: "linux"
* indent-tabs-mode: t
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/
@@ -261,7 +261,6 @@ extern int sysctl_legacy_va_layout;
/* VIRT <-> MACHINE conversion */
#define virt_to_machine(v) (phys_to_machine(__pa(v)))
#define machine_to_virt(m) (__va(machine_to_phys(m)))
#define virt_to_mfn(v) (pfn_to_mfn(__pa(v) >> PAGE_SHIFT))
#define mfn_to_virt(m) (__va(mfn_to_pfn(m) << PAGE_SHIFT))
@@ -460,7 +460,7 @@ void make_pages_writable(void *va, unsigned int nr);
#define kern_addr_valid(addr) (1)
#endif /* !CONFIG_DISCONTIGMEM */
int direct_remap_pfn_range(struct mm_struct *mm,
int direct_remap_pfn_range(struct vm_area_struct *vma,
unsigned long address,
unsigned long mfn,
unsigned long size,
@@ -474,10 +474,10 @@ int touch_pte_range(struct mm_struct *mm,
unsigned long size);
#define io_remap_page_range(vma,from,phys,size,prot) \
direct_remap_pfn_range(vma->vm_mm,from,phys>>PAGE_SHIFT,size,prot,DOMID_IO)
direct_remap_pfn_range(vma,from,(phys)>>PAGE_SHIFT,size,prot,DOMID_IO)
#define io_remap_pfn_range(vma,from,pfn,size,prot) \
direct_remap_pfn_range(vma->vm_mm,from,pfn,size,prot,DOMID_IO)
direct_remap_pfn_range(vma,from,pfn,size,prot,DOMID_IO)
#define MK_IOSPACE_PFN(space, pfn) (pfn)
#define GET_IOSPACE(pfn) 0
@@ -497,11 +497,22 @@ __asm__ __volatile__("6667:movl %1, %0\n6668:\n" \
* includes these barriers, for example.
*/
/*
* Don't use smp_processor_id() in preemptible code: debug builds will barf.
* It's okay in these cases as we only read the upcall mask in preemptible
* regions, which is always safe.
*/
#ifdef CONFIG_SMP
#define __this_cpu() __smp_processor_id()
#else
#define __this_cpu() 0
#endif
#define __cli() \
do { \
vcpu_info_t *_vcpu; \
preempt_disable(); \
_vcpu = &HYPERVISOR_shared_info->vcpu_data[smp_processor_id()]; \
_vcpu = &HYPERVISOR_shared_info->vcpu_data[__this_cpu()]; \
_vcpu->evtchn_upcall_mask = 1; \
preempt_enable_no_resched(); \
barrier(); \
@@ -512,7 +523,7 @@ do { \
vcpu_info_t *_vcpu; \
barrier(); \
preempt_disable(); \
_vcpu = &HYPERVISOR_shared_info->vcpu_data[smp_processor_id()]; \
_vcpu = &HYPERVISOR_shared_info->vcpu_data[__this_cpu()]; \
_vcpu->evtchn_upcall_mask = 0; \
barrier(); /* unmask then check (avoid races) */ \
if ( unlikely(_vcpu->evtchn_upcall_pending) ) \
@@ -523,7 +534,7 @@ do { \
#define __save_flags(x) \
do { \
vcpu_info_t *_vcpu; \
_vcpu = &HYPERVISOR_shared_info->vcpu_data[smp_processor_id()]; \
_vcpu = &HYPERVISOR_shared_info->vcpu_data[__this_cpu()]; \
(x) = _vcpu->evtchn_upcall_mask; \
} while (0)
@@ -532,7 +543,7 @@ do { \
vcpu_info_t *_vcpu; \
barrier(); \
preempt_disable(); \
_vcpu = &HYPERVISOR_shared_info->vcpu_data[smp_processor_id()]; \
_vcpu = &HYPERVISOR_shared_info->vcpu_data[__this_cpu()]; \
if ((_vcpu->evtchn_upcall_mask = (x)) == 0) { \
barrier(); /* unmask then check (avoid races) */ \
if ( unlikely(_vcpu->evtchn_upcall_pending) ) \
@@ -548,7 +559,7 @@ do { \
do { \
vcpu_info_t *_vcpu; \
preempt_disable(); \
_vcpu = &HYPERVISOR_shared_info->vcpu_data[smp_processor_id()]; \
_vcpu = &HYPERVISOR_shared_info->vcpu_data[__this_cpu()]; \
(x) = _vcpu->evtchn_upcall_mask; \
_vcpu->evtchn_upcall_mask = 1; \
preempt_enable_no_resched(); \
@@ -561,14 +572,8 @@ do { \
#define local_irq_disable() __cli()
#define local_irq_enable() __sti()
/* Don't use smp_processor_id: this is called in debug versions of that fn. */
#ifdef CONFIG_SMP
#define irqs_disabled() \
HYPERVISOR_shared_info->vcpu_data[__smp_processor_id()].evtchn_upcall_mask
#else
#define irqs_disabled() \
HYPERVISOR_shared_info->vcpu_data[0].evtchn_upcall_mask
#endif
#define irqs_disabled() \
HYPERVISOR_shared_info->vcpu_data[__this_cpu()].evtchn_upcall_mask
/*
* disable hlt during certain critical i/o operations
@@ -35,7 +35,7 @@ static inline void __prepare_arch_switch(void)
* of cr3/ldt (i.e., not in __switch_to).
*/
__asm__ __volatile__ (
"movl %%es,%0 ; movl %%ds,%1 ; movl %%fs,%2 ; movl %%gs,%3"
"mov %%es,%0 ; mov %%ds,%1 ; mov %%fs,%2 ; mov %%gs,%3"
: "=m" (current->thread.es),
"=m" (current->thread.ds),
"=m" (current->thread.fsindex),
@@ -239,7 +239,6 @@ extern __inline__ int get_order(unsigned long size)
/* VIRT <-> MACHINE conversion */
#define virt_to_machine(v) (phys_to_machine(__pa(v)))
#define machine_to_virt(m) (__va(machine_to_phys(m)))
#define virt_to_mfn(v) (pfn_to_mfn(__pa(v) >> PAGE_SHIFT))
#define mfn_to_virt(m) (__va(mfn_to_pfn(m) << PAGE_SHIFT))
@@ -526,7 +526,7 @@ extern int kern_addr_valid(unsigned long addr);
#define DOMID_LOCAL (0xFFFFU)
int direct_remap_pfn_range(struct mm_struct *mm,
int direct_remap_pfn_range(struct vm_area_struct *vma,
unsigned long address,
unsigned long mfn,
unsigned long size,
@@ -542,10 +542,10 @@ int touch_pte_range(struct mm_struct *mm,
unsigned long size);
#define io_remap_page_range(vma, vaddr, paddr, size, prot) \
direct_remap_pfn_range((vma)->vm_mm,vaddr,paddr>>PAGE_SHIFT,size,prot,DOMID_IO)
direct_remap_pfn_range(vma,vaddr,(paddr)>>PAGE_SHIFT,size,prot,DOMID_IO)
#define io_remap_pfn_range(vma, vaddr, pfn, size, prot) \
direct_remap_pfn_range((vma)->vm_mm,vaddr,pfn,size,prot,DOMID_IO)
direct_remap_pfn_range(vma,vaddr,pfn,size,prot,DOMID_IO)
#define MK_IOSPACE_PFN(space, pfn) (pfn)
#define GET_IOSPACE(pfn) 0
@@ -321,11 +321,22 @@ static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
* includes these barriers, for example.
*/
/*
* Don't use smp_processor_id() in preemptible code: debug builds will barf.
* It's okay in these cases as we only read the upcall mask in preemptible
* regions, which is always safe.
*/
#ifdef CONFIG_SMP
#define __this_cpu() __smp_processor_id()
#else
#define __this_cpu() 0
#endif
#define __cli() \
do { \
vcpu_info_t *_vcpu; \
preempt_disable(); \
_vcpu = &HYPERVISOR_shared_info->vcpu_data[smp_processor_id()]; \
_vcpu = &HYPERVISOR_shared_info->vcpu_data[__this_cpu()]; \
_vcpu->evtchn_upcall_mask = 1; \
preempt_enable_no_resched(); \
barrier(); \
@@ -336,7 +347,7 @@ do { \
vcpu_info_t *_vcpu; \
barrier(); \
preempt_disable(); \
_vcpu = &HYPERVISOR_shared_info->vcpu_data[smp_processor_id()]; \
_vcpu = &HYPERVISOR_shared_info->vcpu_data[__this_cpu()]; \
_vcpu->evtchn_upcall_mask = 0; \
barrier(); /* unmask then check (avoid races) */ \
if ( unlikely(_vcpu->evtchn_upcall_pending) ) \
@@ -347,7 +358,7 @@ do { \
#define __save_flags(x) \
do { \
vcpu_info_t *_vcpu; \
_vcpu = &HYPERVISOR_shared_info->vcpu_data[smp_processor_id()]; \
_vcpu = &HYPERVISOR_shared_info->vcpu_data[__this_cpu()]; \
(x) = _vcpu->evtchn_upcall_mask; \
} while (0)
@@ -356,7 +367,7 @@ do { \
vcpu_info_t *_vcpu; \
barrier(); \
preempt_disable(); \
_vcpu = &HYPERVISOR_shared_info->vcpu_data[smp_processor_id()]; \
_vcpu = &HYPERVISOR_shared_info->vcpu_data[__this_cpu()]; \
if ((_vcpu->evtchn_upcall_mask = (x)) == 0) { \
barrier(); /* unmask then check (avoid races) */ \
if ( unlikely(_vcpu->evtchn_upcall_pending) ) \
@@ -372,7 +383,7 @@ do { \
do { \
vcpu_info_t *_vcpu; \
preempt_disable(); \
_vcpu = &HYPERVISOR_shared_info->vcpu_data[smp_processor_id()]; \
_vcpu = &HYPERVISOR_shared_info->vcpu_data[__this_cpu()]; \
(x) = _vcpu->evtchn_upcall_mask; \
_vcpu->evtchn_upcall_mask = 1; \
preempt_enable_no_resched(); \
@@ -387,14 +398,8 @@ void cpu_idle_wait(void);
#define local_irq_disable() __cli()
#define local_irq_enable() __sti()
/* Don't use smp_processor_id: this is called in debug versions of that fn. */
#ifdef CONFIG_SMP
#define irqs_disabled() \
HYPERVISOR_shared_info->vcpu_data[__smp_processor_id()].evtchn_upcall_mask
#else
#define irqs_disabled() \
HYPERVISOR_shared_info->vcpu_data[0].evtchn_upcall_mask
#endif
#define irqs_disabled() \
HYPERVISOR_shared_info->vcpu_data[__this_cpu()].evtchn_upcall_mask
/*
* disable hlt during certain critical i/o operations
@@ -0,0 +1,16 @@
#ifndef __ASM_XEN_DRIVER_UTIL_H__
#define __ASM_XEN_DRIVER_UTIL_H__
#include <linux/config.h>
#include <linux/vmalloc.h>
/* Allocate/destroy a 'vmalloc' VM area. */
extern struct vm_struct *alloc_vm_area(unsigned long size);
extern void free_vm_area(struct vm_struct *area);
/* Lock an area so that PTEs are accessible in the current address space. */
extern void lock_vm_area(struct vm_struct *area);
extern void unlock_vm_area(struct vm_struct *area);
#endif /* __ASM_XEN_DRIVER_UTIL_H__ */
@@ -37,7 +37,7 @@ int gnttab_grant_foreign_access(domid_t domid, unsigned long frame,
void gnttab_end_foreign_access_ref(grant_ref_t ref, int readonly);
void gnttab_end_foreign_access(grant_ref_t ref, int readonly);
int gnttab_grant_foreign_transfer(domid_t domid, unsigned long pfn);
int gnttab_grant_foreign_transfer(domid_t domid);
unsigned long gnttab_end_foreign_transfer_ref(grant_ref_t ref);
unsigned long gnttab_end_foreign_transfer(grant_ref_t ref);
@@ -64,8 +64,7 @@ void gnttab_request_free_callback(struct gnttab_free_callback *callback,
void gnttab_grant_foreign_access_ref(grant_ref_t ref, domid_t domid,
unsigned long frame, int readonly);
void gnttab_grant_foreign_transfer_ref(grant_ref_t, domid_t domid,
unsigned long pfn);
void gnttab_grant_foreign_transfer_ref(grant_ref_t, domid_t domid);
#ifdef __ia64__
#define gnttab_map_vaddr(map) __va(map.dev_bus_addr)
@@ -83,8 +83,7 @@ extern struct semaphore xenbus_lock;
char **xenbus_directory(const char *dir, const char *node, unsigned int *num);
void *xenbus_read(const char *dir, const char *node, unsigned int *len);
int xenbus_write(const char *dir, const char *node,
const char *string, int createflags);
int xenbus_write(const char *dir, const char *node, const char *string);
int xenbus_mkdir(const char *dir, const char *node);
int xenbus_exists(const char *dir, const char *node);
int xenbus_rm(const char *dir, const char *node);
@@ -0,0 +1,74 @@
--- ref-linux-2.6.12/drivers/char/tpm/tpm.c 2005-06-17 15:48:29.000000000 -0400
+++ linux-2.6-xen-sparse/drivers/char/tpm/tpm.c 2005-09-15 14:56:05.000000000 -0400
@@ -473,6 +401,7 @@ ssize_t tpm_write(struct file * file, co
out_size = tpm_transmit(chip, chip->data_buffer, TPM_BUFSIZE);
atomic_set(&chip->data_pending, out_size);
+ atomic_set(&chip->data_position, 0);
up(&chip->buffer_mutex);
/* Set a timeout by which the reader must come claim the result */
@@ -494,29 +423,34 @@ ssize_t tpm_read(struct file * file, cha
{
struct tpm_chip *chip = file->private_data;
int ret_size = -ENODATA;
+ int pos, pending = 0;
- if (atomic_read(&chip->data_pending) != 0) { /* Result available */
+ down(&chip->buffer_mutex);
+ ret_size = atomic_read(&chip->data_pending);
+ if ( ret_size > 0 ) { /* Result available */
+ if (size < ret_size)
+ ret_size = size;
+
+ pos = atomic_read(&chip->data_position);
+
+ if (copy_to_user((void __user *) buf,
+ &chip->data_buffer[pos], ret_size)) {
+ ret_size = -EFAULT;
+ } else {
+ pending = atomic_read(&chip->data_pending) - ret_size;
+ if ( pending ) {
+ atomic_set( &chip->data_pending, pending );
+ atomic_set( &chip->data_position, pos+ret_size );
+ }
+ }
+ }
+ up(&chip->buffer_mutex);
+
+ if ( ret_size <= 0 || pending == 0 ) {
+ atomic_set( &chip->data_pending, 0 );
down(&chip->timer_manipulation_mutex);
del_singleshot_timer_sync(&chip->user_read_timer);
up(&chip->timer_manipulation_mutex);
-
- down(&chip->buffer_mutex);
-
- ret_size = atomic_read(&chip->data_pending);
- atomic_set(&chip->data_pending, 0);
-
- if (ret_size == 0) /* timeout just occurred */
- ret_size = -ETIME;
- else if (ret_size > 0) { /* relay data */
- if (size < ret_size)
- ret_size = size;
-
- if (copy_to_user((void __user *) buf,
- chip->data_buffer, ret_size)) {
- ret_size = -EFAULT;
- }
- }
- up(&chip->buffer_mutex);
}
return ret_size;
--- ref-linux-2.6.12/drivers/char/tpm/tpm.h 2005-06-17 15:48:29.000000000 -0400
+++ linux-2.6-xen-sparse/drivers/char/tpm/tpm.h 2005-09-15 14:56:05.000000000 -0400
@@ -54,6 +54,7 @@ struct tpm_chip {
/* Data passed to and from the tpm via the read/write calls */
u8 *data_buffer;
atomic_t data_pending;
+ atomic_t data_position;
struct semaphore buffer_mutex;
struct timer_list user_read_timer; /* user needs to claim result */
+1 -1
View File
@@ -92,7 +92,7 @@ int xs_printf(struct xs_handle *h, const char *dir, const char *node,
if ((path == NULL) || (buf == NULL))
return 0;
ret = xs_write(h, path, buf, strlen(buf)+1, O_CREAT);
ret = xs_write(h, path, buf, strlen(buf)+1);
free(buf);
free(path);
+1 -1
View File
@@ -165,7 +165,7 @@ static int domain_create_tty(struct domain *dom)
success = asprintf(&path, "%s/tty", dom->conspath) != -1;
if (!success)
goto out;
success = xs_write(xs, path, slave, strlen(slave), O_CREAT);
success = xs_write(xs, path, slave, strlen(slave));
free(path);
if (!success)
goto out;
+29
View File
@@ -0,0 +1,29 @@
DomU GDB server for 32-bit (PAE and non-PAE) systems
----------------------------------------------------
Lines marked below with [*] are optional, if you want full
source-level debugging of your kernel image.
To build the GDB server:
1. Run ./gdbbuild from within this directory.
2. Copy ./gdb-6.2.1-linux-i386-xen/gdb/gdbserver/gdbserver-xen
to your test machine.
To build a debuggable guest kernel image:
1. cd linux-2.6.12-xenU
2. ARCH=xen make menuconfig
3. From within the configurator, enable the following options:
# Kernel hacking -> Compile the kernel with debug info [*]
-> Compile the kernel with frame pointers
4. (Re)build and (re)install your xenU kernel image.
To debug a running guest:
1. Use 'xm list' to discover its domain id ($domid).
2. Run 'gdbserver-xen 127.0.0.1:9999 --attach $domid'
3. Run 'gdb /path/to/vmlinux-syms-2.6.xx-xenU'
4. From within the gdb client session:
# directory /path/to/linux-2.6.xx-xenU [*]
# target remote 127.0.0.1:9999
# bt
# disass
@@ -3475,7 +3475,7 @@ USE_THREAD_DB=
GDBSERVER_DEPFILES="$srv_regobj $srv_tgtobj $srv_thread_depfiles"
GDBSERVER_LIBS="$srv_libs -L../../../../../libxc/ -lxc"
GDBSERVER_LIBS="$srv_libs -L../../../../../libxc/ -lxenctrl"
@@ -107,7 +107,7 @@ USE_THREAD_DB=
GDBSERVER_DEPFILES="$srv_regobj $srv_tgtobj $srv_thread_depfiles"
GDBSERVER_LIBS="$srv_libs -L../../../../../libxc/ -lxc"
GDBSERVER_LIBS="$srv_libs -L../../../../../libxc/ -lxenctrl"
AC_SUBST(GDBSERVER_DEPFILES)
AC_SUBST(GDBSERVER_LIBS)
@@ -37,9 +37,10 @@
#include <errno.h>
#include <xenctrl.h>
#define TRACE_ENTER /* printf("enter %s\n", __FUNCTION__) */
long (*myptrace)(enum __ptrace_request, pid_t, long, long);
int (*myxcwait)(int domain, int *status, int options) ;
long (*myptrace)(int xc_handle, enum __ptrace_request, u32, long, long);
int (*myxcwait)(int xc_handle, int domain, int *status, int options) ;
static int xc_handle;
#define DOMFLAGS_DYING (1<<0) /* Domain is scheduled to die. */
#define DOMFLAGS_SHUTDOWN (1<<2) /* The guest OS has shut down. */
@@ -47,11 +48,7 @@ int (*myxcwait)(int domain, int *status, int options) ;
#define DOMFLAGS_BLOCKED (1<<4) /* Currently blocked pending an event. */
#define DOMFLAGS_RUNNING (1<<5) /* Domain is currently running. */
struct inferior_list all_processes;
static int current_domain;
static int expect_signal = 0;
static int signal_to_send = 0;
@@ -150,7 +147,7 @@ linux_attach (int domain)
{
struct process_info *new_process;
current_domain = domain;
if (myptrace (PTRACE_ATTACH, domain, 0, 0) != 0) {
if (myptrace (xc_handle, PTRACE_ATTACH, domain, 0, 0) != 0) {
fprintf (stderr, "Cannot attach to domain %d: %s (%d)\n", domain,
strerror (errno), errno);
fflush (stderr);
@@ -173,8 +170,7 @@ linux_kill_one_process (struct inferior_list_entry *entry)
{
struct thread_info *thread = (struct thread_info *) entry;
struct process_info *process = get_thread_process (thread);
myptrace (PTRACE_KILL, pid_of (process), 0, 0);
myptrace (xc_handle, PTRACE_KILL, pid_of (process), 0, 0);
}
static void
@@ -190,7 +186,7 @@ linux_detach_one_process (struct inferior_list_entry *entry)
struct thread_info *thread = (struct thread_info *) entry;
struct process_info *process = get_thread_process (thread);
myptrace (PTRACE_DETACH, pid_of (process), 0, 0);
myptrace (xc_handle, PTRACE_DETACH, pid_of (process), 0, 0);
}
@@ -216,7 +212,7 @@ static unsigned char
linux_wait (char *status)
{
int w;
if (myxcwait(current_domain, &w, 0))
if (myxcwait(xc_handle, current_domain, &w, 0))
return -1;
if (w & (DOMFLAGS_SHUTDOWN|DOMFLAGS_DYING)) {
@@ -241,7 +237,7 @@ linux_resume (struct thread_resume *resume_info)
expect_signal = resume_info->sig;
for_each_inferior(&all_threads, regcache_invalidate_one);
myptrace (step ? PTRACE_SINGLESTEP : PTRACE_CONT, current_domain, 0, 0);
myptrace (xc_handle, step ? PTRACE_SINGLESTEP : PTRACE_CONT, current_domain, 0, 0);
}
@@ -265,7 +261,7 @@ regsets_fetch_inferior_registers ()
}
buf = malloc (regset->size);
res = myptrace (regset->get_request, inferior_pid, 0, (PTRACE_XFER_TYPE)buf);
res = myptrace (xc_handle, regset->get_request, inferior_pid, 0, (PTRACE_XFER_TYPE)buf);
if (res < 0)
{
if (errno == EIO)
@@ -317,7 +313,7 @@ regsets_store_inferior_registers ()
buf = malloc (regset->size);
regset->fill_function (buf);
res = myptrace (regset->set_request, inferior_pid, 0, (PTRACE_XFER_TYPE)buf);
res = myptrace (xc_handle, regset->set_request, inferior_pid, 0, (PTRACE_XFER_TYPE)buf);
if (res < 0)
{
if (errno == EIO)
@@ -395,7 +391,7 @@ linux_read_memory (CORE_ADDR memaddr, char *myaddr, int len)
for (i = 0; i < count; i++, addr += sizeof (PTRACE_XFER_TYPE))
{
errno = 0;
buffer[i] = myptrace (PTRACE_PEEKTEXT, inferior_pid, (PTRACE_ARG3_TYPE) addr, 0);
buffer[i] = myptrace (xc_handle, PTRACE_PEEKTEXT, inferior_pid, (PTRACE_ARG3_TYPE) addr, 0);
if (errno)
return errno;
}
@@ -428,13 +424,13 @@ linux_write_memory (CORE_ADDR memaddr, const char *myaddr, int len)
/* Fill start and end extra bytes of buffer with existing memory data. */
buffer[0] = myptrace (PTRACE_PEEKTEXT, inferior_pid,
buffer[0] = myptrace (xc_handle, PTRACE_PEEKTEXT, inferior_pid,
(PTRACE_ARG3_TYPE) addr, 0);
if (count > 1)
{
buffer[count - 1]
= myptrace (PTRACE_PEEKTEXT, inferior_pid,
= myptrace (xc_handle, PTRACE_PEEKTEXT, inferior_pid,
(PTRACE_ARG3_TYPE) (addr + (count - 1)
* sizeof (PTRACE_XFER_TYPE)),
0);
@@ -448,7 +444,7 @@ linux_write_memory (CORE_ADDR memaddr, const char *myaddr, int len)
for (i = 0; i < count; i++, addr += sizeof (PTRACE_XFER_TYPE))
{
errno = 0;
myptrace (PTRACE_POKETEXT, inferior_pid, (PTRACE_ARG3_TYPE) addr, buffer[i]);
myptrace (xc_handle, PTRACE_POKETEXT, inferior_pid, (PTRACE_ARG3_TYPE) addr, buffer[i]);
if (errno)
return errno;
}
@@ -539,7 +535,7 @@ linux_init_signals ()
void
initialize_low (void)
{
xc_handle = xc_interface_open();
set_target_ops (&linux_xen_target_ops);
set_breakpoint_data (the_low_target.breakpoint,
the_low_target.breakpoint_len);
+7 -10
View File
@@ -1,20 +1,17 @@
#!/bin/sh
XENROOT=`hg root`
export XENROOT
cd $XENROOT/tools/debugger/gdb
rm -rf gdb-6.2.1 gdb-6.2.1-linux-i386-xen
# FIXME:cw this should be smarter
wget -c ftp://ftp.gnu.org/gnu/gdb/gdb-6.2.1.tar.bz2
rm -rf gdb-6.2.1 gdb-6.2.1-linux-i386-xen
[ -a gdb-6.2.1.tar.bz2 ] || wget -c ftp://ftp.gnu.org/gnu/gdb/gdb-6.2.1.tar.bz2
tar xjf gdb-6.2.1.tar.bz2
cd $XENROOT/tools/debugger/gdb/gdb-6.2.1-xen-sparse
cd gdb-6.2.1-xen-sparse
./mkbuildtree ../gdb-6.2.1
mkdir $XENROOT/tools/debugger/gdb/gdb-6.2.1-linux-i386-xen
cd $XENROOT/tools/debugger/gdb/gdb-6.2.1-linux-i386-xen
cd ..
mkdir gdb-6.2.1-linux-i386-xen
cd gdb-6.2.1-linux-i386-xen
../gdb-6.2.1/configure
# some people don't have gmake
if which gmake ; then
gmake -j4
Loaded 100 of 235 files, more files were not shown because too many files have changed in this diff. Show more
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.