mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 18:50:05 +01:00
Merge latest xen-unstable into xen-ia64-unstable
This commit is contained in:
commit
46d993a5ca
235 files changed
+13465
-18367
No files matched your search
@@ -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$
|
||||
|
||||
@@ -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
@@ -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
File diff suppressed because it is too large.
Load diff
@@ -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.
|
||||
@@ -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}.
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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
File diff suppressed because it is too large.
Load diff
@@ -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}.
|
||||
@@ -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.
|
||||
@@ -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).
|
||||
@@ -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}
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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}
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
*/
|
||||
@@ -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:
|
||||
*/
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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