diff --git a/.hgignore b/.hgignore index fbb928b09b..b0f13ec626 100644 --- a/.hgignore +++ b/.hgignore @@ -158,6 +158,8 @@ ^tools/xcutils/xc_restore$ ^tools/xcutils/xc_save$ ^tools/xcutils/readnotes$ +^tools/xenfb/sdlfb$ +^tools/xenfb/vncfb$ ^tools/xenmon/xentrace_setmask$ ^tools/xenmon/xenbaked$ ^tools/xenstat/xentop/xentop$ diff --git a/Config.mk b/Config.mk index 4cd113d905..08873d69d4 100644 --- a/Config.mk +++ b/Config.mk @@ -69,7 +69,8 @@ ACM_DEFAULT_SECURITY_POLICY ?= ACM_NULL_POLICY # Optional components XENSTAT_XENTOP ?= y - VTPM_TOOLS ?= n +LIBXENAPI_BINDINGS ?= n +XENFB_TOOLS ?= n -include $(XEN_ROOT)/.config diff --git a/buildconfigs/linux-defconfig_xen0_x86_32 b/buildconfigs/linux-defconfig_xen0_x86_32 index 8cd6a5e503..78b2d95cf8 100644 --- a/buildconfigs/linux-defconfig_xen0_x86_32 +++ b/buildconfigs/linux-defconfig_xen0_x86_32 @@ -179,6 +179,7 @@ CONFIG_HZ_100=y # CONFIG_HZ_250 is not set # CONFIG_HZ_1000 is not set CONFIG_HZ=100 +CONFIG_KEXEC=y # CONFIG_CRASH_DUMP is not set CONFIG_PHYSICAL_START=0x100000 diff --git a/buildconfigs/linux-defconfig_xen0_x86_64 b/buildconfigs/linux-defconfig_xen0_x86_64 index 05c6fed107..603e0a82d9 100644 --- a/buildconfigs/linux-defconfig_xen0_x86_64 +++ b/buildconfigs/linux-defconfig_xen0_x86_64 @@ -126,6 +126,7 @@ CONFIG_FLAT_NODE_MEM_MAP=y # CONFIG_SPARSEMEM_STATIC is not set CONFIG_SPLIT_PTLOCK_CPUS=4096 CONFIG_SWIOTLB=y +CONFIG_KEXEC=y # CONFIG_CRASH_DUMP is not set CONFIG_PHYSICAL_START=0x100000 CONFIG_SECCOMP=y diff --git a/buildconfigs/linux-defconfig_xen_ia64 b/buildconfigs/linux-defconfig_xen_ia64 index 1bdc8edb4c..e04b1db69f 100644 --- a/buildconfigs/linux-defconfig_xen_ia64 +++ b/buildconfigs/linux-defconfig_xen_ia64 @@ -1548,6 +1548,8 @@ CONFIG_XEN_PCIDEV_BACKEND_SLOT=y # CONFIG_XEN_TPMDEV_BACKEND is not set CONFIG_XEN_BLKDEV_FRONTEND=y CONFIG_XEN_NETDEV_FRONTEND=y +CONFIG_XEN_FRAMEBUFFER=y +CONFIG_XEN_KEYBOARD=y # CONFIG_XEN_SCRUB_PAGES is not set CONFIG_XEN_DISABLE_SERIAL=y CONFIG_XEN_SYSFS=y diff --git a/buildconfigs/linux-defconfig_xen_x86_32 b/buildconfigs/linux-defconfig_xen_x86_32 index 0dee9d8734..993da59152 100644 --- a/buildconfigs/linux-defconfig_xen_x86_32 +++ b/buildconfigs/linux-defconfig_xen_x86_32 @@ -184,6 +184,7 @@ CONFIG_MTRR=y CONFIG_REGPARM=y CONFIG_SECCOMP=y CONFIG_HZ_100=y +CONFIG_KEXEC=y # CONFIG_HZ_250 is not set # CONFIG_HZ_1000 is not set CONFIG_HZ=100 @@ -2776,6 +2777,7 @@ CONFIG_NTFS_FS=m # CONFIG_PROC_FS=y CONFIG_PROC_KCORE=y +# CONFIG_PROC_VMCORE is not set CONFIG_SYSFS=y CONFIG_TMPFS=y # CONFIG_HUGETLB_PAGE is not set @@ -3032,6 +3034,8 @@ CONFIG_XEN_NETDEV_LOOPBACK=y # CONFIG_XEN_TPMDEV_BACKEND is not set CONFIG_XEN_BLKDEV_FRONTEND=y CONFIG_XEN_NETDEV_FRONTEND=y +CONFIG_XEN_FRAMEBUFFER=y +CONFIG_XEN_KEYBOARD=y CONFIG_XEN_SCRUB_PAGES=y CONFIG_XEN_DISABLE_SERIAL=y CONFIG_XEN_SYSFS=y diff --git a/buildconfigs/linux-defconfig_xen_x86_64 b/buildconfigs/linux-defconfig_xen_x86_64 index 3f9599adb0..ebee7ab056 100644 --- a/buildconfigs/linux-defconfig_xen_x86_64 +++ b/buildconfigs/linux-defconfig_xen_x86_64 @@ -139,6 +139,7 @@ CONFIG_SWIOTLB=y CONFIG_PHYSICAL_START=0x100000 CONFIG_SECCOMP=y CONFIG_HZ_100=y +CONFIG_KEXEC=y # CONFIG_HZ_250 is not set # CONFIG_HZ_1000 is not set CONFIG_HZ=100 @@ -2865,6 +2866,8 @@ CONFIG_XEN_TPMDEV_BACKEND=m # CONFIG_XEN_TPMDEV_CLOSE_IF_VTPM_FAILS is not set CONFIG_XEN_BLKDEV_FRONTEND=y CONFIG_XEN_NETDEV_FRONTEND=y +CONFIG_XEN_FRAMEBUFFER=y +CONFIG_XEN_KEYBOARD=y CONFIG_XEN_SCRUB_PAGES=y CONFIG_XEN_DISABLE_SERIAL=y CONFIG_XEN_SYSFS=y diff --git a/buildconfigs/mk.linux-2.6-xen b/buildconfigs/mk.linux-2.6-xen index 72bd77ed89..9a1e47a332 100644 --- a/buildconfigs/mk.linux-2.6-xen +++ b/buildconfigs/mk.linux-2.6-xen @@ -1,5 +1,5 @@ LINUX_SERIES = 2.6 -LINUX_VER = 2.6.16.32 +LINUX_VER = 2.6.16.33 EXTRAVERSION ?= xen diff --git a/docs/src/user.tex b/docs/src/user.tex index 2528861a23..f71231d44c 100644 --- a/docs/src/user.tex +++ b/docs/src/user.tex @@ -3240,6 +3240,11 @@ editing \path{grub.conf}. enabled by the BIOS. \item [ apic=bigsmp,default,es7000,summit ] Specify NUMA platform. This can usually be probed automatically. +\item [ dma\_bits=xxx ] Specify width of DMA + addresses in bits. Default is 30 bits (addresses up to 1GB are DMAable). +\item [ dma\_emergency\_pool=xxx ] Specify lower bound on size of DMA + pool below which ordinary allocations will fail rather than fall + back to allocating from the DMA pool. \end{description} In addition, the following options may be specified on the Xen command diff --git a/docs/xen-api/presentation.tex b/docs/xen-api/presentation.tex index 0072704646..ba7ebdbde7 100644 --- a/docs/xen-api/presentation.tex +++ b/docs/xen-api/presentation.tex @@ -131,9 +131,6 @@ of that class that has the specified {\tt uuid}. ``{\tt get\_by\_name\_label(name)}'' RPC that returns a set of objects of that class that have the specified {\tt label}. -\item Each class has a ``{\tt to\_XML()}'' RPC that serialises the -state of all fields as an XML string. - \item Each class has a ``{\tt destroy(Ref x)}'' RPC that explicitly deletes the persistent object specified by {\tt x} from the system. This is a non-cascading delete -- if the object being removed is referenced by another @@ -144,6 +141,6 @@ object then the {\tt destroy} call will fail. \subsection{Additional RPCs} As well as the RPCs enumerated above, some classes have additional RPCs -associated with them. For example, the {\tt VM} class have RPCs for cloning, +associated with them. For example, the {\tt VM} class has RPCs for cloning, suspending, starting etc. Such additional RPCs are described explicitly in the API reference. diff --git a/docs/xen-api/todo.tex b/docs/xen-api/todo.tex index 2778ccce55..6f679ca01e 100644 --- a/docs/xen-api/todo.tex +++ b/docs/xen-api/todo.tex @@ -35,6 +35,8 @@ code, potential error description, but otherwise no value. \item Clarify behaviour of progress field on asyncrhonous request polling when that request fails. +\item Clarify which calls have asynchronous counterparts by marking them as such in the reference. (Individual getters and setters are too small and quick to justify having async versions) + \end{itemize} \subsection{Content} diff --git a/docs/xen-api/wire-protocol.tex b/docs/xen-api/wire-protocol.tex index ecd4ed9416..5b15573809 100644 --- a/docs/xen-api/wire-protocol.tex +++ b/docs/xen-api/wire-protocol.tex @@ -21,9 +21,9 @@ primitive XML-RPC types. In our API Reference we specify the signatures of API functions in the following style: \begin{verbatim} - (ref_vm Set) Host.ListAllVMs() + (ref_vm Set) VM.get_all() \end{verbatim} -This specifies that the function with name {\tt Host.ListAllVMs} takes +This specifies that the function with name {\tt VM.get\_all} takes no parameters and returns a Set of {\tt ref\_vm}s. These types are mapped onto XML-RPC types in a straight-forward manner: \begin{itemize} @@ -105,8 +105,8 @@ In the case where {\tt Status} is set to {\tt Failure} then the struct contains a second element named {\tt ErrorDescription}: \begin{itemize} \item The element of the struct named {\tt ErrorDescription} contains -an array of string values. The first element of the array represents an error code; -the remainder of the array represents error parameters relating to that code. +an array of string values. The first element of the array is an XML-RPC 32-bit {\tt i4} and represents an error code; +the remainder of the array are strings representing error parameters relating to that code. \end{itemize} For example, an XML-RPC return value from the {\tt Host.ListAllVMs} function above @@ -161,19 +161,19 @@ A session can be terminated with the {\tt Session.Logout} function: \subsection{Synchronous and Asynchronous invocation} -Each method call (apart from those on ``Session'' and ``Task'' objects) +Each method call (apart from methods on ``Session'' and ``Task'' objects +and ``getters'' and ``setters'' derived from fields) can be made either synchronously or asynchronously. A synchronous RPC call blocks until the return value is received; the return value of a synchronous RPC call is exactly as specified in Section~\ref{synchronous-result}. -Each of the methods specified in the API Reference is synchronous. -However, although not listed explicitly in this document, each -method call has an asynchronous analogue in the {\tt Async} -namespace. For example, synchronous call {\tt VM.Install(...)} +Only synchronous API calls are listed explicitly in this document. +All asynchronous versions are in the special {\tt Async} namespace. +For example, synchronous call {\tt VM.clone(...)} (described in Chapter~\ref{api-reference}) has an asynchronous counterpart, {\tt -Async.VM.Install(...)}, that is non-blocking. +Async.VM.clone(...)}, that is non-blocking. Instead of returning its result directly, an asynchronous RPC call returns a {\tt task-id}; this identifier is subsequently used @@ -186,39 +186,14 @@ is wrapped in an XML-RPC struct with a {\tt Status}, {\tt ErrorDescription} and The {\tt task-id} is provided in the {\tt Value} field if {\tt Status} is set to {\tt Success}. -Two special RPC calls are provided to poll the status of -asynchronous calls: +The RPC call \begin{verbatim} - Array Async.Task.GetAllTasks (session_id s) - task_status Async.Task.GetStatus (session_id s, task_id t) -\end{verbatim} - -{\tt Async.Task.GetAllTasks} returns a set of the currently -executing asynchronous tasks belong to the current user\footnote{ -% -The current user is determined by the username that was provided -to {\tt Session.Login}. -% -}. - -{\tt Async.Task.GetStatus} returns a {\tt task\_status} result. -This is an XML-RPC struct with three elements: -\begin{itemize} - \item The first element is named {\tt Progress} and contains -an {\tt Integer} between 0 and 100 representing the estimated percentage of -the task currently completed. - \item The second element is named {\tt ETA} and contains a {\tt DateTime} -representing the estimated time the task will be complete. - \item The third element is named {\tt Result}. If {\tt Progress} -is not 100 then {\tt Result} contains the empty string. If {\tt Progress} -{\em is\/} set to 100, then {\tt Result} contains the function's return -result (as specified in Section~\ref{synchronous-result})\footnote{ -% -Recall that this itself is a struct potentially containing status, errorcode, -value fields etc. -% -}. -\end{itemize} + (ref_task Set) Task.get_all(session_id s) +\end{verbatim} +returns a set of all task IDs known to the system. The status (including any +returned result and error codes) of these tasks +can then be queried by accessing the fields of the Task object in the usual way. +Note that, in order to get a consistent snapshot of a task's state, it is advisable to call the ``get\_record'' function. \section{Example interactive session} @@ -267,7 +242,8 @@ Next, the user may acquire a list of all the VMs known to the host: (Note the ca \begin{verbatim} >>> all_vms = xen.VM.do_list(session)['Value'] >>> all_vms -['b7b92d9e-d442-4710-92a5-ab039fd7d89b', '23e1e837-abbf-4675-b077-d4007989b0cc', '2045dbc0-0734-4eea-9cb2-b8218c6b5bf2', '3202ae18-a046-4c32-9fda-e32e9631866e'] +['b7b92d9e-d442-4710-92a5-ab039fd7d89b', '23e1e837-abbf-4675-b077-d4007989b0cc', + '2045dbc0-0734-4eea-9cb2-b8218c6b5bf2', '3202ae18-a046-4c32-9fda-e32e9631866e'] \end{verbatim} Note the VM references are internally UUIDs. Once a reference to a VM has been acquired a lifecycle operation may be invoked: diff --git a/docs/xen-api/xenapi-datamodel-graph.dot b/docs/xen-api/xenapi-datamodel-graph.dot index 794d9ea17a..daeca1f6e5 100644 --- a/docs/xen-api/xenapi-datamodel-graph.dot +++ b/docs/xen-api/xenapi-datamodel-graph.dot @@ -1,5 +1,5 @@ digraph g{ -node [ shape=box ]; session [ URL="session.html" ] task [ URL="task.html" ] VM [ URL="VM.html" ] host [ URL="host.html" ] host_cpu [ URL="host_cpu.html" ] network [ URL="network.html" ] VIF [ URL="VIF.html" ] PIF [ URL="PIF.html" ] SR [ URL="SR.html" ] VDI [ URL="VDI.html" ] VBD [ URL="VBD.html" ] VTPM [ URL="VTPM.html" ] user [ URL="user.html" ] debug [ URL="debug.html" ]; +node [ shape=box ]; session [ URL="session.html" ] task [ URL="task.html" ] VM [ URL="VM.html" ] host [ URL="host.html" ] host_cpu [ URL="host_cpu.html" ] network [ URL="network.html" ] VIF [ URL="VIF.html" ] PIF [ URL="PIF.html" ] SR [ URL="SR.html" ] VDI [ URL="VDI.html" ] VBD [ URL="VBD.html" ] VTPM [ URL="VTPM.html" ] console [ URL="console.html" ] user [ URL="user.html" ] debug [ URL="debug.html" ]; session -> host [ label="this_host(1)" ] session -> user [ label="this_user(1)" ] host -> VM [ color="blue", arrowhead="crow", arrowtail="none" ] @@ -14,4 +14,5 @@ VDI -> VDI [ color="blue", arrowhead="none", arrowtail="crow" ] VBD -> VM [ color="blue", arrowhead="none", arrowtail="crow" ] VTPM -> VM [ label="backend(1)" ] VTPM -> VM [ color="blue", arrowhead="none", arrowtail="crow" ] +console -> VM [ color="blue", arrowhead="none", arrowtail="crow" ] } diff --git a/docs/xen-api/xenapi-datamodel.tex b/docs/xen-api/xenapi-datamodel.tex index 8c06fb8a33..2facf7fec6 100644 --- a/docs/xen-api/xenapi-datamodel.tex +++ b/docs/xen-api/xenapi-datamodel.tex @@ -23,7 +23,7 @@ The following classes are defined: Name & Description \\ \hline {\tt session} & A session \\ -{\tt task} & A longrunning asynchronous task \\ +{\tt task} & A long-running asynchronous task \\ {\tt VM} & A virtual machine (or 'guest') \\ {\tt host} & A physical host \\ {\tt host\_cpu} & A physical CPU \\ @@ -34,6 +34,7 @@ Name & Description \\ {\tt VDI} & A virtual disk image \\ {\tt VBD} & A virtual block device \\ {\tt VTPM} & A virtual TPM device \\ +{\tt console} & A console \\ {\tt user} & A user of the system \\ {\tt debug} & A basic class for testing \\ \hline @@ -54,6 +55,7 @@ PIF.host & host.PIFs & one-to-many\\ PIF.network & network.PIFs & one-to-many\\ SR.VDIs & VDI.SR & many-to-one\\ VTPM.VM & VM.VTPMs & one-to-many\\ +console.VM & VM.consoles & one-to-many\\ host.resident\_VMs & VM.resident\_on & many-to-one\\ host.host\_CPUs & host\_cpu.host & many-to-one\\ \hline @@ -97,6 +99,17 @@ Map (a $\rightarrow$ b) & a table mapping values of type a to values of type b \ \subsection{Enumeration types} The following enumeration types are used: +\begin{longtable}{|ll|} +\hline +{\tt enum console\_protocol} & \\ +\hline +\hspace{0.5cm}{\tt vt100} & VT100 terminal \\ +\hspace{0.5cm}{\tt rfb} & Remote FrameBuffer protocol (as used in VNC) \\ +\hspace{0.5cm}{\tt rdp} & Remote Desktop Protocol \\ +\hline +\end{longtable} + +\vspace{1cm} \begin{longtable}{|ll|} \hline {\tt enum vdi\_type} & \\ @@ -121,6 +134,17 @@ The following enumeration types are used: \hline \end{longtable} +\vspace{1cm} +\begin{longtable}{|ll|} +\hline +{\tt enum task\_status\_type} & \\ +\hline +\hspace{0.5cm}{\tt pending} & task is in progress \\ +\hspace{0.5cm}{\tt success} & task was completed successfully \\ +\hspace{0.5cm}{\tt failure} & task has failed \\ +\hline +\end{longtable} + \vspace{1cm} \begin{longtable}{|ll|} \hline @@ -239,6 +263,16 @@ The following enumeration types are used: \hline \end{longtable} +\vspace{1cm} +\begin{longtable}{|ll|} +\hline +{\tt enum vbd\_type} & \\ +\hline +\hspace{0.5cm}{\tt CD} & VBD will appear to guest as CD \\ +\hspace{0.5cm}{\tt Disk} & VBD will appear to guest as disk \\ +\hline +\end{longtable} + \vspace{1cm} \begin{longtable}{|ll|} \hline @@ -261,6 +295,7 @@ The following enumeration types are used: \hline Quals & Field & Type & Description \\ \hline +$\mathit{RO}_\mathit{run}$ & {\tt uuid} & string & unique identifier/object reference \\ $\mathit{RO}_\mathit{ins}$ & {\tt this\_host} & host ref & Currently connected host \\ $\mathit{RO}_\mathit{ins}$ & {\tt this\_user} & user ref & Currently connected user \\ \hline @@ -318,13 +353,45 @@ void +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_uuid} + +{\bf Overview:} +Get the uuid field of the given session. + + \noindent {\bf Signature:} +\begin{verbatim} string get_uuid (session_id s, session ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt session ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field \vspace{0.3cm} \vspace{0.3cm} \vspace{0.3cm} \subsubsection{RPC name:~get\_this\_host} {\bf Overview:} -get accessor message derived from field this\_host of object session +Get the this\_host field of the given session. \noindent {\bf Signature:} \begin{verbatim} (host ref) get_this_host (session_id s, session ref self)\end{verbatim} @@ -337,7 +404,7 @@ get accessor message derived from field this\_host of object session \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt session ref } & self & object instance \\ \hline +{\tt session ref } & self & reference to the object \\ \hline \end{tabular} @@ -356,7 +423,7 @@ value of the field \subsubsection{RPC name:~get\_this\_user} {\bf Overview:} -get accessor message derived from field this\_user of object session +Get the this\_user field of the given session. \noindent {\bf Signature:} \begin{verbatim} (user ref) get_this_user (session_id s, session ref self)\end{verbatim} @@ -369,7 +436,7 @@ get accessor message derived from field this\_user of object session \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt session ref } & self & object instance \\ \hline +{\tt session ref } & self & reference to the object \\ \hline \end{tabular} @@ -388,7 +455,7 @@ value of the field \subsubsection{RPC name:~create} {\bf Overview:} -constructor for class session +Create a new session instance, and return its handle. \noindent {\bf Signature:} \begin{verbatim} (session ref) create (session_id s, session record args)\end{verbatim} @@ -420,7 +487,7 @@ reference to the newly created object \subsubsection{RPC name:~destroy} {\bf Overview:} -destructor for class session +Destroy the specified session instance. \noindent {\bf Signature:} \begin{verbatim} void destroy (session_id s, session ref self)\end{verbatim} @@ -433,7 +500,7 @@ destructor for class session \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt session ref } & self & object instance \\ \hline +{\tt session ref } & self & reference to the object \\ \hline \end{tabular} @@ -452,7 +519,7 @@ void \subsubsection{RPC name:~get\_by\_uuid} {\bf Overview:} -returns the session instance with a particular uuid +Get a reference to the session instance with the specified UUID. \noindent {\bf Signature:} \begin{verbatim} (session ref) get_by_uuid (session_id s, string uuid)\end{verbatim} @@ -484,7 +551,7 @@ reference to the object \subsubsection{RPC name:~get\_record} {\bf Overview:} -returns a record containing the state of an instance of class session +Get a record containing the current state of the given session. \noindent {\bf Signature:} \begin{verbatim} (session record) get_record (session_id s, session ref self)\end{verbatim} @@ -521,55 +588,30 @@ all fields from the object \begin{longtable}{|lllp{0.38\textwidth}|} \hline \multicolumn{1}{|l}{Name} & \multicolumn{3}{l|}{\bf task} \\ -\multicolumn{1}{|l}{Description} & \multicolumn{3}{l|}{\parbox{11cm}{\em A longrunning asynchronous task}} \\ +\multicolumn{1}{|l}{Description} & \multicolumn{3}{l|}{\parbox{11cm}{\em A long-running asynchronous task}} \\ \hline Quals & Field & Type & Description \\ \hline $\mathit{RO}_\mathit{run}$ & {\tt uuid} & string & unique identifier/object reference \\ $\mathit{RW}$ & {\tt name/label} & string & a human-readable name \\ $\mathit{RW}$ & {\tt name/description} & string & a notes field containg human-readable description \\ +$\mathit{RO}_\mathit{run}$ & {\tt status} & task\_status\_type & current status of the task \\ +$\mathit{RO}_\mathit{run}$ & {\tt progress} & int & if the task is still pending, this field contains the estimated percentage complete (0-100). If task has completed (successfully or unsuccessfully) this should be 100. \\ +$\mathit{RO}_\mathit{run}$ & {\tt eta} & datetime & if the task is still pending, this field contains the estimated completion time. If the task has finished (successfully or not) it contains the time the task finished. \\ +$\mathit{RO}_\mathit{run}$ & {\tt type} & string & if the task has completed successfully, this field contains the type of the encoded result (i.e. name of the class whose reference is in the result field). Undefined otherwise. \\ +$\mathit{RO}_\mathit{run}$ & {\tt result} & string & if the task has completed successfully, this field contains the result value (either Void or an object reference). Undefined otherwise. \\ +$\mathit{RO}_\mathit{run}$ & {\tt error\_code} & int & if the task has failed, this field contains the error code. Undefined otherwise. \\ +$\mathit{RO}_\mathit{run}$ & {\tt error\_info} & string Set & if the task has failed, this field contains the set of associated error strings. Undefined otherwise. \\ \hline \end{longtable} \subsection{Additional RPCs associated with class: task} -\subsubsection{RPC name:~get\_status} +\subsubsection{RPC name:~get\_all} {\bf Overview:} -Poll a running asynchronous RPC invocation and query its status +Return a list of all the tasks known to the system \noindent {\bf Signature:} -\begin{verbatim} (uuid ref) get_status (session_id s, task ref task)\end{verbatim} - - -\noindent{\bf Arguments:} - - -\vspace{0.3cm} -\begin{tabular}{|c|c|p{7cm}|} - \hline -{\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt task ref } & task & The ID of the RPC call to poll \\ \hline - -\end{tabular} - -\vspace{0.3cm} - - \noindent {\bf Return Type:} -{\tt -uuid ref -} - - -String describing status of specified asynchronous RPC invocation, including estimated completion time -\vspace{0.3cm} -\vspace{0.3cm} -\vspace{0.3cm} -\subsubsection{RPC name:~get\_all\_tasks} - -{\bf Overview:} -List all asynchronous RPC calls currently executing - - \noindent {\bf Signature:} -\begin{verbatim} ((task ref) Set) get_all_tasks (session_id s)\end{verbatim} +\begin{verbatim} ((task ref) Set) get_all (session_id s)\end{verbatim} \vspace{0.3cm} @@ -580,17 +622,14 @@ List all asynchronous RPC calls currently executing } -A list of tasks currently executing. Note that -tasks are associated with users rather than sessions. Thus, if you logout and -login again with a different session but the same user, this function will still -return the user's running tasks. +references to all objects \vspace{0.3cm} \vspace{0.3cm} \vspace{0.3cm} \subsubsection{RPC name:~get\_uuid} {\bf Overview:} -get accessor message derived from field uuid of object task +Get the uuid field of the given task. \noindent {\bf Signature:} \begin{verbatim} string get_uuid (session_id s, task ref self)\end{verbatim} @@ -603,7 +642,7 @@ get accessor message derived from field uuid of object task \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt task ref } & self & object instance \\ \hline +{\tt task ref } & self & reference to the object \\ \hline \end{tabular} @@ -622,7 +661,7 @@ value of the field \subsubsection{RPC name:~get\_name\_label} {\bf Overview:} -get accessor message derived from field name/label of object task +Get the name/label field of the given task. \noindent {\bf Signature:} \begin{verbatim} string get_name_label (session_id s, task ref self)\end{verbatim} @@ -635,7 +674,7 @@ get accessor message derived from field name/label of object task \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt task ref } & self & object instance \\ \hline +{\tt task ref } & self & reference to the object \\ \hline \end{tabular} @@ -654,7 +693,7 @@ value of the field \subsubsection{RPC name:~set\_name\_label} {\bf Overview:} -set accessor message derived from field name/label of object task +Set the name/label field of the given task. \noindent {\bf Signature:} \begin{verbatim} void set_name_label (session_id s, task ref self, string value)\end{verbatim} @@ -667,7 +706,7 @@ set accessor message derived from field name/label of object task \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt task ref } & self & object instance \\ \hline +{\tt task ref } & self & reference to the object \\ \hline {\tt string } & value & New value to set \\ \hline @@ -688,7 +727,7 @@ void \subsubsection{RPC name:~get\_name\_description} {\bf Overview:} -get accessor message derived from field name/description of object task +Get the name/description field of the given task. \noindent {\bf Signature:} \begin{verbatim} string get_name_description (session_id s, task ref self)\end{verbatim} @@ -701,7 +740,7 @@ get accessor message derived from field name/description of object task \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt task ref } & self & object instance \\ \hline +{\tt task ref } & self & reference to the object \\ \hline \end{tabular} @@ -720,7 +759,7 @@ value of the field \subsubsection{RPC name:~set\_name\_description} {\bf Overview:} -set accessor message derived from field name/description of object task +Set the name/description field of the given task. \noindent {\bf Signature:} \begin{verbatim} void set_name_description (session_id s, task ref self, string value)\end{verbatim} @@ -733,7 +772,7 @@ set accessor message derived from field name/description of object task \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt task ref } & self & object instance \\ \hline +{\tt task ref } & self & reference to the object \\ \hline {\tt string } & value & New value to set \\ \hline @@ -748,13 +787,237 @@ void +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_status} + +{\bf Overview:} +Get the status field of the given task. + + \noindent {\bf Signature:} +\begin{verbatim} (task_status_type) get_status (session_id s, task ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt task ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +task\_status\_type +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_progress} + +{\bf Overview:} +Get the progress field of the given task. + + \noindent {\bf Signature:} +\begin{verbatim} int get_progress (session_id s, task ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt task ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_eta} + +{\bf Overview:} +Get the eta field of the given task. + + \noindent {\bf Signature:} +\begin{verbatim} datetime get_eta (session_id s, task ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt task ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +datetime +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_type} + +{\bf Overview:} +Get the type field of the given task. + + \noindent {\bf Signature:} +\begin{verbatim} string get_type (session_id s, task ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt task ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_result} + +{\bf Overview:} +Get the result field of the given task. + + \noindent {\bf Signature:} +\begin{verbatim} string get_result (session_id s, task ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt task ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_error\_code} + +{\bf Overview:} +Get the error\_code field of the given task. + + \noindent {\bf Signature:} +\begin{verbatim} int get_error_code (session_id s, task ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt task ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +int +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_error\_info} + +{\bf Overview:} +Get the error\_info field of the given task. + + \noindent {\bf Signature:} +\begin{verbatim} (string Set) get_error_info (session_id s, task ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt task ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string Set +} + + +value of the field \vspace{0.3cm} \vspace{0.3cm} \vspace{0.3cm} \subsubsection{RPC name:~create} {\bf Overview:} -constructor for class task +Create a new task instance, and return its handle. \noindent {\bf Signature:} \begin{verbatim} (task ref) create (session_id s, task record args)\end{verbatim} @@ -786,7 +1049,7 @@ reference to the newly created object \subsubsection{RPC name:~destroy} {\bf Overview:} -destructor for class task +Destroy the specified task instance. \noindent {\bf Signature:} \begin{verbatim} void destroy (session_id s, task ref self)\end{verbatim} @@ -799,7 +1062,7 @@ destructor for class task \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt task ref } & self & object instance \\ \hline +{\tt task ref } & self & reference to the object \\ \hline \end{tabular} @@ -818,7 +1081,7 @@ void \subsubsection{RPC name:~get\_by\_uuid} {\bf Overview:} -returns the task instance with a particular uuid +Get a reference to the task instance with the specified UUID. \noindent {\bf Signature:} \begin{verbatim} (task ref) get_by_uuid (session_id s, string uuid)\end{verbatim} @@ -850,7 +1113,7 @@ reference to the object \subsubsection{RPC name:~get\_record} {\bf Overview:} -returns a record containing the state of an instance of class task +Get a record containing the current state of the given task. \noindent {\bf Signature:} \begin{verbatim} (task record) get_record (session_id s, task ref self)\end{verbatim} @@ -882,7 +1145,7 @@ all fields from the object \subsubsection{RPC name:~get\_by\_name\_label} {\bf Overview:} -returns the task instance with a particular name label +Get all the task instances with the given label. \noindent {\bf Signature:} \begin{verbatim} ((task ref) Set) get_by_name_label (session_id s, string label)\end{verbatim} @@ -929,15 +1192,16 @@ $\mathit{RW}$ & {\tt name/label} & string & a human-readable name \\ $\mathit{RW}$ & {\tt name/description} & string & a notes field containg human-readable description \\ $\mathit{RW}$ & {\tt user\_version} & int & a user version number for this machine \\ $\mathit{RW}$ & {\tt is\_a\_template} & bool & true if this is a template. Template VMs can never be started, they are used only for cloning other VMs \\ +$\mathit{RW}$ & {\tt auto\_power\_on} & bool & true if this VM should be started automatically after host boot \\ $\mathit{RO}_\mathit{run}$ & {\tt resident\_on} & host ref & the host the VM is currently resident on \\ -$\mathit{RO}_\mathit{ins}$ & {\tt memory/static\_max} & int & Statically-set (i.e. absolute) maximum \\ -$\mathit{RW}$ & {\tt memory/dynamic\_max} & int & Dynamic maximum \\ -$\mathit{RO}_\mathit{run}$ & {\tt memory/actual} & int & Guest's actual usage \\ -$\mathit{RW}$ & {\tt memory/dynamic\_min} & int & Dynamic minimum \\ -$\mathit{RO}_\mathit{ins}$ & {\tt memory/static\_min} & int & Statically-set (i.e. absolute) mininum \\ +$\mathit{RO}_\mathit{ins}$ & {\tt memory/static\_max} & int & Statically-set (i.e. absolute) maximum (bytes) \\ +$\mathit{RW}$ & {\tt memory/dynamic\_max} & int & Dynamic maximum (bytes) \\ +$\mathit{RO}_\mathit{run}$ & {\tt memory/actual} & int & Guest's actual usage (bytes) \\ +$\mathit{RW}$ & {\tt memory/dynamic\_min} & int & Dynamic minimum (bytes) \\ +$\mathit{RO}_\mathit{ins}$ & {\tt memory/static\_min} & int & Statically-set (i.e. absolute) mininum (bytes) \\ $\mathit{RW}$ & {\tt VCPUs/policy} & string & the name of the VCPU scheduling policy to be applied \\ $\mathit{RW}$ & {\tt VCPUs/params} & string & string-encoded parameters passed to selected VCPU policy \\ -$\mathit{RO}_\mathit{run}$ & {\tt VCPUs/number} & int & Current number of VCPUs \\ +$\mathit{RW}$ & {\tt VCPUs/number} & int & Current number of VCPUs \\ $\mathit{RO}_\mathit{run}$ & {\tt VCPUs/utilisation} & (int $\rightarrow$ float) Map & Utilisation for all of guest's current VCPUs \\ $\mathit{RO}_\mathit{ins}$ & {\tt VCPUs/features/required} & (cpu\_feature) Set & CPU features the guest demands the host supports \\ $\mathit{RO}_\mathit{ins}$ & {\tt VCPUs/features/can\_use} & (cpu\_feature) Set & CPU features the guest can use if available \\ @@ -947,6 +1211,7 @@ $\mathit{RW}$ & {\tt actions/after\_shutdown} & on\_normal\_exit & action to ta $\mathit{RW}$ & {\tt actions/after\_reboot} & on\_normal\_exit & action to take after the guest has rebooted itself \\ $\mathit{RW}$ & {\tt actions/after\_suspend} & on\_normal\_exit & action to take after the guest has suspended itself \\ $\mathit{RW}$ & {\tt actions/after\_crash} & on\_crash\_behaviour & action to take if the guest crashes \\ +$\mathit{RO}_\mathit{run}$ & {\tt consoles} & (console ref) Set & virtual console devices \\ $\mathit{RO}_\mathit{run}$ & {\tt VIFs} & (VIF ref) Set & virtual network interfaces \\ $\mathit{RO}_\mathit{run}$ & {\tt VBDs} & (VBD ref) Set & virtual block devices \\ $\mathit{RO}_\mathit{run}$ & {\tt VTPMs} & (VTPM ref) Set & virtual TPMs \\ @@ -1322,7 +1587,7 @@ A list of all the IDs of all the VMs \subsubsection{RPC name:~get\_uuid} {\bf Overview:} -get accessor message derived from field uuid of object VM +Get the uuid field of the given VM. \noindent {\bf Signature:} \begin{verbatim} string get_uuid (session_id s, VM ref self)\end{verbatim} @@ -1335,7 +1600,7 @@ get accessor message derived from field uuid of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -1354,7 +1619,7 @@ value of the field \subsubsection{RPC name:~get\_power\_state} {\bf Overview:} -get accessor message derived from field power\_state of object VM +Get the power\_state field of the given VM. \noindent {\bf Signature:} \begin{verbatim} (vm_power_state) get_power_state (session_id s, VM ref self)\end{verbatim} @@ -1367,7 +1632,7 @@ get accessor message derived from field power\_state of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -1386,7 +1651,7 @@ value of the field \subsubsection{RPC name:~get\_name\_label} {\bf Overview:} -get accessor message derived from field name/label of object VM +Get the name/label field of the given VM. \noindent {\bf Signature:} \begin{verbatim} string get_name_label (session_id s, VM ref self)\end{verbatim} @@ -1399,7 +1664,7 @@ get accessor message derived from field name/label of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -1418,7 +1683,7 @@ value of the field \subsubsection{RPC name:~set\_name\_label} {\bf Overview:} -set accessor message derived from field name/label of object VM +Set the name/label field of the given VM. \noindent {\bf Signature:} \begin{verbatim} void set_name_label (session_id s, VM ref self, string value)\end{verbatim} @@ -1431,7 +1696,7 @@ set accessor message derived from field name/label of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline {\tt string } & value & New value to set \\ \hline @@ -1452,7 +1717,7 @@ void \subsubsection{RPC name:~get\_name\_description} {\bf Overview:} -get accessor message derived from field name/description of object VM +Get the name/description field of the given VM. \noindent {\bf Signature:} \begin{verbatim} string get_name_description (session_id s, VM ref self)\end{verbatim} @@ -1465,7 +1730,7 @@ get accessor message derived from field name/description of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -1484,7 +1749,7 @@ value of the field \subsubsection{RPC name:~set\_name\_description} {\bf Overview:} -set accessor message derived from field name/description of object VM +Set the name/description field of the given VM. \noindent {\bf Signature:} \begin{verbatim} void set_name_description (session_id s, VM ref self, string value)\end{verbatim} @@ -1497,7 +1762,7 @@ set accessor message derived from field name/description of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline {\tt string } & value & New value to set \\ \hline @@ -1518,7 +1783,7 @@ void \subsubsection{RPC name:~get\_user\_version} {\bf Overview:} -get accessor message derived from field user\_version of object VM +Get the user\_version field of the given VM. \noindent {\bf Signature:} \begin{verbatim} int get_user_version (session_id s, VM ref self)\end{verbatim} @@ -1531,7 +1796,7 @@ get accessor message derived from field user\_version of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -1550,7 +1815,7 @@ value of the field \subsubsection{RPC name:~set\_user\_version} {\bf Overview:} -set accessor message derived from field user\_version of object VM +Set the user\_version field of the given VM. \noindent {\bf Signature:} \begin{verbatim} void set_user_version (session_id s, VM ref self, int value)\end{verbatim} @@ -1563,7 +1828,7 @@ set accessor message derived from field user\_version of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline {\tt int } & value & New value to set \\ \hline @@ -1584,7 +1849,7 @@ void \subsubsection{RPC name:~get\_is\_a\_template} {\bf Overview:} -get accessor message derived from field is\_a\_template of object VM +Get the is\_a\_template field of the given VM. \noindent {\bf Signature:} \begin{verbatim} bool get_is_a_template (session_id s, VM ref self)\end{verbatim} @@ -1597,7 +1862,7 @@ get accessor message derived from field is\_a\_template of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -1616,7 +1881,7 @@ value of the field \subsubsection{RPC name:~set\_is\_a\_template} {\bf Overview:} -set accessor message derived from field is\_a\_template of object VM +Set the is\_a\_template field of the given VM. \noindent {\bf Signature:} \begin{verbatim} void set_is_a_template (session_id s, VM ref self, bool value)\end{verbatim} @@ -1629,7 +1894,73 @@ set accessor message derived from field is\_a\_template of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt bool } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_auto\_power\_on} + +{\bf Overview:} +Get the auto\_power\_on field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} bool get_auto_power_on (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +bool +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_auto\_power\_on} + +{\bf Overview:} +Set the auto\_power\_on field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_auto_power_on (session_id s, VM ref self, bool value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline {\tt bool } & value & New value to set \\ \hline @@ -1650,7 +1981,7 @@ void \subsubsection{RPC name:~get\_resident\_on} {\bf Overview:} -get accessor message derived from field resident\_on of object VM +Get the resident\_on field of the given VM. \noindent {\bf Signature:} \begin{verbatim} (host ref) get_resident_on (session_id s, VM ref self)\end{verbatim} @@ -1663,7 +1994,7 @@ get accessor message derived from field resident\_on of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -1682,7 +2013,7 @@ value of the field \subsubsection{RPC name:~get\_memory\_static\_max} {\bf Overview:} -get accessor message derived from field memory/static\_max of object VM +Get the memory/static\_max field of the given VM. \noindent {\bf Signature:} \begin{verbatim} int get_memory_static_max (session_id s, VM ref self)\end{verbatim} @@ -1695,7 +2026,7 @@ get accessor message derived from field memory/static\_max of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -1714,7 +2045,7 @@ value of the field \subsubsection{RPC name:~get\_memory\_dynamic\_max} {\bf Overview:} -get accessor message derived from field memory/dynamic\_max of object VM +Get the memory/dynamic\_max field of the given VM. \noindent {\bf Signature:} \begin{verbatim} int get_memory_dynamic_max (session_id s, VM ref self)\end{verbatim} @@ -1727,7 +2058,7 @@ get accessor message derived from field memory/dynamic\_max of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -1746,7 +2077,7 @@ value of the field \subsubsection{RPC name:~set\_memory\_dynamic\_max} {\bf Overview:} -set accessor message derived from field memory/dynamic\_max of object VM +Set the memory/dynamic\_max field of the given VM. \noindent {\bf Signature:} \begin{verbatim} void set_memory_dynamic_max (session_id s, VM ref self, int value)\end{verbatim} @@ -1759,7 +2090,7 @@ set accessor message derived from field memory/dynamic\_max of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline {\tt int } & value & New value to set \\ \hline @@ -1780,7 +2111,7 @@ void \subsubsection{RPC name:~get\_memory\_actual} {\bf Overview:} -get accessor message derived from field memory/actual of object VM +Get the memory/actual field of the given VM. \noindent {\bf Signature:} \begin{verbatim} int get_memory_actual (session_id s, VM ref self)\end{verbatim} @@ -1793,7 +2124,7 @@ get accessor message derived from field memory/actual of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -1812,7 +2143,7 @@ value of the field \subsubsection{RPC name:~get\_memory\_dynamic\_min} {\bf Overview:} -get accessor message derived from field memory/dynamic\_min of object VM +Get the memory/dynamic\_min field of the given VM. \noindent {\bf Signature:} \begin{verbatim} int get_memory_dynamic_min (session_id s, VM ref self)\end{verbatim} @@ -1825,7 +2156,7 @@ get accessor message derived from field memory/dynamic\_min of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -1844,7 +2175,7 @@ value of the field \subsubsection{RPC name:~set\_memory\_dynamic\_min} {\bf Overview:} -set accessor message derived from field memory/dynamic\_min of object VM +Set the memory/dynamic\_min field of the given VM. \noindent {\bf Signature:} \begin{verbatim} void set_memory_dynamic_min (session_id s, VM ref self, int value)\end{verbatim} @@ -1857,7 +2188,7 @@ set accessor message derived from field memory/dynamic\_min of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline {\tt int } & value & New value to set \\ \hline @@ -1878,7 +2209,7 @@ void \subsubsection{RPC name:~get\_memory\_static\_min} {\bf Overview:} -get accessor message derived from field memory/static\_min of object VM +Get the memory/static\_min field of the given VM. \noindent {\bf Signature:} \begin{verbatim} int get_memory_static_min (session_id s, VM ref self)\end{verbatim} @@ -1891,7 +2222,7 @@ get accessor message derived from field memory/static\_min of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -1910,7 +2241,7 @@ value of the field \subsubsection{RPC name:~get\_VCPUs\_policy} {\bf Overview:} -get accessor message derived from field VCPUs/policy of object VM +Get the VCPUs/policy field of the given VM. \noindent {\bf Signature:} \begin{verbatim} string get_VCPUs_policy (session_id s, VM ref self)\end{verbatim} @@ -1923,7 +2254,7 @@ get accessor message derived from field VCPUs/policy of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -1942,7 +2273,7 @@ value of the field \subsubsection{RPC name:~set\_VCPUs\_policy} {\bf Overview:} -set accessor message derived from field VCPUs/policy of object VM +Set the VCPUs/policy field of the given VM. \noindent {\bf Signature:} \begin{verbatim} void set_VCPUs_policy (session_id s, VM ref self, string value)\end{verbatim} @@ -1955,7 +2286,7 @@ set accessor message derived from field VCPUs/policy of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline {\tt string } & value & New value to set \\ \hline @@ -1976,7 +2307,7 @@ void \subsubsection{RPC name:~get\_VCPUs\_params} {\bf Overview:} -get accessor message derived from field VCPUs/params of object VM +Get the VCPUs/params field of the given VM. \noindent {\bf Signature:} \begin{verbatim} string get_VCPUs_params (session_id s, VM ref self)\end{verbatim} @@ -1989,7 +2320,7 @@ get accessor message derived from field VCPUs/params of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -2008,7 +2339,7 @@ value of the field \subsubsection{RPC name:~set\_VCPUs\_params} {\bf Overview:} -set accessor message derived from field VCPUs/params of object VM +Set the VCPUs/params field of the given VM. \noindent {\bf Signature:} \begin{verbatim} void set_VCPUs_params (session_id s, VM ref self, string value)\end{verbatim} @@ -2021,7 +2352,7 @@ set accessor message derived from field VCPUs/params of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline {\tt string } & value & New value to set \\ \hline @@ -2042,7 +2373,7 @@ void \subsubsection{RPC name:~get\_VCPUs\_number} {\bf Overview:} -get accessor message derived from field VCPUs/number of object VM +Get the VCPUs/number field of the given VM. \noindent {\bf Signature:} \begin{verbatim} int get_VCPUs_number (session_id s, VM ref self)\end{verbatim} @@ -2055,7 +2386,7 @@ get accessor message derived from field VCPUs/number of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -2068,13 +2399,47 @@ int value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_VCPUs\_number} + +{\bf Overview:} +Set the VCPUs/number field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_VCPUs_number (session_id s, VM ref self, int value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt int } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + \vspace{0.3cm} \vspace{0.3cm} \vspace{0.3cm} \subsubsection{RPC name:~get\_VCPUs\_utilisation} {\bf Overview:} -get accessor message derived from field VCPUs/utilisation of object VM +Get the VCPUs/utilisation field of the given VM. \noindent {\bf Signature:} \begin{verbatim} ((int -> float) Map) get_VCPUs_utilisation (session_id s, VM ref self)\end{verbatim} @@ -2087,7 +2452,7 @@ get accessor message derived from field VCPUs/utilisation of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -2106,7 +2471,7 @@ value of the field \subsubsection{RPC name:~get\_VCPUs\_features\_required} {\bf Overview:} -get accessor message derived from field VCPUs/features/required of object VM +Get the VCPUs/features/required field of the given VM. \noindent {\bf Signature:} \begin{verbatim} ((cpu_feature) Set) get_VCPUs_features_required (session_id s, VM ref self)\end{verbatim} @@ -2119,7 +2484,7 @@ get accessor message derived from field VCPUs/features/required of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -2138,7 +2503,7 @@ value of the field \subsubsection{RPC name:~get\_VCPUs\_features\_can\_use} {\bf Overview:} -get accessor message derived from field VCPUs/features/can\_use of object VM +Get the VCPUs/features/can\_use field of the given VM. \noindent {\bf Signature:} \begin{verbatim} ((cpu_feature) Set) get_VCPUs_features_can_use (session_id s, VM ref self)\end{verbatim} @@ -2151,7 +2516,7 @@ get accessor message derived from field VCPUs/features/can\_use of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -2170,7 +2535,7 @@ value of the field \subsubsection{RPC name:~get\_VCPUs\_features\_force\_on} {\bf Overview:} -get accessor message derived from field VCPUs/features/force\_on of object VM +Get the VCPUs/features/force\_on field of the given VM. \noindent {\bf Signature:} \begin{verbatim} ((cpu_feature) Set) get_VCPUs_features_force_on (session_id s, VM ref self)\end{verbatim} @@ -2183,7 +2548,7 @@ get accessor message derived from field VCPUs/features/force\_on of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -2196,6 +2561,40 @@ get accessor message derived from field VCPUs/features/force\_on of object VM value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_VCPUs\_features\_force\_on} + +{\bf Overview:} +Set the VCPUs/features/force\_on field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_VCPUs_features_force_on (session_id s, VM ref self, (cpu_feature) Set value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt (cpu\_feature) Set } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + \vspace{0.3cm} \vspace{0.3cm} \vspace{0.3cm} @@ -2215,7 +2614,7 @@ set add message derived from field VCPUs/features/force\_on of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline {\tt cpu\_feature } & value & New value to add \\ \hline @@ -2249,7 +2648,7 @@ set remove message derived from field VCPUs/features/force\_on of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline {\tt cpu\_feature } & value & Value to remove \\ \hline @@ -2270,7 +2669,7 @@ void \subsubsection{RPC name:~get\_VCPUs\_features\_force\_off} {\bf Overview:} -get accessor message derived from field VCPUs/features/force\_off of object VM +Get the VCPUs/features/force\_off field of the given VM. \noindent {\bf Signature:} \begin{verbatim} ((cpu_feature) Set) get_VCPUs_features_force_off (session_id s, VM ref self)\end{verbatim} @@ -2283,7 +2682,7 @@ get accessor message derived from field VCPUs/features/force\_off of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -2296,6 +2695,40 @@ get accessor message derived from field VCPUs/features/force\_off of object VM value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_VCPUs\_features\_force\_off} + +{\bf Overview:} +Set the VCPUs/features/force\_off field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_VCPUs_features_force_off (session_id s, VM ref self, (cpu_feature) Set value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt (cpu\_feature) Set } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + \vspace{0.3cm} \vspace{0.3cm} \vspace{0.3cm} @@ -2315,7 +2748,7 @@ set add message derived from field VCPUs/features/force\_off of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline {\tt cpu\_feature } & value & New value to add \\ \hline @@ -2349,7 +2782,7 @@ set remove message derived from field VCPUs/features/force\_off of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline {\tt cpu\_feature } & value & Value to remove \\ \hline @@ -2370,7 +2803,7 @@ void \subsubsection{RPC name:~get\_actions\_after\_shutdown} {\bf Overview:} -get accessor message derived from field actions/after\_shutdown of object VM +Get the actions/after\_shutdown field of the given VM. \noindent {\bf Signature:} \begin{verbatim} (on_normal_exit) get_actions_after_shutdown (session_id s, VM ref self)\end{verbatim} @@ -2383,7 +2816,7 @@ get accessor message derived from field actions/after\_shutdown of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -2402,7 +2835,7 @@ value of the field \subsubsection{RPC name:~set\_actions\_after\_shutdown} {\bf Overview:} -set accessor message derived from field actions/after\_shutdown of object VM +Set the actions/after\_shutdown field of the given VM. \noindent {\bf Signature:} \begin{verbatim} void set_actions_after_shutdown (session_id s, VM ref self, on_normal_exit value)\end{verbatim} @@ -2415,7 +2848,7 @@ set accessor message derived from field actions/after\_shutdown of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline {\tt on\_normal\_exit } & value & New value to set \\ \hline @@ -2436,7 +2869,7 @@ void \subsubsection{RPC name:~get\_actions\_after\_reboot} {\bf Overview:} -get accessor message derived from field actions/after\_reboot of object VM +Get the actions/after\_reboot field of the given VM. \noindent {\bf Signature:} \begin{verbatim} (on_normal_exit) get_actions_after_reboot (session_id s, VM ref self)\end{verbatim} @@ -2449,7 +2882,7 @@ get accessor message derived from field actions/after\_reboot of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -2468,7 +2901,7 @@ value of the field \subsubsection{RPC name:~set\_actions\_after\_reboot} {\bf Overview:} -set accessor message derived from field actions/after\_reboot of object VM +Set the actions/after\_reboot field of the given VM. \noindent {\bf Signature:} \begin{verbatim} void set_actions_after_reboot (session_id s, VM ref self, on_normal_exit value)\end{verbatim} @@ -2481,7 +2914,7 @@ set accessor message derived from field actions/after\_reboot of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline {\tt on\_normal\_exit } & value & New value to set \\ \hline @@ -2502,7 +2935,7 @@ void \subsubsection{RPC name:~get\_actions\_after\_suspend} {\bf Overview:} -get accessor message derived from field actions/after\_suspend of object VM +Get the actions/after\_suspend field of the given VM. \noindent {\bf Signature:} \begin{verbatim} (on_normal_exit) get_actions_after_suspend (session_id s, VM ref self)\end{verbatim} @@ -2515,7 +2948,7 @@ get accessor message derived from field actions/after\_suspend of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -2534,7 +2967,7 @@ value of the field \subsubsection{RPC name:~set\_actions\_after\_suspend} {\bf Overview:} -set accessor message derived from field actions/after\_suspend of object VM +Set the actions/after\_suspend field of the given VM. \noindent {\bf Signature:} \begin{verbatim} void set_actions_after_suspend (session_id s, VM ref self, on_normal_exit value)\end{verbatim} @@ -2547,7 +2980,7 @@ set accessor message derived from field actions/after\_suspend of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline {\tt on\_normal\_exit } & value & New value to set \\ \hline @@ -2568,7 +3001,7 @@ void \subsubsection{RPC name:~get\_actions\_after\_crash} {\bf Overview:} -get accessor message derived from field actions/after\_crash of object VM +Get the actions/after\_crash field of the given VM. \noindent {\bf Signature:} \begin{verbatim} (on_crash_behaviour) get_actions_after_crash (session_id s, VM ref self)\end{verbatim} @@ -2581,7 +3014,7 @@ get accessor message derived from field actions/after\_crash of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -2600,7 +3033,7 @@ value of the field \subsubsection{RPC name:~set\_actions\_after\_crash} {\bf Overview:} -set accessor message derived from field actions/after\_crash of object VM +Set the actions/after\_crash field of the given VM. \noindent {\bf Signature:} \begin{verbatim} void set_actions_after_crash (session_id s, VM ref self, on_crash_behaviour value)\end{verbatim} @@ -2613,7 +3046,7 @@ set accessor message derived from field actions/after\_crash of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline {\tt on\_crash\_behaviour } & value & New value to set \\ \hline @@ -2628,13 +3061,45 @@ void +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_consoles} + +{\bf Overview:} +Get the consoles field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} ((console ref) Set) get_consoles (session_id s, VM ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +(console ref) Set +} + + +value of the field \vspace{0.3cm} \vspace{0.3cm} \vspace{0.3cm} \subsubsection{RPC name:~get\_VIFs} {\bf Overview:} -get accessor message derived from field VIFs of object VM +Get the VIFs field of the given VM. \noindent {\bf Signature:} \begin{verbatim} ((VIF ref) Set) get_VIFs (session_id s, VM ref self)\end{verbatim} @@ -2647,7 +3112,7 @@ get accessor message derived from field VIFs of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -2666,7 +3131,7 @@ value of the field \subsubsection{RPC name:~get\_VBDs} {\bf Overview:} -get accessor message derived from field VBDs of object VM +Get the VBDs field of the given VM. \noindent {\bf Signature:} \begin{verbatim} ((VBD ref) Set) get_VBDs (session_id s, VM ref self)\end{verbatim} @@ -2679,7 +3144,7 @@ get accessor message derived from field VBDs of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -2698,7 +3163,7 @@ value of the field \subsubsection{RPC name:~get\_VTPMs} {\bf Overview:} -get accessor message derived from field VTPMs of object VM +Get the VTPMs field of the given VM. \noindent {\bf Signature:} \begin{verbatim} ((VTPM ref) Set) get_VTPMs (session_id s, VM ref self)\end{verbatim} @@ -2711,7 +3176,7 @@ get accessor message derived from field VTPMs of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -2730,7 +3195,7 @@ value of the field \subsubsection{RPC name:~get\_bios\_boot} {\bf Overview:} -get accessor message derived from field bios/boot of object VM +Get the bios/boot field of the given VM. \noindent {\bf Signature:} \begin{verbatim} string get_bios_boot (session_id s, VM ref self)\end{verbatim} @@ -2743,7 +3208,7 @@ get accessor message derived from field bios/boot of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -2762,7 +3227,7 @@ value of the field \subsubsection{RPC name:~set\_bios\_boot} {\bf Overview:} -set accessor message derived from field bios/boot of object VM +Set the bios/boot field of the given VM. \noindent {\bf Signature:} \begin{verbatim} void set_bios_boot (session_id s, VM ref self, string value)\end{verbatim} @@ -2775,7 +3240,7 @@ set accessor message derived from field bios/boot of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline {\tt string } & value & New value to set \\ \hline @@ -2796,7 +3261,7 @@ void \subsubsection{RPC name:~get\_platform\_std\_VGA} {\bf Overview:} -get accessor message derived from field platform/std\_VGA of object VM +Get the platform/std\_VGA field of the given VM. \noindent {\bf Signature:} \begin{verbatim} bool get_platform_std_VGA (session_id s, VM ref self)\end{verbatim} @@ -2809,7 +3274,7 @@ get accessor message derived from field platform/std\_VGA of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -2828,7 +3293,7 @@ value of the field \subsubsection{RPC name:~set\_platform\_std\_VGA} {\bf Overview:} -set accessor message derived from field platform/std\_VGA of object VM +Set the platform/std\_VGA field of the given VM. \noindent {\bf Signature:} \begin{verbatim} void set_platform_std_VGA (session_id s, VM ref self, bool value)\end{verbatim} @@ -2841,7 +3306,7 @@ set accessor message derived from field platform/std\_VGA of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline {\tt bool } & value & New value to set \\ \hline @@ -2862,7 +3327,7 @@ void \subsubsection{RPC name:~get\_platform\_serial} {\bf Overview:} -get accessor message derived from field platform/serial of object VM +Get the platform/serial field of the given VM. \noindent {\bf Signature:} \begin{verbatim} string get_platform_serial (session_id s, VM ref self)\end{verbatim} @@ -2875,7 +3340,7 @@ get accessor message derived from field platform/serial of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -2894,7 +3359,7 @@ value of the field \subsubsection{RPC name:~set\_platform\_serial} {\bf Overview:} -set accessor message derived from field platform/serial of object VM +Set the platform/serial field of the given VM. \noindent {\bf Signature:} \begin{verbatim} void set_platform_serial (session_id s, VM ref self, string value)\end{verbatim} @@ -2907,7 +3372,7 @@ set accessor message derived from field platform/serial of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline {\tt string } & value & New value to set \\ \hline @@ -2928,7 +3393,7 @@ void \subsubsection{RPC name:~get\_platform\_localtime} {\bf Overview:} -get accessor message derived from field platform/localtime of object VM +Get the platform/localtime field of the given VM. \noindent {\bf Signature:} \begin{verbatim} bool get_platform_localtime (session_id s, VM ref self)\end{verbatim} @@ -2941,7 +3406,7 @@ get accessor message derived from field platform/localtime of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -2960,7 +3425,7 @@ value of the field \subsubsection{RPC name:~set\_platform\_localtime} {\bf Overview:} -set accessor message derived from field platform/localtime of object VM +Set the platform/localtime field of the given VM. \noindent {\bf Signature:} \begin{verbatim} void set_platform_localtime (session_id s, VM ref self, bool value)\end{verbatim} @@ -2973,7 +3438,7 @@ set accessor message derived from field platform/localtime of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline {\tt bool } & value & New value to set \\ \hline @@ -2994,7 +3459,7 @@ void \subsubsection{RPC name:~get\_platform\_clock\_offset} {\bf Overview:} -get accessor message derived from field platform/clock\_offset of object VM +Get the platform/clock\_offset field of the given VM. \noindent {\bf Signature:} \begin{verbatim} bool get_platform_clock_offset (session_id s, VM ref self)\end{verbatim} @@ -3007,7 +3472,7 @@ get accessor message derived from field platform/clock\_offset of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -3026,7 +3491,7 @@ value of the field \subsubsection{RPC name:~set\_platform\_clock\_offset} {\bf Overview:} -set accessor message derived from field platform/clock\_offset of object VM +Set the platform/clock\_offset field of the given VM. \noindent {\bf Signature:} \begin{verbatim} void set_platform_clock_offset (session_id s, VM ref self, bool value)\end{verbatim} @@ -3039,7 +3504,7 @@ set accessor message derived from field platform/clock\_offset of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline {\tt bool } & value & New value to set \\ \hline @@ -3060,7 +3525,7 @@ void \subsubsection{RPC name:~get\_platform\_enable\_audio} {\bf Overview:} -get accessor message derived from field platform/enable\_audio of object VM +Get the platform/enable\_audio field of the given VM. \noindent {\bf Signature:} \begin{verbatim} bool get_platform_enable_audio (session_id s, VM ref self)\end{verbatim} @@ -3073,7 +3538,7 @@ get accessor message derived from field platform/enable\_audio of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -3092,7 +3557,7 @@ value of the field \subsubsection{RPC name:~set\_platform\_enable\_audio} {\bf Overview:} -set accessor message derived from field platform/enable\_audio of object VM +Set the platform/enable\_audio field of the given VM. \noindent {\bf Signature:} \begin{verbatim} void set_platform_enable_audio (session_id s, VM ref self, bool value)\end{verbatim} @@ -3105,7 +3570,7 @@ set accessor message derived from field platform/enable\_audio of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline {\tt bool } & value & New value to set \\ \hline @@ -3126,7 +3591,7 @@ void \subsubsection{RPC name:~get\_builder} {\bf Overview:} -get accessor message derived from field builder of object VM +Get the builder field of the given VM. \noindent {\bf Signature:} \begin{verbatim} string get_builder (session_id s, VM ref self)\end{verbatim} @@ -3139,7 +3604,7 @@ get accessor message derived from field builder of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -3158,7 +3623,7 @@ value of the field \subsubsection{RPC name:~set\_builder} {\bf Overview:} -set accessor message derived from field builder of object VM +Set the builder field of the given VM. \noindent {\bf Signature:} \begin{verbatim} void set_builder (session_id s, VM ref self, string value)\end{verbatim} @@ -3171,7 +3636,7 @@ set accessor message derived from field builder of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline {\tt string } & value & New value to set \\ \hline @@ -3192,7 +3657,7 @@ void \subsubsection{RPC name:~get\_boot\_method} {\bf Overview:} -get accessor message derived from field boot\_method of object VM +Get the boot\_method field of the given VM. \noindent {\bf Signature:} \begin{verbatim} (boot_type) get_boot_method (session_id s, VM ref self)\end{verbatim} @@ -3205,7 +3670,7 @@ get accessor message derived from field boot\_method of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -3224,7 +3689,7 @@ value of the field \subsubsection{RPC name:~set\_boot\_method} {\bf Overview:} -set accessor message derived from field boot\_method of object VM +Set the boot\_method field of the given VM. \noindent {\bf Signature:} \begin{verbatim} void set_boot_method (session_id s, VM ref self, boot_type value)\end{verbatim} @@ -3237,7 +3702,7 @@ set accessor message derived from field boot\_method of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline {\tt boot\_type } & value & New value to set \\ \hline @@ -3258,7 +3723,7 @@ void \subsubsection{RPC name:~get\_kernel\_kernel} {\bf Overview:} -get accessor message derived from field kernel/kernel of object VM +Get the kernel/kernel field of the given VM. \noindent {\bf Signature:} \begin{verbatim} string get_kernel_kernel (session_id s, VM ref self)\end{verbatim} @@ -3271,7 +3736,7 @@ get accessor message derived from field kernel/kernel of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -3290,7 +3755,7 @@ value of the field \subsubsection{RPC name:~set\_kernel\_kernel} {\bf Overview:} -set accessor message derived from field kernel/kernel of object VM +Set the kernel/kernel field of the given VM. \noindent {\bf Signature:} \begin{verbatim} void set_kernel_kernel (session_id s, VM ref self, string value)\end{verbatim} @@ -3303,7 +3768,7 @@ set accessor message derived from field kernel/kernel of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline {\tt string } & value & New value to set \\ \hline @@ -3324,7 +3789,7 @@ void \subsubsection{RPC name:~get\_kernel\_initrd} {\bf Overview:} -get accessor message derived from field kernel/initrd of object VM +Get the kernel/initrd field of the given VM. \noindent {\bf Signature:} \begin{verbatim} string get_kernel_initrd (session_id s, VM ref self)\end{verbatim} @@ -3337,7 +3802,7 @@ get accessor message derived from field kernel/initrd of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -3356,7 +3821,7 @@ value of the field \subsubsection{RPC name:~set\_kernel\_initrd} {\bf Overview:} -set accessor message derived from field kernel/initrd of object VM +Set the kernel/initrd field of the given VM. \noindent {\bf Signature:} \begin{verbatim} void set_kernel_initrd (session_id s, VM ref self, string value)\end{verbatim} @@ -3369,7 +3834,7 @@ set accessor message derived from field kernel/initrd of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline {\tt string } & value & New value to set \\ \hline @@ -3390,7 +3855,7 @@ void \subsubsection{RPC name:~get\_kernel\_args} {\bf Overview:} -get accessor message derived from field kernel/args of object VM +Get the kernel/args field of the given VM. \noindent {\bf Signature:} \begin{verbatim} string get_kernel_args (session_id s, VM ref self)\end{verbatim} @@ -3403,7 +3868,7 @@ get accessor message derived from field kernel/args of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -3422,7 +3887,7 @@ value of the field \subsubsection{RPC name:~set\_kernel\_args} {\bf Overview:} -set accessor message derived from field kernel/args of object VM +Set the kernel/args field of the given VM. \noindent {\bf Signature:} \begin{verbatim} void set_kernel_args (session_id s, VM ref self, string value)\end{verbatim} @@ -3435,7 +3900,7 @@ set accessor message derived from field kernel/args of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline {\tt string } & value & New value to set \\ \hline @@ -3456,7 +3921,7 @@ void \subsubsection{RPC name:~get\_grub\_cmdline} {\bf Overview:} -get accessor message derived from field grub/cmdline of object VM +Get the grub/cmdline field of the given VM. \noindent {\bf Signature:} \begin{verbatim} string get_grub_cmdline (session_id s, VM ref self)\end{verbatim} @@ -3469,7 +3934,7 @@ get accessor message derived from field grub/cmdline of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -3488,7 +3953,7 @@ value of the field \subsubsection{RPC name:~set\_grub\_cmdline} {\bf Overview:} -set accessor message derived from field grub/cmdline of object VM +Set the grub/cmdline field of the given VM. \noindent {\bf Signature:} \begin{verbatim} void set_grub_cmdline (session_id s, VM ref self, string value)\end{verbatim} @@ -3501,7 +3966,7 @@ set accessor message derived from field grub/cmdline of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline {\tt string } & value & New value to set \\ \hline @@ -3522,7 +3987,7 @@ void \subsubsection{RPC name:~get\_PCI\_bus} {\bf Overview:} -get accessor message derived from field PCI\_bus of object VM +Get the PCI\_bus field of the given VM. \noindent {\bf Signature:} \begin{verbatim} string get_PCI_bus (session_id s, VM ref self)\end{verbatim} @@ -3535,7 +4000,7 @@ get accessor message derived from field PCI\_bus of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -3554,7 +4019,7 @@ value of the field \subsubsection{RPC name:~get\_tools\_version} {\bf Overview:} -get accessor message derived from field tools\_version of object VM +Get the tools\_version field of the given VM. \noindent {\bf Signature:} \begin{verbatim} ((string -> string) Map) get_tools_version (session_id s, VM ref self)\end{verbatim} @@ -3567,7 +4032,7 @@ get accessor message derived from field tools\_version of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -3586,7 +4051,7 @@ value of the field \subsubsection{RPC name:~get\_otherConfig} {\bf Overview:} -get accessor message derived from field otherConfig of object VM +Get the otherConfig field of the given VM. \noindent {\bf Signature:} \begin{verbatim} ((string -> string) Map) get_otherConfig (session_id s, VM ref self)\end{verbatim} @@ -3599,7 +4064,7 @@ get accessor message derived from field otherConfig of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -3612,6 +4077,40 @@ get accessor message derived from field otherConfig of object VM value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_otherConfig} + +{\bf Overview:} +Set the otherConfig field of the given VM. + + \noindent {\bf Signature:} +\begin{verbatim} void set_otherConfig (session_id s, VM ref self, (string -> string) Map value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline + +{\tt (string $\rightarrow$ string) Map } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + \vspace{0.3cm} \vspace{0.3cm} \vspace{0.3cm} @@ -3631,7 +4130,7 @@ map add message derived from field otherConfig of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline {\tt string } & key & Key to add \\ \hline @@ -3667,7 +4166,7 @@ map remove message derived from field otherConfig of object VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline {\tt string } & key & Key to remove \\ \hline @@ -3688,7 +4187,7 @@ void \subsubsection{RPC name:~create} {\bf Overview:} -constructor for class VM +Create a new VM instance, and return its handle. \noindent {\bf Signature:} \begin{verbatim} (VM ref) create (session_id s, VM record args)\end{verbatim} @@ -3720,7 +4219,7 @@ reference to the newly created object \subsubsection{RPC name:~destroy} {\bf Overview:} -destructor for class VM +Destroy the specified VM. The VM is completely removed from the system. This function can only be called when the VM is in the Halted State. \noindent {\bf Signature:} \begin{verbatim} void destroy (session_id s, VM ref self)\end{verbatim} @@ -3733,7 +4232,7 @@ destructor for class VM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VM ref } & self & object instance \\ \hline +{\tt VM ref } & self & reference to the object \\ \hline \end{tabular} @@ -3752,7 +4251,7 @@ void \subsubsection{RPC name:~get\_by\_uuid} {\bf Overview:} -returns the VM instance with a particular uuid +Get a reference to the VM instance with the specified UUID. \noindent {\bf Signature:} \begin{verbatim} (VM ref) get_by_uuid (session_id s, string uuid)\end{verbatim} @@ -3784,7 +4283,7 @@ reference to the object \subsubsection{RPC name:~get\_record} {\bf Overview:} -returns a record containing the state of an instance of class VM +Get a record containing the current state of the given VM. \noindent {\bf Signature:} \begin{verbatim} (VM record) get_record (session_id s, VM ref self)\end{verbatim} @@ -3816,7 +4315,7 @@ all fields from the object \subsubsection{RPC name:~get\_by\_name\_label} {\bf Overview:} -returns the VM instance with a particular name label +Get all the VM instances with the given label. \noindent {\bf Signature:} \begin{verbatim} ((VM ref) Set) get_by_name_label (session_id s, string label)\end{verbatim} @@ -4019,7 +4518,7 @@ A list of all the IDs of all the hosts \subsubsection{RPC name:~get\_uuid} {\bf Overview:} -get accessor message derived from field uuid of object host +Get the uuid field of the given host. \noindent {\bf Signature:} \begin{verbatim} string get_uuid (session_id s, host ref self)\end{verbatim} @@ -4032,7 +4531,7 @@ get accessor message derived from field uuid of object host \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt host ref } & self & object instance \\ \hline +{\tt host ref } & self & reference to the object \\ \hline \end{tabular} @@ -4051,7 +4550,7 @@ value of the field \subsubsection{RPC name:~get\_name\_label} {\bf Overview:} -get accessor message derived from field name/label of object host +Get the name/label field of the given host. \noindent {\bf Signature:} \begin{verbatim} string get_name_label (session_id s, host ref self)\end{verbatim} @@ -4064,7 +4563,7 @@ get accessor message derived from field name/label of object host \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt host ref } & self & object instance \\ \hline +{\tt host ref } & self & reference to the object \\ \hline \end{tabular} @@ -4083,7 +4582,7 @@ value of the field \subsubsection{RPC name:~set\_name\_label} {\bf Overview:} -set accessor message derived from field name/label of object host +Set the name/label field of the given host. \noindent {\bf Signature:} \begin{verbatim} void set_name_label (session_id s, host ref self, string value)\end{verbatim} @@ -4096,7 +4595,7 @@ set accessor message derived from field name/label of object host \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt host ref } & self & object instance \\ \hline +{\tt host ref } & self & reference to the object \\ \hline {\tt string } & value & New value to set \\ \hline @@ -4117,7 +4616,7 @@ void \subsubsection{RPC name:~get\_name\_description} {\bf Overview:} -get accessor message derived from field name/description of object host +Get the name/description field of the given host. \noindent {\bf Signature:} \begin{verbatim} string get_name_description (session_id s, host ref self)\end{verbatim} @@ -4130,7 +4629,7 @@ get accessor message derived from field name/description of object host \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt host ref } & self & object instance \\ \hline +{\tt host ref } & self & reference to the object \\ \hline \end{tabular} @@ -4149,7 +4648,7 @@ value of the field \subsubsection{RPC name:~set\_name\_description} {\bf Overview:} -set accessor message derived from field name/description of object host +Set the name/description field of the given host. \noindent {\bf Signature:} \begin{verbatim} void set_name_description (session_id s, host ref self, string value)\end{verbatim} @@ -4162,7 +4661,7 @@ set accessor message derived from field name/description of object host \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt host ref } & self & object instance \\ \hline +{\tt host ref } & self & reference to the object \\ \hline {\tt string } & value & New value to set \\ \hline @@ -4183,7 +4682,7 @@ void \subsubsection{RPC name:~get\_software\_version} {\bf Overview:} -get accessor message derived from field software\_version of object host +Get the software\_version field of the given host. \noindent {\bf Signature:} \begin{verbatim} ((string -> string) Map) get_software_version (session_id s, host ref self)\end{verbatim} @@ -4196,7 +4695,7 @@ get accessor message derived from field software\_version of object host \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt host ref } & self & object instance \\ \hline +{\tt host ref } & self & reference to the object \\ \hline \end{tabular} @@ -4215,7 +4714,7 @@ value of the field \subsubsection{RPC name:~get\_resident\_VMs} {\bf Overview:} -get accessor message derived from field resident\_VMs of object host +Get the resident\_VMs field of the given host. \noindent {\bf Signature:} \begin{verbatim} ((VM ref) Set) get_resident_VMs (session_id s, host ref self)\end{verbatim} @@ -4228,7 +4727,7 @@ get accessor message derived from field resident\_VMs of object host \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt host ref } & self & object instance \\ \hline +{\tt host ref } & self & reference to the object \\ \hline \end{tabular} @@ -4247,7 +4746,7 @@ value of the field \subsubsection{RPC name:~get\_PIFs} {\bf Overview:} -get accessor message derived from field PIFs of object host +Get the PIFs field of the given host. \noindent {\bf Signature:} \begin{verbatim} ((PIF ref) Set) get_PIFs (session_id s, host ref self)\end{verbatim} @@ -4260,7 +4759,7 @@ get accessor message derived from field PIFs of object host \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt host ref } & self & object instance \\ \hline +{\tt host ref } & self & reference to the object \\ \hline \end{tabular} @@ -4279,7 +4778,7 @@ value of the field \subsubsection{RPC name:~get\_host\_CPUs} {\bf Overview:} -get accessor message derived from field host\_CPUs of object host +Get the host\_CPUs field of the given host. \noindent {\bf Signature:} \begin{verbatim} ((host_cpu ref) Set) get_host_CPUs (session_id s, host ref self)\end{verbatim} @@ -4292,7 +4791,7 @@ get accessor message derived from field host\_CPUs of object host \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt host ref } & self & object instance \\ \hline +{\tt host ref } & self & reference to the object \\ \hline \end{tabular} @@ -4311,7 +4810,7 @@ value of the field \subsubsection{RPC name:~create} {\bf Overview:} -constructor for class host +Create a new host instance, and return its handle. \noindent {\bf Signature:} \begin{verbatim} (host ref) create (session_id s, host record args)\end{verbatim} @@ -4343,7 +4842,7 @@ reference to the newly created object \subsubsection{RPC name:~destroy} {\bf Overview:} -destructor for class host +Destroy the specified host instance. \noindent {\bf Signature:} \begin{verbatim} void destroy (session_id s, host ref self)\end{verbatim} @@ -4356,7 +4855,7 @@ destructor for class host \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt host ref } & self & object instance \\ \hline +{\tt host ref } & self & reference to the object \\ \hline \end{tabular} @@ -4375,7 +4874,7 @@ void \subsubsection{RPC name:~get\_by\_uuid} {\bf Overview:} -returns the host instance with a particular uuid +Get a reference to the host instance with the specified UUID. \noindent {\bf Signature:} \begin{verbatim} (host ref) get_by_uuid (session_id s, string uuid)\end{verbatim} @@ -4407,7 +4906,7 @@ reference to the object \subsubsection{RPC name:~get\_record} {\bf Overview:} -returns a record containing the state of an instance of class host +Get a record containing the current state of the given host. \noindent {\bf Signature:} \begin{verbatim} (host record) get_record (session_id s, host ref self)\end{verbatim} @@ -4439,7 +4938,7 @@ all fields from the object \subsubsection{RPC name:~get\_by\_name\_label} {\bf Overview:} -returns the host instance with a particular name label +Get all the host instances with the given label. \noindent {\bf Signature:} \begin{verbatim} ((host ref) Set) get_by_name_label (session_id s, string label)\end{verbatim} @@ -4494,7 +4993,7 @@ $\mathit{RO}_\mathit{run}$ & {\tt utilisation} & float & the current CPU utilis \subsubsection{RPC name:~get\_uuid} {\bf Overview:} -get accessor message derived from field uuid of object host\_cpu +Get the uuid field of the given host\_cpu. \noindent {\bf Signature:} \begin{verbatim} string get_uuid (session_id s, host_cpu ref self)\end{verbatim} @@ -4507,7 +5006,7 @@ get accessor message derived from field uuid of object host\_cpu \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt host\_cpu ref } & self & object instance \\ \hline +{\tt host\_cpu ref } & self & reference to the object \\ \hline \end{tabular} @@ -4526,7 +5025,7 @@ value of the field \subsubsection{RPC name:~get\_host} {\bf Overview:} -get accessor message derived from field host of object host\_cpu +Get the host field of the given host\_cpu. \noindent {\bf Signature:} \begin{verbatim} (host ref) get_host (session_id s, host_cpu ref self)\end{verbatim} @@ -4539,7 +5038,7 @@ get accessor message derived from field host of object host\_cpu \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt host\_cpu ref } & self & object instance \\ \hline +{\tt host\_cpu ref } & self & reference to the object \\ \hline \end{tabular} @@ -4558,7 +5057,7 @@ value of the field \subsubsection{RPC name:~get\_number} {\bf Overview:} -get accessor message derived from field number of object host\_cpu +Get the number field of the given host\_cpu. \noindent {\bf Signature:} \begin{verbatim} int get_number (session_id s, host_cpu ref self)\end{verbatim} @@ -4571,7 +5070,7 @@ get accessor message derived from field number of object host\_cpu \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt host\_cpu ref } & self & object instance \\ \hline +{\tt host\_cpu ref } & self & reference to the object \\ \hline \end{tabular} @@ -4590,7 +5089,7 @@ value of the field \subsubsection{RPC name:~get\_vendor} {\bf Overview:} -get accessor message derived from field vendor of object host\_cpu +Get the vendor field of the given host\_cpu. \noindent {\bf Signature:} \begin{verbatim} string get_vendor (session_id s, host_cpu ref self)\end{verbatim} @@ -4603,7 +5102,7 @@ get accessor message derived from field vendor of object host\_cpu \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt host\_cpu ref } & self & object instance \\ \hline +{\tt host\_cpu ref } & self & reference to the object \\ \hline \end{tabular} @@ -4622,7 +5121,7 @@ value of the field \subsubsection{RPC name:~get\_speed} {\bf Overview:} -get accessor message derived from field speed of object host\_cpu +Get the speed field of the given host\_cpu. \noindent {\bf Signature:} \begin{verbatim} int get_speed (session_id s, host_cpu ref self)\end{verbatim} @@ -4635,7 +5134,7 @@ get accessor message derived from field speed of object host\_cpu \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt host\_cpu ref } & self & object instance \\ \hline +{\tt host\_cpu ref } & self & reference to the object \\ \hline \end{tabular} @@ -4654,7 +5153,7 @@ value of the field \subsubsection{RPC name:~get\_modelname} {\bf Overview:} -get accessor message derived from field modelname of object host\_cpu +Get the modelname field of the given host\_cpu. \noindent {\bf Signature:} \begin{verbatim} string get_modelname (session_id s, host_cpu ref self)\end{verbatim} @@ -4667,7 +5166,7 @@ get accessor message derived from field modelname of object host\_cpu \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt host\_cpu ref } & self & object instance \\ \hline +{\tt host\_cpu ref } & self & reference to the object \\ \hline \end{tabular} @@ -4686,7 +5185,7 @@ value of the field \subsubsection{RPC name:~get\_features} {\bf Overview:} -get accessor message derived from field features of object host\_cpu +Get the features field of the given host\_cpu. \noindent {\bf Signature:} \begin{verbatim} ((cpu_feature) Set) get_features (session_id s, host_cpu ref self)\end{verbatim} @@ -4699,7 +5198,7 @@ get accessor message derived from field features of object host\_cpu \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt host\_cpu ref } & self & object instance \\ \hline +{\tt host\_cpu ref } & self & reference to the object \\ \hline \end{tabular} @@ -4718,7 +5217,7 @@ value of the field \subsubsection{RPC name:~get\_utilisation} {\bf Overview:} -get accessor message derived from field utilisation of object host\_cpu +Get the utilisation field of the given host\_cpu. \noindent {\bf Signature:} \begin{verbatim} float get_utilisation (session_id s, host_cpu ref self)\end{verbatim} @@ -4731,7 +5230,7 @@ get accessor message derived from field utilisation of object host\_cpu \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt host\_cpu ref } & self & object instance \\ \hline +{\tt host\_cpu ref } & self & reference to the object \\ \hline \end{tabular} @@ -4750,7 +5249,7 @@ value of the field \subsubsection{RPC name:~create} {\bf Overview:} -constructor for class host\_cpu +Create a new host\_cpu instance, and return its handle. \noindent {\bf Signature:} \begin{verbatim} (host_cpu ref) create (session_id s, host_cpu record args)\end{verbatim} @@ -4782,7 +5281,7 @@ reference to the newly created object \subsubsection{RPC name:~destroy} {\bf Overview:} -destructor for class host\_cpu +Destroy the specified host\_cpu instance. \noindent {\bf Signature:} \begin{verbatim} void destroy (session_id s, host_cpu ref self)\end{verbatim} @@ -4795,7 +5294,7 @@ destructor for class host\_cpu \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt host\_cpu ref } & self & object instance \\ \hline +{\tt host\_cpu ref } & self & reference to the object \\ \hline \end{tabular} @@ -4814,7 +5313,7 @@ void \subsubsection{RPC name:~get\_by\_uuid} {\bf Overview:} -returns the host\_cpu instance with a particular uuid +Get a reference to the host\_cpu instance with the specified UUID. \noindent {\bf Signature:} \begin{verbatim} (host_cpu ref) get_by_uuid (session_id s, string uuid)\end{verbatim} @@ -4846,7 +5345,7 @@ reference to the object \subsubsection{RPC name:~get\_record} {\bf Overview:} -returns a record containing the state of an instance of class host\_cpu +Get a record containing the current state of the given host\_cpu. \noindent {\bf Signature:} \begin{verbatim} (host_cpu record) get_record (session_id s, host_cpu ref self)\end{verbatim} @@ -4921,7 +5420,7 @@ A list of all the IDs of all the networks \subsubsection{RPC name:~get\_uuid} {\bf Overview:} -get accessor message derived from field uuid of object network +Get the uuid field of the given network. \noindent {\bf Signature:} \begin{verbatim} string get_uuid (session_id s, network ref self)\end{verbatim} @@ -4934,7 +5433,7 @@ get accessor message derived from field uuid of object network \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt network ref } & self & object instance \\ \hline +{\tt network ref } & self & reference to the object \\ \hline \end{tabular} @@ -4953,7 +5452,7 @@ value of the field \subsubsection{RPC name:~get\_name\_label} {\bf Overview:} -get accessor message derived from field name/label of object network +Get the name/label field of the given network. \noindent {\bf Signature:} \begin{verbatim} string get_name_label (session_id s, network ref self)\end{verbatim} @@ -4966,7 +5465,7 @@ get accessor message derived from field name/label of object network \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt network ref } & self & object instance \\ \hline +{\tt network ref } & self & reference to the object \\ \hline \end{tabular} @@ -4985,7 +5484,7 @@ value of the field \subsubsection{RPC name:~set\_name\_label} {\bf Overview:} -set accessor message derived from field name/label of object network +Set the name/label field of the given network. \noindent {\bf Signature:} \begin{verbatim} void set_name_label (session_id s, network ref self, string value)\end{verbatim} @@ -4998,7 +5497,7 @@ set accessor message derived from field name/label of object network \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt network ref } & self & object instance \\ \hline +{\tt network ref } & self & reference to the object \\ \hline {\tt string } & value & New value to set \\ \hline @@ -5019,7 +5518,7 @@ void \subsubsection{RPC name:~get\_name\_description} {\bf Overview:} -get accessor message derived from field name/description of object network +Get the name/description field of the given network. \noindent {\bf Signature:} \begin{verbatim} string get_name_description (session_id s, network ref self)\end{verbatim} @@ -5032,7 +5531,7 @@ get accessor message derived from field name/description of object network \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt network ref } & self & object instance \\ \hline +{\tt network ref } & self & reference to the object \\ \hline \end{tabular} @@ -5051,7 +5550,7 @@ value of the field \subsubsection{RPC name:~set\_name\_description} {\bf Overview:} -set accessor message derived from field name/description of object network +Set the name/description field of the given network. \noindent {\bf Signature:} \begin{verbatim} void set_name_description (session_id s, network ref self, string value)\end{verbatim} @@ -5064,7 +5563,7 @@ set accessor message derived from field name/description of object network \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt network ref } & self & object instance \\ \hline +{\tt network ref } & self & reference to the object \\ \hline {\tt string } & value & New value to set \\ \hline @@ -5085,7 +5584,7 @@ void \subsubsection{RPC name:~get\_VIFs} {\bf Overview:} -get accessor message derived from field VIFs of object network +Get the VIFs field of the given network. \noindent {\bf Signature:} \begin{verbatim} ((VIF ref) Set) get_VIFs (session_id s, network ref self)\end{verbatim} @@ -5098,7 +5597,7 @@ get accessor message derived from field VIFs of object network \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt network ref } & self & object instance \\ \hline +{\tt network ref } & self & reference to the object \\ \hline \end{tabular} @@ -5117,7 +5616,7 @@ value of the field \subsubsection{RPC name:~get\_PIFs} {\bf Overview:} -get accessor message derived from field PIFs of object network +Get the PIFs field of the given network. \noindent {\bf Signature:} \begin{verbatim} ((PIF ref) Set) get_PIFs (session_id s, network ref self)\end{verbatim} @@ -5130,7 +5629,7 @@ get accessor message derived from field PIFs of object network \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt network ref } & self & object instance \\ \hline +{\tt network ref } & self & reference to the object \\ \hline \end{tabular} @@ -5149,7 +5648,7 @@ value of the field \subsubsection{RPC name:~get\_default\_gateway} {\bf Overview:} -get accessor message derived from field default\_gateway of object network +Get the default\_gateway field of the given network. \noindent {\bf Signature:} \begin{verbatim} string get_default_gateway (session_id s, network ref self)\end{verbatim} @@ -5162,7 +5661,7 @@ get accessor message derived from field default\_gateway of object network \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt network ref } & self & object instance \\ \hline +{\tt network ref } & self & reference to the object \\ \hline \end{tabular} @@ -5181,7 +5680,7 @@ value of the field \subsubsection{RPC name:~set\_default\_gateway} {\bf Overview:} -set accessor message derived from field default\_gateway of object network +Set the default\_gateway field of the given network. \noindent {\bf Signature:} \begin{verbatim} void set_default_gateway (session_id s, network ref self, string value)\end{verbatim} @@ -5194,7 +5693,7 @@ set accessor message derived from field default\_gateway of object network \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt network ref } & self & object instance \\ \hline +{\tt network ref } & self & reference to the object \\ \hline {\tt string } & value & New value to set \\ \hline @@ -5215,7 +5714,7 @@ void \subsubsection{RPC name:~get\_default\_netmask} {\bf Overview:} -get accessor message derived from field default\_netmask of object network +Get the default\_netmask field of the given network. \noindent {\bf Signature:} \begin{verbatim} string get_default_netmask (session_id s, network ref self)\end{verbatim} @@ -5228,7 +5727,7 @@ get accessor message derived from field default\_netmask of object network \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt network ref } & self & object instance \\ \hline +{\tt network ref } & self & reference to the object \\ \hline \end{tabular} @@ -5247,7 +5746,7 @@ value of the field \subsubsection{RPC name:~set\_default\_netmask} {\bf Overview:} -set accessor message derived from field default\_netmask of object network +Set the default\_netmask field of the given network. \noindent {\bf Signature:} \begin{verbatim} void set_default_netmask (session_id s, network ref self, string value)\end{verbatim} @@ -5260,7 +5759,7 @@ set accessor message derived from field default\_netmask of object network \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt network ref } & self & object instance \\ \hline +{\tt network ref } & self & reference to the object \\ \hline {\tt string } & value & New value to set \\ \hline @@ -5281,7 +5780,7 @@ void \subsubsection{RPC name:~create} {\bf Overview:} -constructor for class network +Create a new network instance, and return its handle. \noindent {\bf Signature:} \begin{verbatim} (network ref) create (session_id s, network record args)\end{verbatim} @@ -5313,7 +5812,7 @@ reference to the newly created object \subsubsection{RPC name:~destroy} {\bf Overview:} -destructor for class network +Destroy the specified network instance. \noindent {\bf Signature:} \begin{verbatim} void destroy (session_id s, network ref self)\end{verbatim} @@ -5326,7 +5825,7 @@ destructor for class network \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt network ref } & self & object instance \\ \hline +{\tt network ref } & self & reference to the object \\ \hline \end{tabular} @@ -5345,7 +5844,7 @@ void \subsubsection{RPC name:~get\_by\_uuid} {\bf Overview:} -returns the network instance with a particular uuid +Get a reference to the network instance with the specified UUID. \noindent {\bf Signature:} \begin{verbatim} (network ref) get_by_uuid (session_id s, string uuid)\end{verbatim} @@ -5377,7 +5876,7 @@ reference to the object \subsubsection{RPC name:~get\_record} {\bf Overview:} -returns a record containing the state of an instance of class network +Get a record containing the current state of the given network. \noindent {\bf Signature:} \begin{verbatim} (network record) get_record (session_id s, network ref self)\end{verbatim} @@ -5409,7 +5908,7 @@ all fields from the object \subsubsection{RPC name:~get\_by\_name\_label} {\bf Overview:} -returns the network instance with a particular name label +Get all the network instances with the given label. \noindent {\bf Signature:} \begin{verbatim} ((network ref) Set) get_by_name_label (session_id s, string label)\end{verbatim} @@ -5466,7 +5965,7 @@ $\mathit{RO}_\mathit{run}$ & {\tt io/write\_kbs} & float & Write bandwidth (KiB \subsubsection{RPC name:~get\_uuid} {\bf Overview:} -get accessor message derived from field uuid of object VIF +Get the uuid field of the given VIF. \noindent {\bf Signature:} \begin{verbatim} string get_uuid (session_id s, VIF ref self)\end{verbatim} @@ -5479,7 +5978,7 @@ get accessor message derived from field uuid of object VIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VIF ref } & self & object instance \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline \end{tabular} @@ -5498,7 +5997,7 @@ value of the field \subsubsection{RPC name:~get\_name} {\bf Overview:} -get accessor message derived from field name of object VIF +Get the name field of the given VIF. \noindent {\bf Signature:} \begin{verbatim} string get_name (session_id s, VIF ref self)\end{verbatim} @@ -5511,7 +6010,7 @@ get accessor message derived from field name of object VIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VIF ref } & self & object instance \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline \end{tabular} @@ -5530,7 +6029,7 @@ value of the field \subsubsection{RPC name:~set\_name} {\bf Overview:} -set accessor message derived from field name of object VIF +Set the name field of the given VIF. \noindent {\bf Signature:} \begin{verbatim} void set_name (session_id s, VIF ref self, string value)\end{verbatim} @@ -5543,7 +6042,7 @@ set accessor message derived from field name of object VIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VIF ref } & self & object instance \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline {\tt string } & value & New value to set \\ \hline @@ -5564,7 +6063,7 @@ void \subsubsection{RPC name:~get\_type} {\bf Overview:} -get accessor message derived from field type of object VIF +Get the type field of the given VIF. \noindent {\bf Signature:} \begin{verbatim} (driver_type) get_type (session_id s, VIF ref self)\end{verbatim} @@ -5577,7 +6076,7 @@ get accessor message derived from field type of object VIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VIF ref } & self & object instance \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline \end{tabular} @@ -5596,7 +6095,7 @@ value of the field \subsubsection{RPC name:~set\_type} {\bf Overview:} -set accessor message derived from field type of object VIF +Set the type field of the given VIF. \noindent {\bf Signature:} \begin{verbatim} void set_type (session_id s, VIF ref self, driver_type value)\end{verbatim} @@ -5609,7 +6108,7 @@ set accessor message derived from field type of object VIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VIF ref } & self & object instance \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline {\tt driver\_type } & value & New value to set \\ \hline @@ -5630,7 +6129,7 @@ void \subsubsection{RPC name:~get\_device} {\bf Overview:} -get accessor message derived from field device of object VIF +Get the device field of the given VIF. \noindent {\bf Signature:} \begin{verbatim} string get_device (session_id s, VIF ref self)\end{verbatim} @@ -5643,7 +6142,7 @@ get accessor message derived from field device of object VIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VIF ref } & self & object instance \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline \end{tabular} @@ -5662,7 +6161,7 @@ value of the field \subsubsection{RPC name:~set\_device} {\bf Overview:} -set accessor message derived from field device of object VIF +Set the device field of the given VIF. \noindent {\bf Signature:} \begin{verbatim} void set_device (session_id s, VIF ref self, string value)\end{verbatim} @@ -5675,7 +6174,7 @@ set accessor message derived from field device of object VIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VIF ref } & self & object instance \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline {\tt string } & value & New value to set \\ \hline @@ -5696,7 +6195,7 @@ void \subsubsection{RPC name:~get\_network} {\bf Overview:} -get accessor message derived from field network of object VIF +Get the network field of the given VIF. \noindent {\bf Signature:} \begin{verbatim} (network ref) get_network (session_id s, VIF ref self)\end{verbatim} @@ -5709,7 +6208,7 @@ get accessor message derived from field network of object VIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VIF ref } & self & object instance \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline \end{tabular} @@ -5728,7 +6227,7 @@ value of the field \subsubsection{RPC name:~set\_network} {\bf Overview:} -set accessor message derived from field network of object VIF +Set the network field of the given VIF. \noindent {\bf Signature:} \begin{verbatim} void set_network (session_id s, VIF ref self, network ref value)\end{verbatim} @@ -5741,7 +6240,7 @@ set accessor message derived from field network of object VIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VIF ref } & self & object instance \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline {\tt network ref } & value & New value to set \\ \hline @@ -5762,7 +6261,7 @@ void \subsubsection{RPC name:~get\_VM} {\bf Overview:} -get accessor message derived from field VM of object VIF +Get the VM field of the given VIF. \noindent {\bf Signature:} \begin{verbatim} (VM ref) get_VM (session_id s, VIF ref self)\end{verbatim} @@ -5775,7 +6274,7 @@ get accessor message derived from field VM of object VIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VIF ref } & self & object instance \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline \end{tabular} @@ -5794,7 +6293,7 @@ value of the field \subsubsection{RPC name:~set\_VM} {\bf Overview:} -set accessor message derived from field VM of object VIF +Set the VM field of the given VIF. \noindent {\bf Signature:} \begin{verbatim} void set_VM (session_id s, VIF ref self, VM ref value)\end{verbatim} @@ -5807,7 +6306,7 @@ set accessor message derived from field VM of object VIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VIF ref } & self & object instance \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline {\tt VM ref } & value & New value to set \\ \hline @@ -5828,7 +6327,7 @@ void \subsubsection{RPC name:~get\_MAC} {\bf Overview:} -get accessor message derived from field MAC of object VIF +Get the MAC field of the given VIF. \noindent {\bf Signature:} \begin{verbatim} string get_MAC (session_id s, VIF ref self)\end{verbatim} @@ -5841,7 +6340,7 @@ get accessor message derived from field MAC of object VIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VIF ref } & self & object instance \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline \end{tabular} @@ -5860,7 +6359,7 @@ value of the field \subsubsection{RPC name:~set\_MAC} {\bf Overview:} -set accessor message derived from field MAC of object VIF +Set the MAC field of the given VIF. \noindent {\bf Signature:} \begin{verbatim} void set_MAC (session_id s, VIF ref self, string value)\end{verbatim} @@ -5873,7 +6372,7 @@ set accessor message derived from field MAC of object VIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VIF ref } & self & object instance \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline {\tt string } & value & New value to set \\ \hline @@ -5894,7 +6393,7 @@ void \subsubsection{RPC name:~get\_MTU} {\bf Overview:} -get accessor message derived from field MTU of object VIF +Get the MTU field of the given VIF. \noindent {\bf Signature:} \begin{verbatim} int get_MTU (session_id s, VIF ref self)\end{verbatim} @@ -5907,7 +6406,7 @@ get accessor message derived from field MTU of object VIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VIF ref } & self & object instance \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline \end{tabular} @@ -5926,7 +6425,7 @@ value of the field \subsubsection{RPC name:~set\_MTU} {\bf Overview:} -set accessor message derived from field MTU of object VIF +Set the MTU field of the given VIF. \noindent {\bf Signature:} \begin{verbatim} void set_MTU (session_id s, VIF ref self, int value)\end{verbatim} @@ -5939,7 +6438,7 @@ set accessor message derived from field MTU of object VIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VIF ref } & self & object instance \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline {\tt int } & value & New value to set \\ \hline @@ -5960,7 +6459,7 @@ void \subsubsection{RPC name:~get\_io\_read\_kbs} {\bf Overview:} -get accessor message derived from field io/read\_kbs of object VIF +Get the io/read\_kbs field of the given VIF. \noindent {\bf Signature:} \begin{verbatim} float get_io_read_kbs (session_id s, VIF ref self)\end{verbatim} @@ -5973,7 +6472,7 @@ get accessor message derived from field io/read\_kbs of object VIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VIF ref } & self & object instance \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline \end{tabular} @@ -5992,7 +6491,7 @@ value of the field \subsubsection{RPC name:~get\_io\_write\_kbs} {\bf Overview:} -get accessor message derived from field io/write\_kbs of object VIF +Get the io/write\_kbs field of the given VIF. \noindent {\bf Signature:} \begin{verbatim} float get_io_write_kbs (session_id s, VIF ref self)\end{verbatim} @@ -6005,7 +6504,7 @@ get accessor message derived from field io/write\_kbs of object VIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VIF ref } & self & object instance \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline \end{tabular} @@ -6024,7 +6523,7 @@ value of the field \subsubsection{RPC name:~create} {\bf Overview:} -constructor for class VIF +Create a new VIF instance, and return its handle. \noindent {\bf Signature:} \begin{verbatim} (VIF ref) create (session_id s, VIF record args)\end{verbatim} @@ -6056,7 +6555,7 @@ reference to the newly created object \subsubsection{RPC name:~destroy} {\bf Overview:} -destructor for class VIF +Destroy the specified VIF instance. \noindent {\bf Signature:} \begin{verbatim} void destroy (session_id s, VIF ref self)\end{verbatim} @@ -6069,7 +6568,7 @@ destructor for class VIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VIF ref } & self & object instance \\ \hline +{\tt VIF ref } & self & reference to the object \\ \hline \end{tabular} @@ -6088,7 +6587,7 @@ void \subsubsection{RPC name:~get\_by\_uuid} {\bf Overview:} -returns the VIF instance with a particular uuid +Get a reference to the VIF instance with the specified UUID. \noindent {\bf Signature:} \begin{verbatim} (VIF ref) get_by_uuid (session_id s, string uuid)\end{verbatim} @@ -6120,7 +6619,7 @@ reference to the object \subsubsection{RPC name:~get\_record} {\bf Overview:} -returns a record containing the state of an instance of class VIF +Get a record containing the current state of the given VIF. \noindent {\bf Signature:} \begin{verbatim} (VIF record) get_record (session_id s, VIF ref self)\end{verbatim} @@ -6176,7 +6675,7 @@ $\mathit{RO}_\mathit{run}$ & {\tt io/write\_kbs} & float & Write bandwidth (KiB \subsubsection{RPC name:~get\_uuid} {\bf Overview:} -get accessor message derived from field uuid of object PIF +Get the uuid field of the given PIF. \noindent {\bf Signature:} \begin{verbatim} string get_uuid (session_id s, PIF ref self)\end{verbatim} @@ -6189,7 +6688,7 @@ get accessor message derived from field uuid of object PIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt PIF ref } & self & object instance \\ \hline +{\tt PIF ref } & self & reference to the object \\ \hline \end{tabular} @@ -6208,7 +6707,7 @@ value of the field \subsubsection{RPC name:~get\_name} {\bf Overview:} -get accessor message derived from field name of object PIF +Get the name field of the given PIF. \noindent {\bf Signature:} \begin{verbatim} string get_name (session_id s, PIF ref self)\end{verbatim} @@ -6221,7 +6720,7 @@ get accessor message derived from field name of object PIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt PIF ref } & self & object instance \\ \hline +{\tt PIF ref } & self & reference to the object \\ \hline \end{tabular} @@ -6240,7 +6739,7 @@ value of the field \subsubsection{RPC name:~set\_name} {\bf Overview:} -set accessor message derived from field name of object PIF +Set the name field of the given PIF. \noindent {\bf Signature:} \begin{verbatim} void set_name (session_id s, PIF ref self, string value)\end{verbatim} @@ -6253,7 +6752,7 @@ set accessor message derived from field name of object PIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt PIF ref } & self & object instance \\ \hline +{\tt PIF ref } & self & reference to the object \\ \hline {\tt string } & value & New value to set \\ \hline @@ -6274,7 +6773,7 @@ void \subsubsection{RPC name:~get\_network} {\bf Overview:} -get accessor message derived from field network of object PIF +Get the network field of the given PIF. \noindent {\bf Signature:} \begin{verbatim} (network ref) get_network (session_id s, PIF ref self)\end{verbatim} @@ -6287,7 +6786,7 @@ get accessor message derived from field network of object PIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt PIF ref } & self & object instance \\ \hline +{\tt PIF ref } & self & reference to the object \\ \hline \end{tabular} @@ -6306,7 +6805,7 @@ value of the field \subsubsection{RPC name:~set\_network} {\bf Overview:} -set accessor message derived from field network of object PIF +Set the network field of the given PIF. \noindent {\bf Signature:} \begin{verbatim} void set_network (session_id s, PIF ref self, network ref value)\end{verbatim} @@ -6319,7 +6818,7 @@ set accessor message derived from field network of object PIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt PIF ref } & self & object instance \\ \hline +{\tt PIF ref } & self & reference to the object \\ \hline {\tt network ref } & value & New value to set \\ \hline @@ -6340,7 +6839,7 @@ void \subsubsection{RPC name:~get\_host} {\bf Overview:} -get accessor message derived from field host of object PIF +Get the host field of the given PIF. \noindent {\bf Signature:} \begin{verbatim} (host ref) get_host (session_id s, PIF ref self)\end{verbatim} @@ -6353,7 +6852,7 @@ get accessor message derived from field host of object PIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt PIF ref } & self & object instance \\ \hline +{\tt PIF ref } & self & reference to the object \\ \hline \end{tabular} @@ -6372,7 +6871,7 @@ value of the field \subsubsection{RPC name:~set\_host} {\bf Overview:} -set accessor message derived from field host of object PIF +Set the host field of the given PIF. \noindent {\bf Signature:} \begin{verbatim} void set_host (session_id s, PIF ref self, host ref value)\end{verbatim} @@ -6385,7 +6884,7 @@ set accessor message derived from field host of object PIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt PIF ref } & self & object instance \\ \hline +{\tt PIF ref } & self & reference to the object \\ \hline {\tt host ref } & value & New value to set \\ \hline @@ -6406,7 +6905,7 @@ void \subsubsection{RPC name:~get\_MAC} {\bf Overview:} -get accessor message derived from field MAC of object PIF +Get the MAC field of the given PIF. \noindent {\bf Signature:} \begin{verbatim} string get_MAC (session_id s, PIF ref self)\end{verbatim} @@ -6419,7 +6918,7 @@ get accessor message derived from field MAC of object PIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt PIF ref } & self & object instance \\ \hline +{\tt PIF ref } & self & reference to the object \\ \hline \end{tabular} @@ -6438,7 +6937,7 @@ value of the field \subsubsection{RPC name:~set\_MAC} {\bf Overview:} -set accessor message derived from field MAC of object PIF +Set the MAC field of the given PIF. \noindent {\bf Signature:} \begin{verbatim} void set_MAC (session_id s, PIF ref self, string value)\end{verbatim} @@ -6451,7 +6950,7 @@ set accessor message derived from field MAC of object PIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt PIF ref } & self & object instance \\ \hline +{\tt PIF ref } & self & reference to the object \\ \hline {\tt string } & value & New value to set \\ \hline @@ -6472,7 +6971,7 @@ void \subsubsection{RPC name:~get\_MTU} {\bf Overview:} -get accessor message derived from field MTU of object PIF +Get the MTU field of the given PIF. \noindent {\bf Signature:} \begin{verbatim} int get_MTU (session_id s, PIF ref self)\end{verbatim} @@ -6485,7 +6984,7 @@ get accessor message derived from field MTU of object PIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt PIF ref } & self & object instance \\ \hline +{\tt PIF ref } & self & reference to the object \\ \hline \end{tabular} @@ -6504,7 +7003,7 @@ value of the field \subsubsection{RPC name:~set\_MTU} {\bf Overview:} -set accessor message derived from field MTU of object PIF +Set the MTU field of the given PIF. \noindent {\bf Signature:} \begin{verbatim} void set_MTU (session_id s, PIF ref self, int value)\end{verbatim} @@ -6517,7 +7016,7 @@ set accessor message derived from field MTU of object PIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt PIF ref } & self & object instance \\ \hline +{\tt PIF ref } & self & reference to the object \\ \hline {\tt int } & value & New value to set \\ \hline @@ -6538,7 +7037,7 @@ void \subsubsection{RPC name:~get\_VLAN} {\bf Overview:} -get accessor message derived from field VLAN of object PIF +Get the VLAN field of the given PIF. \noindent {\bf Signature:} \begin{verbatim} string get_VLAN (session_id s, PIF ref self)\end{verbatim} @@ -6551,7 +7050,7 @@ get accessor message derived from field VLAN of object PIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt PIF ref } & self & object instance \\ \hline +{\tt PIF ref } & self & reference to the object \\ \hline \end{tabular} @@ -6570,7 +7069,7 @@ value of the field \subsubsection{RPC name:~set\_VLAN} {\bf Overview:} -set accessor message derived from field VLAN of object PIF +Set the VLAN field of the given PIF. \noindent {\bf Signature:} \begin{verbatim} void set_VLAN (session_id s, PIF ref self, string value)\end{verbatim} @@ -6583,7 +7082,7 @@ set accessor message derived from field VLAN of object PIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt PIF ref } & self & object instance \\ \hline +{\tt PIF ref } & self & reference to the object \\ \hline {\tt string } & value & New value to set \\ \hline @@ -6604,7 +7103,7 @@ void \subsubsection{RPC name:~get\_io\_read\_kbs} {\bf Overview:} -get accessor message derived from field io/read\_kbs of object PIF +Get the io/read\_kbs field of the given PIF. \noindent {\bf Signature:} \begin{verbatim} float get_io_read_kbs (session_id s, PIF ref self)\end{verbatim} @@ -6617,7 +7116,7 @@ get accessor message derived from field io/read\_kbs of object PIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt PIF ref } & self & object instance \\ \hline +{\tt PIF ref } & self & reference to the object \\ \hline \end{tabular} @@ -6636,7 +7135,7 @@ value of the field \subsubsection{RPC name:~get\_io\_write\_kbs} {\bf Overview:} -get accessor message derived from field io/write\_kbs of object PIF +Get the io/write\_kbs field of the given PIF. \noindent {\bf Signature:} \begin{verbatim} float get_io_write_kbs (session_id s, PIF ref self)\end{verbatim} @@ -6649,7 +7148,7 @@ get accessor message derived from field io/write\_kbs of object PIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt PIF ref } & self & object instance \\ \hline +{\tt PIF ref } & self & reference to the object \\ \hline \end{tabular} @@ -6668,7 +7167,7 @@ value of the field \subsubsection{RPC name:~create} {\bf Overview:} -constructor for class PIF +Create a new PIF instance, and return its handle. \noindent {\bf Signature:} \begin{verbatim} (PIF ref) create (session_id s, PIF record args)\end{verbatim} @@ -6700,7 +7199,7 @@ reference to the newly created object \subsubsection{RPC name:~destroy} {\bf Overview:} -destructor for class PIF +Destroy the specified PIF instance. \noindent {\bf Signature:} \begin{verbatim} void destroy (session_id s, PIF ref self)\end{verbatim} @@ -6713,7 +7212,7 @@ destructor for class PIF \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt PIF ref } & self & object instance \\ \hline +{\tt PIF ref } & self & reference to the object \\ \hline \end{tabular} @@ -6732,7 +7231,7 @@ void \subsubsection{RPC name:~get\_by\_uuid} {\bf Overview:} -returns the PIF instance with a particular uuid +Get a reference to the PIF instance with the specified UUID. \noindent {\bf Signature:} \begin{verbatim} (PIF ref) get_by_uuid (session_id s, string uuid)\end{verbatim} @@ -6764,7 +7263,7 @@ reference to the object \subsubsection{RPC name:~get\_record} {\bf Overview:} -returns a record containing the state of an instance of class PIF +Get a record containing the current state of the given PIF. \noindent {\bf Signature:} \begin{verbatim} (PIF record) get_record (session_id s, PIF ref self)\end{verbatim} @@ -6878,7 +7377,7 @@ A list of all the IDs of all the Storage Repositories \subsubsection{RPC name:~get\_uuid} {\bf Overview:} -get accessor message derived from field uuid of object SR +Get the uuid field of the given SR. \noindent {\bf Signature:} \begin{verbatim} string get_uuid (session_id s, SR ref self)\end{verbatim} @@ -6891,7 +7390,7 @@ get accessor message derived from field uuid of object SR \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt SR ref } & self & object instance \\ \hline +{\tt SR ref } & self & reference to the object \\ \hline \end{tabular} @@ -6910,7 +7409,7 @@ value of the field \subsubsection{RPC name:~get\_name\_label} {\bf Overview:} -get accessor message derived from field name/label of object SR +Get the name/label field of the given SR. \noindent {\bf Signature:} \begin{verbatim} string get_name_label (session_id s, SR ref self)\end{verbatim} @@ -6923,7 +7422,7 @@ get accessor message derived from field name/label of object SR \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt SR ref } & self & object instance \\ \hline +{\tt SR ref } & self & reference to the object \\ \hline \end{tabular} @@ -6942,7 +7441,7 @@ value of the field \subsubsection{RPC name:~set\_name\_label} {\bf Overview:} -set accessor message derived from field name/label of object SR +Set the name/label field of the given SR. \noindent {\bf Signature:} \begin{verbatim} void set_name_label (session_id s, SR ref self, string value)\end{verbatim} @@ -6955,7 +7454,7 @@ set accessor message derived from field name/label of object SR \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt SR ref } & self & object instance \\ \hline +{\tt SR ref } & self & reference to the object \\ \hline {\tt string } & value & New value to set \\ \hline @@ -6976,7 +7475,7 @@ void \subsubsection{RPC name:~get\_name\_description} {\bf Overview:} -get accessor message derived from field name/description of object SR +Get the name/description field of the given SR. \noindent {\bf Signature:} \begin{verbatim} string get_name_description (session_id s, SR ref self)\end{verbatim} @@ -6989,7 +7488,7 @@ get accessor message derived from field name/description of object SR \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt SR ref } & self & object instance \\ \hline +{\tt SR ref } & self & reference to the object \\ \hline \end{tabular} @@ -7008,7 +7507,7 @@ value of the field \subsubsection{RPC name:~set\_name\_description} {\bf Overview:} -set accessor message derived from field name/description of object SR +Set the name/description field of the given SR. \noindent {\bf Signature:} \begin{verbatim} void set_name_description (session_id s, SR ref self, string value)\end{verbatim} @@ -7021,7 +7520,7 @@ set accessor message derived from field name/description of object SR \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt SR ref } & self & object instance \\ \hline +{\tt SR ref } & self & reference to the object \\ \hline {\tt string } & value & New value to set \\ \hline @@ -7042,7 +7541,7 @@ void \subsubsection{RPC name:~get\_VDIs} {\bf Overview:} -get accessor message derived from field VDIs of object SR +Get the VDIs field of the given SR. \noindent {\bf Signature:} \begin{verbatim} ((VDI ref) Set) get_VDIs (session_id s, SR ref self)\end{verbatim} @@ -7055,7 +7554,7 @@ get accessor message derived from field VDIs of object SR \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt SR ref } & self & object instance \\ \hline +{\tt SR ref } & self & reference to the object \\ \hline \end{tabular} @@ -7074,7 +7573,7 @@ value of the field \subsubsection{RPC name:~get\_virtual\_allocation} {\bf Overview:} -get accessor message derived from field virtual\_allocation of object SR +Get the virtual\_allocation field of the given SR. \noindent {\bf Signature:} \begin{verbatim} int get_virtual_allocation (session_id s, SR ref self)\end{verbatim} @@ -7087,7 +7586,7 @@ get accessor message derived from field virtual\_allocation of object SR \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt SR ref } & self & object instance \\ \hline +{\tt SR ref } & self & reference to the object \\ \hline \end{tabular} @@ -7106,7 +7605,7 @@ value of the field \subsubsection{RPC name:~get\_physical\_utilisation} {\bf Overview:} -get accessor message derived from field physical\_utilisation of object SR +Get the physical\_utilisation field of the given SR. \noindent {\bf Signature:} \begin{verbatim} int get_physical_utilisation (session_id s, SR ref self)\end{verbatim} @@ -7119,7 +7618,7 @@ get accessor message derived from field physical\_utilisation of object SR \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt SR ref } & self & object instance \\ \hline +{\tt SR ref } & self & reference to the object \\ \hline \end{tabular} @@ -7138,7 +7637,7 @@ value of the field \subsubsection{RPC name:~get\_physical\_size} {\bf Overview:} -get accessor message derived from field physical\_size of object SR +Get the physical\_size field of the given SR. \noindent {\bf Signature:} \begin{verbatim} int get_physical_size (session_id s, SR ref self)\end{verbatim} @@ -7151,7 +7650,7 @@ get accessor message derived from field physical\_size of object SR \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt SR ref } & self & object instance \\ \hline +{\tt SR ref } & self & reference to the object \\ \hline \end{tabular} @@ -7170,7 +7669,7 @@ value of the field \subsubsection{RPC name:~get\_type} {\bf Overview:} -get accessor message derived from field type of object SR +Get the type field of the given SR. \noindent {\bf Signature:} \begin{verbatim} string get_type (session_id s, SR ref self)\end{verbatim} @@ -7183,7 +7682,7 @@ get accessor message derived from field type of object SR \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt SR ref } & self & object instance \\ \hline +{\tt SR ref } & self & reference to the object \\ \hline \end{tabular} @@ -7202,7 +7701,7 @@ value of the field \subsubsection{RPC name:~get\_location} {\bf Overview:} -get accessor message derived from field location of object SR +Get the location field of the given SR. \noindent {\bf Signature:} \begin{verbatim} string get_location (session_id s, SR ref self)\end{verbatim} @@ -7215,7 +7714,7 @@ get accessor message derived from field location of object SR \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt SR ref } & self & object instance \\ \hline +{\tt SR ref } & self & reference to the object \\ \hline \end{tabular} @@ -7234,7 +7733,7 @@ value of the field \subsubsection{RPC name:~create} {\bf Overview:} -constructor for class SR +Create a new SR instance, and return its handle. \noindent {\bf Signature:} \begin{verbatim} (SR ref) create (session_id s, SR record args)\end{verbatim} @@ -7266,7 +7765,7 @@ reference to the newly created object \subsubsection{RPC name:~destroy} {\bf Overview:} -destructor for class SR +Destroy the specified SR instance. \noindent {\bf Signature:} \begin{verbatim} void destroy (session_id s, SR ref self)\end{verbatim} @@ -7279,7 +7778,7 @@ destructor for class SR \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt SR ref } & self & object instance \\ \hline +{\tt SR ref } & self & reference to the object \\ \hline \end{tabular} @@ -7298,7 +7797,7 @@ void \subsubsection{RPC name:~get\_by\_uuid} {\bf Overview:} -returns the SR instance with a particular uuid +Get a reference to the SR instance with the specified UUID. \noindent {\bf Signature:} \begin{verbatim} (SR ref) get_by_uuid (session_id s, string uuid)\end{verbatim} @@ -7330,7 +7829,7 @@ reference to the object \subsubsection{RPC name:~get\_record} {\bf Overview:} -returns a record containing the state of an instance of class SR +Get a record containing the current state of the given SR. \noindent {\bf Signature:} \begin{verbatim} (SR record) get_record (session_id s, SR ref self)\end{verbatim} @@ -7362,7 +7861,7 @@ all fields from the object \subsubsection{RPC name:~get\_by\_name\_label} {\bf Overview:} -returns the SR instance with a particular name label +Get all the SR instances with the given label. \noindent {\bf Signature:} \begin{verbatim} ((SR ref) Set) get_by_name_label (session_id s, string label)\end{verbatim} @@ -7488,7 +7987,7 @@ void \subsubsection{RPC name:~get\_uuid} {\bf Overview:} -get accessor message derived from field uuid of object VDI +Get the uuid field of the given VDI. \noindent {\bf Signature:} \begin{verbatim} string get_uuid (session_id s, VDI ref self)\end{verbatim} @@ -7501,7 +8000,7 @@ get accessor message derived from field uuid of object VDI \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VDI ref } & self & object instance \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline \end{tabular} @@ -7520,7 +8019,7 @@ value of the field \subsubsection{RPC name:~get\_name\_label} {\bf Overview:} -get accessor message derived from field name/label of object VDI +Get the name/label field of the given VDI. \noindent {\bf Signature:} \begin{verbatim} string get_name_label (session_id s, VDI ref self)\end{verbatim} @@ -7533,7 +8032,7 @@ get accessor message derived from field name/label of object VDI \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VDI ref } & self & object instance \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline \end{tabular} @@ -7552,7 +8051,7 @@ value of the field \subsubsection{RPC name:~set\_name\_label} {\bf Overview:} -set accessor message derived from field name/label of object VDI +Set the name/label field of the given VDI. \noindent {\bf Signature:} \begin{verbatim} void set_name_label (session_id s, VDI ref self, string value)\end{verbatim} @@ -7565,7 +8064,7 @@ set accessor message derived from field name/label of object VDI \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VDI ref } & self & object instance \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline {\tt string } & value & New value to set \\ \hline @@ -7586,7 +8085,7 @@ void \subsubsection{RPC name:~get\_name\_description} {\bf Overview:} -get accessor message derived from field name/description of object VDI +Get the name/description field of the given VDI. \noindent {\bf Signature:} \begin{verbatim} string get_name_description (session_id s, VDI ref self)\end{verbatim} @@ -7599,7 +8098,7 @@ get accessor message derived from field name/description of object VDI \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VDI ref } & self & object instance \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline \end{tabular} @@ -7618,7 +8117,7 @@ value of the field \subsubsection{RPC name:~set\_name\_description} {\bf Overview:} -set accessor message derived from field name/description of object VDI +Set the name/description field of the given VDI. \noindent {\bf Signature:} \begin{verbatim} void set_name_description (session_id s, VDI ref self, string value)\end{verbatim} @@ -7631,7 +8130,7 @@ set accessor message derived from field name/description of object VDI \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VDI ref } & self & object instance \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline {\tt string } & value & New value to set \\ \hline @@ -7652,7 +8151,7 @@ void \subsubsection{RPC name:~get\_SR} {\bf Overview:} -get accessor message derived from field SR of object VDI +Get the SR field of the given VDI. \noindent {\bf Signature:} \begin{verbatim} (SR ref) get_SR (session_id s, VDI ref self)\end{verbatim} @@ -7665,7 +8164,7 @@ get accessor message derived from field SR of object VDI \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VDI ref } & self & object instance \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline \end{tabular} @@ -7684,7 +8183,7 @@ value of the field \subsubsection{RPC name:~set\_SR} {\bf Overview:} -set accessor message derived from field SR of object VDI +Set the SR field of the given VDI. \noindent {\bf Signature:} \begin{verbatim} void set_SR (session_id s, VDI ref self, SR ref value)\end{verbatim} @@ -7697,7 +8196,7 @@ set accessor message derived from field SR of object VDI \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VDI ref } & self & object instance \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline {\tt SR ref } & value & New value to set \\ \hline @@ -7718,7 +8217,7 @@ void \subsubsection{RPC name:~get\_VBDs} {\bf Overview:} -get accessor message derived from field VBDs of object VDI +Get the VBDs field of the given VDI. \noindent {\bf Signature:} \begin{verbatim} ((VBD ref) Set) get_VBDs (session_id s, VDI ref self)\end{verbatim} @@ -7731,7 +8230,7 @@ get accessor message derived from field VBDs of object VDI \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VDI ref } & self & object instance \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline \end{tabular} @@ -7750,7 +8249,7 @@ value of the field \subsubsection{RPC name:~get\_virtual\_size} {\bf Overview:} -get accessor message derived from field virtual\_size of object VDI +Get the virtual\_size field of the given VDI. \noindent {\bf Signature:} \begin{verbatim} int get_virtual_size (session_id s, VDI ref self)\end{verbatim} @@ -7763,7 +8262,7 @@ get accessor message derived from field virtual\_size of object VDI \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VDI ref } & self & object instance \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline \end{tabular} @@ -7782,7 +8281,7 @@ value of the field \subsubsection{RPC name:~set\_virtual\_size} {\bf Overview:} -set accessor message derived from field virtual\_size of object VDI +Set the virtual\_size field of the given VDI. \noindent {\bf Signature:} \begin{verbatim} void set_virtual_size (session_id s, VDI ref self, int value)\end{verbatim} @@ -7795,7 +8294,7 @@ set accessor message derived from field virtual\_size of object VDI \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VDI ref } & self & object instance \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline {\tt int } & value & New value to set \\ \hline @@ -7816,7 +8315,7 @@ void \subsubsection{RPC name:~get\_physical\_utilisation} {\bf Overview:} -get accessor message derived from field physical\_utilisation of object VDI +Get the physical\_utilisation field of the given VDI. \noindent {\bf Signature:} \begin{verbatim} int get_physical_utilisation (session_id s, VDI ref self)\end{verbatim} @@ -7829,7 +8328,7 @@ get accessor message derived from field physical\_utilisation of object VDI \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VDI ref } & self & object instance \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline \end{tabular} @@ -7848,7 +8347,7 @@ value of the field \subsubsection{RPC name:~get\_sector\_size} {\bf Overview:} -get accessor message derived from field sector\_size of object VDI +Get the sector\_size field of the given VDI. \noindent {\bf Signature:} \begin{verbatim} int get_sector_size (session_id s, VDI ref self)\end{verbatim} @@ -7861,7 +8360,7 @@ get accessor message derived from field sector\_size of object VDI \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VDI ref } & self & object instance \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline \end{tabular} @@ -7880,7 +8379,7 @@ value of the field \subsubsection{RPC name:~get\_type} {\bf Overview:} -get accessor message derived from field type of object VDI +Get the type field of the given VDI. \noindent {\bf Signature:} \begin{verbatim} (vdi_type) get_type (session_id s, VDI ref self)\end{verbatim} @@ -7893,7 +8392,7 @@ get accessor message derived from field type of object VDI \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VDI ref } & self & object instance \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline \end{tabular} @@ -7912,7 +8411,7 @@ value of the field \subsubsection{RPC name:~get\_parent} {\bf Overview:} -get accessor message derived from field parent of object VDI +Get the parent field of the given VDI. \noindent {\bf Signature:} \begin{verbatim} (VDI ref) get_parent (session_id s, VDI ref self)\end{verbatim} @@ -7925,7 +8424,7 @@ get accessor message derived from field parent of object VDI \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VDI ref } & self & object instance \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline \end{tabular} @@ -7944,7 +8443,7 @@ value of the field \subsubsection{RPC name:~get\_children} {\bf Overview:} -get accessor message derived from field children of object VDI +Get the children field of the given VDI. \noindent {\bf Signature:} \begin{verbatim} ((VDI ref) Set) get_children (session_id s, VDI ref self)\end{verbatim} @@ -7957,7 +8456,7 @@ get accessor message derived from field children of object VDI \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VDI ref } & self & object instance \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline \end{tabular} @@ -7976,7 +8475,7 @@ value of the field \subsubsection{RPC name:~get\_sharable} {\bf Overview:} -get accessor message derived from field sharable of object VDI +Get the sharable field of the given VDI. \noindent {\bf Signature:} \begin{verbatim} bool get_sharable (session_id s, VDI ref self)\end{verbatim} @@ -7989,7 +8488,7 @@ get accessor message derived from field sharable of object VDI \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VDI ref } & self & object instance \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline \end{tabular} @@ -8008,7 +8507,7 @@ value of the field \subsubsection{RPC name:~set\_sharable} {\bf Overview:} -set accessor message derived from field sharable of object VDI +Set the sharable field of the given VDI. \noindent {\bf Signature:} \begin{verbatim} void set_sharable (session_id s, VDI ref self, bool value)\end{verbatim} @@ -8021,7 +8520,7 @@ set accessor message derived from field sharable of object VDI \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VDI ref } & self & object instance \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline {\tt bool } & value & New value to set \\ \hline @@ -8042,7 +8541,7 @@ void \subsubsection{RPC name:~get\_read\_only} {\bf Overview:} -get accessor message derived from field read\_only of object VDI +Get the read\_only field of the given VDI. \noindent {\bf Signature:} \begin{verbatim} bool get_read_only (session_id s, VDI ref self)\end{verbatim} @@ -8055,7 +8554,7 @@ get accessor message derived from field read\_only of object VDI \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VDI ref } & self & object instance \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline \end{tabular} @@ -8074,7 +8573,7 @@ value of the field \subsubsection{RPC name:~set\_read\_only} {\bf Overview:} -set accessor message derived from field read\_only of object VDI +Set the read\_only field of the given VDI. \noindent {\bf Signature:} \begin{verbatim} void set_read_only (session_id s, VDI ref self, bool value)\end{verbatim} @@ -8087,7 +8586,7 @@ set accessor message derived from field read\_only of object VDI \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VDI ref } & self & object instance \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline {\tt bool } & value & New value to set \\ \hline @@ -8108,7 +8607,7 @@ void \subsubsection{RPC name:~create} {\bf Overview:} -constructor for class VDI +Create a new VDI instance, and return its handle. \noindent {\bf Signature:} \begin{verbatim} (VDI ref) create (session_id s, VDI record args)\end{verbatim} @@ -8140,7 +8639,7 @@ reference to the newly created object \subsubsection{RPC name:~destroy} {\bf Overview:} -destructor for class VDI +Destroy the specified VDI instance. \noindent {\bf Signature:} \begin{verbatim} void destroy (session_id s, VDI ref self)\end{verbatim} @@ -8153,7 +8652,7 @@ destructor for class VDI \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VDI ref } & self & object instance \\ \hline +{\tt VDI ref } & self & reference to the object \\ \hline \end{tabular} @@ -8172,7 +8671,7 @@ void \subsubsection{RPC name:~get\_by\_uuid} {\bf Overview:} -returns the VDI instance with a particular uuid +Get a reference to the VDI instance with the specified UUID. \noindent {\bf Signature:} \begin{verbatim} (VDI ref) get_by_uuid (session_id s, string uuid)\end{verbatim} @@ -8204,7 +8703,7 @@ reference to the object \subsubsection{RPC name:~get\_record} {\bf Overview:} -returns a record containing the state of an instance of class VDI +Get a record containing the current state of the given VDI. \noindent {\bf Signature:} \begin{verbatim} (VDI record) get_record (session_id s, VDI ref self)\end{verbatim} @@ -8236,7 +8735,7 @@ all fields from the object \subsubsection{RPC name:~get\_by\_name\_label} {\bf Overview:} -returns the VDI instance with a particular name label +Get all the VDI instances with the given label. \noindent {\bf Signature:} \begin{verbatim} ((VDI ref) Set) get_by_name_label (session_id s, string label)\end{verbatim} @@ -8282,6 +8781,7 @@ $\mathit{RW}$ & {\tt VM} & VM ref & the virtual machine \\ $\mathit{RW}$ & {\tt VDI} & VDI ref & the virtual disk \\ $\mathit{RW}$ & {\tt device} & string & device seen by the guest e.g. hda1 \\ $\mathit{RW}$ & {\tt mode} & vbd\_mode & the mode the disk should be mounted with \\ +$\mathit{RW}$ & {\tt type} & vbd\_type & how the VBD will appear to the guest (e.g. disk or CD) \\ $\mathit{RW}$ & {\tt driver} & driver\_type & the style of driver \\ $\mathit{RO}_\mathit{run}$ & {\tt io/read\_kbs} & float & Read bandwidth (KiB/s) \\ $\mathit{RO}_\mathit{run}$ & {\tt io/write\_kbs} & float & Write bandwidth (KiB/s) \\ @@ -8325,7 +8825,7 @@ void \subsubsection{RPC name:~get\_uuid} {\bf Overview:} -get accessor message derived from field uuid of object VBD +Get the uuid field of the given VBD. \noindent {\bf Signature:} \begin{verbatim} string get_uuid (session_id s, VBD ref self)\end{verbatim} @@ -8338,7 +8838,7 @@ get accessor message derived from field uuid of object VBD \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VBD ref } & self & object instance \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline \end{tabular} @@ -8357,7 +8857,7 @@ value of the field \subsubsection{RPC name:~get\_VM} {\bf Overview:} -get accessor message derived from field VM of object VBD +Get the VM field of the given VBD. \noindent {\bf Signature:} \begin{verbatim} (VM ref) get_VM (session_id s, VBD ref self)\end{verbatim} @@ -8370,7 +8870,7 @@ get accessor message derived from field VM of object VBD \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VBD ref } & self & object instance \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline \end{tabular} @@ -8389,7 +8889,7 @@ value of the field \subsubsection{RPC name:~set\_VM} {\bf Overview:} -set accessor message derived from field VM of object VBD +Set the VM field of the given VBD. \noindent {\bf Signature:} \begin{verbatim} void set_VM (session_id s, VBD ref self, VM ref value)\end{verbatim} @@ -8402,7 +8902,7 @@ set accessor message derived from field VM of object VBD \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VBD ref } & self & object instance \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline {\tt VM ref } & value & New value to set \\ \hline @@ -8423,7 +8923,7 @@ void \subsubsection{RPC name:~get\_VDI} {\bf Overview:} -get accessor message derived from field VDI of object VBD +Get the VDI field of the given VBD. \noindent {\bf Signature:} \begin{verbatim} (VDI ref) get_VDI (session_id s, VBD ref self)\end{verbatim} @@ -8436,7 +8936,7 @@ get accessor message derived from field VDI of object VBD \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VBD ref } & self & object instance \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline \end{tabular} @@ -8455,7 +8955,7 @@ value of the field \subsubsection{RPC name:~set\_VDI} {\bf Overview:} -set accessor message derived from field VDI of object VBD +Set the VDI field of the given VBD. \noindent {\bf Signature:} \begin{verbatim} void set_VDI (session_id s, VBD ref self, VDI ref value)\end{verbatim} @@ -8468,7 +8968,7 @@ set accessor message derived from field VDI of object VBD \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VBD ref } & self & object instance \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline {\tt VDI ref } & value & New value to set \\ \hline @@ -8489,7 +8989,7 @@ void \subsubsection{RPC name:~get\_device} {\bf Overview:} -get accessor message derived from field device of object VBD +Get the device field of the given VBD. \noindent {\bf Signature:} \begin{verbatim} string get_device (session_id s, VBD ref self)\end{verbatim} @@ -8502,7 +9002,7 @@ get accessor message derived from field device of object VBD \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VBD ref } & self & object instance \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline \end{tabular} @@ -8521,7 +9021,7 @@ value of the field \subsubsection{RPC name:~set\_device} {\bf Overview:} -set accessor message derived from field device of object VBD +Set the device field of the given VBD. \noindent {\bf Signature:} \begin{verbatim} void set_device (session_id s, VBD ref self, string value)\end{verbatim} @@ -8534,7 +9034,7 @@ set accessor message derived from field device of object VBD \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VBD ref } & self & object instance \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline {\tt string } & value & New value to set \\ \hline @@ -8555,7 +9055,7 @@ void \subsubsection{RPC name:~get\_mode} {\bf Overview:} -get accessor message derived from field mode of object VBD +Get the mode field of the given VBD. \noindent {\bf Signature:} \begin{verbatim} (vbd_mode) get_mode (session_id s, VBD ref self)\end{verbatim} @@ -8568,7 +9068,7 @@ get accessor message derived from field mode of object VBD \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VBD ref } & self & object instance \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline \end{tabular} @@ -8587,7 +9087,7 @@ value of the field \subsubsection{RPC name:~set\_mode} {\bf Overview:} -set accessor message derived from field mode of object VBD +Set the mode field of the given VBD. \noindent {\bf Signature:} \begin{verbatim} void set_mode (session_id s, VBD ref self, vbd_mode value)\end{verbatim} @@ -8600,7 +9100,7 @@ set accessor message derived from field mode of object VBD \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VBD ref } & self & object instance \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline {\tt vbd\_mode } & value & New value to set \\ \hline @@ -8615,13 +9115,79 @@ void +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_type} + +{\bf Overview:} +Get the type field of the given VBD. + + \noindent {\bf Signature:} +\begin{verbatim} (vbd_type) get_type (session_id s, VBD ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +vbd\_type +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~set\_type} + +{\bf Overview:} +Set the type field of the given VBD. + + \noindent {\bf Signature:} +\begin{verbatim} void set_type (session_id s, VBD ref self, vbd_type value)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline + +{\tt vbd\_type } & value & New value to set \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + \vspace{0.3cm} \vspace{0.3cm} \vspace{0.3cm} \subsubsection{RPC name:~get\_driver} {\bf Overview:} -get accessor message derived from field driver of object VBD +Get the driver field of the given VBD. \noindent {\bf Signature:} \begin{verbatim} (driver_type) get_driver (session_id s, VBD ref self)\end{verbatim} @@ -8634,7 +9200,7 @@ get accessor message derived from field driver of object VBD \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VBD ref } & self & object instance \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline \end{tabular} @@ -8653,7 +9219,7 @@ value of the field \subsubsection{RPC name:~set\_driver} {\bf Overview:} -set accessor message derived from field driver of object VBD +Set the driver field of the given VBD. \noindent {\bf Signature:} \begin{verbatim} void set_driver (session_id s, VBD ref self, driver_type value)\end{verbatim} @@ -8666,7 +9232,7 @@ set accessor message derived from field driver of object VBD \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VBD ref } & self & object instance \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline {\tt driver\_type } & value & New value to set \\ \hline @@ -8687,7 +9253,7 @@ void \subsubsection{RPC name:~get\_io\_read\_kbs} {\bf Overview:} -get accessor message derived from field io/read\_kbs of object VBD +Get the io/read\_kbs field of the given VBD. \noindent {\bf Signature:} \begin{verbatim} float get_io_read_kbs (session_id s, VBD ref self)\end{verbatim} @@ -8700,7 +9266,7 @@ get accessor message derived from field io/read\_kbs of object VBD \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VBD ref } & self & object instance \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline \end{tabular} @@ -8719,7 +9285,7 @@ value of the field \subsubsection{RPC name:~get\_io\_write\_kbs} {\bf Overview:} -get accessor message derived from field io/write\_kbs of object VBD +Get the io/write\_kbs field of the given VBD. \noindent {\bf Signature:} \begin{verbatim} float get_io_write_kbs (session_id s, VBD ref self)\end{verbatim} @@ -8732,7 +9298,7 @@ get accessor message derived from field io/write\_kbs of object VBD \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VBD ref } & self & object instance \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline \end{tabular} @@ -8751,7 +9317,7 @@ value of the field \subsubsection{RPC name:~create} {\bf Overview:} -constructor for class VBD +Create a new VBD instance, and return its handle. \noindent {\bf Signature:} \begin{verbatim} (VBD ref) create (session_id s, VBD record args)\end{verbatim} @@ -8783,7 +9349,7 @@ reference to the newly created object \subsubsection{RPC name:~destroy} {\bf Overview:} -destructor for class VBD +Destroy the specified VBD instance. \noindent {\bf Signature:} \begin{verbatim} void destroy (session_id s, VBD ref self)\end{verbatim} @@ -8796,7 +9362,7 @@ destructor for class VBD \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VBD ref } & self & object instance \\ \hline +{\tt VBD ref } & self & reference to the object \\ \hline \end{tabular} @@ -8815,7 +9381,7 @@ void \subsubsection{RPC name:~get\_by\_uuid} {\bf Overview:} -returns the VBD instance with a particular uuid +Get a reference to the VBD instance with the specified UUID. \noindent {\bf Signature:} \begin{verbatim} (VBD ref) get_by_uuid (session_id s, string uuid)\end{verbatim} @@ -8847,7 +9413,7 @@ reference to the object \subsubsection{RPC name:~get\_record} {\bf Overview:} -returns a record containing the state of an instance of class VBD +Get a record containing the current state of the given VBD. \noindent {\bf Signature:} \begin{verbatim} (VBD record) get_record (session_id s, VBD ref self)\end{verbatim} @@ -8899,7 +9465,7 @@ $\mathit{RO}_\mathit{ins}$ & {\tt instance} & int & the instance number the vir \subsubsection{RPC name:~get\_uuid} {\bf Overview:} -get accessor message derived from field uuid of object VTPM +Get the uuid field of the given VTPM. \noindent {\bf Signature:} \begin{verbatim} string get_uuid (session_id s, VTPM ref self)\end{verbatim} @@ -8912,7 +9478,7 @@ get accessor message derived from field uuid of object VTPM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VTPM ref } & self & object instance \\ \hline +{\tt VTPM ref } & self & reference to the object \\ \hline \end{tabular} @@ -8931,7 +9497,7 @@ value of the field \subsubsection{RPC name:~get\_VM} {\bf Overview:} -get accessor message derived from field VM of object VTPM +Get the VM field of the given VTPM. \noindent {\bf Signature:} \begin{verbatim} (VM ref) get_VM (session_id s, VTPM ref self)\end{verbatim} @@ -8944,7 +9510,7 @@ get accessor message derived from field VM of object VTPM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VTPM ref } & self & object instance \\ \hline +{\tt VTPM ref } & self & reference to the object \\ \hline \end{tabular} @@ -8963,7 +9529,7 @@ value of the field \subsubsection{RPC name:~get\_backend} {\bf Overview:} -get accessor message derived from field backend of object VTPM +Get the backend field of the given VTPM. \noindent {\bf Signature:} \begin{verbatim} (VM ref) get_backend (session_id s, VTPM ref self)\end{verbatim} @@ -8976,7 +9542,7 @@ get accessor message derived from field backend of object VTPM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VTPM ref } & self & object instance \\ \hline +{\tt VTPM ref } & self & reference to the object \\ \hline \end{tabular} @@ -8995,7 +9561,7 @@ value of the field \subsubsection{RPC name:~get\_driver} {\bf Overview:} -get accessor message derived from field driver of object VTPM +Get the driver field of the given VTPM. \noindent {\bf Signature:} \begin{verbatim} (driver_type) get_driver (session_id s, VTPM ref self)\end{verbatim} @@ -9008,7 +9574,7 @@ get accessor message derived from field driver of object VTPM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VTPM ref } & self & object instance \\ \hline +{\tt VTPM ref } & self & reference to the object \\ \hline \end{tabular} @@ -9027,7 +9593,7 @@ value of the field \subsubsection{RPC name:~get\_instance} {\bf Overview:} -get accessor message derived from field instance of object VTPM +Get the instance field of the given VTPM. \noindent {\bf Signature:} \begin{verbatim} int get_instance (session_id s, VTPM ref self)\end{verbatim} @@ -9040,7 +9606,7 @@ get accessor message derived from field instance of object VTPM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VTPM ref } & self & object instance \\ \hline +{\tt VTPM ref } & self & reference to the object \\ \hline \end{tabular} @@ -9059,7 +9625,7 @@ value of the field \subsubsection{RPC name:~create} {\bf Overview:} -constructor for class VTPM +Create a new VTPM instance, and return its handle. \noindent {\bf Signature:} \begin{verbatim} (VTPM ref) create (session_id s, VTPM record args)\end{verbatim} @@ -9091,7 +9657,7 @@ reference to the newly created object \subsubsection{RPC name:~destroy} {\bf Overview:} -destructor for class VTPM +Destroy the specified VTPM instance. \noindent {\bf Signature:} \begin{verbatim} void destroy (session_id s, VTPM ref self)\end{verbatim} @@ -9104,7 +9670,7 @@ destructor for class VTPM \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt VTPM ref } & self & object instance \\ \hline +{\tt VTPM ref } & self & reference to the object \\ \hline \end{tabular} @@ -9123,7 +9689,7 @@ void \subsubsection{RPC name:~get\_by\_uuid} {\bf Overview:} -returns the VTPM instance with a particular uuid +Get a reference to the VTPM instance with the specified UUID. \noindent {\bf Signature:} \begin{verbatim} (VTPM ref) get_by_uuid (session_id s, string uuid)\end{verbatim} @@ -9155,7 +9721,7 @@ reference to the object \subsubsection{RPC name:~get\_record} {\bf Overview:} -returns a record containing the state of an instance of class VTPM +Get a record containing the current state of the given VTPM. \noindent {\bf Signature:} \begin{verbatim} (VTPM record) get_record (session_id s, VTPM ref self)\end{verbatim} @@ -9185,6 +9751,281 @@ all fields from the object \vspace{0.3cm} \vspace{0.3cm} +\vspace{1cm} +\newpage +\section{Class: console} +\subsection{Fields for class: console} +\begin{longtable}{|lllp{0.38\textwidth}|} +\hline +\multicolumn{1}{|l}{Name} & \multicolumn{3}{l|}{\bf console} \\ +\multicolumn{1}{|l}{Description} & \multicolumn{3}{l|}{\parbox{11cm}{\em A console}} \\ +\hline +Quals & Field & Type & Description \\ +\hline +$\mathit{RO}_\mathit{run}$ & {\tt uuid} & string & unique identifier/object reference \\ +$\mathit{RO}_\mathit{run}$ & {\tt protocol} & console\_protocol & the protocol used by this console \\ +$\mathit{RO}_\mathit{run}$ & {\tt uri} & string & URI for the console service \\ +$\mathit{RO}_\mathit{run}$ & {\tt VM} & VM ref & VM to which this console is attached \\ +\hline +\end{longtable} +\subsection{Additional RPCs associated with class: console} +\subsubsection{RPC name:~get\_record} + +{\bf Overview:} +Get the current state of the given console. + + \noindent {\bf Signature:} +\begin{verbatim} (console record) get_record (session_id s, console ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt console ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +console record +} + + +all fields from the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_by\_uuid} + +{\bf Overview:} +Get a reference to the object with the specified UUID. + + \noindent {\bf Signature:} +\begin{verbatim} (console ref) get_by_uuid (session_id s, string uuid)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt string } & uuid & UUID of object to return \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +console ref +} + + +reference to the object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~create} + +{\bf Overview:} +Create a new console instance, and return its handle. + + \noindent {\bf Signature:} +\begin{verbatim} (console ref) create (session_id s, console record args)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt console record } & args & All constructor arguments \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +console ref +} + + +reference to the newly created object +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~destroy} + +{\bf Overview:} +Destroy the specified console instance. + + \noindent {\bf Signature:} +\begin{verbatim} void destroy (session_id s, console ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt console ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +void +} + + + +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_uuid} + +{\bf Overview:} +Get the uuid field of the given console. + + \noindent {\bf Signature:} +\begin{verbatim} string get_uuid (session_id s, console ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt console ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_protocol} + +{\bf Overview:} +Get the protocol field of the given console. + + \noindent {\bf Signature:} +\begin{verbatim} (console_protocol) get_protocol (session_id s, console ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt console ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +console\_protocol +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_uri} + +{\bf Overview:} +Get the uri field of the given console. + + \noindent {\bf Signature:} +\begin{verbatim} string get_uri (session_id s, console ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt console ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +string +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} +\subsubsection{RPC name:~get\_VM} + +{\bf Overview:} +Get the VM field of the given console. + + \noindent {\bf Signature:} +\begin{verbatim} (VM ref) get_VM (session_id s, console ref self)\end{verbatim} + + +\noindent{\bf Arguments:} + + +\vspace{0.3cm} +\begin{tabular}{|c|c|p{7cm}|} + \hline +{\bf type} & {\bf name} & {\bf description} \\ \hline +{\tt console ref } & self & reference to the object \\ \hline + +\end{tabular} + +\vspace{0.3cm} + + \noindent {\bf Return Type:} +{\tt +VM ref +} + + +value of the field +\vspace{0.3cm} +\vspace{0.3cm} +\vspace{0.3cm} + \vspace{1cm} \newpage \section{Class: user} @@ -9205,7 +10046,7 @@ $\mathit{RW}$ & {\tt fullname} & string & full name \\ \subsubsection{RPC name:~get\_uuid} {\bf Overview:} -get accessor message derived from field uuid of object user +Get the uuid field of the given user. \noindent {\bf Signature:} \begin{verbatim} string get_uuid (session_id s, user ref self)\end{verbatim} @@ -9218,7 +10059,7 @@ get accessor message derived from field uuid of object user \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt user ref } & self & object instance \\ \hline +{\tt user ref } & self & reference to the object \\ \hline \end{tabular} @@ -9237,7 +10078,7 @@ value of the field \subsubsection{RPC name:~get\_short\_name} {\bf Overview:} -get accessor message derived from field short\_name of object user +Get the short\_name field of the given user. \noindent {\bf Signature:} \begin{verbatim} string get_short_name (session_id s, user ref self)\end{verbatim} @@ -9250,7 +10091,7 @@ get accessor message derived from field short\_name of object user \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt user ref } & self & object instance \\ \hline +{\tt user ref } & self & reference to the object \\ \hline \end{tabular} @@ -9269,7 +10110,7 @@ value of the field \subsubsection{RPC name:~get\_fullname} {\bf Overview:} -get accessor message derived from field fullname of object user +Get the fullname field of the given user. \noindent {\bf Signature:} \begin{verbatim} string get_fullname (session_id s, user ref self)\end{verbatim} @@ -9282,7 +10123,7 @@ get accessor message derived from field fullname of object user \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt user ref } & self & object instance \\ \hline +{\tt user ref } & self & reference to the object \\ \hline \end{tabular} @@ -9301,7 +10142,7 @@ value of the field \subsubsection{RPC name:~set\_fullname} {\bf Overview:} -set accessor message derived from field fullname of object user +Set the fullname field of the given user. \noindent {\bf Signature:} \begin{verbatim} void set_fullname (session_id s, user ref self, string value)\end{verbatim} @@ -9314,7 +10155,7 @@ set accessor message derived from field fullname of object user \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt user ref } & self & object instance \\ \hline +{\tt user ref } & self & reference to the object \\ \hline {\tt string } & value & New value to set \\ \hline @@ -9335,7 +10176,7 @@ void \subsubsection{RPC name:~create} {\bf Overview:} -constructor for class user +Create a new user instance, and return its handle. \noindent {\bf Signature:} \begin{verbatim} (user ref) create (session_id s, user record args)\end{verbatim} @@ -9367,7 +10208,7 @@ reference to the newly created object \subsubsection{RPC name:~destroy} {\bf Overview:} -destructor for class user +Destroy the specified user instance. \noindent {\bf Signature:} \begin{verbatim} void destroy (session_id s, user ref self)\end{verbatim} @@ -9380,7 +10221,7 @@ destructor for class user \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt user ref } & self & object instance \\ \hline +{\tt user ref } & self & reference to the object \\ \hline \end{tabular} @@ -9399,7 +10240,7 @@ void \subsubsection{RPC name:~get\_by\_uuid} {\bf Overview:} -returns the user instance with a particular uuid +Get a reference to the user instance with the specified UUID. \noindent {\bf Signature:} \begin{verbatim} (user ref) get_by_uuid (session_id s, string uuid)\end{verbatim} @@ -9431,7 +10272,7 @@ reference to the object \subsubsection{RPC name:~get\_record} {\bf Overview:} -returns a record containing the state of an instance of class user +Get a record containing the current state of the given user. \noindent {\bf Signature:} \begin{verbatim} (user record) get_record (session_id s, user ref self)\end{verbatim} @@ -9512,7 +10353,7 @@ void \subsubsection{RPC name:~create} {\bf Overview:} -constructor for class debug +Create a new debug instance, and return its handle. \noindent {\bf Signature:} \begin{verbatim} (debug ref) create (session_id s, debug record args)\end{verbatim} @@ -9544,7 +10385,7 @@ reference to the newly created object \subsubsection{RPC name:~destroy} {\bf Overview:} -destructor for class debug +Destroy the specified debug instance. \noindent {\bf Signature:} \begin{verbatim} void destroy (session_id s, debug ref self)\end{verbatim} @@ -9557,7 +10398,7 @@ destructor for class debug \begin{tabular}{|c|c|p{7cm}|} \hline {\bf type} & {\bf name} & {\bf description} \\ \hline -{\tt debug ref } & self & object instance \\ \hline +{\tt debug ref } & self & reference to the object \\ \hline \end{tabular} @@ -9576,7 +10417,7 @@ void \subsubsection{RPC name:~get\_by\_uuid} {\bf Overview:} -returns the debug instance with a particular uuid +Get a reference to the debug instance with the specified UUID. \noindent {\bf Signature:} \begin{verbatim} (debug ref) get_by_uuid (session_id s, string uuid)\end{verbatim} @@ -9608,7 +10449,7 @@ reference to the object \subsubsection{RPC name:~get\_record} {\bf Overview:} -returns a record containing the state of an instance of class debug +Get a record containing the current state of the given debug. \noindent {\bf Signature:} \begin{verbatim} (debug record) get_record (session_id s, debug ref self)\end{verbatim} diff --git a/extras/mini-os/events.c b/extras/mini-os/events.c index 5d60ab70fc..dad8e42377 100644 --- a/extras/mini-os/events.c +++ b/extras/mini-os/events.c @@ -31,26 +31,27 @@ typedef struct _ev_action_t { u32 count; } ev_action_t; - static ev_action_t ev_actions[NR_EVS]; void default_handler(evtchn_port_t port, struct pt_regs *regs, void *data); +static unsigned long bound_ports[NR_EVS/(8*sizeof(unsigned long))]; + void unbind_all_ports(void) { int i; - for(i=0;i +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +/* This keeps a track of which one is crashing cpu. */ +static int crashing_cpu; + +static u32 *append_elf_note(u32 *buf, char *name, unsigned type, void *data, + size_t data_len) +{ + struct elf_note note; + + note.n_namesz = strlen(name) + 1; + note.n_descsz = data_len; + note.n_type = type; + memcpy(buf, ¬e, sizeof(note)); + buf += (sizeof(note) +3)/4; + memcpy(buf, name, note.n_namesz); + buf += (note.n_namesz + 3)/4; + memcpy(buf, data, note.n_descsz); + buf += (note.n_descsz + 3)/4; + + return buf; +} + +static void final_note(u32 *buf) +{ + struct elf_note note; + + note.n_namesz = 0; + note.n_descsz = 0; + note.n_type = 0; + memcpy(buf, ¬e, sizeof(note)); +} + +static void crash_save_this_cpu(struct pt_regs *regs, int cpu) +{ + struct elf_prstatus prstatus; + u32 *buf; + + if ((cpu < 0) || (cpu >= NR_CPUS)) + return; + + /* Using ELF notes here is opportunistic. + * I need a well defined structure format + * for the data I pass, and I need tags + * on the data to indicate what information I have + * squirrelled away. ELF notes happen to provide + * all of that that no need to invent something new. + */ + buf = (u32*)per_cpu_ptr(crash_notes, cpu); + if (!buf) + return; + memset(&prstatus, 0, sizeof(prstatus)); + prstatus.pr_pid = current->pid; + elf_core_copy_regs(&prstatus.pr_reg, regs); + buf = append_elf_note(buf, "CORE", NT_PRSTATUS, &prstatus, + sizeof(prstatus)); + final_note(buf); +} + +static void crash_save_self(struct pt_regs *regs) +{ + int cpu; + + cpu = smp_processor_id(); + crash_save_this_cpu(regs, cpu); +} + +#ifndef CONFIG_XEN +#ifdef CONFIG_SMP +static atomic_t waiting_for_crash_ipi; + +static int crash_nmi_callback(struct pt_regs *regs, int cpu) +{ + struct pt_regs fixed_regs; + + /* Don't do anything if this handler is invoked on crashing cpu. + * Otherwise, system will completely hang. Crashing cpu can get + * an NMI if system was initially booted with nmi_watchdog parameter. + */ + if (cpu == crashing_cpu) + return 1; + local_irq_disable(); + + if (!user_mode(regs)) { + crash_fixup_ss_esp(&fixed_regs, regs); + regs = &fixed_regs; + } + crash_save_this_cpu(regs, cpu); + disable_local_APIC(); + atomic_dec(&waiting_for_crash_ipi); + /* Assume hlt works */ + halt(); + for(;;); + + return 1; +} + +/* + * By using the NMI code instead of a vector we just sneak thru the + * word generator coming out with just what we want. AND it does + * not matter if clustered_apic_mode is set or not. + */ +static void smp_send_nmi_allbutself(void) +{ + send_IPI_allbutself(APIC_DM_NMI); +} + +static void nmi_shootdown_cpus(void) +{ + unsigned long msecs; + + atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1); + /* Would it be better to replace the trap vector here? */ + set_nmi_callback(crash_nmi_callback); + /* Ensure the new callback function is set before sending + * out the NMI + */ + wmb(); + + smp_send_nmi_allbutself(); + + msecs = 1000; /* Wait at most a second for the other cpus to stop */ + while ((atomic_read(&waiting_for_crash_ipi) > 0) && msecs) { + mdelay(1); + msecs--; + } + + /* Leave the nmi callback set */ + disable_local_APIC(); +} +#else +static void nmi_shootdown_cpus(void) +{ + /* There are no cpus to shootdown */ +} +#endif +#endif /* CONFIG_XEN */ + +void machine_crash_shutdown(struct pt_regs *regs) +{ + /* This function is only called after the system + * has paniced or is otherwise in a critical state. + * The minimum amount of code to allow a kexec'd kernel + * to run successfully needs to happen here. + * + * In practice this means shooting down the other cpus in + * an SMP system. + */ + /* The kernel is broken so disable interrupts */ + local_irq_disable(); + + /* Make a note of crashing cpu. Will be used in NMI callback.*/ + crashing_cpu = smp_processor_id(); +#ifndef CONFIG_XEN + nmi_shootdown_cpus(); + lapic_shutdown(); +#if defined(CONFIG_X86_IO_APIC) + disable_IO_APIC(); +#endif +#endif /* CONFIG_XEN */ + crash_save_self(regs); +} diff --git a/linux-2.6-xen-sparse/arch/i386/kernel/machine_kexec.c b/linux-2.6-xen-sparse/arch/i386/kernel/machine_kexec.c new file mode 100644 index 0000000000..d3979a6531 --- /dev/null +++ b/linux-2.6-xen-sparse/arch/i386/kernel/machine_kexec.c @@ -0,0 +1,129 @@ +/* + * machine_kexec.c - handle transition of Linux booting another kernel + * Copyright (C) 2002-2005 Eric Biederman + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_XEN +#include +#endif + +#define PAGE_ALIGNED __attribute__ ((__aligned__(PAGE_SIZE))) +static u32 kexec_pgd[1024] PAGE_ALIGNED; +#ifdef CONFIG_X86_PAE +static u32 kexec_pmd0[1024] PAGE_ALIGNED; +static u32 kexec_pmd1[1024] PAGE_ALIGNED; +#endif +static u32 kexec_pte0[1024] PAGE_ALIGNED; +static u32 kexec_pte1[1024] PAGE_ALIGNED; + +#ifdef CONFIG_XEN + +#define __ma(x) (pfn_to_mfn(__pa((x)) >> PAGE_SHIFT) << PAGE_SHIFT) + +#if PAGES_NR > KEXEC_XEN_NO_PAGES +#error PAGES_NR is greater than KEXEC_XEN_NO_PAGES - Xen support will break +#endif + +#if PA_CONTROL_PAGE != 0 +#error PA_CONTROL_PAGE is non zero - Xen support will break +#endif + +void machine_kexec_setup_load_arg(xen_kexec_image_t *xki, struct kimage *image) +{ + void *control_page; + + memset(xki->page_list, 0, sizeof(xki->page_list)); + + control_page = page_address(image->control_code_page); + memcpy(control_page, relocate_kernel, PAGE_SIZE); + + xki->page_list[PA_CONTROL_PAGE] = __ma(control_page); + xki->page_list[PA_PGD] = __ma(kexec_pgd); +#ifdef CONFIG_X86_PAE + xki->page_list[PA_PMD_0] = __ma(kexec_pmd0); + xki->page_list[PA_PMD_1] = __ma(kexec_pmd1); +#endif + xki->page_list[PA_PTE_0] = __ma(kexec_pte0); + xki->page_list[PA_PTE_1] = __ma(kexec_pte1); + +} + +#endif /* CONFIG_XEN */ + +/* + * A architecture hook called to validate the + * proposed image and prepare the control pages + * as needed. The pages for KEXEC_CONTROL_CODE_SIZE + * have been allocated, but the segments have yet + * been copied into the kernel. + * + * Do what every setup is needed on image and the + * reboot code buffer to allow us to avoid allocations + * later. + * + * Currently nothing. + */ +int machine_kexec_prepare(struct kimage *image) +{ + return 0; +} + +/* + * Undo anything leftover by machine_kexec_prepare + * when an image is freed. + */ +void machine_kexec_cleanup(struct kimage *image) +{ +} + +#ifndef CONFIG_XEN +/* + * Do not allocate memory (or fail in any way) in machine_kexec(). + * We are past the point of no return, committed to rebooting now. + */ +NORET_TYPE void machine_kexec(struct kimage *image) +{ + unsigned long page_list[PAGES_NR]; + void *control_page; + + /* Interrupts aren't acceptable while we reboot */ + local_irq_disable(); + + control_page = page_address(image->control_code_page); + memcpy(control_page, relocate_kernel, PAGE_SIZE); + + page_list[PA_CONTROL_PAGE] = __pa(control_page); + page_list[VA_CONTROL_PAGE] = (unsigned long)relocate_kernel; + page_list[PA_PGD] = __pa(kexec_pgd); + page_list[VA_PGD] = (unsigned long)kexec_pgd; +#ifdef CONFIG_X86_PAE + page_list[PA_PMD_0] = __pa(kexec_pmd0); + page_list[VA_PMD_0] = (unsigned long)kexec_pmd0; + page_list[PA_PMD_1] = __pa(kexec_pmd1); + page_list[VA_PMD_1] = (unsigned long)kexec_pmd1; +#endif + page_list[PA_PTE_0] = __pa(kexec_pte0); + page_list[VA_PTE_0] = (unsigned long)kexec_pte0; + page_list[PA_PTE_1] = __pa(kexec_pte1); + page_list[VA_PTE_1] = (unsigned long)kexec_pte1; + + relocate_kernel((unsigned long)image->head, (unsigned long)page_list, + image->start, cpu_has_pae); +} +#endif diff --git a/linux-2.6-xen-sparse/arch/i386/kernel/setup-xen.c b/linux-2.6-xen-sparse/arch/i386/kernel/setup-xen.c index 2586296dbb..819f36f11e 100644 --- a/linux-2.6-xen-sparse/arch/i386/kernel/setup-xen.c +++ b/linux-2.6-xen-sparse/arch/i386/kernel/setup-xen.c @@ -69,6 +69,10 @@ #include "setup_arch_pre.h" #include +#ifdef CONFIG_XEN +#include +#endif + /* Forward Declaration. */ void __init find_max_pfn(void); @@ -943,6 +947,7 @@ static void __init parse_cmdline_early (char ** cmdline_p) * after a kernel panic. */ else if (!memcmp(from, "crashkernel=", 12)) { +#ifndef CONFIG_XEN unsigned long size, base; size = memparse(from+12, &from); if (*from == '@') { @@ -953,6 +958,10 @@ static void __init parse_cmdline_early (char ** cmdline_p) crashk_res.start = base; crashk_res.end = base + size - 1; } +#else + printk("Ignoring crashkernel command line, " + "parameter will be supplied by xen\n"); +#endif } #endif #ifdef CONFIG_PROC_VMCORE @@ -1322,9 +1331,13 @@ void __init setup_bootmem_allocator(void) } #endif #ifdef CONFIG_KEXEC +#ifdef CONFIG_XEN + xen_machine_kexec_setup_resources(); +#else if (crashk_res.start != crashk_res.end) reserve_bootmem(crashk_res.start, crashk_res.end - crashk_res.start + 1); +#endif #endif if (!xen_feature(XENFEAT_auto_translated_physmap)) @@ -1389,7 +1402,11 @@ legacy_init_iomem_resources(struct e820entry *e820, int nr_map, request_resource(res, data_resource); #endif #ifdef CONFIG_KEXEC - request_resource(res, &crashk_res); + if (crashk_res.start != crashk_res.end) + request_resource(res, &crashk_res); +#ifdef CONFIG_XEN + xen_machine_kexec_register_resources(res); +#endif #endif } } @@ -1850,9 +1867,11 @@ void __init setup_arch(char **cmdline_p) #endif #endif } else { - extern int console_use_vt; - console_use_vt = 0; +#if defined(CONFIG_VT) && defined(CONFIG_DUMMY_CONSOLE) + conswitchp = &dummy_con; +#endif } + xencons_early_setup(); } static int diff --git a/linux-2.6-xen-sparse/arch/i386/kernel/swiotlb.c b/linux-2.6-xen-sparse/arch/i386/kernel/swiotlb.c index b100261073..4fa98132f4 100644 --- a/linux-2.6-xen-sparse/arch/i386/kernel/swiotlb.c +++ b/linux-2.6-xen-sparse/arch/i386/kernel/swiotlb.c @@ -47,8 +47,8 @@ EXPORT_SYMBOL(swiotlb); */ #define IO_TLB_SHIFT 11 -/* Width of DMA addresses in the IO TLB. 31 bits is an aacraid limitation. */ -#define IO_TLB_DMA_BITS 31 +/* Width of DMA addresses in the IO TLB. 30 bits is a b44 limitation. */ +#define DEFAULT_IO_TLB_DMA_BITS 30 static int swiotlb_force; static char *iotlb_virt_start; @@ -98,6 +98,15 @@ static struct phys_addr { */ static DEFINE_SPINLOCK(io_tlb_lock); +static unsigned int io_tlb_dma_bits = DEFAULT_IO_TLB_DMA_BITS; +static int __init +setup_io_tlb_bits(char *str) +{ + io_tlb_dma_bits = simple_strtoul(str, NULL, 0); + return 0; +} +__setup("swiotlb_bits=", setup_io_tlb_bits); + static int __init setup_io_tlb_npages(char *str) { @@ -158,7 +167,7 @@ swiotlb_init_with_default_size (size_t default_size) int rc = xen_create_contiguous_region( (unsigned long)iotlb_virt_start + (i << IO_TLB_SHIFT), get_order(IO_TLB_SEGSIZE << IO_TLB_SHIFT), - IO_TLB_DMA_BITS); + io_tlb_dma_bits); BUG_ON(rc); } @@ -183,10 +192,12 @@ swiotlb_init_with_default_size (size_t default_size) printk(KERN_INFO "Software IO TLB enabled: \n" " Aperture: %lu megabytes\n" - " Kernel range: 0x%016lx - 0x%016lx\n", + " Kernel range: 0x%016lx - 0x%016lx\n" + " Address size: %u bits\n", bytes >> 20, (unsigned long)iotlb_virt_start, - (unsigned long)iotlb_virt_start + bytes); + (unsigned long)iotlb_virt_start + bytes, + io_tlb_dma_bits); } void @@ -654,7 +665,7 @@ swiotlb_dma_mapping_error(dma_addr_t dma_addr) int swiotlb_dma_supported (struct device *hwdev, u64 mask) { - return (mask >= ((1UL << IO_TLB_DMA_BITS) - 1)); + return (mask >= ((1UL << io_tlb_dma_bits) - 1)); } EXPORT_SYMBOL(swiotlb_init); diff --git a/linux-2.6-xen-sparse/arch/i386/kernel/time-xen.c b/linux-2.6-xen-sparse/arch/i386/kernel/time-xen.c index 05f3c47e50..816b890050 100644 --- a/linux-2.6-xen-sparse/arch/i386/kernel/time-xen.c +++ b/linux-2.6-xen-sparse/arch/i386/kernel/time-xen.c @@ -710,6 +710,10 @@ irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) (cputime_t)delta_cpu); } + /* Offlined for more than a few seconds? Avoid lockup warnings. */ + if (stolen > 5*HZ) + touch_softlockup_watchdog(); + /* Local timer processing (see update_process_times()). */ run_local_timers(); if (rcu_pending(cpu)) @@ -1043,9 +1047,9 @@ void time_resume(void) #ifdef CONFIG_SMP static char timer_name[NR_CPUS][15]; -void local_setup_timer(unsigned int cpu) +int local_setup_timer(unsigned int cpu) { - int seq; + int seq, irq; BUG_ON(cpu == 0); @@ -1058,15 +1062,17 @@ void local_setup_timer(unsigned int cpu) } while (read_seqretry(&xtime_lock, seq)); sprintf(timer_name[cpu], "timer%d", cpu); - per_cpu(timer_irq, cpu) = - bind_virq_to_irqhandler( - VIRQ_TIMER, - cpu, - timer_interrupt, - SA_INTERRUPT, - timer_name[cpu], - NULL); - BUG_ON(per_cpu(timer_irq, cpu) < 0); + irq = bind_virq_to_irqhandler(VIRQ_TIMER, + cpu, + timer_interrupt, + SA_INTERRUPT, + timer_name[cpu], + NULL); + if (irq < 0) + return irq; + per_cpu(timer_irq, cpu) = irq; + + return 0; } void local_teardown_timer(unsigned int cpu) diff --git a/linux-2.6-xen-sparse/arch/ia64/Kconfig b/linux-2.6-xen-sparse/arch/ia64/Kconfig index 4073a04638..5a3768d779 100644 --- a/linux-2.6-xen-sparse/arch/ia64/Kconfig +++ b/linux-2.6-xen-sparse/arch/ia64/Kconfig @@ -533,9 +533,6 @@ config XEN_BALLOON config XEN_SKBUFF default y -config XEN_DEVMEM - default n - config XEN_REBOOT default y diff --git a/linux-2.6-xen-sparse/arch/ia64/kernel/entry.S b/linux-2.6-xen-sparse/arch/ia64/kernel/entry.S index 67738bb21d..67f37b2c53 100644 --- a/linux-2.6-xen-sparse/arch/ia64/kernel/entry.S +++ b/linux-2.6-xen-sparse/arch/ia64/kernel/entry.S @@ -602,7 +602,7 @@ GLOBAL_ENTRY(ia64_strace_leave_kernel) .ret4: br.cond.sptk ia64_leave_kernel END(ia64_strace_leave_kernel) -GLOBAL_ENTRY(ia64_ret_from_clone) +GLOBAL_ENTRY(__ia64_ret_from_clone) PT_REGS_UNWIND_INFO(0) { /* * Some versions of gas generate bad unwind info if the first instruction of a @@ -628,7 +628,7 @@ GLOBAL_ENTRY(ia64_ret_from_clone) cmp.ne p6,p0=r2,r0 (p6) br.cond.spnt .strace_check_retval ;; // added stop bits to prevent r8 dependency -END(ia64_ret_from_clone) +END(__ia64_ret_from_clone) // fall through GLOBAL_ENTRY(ia64_ret_from_syscall) PT_REGS_UNWIND_INFO(0) diff --git a/linux-2.6-xen-sparse/arch/ia64/kernel/iosapic.c b/linux-2.6-xen-sparse/arch/ia64/kernel/iosapic.c index d9b11edf96..5cbfa26004 100644 --- a/linux-2.6-xen-sparse/arch/ia64/kernel/iosapic.c +++ b/linux-2.6-xen-sparse/arch/ia64/kernel/iosapic.c @@ -197,6 +197,16 @@ int xen_assign_irq_vector(int irq) return irq_op.vector; } + +void xen_free_irq_vector(int vector) +{ + struct physdev_irq irq_op; + + irq_op.vector = vector; + if (HYPERVISOR_physdev_op(PHYSDEVOP_free_irq_vector, &irq_op)) + printk(KERN_WARNING "%s: xen_free_irq_vecotr fail vector=%d\n", + __FUNCTION__, vector); +} #endif /* XEN */ /* diff --git a/linux-2.6-xen-sparse/arch/ia64/kernel/irq_ia64.c b/linux-2.6-xen-sparse/arch/ia64/kernel/irq_ia64.c index 49b6f8fe57..495eb496f5 100644 --- a/linux-2.6-xen-sparse/arch/ia64/kernel/irq_ia64.c +++ b/linux-2.6-xen-sparse/arch/ia64/kernel/irq_ia64.c @@ -94,6 +94,13 @@ free_irq_vector (int vector) if (vector < IA64_FIRST_DEVICE_VECTOR || vector > IA64_LAST_DEVICE_VECTOR) return; +#ifdef CONFIG_XEN + if (is_running_on_xen()) { + extern void xen_free_irq_vector(int); + xen_free_irq_vector(vector); + return; + } +#endif pos = vector - IA64_FIRST_DEVICE_VECTOR; if (!test_and_clear_bit(pos, ia64_vector_mask)) printk(KERN_WARNING "%s: double free!\n", __FUNCTION__); @@ -241,9 +248,15 @@ static struct irqaction ipi_irqaction = { static DEFINE_PER_CPU(int, timer_irq) = -1; static DEFINE_PER_CPU(int, ipi_irq) = -1; static DEFINE_PER_CPU(int, resched_irq) = -1; +static DEFINE_PER_CPU(int, cmc_irq) = -1; +static DEFINE_PER_CPU(int, cmcp_irq) = -1; +static DEFINE_PER_CPU(int, cpep_irq) = -1; static char timer_name[NR_CPUS][15]; static char ipi_name[NR_CPUS][15]; static char resched_name[NR_CPUS][15]; +static char cmc_name[NR_CPUS][15]; +static char cmcp_name[NR_CPUS][15]; +static char cpep_name[NR_CPUS][15]; struct saved_irq { unsigned int irq; @@ -323,6 +336,43 @@ xen_register_percpu_irq (unsigned int irq, struct irqaction *action, int save) break; case IA64_SPURIOUS_INT_VECTOR: break; + case IA64_CMC_VECTOR: + sprintf(cmc_name[cpu], "%s%d", action->name, cpu); + ret = bind_virq_to_irqhandler(VIRQ_MCA_CMC, cpu, + action->handler, + action->flags, + cmc_name[cpu], + action->dev_id); + per_cpu(cmc_irq,cpu) = ret; + printk(KERN_INFO "register VIRQ_MCA_CMC (%s) to xen " + "irq (%d)\n", cmc_name[cpu], ret); + break; + case IA64_CMCP_VECTOR: + sprintf(cmcp_name[cpu], "%s%d", action->name, cpu); + ret = bind_ipi_to_irqhandler(CMCP_VECTOR, cpu, + action->handler, + action->flags, + cmcp_name[cpu], + action->dev_id); + per_cpu(cmcp_irq,cpu) = ret; + printk(KERN_INFO "register CMCP_VECTOR (%s) to xen " + "irq (%d)\n", cmcp_name[cpu], ret); + break; + case IA64_CPEP_VECTOR: + sprintf(cpep_name[cpu], "%s%d", action->name, cpu); + ret = bind_ipi_to_irqhandler(CPEP_VECTOR, cpu, + action->handler, + action->flags, + cpep_name[cpu], + action->dev_id); + per_cpu(cpep_irq,cpu) = ret; + printk(KERN_INFO "register CPEP_VECTOR (%s) to xen " + "irq (%d)\n", cpep_name[cpu], ret); + break; + case IA64_CPE_VECTOR: + printk(KERN_WARNING "register IA64_CPE_VECTOR " + "IGNORED\n"); + break; default: printk(KERN_WARNING "Percpu irq %d is unsupported by xen!\n", irq); break; @@ -373,6 +423,18 @@ unbind_evtchn_callback(struct notifier_block *nfb, if (action == CPU_DEAD) { /* Unregister evtchn. */ + if (per_cpu(cpep_irq,cpu) >= 0) { + unbind_from_irqhandler(per_cpu(cpep_irq, cpu), NULL); + per_cpu(cpep_irq, cpu) = -1; + } + if (per_cpu(cmcp_irq,cpu) >= 0) { + unbind_from_irqhandler(per_cpu(cmcp_irq, cpu), NULL); + per_cpu(cmcp_irq, cpu) = -1; + } + if (per_cpu(cmc_irq,cpu) >= 0) { + unbind_from_irqhandler(per_cpu(cmc_irq, cpu), NULL); + per_cpu(cmc_irq, cpu) = -1; + } if (per_cpu(ipi_irq,cpu) >= 0) { unbind_from_irqhandler (per_cpu(ipi_irq, cpu), NULL); per_cpu(ipi_irq, cpu) = -1; @@ -503,6 +565,12 @@ ia64_send_ipi (int cpu, int vector, int delivery_mode, int redirect) case IA64_IPI_RESCHEDULE: irq = per_cpu(ipi_to_irq, cpu)[RESCHEDULE_VECTOR]; break; + case IA64_CMCP_VECTOR: + irq = per_cpu(ipi_to_irq, cpu)[CMCP_VECTOR]; + break; + case IA64_CPEP_VECTOR: + irq = per_cpu(ipi_to_irq, cpu)[CPEP_VECTOR]; + break; default: printk(KERN_WARNING"Unsupported IPI type 0x%x\n", vector); irq = 0; diff --git a/linux-2.6-xen-sparse/arch/ia64/kernel/perfmon.c b/linux-2.6-xen-sparse/arch/ia64/kernel/perfmon.c new file mode 100644 index 0000000000..2d5a61865d --- /dev/null +++ b/linux-2.6-xen-sparse/arch/ia64/kernel/perfmon.c @@ -0,0 +1,6941 @@ +/* + * This file implements the perfmon-2 subsystem which is used + * to program the IA-64 Performance Monitoring Unit (PMU). + * + * The initial version of perfmon.c was written by + * Ganesh Venkitachalam, IBM Corp. + * + * Then it was modified for perfmon-1.x by Stephane Eranian and + * David Mosberger, Hewlett Packard Co. + * + * Version Perfmon-2.x is a rewrite of perfmon-1.x + * by Stephane Eranian, Hewlett Packard Co. + * + * Copyright (C) 1999-2005 Hewlett Packard Co + * Stephane Eranian + * David Mosberger-Tang + * + * More information about perfmon available at: + * http://www.hpl.hp.com/research/linux/perfmon + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_PERFMON +#ifdef CONFIG_XEN +//#include +#include + +static int xenoprof_is_primary = 0; +#define init_xenoprof_primary(is_primary) (xenoprof_is_primary = (is_primary)) +#define is_xenoprof_primary() (xenoprof_is_primary) +#define XEN_NOT_SUPPORTED_YET \ + do { \ + if (is_running_on_xen()) { \ + printk("%s is not supported yet under xen.\n", \ + __func__); \ + return -ENOSYS; \ + } \ + } while (0) +#else +#define init_xenoprof_primary(is_primary) do { } while (0) +#define is_xenoprof_primary() (0) +#define XEN_NOT_SUPPORTED_YET do { } while (0) +#define HYPERVISOR_perfmon_op(cmd, arg, count) do { } while (0) +#endif + +/* + * perfmon context state + */ +#define PFM_CTX_UNLOADED 1 /* context is not loaded onto any task */ +#define PFM_CTX_LOADED 2 /* context is loaded onto a task */ +#define PFM_CTX_MASKED 3 /* context is loaded but monitoring is masked due to overflow */ +#define PFM_CTX_ZOMBIE 4 /* owner of the context is closing it */ + +#define PFM_INVALID_ACTIVATION (~0UL) + +/* + * depth of message queue + */ +#define PFM_MAX_MSGS 32 +#define PFM_CTXQ_EMPTY(g) ((g)->ctx_msgq_head == (g)->ctx_msgq_tail) + +/* + * type of a PMU register (bitmask). + * bitmask structure: + * bit0 : register implemented + * bit1 : end marker + * bit2-3 : reserved + * bit4 : pmc has pmc.pm + * bit5 : pmc controls a counter (has pmc.oi), pmd is used as counter + * bit6-7 : register type + * bit8-31: reserved + */ +#define PFM_REG_NOTIMPL 0x0 /* not implemented at all */ +#define PFM_REG_IMPL 0x1 /* register implemented */ +#define PFM_REG_END 0x2 /* end marker */ +#define PFM_REG_MONITOR (0x1<<4|PFM_REG_IMPL) /* a PMC with a pmc.pm field only */ +#define PFM_REG_COUNTING (0x2<<4|PFM_REG_MONITOR) /* a monitor + pmc.oi+ PMD used as a counter */ +#define PFM_REG_CONTROL (0x4<<4|PFM_REG_IMPL) /* PMU control register */ +#define PFM_REG_CONFIG (0x8<<4|PFM_REG_IMPL) /* configuration register */ +#define PFM_REG_BUFFER (0xc<<4|PFM_REG_IMPL) /* PMD used as buffer */ + +#define PMC_IS_LAST(i) (pmu_conf->pmc_desc[i].type & PFM_REG_END) +#define PMD_IS_LAST(i) (pmu_conf->pmd_desc[i].type & PFM_REG_END) + +#define PMC_OVFL_NOTIFY(ctx, i) ((ctx)->ctx_pmds[i].flags & PFM_REGFL_OVFL_NOTIFY) + +/* i assumed unsigned */ +#define PMC_IS_IMPL(i) (i< PMU_MAX_PMCS && (pmu_conf->pmc_desc[i].type & PFM_REG_IMPL)) +#define PMD_IS_IMPL(i) (i< PMU_MAX_PMDS && (pmu_conf->pmd_desc[i].type & PFM_REG_IMPL)) + +/* XXX: these assume that register i is implemented */ +#define PMD_IS_COUNTING(i) ((pmu_conf->pmd_desc[i].type & PFM_REG_COUNTING) == PFM_REG_COUNTING) +#define PMC_IS_COUNTING(i) ((pmu_conf->pmc_desc[i].type & PFM_REG_COUNTING) == PFM_REG_COUNTING) +#define PMC_IS_MONITOR(i) ((pmu_conf->pmc_desc[i].type & PFM_REG_MONITOR) == PFM_REG_MONITOR) +#define PMC_IS_CONTROL(i) ((pmu_conf->pmc_desc[i].type & PFM_REG_CONTROL) == PFM_REG_CONTROL) + +#define PMC_DFL_VAL(i) pmu_conf->pmc_desc[i].default_value +#define PMC_RSVD_MASK(i) pmu_conf->pmc_desc[i].reserved_mask +#define PMD_PMD_DEP(i) pmu_conf->pmd_desc[i].dep_pmd[0] +#define PMC_PMD_DEP(i) pmu_conf->pmc_desc[i].dep_pmd[0] + +#define PFM_NUM_IBRS IA64_NUM_DBG_REGS +#define PFM_NUM_DBRS IA64_NUM_DBG_REGS + +#define CTX_OVFL_NOBLOCK(c) ((c)->ctx_fl_block == 0) +#define CTX_HAS_SMPL(c) ((c)->ctx_fl_is_sampling) +#define PFM_CTX_TASK(h) (h)->ctx_task + +#define PMU_PMC_OI 5 /* position of pmc.oi bit */ + +/* XXX: does not support more than 64 PMDs */ +#define CTX_USED_PMD(ctx, mask) (ctx)->ctx_used_pmds[0] |= (mask) +#define CTX_IS_USED_PMD(ctx, c) (((ctx)->ctx_used_pmds[0] & (1UL << (c))) != 0UL) + +#define CTX_USED_MONITOR(ctx, mask) (ctx)->ctx_used_monitors[0] |= (mask) + +#define CTX_USED_IBR(ctx,n) (ctx)->ctx_used_ibrs[(n)>>6] |= 1UL<< ((n) % 64) +#define CTX_USED_DBR(ctx,n) (ctx)->ctx_used_dbrs[(n)>>6] |= 1UL<< ((n) % 64) +#define CTX_USES_DBREGS(ctx) (((pfm_context_t *)(ctx))->ctx_fl_using_dbreg==1) +#define PFM_CODE_RR 0 /* requesting code range restriction */ +#define PFM_DATA_RR 1 /* requestion data range restriction */ + +#define PFM_CPUINFO_CLEAR(v) pfm_get_cpu_var(pfm_syst_info) &= ~(v) +#define PFM_CPUINFO_SET(v) pfm_get_cpu_var(pfm_syst_info) |= (v) +#define PFM_CPUINFO_GET() pfm_get_cpu_var(pfm_syst_info) + +#define RDEP(x) (1UL<<(x)) + +/* + * context protection macros + * in SMP: + * - we need to protect against CPU concurrency (spin_lock) + * - we need to protect against PMU overflow interrupts (local_irq_disable) + * in UP: + * - we need to protect against PMU overflow interrupts (local_irq_disable) + * + * spin_lock_irqsave()/spin_lock_irqrestore(): + * in SMP: local_irq_disable + spin_lock + * in UP : local_irq_disable + * + * spin_lock()/spin_lock(): + * in UP : removed automatically + * in SMP: protect against context accesses from other CPU. interrupts + * are not masked. This is useful for the PMU interrupt handler + * because we know we will not get PMU concurrency in that code. + */ +#define PROTECT_CTX(c, f) \ + do { \ + DPRINT(("spinlock_irq_save ctx %p by [%d]\n", c, current->pid)); \ + spin_lock_irqsave(&(c)->ctx_lock, f); \ + DPRINT(("spinlocked ctx %p by [%d]\n", c, current->pid)); \ + } while(0) + +#define UNPROTECT_CTX(c, f) \ + do { \ + DPRINT(("spinlock_irq_restore ctx %p by [%d]\n", c, current->pid)); \ + spin_unlock_irqrestore(&(c)->ctx_lock, f); \ + } while(0) + +#define PROTECT_CTX_NOPRINT(c, f) \ + do { \ + spin_lock_irqsave(&(c)->ctx_lock, f); \ + } while(0) + + +#define UNPROTECT_CTX_NOPRINT(c, f) \ + do { \ + spin_unlock_irqrestore(&(c)->ctx_lock, f); \ + } while(0) + + +#define PROTECT_CTX_NOIRQ(c) \ + do { \ + spin_lock(&(c)->ctx_lock); \ + } while(0) + +#define UNPROTECT_CTX_NOIRQ(c) \ + do { \ + spin_unlock(&(c)->ctx_lock); \ + } while(0) + + +#ifdef CONFIG_SMP + +#define GET_ACTIVATION() pfm_get_cpu_var(pmu_activation_number) +#define INC_ACTIVATION() pfm_get_cpu_var(pmu_activation_number)++ +#define SET_ACTIVATION(c) (c)->ctx_last_activation = GET_ACTIVATION() + +#else /* !CONFIG_SMP */ +#define SET_ACTIVATION(t) do {} while(0) +#define GET_ACTIVATION(t) do {} while(0) +#define INC_ACTIVATION(t) do {} while(0) +#endif /* CONFIG_SMP */ + +#define SET_PMU_OWNER(t, c) do { pfm_get_cpu_var(pmu_owner) = (t); pfm_get_cpu_var(pmu_ctx) = (c); } while(0) +#define GET_PMU_OWNER() pfm_get_cpu_var(pmu_owner) +#define GET_PMU_CTX() pfm_get_cpu_var(pmu_ctx) + +#define LOCK_PFS(g) spin_lock_irqsave(&pfm_sessions.pfs_lock, g) +#define UNLOCK_PFS(g) spin_unlock_irqrestore(&pfm_sessions.pfs_lock, g) + +#define PFM_REG_RETFLAG_SET(flags, val) do { flags &= ~PFM_REG_RETFL_MASK; flags |= (val); } while(0) + +/* + * cmp0 must be the value of pmc0 + */ +#define PMC0_HAS_OVFL(cmp0) (cmp0 & ~0x1UL) + +#define PFMFS_MAGIC 0xa0b4d889 + +/* + * debugging + */ +#define PFM_DEBUGGING 1 +#ifdef PFM_DEBUGGING +#define DPRINT(a) \ + do { \ + if (unlikely(pfm_sysctl.debug >0)) { printk("%s.%d: CPU%d [%d] ", __FUNCTION__, __LINE__, smp_processor_id(), current->pid); printk a; } \ + } while (0) + +#define DPRINT_ovfl(a) \ + do { \ + if (unlikely(pfm_sysctl.debug > 0 && pfm_sysctl.debug_ovfl >0)) { printk("%s.%d: CPU%d [%d] ", __FUNCTION__, __LINE__, smp_processor_id(), current->pid); printk a; } \ + } while (0) +#endif + +/* + * 64-bit software counter structure + * + * the next_reset_type is applied to the next call to pfm_reset_regs() + */ +typedef struct { + unsigned long val; /* virtual 64bit counter value */ + unsigned long lval; /* last reset value */ + unsigned long long_reset; /* reset value on sampling overflow */ + unsigned long short_reset; /* reset value on overflow */ + unsigned long reset_pmds[4]; /* which other pmds to reset when this counter overflows */ + unsigned long smpl_pmds[4]; /* which pmds are accessed when counter overflow */ + unsigned long seed; /* seed for random-number generator */ + unsigned long mask; /* mask for random-number generator */ + unsigned int flags; /* notify/do not notify */ + unsigned long eventid; /* overflow event identifier */ +} pfm_counter_t; + +/* + * context flags + */ +typedef struct { + unsigned int block:1; /* when 1, task will blocked on user notifications */ + unsigned int system:1; /* do system wide monitoring */ + unsigned int using_dbreg:1; /* using range restrictions (debug registers) */ + unsigned int is_sampling:1; /* true if using a custom format */ + unsigned int excl_idle:1; /* exclude idle task in system wide session */ + unsigned int going_zombie:1; /* context is zombie (MASKED+blocking) */ + unsigned int trap_reason:2; /* reason for going into pfm_handle_work() */ + unsigned int no_msg:1; /* no message sent on overflow */ + unsigned int can_restart:1; /* allowed to issue a PFM_RESTART */ + unsigned int reserved:22; +} pfm_context_flags_t; + +#define PFM_TRAP_REASON_NONE 0x0 /* default value */ +#define PFM_TRAP_REASON_BLOCK 0x1 /* we need to block on overflow */ +#define PFM_TRAP_REASON_RESET 0x2 /* we need to reset PMDs */ + + +/* + * perfmon context: encapsulates all the state of a monitoring session + */ + +typedef struct pfm_context { + spinlock_t ctx_lock; /* context protection */ + + pfm_context_flags_t ctx_flags; /* bitmask of flags (block reason incl.) */ + unsigned int ctx_state; /* state: active/inactive (no bitfield) */ + + struct task_struct *ctx_task; /* task to which context is attached */ + + unsigned long ctx_ovfl_regs[4]; /* which registers overflowed (notification) */ + + struct completion ctx_restart_done; /* use for blocking notification mode */ + + unsigned long ctx_used_pmds[4]; /* bitmask of PMD used */ + unsigned long ctx_all_pmds[4]; /* bitmask of all accessible PMDs */ + unsigned long ctx_reload_pmds[4]; /* bitmask of force reload PMD on ctxsw in */ + + unsigned long ctx_all_pmcs[4]; /* bitmask of all accessible PMCs */ + unsigned long ctx_reload_pmcs[4]; /* bitmask of force reload PMC on ctxsw in */ + unsigned long ctx_used_monitors[4]; /* bitmask of monitor PMC being used */ + + unsigned long ctx_pmcs[IA64_NUM_PMC_REGS]; /* saved copies of PMC values */ + + unsigned int ctx_used_ibrs[1]; /* bitmask of used IBR (speedup ctxsw in) */ + unsigned int ctx_used_dbrs[1]; /* bitmask of used DBR (speedup ctxsw in) */ + unsigned long ctx_dbrs[IA64_NUM_DBG_REGS]; /* DBR values (cache) when not loaded */ + unsigned long ctx_ibrs[IA64_NUM_DBG_REGS]; /* IBR values (cache) when not loaded */ + + pfm_counter_t ctx_pmds[IA64_NUM_PMD_REGS]; /* software state for PMDS */ + + u64 ctx_saved_psr_up; /* only contains psr.up value */ + + unsigned long ctx_last_activation; /* context last activation number for last_cpu */ + unsigned int ctx_last_cpu; /* CPU id of current or last CPU used (SMP only) */ + unsigned int ctx_cpu; /* cpu to which perfmon is applied (system wide) */ + + int ctx_fd; /* file descriptor used my this context */ + pfm_ovfl_arg_t ctx_ovfl_arg; /* argument to custom buffer format handler */ + + pfm_buffer_fmt_t *ctx_buf_fmt; /* buffer format callbacks */ + void *ctx_smpl_hdr; /* points to sampling buffer header kernel vaddr */ + unsigned long ctx_smpl_size; /* size of sampling buffer */ + void *ctx_smpl_vaddr; /* user level virtual address of smpl buffer */ + + wait_queue_head_t ctx_msgq_wait; + pfm_msg_t ctx_msgq[PFM_MAX_MSGS]; + int ctx_msgq_head; + int ctx_msgq_tail; + struct fasync_struct *ctx_async_queue; + + wait_queue_head_t ctx_zombieq; /* termination cleanup wait queue */ +} pfm_context_t; + +/* + * magic number used to verify that structure is really + * a perfmon context + */ +#define PFM_IS_FILE(f) ((f)->f_op == &pfm_file_ops) + +#define PFM_GET_CTX(t) ((pfm_context_t *)(t)->thread.pfm_context) + +#ifdef CONFIG_SMP +#define SET_LAST_CPU(ctx, v) (ctx)->ctx_last_cpu = (v) +#define GET_LAST_CPU(ctx) (ctx)->ctx_last_cpu +#else +#define SET_LAST_CPU(ctx, v) do {} while(0) +#define GET_LAST_CPU(ctx) do {} while(0) +#endif + + +#define ctx_fl_block ctx_flags.block +#define ctx_fl_system ctx_flags.system +#define ctx_fl_using_dbreg ctx_flags.using_dbreg +#define ctx_fl_is_sampling ctx_flags.is_sampling +#define ctx_fl_excl_idle ctx_flags.excl_idle +#define ctx_fl_going_zombie ctx_flags.going_zombie +#define ctx_fl_trap_reason ctx_flags.trap_reason +#define ctx_fl_no_msg ctx_flags.no_msg +#define ctx_fl_can_restart ctx_flags.can_restart + +#define PFM_SET_WORK_PENDING(t, v) do { (t)->thread.pfm_needs_checking = v; } while(0); +#define PFM_GET_WORK_PENDING(t) (t)->thread.pfm_needs_checking + +/* + * global information about all sessions + * mostly used to synchronize between system wide and per-process + */ +typedef struct { + spinlock_t pfs_lock; /* lock the structure */ + + unsigned int pfs_task_sessions; /* number of per task sessions */ + unsigned int pfs_sys_sessions; /* number of per system wide sessions */ + unsigned int pfs_sys_use_dbregs; /* incremented when a system wide session uses debug regs */ + unsigned int pfs_ptrace_use_dbregs; /* incremented when a process uses debug regs */ + struct task_struct *pfs_sys_session[NR_CPUS]; /* point to task owning a system-wide session */ +} pfm_session_t; + +/* + * information about a PMC or PMD. + * dep_pmd[]: a bitmask of dependent PMD registers + * dep_pmc[]: a bitmask of dependent PMC registers + */ +typedef int (*pfm_reg_check_t)(struct task_struct *task, pfm_context_t *ctx, unsigned int cnum, unsigned long *val, struct pt_regs *regs); +typedef struct { + unsigned int type; + int pm_pos; + unsigned long default_value; /* power-on default value */ + unsigned long reserved_mask; /* bitmask of reserved bits */ + pfm_reg_check_t read_check; + pfm_reg_check_t write_check; + unsigned long dep_pmd[4]; + unsigned long dep_pmc[4]; +} pfm_reg_desc_t; + +/* assume cnum is a valid monitor */ +#define PMC_PM(cnum, val) (((val) >> (pmu_conf->pmc_desc[cnum].pm_pos)) & 0x1) + +/* + * This structure is initialized at boot time and contains + * a description of the PMU main characteristics. + * + * If the probe function is defined, detection is based + * on its return value: + * - 0 means recognized PMU + * - anything else means not supported + * When the probe function is not defined, then the pmu_family field + * is used and it must match the host CPU family such that: + * - cpu->family & config->pmu_family != 0 + */ +typedef struct { + unsigned long ovfl_val; /* overflow value for counters */ + + pfm_reg_desc_t *pmc_desc; /* detailed PMC register dependencies descriptions */ + pfm_reg_desc_t *pmd_desc; /* detailed PMD register dependencies descriptions */ + + unsigned int num_pmcs; /* number of PMCS: computed at init time */ + unsigned int num_pmds; /* number of PMDS: computed at init time */ + unsigned long impl_pmcs[4]; /* bitmask of implemented PMCS */ + unsigned long impl_pmds[4]; /* bitmask of implemented PMDS */ + + char *pmu_name; /* PMU family name */ + unsigned int pmu_family; /* cpuid family pattern used to identify pmu */ + unsigned int flags; /* pmu specific flags */ + unsigned int num_ibrs; /* number of IBRS: computed at init time */ + unsigned int num_dbrs; /* number of DBRS: computed at init time */ + unsigned int num_counters; /* PMC/PMD counting pairs : computed at init time */ + int (*probe)(void); /* customized probe routine */ + unsigned int use_rr_dbregs:1; /* set if debug registers used for range restriction */ +} pmu_config_t; +/* + * PMU specific flags + */ +#define PFM_PMU_IRQ_RESEND 1 /* PMU needs explicit IRQ resend */ + +/* + * debug register related type definitions + */ +typedef struct { + unsigned long ibr_mask:56; + unsigned long ibr_plm:4; + unsigned long ibr_ig:3; + unsigned long ibr_x:1; +} ibr_mask_reg_t; + +typedef struct { + unsigned long dbr_mask:56; + unsigned long dbr_plm:4; + unsigned long dbr_ig:2; + unsigned long dbr_w:1; + unsigned long dbr_r:1; +} dbr_mask_reg_t; + +typedef union { + unsigned long val; + ibr_mask_reg_t ibr; + dbr_mask_reg_t dbr; +} dbreg_t; + + +/* + * perfmon command descriptions + */ +typedef struct { + int (*cmd_func)(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs); + char *cmd_name; + int cmd_flags; + unsigned int cmd_narg; + size_t cmd_argsize; + int (*cmd_getsize)(void *arg, size_t *sz); +} pfm_cmd_desc_t; + +#define PFM_CMD_FD 0x01 /* command requires a file descriptor */ +#define PFM_CMD_ARG_READ 0x02 /* command must read argument(s) */ +#define PFM_CMD_ARG_RW 0x04 /* command must read/write argument(s) */ +#define PFM_CMD_STOP 0x08 /* command does not work on zombie context */ + + +#define PFM_CMD_NAME(cmd) pfm_cmd_tab[(cmd)].cmd_name +#define PFM_CMD_READ_ARG(cmd) (pfm_cmd_tab[(cmd)].cmd_flags & PFM_CMD_ARG_READ) +#define PFM_CMD_RW_ARG(cmd) (pfm_cmd_tab[(cmd)].cmd_flags & PFM_CMD_ARG_RW) +#define PFM_CMD_USE_FD(cmd) (pfm_cmd_tab[(cmd)].cmd_flags & PFM_CMD_FD) +#define PFM_CMD_STOPPED(cmd) (pfm_cmd_tab[(cmd)].cmd_flags & PFM_CMD_STOP) + +#define PFM_CMD_ARG_MANY -1 /* cannot be zero */ + +typedef struct { + unsigned long pfm_spurious_ovfl_intr_count; /* keep track of spurious ovfl interrupts */ + unsigned long pfm_replay_ovfl_intr_count; /* keep track of replayed ovfl interrupts */ + unsigned long pfm_ovfl_intr_count; /* keep track of ovfl interrupts */ + unsigned long pfm_ovfl_intr_cycles; /* cycles spent processing ovfl interrupts */ + unsigned long pfm_ovfl_intr_cycles_min; /* min cycles spent processing ovfl interrupts */ + unsigned long pfm_ovfl_intr_cycles_max; /* max cycles spent processing ovfl interrupts */ + unsigned long pfm_smpl_handler_calls; + unsigned long pfm_smpl_handler_cycles; + char pad[SMP_CACHE_BYTES] ____cacheline_aligned; +} pfm_stats_t; + +/* + * perfmon internal variables + */ +static pfm_stats_t pfm_stats[NR_CPUS]; +static pfm_session_t pfm_sessions; /* global sessions information */ + +static DEFINE_SPINLOCK(pfm_alt_install_check); +static pfm_intr_handler_desc_t *pfm_alt_intr_handler; + +static struct proc_dir_entry *perfmon_dir; +static pfm_uuid_t pfm_null_uuid = {0,}; + +static spinlock_t pfm_buffer_fmt_lock; +static LIST_HEAD(pfm_buffer_fmt_list); + +static pmu_config_t *pmu_conf; + +/* sysctl() controls */ +pfm_sysctl_t pfm_sysctl; +EXPORT_SYMBOL(pfm_sysctl); + +static ctl_table pfm_ctl_table[]={ + {1, "debug", &pfm_sysctl.debug, sizeof(int), 0666, NULL, &proc_dointvec, NULL,}, + {2, "debug_ovfl", &pfm_sysctl.debug_ovfl, sizeof(int), 0666, NULL, &proc_dointvec, NULL,}, + {3, "fastctxsw", &pfm_sysctl.fastctxsw, sizeof(int), 0600, NULL, &proc_dointvec, NULL,}, + {4, "expert_mode", &pfm_sysctl.expert_mode, sizeof(int), 0600, NULL, &proc_dointvec, NULL,}, + { 0, }, +}; +static ctl_table pfm_sysctl_dir[] = { + {1, "perfmon", NULL, 0, 0755, pfm_ctl_table, }, + {0,}, +}; +static ctl_table pfm_sysctl_root[] = { + {1, "kernel", NULL, 0, 0755, pfm_sysctl_dir, }, + {0,}, +}; +static struct ctl_table_header *pfm_sysctl_header; + +static int pfm_context_unload(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs); +static int pfm_flush(struct file *filp); + +#define pfm_get_cpu_var(v) __ia64_per_cpu_var(v) +#define pfm_get_cpu_data(a,b) per_cpu(a, b) + +static inline void +pfm_put_task(struct task_struct *task) +{ + if (task != current) put_task_struct(task); +} + +static inline void +pfm_set_task_notify(struct task_struct *task) +{ + struct thread_info *info; + + info = (struct thread_info *) ((char *) task + IA64_TASK_SIZE); + set_bit(TIF_NOTIFY_RESUME, &info->flags); +} + +static inline void +pfm_clear_task_notify(void) +{ + clear_thread_flag(TIF_NOTIFY_RESUME); +} + +static inline void +pfm_reserve_page(unsigned long a) +{ + SetPageReserved(vmalloc_to_page((void *)a)); +} +static inline void +pfm_unreserve_page(unsigned long a) +{ + ClearPageReserved(vmalloc_to_page((void*)a)); +} + +static inline unsigned long +pfm_protect_ctx_ctxsw(pfm_context_t *x) +{ + spin_lock(&(x)->ctx_lock); + return 0UL; +} + +static inline void +pfm_unprotect_ctx_ctxsw(pfm_context_t *x, unsigned long f) +{ + spin_unlock(&(x)->ctx_lock); +} + +static inline unsigned int +pfm_do_munmap(struct mm_struct *mm, unsigned long addr, size_t len, int acct) +{ + return do_munmap(mm, addr, len); +} + +static inline unsigned long +pfm_get_unmapped_area(struct file *file, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags, unsigned long exec) +{ + return get_unmapped_area(file, addr, len, pgoff, flags); +} + + +static struct super_block * +pfmfs_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) +{ + return get_sb_pseudo(fs_type, "pfm:", NULL, PFMFS_MAGIC); +} + +static struct file_system_type pfm_fs_type = { + .name = "pfmfs", + .get_sb = pfmfs_get_sb, + .kill_sb = kill_anon_super, +}; + +DEFINE_PER_CPU(unsigned long, pfm_syst_info); +DEFINE_PER_CPU(struct task_struct *, pmu_owner); +DEFINE_PER_CPU(pfm_context_t *, pmu_ctx); +DEFINE_PER_CPU(unsigned long, pmu_activation_number); +EXPORT_PER_CPU_SYMBOL_GPL(pfm_syst_info); + + +/* forward declaration */ +static struct file_operations pfm_file_ops; + +/* + * forward declarations + */ +#ifndef CONFIG_SMP +static void pfm_lazy_save_regs (struct task_struct *ta); +#endif + +void dump_pmu_state(const char *); +static int pfm_write_ibr_dbr(int mode, pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs); + +#include "perfmon_itanium.h" +#include "perfmon_mckinley.h" +#include "perfmon_montecito.h" +#include "perfmon_generic.h" + +static pmu_config_t *pmu_confs[]={ + &pmu_conf_mont, + &pmu_conf_mck, + &pmu_conf_ita, + &pmu_conf_gen, /* must be last */ + NULL +}; + + +static int pfm_end_notify_user(pfm_context_t *ctx); + +static inline void +pfm_clear_psr_pp(void) +{ + ia64_rsm(IA64_PSR_PP); + ia64_srlz_i(); +} + +static inline void +pfm_set_psr_pp(void) +{ + ia64_ssm(IA64_PSR_PP); + ia64_srlz_i(); +} + +static inline void +pfm_clear_psr_up(void) +{ + ia64_rsm(IA64_PSR_UP); + ia64_srlz_i(); +} + +static inline void +pfm_set_psr_up(void) +{ + ia64_ssm(IA64_PSR_UP); + ia64_srlz_i(); +} + +static inline unsigned long +pfm_get_psr(void) +{ + unsigned long tmp; + tmp = ia64_getreg(_IA64_REG_PSR); + ia64_srlz_i(); + return tmp; +} + +static inline void +pfm_set_psr_l(unsigned long val) +{ + ia64_setreg(_IA64_REG_PSR_L, val); + ia64_srlz_i(); +} + +static inline void +pfm_freeze_pmu(void) +{ + ia64_set_pmc(0,1UL); + ia64_srlz_d(); +} + +static inline void +pfm_unfreeze_pmu(void) +{ + ia64_set_pmc(0,0UL); + ia64_srlz_d(); +} + +static inline void +pfm_restore_ibrs(unsigned long *ibrs, unsigned int nibrs) +{ + int i; + + for (i=0; i < nibrs; i++) { + ia64_set_ibr(i, ibrs[i]); + ia64_dv_serialize_instruction(); + } + ia64_srlz_i(); +} + +static inline void +pfm_restore_dbrs(unsigned long *dbrs, unsigned int ndbrs) +{ + int i; + + for (i=0; i < ndbrs; i++) { + ia64_set_dbr(i, dbrs[i]); + ia64_dv_serialize_data(); + } + ia64_srlz_d(); +} + +/* + * PMD[i] must be a counter. no check is made + */ +static inline unsigned long +pfm_read_soft_counter(pfm_context_t *ctx, int i) +{ + return ctx->ctx_pmds[i].val + (ia64_get_pmd(i) & pmu_conf->ovfl_val); +} + +/* + * PMD[i] must be a counter. no check is made + */ +static inline void +pfm_write_soft_counter(pfm_context_t *ctx, int i, unsigned long val) +{ + unsigned long ovfl_val = pmu_conf->ovfl_val; + + ctx->ctx_pmds[i].val = val & ~ovfl_val; + /* + * writing to unimplemented part is ignore, so we do not need to + * mask off top part + */ + ia64_set_pmd(i, val & ovfl_val); +} + +static pfm_msg_t * +pfm_get_new_msg(pfm_context_t *ctx) +{ + int idx, next; + + next = (ctx->ctx_msgq_tail+1) % PFM_MAX_MSGS; + + DPRINT(("ctx_fd=%p head=%d tail=%d\n", ctx, ctx->ctx_msgq_head, ctx->ctx_msgq_tail)); + if (next == ctx->ctx_msgq_head) return NULL; + + idx = ctx->ctx_msgq_tail; + ctx->ctx_msgq_tail = next; + + DPRINT(("ctx=%p head=%d tail=%d msg=%d\n", ctx, ctx->ctx_msgq_head, ctx->ctx_msgq_tail, idx)); + + return ctx->ctx_msgq+idx; +} + +static pfm_msg_t * +pfm_get_next_msg(pfm_context_t *ctx) +{ + pfm_msg_t *msg; + + DPRINT(("ctx=%p head=%d tail=%d\n", ctx, ctx->ctx_msgq_head, ctx->ctx_msgq_tail)); + + if (PFM_CTXQ_EMPTY(ctx)) return NULL; + + /* + * get oldest message + */ + msg = ctx->ctx_msgq+ctx->ctx_msgq_head; + + /* + * and move forward + */ + ctx->ctx_msgq_head = (ctx->ctx_msgq_head+1) % PFM_MAX_MSGS; + + DPRINT(("ctx=%p head=%d tail=%d type=%d\n", ctx, ctx->ctx_msgq_head, ctx->ctx_msgq_tail, msg->pfm_gen_msg.msg_type)); + + return msg; +} + +static void +pfm_reset_msgq(pfm_context_t *ctx) +{ + ctx->ctx_msgq_head = ctx->ctx_msgq_tail = 0; + DPRINT(("ctx=%p msgq reset\n", ctx)); +} + +static void * +pfm_rvmalloc(unsigned long size) +{ + void *mem; + unsigned long addr; + + size = PAGE_ALIGN(size); + mem = vmalloc(size); + if (mem) { + //printk("perfmon: CPU%d pfm_rvmalloc(%ld)=%p\n", smp_processor_id(), size, mem); + memset(mem, 0, size); + addr = (unsigned long)mem; + while (size > 0) { + pfm_reserve_page(addr); + addr+=PAGE_SIZE; + size-=PAGE_SIZE; + } + } + return mem; +} + +static void +pfm_rvfree(void *mem, unsigned long size) +{ + unsigned long addr; + + if (mem) { + DPRINT(("freeing physical buffer @%p size=%lu\n", mem, size)); + addr = (unsigned long) mem; + while ((long) size > 0) { + pfm_unreserve_page(addr); + addr+=PAGE_SIZE; + size-=PAGE_SIZE; + } + vfree(mem); + } + return; +} + +static pfm_context_t * +pfm_context_alloc(void) +{ + pfm_context_t *ctx; + + /* + * allocate context descriptor + * must be able to free with interrupts disabled + */ + ctx = kmalloc(sizeof(pfm_context_t), GFP_KERNEL); + if (ctx) { + memset(ctx, 0, sizeof(pfm_context_t)); + DPRINT(("alloc ctx @%p\n", ctx)); + } + return ctx; +} + +static void +pfm_context_free(pfm_context_t *ctx) +{ + if (ctx) { + DPRINT(("free ctx @%p\n", ctx)); + kfree(ctx); + } +} + +static void +pfm_mask_monitoring(struct task_struct *task) +{ + pfm_context_t *ctx = PFM_GET_CTX(task); + struct thread_struct *th = &task->thread; + unsigned long mask, val, ovfl_mask; + int i; + + DPRINT_ovfl(("masking monitoring for [%d]\n", task->pid)); + + ovfl_mask = pmu_conf->ovfl_val; + /* + * monitoring can only be masked as a result of a valid + * counter overflow. In UP, it means that the PMU still + * has an owner. Note that the owner can be different + * from the current task. However the PMU state belongs + * to the owner. + * In SMP, a valid overflow only happens when task is + * current. Therefore if we come here, we know that + * the PMU state belongs to the current task, therefore + * we can access the live registers. + * + * So in both cases, the live register contains the owner's + * state. We can ONLY touch the PMU registers and NOT the PSR. + * + * As a consequence to this call, the thread->pmds[] array + * contains stale information which must be ignored + * when context is reloaded AND monitoring is active (see + * pfm_restart). + */ + mask = ctx->ctx_used_pmds[0]; + for (i = 0; mask; i++, mask>>=1) { + /* skip non used pmds */ + if ((mask & 0x1) == 0) continue; + val = ia64_get_pmd(i); + + if (PMD_IS_COUNTING(i)) { + /* + * we rebuild the full 64 bit value of the counter + */ + ctx->ctx_pmds[i].val += (val & ovfl_mask); + } else { + ctx->ctx_pmds[i].val = val; + } + DPRINT_ovfl(("pmd[%d]=0x%lx hw_pmd=0x%lx\n", + i, + ctx->ctx_pmds[i].val, + val & ovfl_mask)); + } + /* + * mask monitoring by setting the privilege level to 0 + * we cannot use psr.pp/psr.up for this, it is controlled by + * the user + * + * if task is current, modify actual registers, otherwise modify + * thread save state, i.e., what will be restored in pfm_load_regs() + */ + mask = ctx->ctx_used_monitors[0] >> PMU_FIRST_COUNTER; + for(i= PMU_FIRST_COUNTER; mask; i++, mask>>=1) { + if ((mask & 0x1) == 0UL) continue; + ia64_set_pmc(i, th->pmcs[i] & ~0xfUL); + th->pmcs[i] &= ~0xfUL; + DPRINT_ovfl(("pmc[%d]=0x%lx\n", i, th->pmcs[i])); + } + /* + * make all of this visible + */ + ia64_srlz_d(); +} + +/* + * must always be done with task == current + * + * context must be in MASKED state when calling + */ +static void +pfm_restore_monitoring(struct task_struct *task) +{ + pfm_context_t *ctx = PFM_GET_CTX(task); + struct thread_struct *th = &task->thread; + unsigned long mask, ovfl_mask; + unsigned long psr, val; + int i, is_system; + + is_system = ctx->ctx_fl_system; + ovfl_mask = pmu_conf->ovfl_val; + + if (task != current) { + printk(KERN_ERR "perfmon.%d: invalid task[%d] current[%d]\n", __LINE__, task->pid, current->pid); + return; + } + if (ctx->ctx_state != PFM_CTX_MASKED) { + printk(KERN_ERR "perfmon.%d: task[%d] current[%d] invalid state=%d\n", __LINE__, + task->pid, current->pid, ctx->ctx_state); + return; + } + psr = pfm_get_psr(); + /* + * monitoring is masked via the PMC. + * As we restore their value, we do not want each counter to + * restart right away. We stop monitoring using the PSR, + * restore the PMC (and PMD) and then re-establish the psr + * as it was. Note that there can be no pending overflow at + * this point, because monitoring was MASKED. + * + * system-wide session are pinned and self-monitoring + */ + if (is_system && (PFM_CPUINFO_GET() & PFM_CPUINFO_DCR_PP)) { + /* disable dcr pp */ + ia64_setreg(_IA64_REG_CR_DCR, ia64_getreg(_IA64_REG_CR_DCR) & ~IA64_DCR_PP); + pfm_clear_psr_pp(); + } else { + pfm_clear_psr_up(); + } + /* + * first, we restore the PMD + */ + mask = ctx->ctx_used_pmds[0]; + for (i = 0; mask; i++, mask>>=1) { + /* skip non used pmds */ + if ((mask & 0x1) == 0) continue; + + if (PMD_IS_COUNTING(i)) { + /* + * we split the 64bit value according to + * counter width + */ + val = ctx->ctx_pmds[i].val & ovfl_mask; + ctx->ctx_pmds[i].val &= ~ovfl_mask; + } else { + val = ctx->ctx_pmds[i].val; + } + ia64_set_pmd(i, val); + + DPRINT(("pmd[%d]=0x%lx hw_pmd=0x%lx\n", + i, + ctx->ctx_pmds[i].val, + val)); + } + /* + * restore the PMCs + */ + mask = ctx->ctx_used_monitors[0] >> PMU_FIRST_COUNTER; + for(i= PMU_FIRST_COUNTER; mask; i++, mask>>=1) { + if ((mask & 0x1) == 0UL) continue; + th->pmcs[i] = ctx->ctx_pmcs[i]; + ia64_set_pmc(i, th->pmcs[i]); + DPRINT(("[%d] pmc[%d]=0x%lx\n", task->pid, i, th->pmcs[i])); + } + ia64_srlz_d(); + + /* + * must restore DBR/IBR because could be modified while masked + * XXX: need to optimize + */ + if (ctx->ctx_fl_using_dbreg) { + pfm_restore_ibrs(ctx->ctx_ibrs, pmu_conf->num_ibrs); + pfm_restore_dbrs(ctx->ctx_dbrs, pmu_conf->num_dbrs); + } + + /* + * now restore PSR + */ + if (is_system && (PFM_CPUINFO_GET() & PFM_CPUINFO_DCR_PP)) { + /* enable dcr pp */ + ia64_setreg(_IA64_REG_CR_DCR, ia64_getreg(_IA64_REG_CR_DCR) | IA64_DCR_PP); + ia64_srlz_i(); + } + pfm_set_psr_l(psr); +} + +static inline void +pfm_save_pmds(unsigned long *pmds, unsigned long mask) +{ + int i; + + ia64_srlz_d(); + + for (i=0; mask; i++, mask>>=1) { + if (mask & 0x1) pmds[i] = ia64_get_pmd(i); + } +} + +/* + * reload from thread state (used for ctxw only) + */ +static inline void +pfm_restore_pmds(unsigned long *pmds, unsigned long mask) +{ + int i; + unsigned long val, ovfl_val = pmu_conf->ovfl_val; + + for (i=0; mask; i++, mask>>=1) { + if ((mask & 0x1) == 0) continue; + val = PMD_IS_COUNTING(i) ? pmds[i] & ovfl_val : pmds[i]; + ia64_set_pmd(i, val); + } + ia64_srlz_d(); +} + +/* + * propagate PMD from context to thread-state + */ +static inline void +pfm_copy_pmds(struct task_struct *task, pfm_context_t *ctx) +{ + struct thread_struct *thread = &task->thread; + unsigned long ovfl_val = pmu_conf->ovfl_val; + unsigned long mask = ctx->ctx_all_pmds[0]; + unsigned long val; + int i; + + DPRINT(("mask=0x%lx\n", mask)); + + for (i=0; mask; i++, mask>>=1) { + + val = ctx->ctx_pmds[i].val; + + /* + * We break up the 64 bit value into 2 pieces + * the lower bits go to the machine state in the + * thread (will be reloaded on ctxsw in). + * The upper part stays in the soft-counter. + */ + if (PMD_IS_COUNTING(i)) { + ctx->ctx_pmds[i].val = val & ~ovfl_val; + val &= ovfl_val; + } + thread->pmds[i] = val; + + DPRINT(("pmd[%d]=0x%lx soft_val=0x%lx\n", + i, + thread->pmds[i], + ctx->ctx_pmds[i].val)); + } +} + +/* + * propagate PMC from context to thread-state + */ +static inline void +pfm_copy_pmcs(struct task_struct *task, pfm_context_t *ctx) +{ + struct thread_struct *thread = &task->thread; + unsigned long mask = ctx->ctx_all_pmcs[0]; + int i; + + DPRINT(("mask=0x%lx\n", mask)); + + for (i=0; mask; i++, mask>>=1) { + /* masking 0 with ovfl_val yields 0 */ + thread->pmcs[i] = ctx->ctx_pmcs[i]; + DPRINT(("pmc[%d]=0x%lx\n", i, thread->pmcs[i])); + } +} + + + +static inline void +pfm_restore_pmcs(unsigned long *pmcs, unsigned long mask) +{ + int i; + + for (i=0; mask; i++, mask>>=1) { + if ((mask & 0x1) == 0) continue; + ia64_set_pmc(i, pmcs[i]); + } + ia64_srlz_d(); +} + +static inline int +pfm_uuid_cmp(pfm_uuid_t a, pfm_uuid_t b) +{ + return memcmp(a, b, sizeof(pfm_uuid_t)); +} + +static inline int +pfm_buf_fmt_exit(pfm_buffer_fmt_t *fmt, struct task_struct *task, void *buf, struct pt_regs *regs) +{ + int ret = 0; + if (fmt->fmt_exit) ret = (*fmt->fmt_exit)(task, buf, regs); + return ret; +} + +static inline int +pfm_buf_fmt_getsize(pfm_buffer_fmt_t *fmt, struct task_struct *task, unsigned int flags, int cpu, void *arg, unsigned long *size) +{ + int ret = 0; + if (fmt->fmt_getsize) ret = (*fmt->fmt_getsize)(task, flags, cpu, arg, size); + return ret; +} + + +static inline int +pfm_buf_fmt_validate(pfm_buffer_fmt_t *fmt, struct task_struct *task, unsigned int flags, + int cpu, void *arg) +{ + int ret = 0; + if (fmt->fmt_validate) ret = (*fmt->fmt_validate)(task, flags, cpu, arg); + return ret; +} + +static inline int +pfm_buf_fmt_init(pfm_buffer_fmt_t *fmt, struct task_struct *task, void *buf, unsigned int flags, + int cpu, void *arg) +{ + int ret = 0; + if (fmt->fmt_init) ret = (*fmt->fmt_init)(task, buf, flags, cpu, arg); + return ret; +} + +static inline int +pfm_buf_fmt_restart(pfm_buffer_fmt_t *fmt, struct task_struct *task, pfm_ovfl_ctrl_t *ctrl, void *buf, struct pt_regs *regs) +{ + int ret = 0; + if (fmt->fmt_restart) ret = (*fmt->fmt_restart)(task, ctrl, buf, regs); + return ret; +} + +static inline int +pfm_buf_fmt_restart_active(pfm_buffer_fmt_t *fmt, struct task_struct *task, pfm_ovfl_ctrl_t *ctrl, void *buf, struct pt_regs *regs) +{ + int ret = 0; + if (fmt->fmt_restart_active) ret = (*fmt->fmt_restart_active)(task, ctrl, buf, regs); + return ret; +} + +static pfm_buffer_fmt_t * +__pfm_find_buffer_fmt(pfm_uuid_t uuid) +{ + struct list_head * pos; + pfm_buffer_fmt_t * entry; + + list_for_each(pos, &pfm_buffer_fmt_list) { + entry = list_entry(pos, pfm_buffer_fmt_t, fmt_list); + if (pfm_uuid_cmp(uuid, entry->fmt_uuid) == 0) + return entry; + } + return NULL; +} + +/* + * find a buffer format based on its uuid + */ +static pfm_buffer_fmt_t * +pfm_find_buffer_fmt(pfm_uuid_t uuid) +{ + pfm_buffer_fmt_t * fmt; + spin_lock(&pfm_buffer_fmt_lock); + fmt = __pfm_find_buffer_fmt(uuid); + spin_unlock(&pfm_buffer_fmt_lock); + return fmt; +} + +int +pfm_register_buffer_fmt(pfm_buffer_fmt_t *fmt) +{ + int ret = 0; + + /* some sanity checks */ + if (fmt == NULL || fmt->fmt_name == NULL) return -EINVAL; + + /* we need at least a handler */ + if (fmt->fmt_handler == NULL) return -EINVAL; + + /* + * XXX: need check validity of fmt_arg_size + */ + + spin_lock(&pfm_buffer_fmt_lock); + + if (__pfm_find_buffer_fmt(fmt->fmt_uuid)) { + printk(KERN_ERR "perfmon: duplicate sampling format: %s\n", fmt->fmt_name); + ret = -EBUSY; + goto out; + } + list_add(&fmt->fmt_list, &pfm_buffer_fmt_list); + printk(KERN_INFO "perfmon: added sampling format %s\n", fmt->fmt_name); + +out: + spin_unlock(&pfm_buffer_fmt_lock); + return ret; +} +EXPORT_SYMBOL(pfm_register_buffer_fmt); + +int +pfm_unregister_buffer_fmt(pfm_uuid_t uuid) +{ + pfm_buffer_fmt_t *fmt; + int ret = 0; + + spin_lock(&pfm_buffer_fmt_lock); + + fmt = __pfm_find_buffer_fmt(uuid); + if (!fmt) { + printk(KERN_ERR "perfmon: cannot unregister format, not found\n"); + ret = -EINVAL; + goto out; + } + list_del_init(&fmt->fmt_list); + printk(KERN_INFO "perfmon: removed sampling format: %s\n", fmt->fmt_name); + +out: + spin_unlock(&pfm_buffer_fmt_lock); + return ret; + +} +EXPORT_SYMBOL(pfm_unregister_buffer_fmt); + +extern void update_pal_halt_status(int); + +static int +pfm_reserve_session(struct task_struct *task, int is_syswide, unsigned int cpu) +{ + unsigned long flags; + /* + * validy checks on cpu_mask have been done upstream + */ + LOCK_PFS(flags); + + DPRINT(("in sys_sessions=%u task_sessions=%u dbregs=%u syswide=%d cpu=%u\n", + pfm_sessions.pfs_sys_sessions, + pfm_sessions.pfs_task_sessions, + pfm_sessions.pfs_sys_use_dbregs, + is_syswide, + cpu)); + + if (is_syswide) { + /* + * cannot mix system wide and per-task sessions + */ + if (pfm_sessions.pfs_task_sessions > 0UL) { + DPRINT(("system wide not possible, %u conflicting task_sessions\n", + pfm_sessions.pfs_task_sessions)); + goto abort; + } + + if (pfm_sessions.pfs_sys_session[cpu]) goto error_conflict; + + DPRINT(("reserving system wide session on CPU%u currently on CPU%u\n", cpu, smp_processor_id())); + + pfm_sessions.pfs_sys_session[cpu] = task; + + pfm_sessions.pfs_sys_sessions++ ; + + } else { + if (pfm_sessions.pfs_sys_sessions) goto abort; + pfm_sessions.pfs_task_sessions++; + } + + DPRINT(("out sys_sessions=%u task_sessions=%u dbregs=%u syswide=%d cpu=%u\n", + pfm_sessions.pfs_sys_sessions, + pfm_sessions.pfs_task_sessions, + pfm_sessions.pfs_sys_use_dbregs, + is_syswide, + cpu)); + + /* + * disable default_idle() to go to PAL_HALT + */ + update_pal_halt_status(0); + + UNLOCK_PFS(flags); + + return 0; + +error_conflict: + DPRINT(("system wide not possible, conflicting session [%d] on CPU%d\n", + pfm_sessions.pfs_sys_session[cpu]->pid, + cpu)); +abort: + UNLOCK_PFS(flags); + + return -EBUSY; + +} + +static int +pfm_unreserve_session(pfm_context_t *ctx, int is_syswide, unsigned int cpu) +{ + unsigned long flags; + /* + * validy checks on cpu_mask have been done upstream + */ + LOCK_PFS(flags); + + DPRINT(("in sys_sessions=%u task_sessions=%u dbregs=%u syswide=%d cpu=%u\n", + pfm_sessions.pfs_sys_sessions, + pfm_sessions.pfs_task_sessions, + pfm_sessions.pfs_sys_use_dbregs, + is_syswide, + cpu)); + + + if (is_syswide) { + pfm_sessions.pfs_sys_session[cpu] = NULL; + /* + * would not work with perfmon+more than one bit in cpu_mask + */ + if (ctx && ctx->ctx_fl_using_dbreg) { + if (pfm_sessions.pfs_sys_use_dbregs == 0) { + printk(KERN_ERR "perfmon: invalid release for ctx %p sys_use_dbregs=0\n", ctx); + } else { + pfm_sessions.pfs_sys_use_dbregs--; + } + } + pfm_sessions.pfs_sys_sessions--; + } else { + pfm_sessions.pfs_task_sessions--; + } + DPRINT(("out sys_sessions=%u task_sessions=%u dbregs=%u syswide=%d cpu=%u\n", + pfm_sessions.pfs_sys_sessions, + pfm_sessions.pfs_task_sessions, + pfm_sessions.pfs_sys_use_dbregs, + is_syswide, + cpu)); + + /* + * if possible, enable default_idle() to go into PAL_HALT + */ + if (pfm_sessions.pfs_task_sessions == 0 && pfm_sessions.pfs_sys_sessions == 0) + update_pal_halt_status(1); + + UNLOCK_PFS(flags); + + return 0; +} + +/* + * removes virtual mapping of the sampling buffer. + * IMPORTANT: cannot be called with interrupts disable, e.g. inside + * a PROTECT_CTX() section. + */ +static int +pfm_remove_smpl_mapping(struct task_struct *task, void *vaddr, unsigned long size) +{ + int r; + + /* sanity checks */ + if (task->mm == NULL || size == 0UL || vaddr == NULL) { + printk(KERN_ERR "perfmon: pfm_remove_smpl_mapping [%d] invalid context mm=%p\n", task->pid, task->mm); + return -EINVAL; + } + + DPRINT(("smpl_vaddr=%p size=%lu\n", vaddr, size)); + + /* + * does the actual unmapping + */ + down_write(&task->mm->mmap_sem); + + DPRINT(("down_write done smpl_vaddr=%p size=%lu\n", vaddr, size)); + + r = pfm_do_munmap(task->mm, (unsigned long)vaddr, size, 0); + + up_write(&task->mm->mmap_sem); + if (r !=0) { + printk(KERN_ERR "perfmon: [%d] unable to unmap sampling buffer @%p size=%lu\n", task->pid, vaddr, size); + } + + DPRINT(("do_unmap(%p, %lu)=%d\n", vaddr, size, r)); + + return 0; +} + +/* + * free actual physical storage used by sampling buffer + */ +#if 0 +static int +pfm_free_smpl_buffer(pfm_context_t *ctx) +{ + pfm_buffer_fmt_t *fmt; + + if (ctx->ctx_smpl_hdr == NULL) goto invalid_free; + + /* + * we won't use the buffer format anymore + */ + fmt = ctx->ctx_buf_fmt; + + DPRINT(("sampling buffer @%p size %lu vaddr=%p\n", + ctx->ctx_smpl_hdr, + ctx->ctx_smpl_size, + ctx->ctx_smpl_vaddr)); + + pfm_buf_fmt_exit(fmt, current, NULL, NULL); + + /* + * free the buffer + */ + pfm_rvfree(ctx->ctx_smpl_hdr, ctx->ctx_smpl_size); + + ctx->ctx_smpl_hdr = NULL; + ctx->ctx_smpl_size = 0UL; + + return 0; + +invalid_free: + printk(KERN_ERR "perfmon: pfm_free_smpl_buffer [%d] no buffer\n", current->pid); + return -EINVAL; +} +#endif + +static inline void +pfm_exit_smpl_buffer(pfm_buffer_fmt_t *fmt) +{ + if (fmt == NULL) return; + + pfm_buf_fmt_exit(fmt, current, NULL, NULL); + +} + +/* + * pfmfs should _never_ be mounted by userland - too much of security hassle, + * no real gain from having the whole whorehouse mounted. So we don't need + * any operations on the root directory. However, we need a non-trivial + * d_name - pfm: will go nicely and kill the special-casing in procfs. + */ +static struct vfsmount *pfmfs_mnt; + +static int __init +init_pfm_fs(void) +{ + int err = register_filesystem(&pfm_fs_type); + if (!err) { + pfmfs_mnt = kern_mount(&pfm_fs_type); + err = PTR_ERR(pfmfs_mnt); + if (IS_ERR(pfmfs_mnt)) + unregister_filesystem(&pfm_fs_type); + else + err = 0; + } + return err; +} + +static void __exit +exit_pfm_fs(void) +{ + unregister_filesystem(&pfm_fs_type); + mntput(pfmfs_mnt); +} + +static ssize_t +pfm_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos) +{ + pfm_context_t *ctx; + pfm_msg_t *msg; + ssize_t ret; + unsigned long flags; + DECLARE_WAITQUEUE(wait, current); + XEN_NOT_SUPPORTED_YET; + if (PFM_IS_FILE(filp) == 0) { + printk(KERN_ERR "perfmon: pfm_poll: bad magic [%d]\n", current->pid); + return -EINVAL; + } + + ctx = (pfm_context_t *)filp->private_data; + if (ctx == NULL) { + printk(KERN_ERR "perfmon: pfm_read: NULL ctx [%d]\n", current->pid); + return -EINVAL; + } + + /* + * check even when there is no message + */ + if (size < sizeof(pfm_msg_t)) { + DPRINT(("message is too small ctx=%p (>=%ld)\n", ctx, sizeof(pfm_msg_t))); + return -EINVAL; + } + + PROTECT_CTX(ctx, flags); + + /* + * put ourselves on the wait queue + */ + add_wait_queue(&ctx->ctx_msgq_wait, &wait); + + + for(;;) { + /* + * check wait queue + */ + + set_current_state(TASK_INTERRUPTIBLE); + + DPRINT(("head=%d tail=%d\n", ctx->ctx_msgq_head, ctx->ctx_msgq_tail)); + + ret = 0; + if(PFM_CTXQ_EMPTY(ctx) == 0) break; + + UNPROTECT_CTX(ctx, flags); + + /* + * check non-blocking read + */ + ret = -EAGAIN; + if(filp->f_flags & O_NONBLOCK) break; + + /* + * check pending signals + */ + if(signal_pending(current)) { + ret = -EINTR; + break; + } + /* + * no message, so wait + */ + schedule(); + + PROTECT_CTX(ctx, flags); + } + DPRINT(("[%d] back to running ret=%ld\n", current->pid, ret)); + set_current_state(TASK_RUNNING); + remove_wait_queue(&ctx->ctx_msgq_wait, &wait); + + if (ret < 0) goto abort; + + ret = -EINVAL; + msg = pfm_get_next_msg(ctx); + if (msg == NULL) { + printk(KERN_ERR "perfmon: pfm_read no msg for ctx=%p [%d]\n", ctx, current->pid); + goto abort_locked; + } + + DPRINT(("fd=%d type=%d\n", msg->pfm_gen_msg.msg_ctx_fd, msg->pfm_gen_msg.msg_type)); + + ret = -EFAULT; + if(copy_to_user(buf, msg, sizeof(pfm_msg_t)) == 0) ret = sizeof(pfm_msg_t); + +abort_locked: + UNPROTECT_CTX(ctx, flags); +abort: + return ret; +} + +static ssize_t +pfm_write(struct file *file, const char __user *ubuf, + size_t size, loff_t *ppos) +{ + DPRINT(("pfm_write called\n")); + return -EINVAL; +} + +static unsigned int +pfm_poll(struct file *filp, poll_table * wait) +{ + pfm_context_t *ctx; + unsigned long flags; + unsigned int mask = 0; + + if (PFM_IS_FILE(filp) == 0) { + printk(KERN_ERR "perfmon: pfm_poll: bad magic [%d]\n", current->pid); + return 0; + } + + ctx = (pfm_context_t *)filp->private_data; + if (ctx == NULL) { + printk(KERN_ERR "perfmon: pfm_poll: NULL ctx [%d]\n", current->pid); + return 0; + } + + + DPRINT(("pfm_poll ctx_fd=%d before poll_wait\n", ctx->ctx_fd)); + + poll_wait(filp, &ctx->ctx_msgq_wait, wait); + + PROTECT_CTX(ctx, flags); + + if (PFM_CTXQ_EMPTY(ctx) == 0) + mask = POLLIN | POLLRDNORM; + + UNPROTECT_CTX(ctx, flags); + + DPRINT(("pfm_poll ctx_fd=%d mask=0x%x\n", ctx->ctx_fd, mask)); + + return mask; +} + +static int +pfm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + DPRINT(("pfm_ioctl called\n")); + return -EINVAL; +} + +/* + * interrupt cannot be masked when coming here + */ +static inline int +pfm_do_fasync(int fd, struct file *filp, pfm_context_t *ctx, int on) +{ + int ret; + + ret = fasync_helper (fd, filp, on, &ctx->ctx_async_queue); + + DPRINT(("pfm_fasync called by [%d] on ctx_fd=%d on=%d async_queue=%p ret=%d\n", + current->pid, + fd, + on, + ctx->ctx_async_queue, ret)); + + return ret; +} + +static int +pfm_fasync(int fd, struct file *filp, int on) +{ + pfm_context_t *ctx; + int ret; + + if (PFM_IS_FILE(filp) == 0) { + printk(KERN_ERR "perfmon: pfm_fasync bad magic [%d]\n", current->pid); + return -EBADF; + } + + ctx = (pfm_context_t *)filp->private_data; + if (ctx == NULL) { + printk(KERN_ERR "perfmon: pfm_fasync NULL ctx [%d]\n", current->pid); + return -EBADF; + } + /* + * we cannot mask interrupts during this call because this may + * may go to sleep if memory is not readily avalaible. + * + * We are protected from the conetxt disappearing by the get_fd()/put_fd() + * done in caller. Serialization of this function is ensured by caller. + */ + ret = pfm_do_fasync(fd, filp, ctx, on); + + + DPRINT(("pfm_fasync called on ctx_fd=%d on=%d async_queue=%p ret=%d\n", + fd, + on, + ctx->ctx_async_queue, ret)); + + return ret; +} + +#ifdef CONFIG_SMP +/* + * this function is exclusively called from pfm_close(). + * The context is not protected at that time, nor are interrupts + * on the remote CPU. That's necessary to avoid deadlocks. + */ +static void +pfm_syswide_force_stop(void *info) +{ + pfm_context_t *ctx = (pfm_context_t *)info; + struct pt_regs *regs = task_pt_regs(current); + struct task_struct *owner; + unsigned long flags; + int ret; + + if (ctx->ctx_cpu != smp_processor_id()) { + printk(KERN_ERR "perfmon: pfm_syswide_force_stop for CPU%d but on CPU%d\n", + ctx->ctx_cpu, + smp_processor_id()); + return; + } + owner = GET_PMU_OWNER(); + if (owner != ctx->ctx_task) { + printk(KERN_ERR "perfmon: pfm_syswide_force_stop CPU%d unexpected owner [%d] instead of [%d]\n", + smp_processor_id(), + owner->pid, ctx->ctx_task->pid); + return; + } + if (GET_PMU_CTX() != ctx) { + printk(KERN_ERR "perfmon: pfm_syswide_force_stop CPU%d unexpected ctx %p instead of %p\n", + smp_processor_id(), + GET_PMU_CTX(), ctx); + return; + } + + DPRINT(("on CPU%d forcing system wide stop for [%d]\n", smp_processor_id(), ctx->ctx_task->pid)); + /* + * the context is already protected in pfm_close(), we simply + * need to mask interrupts to avoid a PMU interrupt race on + * this CPU + */ + local_irq_save(flags); + + ret = pfm_context_unload(ctx, NULL, 0, regs); + if (ret) { + DPRINT(("context_unload returned %d\n", ret)); + } + + /* + * unmask interrupts, PMU interrupts are now spurious here + */ + local_irq_restore(flags); +} + +static void +pfm_syswide_cleanup_other_cpu(pfm_context_t *ctx) +{ + int ret; + + DPRINT(("calling CPU%d for cleanup\n", ctx->ctx_cpu)); + ret = smp_call_function_single(ctx->ctx_cpu, pfm_syswide_force_stop, ctx, 0, 1); + DPRINT(("called CPU%d for cleanup ret=%d\n", ctx->ctx_cpu, ret)); +} +#endif /* CONFIG_SMP */ + +/* + * called for each close(). Partially free resources. + * When caller is self-monitoring, the context is unloaded. + */ +static int +pfm_flush(struct file *filp) +{ + pfm_context_t *ctx; + struct task_struct *task; + struct pt_regs *regs; + unsigned long flags; + unsigned long smpl_buf_size = 0UL; + void *smpl_buf_vaddr = NULL; + int state, is_system; + + if (PFM_IS_FILE(filp) == 0) { + DPRINT(("bad magic for\n")); + return -EBADF; + } + + ctx = (pfm_context_t *)filp->private_data; + if (ctx == NULL) { + printk(KERN_ERR "perfmon: pfm_flush: NULL ctx [%d]\n", current->pid); + return -EBADF; + } + + /* + * remove our file from the async queue, if we use this mode. + * This can be done without the context being protected. We come + * here when the context has become unreacheable by other tasks. + * + * We may still have active monitoring at this point and we may + * end up in pfm_overflow_handler(). However, fasync_helper() + * operates with interrupts disabled and it cleans up the + * queue. If the PMU handler is called prior to entering + * fasync_helper() then it will send a signal. If it is + * invoked after, it will find an empty queue and no + * signal will be sent. In both case, we are safe + */ + if (filp->f_flags & FASYNC) { + DPRINT(("cleaning up async_queue=%p\n", ctx->ctx_async_queue)); + pfm_do_fasync (-1, filp, ctx, 0); + } + + PROTECT_CTX(ctx, flags); + + state = ctx->ctx_state; + is_system = ctx->ctx_fl_system; + + task = PFM_CTX_TASK(ctx); + regs = task_pt_regs(task); + + DPRINT(("ctx_state=%d is_current=%d\n", + state, + task == current ? 1 : 0)); + + /* + * if state == UNLOADED, then task is NULL + */ + + /* + * we must stop and unload because we are losing access to the context. + */ + if (task == current) { +#ifdef CONFIG_SMP + /* + * the task IS the owner but it migrated to another CPU: that's bad + * but we must handle this cleanly. Unfortunately, the kernel does + * not provide a mechanism to block migration (while the context is loaded). + * + * We need to release the resource on the ORIGINAL cpu. + */ + if (is_system && ctx->ctx_cpu != smp_processor_id()) { + + DPRINT(("should be running on CPU%d\n", ctx->ctx_cpu)); + /* + * keep context protected but unmask interrupt for IPI + */ + local_irq_restore(flags); + + pfm_syswide_cleanup_other_cpu(ctx); + + /* + * restore interrupt masking + */ + local_irq_save(flags); + + /* + * context is unloaded at this point + */ + } else +#endif /* CONFIG_SMP */ + { + + DPRINT(("forcing unload\n")); + /* + * stop and unload, returning with state UNLOADED + * and session unreserved. + */ + pfm_context_unload(ctx, NULL, 0, regs); + + DPRINT(("ctx_state=%d\n", ctx->ctx_state)); + } + } + + /* + * remove virtual mapping, if any, for the calling task. + * cannot reset ctx field until last user is calling close(). + * + * ctx_smpl_vaddr must never be cleared because it is needed + * by every task with access to the context + * + * When called from do_exit(), the mm context is gone already, therefore + * mm is NULL, i.e., the VMA is already gone and we do not have to + * do anything here + */ + if (ctx->ctx_smpl_vaddr && current->mm) { + smpl_buf_vaddr = ctx->ctx_smpl_vaddr; + smpl_buf_size = ctx->ctx_smpl_size; + } + + UNPROTECT_CTX(ctx, flags); + + /* + * if there was a mapping, then we systematically remove it + * at this point. Cannot be done inside critical section + * because some VM function reenables interrupts. + * + */ + if (smpl_buf_vaddr) pfm_remove_smpl_mapping(current, smpl_buf_vaddr, smpl_buf_size); + + return 0; +} +/* + * called either on explicit close() or from exit_files(). + * Only the LAST user of the file gets to this point, i.e., it is + * called only ONCE. + * + * IMPORTANT: we get called ONLY when the refcnt on the file gets to zero + * (fput()),i.e, last task to access the file. Nobody else can access the + * file at this point. + * + * When called from exit_files(), the VMA has been freed because exit_mm() + * is executed before exit_files(). + * + * When called from exit_files(), the current task is not yet ZOMBIE but we + * flush the PMU state to the context. + */ +static int +pfm_close(struct inode *inode, struct file *filp) +{ + pfm_context_t *ctx; + struct task_struct *task; + struct pt_regs *regs; + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + unsigned long smpl_buf_size = 0UL; + void *smpl_buf_addr = NULL; + int free_possible = 1; + int state, is_system; + + DPRINT(("pfm_close called private=%p\n", filp->private_data)); + + if (PFM_IS_FILE(filp) == 0) { + DPRINT(("bad magic\n")); + return -EBADF; + } + + ctx = (pfm_context_t *)filp->private_data; + if (ctx == NULL) { + printk(KERN_ERR "perfmon: pfm_close: NULL ctx [%d]\n", current->pid); + return -EBADF; + } + + PROTECT_CTX(ctx, flags); + + state = ctx->ctx_state; + is_system = ctx->ctx_fl_system; + + task = PFM_CTX_TASK(ctx); + regs = task_pt_regs(task); + + DPRINT(("ctx_state=%d is_current=%d\n", + state, + task == current ? 1 : 0)); + + /* + * if task == current, then pfm_flush() unloaded the context + */ + if (state == PFM_CTX_UNLOADED) goto doit; + + /* + * context is loaded/masked and task != current, we need to + * either force an unload or go zombie + */ + + /* + * The task is currently blocked or will block after an overflow. + * we must force it to wakeup to get out of the + * MASKED state and transition to the unloaded state by itself. + * + * This situation is only possible for per-task mode + */ + if (state == PFM_CTX_MASKED && CTX_OVFL_NOBLOCK(ctx) == 0) { + + /* + * set a "partial" zombie state to be checked + * upon return from down() in pfm_handle_work(). + * + * We cannot use the ZOMBIE state, because it is checked + * by pfm_load_regs() which is called upon wakeup from down(). + * In such case, it would free the context and then we would + * return to pfm_handle_work() which would access the + * stale context. Instead, we set a flag invisible to pfm_load_regs() + * but visible to pfm_handle_work(). + * + * For some window of time, we have a zombie context with + * ctx_state = MASKED and not ZOMBIE + */ + ctx->ctx_fl_going_zombie = 1; + + /* + * force task to wake up from MASKED state + */ + complete(&ctx->ctx_restart_done); + + DPRINT(("waking up ctx_state=%d\n", state)); + + /* + * put ourself to sleep waiting for the other + * task to report completion + * + * the context is protected by mutex, therefore there + * is no risk of being notified of completion before + * begin actually on the waitq. + */ + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&ctx->ctx_zombieq, &wait); + + UNPROTECT_CTX(ctx, flags); + + /* + * XXX: check for signals : + * - ok for explicit close + * - not ok when coming from exit_files() + */ + schedule(); + + + PROTECT_CTX(ctx, flags); + + + remove_wait_queue(&ctx->ctx_zombieq, &wait); + set_current_state(TASK_RUNNING); + + /* + * context is unloaded at this point + */ + DPRINT(("after zombie wakeup ctx_state=%d for\n", state)); + } + else if (task != current) { +#ifdef CONFIG_SMP + /* + * switch context to zombie state + */ + ctx->ctx_state = PFM_CTX_ZOMBIE; + + DPRINT(("zombie ctx for [%d]\n", task->pid)); + /* + * cannot free the context on the spot. deferred until + * the task notices the ZOMBIE state + */ + free_possible = 0; +#else + pfm_context_unload(ctx, NULL, 0, regs); +#endif + } + +doit: + /* reload state, may have changed during opening of critical section */ + state = ctx->ctx_state; + + /* + * the context is still attached to a task (possibly current) + * we cannot destroy it right now + */ + + /* + * we must free the sampling buffer right here because + * we cannot rely on it being cleaned up later by the + * monitored task. It is not possible to free vmalloc'ed + * memory in pfm_load_regs(). Instead, we remove the buffer + * now. should there be subsequent PMU overflow originally + * meant for sampling, the will be converted to spurious + * and that's fine because the monitoring tools is gone anyway. + */ + if (ctx->ctx_smpl_hdr) { + smpl_buf_addr = ctx->ctx_smpl_hdr; + smpl_buf_size = ctx->ctx_smpl_size; + /* no more sampling */ + ctx->ctx_smpl_hdr = NULL; + ctx->ctx_fl_is_sampling = 0; + } + + DPRINT(("ctx_state=%d free_possible=%d addr=%p size=%lu\n", + state, + free_possible, + smpl_buf_addr, + smpl_buf_size)); + + if (smpl_buf_addr) pfm_exit_smpl_buffer(ctx->ctx_buf_fmt); + + /* + * UNLOADED that the session has already been unreserved. + */ + if (state == PFM_CTX_ZOMBIE) { + pfm_unreserve_session(ctx, ctx->ctx_fl_system , ctx->ctx_cpu); + } + + /* + * disconnect file descriptor from context must be done + * before we unlock. + */ + filp->private_data = NULL; + + /* + * if we free on the spot, the context is now completely unreacheable + * from the callers side. The monitored task side is also cut, so we + * can freely cut. + * + * If we have a deferred free, only the caller side is disconnected. + */ + UNPROTECT_CTX(ctx, flags); + + /* + * All memory free operations (especially for vmalloc'ed memory) + * MUST be done with interrupts ENABLED. + */ + if (smpl_buf_addr) pfm_rvfree(smpl_buf_addr, smpl_buf_size); + + /* + * return the memory used by the context + */ + if (free_possible) pfm_context_free(ctx); + + if (is_running_on_xen()) { + if (is_xenoprof_primary()) { + int ret = HYPERVISOR_perfmon_op(PFM_DESTROY_CONTEXT, + NULL, 0); + if (ret) + printk("%s:%d PFM_DESTROY_CONTEXT hypercall " + "failed\n", __func__, __LINE__); + } + } + return 0; +} + +static int +pfm_no_open(struct inode *irrelevant, struct file *dontcare) +{ + DPRINT(("pfm_no_open called\n")); + return -ENXIO; +} + + + +static struct file_operations pfm_file_ops = { + .llseek = no_llseek, + .read = pfm_read, + .write = pfm_write, + .poll = pfm_poll, + .ioctl = pfm_ioctl, + .open = pfm_no_open, /* special open code to disallow open via /proc */ + .fasync = pfm_fasync, + .release = pfm_close, + .flush = pfm_flush +}; + +static int +pfmfs_delete_dentry(struct dentry *dentry) +{ + return 1; +} + +static struct dentry_operations pfmfs_dentry_operations = { + .d_delete = pfmfs_delete_dentry, +}; + + +static int +pfm_alloc_fd(struct file **cfile) +{ + int fd, ret = 0; + struct file *file = NULL; + struct inode * inode; + char name[32]; + struct qstr this; + + fd = get_unused_fd(); + if (fd < 0) return -ENFILE; + + ret = -ENFILE; + + file = get_empty_filp(); + if (!file) goto out; + + /* + * allocate a new inode + */ + inode = new_inode(pfmfs_mnt->mnt_sb); + if (!inode) goto out; + + DPRINT(("new inode ino=%ld @%p\n", inode->i_ino, inode)); + + inode->i_mode = S_IFCHR|S_IRUGO; + inode->i_uid = current->fsuid; + inode->i_gid = current->fsgid; + + sprintf(name, "[%lu]", inode->i_ino); + this.name = name; + this.len = strlen(name); + this.hash = inode->i_ino; + + ret = -ENOMEM; + + /* + * allocate a new dcache entry + */ + file->f_dentry = d_alloc(pfmfs_mnt->mnt_sb->s_root, &this); + if (!file->f_dentry) goto out; + + file->f_dentry->d_op = &pfmfs_dentry_operations; + + d_add(file->f_dentry, inode); + file->f_vfsmnt = mntget(pfmfs_mnt); + file->f_mapping = inode->i_mapping; + + file->f_op = &pfm_file_ops; + file->f_mode = FMODE_READ; + file->f_flags = O_RDONLY; + file->f_pos = 0; + + /* + * may have to delay until context is attached? + */ + fd_install(fd, file); + + /* + * the file structure we will use + */ + *cfile = file; + + return fd; +out: + if (file) put_filp(file); + put_unused_fd(fd); + return ret; +} + +static void +pfm_free_fd(int fd, struct file *file) +{ + struct files_struct *files = current->files; + struct fdtable *fdt; + + /* + * there ie no fd_uninstall(), so we do it here + */ + spin_lock(&files->file_lock); + fdt = files_fdtable(files); + rcu_assign_pointer(fdt->fd[fd], NULL); + spin_unlock(&files->file_lock); + + if (file) + put_filp(file); + put_unused_fd(fd); +} + +static int +pfm_remap_buffer(struct vm_area_struct *vma, unsigned long buf, unsigned long addr, unsigned long size) +{ + DPRINT(("CPU%d buf=0x%lx addr=0x%lx size=%ld\n", smp_processor_id(), buf, addr, size)); + + while (size > 0) { + unsigned long pfn = ia64_tpa(buf) >> PAGE_SHIFT; + + + if (remap_pfn_range(vma, addr, pfn, PAGE_SIZE, PAGE_READONLY)) + return -ENOMEM; + + addr += PAGE_SIZE; + buf += PAGE_SIZE; + size -= PAGE_SIZE; + } + return 0; +} + +/* + * allocate a sampling buffer and remaps it into the user address space of the task + */ +static int +pfm_smpl_buffer_alloc(struct task_struct *task, pfm_context_t *ctx, unsigned long rsize, void **user_vaddr) +{ + struct mm_struct *mm = task->mm; + struct vm_area_struct *vma = NULL; + unsigned long size; + void *smpl_buf; + + + /* + * the fixed header + requested size and align to page boundary + */ + size = PAGE_ALIGN(rsize); + + DPRINT(("sampling buffer rsize=%lu size=%lu bytes\n", rsize, size)); + + /* + * check requested size to avoid Denial-of-service attacks + * XXX: may have to refine this test + * Check against address space limit. + * + * if ((mm->total_vm << PAGE_SHIFT) + len> task->rlim[RLIMIT_AS].rlim_cur) + * return -ENOMEM; + */ + if (size > task->signal->rlim[RLIMIT_MEMLOCK].rlim_cur) + return -ENOMEM; + + /* + * We do the easy to undo allocations first. + * + * pfm_rvmalloc(), clears the buffer, so there is no leak + */ + smpl_buf = pfm_rvmalloc(size); + if (smpl_buf == NULL) { + DPRINT(("Can't allocate sampling buffer\n")); + return -ENOMEM; + } + + DPRINT(("smpl_buf @%p\n", smpl_buf)); + + /* allocate vma */ + vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); + if (!vma) { + DPRINT(("Cannot allocate vma\n")); + goto error_kmem; + } + memset(vma, 0, sizeof(*vma)); + + /* + * partially initialize the vma for the sampling buffer + */ + vma->vm_mm = mm; + vma->vm_flags = VM_READ| VM_MAYREAD |VM_RESERVED; + vma->vm_page_prot = PAGE_READONLY; /* XXX may need to change */ + + /* + * Now we have everything we need and we can initialize + * and connect all the data structures + */ + + ctx->ctx_smpl_hdr = smpl_buf; + ctx->ctx_smpl_size = size; /* aligned size */ + + /* + * Let's do the difficult operations next. + * + * now we atomically find some area in the address space and + * remap the buffer in it. + */ + down_write(&task->mm->mmap_sem); + + /* find some free area in address space, must have mmap sem held */ + vma->vm_start = pfm_get_unmapped_area(NULL, 0, size, 0, MAP_PRIVATE|MAP_ANONYMOUS, 0); + if (vma->vm_start == 0UL) { + DPRINT(("Cannot find unmapped area for size %ld\n", size)); + up_write(&task->mm->mmap_sem); + goto error; + } + vma->vm_end = vma->vm_start + size; + vma->vm_pgoff = vma->vm_start >> PAGE_SHIFT; + + DPRINT(("aligned size=%ld, hdr=%p mapped @0x%lx\n", size, ctx->ctx_smpl_hdr, vma->vm_start)); + + /* can only be applied to current task, need to have the mm semaphore held when called */ + if (pfm_remap_buffer(vma, (unsigned long)smpl_buf, vma->vm_start, size)) { + DPRINT(("Can't remap buffer\n")); + up_write(&task->mm->mmap_sem); + goto error; + } + + /* + * now insert the vma in the vm list for the process, must be + * done with mmap lock held + */ + insert_vm_struct(mm, vma); + + mm->total_vm += size >> PAGE_SHIFT; + vm_stat_account(vma->vm_mm, vma->vm_flags, vma->vm_file, + vma_pages(vma)); + up_write(&task->mm->mmap_sem); + + /* + * keep track of user level virtual address + */ + ctx->ctx_smpl_vaddr = (void *)vma->vm_start; + *(unsigned long *)user_vaddr = vma->vm_start; + + return 0; + +error: + kmem_cache_free(vm_area_cachep, vma); +error_kmem: + pfm_rvfree(smpl_buf, size); + + return -ENOMEM; +} + +/* + * XXX: do something better here + */ +static int +pfm_bad_permissions(struct task_struct *task) +{ + /* inspired by ptrace_attach() */ + DPRINT(("cur: uid=%d gid=%d task: euid=%d suid=%d uid=%d egid=%d sgid=%d\n", + current->uid, + current->gid, + task->euid, + task->suid, + task->uid, + task->egid, + task->sgid)); + + return ((current->uid != task->euid) + || (current->uid != task->suid) + || (current->uid != task->uid) + || (current->gid != task->egid) + || (current->gid != task->sgid) + || (current->gid != task->gid)) && !capable(CAP_SYS_PTRACE); +} + +static int +pfarg_is_sane(struct task_struct *task, pfarg_context_t *pfx) +{ + int ctx_flags; + + /* valid signal */ + + ctx_flags = pfx->ctx_flags; + + if (ctx_flags & PFM_FL_SYSTEM_WIDE) { + + /* + * cannot block in this mode + */ + if (ctx_flags & PFM_FL_NOTIFY_BLOCK) { + DPRINT(("cannot use blocking mode when in system wide monitoring\n")); + return -EINVAL; + } + } else { + } + /* probably more to add here */ + + return 0; +} + +static int +pfm_setup_buffer_fmt(struct task_struct *task, pfm_context_t *ctx, unsigned int ctx_flags, + unsigned int cpu, pfarg_context_t *arg) +{ + pfm_buffer_fmt_t *fmt = NULL; + unsigned long size = 0UL; + void *uaddr = NULL; + void *fmt_arg = NULL; + int ret = 0; +#define PFM_CTXARG_BUF_ARG(a) (pfm_buffer_fmt_t *)(a+1) + + /* invoke and lock buffer format, if found */ + fmt = pfm_find_buffer_fmt(arg->ctx_smpl_buf_id); + if (fmt == NULL) { + DPRINT(("[%d] cannot find buffer format\n", task->pid)); + return -EINVAL; + } + + /* + * buffer argument MUST be contiguous to pfarg_context_t + */ + if (fmt->fmt_arg_size) fmt_arg = PFM_CTXARG_BUF_ARG(arg); + + ret = pfm_buf_fmt_validate(fmt, task, ctx_flags, cpu, fmt_arg); + + DPRINT(("[%d] after validate(0x%x,%d,%p)=%d\n", task->pid, ctx_flags, cpu, fmt_arg, ret)); + + if (ret) goto error; + + /* link buffer format and context */ + ctx->ctx_buf_fmt = fmt; + + /* + * check if buffer format wants to use perfmon buffer allocation/mapping service + */ + ret = pfm_buf_fmt_getsize(fmt, task, ctx_flags, cpu, fmt_arg, &size); + if (ret) goto error; + + if (size) { + /* + * buffer is always remapped into the caller's address space + */ + ret = pfm_smpl_buffer_alloc(current, ctx, size, &uaddr); + if (ret) goto error; + + /* keep track of user address of buffer */ + arg->ctx_smpl_vaddr = uaddr; + } + ret = pfm_buf_fmt_init(fmt, task, ctx->ctx_smpl_hdr, ctx_flags, cpu, fmt_arg); + +error: + return ret; +} + +static void +pfm_reset_pmu_state(pfm_context_t *ctx) +{ + int i; + + /* + * install reset values for PMC. + */ + for (i=1; PMC_IS_LAST(i) == 0; i++) { + if (PMC_IS_IMPL(i) == 0) continue; + ctx->ctx_pmcs[i] = PMC_DFL_VAL(i); + DPRINT(("pmc[%d]=0x%lx\n", i, ctx->ctx_pmcs[i])); + } + /* + * PMD registers are set to 0UL when the context in memset() + */ + + /* + * On context switched restore, we must restore ALL pmc and ALL pmd even + * when they are not actively used by the task. In UP, the incoming process + * may otherwise pick up left over PMC, PMD state from the previous process. + * As opposed to PMD, stale PMC can cause harm to the incoming + * process because they may change what is being measured. + * Therefore, we must systematically reinstall the entire + * PMC state. In SMP, the same thing is possible on the + * same CPU but also on between 2 CPUs. + * + * The problem with PMD is information leaking especially + * to user level when psr.sp=0 + * + * There is unfortunately no easy way to avoid this problem + * on either UP or SMP. This definitively slows down the + * pfm_load_regs() function. + */ + + /* + * bitmask of all PMCs accessible to this context + * + * PMC0 is treated differently. + */ + ctx->ctx_all_pmcs[0] = pmu_conf->impl_pmcs[0] & ~0x1; + + /* + * bitmask of all PMDs that are accesible to this context + */ + ctx->ctx_all_pmds[0] = pmu_conf->impl_pmds[0]; + + DPRINT(("<%d> all_pmcs=0x%lx all_pmds=0x%lx\n", ctx->ctx_fd, ctx->ctx_all_pmcs[0],ctx->ctx_all_pmds[0])); + + /* + * useful in case of re-enable after disable + */ + ctx->ctx_used_ibrs[0] = 0UL; + ctx->ctx_used_dbrs[0] = 0UL; +} + +static int +pfm_ctx_getsize(void *arg, size_t *sz) +{ + pfarg_context_t *req = (pfarg_context_t *)arg; + pfm_buffer_fmt_t *fmt; + + *sz = 0; + + if (!pfm_uuid_cmp(req->ctx_smpl_buf_id, pfm_null_uuid)) return 0; + + fmt = pfm_find_buffer_fmt(req->ctx_smpl_buf_id); + if (fmt == NULL) { + DPRINT(("cannot find buffer format\n")); + return -EINVAL; + } + /* get just enough to copy in user parameters */ + *sz = fmt->fmt_arg_size; + DPRINT(("arg_size=%lu\n", *sz)); + + return 0; +} + + + +/* + * cannot attach if : + * - kernel task + * - task not owned by caller + * - task incompatible with context mode + */ +static int +pfm_task_incompatible(pfm_context_t *ctx, struct task_struct *task) +{ + /* + * no kernel task or task not owner by caller + */ + if (task->mm == NULL) { + DPRINT(("task [%d] has not memory context (kernel thread)\n", task->pid)); + return -EPERM; + } + if (pfm_bad_permissions(task)) { + DPRINT(("no permission to attach to [%d]\n", task->pid)); + return -EPERM; + } + /* + * cannot block in self-monitoring mode + */ + if (CTX_OVFL_NOBLOCK(ctx) == 0 && task == current) { + DPRINT(("cannot load a blocking context on self for [%d]\n", task->pid)); + return -EINVAL; + } + + if (task->exit_state == EXIT_ZOMBIE) { + DPRINT(("cannot attach to zombie task [%d]\n", task->pid)); + return -EBUSY; + } + + /* + * always ok for self + */ + if (task == current) return 0; + + if ((task->state != TASK_STOPPED) && (task->state != TASK_TRACED)) { + DPRINT(("cannot attach to non-stopped task [%d] state=%ld\n", task->pid, task->state)); + return -EBUSY; + } + /* + * make sure the task is off any CPU + */ + wait_task_inactive(task); + + /* more to come... */ + + return 0; +} + +static int +pfm_get_task(pfm_context_t *ctx, pid_t pid, struct task_struct **task) +{ + struct task_struct *p = current; + int ret; + + /* XXX: need to add more checks here */ + if (pid < 2) return -EPERM; + + if (pid != current->pid) { + + read_lock(&tasklist_lock); + + p = find_task_by_pid(pid); + + /* make sure task cannot go away while we operate on it */ + if (p) get_task_struct(p); + + read_unlock(&tasklist_lock); + + if (p == NULL) return -ESRCH; + } + + ret = pfm_task_incompatible(ctx, p); + if (ret == 0) { + *task = p; + } else if (p != current) { + pfm_put_task(p); + } + return ret; +} + + + +static int +pfm_context_create(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) +{ + pfarg_context_t *req = (pfarg_context_t *)arg; + struct file *filp; + int ctx_flags; + int ret; + + /* let's check the arguments first */ + ret = pfarg_is_sane(current, req); + if (ret < 0) return ret; + + ctx_flags = req->ctx_flags; + + ret = -ENOMEM; + + ctx = pfm_context_alloc(); + if (!ctx) goto error; + + ret = pfm_alloc_fd(&filp); + if (ret < 0) goto error_file; + + req->ctx_fd = ctx->ctx_fd = ret; + + /* + * attach context to file + */ + filp->private_data = ctx; + + /* + * does the user want to sample? + */ + if (pfm_uuid_cmp(req->ctx_smpl_buf_id, pfm_null_uuid)) { + ret = pfm_setup_buffer_fmt(current, ctx, ctx_flags, 0, req); + if (ret) goto buffer_error; + } + + /* + * init context protection lock + */ + spin_lock_init(&ctx->ctx_lock); + + /* + * context is unloaded + */ + ctx->ctx_state = PFM_CTX_UNLOADED; + + /* + * initialization of context's flags + */ + ctx->ctx_fl_block = (ctx_flags & PFM_FL_NOTIFY_BLOCK) ? 1 : 0; + ctx->ctx_fl_system = (ctx_flags & PFM_FL_SYSTEM_WIDE) ? 1: 0; + ctx->ctx_fl_is_sampling = ctx->ctx_buf_fmt ? 1 : 0; /* assume record() is defined */ + ctx->ctx_fl_no_msg = (ctx_flags & PFM_FL_OVFL_NO_MSG) ? 1: 0; + /* + * will move to set properties + * ctx->ctx_fl_excl_idle = (ctx_flags & PFM_FL_EXCL_IDLE) ? 1: 0; + */ + + /* + * init restart semaphore to locked + */ + init_completion(&ctx->ctx_restart_done); + + /* + * activation is used in SMP only + */ + ctx->ctx_last_activation = PFM_INVALID_ACTIVATION; + SET_LAST_CPU(ctx, -1); + + /* + * initialize notification message queue + */ + ctx->ctx_msgq_head = ctx->ctx_msgq_tail = 0; + init_waitqueue_head(&ctx->ctx_msgq_wait); + init_waitqueue_head(&ctx->ctx_zombieq); + + DPRINT(("ctx=%p flags=0x%x system=%d notify_block=%d excl_idle=%d no_msg=%d ctx_fd=%d \n", + ctx, + ctx_flags, + ctx->ctx_fl_system, + ctx->ctx_fl_block, + ctx->ctx_fl_excl_idle, + ctx->ctx_fl_no_msg, + ctx->ctx_fd)); + + /* + * initialize soft PMU state + */ + pfm_reset_pmu_state(ctx); + + if (is_running_on_xen()) { + /* + * kludge to get xenoprof.is_primary. + * XENOPROF_init/ia64 is nop. so it is safe to call it here. + */ + struct xenoprof_init init; + ret = HYPERVISOR_xenoprof_op(XENOPROF_init, &init); + if (ret) + goto buffer_error; + init_xenoprof_primary(init.is_primary); + + if (is_xenoprof_primary()) { + ret = HYPERVISOR_perfmon_op(PFM_CREATE_CONTEXT, arg, 0); + if (ret) + goto buffer_error; + } + } + return 0; + +buffer_error: + pfm_free_fd(ctx->ctx_fd, filp); + + if (ctx->ctx_buf_fmt) { + pfm_buf_fmt_exit(ctx->ctx_buf_fmt, current, NULL, regs); + } +error_file: + pfm_context_free(ctx); + +error: + return ret; +} + +static inline unsigned long +pfm_new_counter_value (pfm_counter_t *reg, int is_long_reset) +{ + unsigned long val = is_long_reset ? reg->long_reset : reg->short_reset; + unsigned long new_seed, old_seed = reg->seed, mask = reg->mask; + extern unsigned long carta_random32 (unsigned long seed); + + if (reg->flags & PFM_REGFL_RANDOM) { + new_seed = carta_random32(old_seed); + val -= (old_seed & mask); /* counter values are negative numbers! */ + if ((mask >> 32) != 0) + /* construct a full 64-bit random value: */ + new_seed |= carta_random32(old_seed >> 32) << 32; + reg->seed = new_seed; + } + reg->lval = val; + return val; +} + +static void +pfm_reset_regs_masked(pfm_context_t *ctx, unsigned long *ovfl_regs, int is_long_reset) +{ + unsigned long mask = ovfl_regs[0]; + unsigned long reset_others = 0UL; + unsigned long val; + int i; + + /* + * now restore reset value on sampling overflowed counters + */ + mask >>= PMU_FIRST_COUNTER; + for(i = PMU_FIRST_COUNTER; mask; i++, mask >>= 1) { + + if ((mask & 0x1UL) == 0UL) continue; + + ctx->ctx_pmds[i].val = val = pfm_new_counter_value(ctx->ctx_pmds+ i, is_long_reset); + reset_others |= ctx->ctx_pmds[i].reset_pmds[0]; + + DPRINT_ovfl((" %s reset ctx_pmds[%d]=%lx\n", is_long_reset ? "long" : "short", i, val)); + } + + /* + * Now take care of resetting the other registers + */ + for(i = 0; reset_others; i++, reset_others >>= 1) { + + if ((reset_others & 0x1) == 0) continue; + + ctx->ctx_pmds[i].val = val = pfm_new_counter_value(ctx->ctx_pmds + i, is_long_reset); + + DPRINT_ovfl(("%s reset_others pmd[%d]=%lx\n", + is_long_reset ? "long" : "short", i, val)); + } +} + +static void +pfm_reset_regs(pfm_context_t *ctx, unsigned long *ovfl_regs, int is_long_reset) +{ + unsigned long mask = ovfl_regs[0]; + unsigned long reset_others = 0UL; + unsigned long val; + int i; + + DPRINT_ovfl(("ovfl_regs=0x%lx is_long_reset=%d\n", ovfl_regs[0], is_long_reset)); + + if (ctx->ctx_state == PFM_CTX_MASKED) { + pfm_reset_regs_masked(ctx, ovfl_regs, is_long_reset); + return; + } + + /* + * now restore reset value on sampling overflowed counters + */ + mask >>= PMU_FIRST_COUNTER; + for(i = PMU_FIRST_COUNTER; mask; i++, mask >>= 1) { + + if ((mask & 0x1UL) == 0UL) continue; + + val = pfm_new_counter_value(ctx->ctx_pmds+ i, is_long_reset); + reset_others |= ctx->ctx_pmds[i].reset_pmds[0]; + + DPRINT_ovfl((" %s reset ctx_pmds[%d]=%lx\n", is_long_reset ? "long" : "short", i, val)); + + pfm_write_soft_counter(ctx, i, val); + } + + /* + * Now take care of resetting the other registers + */ + for(i = 0; reset_others; i++, reset_others >>= 1) { + + if ((reset_others & 0x1) == 0) continue; + + val = pfm_new_counter_value(ctx->ctx_pmds + i, is_long_reset); + + if (PMD_IS_COUNTING(i)) { + pfm_write_soft_counter(ctx, i, val); + } else { + ia64_set_pmd(i, val); + } + DPRINT_ovfl(("%s reset_others pmd[%d]=%lx\n", + is_long_reset ? "long" : "short", i, val)); + } + ia64_srlz_d(); +} + +static int +pfm_write_pmcs(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) +{ + struct thread_struct *thread = NULL; + struct task_struct *task; + pfarg_reg_t *req = (pfarg_reg_t *)arg; + unsigned long value, pmc_pm; + unsigned long smpl_pmds, reset_pmds, impl_pmds; + unsigned int cnum, reg_flags, flags, pmc_type; + int i, can_access_pmu = 0, is_loaded, is_system, expert_mode; + int is_monitor, is_counting, state; + int ret = -EINVAL; + pfm_reg_check_t wr_func; +#define PFM_CHECK_PMC_PM(x, y, z) ((x)->ctx_fl_system ^ PMC_PM(y, z)) + + if (is_running_on_xen()) { + if (is_xenoprof_primary()) + return HYPERVISOR_perfmon_op(PFM_WRITE_PMCS, + arg, count); + return 0; + } + state = ctx->ctx_state; + is_loaded = state == PFM_CTX_LOADED ? 1 : 0; + is_system = ctx->ctx_fl_system; + task = ctx->ctx_task; + impl_pmds = pmu_conf->impl_pmds[0]; + + if (state == PFM_CTX_ZOMBIE) return -EINVAL; + + if (is_loaded) { + thread = &task->thread; + /* + * In system wide and when the context is loaded, access can only happen + * when the caller is running on the CPU being monitored by the session. + * It does not have to be the owner (ctx_task) of the context per se. + */ + if (is_system && ctx->ctx_cpu != smp_processor_id()) { + DPRINT(("should be running on CPU%d\n", ctx->ctx_cpu)); + return -EBUSY; + } + can_access_pmu = GET_PMU_OWNER() == task || is_system ? 1 : 0; + } + expert_mode = pfm_sysctl.expert_mode; + + for (i = 0; i < count; i++, req++) { + + cnum = req->reg_num; + reg_flags = req->reg_flags; + value = req->reg_value; + smpl_pmds = req->reg_smpl_pmds[0]; + reset_pmds = req->reg_reset_pmds[0]; + flags = 0; + + + if (cnum >= PMU_MAX_PMCS) { + DPRINT(("pmc%u is invalid\n", cnum)); + goto error; + } + + pmc_type = pmu_conf->pmc_desc[cnum].type; + pmc_pm = (value >> pmu_conf->pmc_desc[cnum].pm_pos) & 0x1; + is_counting = (pmc_type & PFM_REG_COUNTING) == PFM_REG_COUNTING ? 1 : 0; + is_monitor = (pmc_type & PFM_REG_MONITOR) == PFM_REG_MONITOR ? 1 : 0; + + /* + * we reject all non implemented PMC as well + * as attempts to modify PMC[0-3] which are used + * as status registers by the PMU + */ + if ((pmc_type & PFM_REG_IMPL) == 0 || (pmc_type & PFM_REG_CONTROL) == PFM_REG_CONTROL) { + DPRINT(("pmc%u is unimplemented or no-access pmc_type=%x\n", cnum, pmc_type)); + goto error; + } + wr_func = pmu_conf->pmc_desc[cnum].write_check; + /* + * If the PMC is a monitor, then if the value is not the default: + * - system-wide session: PMCx.pm=1 (privileged monitor) + * - per-task : PMCx.pm=0 (user monitor) + */ + if (is_monitor && value != PMC_DFL_VAL(cnum) && is_system ^ pmc_pm) { + DPRINT(("pmc%u pmc_pm=%lu is_system=%d\n", + cnum, + pmc_pm, + is_system)); + goto error; + } + + if (is_counting) { + /* + * enforce generation of overflow interrupt. Necessary on all + * CPUs. + */ + value |= 1 << PMU_PMC_OI; + + if (reg_flags & PFM_REGFL_OVFL_NOTIFY) { + flags |= PFM_REGFL_OVFL_NOTIFY; + } + + if (reg_flags & PFM_REGFL_RANDOM) flags |= PFM_REGFL_RANDOM; + + /* verify validity of smpl_pmds */ + if ((smpl_pmds & impl_pmds) != smpl_pmds) { + DPRINT(("invalid smpl_pmds 0x%lx for pmc%u\n", smpl_pmds, cnum)); + goto error; + } + + /* verify validity of reset_pmds */ + if ((reset_pmds & impl_pmds) != reset_pmds) { + DPRINT(("invalid reset_pmds 0x%lx for pmc%u\n", reset_pmds, cnum)); + goto error; + } + } else { + if (reg_flags & (PFM_REGFL_OVFL_NOTIFY|PFM_REGFL_RANDOM)) { + DPRINT(("cannot set ovfl_notify or random on pmc%u\n", cnum)); + goto error; + } + /* eventid on non-counting monitors are ignored */ + } + + /* + * execute write checker, if any + */ + if (likely(expert_mode == 0 && wr_func)) { + ret = (*wr_func)(task, ctx, cnum, &value, regs); + if (ret) goto error; + ret = -EINVAL; + } + + /* + * no error on this register + */ + PFM_REG_RETFLAG_SET(req->reg_flags, 0); + + /* + * Now we commit the changes to the software state + */ + + /* + * update overflow information + */ + if (is_counting) { + /* + * full flag update each time a register is programmed + */ + ctx->ctx_pmds[cnum].flags = flags; + + ctx->ctx_pmds[cnum].reset_pmds[0] = reset_pmds; + ctx->ctx_pmds[cnum].smpl_pmds[0] = smpl_pmds; + ctx->ctx_pmds[cnum].eventid = req->reg_smpl_eventid; + + /* + * Mark all PMDS to be accessed as used. + * + * We do not keep track of PMC because we have to + * systematically restore ALL of them. + * + * We do not update the used_monitors mask, because + * if we have not programmed them, then will be in + * a quiescent state, therefore we will not need to + * mask/restore then when context is MASKED. + */ + CTX_USED_PMD(ctx, reset_pmds); + CTX_USED_PMD(ctx, smpl_pmds); + /* + * make sure we do not try to reset on + * restart because we have established new values + */ + if (state == PFM_CTX_MASKED) ctx->ctx_ovfl_regs[0] &= ~1UL << cnum; + } + /* + * Needed in case the user does not initialize the equivalent + * PMD. Clearing is done indirectly via pfm_reset_pmu_state() so there is no + * possible leak here. + */ + CTX_USED_PMD(ctx, pmu_conf->pmc_desc[cnum].dep_pmd[0]); + + /* + * keep track of the monitor PMC that we are using. + * we save the value of the pmc in ctx_pmcs[] and if + * the monitoring is not stopped for the context we also + * place it in the saved state area so that it will be + * picked up later by the context switch code. + * + * The value in ctx_pmcs[] can only be changed in pfm_write_pmcs(). + * + * The value in thread->pmcs[] may be modified on overflow, i.e., when + * monitoring needs to be stopped. + */ + if (is_monitor) CTX_USED_MONITOR(ctx, 1UL << cnum); + + /* + * update context state + */ + ctx->ctx_pmcs[cnum] = value; + + if (is_loaded) { + /* + * write thread state + */ + if (is_system == 0) thread->pmcs[cnum] = value; + + /* + * write hardware register if we can + */ + if (can_access_pmu) { + ia64_set_pmc(cnum, value); + } +#ifdef CONFIG_SMP + else { + /* + * per-task SMP only here + * + * we are guaranteed that the task is not running on the other CPU, + * we indicate that this PMD will need to be reloaded if the task + * is rescheduled on the CPU it ran last on. + */ + ctx->ctx_reload_pmcs[0] |= 1UL << cnum; + } +#endif + } + + DPRINT(("pmc[%u]=0x%lx ld=%d apmu=%d flags=0x%x all_pmcs=0x%lx used_pmds=0x%lx eventid=%ld smpl_pmds=0x%lx reset_pmds=0x%lx reloads_pmcs=0x%lx used_monitors=0x%lx ovfl_regs=0x%lx\n", + cnum, + value, + is_loaded, + can_access_pmu, + flags, + ctx->ctx_all_pmcs[0], + ctx->ctx_used_pmds[0], + ctx->ctx_pmds[cnum].eventid, + smpl_pmds, + reset_pmds, + ctx->ctx_reload_pmcs[0], + ctx->ctx_used_monitors[0], + ctx->ctx_ovfl_regs[0])); + } + + /* + * make sure the changes are visible + */ + if (can_access_pmu) ia64_srlz_d(); + + return 0; +error: + PFM_REG_RETFLAG_SET(req->reg_flags, PFM_REG_RETFL_EINVAL); + return ret; +} + +static int +pfm_write_pmds(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) +{ + struct thread_struct *thread = NULL; + struct task_struct *task; + pfarg_reg_t *req = (pfarg_reg_t *)arg; + unsigned long value, hw_value, ovfl_mask; + unsigned int cnum; + int i, can_access_pmu = 0, state; + int is_counting, is_loaded, is_system, expert_mode; + int ret = -EINVAL; + pfm_reg_check_t wr_func; + + if (is_running_on_xen()) { + if (is_xenoprof_primary()) + return HYPERVISOR_perfmon_op(PFM_WRITE_PMDS, + arg, count); + return 0; + } + + state = ctx->ctx_state; + is_loaded = state == PFM_CTX_LOADED ? 1 : 0; + is_system = ctx->ctx_fl_system; + ovfl_mask = pmu_conf->ovfl_val; + task = ctx->ctx_task; + + if (unlikely(state == PFM_CTX_ZOMBIE)) return -EINVAL; + + /* + * on both UP and SMP, we can only write to the PMC when the task is + * the owner of the local PMU. + */ + if (likely(is_loaded)) { + thread = &task->thread; + /* + * In system wide and when the context is loaded, access can only happen + * when the caller is running on the CPU being monitored by the session. + * It does not have to be the owner (ctx_task) of the context per se. + */ + if (unlikely(is_system && ctx->ctx_cpu != smp_processor_id())) { + DPRINT(("should be running on CPU%d\n", ctx->ctx_cpu)); + return -EBUSY; + } + can_access_pmu = GET_PMU_OWNER() == task || is_system ? 1 : 0; + } + expert_mode = pfm_sysctl.expert_mode; + + for (i = 0; i < count; i++, req++) { + + cnum = req->reg_num; + value = req->reg_value; + + if (!PMD_IS_IMPL(cnum)) { + DPRINT(("pmd[%u] is unimplemented or invalid\n", cnum)); + goto abort_mission; + } + is_counting = PMD_IS_COUNTING(cnum); + wr_func = pmu_conf->pmd_desc[cnum].write_check; + + /* + * execute write checker, if any + */ + if (unlikely(expert_mode == 0 && wr_func)) { + unsigned long v = value; + + ret = (*wr_func)(task, ctx, cnum, &v, regs); + if (ret) goto abort_mission; + + value = v; + ret = -EINVAL; + } + + /* + * no error on this register + */ + PFM_REG_RETFLAG_SET(req->reg_flags, 0); + + /* + * now commit changes to software state + */ + hw_value = value; + + /* + * update virtualized (64bits) counter + */ + if (is_counting) { + /* + * write context state + */ + ctx->ctx_pmds[cnum].lval = value; + + /* + * when context is load we use the split value + */ + if (is_loaded) { + hw_value = value & ovfl_mask; + value = value & ~ovfl_mask; + } + } + /* + * update reset values (not just for counters) + */ + ctx->ctx_pmds[cnum].long_reset = req->reg_long_reset; + ctx->ctx_pmds[cnum].short_reset = req->reg_short_reset; + + /* + * update randomization parameters (not just for counters) + */ + ctx->ctx_pmds[cnum].seed = req->reg_random_seed; + ctx->ctx_pmds[cnum].mask = req->reg_random_mask; + + /* + * update context value + */ + ctx->ctx_pmds[cnum].val = value; + + /* + * Keep track of what we use + * + * We do not keep track of PMC because we have to + * systematically restore ALL of them. + */ + CTX_USED_PMD(ctx, PMD_PMD_DEP(cnum)); + + /* + * mark this PMD register used as well + */ + CTX_USED_PMD(ctx, RDEP(cnum)); + + /* + * make sure we do not try to reset on + * restart because we have established new values + */ + if (is_counting && state == PFM_CTX_MASKED) { + ctx->ctx_ovfl_regs[0] &= ~1UL << cnum; + } + + if (is_loaded) { + /* + * write thread state + */ + if (is_system == 0) thread->pmds[cnum] = hw_value; + + /* + * write hardware register if we can + */ + if (can_access_pmu) { + ia64_set_pmd(cnum, hw_value); + } else { +#ifdef CONFIG_SMP + /* + * we are guaranteed that the task is not running on the other CPU, + * we indicate that this PMD will need to be reloaded if the task + * is rescheduled on the CPU it ran last on. + */ + ctx->ctx_reload_pmds[0] |= 1UL << cnum; +#endif + } + } + + DPRINT(("pmd[%u]=0x%lx ld=%d apmu=%d, hw_value=0x%lx ctx_pmd=0x%lx short_reset=0x%lx " + "long_reset=0x%lx notify=%c seed=0x%lx mask=0x%lx used_pmds=0x%lx reset_pmds=0x%lx reload_pmds=0x%lx all_pmds=0x%lx ovfl_regs=0x%lx\n", + cnum, + value, + is_loaded, + can_access_pmu, + hw_value, + ctx->ctx_pmds[cnum].val, + ctx->ctx_pmds[cnum].short_reset, + ctx->ctx_pmds[cnum].long_reset, + PMC_OVFL_NOTIFY(ctx, cnum) ? 'Y':'N', + ctx->ctx_pmds[cnum].seed, + ctx->ctx_pmds[cnum].mask, + ctx->ctx_used_pmds[0], + ctx->ctx_pmds[cnum].reset_pmds[0], + ctx->ctx_reload_pmds[0], + ctx->ctx_all_pmds[0], + ctx->ctx_ovfl_regs[0])); + } + + /* + * make changes visible + */ + if (can_access_pmu) ia64_srlz_d(); + + return 0; + +abort_mission: + /* + * for now, we have only one possibility for error + */ + PFM_REG_RETFLAG_SET(req->reg_flags, PFM_REG_RETFL_EINVAL); + return ret; +} + +/* + * By the way of PROTECT_CONTEXT(), interrupts are masked while we are in this function. + * Therefore we know, we do not have to worry about the PMU overflow interrupt. If an + * interrupt is delivered during the call, it will be kept pending until we leave, making + * it appears as if it had been generated at the UNPROTECT_CONTEXT(). At least we are + * guaranteed to return consistent data to the user, it may simply be old. It is not + * trivial to treat the overflow while inside the call because you may end up in + * some module sampling buffer code causing deadlocks. + */ +static int +pfm_read_pmds(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) +{ + struct thread_struct *thread = NULL; + struct task_struct *task; + unsigned long val = 0UL, lval, ovfl_mask, sval; + pfarg_reg_t *req = (pfarg_reg_t *)arg; + unsigned int cnum, reg_flags = 0; + int i, can_access_pmu = 0, state; + int is_loaded, is_system, is_counting, expert_mode; + int ret = -EINVAL; + pfm_reg_check_t rd_func; + XEN_NOT_SUPPORTED_YET; + + /* + * access is possible when loaded only for + * self-monitoring tasks or in UP mode + */ + + state = ctx->ctx_state; + is_loaded = state == PFM_CTX_LOADED ? 1 : 0; + is_system = ctx->ctx_fl_system; + ovfl_mask = pmu_conf->ovfl_val; + task = ctx->ctx_task; + + if (state == PFM_CTX_ZOMBIE) return -EINVAL; + + if (likely(is_loaded)) { + thread = &task->thread; + /* + * In system wide and when the context is loaded, access can only happen + * when the caller is running on the CPU being monitored by the session. + * It does not have to be the owner (ctx_task) of the context per se. + */ + if (unlikely(is_system && ctx->ctx_cpu != smp_processor_id())) { + DPRINT(("should be running on CPU%d\n", ctx->ctx_cpu)); + return -EBUSY; + } + /* + * this can be true when not self-monitoring only in UP + */ + can_access_pmu = GET_PMU_OWNER() == task || is_system ? 1 : 0; + + if (can_access_pmu) ia64_srlz_d(); + } + expert_mode = pfm_sysctl.expert_mode; + + DPRINT(("ld=%d apmu=%d ctx_state=%d\n", + is_loaded, + can_access_pmu, + state)); + + /* + * on both UP and SMP, we can only read the PMD from the hardware register when + * the task is the owner of the local PMU. + */ + + for (i = 0; i < count; i++, req++) { + + cnum = req->reg_num; + reg_flags = req->reg_flags; + + if (unlikely(!PMD_IS_IMPL(cnum))) goto error; + /* + * we can only read the register that we use. That includes + * the one we explicitely initialize AND the one we want included + * in the sampling buffer (smpl_regs). + * + * Having this restriction allows optimization in the ctxsw routine + * without compromising security (leaks) + */ + if (unlikely(!CTX_IS_USED_PMD(ctx, cnum))) goto error; + + sval = ctx->ctx_pmds[cnum].val; + lval = ctx->ctx_pmds[cnum].lval; + is_counting = PMD_IS_COUNTING(cnum); + + /* + * If the task is not the current one, then we check if the + * PMU state is still in the local live register due to lazy ctxsw. + * If true, then we read directly from the registers. + */ + if (can_access_pmu){ + val = ia64_get_pmd(cnum); + } else { + /* + * context has been saved + * if context is zombie, then task does not exist anymore. + * In this case, we use the full value saved in the context (pfm_flush_regs()). + */ + val = is_loaded ? thread->pmds[cnum] : 0UL; + } + rd_func = pmu_conf->pmd_desc[cnum].read_check; + + if (is_counting) { + /* + * XXX: need to check for overflow when loaded + */ + val &= ovfl_mask; + val += sval; + } + + /* + * execute read checker, if any + */ + if (unlikely(expert_mode == 0 && rd_func)) { + unsigned long v = val; + ret = (*rd_func)(ctx->ctx_task, ctx, cnum, &v, regs); + if (ret) goto error; + val = v; + ret = -EINVAL; + } + + PFM_REG_RETFLAG_SET(reg_flags, 0); + + DPRINT(("pmd[%u]=0x%lx\n", cnum, val)); + + /* + * update register return value, abort all if problem during copy. + * we only modify the reg_flags field. no check mode is fine because + * access has been verified upfront in sys_perfmonctl(). + */ + req->reg_value = val; + req->reg_flags = reg_flags; + req->reg_last_reset_val = lval; + } + + return 0; + +error: + PFM_REG_RETFLAG_SET(req->reg_flags, PFM_REG_RETFL_EINVAL); + return ret; +} + +int +pfm_mod_write_pmcs(struct task_struct *task, void *req, unsigned int nreq, struct pt_regs *regs) +{ + pfm_context_t *ctx; + + if (req == NULL) return -EINVAL; + + ctx = GET_PMU_CTX(); + + if (ctx == NULL) return -EINVAL; + + /* + * for now limit to current task, which is enough when calling + * from overflow handler + */ + if (task != current && ctx->ctx_fl_system == 0) return -EBUSY; + + return pfm_write_pmcs(ctx, req, nreq, regs); +} +EXPORT_SYMBOL(pfm_mod_write_pmcs); + +int +pfm_mod_read_pmds(struct task_struct *task, void *req, unsigned int nreq, struct pt_regs *regs) +{ + pfm_context_t *ctx; + + if (req == NULL) return -EINVAL; + + ctx = GET_PMU_CTX(); + + if (ctx == NULL) return -EINVAL; + + /* + * for now limit to current task, which is enough when calling + * from overflow handler + */ + if (task != current && ctx->ctx_fl_system == 0) return -EBUSY; + + return pfm_read_pmds(ctx, req, nreq, regs); +} +EXPORT_SYMBOL(pfm_mod_read_pmds); + +/* + * Only call this function when a process it trying to + * write the debug registers (reading is always allowed) + */ +int +pfm_use_debug_registers(struct task_struct *task) +{ + pfm_context_t *ctx = task->thread.pfm_context; + unsigned long flags; + int ret = 0; + + if (pmu_conf->use_rr_dbregs == 0) return 0; + + DPRINT(("called for [%d]\n", task->pid)); + + /* + * do it only once + */ + if (task->thread.flags & IA64_THREAD_DBG_VALID) return 0; + + /* + * Even on SMP, we do not need to use an atomic here because + * the only way in is via ptrace() and this is possible only when the + * process is stopped. Even in the case where the ctxsw out is not totally + * completed by the time we come here, there is no way the 'stopped' process + * could be in the middle of fiddling with the pfm_write_ibr_dbr() routine. + * So this is always safe. + */ + if (ctx && ctx->ctx_fl_using_dbreg == 1) return -1; + + LOCK_PFS(flags); + + /* + * We cannot allow setting breakpoints when system wide monitoring + * sessions are using the debug registers. + */ + if (pfm_sessions.pfs_sys_use_dbregs> 0) + ret = -1; + else + pfm_sessions.pfs_ptrace_use_dbregs++; + + DPRINT(("ptrace_use_dbregs=%u sys_use_dbregs=%u by [%d] ret = %d\n", + pfm_sessions.pfs_ptrace_use_dbregs, + pfm_sessions.pfs_sys_use_dbregs, + task->pid, ret)); + + UNLOCK_PFS(flags); + + return ret; +} + +/* + * This function is called for every task that exits with the + * IA64_THREAD_DBG_VALID set. This indicates a task which was + * able to use the debug registers for debugging purposes via + * ptrace(). Therefore we know it was not using them for + * perfmormance monitoring, so we only decrement the number + * of "ptraced" debug register users to keep the count up to date + */ +int +pfm_release_debug_registers(struct task_struct *task) +{ + unsigned long flags; + int ret; + + if (pmu_conf->use_rr_dbregs == 0) return 0; + + LOCK_PFS(flags); + if (pfm_sessions.pfs_ptrace_use_dbregs == 0) { + printk(KERN_ERR "perfmon: invalid release for [%d] ptrace_use_dbregs=0\n", task->pid); + ret = -1; + } else { + pfm_sessions.pfs_ptrace_use_dbregs--; + ret = 0; + } + UNLOCK_PFS(flags); + + return ret; +} + +static int +pfm_restart(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) +{ + struct task_struct *task; + pfm_buffer_fmt_t *fmt; + pfm_ovfl_ctrl_t rst_ctrl; + int state, is_system; + int ret = 0; + XEN_NOT_SUPPORTED_YET; + + state = ctx->ctx_state; + fmt = ctx->ctx_buf_fmt; + is_system = ctx->ctx_fl_system; + task = PFM_CTX_TASK(ctx); + + switch(state) { + case PFM_CTX_MASKED: + break; + case PFM_CTX_LOADED: + if (CTX_HAS_SMPL(ctx) && fmt->fmt_restart_active) break; + /* fall through */ + case PFM_CTX_UNLOADED: + case PFM_CTX_ZOMBIE: + DPRINT(("invalid state=%d\n", state)); + return -EBUSY; + default: + DPRINT(("state=%d, cannot operate (no active_restart handler)\n", state)); + return -EINVAL; + } + + /* + * In system wide and when the context is loaded, access can only happen + * when the caller is running on the CPU being monitored by the session. + * It does not have to be the owner (ctx_task) of the context per se. + */ + if (is_system && ctx->ctx_cpu != smp_processor_id()) { + DPRINT(("should be running on CPU%d\n", ctx->ctx_cpu)); + return -EBUSY; + } + + /* sanity check */ + if (unlikely(task == NULL)) { + printk(KERN_ERR "perfmon: [%d] pfm_restart no task\n", current->pid); + return -EINVAL; + } + + if (task == current || is_system) { + + fmt = ctx->ctx_buf_fmt; + + DPRINT(("restarting self %d ovfl=0x%lx\n", + task->pid, + ctx->ctx_ovfl_regs[0])); + + if (CTX_HAS_SMPL(ctx)) { + + prefetch(ctx->ctx_smpl_hdr); + + rst_ctrl.bits.mask_monitoring = 0; + rst_ctrl.bits.reset_ovfl_pmds = 0; + + if (state == PFM_CTX_LOADED) + ret = pfm_buf_fmt_restart_active(fmt, task, &rst_ctrl, ctx->ctx_smpl_hdr, regs); + else + ret = pfm_buf_fmt_restart(fmt, task, &rst_ctrl, ctx->ctx_smpl_hdr, regs); + } else { + rst_ctrl.bits.mask_monitoring = 0; + rst_ctrl.bits.reset_ovfl_pmds = 1; + } + + if (ret == 0) { + if (rst_ctrl.bits.reset_ovfl_pmds) + pfm_reset_regs(ctx, ctx->ctx_ovfl_regs, PFM_PMD_LONG_RESET); + + if (rst_ctrl.bits.mask_monitoring == 0) { + DPRINT(("resuming monitoring for [%d]\n", task->pid)); + + if (state == PFM_CTX_MASKED) pfm_restore_monitoring(task); + } else { + DPRINT(("keeping monitoring stopped for [%d]\n", task->pid)); + + // cannot use pfm_stop_monitoring(task, regs); + } + } + /* + * clear overflowed PMD mask to remove any stale information + */ + ctx->ctx_ovfl_regs[0] = 0UL; + + /* + * back to LOADED state + */ + ctx->ctx_state = PFM_CTX_LOADED; + + /* + * XXX: not really useful for self monitoring + */ + ctx->ctx_fl_can_restart = 0; + + return 0; + } + + /* + * restart another task + */ + + /* + * When PFM_CTX_MASKED, we cannot issue a restart before the previous + * one is seen by the task. + */ + if (state == PFM_CTX_MASKED) { + if (ctx->ctx_fl_can_restart == 0) return -EINVAL; + /* + * will prevent subsequent restart before this one is + * seen by other task + */ + ctx->ctx_fl_can_restart = 0; + } + + /* + * if blocking, then post the semaphore is PFM_CTX_MASKED, i.e. + * the task is blocked or on its way to block. That's the normal + * restart path. If the monitoring is not masked, then the task + * can be actively monitoring and we cannot directly intervene. + * Therefore we use the trap mechanism to catch the task and + * force it to reset the buffer/reset PMDs. + * + * if non-blocking, then we ensure that the task will go into + * pfm_handle_work() before returning to user mode. + * + * We cannot explicitely reset another task, it MUST always + * be done by the task itself. This works for system wide because + * the tool that is controlling the session is logically doing + * "self-monitoring". + */ + if (CTX_OVFL_NOBLOCK(ctx) == 0 && state == PFM_CTX_MASKED) { + DPRINT(("unblocking [%d] \n", task->pid)); + complete(&ctx->ctx_restart_done); + } else { + DPRINT(("[%d] armed exit trap\n", task->pid)); + + ctx->ctx_fl_trap_reason = PFM_TRAP_REASON_RESET; + + PFM_SET_WORK_PENDING(task, 1); + + pfm_set_task_notify(task); + + /* + * XXX: send reschedule if task runs on another CPU + */ + } + return 0; +} + +static int +pfm_debug(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) +{ + unsigned int m = *(unsigned int *)arg; + XEN_NOT_SUPPORTED_YET; + + pfm_sysctl.debug = m == 0 ? 0 : 1; + + printk(KERN_INFO "perfmon debugging %s (timing reset)\n", pfm_sysctl.debug ? "on" : "off"); + + if (m == 0) { + memset(pfm_stats, 0, sizeof(pfm_stats)); + for(m=0; m < NR_CPUS; m++) pfm_stats[m].pfm_ovfl_intr_cycles_min = ~0UL; + } + return 0; +} + +/* + * arg can be NULL and count can be zero for this function + */ +static int +pfm_write_ibr_dbr(int mode, pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) +{ + struct thread_struct *thread = NULL; + struct task_struct *task; + pfarg_dbreg_t *req = (pfarg_dbreg_t *)arg; + unsigned long flags; + dbreg_t dbreg; + unsigned int rnum; + int first_time; + int ret = 0, state; + int i, can_access_pmu = 0; + int is_system, is_loaded; + + if (pmu_conf->use_rr_dbregs == 0) return -EINVAL; + + state = ctx->ctx_state; + is_loaded = state == PFM_CTX_LOADED ? 1 : 0; + is_system = ctx->ctx_fl_system; + task = ctx->ctx_task; + + if (state == PFM_CTX_ZOMBIE) return -EINVAL; + + /* + * on both UP and SMP, we can only write to the PMC when the task is + * the owner of the local PMU. + */ + if (is_loaded) { + thread = &task->thread; + /* + * In system wide and when the context is loaded, access can only happen + * when the caller is running on the CPU being monitored by the session. + * It does not have to be the owner (ctx_task) of the context per se. + */ + if (unlikely(is_system && ctx->ctx_cpu != smp_processor_id())) { + DPRINT(("should be running on CPU%d\n", ctx->ctx_cpu)); + return -EBUSY; + } + can_access_pmu = GET_PMU_OWNER() == task || is_system ? 1 : 0; + } + + /* + * we do not need to check for ipsr.db because we do clear ibr.x, dbr.r, and dbr.w + * ensuring that no real breakpoint can be installed via this call. + * + * IMPORTANT: regs can be NULL in this function + */ + + first_time = ctx->ctx_fl_using_dbreg == 0; + + /* + * don't bother if we are loaded and task is being debugged + */ + if (is_loaded && (thread->flags & IA64_THREAD_DBG_VALID) != 0) { + DPRINT(("debug registers already in use for [%d]\n", task->pid)); + return -EBUSY; + } + + /* + * check for debug registers in system wide mode + * + * If though a check is done in pfm_context_load(), + * we must repeat it here, in case the registers are + * written after the context is loaded + */ + if (is_loaded) { + LOCK_PFS(flags); + + if (first_time && is_system) { + if (pfm_sessions.pfs_ptrace_use_dbregs) + ret = -EBUSY; + else + pfm_sessions.pfs_sys_use_dbregs++; + } + UNLOCK_PFS(flags); + } + + if (ret != 0) return ret; + + /* + * mark ourself as user of the debug registers for + * perfmon purposes. + */ + ctx->ctx_fl_using_dbreg = 1; + + /* + * clear hardware registers to make sure we don't + * pick up stale state. + * + * for a system wide session, we do not use + * thread.dbr, thread.ibr because this process + * never leaves the current CPU and the state + * is shared by all processes running on it + */ + if (first_time && can_access_pmu) { + DPRINT(("[%d] clearing ibrs, dbrs\n", task->pid)); + for (i=0; i < pmu_conf->num_ibrs; i++) { + ia64_set_ibr(i, 0UL); + ia64_dv_serialize_instruction(); + } + ia64_srlz_i(); + for (i=0; i < pmu_conf->num_dbrs; i++) { + ia64_set_dbr(i, 0UL); + ia64_dv_serialize_data(); + } + ia64_srlz_d(); + } + + /* + * Now install the values into the registers + */ + for (i = 0; i < count; i++, req++) { + + rnum = req->dbreg_num; + dbreg.val = req->dbreg_value; + + ret = -EINVAL; + + if ((mode == PFM_CODE_RR && rnum >= PFM_NUM_IBRS) || ((mode == PFM_DATA_RR) && rnum >= PFM_NUM_DBRS)) { + DPRINT(("invalid register %u val=0x%lx mode=%d i=%d count=%d\n", + rnum, dbreg.val, mode, i, count)); + + goto abort_mission; + } + + /* + * make sure we do not install enabled breakpoint + */ + if (rnum & 0x1) { + if (mode == PFM_CODE_RR) + dbreg.ibr.ibr_x = 0; + else + dbreg.dbr.dbr_r = dbreg.dbr.dbr_w = 0; + } + + PFM_REG_RETFLAG_SET(req->dbreg_flags, 0); + + /* + * Debug registers, just like PMC, can only be modified + * by a kernel call. Moreover, perfmon() access to those + * registers are centralized in this routine. The hardware + * does not modify the value of these registers, therefore, + * if we save them as they are written, we can avoid having + * to save them on context switch out. This is made possible + * by the fact that when perfmon uses debug registers, ptrace() + * won't be able to modify them concurrently. + */ + if (mode == PFM_CODE_RR) { + CTX_USED_IBR(ctx, rnum); + + if (can_access_pmu) { + ia64_set_ibr(rnum, dbreg.val); + ia64_dv_serialize_instruction(); + } + + ctx->ctx_ibrs[rnum] = dbreg.val; + + DPRINT(("write ibr%u=0x%lx used_ibrs=0x%x ld=%d apmu=%d\n", + rnum, dbreg.val, ctx->ctx_used_ibrs[0], is_loaded, can_access_pmu)); + } else { + CTX_USED_DBR(ctx, rnum); + + if (can_access_pmu) { + ia64_set_dbr(rnum, dbreg.val); + ia64_dv_serialize_data(); + } + ctx->ctx_dbrs[rnum] = dbreg.val; + + DPRINT(("write dbr%u=0x%lx used_dbrs=0x%x ld=%d apmu=%d\n", + rnum, dbreg.val, ctx->ctx_used_dbrs[0], is_loaded, can_access_pmu)); + } + } + + return 0; + +abort_mission: + /* + * in case it was our first attempt, we undo the global modifications + */ + if (first_time) { + LOCK_PFS(flags); + if (ctx->ctx_fl_system) { + pfm_sessions.pfs_sys_use_dbregs--; + } + UNLOCK_PFS(flags); + ctx->ctx_fl_using_dbreg = 0; + } + /* + * install error return flag + */ + PFM_REG_RETFLAG_SET(req->dbreg_flags, PFM_REG_RETFL_EINVAL); + + return ret; +} + +static int +pfm_write_ibrs(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) +{ + return pfm_write_ibr_dbr(PFM_CODE_RR, ctx, arg, count, regs); +} + +static int +pfm_write_dbrs(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) +{ + return pfm_write_ibr_dbr(PFM_DATA_RR, ctx, arg, count, regs); +} + +int +pfm_mod_write_ibrs(struct task_struct *task, void *req, unsigned int nreq, struct pt_regs *regs) +{ + pfm_context_t *ctx; + + if (req == NULL) return -EINVAL; + + ctx = GET_PMU_CTX(); + + if (ctx == NULL) return -EINVAL; + + /* + * for now limit to current task, which is enough when calling + * from overflow handler + */ + if (task != current && ctx->ctx_fl_system == 0) return -EBUSY; + + return pfm_write_ibrs(ctx, req, nreq, regs); +} +EXPORT_SYMBOL(pfm_mod_write_ibrs); + +int +pfm_mod_write_dbrs(struct task_struct *task, void *req, unsigned int nreq, struct pt_regs *regs) +{ + pfm_context_t *ctx; + + if (req == NULL) return -EINVAL; + + ctx = GET_PMU_CTX(); + + if (ctx == NULL) return -EINVAL; + + /* + * for now limit to current task, which is enough when calling + * from overflow handler + */ + if (task != current && ctx->ctx_fl_system == 0) return -EBUSY; + + return pfm_write_dbrs(ctx, req, nreq, regs); +} +EXPORT_SYMBOL(pfm_mod_write_dbrs); + + +static int +pfm_get_features(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) +{ + pfarg_features_t *req = (pfarg_features_t *)arg; + + if (is_running_on_xen()) + return HYPERVISOR_perfmon_op(PFM_GET_FEATURES, &arg, 0); + req->ft_version = PFM_VERSION; + return 0; +} + +static int +pfm_stop(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) +{ + struct pt_regs *tregs; + struct task_struct *task = PFM_CTX_TASK(ctx); + int state, is_system; + + if (is_running_on_xen()) { + if (is_xenoprof_primary()) + return HYPERVISOR_perfmon_op(PFM_STOP, NULL, 0); + return 0; + } + + state = ctx->ctx_state; + is_system = ctx->ctx_fl_system; + + /* + * context must be attached to issue the stop command (includes LOADED,MASKED,ZOMBIE) + */ + if (state == PFM_CTX_UNLOADED) return -EINVAL; + + /* + * In system wide and when the context is loaded, access can only happen + * when the caller is running on the CPU being monitored by the session. + * It does not have to be the owner (ctx_task) of the context per se. + */ + if (is_system && ctx->ctx_cpu != smp_processor_id()) { + DPRINT(("should be running on CPU%d\n", ctx->ctx_cpu)); + return -EBUSY; + } + DPRINT(("task [%d] ctx_state=%d is_system=%d\n", + PFM_CTX_TASK(ctx)->pid, + state, + is_system)); + /* + * in system mode, we need to update the PMU directly + * and the user level state of the caller, which may not + * necessarily be the creator of the context. + */ + if (is_system) { + /* + * Update local PMU first + * + * disable dcr pp + */ + ia64_setreg(_IA64_REG_CR_DCR, ia64_getreg(_IA64_REG_CR_DCR) & ~IA64_DCR_PP); + ia64_srlz_i(); + + /* + * update local cpuinfo + */ + PFM_CPUINFO_CLEAR(PFM_CPUINFO_DCR_PP); + + /* + * stop monitoring, does srlz.i + */ + pfm_clear_psr_pp(); + + /* + * stop monitoring in the caller + */ + ia64_psr(regs)->pp = 0; + + return 0; + } + /* + * per-task mode + */ + + if (task == current) { + /* stop monitoring at kernel level */ + pfm_clear_psr_up(); + + /* + * stop monitoring at the user level + */ + ia64_psr(regs)->up = 0; + } else { + tregs = task_pt_regs(task); + + /* + * stop monitoring at the user level + */ + ia64_psr(tregs)->up = 0; + + /* + * monitoring disabled in kernel at next reschedule + */ + ctx->ctx_saved_psr_up = 0; + DPRINT(("task=[%d]\n", task->pid)); + } + return 0; +} + + +static int +pfm_start(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) +{ + struct pt_regs *tregs; + int state, is_system; + + if (is_running_on_xen()) { + if (is_xenoprof_primary()) + return HYPERVISOR_perfmon_op(PFM_START, NULL, 0); + return 0; + } + state = ctx->ctx_state; + is_system = ctx->ctx_fl_system; + + if (state != PFM_CTX_LOADED) return -EINVAL; + + /* + * In system wide and when the context is loaded, access can only happen + * when the caller is running on the CPU being monitored by the session. + * It does not have to be the owner (ctx_task) of the context per se. + */ + if (is_system && ctx->ctx_cpu != smp_processor_id()) { + DPRINT(("should be running on CPU%d\n", ctx->ctx_cpu)); + return -EBUSY; + } + + /* + * in system mode, we need to update the PMU directly + * and the user level state of the caller, which may not + * necessarily be the creator of the context. + */ + if (is_system) { + + /* + * set user level psr.pp for the caller + */ + ia64_psr(regs)->pp = 1; + + /* + * now update the local PMU and cpuinfo + */ + PFM_CPUINFO_SET(PFM_CPUINFO_DCR_PP); + + /* + * start monitoring at kernel level + */ + pfm_set_psr_pp(); + + /* enable dcr pp */ + ia64_setreg(_IA64_REG_CR_DCR, ia64_getreg(_IA64_REG_CR_DCR) | IA64_DCR_PP); + ia64_srlz_i(); + + return 0; + } + + /* + * per-process mode + */ + + if (ctx->ctx_task == current) { + + /* start monitoring at kernel level */ + pfm_set_psr_up(); + + /* + * activate monitoring at user level + */ + ia64_psr(regs)->up = 1; + + } else { + tregs = task_pt_regs(ctx->ctx_task); + + /* + * start monitoring at the kernel level the next + * time the task is scheduled + */ + ctx->ctx_saved_psr_up = IA64_PSR_UP; + + /* + * activate monitoring at user level + */ + ia64_psr(tregs)->up = 1; + } + return 0; +} + +static int +pfm_get_pmc_reset(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) +{ + pfarg_reg_t *req = (pfarg_reg_t *)arg; + unsigned int cnum; + int i; + int ret = -EINVAL; + XEN_NOT_SUPPORTED_YET; + + for (i = 0; i < count; i++, req++) { + + cnum = req->reg_num; + + if (!PMC_IS_IMPL(cnum)) goto abort_mission; + + req->reg_value = PMC_DFL_VAL(cnum); + + PFM_REG_RETFLAG_SET(req->reg_flags, 0); + + DPRINT(("pmc_reset_val pmc[%u]=0x%lx\n", cnum, req->reg_value)); + } + return 0; + +abort_mission: + PFM_REG_RETFLAG_SET(req->reg_flags, PFM_REG_RETFL_EINVAL); + return ret; +} + +static int +pfm_check_task_exist(pfm_context_t *ctx) +{ + struct task_struct *g, *t; + int ret = -ESRCH; + + read_lock(&tasklist_lock); + + do_each_thread (g, t) { + if (t->thread.pfm_context == ctx) { + ret = 0; + break; + } + } while_each_thread (g, t); + + read_unlock(&tasklist_lock); + + DPRINT(("pfm_check_task_exist: ret=%d ctx=%p\n", ret, ctx)); + + return ret; +} + +static int +pfm_context_load(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) +{ + struct task_struct *task; + struct thread_struct *thread; + struct pfm_context_t *old; + unsigned long flags; +#ifndef CONFIG_SMP + struct task_struct *owner_task = NULL; +#endif + pfarg_load_t *req = (pfarg_load_t *)arg; + unsigned long *pmcs_source, *pmds_source; + int the_cpu; + int ret = 0; + int state, is_system, set_dbregs = 0; + + if (is_running_on_xen()) { + if (is_xenoprof_primary()) + return HYPERVISOR_perfmon_op(PFM_LOAD_CONTEXT, arg, 0); + return 0; + } + state = ctx->ctx_state; + is_system = ctx->ctx_fl_system; + /* + * can only load from unloaded or terminated state + */ + if (state != PFM_CTX_UNLOADED) { + DPRINT(("cannot load to [%d], invalid ctx_state=%d\n", + req->load_pid, + ctx->ctx_state)); + return -EBUSY; + } + + DPRINT(("load_pid [%d] using_dbreg=%d\n", req->load_pid, ctx->ctx_fl_using_dbreg)); + + if (CTX_OVFL_NOBLOCK(ctx) == 0 && req->load_pid == current->pid) { + DPRINT(("cannot use blocking mode on self\n")); + return -EINVAL; + } + + ret = pfm_get_task(ctx, req->load_pid, &task); + if (ret) { + DPRINT(("load_pid [%d] get_task=%d\n", req->load_pid, ret)); + return ret; + } + + ret = -EINVAL; + + /* + * system wide is self monitoring only + */ + if (is_system && task != current) { + DPRINT(("system wide is self monitoring only load_pid=%d\n", + req->load_pid)); + goto error; + } + + thread = &task->thread; + + ret = 0; + /* + * cannot load a context which is using range restrictions, + * into a task that is being debugged. + */ + if (ctx->ctx_fl_using_dbreg) { + if (thread->flags & IA64_THREAD_DBG_VALID) { + ret = -EBUSY; + DPRINT(("load_pid [%d] task is debugged, cannot load range restrictions\n", req->load_pid)); + goto error; + } + LOCK_PFS(flags); + + if (is_system) { + if (pfm_sessions.pfs_ptrace_use_dbregs) { + DPRINT(("cannot load [%d] dbregs in use\n", task->pid)); + ret = -EBUSY; + } else { + pfm_sessions.pfs_sys_use_dbregs++; + DPRINT(("load [%d] increased sys_use_dbreg=%u\n", task->pid, pfm_sessions.pfs_sys_use_dbregs)); + set_dbregs = 1; + } + } + + UNLOCK_PFS(flags); + + if (ret) goto error; + } + + /* + * SMP system-wide monitoring implies self-monitoring. + * + * The programming model expects the task to + * be pinned on a CPU throughout the session. + * Here we take note of the current CPU at the + * time the context is loaded. No call from + * another CPU will be allowed. + * + * The pinning via shed_setaffinity() + * must be done by the calling task prior + * to this call. + * + * systemwide: keep track of CPU this session is supposed to run on + */ + the_cpu = ctx->ctx_cpu = smp_processor_id(); + + ret = -EBUSY; + /* + * now reserve the session + */ + ret = pfm_reserve_session(current, is_system, the_cpu); + if (ret) goto error; + + /* + * task is necessarily stopped at this point. + * + * If the previous context was zombie, then it got removed in + * pfm_save_regs(). Therefore we should not see it here. + * If we see a context, then this is an active context + * + * XXX: needs to be atomic + */ + DPRINT(("before cmpxchg() old_ctx=%p new_ctx=%p\n", + thread->pfm_context, ctx)); + + ret = -EBUSY; + old = ia64_cmpxchg(acq, &thread->pfm_context, NULL, ctx, sizeof(pfm_context_t *)); + if (old != NULL) { + DPRINT(("load_pid [%d] already has a context\n", req->load_pid)); + goto error_unres; + } + + pfm_reset_msgq(ctx); + + ctx->ctx_state = PFM_CTX_LOADED; + + /* + * link context to task + */ + ctx->ctx_task = task; + + if (is_system) { + /* + * we load as stopped + */ + PFM_CPUINFO_SET(PFM_CPUINFO_SYST_WIDE); + PFM_CPUINFO_CLEAR(PFM_CPUINFO_DCR_PP); + + if (ctx->ctx_fl_excl_idle) PFM_CPUINFO_SET(PFM_CPUINFO_EXCL_IDLE); + } else { + thread->flags |= IA64_THREAD_PM_VALID; + } + + /* + * propagate into thread-state + */ + pfm_copy_pmds(task, ctx); + pfm_copy_pmcs(task, ctx); + + pmcs_source = thread->pmcs; + pmds_source = thread->pmds; + + /* + * always the case for system-wide + */ + if (task == current) { + + if (is_system == 0) { + + /* allow user level control */ + ia64_psr(regs)->sp = 0; + DPRINT(("clearing psr.sp for [%d]\n", task->pid)); + + SET_LAST_CPU(ctx, smp_processor_id()); + INC_ACTIVATION(); + SET_ACTIVATION(ctx); +#ifndef CONFIG_SMP + /* + * push the other task out, if any + */ + owner_task = GET_PMU_OWNER(); + if (owner_task) pfm_lazy_save_regs(owner_task); +#endif + } + /* + * load all PMD from ctx to PMU (as opposed to thread state) + * restore all PMC from ctx to PMU + */ + pfm_restore_pmds(pmds_source, ctx->ctx_all_pmds[0]); + pfm_restore_pmcs(pmcs_source, ctx->ctx_all_pmcs[0]); + + ctx->ctx_reload_pmcs[0] = 0UL; + ctx->ctx_reload_pmds[0] = 0UL; + + /* + * guaranteed safe by earlier check against DBG_VALID + */ + if (ctx->ctx_fl_using_dbreg) { + pfm_restore_ibrs(ctx->ctx_ibrs, pmu_conf->num_ibrs); + pfm_restore_dbrs(ctx->ctx_dbrs, pmu_conf->num_dbrs); + } + /* + * set new ownership + */ + SET_PMU_OWNER(task, ctx); + + DPRINT(("context loaded on PMU for [%d]\n", task->pid)); + } else { + /* + * when not current, task MUST be stopped, so this is safe + */ + regs = task_pt_regs(task); + + /* force a full reload */ + ctx->ctx_last_activation = PFM_INVALID_ACTIVATION; + SET_LAST_CPU(ctx, -1); + + /* initial saved psr (stopped) */ + ctx->ctx_saved_psr_up = 0UL; + ia64_psr(regs)->up = ia64_psr(regs)->pp = 0; + } + + ret = 0; + +error_unres: + if (ret) pfm_unreserve_session(ctx, ctx->ctx_fl_system, the_cpu); +error: + /* + * we must undo the dbregs setting (for system-wide) + */ + if (ret && set_dbregs) { + LOCK_PFS(flags); + pfm_sessions.pfs_sys_use_dbregs--; + UNLOCK_PFS(flags); + } + /* + * release task, there is now a link with the context + */ + if (is_system == 0 && task != current) { + pfm_put_task(task); + + if (ret == 0) { + ret = pfm_check_task_exist(ctx); + if (ret) { + ctx->ctx_state = PFM_CTX_UNLOADED; + ctx->ctx_task = NULL; + } + } + } + return ret; +} + +/* + * in this function, we do not need to increase the use count + * for the task via get_task_struct(), because we hold the + * context lock. If the task were to disappear while having + * a context attached, it would go through pfm_exit_thread() + * which also grabs the context lock and would therefore be blocked + * until we are here. + */ +static void pfm_flush_pmds(struct task_struct *, pfm_context_t *ctx); + +static int +pfm_context_unload(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) +{ + struct task_struct *task = PFM_CTX_TASK(ctx); + struct pt_regs *tregs; + int prev_state, is_system; + int ret; + + if (is_running_on_xen()) { + if (is_xenoprof_primary()) + return HYPERVISOR_perfmon_op(PFM_UNLOAD_CONTEXT, + NULL, 0); + return 0; + } + DPRINT(("ctx_state=%d task [%d]\n", ctx->ctx_state, task ? task->pid : -1)); + + prev_state = ctx->ctx_state; + is_system = ctx->ctx_fl_system; + + /* + * unload only when necessary + */ + if (prev_state == PFM_CTX_UNLOADED) { + DPRINT(("ctx_state=%d, nothing to do\n", prev_state)); + return 0; + } + + /* + * clear psr and dcr bits + */ + ret = pfm_stop(ctx, NULL, 0, regs); + if (ret) return ret; + + ctx->ctx_state = PFM_CTX_UNLOADED; + + /* + * in system mode, we need to update the PMU directly + * and the user level state of the caller, which may not + * necessarily be the creator of the context. + */ + if (is_system) { + + /* + * Update cpuinfo + * + * local PMU is taken care of in pfm_stop() + */ + PFM_CPUINFO_CLEAR(PFM_CPUINFO_SYST_WIDE); + PFM_CPUINFO_CLEAR(PFM_CPUINFO_EXCL_IDLE); + + /* + * save PMDs in context + * release ownership + */ + pfm_flush_pmds(current, ctx); + + /* + * at this point we are done with the PMU + * so we can unreserve the resource. + */ + if (prev_state != PFM_CTX_ZOMBIE) + pfm_unreserve_session(ctx, 1 , ctx->ctx_cpu); + + /* + * disconnect context from task + */ + task->thread.pfm_context = NULL; + /* + * disconnect task from context + */ + ctx->ctx_task = NULL; + + /* + * There is nothing more to cleanup here. + */ + return 0; + } + + /* + * per-task mode + */ + tregs = task == current ? regs : task_pt_regs(task); + + if (task == current) { + /* + * cancel user level control + */ + ia64_psr(regs)->sp = 1; + + DPRINT(("setting psr.sp for [%d]\n", task->pid)); + } + /* + * save PMDs to context + * release ownership + */ + pfm_flush_pmds(task, ctx); + + /* + * at this point we are done with the PMU + * so we can unreserve the resource. + * + * when state was ZOMBIE, we have already unreserved. + */ + if (prev_state != PFM_CTX_ZOMBIE) + pfm_unreserve_session(ctx, 0 , ctx->ctx_cpu); + + /* + * reset activation counter and psr + */ + ctx->ctx_last_activation = PFM_INVALID_ACTIVATION; + SET_LAST_CPU(ctx, -1); + + /* + * PMU state will not be restored + */ + task->thread.flags &= ~IA64_THREAD_PM_VALID; + + /* + * break links between context and task + */ + task->thread.pfm_context = NULL; + ctx->ctx_task = NULL; + + PFM_SET_WORK_PENDING(task, 0); + + ctx->ctx_fl_trap_reason = PFM_TRAP_REASON_NONE; + ctx->ctx_fl_can_restart = 0; + ctx->ctx_fl_going_zombie = 0; + + DPRINT(("disconnected [%d] from context\n", task->pid)); + + return 0; +} + + +/* + * called only from exit_thread(): task == current + * we come here only if current has a context attached (loaded or masked) + */ +void +pfm_exit_thread(struct task_struct *task) +{ + pfm_context_t *ctx; + unsigned long flags; + struct pt_regs *regs = task_pt_regs(task); + int ret, state; + int free_ok = 0; + + ctx = PFM_GET_CTX(task); + + PROTECT_CTX(ctx, flags); + + DPRINT(("state=%d task [%d]\n", ctx->ctx_state, task->pid)); + + state = ctx->ctx_state; + switch(state) { + case PFM_CTX_UNLOADED: + /* + * only comes to thios function if pfm_context is not NULL, i.e., cannot + * be in unloaded state + */ + printk(KERN_ERR "perfmon: pfm_exit_thread [%d] ctx unloaded\n", task->pid); + break; + case PFM_CTX_LOADED: + case PFM_CTX_MASKED: + ret = pfm_context_unload(ctx, NULL, 0, regs); + if (ret) { + printk(KERN_ERR "perfmon: pfm_exit_thread [%d] state=%d unload failed %d\n", task->pid, state, ret); + } + DPRINT(("ctx unloaded for current state was %d\n", state)); + + pfm_end_notify_user(ctx); + break; + case PFM_CTX_ZOMBIE: + ret = pfm_context_unload(ctx, NULL, 0, regs); + if (ret) { + printk(KERN_ERR "perfmon: pfm_exit_thread [%d] state=%d unload failed %d\n", task->pid, state, ret); + } + free_ok = 1; + break; + default: + printk(KERN_ERR "perfmon: pfm_exit_thread [%d] unexpected state=%d\n", task->pid, state); + break; + } + UNPROTECT_CTX(ctx, flags); + + { u64 psr = pfm_get_psr(); + BUG_ON(psr & (IA64_PSR_UP|IA64_PSR_PP)); + BUG_ON(GET_PMU_OWNER()); + BUG_ON(ia64_psr(regs)->up); + BUG_ON(ia64_psr(regs)->pp); + } + + /* + * All memory free operations (especially for vmalloc'ed memory) + * MUST be done with interrupts ENABLED. + */ + if (free_ok) pfm_context_free(ctx); +} + +/* + * functions MUST be listed in the increasing order of their index (see permfon.h) + */ +#define PFM_CMD(name, flags, arg_count, arg_type, getsz) { name, #name, flags, arg_count, sizeof(arg_type), getsz } +#define PFM_CMD_S(name, flags) { name, #name, flags, 0, 0, NULL } +#define PFM_CMD_PCLRWS (PFM_CMD_FD|PFM_CMD_ARG_RW|PFM_CMD_STOP) +#define PFM_CMD_PCLRW (PFM_CMD_FD|PFM_CMD_ARG_RW) +#define PFM_CMD_NONE { NULL, "no-cmd", 0, 0, 0, NULL} + +static pfm_cmd_desc_t pfm_cmd_tab[]={ +/* 0 */PFM_CMD_NONE, +/* 1 */PFM_CMD(pfm_write_pmcs, PFM_CMD_PCLRWS, PFM_CMD_ARG_MANY, pfarg_reg_t, NULL), +/* 2 */PFM_CMD(pfm_write_pmds, PFM_CMD_PCLRWS, PFM_CMD_ARG_MANY, pfarg_reg_t, NULL), +/* 3 */PFM_CMD(pfm_read_pmds, PFM_CMD_PCLRWS, PFM_CMD_ARG_MANY, pfarg_reg_t, NULL), +/* 4 */PFM_CMD_S(pfm_stop, PFM_CMD_PCLRWS), +/* 5 */PFM_CMD_S(pfm_start, PFM_CMD_PCLRWS), +/* 6 */PFM_CMD_NONE, +/* 7 */PFM_CMD_NONE, +/* 8 */PFM_CMD(pfm_context_create, PFM_CMD_ARG_RW, 1, pfarg_context_t, pfm_ctx_getsize), +/* 9 */PFM_CMD_NONE, +/* 10 */PFM_CMD_S(pfm_restart, PFM_CMD_PCLRW), +/* 11 */PFM_CMD_NONE, +/* 12 */PFM_CMD(pfm_get_features, PFM_CMD_ARG_RW, 1, pfarg_features_t, NULL), +/* 13 */PFM_CMD(pfm_debug, 0, 1, unsigned int, NULL), +/* 14 */PFM_CMD_NONE, +/* 15 */PFM_CMD(pfm_get_pmc_reset, PFM_CMD_ARG_RW, PFM_CMD_ARG_MANY, pfarg_reg_t, NULL), +/* 16 */PFM_CMD(pfm_context_load, PFM_CMD_PCLRWS, 1, pfarg_load_t, NULL), +/* 17 */PFM_CMD_S(pfm_context_unload, PFM_CMD_PCLRWS), +/* 18 */PFM_CMD_NONE, +/* 19 */PFM_CMD_NONE, +/* 20 */PFM_CMD_NONE, +/* 21 */PFM_CMD_NONE, +/* 22 */PFM_CMD_NONE, +/* 23 */PFM_CMD_NONE, +/* 24 */PFM_CMD_NONE, +/* 25 */PFM_CMD_NONE, +/* 26 */PFM_CMD_NONE, +/* 27 */PFM_CMD_NONE, +/* 28 */PFM_CMD_NONE, +/* 29 */PFM_CMD_NONE, +/* 30 */PFM_CMD_NONE, +/* 31 */PFM_CMD_NONE, +/* 32 */PFM_CMD(pfm_write_ibrs, PFM_CMD_PCLRWS, PFM_CMD_ARG_MANY, pfarg_dbreg_t, NULL), +/* 33 */PFM_CMD(pfm_write_dbrs, PFM_CMD_PCLRWS, PFM_CMD_ARG_MANY, pfarg_dbreg_t, NULL) +}; +#define PFM_CMD_COUNT (sizeof(pfm_cmd_tab)/sizeof(pfm_cmd_desc_t)) + +static int +pfm_check_task_state(pfm_context_t *ctx, int cmd, unsigned long flags) +{ + struct task_struct *task; + int state, old_state; + +recheck: + state = ctx->ctx_state; + task = ctx->ctx_task; + + if (task == NULL) { + DPRINT(("context %d no task, state=%d\n", ctx->ctx_fd, state)); + return 0; + } + + DPRINT(("context %d state=%d [%d] task_state=%ld must_stop=%d\n", + ctx->ctx_fd, + state, + task->pid, + task->state, PFM_CMD_STOPPED(cmd))); + + /* + * self-monitoring always ok. + * + * for system-wide the caller can either be the creator of the + * context (to one to which the context is attached to) OR + * a task running on the same CPU as the session. + */ + if (task == current || ctx->ctx_fl_system) return 0; + + /* + * we are monitoring another thread + */ + switch(state) { + case PFM_CTX_UNLOADED: + /* + * if context is UNLOADED we are safe to go + */ + return 0; + case PFM_CTX_ZOMBIE: + /* + * no command can operate on a zombie context + */ + DPRINT(("cmd %d state zombie cannot operate on context\n", cmd)); + return -EINVAL; + case PFM_CTX_MASKED: + /* + * PMU state has been saved to software even though + * the thread may still be running. + */ + if (cmd != PFM_UNLOAD_CONTEXT) return 0; + } + + /* + * context is LOADED or MASKED. Some commands may need to have + * the task stopped. + * + * We could lift this restriction for UP but it would mean that + * the user has no guarantee the task would not run between + * two successive calls to perfmonctl(). That's probably OK. + * If this user wants to ensure the task does not run, then + * the task must be stopped. + */ + if (PFM_CMD_STOPPED(cmd)) { + if ((task->state != TASK_STOPPED) && (task->state != TASK_TRACED)) { + DPRINT(("[%d] task not in stopped state\n", task->pid)); + return -EBUSY; + } + /* + * task is now stopped, wait for ctxsw out + * + * This is an interesting point in the code. + * We need to unprotect the context because + * the pfm_save_regs() routines needs to grab + * the same lock. There are danger in doing + * this because it leaves a window open for + * another task to get access to the context + * and possibly change its state. The one thing + * that is not possible is for the context to disappear + * because we are protected by the VFS layer, i.e., + * get_fd()/put_fd(). + */ + old_state = state; + + UNPROTECT_CTX(ctx, flags); + + wait_task_inactive(task); + + PROTECT_CTX(ctx, flags); + + /* + * we must recheck to verify if state has changed + */ + if (ctx->ctx_state != old_state) { + DPRINT(("old_state=%d new_state=%d\n", old_state, ctx->ctx_state)); + goto recheck; + } + } + return 0; +} + +/* + * system-call entry point (must return long) + */ +asmlinkage long +sys_perfmonctl (int fd, int cmd, void __user *arg, int count) +{ + struct file *file = NULL; + pfm_context_t *ctx = NULL; + unsigned long flags = 0UL; + void *args_k = NULL; + long ret; /* will expand int return types */ + size_t base_sz, sz, xtra_sz = 0; + int narg, completed_args = 0, call_made = 0, cmd_flags; + int (*func)(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs); + int (*getsize)(void *arg, size_t *sz); +#define PFM_MAX_ARGSIZE 4096 + + /* + * reject any call if perfmon was disabled at initialization + */ + if (unlikely(pmu_conf == NULL)) return -ENOSYS; + + if (unlikely(cmd < 0 || cmd >= PFM_CMD_COUNT)) { + DPRINT(("invalid cmd=%d\n", cmd)); + return -EINVAL; + } + + func = pfm_cmd_tab[cmd].cmd_func; + narg = pfm_cmd_tab[cmd].cmd_narg; + base_sz = pfm_cmd_tab[cmd].cmd_argsize; + getsize = pfm_cmd_tab[cmd].cmd_getsize; + cmd_flags = pfm_cmd_tab[cmd].cmd_flags; + + if (unlikely(func == NULL)) { + DPRINT(("invalid cmd=%d\n", cmd)); + return -EINVAL; + } + + DPRINT(("cmd=%s idx=%d narg=0x%x argsz=%lu count=%d\n", + PFM_CMD_NAME(cmd), + cmd, + narg, + base_sz, + count)); + + /* + * check if number of arguments matches what the command expects + */ + if (unlikely((narg == PFM_CMD_ARG_MANY && count <= 0) || (narg > 0 && narg != count))) + return -EINVAL; + +restart_args: + sz = xtra_sz + base_sz*count; + /* + * limit abuse to min page size + */ + if (unlikely(sz > PFM_MAX_ARGSIZE)) { + printk(KERN_ERR "perfmon: [%d] argument too big %lu\n", current->pid, sz); + return -E2BIG; + } + + /* + * allocate default-sized argument buffer + */ + if (likely(count && args_k == NULL)) { + args_k = kmalloc(PFM_MAX_ARGSIZE, GFP_KERNEL); + if (args_k == NULL) return -ENOMEM; + } + + ret = -EFAULT; + + /* + * copy arguments + * + * assume sz = 0 for command without parameters + */ + if (sz && copy_from_user(args_k, arg, sz)) { + DPRINT(("cannot copy_from_user %lu bytes @%p\n", sz, arg)); + goto error_args; + } + + /* + * check if command supports extra parameters + */ + if (completed_args == 0 && getsize) { + /* + * get extra parameters size (based on main argument) + */ + ret = (*getsize)(args_k, &xtra_sz); + if (ret) goto error_args; + + completed_args = 1; + + DPRINT(("restart_args sz=%lu xtra_sz=%lu\n", sz, xtra_sz)); + + /* retry if necessary */ + if (likely(xtra_sz)) goto restart_args; + } + + if (unlikely((cmd_flags & PFM_CMD_FD) == 0)) goto skip_fd; + + ret = -EBADF; + + file = fget(fd); + if (unlikely(file == NULL)) { + DPRINT(("invalid fd %d\n", fd)); + goto error_args; + } + if (unlikely(PFM_IS_FILE(file) == 0)) { + DPRINT(("fd %d not related to perfmon\n", fd)); + goto error_args; + } + + ctx = (pfm_context_t *)file->private_data; + if (unlikely(ctx == NULL)) { + DPRINT(("no context for fd %d\n", fd)); + goto error_args; + } + prefetch(&ctx->ctx_state); + + PROTECT_CTX(ctx, flags); + + /* + * check task is stopped + */ + ret = pfm_check_task_state(ctx, cmd, flags); + if (unlikely(ret)) goto abort_locked; + +skip_fd: + ret = (*func)(ctx, args_k, count, task_pt_regs(current)); + + call_made = 1; + +abort_locked: + if (likely(ctx)) { + DPRINT(("context unlocked\n")); + UNPROTECT_CTX(ctx, flags); + } + + /* copy argument back to user, if needed */ + if (call_made && PFM_CMD_RW_ARG(cmd) && copy_to_user(arg, args_k, base_sz*count)) ret = -EFAULT; + +error_args: + if (file) + fput(file); + + kfree(args_k); + + DPRINT(("cmd=%s ret=%ld\n", PFM_CMD_NAME(cmd), ret)); + + return ret; +} + +static void +pfm_resume_after_ovfl(pfm_context_t *ctx, unsigned long ovfl_regs, struct pt_regs *regs) +{ + pfm_buffer_fmt_t *fmt = ctx->ctx_buf_fmt; + pfm_ovfl_ctrl_t rst_ctrl; + int state; + int ret = 0; + + state = ctx->ctx_state; + /* + * Unlock sampling buffer and reset index atomically + * XXX: not really needed when blocking + */ + if (CTX_HAS_SMPL(ctx)) { + + rst_ctrl.bits.mask_monitoring = 0; + rst_ctrl.bits.reset_ovfl_pmds = 0; + + if (state == PFM_CTX_LOADED) + ret = pfm_buf_fmt_restart_active(fmt, current, &rst_ctrl, ctx->ctx_smpl_hdr, regs); + else + ret = pfm_buf_fmt_restart(fmt, current, &rst_ctrl, ctx->ctx_smpl_hdr, regs); + } else { + rst_ctrl.bits.mask_monitoring = 0; + rst_ctrl.bits.reset_ovfl_pmds = 1; + } + + if (ret == 0) { + if (rst_ctrl.bits.reset_ovfl_pmds) { + pfm_reset_regs(ctx, &ovfl_regs, PFM_PMD_LONG_RESET); + } + if (rst_ctrl.bits.mask_monitoring == 0) { + DPRINT(("resuming monitoring\n")); + if (ctx->ctx_state == PFM_CTX_MASKED) pfm_restore_monitoring(current); + } else { + DPRINT(("stopping monitoring\n")); + //pfm_stop_monitoring(current, regs); + } + ctx->ctx_state = PFM_CTX_LOADED; + } +} + +/* + * context MUST BE LOCKED when calling + * can only be called for current + */ +static void +pfm_context_force_terminate(pfm_context_t *ctx, struct pt_regs *regs) +{ + int ret; + + DPRINT(("entering for [%d]\n", current->pid)); + + ret = pfm_context_unload(ctx, NULL, 0, regs); + if (ret) { + printk(KERN_ERR "pfm_context_force_terminate: [%d] unloaded failed with %d\n", current->pid, ret); + } + + /* + * and wakeup controlling task, indicating we are now disconnected + */ + wake_up_interruptible(&ctx->ctx_zombieq); + + /* + * given that context is still locked, the controlling + * task will only get access when we return from + * pfm_handle_work(). + */ +} + +static int pfm_ovfl_notify_user(pfm_context_t *ctx, unsigned long ovfl_pmds); + /* + * pfm_handle_work() can be called with interrupts enabled + * (TIF_NEED_RESCHED) or disabled. The down_interruptible + * call may sleep, therefore we must re-enable interrupts + * to avoid deadlocks. It is safe to do so because this function + * is called ONLY when returning to user level (PUStk=1), in which case + * there is no risk of kernel stack overflow due to deep + * interrupt nesting. + */ +void +pfm_handle_work(void) +{ + pfm_context_t *ctx; + struct pt_regs *regs; + unsigned long flags, dummy_flags; + unsigned long ovfl_regs; + unsigned int reason; + int ret; + + ctx = PFM_GET_CTX(current); + if (ctx == NULL) { + printk(KERN_ERR "perfmon: [%d] has no PFM context\n", current->pid); + return; + } + + PROTECT_CTX(ctx, flags); + + PFM_SET_WORK_PENDING(current, 0); + + pfm_clear_task_notify(); + + regs = task_pt_regs(current); + + /* + * extract reason for being here and clear + */ + reason = ctx->ctx_fl_trap_reason; + ctx->ctx_fl_trap_reason = PFM_TRAP_REASON_NONE; + ovfl_regs = ctx->ctx_ovfl_regs[0]; + + DPRINT(("reason=%d state=%d\n", reason, ctx->ctx_state)); + + /* + * must be done before we check for simple-reset mode + */ + if (ctx->ctx_fl_going_zombie || ctx->ctx_state == PFM_CTX_ZOMBIE) goto do_zombie; + + + //if (CTX_OVFL_NOBLOCK(ctx)) goto skip_blocking; + if (reason == PFM_TRAP_REASON_RESET) goto skip_blocking; + + /* + * restore interrupt mask to what it was on entry. + * Could be enabled/diasbled. + */ + UNPROTECT_CTX(ctx, flags); + + /* + * force interrupt enable because of down_interruptible() + */ + local_irq_enable(); + + DPRINT(("before block sleeping\n")); + + /* + * may go through without blocking on SMP systems + * if restart has been received already by the time we call down() + */ + ret = wait_for_completion_interruptible(&ctx->ctx_restart_done); + + DPRINT(("after block sleeping ret=%d\n", ret)); + + /* + * lock context and mask interrupts again + * We save flags into a dummy because we may have + * altered interrupts mask compared to entry in this + * function. + */ + PROTECT_CTX(ctx, dummy_flags); + + /* + * we need to read the ovfl_regs only after wake-up + * because we may have had pfm_write_pmds() in between + * and that can changed PMD values and therefore + * ovfl_regs is reset for these new PMD values. + */ + ovfl_regs = ctx->ctx_ovfl_regs[0]; + + if (ctx->ctx_fl_going_zombie) { +do_zombie: + DPRINT(("context is zombie, bailing out\n")); + pfm_context_force_terminate(ctx, regs); + goto nothing_to_do; + } + /* + * in case of interruption of down() we don't restart anything + */ + if (ret < 0) goto nothing_to_do; + +skip_blocking: + pfm_resume_after_ovfl(ctx, ovfl_regs, regs); + ctx->ctx_ovfl_regs[0] = 0UL; + +nothing_to_do: + /* + * restore flags as they were upon entry + */ + UNPROTECT_CTX(ctx, flags); +} + +static int +pfm_notify_user(pfm_context_t *ctx, pfm_msg_t *msg) +{ + if (ctx->ctx_state == PFM_CTX_ZOMBIE) { + DPRINT(("ignoring overflow notification, owner is zombie\n")); + return 0; + } + + DPRINT(("waking up somebody\n")); + + if (msg) wake_up_interruptible(&ctx->ctx_msgq_wait); + + /* + * safe, we are not in intr handler, nor in ctxsw when + * we come here + */ + kill_fasync (&ctx->ctx_async_queue, SIGIO, POLL_IN); + + return 0; +} + +static int +pfm_ovfl_notify_user(pfm_context_t *ctx, unsigned long ovfl_pmds) +{ + pfm_msg_t *msg = NULL; + + if (ctx->ctx_fl_no_msg == 0) { + msg = pfm_get_new_msg(ctx); + if (msg == NULL) { + printk(KERN_ERR "perfmon: pfm_ovfl_notify_user no more notification msgs\n"); + return -1; + } + + msg->pfm_ovfl_msg.msg_type = PFM_MSG_OVFL; + msg->pfm_ovfl_msg.msg_ctx_fd = ctx->ctx_fd; + msg->pfm_ovfl_msg.msg_active_set = 0; + msg->pfm_ovfl_msg.msg_ovfl_pmds[0] = ovfl_pmds; + msg->pfm_ovfl_msg.msg_ovfl_pmds[1] = 0UL; + msg->pfm_ovfl_msg.msg_ovfl_pmds[2] = 0UL; + msg->pfm_ovfl_msg.msg_ovfl_pmds[3] = 0UL; + msg->pfm_ovfl_msg.msg_tstamp = 0UL; + } + + DPRINT(("ovfl msg: msg=%p no_msg=%d fd=%d ovfl_pmds=0x%lx\n", + msg, + ctx->ctx_fl_no_msg, + ctx->ctx_fd, + ovfl_pmds)); + + return pfm_notify_user(ctx, msg); +} + +static int +pfm_end_notify_user(pfm_context_t *ctx) +{ + pfm_msg_t *msg; + + msg = pfm_get_new_msg(ctx); + if (msg == NULL) { + printk(KERN_ERR "perfmon: pfm_end_notify_user no more notification msgs\n"); + return -1; + } + /* no leak */ + memset(msg, 0, sizeof(*msg)); + + msg->pfm_end_msg.msg_type = PFM_MSG_END; + msg->pfm_end_msg.msg_ctx_fd = ctx->ctx_fd; + msg->pfm_ovfl_msg.msg_tstamp = 0UL; + + DPRINT(("end msg: msg=%p no_msg=%d ctx_fd=%d\n", + msg, + ctx->ctx_fl_no_msg, + ctx->ctx_fd)); + + return pfm_notify_user(ctx, msg); +} + +/* + * main overflow processing routine. + * it can be called from the interrupt path or explicitely during the context switch code + */ +static void +pfm_overflow_handler(struct task_struct *task, pfm_context_t *ctx, u64 pmc0, struct pt_regs *regs) +{ + pfm_ovfl_arg_t *ovfl_arg; + unsigned long mask; + unsigned long old_val, ovfl_val, new_val; + unsigned long ovfl_notify = 0UL, ovfl_pmds = 0UL, smpl_pmds = 0UL, reset_pmds; + unsigned long tstamp; + pfm_ovfl_ctrl_t ovfl_ctrl; + unsigned int i, has_smpl; + int must_notify = 0; + + if (unlikely(ctx->ctx_state == PFM_CTX_ZOMBIE)) goto stop_monitoring; + + /* + * sanity test. Should never happen + */ + if (unlikely((pmc0 & 0x1) == 0)) goto sanity_check; + + tstamp = ia64_get_itc(); + mask = pmc0 >> PMU_FIRST_COUNTER; + ovfl_val = pmu_conf->ovfl_val; + has_smpl = CTX_HAS_SMPL(ctx); + + DPRINT_ovfl(("pmc0=0x%lx pid=%d iip=0x%lx, %s " + "used_pmds=0x%lx\n", + pmc0, + task ? task->pid: -1, + (regs ? regs->cr_iip : 0), + CTX_OVFL_NOBLOCK(ctx) ? "nonblocking" : "blocking", + ctx->ctx_used_pmds[0])); + + + /* + * first we update the virtual counters + * assume there was a prior ia64_srlz_d() issued + */ + for (i = PMU_FIRST_COUNTER; mask ; i++, mask >>= 1) { + + /* skip pmd which did not overflow */ + if ((mask & 0x1) == 0) continue; + + /* + * Note that the pmd is not necessarily 0 at this point as qualified events + * may have happened before the PMU was frozen. The residual count is not + * taken into consideration here but will be with any read of the pmd via + * pfm_read_pmds(). + */ + old_val = new_val = ctx->ctx_pmds[i].val; + new_val += 1 + ovfl_val; + ctx->ctx_pmds[i].val = new_val; + + /* + * check for overflow condition + */ + if (likely(old_val > new_val)) { + ovfl_pmds |= 1UL << i; + if (PMC_OVFL_NOTIFY(ctx, i)) ovfl_notify |= 1UL << i; + } + + DPRINT_ovfl(("ctx_pmd[%d].val=0x%lx old_val=0x%lx pmd=0x%lx ovfl_pmds=0x%lx ovfl_notify=0x%lx\n", + i, + new_val, + old_val, + ia64_get_pmd(i) & ovfl_val, + ovfl_pmds, + ovfl_notify)); + } + + /* + * there was no 64-bit overflow, nothing else to do + */ + if (ovfl_pmds == 0UL) return; + + /* + * reset all control bits + */ + ovfl_ctrl.val = 0; + reset_pmds = 0UL; + + /* + * if a sampling format module exists, then we "cache" the overflow by + * calling the module's handler() routine. + */ + if (has_smpl) { + unsigned long start_cycles, end_cycles; + unsigned long pmd_mask; + int j, k, ret = 0; + int this_cpu = smp_processor_id(); + + pmd_mask = ovfl_pmds >> PMU_FIRST_COUNTER; + ovfl_arg = &ctx->ctx_ovfl_arg; + + prefetch(ctx->ctx_smpl_hdr); + + for(i=PMU_FIRST_COUNTER; pmd_mask && ret == 0; i++, pmd_mask >>=1) { + + mask = 1UL << i; + + if ((pmd_mask & 0x1) == 0) continue; + + ovfl_arg->ovfl_pmd = (unsigned char )i; + ovfl_arg->ovfl_notify = ovfl_notify & mask ? 1 : 0; + ovfl_arg->active_set = 0; + ovfl_arg->ovfl_ctrl.val = 0; /* module must fill in all fields */ + ovfl_arg->smpl_pmds[0] = smpl_pmds = ctx->ctx_pmds[i].smpl_pmds[0]; + + ovfl_arg->pmd_value = ctx->ctx_pmds[i].val; + ovfl_arg->pmd_last_reset = ctx->ctx_pmds[i].lval; + ovfl_arg->pmd_eventid = ctx->ctx_pmds[i].eventid; + + /* + * copy values of pmds of interest. Sampling format may copy them + * into sampling buffer. + */ + if (smpl_pmds) { + for(j=0, k=0; smpl_pmds; j++, smpl_pmds >>=1) { + if ((smpl_pmds & 0x1) == 0) continue; + ovfl_arg->smpl_pmds_values[k++] = PMD_IS_COUNTING(j) ? pfm_read_soft_counter(ctx, j) : ia64_get_pmd(j); + DPRINT_ovfl(("smpl_pmd[%d]=pmd%u=0x%lx\n", k-1, j, ovfl_arg->smpl_pmds_values[k-1])); + } + } + + pfm_stats[this_cpu].pfm_smpl_handler_calls++; + + start_cycles = ia64_get_itc(); + + /* + * call custom buffer format record (handler) routine + */ + ret = (*ctx->ctx_buf_fmt->fmt_handler)(task, ctx->ctx_smpl_hdr, ovfl_arg, regs, tstamp); + + end_cycles = ia64_get_itc(); + + /* + * For those controls, we take the union because they have + * an all or nothing behavior. + */ + ovfl_ctrl.bits.notify_user |= ovfl_arg->ovfl_ctrl.bits.notify_user; + ovfl_ctrl.bits.block_task |= ovfl_arg->ovfl_ctrl.bits.block_task; + ovfl_ctrl.bits.mask_monitoring |= ovfl_arg->ovfl_ctrl.bits.mask_monitoring; + /* + * build the bitmask of pmds to reset now + */ + if (ovfl_arg->ovfl_ctrl.bits.reset_ovfl_pmds) reset_pmds |= mask; + + pfm_stats[this_cpu].pfm_smpl_handler_cycles += end_cycles - start_cycles; + } + /* + * when the module cannot handle the rest of the overflows, we abort right here + */ + if (ret && pmd_mask) { + DPRINT(("handler aborts leftover ovfl_pmds=0x%lx\n", + pmd_mask<ctx_ovfl_regs[0] = ovfl_pmds; + + /* + * check for blocking context + */ + if (CTX_OVFL_NOBLOCK(ctx) == 0 && ovfl_ctrl.bits.block_task) { + + ctx->ctx_fl_trap_reason = PFM_TRAP_REASON_BLOCK; + + /* + * set the perfmon specific checking pending work for the task + */ + PFM_SET_WORK_PENDING(task, 1); + + /* + * when coming from ctxsw, current still points to the + * previous task, therefore we must work with task and not current. + */ + pfm_set_task_notify(task); + } + /* + * defer until state is changed (shorten spin window). the context is locked + * anyway, so the signal receiver would come spin for nothing. + */ + must_notify = 1; + } + + DPRINT_ovfl(("owner [%d] pending=%ld reason=%u ovfl_pmds=0x%lx ovfl_notify=0x%lx masked=%d\n", + GET_PMU_OWNER() ? GET_PMU_OWNER()->pid : -1, + PFM_GET_WORK_PENDING(task), + ctx->ctx_fl_trap_reason, + ovfl_pmds, + ovfl_notify, + ovfl_ctrl.bits.mask_monitoring ? 1 : 0)); + /* + * in case monitoring must be stopped, we toggle the psr bits + */ + if (ovfl_ctrl.bits.mask_monitoring) { + pfm_mask_monitoring(task); + ctx->ctx_state = PFM_CTX_MASKED; + ctx->ctx_fl_can_restart = 1; + } + + /* + * send notification now + */ + if (must_notify) pfm_ovfl_notify_user(ctx, ovfl_notify); + + return; + +sanity_check: + printk(KERN_ERR "perfmon: CPU%d overflow handler [%d] pmc0=0x%lx\n", + smp_processor_id(), + task ? task->pid : -1, + pmc0); + return; + +stop_monitoring: + /* + * in SMP, zombie context is never restored but reclaimed in pfm_load_regs(). + * Moreover, zombies are also reclaimed in pfm_save_regs(). Therefore we can + * come here as zombie only if the task is the current task. In which case, we + * can access the PMU hardware directly. + * + * Note that zombies do have PM_VALID set. So here we do the minimal. + * + * In case the context was zombified it could not be reclaimed at the time + * the monitoring program exited. At this point, the PMU reservation has been + * returned, the sampiing buffer has been freed. We must convert this call + * into a spurious interrupt. However, we must also avoid infinite overflows + * by stopping monitoring for this task. We can only come here for a per-task + * context. All we need to do is to stop monitoring using the psr bits which + * are always task private. By re-enabling secure montioring, we ensure that + * the monitored task will not be able to re-activate monitoring. + * The task will eventually be context switched out, at which point the context + * will be reclaimed (that includes releasing ownership of the PMU). + * + * So there might be a window of time where the number of per-task session is zero + * yet one PMU might have a owner and get at most one overflow interrupt for a zombie + * context. This is safe because if a per-task session comes in, it will push this one + * out and by the virtue on pfm_save_regs(), this one will disappear. If a system wide + * session is force on that CPU, given that we use task pinning, pfm_save_regs() will + * also push our zombie context out. + * + * Overall pretty hairy stuff.... + */ + DPRINT(("ctx is zombie for [%d], converted to spurious\n", task ? task->pid: -1)); + pfm_clear_psr_up(); + ia64_psr(regs)->up = 0; + ia64_psr(regs)->sp = 1; + return; +} + +static int +pfm_do_interrupt_handler(int irq, void *arg, struct pt_regs *regs) +{ + struct task_struct *task; + pfm_context_t *ctx; + unsigned long flags; + u64 pmc0; + int this_cpu = smp_processor_id(); + int retval = 0; + + pfm_stats[this_cpu].pfm_ovfl_intr_count++; + + /* + * srlz.d done before arriving here + */ + pmc0 = ia64_get_pmc(0); + + task = GET_PMU_OWNER(); + ctx = GET_PMU_CTX(); + + /* + * if we have some pending bits set + * assumes : if any PMC0.bit[63-1] is set, then PMC0.fr = 1 + */ + if (PMC0_HAS_OVFL(pmc0) && task) { + /* + * we assume that pmc0.fr is always set here + */ + + /* sanity check */ + if (!ctx) goto report_spurious1; + + if (ctx->ctx_fl_system == 0 && (task->thread.flags & IA64_THREAD_PM_VALID) == 0) + goto report_spurious2; + + PROTECT_CTX_NOPRINT(ctx, flags); + + pfm_overflow_handler(task, ctx, pmc0, regs); + + UNPROTECT_CTX_NOPRINT(ctx, flags); + + } else { + pfm_stats[this_cpu].pfm_spurious_ovfl_intr_count++; + retval = -1; + } + /* + * keep it unfrozen at all times + */ + pfm_unfreeze_pmu(); + + return retval; + +report_spurious1: + printk(KERN_INFO "perfmon: spurious overflow interrupt on CPU%d: process %d has no PFM context\n", + this_cpu, task->pid); + pfm_unfreeze_pmu(); + return -1; +report_spurious2: + printk(KERN_INFO "perfmon: spurious overflow interrupt on CPU%d: process %d, invalid flag\n", + this_cpu, + task->pid); + pfm_unfreeze_pmu(); + return -1; +} + +static irqreturn_t +pfm_interrupt_handler(int irq, void *arg, struct pt_regs *regs) +{ + unsigned long start_cycles, total_cycles; + unsigned long min, max; + int this_cpu; + int ret; + + this_cpu = get_cpu(); + if (likely(!pfm_alt_intr_handler)) { + min = pfm_stats[this_cpu].pfm_ovfl_intr_cycles_min; + max = pfm_stats[this_cpu].pfm_ovfl_intr_cycles_max; + + start_cycles = ia64_get_itc(); + + ret = pfm_do_interrupt_handler(irq, arg, regs); + + total_cycles = ia64_get_itc(); + + /* + * don't measure spurious interrupts + */ + if (likely(ret == 0)) { + total_cycles -= start_cycles; + + if (total_cycles < min) pfm_stats[this_cpu].pfm_ovfl_intr_cycles_min = total_cycles; + if (total_cycles > max) pfm_stats[this_cpu].pfm_ovfl_intr_cycles_max = total_cycles; + + pfm_stats[this_cpu].pfm_ovfl_intr_cycles += total_cycles; + } + } + else { + (*pfm_alt_intr_handler->handler)(irq, arg, regs); + } + + put_cpu_no_resched(); + return IRQ_HANDLED; +} + +/* + * /proc/perfmon interface, for debug only + */ + +#define PFM_PROC_SHOW_HEADER ((void *)NR_CPUS+1) + +static void * +pfm_proc_start(struct seq_file *m, loff_t *pos) +{ + if (*pos == 0) { + return PFM_PROC_SHOW_HEADER; + } + + while (*pos <= NR_CPUS) { + if (cpu_online(*pos - 1)) { + return (void *)*pos; + } + ++*pos; + } + return NULL; +} + +static void * +pfm_proc_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return pfm_proc_start(m, pos); +} + +static void +pfm_proc_stop(struct seq_file *m, void *v) +{ +} + +static void +pfm_proc_show_header(struct seq_file *m) +{ + struct list_head * pos; + pfm_buffer_fmt_t * entry; + unsigned long flags; + + seq_printf(m, + "perfmon version : %u.%u\n" + "model : %s\n" + "fastctxsw : %s\n" + "expert mode : %s\n" + "ovfl_mask : 0x%lx\n" + "PMU flags : 0x%x\n", + PFM_VERSION_MAJ, PFM_VERSION_MIN, + pmu_conf->pmu_name, + pfm_sysctl.fastctxsw > 0 ? "Yes": "No", + pfm_sysctl.expert_mode > 0 ? "Yes": "No", + pmu_conf->ovfl_val, + pmu_conf->flags); + + LOCK_PFS(flags); + + seq_printf(m, + "proc_sessions : %u\n" + "sys_sessions : %u\n" + "sys_use_dbregs : %u\n" + "ptrace_use_dbregs : %u\n", + pfm_sessions.pfs_task_sessions, + pfm_sessions.pfs_sys_sessions, + pfm_sessions.pfs_sys_use_dbregs, + pfm_sessions.pfs_ptrace_use_dbregs); + + UNLOCK_PFS(flags); + + spin_lock(&pfm_buffer_fmt_lock); + + list_for_each(pos, &pfm_buffer_fmt_list) { + entry = list_entry(pos, pfm_buffer_fmt_t, fmt_list); + seq_printf(m, "format : %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x %s\n", + entry->fmt_uuid[0], + entry->fmt_uuid[1], + entry->fmt_uuid[2], + entry->fmt_uuid[3], + entry->fmt_uuid[4], + entry->fmt_uuid[5], + entry->fmt_uuid[6], + entry->fmt_uuid[7], + entry->fmt_uuid[8], + entry->fmt_uuid[9], + entry->fmt_uuid[10], + entry->fmt_uuid[11], + entry->fmt_uuid[12], + entry->fmt_uuid[13], + entry->fmt_uuid[14], + entry->fmt_uuid[15], + entry->fmt_name); + } + spin_unlock(&pfm_buffer_fmt_lock); + +} + +static int +pfm_proc_show(struct seq_file *m, void *v) +{ + unsigned long psr; + unsigned int i; + int cpu; + + if (v == PFM_PROC_SHOW_HEADER) { + pfm_proc_show_header(m); + return 0; + } + + /* show info for CPU (v - 1) */ + + cpu = (long)v - 1; + seq_printf(m, + "CPU%-2d overflow intrs : %lu\n" + "CPU%-2d overflow cycles : %lu\n" + "CPU%-2d overflow min : %lu\n" + "CPU%-2d overflow max : %lu\n" + "CPU%-2d smpl handler calls : %lu\n" + "CPU%-2d smpl handler cycles : %lu\n" + "CPU%-2d spurious intrs : %lu\n" + "CPU%-2d replay intrs : %lu\n" + "CPU%-2d syst_wide : %d\n" + "CPU%-2d dcr_pp : %d\n" + "CPU%-2d exclude idle : %d\n" + "CPU%-2d owner : %d\n" + "CPU%-2d context : %p\n" + "CPU%-2d activations : %lu\n", + cpu, pfm_stats[cpu].pfm_ovfl_intr_count, + cpu, pfm_stats[cpu].pfm_ovfl_intr_cycles, + cpu, pfm_stats[cpu].pfm_ovfl_intr_cycles_min, + cpu, pfm_stats[cpu].pfm_ovfl_intr_cycles_max, + cpu, pfm_stats[cpu].pfm_smpl_handler_calls, + cpu, pfm_stats[cpu].pfm_smpl_handler_cycles, + cpu, pfm_stats[cpu].pfm_spurious_ovfl_intr_count, + cpu, pfm_stats[cpu].pfm_replay_ovfl_intr_count, + cpu, pfm_get_cpu_data(pfm_syst_info, cpu) & PFM_CPUINFO_SYST_WIDE ? 1 : 0, + cpu, pfm_get_cpu_data(pfm_syst_info, cpu) & PFM_CPUINFO_DCR_PP ? 1 : 0, + cpu, pfm_get_cpu_data(pfm_syst_info, cpu) & PFM_CPUINFO_EXCL_IDLE ? 1 : 0, + cpu, pfm_get_cpu_data(pmu_owner, cpu) ? pfm_get_cpu_data(pmu_owner, cpu)->pid: -1, + cpu, pfm_get_cpu_data(pmu_ctx, cpu), + cpu, pfm_get_cpu_data(pmu_activation_number, cpu)); + + if (num_online_cpus() == 1 && pfm_sysctl.debug > 0) { + + psr = pfm_get_psr(); + + ia64_srlz_d(); + + seq_printf(m, + "CPU%-2d psr : 0x%lx\n" + "CPU%-2d pmc0 : 0x%lx\n", + cpu, psr, + cpu, ia64_get_pmc(0)); + + for (i=0; PMC_IS_LAST(i) == 0; i++) { + if (PMC_IS_COUNTING(i) == 0) continue; + seq_printf(m, + "CPU%-2d pmc%u : 0x%lx\n" + "CPU%-2d pmd%u : 0x%lx\n", + cpu, i, ia64_get_pmc(i), + cpu, i, ia64_get_pmd(i)); + } + } + return 0; +} + +struct seq_operations pfm_seq_ops = { + .start = pfm_proc_start, + .next = pfm_proc_next, + .stop = pfm_proc_stop, + .show = pfm_proc_show +}; + +static int +pfm_proc_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &pfm_seq_ops); +} + + +/* + * we come here as soon as local_cpu_data->pfm_syst_wide is set. this happens + * during pfm_enable() hence before pfm_start(). We cannot assume monitoring + * is active or inactive based on mode. We must rely on the value in + * local_cpu_data->pfm_syst_info + */ +void +pfm_syst_wide_update_task(struct task_struct *task, unsigned long info, int is_ctxswin) +{ + struct pt_regs *regs; + unsigned long dcr; + unsigned long dcr_pp; + + dcr_pp = info & PFM_CPUINFO_DCR_PP ? 1 : 0; + + /* + * pid 0 is guaranteed to be the idle task. There is one such task with pid 0 + * on every CPU, so we can rely on the pid to identify the idle task. + */ + if ((info & PFM_CPUINFO_EXCL_IDLE) == 0 || task->pid) { + regs = task_pt_regs(task); + ia64_psr(regs)->pp = is_ctxswin ? dcr_pp : 0; + return; + } + /* + * if monitoring has started + */ + if (dcr_pp) { + dcr = ia64_getreg(_IA64_REG_CR_DCR); + /* + * context switching in? + */ + if (is_ctxswin) { + /* mask monitoring for the idle task */ + ia64_setreg(_IA64_REG_CR_DCR, dcr & ~IA64_DCR_PP); + pfm_clear_psr_pp(); + ia64_srlz_i(); + return; + } + /* + * context switching out + * restore monitoring for next task + * + * Due to inlining this odd if-then-else construction generates + * better code. + */ + ia64_setreg(_IA64_REG_CR_DCR, dcr |IA64_DCR_PP); + pfm_set_psr_pp(); + ia64_srlz_i(); + } +} + +#ifdef CONFIG_SMP + +static void +pfm_force_cleanup(pfm_context_t *ctx, struct pt_regs *regs) +{ + struct task_struct *task = ctx->ctx_task; + + ia64_psr(regs)->up = 0; + ia64_psr(regs)->sp = 1; + + if (GET_PMU_OWNER() == task) { + DPRINT(("cleared ownership for [%d]\n", ctx->ctx_task->pid)); + SET_PMU_OWNER(NULL, NULL); + } + + /* + * disconnect the task from the context and vice-versa + */ + PFM_SET_WORK_PENDING(task, 0); + + task->thread.pfm_context = NULL; + task->thread.flags &= ~IA64_THREAD_PM_VALID; + + DPRINT(("force cleanup for [%d]\n", task->pid)); +} + + +/* + * in 2.6, interrupts are masked when we come here and the runqueue lock is held + */ +void +pfm_save_regs(struct task_struct *task) +{ + pfm_context_t *ctx; + struct thread_struct *t; + unsigned long flags; + u64 psr; + + + ctx = PFM_GET_CTX(task); + if (ctx == NULL) return; + t = &task->thread; + + /* + * we always come here with interrupts ALREADY disabled by + * the scheduler. So we simply need to protect against concurrent + * access, not CPU concurrency. + */ + flags = pfm_protect_ctx_ctxsw(ctx); + + if (ctx->ctx_state == PFM_CTX_ZOMBIE) { + struct pt_regs *regs = task_pt_regs(task); + + pfm_clear_psr_up(); + + pfm_force_cleanup(ctx, regs); + + BUG_ON(ctx->ctx_smpl_hdr); + + pfm_unprotect_ctx_ctxsw(ctx, flags); + + pfm_context_free(ctx); + return; + } + + /* + * save current PSR: needed because we modify it + */ + ia64_srlz_d(); + psr = pfm_get_psr(); + + BUG_ON(psr & (IA64_PSR_I)); + + /* + * stop monitoring: + * This is the last instruction which may generate an overflow + * + * We do not need to set psr.sp because, it is irrelevant in kernel. + * It will be restored from ipsr when going back to user level + */ + pfm_clear_psr_up(); + + /* + * keep a copy of psr.up (for reload) + */ + ctx->ctx_saved_psr_up = psr & IA64_PSR_UP; + + /* + * release ownership of this PMU. + * PM interrupts are masked, so nothing + * can happen. + */ + SET_PMU_OWNER(NULL, NULL); + + /* + * we systematically save the PMD as we have no + * guarantee we will be schedule at that same + * CPU again. + */ + pfm_save_pmds(t->pmds, ctx->ctx_used_pmds[0]); + + /* + * save pmc0 ia64_srlz_d() done in pfm_save_pmds() + * we will need it on the restore path to check + * for pending overflow. + */ + t->pmcs[0] = ia64_get_pmc(0); + + /* + * unfreeze PMU if had pending overflows + */ + if (t->pmcs[0] & ~0x1UL) pfm_unfreeze_pmu(); + + /* + * finally, allow context access. + * interrupts will still be masked after this call. + */ + pfm_unprotect_ctx_ctxsw(ctx, flags); +} + +#else /* !CONFIG_SMP */ +void +pfm_save_regs(struct task_struct *task) +{ + pfm_context_t *ctx; + u64 psr; + + ctx = PFM_GET_CTX(task); + if (ctx == NULL) return; + + /* + * save current PSR: needed because we modify it + */ + psr = pfm_get_psr(); + + BUG_ON(psr & (IA64_PSR_I)); + + /* + * stop monitoring: + * This is the last instruction which may generate an overflow + * + * We do not need to set psr.sp because, it is irrelevant in kernel. + * It will be restored from ipsr when going back to user level + */ + pfm_clear_psr_up(); + + /* + * keep a copy of psr.up (for reload) + */ + ctx->ctx_saved_psr_up = psr & IA64_PSR_UP; +} + +static void +pfm_lazy_save_regs (struct task_struct *task) +{ + pfm_context_t *ctx; + struct thread_struct *t; + unsigned long flags; + + { u64 psr = pfm_get_psr(); + BUG_ON(psr & IA64_PSR_UP); + } + + ctx = PFM_GET_CTX(task); + t = &task->thread; + + /* + * we need to mask PMU overflow here to + * make sure that we maintain pmc0 until + * we save it. overflow interrupts are + * treated as spurious if there is no + * owner. + * + * XXX: I don't think this is necessary + */ + PROTECT_CTX(ctx,flags); + + /* + * release ownership of this PMU. + * must be done before we save the registers. + * + * after this call any PMU interrupt is treated + * as spurious. + */ + SET_PMU_OWNER(NULL, NULL); + + /* + * save all the pmds we use + */ + pfm_save_pmds(t->pmds, ctx->ctx_used_pmds[0]); + + /* + * save pmc0 ia64_srlz_d() done in pfm_save_pmds() + * it is needed to check for pended overflow + * on the restore path + */ + t->pmcs[0] = ia64_get_pmc(0); + + /* + * unfreeze PMU if had pending overflows + */ + if (t->pmcs[0] & ~0x1UL) pfm_unfreeze_pmu(); + + /* + * now get can unmask PMU interrupts, they will + * be treated as purely spurious and we will not + * lose any information + */ + UNPROTECT_CTX(ctx,flags); +} +#endif /* CONFIG_SMP */ + +#ifdef CONFIG_SMP +/* + * in 2.6, interrupts are masked when we come here and the runqueue lock is held + */ +void +pfm_load_regs (struct task_struct *task) +{ + pfm_context_t *ctx; + struct thread_struct *t; + unsigned long pmc_mask = 0UL, pmd_mask = 0UL; + unsigned long flags; + u64 psr, psr_up; + int need_irq_resend; + + ctx = PFM_GET_CTX(task); + if (unlikely(ctx == NULL)) return; + + BUG_ON(GET_PMU_OWNER()); + + t = &task->thread; + /* + * possible on unload + */ + if (unlikely((t->flags & IA64_THREAD_PM_VALID) == 0)) return; + + /* + * we always come here with interrupts ALREADY disabled by + * the scheduler. So we simply need to protect against concurrent + * access, not CPU concurrency. + */ + flags = pfm_protect_ctx_ctxsw(ctx); + psr = pfm_get_psr(); + + need_irq_resend = pmu_conf->flags & PFM_PMU_IRQ_RESEND; + + BUG_ON(psr & (IA64_PSR_UP|IA64_PSR_PP)); + BUG_ON(psr & IA64_PSR_I); + + if (unlikely(ctx->ctx_state == PFM_CTX_ZOMBIE)) { + struct pt_regs *regs = task_pt_regs(task); + + BUG_ON(ctx->ctx_smpl_hdr); + + pfm_force_cleanup(ctx, regs); + + pfm_unprotect_ctx_ctxsw(ctx, flags); + + /* + * this one (kmalloc'ed) is fine with interrupts disabled + */ + pfm_context_free(ctx); + + return; + } + + /* + * we restore ALL the debug registers to avoid picking up + * stale state. + */ + if (ctx->ctx_fl_using_dbreg) { + pfm_restore_ibrs(ctx->ctx_ibrs, pmu_conf->num_ibrs); + pfm_restore_dbrs(ctx->ctx_dbrs, pmu_conf->num_dbrs); + } + /* + * retrieve saved psr.up + */ + psr_up = ctx->ctx_saved_psr_up; + + /* + * if we were the last user of the PMU on that CPU, + * then nothing to do except restore psr + */ + if (GET_LAST_CPU(ctx) == smp_processor_id() && ctx->ctx_last_activation == GET_ACTIVATION()) { + + /* + * retrieve partial reload masks (due to user modifications) + */ + pmc_mask = ctx->ctx_reload_pmcs[0]; + pmd_mask = ctx->ctx_reload_pmds[0]; + + } else { + /* + * To avoid leaking information to the user level when psr.sp=0, + * we must reload ALL implemented pmds (even the ones we don't use). + * In the kernel we only allow PFM_READ_PMDS on registers which + * we initialized or requested (sampling) so there is no risk there. + */ + pmd_mask = pfm_sysctl.fastctxsw ? ctx->ctx_used_pmds[0] : ctx->ctx_all_pmds[0]; + + /* + * ALL accessible PMCs are systematically reloaded, unused registers + * get their default (from pfm_reset_pmu_state()) values to avoid picking + * up stale configuration. + * + * PMC0 is never in the mask. It is always restored separately. + */ + pmc_mask = ctx->ctx_all_pmcs[0]; + } + /* + * when context is MASKED, we will restore PMC with plm=0 + * and PMD with stale information, but that's ok, nothing + * will be captured. + * + * XXX: optimize here + */ + if (pmd_mask) pfm_restore_pmds(t->pmds, pmd_mask); + if (pmc_mask) pfm_restore_pmcs(t->pmcs, pmc_mask); + + /* + * check for pending overflow at the time the state + * was saved. + */ + if (unlikely(PMC0_HAS_OVFL(t->pmcs[0]))) { + /* + * reload pmc0 with the overflow information + * On McKinley PMU, this will trigger a PMU interrupt + */ + ia64_set_pmc(0, t->pmcs[0]); + ia64_srlz_d(); + t->pmcs[0] = 0UL; + + /* + * will replay the PMU interrupt + */ + if (need_irq_resend) hw_resend_irq(NULL, IA64_PERFMON_VECTOR); + + pfm_stats[smp_processor_id()].pfm_replay_ovfl_intr_count++; + } + + /* + * we just did a reload, so we reset the partial reload fields + */ + ctx->ctx_reload_pmcs[0] = 0UL; + ctx->ctx_reload_pmds[0] = 0UL; + + SET_LAST_CPU(ctx, smp_processor_id()); + + /* + * dump activation value for this PMU + */ + INC_ACTIVATION(); + /* + * record current activation for this context + */ + SET_ACTIVATION(ctx); + + /* + * establish new ownership. + */ + SET_PMU_OWNER(task, ctx); + + /* + * restore the psr.up bit. measurement + * is active again. + * no PMU interrupt can happen at this point + * because we still have interrupts disabled. + */ + if (likely(psr_up)) pfm_set_psr_up(); + + /* + * allow concurrent access to context + */ + pfm_unprotect_ctx_ctxsw(ctx, flags); +} +#else /* !CONFIG_SMP */ +/* + * reload PMU state for UP kernels + * in 2.5 we come here with interrupts disabled + */ +void +pfm_load_regs (struct task_struct *task) +{ + struct thread_struct *t; + pfm_context_t *ctx; + struct task_struct *owner; + unsigned long pmd_mask, pmc_mask; + u64 psr, psr_up; + int need_irq_resend; + + owner = GET_PMU_OWNER(); + ctx = PFM_GET_CTX(task); + t = &task->thread; + psr = pfm_get_psr(); + + BUG_ON(psr & (IA64_PSR_UP|IA64_PSR_PP)); + BUG_ON(psr & IA64_PSR_I); + + /* + * we restore ALL the debug registers to avoid picking up + * stale state. + * + * This must be done even when the task is still the owner + * as the registers may have been modified via ptrace() + * (not perfmon) by the previous task. + */ + if (ctx->ctx_fl_using_dbreg) { + pfm_restore_ibrs(ctx->ctx_ibrs, pmu_conf->num_ibrs); + pfm_restore_dbrs(ctx->ctx_dbrs, pmu_conf->num_dbrs); + } + + /* + * retrieved saved psr.up + */ + psr_up = ctx->ctx_saved_psr_up; + need_irq_resend = pmu_conf->flags & PFM_PMU_IRQ_RESEND; + + /* + * short path, our state is still there, just + * need to restore psr and we go + * + * we do not touch either PMC nor PMD. the psr is not touched + * by the overflow_handler. So we are safe w.r.t. to interrupt + * concurrency even without interrupt masking. + */ + if (likely(owner == task)) { + if (likely(psr_up)) pfm_set_psr_up(); + return; + } + + /* + * someone else is still using the PMU, first push it out and + * then we'll be able to install our stuff ! + * + * Upon return, there will be no owner for the current PMU + */ + if (owner) pfm_lazy_save_regs(owner); + + /* + * To avoid leaking information to the user level when psr.sp=0, + * we must reload ALL implemented pmds (even the ones we don't use). + * In the kernel we only allow PFM_READ_PMDS on registers which + * we initialized or requested (sampling) so there is no risk there. + */ + pmd_mask = pfm_sysctl.fastctxsw ? ctx->ctx_used_pmds[0] : ctx->ctx_all_pmds[0]; + + /* + * ALL accessible PMCs are systematically reloaded, unused registers + * get their default (from pfm_reset_pmu_state()) values to avoid picking + * up stale configuration. + * + * PMC0 is never in the mask. It is always restored separately + */ + pmc_mask = ctx->ctx_all_pmcs[0]; + + pfm_restore_pmds(t->pmds, pmd_mask); + pfm_restore_pmcs(t->pmcs, pmc_mask); + + /* + * check for pending overflow at the time the state + * was saved. + */ + if (unlikely(PMC0_HAS_OVFL(t->pmcs[0]))) { + /* + * reload pmc0 with the overflow information + * On McKinley PMU, this will trigger a PMU interrupt + */ + ia64_set_pmc(0, t->pmcs[0]); + ia64_srlz_d(); + + t->pmcs[0] = 0UL; + + /* + * will replay the PMU interrupt + */ + if (need_irq_resend) hw_resend_irq(NULL, IA64_PERFMON_VECTOR); + + pfm_stats[smp_processor_id()].pfm_replay_ovfl_intr_count++; + } + + /* + * establish new ownership. + */ + SET_PMU_OWNER(task, ctx); + + /* + * restore the psr.up bit. measurement + * is active again. + * no PMU interrupt can happen at this point + * because we still have interrupts disabled. + */ + if (likely(psr_up)) pfm_set_psr_up(); +} +#endif /* CONFIG_SMP */ + +/* + * this function assumes monitoring is stopped + */ +static void +pfm_flush_pmds(struct task_struct *task, pfm_context_t *ctx) +{ + u64 pmc0; + unsigned long mask2, val, pmd_val, ovfl_val; + int i, can_access_pmu = 0; + int is_self; + + /* + * is the caller the task being monitored (or which initiated the + * session for system wide measurements) + */ + is_self = ctx->ctx_task == task ? 1 : 0; + + /* + * can access PMU is task is the owner of the PMU state on the current CPU + * or if we are running on the CPU bound to the context in system-wide mode + * (that is not necessarily the task the context is attached to in this mode). + * In system-wide we always have can_access_pmu true because a task running on an + * invalid processor is flagged earlier in the call stack (see pfm_stop). + */ + can_access_pmu = (GET_PMU_OWNER() == task) || (ctx->ctx_fl_system && ctx->ctx_cpu == smp_processor_id()); + if (can_access_pmu) { + /* + * Mark the PMU as not owned + * This will cause the interrupt handler to do nothing in case an overflow + * interrupt was in-flight + * This also guarantees that pmc0 will contain the final state + * It virtually gives us full control on overflow processing from that point + * on. + */ + SET_PMU_OWNER(NULL, NULL); + DPRINT(("releasing ownership\n")); + + /* + * read current overflow status: + * + * we are guaranteed to read the final stable state + */ + ia64_srlz_d(); + pmc0 = ia64_get_pmc(0); /* slow */ + + /* + * reset freeze bit, overflow status information destroyed + */ + pfm_unfreeze_pmu(); + } else { + pmc0 = task->thread.pmcs[0]; + /* + * clear whatever overflow status bits there were + */ + task->thread.pmcs[0] = 0; + } + ovfl_val = pmu_conf->ovfl_val; + /* + * we save all the used pmds + * we take care of overflows for counting PMDs + * + * XXX: sampling situation is not taken into account here + */ + mask2 = ctx->ctx_used_pmds[0]; + + DPRINT(("is_self=%d ovfl_val=0x%lx mask2=0x%lx\n", is_self, ovfl_val, mask2)); + + for (i = 0; mask2; i++, mask2>>=1) { + + /* skip non used pmds */ + if ((mask2 & 0x1) == 0) continue; + + /* + * can access PMU always true in system wide mode + */ + val = pmd_val = can_access_pmu ? ia64_get_pmd(i) : task->thread.pmds[i]; + + if (PMD_IS_COUNTING(i)) { + DPRINT(("[%d] pmd[%d] ctx_pmd=0x%lx hw_pmd=0x%lx\n", + task->pid, + i, + ctx->ctx_pmds[i].val, + val & ovfl_val)); + + /* + * we rebuild the full 64 bit value of the counter + */ + val = ctx->ctx_pmds[i].val + (val & ovfl_val); + + /* + * now everything is in ctx_pmds[] and we need + * to clear the saved context from save_regs() such that + * pfm_read_pmds() gets the correct value + */ + pmd_val = 0UL; + + /* + * take care of overflow inline + */ + if (pmc0 & (1UL << i)) { + val += 1 + ovfl_val; + DPRINT(("[%d] pmd[%d] overflowed\n", task->pid, i)); + } + } + + DPRINT(("[%d] ctx_pmd[%d]=0x%lx pmd_val=0x%lx\n", task->pid, i, val, pmd_val)); + + if (is_self) task->thread.pmds[i] = pmd_val; + + ctx->ctx_pmds[i].val = val; + } +} + +static struct irqaction perfmon_irqaction = { + .handler = pfm_interrupt_handler, + .flags = SA_INTERRUPT, + .name = "perfmon" +}; + +static void +pfm_alt_save_pmu_state(void *data) +{ + struct pt_regs *regs; + + regs = task_pt_regs(current); + + DPRINT(("called\n")); + + /* + * should not be necessary but + * let's take not risk + */ + pfm_clear_psr_up(); + pfm_clear_psr_pp(); + ia64_psr(regs)->pp = 0; + + /* + * This call is required + * May cause a spurious interrupt on some processors + */ + pfm_freeze_pmu(); + + ia64_srlz_d(); +} + +void +pfm_alt_restore_pmu_state(void *data) +{ + struct pt_regs *regs; + + regs = task_pt_regs(current); + + DPRINT(("called\n")); + + /* + * put PMU back in state expected + * by perfmon + */ + pfm_clear_psr_up(); + pfm_clear_psr_pp(); + ia64_psr(regs)->pp = 0; + + /* + * perfmon runs with PMU unfrozen at all times + */ + pfm_unfreeze_pmu(); + + ia64_srlz_d(); +} + +int +pfm_install_alt_pmu_interrupt(pfm_intr_handler_desc_t *hdl) +{ + int ret, i; + int reserve_cpu; + + /* some sanity checks */ + if (hdl == NULL || hdl->handler == NULL) return -EINVAL; + + /* do the easy test first */ + if (pfm_alt_intr_handler) return -EBUSY; + + /* one at a time in the install or remove, just fail the others */ + if (!spin_trylock(&pfm_alt_install_check)) { + return -EBUSY; + } + + /* reserve our session */ + for_each_online_cpu(reserve_cpu) { + ret = pfm_reserve_session(NULL, 1, reserve_cpu); + if (ret) goto cleanup_reserve; + } + + /* save the current system wide pmu states */ + ret = on_each_cpu(pfm_alt_save_pmu_state, NULL, 0, 1); + if (ret) { + DPRINT(("on_each_cpu() failed: %d\n", ret)); + goto cleanup_reserve; + } + + /* officially change to the alternate interrupt handler */ + pfm_alt_intr_handler = hdl; + + spin_unlock(&pfm_alt_install_check); + + return 0; + +cleanup_reserve: + for_each_online_cpu(i) { + /* don't unreserve more than we reserved */ + if (i >= reserve_cpu) break; + + pfm_unreserve_session(NULL, 1, i); + } + + spin_unlock(&pfm_alt_install_check); + + return ret; +} +EXPORT_SYMBOL_GPL(pfm_install_alt_pmu_interrupt); + +int +pfm_remove_alt_pmu_interrupt(pfm_intr_handler_desc_t *hdl) +{ + int i; + int ret; + + if (hdl == NULL) return -EINVAL; + + /* cannot remove someone else's handler! */ + if (pfm_alt_intr_handler != hdl) return -EINVAL; + + /* one at a time in the install or remove, just fail the others */ + if (!spin_trylock(&pfm_alt_install_check)) { + return -EBUSY; + } + + pfm_alt_intr_handler = NULL; + + ret = on_each_cpu(pfm_alt_restore_pmu_state, NULL, 0, 1); + if (ret) { + DPRINT(("on_each_cpu() failed: %d\n", ret)); + } + + for_each_online_cpu(i) { + pfm_unreserve_session(NULL, 1, i); + } + + spin_unlock(&pfm_alt_install_check); + + return 0; +} +EXPORT_SYMBOL_GPL(pfm_remove_alt_pmu_interrupt); + +/* + * perfmon initialization routine, called from the initcall() table + */ +static int init_pfm_fs(void); + +static int __init +pfm_probe_pmu(void) +{ + pmu_config_t **p; + int family; + + family = local_cpu_data->family; + p = pmu_confs; + + while(*p) { + if ((*p)->probe) { + if ((*p)->probe() == 0) goto found; + } else if ((*p)->pmu_family == family || (*p)->pmu_family == 0xff) { + goto found; + } + p++; + } + return -1; +found: + pmu_conf = *p; + return 0; +} + +static struct file_operations pfm_proc_fops = { + .open = pfm_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +int __init +pfm_init(void) +{ + unsigned int n, n_counters, i; + + printk("perfmon: version %u.%u IRQ %u\n", + PFM_VERSION_MAJ, + PFM_VERSION_MIN, + IA64_PERFMON_VECTOR); + + if (pfm_probe_pmu()) { + printk(KERN_INFO "perfmon: disabled, there is no support for processor family %d\n", + local_cpu_data->family); + return -ENODEV; + } + + /* + * compute the number of implemented PMD/PMC from the + * description tables + */ + n = 0; + for (i=0; PMC_IS_LAST(i) == 0; i++) { + if (PMC_IS_IMPL(i) == 0) continue; + pmu_conf->impl_pmcs[i>>6] |= 1UL << (i&63); + n++; + } + pmu_conf->num_pmcs = n; + + n = 0; n_counters = 0; + for (i=0; PMD_IS_LAST(i) == 0; i++) { + if (PMD_IS_IMPL(i) == 0) continue; + pmu_conf->impl_pmds[i>>6] |= 1UL << (i&63); + n++; + if (PMD_IS_COUNTING(i)) n_counters++; + } + pmu_conf->num_pmds = n; + pmu_conf->num_counters = n_counters; + + /* + * sanity checks on the number of debug registers + */ + if (pmu_conf->use_rr_dbregs) { + if (pmu_conf->num_ibrs > IA64_NUM_DBG_REGS) { + printk(KERN_INFO "perfmon: unsupported number of code debug registers (%u)\n", pmu_conf->num_ibrs); + pmu_conf = NULL; + return -1; + } + if (pmu_conf->num_dbrs > IA64_NUM_DBG_REGS) { + printk(KERN_INFO "perfmon: unsupported number of data debug registers (%u)\n", pmu_conf->num_ibrs); + pmu_conf = NULL; + return -1; + } + } + + printk("perfmon: %s PMU detected, %u PMCs, %u PMDs, %u counters (%lu bits)\n", + pmu_conf->pmu_name, + pmu_conf->num_pmcs, + pmu_conf->num_pmds, + pmu_conf->num_counters, + ffz(pmu_conf->ovfl_val)); + + /* sanity check */ + if (pmu_conf->num_pmds >= IA64_NUM_PMD_REGS || pmu_conf->num_pmcs >= IA64_NUM_PMC_REGS) { + printk(KERN_ERR "perfmon: not enough pmc/pmd, perfmon disabled\n"); + pmu_conf = NULL; + return -1; + } + + /* + * create /proc/perfmon (mostly for debugging purposes) + */ + perfmon_dir = create_proc_entry("perfmon", S_IRUGO, NULL); + if (perfmon_dir == NULL) { + printk(KERN_ERR "perfmon: cannot create /proc entry, perfmon disabled\n"); + pmu_conf = NULL; + return -1; + } + /* + * install customized file operations for /proc/perfmon entry + */ + perfmon_dir->proc_fops = &pfm_proc_fops; + + /* + * create /proc/sys/kernel/perfmon (for debugging purposes) + */ + pfm_sysctl_header = register_sysctl_table(pfm_sysctl_root, 0); + + /* + * initialize all our spinlocks + */ + spin_lock_init(&pfm_sessions.pfs_lock); + spin_lock_init(&pfm_buffer_fmt_lock); + + init_pfm_fs(); + + for(i=0; i < NR_CPUS; i++) pfm_stats[i].pfm_ovfl_intr_cycles_min = ~0UL; + + return 0; +} + +__initcall(pfm_init); + +/* + * this function is called before pfm_init() + */ +void +pfm_init_percpu (void) +{ + /* + * make sure no measurement is active + * (may inherit programmed PMCs from EFI). + */ + pfm_clear_psr_pp(); + pfm_clear_psr_up(); + + /* + * we run with the PMU not frozen at all times + */ + pfm_unfreeze_pmu(); + + if (smp_processor_id() == 0) + register_percpu_irq(IA64_PERFMON_VECTOR, &perfmon_irqaction); + + ia64_setreg(_IA64_REG_CR_PMV, IA64_PERFMON_VECTOR); + ia64_srlz_d(); +} + +/* + * used for debug purposes only + */ +void +dump_pmu_state(const char *from) +{ + struct task_struct *task; + struct thread_struct *t; + struct pt_regs *regs; + pfm_context_t *ctx; + unsigned long psr, dcr, info, flags; + int i, this_cpu; + + local_irq_save(flags); + + this_cpu = smp_processor_id(); + regs = task_pt_regs(current); + info = PFM_CPUINFO_GET(); + dcr = ia64_getreg(_IA64_REG_CR_DCR); + + if (info == 0 && ia64_psr(regs)->pp == 0 && (dcr & IA64_DCR_PP) == 0) { + local_irq_restore(flags); + return; + } + + printk("CPU%d from %s() current [%d] iip=0x%lx %s\n", + this_cpu, + from, + current->pid, + regs->cr_iip, + current->comm); + + task = GET_PMU_OWNER(); + ctx = GET_PMU_CTX(); + + printk("->CPU%d owner [%d] ctx=%p\n", this_cpu, task ? task->pid : -1, ctx); + + psr = pfm_get_psr(); + + printk("->CPU%d pmc0=0x%lx psr.pp=%d psr.up=%d dcr.pp=%d syst_info=0x%lx user_psr.up=%d user_psr.pp=%d\n", + this_cpu, + ia64_get_pmc(0), + psr & IA64_PSR_PP ? 1 : 0, + psr & IA64_PSR_UP ? 1 : 0, + dcr & IA64_DCR_PP ? 1 : 0, + info, + ia64_psr(regs)->up, + ia64_psr(regs)->pp); + + ia64_psr(regs)->up = 0; + ia64_psr(regs)->pp = 0; + + t = ¤t->thread; + + for (i=1; PMC_IS_LAST(i) == 0; i++) { + if (PMC_IS_IMPL(i) == 0) continue; + printk("->CPU%d pmc[%d]=0x%lx thread_pmc[%d]=0x%lx\n", this_cpu, i, ia64_get_pmc(i), i, t->pmcs[i]); + } + + for (i=1; PMD_IS_LAST(i) == 0; i++) { + if (PMD_IS_IMPL(i) == 0) continue; + printk("->CPU%d pmd[%d]=0x%lx thread_pmd[%d]=0x%lx\n", this_cpu, i, ia64_get_pmd(i), i, t->pmds[i]); + } + + if (ctx) { + printk("->CPU%d ctx_state=%d vaddr=%p addr=%p fd=%d ctx_task=[%d] saved_psr_up=0x%lx\n", + this_cpu, + ctx->ctx_state, + ctx->ctx_smpl_vaddr, + ctx->ctx_smpl_hdr, + ctx->ctx_msgq_head, + ctx->ctx_msgq_tail, + ctx->ctx_saved_psr_up); + } + local_irq_restore(flags); +} + +/* + * called from process.c:copy_thread(). task is new child. + */ +void +pfm_inherit(struct task_struct *task, struct pt_regs *regs) +{ + struct thread_struct *thread; + + DPRINT(("perfmon: pfm_inherit clearing state for [%d]\n", task->pid)); + + thread = &task->thread; + + /* + * cut links inherited from parent (current) + */ + thread->pfm_context = NULL; + + PFM_SET_WORK_PENDING(task, 0); + + /* + * the psr bits are already set properly in copy_threads() + */ +} +#else /* !CONFIG_PERFMON */ +asmlinkage long +sys_perfmonctl (int fd, int cmd, void *arg, int count) +{ + return -ENOSYS; +} +#endif /* CONFIG_PERFMON */ diff --git a/linux-2.6-xen-sparse/arch/ia64/kernel/setup.c b/linux-2.6-xen-sparse/arch/ia64/kernel/setup.c index 8f15b3001c..4158332846 100644 --- a/linux-2.6-xen-sparse/arch/ia64/kernel/setup.c +++ b/linux-2.6-xen-sparse/arch/ia64/kernel/setup.c @@ -550,14 +550,16 @@ setup_arch (char **cmdline_p) xen_start_info->nr_pages, xen_start_info->flags); if (!is_initial_xendomain()) { - extern int console_use_vt; +#if !defined(CONFIG_VT) || !defined(CONFIG_DUMMY_CONSOLE) conswitchp = NULL; - console_use_vt = 0; +#endif } } + xencons_early_setup(); #endif #endif + /* enable IA-64 Machine Check Abort Handling unless disabled */ if (!strstr(saved_command_line, "nomca")) ia64_mca_init(); diff --git a/linux-2.6-xen-sparse/arch/ia64/oprofile/Makefile b/linux-2.6-xen-sparse/arch/ia64/oprofile/Makefile new file mode 100644 index 0000000000..555d4a9d7a --- /dev/null +++ b/linux-2.6-xen-sparse/arch/ia64/oprofile/Makefile @@ -0,0 +1,14 @@ +obj-$(CONFIG_OPROFILE) += oprofile.o + +DRIVER_OBJS := $(addprefix ../../../drivers/oprofile/, \ + oprof.o cpu_buffer.o buffer_sync.o \ + event_buffer.o oprofile_files.o \ + oprofilefs.o oprofile_stats.o \ + timer_int.o ) + +oprofile-y := $(DRIVER_OBJS) init.o backtrace.o +oprofile-$(CONFIG_PERFMON) += perfmon.o +ifeq ($(CONFIG_XEN), y) +oprofile-$(CONFIG_PERFMON) += xenoprof.o \ + ../../../drivers/xen/xenoprof/xenoprofile.o +endif diff --git a/linux-2.6-xen-sparse/arch/ia64/oprofile/init.c b/linux-2.6-xen-sparse/arch/ia64/oprofile/init.c new file mode 100644 index 0000000000..f218b7eb45 --- /dev/null +++ b/linux-2.6-xen-sparse/arch/ia64/oprofile/init.c @@ -0,0 +1,52 @@ +/** + * @file init.c + * + * @remark Copyright 2002 OProfile authors + * @remark Read the file COPYING + * + * @author John Levon + */ + +#include +#include +#include +#include +#include "oprofile_perfmon.h" + +extern int perfmon_init(struct oprofile_operations * ops); +extern void perfmon_exit(void); +extern void ia64_backtrace(struct pt_regs * const regs, unsigned int depth); + +int __init oprofile_arch_init(struct oprofile_operations * ops) +{ + int ret = -ENODEV; + + if (is_running_on_xen()) { + ret = xen_perfmon_init(); + if (ret) + return ret; + return xenoprofile_init(ops); + } + +#ifdef CONFIG_PERFMON + /* perfmon_init() can fail, but we have no way to report it */ + ret = perfmon_init(ops); +#endif + ops->backtrace = ia64_backtrace; + + return ret; +} + + +void oprofile_arch_exit(void) +{ + if (is_running_on_xen()) { + xenoprofile_exit(); + xen_perfmon_exit(); + return; + } + +#ifdef CONFIG_PERFMON + perfmon_exit(); +#endif +} diff --git a/linux-2.6-xen-sparse/arch/ia64/oprofile/oprofile_perfmon.h b/linux-2.6-xen-sparse/arch/ia64/oprofile/oprofile_perfmon.h new file mode 100644 index 0000000000..14f1677aea --- /dev/null +++ b/linux-2.6-xen-sparse/arch/ia64/oprofile/oprofile_perfmon.h @@ -0,0 +1,30 @@ +#ifndef OPROFILE_PERFMON_H +#define OPROFILE_PERFMON_H + +#include + +#ifdef CONFIG_PERFMON +int __perfmon_init(void); +void __perfmon_exit(void); +int perfmon_start(void); +void perfmon_stop(void); +#else +#define __perfmon_init() (-ENOSYS) +#define __perfmon_exit() do {} while (0) +#endif /* CONFIG_PERFMON */ + +#ifdef CONFIG_XEN +#define STATIC_IF_NO_XEN /* nothing */ +#define xen_perfmon_init() __perfmon_init() +#define xen_perfmon_exit() __perfmon_exit() +extern int xenoprofile_init(struct oprofile_operations * ops); +extern void xenoprofile_exit(void); +#else +#define STATIC_IF_NO_XEN static +#define xen_perfmon_init() (-ENOSYS) +#define xen_perfmon_exit() do {} while (0) +#define xenoprofile_init() (-ENOSYS) +#define xenoprofile_exit() do {} while (0) +#endif /* CONFIG_XEN */ + +#endif /* OPROFILE_PERFMON_H */ diff --git a/linux-2.6-xen-sparse/arch/ia64/oprofile/perfmon.c b/linux-2.6-xen-sparse/arch/ia64/oprofile/perfmon.c new file mode 100644 index 0000000000..cd2a002994 --- /dev/null +++ b/linux-2.6-xen-sparse/arch/ia64/oprofile/perfmon.c @@ -0,0 +1,119 @@ +/** + * @file perfmon.c + * + * @remark Copyright 2003 OProfile authors + * @remark Read the file COPYING + * + * @author John Levon + */ + +#include +#include +#include +#include +#include +#include +#include +#include "oprofile_perfmon.h" + +static int allow_ints; + +static int +perfmon_handler(struct task_struct *task, void *buf, pfm_ovfl_arg_t *arg, + struct pt_regs *regs, unsigned long stamp) +{ + int event = arg->pmd_eventid; + + arg->ovfl_ctrl.bits.reset_ovfl_pmds = 1; + + /* the owner of the oprofile event buffer may have exited + * without perfmon being shutdown (e.g. SIGSEGV) + */ + if (allow_ints) + oprofile_add_sample(regs, event); + return 0; +} + + +STATIC_IF_NO_XEN +int perfmon_start(void) +{ + allow_ints = 1; + return 0; +} + + +STATIC_IF_NO_XEN +void perfmon_stop(void) +{ + allow_ints = 0; +} + + +#define OPROFILE_FMT_UUID { \ + 0x77, 0x7a, 0x6e, 0x61, 0x20, 0x65, 0x73, 0x69, 0x74, 0x6e, 0x72, 0x20, 0x61, 0x65, 0x0a, 0x6c } + +static pfm_buffer_fmt_t oprofile_fmt = { + .fmt_name = "oprofile_format", + .fmt_uuid = OPROFILE_FMT_UUID, + .fmt_handler = perfmon_handler, +}; + + +static char * get_cpu_type(void) +{ + __u8 family = local_cpu_data->family; + + switch (family) { + case 0x07: + return "ia64/itanium"; + case 0x1f: + return "ia64/itanium2"; + default: + return "ia64/ia64"; + } +} + + +/* all the ops are handled via userspace for IA64 perfmon */ + +static int using_perfmon; + +STATIC_IF_NO_XEN +int __perfmon_init(void) +{ + int ret = pfm_register_buffer_fmt(&oprofile_fmt); + if (ret) + return -ENODEV; + + using_perfmon = 1; + return 0; +} + +STATIC_IF_NO_XEN +void __perfmon_exit(void) +{ + if (!using_perfmon) + return; + + pfm_unregister_buffer_fmt(oprofile_fmt.fmt_uuid); +} + +int perfmon_init(struct oprofile_operations * ops) +{ + int ret = __perfmon_init(); + if (ret) + return -ENODEV; + + ops->cpu_type = get_cpu_type(); + ops->start = perfmon_start; + ops->stop = perfmon_stop; + printk(KERN_INFO "oprofile: using perfmon.\n"); + return 0; +} + + +void perfmon_exit(void) +{ + __perfmon_exit(); +} diff --git a/linux-2.6-xen-sparse/arch/ia64/oprofile/xenoprof.c b/linux-2.6-xen-sparse/arch/ia64/oprofile/xenoprof.c new file mode 100644 index 0000000000..998be3e66b --- /dev/null +++ b/linux-2.6-xen-sparse/arch/ia64/oprofile/xenoprof.c @@ -0,0 +1,142 @@ +/****************************************************************************** + * xenoprof ia64 specific part + * + * Copyright (c) 2006 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include +#include +#include + +#include +#include +#include +#include + +#include "oprofile_perfmon.h" + +void __init xenoprof_arch_init_counter(struct xenoprof_init *init) +{ + init->num_events = 0; /* perfmon manages. */ +} + +void xenoprof_arch_counter(void) +{ + /* nothing. perfmon does. */ +} + +void xenoprof_arch_start(void) +{ + perfmon_start(); +} + +void xenoprof_arch_stop(void) +{ + perfmon_stop(); +} + +/* XXX move them to an appropriate header file. */ +struct resource* xen_ia64_allocate_resource(unsigned long size); +void xen_ia64_release_resource(struct resource* res); +void xen_ia64_unmap_resource(struct resource* res); + +struct resource* +xenoprof_ia64_allocate_resource(int32_t max_samples) +{ + unsigned long bufsize; + + /* XXX add hypercall to get bufsize? */ + /* this value is taken from alloc_xenoprof_struct(). */ +#if 0 + bufsize = NR_CPUS * (sizeof(struct xenoprof_buf) + + (max_samples - 1) * sizeof(struct event_log)); + bufsize = PAGE_ALIGN(bufsize) + PAGE_SIZE; +#else +#define MAX_OPROF_SHARED_PAGES 32 + bufsize = (MAX_OPROF_SHARED_PAGES + 1) * PAGE_SIZE; +#endif + return xen_ia64_allocate_resource(bufsize); +} + +void xenoprof_arch_unmap_shared_buffer(struct xenoprof_shared_buffer* sbuf) +{ + if (sbuf->buffer) { + xen_ia64_unmap_resource(sbuf->arch.res); + sbuf->buffer = NULL; + sbuf->arch.res = NULL; + } +} + +int xenoprof_arch_map_shared_buffer(struct xenoprof_get_buffer* get_buffer, + struct xenoprof_shared_buffer* sbuf) +{ + int ret; + struct resource* res; + + sbuf->buffer = NULL; + sbuf->arch.res = NULL; + + res = xenoprof_ia64_allocate_resource(get_buffer->max_samples); + if (IS_ERR(res)) + return PTR_ERR(res); + + get_buffer->buf_gmaddr = res->start; + + ret = HYPERVISOR_xenoprof_op(XENOPROF_get_buffer, get_buffer); + if (ret) { + xen_ia64_release_resource(res); + return ret; + } + + BUG_ON((res->end - res->start + 1) < + get_buffer->bufsize * get_buffer->nbuf); + + sbuf->buffer = __va(res->start); + sbuf->arch.res = res; + + return ret; +} + +int xenoprof_arch_set_passive(struct xenoprof_passive* pdomain, + struct xenoprof_shared_buffer* sbuf) +{ + int ret; + struct resource* res; + + sbuf->buffer = NULL; + sbuf->arch.res = NULL; + + res = xenoprof_ia64_allocate_resource(pdomain->max_samples); + if (IS_ERR(res)) + return PTR_ERR(res); + + pdomain->buf_gmaddr = res->start; + + ret = HYPERVISOR_xenoprof_op(XENOPROF_set_passive, pdomain); + if (ret) { + xen_ia64_release_resource(res); + return ret; + } + + BUG_ON((res->end - res->start + 1) < pdomain->bufsize * pdomain->nbuf); + + sbuf->buffer = __va(res->start); + sbuf->arch.res = res; + + return ret; +} diff --git a/linux-2.6-xen-sparse/arch/ia64/xen/Makefile b/linux-2.6-xen-sparse/arch/ia64/xen/Makefile index 36434aac72..6bd31af099 100644 --- a/linux-2.6-xen-sparse/arch/ia64/xen/Makefile +++ b/linux-2.6-xen-sparse/arch/ia64/xen/Makefile @@ -4,6 +4,6 @@ obj-y := hypercall.o xenivt.o xenentry.o xensetup.o xenpal.o xenhpski.o \ hypervisor.o pci-dma-xen.o util.o xencomm.o xcom_hcall.o \ - xcom_mini.o xcom_privcmd.o + xcom_mini.o xcom_privcmd.o mem.o pci-dma-xen-y := ../../i386/kernel/pci-dma-xen.o diff --git a/linux-2.6-xen-sparse/arch/ia64/xen/hypervisor.c b/linux-2.6-xen-sparse/arch/ia64/xen/hypervisor.c index 2a85caa0d5..9bd507a6bf 100644 --- a/linux-2.6-xen-sparse/arch/ia64/xen/hypervisor.c +++ b/linux-2.6-xen-sparse/arch/ia64/xen/hypervisor.c @@ -497,7 +497,7 @@ xen_ia64_privcmd_entry_mmap(struct vm_area_struct* vma, unsigned long addr, struct xen_ia64_privcmd_range* privcmd_range, int i, - unsigned long mfn, + unsigned long gmfn, pgprot_t prot, domid_t domid) { @@ -506,7 +506,7 @@ xen_ia64_privcmd_entry_mmap(struct vm_area_struct* vma, unsigned long gpfn; unsigned long flags; - if ((addr & ~PAGE_MASK) != 0 || mfn == INVALID_MFN) { + if ((addr & ~PAGE_MASK) != 0 || gmfn == INVALID_MFN) { error = -EINVAL; goto out; } @@ -521,7 +521,7 @@ xen_ia64_privcmd_entry_mmap(struct vm_area_struct* vma, if (pgprot_val(prot) == PROT_READ) { flags = ASSIGN_readonly; } - error = HYPERVISOR_add_physmap(gpfn, mfn, flags, domid); + error = HYPERVISOR_add_physmap_with_gmfn(gpfn, gmfn, flags, domid); if (error != 0) { goto out; } @@ -732,7 +732,7 @@ out_enomem0: int direct_remap_pfn_range(struct vm_area_struct *vma, unsigned long address, // process virtual address - unsigned long mfn, // mfn, mfn + 1, ... mfn + size/PAGE_SIZE + unsigned long gmfn, // gmfn, gmfn + 1, ... gmfn + size/PAGE_SIZE unsigned long size, pgprot_t prot, domid_t domid) // target domain @@ -755,13 +755,13 @@ direct_remap_pfn_range(struct vm_area_struct *vma, i = (address - vma->vm_start) >> PAGE_SHIFT; for (offset = 0; offset < size; offset += PAGE_SIZE) { - error = xen_ia64_privcmd_entry_mmap(vma, (address + offset) & PAGE_MASK, privcmd_range, entry_offset + i, mfn, prot, domid); + error = xen_ia64_privcmd_entry_mmap(vma, (address + offset) & PAGE_MASK, privcmd_range, entry_offset + i, gmfn, prot, domid); if (error != 0) { break; } i++; - mfn++; + gmfn++; } return error; @@ -1050,3 +1050,55 @@ EXPORT_SYMBOL_GPL(p2m_convert_max_pfn); EXPORT_SYMBOL_GPL(p2m_pte); EXPORT_SYMBOL_GPL(p2m_phystomach); #endif + +/////////////////////////////////////////////////////////////////////////// +// for xenoprof + +struct resource* +xen_ia64_allocate_resource(unsigned long size) +{ + struct resource* res; + int error; + + res = kmalloc(sizeof(*res), GFP_KERNEL); + if (res == NULL) + return ERR_PTR(-ENOMEM); + + res->name = "Xen"; + res->flags = IORESOURCE_MEM; + error = allocate_resource(&iomem_resource, res, PAGE_ALIGN(size), + privcmd_resource_min, privcmd_resource_max, + IA64_GRANULE_SIZE, NULL, NULL); + if (error) { + kfree(res); + return ERR_PTR(error); + } + return res; +} +EXPORT_SYMBOL_GPL(xen_ia64_allocate_resource); + +void +xen_ia64_release_resource(struct resource* res) +{ + release_resource(res); + kfree(res); +} +EXPORT_SYMBOL_GPL(xen_ia64_release_resource); + +void +xen_ia64_unmap_resource(struct resource* res) +{ + unsigned long gpfn = res->start >> PAGE_SHIFT; + unsigned long nr_pages = (res->end - res->start) >> PAGE_SHIFT; + unsigned long i; + + for (i = 0; i < nr_pages; i++) { + int error = HYPERVISOR_zap_physmap(gpfn + i, 0); + if (error) + printk(KERN_ERR + "%s:%d zap_phsymap failed %d gpfn %lx\n", + __func__, __LINE__, error, gpfn + i); + } + xen_ia64_release_resource(res); +} +EXPORT_SYMBOL_GPL(xen_ia64_unmap_resource); diff --git a/linux-2.6-xen-sparse/arch/ia64/xen/mem.c b/linux-2.6-xen-sparse/arch/ia64/xen/mem.c new file mode 100644 index 0000000000..1c995b6f11 --- /dev/null +++ b/linux-2.6-xen-sparse/arch/ia64/xen/mem.c @@ -0,0 +1,76 @@ +/* + * Originally from linux/drivers/char/mem.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * Added devfs support. + * Jan-11-1998, C. Scott Ananian + * Shared /dev/zero mmaping support, Feb 2000, Kanoj Sarcar + */ +/* + * taken from + * linux/drivers/char/mem.c and linux-2.6-xen-sparse/drivers/xen/char/mem.c. + * adjusted for IA64 and made transparent. + * Copyright (c) 2006 Isaku Yamahata + * VA Linux Systems Japan K.K. + */ + +#include +#include +#include + +/* + * Architectures vary in how they handle caching for addresses + * outside of main memory. + * + */ +static inline int uncached_access(struct file *file, unsigned long addr) +{ + /* + * On ia64, we ignore O_SYNC because we cannot tolerate memory attribute aliases. + */ + return !(efi_mem_attributes(addr) & EFI_MEMORY_WB); +} + +int xen_mmap_mem(struct file * file, struct vm_area_struct * vma) +{ + unsigned long addr = vma->vm_pgoff << PAGE_SHIFT; + size_t size = vma->vm_end - vma->vm_start; + + +#if 0 + /* + *XXX FIXME: linux-2.6.16.29, linux-2.6.17 + * valid_mmap_phys_addr_range() in linux/arch/ia64/kernel/efi.c + * fails checks. + * linux-2.6.18.1's returns always 1. + * Its comments says + * + * MMIO regions are often missing from the EFI memory map. + * We must allow mmap of them for programs like X, so we + * currently can't do any useful validation. + */ + if (!valid_mmap_phys_addr_range(addr, &size)) + return -EINVAL; + if (size < vma->vm_end - vma->vm_start) + return -EINVAL; +#endif + + if (is_running_on_xen()) { + unsigned long offset = HYPERVISOR_ioremap(addr, size); + if (IS_ERR_VALUE(offset)) + return offset; + } + + if (uncached_access(file, vma->vm_pgoff << PAGE_SHIFT)) + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + /* Remap-pfn-range will mark the range VM_IO and VM_RESERVED */ + if (remap_pfn_range(vma, + vma->vm_start, + vma->vm_pgoff, + size, + vma->vm_page_prot)) + return -EAGAIN; + return 0; +} diff --git a/linux-2.6-xen-sparse/arch/ia64/xen/xcom_hcall.c b/linux-2.6-xen-sparse/arch/ia64/xen/xcom_hcall.c index fda0a45ec5..49f9ea6708 100644 --- a/linux-2.6-xen-sparse/arch/ia64/xen/xcom_hcall.c +++ b/linux-2.6-xen-sparse/arch/ia64/xen/xcom_hcall.c @@ -32,10 +32,12 @@ #include #include #include +#include #include #include #include #include +#include /* Xencomm notes: * This file defines hypercalls to be used by xencomm. The hypercalls simply @@ -301,3 +303,63 @@ xencomm_hypercall_suspend(unsigned long srec) return xencomm_arch_hypercall_suspend(xencomm_create_inline(&arg)); } + +int +xencomm_hypercall_xenoprof_op(int op, void *arg) +{ + switch (op) { + case XENOPROF_init: + case XENOPROF_set_active: + case XENOPROF_set_passive: + case XENOPROF_counter: + case XENOPROF_get_buffer: + break; + + case XENOPROF_reset_active_list: + case XENOPROF_reset_passive_list: + case XENOPROF_reserve_counters: + case XENOPROF_setup_events: + case XENOPROF_enable_virq: + case XENOPROF_start: + case XENOPROF_stop: + case XENOPROF_disable_virq: + case XENOPROF_release_counters: + case XENOPROF_shutdown: + return xencomm_arch_hypercall_xenoprof_op(op, arg); + break; + + default: + printk("%s: op %d isn't supported\n", __func__, op); + return -ENOSYS; + } + return xencomm_arch_hypercall_xenoprof_op(op, + xencomm_create_inline(arg)); +} + +int +xencomm_hypercall_perfmon_op(unsigned long cmd, void* arg, unsigned long count) +{ + switch (cmd) { + case PFM_GET_FEATURES: + case PFM_CREATE_CONTEXT: + case PFM_WRITE_PMCS: + case PFM_WRITE_PMDS: + case PFM_LOAD_CONTEXT: + break; + + case PFM_DESTROY_CONTEXT: + case PFM_UNLOAD_CONTEXT: + case PFM_START: + case PFM_STOP: + return xencomm_arch_hypercall_perfmon_op(cmd, arg, count); + + default: + printk("%s:%d cmd %ld isn't supported\n", + __func__,__LINE__, cmd); + BUG(); + } + + return xencomm_arch_hypercall_perfmon_op(cmd, + xencomm_create_inline(arg), + count); +} diff --git a/linux-2.6-xen-sparse/arch/ia64/xen/xcom_mini.c b/linux-2.6-xen-sparse/arch/ia64/xen/xcom_mini.c index 5adec0c325..a2f2c97ac4 100644 --- a/linux-2.6-xen-sparse/arch/ia64/xen/xcom_mini.c +++ b/linux-2.6-xen-sparse/arch/ia64/xen/xcom_mini.c @@ -28,12 +28,14 @@ #include #include #include +#include #ifdef CONFIG_VMX_GUEST #include #else #include #endif #include +#include int xencomm_mini_hypercall_event_channel_op(int cmd, void *op) @@ -93,6 +95,9 @@ xencommize_mini_grant_table_op(struct xencomm_mini *xc_area, int *nbr_area, case GNTTABOP_transfer: argsize = sizeof(struct gnttab_transfer); break; + case GNTTABOP_copy: + argsize = sizeof(struct gnttab_copy); + break; default: printk("%s: unknown mini grant table op %d\n", __func__, cmd); BUG(); @@ -317,3 +322,96 @@ xencomm_mini_hypercall_xen_version(int cmd, void *arg) return xencomm_arch_hypercall_xen_version(cmd, desc); } EXPORT_SYMBOL(xencomm_mini_hypercall_xen_version); + +int +xencomm_mini_hypercall_xenoprof_op(int op, void *arg) +{ + unsigned int argsize; + struct xencomm_mini xc_area[2]; + int nbr_area = 2; + struct xencomm_handle *desc; + int rc; + + switch (op) { + case XENOPROF_init: + argsize = sizeof(xenoprof_init_t); + break; + case XENOPROF_set_active: + argsize = sizeof(domid_t); + break; + case XENOPROF_set_passive: + argsize = sizeof(xenoprof_passive_t); + break; + case XENOPROF_counter: + argsize = sizeof(xenoprof_counter_t); + break; + case XENOPROF_get_buffer: + argsize = sizeof(xenoprof_get_buffer_t); + break; + + case XENOPROF_reset_active_list: + case XENOPROF_reset_passive_list: + case XENOPROF_reserve_counters: + case XENOPROF_setup_events: + case XENOPROF_enable_virq: + case XENOPROF_start: + case XENOPROF_stop: + case XENOPROF_disable_virq: + case XENOPROF_release_counters: + case XENOPROF_shutdown: + return xencomm_arch_hypercall_xenoprof_op(op, arg); + + default: + printk("%s: op %d isn't supported\n", __func__, op); + return -ENOSYS; + } + rc = xencomm_create_mini(xc_area, &nbr_area, arg, argsize, &desc); + if (rc) + return rc; + return xencomm_arch_hypercall_xenoprof_op(op, desc); +} +EXPORT_SYMBOL_GPL(xencomm_mini_hypercall_xenoprof_op); + +int +xencomm_mini_hypercall_perfmon_op(unsigned long cmd, void* arg, + unsigned long count) +{ + unsigned int argsize; + struct xencomm_mini xc_area[2]; + int nbr_area = 2; + struct xencomm_handle *desc; + int rc; + + switch (cmd) { + case PFM_GET_FEATURES: + argsize = sizeof(pfarg_features_t); + break; + case PFM_CREATE_CONTEXT: + argsize = sizeof(pfarg_context_t); + break; + case PFM_LOAD_CONTEXT: + argsize = sizeof(pfarg_load_t); + break; + case PFM_WRITE_PMCS: + case PFM_WRITE_PMDS: + argsize = sizeof(pfarg_reg_t) * count; + break; + + case PFM_DESTROY_CONTEXT: + case PFM_UNLOAD_CONTEXT: + case PFM_START: + case PFM_STOP: + return xencomm_arch_hypercall_perfmon_op(cmd, arg, count); + + default: + printk("%s:%d cmd %ld isn't supported\n", + __func__, __LINE__, cmd); + BUG(); + } + + rc = xencomm_create_mini(xc_area, &nbr_area, arg, argsize, &desc); + if (rc) + return rc; + return xencomm_arch_hypercall_perfmon_op(cmd, desc, count); +} +EXPORT_SYMBOL_GPL(xencomm_mini_hypercall_perfmon_op); diff --git a/linux-2.6-xen-sparse/arch/ia64/xen/xcom_privcmd.c b/linux-2.6-xen-sparse/arch/ia64/xen/xcom_privcmd.c index 51cbb9f4c0..0f5fccdac7 100644 --- a/linux-2.6-xen-sparse/arch/ia64/xen/xcom_privcmd.c +++ b/linux-2.6-xen-sparse/arch/ia64/xen/xcom_privcmd.c @@ -579,9 +579,16 @@ xencomm_privcmd_hvm_op(privcmd_hypercall_t *hypercall) case HVMOP_set_param: argsize = sizeof(xen_hvm_param_t); break; - case HVMOP_set_irq_level: - argsize = sizeof(xen_hvm_set_irq_level_t); + case HVMOP_set_pci_intx_level: + argsize = sizeof(xen_hvm_set_pci_intx_level_t); break; + case HVMOP_set_isa_irq_level: + argsize = sizeof(xen_hvm_set_isa_irq_level_t); + break; + case HVMOP_set_pci_link_route: + argsize = sizeof(xen_hvm_set_pci_link_route_t); + break; + default: printk("%s: unknown HVMOP %d\n", __func__, cmd); return -EINVAL; diff --git a/linux-2.6-xen-sparse/arch/ia64/xen/xenentry.S b/linux-2.6-xen-sparse/arch/ia64/xen/xenentry.S index bd66913f4f..aad0aac631 100644 --- a/linux-2.6-xen-sparse/arch/ia64/xen/xenentry.S +++ b/linux-2.6-xen-sparse/arch/ia64/xen/xenentry.S @@ -237,6 +237,47 @@ END(xen_trace_syscall) END(ia64_trace_syscall) #endif +#ifdef CONFIG_XEN +GLOBAL_ENTRY(xen_ret_from_clone) + PT_REGS_UNWIND_INFO(0) + movl r16=running_on_xen;; + ld4 r16=[r16];; + cmp.eq p7,p0=r16,r0 +(p7) br.cond.sptk.many __ia64_ret_from_clone;; +#else +GLOBAL_ENTRY(ia64_ret_from_clone) + PT_REGS_UNWIND_INFO(0) +#endif +{ /* + * Some versions of gas generate bad unwind info if the first instruction of a + * procedure doesn't go into the first slot of a bundle. This is a workaround. + */ + nop.m 0 + nop.i 0 + /* + * We need to call schedule_tail() to complete the scheduling process. + * Called by ia64_switch_to() after do_fork()->copy_thread(). r8 contains the + * address of the previously executing task. + */ + br.call.sptk.many rp=ia64_invoke_schedule_tail +} +.ret8: + adds r2=TI_FLAGS+IA64_TASK_SIZE,r13 + ;; + ld4 r2=[r2] + ;; + mov r8=0 + and r2=_TIF_SYSCALL_TRACEAUDIT,r2 + ;; + cmp.ne p6,p0=r2,r0 +(p6) br.cond.spnt .strace_check_retval + ;; // added stop bits to prevent r8 dependency +#ifdef CONFIG_XEN + br.cond.sptk ia64_ret_from_syscall +END(xen_ret_from_clone) +#else +END(ia64_ret_from_clone) +#endif /* * ia64_leave_syscall(): Same as ia64_leave_kernel, except that it doesn't * need to switch to bank 0 and doesn't restore the scratch registers. diff --git a/linux-2.6-xen-sparse/arch/x86_64/Kconfig b/linux-2.6-xen-sparse/arch/x86_64/Kconfig index 79bd0044fa..5fcf581612 100644 --- a/linux-2.6-xen-sparse/arch/x86_64/Kconfig +++ b/linux-2.6-xen-sparse/arch/x86_64/Kconfig @@ -435,7 +435,7 @@ config X86_MCE_AMD config KEXEC bool "kexec system call (EXPERIMENTAL)" - depends on EXPERIMENTAL && !X86_64_XEN + depends on EXPERIMENTAL && !XEN_UNPRIVILEGED_GUEST help kexec is a system call that implements the ability to shutdown your current kernel, and to start another kernel. It is like a reboot diff --git a/linux-2.6-xen-sparse/arch/x86_64/kernel/crash.c b/linux-2.6-xen-sparse/arch/x86_64/kernel/crash.c new file mode 100644 index 0000000000..3263b5942a --- /dev/null +++ b/linux-2.6-xen-sparse/arch/x86_64/kernel/crash.c @@ -0,0 +1,190 @@ +/* + * Architecture specific (x86_64) functions for kexec based crash dumps. + * + * Created by: Hariprasad Nellitheertha (hari@in.ibm.com) + * + * Copyright (C) IBM Corporation, 2004. All rights reserved. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* This keeps a track of which one is crashing cpu. */ +static int crashing_cpu; + +static u32 *append_elf_note(u32 *buf, char *name, unsigned type, + void *data, size_t data_len) +{ + struct elf_note note; + + note.n_namesz = strlen(name) + 1; + note.n_descsz = data_len; + note.n_type = type; + memcpy(buf, ¬e, sizeof(note)); + buf += (sizeof(note) +3)/4; + memcpy(buf, name, note.n_namesz); + buf += (note.n_namesz + 3)/4; + memcpy(buf, data, note.n_descsz); + buf += (note.n_descsz + 3)/4; + + return buf; +} + +static void final_note(u32 *buf) +{ + struct elf_note note; + + note.n_namesz = 0; + note.n_descsz = 0; + note.n_type = 0; + memcpy(buf, ¬e, sizeof(note)); +} + +static void crash_save_this_cpu(struct pt_regs *regs, int cpu) +{ + struct elf_prstatus prstatus; + u32 *buf; + + if ((cpu < 0) || (cpu >= NR_CPUS)) + return; + + /* Using ELF notes here is opportunistic. + * I need a well defined structure format + * for the data I pass, and I need tags + * on the data to indicate what information I have + * squirrelled away. ELF notes happen to provide + * all of that that no need to invent something new. + */ + + buf = (u32*)per_cpu_ptr(crash_notes, cpu); + + if (!buf) + return; + + memset(&prstatus, 0, sizeof(prstatus)); + prstatus.pr_pid = current->pid; + elf_core_copy_regs(&prstatus.pr_reg, regs); + buf = append_elf_note(buf, "CORE", NT_PRSTATUS, &prstatus, + sizeof(prstatus)); + final_note(buf); +} + +static void crash_save_self(struct pt_regs *regs) +{ + int cpu; + + cpu = smp_processor_id(); + crash_save_this_cpu(regs, cpu); +} + +#ifndef CONFIG_XEN +#ifdef CONFIG_SMP +static atomic_t waiting_for_crash_ipi; + +static int crash_nmi_callback(struct pt_regs *regs, int cpu) +{ + /* + * Don't do anything if this handler is invoked on crashing cpu. + * Otherwise, system will completely hang. Crashing cpu can get + * an NMI if system was initially booted with nmi_watchdog parameter. + */ + if (cpu == crashing_cpu) + return 1; + local_irq_disable(); + + crash_save_this_cpu(regs, cpu); + disable_local_APIC(); + atomic_dec(&waiting_for_crash_ipi); + /* Assume hlt works */ + for(;;) + asm("hlt"); + + return 1; +} + +static void smp_send_nmi_allbutself(void) +{ + send_IPI_allbutself(APIC_DM_NMI); +} + +/* + * This code is a best effort heuristic to get the + * other cpus to stop executing. So races with + * cpu hotplug shouldn't matter. + */ + +static void nmi_shootdown_cpus(void) +{ + unsigned long msecs; + + atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1); + set_nmi_callback(crash_nmi_callback); + + /* + * Ensure the new callback function is set before sending + * out the NMI + */ + wmb(); + + smp_send_nmi_allbutself(); + + msecs = 1000; /* Wait at most a second for the other cpus to stop */ + while ((atomic_read(&waiting_for_crash_ipi) > 0) && msecs) { + mdelay(1); + msecs--; + } + /* Leave the nmi callback set */ + disable_local_APIC(); +} +#else +static void nmi_shootdown_cpus(void) +{ + /* There are no cpus to shootdown */ +} +#endif +#endif /* CONFIG_XEN */ + +void machine_crash_shutdown(struct pt_regs *regs) +{ + /* + * This function is only called after the system + * has paniced or is otherwise in a critical state. + * The minimum amount of code to allow a kexec'd kernel + * to run successfully needs to happen here. + * + * In practice this means shooting down the other cpus in + * an SMP system. + */ + /* The kernel is broken so disable interrupts */ + local_irq_disable(); + + /* Make a note of crashing cpu. Will be used in NMI callback.*/ + crashing_cpu = smp_processor_id(); + +#ifndef CONFIG_XEN + nmi_shootdown_cpus(); + + if(cpu_has_apic) + disable_local_APIC(); + +#if defined(CONFIG_X86_IO_APIC) + disable_IO_APIC(); +#endif +#endif /* CONFIG_XEN */ + crash_save_self(regs); +} diff --git a/linux-2.6-xen-sparse/arch/x86_64/kernel/e820-xen.c b/linux-2.6-xen-sparse/arch/x86_64/kernel/e820-xen.c index 333a9a26f4..37427e6285 100644 --- a/linux-2.6-xen-sparse/arch/x86_64/kernel/e820-xen.c +++ b/linux-2.6-xen-sparse/arch/x86_64/kernel/e820-xen.c @@ -260,7 +260,11 @@ void __init e820_reserve_resources(struct e820entry *e820, int nr_map) request_resource(res, &data_resource); #endif #ifdef CONFIG_KEXEC - request_resource(res, &crashk_res); + if (crashk_res.start != crashk_res.end) + request_resource(res, &crashk_res); +#ifdef CONFIG_XEN + xen_machine_kexec_register_resources(res); +#endif #endif } } diff --git a/linux-2.6-xen-sparse/arch/x86_64/kernel/machine_kexec.c b/linux-2.6-xen-sparse/arch/x86_64/kernel/machine_kexec.c new file mode 100644 index 0000000000..f1d9583880 --- /dev/null +++ b/linux-2.6-xen-sparse/arch/x86_64/kernel/machine_kexec.c @@ -0,0 +1,279 @@ +/* + * machine_kexec.c - handle transition of Linux booting another kernel + * Copyright (C) 2002-2005 Eric Biederman + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define PAGE_ALIGNED __attribute__ ((__aligned__(PAGE_SIZE))) +static u64 kexec_pgd[512] PAGE_ALIGNED; +static u64 kexec_pud0[512] PAGE_ALIGNED; +static u64 kexec_pmd0[512] PAGE_ALIGNED; +static u64 kexec_pte0[512] PAGE_ALIGNED; +static u64 kexec_pud1[512] PAGE_ALIGNED; +static u64 kexec_pmd1[512] PAGE_ALIGNED; +static u64 kexec_pte1[512] PAGE_ALIGNED; + +#ifdef CONFIG_XEN + +/* In the case of Xen, override hypervisor functions to be able to create + * a regular identity mapping page table... + */ + +#include +#include + +#define x__pmd(x) ((pmd_t) { (x) } ) +#define x__pud(x) ((pud_t) { (x) } ) +#define x__pgd(x) ((pgd_t) { (x) } ) + +#define x_pmd_val(x) ((x).pmd) +#define x_pud_val(x) ((x).pud) +#define x_pgd_val(x) ((x).pgd) + +static inline void x_set_pmd(pmd_t *dst, pmd_t val) +{ + x_pmd_val(*dst) = x_pmd_val(val); +} + +static inline void x_set_pud(pud_t *dst, pud_t val) +{ + x_pud_val(*dst) = phys_to_machine(x_pud_val(val)); +} + +static inline void x_pud_clear (pud_t *pud) +{ + x_pud_val(*pud) = 0; +} + +static inline void x_set_pgd(pgd_t *dst, pgd_t val) +{ + x_pgd_val(*dst) = phys_to_machine(x_pgd_val(val)); +} + +static inline void x_pgd_clear (pgd_t * pgd) +{ + x_pgd_val(*pgd) = 0; +} + +#define X__PAGE_KERNEL_LARGE_EXEC \ + _PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_PSE +#define X_KERNPG_TABLE _PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY + +#define __ma(x) (pfn_to_mfn(__pa((x)) >> PAGE_SHIFT) << PAGE_SHIFT) + +#if PAGES_NR > KEXEC_XEN_NO_PAGES +#error PAGES_NR is greater than KEXEC_XEN_NO_PAGES - Xen support will break +#endif + +#if PA_CONTROL_PAGE != 0 +#error PA_CONTROL_PAGE is non zero - Xen support will break +#endif + +void machine_kexec_setup_load_arg(xen_kexec_image_t *xki, struct kimage *image) +{ + void *control_page; + void *table_page; + + memset(xki->page_list, 0, sizeof(xki->page_list)); + + control_page = page_address(image->control_code_page) + PAGE_SIZE; + memcpy(control_page, relocate_kernel, PAGE_SIZE); + + table_page = page_address(image->control_code_page); + + xki->page_list[PA_CONTROL_PAGE] = __ma(control_page); + xki->page_list[PA_TABLE_PAGE] = __ma(table_page); + + xki->page_list[PA_PGD] = __ma(kexec_pgd); + xki->page_list[PA_PUD_0] = __ma(kexec_pud0); + xki->page_list[PA_PUD_1] = __ma(kexec_pud1); + xki->page_list[PA_PMD_0] = __ma(kexec_pmd0); + xki->page_list[PA_PMD_1] = __ma(kexec_pmd1); + xki->page_list[PA_PTE_0] = __ma(kexec_pte0); + xki->page_list[PA_PTE_1] = __ma(kexec_pte1); +} + +#else /* CONFIG_XEN */ + +#define x__pmd(x) __pmd(x) +#define x__pud(x) __pud(x) +#define x__pgd(x) __pgd(x) + +#define x_set_pmd(x, y) set_pmd(x, y) +#define x_set_pud(x, y) set_pud(x, y) +#define x_set_pgd(x, y) set_pgd(x, y) + +#define x_pud_clear(x) pud_clear(x) +#define x_pgd_clear(x) pgd_clear(x) + +#define X__PAGE_KERNEL_LARGE_EXEC __PAGE_KERNEL_LARGE_EXEC +#define X_KERNPG_TABLE _KERNPG_TABLE + +#endif /* CONFIG_XEN */ + +static void init_level2_page(pmd_t *level2p, unsigned long addr) +{ + unsigned long end_addr; + + addr &= PAGE_MASK; + end_addr = addr + PUD_SIZE; + while (addr < end_addr) { + x_set_pmd(level2p++, x__pmd(addr | X__PAGE_KERNEL_LARGE_EXEC)); + addr += PMD_SIZE; + } +} + +static int init_level3_page(struct kimage *image, pud_t *level3p, + unsigned long addr, unsigned long last_addr) +{ + unsigned long end_addr; + int result; + + result = 0; + addr &= PAGE_MASK; + end_addr = addr + PGDIR_SIZE; + while ((addr < last_addr) && (addr < end_addr)) { + struct page *page; + pmd_t *level2p; + + page = kimage_alloc_control_pages(image, 0); + if (!page) { + result = -ENOMEM; + goto out; + } + level2p = (pmd_t *)page_address(page); + init_level2_page(level2p, addr); + x_set_pud(level3p++, x__pud(__pa(level2p) | X_KERNPG_TABLE)); + addr += PUD_SIZE; + } + /* clear the unused entries */ + while (addr < end_addr) { + x_pud_clear(level3p++); + addr += PUD_SIZE; + } +out: + return result; +} + + +static int init_level4_page(struct kimage *image, pgd_t *level4p, + unsigned long addr, unsigned long last_addr) +{ + unsigned long end_addr; + int result; + + result = 0; + addr &= PAGE_MASK; + end_addr = addr + (PTRS_PER_PGD * PGDIR_SIZE); + while ((addr < last_addr) && (addr < end_addr)) { + struct page *page; + pud_t *level3p; + + page = kimage_alloc_control_pages(image, 0); + if (!page) { + result = -ENOMEM; + goto out; + } + level3p = (pud_t *)page_address(page); + result = init_level3_page(image, level3p, addr, last_addr); + if (result) { + goto out; + } + x_set_pgd(level4p++, x__pgd(__pa(level3p) | X_KERNPG_TABLE)); + addr += PGDIR_SIZE; + } + /* clear the unused entries */ + while (addr < end_addr) { + x_pgd_clear(level4p++); + addr += PGDIR_SIZE; + } +out: + return result; +} + + +static int init_pgtable(struct kimage *image, unsigned long start_pgtable) +{ + pgd_t *level4p; + unsigned long x_end_pfn = end_pfn; + +#ifdef CONFIG_XEN + x_end_pfn = HYPERVISOR_memory_op(XENMEM_maximum_ram_page, NULL); +#endif + + level4p = (pgd_t *)__va(start_pgtable); + return init_level4_page(image, level4p, 0, x_end_pfn << PAGE_SHIFT); +} + +int machine_kexec_prepare(struct kimage *image) +{ + unsigned long start_pgtable; + int result; + + /* Calculate the offsets */ + start_pgtable = page_to_pfn(image->control_code_page) << PAGE_SHIFT; + + /* Setup the identity mapped 64bit page table */ + result = init_pgtable(image, start_pgtable); + if (result) + return result; + + return 0; +} + +void machine_kexec_cleanup(struct kimage *image) +{ + return; +} + +#ifndef CONFIG_XEN +/* + * Do not allocate memory (or fail in any way) in machine_kexec(). + * We are past the point of no return, committed to rebooting now. + */ +NORET_TYPE void machine_kexec(struct kimage *image) +{ + unsigned long page_list[PAGES_NR]; + void *control_page; + + /* Interrupts aren't acceptable while we reboot */ + local_irq_disable(); + + control_page = page_address(image->control_code_page) + PAGE_SIZE; + memcpy(control_page, relocate_kernel, PAGE_SIZE); + + page_list[PA_CONTROL_PAGE] = __pa(control_page); + page_list[VA_CONTROL_PAGE] = (unsigned long)relocate_kernel; + page_list[PA_PGD] = __pa(kexec_pgd); + page_list[VA_PGD] = (unsigned long)kexec_pgd; + page_list[PA_PUD_0] = __pa(kexec_pud0); + page_list[VA_PUD_0] = (unsigned long)kexec_pud0; + page_list[PA_PMD_0] = __pa(kexec_pmd0); + page_list[VA_PMD_0] = (unsigned long)kexec_pmd0; + page_list[PA_PTE_0] = __pa(kexec_pte0); + page_list[VA_PTE_0] = (unsigned long)kexec_pte0; + page_list[PA_PUD_1] = __pa(kexec_pud1); + page_list[VA_PUD_1] = (unsigned long)kexec_pud1; + page_list[PA_PMD_1] = __pa(kexec_pmd1); + page_list[VA_PMD_1] = (unsigned long)kexec_pmd1; + page_list[PA_PTE_1] = __pa(kexec_pte1); + page_list[VA_PTE_1] = (unsigned long)kexec_pte1; + + page_list[PA_TABLE_PAGE] = + (unsigned long)__pa(page_address(image->control_code_page)); + + relocate_kernel((unsigned long)image->head, (unsigned long)page_list, + image->start); +} +#endif diff --git a/linux-2.6-xen-sparse/arch/x86_64/kernel/setup-xen.c b/linux-2.6-xen-sparse/arch/x86_64/kernel/setup-xen.c index 711ce5d198..43e53c88e4 100644 --- a/linux-2.6-xen-sparse/arch/x86_64/kernel/setup-xen.c +++ b/linux-2.6-xen-sparse/arch/x86_64/kernel/setup-xen.c @@ -80,6 +80,10 @@ #include #include +#ifdef CONFIG_XEN +#include +#endif + extern unsigned long start_pfn; extern struct edid_info edid_info; @@ -450,6 +454,7 @@ static __init void parse_cmdline_early (char ** cmdline_p) * after a kernel panic. */ else if (!memcmp(from, "crashkernel=", 12)) { +#ifndef CONFIG_XEN unsigned long size, base; size = memparse(from+12, &from); if (*from == '@') { @@ -460,6 +465,10 @@ static __init void parse_cmdline_early (char ** cmdline_p) crashk_res.start = base; crashk_res.end = base + size - 1; } +#else + printk("Ignoring crashkernel command line, " + "parameter will be supplied by xen\n"); +#endif } #endif @@ -812,10 +821,14 @@ void __init setup_arch(char **cmdline_p) #endif #endif /* !CONFIG_XEN */ #ifdef CONFIG_KEXEC +#ifdef CONFIG_XEN + xen_machine_kexec_setup_resources(); +#else if (crashk_res.start != crashk_res.end) { reserve_bootmem(crashk_res.start, crashk_res.end - crashk_res.start + 1); } +#endif #endif paging_init(); @@ -970,10 +983,12 @@ void __init setup_arch(char **cmdline_p) #endif #endif } else { - extern int console_use_vt; - console_use_vt = 0; - } +#if defined(CONFIG_VT) && defined(CONFIG_DUMMY_CONSOLE) + conswitchp = &dummy_con; +#endif + } } + xencons_early_setup(); #else /* CONFIG_XEN */ #ifdef CONFIG_VT diff --git a/linux-2.6-xen-sparse/drivers/xen/Kconfig b/linux-2.6-xen-sparse/drivers/xen/Kconfig index 647dce8386..907d4b5757 100644 --- a/linux-2.6-xen-sparse/drivers/xen/Kconfig +++ b/linux-2.6-xen-sparse/drivers/xen/Kconfig @@ -172,6 +172,29 @@ 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_FRAMEBUFFER + tristate "Framebuffer-device frontend driver" + depends on XEN && FB + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + default y + help + The framebuffer-device frontend drivers allows the kernel to create a + virtual framebuffer. This framebuffer can be viewed in another + domain. Unless this domain has access to a real video card, you + probably want to say Y here. + +config XEN_KEYBOARD + tristate "Keyboard-device frontend driver" + depends on XEN && XEN_FRAMEBUFFER && INPUT + default y + help + The keyboard-device frontend driver allows the kernel to create a + virtual keyboard. This keyboard can then be driven by another + domain. If you've said Y to CONFIG_XEN_FRAMEBUFFER, you probably + want to say Y here. + config XEN_SCRUB_PAGES bool "Scrub memory before freeing it to Xen" default y diff --git a/linux-2.6-xen-sparse/drivers/xen/Makefile b/linux-2.6-xen-sparse/drivers/xen/Makefile index 79eddeec16..cfae4625c4 100644 --- a/linux-2.6-xen-sparse/drivers/xen/Makefile +++ b/linux-2.6-xen-sparse/drivers/xen/Makefile @@ -15,3 +15,5 @@ obj-$(CONFIG_XEN_BLKDEV_FRONTEND) += blkfront/ obj-$(CONFIG_XEN_NETDEV_FRONTEND) += netfront/ obj-$(CONFIG_XEN_PCIDEV_BACKEND) += pciback/ obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += pcifront/ +obj-$(CONFIG_XEN_FRAMEBUFFER) += fbfront/ +obj-$(CONFIG_XEN_KEYBOARD) += fbfront/ diff --git a/linux-2.6-xen-sparse/drivers/xen/blkfront/blkfront.c b/linux-2.6-xen-sparse/drivers/xen/blkfront/blkfront.c index 95cff46ff9..6b7100a1bc 100644 --- a/linux-2.6-xen-sparse/drivers/xen/blkfront/blkfront.c +++ b/linux-2.6-xen-sparse/drivers/xen/blkfront/blkfront.c @@ -359,7 +359,7 @@ static void blkfront_closing(struct xenbus_device *dev) DPRINTK("blkfront_closing: %s removed\n", dev->nodename); if (info->rq == NULL) - return; + goto out; spin_lock_irqsave(&blkif_io_lock, flags); /* No more blkif_request(). */ @@ -373,6 +373,7 @@ static void blkfront_closing(struct xenbus_device *dev) xlvbd_del(info); + out: xenbus_frontend_closed(dev); } diff --git a/linux-2.6-xen-sparse/drivers/xen/char/mem.c b/linux-2.6-xen-sparse/drivers/xen/char/mem.c index ac85c8dbb2..135bad413e 100644 --- a/linux-2.6-xen-sparse/drivers/xen/char/mem.c +++ b/linux-2.6-xen-sparse/drivers/xen/char/mem.c @@ -147,7 +147,7 @@ static inline int uncached_access(struct file *file) return 0; } -static int mmap_mem(struct file * file, struct vm_area_struct * vma) +static int xen_mmap_mem(struct file * file, struct vm_area_struct * vma) { size_t size = vma->vm_end - vma->vm_start; @@ -200,6 +200,6 @@ struct file_operations mem_fops = { .llseek = memory_lseek, .read = read_mem, .write = write_mem, - .mmap = mmap_mem, + .mmap = xen_mmap_mem, .open = open_mem, }; diff --git a/linux-2.6-xen-sparse/drivers/xen/console/console.c b/linux-2.6-xen-sparse/drivers/xen/console/console.c index ff3e2a411e..3b48932e0a 100644 --- a/linux-2.6-xen-sparse/drivers/xen/console/console.c +++ b/linux-2.6-xen-sparse/drivers/xen/console/console.c @@ -57,6 +57,7 @@ #include #include #include +#include #include /* @@ -64,46 +65,64 @@ * 'xencons=off' [XC_OFF]: Console is disabled. * 'xencons=tty' [XC_TTY]: Console attached to '/dev/tty[0-9]+'. * 'xencons=ttyS' [XC_SERIAL]: Console attached to '/dev/ttyS[0-9]+'. - * [XC_DEFAULT]: DOM0 -> XC_SERIAL ; all others -> XC_TTY. + * 'xencons=xvc' [XC_XVC]: Console attached to '/dev/xvc0'. + * default: DOM0 -> XC_SERIAL ; all others -> XC_TTY. * * NB. In mode XC_TTY, we create dummy consoles for tty2-63. This suppresses * warnings from standard distro startup scripts. */ -static enum { XC_OFF, XC_DEFAULT, XC_TTY, XC_SERIAL } xc_mode = XC_DEFAULT; +static enum { + XC_OFF, XC_TTY, XC_SERIAL, XC_XVC +} xc_mode; static int xc_num = -1; +/* /dev/xvc0 device number allocated by lanana.org. */ +#define XEN_XVC_MAJOR 204 +#define XEN_XVC_MINOR 191 + #ifdef CONFIG_MAGIC_SYSRQ static unsigned long sysrq_requested; extern int sysrq_enabled; #endif +void xencons_early_setup(void) +{ + extern int console_use_vt; + + if (is_initial_xendomain()) { + xc_mode = XC_SERIAL; + } else { + xc_mode = XC_TTY; + console_use_vt = 0; + } +} + static int __init xencons_setup(char *str) { char *q; int n; + extern int console_use_vt; - if (!strncmp(str, "ttyS", 4)) + console_use_vt = 1; + if (!strncmp(str, "ttyS", 4)) { xc_mode = XC_SERIAL; - else if (!strncmp(str, "tty", 3)) + str += 4; + } else if (!strncmp(str, "tty", 3)) { xc_mode = XC_TTY; - else if (!strncmp(str, "off", 3)) + str += 3; + console_use_vt = 0; + } else if (!strncmp(str, "xvc", 3)) { + xc_mode = XC_XVC; + str += 3; + } else if (!strncmp(str, "off", 3)) { xc_mode = XC_OFF; - - switch (xc_mode) { - case XC_SERIAL: - n = simple_strtol(str+4, &q, 10); - if (q > (str + 4)) - xc_num = n; - break; - case XC_TTY: - n = simple_strtol(str+3, &q, 10); - if (q > (str + 3)) - xc_num = n; - break; - default: - break; + str += 3; } + n = simple_strtol(str, &q, 10); + if (q != str) + xc_num = n; + return 1; } __setup("xencons=", xencons_setup); @@ -189,18 +208,20 @@ static int __init xen_console_init(void) goto out; if (is_initial_xendomain()) { - if (xc_mode == XC_DEFAULT) - xc_mode = XC_SERIAL; kcons_info.write = kcons_write_dom0; } else { if (!xen_start_info->console.domU.evtchn) goto out; - if (xc_mode == XC_DEFAULT) - xc_mode = XC_TTY; kcons_info.write = kcons_write; } switch (xc_mode) { + case XC_XVC: + strcpy(kcons_info.name, "xvc"); + if (xc_num == -1) + xc_num = 0; + break; + case XC_SERIAL: strcpy(kcons_info.name, "ttyS"); if (xc_num == -1) @@ -305,7 +326,7 @@ void dom0_init_screen_info(const struct dom0_vga_console_info *info) /******************** User-space console driver (/dev/console) ************/ #define DRV(_d) (_d) -#define DUMMY_TTY(_tty) ((xc_mode != XC_SERIAL) && \ +#define DUMMY_TTY(_tty) ((xc_mode == XC_TTY) && \ ((_tty)->index != (xc_num - 1))) static struct termios *xencons_termios[MAX_NR_CONSOLES]; @@ -628,8 +649,8 @@ static int __init xencons_init(void) return rc; } - xencons_driver = alloc_tty_driver((xc_mode == XC_SERIAL) ? - 1 : MAX_NR_CONSOLES); + xencons_driver = alloc_tty_driver((xc_mode == XC_TTY) ? + MAX_NR_CONSOLES : 1); if (xencons_driver == NULL) return -ENOMEM; @@ -644,14 +665,23 @@ static int __init xencons_init(void) DRV(xencons_driver)->termios = xencons_termios; DRV(xencons_driver)->termios_locked = xencons_termios_locked; - if (xc_mode == XC_SERIAL) { + switch (xc_mode) { + case XC_XVC: + DRV(xencons_driver)->name = "xvc"; + DRV(xencons_driver)->major = XEN_XVC_MAJOR; + DRV(xencons_driver)->minor_start = XEN_XVC_MINOR; + DRV(xencons_driver)->name_base = xc_num; + break; + case XC_SERIAL: DRV(xencons_driver)->name = "ttyS"; DRV(xencons_driver)->minor_start = 64 + xc_num; - DRV(xencons_driver)->name_base = 0 + xc_num; - } else { + DRV(xencons_driver)->name_base = xc_num; + break; + default: DRV(xencons_driver)->name = "tty"; DRV(xencons_driver)->minor_start = 1; DRV(xencons_driver)->name_base = 1; + break; } tty_set_operations(xencons_driver, &xencons_ops); diff --git a/linux-2.6-xen-sparse/drivers/xen/core/Makefile b/linux-2.6-xen-sparse/drivers/xen/core/Makefile index 6154454339..519ea37351 100644 --- a/linux-2.6-xen-sparse/drivers/xen/core/Makefile +++ b/linux-2.6-xen-sparse/drivers/xen/core/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_XEN_SYSFS) += xen_sysfs.o obj-$(CONFIG_XEN_SKBUFF) += skbuff.o obj-$(CONFIG_XEN_REBOOT) += reboot.o machine_reboot.o obj-$(CONFIG_XEN_SMPBOOT) += smpboot.o +obj-$(CONFIG_KEXEC) += machine_kexec.o diff --git a/linux-2.6-xen-sparse/drivers/xen/core/evtchn.c b/linux-2.6-xen-sparse/drivers/xen/core/evtchn.c index b455a3b10a..445cacf785 100644 --- a/linux-2.6-xen-sparse/drivers/xen/core/evtchn.c +++ b/linux-2.6-xen-sparse/drivers/xen/core/evtchn.c @@ -244,17 +244,22 @@ asmlinkage void evtchn_do_upcall(struct pt_regs *regs) static int find_unbound_irq(void) { - int irq; + static int warned; + int dynirq, irq; - /* Only allocate from dynirq range */ - for (irq = DYNIRQ_BASE; irq < NR_IRQS; irq++) + for (dynirq = 0; dynirq < NR_DYNIRQS; dynirq++) { + irq = dynirq_to_irq(dynirq); if (irq_bindcount[irq] == 0) - break; + return irq; + } - if (irq == NR_IRQS) - panic("No available IRQ to bind to: increase NR_IRQS!\n"); + if (!warned) { + warned = 1; + printk(KERN_WARNING "No available IRQ to bind to: " + "increase NR_DYNIRQS.\n"); + } - return irq; + return -ENOSPC; } static int bind_evtchn_to_irq(unsigned int evtchn) @@ -264,15 +269,17 @@ static int bind_evtchn_to_irq(unsigned int evtchn) spin_lock(&irq_mapping_update_lock); if ((irq = evtchn_to_irq[evtchn]) == -1) { - irq = find_unbound_irq(); + if ((irq = find_unbound_irq()) < 0) + goto out; + evtchn_to_irq[evtchn] = irq; irq_info[irq] = mk_irq_info(IRQT_EVTCHN, 0, evtchn); } irq_bindcount[irq]++; + out: spin_unlock(&irq_mapping_update_lock); - return irq; } @@ -284,6 +291,9 @@ static int bind_virq_to_irq(unsigned int virq, unsigned int cpu) spin_lock(&irq_mapping_update_lock); if ((irq = per_cpu(virq_to_irq, cpu)[virq]) == -1) { + if ((irq = find_unbound_irq()) < 0) + goto out; + bind_virq.virq = virq; bind_virq.vcpu = cpu; if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq, @@ -291,7 +301,6 @@ static int bind_virq_to_irq(unsigned int virq, unsigned int cpu) BUG(); evtchn = bind_virq.port; - irq = find_unbound_irq(); evtchn_to_irq[evtchn] = irq; irq_info[irq] = mk_irq_info(IRQT_VIRQ, virq, evtchn); @@ -302,8 +311,8 @@ static int bind_virq_to_irq(unsigned int virq, unsigned int cpu) irq_bindcount[irq]++; + out: spin_unlock(&irq_mapping_update_lock); - return irq; } @@ -315,13 +324,15 @@ static int bind_ipi_to_irq(unsigned int ipi, unsigned int cpu) spin_lock(&irq_mapping_update_lock); if ((irq = per_cpu(ipi_to_irq, cpu)[ipi]) == -1) { + if ((irq = find_unbound_irq()) < 0) + goto out; + bind_ipi.vcpu = cpu; if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_ipi, &bind_ipi) != 0) BUG(); evtchn = bind_ipi.port; - irq = find_unbound_irq(); evtchn_to_irq[evtchn] = irq; irq_info[irq] = mk_irq_info(IRQT_IPI, ipi, evtchn); @@ -332,8 +343,8 @@ static int bind_ipi_to_irq(unsigned int ipi, unsigned int cpu) irq_bindcount[irq]++; + out: spin_unlock(&irq_mapping_update_lock); - return irq; } @@ -383,6 +394,9 @@ int bind_evtchn_to_irqhandler( int retval; irq = bind_evtchn_to_irq(evtchn); + if (irq < 0) + return irq; + retval = request_irq(irq, handler, irqflags, devname, dev_id); if (retval != 0) { unbind_from_irq(irq); @@ -405,6 +419,9 @@ int bind_virq_to_irqhandler( int retval; irq = bind_virq_to_irq(virq, cpu); + if (irq < 0) + return irq; + retval = request_irq(irq, handler, irqflags, devname, dev_id); if (retval != 0) { unbind_from_irq(irq); @@ -427,6 +444,9 @@ int bind_ipi_to_irqhandler( int retval; irq = bind_ipi_to_irq(ipi, cpu); + if (irq < 0) + return irq; + retval = request_irq(irq, handler, irqflags, devname, dev_id); if (retval != 0) { unbind_from_irq(irq); diff --git a/linux-2.6-xen-sparse/drivers/xen/core/machine_kexec.c b/linux-2.6-xen-sparse/drivers/xen/core/machine_kexec.c new file mode 100644 index 0000000000..a16e9d9a22 --- /dev/null +++ b/linux-2.6-xen-sparse/drivers/xen/core/machine_kexec.c @@ -0,0 +1,190 @@ +/* + * drivers/xen/core/machine_kexec.c + * handle transition of Linux booting another kernel + */ + +#include +#include +#include +#include +#include + +extern void machine_kexec_setup_load_arg(xen_kexec_image_t *xki, + struct kimage *image); + +int xen_max_nr_phys_cpus; +struct resource xen_hypervisor_res; +struct resource *xen_phys_cpus; + +void xen_machine_kexec_setup_resources(void) +{ + xen_kexec_range_t range; + struct resource *res; + int k = 0; + + if (!is_initial_xendomain()) + return; + + /* determine maximum number of physical cpus */ + + while (1) { + memset(&range, 0, sizeof(range)); + range.range = KEXEC_RANGE_MA_CPU; + range.nr = k; + + if(HYPERVISOR_kexec_op(KEXEC_CMD_kexec_get_range, &range)) + break; + + k++; + } + + if (k == 0) + return; + + xen_max_nr_phys_cpus = k; + + /* allocate xen_phys_cpus */ + + xen_phys_cpus = alloc_bootmem_low(k * sizeof(struct resource)); + BUG_ON(xen_phys_cpus == NULL); + + /* fill in xen_phys_cpus with per-cpu crash note information */ + + for (k = 0; k < xen_max_nr_phys_cpus; k++) { + memset(&range, 0, sizeof(range)); + range.range = KEXEC_RANGE_MA_CPU; + range.nr = k; + + if (HYPERVISOR_kexec_op(KEXEC_CMD_kexec_get_range, &range)) + goto err; + + res = xen_phys_cpus + k; + + memset(res, 0, sizeof(*res)); + res->name = "Crash note"; + res->start = range.start; + res->end = range.start + range.size - 1; + res->flags = IORESOURCE_BUSY | IORESOURCE_MEM; + } + + /* fill in xen_hypervisor_res with hypervisor machine address range */ + + memset(&range, 0, sizeof(range)); + range.range = KEXEC_RANGE_MA_XEN; + + if (HYPERVISOR_kexec_op(KEXEC_CMD_kexec_get_range, &range)) + goto err; + + xen_hypervisor_res.name = "Hypervisor code and data"; + xen_hypervisor_res.start = range.start; + xen_hypervisor_res.end = range.start + range.size - 1; + xen_hypervisor_res.flags = IORESOURCE_BUSY | IORESOURCE_MEM; + + /* fill in crashk_res if range is reserved by hypervisor */ + + memset(&range, 0, sizeof(range)); + range.range = KEXEC_RANGE_MA_CRASH; + + if (HYPERVISOR_kexec_op(KEXEC_CMD_kexec_get_range, &range)) + return; + + if (range.size) { + crashk_res.start = range.start; + crashk_res.end = range.start + range.size - 1; + } + + return; + + err: + /* + * It isn't possible to free xen_phys_cpus this early in the + * boot. Since failure at this stage is unexpected and the + * amount is small we leak the memory. + */ + xen_max_nr_phys_cpus = 0; + return; +} + +void xen_machine_kexec_register_resources(struct resource *res) +{ + int k; + + request_resource(res, &xen_hypervisor_res); + + for (k = 0; k < xen_max_nr_phys_cpus; k++) + request_resource(&xen_hypervisor_res, xen_phys_cpus + k); + +} + +static void setup_load_arg(xen_kexec_image_t *xki, struct kimage *image) +{ + machine_kexec_setup_load_arg(xki, image); + + xki->indirection_page = image->head; + xki->start_address = image->start; +} + +/* + * Load the image into xen so xen can kdump itself + * This might have been done in prepare, but prepare + * is currently called too early. It might make sense + * to move prepare, but for now, just add an extra hook. + */ +int xen_machine_kexec_load(struct kimage *image) +{ + xen_kexec_load_t xkl; + + memset(&xkl, 0, sizeof(xkl)); + xkl.type = image->type; + setup_load_arg(&xkl.image, image); + return HYPERVISOR_kexec_op(KEXEC_CMD_kexec_load, &xkl); +} + +/* + * Unload the image that was stored by machine_kexec_load() + * This might have been done in machine_kexec_cleanup() but it + * is called too late, and its possible xen could try and kdump + * using resources that have been freed. + */ +void xen_machine_kexec_unload(struct kimage *image) +{ + xen_kexec_load_t xkl; + + memset(&xkl, 0, sizeof(xkl)); + xkl.type = image->type; + HYPERVISOR_kexec_op(KEXEC_CMD_kexec_unload, &xkl); +} + +/* + * Do not allocate memory (or fail in any way) in machine_kexec(). + * We are past the point of no return, committed to rebooting now. + * + * This has the hypervisor move to the prefered reboot CPU, + * stop all CPUs and kexec. That is it combines machine_shutdown() + * and machine_kexec() in Linux kexec terms. + */ +NORET_TYPE void machine_kexec(struct kimage *image) +{ + xen_kexec_exec_t xke; + + memset(&xke, 0, sizeof(xke)); + xke.type = image->type; + HYPERVISOR_kexec_op(KEXEC_CMD_kexec, &xke); + panic("KEXEC_CMD_kexec hypercall should not return\n"); +} + +void machine_shutdown(void) +{ + /* do nothing */ +} + + +/* + * Local variables: + * c-file-style: "linux" + * indent-tabs-mode: t + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ diff --git a/linux-2.6-xen-sparse/drivers/xen/core/smpboot.c b/linux-2.6-xen-sparse/drivers/xen/core/smpboot.c index 2538791d0e..36d5a884d4 100644 --- a/linux-2.6-xen-sparse/drivers/xen/core/smpboot.c +++ b/linux-2.6-xen-sparse/drivers/xen/core/smpboot.c @@ -33,7 +33,7 @@ extern irqreturn_t smp_reschedule_interrupt(int, void *, struct pt_regs *); extern irqreturn_t smp_call_function_interrupt(int, void *, struct pt_regs *); -extern void local_setup_timer(unsigned int cpu); +extern int local_setup_timer(unsigned int cpu); extern void local_teardown_timer(unsigned int cpu); extern void hypervisor_callback(void); @@ -110,32 +110,45 @@ set_cpu_sibling_map(int cpu) cpu_data[cpu].booted_cores = 1; } -static void xen_smp_intr_init(unsigned int cpu) +static int xen_smp_intr_init(unsigned int cpu) { + int rc; + + per_cpu(resched_irq, cpu) = per_cpu(callfunc_irq, cpu) = -1; + sprintf(resched_name[cpu], "resched%d", cpu); - per_cpu(resched_irq, cpu) = - bind_ipi_to_irqhandler( - RESCHEDULE_VECTOR, - cpu, - smp_reschedule_interrupt, - SA_INTERRUPT, - resched_name[cpu], - NULL); - BUG_ON(per_cpu(resched_irq, cpu) < 0); + rc = bind_ipi_to_irqhandler(RESCHEDULE_VECTOR, + cpu, + smp_reschedule_interrupt, + SA_INTERRUPT, + resched_name[cpu], + NULL); + if (rc < 0) + goto fail; + per_cpu(resched_irq, cpu) = rc; sprintf(callfunc_name[cpu], "callfunc%d", cpu); - per_cpu(callfunc_irq, cpu) = - bind_ipi_to_irqhandler( - CALL_FUNCTION_VECTOR, - cpu, - smp_call_function_interrupt, - SA_INTERRUPT, - callfunc_name[cpu], - NULL); - BUG_ON(per_cpu(callfunc_irq, cpu) < 0); + rc = bind_ipi_to_irqhandler(CALL_FUNCTION_VECTOR, + cpu, + smp_call_function_interrupt, + SA_INTERRUPT, + callfunc_name[cpu], + NULL); + if (rc < 0) + goto fail; + per_cpu(callfunc_irq, cpu) = rc; - if (cpu != 0) - local_setup_timer(cpu); + if ((cpu != 0) && ((rc = local_setup_timer(cpu)) != 0)) + goto fail; + + return 0; + + fail: + if (per_cpu(resched_irq, cpu) >= 0) + unbind_from_irqhandler(per_cpu(resched_irq, cpu), NULL); + if (per_cpu(callfunc_irq, cpu) >= 0) + unbind_from_irqhandler(per_cpu(callfunc_irq, cpu), NULL); + return rc; } #ifdef CONFIG_HOTPLUG_CPU @@ -253,7 +266,8 @@ void __init smp_prepare_cpus(unsigned int max_cpus) set_cpu_sibling_map(0); - xen_smp_intr_init(0); + if (xen_smp_intr_init(0)) + BUG(); /* Restrict the possible_map according to max_cpus. */ while ((num_possible_cpus() > 1) && (num_possible_cpus() > max_cpus)) { @@ -419,7 +433,13 @@ int __devinit __cpu_up(unsigned int cpu) set_cpu_sibling_map(cpu); wmb(); - xen_smp_intr_init(cpu); + + rc = xen_smp_intr_init(cpu); + if (rc) { + remove_siblinginfo(cpu); + return rc; + } + cpu_set(cpu, cpu_online_map); rc = HYPERVISOR_vcpu_op(VCPUOP_up, cpu, NULL); diff --git a/linux-2.6-xen-sparse/drivers/xen/fbfront/Makefile b/linux-2.6-xen-sparse/drivers/xen/fbfront/Makefile new file mode 100644 index 0000000000..e2b890942f --- /dev/null +++ b/linux-2.6-xen-sparse/drivers/xen/fbfront/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_XEN_FRAMEBUFFER) := xenfb.o +obj-$(CONFIG_XEN_KEYBOARD) += xenkbd.o diff --git a/linux-2.6-xen-sparse/drivers/xen/fbfront/xenfb.c b/linux-2.6-xen-sparse/drivers/xen/fbfront/xenfb.c new file mode 100644 index 0000000000..9c8bfbe9a2 --- /dev/null +++ b/linux-2.6-xen-sparse/drivers/xen/fbfront/xenfb.c @@ -0,0 +1,682 @@ +/* + * linux/drivers/video/xenfb.c -- Xen para-virtual frame buffer device + * + * Copyright (C) 2005-2006 Anthony Liguori + * Copyright (C) 2006 Red Hat, Inc., Markus Armbruster + * + * Based on linux/drivers/video/q40fb.c + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +/* + * TODO: + * + * Switch to grant tables when they become capable of dealing with the + * frame buffer. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct xenfb_mapping +{ + struct list_head link; + struct vm_area_struct *vma; + atomic_t map_refs; + int faults; + struct xenfb_info *info; +}; + +struct xenfb_info +{ + struct task_struct *kthread; + wait_queue_head_t wq; + + unsigned char *fb; + struct fb_info *fb_info; + struct timer_list refresh; + int dirty; + int x1, y1, x2, y2; /* dirty rectangle, + protected by mm_lock */ + spinlock_t mm_lock; + int nr_pages; + struct page **pages; + struct list_head mappings; /* protected by mm_lock */ + + unsigned evtchn; + int irq; + struct xenfb_page *page; + unsigned long *mfns; + int update_wanted; /* XENFB_TYPE_UPDATE wanted */ + + struct xenbus_device *xbdev; +}; + +static int xenfb_fps = 20; +static unsigned long xenfb_mem_len = XENFB_WIDTH * XENFB_HEIGHT * XENFB_DEPTH / 8; + +static int xenfb_remove(struct xenbus_device *); +static void xenfb_init_shared_page(struct xenfb_info *); +static int xenfb_connect_backend(struct xenbus_device *, struct xenfb_info *); +static void xenfb_disconnect_backend(struct xenfb_info *); + +static void xenfb_do_update(struct xenfb_info *info, + int x, int y, int w, int h) +{ + union xenfb_out_event event; + __u32 prod; + + event.type = XENFB_TYPE_UPDATE; + event.update.x = x; + event.update.y = y; + event.update.width = w; + event.update.height = h; + + prod = info->page->out_prod; + /* caller ensures !xenfb_queue_full() */ + mb(); /* ensure ring space available */ + XENFB_OUT_RING_REF(info->page, prod) = event; + wmb(); /* ensure ring contents visible */ + info->page->out_prod = prod + 1; + + notify_remote_via_evtchn(info->evtchn); +} + +static int xenfb_queue_full(struct xenfb_info *info) +{ + __u32 cons, prod; + + prod = info->page->out_prod; + cons = info->page->out_cons; + return prod - cons == XENFB_OUT_RING_LEN; +} + +static void xenfb_update_screen(struct xenfb_info *info) +{ + unsigned long flags; + int y1, y2, x1, x2; + struct xenfb_mapping *map; + + if (!info->update_wanted) + return; + if (xenfb_queue_full(info)) + return; + + spin_lock_irqsave(&info->mm_lock, flags); + + y1 = info->y1; + y2 = info->y2; + x1 = info->x1; + x2 = info->x2; + info->x1 = info->y1 = INT_MAX; + info->x2 = info->y2 = 0; + + list_for_each_entry(map, &info->mappings, link) { + if (!map->faults) + continue; + zap_page_range(map->vma, map->vma->vm_start, + map->vma->vm_end - map->vma->vm_start, NULL); + map->faults = 0; + } + + spin_unlock_irqrestore(&info->mm_lock, flags); + + xenfb_do_update(info, x1, y1, x2 - x1, y2 - y1); +} + +static int xenfb_thread(void *data) +{ + struct xenfb_info *info = data; + + while (!kthread_should_stop()) { + if (info->dirty) { + info->dirty = 0; + xenfb_update_screen(info); + } + wait_event_interruptible(info->wq, + kthread_should_stop() || info->dirty); + try_to_freeze(); + } + return 0; +} + +static int xenfb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *info) +{ + u32 v; + + if (regno > info->cmap.len) + return 1; + + red >>= (16 - info->var.red.length); + green >>= (16 - info->var.green.length); + blue >>= (16 - info->var.blue.length); + + v = (red << info->var.red.offset) | + (green << info->var.green.offset) | + (blue << info->var.blue.offset); + + /* FIXME is this sane? check against xxxfb_setcolreg()! */ + switch (info->var.bits_per_pixel) { + case 16: + case 24: + case 32: + ((u32 *)info->pseudo_palette)[regno] = v; + break; + } + + return 0; +} + +static void xenfb_timer(unsigned long data) +{ + struct xenfb_info *info = (struct xenfb_info *)data; + info->dirty = 1; + wake_up(&info->wq); +} + +static void __xenfb_refresh(struct xenfb_info *info, + int x1, int y1, int w, int h) +{ + int y2, x2; + + y2 = y1 + h; + x2 = x1 + w; + + if (info->y1 > y1) + info->y1 = y1; + if (info->y2 < y2) + info->y2 = y2; + if (info->x1 > x1) + info->x1 = x1; + if (info->x2 < x2) + info->x2 = x2; + + if (timer_pending(&info->refresh)) + return; + + mod_timer(&info->refresh, jiffies + HZ/xenfb_fps); +} + +static void xenfb_refresh(struct xenfb_info *info, + int x1, int y1, int w, int h) +{ + unsigned long flags; + + spin_lock_irqsave(&info->mm_lock, flags); + __xenfb_refresh(info, x1, y1, w, h); + spin_unlock_irqrestore(&info->mm_lock, flags); +} + +static void xenfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect) +{ + struct xenfb_info *info = p->par; + + cfb_fillrect(p, rect); + xenfb_refresh(info, rect->dx, rect->dy, rect->width, rect->height); +} + +static void xenfb_imageblit(struct fb_info *p, const struct fb_image *image) +{ + struct xenfb_info *info = p->par; + + cfb_imageblit(p, image); + xenfb_refresh(info, image->dx, image->dy, image->width, image->height); +} + +static void xenfb_copyarea(struct fb_info *p, const struct fb_copyarea *area) +{ + struct xenfb_info *info = p->par; + + cfb_copyarea(p, area); + xenfb_refresh(info, area->dx, area->dy, area->width, area->height); +} + +static void xenfb_vm_open(struct vm_area_struct *vma) +{ + struct xenfb_mapping *map = vma->vm_private_data; + atomic_inc(&map->map_refs); +} + +static void xenfb_vm_close(struct vm_area_struct *vma) +{ + struct xenfb_mapping *map = vma->vm_private_data; + struct xenfb_info *info = map->info; + unsigned long flags; + + spin_lock_irqsave(&info->mm_lock, flags); + if (atomic_dec_and_test(&map->map_refs)) { + list_del(&map->link); + kfree(map); + } + spin_unlock_irqrestore(&info->mm_lock, flags); +} + +static struct page *xenfb_vm_nopage(struct vm_area_struct *vma, + unsigned long vaddr, int *type) +{ + struct xenfb_mapping *map = vma->vm_private_data; + struct xenfb_info *info = map->info; + int pgnr = (vaddr - vma->vm_start) >> PAGE_SHIFT; + unsigned long flags; + struct page *page; + int y1, y2; + + if (pgnr >= info->nr_pages) + return NOPAGE_SIGBUS; + + spin_lock_irqsave(&info->mm_lock, flags); + page = info->pages[pgnr]; + get_page(page); + map->faults++; + + y1 = pgnr * PAGE_SIZE / info->fb_info->fix.line_length; + y2 = (pgnr * PAGE_SIZE + PAGE_SIZE - 1) / info->fb_info->fix.line_length; + if (y2 > info->fb_info->var.yres) + y2 = info->fb_info->var.yres; + __xenfb_refresh(info, 0, y1, info->fb_info->var.xres, y2 - y1); + spin_unlock_irqrestore(&info->mm_lock, flags); + + if (type) + *type = VM_FAULT_MINOR; + + return page; +} + +static struct vm_operations_struct xenfb_vm_ops = { + .open = xenfb_vm_open, + .close = xenfb_vm_close, + .nopage = xenfb_vm_nopage, +}; + +static int xenfb_mmap(struct fb_info *fb_info, struct vm_area_struct *vma) +{ + struct xenfb_info *info = fb_info->par; + unsigned long flags; + struct xenfb_mapping *map; + int map_pages; + + if (!(vma->vm_flags & VM_WRITE)) + return -EINVAL; + if (!(vma->vm_flags & VM_SHARED)) + return -EINVAL; + if (vma->vm_pgoff != 0) + return -EINVAL; + + map_pages = (vma->vm_end - vma->vm_start + PAGE_SIZE-1) >> PAGE_SHIFT; + if (map_pages > info->nr_pages) + return -EINVAL; + + map = kzalloc(sizeof(*map), GFP_KERNEL); + if (map == NULL) + return -ENOMEM; + + map->vma = vma; + map->faults = 0; + map->info = info; + atomic_set(&map->map_refs, 1); + + spin_lock_irqsave(&info->mm_lock, flags); + list_add(&map->link, &info->mappings); + spin_unlock_irqrestore(&info->mm_lock, flags); + + vma->vm_ops = &xenfb_vm_ops; + vma->vm_flags |= (VM_DONTEXPAND | VM_RESERVED); + vma->vm_private_data = map; + + return 0; +} + +static struct fb_ops xenfb_fb_ops = { + .owner = THIS_MODULE, + .fb_setcolreg = xenfb_setcolreg, + .fb_fillrect = xenfb_fillrect, + .fb_copyarea = xenfb_copyarea, + .fb_imageblit = xenfb_imageblit, + .fb_mmap = xenfb_mmap, +}; + +static irqreturn_t xenfb_event_handler(int rq, void *dev_id, + struct pt_regs *regs) +{ + /* + * No in events recognized, simply ignore them all. + * If you need to recognize some, see xenbkd's input_handler() + * for how to do that. + */ + struct xenfb_info *info = dev_id; + struct xenfb_page *page = info->page; + + if (page->in_cons != page->in_prod) { + info->page->in_cons = info->page->in_prod; + notify_remote_via_evtchn(info->evtchn); + } + return IRQ_HANDLED; +} + +static unsigned long vmalloc_to_mfn(void *address) +{ + return pfn_to_mfn(vmalloc_to_pfn(address)); +} + +static int __devinit xenfb_probe(struct xenbus_device *dev, + const struct xenbus_device_id *id) +{ + struct xenfb_info *info; + struct fb_info *fb_info; + int ret; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (info == NULL) { + xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure"); + return -ENOMEM; + } + dev->dev.driver_data = info; + info->xbdev = dev; + info->irq = -1; + info->x1 = info->y1 = INT_MAX; + spin_lock_init(&info->mm_lock); + init_waitqueue_head(&info->wq); + init_timer(&info->refresh); + info->refresh.function = xenfb_timer; + info->refresh.data = (unsigned long)info; + INIT_LIST_HEAD(&info->mappings); + + info->fb = vmalloc(xenfb_mem_len); + if (info->fb == NULL) + goto error_nomem; + memset(info->fb, 0, xenfb_mem_len); + + info->nr_pages = (xenfb_mem_len + PAGE_SIZE - 1) >> PAGE_SHIFT; + + info->pages = kmalloc(sizeof(struct page *) * info->nr_pages, + GFP_KERNEL); + if (info->pages == NULL) + goto error_nomem; + + info->mfns = vmalloc(sizeof(unsigned long) * info->nr_pages); + if (!info->mfns) + goto error_nomem; + + /* set up shared page */ + info->page = (void *)__get_free_page(GFP_KERNEL); + if (!info->page) + goto error_nomem; + + xenfb_init_shared_page(info); + + fb_info = framebuffer_alloc(sizeof(u32) * 256, NULL); + /* see fishy hackery below */ + if (fb_info == NULL) + goto error_nomem; + + /* FIXME fishy hackery */ + fb_info->pseudo_palette = fb_info->par; + fb_info->par = info; + /* /FIXME */ + fb_info->screen_base = info->fb; + + fb_info->fbops = &xenfb_fb_ops; + fb_info->var.xres_virtual = fb_info->var.xres = info->page->width; + fb_info->var.yres_virtual = fb_info->var.yres = info->page->height; + fb_info->var.bits_per_pixel = info->page->depth; + + fb_info->var.red = (struct fb_bitfield){16, 8, 0}; + fb_info->var.green = (struct fb_bitfield){8, 8, 0}; + fb_info->var.blue = (struct fb_bitfield){0, 8, 0}; + + fb_info->var.activate = FB_ACTIVATE_NOW; + fb_info->var.height = -1; + fb_info->var.width = -1; + fb_info->var.vmode = FB_VMODE_NONINTERLACED; + + fb_info->fix.visual = FB_VISUAL_TRUECOLOR; + fb_info->fix.line_length = info->page->line_length; + fb_info->fix.smem_start = 0; + fb_info->fix.smem_len = xenfb_mem_len; + strcpy(fb_info->fix.id, "xen"); + fb_info->fix.type = FB_TYPE_PACKED_PIXELS; + fb_info->fix.accel = FB_ACCEL_NONE; + + fb_info->flags = FBINFO_FLAG_DEFAULT; + + ret = fb_alloc_cmap(&fb_info->cmap, 256, 0); + if (ret < 0) { + framebuffer_release(fb_info); + xenbus_dev_fatal(dev, ret, "fb_alloc_cmap"); + goto error; + } + + ret = register_framebuffer(fb_info); + if (ret) { + fb_dealloc_cmap(&info->fb_info->cmap); + framebuffer_release(fb_info); + xenbus_dev_fatal(dev, ret, "register_framebuffer"); + goto error; + } + info->fb_info = fb_info; + + /* FIXME should this be delayed until backend XenbusStateConnected? */ + info->kthread = kthread_run(xenfb_thread, info, "xenfb thread"); + if (IS_ERR(info->kthread)) { + ret = PTR_ERR(info->kthread); + info->kthread = NULL; + xenbus_dev_fatal(dev, ret, "register_framebuffer"); + goto error; + } + + ret = xenfb_connect_backend(dev, info); + if (ret < 0) + goto error; + + return 0; + + error_nomem: + ret = -ENOMEM; + xenbus_dev_fatal(dev, ret, "allocating device memory"); + error: + xenfb_remove(dev); + return ret; +} + +static int xenfb_resume(struct xenbus_device *dev) +{ + struct xenfb_info *info = dev->dev.driver_data; + + xenfb_disconnect_backend(info); + xenfb_init_shared_page(info); + return xenfb_connect_backend(dev, info); +} + +static int xenfb_remove(struct xenbus_device *dev) +{ + struct xenfb_info *info = dev->dev.driver_data; + + del_timer(&info->refresh); + if (info->kthread) + kthread_stop(info->kthread); + xenfb_disconnect_backend(info); + if (info->fb_info) { + unregister_framebuffer(info->fb_info); + fb_dealloc_cmap(&info->fb_info->cmap); + framebuffer_release(info->fb_info); + } + free_page((unsigned long)info->page); + vfree(info->mfns); + kfree(info->pages); + vfree(info->fb); + kfree(info); + + return 0; +} + +static void xenfb_init_shared_page(struct xenfb_info *info) +{ + int i; + + for (i = 0; i < info->nr_pages; i++) + info->pages[i] = vmalloc_to_page(info->fb + i * PAGE_SIZE); + + for (i = 0; i < info->nr_pages; i++) + info->mfns[i] = vmalloc_to_mfn(info->fb + i * PAGE_SIZE); + + info->page->pd[0] = vmalloc_to_mfn(info->mfns); + info->page->pd[1] = 0; + info->page->width = XENFB_WIDTH; + info->page->height = XENFB_HEIGHT; + info->page->depth = XENFB_DEPTH; + info->page->line_length = (info->page->depth / 8) * info->page->width; + info->page->mem_length = xenfb_mem_len; + info->page->in_cons = info->page->in_prod = 0; + info->page->out_cons = info->page->out_prod = 0; +} + +static int xenfb_connect_backend(struct xenbus_device *dev, + struct xenfb_info *info) +{ + int ret; + struct xenbus_transaction xbt; + + ret = xenbus_alloc_evtchn(dev, &info->evtchn); + if (ret) + return ret; + ret = bind_evtchn_to_irqhandler(info->evtchn, xenfb_event_handler, + 0, "xenfb", info); + if (ret < 0) { + xenbus_free_evtchn(dev, info->evtchn); + xenbus_dev_fatal(dev, ret, "bind_evtchn_to_irqhandler"); + return ret; + } + info->irq = ret; + + again: + ret = xenbus_transaction_start(&xbt); + if (ret) { + xenbus_dev_fatal(dev, ret, "starting transaction"); + return ret; + } + ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu", + virt_to_mfn(info->page)); + if (ret) + goto error_xenbus; + ret = xenbus_printf(xbt, dev->nodename, "event-channel", "%u", + info->evtchn); + if (ret) + goto error_xenbus; + ret = xenbus_printf(xbt, dev->nodename, "feature-update", "1"); + if (ret) + goto error_xenbus; + ret = xenbus_transaction_end(xbt, 0); + if (ret) { + if (ret == -EAGAIN) + goto again; + xenbus_dev_fatal(dev, ret, "completing transaction"); + return ret; + } + + xenbus_switch_state(dev, XenbusStateInitialised); + return 0; + + error_xenbus: + xenbus_transaction_end(xbt, 1); + xenbus_dev_fatal(dev, ret, "writing xenstore"); + return ret; +} + +static void xenfb_disconnect_backend(struct xenfb_info *info) +{ + if (info->irq >= 0) + unbind_from_irqhandler(info->irq, info); + info->irq = -1; +} + +static void xenfb_backend_changed(struct xenbus_device *dev, + enum xenbus_state backend_state) +{ + struct xenfb_info *info = dev->dev.driver_data; + int val; + + switch (backend_state) { + case XenbusStateInitialising: + case XenbusStateInitialised: + case XenbusStateUnknown: + case XenbusStateClosed: + break; + + case XenbusStateInitWait: + InitWait: + xenbus_switch_state(dev, XenbusStateConnected); + break; + + case XenbusStateConnected: + /* + * Work around xenbus race condition: If backend goes + * through InitWait to Connected fast enough, we can + * get Connected twice here. + */ + if (dev->state != XenbusStateConnected) + goto InitWait; /* no InitWait seen yet, fudge it */ + + if (xenbus_scanf(XBT_NIL, info->xbdev->otherend, + "request-update", "%d", &val) < 0) + val = 0; + if (val) + info->update_wanted = 1; + break; + + case XenbusStateClosing: + // FIXME is this safe in any dev->state? + xenbus_frontend_closed(dev); + break; + } +} + +static struct xenbus_device_id xenfb_ids[] = { + { "vfb" }, + { "" } +}; + +static struct xenbus_driver xenfb = { + .name = "vfb", + .owner = THIS_MODULE, + .ids = xenfb_ids, + .probe = xenfb_probe, + .remove = xenfb_remove, + .resume = xenfb_resume, + .otherend_changed = xenfb_backend_changed, +}; + +static int __init xenfb_init(void) +{ + if (!is_running_on_xen()) + return -ENODEV; + + /* Nothing to do if running in dom0. */ + if (is_initial_xendomain()) + return -ENODEV; + + return xenbus_register_frontend(&xenfb); +} + +static void __exit xenfb_cleanup(void) +{ + return xenbus_unregister_driver(&xenfb); +} + +module_init(xenfb_init); +module_exit(xenfb_cleanup); + +MODULE_LICENSE("GPL"); diff --git a/linux-2.6-xen-sparse/drivers/xen/fbfront/xenkbd.c b/linux-2.6-xen-sparse/drivers/xen/fbfront/xenkbd.c new file mode 100644 index 0000000000..7737732e34 --- /dev/null +++ b/linux-2.6-xen-sparse/drivers/xen/fbfront/xenkbd.c @@ -0,0 +1,300 @@ +/* + * linux/drivers/input/keyboard/xenkbd.c -- Xen para-virtual input device + * + * Copyright (C) 2005 Anthony Liguori + * Copyright (C) 2006 Red Hat, Inc., Markus Armbruster + * + * Based on linux/drivers/input/mouse/sermouse.c + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +/* + * TODO: + * + * Switch to grant tables together with xenfb.c. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct xenkbd_info +{ + struct input_dev *dev; + struct xenkbd_page *page; + unsigned evtchn; + int irq; + struct xenbus_device *xbdev; +}; + +static int xenkbd_remove(struct xenbus_device *); +static int xenkbd_connect_backend(struct xenbus_device *, struct xenkbd_info *); +static void xenkbd_disconnect_backend(struct xenkbd_info *); + +/* + * Note: if you need to send out events, see xenfb_do_update() for how + * to do that. + */ + +static irqreturn_t input_handler(int rq, void *dev_id, struct pt_regs *regs) +{ + struct xenkbd_info *info = dev_id; + struct xenkbd_page *page = info->page; + __u32 cons, prod; + + prod = page->in_prod; + if (prod == page->out_cons) + return IRQ_HANDLED; + rmb(); /* ensure we see ring contents up to prod */ + for (cons = page->in_cons; cons != prod; cons++) { + union xenkbd_in_event *event; + event = &XENKBD_IN_RING_REF(page, cons); + + switch (event->type) { + case XENKBD_TYPE_MOTION: + input_report_rel(info->dev, REL_X, event->motion.rel_x); + input_report_rel(info->dev, REL_Y, event->motion.rel_y); + break; + case XENKBD_TYPE_KEY: + input_report_key(info->dev, event->key.keycode, event->key.pressed); + break; + case XENKBD_TYPE_POS: + input_report_abs(info->dev, ABS_X, event->pos.abs_x); + input_report_abs(info->dev, ABS_Y, event->pos.abs_y); + break; + } + } + input_sync(info->dev); + mb(); /* ensure we got ring contents */ + page->in_cons = cons; + notify_remote_via_evtchn(info->evtchn); + + return IRQ_HANDLED; +} + +int __devinit xenkbd_probe(struct xenbus_device *dev, + const struct xenbus_device_id *id) +{ + int ret, i; + struct xenkbd_info *info; + struct input_dev *input_dev; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure"); + return -ENOMEM; + } + dev->dev.driver_data = info; + info->xbdev = dev; + + info->page = (void *)__get_free_page(GFP_KERNEL); + if (!info->page) + goto error_nomem; + info->page->in_cons = info->page->in_prod = 0; + info->page->out_cons = info->page->out_prod = 0; + + input_dev = input_allocate_device(); + if (!input_dev) + goto error_nomem; + + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_ABS); + input_dev->keybit[LONG(BTN_MOUSE)] + = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT); + /* TODO additional buttons */ + input_dev->relbit[0] = BIT(REL_X) | BIT(REL_Y); + + /* FIXME not sure this is quite right */ + for (i = 0; i < 256; i++) + set_bit(i, input_dev->keybit); + + input_dev->name = "Xen Virtual Keyboard/Mouse"; + + input_set_abs_params(input_dev, ABS_X, 0, XENFB_WIDTH, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, XENFB_HEIGHT, 0, 0); + + ret = input_register_device(input_dev); + if (ret) { + input_free_device(input_dev); + xenbus_dev_fatal(dev, ret, "input_register_device"); + goto error; + } + info->dev = input_dev; + + ret = xenkbd_connect_backend(dev, info); + if (ret < 0) + goto error; + + return 0; + + error_nomem: + ret = -ENOMEM; + xenbus_dev_fatal(dev, ret, "allocating device memory"); + error: + xenkbd_remove(dev); + return ret; +} + +static int xenkbd_resume(struct xenbus_device *dev) +{ + struct xenkbd_info *info = dev->dev.driver_data; + + xenkbd_disconnect_backend(info); + return xenkbd_connect_backend(dev, info); +} + +static int xenkbd_remove(struct xenbus_device *dev) +{ + struct xenkbd_info *info = dev->dev.driver_data; + + xenkbd_disconnect_backend(info); + input_unregister_device(info->dev); + free_page((unsigned long)info->page); + kfree(info); + return 0; +} + +static int xenkbd_connect_backend(struct xenbus_device *dev, + struct xenkbd_info *info) +{ + int ret; + struct xenbus_transaction xbt; + + ret = xenbus_alloc_evtchn(dev, &info->evtchn); + if (ret) + return ret; + ret = bind_evtchn_to_irqhandler(info->evtchn, input_handler, 0, + "xenkbd", info); + if (ret < 0) { + xenbus_free_evtchn(dev, info->evtchn); + xenbus_dev_fatal(dev, ret, "bind_evtchn_to_irqhandler"); + return ret; + } + info->irq = ret; + + again: + ret = xenbus_transaction_start(&xbt); + if (ret) { + xenbus_dev_fatal(dev, ret, "starting transaction"); + return ret; + } + ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu", + virt_to_mfn(info->page)); + if (ret) + goto error_xenbus; + ret = xenbus_printf(xbt, dev->nodename, "event-channel", "%u", + info->evtchn); + if (ret) + goto error_xenbus; + ret = xenbus_transaction_end(xbt, 0); + if (ret) { + if (ret == -EAGAIN) + goto again; + xenbus_dev_fatal(dev, ret, "completing transaction"); + return ret; + } + + xenbus_switch_state(dev, XenbusStateInitialised); + return 0; + + error_xenbus: + xenbus_transaction_end(xbt, 1); + xenbus_dev_fatal(dev, ret, "writing xenstore"); + return ret; +} + +static void xenkbd_disconnect_backend(struct xenkbd_info *info) +{ + if (info->irq >= 0) + unbind_from_irqhandler(info->irq, info); + info->irq = -1; +} + +static void xenkbd_backend_changed(struct xenbus_device *dev, + enum xenbus_state backend_state) +{ + struct xenkbd_info *info = dev->dev.driver_data; + int ret, val; + + switch (backend_state) { + case XenbusStateInitialising: + case XenbusStateInitialised: + case XenbusStateUnknown: + case XenbusStateClosed: + break; + + case XenbusStateInitWait: + InitWait: + ret = xenbus_scanf(XBT_NIL, info->xbdev->otherend, + "feature-abs-pointer", "%d", &val); + if (ret < 0) + val = 0; + if (val) { + ret = xenbus_printf(XBT_NIL, info->xbdev->nodename, + "request-abs-pointer", "1"); + if (ret) + ; /* FIXME */ + } + xenbus_switch_state(dev, XenbusStateConnected); + break; + + case XenbusStateConnected: + /* + * Work around xenbus race condition: If backend goes + * through InitWait to Connected fast enough, we can + * get Connected twice here. + */ + if (dev->state != XenbusStateConnected) + goto InitWait; /* no InitWait seen yet, fudge it */ + break; + + case XenbusStateClosing: + xenbus_frontend_closed(dev); + break; + } +} + +static struct xenbus_device_id xenkbd_ids[] = { + { "vkbd" }, + { "" } +}; + +static struct xenbus_driver xenkbd = { + .name = "vkbd", + .owner = THIS_MODULE, + .ids = xenkbd_ids, + .probe = xenkbd_probe, + .remove = xenkbd_remove, + .resume = xenkbd_resume, + .otherend_changed = xenkbd_backend_changed, +}; + +static int __init xenkbd_init(void) +{ + if (!is_running_on_xen()) + return -ENODEV; + + /* Nothing to do if running in dom0. */ + if (is_initial_xendomain()) + return -ENODEV; + + return xenbus_register_frontend(&xenkbd); +} + +static void __exit xenkbd_cleanup(void) +{ + return xenbus_unregister_driver(&xenkbd); +} + +module_init(xenkbd_init); +module_exit(xenkbd_cleanup); + +MODULE_LICENSE("GPL"); diff --git a/linux-2.6-xen-sparse/drivers/xen/netback/netback.c b/linux-2.6-xen-sparse/drivers/xen/netback/netback.c index 1d24fc9b88..1884597430 100644 --- a/linux-2.6-xen-sparse/drivers/xen/netback/netback.c +++ b/linux-2.6-xen-sparse/drivers/xen/netback/netback.c @@ -183,7 +183,7 @@ static struct sk_buff *netbk_copy_skb(struct sk_buff *skb) BUG_ON(skb_shinfo(skb)->frag_list != NULL); - nskb = alloc_skb(SKB_MAX_HEAD(0), GFP_ATOMIC); + nskb = alloc_skb(SKB_MAX_HEAD(0), GFP_ATOMIC | __GFP_NOWARN); if (unlikely(!nskb)) goto err; @@ -1224,7 +1224,8 @@ static void net_tx_action(unsigned long unused) ret < MAX_SKB_FRAGS) ? PKT_PROT_LEN : txreq.size; - skb = alloc_skb(data_len + 16 + NET_IP_ALIGN, GFP_ATOMIC); + skb = alloc_skb(data_len + 16 + NET_IP_ALIGN, + GFP_ATOMIC | __GFP_NOWARN); if (unlikely(skb == NULL)) { DPRINTK("Can't allocate a skb in start_xmit.\n"); netbk_tx_err(netif, &txreq, i); @@ -1506,13 +1507,12 @@ static int __init netback_init(void) netif_xenbus_init(); #ifdef NETBE_DEBUG_INTERRUPT - (void)bind_virq_to_irqhandler( - VIRQ_DEBUG, - 0, - netif_be_dbg, - SA_SHIRQ, - "net-be-dbg", - &netif_be_dbg); + (void)bind_virq_to_irqhandler(VIRQ_DEBUG, + 0, + netif_be_dbg, + SA_SHIRQ, + "net-be-dbg", + &netif_be_dbg); #endif return 0; diff --git a/linux-2.6-xen-sparse/drivers/xen/xenbus/Makefile b/linux-2.6-xen-sparse/drivers/xen/xenbus/Makefile index ce5acc2457..d7091dcbcb 100644 --- a/linux-2.6-xen-sparse/drivers/xen/xenbus/Makefile +++ b/linux-2.6-xen-sparse/drivers/xen/xenbus/Makefile @@ -1,13 +1,9 @@ -obj-y += xenbus.o +obj-y += xenbus_client.o xenbus_comms.o xenbus_xs.o xenbus_probe.o obj-$(CONFIG_XEN_BACKEND) += xenbus_be.o xenbus_be-objs = xenbus_be-objs += xenbus_backend_client.o -xenbus-objs = -xenbus-objs += xenbus_client.o -xenbus-objs += xenbus_comms.o -xenbus-objs += xenbus_xs.o -xenbus-objs += xenbus_probe.o -obj-$(CONFIG_XEN_BACKEND) += xenbus_probe_backend.o +xenbus-$(CONFIG_XEN_BACKEND) += xenbus_probe_backend.o +obj-y += $(xenbus-y) $(xenbus-m) obj-$(CONFIG_XEN_XENBUS_DEV) += xenbus_dev.o diff --git a/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_client.c b/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_client.c index 0111e8e3a2..a11140bda0 100644 --- a/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_client.c +++ b/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_client.c @@ -289,6 +289,7 @@ int xenbus_free_evtchn(struct xenbus_device *dev, int port) return err; } +EXPORT_SYMBOL_GPL(xenbus_free_evtchn); enum xenbus_state xenbus_read_driver_state(const char *path) diff --git a/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_comms.c b/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_comms.c index f0e42ba715..e5af661c87 100644 --- a/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_comms.c +++ b/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_comms.c @@ -52,7 +52,7 @@ extern void xenbus_probe(void *); extern int xenstored_ready; static DECLARE_WORK(probe_work, xenbus_probe, NULL); -DECLARE_WAIT_QUEUE_HEAD(xb_waitq); +static DECLARE_WAIT_QUEUE_HEAD(xb_waitq); static irqreturn_t wake_waiting(int irq, void *unused, struct pt_regs *regs) { diff --git a/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_comms.h b/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_comms.h index 9599d037c4..b54d168cbb 100644 --- a/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_comms.h +++ b/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_comms.h @@ -38,7 +38,6 @@ int xb_init_comms(void); int xb_write(const void *data, unsigned len); int xb_read(void *data, unsigned len); int xs_input_avail(void); -extern wait_queue_head_t xb_waitq; extern struct xenstore_domain_interface *xen_store_interface; extern int xen_store_evtchn; diff --git a/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_probe.h b/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_probe.h index 2d2e567826..417b23e184 100644 --- a/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_probe.h +++ b/linux-2.6-xen-sparse/drivers/xen/xenbus/xenbus_probe.h @@ -34,7 +34,7 @@ #ifndef _XENBUS_PROBE_H #define _XENBUS_PROBE_H -#ifdef CONFIG_XEN_BACKEND +#if defined(CONFIG_XEN_BACKEND) || defined(CONFIG_XEN_BACKEND_MODULE) extern void xenbus_backend_suspend(int (*fn)(struct device *, void *)); extern void xenbus_backend_resume(int (*fn)(struct device *, void *)); extern void xenbus_backend_probe_and_watch(void); diff --git a/linux-2.6-xen-sparse/include/asm-i386/kexec.h b/linux-2.6-xen-sparse/include/asm-i386/kexec.h new file mode 100644 index 0000000000..4610897c78 --- /dev/null +++ b/linux-2.6-xen-sparse/include/asm-i386/kexec.h @@ -0,0 +1,117 @@ +#ifndef _I386_KEXEC_H +#define _I386_KEXEC_H + +#define PA_CONTROL_PAGE 0 +#define VA_CONTROL_PAGE 1 +#define PA_PGD 2 +#define VA_PGD 3 +#define PA_PTE_0 4 +#define VA_PTE_0 5 +#define PA_PTE_1 6 +#define VA_PTE_1 7 +#ifdef CONFIG_X86_PAE +#define PA_PMD_0 8 +#define VA_PMD_0 9 +#define PA_PMD_1 10 +#define VA_PMD_1 11 +#define PAGES_NR 12 +#else +#define PAGES_NR 8 +#endif + +#ifndef __ASSEMBLY__ + +#include +#include +#include + +/* + * KEXEC_SOURCE_MEMORY_LIMIT maximum page get_free_page can return. + * I.e. Maximum page that is mapped directly into kernel memory, + * and kmap is not required. + * + * Someone correct me if FIXADDR_START - PAGEOFFSET is not the correct + * calculation for the amount of memory directly mappable into the + * kernel memory space. + */ + +/* Maximum physical address we can use pages from */ +#define KEXEC_SOURCE_MEMORY_LIMIT (-1UL) +/* Maximum address we can reach in physical address mode */ +#define KEXEC_DESTINATION_MEMORY_LIMIT (-1UL) +/* Maximum address we can use for the control code buffer */ +#define KEXEC_CONTROL_MEMORY_LIMIT TASK_SIZE + +#define KEXEC_CONTROL_CODE_SIZE 4096 + +/* The native architecture */ +#define KEXEC_ARCH KEXEC_ARCH_386 + +#define MAX_NOTE_BYTES 1024 + +/* CPU does not save ss and esp on stack if execution is already + * running in kernel mode at the time of NMI occurrence. This code + * fixes it. + */ +static inline void crash_fixup_ss_esp(struct pt_regs *newregs, + struct pt_regs *oldregs) +{ + memcpy(newregs, oldregs, sizeof(*newregs)); + newregs->esp = (unsigned long)&(oldregs->esp); + __asm__ __volatile__( + "xorl %%eax, %%eax\n\t" + "movw %%ss, %%ax\n\t" + :"=a"(newregs->xss)); +} + +/* + * This function is responsible for capturing register states if coming + * via panic otherwise just fix up the ss and esp if coming via kernel + * mode exception. + */ +static inline void crash_setup_regs(struct pt_regs *newregs, + struct pt_regs *oldregs) +{ + if (oldregs) + crash_fixup_ss_esp(newregs, oldregs); + else { + __asm__ __volatile__("movl %%ebx,%0" : "=m"(newregs->ebx)); + __asm__ __volatile__("movl %%ecx,%0" : "=m"(newregs->ecx)); + __asm__ __volatile__("movl %%edx,%0" : "=m"(newregs->edx)); + __asm__ __volatile__("movl %%esi,%0" : "=m"(newregs->esi)); + __asm__ __volatile__("movl %%edi,%0" : "=m"(newregs->edi)); + __asm__ __volatile__("movl %%ebp,%0" : "=m"(newregs->ebp)); + __asm__ __volatile__("movl %%eax,%0" : "=m"(newregs->eax)); + __asm__ __volatile__("movl %%esp,%0" : "=m"(newregs->esp)); + __asm__ __volatile__("movw %%ss, %%ax;" :"=a"(newregs->xss)); + __asm__ __volatile__("movw %%cs, %%ax;" :"=a"(newregs->xcs)); + __asm__ __volatile__("movw %%ds, %%ax;" :"=a"(newregs->xds)); + __asm__ __volatile__("movw %%es, %%ax;" :"=a"(newregs->xes)); + __asm__ __volatile__("pushfl; popl %0" :"=m"(newregs->eflags)); + + newregs->eip = (unsigned long)current_text_addr(); + } +} +asmlinkage NORET_TYPE void +relocate_kernel(unsigned long indirection_page, + unsigned long control_page, + unsigned long start_address, + unsigned int has_pae) ATTRIB_NORET; + + +/* Under Xen we need to work with machine addresses. These macros give the + * machine address of a certain page to the generic kexec code instead of + * the pseudo physical address which would be given by the default macros. + */ + +#ifdef CONFIG_XEN +#define KEXEC_ARCH_HAS_PAGE_MACROS +#define kexec_page_to_pfn(page) pfn_to_mfn(page_to_pfn(page)) +#define kexec_pfn_to_page(pfn) pfn_to_page(mfn_to_pfn(pfn)) +#define kexec_virt_to_phys(addr) virt_to_machine(addr) +#define kexec_phys_to_virt(addr) phys_to_virt(machine_to_phys(addr)) +#endif + +#endif /* __ASSEMBLY__ */ + +#endif /* _I386_KEXEC_H */ diff --git a/linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/hypercall.h b/linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/hypercall.h index a12e349016..c3ed3584b6 100644 --- a/linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/hypercall.h +++ b/linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/hypercall.h @@ -395,5 +395,13 @@ HYPERVISOR_xenoprof_op( return _hypercall2(int, xenoprof_op, op, arg); } +static inline int +HYPERVISOR_kexec_op( + unsigned long op, void *args) +{ + return _hypercall2(int, kexec_op, op, args); +} + + #endif /* __HYPERCALL_H__ */ diff --git a/linux-2.6-xen-sparse/include/asm-ia64/hypercall.h b/linux-2.6-xen-sparse/include/asm-ia64/hypercall.h index 7a522be483..19be0e6ed3 100644 --- a/linux-2.6-xen-sparse/include/asm-ia64/hypercall.h +++ b/linux-2.6-xen-sparse/include/asm-ia64/hypercall.h @@ -271,6 +271,12 @@ HYPERVISOR_physdev_op(int cmd, void *arg) } } +static inline int +xencomm_arch_hypercall_xenoprof_op(int op, struct xencomm_handle *arg) +{ + return _hypercall2(int, xenoprof_op, op, arg); +} + extern fastcall unsigned int __do_IRQ(unsigned int irq, struct pt_regs *regs); static inline void exit_idle(void) {} #define do_IRQ(irq, regs) ({ \ @@ -381,6 +387,28 @@ HYPERVISOR_add_physmap(unsigned long gpfn, unsigned long mfn, return ret; } +static inline unsigned long +__HYPERVISOR_add_physmap_with_gmfn(unsigned long gpfn, unsigned long gmfn, + unsigned long flags, domid_t domid) +{ + return _hypercall5(unsigned long, ia64_dom0vp_op, + IA64_DOM0VP_add_physmap_with_gmfn, + gpfn, gmfn, flags, domid); +} + +static inline unsigned long +HYPERVISOR_add_physmap_with_gmfn(unsigned long gpfn, unsigned long gmfn, + unsigned long flags, domid_t domid) +{ + unsigned long ret = 0; + BUG_ON(!is_running_on_xen());//XXX + if (is_running_on_xen()) { + ret = __HYPERVISOR_add_physmap_with_gmfn(gpfn, gmfn, + flags, domid); + } + return ret; +} + #ifdef CONFIG_XEN_IA64_EXPOSE_P2M static inline unsigned long HYPERVISOR_expose_p2m(unsigned long conv_start_gpfn, @@ -393,6 +421,15 @@ HYPERVISOR_expose_p2m(unsigned long conv_start_gpfn, } #endif +static inline int +xencomm_arch_hypercall_perfmon_op(unsigned long cmd, + struct xencomm_handle *arg, + unsigned long count) +{ + return _hypercall4(int, ia64_dom0vp_op, + IA64_DOM0VP_perfmon, cmd, arg, count); +} + // for balloon driver #define HYPERVISOR_update_va_mapping(va, new_val, flags) (0) @@ -406,6 +443,8 @@ HYPERVISOR_expose_p2m(unsigned long conv_start_gpfn, #define HYPERVISOR_console_io xencomm_mini_hypercall_console_io #define HYPERVISOR_hvm_op xencomm_mini_hypercall_hvm_op #define HYPERVISOR_memory_op xencomm_mini_hypercall_memory_op +#define HYPERVISOR_xenoprof_op xencomm_mini_hypercall_xenoprof_op +#define HYPERVISOR_perfmon_op xencomm_mini_hypercall_perfmon_op #else #define HYPERVISOR_sched_op xencomm_hypercall_sched_op #define HYPERVISOR_event_channel_op xencomm_hypercall_event_channel_op @@ -415,6 +454,8 @@ HYPERVISOR_expose_p2m(unsigned long conv_start_gpfn, #define HYPERVISOR_console_io xencomm_hypercall_console_io #define HYPERVISOR_hvm_op xencomm_hypercall_hvm_op #define HYPERVISOR_memory_op xencomm_hypercall_memory_op +#define HYPERVISOR_xenoprof_op xencomm_hypercall_xenoprof_op +#define HYPERVISOR_perfmon_op xencomm_hypercall_perfmon_op #endif #define HYPERVISOR_suspend xencomm_hypercall_suspend diff --git a/linux-2.6-xen-sparse/include/asm-ia64/io.h b/linux-2.6-xen-sparse/include/asm-ia64/io.h index a841d78d79..73c9b60904 100644 --- a/linux-2.6-xen-sparse/include/asm-ia64/io.h +++ b/linux-2.6-xen-sparse/include/asm-ia64/io.h @@ -129,6 +129,11 @@ extern int valid_mmap_phys_addr_range (unsigned long addr, size_t *count); (((bvec_to_bus((vec1)) + (vec1)->bv_len) == bvec_to_bus((vec2))) && \ ((bvec_to_pseudophys((vec1)) + (vec1)->bv_len) == \ bvec_to_pseudophys((vec2)))) + +/* We will be supplying our own /dev/mem implementation */ +#define ARCH_HAS_DEV_MEM +#define ARCH_HAS_DEV_MEM_MMAP_MEM +int xen_mmap_mem(struct file * file, struct vm_area_struct * vma); #endif /* CONFIG_XEN */ # endif /* KERNEL */ @@ -458,6 +463,8 @@ static inline void __iomem * ioremap (unsigned long offset, unsigned long size) { offset = HYPERVISOR_ioremap(offset, size); + if (IS_ERR_VALUE(offset)) + return (void __iomem*)offset; return (void __iomem *) (__IA64_UNCACHED_OFFSET | (offset)); } diff --git a/linux-2.6-xen-sparse/include/asm-ia64/irq.h b/linux-2.6-xen-sparse/include/asm-ia64/irq.h index a86f83b9f3..dbe0a2e2b8 100644 --- a/linux-2.6-xen-sparse/include/asm-ia64/irq.h +++ b/linux-2.6-xen-sparse/include/asm-ia64/irq.h @@ -42,7 +42,9 @@ #define RESCHEDULE_VECTOR 0 #define IPI_VECTOR 1 -#define NR_IPIS 2 +#define CMCP_VECTOR 2 +#define CPEP_VECTOR 3 +#define NR_IPIS 4 #endif /* CONFIG_XEN */ /* diff --git a/linux-2.6-xen-sparse/include/asm-ia64/privop.h b/linux-2.6-xen-sparse/include/asm-ia64/privop.h index 28c1e886c0..05935407ee 100644 --- a/linux-2.6-xen-sparse/include/asm-ia64/privop.h +++ b/linux-2.6-xen-sparse/include/asm-ia64/privop.h @@ -49,6 +49,7 @@ #define ia64_leave_kernel __ia64_leave_kernel #define ia64_leave_syscall __ia64_leave_syscall #define ia64_trace_syscall __ia64_trace_syscall +#define ia64_ret_from_clone __ia64_ret_from_clone #define ia64_switch_to __ia64_switch_to #define ia64_pal_call_static __ia64_pal_call_static diff --git a/linux-2.6-xen-sparse/include/asm-ia64/sal.h b/linux-2.6-xen-sparse/include/asm-ia64/sal.h new file mode 100644 index 0000000000..9439b49d8b --- /dev/null +++ b/linux-2.6-xen-sparse/include/asm-ia64/sal.h @@ -0,0 +1,904 @@ +#ifndef _ASM_IA64_SAL_H +#define _ASM_IA64_SAL_H + +/* + * System Abstraction Layer definitions. + * + * This is based on version 2.5 of the manual "IA-64 System + * Abstraction Layer". + * + * Copyright (C) 2001 Intel + * Copyright (C) 2002 Jenna Hall + * Copyright (C) 2001 Fred Lewis + * Copyright (C) 1998, 1999, 2001, 2003 Hewlett-Packard Co + * David Mosberger-Tang + * Copyright (C) 1999 Srinivasa Prasad Thirumalachar + * + * 02/01/04 J. Hall Updated Error Record Structures to conform to July 2001 + * revision of the SAL spec. + * 01/01/03 fvlewis Updated Error Record Structures to conform with Nov. 2000 + * revision of the SAL spec. + * 99/09/29 davidm Updated for SAL 2.6. + * 00/03/29 cfleck Updated SAL Error Logging info for processor (SAL 2.6) + * (plus examples of platform error info structures from smariset @ Intel) + */ + +#define IA64_SAL_PLATFORM_FEATURE_BUS_LOCK_BIT 0 +#define IA64_SAL_PLATFORM_FEATURE_IRQ_REDIR_HINT_BIT 1 +#define IA64_SAL_PLATFORM_FEATURE_IPI_REDIR_HINT_BIT 2 +#define IA64_SAL_PLATFORM_FEATURE_ITC_DRIFT_BIT 3 + +#define IA64_SAL_PLATFORM_FEATURE_BUS_LOCK (1< +#include +#include + +#include +#include +#include +#ifdef CONFIG_XEN +#include +#endif + +extern spinlock_t sal_lock; + +/* SAL spec _requires_ eight args for each call. */ +#define __SAL_CALL(result,a0,a1,a2,a3,a4,a5,a6,a7) \ + result = (*ia64_sal)(a0,a1,a2,a3,a4,a5,a6,a7) + +# define SAL_CALL(result,args...) do { \ + unsigned long __ia64_sc_flags; \ + struct ia64_fpreg __ia64_sc_fr[6]; \ + ia64_save_scratch_fpregs(__ia64_sc_fr); \ + spin_lock_irqsave(&sal_lock, __ia64_sc_flags); \ + __SAL_CALL(result, args); \ + spin_unlock_irqrestore(&sal_lock, __ia64_sc_flags); \ + ia64_load_scratch_fpregs(__ia64_sc_fr); \ +} while (0) + +# define SAL_CALL_NOLOCK(result,args...) do { \ + unsigned long __ia64_scn_flags; \ + struct ia64_fpreg __ia64_scn_fr[6]; \ + ia64_save_scratch_fpregs(__ia64_scn_fr); \ + local_irq_save(__ia64_scn_flags); \ + __SAL_CALL(result, args); \ + local_irq_restore(__ia64_scn_flags); \ + ia64_load_scratch_fpregs(__ia64_scn_fr); \ +} while (0) + +# define SAL_CALL_REENTRANT(result,args...) do { \ + struct ia64_fpreg __ia64_scs_fr[6]; \ + ia64_save_scratch_fpregs(__ia64_scs_fr); \ + preempt_disable(); \ + __SAL_CALL(result, args); \ + preempt_enable(); \ + ia64_load_scratch_fpregs(__ia64_scs_fr); \ +} while (0) + +#define SAL_SET_VECTORS 0x01000000 +#define SAL_GET_STATE_INFO 0x01000001 +#define SAL_GET_STATE_INFO_SIZE 0x01000002 +#define SAL_CLEAR_STATE_INFO 0x01000003 +#define SAL_MC_RENDEZ 0x01000004 +#define SAL_MC_SET_PARAMS 0x01000005 +#define SAL_REGISTER_PHYSICAL_ADDR 0x01000006 + +#define SAL_CACHE_FLUSH 0x01000008 +#define SAL_CACHE_INIT 0x01000009 +#define SAL_PCI_CONFIG_READ 0x01000010 +#define SAL_PCI_CONFIG_WRITE 0x01000011 +#define SAL_FREQ_BASE 0x01000012 +#define SAL_PHYSICAL_ID_INFO 0x01000013 + +#define SAL_UPDATE_PAL 0x01000020 + +struct ia64_sal_retval { + /* + * A zero status value indicates call completed without error. + * A negative status value indicates reason of call failure. + * A positive status value indicates success but an + * informational value should be printed (e.g., "reboot for + * change to take effect"). + */ + s64 status; + u64 v0; + u64 v1; + u64 v2; +}; + +typedef struct ia64_sal_retval (*ia64_sal_handler) (u64, ...); + +enum { + SAL_FREQ_BASE_PLATFORM = 0, + SAL_FREQ_BASE_INTERVAL_TIMER = 1, + SAL_FREQ_BASE_REALTIME_CLOCK = 2 +}; + +/* + * The SAL system table is followed by a variable number of variable + * length descriptors. The structure of these descriptors follows + * below. + * The defininition follows SAL specs from July 2000 + */ +struct ia64_sal_systab { + u8 signature[4]; /* should be "SST_" */ + u32 size; /* size of this table in bytes */ + u8 sal_rev_minor; + u8 sal_rev_major; + u16 entry_count; /* # of entries in variable portion */ + u8 checksum; + u8 reserved1[7]; + u8 sal_a_rev_minor; + u8 sal_a_rev_major; + u8 sal_b_rev_minor; + u8 sal_b_rev_major; + /* oem_id & product_id: terminating NUL is missing if string is exactly 32 bytes long. */ + u8 oem_id[32]; + u8 product_id[32]; /* ASCII product id */ + u8 reserved2[8]; +}; + +enum sal_systab_entry_type { + SAL_DESC_ENTRY_POINT = 0, + SAL_DESC_MEMORY = 1, + SAL_DESC_PLATFORM_FEATURE = 2, + SAL_DESC_TR = 3, + SAL_DESC_PTC = 4, + SAL_DESC_AP_WAKEUP = 5 +}; + +/* + * Entry type: Size: + * 0 48 + * 1 32 + * 2 16 + * 3 32 + * 4 16 + * 5 16 + */ +#define SAL_DESC_SIZE(type) "\060\040\020\040\020\020"[(unsigned) type] + +typedef struct ia64_sal_desc_entry_point { + u8 type; + u8 reserved1[7]; + u64 pal_proc; + u64 sal_proc; + u64 gp; + u8 reserved2[16]; +}ia64_sal_desc_entry_point_t; + +typedef struct ia64_sal_desc_memory { + u8 type; + u8 used_by_sal; /* needs to be mapped for SAL? */ + u8 mem_attr; /* current memory attribute setting */ + u8 access_rights; /* access rights set up by SAL */ + u8 mem_attr_mask; /* mask of supported memory attributes */ + u8 reserved1; + u8 mem_type; /* memory type */ + u8 mem_usage; /* memory usage */ + u64 addr; /* physical address of memory */ + u32 length; /* length (multiple of 4KB pages) */ + u32 reserved2; + u8 oem_reserved[8]; +} ia64_sal_desc_memory_t; + +typedef struct ia64_sal_desc_platform_feature { + u8 type; + u8 feature_mask; + u8 reserved1[14]; +} ia64_sal_desc_platform_feature_t; + +typedef struct ia64_sal_desc_tr { + u8 type; + u8 tr_type; /* 0 == instruction, 1 == data */ + u8 regnum; /* translation register number */ + u8 reserved1[5]; + u64 addr; /* virtual address of area covered */ + u64 page_size; /* encoded page size */ + u8 reserved2[8]; +} ia64_sal_desc_tr_t; + +typedef struct ia64_sal_desc_ptc { + u8 type; + u8 reserved1[3]; + u32 num_domains; /* # of coherence domains */ + u64 domain_info; /* physical address of domain info table */ +} ia64_sal_desc_ptc_t; + +typedef struct ia64_sal_ptc_domain_info { + u64 proc_count; /* number of processors in domain */ + u64 proc_list; /* physical address of LID array */ +} ia64_sal_ptc_domain_info_t; + +typedef struct ia64_sal_ptc_domain_proc_entry { + u64 id : 8; /* id of processor */ + u64 eid : 8; /* eid of processor */ +} ia64_sal_ptc_domain_proc_entry_t; + + +#define IA64_SAL_AP_EXTERNAL_INT 0 + +typedef struct ia64_sal_desc_ap_wakeup { + u8 type; + u8 mechanism; /* 0 == external interrupt */ + u8 reserved1[6]; + u64 vector; /* interrupt vector in range 0x10-0xff */ +} ia64_sal_desc_ap_wakeup_t ; + +extern ia64_sal_handler ia64_sal; +extern struct ia64_sal_desc_ptc *ia64_ptc_domain_info; + +extern unsigned short sal_revision; /* supported SAL spec revision */ +extern unsigned short sal_version; /* SAL version; OEM dependent */ +#define SAL_VERSION_CODE(major, minor) ((BIN2BCD(major) << 8) | BIN2BCD(minor)) + +extern const char *ia64_sal_strerror (long status); +extern void ia64_sal_init (struct ia64_sal_systab *sal_systab); + +/* SAL information type encodings */ +enum { + SAL_INFO_TYPE_MCA = 0, /* Machine check abort information */ + SAL_INFO_TYPE_INIT = 1, /* Init information */ + SAL_INFO_TYPE_CMC = 2, /* Corrected machine check information */ + SAL_INFO_TYPE_CPE = 3 /* Corrected platform error information */ +}; + +/* Encodings for machine check parameter types */ +enum { + SAL_MC_PARAM_RENDEZ_INT = 1, /* Rendezvous interrupt */ + SAL_MC_PARAM_RENDEZ_WAKEUP = 2, /* Wakeup */ + SAL_MC_PARAM_CPE_INT = 3 /* Corrected Platform Error Int */ +}; + +/* Encodings for rendezvous mechanisms */ +enum { + SAL_MC_PARAM_MECHANISM_INT = 1, /* Use interrupt */ + SAL_MC_PARAM_MECHANISM_MEM = 2 /* Use memory synchronization variable*/ +}; + +/* Encodings for vectors which can be registered by the OS with SAL */ +enum { + SAL_VECTOR_OS_MCA = 0, + SAL_VECTOR_OS_INIT = 1, + SAL_VECTOR_OS_BOOT_RENDEZ = 2 +}; + +/* Encodings for mca_opt parameter sent to SAL_MC_SET_PARAMS */ +#define SAL_MC_PARAM_RZ_ALWAYS 0x1 +#define SAL_MC_PARAM_BINIT_ESCALATE 0x10 + +/* + * Definition of the SAL Error Log from the SAL spec + */ + +/* SAL Error Record Section GUID Definitions */ +#define SAL_PROC_DEV_ERR_SECT_GUID \ + EFI_GUID(0xe429faf1, 0x3cb7, 0x11d4, 0xbc, 0xa7, 0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81) +#define SAL_PLAT_MEM_DEV_ERR_SECT_GUID \ + EFI_GUID(0xe429faf2, 0x3cb7, 0x11d4, 0xbc, 0xa7, 0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81) +#define SAL_PLAT_SEL_DEV_ERR_SECT_GUID \ + EFI_GUID(0xe429faf3, 0x3cb7, 0x11d4, 0xbc, 0xa7, 0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81) +#define SAL_PLAT_PCI_BUS_ERR_SECT_GUID \ + EFI_GUID(0xe429faf4, 0x3cb7, 0x11d4, 0xbc, 0xa7, 0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81) +#define SAL_PLAT_SMBIOS_DEV_ERR_SECT_GUID \ + EFI_GUID(0xe429faf5, 0x3cb7, 0x11d4, 0xbc, 0xa7, 0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81) +#define SAL_PLAT_PCI_COMP_ERR_SECT_GUID \ + EFI_GUID(0xe429faf6, 0x3cb7, 0x11d4, 0xbc, 0xa7, 0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81) +#define SAL_PLAT_SPECIFIC_ERR_SECT_GUID \ + EFI_GUID(0xe429faf7, 0x3cb7, 0x11d4, 0xbc, 0xa7, 0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81) +#define SAL_PLAT_HOST_CTLR_ERR_SECT_GUID \ + EFI_GUID(0xe429faf8, 0x3cb7, 0x11d4, 0xbc, 0xa7, 0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81) +#define SAL_PLAT_BUS_ERR_SECT_GUID \ + EFI_GUID(0xe429faf9, 0x3cb7, 0x11d4, 0xbc, 0xa7, 0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81) + +#define MAX_CACHE_ERRORS 6 +#define MAX_TLB_ERRORS 6 +#define MAX_BUS_ERRORS 1 + +/* Definition of version according to SAL spec for logging purposes */ +typedef struct sal_log_revision { + u8 minor; /* BCD (0..99) */ + u8 major; /* BCD (0..99) */ +} sal_log_revision_t; + +/* Definition of timestamp according to SAL spec for logging purposes */ +typedef struct sal_log_timestamp { + u8 slh_second; /* Second (0..59) */ + u8 slh_minute; /* Minute (0..59) */ + u8 slh_hour; /* Hour (0..23) */ + u8 slh_reserved; + u8 slh_day; /* Day (1..31) */ + u8 slh_month; /* Month (1..12) */ + u8 slh_year; /* Year (00..99) */ + u8 slh_century; /* Century (19, 20, 21, ...) */ +} sal_log_timestamp_t; + +/* Definition of log record header structures */ +typedef struct sal_log_record_header { + u64 id; /* Unique monotonically increasing ID */ + sal_log_revision_t revision; /* Major and Minor revision of header */ + u8 severity; /* Error Severity */ + u8 validation_bits; /* 0: platform_guid, 1: !timestamp */ + u32 len; /* Length of this error log in bytes */ + sal_log_timestamp_t timestamp; /* Timestamp */ + efi_guid_t platform_guid; /* Unique OEM Platform ID */ +} sal_log_record_header_t; + +#define sal_log_severity_recoverable 0 +#define sal_log_severity_fatal 1 +#define sal_log_severity_corrected 2 + +/* Definition of log section header structures */ +typedef struct sal_log_sec_header { + efi_guid_t guid; /* Unique Section ID */ + sal_log_revision_t revision; /* Major and Minor revision of Section */ + u16 reserved; + u32 len; /* Section length */ +} sal_log_section_hdr_t; + +typedef struct sal_log_mod_error_info { + struct { + u64 check_info : 1, + requestor_identifier : 1, + responder_identifier : 1, + target_identifier : 1, + precise_ip : 1, + reserved : 59; + } valid; + u64 check_info; + u64 requestor_identifier; + u64 responder_identifier; + u64 target_identifier; + u64 precise_ip; +} sal_log_mod_error_info_t; + +typedef struct sal_processor_static_info { + struct { + u64 minstate : 1, + br : 1, + cr : 1, + ar : 1, + rr : 1, + fr : 1, + reserved : 58; + } valid; + pal_min_state_area_t min_state_area; + u64 br[8]; + u64 cr[128]; + u64 ar[128]; + u64 rr[8]; + struct ia64_fpreg __attribute__ ((packed)) fr[128]; +} sal_processor_static_info_t; + +struct sal_cpuid_info { + u64 regs[5]; + u64 reserved; +}; + +typedef struct sal_log_processor_info { + sal_log_section_hdr_t header; + struct { + u64 proc_error_map : 1, + proc_state_param : 1, + proc_cr_lid : 1, + psi_static_struct : 1, + num_cache_check : 4, + num_tlb_check : 4, + num_bus_check : 4, + num_reg_file_check : 4, + num_ms_check : 4, + cpuid_info : 1, + reserved1 : 39; + } valid; + u64 proc_error_map; + u64 proc_state_parameter; + u64 proc_cr_lid; + /* + * The rest of this structure consists of variable-length arrays, which can't be + * expressed in C. + */ + sal_log_mod_error_info_t info[0]; + /* + * This is what the rest looked like if C supported variable-length arrays: + * + * sal_log_mod_error_info_t cache_check_info[.valid.num_cache_check]; + * sal_log_mod_error_info_t tlb_check_info[.valid.num_tlb_check]; + * sal_log_mod_error_info_t bus_check_info[.valid.num_bus_check]; + * sal_log_mod_error_info_t reg_file_check_info[.valid.num_reg_file_check]; + * sal_log_mod_error_info_t ms_check_info[.valid.num_ms_check]; + * struct sal_cpuid_info cpuid_info; + * sal_processor_static_info_t processor_static_info; + */ +} sal_log_processor_info_t; + +/* Given a sal_log_processor_info_t pointer, return a pointer to the processor_static_info: */ +#define SAL_LPI_PSI_INFO(l) \ +({ sal_log_processor_info_t *_l = (l); \ + ((sal_processor_static_info_t *) \ + ((char *) _l->info + ((_l->valid.num_cache_check + _l->valid.num_tlb_check \ + + _l->valid.num_bus_check + _l->valid.num_reg_file_check \ + + _l->valid.num_ms_check) * sizeof(sal_log_mod_error_info_t) \ + + sizeof(struct sal_cpuid_info)))); \ +}) + +/* platform error log structures */ + +typedef struct sal_log_mem_dev_err_info { + sal_log_section_hdr_t header; + struct { + u64 error_status : 1, + physical_addr : 1, + addr_mask : 1, + node : 1, + card : 1, + module : 1, + bank : 1, + device : 1, + row : 1, + column : 1, + bit_position : 1, + requestor_id : 1, + responder_id : 1, + target_id : 1, + bus_spec_data : 1, + oem_id : 1, + oem_data : 1, + reserved : 47; + } valid; + u64 error_status; + u64 physical_addr; + u64 addr_mask; + u16 node; + u16 card; + u16 module; + u16 bank; + u16 device; + u16 row; + u16 column; + u16 bit_position; + u64 requestor_id; + u64 responder_id; + u64 target_id; + u64 bus_spec_data; + u8 oem_id[16]; + u8 oem_data[1]; /* Variable length data */ +} sal_log_mem_dev_err_info_t; + +typedef struct sal_log_sel_dev_err_info { + sal_log_section_hdr_t header; + struct { + u64 record_id : 1, + record_type : 1, + generator_id : 1, + evm_rev : 1, + sensor_type : 1, + sensor_num : 1, + event_dir : 1, + event_data1 : 1, + event_data2 : 1, + event_data3 : 1, + reserved : 54; + } valid; + u16 record_id; + u8 record_type; + u8 timestamp[4]; + u16 generator_id; + u8 evm_rev; + u8 sensor_type; + u8 sensor_num; + u8 event_dir; + u8 event_data1; + u8 event_data2; + u8 event_data3; +} sal_log_sel_dev_err_info_t; + +typedef struct sal_log_pci_bus_err_info { + sal_log_section_hdr_t header; + struct { + u64 err_status : 1, + err_type : 1, + bus_id : 1, + bus_address : 1, + bus_data : 1, + bus_cmd : 1, + requestor_id : 1, + responder_id : 1, + target_id : 1, + oem_data : 1, + reserved : 54; + } valid; + u64 err_status; + u16 err_type; + u16 bus_id; + u32 reserved; + u64 bus_address; + u64 bus_data; + u64 bus_cmd; + u64 requestor_id; + u64 responder_id; + u64 target_id; + u8 oem_data[1]; /* Variable length data */ +} sal_log_pci_bus_err_info_t; + +typedef struct sal_log_smbios_dev_err_info { + sal_log_section_hdr_t header; + struct { + u64 event_type : 1, + length : 1, + time_stamp : 1, + data : 1, + reserved1 : 60; + } valid; + u8 event_type; + u8 length; + u8 time_stamp[6]; + u8 data[1]; /* data of variable length, length == slsmb_length */ +} sal_log_smbios_dev_err_info_t; + +typedef struct sal_log_pci_comp_err_info { + sal_log_section_hdr_t header; + struct { + u64 err_status : 1, + comp_info : 1, + num_mem_regs : 1, + num_io_regs : 1, + reg_data_pairs : 1, + oem_data : 1, + reserved : 58; + } valid; + u64 err_status; + struct { + u16 vendor_id; + u16 device_id; + u8 class_code[3]; + u8 func_num; + u8 dev_num; + u8 bus_num; + u8 seg_num; + u8 reserved[5]; + } comp_info; + u32 num_mem_regs; + u32 num_io_regs; + u64 reg_data_pairs[1]; + /* + * array of address/data register pairs is num_mem_regs + num_io_regs elements + * long. Each array element consists of a u64 address followed by a u64 data + * value. The oem_data array immediately follows the reg_data_pairs array + */ + u8 oem_data[1]; /* Variable length data */ +} sal_log_pci_comp_err_info_t; + +typedef struct sal_log_plat_specific_err_info { + sal_log_section_hdr_t header; + struct { + u64 err_status : 1, + guid : 1, + oem_data : 1, + reserved : 61; + } valid; + u64 err_status; + efi_guid_t guid; + u8 oem_data[1]; /* platform specific variable length data */ +} sal_log_plat_specific_err_info_t; + +typedef struct sal_log_host_ctlr_err_info { + sal_log_section_hdr_t header; + struct { + u64 err_status : 1, + requestor_id : 1, + responder_id : 1, + target_id : 1, + bus_spec_data : 1, + oem_data : 1, + reserved : 58; + } valid; + u64 err_status; + u64 requestor_id; + u64 responder_id; + u64 target_id; + u64 bus_spec_data; + u8 oem_data[1]; /* Variable length OEM data */ +} sal_log_host_ctlr_err_info_t; + +typedef struct sal_log_plat_bus_err_info { + sal_log_section_hdr_t header; + struct { + u64 err_status : 1, + requestor_id : 1, + responder_id : 1, + target_id : 1, + bus_spec_data : 1, + oem_data : 1, + reserved : 58; + } valid; + u64 err_status; + u64 requestor_id; + u64 responder_id; + u64 target_id; + u64 bus_spec_data; + u8 oem_data[1]; /* Variable length OEM data */ +} sal_log_plat_bus_err_info_t; + +/* Overall platform error section structure */ +typedef union sal_log_platform_err_info { + sal_log_mem_dev_err_info_t mem_dev_err; + sal_log_sel_dev_err_info_t sel_dev_err; + sal_log_pci_bus_err_info_t pci_bus_err; + sal_log_smbios_dev_err_info_t smbios_dev_err; + sal_log_pci_comp_err_info_t pci_comp_err; + sal_log_plat_specific_err_info_t plat_specific_err; + sal_log_host_ctlr_err_info_t host_ctlr_err; + sal_log_plat_bus_err_info_t plat_bus_err; +} sal_log_platform_err_info_t; + +/* SAL log over-all, multi-section error record structure (processor+platform) */ +typedef struct err_rec { + sal_log_record_header_t sal_elog_header; + sal_log_processor_info_t proc_err; + sal_log_platform_err_info_t plat_err; + u8 oem_data_pad[1024]; +} ia64_err_rec_t; + +/* + * Now define a couple of inline functions for improved type checking + * and convenience. + */ +static inline long +ia64_sal_freq_base (unsigned long which, unsigned long *ticks_per_second, + unsigned long *drift_info) +{ + struct ia64_sal_retval isrv; + + SAL_CALL(isrv, SAL_FREQ_BASE, which, 0, 0, 0, 0, 0, 0); + *ticks_per_second = isrv.v0; + *drift_info = isrv.v1; + return isrv.status; +} + +extern s64 ia64_sal_cache_flush (u64 cache_type); + +/* Initialize all the processor and platform level instruction and data caches */ +static inline s64 +ia64_sal_cache_init (void) +{ + struct ia64_sal_retval isrv; + SAL_CALL(isrv, SAL_CACHE_INIT, 0, 0, 0, 0, 0, 0, 0); + return isrv.status; +} + +/* + * Clear the processor and platform information logged by SAL with respect to the machine + * state at the time of MCA's, INITs, CMCs, or CPEs. + */ +static inline s64 +ia64_sal_clear_state_info (u64 sal_info_type) +{ + struct ia64_sal_retval isrv; + SAL_CALL_REENTRANT(isrv, SAL_CLEAR_STATE_INFO, sal_info_type, 0, + 0, 0, 0, 0, 0); + return isrv.status; +} + + +/* Get the processor and platform information logged by SAL with respect to the machine + * state at the time of the MCAs, INITs, CMCs, or CPEs. + */ +#ifdef CONFIG_XEN +static inline u64 ia64_sal_get_state_info_size (u64 sal_info_type); +#endif + +static inline u64 +ia64_sal_get_state_info (u64 sal_info_type, u64 *sal_info) +{ + struct ia64_sal_retval isrv; +#ifdef CONFIG_XEN + if (is_running_on_xen()) { + struct xencomm_handle *desc; + + if (xencomm_create(sal_info, + ia64_sal_get_state_info_size(sal_info_type), + &desc, GFP_KERNEL)) + return 0; + + SAL_CALL_REENTRANT(isrv, SAL_GET_STATE_INFO, sal_info_type, 0, + desc, 0, 0, 0, 0); + xencomm_free(desc); + } else +#endif + SAL_CALL_REENTRANT(isrv, SAL_GET_STATE_INFO, sal_info_type, 0, + sal_info, 0, 0, 0, 0); + if (isrv.status) + return 0; + + return isrv.v0; +} + +/* + * Get the maximum size of the information logged by SAL with respect to the machine state + * at the time of MCAs, INITs, CMCs, or CPEs. + */ +static inline u64 +ia64_sal_get_state_info_size (u64 sal_info_type) +{ + struct ia64_sal_retval isrv; + SAL_CALL_REENTRANT(isrv, SAL_GET_STATE_INFO_SIZE, sal_info_type, 0, + 0, 0, 0, 0, 0); + if (isrv.status) + return 0; + return isrv.v0; +} + +/* + * Causes the processor to go into a spin loop within SAL where SAL awaits a wakeup from + * the monarch processor. Must not lock, because it will not return on any cpu until the + * monarch processor sends a wake up. + */ +static inline s64 +ia64_sal_mc_rendez (void) +{ + struct ia64_sal_retval isrv; + SAL_CALL_NOLOCK(isrv, SAL_MC_RENDEZ, 0, 0, 0, 0, 0, 0, 0); + return isrv.status; +} + +/* + * Allow the OS to specify the interrupt number to be used by SAL to interrupt OS during + * the machine check rendezvous sequence as well as the mechanism to wake up the + * non-monarch processor at the end of machine check processing. + * Returns the complete ia64_sal_retval because some calls return more than just a status + * value. + */ +static inline struct ia64_sal_retval +ia64_sal_mc_set_params (u64 param_type, u64 i_or_m, u64 i_or_m_val, u64 timeout, u64 rz_always) +{ + struct ia64_sal_retval isrv; + SAL_CALL(isrv, SAL_MC_SET_PARAMS, param_type, i_or_m, i_or_m_val, + timeout, rz_always, 0, 0); + return isrv; +} + +/* Read from PCI configuration space */ +static inline s64 +ia64_sal_pci_config_read (u64 pci_config_addr, int type, u64 size, u64 *value) +{ + struct ia64_sal_retval isrv; + SAL_CALL(isrv, SAL_PCI_CONFIG_READ, pci_config_addr, size, type, 0, 0, 0, 0); + if (value) + *value = isrv.v0; + return isrv.status; +} + +/* Write to PCI configuration space */ +static inline s64 +ia64_sal_pci_config_write (u64 pci_config_addr, int type, u64 size, u64 value) +{ + struct ia64_sal_retval isrv; + SAL_CALL(isrv, SAL_PCI_CONFIG_WRITE, pci_config_addr, size, value, + type, 0, 0, 0); + return isrv.status; +} + +/* + * Register physical addresses of locations needed by SAL when SAL procedures are invoked + * in virtual mode. + */ +static inline s64 +ia64_sal_register_physical_addr (u64 phys_entry, u64 phys_addr) +{ + struct ia64_sal_retval isrv; + SAL_CALL(isrv, SAL_REGISTER_PHYSICAL_ADDR, phys_entry, phys_addr, + 0, 0, 0, 0, 0); + return isrv.status; +} + +/* + * Register software dependent code locations within SAL. These locations are handlers or + * entry points where SAL will pass control for the specified event. These event handlers + * are for the bott rendezvous, MCAs and INIT scenarios. + */ +static inline s64 +ia64_sal_set_vectors (u64 vector_type, + u64 handler_addr1, u64 gp1, u64 handler_len1, + u64 handler_addr2, u64 gp2, u64 handler_len2) +{ + struct ia64_sal_retval isrv; + SAL_CALL(isrv, SAL_SET_VECTORS, vector_type, + handler_addr1, gp1, handler_len1, + handler_addr2, gp2, handler_len2); + + return isrv.status; +} + +/* Update the contents of PAL block in the non-volatile storage device */ +static inline s64 +ia64_sal_update_pal (u64 param_buf, u64 scratch_buf, u64 scratch_buf_size, + u64 *error_code, u64 *scratch_buf_size_needed) +{ + struct ia64_sal_retval isrv; + SAL_CALL(isrv, SAL_UPDATE_PAL, param_buf, scratch_buf, scratch_buf_size, + 0, 0, 0, 0); + if (error_code) + *error_code = isrv.v0; + if (scratch_buf_size_needed) + *scratch_buf_size_needed = isrv.v1; + return isrv.status; +} + +/* Get physical processor die mapping in the platform. */ +static inline s64 +ia64_sal_physical_id_info(u16 *splid) +{ + struct ia64_sal_retval isrv; + SAL_CALL(isrv, SAL_PHYSICAL_ID_INFO, 0, 0, 0, 0, 0, 0, 0); + if (splid) + *splid = isrv.v0; + return isrv.status; +} + +extern unsigned long sal_platform_features; + +extern int (*salinfo_platform_oemdata)(const u8 *, u8 **, u64 *); + +struct sal_ret_values { + long r8; long r9; long r10; long r11; +}; + +#define IA64_SAL_OEMFUNC_MIN 0x02000000 +#define IA64_SAL_OEMFUNC_MAX 0x03ffffff + +extern int ia64_sal_oemcall(struct ia64_sal_retval *, u64, u64, u64, u64, u64, + u64, u64, u64); +extern int ia64_sal_oemcall_nolock(struct ia64_sal_retval *, u64, u64, u64, + u64, u64, u64, u64, u64); +extern int ia64_sal_oemcall_reentrant(struct ia64_sal_retval *, u64, u64, u64, + u64, u64, u64, u64, u64); +#ifdef CONFIG_HOTPLUG_CPU +/* + * System Abstraction Layer Specification + * Section 3.2.5.1: OS_BOOT_RENDEZ to SAL return State. + * Note: region regs are stored first in head.S _start. Hence they must + * stay up front. + */ +struct sal_to_os_boot { + u64 rr[8]; /* Region Registers */ + u64 br[6]; /* br0: return addr into SAL boot rendez routine */ + u64 gr1; /* SAL:GP */ + u64 gr12; /* SAL:SP */ + u64 gr13; /* SAL: Task Pointer */ + u64 fpsr; + u64 pfs; + u64 rnat; + u64 unat; + u64 bspstore; + u64 dcr; /* Default Control Register */ + u64 iva; + u64 pta; + u64 itv; + u64 pmv; + u64 cmcv; + u64 lrr[2]; + u64 gr[4]; + u64 pr; /* Predicate registers */ + u64 lc; /* Loop Count */ + struct ia64_fpreg fp[20]; +}; + +/* + * Global array allocated for NR_CPUS at boot time + */ +extern struct sal_to_os_boot sal_boot_rendez_state[NR_CPUS]; + +extern void ia64_jump_to_sal(struct sal_to_os_boot *); +#endif + +extern void ia64_sal_handler_init(void *entry_point, void *gpval); + +#endif /* __ASSEMBLY__ */ + +#endif /* _ASM_IA64_SAL_H */ diff --git a/linux-2.6-xen-sparse/include/asm-ia64/uaccess.h b/linux-2.6-xen-sparse/include/asm-ia64/uaccess.h new file mode 100644 index 0000000000..e015fd0d3b --- /dev/null +++ b/linux-2.6-xen-sparse/include/asm-ia64/uaccess.h @@ -0,0 +1,421 @@ +#ifndef _ASM_IA64_UACCESS_H +#define _ASM_IA64_UACCESS_H + +/* + * This file defines various macros to transfer memory areas across + * the user/kernel boundary. This needs to be done carefully because + * this code is executed in kernel mode and uses user-specified + * addresses. Thus, we need to be careful not to let the user to + * trick us into accessing kernel memory that would normally be + * inaccessible. This code is also fairly performance sensitive, + * so we want to spend as little time doing safety checks as + * possible. + * + * To make matters a bit more interesting, these macros sometimes also + * called from within the kernel itself, in which case the address + * validity check must be skipped. The get_fs() macro tells us what + * to do: if get_fs()==USER_DS, checking is performed, if + * get_fs()==KERNEL_DS, checking is bypassed. + * + * Note that even if the memory area specified by the user is in a + * valid address range, it is still possible that we'll get a page + * fault while accessing it. This is handled by filling out an + * exception handler fixup entry for each instruction that has the + * potential to fault. When such a fault occurs, the page fault + * handler checks to see whether the faulting instruction has a fixup + * associated and, if so, sets r8 to -EFAULT and clears r9 to 0 and + * then resumes execution at the continuation point. + * + * Based on . + * + * Copyright (C) 1998, 1999, 2001-2004 Hewlett-Packard Co + * David Mosberger-Tang + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * For historical reasons, the following macros are grossly misnamed: + */ +#define KERNEL_DS ((mm_segment_t) { ~0UL }) /* cf. access_ok() */ +#define USER_DS ((mm_segment_t) { TASK_SIZE-1 }) /* cf. access_ok() */ + +#define VERIFY_READ 0 +#define VERIFY_WRITE 1 + +#define get_ds() (KERNEL_DS) +#define get_fs() (current_thread_info()->addr_limit) +#define set_fs(x) (current_thread_info()->addr_limit = (x)) + +#define segment_eq(a, b) ((a).seg == (b).seg) + +/* + * When accessing user memory, we need to make sure the entire area really is in + * user-level space. In order to do this efficiently, we make sure that the page at + * address TASK_SIZE is never valid. We also need to make sure that the address doesn't + * point inside the virtually mapped linear page table. + */ +#define __access_ok(addr, size, segment) \ +({ \ + __chk_user_ptr(addr); \ + (likely((unsigned long) (addr) <= (segment).seg) \ + && ((segment).seg == KERNEL_DS.seg \ + || likely(REGION_OFFSET((unsigned long) (addr)) < RGN_MAP_LIMIT))); \ +}) +#define access_ok(type, addr, size) __access_ok((addr), (size), get_fs()) + +/* + * These are the main single-value transfer routines. They automatically + * use the right size if we just have the right pointer type. + * + * Careful to not + * (a) re-use the arguments for side effects (sizeof/typeof is ok) + * (b) require any knowledge of processes at this stage + */ +#define put_user(x, ptr) __put_user_check((__typeof__(*(ptr))) (x), (ptr), sizeof(*(ptr)), get_fs()) +#define get_user(x, ptr) __get_user_check((x), (ptr), sizeof(*(ptr)), get_fs()) + +/* + * The "__xxx" versions do not do address space checking, useful when + * doing multiple accesses to the same area (the programmer has to do the + * checks by hand with "access_ok()") + */ +#define __put_user(x, ptr) __put_user_nocheck((__typeof__(*(ptr))) (x), (ptr), sizeof(*(ptr))) +#define __get_user(x, ptr) __get_user_nocheck((x), (ptr), sizeof(*(ptr))) + +extern long __put_user_unaligned_unknown (void); + +#define __put_user_unaligned(x, ptr) \ +({ \ + long __ret; \ + switch (sizeof(*(ptr))) { \ + case 1: __ret = __put_user((x), (ptr)); break; \ + case 2: __ret = (__put_user((x), (u8 __user *)(ptr))) \ + | (__put_user((x) >> 8, ((u8 __user *)(ptr) + 1))); break; \ + case 4: __ret = (__put_user((x), (u16 __user *)(ptr))) \ + | (__put_user((x) >> 16, ((u16 __user *)(ptr) + 1))); break; \ + case 8: __ret = (__put_user((x), (u32 __user *)(ptr))) \ + | (__put_user((x) >> 32, ((u32 __user *)(ptr) + 1))); break; \ + default: __ret = __put_user_unaligned_unknown(); \ + } \ + __ret; \ +}) + +extern long __get_user_unaligned_unknown (void); + +#define __get_user_unaligned(x, ptr) \ +({ \ + long __ret; \ + switch (sizeof(*(ptr))) { \ + case 1: __ret = __get_user((x), (ptr)); break; \ + case 2: __ret = (__get_user((x), (u8 __user *)(ptr))) \ + | (__get_user((x) >> 8, ((u8 __user *)(ptr) + 1))); break; \ + case 4: __ret = (__get_user((x), (u16 __user *)(ptr))) \ + | (__get_user((x) >> 16, ((u16 __user *)(ptr) + 1))); break; \ + case 8: __ret = (__get_user((x), (u32 __user *)(ptr))) \ + | (__get_user((x) >> 32, ((u32 __user *)(ptr) + 1))); break; \ + default: __ret = __get_user_unaligned_unknown(); \ + } \ + __ret; \ +}) + +#ifdef ASM_SUPPORTED + struct __large_struct { unsigned long buf[100]; }; +# define __m(x) (*(struct __large_struct __user *)(x)) + +/* We need to declare the __ex_table section before we can use it in .xdata. */ +asm (".section \"__ex_table\", \"a\"\n\t.previous"); + +# define __get_user_size(val, addr, n, err) \ +do { \ + register long __gu_r8 asm ("r8") = 0; \ + register long __gu_r9 asm ("r9"); \ + asm ("\n[1:]\tld"#n" %0=%2%P2\t// %0 and %1 get overwritten by exception handler\n" \ + "\t.xdata4 \"__ex_table\", 1b-., 1f-.+4\n" \ + "[1:]" \ + : "=r"(__gu_r9), "=r"(__gu_r8) : "m"(__m(addr)), "1"(__gu_r8)); \ + (err) = __gu_r8; \ + (val) = __gu_r9; \ +} while (0) + +/* + * The "__put_user_size()" macro tells gcc it reads from memory instead of writing it. This + * is because they do not write to any memory gcc knows about, so there are no aliasing + * issues. + */ +# define __put_user_size(val, addr, n, err) \ +do { \ + register long __pu_r8 asm ("r8") = 0; \ + asm volatile ("\n[1:]\tst"#n" %1=%r2%P1\t// %0 gets overwritten by exception handler\n" \ + "\t.xdata4 \"__ex_table\", 1b-., 1f-.\n" \ + "[1:]" \ + : "=r"(__pu_r8) : "m"(__m(addr)), "rO"(val), "0"(__pu_r8)); \ + (err) = __pu_r8; \ +} while (0) + +#else /* !ASM_SUPPORTED */ +# define RELOC_TYPE 2 /* ip-rel */ +# define __get_user_size(val, addr, n, err) \ +do { \ + __ld_user("__ex_table", (unsigned long) addr, n, RELOC_TYPE); \ + (err) = ia64_getreg(_IA64_REG_R8); \ + (val) = ia64_getreg(_IA64_REG_R9); \ +} while (0) +# define __put_user_size(val, addr, n, err) \ +do { \ + __st_user("__ex_table", (unsigned long) addr, n, RELOC_TYPE, (unsigned long) (val)); \ + (err) = ia64_getreg(_IA64_REG_R8); \ +} while (0) +#endif /* !ASM_SUPPORTED */ + +extern void __get_user_unknown (void); + +/* + * Evaluating arguments X, PTR, SIZE, and SEGMENT may involve subroutine-calls, which + * could clobber r8 and r9 (among others). Thus, be careful not to evaluate it while + * using r8/r9. + */ +#define __do_get_user(check, x, ptr, size, segment) \ +({ \ + const __typeof__(*(ptr)) __user *__gu_ptr = (ptr); \ + __typeof__ (size) __gu_size = (size); \ + long __gu_err = -EFAULT; \ + unsigned long __gu_val = 0; \ + if (!check || __access_ok(__gu_ptr, size, segment)) \ + switch (__gu_size) { \ + case 1: __get_user_size(__gu_val, __gu_ptr, 1, __gu_err); break; \ + case 2: __get_user_size(__gu_val, __gu_ptr, 2, __gu_err); break; \ + case 4: __get_user_size(__gu_val, __gu_ptr, 4, __gu_err); break; \ + case 8: __get_user_size(__gu_val, __gu_ptr, 8, __gu_err); break; \ + default: __get_user_unknown(); break; \ + } \ + (x) = (__typeof__(*(__gu_ptr))) __gu_val; \ + __gu_err; \ +}) + +#define __get_user_nocheck(x, ptr, size) __do_get_user(0, x, ptr, size, KERNEL_DS) +#define __get_user_check(x, ptr, size, segment) __do_get_user(1, x, ptr, size, segment) + +extern void __put_user_unknown (void); + +/* + * Evaluating arguments X, PTR, SIZE, and SEGMENT may involve subroutine-calls, which + * could clobber r8 (among others). Thus, be careful not to evaluate them while using r8. + */ +#define __do_put_user(check, x, ptr, size, segment) \ +({ \ + __typeof__ (x) __pu_x = (x); \ + __typeof__ (*(ptr)) __user *__pu_ptr = (ptr); \ + __typeof__ (size) __pu_size = (size); \ + long __pu_err = -EFAULT; \ + \ + if (!check || __access_ok(__pu_ptr, __pu_size, segment)) \ + switch (__pu_size) { \ + case 1: __put_user_size(__pu_x, __pu_ptr, 1, __pu_err); break; \ + case 2: __put_user_size(__pu_x, __pu_ptr, 2, __pu_err); break; \ + case 4: __put_user_size(__pu_x, __pu_ptr, 4, __pu_err); break; \ + case 8: __put_user_size(__pu_x, __pu_ptr, 8, __pu_err); break; \ + default: __put_user_unknown(); break; \ + } \ + __pu_err; \ +}) + +#define __put_user_nocheck(x, ptr, size) __do_put_user(0, x, ptr, size, KERNEL_DS) +#define __put_user_check(x, ptr, size, segment) __do_put_user(1, x, ptr, size, segment) + +/* + * Complex access routines + */ +extern unsigned long __must_check __copy_user (void __user *to, const void __user *from, + unsigned long count); + +static inline unsigned long +__copy_to_user (void __user *to, const void *from, unsigned long count) +{ + return __copy_user(to, (__force void __user *) from, count); +} + +static inline unsigned long +__copy_from_user (void *to, const void __user *from, unsigned long count) +{ + return __copy_user((__force void __user *) to, from, count); +} + +#define __copy_to_user_inatomic __copy_to_user +#define __copy_from_user_inatomic __copy_from_user +#define copy_to_user(to, from, n) \ +({ \ + void __user *__cu_to = (to); \ + const void *__cu_from = (from); \ + long __cu_len = (n); \ + \ + if (__access_ok(__cu_to, __cu_len, get_fs())) \ + __cu_len = __copy_user(__cu_to, (__force void __user *) __cu_from, __cu_len); \ + __cu_len; \ +}) + +#define copy_from_user(to, from, n) \ +({ \ + void *__cu_to = (to); \ + const void __user *__cu_from = (from); \ + long __cu_len = (n); \ + \ + __chk_user_ptr(__cu_from); \ + if (__access_ok(__cu_from, __cu_len, get_fs())) \ + __cu_len = __copy_user((__force void __user *) __cu_to, __cu_from, __cu_len); \ + __cu_len; \ +}) + +#define __copy_in_user(to, from, size) __copy_user((to), (from), (size)) + +static inline unsigned long +copy_in_user (void __user *to, const void __user *from, unsigned long n) +{ + if (likely(access_ok(VERIFY_READ, from, n) && access_ok(VERIFY_WRITE, to, n))) + n = __copy_user(to, from, n); + return n; +} + +extern unsigned long __do_clear_user (void __user *, unsigned long); + +#define __clear_user(to, n) __do_clear_user(to, n) + +#define clear_user(to, n) \ +({ \ + unsigned long __cu_len = (n); \ + if (__access_ok(to, __cu_len, get_fs())) \ + __cu_len = __do_clear_user(to, __cu_len); \ + __cu_len; \ +}) + + +/* + * Returns: -EFAULT if exception before terminator, N if the entire buffer filled, else + * strlen. + */ +extern long __must_check __strncpy_from_user (char *to, const char __user *from, long to_len); + +#define strncpy_from_user(to, from, n) \ +({ \ + const char __user * __sfu_from = (from); \ + long __sfu_ret = -EFAULT; \ + if (__access_ok(__sfu_from, 0, get_fs())) \ + __sfu_ret = __strncpy_from_user((to), __sfu_from, (n)); \ + __sfu_ret; \ +}) + +/* Returns: 0 if bad, string length+1 (memory size) of string if ok */ +extern unsigned long __strlen_user (const char __user *); + +#define strlen_user(str) \ +({ \ + const char __user *__su_str = (str); \ + unsigned long __su_ret = 0; \ + if (__access_ok(__su_str, 0, get_fs())) \ + __su_ret = __strlen_user(__su_str); \ + __su_ret; \ +}) + +/* + * Returns: 0 if exception before NUL or reaching the supplied limit + * (N), a value greater than N if the limit would be exceeded, else + * strlen. + */ +extern unsigned long __strnlen_user (const char __user *, long); + +#define strnlen_user(str, len) \ +({ \ + const char __user *__su_str = (str); \ + unsigned long __su_ret = 0; \ + if (__access_ok(__su_str, 0, get_fs())) \ + __su_ret = __strnlen_user(__su_str, len); \ + __su_ret; \ +}) + +/* Generic code can't deal with the location-relative format that we use for compactness. */ +#define ARCH_HAS_SORT_EXTABLE +#define ARCH_HAS_SEARCH_EXTABLE + +struct exception_table_entry { + int addr; /* location-relative address of insn this fixup is for */ + int cont; /* location-relative continuation addr.; if bit 2 is set, r9 is set to 0 */ +}; + +extern void ia64_handle_exception (struct pt_regs *regs, const struct exception_table_entry *e); +extern const struct exception_table_entry *search_exception_tables (unsigned long addr); + +static inline int +ia64_done_with_exception (struct pt_regs *regs) +{ + const struct exception_table_entry *e; + e = search_exception_tables(regs->cr_iip + ia64_psr(regs)->ri); + if (e) { + ia64_handle_exception(regs, e); + return 1; + } + return 0; +} + +#define ARCH_HAS_TRANSLATE_MEM_PTR 1 +#ifndef CONFIG_XEN +static __inline__ char * +xlate_dev_mem_ptr (unsigned long p) +{ + struct page *page; + char * ptr; + + page = pfn_to_page(p >> PAGE_SHIFT); + if (PageUncached(page)) + ptr = (char *)p + __IA64_UNCACHED_OFFSET; + else + ptr = __va(p); + + return ptr; +} +#else +static __inline__ char * +xlate_dev_mem_ptr (unsigned long p, ssize_t sz) +{ + unsigned long pfn = p >> PAGE_SHIFT; + + if (pfn_valid(pfn) && !PageUncached(pfn_to_page(pfn))) + return __va(p); + + return ioremap(p, sz); +} + +static __inline__ void +xlate_dev_mem_ptr_unmap (char* v) +{ + if (REGION_NUMBER(v) == RGN_UNCACHED) + iounmap(v); +} +#endif + +/* + * Convert a virtual cached kernel memory pointer to an uncached pointer + */ +static __inline__ char * +xlate_dev_kmem_ptr (char * p) +{ + struct page *page; + char * ptr; + + page = virt_to_page((unsigned long)p >> PAGE_SHIFT); + if (PageUncached(page)) + ptr = (char *)__pa(p) + __IA64_UNCACHED_OFFSET; + else + ptr = p; + + return ptr; +} + +#endif /* _ASM_IA64_UACCESS_H */ diff --git a/linux-2.6-xen-sparse/include/asm-ia64/xen/privop.h b/linux-2.6-xen-sparse/include/asm-ia64/xen/privop.h index 6f3c20a8ed..a2615ca7e7 100644 --- a/linux-2.6-xen-sparse/include/asm-ia64/xen/privop.h +++ b/linux-2.6-xen-sparse/include/asm-ia64/xen/privop.h @@ -295,6 +295,7 @@ extern void xen_ptcga(unsigned long addr, unsigned long size); #define ia64_leave_kernel xen_leave_kernel #define ia64_leave_syscall xen_leave_syscall #define ia64_trace_syscall xen_trace_syscall +#define ia64_ret_from_clone xen_ret_from_clone #define ia64_switch_to xen_switch_to #define ia64_pal_call_static xen_pal_call_static diff --git a/linux-2.6-xen-sparse/include/asm-ia64/xen/xcom_hcall.h b/linux-2.6-xen-sparse/include/asm-ia64/xen/xcom_hcall.h index 3c073a71cd..79e790d75b 100644 --- a/linux-2.6-xen-sparse/include/asm-ia64/xen/xcom_hcall.h +++ b/linux-2.6-xen-sparse/include/asm-ia64/xen/xcom_hcall.h @@ -46,6 +46,11 @@ extern unsigned long xencomm_hypercall_hvm_op(int cmd, void *arg); extern int xencomm_hypercall_suspend(unsigned long srec); +extern int xencomm_hypercall_xenoprof_op(int op, void *arg); + +extern int xencomm_hypercall_perfmon_op(unsigned long cmd, void* arg, + unsigned long count); + /* Using mini xencomm. */ extern int xencomm_mini_hypercall_console_io(int cmd, int count, char *str); @@ -68,6 +73,11 @@ extern int xencomm_mini_hypercall_memory_op(unsigned int cmd, void *arg); extern unsigned long xencomm_mini_hypercall_hvm_op(int cmd, void *arg); +extern int xencomm_mini_hypercall_xenoprof_op(int op, void *arg); + +extern int xencomm_mini_hypercall_perfmon_op(unsigned long cmd, void* arg, + unsigned long count); + /* For privcmd. Locally declare argument type to avoid include storm. Type coherency will be checked within privcmd.c */ struct privcmd_hypercall; diff --git a/linux-2.6-xen-sparse/include/asm-ia64/xenoprof.h b/linux-2.6-xen-sparse/include/asm-ia64/xenoprof.h new file mode 100644 index 0000000000..b45c6ca602 --- /dev/null +++ b/linux-2.6-xen-sparse/include/asm-ia64/xenoprof.h @@ -0,0 +1,48 @@ +/****************************************************************************** + * asm-ia64/xenoprof.h + * + * Copyright (c) 2006 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __ASM_XENOPROF_H__ +#define __ASM_XENOPROF_H__ +#ifdef CONFIG_XEN + +#undef HAVE_XENOPROF_CREATE_FILES + +struct xenoprof_init; +void xenoprof_arch_init_counter(struct xenoprof_init *init); +void xenoprof_arch_counter(void); +void xenoprof_arch_start(void); +void xenoprof_arch_stop(void); + +struct xenoprof_arch_shared_buffer { + struct resource* res; +}; + +struct xenoprof_shared_buffer; +void xenoprof_arch_unmap_shared_buffer(struct xenoprof_shared_buffer* sbuf); +struct xenoprof_get_buffer; +int xenoprof_arch_map_shared_buffer(struct xenoprof_get_buffer* get_buffer, + struct xenoprof_shared_buffer* sbuf); +struct xenoprof_passive; +int xenoprof_arch_set_passive(struct xenoprof_passive* pdomain, + struct xenoprof_shared_buffer* sbuf); + +#endif /* CONFIG_XEN */ +#endif /* __ASM_XENOPROF_H__ */ diff --git a/linux-2.6-xen-sparse/include/asm-x86_64/kexec.h b/linux-2.6-xen-sparse/include/asm-x86_64/kexec.h new file mode 100644 index 0000000000..3e112d941b --- /dev/null +++ b/linux-2.6-xen-sparse/include/asm-x86_64/kexec.h @@ -0,0 +1,109 @@ +#ifndef _X86_64_KEXEC_H +#define _X86_64_KEXEC_H + +#define PA_CONTROL_PAGE 0 +#define VA_CONTROL_PAGE 1 +#define PA_PGD 2 +#define VA_PGD 3 +#define PA_PUD_0 4 +#define VA_PUD_0 5 +#define PA_PMD_0 6 +#define VA_PMD_0 7 +#define PA_PTE_0 8 +#define VA_PTE_0 9 +#define PA_PUD_1 10 +#define VA_PUD_1 11 +#define PA_PMD_1 12 +#define VA_PMD_1 13 +#define PA_PTE_1 14 +#define VA_PTE_1 15 +#define PA_TABLE_PAGE 16 +#define PAGES_NR 17 + +#ifndef __ASSEMBLY__ + +#include + +#include +#include + +/* + * KEXEC_SOURCE_MEMORY_LIMIT maximum page get_free_page can return. + * I.e. Maximum page that is mapped directly into kernel memory, + * and kmap is not required. + * + * So far x86_64 is limited to 40 physical address bits. + */ + +/* Maximum physical address we can use pages from */ +#define KEXEC_SOURCE_MEMORY_LIMIT (0xFFFFFFFFFFUL) +/* Maximum address we can reach in physical address mode */ +#define KEXEC_DESTINATION_MEMORY_LIMIT (0xFFFFFFFFFFUL) +/* Maximum address we can use for the control pages */ +#define KEXEC_CONTROL_MEMORY_LIMIT (0xFFFFFFFFFFUL) + +/* Allocate one page for the pdp and the second for the code */ +#define KEXEC_CONTROL_CODE_SIZE (4096UL + 4096UL) + +/* The native architecture */ +#define KEXEC_ARCH KEXEC_ARCH_X86_64 + +#define MAX_NOTE_BYTES 1024 + +/* + * Saving the registers of the cpu on which panic occured in + * crash_kexec to save a valid sp. The registers of other cpus + * will be saved in machine_crash_shutdown while shooting down them. + */ + +static inline void crash_setup_regs(struct pt_regs *newregs, + struct pt_regs *oldregs) +{ + if (oldregs) + memcpy(newregs, oldregs, sizeof(*newregs)); + else { + __asm__ __volatile__("movq %%rbx,%0" : "=m"(newregs->rbx)); + __asm__ __volatile__("movq %%rcx,%0" : "=m"(newregs->rcx)); + __asm__ __volatile__("movq %%rdx,%0" : "=m"(newregs->rdx)); + __asm__ __volatile__("movq %%rsi,%0" : "=m"(newregs->rsi)); + __asm__ __volatile__("movq %%rdi,%0" : "=m"(newregs->rdi)); + __asm__ __volatile__("movq %%rbp,%0" : "=m"(newregs->rbp)); + __asm__ __volatile__("movq %%rax,%0" : "=m"(newregs->rax)); + __asm__ __volatile__("movq %%rsp,%0" : "=m"(newregs->rsp)); + __asm__ __volatile__("movq %%r8,%0" : "=m"(newregs->r8)); + __asm__ __volatile__("movq %%r9,%0" : "=m"(newregs->r9)); + __asm__ __volatile__("movq %%r10,%0" : "=m"(newregs->r10)); + __asm__ __volatile__("movq %%r11,%0" : "=m"(newregs->r11)); + __asm__ __volatile__("movq %%r12,%0" : "=m"(newregs->r12)); + __asm__ __volatile__("movq %%r13,%0" : "=m"(newregs->r13)); + __asm__ __volatile__("movq %%r14,%0" : "=m"(newregs->r14)); + __asm__ __volatile__("movq %%r15,%0" : "=m"(newregs->r15)); + __asm__ __volatile__("movl %%ss, %%eax;" :"=a"(newregs->ss)); + __asm__ __volatile__("movl %%cs, %%eax;" :"=a"(newregs->cs)); + __asm__ __volatile__("pushfq; popq %0" :"=m"(newregs->eflags)); + + newregs->rip = (unsigned long)current_text_addr(); + } +} + +NORET_TYPE void +relocate_kernel(unsigned long indirection_page, + unsigned long page_list, + unsigned long start_address) ATTRIB_NORET; + +/* Under Xen we need to work with machine addresses. These macros give the + * machine address of a certain page to the generic kexec code instead of + * the pseudo physical address which would be given by the default macros. + */ + +#ifdef CONFIG_XEN +#define KEXEC_ARCH_HAS_PAGE_MACROS +#define kexec_page_to_pfn(page) pfn_to_mfn(page_to_pfn(page)) +#define kexec_pfn_to_page(pfn) pfn_to_page(mfn_to_pfn(pfn)) +#define kexec_virt_to_phys(addr) virt_to_machine(addr) +#define kexec_phys_to_virt(addr) phys_to_virt(machine_to_phys(addr)) +#endif + +#endif /* __ASSEMBLY__ */ + +#endif /* _X86_64_KEXEC_H */ diff --git a/linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/agp.h b/linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/agp.h new file mode 100644 index 0000000000..60c12a8b71 --- /dev/null +++ b/linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/agp.h @@ -0,0 +1,35 @@ +#ifndef AGP_H +#define AGP_H 1 + +#include +#include + +/* + * Functions to keep the agpgart mappings coherent. + * The GART gives the CPU a physical alias of memory. The alias is + * mapped uncacheable. Make sure there are no conflicting mappings + * with different cachability attributes for the same page. + */ + +int map_page_into_agp(struct page *page); +int unmap_page_from_agp(struct page *page); +#define flush_agp_mappings() global_flush_tlb() + +/* Could use CLFLUSH here if the cpu supports it. But then it would + need to be called for each cacheline of the whole page so it may not be + worth it. Would need a page for it. */ +#define flush_agp_cache() wbinvd() + +/* Convert a physical address to an address suitable for the GART. */ +#define phys_to_gart(x) phys_to_machine(x) +#define gart_to_phys(x) machine_to_phys(x) + +/* GATT allocation. Returns/accepts GATT kernel virtual address. */ +#define alloc_gatt_pages(order) ({ \ + char *_t; dma_addr_t _d; \ + _t = dma_alloc_coherent(NULL,PAGE_SIZE<<(order),&_d,GFP_KERNEL); \ + _t; }) +#define free_gatt_pages(table, order) \ + dma_free_coherent(NULL,PAGE_SIZE<<(order),(table),virt_to_bus(table)) + +#endif diff --git a/linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/hypercall.h b/linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/hypercall.h index 956a4c4b0d..5c6c8deff6 100644 --- a/linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/hypercall.h +++ b/linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/hypercall.h @@ -396,4 +396,11 @@ HYPERVISOR_xenoprof_op( return _hypercall2(int, xenoprof_op, op, arg); } +static inline int +HYPERVISOR_kexec_op( + unsigned long op, void *args) +{ + return _hypercall2(int, kexec_op, op, args); +} + #endif /* __HYPERCALL_H__ */ diff --git a/linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/ptrace.h b/linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/ptrace.h index 052f05bca8..e6801fa7af 100644 --- a/linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/ptrace.h +++ b/linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/ptrace.h @@ -90,6 +90,8 @@ extern unsigned long profile_pc(struct pt_regs *regs); #define profile_pc(regs) instruction_pointer(regs) #endif +#include + void signal_fault(struct pt_regs *regs, void __user *frame, char *where); struct task_struct; diff --git a/linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/xenoprof.h b/linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/xenoprof.h new file mode 100644 index 0000000000..47b3650a06 --- /dev/null +++ b/linux-2.6-xen-sparse/include/asm-x86_64/mach-xen/asm/xenoprof.h @@ -0,0 +1 @@ +#include diff --git a/linux-2.6-xen-sparse/include/linux/kexec.h b/linux-2.6-xen-sparse/include/linux/kexec.h new file mode 100644 index 0000000000..8f4fccf63c --- /dev/null +++ b/linux-2.6-xen-sparse/include/linux/kexec.h @@ -0,0 +1,152 @@ +#ifndef LINUX_KEXEC_H +#define LINUX_KEXEC_H + +#ifdef CONFIG_KEXEC +#include +#include +#include +#include +#include +#include + +/* Verify architecture specific macros are defined */ + +#ifndef KEXEC_SOURCE_MEMORY_LIMIT +#error KEXEC_SOURCE_MEMORY_LIMIT not defined +#endif + +#ifndef KEXEC_DESTINATION_MEMORY_LIMIT +#error KEXEC_DESTINATION_MEMORY_LIMIT not defined +#endif + +#ifndef KEXEC_CONTROL_MEMORY_LIMIT +#error KEXEC_CONTROL_MEMORY_LIMIT not defined +#endif + +#ifndef KEXEC_CONTROL_CODE_SIZE +#error KEXEC_CONTROL_CODE_SIZE not defined +#endif + +#ifndef KEXEC_ARCH +#error KEXEC_ARCH not defined +#endif + +#ifndef KEXEC_ARCH_HAS_PAGE_MACROS +#define kexec_page_to_pfn(page) page_to_pfn(page) +#define kexec_pfn_to_page(pfn) pfn_to_page(pfn) +#define kexec_virt_to_phys(addr) virt_to_phys(addr) +#define kexec_phys_to_virt(addr) phys_to_virt(addr) +#endif + +/* + * This structure is used to hold the arguments that are used when loading + * kernel binaries. + */ + +typedef unsigned long kimage_entry_t; +#define IND_DESTINATION 0x1 +#define IND_INDIRECTION 0x2 +#define IND_DONE 0x4 +#define IND_SOURCE 0x8 + +#define KEXEC_SEGMENT_MAX 16 +struct kexec_segment { + void __user *buf; + size_t bufsz; + unsigned long mem; /* User space sees this as a (void *) ... */ + size_t memsz; +}; + +#ifdef CONFIG_COMPAT +struct compat_kexec_segment { + compat_uptr_t buf; + compat_size_t bufsz; + compat_ulong_t mem; /* User space sees this as a (void *) ... */ + compat_size_t memsz; +}; +#endif + +struct kimage { + kimage_entry_t head; + kimage_entry_t *entry; + kimage_entry_t *last_entry; + + unsigned long destination; + + unsigned long start; + struct page *control_code_page; + + unsigned long nr_segments; + struct kexec_segment segment[KEXEC_SEGMENT_MAX]; + + struct list_head control_pages; + struct list_head dest_pages; + struct list_head unuseable_pages; + + /* Address of next control page to allocate for crash kernels. */ + unsigned long control_page; + + /* Flags to indicate special processing */ + unsigned int type : 1; +#define KEXEC_TYPE_DEFAULT 0 +#define KEXEC_TYPE_CRASH 1 +}; + + + +/* kexec interface functions */ +extern NORET_TYPE void machine_kexec(struct kimage *image) ATTRIB_NORET; +extern int machine_kexec_prepare(struct kimage *image); +extern void machine_kexec_cleanup(struct kimage *image); +#ifdef CONFIG_XEN +extern int xen_machine_kexec_load(struct kimage *image); +extern void xen_machine_kexec_unload(struct kimage *image); +extern void xen_machine_kexec_setup_resources(void); +extern void xen_machine_kexec_register_resources(struct resource *res); +#endif +extern asmlinkage long sys_kexec_load(unsigned long entry, + unsigned long nr_segments, + struct kexec_segment __user *segments, + unsigned long flags); +#ifdef CONFIG_COMPAT +extern asmlinkage long compat_sys_kexec_load(unsigned long entry, + unsigned long nr_segments, + struct compat_kexec_segment __user *segments, + unsigned long flags); +#endif +extern struct page *kimage_alloc_control_pages(struct kimage *image, + unsigned int order); +extern void crash_kexec(struct pt_regs *); +int kexec_should_crash(struct task_struct *); +extern struct kimage *kexec_image; + +#define KEXEC_ON_CRASH 0x00000001 +#define KEXEC_ARCH_MASK 0xffff0000 + +/* These values match the ELF architecture values. + * Unless there is a good reason that should continue to be the case. + */ +#define KEXEC_ARCH_DEFAULT ( 0 << 16) +#define KEXEC_ARCH_386 ( 3 << 16) +#define KEXEC_ARCH_X86_64 (62 << 16) +#define KEXEC_ARCH_PPC (20 << 16) +#define KEXEC_ARCH_PPC64 (21 << 16) +#define KEXEC_ARCH_IA_64 (50 << 16) +#define KEXEC_ARCH_S390 (22 << 16) +#define KEXEC_ARCH_SH (42 << 16) + +#define KEXEC_FLAGS (KEXEC_ON_CRASH) /* List of defined/legal kexec flags */ + +/* Location of a reserved region to hold the crash kernel. + */ +extern struct resource crashk_res; +typedef u32 note_buf_t[MAX_NOTE_BYTES/4]; +extern note_buf_t *crash_notes; + +#else /* !CONFIG_KEXEC */ +struct pt_regs; +struct task_struct; +static inline void crash_kexec(struct pt_regs *regs) { } +static inline int kexec_should_crash(struct task_struct *p) { return 0; } +#endif /* CONFIG_KEXEC */ +#endif /* LINUX_KEXEC_H */ diff --git a/linux-2.6-xen-sparse/include/xen/xencons.h b/linux-2.6-xen-sparse/include/xen/xencons.h index ae873746aa..9f280c5e0b 100644 --- a/linux-2.6-xen-sparse/include/xen/xencons.h +++ b/linux-2.6-xen-sparse/include/xen/xencons.h @@ -14,4 +14,6 @@ void xencons_tx(void); int xencons_ring_init(void); int xencons_ring_send(const char *data, unsigned len); +void xencons_early_setup(void); + #endif /* __ASM_XENCONS_H__ */ diff --git a/linux-2.6-xen-sparse/kernel/kexec.c b/linux-2.6-xen-sparse/kernel/kexec.c new file mode 100644 index 0000000000..9a1dbb6ae3 --- /dev/null +++ b/linux-2.6-xen-sparse/kernel/kexec.c @@ -0,0 +1,1105 @@ +/* + * kexec.c - kexec system call + * Copyright (C) 2002-2004 Eric Biederman + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* Per cpu memory for storing cpu states in case of system crash. */ +note_buf_t* crash_notes; + +/* Location of the reserved area for the crash kernel */ +struct resource crashk_res = { + .name = "Crash kernel", + .start = 0, + .end = 0, + .flags = IORESOURCE_BUSY | IORESOURCE_MEM +}; + +int kexec_should_crash(struct task_struct *p) +{ + if (in_interrupt() || !p->pid || p->pid == 1 || panic_on_oops) + return 1; + return 0; +} + +/* + * When kexec transitions to the new kernel there is a one-to-one + * mapping between physical and virtual addresses. On processors + * where you can disable the MMU this is trivial, and easy. For + * others it is still a simple predictable page table to setup. + * + * In that environment kexec copies the new kernel to its final + * resting place. This means I can only support memory whose + * physical address can fit in an unsigned long. In particular + * addresses where (pfn << PAGE_SHIFT) > ULONG_MAX cannot be handled. + * If the assembly stub has more restrictive requirements + * KEXEC_SOURCE_MEMORY_LIMIT and KEXEC_DEST_MEMORY_LIMIT can be + * defined more restrictively in . + * + * The code for the transition from the current kernel to the + * the new kernel is placed in the control_code_buffer, whose size + * is given by KEXEC_CONTROL_CODE_SIZE. In the best case only a single + * page of memory is necessary, but some architectures require more. + * Because this memory must be identity mapped in the transition from + * virtual to physical addresses it must live in the range + * 0 - TASK_SIZE, as only the user space mappings are arbitrarily + * modifiable. + * + * The assembly stub in the control code buffer is passed a linked list + * of descriptor pages detailing the source pages of the new kernel, + * and the destination addresses of those source pages. As this data + * structure is not used in the context of the current OS, it must + * be self-contained. + * + * The code has been made to work with highmem pages and will use a + * destination page in its final resting place (if it happens + * to allocate it). The end product of this is that most of the + * physical address space, and most of RAM can be used. + * + * Future directions include: + * - allocating a page table with the control code buffer identity + * mapped, to simplify machine_kexec and make kexec_on_panic more + * reliable. + */ + +/* + * KIMAGE_NO_DEST is an impossible destination address..., for + * allocating pages whose destination address we do not care about. + */ +#define KIMAGE_NO_DEST (-1UL) + +static int kimage_is_destination_range(struct kimage *image, + unsigned long start, unsigned long end); +static struct page *kimage_alloc_page(struct kimage *image, + gfp_t gfp_mask, + unsigned long dest); + +static int do_kimage_alloc(struct kimage **rimage, unsigned long entry, + unsigned long nr_segments, + struct kexec_segment __user *segments) +{ + size_t segment_bytes; + struct kimage *image; + unsigned long i; + int result; + + /* Allocate a controlling structure */ + result = -ENOMEM; + image = kmalloc(sizeof(*image), GFP_KERNEL); + if (!image) + goto out; + + memset(image, 0, sizeof(*image)); + image->head = 0; + image->entry = &image->head; + image->last_entry = &image->head; + image->control_page = ~0; /* By default this does not apply */ + image->start = entry; + image->type = KEXEC_TYPE_DEFAULT; + + /* Initialize the list of control pages */ + INIT_LIST_HEAD(&image->control_pages); + + /* Initialize the list of destination pages */ + INIT_LIST_HEAD(&image->dest_pages); + + /* Initialize the list of unuseable pages */ + INIT_LIST_HEAD(&image->unuseable_pages); + + /* Read in the segments */ + image->nr_segments = nr_segments; + segment_bytes = nr_segments * sizeof(*segments); + result = copy_from_user(image->segment, segments, segment_bytes); + if (result) + goto out; + + /* + * Verify we have good destination addresses. The caller is + * responsible for making certain we don't attempt to load + * the new image into invalid or reserved areas of RAM. This + * just verifies it is an address we can use. + * + * Since the kernel does everything in page size chunks ensure + * the destination addreses are page aligned. Too many + * special cases crop of when we don't do this. The most + * insidious is getting overlapping destination addresses + * simply because addresses are changed to page size + * granularity. + */ + result = -EADDRNOTAVAIL; + for (i = 0; i < nr_segments; i++) { + unsigned long mstart, mend; + + mstart = image->segment[i].mem; + mend = mstart + image->segment[i].memsz; + if ((mstart & ~PAGE_MASK) || (mend & ~PAGE_MASK)) + goto out; + if (mend >= KEXEC_DESTINATION_MEMORY_LIMIT) + goto out; + } + + /* Verify our destination addresses do not overlap. + * If we alloed overlapping destination addresses + * through very weird things can happen with no + * easy explanation as one segment stops on another. + */ + result = -EINVAL; + for (i = 0; i < nr_segments; i++) { + unsigned long mstart, mend; + unsigned long j; + + mstart = image->segment[i].mem; + mend = mstart + image->segment[i].memsz; + for (j = 0; j < i; j++) { + unsigned long pstart, pend; + pstart = image->segment[j].mem; + pend = pstart + image->segment[j].memsz; + /* Do the segments overlap ? */ + if ((mend > pstart) && (mstart < pend)) + goto out; + } + } + + /* Ensure our buffer sizes are strictly less than + * our memory sizes. This should always be the case, + * and it is easier to check up front than to be surprised + * later on. + */ + result = -EINVAL; + for (i = 0; i < nr_segments; i++) { + if (image->segment[i].bufsz > image->segment[i].memsz) + goto out; + } + + result = 0; +out: + if (result == 0) + *rimage = image; + else + kfree(image); + + return result; + +} + +static int kimage_normal_alloc(struct kimage **rimage, unsigned long entry, + unsigned long nr_segments, + struct kexec_segment __user *segments) +{ + int result; + struct kimage *image; + + /* Allocate and initialize a controlling structure */ + image = NULL; + result = do_kimage_alloc(&image, entry, nr_segments, segments); + if (result) + goto out; + + *rimage = image; + + /* + * Find a location for the control code buffer, and add it + * the vector of segments so that it's pages will also be + * counted as destination pages. + */ + result = -ENOMEM; + image->control_code_page = kimage_alloc_control_pages(image, + get_order(KEXEC_CONTROL_CODE_SIZE)); + if (!image->control_code_page) { + printk(KERN_ERR "Could not allocate control_code_buffer\n"); + goto out; + } + + result = 0; + out: + if (result == 0) + *rimage = image; + else + kfree(image); + + return result; +} + +static int kimage_crash_alloc(struct kimage **rimage, unsigned long entry, + unsigned long nr_segments, + struct kexec_segment __user *segments) +{ + int result; + struct kimage *image; + unsigned long i; + + image = NULL; + /* Verify we have a valid entry point */ + if ((entry < crashk_res.start) || (entry > crashk_res.end)) { + result = -EADDRNOTAVAIL; + goto out; + } + + /* Allocate and initialize a controlling structure */ + result = do_kimage_alloc(&image, entry, nr_segments, segments); + if (result) + goto out; + + /* Enable the special crash kernel control page + * allocation policy. + */ + image->control_page = crashk_res.start; + image->type = KEXEC_TYPE_CRASH; + + /* + * Verify we have good destination addresses. Normally + * the caller is responsible for making certain we don't + * attempt to load the new image into invalid or reserved + * areas of RAM. But crash kernels are preloaded into a + * reserved area of ram. We must ensure the addresses + * are in the reserved area otherwise preloading the + * kernel could corrupt things. + */ + result = -EADDRNOTAVAIL; + for (i = 0; i < nr_segments; i++) { + unsigned long mstart, mend; + + mstart = image->segment[i].mem; + mend = mstart + image->segment[i].memsz - 1; + /* Ensure we are within the crash kernel limits */ + if ((mstart < crashk_res.start) || (mend > crashk_res.end)) + goto out; + } + + /* + * Find a location for the control code buffer, and add + * the vector of segments so that it's pages will also be + * counted as destination pages. + */ + result = -ENOMEM; + image->control_code_page = kimage_alloc_control_pages(image, + get_order(KEXEC_CONTROL_CODE_SIZE)); + if (!image->control_code_page) { + printk(KERN_ERR "Could not allocate control_code_buffer\n"); + goto out; + } + + result = 0; +out: + if (result == 0) + *rimage = image; + else + kfree(image); + + return result; +} + +static int kimage_is_destination_range(struct kimage *image, + unsigned long start, + unsigned long end) +{ + unsigned long i; + + for (i = 0; i < image->nr_segments; i++) { + unsigned long mstart, mend; + + mstart = image->segment[i].mem; + mend = mstart + image->segment[i].memsz; + if ((end > mstart) && (start < mend)) + return 1; + } + + return 0; +} + +static struct page *kimage_alloc_pages(gfp_t gfp_mask, unsigned int order) +{ + struct page *pages; + + pages = alloc_pages(gfp_mask, order); + if (pages) { + unsigned int count, i; + pages->mapping = NULL; + set_page_private(pages, order); + count = 1 << order; + for (i = 0; i < count; i++) + SetPageReserved(pages + i); + } + + return pages; +} + +static void kimage_free_pages(struct page *page) +{ + unsigned int order, count, i; + + order = page_private(page); + count = 1 << order; + for (i = 0; i < count; i++) + ClearPageReserved(page + i); + __free_pages(page, order); +} + +static void kimage_free_page_list(struct list_head *list) +{ + struct list_head *pos, *next; + + list_for_each_safe(pos, next, list) { + struct page *page; + + page = list_entry(pos, struct page, lru); + list_del(&page->lru); + kimage_free_pages(page); + } +} + +static struct page *kimage_alloc_normal_control_pages(struct kimage *image, + unsigned int order) +{ + /* Control pages are special, they are the intermediaries + * that are needed while we copy the rest of the pages + * to their final resting place. As such they must + * not conflict with either the destination addresses + * or memory the kernel is already using. + * + * The only case where we really need more than one of + * these are for architectures where we cannot disable + * the MMU and must instead generate an identity mapped + * page table for all of the memory. + * + * At worst this runs in O(N) of the image size. + */ + struct list_head extra_pages; + struct page *pages; + unsigned int count; + + count = 1 << order; + INIT_LIST_HEAD(&extra_pages); + + /* Loop while I can allocate a page and the page allocated + * is a destination page. + */ + do { + unsigned long pfn, epfn, addr, eaddr; + + pages = kimage_alloc_pages(GFP_KERNEL, order); + if (!pages) + break; + pfn = kexec_page_to_pfn(pages); + epfn = pfn + count; + addr = pfn << PAGE_SHIFT; + eaddr = epfn << PAGE_SHIFT; + if ((epfn >= (KEXEC_CONTROL_MEMORY_LIMIT >> PAGE_SHIFT)) || + kimage_is_destination_range(image, addr, eaddr)) { + list_add(&pages->lru, &extra_pages); + pages = NULL; + } + } while (!pages); + + if (pages) { + /* Remember the allocated page... */ + list_add(&pages->lru, &image->control_pages); + + /* Because the page is already in it's destination + * location we will never allocate another page at + * that address. Therefore kimage_alloc_pages + * will not return it (again) and we don't need + * to give it an entry in image->segment[]. + */ + } + /* Deal with the destination pages I have inadvertently allocated. + * + * Ideally I would convert multi-page allocations into single + * page allocations, and add everyting to image->dest_pages. + * + * For now it is simpler to just free the pages. + */ + kimage_free_page_list(&extra_pages); + + return pages; +} + +#ifndef CONFIG_XEN +static struct page *kimage_alloc_crash_control_pages(struct kimage *image, + unsigned int order) +{ + /* Control pages are special, they are the intermediaries + * that are needed while we copy the rest of the pages + * to their final resting place. As such they must + * not conflict with either the destination addresses + * or memory the kernel is already using. + * + * Control pages are also the only pags we must allocate + * when loading a crash kernel. All of the other pages + * are specified by the segments and we just memcpy + * into them directly. + * + * The only case where we really need more than one of + * these are for architectures where we cannot disable + * the MMU and must instead generate an identity mapped + * page table for all of the memory. + * + * Given the low demand this implements a very simple + * allocator that finds the first hole of the appropriate + * size in the reserved memory region, and allocates all + * of the memory up to and including the hole. + */ + unsigned long hole_start, hole_end, size; + struct page *pages; + + pages = NULL; + size = (1 << order) << PAGE_SHIFT; + hole_start = (image->control_page + (size - 1)) & ~(size - 1); + hole_end = hole_start + size - 1; + while (hole_end <= crashk_res.end) { + unsigned long i; + + if (hole_end > KEXEC_CONTROL_MEMORY_LIMIT) + break; + if (hole_end > crashk_res.end) + break; + /* See if I overlap any of the segments */ + for (i = 0; i < image->nr_segments; i++) { + unsigned long mstart, mend; + + mstart = image->segment[i].mem; + mend = mstart + image->segment[i].memsz - 1; + if ((hole_end >= mstart) && (hole_start <= mend)) { + /* Advance the hole to the end of the segment */ + hole_start = (mend + (size - 1)) & ~(size - 1); + hole_end = hole_start + size - 1; + break; + } + } + /* If I don't overlap any segments I have found my hole! */ + if (i == image->nr_segments) { + pages = kexec_pfn_to_page(hole_start >> PAGE_SHIFT); + break; + } + } + if (pages) + image->control_page = hole_end; + + return pages; +} + + +struct page *kimage_alloc_control_pages(struct kimage *image, + unsigned int order) +{ + struct page *pages = NULL; + + switch (image->type) { + case KEXEC_TYPE_DEFAULT: + pages = kimage_alloc_normal_control_pages(image, order); + break; + case KEXEC_TYPE_CRASH: + pages = kimage_alloc_crash_control_pages(image, order); + break; + } + + return pages; +} +#else /* !CONFIG_XEN */ +struct page *kimage_alloc_control_pages(struct kimage *image, + unsigned int order) +{ + return kimage_alloc_normal_control_pages(image, order); +} +#endif + +static int kimage_add_entry(struct kimage *image, kimage_entry_t entry) +{ + if (*image->entry != 0) + image->entry++; + + if (image->entry == image->last_entry) { + kimage_entry_t *ind_page; + struct page *page; + + page = kimage_alloc_page(image, GFP_KERNEL, KIMAGE_NO_DEST); + if (!page) + return -ENOMEM; + + ind_page = page_address(page); + *image->entry = kexec_virt_to_phys(ind_page) | IND_INDIRECTION; + image->entry = ind_page; + image->last_entry = ind_page + + ((PAGE_SIZE/sizeof(kimage_entry_t)) - 1); + } + *image->entry = entry; + image->entry++; + *image->entry = 0; + + return 0; +} + +static int kimage_set_destination(struct kimage *image, + unsigned long destination) +{ + int result; + + destination &= PAGE_MASK; + result = kimage_add_entry(image, destination | IND_DESTINATION); + if (result == 0) + image->destination = destination; + + return result; +} + + +static int kimage_add_page(struct kimage *image, unsigned long page) +{ + int result; + + page &= PAGE_MASK; + result = kimage_add_entry(image, page | IND_SOURCE); + if (result == 0) + image->destination += PAGE_SIZE; + + return result; +} + + +static void kimage_free_extra_pages(struct kimage *image) +{ + /* Walk through and free any extra destination pages I may have */ + kimage_free_page_list(&image->dest_pages); + + /* Walk through and free any unuseable pages I have cached */ + kimage_free_page_list(&image->unuseable_pages); + +} +static int kimage_terminate(struct kimage *image) +{ + if (*image->entry != 0) + image->entry++; + + *image->entry = IND_DONE; + + return 0; +} + +#define for_each_kimage_entry(image, ptr, entry) \ + for (ptr = &image->head; (entry = *ptr) && !(entry & IND_DONE); \ + ptr = (entry & IND_INDIRECTION)? \ + kexec_phys_to_virt((entry & PAGE_MASK)): ptr +1) + +static void kimage_free_entry(kimage_entry_t entry) +{ + struct page *page; + + page = kexec_pfn_to_page(entry >> PAGE_SHIFT); + kimage_free_pages(page); +} + +static void kimage_free(struct kimage *image) +{ + kimage_entry_t *ptr, entry; + kimage_entry_t ind = 0; + + if (!image) + return; + +#ifdef CONFIG_XEN + xen_machine_kexec_unload(image); +#endif + + kimage_free_extra_pages(image); + for_each_kimage_entry(image, ptr, entry) { + if (entry & IND_INDIRECTION) { + /* Free the previous indirection page */ + if (ind & IND_INDIRECTION) + kimage_free_entry(ind); + /* Save this indirection page until we are + * done with it. + */ + ind = entry; + } + else if (entry & IND_SOURCE) + kimage_free_entry(entry); + } + /* Free the final indirection page */ + if (ind & IND_INDIRECTION) + kimage_free_entry(ind); + + /* Handle any machine specific cleanup */ + machine_kexec_cleanup(image); + + /* Free the kexec control pages... */ + kimage_free_page_list(&image->control_pages); + kfree(image); +} + +static kimage_entry_t *kimage_dst_used(struct kimage *image, + unsigned long page) +{ + kimage_entry_t *ptr, entry; + unsigned long destination = 0; + + for_each_kimage_entry(image, ptr, entry) { + if (entry & IND_DESTINATION) + destination = entry & PAGE_MASK; + else if (entry & IND_SOURCE) { + if (page == destination) + return ptr; + destination += PAGE_SIZE; + } + } + + return NULL; +} + +static struct page *kimage_alloc_page(struct kimage *image, + gfp_t gfp_mask, + unsigned long destination) +{ + /* + * Here we implement safeguards to ensure that a source page + * is not copied to its destination page before the data on + * the destination page is no longer useful. + * + * To do this we maintain the invariant that a source page is + * either its own destination page, or it is not a + * destination page at all. + * + * That is slightly stronger than required, but the proof + * that no problems will not occur is trivial, and the + * implementation is simply to verify. + * + * When allocating all pages normally this algorithm will run + * in O(N) time, but in the worst case it will run in O(N^2) + * time. If the runtime is a problem the data structures can + * be fixed. + */ + struct page *page; + unsigned long addr; + + /* + * Walk through the list of destination pages, and see if I + * have a match. + */ + list_for_each_entry(page, &image->dest_pages, lru) { + addr = kexec_page_to_pfn(page) << PAGE_SHIFT; + if (addr == destination) { + list_del(&page->lru); + return page; + } + } + page = NULL; + while (1) { + kimage_entry_t *old; + + /* Allocate a page, if we run out of memory give up */ + page = kimage_alloc_pages(gfp_mask, 0); + if (!page) + return NULL; + /* If the page cannot be used file it away */ + if (kexec_page_to_pfn(page) > + (KEXEC_SOURCE_MEMORY_LIMIT >> PAGE_SHIFT)) { + list_add(&page->lru, &image->unuseable_pages); + continue; + } + addr = kexec_page_to_pfn(page) << PAGE_SHIFT; + + /* If it is the destination page we want use it */ + if (addr == destination) + break; + + /* If the page is not a destination page use it */ + if (!kimage_is_destination_range(image, addr, + addr + PAGE_SIZE)) + break; + + /* + * I know that the page is someones destination page. + * See if there is already a source page for this + * destination page. And if so swap the source pages. + */ + old = kimage_dst_used(image, addr); + if (old) { + /* If so move it */ + unsigned long old_addr; + struct page *old_page; + + old_addr = *old & PAGE_MASK; + old_page = kexec_pfn_to_page(old_addr >> PAGE_SHIFT); + copy_highpage(page, old_page); + *old = addr | (*old & ~PAGE_MASK); + + /* The old page I have found cannot be a + * destination page, so return it. + */ + addr = old_addr; + page = old_page; + break; + } + else { + /* Place the page on the destination list I + * will use it later. + */ + list_add(&page->lru, &image->dest_pages); + } + } + + return page; +} + +static int kimage_load_normal_segment(struct kimage *image, + struct kexec_segment *segment) +{ + unsigned long maddr; + unsigned long ubytes, mbytes; + int result; + unsigned char __user *buf; + + result = 0; + buf = segment->buf; + ubytes = segment->bufsz; + mbytes = segment->memsz; + maddr = segment->mem; + + result = kimage_set_destination(image, maddr); + if (result < 0) + goto out; + + while (mbytes) { + struct page *page; + char *ptr; + size_t uchunk, mchunk; + + page = kimage_alloc_page(image, GFP_HIGHUSER, maddr); + if (page == 0) { + result = -ENOMEM; + goto out; + } + result = kimage_add_page(image, kexec_page_to_pfn(page) + << PAGE_SHIFT); + if (result < 0) + goto out; + + ptr = kmap(page); + /* Start with a clear page */ + memset(ptr, 0, PAGE_SIZE); + ptr += maddr & ~PAGE_MASK; + mchunk = PAGE_SIZE - (maddr & ~PAGE_MASK); + if (mchunk > mbytes) + mchunk = mbytes; + + uchunk = mchunk; + if (uchunk > ubytes) + uchunk = ubytes; + + result = copy_from_user(ptr, buf, uchunk); + kunmap(page); + if (result) { + result = (result < 0) ? result : -EIO; + goto out; + } + ubytes -= uchunk; + maddr += mchunk; + buf += mchunk; + mbytes -= mchunk; + } +out: + return result; +} + +#ifndef CONFIG_XEN +static int kimage_load_crash_segment(struct kimage *image, + struct kexec_segment *segment) +{ + /* For crash dumps kernels we simply copy the data from + * user space to it's destination. + * We do things a page at a time for the sake of kmap. + */ + unsigned long maddr; + unsigned long ubytes, mbytes; + int result; + unsigned char __user *buf; + + result = 0; + buf = segment->buf; + ubytes = segment->bufsz; + mbytes = segment->memsz; + maddr = segment->mem; + while (mbytes) { + struct page *page; + char *ptr; + size_t uchunk, mchunk; + + page = kexec_pfn_to_page(maddr >> PAGE_SHIFT); + if (page == 0) { + result = -ENOMEM; + goto out; + } + ptr = kmap(page); + ptr += maddr & ~PAGE_MASK; + mchunk = PAGE_SIZE - (maddr & ~PAGE_MASK); + if (mchunk > mbytes) + mchunk = mbytes; + + uchunk = mchunk; + if (uchunk > ubytes) { + uchunk = ubytes; + /* Zero the trailing part of the page */ + memset(ptr + uchunk, 0, mchunk - uchunk); + } + result = copy_from_user(ptr, buf, uchunk); + kunmap(page); + if (result) { + result = (result < 0) ? result : -EIO; + goto out; + } + ubytes -= uchunk; + maddr += mchunk; + buf += mchunk; + mbytes -= mchunk; + } +out: + return result; +} + +static int kimage_load_segment(struct kimage *image, + struct kexec_segment *segment) +{ + int result = -ENOMEM; + + switch (image->type) { + case KEXEC_TYPE_DEFAULT: + result = kimage_load_normal_segment(image, segment); + break; + case KEXEC_TYPE_CRASH: + result = kimage_load_crash_segment(image, segment); + break; + } + + return result; +} +#else /* CONFIG_XEN */ +static int kimage_load_segment(struct kimage *image, + struct kexec_segment *segment) +{ + return kimage_load_normal_segment(image, segment); +} +#endif + +/* + * Exec Kernel system call: for obvious reasons only root may call it. + * + * This call breaks up into three pieces. + * - A generic part which loads the new kernel from the current + * address space, and very carefully places the data in the + * allocated pages. + * + * - A generic part that interacts with the kernel and tells all of + * the devices to shut down. Preventing on-going dmas, and placing + * the devices in a consistent state so a later kernel can + * reinitialize them. + * + * - A machine specific part that includes the syscall number + * and the copies the image to it's final destination. And + * jumps into the image at entry. + * + * kexec does not sync, or unmount filesystems so if you need + * that to happen you need to do that yourself. + */ +struct kimage *kexec_image = NULL; +static struct kimage *kexec_crash_image = NULL; +/* + * A home grown binary mutex. + * Nothing can wait so this mutex is safe to use + * in interrupt context :) + */ +static int kexec_lock = 0; + +asmlinkage long sys_kexec_load(unsigned long entry, unsigned long nr_segments, + struct kexec_segment __user *segments, + unsigned long flags) +{ + struct kimage **dest_image, *image; + int locked; + int result; + + /* We only trust the superuser with rebooting the system. */ + if (!capable(CAP_SYS_BOOT)) + return -EPERM; + + /* + * Verify we have a legal set of flags + * This leaves us room for future extensions. + */ + if ((flags & KEXEC_FLAGS) != (flags & ~KEXEC_ARCH_MASK)) + return -EINVAL; + + /* Verify we are on the appropriate architecture */ + if (((flags & KEXEC_ARCH_MASK) != KEXEC_ARCH) && + ((flags & KEXEC_ARCH_MASK) != KEXEC_ARCH_DEFAULT)) + return -EINVAL; + + /* Put an artificial cap on the number + * of segments passed to kexec_load. + */ + if (nr_segments > KEXEC_SEGMENT_MAX) + return -EINVAL; + + image = NULL; + result = 0; + + /* Because we write directly to the reserved memory + * region when loading crash kernels we need a mutex here to + * prevent multiple crash kernels from attempting to load + * simultaneously, and to prevent a crash kernel from loading + * over the top of a in use crash kernel. + * + * KISS: always take the mutex. + */ + locked = xchg(&kexec_lock, 1); + if (locked) + return -EBUSY; + + dest_image = &kexec_image; + if (flags & KEXEC_ON_CRASH) + dest_image = &kexec_crash_image; + if (nr_segments > 0) { + unsigned long i; + + /* Loading another kernel to reboot into */ + if ((flags & KEXEC_ON_CRASH) == 0) + result = kimage_normal_alloc(&image, entry, + nr_segments, segments); + /* Loading another kernel to switch to if this one crashes */ + else if (flags & KEXEC_ON_CRASH) { + /* Free any current crash dump kernel before + * we corrupt it. + */ + kimage_free(xchg(&kexec_crash_image, NULL)); + result = kimage_crash_alloc(&image, entry, + nr_segments, segments); + } + if (result) + goto out; + + result = machine_kexec_prepare(image); + if (result) + goto out; + + for (i = 0; i < nr_segments; i++) { + result = kimage_load_segment(image, &image->segment[i]); + if (result) + goto out; + } + result = kimage_terminate(image); + if (result) + goto out; + } +#ifdef CONFIG_XEN + result = xen_machine_kexec_load(image); + if (result) + goto out; +#endif + /* Install the new kernel, and Uninstall the old */ + image = xchg(dest_image, image); + +out: + xchg(&kexec_lock, 0); /* Release the mutex */ + kimage_free(image); + + return result; +} + +#ifdef CONFIG_COMPAT +asmlinkage long compat_sys_kexec_load(unsigned long entry, + unsigned long nr_segments, + struct compat_kexec_segment __user *segments, + unsigned long flags) +{ + struct compat_kexec_segment in; + struct kexec_segment out, __user *ksegments; + unsigned long i, result; + + /* Don't allow clients that don't understand the native + * architecture to do anything. + */ + if ((flags & KEXEC_ARCH_MASK) == KEXEC_ARCH_DEFAULT) + return -EINVAL; + + if (nr_segments > KEXEC_SEGMENT_MAX) + return -EINVAL; + + ksegments = compat_alloc_user_space(nr_segments * sizeof(out)); + for (i=0; i < nr_segments; i++) { + result = copy_from_user(&in, &segments[i], sizeof(in)); + if (result) + return -EFAULT; + + out.buf = compat_ptr(in.buf); + out.bufsz = in.bufsz; + out.mem = in.mem; + out.memsz = in.memsz; + + result = copy_to_user(&ksegments[i], &out, sizeof(out)); + if (result) + return -EFAULT; + } + + return sys_kexec_load(entry, nr_segments, ksegments, flags); +} +#endif + +void crash_kexec(struct pt_regs *regs) +{ + struct kimage *image; + int locked; + + /* Take the kexec_lock here to prevent sys_kexec_load + * running on one cpu from replacing the crash kernel + * we are using after a panic on a different cpu. + * + * If the crash kernel was not located in a fixed area + * of memory the xchg(&kexec_crash_image) would be + * sufficient. But since I reuse the memory... + */ + locked = xchg(&kexec_lock, 1); + if (!locked) { + image = xchg(&kexec_crash_image, NULL); + if (image) { + struct pt_regs fixed_regs; + crash_setup_regs(&fixed_regs, regs); + machine_crash_shutdown(&fixed_regs); + machine_kexec(image); + } + xchg(&kexec_lock, 0); + } +} + +static int __init crash_notes_memory_init(void) +{ + /* Allocate memory for saving cpu registers. */ + crash_notes = alloc_percpu(note_buf_t); + if (!crash_notes) { + printk("Kexec: Memory allocation for saving cpu register" + " states failed\n"); + return -ENOMEM; + } + return 0; +} +module_init(crash_notes_memory_init) diff --git a/linux-2.6-xen-sparse/mm/memory.c b/linux-2.6-xen-sparse/mm/memory.c index d7319d32f9..d44d1ce44f 100644 --- a/linux-2.6-xen-sparse/mm/memory.c +++ b/linux-2.6-xen-sparse/mm/memory.c @@ -882,6 +882,7 @@ unsigned long zap_page_range(struct vm_area_struct *vma, unsigned long address, tlb_finish_mmu(tlb, address, end); return end; } +EXPORT_SYMBOL(zap_page_range); /* * Do a quick page-table lookup for a single page. diff --git a/patches/linux-2.6.16.32/blktap-aio-16_03_06.patch b/patches/linux-2.6.16.33/blktap-aio-16_03_06.patch similarity index 100% rename from patches/linux-2.6.16.32/blktap-aio-16_03_06.patch rename to patches/linux-2.6.16.33/blktap-aio-16_03_06.patch diff --git a/patches/linux-2.6.16.32/device_bind.patch b/patches/linux-2.6.16.33/device_bind.patch similarity index 100% rename from patches/linux-2.6.16.32/device_bind.patch rename to patches/linux-2.6.16.33/device_bind.patch diff --git a/patches/linux-2.6.16.32/fix-hz-suspend.patch b/patches/linux-2.6.16.33/fix-hz-suspend.patch similarity index 100% rename from patches/linux-2.6.16.32/fix-hz-suspend.patch rename to patches/linux-2.6.16.33/fix-hz-suspend.patch diff --git a/patches/linux-2.6.16.32/fix-ide-cd-pio-mode.patch b/patches/linux-2.6.16.33/fix-ide-cd-pio-mode.patch similarity index 100% rename from patches/linux-2.6.16.32/fix-ide-cd-pio-mode.patch rename to patches/linux-2.6.16.33/fix-ide-cd-pio-mode.patch diff --git a/patches/linux-2.6.16.33/git-2a8a3d5b65e86ec1dfef7d268c64a909eab94af7.patch b/patches/linux-2.6.16.33/git-2a8a3d5b65e86ec1dfef7d268c64a909eab94af7.patch new file mode 100644 index 0000000000..3e7c024b38 --- /dev/null +++ b/patches/linux-2.6.16.33/git-2a8a3d5b65e86ec1dfef7d268c64a909eab94af7.patch @@ -0,0 +1,62 @@ +From: Eric W. Biederman +Date: Sun, 30 Jul 2006 10:03:20 +0000 (-0700) +Subject: [PATCH] machine_kexec.c: Fix the description of segment handling +X-Git-Tag: v2.6.18-rc4 +X-Git-Url: http://www.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=2a8a3d5b65e86ec1dfef7d268c64a909eab94af7 + +[PATCH] machine_kexec.c: Fix the description of segment handling + +One of my original comments in machine_kexec was unclear +and this should fix it. + +Signed-off-by: Eric W. Biederman +Cc: Andi Kleen +Acked-by: Horms +Signed-off-by: Andrew Morton +Signed-off-by: Linus Torvalds +--- + +--- a/arch/i386/kernel/machine_kexec.c ++++ b/arch/i386/kernel/machine_kexec.c +@@ -189,14 +189,11 @@ NORET_TYPE void machine_kexec(struct kim + memcpy((void *)reboot_code_buffer, relocate_new_kernel, + relocate_new_kernel_size); + +- /* The segment registers are funny things, they are +- * automatically loaded from a table, in memory wherever you +- * set them to a specific selector, but this table is never +- * accessed again you set the segment to a different selector. +- * +- * The more common model is are caches where the behide +- * the scenes work is done, but is also dropped at arbitrary +- * times. ++ /* The segment registers are funny things, they have both a ++ * visible and an invisible part. Whenever the visible part is ++ * set to a specific selector, the invisible part is loaded ++ * with from a table in memory. At no other time is the ++ * descriptor table in memory accessed. + * + * I take advantage of this here by force loading the + * segments, before I zap the gdt with an invalid value. +--- a/arch/x86_64/kernel/machine_kexec.c ++++ b/arch/x86_64/kernel/machine_kexec.c +@@ -207,14 +207,11 @@ NORET_TYPE void machine_kexec(struct kim + __flush_tlb(); + + +- /* The segment registers are funny things, they are +- * automatically loaded from a table, in memory wherever you +- * set them to a specific selector, but this table is never +- * accessed again unless you set the segment to a different selector. +- * +- * The more common model are caches where the behide +- * the scenes work is done, but is also dropped at arbitrary +- * times. ++ /* The segment registers are funny things, they have both a ++ * visible and an invisible part. Whenever the visible part is ++ * set to a specific selector, the invisible part is loaded ++ * with from a table in memory. At no other time is the ++ * descriptor table in memory accessed. + * + * I take advantage of this here by force loading the + * segments, before I zap the gdt with an invalid value. diff --git a/patches/linux-2.6.16.33/git-2efe55a9cec8418f0e0cde3dc3787a42fddc4411.patch b/patches/linux-2.6.16.33/git-2efe55a9cec8418f0e0cde3dc3787a42fddc4411.patch new file mode 100644 index 0000000000..9d0b701ffb --- /dev/null +++ b/patches/linux-2.6.16.33/git-2efe55a9cec8418f0e0cde3dc3787a42fddc4411.patch @@ -0,0 +1,93 @@ +From: Tobias Klauser +Date: Mon, 26 Jun 2006 16:57:34 +0000 (+0200) +Subject: Storage class should be first +X-Git-Tag: v2.6.18-rc1 +X-Git-Url: http://www.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=2efe55a9cec8418f0e0cde3dc3787a42fddc4411 + +Storage class should be first + +Storage class should be before const + +Signed-off-by: Tobias Klauser +Signed-off-by: Adrian Bunk +--- + +--- a/arch/i386/kernel/machine_kexec.c ++++ b/arch/i386/kernel/machine_kexec.c +@@ -133,9 +133,9 @@ typedef asmlinkage NORET_TYPE void (*rel + unsigned long start_address, + unsigned int has_pae) ATTRIB_NORET; + +-const extern unsigned char relocate_new_kernel[]; ++extern const unsigned char relocate_new_kernel[]; + extern void relocate_new_kernel_end(void); +-const extern unsigned int relocate_new_kernel_size; ++extern const unsigned int relocate_new_kernel_size; + + /* + * A architecture hook called to validate the +--- a/arch/powerpc/kernel/machine_kexec_32.c ++++ b/arch/powerpc/kernel/machine_kexec_32.c +@@ -30,8 +30,8 @@ typedef NORET_TYPE void (*relocate_new_k + */ + void default_machine_kexec(struct kimage *image) + { +- const extern unsigned char relocate_new_kernel[]; +- const extern unsigned int relocate_new_kernel_size; ++ extern const unsigned char relocate_new_kernel[]; ++ extern const unsigned int relocate_new_kernel_size; + unsigned long page_list; + unsigned long reboot_code_buffer, reboot_code_buffer_phys; + relocate_new_kernel_t rnk; +--- a/arch/ppc/kernel/machine_kexec.c ++++ b/arch/ppc/kernel/machine_kexec.c +@@ -25,8 +25,8 @@ typedef NORET_TYPE void (*relocate_new_k + unsigned long reboot_code_buffer, + unsigned long start_address) ATTRIB_NORET; + +-const extern unsigned char relocate_new_kernel[]; +-const extern unsigned int relocate_new_kernel_size; ++extern const unsigned char relocate_new_kernel[]; ++extern const unsigned int relocate_new_kernel_size; + + void machine_shutdown(void) + { +--- a/arch/s390/kernel/machine_kexec.c ++++ b/arch/s390/kernel/machine_kexec.c +@@ -27,8 +27,8 @@ static void kexec_halt_all_cpus(void *); + + typedef void (*relocate_kernel_t) (kimage_entry_t *, unsigned long); + +-const extern unsigned char relocate_kernel[]; +-const extern unsigned long long relocate_kernel_len; ++extern const unsigned char relocate_kernel[]; ++extern const unsigned long long relocate_kernel_len; + + int + machine_kexec_prepare(struct kimage *image) +--- a/arch/sh/kernel/machine_kexec.c ++++ b/arch/sh/kernel/machine_kexec.c +@@ -25,8 +25,8 @@ typedef NORET_TYPE void (*relocate_new_k + unsigned long start_address, + unsigned long vbr_reg) ATTRIB_NORET; + +-const extern unsigned char relocate_new_kernel[]; +-const extern unsigned int relocate_new_kernel_size; ++extern const unsigned char relocate_new_kernel[]; ++extern const unsigned int relocate_new_kernel_size; + extern void *gdb_vbr_vector; + + /* +--- a/arch/x86_64/kernel/machine_kexec.c ++++ b/arch/x86_64/kernel/machine_kexec.c +@@ -149,8 +149,8 @@ typedef NORET_TYPE void (*relocate_new_k + unsigned long start_address, + unsigned long pgtable) ATTRIB_NORET; + +-const extern unsigned char relocate_new_kernel[]; +-const extern unsigned long relocate_new_kernel_size; ++extern const unsigned char relocate_new_kernel[]; ++extern const unsigned long relocate_new_kernel_size; + + int machine_kexec_prepare(struct kimage *image) + { diff --git a/patches/linux-2.6.16.33/git-3566561bfadffcb5dbc85d576be80c0dbf2cccc9.patch b/patches/linux-2.6.16.33/git-3566561bfadffcb5dbc85d576be80c0dbf2cccc9.patch new file mode 100644 index 0000000000..f0650a1bb4 --- /dev/null +++ b/patches/linux-2.6.16.33/git-3566561bfadffcb5dbc85d576be80c0dbf2cccc9.patch @@ -0,0 +1,401 @@ +From: Magnus Damm +Date: Tue, 26 Sep 2006 08:52:38 +0000 (+0200) +Subject: [PATCH] i386: Avoid overwriting the current pgd (V4, i386) +X-Git-Url: http://www.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=3566561bfadffcb5dbc85d576be80c0dbf2cccc9 + +[PATCH] i386: Avoid overwriting the current pgd (V4, i386) + +kexec: Avoid overwriting the current pgd (V4, i386) + +This patch upgrades the i386-specific kexec code to avoid overwriting the +current pgd. Overwriting the current pgd is bad when CONFIG_CRASH_DUMP is used +to start a secondary kernel that dumps the memory of the previous kernel. + +The code introduces a new set of page tables. These tables are used to provide +an executable identity mapping without overwriting the current pgd. + +Signed-off-by: Magnus Damm +Signed-off-by: Andi Kleen +--- + +--- a/arch/i386/kernel/machine_kexec.c ++++ b/arch/i386/kernel/machine_kexec.c +@@ -21,70 +21,13 @@ + #include + + #define PAGE_ALIGNED __attribute__ ((__aligned__(PAGE_SIZE))) +- +-#define L0_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY) +-#define L1_ATTR (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY) +-#define L2_ATTR (_PAGE_PRESENT) +- +-#define LEVEL0_SIZE (1UL << 12UL) +- +-#ifndef CONFIG_X86_PAE +-#define LEVEL1_SIZE (1UL << 22UL) +-static u32 pgtable_level1[1024] PAGE_ALIGNED; +- +-static void identity_map_page(unsigned long address) +-{ +- unsigned long level1_index, level2_index; +- u32 *pgtable_level2; +- +- /* Find the current page table */ +- pgtable_level2 = __va(read_cr3()); +- +- /* Find the indexes of the physical address to identity map */ +- level1_index = (address % LEVEL1_SIZE)/LEVEL0_SIZE; +- level2_index = address / LEVEL1_SIZE; +- +- /* Identity map the page table entry */ +- pgtable_level1[level1_index] = address | L0_ATTR; +- pgtable_level2[level2_index] = __pa(pgtable_level1) | L1_ATTR; +- +- /* Flush the tlb so the new mapping takes effect. +- * Global tlb entries are not flushed but that is not an issue. +- */ +- load_cr3(pgtable_level2); +-} +- +-#else +-#define LEVEL1_SIZE (1UL << 21UL) +-#define LEVEL2_SIZE (1UL << 30UL) +-static u64 pgtable_level1[512] PAGE_ALIGNED; +-static u64 pgtable_level2[512] PAGE_ALIGNED; +- +-static void identity_map_page(unsigned long address) +-{ +- unsigned long level1_index, level2_index, level3_index; +- u64 *pgtable_level3; +- +- /* Find the current page table */ +- pgtable_level3 = __va(read_cr3()); +- +- /* Find the indexes of the physical address to identity map */ +- level1_index = (address % LEVEL1_SIZE)/LEVEL0_SIZE; +- level2_index = (address % LEVEL2_SIZE)/LEVEL1_SIZE; +- level3_index = address / LEVEL2_SIZE; +- +- /* Identity map the page table entry */ +- pgtable_level1[level1_index] = address | L0_ATTR; +- pgtable_level2[level2_index] = __pa(pgtable_level1) | L1_ATTR; +- set_64bit(&pgtable_level3[level3_index], +- __pa(pgtable_level2) | L2_ATTR); +- +- /* Flush the tlb so the new mapping takes effect. +- * Global tlb entries are not flushed but that is not an issue. +- */ +- load_cr3(pgtable_level3); +-} ++static u32 kexec_pgd[1024] PAGE_ALIGNED; ++#ifdef CONFIG_X86_PAE ++static u32 kexec_pmd0[1024] PAGE_ALIGNED; ++static u32 kexec_pmd1[1024] PAGE_ALIGNED; + #endif ++static u32 kexec_pte0[1024] PAGE_ALIGNED; ++static u32 kexec_pte1[1024] PAGE_ALIGNED; + + static void set_idt(void *newidt, __u16 limit) + { +@@ -128,16 +71,6 @@ static void load_segments(void) + #undef __STR + } + +-typedef asmlinkage NORET_TYPE void (*relocate_new_kernel_t)( +- unsigned long indirection_page, +- unsigned long reboot_code_buffer, +- unsigned long start_address, +- unsigned int has_pae) ATTRIB_NORET; +- +-extern const unsigned char relocate_new_kernel[]; +-extern void relocate_new_kernel_end(void); +-extern const unsigned int relocate_new_kernel_size; +- + /* + * A architecture hook called to validate the + * proposed image and prepare the control pages +@@ -170,25 +103,29 @@ void machine_kexec_cleanup(struct kimage + */ + NORET_TYPE void machine_kexec(struct kimage *image) + { +- unsigned long page_list; +- unsigned long reboot_code_buffer; +- +- relocate_new_kernel_t rnk; ++ unsigned long page_list[PAGES_NR]; ++ void *control_page; + + /* Interrupts aren't acceptable while we reboot */ + local_irq_disable(); + +- /* Compute some offsets */ +- reboot_code_buffer = page_to_pfn(image->control_code_page) +- << PAGE_SHIFT; +- page_list = image->head; +- +- /* Set up an identity mapping for the reboot_code_buffer */ +- identity_map_page(reboot_code_buffer); +- +- /* copy it out */ +- memcpy((void *)reboot_code_buffer, relocate_new_kernel, +- relocate_new_kernel_size); ++ control_page = page_address(image->control_code_page); ++ memcpy(control_page, relocate_kernel, PAGE_SIZE); ++ ++ page_list[PA_CONTROL_PAGE] = __pa(control_page); ++ page_list[VA_CONTROL_PAGE] = (unsigned long)relocate_kernel; ++ page_list[PA_PGD] = __pa(kexec_pgd); ++ page_list[VA_PGD] = (unsigned long)kexec_pgd; ++#ifdef CONFIG_X86_PAE ++ page_list[PA_PMD_0] = __pa(kexec_pmd0); ++ page_list[VA_PMD_0] = (unsigned long)kexec_pmd0; ++ page_list[PA_PMD_1] = __pa(kexec_pmd1); ++ page_list[VA_PMD_1] = (unsigned long)kexec_pmd1; ++#endif ++ page_list[PA_PTE_0] = __pa(kexec_pte0); ++ page_list[VA_PTE_0] = (unsigned long)kexec_pte0; ++ page_list[PA_PTE_1] = __pa(kexec_pte1); ++ page_list[VA_PTE_1] = (unsigned long)kexec_pte1; + + /* The segment registers are funny things, they have both a + * visible and an invisible part. Whenever the visible part is +@@ -207,8 +144,8 @@ NORET_TYPE void machine_kexec(struct kim + set_idt(phys_to_virt(0),0); + + /* now call it */ +- rnk = (relocate_new_kernel_t) reboot_code_buffer; +- (*rnk)(page_list, reboot_code_buffer, image->start, cpu_has_pae); ++ relocate_kernel((unsigned long)image->head, (unsigned long)page_list, ++ image->start, cpu_has_pae); + } + + /* crashkernel=size@addr specifies the location to reserve for +--- a/arch/i386/kernel/relocate_kernel.S ++++ b/arch/i386/kernel/relocate_kernel.S +@@ -7,16 +7,138 @@ + */ + + #include ++#include ++#include ++ ++/* ++ * Must be relocatable PIC code callable as a C function ++ */ ++ ++#define PTR(x) (x << 2) ++#define PAGE_ALIGNED (1 << PAGE_SHIFT) ++#define PAGE_ATTR 0x63 /* _PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED|_PAGE_DIRTY */ ++#define PAE_PGD_ATTR 0x01 /* _PAGE_PRESENT */ ++ ++ .text ++ .align PAGE_ALIGNED ++ .globl relocate_kernel ++relocate_kernel: ++ movl 8(%esp), %ebp /* list of pages */ ++ ++#ifdef CONFIG_X86_PAE ++ /* map the control page at its virtual address */ ++ ++ movl PTR(VA_PGD)(%ebp), %edi ++ movl PTR(VA_CONTROL_PAGE)(%ebp), %eax ++ andl $0xc0000000, %eax ++ shrl $27, %eax ++ addl %edi, %eax ++ ++ movl PTR(PA_PMD_0)(%ebp), %edx ++ orl $PAE_PGD_ATTR, %edx ++ movl %edx, (%eax) ++ ++ movl PTR(VA_PMD_0)(%ebp), %edi ++ movl PTR(VA_CONTROL_PAGE)(%ebp), %eax ++ andl $0x3fe00000, %eax ++ shrl $18, %eax ++ addl %edi, %eax ++ ++ movl PTR(PA_PTE_0)(%ebp), %edx ++ orl $PAGE_ATTR, %edx ++ movl %edx, (%eax) ++ ++ movl PTR(VA_PTE_0)(%ebp), %edi ++ movl PTR(VA_CONTROL_PAGE)(%ebp), %eax ++ andl $0x001ff000, %eax ++ shrl $9, %eax ++ addl %edi, %eax ++ ++ movl PTR(PA_CONTROL_PAGE)(%ebp), %edx ++ orl $PAGE_ATTR, %edx ++ movl %edx, (%eax) ++ ++ /* identity map the control page at its physical address */ ++ ++ movl PTR(VA_PGD)(%ebp), %edi ++ movl PTR(PA_CONTROL_PAGE)(%ebp), %eax ++ andl $0xc0000000, %eax ++ shrl $27, %eax ++ addl %edi, %eax ++ ++ movl PTR(PA_PMD_1)(%ebp), %edx ++ orl $PAE_PGD_ATTR, %edx ++ movl %edx, (%eax) ++ ++ movl PTR(VA_PMD_1)(%ebp), %edi ++ movl PTR(PA_CONTROL_PAGE)(%ebp), %eax ++ andl $0x3fe00000, %eax ++ shrl $18, %eax ++ addl %edi, %eax ++ ++ movl PTR(PA_PTE_1)(%ebp), %edx ++ orl $PAGE_ATTR, %edx ++ movl %edx, (%eax) ++ ++ movl PTR(VA_PTE_1)(%ebp), %edi ++ movl PTR(PA_CONTROL_PAGE)(%ebp), %eax ++ andl $0x001ff000, %eax ++ shrl $9, %eax ++ addl %edi, %eax ++ ++ movl PTR(PA_CONTROL_PAGE)(%ebp), %edx ++ orl $PAGE_ATTR, %edx ++ movl %edx, (%eax) ++#else ++ /* map the control page at its virtual address */ ++ ++ movl PTR(VA_PGD)(%ebp), %edi ++ movl PTR(VA_CONTROL_PAGE)(%ebp), %eax ++ andl $0xffc00000, %eax ++ shrl $20, %eax ++ addl %edi, %eax ++ ++ movl PTR(PA_PTE_0)(%ebp), %edx ++ orl $PAGE_ATTR, %edx ++ movl %edx, (%eax) ++ ++ movl PTR(VA_PTE_0)(%ebp), %edi ++ movl PTR(VA_CONTROL_PAGE)(%ebp), %eax ++ andl $0x003ff000, %eax ++ shrl $10, %eax ++ addl %edi, %eax ++ ++ movl PTR(PA_CONTROL_PAGE)(%ebp), %edx ++ orl $PAGE_ATTR, %edx ++ movl %edx, (%eax) ++ ++ /* identity map the control page at its physical address */ ++ ++ movl PTR(VA_PGD)(%ebp), %edi ++ movl PTR(PA_CONTROL_PAGE)(%ebp), %eax ++ andl $0xffc00000, %eax ++ shrl $20, %eax ++ addl %edi, %eax ++ ++ movl PTR(PA_PTE_1)(%ebp), %edx ++ orl $PAGE_ATTR, %edx ++ movl %edx, (%eax) ++ ++ movl PTR(VA_PTE_1)(%ebp), %edi ++ movl PTR(PA_CONTROL_PAGE)(%ebp), %eax ++ andl $0x003ff000, %eax ++ shrl $10, %eax ++ addl %edi, %eax ++ ++ movl PTR(PA_CONTROL_PAGE)(%ebp), %edx ++ orl $PAGE_ATTR, %edx ++ movl %edx, (%eax) ++#endif + +- /* +- * Must be relocatable PIC code callable as a C function, that once +- * it starts can not use the previous processes stack. +- */ +- .globl relocate_new_kernel + relocate_new_kernel: + /* read the arguments and say goodbye to the stack */ + movl 4(%esp), %ebx /* page_list */ +- movl 8(%esp), %ebp /* reboot_code_buffer */ ++ movl 8(%esp), %ebp /* list of pages */ + movl 12(%esp), %edx /* start address */ + movl 16(%esp), %ecx /* cpu_has_pae */ + +@@ -24,11 +146,26 @@ relocate_new_kernel: + pushl $0 + popfl + +- /* set a new stack at the bottom of our page... */ +- lea 4096(%ebp), %esp ++ /* get physical address of control page now */ ++ /* this is impossible after page table switch */ ++ movl PTR(PA_CONTROL_PAGE)(%ebp), %edi ++ ++ /* switch to new set of page tables */ ++ movl PTR(PA_PGD)(%ebp), %eax ++ movl %eax, %cr3 ++ ++ /* setup a new stack at the end of the physical control page */ ++ lea 4096(%edi), %esp + +- /* store the parameters back on the stack */ +- pushl %edx /* store the start address */ ++ /* jump to identity mapped page */ ++ movl %edi, %eax ++ addl $(identity_mapped - relocate_kernel), %eax ++ pushl %eax ++ ret ++ ++identity_mapped: ++ /* store the start address on the stack */ ++ pushl %edx + + /* Set cr0 to a known state: + * 31 0 == Paging disabled +@@ -113,8 +250,3 @@ relocate_new_kernel: + xorl %edi, %edi + xorl %ebp, %ebp + ret +-relocate_new_kernel_end: +- +- .globl relocate_new_kernel_size +-relocate_new_kernel_size: +- .long relocate_new_kernel_end - relocate_new_kernel +--- a/include/asm-i386/kexec.h ++++ b/include/asm-i386/kexec.h +@@ -1,6 +1,26 @@ + #ifndef _I386_KEXEC_H + #define _I386_KEXEC_H + ++#define PA_CONTROL_PAGE 0 ++#define VA_CONTROL_PAGE 1 ++#define PA_PGD 2 ++#define VA_PGD 3 ++#define PA_PTE_0 4 ++#define VA_PTE_0 5 ++#define PA_PTE_1 6 ++#define VA_PTE_1 7 ++#ifdef CONFIG_X86_PAE ++#define PA_PMD_0 8 ++#define VA_PMD_0 9 ++#define PA_PMD_1 10 ++#define VA_PMD_1 11 ++#define PAGES_NR 12 ++#else ++#define PAGES_NR 8 ++#endif ++ ++#ifndef __ASSEMBLY__ ++ + #include + #include + #include +@@ -72,5 +92,12 @@ static inline void crash_setup_regs(stru + newregs->eip = (unsigned long)current_text_addr(); + } + } ++asmlinkage NORET_TYPE void ++relocate_kernel(unsigned long indirection_page, ++ unsigned long control_page, ++ unsigned long start_address, ++ unsigned int has_pae) ATTRIB_NORET; ++ ++#endif /* __ASSEMBLY__ */ + + #endif /* _I386_KEXEC_H */ diff --git a/patches/linux-2.6.16.33/git-4bfaaef01a1badb9e8ffb0c0a37cd2379008d21f.patch b/patches/linux-2.6.16.33/git-4bfaaef01a1badb9e8ffb0c0a37cd2379008d21f.patch new file mode 100644 index 0000000000..6e1f65fe9f --- /dev/null +++ b/patches/linux-2.6.16.33/git-4bfaaef01a1badb9e8ffb0c0a37cd2379008d21f.patch @@ -0,0 +1,375 @@ +From: Magnus Damm +Date: Tue, 26 Sep 2006 08:52:38 +0000 (+0200) +Subject: [PATCH] Avoid overwriting the current pgd (V4, x86_64) +X-Git-Tag: v2.6.19-rc1 +X-Git-Url: http://www.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=4bfaaef01a1badb9e8ffb0c0a37cd2379008d21f + +[PATCH] Avoid overwriting the current pgd (V4, x86_64) + +kexec: Avoid overwriting the current pgd (V4, x86_64) + +This patch upgrades the x86_64-specific kexec code to avoid overwriting the +current pgd. Overwriting the current pgd is bad when CONFIG_CRASH_DUMP is used +to start a secondary kernel that dumps the memory of the previous kernel. + +The code introduces a new set of page tables. These tables are used to provide +an executable identity mapping without overwriting the current pgd. + +Signed-off-by: Magnus Damm +Signed-off-by: Andi Kleen +--- + +--- a/arch/x86_64/kernel/machine_kexec.c ++++ b/arch/x86_64/kernel/machine_kexec.c +@@ -15,6 +15,15 @@ + #include + #include + ++#define PAGE_ALIGNED __attribute__ ((__aligned__(PAGE_SIZE))) ++static u64 kexec_pgd[512] PAGE_ALIGNED; ++static u64 kexec_pud0[512] PAGE_ALIGNED; ++static u64 kexec_pmd0[512] PAGE_ALIGNED; ++static u64 kexec_pte0[512] PAGE_ALIGNED; ++static u64 kexec_pud1[512] PAGE_ALIGNED; ++static u64 kexec_pmd1[512] PAGE_ALIGNED; ++static u64 kexec_pte1[512] PAGE_ALIGNED; ++ + static void init_level2_page(pmd_t *level2p, unsigned long addr) + { + unsigned long end_addr; +@@ -144,32 +153,19 @@ static void load_segments(void) + ); + } + +-typedef NORET_TYPE void (*relocate_new_kernel_t)(unsigned long indirection_page, +- unsigned long control_code_buffer, +- unsigned long start_address, +- unsigned long pgtable) ATTRIB_NORET; +- +-extern const unsigned char relocate_new_kernel[]; +-extern const unsigned long relocate_new_kernel_size; +- + int machine_kexec_prepare(struct kimage *image) + { +- unsigned long start_pgtable, control_code_buffer; ++ unsigned long start_pgtable; + int result; + + /* Calculate the offsets */ + start_pgtable = page_to_pfn(image->control_code_page) << PAGE_SHIFT; +- control_code_buffer = start_pgtable + PAGE_SIZE; + + /* Setup the identity mapped 64bit page table */ + result = init_pgtable(image, start_pgtable); + if (result) + return result; + +- /* Place the code in the reboot code buffer */ +- memcpy(__va(control_code_buffer), relocate_new_kernel, +- relocate_new_kernel_size); +- + return 0; + } + +@@ -184,28 +180,34 @@ void machine_kexec_cleanup(struct kimage + */ + NORET_TYPE void machine_kexec(struct kimage *image) + { +- unsigned long page_list; +- unsigned long control_code_buffer; +- unsigned long start_pgtable; +- relocate_new_kernel_t rnk; ++ unsigned long page_list[PAGES_NR]; ++ void *control_page; + + /* Interrupts aren't acceptable while we reboot */ + local_irq_disable(); + +- /* Calculate the offsets */ +- page_list = image->head; +- start_pgtable = page_to_pfn(image->control_code_page) << PAGE_SHIFT; +- control_code_buffer = start_pgtable + PAGE_SIZE; ++ control_page = page_address(image->control_code_page) + PAGE_SIZE; ++ memcpy(control_page, relocate_kernel, PAGE_SIZE); + +- /* Set the low half of the page table to my identity mapped +- * page table for kexec. Leave the high half pointing at the +- * kernel pages. Don't bother to flush the global pages +- * as that will happen when I fully switch to my identity mapped +- * page table anyway. +- */ +- memcpy(__va(read_cr3()), __va(start_pgtable), PAGE_SIZE/2); +- __flush_tlb(); ++ page_list[PA_CONTROL_PAGE] = __pa(control_page); ++ page_list[VA_CONTROL_PAGE] = (unsigned long)relocate_kernel; ++ page_list[PA_PGD] = __pa(kexec_pgd); ++ page_list[VA_PGD] = (unsigned long)kexec_pgd; ++ page_list[PA_PUD_0] = __pa(kexec_pud0); ++ page_list[VA_PUD_0] = (unsigned long)kexec_pud0; ++ page_list[PA_PMD_0] = __pa(kexec_pmd0); ++ page_list[VA_PMD_0] = (unsigned long)kexec_pmd0; ++ page_list[PA_PTE_0] = __pa(kexec_pte0); ++ page_list[VA_PTE_0] = (unsigned long)kexec_pte0; ++ page_list[PA_PUD_1] = __pa(kexec_pud1); ++ page_list[VA_PUD_1] = (unsigned long)kexec_pud1; ++ page_list[PA_PMD_1] = __pa(kexec_pmd1); ++ page_list[VA_PMD_1] = (unsigned long)kexec_pmd1; ++ page_list[PA_PTE_1] = __pa(kexec_pte1); ++ page_list[VA_PTE_1] = (unsigned long)kexec_pte1; + ++ page_list[PA_TABLE_PAGE] = ++ (unsigned long)__pa(page_address(image->control_code_page)); + + /* The segment registers are funny things, they have both a + * visible and an invisible part. Whenever the visible part is +@@ -222,9 +224,10 @@ NORET_TYPE void machine_kexec(struct kim + */ + set_gdt(phys_to_virt(0),0); + set_idt(phys_to_virt(0),0); ++ + /* now call it */ +- rnk = (relocate_new_kernel_t) control_code_buffer; +- (*rnk)(page_list, control_code_buffer, image->start, start_pgtable); ++ relocate_kernel((unsigned long)image->head, (unsigned long)page_list, ++ image->start); + } + + /* crashkernel=size@addr specifies the location to reserve for +--- a/arch/x86_64/kernel/relocate_kernel.S ++++ b/arch/x86_64/kernel/relocate_kernel.S +@@ -7,31 +7,169 @@ + */ + + #include ++#include ++#include + +- /* +- * Must be relocatable PIC code callable as a C function, that once +- * it starts can not use the previous processes stack. +- */ +- .globl relocate_new_kernel ++/* ++ * Must be relocatable PIC code callable as a C function ++ */ ++ ++#define PTR(x) (x << 3) ++#define PAGE_ALIGNED (1 << PAGE_SHIFT) ++#define PAGE_ATTR 0x63 /* _PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED|_PAGE_DIRTY */ ++ ++ .text ++ .align PAGE_ALIGNED + .code64 ++ .globl relocate_kernel ++relocate_kernel: ++ /* %rdi indirection_page ++ * %rsi page_list ++ * %rdx start address ++ */ ++ ++ /* map the control page at its virtual address */ ++ ++ movq $0x0000ff8000000000, %r10 /* mask */ ++ mov $(39 - 3), %cl /* bits to shift */ ++ movq PTR(VA_CONTROL_PAGE)(%rsi), %r11 /* address to map */ ++ ++ movq %r11, %r9 ++ andq %r10, %r9 ++ shrq %cl, %r9 ++ ++ movq PTR(VA_PGD)(%rsi), %r8 ++ addq %r8, %r9 ++ movq PTR(PA_PUD_0)(%rsi), %r8 ++ orq $PAGE_ATTR, %r8 ++ movq %r8, (%r9) ++ ++ shrq $9, %r10 ++ sub $9, %cl ++ ++ movq %r11, %r9 ++ andq %r10, %r9 ++ shrq %cl, %r9 ++ ++ movq PTR(VA_PUD_0)(%rsi), %r8 ++ addq %r8, %r9 ++ movq PTR(PA_PMD_0)(%rsi), %r8 ++ orq $PAGE_ATTR, %r8 ++ movq %r8, (%r9) ++ ++ shrq $9, %r10 ++ sub $9, %cl ++ ++ movq %r11, %r9 ++ andq %r10, %r9 ++ shrq %cl, %r9 ++ ++ movq PTR(VA_PMD_0)(%rsi), %r8 ++ addq %r8, %r9 ++ movq PTR(PA_PTE_0)(%rsi), %r8 ++ orq $PAGE_ATTR, %r8 ++ movq %r8, (%r9) ++ ++ shrq $9, %r10 ++ sub $9, %cl ++ ++ movq %r11, %r9 ++ andq %r10, %r9 ++ shrq %cl, %r9 ++ ++ movq PTR(VA_PTE_0)(%rsi), %r8 ++ addq %r8, %r9 ++ movq PTR(PA_CONTROL_PAGE)(%rsi), %r8 ++ orq $PAGE_ATTR, %r8 ++ movq %r8, (%r9) ++ ++ /* identity map the control page at its physical address */ ++ ++ movq $0x0000ff8000000000, %r10 /* mask */ ++ mov $(39 - 3), %cl /* bits to shift */ ++ movq PTR(PA_CONTROL_PAGE)(%rsi), %r11 /* address to map */ ++ ++ movq %r11, %r9 ++ andq %r10, %r9 ++ shrq %cl, %r9 ++ ++ movq PTR(VA_PGD)(%rsi), %r8 ++ addq %r8, %r9 ++ movq PTR(PA_PUD_1)(%rsi), %r8 ++ orq $PAGE_ATTR, %r8 ++ movq %r8, (%r9) ++ ++ shrq $9, %r10 ++ sub $9, %cl ++ ++ movq %r11, %r9 ++ andq %r10, %r9 ++ shrq %cl, %r9 ++ ++ movq PTR(VA_PUD_1)(%rsi), %r8 ++ addq %r8, %r9 ++ movq PTR(PA_PMD_1)(%rsi), %r8 ++ orq $PAGE_ATTR, %r8 ++ movq %r8, (%r9) ++ ++ shrq $9, %r10 ++ sub $9, %cl ++ ++ movq %r11, %r9 ++ andq %r10, %r9 ++ shrq %cl, %r9 ++ ++ movq PTR(VA_PMD_1)(%rsi), %r8 ++ addq %r8, %r9 ++ movq PTR(PA_PTE_1)(%rsi), %r8 ++ orq $PAGE_ATTR, %r8 ++ movq %r8, (%r9) ++ ++ shrq $9, %r10 ++ sub $9, %cl ++ ++ movq %r11, %r9 ++ andq %r10, %r9 ++ shrq %cl, %r9 ++ ++ movq PTR(VA_PTE_1)(%rsi), %r8 ++ addq %r8, %r9 ++ movq PTR(PA_CONTROL_PAGE)(%rsi), %r8 ++ orq $PAGE_ATTR, %r8 ++ movq %r8, (%r9) ++ + relocate_new_kernel: +- /* %rdi page_list +- * %rsi reboot_code_buffer ++ /* %rdi indirection_page ++ * %rsi page_list + * %rdx start address +- * %rcx page_table +- * %r8 arg5 +- * %r9 arg6 + */ + + /* zero out flags, and disable interrupts */ + pushq $0 + popfq + +- /* set a new stack at the bottom of our page... */ +- lea 4096(%rsi), %rsp ++ /* get physical address of control page now */ ++ /* this is impossible after page table switch */ ++ movq PTR(PA_CONTROL_PAGE)(%rsi), %r8 ++ ++ /* get physical address of page table now too */ ++ movq PTR(PA_TABLE_PAGE)(%rsi), %rcx ++ ++ /* switch to new set of page tables */ ++ movq PTR(PA_PGD)(%rsi), %r9 ++ movq %r9, %cr3 ++ ++ /* setup a new stack at the end of the physical control page */ ++ lea 4096(%r8), %rsp ++ ++ /* jump to identity mapped page */ ++ addq $(identity_mapped - relocate_kernel), %r8 ++ pushq %r8 ++ ret + +- /* store the parameters back on the stack */ +- pushq %rdx /* store the start address */ ++identity_mapped: ++ /* store the start address on the stack */ ++ pushq %rdx + + /* Set cr0 to a known state: + * 31 1 == Paging enabled +@@ -136,8 +274,3 @@ relocate_new_kernel: + xorq %r15, %r15 + + ret +-relocate_new_kernel_end: +- +- .globl relocate_new_kernel_size +-relocate_new_kernel_size: +- .quad relocate_new_kernel_end - relocate_new_kernel +--- a/include/asm-x86_64/kexec.h ++++ b/include/asm-x86_64/kexec.h +@@ -1,6 +1,27 @@ + #ifndef _X86_64_KEXEC_H + #define _X86_64_KEXEC_H + ++#define PA_CONTROL_PAGE 0 ++#define VA_CONTROL_PAGE 1 ++#define PA_PGD 2 ++#define VA_PGD 3 ++#define PA_PUD_0 4 ++#define VA_PUD_0 5 ++#define PA_PMD_0 6 ++#define VA_PMD_0 7 ++#define PA_PTE_0 8 ++#define VA_PTE_0 9 ++#define PA_PUD_1 10 ++#define VA_PUD_1 11 ++#define PA_PMD_1 12 ++#define VA_PMD_1 13 ++#define PA_PTE_1 14 ++#define VA_PTE_1 15 ++#define PA_TABLE_PAGE 16 ++#define PAGES_NR 17 ++ ++#ifndef __ASSEMBLY__ ++ + #include + + #include +@@ -64,4 +85,12 @@ static inline void crash_setup_regs(stru + newregs->rip = (unsigned long)current_text_addr(); + } + } ++ ++NORET_TYPE void ++relocate_kernel(unsigned long indirection_page, ++ unsigned long page_list, ++ unsigned long start_address) ATTRIB_NORET; ++ ++#endif /* __ASSEMBLY__ */ ++ + #endif /* _X86_64_KEXEC_H */ diff --git a/patches/linux-2.6.16.33/git-dbaab49f92ff6ae6255762a948375e4036cbdbd2.patch b/patches/linux-2.6.16.33/git-dbaab49f92ff6ae6255762a948375e4036cbdbd2.patch new file mode 100644 index 0000000000..199cabd4c9 --- /dev/null +++ b/patches/linux-2.6.16.33/git-dbaab49f92ff6ae6255762a948375e4036cbdbd2.patch @@ -0,0 +1,47 @@ +commit dbaab49f92ff6ae6255762a948375e4036cbdbd2 +Author: Vivek Goyal +Date: Sat Oct 21 18:37:03 2006 +0200 + + [PATCH] x86-64: Overlapping program headers in physical addr space fix + + o A recent change to vmlinux.ld.S file broke kexec as now resulting vmlinux + program headers are overlapping in physical address space. + + o Now all the vsyscall related sections are placed after data and after + that mostly init data sections are placed. To avoid physical overlap + among phdrs, there are three possible solutions. + - Place vsyscall sections also in data phdrs instead of user + - move vsyscal sections after init data in bss. + - create another phdrs say data.init and move all the sections + after vsyscall into this new phdr. + + o This patch implements the third solution. + + Signed-off-by: Vivek Goyal + Signed-off-by: Andi Kleen + Cc: Magnus Damm + Cc: Andi Kleen + Cc: "Eric W. Biederman" + Signed-off-by: Andrew Morton + +diff --git a/arch/x86_64/kernel/vmlinux.lds.S b/arch/x86_64/kernel/vmlinux.lds.S +index b9df2ab..1283614 100644 +--- a/arch/x86_64/kernel/vmlinux.lds.S ++++ b/arch/x86_64/kernel/vmlinux.lds.S +@@ -17,6 +17,7 @@ PHDRS { + text PT_LOAD FLAGS(5); /* R_E */ + data PT_LOAD FLAGS(7); /* RWE */ + user PT_LOAD FLAGS(7); /* RWE */ ++ data.init PT_LOAD FLAGS(7); /* RWE */ + note PT_NOTE FLAGS(4); /* R__ */ + } + SECTIONS +@@ -131,7 +132,7 @@ SECTIONS + . = ALIGN(8192); /* init_task */ + .data.init_task : AT(ADDR(.data.init_task) - LOAD_OFFSET) { + *(.data.init_task) +- } :data ++ }:data.init + + . = ALIGN(4096); + .data.page_aligned : AT(ADDR(.data.page_aligned) - LOAD_OFFSET) { diff --git a/patches/linux-2.6.16.32/i386-mach-io-check-nmi.patch b/patches/linux-2.6.16.33/i386-mach-io-check-nmi.patch similarity index 100% rename from patches/linux-2.6.16.32/i386-mach-io-check-nmi.patch rename to patches/linux-2.6.16.33/i386-mach-io-check-nmi.patch diff --git a/patches/linux-2.6.16.32/ipv6-no-autoconf.patch b/patches/linux-2.6.16.33/ipv6-no-autoconf.patch similarity index 100% rename from patches/linux-2.6.16.32/ipv6-no-autoconf.patch rename to patches/linux-2.6.16.33/ipv6-no-autoconf.patch diff --git a/patches/linux-2.6.16.32/kasprintf.patch b/patches/linux-2.6.16.33/kasprintf.patch similarity index 100% rename from patches/linux-2.6.16.32/kasprintf.patch rename to patches/linux-2.6.16.33/kasprintf.patch diff --git a/patches/linux-2.6.16.33/linux-2.6.19-rc1-kexec-move_segment_code-i386.patch b/patches/linux-2.6.16.33/linux-2.6.19-rc1-kexec-move_segment_code-i386.patch new file mode 100644 index 0000000000..a0c9518f48 --- /dev/null +++ b/patches/linux-2.6.16.33/linux-2.6.19-rc1-kexec-move_segment_code-i386.patch @@ -0,0 +1,169 @@ +kexec: Move asm segment handling code to the assembly file (i386) + +This patch moves the idt, gdt, and segment handling code from machine_kexec.c +to relocate_kernel.S. The main reason behind this move is to avoid code +duplication in the Xen hypervisor. With this patch all code required to kexec +is put on the control page. + +On top of that this patch also counts as a cleanup - I think it is much +nicer to write assembly directly in assembly files than wrap inline assembly +in C functions for no apparent reason. + +Signed-off-by: Magnus Damm +--- + + Applies to 2.6.19-rc1. + + machine_kexec.c | 59 ----------------------------------------------------- + relocate_kernel.S | 58 +++++++++++++++++++++++++++++++++++++++++++++++----- + 2 files changed, 53 insertions(+), 64 deletions(-) + +--- 0002/arch/i386/kernel/machine_kexec.c ++++ work/arch/i386/kernel/machine_kexec.c 2006-10-05 15:49:08.000000000 +0900 +@@ -29,48 +29,6 @@ static u32 kexec_pmd1[1024] PAGE_ALIGNED + static u32 kexec_pte0[1024] PAGE_ALIGNED; + static u32 kexec_pte1[1024] PAGE_ALIGNED; + +-static void set_idt(void *newidt, __u16 limit) +-{ +- struct Xgt_desc_struct curidt; +- +- /* ia32 supports unaliged loads & stores */ +- curidt.size = limit; +- curidt.address = (unsigned long)newidt; +- +- load_idt(&curidt); +-}; +- +- +-static void set_gdt(void *newgdt, __u16 limit) +-{ +- struct Xgt_desc_struct curgdt; +- +- /* ia32 supports unaligned loads & stores */ +- curgdt.size = limit; +- curgdt.address = (unsigned long)newgdt; +- +- load_gdt(&curgdt); +-}; +- +-static void load_segments(void) +-{ +-#define __STR(X) #X +-#define STR(X) __STR(X) +- +- __asm__ __volatile__ ( +- "\tljmp $"STR(__KERNEL_CS)",$1f\n" +- "\t1:\n" +- "\tmovl $"STR(__KERNEL_DS)",%%eax\n" +- "\tmovl %%eax,%%ds\n" +- "\tmovl %%eax,%%es\n" +- "\tmovl %%eax,%%fs\n" +- "\tmovl %%eax,%%gs\n" +- "\tmovl %%eax,%%ss\n" +- ::: "eax", "memory"); +-#undef STR +-#undef __STR +-} +- + /* + * A architecture hook called to validate the + * proposed image and prepare the control pages +@@ -127,23 +85,6 @@ NORET_TYPE void machine_kexec(struct kim + page_list[PA_PTE_1] = __pa(kexec_pte1); + page_list[VA_PTE_1] = (unsigned long)kexec_pte1; + +- /* The segment registers are funny things, they have both a +- * visible and an invisible part. Whenever the visible part is +- * set to a specific selector, the invisible part is loaded +- * with from a table in memory. At no other time is the +- * descriptor table in memory accessed. +- * +- * I take advantage of this here by force loading the +- * segments, before I zap the gdt with an invalid value. +- */ +- load_segments(); +- /* The gdt & idt are now invalid. +- * If you want to load them you must set up your own idt & gdt. +- */ +- set_gdt(phys_to_virt(0),0); +- set_idt(phys_to_virt(0),0); +- +- /* now call it */ + relocate_kernel((unsigned long)image->head, (unsigned long)page_list, + image->start, cpu_has_pae); + } +--- 0002/arch/i386/kernel/relocate_kernel.S ++++ work/arch/i386/kernel/relocate_kernel.S 2006-10-05 16:03:21.000000000 +0900 +@@ -154,14 +154,45 @@ relocate_new_kernel: + movl PTR(PA_PGD)(%ebp), %eax + movl %eax, %cr3 + ++ /* setup idt */ ++ movl %edi, %eax ++ addl $(idt_48 - relocate_kernel), %eax ++ lidtl (%eax) ++ ++ /* setup gdt */ ++ movl %edi, %eax ++ addl $(gdt - relocate_kernel), %eax ++ movl %edi, %esi ++ addl $((gdt_48 - relocate_kernel) + 2), %esi ++ movl %eax, (%esi) ++ ++ movl %edi, %eax ++ addl $(gdt_48 - relocate_kernel), %eax ++ lgdtl (%eax) ++ ++ /* setup data segment registers */ ++ mov $(gdt_ds - gdt), %eax ++ mov %eax, %ds ++ mov %eax, %es ++ mov %eax, %fs ++ mov %eax, %gs ++ mov %eax, %ss ++ + /* setup a new stack at the end of the physical control page */ + lea 4096(%edi), %esp + +- /* jump to identity mapped page */ +- movl %edi, %eax +- addl $(identity_mapped - relocate_kernel), %eax +- pushl %eax +- ret ++ /* load new code segment and jump to identity mapped page */ ++ movl %edi, %esi ++ xorl %eax, %eax ++ pushl %eax ++ pushl %esi ++ pushl %eax ++ movl $(gdt_cs - gdt), %eax ++ pushl %eax ++ movl %edi, %eax ++ addl $(identity_mapped - relocate_kernel),%eax ++ pushl %eax ++ iretl + + identity_mapped: + /* store the start address on the stack */ +@@ -250,3 +281,20 @@ identity_mapped: + xorl %edi, %edi + xorl %ebp, %ebp + ret ++ ++ .align 16 ++gdt: ++ .quad 0x0000000000000000 /* NULL descriptor */ ++gdt_cs: ++ .quad 0x00cf9a000000ffff /* kernel 4GB code at 0x00000000 */ ++gdt_ds: ++ .quad 0x00cf92000000ffff /* kernel 4GB data at 0x00000000 */ ++gdt_end: ++ ++gdt_48: ++ .word gdt_end - gdt - 1 /* limit */ ++ .long 0 /* base - filled in by code above */ ++ ++idt_48: ++ .word 0 /* limit */ ++ .long 0 /* base */ diff --git a/patches/linux-2.6.16.33/linux-2.6.19-rc1-kexec-move_segment_code-x86_64.patch b/patches/linux-2.6.16.33/linux-2.6.19-rc1-kexec-move_segment_code-x86_64.patch new file mode 100644 index 0000000000..1cf613284f --- /dev/null +++ b/patches/linux-2.6.16.33/linux-2.6.19-rc1-kexec-move_segment_code-x86_64.patch @@ -0,0 +1,161 @@ +kexec: Move asm segment handling code to the assembly file (x86_64) + +This patch moves the idt, gdt, and segment handling code from machine_kexec.c +to relocate_kernel.S. The main reason behind this move is to avoid code +duplication in the Xen hypervisor. With this patch all code required to kexec +is put on the control page. + +On top of that this patch also counts as a cleanup - I think it is much +nicer to write assembly directly in assembly files than wrap inline assembly +in C functions for no apparent reason. + +Signed-off-by: Magnus Damm +--- + + Applies to 2.6.19-rc1. + + machine_kexec.c | 58 ----------------------------------------------------- + relocate_kernel.S | 50 +++++++++++++++++++++++++++++++++++++++++---- + 2 files changed, 45 insertions(+), 63 deletions(-) + +--- 0002/arch/x86_64/kernel/machine_kexec.c ++++ work/arch/x86_64/kernel/machine_kexec.c 2006-10-05 16:15:49.000000000 +0900 +@@ -112,47 +112,6 @@ static int init_pgtable(struct kimage *i + return init_level4_page(image, level4p, 0, end_pfn << PAGE_SHIFT); + } + +-static void set_idt(void *newidt, u16 limit) +-{ +- struct desc_ptr curidt; +- +- /* x86-64 supports unaliged loads & stores */ +- curidt.size = limit; +- curidt.address = (unsigned long)newidt; +- +- __asm__ __volatile__ ( +- "lidtq %0\n" +- : : "m" (curidt) +- ); +-}; +- +- +-static void set_gdt(void *newgdt, u16 limit) +-{ +- struct desc_ptr curgdt; +- +- /* x86-64 supports unaligned loads & stores */ +- curgdt.size = limit; +- curgdt.address = (unsigned long)newgdt; +- +- __asm__ __volatile__ ( +- "lgdtq %0\n" +- : : "m" (curgdt) +- ); +-}; +- +-static void load_segments(void) +-{ +- __asm__ __volatile__ ( +- "\tmovl %0,%%ds\n" +- "\tmovl %0,%%es\n" +- "\tmovl %0,%%ss\n" +- "\tmovl %0,%%fs\n" +- "\tmovl %0,%%gs\n" +- : : "a" (__KERNEL_DS) : "memory" +- ); +-} +- + int machine_kexec_prepare(struct kimage *image) + { + unsigned long start_pgtable; +@@ -209,23 +168,6 @@ NORET_TYPE void machine_kexec(struct kim + page_list[PA_TABLE_PAGE] = + (unsigned long)__pa(page_address(image->control_code_page)); + +- /* The segment registers are funny things, they have both a +- * visible and an invisible part. Whenever the visible part is +- * set to a specific selector, the invisible part is loaded +- * with from a table in memory. At no other time is the +- * descriptor table in memory accessed. +- * +- * I take advantage of this here by force loading the +- * segments, before I zap the gdt with an invalid value. +- */ +- load_segments(); +- /* The gdt & idt are now invalid. +- * If you want to load them you must set up your own idt & gdt. +- */ +- set_gdt(phys_to_virt(0),0); +- set_idt(phys_to_virt(0),0); +- +- /* now call it */ + relocate_kernel((unsigned long)image->head, (unsigned long)page_list, + image->start); + } +--- 0002/arch/x86_64/kernel/relocate_kernel.S ++++ work/arch/x86_64/kernel/relocate_kernel.S 2006-10-05 16:18:07.000000000 +0900 +@@ -159,13 +159,39 @@ relocate_new_kernel: + movq PTR(PA_PGD)(%rsi), %r9 + movq %r9, %cr3 + ++ /* setup idt */ ++ movq %r8, %rax ++ addq $(idt_80 - relocate_kernel), %rax ++ lidtq (%rax) ++ ++ /* setup gdt */ ++ movq %r8, %rax ++ addq $(gdt - relocate_kernel), %rax ++ movq %r8, %r9 ++ addq $((gdt_80 - relocate_kernel) + 2), %r9 ++ movq %rax, (%r9) ++ ++ movq %r8, %rax ++ addq $(gdt_80 - relocate_kernel), %rax ++ lgdtq (%rax) ++ ++ /* setup data segment registers */ ++ xorl %eax, %eax ++ movl %eax, %ds ++ movl %eax, %es ++ movl %eax, %fs ++ movl %eax, %gs ++ movl %eax, %ss ++ + /* setup a new stack at the end of the physical control page */ + lea 4096(%r8), %rsp + +- /* jump to identity mapped page */ +- addq $(identity_mapped - relocate_kernel), %r8 +- pushq %r8 +- ret ++ /* load new code segment and jump to identity mapped page */ ++ movq %r8, %rax ++ addq $(identity_mapped - relocate_kernel), %rax ++ pushq $(gdt_cs - gdt) ++ pushq %rax ++ lretq + + identity_mapped: + /* store the start address on the stack */ +@@ -272,5 +298,19 @@ identity_mapped: + xorq %r13, %r13 + xorq %r14, %r14 + xorq %r15, %r15 +- + ret ++ ++ .align 16 ++gdt: ++ .quad 0x0000000000000000 /* NULL descriptor */ ++gdt_cs: ++ .quad 0x00af9a000000ffff ++gdt_end: ++ ++gdt_80: ++ .word gdt_end - gdt - 1 /* limit */ ++ .quad 0 /* base - filled in by code above */ ++ ++idt_80: ++ .word 0 /* limit */ ++ .quad 0 /* base */ diff --git a/patches/linux-2.6.16.32/net-csum.patch b/patches/linux-2.6.16.33/net-csum.patch similarity index 100% rename from patches/linux-2.6.16.32/net-csum.patch rename to patches/linux-2.6.16.33/net-csum.patch diff --git a/patches/linux-2.6.16.32/net-gso-0-base.patch b/patches/linux-2.6.16.33/net-gso-0-base.patch similarity index 100% rename from patches/linux-2.6.16.32/net-gso-0-base.patch rename to patches/linux-2.6.16.33/net-gso-0-base.patch diff --git a/patches/linux-2.6.16.32/net-gso-1-check-dodgy.patch b/patches/linux-2.6.16.33/net-gso-1-check-dodgy.patch similarity index 100% rename from patches/linux-2.6.16.32/net-gso-1-check-dodgy.patch rename to patches/linux-2.6.16.33/net-gso-1-check-dodgy.patch diff --git a/patches/linux-2.6.16.32/net-gso-2-checksum-fix.patch b/patches/linux-2.6.16.33/net-gso-2-checksum-fix.patch similarity index 100% rename from patches/linux-2.6.16.32/net-gso-2-checksum-fix.patch rename to patches/linux-2.6.16.33/net-gso-2-checksum-fix.patch diff --git a/patches/linux-2.6.16.32/net-gso-3-fix-errorcheck.patch b/patches/linux-2.6.16.33/net-gso-3-fix-errorcheck.patch similarity index 100% rename from patches/linux-2.6.16.32/net-gso-3-fix-errorcheck.patch rename to patches/linux-2.6.16.33/net-gso-3-fix-errorcheck.patch diff --git a/patches/linux-2.6.16.32/net-gso-4-kill-warnon.patch b/patches/linux-2.6.16.33/net-gso-4-kill-warnon.patch similarity index 100% rename from patches/linux-2.6.16.32/net-gso-4-kill-warnon.patch rename to patches/linux-2.6.16.33/net-gso-4-kill-warnon.patch diff --git a/patches/linux-2.6.16.32/net-gso-5-rcv-mss.patch b/patches/linux-2.6.16.33/net-gso-5-rcv-mss.patch similarity index 100% rename from patches/linux-2.6.16.32/net-gso-5-rcv-mss.patch rename to patches/linux-2.6.16.33/net-gso-5-rcv-mss.patch diff --git a/patches/linux-2.6.16.32/pci-mmconfig-fix-from-2.6.17.patch b/patches/linux-2.6.16.33/pci-mmconfig-fix-from-2.6.17.patch similarity index 100% rename from patches/linux-2.6.16.32/pci-mmconfig-fix-from-2.6.17.patch rename to patches/linux-2.6.16.33/pci-mmconfig-fix-from-2.6.17.patch diff --git a/patches/linux-2.6.16.32/pmd-shared.patch b/patches/linux-2.6.16.33/pmd-shared.patch similarity index 100% rename from patches/linux-2.6.16.32/pmd-shared.patch rename to patches/linux-2.6.16.33/pmd-shared.patch diff --git a/patches/linux-2.6.16.32/rcu_needs_cpu.patch b/patches/linux-2.6.16.33/rcu_needs_cpu.patch similarity index 100% rename from patches/linux-2.6.16.32/rcu_needs_cpu.patch rename to patches/linux-2.6.16.33/rcu_needs_cpu.patch diff --git a/patches/linux-2.6.16.32/rename-TSS_sysenter_esp0-SYSENTER_stack_esp0.patch b/patches/linux-2.6.16.33/rename-TSS_sysenter_esp0-SYSENTER_stack_esp0.patch similarity index 100% rename from patches/linux-2.6.16.32/rename-TSS_sysenter_esp0-SYSENTER_stack_esp0.patch rename to patches/linux-2.6.16.33/rename-TSS_sysenter_esp0-SYSENTER_stack_esp0.patch diff --git a/patches/linux-2.6.16.32/series b/patches/linux-2.6.16.33/series similarity index 67% rename from patches/linux-2.6.16.32/series rename to patches/linux-2.6.16.33/series index ef2c35842e..28fabf00ff 100644 --- a/patches/linux-2.6.16.32/series +++ b/patches/linux-2.6.16.33/series @@ -1,3 +1,9 @@ +git-2efe55a9cec8418f0e0cde3dc3787a42fddc4411.patch +git-2a8a3d5b65e86ec1dfef7d268c64a909eab94af7.patch +git-3566561bfadffcb5dbc85d576be80c0dbf2cccc9.patch +linux-2.6.19-rc1-kexec-move_segment_code-i386.patch +git-4bfaaef01a1badb9e8ffb0c0a37cd2379008d21f.patch +linux-2.6.19-rc1-kexec-move_segment_code-x86_64.patch blktap-aio-16_03_06.patch device_bind.patch fix-hz-suspend.patch @@ -22,6 +28,7 @@ xen-hotplug.patch xenoprof-generic.patch x86-put-note-sections-into-a-pt_note-segment-in-vmlinux.patch x86_64-put-note-sections-into-a-pt_note-segment-in-vmlinux.patch +git-dbaab49f92ff6ae6255762a948375e4036cbdbd2.patch x86-elfnote-as-preprocessor-macro.patch vsnprintf.patch kasprintf.patch diff --git a/patches/linux-2.6.16.32/smp-alts.patch b/patches/linux-2.6.16.33/smp-alts.patch similarity index 100% rename from patches/linux-2.6.16.32/smp-alts.patch rename to patches/linux-2.6.16.33/smp-alts.patch diff --git a/patches/linux-2.6.16.32/tpm_plugin_2.6.17.patch b/patches/linux-2.6.16.33/tpm_plugin_2.6.17.patch similarity index 100% rename from patches/linux-2.6.16.32/tpm_plugin_2.6.17.patch rename to patches/linux-2.6.16.33/tpm_plugin_2.6.17.patch diff --git a/patches/linux-2.6.16.32/vsnprintf.patch b/patches/linux-2.6.16.33/vsnprintf.patch similarity index 100% rename from patches/linux-2.6.16.32/vsnprintf.patch rename to patches/linux-2.6.16.33/vsnprintf.patch diff --git a/patches/linux-2.6.16.32/x86-elfnote-as-preprocessor-macro.patch b/patches/linux-2.6.16.33/x86-elfnote-as-preprocessor-macro.patch similarity index 100% rename from patches/linux-2.6.16.32/x86-elfnote-as-preprocessor-macro.patch rename to patches/linux-2.6.16.33/x86-elfnote-as-preprocessor-macro.patch diff --git a/patches/linux-2.6.16.32/x86-increase-interrupt-vector-range.patch b/patches/linux-2.6.16.33/x86-increase-interrupt-vector-range.patch similarity index 100% rename from patches/linux-2.6.16.32/x86-increase-interrupt-vector-range.patch rename to patches/linux-2.6.16.33/x86-increase-interrupt-vector-range.patch diff --git a/patches/linux-2.6.16.32/x86-put-note-sections-into-a-pt_note-segment-in-vmlinux.patch b/patches/linux-2.6.16.33/x86-put-note-sections-into-a-pt_note-segment-in-vmlinux.patch similarity index 100% rename from patches/linux-2.6.16.32/x86-put-note-sections-into-a-pt_note-segment-in-vmlinux.patch rename to patches/linux-2.6.16.33/x86-put-note-sections-into-a-pt_note-segment-in-vmlinux.patch diff --git a/patches/linux-2.6.16.32/x86_64-put-note-sections-into-a-pt_note-segment-in-vmlinux.patch b/patches/linux-2.6.16.33/x86_64-put-note-sections-into-a-pt_note-segment-in-vmlinux.patch similarity index 100% rename from patches/linux-2.6.16.32/x86_64-put-note-sections-into-a-pt_note-segment-in-vmlinux.patch rename to patches/linux-2.6.16.33/x86_64-put-note-sections-into-a-pt_note-segment-in-vmlinux.patch diff --git a/patches/linux-2.6.16.32/xen-hotplug.patch b/patches/linux-2.6.16.33/xen-hotplug.patch similarity index 100% rename from patches/linux-2.6.16.32/xen-hotplug.patch rename to patches/linux-2.6.16.33/xen-hotplug.patch diff --git a/patches/linux-2.6.16.32/xenoprof-generic.patch b/patches/linux-2.6.16.33/xenoprof-generic.patch similarity index 100% rename from patches/linux-2.6.16.32/xenoprof-generic.patch rename to patches/linux-2.6.16.33/xenoprof-generic.patch diff --git a/tools/Makefile b/tools/Makefile index fa9d63da16..b4156fc904 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -19,6 +19,8 @@ SUBDIRS-y += xenstat SUBDIRS-y += libaio SUBDIRS-y += blktap SUBDIRS-y += libfsimage +SUBDIRS-$(XENFB_TOOLS) += xenfb +SUBDIRS-$(LIBXENAPI_BINDINGS) += libxen # These don't cross-compile ifeq ($(XEN_COMPILE_ARCH),$(XEN_TARGET_ARCH)) diff --git a/tools/blktap/drivers/block-aio.c b/tools/blktap/drivers/block-aio.c index ca086b7faa..7f8dd5f773 100644 --- a/tools/blktap/drivers/block-aio.c +++ b/tools/blktap/drivers/block-aio.c @@ -311,12 +311,8 @@ int tdaio_do_callbacks(struct td_state *s, int sid) struct pending_aio *pio; pio = &prv->pending_aio[(long)io->data]; - - if (ep->res != io->u.c.nbytes) { - /* TODO: handle this case better. */ - DPRINTF("AIO did less than I asked it to. \n"); - } - rsp += pio->cb(s, ep->res2, pio->id, pio->private); + rsp += pio->cb(s, ep->res == io->u.c.nbytes ? 0 : 1, + pio->id, pio->private); prv->iocb_free[prv->iocb_free_count++] = io; } diff --git a/tools/blktap/drivers/block-qcow.c b/tools/blktap/drivers/block-qcow.c index d1c8777a2c..f7a57cd520 100644 --- a/tools/blktap/drivers/block-qcow.c +++ b/tools/blktap/drivers/block-qcow.c @@ -1145,13 +1145,6 @@ int tdqcow_do_callbacks(struct td_state *s, int sid) pio = &prv->pending_aio[(long)io->data]; - if (ep->res != io->u.c.nbytes) { - /* TODO: handle this case better. */ - ptr = (int *)&ep->res; - DPRINTF("AIO did less than I asked it to " - "[%lu,%lu,%d]\n", - ep->res, io->u.c.nbytes, *ptr); - } aio_unlock(prv, pio->sector); if (pio->id >= 0) { if (prv->crypt_method) @@ -1162,7 +1155,7 @@ int tdqcow_do_callbacks(struct td_state *s, int sid) &prv->aes_decrypt_key); prv->nr_reqs[pio->qcow_idx]--; if (prv->nr_reqs[pio->qcow_idx] == 0) - rsp += pio->cb(s, ep->res2, pio->id, + rsp += pio->cb(s, ep->res == io->u.c.nbytes ? 0 : 1, pio->id, pio->private); } else if (pio->id == -2) free(pio->buf); diff --git a/tools/blktap/drivers/tapdisk.c b/tools/blktap/drivers/tapdisk.c index 0c9b623abe..47d1e984a3 100644 --- a/tools/blktap/drivers/tapdisk.c +++ b/tools/blktap/drivers/tapdisk.c @@ -424,8 +424,7 @@ int send_responses(struct td_state *s, int res, int idx, void *private) } if (res != 0) { - DPRINTF("*** request error %d! \n", res); - return 0; + blkif->pending_list[idx].status = BLKIF_RSP_ERROR; } blkif->pending_list[idx].count--; diff --git a/tools/check/Makefile b/tools/check/Makefile index 4605c03c39..0b87aea259 100644 --- a/tools/check/Makefile +++ b/tools/check/Makefile @@ -1,3 +1,5 @@ +XEN_ROOT = ../.. +include $(XEN_ROOT)/tools/Rules.mk .PHONY: all all: build @@ -5,7 +7,7 @@ all: build # Check this machine is OK for building on. .PHONY: build build: - ./chk build + XENFB_TOOLS=$(XENFB_TOOLS) ./chk build # Check this machine is OK for installing on. # DO NOT use this check from 'make install' in the parent @@ -13,7 +15,7 @@ build: # copy rather than actually installing. .PHONY: install install: - ./chk install + XENFB_TOOLS=$(XENFB_TOOLS) ./chk install .PHONY: clean clean: diff --git a/tools/check/check_libvncserver b/tools/check/check_libvncserver new file mode 100755 index 0000000000..69efd063b3 --- /dev/null +++ b/tools/check/check_libvncserver @@ -0,0 +1,27 @@ +#!/bin/sh +# CHECK-BUILD CHECK-INSTALL + +if [ ! "$XENFB_TOOLS" = "y" ] +then + echo -n "unused, " + exit 0 +fi + +RC=0 + +LIBVNCSERVER_CONFIG="$(which libvncserver-config)" + +if test -z ${LIBVNCSERVER_CONFIG}; then + RC=1 +else + ${LIBVNCSERVER_CONFIG} --libs 2>&1 > /dev/null + RC=$? +fi + +if test $RC -ne 0; then + echo "FAILED" + echo " *** libvncserver-config is missing. " + echo " *** Please install libvncserver." +fi + +exit $RC diff --git a/tools/check/check_sdl b/tools/check/check_sdl new file mode 100755 index 0000000000..aac0130fe3 --- /dev/null +++ b/tools/check/check_sdl @@ -0,0 +1,27 @@ +#!/bin/sh +# CHECK-BUILD CHECK-INSTALL + +if [ ! "$XENFB_TOOLS" = "y" ] +then + echo -n "unused, " + exit 0 +fi + +RC=0 + +SDL_CONFIG="$(which sdl-config)" + +if test -z ${SDL_CONFIG}; then + RC=1 +else + ${SDL_CONFIG} --libs 2>&1 > /dev/null + RC=$? +fi + +if test $RC -ne 0; then + echo "FAILED" + echo " *** sdl-config is missing. " + echo " *** Please install libsdl-dev or sdl." +fi + +exit $RC diff --git a/tools/examples/init.d/xend b/tools/examples/init.d/xend index 8ad407637c..c12b1a0386 100755 --- a/tools/examples/init.d/xend +++ b/tools/examples/init.d/xend @@ -36,7 +36,10 @@ case "$1" in status) xend status ;; - restart|reload|force-reload) + reload) + xend reload + ;; + restart|force-reload) xend restart await_daemons_up ;; diff --git a/tools/examples/init.d/xendomains b/tools/examples/init.d/xendomains index b3722882d6..7aca157bc9 100644 --- a/tools/examples/init.d/xendomains +++ b/tools/examples/init.d/xendomains @@ -204,12 +204,14 @@ start() return; fi + saved_domains=" " if [ "$XENDOMAINS_RESTORE" = "true" ] && contains_something "$XENDOMAINS_SAVE" then mkdir -p $(dirname "$LOCKFILE") touch $LOCKFILE echo -n "Restoring Xen domains:" + saved_domains=`ls $XENDOMAINS_SAVE` for dom in $XENDOMAINS_SAVE/*; do echo -n " ${dom##*/}" xm restore $dom @@ -234,9 +236,14 @@ start() # Create all domains with config files in XENDOMAINS_AUTO. # TODO: We should record which domain name belongs # so we have the option to selectively shut down / migrate later + # If a domain statefile from $XENDOMAINS_SAVE matches a domain name + # in $XENDOMAINS_AUTO, do not try to start that domain; if it didn't + # restore correctly it requires administrative attention. for dom in $XENDOMAINS_AUTO/*; do echo -n " ${dom##*/}" - if is_running $dom; then + shortdom=$(echo $dom | sed -n 's/^.*\/\(.*\)$/\1/p') + echo $saved_domains | grep -w $shortdom > /dev/null + if [ $? -eq 0 ] || is_running $dom; then echo -n "(skip)" else xm create --quiet --defconfig $dom diff --git a/tools/examples/network-route b/tools/examples/network-route index cb217c068d..de535c3280 100755 --- a/tools/examples/network-route +++ b/tools/examples/network-route @@ -16,4 +16,12 @@ # #============================================================================ +dir=$(dirname "$0") +. "$dir/xen-script-common.sh" + +evalVariables "$@" + +netdev=${netdev:-eth${vifnum}} + echo 1 >/proc/sys/net/ipv4/ip_forward +echo 1 >/proc/sys/net/ipv4/conf/${netdev}/proxy_arp diff --git a/tools/examples/xm-config.xml b/tools/examples/xm-config.xml new file mode 100644 index 0000000000..dbd8778bae --- /dev/null +++ b/tools/examples/xm-config.xml @@ -0,0 +1,43 @@ + + + + + + + + diff --git a/tools/firmware/hvmloader/Makefile b/tools/firmware/hvmloader/Makefile index 4e4502d673..763f472987 100644 --- a/tools/firmware/hvmloader/Makefile +++ b/tools/firmware/hvmloader/Makefile @@ -18,29 +18,24 @@ # Place - Suite 330, Boston, MA 02111-1307 USA. # -# External CFLAGS can do more harm than good. -CFLAGS := - override XEN_TARGET_ARCH = x86_32 XEN_ROOT = ../../.. -include $(XEN_ROOT)/Config.mk +CFLAGS := -I$(XEN_ROOT)/tools/libxc -I. +include $(XEN_ROOT)/tools/Rules.mk # The HVM loader is started in 32-bit mode at the address below: LOADADDR = 0x100000 -DEFINES =-DDEBUG -XENINC =-I$(XEN_ROOT)/tools/libxc - # Disable PIE/SSP if GCC supports them. They can break us. CFLAGS += $(call cc-option,$(CC),-nopie,) CFLAGS += $(call cc-option,$(CC),-fno-stack-protector,) CFLAGS += $(call cc-option,$(CC),-fno-stack-protector-all,) OBJCOPY = objcopy -CFLAGS += $(DEFINES) -I. $(XENINC) -fno-builtin -O2 -msoft-float +CFLAGS += -fno-builtin -O2 -msoft-float LDFLAGS = -nostdlib -Wl,-N -Wl,-Ttext -Wl,$(LOADADDR) -SRCS = hvmloader.c mp_tables.c util.c smbios.c acpi_utils.c +SRCS = hvmloader.c mp_tables.c util.c smbios.c OBJS = $(patsubst %.c,%.o,$(SRCS)) .PHONY: all @@ -65,5 +60,5 @@ roms.h: ../rombios/BIOS-bochs-latest ../vgabios/VGABIOS-lgpl-latest.bin ../vgabi .PHONY: clean clean: rm -f roms.h acpi.h - rm -f hvmloader hvmloader.tmp hvmloader.o $(OBJS) + rm -f hvmloader hvmloader.tmp *.o $(MAKE) -C acpi clean diff --git a/tools/firmware/hvmloader/acpi/Makefile b/tools/firmware/hvmloader/acpi/Makefile index 9aec04735a..16803d78d6 100644 --- a/tools/firmware/hvmloader/acpi/Makefile +++ b/tools/firmware/hvmloader/acpi/Makefile @@ -27,15 +27,28 @@ OBJS = $(patsubst %.c,%.o,$(C_SRC)) IASL_VER = acpica-unix-20050513 IASL_URL = http://developer.intel.com/technology/iapc/acpi/downloads/$(IASL_VER).tar.gz +# Disable PIE/SSP if GCC supports them. They can break us. +CFLAGS += $(call cc-option,$(CC),-nopie,) +CFLAGS += $(call cc-option,$(CC),-fno-stack-protector,) +CFLAGS += $(call cc-option,$(CC),-fno-stack-protector-all,) + +CFLAGS += -fno-builtin -O2 -msoft-float + vpath iasl $(PATH) all: acpi.a +ssdt_tpm.h: ssdt_tpm.asl + $(MAKE) iasl + iasl -tc ssdt_tpm.asl + mv ssdt_tpm.hex ssdt_tpm.h + rm -f *.aml + dsdt.c: dsdt.asl $(MAKE) iasl iasl -tc dsdt.asl mv dsdt.hex dsdt.c echo "int DsdtLen=sizeof(AmlCode);" >> dsdt.c - rm *.aml + rm -f *.aml iasl: @echo diff --git a/tools/firmware/hvmloader/acpi/acpi2_0.h b/tools/firmware/hvmloader/acpi/acpi2_0.h index 62cb16eeb8..5b9b9f0495 100644 --- a/tools/firmware/hvmloader/acpi/acpi2_0.h +++ b/tools/firmware/hvmloader/acpi/acpi2_0.h @@ -119,7 +119,7 @@ struct acpi_20_rsdp { */ struct acpi_20_rsdt { struct acpi_header header; - uint32_t entry[ACPI_MAX_NUM_TABLES]; + uint32_t entry[1]; }; #define ACPI_2_0_RSDT_REVISION 0x01 @@ -128,20 +128,19 @@ struct acpi_20_rsdt { */ struct acpi_20_xsdt { struct acpi_header header; - uint64_t entry[ACPI_MAX_NUM_TABLES]; + uint64_t entry[1]; }; #define ACPI_2_0_XSDT_REVISION 0x01 /* * TCG Hardware Interface Table (TCPA) */ - -typedef struct _ACPI_2_0_TCPA_CLIENT { +struct acpi_20_tcpa { struct acpi_header header; - uint16_t PlatformClass; - uint32_t LAML; - uint64_t LASA; -} ACPI_2_0_TCPA_CLIENT; + uint16_t platform_class; + uint32_t laml; + uint64_t lasa; +}; #define ACPI_2_0_TCPA_REVISION 0x02 #define ACPI_2_0_TCPA_LAML_SIZE (64*1024) diff --git a/tools/firmware/hvmloader/acpi/build.c b/tools/firmware/hvmloader/acpi/build.c index 68b1e18bb0..b221391807 100644 --- a/tools/firmware/hvmloader/acpi/build.c +++ b/tools/firmware/hvmloader/acpi/build.c @@ -17,8 +17,12 @@ */ #include "acpi2_0.h" +#include "ssdt_tpm.h" #include "../config.h" #include "../util.h" +#include + +#define align16(sz) (((sz) + 15) & ~15) extern struct acpi_20_rsdp Rsdp; extern struct acpi_20_rsdt Rsdt; @@ -103,39 +107,88 @@ int construct_madt(struct acpi_20_madt *madt) madt->header.length = offset; set_checksum(madt, offsetof(struct acpi_header, checksum), offset); - return offset; + return align16(offset); } -/* - * Copy all the ACPI table to buffer. - * Buffer layout: FACS, DSDT, FADT, MADT, XSDT, RSDT, RSDP. - */ +int construct_secondary_tables(uint8_t *buf, unsigned long *table_ptrs) +{ + int offset = 0, nr_tables = 0; + struct acpi_20_madt *madt; + struct acpi_20_tcpa *tcpa; + static const uint16_t tis_signature[] = {0x0001, 0x0001, 0x0001}; + uint16_t *tis_hdr; + + /* MADT. */ + if ( (get_vcpu_nr() > 1) || get_apic_mode() ) + { + madt = (struct acpi_20_madt *)&buf[offset]; + offset += construct_madt(madt); + table_ptrs[nr_tables++] = (unsigned long)madt; + } + + /* TPM TCPA and SSDT. */ + tis_hdr = (uint16_t *)0xFED40F00; + if ( (tis_hdr[0] == tis_signature[0]) && + (tis_hdr[1] == tis_signature[1]) && + (tis_hdr[2] == tis_signature[2]) ) + { + memcpy(&buf[offset], AmlCode_TPM, sizeof(AmlCode_TPM)); + table_ptrs[nr_tables++] = (unsigned long)&buf[offset]; + offset += align16(sizeof(AmlCode_TPM)); + + tcpa = (struct acpi_20_tcpa *)&buf[offset]; + memset(tcpa, 0, sizeof(*tcpa)); + offset += align16(sizeof(*tcpa)); + table_ptrs[nr_tables++] = (unsigned long)tcpa; + + tcpa->header.signature = ACPI_2_0_TCPA_SIGNATURE; + tcpa->header.length = sizeof(*tcpa); + tcpa->header.revision = ACPI_2_0_TCPA_REVISION; + strncpy(tcpa->header.oem_id, "IBM ", 6); + tcpa->header.oem_table_id = ASCII64(' ', ' ', ' ', ' ', + ' ', 'x', 'e', 'n'); + tcpa->header.oem_revision = 1; + tcpa->header.creator_id = ASCII32('I', 'B', 'M', ' '); + tcpa->header.creator_revision = 1; + tcpa->lasa = e820_malloc( + ACPI_2_0_TCPA_LAML_SIZE, E820_RESERVED, (uint32_t)~0); + if ( tcpa->lasa ) + { + tcpa->laml = ACPI_2_0_TCPA_LAML_SIZE; + memset((char *)(unsigned long)tcpa->lasa, 0, tcpa->laml); + set_checksum(tcpa, + offsetof(struct acpi_header, checksum), + tcpa->header.length); + } + } + + table_ptrs[nr_tables] = 0; + return align16(offset); +} + +/* Copy all the ACPI table to buffer. */ int acpi_build_tables(uint8_t *buf) { struct acpi_20_rsdp *rsdp; struct acpi_20_rsdt *rsdt; struct acpi_20_xsdt *xsdt; struct acpi_20_fadt *fadt; - struct acpi_20_madt *madt = 0; struct acpi_20_facs *facs; unsigned char *dsdt; - int offset = 0, requires_madt; - - requires_madt = ((get_vcpu_nr() > 1) || get_apic_mode()); - -#define inc_offset(sz) (offset = (offset + (sz) + 15) & ~15) + unsigned long secondary_tables[16]; + int offset = 0, i; facs = (struct acpi_20_facs *)&buf[offset]; memcpy(facs, &Facs, sizeof(struct acpi_20_facs)); - inc_offset(sizeof(struct acpi_20_facs)); + offset += align16(sizeof(struct acpi_20_facs)); dsdt = (unsigned char *)&buf[offset]; memcpy(dsdt, &AmlCode, DsdtLen); - inc_offset(DsdtLen); + offset += align16(DsdtLen); fadt = (struct acpi_20_fadt *)&buf[offset]; memcpy(fadt, &Fadt, sizeof(struct acpi_20_fadt)); - inc_offset(sizeof(struct acpi_20_fadt)); + offset += align16(sizeof(struct acpi_20_fadt)); fadt->dsdt = (unsigned long)dsdt; fadt->x_dsdt = (unsigned long)dsdt; fadt->firmware_ctrl = (unsigned long)facs; @@ -144,43 +197,33 @@ int acpi_build_tables(uint8_t *buf) offsetof(struct acpi_header, checksum), sizeof(struct acpi_20_fadt)); - if ( requires_madt ) - { - madt = (struct acpi_20_madt *)&buf[offset]; - inc_offset(construct_madt(madt)); - } + offset += construct_secondary_tables(&buf[offset], secondary_tables); xsdt = (struct acpi_20_xsdt *)&buf[offset]; - memcpy(xsdt, &Xsdt, sizeof(struct acpi_20_xsdt)); - inc_offset(sizeof(struct acpi_20_xsdt)); + memcpy(xsdt, &Xsdt, sizeof(struct acpi_header)); xsdt->entry[0] = (unsigned long)fadt; - xsdt->header.length = sizeof(struct acpi_header) + sizeof(uint64_t); - if ( requires_madt ) - { - xsdt->entry[1] = (unsigned long)madt; - xsdt->header.length += sizeof(uint64_t); - } + for ( i = 0; secondary_tables[i]; i++ ) + xsdt->entry[i+1] = secondary_tables[i]; + xsdt->header.length = sizeof(struct acpi_header) + (i+1)*sizeof(uint64_t); + offset += align16(xsdt->header.length); set_checksum(xsdt, offsetof(struct acpi_header, checksum), xsdt->header.length); rsdt = (struct acpi_20_rsdt *)&buf[offset]; - memcpy(rsdt, &Rsdt, sizeof(struct acpi_20_rsdt)); - inc_offset(sizeof(struct acpi_20_rsdt)); + memcpy(rsdt, &Rsdt, sizeof(struct acpi_header)); rsdt->entry[0] = (unsigned long)fadt; - rsdt->header.length = sizeof(struct acpi_header) + sizeof(uint32_t); - if ( requires_madt ) - { - rsdt->entry[1] = (unsigned long)madt; - rsdt->header.length += sizeof(uint32_t); - } + for ( i = 0; secondary_tables[i]; i++ ) + rsdt->entry[i+1] = secondary_tables[i]; + rsdt->header.length = sizeof(struct acpi_header) + (i+1)*sizeof(uint32_t); + offset += align16(rsdt->header.length); set_checksum(rsdt, offsetof(struct acpi_header, checksum), rsdt->header.length); rsdp = (struct acpi_20_rsdp *)&buf[offset]; memcpy(rsdp, &Rsdp, sizeof(struct acpi_20_rsdp)); - inc_offset(sizeof(struct acpi_20_rsdp)); + offset += align16(sizeof(struct acpi_20_rsdp)); rsdp->rsdt_address = (unsigned long)rsdt; rsdp->xsdt_address = (unsigned long)xsdt; set_checksum(rsdp, @@ -190,8 +233,6 @@ int acpi_build_tables(uint8_t *buf) offsetof(struct acpi_20_rsdp, extended_checksum), sizeof(struct acpi_20_rsdp)); -#undef inc_offset - return offset; } diff --git a/tools/firmware/hvmloader/acpi_ssdt_tpm.asl b/tools/firmware/hvmloader/acpi/ssdt_tpm.asl similarity index 100% rename from tools/firmware/hvmloader/acpi_ssdt_tpm.asl rename to tools/firmware/hvmloader/acpi/ssdt_tpm.asl diff --git a/tools/firmware/hvmloader/acpi_ssdt_tpm.h b/tools/firmware/hvmloader/acpi/ssdt_tpm.h similarity index 100% rename from tools/firmware/hvmloader/acpi_ssdt_tpm.h rename to tools/firmware/hvmloader/acpi/ssdt_tpm.h diff --git a/tools/firmware/hvmloader/acpi_utils.c b/tools/firmware/hvmloader/acpi_utils.c deleted file mode 100644 index 5fd9847d4e..0000000000 --- a/tools/firmware/hvmloader/acpi_utils.c +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Commonly used ACPI utility functions. - * Probing for devices and writing SSDT entries into XSDT and RSDT tables. - * - * Yu Ke, ke.yu@intel.com - * Copyright (c) 2005, Intel Corporation. - * Copyright (c) 2006, IBM Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - */ - -#include "acpi/acpi2_0.h" -#include "acpi_utils.h" -#include "util.h" -#include - -static int acpi_rsdt_add_entry_pointer(unsigned char *acpi_start, - unsigned char *entry); -static int acpi_xsdt_add_entry_pointer(unsigned char *acpi_start, - unsigned char *entry); -static unsigned char *acpi_xsdt_add_entry(unsigned char *acpi_start, - unsigned char **freemem, - unsigned char *limit, - unsigned char *table, - unsigned int table_size); - -void set_checksum(void *start, int checksum_offset, int len) -{ - unsigned char sum = 0; - unsigned char *ptr; - - ptr = start; - ptr[checksum_offset] = 0; - while ( len-- ) - sum += *ptr++; - - ptr = start; - ptr[checksum_offset] = -sum; -} - - -#include "acpi_ssdt_tpm.h" -static void acpi_tpm_tis_probe(unsigned char *acpi_start, - unsigned char **freemem, - unsigned char *limit) -{ - unsigned char *addr; - ACPI_2_0_TCPA_CLIENT *tcpa; - /* check TPM_DID, TPM_VID, TPM_RID in ioemu/hw/tpm_tis.c */ - uint16_t tis_did_vid_rid[] = {0x0001, 0x0001, 0x0001}; - static const ACPI_2_0_TCPA_CLIENT Tcpa = { - .header = { - .signature = ACPI_2_0_TCPA_SIGNATURE, - .length = sizeof(ACPI_2_0_TCPA_CLIENT), - .revision = ACPI_2_0_TCPA_REVISION, - .oem_id = {'I', 'B', 'M', ' ', ' ', ' '}, - .oem_table_id = ASCII64(' ', ' ', ' ', ' ', ' ', 'x', 'e', 'n'), - .oem_revision = 1, - .creator_id = ASCII32('I', 'B', 'M', ' '), - .creator_revision = 1, - } - }; - - /* probe for TIS interface ... */ - if ( memcmp((char *)(0xFED40000 + 0xF00), - tis_did_vid_rid, - sizeof(tis_did_vid_rid)) != 0 ) - return; - - printf("TIS is available\n"); - addr = acpi_xsdt_add_entry(acpi_start, freemem, limit, - AmlCode_TPM, sizeof(AmlCode_TPM)); - if ( addr == NULL ) - return; - - /* legacy systems need an RSDT entry */ - if ( acpi_rsdt_add_entry_pointer(acpi_start, addr) != 1 ) - return; - - /* add ACPI TCPA table */ - addr = acpi_xsdt_add_entry(acpi_start, freemem, limit, - (unsigned char *)&Tcpa, - sizeof(Tcpa)); - if ( addr == NULL ) - return; - - tcpa = (ACPI_2_0_TCPA_CLIENT *)addr; - tcpa->LASA = e820_malloc( - ACPI_2_0_TCPA_LAML_SIZE, E820_RESERVED, (uint32_t)~0); - if ( tcpa->LASA ) - { - tcpa->LAML = ACPI_2_0_TCPA_LAML_SIZE; - memset((char *)(unsigned long)tcpa->LASA, - 0x0, - tcpa->LAML); - set_checksum(tcpa, - FIELD_OFFSET(struct acpi_header, checksum), - tcpa->header.length); - } - - if ( acpi_rsdt_add_entry_pointer(acpi_start, addr) != 1 ) - return; -} - - -/* - * Call functions that probe for devices and have them register their - * SSDT entries with the XSDT and RSDT tables. - */ -void acpi_update(unsigned char *acpi_start, - unsigned long acpi_size, - unsigned char *limit, - unsigned char **freemem) -{ - acpi_tpm_tis_probe(acpi_start, freemem, limit); -} - - -/* - * Search for the RSDP in memory below the BIOS - */ -struct acpi_20_rsdp *acpi_rsdp_get(unsigned char *acpi_start) -{ - int offset = 0; - int found = 0; - static int displayed = 0; - struct acpi_20_rsdp *rsdp; - - while ( &acpi_start[offset] < (unsigned char *)0xf0000 ) - { - rsdp = (struct acpi_20_rsdp *)&acpi_start[offset]; - if ( rsdp->signature == ACPI_2_0_RSDP_SIGNATURE ) - { - found = 1; - break; - } - offset += 0x10; - } - - if ( !found ) - rsdp = NULL; - - if ( !displayed ) - { - if ( rsdp ) - printf("Found RSDP at %lx\n",(long)rsdp); - else - printf("ERROR: RSDP was not found\n"); - displayed = 1; - } - - return rsdp; -} - - -struct acpi_20_rsdt *acpi_rsdt_get(unsigned char *acpi_start) -{ - struct acpi_20_rsdp *rsdp; - struct acpi_20_rsdt *rsdt; - - rsdp = acpi_rsdp_get(acpi_start); - if (!rsdp) - return NULL; - - rsdt = (struct acpi_20_rsdt *) - (acpi_start + rsdp->rsdt_address - ACPI_PHYSICAL_ADDRESS); - if ( rsdt->header.signature != ACPI_2_0_RSDT_SIGNATURE ) - { - printf("Bad RSDT signature\n"); - return NULL; - } - - return rsdt; -} - -/* - * Add an entry to the RSDT table given the pointer to the entry. - */ -static int acpi_rsdt_add_entry_pointer(unsigned char *acpi_start, - unsigned char *entry) -{ - struct acpi_20_rsdt *rsdt = acpi_rsdt_get(acpi_start); - int found = 0; - int i = 0; - - /* Find an empty slot in the RSDT table. */ - while ( i < ACPI_MAX_NUM_TABLES ) - { - if ( rsdt->entry[i] == 0 ) - { - found = 1; - break; - } - i++; - } - - if ( found ) - { - rsdt->entry[i] = (uint64_t)(unsigned long)entry; - rsdt->header.length = - sizeof(struct acpi_header) + - (i + 1) * sizeof(uint64_t); - set_checksum(rsdt, - FIELD_OFFSET(struct acpi_header, checksum), - rsdt->header.length); - } - - return found; -} - -/* Get the XSDT table. */ -struct acpi_20_xsdt *acpi_xsdt_get(unsigned char *acpi_start) -{ - struct acpi_20_rsdp *rsdp; - struct acpi_20_xsdt *xsdt; - - rsdp = acpi_rsdp_get(acpi_start); - if (!rsdp) - return NULL; - - xsdt = (struct acpi_20_xsdt *) - (acpi_start + rsdp->xsdt_address - ACPI_PHYSICAL_ADDRESS); - if ( xsdt->header.signature != ACPI_2_0_XSDT_SIGNATURE ) - { - printf("Bad XSDT signature\n"); - return NULL; - } - return xsdt; -} - -/* - * Add an entry to the XSDT table given the pointer to the entry. - */ -static int acpi_xsdt_add_entry_pointer(unsigned char *acpi_start, - unsigned char *entry) -{ - struct acpi_20_xsdt *xsdt = acpi_xsdt_get(acpi_start); - int found = 0; - int i = 0; - - /* Find an empty slot in the XSDT table. */ - while ( i < ACPI_MAX_NUM_TABLES ) - { - if ( xsdt->entry[i] == 0 ) - { - found = 1; - break; - } - i++; - } - - if ( found ) - { - xsdt->entry[i] = (uint64_t)(unsigned long)entry; - xsdt->header.length = - sizeof(struct acpi_header) + - (i + 1) * sizeof(uint64_t); - set_checksum(xsdt, - FIELD_OFFSET(struct acpi_header, checksum), - xsdt->header.length); - } - - return found; -} - -/* - add an entry to the xdst table entry pointers - copy the given ssdt data to the current available memory at - freemem, if it does not exceed the limit - */ -static unsigned char *acpi_xsdt_add_entry(unsigned char *acpi_start, - unsigned char **freemem, - unsigned char *limit, - unsigned char *table, - unsigned int table_size) -{ - struct acpi_20_xsdt *xsdt = acpi_xsdt_get(acpi_start); - int found = 0, i = 0; - unsigned char *addr = NULL; - - /* Check for an empty slot in the Xsdt table. */ - while ( i < ACPI_MAX_NUM_TABLES ) - { - if ( xsdt->entry[i] == 0 ) - { - found = 1; - break; - } - i++; - } - - if ( found ) - { - /* memory below hard limit ? */ - if ( (*freemem + table_size) <= limit ) - { - addr = *freemem; - memcpy(addr, table, table_size); - printf("Copied dyn. ACPI entry to %lx\n",(long)addr); - *freemem += ((table_size + 0xf) & ~0xf); - - acpi_xsdt_add_entry_pointer(acpi_start, addr); - } - } - - return addr; -} diff --git a/tools/firmware/hvmloader/acpi_utils.h b/tools/firmware/hvmloader/acpi_utils.h deleted file mode 100644 index 814fa3229d..0000000000 --- a/tools/firmware/hvmloader/acpi_utils.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Commonly used ACPI utility functions. - * - * Yu Ke, ke.yu@intel.com - * Copyright (c) 2005, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307 USA. - */ -#ifndef ACPI_UTILS_H -#define ACPI_UTILS_H - -#define FIELD_OFFSET(TYPE,Field) ((unsigned int)(&(((TYPE *) 0)->Field))) - -#define NULL ((void*)0) - -void set_checksum(void *start, int checksum_offset, int len); -void acpi_update(unsigned char *acpi_start, - unsigned long acpi_size, - unsigned char *limit, - unsigned char **freemem); - -struct acpi_20_rsdt *acpi_rsdt_get(unsigned char *acpi_start); -struct acpi_20_xsdt *acpi_xsdt_get(unsigned char *acpi_start); - -#endif diff --git a/tools/firmware/hvmloader/hvmloader.c b/tools/firmware/hvmloader/hvmloader.c index 66c113ec7c..b7729ed85d 100644 --- a/tools/firmware/hvmloader/hvmloader.c +++ b/tools/firmware/hvmloader/hvmloader.c @@ -23,7 +23,6 @@ #include "acpi/acpi2_0.h" /* for ACPI_PHYSICAL_ADDRESS */ #include "hypercall.h" #include "util.h" -#include "acpi_utils.h" #include "smbios.h" #include "config.h" #include "apic_regs.h" @@ -283,7 +282,6 @@ static void pci_setup(void) int main(void) { int acpi_sz; - uint8_t *freemem; printf("HVM Loader\n"); @@ -318,12 +316,7 @@ int main(void) { printf("Loading ACPI ...\n"); acpi_sz = acpi_build_tables((uint8_t *)ACPI_PHYSICAL_ADDRESS); - freemem = (uint8_t *)ACPI_PHYSICAL_ADDRESS + acpi_sz; - ASSERT(freemem <= (uint8_t *)0xF0000); - acpi_update((unsigned char *)ACPI_PHYSICAL_ADDRESS, - freemem - (uint8_t *)ACPI_PHYSICAL_ADDRESS, - (unsigned char *)0xF0000, - &freemem); + ASSERT((ACPI_PHYSICAL_ADDRESS + acpi_sz) <= 0xF0000); } if ( check_amd() ) diff --git a/tools/firmware/hvmloader/mp_tables.c b/tools/firmware/hvmloader/mp_tables.c index 9a1238e51a..f763d77f6b 100644 --- a/tools/firmware/hvmloader/mp_tables.c +++ b/tools/firmware/hvmloader/mp_tables.c @@ -28,7 +28,6 @@ * Place - Suite 330, Boston, MA 02111-1307 USA. */ -#include #include "config.h" /* FIXME find a header that already has types defined!!! */ diff --git a/tools/firmware/hvmloader/util.h b/tools/firmware/hvmloader/util.h index 4b2f874ab3..9dac8b5343 100644 --- a/tools/firmware/hvmloader/util.h +++ b/tools/firmware/hvmloader/util.h @@ -6,6 +6,9 @@ #undef offsetof #define offsetof(t, m) ((unsigned long)&((t *)0)->m) +#undef NULL +#define NULL ((void*)0) + extern void __assert_failed(char *assertion, char *file, int line) __attribute__((noreturn)); #define ASSERT(p) \ diff --git a/tools/firmware/vmxassist/head.S b/tools/firmware/vmxassist/head.S index a4cb614c68..7cb4ec1069 100644 --- a/tools/firmware/vmxassist/head.S +++ b/tools/firmware/vmxassist/head.S @@ -130,7 +130,7 @@ _start: clts /* setup my own stack */ - movl $stack_top - 4*4, %esp + movl $stack_top, %esp movl %esp, %ebp /* go ... */ diff --git a/tools/firmware/vmxassist/setup.c b/tools/firmware/vmxassist/setup.c index 4f82ca0d9e..9f8928ff0d 100644 --- a/tools/firmware/vmxassist/setup.c +++ b/tools/firmware/vmxassist/setup.c @@ -66,7 +66,7 @@ struct vmx_assist_context newctx; unsigned long memory_size; int initialize_real_mode; -extern char stack[], stack_top[]; +extern char stack_top[]; extern unsigned trap_handlers[]; void @@ -125,7 +125,7 @@ setup_gdt(void) /* setup task state segment */ memset(&tss, 0, sizeof(tss)); tss.ss0 = DATA_SELECTOR; - tss.esp0 = (unsigned) stack_top - 4*4; + tss.esp0 = (unsigned) stack_top; tss.iomap_base = offsetof(struct tss, iomap); /* initialize gdt's tss selector */ @@ -201,7 +201,7 @@ void enter_real_mode(struct regs *regs) { /* mask off TSS busy bit */ - gdt[TSS_SELECTOR / sizeof(gdt[0])] &= ~0x0000020000000000ULL; + gdt[TSS_SELECTOR / sizeof(gdt[0])] &= ~0x0000020000000000ULL; /* start 8086 emulation of BIOS */ if (initialize_real_mode) { @@ -219,8 +219,10 @@ enter_real_mode(struct regs *regs) regs->cs = booting_vector << 8; /* AP entry point */ regs->eip = 0; } - regs->uesp = 0; - regs->uss = 0; + + regs->uesp = regs->uss = 0; + regs->eax = regs->ecx = regs->edx = regs->ebx = 0; + regs->esp = regs->ebp = regs->esi = regs->edi = 0; /* intercept accesses to the PIC */ setiomap(PIC_MASTER+PIC_CMD); @@ -236,14 +238,12 @@ enter_real_mode(struct regs *regs) /* this should get us into 16-bit mode */ return; - } else { - /* go from protected to real mode */ - regs->eflags |= EFLAGS_VM; - - set_mode(regs, VM86_PROTECTED_TO_REAL); - - emulate(regs); } + + /* go from protected to real mode */ + regs->eflags |= EFLAGS_VM; + set_mode(regs, VM86_PROTECTED_TO_REAL); + emulate(regs); } /* @@ -258,7 +258,7 @@ setup_ctx(void) memset(c, 0, sizeof(*c)); c->eip = (unsigned long) switch_to_real_mode; - c->esp = (unsigned) stack_top - 4*4; + c->esp = (unsigned) stack_top; c->eflags = 0x2; /* no interrupts, please */ /* diff --git a/tools/firmware/vmxassist/trap.S b/tools/firmware/vmxassist/trap.S index 30e87adb85..d5ece3e143 100644 --- a/tools/firmware/vmxassist/trap.S +++ b/tools/firmware/vmxassist/trap.S @@ -100,10 +100,6 @@ trap_handlers: .code32 .align 16 common_trap: /* common trap handler */ - pushl %gs - pushl %fs - pushl %ds - pushl %es pushal movl $(DATA_SELECTOR), %eax /* make sure these are sane */ @@ -114,17 +110,13 @@ common_trap: /* common trap handler */ movl %esp, %ebp pushl %ebp - pushl 52(%ebp) - pushl 48(%ebp) + pushl 36(%ebp) + pushl 32(%ebp) call trap /* trap(trapno, errno, regs) */ addl $12, %esp trap_return: popal - popl %es - popl %ds - popl %fs - popl %gs addl $8, %esp /* skip trapno, errno */ iret /* NOT REACHED */ @@ -152,10 +144,6 @@ switch_to_real_mode: pushl oldctx+VMX_ASSIST_CTX_EIP pushl $-1 /* trapno, errno */ pushl $-1 - pushl %gs - pushl %fs - pushl %ds - pushl %es pushal movl %esp, %ebp diff --git a/tools/firmware/vmxassist/util.c b/tools/firmware/vmxassist/util.c index 6ae4b25f79..2c4c016989 100644 --- a/tools/firmware/vmxassist/util.c +++ b/tools/firmware/vmxassist/util.c @@ -62,17 +62,15 @@ dump_regs(struct regs *regs) regs->eax, regs->ecx, regs->edx, regs->ebx); printf("esp %8x ebp %8x esi %8x edi %8x\n", regs->esp, regs->ebp, regs->esi, regs->edi); - printf("es %8x ds %8x fs %8x gs %8x\n", - regs->es, regs->ds, regs->fs, regs->gs); printf("trapno %8x errno %8x\n", regs->trapno, regs->errno); printf("eip %8x cs %8x eflags %8x\n", regs->eip, regs->cs, regs->eflags); - printf("uesp %8x uss %8x \n", + printf("uesp %8x uss %8x\n", regs->uesp, regs->uss); printf("ves %8x vds %8x vfs %8x vgs %8x\n", regs->ves, regs->vds, regs->vfs, regs->vgs); - printf("cr0 %8lx cr2 %8x cr3 %8lx cr4 %8lx\n", + printf("cr0 %8lx cr2 %8x cr3 %8lx cr4 %8lx\n\n", (long)oldctx.cr0, get_cr2(), (long)oldctx.cr3, (long)oldctx.cr4); } diff --git a/tools/firmware/vmxassist/vm86.c b/tools/firmware/vmxassist/vm86.c index 5bae53ed6d..7a76ad8b15 100644 --- a/tools/firmware/vmxassist/vm86.c +++ b/tools/firmware/vmxassist/vm86.c @@ -1,6 +1,6 @@ /* * vm86.c: A vm86 emulator. The main purpose of this emulator is to do as - * little work as possible. + * little work as possible. * * Leendert van Doorn, leendert@watson.ibm.com * Copyright (c) 2005-2006, International Business Machines Corporation. @@ -52,8 +52,8 @@ char *states[] = { static char *rnames[] = { "ax", "cx", "dx", "bx", "sp", "bp", "si", "di" }; #endif /* DEBUG */ -#define PDE_PS (1 << 7) -#define PT_ENTRY_PRESENT 0x1 +#define PDE_PS (1 << 7) +#define PT_ENTRY_PRESENT 0x1 /* We only support access to <=4G physical memory due to 1:1 mapping */ static uint64_t @@ -136,7 +136,7 @@ address(struct regs *regs, unsigned seg, unsigned off) } if (mode == VM86_REAL || seg > oldctx.gdtr_limit || - (mode == VM86_REAL_TO_PROTECTED && regs->cs == seg)) + (mode == VM86_REAL_TO_PROTECTED && regs->cs == seg)) return ((seg & 0xFFFF) << 4) + off; gdt_phys_base = guest_linear_to_phys(oldctx.gdtr_base); @@ -153,13 +153,13 @@ address(struct regs *regs, unsigned seg, unsigned off) seg_limit = (entry_high & 0xF0000) | (entry_low & 0xFFFF); if (entry_high & 0x8000 && - ((entry_high & 0x800000 && off >> 12 <= seg_limit) || - (!(entry_high & 0x800000) && off <= seg_limit))) + ((entry_high & 0x800000 && off >> 12 <= seg_limit) || + (!(entry_high & 0x800000) && off <= seg_limit))) return seg_base + off; panic("should never reach here in function address():\n\t" - "entry=0x%08x%08x, mode=%d, seg=0x%08x, offset=0x%08x\n", - entry_high, entry_low, mode, seg, off); + "entry=0x%08x%08x, mode=%d, seg=0x%08x, offset=0x%08x\n", + entry_high, entry_low, mode, seg, off); return 0; } @@ -172,7 +172,7 @@ trace(struct regs *regs, int adjust, char *fmt, ...) va_list ap; if ((traceset & (1 << mode)) && - (mode == VM86_REAL_TO_PROTECTED || mode == VM86_REAL)) { + (mode == VM86_REAL_TO_PROTECTED || mode == VM86_REAL)) { /* 16-bit, seg:off addressing */ unsigned addr = address(regs, regs->cs, off); printf("0x%08x: 0x%x:0x%04x ", addr, regs->cs, off); @@ -183,7 +183,7 @@ trace(struct regs *regs, int adjust, char *fmt, ...) printf("\n"); } if ((traceset & (1 << mode)) && - (mode == VM86_PROTECTED_TO_REAL || mode == VM86_PROTECTED)) { + (mode == VM86_PROTECTED_TO_REAL || mode == VM86_PROTECTED)) { /* 16-bit, gdt addressing */ unsigned addr = address(regs, regs->cs, off); printf("0x%08x: 0x%x:0x%08x ", addr, regs->cs, off); @@ -376,9 +376,9 @@ segment(unsigned prefix, struct regs *regs, unsigned seg) if (prefix & SEG_SS) seg = regs->uss; if (prefix & SEG_FS) - seg = regs->fs; + seg = regs->vfs; if (prefix & SEG_GS) - seg = regs->gs; + seg = regs->vgs; return seg; } @@ -430,7 +430,7 @@ operand(unsigned prefix, struct regs *regs, unsigned modrm) case 2: return address(regs, seg, regs->edx); case 3: return address(regs, seg, regs->ebx); case 4: return address(regs, seg, - sib(regs, mod, fetch8(regs))); + sib(regs, mod, fetch8(regs))); case 5: return address(regs, seg, fetch32(regs)); case 6: return address(regs, seg, regs->esi); case 7: return address(regs, seg, regs->edi); @@ -450,7 +450,7 @@ operand(unsigned prefix, struct regs *regs, unsigned modrm) case 2: return address(regs, seg, regs->edx + disp); case 3: return address(regs, seg, regs->ebx + disp); case 4: return address(regs, seg, - sib(regs, mod, fetch8(regs))); + sib(regs, mod, fetch8(regs))); case 5: return address(regs, seg, regs->ebp + disp); case 6: return address(regs, seg, regs->esi + disp); case 7: return address(regs, seg, regs->edi + disp); @@ -507,7 +507,7 @@ operand(unsigned prefix, struct regs *regs, unsigned modrm) } } - return 0; + return 0; } /* @@ -859,7 +859,7 @@ mov_to_seg(struct regs *regs, unsigned prefix, unsigned opc) fail: printf("%s:%d: missed opcode %02x %02x\n", - __FUNCTION__, __LINE__, opc, modrm); + __FUNCTION__, __LINE__, opc, modrm); return 0; } @@ -896,11 +896,11 @@ load_seg(unsigned long sel, uint32_t *base, uint32_t *limit, union vmcs_arbytes ((entry >> (32-16)) & 0x00FF0000) | ((entry >> ( 16)) & 0x0000FFFF)); *limit = (((entry >> (48-16)) & 0x000F0000) | - ((entry ) & 0x0000FFFF)); + (entry & 0x0000FFFF)); arbytes->bytes = 0; arbytes->fields.seg_type = (entry >> (8+32)) & 0xF; /* TYPE */ - arbytes->fields.s = (entry >> (12+32)) & 0x1; /* S */ + arbytes->fields.s = (entry >> (12+32)) & 0x1; /* S */ if (arbytes->fields.s) arbytes->fields.seg_type |= 1; /* accessed */ arbytes->fields.dpl = (entry >> (13+32)) & 0x3; /* DPL */ @@ -924,7 +924,7 @@ static void load_or_clear_seg(unsigned long sel, uint32_t *base, uint32_t *limit, union vmcs_arbytes *arbytes) { if (!load_seg(sel, base, limit, arbytes)) - load_seg(0, base, limit, arbytes); + load_seg(0, base, limit, arbytes); } @@ -934,6 +934,8 @@ load_or_clear_seg(unsigned long sel, uint32_t *base, uint32_t *limit, union vmcs static void protected_mode(struct regs *regs) { + extern char stack_top[]; + regs->eflags &= ~(EFLAGS_TF|EFLAGS_VM); oldctx.eip = regs->eip; @@ -958,12 +960,10 @@ protected_mode(struct regs *regs) &oldctx.gs_limit, &oldctx.gs_arbytes); /* initialize jump environment to warp back to protected mode */ + regs->uss = DATA_SELECTOR; + regs->uesp = stack_top; regs->cs = CODE_SELECTOR; - regs->ds = DATA_SELECTOR; - regs->es = DATA_SELECTOR; - regs->fs = DATA_SELECTOR; - regs->gs = DATA_SELECTOR; - regs->eip = (unsigned) &switch_to_protected_mode; + regs->eip = (unsigned) switch_to_protected_mode; /* this should get us into 32-bit mode */ } @@ -975,10 +975,6 @@ static void real_mode(struct regs *regs) { regs->eflags |= EFLAGS_VM | 0x02; - regs->ds = DATA_SELECTOR; - regs->es = DATA_SELECTOR; - regs->fs = DATA_SELECTOR; - regs->gs = DATA_SELECTOR; /* * When we transition from protected to real-mode and we @@ -992,21 +988,21 @@ real_mode(struct regs *regs) panic("%%ss 0x%lx higher than 1MB", regs->uss); regs->uss = address(regs, regs->uss, 0) >> 4; } else { - regs->uss = saved_rm_regs.uss; + regs->uss = saved_rm_regs.uss; } if (regs->vds != 0) { if (regs->vds >= HIGHMEM) panic("%%ds 0x%lx higher than 1MB", regs->vds); regs->vds = address(regs, regs->vds, 0) >> 4; } else { - regs->vds = saved_rm_regs.vds; + regs->vds = saved_rm_regs.vds; } if (regs->ves != 0) { if (regs->ves >= HIGHMEM) panic("%%es 0x%lx higher than 1MB", regs->ves); regs->ves = address(regs, regs->ves, 0) >> 4; } else { - regs->ves = saved_rm_regs.ves; + regs->ves = saved_rm_regs.ves; } /* this should get us into 16-bit mode */ @@ -1033,10 +1029,7 @@ set_mode(struct regs *regs, enum vm86_mode newmode) (mode == VM86_REAL_TO_PROTECTED)) { regs->eflags &= ~EFLAGS_TF; real_mode(regs); - break; - } else if (mode == VM86_REAL) { - break; - } else + } else if (mode != VM86_REAL) panic("unexpected real mode transition"); break; @@ -1053,28 +1046,19 @@ set_mode(struct regs *regs, enum vm86_mode newmode) oldctx.fs_sel = 0; oldctx.gs_sel = 0; oldctx.ss_sel = 0; - break; - } else if (mode == VM86_REAL_TO_PROTECTED) { - break; - } else + } else if (mode != VM86_REAL_TO_PROTECTED) panic("unexpected real-to-protected mode transition"); break; case VM86_PROTECTED_TO_REAL: - if (mode == VM86_PROTECTED) { - break; - } else + if (mode != VM86_PROTECTED) panic("unexpected protected-to-real mode transition"); break; case VM86_PROTECTED: - if (mode == VM86_REAL_TO_PROTECTED) { - protected_mode(regs); -// printf("\n"); - mode = newmode; - return; - } else + if (mode != VM86_REAL_TO_PROTECTED) panic("unexpected protected mode transition"); + protected_mode(regs); break; } @@ -1088,25 +1072,19 @@ jmpl(struct regs *regs, int prefix) unsigned n = regs->eip; unsigned cs, eip; - if (mode == VM86_REAL_TO_PROTECTED) { /* jump to protected mode */ - eip = (prefix & DATA32) ? fetch32(regs) : fetch16(regs); - cs = fetch16(regs); + eip = (prefix & DATA32) ? fetch32(regs) : fetch16(regs); + cs = fetch16(regs); - TRACE((regs, (regs->eip - n) + 1, "jmpl 0x%x:0x%x", cs, eip)); + TRACE((regs, (regs->eip - n) + 1, "jmpl 0x%x:0x%x", cs, eip)); - regs->cs = cs; - regs->eip = eip; + regs->cs = cs; + regs->eip = eip; + + if (mode == VM86_REAL_TO_PROTECTED) /* jump to protected mode */ set_mode(regs, VM86_PROTECTED); - } else if (mode == VM86_PROTECTED_TO_REAL) { /* jump to real mode */ - eip = (prefix & DATA32) ? fetch32(regs) : fetch16(regs); - cs = fetch16(regs); - - TRACE((regs, (regs->eip - n) + 1, "jmpl 0x%x:0x%x", cs, eip)); - - regs->cs = cs; - regs->eip = eip; + else if (mode == VM86_PROTECTED_TO_REAL)/* jump to real mode */ set_mode(regs, VM86_REAL); - } else + else panic("jmpl"); } @@ -1117,29 +1095,22 @@ jmpl_indirect(struct regs *regs, int prefix, unsigned modrm) unsigned cs, eip; unsigned addr; - addr = operand(prefix, regs, modrm); + addr = operand(prefix, regs, modrm); - if (mode == VM86_REAL_TO_PROTECTED) { /* jump to protected mode */ - eip = (prefix & DATA32) ? read32(addr) : read16(addr); - addr += (prefix & DATA32) ? 4 : 2; - cs = read16(addr); + eip = (prefix & DATA32) ? read32(addr) : read16(addr); + addr += (prefix & DATA32) ? 4 : 2; + cs = read16(addr); - TRACE((regs, (regs->eip - n) + 1, "jmpl 0x%x:0x%x", cs, eip)); + TRACE((regs, (regs->eip - n) + 1, "jmpl 0x%x:0x%x", cs, eip)); - regs->cs = cs; - regs->eip = eip; + regs->cs = cs; + regs->eip = eip; + + if (mode == VM86_REAL_TO_PROTECTED) /* jump to protected mode */ set_mode(regs, VM86_PROTECTED); - } else if (mode == VM86_PROTECTED_TO_REAL) { /* jump to real mode */ - eip = (prefix & DATA32) ? read32(addr) : read16(addr); - addr += (prefix & DATA32) ? 4 : 2; - cs = read16(addr); - - TRACE((regs, (regs->eip - n) + 1, "jmpl 0x%x:0x%x", cs, eip)); - - regs->cs = cs; - regs->eip = eip; + else if (mode == VM86_PROTECTED_TO_REAL)/* jump to real mode */ set_mode(regs, VM86_REAL); - } else + else panic("jmpl"); } @@ -1158,15 +1129,14 @@ retl(struct regs *regs, int prefix) TRACE((regs, 1, "retl (to 0x%x:0x%x)", cs, eip)); - if (mode == VM86_REAL_TO_PROTECTED) { /* jump to protected mode */ - regs->cs = cs; - regs->eip = eip; + regs->cs = cs; + regs->eip = eip; + + if (mode == VM86_REAL_TO_PROTECTED) /* jump to protected mode */ set_mode(regs, VM86_PROTECTED); - } else if (mode == VM86_PROTECTED_TO_REAL) { /* jump to real mode */ - regs->cs = cs; - regs->eip = eip; + else if (mode == VM86_PROTECTED_TO_REAL)/* jump to real mode */ set_mode(regs, VM86_REAL); - } else + else panic("retl"); } @@ -1266,8 +1236,8 @@ pushrm(struct regs *regs, int prefix, unsigned modrm) unsigned addr; unsigned data; - addr = operand(prefix, regs, modrm); - + addr = operand(prefix, regs, modrm); + if (prefix & DATA32) { data = read32(addr); push32(regs, data); @@ -1393,11 +1363,11 @@ opcode(struct regs *regs) case 0x3B: /* addr32 cmp r/m16, r16 */ if (mode != VM86_REAL && mode != VM86_REAL_TO_PROTECTED) goto invalid; - if ((prefix & ADDR32) == 0) - goto invalid; - if (!cmp(regs, prefix, opc)) - goto invalid; - return OPC_EMULATED; + if ((prefix & ADDR32) == 0) + goto invalid; + if (!cmp(regs, prefix, opc)) + goto invalid; + return OPC_EMULATED; case 0x3E: TRACE((regs, regs->eip - eip, "%%ds:")); @@ -1419,7 +1389,7 @@ opcode(struct regs *regs) prefix |= DATA32; continue; - case 0x67: + case 0x67: TRACE((regs, regs->eip - eip, "addr32")); prefix |= ADDR32; continue; @@ -1428,18 +1398,18 @@ opcode(struct regs *regs) case 0x8A: /* addr32 mov r/m8, r8 */ if (mode != VM86_REAL && mode != VM86_REAL_TO_PROTECTED) goto invalid; - if ((prefix & ADDR32) == 0) - goto invalid; - if (!movr(regs, prefix, opc)) - goto invalid; - return OPC_EMULATED; + if ((prefix & ADDR32) == 0) + goto invalid; + if (!movr(regs, prefix, opc)) + goto invalid; + return OPC_EMULATED; case 0x89: /* addr32 mov r16, r/m16 */ if (mode == VM86_PROTECTED_TO_REAL) { unsigned modrm = fetch8(regs); unsigned addr = operand(prefix, regs, modrm); unsigned val, r = (modrm >> 3) & 7; - + if (prefix & DATA32) { val = getreg16(regs, r); write32(addr, val); @@ -1454,11 +1424,11 @@ opcode(struct regs *regs) case 0x8B: /* addr32 mov r/m16, r16 */ if (mode != VM86_REAL && mode != VM86_REAL_TO_PROTECTED) goto invalid; - if ((prefix & ADDR32) == 0) - goto invalid; - if (!movr(regs, prefix, opc)) - goto invalid; - return OPC_EMULATED; + if ((prefix & ADDR32) == 0) + goto invalid; + if (!movr(regs, prefix, opc)) + goto invalid; + return OPC_EMULATED; case 0x8E: /* mov r16, sreg */ if (!mov_to_seg(regs, prefix, opc)) @@ -1466,11 +1436,11 @@ opcode(struct regs *regs) return OPC_EMULATED; case 0x8F: /* addr32 pop r/m16 */ - if ((prefix & ADDR32) == 0) - goto invalid; - if (!pop(regs, prefix, opc)) - goto invalid; - return OPC_EMULATED; + if ((prefix & ADDR32) == 0) + goto invalid; + if (!pop(regs, prefix, opc)) + goto invalid; + return OPC_EMULATED; case 0x90: /* nop */ TRACE((regs, regs->eip - eip, "nop")); @@ -1494,7 +1464,7 @@ opcode(struct regs *regs) regs->eflags |= EFLAGS_VM; return OPC_EMULATED; - case 0xA1: /* mov ax, r/m16 */ + case 0xA1: /* mov ax, r/m16 */ { int addr, data; int seg = segment(prefix, regs, regs->vds); @@ -1528,15 +1498,15 @@ opcode(struct regs *regs) return OPC_EMULATED; case 0xC6: /* addr32 movb $imm, r/m8 */ - if ((prefix & ADDR32) == 0) - goto invalid; - if (!movr(regs, prefix, opc)) - goto invalid; + if ((prefix & ADDR32) == 0) + goto invalid; + if (!movr(regs, prefix, opc)) + goto invalid; return OPC_EMULATED; case 0xCB: /* retl */ if ((mode == VM86_REAL_TO_PROTECTED) || - (mode == VM86_PROTECTED_TO_REAL)) { + (mode == VM86_PROTECTED_TO_REAL)) { retl(regs, prefix); return OPC_INVALID; } @@ -1574,7 +1544,7 @@ opcode(struct regs *regs) case 0xEA: /* jmpl */ if ((mode == VM86_REAL_TO_PROTECTED) || - (mode == VM86_PROTECTED_TO_REAL)) { + (mode == VM86_PROTECTED_TO_REAL)) { jmpl(regs, prefix); return OPC_INVALID; } @@ -1586,7 +1556,7 @@ opcode(struct regs *regs) switch((modrm >> 3) & 7) { case 5: /* jmpl (indirect) */ if ((mode == VM86_REAL_TO_PROTECTED) || - (mode == VM86_PROTECTED_TO_REAL)) { + (mode == VM86_PROTECTED_TO_REAL)) { jmpl_indirect(regs, prefix, modrm); return OPC_INVALID; } @@ -1603,7 +1573,7 @@ opcode(struct regs *regs) case 0xEB: /* short jump */ if ((mode == VM86_REAL_TO_PROTECTED) || - (mode == VM86_PROTECTED_TO_REAL)) { + (mode == VM86_PROTECTED_TO_REAL)) { disp = (char) fetch8(regs); TRACE((regs, 2, "jmp 0x%x", regs->eip + disp)); regs->eip += disp; @@ -1626,10 +1596,10 @@ opcode(struct regs *regs) continue; case 0xF6: /* addr32 testb $imm, r/m8 */ - if ((prefix & ADDR32) == 0) - goto invalid; - if (!test(regs, prefix, opc)) - goto invalid; + if ((prefix & ADDR32) == 0) + goto invalid; + if (!test(regs, prefix, opc)) + goto invalid; return OPC_EMULATED; case 0xFA: /* cli */ @@ -1689,6 +1659,8 @@ trap(int trapno, int errno, struct regs *regs) case 1: /* Debug */ if (regs->eflags & EFLAGS_VM) { /* emulate any 8086 instructions */ + if (mode == VM86_REAL) + return; if (mode != VM86_REAL_TO_PROTECTED) panic("not in real-to-protected mode"); emulate(regs); @@ -1709,7 +1681,7 @@ trap(int trapno, int errno, struct regs *regs) default: invalid: printf("Trap (0x%x) while in %s mode\n", - trapno, regs->eflags & EFLAGS_VM ? "real" : "protected"); + trapno, regs->eflags & EFLAGS_VM ? "real" : "protected"); if (trapno == 14) printf("Page fault address 0x%x\n", get_cr2()); dump_regs(regs); diff --git a/tools/firmware/vmxassist/vm86.h b/tools/firmware/vmxassist/vm86.h index 4c6609daf0..ea8c2728d3 100644 --- a/tools/firmware/vmxassist/vm86.h +++ b/tools/firmware/vmxassist/vm86.h @@ -34,7 +34,6 @@ struct regs { unsigned edi, esi, ebp, esp, ebx, edx, ecx, eax; - unsigned es, ds, fs, gs; unsigned trapno, errno; unsigned eip, cs, eflags, uesp, uss; unsigned ves, vds, vfs, vgs; @@ -55,7 +54,6 @@ enum vm86_mode { extern enum vm86_mode prevmode, mode; extern struct vmx_assist_context oldctx; -extern struct vmx_assist_context newctx; extern void emulate(struct regs *); extern void dump_regs(struct regs *); diff --git a/tools/ioemu/Makefile.target b/tools/ioemu/Makefile.target index b35cdbaeb5..4872128d20 100644 --- a/tools/ioemu/Makefile.target +++ b/tools/ioemu/Makefile.target @@ -296,7 +296,7 @@ endif # qemu-dm objects ifeq ($(ARCH),ia64) -LIBOBJS=helper2.o exec-dm.o i8259-dm.o +LIBOBJS=helper2.o exec-dm.o i8259-dm.o piix_pci-dm.o else LIBOBJS=helper2.o exec-dm.o i8259-dm.o rtc-dm.o piix_pci-dm.o endif @@ -360,7 +360,7 @@ ifeq ($(TARGET_BASE_ARCH), i386) # Hardware support VL_OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) ifeq ($(ARCH),ia64) -VL_OBJS+= fdc.o mc146818rtc.o serial.o pc.o piix_pci.o +VL_OBJS+= fdc.o mc146818rtc.o serial.o pc.o else VL_OBJS+= fdc.o serial.o pc.o endif diff --git a/tools/ioemu/hw/ide.c b/tools/ioemu/hw/ide.c index 94d5beaad5..532afbbffd 100644 --- a/tools/ioemu/hw/ide.c +++ b/tools/ioemu/hw/ide.c @@ -680,7 +680,7 @@ static void ide_set_sector(IDEState *s, int64_t sector_num) static void ide_sector_read(IDEState *s) { int64_t sector_num; - int ret, n; + int n; s->status = READY_STAT | SEEK_STAT; s->error = 0; /* not needed by IDE spec, but needed by Windows */ @@ -695,7 +695,11 @@ static void ide_sector_read(IDEState *s) #endif if (n > s->req_nb_sectors) n = s->req_nb_sectors; - ret = bdrv_read(s->bs, sector_num, s->io_buffer, n); + if (bdrv_read(s->bs, sector_num, s->io_buffer, n) != 0) { + ide_abort_command(s); + ide_set_irq(s); + return; + } ide_transfer_start(s, s->io_buffer, 512 * n, ide_sector_read); ide_set_irq(s); ide_set_sector(s, sector_num + n); @@ -721,7 +725,11 @@ static int ide_read_dma_cb(IDEState *s, if (n > MAX_MULT_SECTORS) n = MAX_MULT_SECTORS; sector_num = ide_get_sector(s); - bdrv_read(s->bs, sector_num, s->io_buffer, n); + if (bdrv_read(s->bs, sector_num, s->io_buffer, n) != 0) { + ide_abort_command(s); + ide_set_irq(s); + return 0; + } s->io_buffer_index = 0; s->io_buffer_size = n * 512; len = s->io_buffer_size; @@ -767,7 +775,7 @@ static void ide_sector_write_timer_cb(void *opaque) static void ide_sector_write(IDEState *s) { int64_t sector_num; - int ret, n, n1; + int n, n1; s->status = READY_STAT | SEEK_STAT; sector_num = ide_get_sector(s); @@ -777,7 +785,11 @@ static void ide_sector_write(IDEState *s) n = s->nsector; if (n > s->req_nb_sectors) n = s->req_nb_sectors; - ret = bdrv_write(s->bs, sector_num, s->io_buffer, n); + if (bdrv_write(s->bs, sector_num, s->io_buffer, n) != 0) { + ide_abort_command(s); + ide_set_irq(s); + return; + } s->nsector -= n; if (s->nsector == 0) { /* no more sector to write */ @@ -823,8 +835,13 @@ static int ide_write_dma_cb(IDEState *s, if (len == 0) { n = s->io_buffer_size >> 9; sector_num = ide_get_sector(s); - bdrv_write(s->bs, sector_num, s->io_buffer, - s->io_buffer_size >> 9); + if (bdrv_write(s->bs, sector_num, s->io_buffer, + s->io_buffer_size >> 9) != 0) { + ide_abort_command(s); + ide_set_irq(s); + return 0; + } + sector_num += n; ide_set_sector(s, sector_num); s->nsector -= n; diff --git a/tools/ioemu/hw/pci.c b/tools/ioemu/hw/pci.c index cea81f7b7f..77737c8615 100644 --- a/tools/ioemu/hw/pci.c +++ b/tools/ioemu/hw/pci.c @@ -221,24 +221,17 @@ uint32_t pci_default_read_config(PCIDevice *d, uint32_t address, int len) { uint32_t val; - switch(len) { - default: - case 4: - if (address <= 0xfc) { - val = le32_to_cpu(*(uint32_t *)(d->config + address)); - break; - } - /* fall through */ - case 2: - if (address <= 0xfe) { - val = le16_to_cpu(*(uint16_t *)(d->config + address)); - break; - } - /* fall through */ case 1: val = d->config[address]; break; + case 2: + val = le16_to_cpu(*(uint16_t *)(d->config + address)); + break; + default: + case 4: + val = le32_to_cpu(*(uint32_t *)(d->config + address)); + break; } return val; } @@ -340,8 +333,7 @@ void pci_default_write_config(PCIDevice *d, d->config[addr] = val; } - if (++addr > 0xff) - break; + addr++; val >>= 8; } diff --git a/tools/ioemu/hw/tpm_tis.c b/tools/ioemu/hw/tpm_tis.c index a4d07bc67f..ace469f1ec 100644 --- a/tools/ioemu/hw/tpm_tis.c +++ b/tools/ioemu/hw/tpm_tis.c @@ -132,7 +132,7 @@ typedef struct TPMState { /* local prototypes */ -static int TPM_Send(tpmState *s, tpmBuffer *buffer, char *msg); +static int TPM_Send(tpmState *s, tpmBuffer *buffer, uint8_t locty, char *msg); static int TPM_Receive(tpmState *s, tpmBuffer *buffer); static uint32_t vtpm_instance_from_xenstore(void); static void tis_poll_timer(void *opaque); @@ -271,6 +271,8 @@ static int create_local_socket(tpmState *s, uint32_t vtpm_instance) /* * the 'write' method for sending requests to the vTPM * four bytes with the vTPM instance number are prepended to each request + * the locality in which the command was sent is transmitted in the + * highest 3 bits */ static int write_local_socket(tpmState *s, const tpmBuffer *buffer) { @@ -608,7 +610,7 @@ static void tis_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) } } if (val & STS_TPM_GO) { - n = TPM_Send(s, &s->buffer,"tpm_data_write"); + n = TPM_Send(s, &s->buffer, locty, "tpm_data_write"); if (n > 0) { /* sending of data was successful */ s->offset = 0; @@ -915,7 +917,7 @@ const static unsigned char tpm_failure[] = { /* * Send a TPM request. */ -static int TPM_Send(tpmState *s, tpmBuffer *buffer, char *msg) +static int TPM_Send(tpmState *s, tpmBuffer *buffer, uint8_t locty, char *msg) { int len; uint32_t size = tpm_get_size_from_buffer(buffer->buf); @@ -945,6 +947,10 @@ static int TPM_Send(tpmState *s, tpmBuffer *buffer, char *msg) showBuff(buffer->buf, "To TPM"); #endif + /* transmit the locality in the highest 3 bits */ + buffer->instance[0] &= 0x1f; + buffer->instance[0] |= (locty << 5); + len = vTPMTransmit[s->Transmitlayer].write(s, buffer); if (len < 0) { s->Transmitlayer = -1; diff --git a/tools/ioemu/hw/usb-hid.c b/tools/ioemu/hw/usb-hid.c index 8fc0b744b7..60849a4ed0 100644 --- a/tools/ioemu/hw/usb-hid.c +++ b/tools/ioemu/hw/usb-hid.c @@ -39,6 +39,7 @@ typedef struct USBMouseState { int x, y; int kind; int mouse_grabbed; + int status_changed; } USBMouseState; /* mostly the same values as the Bochs USB Mouse device */ @@ -231,6 +232,7 @@ static void usb_mouse_event(void *opaque, s->dy += dy1; s->dz += dz1; s->buttons_state = buttons_state; + s->status_changed = 1; } static void usb_tablet_event(void *opaque, @@ -242,6 +244,7 @@ static void usb_tablet_event(void *opaque, s->y = y; s->dz += dz; s->buttons_state = buttons_state; + s->status_changed = 1; } static inline int int_clamp(int val, int vmin, int vmax) @@ -483,10 +486,16 @@ static int usb_mouse_handle_data(USBDevice *dev, int pid, switch(pid) { case USB_TOKEN_IN: if (devep == 1) { - if (s->kind == USB_MOUSE) - ret = usb_mouse_poll(s, data, len); - else if (s->kind == USB_TABLET) - ret = usb_tablet_poll(s, data, len); + if (s->kind == USB_MOUSE) + ret = usb_mouse_poll(s, data, len); + else if (s->kind == USB_TABLET) + ret = usb_tablet_poll(s, data, len); + + if (!s->status_changed) + ret = USB_RET_NAK; + else + s->status_changed = 0; + } else { goto fail; } @@ -523,6 +532,7 @@ USBDevice *usb_tablet_init(void) s->dev.handle_data = usb_mouse_handle_data; s->dev.handle_destroy = usb_mouse_handle_destroy; s->kind = USB_TABLET; + s->status_changed = 0; pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Tablet"); @@ -544,6 +554,7 @@ USBDevice *usb_mouse_init(void) s->dev.handle_data = usb_mouse_handle_data; s->dev.handle_destroy = usb_mouse_handle_destroy; s->kind = USB_MOUSE; + s->status_changed = 0; pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Mouse"); diff --git a/tools/ioemu/hw/usb-uhci.c b/tools/ioemu/hw/usb-uhci.c index c6394cdda3..9503c51c3e 100644 --- a/tools/ioemu/hw/usb-uhci.c +++ b/tools/ioemu/hw/usb-uhci.c @@ -421,15 +421,13 @@ static int uhci_broadcast_packet(UHCIState *s, uint8_t pid, static int uhci_handle_td(UHCIState *s, UHCI_TD *td, int *int_mask) { uint8_t pid; - uint8_t buf[1280]; + uint8_t buf[2048]; int len, max_len, err, ret; - if (td->ctrl & TD_CTRL_IOC) { - *int_mask |= 0x01; + if (!(td->ctrl & TD_CTRL_ACTIVE)){ + ret = 1; + goto out; } - - if (!(td->ctrl & TD_CTRL_ACTIVE)) - return 1; /* TD is active */ max_len = ((td->token >> 21) + 1) & 0x7ff; @@ -467,7 +465,8 @@ static int uhci_handle_td(UHCIState *s, UHCI_TD *td, int *int_mask) /* invalid pid : frame interrupted */ s->status |= UHCI_STS_HCPERR; uhci_update_irq(s); - return -1; + ret = -1; + goto out; } if (td->ctrl & TD_CTRL_IOS) td->ctrl &= ~TD_CTRL_ACTIVE; @@ -479,10 +478,12 @@ static int uhci_handle_td(UHCIState *s, UHCI_TD *td, int *int_mask) len < max_len) { *int_mask |= 0x02; /* short packet: do not update QH */ - return 1; + ret = 1; + goto out; } else { /* success */ - return 0; + ret = 0; + goto out; } } else { switch(ret) { @@ -501,23 +502,34 @@ static int uhci_handle_td(UHCIState *s, UHCI_TD *td, int *int_mask) } td->ctrl = (td->ctrl & ~(3 << TD_CTRL_ERROR_SHIFT)) | (err << TD_CTRL_ERROR_SHIFT); - return 1; + ret = 1; + goto out; case USB_RET_NAK: td->ctrl |= TD_CTRL_NAK; if (pid == USB_TOKEN_SETUP) goto do_timeout; - return 1; + ret = 1; + goto out; case USB_RET_STALL: td->ctrl |= TD_CTRL_STALL; td->ctrl &= ~TD_CTRL_ACTIVE; - return 1; + ret = 1; + goto out; case USB_RET_BABBLE: td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL; td->ctrl &= ~TD_CTRL_ACTIVE; /* frame interrupted */ - return -1; + ret = -1; + goto out; } } + +out: + /* If TD is inactive and IOC bit set to 1 then update int_mask */ + if ((td->ctrl & TD_CTRL_IOC) && (!(td->ctrl & TD_CTRL_ACTIVE))) { + *int_mask |= 0x01; + } + return ret; } static void uhci_frame_timer(void *opaque) diff --git a/tools/ioemu/keymaps.c b/tools/ioemu/keymaps.c index bd893288aa..b86ab2a0b0 100644 --- a/tools/ioemu/keymaps.c +++ b/tools/ioemu/keymaps.c @@ -36,8 +36,10 @@ static int get_keysym(const char *name) #define MAX_EXTRA_COUNT 256 typedef struct { uint16_t keysym2keycode[MAX_NORMAL_KEYCODE]; + int keysym2numlock[MAX_NORMAL_KEYCODE]; struct { int keysym; + int numlock; uint16_t keycode; } keysym2keycode_extra[MAX_EXTRA_COUNT]; int extra_count; @@ -50,6 +52,8 @@ static kbd_layout_t *parse_keyboard_layout(const char *language, char file_name[1024]; char line[1024]; int len; + int *keycode2numlock; + int i; snprintf(file_name, sizeof(file_name), "%s/keymaps/%s", bios_dir, language); @@ -63,6 +67,15 @@ static kbd_layout_t *parse_keyboard_layout(const char *language, "Could not read keymap file: '%s'\n", file_name); return 0; } + + /* Allocate a temporary map tracking which keycodes change when numlock is + set. Keycodes are 16 bit, so 65536 is safe. */ + keycode2numlock = malloc(65536 * sizeof(int)); + if (!keycode2numlock) { + perror("Could not read keymap file"); + return 0; + } + for(;;) { if (fgets(line, 1024, f) == NULL) break; @@ -86,13 +99,19 @@ static kbd_layout_t *parse_keyboard_layout(const char *language, if (keysym == 0) { // fprintf(stderr, "Warning: unknown keysym %s\n", line); } else { - const char *rest = end_of_keysym + 1; - int keycode = strtol(rest, NULL, 0); + char *rest = end_of_keysym + 1; + int keycode = strtol(rest, &rest, 0); + int numlock = (rest != NULL && + strstr(rest, "numlock") != NULL); + + keycode2numlock[keycode] = numlock; + /* if(keycode&0x80) keycode=(keycode<<8)^0x80e0; */ if (keysym < MAX_NORMAL_KEYCODE) { //fprintf(stderr,"Setting keysym %s (%d) to %d\n",line,keysym,keycode); k->keysym2keycode[keysym] = keycode; + k->keysym2numlock[keysym] = numlock; } else { if (k->extra_count >= MAX_EXTRA_COUNT) { fprintf(stderr, @@ -107,6 +126,8 @@ static kbd_layout_t *parse_keyboard_layout(const char *language, keysym = keysym; k->keysym2keycode_extra[k->extra_count]. keycode = keycode; + k->keysym2keycode_extra[k->extra_count]. + numlock = numlock; k->extra_count++; } } @@ -115,6 +136,22 @@ static kbd_layout_t *parse_keyboard_layout(const char *language, } } fclose(f); + + for (i = 0; i < MAX_NORMAL_KEYCODE; i++) { + if (k->keysym2numlock[i] != 1) { + k->keysym2numlock[i] = -keycode2numlock[k->keysym2keycode[i]]; + } + } + + for (i = 0; i < k->extra_count; i++) { + if (k->keysym2keycode_extra[i].numlock != 1) { + k->keysym2keycode_extra[i].numlock = + -keycode2numlock[k->keysym2keycode_extra[i].keycode]; + } + } + + free(keycode2numlock); + return k; } @@ -143,3 +180,25 @@ static int keysym2scancode(void *kbd_layout, int keysym) } return 0; } + +/** + * Returns 1 if the given keysym requires numlock to be pressed, -1 if it + * requires it to be cleared, and 0 otherwise. + */ +static int keysym2numlock(void *kbd_layout, int keysym) +{ + kbd_layout_t *k = kbd_layout; + if (keysym < MAX_NORMAL_KEYCODE) { + return k->keysym2numlock[keysym]; + } else { + int i; +#ifdef XK_ISO_Left_Tab + if (keysym == XK_ISO_Left_Tab) + keysym = XK_Tab; +#endif + for (i = 0; i < k->extra_count; i++) + if (k->keysym2keycode_extra[i].keysym == keysym) + return k->keysym2keycode_extra[i].numlock; + } + return 0; +} diff --git a/tools/ioemu/patches/acpi-support b/tools/ioemu/patches/acpi-support index 9baa69fb3b..93f67261c1 100644 --- a/tools/ioemu/patches/acpi-support +++ b/tools/ioemu/patches/acpi-support @@ -1,8 +1,8 @@ Index: ioemu/Makefile.target =================================================================== ---- ioemu.orig/Makefile.target 2006-08-17 19:49:50.228216099 +0100 -+++ ioemu/Makefile.target 2006-08-17 19:50:02.405870095 +0100 -@@ -357,6 +357,7 @@ +--- ioemu.orig/Makefile.target 2006-12-08 02:00:40.000000000 +0000 ++++ ioemu/Makefile.target 2006-12-08 02:00:40.000000000 +0000 +@@ -358,6 +358,7 @@ VL_OBJS+= fdc.o mc146818rtc.o serial.o pc.o VL_OBJS+= cirrus_vga.o mixeng.o parallel.o acpi.o piix_pci.o VL_OBJS+= usb-uhci.o @@ -12,8 +12,8 @@ Index: ioemu/Makefile.target ifeq ($(TARGET_BASE_ARCH), ppc) Index: ioemu/hw/pc.c =================================================================== ---- ioemu.orig/hw/pc.c 2006-08-17 19:49:59.312212039 +0100 -+++ ioemu/hw/pc.c 2006-08-17 19:50:02.406869984 +0100 +--- ioemu.orig/hw/pc.c 2006-12-08 02:00:40.000000000 +0000 ++++ ioemu/hw/pc.c 2006-12-08 02:00:40.000000000 +0000 @@ -874,13 +874,19 @@ cmos_init(ram_size, boot_device, bs_table, timeoffset); @@ -49,8 +49,8 @@ Index: ioemu/hw/pc.c Index: ioemu/hw/piix4acpi.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ ioemu/hw/piix4acpi.c 2006-08-17 19:50:02.407869874 +0100 -@@ -0,0 +1,388 @@ ++++ ioemu/hw/piix4acpi.c 2006-12-08 02:00:40.000000000 +0000 +@@ -0,0 +1,396 @@ +/* + * PIIX4 ACPI controller emulation + * @@ -434,15 +434,23 @@ Index: ioemu/hw/piix4acpi.c + pci_conf[0x0e] = 0x00; + pci_conf[0x3d] = 0x01; /* Hardwired to PIRQA is used */ + -+ pci_register_io_region((PCIDevice *)d, 4, 0x10, -+ PCI_ADDRESS_SPACE_IO, acpi_map); + -+ acpi_reset (d); ++ /* PMBA POWER MANAGEMENT BASE ADDRESS, hardcoded to 0x1f40 ++ * to make shutdown work for IPF, due to IPF Guest Firmware ++ * will enumerate pci devices. ++ * ++ * TODO: if Guest Firmware or Guest OS will change this PMBA, ++ * More logic will be added. ++ */ ++ pci_conf[0x40] = 0x41; /* Special device-specific BAR at 0x40 */ ++ pci_conf[0x41] = 0x1f; ++ acpi_map(d, 0, 0x1f40, 0x10, PCI_ADDRESS_SPACE_IO); ++ acpi_reset(d); +} Index: ioemu/vl.c =================================================================== ---- ioemu.orig/vl.c 2006-08-17 19:49:59.315211708 +0100 -+++ ioemu/vl.c 2006-08-17 19:50:02.410869542 +0100 +--- ioemu.orig/vl.c 2006-12-08 02:00:40.000000000 +0000 ++++ ioemu/vl.c 2006-12-08 02:00:40.000000000 +0000 @@ -156,7 +156,7 @@ #else #define MAX_CPUS 1 @@ -476,7 +484,7 @@ Index: ioemu/vl.c { NULL }, }; -@@ -6256,6 +6259,9 @@ +@@ -6240,6 +6243,9 @@ case QEMU_OPTION_timeoffset: timeoffset = strtol(optarg, NULL, 0); break; @@ -488,8 +496,8 @@ Index: ioemu/vl.c } Index: ioemu/vl.h =================================================================== ---- ioemu.orig/vl.h 2006-08-17 19:49:59.316211597 +0100 -+++ ioemu/vl.h 2006-08-17 19:50:02.411869432 +0100 +--- ioemu.orig/vl.h 2006-12-08 02:00:40.000000000 +0000 ++++ ioemu/vl.h 2006-12-08 02:00:40.000000000 +0000 @@ -168,6 +168,7 @@ extern int kqemu_allowed; extern int win2k_install_hack; @@ -510,8 +518,8 @@ Index: ioemu/vl.h extern QEMUMachine isapc_machine; Index: ioemu/hw/piix_pci.c =================================================================== ---- ioemu.orig/hw/piix_pci.c 2006-08-17 19:38:05.806252180 +0100 -+++ ioemu/hw/piix_pci.c 2006-08-17 19:50:02.411869432 +0100 +--- ioemu.orig/hw/piix_pci.c 2006-12-08 02:00:39.000000000 +0000 ++++ ioemu/hw/piix_pci.c 2006-12-08 02:00:40.000000000 +0000 @@ -241,7 +241,7 @@ static uint32_t pci_bios_io_addr; static uint32_t pci_bios_mem_addr; @@ -521,33 +529,22 @@ Index: ioemu/hw/piix_pci.c static void pci_config_writel(PCIDevice *d, uint32_t addr, uint32_t val) { -@@ -336,6 +336,14 @@ +@@ -336,6 +336,18 @@ pci_set_io_region_addr(d, 3, 0x374); } break; + case 0x0680: + if (vendor_id == 0x8086 && device_id == 0x7113) { -+ /* PIIX4 ACPI PM */ -+ pci_config_writew(d, 0x20, 0x0000); /* NO smb bus IO enable in PIIX4 */ ++ /* ++ * PIIX4 ACPI PM. ++ * Special device with special PCI config space. No ordinary BARs. ++ */ ++ pci_config_writew(d, 0x20, 0x0000); // No smb bus IO enable + pci_config_writew(d, 0x22, 0x0000); -+ goto default_map; ++ pci_config_writew(d, 0x3c, 0x0009); // Hardcoded IRQ9 ++ pci_config_writew(d, 0x3d, 0x0001); + } + break; case 0x0300: if (vendor_id != 0x1234) goto default_map; -@@ -386,6 +394,14 @@ - pic_irq = pci_irqs[pin]; - pci_config_writeb(d, PCI_INTERRUPT_LINE, pic_irq); - } -+ -+ if (class== 0x0680&& vendor_id == 0x8086 && device_id == 0x7113) { -+ // PIIX4 ACPI PM -+ pci_config_writew(d, 0x20, 0x0000); // NO smb bus IO enable in PIIX4 -+ pci_config_writew(d, 0x22, 0x0000); -+ pci_config_writew(d, 0x3c, 0x0009); // Hardcodeed IRQ9 -+ pci_config_writew(d, 0x3d, 0x0001); -+ } - } - - /* diff --git a/tools/ioemu/patches/acpi-timer-support b/tools/ioemu/patches/acpi-timer-support index 74863b1a39..2698827caa 100644 --- a/tools/ioemu/patches/acpi-timer-support +++ b/tools/ioemu/patches/acpi-timer-support @@ -1,7 +1,7 @@ Index: ioemu/hw/piix4acpi.c =================================================================== ---- ioemu.orig/hw/piix4acpi.c 2006-08-17 19:50:02.407869874 +0100 -+++ ioemu/hw/piix4acpi.c 2006-08-17 19:50:05.060576667 +0100 +--- ioemu.orig/hw/piix4acpi.c 2006-12-08 01:35:52.000000000 +0000 ++++ ioemu/hw/piix4acpi.c 2006-12-08 01:35:59.000000000 +0000 @@ -24,31 +24,30 @@ */ @@ -186,10 +186,3 @@ Index: ioemu/hw/piix4acpi.c /* PIIX4 acpi pci configuration space, func 2 */ void pci_piix4_acpi_init(PCIBus *bus, int devfn) -@@ -384,5 +383,5 @@ - pci_register_io_region((PCIDevice *)d, 4, 0x10, - PCI_ADDRESS_SPACE_IO, acpi_map); - -- acpi_reset (d); -+ acpi_reset(d); - } diff --git a/tools/ioemu/patches/domain-destroy b/tools/ioemu/patches/domain-destroy index be630fd895..998325e9fe 100644 --- a/tools/ioemu/patches/domain-destroy +++ b/tools/ioemu/patches/domain-destroy @@ -1,7 +1,7 @@ Index: ioemu/monitor.c =================================================================== ---- ioemu.orig/monitor.c 2006-08-17 19:37:36.489509621 +0100 -+++ ioemu/monitor.c 2006-08-17 19:49:44.491850141 +0100 +--- ioemu.orig/monitor.c 2006-12-08 01:26:07.000000000 +0000 ++++ ioemu/monitor.c 2006-12-08 01:26:08.000000000 +0000 @@ -308,6 +308,7 @@ static void do_quit(void) @@ -12,11 +12,11 @@ Index: ioemu/monitor.c Index: ioemu/target-i386-dm/helper2.c =================================================================== ---- ioemu.orig/target-i386-dm/helper2.c 2006-08-17 19:49:40.116333768 +0100 -+++ ioemu/target-i386-dm/helper2.c 2006-08-17 19:49:44.491850141 +0100 -@@ -488,5 +488,25 @@ - xc_evtchn_notify(xce_handle, ioreq_local_port[send_vcpu]); - } +--- ioemu.orig/target-i386-dm/helper2.c 2006-12-08 01:26:08.000000000 +0000 ++++ ioemu/target-i386-dm/helper2.c 2006-12-08 01:26:08.000000000 +0000 +@@ -507,5 +507,25 @@ + /* Wait up to 10 msec. */ + main_loop_wait(10); } + destroy_hvm_domain(); return 0; @@ -42,8 +42,8 @@ Index: ioemu/target-i386-dm/helper2.c +} Index: ioemu/vl.h =================================================================== ---- ioemu.orig/vl.h 2006-08-17 19:49:40.120333326 +0100 -+++ ioemu/vl.h 2006-08-17 19:49:44.492850031 +0100 +--- ioemu.orig/vl.h 2006-12-08 01:26:08.000000000 +0000 ++++ ioemu/vl.h 2006-12-08 01:26:08.000000000 +0000 @@ -1190,4 +1190,7 @@ void kqemu_record_dump(void); diff --git a/tools/ioemu/patches/domain-reset b/tools/ioemu/patches/domain-reset index 838e345d2a..c754cfaf78 100644 --- a/tools/ioemu/patches/domain-reset +++ b/tools/ioemu/patches/domain-reset @@ -1,7 +1,7 @@ Index: ioemu/target-i386-dm/helper2.c =================================================================== ---- ioemu.orig/target-i386-dm/helper2.c 2006-08-17 19:37:36.530505066 +0100 -+++ ioemu/target-i386-dm/helper2.c 2006-08-17 19:49:40.116333768 +0100 +--- ioemu.orig/target-i386-dm/helper2.c 2006-12-08 01:26:06.000000000 +0000 ++++ ioemu/target-i386-dm/helper2.c 2006-12-08 01:26:08.000000000 +0000 @@ -127,6 +127,25 @@ /* called from main_cpu_reset */ void cpu_reset(CPUX86State *env) @@ -28,7 +28,7 @@ Index: ioemu/target-i386-dm/helper2.c } void cpu_x86_close(CPUX86State *env) -@@ -455,6 +474,10 @@ +@@ -479,6 +498,10 @@ if (vm_running) { if (shutdown_requested) break; @@ -41,8 +41,8 @@ Index: ioemu/target-i386-dm/helper2.c /* Wait up to 10 msec. */ Index: ioemu/vl.c =================================================================== ---- ioemu.orig/vl.c 2006-08-17 19:49:39.442408257 +0100 -+++ ioemu/vl.c 2006-08-17 19:49:40.119333436 +0100 +--- ioemu.orig/vl.c 2006-12-08 01:26:08.000000000 +0000 ++++ ioemu/vl.c 2006-12-08 01:26:08.000000000 +0000 @@ -4948,7 +4948,7 @@ } QEMUResetEntry; @@ -54,8 +54,8 @@ Index: ioemu/vl.c Index: ioemu/vl.h =================================================================== ---- ioemu.orig/vl.h 2006-08-17 19:47:32.680418959 +0100 -+++ ioemu/vl.h 2006-08-17 19:49:40.120333326 +0100 +--- ioemu.orig/vl.h 2006-12-08 01:26:07.000000000 +0000 ++++ ioemu/vl.h 2006-12-08 01:26:08.000000000 +0000 @@ -131,6 +131,7 @@ void qemu_register_reset(QEMUResetHandler *func, void *opaque); diff --git a/tools/ioemu/patches/domain-timeoffset b/tools/ioemu/patches/domain-timeoffset index 6588314efe..45e081381d 100644 --- a/tools/ioemu/patches/domain-timeoffset +++ b/tools/ioemu/patches/domain-timeoffset @@ -1,7 +1,7 @@ Index: ioemu/hw/mc146818rtc.c =================================================================== ---- ioemu.orig/hw/mc146818rtc.c 2006-10-24 14:45:21.000000000 +0100 -+++ ioemu/hw/mc146818rtc.c 2006-10-24 14:45:39.000000000 +0100 +--- ioemu.orig/hw/mc146818rtc.c 2006-12-08 18:36:31.000000000 +0000 ++++ ioemu/hw/mc146818rtc.c 2006-12-08 18:36:36.000000000 +0000 @@ -178,10 +178,27 @@ } } @@ -46,8 +46,8 @@ Index: ioemu/hw/mc146818rtc.c static void rtc_copy_date(RTCState *s) Index: ioemu/hw/pc.c =================================================================== ---- ioemu.orig/hw/pc.c 2006-10-24 14:45:38.000000000 +0100 -+++ ioemu/hw/pc.c 2006-10-24 14:45:39.000000000 +0100 +--- ioemu.orig/hw/pc.c 2006-12-08 18:36:35.000000000 +0000 ++++ ioemu/hw/pc.c 2006-12-08 18:36:36.000000000 +0000 @@ -159,7 +159,7 @@ } @@ -117,8 +117,8 @@ Index: ioemu/hw/pc.c QEMUMachine pc_machine = { Index: ioemu/vl.c =================================================================== ---- ioemu.orig/vl.c 2006-10-24 14:45:38.000000000 +0100 -+++ ioemu/vl.c 2006-10-24 14:45:39.000000000 +0100 +--- ioemu.orig/vl.c 2006-12-08 18:36:35.000000000 +0000 ++++ ioemu/vl.c 2006-12-08 18:36:36.000000000 +0000 @@ -163,6 +163,8 @@ int xc_handle; @@ -152,7 +152,7 @@ Index: ioemu/vl.c { NULL }, }; -@@ -6248,6 +6253,9 @@ +@@ -6232,6 +6237,9 @@ vcpus = atoi(optarg); fprintf(logfile, "qemu: the number of cpus is %d\n", vcpus); break; @@ -162,7 +162,7 @@ Index: ioemu/vl.c } } } -@@ -6507,7 +6515,8 @@ +@@ -6492,7 +6500,8 @@ machine->init(ram_size, vga_ram_size, boot_device, ds, fd_filename, snapshot, @@ -174,8 +174,8 @@ Index: ioemu/vl.c if (usb_enabled) { Index: ioemu/vl.h =================================================================== ---- ioemu.orig/vl.h 2006-10-24 14:45:38.000000000 +0100 -+++ ioemu/vl.h 2006-10-24 14:45:39.000000000 +0100 +--- ioemu.orig/vl.h 2006-12-08 18:36:35.000000000 +0000 ++++ ioemu/vl.h 2006-12-08 18:36:36.000000000 +0000 @@ -576,7 +576,7 @@ int boot_device, DisplayState *ds, const char **fd_filename, int snapshot, diff --git a/tools/ioemu/patches/fix-interrupt-routing b/tools/ioemu/patches/fix-interrupt-routing new file mode 100644 index 0000000000..b5d8285398 --- /dev/null +++ b/tools/ioemu/patches/fix-interrupt-routing @@ -0,0 +1,459 @@ +# HG changeset patch +# User kfraser@localhost.localdomain +# Node ID f555a90bcc373a7379bc18f875eac5e7c7122ae9 +# Parent b80f00215bbaf2050765e557f1a017a71e1e8529 +[HVM] Reworked interrupt distribution logic. + +TODO: + 1. Fix IO-APIC ID to not conflict with LAPIC IDS. + 2. Fix i8259 device model (seems to work already though!). + 3. Add INTSRC overrides in MPBIOS and ACPI tables so + that PCI legacy IRQ routing always ends up at an + IO-APIC input with level trigger. Restricting link + routing to {5,6,10,11} and setting overrides for all + four of those would work. + +Signed-off-by: Keir Fraser + +Index: ioemu/Makefile.target +=================================================================== +--- ioemu.orig/Makefile.target 2006-12-08 18:21:56.000000000 +0000 ++++ ioemu/Makefile.target 2006-12-08 18:22:35.000000000 +0000 +@@ -298,7 +298,7 @@ + ifeq ($(ARCH),ia64) + LIBOBJS=helper2.o exec-dm.o i8259-dm.o + else +-LIBOBJS=helper2.o exec-dm.o i8259-dm.o rtc-dm.o ++LIBOBJS=helper2.o exec-dm.o i8259-dm.o rtc-dm.o piix_pci-dm.o + endif + + all: $(PROGS) +@@ -360,11 +360,11 @@ + # Hardware support + VL_OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) + ifeq ($(ARCH),ia64) +-VL_OBJS+= fdc.o mc146818rtc.o serial.o pc.o ++VL_OBJS+= fdc.o mc146818rtc.o serial.o pc.o piix_pci.o + else + VL_OBJS+= fdc.o serial.o pc.o + endif +-VL_OBJS+= cirrus_vga.o mixeng.o parallel.o acpi.o piix_pci.o ++VL_OBJS+= cirrus_vga.o mixeng.o parallel.o acpi.o + VL_OBJS+= usb-uhci.o + VL_OBJS+= piix4acpi.o + VL_OBJS+= xenstore.o +Index: ioemu/target-i386-dm/i8259-dm.c +=================================================================== +--- ioemu.orig/target-i386-dm/i8259-dm.c 2006-12-08 18:21:36.000000000 +0000 ++++ ioemu/target-i386-dm/i8259-dm.c 2006-12-08 18:22:35.000000000 +0000 +@@ -33,7 +33,7 @@ + + void pic_set_irq_new(void *opaque, int irq, int level) + { +- xc_hvm_set_irq_level(xc_handle, domid, irq, level); ++ xc_hvm_set_isa_irq_level(xc_handle, domid, irq, level); + } + + /* obsolete function */ +Index: ioemu/target-i386-dm/piix_pci-dm.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ ioemu/target-i386-dm/piix_pci-dm.c 2006-12-08 18:22:35.000000000 +0000 +@@ -0,0 +1,397 @@ ++/* ++ * QEMU i440FX/PIIX3 PCI Bridge Emulation ++ * ++ * Copyright (c) 2006 Fabrice Bellard ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++ ++#include "vl.h" ++typedef uint32_t pci_addr_t; ++#include "hw/pci_host.h" ++ ++typedef PCIHostState I440FXState; ++ ++static void i440fx_addr_writel(void* opaque, uint32_t addr, uint32_t val) ++{ ++ I440FXState *s = opaque; ++ s->config_reg = val; ++} ++ ++static uint32_t i440fx_addr_readl(void* opaque, uint32_t addr) ++{ ++ I440FXState *s = opaque; ++ return s->config_reg; ++} ++ ++static void i440fx_set_irq(PCIDevice *pci_dev, void *pic, int intx, int level) ++{ ++ xc_hvm_set_pci_intx_level(xc_handle, domid, 0, 0, pci_dev->devfn >> 3, ++ intx, level); ++} ++ ++PCIBus *i440fx_init(void) ++{ ++ PCIBus *b; ++ PCIDevice *d; ++ I440FXState *s; ++ ++ s = qemu_mallocz(sizeof(I440FXState)); ++ b = pci_register_bus(i440fx_set_irq, NULL, 0); ++ s->bus = b; ++ ++ register_ioport_write(0xcf8, 4, 4, i440fx_addr_writel, s); ++ register_ioport_read(0xcf8, 4, 4, i440fx_addr_readl, s); ++ ++ register_ioport_write(0xcfc, 4, 1, pci_host_data_writeb, s); ++ register_ioport_write(0xcfc, 4, 2, pci_host_data_writew, s); ++ register_ioport_write(0xcfc, 4, 4, pci_host_data_writel, s); ++ register_ioport_read(0xcfc, 4, 1, pci_host_data_readb, s); ++ register_ioport_read(0xcfc, 4, 2, pci_host_data_readw, s); ++ register_ioport_read(0xcfc, 4, 4, pci_host_data_readl, s); ++ ++ d = pci_register_device(b, "i440FX", sizeof(PCIDevice), 0, ++ NULL, NULL); ++ ++ d->config[0x00] = 0x86; // vendor_id ++ d->config[0x01] = 0x80; ++ d->config[0x02] = 0x37; // device_id ++ d->config[0x03] = 0x12; ++ d->config[0x08] = 0x02; // revision ++ d->config[0x0a] = 0x00; // class_sub = host2pci ++ d->config[0x0b] = 0x06; // class_base = PCI_bridge ++ d->config[0x0e] = 0x00; // header_type ++ return b; ++} ++ ++/* PIIX3 PCI to ISA bridge */ ++ ++static PCIDevice *piix3_dev; ++ ++static int pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num) ++{ ++ /* This is the barber's pole mapping used by Xen. */ ++ return (irq_num + (pci_dev->devfn >> 3)) & 3; ++} ++ ++static void piix3_write_config(PCIDevice *d, ++ uint32_t address, uint32_t val, int len) ++{ ++ int i; ++ ++ /* Scan for updates to PCI link routes (0x60-0x63). */ ++ for (i = 0; i < len; i++) { ++ uint8_t v = (val >> (8*i)) & 0xff; ++ if (v & 0x80) ++ v = 0; ++ v &= 0xf; ++ if (((address+i) >= 0x60) && ((address+i) <= 0x63)) ++ xc_hvm_set_pci_link_route(xc_handle, domid, address + i - 0x60, v); ++ } ++ ++ /* Hand off to default logic. */ ++ pci_default_write_config(d, address, val, len); ++} ++ ++static void piix3_reset(PCIDevice *d) ++{ ++ uint8_t *pci_conf = d->config; ++ ++ pci_conf[0x04] = 0x07; // master, memory and I/O ++ pci_conf[0x05] = 0x00; ++ pci_conf[0x06] = 0x00; ++ pci_conf[0x07] = 0x02; // PCI_status_devsel_medium ++ pci_conf[0x4c] = 0x4d; ++ pci_conf[0x4e] = 0x03; ++ pci_conf[0x4f] = 0x00; ++ pci_conf[0x60] = 0x80; ++ pci_conf[0x61] = 0x80; ++ pci_conf[0x62] = 0x80; ++ pci_conf[0x63] = 0x80; ++ pci_conf[0x69] = 0x02; ++ pci_conf[0x70] = 0x80; ++ pci_conf[0x76] = 0x0c; ++ pci_conf[0x77] = 0x0c; ++ pci_conf[0x78] = 0x02; ++ pci_conf[0x79] = 0x00; ++ pci_conf[0x80] = 0x00; ++ pci_conf[0x82] = 0x00; ++ pci_conf[0xa0] = 0x08; ++ pci_conf[0xa0] = 0x08; ++ pci_conf[0xa2] = 0x00; ++ pci_conf[0xa3] = 0x00; ++ pci_conf[0xa4] = 0x00; ++ pci_conf[0xa5] = 0x00; ++ pci_conf[0xa6] = 0x00; ++ pci_conf[0xa7] = 0x00; ++ pci_conf[0xa8] = 0x0f; ++ pci_conf[0xaa] = 0x00; ++ pci_conf[0xab] = 0x00; ++ pci_conf[0xac] = 0x00; ++ pci_conf[0xae] = 0x00; ++} ++ ++int piix3_init(PCIBus *bus) ++{ ++ PCIDevice *d; ++ uint8_t *pci_conf; ++ ++ d = pci_register_device(bus, "PIIX3", sizeof(PCIDevice), ++ -1, NULL, piix3_write_config); ++ register_savevm("PIIX3", 0, 1, generic_pci_save, generic_pci_load, d); ++ ++ piix3_dev = d; ++ pci_conf = d->config; ++ ++ pci_conf[0x00] = 0x86; // Intel ++ pci_conf[0x01] = 0x80; ++ pci_conf[0x02] = 0x00; // 82371SB PIIX3 PCI-to-ISA bridge (Step A1) ++ pci_conf[0x03] = 0x70; ++ pci_conf[0x0a] = 0x01; // class_sub = PCI_ISA ++ pci_conf[0x0b] = 0x06; // class_base = PCI_bridge ++ pci_conf[0x0e] = 0x80; // header_type = PCI_multifunction, generic ++ ++ piix3_reset(d); ++ return d->devfn; ++} ++ ++/***********************************************************/ ++/* XXX: the following should be moved to the PC BIOS */ ++ ++static __attribute__((unused)) uint32_t isa_inb(uint32_t addr) ++{ ++ return cpu_inb(NULL, addr); ++} ++ ++static void isa_outb(uint32_t val, uint32_t addr) ++{ ++ cpu_outb(NULL, addr, val); ++} ++ ++static __attribute__((unused)) uint32_t isa_inw(uint32_t addr) ++{ ++ return cpu_inw(NULL, addr); ++} ++ ++static __attribute__((unused)) void isa_outw(uint32_t val, uint32_t addr) ++{ ++ cpu_outw(NULL, addr, val); ++} ++ ++static __attribute__((unused)) uint32_t isa_inl(uint32_t addr) ++{ ++ return cpu_inl(NULL, addr); ++} ++ ++static __attribute__((unused)) void isa_outl(uint32_t val, uint32_t addr) ++{ ++ cpu_outl(NULL, addr, val); ++} ++ ++static uint32_t pci_bios_io_addr; ++static uint32_t pci_bios_mem_addr; ++/* host irqs corresponding to PCI irqs A-D */ ++static uint8_t pci_irqs[4] = { 10, 11, 10, 11 }; ++ ++static void pci_config_writel(PCIDevice *d, uint32_t addr, uint32_t val) ++{ ++ PCIBus *s = d->bus; ++ addr |= (pci_bus_num(s) << 16) | (d->devfn << 8); ++ pci_data_write(s, addr, val, 4); ++} ++ ++static void pci_config_writew(PCIDevice *d, uint32_t addr, uint32_t val) ++{ ++ PCIBus *s = d->bus; ++ addr |= (pci_bus_num(s) << 16) | (d->devfn << 8); ++ pci_data_write(s, addr, val, 2); ++} ++ ++static void pci_config_writeb(PCIDevice *d, uint32_t addr, uint32_t val) ++{ ++ PCIBus *s = d->bus; ++ addr |= (pci_bus_num(s) << 16) | (d->devfn << 8); ++ pci_data_write(s, addr, val, 1); ++} ++ ++static __attribute__((unused)) uint32_t pci_config_readl(PCIDevice *d, uint32_t addr) ++{ ++ PCIBus *s = d->bus; ++ addr |= (pci_bus_num(s) << 16) | (d->devfn << 8); ++ return pci_data_read(s, addr, 4); ++} ++ ++static uint32_t pci_config_readw(PCIDevice *d, uint32_t addr) ++{ ++ PCIBus *s = d->bus; ++ addr |= (pci_bus_num(s) << 16) | (d->devfn << 8); ++ return pci_data_read(s, addr, 2); ++} ++ ++static uint32_t pci_config_readb(PCIDevice *d, uint32_t addr) ++{ ++ PCIBus *s = d->bus; ++ addr |= (pci_bus_num(s) << 16) | (d->devfn << 8); ++ return pci_data_read(s, addr, 1); ++} ++ ++static void pci_set_io_region_addr(PCIDevice *d, int region_num, uint32_t addr) ++{ ++ PCIIORegion *r; ++ uint16_t cmd; ++ uint32_t ofs; ++ ++ if ( region_num == PCI_ROM_SLOT ) { ++ ofs = 0x30; ++ }else{ ++ ofs = 0x10 + region_num * 4; ++ } ++ ++ pci_config_writel(d, ofs, addr); ++ r = &d->io_regions[region_num]; ++ ++ /* enable memory mappings */ ++ cmd = pci_config_readw(d, PCI_COMMAND); ++ if ( region_num == PCI_ROM_SLOT ) ++ cmd |= 2; ++ else if (r->type & PCI_ADDRESS_SPACE_IO) ++ cmd |= 1; ++ else ++ cmd |= 2; ++ pci_config_writew(d, PCI_COMMAND, cmd); ++} ++ ++static void pci_bios_init_device(PCIDevice *d) ++{ ++ int class; ++ PCIIORegion *r; ++ uint32_t *paddr; ++ int i, pin, pic_irq, vendor_id, device_id; ++ ++ class = pci_config_readw(d, PCI_CLASS_DEVICE); ++ vendor_id = pci_config_readw(d, PCI_VENDOR_ID); ++ device_id = pci_config_readw(d, PCI_DEVICE_ID); ++ switch(class) { ++ case 0x0101: ++ if (vendor_id == 0x8086 && device_id == 0x7010) { ++ /* PIIX3 IDE */ ++ pci_config_writew(d, 0x40, 0x8000); // enable IDE0 ++ pci_config_writew(d, 0x42, 0x8000); // enable IDE1 ++ goto default_map; ++ } else { ++ /* IDE: we map it as in ISA mode */ ++ pci_set_io_region_addr(d, 0, 0x1f0); ++ pci_set_io_region_addr(d, 1, 0x3f4); ++ pci_set_io_region_addr(d, 2, 0x170); ++ pci_set_io_region_addr(d, 3, 0x374); ++ } ++ break; ++ case 0x0680: ++ if (vendor_id == 0x8086 && device_id == 0x7113) { ++ /* ++ * PIIX4 ACPI PM. ++ * Special device with special PCI config space. No ordinary BARs. ++ */ ++ pci_config_writew(d, 0x20, 0x0000); // No smb bus IO enable ++ pci_config_writew(d, 0x22, 0x0000); ++ pci_config_writew(d, 0x3c, 0x0009); // Hardcoded IRQ9 ++ pci_config_writew(d, 0x3d, 0x0001); ++ } ++ break; ++ case 0x0300: ++ if (vendor_id != 0x1234) ++ goto default_map; ++ /* VGA: map frame buffer to default Bochs VBE address */ ++ pci_set_io_region_addr(d, 0, 0xE0000000); ++ break; ++ case 0x0800: ++ /* PIC */ ++ vendor_id = pci_config_readw(d, PCI_VENDOR_ID); ++ device_id = pci_config_readw(d, PCI_DEVICE_ID); ++ if (vendor_id == 0x1014) { ++ /* IBM */ ++ if (device_id == 0x0046 || device_id == 0xFFFF) { ++ /* MPIC & MPIC2 */ ++ pci_set_io_region_addr(d, 0, 0x80800000 + 0x00040000); ++ } ++ } ++ break; ++ case 0xff00: ++ if (vendor_id == 0x0106b && ++ (device_id == 0x0017 || device_id == 0x0022)) { ++ /* macio bridge */ ++ pci_set_io_region_addr(d, 0, 0x80800000); ++ } ++ break; ++ default: ++ default_map: ++ /* default memory mappings */ ++ for(i = 0; i < PCI_NUM_REGIONS; i++) { ++ r = &d->io_regions[i]; ++ if (r->size) { ++ if (r->type & PCI_ADDRESS_SPACE_IO) ++ paddr = &pci_bios_io_addr; ++ else ++ paddr = &pci_bios_mem_addr; ++ *paddr = (*paddr + r->size - 1) & ~(r->size - 1); ++ pci_set_io_region_addr(d, i, *paddr); ++ *paddr += r->size; ++ } ++ } ++ break; ++ } ++ ++ /* map the interrupt */ ++ pin = pci_config_readb(d, PCI_INTERRUPT_PIN); ++ if (pin != 0) { ++ pin = pci_slot_get_pirq(d, pin - 1); ++ pic_irq = pci_irqs[pin]; ++ pci_config_writeb(d, PCI_INTERRUPT_LINE, pic_irq); ++ } ++} ++ ++/* ++ * This function initializes the PCI devices as a normal PCI BIOS ++ * would do. It is provided just in case the BIOS has no support for ++ * PCI. ++ */ ++void pci_bios_init(void) ++{ ++ int i, irq; ++ uint8_t elcr[2]; ++ ++ pci_bios_io_addr = 0xc000; ++ pci_bios_mem_addr = HVM_BELOW_4G_MMIO_START; ++ ++ /* activate IRQ mappings */ ++ elcr[0] = 0x00; ++ elcr[1] = 0x00; ++ for(i = 0; i < 4; i++) { ++ irq = pci_irqs[i]; ++ /* set to trigger level */ ++ elcr[irq >> 3] |= (1 << (irq & 7)); ++ /* activate irq remapping in PIIX */ ++ pci_config_writeb(piix3_dev, 0x60 + i, irq); ++ } ++ isa_outb(elcr[0], 0x4d0); ++ isa_outb(elcr[1], 0x4d1); ++ ++ pci_for_each_device(pci_bios_init_device); ++} ++ diff --git a/tools/ioemu/patches/hypervisor-pit b/tools/ioemu/patches/hypervisor-pit index 8d151a1b95..012254f615 100644 --- a/tools/ioemu/patches/hypervisor-pit +++ b/tools/ioemu/patches/hypervisor-pit @@ -1,8 +1,8 @@ Index: ioemu/Makefile.target =================================================================== ---- ioemu.orig/Makefile.target 2006-08-17 19:49:33.813030472 +0100 -+++ ioemu/Makefile.target 2006-08-17 19:49:50.228216099 +0100 -@@ -354,7 +354,7 @@ +--- ioemu.orig/Makefile.target 2006-12-08 01:41:12.000000000 +0000 ++++ ioemu/Makefile.target 2006-12-08 01:41:12.000000000 +0000 +@@ -355,7 +355,7 @@ ifeq ($(TARGET_BASE_ARCH), i386) # Hardware support VL_OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) @@ -13,8 +13,8 @@ Index: ioemu/Makefile.target DEFINES += -DHAS_AUDIO Index: ioemu/hw/pc.c =================================================================== ---- ioemu.orig/hw/pc.c 2006-08-17 19:49:35.507843144 +0100 -+++ ioemu/hw/pc.c 2006-08-17 19:49:50.229215988 +0100 +--- ioemu.orig/hw/pc.c 2006-12-08 01:41:12.000000000 +0000 ++++ ioemu/hw/pc.c 2006-12-08 01:41:12.000000000 +0000 @@ -38,7 +38,9 @@ static fdctrl_t *floppy_controller; @@ -38,8 +38,8 @@ Index: ioemu/hw/pc.c pic_set_alt_irq_func(isa_pic, ioapic_set_irq, ioapic); Index: ioemu/vl.c =================================================================== ---- ioemu.orig/vl.c 2006-08-17 19:49:48.566399780 +0100 -+++ ioemu/vl.c 2006-08-17 19:49:50.231215767 +0100 +--- ioemu.orig/vl.c 2006-12-08 01:41:12.000000000 +0000 ++++ ioemu/vl.c 2006-12-08 01:41:12.000000000 +0000 @@ -5570,6 +5570,7 @@ #ifdef HAS_AUDIO diff --git a/tools/ioemu/patches/hypervisor-rtc b/tools/ioemu/patches/hypervisor-rtc index 7563425e3b..a3357ea9bd 100644 --- a/tools/ioemu/patches/hypervisor-rtc +++ b/tools/ioemu/patches/hypervisor-rtc @@ -5,9 +5,11 @@ [HVM] Move RTC emulation into the hypervisor. Signed-off-by: Xiaowei Yang ---- ioemu/Makefile.target Wed Oct 18 18:13:57 2006 +0100 -+++ ioemu/Makefile.target Wed Oct 18 18:35:21 2006 +0100 -@@ -294,7 +294,11 @@ endif +Index: ioemu/Makefile.target +=================================================================== +--- ioemu.orig/Makefile.target 2006-12-08 01:41:15.000000000 +0000 ++++ ioemu/Makefile.target 2006-12-08 01:41:15.000000000 +0000 +@@ -295,7 +295,11 @@ endif # qemu-dm objects @@ -19,7 +21,7 @@ Signed-off-by: Xiaowei Yang all: $(PROGS) -@@ -354,7 +358,11 @@ ifeq ($(TARGET_BASE_ARCH), i386) +@@ -355,7 +359,11 @@ ifeq ($(TARGET_BASE_ARCH), i386) # Hardware support VL_OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) @@ -31,8 +33,10 @@ Signed-off-by: Xiaowei Yang VL_OBJS+= cirrus_vga.o mixeng.o parallel.o acpi.o piix_pci.o VL_OBJS+= usb-uhci.o VL_OBJS+= piix4acpi.o ---- /dev/null Thu Jan 01 00:00:00 1970 +0000 -+++ ioemu/target-i386-dm/rtc-dm.c Wed Oct 18 18:35:21 2006 +0100 +Index: ioemu/target-i386-dm/rtc-dm.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ ioemu/target-i386-dm/rtc-dm.c 2006-12-08 01:41:15.000000000 +0000 @@ -0,0 +1,107 @@ +/* + * QEMU MC146818 RTC emulation diff --git a/tools/ioemu/patches/ide-error-reporting b/tools/ioemu/patches/ide-error-reporting new file mode 100644 index 0000000000..7314a49c06 --- /dev/null +++ b/tools/ioemu/patches/ide-error-reporting @@ -0,0 +1,110 @@ +# HG changeset patch +# User kfraser@localhost.localdomain +# Node ID fd28a1b139dea91b8bfcf06dd233dbdda8f51ff1 +# Parent d8befb109c394c2c2d3e1870a500107d461724ef +[QEMU] Error reporting in IDE device model. + +Following on from my patch to make blktap report I/O errors back to +guest OS, a similar problem exists in the QEMU codebase. The IDE +driver never reports I/O errors during read/write operations back to +the guest OS. Instead all I/O operations are reported as +succesfull. If, for example, the host FS holding the disk image fills +up, then writes may fail due to lack of space. Since the guest OS +never sees these failures, it assumes all is well & will continue +writing. Eventually this can lead to severe & unrecoverable filesystem +corruption. + +The attached patch fixes QEMU ide driver such that any failure of a +read or write operation sets the appropriate IDE status/error +registers. Having read the ATA-6 spec I think the most compliant +behaviour is to set the status register to 'READY_STAT | ERR_STAT', +and the error register to ABRT_ERR. There is already a convenience +function ide_abort_command() in the QEMU codebase which does just +this, so the attached patch simply calls that function. + +With this patch the guest OS sees the I/O failure & the kernel logs +IDE errors and then retries the operation. This at least ensures that +the guest can be shutdown the out of space issue in the host corrected +and the guest restarted, without any serious filesystem damage having +occurred. + +From: Daniel Berrange +Signed-off-by: Keir Fraser + +Index: ioemu/hw/ide.c +=================================================================== +--- ioemu.orig/hw/ide.c 2006-12-08 18:21:36.000000000 +0000 ++++ ioemu/hw/ide.c 2006-12-08 18:23:18.000000000 +0000 +@@ -680,7 +680,7 @@ + static void ide_sector_read(IDEState *s) + { + int64_t sector_num; +- int ret, n; ++ int n; + + s->status = READY_STAT | SEEK_STAT; + s->error = 0; /* not needed by IDE spec, but needed by Windows */ +@@ -695,7 +695,11 @@ + #endif + if (n > s->req_nb_sectors) + n = s->req_nb_sectors; +- ret = bdrv_read(s->bs, sector_num, s->io_buffer, n); ++ if (bdrv_read(s->bs, sector_num, s->io_buffer, n) != 0) { ++ ide_abort_command(s); ++ ide_set_irq(s); ++ return; ++ } + ide_transfer_start(s, s->io_buffer, 512 * n, ide_sector_read); + ide_set_irq(s); + ide_set_sector(s, sector_num + n); +@@ -721,7 +725,11 @@ + if (n > MAX_MULT_SECTORS) + n = MAX_MULT_SECTORS; + sector_num = ide_get_sector(s); +- bdrv_read(s->bs, sector_num, s->io_buffer, n); ++ if (bdrv_read(s->bs, sector_num, s->io_buffer, n) != 0) { ++ ide_abort_command(s); ++ ide_set_irq(s); ++ return 0; ++ } + s->io_buffer_index = 0; + s->io_buffer_size = n * 512; + len = s->io_buffer_size; +@@ -767,7 +775,7 @@ + static void ide_sector_write(IDEState *s) + { + int64_t sector_num; +- int ret, n, n1; ++ int n, n1; + + s->status = READY_STAT | SEEK_STAT; + sector_num = ide_get_sector(s); +@@ -777,7 +785,11 @@ + n = s->nsector; + if (n > s->req_nb_sectors) + n = s->req_nb_sectors; +- ret = bdrv_write(s->bs, sector_num, s->io_buffer, n); ++ if (bdrv_write(s->bs, sector_num, s->io_buffer, n) != 0) { ++ ide_abort_command(s); ++ ide_set_irq(s); ++ return; ++ } + s->nsector -= n; + if (s->nsector == 0) { + /* no more sector to write */ +@@ -823,8 +835,13 @@ + if (len == 0) { + n = s->io_buffer_size >> 9; + sector_num = ide_get_sector(s); +- bdrv_write(s->bs, sector_num, s->io_buffer, +- s->io_buffer_size >> 9); ++ if (bdrv_write(s->bs, sector_num, s->io_buffer, ++ s->io_buffer_size >> 9) != 0) { ++ ide_abort_command(s); ++ ide_set_irq(s); ++ return 0; ++ } ++ + sector_num += n; + ide_set_sector(s, sector_num); + s->nsector -= n; diff --git a/tools/ioemu/patches/ioemu-ia64 b/tools/ioemu/patches/ioemu-ia64 index b108178f73..d8afe7fe3c 100644 --- a/tools/ioemu/patches/ioemu-ia64 +++ b/tools/ioemu/patches/ioemu-ia64 @@ -1,7 +1,7 @@ Index: ioemu/hw/iommu.c =================================================================== ---- ioemu.orig/hw/iommu.c 2006-08-17 19:37:36.791476068 +0100 -+++ ioemu/hw/iommu.c 2006-08-17 19:48:27.357375720 +0100 +--- ioemu.orig/hw/iommu.c 2006-12-08 02:02:07.000000000 +0000 ++++ ioemu/hw/iommu.c 2006-12-08 02:02:34.000000000 +0000 @@ -82,7 +82,11 @@ #define IOPTE_VALID 0x00000002 /* IOPTE is valid */ #define IOPTE_WAZ 0x00000001 /* Write as zeros */ @@ -16,8 +16,8 @@ Index: ioemu/hw/iommu.c Index: ioemu/cpu-all.h =================================================================== ---- ioemu.orig/cpu-all.h 2006-08-17 19:37:36.791476068 +0100 -+++ ioemu/cpu-all.h 2006-08-17 19:48:27.358375609 +0100 +--- ioemu.orig/cpu-all.h 2006-12-08 02:02:07.000000000 +0000 ++++ ioemu/cpu-all.h 2006-12-08 02:02:34.000000000 +0000 @@ -835,6 +835,31 @@ :"=m" (*(volatile long *)addr) :"dIr" (nr)); @@ -52,36 +52,36 @@ Index: ioemu/cpu-all.h /* memory API */ Index: ioemu/vl.c =================================================================== ---- ioemu.orig/vl.c 2006-08-17 19:47:08.538087284 +0100 -+++ ioemu/vl.c 2006-08-17 19:57:50.666108706 +0100 -@@ -6144,6 +6144,11 @@ - - xc_handle = xc_interface_open(); +--- ioemu.orig/vl.c 2006-12-08 02:02:28.000000000 +0000 ++++ ioemu/vl.c 2006-12-08 02:02:34.000000000 +0000 +@@ -6137,6 +6137,11 @@ + exit(1); + } +#if defined (__ia64__) + if (ram_size > MMIO_START) + ram_size += 1 * MEM_G; /* skip 3G-4G MMIO, LEGACY_IO_SPACE etc. */ +#endif + - nr_pages = ram_size/PAGE_SIZE; - tmp_nr_pages = nr_pages; + /* init the memory */ + phys_ram_size = ram_size + vga_ram_size + bios_size; @@ -6161,6 +6166,7 @@ exit(-1); } +#if defined(__i386__) || defined(__x86_64__) - if (xc_get_pfn_list(xc_handle, domid, page_array, nr_pages) != nr_pages) { - fprintf(logfile, "xc_get_pfn_list returned error %d\n", errno); - exit(-1); -@@ -6191,6 +6197,41 @@ + for ( i = 0; i < tmp_nr_pages; i++) + page_array[i] = i; + +@@ -6185,6 +6191,48 @@ free(page_array); +#elif defined(__ia64__) + + if (xc_ia64_get_pfn_list(xc_handle, domid, page_array, -+ IO_PAGE_START >> PAGE_SHIFT, 1) != 1) { ++ IO_PAGE_START >> PAGE_SHIFT, 3) != 3) { + fprintf(logfile, "xc_ia64_get_pfn_list returned error %d\n", errno); + exit(-1); + } @@ -93,6 +93,12 @@ Index: ioemu/vl.c + fprintf(logfile, "shared page at pfn:%lx, mfn: %016lx\n", + IO_PAGE_START >> PAGE_SHIFT, page_array[0]); + ++ buffered_io_page =xc_map_foreign_range(xc_handle, domid, PAGE_SIZE, ++ PROT_READ|PROT_WRITE, ++ page_array[2]); ++ fprintf(logfile, "Buffered IO page at pfn:%lx, mfn: %016lx\n", ++ BUFFER_IO_PAGE_START >> PAGE_SHIFT, page_array[2]); ++ + if (xc_ia64_get_pfn_list(xc_handle, domid, + page_array, 0, nr_pages) != nr_pages) { + fprintf(logfile, "xc_ia64_get_pfn_list returned error %d\n", errno); @@ -100,9 +106,9 @@ Index: ioemu/vl.c + } + + if (ram_size > MMIO_START) { -+ for (i = 0 ; i < MEM_G >> PAGE_SHIFT; i++) -+ page_array[MMIO_START >> PAGE_SHIFT + i] = -+ page_array[IO_PAGE_START >> PAGE_SHIFT + 1]; ++ for (i = 0 ; i < (MEM_G >> PAGE_SHIFT); i++) ++ page_array[(MMIO_START >> PAGE_SHIFT) + i] = ++ page_array[(IO_PAGE_START >> PAGE_SHIFT) + 1]; + } + + phys_ram_base = xc_map_foreign_batch(xc_handle, domid, @@ -112,52 +118,15 @@ Index: ioemu/vl.c + fprintf(logfile, "xc_map_foreign_batch returned error %d\n", errno); + exit(-1); + } ++ free(page_array); +#endif #else /* !CONFIG_DM */ phys_ram_base = qemu_vmalloc(phys_ram_size); -Index: ioemu/target-i386-dm/exec-dm.c -=================================================================== ---- ioemu.orig/target-i386-dm/exec-dm.c 2006-08-17 19:37:36.792475957 +0100 -+++ ioemu/target-i386-dm/exec-dm.c 2006-08-17 19:48:27.361375278 +0100 -@@ -341,6 +341,23 @@ - return io_mem_read[io_index >> IO_MEM_SHIFT]; - } - -+#ifdef __ia64__ -+/* IA64 has seperate I/D cache, with coherence maintained by DMA controller. -+ * So to emulate right behavior that guest OS is assumed, we need to flush -+ * I/D cache here. -+ */ -+static void sync_icache(unsigned long address, int len) -+{ -+ int l; -+ -+ for(l = 0; l < (len + 32); l += 32) -+ __ia64_fc(address + l); -+ -+ ia64_sync_i(); -+ ia64_srlz_i(); -+} -+#endif -+ - /* physical memory access (slow version, mainly for debug) */ - #if defined(CONFIG_USER_ONLY) - void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, -@@ -432,6 +449,9 @@ - /* RAM case */ - ptr = phys_ram_base + addr1; - memcpy(ptr, buf, l); -+#ifdef __ia64__ -+ sync_icache((unsigned long)ptr, l); -+#endif - } - } else { - if (io_index) { Index: ioemu/exec-all.h =================================================================== ---- ioemu.orig/exec-all.h 2006-08-17 19:37:36.791476068 +0100 -+++ ioemu/exec-all.h 2006-08-17 19:48:27.362375167 +0100 +--- ioemu.orig/exec-all.h 2006-12-08 02:02:07.000000000 +0000 ++++ ioemu/exec-all.h 2006-12-08 02:02:34.000000000 +0000 @@ -462,12 +462,13 @@ } #endif @@ -177,9 +146,9 @@ Index: ioemu/exec-all.h Index: ioemu/target-i386-dm/cpu.h =================================================================== ---- ioemu.orig/target-i386-dm/cpu.h 2006-08-17 19:37:36.792475957 +0100 -+++ ioemu/target-i386-dm/cpu.h 2006-08-17 19:48:27.362375167 +0100 -@@ -80,7 +80,11 @@ +--- ioemu.orig/target-i386-dm/cpu.h 2006-12-08 02:02:07.000000000 +0000 ++++ ioemu/target-i386-dm/cpu.h 2006-12-08 02:02:34.000000000 +0000 +@@ -78,7 +78,11 @@ /* helper2.c */ int main_loop(void); @@ -194,7 +163,7 @@ Index: ioemu/target-i386-dm/cpu.h Index: ioemu/ia64_intrinsic.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ ioemu/ia64_intrinsic.h 2006-08-17 19:48:27.363375057 +0100 ++++ ioemu/ia64_intrinsic.h 2006-12-08 02:02:34.000000000 +0000 @@ -0,0 +1,276 @@ +#ifndef IA64_INTRINSIC_H +#define IA64_INTRINSIC_H diff --git a/tools/ioemu/patches/limit-fdc-sector-size-to-16K b/tools/ioemu/patches/limit-fdc-sector-size-to-16K new file mode 100644 index 0000000000..e97973fcec --- /dev/null +++ b/tools/ioemu/patches/limit-fdc-sector-size-to-16K @@ -0,0 +1,32 @@ +# HG changeset patch +# User kfraser@localhost.localdomain +# Node ID f711b87ba951e608287abd0de028c6f0d83400a9 +# Parent f3ee62b7fb5299c89d442845e0883bcfab78c067 +[QEMU] fdc: Limit sector size to 16K + +In fdctrl_start_transfer the sector size field (fifo[5]) is not +checked for overflows. This allows an arbitrarily large sector size +to be used, which can in turn result in a negative data_len field that +is then used for DMA transfers. + +This can lead to the corrpuption of qemu state because some subsequent +checks on the transfer length is conducted using signed integers. + +This patch limits the value fifo[5] to 7 which is the standard limit +on floppy sector size. + +Signed-off-by: Herbert Xu + +Index: ioemu/hw/fdc.c +=================================================================== +--- ioemu.orig/hw/fdc.c 2006-12-08 18:21:36.000000000 +0000 ++++ ioemu/hw/fdc.c 2006-12-08 18:22:57.000000000 +0000 +@@ -898,7 +898,7 @@ + fdctrl->data_len = fdctrl->fifo[8]; + } else { + int tmp; +- fdctrl->data_len = 128 << fdctrl->fifo[5]; ++ fdctrl->data_len = 128 << (fdctrl->fifo[5] > 7 ? 7 : fdctrl->fifo[5]); + tmp = (cur_drv->last_sect - ks + 1); + if (fdctrl->fifo[0] & 0x80) + tmp += cur_drv->last_sect; diff --git a/tools/ioemu/patches/ne2000-bounds-checks b/tools/ioemu/patches/ne2000-bounds-checks new file mode 100644 index 0000000000..5cd37e4aa9 --- /dev/null +++ b/tools/ioemu/patches/ne2000-bounds-checks @@ -0,0 +1,113 @@ +# HG changeset patch +# User kaf24@localhost.localdomain +# Node ID 66fe61db9e69e03e12d0c4086683bebfb4a67780 +# Parent 1940ee13f9d6ab1be2c614a0fbf7769536a056d2 +[QEMU] ne2000: Stop memory access beyond buffer + +As a program that runs in dom0 which serves users from guests, +the qemu drivers need to be vigilant to the input that comes +from the guests since they may be malicious. + +As it is there are multiple ways to get ne2000 to read/write +memory beyond the 48K buffer that it has allocated for each +adapter. + +This patch checks the addresses and prevents this from occuring. + +The boundary is checked each time since it's changed for every +packet received while the other parameters are only changed +(by the guest) during setup. + +Signed-off: Herbert Xu + +Index: ioemu/hw/ne2000.c +=================================================================== +--- ioemu.orig/hw/ne2000.c 2006-12-08 18:20:45.000000000 +0000 ++++ ioemu/hw/ne2000.c 2006-12-08 18:20:53.000000000 +0000 +@@ -137,6 +137,7 @@ + uint8_t curpag; + uint8_t mult[8]; /* multicast mask array */ + int irq; ++ int tainted; + PCIDevice *pci_dev; + VLANClientState *vc; + uint8_t macaddr[6]; +@@ -226,6 +227,27 @@ + + #define MIN_BUF_SIZE 60 + ++static inline int ne2000_valid_ring_addr(NE2000State *s, unsigned int addr) ++{ ++ addr <<= 8; ++ return addr < s->stop && addr >= s->start; ++} ++ ++static inline int ne2000_check_state(NE2000State *s) ++{ ++ if (!s->tainted) ++ return 0; ++ ++ if (s->start >= s->stop || s->stop > NE2000_MEM_SIZE) ++ return -EINVAL; ++ ++ if (!ne2000_valid_ring_addr(s, s->curpag)) ++ return -EINVAL; ++ ++ s->tainted = 0; ++ return 0; ++} ++ + static void ne2000_receive(void *opaque, const uint8_t *buf, int size) + { + NE2000State *s = opaque; +@@ -239,6 +261,12 @@ + printf("NE2000: received len=%d\n", size); + #endif + ++ if (ne2000_check_state(s)) ++ return; ++ ++ if (!ne2000_valid_ring_addr(s, s->boundary)) ++ return; ++ + if (s->cmd & E8390_STOP || ne2000_buffer_full(s)) + return; + +@@ -359,9 +387,11 @@ + switch(offset) { + case EN0_STARTPG: + s->start = val << 8; ++ s->tainted = 1; + break; + case EN0_STOPPG: + s->stop = val << 8; ++ s->tainted = 1; + break; + case EN0_BOUNDARY: + s->boundary = val; +@@ -406,6 +436,7 @@ + break; + case EN1_CURPAG: + s->curpag = val; ++ s->tainted = 1; + break; + case EN1_MULT ... EN1_MULT + 7: + s->mult[offset - EN1_MULT] = val; +@@ -509,7 +540,7 @@ + { + addr &= ~1; /* XXX: check exact behaviour if not even */ + if (addr < 32 || +- (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { ++ (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE - 2)) { + cpu_to_le32wu((uint32_t *)(s->mem + addr), val); + } + } +@@ -539,7 +570,7 @@ + { + addr &= ~1; /* XXX: check exact behaviour if not even */ + if (addr < 32 || +- (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE)) { ++ (addr >= NE2000_PMEM_START && addr < NE2000_MEM_SIZE - 2)) { + return le32_to_cpupu((uint32_t *)(s->mem + addr)); + } else { + return 0xffffffff; diff --git a/tools/ioemu/patches/nodelay-serial-over-tcp b/tools/ioemu/patches/nodelay-serial-over-tcp new file mode 100644 index 0000000000..ec3bde9d95 --- /dev/null +++ b/tools/ioemu/patches/nodelay-serial-over-tcp @@ -0,0 +1,29 @@ +# HG changeset patch +# User PeterJohnston +# Node ID b8cc9ffda0a3dc449b026c72c97f78dea2e6f114 +# Parent a8d2b1393b769048c7b62822e45bef27eef80fb6 +[QEMU] Add TCP_NODELAY to tcp connections exporting serial ports. + +Signed-off-by: Steven Smith + +Index: ioemu/vl.c +=================================================================== +--- ioemu.orig/vl.c 2006-12-08 18:21:56.000000000 +0000 ++++ ioemu/vl.c 2006-12-08 18:22:42.000000000 +0000 +@@ -2530,6 +2530,7 @@ + int is_waitconnect = 1; + const char *ptr; + struct sockaddr_in saddr; ++ int opt; + + if (parse_host_port(&saddr, host_str) < 0) + goto fail; +@@ -2598,6 +2599,8 @@ + } + } + s->fd = fd; ++ opt = 1; ++ setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&opt, sizeof(opt)); + if (s->connected) + tcp_chr_connect(chr); + else diff --git a/tools/ioemu/patches/qemu-bootorder b/tools/ioemu/patches/qemu-bootorder index e162320bdd..e0142b4313 100644 --- a/tools/ioemu/patches/qemu-bootorder +++ b/tools/ioemu/patches/qemu-bootorder @@ -1,7 +1,7 @@ Index: ioemu/vl.c =================================================================== ---- ioemu.orig/vl.c 2006-10-24 14:33:47.000000000 +0100 -+++ ioemu/vl.c 2006-10-24 14:33:47.000000000 +0100 +--- ioemu.orig/vl.c 2006-12-08 02:02:38.000000000 +0000 ++++ ioemu/vl.c 2006-12-08 02:02:38.000000000 +0000 @@ -125,7 +125,7 @@ struct sockaddr_in vnclisten_addr; const char* keyboard_layout = NULL; @@ -11,7 +11,7 @@ Index: ioemu/vl.c uint64_t ram_size; int pit_min_timer_count = 0; int nb_nics; -@@ -6075,14 +6075,14 @@ +@@ -6059,14 +6059,14 @@ break; #endif /* !CONFIG_DM */ case QEMU_OPTION_boot: @@ -32,7 +32,7 @@ Index: ioemu/vl.c exit(1); } break; -@@ -6349,6 +6349,7 @@ +@@ -6333,6 +6333,7 @@ fd_filename[0] == '\0') help(); @@ -40,7 +40,7 @@ Index: ioemu/vl.c /* boot to cd by default if no hard disk */ if (hd_filename[0] == '\0' && boot_device == 'c') { if (fd_filename[0] != '\0') -@@ -6356,6 +6357,7 @@ +@@ -6340,6 +6341,7 @@ else boot_device = 'd'; } @@ -48,7 +48,7 @@ Index: ioemu/vl.c #endif /* !CONFIG_DM */ setvbuf(stdout, NULL, _IOLBF, 0); -@@ -6614,6 +6616,7 @@ +@@ -6598,6 +6600,7 @@ ds, fd_filename, snapshot, kernel_filename, kernel_cmdline, initrd_filename, timeoffset); @@ -58,8 +58,8 @@ Index: ioemu/vl.c if (usb_enabled) { Index: ioemu/vl.h =================================================================== ---- ioemu.orig/vl.h 2006-10-24 14:33:47.000000000 +0100 -+++ ioemu/vl.h 2006-10-24 14:33:47.000000000 +0100 +--- ioemu.orig/vl.h 2006-12-08 02:02:38.000000000 +0000 ++++ ioemu/vl.h 2006-12-08 02:02:38.000000000 +0000 @@ -578,7 +578,7 @@ #ifndef QEMU_TOOL @@ -80,8 +80,8 @@ Index: ioemu/vl.h uint32_t initrd_image, uint32_t initrd_size, Index: ioemu/hw/pc.c =================================================================== ---- ioemu.orig/hw/pc.c 2006-10-24 14:33:47.000000000 +0100 -+++ ioemu/hw/pc.c 2006-10-24 14:33:47.000000000 +0100 +--- ioemu.orig/hw/pc.c 2006-12-08 02:02:38.000000000 +0000 ++++ ioemu/hw/pc.c 2006-12-08 02:02:38.000000000 +0000 @@ -158,8 +158,23 @@ rtc_set_memory(s, info_ofs + 8, sectors); } diff --git a/tools/ioemu/patches/qemu-daemonize b/tools/ioemu/patches/qemu-daemonize index 51568fe2ff..0a3d087460 100644 --- a/tools/ioemu/patches/qemu-daemonize +++ b/tools/ioemu/patches/qemu-daemonize @@ -2,9 +2,9 @@ Changes required because qemu-dm runs daemonized. Index: ioemu/vl.c =================================================================== ---- ioemu.orig/vl.c 2006-10-24 14:33:47.000000000 +0100 -+++ ioemu/vl.c 2006-10-24 14:33:47.000000000 +0100 -@@ -6054,10 +6054,11 @@ +--- ioemu.orig/vl.c 2006-12-08 02:00:42.000000000 +0000 ++++ ioemu/vl.c 2006-12-08 02:00:42.000000000 +0000 +@@ -6038,10 +6038,11 @@ } break; case QEMU_OPTION_nographic: diff --git a/tools/ioemu/patches/qemu-dm b/tools/ioemu/patches/qemu-dm index 056ce40e3a..472cf61428 100644 --- a/tools/ioemu/patches/qemu-dm +++ b/tools/ioemu/patches/qemu-dm @@ -1,8 +1,8 @@ Index: ioemu/Makefile.target =================================================================== ---- ioemu.orig/Makefile.target 2006-08-06 02:14:04.797460093 +0100 -+++ ioemu/Makefile.target 2006-08-06 02:14:09.794902973 +0100 -@@ -302,7 +302,7 @@ +--- ioemu.orig/Makefile.target 2006-12-08 01:41:05.000000000 +0000 ++++ ioemu/Makefile.target 2006-12-08 01:41:10.000000000 +0000 +@@ -303,7 +303,7 @@ endif # must use static linking to avoid leaving stuff in virtual address space @@ -13,8 +13,8 @@ Index: ioemu/Makefile.target VL_OBJS+=tap-win32.o Index: ioemu/configure =================================================================== ---- ioemu.orig/configure 2006-08-06 02:14:04.797460093 +0100 -+++ ioemu/configure 2006-08-06 02:14:09.795902861 +0100 +--- ioemu.orig/configure 2006-12-08 01:40:58.000000000 +0000 ++++ ioemu/configure 2006-12-08 01:41:10.000000000 +0000 @@ -75,8 +75,8 @@ bigendian="no" mingw32="no" @@ -37,8 +37,8 @@ Index: ioemu/configure target_user_only="yes" Index: ioemu/cpu-all.h =================================================================== ---- ioemu.orig/cpu-all.h 2006-08-06 02:14:04.797460093 +0100 -+++ ioemu/cpu-all.h 2006-08-06 02:14:09.796902750 +0100 +--- ioemu.orig/cpu-all.h 2006-12-08 01:40:58.000000000 +0000 ++++ ioemu/cpu-all.h 2006-12-08 01:41:10.000000000 +0000 @@ -690,7 +690,9 @@ void page_set_flags(target_ulong start, target_ulong end, int flags); void page_unprotect_range(target_ulong data, target_ulong data_size); @@ -64,8 +64,8 @@ Index: ioemu/cpu-all.h void cpu_dump_state(CPUState *env, FILE *f, Index: ioemu/disas.h =================================================================== ---- ioemu.orig/disas.h 2006-08-06 02:14:04.797460093 +0100 -+++ ioemu/disas.h 2006-08-06 02:14:09.796902750 +0100 +--- ioemu.orig/disas.h 2006-12-08 01:40:58.000000000 +0000 ++++ ioemu/disas.h 2006-12-08 01:41:10.000000000 +0000 @@ -1,6 +1,7 @@ #ifndef _QEMU_DISAS_H #define _QEMU_DISAS_H @@ -83,8 +83,8 @@ Index: ioemu/disas.h #endif /* _QEMU_DISAS_H */ Index: ioemu/exec-all.h =================================================================== ---- ioemu.orig/exec-all.h 2006-08-06 02:14:04.798459982 +0100 -+++ ioemu/exec-all.h 2006-08-06 02:14:09.796902750 +0100 +--- ioemu.orig/exec-all.h 2006-12-08 01:40:58.000000000 +0000 ++++ ioemu/exec-all.h 2006-12-08 01:41:10.000000000 +0000 @@ -509,7 +509,7 @@ extern int tb_invalidated_flag; @@ -105,8 +105,8 @@ Index: ioemu/exec-all.h return addr; Index: ioemu/hw/pc.c =================================================================== ---- ioemu.orig/hw/pc.c 2006-08-06 02:14:04.797460093 +0100 -+++ ioemu/hw/pc.c 2006-08-06 02:14:09.797902638 +0100 +--- ioemu.orig/hw/pc.c 2006-12-08 01:40:58.000000000 +0000 ++++ ioemu/hw/pc.c 2006-12-08 01:41:10.000000000 +0000 @@ -73,6 +73,7 @@ } } @@ -184,8 +184,8 @@ Index: ioemu/hw/pc.c if (serial_hds[i]) { Index: ioemu/hw/vga_int.h =================================================================== ---- ioemu.orig/hw/vga_int.h 2006-08-06 02:14:04.797460093 +0100 -+++ ioemu/hw/vga_int.h 2006-08-06 02:14:09.797902638 +0100 +--- ioemu.orig/hw/vga_int.h 2006-12-08 01:40:58.000000000 +0000 ++++ ioemu/hw/vga_int.h 2006-12-08 01:41:10.000000000 +0000 @@ -28,7 +28,7 @@ #define ST01_DISP_ENABLE 0x01 @@ -197,8 +197,8 @@ Index: ioemu/hw/vga_int.h #define VBE_DISPI_MAX_YRES 1200 Index: ioemu/monitor.c =================================================================== ---- ioemu.orig/monitor.c 2006-08-06 02:14:04.798459982 +0100 -+++ ioemu/monitor.c 2006-08-06 02:14:49.574468309 +0100 +--- ioemu.orig/monitor.c 2006-12-08 01:40:58.000000000 +0000 ++++ ioemu/monitor.c 2006-12-08 01:41:10.000000000 +0000 @@ -68,6 +68,12 @@ void term_flush(void) @@ -429,8 +429,8 @@ Index: ioemu/monitor.c { Index: ioemu/vl.c =================================================================== ---- ioemu.orig/vl.c 2006-08-06 02:14:04.797460093 +0100 -+++ ioemu/vl.c 2006-08-06 02:14:09.802902081 +0100 +--- ioemu.orig/vl.c 2006-12-08 01:40:58.000000000 +0000 ++++ ioemu/vl.c 2006-12-08 01:41:10.000000000 +0000 @@ -422,12 +422,15 @@ void hw_error(const char *fmt, ...) { diff --git a/tools/ioemu/patches/qemu-no-apic b/tools/ioemu/patches/qemu-no-apic index a2409da86f..68291defab 100644 --- a/tools/ioemu/patches/qemu-no-apic +++ b/tools/ioemu/patches/qemu-no-apic @@ -1,8 +1,8 @@ Index: ioemu/Makefile.target =================================================================== ---- ioemu.orig/Makefile.target 2006-08-06 02:21:42.270461924 +0100 -+++ ioemu/Makefile.target 2006-08-06 02:22:26.380544784 +0100 -@@ -355,7 +355,7 @@ +--- ioemu.orig/Makefile.target 2006-12-08 01:41:11.000000000 +0000 ++++ ioemu/Makefile.target 2006-12-08 01:41:12.000000000 +0000 +@@ -356,7 +356,7 @@ # Hardware support VL_OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) VL_OBJS+= fdc.o mc146818rtc.o serial.o i8254.o pcspk.o pc.o @@ -13,8 +13,8 @@ Index: ioemu/Makefile.target endif Index: ioemu/hw/pc.c =================================================================== ---- ioemu.orig/hw/pc.c 2006-08-06 02:22:01.524315611 +0100 -+++ ioemu/hw/pc.c 2006-08-06 02:22:11.875161758 +0100 +--- ioemu.orig/hw/pc.c 2006-12-08 01:41:12.000000000 +0000 ++++ ioemu/hw/pc.c 2006-12-08 01:41:12.000000000 +0000 @@ -39,7 +39,9 @@ static fdctrl_t *floppy_controller; static RTCState *rtc_state; diff --git a/tools/ioemu/patches/qemu-pci b/tools/ioemu/patches/qemu-pci index 6f052962c2..7cadbbe7ad 100755 --- a/tools/ioemu/patches/qemu-pci +++ b/tools/ioemu/patches/qemu-pci @@ -1,7 +1,7 @@ Index: ioemu/hw/pci.c =================================================================== ---- ioemu.orig/hw/pci.c 2006-09-21 11:31:14.000000000 +0100 -+++ ioemu/hw/pci.c 2006-09-21 11:31:32.000000000 +0100 +--- ioemu.orig/hw/pci.c 2006-12-08 02:02:05.000000000 +0000 ++++ ioemu/hw/pci.c 2006-12-08 18:16:55.000000000 +0000 @@ -286,6 +286,7 @@ case 0x0b: case 0x0e: @@ -31,8 +31,8 @@ Index: ioemu/hw/pci.c addr++; Index: ioemu/hw/rtl8139.c =================================================================== ---- ioemu.orig/hw/rtl8139.c 2006-09-21 11:31:14.000000000 +0100 -+++ ioemu/hw/rtl8139.c 2006-09-21 11:31:32.000000000 +0100 +--- ioemu.orig/hw/rtl8139.c 2006-12-08 02:02:05.000000000 +0000 ++++ ioemu/hw/rtl8139.c 2006-12-08 18:16:47.000000000 +0000 @@ -3423,6 +3423,8 @@ pci_conf[0x0e] = 0x00; /* header_type */ pci_conf[0x3d] = 1; /* interrupt pin 0 */ @@ -44,8 +44,8 @@ Index: ioemu/hw/rtl8139.c Index: ioemu/hw/usb-uhci.c =================================================================== ---- ioemu.orig/hw/usb-uhci.c 2006-09-21 11:31:14.000000000 +0100 -+++ ioemu/hw/usb-uhci.c 2006-09-21 11:31:32.000000000 +0100 +--- ioemu.orig/hw/usb-uhci.c 2006-12-08 02:02:05.000000000 +0000 ++++ ioemu/hw/usb-uhci.c 2006-12-08 02:02:38.000000000 +0000 @@ -659,6 +659,8 @@ pci_conf[0x0e] = 0x00; // header_type pci_conf[0x3d] = 4; // interrupt pin 3 @@ -55,3 +55,21 @@ Index: ioemu/hw/usb-uhci.c for(i = 0; i < NB_PORTS; i++) { qemu_register_usb_port(&s->ports[i].port, s, i, uhci_attach); +Index: ioemu/vl.h +=================================================================== +--- ioemu.orig/vl.h 2006-12-08 18:16:47.000000000 +0000 ++++ ioemu/vl.h 2006-12-08 18:16:55.000000000 +0000 +@@ -650,8 +650,11 @@ + #define PCI_MAX_LAT 0x3f /* 8 bits */ + + struct PCIDevice { +- /* PCI config space */ +- uint8_t config[256]; ++ /* ++ * PCI config space. The 4 extra bytes are a safety buffer for guest ++ * word/dword writes that can extend past byte 0xff. ++ */ ++ uint8_t config[256+4]; + + /* the following fields are read only */ + PCIBus *bus; diff --git a/tools/ioemu/patches/qemu-serial-fixes b/tools/ioemu/patches/qemu-serial-fixes new file mode 100644 index 0000000000..1afc7a45e8 --- /dev/null +++ b/tools/ioemu/patches/qemu-serial-fixes @@ -0,0 +1,133 @@ +# HG changeset patch +# User kfraser@localhost.localdomain +# Node ID c33272c2571c7bab7056d8228490700d1df405f9 +# Parent b3d94f4ddffefed8a5cb8dd65a60da9491d460e7 +[HVM] Fix Qemu-dm serial issues: + 1. Retry transmit via a polling timer if a byte cannot be written + immediately to its destination. + 2. Turn off output processing of raw serial lines. + +Signed-off-by: Xiaowei Yang +Signed-off-by: Yunhong Jiang +Signed-off-by: Keir Fraser + +Index: ioemu/vl.c +=================================================================== +--- ioemu.orig/vl.c 2006-12-08 01:28:59.000000000 +0000 ++++ ioemu/vl.c 2006-12-08 01:28:59.000000000 +0000 +@@ -1684,7 +1684,7 @@ + + tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP + |INLCR|IGNCR|ICRNL|IXON); +- tty.c_oflag |= OPOST; ++ tty.c_oflag &= ~OPOST; /* no output mangling of raw serial stream */ + tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN|ISIG); + tty.c_cflag &= ~(CSIZE|PARENB|PARODD|CRTSCTS); + switch(data_bits) { +Index: ioemu/hw/serial.c +=================================================================== +--- ioemu.orig/hw/serial.c 2006-12-08 01:28:17.000000000 +0000 ++++ ioemu/hw/serial.c 2006-12-08 01:29:10.000000000 +0000 +@@ -73,6 +73,11 @@ + #define UART_LSR_OE 0x02 /* Overrun error indicator */ + #define UART_LSR_DR 0x01 /* Receiver data ready */ + ++/* Maximum retries for a single byte transmit. */ ++#define WRITE_MAX_SINGLE_RETRIES 3 ++/* Maximum retries for a sequence of back-to-back unsuccessful transmits. */ ++#define WRITE_MAX_TOTAL_RETRIES 10 ++ + struct SerialState { + uint8_t divider; + uint8_t rbr; /* receive register */ +@@ -93,6 +98,19 @@ + int last_break_enable; + target_ulong base; + int it_shift; ++ ++ /* ++ * If a character transmitted via UART cannot be written to its ++ * destination immediately we remember it here and retry a few times via ++ * a polling timer. ++ * - write_single_retries: Number of write retries for current byte. ++ * - write_total_retries: Number of write retries for back-to-back ++ * unsuccessful transmits. ++ */ ++ int write_single_retries; ++ int write_total_retries; ++ char write_chr; ++ QEMUTimer *write_retry_timer; + }; + + static void serial_update_irq(SerialState *s) +@@ -204,10 +222,37 @@ + tokens_avail--; + } + ++static void serial_chr_write(void *opaque) ++{ ++ SerialState *s = opaque; ++ ++ /* Cancel any outstanding retry if this is a new byte. */ ++ qemu_del_timer(s->write_retry_timer); ++ ++ /* Retry every 100ms for 300ms total. */ ++ if (qemu_chr_write(s->chr, &s->write_chr, 1) == -1) { ++ s->write_total_retries++; ++ if (s->write_single_retries++ >= WRITE_MAX_SINGLE_RETRIES) ++ fprintf(stderr, "serial: write error\n"); ++ else if (s->write_total_retries <= WRITE_MAX_TOTAL_RETRIES) { ++ qemu_mod_timer(s->write_retry_timer, ++ qemu_get_clock(vm_clock) + ticks_per_sec / 10); ++ return; ++ } ++ } else { ++ s->write_total_retries = 0; /* if successful then reset counter */ ++ } ++ ++ /* Success: Notify guest that THR is empty. */ ++ s->thr_ipending = 1; ++ s->lsr |= UART_LSR_THRE; ++ s->lsr |= UART_LSR_TEMT; ++ serial_update_irq(s); ++} ++ + static void serial_ioport_write(void *opaque, uint32_t addr, uint32_t val) + { + SerialState *s = opaque; +- unsigned char ch; + + addr &= 7; + #ifdef DEBUG_SERIAL +@@ -223,12 +268,9 @@ + s->thr_ipending = 0; + s->lsr &= ~UART_LSR_THRE; + serial_update_irq(s); +- ch = val; +- qemu_chr_write(s->chr, &ch, 1); +- s->thr_ipending = 1; +- s->lsr |= UART_LSR_THRE; +- s->lsr |= UART_LSR_TEMT; +- serial_update_irq(s); ++ s->write_chr = val; ++ s->write_single_retries = 0; ++ serial_chr_write(s); + } + break; + case 1: +@@ -424,6 +466,7 @@ + s->lsr = UART_LSR_TEMT | UART_LSR_THRE; + s->iir = UART_IIR_NO_INT; + s->msr = UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS; ++ s->write_retry_timer = qemu_new_timer(vm_clock, serial_chr_write, s); + + register_savevm("serial", base, 1, serial_save, serial_load, s); + +@@ -511,6 +554,7 @@ + s->msr = UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS; + s->base = base; + s->it_shift = it_shift; ++ s->write_retry_timer = qemu_new_timer(vm_clock, serial_chr_write, s); + + register_savevm("serial", base, 1, serial_save, serial_load, s); + diff --git a/tools/ioemu/patches/qemu-target-i386-dm b/tools/ioemu/patches/qemu-target-i386-dm index 8bf4b3ea01..7bb2f9a905 100644 --- a/tools/ioemu/patches/qemu-target-i386-dm +++ b/tools/ioemu/patches/qemu-target-i386-dm @@ -1,7 +1,7 @@ Index: ioemu/Makefile.target =================================================================== ---- ioemu.orig/Makefile.target 2006-10-24 13:47:23.000000000 +0100 -+++ ioemu/Makefile.target 2006-10-24 14:30:56.000000000 +0100 +--- ioemu.orig/Makefile.target 2006-12-08 01:41:10.000000000 +0000 ++++ ioemu/Makefile.target 2006-12-08 01:41:11.000000000 +0000 @@ -62,6 +62,8 @@ QEMU_SYSTEM=qemu-fast endif @@ -11,7 +11,7 @@ Index: ioemu/Makefile.target ifdef CONFIG_USER_ONLY PROGS=$(QEMU_USER) else -@@ -291,6 +293,9 @@ +@@ -292,6 +294,9 @@ OBJS+=gdbstub.o endif @@ -21,7 +21,7 @@ Index: ioemu/Makefile.target all: $(PROGS) $(QEMU_USER): $(OBJS) -@@ -349,7 +354,7 @@ +@@ -350,7 +355,7 @@ ifeq ($(TARGET_BASE_ARCH), i386) # Hardware support VL_OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) @@ -32,8 +32,8 @@ Index: ioemu/Makefile.target DEFINES += -DHAS_AUDIO Index: ioemu/configure =================================================================== ---- ioemu.orig/configure 2006-10-24 13:47:23.000000000 +0100 -+++ ioemu/configure 2006-10-24 14:29:34.000000000 +0100 +--- ioemu.orig/configure 2006-12-08 01:41:10.000000000 +0000 ++++ ioemu/configure 2006-12-08 01:41:11.000000000 +0000 @@ -373,6 +373,8 @@ if [ "$user" = "yes" ] ; then target_list="i386-user arm-user armeb-user sparc-user ppc-user mips-user mipsel-user $target_list" @@ -45,8 +45,8 @@ Index: ioemu/configure fi Index: ioemu/monitor.c =================================================================== ---- ioemu.orig/monitor.c 2006-10-24 13:47:23.000000000 +0100 -+++ ioemu/monitor.c 2006-10-24 14:30:56.000000000 +0100 +--- ioemu.orig/monitor.c 2006-12-08 01:41:10.000000000 +0000 ++++ ioemu/monitor.c 2006-12-08 01:41:11.000000000 +0000 @@ -1262,6 +1262,10 @@ "", "show profiling information", }, { "capture", "", do_info_capture, @@ -60,8 +60,8 @@ Index: ioemu/monitor.c Index: ioemu/vl.c =================================================================== ---- ioemu.orig/vl.c 2006-10-24 13:47:23.000000000 +0100 -+++ ioemu/vl.c 2006-10-24 14:30:56.000000000 +0100 +--- ioemu.orig/vl.c 2006-12-08 01:41:10.000000000 +0000 ++++ ioemu/vl.c 2006-12-08 01:41:11.000000000 +0000 @@ -87,7 +87,7 @@ #include "exec-all.h" @@ -98,8 +98,8 @@ Index: ioemu/vl.c { Index: ioemu/vl.h =================================================================== ---- ioemu.orig/vl.h 2006-10-24 13:47:23.000000000 +0100 -+++ ioemu/vl.h 2006-10-24 14:30:56.000000000 +0100 +--- ioemu.orig/vl.h 2006-12-08 01:40:58.000000000 +0000 ++++ ioemu/vl.h 2006-12-08 01:41:11.000000000 +0000 @@ -37,6 +37,8 @@ #include #include @@ -132,8 +132,8 @@ Index: ioemu/vl.h Index: ioemu/target-i386-dm/cpu.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ ioemu/target-i386-dm/cpu.h 2006-10-24 14:30:56.000000000 +0100 -@@ -0,0 +1,86 @@ ++++ ioemu/target-i386-dm/cpu.h 2006-12-08 01:41:11.000000000 +0000 +@@ -0,0 +1,84 @@ +/* + * i386 virtual CPU header + * @@ -191,8 +191,6 @@ Index: ioemu/target-i386-dm/cpu.h + int interrupt_request; + + CPU_COMMON -+ -+ int send_event; +} CPUX86State; + +CPUX86State *cpu_x86_init(void); @@ -223,8 +221,8 @@ Index: ioemu/target-i386-dm/cpu.h Index: ioemu/target-i386-dm/exec-dm.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ ioemu/target-i386-dm/exec-dm.c 2006-10-24 14:30:56.000000000 +0100 -@@ -0,0 +1,516 @@ ++++ ioemu/target-i386-dm/exec-dm.c 2006-12-08 01:41:11.000000000 +0000 +@@ -0,0 +1,546 @@ +/* + * virtual page mapping and translated block handling + * @@ -259,6 +257,8 @@ Index: ioemu/target-i386-dm/exec-dm.c +#include +#include + ++#include ++ +#include "cpu.h" +#include "exec-all.h" + @@ -568,6 +568,23 @@ Index: ioemu/target-i386-dm/exec-dm.c + return io_mem_read[io_index >> IO_MEM_SHIFT]; +} + ++#ifdef __ia64__ ++/* IA64 has seperate I/D cache, with coherence maintained by DMA controller. ++ * So to emulate right behavior that guest OS is assumed, we need to flush ++ * I/D cache here. ++ */ ++static void sync_icache(unsigned long address, int len) ++{ ++ int l; ++ ++ for(l = 0; l < (len + 32); l += 32) ++ __ia64_fc(address + l); ++ ++ ia64_sync_i(); ++ ia64_srlz_i(); ++} ++#endif ++ +/* physical memory access (slow version, mainly for debug) */ +#if defined(CONFIG_USER_ONLY) +void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, @@ -617,22 +634,36 @@ Index: ioemu/target-i386-dm/exec-dm.c + return 0; +} + ++static inline int paddr_is_ram(target_phys_addr_t addr) ++{ ++ /* Is this guest physical address RAM-backed? */ ++#if defined(CONFIG_DM) && (defined(__i386__) || defined(__x86_64__)) ++ if (ram_size <= HVM_BELOW_4G_RAM_END) ++ /* RAM is contiguous */ ++ return (addr < ram_size); ++ else ++ /* There is RAM below and above the MMIO hole */ ++ return ((addr < HVM_BELOW_4G_MMIO_START) || ++ ((addr >= HVM_BELOW_4G_MMIO_START + HVM_BELOW_4G_MMIO_LENGTH) ++ && (addr < ram_size + HVM_BELOW_4G_MMIO_LENGTH))); ++#else ++ return (addr < ram_size); ++#endif ++} ++ +void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, + int len, int is_write) +{ + int l, io_index; + uint8_t *ptr; + uint32_t val; -+ target_phys_addr_t page; -+ unsigned long pd; + + while (len > 0) { -+ page = addr & TARGET_PAGE_MASK; -+ l = (page + TARGET_PAGE_SIZE) - addr; ++ /* How much can we copy before the next page boundary? */ ++ l = TARGET_PAGE_SIZE - (addr & ~TARGET_PAGE_MASK); + if (l > len) + l = len; + -+ pd = page; + io_index = iomem_index(addr); + if (is_write) { + if (io_index) { @@ -652,13 +683,12 @@ Index: ioemu/target-i386-dm/exec-dm.c + io_mem_write[io_index][0](io_mem_opaque[io_index], addr, val); + l = 1; + } -+ } else { -+ unsigned long addr1; -+ -+ addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); -+ /* RAM case */ -+ ptr = phys_ram_base + addr1; -+ memcpy(ptr, buf, l); ++ } else if (paddr_is_ram(addr)) { ++ /* Reading from RAM */ ++ memcpy(phys_ram_base + addr, buf, l); ++#ifdef __ia64__ ++ sync_icache((unsigned long)(phys_ram_base + addr), l); ++#endif + } + } else { + if (io_index) { @@ -678,14 +708,12 @@ Index: ioemu/target-i386-dm/exec-dm.c + stb_raw(buf, val); + l = 1; + } -+ } else if (addr < ram_size) { -+ /* RAM case */ -+ ptr = phys_ram_base + (pd & TARGET_PAGE_MASK) + -+ (addr & ~TARGET_PAGE_MASK); -+ memcpy(buf, ptr, l); ++ } else if (paddr_is_ram(addr)) { ++ /* Reading from RAM */ ++ memcpy(buf, phys_ram_base + addr, l); + } else { -+ /* unreported MMIO space */ -+ memset(buf, 0xff, len); ++ /* Neither RAM nor known MMIO space */ ++ memset(buf, 0xff, len); + } + } + len -= l; @@ -744,8 +772,8 @@ Index: ioemu/target-i386-dm/exec-dm.c Index: ioemu/target-i386-dm/helper2.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ ioemu/target-i386-dm/helper2.c 2006-10-24 14:31:01.000000000 +0100 -@@ -0,0 +1,469 @@ ++++ ioemu/target-i386-dm/helper2.c 2006-12-08 01:41:11.000000000 +0000 +@@ -0,0 +1,488 @@ +/* + * i386 helpers (without register variable usage) + * @@ -918,10 +946,10 @@ Index: ioemu/target-i386-dm/helper2.c + for (i = 0; i < vcpus; i++) { + req = &(shared_page->vcpu_iodata[i].vp_ioreq); + term_printf("vcpu %d: event port %d\n", i, ioreq_local_port[i]); -+ term_printf(" req state: %x, pvalid: %x, addr: %"PRIx64", " ++ term_printf(" req state: %x, ptr: %x, addr: %"PRIx64", " + "data: %"PRIx64", count: %"PRIx64", size: %"PRIx64"\n", -+ req->state, req->pdata_valid, req->addr, -+ req->u.data, req->count, req->size); ++ req->state, req->data_is_ptr, req->addr, ++ req->data, req->count, req->size); + term_printf(" IO totally occurred on this vcpu: %"PRIx64"\n", + req->io_count); + } @@ -934,18 +962,19 @@ Index: ioemu/target-i386-dm/helper2.c + + req = &(shared_page->vcpu_iodata[vcpu].vp_ioreq); + -+ if (req->state == STATE_IOREQ_READY) { -+ req->state = STATE_IOREQ_INPROCESS; -+ rmb(); -+ return req; ++ if (req->state != STATE_IOREQ_READY) { ++ fprintf(logfile, "I/O request not ready: " ++ "%x, ptr: %x, port: %"PRIx64", " ++ "data: %"PRIx64", count: %"PRIx64", size: %"PRIx64"\n", ++ req->state, req->data_is_ptr, req->addr, ++ req->data, req->count, req->size); ++ return NULL; + } + -+ fprintf(logfile, "False I/O request ... in-service already: " -+ "%x, pvalid: %x, port: %"PRIx64", " -+ "data: %"PRIx64", count: %"PRIx64", size: %"PRIx64"\n", -+ req->state, req->pdata_valid, req->addr, -+ req->u.data, req->count, req->size); -+ return NULL; ++ rmb(); /* see IOREQ_READY /then/ read contents of ioreq */ ++ ++ req->state = STATE_IOREQ_INPROCESS; ++ return req; +} + +//use poll to get the port notification @@ -1030,26 +1059,26 @@ Index: ioemu/target-i386-dm/helper2.c + sign = req->df ? -1 : 1; + + if (req->dir == IOREQ_READ) { -+ if (!req->pdata_valid) { -+ req->u.data = do_inp(env, req->addr, req->size); ++ if (!req->data_is_ptr) { ++ req->data = do_inp(env, req->addr, req->size); + } else { + unsigned long tmp; + + for (i = 0; i < req->count; i++) { + tmp = do_inp(env, req->addr, req->size); -+ write_physical((target_phys_addr_t) req->u.pdata ++ write_physical((target_phys_addr_t) req->data + + (sign * i * req->size), + req->size, &tmp); + } + } + } else if (req->dir == IOREQ_WRITE) { -+ if (!req->pdata_valid) { -+ do_outp(env, req->addr, req->size, req->u.data); ++ if (!req->data_is_ptr) { ++ do_outp(env, req->addr, req->size, req->data); + } else { + for (i = 0; i < req->count; i++) { + unsigned long tmp; + -+ read_physical((target_phys_addr_t) req->u.pdata ++ read_physical((target_phys_addr_t) req->data + + (sign * i * req->size), + req->size, &tmp); + do_outp(env, req->addr, req->size, tmp); @@ -1064,18 +1093,18 @@ Index: ioemu/target-i386-dm/helper2.c + + sign = req->df ? -1 : 1; + -+ if (!req->pdata_valid) { ++ if (!req->data_is_ptr) { + if (req->dir == IOREQ_READ) { + for (i = 0; i < req->count; i++) { + read_physical(req->addr + + (sign * i * req->size), -+ req->size, &req->u.data); ++ req->size, &req->data); + } + } else if (req->dir == IOREQ_WRITE) { + for (i = 0; i < req->count; i++) { + write_physical(req->addr + + (sign * i * req->size), -+ req->size, &req->u.data); ++ req->size, &req->data); + } + } + } else { @@ -1086,13 +1115,13 @@ Index: ioemu/target-i386-dm/helper2.c + read_physical(req->addr + + (sign * i * req->size), + req->size, &tmp); -+ write_physical((target_phys_addr_t )req->u.pdata ++ write_physical((target_phys_addr_t )req->data + + (sign * i * req->size), + req->size, &tmp); + } + } else if (req->dir == IOREQ_WRITE) { + for (i = 0; i < req->count; i++) { -+ read_physical((target_phys_addr_t) req->u.pdata ++ read_physical((target_phys_addr_t) req->data + + (sign * i * req->size), + req->size, &tmp); + write_physical(req->addr @@ -1107,45 +1136,60 @@ Index: ioemu/target-i386-dm/helper2.c +{ + unsigned long tmp1, tmp2; + -+ if (req->pdata_valid != 0) ++ if (req->data_is_ptr != 0) + hw_error("expected scalar value"); + + read_physical(req->addr, req->size, &tmp1); + if (req->dir == IOREQ_WRITE) { -+ tmp2 = tmp1 & (unsigned long) req->u.data; ++ tmp2 = tmp1 & (unsigned long) req->data; + write_physical(req->addr, req->size, &tmp2); + } -+ req->u.data = tmp1; ++ req->data = tmp1; ++} ++ ++void cpu_ioreq_add(CPUState *env, ioreq_t *req) ++{ ++ unsigned long tmp1, tmp2; ++ ++ if (req->data_is_ptr != 0) ++ hw_error("expected scalar value"); ++ ++ read_physical(req->addr, req->size, &tmp1); ++ if (req->dir == IOREQ_WRITE) { ++ tmp2 = tmp1 + (unsigned long) req->data; ++ write_physical(req->addr, req->size, &tmp2); ++ } ++ req->data = tmp1; +} + +void cpu_ioreq_or(CPUState *env, ioreq_t *req) +{ + unsigned long tmp1, tmp2; + -+ if (req->pdata_valid != 0) ++ if (req->data_is_ptr != 0) + hw_error("expected scalar value"); + + read_physical(req->addr, req->size, &tmp1); + if (req->dir == IOREQ_WRITE) { -+ tmp2 = tmp1 | (unsigned long) req->u.data; ++ tmp2 = tmp1 | (unsigned long) req->data; + write_physical(req->addr, req->size, &tmp2); + } -+ req->u.data = tmp1; ++ req->data = tmp1; +} + +void cpu_ioreq_xor(CPUState *env, ioreq_t *req) +{ + unsigned long tmp1, tmp2; + -+ if (req->pdata_valid != 0) ++ if (req->data_is_ptr != 0) + hw_error("expected scalar value"); + + read_physical(req->addr, req->size, &tmp1); + if (req->dir == IOREQ_WRITE) { -+ tmp2 = tmp1 ^ (unsigned long) req->u.data; ++ tmp2 = tmp1 ^ (unsigned long) req->data; + write_physical(req->addr, req->size, &tmp2); + } -+ req->u.data = tmp1; ++ req->data = tmp1; +} + +void cpu_handle_ioreq(void *opaque) @@ -1154,9 +1198,9 @@ Index: ioemu/target-i386-dm/helper2.c + ioreq_t *req = cpu_get_ioreq(); + + if (req) { -+ if ((!req->pdata_valid) && (req->dir == IOREQ_WRITE)) { ++ if ((!req->data_is_ptr) && (req->dir == IOREQ_WRITE)) { + if (req->size != 4) -+ req->u.data &= (1UL << (8 * req->size))-1; ++ req->data &= (1UL << (8 * req->size))-1; + } + + switch (req->type) { @@ -1169,6 +1213,9 @@ Index: ioemu/target-i386-dm/helper2.c + case IOREQ_TYPE_AND: + cpu_ioreq_and(env, req); + break; ++ case IOREQ_TYPE_ADD: ++ cpu_ioreq_add(env, req); ++ break; + case IOREQ_TYPE_OR: + cpu_ioreq_or(env, req); + break; @@ -1179,12 +1226,19 @@ Index: ioemu/target-i386-dm/helper2.c + hw_error("Invalid ioreq type 0x%x\n", req->type); + } + -+ /* No state change if state = STATE_IORESP_HOOK */ -+ if (req->state == STATE_IOREQ_INPROCESS) { -+ mb(); -+ req->state = STATE_IORESP_READY; ++ if (req->state != STATE_IOREQ_INPROCESS) { ++ fprintf(logfile, "Badness in I/O request ... not in service?!: " ++ "%x, ptr: %x, port: %"PRIx64", " ++ "data: %"PRIx64", count: %"PRIx64", size: %"PRIx64"\n", ++ req->state, req->data_is_ptr, req->addr, ++ req->data, req->count, req->size); ++ destroy_hvm_domain(); ++ return; + } -+ env->send_event = 1; ++ ++ wmb(); /* Update ioreq contents /then/ update state. */ ++ req->state = STATE_IORESP_READY; ++ xc_evtchn_notify(xce_handle, ioreq_local_port[send_vcpu]); + } +} + @@ -1197,8 +1251,6 @@ Index: ioemu/target-i386-dm/helper2.c + + qemu_set_fd_handler(evtchn_fd, cpu_handle_ioreq, NULL, env); + -+ env->send_event = 0; -+ + while (1) { + if (vm_running) { + if (shutdown_requested) @@ -1207,19 +1259,14 @@ Index: ioemu/target-i386-dm/helper2.c + + /* Wait up to 10 msec. */ + main_loop_wait(10); -+ -+ if (env->send_event) { -+ env->send_event = 0; -+ xc_evtchn_notify(xce_handle, ioreq_local_port[send_vcpu]); -+ } + } + return 0; +} Index: ioemu/target-i386-dm/i8259-dm.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ ioemu/target-i386-dm/i8259-dm.c 2006-10-24 13:47:23.000000000 +0100 -@@ -0,0 +1,107 @@ ++++ ioemu/target-i386-dm/i8259-dm.c 2006-12-08 01:41:11.000000000 +0000 +@@ -0,0 +1,67 @@ +/* Xen 8259 stub for interrupt controller emulation + * + * Copyright (c) 2003-2004 Fabrice Bellard @@ -1244,58 +1291,18 @@ Index: ioemu/target-i386-dm/i8259-dm.c + * THE SOFTWARE. + */ +#include "vl.h" -+ -+/* debug PIC */ -+//#define DEBUG_PIC -+ -+//#define DEBUG_IRQ_LATENCY -+//#define DEBUG_IRQ_COUNT -+ +#include "xenctrl.h" +#include +#include +#include "cpu.h" +#include "cpu-all.h" + -+extern shared_iopage_t *shared_page; -+ +struct PicState2 { +}; + +void pic_set_irq_new(void *opaque, int irq, int level) +{ -+ /* PicState2 *s = opaque; */ -+ global_iodata_t *gio; -+ int mask; -+ -+ gio = &shared_page->sp_global; -+ mask = 1 << irq; -+ if ( gio->pic_elcr & mask ) { -+ /* level */ -+ if ( level ) { -+ atomic_clear_bit(irq, &gio->pic_clear_irr); -+ atomic_set_bit(irq, &gio->pic_irr); -+ cpu_single_env->send_event = 1; -+ } -+ else { -+ atomic_clear_bit(irq, &gio->pic_irr); -+ atomic_set_bit(irq, &gio->pic_clear_irr); -+ cpu_single_env->send_event = 1; -+ } -+ } -+ else { -+ /* edge */ -+ if ( level ) { -+ if ( (mask & gio->pic_last_irr) == 0 ) { -+ atomic_set_bit(irq, &gio->pic_irr); -+ atomic_set_bit(irq, &gio->pic_last_irr); -+ cpu_single_env->send_event = 1; -+ } -+ } -+ else { -+ atomic_clear_bit(irq, &gio->pic_last_irr); -+ } -+ } ++ xc_hvm_set_irq_level(xc_handle, domid, irq, level); +} + +/* obsolete function */ @@ -1330,17 +1337,22 @@ Index: ioemu/target-i386-dm/i8259-dm.c Index: ioemu/target-i386-dm/qemu-dm.debug =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ ioemu/target-i386-dm/qemu-dm.debug 2006-10-24 13:47:23.000000000 +0100 -@@ -0,0 +1,5 @@ ++++ ioemu/target-i386-dm/qemu-dm.debug 2006-12-08 01:41:11.000000000 +0000 +@@ -0,0 +1,10 @@ +#!/bin/sh + ++if [ "`arch`" = "x86_64" ]; then ++ LIBDIR="lib64" ++else ++ LIBDIR="lib" ++fi +echo $* > /tmp/args +echo $DISPLAY >> /tmp/args -+exec /usr/lib/xen/bin/qemu-dm $* ++exec /usr/$LIBDIR/xen/bin/qemu-dm $* Index: ioemu/target-i386-dm/qemu-ifup =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ ioemu/target-i386-dm/qemu-ifup 2006-10-24 13:47:23.000000000 +0100 ++++ ioemu/target-i386-dm/qemu-ifup 2006-12-08 01:41:11.000000000 +0000 @@ -0,0 +1,10 @@ +#!/bin/sh + diff --git a/tools/ioemu/patches/remove-pci-bridge-setup b/tools/ioemu/patches/remove-pci-bridge-setup new file mode 100644 index 0000000000..a4f4c80b80 --- /dev/null +++ b/tools/ioemu/patches/remove-pci-bridge-setup @@ -0,0 +1,289 @@ +# HG changeset patch +# User kfraser@localhost.localdomain +# Node ID a8d31d5ce2589762c3226185deeca3afca47a698 +# Parent b8cc9ffda0a3dc449b026c72c97f78dea2e6f114 +[HVM] Move PCI and PCI-ISA bridge setup to hvmloader. +Signed-off-by: Keir Fraser + +Index: ioemu/target-i386-dm/piix_pci-dm.c +=================================================================== +--- ioemu.orig/target-i386-dm/piix_pci-dm.c 2006-12-08 18:22:35.000000000 +0000 ++++ ioemu/target-i386-dm/piix_pci-dm.c 2006-12-08 18:22:50.000000000 +0000 +@@ -84,12 +84,6 @@ + + static PCIDevice *piix3_dev; + +-static int pci_slot_get_pirq(PCIDevice *pci_dev, int irq_num) +-{ +- /* This is the barber's pole mapping used by Xen. */ +- return (irq_num + (pci_dev->devfn >> 3)) & 3; +-} +- + static void piix3_write_config(PCIDevice *d, + uint32_t address, uint32_t val, int len) + { +@@ -114,12 +108,9 @@ + uint8_t *pci_conf = d->config; + + pci_conf[0x04] = 0x07; // master, memory and I/O +- pci_conf[0x05] = 0x00; +- pci_conf[0x06] = 0x00; + pci_conf[0x07] = 0x02; // PCI_status_devsel_medium + pci_conf[0x4c] = 0x4d; + pci_conf[0x4e] = 0x03; +- pci_conf[0x4f] = 0x00; + pci_conf[0x60] = 0x80; + pci_conf[0x61] = 0x80; + pci_conf[0x62] = 0x80; +@@ -129,22 +120,9 @@ + pci_conf[0x76] = 0x0c; + pci_conf[0x77] = 0x0c; + pci_conf[0x78] = 0x02; +- pci_conf[0x79] = 0x00; +- pci_conf[0x80] = 0x00; +- pci_conf[0x82] = 0x00; + pci_conf[0xa0] = 0x08; + pci_conf[0xa0] = 0x08; +- pci_conf[0xa2] = 0x00; +- pci_conf[0xa3] = 0x00; +- pci_conf[0xa4] = 0x00; +- pci_conf[0xa5] = 0x00; +- pci_conf[0xa6] = 0x00; +- pci_conf[0xa7] = 0x00; + pci_conf[0xa8] = 0x0f; +- pci_conf[0xaa] = 0x00; +- pci_conf[0xab] = 0x00; +- pci_conf[0xac] = 0x00; +- pci_conf[0xae] = 0x00; + } + + int piix3_init(PCIBus *bus) +@@ -171,227 +149,4 @@ + return d->devfn; + } + +-/***********************************************************/ +-/* XXX: the following should be moved to the PC BIOS */ +- +-static __attribute__((unused)) uint32_t isa_inb(uint32_t addr) +-{ +- return cpu_inb(NULL, addr); +-} +- +-static void isa_outb(uint32_t val, uint32_t addr) +-{ +- cpu_outb(NULL, addr, val); +-} +- +-static __attribute__((unused)) uint32_t isa_inw(uint32_t addr) +-{ +- return cpu_inw(NULL, addr); +-} +- +-static __attribute__((unused)) void isa_outw(uint32_t val, uint32_t addr) +-{ +- cpu_outw(NULL, addr, val); +-} +- +-static __attribute__((unused)) uint32_t isa_inl(uint32_t addr) +-{ +- return cpu_inl(NULL, addr); +-} +- +-static __attribute__((unused)) void isa_outl(uint32_t val, uint32_t addr) +-{ +- cpu_outl(NULL, addr, val); +-} +- +-static uint32_t pci_bios_io_addr; +-static uint32_t pci_bios_mem_addr; +-/* host irqs corresponding to PCI irqs A-D */ +-static uint8_t pci_irqs[4] = { 10, 11, 10, 11 }; +- +-static void pci_config_writel(PCIDevice *d, uint32_t addr, uint32_t val) +-{ +- PCIBus *s = d->bus; +- addr |= (pci_bus_num(s) << 16) | (d->devfn << 8); +- pci_data_write(s, addr, val, 4); +-} +- +-static void pci_config_writew(PCIDevice *d, uint32_t addr, uint32_t val) +-{ +- PCIBus *s = d->bus; +- addr |= (pci_bus_num(s) << 16) | (d->devfn << 8); +- pci_data_write(s, addr, val, 2); +-} +- +-static void pci_config_writeb(PCIDevice *d, uint32_t addr, uint32_t val) +-{ +- PCIBus *s = d->bus; +- addr |= (pci_bus_num(s) << 16) | (d->devfn << 8); +- pci_data_write(s, addr, val, 1); +-} +- +-static __attribute__((unused)) uint32_t pci_config_readl(PCIDevice *d, uint32_t addr) +-{ +- PCIBus *s = d->bus; +- addr |= (pci_bus_num(s) << 16) | (d->devfn << 8); +- return pci_data_read(s, addr, 4); +-} +- +-static uint32_t pci_config_readw(PCIDevice *d, uint32_t addr) +-{ +- PCIBus *s = d->bus; +- addr |= (pci_bus_num(s) << 16) | (d->devfn << 8); +- return pci_data_read(s, addr, 2); +-} +- +-static uint32_t pci_config_readb(PCIDevice *d, uint32_t addr) +-{ +- PCIBus *s = d->bus; +- addr |= (pci_bus_num(s) << 16) | (d->devfn << 8); +- return pci_data_read(s, addr, 1); +-} +- +-static void pci_set_io_region_addr(PCIDevice *d, int region_num, uint32_t addr) +-{ +- PCIIORegion *r; +- uint16_t cmd; +- uint32_t ofs; +- +- if ( region_num == PCI_ROM_SLOT ) { +- ofs = 0x30; +- }else{ +- ofs = 0x10 + region_num * 4; +- } +- +- pci_config_writel(d, ofs, addr); +- r = &d->io_regions[region_num]; +- +- /* enable memory mappings */ +- cmd = pci_config_readw(d, PCI_COMMAND); +- if ( region_num == PCI_ROM_SLOT ) +- cmd |= 2; +- else if (r->type & PCI_ADDRESS_SPACE_IO) +- cmd |= 1; +- else +- cmd |= 2; +- pci_config_writew(d, PCI_COMMAND, cmd); +-} +- +-static void pci_bios_init_device(PCIDevice *d) +-{ +- int class; +- PCIIORegion *r; +- uint32_t *paddr; +- int i, pin, pic_irq, vendor_id, device_id; +- +- class = pci_config_readw(d, PCI_CLASS_DEVICE); +- vendor_id = pci_config_readw(d, PCI_VENDOR_ID); +- device_id = pci_config_readw(d, PCI_DEVICE_ID); +- switch(class) { +- case 0x0101: +- if (vendor_id == 0x8086 && device_id == 0x7010) { +- /* PIIX3 IDE */ +- pci_config_writew(d, 0x40, 0x8000); // enable IDE0 +- pci_config_writew(d, 0x42, 0x8000); // enable IDE1 +- goto default_map; +- } else { +- /* IDE: we map it as in ISA mode */ +- pci_set_io_region_addr(d, 0, 0x1f0); +- pci_set_io_region_addr(d, 1, 0x3f4); +- pci_set_io_region_addr(d, 2, 0x170); +- pci_set_io_region_addr(d, 3, 0x374); +- } +- break; +- case 0x0680: +- if (vendor_id == 0x8086 && device_id == 0x7113) { +- /* +- * PIIX4 ACPI PM. +- * Special device with special PCI config space. No ordinary BARs. +- */ +- pci_config_writew(d, 0x20, 0x0000); // No smb bus IO enable +- pci_config_writew(d, 0x22, 0x0000); +- pci_config_writew(d, 0x3c, 0x0009); // Hardcoded IRQ9 +- pci_config_writew(d, 0x3d, 0x0001); +- } +- break; +- case 0x0300: +- if (vendor_id != 0x1234) +- goto default_map; +- /* VGA: map frame buffer to default Bochs VBE address */ +- pci_set_io_region_addr(d, 0, 0xE0000000); +- break; +- case 0x0800: +- /* PIC */ +- vendor_id = pci_config_readw(d, PCI_VENDOR_ID); +- device_id = pci_config_readw(d, PCI_DEVICE_ID); +- if (vendor_id == 0x1014) { +- /* IBM */ +- if (device_id == 0x0046 || device_id == 0xFFFF) { +- /* MPIC & MPIC2 */ +- pci_set_io_region_addr(d, 0, 0x80800000 + 0x00040000); +- } +- } +- break; +- case 0xff00: +- if (vendor_id == 0x0106b && +- (device_id == 0x0017 || device_id == 0x0022)) { +- /* macio bridge */ +- pci_set_io_region_addr(d, 0, 0x80800000); +- } +- break; +- default: +- default_map: +- /* default memory mappings */ +- for(i = 0; i < PCI_NUM_REGIONS; i++) { +- r = &d->io_regions[i]; +- if (r->size) { +- if (r->type & PCI_ADDRESS_SPACE_IO) +- paddr = &pci_bios_io_addr; +- else +- paddr = &pci_bios_mem_addr; +- *paddr = (*paddr + r->size - 1) & ~(r->size - 1); +- pci_set_io_region_addr(d, i, *paddr); +- *paddr += r->size; +- } +- } +- break; +- } +- +- /* map the interrupt */ +- pin = pci_config_readb(d, PCI_INTERRUPT_PIN); +- if (pin != 0) { +- pin = pci_slot_get_pirq(d, pin - 1); +- pic_irq = pci_irqs[pin]; +- pci_config_writeb(d, PCI_INTERRUPT_LINE, pic_irq); +- } +-} +- +-/* +- * This function initializes the PCI devices as a normal PCI BIOS +- * would do. It is provided just in case the BIOS has no support for +- * PCI. +- */ +-void pci_bios_init(void) +-{ +- int i, irq; +- uint8_t elcr[2]; +- +- pci_bios_io_addr = 0xc000; +- pci_bios_mem_addr = HVM_BELOW_4G_MMIO_START; +- +- /* activate IRQ mappings */ +- elcr[0] = 0x00; +- elcr[1] = 0x00; +- for(i = 0; i < 4; i++) { +- irq = pci_irqs[i]; +- /* set to trigger level */ +- elcr[irq >> 3] |= (1 << (irq & 7)); +- /* activate irq remapping in PIIX */ +- pci_config_writeb(piix3_dev, 0x60 + i, irq); +- } +- isa_outb(elcr[0], 0x4d0); +- isa_outb(elcr[1], 0x4d1); +- +- pci_for_each_device(pci_bios_init_device); +-} +- ++void pci_bios_init(void) {} diff --git a/tools/ioemu/patches/rtl8139-bound-chaining b/tools/ioemu/patches/rtl8139-bound-chaining new file mode 100644 index 0000000000..a360ddbc49 --- /dev/null +++ b/tools/ioemu/patches/rtl8139-bound-chaining @@ -0,0 +1,36 @@ +# HG changeset patch +# User kfraser@localhost.localdomain +# Node ID 075f4ffdbbce5527ba525a515abe320703d17a0e +# Parent 51edd3c6a4d861db6ce1c9a02251ed49213c3002 +[QEMU] rtl8139: Disallow chaining above 64K + +As it stands the 8139C+ TX chaining is only bounded by realloc failure. +This is contrary to how the real hardware operates. It also has DoS +potential when ioemu runs in dom0. + +This patch makes any attempt to chain a frame beyond 64K fail +immediately. + +Signed-off-by: Herbert Xu + +Index: ioemu/hw/rtl8139.c +=================================================================== +--- ioemu.orig/hw/rtl8139.c 2006-12-08 18:21:36.000000000 +0000 ++++ ioemu/hw/rtl8139.c 2006-12-08 18:22:22.000000000 +0000 +@@ -1999,12 +1999,12 @@ + DEBUG_PRINT(("RTL8139: +++ C+ mode transmission buffer allocated space %d\n", s->cplus_txbuffer_len)); + } + +- while (s->cplus_txbuffer && s->cplus_txbuffer_offset + txsize >= s->cplus_txbuffer_len) ++ if (s->cplus_txbuffer && s->cplus_txbuffer_offset + txsize >= s->cplus_txbuffer_len) + { +- s->cplus_txbuffer_len += CP_TX_BUFFER_SIZE; +- s->cplus_txbuffer = realloc(s->cplus_txbuffer, s->cplus_txbuffer_len); ++ free(s->cplus_txbuffer); ++ s->cplus_txbuffer = NULL; + +- DEBUG_PRINT(("RTL8139: +++ C+ mode transmission buffer space changed to %d\n", s->cplus_txbuffer_len)); ++ DEBUG_PRINT(("RTL8139: +++ C+ mode transmission buffer space exceeded: %d\n", s->cplus_txbuffer_offset + txsize)); + } + + if (!s->cplus_txbuffer) diff --git a/tools/ioemu/patches/series b/tools/ioemu/patches/series index f6459c2139..6d6f1f8ee6 100644 --- a/tools/ioemu/patches/series +++ b/tools/ioemu/patches/series @@ -53,3 +53,19 @@ serial-port-rate-limit hypervisor-rtc ide-cd-dma vnc-password +ne2000-bounds-checks +xenstore-device-info-functions +tpm-tis-device +qemu-serial-fixes +vnc-japan-keymap +rtl8139-bound-chaining +fix-interrupt-routing +nodelay-serial-over-tcp +remove-pci-bridge-setup +limit-fdc-sector-size-to-16K +usb-uhci-buffer-size +vnc-monitor-shift-key-processing +ide-error-reporting +vnc-numpad-handling +xen-mapcache -p3 +usb-mouse-tablet-status-check -p3 diff --git a/tools/ioemu/patches/shared-vram b/tools/ioemu/patches/shared-vram index a3aa12aa5d..aa67a1ae8c 100644 --- a/tools/ioemu/patches/shared-vram +++ b/tools/ioemu/patches/shared-vram @@ -1,7 +1,7 @@ Index: ioemu/hw/cirrus_vga.c =================================================================== ---- ioemu.orig/hw/cirrus_vga.c 2006-08-17 19:37:36.372522620 +0100 -+++ ioemu/hw/cirrus_vga.c 2006-08-17 19:49:52.157002909 +0100 +--- ioemu.orig/hw/cirrus_vga.c 2006-12-08 01:57:54.000000000 +0000 ++++ ioemu/hw/cirrus_vga.c 2006-12-08 02:00:04.000000000 +0000 @@ -28,6 +28,9 @@ */ #include "vl.h" @@ -176,8 +176,8 @@ Index: ioemu/hw/cirrus_vga.c } Index: ioemu/hw/pc.c =================================================================== ---- ioemu.orig/hw/pc.c 2006-08-17 19:49:50.229215988 +0100 -+++ ioemu/hw/pc.c 2006-08-17 19:49:52.158002799 +0100 +--- ioemu.orig/hw/pc.c 2006-12-08 02:00:04.000000000 +0000 ++++ ioemu/hw/pc.c 2006-12-08 02:00:04.000000000 +0000 @@ -790,14 +790,14 @@ if (cirrus_vga_enabled) { if (pci_enabled) { @@ -198,8 +198,8 @@ Index: ioemu/hw/pc.c Index: ioemu/hw/vga.c =================================================================== ---- ioemu.orig/hw/vga.c 2006-08-17 19:49:37.764593706 +0100 -+++ ioemu/hw/vga.c 2006-08-17 19:49:52.159002688 +0100 +--- ioemu.orig/hw/vga.c 2006-12-08 02:00:04.000000000 +0000 ++++ ioemu/hw/vga.c 2006-12-08 02:00:04.000000000 +0000 @@ -1858,6 +1858,7 @@ /* TODO: add vbe support if enabled */ } @@ -251,8 +251,8 @@ Index: ioemu/hw/vga.c Index: ioemu/hw/vga_int.h =================================================================== ---- ioemu.orig/hw/vga_int.h 2006-08-17 19:37:36.372522620 +0100 -+++ ioemu/hw/vga_int.h 2006-08-17 19:49:52.159002688 +0100 +--- ioemu.orig/hw/vga_int.h 2006-12-08 01:57:54.000000000 +0000 ++++ ioemu/hw/vga_int.h 2006-12-08 02:00:04.000000000 +0000 @@ -169,5 +169,6 @@ unsigned int color0, unsigned int color1, unsigned int color_xor); @@ -262,9 +262,9 @@ Index: ioemu/hw/vga_int.h extern const uint8_t gr_mask[16]; Index: ioemu/vl.c =================================================================== ---- ioemu.orig/vl.c 2006-08-17 19:49:50.231215767 +0100 -+++ ioemu/vl.c 2006-08-17 19:49:52.162002356 +0100 -@@ -5693,6 +5693,78 @@ +--- ioemu.orig/vl.c 2006-12-08 02:00:04.000000000 +0000 ++++ ioemu/vl.c 2006-12-08 02:00:27.000000000 +0000 +@@ -5693,6 +5693,62 @@ #define MAX_NET_CLIENTS 32 @@ -303,9 +303,6 @@ Index: ioemu/vl.c + unsigned long nr_pages, unsigned int address_bits, + xen_pfn_t *extent_start) +{ -+#if 0 -+ int i; -+#endif + xc_dominfo_t info; + int err = 0; + @@ -324,19 +321,6 @@ Index: ioemu/vl.c + return -1; + } + -+ err = xc_domain_translate_gpfn_list(xc_handle, domid, nr_pages, -+ extent_start, extent_start); -+ if (err) { -+ fprintf(stderr, "Failed to translate gpfn list\n"); -+ return -1; -+ } -+ -+#if 0 /* Generates lots of log file output - turn on for debugging */ -+ for (i = 0; i < nr_pages; i++) -+ fprintf(stderr, "set_map result i %x result %lx\n", i, -+ extent_start[i]); -+#endif -+ + return 0; +} + @@ -345,8 +329,8 @@ Index: ioemu/vl.c #ifdef CONFIG_GDBSTUB Index: ioemu/vl.h =================================================================== ---- ioemu.orig/vl.h 2006-08-17 19:49:44.492850031 +0100 -+++ ioemu/vl.h 2006-08-17 19:49:52.163002246 +0100 +--- ioemu.orig/vl.h 2006-12-08 02:00:04.000000000 +0000 ++++ ioemu/vl.h 2006-12-08 02:00:04.000000000 +0000 @@ -145,6 +145,13 @@ void main_loop_wait(int timeout); diff --git a/tools/ioemu/patches/tpm-tis-device b/tools/ioemu/patches/tpm-tis-device new file mode 100644 index 0000000000..f0b4c00858 --- /dev/null +++ b/tools/ioemu/patches/tpm-tis-device @@ -0,0 +1,1188 @@ +# HG changeset patch +# User kaf24@localhost.localdomain +# Node ID d60b709724f48397b95da3d56299213cae391789 +# Parent bbcac2aea0e8196cd75a3bf6dbe57bebf8c1e5b2 +[QEMU] Add a TIS device model compliant to the 1.2 TPM specification. +It implements all registers necessary to make the Linux TIS driver +work (tpm_tis.c). All of the basic registers supported by this type of +device are implemented. Also the locality selection has been +implemented, but has not been tested. The legacy registers as +described in the specification are not supported. + +Current caveat: The device has so far not yet been integrated with the +virtual TPM available in the repository. It will require changes to +the virtual TPM spawned by the vTPM manager to offer an additional message +interface. The TIS interface itself then needs to have an additional +transport implemented. (see vTPMTransmit array). + +The relevant specification for the device model can be found here: +https://www.trustedcomputinggroup.org/groups/pc_client/TCG_PCClientTPMSpecification_1-20_1-00_FINAL.pdf + +Signed-off-by: Stefan Berger + +Index: ioemu/Makefile.target +=================================================================== +--- ioemu.orig/Makefile.target 2006-12-08 18:33:48.000000000 +0000 ++++ ioemu/Makefile.target 2006-12-08 18:35:14.000000000 +0000 +@@ -369,6 +369,7 @@ + VL_OBJS+= piix4acpi.o + VL_OBJS+= xenstore.o + VL_OBJS+= xen_platform.o ++VL_OBJS+= tpm_tis.o + DEFINES += -DHAS_AUDIO + endif + ifeq ($(TARGET_BASE_ARCH), ppc) +Index: ioemu/hw/pc.c +=================================================================== +--- ioemu.orig/hw/pc.c 2006-12-08 18:33:47.000000000 +0000 ++++ ioemu/hw/pc.c 2006-12-08 18:33:48.000000000 +0000 +@@ -875,6 +875,9 @@ + } + } + ++ if (has_tpm_device()) ++ tpm_tis_init(&pic_set_irq_new, isa_pic, 11); ++ + kbd_init(); + DMA_init(0); + #ifdef HAS_AUDIO +Index: ioemu/hw/tpm_tis.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ ioemu/hw/tpm_tis.c 2006-12-08 18:35:25.000000000 +0000 +@@ -0,0 +1,1120 @@ ++/* ++ * tpm_tis.c - QEMU emulator for a 1.2 TPM with TIS interface ++ * ++ * Copyright (C) 2006 IBM Corporation ++ * ++ * Author: Stefan Berger ++ * David Safford ++ * ++ * 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. ++ * ++ * ++ * Implementation of the TIS interface according to specs at ++ * https://www.trustedcomputinggroup.org/groups/pc_client/TCG_PCClientTPMSpecification_1-20_1-00_FINAL.pdf ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "vl.h" ++ ++//#define DEBUG_TPM ++ ++#define TPM_MAX_PKT 4096 ++ ++#define VTPM_BAD_INSTANCE (uint32_t)0xffffffff ++ ++#define TIS_ADDR_BASE 0xFED40000 ++ ++/* tis registers */ ++#define TPM_REG_ACCESS 0x00 ++#define TPM_REG_INT_ENABLE 0x08 ++#define TPM_REG_INT_VECTOR 0x0c ++#define TPM_REG_INT_STATUS 0x10 ++#define TPM_REG_INTF_CAPABILITY 0x14 ++#define TPM_REG_STS 0x18 ++#define TPM_REG_DATA_FIFO 0x24 ++#define TPM_REG_DID_VID 0xf00 ++#define TPM_REG_RID 0xf04 ++ ++#define STS_VALID (1 << 7) ++#define STS_COMMAND_READY (1 << 6) ++#define STS_TPM_GO (1 << 5) ++#define STS_DATA_AVAILABLE (1 << 4) ++#define STS_EXPECT (1 << 3) ++#define STS_RESPONSE_RETRY (1 << 1) ++ ++#define ACCESS_TPM_REG_VALID_STS (1 << 7) ++#define ACCESS_ACTIVE_LOCALITY (1 << 5) ++#define ACCESS_BEEN_SEIZED (1 << 4) ++#define ACCESS_SEIZE (1 << 3) ++#define ACCESS_PENDING_REQUEST (1 << 2) ++#define ACCESS_REQUEST_USE (1 << 1) ++#define ACCESS_TPM_ESTABLISHMENT (1 << 0) ++ ++#define INT_ENABLED (1 << 31) ++#define INT_DATA_AVAILABLE (1 << 0) ++#define INT_LOCALITY_CHANGED (1 << 2) ++#define INT_COMMAND_READY (1 << 7) ++ ++#define INTERRUPTS_SUPPORTED (INT_LOCALITY_CHANGED | \ ++ INT_DATA_AVAILABLE | \ ++ INT_COMMAND_READY) ++#define CAPABILITIES_SUPPORTED ((1 << 4) | \ ++ INTERRUPTS_SUPPORTED) ++ ++enum { ++ STATE_IDLE = 0, ++ STATE_READY, ++ STATE_COMPLETION, ++ STATE_EXECUTION, ++ STATE_RECEPTION ++}; ++ ++#define NUM_LOCALITIES 5 ++#define NO_LOCALITY 0xff ++ ++#define IS_VALID_LOC(x) ((x) < NUM_LOCALITIES) ++ ++#define TPM_DID 0x0001 ++#define TPM_VID 0x0001 ++#define TPM_RID 0x0001 ++ ++/* if the connection to the vTPM should be closed after a successfully ++ received response; set to '0' to allow keeping the connection */ ++#define FORCE_CLOSE 0 ++ ++/* local data structures */ ++ ++typedef struct TPMTx { ++ int fd[2]; ++} tpmTx; ++ ++typedef struct TPMBuffer { ++ uint8_t instance[4]; /* instance number in network byte order */ ++ uint8_t buf[TPM_MAX_PKT]; ++} __attribute__((packed)) tpmBuffer; ++ ++/* locality data */ ++typedef struct TPMLocal { ++ uint32_t state; ++ uint8_t access; ++ uint8_t sts; ++ uint32_t inte; ++ uint32_t ints; ++} tpmLoc; ++ ++/* overall state of the TPM interface; 's' marks as save upon suspension */ ++typedef struct TPMState { ++ uint32_t offset; /* s */ ++ tpmBuffer buffer; /* s */ ++ uint8_t active_loc; /* s */ ++ uint8_t aborting_locty; ++ uint8_t next_locty; ++ uint8_t irq_pending; /* s */ ++ tpmLoc loc[NUM_LOCALITIES]; /* s */ ++ QEMUTimer *poll_timer; ++ SetIRQFunc *set_irq; ++ void *irq_opaque; ++ int irq; ++ int poll_attempts; ++ uint32_t vtpm_instance; /* vtpm inst. number; determined from xenstore*/ ++ int Transmitlayer; ++ tpmTx tpmTx; ++} tpmState; ++ ++ ++/* local prototypes */ ++static int TPM_Send(tpmState *s, tpmBuffer *buffer, uint8_t locty, char *msg); ++static int TPM_Receive(tpmState *s, tpmBuffer *buffer); ++static uint32_t vtpm_instance_from_xenstore(void); ++static void tis_poll_timer(void *opaque); ++static void tis_prep_next_interrupt(tpmState *s); ++static void tis_raise_irq(tpmState *s, uint8_t locty, uint32_t irqmask); ++static void close_vtpm_channel(tpmState *s, int force); ++static void open_vtpm_channel(tpmState *s); ++static void tis_attempt_receive(tpmState *s, uint8_t locty); ++ ++/* transport layer functions: local sockets */ ++static int create_local_socket(tpmState *s, uint32_t vtpm_instance); ++static int write_local_socket(tpmState *s, const tpmBuffer *); ++static int read_local_socket(tpmState *s, tpmBuffer *); ++static int close_local_socket(tpmState *s, int force); ++static int has_channel_local_socket(tpmState *s); ++#define LOCAL_SOCKET_PATH "/var/vtpm/vtpm_all.socket" ++ ++ ++#define NUM_TRANSPORTS 1 ++ ++struct vTPM_transmit { ++ int (*open) (tpmState *s, uint32_t vtpm_instance); ++ int (*write) (tpmState *s, const tpmBuffer *); ++ int (*read) (tpmState *s, tpmBuffer *); ++ int (*close) (tpmState *s, int); ++ int (*has_channel) (tpmState *s); ++} vTPMTransmit[NUM_TRANSPORTS] = { ++ { .open = create_local_socket, ++ .write = write_local_socket, ++ .read = read_local_socket, ++ .close = close_local_socket, ++ .has_channel = has_channel_local_socket, ++ } ++}; ++ ++ ++#define IS_COMM_WITH_VTPM(s) \ ++ ((s)->Transmitlayer >= 0 && \ ++ vTPMTransmit[(s)->Transmitlayer].has_channel(s)) ++ ++ ++/********************************************************************** ++ helper functions ++ *********************************************************************/ ++ ++static inline uint32_t tpm_get_size_from_buffer(const uint8_t *buffer) ++{ ++ uint32_t len = (buffer[4] << 8) + buffer[5]; ++ return len; ++} ++ ++static inline void tpm_initialize_instance(tpmState *s, uint32_t instance) ++{ ++ s->buffer.instance[0] = (instance >> 24) & 0xff; ++ s->buffer.instance[1] = (instance >> 16) & 0xff; ++ s->buffer.instance[2] = (instance >> 8) & 0xff; ++ s->buffer.instance[3] = (instance >> 0) & 0xff; ++} ++ ++/* ++ * open communication channel with a vTPM ++ */ ++static void open_vtpm_channel(tpmState *s) ++{ ++ int idx; ++ /* search a usable transmit layer */ ++ for (idx = 0; idx < NUM_TRANSPORTS; idx++) { ++ if (1 == vTPMTransmit[idx].open(s, s->vtpm_instance)) { ++ /* found one */ ++ s->Transmitlayer = idx; ++ break; ++ } ++ } ++} ++ ++/* ++ * close the communication channel with the vTPM ++ */ ++static inline void close_vtpm_channel(tpmState *s, int force) ++{ ++ if (1 == vTPMTransmit[s->Transmitlayer].close(s, force)) { ++ s->Transmitlayer = -1; ++ } ++} ++ ++static inline uint8_t locality_from_addr(target_phys_addr_t addr) ++{ ++ return (uint8_t)((addr >> 12) & 0x7); ++} ++ ++ ++/********************************************************************** ++ low-level transmission layer methods ++ *********************************************************************/ ++ ++/* ++ * the 'open' method that creates the filedescriptor for communicating ++ * only one is needed for reading and writing ++ */ ++static int create_local_socket(tpmState *s, uint32_t vtpm_instance) ++{ ++ int success = 1; ++ if (s->tpmTx.fd[0] < 0) { ++ s->tpmTx.fd[0] = socket(PF_LOCAL, SOCK_STREAM, 0); ++ ++ if (has_channel_local_socket(s)) { ++ struct sockaddr_un addr; ++ memset(&addr, 0x0, sizeof(addr)); ++ addr.sun_family = AF_LOCAL; ++ strcpy(addr.sun_path, LOCAL_SOCKET_PATH); ++ if (connect(s->tpmTx.fd[0], ++ (struct sockaddr *)&addr, ++ sizeof(addr)) != 0) { ++ close_local_socket(s, 1); ++ success = 0; ++ } else { ++ /* put filedescriptor in non-blocking mode for polling */ ++ int flags = fcntl(s->tpmTx.fd[0], F_GETFL); ++ fcntl(s->tpmTx.fd[0], F_SETFL, flags | O_NONBLOCK); ++ } ++#ifdef DEBUG_TPM ++ if (success) ++ fprintf(logfile,"Successfully connected using local socket " ++ LOCAL_SOCKET_PATH ".\n"); ++ else ++ fprintf(logfile,"Could not connect to local socket " ++ LOCAL_SOCKET_PATH ".\n"); ++#endif ++ } else { ++ success = 0; ++ } ++ } ++ return success; ++} ++ ++/* ++ * the 'write' method for sending requests to the vTPM ++ * four bytes with the vTPM instance number are prepended to each request ++ * the locality in which the command was sent is transmitted in the ++ * highest 3 bits ++ */ ++static int write_local_socket(tpmState *s, const tpmBuffer *buffer) ++{ ++ uint32_t size = tpm_get_size_from_buffer(buffer->buf); ++ int len; ++ ++ len = write(s->tpmTx.fd[0], ++ buffer->instance, ++ sizeof(buffer->instance) + size); ++ if (len == sizeof(buffer->instance) + size) { ++ return len; ++ } else { ++ return -1; ++ } ++} ++ ++/* ++ * the 'read' method for receiving of responses from the TPM ++ * this function expects that four bytes with the instance number ++ * are received from the vTPM ++ */ ++static int read_local_socket(tpmState *s, tpmBuffer *buffer) ++{ ++ int off; ++#ifdef DEBUG_TPM ++ fprintf(logfile, "Reading from fd %d\n", s->tpmTx.fd[0]); ++#endif ++ off = read(s->tpmTx.fd[0], ++ buffer->instance, ++ sizeof(buffer->instance)+TPM_MAX_PKT); ++#ifdef DEBUG_TPM ++ fprintf(logfile, "Read %d bytes\n", off); ++#endif ++ return off; ++} ++ ++/* ++ * the 'close' method ++ * shut down communication with the vTPM ++ * 'force' = 1 indicates that the socket *must* be closed ++ * 'force' = 0 indicates that a connection may be maintained ++ */ ++static int close_local_socket(tpmState *s, int force) ++{ ++ if (force) { ++ close(s->tpmTx.fd[0]); ++#ifdef DEBUG_TPM ++ fprintf(logfile,"Closed connection with fd %d\n",s->tpmTx.fd[0]); ++#endif ++ s->tpmTx.fd[0] = -1; ++ return 1; /* socket was closed */ ++ } ++#ifdef DEBUG_TPM ++ fprintf(logfile,"Keeping connection with fd %d\n",s->tpmTx.fd[0]); ++#endif ++ return 0; ++} ++ ++/* ++ * the 'has_channel' method that checks whether there's a communication ++ * channel with the vTPM ++ */ ++static int has_channel_local_socket(tpmState *s) ++{ ++ return (s->tpmTx.fd[0] > 0); ++} ++ ++/**********************************************************************/ ++ ++/* ++ * read a byte of response data ++ */ ++static uint32_t tpm_data_read(tpmState *s, uint8_t locty) ++{ ++ uint32_t ret, len; ++ ++ /* try to receive data, if none are there it is ok */ ++ tis_attempt_receive(s, locty); ++ ++ if (s->loc[locty].state != STATE_COMPLETION) { ++ return 0xff; ++ } ++ ++ len = tpm_get_size_from_buffer(s->buffer.buf); ++ ret = s->buffer.buf[s->offset++]; ++ if (s->offset >= len) { ++ s->loc[locty].sts = STS_VALID ; ++ s->offset = 0; ++ } ++#ifdef DEBUG_TPM ++ fprintf(logfile,"tpm_data_read byte x%02x [%d]\n",ret,s->offset-1); ++#endif ++ return ret; ++} ++ ++ ++ ++/* raise an interrupt if allowed */ ++static void tis_raise_irq(tpmState *s, uint8_t locty, uint32_t irqmask) ++{ ++ if (!s->irq_pending && ++ (s->loc[locty].inte & INT_ENABLED) && ++ (s->loc[locty].inte & irqmask)) { ++ if ((irqmask & s->loc[locty].ints) == 0) { ++#ifdef DEBUG_TPM ++ fprintf(logfile,"Raising IRQ for flag %08x\n",irqmask); ++#endif ++ s->set_irq(s->irq_opaque, s->irq, 1); ++ s->irq_pending = 1; ++ s->loc[locty].ints |= irqmask; ++ } ++ } ++} ++ ++/* abort execution of command */ ++static void tis_abort(tpmState *s) ++{ ++ s->offset = 0; ++ s->active_loc = s->next_locty; ++ ++ /* ++ * Need to react differently depending on who's aborting now and ++ * which locality will become active afterwards. ++ */ ++ if (s->aborting_locty == s->next_locty) { ++ s->loc[s->aborting_locty].state = STATE_READY; ++ s->loc[s->aborting_locty].sts = STS_COMMAND_READY; ++ tis_raise_irq(s, s->aborting_locty, INT_COMMAND_READY); ++ } ++ ++ /* locality after abort is another one than the current one */ ++ if (s->aborting_locty != s->next_locty && s->next_locty != NO_LOCALITY) { ++ s->loc[s->aborting_locty].access &= ~ACCESS_ACTIVE_LOCALITY; ++ s->loc[s->next_locty].access |= ACCESS_ACTIVE_LOCALITY; ++ tis_raise_irq(s, s->next_locty, INT_LOCALITY_CHANGED); ++ } ++ ++ s->aborting_locty = NO_LOCALITY; /* nobody's aborting a command anymore */ ++ ++ qemu_del_timer(s->poll_timer); ++} ++ ++/* abort current command */ ++static void tis_prep_abort(tpmState *s, uint8_t locty, uint8_t newlocty) ++{ ++ s->aborting_locty = locty; /* current locality */ ++ s->next_locty = newlocty; /* locality after successful abort */ ++ ++ /* ++ * only abort a command using an interrupt if currently executing ++ * a command AND if there's a valid connection to the vTPM. ++ */ ++ if (s->loc[locty].state == STATE_EXECUTION && ++ IS_COMM_WITH_VTPM(s)) { ++ /* start timer and inside the timer wait for the result */ ++ s->poll_attempts = 0; ++ tis_prep_next_interrupt(s); ++ } else { ++ tis_abort(s); ++ } ++} ++ ++ ++/* ++ * Try to receive a response from the vTPM ++ */ ++static void tis_attempt_receive(tpmState *s, uint8_t locty) ++{ ++ /* ++ * Attempt to read from the vTPM here if ++ * - not aborting a command ++ * - command has been sent and state is 'EXECUTION' now ++ * - no data are already available (data have already been read) ++ * - there's a communication path to the vTPM established ++ */ ++ if (!IS_VALID_LOC(s->aborting_locty)) { ++ if (s->loc[locty].state == STATE_EXECUTION) { ++ if (0 == (s->loc[locty].sts & STS_DATA_AVAILABLE)){ ++ if (IS_COMM_WITH_VTPM(s)) { ++ int n = TPM_Receive(s, &s->buffer); ++ if (n > 0) { ++ s->loc[locty].sts = STS_VALID | STS_DATA_AVAILABLE; ++ s->loc[locty].state = STATE_COMPLETION; ++ close_vtpm_channel(s, FORCE_CLOSE); ++ tis_raise_irq(s, locty, INT_DATA_AVAILABLE); ++ } ++ } ++ } ++ } ++ } ++} ++ ++/* ++ * Read a register of the TIS interface ++ * See specs pages 33-63 for description of the registers ++ */ ++static uint32_t tis_mem_readl(void *opaque, target_phys_addr_t addr) ++{ ++ tpmState *s = (tpmState *)opaque; ++ uint16_t offset = addr & 0xffc; ++ uint8_t shift = (addr & 0x3) * 8; ++ uint32_t val = 0; ++ uint8_t locty = locality_from_addr(addr); ++ ++ if (offset == TPM_REG_ACCESS) { ++ if (s->active_loc == locty) { ++ s->loc[locty].access |= (1 << 5); ++ } else { ++ s->loc[locty].access &= ~(1 << 5); ++ } ++ val = s->loc[locty].access; ++ } else ++ if (offset == TPM_REG_INT_ENABLE) { ++ val = s->loc[locty].inte; ++ } else ++ if (offset == TPM_REG_INT_VECTOR) { ++ val = s->irq; ++ } else ++ if (offset == TPM_REG_INT_STATUS) { ++ tis_attempt_receive(s, locty); ++ val = s->loc[locty].ints; ++ } else ++ if (offset == TPM_REG_INTF_CAPABILITY) { ++ val = CAPABILITIES_SUPPORTED; ++ } else ++ if (offset == TPM_REG_STS) { /* status register */ ++ tis_attempt_receive(s, locty); ++ val = (sizeof(s->buffer.buf) - s->offset) << 8 | s->loc[locty].sts; ++ } else ++ if (offset == TPM_REG_DATA_FIFO) { ++ val = tpm_data_read(s, locty); ++ } else ++ if (offset == TPM_REG_DID_VID) { ++ val = (TPM_DID << 16) | TPM_VID; ++ } else ++ if (offset == TPM_REG_RID) { ++ val = TPM_RID; ++ } ++ ++ if (shift) ++ val >>= shift; ++ ++#ifdef DEBUG_TPM ++ fprintf(logfile," read(%08x) = %08x\n", ++ addr, ++ val); ++#endif ++ ++ return val; ++} ++ ++/* ++ * Write a value to a register of the TIS interface ++ * See specs pages 33-63 for description of the registers ++ */ ++static void tis_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) ++{ ++ tpmState* s=(tpmState*)opaque; ++ uint16_t off = addr & 0xfff; ++ uint8_t locty = locality_from_addr(addr); ++ int n, c; ++ uint32_t len; ++ ++#ifdef DEBUG_TPM ++ fprintf(logfile,"write(%08x) = %08x\n", ++ addr, ++ val); ++#endif ++ ++ if (off == TPM_REG_ACCESS) { ++ if (val & ACCESS_ACTIVE_LOCALITY) { ++ /* give up locality if currently owned */ ++ if (s->active_loc == locty) { ++ uint8_t newlocty = NO_LOCALITY; ++ s->loc[locty].access &= ~(ACCESS_PENDING_REQUEST); ++ /* anybody wants the locality ? */ ++ for (c = NUM_LOCALITIES - 1; c >= 0; c--) { ++ if (s->loc[c].access & ACCESS_REQUEST_USE) { ++ s->loc[c].access |= ACCESS_TPM_REG_VALID_STS; ++ s->loc[c].access &= ~ACCESS_REQUEST_USE; ++ newlocty = c; ++ break; ++ } ++ } ++ tis_prep_abort(s, locty, newlocty); ++ } ++ } ++ if (val & ACCESS_BEEN_SEIZED) { ++ /* clear the flag */ ++ s->loc[locty].access &= ~ACCESS_BEEN_SEIZED; ++ } ++ if (val & ACCESS_SEIZE) { ++ if (locty > s->active_loc && IS_VALID_LOC(s->active_loc)) { ++ s->loc[s->active_loc].access |= ACCESS_BEEN_SEIZED; ++ s->loc[locty].access = ACCESS_TPM_REG_VALID_STS; ++ tis_prep_abort(s, s->active_loc, locty); ++ } ++ } ++ if (val & ACCESS_REQUEST_USE) { ++ if (IS_VALID_LOC(s->active_loc)) { ++ /* locality election */ ++ s->loc[s->active_loc].access |= ACCESS_PENDING_REQUEST; ++ } else { ++ /* no locality active -> make this one active now */ ++ s->loc[locty].access |= ACCESS_ACTIVE_LOCALITY; ++ s->active_loc = locty; ++ tis_raise_irq(s, locty, INT_LOCALITY_CHANGED); ++ } ++ } ++ } else ++ if (off == TPM_REG_INT_ENABLE) { ++ s->loc[locty].inte = (val & (INT_ENABLED | (0x3 << 3) | ++ INTERRUPTS_SUPPORTED)); ++ } else ++ if (off == TPM_REG_INT_STATUS) { ++ /* clearing of interrupt flags */ ++ if ((val & INTERRUPTS_SUPPORTED) && ++ (s->loc[locty].ints & INTERRUPTS_SUPPORTED)) { ++ s->set_irq(s->irq_opaque, s->irq, 0); ++ s->irq_pending = 0; ++ } ++ s->loc[locty].ints &= ~(val & INTERRUPTS_SUPPORTED); ++ } else ++ if (off == TPM_REG_STS) { ++ if (val & STS_COMMAND_READY) { ++ if (s->loc[locty].state == STATE_IDLE) { ++ s->loc[locty].sts = STS_COMMAND_READY; ++ s->loc[locty].state = STATE_READY; ++ tis_raise_irq(s, locty, INT_COMMAND_READY); ++ } else if (s->loc[locty].state == STATE_COMPLETION || ++ s->loc[locty].state == STATE_EXECUTION || ++ s->loc[locty].state == STATE_RECEPTION) { ++ /* abort currently running command */ ++ tis_prep_abort(s, locty, locty); ++ } ++ } ++ if (val & STS_TPM_GO) { ++ n = TPM_Send(s, &s->buffer, locty, "tpm_data_write"); ++ if (n > 0) { ++ /* sending of data was successful */ ++ s->offset = 0; ++ s->loc[locty].state = STATE_EXECUTION; ++ if (s->loc[locty].inte & (INT_ENABLED | INT_DATA_AVAILABLE)) { ++ s->poll_attempts = 0; ++ tis_prep_next_interrupt(s); ++ } ++ } ++ } ++ if (val & STS_RESPONSE_RETRY) { ++ s->offset = 0; ++ } ++ } else if (off == TPM_REG_DATA_FIFO) { ++ /* data fifo */ ++ if (s->loc[locty].state == STATE_IDLE || ++ s->loc[locty].state == STATE_EXECUTION || ++ s->loc[locty].state == STATE_COMPLETION) { ++ /* drop the byte */ ++ } else { ++#ifdef TPM_DEBUG ++ fprintf(logfile,"Byte to send to TPM: %02x\n", val); ++#endif ++ s->loc[locty].state = STATE_RECEPTION; ++ ++ if (s->offset < sizeof(s->buffer.buf)) ++ s->buffer.buf[s->offset++] = (uint8_t)val; ++ ++ if (s->offset > 5) { ++ /* we have a packet length - see if we have all of it */ ++ len = tpm_get_size_from_buffer(s->buffer.buf); ++ if (len > s->offset) { ++ s->loc[locty].sts = STS_EXPECT | STS_VALID; ++ } else { ++ s->loc[locty].sts = STS_VALID; ++ } ++ } ++ } ++ } ++} ++ ++/* ++ * Prepare the next interrupt for example after a command has ++ * been sent out for the purpose of receiving the response. ++ * Depending on how many interrupts (used for polling on the fd) have ++ * already been schedule, this function determines the delta in time ++ * to the next interrupt. This accomodates for commands that finish ++ * quickly. ++ */ ++static void tis_prep_next_interrupt(tpmState *s) ++{ ++ int64_t expiration; ++ int rate = 5; /* 5 times per second */ ++ ++ /* ++ poll often at the beginning for quickly finished commands, ++ then back off ++ */ ++ if (s->poll_attempts < 5) { ++ rate = 20; ++ } else if (s->poll_attempts < 10) { ++ rate = 10; ++ } ++ ++ expiration = qemu_get_clock(vm_clock) + (ticks_per_sec / rate); ++ qemu_mod_timer(s->poll_timer, expiration); ++ s->poll_attempts++; ++} ++ ++ ++/* ++ * The polling routine called when the 'timer interrupt' fires. ++ * Tries to receive a command from the vTPM. ++ */ ++static void tis_poll_timer(void *opaque) ++{ ++ tpmState* s=(tpmState*)opaque; ++ uint8_t locty = s->active_loc; ++ ++ if (!IS_VALID_LOC(locty) || ++ (!(s->loc[locty].inte & INT_ENABLED) && ++ (s->aborting_locty != NO_LOCALITY)) || ++ !IS_COMM_WITH_VTPM(s)) { ++ /* no more interrupts requested, so no more polling needed */ ++ qemu_del_timer(s->poll_timer); ++ } ++ ++ if (!IS_COMM_WITH_VTPM(s)) { ++ if (s->aborting_locty != NO_LOCALITY) { ++ tis_abort(s); ++ } ++ return; ++ } ++ ++ if (s->aborting_locty != NO_LOCALITY) { ++ int n = TPM_Receive(s, &s->buffer); ++#ifdef DEBUG_TPM ++ fprintf(logfile,"Receiving for abort.\n"); ++#endif ++ if (n > 0) { ++ close_vtpm_channel(s, FORCE_CLOSE); ++ tis_abort(s); ++#ifdef DEBUG_TPM ++ fprintf(logfile,"Abort is complete.\n"); ++#endif ++ } else { ++ tis_prep_next_interrupt(s); ++ } ++ } else if (IS_VALID_LOC(locty)) { ++ if (s->loc[locty].state == STATE_EXECUTION) { ++ /* poll for result */ ++ int n = TPM_Receive(s, &s->buffer); ++ if (n > 0) { ++ s->loc[locty].sts = STS_VALID | STS_DATA_AVAILABLE; ++ s->loc[locty].state = STATE_COMPLETION; ++ close_vtpm_channel(s, FORCE_CLOSE); ++ tis_raise_irq(s, locty, INT_DATA_AVAILABLE); ++ } else { ++ /* nothing received */ ++ tis_prep_next_interrupt(s); ++ } ++ } ++ } ++} ++ ++ ++static CPUReadMemoryFunc *tis_readfn[3]={ ++ tis_mem_readl, ++ tis_mem_readl, ++ tis_mem_readl ++}; ++ ++static CPUWriteMemoryFunc *tis_writefn[3]={ ++ tis_mem_writel, ++ tis_mem_writel, ++ tis_mem_writel ++}; ++ ++/* ++ * Save the internal state of this interface for later resumption. ++ * Need to get any outstanding responses from the vTPM back, so ++ * this might delay the suspend for a while. ++ */ ++static void tpm_save(QEMUFile* f,void* opaque) ++{ ++ tpmState* s=(tpmState*)opaque; ++ int c; ++ ++ /* need to wait for outstanding requests to complete */ ++ if (IS_COMM_WITH_VTPM(s)) { ++ int repeats = 30; /* 30 seconds; really should be infty */ ++ while (repeats > 0 && ++ !(s->loc[s->active_loc].sts & STS_DATA_AVAILABLE)) { ++ int n = TPM_Receive(s, &s->buffer); ++ if (n > 0) { ++ if (IS_VALID_LOC(s->active_loc)) { ++ s->loc[s->active_loc].sts = STS_VALID | STS_DATA_AVAILABLE; ++ } ++ /* close the connection with the vTPM for good */ ++ close_vtpm_channel(s, 1); ++ break; ++ } ++ sleep(1); ++ } ++ } ++ ++ qemu_put_be32s(f,&s->offset); ++ qemu_put_buffer(f, s->buffer.buf, TPM_MAX_PKT); ++ qemu_put_8s(f, &s->active_loc); ++ qemu_put_8s(f, &s->irq_pending); ++ for (c = 0; c < NUM_LOCALITIES; c++) { ++ qemu_put_be32s(f, &s->loc[c].state); ++ qemu_put_8s(f, &s->loc[c].access); ++ qemu_put_8s(f, &s->loc[c].sts); ++ qemu_put_be32s(f, &s->loc[c].inte); ++ qemu_put_be32s(f, &s->loc[c].ints); ++ } ++} ++ ++/* ++ * load TIS interface state ++ */ ++static int tpm_load(QEMUFile* f,void* opaque,int version_id) ++{ ++ tpmState* s=(tpmState*)opaque; ++ int c; ++ ++ if (version_id != 1) ++ return -EINVAL; ++ ++ qemu_get_be32s(f,&s->offset); ++ qemu_get_buffer(f, s->buffer.buf, TPM_MAX_PKT); ++ qemu_get_8s(f, &s->active_loc); ++ qemu_get_8s(f, &s->irq_pending); ++ for (c = 0; c < NUM_LOCALITIES; c++) { ++ qemu_get_be32s(f, &s->loc[c].state); ++ qemu_get_8s(f, &s->loc[c].access); ++ qemu_get_8s(f, &s->loc[c].sts); ++ qemu_get_be32s(f, &s->loc[c].inte); ++ qemu_get_be32s(f, &s->loc[c].ints); ++ } ++ ++ /* need to be able to get the instance number from the xenstore */ ++ s->vtpm_instance = vtpm_instance_from_xenstore(); ++ if (s->vtpm_instance == VTPM_BAD_INSTANCE) ++ return -EINVAL; ++ tpm_initialize_instance(s, s->vtpm_instance); ++ ++ return 0; ++} ++ ++ ++typedef struct LPCtpmState { ++ tpmState tpm; ++ int mem; ++} LPCtpmState; ++ ++ ++/* ++ * initialize TIS interface ++ */ ++void tpm_tis_init(SetIRQFunc *set_irq, void *opaque, int irq) ++{ ++ LPCtpmState *d; ++ tpmState *s; ++ int c = 0; ++ uint32_t vtpm_in; ++ ++ vtpm_in = vtpm_instance_from_xenstore(); ++ /* no valid vtpm instance -> no device */ ++ if (vtpm_in == VTPM_BAD_INSTANCE) ++ return; ++ ++ d = qemu_mallocz(sizeof(LPCtpmState)); ++ d->mem = cpu_register_io_memory(0, tis_readfn, tis_writefn, d); ++ ++ if (d->mem == -1) { ++ return; ++ } ++ ++ cpu_register_physical_memory(TIS_ADDR_BASE, ++ 0x1000 * NUM_LOCALITIES, d->mem); ++ ++ /* initialize tpmState */ ++ s = &d->tpm; ++ ++ s->offset = 0; ++ s->active_loc = NO_LOCALITY; ++ ++ while (c < NUM_LOCALITIES) { ++ s->loc[c].access = (1 << 7); ++ s->loc[c].sts = 0; ++ s->loc[c].inte = (1 << 3); ++ s->loc[c].ints = 0; ++ s->loc[c].state = STATE_IDLE; ++ c++; ++ } ++ s->poll_timer = qemu_new_timer(vm_clock, tis_poll_timer, s); ++ s->set_irq = set_irq; ++ s->irq_opaque = opaque; ++ s->irq = irq; ++ s->vtpm_instance = vtpm_in; ++ s->Transmitlayer = -1; ++ s->tpmTx.fd[0] = -1; ++ s->tpmTx.fd[1] = -1; ++ ++ tpm_initialize_instance(s, s->vtpm_instance); ++ memset(s->buffer.buf,0,sizeof(s->buffer.buf)); ++ ++ register_savevm("tpm-tis", 0, 1, tpm_save, tpm_load, s); ++} ++ ++/****************************************************************************/ ++/* optional verbose logging of data to/from vtpm */ ++/****************************************************************************/ ++#ifdef DEBUG_TPM ++static void showBuff(unsigned char *buff, char *string) ++{ ++ uint32_t i, len; ++ ++ len = tpm_get_size_from_buffer(buff); ++ fprintf(logfile,"%s length = %d\n", string, len); ++ for (i = 0; i < len; i++) { ++ if (i && !(i % 16)) { ++ fprintf(logfile,"\n"); ++ } ++ fprintf(logfile,"%.2X ", buff[i]); ++ } ++ fprintf(logfile,"\n"); ++} ++#endif ++ ++/****************************************************************************/ ++/* Transmit request to TPM and read Response */ ++/****************************************************************************/ ++ ++const static unsigned char tpm_failure[] = { ++ 0x00, 0x00, ++ 0x00, 0x00, 0x00, 0x0a, ++ 0x00, 0x00, 0x00, 0x09 ++}; ++ ++ ++/* ++ * Send a TPM request. ++ */ ++static int TPM_Send(tpmState *s, tpmBuffer *buffer, uint8_t locty, char *msg) ++{ ++ int len; ++ uint32_t size = tpm_get_size_from_buffer(buffer->buf); ++ ++ /* try to establish a connection to the vTPM */ ++ if ( !IS_COMM_WITH_VTPM(s)) { ++ open_vtpm_channel(s); ++ } ++ ++ if ( !IS_COMM_WITH_VTPM(s)) { ++ unsigned char tag = buffer->buf[1]; ++ ++ /* there's a failure response from the TPM */ ++ memcpy(buffer->buf, tpm_failure, sizeof(tpm_failure)); ++ buffer->buf[1] = tag + 3; ++ if (IS_VALID_LOC(s->active_loc)) { ++ s->loc[s->active_loc].sts = STS_DATA_AVAILABLE | STS_VALID; ++ } ++#ifdef DEBUG_TPM ++ fprintf(logfile,"No TPM running!\n"); ++#endif ++ /* the request went out ok. */ ++ return sizeof(buffer->instance) + size; ++ } ++ ++#ifdef DEBUG_TPM ++ showBuff(buffer->buf, "To TPM"); ++#endif ++ ++ /* transmit the locality in the highest 3 bits */ ++ buffer->instance[0] &= 0x1f; ++ buffer->instance[0] |= (locty << 5); ++ ++ len = vTPMTransmit[s->Transmitlayer].write(s, buffer); ++ if (len < 0) { ++ s->Transmitlayer = -1; ++ } ++ return len; ++} ++ ++/* ++ * Try to receive data from the file descriptor. Since it is in ++ * non-blocking mode it is possible that no data are actually received - ++ * whatever calls this function needs to try again later. ++ */ ++static int TPM_Receive(tpmState *s, tpmBuffer *buffer) ++{ ++ int off; ++ ++ off = vTPMTransmit[s->Transmitlayer].read(s, buffer); ++ ++ if (off < 0) { ++ /* EAGAIN is set in errno due to non-blocking mode */ ++ return -1; ++ } ++ ++ if (off == 0) { ++#ifdef DEBUG_TPM ++ fprintf(logfile,"TPM GONE? errno=%d\n",errno); ++#endif ++ close_vtpm_channel(s, 1); ++ /* pretend that data are available */ ++ if (IS_VALID_LOC(s->active_loc)) { ++ s->loc[s->active_loc].sts = STS_VALID | STS_DATA_AVAILABLE; ++ s->loc[s->active_loc].state = STATE_COMPLETION; ++ tis_raise_irq(s, s->active_loc, INT_DATA_AVAILABLE); ++ } ++ return -1; ++ } ++ ++#ifdef DEBUG_TPM ++ if (off > sizeof(buffer->instance ) + 6) { ++ uint32_t size = tpm_get_size_from_buffer(buffer->buf); ++ if (size + sizeof(buffer->instance) != off) { ++ fprintf(logfile,"TPM: Packet size is bad! %d != %d\n", ++ size + sizeof(buffer->instance), ++ off); ++ } else { ++ uint32_t ret; ++ showBuff(buffer->buf, "From TPM"); ++ ret = (buffer->buf[8])*256 + buffer->buf[9]; ++ if (ret) ++ fprintf(logfile,"Receive failed with error %d\n", ret); ++ else ++ fprintf(logfile,"Receive succeeded. Got response of length %d (=%d)\n", ++ size, off); ++ } ++ } ++#endif ++ ++ /* assuming reading in one chunk for now */ ++ return off; ++} ++ ++ ++/**************************************************************************** ++ Helper functions for reading data from the xenstore such as ++ reading virtual TPM instance information ++ ****************************************************************************/ ++int has_tpm_device(void) ++{ ++ int ret = 0; ++ struct xs_handle *handle = xs_daemon_open(); ++ if (handle) { ++ ret = xenstore_domain_has_devtype(handle, "vtpm"); ++ xs_daemon_close(handle); ++ } ++ return ret; ++} ++ ++ ++/* ++ * Wait until hotplug scripts have finished then read the vTPM instance ++ * number from the xenstore. ++ */ ++static uint32_t vtpm_instance_from_xenstore(void) ++{ ++ unsigned int num; ++ uint32_t number = VTPM_BAD_INSTANCE; ++ int end = 0; ++ char *token = "tok"; ++ int subscribed = 0; ++ int ctr = 0; ++ fd_set readfds; ++ ++ struct xs_handle *handle = xs_daemon_open(); ++ ++ FD_ZERO(&readfds); ++ ++ if (handle) { ++ char **e = xenstore_domain_get_devices(handle, "vtpm", &num); ++ int fd = xs_fileno(handle); ++ FD_SET(fd, &readfds); ++ if (e) { ++ do { ++ struct timeval tv = { ++ .tv_sec = 30, ++ .tv_usec = 0, ++ }; ++ /* need to make sure that the hotplug scripts have finished */ ++ char *status = xenstore_read_hotplug_status(handle, ++ "vtpm", ++ e[0]); ++ if (status) { ++ if (!strcmp(status, "connected")) { ++ char *inst = xenstore_backend_read_variable(handle, ++ "vtpm", ++ e[0], ++ "instance"); ++ if (1 != (sscanf(inst,"%d",&number))) ++ number = VTPM_BAD_INSTANCE; ++ free(inst); ++ } else { ++ fprintf(logfile, ++ "bad status '%s' from vtpm hotplug\n", ++ status); ++ } ++ free(status); ++ end = 1; ++ } else { ++ /* no status, yet */ ++ int rc; ++ unsigned int nr; ++ char **f; ++ ++ if (!subscribed) { ++ rc = xenstore_subscribe_to_hotplug_status(handle, ++ "vtpm", ++ e[0], ++ token); ++ if (rc != 0) ++ break; ++ subscribed = 1; ++ } ++ rc = select(fd+1, &readfds, NULL, NULL, &tv); ++ /* get what's available -- drain the fd */ ++ f = xs_read_watch(handle, &nr); ++ ctr++; ++ free(f); ++ if (ctr > 2) ++ end = 1; ++ } ++ } while (end == 0); ++ free(e); ++ } ++ if (subscribed) { ++ /* clean up */ ++ xenstore_unsubscribe_from_hotplug_status(handle, ++ "vtpm", ++ e[0], ++ token); ++ } ++ xs_daemon_close(handle); ++ } ++ if (number == VTPM_BAD_INSTANCE) ++ fprintf(logfile, "no valid vtpm instance"); ++ else ++ fprintf(logfile,"vtpm instance:%d\n",number); ++ return number; ++} +Index: ioemu/vl.h +=================================================================== +--- ioemu.orig/vl.h 2006-12-08 18:33:48.000000000 +0000 ++++ ioemu/vl.h 2006-12-08 18:35:14.000000000 +0000 +@@ -932,6 +932,10 @@ + void piix4_pm_init(PCIBus *bus, int devfn); + void acpi_bios_init(void); + ++/* tpm_tis.c */ ++int has_tpm_device(void); ++void tpm_tis_init(SetIRQFunc *set_irq, void *irq_opaque, int irq); ++ + /* piix4acpi.c */ + extern void pci_piix4_acpi_init(PCIBus *bus, int devfn); + diff --git a/tools/ioemu/patches/usb-mouse-tablet-status-check b/tools/ioemu/patches/usb-mouse-tablet-status-check new file mode 100644 index 0000000000..6047523fea --- /dev/null +++ b/tools/ioemu/patches/usb-mouse-tablet-status-check @@ -0,0 +1,168 @@ +# HG changeset patch +# User kfraser@localhost.localdomain +# Node ID 60bbcf799384d779c2a561b9d9ba30f28e31d970 +# Parent fb3cb6f52a2905be938559529ae43b6ba990c878 +[HVM] qemu mouse: Adds support for USB mouse/tablet status check and +restricts Universal Host Controller interrupt generating when received +NAK in interrupt transfer. + +According to usb spec, USB mouse/tablet device returns NAK to host +controller if its status does not alter in interrupt transfer. +And UHC should leave a TD active when receiving NAK and execute this +incompleted TD in a subseqent frame. UHC only generates an interrupt +on complete after the TD with ICO bit is completed. + +This patch make UHC & USB mouse/tablet behave consistently with spec. + +Signed-off-by: Xinmei Huang + +diff -r fb3cb6f52a29 -r 60bbcf799384 tools/ioemu/hw/usb-hid.c +--- a/tools/ioemu/hw/usb-hid.c Thu Dec 07 11:51:22 2006 +0000 ++++ b/tools/ioemu/hw/usb-hid.c Thu Dec 07 11:52:26 2006 +0000 +@@ -39,6 +39,7 @@ typedef struct USBMouseState { + int x, y; + int kind; + int mouse_grabbed; ++ int status_changed; + } USBMouseState; + + /* mostly the same values as the Bochs USB Mouse device */ +@@ -231,6 +232,7 @@ static void usb_mouse_event(void *opaque + s->dy += dy1; + s->dz += dz1; + s->buttons_state = buttons_state; ++ s->status_changed = 1; + } + + static void usb_tablet_event(void *opaque, +@@ -242,6 +244,7 @@ static void usb_tablet_event(void *opaqu + s->y = y; + s->dz += dz; + s->buttons_state = buttons_state; ++ s->status_changed = 1; + } + + static inline int int_clamp(int val, int vmin, int vmax) +@@ -483,10 +486,16 @@ static int usb_mouse_handle_data(USBDevi + switch(pid) { + case USB_TOKEN_IN: + if (devep == 1) { +- if (s->kind == USB_MOUSE) +- ret = usb_mouse_poll(s, data, len); +- else if (s->kind == USB_TABLET) +- ret = usb_tablet_poll(s, data, len); ++ if (s->kind == USB_MOUSE) ++ ret = usb_mouse_poll(s, data, len); ++ else if (s->kind == USB_TABLET) ++ ret = usb_tablet_poll(s, data, len); ++ ++ if (!s->status_changed) ++ ret = USB_RET_NAK; ++ else ++ s->status_changed = 0; ++ + } else { + goto fail; + } +@@ -523,6 +532,7 @@ USBDevice *usb_tablet_init(void) + s->dev.handle_data = usb_mouse_handle_data; + s->dev.handle_destroy = usb_mouse_handle_destroy; + s->kind = USB_TABLET; ++ s->status_changed = 0; + + pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Tablet"); + +@@ -544,6 +554,7 @@ USBDevice *usb_mouse_init(void) + s->dev.handle_data = usb_mouse_handle_data; + s->dev.handle_destroy = usb_mouse_handle_destroy; + s->kind = USB_MOUSE; ++ s->status_changed = 0; + + pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Mouse"); + +diff -r fb3cb6f52a29 -r 60bbcf799384 tools/ioemu/hw/usb-uhci.c +--- a/tools/ioemu/hw/usb-uhci.c Thu Dec 07 11:51:22 2006 +0000 ++++ b/tools/ioemu/hw/usb-uhci.c Thu Dec 07 11:52:26 2006 +0000 +@@ -424,12 +424,10 @@ static int uhci_handle_td(UHCIState *s, + uint8_t buf[2048]; + int len, max_len, err, ret; + +- if (td->ctrl & TD_CTRL_IOC) { +- *int_mask |= 0x01; +- } +- +- if (!(td->ctrl & TD_CTRL_ACTIVE)) +- return 1; ++ if (!(td->ctrl & TD_CTRL_ACTIVE)){ ++ ret = 1; ++ goto out; ++ } + + /* TD is active */ + max_len = ((td->token >> 21) + 1) & 0x7ff; +@@ -467,7 +465,8 @@ static int uhci_handle_td(UHCIState *s, + /* invalid pid : frame interrupted */ + s->status |= UHCI_STS_HCPERR; + uhci_update_irq(s); +- return -1; ++ ret = -1; ++ goto out; + } + if (td->ctrl & TD_CTRL_IOS) + td->ctrl &= ~TD_CTRL_ACTIVE; +@@ -479,10 +478,12 @@ static int uhci_handle_td(UHCIState *s, + len < max_len) { + *int_mask |= 0x02; + /* short packet: do not update QH */ +- return 1; ++ ret = 1; ++ goto out; + } else { + /* success */ +- return 0; ++ ret = 0; ++ goto out; + } + } else { + switch(ret) { +@@ -501,23 +502,34 @@ static int uhci_handle_td(UHCIState *s, + } + td->ctrl = (td->ctrl & ~(3 << TD_CTRL_ERROR_SHIFT)) | + (err << TD_CTRL_ERROR_SHIFT); +- return 1; ++ ret = 1; ++ goto out; + case USB_RET_NAK: + td->ctrl |= TD_CTRL_NAK; + if (pid == USB_TOKEN_SETUP) + goto do_timeout; +- return 1; ++ ret = 1; ++ goto out; + case USB_RET_STALL: + td->ctrl |= TD_CTRL_STALL; + td->ctrl &= ~TD_CTRL_ACTIVE; +- return 1; ++ ret = 1; ++ goto out; + case USB_RET_BABBLE: + td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL; + td->ctrl &= ~TD_CTRL_ACTIVE; + /* frame interrupted */ +- return -1; +- } +- } ++ ret = -1; ++ goto out; ++ } ++ } ++ ++out: ++ /* If TD is inactive and IOC bit set to 1 then update int_mask */ ++ if ((td->ctrl & TD_CTRL_IOC) && (!(td->ctrl & TD_CTRL_ACTIVE))) { ++ *int_mask |= 0x01; ++ } ++ return ret; + } + + static void uhci_frame_timer(void *opaque) diff --git a/tools/ioemu/patches/usb-uhci-buffer-size b/tools/ioemu/patches/usb-uhci-buffer-size new file mode 100644 index 0000000000..785692ff47 --- /dev/null +++ b/tools/ioemu/patches/usb-uhci-buffer-size @@ -0,0 +1,25 @@ +# HG changeset patch +# User kfraser@localhost.localdomain +# Node ID f19ddc0ee3e68d5d8a250ba0a20ab7d90ae9a36a +# Parent f66f7c3a82a7420d80714b0d349ee9a24b50ec28 +[QEMU] usb-uhci: Data buffer is too small + +The data buffer is only 1280 bytes long but the user-supplied length +can be as large as 0x7ff. This patch extends the buffer to 2048 +bytes. + +Signed-off-by: Herbert Xu + +Index: ioemu/hw/usb-uhci.c +=================================================================== +--- ioemu.orig/hw/usb-uhci.c 2006-12-08 18:21:36.000000000 +0000 ++++ ioemu/hw/usb-uhci.c 2006-12-08 18:23:06.000000000 +0000 +@@ -421,7 +421,7 @@ + static int uhci_handle_td(UHCIState *s, UHCI_TD *td, int *int_mask) + { + uint8_t pid; +- uint8_t buf[1280]; ++ uint8_t buf[2048]; + int len, max_len, err, ret; + + if (td->ctrl & TD_CTRL_IOC) { diff --git a/tools/ioemu/patches/vnc-access-monitor-vt b/tools/ioemu/patches/vnc-access-monitor-vt index 0320a978ca..4607815ea8 100644 --- a/tools/ioemu/patches/vnc-access-monitor-vt +++ b/tools/ioemu/patches/vnc-access-monitor-vt @@ -1,7 +1,7 @@ Index: ioemu/vnc.c =================================================================== ---- ioemu.orig/vnc.c 2006-10-24 14:33:46.000000000 +0100 -+++ ioemu/vnc.c 2006-10-24 14:33:46.000000000 +0100 +--- ioemu.orig/vnc.c 2006-12-06 23:46:11.000000000 +0000 ++++ ioemu/vnc.c 2006-12-06 23:46:11.000000000 +0000 @@ -33,6 +33,10 @@ #include "vnc_keysym.h" #include "keymaps.c" @@ -22,7 +22,7 @@ Index: ioemu/vnc.c }; #define DIRTY_PIXEL_BITS 64 -@@ -794,16 +800,80 @@ +@@ -796,16 +802,80 @@ static void do_key_event(VncState *vs, int down, uint32_t sym) { diff --git a/tools/ioemu/patches/vnc-backoff-screen-scan b/tools/ioemu/patches/vnc-backoff-screen-scan index 813a63ae48..172a265bc8 100644 --- a/tools/ioemu/patches/vnc-backoff-screen-scan +++ b/tools/ioemu/patches/vnc-backoff-screen-scan @@ -1,7 +1,7 @@ Index: ioemu/vnc.c =================================================================== ---- ioemu.orig/vnc.c 2006-10-24 14:33:17.000000000 +0100 -+++ ioemu/vnc.c 2006-10-24 14:33:24.000000000 +0100 +--- ioemu.orig/vnc.c 2006-12-06 23:46:12.000000000 +0000 ++++ ioemu/vnc.c 2006-12-06 23:46:12.000000000 +0000 @@ -28,7 +28,19 @@ #include "qemu_socket.h" #include @@ -45,7 +45,7 @@ Index: ioemu/vnc.c int ctl_keys; /* Ctrl+Alt starts calibration */ }; -@@ -381,7 +392,7 @@ +@@ -383,7 +394,7 @@ int y = 0; int pitch = ds->linesize; VncState *vs = ds->opaque; @@ -54,7 +54,7 @@ Index: ioemu/vnc.c if (src_x < vs->visible_x || src_y < vs->visible_y || dst_x < vs->visible_x || dst_y < vs->visible_y || -@@ -391,10 +402,8 @@ +@@ -393,10 +404,8 @@ (dst_y + h) > (vs->visible_y + vs->visible_h)) updating_client = 0; @@ -66,7 +66,7 @@ Index: ioemu/vnc.c if (dst_y > src_y) { y = h - 1; -@@ -446,110 +455,149 @@ +@@ -448,110 +457,149 @@ static void _vnc_update_client(void *opaque) { VncState *vs = opaque; @@ -299,7 +299,7 @@ Index: ioemu/vnc.c } static void vnc_update_client(void *opaque) -@@ -562,8 +610,10 @@ +@@ -564,8 +612,10 @@ static void vnc_timer_init(VncState *vs) { @@ -311,7 +311,7 @@ Index: ioemu/vnc.c } static void vnc_dpy_refresh(DisplayState *ds) -@@ -623,7 +673,6 @@ +@@ -625,7 +675,6 @@ vs->csock = -1; buffer_reset(&vs->input); buffer_reset(&vs->output); @@ -319,7 +319,7 @@ Index: ioemu/vnc.c return 0; } return ret; -@@ -895,7 +944,6 @@ +@@ -897,7 +946,6 @@ int x_position, int y_position, int w, int h) { @@ -327,7 +327,7 @@ Index: ioemu/vnc.c if (!incremental) framebuffer_set_updated(vs, x_position, y_position, w, h); vs->visible_x = x_position; -@@ -1018,6 +1066,7 @@ +@@ -1020,6 +1068,7 @@ { int i; uint16_t limit; @@ -335,7 +335,7 @@ Index: ioemu/vnc.c switch (data[0]) { case 0: -@@ -1061,12 +1110,18 @@ +@@ -1063,12 +1112,18 @@ if (len == 1) return 8; @@ -356,8 +356,8 @@ Index: ioemu/vnc.c case 6: Index: ioemu/vl.c =================================================================== ---- ioemu.orig/vl.c 2006-10-24 14:33:17.000000000 +0100 -+++ ioemu/vl.c 2006-10-24 14:33:24.000000000 +0100 +--- ioemu.orig/vl.c 2006-12-06 23:46:12.000000000 +0000 ++++ ioemu/vl.c 2006-12-06 23:46:12.000000000 +0000 @@ -726,6 +726,12 @@ } } @@ -373,8 +373,8 @@ Index: ioemu/vl.c void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time) Index: ioemu/vl.h =================================================================== ---- ioemu.orig/vl.h 2006-10-24 14:33:17.000000000 +0100 -+++ ioemu/vl.h 2006-10-24 14:33:24.000000000 +0100 +--- ioemu.orig/vl.h 2006-12-06 23:46:12.000000000 +0000 ++++ ioemu/vl.h 2006-12-06 23:46:12.000000000 +0000 @@ -407,6 +407,7 @@ void qemu_free_timer(QEMUTimer *ts); void qemu_del_timer(QEMUTimer *ts); diff --git a/tools/ioemu/patches/vnc-display-find-unused b/tools/ioemu/patches/vnc-display-find-unused index bb4f36a9da..2f7e509153 100644 --- a/tools/ioemu/patches/vnc-display-find-unused +++ b/tools/ioemu/patches/vnc-display-find-unused @@ -1,8 +1,8 @@ Index: ioemu/vnc.c =================================================================== ---- ioemu.orig/vnc.c 2006-10-24 14:31:09.000000000 +0100 -+++ ioemu/vnc.c 2006-10-24 14:31:36.000000000 +0100 -@@ -1195,7 +1195,7 @@ +--- ioemu.orig/vnc.c 2006-12-08 02:02:36.000000000 +0000 ++++ ioemu/vnc.c 2006-12-08 02:02:37.000000000 +0000 +@@ -1197,7 +1197,7 @@ } } @@ -11,7 +11,7 @@ Index: ioemu/vnc.c { struct sockaddr_in addr; int reuse_addr, ret; -@@ -1226,10 +1226,6 @@ +@@ -1228,10 +1228,6 @@ exit(1); } @@ -22,7 +22,7 @@ Index: ioemu/vnc.c reuse_addr = 1; ret = setsockopt(vs->lsock, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuse_addr, sizeof(reuse_addr)); -@@ -1238,7 +1234,16 @@ +@@ -1240,7 +1236,16 @@ exit(1); } @@ -39,7 +39,7 @@ Index: ioemu/vnc.c fprintf(stderr, "bind() failed\n"); exit(1); } -@@ -1259,6 +1264,8 @@ +@@ -1261,6 +1266,8 @@ vs->ds->dpy_refresh = vnc_dpy_refresh; vnc_dpy_resize(vs->ds, 640, 400); @@ -50,8 +50,8 @@ Index: ioemu/vnc.c int vnc_start_viewer(int port) Index: ioemu/vl.c =================================================================== ---- ioemu.orig/vl.c 2006-10-24 14:31:09.000000000 +0100 -+++ ioemu/vl.c 2006-10-24 14:31:41.000000000 +0100 +--- ioemu.orig/vl.c 2006-12-08 02:02:36.000000000 +0000 ++++ ioemu/vl.c 2006-12-08 02:02:37.000000000 +0000 @@ -121,6 +121,7 @@ static DisplayState display_state; int nographic; @@ -84,7 +84,7 @@ Index: ioemu/vl.c /* temporary options */ { "usb", 0, QEMU_OPTION_usb }, -@@ -5873,6 +5877,7 @@ +@@ -5857,6 +5861,7 @@ snapshot = 0; nographic = 0; vncviewer = 0; @@ -92,7 +92,7 @@ Index: ioemu/vl.c kernel_filename = NULL; kernel_cmdline = ""; #ifdef TARGET_PPC -@@ -6270,6 +6275,11 @@ +@@ -6254,6 +6259,11 @@ case QEMU_OPTION_vncviewer: vncviewer++; break; @@ -104,7 +104,7 @@ Index: ioemu/vl.c } } } -@@ -6483,7 +6493,7 @@ +@@ -6468,7 +6478,7 @@ if (nographic) { dumb_display_init(ds); } else if (vnc_display != -1) { @@ -115,8 +115,8 @@ Index: ioemu/vl.c } else { Index: ioemu/vl.h =================================================================== ---- ioemu.orig/vl.h 2006-10-24 14:31:09.000000000 +0100 -+++ ioemu/vl.h 2006-10-24 14:31:36.000000000 +0100 +--- ioemu.orig/vl.h 2006-12-08 02:02:36.000000000 +0000 ++++ ioemu/vl.h 2006-12-08 02:02:37.000000000 +0000 @@ -785,7 +785,7 @@ void cocoa_display_init(DisplayState *ds, int full_screen); diff --git a/tools/ioemu/patches/vnc-fixes b/tools/ioemu/patches/vnc-fixes index 7e08920961..0c847affa0 100644 --- a/tools/ioemu/patches/vnc-fixes +++ b/tools/ioemu/patches/vnc-fixes @@ -1,8 +1,8 @@ Index: ioemu/vl.c =================================================================== ---- ioemu.orig/vl.c 2006-10-24 13:47:23.000000000 +0100 -+++ ioemu/vl.c 2006-10-24 14:19:36.000000000 +0100 -@@ -6534,8 +6534,10 @@ +--- ioemu.orig/vl.c 2006-12-08 02:02:36.000000000 +0000 ++++ ioemu/vl.c 2006-12-08 02:02:36.000000000 +0000 +@@ -6519,8 +6519,10 @@ } } @@ -17,8 +17,8 @@ Index: ioemu/vl.c if (use_gdbstub) { Index: ioemu/vnc.c =================================================================== ---- ioemu.orig/vnc.c 2006-10-24 13:47:23.000000000 +0100 -+++ ioemu/vnc.c 2006-10-24 14:20:00.000000000 +0100 +--- ioemu.orig/vnc.c 2006-12-08 02:02:36.000000000 +0000 ++++ ioemu/vnc.c 2006-12-08 02:02:36.000000000 +0000 @@ -3,6 +3,7 @@ * * Copyright (C) 2006 Anthony Liguori @@ -92,7 +92,7 @@ Index: ioemu/vnc.c static inline void vnc_set_bit(uint32_t *d, int k) { d[k >> 5] |= 1 << (k & 0x1f); -@@ -139,20 +161,35 @@ +@@ -139,20 +161,37 @@ } return 0; } @@ -121,6 +121,8 @@ Index: ioemu/vnc.c mask = ~(0ULL); + h += y; ++ if (h > vs->ds->height) ++ h = vs->ds->height; for (; y < h; y++) - vs->dirty_row[y] |= mask; + row[y] |= mask; @@ -134,7 +136,7 @@ Index: ioemu/vnc.c } static void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h, -@@ -169,16 +206,23 @@ +@@ -169,16 +208,23 @@ static void vnc_dpy_resize(DisplayState *ds, int w, int h) { VncState *vs = ds->opaque; @@ -160,7 +162,7 @@ Index: ioemu/vnc.c ds->width = w; ds->height = h; ds->linesize = w * vs->depth; -@@ -191,6 +235,10 @@ +@@ -191,6 +237,10 @@ vs->width = ds->width; vs->height = ds->height; } @@ -171,7 +173,7 @@ Index: ioemu/vnc.c } /* fastest code */ -@@ -326,8 +374,20 @@ +@@ -326,8 +376,20 @@ int y = 0; int pitch = ds->linesize; VncState *vs = ds->opaque; @@ -193,7 +195,7 @@ Index: ioemu/vnc.c if (dst_y > src_y) { y = h - 1; -@@ -349,31 +409,34 @@ +@@ -349,31 +411,34 @@ old_row += pitch; } @@ -240,7 +242,7 @@ Index: ioemu/vnc.c { VncState *vs = opaque; int64_t now = qemu_get_clock(rt_clock); -@@ -382,14 +445,18 @@ +@@ -382,14 +447,18 @@ int y; char *row; char *old_row; @@ -262,7 +264,7 @@ Index: ioemu/vnc.c /* Walk through the dirty map and eliminate tiles that really aren't dirty */ -@@ -397,23 +464,25 @@ +@@ -397,23 +466,25 @@ old_row = vs->old_data; for (y = 0; y < vs->ds->height; y++) { @@ -297,7 +299,7 @@ Index: ioemu/vnc.c } } -@@ -421,7 +490,8 @@ +@@ -421,7 +492,8 @@ old_row += vs->ds->linesize; } @@ -307,7 +309,7 @@ Index: ioemu/vnc.c return; /* Count rectangles */ -@@ -431,34 +501,56 @@ +@@ -431,34 +503,56 @@ saved_offset = vs->output.offset; vnc_write_u16(vs, 0); @@ -375,7 +377,7 @@ Index: ioemu/vnc.c } static void vnc_timer_init(VncState *vs) -@@ -469,8 +561,6 @@ +@@ -469,8 +563,6 @@ static void vnc_dpy_refresh(DisplayState *ds) { @@ -384,7 +386,7 @@ Index: ioemu/vnc.c vga_hw_update(); } -@@ -506,7 +596,7 @@ +@@ -506,7 +598,7 @@ static void buffer_reset(Buffer *buffer) { @@ -393,7 +395,7 @@ Index: ioemu/vnc.c } static void buffer_append(Buffer *buffer, const void *data, size_t len) -@@ -547,12 +637,12 @@ +@@ -547,12 +639,12 @@ if (!ret) return; @@ -409,7 +411,7 @@ Index: ioemu/vnc.c } static void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting) -@@ -584,11 +674,11 @@ +@@ -584,11 +676,11 @@ return; if (!ret) { @@ -424,7 +426,7 @@ Index: ioemu/vnc.c } } -@@ -596,9 +686,9 @@ +@@ -596,9 +688,9 @@ { buffer_reserve(&vs->output, len); @@ -437,7 +439,7 @@ Index: ioemu/vnc.c buffer_append(&vs->output, data, len); } -@@ -720,22 +810,25 @@ +@@ -720,22 +812,25 @@ do_key_event(vs, down, sym); } @@ -474,7 +476,7 @@ Index: ioemu/vnc.c qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock)); } -@@ -843,8 +936,6 @@ +@@ -843,8 +938,6 @@ } vnc_dpy_resize(vs->ds, vs->ds->width, vs->ds->height); @@ -483,7 +485,7 @@ Index: ioemu/vnc.c vga_hw_invalidate(); vga_hw_update(); -@@ -924,6 +1015,8 @@ +@@ -924,6 +1017,8 @@ { char pad[3] = { 0, 0, 0 }; @@ -492,7 +494,7 @@ Index: ioemu/vnc.c vs->width = vs->ds->width; vs->height = vs->ds->height; vnc_write_u16(vs, vs->ds->width); -@@ -1010,11 +1103,11 @@ +@@ -1010,11 +1105,11 @@ vnc_write(vs, "RFB 003.003\n", 12); vnc_flush(vs); vnc_read_when(vs, protocol_version, 12); @@ -506,7 +508,7 @@ Index: ioemu/vnc.c } } -@@ -1071,17 +1164,15 @@ +@@ -1071,17 +1166,15 @@ exit(1); } @@ -529,8 +531,8 @@ Index: ioemu/vnc.c } Index: ioemu/vl.h =================================================================== ---- ioemu.orig/vl.h 2006-10-24 13:47:23.000000000 +0100 -+++ ioemu/vl.h 2006-10-24 14:19:36.000000000 +0100 +--- ioemu.orig/vl.h 2006-12-08 02:02:36.000000000 +0000 ++++ ioemu/vl.h 2006-12-08 02:02:36.000000000 +0000 @@ -319,6 +319,7 @@ int is_graphic_console(void); CharDriverState *text_console_init(DisplayState *ds); diff --git a/tools/ioemu/patches/vnc-japan-keymap b/tools/ioemu/patches/vnc-japan-keymap new file mode 100644 index 0000000000..86135a9b22 --- /dev/null +++ b/tools/ioemu/patches/vnc-japan-keymap @@ -0,0 +1,39 @@ +# HG changeset patch +# User kasai.takanori@jp.fujitsu.com +# Node ID ea1ffa51b4121d36cffdc90276378a6ed334c2cc +# Parent edd592c823a520d4072a95ac39beb2012c05321e +Add the Japanese keymap for VNC Server. + +Signed-off-by: Takanori Kasai < kasai.takanori@jp.fujitsu.com > + +Index: ioemu/keymaps/ja +=================================================================== +--- ioemu.orig/keymaps/ja 2006-12-08 18:21:36.000000000 +0000 ++++ ioemu/keymaps/ja 2006-12-08 18:21:56.000000000 +0000 +@@ -102,3 +102,6 @@ + Henkan_Mode 0x79 + Katakana 0x70 + Muhenkan 0x7b ++Henkan_Mode_Real 0x79 ++Henkan_Mode_Ultra 0x79 ++backslash_ja 0x73 +Index: ioemu/vnc_keysym.h +=================================================================== +--- ioemu.orig/vnc_keysym.h 2006-12-08 18:21:36.000000000 +0000 ++++ ioemu/vnc_keysym.h 2006-12-08 18:21:56.000000000 +0000 +@@ -271,5 +271,15 @@ + {"Num_Lock", 0xff7f}, /* XK_Num_Lock */ + {"Pause", 0xff13}, /* XK_Pause */ + {"Escape", 0xff1b}, /* XK_Escape */ ++ ++ /* localized keys */ ++{"BackApostrophe", 0xff21}, ++{"Muhenkan", 0xff22}, ++{"Katakana", 0xff25}, ++{"Zenkaku_Hankaku", 0xff29}, ++{"Henkan_Mode_Real", 0xff23}, ++{"Henkan_Mode_Ultra", 0xff3e}, ++{"backslash_ja", 0xffa5}, ++ + {0,0}, + }; diff --git a/tools/ioemu/patches/vnc-listen-specific-interface b/tools/ioemu/patches/vnc-listen-specific-interface index 3c42abb94c..a11a554a52 100644 --- a/tools/ioemu/patches/vnc-listen-specific-interface +++ b/tools/ioemu/patches/vnc-listen-specific-interface @@ -20,8 +20,8 @@ Signed-off-by: Daniel P. Berrange Index: ioemu/vl.c =================================================================== ---- ioemu.orig/vl.c 2006-10-24 14:33:46.000000000 +0100 -+++ ioemu/vl.c 2006-10-24 14:34:28.000000000 +0100 +--- ioemu.orig/vl.c 2006-12-08 02:02:37.000000000 +0000 ++++ ioemu/vl.c 2006-12-08 02:02:37.000000000 +0000 @@ -122,6 +122,7 @@ int nographic; int vncviewer; @@ -95,7 +95,7 @@ Index: ioemu/vl.c /* temporary options */ { "usb", 0, QEMU_OPTION_usb }, -@@ -5905,6 +5915,8 @@ +@@ -5889,6 +5899,8 @@ nb_nics = 0; /* default mac address of the first network interface */ @@ -103,8 +103,8 @@ Index: ioemu/vl.c + memset(&vnclisten_addr.sin_addr, 0, sizeof(vnclisten_addr.sin_addr)); /* init debug */ - sprintf(qemu_dm_logfilename, "/var/log/xen/qemu-dm.%d.log", getpid()); -@@ -6280,6 +6292,9 @@ + sprintf(qemu_dm_logfilename, "/var/log/xen/qemu-dm.%ld.log", (long)getpid()); +@@ -6264,6 +6276,9 @@ if (vnc_display == -1) vnc_display = 0; break; @@ -114,7 +114,7 @@ Index: ioemu/vl.c } } } -@@ -6493,7 +6508,7 @@ +@@ -6478,7 +6493,7 @@ if (nographic) { dumb_display_init(ds); } else if (vnc_display != -1) { @@ -125,8 +125,8 @@ Index: ioemu/vl.c } else { Index: ioemu/vl.h =================================================================== ---- ioemu.orig/vl.h 2006-10-24 14:33:46.000000000 +0100 -+++ ioemu/vl.h 2006-10-24 14:34:22.000000000 +0100 +--- ioemu.orig/vl.h 2006-12-08 02:02:37.000000000 +0000 ++++ ioemu/vl.h 2006-12-08 02:02:37.000000000 +0000 @@ -37,6 +37,8 @@ #include #include @@ -147,9 +147,9 @@ Index: ioemu/vl.h /* ide.c */ Index: ioemu/vnc.c =================================================================== ---- ioemu.orig/vnc.c 2006-10-24 14:33:46.000000000 +0100 -+++ ioemu/vnc.c 2006-10-24 14:34:22.000000000 +0100 -@@ -1195,9 +1195,8 @@ +--- ioemu.orig/vnc.c 2006-12-08 02:02:37.000000000 +0000 ++++ ioemu/vnc.c 2006-12-08 02:02:37.000000000 +0000 +@@ -1197,9 +1197,8 @@ } } @@ -160,7 +160,7 @@ Index: ioemu/vnc.c int reuse_addr, ret; VncState *vs; -@@ -1235,11 +1234,10 @@ +@@ -1237,11 +1236,10 @@ } retry: diff --git a/tools/ioemu/patches/vnc-monitor-shift-key-processing b/tools/ioemu/patches/vnc-monitor-shift-key-processing new file mode 100644 index 0000000000..afd164ad94 --- /dev/null +++ b/tools/ioemu/patches/vnc-monitor-shift-key-processing @@ -0,0 +1,60 @@ +# HG changeset patch +# User kfraser@localhost.localdomain +# Node ID 582d21e2d3cd12a13ad4debee9af8bb0f1be413a +# Parent b7095209e31ae1f52cd4b196225a360543e37a80 +[QEMU] Do shift-key processing in QEMU monitor terminal when connected via VNC. +Signed-off-by: Daniel P. Berrange + +Index: ioemu/vnc.c +=================================================================== +--- ioemu.orig/vnc.c 2006-12-08 18:21:36.000000000 +0000 ++++ ioemu/vnc.c 2006-12-08 18:23:12.000000000 +0000 +@@ -114,6 +114,7 @@ + int visible_h; + + int ctl_keys; /* Ctrl+Alt starts calibration */ ++ int shift_keys; /* Shift / CapsLock keys */ + }; + + #define DIRTY_PIXEL_BITS 64 +@@ -870,9 +871,12 @@ + } else if (down) { + int qemu_keysym = 0; + +- if (sym <= 128) /* normal ascii */ ++ if (sym <= 128) { /* normal ascii */ ++ int shifted = vs->shift_keys == 1 || vs->shift_keys == 2; + qemu_keysym = sym; +- else { ++ if (sym >= 'a' && sym <= 'z' && shifted) ++ qemu_keysym -= 'a' - 'A'; ++ } else { + switch (sym) { + case XK_Up: qemu_keysym = QEMU_KEY_UP; break; + case XK_Down: qemu_keysym = QEMU_KEY_DOWN; break; +@@ -903,6 +907,10 @@ + vs->ctl_keys |= 2; + break; + ++ case XK_Shift_L: ++ vs->shift_keys |= 1; ++ break; ++ + default: + break; + } +@@ -916,6 +924,14 @@ + vs->ctl_keys &= ~2; + break; + ++ case XK_Shift_L: ++ vs->shift_keys &= ~1; ++ break; ++ ++ case XK_Caps_Lock: ++ vs->shift_keys ^= 2; ++ break; ++ + case XK_1 ... XK_9: + if ((vs->ctl_keys & 3) != 3) + break; diff --git a/tools/ioemu/patches/vnc-numpad-handling b/tools/ioemu/patches/vnc-numpad-handling new file mode 100644 index 0000000000..d55de2da20 --- /dev/null +++ b/tools/ioemu/patches/vnc-numpad-handling @@ -0,0 +1,236 @@ +# HG changeset patch +# User Ewan Mellor +# Node ID c7f4a89eb054a1ad411da1e4cdc8aeda1a98c4fa +# Parent 565cd8f32c70da8ae7dbaaeb9dff28aa8b6307e1 +Fix numpad handling in QEMU's VNC server. The keymaps that we have include +information on which keys change depending upon the numlock setting, but +this isn't being used. By forcing numlock on and off as necessary, when +receiving these keysyms through the VNC connection, we ensure that the +server's numlock status is the same as the client's. + +Signed-off-by: Ewan Mellor + +Index: ioemu/keymaps.c +=================================================================== +--- ioemu.orig/keymaps.c 2006-12-06 23:41:30.000000000 +0000 ++++ ioemu/keymaps.c 2006-12-08 18:20:27.000000000 +0000 +@@ -36,8 +36,10 @@ + #define MAX_EXTRA_COUNT 256 + typedef struct { + uint16_t keysym2keycode[MAX_NORMAL_KEYCODE]; ++ int keysym2numlock[MAX_NORMAL_KEYCODE]; + struct { + int keysym; ++ int numlock; + uint16_t keycode; + } keysym2keycode_extra[MAX_EXTRA_COUNT]; + int extra_count; +@@ -50,6 +52,8 @@ + char file_name[1024]; + char line[1024]; + int len; ++ int *keycode2numlock; ++ int i; + + snprintf(file_name, sizeof(file_name), + "%s/keymaps/%s", bios_dir, language); +@@ -63,6 +67,15 @@ + "Could not read keymap file: '%s'\n", file_name); + return 0; + } ++ ++ /* Allocate a temporary map tracking which keycodes change when numlock is ++ set. Keycodes are 16 bit, so 65536 is safe. */ ++ keycode2numlock = malloc(65536 * sizeof(int)); ++ if (!keycode2numlock) { ++ perror("Could not read keymap file"); ++ return 0; ++ } ++ + for(;;) { + if (fgets(line, 1024, f) == NULL) + break; +@@ -86,13 +99,19 @@ + if (keysym == 0) { + // fprintf(stderr, "Warning: unknown keysym %s\n", line); + } else { +- const char *rest = end_of_keysym + 1; +- int keycode = strtol(rest, NULL, 0); ++ char *rest = end_of_keysym + 1; ++ int keycode = strtol(rest, &rest, 0); ++ int numlock = (rest != NULL && ++ strstr(rest, "numlock") != NULL); ++ ++ keycode2numlock[keycode] = numlock; ++ + /* if(keycode&0x80) + keycode=(keycode<<8)^0x80e0; */ + if (keysym < MAX_NORMAL_KEYCODE) { + //fprintf(stderr,"Setting keysym %s (%d) to %d\n",line,keysym,keycode); + k->keysym2keycode[keysym] = keycode; ++ k->keysym2numlock[keysym] = numlock; + } else { + if (k->extra_count >= MAX_EXTRA_COUNT) { + fprintf(stderr, +@@ -107,6 +126,8 @@ + keysym = keysym; + k->keysym2keycode_extra[k->extra_count]. + keycode = keycode; ++ k->keysym2keycode_extra[k->extra_count]. ++ numlock = numlock; + k->extra_count++; + } + } +@@ -115,6 +136,22 @@ + } + } + fclose(f); ++ ++ for (i = 0; i < MAX_NORMAL_KEYCODE; i++) { ++ if (k->keysym2numlock[i] != 1) { ++ k->keysym2numlock[i] = -keycode2numlock[k->keysym2keycode[i]]; ++ } ++ } ++ ++ for (i = 0; i < k->extra_count; i++) { ++ if (k->keysym2keycode_extra[i].numlock != 1) { ++ k->keysym2keycode_extra[i].numlock = ++ -keycode2numlock[k->keysym2keycode_extra[i].keycode]; ++ } ++ } ++ ++ free(keycode2numlock); ++ + return k; + } + +@@ -143,3 +180,25 @@ + } + return 0; + } ++ ++/** ++ * Returns 1 if the given keysym requires numlock to be pressed, -1 if it ++ * requires it to be cleared, and 0 otherwise. ++ */ ++static int keysym2numlock(void *kbd_layout, int keysym) ++{ ++ kbd_layout_t *k = kbd_layout; ++ if (keysym < MAX_NORMAL_KEYCODE) { ++ return k->keysym2numlock[keysym]; ++ } else { ++ int i; ++#ifdef XK_ISO_Left_Tab ++ if (keysym == XK_ISO_Left_Tab) ++ keysym = XK_Tab; ++#endif ++ for (i = 0; i < k->extra_count; i++) ++ if (k->keysym2keycode_extra[i].keysym == keysym) ++ return k->keysym2keycode_extra[i].numlock; ++ } ++ return 0; ++} +Index: ioemu/vnc.c +=================================================================== +--- ioemu.orig/vnc.c 2006-12-08 18:18:26.000000000 +0000 ++++ ioemu/vnc.c 2006-12-08 18:19:43.000000000 +0000 +@@ -115,6 +115,7 @@ + + int ctl_keys; /* Ctrl+Alt starts calibration */ + int shift_keys; /* Shift / CapsLock keys */ ++ int numlock; + }; + + #define DIRTY_PIXEL_BITS 64 +@@ -854,14 +855,40 @@ + } + } + ++static void press_key(VncState *vs, int keycode) ++{ ++ kbd_put_keycode(keysym2scancode(vs->kbd_layout, keycode) & 0x7f); ++ kbd_put_keycode(keysym2scancode(vs->kbd_layout, keycode) | 0x80); ++} ++ + static void do_key_event(VncState *vs, int down, uint32_t sym) + { + sym &= 0xFFFF; + + if (is_graphic_console()) { + int keycode; ++ int numlock; + + keycode = keysym2scancode(vs->kbd_layout, sym); ++ numlock = keysym2numlock(vs->kbd_layout, sym); ++ ++ /* If the numlock state needs to change then simulate an additional ++ keypress before sending this one. This will happen if the user ++ toggles numlock away from the VNC window. ++ */ ++ if (numlock == 1) { ++ if (!vs->numlock) { ++ vs->numlock = 1; ++ press_key(vs, XK_Num_Lock); ++ } ++ } ++ else if (numlock == -1) { ++ if (vs->numlock) { ++ vs->numlock = 0; ++ press_key(vs, XK_Num_Lock); ++ } ++ } ++ + if (keycode & 0x80) + kbd_put_keycode(0xe0); + if (down) +@@ -932,6 +959,10 @@ + vs->shift_keys ^= 2; + break; + ++ case XK_Num_Lock: ++ vs->numlock = !vs->numlock; ++ break; ++ + case XK_1 ... XK_9: + if ((vs->ctl_keys & 3) != 3) + break; +@@ -1355,6 +1386,7 @@ + vs->lsock = -1; + vs->csock = -1; + vs->depth = 4; ++ vs->numlock = 0; + + vs->ds = ds; + +Index: ioemu/vnc_keysym.h +=================================================================== +--- ioemu.orig/vnc_keysym.h 2006-12-08 18:17:01.000000000 +0000 ++++ ioemu/vnc_keysym.h 2006-12-08 18:19:43.000000000 +0000 +@@ -231,6 +231,19 @@ + {"Home", 0xff50}, /* XK_Home */ + {"End", 0xff57}, /* XK_End */ + {"Scroll_Lock", 0xff14}, /* XK_Scroll_Lock */ ++{"KP_Home", 0xff95}, ++{"KP_Left", 0xff96}, ++{"KP_Up", 0xff97}, ++{"KP_Right", 0xff98}, ++{"KP_Down", 0xff99}, ++{"KP_Prior", 0xff9a}, ++{"KP_Page_Up", 0xff9a}, ++{"KP_Next", 0xff9b}, ++{"KP_Page_Down", 0xff9b}, ++{"KP_End", 0xff9c}, ++{"KP_Begin", 0xff9d}, ++{"KP_Insert", 0xff9e}, ++{"KP_Delete", 0xff9f}, + {"F1", 0xffbe}, /* XK_F1 */ + {"F2", 0xffbf}, /* XK_F2 */ + {"F3", 0xffc0}, /* XK_F3 */ +@@ -258,6 +271,7 @@ + {"KP_8", 0xffb8}, /* XK_KP_8 */ + {"KP_9", 0xffb9}, /* XK_KP_9 */ + {"KP_Add", 0xffab}, /* XK_KP_Add */ ++{"KP_Separator", 0xffac},/* XK_KP_Separator */ + {"KP_Decimal", 0xffae}, /* XK_KP_Decimal */ + {"KP_Divide", 0xffaf}, /* XK_KP_Divide */ + {"KP_Enter", 0xff8d}, /* XK_KP_Enter */ diff --git a/tools/ioemu/patches/vnc-password b/tools/ioemu/patches/vnc-password index 49c5d73b47..1a923ddfc0 100644 --- a/tools/ioemu/patches/vnc-password +++ b/tools/ioemu/patches/vnc-password @@ -15,9 +15,11 @@ The difference is follows. Signed-off-by: Masami Watanabe ---- ioemu/Makefile.target Fri Oct 20 09:32:16 2006 +0100 -+++ ioemu/Makefile.target Fri Oct 20 09:50:09 2006 +0100 -@@ -406,6 +406,7 @@ VL_OBJS+=sdl.o +Index: ioemu/Makefile.target +=================================================================== +--- ioemu.orig/Makefile.target 2006-12-08 18:20:53.000000000 +0000 ++++ ioemu/Makefile.target 2006-12-08 18:20:53.000000000 +0000 +@@ -407,6 +407,7 @@ VL_OBJS+=sdl.o endif VL_OBJS+=vnc.o @@ -25,29 +27,31 @@ Signed-off-by: Masami Watanabe ifdef CONFIG_COCOA VL_OBJS+=cocoa.o COCOA_LIBS=-F/System/Library/Frameworks -framework Cocoa -framework IOKit -@@ -464,6 +465,9 @@ sdl.o: sdl.c keymaps.c sdl_keysym.h - $(CC) $(CFLAGS) $(DEFINES) $(SDL_CFLAGS) -c -o $@ $< - +@@ -467,6 +468,9 @@ vnc.o: vnc.c keymaps.c sdl_keysym.h vnchextile.h -+ $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< -+ -+d3des.o: d3des.c d3des.h $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< ++d3des.o: d3des.c d3des.h ++ $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< ++ sdlaudio.o: sdlaudio.c ---- ioemu/vl.c Fri Oct 20 09:32:16 2006 +0100 -+++ ioemu/vl.c Fri Oct 20 09:50:09 2006 +0100 -@@ -170,6 +170,9 @@ time_t timeoffset = 0; + $(CC) $(CFLAGS) $(DEFINES) $(SDL_CFLAGS) -c -o $@ $< +Index: ioemu/vl.c +=================================================================== +--- ioemu.orig/vl.c 2006-12-08 18:20:52.000000000 +0000 ++++ ioemu/vl.c 2006-12-08 18:20:53.000000000 +0000 +@@ -171,6 +171,9 @@ char domain_name[1024] = { 'H','V', 'M', 'X', 'E', 'N', '-'}; extern int domid; -+ + +char vncpasswd[64]; +unsigned char challenge[AUTHCHALLENGESIZE]; - ++ /***********************************************************/ /* x86 ISA bus support */ -@@ -5911,6 +5914,7 @@ int main(int argc, char **argv) + +@@ -5895,6 +5898,7 @@ vncunused = 0; kernel_filename = NULL; kernel_cmdline = ""; @@ -55,7 +59,7 @@ Signed-off-by: Masami Watanabe #ifndef CONFIG_DM #ifdef TARGET_PPC cdrom_index = 1; -@@ -6559,6 +6563,10 @@ int main(int argc, char **argv) +@@ -6543,6 +6547,10 @@ init_ioports(); @@ -66,9 +70,11 @@ Signed-off-by: Masami Watanabe /* terminal init */ if (nographic) { dumb_display_init(ds); ---- ioemu/vl.h Fri Oct 20 09:32:16 2006 +0100 -+++ ioemu/vl.h Fri Oct 20 09:50:09 2006 +0100 -@@ -1211,6 +1211,7 @@ void xenstore_process_event(void *opaque +Index: ioemu/vl.h +=================================================================== +--- ioemu.orig/vl.h 2006-12-08 18:20:52.000000000 +0000 ++++ ioemu/vl.h 2006-12-08 18:20:53.000000000 +0000 +@@ -1214,6 +1214,7 @@ void xenstore_process_event(void *opaque); void xenstore_check_new_media_present(int timeout); void xenstore_write_vncport(int vnc_display); @@ -76,7 +82,7 @@ Signed-off-by: Masami Watanabe /* xen_platform.c */ void pci_xen_platform_init(PCIBus *bus); -@@ -1222,4 +1223,7 @@ extern char domain_name[]; +@@ -1225,4 +1226,7 @@ void destroy_hvm_domain(void); @@ -84,8 +90,10 @@ Signed-off-by: Masami Watanabe +#define AUTHCHALLENGESIZE 16 + #endif /* VL_H */ ---- ioemu/vnc.c Fri Oct 20 09:32:16 2006 +0100 -+++ ioemu/vnc.c Fri Oct 20 09:50:09 2006 +0100 +Index: ioemu/vnc.c +=================================================================== +--- ioemu.orig/vnc.c 2006-12-08 18:20:52.000000000 +0000 ++++ ioemu/vnc.c 2006-12-08 18:20:53.000000000 +0000 @@ -44,6 +44,7 @@ #include "vnc_keysym.h" @@ -94,7 +102,7 @@ Signed-off-by: Masami Watanabe #define XK_MISCELLANY #define XK_LATIN1 -@@ -137,6 +138,9 @@ static void vnc_update_client(void *opaq +@@ -137,6 +138,9 @@ static void vnc_update_client(void *opaque); static void vnc_client_read(void *opaque); static void framebuffer_set_updated(VncState *vs, int x, int y, int w, int h); @@ -104,7 +112,7 @@ Signed-off-by: Masami Watanabe #if 0 static inline void vnc_set_bit(uint32_t *d, int k) -@@ -1208,23 +1212,92 @@ static int protocol_client_init(VncState +@@ -1210,23 +1214,92 @@ return 0; } @@ -166,9 +174,8 @@ Signed-off-by: Masami Watanabe - vnc_write_u32(vs, 1); /* None */ - vnc_flush(vs); -- + - vnc_read_when(vs, protocol_client_init, 1); -+ + support = 0; + if (maj = 3) { + if (min == 3 || min ==4) { @@ -202,7 +209,7 @@ Signed-off-by: Masami Watanabe return 0; } -@@ -1342,3 +1415,32 @@ int vnc_start_viewer(int port) +@@ -1344,3 +1417,32 @@ return pid; } } @@ -235,9 +242,11 @@ Signed-off-by: Masami Watanabe + + return; +} ---- ioemu/xenstore.c Fri Oct 20 09:32:16 2006 +0100 -+++ ioemu/xenstore.c Fri Oct 20 09:50:09 2006 +0100 -@@ -213,3 +213,54 @@ void xenstore_write_vncport(int display) +Index: ioemu/xenstore.c +=================================================================== +--- ioemu.orig/xenstore.c 2006-12-08 18:20:52.000000000 +0000 ++++ ioemu/xenstore.c 2006-12-08 18:20:53.000000000 +0000 +@@ -213,3 +213,54 @@ free(portstr); free(buf); } @@ -292,8 +301,10 @@ Signed-off-by: Masami Watanabe + + return rc; +} ---- /dev/null Thu Jan 01 00:00:00 1970 +0000 -+++ ioemu/d3des.c Fri Oct 20 09:50:09 2006 +0100 +Index: ioemu/d3des.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ ioemu/d3des.c 2006-12-08 18:20:53.000000000 +0000 @@ -0,0 +1,434 @@ +/* + * This is D3DES (V5.09) by Richard Outerbridge with the double and @@ -729,8 +740,10 @@ Signed-off-by: Masami Watanabe + * + * d3des V5.0a rwo 9208.07 18:44 Graven Imagery + **********************************************************************/ ---- /dev/null Thu Jan 01 00:00:00 1970 +0000 -+++ ioemu/d3des.h Fri Oct 20 09:50:09 2006 +0100 +Index: ioemu/d3des.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ ioemu/d3des.h 2006-12-08 18:20:53.000000000 +0000 @@ -0,0 +1,51 @@ +/* + * This is D3DES (V5.09) by Richard Outerbridge with the double and diff --git a/tools/ioemu/patches/vnc-protocol-fixes b/tools/ioemu/patches/vnc-protocol-fixes index 84e53614da..7eaa4dfc4b 100644 --- a/tools/ioemu/patches/vnc-protocol-fixes +++ b/tools/ioemu/patches/vnc-protocol-fixes @@ -9,8 +9,8 @@ Signed-off-by: Steven Smith Index: ioemu/vnc.c =================================================================== ---- ioemu.orig/vnc.c 2006-10-24 14:28:05.000000000 +0100 -+++ ioemu/vnc.c 2006-10-24 14:30:11.000000000 +0100 +--- ioemu.orig/vnc.c 2006-12-06 23:46:11.000000000 +0000 ++++ ioemu/vnc.c 2006-12-06 23:46:11.000000000 +0000 @@ -26,6 +26,7 @@ #include "vl.h" @@ -19,7 +19,7 @@ Index: ioemu/vnc.c #define VNC_REFRESH_INTERVAL (1000 / 30) -@@ -677,8 +678,10 @@ +@@ -679,8 +680,10 @@ memmove(vs->input.buffer, vs->input.buffer + len, vs->input.offset - len); vs->input.offset -= len; @@ -31,7 +31,7 @@ Index: ioemu/vnc.c } } -@@ -961,8 +964,12 @@ +@@ -963,8 +966,12 @@ if (len == 1) return 4; @@ -46,7 +46,7 @@ Index: ioemu/vnc.c limit = read_u16(data, 2); for (i = 0; i < limit; i++) { -@@ -996,8 +1003,12 @@ +@@ -998,8 +1005,12 @@ if (len == 1) return 8; diff --git a/tools/ioemu/patches/vnc-start-vncviewer b/tools/ioemu/patches/vnc-start-vncviewer index 95080b9995..e897b09c02 100644 --- a/tools/ioemu/patches/vnc-start-vncviewer +++ b/tools/ioemu/patches/vnc-start-vncviewer @@ -1,8 +1,8 @@ Index: ioemu/vnc.c =================================================================== ---- ioemu.orig/vnc.c 2006-10-24 14:33:46.000000000 +0100 -+++ ioemu/vnc.c 2006-10-24 14:33:46.000000000 +0100 -@@ -1187,3 +1187,25 @@ +--- ioemu.orig/vnc.c 2006-12-08 02:02:36.000000000 +0000 ++++ ioemu/vnc.c 2006-12-08 02:02:36.000000000 +0000 +@@ -1189,3 +1189,25 @@ vnc_dpy_resize(vs->ds, 640, 400); } @@ -30,8 +30,8 @@ Index: ioemu/vnc.c +} Index: ioemu/vl.c =================================================================== ---- ioemu.orig/vl.c 2006-10-24 14:33:46.000000000 +0100 -+++ ioemu/vl.c 2006-10-24 14:33:46.000000000 +0100 +--- ioemu.orig/vl.c 2006-12-08 02:02:36.000000000 +0000 ++++ ioemu/vl.c 2006-12-08 02:02:36.000000000 +0000 @@ -120,6 +120,7 @@ int bios_size; static DisplayState display_state; @@ -64,7 +64,7 @@ Index: ioemu/vl.c /* temporary options */ { "usb", 0, QEMU_OPTION_usb }, -@@ -5868,6 +5872,7 @@ +@@ -5852,6 +5856,7 @@ #endif snapshot = 0; nographic = 0; @@ -72,7 +72,7 @@ Index: ioemu/vl.c kernel_filename = NULL; kernel_cmdline = ""; #ifdef TARGET_PPC -@@ -6262,6 +6267,9 @@ +@@ -6246,6 +6251,9 @@ case QEMU_OPTION_acpi: acpi_enabled = 1; break; @@ -82,7 +82,7 @@ Index: ioemu/vl.c } } } -@@ -6476,6 +6484,8 @@ +@@ -6461,6 +6469,8 @@ dumb_display_init(ds); } else if (vnc_display != -1) { vnc_display_init(ds, vnc_display); @@ -93,8 +93,8 @@ Index: ioemu/vl.c sdl_display_init(ds, full_screen); Index: ioemu/vl.h =================================================================== ---- ioemu.orig/vl.h 2006-10-24 14:33:46.000000000 +0100 -+++ ioemu/vl.h 2006-10-24 14:33:46.000000000 +0100 +--- ioemu.orig/vl.h 2006-12-08 02:02:36.000000000 +0000 ++++ ioemu/vl.h 2006-12-08 02:02:36.000000000 +0000 @@ -786,6 +786,7 @@ /* vnc.c */ diff --git a/tools/ioemu/patches/vnc-title-domain-name b/tools/ioemu/patches/vnc-title-domain-name index 2ba1489cf9..0c837147ad 100644 --- a/tools/ioemu/patches/vnc-title-domain-name +++ b/tools/ioemu/patches/vnc-title-domain-name @@ -1,8 +1,8 @@ Index: ioemu/vnc.c =================================================================== ---- ioemu.orig/vnc.c 2006-10-24 14:33:46.000000000 +0100 -+++ ioemu/vnc.c 2006-10-24 14:33:46.000000000 +0100 -@@ -1024,6 +1024,7 @@ +--- ioemu.orig/vnc.c 2006-12-06 23:46:11.000000000 +0000 ++++ ioemu/vnc.c 2006-12-06 23:46:11.000000000 +0000 +@@ -1026,6 +1026,7 @@ static int protocol_client_init(VncState *vs, char *data, size_t len) { @@ -10,7 +10,7 @@ Index: ioemu/vnc.c char pad[3] = { 0, 0, 0 }; vga_hw_update(); -@@ -1071,8 +1072,10 @@ +@@ -1073,8 +1074,10 @@ vnc_write(vs, pad, 3); /* padding */ diff --git a/tools/ioemu/patches/xen-build b/tools/ioemu/patches/xen-build index 8aae60d005..016b37771b 100644 --- a/tools/ioemu/patches/xen-build +++ b/tools/ioemu/patches/xen-build @@ -1,7 +1,7 @@ Index: ioemu/Makefile =================================================================== ---- ioemu.orig/Makefile 2006-10-24 14:37:25.000000000 +0100 -+++ ioemu/Makefile 2006-10-24 14:37:28.000000000 +0100 +--- ioemu.orig/Makefile 2006-12-08 01:26:04.000000000 +0000 ++++ ioemu/Makefile 2006-12-08 01:26:06.000000000 +0000 @@ -1,11 +1,14 @@ # Makefile for QEMU. @@ -85,8 +85,8 @@ Index: ioemu/Makefile info: qemu-doc.info qemu-tech.info Index: ioemu/Makefile.target =================================================================== ---- ioemu.orig/Makefile.target 2006-10-24 14:37:25.000000000 +0100 -+++ ioemu/Makefile.target 2006-10-24 14:40:25.000000000 +0100 +--- ioemu.orig/Makefile.target 2006-12-08 01:26:04.000000000 +0000 ++++ ioemu/Makefile.target 2006-12-08 01:41:05.000000000 +0000 @@ -1,5 +1,8 @@ include config.mak @@ -120,9 +120,13 @@ Index: ioemu/Makefile.target #CFLAGS+=-Werror LDFLAGS=-g LIBS= -@@ -167,6 +177,9 @@ +@@ -165,8 +175,12 @@ - DEFINES+=-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE + ######################################################### + +-DEFINES+=-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE ++DEFINES+=-D_GNU_SOURCE ++#-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE LIBS+=-lm +LIBS+=-L../../libxc -lxenctrl -lxenguest +LIBS+=-L../../xenstore -lxenstore @@ -130,7 +134,7 @@ Index: ioemu/Makefile.target ifndef CONFIG_USER_ONLY LIBS+=-lz endif -@@ -281,7 +294,7 @@ +@@ -281,7 +295,7 @@ all: $(PROGS) $(QEMU_USER): $(OBJS) @@ -139,7 +143,7 @@ Index: ioemu/Makefile.target ifeq ($(ARCH),alpha) # Mark as 32 bit binary, i. e. it will be mapped into the low 31 bit of # the address space (31 bit so sign extending doesn't matter) -@@ -528,10 +541,16 @@ +@@ -528,10 +542,16 @@ clean: rm -f *.o *.a *~ $(PROGS) gen-op.h opc.h op.h nwfpe/*.o slirp/*.o fpu/*.o @@ -159,8 +163,8 @@ Index: ioemu/Makefile.target include .depend Index: ioemu/configure =================================================================== ---- ioemu.orig/configure 2006-10-24 14:37:25.000000000 +0100 -+++ ioemu/configure 2006-10-24 14:40:20.000000000 +0100 +--- ioemu.orig/configure 2006-12-08 01:26:04.000000000 +0000 ++++ ioemu/configure 2006-12-08 01:40:58.000000000 +0000 @@ -18,8 +18,8 @@ # default parameters diff --git a/tools/ioemu/patches/xen-mapcache b/tools/ioemu/patches/xen-mapcache new file mode 100644 index 0000000000..dc37d662a7 --- /dev/null +++ b/tools/ioemu/patches/xen-mapcache @@ -0,0 +1,300 @@ +# HG changeset patch +# User kfraser@localhost.localdomain +# Node ID 67a06a9b7b1dca707e1cd3b08ae0a341d6e97b3d +# Parent 3f0ca90351e268084fbdb733d70fc596cb46537d +[HVM] qemu: Add guest address-space mapping cache. + +On IA32 host or IA32 PAE host, at present, generally, we can't create +an HVM guest with more than 2G memory, because generally it's almost +impossible for Qemu to find a large enough and consecutive virtual +address space to map an HVM guest's whole physical address space. +The attached patch fixes this issue using dynamic mapping based on +little blocks of memory. + +Signed-off-by: Jun Nakajima +Signed-off-by: Dexuan Cui +Signed-off-by: Keir Fraser + +diff -r 3f0ca90351e2 -r 67a06a9b7b1d tools/ioemu/target-i386-dm/cpu.h +--- a/tools/ioemu/target-i386-dm/cpu.h Thu Dec 07 10:54:43 2006 +0000 ++++ b/tools/ioemu/target-i386-dm/cpu.h Thu Dec 07 11:12:52 2006 +0000 +@@ -25,7 +25,8 @@ + #ifdef TARGET_X86_64 + #define TARGET_LONG_BITS 64 + #else +-#define TARGET_LONG_BITS 32 ++/* #define TARGET_LONG_BITS 32 */ ++#define TARGET_LONG_BITS 64 /* for Qemu map cache */ + #endif + + /* target supports implicit self modifying code */ +diff -r 3f0ca90351e2 -r 67a06a9b7b1d tools/ioemu/target-i386-dm/exec-dm.c +--- a/tools/ioemu/target-i386-dm/exec-dm.c Thu Dec 07 10:54:43 2006 +0000 ++++ b/tools/ioemu/target-i386-dm/exec-dm.c Thu Dec 07 11:12:52 2006 +0000 +@@ -36,6 +36,7 @@ + + #include "cpu.h" + #include "exec-all.h" ++#include "vl.h" + + //#define DEBUG_TB_INVALIDATE + //#define DEBUG_FLUSH +@@ -426,6 +427,12 @@ static inline int paddr_is_ram(target_ph + #endif + } + ++#if defined(__i386__) || defined(__x86_64__) ++#define phys_ram_addr(x) (qemu_map_cache(x)) ++#elif defined(__ia64__) ++#define phys_ram_addr(x) (phys_ram_base + (x)) ++#endif ++ + void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, + int len, int is_write) + { +@@ -438,7 +445,7 @@ void cpu_physical_memory_rw(target_phys_ + l = TARGET_PAGE_SIZE - (addr & ~TARGET_PAGE_MASK); + if (l > len) + l = len; +- ++ + io_index = iomem_index(addr); + if (is_write) { + if (io_index) { +@@ -460,9 +467,10 @@ void cpu_physical_memory_rw(target_phys_ + } + } else if (paddr_is_ram(addr)) { + /* Reading from RAM */ +- memcpy(phys_ram_base + addr, buf, l); ++ ptr = phys_ram_addr(addr); ++ memcpy(ptr, buf, l); + #ifdef __ia64__ +- sync_icache((unsigned long)(phys_ram_base + addr), l); ++ sync_icache(ptr, l); + #endif + } + } else { +@@ -485,7 +493,8 @@ void cpu_physical_memory_rw(target_phys_ + } + } else if (paddr_is_ram(addr)) { + /* Reading from RAM */ +- memcpy(buf, phys_ram_base + addr, l); ++ ptr = phys_ram_addr(addr); ++ memcpy(buf, ptr, l); + } else { + /* Neither RAM nor known MMIO space */ + memset(buf, 0xff, len); +diff -r 3f0ca90351e2 -r 67a06a9b7b1d tools/ioemu/vl.c +--- a/tools/ioemu/vl.c Thu Dec 07 10:54:43 2006 +0000 ++++ b/tools/ioemu/vl.c Thu Dec 07 11:12:52 2006 +0000 +@@ -5807,6 +5807,92 @@ int set_mm_mapping(int xc_handle, uint32 + + return 0; + } ++ ++#if defined(__i386__) || defined(__x86_64__) ++static struct map_cache *mapcache_entry; ++static unsigned long nr_buckets; ++ ++static int qemu_map_cache_init(unsigned long nr_pages) ++{ ++ unsigned long max_pages = MAX_MCACHE_SIZE >> PAGE_SHIFT; ++ int i; ++ ++ if (nr_pages < max_pages) ++ max_pages = nr_pages; ++ ++ nr_buckets = (max_pages << PAGE_SHIFT) >> MCACHE_BUCKET_SHIFT; ++ ++ fprintf(logfile, "qemu_map_cache_init nr_buckets = %lx\n", nr_buckets); ++ ++ mapcache_entry = malloc(nr_buckets * sizeof(struct map_cache)); ++ if (mapcache_entry == NULL) { ++ errno = ENOMEM; ++ return -1; ++ } ++ ++ memset(mapcache_entry, 0, nr_buckets * sizeof(struct map_cache)); ++ ++ /* ++ * To avoid ENOMEM from xc_map_foreign_batch() at runtime, we ++ * pre-fill all the map caches in advance. ++ */ ++ for (i = 0; i < nr_buckets; i++) ++ (void)qemu_map_cache(((target_phys_addr_t)i) << MCACHE_BUCKET_SHIFT); ++ ++ return 0; ++} ++ ++uint8_t *qemu_map_cache(target_phys_addr_t phys_addr) ++{ ++ struct map_cache *entry; ++ unsigned long address_index = phys_addr >> MCACHE_BUCKET_SHIFT; ++ unsigned long address_offset = phys_addr & (MCACHE_BUCKET_SIZE-1); ++ ++ /* For most cases (>99.9%), the page address is the same. */ ++ static unsigned long last_address_index = ~0UL; ++ static uint8_t *last_address_vaddr; ++ ++ if (address_index == last_address_index) ++ return last_address_vaddr + address_offset; ++ ++ entry = &mapcache_entry[address_index % nr_buckets]; ++ ++ if (entry->vaddr_base == NULL || entry->paddr_index != address_index) ++ { ++ /* We need to remap a bucket. */ ++ uint8_t *vaddr_base; ++ unsigned long pfns[MCACHE_BUCKET_SIZE >> PAGE_SHIFT]; ++ unsigned int i; ++ ++ if (entry->vaddr_base != NULL) { ++ errno = munmap(entry->vaddr_base, MCACHE_BUCKET_SIZE); ++ if (errno) { ++ fprintf(logfile, "unmap fails %d\n", errno); ++ exit(-1); ++ } ++ } ++ ++ for (i = 0; i < MCACHE_BUCKET_SIZE >> PAGE_SHIFT; i++) ++ pfns[i] = (address_index << (MCACHE_BUCKET_SHIFT-PAGE_SHIFT)) + i; ++ ++ vaddr_base = xc_map_foreign_batch( ++ xc_handle, domid, PROT_READ|PROT_WRITE, ++ pfns, MCACHE_BUCKET_SIZE >> PAGE_SHIFT); ++ if (vaddr_base == NULL) { ++ fprintf(logfile, "xc_map_foreign_batch error %d\n", errno); ++ exit(-1); ++ } ++ ++ entry->vaddr_base = vaddr_base; ++ entry->paddr_index = address_index;; ++ } ++ ++ last_address_index = address_index; ++ last_address_vaddr = entry->vaddr_base; ++ ++ return last_address_vaddr + address_offset; ++} ++#endif + + int main(int argc, char **argv) + { +@@ -6130,6 +6216,7 @@ int main(int argc, char **argv) + break; + case QEMU_OPTION_m: + ram_size = atol(optarg) * 1024 * 1024; ++ ram_size = (uint64_t)atol(optarg) * 1024 * 1024; + if (ram_size <= 0) + help(); + #ifndef CONFIG_DM +@@ -6400,50 +6487,41 @@ int main(int argc, char **argv) + shared_page_nr = nr_pages - 1; + #endif + ++#if defined(__i386__) || defined(__x86_64__) ++ ++ if ( qemu_map_cache_init(tmp_nr_pages) ) ++ { ++ fprintf(logfile, "qemu_map_cache_init returned: error %d\n", errno); ++ exit(-1); ++ } ++ ++ shared_page = xc_map_foreign_range(xc_handle, domid, PAGE_SIZE, ++ PROT_READ|PROT_WRITE, shared_page_nr); ++ if (shared_page == NULL) { ++ fprintf(logfile, "map shared IO page returned error %d\n", errno); ++ exit(-1); ++ } ++ ++ fprintf(logfile, "shared page at pfn:%lx\n", shared_page_nr); ++ ++ buffered_io_page = xc_map_foreign_range(xc_handle, domid, PAGE_SIZE, ++ PROT_READ|PROT_WRITE, ++ shared_page_nr - 2); ++ if (buffered_io_page == NULL) { ++ fprintf(logfile, "map buffered IO page returned error %d\n", errno); ++ exit(-1); ++ } ++ ++ fprintf(logfile, "buffered io page at pfn:%lx\n", shared_page_nr - 2); ++ ++#elif defined(__ia64__) ++ + page_array = (xen_pfn_t *)malloc(tmp_nr_pages * sizeof(xen_pfn_t)); + if (page_array == NULL) { + fprintf(logfile, "malloc returned error %d\n", errno); + exit(-1); + } + +-#if defined(__i386__) || defined(__x86_64__) +- for ( i = 0; i < tmp_nr_pages; i++) +- page_array[i] = i; +- +- phys_ram_base = xc_map_foreign_batch(xc_handle, domid, +- PROT_READ|PROT_WRITE, page_array, +- tmp_nr_pages); +- if (phys_ram_base == NULL) { +- fprintf(logfile, "batch map guest memory returned error %d\n", errno); +- exit(-1); +- } +- +- shared_page = xc_map_foreign_range(xc_handle, domid, PAGE_SIZE, +- PROT_READ|PROT_WRITE, +- page_array[shared_page_nr]); +- if (shared_page == NULL) { +- fprintf(logfile, "map shared IO page returned error %d\n", errno); +- exit(-1); +- } +- +- fprintf(logfile, "shared page at pfn:%lx, mfn: %"PRIx64"\n", +- shared_page_nr, (uint64_t)(page_array[shared_page_nr])); +- +- buffered_io_page = xc_map_foreign_range(xc_handle, domid, PAGE_SIZE, +- PROT_READ|PROT_WRITE, +- page_array[shared_page_nr - 2]); +- if (buffered_io_page == NULL) { +- fprintf(logfile, "map buffered IO page returned error %d\n", errno); +- exit(-1); +- } +- +- fprintf(logfile, "buffered io page at pfn:%lx, mfn: %"PRIx64"\n", +- shared_page_nr - 2, (uint64_t)(page_array[shared_page_nr - 2])); +- +- free(page_array); +- +-#elif defined(__ia64__) +- + if (xc_ia64_get_pfn_list(xc_handle, domid, page_array, + IO_PAGE_START >> PAGE_SHIFT, 3) != 3) { + fprintf(logfile, "xc_ia64_get_pfn_list returned error %d\n", errno); +diff -r 3f0ca90351e2 -r 67a06a9b7b1d tools/ioemu/vl.h +--- a/tools/ioemu/vl.h Thu Dec 07 10:54:43 2006 +0000 ++++ b/tools/ioemu/vl.h Thu Dec 07 11:12:52 2006 +0000 +@@ -156,6 +156,26 @@ extern void *shared_vram; + + extern FILE *logfile; + ++ ++#if defined(__i386__) || defined(__x86_64__) ++#if defined(__i386__) ++#define MAX_MCACHE_SIZE 0x40000000 /* 1GB max for x86 */ ++#define MCACHE_BUCKET_SHIFT 16 ++#elif defined(__x86_64__) ++#define MAX_MCACHE_SIZE 0x1000000000 /* 64GB max for x86_64 */ ++#define MCACHE_BUCKET_SHIFT 20 ++#endif ++ ++#define MCACHE_BUCKET_SIZE (1UL << MCACHE_BUCKET_SHIFT) ++ ++struct map_cache { ++ unsigned long paddr_index; ++ uint8_t *vaddr_base; ++}; ++ ++uint8_t *qemu_map_cache(target_phys_addr_t phys_addr); ++#endif ++ + extern int xc_handle; + extern int domid; + diff --git a/tools/ioemu/patches/xen-mm b/tools/ioemu/patches/xen-mm index 28289837f9..8587c8cdeb 100644 --- a/tools/ioemu/patches/xen-mm +++ b/tools/ioemu/patches/xen-mm @@ -1,7 +1,7 @@ Index: ioemu/hw/pc.c =================================================================== ---- ioemu.orig/hw/pc.c 2006-08-17 19:36:00.588166019 +0100 -+++ ioemu/hw/pc.c 2006-08-17 19:37:36.704485734 +0100 +--- ioemu.orig/hw/pc.c 2006-12-08 02:00:38.000000000 +0000 ++++ ioemu/hw/pc.c 2006-12-08 02:02:07.000000000 +0000 @@ -646,7 +646,9 @@ } @@ -25,8 +25,8 @@ Index: ioemu/hw/pc.c isa_bios_size = bios_size; Index: ioemu/vl.c =================================================================== ---- ioemu.orig/vl.c 2006-08-17 19:36:00.667157242 +0100 -+++ ioemu/vl.c 2006-08-17 19:47:08.538087284 +0100 +--- ioemu.orig/vl.c 2006-12-08 02:00:39.000000000 +0000 ++++ ioemu/vl.c 2006-12-08 02:02:28.000000000 +0000 @@ -158,6 +158,8 @@ int acpi_enabled = 1; int fd_bootchk = 1; @@ -60,7 +60,7 @@ Index: ioemu/vl.c break; case QEMU_OPTION_l: { -@@ -6133,12 +6140,67 @@ +@@ -6133,12 +6140,61 @@ /* init the memory */ phys_ram_size = ram_size + vga_ram_size + bios_size; @@ -85,14 +85,8 @@ Index: ioemu/vl.c + exit(-1); + } + -+ if (xc_get_pfn_list(xc_handle, domid, page_array, nr_pages) != nr_pages) { -+ fprintf(logfile, "xc_get_pfn_list returned error %d\n", errno); -+ exit(-1); -+ } -+ -+ if (ram_size > HVM_BELOW_4G_RAM_END) -+ for (i = 0; i < nr_pages - (HVM_BELOW_4G_RAM_END >> PAGE_SHIFT); i++) -+ page_array[tmp_nr_pages - 1 - i] = page_array[nr_pages - 1 - i]; ++ for ( i = 0; i < tmp_nr_pages; i++) ++ page_array[i] = i; + + phys_ram_base = xc_map_foreign_batch(xc_handle, domid, + PROT_READ|PROT_WRITE, page_array, @@ -130,8 +124,8 @@ Index: ioemu/vl.c if (cdrom_index >= 0) { Index: ioemu/hw/piix_pci.c =================================================================== ---- ioemu.orig/hw/piix_pci.c 2006-08-17 19:37:36.189542951 +0100 -+++ ioemu/hw/piix_pci.c 2006-08-17 19:38:05.806252180 +0100 +--- ioemu.orig/hw/piix_pci.c 2006-12-08 02:00:36.000000000 +0000 ++++ ioemu/hw/piix_pci.c 2006-12-08 02:02:06.000000000 +0000 @@ -399,7 +399,7 @@ uint8_t elcr[2]; @@ -143,8 +137,8 @@ Index: ioemu/hw/piix_pci.c elcr[0] = 0x00; Index: ioemu/vl.h =================================================================== ---- ioemu.orig/vl.h 2006-08-17 19:37:36.529505177 +0100 -+++ ioemu/vl.h 2006-08-17 19:47:32.680418959 +0100 +--- ioemu.orig/vl.h 2006-12-08 02:00:39.000000000 +0000 ++++ ioemu/vl.h 2006-12-08 02:02:07.000000000 +0000 @@ -39,6 +39,7 @@ #include #include "xenctrl.h" diff --git a/tools/ioemu/patches/xen-platform-device b/tools/ioemu/patches/xen-platform-device index d2fcda7452..f57d1fc477 100644 --- a/tools/ioemu/patches/xen-platform-device +++ b/tools/ioemu/patches/xen-platform-device @@ -3,9 +3,9 @@ will come later. Index: ioemu/Makefile.target =================================================================== ---- ioemu.orig/Makefile.target 2006-10-24 14:41:01.000000000 +0100 -+++ ioemu/Makefile.target 2006-10-24 14:41:01.000000000 +0100 -@@ -359,6 +359,7 @@ +--- ioemu.orig/Makefile.target 2006-12-08 01:41:14.000000000 +0000 ++++ ioemu/Makefile.target 2006-12-08 01:41:15.000000000 +0000 +@@ -360,6 +360,7 @@ VL_OBJS+= usb-uhci.o VL_OBJS+= piix4acpi.o VL_OBJS+= xenstore.o @@ -15,8 +15,8 @@ Index: ioemu/Makefile.target ifeq ($(TARGET_BASE_ARCH), ppc) Index: ioemu/hw/pc.c =================================================================== ---- ioemu.orig/hw/pc.c 2006-10-24 14:41:00.000000000 +0100 -+++ ioemu/hw/pc.c 2006-10-24 14:41:01.000000000 +0100 +--- ioemu.orig/hw/pc.c 2006-12-08 01:41:13.000000000 +0000 ++++ ioemu/hw/pc.c 2006-12-08 01:41:15.000000000 +0000 @@ -823,6 +823,9 @@ } #endif /* !CONFIG_DM */ @@ -30,7 +30,7 @@ Index: ioemu/hw/pc.c Index: ioemu/hw/xen_platform.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ ioemu/hw/xen_platform.c 2006-10-24 14:41:04.000000000 +0100 ++++ ioemu/hw/xen_platform.c 2006-12-08 01:41:15.000000000 +0000 @@ -0,0 +1,144 @@ +/* + * XEN platform fake pci device, formerly known as the event channel device @@ -178,8 +178,8 @@ Index: ioemu/hw/xen_platform.c +} Index: ioemu/vl.h =================================================================== ---- ioemu.orig/vl.h 2006-10-24 14:41:01.000000000 +0100 -+++ ioemu/vl.h 2006-10-24 14:41:01.000000000 +0100 +--- ioemu.orig/vl.h 2006-12-08 01:41:14.000000000 +0000 ++++ ioemu/vl.h 2006-12-08 01:41:15.000000000 +0000 @@ -1212,6 +1212,9 @@ void xenstore_check_new_media_present(int timeout); void xenstore_write_vncport(int vnc_display); diff --git a/tools/ioemu/patches/xen-support-buffered-ioreqs b/tools/ioemu/patches/xen-support-buffered-ioreqs index 7865dda35b..10d2a9a84a 100644 --- a/tools/ioemu/patches/xen-support-buffered-ioreqs +++ b/tools/ioemu/patches/xen-support-buffered-ioreqs @@ -1,8 +1,8 @@ Index: ioemu/vl.c =================================================================== ---- ioemu.orig/vl.c 2006-10-24 14:33:47.000000000 +0100 -+++ ioemu/vl.c 2006-10-24 14:33:47.000000000 +0100 -@@ -5854,6 +5854,7 @@ +--- ioemu.orig/vl.c 2006-12-08 02:02:37.000000000 +0000 ++++ ioemu/vl.c 2006-12-08 02:02:37.000000000 +0000 +@@ -5838,6 +5838,7 @@ unsigned long nr_pages, tmp_nr_pages, shared_page_nr; xen_pfn_t *page_array; extern void *shared_page; @@ -10,11 +10,10 @@ Index: ioemu/vl.c char qemu_dm_logfilename[64]; -@@ -6440,6 +6441,18 @@ +@@ -6418,6 +6419,17 @@ fprintf(logfile, "shared page at pfn:%lx, mfn: %"PRIx64"\n", shared_page_nr, (uint64_t)(page_array[shared_page_nr])); -+ /* not yet add for IA64 */ + buffered_io_page = xc_map_foreign_range(xc_handle, domid, PAGE_SIZE, + PROT_READ|PROT_WRITE, + page_array[shared_page_nr - 2]); @@ -31,8 +30,8 @@ Index: ioemu/vl.c #elif defined(__ia64__) Index: ioemu/target-i386-dm/helper2.c =================================================================== ---- ioemu.orig/target-i386-dm/helper2.c 2006-10-24 14:33:45.000000000 +0100 -+++ ioemu/target-i386-dm/helper2.c 2006-10-24 14:33:47.000000000 +0100 +--- ioemu.orig/target-i386-dm/helper2.c 2006-12-08 02:02:35.000000000 +0000 ++++ ioemu/target-i386-dm/helper2.c 2006-12-08 02:02:37.000000000 +0000 @@ -76,6 +76,10 @@ shared_iopage_t *shared_page = NULL; @@ -44,14 +43,14 @@ Index: ioemu/target-i386-dm/helper2.c /* the evtchn fd for polling */ int xce_handle = -1; -@@ -419,36 +423,68 @@ - req->u.data = tmp1; +@@ -435,39 +439,71 @@ + req->data = tmp1; } +void __handle_ioreq(CPUState *env, ioreq_t *req) +{ -+ if (!req->pdata_valid && req->dir == IOREQ_WRITE && req->size != 4) -+ req->u.data &= (1UL << (8 * req->size)) - 1; ++ if (!req->data_is_ptr && req->dir == IOREQ_WRITE && req->size != 4) ++ req->data &= (1UL << (8 * req->size)) - 1; + + switch (req->type) { + case IOREQ_TYPE_PIO: @@ -63,6 +62,9 @@ Index: ioemu/target-i386-dm/helper2.c + case IOREQ_TYPE_AND: + cpu_ioreq_and(env, req); + break; ++ case IOREQ_TYPE_ADD: ++ cpu_ioreq_add(env, req); ++ break; + case IOREQ_TYPE_OR: + cpu_ioreq_or(env, req); + break; @@ -109,9 +111,9 @@ Index: ioemu/target-i386-dm/helper2.c + handle_buffered_io(env); if (req) { -- if ((!req->pdata_valid) && (req->dir == IOREQ_WRITE)) { +- if ((!req->data_is_ptr) && (req->dir == IOREQ_WRITE)) { - if (req->size != 4) -- req->u.data &= (1UL << (8 * req->size))-1; +- req->data &= (1UL << (8 * req->size))-1; - } - - switch (req->type) { @@ -124,6 +126,9 @@ Index: ioemu/target-i386-dm/helper2.c - case IOREQ_TYPE_AND: - cpu_ioreq_and(env, req); - break; +- case IOREQ_TYPE_ADD: +- cpu_ioreq_add(env, req); +- break; - case IOREQ_TYPE_OR: - cpu_ioreq_or(env, req); - break; @@ -135,9 +140,9 @@ Index: ioemu/target-i386-dm/helper2.c - } + __handle_ioreq(env, req); - /* No state change if state = STATE_IORESP_HOOK */ - if (req->state == STATE_IOREQ_INPROCESS) { -@@ -466,6 +502,10 @@ + if (req->state != STATE_IOREQ_INPROCESS) { + fprintf(logfile, "Badness in I/O request ... not in service?!: " +@@ -492,6 +528,10 @@ CPUState *env = cpu_single_env; int evtchn_fd = xc_evtchn_fd(xce_handle); @@ -147,4 +152,4 @@ Index: ioemu/target-i386-dm/helper2.c + qemu_set_fd_handler(evtchn_fd, cpu_handle_ioreq, NULL, env); - env->send_event = 0; + while (1) { diff --git a/tools/ioemu/patches/xenstore-block-device-config b/tools/ioemu/patches/xenstore-block-device-config index e3782d5b65..5944e3b45f 100644 --- a/tools/ioemu/patches/xenstore-block-device-config +++ b/tools/ioemu/patches/xenstore-block-device-config @@ -1,8 +1,8 @@ Index: ioemu/Makefile.target =================================================================== ---- ioemu.orig/Makefile.target 2006-10-24 14:31:36.000000000 +0100 -+++ ioemu/Makefile.target 2006-10-24 14:33:28.000000000 +0100 -@@ -358,6 +358,7 @@ +--- ioemu.orig/Makefile.target 2006-12-08 02:02:36.000000000 +0000 ++++ ioemu/Makefile.target 2006-12-08 02:02:37.000000000 +0000 +@@ -359,6 +359,7 @@ VL_OBJS+= cirrus_vga.o mixeng.o parallel.o acpi.o piix_pci.o VL_OBJS+= usb-uhci.o VL_OBJS+= piix4acpi.o @@ -13,7 +13,7 @@ Index: ioemu/Makefile.target Index: ioemu/xenstore.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ ioemu/xenstore.c 2006-10-24 14:33:28.000000000 +0100 ++++ ioemu/xenstore.c 2006-12-08 02:02:37.000000000 +0000 @@ -0,0 +1,187 @@ +/* + * This file is subject to the terms and conditions of the GNU General @@ -117,7 +117,7 @@ Index: ioemu/xenstore.c + if (strncmp(dev, "hd", 2) || strlen(dev) != 3) + continue; + hd_index = dev[2] - 'a'; -+ if (hd_index > MAX_DISKS) ++ if (hd_index >= MAX_DISKS) + continue; + /* read the type of the device */ + if (pasprintf(&buf, "%s/device/vbd/%s/device-type", path, e[i]) == -1) @@ -204,8 +204,8 @@ Index: ioemu/xenstore.c +} Index: ioemu/vl.c =================================================================== ---- ioemu.orig/vl.c 2006-10-24 14:33:24.000000000 +0100 -+++ ioemu/vl.c 2006-10-24 14:33:28.000000000 +0100 +--- ioemu.orig/vl.c 2006-12-08 02:02:37.000000000 +0000 ++++ ioemu/vl.c 2006-12-08 02:02:37.000000000 +0000 @@ -5256,9 +5256,11 @@ "Standard options:\n" "-M machine select emulated machine (-M ? for list)\n" @@ -246,7 +246,7 @@ Index: ioemu/vl.c { "boot", HAS_ARG, QEMU_OPTION_boot }, { "snapshot", 0, QEMU_OPTION_snapshot }, #ifdef TARGET_I386 -@@ -5817,10 +5823,16 @@ +@@ -5801,10 +5807,16 @@ #ifdef CONFIG_GDBSTUB int use_gdbstub, gdbstub_port; #endif @@ -265,7 +265,7 @@ Index: ioemu/vl.c const char *kernel_filename, *kernel_cmdline; DisplayState *ds = &display_state; int cyls, heads, secs, translation; -@@ -5881,8 +5893,10 @@ +@@ -5865,8 +5877,10 @@ initrd_filename = NULL; for(i = 0; i < MAX_FD; i++) fd_filename[i] = NULL; @@ -276,7 +276,7 @@ Index: ioemu/vl.c ram_size = DEFAULT_RAM_SIZE * 1024 * 1024; vga_ram_size = VGA_RAM_SIZE; bios_size = BIOS_SIZE; -@@ -5896,11 +5910,13 @@ +@@ -5880,11 +5894,13 @@ vncunused = 0; kernel_filename = NULL; kernel_cmdline = ""; @@ -290,7 +290,7 @@ Index: ioemu/vl.c cyls = heads = secs = 0; translation = BIOS_ATA_TRANSLATION_AUTO; pstrcpy(monitor_device, sizeof(monitor_device), "vc"); -@@ -5935,7 +5951,11 @@ +@@ -5919,7 +5935,11 @@ break; r = argv[optind]; if (r[0] != '-') { @@ -302,7 +302,7 @@ Index: ioemu/vl.c } else { const QEMUOption *popt; -@@ -5979,6 +5999,7 @@ +@@ -5963,6 +5983,7 @@ case QEMU_OPTION_initrd: initrd_filename = optarg; break; @@ -310,7 +310,7 @@ Index: ioemu/vl.c case QEMU_OPTION_hda: case QEMU_OPTION_hdb: case QEMU_OPTION_hdc: -@@ -5991,6 +6012,7 @@ +@@ -5975,6 +5996,7 @@ cdrom_index = -1; } break; @@ -318,7 +318,7 @@ Index: ioemu/vl.c case QEMU_OPTION_snapshot: snapshot = 1; break; -@@ -6043,11 +6065,13 @@ +@@ -6027,11 +6049,13 @@ case QEMU_OPTION_append: kernel_cmdline = optarg; break; @@ -332,7 +332,7 @@ Index: ioemu/vl.c case QEMU_OPTION_boot: boot_device = optarg[0]; if (boot_device != 'a' && -@@ -6305,12 +6329,18 @@ +@@ -6289,12 +6313,18 @@ } } @@ -351,7 +351,7 @@ Index: ioemu/vl.c if (!linux_boot && hd_filename[0] == '\0' && (cdrom_index >= 0 && hd_filename[cdrom_index] == '\0') && -@@ -6324,6 +6354,7 @@ +@@ -6308,6 +6338,7 @@ else boot_device = 'd'; } @@ -359,7 +359,7 @@ Index: ioemu/vl.c setvbuf(stdout, NULL, _IOLBF, 0); -@@ -6456,6 +6487,7 @@ +@@ -6441,6 +6472,7 @@ #endif /* !CONFIG_DM */ @@ -367,7 +367,7 @@ Index: ioemu/vl.c /* we always create the cdrom drive, even if no disk is there */ bdrv_init(); if (cdrom_index >= 0) { -@@ -6482,6 +6514,7 @@ +@@ -6467,6 +6499,7 @@ } } } @@ -375,7 +375,7 @@ Index: ioemu/vl.c /* we always create at least one floppy disk */ fd_table[0] = bdrv_new("fda"); -@@ -6560,6 +6593,8 @@ +@@ -6545,6 +6578,8 @@ } } @@ -386,8 +386,8 @@ Index: ioemu/vl.c kernel_filename, kernel_cmdline, initrd_filename, Index: ioemu/monitor.c =================================================================== ---- ioemu.orig/monitor.c 2006-10-24 14:31:36.000000000 +0100 -+++ ioemu/monitor.c 2006-10-24 14:33:28.000000000 +0100 +--- ioemu.orig/monitor.c 2006-12-08 02:02:35.000000000 +0000 ++++ ioemu/monitor.c 2006-12-08 02:02:37.000000000 +0000 @@ -24,6 +24,7 @@ #include "vl.h" #include "disas.h" @@ -416,8 +416,8 @@ Index: ioemu/monitor.c int i; Index: ioemu/block.c =================================================================== ---- ioemu.orig/block.c 2006-10-24 14:31:36.000000000 +0100 -+++ ioemu/block.c 2006-10-24 14:33:28.000000000 +0100 +--- ioemu.orig/block.c 2006-12-08 02:02:06.000000000 +0000 ++++ ioemu/block.c 2006-12-08 02:02:37.000000000 +0000 @@ -758,6 +758,7 @@ static void raw_close(BlockDriverState *bs) { @@ -428,8 +428,8 @@ Index: ioemu/block.c Index: ioemu/vl.h =================================================================== ---- ioemu.orig/vl.h 2006-10-24 14:33:24.000000000 +0100 -+++ ioemu/vl.h 2006-10-24 14:33:28.000000000 +0100 +--- ioemu.orig/vl.h 2006-12-08 02:02:37.000000000 +0000 ++++ ioemu/vl.h 2006-12-08 02:02:37.000000000 +0000 @@ -1191,6 +1191,8 @@ void term_print_help(void); void monitor_readline(const char *prompt, int is_password, @@ -455,8 +455,8 @@ Index: ioemu/vl.h extern char domain_name[]; Index: ioemu/hw/ide.c =================================================================== ---- ioemu.orig/hw/ide.c 2006-10-24 14:31:36.000000000 +0100 -+++ ioemu/hw/ide.c 2006-10-24 14:33:28.000000000 +0100 +--- ioemu.orig/hw/ide.c 2006-12-08 02:02:35.000000000 +0000 ++++ ioemu/hw/ide.c 2006-12-08 02:02:37.000000000 +0000 @@ -1158,6 +1158,7 @@ } else { ide_atapi_cmd_error(s, SENSE_NOT_READY, diff --git a/tools/ioemu/patches/xenstore-device-info-functions b/tools/ioemu/patches/xenstore-device-info-functions new file mode 100644 index 0000000000..f0b1d46fe4 --- /dev/null +++ b/tools/ioemu/patches/xenstore-device-info-functions @@ -0,0 +1,190 @@ +# HG changeset patch +# User kaf24@localhost.localdomain +# Node ID bbcac2aea0e8196cd75a3bf6dbe57bebf8c1e5b2 +# Parent dc973fe5633386547ce5bc8fd4cf5f2bb5b55174 +[QEMU] Helper functions to interface with the xenstore and read device information from it. + + - detect what types of devices a domain has or whether a domain has a + device of a certain type + - read the content of a variable related to a device, i.e., + hotplug-status + - subscribe to changes of the hotplug status of a device for not + having to poll the status + +Signed-off-by: Stefan Berger + +Index: ioemu/xenstore.c +=================================================================== +--- ioemu.orig/xenstore.c 2006-12-08 18:20:53.000000000 +0000 ++++ ioemu/xenstore.c 2006-12-08 18:20:53.000000000 +0000 +@@ -264,3 +264,140 @@ + + return rc; + } ++ ++ ++/* ++ * get all device instances of a certain type ++ */ ++char **xenstore_domain_get_devices(struct xs_handle *handle, ++ const char *devtype, unsigned int *num) ++{ ++ char *path; ++ char *buf = NULL; ++ char **e = NULL; ++ ++ path = xs_get_domain_path(handle, domid); ++ if (path == NULL) ++ goto out; ++ ++ if (pasprintf(&buf, "%s/device/%s", path,devtype) == -1) ++ goto out; ++ ++ e = xs_directory(handle, XBT_NULL, buf, num); ++ ++ out: ++ free(path); ++ free(buf); ++ return e; ++} ++ ++/* ++ * Check whether a domain has devices of the given type ++ */ ++int xenstore_domain_has_devtype(struct xs_handle *handle, const char *devtype) ++{ ++ int rc = 0; ++ unsigned int num; ++ char **e = xenstore_domain_get_devices(handle, devtype, &num); ++ if (e) ++ rc = 1; ++ free(e); ++ return rc; ++} ++ ++/* ++ * Function that creates a path to a variable of an instance of a ++ * certain device ++ */ ++static char *get_device_variable_path(const char *devtype, const char *inst, ++ const char *var) ++{ ++ char *buf = NULL; ++ if (pasprintf(&buf, "/local/domain/0/backend/%s/%d/%s/%s", ++ devtype, ++ domid, ++ inst, ++ var) == -1) { ++ free(buf); ++ buf = NULL; ++ } ++ return buf; ++} ++ ++char *xenstore_backend_read_variable(struct xs_handle *handle, ++ const char *devtype, const char *inst, ++ const char *var) ++{ ++ char *value = NULL; ++ char *buf = NULL; ++ unsigned int len; ++ ++ buf = get_device_variable_path(devtype, inst, var); ++ if (NULL == buf) ++ goto out; ++ ++ value = xs_read(handle, XBT_NULL, buf, &len); ++ ++ free(buf); ++ ++out: ++ return value; ++} ++ ++/* ++ Read the hotplug status variable from the backend given the type ++ of device and its instance. ++*/ ++char *xenstore_read_hotplug_status(struct xs_handle *handle, ++ const char *devtype, const char *inst) ++{ ++ return xenstore_backend_read_variable(handle, devtype, inst, ++ "hotplug-status"); ++} ++ ++/* ++ Subscribe to the hotplug status of a device given the type of device and ++ its instance. ++ In case an error occurrs, a negative number is returned. ++ */ ++int xenstore_subscribe_to_hotplug_status(struct xs_handle *handle, ++ const char *devtype, ++ const char *inst, ++ const char *token) ++{ ++ int rc = 0; ++ char *path = get_device_variable_path(devtype, inst, "hotplug-status"); ++ ++ if (path == NULL) ++ return -1; ++ ++ if (0 == xs_watch(handle, path, token)) ++ rc = -2; ++ ++ free(path); ++ ++ return rc; ++} ++ ++/* ++ * Unsubscribe from a subscription to the status of a hotplug variable of ++ * a device. ++ */ ++int xenstore_unsubscribe_from_hotplug_status(struct xs_handle *handle, ++ const char *devtype, ++ const char *inst, ++ const char *token) ++{ ++ int rc = 0; ++ char *path; ++ path = get_device_variable_path(devtype, inst, "hotplug-status"); ++ if (path == NULL) ++ return -1; ++ ++ if (0 == xs_unwatch(handle, path, token)) ++ rc = -2; ++ ++ free(path); ++ ++ return rc; ++} +Index: ioemu/vl.h +=================================================================== +--- ioemu.orig/vl.h 2006-12-08 18:20:53.000000000 +0000 ++++ ioemu/vl.h 2006-12-08 18:20:53.000000000 +0000 +@@ -1216,6 +1216,25 @@ + void xenstore_write_vncport(int vnc_display); + int xenstore_read_vncpasswd(int domid); + ++int xenstore_domain_has_devtype(struct xs_handle *handle, ++ const char *devtype); ++char **xenstore_domain_get_devices(struct xs_handle *handle, ++ const char *devtype, unsigned int *num); ++char *xenstore_read_hotplug_status(struct xs_handle *handle, ++ const char *devtype, const char *inst); ++char *xenstore_backend_read_variable(struct xs_handle *, ++ const char *devtype, const char *inst, ++ const char *var); ++int xenstore_subscribe_to_hotplug_status(struct xs_handle *handle, ++ const char *devtype, ++ const char *inst, ++ const char *token); ++int xenstore_unsubscribe_from_hotplug_status(struct xs_handle *handle, ++ const char *devtype, ++ const char *inst, ++ const char *token); ++ ++ + /* xen_platform.c */ + void pci_xen_platform_init(PCIBus *bus); + diff --git a/tools/ioemu/patches/xenstore-write-vnc-port b/tools/ioemu/patches/xenstore-write-vnc-port index 6db523e980..259d6451f3 100644 --- a/tools/ioemu/patches/xenstore-write-vnc-port +++ b/tools/ioemu/patches/xenstore-write-vnc-port @@ -1,7 +1,7 @@ Index: ioemu/xenstore.c =================================================================== ---- ioemu.orig/xenstore.c 2006-10-24 14:33:47.000000000 +0100 -+++ ioemu/xenstore.c 2006-10-24 14:33:47.000000000 +0100 +--- ioemu.orig/xenstore.c 2006-12-08 02:02:37.000000000 +0000 ++++ ioemu/xenstore.c 2006-12-08 02:02:37.000000000 +0000 @@ -185,3 +185,31 @@ free(image); free(vec); @@ -36,9 +36,9 @@ Index: ioemu/xenstore.c +} Index: ioemu/vl.c =================================================================== ---- ioemu.orig/vl.c 2006-10-24 14:33:47.000000000 +0100 -+++ ioemu/vl.c 2006-10-24 14:33:47.000000000 +0100 -@@ -6550,6 +6550,7 @@ +--- ioemu.orig/vl.c 2006-12-08 02:02:37.000000000 +0000 ++++ ioemu/vl.c 2006-12-08 02:02:37.000000000 +0000 +@@ -6535,6 +6535,7 @@ vnc_display = vnc_display_init(ds, vnc_display, vncunused, &vnclisten_addr); if (vncviewer) vnc_start_viewer(vnc_display); @@ -48,8 +48,8 @@ Index: ioemu/vl.c sdl_display_init(ds, full_screen); Index: ioemu/vl.h =================================================================== ---- ioemu.orig/vl.h 2006-10-24 14:33:47.000000000 +0100 -+++ ioemu/vl.h 2006-10-24 14:33:47.000000000 +0100 +--- ioemu.orig/vl.h 2006-12-08 02:02:37.000000000 +0000 ++++ ioemu/vl.h 2006-12-08 02:02:37.000000000 +0000 @@ -1210,6 +1210,7 @@ int xenstore_fd(void); void xenstore_process_event(void *opaque); diff --git a/tools/ioemu/target-i386-dm/cpu.h b/tools/ioemu/target-i386-dm/cpu.h index dc016edd3e..017b8c021b 100644 --- a/tools/ioemu/target-i386-dm/cpu.h +++ b/tools/ioemu/target-i386-dm/cpu.h @@ -25,7 +25,8 @@ #ifdef TARGET_X86_64 #define TARGET_LONG_BITS 64 #else -#define TARGET_LONG_BITS 32 +/* #define TARGET_LONG_BITS 32 */ +#define TARGET_LONG_BITS 64 /* for Qemu map cache */ #endif /* target supports implicit self modifying code */ diff --git a/tools/ioemu/target-i386-dm/exec-dm.c b/tools/ioemu/target-i386-dm/exec-dm.c index be8b280dda..755d547772 100644 --- a/tools/ioemu/target-i386-dm/exec-dm.c +++ b/tools/ioemu/target-i386-dm/exec-dm.c @@ -36,6 +36,7 @@ #include "cpu.h" #include "exec-all.h" +#include "vl.h" //#define DEBUG_TB_INVALIDATE //#define DEBUG_FLUSH @@ -426,6 +427,12 @@ static inline int paddr_is_ram(target_phys_addr_t addr) #endif } +#if defined(__i386__) || defined(__x86_64__) +#define phys_ram_addr(x) (qemu_map_cache(x)) +#elif defined(__ia64__) +#define phys_ram_addr(x) (phys_ram_base + (x)) +#endif + void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, int len, int is_write) { @@ -438,7 +445,7 @@ void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, l = TARGET_PAGE_SIZE - (addr & ~TARGET_PAGE_MASK); if (l > len) l = len; - + io_index = iomem_index(addr); if (is_write) { if (io_index) { @@ -460,9 +467,10 @@ void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, } } else if (paddr_is_ram(addr)) { /* Reading from RAM */ - memcpy(phys_ram_base + addr, buf, l); + ptr = phys_ram_addr(addr); + memcpy(ptr, buf, l); #ifdef __ia64__ - sync_icache((unsigned long)(phys_ram_base + addr), l); + sync_icache(ptr, l); #endif } } else { @@ -485,7 +493,8 @@ void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, } } else if (paddr_is_ram(addr)) { /* Reading from RAM */ - memcpy(buf, phys_ram_base + addr, l); + ptr = phys_ram_addr(addr); + memcpy(buf, ptr, l); } else { /* Neither RAM nor known MMIO space */ memset(buf, 0xff, len); diff --git a/tools/ioemu/vl.c b/tools/ioemu/vl.c index a2700ef313..e14e6a1d9e 100644 --- a/tools/ioemu/vl.c +++ b/tools/ioemu/vl.c @@ -5808,6 +5808,92 @@ int set_mm_mapping(int xc_handle, uint32_t domid, return 0; } +#if defined(__i386__) || defined(__x86_64__) +static struct map_cache *mapcache_entry; +static unsigned long nr_buckets; + +static int qemu_map_cache_init(unsigned long nr_pages) +{ + unsigned long max_pages = MAX_MCACHE_SIZE >> PAGE_SHIFT; + int i; + + if (nr_pages < max_pages) + max_pages = nr_pages; + + nr_buckets = (max_pages << PAGE_SHIFT) >> MCACHE_BUCKET_SHIFT; + + fprintf(logfile, "qemu_map_cache_init nr_buckets = %lx\n", nr_buckets); + + mapcache_entry = malloc(nr_buckets * sizeof(struct map_cache)); + if (mapcache_entry == NULL) { + errno = ENOMEM; + return -1; + } + + memset(mapcache_entry, 0, nr_buckets * sizeof(struct map_cache)); + + /* + * To avoid ENOMEM from xc_map_foreign_batch() at runtime, we + * pre-fill all the map caches in advance. + */ + for (i = 0; i < nr_buckets; i++) + (void)qemu_map_cache(((target_phys_addr_t)i) << MCACHE_BUCKET_SHIFT); + + return 0; +} + +uint8_t *qemu_map_cache(target_phys_addr_t phys_addr) +{ + struct map_cache *entry; + unsigned long address_index = phys_addr >> MCACHE_BUCKET_SHIFT; + unsigned long address_offset = phys_addr & (MCACHE_BUCKET_SIZE-1); + + /* For most cases (>99.9%), the page address is the same. */ + static unsigned long last_address_index = ~0UL; + static uint8_t *last_address_vaddr; + + if (address_index == last_address_index) + return last_address_vaddr + address_offset; + + entry = &mapcache_entry[address_index % nr_buckets]; + + if (entry->vaddr_base == NULL || entry->paddr_index != address_index) + { + /* We need to remap a bucket. */ + uint8_t *vaddr_base; + unsigned long pfns[MCACHE_BUCKET_SIZE >> PAGE_SHIFT]; + unsigned int i; + + if (entry->vaddr_base != NULL) { + errno = munmap(entry->vaddr_base, MCACHE_BUCKET_SIZE); + if (errno) { + fprintf(logfile, "unmap fails %d\n", errno); + exit(-1); + } + } + + for (i = 0; i < MCACHE_BUCKET_SIZE >> PAGE_SHIFT; i++) + pfns[i] = (address_index << (MCACHE_BUCKET_SHIFT-PAGE_SHIFT)) + i; + + vaddr_base = xc_map_foreign_batch( + xc_handle, domid, PROT_READ|PROT_WRITE, + pfns, MCACHE_BUCKET_SIZE >> PAGE_SHIFT); + if (vaddr_base == NULL) { + fprintf(logfile, "xc_map_foreign_batch error %d\n", errno); + exit(-1); + } + + entry->vaddr_base = vaddr_base; + entry->paddr_index = address_index;; + } + + last_address_index = address_index; + last_address_vaddr = entry->vaddr_base; + + return last_address_vaddr + address_offset; +} +#endif + int main(int argc, char **argv) { #ifdef CONFIG_GDBSTUB @@ -6130,6 +6216,7 @@ int main(int argc, char **argv) break; case QEMU_OPTION_m: ram_size = atol(optarg) * 1024 * 1024; + ram_size = (uint64_t)atol(optarg) * 1024 * 1024; if (ram_size <= 0) help(); #ifndef CONFIG_DM @@ -6378,6 +6465,10 @@ int main(int argc, char **argv) } #if defined (__ia64__) + /* ram_size passed from xend has added on GFW memory, + so we must subtract it here */ + ram_size -= 16 * MEM_M; + if (ram_size > MMIO_START) ram_size += 1 * MEM_G; /* skip 3G-4G MMIO, LEGACY_IO_SPACE etc. */ #endif @@ -6400,79 +6491,58 @@ int main(int argc, char **argv) shared_page_nr = nr_pages - 1; #endif +#if defined(__i386__) || defined(__x86_64__) + + if ( qemu_map_cache_init(tmp_nr_pages) ) + { + fprintf(logfile, "qemu_map_cache_init returned: error %d\n", errno); + exit(-1); + } + + shared_page = xc_map_foreign_range(xc_handle, domid, PAGE_SIZE, + PROT_READ|PROT_WRITE, shared_page_nr); + if (shared_page == NULL) { + fprintf(logfile, "map shared IO page returned error %d\n", errno); + exit(-1); + } + + fprintf(logfile, "shared page at pfn:%lx\n", shared_page_nr); + + buffered_io_page = xc_map_foreign_range(xc_handle, domid, PAGE_SIZE, + PROT_READ|PROT_WRITE, + shared_page_nr - 2); + if (buffered_io_page == NULL) { + fprintf(logfile, "map buffered IO page returned error %d\n", errno); + exit(-1); + } + + fprintf(logfile, "buffered io page at pfn:%lx\n", shared_page_nr - 2); + +#elif defined(__ia64__) + page_array = (xen_pfn_t *)malloc(tmp_nr_pages * sizeof(xen_pfn_t)); if (page_array == NULL) { fprintf(logfile, "malloc returned error %d\n", errno); exit(-1); } -#if defined(__i386__) || defined(__x86_64__) - for ( i = 0; i < tmp_nr_pages; i++) - page_array[i] = i; - - phys_ram_base = xc_map_foreign_batch(xc_handle, domid, - PROT_READ|PROT_WRITE, page_array, - tmp_nr_pages); - if (phys_ram_base == NULL) { - fprintf(logfile, "batch map guest memory returned error %d\n", errno); - exit(-1); - } - shared_page = xc_map_foreign_range(xc_handle, domid, PAGE_SIZE, PROT_READ|PROT_WRITE, - page_array[shared_page_nr]); - if (shared_page == NULL) { - fprintf(logfile, "map shared IO page returned error %d\n", errno); - exit(-1); - } - - fprintf(logfile, "shared page at pfn:%lx, mfn: %"PRIx64"\n", - shared_page_nr, (uint64_t)(page_array[shared_page_nr])); - - buffered_io_page = xc_map_foreign_range(xc_handle, domid, PAGE_SIZE, - PROT_READ|PROT_WRITE, - page_array[shared_page_nr - 2]); - if (buffered_io_page == NULL) { - fprintf(logfile, "map buffered IO page returned error %d\n", errno); - exit(-1); - } - - fprintf(logfile, "buffered io page at pfn:%lx, mfn: %"PRIx64"\n", - shared_page_nr - 2, (uint64_t)(page_array[shared_page_nr - 2])); - - free(page_array); - -#elif defined(__ia64__) - - if (xc_ia64_get_pfn_list(xc_handle, domid, page_array, - IO_PAGE_START >> PAGE_SHIFT, 3) != 3) { - fprintf(logfile, "xc_ia64_get_pfn_list returned error %d\n", errno); - exit(-1); - } - - shared_page = xc_map_foreign_range(xc_handle, domid, PAGE_SIZE, - PROT_READ|PROT_WRITE, - page_array[0]); - - fprintf(logfile, "shared page at pfn:%lx, mfn: %016lx\n", - IO_PAGE_START >> PAGE_SHIFT, page_array[0]); + IO_PAGE_START >> PAGE_SHIFT); buffered_io_page =xc_map_foreign_range(xc_handle, domid, PAGE_SIZE, PROT_READ|PROT_WRITE, - page_array[2]); - fprintf(logfile, "Buffered IO page at pfn:%lx, mfn: %016lx\n", - BUFFER_IO_PAGE_START >> PAGE_SHIFT, page_array[2]); - - if (xc_ia64_get_pfn_list(xc_handle, domid, - page_array, 0, nr_pages) != nr_pages) { - fprintf(logfile, "xc_ia64_get_pfn_list returned error %d\n", errno); - exit(-1); - } + BUFFER_IO_PAGE_START >> PAGE_SHIFT); + for (i = 0; i < tmp_nr_pages; i++) + page_array[i] = i; + + /* VTI will not use memory between 3G~4G, so we just pass a legal pfn + to make QEMU map continuous virtual memory space */ if (ram_size > MMIO_START) { for (i = 0 ; i < (MEM_G >> PAGE_SHIFT); i++) page_array[(MMIO_START >> PAGE_SHIFT) + i] = - page_array[(IO_PAGE_START >> PAGE_SHIFT) + 1]; + (STORE_PAGE_START >> PAGE_SHIFT); } phys_ram_base = xc_map_foreign_batch(xc_handle, domid, diff --git a/tools/ioemu/vl.h b/tools/ioemu/vl.h index 4ff660c217..9e4d92a17b 100644 --- a/tools/ioemu/vl.h +++ b/tools/ioemu/vl.h @@ -156,6 +156,26 @@ extern void *shared_vram; extern FILE *logfile; + +#if defined(__i386__) || defined(__x86_64__) +#if defined(__i386__) +#define MAX_MCACHE_SIZE 0x40000000 /* 1GB max for x86 */ +#define MCACHE_BUCKET_SHIFT 16 +#elif defined(__x86_64__) +#define MAX_MCACHE_SIZE 0x1000000000 /* 64GB max for x86_64 */ +#define MCACHE_BUCKET_SHIFT 20 +#endif + +#define MCACHE_BUCKET_SIZE (1UL << MCACHE_BUCKET_SHIFT) + +struct map_cache { + unsigned long paddr_index; + uint8_t *vaddr_base; +}; + +uint8_t *qemu_map_cache(target_phys_addr_t phys_addr); +#endif + extern int xc_handle; extern int domid; @@ -650,8 +670,11 @@ typedef struct PCIIORegion { #define PCI_MAX_LAT 0x3f /* 8 bits */ struct PCIDevice { - /* PCI config space */ - uint8_t config[256]; + /* + * PCI config space. The 4 extra bytes are a safety buffer for guest + * word/dword writes that can extend past byte 0xff. + */ + uint8_t config[256+4]; /* the following fields are read only */ PCIBus *bus; diff --git a/tools/ioemu/vnc.c b/tools/ioemu/vnc.c index 631754ca03..6d7cc31d4a 100644 --- a/tools/ioemu/vnc.c +++ b/tools/ioemu/vnc.c @@ -114,6 +114,8 @@ struct VncState int visible_h; int ctl_keys; /* Ctrl+Alt starts calibration */ + int shift_keys; /* Shift / CapsLock keys */ + int numlock; }; #define DIRTY_PIXEL_BITS 64 @@ -853,14 +855,40 @@ static void pointer_event(VncState *vs, int button_mask, int x, int y) } } +static void press_key(VncState *vs, int keycode) +{ + kbd_put_keycode(keysym2scancode(vs->kbd_layout, keycode) & 0x7f); + kbd_put_keycode(keysym2scancode(vs->kbd_layout, keycode) | 0x80); +} + static void do_key_event(VncState *vs, int down, uint32_t sym) { sym &= 0xFFFF; if (is_graphic_console()) { int keycode; + int numlock; keycode = keysym2scancode(vs->kbd_layout, sym); + numlock = keysym2numlock(vs->kbd_layout, sym); + + /* If the numlock state needs to change then simulate an additional + keypress before sending this one. This will happen if the user + toggles numlock away from the VNC window. + */ + if (numlock == 1) { + if (!vs->numlock) { + vs->numlock = 1; + press_key(vs, XK_Num_Lock); + } + } + else if (numlock == -1) { + if (vs->numlock) { + vs->numlock = 0; + press_key(vs, XK_Num_Lock); + } + } + if (keycode & 0x80) kbd_put_keycode(0xe0); if (down) @@ -870,9 +898,12 @@ static void do_key_event(VncState *vs, int down, uint32_t sym) } else if (down) { int qemu_keysym = 0; - if (sym <= 128) /* normal ascii */ + if (sym <= 128) { /* normal ascii */ + int shifted = vs->shift_keys == 1 || vs->shift_keys == 2; qemu_keysym = sym; - else { + if (sym >= 'a' && sym <= 'z' && shifted) + qemu_keysym -= 'a' - 'A'; + } else { switch (sym) { case XK_Up: qemu_keysym = QEMU_KEY_UP; break; case XK_Down: qemu_keysym = QEMU_KEY_DOWN; break; @@ -903,6 +934,10 @@ static void do_key_event(VncState *vs, int down, uint32_t sym) vs->ctl_keys |= 2; break; + case XK_Shift_L: + vs->shift_keys |= 1; + break; + default: break; } @@ -916,6 +951,18 @@ static void do_key_event(VncState *vs, int down, uint32_t sym) vs->ctl_keys &= ~2; break; + case XK_Shift_L: + vs->shift_keys &= ~1; + break; + + case XK_Caps_Lock: + vs->shift_keys ^= 2; + break; + + case XK_Num_Lock: + vs->numlock = !vs->numlock; + break; + case XK_1 ... XK_9: if ((vs->ctl_keys & 3) != 3) break; @@ -1339,6 +1386,7 @@ int vnc_display_init(DisplayState *ds, int display, int find_unused, struct sock vs->lsock = -1; vs->csock = -1; vs->depth = 4; + vs->numlock = 0; vs->ds = ds; diff --git a/tools/ioemu/vnc_keysym.h b/tools/ioemu/vnc_keysym.h index 14fe47f9e1..5c47104442 100644 --- a/tools/ioemu/vnc_keysym.h +++ b/tools/ioemu/vnc_keysym.h @@ -231,6 +231,19 @@ static name2keysym_t name2keysym[]={ {"Home", 0xff50}, /* XK_Home */ {"End", 0xff57}, /* XK_End */ {"Scroll_Lock", 0xff14}, /* XK_Scroll_Lock */ +{"KP_Home", 0xff95}, +{"KP_Left", 0xff96}, +{"KP_Up", 0xff97}, +{"KP_Right", 0xff98}, +{"KP_Down", 0xff99}, +{"KP_Prior", 0xff9a}, +{"KP_Page_Up", 0xff9a}, +{"KP_Next", 0xff9b}, +{"KP_Page_Down", 0xff9b}, +{"KP_End", 0xff9c}, +{"KP_Begin", 0xff9d}, +{"KP_Insert", 0xff9e}, +{"KP_Delete", 0xff9f}, {"F1", 0xffbe}, /* XK_F1 */ {"F2", 0xffbf}, /* XK_F2 */ {"F3", 0xffc0}, /* XK_F3 */ @@ -258,6 +271,7 @@ static name2keysym_t name2keysym[]={ {"KP_8", 0xffb8}, /* XK_KP_8 */ {"KP_9", 0xffb9}, /* XK_KP_9 */ {"KP_Add", 0xffab}, /* XK_KP_Add */ +{"KP_Separator", 0xffac},/* XK_KP_Separator */ {"KP_Decimal", 0xffae}, /* XK_KP_Decimal */ {"KP_Divide", 0xffaf}, /* XK_KP_Divide */ {"KP_Enter", 0xff8d}, /* XK_KP_Enter */ diff --git a/tools/libxc/Makefile b/tools/libxc/Makefile index 129b867ff6..f92cde836d 100644 --- a/tools/libxc/Makefile +++ b/tools/libxc/Makefile @@ -119,7 +119,7 @@ libxenctrl.so.$(MAJOR): libxenctrl.so.$(MAJOR).$(MINOR) ln -sf $< $@ libxenctrl.so.$(MAJOR).$(MINOR): $(CTRL_PIC_OBJS) - $(CC) $(CFLAGS) $(LDFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,libxenctrl.so.$(MAJOR) $(SHLIB_CFLAGS) -o $@ $^ + $(CC) $(CFLAGS) $(LDFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,libxenctrl.so.$(MAJOR) $(SHLIB_CFLAGS) -o $@ $^ -lpthread # libxenguest @@ -132,7 +132,7 @@ libxenguest.so.$(MAJOR): libxenguest.so.$(MAJOR).$(MINOR) ln -sf $< $@ libxenguest.so.$(MAJOR).$(MINOR): $(GUEST_PIC_OBJS) libxenctrl.so - $(CC) $(CFLAGS) $(LDFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,libxenguest.so.$(MAJOR) $(SHLIB_CFLAGS) -o $@ $(GUEST_PIC_OBJS) -lz -lxenctrl + $(CC) $(CFLAGS) $(LDFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,libxenguest.so.$(MAJOR) $(SHLIB_CFLAGS) -o $@ $(GUEST_PIC_OBJS) -lz -lxenctrl -lpthread -include $(DEPS) diff --git a/tools/libxc/ia64/xc_ia64_hvm_build.c b/tools/libxc/ia64/xc_ia64_hvm_build.c index a97b82af23..ac3a2ff00d 100644 --- a/tools/libxc/ia64/xc_ia64_hvm_build.c +++ b/tools/libxc/ia64/xc_ia64_hvm_build.c @@ -13,35 +13,18 @@ xc_ia64_copy_to_domain_pages(int xc_handle, uint32_t domid, void* src_page, unsigned long dst_pfn, int nr_pages) { // N.B. gva should be page aligned - - xen_pfn_t *page_array = NULL; int i; - page_array = malloc(nr_pages * sizeof(xen_pfn_t)); - if (page_array == NULL) { - PERROR("Could not allocate memory"); - goto error_out; - } - if (xc_ia64_get_pfn_list(xc_handle, domid, page_array, - dst_pfn, nr_pages) != nr_pages) { - PERROR("Could not get the page frame list"); - goto error_out; - } - for (i = 0; i < nr_pages; i++) { - if (xc_copy_to_domain_page(xc_handle, domid, page_array[i], + if (xc_copy_to_domain_page(xc_handle, domid, dst_pfn + i, src_page + (i << PAGE_SHIFT))) - goto error_out; + return -1; } - free(page_array); - return 0; -error_out: - free(page_array); - return -1; + return 0; } -static void +int xc_set_hvm_param(int handle, domid_t dom, int param, unsigned long value) { DECLARE_HYPERCALL; @@ -56,15 +39,37 @@ xc_set_hvm_param(int handle, domid_t dom, int param, unsigned long value) arg.index = param; arg.value = value; - if (mlock(&arg, sizeof(arg)) != 0) { - PERROR("Could not lock memory for set parameter"); - return; - } + if (mlock(&arg, sizeof(arg)) != 0) + return -1; rc = do_xen_hypercall(handle, &hypercall); safe_munlock(&arg, sizeof(arg)); - if (rc < 0) - PERROR("set HVM parameter failed (%d)", rc); + + return rc; +} + +int +xc_get_hvm_param(int handle, domid_t dom, int param, unsigned long *value) +{ + DECLARE_HYPERCALL; + xen_hvm_param_t arg; + int rc; + + hypercall.op = __HYPERVISOR_hvm_op; + hypercall.arg[0] = HVMOP_get_param; + hypercall.arg[1] = (unsigned long)&arg; + + arg.domid = dom; + arg.index = param; + + if (mlock(&arg, sizeof(arg)) != 0) + return -1; + + rc = do_xen_hypercall(handle, &hypercall); + safe_munlock(&arg, sizeof(arg)); + + *value = arg.value; + return rc; } #define HOB_SIGNATURE 0x3436474953424f48 // "HOBSIG64" @@ -546,19 +551,32 @@ add_pal_hob(void* hob_buf) return 0; } +#define GFW_PAGES (GFW_SIZE >> PAGE_SHIFT) +#define VGA_START_PAGE (VGA_IO_START >> PAGE_SHIFT) +#define VGA_END_PAGE ((VGA_IO_START + VGA_IO_SIZE) >> PAGE_SHIFT) +/* + * In this function, we will allocate memory and build P2M/M2P table for VTI + * guest. Frist, a pfn list will be initialized discontiguous, normal memory + * begins with 0, GFW memory and other three pages at their place defined in + * xen/include/public/arch-ia64.h xc_domain_memory_populate_physmap() called + * three times, to set parameter 'extent_order' to different value, this is + * convenient to allocate discontiguous memory with different size. + */ static int setup_guest(int xc_handle, uint32_t dom, unsigned long memsize, - char *image, unsigned long image_size, uint32_t vcpus, - unsigned int store_evtchn, unsigned long *store_mfn) + char *image, unsigned long image_size, vcpu_guest_context_t *ctxt) { - unsigned long page_array[3]; + xen_pfn_t *pfn_list; shared_iopage_t *sp; void *ioreq_buffer_page; - // memsize = required memsize(in configure file) + 16M + // memsize equal to normal memory size(in configure file) + 16M // dom_memsize will pass to xc_ia64_build_hob(), so must be subbed 16M unsigned long dom_memsize = ((memsize - 16) << 20); unsigned long nr_pages = (unsigned long)memsize << (20 - PAGE_SHIFT); + unsigned long normal_pages = nr_pages - GFW_PAGES; + unsigned long vcpus; int rc; + long i, j; DECLARE_DOMCTL; // ROM size for guest firmware, ioreq page and xenstore page @@ -569,14 +587,63 @@ setup_guest(int xc_handle, uint32_t dom, unsigned long memsize, return -1; } - rc = xc_domain_memory_increase_reservation(xc_handle, dom, nr_pages, - 0, 0, NULL); + pfn_list = malloc(nr_pages * sizeof(xen_pfn_t)); + if (pfn_list == NULL) { + PERROR("Could not allocate memory.\n"); + return -1; + } + + // Allocate pfn for normal memory + for (i = 0; i < dom_memsize >> PAGE_SHIFT; i++) + pfn_list[i] = i; + + // If normal memory > 3G. Reserve 3G ~ 4G for MMIO, GFW and others. + for (j = (MMIO_START >> PAGE_SHIFT); j < (dom_memsize >> PAGE_SHIFT); j++) + pfn_list[j] += ((1 * MEM_G) >> PAGE_SHIFT); + + // Allocate memory for VTI guest, up to VGA hole from 0xA0000-0xC0000. + rc = xc_domain_memory_populate_physmap(xc_handle, dom, + (normal_pages > VGA_START_PAGE) ? + VGA_START_PAGE : normal_pages, + 0, 0, &pfn_list[0]); + + // We're not likely to attempt to create a domain with less than + // 640k of memory, but test for completeness + if (rc == 0 && nr_pages > VGA_END_PAGE) + rc = xc_domain_memory_populate_physmap(xc_handle, dom, + normal_pages - VGA_END_PAGE, + 0, 0, &pfn_list[VGA_END_PAGE]); if (rc != 0) { - PERROR("Could not allocate memory for HVM guest.\n"); + PERROR("Could not allocate normal memory for Vti guest.\n"); + goto error_out; + } + + // We allocate additional pfn for GFW and other three pages, so + // the pfn_list is not contiguous. Due to this we must support + // old interface xc_ia64_get_pfn_list(). + // Here i = (dom_memsize >> PAGE_SHIFT) + for (j = 0; i < nr_pages - 3; i++, j++) + pfn_list[i] = (GFW_START >> PAGE_SHIFT) + j; + + rc = xc_domain_memory_populate_physmap(xc_handle, dom, GFW_PAGES, + 0, 0, &pfn_list[normal_pages]); + if (rc != 0) { + PERROR("Could not allocate GFW memory for Vti guest.\n"); + goto error_out; + } + + // Here i = (dom_memsize >> PAGE_SHIFT) + GFW_PAGES + pfn_list[i] = IO_PAGE_START >> PAGE_SHIFT; + pfn_list[i+1] = STORE_PAGE_START >> PAGE_SHIFT; + pfn_list[i+2] = BUFFER_IO_PAGE_START >> PAGE_SHIFT; + + rc = xc_domain_memory_populate_physmap(xc_handle, dom, 3, + 0, 0, &pfn_list[nr_pages - 3]); + if (rc != 0) { + PERROR("Could not allocate IO page or store page or buffer io page.\n"); goto error_out; } - /* This will creates the physmap. */ domctl.u.arch_setup.flags = XEN_DOMAINSETUP_hvm_guest; domctl.u.arch_setup.bp = 0; domctl.u.arch_setup.maxmem = 0; @@ -585,7 +652,7 @@ setup_guest(int xc_handle, uint32_t dom, unsigned long memsize, if (xc_domctl(xc_handle, &domctl)) goto error_out; - /* Load guest firmware */ + // Load guest firmware if (xc_ia64_copy_to_domain_pages(xc_handle, dom, image, (GFW_START + GFW_SIZE - image_size) >> PAGE_SHIFT, image_size >> PAGE_SHIFT)) { @@ -593,36 +660,33 @@ setup_guest(int xc_handle, uint32_t dom, unsigned long memsize, goto error_out; } - /* Hand-off state passed to guest firmware */ - if (xc_ia64_build_hob(xc_handle, dom, dom_memsize, - (unsigned long)vcpus) < 0) { + // Get number of vcpus, stored by pyxc_hvm_build() + xc_get_hvm_param(xc_handle, dom, HVM_PARAM_VCPUS, &vcpus); + + // Hand-off state passed to guest firmware + if (xc_ia64_build_hob(xc_handle, dom, dom_memsize, vcpus) < 0) { PERROR("Could not build hob\n"); goto error_out; } - /* Retrieve special pages like io, xenstore, etc. */ - if (xc_ia64_get_pfn_list(xc_handle, dom, page_array, - IO_PAGE_START>>PAGE_SHIFT, 3) != 3) { - PERROR("Could not get the page frame list"); - goto error_out; - } - xc_set_hvm_param(xc_handle, dom, - HVM_PARAM_STORE_PFN, STORE_PAGE_START>>PAGE_SHIFT); - xc_set_hvm_param(xc_handle, dom, HVM_PARAM_STORE_EVTCHN, store_evtchn); + HVM_PARAM_STORE_PFN, pfn_list[nr_pages - 2]); - *store_mfn = page_array[1]; - sp = (shared_iopage_t *)xc_map_foreign_range(xc_handle, dom, - PAGE_SIZE, PROT_READ|PROT_WRITE, page_array[0]); + // Retrieve special pages like io, xenstore, etc. + sp = (shared_iopage_t *)xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, + PROT_READ | PROT_WRITE, + pfn_list[nr_pages - 3]); if (sp == 0) goto error_out; memset(sp, 0, PAGE_SIZE); munmap(sp, PAGE_SIZE); - ioreq_buffer_page = xc_map_foreign_range(xc_handle, dom, - PAGE_SIZE, PROT_READ|PROT_WRITE, page_array[2]); + ioreq_buffer_page = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, + PROT_READ | PROT_WRITE, + pfn_list[nr_pages - 1]); memset(ioreq_buffer_page,0,PAGE_SIZE); munmap(ioreq_buffer_page, PAGE_SIZE); + free(pfn_list); return 0; error_out: @@ -630,12 +694,9 @@ error_out: } int -xc_hvm_build(int xc_handle, uint32_t domid, int memsize, - const char *image_name, unsigned int vcpus, unsigned int pae, - unsigned int acpi, unsigned int store_evtchn, - unsigned long *store_mfn) +xc_hvm_build(int xc_handle, uint32_t domid, int memsize, const char *image_name) { - struct xen_domctl launch_domctl, domctl; + struct xen_domctl launch_domctl; int rc; vcpu_guest_context_t st_ctxt, *ctxt = &st_ctxt; char *image = NULL; @@ -661,18 +722,10 @@ xc_hvm_build(int xc_handle, uint32_t domid, int memsize, return 1; } - domctl.cmd = XEN_DOMCTL_getdomaininfo; - domctl.domain = (domid_t)domid; - if (do_domctl(xc_handle, &domctl) < 0 || - (uint16_t)domctl.domain != domid) { - PERROR("Could not get info on domain"); - goto error_out; - } - memset(ctxt, 0, sizeof(*ctxt)); if (setup_guest(xc_handle, domid, (unsigned long)memsize, image, - image_size, vcpus, store_evtchn, store_mfn) < 0) { + image_size, ctxt) < 0) { ERROR("Error constructing guest OS"); goto error_out; } diff --git a/tools/libxc/ia64/xc_ia64_linux_restore.c b/tools/libxc/ia64/xc_ia64_linux_restore.c index e6aed35ead..5662805e16 100644 --- a/tools/libxc/ia64/xc_ia64_linux_restore.c +++ b/tools/libxc/ia64/xc_ia64_linux_restore.c @@ -63,7 +63,7 @@ xc_linux_restore(int xc_handle, int io_fd, uint32_t dom, { DECLARE_DOMCTL; int rc = 1, i; - unsigned long mfn, pfn; + unsigned long gmfn; unsigned long ver; /* The new domain's shared-info frame number. */ @@ -99,28 +99,29 @@ xc_linux_restore(int xc_handle, int io_fd, uint32_t dom, return 1; } - /* Get the domain's shared-info frame. */ - domctl.cmd = XEN_DOMCTL_getdomaininfo; - domctl.domain = (domid_t)dom; - if (xc_domctl(xc_handle, &domctl) < 0) { - ERROR("Could not get information on new domain"); - goto out; - } - shared_info_frame = domctl.u.getdomaininfo.shared_info_frame; - if (xc_domain_setmaxmem(xc_handle, dom, PFN_TO_KB(max_pfn)) != 0) { errno = ENOMEM; goto out; } - if (xc_domain_memory_increase_reservation(xc_handle, dom, max_pfn, - 0, 0, NULL) != 0) { - ERROR("Failed to increase reservation by %ld KB", PFN_TO_KB(max_pfn)); - errno = ENOMEM; + /* Get pages. */ + page_array = malloc(max_pfn * sizeof(unsigned long)); + if (page_array == NULL) { + ERROR("Could not allocate memory"); goto out; } - DPRINTF("Increased domain reservation by %ld KB\n", PFN_TO_KB(max_pfn)); + for ( i = 0; i < max_pfn; i++ ) + page_array[i] = i; + + if ( xc_domain_memory_populate_physmap(xc_handle, dom, max_pfn, + 0, 0, page_array) ) + { + ERROR("Failed to allocate memory for %ld KB to dom %d.\n", + PFN_TO_KB(max_pfn), dom); + goto out; + } + DPRINTF("Allocated memory by %ld KB\n", PFN_TO_KB(max_pfn)); if (!read_exact(io_fd, &domctl.u.arch_setup, sizeof(domctl.u.arch_setup))) { ERROR("read: domain setup"); @@ -138,34 +139,28 @@ xc_linux_restore(int xc_handle, int io_fd, uint32_t dom, if (xc_domctl(xc_handle, &domctl)) goto out; - /* Get pages. */ - page_array = malloc(max_pfn * sizeof(unsigned long)); - if (page_array == NULL ) { - ERROR("Could not allocate memory"); - goto out; - } - - if (xc_ia64_get_pfn_list(xc_handle, dom, page_array, - 0, max_pfn) != max_pfn) { - ERROR("Could not get the page frame list"); + /* Get the domain's shared-info frame. */ + domctl.cmd = XEN_DOMCTL_getdomaininfo; + domctl.domain = (domid_t)dom; + if (xc_domctl(xc_handle, &domctl) < 0) { + ERROR("Could not get information on new domain"); goto out; } + shared_info_frame = domctl.u.getdomaininfo.shared_info_frame; DPRINTF("Reloading memory pages: 0%%\n"); while (1) { - if (!read_exact(io_fd, &mfn, sizeof(unsigned long))) { + if (!read_exact(io_fd, &gmfn, sizeof(unsigned long))) { ERROR("Error when reading batch size"); goto out; } - if (mfn == INVALID_MFN) + if (gmfn == INVALID_MFN) break; - pfn = page_array[mfn]; + //DPRINTF("xc_linux_restore: page %lu/%lu at %lx\n", gmfn, max_pfn, pfn); - //DPRINTF("xc_linux_restore: page %lu/%lu at %lx\n", mfn, max_pfn, pfn); - - if (read_page(xc_handle, io_fd, dom, page_array[mfn]) < 0) + if (read_page(xc_handle, io_fd, dom, gmfn) < 0) goto out; } @@ -279,19 +274,19 @@ xc_linux_restore(int xc_handle, int io_fd, uint32_t dom, for (i = 0; i < MAX_VIRT_CPUS; i++) shared_info->vcpu_info[i].evtchn_pending_sel = 0; - mfn = page_array[shared_info->arch.start_info_pfn]; + gmfn = shared_info->arch.start_info_pfn; munmap (shared_info, PAGE_SIZE); /* Uncanonicalise the suspend-record frame number and poke resume rec. */ start_info = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, - PROT_READ | PROT_WRITE, mfn); + PROT_READ | PROT_WRITE, gmfn); start_info->nr_pages = max_pfn; start_info->shared_info = shared_info_frame << PAGE_SHIFT; start_info->flags = 0; - *store_mfn = page_array[start_info->store_mfn]; + *store_mfn = start_info->store_mfn; start_info->store_evtchn = store_evtchn; - *console_mfn = page_array[start_info->console.domU.mfn]; + *console_mfn = start_info->console.domU.mfn; start_info->console.domU.evtchn = console_evtchn; munmap(start_info, PAGE_SIZE); @@ -315,7 +310,8 @@ xc_linux_restore(int xc_handle, int io_fd, uint32_t dom, if ((rc != 0) && (dom != 0)) xc_domain_destroy(xc_handle, dom); - free (page_array); + if (page_array != NULL) + free(page_array); DPRINTF("Restore exit with rc=%d\n", rc); diff --git a/tools/libxc/ia64/xc_ia64_linux_save.c b/tools/libxc/ia64/xc_ia64_linux_save.c index 42ec21c802..034dc35f43 100644 --- a/tools/libxc/ia64/xc_ia64_linux_save.c +++ b/tools/libxc/ia64/xc_ia64_linux_save.c @@ -347,13 +347,13 @@ xc_linux_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters, page_array[N], N, max_pfn); mem = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, - PROT_READ|PROT_WRITE, page_array[N]); + PROT_READ|PROT_WRITE, N); if (mem == NULL) { /* The page may have move. It will be remarked dirty. FIXME: to be tracked. */ - fprintf(stderr, "cannot map page %lx: %s\n", - page_array[N], strerror (errno)); + fprintf(stderr, "cannot map mfn page %lx gpfn %lx: %s\n", + page_array[N], N, safe_strerror(errno)); continue; } diff --git a/tools/libxc/xc_core.c b/tools/libxc/xc_core.c index 550d5691e1..035bf301e5 100644 --- a/tools/libxc/xc_core.c +++ b/tools/libxc/xc_core.c @@ -140,7 +140,7 @@ static int local_file_dump(void *args, char *buffer, unsigned int length) bytes = write(da->fd, &buffer[offset], length-offset); if ( bytes <= 0 ) { - PERROR("Failed to write buffer: %s", strerror(errno)); + PERROR("Failed to write buffer"); return -errno; } } @@ -158,7 +158,7 @@ xc_domain_dumpcore(int xc_handle, if ( (da.fd = open(corename, O_CREAT|O_RDWR, S_IWUSR|S_IRUSR)) < 0 ) { - PERROR("Could not open corefile %s: %s", corename, strerror(errno)); + PERROR("Could not open corefile %s", corename); return -errno; } diff --git a/tools/libxc/xc_domain.c b/tools/libxc/xc_domain.c index b63e172a22..82aef99ccc 100644 --- a/tools/libxc/xc_domain.c +++ b/tools/libxc/xc_domain.c @@ -316,6 +316,52 @@ int xc_domain_setmaxmem(int xc_handle, return do_domctl(xc_handle, &domctl); } +#if defined(__i386__) || defined(__x86_64__) +#include +int xc_domain_set_memmap_limit(int xc_handle, + uint32_t domid, + unsigned long map_limitkb) +{ + int rc; + + struct xen_foreign_memory_map fmap = { + .domid = domid, + .map = { .nr_entries = 1 } + }; + + struct e820entry e820 = { + .addr = 0, + .size = (uint64_t)map_limitkb << 10, + .type = E820_RAM + }; + + set_xen_guest_handle(fmap.map.buffer, &e820); + + if ( lock_pages(&fmap, sizeof(fmap)) || lock_pages(&e820, sizeof(e820)) ) + { + PERROR("Could not lock memory for Xen hypercall"); + rc = -1; + goto out; + } + + rc = xc_memory_op(xc_handle, XENMEM_set_memory_map, &fmap); + + out: + unlock_pages(&fmap, sizeof(fmap)); + unlock_pages(&e820, sizeof(e820)); + return rc; +} +#else +int xc_domain_set_memmap_limit(int xc_handle, + uint32_t domid, + unsigned long map_limitkb) +{ + PERROR("Function not implemented"); + errno = ENOSYS; + return -1; +} +#endif + int xc_domain_set_time_offset(int xc_handle, uint32_t domid, int32_t time_offset_seconds) diff --git a/tools/libxc/xc_hvm_build.c b/tools/libxc/xc_hvm_build.c index 1c038755cd..21e20ffaf9 100644 --- a/tools/libxc/xc_hvm_build.c +++ b/tools/libxc/xc_hvm_build.c @@ -285,7 +285,6 @@ static int xc_hvm_build_internal(int xc_handle, if ( setup_guest(xc_handle, domid, memsize, image, image_size, &ctxt) < 0 ) { - ERROR("Error constructing guest OS"); goto error_out; } @@ -329,26 +328,30 @@ static int parseelfimage(char *elfbase, if ( !IS_ELF(*ehdr) ) { - ERROR("Kernel image does not have an ELF header."); + xc_set_error(XC_INVALID_KERNEL, + "Kernel image does not have an ELF header."); return -EINVAL; } if ( (ehdr->e_phoff + (ehdr->e_phnum * ehdr->e_phentsize)) > elfsize ) { - ERROR("ELF program headers extend beyond end of image."); + xc_set_error(XC_INVALID_KERNEL, + "ELF program headers extend beyond end of image."); return -EINVAL; } if ( (ehdr->e_shoff + (ehdr->e_shnum * ehdr->e_shentsize)) > elfsize ) { - ERROR("ELF section headers extend beyond end of image."); + xc_set_error(XC_INVALID_KERNEL, + "ELF section headers extend beyond end of image."); return -EINVAL; } /* Find the section-header strings table. */ if ( ehdr->e_shstrndx == SHN_UNDEF ) { - ERROR("ELF image has no section-header strings table (shstrtab)."); + xc_set_error(XC_INVALID_KERNEL, + "ELF image has no section-header strings table (shstrtab)."); return -EINVAL; } shdr = (Elf32_Shdr *)(elfbase + ehdr->e_shoff + @@ -370,7 +373,8 @@ static int parseelfimage(char *elfbase, (ehdr->e_entry < kernstart) || (ehdr->e_entry > kernend) ) { - ERROR("Malformed ELF image."); + xc_set_error(XC_INVALID_KERNEL, + "Malformed ELF image."); return -EINVAL; } diff --git a/tools/libxc/xc_linux.c b/tools/libxc/xc_linux.c index a70307a542..fa4927aafa 100644 --- a/tools/libxc/xc_linux.c +++ b/tools/libxc/xc_linux.c @@ -250,6 +250,15 @@ int xc_evtchn_notify(int xce_handle, evtchn_port_t port) return ioctl(xce_handle, IOCTL_EVTCHN_NOTIFY, ¬ify); } +evtchn_port_t xc_evtchn_bind_unbound_port(int xce_handle, int domid) +{ + struct ioctl_evtchn_bind_unbound_port bind; + + bind.remote_domain = domid; + + return ioctl(xce_handle, IOCTL_EVTCHN_BIND_UNBOUND_PORT, &bind); +} + evtchn_port_t xc_evtchn_bind_interdomain(int xce_handle, int domid, evtchn_port_t remote_port) { diff --git a/tools/libxc/xc_linux_build.c b/tools/libxc/xc_linux_build.c index 9bd4a5e3f4..7cbd03529b 100644 --- a/tools/libxc/xc_linux_build.c +++ b/tools/libxc/xc_linux_build.c @@ -120,7 +120,7 @@ static int probeimageformat(const char *image, if ( probe_elf(image, image_size, load_funcs) && probe_bin(image, image_size, load_funcs) ) { - ERROR( "Unrecognized image format" ); + xc_set_error(XC_INVALID_KERNEL, "Not a valid ELF or raw kernel image"); return -EINVAL; } @@ -175,11 +175,10 @@ static int load_initrd(int xc_handle, domid_t dom, return 0; } -#define alloc_pt(ltab, vltab, pltab) \ +#define alloc_pt(ltab, vltab) \ do { \ - pltab = ppt_alloc++; \ - ltab = (uint64_t)page_array[pltab] << PAGE_SHIFT; \ - pltab <<= PAGE_SHIFT; \ + ltab = ppt_alloc++; \ + ltab = (uint64_t)page_array[ltab] << PAGE_SHIFT; \ if ( vltab != NULL ) \ munmap(vltab, PAGE_SIZE); \ if ( (vltab = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, \ @@ -202,43 +201,32 @@ static int setup_pg_tables(int xc_handle, uint32_t dom, { l1_pgentry_t *vl1tab=NULL, *vl1e=NULL; l2_pgentry_t *vl2tab=NULL, *vl2e=NULL; - unsigned long l1tab = 0, pl1tab; - unsigned long l2tab = 0, pl2tab; + unsigned long l1tab = 0; + unsigned long l2tab = 0; unsigned long ppt_alloc; unsigned long count; ppt_alloc = (vpt_start - dsi_v_start) >> PAGE_SHIFT; - alloc_pt(l2tab, vl2tab, pl2tab); + alloc_pt(l2tab, vl2tab); vl2e = &vl2tab[l2_table_offset(dsi_v_start)]; - if (shadow_mode_enabled) - ctxt->ctrlreg[3] = xen_pfn_to_cr3(pl2tab >> PAGE_SHIFT); - else - ctxt->ctrlreg[3] = xen_pfn_to_cr3(l2tab >> PAGE_SHIFT); + ctxt->ctrlreg[3] = xen_pfn_to_cr3(l2tab >> PAGE_SHIFT); for ( count = 0; count < ((v_end - dsi_v_start) >> PAGE_SHIFT); count++ ) { if ( ((unsigned long)vl1e & (PAGE_SIZE-1)) == 0 ) { - alloc_pt(l1tab, vl1tab, pl1tab); + alloc_pt(l1tab, vl1tab); vl1e = &vl1tab[l1_table_offset(dsi_v_start + (count<= ((vpt_start-dsi_v_start)>>PAGE_SHIFT)) && (count < ((vpt_end -dsi_v_start)>>PAGE_SHIFT)) ) *vl1e &= ~_PAGE_RW; - } + vl1e++; } munmap(vl1tab, PAGE_SIZE); @@ -266,7 +254,7 @@ static int setup_pg_tables_pae(int xc_handle, uint32_t dom, l1_pgentry_64_t *vl1tab = NULL, *vl1e = NULL; l2_pgentry_64_t *vl2tab = NULL, *vl2e = NULL; l3_pgentry_64_t *vl3tab = NULL, *vl3e = NULL; - uint64_t l1tab, l2tab, l3tab, pl1tab, pl2tab, pl3tab; + uint64_t l1tab, l2tab, l3tab; unsigned long ppt_alloc, count, nmfn; /* First allocate page for page dir. */ @@ -287,12 +275,9 @@ static int setup_pg_tables_pae(int xc_handle, uint32_t dom, page_array[ppt_alloc] = nmfn; } - alloc_pt(l3tab, vl3tab, pl3tab); + alloc_pt(l3tab, vl3tab); vl3e = &vl3tab[l3_table_offset_pae(dsi_v_start)]; - if (shadow_mode_enabled) - ctxt->ctrlreg[3] = xen_pfn_to_cr3(pl3tab >> PAGE_SHIFT); - else - ctxt->ctrlreg[3] = xen_pfn_to_cr3(l3tab >> PAGE_SHIFT); + ctxt->ctrlreg[3] = xen_pfn_to_cr3(l3tab >> PAGE_SHIFT); for ( count = 0; count < ((v_end - dsi_v_start) >> PAGE_SHIFT); count++) { @@ -300,42 +285,33 @@ static int setup_pg_tables_pae(int xc_handle, uint32_t dom, { if ( !((unsigned long)vl2e & (PAGE_SIZE-1)) ) { - alloc_pt(l2tab, vl2tab, pl2tab); + alloc_pt(l2tab, vl2tab); vl2e = &vl2tab[l2_table_offset_pae( dsi_v_start + (count << PAGE_SHIFT))]; - if (shadow_mode_enabled) - *vl3e = pl2tab | L3_PROT; - else - *vl3e++ = l2tab | L3_PROT; + *vl3e++ = l2tab | L3_PROT; } - alloc_pt(l1tab, vl1tab, pl1tab); + alloc_pt(l1tab, vl1tab); vl1e = &vl1tab[l1_table_offset_pae( dsi_v_start + (count << PAGE_SHIFT))]; - if (shadow_mode_enabled) - *vl2e = pl1tab | L2_PROT; - else - *vl2e++ = l1tab | L2_PROT; + *vl2e++ = l1tab | L2_PROT; + } - if ( shadow_mode_enabled ) - { - *vl1e = (count << PAGE_SHIFT) | L1_PROT; - } - else - { - *vl1e = ((uint64_t)page_array[count] << PAGE_SHIFT) | L1_PROT; + *vl1e = ((uint64_t)page_array[count] << PAGE_SHIFT) | L1_PROT; + + if ( !shadow_mode_enabled ) if ( (count >= ((vpt_start-dsi_v_start)>>PAGE_SHIFT)) && (count < ((vpt_end -dsi_v_start)>>PAGE_SHIFT)) ) *vl1e &= ~_PAGE_RW; - } + vl1e++; } /* Xen requires a mid-level pgdir mapping 0xC0000000 region. */ if ( (vl3tab[3] & _PAGE_PRESENT) == 0 ) { - alloc_pt(l2tab, vl2tab, pl2tab); + alloc_pt(l2tab, vl2tab); vl3tab[3] = l2tab | L3_PROT; } @@ -371,69 +347,48 @@ static int setup_pg_tables_64(int xc_handle, uint32_t dom, l2_pgentry_t *vl2tab=NULL, *vl2e=NULL; l3_pgentry_t *vl3tab=NULL, *vl3e=NULL; l4_pgentry_t *vl4tab=NULL, *vl4e=NULL; - unsigned long l2tab = 0, pl2tab; - unsigned long l1tab = 0, pl1tab; - unsigned long l3tab = 0, pl3tab; - unsigned long l4tab = 0, pl4tab; + unsigned long l2tab = 0; + unsigned long l1tab = 0; + unsigned long l3tab = 0; + unsigned long l4tab = 0; unsigned long ppt_alloc; unsigned long count; /* First allocate page for page dir. */ ppt_alloc = (vpt_start - dsi_v_start) >> PAGE_SHIFT; - alloc_pt(l4tab, vl4tab, pl4tab); + alloc_pt(l4tab, vl4tab); vl4e = &vl4tab[l4_table_offset(dsi_v_start)]; - if (shadow_mode_enabled) - ctxt->ctrlreg[3] = xen_pfn_to_cr3(pl4tab >> PAGE_SHIFT); - else - ctxt->ctrlreg[3] = xen_pfn_to_cr3(l4tab >> PAGE_SHIFT); + ctxt->ctrlreg[3] = xen_pfn_to_cr3(l4tab >> PAGE_SHIFT); for ( count = 0; count < ((v_end-dsi_v_start)>>PAGE_SHIFT); count++) { if ( !((unsigned long)vl1e & (PAGE_SIZE-1)) ) { - alloc_pt(l1tab, vl1tab, pl1tab); + alloc_pt(l1tab, vl1tab); if ( !((unsigned long)vl2e & (PAGE_SIZE-1)) ) { - alloc_pt(l2tab, vl2tab, pl2tab); + alloc_pt(l2tab, vl2tab); if ( !((unsigned long)vl3e & (PAGE_SIZE-1)) ) { - alloc_pt(l3tab, vl3tab, pl3tab); + alloc_pt(l3tab, vl3tab); vl3e = &vl3tab[l3_table_offset(dsi_v_start + (count<= ((vpt_start-dsi_v_start)>>PAGE_SHIFT)) && (count < ((vpt_end -dsi_v_start)>>PAGE_SHIFT)) ) - { - *vl1e &= ~_PAGE_RW; - } - } + *vl1e &= ~_PAGE_RW; + vl1e++; } @@ -553,8 +508,8 @@ static int setup_guest(int xc_handle, (load_funcs.loadimage)(image, image_size, xc_handle, dom, page_array + start_page, &dsi); - *store_mfn = page_array[nr_pages - 2]; - *console_mfn = page_array[nr_pages - 1]; + *store_mfn = page_array[nr_pages - 2]; //XXX + *console_mfn = page_array[nr_pages - 1]; //XXX IPRINTF("start_info: 0x%lx at 0x%lx, " "store_mfn: 0x%lx at 0x%lx, " "console_mfn: 0x%lx at 0x%lx\n", @@ -565,6 +520,9 @@ static int setup_guest(int xc_handle, start_info = xc_map_foreign_range( xc_handle, dom, PAGE_SIZE, PROT_READ|PROT_WRITE, page_array[nr_pages - 3]); + if ( start_info == NULL ) + goto error_out; + memset(start_info, 0, sizeof(*start_info)); rc = xc_version(xc_handle, XENVER_version, NULL); sprintf(start_info->magic, "xen-%i.%i-ia64", rc >> 16, rc & (0xFFFF)); @@ -590,11 +548,26 @@ static int setup_guest(int xc_handle, ctxt->user_regs.r28 = start_info_mpa + sizeof (start_info_t); munmap(start_info, PAGE_SIZE); + /* + * shared_info is assiged into guest pseudo physical address space + * by XEN_DOMCTL_arch_setup. shared_info_frame is stale value until that. + * So passed shared_info_frame is stale. obtain the right value here. + */ + domctl.cmd = XEN_DOMCTL_getdomaininfo; + domctl.domain = (domid_t)dom; + if ( (xc_domctl(xc_handle, &domctl) < 0) || + ((uint16_t)domctl.domain != dom) ) + { + PERROR("Could not get info on domain"); + goto error_out; + } + shared_info_frame = domctl.u.getdomaininfo.shared_info_frame; + /* shared_info page starts its life empty. */ shared_info = xc_map_foreign_range( xc_handle, dom, PAGE_SIZE, PROT_READ|PROT_WRITE, shared_info_frame); - printf("shared_info = %p, err=%s frame=%lx\n", - shared_info, strerror (errno), shared_info_frame); + printf("shared_info = %p frame=%lx\n", + shared_info, shared_info_frame); //memset(shared_info, 0, PAGE_SIZE); /* Mask all upcalls... */ for ( i = 0; i < MAX_VIRT_CPUS; i++ ) @@ -602,7 +575,6 @@ static int setup_guest(int xc_handle, shared_info->arch.start_info_pfn = nr_pages - 3; munmap(shared_info, PAGE_SIZE); - free(page_array); return 0; @@ -618,17 +590,20 @@ static int compat_check(int xc_handle, struct domain_setup_info *dsi) xen_capabilities_info_t xen_caps = ""; if (xc_version(xc_handle, XENVER_capabilities, &xen_caps) != 0) { - ERROR("Cannot determine host capabilities."); + xc_set_error(XC_INVALID_KERNEL, + "Cannot determine host capabilities."); return 0; } if (strstr(xen_caps, "xen-3.0-x86_32p")) { if (dsi->pae_kernel == PAEKERN_no) { - ERROR("Non PAE-kernel on PAE host."); + xc_set_error(XC_INVALID_KERNEL, + "Non PAE-kernel on PAE host."); return 0; } } else if (dsi->pae_kernel != PAEKERN_no) { - ERROR("PAE-kernel on non-PAE host."); + xc_set_error(XC_INVALID_KERNEL, + "PAE-kernel on non-PAE host."); return 0; } @@ -752,6 +727,27 @@ static int setup_guest(int xc_handle, goto error_out; } + + if ( shadow_mode_enabled ) + { + /* + * Enable shadow translate mode. This must happen after + * populate physmap because the p2m reservation is based on + * the domains current memory allocation. + */ + if ( xc_shadow_control(xc_handle, dom, + XEN_DOMCTL_SHADOW_OP_ENABLE_TRANSLATE, + NULL, 0, NULL, 0, NULL) < 0 ) + { + PERROR("Could not enable translation mode"); + goto error_out; + } + + /* Reinitialise the gpfn->gmfn array. */ + for ( i = 0; i < nr_pages; i++ ) + page_array[i] = i; + } + rc = (load_funcs.loadimage)(image, image_size, xc_handle, dom, page_array, &dsi); @@ -953,15 +949,6 @@ static int setup_guest(int xc_handle, { struct xen_add_to_physmap xatp; - /* Enable shadow translate mode */ - if ( xc_shadow_control(xc_handle, dom, - XEN_DOMCTL_SHADOW_OP_ENABLE_TRANSLATE, - NULL, 0, NULL, 0, NULL) < 0 ) - { - PERROR("Could not enable translation mode"); - goto error_out; - } - guest_shared_info_mfn = (vsharedinfo_start-dsi.v_start) >> PAGE_SHIFT; /* Map shared info frame into guest physmap. */ @@ -1063,8 +1050,7 @@ static int setup_guest(int xc_handle, if ( pfn >= nr_pages ) goto error_out; domctl.domain = (domid_t)dom; - domctl.u.hypercall_init.gmfn = shadow_mode_enabled ? - pfn : page_array[pfn]; + domctl.u.hypercall_init.gmfn = page_array[pfn]; domctl.cmd = XEN_DOMCTL_hypercall_init; if ( xc_domctl(xc_handle, &domctl) ) goto error_out; @@ -1141,7 +1127,6 @@ static int xc_linux_build_internal(int xc_handle, console_evtchn, console_mfn, features_bitmap) < 0 ) { - ERROR("Error constructing guest OS"); goto error_out; } diff --git a/tools/libxc/xc_load_elf.c b/tools/libxc/xc_load_elf.c index 9ca9759398..f8a28fb83c 100644 --- a/tools/libxc/xc_load_elf.c +++ b/tools/libxc/xc_load_elf.c @@ -29,20 +29,46 @@ loadelfsymtab( */ #if defined(__ia64__) #define ELFCLASS ELFCLASS64 +#define ELFCLASS_DESC "64-bit" + #define ELFDATA ELFDATA2LSB +#define ELFDATA_DESC "Little-Endian" + #define ELFMACHINE EM_IA_64 +#define ELFMACHINE_DESC "ia64" + + #elif defined(__i386__) #define ELFCLASS ELFCLASS32 +#define ELFCLASS_DESC "32-bit" + #define ELFDATA ELFDATA2LSB +#define ELFDATA_DESC "Little-Endian" + #define ELFMACHINE EM_386 +#define ELFMACHINE_DESC "i386" + + #elif defined(__x86_64__) #define ELFCLASS ELFCLASS64 +#define ELFCLASS_DESC "64-bit" + #define ELFDATA ELFDATA2LSB +#define ELFDATA_DESC "Little-Endian" + #define ELFMACHINE EM_X86_64 +#define ELFMACHINE_DESC "x86_64" + + #elif defined(__powerpc__) #define ELFCLASS ELFCLASS64 +#define ELFCLASS_DESC "64-bit" + #define ELFDATA ELFDATA2MSB +#define ELFDATA_DESC "Big-Endian" + #define ELFMACHINE EM_PPC64 +#define ELFMACHINE_DESC "ppc64" #endif int probe_elf(const char *image, @@ -231,7 +257,8 @@ unsigned long long xen_elfnote_numeric(struct domain_setup_info *dsi, *defined = 1; return *(uint64_t*)ELFNOTE_DESC(note); default: - ERROR("elfnotes: unknown data size %#x for numeric type note %#x\n", + xc_set_error(XC_INVALID_KERNEL, + "elfnotes: unknown data size %#x for numeric type note %#x\n", note->descsz, type); return 0; } @@ -250,35 +277,59 @@ static int parseelfimage(const char *image, if ( !IS_ELF(*ehdr) ) { - ERROR("Kernel image does not have an ELF header."); + xc_set_error(XC_INVALID_KERNEL, + "Kernel image does not have an ELF header."); return -EINVAL; } - if ( (ehdr->e_ident[EI_CLASS] != ELFCLASS) || - (ehdr->e_machine != ELFMACHINE) || - (ehdr->e_ident[EI_DATA] != ELFDATA) || - (ehdr->e_type != ET_EXEC) ) + if (ehdr->e_machine != ELFMACHINE) { - ERROR("Kernel not a Xen-compatible Elf image."); + xc_set_error(XC_INVALID_KERNEL, + "Kernel ELF architecture '%d' does not match Xen architecture '%d' (%s)", + ehdr->e_machine, ELFMACHINE, ELFMACHINE_DESC); + return -EINVAL; + } + if (ehdr->e_ident[EI_CLASS] != ELFCLASS) + { + xc_set_error(XC_INVALID_KERNEL, + "Kernel ELF wordsize '%d' does not match Xen wordsize '%d' (%s)", + ehdr->e_ident[EI_CLASS], ELFCLASS, ELFCLASS_DESC); + return -EINVAL; + } + if (ehdr->e_ident[EI_DATA] != ELFDATA) + { + xc_set_error(XC_INVALID_KERNEL, + "Kernel ELF endianness '%d' does not match Xen endianness '%d' (%s)", + ehdr->e_ident[EI_DATA], ELFDATA, ELFDATA_DESC); + return -EINVAL; + } + if (ehdr->e_type != ET_EXEC) + { + xc_set_error(XC_INVALID_KERNEL, + "Kernel ELF type '%d' does not match Xen type '%d'", + ehdr->e_type, ET_EXEC); return -EINVAL; } if ( (ehdr->e_phoff + (ehdr->e_phnum*ehdr->e_phentsize)) > image_len ) { - ERROR("ELF program headers extend beyond end of image."); + xc_set_error(XC_INVALID_KERNEL, + "ELF program headers extend beyond end of image."); return -EINVAL; } if ( (ehdr->e_shoff + (ehdr->e_shnum*ehdr->e_shentsize)) > image_len ) { - ERROR("ELF section headers extend beyond end of image."); + xc_set_error(XC_INVALID_KERNEL, + "ELF section headers extend beyond end of image."); return -EINVAL; } /* Find the section-header strings table. */ if ( ehdr->e_shstrndx == SHN_UNDEF ) { - ERROR("ELF image has no section-header strings table (shstrtab)."); + xc_set_error(XC_INVALID_KERNEL, + "ELF image has no section-header strings table (shstrtab)."); return -EINVAL; } shdr = (Elf_Shdr *)(image + ehdr->e_shoff + @@ -325,22 +376,25 @@ static int parseelfimage(const char *image, if ( ( loader == NULL || strncmp(loader, "generic", 7) ) && ( guest_os == NULL || strncmp(guest_os, "linux", 5) ) ) { - ERROR("Will only load images built for the generic loader " - "or Linux images"); + xc_set_error(XC_INVALID_KERNEL, + "Will only load images built for the generic loader " + "or Linux images"); return -EINVAL; } if ( xen_version == NULL || strncmp(xen_version, "xen-3.0", 7) ) { - ERROR("Will only load images built for Xen v3.0"); + xc_set_error(XC_INVALID_KERNEL, + "Will only load images built for Xen v3.0"); return -EINVAL; } } else { #if defined(__x86_64__) || defined(__i386__) - ERROR("Not a Xen-ELF image: " - "No ELF notes or '__xen_guest' section found."); + xc_set_error(XC_INVALID_KERNEL, + "Not a Xen-ELF image: " + "No ELF notes or '__xen_guest' section found."); return -EINVAL; #endif } @@ -396,8 +450,9 @@ static int parseelfimage(const char *image, if ( elf_pa_off_defined && !virt_base_defined ) { - ERROR("Neither ELF_PADDR_OFFSET nor VIRT_BASE found in ELF " - " notes or __xen_guest section."); + xc_set_error(XC_INVALID_KERNEL, + "Neither ELF_PADDR_OFFSET nor VIRT_BASE found in ELF " + " notes or __xen_guest section."); return -EINVAL; } @@ -409,7 +464,8 @@ static int parseelfimage(const char *image, vaddr = phdr->p_paddr - dsi->elf_paddr_offset + dsi->v_start; if ( (vaddr + phdr->p_memsz) < vaddr ) { - ERROR("ELF program header %d is too large.", h); + xc_set_error(XC_INVALID_KERNEL, + "ELF program header %d is too large.", h); return -EINVAL; } @@ -431,7 +487,8 @@ static int parseelfimage(const char *image, (dsi->v_kernentry > kernend) || (dsi->v_start > kernstart) ) { - ERROR("ELF start or entries are out of bounds."); + xc_set_error(XC_INVALID_KERNEL, + "ELF start or entries are out of bounds."); return -EINVAL; } diff --git a/tools/libxc/xc_private.c b/tools/libxc/xc_private.c index d8dbb5e0dd..82e190989c 100644 --- a/tools/libxc/xc_private.c +++ b/tools/libxc/xc_private.c @@ -7,6 +7,82 @@ #include #include "xc_private.h" #include "xg_private.h" +#include +#include + +static __thread xc_error last_error = { XC_ERROR_NONE, ""}; +#if DEBUG +static xc_error_handler error_handler = xc_default_error_handler; +#else +static xc_error_handler error_handler = NULL; +#endif + +void xc_default_error_handler(const xc_error const *err) +{ + const char *desc = xc_error_code_to_desc(err->code); + fprintf(stderr, "ERROR %s: %s\n", desc, err->message); +} + +const xc_error const *xc_get_last_error(void) +{ + return &last_error; +} + +void xc_clear_last_error(void) +{ + last_error.code = XC_ERROR_NONE; + last_error.message[0] = '\0'; +} + +const char *xc_error_code_to_desc(int code) +{ + /* Sync to members of xc_error_code enumeration in xenctrl.h */ + switch ( code ) + { + case XC_ERROR_NONE: + return "No error details"; + case XC_INTERNAL_ERROR: + return "Internal error"; + case XC_INVALID_KERNEL: + return "Invalid kernel"; + } + + return "Unknown error code"; +} + +xc_error_handler xc_set_error_handler(xc_error_handler handler) +{ + xc_error_handler old = error_handler; + error_handler = handler; + return old; +} + + +static void _xc_set_error(int code, const char *msg) +{ + last_error.code = code; + strncpy(last_error.message, msg, XC_MAX_ERROR_MSG_LEN - 1); + last_error.message[XC_MAX_ERROR_MSG_LEN-1] = '\0'; +} + +void xc_set_error(int code, const char *fmt, ...) +{ + int saved_errno = errno; + char msg[XC_MAX_ERROR_MSG_LEN]; + va_list args; + + va_start(args, fmt); + vsnprintf(msg, XC_MAX_ERROR_MSG_LEN-1, fmt, args); + msg[XC_MAX_ERROR_MSG_LEN-1] = '\0'; + va_end(args); + + _xc_set_error(code, msg); + + errno = saved_errno; + + if ( error_handler != NULL ) + error_handler(&last_error); +} int lock_pages(void *addr, size_t len) { @@ -407,6 +483,25 @@ unsigned long xc_make_page_below_4G( return new_mfn; } +char *safe_strerror(int errcode) +{ + static __thread char errbuf[32]; + static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + char *strerror_str; + + /* + * Thread-unsafe strerror() is protected by a local mutex. We copy + * the string to a thread-private buffer before releasing the mutex. + */ + pthread_mutex_lock(&mutex); + strerror_str = strerror(errcode); + strncpy(errbuf, strerror_str, sizeof(errbuf)); + errbuf[sizeof(errbuf)-1] = '\0'; + pthread_mutex_unlock(&mutex); + + return errbuf; +} + /* * Local variables: * mode: C diff --git a/tools/libxc/xc_private.h b/tools/libxc/xc_private.h index 20f7a9b445..6dfbdead7a 100644 --- a/tools/libxc/xc_private.h +++ b/tools/libxc/xc_private.h @@ -59,24 +59,16 @@ #define PPRINTF(_f, _a...) #endif -#define ERROR(_m, _a...) \ -do { \ - int __saved_errno = errno; \ - DPRINTF("ERROR: " _m "\n" , ## _a ); \ - errno = __saved_errno; \ -} while (0) +char *safe_strerror(int errcode); +void xc_set_error(int code, const char *fmt, ...); + +#define ERROR(_m, _a...) xc_set_error(XC_INTERNAL_ERROR, _m , ## _a ) +#define PERROR(_m, _a...) xc_set_error(XC_INTERNAL_ERROR, _m " (%d = %s)", \ + ## _a , errno, safe_strerror(errno)) int lock_pages(void *addr, size_t len); void unlock_pages(void *addr, size_t len); -#define PERROR(_m, _a...) \ -do { \ - int __saved_errno = errno; \ - DPRINTF("ERROR: " _m " (%d = %s)\n" , ## _a , \ - __saved_errno, strerror(__saved_errno)); \ - errno = __saved_errno; \ -} while (0) - static inline void safe_munlock(const void *addr, size_t len) { int saved_errno = errno; diff --git a/tools/libxc/xc_solaris.c b/tools/libxc/xc_solaris.c index 902de64fa4..889db6396d 100644 --- a/tools/libxc/xc_solaris.c +++ b/tools/libxc/xc_solaris.c @@ -165,6 +165,15 @@ int xc_evtchn_notify(int xce_handle, evtchn_port_t port) return ioctl(xce_handle, IOCTL_EVTCHN_NOTIFY, ¬ify); } +evtchn_port_t xc_evtchn_bind_unbound_port(int xce_handle, int domid) +{ + struct ioctl_evtchn_bind_unbound_port bind; + + bind.remote_domain = domid; + + return ioctl(xce_handle, IOCTL_EVTCHN_BIND_UNBOUND_PORT, &bind); +} + evtchn_port_t xc_evtchn_bind_interdomain(int xce_handle, int domid, evtchn_port_t remote_port) { diff --git a/tools/libxc/xenctrl.h b/tools/libxc/xenctrl.h index c347aeebf1..13a8a4024e 100644 --- a/tools/libxc/xenctrl.h +++ b/tools/libxc/xenctrl.h @@ -382,6 +382,10 @@ int xc_sched_credit_domain_get(int xc_handle, * This function allocates an unbound port. Ports are named endpoints used for * interdomain communication. This function is most useful in opening a * well-known port within a domain to receive events on. + * + * NOTE: If you are allocating a *local* unbound port, you probably want to + * use xc_evtchn_bind_unbound_port(). This function is intended for allocating + * ports *only* during domain creation. * * @parm xc_handle a handle to an open hypervisor interface * @parm dom the ID of the local domain (the 'allocatee') @@ -415,6 +419,10 @@ int xc_domain_setmaxmem(int xc_handle, uint32_t domid, unsigned int max_memkb); +int xc_domain_set_memmap_limit(int xc_handle, + uint32_t domid, + unsigned long map_limitkb); + int xc_domain_set_time_offset(int xc_handle, uint32_t domid, int32_t time_offset_seconds); @@ -633,6 +641,12 @@ int xc_evtchn_fd(int xce_handle); */ int xc_evtchn_notify(int xce_handle, evtchn_port_t port); +/* + * Returns a new event port awaiting interdomain connection from the given + * domain ID, or -1 on failure, in which case errno will be set appropriately. + */ +evtchn_port_t xc_evtchn_bind_unbound_port(int xce_handle, int domid); + /* * Returns a new event port bound to the remote port for the given domain ID, * or -1 on failure, in which case errno will be set appropriately. @@ -665,15 +679,57 @@ evtchn_port_t xc_evtchn_pending(int xce_handle); int xc_evtchn_unmask(int xce_handle, evtchn_port_t port); int xc_hvm_set_pci_intx_level( - int xce_handle, domid_t dom, + int xc_handle, domid_t dom, uint8_t domain, uint8_t bus, uint8_t device, uint8_t intx, unsigned int level); int xc_hvm_set_isa_irq_level( - int xce_handle, domid_t dom, + int xc_handle, domid_t dom, uint8_t isa_irq, unsigned int level); int xc_hvm_set_pci_link_route( - int xce_handle, domid_t dom, uint8_t link, uint8_t isa_irq); + int xc_handle, domid_t dom, uint8_t link, uint8_t isa_irq); + + +typedef enum { + XC_ERROR_NONE = 0, + XC_INTERNAL_ERROR = 1, + XC_INVALID_KERNEL = 2, +} xc_error_code; + +#define XC_MAX_ERROR_MSG_LEN 1024 +typedef struct { + int code; + char message[XC_MAX_ERROR_MSG_LEN]; +} xc_error; + +/* + * Return a pointer to the last error. This pointer and the + * data pointed to are only valid until the next call to + * libxc. + */ +const xc_error const *xc_get_last_error(void); + +/* + * Clear the last error + */ +void xc_clear_last_error(void); + +typedef void (*xc_error_handler)(const xc_error const* err); + +/* + * The default error handler which prints to stderr + */ +void xc_default_error_handler(const xc_error const* err); + +/* + * Convert an error code into a text description + */ +const char *xc_error_code_to_desc(int code); + +/* + * Registers a callback to handle errors + */ +xc_error_handler xc_set_error_handler(xc_error_handler handler); #endif diff --git a/tools/libxen/Makefile b/tools/libxen/Makefile index 88755ab6dc..c72206442a 100644 --- a/tools/libxen/Makefile +++ b/tools/libxen/Makefile @@ -15,23 +15,58 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # +XEN_ROOT=../.. +include $(XEN_ROOT)/tools/Rules.mk + +MAJOR = 0.1 +MINOR = 0 CFLAGS = -Iinclude \ - $(shell xml2-config --cflags) \ + $(shell xml2-config --cflags) \ $(shell curl-config --cflags) \ -W -Wall -Wmissing-prototypes -Werror -std=c99 -O2 -fPIC LDFLAGS = $(shell xml2-config --libs) \ $(shell curl-config --libs) -test/test_bindings: test/test_bindings.o src/libxen.so - $(CC) $(LDFLAGS) -o $@ $< -L src -lxen +LIBXENAPI_HDRS = $(wildcard include/*.h) +LIBXENAPI_OBJS = $(patsubst %.c, %.o, $(wildcard src/*.c)) -src/libxen.so: $(patsubst %.c, %.o, $(wildcard src/*.c)) - $(CC) -shared -o $@ $^ + +.PHONY: all +all: libxenapi.so libxenapi.a + +libxenapi.so: libxenapi.so.$(MAJOR) + ln -sf $< $@ + +libxenapi.so.$(MAJOR): libxenapi.so.$(MAJOR).$(MINOR) + ln -sf $< $@ + +libxenapi.so.$(MAJOR).$(MINOR): $(LIBXENAPI_OBJS) + $(CC) $(CFLAGS) $(LDFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,libxenapi.so.$(MAJOR) $(SHLIB_CFLAGS) -o $@ $^ + +libxenapi.a: $(LIBXENAPI_OBJS) + $(AR) rcs libxenapi.a $^ + +test/test_bindings: test/test_bindings.o libxenapi.so + $(CC) $(LDFLAGS) -o $@ $< -L . -lxenapi + + +.PHONY: install +install: all + $(INSTALL_DIR) -p $(DESTDIR)/usr/include/xen/api + $(INSTALL_DIR) -p $(DESTDIR)/usr/$(LIBDIR) + $(INSTALL_PROG) libxenapi.so.$(MAJOR).$(MINOR) $(DESTDIR)/usr/$(LIBDIR) + ln -sf libxenapi.so.$(MAJOR).$(MINOR) $(DESTDIR)/usr/$(LIBDIR)/libxenapi.so.$(MAJOR) + ln -sf libxenapi.so.$(MAJOR) $(DESTDIR)/usr/$(LIBDIR)/libxenapi.so + $(INSTALL_DATA) libxenapi.a $(DESTDIR)/usr/$(LIBDIR) + for i in $(LIBXENAPI_HDRS); do \ + $(INSTALL_DATA) $$i $(DESTDIR)/usr/include/xen/api; \ + done .PHONY: clean clean: rm -f `find -name *.o` - rm -f src/libxen.so + rm -f libxenapi.so* + rm -f libxenapi.a rm -f test/test_bindings diff --git a/tools/libxen/README b/tools/libxen/README index 4aa9450381..2dd79f46fc 100644 --- a/tools/libxen/README +++ b/tools/libxen/README @@ -44,11 +44,12 @@ The bindings depend upon libxml2, the XML toolkit from the GNOME project; the test program depends upon libcurl3 also. On Debian, you need the packages libxml2-dev and libcurl3-dev. -To compile, type make. +To compile, type make. To compile the test also, type make +test/test_bindings, remembering the additional dependency. To run the test, do -LD_LIBRARY_PATH=src ./test/test_bindings +LD_LIBRARY_PATH=. ./test/test_bindings where is the fragment of the server URL that follows the http://, for example "localhost:8005/RPC2". diff --git a/tools/libxen/include/xen_console.h b/tools/libxen/include/xen_console.h new file mode 100644 index 0000000000..450c142637 --- /dev/null +++ b/tools/libxen/include/xen_console.h @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2006, XenSource Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_CONSOLE_H +#define XEN_CONSOLE_H + +#include "xen_common.h" +#include "xen_console_decl.h" +#include "xen_console_protocol.h" +#include "xen_vm_decl.h" + + +/* + * The console class. + * + * A console. + */ + + +/** + * Free the given xen_console. The given handle must have been + * allocated by this library. + */ +extern void +xen_console_free(xen_console console); + + +typedef struct xen_console_set +{ + size_t size; + xen_console *contents[]; +} xen_console_set; + +/** + * Allocate a xen_console_set of the given size. + */ +extern xen_console_set * +xen_console_set_alloc(size_t size); + +/** + * Free the given xen_console_set. The given set must have been + * allocated by this library. + */ +extern void +xen_console_set_free(xen_console_set *set); + + +typedef struct xen_console_record +{ + xen_console handle; + char *uuid; + enum xen_console_protocol protocol; + char *uri; + struct xen_vm_record_opt *vm; +} xen_console_record; + +/** + * Allocate a xen_console_record. + */ +extern xen_console_record * +xen_console_record_alloc(void); + +/** + * Free the given xen_console_record, and all referenced values. The + * given record must have been allocated by this library. + */ +extern void +xen_console_record_free(xen_console_record *record); + + +typedef struct xen_console_record_opt +{ + bool is_record; + union + { + xen_console handle; + xen_console_record *record; + } u; +} xen_console_record_opt; + +/** + * Allocate a xen_console_record_opt. + */ +extern xen_console_record_opt * +xen_console_record_opt_alloc(void); + +/** + * Free the given xen_console_record_opt, and all referenced values. + * The given record_opt must have been allocated by this library. + */ +extern void +xen_console_record_opt_free(xen_console_record_opt *record_opt); + + +typedef struct xen_console_record_set +{ + size_t size; + xen_console_record *contents[]; +} xen_console_record_set; + +/** + * Allocate a xen_console_record_set of the given size. + */ +extern xen_console_record_set * +xen_console_record_set_alloc(size_t size); + +/** + * Free the given xen_console_record_set, and all referenced values. + * The given set must have been allocated by this library. + */ +extern void +xen_console_record_set_free(xen_console_record_set *set); + + + +typedef struct xen_console_record_opt_set +{ + size_t size; + xen_console_record_opt *contents[]; +} xen_console_record_opt_set; + +/** + * Allocate a xen_console_record_opt_set of the given size. + */ +extern xen_console_record_opt_set * +xen_console_record_opt_set_alloc(size_t size); + +/** + * Free the given xen_console_record_opt_set, and all referenced + * values. The given set must have been allocated by this library. + */ +extern void +xen_console_record_opt_set_free(xen_console_record_opt_set *set); + + +/** + * Get the current state of the given console. + */ +extern bool +xen_console_get_record(xen_session *session, xen_console_record **result, xen_console console); + + +/** + * Get a reference to the object with the specified UUID. + */ +extern bool +xen_console_get_by_uuid(xen_session *session, xen_console *result, char *uuid); + + +/** + * Create a new console instance, and return its handle. + */ +extern bool +xen_console_create(xen_session *session, xen_console *result, xen_console_record *record); + + +/** + * Destroy the specified console instance. + */ +extern bool +xen_console_destroy(xen_session *session, xen_console console); + + +/** + * Get the uuid field of the given console. + */ +extern bool +xen_console_get_uuid(xen_session *session, char **result, xen_console console); + + +/** + * Get the protocol field of the given console. + */ +extern bool +xen_console_get_protocol(xen_session *session, enum xen_console_protocol *result, xen_console console); + + +/** + * Get the uri field of the given console. + */ +extern bool +xen_console_get_uri(xen_session *session, char **result, xen_console console); + + +/** + * Get the VM field of the given console. + */ +extern bool +xen_console_get_vm(xen_session *session, xen_vm *result, xen_console console); + + +#endif diff --git a/tools/libxen/include/xen_console_decl.h b/tools/libxen/include/xen_console_decl.h new file mode 100644 index 0000000000..1392164593 --- /dev/null +++ b/tools/libxen/include/xen_console_decl.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006, XenSource Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_CONSOLE_DECL_H +#define XEN_CONSOLE_DECL_H + +typedef void *xen_console; + +struct xen_console_set; +struct xen_console_record; +struct xen_console_record_set; +struct xen_console_record_opt; +struct xen_console_record_opt_set; + +#endif diff --git a/tools/libxen/include/xen_console_protocol.h b/tools/libxen/include/xen_console_protocol.h new file mode 100644 index 0000000000..e6fcf62b4a --- /dev/null +++ b/tools/libxen/include/xen_console_protocol.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2006, XenSource Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XEN_CONSOLE_PROTOCOL_H +#define XEN_CONSOLE_PROTOCOL_H + + +#include "xen_common.h" + + +enum xen_console_protocol +{ + /** + * VT100 terminal + */ + XEN_CONSOLE_PROTOCOL_VT100, + + /** + * Remote FrameBuffer protocol (as used in VNC) + */ + XEN_CONSOLE_PROTOCOL_RFB, + + /** + * Remote Desktop Protocol + */ + XEN_CONSOLE_PROTOCOL_RDP +}; + + +typedef struct xen_console_protocol_set +{ + size_t size; + enum xen_console_protocol contents[]; +} xen_console_protocol_set; + +/** + * Allocate a xen_console_protocol_set of the given size. + */ +extern xen_console_protocol_set * +xen_console_protocol_set_alloc(size_t size); + +/** + * Free the given xen_console_protocol_set. The given set must have + * been allocated by this library. + */ +extern void +xen_console_protocol_set_free(xen_console_protocol_set *set); + + +/** + * Return the name corresponding to the given code. This string must + * not be modified or freed. + */ +extern const char * +xen_console_protocol_to_string(enum xen_console_protocol val); + + +/** + * Return the correct code for the given string, or set the session + * object to failure and return an undefined value if the given string does + * not match a known code. + */ +extern enum xen_console_protocol +xen_console_protocol_from_string(xen_session *session, const char *str); + + +#endif diff --git a/tools/libxen/include/xen_console_protocol_internal.h b/tools/libxen/include/xen_console_protocol_internal.h new file mode 100644 index 0000000000..70cdae37af --- /dev/null +++ b/tools/libxen/include/xen_console_protocol_internal.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2006, XenSource Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* + * Declarations of the abstract types used during demarshalling of enum + * xen_console_protocol. Internal to this library -- do not use from outside. + */ + + +#ifndef XEN_CONSOLE_PROTOCOL_INTERNAL_H +#define XEN_CONSOLE_PROTOCOL_INTERNAL_H + + +#include "xen_internal.h" + + +extern const abstract_type xen_console_protocol_abstract_type_; +extern const abstract_type xen_console_protocol_set_abstract_type_; + + +#endif diff --git a/tools/libxen/include/xen_vm.h b/tools/libxen/include/xen_vm.h index f589a3eea5..522029f5ee 100644 --- a/tools/libxen/include/xen_vm.h +++ b/tools/libxen/include/xen_vm.h @@ -21,6 +21,7 @@ #include "xen_boot_type.h" #include "xen_common.h" +#include "xen_console_decl.h" #include "xen_cpu_feature.h" #include "xen_host_decl.h" #include "xen_int_float_map.h" @@ -96,6 +97,7 @@ typedef struct xen_vm_record enum xen_on_normal_exit actions_after_reboot; enum xen_on_normal_exit actions_after_suspend; enum xen_on_crash_behaviour actions_after_crash; + struct xen_console_record_opt_set *consoles; struct xen_vif_record_opt_set *vifs; struct xen_vbd_record_opt_set *vbds; struct xen_vtpm_record_opt_set *vtpms; @@ -400,6 +402,13 @@ extern bool xen_vm_get_actions_after_crash(xen_session *session, enum xen_on_crash_behaviour *result, xen_vm vm); +/** + * Get the consoles field of the given VM. + */ +extern bool +xen_vm_get_consoles(xen_session *session, struct xen_console_set **result, xen_vm vm); + + /** * Get the VIFs field of the given VM. */ diff --git a/tools/libxen/src/xen_common.c b/tools/libxen/src/xen_common.c index bd370bb257..ea0909d1a4 100644 --- a/tools/libxen/src/xen_common.c +++ b/tools/libxen/src/xen_common.c @@ -35,6 +35,14 @@ #include "xen_string_string_map.h" +/* + * Whether to ignore missing structure entries. This is not something we + * want to do, once the API has stabilised, as it indicates that the server is + * broken, but at the moment, complaining is just slowing development down. + */ +#define PERMISSIVE 1 + + static xmlXPathCompExprPtr responsePath = NULL; static xmlXPathCompExprPtr faultPath = NULL; @@ -510,8 +518,14 @@ static void parse_into(xen_session *s, xmlNode *value_node, xmlChar *string = string_from_value(value_node, "string"); if (string == NULL) { +#if PERMISSIVE + fprintf(stderr, + "Expected an Enum from the server, but didn't get one\n"); + ((int *)value)[slot] = 0; +#else server_error( s, "Expected an Enum from the server, but didn't get one"); +#endif } else { @@ -559,8 +573,14 @@ static void parse_into(xen_session *s, xmlNode *value_node, xmlChar *string = string_from_value(value_node, "boolean"); if (string == NULL) { +#if PERMISSIVE + fprintf(stderr, + "Expected a Bool from the server, but didn't get one\n"); + ((bool *)value)[slot] = false; +#else server_error( s, "Expected a Bool from the server, but didn't get one"); +#endif } else { @@ -772,12 +792,18 @@ static void parse_into(xen_session *s, xmlNode *value_node, if (j == seen_count) { +#if PERMISSIVE + fprintf(stderr, + "Struct did not contain expected field %s.\n", + mem->key); +#else server_error_2(s, "Struct did not contain expected field", mem->key); free(result); free(checklist); return; +#endif } } diff --git a/tools/libxen/src/xen_console.c b/tools/libxen/src/xen_console.c new file mode 100644 index 0000000000..d33d927342 --- /dev/null +++ b/tools/libxen/src/xen_console.c @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2006, XenSource Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include + +#include "xen_common.h" +#include "xen_console.h" +#include "xen_console_protocol_internal.h" +#include "xen_internal.h" +#include "xen_vm.h" + + +XEN_FREE(xen_console) +XEN_SET_ALLOC_FREE(xen_console) +XEN_ALLOC(xen_console_record) +XEN_SET_ALLOC_FREE(xen_console_record) +XEN_ALLOC(xen_console_record_opt) +XEN_RECORD_OPT_FREE(xen_console) +XEN_SET_ALLOC_FREE(xen_console_record_opt) + + +static const struct_member xen_console_record_struct_members[] = + { + { .key = "uuid", + .type = &abstract_type_string, + .offset = offsetof(xen_console_record, uuid) }, + { .key = "protocol", + .type = &xen_console_protocol_abstract_type_, + .offset = offsetof(xen_console_record, protocol) }, + { .key = "uri", + .type = &abstract_type_string, + .offset = offsetof(xen_console_record, uri) }, + { .key = "VM", + .type = &abstract_type_ref, + .offset = offsetof(xen_console_record, vm) } + }; + +const abstract_type xen_console_record_abstract_type_ = + { + .typename = STRUCT, + .struct_size = sizeof(xen_console_record), + .member_count = + sizeof(xen_console_record_struct_members) / sizeof(struct_member), + .members = xen_console_record_struct_members + }; + + +void +xen_console_record_free(xen_console_record *record) +{ + if (record == NULL) + { + return; + } + free(record->handle); + free(record->uuid); + free(record->uri); + xen_vm_record_opt_free(record->vm); + free(record); +} + + +bool +xen_console_get_record(xen_session *session, xen_console_record **result, xen_console console) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = console } + }; + + abstract_type result_type = xen_console_record_abstract_type_; + + *result = NULL; + XEN_CALL_("console.get_record"); + + if (session->ok) + { + (*result)->handle = xen_strdup_((*result)->uuid); + } + + return session->ok; +} + + +bool +xen_console_get_by_uuid(xen_session *session, xen_console *result, char *uuid) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = uuid } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("console.get_by_uuid"); + return session->ok; +} + + +bool +xen_console_create(xen_session *session, xen_console *result, xen_console_record *record) +{ + abstract_value param_values[] = + { + { .type = &xen_console_record_abstract_type_, + .u.struct_val = record } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("console.create"); + return session->ok; +} + + +bool +xen_console_destroy(xen_session *session, xen_console console) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = console } + }; + + xen_call_(session, "console.destroy", param_values, 1, NULL, NULL); + return session->ok; +} + + +bool +xen_console_get_protocol(xen_session *session, enum xen_console_protocol *result, xen_console console) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = console } + }; + + abstract_type result_type = xen_console_protocol_abstract_type_; + char *result_str = NULL; + XEN_CALL_("console.get_protocol"); + *result = xen_console_protocol_from_string(session, result_str); + return session->ok; +} + + +bool +xen_console_get_uri(xen_session *session, char **result, xen_console console) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = console } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("console.get_uri"); + return session->ok; +} + + +bool +xen_console_get_vm(xen_session *session, xen_vm *result, xen_console console) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = console } + }; + + abstract_type result_type = abstract_type_string; + + *result = NULL; + XEN_CALL_("console.get_VM"); + return session->ok; +} + + +bool +xen_console_get_uuid(xen_session *session, char **result, xen_console console) +{ + *result = session->ok ? xen_strdup_((char *)console) : NULL; + return session->ok; +} diff --git a/tools/libxen/src/xen_console_protocol.c b/tools/libxen/src/xen_console_protocol.c new file mode 100644 index 0000000000..cb87ac01c9 --- /dev/null +++ b/tools/libxen/src/xen_console_protocol.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2006, XenSource Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "xen_internal.h" +#include "xen_console_protocol.h" +#include "xen_console_protocol_internal.h" + + +/* + * Maintain this in the same order as the enum declaration! + */ +static const char *lookup_table[] = +{ + "vt100", + "rfb", + "rdp" +}; + + +extern xen_console_protocol_set * +xen_console_protocol_set_alloc(size_t size) +{ + return calloc(1, sizeof(xen_console_protocol_set) + + size * sizeof(enum xen_console_protocol)); +} + + +extern void +xen_console_protocol_set_free(xen_console_protocol_set *set) +{ + free(set); +} + + +const char * +xen_console_protocol_to_string(enum xen_console_protocol val) +{ + return lookup_table[val]; +} + + +extern enum xen_console_protocol +xen_console_protocol_from_string(xen_session *session, const char *str) +{ + return ENUM_LOOKUP(session, str, lookup_table); +} + + +const abstract_type xen_console_protocol_abstract_type_ = + { + .typename = ENUM, + .enum_marshaller = + (const char *(*)(int))&xen_console_protocol_to_string, + .enum_demarshaller = + (int (*)(xen_session *, const char *))&xen_console_protocol_from_string + }; + + +const abstract_type xen_console_protocol_set_abstract_type_ = + { + .typename = SET, + .child = &xen_console_protocol_abstract_type_ + }; + + diff --git a/tools/libxen/src/xen_vm.c b/tools/libxen/src/xen_vm.c index cd0271c71c..9743222c02 100644 --- a/tools/libxen/src/xen_vm.c +++ b/tools/libxen/src/xen_vm.c @@ -22,6 +22,7 @@ #include "xen_boot_type_internal.h" #include "xen_common.h" +#include "xen_console.h" #include "xen_cpu_feature.h" #include "xen_cpu_feature_internal.h" #include "xen_host.h" @@ -84,28 +85,28 @@ static const struct_member xen_vm_record_struct_members[] = { .key = "memory_static_min", .type = &abstract_type_int, .offset = offsetof(xen_vm_record, memory_static_min) }, - { .key = "vcpus_policy", + { .key = "VCPUs_policy", .type = &abstract_type_string, .offset = offsetof(xen_vm_record, vcpus_policy) }, - { .key = "vcpus_params", + { .key = "VCPUs_params", .type = &abstract_type_string, .offset = offsetof(xen_vm_record, vcpus_params) }, - { .key = "vcpus_number", + { .key = "VCPUs_number", .type = &abstract_type_int, .offset = offsetof(xen_vm_record, vcpus_number) }, - { .key = "vcpus_utilisation", + { .key = "VCPUs_utilisation", .type = &abstract_type_int_float_map, .offset = offsetof(xen_vm_record, vcpus_utilisation) }, - { .key = "vcpus_features_required", + { .key = "VCPUs_features_required", .type = &xen_cpu_feature_set_abstract_type_, .offset = offsetof(xen_vm_record, vcpus_features_required) }, - { .key = "vcpus_features_can_use", + { .key = "VCPUs_features_can_use", .type = &xen_cpu_feature_set_abstract_type_, .offset = offsetof(xen_vm_record, vcpus_features_can_use) }, - { .key = "vcpus_features_force_on", + { .key = "VCPUs_features_force_on", .type = &xen_cpu_feature_set_abstract_type_, .offset = offsetof(xen_vm_record, vcpus_features_force_on) }, - { .key = "vcpus_features_force_off", + { .key = "VCPUs_features_force_off", .type = &xen_cpu_feature_set_abstract_type_, .offset = offsetof(xen_vm_record, vcpus_features_force_off) }, { .key = "actions_after_shutdown", @@ -120,6 +121,9 @@ static const struct_member xen_vm_record_struct_members[] = { .key = "actions_after_crash", .type = &xen_on_crash_behaviour_abstract_type_, .offset = offsetof(xen_vm_record, actions_after_crash) }, + { .key = "consoles", + .type = &abstract_type_ref_set, + .offset = offsetof(xen_vm_record, consoles) }, { .key = "VIFs", .type = &abstract_type_ref_set, .offset = offsetof(xen_vm_record, vifs) }, @@ -205,6 +209,7 @@ xen_vm_record_free(xen_vm_record *record) xen_cpu_feature_set_free(record->vcpus_features_can_use); xen_cpu_feature_set_free(record->vcpus_features_force_on); xen_cpu_feature_set_free(record->vcpus_features_force_off); + xen_console_record_opt_set_free(record->consoles); xen_vif_record_opt_set_free(record->vifs); xen_vbd_record_opt_set_free(record->vbds); xen_vtpm_record_opt_set_free(record->vtpms); @@ -693,6 +698,23 @@ xen_vm_get_actions_after_crash(xen_session *session, enum xen_on_crash_behaviour } +bool +xen_vm_get_consoles(xen_session *session, struct xen_console_set **result, xen_vm vm) +{ + abstract_value param_values[] = + { + { .type = &abstract_type_string, + .u.string_val = vm } + }; + + abstract_type result_type = abstract_type_string_set; + + *result = NULL; + XEN_CALL_("VM.get_consoles"); + return session->ok; +} + + bool xen_vm_get_vifs(xen_session *session, struct xen_vif_set **result, xen_vm vm) { @@ -1102,7 +1124,7 @@ xen_vm_set_vcpus_policy(xen_session *session, xen_vm vm, char *policy) .u.string_val = policy } }; - xen_call_(session, "VM.set_vcpus_policy", param_values, 2, NULL, NULL); + xen_call_(session, "VM.set_VCPUs_policy", param_values, 2, NULL, NULL); return session->ok; } @@ -1118,7 +1140,7 @@ xen_vm_set_vcpus_params(xen_session *session, xen_vm vm, char *params) .u.string_val = params } }; - xen_call_(session, "VM.set_vcpus_params", param_values, 2, NULL, NULL); + xen_call_(session, "VM.set_VCPUs_params", param_values, 2, NULL, NULL); return session->ok; } @@ -1134,7 +1156,7 @@ xen_vm_set_vcpus_features_force_on(xen_session *session, xen_vm vm, struct xen_c .u.set_val = (arbitrary_set *)force_on } }; - xen_call_(session, "VM.set_vcpus_features_force_on", param_values, 2, NULL, NULL); + xen_call_(session, "VM.set_VCPUs_features_force_on", param_values, 2, NULL, NULL); return session->ok; } @@ -1150,7 +1172,7 @@ xen_vm_set_vcpus_features_force_off(xen_session *session, xen_vm vm, struct xen_ .u.set_val = (arbitrary_set *)force_off } }; - xen_call_(session, "VM.set_vcpus_features_force_off", param_values, 2, NULL, NULL); + xen_call_(session, "VM.set_VCPUs_features_force_off", param_values, 2, NULL, NULL); return session->ok; } diff --git a/tools/misc/xend b/tools/misc/xend index b0ee27f556..c35fbf00e0 100644 --- a/tools/misc/xend +++ b/tools/misc/xend @@ -109,7 +109,7 @@ def main(): daemon = SrvDaemon.instance() if not sys.argv[1:]: - print 'usage: %s {start|stop|restart}' % sys.argv[0] + print 'usage: %s {start|stop|reload|restart}' % sys.argv[0] elif sys.argv[1] == 'start': if os.uname()[0] != "SunOS": start_xenstored() @@ -123,6 +123,8 @@ def main(): return daemon.start(trace=1) elif sys.argv[1] == 'stop': return daemon.stop() + elif sys.argv[1] == 'reload': + return daemon.reloadConfig() elif sys.argv[1] == 'restart': start_xenstored() start_consoled() diff --git a/tools/pygrub/Makefile b/tools/pygrub/Makefile index da75bda4b0..2e7b3e4d21 100644 --- a/tools/pygrub/Makefile +++ b/tools/pygrub/Makefile @@ -12,11 +12,11 @@ build: ifndef XEN_PYTHON_NATIVE_INSTALL install: all CC="$(CC)" CFLAGS="$(CFLAGS)" python setup.py install --home="$(DESTDIR)/usr" --prefix="" - $(INSTALL_DIR) -p $(DESTDIR)/var/lib/xen + $(INSTALL_DIR) -p $(DESTDIR)/var/run/xend/boot else install: all CC="$(CC)" CFLAGS="$(CFLAGS)" python setup.py install --root="$(DESTDIR)" - $(INSTALL_DIR) -p $(DESTDIR)/var/lib/xen + $(INSTALL_DIR) -p $(DESTDIR)/var/run/xend/boot endif .PHONY: clean diff --git a/tools/pygrub/src/pygrub b/tools/pygrub/src/pygrub index 64a3662704..8fb43ca680 100644 --- a/tools/pygrub/src/pygrub +++ b/tools/pygrub/src/pygrub @@ -514,14 +514,16 @@ if __name__ == "__main__": fs = fsimage.open(file, offset) kernel = fs.open_file(img.kernel[1],).read() - (tfd, fn) = tempfile.mkstemp(prefix="vmlinuz.", dir="/var/lib/xen") + (tfd, fn) = tempfile.mkstemp(prefix="boot_kernel.", + dir="/var/run/xend/boot") os.write(tfd, kernel) os.close(tfd) sxp = "linux (kernel %s)" %(fn,) if img.initrd: initrd = fs.open_file(img.initrd[1],).read() - (tfd, fn) = tempfile.mkstemp(prefix="initrd.", dir="/var/lib/xen") + (tfd, fn) = tempfile.mkstemp(prefix="boot_ramdisk.", + dir="/var/run/xend/boot") os.write(tfd, initrd) os.close(tfd) sxp += "(ramdisk %s)" %(fn,) diff --git a/tools/python/scripts/xapi.domcfg.py b/tools/python/scripts/xapi.domcfg.py index 8565bd8975..d7b8ae550c 100644 --- a/tools/python/scripts/xapi.domcfg.py +++ b/tools/python/scripts/xapi.domcfg.py @@ -26,7 +26,7 @@ platform_serial = '' platform_localtime = False platform_clock_offset = False platform_enable_audio = False -builder = '' +builder = 'linux' boot_method = '' # this will remove the kernel/initrd ?? kernel_kernel = '/boot/vmlinuz-2.6.16.29-xen' kernel_initrd = '/root/initrd-2.6.16.29-xen.img' diff --git a/tools/python/scripts/xapi.py b/tools/python/scripts/xapi.py index 387ad0a487..06baeefe4c 100644 --- a/tools/python/scripts/xapi.py +++ b/tools/python/scripts/xapi.py @@ -29,17 +29,20 @@ from getpass import getpass MB = 1024 * 1024 HOST_INFO_FORMAT = '%-20s: %-50s' -VM_LIST_FORMAT = '%(name_label)-18s %(memory_actual)-5s %(vcpus_number)-5s'\ +VM_LIST_FORMAT = '%(name_label)-18s %(memory_actual)-5s %(VCPUs_number)-5s'\ ' %(power_state)-10s %(uuid)-36s' SR_LIST_FORMAT = '%(name_label)-18s %(uuid)-36s %(physical_size)-10s' \ '%(type)-10s' VDI_LIST_FORMAT = '%(name_label)-18s %(uuid)-36s %(virtual_size)-8s '\ '%(sector_size)-8s' +VBD_LIST_FORMAT = '%(name_label)-18s %(uuid)-36s %(VDI)-8s '\ + '%(image)-8s' COMMANDS = { 'host-info': ('', 'Get Xen Host Info'), 'host-set-name': ('', 'Set host name'), 'sr-list': ('', 'List all SRs'), + 'vbd-list': ('', 'List all VBDs'), 'vbd-create': (' [opts]', 'Create VBD attached to domname'), 'vdi-create': (' [opts]', 'Create a VDI'), @@ -165,6 +168,13 @@ def resolve_vm(server, session, vm_name): else: return vm_uuid[0] +def resolve_vdi(server, session, vdi_name): + vdi_uuid = execute(server.VDI.get_by_name_label, session, vdi_name) + if not vdi_uuid: + return None + else: + return vdi_uuid[0] + # # Actual commands # @@ -216,16 +226,16 @@ def xapi_vm_list(*args): if not is_long: print VM_LIST_FORMAT % {'name_label':'Name', 'memory_actual':'Mem', - 'vcpus_number': 'VCPUs', + 'VCPUs_number': 'VCPUs', 'power_state': 'State', 'uuid': 'UUID'} for uuid in vm_uuids: vm_info = execute(server.VM.get_record, session, uuid) if is_long: - vbds = vm_info['vbds'] - vifs = vm_info['vifs'] - vtpms = vm_info['vtpms'] + vbds = vm_info['VBDs'] + vifs = vm_info['VIFs'] + vtpms = vm_info['VTPMs'] vif_infos = [] vbd_infos = [] vtpm_infos = [] @@ -238,9 +248,9 @@ def xapi_vm_list(*args): for vtpm in vtpms: vtpm_info = execute(server.VTPM.get_record, session, vtpm) vtpm_infos.append(vtpm_info) - vm_info['vbds'] = vbd_infos - vm_info['vifs'] = vif_infos - vm_info['vtpms'] = vtpm_infos + vm_info['VBDs'] = vbd_infos + vm_info['VIFs'] = vif_infos + vm_info['VTPMs'] = vtpm_infos pprint(vm_info) else: print VM_LIST_FORMAT % _stringify(vm_info) @@ -276,7 +286,7 @@ def xapi_vm_start(*args): server, session = _connect() vm_uuid = resolve_vm(server, session, args[0]) print 'Starting VM %s (%s)' % (args[0], vm_uuid) - success = execute(server.VM.start, session, vm_uuid) + success = execute(server.VM.start, session, vm_uuid, False) print 'Done.' def xapi_vm_shutdown(*args): @@ -334,6 +344,22 @@ def xapi_vif_create(*args): vif_uuid = execute(server.VIF.create, session, cfg) print 'Done. (%s)' % vif_uuid +def xapi_vbd_list(*args): + server, session = _connect() + domname = args[0] + + dom_uuid = resolve_vm(server, session, domname) + vbds = execute(server.VM.get_VBDs, session, dom_uuid) + + print VBD_LIST_FORMAT % {'name_label': 'VDI Label', + 'uuid' : 'UUID', + 'VDI': 'VDI', + 'image': 'Image'} + + for vbd in vbds: + vbd_struct = execute(server.VBD.get_record, session, vbd) + print VBD_LIST_FORMAT % vbd_struct + def xapi_vdi_list(*args): server, session = _connect() vdis = execute(server.VDI.get_all, session) @@ -420,8 +446,6 @@ def xapi_vtpm_create(*args): print "Has driver type '%s'" % driver vtpm_rec = execute(server.VTPM.get_record, session, vtpm_uuid) print "Has vtpm record '%s'" % vtpm_rec - vm = execute(server.VTPM.get_VM, session, vtpm_uuid) - print "Has VM '%s'" % vm # diff --git a/tools/python/xen/lowlevel/xc/xc.c b/tools/python/xen/lowlevel/xc/xc.c index 59bdcdc4a5..32e57fe374 100644 --- a/tools/python/xen/lowlevel/xc/xc.c +++ b/tools/python/xen/lowlevel/xc/xc.c @@ -29,7 +29,7 @@ #define PKG "xen.lowlevel.xc" #define CLS "xc" -static PyObject *xc_error, *zero; +static PyObject *xc_error_obj, *zero; typedef struct { PyObject_HEAD; @@ -40,6 +40,26 @@ typedef struct { static PyObject *dom_op(XcObject *self, PyObject *args, int (*fn)(int, uint32_t)); +static PyObject *pyxc_error_to_exception(void) +{ + PyObject *pyerr; + const xc_error const *err = xc_get_last_error(); + const char *desc = xc_error_code_to_desc(err->code); + + if (err->code == XC_ERROR_NONE) + return PyErr_SetFromErrno(xc_error_obj); + + if (err->message[0] != '\0') + pyerr = Py_BuildValue("(iss)", err->code, desc, err->message); + else + pyerr = Py_BuildValue("(is)", err->code, desc); + + xc_clear_last_error(); + + PyErr_SetObject(xc_error_obj, pyerr); + + return NULL; +} static PyObject *pyxc_domain_dumpcore(XcObject *self, PyObject *args) { @@ -53,7 +73,7 @@ static PyObject *pyxc_domain_dumpcore(XcObject *self, PyObject *args) return NULL; if (xc_domain_dumpcore(self->xc_handle, dom, corefile) != 0) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); Py_INCREF(zero); return zero; @@ -101,13 +121,13 @@ static PyObject *pyxc_domain_create(XcObject *self, if ( (ret = xc_domain_create(self->xc_handle, ssidref, handle, flags, &dom)) < 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); return PyInt_FromLong(dom); out_exception: errno = EINVAL; - PyErr_SetFromErrno(xc_error); + PyErr_SetFromErrno(xc_error_obj); return NULL; } @@ -119,7 +139,7 @@ static PyObject *pyxc_domain_max_vcpus(XcObject *self, PyObject *args) return NULL; if (xc_domain_max_vcpus(self->xc_handle, dom, max) != 0) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); Py_INCREF(zero); return zero; @@ -164,7 +184,7 @@ static PyObject *pyxc_vcpu_setaffinity(XcObject *self, } if ( xc_vcpu_setaffinity(self->xc_handle, dom, vcpu, cpumap) != 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); Py_INCREF(zero); return zero; @@ -184,7 +204,7 @@ static PyObject *pyxc_domain_setcpuweight(XcObject *self, return NULL; if ( xc_domain_setcpuweight(self->xc_handle, dom, cpuweight) != 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); Py_INCREF(zero); return zero; @@ -215,14 +235,13 @@ static PyObject *pyxc_domain_sethandle(XcObject *self, PyObject *args) } if (xc_domain_sethandle(self->xc_handle, dom, handle) < 0) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); Py_INCREF(zero); return zero; out_exception: - errno = EINVAL; - PyErr_SetFromErrno(xc_error); + PyErr_SetFromErrno(xc_error_obj); return NULL; } @@ -251,7 +270,7 @@ static PyObject *pyxc_domain_getinfo(XcObject *self, if (nr_doms < 0) { free(info); - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); } list = PyList_New(nr_doms); @@ -306,10 +325,10 @@ static PyObject *pyxc_vcpu_getinfo(XcObject *self, rc = xc_vcpu_getinfo(self->xc_handle, dom, vcpu, &info); if ( rc < 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); rc = xc_vcpu_getaffinity(self->xc_handle, dom, vcpu, &cpumap); if ( rc < 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); info_dict = Py_BuildValue("{s:i,s:i,s:i,s:L,s:i}", "online", info.online, @@ -360,9 +379,7 @@ static PyObject *pyxc_linux_build(XcObject *self, ramdisk, cmdline, features, flags, store_evtchn, &store_mfn, console_evtchn, &console_mfn) != 0 ) { - if (!errno) - errno = EINVAL; - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); } return Py_BuildValue("{s:i,s:i}", "store_mfn", store_mfn, @@ -415,10 +432,13 @@ static PyObject *pyxc_hvm_build(XcObject *self, PyObject *kwds) { uint32_t dom; +#if !defined(__ia64__) struct hvm_info_table *va_hvm; uint8_t *va_map, sum; + int i; +#endif char *image; - int i, store_evtchn, memsize, vcpus = 1, pae = 0, acpi = 0, apic = 1; + int store_evtchn, memsize, vcpus = 1, pae = 0, acpi = 0, apic = 1; unsigned long store_mfn; static char *kwd_list[] = { "domid", "store_evtchn", @@ -429,15 +449,20 @@ static PyObject *pyxc_hvm_build(XcObject *self, &image, &vcpus, &pae, &acpi, &apic) ) return NULL; +#if defined(__ia64__) + /* Set vcpus to later be retrieved in setup_guest() */ + xc_set_hvm_param(self->xc_handle, dom, HVM_PARAM_VCPUS, vcpus); +#endif if ( xc_hvm_build(self->xc_handle, dom, memsize, image) != 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); +#if !defined(__ia64__) /* Set up the HVM info table. */ - va_map = xc_map_foreign_range(self->xc_handle, dom, PAGE_SIZE, + va_map = xc_map_foreign_range(self->xc_handle, dom, XC_PAGE_SIZE, PROT_READ | PROT_WRITE, HVM_INFO_PFN); if ( va_map == NULL ) - return PyErr_SetFromErrno(xc_error); + return PyErr_SetFromErrno(xc_error_obj); va_hvm = (struct hvm_info_table *)(va_map + HVM_INFO_OFFSET); memset(va_hvm, 0, sizeof(*va_hvm)); strncpy(va_hvm->signature, "HVM INFO", 8); @@ -448,10 +473,13 @@ static PyObject *pyxc_hvm_build(XcObject *self, for ( i = 0, sum = 0; i < va_hvm->length; i++ ) sum += ((uint8_t *)va_hvm)[i]; va_hvm->checksum = -sum; - munmap(va_map, PAGE_SIZE); + munmap(va_map, XC_PAGE_SIZE); +#endif xc_get_hvm_param(self->xc_handle, dom, HVM_PARAM_STORE_PFN, &store_mfn); +#if !defined(__ia64__) xc_set_hvm_param(self->xc_handle, dom, HVM_PARAM_PAE_ENABLED, pae); +#endif xc_set_hvm_param(self->xc_handle, dom, HVM_PARAM_STORE_EVTCHN, store_evtchn); @@ -472,7 +500,7 @@ static PyObject *pyxc_evtchn_alloc_unbound(XcObject *self, return NULL; if ( (port = xc_evtchn_alloc_unbound(self->xc_handle, dom, remote_dom)) < 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); return PyInt_FromLong(port); } @@ -493,7 +521,7 @@ static PyObject *pyxc_physdev_pci_access_modify(XcObject *self, ret = xc_physdev_pci_access_modify( self->xc_handle, dom, bus, dev, func, enable); if ( ret != 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); Py_INCREF(zero); return zero; @@ -515,7 +543,7 @@ static PyObject *pyxc_readconsolering(XcObject *self, ret = xc_readconsolering(self->xc_handle, &str, &count, clear); if ( ret < 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); return PyString_FromStringAndSize(str, count); } @@ -545,7 +573,7 @@ static PyObject *pyxc_physinfo(XcObject *self) int i; if ( xc_physinfo(self->xc_handle, &info) != 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); *q=0; for(i=0;ixc_handle, XENVER_version, NULL); if ( xc_version(self->xc_handle, XENVER_extraversion, &xen_extra) != 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); if ( xc_version(self->xc_handle, XENVER_compile_info, &xen_cc) != 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); if ( xc_version(self->xc_handle, XENVER_changeset, &xen_chgset) != 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); if ( xc_version(self->xc_handle, XENVER_capabilities, &xen_caps) != 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); if ( xc_version(self->xc_handle, XENVER_platform_parameters, &p_parms) != 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); sprintf(str, "virt_start=0x%lx", p_parms.virt_start); xen_pagesize = xc_version(self->xc_handle, XENVER_pagesize, NULL); if (xen_pagesize < 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); return Py_BuildValue("{s:i,s:i,s:s,s:s,s:i,s:s,s:s,s:s,s:s,s:s,s:s}", "xen_major", xen_version >> 16, @@ -634,7 +662,7 @@ static PyObject *pyxc_sedf_domain_set(XcObject *self, return NULL; if ( xc_sedf_domain_set(self->xc_handle, domid, period, slice, latency, extratime,weight) != 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); Py_INCREF(zero); return zero; @@ -651,7 +679,7 @@ static PyObject *pyxc_sedf_domain_get(XcObject *self, PyObject *args) if (xc_sedf_domain_get(self->xc_handle, domid, &period, &slice,&latency,&extratime,&weight)) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); return Py_BuildValue("{s:i,s:L,s:L,s:L,s:i,s:i}", "domid", domid, @@ -679,7 +707,7 @@ static PyObject *pyxc_shadow_control(PyObject *self, if ( xc_shadow_control(xc->xc_handle, dom, op, NULL, 0, NULL, 0, NULL) < 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); Py_INCREF(zero); return zero; @@ -709,7 +737,7 @@ static PyObject *pyxc_shadow_mem_control(PyObject *self, op = XEN_DOMCTL_SHADOW_OP_SET_ALLOCATION; } if ( xc_shadow_control(xc->xc_handle, dom, op, NULL, 0, &mb, 0, NULL) < 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); mbarg = mb; return Py_BuildValue("i", mbarg); @@ -719,7 +747,7 @@ static PyObject *pyxc_sched_id_get(XcObject *self) { int sched_id; if (xc_sched_id(self->xc_handle, &sched_id) != 0) - return PyErr_SetFromErrno(xc_error); + return PyErr_SetFromErrno(xc_error_obj); return Py_BuildValue("i", sched_id); } @@ -745,7 +773,7 @@ static PyObject *pyxc_sched_credit_domain_set(XcObject *self, sdom.cap = cap; if ( xc_sched_credit_domain_set(self->xc_handle, domid, &sdom) != 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); Py_INCREF(zero); return zero; @@ -760,7 +788,7 @@ static PyObject *pyxc_sched_credit_domain_get(XcObject *self, PyObject *args) return NULL; if ( xc_sched_credit_domain_get(self->xc_handle, domid, &sdom) != 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); return Py_BuildValue("{s:H,s:H}", "weight", sdom.weight, @@ -776,7 +804,22 @@ static PyObject *pyxc_domain_setmaxmem(XcObject *self, PyObject *args) return NULL; if (xc_domain_setmaxmem(self->xc_handle, dom, maxmem_kb) != 0) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); + + Py_INCREF(zero); + return zero; +} + +static PyObject *pyxc_domain_set_memmap_limit(XcObject *self, PyObject *args) +{ + uint32_t dom; + unsigned int maplimit_kb; + + if ( !PyArg_ParseTuple(args, "ii", &dom, &maplimit_kb) ) + return NULL; + + if ( xc_domain_set_memmap_limit(self->xc_handle, dom, maplimit_kb) != 0 ) + return pyxc_error_to_exception(); Py_INCREF(zero); return zero; @@ -803,7 +846,7 @@ static PyObject *pyxc_domain_memory_increase_reservation(XcObject *self, if ( xc_domain_memory_increase_reservation(self->xc_handle, dom, nr_extents, extent_order, address_bits, NULL) ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); Py_INCREF(zero); return zero; @@ -847,7 +890,7 @@ static PyObject *pyxc_domain_ioport_permission(XcObject *self, ret = xc_domain_ioport_permission( self->xc_handle, dom, first_port, nr_ports, allow_access); if ( ret != 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); Py_INCREF(zero); return zero; @@ -870,7 +913,7 @@ static PyObject *pyxc_domain_irq_permission(PyObject *self, ret = xc_domain_irq_permission( xc->xc_handle, dom, pirq, allow_access); if ( ret != 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); Py_INCREF(zero); return zero; @@ -893,7 +936,7 @@ static PyObject *pyxc_domain_iomem_permission(PyObject *self, ret = xc_domain_iomem_permission( xc->xc_handle, dom, first_pfn, nr_pfns, allow_access); if ( ret != 0 ) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); Py_INCREF(zero); return zero; @@ -933,7 +976,7 @@ static PyObject *dom_op(XcObject *self, PyObject *args, return NULL; if (fn(self->xc_handle, dom) != 0) - return PyErr_SetFromErrno(xc_error); + return pyxc_error_to_exception(); Py_INCREF(zero); return zero; @@ -1199,6 +1242,14 @@ static PyMethodDef pyxc_methods[] = { " maxmem_kb [int]: .\n" "Returns: [int] 0 on success; -1 on error.\n" }, + { "domain_set_memmap_limit", + (PyCFunction)pyxc_domain_set_memmap_limit, + METH_VARARGS, "\n" + "Set a domain's physical memory mappping limit\n" + " dom [int]: Identifier of domain.\n" + " map_limitkb [int]: .\n" + "Returns: [int] 0 on success; -1 on error.\n" }, + { "domain_memory_increase_reservation", (PyCFunction)pyxc_domain_memory_increase_reservation, METH_VARARGS | METH_KEYWORDS, "\n" @@ -1284,7 +1335,7 @@ static int PyXc_init(XcObject *self, PyObject *args, PyObject *kwds) { if ((self->xc_handle = xc_interface_open()) == -1) { - PyErr_SetFromErrno(xc_error); + pyxc_error_to_exception(); return -1; } @@ -1357,7 +1408,7 @@ PyMODINIT_FUNC initxc(void) if (m == NULL) return; - xc_error = PyErr_NewException(PKG ".Error", PyExc_RuntimeError, NULL); + xc_error_obj = PyErr_NewException(PKG ".Error", PyExc_RuntimeError, NULL); zero = PyInt_FromLong(0); /* KAF: This ensures that we get debug output in a timely manner. */ @@ -1367,8 +1418,8 @@ PyMODINIT_FUNC initxc(void) Py_INCREF(&PyXcType); PyModule_AddObject(m, CLS, (PyObject *)&PyXcType); - Py_INCREF(xc_error); - PyModule_AddObject(m, "Error", xc_error); + Py_INCREF(xc_error_obj); + PyModule_AddObject(m, "Error", xc_error_obj); /* Expose some libxc constants to Python */ PyModule_AddIntConstant(m, "XEN_SCHEDULER_SEDF", XEN_SCHEDULER_SEDF); diff --git a/tools/python/xen/util/bugtool.py b/tools/python/xen/util/bugtool.py index 77df9d4420..18569dcb33 100644 --- a/tools/python/xen/util/bugtool.py +++ b/tools/python/xen/util/bugtool.py @@ -43,8 +43,9 @@ TITLE_RE = re.compile(r'(.*)') FILES_TO_SEND = [ '/var/log/' + x for x in [ 'syslog', 'messages', 'debug', - 'xen/xend.log', 'xen/xend-debug.log', 'xen/xenstored-trace.log', - 'xen/xen-hotplug.log' ] ] + 'xen/xend-debug.log', 'xen/xenstored-trace.log', + 'xen/xen-hotplug.log', 'xen/xend.log' ] + + [ 'xen/xend.log.%d' % z for z in range(1,6) ] ] #FILES_TO_SEND = [ ] diff --git a/tools/python/xen/util/mkdir.py b/tools/python/xen/util/mkdir.py new file mode 100644 index 0000000000..121e498a23 --- /dev/null +++ b/tools/python/xen/util/mkdir.py @@ -0,0 +1,44 @@ +#============================================================================ +# This library is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (c) 2006 XenSource Inc. +#============================================================================ + +import errno +import os +import os.path +import stat + + +def parents(dir, perms, enforcePermissions = False): + """ + Ensure that the given directory exists, creating it if necessary, but not + complaining if it's already there. + + @param dir The directory name. + @param perms One of the stat.S_ constants. + @param enforcePermissions Enforce our ownership and the given permissions, + even if the directory pre-existed with different ones. + """ + # Catch the exception here, rather than checking for the directory's + # existence first, to avoid races. + try: + os.makedirs(dir, perms) + except OSError, exn: + if exn.args[0] != errno.EEXIST or not os.path.isdir(dir): + raise + if enforcePermissions: + os.chown(dir, os.geteuid(), os.getegid()) + os.chmod(dir, stat.S_IRWXU) diff --git a/tools/python/xen/util/xmlrpclib2.py b/tools/python/xen/util/xmlrpclib2.py index 3a3973f838..efb29d7b0e 100644 --- a/tools/python/xen/util/xmlrpclib2.py +++ b/tools/python/xen/util/xmlrpclib2.py @@ -30,6 +30,8 @@ from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler import SocketServer import xmlrpclib, socket, os, stat +import mkdir + from xen.web import connection from xen.xend.XendLogging import log @@ -234,14 +236,9 @@ class UnixXMLRPCServer(TCPXMLRPCServer): address_family = socket.AF_UNIX def __init__(self, addr, allowed, logRequests = 1): - parent = os.path.dirname(addr) - if os.path.exists(parent): - os.chown(parent, os.geteuid(), os.getegid()) - os.chmod(parent, stat.S_IRWXU) - if self.allow_reuse_address and os.path.exists(addr): - os.unlink(addr) - else: - os.makedirs(parent, stat.S_IRWXU) + mkdir.parents(os.path.dirname(addr), stat.S_IRWXU, True) + if self.allow_reuse_address and os.path.exists(addr): + os.unlink(addr) TCPXMLRPCServer.__init__(self, addr, allowed, UnixXMLRPCRequestHandler, logRequests) diff --git a/tools/python/xen/web/httpserver.py b/tools/python/xen/web/httpserver.py index bcf5dbd6b1..2326a2b875 100644 --- a/tools/python/xen/web/httpserver.py +++ b/tools/python/xen/web/httpserver.py @@ -264,7 +264,32 @@ class HttpServerRequest(http.HttpRequest): s += x + "/" self.write(' %s/' % (s, x)) self.write("") - + +class HttpServerClient: + + def __init__(self, server, sock, addr): + self.server = server + self.sock = sock + self.addr = addr + + def process(self): + thread = threading.Thread(target=self.doProcess) + thread.setDaemon(True) + thread.start() + + def doProcess(self): + try: + rp = RequestProcessor(self.server, self.sock, self.addr) + rp.process() + except SystemExit: + raise + except Exception, ex: + print 'HttpServer>processRequest> exception: ', ex + try: + self.sock.close() + except: + pass + class HttpServer: backlog = 5 @@ -286,8 +311,8 @@ class HttpServer: while not self.closed: (sock, addr) = self.accept() - self.processRequest(sock, addr) - + cl = HttpServerClient(self, sock, addr) + cl.process() def stop(self): self.close() @@ -314,19 +339,6 @@ class HttpServer: except: pass - def processRequest(self, sock, addr): - try: - rp = RequestProcessor(self, sock, addr) - rp.process() - except SystemExit: - raise - except Exception, ex: - print 'HttpServer>processRequest> exception: ', ex - try: - sock.close() - except: - pass - def getServerAddr(self): return (socket.gethostname(), self.port) diff --git a/tools/python/xen/web/unix.py b/tools/python/xen/web/unix.py index 8ec715c12f..12b6e9694e 100644 --- a/tools/python/xen/web/unix.py +++ b/tools/python/xen/web/unix.py @@ -22,6 +22,8 @@ import os.path import socket import stat +from xen.util import mkdir + import connection @@ -30,13 +32,9 @@ def bind(path): created such that only the current user may access it.""" parent = os.path.dirname(path) - if os.path.exists(parent): - os.chown(parent, os.geteuid(), os.getegid()) - os.chmod(parent, stat.S_IRWXU) - if os.path.exists(path): - os.unlink(path) - else: - os.makedirs(parent, stat.S_IRWXU) + mkdir.parents(parent, stat.S_IRWXU, True) + if os.path.exists(path): + os.unlink(path) sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sock.bind(path) diff --git a/tools/python/xen/xend/XendAPI.py b/tools/python/xen/xend/XendAPI.py index 1a4cc1efc7..e2640589ec 100644 --- a/tools/python/xen/xend/XendAPI.py +++ b/tools/python/xen/xend/XendAPI.py @@ -248,8 +248,8 @@ def valid_sr(func): # Bridge to Legacy XM API calls # ----------------------------- -def do_vm_func(fn_name, vm_ref, *args): - """Helper wrapper func to abstract away from repeative code. +def do_vm_func(fn_name, vm_ref, *args, **kwargs): + """Helper wrapper func to abstract away from repetitive code. @param fn_name: function name for XendDomain instance @type fn_name: string @@ -260,7 +260,7 @@ def do_vm_func(fn_name, vm_ref, *args): """ xendom = XendDomain.instance() fn = getattr(xendom, fn_name) - xendom.do_legacy_api_with_uuid(fn, vm_ref, *args) + xendom.do_legacy_api_with_uuid(fn, vm_ref, *args, **kwargs) return xen_api_success_void() @@ -303,8 +303,8 @@ class XendAPI: # all get_by_uuid() methods. for cls in classes.keys(): - get_by_uuid = '%s_get_by_uuid' % cls.lower() - get_uuid = '%s_get_uuid' % cls.lower() + get_by_uuid = '%s_get_by_uuid' % cls + get_uuid = '%s_get_uuid' % cls setattr(XendAPI, get_by_uuid, lambda s, sess, obj_ref: xen_api_success(obj_ref)) setattr(XendAPI, get_uuid, @@ -327,7 +327,7 @@ class XendAPI: # wrap validators around readable class attributes for attr_name in ro_attrs + rw_attrs + self.Base_attr_ro: - getter_name = '%s_get_%s' % (cls.lower(), attr_name.lower()) + getter_name = '%s_get_%s' % (cls, attr_name) try: getter = getattr(XendAPI, getter_name) for validator in validators: @@ -340,7 +340,7 @@ class XendAPI: # wrap validators around writable class attrributes for attr_name in rw_attrs + self.Base_attr_rw: - setter_name = '%s_set_%s' % (cls.lower(), attr_name.lower()) + setter_name = '%s_set_%s' % (cls, attr_name) try: setter = getattr(XendAPI, setter_name) for validator in validators: @@ -353,7 +353,8 @@ class XendAPI: # wrap validators around methods for method_name in methods + self.Base_methods: - method_full_name = '%s_%s' % (cls.lower(),method_name.lower()) + method_full_name = '%s_%s' % (cls, method_name) + try: method = getattr(XendAPI, method_full_name) for validator in validators: @@ -366,7 +367,7 @@ class XendAPI: # wrap validators around class functions for func_name in funcs + self.Base_funcs: - func_full_name = '%s_%s' % (cls.lower(), func_name.lower()) + func_full_name = '%s_%s' % (cls, func_name) try: method = getattr(XendAPI, func_full_name) method = session_required(method) @@ -379,7 +380,7 @@ class XendAPI: Base_attr_ro = ['uuid'] Base_attr_rw = [] - Base_methods = ['destroy', 'to_XML', 'get_record'] + Base_methods = ['destroy', 'get_record'] Base_funcs = ['create', 'get_by_uuid', 'get_all'] # Xen API: Class Session @@ -411,8 +412,6 @@ class XendAPI: record = {'this_host': XendNode.instance().uuid, 'this_user': auth_manager().get_user(session)} return xen_api_success(record) - def session_to_xml(self, session): - return xen_api_todo() # attributes (ro) def session_get_this_host(self, session): @@ -536,8 +535,6 @@ class XendAPI: 'features': node.get_host_cpu_features(host_cpu_ref), 'utilisation': node.get_host_cpu_load(host_cpu_ref)} return xen_api_success(record) - def host_cpu_to_xml(self, session, host_cpu_ref): - return xen_api_todo() # class methods def host_cpu_get_all(self, session): @@ -586,7 +583,7 @@ class XendAPI: 'VCPUs_policy', 'VCPUs_params', 'VCPUs_features_force_on', - 'VCPUS_features_force_off', + 'VCPUs_features_force_off', 'actions_after_shutdown', 'actions_after_reboot', 'actions_after_suspend', @@ -656,304 +653,302 @@ class XendAPI: 'otherConfig'] # attributes (ro) - def vm_get_power_state(self, session, vm_ref): + def VM_get_power_state(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success(dom.state) - def vm_get_resident_on(self, session, vm_ref): + def VM_get_resident_on(self, session, vm_ref): return xen_api_success(XendNode.instance().uuid) - def vm_get_memory_actual(self, session, vm_ref): + def VM_get_memory_actual(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_todo() # unsupported by xc - def vm_get_memory_static_max(self, session, vm_ref): + def VM_get_memory_static_max(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success(dom.get_memory_static_max()) - def vm_get_memory_static_min(self, session, vm_ref): + def VM_get_memory_static_min(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success(dom.get_memory_static_min()) - def vm_get_VCPUs_number(self, session, vm_ref): + def VM_get_VCPUs_number(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success(dom.getVCpuCount()) - def vm_get_VCPUs_utilisation(self, session, vm_ref): + def VM_get_VCPUs_utilisation(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success(dom.get_vcpus_util()) - def vm_get_VCPUs_features_required(self, session, vm_ref): + def VM_get_VCPUs_features_required(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_todo() # unsupported by xc - def vm_get_VCPUs_can_use(self, session, vm_ref): + def VM_get_VCPUs_can_use(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_todo() # unsupported by xc - def vm_get_VIFs(self, session, vm_ref): + def VM_get_VIFs(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success(dom.get_vifs()) - def vm_get_VBDs(self, session, vm_ref): + def VM_get_VBDs(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success(dom.get_vbds()) - def vm_get_VTPMs(self, session, vm_ref): + def VM_get_VTPMs(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success(dom.get_vtpms()) - def vm_get_PCI_bus(self, session, vm_ref): + def VM_get_PCI_bus(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_todo() # unsupported by xc - def vm_get_tools_version(self, session, vm_ref): + def VM_get_tools_version(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_todo() # attributes (rw) - def vm_get_name_label(self, session, vm_ref): + def VM_get_name_label(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success(dom.getName()) - def vm_get_name_description(self, session, vm_ref): + def VM_get_name_description(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_todo() - def vm_get_user_version(self, session, vm_ref): + def VM_get_user_version(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_todo() - def vm_get_is_a_template(self, session, vm_ref): + def VM_get_is_a_template(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_todo() - def vm_get_memory_dynamic_max(self, session, vm_ref): + def VM_get_memory_dynamic_max(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) - return xen_api_todo() + return xen_api_success(dom.get_memory_dynamic_max()) - def vm_get_memory_dynamic_min(self, session, vm_ref): + def VM_get_memory_dynamic_min(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) - return xen_api_todo() + return xen_api_success(dom.get_memory_dynamic_min()) - def vm_get_VCPUs_policy(self, session, vm_ref): + def VM_get_VCPUs_policy(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_todo() # need to access scheduler - def vm_get_VCPUs_params(self, session, vm_ref): + def VM_get_VCPUs_params(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_todo() # need access to scheduler - def vm_get_VCPUs_features_force_on(self, session, vm_ref): + def VM_get_VCPUs_features_force_on(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_todo() - def vm_get_VCPUs_features_force_off(self, session, vm_ref): + def VM_get_VCPUs_features_force_off(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_todo() - def vm_get_actions_after_shutdown(self, session, vm_ref): + def VM_get_actions_after_shutdown(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success(dom.get_on_shutdown()) - def vm_get_actions_after_reboot(self, session, vm_ref): + def VM_get_actions_after_reboot(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success(dom.get_on_reboot()) - def vm_get_actions_after_suspend(self, session, vm_ref): + def VM_get_actions_after_suspend(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success(dom.get_on_suspend()) - def vm_get_actions_after_crash(self, session, vm_ref): + def VM_get_actions_after_crash(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success(dom.get_on_crash()) - def vm_get_bios_boot(self, session, vm_ref): + def VM_get_bios_boot(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success(dom.get_bios_boot()) - def vm_get_platform_std_VGA(self, session, vm_ref): + def VM_get_platform_std_VGA(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_todo() - def vm_get_platform_serial(self, session, vm_ref): + def VM_get_platform_serial(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_todo() - def vm_get_platform_localtime(self, session, vm_ref): + def VM_get_platform_localtime(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_todo() - def vm_get_platform_clock_offset(self, session, vm_ref): + def VM_get_platform_clock_offset(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_todo() - def vm_get_platform_enable_audio(self, session, vm_ref): + def VM_get_platform_enable_audio(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_todo() - def vm_get_platform_keymap(self, session, vm_ref): + def VM_get_platform_keymap(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_todo() - def vm_get_builder(self, session, vm_ref): + def VM_get_builder(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) - return xen_api_todo() + return xen_api_success(dom.get_builder()) - def vm_get_boot_method(self, session, vm_ref): + def VM_get_boot_method(self, session, vm_ref): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + return xen_api_success(dom.get_boot_method()) + + def VM_get_kernel_kernel(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success('') - def vm_get_kernel_kernel(self, session, vm_ref): + def VM_get_kernel_initrd(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success('') - def vm_get_kernel_initrd(self, session, vm_ref): + def VM_get_kernel_args(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success('') - def vm_get_kernel_args(self, session, vm_ref): + def VM_get_grub_cmdline(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success('') - def vm_get_grub_cmdline(self, session, vm_ref): - dom = XendDomain.instance().get_vm_by_uuid(vm_ref) - return xen_api_success('') - - def vm_get_otherConfig(self, session, vm_ref): + def VM_get_otherConfig(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_todo() - def vm_set_name_label(self, session, vm_ref): + def VM_set_name_label(self, session, vm_ref, label): + dom = XendDomain.instance().get_vm_by_uuid(vm_ref) + dom.setName(label) + return xen_api_success_void() + + def VM_set_name_description(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success_void() - def vm_set_name_description(self, session, vm_ref): + def VM_set_user_version(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success_void() - def vm_set_user_version(self, session, vm_ref): + def VM_set_is_a_template(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success_void() - def vm_set_is_a_template(self, session, vm_ref): + def VM_set_memory_dynamic_max(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success_void() - def vm_set_memory_dynamic_max(self, session, vm_ref): + def VM_set_memory_dynamic_min(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success_void() - def vm_set_memory_dynamic_min(self, session, vm_ref): + def VM_set_VCPUs_policy(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success_void() - def vm_set_vcpus_policy(self, session, vm_ref): + def VM_set_VCPUs_params(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success_void() - def vm_set_vcpus_params(self, session, vm_ref): + def VM_set_VCPUs_features_force_on(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success_void() - def vm_set_vcpus_features_force_on(self, session, vm_ref): + def VM_set_VCPUs_features_force_off(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success_void() - def vm_set_vcpus_features_force_off(self, session, vm_ref): + def VM_set_actions_after_shutdown(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success_void() - def vm_set_actions_after_shutdown(self, session, vm_ref): + def VM_set_actions_after_reboot(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success_void() - def vm_set_actions_after_reboot(self, session, vm_ref): + def VM_set_actions_after_suspend(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success_void() - def vm_set_actions_after_suspend(self, session, vm_ref): + def VM_set_actions_after_crash(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success_void() - def vm_set_actions_after_crash(self, session, vm_ref): + def VM_set_bios_boot(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success_void() - def vm_set_bios_boot(self, session, vm_ref): + def VM_set_platform_std_VGA(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success_void() - def vm_set_platform_std_VGA(self, session, vm_ref): + def VM_set_platform_serial(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success_void() - def vm_set_platform_serial(self, session, vm_ref): + def VM_set_platform_localtime(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success_void() - def vm_set_platform_localtime(self, session, vm_ref): + def VM_set_platform_clock_offset(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success_void() - def vm_set_platform_clock_offset(self, session, vm_ref): + def VM_set_platform_enable_audio(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success_void() - def vm_set_platform_enable_audio(self, session, vm_ref): + def VM_set_builder(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success_void() - def vm_set_builder(self, session, vm_ref): + def VM_set_boot_method(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success_void() - def vm_set_boot_method(self, session, vm_ref): + def VM_set_kernel_kernel(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success_void() - def vm_set_kernel_kernel(self, session, vm_ref): + def VM_set_kernel_initrd(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success_void() - def vm_set_kernel_initrd(self, session, vm_ref): + def VM_set_kernel_args(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success_void() - def vm_set_kernel_args(self, session, vm_ref): + def VM_set_grub_cmdline(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success_void() - def vm_set_grub_cmdline(self, session, vm_ref): - dom = XendDomain.instance().get_vm_by_uuid(vm_ref) - return xen_api_success_void() - - def vm_set_otherConfig(self, session, vm_ref): + def VM_set_otherConfig(self, session, vm_ref): dom = XendDomain.instance().get_vm_by_uuid(vm_ref) return xen_api_success_void() # class methods - def vm_get_all(self, session): - refs = [d.get_uuid() for d in XendDomain.instance().list()] + def VM_get_all(self, session): + refs = [d.get_uuid() for d in XendDomain.instance().list('all')] return xen_api_success(refs) - def vm_get_by_name_label(self, session, label): + def VM_get_by_name_label(self, session, label): xendom = XendDomain.instance() dom = xendom.domain_lookup_nr(label) if dom: return xen_api_success([dom.get_uuid()]) return xen_api_error(XEND_ERROR_VM_INVALID) - def vm_create(self, session, vm_struct): + def VM_create(self, session, vm_struct): xendom = XendDomain.instance() domuuid = xendom.create_domain(vm_struct) return xen_api_success(domuuid) # object methods - def vm_to_xml(self, session, vm_ref): - return xen_api_todo() - - def vm_get_record(self, session, vm_ref): + def VM_get_record(self, session, vm_ref): xendom = XendDomain.instance() xeninfo = xendom.get_vm_by_uuid(vm_ref) if not xeninfo: @@ -969,17 +964,17 @@ class XendAPI: 'resident_on': XendNode.instance().uuid, 'memory_static_min': xeninfo.get_memory_static_min(), 'memory_static_max': xeninfo.get_memory_static_max(), - 'memory_dynamic_min': xeninfo.get_memory_static_min(), - 'memory_dynamic_max': xeninfo.get_memory_static_max(), + 'memory_dynamic_min': xeninfo.get_memory_dynamic_min(), + 'memory_dynamic_max': xeninfo.get_memory_dynamic_max(), 'memory_actual': xeninfo.get_memory_static_min(), - 'vcpus_policy': xeninfo.get_vcpus_policy(), - 'vcpus_params': xeninfo.get_vcpus_params(), - 'vcpus_number': xeninfo.getVCpuCount(), - 'vcpus_utilisation': xeninfo.get_vcpus_util(), - 'vcpus_features_required': [], - 'vcpus_features_can_use': [], - 'vcpus_features_force_on': [], - 'vcpus_features_force_off': [], + 'VCPUs_policy': xeninfo.get_vcpus_policy(), + 'VCPUs_params': xeninfo.get_vcpus_params(), + 'VCPUs_number': xeninfo.getVCpuCount(), + 'VCPUs_utilisation': xeninfo.get_vcpus_util(), + 'VCPUs_features_required': [], + 'VCPUs_features_can_use': [], + 'VCPUs_features_force_on': [], + 'VCPUs_features_force_off': [], 'actions_after_shutdown': xeninfo.get_on_shutdown(), 'actions_after_reboot': xeninfo.get_on_reboot(), 'actions_after_suspend': xeninfo.get_on_suspend(), @@ -1006,45 +1001,43 @@ class XendAPI: } return xen_api_success(record) - def vm_clean_reboot(self, session, vm_ref): + def VM_clean_reboot(self, session, vm_ref): xendom = XendDomain.instance() xeninfo = xendom.get_vm_by_uuid(vm_ref) xeninfo.shutdown("reboot") return xen_api_success_void() - def vm_clean_shutdown(self, session, vm_ref): + def VM_clean_shutdown(self, session, vm_ref): xendom = XendDomain.instance() xeninfo = xendom.get_vm_by_uuid(vm_ref) xeninfo.shutdown("poweroff") return xen_api_success_void() - def vm_clone(self, session, vm_ref): + def VM_clone(self, session, vm_ref): return xen_api_error(XEND_ERROR_UNSUPPORTED) - def vm_destroy(self, session, vm_ref): + def VM_destroy(self, session, vm_ref): return do_vm_func("domain_delete", vm_ref) - def vm_hard_reboot(self, session, vm_ref): + def VM_hard_reboot(self, session, vm_ref): return xen_api_error(XEND_ERROR_UNSUPPORTED) - def vm_hard_shutdown(self, session, vm_ref): + def VM_hard_shutdown(self, session, vm_ref): return do_vm_func("domain_destroy", vm_ref) - def vm_pause(self, session, vm_ref): + def VM_pause(self, session, vm_ref): return do_vm_func("domain_pause", vm_ref) - def vm_resume(self, session, vm_ref, start_paused): - return do_vm_func("domain_resume", vm_ref) - def vm_start(self, session, vm_ref): - return do_vm_func("domain_start", vm_ref) - def vm_suspend(self, session, vm_ref): + def VM_resume(self, session, vm_ref, start_paused): + return do_vm_func("domain_resume", vm_ref, start_paused = start_paused) + def VM_start(self, session, vm_ref, start_paused): + return do_vm_func("domain_start", vm_ref, start_paused = start_paused) + def VM_suspend(self, session, vm_ref): return do_vm_func("domain_suspend", vm_ref) - def vm_unpause(self, session, vm_ref): + def VM_unpause(self, session, vm_ref): return do_vm_func("domain_unpause", vm_ref) - # Xen API: Class VDI - # ---------------------------------------------------------------- - # TODO: NOT IMPLEMENTED. - # Xen API: Class VBD # ---------------------------------------------------------------- + # Note: accepts a non-API standard 'image' attribute to emulate + # regular xm created VBDs VBD_attr_ro = ['image', - 'IO_bandwidth_incoming_kbs', - 'IO_bandwidth_outgoing_kbs'] + 'io_read_kbs', + 'io_write_kbs'] VBD_attr_rw = ['VM', 'VDI', 'device', @@ -1053,8 +1046,10 @@ class XendAPI: VBD_attr_inst = VBD_attr_rw + ['image'] + VBD_methods = ['media_change'] + # object methods - def vbd_get_record(self, session, vbd_ref): + def VBD_get_record(self, session, vbd_ref): xendom = XendDomain.instance() vm = xendom.get_vm_with_dev_uuid('vbd', vbd_ref) if not vm: @@ -1062,10 +1057,22 @@ class XendAPI: cfg = vm.get_dev_xenapi_config('vbd', vbd_ref) if not cfg: return xen_api_error(XEND_ERROR_VBD_INVALID) - return xen_api_success(cfg) - + + valid_vbd_keys = self.VBD_attr_ro + self.VBD_attr_rw + \ + self.Base_attr_ro + self.Base_attr_rw + + return_cfg = {} + for k in cfg.keys(): + if k in valid_vbd_keys: + return_cfg[k] = cfg[k] + + return xen_api_success(return_cfg) + + def VBD_media_change(self, session, vbd_ref, vdi_ref): + return xen_api_error(XEND_ERROR_UNSUPPORTED) + # class methods - def vbd_create(self, session, vbd_struct): + def VBD_create(self, session, vbd_struct): xendom = XendDomain.instance() if not xendom.is_valid_vm(vbd_struct['VM']): return xen_api_error(XEND_ERROR_DOMAIN_INVALID) @@ -1092,22 +1099,22 @@ class XendAPI: return xen_api_success(vbd_ref) # attributes (rw) - def vbd_get_vm(self, session, vbd_ref): + def VBD_get_VM(self, session, vbd_ref): xendom = XendDomain.instance() return xen_api_success(xendom.get_dev_property('vbd', vbd_ref, 'VM')) - def vbd_get_vdi(self, session, vbd_ref): + def VBD_get_VDI(self, session, vbd_ref): return xen_api_todo() - def vbd_get_device(self, session, vbd_ref): + def VBD_get_device(self, session, vbd_ref): xendom = XendDomain.instance() return xen_api_success(xendom.get_dev_property('vbd', vbd_ref, 'device')) - def vbd_get_mode(self, session, vbd_ref): + def VBD_get_mode(self, session, vbd_ref): xendom = XendDomain.instance() return xen_api_success(xendom.get_dev_property('vbd', vbd_ref, 'mode')) - def vbd_get_driver(self, session, vbd_ref): + def VBD_get_driver(self, session, vbd_ref): xendom = XendDomain.instance() return xen_api_success(xendom.get_dev_property('vbd', vbd_ref, 'driver')) @@ -1115,10 +1122,8 @@ class XendAPI: # Xen API: Class VIF # ---------------------------------------------------------------- - VIF_attr_ro = ['network_read_kbs', - 'network_write_kbs', - 'IO_bandwidth_incoming_kbs', - 'IO_bandwidth_outgoing_kbs'] + VIF_attr_ro = ['io_read_kbs', + 'io_write_kbs'] VIF_attr_rw = ['name', 'type', 'device', @@ -1130,7 +1135,7 @@ class XendAPI: VIF_attr_inst = VIF_attr_rw # object methods - def vif_get_record(self, session, vif_ref): + def VIF_get_record(self, session, vif_ref): xendom = XendDomain.instance() vm = xendom.get_vm_with_dev_uuid('vif', vif_ref) if not vm: @@ -1138,16 +1143,19 @@ class XendAPI: cfg = vm.get_dev_xenapi_config('vif', vif_ref) if not cfg: return xen_api_error(XEND_ERROR_VIF_INVALID) + valid_vif_keys = self.VIF_attr_ro + self.VIF_attr_rw + \ self.Base_attr_ro + self.Base_attr_rw + + return_cfg = {} for k in cfg.keys(): - if k not in valid_vif_keys: - del cfg[k] + if k in valid_vif_keys: + return_cfg[k] = cfg[k] - return xen_api_success(cfg) + return xen_api_success(return_cfg) # class methods - def vif_create(self, session, vif_struct): + def VIF_create(self, session, vif_struct): xendom = XendDomain.instance() if xendom.is_valid_vm(vif_struct['VM']): dom = xendom.get_vm_by_uuid(vif_struct['VM']) @@ -1180,99 +1188,96 @@ class XendAPI: VDI_methods = ['snapshot'] VDI_funcs = ['get_by_name_label'] - def vdi_get_VBDs(self, session, vdi_ref): + def VDI_get_VBDs(self, session, vdi_ref): return xen_api_todo() - def vdi_get_physical_utilisation(self, session, vdi_ref): + def VDI_get_physical_utilisation(self, session, vdi_ref): sr = XendNode.instance().get_sr() image = sr.xen_api_get_by_uuid(vdi_ref) return xen_api_success(image.get_physical_utilisation()) - def vdi_get_sector_size(self, session, vdi_ref): + def VDI_get_sector_size(self, session, vdi_ref): sr = XendNode.instance().get_sr() image = sr.xen_api_get_by_uuid(vdi_ref) return xen_api_success(image.sector_size) - def vdi_get_type(self, session, vdi_ref): + def VDI_get_type(self, session, vdi_ref): sr = XendNode.instance().get_sr() image = sr.xen_api_get_by_uuid(vdi_ref) return xen_api_success(image.type) - def vdi_get_parent(self, session, vdi_ref): + def VDI_get_parent(self, session, vdi_ref): sr = XendNode.instance().get_sr() image = sr.xen_api_get_by_uuid(vdi_ref) return xen_api_success(image.parent) - def vdi_get_children(self, session, vdi_ref): + def VDI_get_children(self, session, vdi_ref): sr = XendNode.instance().get_sr() image = sr.xen_api_get_by_uuid(vdi_ref) return xen_api_success(image.children) - def vdi_get_name_label(self, session, vdi_ref): + def VDI_get_name_label(self, session, vdi_ref): sr = XendNode.instance().get_sr() image = sr.xen_api_get_by_uuid(vdi_ref) return xen_api_success(image.name_label) - def vdi_get_name_description(self, session, vdi_ref): + def VDI_get_name_description(self, session, vdi_ref): sr = XendNode.instance().get_sr() image = sr.xen_api_get_by_uuid(vdi_ref) return xen_api_success(image.name_description) - def vdi_get_sr(self, session, vdi_ref): + def VDI_get_SR(self, session, vdi_ref): sr = XendNode.instance().get_sr() return xen_api_success(sr.uuid) - def vdi_get_virtual_size(self, session, vdi_ref): + def VDI_get_virtual_size(self, session, vdi_ref): sr = XendNode.instance().get_sr() image = sr.xen_api_get_by_uuid(vdi_ref) return xen_api_success(image.virtual_size) - def vdi_get_sharable(self, session, vdi_ref): + def VDI_get_sharable(self, session, vdi_ref): sr = XendNode.instance().get_sr() image = sr.xen_api_get_by_uuid(vdi_ref) return xen_api_success(image.sharable) - def vdi_get_read_only(self, session, vdi_ref): + def VDI_get_read_only(self, session, vdi_ref): sr = XendNode.instance().get_sr() image = sr.xen_api_get_by_uuid(vdi_ref) return xen_api_success(image.sharable) - def vdi_set_name_label(self, session, vdi_ref, value): + def VDI_set_name_label(self, session, vdi_ref, value): sr = XendNode.instance().get_sr() image = sr.xen_api_get_by_uuid(vdi_ref) image.name_label = value return xen_api_success_void() - def vdi_set_name_description(self, session, vdi_ref, value): + def VDI_set_name_description(self, session, vdi_ref, value): sr = XendNode.instance().get_sr() image = sr.xen_api_get_by_uuid(vdi_ref) image.name_description = value return xen_api_success_void() - def vdi_set_sr(self, session, vdi_ref, value): + def VDI_set_SR(self, session, vdi_ref, value): return xen_api_error(XEND_ERROR_UNSUPPORTED) - def vdi_set_virtual_size(self, session, vdi_ref, value): + def VDI_set_virtual_size(self, session, vdi_ref, value): return xen_api_error(XEND_ERROR_UNSUPPORTED) - def vdi_set_sharable(self, session, vdi_ref, value): + def VDI_set_sharable(self, session, vdi_ref, value): return xen_api_todo() - def vdi_set_read_only(self, session, vdi_ref, value): + def VDI_set_read_only(self, session, vdi_ref, value): return xen_api_todo() # Object Methods - def vdi_snapshot(self, session, vdi_ref): + def VDI_snapshot(self, session, vdi_ref): return xen_api_todo() - def vdi_destroy(self, session, vdi_ref): + def VDI_destroy(self, session, vdi_ref): sr = XendNode.instance().get_sr() sr.destroy_image(vdi_ref) return xen_api_success_void() - def vdi_to_xml(self, session, vdi_ref): - return xen_api_todo() - - def vdi_get_record(self, session, vdi_ref): + def VDI_get_record(self, session, vdi_ref): sr = XendNode.instance().get_sr() image = sr.xen_api_get_by_uuid(vdi_ref) if image: @@ -1295,7 +1300,7 @@ class XendAPI: return xen_api_error(XEND_ERROR_VDI_INVALID) # Class Functions - def vdi_create(self, session, vdi_struct): + def VDI_create(self, session, vdi_struct): sr = XendNode.instance().get_sr() sr_ref = vdi_struct['SR'] if sr.uuid != sr_ref: @@ -1304,11 +1309,11 @@ class XendAPI: vdi_uuid = sr.create_image(vdi_struct) return xen_api_success(vdi_uuid) - def vdi_get_all(self, session): + def VDI_get_all(self, session): sr = XendNode.instance().get_sr() return xen_api_success(sr.list_images()) - def vdi_get_by_name_label(self, session, name): + def VDI_get_by_name_label(self, session, name): sr = XendNode.instance().get_sr() image_uuid = sr.xen_api_get_by_name_label(name) if image_uuid: @@ -1329,7 +1334,7 @@ class XendAPI: VTPM_attr_inst = VTPM_attr_rw # object methods - def vtpm_get_record(self, session, vtpm_ref): + def VTPM_get_record(self, session, vtpm_ref): xendom = XendDomain.instance() vm = xendom.get_vm_with_dev_uuid('vtpm', vtpm_ref) if not vm: @@ -1346,7 +1351,7 @@ class XendAPI: return xen_api_success(cfg) # Class Functions - def vtpm_get_instance(self, session, vtpm_ref): + def VTPM_get_instance(self, session, vtpm_ref): xendom = XendDomain.instance() vm = xendom.get_vm_with_dev_uuid('vtpm', vtpm_ref) if not vm: @@ -1360,7 +1365,7 @@ class XendAPI: instance = -1 return xen_api_success(instance) - def vtpm_get_driver(self, session, vtpm_ref): + def VTPM_get_driver(self, session, vtpm_ref): xendom = XendDomain.instance() vm = xendom.get_vm_with_dev_uuid('vtpm', vtpm_ref) if not vm: @@ -1374,7 +1379,7 @@ class XendAPI: driver = "Unknown" return xen_api_success(driver) - def vtpm_get_backend(self, session, vtpm_ref): + def VTPM_get_backend(self, session, vtpm_ref): xendom = XendDomain.instance() vm = xendom.get_vm_with_dev_uuid('vtpm', vtpm_ref) if not vm: @@ -1388,12 +1393,12 @@ class XendAPI: backend = "Domain-0" return xen_api_success(backend) - def vtpm_get_VM(self, session, vtpm_ref): + def VTPM_get_VM(self, session, vtpm_ref): xendom = XendDomain.instance() return xen_api_success(xendom.get_dev_property('vtpm', vtpm_ref, 'VM')) # class methods - def vtpm_create(self, session, vtpm_struct): + def VTPM_create(self, session, vtpm_struct): xendom = XendDomain.instance() if xendom.is_valid_vm(vtpm_struct['VM']): dom = xendom.get_vm_by_uuid(vtpm_struct['VM']) @@ -1429,32 +1434,30 @@ class XendAPI: SR_funcs = ['get_by_name_label'] # Class Functions - def sr_get_all(self, session): + def SR_get_all(self, session): sr = XendNode.instance().get_sr() return xen_api_success([sr.uuid]) - def sr_get_by_name_label(self, session, label): + def SR_get_by_name_label(self, session, label): sr = XendNode.instance().get_sr() if sr.name_label != label: return xen_api_error(XEND_ERROR_SR_INVALID) return xen_api_success([sr.uuid]) - def sr_create(self, session): + def SR_create(self, session): return xen_api_error(XEND_ERROR_UNSUPPORTED) - def sr_get_by_uuid(self, session): + def SR_get_by_uuid(self, session): return xen_api_success(XendNode.instance().get_sr().uuid) # Class Methods - def sr_clone(self, session, sr_ref): - return xen_api_error(XEND_ERROR_UNSUPPORTED) - def sr_destroy(self, session, sr_ref): + def SR_clone(self, session, sr_ref): return xen_api_error(XEND_ERROR_UNSUPPORTED) - def sr_to_xml(self, session, sr_ref): - return xen_api_todo() + def SR_destroy(self, session, sr_ref): + return xen_api_error(XEND_ERROR_UNSUPPORTED) - def sr_get_record(self, session, sr_ref): + def SR_get_record(self, session, sr_ref): sr = XendNode.instance().get_sr() return xen_api_success({ 'uuid': sr.uuid, @@ -1469,44 +1472,44 @@ class XendAPI: }) # Attribute acceess - def sr_get_vdis(self, session, sr_ref): + def SR_get_VDIs(self, session, sr_ref): sr = XendNode.instance().get_sr() return xen_api_success(sr.list_images()) - def sr_get_virtual_allocation(self, session, sr_ref): + def SR_get_virtual_allocation(self, session, sr_ref): sr = XendNode.instance().get_sr() return sr.used_space_bytes() - def sr_get_physical_utilisation(self, session, sr_ref): + def SR_get_physical_utilisation(self, session, sr_ref): sr = XendNode.instance().get_sr() return sr.used_space_bytes() - def sr_get_physical_size(self, session, sr_ref): + def SR_get_physical_size(self, session, sr_ref): sr = XendNode.instance().get_sr() return sr.total_space_bytes() - def sr_get_type(self, session, sr_ref): + def SR_get_type(self, session, sr_ref): sr = XendNode.instance().get_sr() return xen_api_success(sr.type) - def sr_get_location(self, session, sr_ref): + def SR_get_location(self, session, sr_ref): sr = XendNode.instance().get_sr() return xen_api_success(sr.location) - def sr_get_name_label(self, session, sr_ref): + def SR_get_name_label(self, session, sr_ref): sr = XendNode.instance().get_sr() return xen_api_success(sr.name_label) - def sr_get_name_description(self, session, sr_ref): + def SR_get_name_description(self, session, sr_ref): sr = XendNode.instance().get_sr() return xen_api_success(sr.name_description) - def sr_set_name_label(self, session, sr_ref, value): + def SR_set_name_label(self, session, sr_ref, value): sr = XendNode.instance().get_sr() sr.name_label = value return xen_api_success_void() - def sr_set_name_description(self, session, sr_ref, value): + def SR_set_name_description(self, session, sr_ref, value): sr = XendNode.instance().get_sr() sr.name_description = value return xen_api_success_void() @@ -1525,24 +1528,24 @@ if __name__ == "__main__": methods = getattr(XendAPI, '%s_methods' % cls, []) funcs = getattr(XendAPI, '%s_funcs' % cls, []) - ref = '%s_ref' % cls.lower() + ref = '%s_ref' % cls for attr_name in ro_attrs + rw_attrs + XendAPI.Base_attr_ro: - getter_name = '%s_get_%s' % (cls.lower(), attr_name.lower()) + getter_name = '%s_get_%s' % (cls, attr_name) output('def %s(self, session, %s):' % (getter_name, ref)) output(' return xen_api_todo()') for attr_name in rw_attrs + XendAPI.Base_attr_rw: - setter_name = '%s_set_%s' % (cls.lower(), attr_name.lower()) + setter_name = '%s_set_%s' % (cls, attr_name) output('def %s(self, session, %s, value):' % (setter_name, ref)) output(' return xen_api_todo()') for method_name in methods + XendAPI.Base_methods: - method_full_name = '%s_%s' % (cls.lower(),method_name.lower()) + method_full_name = '%s_%s' % (cls,method_name) output('def %s(self, session, %s):' % (method_full_name, ref)) output(' return xen_api_todo()') for func_name in funcs + XendAPI.Base_funcs: - func_full_name = '%s_%s' % (cls.lower(), func_name.lower()) + func_full_name = '%s_%s' % (cls, func_name) output('def %s(self, session):' % func_full_name) output(' return xen_api_todo()') diff --git a/tools/python/xen/xend/XendAuthSessions.py b/tools/python/xen/xend/XendAuthSessions.py index 616724b96b..4081177597 100644 --- a/tools/python/xen/xend/XendAuthSessions.py +++ b/tools/python/xen/xend/XendAuthSessions.py @@ -55,7 +55,7 @@ class XendAuthSessions: @return: Session UUID """ if self.is_authorized(username, password): - return login_unconditionally(username) + return self.login_unconditionally(username) raise XendError("Login failed") diff --git a/tools/python/xen/xend/XendBootloader.py b/tools/python/xen/xend/XendBootloader.py index 879b39806d..48ebb55e66 100644 --- a/tools/python/xen/xend/XendBootloader.py +++ b/tools/python/xen/xend/XendBootloader.py @@ -12,11 +12,12 @@ # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # -import os, select, errno +import os, select, errno, stat import random import shlex from xen.xend import sxp +from xen.util import mkdir from XendLogging import log from XendError import VmError @@ -37,11 +38,16 @@ def bootloader(blexec, disk, quiet = 0, blargs = None, imgcfg = None): log.error(msg) raise VmError(msg) + mkdir.parents("/var/run/xend/boot/", stat.S_IRWXU) + while True: - fifo = "/var/lib/xen/xenbl.%s" % random.randint(0, 32000) - if not os.path.exists(fifo): - break - os.mkfifo(fifo, 0600) + fifo = "/var/run/xend/boot/xenbl.%s" %(random.randint(0, 32000),) + try: + os.mkfifo(fifo, 0600) + except OSError, e: + if (e.errno != errno.EEXIST): + raise + break child = os.fork() if (not child): diff --git a/tools/python/xen/xend/XendCheckpoint.py b/tools/python/xen/xend/XendCheckpoint.py index b7c964d70a..21a323cdcc 100644 --- a/tools/python/xen/xend/XendCheckpoint.py +++ b/tools/python/xen/xend/XendCheckpoint.py @@ -18,8 +18,8 @@ import xen.lowlevel.xc from xen.xend import balloon, sxp from xen.xend.XendError import XendError, VmError from xen.xend.XendLogging import log -from xen.xend.XendConstants import * from xen.xend.XendConfig import XendConfig +from xen.xend.XendConstants import * SIGNATURE = "LinuxGuestRecord" XC_SAVE = "xc_save" @@ -137,7 +137,6 @@ def restore(xd, fd, dominfo = None, paused = False): vmconfig = p.get_val() if dominfo: - dominfo.update(XendConfig(sxp = vmconfig), refresh = False) dominfo.resume() else: dominfo = xd.restore_(vmconfig) diff --git a/tools/python/xen/xend/XendConfig.py b/tools/python/xen/xend/XendConfig.py index da7898c72c..804a50209c 100644 --- a/tools/python/xen/xend/XendConfig.py +++ b/tools/python/xen/xend/XendConfig.py @@ -31,143 +31,230 @@ from xen.xend.XendConstants import DOM_STATE_HALTED XendConfig API XendConfig will try to mirror as closely the Xen API VM Struct - providing a backwards compatibility mode for SXP dumping, loading. + with extra parameters for those options that are not supported. """ +def reverse_dict(adict): + """Return the reverse mapping of a dictionary.""" + return dict([(v, k) for k, v in adict.items()]) -LEGACY_CFG_TO_XENAPI_CFG = { +def bool0(v): + return v != '0' and bool(v) + +# Recursively copy a data struct, scrubbing out VNC passwords. +# Will scrub any dict entry with a key of 'vncpasswd' or any +# 2-element list whose first member is 'vncpasswd'. It will +# also scrub a string matching '(vncpasswd XYZ)'. Everything +# else is no-op passthrough +def scrub_password(data): + if type(data) == dict or type(data) == XendConfig: + scrubbed = {} + for key in data.keys(): + if key == "vncpasswd": + scrubbed[key] = "XXXXXXXX" + else: + scrubbed[key] = scrub_password(data[key]) + return scrubbed + elif type(data) == list: + if len(data) == 2 and type(data[0]) == str and data[0] == 'vncpasswd': + return ['vncpasswd', 'XXXXXXXX'] + else: + scrubbed = [] + for entry in data: + scrubbed.append(scrub_password(entry)) + return scrubbed + elif type(data) == tuple: + scrubbed = [] + for entry in data: + scrubbed.append(scrub_password(entry)) + return tuple(scrubbed) + elif type(data) == str: + return re.sub(r'\(vncpasswd\s+[^\)]+\)','(vncpasswd XXXXXX)', data) + else: + return data + + +# Mapping from XendConfig configuration keys to the old +# legacy configuration keys that map directly. + +XENAPI_CFG_TO_LEGACY_CFG = { 'uuid': 'uuid', - 'vcpus': 'vcpus_number', - 'maxmem': 'memory_static_max', - 'memory': 'memory_static_min', - 'name': 'name_label', - 'on_poweroff': 'actions_after_shutdown', - 'on_reboot': 'actions_after_reboot', - 'on_crash': 'actions_after_crash', - 'bootloader': 'boot_method', - 'kernel_kernel': 'kernel_kernel', - 'kernel_initrd': 'kernel_initrd', - 'kernel_args': 'kernel_args', - } + 'vcpus_number': 'vcpus', + 'memory_static_min': 'memory', + 'memory_static_max': 'maxmem', + 'name_label': 'name', + 'actions_after_shutdown': 'on_poweroff', + 'actions_after_reboot': 'on_reboot', + 'actions_after_crash': 'on_crash', + 'platform_localtime': 'localtime', +} -XENAPI_CFG_CUSTOM_TRANSLATE = [ - 'vifs', - 'vbds', - ] +LEGACY_CFG_TO_XENAPI_CFG = reverse_dict(XENAPI_CFG_TO_LEGACY_CFG) +# Mapping from XendConfig configuration keys to the old +# legacy configuration keys that are found in the 'image' +# SXP object. XENAPI_HVM_CFG = { - 'platform_std_vga': 'std-vga', + 'platform_std_vga': 'stdvga', 'platform_serial' : 'serial', 'platform_localtime': 'localtime', 'platform_enable_audio': 'soundhw', 'platform_keymap' : 'keymap', } -XENAPI_UNSUPPORTED_IN_LEGACY_CFG = [ - 'name_description', - 'user_version', - 'is_a_template', - 'memory_dynamic_min', - 'memory_dynamic_max', - 'memory_actual', - 'vcpus_policy', - 'vcpus_params', - 'vcpus_features_required', - 'vcpus_features_can_use', - 'vcpus_features_force_on', - 'vcpus_features_force_off', - 'actions_after_suspend', - 'bios_boot', - 'platform_std_vga', - 'platform_serial', - 'platform_localtime', - 'platform_clock_offset', - 'platform_enable_audio', - 'platform_keymap', - 'builder', - 'grub_cmdline', - 'pci_bus', - 'otherconfig' - ] +# List of XendConfig configuration keys that have no equivalent +# in the old world. +XENAPI_CFG_TYPES = { + 'uuid': str, + 'power_state': str, + 'name_label': str, + 'name_description': str, + 'user_version': str, + 'is_a_template': bool0, + 'resident_on': str, + 'memory_static_min': int, + 'memory_static_max': int, + 'memory_dynamic_min': int, + 'memory_dynamic_max': int, + 'memory_actual': int, + 'vcpus_policy': str, + 'vcpus_params': str, + 'vcpus_number': int, + 'vcpus_features_required': list, + 'vcpus_features_can_use': list, + 'vcpus_features_force_on': list, + 'vcpus_features_force_off': list, + 'actions_after_shutdown': str, + 'actions_after_reboot': str, + 'actions_after_suspend': str, + 'actions_after_crash': str, + 'tpm_instance': int, + 'tpm_backend': int, + 'bios_boot': str, + 'platform_std_vga': bool0, + 'platform_serial': str, + 'platform_localtime': bool0, + 'platform_clock_offset': bool0, + 'platform_enable_audio': bool0, + 'platform_keymap': str, + 'boot_method': str, + 'builder': str, + 'kernel_kernel': str, + 'kernel_initrd': str, + 'kernel_args': str, + 'grub_cmdline': str, + 'pci_bus': str, + 'tools_version': dict, + 'otherconfig': dict, +} -# configuration params that need to be converted to ints -# since the XMLRPC transport for Xen API does not use -# 32 bit ints but string representation of 64 bit ints. -XENAPI_INT_CFG = [ - 'user_version', - 'vcpus_number', - 'memory_static_min', - 'memory_static_max', - 'memory_dynamic_min', - 'memory_dynamic_max', - 'tpm_instance', - 'tpm_backend', -] +# List of legacy configuration keys that have no equivalent in the +# Xen API, but are still stored in XendConfig. -## -## Xend Configuration Parameters -## - - -# All parameters of VMs that may be configured on-the-fly, or at start-up. -VM_CONFIG_ENTRIES = [ - ('name', str), - ('on_crash', str), - ('on_poweroff', str), - ('on_reboot', str), - ('on_xend_start', str), - ('on_xend_stop', str), +LEGACY_UNSUPPORTED_BY_XENAPI_CFG = [ + # roundtripped (dynamic, unmodified) + 'shadow_memory', + 'security', + 'vcpu_avail', + 'cpu_weight', + 'cpu_cap', + 'bootloader', + 'bootloader_args', + 'features', + # read/write + 'on_xend_start', + 'on_xend_stop', + # read-only + 'domid', + 'start_time', + 'cpu_time', + 'online_vcpus', + # write-once + 'cpu', + 'cpus', ] -# All entries written to the store. This is VM_CONFIG_ENTRIES, plus those -# entries written to the store that cannot be reconfigured on-the-fly. -VM_STORE_ENTRIES = [ - ('uuid', str), - ('vcpus', int), - ('vcpu_avail', int), - ('memory', int), - ('maxmem', int), - ('start_time', float), +LEGACY_CFG_TYPES = { + 'uuid': str, + 'name': str, + 'vcpus': int, + 'vcpu_avail': int, + 'memory': int, + 'shadow_memory': int, + 'maxmem': int, + 'start_time': float, + 'cpu_cap': int, + 'cpu_weight': int, + 'cpu_time': float, + 'bootloader': str, + 'bootloader_args': str, + 'features': str, + 'localtime': int, + 'name': str, + 'on_poweroff': str, + 'on_reboot': str, + 'on_crash': str, + 'on_xend_stop': str, + 'on_xend_start': str, + 'online_vcpus': int, +} + +# Values that should be stored in xenstore's /vm/ that is used +# by Xend. Used in XendDomainInfo to restore running VM state from +# xenstore. +LEGACY_XENSTORE_VM_PARAMS = [ + 'uuid', + 'name', + 'vcpus', + 'vcpu_avail', + 'memory', + 'shadow_memory', + 'maxmem', + 'start_time', + 'name', + 'on_poweroff', + 'on_crash', + 'on_reboot', + 'on_xend_start', + 'on_xend_stop', ] -VM_STORED_ENTRIES = VM_CONFIG_ENTRIES + VM_STORE_ENTRIES - -# Configuration entries that we expect to round-trip -- be read from the -# config file or xc, written to save-files (i.e. through sxpr), and reused as -# config on restart or restore, all without munging. Some configuration -# entries are munged for backwards compatibility reasons, or because they -# don't come out of xc in the same form as they are specified in the config -# file, so those are handled separately. - -ROUNDTRIPPING_CONFIG_ENTRIES = [ - ('uuid', str), - ('vcpus', int), - ('vcpu_avail', int), - ('cpu_cap', int), - ('cpu_weight', int), - ('memory', int), - ('shadow_memory', int), - ('maxmem', int), - ('bootloader', str), - ('bootloader_args', str), - ('features', str), - ('localtime', int), -] -ROUNDTRIPPING_CONFIG_ENTRIES += VM_CONFIG_ENTRIES - -## Static Configuration - -STATIC_CONFIG_ENTRIES = [ - ('cpu', int), - ('cpus', str), - ('image', list), - ('security', list), # TODO: what if null? +LEGACY_IMAGE_CFG = [ + ('root', str), + ('ip', str), + ('nographic', int), + ('vnc', int), + ('sdl', int), + ('vncdisplay', int), + ('vncunused', int), + ('vncpasswd', str), ] -DEPRECATED_ENTRIES = [ - ('restart', str), +LEGACY_IMAGE_HVM_CFG = [ + ('device_model', str), + ('display', str), + ('xauthority', str), + ('vncconsole', int), + ('pae', int), + ('apic', int), +] + +LEGACY_IMAGE_HVM_DEVICES_CFG = [ + ('acpi', int), + ('boot', str), + ('fda', str), + ('fdb', str), + ('isa', str), + ('keymap', str), + ('localtime', str), + ('serial', str), + ('stdvga', int), + ('soundhw', str), + ('usb', str), + ('usbdevice', str), + ('vcpus', int), ] ## @@ -178,245 +265,182 @@ CONFIG_RESTART_MODES = ('restart', 'destroy', 'preserve', 'rename-restart') CONFIG_OLD_DOM_STATES = ('running', 'blocked', 'paused', 'shutdown', 'crashed', 'dying') -## -## Defaults -## - -def DEFAULT_VCPUS(info): - if 'max_vcpu_id' in info: return int(info['max_vcpu_id']) + 1 - else: return 1 - -DEFAULT_CONFIGURATION = ( - ('uuid', lambda info: uuid.createString()), - ('name', lambda info: 'Domain-' + info['uuid']), - - ('on_poweroff', lambda info: 'destroy'), - ('on_reboot', lambda info: 'restart'), - ('on_crash', lambda info: 'restart'), - ('features', lambda info: ''), - - - ('memory', lambda info: 0), - ('shadow_memory',lambda info: 0), - ('maxmem', lambda info: 0), - ('bootloader', lambda info: None), - ('bootloader_args', lambda info: None), - ('backend', lambda info: []), - ('device', lambda info: {}), - ('image', lambda info: None), - ('security', lambda info: []), - ('on_xend_start', lambda info: 'ignore'), - ('on_xend_stop', lambda info: 'ignore'), - - ('cpus', lambda info: []), - ('cpu_cap', lambda info: 0), - ('cpu_weight', lambda info: 256), - ('vcpus', lambda info: DEFAULT_VCPUS(info)), - ('online_vcpus', lambda info: info['vcpus']), - ('max_vcpu_id', lambda info: info['vcpus']-1), - ('vcpu_avail', lambda info: (1</image value and xm list.""" + image = [self['image'].get('type', 'linux')] + if self.has_key('kernel_kernel'): + image.append(['kernel', self['kernel_kernel']]) + if self.has_key('kernel_initrd') and self['kernel_initrd']: + image.append(['ramdisk', self['kernel_initrd']]) + if self.has_key('kernel_args') and self['kernel_args']: + image.append(['args', self['kernel_args']]) + + for arg, conv in LEGACY_IMAGE_CFG: + if self['image'].has_key(arg): + image.append([arg, self['image'][arg]]) + + if 'hvm' in self['image']: + for arg, conv in LEGACY_IMAGE_HVM_CFG: + if self['image']['hvm'].get(arg): + image.append([arg, self['image']['hvm'][arg]]) + + if 'hvm' in self['image'] and 'devices' in self['image']['hvm']: + for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG: + if self['image']['hvm']['devices'].get(arg): + image.append([arg, + self['image']['hvm']['devices'][arg]]) + + return image + + def update_with_image_sxp(self, image_sxp): + # Convert Legacy "image" config to Xen API kernel_* + # configuration + self['kernel_kernel'] = sxp.child_value(image_sxp, 'kernel','') + self['kernel_initrd'] = sxp.child_value(image_sxp, 'ramdisk','') + kernel_args = sxp.child_value(image_sxp, 'args', '') + + # attempt to extract extra arguments from SXP config + arg_ip = sxp.child_value(image_sxp, 'ip') + if arg_ip and not re.search(r'ip=[^ ]+', kernel_args): + kernel_args += ' ip=%s' % arg_ip + arg_root = sxp.child_value(image_sxp, 'root') + if arg_root and not re.search(r'root=', kernel_args): + kernel_args += ' root=%s' % arg_root + self['kernel_args'] = kernel_args + + # Store image SXP in python dictionary format + image = {} + image['type'] = sxp.name(image_sxp) + for arg, conv in LEGACY_IMAGE_CFG: + val = sxp.child_value(image_sxp, arg, None) + if val != None: + image[arg] = conv(val) + + image_hvm = {} + for arg, conv in LEGACY_IMAGE_HVM_CFG: + val = sxp.child_value(image_sxp, arg, None) + if val != None: + image_hvm[arg] = conv(val) + + image_hvm_devices = {} + for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG: + val = sxp.child_value(image_sxp, arg, None) + if val != None: + image_hvm_devices[arg] = conv(val) + + if image_hvm or image_hvm_devices: + image['hvm'] = image_hvm + image['hvm']['devices'] = image_hvm_devices + + self['image'] = image + + for apikey, imgkey in XENAPI_HVM_CFG.items(): + val = sxp.child_value(image_sxp, imgkey, None) + if val != None: + type_conv = XENAPI_CFG_TYPES[apikey] + if callable(conv): + self[apikey] = type_conv(val) + else: + self[apikey] = val + + # # debugging # diff --git a/tools/python/xen/xend/XendConstants.py b/tools/python/xen/xend/XendConstants.py index 96f7a22d7e..e07fa127f1 100644 --- a/tools/python/xen/xend/XendConstants.py +++ b/tools/python/xen/xend/XendConstants.py @@ -34,6 +34,8 @@ DOMAIN_SHUTDOWN_REASONS = { DOMAIN_CRASH : "crash", DOMAIN_HALT : "halt" } +REVERSE_DOMAIN_SHUTDOWN_REASONS = \ + dict([(y, x) for x, y in DOMAIN_SHUTDOWN_REASONS.items()]) restart_modes = [ "restart", diff --git a/tools/python/xen/xend/XendDevices.py b/tools/python/xen/xend/XendDevices.py index ce91e28271..7b362ad727 100644 --- a/tools/python/xen/xend/XendDevices.py +++ b/tools/python/xen/xend/XendDevices.py @@ -19,7 +19,7 @@ # A collection of DevControllers # -from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif +from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif, vfbif from xen.xend.server.BlktapController import BlktapController class XendDevices: @@ -41,6 +41,8 @@ class XendDevices: 'irq': irqif.IRQController, 'usb': usbif.UsbifController, 'tap': BlktapController, + 'vfb': vfbif.VfbifController, + 'vkbd': vfbif.VkbdifController, } #@classmethod diff --git a/tools/python/xen/xend/XendDomain.py b/tools/python/xen/xend/XendDomain.py index 4d84bc14e6..4b11634403 100644 --- a/tools/python/xen/xend/XendDomain.py +++ b/tools/python/xen/xend/XendDomain.py @@ -23,6 +23,7 @@ """ import os +import stat import shutil import socket import threading @@ -44,7 +45,7 @@ from xen.xend.XendDevices import XendDevices from xen.xend.xenstore.xstransact import xstransact from xen.xend.xenstore.xswatch import xswatch -from xen.util import security +from xen.util import mkdir, security from xen.xend import uuid xc = xen.lowlevel.xc.xc() @@ -99,11 +100,7 @@ class XendDomain: """Singleton initialisation function.""" dom_path = self._managed_path() - try: - os.stat(dom_path) - except OSError: - log.info("Making %s", dom_path) - os.makedirs(dom_path, 0755) + mkdir.parents(dom_path, stat.S_IRWXU) xstransact.Mkdir(XS_VMROOT) xstransact.SetPermissions(XS_VMROOT, {'dom': DOM0_ID}) @@ -184,7 +181,7 @@ class XendDomain: if not dom_uuid: continue - dom_name = dom.get('name', 'Domain-%s' % dom_uuid) + dom_name = dom.get('name_label', 'Domain-%s' % dom_uuid) try: running_dom = self.domain_lookup_nr(dom_name) if not running_dom: @@ -271,25 +268,17 @@ class XendDomain: domains_dir = self._managed_path() dom_uuid = dominfo.get_uuid() domain_config_dir = self._managed_path(dom_uuid) - - # make sure the domain dir exists - if not os.path.exists(domains_dir): - os.makedirs(domains_dir, 0755) - elif not os.path.isdir(domains_dir): - log.error("xend_domain_dir is not a directory.") - raise XendError("Unable to save managed configuration " - "because %s is not a directory." % - domains_dir) - - if not os.path.exists(domain_config_dir): + + def make_or_raise(path): try: - os.makedirs(domain_config_dir, 0755) - except IOError: - log.exception("Failed to create directory: %s" % - domain_config_dir) - raise XendError("Failed to create directory: %s" % - domain_config_dir) - + mkdir.parents(path, stat.S_IRWXU) + except: + log.exception("%s could not be created." % path) + raise XendError("%s could not be created." % path) + + make_or_raise(domains_dir) + make_or_raise(domain_config_dir) + try: sxp_cache_file = open(self._managed_config_path(dom_uuid),'w') prettyprint(dominfo.sxpr(), sxp_cache_file, width = 78) @@ -423,7 +412,6 @@ class XendDomain: self._remove_domain(dom, domid) - def _add_domain(self, info): """Add a domain to the list of running domains @@ -433,6 +421,11 @@ class XendDomain: """ log.debug("Adding Domain: %s" % info.getDomid()) self.domains[info.getDomid()] = info + + # update the managed domains with a new XendDomainInfo object + # if we are keeping track of it. + if info.get_uuid() in self.managed_domains: + self._managed_domain_register(info) def _remove_domain(self, info, domid = None): """Remove the domain from the list of running domains @@ -647,18 +640,23 @@ class XendDomain: def is_valid_dev(self, klass, dev_uuid): return (self.get_vm_with_dev_uuid(klass, dev_uuid) != None) - def do_legacy_api_with_uuid(self, fn, vm_uuid, *args): + def do_legacy_api_with_uuid(self, fn, vm_uuid, *args, **kwargs): + dom = self.uuid_to_dom(vm_uuid) + fn(dom, *args, **kwargs) + + def uuid_to_dom(self, vm_uuid): self.domains_lock.acquire() try: for domid, dom in self.domains.items(): - if dom.get_uuid == vm_uuid: - return fn(domid, *args) + if dom.get_uuid() == vm_uuid: + return domid if vm_uuid in self.managed_domains: domid = self.managed_domains[vm_uuid].getDomid() - if domid == None: - domid = self.managed_domains[vm_uuid].getName() - return fn(domid, *args) + if domid is None: + return self.managed_domains[vm_uuid].getName() + else: + return domid raise XendInvalidDomain("Domain does not exist") finally: @@ -669,7 +667,7 @@ class XendDomain: self.domains_lock.acquire() try: try: - xeninfo = XendConfig(xenapi_vm = xenapi_vm) + xeninfo = XendConfig(xapi = xenapi_vm) dominfo = XendDomainInfo.createDormant(xeninfo) log.debug("Creating new managed domain: %s: %s" % (dominfo.getName(), dominfo.get_uuid())) @@ -795,7 +793,7 @@ class XendDomain: raise XendError("can't write guest state file %s: %s" % (path, ex[1])) - def domain_resume(self, domname): + def domain_resume(self, domname, start_paused = False): """Resumes a domain that is persistently managed by Xend. @param domname: Domain Name @@ -815,7 +813,7 @@ class XendDomain: raise XendError("Cannot save privileged domain %s" % domname) if dominfo.state != DOM_STATE_HALTED: - raise XendError("Cannot suspend domain that is not running.") + raise XendError("Cannot resume domain that is not halted.") dom_uuid = dominfo.get_uuid() chkpath = self._managed_check_point_path(dom_uuid) @@ -827,7 +825,8 @@ class XendDomain: log.debug('Current DomainInfo state: %d' % dominfo.state) XendCheckpoint.restore(self, os.open(chkpath, os.O_RDONLY), - dominfo) + dominfo, + paused = start_paused) self._add_domain(dominfo) os.unlink(chkpath) except OSError, ex: @@ -872,8 +871,8 @@ class XendDomain: self.domains_lock.acquire() try: try: - xeninfo = XendConfig(sxp = config) - dominfo = XendDomainInfo.createDormant(xeninfo) + domconfig = XendConfig(sxp_obj = config) + dominfo = XendDomainInfo.createDormant(domconfig) log.debug("Creating new managed domain: %s" % dominfo.getName()) self._managed_domain_register(dominfo) @@ -886,7 +885,7 @@ class XendDomain: finally: self.domains_lock.release() - def domain_start(self, domid): + def domain_start(self, domid, start_paused = True): """Start a managed domain @require: Domain must not be running. @@ -911,6 +910,9 @@ class XendDomain: self._add_domain(dominfo) finally: self.domains_lock.release() + dominfo.waitForDevices() + if not start_paused: + dominfo.unpause() def domain_delete(self, domid): @@ -932,6 +934,9 @@ class XendDomain: if dominfo.state != DOM_STATE_HALTED: raise XendError("Domain is still running") + log.info("Domain %s (%s) deleted." % + (dominfo.getName(), dominfo.info.get('uuid'))) + self._managed_domain_unregister(dominfo) self._remove_domain(dominfo) XendDevices.destroy_device_state(dominfo) diff --git a/tools/python/xen/xend/XendDomainInfo.py b/tools/python/xen/xend/XendDomainInfo.py index 958c5cf8f8..c0a66aaf67 100644 --- a/tools/python/xen/xend/XendDomainInfo.py +++ b/tools/python/xen/xend/XendDomainInfo.py @@ -38,10 +38,10 @@ from xen.util.blkif import blkdev_uname_to_file from xen.util import security from xen.xend import balloon, sxp, uuid, image, arch -from xen.xend import XendRoot, XendNode +from xen.xend import XendRoot, XendNode, XendConfig +from xen.xend.XendConfig import scrub_password from xen.xend.XendBootloader import bootloader -from xen.xend.XendConfig import XendConfig from xen.xend.XendError import XendError, VmError from xen.xend.XendDevices import XendDevices from xen.xend.xenstore.xstransact import xstransact, complete @@ -58,6 +58,11 @@ xroot = XendRoot.instance() log = logging.getLogger("xend.XendDomainInfo") #log.setLevel(logging.TRACE) + +def bool0(v): + return v != "0" and bool(v) + + ## # All parameters of VMs that may be configured on-the-fly, or at start-up. # @@ -88,7 +93,7 @@ ROUNDTRIPPING_CONFIG_ENTRIES = [ ('bootloader', str), ('bootloader_args', str), ('features', str), - ('localtime', int), + ('localtime', bool0), ] ROUNDTRIPPING_CONFIG_ENTRIES += VM_CONFIG_PARAMS @@ -144,8 +149,8 @@ def create(config): @raise VmError: Invalid configuration or failure to start. """ - log.debug("XendDomainInfo.create(%s)", config) - vm = XendDomainInfo(XendConfig(sxp = config)) + log.debug("XendDomainInfo.create(%s)", scrub_password(config)) + vm = XendDomainInfo(XendConfig.XendConfig(sxp_obj = config)) try: vm.start() except: @@ -171,14 +176,13 @@ def recreate(info, priv): @raise XendError: Errors with configuration. """ - log.debug("XendDomainInfo.recreate(%s)", info) + log.debug("XendDomainInfo.recreate(%s)", scrub_password(info)) assert not info['dying'] - xeninfo = XendConfig(cfg = info) + xeninfo = XendConfig.XendConfig(dominfo = info) domid = xeninfo['domid'] - uuid1 = xeninfo['handle'] - xeninfo['uuid'] = uuid.toString(uuid1) + uuid1 = uuid.fromString(xeninfo['uuid']) needs_reinitialising = False dompath = GetDomainPath(domid) @@ -228,6 +232,15 @@ def recreate(info, priv): vm._storeVmDetails() vm._storeDomDetails() + if vm.info['image']: # Only dom0 should be without an image entry when + # recreating, but we cope with missing ones + # elsewhere just in case. + vm.image = image.create(vm, + vm.info, + vm.info['image'], + vm.info['devices']) + vm.image.recreate() + vm._registerWatches() vm.refreshShutdown(xeninfo) return vm @@ -236,7 +249,7 @@ def recreate(info, priv): def restore(config): """Create a domain and a VM object to do a restore. - @param config: Domain configuration object + @param config: Domain SXP configuration @type config: list of lists. (see C{create}) @rtype: XendDomainInfo @@ -245,8 +258,9 @@ def restore(config): @raise XendError: Errors with configuration. """ - log.debug("XendDomainInfo.restore(%s)", config) - vm = XendDomainInfo(XendConfig(sxp = config), resume = True) + log.debug("XendDomainInfo.restore(%s)", scrub_password(config)) + vm = XendDomainInfo(XendConfig.XendConfig(sxp_obj = config), + resume = True) try: vm.resume() return vm @@ -254,24 +268,24 @@ def restore(config): vm.destroy() raise -def createDormant(xeninfo): +def createDormant(domconfig): """Create a dormant/inactive XenDomainInfo without creating VM. This is for creating instances of persistent domains that are not yet start. - @param xeninfo: Parsed configuration - @type xeninfo: dictionary + @param domconfig: Parsed configuration + @type domconfig: XendConfig object @rtype: XendDomainInfo @return: A up and running XendDomainInfo instance @raise XendError: Errors with configuration. """ - log.debug("XendDomainInfo.createDormant(%s)", xeninfo) + log.debug("XendDomainInfo.createDormant(%s)", scrub_password(domconfig)) # domid does not make sense for non-running domains. - xeninfo.pop('domid', None) - vm = XendDomainInfo(XendConfig(cfg = xeninfo)) + domconfig.pop('domid', None) + vm = XendDomainInfo(domconfig) return vm def domain_by_name(name): @@ -383,14 +397,6 @@ class XendDomainInfo: #if not self._infoIsSet('uuid'): # self.info['uuid'] = uuid.toString(uuid.create()) - #REMOVE: domid logic can be shortened - #if domid is not None: - # self.domid = domid - #elif info.has_key('dom'): - # self.domid = int(info['dom']) - #else: - # self.domid = None - self.vmpath = XS_VMROOT + self.info['uuid'] self.dompath = dompath @@ -403,6 +409,7 @@ class XendDomainInfo: self.vmWatch = None self.shutdownWatch = None self.shutdownStartTime = None + self._resume = resume self.state = DOM_STATE_HALTED self.state_updated = threading.Condition() @@ -416,8 +423,7 @@ class XendDomainInfo: if augment: self._augmentInfo(priv) - self._checkName(self.info['name']) - self.setResume(resume) + self._checkName(self.info['name_label']) # @@ -439,7 +445,6 @@ class XendDomainInfo: self._storeDomDetails() self._registerWatches() self.refreshShutdown() - self.unpause() # save running configuration if XendDomains believe domain is # persistent @@ -478,10 +483,11 @@ class XendDomainInfo: if self.domid == 0: raise XendError('Domain 0 cannot be shutdown') - if not reason in DOMAIN_SHUTDOWN_REASONS.values(): + if reason not in DOMAIN_SHUTDOWN_REASONS.values(): raise XendError('Invalid reason: %s' % reason) - self._storeDom("control/shutdown", reason) - + self._removeVm('xend/previous_restart_time') + self.storeDom("control/shutdown", reason) + def pause(self): """Pause domain @@ -507,18 +513,20 @@ class XendDomainInfo: def send_sysrq(self, key): """ Send a Sysrq equivalent key via xenstored.""" asserts.isCharConvertible(key) - self._storeDom("control/sysrq", '%c' % key) + self.storeDom("control/sysrq", '%c' % key) def device_create(self, dev_config): """Create a new device. @param dev_config: device configuration - @type dev_config: dictionary (parsed config) + @type dev_config: SXP object (parsed config) """ - log.debug("XendDomainInfo.device_create: %s" % dev_config) + log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config)) dev_type = sxp.name(dev_config) - devid = self._createDevice(dev_type, dev_config) - self.info.device_add(dev_type, cfg_sxp = dev_config) + dev_uuid = self.info.device_add(dev_type, cfg_sxp = dev_config) + dev_config_dict = self.info['devices'][dev_uuid][1] + log.debug("XendDomainInfo.device_create: %s" % scrub_password(dev_config_dict)) + devid = self._createDevice(dev_type, dev_config_dict) self._waitForDevice(dev_type, devid) return self.getDeviceController(dev_type).sxpr(devid) @@ -526,12 +534,26 @@ class XendDomainInfo: """Configure an existing device. @param dev_config: device configuration - @type dev_config: dictionary (parsed config) + @type dev_config: SXP object (parsed config) @param devid: device id @type devid: int + @return: Returns True if successfully updated device + @rtype: boolean """ deviceClass = sxp.name(dev_config) - self._reconfigureDevice(deviceClass, devid, dev_config) + + # look up uuid of the device + dev_control = self.getDeviceController(deviceClass) + dev_sxpr = dev_control.sxpr(devid) + dev_uuid = sxp.child_value(sxpr, 'uuid') + if not dev_uuid: + return False + + self.info.device_update(dev_uuid, dev_config) + dev_config_dict = self.info['devices'].get(dev_uuid) + if dev_config_dict: + dev_control.reconfigureDevice(devid, dev_config_dict[1]) + return True def waitForDevices(self): """Wait for this domain's configured devices to connect. @@ -559,8 +581,18 @@ class XendDomainInfo: return self.getDeviceController(deviceClass).destroyDevice(devid) + def getDeviceSxprs(self, deviceClass): - return self.getDeviceController(deviceClass).sxprs() + if self.state == DOM_STATE_RUNNING: + return self.getDeviceController(deviceClass).sxprs() + else: + sxprs = [] + dev_num = 0 + for dev_type, dev_info in self.info.all_devices_sxpr(): + if dev_type == deviceClass: + sxprs.append([dev_num, dev_info]) + dev_num += 1 + return sxprs def setMemoryTarget(self, target): @@ -568,22 +600,22 @@ class XendDomainInfo: @param target: In MiB. """ log.debug("Setting memory target of domain %s (%d) to %d MiB.", - self.info['name'], self.domid, target) + self.info['name_label'], self.domid, target) if target <= 0: raise XendError('Invalid memory size') - self.info['memory'] = target + self.info['memory_static_min'] = target self.storeVm("memory", target) - self._storeDom("memory/target", target << 10) + self.storeDom("memory/target", target << 10) def getVCPUInfo(self): try: # We include the domain name and ID, to help xm. sxpr = ['domain', ['domid', self.domid], - ['name', self.info['name']], - ['vcpu_count', self.info['online_vcpus']]] + ['name', self.info['name_label']], + ['vcpu_count', self.info['vcpus_number']]] for i in range(0, self.info['max_vcpu_id']+1): info = xc.vcpu_getinfo(self.domid, i) @@ -611,30 +643,40 @@ class XendDomainInfo: values taken from the store. This recovers those values known to xend but not to the hypervisor. """ - def useIfNeeded(name, val): - if not self._infoIsSet(name) and val is not None: - self.info[name] = val - + augment_entries = XendConfig.LEGACY_XENSTORE_VM_PARAMS[:] if priv: - entries = VM_STORE_ENTRIES[:] - entries.remove(('memory', int)) - entries.remove(('maxmem', int)) - else: - entries = VM_STORE_ENTRIES - entries.append(('image', str)) - entries.append(('security', str)) + augment_entries.remove('memory') + augment_entries.remove('maxmem') - map(lambda x, y: useIfNeeded(x[0], y), entries, - self._readVMDetails(entries)) + vm_config = self._readVMDetails([(k, XendConfig.LEGACY_CFG_TYPES[k]) + for k in augment_entries]) + + # make returned lists into a dictionary + vm_config = dict(zip(augment_entries, vm_config)) + + for arg in augment_entries: + xapicfg = arg + val = vm_config[arg] + if val != None: + if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG: + xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg] + self.info[xapiarg] = val + else: + self.info[arg] = val + # read image value + image_sxp = self._readVm('image') + if image_sxp: + self.info.update_with_image_sxp(sxp.from_string(image_sxp)) + + # read devices devices = [] - for devclass in XendDevices.valid_devices(): devconfig = self.getDeviceController(devclass).configurations() if devconfig: - devices.extend(map(lambda conf: (devclass, conf), devconfig)) + devices.extend(devconfig) - if not self.info['device'] and devices is not None: + if not self.info['devices'] and devices is not None: for device in devices: self.info.device_add(device[0], cfg_sxp = device) @@ -661,16 +703,19 @@ class XendDomainInfo: # Function to update xenstore /dom/* # - def _readDom(self, *args): + def readDom(self, *args): return xstransact.Read(self.dompath, *args) + def gatherDom(self, *args): + return xstransact.Gather(self.dompath, *args) + def _writeDom(self, *args): return xstransact.Write(self.dompath, *args) def _removeDom(self, *args): return xstransact.Remove(self.dompath, *args) - def _storeDom(self, *args): + def storeDom(self, *args): return xstransact.Store(self.dompath, *args) def _recreateDom(self): @@ -679,16 +724,16 @@ class XendDomainInfo: def _recreateDomFunc(self, t): t.remove() t.mkdir() - t.set_permissions({ 'dom' : self.domid }) + t.set_permissions({'dom' : self.domid}) t.write('vm', self.vmpath) def _storeDomDetails(self): to_store = { 'domid': str(self.domid), 'vm': self.vmpath, - 'name': self.info['name'], + 'name': self.info['name_label'], 'console/limit': str(xroot.get_console_limit() * 1024), - 'memory/target': str(self.info['memory'] * 1024) + 'memory/target': str(self.info['memory_static_min'] * 1024) } def f(n, v): @@ -702,7 +747,7 @@ class XendDomainInfo: to_store.update(self._vcpuDomDetails()) - log.debug("Storing domain details: %s", to_store) + log.debug("Storing domain details: %s", scrub_password(to_store)) self._writeDom(to_store) @@ -714,7 +759,7 @@ class XendDomainInfo: return 'offline' result = {} - for v in range(0, self.info['vcpus']): + for v in range(0, self.info['vcpus_number']): result["cpu/%d/availability" % v] = availability(v) return result @@ -736,19 +781,29 @@ class XendDomainInfo: log.trace("XendDomainInfo.storeChanged"); changed = False + + # Check whether values in the configuration have + # changed in Xenstore. - def f(x, y): - if y is not None and self.info[x[0]] != y: - self.info[x[0]] = y - changed = True + cfg_vm = ['name', 'on_poweroff', 'on_reboot', 'on_crash'] + + vm_details = self._readVMDetails([(k,XendConfig.LEGACY_CFG_TYPES[k]) + for k in cfg_vm]) - map(f, VM_CONFIG_PARAMS, self._readVMDetails(VM_CONFIG_PARAMS)) + # convert two lists into a python dictionary + vm_details = dict(zip(cfg_vm, vm_details)) + + for arg, val in vm_details.items(): + if arg in XendConfig.LEGACY_CFG_TO_XENAPI_CFG: + xapiarg = XendConfig.LEGACY_CFG_TO_XENAPI_CFG[arg] + if val != None and val != self.info[xapiarg]: + self.info[xapiarg] = val + changed= True - im = self._readVm('image') - current_im = self.info['image'] - if (im is not None and - (current_im is None or sxp.to_string(current_im) != im)): - self.info['image'] = sxp.from_string(im) + # Check whether image definition has been updated + image_sxp = self._readVm('image') + if image_sxp and image_sxp != self.info.image_sxpr(): + self.info.update_with_image_sxp(sxp.from_string(image_sxp)) changed = True if changed: @@ -761,17 +816,17 @@ class XendDomainInfo: def _handleShutdownWatch(self, _): log.debug('XendDomainInfo.handleShutdownWatch') - reason = self._readDom('control/shutdown') + reason = self.readDom('control/shutdown') if reason and reason != 'suspend': - sst = self._readDom('xend/shutdown_start_time') + sst = self.readDom('xend/shutdown_start_time') now = time.time() if sst: self.shutdownStartTime = float(sst) timeout = float(sst) + SHUTDOWN_TIMEOUT - now else: self.shutdownStartTime = now - self._storeDom('xend/shutdown_start_time', now) + self.storeDom('xend/shutdown_start_time', now) timeout = SHUTDOWN_TIMEOUT log.trace( @@ -792,17 +847,17 @@ class XendDomainInfo: def setName(self, name): self._checkName(name) - self.info['name'] = name + self.info['name_label'] = name self.storeVm("name", name) def getName(self): - return self.info['name'] + return self.info['name_label'] def getDomainPath(self): return self.dompath def getShutdownReason(self): - return self._readDom('control/shutdown') + return self.readDom('control/shutdown') def getStorePort(self): """For use only by image.py and XendCheckpoint.py.""" @@ -817,31 +872,41 @@ class XendDomainInfo: return self.info['features'] def getVCpuCount(self): - return self.info['vcpus'] + return self.info['vcpus_number'] def setVCpuCount(self, vcpus): self.info['vcpu_avail'] = (1 << vcpus) - 1 self.storeVm('vcpu_avail', self.info['vcpu_avail']) - self._writeDom(self._vcpuDomDetails()) + # update dom differently depending on whether we are adjusting + # vcpu number up or down, otherwise _vcpuDomDetails does not + # disable the vcpus + if self.info['vcpus_number'] > vcpus: + # decreasing + self._writeDom(self._vcpuDomDetails()) + self.info['vcpus_number'] = vcpus + else: + # same or increasing + self.info['vcpus_number'] = vcpus + self._writeDom(self._vcpuDomDetails()) def getLabel(self): return security.get_security_info(self.info, 'label') def getMemoryTarget(self): """Get this domain's target memory size, in KB.""" - return self.info['memory'] * 1024 + return self.info['memory_static_min'] * 1024 def getResume(self): - return "%s" % self.info['resume'] + return str(self._resume) def getCap(self): - return self.info['cpu_cap'] + return self.info.get('cpu_cap', 0) def getWeight(self): return self.info['cpu_weight'] def setResume(self, state): - self.info['resume'] = state + self._resume = state def getRestartCount(self): return self._readVm('xend/restart_count') @@ -886,13 +951,13 @@ class XendDomainInfo: return elif xeninfo['crashed']: - if self._readDom('xend/shutdown_completed'): + if self.readDom('xend/shutdown_completed'): # We've seen this shutdown already, but we are preserving # the domain for debugging. Leave it alone. return log.warn('Domain has crashed: name=%s id=%d.', - self.info['name'], self.domid) + self.info['name_label'], self.domid) if xroot.get_enable_dump(): self.dumpCore() @@ -902,7 +967,7 @@ class XendDomainInfo: elif xeninfo['shutdown']: self._stateSet(DOM_STATE_SHUTDOWN) - if self._readDom('xend/shutdown_completed'): + if self.readDom('xend/shutdown_completed'): # We've seen this shutdown already, but we are preserving # the domain for debugging. Leave it alone. return @@ -911,7 +976,7 @@ class XendDomainInfo: reason = shutdown_reason(xeninfo['shutdown_reason']) log.info('Domain has shutdown: name=%s id=%d reason=%s.', - self.info['name'], self.domid, reason) + self.info['name_label'], self.domid, reason) self._clearRestart() @@ -946,7 +1011,7 @@ class XendDomainInfo: if timeout < 0: log.info( "Domain shutdown timeout expired: name=%s id=%s", - self.info['name'], self.domid) + self.info['name_label'], self.domid) self.destroy() finally: self.refresh_shutdown_lock.release() @@ -966,11 +1031,23 @@ class XendDomainInfo: def _maybeRestart(self, reason): # Dispatch to the correct method based upon the configured on_{reason} # behaviour. - {"destroy" : self.destroy, - "restart" : self._restart, - "preserve" : self._preserve, - "rename-restart" : self._renameRestart}[self.info['on_' + reason]]() + actions = {"destroy" : self.destroy, + "restart" : self._restart, + "preserve" : self._preserve, + "rename-restart" : self._renameRestart} + action_conf = { + 'poweroff': 'actions_after_shutdown', + 'reboot': 'actions_after_reboot', + 'crash': 'actions_after_crash', + } + + action_target = self.info.get(action_conf.get(reason)) + func = actions.get(action_target, None) + if func and callable(func): + func() + else: + self.destroy() # default to destroy def _renameRestart(self): self._restart(True) @@ -1009,7 +1086,7 @@ class XendDomainInfo: log.error( 'VM %s restarting too fast (%f seconds since the last ' 'restart). Refusing to restart to avoid loops.', - self.info['name'], timeout) + self.info['name_label'], timeout) self.destroy() return @@ -1056,11 +1133,11 @@ class XendDomainInfo: new_uuid = uuid.createString() new_name = 'Domain-%s' % new_uuid log.info("Renaming dead domain %s (%d, %s) to %s (%s).", - self.info['name'], self.domid, self.info['uuid'], + self.info['name_label'], self.domid, self.info['uuid'], new_name, new_uuid) self._unwatchVm() self._releaseDevices() - self.info['name'] = new_name + self.info['name_label'] = new_name self.info['uuid'] = new_uuid self.vmpath = XS_VMROOT + new_uuid self._storeVmDetails() @@ -1068,10 +1145,10 @@ class XendDomainInfo: def _preserve(self): - log.info("Preserving dead domain %s (%d).", self.info['name'], + log.info("Preserving dead domain %s (%d).", self.info['name_label'], self.domid) self._unwatchVm() - self._storeDom('xend/shutdown_completed', 'True') + self.storeDom('xend/shutdown_completed', 'True') self._stateSet(DOM_STATE_HALTED) # @@ -1085,7 +1162,7 @@ class XendDomainInfo: if not corefile: this_time = time.strftime("%Y-%m%d-%H%M.%S", time.localtime()) corefile = "/var/xen/dump/%s-%s.%s.core" % (this_time, - self.info['name'], self.domid) + self.info['name_label'], self.domid) if os.path.isdir(corefile): raise XendError("Cannot dump core in a directory: %s" % @@ -1096,7 +1173,7 @@ class XendDomainInfo: corefile_incomp = corefile+'-incomplete' os.rename(corefile, corefile_incomp) log.exception("XendDomainInfo.dumpCore failed: id = %s name = %s", - self.domid, self.info['name']) + self.domid, self.info['name_label']) raise XendError("Failed to dump core: %s" % str(ex)) # @@ -1118,9 +1195,9 @@ class XendDomainInfo: @raise: VmError for invalid devices """ - for (devclass, config) in self.info.all_devices_sxpr(): - if devclass in XendDevices.valid_devices(): - log.info("createDevice: %s : %s" % (devclass, config)) + for (devclass, config) in self.info.get('devices', {}).values(): + if devclass in XendDevices.valid_devices(): + log.info("createDevice: %s : %s" % (devclass, scrub_password(config))) self._createDevice(devclass, config) if self.image: @@ -1140,7 +1217,7 @@ class XendDomainInfo: # there's nothing more we can do. log.exception( "Device release failed: %s; %s; %s", - self.info['name'], devclass, dev) + self.info['name_label'], devclass, dev) if t.commit(): break @@ -1212,11 +1289,12 @@ class XendDomainInfo: log.debug('XendDomainInfo.constructDomain') - hvm = (self._infoIsSet('image') and - sxp.name(self.info['image']) == "hvm") + image_cfg = self.info.get('image', {}) + hvm = image_cfg.has_key('hvm') + if hvm: info = xc.xeninfo() - if not 'hvm' in info['xen_caps']: + if 'hvm' not in info['xen_caps']: raise VmError("HVM guest support is unavailable: is VT/AMD-V " "supported by your CPU and enabled in your " "BIOS?") @@ -1229,14 +1307,14 @@ class XendDomainInfo: if self.domid < 0: raise VmError('Creating domain failed: name=%s' % - self.info['name']) + self.info['name_label']) self.dompath = GetDomainPath(self.domid) self._recreateDom() # Set maximum number of vcpus in domain - xc.domain_max_vcpus(self.domid, int(self.info['vcpus'])) + xc.domain_max_vcpus(self.domid, int(self.info['vcpus_number'])) def _introduceDomain(self): @@ -1257,7 +1335,7 @@ class XendDomainInfo: # if we have a boot loader but no image, then we need to set things # up by running the boot loader non-interactively - if self._infoIsSet('bootloader') and not self._infoIsSet('image'): + if self.info.get('bootloader'): self._configureBootloader() if not self._infoIsSet('image'): @@ -1265,11 +1343,12 @@ class XendDomainInfo: try: self.image = image.create(self, + self.info, self.info['image'], - self.info.all_devices_sxpr()) + self.info['devices']) - localtime = self.info.get('localtime', 0) - if localtime is not None and localtime == 1: + localtime = self.info.get('localtime', False) + if localtime: xc.domain_set_time_offset(self.domid) xc.domain_setcpuweight(self.domid, self.info['cpu_weight']) @@ -1285,12 +1364,12 @@ class XendDomainInfo: # the various headrooms necessary, given the raw configured # values. maxmem, memory, and shadow are all in KiB. maxmem = self.image.getRequiredAvailableMemory( - self.info['maxmem'] * 1024) + self.info['memory_static_min'] * 1024) memory = self.image.getRequiredAvailableMemory( - self.info['memory'] * 1024) + self.info['memory_static_max'] * 1024) shadow = self.image.getRequiredShadowMemory( self.info['shadow_memory'] * 1024, - self.info['maxmem'] * 1024) + self.info['memory_static_max'] * 1024) # Round shadow up to a multiple of a MiB, as shadow_mem_control # takes MiB and we must not round down and end up under-providing. @@ -1318,7 +1397,7 @@ class XendDomainInfo: self._createDevices() - if self.info['bootloader'] not in [None, 'kernel_external']: + if self.info['bootloader']: self.image.cleanupBootloading() self.info['start_time'] = time.time() @@ -1326,7 +1405,7 @@ class XendDomainInfo: self._stateSet(DOM_STATE_RUNNING) except RuntimeError, exn: log.exception("XendDomainInfo.initDomain: exception occurred") - if self.info['bootloader'] not in [None, 'kernel_external'] \ + if self.info['bootloader'] not in (None, 'kernel_external') \ and self.image is not None: self.image.cleanupBootloading() raise VmError(str(exn)) @@ -1339,7 +1418,6 @@ class XendDomainInfo: self.refresh_shutdown_lock.acquire() try: self.unwatchShutdown() - self._releaseDevices() if self.image: @@ -1460,14 +1538,20 @@ class XendDomainInfo: def _configureBootloader(self): """Run the bootloader if we're configured to do so.""" - if not self.info['bootloader']: + if not self.info.get('bootloader'): return blcfg = None + # FIXME: this assumes that we want to use the first disk device - for (n, c) in self.info.all_devices_sxpr(): - if not n or not c or not(n in ["vbd", "tap"]): + for (devtype, devinfo) in self.info.all_devices_sxpr(): + if not devtype or not devinfo or devtype not in ('vbd', 'tap'): continue - disk = sxp.child_value(c, "uname") + disk = None + for param in devinfo: + if param[0] == 'uname': + disk = param[1] + break + if disk is None: continue fn = blkdev_uname_to_file(disk) @@ -1479,7 +1563,8 @@ class XendDomainInfo: msg = "Had a bootloader specified, but can't find disk" log.error(msg) raise VmError(msg) - self.info['image'] = blcfg + + self.info.update_with_image_sxp(blcfg) # # VM Functions @@ -1517,7 +1602,8 @@ class XendDomainInfo: if arch.type == "x86": # 1MB per vcpu plus 4Kib/Mib of RAM. This is higher than # the minimum that Xen would allocate if no value were given. - overhead_kb = self.info['vcpus'] * 1024 + self.info['maxmem'] * 4 + overhead_kb = self.info['vcpus_number'] * 1024 + \ + self.info['memory_static_max'] * 4 overhead_kb = ((overhead_kb + 1023) / 1024) * 1024 # The domain might already have some shadow memory overhead_kb -= xc.shadow_mem_control(self.domid) * 1024 @@ -1559,12 +1645,15 @@ class XendDomainInfo: def _storeVmDetails(self): to_store = {} - for k in VM_STORE_ENTRIES: - if self._infoIsSet(k[0]): - to_store[k[0]] = str(self.info[k[0]]) + for key in XendConfig.LEGACY_XENSTORE_VM_PARAMS: + info_key = XendConfig.LEGACY_CFG_TO_XENAPI_CFG.get(key, key) + if self._infoIsSet(info_key): + to_store[key] = str(self.info[info_key]) - if self._infoIsSet('image'): - to_store['image'] = sxp.to_string(self.info['image']) + if self.info.get('image'): + image_sxpr = self.info.image_sxpr() + if image_sxpr: + to_store['image'] = sxp.to_string(image_sxpr) if self._infoIsSet('security'): secinfo = self.info['security'] @@ -1587,7 +1676,7 @@ class XendDomainInfo: if not self._readVm('xend/restart_count'): to_store['xend/restart_count'] = str(0) - log.debug("Storing VM details: %s", to_store) + log.debug("Storing VM details: %s", scrub_password(to_store)) self._writeVm(to_store) self._setVmPermissions() @@ -1634,8 +1723,11 @@ class XendDomainInfo: raise VmError('Invalid VM Name') dom = XendDomain.instance().domain_lookup_nr(name) - if dom and dom != self and not dom.info['dying']: - raise VmError("VM name '%s' already exists" % name) + if dom and dom.info['uuid'] != self.info['uuid']: + raise VmError("VM name '%s' already exists%s" % + (name, + dom.domid is not None and + ("as domain %s" % str(dom.domid)) or "")) def update(self, info = None, refresh = True): @@ -1657,6 +1749,7 @@ class XendDomainInfo: #create new security element self.info.update({'security': [['ssidref', str(info['ssidref'])]]}) + #ssidref field not used any longer if 'ssidref' in info: info.pop('ssidref') @@ -1664,8 +1757,7 @@ class XendDomainInfo: # make sure state is reset for info # TODO: we should eventually get rid of old_dom_states - self.info.update(info) - self.info.validate() + self.info.update_config(info) if refresh: self.refreshShutdown(info) @@ -1674,11 +1766,11 @@ class XendDomainInfo: str(self.domid), self.info) def sxpr(self, ignore_store = False): - result = self.info.get_sxp(domain = self, + result = self.info.to_sxp(domain = self, ignore_devices = ignore_store) if not ignore_store and self.dompath: - vnc_port = self._readDom('console/vnc-port') + vnc_port = self.readDom('console/vnc-port') if vnc_port is not None: result.append(['device', ['console', ['vnc-port', str(vnc_port)]]]) @@ -1696,9 +1788,15 @@ class XendDomainInfo: return dom_uuid def get_memory_static_max(self): - return self.info['maxmem'] + return self.info.get('memory_static_max', 0) def get_memory_static_min(self): - return self.info['memory'] + return self.info.get('memory_static_min', 0) + def get_memory_dynamic_max(self): + return self.info.get('memory_dynamic_max', 0) + def get_memory_dynamic_min(self): + return self.info.get('memory_dynamic_min', 0) + + def get_vcpus_policy(self): sched_id = xc.sched_id_get() if sched_id == xen.lowlevel.xc.XEN_SCHEDULER_SEDF: @@ -1714,58 +1812,59 @@ class XendDomainInfo: def get_bios_boot(self): return '' # TODO def get_platform_std_vga(self): - return False + return self.info.get('platform_std_vga', False) + def get_platform_keymap(self): + return '' def get_platform_serial(self): - return '' # TODO + return self.info.get('platform_serial', '') def get_platform_localtime(self): - return False # TODO + return self.info.get('platform_localtime', False) def get_platform_clock_offset(self): - return False # TODO + return self.info.get('platform_clock_offset', False) def get_platform_enable_audio(self): - return False # TODO + return self.info.get('platform_enable_audio', False) + def get_platform_keymap(self): + return self.info.get('platform_keymap', '') def get_builder(self): - return 'Linux' # TODO + return self.info.get('builder', 0) def get_boot_method(self): - bootloader = self.info['bootloader'] - if not bootloader or bootloader not in XEN_API_BOOT_TYPE: - return 'kernel_external' - return bootloader - + return self.info.get('boot_method', XEN_API_BOOT_TYPE[2]) def get_kernel_image(self): - return self.info['kernel_kernel'] + return self.info.get('kernel_kernel', '') def get_kernel_initrd(self): - return self.info['kernel_initrd'] + return self.info.get('kernel_initrd', '') def get_kernel_args(self): - return self.info['kernel_args'] + return self.info.get('kernel_args', '') def get_grub_cmdline(self): return '' # TODO def get_pci_bus(self): - return 0 # TODO + return '' # TODO def get_tools_version(self): return {} # TODO def get_other_config(self): return {} # TODO def get_on_shutdown(self): - after_shutdown = self.info.get('on_poweroff') + after_shutdown = self.info.get('action_after_shutdown') if not after_shutdown or after_shutdown not in XEN_API_ON_NORMAL_EXIT: return XEN_API_ON_NORMAL_EXIT[-1] return after_shutdown def get_on_reboot(self): - after_reboot = self.info.get('on_reboot') + after_reboot = self.info.get('action_after_reboot') if not after_reboot or after_reboot not in XEN_API_ON_NORMAL_EXIT: return XEN_API_ON_NORMAL_EXIT[-1] return after_reboot def get_on_suspend(self): - after_suspend = self.info.get('on_suspend') # TODO: not supported + # TODO: not supported + after_suspend = self.info.get('action_after_suspend') if not after_suspend or after_suspend not in XEN_API_ON_NORMAL_EXIT: return XEN_API_ON_NORMAL_EXIT[-1] return after_suspend def get_on_crash(self): - after_crash = self.info.get('on_crash') + after_crash = self.info.get('action_after_crash') if not after_crash or after_crash not in XEN_API_ON_CRASH_BEHAVIOUR: return XEN_API_ON_CRASH_BEHAVIOUR[0] return after_crash @@ -1779,7 +1878,7 @@ class XendDomainInfo: @rtype: dictionary """ - dev_type_config = self.info['device'].get(dev_uuid) + dev_type_config = self.info['devices'].get(dev_uuid) # shortcut if the domain isn't started because # the devcontrollers will have no better information @@ -1834,18 +1933,16 @@ class XendDomainInfo: config['network'] = '' # Invalid for Xend config['MTU'] = 1500 # TODO - config['network_read_kbs'] = 0.0 - config['network_write_kbs'] = 0.0 - config['IO_bandwidth_incoming_kbs'] = 0.0 - config['IO_bandwidth_outgoing_kbs'] = 0.0 + config['io_read_kbs'] = 0.0 + config['io_write_kbs'] = 0.0 - if dev_class =='vbd': - config['VDI'] = '' # TODO + if dev_class == 'vbd': + config['VDI'] = config.get('VDI', '') config['device'] = config.get('dev', '') config['driver'] = 'paravirtualised' # TODO config['image'] = config.get('uname', '') - config['IO_bandwidth_incoming_kbs'] = 0.0 - config['IO_bandwidth_outgoing_kbs'] = 0.0 + config['io_read_kbs'] = 0.0 + config['io_write_kbs'] = 0.0 if config['mode'] == 'r': config['mode'] = 'RO' else: @@ -1959,32 +2056,12 @@ class XendDomainInfo: return dev_uuid def has_device(self, dev_class, dev_uuid): - return (dev_uuid in self.info['%s_refs' % dev_class]) - - """ - def stateChar(name): - if name in self.info: - if self.info[name]: - return name[0] - else: - return '-' - else: - return '?' - - state = reduce(lambda x, y: x + y, map(stateChar, DOM_STATES_OLD)) - - sxpr.append(['state', state]) - - if self.store_mfn: - sxpr.append(['store_mfn', self.store_mfn]) - if self.console_mfn: - sxpr.append(['console_mfn', self.console_mfn]) - """ + return (dev_uuid in self.info['%s_refs' % dev_class.lower()]) def __str__(self): return '' % \ - (str(self.domid), self.info['name'], - str(self.info['memory']), DOM_STATES[self.state]) + (str(self.domid), self.info['name_label'], + str(self.info['memory_static_min']), DOM_STATES[self.state]) __repr__ = __str__ diff --git a/tools/python/xen/xend/XendLogging.py b/tools/python/xen/xend/XendLogging.py index a3d6de86d1..32cd13c5db 100644 --- a/tools/python/xen/xend/XendLogging.py +++ b/tools/python/xen/xend/XendLogging.py @@ -16,13 +16,15 @@ # Copyright (C) 2005, 2006 XenSource Ltd. #============================================================================ - +import os +import stat import tempfile import types import logging import logging.handlers import fcntl +from xen.util import mkdir from xen.xend.server import params @@ -80,6 +82,7 @@ def init(filename, level): global logfilename def openFileHandler(fname): + mkdir.parents(os.path.dirname(fname), stat.S_IRWXU) return XendRotatingFileHandler(fname, mode = 'a', maxBytes = MAX_BYTES, backupCount = BACKUP_COUNT) diff --git a/tools/python/xen/xend/XendStorageRepository.py b/tools/python/xen/xend/XendStorageRepository.py index d906f5b5fd..a16a293191 100644 --- a/tools/python/xen/xend/XendStorageRepository.py +++ b/tools/python/xen/xend/XendStorageRepository.py @@ -19,10 +19,12 @@ # The default QCOW Xen API Storage Repository # -import os import commands +import os +import stat import threading +from xen.util import mkdir from xen.xend import uuid from xen.xend.XendError import XendError from xen.xend.XendVDI import * @@ -98,10 +100,7 @@ class XendStorageRepository: """ self.lock.acquire() try: - # create directory if /var/lib/xend/storage does not exist - if not os.path.exists(XEND_STORAGE_DIR): - os.makedirs(XEND_STORAGE_DIR) - os.chmod(XEND_STORAGE_DIR, 0700) + mkdir.parents(XEND_STORAGE_DIR, stat.S_IRWXU) # scan the directory and populate self.images total_used = 0 diff --git a/tools/python/xen/xend/image.py b/tools/python/xen/xend/image.py index 12621a8dfb..52559885d1 100644 --- a/tools/python/xen/xend/image.py +++ b/tools/python/xen/xend/image.py @@ -23,7 +23,7 @@ import math import signal import xen.lowlevel.xc -from xen.xend import sxp +from xen.xend.XendConstants import REVERSE_DOMAIN_SHUTDOWN_REASONS from xen.xend.XendError import VmError, XendError from xen.xend.XendLogging import log from xen.xend.server.netif import randomMAC @@ -31,19 +31,18 @@ from xen.xend.xenstore.xswatch import xswatch from xen.xend import arch from xen.xend import FlatDeviceTree - xc = xen.lowlevel.xc.xc() - MAX_GUEST_CMDLINE = 1024 -def create(vm, imageConfig, deviceConfig): +def create(vm, vmConfig, imageConfig, deviceConfig): """Create an image handler for a vm. @return ImageHandler instance """ - return findImageHandlerClass(imageConfig)(vm, imageConfig, deviceConfig) + return findImageHandlerClass(imageConfig)(vm, vmConfig, imageConfig, + deviceConfig) class ImageHandler: @@ -66,34 +65,20 @@ class ImageHandler: ostype = None - def __init__(self, vm, imageConfig, deviceConfig): + def __init__(self, vm, vmConfig, imageConfig, deviceConfig): self.vm = vm self.kernel = None self.ramdisk = None self.cmdline = None - self.configure(imageConfig, deviceConfig) + self.configure(vmConfig, imageConfig, deviceConfig) - def configure(self, imageConfig, _): + def configure(self, vmConfig, imageConfig, _): """Config actions common to all unix-like domains.""" - - def get_cfg(name, default = None): - return sxp.child_value(imageConfig, name, default) - - self.kernel = get_cfg("kernel") - self.cmdline = "" - ip = get_cfg("ip") - if ip: - self.cmdline += " ip=" + ip - root = get_cfg("root") - if root: - self.cmdline += " root=" + root - args = get_cfg("args") - if args: - self.cmdline += " " + args - self.ramdisk = get_cfg("ramdisk", '') - + self.kernel = vmConfig['kernel_kernel'] + self.cmdline = vmConfig['kernel_args'] + self.ramdisk = vmConfig['kernel_initrd'] self.vm.storeVm(("image/ostype", self.ostype), ("image/kernel", self.kernel), ("image/cmdline", self.cmdline), @@ -181,6 +166,10 @@ class ImageHandler: pass + def recreate(self): + pass + + class LinuxImageHandler(ImageHandler): ostype = "linux" @@ -214,8 +203,8 @@ class PPC_LinuxImageHandler(LinuxImageHandler): ostype = "linux" - def configure(self, imageConfig, deviceConfig): - LinuxImageHandler.configure(self, imageConfig, deviceConfig) + def configure(self, vmConfig, imageConfig, deviceConfig): + LinuxImageHandler.configure(self, vmConfig, imageConfig, deviceConfig) self.imageConfig = imageConfig def buildDomain(self): @@ -302,37 +291,42 @@ class PPC_ProseImageHandler(LinuxImageHandler): class HVMImageHandler(ImageHandler): - def __init__(self, vm, imageConfig, deviceConfig): - ImageHandler.__init__(self, vm, imageConfig, deviceConfig) - self.shutdownWatch = None + ostype = "hvm" - def configure(self, imageConfig, deviceConfig): - ImageHandler.configure(self, imageConfig, deviceConfig) + def __init__(self, vm, vmConfig, imageConfig, deviceConfig): + ImageHandler.__init__(self, vm, vmConfig, imageConfig, deviceConfig) + self.shutdownWatch = None + self.rebootFeatureWatch = None + + def configure(self, vmConfig, imageConfig, deviceConfig): + ImageHandler.configure(self, vmConfig, imageConfig, deviceConfig) info = xc.xeninfo() - if not 'hvm' in info['xen_caps']: + if 'hvm' not in info['xen_caps']: raise VmError("HVM guest support is unavailable: is VT/AMD-V " "supported by your CPU and enabled in your BIOS?") self.dmargs = self.parseDeviceModelArgs(imageConfig, deviceConfig) - self.device_model = sxp.child_value(imageConfig, 'device_model') + self.device_model = imageConfig['hvm'].get('device_model') if not self.device_model: raise VmError("hvm: missing device model") - self.display = sxp.child_value(imageConfig, 'display') - self.xauthority = sxp.child_value(imageConfig, 'xauthority') - self.vncconsole = sxp.child_value(imageConfig, 'vncconsole') + + self.display = imageConfig['hvm'].get('display') + self.xauthority = imageConfig['hvm'].get('xauthority') + self.vncconsole = imageConfig['hvm'].get('vncconsole') self.vm.storeVm(("image/dmargs", " ".join(self.dmargs)), ("image/device-model", self.device_model), ("image/display", self.display)) - self.pid = 0 + self.pid = None self.dmargs += self.configVNC(imageConfig) - self.pae = int(sxp.child_value(imageConfig, 'pae', 1)) - self.acpi = int(sxp.child_value(imageConfig, 'acpi', 1)) - self.apic = int(sxp.child_value(imageConfig, 'apic', 1)) + self.pae = imageConfig['hvm'].get('pae', 0) + self.apic = imageConfig['hvm'].get('apic', 0) + self.acpi = imageConfig['hvm']['devices'].get('acpi', 0) + def buildDomain(self): store_evtchn = self.vm.getStorePort() @@ -349,6 +343,7 @@ class HVMImageHandler(ImageHandler): log.debug("apic = %d", self.apic) self.register_shutdown_watch() + self.register_reboot_feature_watch() return xc.hvm_build(domid = self.vm.getDomid(), image = self.kernel, @@ -366,8 +361,10 @@ class HVMImageHandler(ImageHandler): 'localtime', 'serial', 'stdvga', 'isa', 'vcpus', 'acpi', 'usb', 'usbdevice', 'keymap' ] ret = [] + hvmDeviceConfig = imageConfig['hvm']['devices'] + for a in dmargs: - v = sxp.child_value(imageConfig, a) + v = hvmDeviceConfig.get(a) # python doesn't allow '-' in variable names if a == 'stdvga': a = 'std-vga' @@ -382,7 +379,7 @@ class HVMImageHandler(ImageHandler): ret.append("-%s" % a) ret.append("%s" % v) - if a in ['fda', 'fdb' ]: + if a in ['fda', 'fdb']: if v: if not os.path.isabs(v): raise VmError("Floppy file %s does not exist." % v) @@ -390,26 +387,27 @@ class HVMImageHandler(ImageHandler): # Handle disk/network related options mac = None - ret = ret + ["-domain-name", "%s" % self.vm.info['name']] + ret = ret + ["-domain-name", str(self.vm.info['name_label'])] nics = 0 - for (name, info) in deviceConfig: - if name == 'vbd': - uname = sxp.child_value(info, 'uname') + + for devuuid, (devtype, devinfo) in deviceConfig.items(): + if devtype == 'vbd': + uname = devinfo.get('uname') if uname is not None and 'file:' in uname: (_, vbdparam) = string.split(uname, ':', 1) if not os.path.isfile(vbdparam): raise VmError('Disk image does not exist: %s' % vbdparam) - if name == 'vif': - type = sxp.child_value(info, 'type') - if type != 'ioemu': + if devtype == 'vif': + dtype = devinfo.get('type', 'ioemu') + if dtype != 'ioemu': continue nics += 1 - mac = sxp.child_value(info, 'mac') + mac = devinfo.get('mac') if mac == None: mac = randomMAC() - bridge = sxp.child_value(info, 'bridge', 'xenbr0') - model = sxp.child_value(info, 'model', 'rtl8139') + bridge = devinfo.get('bridge', 'xenbr0') + model = devinfo.get('model', 'rtl8139') ret.append("-net") ret.append("nic,vlan=%d,macaddr=%s,model=%s" % (nics, mac, model)) @@ -417,31 +415,32 @@ class HVMImageHandler(ImageHandler): ret.append("tap,vlan=%d,bridge=%s" % (nics, bridge)) return ret - def configVNC(self, config): + def configVNC(self, imageConfig): # Handle graphics library related options - vnc = sxp.child_value(config, 'vnc') - sdl = sxp.child_value(config, 'sdl') + vnc = imageConfig.get('vnc') + sdl = imageConfig.get('sdl') ret = [] - nographic = sxp.child_value(config, 'nographic') + nographic = imageConfig.get('nographic') # get password from VM config (if password omitted, None) - vncpasswd_vmconfig = sxp.child_value(config, 'vncpasswd') + vncpasswd_vmconfig = imageConfig.get('vncpasswd') if nographic: ret.append('-nographic') return ret if vnc: - vncdisplay = int(sxp.child_value(config, 'vncdisplay', - self.vm.getDomid())) + vncdisplay = imageConfig.get('vncdisplay', + int(self.vm.getDomid())) + vncunused = imageConfig.get('vncunused') - vncunused = sxp.child_value(config, 'vncunused') if vncunused: ret += ['-vncunused'] else: ret += ['-vnc', '%d' % vncdisplay] - vnclisten = sxp.child_value(config, 'vnclisten') + vnclisten = imageConfig.get('vnclisten') + if not(vnclisten): vnclisten = (xen.xend.XendRoot.instance(). get_vnclisten_address()) @@ -477,21 +476,37 @@ class HVMImageHandler(ImageHandler): if self.vncconsole: args = args + ([ "-vncviewer" ]) log.info("spawning device models: %s %s", self.device_model, args) + # keep track of pid and spawned options to kill it later self.pid = os.spawnve(os.P_NOWAIT, self.device_model, args, env) + self.vm.storeDom("image/device-model-pid", self.pid) log.info("device model pid: %d", self.pid) + def recreate(self): + self.register_shutdown_watch() + self.register_reboot_feature_watch() + self.pid = self.vm.gatherDom(('image/device-model-pid', int)) + def destroy(self): - self.unregister_shutdown_watch(); - if not self.pid: - return - os.kill(self.pid, signal.SIGKILL) - os.waitpid(self.pid, 0) - self.pid = 0 + self.unregister_shutdown_watch() + self.unregister_reboot_feature_watch(); + if self.pid: + try: + os.kill(self.pid, signal.SIGKILL) + except OSError, exn: + log.exception(exn) + try: + os.waitpid(self.pid, 0) + except OSError, exn: + # This is expected if Xend has been restarted within the + # life of this domain. In this case, we can kill the process, + # but we can't wait for it because it's not our child. + pass + self.pid = None def register_shutdown_watch(self): """ add xen store watch on control/shutdown """ - self.shutdownWatch = xswatch(self.vm.dompath + "/control/shutdown", \ - self.hvm_shutdown) + self.shutdownWatch = xswatch(self.vm.dompath + "/control/shutdown", + self.hvm_shutdown) log.debug("hvm shutdown watch registered") def unregister_shutdown_watch(self): @@ -510,28 +525,55 @@ class HVMImageHandler(ImageHandler): """ watch call back on node control/shutdown, if node changed, this function will be called """ - from xen.xend.XendConstants import DOMAIN_SHUTDOWN_REASONS xd = xen.xend.XendDomain.instance() try: vm = xd.domain_lookup( self.vm.getDomid() ) except XendError: # domain isn't registered, no need to clean it up. - return + return False reason = vm.getShutdownReason() log.debug("hvm_shutdown fired, shutdown reason=%s", reason) - for x in DOMAIN_SHUTDOWN_REASONS.keys(): - if DOMAIN_SHUTDOWN_REASONS[x] == reason: - vm.info['shutdown'] = 1 - vm.info['shutdown_reason'] = x - vm.refreshShutdown(vm.info) + if reason in REVERSE_DOMAIN_SHUTDOWN_REASONS: + vm.info['shutdown'] = 1 + vm.info['shutdown_reason'] = \ + REVERSE_DOMAIN_SHUTDOWN_REASONS[reason] + vm.refreshShutdown(vm.info) + + return True # Keep watching + + def register_reboot_feature_watch(self): + """ add xen store watch on control/feature-reboot """ + self.rebootFeatureWatch = xswatch(self.vm.dompath + "/control/feature-reboot", \ + self.hvm_reboot_feature) + log.debug("hvm reboot feature watch registered") + + def unregister_reboot_feature_watch(self): + """Remove the watch on the control/feature-reboot, if any. Nothrow + guarantee.""" + + try: + if self.rebootFeatureWatch: + self.rebootFeatureWatch.unwatch() + except: + log.exception("Unwatching hvm reboot feature watch failed.") + self.rebootFeatureWatch = None + log.debug("hvm reboot feature watch unregistered") + + def hvm_reboot_feature(self, _): + """ watch call back on node control/feature-reboot, + if node changed, this function will be called + """ + status = self.vm.readDom('control/feature-reboot') + log.debug("hvm_reboot_feature fired, module status=%s", status) + if status == '1': + self.unregister_shutdown_watch() + + return True # Keep watching - return 1 # Keep watching class IA64_HVM_ImageHandler(HVMImageHandler): - ostype = "hvm" - def getRequiredAvailableMemory(self, mem_kb): page_kb = 16 # ROM size for guest firmware, ioreq page and xenstore page @@ -544,8 +586,6 @@ class IA64_HVM_ImageHandler(HVMImageHandler): class X86_HVM_ImageHandler(HVMImageHandler): - ostype = "hvm" - def getRequiredAvailableMemory(self, mem_kb): # Add 8 MiB overhead for QEMU's video RAM. return mem_kb + 8192 @@ -562,6 +602,14 @@ class X86_HVM_ImageHandler(HVMImageHandler): return max(4 * (256 * self.vm.getVCpuCount() + 2 * (maxmem_kb / 1024)), shadow_mem_kb) +class X86_Linux_ImageHandler(LinuxImageHandler): + + def buildDomain(self): + # set physical mapping limit + # add an 8MB slack to balance backend allocations. + mem_kb = self.getRequiredInitialReservation() + (8 * 1024) + xc.domain_set_memmap_limit(self.vm.getDomid(), mem_kb) + return LinuxImageHandler.buildDomain(self) _handlers = { "powerpc": { @@ -573,7 +621,7 @@ _handlers = { "hvm": IA64_HVM_ImageHandler, }, "x86": { - "linux": LinuxImageHandler, + "linux": X86_Linux_ImageHandler, "hvm": X86_HVM_ImageHandler, }, } @@ -584,10 +632,10 @@ def findImageHandlerClass(image): @param image config @return ImageHandler subclass or None """ - type = sxp.name(image) - if type is None: + image_type = image['type'] + if image_type is None: raise VmError('missing image type') try: - return _handlers[arch.type][type] + return _handlers[arch.type][image_type] except KeyError: - raise VmError('unknown image type: ' + type) + raise VmError('unknown image type: ' + image_type) diff --git a/tools/python/xen/xend/server/DevController.py b/tools/python/xen/xend/server/DevController.py index cbe3b18845..b8dc8f4763 100644 --- a/tools/python/xen/xend/server/DevController.py +++ b/tools/python/xen/xend/server/DevController.py @@ -17,6 +17,7 @@ #============================================================================ from threading import Event +import types from xen.xend import sxp from xen.xend.XendError import VmError @@ -86,7 +87,7 @@ class DevController: import xen.xend.XendDomain xd = xen.xend.XendDomain.instance() - backdom_name = sxp.child_value(config, 'backend') + backdom_name = config.get('backend') if backdom_name is None: backdom = xen.xend.XendDomain.DOM0_ID else: @@ -223,11 +224,13 @@ class DevController: configDict = self.getDeviceConfiguration(devid) sxpr = [self.deviceClass] for key, val in configDict.items(): - if type(val) == type(list()): + if isinstance(val, (types.ListType, types.TupleType)): for v in val: - sxpr.append([key, v]) + if v != None: + sxpr.append([key, v]) else: - sxpr.append([key, val]) + if val != None: + sxpr.append([key, val]) return sxpr def sxprs(self): @@ -405,7 +408,7 @@ class DevController: import xen.xend.XendDomain xd = xen.xend.XendDomain.instance() - backdom_name = sxp.child_value(config, 'backend') + backdom_name = config.get('backend') if backdom_name: backdom = xd.domain_lookup_nr(backdom_name) else: diff --git a/tools/python/xen/xend/server/SrvDaemon.py b/tools/python/xen/xend/server/SrvDaemon.py index 864c371552..bd93e6fc56 100644 --- a/tools/python/xen/xend/server/SrvDaemon.py +++ b/tools/python/xen/xend/server/SrvDaemon.py @@ -6,7 +6,9 @@ ########################################################### import os +import os.path import signal +import stat import sys import threading import time @@ -19,6 +21,7 @@ import xen.lowlevel.xc from xen.xend.XendLogging import log from xen.xend import osdep +from xen.util import mkdir import relocate import SrvServer @@ -57,6 +60,14 @@ class Daemon: return running + def reloadConfig(self): + """ + """ + pid = read_pid(XEND_PID_FILE) + if find_process(pid, XEND_PROCESS_NAME): + os.kill(pid, signal.SIGHUP) + + def status(self): """Returns the status of the xend daemon. The return value is defined by the LSB: @@ -102,17 +113,31 @@ class Daemon: # Detach from standard file descriptors, and redirect them to # /dev/null or the log as appropriate. + # We open the log file first, so that we can diagnose a failure to do + # so _before_ we close stderr. + try: + parent = os.path.dirname(XEND_DEBUG_LOG) + mkdir.parents(parent, stat.S_IRWXU) + fd = os.open(XEND_DEBUG_LOG, os.O_WRONLY|os.O_CREAT|os.O_APPEND) + except Exception, exn: + print >>sys.stderr, exn + print >>sys.stderr, ("Xend failed to open %s. Exiting!" % + XEND_DEBUG_LOG) + sys.exit(1) + os.close(0) os.close(1) os.close(2) if XEND_DEBUG: os.open('/dev/null', os.O_RDONLY) - os.open(XEND_DEBUG_LOG, os.O_WRONLY|os.O_CREAT|os.O_APPEND) - os.dup(1) + os.dup(fd) + os.dup(fd) else: os.open('/dev/null', os.O_RDWR) os.dup(0) - os.open(XEND_DEBUG_LOG, os.O_WRONLY|os.O_CREAT|os.O_APPEND) + os.dup(fd) + os.close(fd) + print >>sys.stderr, ("Xend started at %s." % time.asctime(time.localtime())) @@ -253,6 +278,8 @@ class Daemon: modulename = m.group(1) if re.search('sxp.py', modulename): return None + if re.search('SrvServer.py', modulename): + return None self.traceindent += 1 self.print_trace("> %s:%s\n" % (modulename, code.co_name)) diff --git a/tools/python/xen/xend/server/SrvDomain.py b/tools/python/xen/xend/server/SrvDomain.py index 82998e4d98..00c18c9386 100644 --- a/tools/python/xen/xend/server/SrvDomain.py +++ b/tools/python/xen/xend/server/SrvDomain.py @@ -21,6 +21,7 @@ from xen.web import http from xen.xend import sxp from xen.xend import XendDomain from xen.xend.Args import FormFn +from xen.xend.XendLogging import log from xen.web.SrvDir import SrvDir @@ -63,6 +64,18 @@ class SrvDomain(SrvDir): self.acceptCommand(req) return self.dom.shutdown(req.args['reason'][0]) + def op_delete(self, _, req): + self.acceptCommand(req) + return self.xd.domain_delete(self.dom.getName()) + + def op_start(self, _, req): + self.acceptCommand(req) + paused = False + if 'paused' in req.args and req.args['paused'] == [1]: + paused = True + log.debug("Starting domain " + self.dom.getName() + " " + str(paused)) + return self.xd.domain_start(self.dom.getName(), paused) + def op_sysrq(self, _, req): self.acceptCommand(req) return self.dom.send_sysrq(int(req.args['key'][0])) diff --git a/tools/python/xen/xend/server/SrvDomainDir.py b/tools/python/xen/xend/server/SrvDomainDir.py index fe97b29aef..950b26316d 100644 --- a/tools/python/xen/xend/server/SrvDomainDir.py +++ b/tools/python/xen/xend/server/SrvDomainDir.py @@ -25,6 +25,8 @@ from xen.xend import XendDomain from xen.xend.XendDomainInfo import XendDomainInfo from xen.xend.Args import FormFn from xen.xend.XendError import XendError +from xen.xend.XendLogging import log +from xen.xend.XendConstants import DOM_STATE_RUNNING from xen.web.SrvDir import SrvDir from SrvDomain import SrvDomain @@ -101,6 +103,35 @@ class SrvDomainDir(SrvDir): out.close() return val + def op_new(self, _, req): + """Define a new domain. + Expects the domain config in request parameter 'config' in SXP format. + """ + ok = 0 + errmsg = '' + try: + configstring = req.args.get('config')[0] + #print 'op_create>', 'config:', configstring + pin = sxp.Parser() + pin.input(configstring) + pin.input_eof() + config = pin.get_val() + ok = 1 + except sxp.ParseError, ex: + errmsg = 'Invalid configuration ' + str(ex) + except Exception, ex: + print 'op_create> Exception in config', ex + traceback.print_exc() + errmsg = 'Configuration error ' + str(ex) + if not ok: + raise XendError(errmsg) + try: + self.xd.domain_new(config) + except Exception, ex: + print 'op_create> Exception creating domain:' + traceback.print_exc() + raise XendError("Error creating domain: " + str(ex)) + def op_restore(self, op, req): """Restore a domain from file. @@ -159,16 +190,19 @@ class SrvDomainDir(SrvDir): if detail: sxp.show(map(XendDomainInfo.sxpr, self.xd.list()), out=req) else: - sxp.show(self.xd.list_names(), out=req) + state = DOM_STATE_RUNNING + if 'state' in req.args and len(req.args['state']) > 0: + state = req.args['state'][0] + log.debug("Listing domains in state " + str(state)) + sxp.show(self.xd.list_names(state), out=req) else: domains = self.xd.list_sorted() req.write('
    ') for d in domains: req.write( - '
  • Domain %s: id = %s, memory = %d, ' - 'ssidref = %d.' + '
  • Domain %s: id = %s, memory = %d' % (url, d.getName(), d.getName(), d.getDomid(), - d.getMemoryTarget(), d.getSsidref())) + d.getMemoryTarget())) req.write('
  • ') req.write('
') diff --git a/tools/python/xen/xend/server/SrvServer.py b/tools/python/xen/xend/server/SrvServer.py index 934e6d300b..4dfe8584e0 100644 --- a/tools/python/xen/xend/server/SrvServer.py +++ b/tools/python/xen/xend/server/SrvServer.py @@ -62,9 +62,11 @@ xroot = XendRoot.instance() class XendServers: - def __init__(self): + def __init__(self, root): self.servers = [] + self.root = root self.cleaningUp = False + self.reloadingConfig = False def add(self, server): self.servers.append(server) @@ -78,6 +80,11 @@ class XendServers: except: pass + def reloadConfig(self, signum = 0, frame = None): + log.debug("SrvServer.reloadConfig()") + self.reloadingConfig = True + self.cleanup(signum, frame) + def start(self, status): # Running the network script will spawn another process, which takes # the status fd with it unless we set FD_CLOEXEC. Failing to do this @@ -86,66 +93,90 @@ class XendServers: fcntl.fcntl(status, fcntl.F_SETFD, fcntl.FD_CLOEXEC) Vifctl.network('start') - threads = [] - for server in self.servers: - thread = Thread(target=server.run, name=server.__class__.__name__) - if isinstance(server, HttpServer): - thread.setDaemon(True) - thread.start() - threads.append(thread) - - - # check for when all threads have initialized themselves and then - # close the status pipe - - threads_left = True - while threads_left: - threads_left = False - - for server in self.servers: - if not server.ready: - threads_left = True - break - - if threads_left: - time.sleep(.5) - - if status: - status.write('0') - status.close() # Prepare to catch SIGTERM (received when 'xend stop' is executed) # and call each server's cleanup if possible signal.signal(signal.SIGTERM, self.cleanup) + signal.signal(signal.SIGHUP, self.reloadConfig) - # Interruptible Thread.join - Python Bug #1167930 - # Replaces: for t in threads: t.join() - # Reason: The above will cause python signal handlers to be - # blocked so we're not able to catch SIGTERM in any - # way for cleanup - runningThreads = threads - while len(runningThreads) > 0: - try: - for t in threads: - t.join(1.0) - runningThreads = [t for t in threads - if t.isAlive() and not t.isDaemon()] - if self.cleaningUp and len(runningThreads) > 0: - log.debug("Waiting for %s." % - [x.getName() for x in runningThreads]) - except: - pass + while True: + threads = [] + for server in self.servers: + if server.ready: + continue + + thread = Thread(target=server.run, name=server.__class__.__name__) + if isinstance(server, HttpServer): + thread.setDaemon(True) + thread.start() + threads.append(thread) -def create(): - root = SrvDir() - root.putChild('xend', SrvRoot()) - servers = XendServers() - if xroot.get_xend_http_server(): + # check for when all threads have initialized themselves and then + # close the status pipe + + retryCount = 0 + threads_left = True + while threads_left: + threads_left = False + + for server in self.servers: + if not server.ready: + threads_left = True + break + + if threads_left: + time.sleep(.5) + retryCount += 1 + if retryCount > 60: + for server in self.servers: + if not server.ready: + log.error("Server " + + server.__class__.__name__ + + " did not initialise!") + break + + if status: + status.write('0') + status.close() + status = None + + # Interruptible Thread.join - Python Bug #1167930 + # Replaces: for t in threads: t.join() + # Reason: The above will cause python signal handlers to be + # blocked so we're not able to catch SIGTERM in any + # way for cleanup + runningThreads = threads + while len(runningThreads) > 0: + try: + for t in threads: + t.join(1.0) + runningThreads = [t for t in threads + if t.isAlive() and not t.isDaemon()] + if self.cleaningUp and len(runningThreads) > 0: + log.debug("Waiting for %s." % + [x.getName() for x in runningThreads]) + except: + pass + + if self.reloadingConfig: + log.info("Restarting all XML-RPC and Xen-API servers...") + self.cleaningUp = False + self.reloadingConfig = False + xroot.set_config() + new_servers = [x for x in self.servers + if isinstance(x, HttpServer)] + self.servers = new_servers + _loadConfig(self, self.root, True) + else: + break + +def _loadConfig(servers, root, reload): + if not reload and xroot.get_xend_http_server(): servers.add(HttpServer(root, xroot.get_xend_address(), xroot.get_xend_port())) - if xroot.get_xend_unix_server(): + if not reload and xroot.get_xend_unix_server(): path = xroot.get_xend_unix_path() log.info('unix path=' + path) servers.add(UnixHttpServer(root, path)) @@ -154,7 +185,7 @@ def create(): if api_cfg: try: addrs = [(str(x[0]).split(':'), - len(x) > 1 and x[1] or XendAPI.AUTH_NONE, + len(x) > 1 and x[1] or XendAPI.AUTH_PAM, len(x) > 2 and x[2] and map(re.compile, x[2].split(" ")) or None) for x in api_cfg] @@ -188,4 +219,11 @@ def create(): if xroot.get_xend_unix_xmlrpc_server(): servers.add(XMLRPCServer(XendAPI.AUTH_PAM)) + + +def create(): + root = SrvDir() + root.putChild('xend', SrvRoot()) + servers = XendServers(root) + _loadConfig(servers, root, False) return servers diff --git a/tools/python/xen/xend/server/XMLRPCServer.py b/tools/python/xen/xend/server/XMLRPCServer.py index 45540995bd..c29b4b6bdd 100644 --- a/tools/python/xen/xend/server/XMLRPCServer.py +++ b/tools/python/xen/xend/server/XMLRPCServer.py @@ -16,6 +16,8 @@ # Copyright (C) 2006 XenSource Ltd. #============================================================================ +import errno +import socket import types import xmlrpclib from xen.util.xmlrpclib2 import UnixXMLRPCServer, TCPXMLRPCServer @@ -105,20 +107,25 @@ class XMLRPCServer: "; authentication has been disabled for this server." or ".") - if self.use_tcp: - log.info("Opening TCP XML-RPC server on %s%d%s", - self.host and '%s:' % self.host or - 'all interfaces, port ', - self.port, authmsg) - self.server = TCPXMLRPCServer((self.host, self.port), - self.hosts_allowed, - logRequests = False) - else: - log.info("Opening Unix domain socket XML-RPC server on %s%s", - self.path, authmsg) - self.server = UnixXMLRPCServer(self.path, self.hosts_allowed, - logRequests = False) - + try: + if self.use_tcp: + log.info("Opening TCP XML-RPC server on %s%d%s", + self.host and '%s:' % self.host or + 'all interfaces, port ', + self.port, authmsg) + self.server = TCPXMLRPCServer((self.host, self.port), + self.hosts_allowed, + logRequests = False) + else: + log.info("Opening Unix domain socket XML-RPC server on %s%s", + self.path, authmsg) + self.server = UnixXMLRPCServer(self.path, self.hosts_allowed, + logRequests = False) + except socket.error, exn: + log.error('Cannot start server: %s!', exn.args[1]) + ready = True + running = False + return # Register Xen API Functions # ------------------------------------------------------------------- @@ -177,6 +184,11 @@ class XMLRPCServer: def cleanup(self): log.debug("XMLRPCServer.cleanup()") + try: + self.server.socket.close() + except Exception, exn: + log.exception(exn) + pass def shutdown(self): self.running = False diff --git a/tools/python/xen/xend/server/blkif.py b/tools/python/xen/xend/server/blkif.py index 6bd49acfc9..bba11ebe1f 100644 --- a/tools/python/xen/xend/server/blkif.py +++ b/tools/python/xen/xend/server/blkif.py @@ -21,7 +21,6 @@ import string from xen.util import blkif from xen.util import security -from xen.xend import sxp from xen.xend.XendError import VmError from xen.xend.server.DevController import DevController @@ -37,9 +36,9 @@ class BlkifController(DevController): def getDeviceDetails(self, config): """@see DevController.getDeviceDetails""" - uname = sxp.child_value(config, 'uname', '') - dev = sxp.child_value(config, 'dev', '') - + uname = config.get('uname', '') + dev = config.get('dev', '') + if 'ioemu:' in dev: (_, dev) = string.split(dev, ':', 1) try: @@ -59,17 +58,17 @@ class BlkifController(DevController): except ValueError: (typ, params) = ("", "") - mode = sxp.child_value(config, 'mode', 'r') + mode = config.get('mode', 'r') if mode not in ('r', 'w', 'w!'): raise VmError('Invalid mode') - back = { 'dev' : dev, - 'type' : typ, - 'params' : params, - 'mode' : mode - } + back = {'dev' : dev, + 'type' : typ, + 'params' : params, + 'mode' : mode, + } - uuid = sxp.child_value(config, 'uuid') + uuid = config.get('uuid') if uuid: back['uuid'] = uuid @@ -125,6 +124,8 @@ class BlkifController(DevController): config['dev'] = dev if typ and params: config['uname'] = typ +':' + params + else: + config['uname'] = None if mode: config['mode'] = mode if uuid: diff --git a/tools/python/xen/xend/server/iopif.py b/tools/python/xen/xend/server/iopif.py index 96651a7c95..3b1a263736 100644 --- a/tools/python/xen/xend/server/iopif.py +++ b/tools/python/xen/xend/server/iopif.py @@ -22,7 +22,6 @@ import types import xen.lowlevel.xc -from xen.xend import sxp from xen.xend.XendError import VmError from xen.xend.server.DevController import DevController @@ -49,13 +48,12 @@ class IOPortsController(DevController): def __init__(self, vm): DevController.__init__(self, vm) - def getDeviceDetails(self, config): """@see DevController.getDeviceDetails""" def get_param(field): try: - val = sxp.child_value(config, field) + val = config.get(field) if not val: raise VmError('ioports: Missing %s config setting' % field) diff --git a/tools/python/xen/xend/server/irqif.py b/tools/python/xen/xend/server/irqif.py index 6e539dbab9..346ad7cf4d 100644 --- a/tools/python/xen/xend/server/irqif.py +++ b/tools/python/xen/xend/server/irqif.py @@ -45,7 +45,7 @@ class IRQController(DevController): def get_param(field): try: - val = sxp.child_value(config, field) + val = config.get(field) if not val: raise VmError('irq: Missing %s config setting' % field) diff --git a/tools/python/xen/xend/server/netif.py b/tools/python/xen/xend/server/netif.py index 4c7e359617..8b0a2ce66c 100644 --- a/tools/python/xen/xend/server/netif.py +++ b/tools/python/xen/xend/server/netif.py @@ -24,7 +24,6 @@ import os import random import re -from xen.xend import sxp from xen.xend import XendRoot from xen.xend.server.DevController import DevController @@ -139,22 +138,15 @@ class NetifController(DevController): def getDeviceDetails(self, config): """@see DevController.getDeviceDetails""" - def _get_config_ipaddr(config): - val = [] - for ipaddr in sxp.children(config, elt='ip'): - val.append(sxp.child0(ipaddr)) - return val - script = os.path.join(xroot.network_script_dir, - sxp.child_value(config, 'script', - xroot.get_vif_script())) - typ = sxp.child_value(config, 'type') - bridge = sxp.child_value(config, 'bridge') - mac = sxp.child_value(config, 'mac') - vifname = sxp.child_value(config, 'vifname') - rate = sxp.child_value(config, 'rate') - uuid = sxp.child_value(config, 'uuid') - ipaddr = _get_config_ipaddr(config) + config.get('script', xroot.get_vif_script())) + typ = config.get('type') + bridge = config.get('bridge') + mac = config.get('mac') + vifname = config.get('vifname') + rate = config.get('rate') + uuid = config.get('uuid') + ipaddr = config.get('ip') devid = self.allocateDeviceID() @@ -172,7 +164,7 @@ class NetifController(DevController): front = { 'handle' : "%i" % devid, 'mac' : mac } if ipaddr: - back['ip'] = ' '.join(ipaddr) + back['ip'] = ipaddr if bridge: back['bridge'] = bridge if vifname: @@ -197,7 +189,7 @@ class NetifController(DevController): network_script_dir = xroot.network_script_dir + os.sep result['script'] = script.replace(network_script_dir, "") if ip: - result['ip'] = ip.split(" ") + result['ip'] = ip if bridge: result['bridge'] = bridge if mac: diff --git a/tools/python/xen/xend/server/pciif.py b/tools/python/xen/xend/server/pciif.py index eaab90529f..68af9d78cc 100644 --- a/tools/python/xen/xend/server/pciif.py +++ b/tools/python/xen/xend/server/pciif.py @@ -53,60 +53,29 @@ class PciController(DevController): def getDeviceDetails(self, config): """@see DevController.getDeviceDetails""" - #log.debug('pci config='+sxp.to_string(config)) - - def get_param(config, field, default=None): + def parse_hex(val): try: - val = sxp.child_value(config, field) - - if not val: - if default==None: - raise VmError('pci: Missing %s config setting' % field) - else: - return default - if isinstance(val, types.StringTypes): return int(val, 16) else: return val - except: - if default==None: - raise VmError('pci: Invalid config setting %s: %s' % - (field, val)) - else: - return default - - back = {} - - val = sxp.child_value(config, 'dev') - if isinstance(val, (types.ListType, types.TupleType)): - pcidevid = 0 - for dev_config in sxp.children(config, 'dev'): - domain = get_param(dev_config, 'domain', 0) - bus = get_param(dev_config,'bus') - slot = get_param(dev_config,'slot') - func = get_param(dev_config,'func') - - self.setupDevice(domain, bus, slot, func) - - back['dev-%i' % pcidevid]="%04x:%02x:%02x.%02x"% \ - (domain, bus, slot, func) - pcidevid+=1 + except ValueError: + return None - back['num_devs']=str(pcidevid) - - else: - # Xen 2.0 configuration compatibility - domain = get_param(config, 'domain', 0) - bus = get_param(config, 'bus') - slot = get_param(config, 'dev') - func = get_param(config, 'func') - + back = {} + pcidevid = 0 + for pci_config in config.get('devs', []): + domain = parse_hex(pci_config.get('domain', 0)) + bus = parse_hex(pci_config.get('bus', 0)) + slot = parse_hex(pci_config.get('slot', 0)) + func = parse_hex(pci_config.get('func', 0)) self.setupDevice(domain, bus, slot, func) + back['dev-%i' % pcidevid] = "%04x:%02x:%02x.%02x" % \ + (domain, bus, slot, func) + pcidevid += 1 - back['dev-0']="%04x:%02x:%02x.%02x"%(domain, bus, slot, func) - back['num_devs']=str(1) - + back['num_devs']=str(pcidevid) + back['uuid'] = config.get('uuid','') return (0, back, {}) def getDeviceConfiguration(self, devid): @@ -129,7 +98,8 @@ class PciController(DevController): 'slot': '0x%(slot)s' % pci_dev_info, 'func': '0x%(func)s' % pci_dev_info}) - result['dev'] = pci_devs + result['devs'] = pci_devs + result['uuid'] = self.readBackend(devid, 'uuid') return result def configuration(self, devid): @@ -142,7 +112,8 @@ class PciController(DevController): sxpr = [self.deviceClass] # remove devs - devs = configDict.pop('dev', []) + devs = configDict.pop('devs', []) + for dev in devs: dev_sxpr = ['dev'] for dev_item in dev.items(): diff --git a/tools/python/xen/xend/server/pciquirk.py b/tools/python/xen/xend/server/pciquirk.py index ea53eee5b2..c0a4a68c83 100644 --- a/tools/python/xen/xend/server/pciquirk.py +++ b/tools/python/xen/xend/server/pciquirk.py @@ -15,25 +15,24 @@ class PCIQuirk: self.device = device self.subvendor = subvendor self.subdevice = subdevice - self.domain = domain - self.bus = bus - self.slot = slot - self.func = func + self.domain = domain + self.bus = bus + self.slot = slot + self.func = func self.devid = "%04x:%04x:%04x:%04x" % (vendor, device, subvendor, subdevice) - self.pciid = "%04x:%02x:%02x.%01x" % (domain, bus, slot, func) + self.pciid = "%04x:%02x:%02x.%01x" % (domain, bus, slot, func) + self.quirks = self.__getQuirksByID() - self.quirks = self.__getQuirksByID( ) - - self.__sendQuirks( ) - self.__sendPermDevs( ) + self.__sendQuirks() + self.__sendPermDevs() def __matchPCIdev( self, list ): ret = False if list == None: return False for id in list: - if id.startswith( self.devid[:9] ): # id's vendor and device ID match + if id.startswith(self.devid[:9]): # id's vendor and device ID match skey = id.split(':') size = len(skey) if (size == 2): # subvendor/subdevice not suplied @@ -41,13 +40,13 @@ class PCIQuirk: break elif (size == 4): # check subvendor/subdevice # check subvendor - subven = '%04x' % self.subvendor + subven = '%04x' % self.subvendor if ((skey[2] != 'FFFF') and (skey[2] != 'ffff') and (skey[2] != subven)): continue # check subdevice - subdev = '%04x' % self.subdevice + subdev = '%04x' % self.subdevice if ((skey[3] != 'FFFF') and (skey[3] != 'ffff') and (skey[3] != subdev)): @@ -101,8 +100,8 @@ class PCIQuirk: self.slot, self.func, quirk) ) f.close() except Exception, e: - raise VmError("pci: failed to open/write/close quirks sysfs " + \ - "node - " + str(e)) + raise VmError("pci: failed to open/write/close quirks " + + "sysfs node - " + str(e)) def __devIsUnconstrained( self ): if os.path.exists(PERMISSIVE_CONFIG_FILE): @@ -126,20 +125,22 @@ class PCIQuirk: devices = child_at(child(pci_perm_dev_config, 'unconstrained_dev_ids'),0) if self.__matchPCIdev( devices ): - log.debug("Permissive mode enabled for PCI device [%s]" % self.devid) + log.debug("Permissive mode enabled for PCI device [%s]" % + self.devid) return True - log.debug("Permissive mode NOT enabled for PCI device [%s]" % self.devid) + log.debug("Permissive mode NOT enabled for PCI device [%s]" % + self.devid) return False def __sendPermDevs(self): if self.__devIsUnconstrained( ): - log.debug("Unconstrained device: %04x:%02x:%02x.%1x" % (self.domain, - self.bus, self.slot, self.func)) + log.debug("Unconstrained device: %04x:%02x:%02x.%1x" % + (self.domain, self.bus, self.slot, self.func)) try: f = file(PERMISSIVE_SYSFS_NODE ,"w") f.write( "%04x:%02x:%02x.%1x" % (self.domain, self.bus, - self.slot, self.func) ) + self.slot, self.func)) f.close() except Exception, e: - raise VmError("pci: failed to open/write/close permissive " + \ - "sysfs node: " + str(e)) + raise VmError("pci: failed to open/write/close permissive " + + "sysfs node: " + str(e)) diff --git a/tools/python/xen/xend/server/tests/test_controllers.py b/tools/python/xen/xend/server/tests/test_controllers.py index 16b7941e5a..aa57f6321c 100644 --- a/tools/python/xen/xend/server/tests/test_controllers.py +++ b/tools/python/xen/xend/server/tests/test_controllers.py @@ -21,13 +21,13 @@ class test_controllers(unittest.TestCase): def testNetif(self): controller = self.controllerInstance(netif.NetifController) - self.assertNetif(controller.getDeviceDetails(['vif']), None) + self.assertNetif(controller.getDeviceDetails({}), None) self.assertNetif( - controller.getDeviceDetails( - ['vif', ['mac', 'aa:bb:cc:dd:ee:ff']]), + controller.getDeviceDetails({'mac': 'aa:bb:cc:dd:ee:ff'}), 'aa:bb:cc:dd:ee:ff') + def assertNetif(self, results, expectedMac): (devid, backdets, frontdets) = results diff --git a/tools/python/xen/xend/server/tpmif.py b/tools/python/xen/xend/server/tpmif.py index 494a197b8e..c2a5f1f64b 100644 --- a/tools/python/xen/xend/server/tpmif.py +++ b/tools/python/xen/xend/server/tpmif.py @@ -18,10 +18,8 @@ # Copyright (C) 2005 XenSource Ltd #============================================================================ -"""Support for virtual TPM interfaces. -""" +"""Support for virtual TPM interfaces.""" -from xen.xend import sxp from xen.xend import XendRoot from xen.xend.XendLogging import log from xen.xend.XendError import XendError @@ -49,12 +47,12 @@ class TPMifController(DevController): """@see DevController.getDeviceDetails""" devid = self.allocateDeviceID() - inst = int(sxp.child_value(config, 'pref_instance', '-1')) + inst = int(config.get('pref_instance', -1)) if inst == -1: - inst = int(sxp.child_value(config, 'instance' , '0')) + inst = int(config.get('instance', 0)) - typ = sxp.child_value(config, 'type') - uuid = sxp.child_value(config, 'uuid') + typ = config.get('type') + uuid = config.get('uuid') log.info("The domain has a TPM with pref. instance %d and devid %d.", inst, devid) diff --git a/tools/python/xen/xend/server/vfbif.py b/tools/python/xen/xend/server/vfbif.py new file mode 100644 index 0000000000..e24c2284f6 --- /dev/null +++ b/tools/python/xen/xend/server/vfbif.py @@ -0,0 +1,74 @@ +from xen.xend.server.DevController import DevController + +from xen.xend.XendError import VmError +import xen.xend +import os + +def spawn_detached(path, args, env): + p = os.fork() + if p == 0: + os.spawnve(os.P_NOWAIT, path, args, env) + os._exit(0) + else: + os.waitpid(p, 0) + +class VfbifController(DevController): + """Virtual frame buffer controller. Handles all vfb devices for a domain. + Note that we only support a single vfb per domain at the moment. + """ + + def __init__(self, vm): + DevController.__init__(self, vm) + self.config = {} + + def getDeviceDetails(self, config): + """@see DevController.getDeviceDetails""" + devid = 0 + back = {} + front = {} + return (devid, back, front) + + def getDeviceConfiguration(self, devid): + r = DevController.getDeviceConfiguration(self, devid) + for (k,v) in self.config.iteritems(): + r[k] = v + return r + + def createDevice(self, config): + DevController.createDevice(self, config) + self.config = config + std_args = [ "--domid", "%d" % self.vm.getDomid(), + "--title", self.vm.getName() ] + t = config.get("type", None) + if t == "vnc": + # Try to start the vnc backend + args = [xen.util.auxbin.pathTo("xen-vncfb")] + if config.has_key("vncunused"): + args += ["--unused"] + elif config.has_key("vncdisplay"): + args += ["--vncport", "%d" % (5900 + config["vncdisplay"])] + vnclisten = config.get("vnclisten", + xen.xend.XendRoot.instance().get_vnclisten_address()) + args += [ "--listen", vnclisten ] + spawn_detached(args[0], args + std_args, os.environ) + elif t == "sdl": + args = [xen.util.auxbin.pathTo("xen-sdlfb")] + env = dict(os.environ) + if config.has_key("display"): + env['DISPLAY'] = config["display"] + if config.has_key("xauthority"): + env['XAUTHORITY'] = config["xauthority"] + spawn_detached(args[0], args + std_args, env) + else: + raise VmError('Unknown vfb type %s (%s)' % (t, repr(config))) + +class VkbdifController(DevController): + """Virtual keyboard controller. Handles all vkbd devices for a domain. + """ + + def getDeviceDetails(self, config): + """@see DevController.getDeviceDetails""" + devid = 0 + back = {} + front = {} + return (devid, back, front) diff --git a/tools/python/xen/xend/xenstore/xstransact.py b/tools/python/xen/xend/xenstore/xstransact.py index c6a3bb9d82..dd9aa98544 100644 --- a/tools/python/xen/xend/xenstore/xstransact.py +++ b/tools/python/xen/xend/xenstore/xstransact.py @@ -11,11 +11,12 @@ from xen.xend.xenstore.xsutil import xshandle class xstransact: def __init__(self, path = ""): - assert path is not None self.in_transaction = False # Set this temporarily -- if this # constructor fails, then we need to # protect __del__. + + assert path is not None self.path = path.rstrip("/") self.transaction = xshandle().transaction_start() self.in_transaction = True diff --git a/tools/python/xen/xend/xenstore/xswatch.py b/tools/python/xen/xend/xenstore/xswatch.py index e84fe83ba9..a1ea740a94 100644 --- a/tools/python/xen/xend/xenstore/xswatch.py +++ b/tools/python/xen/xend/xenstore/xswatch.py @@ -5,6 +5,7 @@ # Public License. See the file "COPYING" in the main directory of # this archive for more details. +import errno import threading from xen.xend.xenstore.xsutil import xshandle @@ -65,7 +66,15 @@ def watchMain(): watch = we[1] res = watch.fn(we[0], *watch.args, **watch.kwargs) if not res: - watch.unwatch() + try: + watch.unwatch() + except RuntimeError, exn: + if exn.args[0] == errno.ENOENT: + # The watch has already been unregistered -- that's + # fine. + pass + else: + raise except: log.exception("read_watch failed") # Ignore this exception -- there's no point throwing it diff --git a/tools/python/xen/xm/XenAPI.py b/tools/python/xen/xm/XenAPI.py new file mode 100644 index 0000000000..7d469fc997 --- /dev/null +++ b/tools/python/xen/xm/XenAPI.py @@ -0,0 +1,132 @@ +#============================================================================ +# This library is free software; you can redistribute it and/or +# modify it under the terms of version 2.1 of the GNU Lesser General Public +# License as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2006 XenSource Inc. +#============================================================================ +# +# Parts of this file are based upon xmlrpclib.py, the XML-RPC client +# interface included in the Python distribution. +# +# Copyright (c) 1999-2002 by Secret Labs AB +# Copyright (c) 1999-2002 by Fredrik Lundh +# +# By obtaining, using, and/or copying this software and/or its +# associated documentation, you agree that you have read, understood, +# and will comply with the following terms and conditions: +# +# Permission to use, copy, modify, and distribute this software and +# its associated documentation for any purpose and without fee is +# hereby granted, provided that the above copyright notice appears in +# all copies, and that both that copyright notice and this permission +# notice appear in supporting documentation, and that the name of +# Secret Labs AB or the author not be used in advertising or publicity +# pertaining to distribution of the software without specific, written +# prior permission. +# +# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD +# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- +# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR +# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# -------------------------------------------------------------------- + +import xmlrpclib + +import xen.util.xmlrpclib2 + + +class Failure(Exception): + def __init__(self, details): + self.details = details + + def __str__(self): + return "Xen-API failure: %s" % str(self.details) + + +class Session(xen.util.xmlrpclib2.ServerProxy): + """A server proxy and session manager for communicating with Xend using + the Xen-API. + + Example: + + session = Session('http://localhost:9363/') + session.login_with_password('me', 'mypassword') + session.xenapi.VM.start(vm_uuid) + session.xenapi.session.logout() + + For now, this class also supports the legacy XML-RPC API, using + session.xend.domain('Domain-0') and similar. This support will disappear + once there is a working Xen-API replacement for every call in the legacy + API. + """ + + def __init__(self, uri, transport=None, encoding=None, verbose=0, + allow_none=1): + xen.util.xmlrpclib2.ServerProxy.__init__(self, uri, transport, + encoding, verbose, + allow_none) + self._session = None + + + def _xen_request(self, methodname, params): + full_params = (self._session,) + params + return _parse_result(getattr(self, methodname)(*full_params)) + + + def _login(self, method, username, password): + self._session = _parse_result( + getattr(self, 'session.%s' % method)(username, password)) + + + def __getattr__(self, name): + if name == 'xenapi': + return _Dispatcher(self._xen_request, None) + elif name.startswith('login'): + return lambda u, p: self._login(name, u, p) + else: + return xen.util.xmlrpclib2.ServerProxy.__getattr__(self, name) + + +def _parse_result(result): + if 'Status' not in result: + raise xmlrpclib.Fault(500, 'Missing Status in response from server') + if result['Status'] == 'Success': + if 'Value' in result: + return result['Value'] + else: + raise xmlrpclib.Fault(500, + 'Missing Value in response from server') + else: + if 'ErrorDescription' in result: + raise Failure(result['ErrorDescription']) + else: + raise xmlrpclib.Fault( + 500, 'Missing ErrorDescription in response from server') + + +# Based upon _Method from xmlrpclib. +class _Dispatcher: + def __init__(self, send, name): + self.__send = send + self.__name = name + def __getattr__(self, name): + if self.__name is None: + return _Dispatcher(self.__send, name) + else: + return _Dispatcher(self.__send, "%s.%s" % (self.__name, name)) + def __call__(self, *args): + return self.__send(self.__name, args) diff --git a/tools/python/xen/xm/create.py b/tools/python/xen/xm/create.py index e1df262c8f..aa0c03cac3 100644 --- a/tools/python/xen/xm/create.py +++ b/tools/python/xen/xm/create.py @@ -29,13 +29,13 @@ import xmlrpclib from xen.xend import sxp from xen.xend import PrettyPrint import xen.xend.XendClient -from xen.xend.XendClient import server from xen.xend.XendBootloader import bootloader from xen.util import blkif from xen.util import security from xen.xm.opts import * +from main import server import console @@ -284,6 +284,18 @@ gopts.var('usbport', val='PATH', use="""Add a physical USB port to a domain, as specified by the path to that port. This option may be repeated to add more than one port.""") +gopts.var('vfb', val="type={vnc,sdl},vncunused=1,vncdisplay=N,vnclisten=ADDR,display=DISPLAY,xauthority=XAUTHORITY", + fn=append_value, default=[], + use="""Make the domain a framebuffer backend. + The backend type should be either sdl or vnc. + For type=vnc, connect an external vncviewer. The server will listen + on ADDR (default 127.0.0.1) on port N+5900. N defaults to the + domain id. If vncunused=1, the server will try to find an arbitrary + unused port above 5900. + For type=sdl, a viewer will be started automatically using the + given DISPLAY and XAUTHORITY, which default to the current user's + ones.""") + gopts.var('vif', val="type=TYPE,mac=MAC,bridge=BRIDGE,ip=IPADDR,script=SCRIPT,backend=DOM,vifname=NAME", fn=append_value, default=[], use="""Add a network interface with the given MAC address and bridge. @@ -512,8 +524,8 @@ def configure_image(vals): config_image.append(['args', vals.extra]) if vals.builder == 'hvm': - configure_hvm(config_image, vals) - + configure_hvm(config_image, vals) + return config_image def configure_disks(config_devs, vals): @@ -564,6 +576,23 @@ def configure_usb(config_devs, vals): config_usb = ['usbport', ['path', path]] config_devs.append(['device', config_usb]) +def configure_vfbs(config_devs, vals): + for f in vals.vfb: + d = comma_sep_kv_to_dict(f) + config = ['vfb'] + if not d.has_key("type"): + d['type'] = 'sdl' + for (k,v) in d.iteritems(): + if not k in [ 'vnclisten', 'vncunused', 'vncdisplay', 'display', + 'xauthority', 'type' ]: + err("configuration option %s unknown to vfbs" % k) + config.append([k,v]) + if not d.has_key("display") and os.environ.has_key("DISPLAY"): + config.append(["display", os.environ['DISPLAY']]) + if not d.has_key("xauthority"): + config.append(["xauthority", get_xauthority()]) + config_devs.append(['device', ['vkbd']]) + config_devs.append(['device', config]) def configure_security(config, vals): """Create the config for ACM security labels. @@ -675,8 +704,10 @@ def configure_hvm(config_image, vals): config_image.append(['vncpasswd', vals.vncpasswd]) def run_bootloader(vals, config_image): + if not os.access(vals.bootloader, os.F_OK): + err("Bootloader '%s' does not exist" % vals.bootloader) if not os.access(vals.bootloader, os.X_OK): - err("Bootloader isn't executable") + err("Bootloader '%s' isn't executable" % vals.bootloader) if len(vals.disk) < 1: err("No disks configured and boot loader requested") (uname, dev, mode, backend) = vals.disk[0] @@ -742,6 +773,7 @@ def make_config(vals): configure_vifs(config_devs, vals) configure_usb(config_devs, vals) configure_vtpm(config_devs, vals) + configure_vfbs(config_devs, vals) configure_security(config, vals) config += config_devs diff --git a/tools/python/xen/xm/main.py b/tools/python/xen/xm/main.py index 0b7a728cd7..779b5280b8 100644 --- a/tools/python/xen/xm/main.py +++ b/tools/python/xen/xm/main.py @@ -21,6 +21,7 @@ """Grand unified management application for Xen. """ +import atexit import os import sys import re @@ -29,8 +30,10 @@ import socket import traceback import xmlrpclib import traceback +import time import datetime from select import select +import xml.dom.minidom import warnings warnings.filterwarnings('ignore', category=FutureWarning) @@ -38,12 +41,14 @@ warnings.filterwarnings('ignore', category=FutureWarning) from xen.xend import PrettyPrint from xen.xend import sxp from xen.xend import XendClient -from xen.xend.XendClient import server from xen.xend.XendConstants import * from xen.xm.opts import OptionError, Opts, wrap, set_true from xen.xm import console from xen.util import security +from xen.util.xmlrpclib2 import ServerProxy + +import XenAPI # getopt.gnu_getopt is better, but only exists in Python 2.3+. Use # getopt.getopt if gnu_getopt is not available. This will mean that options @@ -51,6 +56,12 @@ from xen.util import security if not hasattr(getopt, 'gnu_getopt'): getopt.gnu_getopt = getopt.getopt +XM_CONFIG_FILE = '/etc/xen/xm-config.xml' + +# Supported types of server +SERVER_LEGACY_XMLRPC = 'LegacyXMLRPC' +SERVER_XEN_API = 'Xen-API' + # General help message USAGE_HELP = "Usage: xm [args]\n\n" \ @@ -127,9 +138,9 @@ SUBCOMMAND_HELP = { # device commands - 'block-attach' : (' ', + 'block-attach' : (' [BackDomain]', 'Create a new virtual block device.'), - 'block-configure': (' [BackDomId]', + 'block-configure': (' [BackDomain]', 'Change block device configuration'), 'block-detach' : (' ', 'Destroy a domain\'s virtual block device.'), @@ -207,7 +218,13 @@ SUBCOMMAND_OPTIONS = { ('-L', '--live', 'Dump core without pausing the domain'), ('-C', '--crash', 'Crash domain after dumping core'), ), - 'restore': ( + 'start': ( + ('-p', '--paused', 'Do not unpause domain after starting it'), + ), + 'resume': ( + ('-p', '--paused', 'Do not unpause domain after resuming it'), + ), + 'restore': ( ('-p', '--paused', 'Do not unpause domain after restoring it'), ), } @@ -313,6 +330,40 @@ acm_commands = [ all_commands = (domain_commands + host_commands + scheduler_commands + device_commands + vnet_commands + acm_commands) + +## +# Configuration File Parsing +## + +config = None +if os.path.isfile(XM_CONFIG_FILE): + try: + config = xml.dom.minidom.parse(XM_CONFIG_FILE) + except: + print >>sys.stderr, ('Ignoring invalid configuration file %s.' % + XM_CONFIG_FILE) + +def parseServer(): + if config: + server = config.getElementsByTagName('server') + if server: + st = server[0].getAttribute('type') + if st != SERVER_XEN_API and st != SERVER_LEGACY_XMLRPC: + print >>sys.stderr, ('Invalid server type %s; using %s.' % + (st, SERVER_LEGACY_XMLRPC)) + st = SERVER_LEGACY_XMLRPC + return (st, server[0].getAttribute('uri')) + + return SERVER_LEGACY_XMLRPC, XendClient.uri + +def parseAuthentication(): + server = config.getElementsByTagName('server')[0] + return (server.getAttribute('username'), + server.getAttribute('password')) + +serverType, serverURI = parseServer() + + #################################################################### # # Help/usage printing functions @@ -463,6 +514,16 @@ def err(msg): print >>sys.stderr, "Error:", msg +def get_single_vm(dom): + uuids = server.xenapi.VM.get_by_name_label(dom) + n = len(uuids) + if n == 1: + return uuids[0] + else: + dominfo = server.xend.domain(dom, False) + return dominfo['uuid'] + + ######################################################################### # # Main xm functions @@ -571,15 +632,21 @@ def parse_doms_info(info): def get_status(n, t, d): return DOM_STATES[t(sxp.child_value(info, n, d))] - + + start_time = get_info('start_time', float, -1) + if start_time == -1: + up_time = float(-1) + else: + up_time = time.time() - start_time + return { 'domid' : get_info('domid', str, ''), 'name' : get_info('name', str, '??'), - 'mem' : get_info('memory_dynamic_max', int, 0), - 'vcpus' : get_info('online_vcpus', int, 0), + 'mem' : get_info('memory_dynamic_min', int, 0), + 'vcpus' : get_info('online_vcpus', int, 0), 'state' : get_info('state', str, ''), 'cpu_time' : get_info('cpu_time', float, 0), - 'up_time' : get_info('up_time', float, -1), + 'up_time' : up_time, 'seclabel' : security.get_security_printlabel(info), } @@ -742,24 +809,70 @@ def xm_vcpu_list(args): print format % locals() def xm_start(args): - arg_check(args, "start", 1) - dom = args[0] - server.xend.domain.start(dom) + arg_check(args, "start", 1, 2) + + try: + (options, params) = getopt.gnu_getopt(args, 'p', ['paused']) + except getopt.GetoptError, opterr: + err(opterr) + sys.exit(1) + + paused = False + for (k, v) in options: + if k in ['-p', '--paused']: + paused = True + + if len(params) != 1: + err("Wrong number of parameters") + usage('start') + sys.exit(1) + + dom = params[0] + if serverType == SERVER_XEN_API: + server.xenapi.VM.start(get_single_vm(dom), paused) + else: + server.xend.domain.start(dom, paused) def xm_delete(args): arg_check(args, "delete", 1) dom = args[0] - server.xend.domain.delete(dom) + if serverType == SERVER_XEN_API: + server.xenapi.VM.destroy(get_single_vm(dom)) + else: + server.xend.domain.delete(dom) def xm_suspend(args): arg_check(args, "suspend", 1) dom = args[0] - server.xend.domain.suspend(dom) + if serverType == SERVER_XEN_API: + server.xenapi.VM.suspend(get_single_vm(dom)) + else: + server.xend.domain.suspend(dom) def xm_resume(args): - arg_check(args, "resume", 1) - dom = args[0] - server.xend.domain.resume(dom) + arg_check(args, "resume", 1, 2) + + try: + (options, params) = getopt.gnu_getopt(args, 'p', ['paused']) + except getopt.GetoptError, opterr: + err(opterr) + sys.exit(1) + + paused = False + for (k, v) in options: + if k in ['-p', '--paused']: + paused = True + + if len(params) != 1: + err("Wrong number of parameters") + usage('resume') + sys.exit(1) + + dom = params[0] + if serverType == SERVER_XEN_API: + server.xenapi.VM.resume(get_single_vm(dom), paused) + else: + server.xend.domain.resume(dom, paused) def xm_reboot(args): arg_check(args, "reboot", 1, 3) @@ -775,13 +888,19 @@ def xm_pause(args): arg_check(args, "pause", 1) dom = args[0] - server.xend.domain.pause(dom) + if serverType == SERVER_XEN_API: + server.xenapi.VM.pause(get_single_vm(dom)) + else: + server.xend.domain.pause(dom) def xm_unpause(args): arg_check(args, "unpause", 1) dom = args[0] - server.xend.domain.unpause(dom) + if serverType == SERVER_XEN_API: + server.xenapi.VM.unpause(get_single_vm(dom)) + else: + server.xend.domain.unpause(dom) def xm_dump_core(args): live = False @@ -822,7 +941,10 @@ def xm_dump_core(args): def xm_rename(args): arg_check(args, "rename", 2) - server.xend.domain.setName(args[0], args[1]) + if serverType == SERVER_XEN_API: + server.xenapi.VM.set_name_label(get_single_vm(args[0]), args[1]) + else: + server.xend.domain.setName(args[0], args[1]) def xm_importcommand(command, args): cmd = __import__(command, globals(), locals(), 'xen.xm') @@ -880,7 +1002,12 @@ def xm_vcpu_set(args): def xm_destroy(args): arg_check(args, "destroy", 1) - server.xend.domain.destroy(args[0]) + + dom = args[0] + if serverType == SERVER_XEN_API: + server.xenapi.VM.hard_shutdown(get_single_vm(dom)) + else: + server.xend.domain.destroy(dom) def xm_domid(args): @@ -1094,7 +1221,7 @@ def xm_uptime(args): for dom in doms: d = parse_doms_info(dom) - if d['domid'] > 0: + if int(d['domid']) > 0: uptime = int(round(d['up_time'])) else: f=open('/proc/uptime', 'r') @@ -1121,10 +1248,10 @@ def xm_uptime(args): if short_mode: now = datetime.datetime.now() upstring = now.strftime(" %H:%M:%S") + " up " + upstring - upstring += ", " + d['name'] + " (" + str(d['domid']) + ")" + upstring += ", " + d['name'] + " (" + d['domid'] + ")" else: upstring += ':%(seconds)02d' % vars() - upstring = ("%(name)-32s %(domid)3d " % d) + upstring + upstring = ("%(name)-32s %(domid)3s " % d) + upstring print upstring @@ -1537,6 +1664,8 @@ def deprecated(old,new): "Command %s is deprecated. Please use xm %s instead." % (old, new)) def main(argv=sys.argv): + global server + if len(argv) < 2: usage() @@ -1555,6 +1684,19 @@ def main(argv=sys.argv): args = argv[2:] if cmd: try: + if serverType == SERVER_XEN_API: + server = XenAPI.Session(serverURI) + username, password = parseAuthentication() + server.login_with_password(username, password) + def logout(): + try: + server.xenapi.session.logout() + except: + pass + atexit.register(logout) + else: + server = ServerProxy(serverURI) + rc = cmd(args) if rc: usage() @@ -1575,6 +1717,9 @@ def main(argv=sys.argv): sys.exit(1) except SystemExit: sys.exit(1) + except XenAPI.Failure, exn: + err(str(exn)) + sys.exit(1) except xmlrpclib.Fault, ex: if ex.faultCode == XendClient.ERROR_INVALID_DOMAIN: err("Domain '%s' does not exist." % ex.faultString) diff --git a/tools/python/xen/xm/migrate.py b/tools/python/xen/xm/migrate.py index b362ae9057..72aca71932 100644 --- a/tools/python/xen/xm/migrate.py +++ b/tools/python/xen/xm/migrate.py @@ -21,9 +21,10 @@ import sys -from xen.xend.XendClient import server from xen.xm.opts import * +from main import server + gopts = Opts(use="""[options] DOM HOST Migrate domain DOM to host HOST. diff --git a/tools/python/xen/xm/new.py b/tools/python/xen/xm/new.py index fec2fc76e6..2c6cae84b6 100644 --- a/tools/python/xen/xm/new.py +++ b/tools/python/xen/xm/new.py @@ -21,10 +21,9 @@ import xmlrpclib from xen.xend import PrettyPrint from xen.xend import sxp from xen.xend import XendClient -from xen.xend.XendClient import server -from xen.xm.opts import * -from xen.xm.create import * +from opts import * +from create import * def make_unstarted_domain(opts, config): """Create an unstarted domain. @@ -58,6 +57,12 @@ def main(argv): if not opts: return + if type(config) == str: + try: + config = sxp.parse(file(config))[0] + except IOError, exn: + raise OptionError("Cannot read file %s: %s" % (config, exn[1])) + if opts.vals.dryrun: PrettyPrint.prettyprint(config) else: diff --git a/tools/python/xen/xm/shutdown.py b/tools/python/xen/xm/shutdown.py index a63c3523ca..b5542d9611 100644 --- a/tools/python/xen/xm/shutdown.py +++ b/tools/python/xen/xm/shutdown.py @@ -19,9 +19,9 @@ """ import time -from xen.xend.XendClient import server from xen.xend import sxp -from xen.xm.opts import * +from opts import * +from main import server gopts = Opts(use="""[options] [DOM] diff --git a/tools/tests/test_x86_emulator.c b/tools/tests/test_x86_emulator.c index d3b7af0a7c..64797d3f81 100644 --- a/tools/tests/test_x86_emulator.c +++ b/tools/tests/test_x86_emulator.c @@ -17,12 +17,14 @@ typedef int64_t s64; #define PFEC_write_access (1U<<1) -static int read_any( - unsigned long addr, +static int read( + unsigned int seg, + unsigned long offset, unsigned long *val, unsigned int bytes, struct x86_emulate_ctxt *ctxt) { + unsigned long addr = offset; switch ( bytes ) { case 1: *val = *(u8 *)addr; break; @@ -33,12 +35,14 @@ static int read_any( return X86EMUL_CONTINUE; } -static int write_any( - unsigned long addr, +static int write( + unsigned int seg, + unsigned long offset, unsigned long val, unsigned int bytes, struct x86_emulate_ctxt *ctxt) { + unsigned long addr = offset; switch ( bytes ) { case 1: *(u8 *)addr = (u8)val; break; @@ -49,13 +53,15 @@ static int write_any( return X86EMUL_CONTINUE; } -static int cmpxchg_any( - unsigned long addr, +static int cmpxchg( + unsigned int seg, + unsigned long offset, unsigned long old, unsigned long new, unsigned int bytes, struct x86_emulate_ctxt *ctxt) { + unsigned long addr = offset; switch ( bytes ) { case 1: *(u8 *)addr = (u8)new; break; @@ -66,21 +72,27 @@ static int cmpxchg_any( return X86EMUL_CONTINUE; } -static int cmpxchg8b_any( - unsigned long addr, +static int cmpxchg8b( + unsigned int seg, + unsigned long offset, unsigned long old_lo, unsigned long old_hi, unsigned long new_lo, unsigned long new_hi, struct x86_emulate_ctxt *ctxt) { + unsigned long addr = offset; ((unsigned long *)addr)[0] = new_lo; ((unsigned long *)addr)[1] = new_hi; return X86EMUL_CONTINUE; } static struct x86_emulate_ops emulops = { - read_any, write_any, read_any, write_any, cmpxchg_any, cmpxchg8b_any + .read = read, + .insn_fetch = read, + .write = write, + .cmpxchg = cmpxchg, + .cmpxchg8b = cmpxchg8b }; int main(int argc, char **argv) @@ -108,7 +120,7 @@ int main(int argc, char **argv) regs.eip = (unsigned long)&instr[0]; regs.ecx = 0x12345678; regs.error_code = PFEC_write_access; - ctxt.cr2 = (unsigned long)res; + regs.eax = (unsigned long)res; *res = 0x7FFFFFFF; rc = x86_emulate_memop(&ctxt, &emulops); if ( (rc != 0) || @@ -127,7 +139,7 @@ int main(int argc, char **argv) #else regs.ecx = 0x12345678UL; #endif - ctxt.cr2 = (unsigned long)res; + regs.eax = (unsigned long)res; regs.error_code = 0; rc = x86_emulate_memop(&ctxt, &emulops); if ( (rc != 0) || @@ -142,7 +154,7 @@ int main(int argc, char **argv) regs.eflags = 0x200; regs.eip = (unsigned long)&instr[0]; regs.ecx = ~0UL; - ctxt.cr2 = (unsigned long)res; + regs.eax = (unsigned long)res; regs.error_code = 0; rc = x86_emulate_memop(&ctxt, &emulops); if ( (rc != 0) || @@ -152,13 +164,13 @@ int main(int argc, char **argv) goto fail; printf("okay\n"); - printf("%-40s", "Testing lock cmpxchgb %%cl,(%%eax)..."); - instr[0] = 0xf0; instr[1] = 0x0f; instr[2] = 0xb0; instr[3] = 0x08; + printf("%-40s", "Testing lock cmpxchgb %%cl,(%%ebx)..."); + instr[0] = 0xf0; instr[1] = 0x0f; instr[2] = 0xb0; instr[3] = 0x0b; regs.eflags = 0x200; regs.eip = (unsigned long)&instr[0]; regs.eax = 0x92345677UL; regs.ecx = 0xAA; - ctxt.cr2 = (unsigned long)res; + regs.ebx = (unsigned long)res; regs.error_code = PFEC_write_access; rc = x86_emulate_memop(&ctxt, &emulops); if ( (rc != 0) || @@ -169,13 +181,13 @@ int main(int argc, char **argv) goto fail; printf("okay\n"); - printf("%-40s", "Testing lock cmpxchgb %%cl,(%%eax)..."); - instr[0] = 0xf0; instr[1] = 0x0f; instr[2] = 0xb0; instr[3] = 0x08; + printf("%-40s", "Testing lock cmpxchgb %%cl,(%%ebx)..."); + instr[0] = 0xf0; instr[1] = 0x0f; instr[2] = 0xb0; instr[3] = 0x0b; regs.eflags = 0x200; regs.eip = (unsigned long)&instr[0]; regs.eax = 0xAABBCC77UL; regs.ecx = 0xFF; - ctxt.cr2 = (unsigned long)res; + regs.ebx = (unsigned long)res; regs.error_code = PFEC_write_access; rc = x86_emulate_memop(&ctxt, &emulops); if ( (rc != 0) || @@ -192,7 +204,7 @@ int main(int argc, char **argv) regs.eflags = 0x200; regs.eip = (unsigned long)&instr[0]; regs.ecx = 0x12345678; - ctxt.cr2 = (unsigned long)res; + regs.eax = (unsigned long)res; regs.error_code = PFEC_write_access; rc = x86_emulate_memop(&ctxt, &emulops); if ( (rc != 0) || @@ -203,14 +215,14 @@ int main(int argc, char **argv) goto fail; printf("okay\n"); - printf("%-40s", "Testing lock cmpxchgl %%ecx,(%%eax)..."); - instr[0] = 0xf0; instr[1] = 0x0f; instr[2] = 0xb1; instr[3] = 0x08; + printf("%-40s", "Testing lock cmpxchgl %%ecx,(%%ebx)..."); + instr[0] = 0xf0; instr[1] = 0x0f; instr[2] = 0xb1; instr[3] = 0x0b; regs.eflags = 0x200; *res = 0x923456AA; regs.eip = (unsigned long)&instr[0]; regs.eax = 0x923456AAUL; regs.ecx = 0xDDEEFF00L; - ctxt.cr2 = (unsigned long)res; + regs.ebx = (unsigned long)res; regs.error_code = PFEC_write_access; rc = x86_emulate_memop(&ctxt, &emulops); if ( (rc != 0) || @@ -230,7 +242,6 @@ int main(int argc, char **argv) regs.esi = (unsigned long)res + 0; regs.edi = (unsigned long)res + 2; regs.error_code = 0; /* read fault */ - ctxt.cr2 = regs.esi; rc = x86_emulate_memop(&ctxt, &emulops); if ( (rc != 0) || (*res != 0x44554455) || @@ -248,7 +259,6 @@ int main(int argc, char **argv) regs.eflags = 0x200; regs.eip = (unsigned long)&instr[0]; regs.edi = (unsigned long)res; - ctxt.cr2 = regs.edi; regs.error_code = PFEC_write_access; rc = x86_emulate_memop(&ctxt, &emulops); if ( (rc != 0) || @@ -258,6 +268,22 @@ int main(int argc, char **argv) goto fail; printf("okay\n"); + printf("%-40s", "Testing btrl %eax,(%edi)..."); + instr[0] = 0x0f; instr[1] = 0xb3; instr[2] = 0x07; + *res = 0x2233445F; + regs.eflags = 0x200; + regs.eip = (unsigned long)&instr[0]; + regs.eax = -32; + regs.edi = (unsigned long)(res+1); + regs.error_code = PFEC_write_access; + rc = x86_emulate_memop(&ctxt, &emulops); + if ( (rc != 0) || + (*res != 0x2233445E) || + ((regs.eflags&0x201) != 0x201) || + (regs.eip != (unsigned long)&instr[3]) ) + goto fail; + printf("okay\n"); + res[0] = 0x12345678; res[1] = 0x87654321; @@ -270,7 +296,6 @@ int main(int argc, char **argv) regs.ecx = 0xCCCCFFFF; regs.eip = (unsigned long)&instr[0]; regs.edi = (unsigned long)res; - ctxt.cr2 = regs.edi; regs.error_code = PFEC_write_access; rc = x86_emulate_memop(&ctxt, &emulops); if ( (rc != 0) || @@ -283,9 +308,9 @@ int main(int argc, char **argv) printf("%-40s", "Testing cmpxchg8b (%edi) [failing]..."); instr[0] = 0x0f; instr[1] = 0xc7; instr[2] = 0x0f; + regs.eflags = 0x200; regs.eip = (unsigned long)&instr[0]; regs.edi = (unsigned long)res; - ctxt.cr2 = regs.edi; regs.error_code = PFEC_write_access; rc = x86_emulate_memop(&ctxt, &emulops); if ( (rc != 0) || @@ -300,9 +325,10 @@ int main(int argc, char **argv) printf("%-40s", "Testing movsxbd (%%eax),%%ecx..."); instr[0] = 0x0f; instr[1] = 0xbe; instr[2] = 0x08; + regs.eflags = 0x200; regs.eip = (unsigned long)&instr[0]; regs.ecx = 0x12345678; - ctxt.cr2 = (unsigned long)res; + regs.eax = (unsigned long)res; *res = 0x82; regs.error_code = 0; rc = x86_emulate_memop(&ctxt, &emulops); @@ -316,9 +342,10 @@ int main(int argc, char **argv) printf("%-40s", "Testing movzxwd (%%eax),%%ecx..."); instr[0] = 0x0f; instr[1] = 0xb7; instr[2] = 0x08; + regs.eflags = 0x200; regs.eip = (unsigned long)&instr[0]; regs.ecx = 0x12345678; - ctxt.cr2 = (unsigned long)res; + regs.eax = (unsigned long)res; *res = 0x1234aa82; regs.error_code = 0; rc = x86_emulate_memop(&ctxt, &emulops); @@ -330,6 +357,23 @@ int main(int argc, char **argv) goto fail; printf("okay\n"); + printf("%-40s", "Testing xadd %%ax,(%%ecx)..."); + instr[0] = 0x66; instr[1] = 0x0f; instr[2] = 0xc1; instr[3] = 0x01; + regs.eflags = 0x200; + regs.eip = (unsigned long)&instr[0]; + regs.ecx = (unsigned long)res; + regs.eax = 0x12345678; + *res = 0x11111111; + regs.error_code = 0; + rc = x86_emulate_memop(&ctxt, &emulops); + if ( (rc != 0) || + (*res != 0x11116789) || + (regs.eax != 0x12341111) || + ((regs.eflags&0x240) != 0x200) || + (regs.eip != (unsigned long)&instr[4]) ) + goto fail; + printf("okay\n"); + return 0; fail: diff --git a/tools/xenfb/Makefile b/tools/xenfb/Makefile new file mode 100644 index 0000000000..dc2547a864 --- /dev/null +++ b/tools/xenfb/Makefile @@ -0,0 +1,35 @@ +XEN_ROOT=../.. +include $(XEN_ROOT)/tools/Rules.mk + +CFLAGS += -I$(XEN_LIBXC) -I$(XEN_XENSTORE) -I$(XEN_ROOT)/linux-2.6-xen-sparse/include +LDFLAGS += -L$(XEN_LIBXC) -L$(XEN_XENSTORE) + +INSTALL = install +INSTALL_PROG = $(INSTALL) -m0755 +INSTALL_DIR = $(INSTALL) -d -m0755 + +.PHONY: all +all: build + +.PHONY: build +build: mk-symlinks + $(MAKE) vncfb sdlfb + +install: all + $(INSTALL_DIR) -p $(DESTDIR)/usr/$(LIBDIR)/xen/bin + $(INSTALL_PROG) vncfb $(DESTDIR)/usr/$(LIBDIR)/xen/bin/xen-vncfb + $(INSTALL_PROG) sdlfb $(DESTDIR)/usr/$(LIBDIR)/xen/bin/xen-sdlfb + +sdlfb: sdlfb.o xenfb.o + +sdlfb.o: CFLAGS += $(shell sdl-config --cflags) +sdlfb: LDLIBS += $(shell sdl-config --libs) -lxenctrl -lxenstore + +clean: + $(RM) *.o *~ vncfb sdlfb + +vncfb: vncfb.o xenfb.o +vncfb.o: CFLAGS += $(shell libvncserver-config --cflags) +vncfb: LDLIBS += $(shell libvncserver-config --libs) -lxenctrl -lxenstore + +sdlfb.o xenfb.o vncfb.o: xenfb.h diff --git a/tools/xenfb/sdlfb.c b/tools/xenfb/sdlfb.c new file mode 100644 index 0000000000..6276c34659 --- /dev/null +++ b/tools/xenfb/sdlfb.c @@ -0,0 +1,342 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "xenfb.h" + +struct SDLFBData +{ + SDL_Surface *dst; + SDL_Surface *src; +}; + +/* + * Map from scancode to Linux input layer keycode. Scancodes are + * hardware-specific. This map assumes a standard AT or PS/2 + * keyboard. + * + * Why use scancodes? We can't use key symbols, because they don't + * identify keys --- they're what keys are mapped to. The standard + * German keymap, for instance, maps both KEY_COMMA and KEY_102ND to + * SDLK_LESS. + */ +static int keymap[256] = { + [9] = KEY_ESC, + [10] = KEY_1, + [11] = KEY_2, + [12] = KEY_3, + [13] = KEY_4, + [14] = KEY_5, + [15] = KEY_6, + [16] = KEY_7, + [17] = KEY_8, + [18] = KEY_9, + [19] = KEY_0, + [20] = KEY_MINUS, + [21] = KEY_EQUAL, + [22] = KEY_BACKSPACE, + [23] = KEY_TAB, + [24] = KEY_Q, + [25] = KEY_W, + [26] = KEY_E, + [27] = KEY_R, + [28] = KEY_T, + [29] = KEY_Y, + [30] = KEY_U, + [31] = KEY_I, + [32] = KEY_O, + [33] = KEY_P, + [34] = KEY_LEFTBRACE, + [35] = KEY_RIGHTBRACE, + [36] = KEY_ENTER, + [37] = KEY_LEFTCTRL, + [38] = KEY_A, + [39] = KEY_S, + [40] = KEY_D, + [41] = KEY_F, + [42] = KEY_G, + [43] = KEY_H, + [44] = KEY_J, + [45] = KEY_K, + [46] = KEY_L, + [47] = KEY_SEMICOLON, + [48] = KEY_APOSTROPHE, + [49] = KEY_GRAVE, + [50] = KEY_LEFTSHIFT, + [51] = KEY_BACKSLASH, + [52] = KEY_Z, + [53] = KEY_X, + [54] = KEY_C, + [55] = KEY_V, + [56] = KEY_B, + [57] = KEY_N, + [58] = KEY_M, + [59] = KEY_COMMA, + [60] = KEY_DOT, + [61] = KEY_SLASH, + [62] = KEY_RIGHTSHIFT, + [63] = KEY_KPASTERISK, + [64] = KEY_LEFTALT, + [65] = KEY_SPACE, + [66] = KEY_CAPSLOCK, + [67] = KEY_F1, + [68] = KEY_F2, + [69] = KEY_F3, + [70] = KEY_F4, + [71] = KEY_F5, + [72] = KEY_F6, + [73] = KEY_F7, + [74] = KEY_F8, + [75] = KEY_F9, + [76] = KEY_F10, + [77] = KEY_NUMLOCK, + [78] = KEY_SCROLLLOCK, + [79] = KEY_KP7, + [80] = KEY_KP8, + [81] = KEY_KP9, + [82] = KEY_KPMINUS, + [83] = KEY_KP4, + [84] = KEY_KP5, + [85] = KEY_KP6, + [86] = KEY_KPPLUS, + [87] = KEY_KP1, + [88] = KEY_KP2, + [89] = KEY_KP3, + [90] = KEY_KP0, + [91] = KEY_KPDOT, + [94] = KEY_102ND, /* FIXME is this correct? */ + [95] = KEY_F11, + [96] = KEY_F12, + [108] = KEY_KPENTER, + [109] = KEY_RIGHTCTRL, + [112] = KEY_KPSLASH, + [111] = KEY_SYSRQ, + [113] = KEY_RIGHTALT, + [97] = KEY_HOME, + [98] = KEY_UP, + [99] = KEY_PAGEUP, + [100] = KEY_LEFT, + [102] = KEY_RIGHT, + [103] = KEY_END, + [104] = KEY_DOWN, + [105] = KEY_PAGEDOWN, + [106] = KEY_INSERT, + [107] = KEY_DELETE, + [110] = KEY_PAUSE, + [115] = KEY_LEFTMETA, + [116] = KEY_RIGHTMETA, + [117] = KEY_MENU, +}; + +static int btnmap[] = { + [SDL_BUTTON_LEFT] = BTN_LEFT, + [SDL_BUTTON_MIDDLE] = BTN_MIDDLE, + [SDL_BUTTON_RIGHT] = BTN_RIGHT, + /* FIXME not 100% sure about these: */ + [SDL_BUTTON_WHEELUP] = BTN_FORWARD, + [SDL_BUTTON_WHEELDOWN] BTN_BACK +}; + +static void sdl_update(struct xenfb *xenfb, int x, int y, int width, int height) +{ + struct SDLFBData *data = xenfb->user_data; + SDL_Rect r = { x, y, width, height }; + SDL_BlitSurface(data->src, &r, data->dst, &r); + SDL_UpdateRect(data->dst, x, y, width, height); +} + +static int sdl_on_event(struct xenfb *xenfb, SDL_Event *event) +{ + int x, y, ret; + + switch (event->type) { + case SDL_KEYDOWN: + case SDL_KEYUP: + if (keymap[event->key.keysym.scancode] == 0) + break; + ret = xenfb_send_key(xenfb, + event->type == SDL_KEYDOWN, + keymap[event->key.keysym.scancode]); + if (ret < 0) + fprintf(stderr, "Key %d %s lost (%s)\n", + keymap[event->key.keysym.scancode], + event->type == SDL_KEYDOWN ? "down" : "up", + strerror(errno)); + break; + case SDL_MOUSEMOTION: + if (xenfb->abs_pointer_wanted) { + SDL_GetMouseState(&x, &y); + ret = xenfb_send_position(xenfb, x, y); + } else { + SDL_GetRelativeMouseState(&x, &y); + ret = xenfb_send_motion(xenfb, x, y); + } + if (ret < 0) + fprintf(stderr, "Pointer to %d,%d lost (%s)\n", + x, y, strerror(errno)); + break; + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + if (event->button.button >= sizeof(btnmap) / sizeof(*btnmap)) + break; + if (btnmap[event->button.button] == 0) + break; + ret = xenfb_send_key(xenfb, + event->type == SDL_MOUSEBUTTONDOWN, + btnmap[event->button.button]); + if (ret < 0) + fprintf(stderr, "Button %d %s lost (%s)\n", + btnmap[event->button.button] - BTN_MOUSE, + event->type == SDL_MOUSEBUTTONDOWN ? "down" : "up", + strerror(errno)); + break; + case SDL_QUIT: + return 0; + } + + return 1; +} + +static struct option options[] = { + { "domid", 1, NULL, 'd' }, + { "title", 1, NULL, 't' }, + { NULL } +}; + +int main(int argc, char **argv) +{ + struct xenfb *xenfb; + int domid = -1; + char * title = NULL; + fd_set readfds; + int nfds; + struct SDLFBData data; + SDL_Rect r; + struct timeval tv; + SDL_Event event; + int do_quit = 0; + int opt; + char *endp; + int retval; + + while ((opt = getopt_long(argc, argv, "d:t:", options, + NULL)) != -1) { + switch (opt) { + case 'd': + domid = strtol(optarg, &endp, 10); + if (endp == optarg || *endp) { + fprintf(stderr, "Invalid domain id specified\n"); + exit(1); + } + break; + case 't': + title = strdup(optarg); + break; + case '?': + exit(1); + } + } + if (optind != argc) { + fprintf(stderr, "Invalid options!\n"); + exit(1); + } + if (domid <= 0) { + fprintf(stderr, "Domain ID must be specified!\n"); + exit(1); + } + + xenfb = xenfb_new(); + if (xenfb == NULL) { + fprintf(stderr, "Could not create framebuffer (%s)\n", + strerror(errno)); + exit(1); + } + + if (xenfb_attach_dom(xenfb, domid) < 0) { + fprintf(stderr, "Could not connect to domain (%s)\n", + strerror(errno)); + exit(1); + } + + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + fprintf(stderr, "Could not initialize SDL\n"); + exit(1); + } + + data.dst = SDL_SetVideoMode(xenfb->width, xenfb->height, xenfb->depth, + SDL_SWSURFACE); + if (!data.dst) { + fprintf(stderr, "SDL_SetVideoMode failed\n"); + exit(1); + } + + data.src = SDL_CreateRGBSurfaceFrom(xenfb->pixels, + xenfb->width, xenfb->height, + xenfb->depth, xenfb->row_stride, + 0xFF0000, 0xFF00, 0xFF, 0); + + if (!data.src) { + fprintf(stderr, "SDL_CreateRGBSurfaceFrom failed\n"); + exit(1); + } + + if (title == NULL) + title = strdup("xen-sdlfb"); + SDL_WM_SetCaption(title, title); + + r.x = r.y = 0; + r.w = xenfb->width; + r.h = xenfb->height; + SDL_BlitSurface(data.src, &r, data.dst, &r); + SDL_UpdateRect(data.dst, 0, 0, xenfb->width, xenfb->height); + + xenfb->update = sdl_update; + xenfb->user_data = &data; + + SDL_ShowCursor(0); + + /* + * We need to wait for fds becoming ready or SDL events to + * arrive. We time out the select after 10ms to poll for SDL + * events. Clunky, but works. Could avoid the clunkiness + * with a separate thread. + */ + for (;;) { + FD_ZERO(&readfds); + nfds = xenfb_select_fds(xenfb, &readfds); + tv = (struct timeval){0, 10000}; + + if (select(nfds, &readfds, NULL, NULL, &tv) < 0) { + if (errno == EINTR) + continue; + fprintf(stderr, + "Can't select() on event channel (%s)\n", + strerror(errno)); + break; + } + + while (SDL_PollEvent(&event)) { + if (!sdl_on_event(xenfb, &event)) + do_quit = 1; + } + + if (do_quit) + break; + + retval = xenfb_poll(xenfb, &readfds); + if (retval == -2) + xenfb_teardown(xenfb); + if (retval < 0) + break; + } + + xenfb_delete(xenfb); + + SDL_Quit(); + + return 0; +} diff --git a/tools/xenfb/vncfb.c b/tools/xenfb/vncfb.c new file mode 100644 index 0000000000..49fc730625 --- /dev/null +++ b/tools/xenfb/vncfb.c @@ -0,0 +1,401 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "xenfb.h" + +static int xk2linux[0x10000] = { + [XK_a] = KEY_A, + [XK_b] = KEY_B, + [XK_c] = KEY_C, + [XK_d] = KEY_D, + [XK_e] = KEY_E, + [XK_f] = KEY_F, + [XK_g] = KEY_G, + [XK_h] = KEY_H, + [XK_i] = KEY_I, + [XK_j] = KEY_J, + [XK_k] = KEY_K, + [XK_l] = KEY_L, + [XK_m] = KEY_M, + [XK_n] = KEY_N, + [XK_o] = KEY_O, + [XK_p] = KEY_P, + [XK_q] = KEY_Q, + [XK_r] = KEY_R, + [XK_s] = KEY_S, + [XK_t] = KEY_T, + [XK_u] = KEY_U, + [XK_v] = KEY_V, + [XK_w] = KEY_W, + [XK_x] = KEY_X, + [XK_y] = KEY_Y, + [XK_z] = KEY_Z, + [XK_A] = KEY_A, + [XK_B] = KEY_B, + [XK_C] = KEY_C, + [XK_D] = KEY_D, + [XK_E] = KEY_E, + [XK_F] = KEY_F, + [XK_G] = KEY_G, + [XK_H] = KEY_H, + [XK_I] = KEY_I, + [XK_J] = KEY_J, + [XK_K] = KEY_K, + [XK_L] = KEY_L, + [XK_M] = KEY_M, + [XK_N] = KEY_N, + [XK_O] = KEY_O, + [XK_P] = KEY_P, + [XK_Q] = KEY_Q, + [XK_R] = KEY_R, + [XK_S] = KEY_S, + [XK_T] = KEY_T, + [XK_U] = KEY_U, + [XK_V] = KEY_V, + [XK_W] = KEY_W, + [XK_X] = KEY_X, + [XK_Y] = KEY_Y, + [XK_Z] = KEY_Z, + [XK_0] = KEY_0, + [XK_1] = KEY_1, + [XK_2] = KEY_2, + [XK_3] = KEY_3, + [XK_4] = KEY_4, + [XK_5] = KEY_5, + [XK_6] = KEY_6, + [XK_7] = KEY_7, + [XK_8] = KEY_8, + [XK_9] = KEY_9, + [XK_Return] = KEY_ENTER, + [XK_BackSpace] = KEY_BACKSPACE, + [XK_Tab] = KEY_TAB, + [XK_Pause] = KEY_PAUSE, + [XK_Delete] = KEY_DELETE, + [XK_slash] = KEY_SLASH, + [XK_minus] = KEY_MINUS, + [XK_equal] = KEY_EQUAL, + [XK_Escape] = KEY_ESC, + [XK_braceleft] = KEY_LEFTBRACE, + [XK_braceright] = KEY_RIGHTBRACE, + [XK_bracketleft] = KEY_LEFTMETA, + [XK_bracketright] = KEY_RIGHTMETA, + [XK_Control_L] = KEY_LEFTCTRL, + [XK_Control_R] = KEY_RIGHTCTRL, + [XK_Shift_L] = KEY_LEFTSHIFT, + [XK_Shift_R] = KEY_RIGHTSHIFT, + [XK_Alt_L] = KEY_LEFTALT, + [XK_Alt_R] = KEY_RIGHTALT, + [XK_semicolon] = KEY_SEMICOLON, + [XK_apostrophe] = KEY_APOSTROPHE, + [XK_grave] = KEY_GRAVE, + [XK_backslash] = KEY_BACKSLASH, + [XK_comma] = KEY_COMMA, + [XK_period] = KEY_DOT, + [XK_space] = KEY_SPACE, + [XK_Caps_Lock] = KEY_CAPSLOCK, + [XK_Num_Lock] = KEY_NUMLOCK, + [XK_Scroll_Lock] = KEY_SCROLLLOCK, + [XK_Sys_Req] = KEY_SYSRQ, + [XK_Linefeed] = KEY_LINEFEED, + [XK_Home] = KEY_HOME, + [XK_Pause] = KEY_PAUSE, + [XK_F1] = KEY_F1, + [XK_F2] = KEY_F2, + [XK_F3] = KEY_F3, + [XK_F4] = KEY_F4, + [XK_F5] = KEY_F5, + [XK_F6] = KEY_F6, + [XK_F7] = KEY_F7, + [XK_F8] = KEY_F8, + [XK_F9] = KEY_F9, + [XK_F10] = KEY_F10, + [XK_F11] = KEY_F11, + [XK_F12] = KEY_F12, + [XK_Up] = KEY_UP, + [XK_Page_Up] = KEY_PAGEUP, + [XK_Left] = KEY_LEFT, + [XK_Right] = KEY_RIGHT, + [XK_End] = KEY_END, + [XK_Down] = KEY_DOWN, + [XK_Page_Down] = KEY_PAGEDOWN, + [XK_Insert] = KEY_INSERT, + [XK_colon] = KEY_SEMICOLON, + [XK_quotedbl] = KEY_APOSTROPHE, + [XK_less] = KEY_COMMA, + [XK_greater] = KEY_DOT, + [XK_question] = KEY_SLASH, + [XK_bar] = KEY_BACKSLASH, + [XK_asciitilde] = KEY_GRAVE, + [XK_exclam] = KEY_1, + [XK_at] = KEY_2, + [XK_numbersign] = KEY_3, + [XK_dollar] = KEY_4, + [XK_percent] = KEY_5, + [XK_asciicircum] = KEY_6, + [XK_ampersand] = KEY_7, + [XK_asterisk] = KEY_8, + [XK_parenleft] = KEY_9, + [XK_parenright] = KEY_0, + [XK_underscore] = KEY_MINUS, + [XK_plus] = KEY_EQUAL, +}; + +static void on_kbd_event(rfbBool down, rfbKeySym keycode, rfbClientPtr cl) +{ + /* + * We need to map to the key's Linux input layer keycode. + * Unfortunately, we don't get the key here, only the + * rfbKeySym, which is what the key is mapped to. Mapping + * back to the key is impossible in general, even when you + * know the keymap. For instance, the standard German keymap + * maps both KEY_COMMA and KEY_102ND to XK_less. We simply + * assume standard US layout. This sucks. + */ + rfbScreenInfoPtr server = cl->screen; + struct xenfb *xenfb = server->screenData; + if (keycode >= sizeof(xk2linux) / sizeof(*xk2linux)) + return; + if (xk2linux[keycode] == 0) + return; + if (xenfb_send_key(xenfb, down, xk2linux[keycode]) < 0) + fprintf(stderr, "Key %d %s lost (%s)\n", + xk2linux[keycode], down ? "down" : "up", + strerror(errno)); +} + +static void on_ptr_event(int buttonMask, int x, int y, rfbClientPtr cl) +{ + /* initial pointer state: at (0,0), buttons up */ + static int last_x, last_y, last_button; + rfbScreenInfoPtr server = cl->screen; + struct xenfb *xenfb = server->screenData; + int i, last_down, down, ret; + + for (i = 0; i < 8; i++) { + last_down = last_button & (1 << i); + down = buttonMask & (1 << i); + if (down == last_down) + continue; + /* FIXME this assumes buttons are numbered the same; verify they are */ + if (xenfb_send_key(xenfb, down != 0, BTN_MOUSE + i) < 0) + fprintf(stderr, "Button %d %s lost (%s)\n", + i, down ? "down" : "up", strerror(errno)); + } + + if (x != last_x || y != last_y) { + if (xenfb->abs_pointer_wanted) + ret = xenfb_send_position(xenfb, x, y); + else + ret = xenfb_send_motion(xenfb, x - last_x, y - last_y); + if (ret < 0) + fprintf(stderr, "Pointer to %d,%d lost (%s)\n", + x, y, strerror(errno)); + } + + last_button = buttonMask; + last_x = x; + last_y = y; +} + +static void xenstore_write_vncport(int port, int domid) +{ + char *buf = NULL, *path; + char portstr[10]; + struct xs_handle *xsh = NULL; + + xsh = xs_daemon_open(); + if (xsh == NULL) + return; + + path = xs_get_domain_path(xsh, domid); + if (path == NULL) { + fprintf(stderr, "Can't get domain path (%s)\n", + strerror(errno)); + goto out; + } + + if (asprintf(&buf, "%s/console/vnc-port", path) == -1) { + fprintf(stderr, "Can't make vncport path\n"); + goto out; + } + + if (snprintf(portstr, sizeof(portstr), "%d", port) == -1) { + fprintf(stderr, "Can't make vncport value\n"); + goto out; + } + + if (!xs_write(xsh, XBT_NULL, buf, portstr, strlen(portstr))) + fprintf(stderr, "Can't set vncport (%s)\n", + strerror(errno)); + + out: + free(buf); +} + + +static void vnc_update(struct xenfb *xenfb, int x, int y, int w, int h) +{ + rfbScreenInfoPtr server = xenfb->user_data; + rfbMarkRectAsModified(server, x, y, x + w, y + h); +} + +static struct option options[] = { + { "domid", 1, NULL, 'd' }, + { "vncport", 1, NULL, 'p' }, + { "title", 1, NULL, 't' }, + { "unused", 0, NULL, 'u' }, + { "listen", 1, NULL, 'l' }, + { NULL } +}; + +int main(int argc, char **argv) +{ + rfbScreenInfoPtr server; + char *fake_argv[7] = { "vncfb", "-rfbport", "5901", + "-desktop", "xen-vncfb", + "-listen", "127.0.0.1" }; + int fake_argc = sizeof(fake_argv) / sizeof(fake_argv[0]); + int domid = -1, port = -1; + char *title = NULL; + char *listen = NULL; + bool unused = false; + int opt; + struct xenfb *xenfb; + fd_set readfds; + int nfds; + char portstr[10]; + char *endp; + int r; + + while ((opt = getopt_long(argc, argv, "d:p:t:u", options, + NULL)) != -1) { + switch (opt) { + case 'd': + errno = 0; + domid = strtol(optarg, &endp, 10); + if (endp == optarg || *endp || errno) { + fprintf(stderr, "Invalid domain id specified\n"); + exit(1); + } + break; + case 'p': + errno = 0; + port = strtol(optarg, &endp, 10); + if (endp == optarg || *endp || errno) { + fprintf(stderr, "Invalid port specified\n"); + exit(1); + } + break; + case 't': + title = strdup(optarg); + break; + case 'u': + unused = true; + break; + case 'l': + listen = strdup(optarg); + break; + case '?': + exit(1); + } + } + if (optind != argc) { + fprintf(stderr, "Invalid options!\n"); + exit(1); + } + if (domid <= 0) { + fprintf(stderr, "Domain ID must be specified!\n"); + exit(1); + } + + if (port <= 0) + port = 5900 + domid; + if (snprintf(portstr, sizeof(portstr), "%d", port) == -1) { + fprintf(stderr, "Invalid port specified\n"); + exit(1); + } + + fake_argv[2] = portstr; + + if (title != NULL) + fake_argv[4] = title; + + if (listen != NULL) + fake_argv[6] = listen; + + signal(SIGPIPE, SIG_IGN); + + xenfb = xenfb_new(); + if (xenfb == NULL) { + fprintf(stderr, "Could not create framebuffer (%s)\n", + strerror(errno)); + exit(1); + } + + if (xenfb_attach_dom(xenfb, domid) < 0) { + fprintf(stderr, "Could not connect to domain (%s)\n", + strerror(errno)); + exit(1); + } + + server = rfbGetScreen(&fake_argc, fake_argv, + xenfb->width, xenfb->height, + 8, 3, xenfb->depth / 8); + if (server == NULL) { + fprintf(stderr, "Could not create VNC server\n"); + exit(1); + } + + xenfb->user_data = server; + xenfb->update = vnc_update; + + if (unused) + server->autoPort = true; + + server->serverFormat.redShift = 16; + server->serverFormat.greenShift = 8; + server->serverFormat.blueShift = 0; + server->kbdAddEvent = on_kbd_event; + server->ptrAddEvent = on_ptr_event; + server->frameBuffer = xenfb->pixels; + server->screenData = xenfb; + server->cursor = NULL; + rfbInitServer(server); + + rfbRunEventLoop(server, -1, true); + + xenstore_write_vncport(server->port, domid); + + for (;;) { + FD_ZERO(&readfds); + nfds = xenfb_select_fds(xenfb, &readfds); + + if (select(nfds, &readfds, NULL, NULL, NULL) < 0) { + if (errno == EINTR) + continue; + fprintf(stderr, + "Can't select() on event channel (%s)\n", + strerror(errno)); + break; + } + + r = xenfb_poll(xenfb, &readfds); + if (r == -2) + xenfb_teardown(xenfb); + if (r < 0) + break; + } + + rfbScreenCleanup(server); + xenfb_delete(xenfb); + + return 0; +} diff --git a/tools/xenfb/xenfb.c b/tools/xenfb/xenfb.c new file mode 100644 index 0000000000..19ab6c9553 --- /dev/null +++ b/tools/xenfb/xenfb.c @@ -0,0 +1,711 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xenfb.h" + +// FIXME defend against malicious frontend? + +struct xenfb_device { + const char *devicetype; + char nodename[64]; /* backend xenstore dir */ + char otherend[64]; /* frontend xenstore dir */ + int otherend_id; /* frontend domid */ + enum xenbus_state state; /* backend state */ + void *page; /* shared page */ + evtchn_port_t port; + struct xenfb_private *xenfb; +}; + +struct xenfb_private { + struct xenfb pub; + int evt_xch; /* event channel driver handle */ + int xc; /* hypervisor interface handle */ + struct xs_handle *xsh; /* xs daemon handle */ + struct xenfb_device fb, kbd; + size_t fb_len; /* size of framebuffer */ +}; + +static void xenfb_detach_dom(struct xenfb_private *); + +static char *xenfb_path_in_dom(struct xs_handle *xsh, + char *buf, size_t size, + unsigned domid, const char *fmt, ...) +{ + va_list ap; + char *domp = xs_get_domain_path(xsh, domid); + int n; + + if (domp == NULL) + return NULL; + + n = snprintf(buf, size, "%s/", domp); + free(domp); + if (n >= size) + return NULL; + + va_start(ap, fmt); + n += vsnprintf(buf + n, size - n, fmt, ap); + va_end(ap); + if (n >= size) + return NULL; + + return buf; +} + +static int xenfb_xs_scanf1(struct xs_handle *xsh, + const char *dir, const char *node, + const char *fmt, void *dest) +{ + char buf[1024]; + char *p; + int ret; + + if (snprintf(buf, sizeof(buf), "%s/%s", dir, node) >= sizeof(buf)) { + errno = ENOENT; + return -1; + } + p = xs_read(xsh, XBT_NULL, buf, NULL); + if (!p) { + errno = ENOENT; + return -1; + } + ret = sscanf(p, fmt, dest); + free(p); + if (ret != 1) { + errno = EDOM; + return -1; + } + return ret; +} + +static int xenfb_xs_printf(struct xs_handle *xsh, + const char *dir, const char *node, char *fmt, ...) +{ + va_list ap; + char key[1024]; + char val[1024]; + int n; + + if (snprintf(key, sizeof(key), "%s/%s", dir, node) >= sizeof(key)) { + errno = ENOENT; + return -1; + } + + va_start(ap, fmt); + n = vsnprintf(val, sizeof(val), fmt, ap); + va_end(ap); + if (n >= sizeof(val)) { + errno = ENOSPC; /* close enough */ + return -1; + } + + if (!xs_write(xsh, XBT_NULL, key, val, n)) + return -1; + return 0; +} + +static void xenfb_device_init(struct xenfb_device *dev, + const char *type, + struct xenfb_private *xenfb) +{ + dev->devicetype = type; + dev->otherend_id = -1; + dev->port = -1; + dev->xenfb = xenfb; +} + +int xenfb_device_set_domain(struct xenfb_device *dev, int domid) +{ + struct xenfb_private *xenfb = dev->xenfb; + + dev->otherend_id = domid; + + if (!xenfb_path_in_dom(xenfb->xsh, + dev->otherend, sizeof(dev->otherend), + domid, "device/%s/0", dev->devicetype)) { + errno = ENOENT; + return -1; + } + if (!xenfb_path_in_dom(xenfb->xsh, + dev->nodename, sizeof(dev->nodename), + 0, "backend/%s/%d/0", dev->devicetype, domid)) { + errno = ENOENT; + return -1; + } + + return 0; +} + +struct xenfb *xenfb_new(void) +{ + struct xenfb_private *xenfb = malloc(sizeof(*xenfb)); + int serrno; + + if (xenfb == NULL) + return NULL; + + memset(xenfb, 0, sizeof(*xenfb)); + xenfb->evt_xch = xenfb->xc = -1; + xenfb_device_init(&xenfb->fb, "vfb", xenfb); + xenfb_device_init(&xenfb->kbd, "vkbd", xenfb); + + xenfb->evt_xch = xc_evtchn_open(); + if (xenfb->evt_xch == -1) + goto fail; + + xenfb->xc = xc_interface_open(); + if (xenfb->xc == -1) + goto fail; + + xenfb->xsh = xs_daemon_open(); + if (!xenfb->xsh) + goto fail; + + return &xenfb->pub; + + fail: + serrno = errno; + xenfb_delete(&xenfb->pub); + errno = serrno; + return NULL; +} + +/* Remove the backend area in xenbus since the framebuffer really is + going away. */ +void xenfb_teardown(struct xenfb *xenfb_pub) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + + xs_rm(xenfb->xsh, XBT_NULL, xenfb->fb.nodename); + xs_rm(xenfb->xsh, XBT_NULL, xenfb->kbd.nodename); +} + + +void xenfb_delete(struct xenfb *xenfb_pub) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + + xenfb_detach_dom(xenfb); + if (xenfb->xc >= 0) + xc_interface_close(xenfb->xc); + if (xenfb->evt_xch >= 0) + xc_evtchn_close(xenfb->evt_xch); + if (xenfb->xsh) + xs_daemon_close(xenfb->xsh); + free(xenfb); +} + +static enum xenbus_state xenfb_read_state(struct xs_handle *xsh, + const char *dir) +{ + int ret, state; + + ret = xenfb_xs_scanf1(xsh, dir, "state", "%d", &state); + if (ret < 0) + return XenbusStateUnknown; + + if ((unsigned)state > XenbusStateClosed) + state = XenbusStateUnknown; + return state; +} + +static int xenfb_switch_state(struct xenfb_device *dev, + enum xenbus_state state) +{ + struct xs_handle *xsh = dev->xenfb->xsh; + + if (xenfb_xs_printf(xsh, dev->nodename, "state", "%d", state) < 0) + return -1; + dev->state = state; + return 0; +} + +static int xenfb_wait_for_state(struct xs_handle *xsh, const char *dir, + unsigned awaited) +{ + unsigned state, dummy; + char **vec; + + for (;;) { + state = xenfb_read_state(xsh, dir); + if (state < 0) + return -1; + + if ((1 << state) & awaited) + return state; + + vec = xs_read_watch(xsh, &dummy); + if (!vec) + return -1; + free(vec); + } +} + +static int xenfb_wait_for_backend_creation(struct xenfb_device *dev) +{ + struct xs_handle *xsh = dev->xenfb->xsh; + int state; + + if (!xs_watch(xsh, dev->nodename, "")) + return -1; + state = xenfb_wait_for_state(xsh, dev->nodename, + (1 << XenbusStateInitialising) + | (1 << XenbusStateClosed) +#if 1 /* TODO fudging state to permit restarting; to be removed */ + | (1 << XenbusStateInitWait) + | (1 << XenbusStateConnected) + | (1 << XenbusStateClosing) +#endif + ); + xs_unwatch(xsh, dev->nodename, ""); + + switch (state) { +#if 1 + case XenbusStateInitWait: + case XenbusStateConnected: + printf("Fudging state to %d\n", XenbusStateInitialising); /* FIXME */ +#endif + case XenbusStateInitialising: + case XenbusStateClosing: + case XenbusStateClosed: + break; + default: + return -1; + } + + return 0; +} + +static int xenfb_hotplug(struct xenfb_device *dev) +{ + if (xenfb_xs_printf(dev->xenfb->xsh, dev->nodename, + "hotplug-status", "connected")) + return -1; + return 0; +} + +static int xenfb_wait_for_frontend_initialised(struct xenfb_device *dev) +{ + switch (xenfb_wait_for_state(dev->xenfb->xsh, dev->otherend, +#if 1 /* TODO fudging state to permit restarting; to be removed */ + (1 << XenbusStateInitialised) + | (1 << XenbusStateConnected) +#else + 1 << XenbusStateInitialised, +#endif + )) { +#if 1 + case XenbusStateConnected: + printf("Fudging state to %d\n", XenbusStateInitialised); /* FIXME */ +#endif + case XenbusStateInitialised: + break; + default: + return -1; + } + + return 0; +} + +static int xenfb_map_fb(struct xenfb_private *xenfb, int domid) +{ + struct xenfb_page *page = xenfb->fb.page; + int n_fbmfns; + int n_fbdirs; + unsigned long *fbmfns; + + n_fbmfns = (xenfb->fb_len + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; + n_fbdirs = n_fbmfns * sizeof(unsigned long); + n_fbdirs = (n_fbdirs + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; + + /* + * Bug alert: xc_map_foreign_batch() can fail partly and + * return a non-null value. This is a design flaw. When it + * happens, we happily continue here, and later crash on + * access. + */ + fbmfns = xc_map_foreign_batch(xenfb->xc, domid, + PROT_READ, page->pd, n_fbdirs); + if (fbmfns == NULL) + return -1; + + xenfb->pub.pixels = xc_map_foreign_batch(xenfb->xc, domid, + PROT_READ | PROT_WRITE, fbmfns, n_fbmfns); + if (xenfb->pub.pixels == NULL) { + munmap(fbmfns, n_fbdirs * XC_PAGE_SIZE); + return -1; + } + + return munmap(fbmfns, n_fbdirs * XC_PAGE_SIZE); +} + +static int xenfb_bind(struct xenfb_device *dev) +{ + struct xenfb_private *xenfb = dev->xenfb; + unsigned long mfn; + evtchn_port_t evtchn; + + if (xenfb_xs_scanf1(xenfb->xsh, dev->otherend, "page-ref", "%lu", + &mfn) < 0) + return -1; + if (xenfb_xs_scanf1(xenfb->xsh, dev->otherend, "event-channel", "%u", + &evtchn) < 0) + return -1; + + dev->port = xc_evtchn_bind_interdomain(xenfb->evt_xch, + dev->otherend_id, evtchn); + if (dev->port == -1) + return -1; + + dev->page = xc_map_foreign_range(xenfb->xc, dev->otherend_id, + XC_PAGE_SIZE, PROT_READ | PROT_WRITE, mfn); + if (dev->page == NULL) + return -1; + + return 0; +} + +static void xenfb_unbind(struct xenfb_device *dev) +{ + if (dev->page) { + munmap(dev->page, XC_PAGE_SIZE); + dev->page = NULL; + } + if (dev->port >= 0) { + xc_evtchn_unbind(dev->xenfb->evt_xch, dev->port); + dev->port = -1; + } +} + +static int xenfb_wait_for_frontend_connected(struct xenfb_device *dev) +{ + switch (xenfb_wait_for_state(dev->xenfb->xsh, dev->otherend, + 1 << XenbusStateConnected)) { + case XenbusStateConnected: + break; + default: + return -1; + } + + return 0; +} + +static void xenfb_dev_fatal(struct xenfb_device *dev, int err, + const char *fmt, ...) +{ + struct xs_handle *xsh = dev->xenfb->xsh; + va_list ap; + char errdir[80]; + char buf[1024]; + int n; + + fprintf(stderr, "%s ", dev->nodename); /* somewhat crude */ + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + if (err) + fprintf(stderr, " (%s)", strerror(err)); + putc('\n', stderr); + + if (!xenfb_path_in_dom(xsh, errdir, sizeof(errdir), 0, + "error/%s", dev->nodename)) + goto out; /* FIXME complain */ + + va_start(ap, fmt); + n = snprintf(buf, sizeof(buf), "%d ", err); + snprintf(buf + n, sizeof(buf) - n, fmt, ap); + va_end(ap); + + if (xenfb_xs_printf(xsh, buf, "error", "%s", buf) < 0) + goto out; /* FIXME complain */ + + out: + xenfb_switch_state(dev, XenbusStateClosing); +} + +int xenfb_attach_dom(struct xenfb *xenfb_pub, int domid) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + struct xs_handle *xsh = xenfb->xsh; + int val, serrno; + struct xenfb_page *fb_page; + + xenfb_detach_dom(xenfb); + + xenfb_device_set_domain(&xenfb->fb, domid); + xenfb_device_set_domain(&xenfb->kbd, domid); + + if (xenfb_wait_for_backend_creation(&xenfb->fb) < 0) + goto error; + if (xenfb_wait_for_backend_creation(&xenfb->kbd) < 0) + goto error; + + if (xenfb_xs_printf(xsh, xenfb->kbd.nodename, "feature-abs-pointer", "1")) + goto error; + if (xenfb_switch_state(&xenfb->fb, XenbusStateInitWait)) + goto error; + if (xenfb_switch_state(&xenfb->kbd, XenbusStateInitWait)) + goto error; + + if (xenfb_hotplug(&xenfb->fb) < 0) + goto error; + if (xenfb_hotplug(&xenfb->kbd) < 0) + goto error; + + if (!xs_watch(xsh, xenfb->fb.otherend, "")) + goto error; + if (!xs_watch(xsh, xenfb->kbd.otherend, "")) + goto error; + + if (xenfb_wait_for_frontend_initialised(&xenfb->fb) < 0) + goto error; + if (xenfb_wait_for_frontend_initialised(&xenfb->kbd) < 0) + goto error; + + if (xenfb_bind(&xenfb->fb) < 0) + goto error; + if (xenfb_bind(&xenfb->kbd) < 0) + goto error; + + if (xenfb_xs_scanf1(xsh, xenfb->fb.otherend, "feature-update", + "%d", &val) < 0) + val = 0; + if (!val) { + errno = ENOTSUP; + goto error; + } + xenfb_xs_printf(xsh, xenfb->fb.nodename, "request-update", "1"); + + /* TODO check for permitted ranges */ + fb_page = xenfb->fb.page; + xenfb->pub.depth = fb_page->depth; + xenfb->pub.width = fb_page->width; + xenfb->pub.height = fb_page->height; + /* TODO check for consistency with the above */ + xenfb->fb_len = fb_page->mem_length; + xenfb->pub.row_stride = fb_page->line_length; + + if (xenfb_map_fb(xenfb, domid) < 0) + goto error; + + if (xenfb_switch_state(&xenfb->fb, XenbusStateConnected)) + goto error; + if (xenfb_switch_state(&xenfb->kbd, XenbusStateConnected)) + goto error; + + if (xenfb_wait_for_frontend_connected(&xenfb->kbd) < 0) + goto error; + if (xenfb_xs_scanf1(xsh, xenfb->kbd.otherend, "request-abs-pointer", + "%d", &val) < 0) + val = 0; + xenfb->pub.abs_pointer_wanted = val; + + return 0; + + error: + serrno = errno; + xenfb_detach_dom(xenfb); + xenfb_dev_fatal(&xenfb->fb, serrno, "on fire"); + xenfb_dev_fatal(&xenfb->kbd, serrno, "on fire"); + errno = serrno; + return -1; +} + +static void xenfb_detach_dom(struct xenfb_private *xenfb) +{ + xenfb_unbind(&xenfb->fb); + xenfb_unbind(&xenfb->kbd); + if (xenfb->pub.pixels) { + munmap(xenfb->pub.pixels, xenfb->fb_len); + xenfb->pub.pixels = NULL; + } +} + +static void xenfb_on_fb_event(struct xenfb_private *xenfb) +{ + uint32_t prod, cons; + struct xenfb_page *page = xenfb->fb.page; + + prod = page->out_prod; + if (prod == page->out_cons) + return; + rmb(); /* ensure we see ring contents up to prod */ + for (cons = page->out_cons; cons != prod; cons++) { + union xenfb_out_event *event = &XENFB_OUT_RING_REF(page, cons); + + switch (event->type) { + case XENFB_TYPE_UPDATE: + if (xenfb->pub.update) + xenfb->pub.update(&xenfb->pub, + event->update.x, event->update.y, + event->update.width, event->update.height); + break; + } + } + mb(); /* ensure we're done with ring contents */ + page->out_cons = cons; + xc_evtchn_notify(xenfb->evt_xch, xenfb->fb.port); +} + +static void xenfb_on_kbd_event(struct xenfb_private *xenfb) +{ + struct xenkbd_page *page = xenfb->kbd.page; + + /* We don't understand any keyboard events, so just ignore them. */ + if (page->out_prod == page->out_cons) + return; + page->out_cons = page->out_prod; + xc_evtchn_notify(xenfb->evt_xch, xenfb->kbd.port); +} + +static int xenfb_on_state_change(struct xenfb_device *dev) +{ + enum xenbus_state state; + + state = xenfb_read_state(dev->xenfb->xsh, dev->otherend); + + switch (state) { + case XenbusStateUnknown: + /* There was an error reading the frontend state. The + domain has probably gone away; in any case, there's + not much point in us continuing. */ + return -1; + case XenbusStateInitialising: + case XenbusStateInitWait: + case XenbusStateInitialised: + case XenbusStateConnected: + break; + case XenbusStateClosing: + xenfb_unbind(dev); + xenfb_switch_state(dev, state); + break; + case XenbusStateClosed: + xenfb_switch_state(dev, state); + } + return 0; +} + +/* Returns 0 normally, -1 on error, or -2 if the domain went away. */ +int xenfb_poll(struct xenfb *xenfb_pub, fd_set *readfds) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + evtchn_port_t port; + unsigned dummy; + char **vec; + int r; + + if (FD_ISSET(xc_evtchn_fd(xenfb->evt_xch), readfds)) { + port = xc_evtchn_pending(xenfb->evt_xch); + if (port == -1) + return -1; + + if (port == xenfb->fb.port) + xenfb_on_fb_event(xenfb); + else if (port == xenfb->kbd.port) + xenfb_on_kbd_event(xenfb); + + if (xc_evtchn_unmask(xenfb->evt_xch, port) == -1) + return -1; + } + + if (FD_ISSET(xs_fileno(xenfb->xsh), readfds)) { + vec = xs_read_watch(xenfb->xsh, &dummy); + free(vec); + r = xenfb_on_state_change(&xenfb->fb); + if (r == 0) + r = xenfb_on_state_change(&xenfb->kbd); + if (r == -1) + return -2; + } + + return 0; +} + +int xenfb_select_fds(struct xenfb *xenfb_pub, fd_set *readfds) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + int fd1 = xc_evtchn_fd(xenfb->evt_xch); + int fd2 = xs_fileno(xenfb->xsh); + + FD_SET(fd1, readfds); + FD_SET(fd2, readfds); + return fd1 > fd2 ? fd1 + 1 : fd2 + 1; +} + +static int xenfb_kbd_event(struct xenfb_private *xenfb, + union xenkbd_in_event *event) +{ + uint32_t prod; + struct xenkbd_page *page = xenfb->kbd.page; + + if (xenfb->kbd.state != XenbusStateConnected) + return 0; + + prod = page->in_prod; + if (prod - page->in_cons == XENKBD_IN_RING_LEN) { + errno = EAGAIN; + return -1; + } + + mb(); /* ensure ring space available */ + XENKBD_IN_RING_REF(page, prod) = *event; + wmb(); /* ensure ring contents visible */ + page->in_prod = prod + 1; + return xc_evtchn_notify(xenfb->evt_xch, xenfb->kbd.port); +} + +int xenfb_send_key(struct xenfb *xenfb_pub, bool down, int keycode) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_KEY; + event.key.pressed = down ? 1 : 0; + event.key.keycode = keycode; + + return xenfb_kbd_event(xenfb, &event); +} + +int xenfb_send_motion(struct xenfb *xenfb_pub, int rel_x, int rel_y) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_MOTION; + event.motion.rel_x = rel_x; + event.motion.rel_y = rel_y; + + return xenfb_kbd_event(xenfb, &event); +} + +int xenfb_send_position(struct xenfb *xenfb_pub, int abs_x, int abs_y) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_POS; + event.pos.abs_x = abs_x; + event.pos.abs_y = abs_y; + + return xenfb_kbd_event(xenfb, &event); +} diff --git a/tools/xenfb/xenfb.h b/tools/xenfb/xenfb.h new file mode 100644 index 0000000000..c08d979b39 --- /dev/null +++ b/tools/xenfb/xenfb.h @@ -0,0 +1,35 @@ +#ifndef _XENFB_H_ +#define _XENFB_H_ + +#include +#include + +struct xenfb +{ + void *pixels; + + int row_stride; + int depth; + int width; + int height; + int abs_pointer_wanted; + + void *user_data; + + void (*update)(struct xenfb *xenfb, int x, int y, int width, int height); +}; + +struct xenfb *xenfb_new(void); +void xenfb_delete(struct xenfb *xenfb); +void xenfb_teardown(struct xenfb *xenfb); + +int xenfb_attach_dom(struct xenfb *xenfb, int domid); + +int xenfb_select_fds(struct xenfb *xenfb, fd_set *readfds); +int xenfb_poll(struct xenfb *xenfb, fd_set *readfds); + +int xenfb_send_key(struct xenfb *xenfb, bool down, int keycode); +int xenfb_send_motion(struct xenfb *xenfb, int rel_x, int rel_y); +int xenfb_send_position(struct xenfb *xenfb, int abs_x, int abs_y); + +#endif diff --git a/tools/xm-test/grouptest/xapi b/tools/xm-test/grouptest/xapi new file mode 100644 index 0000000000..407368e6b4 --- /dev/null +++ b/tools/xm-test/grouptest/xapi @@ -0,0 +1 @@ +vtpm 09_vtpm-xapi.test diff --git a/tools/xm-test/lib/XmTestLib/XenManagedDomain.py b/tools/xm-test/lib/XmTestLib/XenManagedDomain.py new file mode 100644 index 0000000000..1b211fe016 --- /dev/null +++ b/tools/xm-test/lib/XmTestLib/XenManagedDomain.py @@ -0,0 +1,177 @@ +#!/usr/bin/python +""" + Copyright (C) International Business Machines Corp., 2005 + Author: Stefan Berger + + Based on XenDomain.py by Dan Smith + + 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; under version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" +import os +import sys +from XmTestLib import * +from xen.util.xmlrpclib2 import ServerProxy +from types import DictType + + +class XenManagedConfig: + """An object to help create a VM configuration usable via Xen-API""" + def __init__(self): + self.opts = {} + #Array to translate old option to new ones + self.opttrlate = { 'name' : 'name_label' , + 'memory' : [ 'memory_static_max' , + 'memory_static_min' , + 'memory_dynamic_min', + 'memory_dynamic_max' ], + 'kernel' : 'kernel_kernel', + 'ramdisk': 'kernel_initrd', + 'root' : 'kernel_args'} + + def setOpt(self, name, value): + """Set an option in the config""" + if name in self.opttrlate.keys(): + _name = self.opttrlate[name] + else: + _name = name + + if isinstance(_name, list): + for _n in _name: + self.opts[_n] = value + else: + self.opts[_name] = value + + def getOpt(self, name): + """Return the value of a config option""" + if name in self.opts.keys(): + return self.opts[name] + else: + return None + + def setOpts(self, opts): + """Batch-set options from a dictionary""" + for k, v in opts.items(): + self.setOpt(k, v) + + def getOpts(self): + return self.opts + + +class XenManagedDomain(XenDomain): + + def __init__(self, name=None, config=None): + if name: + self.name = name + else: + self.name = getUniqueName() + + self.config = config + self.console = None + self.netEnv = "bridge" + + self.server, self.session = xapi._connect() + server = self.server + try: + self.vm_uuid = xapi.execute(server.VM.create, self.session, + self.config.getOpts()) + xapi._VMuuids.append(self.vm_uuid) + except: + raise DomainError("Could not create VM config file for " + "managed domain.") + + #Only support PV for now. + self.type = "PV" + + def start(self, noConsole=False, startpaused=False): + #start the VM + server = self.server + if self.vm_uuid: + try: + xapi.execute(server.VM.start, self.session, self.vm_uuid, + startpaused) + except: + raise DomainError("Could not start domain") + else: + raise DomainError("VM has not UUID - VM config does not exist?") + + if self.getDomainType() == "HVM": + waitForBoot() + + if self.console and noConsole == True: + self.closeConsole() + + elif self.console and noConsole == False: + return self.console + + elif not self.console and noConsole == False: + return self.getConsole() + + def stop(self): + if self.vm_uuid: + server = self.server + xapi.execute(server.VM.hard_shutdown, self.session, self.vm_uuid) + else: + raise DomainError("VM has not UUID - VM config does not exist?") + + def destroy(self): + #Stop VM first. + self.stop() + if self.vm_uuid: + server = self.server + xapi.execute(server.VM.destroy, self.session, self.vm_uuid) + xapi._VMuuids.remove(self.vm_uuid) + else: + raise DomainError("VM has not UUID - VM config does not exist?") + + def get_uuid(self): + return self.vm_uuid + + def newDevice(self, Device, *args): + raise DomainError("No support for newDevice().") + + def removeDevice(self, id): + raise DomainError("No support for removeDevice().") + + def removeAllDevices(self, id): + raise DomainError("No support for removeAllDevices().") + + def isRunning(self): + return isDomainRunning(self.name) + + def getDevice(self, id): + raise DomainError("No support for getDevice().") + + +class XmTestManagedDomain(XenManagedDomain): + + """Create a new managed xm-test domain + @param name: The requested domain name + @param extraConfig: Additional configuration options + @param baseConfig: The initial configuration defaults to use + """ + def __init__(self, name=None, extraConfig=None, + baseConfig=arch.configDefaults): + config = XenManagedConfig() + config.setOpts(baseConfig) + if extraConfig: + config.setOpts(extraConfig) + + if name: + config.setOpt("name_label", name) + elif not config.getOpt("name_label"): + config.setOpt("name_label", getUniqueName()) + + XenManagedDomain.__init__(self, config.getOpt("name_label"), + config=config) diff --git a/tools/xm-test/lib/XmTestLib/xapi.py b/tools/xm-test/lib/XmTestLib/xapi.py new file mode 100644 index 0000000000..bab0e103d9 --- /dev/null +++ b/tools/xm-test/lib/XmTestLib/xapi.py @@ -0,0 +1,66 @@ +#!/usr/bin/python +#============================================================================ +# This library is free software; you can redistribute it and/or +# modify it under the terms of version 2.1 of the GNU Lesser General Public +# License as published by the Free Software Foundation. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#============================================================================ +# Copyright (C) 2006 XenSource Ltd. +# Copyright (C) 2006 IBM Corporation +#============================================================================ + +import os +import sys +from XmTestLib import * +from xen.util.xmlrpclib2 import ServerProxy +from types import DictType + + +XAPI_DEFAULT_LOGIN = " " +XAPI_DEFAULT_PASSWORD = " " + +class XenAPIError(Exception): + pass + + +#A list of VMs' UUIDs that were created using vm_create +_VMuuids = [] + +#Terminate previously created managed(!) VMs and destroy their configs +def vm_destroy_all(): + server, session = _connect() + for uuid in _VMuuids: + execute(server.VM.hard_shutdown, session, uuid) + execute(server.VM.destroy , session, uuid) + + +def execute(fn, *args): + result = fn(*args) + if type(result) != DictType: + raise TypeError("Function returned object of type: %s" % + str(type(result))) + if 'Value' not in result: + raise XenAPIError(*result['ErrorDescription']) + return result['Value'] + +_initialised = False +_server = None +_session = None +def _connect(*args): + global _server, _session, _initialised + if not _initialised: + _server = ServerProxy('httpu:///var/run/xend/xen-api.sock') + login = XAPI_DEFAULT_LOGIN + password = XAPI_DEFAULT_PASSWORD + creds = (login, password) + _session = execute(_server.session.login_with_password, *creds) + _initialised = True + return (_server, _session) diff --git a/tools/xm-test/ramdisk/Makefile.am b/tools/xm-test/ramdisk/Makefile.am index 14bfc9267c..b6c59d60e5 100644 --- a/tools/xm-test/ramdisk/Makefile.am +++ b/tools/xm-test/ramdisk/Makefile.am @@ -34,6 +34,7 @@ HVM_SCRIPT = bin/create_disk_image XMTEST_MAJ_VER = $(shell echo @PACKAGE_VERSION@ | perl -pe 's/(\d+)\.(\d+)\.\d+/\1.\2/') XMTEST_VER_IMG = initrd-$(XMTEST_MAJ_VER)-$(BR_ARCH).img +XMTEST_DL_IMG = $(shell echo $(XMTEST_VER_IMG) | sed -e 's/x86_64/i386/g') EXTRA_ROOT_DIRS = sys @@ -83,7 +84,12 @@ disk.img: existing existing: @if [ -n "$(INITRD)" ] && [ ! -f $(XMTEST_VER_IMG) ] ; then \ - wget $(INITRD)/$(XMTEST_VER_IMG); \ + if [ ! -f $(XMTEST_DL_IMG) ] ; then \ + wget $(INITRD)/$(XMTEST_DL_IMG); \ + fi; \ + if [ "$(XMTEST_DL_IMG)" != "$(XMTEST_VER_IMG)" ] ; then \ + ln -s $(XMTEST_DL_IMG) $(XMTEST_VER_IMG); \ + fi \ fi @if [ -f $(XMTEST_VER_IMG) ] ; then \ ln -sf $(XMTEST_VER_IMG) initrd.img; \ diff --git a/tools/xm-test/tests/vtpm/09_vtpm-xapi.py b/tools/xm-test/tests/vtpm/09_vtpm-xapi.py new file mode 100644 index 0000000000..8547a08b03 --- /dev/null +++ b/tools/xm-test/tests/vtpm/09_vtpm-xapi.py @@ -0,0 +1,76 @@ +#!/usr/bin/python + +# Copyright (C) International Business Machines Corp., 2006 +# Author: Stefan Berger + +# Test to test the vtpm class through the Xen-API + +from XmTestLib import xapi +from XmTestLib.XenManagedDomain import XmTestManagedDomain +from XmTestLib import * +from vtpm_utils import * +import commands +import os + +def do_test(): + domain = XmTestManagedDomain() + vm_uuid = domain.get_uuid() + + vtpmcfg = {} + vtpmcfg['type'] = "paravirtualised" + vtpmcfg['backend'] = "Domain-0" + vtpmcfg['instance'] = 1 + vtpmcfg['VM'] = vm_uuid + + server, session = xapi._connect() + + vtpm_uuid = xapi.execute(server.VTPM.create, session, vtpmcfg) + + vtpm_id = xapi.execute(server.VTPM.get_instance, session, vtpm_uuid) + vtpm_be = xapi.execute(server.VTPM.get_backend , session, vtpm_uuid) + if vtpm_be != vtpmcfg['backend']: + FAIL("vTPM's backend is in '%s', expected: '%s'" % + (vtpm_be, vtpmcfg['backend'])) + + driver = xapi.execute(server.VTPM.get_driver, session, vtpm_uuid) + if driver != vtpmcfg['type']: + FAIL("vTPM has driver type '%s', expected: '%s'" % + (driver, vtpmcfg['type'])) + + vtpm_rec = xapi.execute(server.VTPM.get_record, session, vtpm_uuid) + + if vtpm_rec['driver'] != vtpmcfg['type']: + FAIL("vTPM record shows driver type '%s', expected: '%s'" % + (vtpm_rec['driver'], vtpmcfg['type'])) + if vtpm_rec['uuid'] != vtpm_uuid: + FAIL("vTPM record shows vtpm uuid '%s', expected: '%s'" % + (vtpm_rec['uuid'], vtpm_uuid)) + if vtpm_rec['VM'] != vm_uuid: + FAIL("vTPM record shows VM uuid '%s', expected: '%s'" % + (vtpm_rec['VM'], vm_uuid)) + + success = domain.start() + + console = domain.getConsole() + + try: + run = console.runCmd("cat /sys/devices/xen/vtpm-0/pcrs") + except ConsoleError, e: + saveLog(console.getHistory()) + vtpm_cleanup(domName) + FAIL("No result from dumping the PCRs") + + if re.search("No such file",run["output"]): + vtpm_cleanup(domName) + FAIL("TPM frontend support not compiled into (domU?) kernel") + + domain.stop() + domain.destroy() + + + +try: + do_test() +finally: + #Make sure all domains are gone that were created in this test case + xapi.vm_destroy_all() diff --git a/tools/xm-test/tests/vtpm/Makefile.am b/tools/xm-test/tests/vtpm/Makefile.am index d6aae0c15b..dd67e459ec 100644 --- a/tools/xm-test/tests/vtpm/Makefile.am +++ b/tools/xm-test/tests/vtpm/Makefile.am @@ -7,7 +7,8 @@ TESTS = 01_vtpm-list_pos.test \ 05_vtpm-loc_migr.test \ 06_vtpm-susp_res_pcrs.test \ 07_vtpm-mig_pcrs.test \ - 08_vtpm-mig_pcrs.test + 08_vtpm-mig_pcrs.test \ + 09_vtpm-xapi.test XFAIL_TESTS = diff --git a/xen/arch/ia64/Makefile b/xen/arch/ia64/Makefile index 7d297f0c22..fecb8562ee 100644 --- a/xen/arch/ia64/Makefile +++ b/xen/arch/ia64/Makefile @@ -58,17 +58,6 @@ $(BASEDIR)/include/asm-ia64/.offsets.h.stamp: || ln -sf $(BASEDIR)/include/xen $(BASEDIR)/include/linux [ -e $(BASEDIR)/include/asm-ia64/xen ] \ || ln -sf $(BASEDIR)/include/asm-ia64/linux $(BASEDIR)/include/asm-ia64/xen -# Link to HVM files in Xen for ia64/vti - [ -e $(BASEDIR)/include/asm-ia64/hvm/support.h ] \ - || ln -sf ../../../include/asm-x86/hvm/support.h $(BASEDIR)/include/asm-ia64/hvm/support.h - [ -e $(BASEDIR)/include/asm-ia64/hvm/io.h ] \ - || ln -sf ../../../include/asm-x86/hvm/io.h $(BASEDIR)/include/asm-ia64/hvm/io.h - [ -e $(BASEDIR)/include/asm-ia64/hvm/vpic.h ] \ - || ln -sf ../../../include/asm-x86/hvm/vpic.h $(BASEDIR)/include/asm-ia64/hvm/vpic.h - [ -e $(BASEDIR)/include/asm-ia64/hvm/vioapic.h ] \ - || ln -sf ../../../include/asm-x86/hvm/vioapic.h $(BASEDIR)/include/asm-ia64/hvm/vioapic.h - [ -e $(BASEDIR)/arch/ia64/vmx/hvm_vioapic.c ] \ - || ln -sf ../../../arch/x86/hvm/vioapic.c $(BASEDIR)/arch/ia64/vmx/hvm_vioapic.c # I'm sure a Makefile wizard would know a better way to do this xen.lds.s: xen/xen.lds.S @@ -80,8 +69,3 @@ clean:: rm -f *.o *~ core xen.lds.s $(BASEDIR)/include/asm-ia64/.offsets.h.stamp asm-offsets.s map.out rm -f asm-xsi-offsets.s $(BASEDIR)/include/asm-ia64/asm-xsi-offsets.h rm -f $(BASEDIR)/System.map - rm -f vmx/hvm_*.c - rm -f $(BASEDIR)/include/asm-ia64/hvm/support.h - rm -f $(BASEDIR)/include/asm-ia64/hvm/io.h - rm -f $(BASEDIR)/include/asm-ia64/hvm/vpic.h - rm -f $(BASEDIR)/include/asm-ia64/hvm/vioapic.h diff --git a/xen/arch/ia64/Rules.mk b/xen/arch/ia64/Rules.mk index 8f69704d7d..e63faf67ef 100644 --- a/xen/arch/ia64/Rules.mk +++ b/xen/arch/ia64/Rules.mk @@ -3,6 +3,7 @@ HAS_ACPI := y HAS_VGA := y +xenoprof := y VALIDATE_VT ?= n no_warns ?= n xen_ia64_expose_p2m ?= y @@ -32,7 +33,7 @@ CPPFLAGS+= -I$(BASEDIR)/include \ -I$(BASEDIR)/arch/ia64/linux -I$(BASEDIR)/arch/ia64/linux-xen CFLAGS += $(CPPFLAGS) #CFLAGS += -Wno-pointer-arith -Wredundant-decls -CFLAGS += -DIA64 -DXEN -DLINUX_2_6 -DV_IOSAPIC_READY +CFLAGS += -DIA64 -DXEN -DLINUX_2_6 CFLAGS += -ffixed-r13 -mfixed-range=f2-f5,f12-f127 CFLAGS += -g #CFLAGS += -DVTI_DEBUG diff --git a/xen/arch/ia64/asm-offsets.c b/xen/arch/ia64/asm-offsets.c index 356e8f9359..789bedb248 100644 --- a/xen/arch/ia64/asm-offsets.c +++ b/xen/arch/ia64/asm-offsets.c @@ -12,6 +12,7 @@ #include #include #include +#include #define task_struct vcpu @@ -221,4 +222,37 @@ void foo(void) DEFINE(FAST_HYPERPRIVOP_PERFC_OFS, offsetof (struct perfcounter, fast_hyperprivop)); DEFINE(FAST_REFLECT_PERFC_OFS, offsetof (struct perfcounter, fast_reflect)); #endif + + BLANK(); + DEFINE(IA64_CPUINFO_PTCE_BASE_OFFSET, + offsetof(struct cpuinfo_ia64, ptce_base)); + DEFINE(IA64_CPUINFO_PTCE_COUNT_OFFSET, + offsetof(struct cpuinfo_ia64, ptce_count)); + DEFINE(IA64_CPUINFO_PTCE_STRIDE_OFFSET, + offsetof(struct cpuinfo_ia64, ptce_stride)); + + BLANK(); + DEFINE(IA64_MCA_CPU_PROC_STATE_DUMP_OFFSET, + offsetof(struct ia64_mca_cpu, proc_state_dump)); + DEFINE(IA64_MCA_CPU_STACK_OFFSET, + offsetof(struct ia64_mca_cpu, stack)); + DEFINE(IA64_MCA_CPU_STACKFRAME_OFFSET, + offsetof(struct ia64_mca_cpu, stackframe)); + DEFINE(IA64_MCA_CPU_RBSTORE_OFFSET, + offsetof(struct ia64_mca_cpu, rbstore)); + + DEFINE(IA64_DOMAIN_SHARED_INFO_OFFSET, + offsetof(struct domain, shared_info)); + DEFINE(IA64_DOMAIN_SHARED_INFO_VA_OFFSET, + offsetof(struct domain, arch.shared_info_va)); + DEFINE(IA64_DOMAIN_FLAGS_OFFSET, + offsetof(struct domain, arch.flags)); + + DEFINE(IA64_VCPU_VHPT_MADDR_OFFSET, + offsetof(struct vcpu, arch.vhpt_maddr)); + + BLANK(); + DEFINE(IA64_MCA_TLB_INFO_SIZE, sizeof(struct ia64_mca_tlb_info)); + DEFINE(IA64_MCA_PERCPU_OFFSET, + offsetof(struct ia64_mca_tlb_info, percpu_paddr)); } diff --git a/xen/arch/ia64/linux-xen/Makefile b/xen/arch/ia64/linux-xen/Makefile index b7f8257579..980ea8e746 100644 --- a/xen/arch/ia64/linux-xen/Makefile +++ b/xen/arch/ia64/linux-xen/Makefile @@ -18,3 +18,5 @@ obj-y += unwind.o obj-y += iosapic.o obj-y += numa.o obj-y += mm_numa.o +obj-y += perfmon.o +obj-y += perfmon_default_smpl.o diff --git a/xen/arch/ia64/linux-xen/README.origin b/xen/arch/ia64/linux-xen/README.origin index 9a489bca74..e667b05603 100644 --- a/xen/arch/ia64/linux-xen/README.origin +++ b/xen/arch/ia64/linux-xen/README.origin @@ -33,3 +33,11 @@ unwind_i.h -> linux/arch/ia64/kernel/unwind_i.h # The files below are from Linux-2.6.16 iosapic.c -> linux/arch/ia64/kernel/iosapic.c + +# The files below are from Linux-2.6.16.33 +perfmon.c -> linux/arch/kernel/perfmon.c +perfmon_default_smpl.c -> linux/arch/kernel/perfmon_default_smpl.c +perfmon_generic.h -> linux/arch/kernel/perfmon_generic.h +perfmon_itanium.h -> linux/arch/kernel/perfmon_itanium.h +perfmon_mckinley.h -> linux/arch/kernel/perfmon_mckinley.h +perfmon_montecito.h -> linux/arch/kernel/perfmon_montecito.h diff --git a/xen/arch/ia64/linux-xen/mca.c b/xen/arch/ia64/linux-xen/mca.c index 64151f3a4e..8715ef1d5e 100644 --- a/xen/arch/ia64/linux-xen/mca.c +++ b/xen/arch/ia64/linux-xen/mca.c @@ -81,6 +81,9 @@ #include #include #include +#include +#include +#include #endif #if defined(IA64_MCA_DEBUG_INFO) @@ -108,18 +111,33 @@ unsigned long __per_cpu_mca[NR_CPUS]; /* In mca_asm.S */ extern void ia64_monarch_init_handler (void); extern void ia64_slave_init_handler (void); +#ifdef XEN +extern void setup_vector (unsigned int vec, struct irqaction *action); +#define setup_irq(irq, action) setup_vector(irq, action) +#endif static ia64_mc_info_t ia64_mc_info; -#ifndef XEN +#ifdef XEN +#define jiffies NOW() +#undef HZ +#define HZ 1000000000UL +#endif + #define MAX_CPE_POLL_INTERVAL (15*60*HZ) /* 15 minutes */ #define MIN_CPE_POLL_INTERVAL (2*60*HZ) /* 2 minutes */ #define CMC_POLL_INTERVAL (1*60*HZ) /* 1 minute */ #define CPE_HISTORY_LENGTH 5 #define CMC_HISTORY_LENGTH 5 +#ifndef XEN static struct timer_list cpe_poll_timer; static struct timer_list cmc_poll_timer; +#else +#define mod_timer(timer, expires) set_timer(timer, expires) +static struct timer cpe_poll_timer; +static struct timer cmc_poll_timer; +#endif /* * This variable tells whether we are currently in polling mode. * Start with this in the wrong state so we won't play w/ timers @@ -136,11 +154,9 @@ static int cmc_polling_enabled = 1; static int cpe_poll_enabled = 1; extern void salinfo_log_wakeup(int type, u8 *buffer, u64 size, int irqsafe); -#endif /* !XEN */ static int mca_init; -#ifndef XEN /* * IA64_MCA log support */ @@ -157,11 +173,24 @@ typedef struct ia64_state_log_s static ia64_state_log_t ia64_state_log[IA64_MAX_LOG_TYPES]; +#ifndef XEN #define IA64_LOG_ALLOCATE(it, size) \ {ia64_state_log[it].isl_log[IA64_LOG_CURR_INDEX(it)] = \ (ia64_err_rec_t *)alloc_bootmem(size); \ ia64_state_log[it].isl_log[IA64_LOG_NEXT_INDEX(it)] = \ (ia64_err_rec_t *)alloc_bootmem(size);} +#else +#define IA64_LOG_ALLOCATE(it, size) \ + do { \ + unsigned int pageorder; \ + pageorder = get_order_from_bytes(sizeof(struct ia64_mca_cpu)); \ + ia64_state_log[it].isl_log[IA64_LOG_CURR_INDEX(it)] = \ + (ia64_err_rec_t *)alloc_xenheap_pages(pageorder); \ + ia64_state_log[it].isl_log[IA64_LOG_NEXT_INDEX(it)] = \ + (ia64_err_rec_t *)alloc_xenheap_pages(pageorder); \ + } while(0) +#endif + #define IA64_LOG_LOCK_INIT(it) spin_lock_init(&ia64_state_log[it].isl_lock) #define IA64_LOG_LOCK(it) spin_lock_irqsave(&ia64_state_log[it].isl_lock, s) #define IA64_LOG_UNLOCK(it) spin_unlock_irqrestore(&ia64_state_log[it].isl_lock,s) @@ -176,6 +205,12 @@ static ia64_state_log_t ia64_state_log[IA64_MAX_LOG_TYPES]; #define IA64_LOG_CURR_BUFFER(it) (void *)((ia64_state_log[it].isl_log[IA64_LOG_CURR_INDEX(it)])) #define IA64_LOG_COUNT(it) ia64_state_log[it].isl_count +#ifdef XEN +struct list_head *sal_queue, sal_log_queues[IA64_MAX_LOG_TYPES]; +sal_log_record_header_t *sal_record; +DEFINE_SPINLOCK(sal_queue_lock); +#endif + /* * ia64_log_init * Reset the OS ia64 log buffer @@ -200,8 +235,19 @@ ia64_log_init(int sal_info_type) IA64_LOG_ALLOCATE(sal_info_type, max_size); memset(IA64_LOG_CURR_BUFFER(sal_info_type), 0, max_size); memset(IA64_LOG_NEXT_BUFFER(sal_info_type), 0, max_size); + +#ifdef XEN + if (sal_record == NULL) { + unsigned int pageorder; + pageorder = get_order_from_bytes(max_size); + sal_record = (sal_log_record_header_t *) + alloc_xenheap_pages(pageorder); + BUG_ON(sal_record == NULL); + } +#endif } +#ifndef XEN /* * ia64_log_get * @@ -277,16 +323,160 @@ ia64_mca_log_sal_error_record(int sal_info_type) if (rh->severity == sal_log_severity_corrected) ia64_sal_clear_state_info(sal_info_type); } +#else /* !XEN */ +/* + * ia64_log_queue + * + * Get the current MCA log from SAL and copy it into the OS log buffer. + * + * Inputs : info_type (SAL_INFO_TYPE_{MCA,INIT,CMC,CPE}) + * Outputs : size (total record length) + * *buffer (ptr to error record) + * + */ +static u64 +ia64_log_queue(int sal_info_type, int virq) +{ + sal_log_record_header_t *log_buffer; + u64 total_len = 0; + int s; + sal_queue_entry_t *e; + unsigned long flags; + + IA64_LOG_LOCK(sal_info_type); + + /* Get the process state information */ + log_buffer = IA64_LOG_NEXT_BUFFER(sal_info_type); + + total_len = ia64_sal_get_state_info(sal_info_type, (u64 *)log_buffer); + + if (total_len) { + int queue_type; + + spin_lock_irqsave(&sal_queue_lock, flags); + + if (sal_info_type == SAL_INFO_TYPE_MCA && virq == VIRQ_MCA_CMC) + queue_type = SAL_INFO_TYPE_CMC; + else + queue_type = sal_info_type; + + e = xmalloc(sal_queue_entry_t); + BUG_ON(e == NULL); + e->cpuid = smp_processor_id(); + e->sal_info_type = sal_info_type; + e->vector = IA64_CMC_VECTOR; + e->virq = virq; + e->length = total_len; + + list_add_tail(&e->list, &sal_queue[queue_type]); + spin_unlock_irqrestore(&sal_queue_lock, flags); + + IA64_LOG_INDEX_INC(sal_info_type); + IA64_LOG_UNLOCK(sal_info_type); + if (sal_info_type != SAL_INFO_TYPE_MCA && + sal_info_type != SAL_INFO_TYPE_INIT) { + IA64_MCA_DEBUG("%s: SAL error record type %d retrieved. " + "Record length = %ld\n", __FUNCTION__, + sal_info_type, total_len); + } + return total_len; + } else { + IA64_LOG_UNLOCK(sal_info_type); + return 0; + } +} +#endif /* !XEN */ /* * platform dependent error handling */ -#endif /* !XEN */ #ifndef PLATFORM_MCA_HANDLERS -#ifndef XEN #ifdef CONFIG_ACPI +#ifdef XEN +/** + * Copy from linux/include/asm-generic/bug.h + */ +#define WARN_ON(condition) do { \ + if (unlikely((condition)!=0)) { \ + printk("Badness in %s at %s:%d\n", __FUNCTION__, __FILE__, __LINE__); \ + dump_stack(); \ + } \ +} while (0) + +/** + * Copy from linux/kernel/irq/manage.c + * + * disable_irq_nosync - disable an irq without waiting + * @irq: Interrupt to disable + * + * Disable the selected interrupt line. Disables and Enables are + * nested. + * Unlike disable_irq(), this function does not ensure existing + * instances of the IRQ handler have completed before returning. + * + * This function may be called from IRQ context. + */ +void disable_irq_nosync(unsigned int irq) +{ + irq_desc_t *desc = irq_desc + irq; + unsigned long flags; + + if (irq >= NR_IRQS) + return; + + spin_lock_irqsave(&desc->lock, flags); + if (!desc->depth++) { + desc->status |= IRQ_DISABLED; + desc->handler->disable(irq); + } + spin_unlock_irqrestore(&desc->lock, flags); +} + +/** + * Copy from linux/kernel/irq/manage.c + * + * enable_irq - enable handling of an irq + * @irq: Interrupt to enable + * + * Undoes the effect of one call to disable_irq(). If this + * matches the last disable, processing of interrupts on this + * IRQ line is re-enabled. + * + * This function may be called from IRQ context. + */ +void enable_irq(unsigned int irq) +{ + irq_desc_t *desc = irq_desc + irq; + unsigned long flags; + + if (irq >= NR_IRQS) + return; + + spin_lock_irqsave(&desc->lock, flags); + switch (desc->depth) { + case 0: + WARN_ON(1); + break; + case 1: { + unsigned int status = desc->status & ~IRQ_DISABLED; + + desc->status = status; + if ((status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) { + desc->status = status | IRQ_REPLAY; + hw_resend_irq(desc->handler,irq); + } + desc->handler->enable(irq); + /* fall-through */ + } + default: + desc->depth--; + } + spin_unlock_irqrestore(&desc->lock, flags); +} +#endif /* XEN */ + int cpe_vector = -1; static irqreturn_t @@ -302,8 +492,15 @@ ia64_mca_cpe_int_handler (int cpe_irq, void *arg, struct pt_regs *ptregs) /* SAL spec states this should run w/ interrupts enabled */ local_irq_enable(); +#ifndef XEN /* Get the CPE error record and log it */ ia64_mca_log_sal_error_record(SAL_INFO_TYPE_CPE); +#else + ia64_log_queue(SAL_INFO_TYPE_CPE, VIRQ_MCA_CPE); + /* CPE error does not inform to dom0 but the following codes are + reserved for future implementation */ +/* send_guest_vcpu_virq(dom0->vcpu[0], VIRQ_MCA_CPE); */ +#endif spin_lock(&cpe_history_lock); if (!cpe_poll_enabled && cpe_vector >= 0) { @@ -345,7 +542,6 @@ ia64_mca_cpe_int_handler (int cpe_irq, void *arg, struct pt_regs *ptregs) } #endif /* CONFIG_ACPI */ -#endif /* !XEN */ static void show_min_state (pal_min_state_area_t *minstate) @@ -593,7 +789,6 @@ init_handler_platform (pal_min_state_area_t *ms, while (1); /* hang city if no debugger */ } -#ifndef XEN #ifdef CONFIG_ACPI /* * ia64_mca_register_cpev @@ -624,9 +819,7 @@ ia64_mca_register_cpev (int cpev) } #endif /* CONFIG_ACPI */ -#endif /* !XEN */ #endif /* PLATFORM_MCA_HANDLERS */ -#ifndef XEN /* * ia64_mca_cmc_vector_setup @@ -713,6 +906,7 @@ ia64_mca_cmc_vector_enable (void *dummy) __FUNCTION__, smp_processor_id(), cmcv.cmcv_vector); } +#ifndef XEN /* * ia64_mca_cmc_vector_disable_keventd * @@ -736,6 +930,7 @@ ia64_mca_cmc_vector_enable_keventd(void *unused) { on_each_cpu(ia64_mca_cmc_vector_enable, NULL, 1, 0); } +#endif /* !XEN */ /* * ia64_mca_wakeup_ipi_wait @@ -887,15 +1082,26 @@ ia64_mca_wakeup_int_handler(int wakeup_irq, void *arg, struct pt_regs *ptregs) static void ia64_return_to_sal_check(int recover) { +#ifdef XEN + int cpu = smp_processor_id(); +#endif /* Copy over some relevant stuff from the sal_to_os_mca_handoff * so that it can be used at the time of os_mca_to_sal_handoff */ +#ifdef XEN + ia64_os_to_sal_handoff_state.imots_sal_gp = + ia64_sal_to_os_handoff_state[cpu].imsto_sal_gp; + + ia64_os_to_sal_handoff_state.imots_sal_check_ra = + ia64_sal_to_os_handoff_state[cpu].imsto_sal_check_ra; +#else ia64_os_to_sal_handoff_state.imots_sal_gp = ia64_sal_to_os_handoff_state.imsto_sal_gp; ia64_os_to_sal_handoff_state.imots_sal_check_ra = ia64_sal_to_os_handoff_state.imsto_sal_check_ra; +#endif if (recover) ia64_os_to_sal_handoff_state.imots_os_status = IA64_MCA_CORRECTED; @@ -905,8 +1111,13 @@ ia64_return_to_sal_check(int recover) /* Default = tell SAL to return to same context */ ia64_os_to_sal_handoff_state.imots_context = IA64_MCA_SAME_CONTEXT; +#ifdef XEN + ia64_os_to_sal_handoff_state.imots_new_min_state = + (u64 *)ia64_sal_to_os_handoff_state[cpu].pal_min_state; +#else ia64_os_to_sal_handoff_state.imots_new_min_state = (u64 *)ia64_sal_to_os_handoff_state.pal_min_state; +#endif } @@ -954,27 +1165,44 @@ EXPORT_SYMBOL(ia64_unreg_MCA_extension); void ia64_mca_ucmc_handler(void) { +#ifdef XEN + int cpu = smp_processor_id(); + pal_processor_state_info_t *psp = (pal_processor_state_info_t *) + &ia64_sal_to_os_handoff_state[cpu].proc_state_param; +#else pal_processor_state_info_t *psp = (pal_processor_state_info_t *) &ia64_sal_to_os_handoff_state.proc_state_param; +#endif int recover; +#ifndef XEN /* Get the MCA error record and log it */ ia64_mca_log_sal_error_record(SAL_INFO_TYPE_MCA); +#else + ia64_log_queue(SAL_INFO_TYPE_MCA, VIRQ_MCA_CMC); + send_guest_vcpu_virq(dom0->vcpu[0], VIRQ_MCA_CMC); +#endif /* TLB error is only exist in this SAL error record */ recover = (psp->tc && !(psp->cc || psp->bc || psp->rc || psp->uc)) /* other error recovery */ +#ifndef XEN || (ia64_mca_ucmc_extension && ia64_mca_ucmc_extension( IA64_LOG_CURR_BUFFER(SAL_INFO_TYPE_MCA), &ia64_sal_to_os_handoff_state, &ia64_os_to_sal_handoff_state)); +#else + ; +#endif +#ifndef XEN if (recover) { sal_log_record_header_t *rh = IA64_LOG_CURR_BUFFER(SAL_INFO_TYPE_MCA); rh->severity = sal_log_severity_corrected; ia64_sal_clear_state_info(SAL_INFO_TYPE_MCA); } +#endif /* * Wakeup all the processors which are spinning in the rendezvous * loop. @@ -985,8 +1213,10 @@ ia64_mca_ucmc_handler(void) ia64_return_to_sal_check(recover); } +#ifndef XEN static DECLARE_WORK(cmc_disable_work, ia64_mca_cmc_vector_disable_keventd, NULL); static DECLARE_WORK(cmc_enable_work, ia64_mca_cmc_vector_enable_keventd, NULL); +#endif /* * ia64_mca_cmc_int_handler @@ -1016,8 +1246,13 @@ ia64_mca_cmc_int_handler(int cmc_irq, void *arg, struct pt_regs *ptregs) /* SAL spec states this should run w/ interrupts enabled */ local_irq_enable(); +#ifndef XEN /* Get the CMC error record and log it */ ia64_mca_log_sal_error_record(SAL_INFO_TYPE_CMC); +#else + ia64_log_queue(SAL_INFO_TYPE_CMC, VIRQ_MCA_CMC); + send_guest_vcpu_virq(dom0->vcpu[0], VIRQ_MCA_CMC); +#endif spin_lock(&cmc_history_lock); if (!cmc_polling_enabled) { @@ -1034,7 +1269,12 @@ ia64_mca_cmc_int_handler(int cmc_irq, void *arg, struct pt_regs *ptregs) cmc_polling_enabled = 1; spin_unlock(&cmc_history_lock); +#ifndef XEN /* XXX FIXME */ schedule_work(&cmc_disable_work); +#else + cpumask_raise_softirq(cpu_online_map, + CMC_DISABLE_SOFTIRQ); +#endif /* * Corrected errors will still be corrected, but @@ -1083,7 +1323,13 @@ ia64_mca_cmc_int_caller(int cmc_irq, void *arg, struct pt_regs *ptregs) if (start_count == -1) start_count = IA64_LOG_COUNT(SAL_INFO_TYPE_CMC); +#ifndef XEN ia64_mca_cmc_int_handler(cmc_irq, arg, ptregs); +#else + IA64_MCA_DEBUG("%s: received polling vector = %#x on CPU %d\n", + __FUNCTION__, cmc_irq, smp_processor_id()); + ia64_log_queue(SAL_INFO_TYPE_CMC, VIRQ_MCA_CMC); +#endif for (++cpuid ; cpuid < NR_CPUS && !cpu_online(cpuid) ; cpuid++); @@ -1094,7 +1340,12 @@ ia64_mca_cmc_int_caller(int cmc_irq, void *arg, struct pt_regs *ptregs) if (start_count == IA64_LOG_COUNT(SAL_INFO_TYPE_CMC)) { printk(KERN_WARNING "Returning to interrupt driven CMC handler\n"); +#ifndef XEN /* XXX FIXME */ schedule_work(&cmc_enable_work); +#else + cpumask_raise_softirq(cpu_online_map, + CMC_ENABLE_SOFTIRQ); +#endif cmc_polling_enabled = 0; } else { @@ -1104,7 +1355,6 @@ ia64_mca_cmc_int_caller(int cmc_irq, void *arg, struct pt_regs *ptregs) start_count = -1; } - return IRQ_HANDLED; } @@ -1118,7 +1368,11 @@ ia64_mca_cmc_int_caller(int cmc_irq, void *arg, struct pt_regs *ptregs) * */ static void +#ifndef XEN ia64_mca_cmc_poll (unsigned long dummy) +#else +ia64_mca_cmc_poll (void *dummy) +#endif { /* Trigger a CMC interrupt cascade */ platform_send_ipi(first_cpu(cpu_online_map), IA64_CMCP_VECTOR, IA64_IPI_DM_INT, 0); @@ -1144,7 +1398,11 @@ static irqreturn_t ia64_mca_cpe_int_caller(int cpe_irq, void *arg, struct pt_regs *ptregs) { static int start_count = -1; +#ifdef XEN + static unsigned long poll_time = MIN_CPE_POLL_INTERVAL; +#else static int poll_time = MIN_CPE_POLL_INTERVAL; +#endif unsigned int cpuid; cpuid = smp_processor_id(); @@ -1153,7 +1411,13 @@ ia64_mca_cpe_int_caller(int cpe_irq, void *arg, struct pt_regs *ptregs) if (start_count == -1) start_count = IA64_LOG_COUNT(SAL_INFO_TYPE_CPE); +#ifndef XEN ia64_mca_cpe_int_handler(cpe_irq, arg, ptregs); +#else + IA64_MCA_DEBUG("%s: received polling vector = %#x on CPU %d\n", + __FUNCTION__, cpe_irq, smp_processor_id()); + ia64_log_queue(SAL_INFO_TYPE_CPE, VIRQ_MCA_CPE); +#endif for (++cpuid ; cpuid < NR_CPUS && !cpu_online(cpuid) ; cpuid++); @@ -1180,7 +1444,6 @@ ia64_mca_cpe_int_caller(int cpe_irq, void *arg, struct pt_regs *ptregs) mod_timer(&cpe_poll_timer, jiffies + poll_time); start_count = -1; } - return IRQ_HANDLED; } @@ -1195,14 +1458,17 @@ ia64_mca_cpe_int_caller(int cpe_irq, void *arg, struct pt_regs *ptregs) * */ static void +#ifndef XEN ia64_mca_cpe_poll (unsigned long dummy) +#else +ia64_mca_cpe_poll (void *dummy) +#endif { /* Trigger a CPE interrupt cascade */ platform_send_ipi(first_cpu(cpu_online_map), IA64_CPEP_VECTOR, IA64_IPI_DM_INT, 0); } #endif /* CONFIG_ACPI */ -#endif /* !XEN */ /* * C portion of the OS INIT handler @@ -1248,7 +1514,6 @@ ia64_init_handler (struct pt_regs *pt, struct switch_stack *sw) init_handler_platform(ms, pt, sw); /* call platform specific routines */ } -#ifndef XEN static int __init ia64_mca_disable_cpe_polling(char *str) { @@ -1260,42 +1525,53 @@ __setup("disable_cpe_poll", ia64_mca_disable_cpe_polling); static struct irqaction cmci_irqaction = { .handler = ia64_mca_cmc_int_handler, +#ifndef XEN .flags = SA_INTERRUPT, +#endif .name = "cmc_hndlr" }; static struct irqaction cmcp_irqaction = { .handler = ia64_mca_cmc_int_caller, +#ifndef XEN .flags = SA_INTERRUPT, +#endif .name = "cmc_poll" }; static struct irqaction mca_rdzv_irqaction = { .handler = ia64_mca_rendez_int_handler, +#ifndef XEN .flags = SA_INTERRUPT, +#endif .name = "mca_rdzv" }; static struct irqaction mca_wkup_irqaction = { .handler = ia64_mca_wakeup_int_handler, +#ifndef XEN .flags = SA_INTERRUPT, +#endif .name = "mca_wkup" }; #ifdef CONFIG_ACPI static struct irqaction mca_cpe_irqaction = { .handler = ia64_mca_cpe_int_handler, +#ifndef XEN .flags = SA_INTERRUPT, +#endif .name = "cpe_hndlr" }; static struct irqaction mca_cpep_irqaction = { .handler = ia64_mca_cpe_int_caller, +#ifndef XEN .flags = SA_INTERRUPT, +#endif .name = "cpe_poll" }; #endif /* CONFIG_ACPI */ -#endif /* !XEN */ /* Do per-CPU MCA-related initialization. */ @@ -1329,6 +1605,13 @@ ia64_mca_cpu_init(void *cpu_data) #endif } } +#ifdef XEN + else if (sal_queue) { + int i; + for (i = 0; i < IA64_MAX_LOG_TYPES; i++) + ia64_log_queue(i, 0); + } +#endif /* * The MCA info structure was allocated earlier and its @@ -1395,18 +1678,15 @@ ia64_mca_init(void) ia64_fptr_t *mon_init_ptr = (ia64_fptr_t *)ia64_monarch_init_handler; ia64_fptr_t *slave_init_ptr = (ia64_fptr_t *)ia64_slave_init_handler; ia64_fptr_t *mca_hldlr_ptr = (ia64_fptr_t *)ia64_os_mca_dispatch; -#ifdef XEN - s64 rc; - - slave_init_ptr = (ia64_fptr_t *)ia64_monarch_init_handler; - - IA64_MCA_DEBUG("%s: begin\n", __FUNCTION__); -#else int i; s64 rc; struct ia64_sal_retval isrv; u64 timeout = IA64_MCA_RENDEZ_TIMEOUT; /* platform specific */ +#ifdef XEN + slave_init_ptr = (ia64_fptr_t *)ia64_monarch_init_handler; +#endif + IA64_MCA_DEBUG("%s: begin\n", __FUNCTION__); /* Clear the Rendez checkin flag for all cpus */ @@ -1451,7 +1731,6 @@ ia64_mca_init(void) } IA64_MCA_DEBUG("%s: registered MCA rendezvous spinloop and wakeup mech.\n", __FUNCTION__); -#endif /* !XEN */ ia64_mc_info.imi_mca_handler = ia64_tpa(mca_hldlr_ptr->fp); /* @@ -1503,7 +1782,6 @@ ia64_mca_init(void) IA64_MCA_DEBUG("%s: registered OS INIT handler with SAL\n", __FUNCTION__); -#ifndef XEN /* * Configure the CMCI/P vector and handler. Interrupts for CMC are * per-processor, so AP CMC interrupts are setup in smp_callin() (smpboot.c). @@ -1531,13 +1809,29 @@ ia64_mca_init(void) ia64_log_init(SAL_INFO_TYPE_INIT); ia64_log_init(SAL_INFO_TYPE_CMC); ia64_log_init(SAL_INFO_TYPE_CPE); -#endif /* !XEN */ + +#ifdef XEN + INIT_LIST_HEAD(&sal_log_queues[SAL_INFO_TYPE_MCA]); + INIT_LIST_HEAD(&sal_log_queues[SAL_INFO_TYPE_INIT]); + INIT_LIST_HEAD(&sal_log_queues[SAL_INFO_TYPE_CMC]); + INIT_LIST_HEAD(&sal_log_queues[SAL_INFO_TYPE_CPE]); + + /* NULL sal_queue used elsewhere to determine MCA init state */ + sal_queue = sal_log_queues; + + open_softirq(CMC_DISABLE_SOFTIRQ, + (softirq_handler)ia64_mca_cmc_vector_disable); + open_softirq(CMC_ENABLE_SOFTIRQ, + (softirq_handler)ia64_mca_cmc_vector_enable); + + for (i = 0; i < IA64_MAX_LOG_TYPES; i++) + ia64_log_queue(i, 0); +#endif mca_init = 1; printk(KERN_INFO "MCA related initialization done\n"); } -#ifndef XEN /* * ia64_mca_late_init * @@ -1555,20 +1849,34 @@ ia64_mca_late_init(void) return 0; /* Setup the CMCI/P vector and handler */ +#ifndef XEN init_timer(&cmc_poll_timer); cmc_poll_timer.function = ia64_mca_cmc_poll; +#else + init_timer(&cmc_poll_timer, ia64_mca_cmc_poll, + NULL, smp_processor_id()); +#endif /* Unmask/enable the vector */ cmc_polling_enabled = 0; +#ifndef XEN /* XXX FIXME */ schedule_work(&cmc_enable_work); +#else + cpumask_raise_softirq(cpu_online_map, CMC_ENABLE_SOFTIRQ); +#endif IA64_MCA_DEBUG("%s: CMCI/P setup and enabled.\n", __FUNCTION__); #ifdef CONFIG_ACPI /* Setup the CPEI/P vector and handler */ cpe_vector = acpi_request_vector(ACPI_INTERRUPT_CPEI); +#ifndef XEN init_timer(&cpe_poll_timer); cpe_poll_timer.function = ia64_mca_cpe_poll; +#else + init_timer(&cpe_poll_timer, ia64_mca_cpe_poll, + NULL,smp_processor_id()); +#endif { irq_desc_t *desc; @@ -1598,5 +1906,8 @@ ia64_mca_late_init(void) return 0; } +#ifndef XEN device_initcall(ia64_mca_late_init); -#endif /* !XEN */ +#else +__initcall(ia64_mca_late_init); +#endif diff --git a/xen/arch/ia64/linux-xen/mca_asm.S b/xen/arch/ia64/linux-xen/mca_asm.S index 1f1cb4dda8..a73a8e1daa 100644 --- a/xen/arch/ia64/linux-xen/mca_asm.S +++ b/xen/arch/ia64/linux-xen/mca_asm.S @@ -24,6 +24,9 @@ #include #include #include +#ifdef XEN +#include +#endif /* * When we get a machine check, the kernel stack pointer is no longer @@ -50,8 +53,7 @@ */ #ifdef XEN #define SAL_TO_OS_MCA_HANDOFF_STATE_SAVE(_tmp) \ - movl _tmp=THIS_CPU(ia64_sal_to_os_handoff_state_addr);; \ - tpa _tmp=_tmp;; \ + GET_THIS_PADDR(_tmp, ia64_sal_to_os_handoff_state_addr);; \ ld8 _tmp=[_tmp];; \ st8 [_tmp]=r1,0x08;; \ st8 [_tmp]=r8,0x08;; \ @@ -72,6 +74,7 @@ st8 [_tmp]=r12,0x08;; \ st8 [_tmp]=r17,0x08;; \ st8 [_tmp]=r18,0x08 +#endif /* XEN */ /* * OS_MCA_TO_SAL_HANDOFF_STATE (SAL 3.0 spec) @@ -101,6 +104,24 @@ * imots_sal_check_ra=Return address to location within SAL_CHECK * */ +#ifdef XEN +#define COLD_BOOT_HANDOFF_STATE(sal_to_os_handoff,os_to_sal_handoff,tmp)\ + movl tmp=IA64_MCA_COLD_BOOT; \ + GET_THIS_PADDR(r2,ia64_sal_to_os_handoff_state_addr);; \ + ld8 sal_to_os_handoff=[sal_to_os_handoff];; \ + movl os_to_sal_handoff=ia64_os_to_sal_handoff_state;; \ + dep os_to_sal_handoff = 0, os_to_sal_handoff, 60, 4;; \ + /*DATA_VA_TO_PA(os_to_sal_handoff);;*/ \ + st8 [os_to_sal_handoff]=tmp,8;; \ + ld8 tmp=[sal_to_os_handoff],48;; \ + st8 [os_to_sal_handoff]=tmp,8;; \ + movl tmp=IA64_MCA_SAME_CONTEXT;; \ + st8 [os_to_sal_handoff]=tmp,8;; \ + ld8 tmp=[sal_to_os_handoff],-8;; \ + st8 [os_to_sal_handoff]=tmp,8;; \ + ld8 tmp=[sal_to_os_handoff];; \ + st8 [os_to_sal_handoff]=tmp;; +#else /* XEN */ #define COLD_BOOT_HANDOFF_STATE(sal_to_os_handoff,os_to_sal_handoff,tmp)\ movl tmp=IA64_MCA_COLD_BOOT; \ movl sal_to_os_handoff=__pa(ia64_sal_to_os_handoff_state); \ @@ -114,13 +135,13 @@ st8 [os_to_sal_handoff]=tmp,8;; \ ld8 tmp=[sal_to_os_handoff];; \ st8 [os_to_sal_handoff]=tmp;; +#endif /* XEN */ #define GET_IA64_MCA_DATA(reg) \ GET_THIS_PADDR(reg, ia64_mca_data) \ ;; \ ld8 reg=[reg] -#endif /* XEN */ .global ia64_os_mca_dispatch .global ia64_os_mca_dispatch_end #ifndef XEN @@ -132,7 +153,40 @@ .text .align 16 -#ifndef XEN +#ifdef XEN +/* + * void set_per_cpu_data(void) + * { + * int i; + * for (i = 0; i < 64; i++) { + * if (ia64_mca_tlb_list[i].cr_lid == ia64_getreg(_IA64_REG_CR_LID)) { + * ia64_set_kr(IA64_KR_PER_CPU_DATA, ia64_mca_tlb_list[i].percpu_paddr); + * return; + * } + * } + * while(1); // Endless loop on error + * } + */ +#define SET_PER_CPU_DATA() \ + LOAD_PHYSICAL(p0,r2,ia64_mca_tlb_list);; \ + mov r7 = r0; \ + mov r6 = r0;; \ + adds r3 = IA64_MCA_PERCPU_OFFSET, r2; \ +1: add r4 = r6, r2; \ + mov r5=cr.lid;; \ + adds r7 = 1, r7; \ + ld8 r4 = [r4];; \ + cmp.ne p6, p7 = r5, r4; \ + cmp4.lt p8, p9 = NR_CPUS-1, r7; \ +(p7) br.cond.dpnt 3f; \ + adds r6 = 16, r6; \ +(p9) br.cond.sptk 1b; \ +2: br 2b;; /* Endless loop on error */ \ +3: add r4 = r6, r3;; \ + ld8 r4 = [r4];; \ + mov ar.k3=r4 +#endif /* XEN */ + /* * Just the TLB purge part is moved to a separate function * so we can re-use the code for cpu hotplug code as well @@ -221,6 +275,44 @@ ia64_do_tlb_purge: ;; srlz.i ;; +#ifdef XEN + // 5. VHPT +#if VHPT_ENABLED + // r25 = __va_ul(vcpu_vhpt_maddr(v)); + GET_THIS_PADDR(r2,cpu_kr);; + add r2=IA64_KR_CURRENT_OFFSET,r2;; + ld8 r2=[r2];; +#ifdef CONFIG_XEN_IA64_PERVCPU_VHPT +#define HAS_PERVCPU_VHPT_MASK 0x2 + dep r3=0,r2,60,4;; // virtual to physical + add r3=IA64_VCPU_DOMAIN_OFFSET,r3;; + ld8 r3=[r3];; + dep r3=0,r3,60,4;; // virtual to physical + add r3=IA64_DOMAIN_FLAGS_OFFSET,r3;; + ld8 r3=[r3];; + and r3=HAS_PERVCPU_VHPT_MASK,r3;; + cmp.eq p6,p0=r3,r0;; +(p6) br.cond.sptk .not_pervcpu_vhpt + add r2=IA64_VCPU_VHPT_MADDR_OFFSET,r2;; + dep r2=0,r2,60,4;; // virtual to physical + ld8 r2=[r2];; + dep r25=-1,r2,60,4;; // physical to virtual + br.sptk .percpu_vhpt_done +#endif +.not_pervcpu_vhpt: + GET_THIS_PADDR(r2, vhpt_paddr);; + ld8 r2=[r2];; + dep r25=-1,r2,60,4;; // physical to virtual +.percpu_vhpt_done: + dep r20=0,r25,0,IA64_GRANULE_SHIFT + mov r24=IA64_GRANULE_SHIFT<<2 + ;; + ptr.d r20,r24 + ;; + srlz.d + ;; +#endif +#endif // Now branch away to caller. br.sptk.many b1 ;; @@ -235,6 +327,9 @@ ia64_os_mca_spin: cmp.ne p6,p0=r4,r0 (p6) br ia64_os_mca_spin +#ifdef XEN + SET_PER_CPU_DATA();; +#endif // Save the SAL to OS MCA handoff state as defined // by SAL SPEC 3.0 // NOTE : The order in which the state gets saved @@ -250,7 +345,19 @@ begin_os_mca_dump: ia64_os_mca_done_dump: +#ifdef XEN + // Set current to ar.k6 + GET_THIS_PADDR(r2,cpu_kr);; + add r2=IA64_KR_CURRENT_OFFSET,r2;; + ld8 r2=[r2];; + mov ar.k6=r2;; + + GET_THIS_PADDR(r2,ia64_sal_to_os_handoff_state_addr);; + ld8 r2=[r2];; + adds r16=56,r2 +#else LOAD_PHYSICAL(p0,r16,ia64_sal_to_os_handoff_state+56) +#endif ;; ld8 r18=[r16] // Get processor state parameter on existing PALE_CHECK. ;; @@ -342,6 +449,28 @@ ia64_reload_tr: ;; srlz.d ;; +#ifdef XEN + // 5. VHPT +#if VHPT_ENABLED + // r25 = __va_ul(vcpu_vhpt_maddr(v)); + dep r20=0,r25,0,IA64_GRANULE_SHIFT + movl r26=PAGE_KERNEL + ;; + mov r21=IA64_TR_VHPT + dep r22=0,r20,60,4 // physical address of + // va_vhpt & ~(IA64_GRANULE_SIZE - 1) + mov r24=IA64_GRANULE_SHIFT<<2 + ;; + or r23=r22,r26 // construct PA | page properties + mov cr.itir=r24 + mov cr.ifa=r20 + ;; + itr.d dtr[r21]=r23 // wire in new mapping... + ;; + srlz.d + ;; +#endif +#endif br.sptk.many done_tlb_purge_and_reload err: COLD_BOOT_HANDOFF_STATE(r20,r21,r22) @@ -874,12 +1003,6 @@ end_os_mca_restore: br ia64_os_mca_done_restore;; //EndStub////////////////////////////////////////////////////////////////////// -#else -ia64_os_mca_dispatch: -1: - br.sptk 1b -ia64_os_mca_dispatch_end: -#endif /* !XEN */ // ok, the issue here is that we need to save state information so @@ -911,6 +1034,15 @@ ia64_os_mca_dispatch_end: GLOBAL_ENTRY(ia64_monarch_init_handler) .prologue +#ifdef XEN /* Need in ia64_monarch_init_handler? */ + SET_PER_CPU_DATA();; + + // Set current to ar.k6 + GET_THIS_PADDR(r2,cpu_kr);; + add r2=IA64_KR_CURRENT_OFFSET,r2;; + ld8 r2=[r2];; + mov ar.k6=r2;; +#endif // stash the information the SAL passed to os SAL_TO_OS_MCA_HANDOFF_STATE_SAVE(r2) ;; diff --git a/xen/arch/ia64/linux-xen/perfmon.c b/xen/arch/ia64/linux-xen/perfmon.c new file mode 100644 index 0000000000..cc776c631a --- /dev/null +++ b/xen/arch/ia64/linux-xen/perfmon.c @@ -0,0 +1,7798 @@ +/* + * This file implements the perfmon-2 subsystem which is used + * to program the IA-64 Performance Monitoring Unit (PMU). + * + * The initial version of perfmon.c was written by + * Ganesh Venkitachalam, IBM Corp. + * + * Then it was modified for perfmon-1.x by Stephane Eranian and + * David Mosberger, Hewlett Packard Co. + * + * Version Perfmon-2.x is a rewrite of perfmon-1.x + * by Stephane Eranian, Hewlett Packard Co. + * + * Copyright (C) 1999-2005 Hewlett Packard Co + * Stephane Eranian + * David Mosberger-Tang + * + * More information about perfmon available at: + * http://www.hpl.hp.com/research/linux/perfmon + * + * + * For Xen/IA64 xenoprof + * Copyright (c) 2006 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef XEN +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef XEN +#include +#include +#define CONFIG_PERFMON +#define pid vcpu_id +#define thread arch._thread +#define task_pt_regs vcpu_regs +#endif + +#ifdef CONFIG_PERFMON +/* + * perfmon context state + */ +#define PFM_CTX_UNLOADED 1 /* context is not loaded onto any task */ +#define PFM_CTX_LOADED 2 /* context is loaded onto a task */ +#define PFM_CTX_MASKED 3 /* context is loaded but monitoring is masked due to overflow */ +#define PFM_CTX_ZOMBIE 4 /* owner of the context is closing it */ + +#define PFM_INVALID_ACTIVATION (~0UL) + +/* + * depth of message queue + */ +#define PFM_MAX_MSGS 32 +#define PFM_CTXQ_EMPTY(g) ((g)->ctx_msgq_head == (g)->ctx_msgq_tail) + +/* + * type of a PMU register (bitmask). + * bitmask structure: + * bit0 : register implemented + * bit1 : end marker + * bit2-3 : reserved + * bit4 : pmc has pmc.pm + * bit5 : pmc controls a counter (has pmc.oi), pmd is used as counter + * bit6-7 : register type + * bit8-31: reserved + */ +#define PFM_REG_NOTIMPL 0x0 /* not implemented at all */ +#define PFM_REG_IMPL 0x1 /* register implemented */ +#define PFM_REG_END 0x2 /* end marker */ +#define PFM_REG_MONITOR (0x1<<4|PFM_REG_IMPL) /* a PMC with a pmc.pm field only */ +#define PFM_REG_COUNTING (0x2<<4|PFM_REG_MONITOR) /* a monitor + pmc.oi+ PMD used as a counter */ +#define PFM_REG_CONTROL (0x4<<4|PFM_REG_IMPL) /* PMU control register */ +#define PFM_REG_CONFIG (0x8<<4|PFM_REG_IMPL) /* configuration register */ +#define PFM_REG_BUFFER (0xc<<4|PFM_REG_IMPL) /* PMD used as buffer */ + +#define PMC_IS_LAST(i) (pmu_conf->pmc_desc[i].type & PFM_REG_END) +#define PMD_IS_LAST(i) (pmu_conf->pmd_desc[i].type & PFM_REG_END) + +#define PMC_OVFL_NOTIFY(ctx, i) ((ctx)->ctx_pmds[i].flags & PFM_REGFL_OVFL_NOTIFY) + +/* i assumed unsigned */ +#define PMC_IS_IMPL(i) (i< PMU_MAX_PMCS && (pmu_conf->pmc_desc[i].type & PFM_REG_IMPL)) +#define PMD_IS_IMPL(i) (i< PMU_MAX_PMDS && (pmu_conf->pmd_desc[i].type & PFM_REG_IMPL)) + +/* XXX: these assume that register i is implemented */ +#define PMD_IS_COUNTING(i) ((pmu_conf->pmd_desc[i].type & PFM_REG_COUNTING) == PFM_REG_COUNTING) +#define PMC_IS_COUNTING(i) ((pmu_conf->pmc_desc[i].type & PFM_REG_COUNTING) == PFM_REG_COUNTING) +#define PMC_IS_MONITOR(i) ((pmu_conf->pmc_desc[i].type & PFM_REG_MONITOR) == PFM_REG_MONITOR) +#define PMC_IS_CONTROL(i) ((pmu_conf->pmc_desc[i].type & PFM_REG_CONTROL) == PFM_REG_CONTROL) + +#define PMC_DFL_VAL(i) pmu_conf->pmc_desc[i].default_value +#define PMC_RSVD_MASK(i) pmu_conf->pmc_desc[i].reserved_mask +#define PMD_PMD_DEP(i) pmu_conf->pmd_desc[i].dep_pmd[0] +#define PMC_PMD_DEP(i) pmu_conf->pmc_desc[i].dep_pmd[0] + +#define PFM_NUM_IBRS IA64_NUM_DBG_REGS +#define PFM_NUM_DBRS IA64_NUM_DBG_REGS + +#define CTX_OVFL_NOBLOCK(c) ((c)->ctx_fl_block == 0) +#define CTX_HAS_SMPL(c) ((c)->ctx_fl_is_sampling) +#define PFM_CTX_TASK(h) (h)->ctx_task + +#define PMU_PMC_OI 5 /* position of pmc.oi bit */ + +/* XXX: does not support more than 64 PMDs */ +#define CTX_USED_PMD(ctx, mask) (ctx)->ctx_used_pmds[0] |= (mask) +#define CTX_IS_USED_PMD(ctx, c) (((ctx)->ctx_used_pmds[0] & (1UL << (c))) != 0UL) + +#define CTX_USED_MONITOR(ctx, mask) (ctx)->ctx_used_monitors[0] |= (mask) + +#define CTX_USED_IBR(ctx,n) (ctx)->ctx_used_ibrs[(n)>>6] |= 1UL<< ((n) % 64) +#define CTX_USED_DBR(ctx,n) (ctx)->ctx_used_dbrs[(n)>>6] |= 1UL<< ((n) % 64) +#define CTX_USES_DBREGS(ctx) (((pfm_context_t *)(ctx))->ctx_fl_using_dbreg==1) +#define PFM_CODE_RR 0 /* requesting code range restriction */ +#define PFM_DATA_RR 1 /* requestion data range restriction */ + +#define PFM_CPUINFO_CLEAR(v) pfm_get_cpu_var(pfm_syst_info) &= ~(v) +#define PFM_CPUINFO_SET(v) pfm_get_cpu_var(pfm_syst_info) |= (v) +#define PFM_CPUINFO_GET() pfm_get_cpu_var(pfm_syst_info) + +#define RDEP(x) (1UL<<(x)) + +/* + * context protection macros + * in SMP: + * - we need to protect against CPU concurrency (spin_lock) + * - we need to protect against PMU overflow interrupts (local_irq_disable) + * in UP: + * - we need to protect against PMU overflow interrupts (local_irq_disable) + * + * spin_lock_irqsave()/spin_lock_irqrestore(): + * in SMP: local_irq_disable + spin_lock + * in UP : local_irq_disable + * + * spin_lock()/spin_lock(): + * in UP : removed automatically + * in SMP: protect against context accesses from other CPU. interrupts + * are not masked. This is useful for the PMU interrupt handler + * because we know we will not get PMU concurrency in that code. + */ +#define PROTECT_CTX(c, f) \ + do { \ + DPRINT(("spinlock_irq_save ctx %p by [%d]\n", c, current->pid)); \ + spin_lock_irqsave(&(c)->ctx_lock, f); \ + DPRINT(("spinlocked ctx %p by [%d]\n", c, current->pid)); \ + } while(0) + +#define UNPROTECT_CTX(c, f) \ + do { \ + DPRINT(("spinlock_irq_restore ctx %p by [%d]\n", c, current->pid)); \ + spin_unlock_irqrestore(&(c)->ctx_lock, f); \ + } while(0) + +#define PROTECT_CTX_NOPRINT(c, f) \ + do { \ + spin_lock_irqsave(&(c)->ctx_lock, f); \ + } while(0) + + +#define UNPROTECT_CTX_NOPRINT(c, f) \ + do { \ + spin_unlock_irqrestore(&(c)->ctx_lock, f); \ + } while(0) + + +#define PROTECT_CTX_NOIRQ(c) \ + do { \ + spin_lock(&(c)->ctx_lock); \ + } while(0) + +#define UNPROTECT_CTX_NOIRQ(c) \ + do { \ + spin_unlock(&(c)->ctx_lock); \ + } while(0) + + +#ifdef CONFIG_SMP + +#define GET_ACTIVATION() pfm_get_cpu_var(pmu_activation_number) +#define INC_ACTIVATION() pfm_get_cpu_var(pmu_activation_number)++ +#define SET_ACTIVATION(c) (c)->ctx_last_activation = GET_ACTIVATION() + +#else /* !CONFIG_SMP */ +#define SET_ACTIVATION(t) do {} while(0) +#define GET_ACTIVATION(t) do {} while(0) +#define INC_ACTIVATION(t) do {} while(0) +#endif /* CONFIG_SMP */ + +#define SET_PMU_OWNER(t, c) do { pfm_get_cpu_var(pmu_owner) = (t); pfm_get_cpu_var(pmu_ctx) = (c); } while(0) +#define GET_PMU_OWNER() pfm_get_cpu_var(pmu_owner) +#define GET_PMU_CTX() pfm_get_cpu_var(pmu_ctx) + +#define LOCK_PFS(g) spin_lock_irqsave(&pfm_sessions.pfs_lock, g) +#define UNLOCK_PFS(g) spin_unlock_irqrestore(&pfm_sessions.pfs_lock, g) + +#define PFM_REG_RETFLAG_SET(flags, val) do { flags &= ~PFM_REG_RETFL_MASK; flags |= (val); } while(0) + +/* + * cmp0 must be the value of pmc0 + */ +#define PMC0_HAS_OVFL(cmp0) (cmp0 & ~0x1UL) + +#define PFMFS_MAGIC 0xa0b4d889 + +/* + * debugging + */ +#define PFM_DEBUGGING 1 +#ifdef PFM_DEBUGGING +#define DPRINT(a) \ + do { \ + if (unlikely(pfm_sysctl.debug >0)) { printk("%s.%d: CPU%d [%d] ", __FUNCTION__, __LINE__, smp_processor_id(), current->pid); printk a; } \ + } while (0) + +#define DPRINT_ovfl(a) \ + do { \ + if (unlikely(pfm_sysctl.debug > 0 && pfm_sysctl.debug_ovfl >0)) { printk("%s.%d: CPU%d [%d] ", __FUNCTION__, __LINE__, smp_processor_id(), current->pid); printk a; } \ + } while (0) +#endif + +/* + * 64-bit software counter structure + * + * the next_reset_type is applied to the next call to pfm_reset_regs() + */ +typedef struct { + unsigned long val; /* virtual 64bit counter value */ + unsigned long lval; /* last reset value */ + unsigned long long_reset; /* reset value on sampling overflow */ + unsigned long short_reset; /* reset value on overflow */ + unsigned long reset_pmds[4]; /* which other pmds to reset when this counter overflows */ + unsigned long smpl_pmds[4]; /* which pmds are accessed when counter overflow */ + unsigned long seed; /* seed for random-number generator */ + unsigned long mask; /* mask for random-number generator */ + unsigned int flags; /* notify/do not notify */ + unsigned long eventid; /* overflow event identifier */ +} pfm_counter_t; + +/* + * context flags + */ +typedef struct { + unsigned int block:1; /* when 1, task will blocked on user notifications */ + unsigned int system:1; /* do system wide monitoring */ + unsigned int using_dbreg:1; /* using range restrictions (debug registers) */ + unsigned int is_sampling:1; /* true if using a custom format */ + unsigned int excl_idle:1; /* exclude idle task in system wide session */ + unsigned int going_zombie:1; /* context is zombie (MASKED+blocking) */ + unsigned int trap_reason:2; /* reason for going into pfm_handle_work() */ + unsigned int no_msg:1; /* no message sent on overflow */ + unsigned int can_restart:1; /* allowed to issue a PFM_RESTART */ + unsigned int reserved:22; +} pfm_context_flags_t; + +#define PFM_TRAP_REASON_NONE 0x0 /* default value */ +#define PFM_TRAP_REASON_BLOCK 0x1 /* we need to block on overflow */ +#define PFM_TRAP_REASON_RESET 0x2 /* we need to reset PMDs */ + + +/* + * perfmon context: encapsulates all the state of a monitoring session + */ + +typedef struct pfm_context { + spinlock_t ctx_lock; /* context protection */ + + pfm_context_flags_t ctx_flags; /* bitmask of flags (block reason incl.) */ + unsigned int ctx_state; /* state: active/inactive (no bitfield) */ + + struct task_struct *ctx_task; /* task to which context is attached */ + + unsigned long ctx_ovfl_regs[4]; /* which registers overflowed (notification) */ + +#ifndef XEN + struct completion ctx_restart_done; /* use for blocking notification mode */ +#endif + + unsigned long ctx_used_pmds[4]; /* bitmask of PMD used */ + unsigned long ctx_all_pmds[4]; /* bitmask of all accessible PMDs */ + unsigned long ctx_reload_pmds[4]; /* bitmask of force reload PMD on ctxsw in */ + + unsigned long ctx_all_pmcs[4]; /* bitmask of all accessible PMCs */ + unsigned long ctx_reload_pmcs[4]; /* bitmask of force reload PMC on ctxsw in */ + unsigned long ctx_used_monitors[4]; /* bitmask of monitor PMC being used */ + + unsigned long ctx_pmcs[IA64_NUM_PMC_REGS]; /* saved copies of PMC values */ + + unsigned int ctx_used_ibrs[1]; /* bitmask of used IBR (speedup ctxsw in) */ + unsigned int ctx_used_dbrs[1]; /* bitmask of used DBR (speedup ctxsw in) */ + unsigned long ctx_dbrs[IA64_NUM_DBG_REGS]; /* DBR values (cache) when not loaded */ + unsigned long ctx_ibrs[IA64_NUM_DBG_REGS]; /* IBR values (cache) when not loaded */ + + pfm_counter_t ctx_pmds[IA64_NUM_PMD_REGS]; /* software state for PMDS */ + + u64 ctx_saved_psr_up; /* only contains psr.up value */ + + unsigned long ctx_last_activation; /* context last activation number for last_cpu */ + unsigned int ctx_last_cpu; /* CPU id of current or last CPU used (SMP only) */ + unsigned int ctx_cpu; /* cpu to which perfmon is applied (system wide) */ + + int ctx_fd; /* file descriptor used my this context */ + pfm_ovfl_arg_t ctx_ovfl_arg; /* argument to custom buffer format handler */ + + pfm_buffer_fmt_t *ctx_buf_fmt; /* buffer format callbacks */ + void *ctx_smpl_hdr; /* points to sampling buffer header kernel vaddr */ + unsigned long ctx_smpl_size; /* size of sampling buffer */ + void *ctx_smpl_vaddr; /* user level virtual address of smpl buffer */ + +#ifndef XEN + wait_queue_head_t ctx_msgq_wait; + pfm_msg_t ctx_msgq[PFM_MAX_MSGS]; + int ctx_msgq_head; + int ctx_msgq_tail; + struct fasync_struct *ctx_async_queue; + + wait_queue_head_t ctx_zombieq; /* termination cleanup wait queue */ +#endif +} pfm_context_t; + +/* + * magic number used to verify that structure is really + * a perfmon context + */ +#define PFM_IS_FILE(f) ((f)->f_op == &pfm_file_ops) + +#define PFM_GET_CTX(t) ((pfm_context_t *)(t)->thread.pfm_context) + +#ifdef CONFIG_SMP +#define SET_LAST_CPU(ctx, v) (ctx)->ctx_last_cpu = (v) +#define GET_LAST_CPU(ctx) (ctx)->ctx_last_cpu +#else +#define SET_LAST_CPU(ctx, v) do {} while(0) +#define GET_LAST_CPU(ctx) do {} while(0) +#endif + + +#define ctx_fl_block ctx_flags.block +#define ctx_fl_system ctx_flags.system +#define ctx_fl_using_dbreg ctx_flags.using_dbreg +#define ctx_fl_is_sampling ctx_flags.is_sampling +#define ctx_fl_excl_idle ctx_flags.excl_idle +#define ctx_fl_going_zombie ctx_flags.going_zombie +#define ctx_fl_trap_reason ctx_flags.trap_reason +#define ctx_fl_no_msg ctx_flags.no_msg +#define ctx_fl_can_restart ctx_flags.can_restart + +#define PFM_SET_WORK_PENDING(t, v) do { (t)->thread.pfm_needs_checking = v; } while(0); +#define PFM_GET_WORK_PENDING(t) (t)->thread.pfm_needs_checking + +/* + * global information about all sessions + * mostly used to synchronize between system wide and per-process + */ +typedef struct { + spinlock_t pfs_lock; /* lock the structure */ + + unsigned int pfs_task_sessions; /* number of per task sessions */ + unsigned int pfs_sys_sessions; /* number of per system wide sessions */ + unsigned int pfs_sys_use_dbregs; /* incremented when a system wide session uses debug regs */ + unsigned int pfs_ptrace_use_dbregs; /* incremented when a process uses debug regs */ + struct task_struct *pfs_sys_session[NR_CPUS]; /* point to task owning a system-wide session */ +#ifdef XEN +#define XENOPROF_TASK ((struct task_struct*)1) +#endif +} pfm_session_t; + +/* + * information about a PMC or PMD. + * dep_pmd[]: a bitmask of dependent PMD registers + * dep_pmc[]: a bitmask of dependent PMC registers + */ +typedef int (*pfm_reg_check_t)(struct task_struct *task, pfm_context_t *ctx, unsigned int cnum, unsigned long *val, struct pt_regs *regs); +typedef struct { + unsigned int type; + int pm_pos; + unsigned long default_value; /* power-on default value */ + unsigned long reserved_mask; /* bitmask of reserved bits */ + pfm_reg_check_t read_check; + pfm_reg_check_t write_check; + unsigned long dep_pmd[4]; + unsigned long dep_pmc[4]; +} pfm_reg_desc_t; + +/* assume cnum is a valid monitor */ +#define PMC_PM(cnum, val) (((val) >> (pmu_conf->pmc_desc[cnum].pm_pos)) & 0x1) + +/* + * This structure is initialized at boot time and contains + * a description of the PMU main characteristics. + * + * If the probe function is defined, detection is based + * on its return value: + * - 0 means recognized PMU + * - anything else means not supported + * When the probe function is not defined, then the pmu_family field + * is used and it must match the host CPU family such that: + * - cpu->family & config->pmu_family != 0 + */ +typedef struct { + unsigned long ovfl_val; /* overflow value for counters */ + + pfm_reg_desc_t *pmc_desc; /* detailed PMC register dependencies descriptions */ + pfm_reg_desc_t *pmd_desc; /* detailed PMD register dependencies descriptions */ + + unsigned int num_pmcs; /* number of PMCS: computed at init time */ + unsigned int num_pmds; /* number of PMDS: computed at init time */ + unsigned long impl_pmcs[4]; /* bitmask of implemented PMCS */ + unsigned long impl_pmds[4]; /* bitmask of implemented PMDS */ + + char *pmu_name; /* PMU family name */ + unsigned int pmu_family; /* cpuid family pattern used to identify pmu */ + unsigned int flags; /* pmu specific flags */ + unsigned int num_ibrs; /* number of IBRS: computed at init time */ + unsigned int num_dbrs; /* number of DBRS: computed at init time */ + unsigned int num_counters; /* PMC/PMD counting pairs : computed at init time */ + int (*probe)(void); /* customized probe routine */ + unsigned int use_rr_dbregs:1; /* set if debug registers used for range restriction */ +} pmu_config_t; +/* + * PMU specific flags + */ +#define PFM_PMU_IRQ_RESEND 1 /* PMU needs explicit IRQ resend */ + +/* + * debug register related type definitions + */ +typedef struct { + unsigned long ibr_mask:56; + unsigned long ibr_plm:4; + unsigned long ibr_ig:3; + unsigned long ibr_x:1; +} ibr_mask_reg_t; + +typedef struct { + unsigned long dbr_mask:56; + unsigned long dbr_plm:4; + unsigned long dbr_ig:2; + unsigned long dbr_w:1; + unsigned long dbr_r:1; +} dbr_mask_reg_t; + +typedef union { + unsigned long val; + ibr_mask_reg_t ibr; + dbr_mask_reg_t dbr; +} dbreg_t; + + +/* + * perfmon command descriptions + */ +typedef struct { + int (*cmd_func)(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs); + char *cmd_name; + int cmd_flags; + unsigned int cmd_narg; + size_t cmd_argsize; + int (*cmd_getsize)(void *arg, size_t *sz); +} pfm_cmd_desc_t; + +#define PFM_CMD_FD 0x01 /* command requires a file descriptor */ +#define PFM_CMD_ARG_READ 0x02 /* command must read argument(s) */ +#define PFM_CMD_ARG_RW 0x04 /* command must read/write argument(s) */ +#define PFM_CMD_STOP 0x08 /* command does not work on zombie context */ + + +#define PFM_CMD_NAME(cmd) pfm_cmd_tab[(cmd)].cmd_name +#define PFM_CMD_READ_ARG(cmd) (pfm_cmd_tab[(cmd)].cmd_flags & PFM_CMD_ARG_READ) +#define PFM_CMD_RW_ARG(cmd) (pfm_cmd_tab[(cmd)].cmd_flags & PFM_CMD_ARG_RW) +#define PFM_CMD_USE_FD(cmd) (pfm_cmd_tab[(cmd)].cmd_flags & PFM_CMD_FD) +#define PFM_CMD_STOPPED(cmd) (pfm_cmd_tab[(cmd)].cmd_flags & PFM_CMD_STOP) + +#define PFM_CMD_ARG_MANY -1 /* cannot be zero */ + +typedef struct { + unsigned long pfm_spurious_ovfl_intr_count; /* keep track of spurious ovfl interrupts */ + unsigned long pfm_replay_ovfl_intr_count; /* keep track of replayed ovfl interrupts */ + unsigned long pfm_ovfl_intr_count; /* keep track of ovfl interrupts */ + unsigned long pfm_ovfl_intr_cycles; /* cycles spent processing ovfl interrupts */ + unsigned long pfm_ovfl_intr_cycles_min; /* min cycles spent processing ovfl interrupts */ + unsigned long pfm_ovfl_intr_cycles_max; /* max cycles spent processing ovfl interrupts */ + unsigned long pfm_smpl_handler_calls; + unsigned long pfm_smpl_handler_cycles; + char pad[SMP_CACHE_BYTES] ____cacheline_aligned; +} pfm_stats_t; + +/* + * perfmon internal variables + */ +static pfm_stats_t pfm_stats[NR_CPUS]; +static pfm_session_t pfm_sessions; /* global sessions information */ + +#ifndef XEN +static DEFINE_SPINLOCK(pfm_alt_install_check); +#endif +static pfm_intr_handler_desc_t *pfm_alt_intr_handler; + +#ifndef XEN +static struct proc_dir_entry *perfmon_dir; +#endif +static pfm_uuid_t pfm_null_uuid = {0,}; + +static spinlock_t pfm_buffer_fmt_lock; +static LIST_HEAD(pfm_buffer_fmt_list); + +static pmu_config_t *pmu_conf; + +/* sysctl() controls */ +pfm_sysctl_t pfm_sysctl; +EXPORT_SYMBOL(pfm_sysctl); + +#ifndef XEN +static ctl_table pfm_ctl_table[]={ + {1, "debug", &pfm_sysctl.debug, sizeof(int), 0666, NULL, &proc_dointvec, NULL,}, + {2, "debug_ovfl", &pfm_sysctl.debug_ovfl, sizeof(int), 0666, NULL, &proc_dointvec, NULL,}, + {3, "fastctxsw", &pfm_sysctl.fastctxsw, sizeof(int), 0600, NULL, &proc_dointvec, NULL,}, + {4, "expert_mode", &pfm_sysctl.expert_mode, sizeof(int), 0600, NULL, &proc_dointvec, NULL,}, + { 0, }, +}; +static ctl_table pfm_sysctl_dir[] = { + {1, "perfmon", NULL, 0, 0755, pfm_ctl_table, }, + {0,}, +}; +static ctl_table pfm_sysctl_root[] = { + {1, "kernel", NULL, 0, 0755, pfm_sysctl_dir, }, + {0,}, +}; +static struct ctl_table_header *pfm_sysctl_header; + +static int pfm_context_unload(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs); +static int pfm_flush(struct file *filp); +#endif + +#define pfm_get_cpu_var(v) __ia64_per_cpu_var(v) +#define pfm_get_cpu_data(a,b) per_cpu(a, b) + +#ifndef XEN +static inline void +pfm_put_task(struct task_struct *task) +{ + if (task != current) put_task_struct(task); +} + +static inline void +pfm_set_task_notify(struct task_struct *task) +{ + struct thread_info *info; + + info = (struct thread_info *) ((char *) task + IA64_TASK_SIZE); + set_bit(TIF_NOTIFY_RESUME, &info->flags); +} + +static inline void +pfm_clear_task_notify(void) +{ + clear_thread_flag(TIF_NOTIFY_RESUME); +} + +static inline void +pfm_reserve_page(unsigned long a) +{ + SetPageReserved(vmalloc_to_page((void *)a)); +} +static inline void +pfm_unreserve_page(unsigned long a) +{ + ClearPageReserved(vmalloc_to_page((void*)a)); +} +#endif + +static inline unsigned long +pfm_protect_ctx_ctxsw(pfm_context_t *x) +{ + spin_lock(&(x)->ctx_lock); + return 0UL; +} + +static inline void +pfm_unprotect_ctx_ctxsw(pfm_context_t *x, unsigned long f) +{ + spin_unlock(&(x)->ctx_lock); +} + +#ifndef XEN +static inline unsigned int +pfm_do_munmap(struct mm_struct *mm, unsigned long addr, size_t len, int acct) +{ + return do_munmap(mm, addr, len); +} + +static inline unsigned long +pfm_get_unmapped_area(struct file *file, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags, unsigned long exec) +{ + return get_unmapped_area(file, addr, len, pgoff, flags); +} + + +static struct super_block * +pfmfs_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) +{ + return get_sb_pseudo(fs_type, "pfm:", NULL, PFMFS_MAGIC); +} + +static struct file_system_type pfm_fs_type = { + .name = "pfmfs", + .get_sb = pfmfs_get_sb, + .kill_sb = kill_anon_super, +}; +#endif + +DEFINE_PER_CPU(unsigned long, pfm_syst_info); +DEFINE_PER_CPU(struct task_struct *, pmu_owner); +DEFINE_PER_CPU(pfm_context_t *, pmu_ctx); +DEFINE_PER_CPU(unsigned long, pmu_activation_number); +#ifndef XEN +EXPORT_PER_CPU_SYMBOL_GPL(pfm_syst_info); + + +/* forward declaration */ +static struct file_operations pfm_file_ops; +#endif + +/* + * forward declarations + */ +#ifndef CONFIG_SMP +static void pfm_lazy_save_regs (struct task_struct *ta); +#endif + +void dump_pmu_state(const char *); +static int pfm_write_ibr_dbr(int mode, pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs); + +#include "perfmon_itanium.h" +#include "perfmon_mckinley.h" +#include "perfmon_montecito.h" +#include "perfmon_generic.h" + +static pmu_config_t *pmu_confs[]={ + &pmu_conf_mont, + &pmu_conf_mck, + &pmu_conf_ita, + &pmu_conf_gen, /* must be last */ + NULL +}; + + +#ifndef XEN +static int pfm_end_notify_user(pfm_context_t *ctx); +#endif + +static inline void +pfm_clear_psr_pp(void) +{ + ia64_rsm(IA64_PSR_PP); + ia64_srlz_i(); +} + +static inline void +pfm_set_psr_pp(void) +{ + ia64_ssm(IA64_PSR_PP); + ia64_srlz_i(); +} + +static inline void +pfm_clear_psr_up(void) +{ + ia64_rsm(IA64_PSR_UP); + ia64_srlz_i(); +} + +static inline void +pfm_set_psr_up(void) +{ + ia64_ssm(IA64_PSR_UP); + ia64_srlz_i(); +} + +static inline unsigned long +pfm_get_psr(void) +{ + unsigned long tmp; + tmp = ia64_getreg(_IA64_REG_PSR); + ia64_srlz_i(); + return tmp; +} + +static inline void +pfm_set_psr_l(unsigned long val) +{ + ia64_setreg(_IA64_REG_PSR_L, val); + ia64_srlz_i(); +} + +static inline void +pfm_freeze_pmu(void) +{ + ia64_set_pmc(0,1UL); + ia64_srlz_d(); +} + +static inline void +pfm_unfreeze_pmu(void) +{ + ia64_set_pmc(0,0UL); + ia64_srlz_d(); +} + +static inline void +pfm_restore_ibrs(unsigned long *ibrs, unsigned int nibrs) +{ + int i; + + for (i=0; i < nibrs; i++) { + ia64_set_ibr(i, ibrs[i]); + ia64_dv_serialize_instruction(); + } + ia64_srlz_i(); +} + +static inline void +pfm_restore_dbrs(unsigned long *dbrs, unsigned int ndbrs) +{ + int i; + + for (i=0; i < ndbrs; i++) { + ia64_set_dbr(i, dbrs[i]); + ia64_dv_serialize_data(); + } + ia64_srlz_d(); +} + +/* + * PMD[i] must be a counter. no check is made + */ +static inline unsigned long +pfm_read_soft_counter(pfm_context_t *ctx, int i) +{ + return ctx->ctx_pmds[i].val + (ia64_get_pmd(i) & pmu_conf->ovfl_val); +} + +/* + * PMD[i] must be a counter. no check is made + */ +static inline void +pfm_write_soft_counter(pfm_context_t *ctx, int i, unsigned long val) +{ + unsigned long ovfl_val = pmu_conf->ovfl_val; + + ctx->ctx_pmds[i].val = val & ~ovfl_val; + /* + * writing to unimplemented part is ignore, so we do not need to + * mask off top part + */ + ia64_set_pmd(i, val & ovfl_val); +} + +#ifndef XEN +static pfm_msg_t * +pfm_get_new_msg(pfm_context_t *ctx) +{ + int idx, next; + + next = (ctx->ctx_msgq_tail+1) % PFM_MAX_MSGS; + + DPRINT(("ctx_fd=%p head=%d tail=%d\n", ctx, ctx->ctx_msgq_head, ctx->ctx_msgq_tail)); + if (next == ctx->ctx_msgq_head) return NULL; + + idx = ctx->ctx_msgq_tail; + ctx->ctx_msgq_tail = next; + + DPRINT(("ctx=%p head=%d tail=%d msg=%d\n", ctx, ctx->ctx_msgq_head, ctx->ctx_msgq_tail, idx)); + + return ctx->ctx_msgq+idx; +} + +static pfm_msg_t * +pfm_get_next_msg(pfm_context_t *ctx) +{ + pfm_msg_t *msg; + + DPRINT(("ctx=%p head=%d tail=%d\n", ctx, ctx->ctx_msgq_head, ctx->ctx_msgq_tail)); + + if (PFM_CTXQ_EMPTY(ctx)) return NULL; + + /* + * get oldest message + */ + msg = ctx->ctx_msgq+ctx->ctx_msgq_head; + + /* + * and move forward + */ + ctx->ctx_msgq_head = (ctx->ctx_msgq_head+1) % PFM_MAX_MSGS; + + DPRINT(("ctx=%p head=%d tail=%d type=%d\n", ctx, ctx->ctx_msgq_head, ctx->ctx_msgq_tail, msg->pfm_gen_msg.msg_type)); + + return msg; +} + +static void +pfm_reset_msgq(pfm_context_t *ctx) +{ + ctx->ctx_msgq_head = ctx->ctx_msgq_tail = 0; + DPRINT(("ctx=%p msgq reset\n", ctx)); +} + +static void * +pfm_rvmalloc(unsigned long size) +{ + void *mem; + unsigned long addr; + + size = PAGE_ALIGN(size); + mem = vmalloc(size); + if (mem) { + //printk("perfmon: CPU%d pfm_rvmalloc(%ld)=%p\n", smp_processor_id(), size, mem); + memset(mem, 0, size); + addr = (unsigned long)mem; + while (size > 0) { + pfm_reserve_page(addr); + addr+=PAGE_SIZE; + size-=PAGE_SIZE; + } + } + return mem; +} + +static void +pfm_rvfree(void *mem, unsigned long size) +{ + unsigned long addr; + + if (mem) { + DPRINT(("freeing physical buffer @%p size=%lu\n", mem, size)); + addr = (unsigned long) mem; + while ((long) size > 0) { + pfm_unreserve_page(addr); + addr+=PAGE_SIZE; + size-=PAGE_SIZE; + } + vfree(mem); + } + return; +} +#endif + +static pfm_context_t * +pfm_context_alloc(void) +{ + pfm_context_t *ctx; + + /* + * allocate context descriptor + * must be able to free with interrupts disabled + */ + ctx = kmalloc(sizeof(pfm_context_t), GFP_KERNEL); + if (ctx) { + memset(ctx, 0, sizeof(pfm_context_t)); + DPRINT(("alloc ctx @%p\n", ctx)); + } + return ctx; +} + +static void +pfm_context_free(pfm_context_t *ctx) +{ + if (ctx) { + DPRINT(("free ctx @%p\n", ctx)); + kfree(ctx); + } +} + +#ifndef XEN +static void +pfm_mask_monitoring(struct task_struct *task) +{ + pfm_context_t *ctx = PFM_GET_CTX(task); + struct thread_struct *th = &task->thread; + unsigned long mask, val, ovfl_mask; + int i; + + DPRINT_ovfl(("masking monitoring for [%d]\n", task->pid)); + + ovfl_mask = pmu_conf->ovfl_val; + /* + * monitoring can only be masked as a result of a valid + * counter overflow. In UP, it means that the PMU still + * has an owner. Note that the owner can be different + * from the current task. However the PMU state belongs + * to the owner. + * In SMP, a valid overflow only happens when task is + * current. Therefore if we come here, we know that + * the PMU state belongs to the current task, therefore + * we can access the live registers. + * + * So in both cases, the live register contains the owner's + * state. We can ONLY touch the PMU registers and NOT the PSR. + * + * As a consequence to this call, the thread->pmds[] array + * contains stale information which must be ignored + * when context is reloaded AND monitoring is active (see + * pfm_restart). + */ + mask = ctx->ctx_used_pmds[0]; + for (i = 0; mask; i++, mask>>=1) { + /* skip non used pmds */ + if ((mask & 0x1) == 0) continue; + val = ia64_get_pmd(i); + + if (PMD_IS_COUNTING(i)) { + /* + * we rebuild the full 64 bit value of the counter + */ + ctx->ctx_pmds[i].val += (val & ovfl_mask); + } else { + ctx->ctx_pmds[i].val = val; + } + DPRINT_ovfl(("pmd[%d]=0x%lx hw_pmd=0x%lx\n", + i, + ctx->ctx_pmds[i].val, + val & ovfl_mask)); + } + /* + * mask monitoring by setting the privilege level to 0 + * we cannot use psr.pp/psr.up for this, it is controlled by + * the user + * + * if task is current, modify actual registers, otherwise modify + * thread save state, i.e., what will be restored in pfm_load_regs() + */ + mask = ctx->ctx_used_monitors[0] >> PMU_FIRST_COUNTER; + for(i= PMU_FIRST_COUNTER; mask; i++, mask>>=1) { + if ((mask & 0x1) == 0UL) continue; + ia64_set_pmc(i, th->pmcs[i] & ~0xfUL); + th->pmcs[i] &= ~0xfUL; + DPRINT_ovfl(("pmc[%d]=0x%lx\n", i, th->pmcs[i])); + } + /* + * make all of this visible + */ + ia64_srlz_d(); +} + +/* + * must always be done with task == current + * + * context must be in MASKED state when calling + */ +static void +pfm_restore_monitoring(struct task_struct *task) +{ + pfm_context_t *ctx = PFM_GET_CTX(task); + struct thread_struct *th = &task->thread; + unsigned long mask, ovfl_mask; + unsigned long psr, val; + int i, is_system; + + is_system = ctx->ctx_fl_system; + ovfl_mask = pmu_conf->ovfl_val; + + if (task != current) { + printk(KERN_ERR "perfmon.%d: invalid task[%d] current[%d]\n", __LINE__, task->pid, current->pid); + return; + } + if (ctx->ctx_state != PFM_CTX_MASKED) { + printk(KERN_ERR "perfmon.%d: task[%d] current[%d] invalid state=%d\n", __LINE__, + task->pid, current->pid, ctx->ctx_state); + return; + } + psr = pfm_get_psr(); + /* + * monitoring is masked via the PMC. + * As we restore their value, we do not want each counter to + * restart right away. We stop monitoring using the PSR, + * restore the PMC (and PMD) and then re-establish the psr + * as it was. Note that there can be no pending overflow at + * this point, because monitoring was MASKED. + * + * system-wide session are pinned and self-monitoring + */ + if (is_system && (PFM_CPUINFO_GET() & PFM_CPUINFO_DCR_PP)) { + /* disable dcr pp */ + ia64_setreg(_IA64_REG_CR_DCR, ia64_getreg(_IA64_REG_CR_DCR) & ~IA64_DCR_PP); + pfm_clear_psr_pp(); + } else { + pfm_clear_psr_up(); + } + /* + * first, we restore the PMD + */ + mask = ctx->ctx_used_pmds[0]; + for (i = 0; mask; i++, mask>>=1) { + /* skip non used pmds */ + if ((mask & 0x1) == 0) continue; + + if (PMD_IS_COUNTING(i)) { + /* + * we split the 64bit value according to + * counter width + */ + val = ctx->ctx_pmds[i].val & ovfl_mask; + ctx->ctx_pmds[i].val &= ~ovfl_mask; + } else { + val = ctx->ctx_pmds[i].val; + } + ia64_set_pmd(i, val); + + DPRINT(("pmd[%d]=0x%lx hw_pmd=0x%lx\n", + i, + ctx->ctx_pmds[i].val, + val)); + } + /* + * restore the PMCs + */ + mask = ctx->ctx_used_monitors[0] >> PMU_FIRST_COUNTER; + for(i= PMU_FIRST_COUNTER; mask; i++, mask>>=1) { + if ((mask & 0x1) == 0UL) continue; + th->pmcs[i] = ctx->ctx_pmcs[i]; + ia64_set_pmc(i, th->pmcs[i]); + DPRINT(("[%d] pmc[%d]=0x%lx\n", task->pid, i, th->pmcs[i])); + } + ia64_srlz_d(); + + /* + * must restore DBR/IBR because could be modified while masked + * XXX: need to optimize + */ + if (ctx->ctx_fl_using_dbreg) { + pfm_restore_ibrs(ctx->ctx_ibrs, pmu_conf->num_ibrs); + pfm_restore_dbrs(ctx->ctx_dbrs, pmu_conf->num_dbrs); + } + + /* + * now restore PSR + */ + if (is_system && (PFM_CPUINFO_GET() & PFM_CPUINFO_DCR_PP)) { + /* enable dcr pp */ + ia64_setreg(_IA64_REG_CR_DCR, ia64_getreg(_IA64_REG_CR_DCR) | IA64_DCR_PP); + ia64_srlz_i(); + } + pfm_set_psr_l(psr); +} +#endif + +static inline void +pfm_save_pmds(unsigned long *pmds, unsigned long mask) +{ + int i; + + ia64_srlz_d(); + + for (i=0; mask; i++, mask>>=1) { + if (mask & 0x1) pmds[i] = ia64_get_pmd(i); + } +} + +#ifndef XEN +/* + * reload from thread state (used for ctxw only) + */ +static inline void +pfm_restore_pmds(unsigned long *pmds, unsigned long mask) +{ + int i; + unsigned long val, ovfl_val = pmu_conf->ovfl_val; + + for (i=0; mask; i++, mask>>=1) { + if ((mask & 0x1) == 0) continue; + val = PMD_IS_COUNTING(i) ? pmds[i] & ovfl_val : pmds[i]; + ia64_set_pmd(i, val); + } + ia64_srlz_d(); +} + +/* + * propagate PMD from context to thread-state + */ +static inline void +pfm_copy_pmds(struct task_struct *task, pfm_context_t *ctx) +{ + struct thread_struct *thread = &task->thread; + unsigned long ovfl_val = pmu_conf->ovfl_val; + unsigned long mask = ctx->ctx_all_pmds[0]; + unsigned long val; + int i; + + DPRINT(("mask=0x%lx\n", mask)); + + for (i=0; mask; i++, mask>>=1) { + + val = ctx->ctx_pmds[i].val; + + /* + * We break up the 64 bit value into 2 pieces + * the lower bits go to the machine state in the + * thread (will be reloaded on ctxsw in). + * The upper part stays in the soft-counter. + */ + if (PMD_IS_COUNTING(i)) { + ctx->ctx_pmds[i].val = val & ~ovfl_val; + val &= ovfl_val; + } + thread->pmds[i] = val; + + DPRINT(("pmd[%d]=0x%lx soft_val=0x%lx\n", + i, + thread->pmds[i], + ctx->ctx_pmds[i].val)); + } +} +#else +static inline void +xenpfm_restore_pmds(pfm_context_t* ctx) +{ + int i; + unsigned long ovfl_val = pmu_conf->ovfl_val; + unsigned long mask = ctx->ctx_all_pmds[0]; + unsigned long val; + + for (i = 0; mask; i++, mask >>= 1) { + if ((mask & 0x1) == 0) + continue; + + val = ctx->ctx_pmds[i].val; + /* + * We break up the 64 bit value into 2 pieces + * the lower bits go to the machine state in the + * thread (will be reloaded on ctxsw in). + * The upper part stays in the soft-counter. + */ + if (PMD_IS_COUNTING(i)) { + ctx->ctx_pmds[i].val = val & ~ovfl_val; + val &= ovfl_val; + } + ia64_set_pmd(i, val); + } + ia64_srlz_d(); +} +#endif + +#ifndef XEN +/* + * propagate PMC from context to thread-state + */ +static inline void +pfm_copy_pmcs(struct task_struct *task, pfm_context_t *ctx) +{ + struct thread_struct *thread = &task->thread; + unsigned long mask = ctx->ctx_all_pmcs[0]; + int i; + + DPRINT(("mask=0x%lx\n", mask)); + + for (i=0; mask; i++, mask>>=1) { + /* masking 0 with ovfl_val yields 0 */ + thread->pmcs[i] = ctx->ctx_pmcs[i]; + DPRINT(("pmc[%d]=0x%lx\n", i, thread->pmcs[i])); + } +} + + + +static inline void +pfm_restore_pmcs(unsigned long *pmcs, unsigned long mask) +{ + int i; + + for (i=0; mask; i++, mask>>=1) { + if ((mask & 0x1) == 0) continue; + ia64_set_pmc(i, pmcs[i]); + } + ia64_srlz_d(); +} +#else +static inline void +xenpfm_restore_pmcs(pfm_context_t* ctx) +{ + int i; + unsigned long mask = ctx->ctx_all_pmcs[0]; + + for (i = 0; mask; i++, mask >>= 1) { + if ((mask & 0x1) == 0) + continue; + ia64_set_pmc(i, ctx->ctx_pmcs[i]); + DPRINT(("pmc[%d]=0x%lx\n", i, ctx->ctx_pmcs[i])); + } + ia64_srlz_d(); + +} +#endif + +static inline int +pfm_uuid_cmp(pfm_uuid_t a, pfm_uuid_t b) +{ + return memcmp(a, b, sizeof(pfm_uuid_t)); +} + +static inline int +pfm_buf_fmt_exit(pfm_buffer_fmt_t *fmt, struct task_struct *task, void *buf, struct pt_regs *regs) +{ + int ret = 0; + if (fmt->fmt_exit) ret = (*fmt->fmt_exit)(task, buf, regs); + return ret; +} + +static inline int +pfm_buf_fmt_getsize(pfm_buffer_fmt_t *fmt, struct task_struct *task, unsigned int flags, int cpu, void *arg, unsigned long *size) +{ + int ret = 0; + if (fmt->fmt_getsize) ret = (*fmt->fmt_getsize)(task, flags, cpu, arg, size); + return ret; +} + + +static inline int +pfm_buf_fmt_validate(pfm_buffer_fmt_t *fmt, struct task_struct *task, unsigned int flags, + int cpu, void *arg) +{ + int ret = 0; + if (fmt->fmt_validate) ret = (*fmt->fmt_validate)(task, flags, cpu, arg); + return ret; +} + +static inline int +pfm_buf_fmt_init(pfm_buffer_fmt_t *fmt, struct task_struct *task, void *buf, unsigned int flags, + int cpu, void *arg) +{ + int ret = 0; + if (fmt->fmt_init) ret = (*fmt->fmt_init)(task, buf, flags, cpu, arg); + return ret; +} + +static inline int +pfm_buf_fmt_restart(pfm_buffer_fmt_t *fmt, struct task_struct *task, pfm_ovfl_ctrl_t *ctrl, void *buf, struct pt_regs *regs) +{ + int ret = 0; + if (fmt->fmt_restart) ret = (*fmt->fmt_restart)(task, ctrl, buf, regs); + return ret; +} + +static inline int +pfm_buf_fmt_restart_active(pfm_buffer_fmt_t *fmt, struct task_struct *task, pfm_ovfl_ctrl_t *ctrl, void *buf, struct pt_regs *regs) +{ + int ret = 0; + if (fmt->fmt_restart_active) ret = (*fmt->fmt_restart_active)(task, ctrl, buf, regs); + return ret; +} + +static pfm_buffer_fmt_t * +__pfm_find_buffer_fmt(pfm_uuid_t uuid) +{ + struct list_head * pos; + pfm_buffer_fmt_t * entry; + + list_for_each(pos, &pfm_buffer_fmt_list) { + entry = list_entry(pos, pfm_buffer_fmt_t, fmt_list); + if (pfm_uuid_cmp(uuid, entry->fmt_uuid) == 0) + return entry; + } + return NULL; +} + +/* + * find a buffer format based on its uuid + */ +static pfm_buffer_fmt_t * +pfm_find_buffer_fmt(pfm_uuid_t uuid) +{ + pfm_buffer_fmt_t * fmt; + spin_lock(&pfm_buffer_fmt_lock); + fmt = __pfm_find_buffer_fmt(uuid); + spin_unlock(&pfm_buffer_fmt_lock); + return fmt; +} + +int +pfm_register_buffer_fmt(pfm_buffer_fmt_t *fmt) +{ + int ret = 0; + + /* some sanity checks */ + if (fmt == NULL || fmt->fmt_name == NULL) return -EINVAL; + + /* we need at least a handler */ + if (fmt->fmt_handler == NULL) return -EINVAL; + + /* + * XXX: need check validity of fmt_arg_size + */ + + spin_lock(&pfm_buffer_fmt_lock); + + if (__pfm_find_buffer_fmt(fmt->fmt_uuid)) { + printk(KERN_ERR "perfmon: duplicate sampling format: %s\n", fmt->fmt_name); + ret = -EBUSY; + goto out; + } + list_add(&fmt->fmt_list, &pfm_buffer_fmt_list); + printk(KERN_INFO "perfmon: added sampling format %s\n", fmt->fmt_name); + +out: + spin_unlock(&pfm_buffer_fmt_lock); + return ret; +} +EXPORT_SYMBOL(pfm_register_buffer_fmt); + +int +pfm_unregister_buffer_fmt(pfm_uuid_t uuid) +{ + pfm_buffer_fmt_t *fmt; + int ret = 0; + + spin_lock(&pfm_buffer_fmt_lock); + + fmt = __pfm_find_buffer_fmt(uuid); + if (!fmt) { + printk(KERN_ERR "perfmon: cannot unregister format, not found\n"); + ret = -EINVAL; + goto out; + } + list_del_init(&fmt->fmt_list); + printk(KERN_INFO "perfmon: removed sampling format: %s\n", fmt->fmt_name); + +out: + spin_unlock(&pfm_buffer_fmt_lock); + return ret; + +} +EXPORT_SYMBOL(pfm_unregister_buffer_fmt); + +extern void update_pal_halt_status(int); + +static int +pfm_reserve_session(struct task_struct *task, int is_syswide, unsigned int cpu) +{ + unsigned long flags; + /* + * validy checks on cpu_mask have been done upstream + */ + LOCK_PFS(flags); + + DPRINT(("in sys_sessions=%u task_sessions=%u dbregs=%u syswide=%d cpu=%u\n", + pfm_sessions.pfs_sys_sessions, + pfm_sessions.pfs_task_sessions, + pfm_sessions.pfs_sys_use_dbregs, + is_syswide, + cpu)); + + if (is_syswide) { + /* + * cannot mix system wide and per-task sessions + */ + if (pfm_sessions.pfs_task_sessions > 0UL) { + DPRINT(("system wide not possible, %u conflicting task_sessions\n", + pfm_sessions.pfs_task_sessions)); + goto abort; + } + + if (pfm_sessions.pfs_sys_session[cpu]) goto error_conflict; + + DPRINT(("reserving system wide session on CPU%u currently on CPU%u\n", cpu, smp_processor_id())); + +#ifndef XEN + pfm_sessions.pfs_sys_session[cpu] = task; +#else + pfm_sessions.pfs_sys_session[cpu] = XENOPROF_TASK; +#endif + + pfm_sessions.pfs_sys_sessions++ ; + + } else { + if (pfm_sessions.pfs_sys_sessions) goto abort; + pfm_sessions.pfs_task_sessions++; + } + + DPRINT(("out sys_sessions=%u task_sessions=%u dbregs=%u syswide=%d cpu=%u\n", + pfm_sessions.pfs_sys_sessions, + pfm_sessions.pfs_task_sessions, + pfm_sessions.pfs_sys_use_dbregs, + is_syswide, + cpu)); + + /* + * disable default_idle() to go to PAL_HALT + */ + update_pal_halt_status(0); + + UNLOCK_PFS(flags); + + return 0; + +error_conflict: + DPRINT(("system wide not possible, conflicting session [%d] on CPU%d\n", +#ifndef XEN + pfm_sessions.pfs_sys_session[cpu]->pid, +#else + -1, +#endif + cpu)); +abort: + UNLOCK_PFS(flags); + + return -EBUSY; + +} + +static int +pfm_unreserve_session(pfm_context_t *ctx, int is_syswide, unsigned int cpu) +{ + unsigned long flags; + /* + * validy checks on cpu_mask have been done upstream + */ + LOCK_PFS(flags); + + DPRINT(("in sys_sessions=%u task_sessions=%u dbregs=%u syswide=%d cpu=%u\n", + pfm_sessions.pfs_sys_sessions, + pfm_sessions.pfs_task_sessions, + pfm_sessions.pfs_sys_use_dbregs, + is_syswide, + cpu)); + + + if (is_syswide) { + pfm_sessions.pfs_sys_session[cpu] = NULL; + /* + * would not work with perfmon+more than one bit in cpu_mask + */ + if (ctx && ctx->ctx_fl_using_dbreg) { + if (pfm_sessions.pfs_sys_use_dbregs == 0) { + printk(KERN_ERR "perfmon: invalid release for ctx %p sys_use_dbregs=0\n", ctx); + } else { + pfm_sessions.pfs_sys_use_dbregs--; + } + } + pfm_sessions.pfs_sys_sessions--; + } else { + pfm_sessions.pfs_task_sessions--; + } + DPRINT(("out sys_sessions=%u task_sessions=%u dbregs=%u syswide=%d cpu=%u\n", + pfm_sessions.pfs_sys_sessions, + pfm_sessions.pfs_task_sessions, + pfm_sessions.pfs_sys_use_dbregs, + is_syswide, + cpu)); + + /* + * if possible, enable default_idle() to go into PAL_HALT + */ + if (pfm_sessions.pfs_task_sessions == 0 && pfm_sessions.pfs_sys_sessions == 0) + update_pal_halt_status(1); + + UNLOCK_PFS(flags); + + return 0; +} + +#ifndef XEN +/* + * removes virtual mapping of the sampling buffer. + * IMPORTANT: cannot be called with interrupts disable, e.g. inside + * a PROTECT_CTX() section. + */ +static int +pfm_remove_smpl_mapping(struct task_struct *task, void *vaddr, unsigned long size) +{ + int r; + + /* sanity checks */ + if (task->mm == NULL || size == 0UL || vaddr == NULL) { + printk(KERN_ERR "perfmon: pfm_remove_smpl_mapping [%d] invalid context mm=%p\n", task->pid, task->mm); + return -EINVAL; + } + + DPRINT(("smpl_vaddr=%p size=%lu\n", vaddr, size)); + + /* + * does the actual unmapping + */ + down_write(&task->mm->mmap_sem); + + DPRINT(("down_write done smpl_vaddr=%p size=%lu\n", vaddr, size)); + + r = pfm_do_munmap(task->mm, (unsigned long)vaddr, size, 0); + + up_write(&task->mm->mmap_sem); + if (r !=0) { + printk(KERN_ERR "perfmon: [%d] unable to unmap sampling buffer @%p size=%lu\n", task->pid, vaddr, size); + } + + DPRINT(("do_unmap(%p, %lu)=%d\n", vaddr, size, r)); + + return 0; +} +#endif + +/* + * free actual physical storage used by sampling buffer + */ +#if 0 +static int +pfm_free_smpl_buffer(pfm_context_t *ctx) +{ + pfm_buffer_fmt_t *fmt; + + if (ctx->ctx_smpl_hdr == NULL) goto invalid_free; + + /* + * we won't use the buffer format anymore + */ + fmt = ctx->ctx_buf_fmt; + + DPRINT(("sampling buffer @%p size %lu vaddr=%p\n", + ctx->ctx_smpl_hdr, + ctx->ctx_smpl_size, + ctx->ctx_smpl_vaddr)); + + pfm_buf_fmt_exit(fmt, current, NULL, NULL); + + /* + * free the buffer + */ + pfm_rvfree(ctx->ctx_smpl_hdr, ctx->ctx_smpl_size); + + ctx->ctx_smpl_hdr = NULL; + ctx->ctx_smpl_size = 0UL; + + return 0; + +invalid_free: + printk(KERN_ERR "perfmon: pfm_free_smpl_buffer [%d] no buffer\n", current->pid); + return -EINVAL; +} +#endif + +static inline void +pfm_exit_smpl_buffer(pfm_buffer_fmt_t *fmt) +{ + if (fmt == NULL) return; + + pfm_buf_fmt_exit(fmt, current, NULL, NULL); + +} + +#ifndef XEN +/* + * pfmfs should _never_ be mounted by userland - too much of security hassle, + * no real gain from having the whole whorehouse mounted. So we don't need + * any operations on the root directory. However, we need a non-trivial + * d_name - pfm: will go nicely and kill the special-casing in procfs. + */ +static struct vfsmount *pfmfs_mnt; + +static int __init +init_pfm_fs(void) +{ + int err = register_filesystem(&pfm_fs_type); + if (!err) { + pfmfs_mnt = kern_mount(&pfm_fs_type); + err = PTR_ERR(pfmfs_mnt); + if (IS_ERR(pfmfs_mnt)) + unregister_filesystem(&pfm_fs_type); + else + err = 0; + } + return err; +} + +static void __exit +exit_pfm_fs(void) +{ + unregister_filesystem(&pfm_fs_type); + mntput(pfmfs_mnt); +} + +static ssize_t +pfm_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos) +{ + pfm_context_t *ctx; + pfm_msg_t *msg; + ssize_t ret; + unsigned long flags; + DECLARE_WAITQUEUE(wait, current); + if (PFM_IS_FILE(filp) == 0) { + printk(KERN_ERR "perfmon: pfm_poll: bad magic [%d]\n", current->pid); + return -EINVAL; + } + + ctx = (pfm_context_t *)filp->private_data; + if (ctx == NULL) { + printk(KERN_ERR "perfmon: pfm_read: NULL ctx [%d]\n", current->pid); + return -EINVAL; + } + + /* + * check even when there is no message + */ + if (size < sizeof(pfm_msg_t)) { + DPRINT(("message is too small ctx=%p (>=%ld)\n", ctx, sizeof(pfm_msg_t))); + return -EINVAL; + } + + PROTECT_CTX(ctx, flags); + + /* + * put ourselves on the wait queue + */ + add_wait_queue(&ctx->ctx_msgq_wait, &wait); + + + for(;;) { + /* + * check wait queue + */ + + set_current_state(TASK_INTERRUPTIBLE); + + DPRINT(("head=%d tail=%d\n", ctx->ctx_msgq_head, ctx->ctx_msgq_tail)); + + ret = 0; + if(PFM_CTXQ_EMPTY(ctx) == 0) break; + + UNPROTECT_CTX(ctx, flags); + + /* + * check non-blocking read + */ + ret = -EAGAIN; + if(filp->f_flags & O_NONBLOCK) break; + + /* + * check pending signals + */ + if(signal_pending(current)) { + ret = -EINTR; + break; + } + /* + * no message, so wait + */ + schedule(); + + PROTECT_CTX(ctx, flags); + } + DPRINT(("[%d] back to running ret=%ld\n", current->pid, ret)); + set_current_state(TASK_RUNNING); + remove_wait_queue(&ctx->ctx_msgq_wait, &wait); + + if (ret < 0) goto abort; + + ret = -EINVAL; + msg = pfm_get_next_msg(ctx); + if (msg == NULL) { + printk(KERN_ERR "perfmon: pfm_read no msg for ctx=%p [%d]\n", ctx, current->pid); + goto abort_locked; + } + + DPRINT(("fd=%d type=%d\n", msg->pfm_gen_msg.msg_ctx_fd, msg->pfm_gen_msg.msg_type)); + + ret = -EFAULT; + if(copy_to_user(buf, msg, sizeof(pfm_msg_t)) == 0) ret = sizeof(pfm_msg_t); + +abort_locked: + UNPROTECT_CTX(ctx, flags); +abort: + return ret; +} + +static ssize_t +pfm_write(struct file *file, const char __user *ubuf, + size_t size, loff_t *ppos) +{ + DPRINT(("pfm_write called\n")); + return -EINVAL; +} + +static unsigned int +pfm_poll(struct file *filp, poll_table * wait) +{ + pfm_context_t *ctx; + unsigned long flags; + unsigned int mask = 0; + + if (PFM_IS_FILE(filp) == 0) { + printk(KERN_ERR "perfmon: pfm_poll: bad magic [%d]\n", current->pid); + return 0; + } + + ctx = (pfm_context_t *)filp->private_data; + if (ctx == NULL) { + printk(KERN_ERR "perfmon: pfm_poll: NULL ctx [%d]\n", current->pid); + return 0; + } + + + DPRINT(("pfm_poll ctx_fd=%d before poll_wait\n", ctx->ctx_fd)); + + poll_wait(filp, &ctx->ctx_msgq_wait, wait); + + PROTECT_CTX(ctx, flags); + + if (PFM_CTXQ_EMPTY(ctx) == 0) + mask = POLLIN | POLLRDNORM; + + UNPROTECT_CTX(ctx, flags); + + DPRINT(("pfm_poll ctx_fd=%d mask=0x%x\n", ctx->ctx_fd, mask)); + + return mask; +} + +static int +pfm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + DPRINT(("pfm_ioctl called\n")); + return -EINVAL; +} + +/* + * interrupt cannot be masked when coming here + */ +static inline int +pfm_do_fasync(int fd, struct file *filp, pfm_context_t *ctx, int on) +{ + int ret; + + ret = fasync_helper (fd, filp, on, &ctx->ctx_async_queue); + + DPRINT(("pfm_fasync called by [%d] on ctx_fd=%d on=%d async_queue=%p ret=%d\n", + current->pid, + fd, + on, + ctx->ctx_async_queue, ret)); + + return ret; +} + +static int +pfm_fasync(int fd, struct file *filp, int on) +{ + pfm_context_t *ctx; + int ret; + + if (PFM_IS_FILE(filp) == 0) { + printk(KERN_ERR "perfmon: pfm_fasync bad magic [%d]\n", current->pid); + return -EBADF; + } + + ctx = (pfm_context_t *)filp->private_data; + if (ctx == NULL) { + printk(KERN_ERR "perfmon: pfm_fasync NULL ctx [%d]\n", current->pid); + return -EBADF; + } + /* + * we cannot mask interrupts during this call because this may + * may go to sleep if memory is not readily avalaible. + * + * We are protected from the conetxt disappearing by the get_fd()/put_fd() + * done in caller. Serialization of this function is ensured by caller. + */ + ret = pfm_do_fasync(fd, filp, ctx, on); + + + DPRINT(("pfm_fasync called on ctx_fd=%d on=%d async_queue=%p ret=%d\n", + fd, + on, + ctx->ctx_async_queue, ret)); + + return ret; +} + +#ifdef CONFIG_SMP +/* + * this function is exclusively called from pfm_close(). + * The context is not protected at that time, nor are interrupts + * on the remote CPU. That's necessary to avoid deadlocks. + */ +static void +pfm_syswide_force_stop(void *info) +{ + pfm_context_t *ctx = (pfm_context_t *)info; + struct pt_regs *regs = task_pt_regs(current); + struct task_struct *owner; + unsigned long flags; + int ret; + + if (ctx->ctx_cpu != smp_processor_id()) { + printk(KERN_ERR "perfmon: pfm_syswide_force_stop for CPU%d but on CPU%d\n", + ctx->ctx_cpu, + smp_processor_id()); + return; + } + owner = GET_PMU_OWNER(); + if (owner != ctx->ctx_task) { + printk(KERN_ERR "perfmon: pfm_syswide_force_stop CPU%d unexpected owner [%d] instead of [%d]\n", + smp_processor_id(), + owner->pid, ctx->ctx_task->pid); + return; + } + if (GET_PMU_CTX() != ctx) { + printk(KERN_ERR "perfmon: pfm_syswide_force_stop CPU%d unexpected ctx %p instead of %p\n", + smp_processor_id(), + GET_PMU_CTX(), ctx); + return; + } + + DPRINT(("on CPU%d forcing system wide stop for [%d]\n", smp_processor_id(), ctx->ctx_task->pid)); + /* + * the context is already protected in pfm_close(), we simply + * need to mask interrupts to avoid a PMU interrupt race on + * this CPU + */ + local_irq_save(flags); + + ret = pfm_context_unload(ctx, NULL, 0, regs); + if (ret) { + DPRINT(("context_unload returned %d\n", ret)); + } + + /* + * unmask interrupts, PMU interrupts are now spurious here + */ + local_irq_restore(flags); +} + +static void +pfm_syswide_cleanup_other_cpu(pfm_context_t *ctx) +{ + int ret; + + DPRINT(("calling CPU%d for cleanup\n", ctx->ctx_cpu)); + ret = smp_call_function_single(ctx->ctx_cpu, pfm_syswide_force_stop, ctx, 0, 1); + DPRINT(("called CPU%d for cleanup ret=%d\n", ctx->ctx_cpu, ret)); +} +#endif /* CONFIG_SMP */ + +/* + * called for each close(). Partially free resources. + * When caller is self-monitoring, the context is unloaded. + */ +static int +pfm_flush(struct file *filp) +{ + pfm_context_t *ctx; + struct task_struct *task; + struct pt_regs *regs; + unsigned long flags; + unsigned long smpl_buf_size = 0UL; + void *smpl_buf_vaddr = NULL; + int state, is_system; + + if (PFM_IS_FILE(filp) == 0) { + DPRINT(("bad magic for\n")); + return -EBADF; + } + + ctx = (pfm_context_t *)filp->private_data; + if (ctx == NULL) { + printk(KERN_ERR "perfmon: pfm_flush: NULL ctx [%d]\n", current->pid); + return -EBADF; + } + + /* + * remove our file from the async queue, if we use this mode. + * This can be done without the context being protected. We come + * here when the context has become unreacheable by other tasks. + * + * We may still have active monitoring at this point and we may + * end up in pfm_overflow_handler(). However, fasync_helper() + * operates with interrupts disabled and it cleans up the + * queue. If the PMU handler is called prior to entering + * fasync_helper() then it will send a signal. If it is + * invoked after, it will find an empty queue and no + * signal will be sent. In both case, we are safe + */ + if (filp->f_flags & FASYNC) { + DPRINT(("cleaning up async_queue=%p\n", ctx->ctx_async_queue)); + pfm_do_fasync (-1, filp, ctx, 0); + } + + PROTECT_CTX(ctx, flags); + + state = ctx->ctx_state; + is_system = ctx->ctx_fl_system; + + task = PFM_CTX_TASK(ctx); + regs = task_pt_regs(task); + + DPRINT(("ctx_state=%d is_current=%d\n", + state, + task == current ? 1 : 0)); + + /* + * if state == UNLOADED, then task is NULL + */ + + /* + * we must stop and unload because we are losing access to the context. + */ + if (task == current) { +#ifdef CONFIG_SMP + /* + * the task IS the owner but it migrated to another CPU: that's bad + * but we must handle this cleanly. Unfortunately, the kernel does + * not provide a mechanism to block migration (while the context is loaded). + * + * We need to release the resource on the ORIGINAL cpu. + */ + if (is_system && ctx->ctx_cpu != smp_processor_id()) { + + DPRINT(("should be running on CPU%d\n", ctx->ctx_cpu)); + /* + * keep context protected but unmask interrupt for IPI + */ + local_irq_restore(flags); + + pfm_syswide_cleanup_other_cpu(ctx); + + /* + * restore interrupt masking + */ + local_irq_save(flags); + + /* + * context is unloaded at this point + */ + } else +#endif /* CONFIG_SMP */ + { + + DPRINT(("forcing unload\n")); + /* + * stop and unload, returning with state UNLOADED + * and session unreserved. + */ + pfm_context_unload(ctx, NULL, 0, regs); + + DPRINT(("ctx_state=%d\n", ctx->ctx_state)); + } + } + + /* + * remove virtual mapping, if any, for the calling task. + * cannot reset ctx field until last user is calling close(). + * + * ctx_smpl_vaddr must never be cleared because it is needed + * by every task with access to the context + * + * When called from do_exit(), the mm context is gone already, therefore + * mm is NULL, i.e., the VMA is already gone and we do not have to + * do anything here + */ + if (ctx->ctx_smpl_vaddr && current->mm) { + smpl_buf_vaddr = ctx->ctx_smpl_vaddr; + smpl_buf_size = ctx->ctx_smpl_size; + } + + UNPROTECT_CTX(ctx, flags); + + /* + * if there was a mapping, then we systematically remove it + * at this point. Cannot be done inside critical section + * because some VM function reenables interrupts. + * + */ + if (smpl_buf_vaddr) pfm_remove_smpl_mapping(current, smpl_buf_vaddr, smpl_buf_size); + + return 0; +} +#endif +/* + * called either on explicit close() or from exit_files(). + * Only the LAST user of the file gets to this point, i.e., it is + * called only ONCE. + * + * IMPORTANT: we get called ONLY when the refcnt on the file gets to zero + * (fput()),i.e, last task to access the file. Nobody else can access the + * file at this point. + * + * When called from exit_files(), the VMA has been freed because exit_mm() + * is executed before exit_files(). + * + * When called from exit_files(), the current task is not yet ZOMBIE but we + * flush the PMU state to the context. + */ +#ifndef XEN +static int +pfm_close(struct inode *inode, struct file *filp) +#else +static int +pfm_close(pfm_context_t *ctx) +#endif +{ +#ifndef XEN + pfm_context_t *ctx; + struct task_struct *task; + struct pt_regs *regs; + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; +#endif + unsigned long smpl_buf_size = 0UL; + void *smpl_buf_addr = NULL; + int free_possible = 1; + int state, is_system; + +#ifndef XEN + DPRINT(("pfm_close called private=%p\n", filp->private_data)); + + if (PFM_IS_FILE(filp) == 0) { + DPRINT(("bad magic\n")); + return -EBADF; + } + + ctx = (pfm_context_t *)filp->private_data; + if (ctx == NULL) { + printk(KERN_ERR "perfmon: pfm_close: NULL ctx [%d]\n", current->pid); + return -EBADF; + } + + PROTECT_CTX(ctx, flags); +#else + BUG_ON(!spin_is_locked(&ctx->ctx_lock)); +#endif + + state = ctx->ctx_state; + is_system = ctx->ctx_fl_system; + +#ifndef XEN + task = PFM_CTX_TASK(ctx); + regs = task_pt_regs(task); + + DPRINT(("ctx_state=%d is_current=%d\n", + state, + task == current ? 1 : 0)); + + /* + * if task == current, then pfm_flush() unloaded the context + */ + if (state == PFM_CTX_UNLOADED) goto doit; + + /* + * context is loaded/masked and task != current, we need to + * either force an unload or go zombie + */ + + /* + * The task is currently blocked or will block after an overflow. + * we must force it to wakeup to get out of the + * MASKED state and transition to the unloaded state by itself. + * + * This situation is only possible for per-task mode + */ + if (state == PFM_CTX_MASKED && CTX_OVFL_NOBLOCK(ctx) == 0) { + + /* + * set a "partial" zombie state to be checked + * upon return from down() in pfm_handle_work(). + * + * We cannot use the ZOMBIE state, because it is checked + * by pfm_load_regs() which is called upon wakeup from down(). + * In such case, it would free the context and then we would + * return to pfm_handle_work() which would access the + * stale context. Instead, we set a flag invisible to pfm_load_regs() + * but visible to pfm_handle_work(). + * + * For some window of time, we have a zombie context with + * ctx_state = MASKED and not ZOMBIE + */ + ctx->ctx_fl_going_zombie = 1; + + /* + * force task to wake up from MASKED state + */ + complete(&ctx->ctx_restart_done); + + DPRINT(("waking up ctx_state=%d\n", state)); + + /* + * put ourself to sleep waiting for the other + * task to report completion + * + * the context is protected by mutex, therefore there + * is no risk of being notified of completion before + * begin actually on the waitq. + */ + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&ctx->ctx_zombieq, &wait); + + UNPROTECT_CTX(ctx, flags); + + /* + * XXX: check for signals : + * - ok for explicit close + * - not ok when coming from exit_files() + */ + schedule(); + + + PROTECT_CTX(ctx, flags); + + + remove_wait_queue(&ctx->ctx_zombieq, &wait); + set_current_state(TASK_RUNNING); + + /* + * context is unloaded at this point + */ + DPRINT(("after zombie wakeup ctx_state=%d for\n", state)); + } + else if (task != current) { +#ifdef CONFIG_SMP + /* + * switch context to zombie state + */ + ctx->ctx_state = PFM_CTX_ZOMBIE; + + DPRINT(("zombie ctx for [%d]\n", task->pid)); + /* + * cannot free the context on the spot. deferred until + * the task notices the ZOMBIE state + */ + free_possible = 0; +#else + pfm_context_unload(ctx, NULL, 0, regs); +#endif + } +#else + /* XXX XEN */ + /* unload context */ + BUG_ON(state != PFM_CTX_UNLOADED); +#endif + +#ifndef XEN +doit: +#endif + /* reload state, may have changed during opening of critical section */ + state = ctx->ctx_state; + + /* + * the context is still attached to a task (possibly current) + * we cannot destroy it right now + */ + + /* + * we must free the sampling buffer right here because + * we cannot rely on it being cleaned up later by the + * monitored task. It is not possible to free vmalloc'ed + * memory in pfm_load_regs(). Instead, we remove the buffer + * now. should there be subsequent PMU overflow originally + * meant for sampling, the will be converted to spurious + * and that's fine because the monitoring tools is gone anyway. + */ + if (ctx->ctx_smpl_hdr) { + smpl_buf_addr = ctx->ctx_smpl_hdr; + smpl_buf_size = ctx->ctx_smpl_size; + /* no more sampling */ + ctx->ctx_smpl_hdr = NULL; + ctx->ctx_fl_is_sampling = 0; + } + + DPRINT(("ctx_state=%d free_possible=%d addr=%p size=%lu\n", + state, + free_possible, + smpl_buf_addr, + smpl_buf_size)); + + if (smpl_buf_addr) pfm_exit_smpl_buffer(ctx->ctx_buf_fmt); + + /* + * UNLOADED that the session has already been unreserved. + */ + if (state == PFM_CTX_ZOMBIE) { + pfm_unreserve_session(ctx, ctx->ctx_fl_system , ctx->ctx_cpu); + } + +#ifndef XEN + /* + * disconnect file descriptor from context must be done + * before we unlock. + */ + filp->private_data = NULL; + + /* + * if we free on the spot, the context is now completely unreacheable + * from the callers side. The monitored task side is also cut, so we + * can freely cut. + * + * If we have a deferred free, only the caller side is disconnected. + */ + UNPROTECT_CTX(ctx, flags); + + /* + * All memory free operations (especially for vmalloc'ed memory) + * MUST be done with interrupts ENABLED. + */ + if (smpl_buf_addr) pfm_rvfree(smpl_buf_addr, smpl_buf_size); +#else + UNPROTECT_CTX_NOIRQ(ctx); +#endif + + /* + * return the memory used by the context + */ + if (free_possible) pfm_context_free(ctx); + + return 0; +} + +#ifndef XEN +static int +pfm_no_open(struct inode *irrelevant, struct file *dontcare) +{ + DPRINT(("pfm_no_open called\n")); + return -ENXIO; +} + + + +static struct file_operations pfm_file_ops = { + .llseek = no_llseek, + .read = pfm_read, + .write = pfm_write, + .poll = pfm_poll, + .ioctl = pfm_ioctl, + .open = pfm_no_open, /* special open code to disallow open via /proc */ + .fasync = pfm_fasync, + .release = pfm_close, + .flush = pfm_flush +}; + +static int +pfmfs_delete_dentry(struct dentry *dentry) +{ + return 1; +} + +static struct dentry_operations pfmfs_dentry_operations = { + .d_delete = pfmfs_delete_dentry, +}; + + +static int +pfm_alloc_fd(struct file **cfile) +{ + int fd, ret = 0; + struct file *file = NULL; + struct inode * inode; + char name[32]; + struct qstr this; + + fd = get_unused_fd(); + if (fd < 0) return -ENFILE; + + ret = -ENFILE; + + file = get_empty_filp(); + if (!file) goto out; + + /* + * allocate a new inode + */ + inode = new_inode(pfmfs_mnt->mnt_sb); + if (!inode) goto out; + + DPRINT(("new inode ino=%ld @%p\n", inode->i_ino, inode)); + + inode->i_mode = S_IFCHR|S_IRUGO; + inode->i_uid = current->fsuid; + inode->i_gid = current->fsgid; + + sprintf(name, "[%lu]", inode->i_ino); + this.name = name; + this.len = strlen(name); + this.hash = inode->i_ino; + + ret = -ENOMEM; + + /* + * allocate a new dcache entry + */ + file->f_dentry = d_alloc(pfmfs_mnt->mnt_sb->s_root, &this); + if (!file->f_dentry) goto out; + + file->f_dentry->d_op = &pfmfs_dentry_operations; + + d_add(file->f_dentry, inode); + file->f_vfsmnt = mntget(pfmfs_mnt); + file->f_mapping = inode->i_mapping; + + file->f_op = &pfm_file_ops; + file->f_mode = FMODE_READ; + file->f_flags = O_RDONLY; + file->f_pos = 0; + + /* + * may have to delay until context is attached? + */ + fd_install(fd, file); + + /* + * the file structure we will use + */ + *cfile = file; + + return fd; +out: + if (file) put_filp(file); + put_unused_fd(fd); + return ret; +} + +static void +pfm_free_fd(int fd, struct file *file) +{ + struct files_struct *files = current->files; + struct fdtable *fdt; + + /* + * there ie no fd_uninstall(), so we do it here + */ + spin_lock(&files->file_lock); + fdt = files_fdtable(files); + rcu_assign_pointer(fdt->fd[fd], NULL); + spin_unlock(&files->file_lock); + + if (file) + put_filp(file); + put_unused_fd(fd); +} + +static int +pfm_remap_buffer(struct vm_area_struct *vma, unsigned long buf, unsigned long addr, unsigned long size) +{ + DPRINT(("CPU%d buf=0x%lx addr=0x%lx size=%ld\n", smp_processor_id(), buf, addr, size)); + + while (size > 0) { + unsigned long pfn = ia64_tpa(buf) >> PAGE_SHIFT; + + + if (remap_pfn_range(vma, addr, pfn, PAGE_SIZE, PAGE_READONLY)) + return -ENOMEM; + + addr += PAGE_SIZE; + buf += PAGE_SIZE; + size -= PAGE_SIZE; + } + return 0; +} +#endif + +/* + * allocate a sampling buffer and remaps it into the user address space of the task + */ +static int +pfm_smpl_buffer_alloc(struct task_struct *task, pfm_context_t *ctx, unsigned long rsize, void **user_vaddr) +{ +#ifndef XEN + struct mm_struct *mm = task->mm; + struct vm_area_struct *vma = NULL; + unsigned long size; + void *smpl_buf; + + + /* + * the fixed header + requested size and align to page boundary + */ + size = PAGE_ALIGN(rsize); + + DPRINT(("sampling buffer rsize=%lu size=%lu bytes\n", rsize, size)); + + /* + * check requested size to avoid Denial-of-service attacks + * XXX: may have to refine this test + * Check against address space limit. + * + * if ((mm->total_vm << PAGE_SHIFT) + len> task->rlim[RLIMIT_AS].rlim_cur) + * return -ENOMEM; + */ + if (size > task->signal->rlim[RLIMIT_MEMLOCK].rlim_cur) + return -ENOMEM; + + /* + * We do the easy to undo allocations first. + * + * pfm_rvmalloc(), clears the buffer, so there is no leak + */ + smpl_buf = pfm_rvmalloc(size); + if (smpl_buf == NULL) { + DPRINT(("Can't allocate sampling buffer\n")); + return -ENOMEM; + } + + DPRINT(("smpl_buf @%p\n", smpl_buf)); + + /* allocate vma */ + vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); + if (!vma) { + DPRINT(("Cannot allocate vma\n")); + goto error_kmem; + } + memset(vma, 0, sizeof(*vma)); + + /* + * partially initialize the vma for the sampling buffer + */ + vma->vm_mm = mm; + vma->vm_flags = VM_READ| VM_MAYREAD |VM_RESERVED; + vma->vm_page_prot = PAGE_READONLY; /* XXX may need to change */ + + /* + * Now we have everything we need and we can initialize + * and connect all the data structures + */ + + ctx->ctx_smpl_hdr = smpl_buf; + ctx->ctx_smpl_size = size; /* aligned size */ + + /* + * Let's do the difficult operations next. + * + * now we atomically find some area in the address space and + * remap the buffer in it. + */ + down_write(&task->mm->mmap_sem); + + /* find some free area in address space, must have mmap sem held */ + vma->vm_start = pfm_get_unmapped_area(NULL, 0, size, 0, MAP_PRIVATE|MAP_ANONYMOUS, 0); + if (vma->vm_start == 0UL) { + DPRINT(("Cannot find unmapped area for size %ld\n", size)); + up_write(&task->mm->mmap_sem); + goto error; + } + vma->vm_end = vma->vm_start + size; + vma->vm_pgoff = vma->vm_start >> PAGE_SHIFT; + + DPRINT(("aligned size=%ld, hdr=%p mapped @0x%lx\n", size, ctx->ctx_smpl_hdr, vma->vm_start)); + + /* can only be applied to current task, need to have the mm semaphore held when called */ + if (pfm_remap_buffer(vma, (unsigned long)smpl_buf, vma->vm_start, size)) { + DPRINT(("Can't remap buffer\n")); + up_write(&task->mm->mmap_sem); + goto error; + } + + /* + * now insert the vma in the vm list for the process, must be + * done with mmap lock held + */ + insert_vm_struct(mm, vma); + + mm->total_vm += size >> PAGE_SHIFT; + vm_stat_account(vma->vm_mm, vma->vm_flags, vma->vm_file, + vma_pages(vma)); + up_write(&task->mm->mmap_sem); + + /* + * keep track of user level virtual address + */ + ctx->ctx_smpl_vaddr = (void *)vma->vm_start; + *(unsigned long *)user_vaddr = vma->vm_start; + + return 0; + +error: + kmem_cache_free(vm_area_cachep, vma); +error_kmem: + pfm_rvfree(smpl_buf, size); + + return -ENOMEM; +#else + /* XXX */ + return 0; +#endif +} + +#ifndef XEN +/* + * XXX: do something better here + */ +static int +pfm_bad_permissions(struct task_struct *task) +{ + /* inspired by ptrace_attach() */ + DPRINT(("cur: uid=%d gid=%d task: euid=%d suid=%d uid=%d egid=%d sgid=%d\n", + current->uid, + current->gid, + task->euid, + task->suid, + task->uid, + task->egid, + task->sgid)); + + return ((current->uid != task->euid) + || (current->uid != task->suid) + || (current->uid != task->uid) + || (current->gid != task->egid) + || (current->gid != task->sgid) + || (current->gid != task->gid)) && !capable(CAP_SYS_PTRACE); +} +#endif + +static int +pfarg_is_sane(struct task_struct *task, pfarg_context_t *pfx) +{ + int ctx_flags; + + /* valid signal */ + + ctx_flags = pfx->ctx_flags; + + if (ctx_flags & PFM_FL_SYSTEM_WIDE) { + + /* + * cannot block in this mode + */ + if (ctx_flags & PFM_FL_NOTIFY_BLOCK) { + DPRINT(("cannot use blocking mode when in system wide monitoring\n")); + return -EINVAL; + } + } else { + } + /* probably more to add here */ + + return 0; +} + +static int +pfm_setup_buffer_fmt(struct task_struct *task, pfm_context_t *ctx, unsigned int ctx_flags, + unsigned int cpu, pfarg_context_t *arg) +{ + pfm_buffer_fmt_t *fmt = NULL; + unsigned long size = 0UL; + void *uaddr = NULL; + void *fmt_arg = NULL; + int ret = 0; +#define PFM_CTXARG_BUF_ARG(a) (pfm_buffer_fmt_t *)(a+1) + + /* invoke and lock buffer format, if found */ + fmt = pfm_find_buffer_fmt(arg->ctx_smpl_buf_id); + if (fmt == NULL) { + DPRINT(("[%d] cannot find buffer format\n", task->pid)); + return -EINVAL; + } + + /* + * buffer argument MUST be contiguous to pfarg_context_t + */ + if (fmt->fmt_arg_size) fmt_arg = PFM_CTXARG_BUF_ARG(arg); + + ret = pfm_buf_fmt_validate(fmt, task, ctx_flags, cpu, fmt_arg); + + DPRINT(("[%d] after validate(0x%x,%d,%p)=%d\n", task->pid, ctx_flags, cpu, fmt_arg, ret)); + + if (ret) goto error; + + /* link buffer format and context */ + ctx->ctx_buf_fmt = fmt; + + /* + * check if buffer format wants to use perfmon buffer allocation/mapping service + */ + ret = pfm_buf_fmt_getsize(fmt, task, ctx_flags, cpu, fmt_arg, &size); + if (ret) goto error; + + if (size) { + /* + * buffer is always remapped into the caller's address space + */ + ret = pfm_smpl_buffer_alloc(current, ctx, size, &uaddr); + if (ret) goto error; + + /* keep track of user address of buffer */ + arg->ctx_smpl_vaddr = uaddr; + } + ret = pfm_buf_fmt_init(fmt, task, ctx->ctx_smpl_hdr, ctx_flags, cpu, fmt_arg); + +error: + return ret; +} + +static void +pfm_reset_pmu_state(pfm_context_t *ctx) +{ + int i; + + /* + * install reset values for PMC. + */ + for (i=1; PMC_IS_LAST(i) == 0; i++) { + if (PMC_IS_IMPL(i) == 0) continue; + ctx->ctx_pmcs[i] = PMC_DFL_VAL(i); + DPRINT(("pmc[%d]=0x%lx\n", i, ctx->ctx_pmcs[i])); + } + /* + * PMD registers are set to 0UL when the context in memset() + */ + + /* + * On context switched restore, we must restore ALL pmc and ALL pmd even + * when they are not actively used by the task. In UP, the incoming process + * may otherwise pick up left over PMC, PMD state from the previous process. + * As opposed to PMD, stale PMC can cause harm to the incoming + * process because they may change what is being measured. + * Therefore, we must systematically reinstall the entire + * PMC state. In SMP, the same thing is possible on the + * same CPU but also on between 2 CPUs. + * + * The problem with PMD is information leaking especially + * to user level when psr.sp=0 + * + * There is unfortunately no easy way to avoid this problem + * on either UP or SMP. This definitively slows down the + * pfm_load_regs() function. + */ + + /* + * bitmask of all PMCs accessible to this context + * + * PMC0 is treated differently. + */ + ctx->ctx_all_pmcs[0] = pmu_conf->impl_pmcs[0] & ~0x1; + + /* + * bitmask of all PMDs that are accesible to this context + */ + ctx->ctx_all_pmds[0] = pmu_conf->impl_pmds[0]; + + DPRINT(("<%d> all_pmcs=0x%lx all_pmds=0x%lx\n", ctx->ctx_fd, ctx->ctx_all_pmcs[0],ctx->ctx_all_pmds[0])); + + /* + * useful in case of re-enable after disable + */ + ctx->ctx_used_ibrs[0] = 0UL; + ctx->ctx_used_dbrs[0] = 0UL; +} + +#ifndef XEN +static int +pfm_ctx_getsize(void *arg, size_t *sz) +{ + pfarg_context_t *req = (pfarg_context_t *)arg; + pfm_buffer_fmt_t *fmt; + + *sz = 0; + + if (!pfm_uuid_cmp(req->ctx_smpl_buf_id, pfm_null_uuid)) return 0; + + fmt = pfm_find_buffer_fmt(req->ctx_smpl_buf_id); + if (fmt == NULL) { + DPRINT(("cannot find buffer format\n")); + return -EINVAL; + } + /* get just enough to copy in user parameters */ + *sz = fmt->fmt_arg_size; + DPRINT(("arg_size=%lu\n", *sz)); + + return 0; +} + + + +/* + * cannot attach if : + * - kernel task + * - task not owned by caller + * - task incompatible with context mode + */ +static int +pfm_task_incompatible(pfm_context_t *ctx, struct task_struct *task) +{ + /* + * no kernel task or task not owner by caller + */ + if (task->mm == NULL) { + DPRINT(("task [%d] has not memory context (kernel thread)\n", task->pid)); + return -EPERM; + } + if (pfm_bad_permissions(task)) { + DPRINT(("no permission to attach to [%d]\n", task->pid)); + return -EPERM; + } + /* + * cannot block in self-monitoring mode + */ + if (CTX_OVFL_NOBLOCK(ctx) == 0 && task == current) { + DPRINT(("cannot load a blocking context on self for [%d]\n", task->pid)); + return -EINVAL; + } + + if (task->exit_state == EXIT_ZOMBIE) { + DPRINT(("cannot attach to zombie task [%d]\n", task->pid)); + return -EBUSY; + } + + /* + * always ok for self + */ + if (task == current) return 0; + + if ((task->state != TASK_STOPPED) && (task->state != TASK_TRACED)) { + DPRINT(("cannot attach to non-stopped task [%d] state=%ld\n", task->pid, task->state)); + return -EBUSY; + } + /* + * make sure the task is off any CPU + */ + wait_task_inactive(task); + + /* more to come... */ + + return 0; +} + +static int +pfm_get_task(pfm_context_t *ctx, pid_t pid, struct task_struct **task) +{ + struct task_struct *p = current; + int ret; + + /* XXX: need to add more checks here */ + if (pid < 2) return -EPERM; + + if (pid != current->pid) { + + read_lock(&tasklist_lock); + + p = find_task_by_pid(pid); + + /* make sure task cannot go away while we operate on it */ + if (p) get_task_struct(p); + + read_unlock(&tasklist_lock); + + if (p == NULL) return -ESRCH; + } + + ret = pfm_task_incompatible(ctx, p); + if (ret == 0) { + *task = p; + } else if (p != current) { + pfm_put_task(p); + } + return ret; +} +#endif + + +#ifndef XEN +static int +pfm_context_create(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) +#else +static pfm_context_t* +pfm_context_create(pfarg_context_t* req) +#endif +{ +#ifndef XEN + pfarg_context_t *req = (pfarg_context_t *)arg; + struct file *filp; +#else + pfm_context_t *ctx; +#endif + int ctx_flags; + int ret; + +#ifndef XEN + /* let's check the arguments first */ + ret = pfarg_is_sane(current, req); + if (ret < 0) return ret; +#endif + + ctx_flags = req->ctx_flags; + + ret = -ENOMEM; + + ctx = pfm_context_alloc(); + if (!ctx) goto error; + +#ifndef XEN + ret = pfm_alloc_fd(&filp); + if (ret < 0) goto error_file; + + req->ctx_fd = ctx->ctx_fd = ret; + + /* + * attach context to file + */ + filp->private_data = ctx; +#endif + + /* + * does the user want to sample? + */ + if (pfm_uuid_cmp(req->ctx_smpl_buf_id, pfm_null_uuid)) { + ret = pfm_setup_buffer_fmt(current, ctx, ctx_flags, 0, req); + if (ret) goto buffer_error; + } + + /* + * init context protection lock + */ + spin_lock_init(&ctx->ctx_lock); + + /* + * context is unloaded + */ + ctx->ctx_state = PFM_CTX_UNLOADED; + + /* + * initialization of context's flags + */ + ctx->ctx_fl_block = (ctx_flags & PFM_FL_NOTIFY_BLOCK) ? 1 : 0; + ctx->ctx_fl_system = (ctx_flags & PFM_FL_SYSTEM_WIDE) ? 1: 0; + ctx->ctx_fl_is_sampling = ctx->ctx_buf_fmt ? 1 : 0; /* assume record() is defined */ + ctx->ctx_fl_no_msg = (ctx_flags & PFM_FL_OVFL_NO_MSG) ? 1: 0; + /* + * will move to set properties + * ctx->ctx_fl_excl_idle = (ctx_flags & PFM_FL_EXCL_IDLE) ? 1: 0; + */ + +#ifndef XEN + /* + * init restart semaphore to locked + */ + init_completion(&ctx->ctx_restart_done); +#endif + + /* + * activation is used in SMP only + */ + ctx->ctx_last_activation = PFM_INVALID_ACTIVATION; + SET_LAST_CPU(ctx, -1); + +#ifndef XEN + /* + * initialize notification message queue + */ + ctx->ctx_msgq_head = ctx->ctx_msgq_tail = 0; + init_waitqueue_head(&ctx->ctx_msgq_wait); + init_waitqueue_head(&ctx->ctx_zombieq); +#endif + + DPRINT(("ctx=%p flags=0x%x system=%d notify_block=%d excl_idle=%d no_msg=%d ctx_fd=%d \n", + ctx, + ctx_flags, + ctx->ctx_fl_system, + ctx->ctx_fl_block, + ctx->ctx_fl_excl_idle, + ctx->ctx_fl_no_msg, + ctx->ctx_fd)); + + /* + * initialize soft PMU state + */ + pfm_reset_pmu_state(ctx); + +#ifndef XEN + return 0; +#else + return ctx; +#endif + +buffer_error: +#ifndef XEN + pfm_free_fd(ctx->ctx_fd, filp); +#endif + + if (ctx->ctx_buf_fmt) { +#ifndef XEN + pfm_buf_fmt_exit(ctx->ctx_buf_fmt, current, NULL, regs); +#else + pfm_buf_fmt_exit(ctx->ctx_buf_fmt, current, NULL, NULL); +#endif + } +#ifndef XEN +error_file: +#endif + pfm_context_free(ctx); + +error: +#ifndef XEN + return ret; +#else + return NULL; +#endif +} + +static inline unsigned long +pfm_new_counter_value (pfm_counter_t *reg, int is_long_reset) +{ + unsigned long val = is_long_reset ? reg->long_reset : reg->short_reset; + unsigned long new_seed, old_seed = reg->seed, mask = reg->mask; + extern unsigned long carta_random32 (unsigned long seed); + + if (reg->flags & PFM_REGFL_RANDOM) { + new_seed = carta_random32(old_seed); + val -= (old_seed & mask); /* counter values are negative numbers! */ + if ((mask >> 32) != 0) + /* construct a full 64-bit random value: */ + new_seed |= carta_random32(old_seed >> 32) << 32; + reg->seed = new_seed; + } + reg->lval = val; + return val; +} + +static void +pfm_reset_regs_masked(pfm_context_t *ctx, unsigned long *ovfl_regs, int is_long_reset) +{ + unsigned long mask = ovfl_regs[0]; + unsigned long reset_others = 0UL; + unsigned long val; + int i; + + /* + * now restore reset value on sampling overflowed counters + */ + mask >>= PMU_FIRST_COUNTER; + for(i = PMU_FIRST_COUNTER; mask; i++, mask >>= 1) { + + if ((mask & 0x1UL) == 0UL) continue; + + ctx->ctx_pmds[i].val = val = pfm_new_counter_value(ctx->ctx_pmds+ i, is_long_reset); + reset_others |= ctx->ctx_pmds[i].reset_pmds[0]; + + DPRINT_ovfl((" %s reset ctx_pmds[%d]=%lx\n", is_long_reset ? "long" : "short", i, val)); + } + + /* + * Now take care of resetting the other registers + */ + for(i = 0; reset_others; i++, reset_others >>= 1) { + + if ((reset_others & 0x1) == 0) continue; + + ctx->ctx_pmds[i].val = val = pfm_new_counter_value(ctx->ctx_pmds + i, is_long_reset); + + DPRINT_ovfl(("%s reset_others pmd[%d]=%lx\n", + is_long_reset ? "long" : "short", i, val)); + } +} + +static void +pfm_reset_regs(pfm_context_t *ctx, unsigned long *ovfl_regs, int is_long_reset) +{ + unsigned long mask = ovfl_regs[0]; + unsigned long reset_others = 0UL; + unsigned long val; + int i; + + DPRINT_ovfl(("ovfl_regs=0x%lx is_long_reset=%d\n", ovfl_regs[0], is_long_reset)); + + if (ctx->ctx_state == PFM_CTX_MASKED) { + pfm_reset_regs_masked(ctx, ovfl_regs, is_long_reset); + return; + } + + /* + * now restore reset value on sampling overflowed counters + */ + mask >>= PMU_FIRST_COUNTER; + for(i = PMU_FIRST_COUNTER; mask; i++, mask >>= 1) { + + if ((mask & 0x1UL) == 0UL) continue; + + val = pfm_new_counter_value(ctx->ctx_pmds+ i, is_long_reset); + reset_others |= ctx->ctx_pmds[i].reset_pmds[0]; + + DPRINT_ovfl((" %s reset ctx_pmds[%d]=%lx\n", is_long_reset ? "long" : "short", i, val)); + + pfm_write_soft_counter(ctx, i, val); + } + + /* + * Now take care of resetting the other registers + */ + for(i = 0; reset_others; i++, reset_others >>= 1) { + + if ((reset_others & 0x1) == 0) continue; + + val = pfm_new_counter_value(ctx->ctx_pmds + i, is_long_reset); + + if (PMD_IS_COUNTING(i)) { + pfm_write_soft_counter(ctx, i, val); + } else { + ia64_set_pmd(i, val); + } + DPRINT_ovfl(("%s reset_others pmd[%d]=%lx\n", + is_long_reset ? "long" : "short", i, val)); + } + ia64_srlz_d(); +} + +static int +pfm_write_pmcs(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) +{ +#ifndef XEN + struct thread_struct *thread = NULL; +#endif + struct task_struct *task; + pfarg_reg_t *req = (pfarg_reg_t *)arg; + unsigned long value, pmc_pm; + unsigned long smpl_pmds, reset_pmds, impl_pmds; + unsigned int cnum, reg_flags, flags, pmc_type; + int i, can_access_pmu = 0, is_loaded, is_system, expert_mode; + int is_monitor, is_counting, state; + int ret = -EINVAL; + pfm_reg_check_t wr_func; +#define PFM_CHECK_PMC_PM(x, y, z) ((x)->ctx_fl_system ^ PMC_PM(y, z)) + + state = ctx->ctx_state; + is_loaded = state == PFM_CTX_LOADED ? 1 : 0; + is_system = ctx->ctx_fl_system; + task = ctx->ctx_task; + impl_pmds = pmu_conf->impl_pmds[0]; +#ifdef XEN + task = NULL; + BUG_ON(regs != NULL); +#endif + + if (state == PFM_CTX_ZOMBIE) return -EINVAL; + +#ifndef XEN + if (is_loaded) { + thread = &task->thread; + /* + * In system wide and when the context is loaded, access can only happen + * when the caller is running on the CPU being monitored by the session. + * It does not have to be the owner (ctx_task) of the context per se. + */ + if (is_system && ctx->ctx_cpu != smp_processor_id()) { + DPRINT(("should be running on CPU%d\n", ctx->ctx_cpu)); + return -EBUSY; + } + can_access_pmu = GET_PMU_OWNER() == task || is_system ? 1 : 0; + } +#else + /* XXX FIXME */ + if (state != PFM_CTX_UNLOADED) { + return -EBUSY; + } +#endif + + expert_mode = pfm_sysctl.expert_mode; + + for (i = 0; i < count; i++, req++) { + + cnum = req->reg_num; + reg_flags = req->reg_flags; + value = req->reg_value; + smpl_pmds = req->reg_smpl_pmds[0]; + reset_pmds = req->reg_reset_pmds[0]; + flags = 0; + + + if (cnum >= PMU_MAX_PMCS) { + DPRINT(("pmc%u is invalid\n", cnum)); + goto error; + } + + pmc_type = pmu_conf->pmc_desc[cnum].type; + pmc_pm = (value >> pmu_conf->pmc_desc[cnum].pm_pos) & 0x1; + is_counting = (pmc_type & PFM_REG_COUNTING) == PFM_REG_COUNTING ? 1 : 0; + is_monitor = (pmc_type & PFM_REG_MONITOR) == PFM_REG_MONITOR ? 1 : 0; + + /* + * we reject all non implemented PMC as well + * as attempts to modify PMC[0-3] which are used + * as status registers by the PMU + */ + if ((pmc_type & PFM_REG_IMPL) == 0 || (pmc_type & PFM_REG_CONTROL) == PFM_REG_CONTROL) { + DPRINT(("pmc%u is unimplemented or no-access pmc_type=%x\n", cnum, pmc_type)); + goto error; + } + wr_func = pmu_conf->pmc_desc[cnum].write_check; + /* + * If the PMC is a monitor, then if the value is not the default: + * - system-wide session: PMCx.pm=1 (privileged monitor) + * - per-task : PMCx.pm=0 (user monitor) + */ + if (is_monitor && value != PMC_DFL_VAL(cnum) && is_system ^ pmc_pm) { + DPRINT(("pmc%u pmc_pm=%lu is_system=%d\n", + cnum, + pmc_pm, + is_system)); + goto error; + } + + if (is_counting) { + /* + * enforce generation of overflow interrupt. Necessary on all + * CPUs. + */ + value |= 1 << PMU_PMC_OI; + + if (reg_flags & PFM_REGFL_OVFL_NOTIFY) { + flags |= PFM_REGFL_OVFL_NOTIFY; + } + + if (reg_flags & PFM_REGFL_RANDOM) flags |= PFM_REGFL_RANDOM; + + /* verify validity of smpl_pmds */ + if ((smpl_pmds & impl_pmds) != smpl_pmds) { + DPRINT(("invalid smpl_pmds 0x%lx for pmc%u\n", smpl_pmds, cnum)); + goto error; + } + + /* verify validity of reset_pmds */ + if ((reset_pmds & impl_pmds) != reset_pmds) { + DPRINT(("invalid reset_pmds 0x%lx for pmc%u\n", reset_pmds, cnum)); + goto error; + } + } else { + if (reg_flags & (PFM_REGFL_OVFL_NOTIFY|PFM_REGFL_RANDOM)) { + DPRINT(("cannot set ovfl_notify or random on pmc%u\n", cnum)); + goto error; + } + /* eventid on non-counting monitors are ignored */ + } + + /* + * execute write checker, if any + */ + if (likely(expert_mode == 0 && wr_func)) { + ret = (*wr_func)(task, ctx, cnum, &value, regs); + if (ret) goto error; + ret = -EINVAL; + } + + /* + * no error on this register + */ + PFM_REG_RETFLAG_SET(req->reg_flags, 0); + + /* + * Now we commit the changes to the software state + */ + + /* + * update overflow information + */ + if (is_counting) { + /* + * full flag update each time a register is programmed + */ + ctx->ctx_pmds[cnum].flags = flags; + + ctx->ctx_pmds[cnum].reset_pmds[0] = reset_pmds; + ctx->ctx_pmds[cnum].smpl_pmds[0] = smpl_pmds; + ctx->ctx_pmds[cnum].eventid = req->reg_smpl_eventid; + + /* + * Mark all PMDS to be accessed as used. + * + * We do not keep track of PMC because we have to + * systematically restore ALL of them. + * + * We do not update the used_monitors mask, because + * if we have not programmed them, then will be in + * a quiescent state, therefore we will not need to + * mask/restore then when context is MASKED. + */ + CTX_USED_PMD(ctx, reset_pmds); + CTX_USED_PMD(ctx, smpl_pmds); + /* + * make sure we do not try to reset on + * restart because we have established new values + */ + if (state == PFM_CTX_MASKED) ctx->ctx_ovfl_regs[0] &= ~1UL << cnum; + } + /* + * Needed in case the user does not initialize the equivalent + * PMD. Clearing is done indirectly via pfm_reset_pmu_state() so there is no + * possible leak here. + */ + CTX_USED_PMD(ctx, pmu_conf->pmc_desc[cnum].dep_pmd[0]); + + /* + * keep track of the monitor PMC that we are using. + * we save the value of the pmc in ctx_pmcs[] and if + * the monitoring is not stopped for the context we also + * place it in the saved state area so that it will be + * picked up later by the context switch code. + * + * The value in ctx_pmcs[] can only be changed in pfm_write_pmcs(). + * + * The value in thread->pmcs[] may be modified on overflow, i.e., when + * monitoring needs to be stopped. + */ + if (is_monitor) CTX_USED_MONITOR(ctx, 1UL << cnum); + + /* + * update context state + */ + ctx->ctx_pmcs[cnum] = value; + +#ifndef XEN + if (is_loaded) { + /* + * write thread state + */ + if (is_system == 0) thread->pmcs[cnum] = value; + + /* + * write hardware register if we can + */ + if (can_access_pmu) { + ia64_set_pmc(cnum, value); + } +#ifdef CONFIG_SMP + else { + /* + * per-task SMP only here + * + * we are guaranteed that the task is not running on the other CPU, + * we indicate that this PMD will need to be reloaded if the task + * is rescheduled on the CPU it ran last on. + */ + ctx->ctx_reload_pmcs[0] |= 1UL << cnum; + } +#endif + } +#endif + + DPRINT(("pmc[%u]=0x%lx ld=%d apmu=%d flags=0x%x all_pmcs=0x%lx used_pmds=0x%lx eventid=%ld smpl_pmds=0x%lx reset_pmds=0x%lx reloads_pmcs=0x%lx used_monitors=0x%lx ovfl_regs=0x%lx\n", + cnum, + value, + is_loaded, + can_access_pmu, + flags, + ctx->ctx_all_pmcs[0], + ctx->ctx_used_pmds[0], + ctx->ctx_pmds[cnum].eventid, + smpl_pmds, + reset_pmds, + ctx->ctx_reload_pmcs[0], + ctx->ctx_used_monitors[0], + ctx->ctx_ovfl_regs[0])); + } + + /* + * make sure the changes are visible + */ + if (can_access_pmu) ia64_srlz_d(); + + return 0; +error: + PFM_REG_RETFLAG_SET(req->reg_flags, PFM_REG_RETFL_EINVAL); + return ret; +} + +static int +pfm_write_pmds(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) +{ +#ifndef XEN + struct thread_struct *thread = NULL; +#endif + struct task_struct *task; + pfarg_reg_t *req = (pfarg_reg_t *)arg; + unsigned long value, hw_value, ovfl_mask; + unsigned int cnum; + int i, can_access_pmu = 0, state; + int is_counting, is_loaded, is_system, expert_mode; + int ret = -EINVAL; + pfm_reg_check_t wr_func; + + + state = ctx->ctx_state; + is_loaded = state == PFM_CTX_LOADED ? 1 : 0; + is_system = ctx->ctx_fl_system; + ovfl_mask = pmu_conf->ovfl_val; + task = ctx->ctx_task; +#ifdef XEN + task = NULL; + BUG_ON(regs != NULL); +#endif + + if (unlikely(state == PFM_CTX_ZOMBIE)) return -EINVAL; + +#ifndef XEN + /* + * on both UP and SMP, we can only write to the PMC when the task is + * the owner of the local PMU. + */ + if (likely(is_loaded)) { + thread = &task->thread; + /* + * In system wide and when the context is loaded, access can only happen + * when the caller is running on the CPU being monitored by the session. + * It does not have to be the owner (ctx_task) of the context per se. + */ + if (unlikely(is_system && ctx->ctx_cpu != smp_processor_id())) { + DPRINT(("should be running on CPU%d\n", ctx->ctx_cpu)); + return -EBUSY; + } + can_access_pmu = GET_PMU_OWNER() == task || is_system ? 1 : 0; + } +#else + /* XXX FIXME */ + if (state != PFM_CTX_UNLOADED) { + return -EBUSY; + } +#endif + expert_mode = pfm_sysctl.expert_mode; + + for (i = 0; i < count; i++, req++) { + + cnum = req->reg_num; + value = req->reg_value; + + if (!PMD_IS_IMPL(cnum)) { + DPRINT(("pmd[%u] is unimplemented or invalid\n", cnum)); + goto abort_mission; + } + is_counting = PMD_IS_COUNTING(cnum); + wr_func = pmu_conf->pmd_desc[cnum].write_check; + + /* + * execute write checker, if any + */ + if (unlikely(expert_mode == 0 && wr_func)) { + unsigned long v = value; + + ret = (*wr_func)(task, ctx, cnum, &v, regs); + if (ret) goto abort_mission; + + value = v; + ret = -EINVAL; + } + + /* + * no error on this register + */ + PFM_REG_RETFLAG_SET(req->reg_flags, 0); + + /* + * now commit changes to software state + */ + hw_value = value; + + /* + * update virtualized (64bits) counter + */ + if (is_counting) { + /* + * write context state + */ + ctx->ctx_pmds[cnum].lval = value; + + /* + * when context is load we use the split value + */ + if (is_loaded) { + hw_value = value & ovfl_mask; + value = value & ~ovfl_mask; + } + } + /* + * update reset values (not just for counters) + */ + ctx->ctx_pmds[cnum].long_reset = req->reg_long_reset; + ctx->ctx_pmds[cnum].short_reset = req->reg_short_reset; + + /* + * update randomization parameters (not just for counters) + */ + ctx->ctx_pmds[cnum].seed = req->reg_random_seed; + ctx->ctx_pmds[cnum].mask = req->reg_random_mask; + + /* + * update context value + */ + ctx->ctx_pmds[cnum].val = value; + + /* + * Keep track of what we use + * + * We do not keep track of PMC because we have to + * systematically restore ALL of them. + */ + CTX_USED_PMD(ctx, PMD_PMD_DEP(cnum)); + + /* + * mark this PMD register used as well + */ + CTX_USED_PMD(ctx, RDEP(cnum)); + + /* + * make sure we do not try to reset on + * restart because we have established new values + */ + if (is_counting && state == PFM_CTX_MASKED) { + ctx->ctx_ovfl_regs[0] &= ~1UL << cnum; + } + + /* XXX FIXME */ +#ifndef XEN + if (is_loaded) { + /* + * write thread state + */ + if (is_system == 0) thread->pmds[cnum] = hw_value; + + /* + * write hardware register if we can + */ + if (can_access_pmu) { + ia64_set_pmd(cnum, hw_value); + } else { +#ifdef CONFIG_SMP + /* + * we are guaranteed that the task is not running on the other CPU, + * we indicate that this PMD will need to be reloaded if the task + * is rescheduled on the CPU it ran last on. + */ + ctx->ctx_reload_pmds[0] |= 1UL << cnum; +#endif + } + } +#endif + + DPRINT(("pmd[%u]=0x%lx ld=%d apmu=%d, hw_value=0x%lx ctx_pmd=0x%lx short_reset=0x%lx " + "long_reset=0x%lx notify=%c seed=0x%lx mask=0x%lx used_pmds=0x%lx reset_pmds=0x%lx reload_pmds=0x%lx all_pmds=0x%lx ovfl_regs=0x%lx\n", + cnum, + value, + is_loaded, + can_access_pmu, + hw_value, + ctx->ctx_pmds[cnum].val, + ctx->ctx_pmds[cnum].short_reset, + ctx->ctx_pmds[cnum].long_reset, + PMC_OVFL_NOTIFY(ctx, cnum) ? 'Y':'N', + ctx->ctx_pmds[cnum].seed, + ctx->ctx_pmds[cnum].mask, + ctx->ctx_used_pmds[0], + ctx->ctx_pmds[cnum].reset_pmds[0], + ctx->ctx_reload_pmds[0], + ctx->ctx_all_pmds[0], + ctx->ctx_ovfl_regs[0])); + } + + /* + * make changes visible + */ + if (can_access_pmu) ia64_srlz_d(); + + return 0; + +abort_mission: + /* + * for now, we have only one possibility for error + */ + PFM_REG_RETFLAG_SET(req->reg_flags, PFM_REG_RETFL_EINVAL); + return ret; +} + +#ifndef XEN +/* + * By the way of PROTECT_CONTEXT(), interrupts are masked while we are in this function. + * Therefore we know, we do not have to worry about the PMU overflow interrupt. If an + * interrupt is delivered during the call, it will be kept pending until we leave, making + * it appears as if it had been generated at the UNPROTECT_CONTEXT(). At least we are + * guaranteed to return consistent data to the user, it may simply be old. It is not + * trivial to treat the overflow while inside the call because you may end up in + * some module sampling buffer code causing deadlocks. + */ +static int +pfm_read_pmds(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) +{ + struct thread_struct *thread = NULL; + struct task_struct *task; + unsigned long val = 0UL, lval, ovfl_mask, sval; + pfarg_reg_t *req = (pfarg_reg_t *)arg; + unsigned int cnum, reg_flags = 0; + int i, can_access_pmu = 0, state; + int is_loaded, is_system, is_counting, expert_mode; + int ret = -EINVAL; + pfm_reg_check_t rd_func; + + /* + * access is possible when loaded only for + * self-monitoring tasks or in UP mode + */ + + state = ctx->ctx_state; + is_loaded = state == PFM_CTX_LOADED ? 1 : 0; + is_system = ctx->ctx_fl_system; + ovfl_mask = pmu_conf->ovfl_val; + task = ctx->ctx_task; + + if (state == PFM_CTX_ZOMBIE) return -EINVAL; + + if (likely(is_loaded)) { + thread = &task->thread; + /* + * In system wide and when the context is loaded, access can only happen + * when the caller is running on the CPU being monitored by the session. + * It does not have to be the owner (ctx_task) of the context per se. + */ + if (unlikely(is_system && ctx->ctx_cpu != smp_processor_id())) { + DPRINT(("should be running on CPU%d\n", ctx->ctx_cpu)); + return -EBUSY; + } + /* + * this can be true when not self-monitoring only in UP + */ + can_access_pmu = GET_PMU_OWNER() == task || is_system ? 1 : 0; + + if (can_access_pmu) ia64_srlz_d(); + } + expert_mode = pfm_sysctl.expert_mode; + + DPRINT(("ld=%d apmu=%d ctx_state=%d\n", + is_loaded, + can_access_pmu, + state)); + + /* + * on both UP and SMP, we can only read the PMD from the hardware register when + * the task is the owner of the local PMU. + */ + + for (i = 0; i < count; i++, req++) { + + cnum = req->reg_num; + reg_flags = req->reg_flags; + + if (unlikely(!PMD_IS_IMPL(cnum))) goto error; + /* + * we can only read the register that we use. That includes + * the one we explicitely initialize AND the one we want included + * in the sampling buffer (smpl_regs). + * + * Having this restriction allows optimization in the ctxsw routine + * without compromising security (leaks) + */ + if (unlikely(!CTX_IS_USED_PMD(ctx, cnum))) goto error; + + sval = ctx->ctx_pmds[cnum].val; + lval = ctx->ctx_pmds[cnum].lval; + is_counting = PMD_IS_COUNTING(cnum); + + /* + * If the task is not the current one, then we check if the + * PMU state is still in the local live register due to lazy ctxsw. + * If true, then we read directly from the registers. + */ + if (can_access_pmu){ + val = ia64_get_pmd(cnum); + } else { + /* + * context has been saved + * if context is zombie, then task does not exist anymore. + * In this case, we use the full value saved in the context (pfm_flush_regs()). + */ + val = is_loaded ? thread->pmds[cnum] : 0UL; + } + rd_func = pmu_conf->pmd_desc[cnum].read_check; + + if (is_counting) { + /* + * XXX: need to check for overflow when loaded + */ + val &= ovfl_mask; + val += sval; + } + + /* + * execute read checker, if any + */ + if (unlikely(expert_mode == 0 && rd_func)) { + unsigned long v = val; + ret = (*rd_func)(ctx->ctx_task, ctx, cnum, &v, regs); + if (ret) goto error; + val = v; + ret = -EINVAL; + } + + PFM_REG_RETFLAG_SET(reg_flags, 0); + + DPRINT(("pmd[%u]=0x%lx\n", cnum, val)); + + /* + * update register return value, abort all if problem during copy. + * we only modify the reg_flags field. no check mode is fine because + * access has been verified upfront in sys_perfmonctl(). + */ + req->reg_value = val; + req->reg_flags = reg_flags; + req->reg_last_reset_val = lval; + } + + return 0; + +error: + PFM_REG_RETFLAG_SET(req->reg_flags, PFM_REG_RETFL_EINVAL); + return ret; +} + +int +pfm_mod_write_pmcs(struct task_struct *task, void *req, unsigned int nreq, struct pt_regs *regs) +{ + pfm_context_t *ctx; + + if (req == NULL) return -EINVAL; + + ctx = GET_PMU_CTX(); + + if (ctx == NULL) return -EINVAL; + + /* + * for now limit to current task, which is enough when calling + * from overflow handler + */ + if (task != current && ctx->ctx_fl_system == 0) return -EBUSY; + + return pfm_write_pmcs(ctx, req, nreq, regs); +} +EXPORT_SYMBOL(pfm_mod_write_pmcs); + +int +pfm_mod_read_pmds(struct task_struct *task, void *req, unsigned int nreq, struct pt_regs *regs) +{ + pfm_context_t *ctx; + + if (req == NULL) return -EINVAL; + + ctx = GET_PMU_CTX(); + + if (ctx == NULL) return -EINVAL; + + /* + * for now limit to current task, which is enough when calling + * from overflow handler + */ + if (task != current && ctx->ctx_fl_system == 0) return -EBUSY; + + return pfm_read_pmds(ctx, req, nreq, regs); +} +EXPORT_SYMBOL(pfm_mod_read_pmds); +#endif + +/* + * Only call this function when a process it trying to + * write the debug registers (reading is always allowed) + */ +int +pfm_use_debug_registers(struct task_struct *task) +{ + pfm_context_t *ctx = task->thread.pfm_context; + unsigned long flags; + int ret = 0; + + if (pmu_conf->use_rr_dbregs == 0) return 0; + + DPRINT(("called for [%d]\n", task->pid)); + + /* + * do it only once + */ + if (task->thread.flags & IA64_THREAD_DBG_VALID) return 0; + + /* + * Even on SMP, we do not need to use an atomic here because + * the only way in is via ptrace() and this is possible only when the + * process is stopped. Even in the case where the ctxsw out is not totally + * completed by the time we come here, there is no way the 'stopped' process + * could be in the middle of fiddling with the pfm_write_ibr_dbr() routine. + * So this is always safe. + */ + if (ctx && ctx->ctx_fl_using_dbreg == 1) return -1; + + LOCK_PFS(flags); + + /* + * We cannot allow setting breakpoints when system wide monitoring + * sessions are using the debug registers. + */ + if (pfm_sessions.pfs_sys_use_dbregs> 0) + ret = -1; + else + pfm_sessions.pfs_ptrace_use_dbregs++; + + DPRINT(("ptrace_use_dbregs=%u sys_use_dbregs=%u by [%d] ret = %d\n", + pfm_sessions.pfs_ptrace_use_dbregs, + pfm_sessions.pfs_sys_use_dbregs, + task->pid, ret)); + + UNLOCK_PFS(flags); + + return ret; +} + +/* + * This function is called for every task that exits with the + * IA64_THREAD_DBG_VALID set. This indicates a task which was + * able to use the debug registers for debugging purposes via + * ptrace(). Therefore we know it was not using them for + * perfmormance monitoring, so we only decrement the number + * of "ptraced" debug register users to keep the count up to date + */ +int +pfm_release_debug_registers(struct task_struct *task) +{ + unsigned long flags; + int ret; + + if (pmu_conf->use_rr_dbregs == 0) return 0; + + LOCK_PFS(flags); + if (pfm_sessions.pfs_ptrace_use_dbregs == 0) { + printk(KERN_ERR "perfmon: invalid release for [%d] ptrace_use_dbregs=0\n", task->pid); + ret = -1; + } else { + pfm_sessions.pfs_ptrace_use_dbregs--; + ret = 0; + } + UNLOCK_PFS(flags); + + return ret; +} + +#ifndef XEN +static int +pfm_restart(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) +{ + struct task_struct *task; + pfm_buffer_fmt_t *fmt; + pfm_ovfl_ctrl_t rst_ctrl; + int state, is_system; + int ret = 0; + + state = ctx->ctx_state; + fmt = ctx->ctx_buf_fmt; + is_system = ctx->ctx_fl_system; + task = PFM_CTX_TASK(ctx); + + switch(state) { + case PFM_CTX_MASKED: + break; + case PFM_CTX_LOADED: + if (CTX_HAS_SMPL(ctx) && fmt->fmt_restart_active) break; + /* fall through */ + case PFM_CTX_UNLOADED: + case PFM_CTX_ZOMBIE: + DPRINT(("invalid state=%d\n", state)); + return -EBUSY; + default: + DPRINT(("state=%d, cannot operate (no active_restart handler)\n", state)); + return -EINVAL; + } + + /* + * In system wide and when the context is loaded, access can only happen + * when the caller is running on the CPU being monitored by the session. + * It does not have to be the owner (ctx_task) of the context per se. + */ + if (is_system && ctx->ctx_cpu != smp_processor_id()) { + DPRINT(("should be running on CPU%d\n", ctx->ctx_cpu)); + return -EBUSY; + } + + /* sanity check */ + if (unlikely(task == NULL)) { + printk(KERN_ERR "perfmon: [%d] pfm_restart no task\n", current->pid); + return -EINVAL; + } + + if (task == current || is_system) { + + fmt = ctx->ctx_buf_fmt; + + DPRINT(("restarting self %d ovfl=0x%lx\n", + task->pid, + ctx->ctx_ovfl_regs[0])); + + if (CTX_HAS_SMPL(ctx)) { + + prefetch(ctx->ctx_smpl_hdr); + + rst_ctrl.bits.mask_monitoring = 0; + rst_ctrl.bits.reset_ovfl_pmds = 0; + + if (state == PFM_CTX_LOADED) + ret = pfm_buf_fmt_restart_active(fmt, task, &rst_ctrl, ctx->ctx_smpl_hdr, regs); + else + ret = pfm_buf_fmt_restart(fmt, task, &rst_ctrl, ctx->ctx_smpl_hdr, regs); + } else { + rst_ctrl.bits.mask_monitoring = 0; + rst_ctrl.bits.reset_ovfl_pmds = 1; + } + + if (ret == 0) { + if (rst_ctrl.bits.reset_ovfl_pmds) + pfm_reset_regs(ctx, ctx->ctx_ovfl_regs, PFM_PMD_LONG_RESET); + + if (rst_ctrl.bits.mask_monitoring == 0) { + DPRINT(("resuming monitoring for [%d]\n", task->pid)); + + if (state == PFM_CTX_MASKED) pfm_restore_monitoring(task); + } else { + DPRINT(("keeping monitoring stopped for [%d]\n", task->pid)); + + // cannot use pfm_stop_monitoring(task, regs); + } + } + /* + * clear overflowed PMD mask to remove any stale information + */ + ctx->ctx_ovfl_regs[0] = 0UL; + + /* + * back to LOADED state + */ + ctx->ctx_state = PFM_CTX_LOADED; + + /* + * XXX: not really useful for self monitoring + */ + ctx->ctx_fl_can_restart = 0; + + return 0; + } + + /* + * restart another task + */ + + /* + * When PFM_CTX_MASKED, we cannot issue a restart before the previous + * one is seen by the task. + */ + if (state == PFM_CTX_MASKED) { + if (ctx->ctx_fl_can_restart == 0) return -EINVAL; + /* + * will prevent subsequent restart before this one is + * seen by other task + */ + ctx->ctx_fl_can_restart = 0; + } + + /* + * if blocking, then post the semaphore is PFM_CTX_MASKED, i.e. + * the task is blocked or on its way to block. That's the normal + * restart path. If the monitoring is not masked, then the task + * can be actively monitoring and we cannot directly intervene. + * Therefore we use the trap mechanism to catch the task and + * force it to reset the buffer/reset PMDs. + * + * if non-blocking, then we ensure that the task will go into + * pfm_handle_work() before returning to user mode. + * + * We cannot explicitely reset another task, it MUST always + * be done by the task itself. This works for system wide because + * the tool that is controlling the session is logically doing + * "self-monitoring". + */ + if (CTX_OVFL_NOBLOCK(ctx) == 0 && state == PFM_CTX_MASKED) { + DPRINT(("unblocking [%d] \n", task->pid)); + complete(&ctx->ctx_restart_done); + } else { + DPRINT(("[%d] armed exit trap\n", task->pid)); + + ctx->ctx_fl_trap_reason = PFM_TRAP_REASON_RESET; + + PFM_SET_WORK_PENDING(task, 1); + + pfm_set_task_notify(task); + + /* + * XXX: send reschedule if task runs on another CPU + */ + } + return 0; +} + +static int +pfm_debug(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) +{ + unsigned int m = *(unsigned int *)arg; + + pfm_sysctl.debug = m == 0 ? 0 : 1; + + printk(KERN_INFO "perfmon debugging %s (timing reset)\n", pfm_sysctl.debug ? "on" : "off"); + + if (m == 0) { + memset(pfm_stats, 0, sizeof(pfm_stats)); + for(m=0; m < NR_CPUS; m++) pfm_stats[m].pfm_ovfl_intr_cycles_min = ~0UL; + } + return 0; +} +#endif + +/* + * arg can be NULL and count can be zero for this function + */ +static int +pfm_write_ibr_dbr(int mode, pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) +{ +#ifndef XEN + struct thread_struct *thread = NULL; +#endif + struct task_struct *task; + pfarg_dbreg_t *req = (pfarg_dbreg_t *)arg; + unsigned long flags; + dbreg_t dbreg; + unsigned int rnum; + int first_time; + int ret = 0, state; + int i, can_access_pmu = 0; + int is_system, is_loaded; + + if (pmu_conf->use_rr_dbregs == 0) return -EINVAL; + + state = ctx->ctx_state; + is_loaded = state == PFM_CTX_LOADED ? 1 : 0; + is_system = ctx->ctx_fl_system; + task = ctx->ctx_task; +#ifdef XEN + task = NULL; + BUG_ON(regs != NULL); + /* currently dbrs, ibrs aren't supported */ + BUG(); +#endif + + if (state == PFM_CTX_ZOMBIE) return -EINVAL; + + /* + * on both UP and SMP, we can only write to the PMC when the task is + * the owner of the local PMU. + */ + if (is_loaded) { +#ifdef XEN + /* XXX */ + return -EBUSY; +#else + thread = &task->thread; + /* + * In system wide and when the context is loaded, access can only happen + * when the caller is running on the CPU being monitored by the session. + * It does not have to be the owner (ctx_task) of the context per se. + */ + if (unlikely(is_system && ctx->ctx_cpu != smp_processor_id())) { + DPRINT(("should be running on CPU%d\n", ctx->ctx_cpu)); + return -EBUSY; + } + can_access_pmu = GET_PMU_OWNER() == task || is_system ? 1 : 0; +#endif + } + + /* + * we do not need to check for ipsr.db because we do clear ibr.x, dbr.r, and dbr.w + * ensuring that no real breakpoint can be installed via this call. + * + * IMPORTANT: regs can be NULL in this function + */ + + first_time = ctx->ctx_fl_using_dbreg == 0; + + /* + * don't bother if we are loaded and task is being debugged + */ +#ifndef XEN + if (is_loaded && (thread->flags & IA64_THREAD_DBG_VALID) != 0) { + DPRINT(("debug registers already in use for [%d]\n", task->pid)); + return -EBUSY; + } +#else + /* Currently no support for is_loaded, see -EBUSY above */ +#endif + + /* + * check for debug registers in system wide mode + * + * If though a check is done in pfm_context_load(), + * we must repeat it here, in case the registers are + * written after the context is loaded + */ + if (is_loaded) { + LOCK_PFS(flags); + + if (first_time && is_system) { + if (pfm_sessions.pfs_ptrace_use_dbregs) + ret = -EBUSY; + else + pfm_sessions.pfs_sys_use_dbregs++; + } + UNLOCK_PFS(flags); + } + + if (ret != 0) return ret; + + /* + * mark ourself as user of the debug registers for + * perfmon purposes. + */ + ctx->ctx_fl_using_dbreg = 1; + + /* + * clear hardware registers to make sure we don't + * pick up stale state. + * + * for a system wide session, we do not use + * thread.dbr, thread.ibr because this process + * never leaves the current CPU and the state + * is shared by all processes running on it + */ + if (first_time && can_access_pmu) { +#ifndef XEN + DPRINT(("[%d] clearing ibrs, dbrs\n", task->pid)); +#endif + for (i=0; i < pmu_conf->num_ibrs; i++) { + ia64_set_ibr(i, 0UL); + ia64_dv_serialize_instruction(); + } + ia64_srlz_i(); + for (i=0; i < pmu_conf->num_dbrs; i++) { + ia64_set_dbr(i, 0UL); + ia64_dv_serialize_data(); + } + ia64_srlz_d(); + } + + /* + * Now install the values into the registers + */ + for (i = 0; i < count; i++, req++) { + + rnum = req->dbreg_num; + dbreg.val = req->dbreg_value; + + ret = -EINVAL; + + if ((mode == PFM_CODE_RR && rnum >= PFM_NUM_IBRS) || ((mode == PFM_DATA_RR) && rnum >= PFM_NUM_DBRS)) { + DPRINT(("invalid register %u val=0x%lx mode=%d i=%d count=%d\n", + rnum, dbreg.val, mode, i, count)); + + goto abort_mission; + } + + /* + * make sure we do not install enabled breakpoint + */ + if (rnum & 0x1) { + if (mode == PFM_CODE_RR) + dbreg.ibr.ibr_x = 0; + else + dbreg.dbr.dbr_r = dbreg.dbr.dbr_w = 0; + } + + PFM_REG_RETFLAG_SET(req->dbreg_flags, 0); + + /* + * Debug registers, just like PMC, can only be modified + * by a kernel call. Moreover, perfmon() access to those + * registers are centralized in this routine. The hardware + * does not modify the value of these registers, therefore, + * if we save them as they are written, we can avoid having + * to save them on context switch out. This is made possible + * by the fact that when perfmon uses debug registers, ptrace() + * won't be able to modify them concurrently. + */ + if (mode == PFM_CODE_RR) { + CTX_USED_IBR(ctx, rnum); + + if (can_access_pmu) { + ia64_set_ibr(rnum, dbreg.val); + ia64_dv_serialize_instruction(); + } + + ctx->ctx_ibrs[rnum] = dbreg.val; + + DPRINT(("write ibr%u=0x%lx used_ibrs=0x%x ld=%d apmu=%d\n", + rnum, dbreg.val, ctx->ctx_used_ibrs[0], is_loaded, can_access_pmu)); + } else { + CTX_USED_DBR(ctx, rnum); + + if (can_access_pmu) { + ia64_set_dbr(rnum, dbreg.val); + ia64_dv_serialize_data(); + } + ctx->ctx_dbrs[rnum] = dbreg.val; + + DPRINT(("write dbr%u=0x%lx used_dbrs=0x%x ld=%d apmu=%d\n", + rnum, dbreg.val, ctx->ctx_used_dbrs[0], is_loaded, can_access_pmu)); + } + } + + return 0; + +abort_mission: + /* + * in case it was our first attempt, we undo the global modifications + */ + if (first_time) { + LOCK_PFS(flags); + if (ctx->ctx_fl_system) { + pfm_sessions.pfs_sys_use_dbregs--; + } + UNLOCK_PFS(flags); + ctx->ctx_fl_using_dbreg = 0; + } + /* + * install error return flag + */ + PFM_REG_RETFLAG_SET(req->dbreg_flags, PFM_REG_RETFL_EINVAL); + + return ret; +} + +static int +pfm_write_ibrs(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) +{ + return pfm_write_ibr_dbr(PFM_CODE_RR, ctx, arg, count, regs); +} + +static int +pfm_write_dbrs(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) +{ + return pfm_write_ibr_dbr(PFM_DATA_RR, ctx, arg, count, regs); +} + +int +pfm_mod_write_ibrs(struct task_struct *task, void *req, unsigned int nreq, struct pt_regs *regs) +{ + pfm_context_t *ctx; + + if (req == NULL) return -EINVAL; + + ctx = GET_PMU_CTX(); + + if (ctx == NULL) return -EINVAL; + + /* + * for now limit to current task, which is enough when calling + * from overflow handler + */ + if (task != current && ctx->ctx_fl_system == 0) return -EBUSY; + + return pfm_write_ibrs(ctx, req, nreq, regs); +} +EXPORT_SYMBOL(pfm_mod_write_ibrs); + +int +pfm_mod_write_dbrs(struct task_struct *task, void *req, unsigned int nreq, struct pt_regs *regs) +{ + pfm_context_t *ctx; + + if (req == NULL) return -EINVAL; + + ctx = GET_PMU_CTX(); + + if (ctx == NULL) return -EINVAL; + + /* + * for now limit to current task, which is enough when calling + * from overflow handler + */ + if (task != current && ctx->ctx_fl_system == 0) return -EBUSY; + + return pfm_write_dbrs(ctx, req, nreq, regs); +} +EXPORT_SYMBOL(pfm_mod_write_dbrs); + + +static int +pfm_get_features(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) +{ + pfarg_features_t *req = (pfarg_features_t *)arg; + + req->ft_version = PFM_VERSION; + return 0; +} + +#ifndef XEN +static int +pfm_stop(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) +{ + struct pt_regs *tregs; + struct task_struct *task = PFM_CTX_TASK(ctx); + int state, is_system; + + state = ctx->ctx_state; + is_system = ctx->ctx_fl_system; + + /* + * context must be attached to issue the stop command (includes LOADED,MASKED,ZOMBIE) + */ + if (state == PFM_CTX_UNLOADED) return -EINVAL; + + /* + * In system wide and when the context is loaded, access can only happen + * when the caller is running on the CPU being monitored by the session. + * It does not have to be the owner (ctx_task) of the context per se. + */ + if (is_system && ctx->ctx_cpu != smp_processor_id()) { + DPRINT(("should be running on CPU%d\n", ctx->ctx_cpu)); + return -EBUSY; + } + DPRINT(("task [%d] ctx_state=%d is_system=%d\n", + PFM_CTX_TASK(ctx)->pid, + state, + is_system)); + /* + * in system mode, we need to update the PMU directly + * and the user level state of the caller, which may not + * necessarily be the creator of the context. + */ + if (is_system) { + /* + * Update local PMU first + * + * disable dcr pp + */ + ia64_setreg(_IA64_REG_CR_DCR, ia64_getreg(_IA64_REG_CR_DCR) & ~IA64_DCR_PP); + ia64_srlz_i(); + + /* + * update local cpuinfo + */ + PFM_CPUINFO_CLEAR(PFM_CPUINFO_DCR_PP); + + /* + * stop monitoring, does srlz.i + */ + pfm_clear_psr_pp(); + + /* + * stop monitoring in the caller + */ + ia64_psr(regs)->pp = 0; + + return 0; + } + /* + * per-task mode + */ + + if (task == current) { + /* stop monitoring at kernel level */ + pfm_clear_psr_up(); + + /* + * stop monitoring at the user level + */ + ia64_psr(regs)->up = 0; + } else { + tregs = task_pt_regs(task); + + /* + * stop monitoring at the user level + */ + ia64_psr(tregs)->up = 0; + + /* + * monitoring disabled in kernel at next reschedule + */ + ctx->ctx_saved_psr_up = 0; + DPRINT(("task=[%d]\n", task->pid)); + } + return 0; +} + + +static int +pfm_start(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) +{ + struct pt_regs *tregs; + int state, is_system; + + state = ctx->ctx_state; + is_system = ctx->ctx_fl_system; + + if (state != PFM_CTX_LOADED) return -EINVAL; + + /* + * In system wide and when the context is loaded, access can only happen + * when the caller is running on the CPU being monitored by the session. + * It does not have to be the owner (ctx_task) of the context per se. + */ + if (is_system && ctx->ctx_cpu != smp_processor_id()) { + DPRINT(("should be running on CPU%d\n", ctx->ctx_cpu)); + return -EBUSY; + } + + /* + * in system mode, we need to update the PMU directly + * and the user level state of the caller, which may not + * necessarily be the creator of the context. + */ + if (is_system) { + + /* + * set user level psr.pp for the caller + */ + ia64_psr(regs)->pp = 1; + + /* + * now update the local PMU and cpuinfo + */ + PFM_CPUINFO_SET(PFM_CPUINFO_DCR_PP); + + /* + * start monitoring at kernel level + */ + pfm_set_psr_pp(); + + /* enable dcr pp */ + ia64_setreg(_IA64_REG_CR_DCR, ia64_getreg(_IA64_REG_CR_DCR) | IA64_DCR_PP); + ia64_srlz_i(); + + return 0; + } + + /* + * per-process mode + */ + + if (ctx->ctx_task == current) { + + /* start monitoring at kernel level */ + pfm_set_psr_up(); + + /* + * activate monitoring at user level + */ + ia64_psr(regs)->up = 1; + + } else { + tregs = task_pt_regs(ctx->ctx_task); + + /* + * start monitoring at the kernel level the next + * time the task is scheduled + */ + ctx->ctx_saved_psr_up = IA64_PSR_UP; + + /* + * activate monitoring at user level + */ + ia64_psr(tregs)->up = 1; + } + return 0; +} + +static int +pfm_get_pmc_reset(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) +{ + pfarg_reg_t *req = (pfarg_reg_t *)arg; + unsigned int cnum; + int i; + int ret = -EINVAL; + + for (i = 0; i < count; i++, req++) { + + cnum = req->reg_num; + + if (!PMC_IS_IMPL(cnum)) goto abort_mission; + + req->reg_value = PMC_DFL_VAL(cnum); + + PFM_REG_RETFLAG_SET(req->reg_flags, 0); + + DPRINT(("pmc_reset_val pmc[%u]=0x%lx\n", cnum, req->reg_value)); + } + return 0; + +abort_mission: + PFM_REG_RETFLAG_SET(req->reg_flags, PFM_REG_RETFL_EINVAL); + return ret; +} + +static int +pfm_check_task_exist(pfm_context_t *ctx) +{ + struct task_struct *g, *t; + int ret = -ESRCH; + + read_lock(&tasklist_lock); + + do_each_thread (g, t) { + if (t->thread.pfm_context == ctx) { + ret = 0; + break; + } + } while_each_thread (g, t); + + read_unlock(&tasklist_lock); + + DPRINT(("pfm_check_task_exist: ret=%d ctx=%p\n", ret, ctx)); + + return ret; +} +#endif + +static int +pfm_context_load(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) +{ + struct task_struct *task; +#ifndef XEN + struct thread_struct *thread; +#endif + struct pfm_context_t *old; + unsigned long flags; +#ifndef CONFIG_SMP + struct task_struct *owner_task = NULL; +#endif + pfarg_load_t *req = (pfarg_load_t *)arg; + unsigned long *pmcs_source, *pmds_source; + int the_cpu; + int ret = 0; + int state, is_system, set_dbregs = 0; + + state = ctx->ctx_state; + is_system = ctx->ctx_fl_system; +#ifdef XEN + task = NULL; + old = NULL; + pmcs_source = pmds_source = NULL; +#ifndef CONFIG_SMP + owner_task = NULL; +#endif + flags = 0; + BUG_ON(count != 0); + BUG_ON(regs != NULL); +#endif + /* + * can only load from unloaded or terminated state + */ + if (state != PFM_CTX_UNLOADED) { + DPRINT(("cannot load to [%d], invalid ctx_state=%d\n", + req->load_pid, + ctx->ctx_state)); + return -EBUSY; + } + +#ifndef XEN + DPRINT(("load_pid [%d] using_dbreg=%d\n", req->load_pid, ctx->ctx_fl_using_dbreg)); + + if (CTX_OVFL_NOBLOCK(ctx) == 0 && req->load_pid == current->pid) { + DPRINT(("cannot use blocking mode on self\n")); + return -EINVAL; + } + + ret = pfm_get_task(ctx, req->load_pid, &task); + if (ret) { + DPRINT(("load_pid [%d] get_task=%d\n", req->load_pid, ret)); + return ret; + } + + ret = -EINVAL; + + /* + * system wide is self monitoring only + */ + if (is_system && task != current) { + DPRINT(("system wide is self monitoring only load_pid=%d\n", + req->load_pid)); + goto error; + } + + thread = &task->thread; +#else + BUG_ON(!spin_is_locked(&ctx->ctx_lock)); + if (!is_system) { + ret = -EINVAL; + goto error; + } +#endif + + ret = 0; +#ifndef XEN + /* + * cannot load a context which is using range restrictions, + * into a task that is being debugged. + */ + if (ctx->ctx_fl_using_dbreg) { + if (thread->flags & IA64_THREAD_DBG_VALID) { + ret = -EBUSY; + DPRINT(("load_pid [%d] task is debugged, cannot load range restrictions\n", req->load_pid)); + goto error; + } + LOCK_PFS(flags); + + if (is_system) { + if (pfm_sessions.pfs_ptrace_use_dbregs) { + DPRINT(("cannot load [%d] dbregs in use\n", task->pid)); + ret = -EBUSY; + } else { + pfm_sessions.pfs_sys_use_dbregs++; + DPRINT(("load [%d] increased sys_use_dbreg=%u\n", task->pid, pfm_sessions.pfs_sys_use_dbregs)); + set_dbregs = 1; + } + } + + UNLOCK_PFS(flags); + + if (ret) goto error; + } +#else + BUG_ON(ctx->ctx_fl_using_dbreg); +#endif + + /* + * SMP system-wide monitoring implies self-monitoring. + * + * The programming model expects the task to + * be pinned on a CPU throughout the session. + * Here we take note of the current CPU at the + * time the context is loaded. No call from + * another CPU will be allowed. + * + * The pinning via shed_setaffinity() + * must be done by the calling task prior + * to this call. + * + * systemwide: keep track of CPU this session is supposed to run on + */ + the_cpu = ctx->ctx_cpu = smp_processor_id(); + + ret = -EBUSY; + /* + * now reserve the session + */ + ret = pfm_reserve_session(current, is_system, the_cpu); + if (ret) goto error; + + /* + * task is necessarily stopped at this point. + * + * If the previous context was zombie, then it got removed in + * pfm_save_regs(). Therefore we should not see it here. + * If we see a context, then this is an active context + * + * XXX: needs to be atomic + */ +#ifndef XEN + DPRINT(("before cmpxchg() old_ctx=%p new_ctx=%p\n", + thread->pfm_context, ctx)); + + ret = -EBUSY; + old = ia64_cmpxchg(acq, &thread->pfm_context, NULL, ctx, sizeof(pfm_context_t *)); + if (old != NULL) { + DPRINT(("load_pid [%d] already has a context\n", req->load_pid)); + goto error_unres; + } + + pfm_reset_msgq(ctx); +#endif + + ctx->ctx_state = PFM_CTX_LOADED; + + /* + * link context to task + */ + ctx->ctx_task = task; + + if (is_system) { + /* + * we load as stopped + */ + PFM_CPUINFO_SET(PFM_CPUINFO_SYST_WIDE); + PFM_CPUINFO_CLEAR(PFM_CPUINFO_DCR_PP); + + if (ctx->ctx_fl_excl_idle) PFM_CPUINFO_SET(PFM_CPUINFO_EXCL_IDLE); + } else { +#ifndef XEN + thread->flags |= IA64_THREAD_PM_VALID; +#else + BUG(); +#endif + } + +#ifndef XEN + /* + * propagate into thread-state + */ + pfm_copy_pmds(task, ctx); + pfm_copy_pmcs(task, ctx); + + pmcs_source = thread->pmcs; + pmds_source = thread->pmds; + + /* + * always the case for system-wide + */ + if (task == current) { + + if (is_system == 0) { + + /* allow user level control */ + ia64_psr(regs)->sp = 0; + DPRINT(("clearing psr.sp for [%d]\n", task->pid)); + + SET_LAST_CPU(ctx, smp_processor_id()); + INC_ACTIVATION(); + SET_ACTIVATION(ctx); +#ifndef CONFIG_SMP + /* + * push the other task out, if any + */ + owner_task = GET_PMU_OWNER(); + if (owner_task) pfm_lazy_save_regs(owner_task); +#endif + } + /* + * load all PMD from ctx to PMU (as opposed to thread state) + * restore all PMC from ctx to PMU + */ + pfm_restore_pmds(pmds_source, ctx->ctx_all_pmds[0]); + pfm_restore_pmcs(pmcs_source, ctx->ctx_all_pmcs[0]); + + ctx->ctx_reload_pmcs[0] = 0UL; + ctx->ctx_reload_pmds[0] = 0UL; + + /* + * guaranteed safe by earlier check against DBG_VALID + */ + if (ctx->ctx_fl_using_dbreg) { + pfm_restore_ibrs(ctx->ctx_ibrs, pmu_conf->num_ibrs); + pfm_restore_dbrs(ctx->ctx_dbrs, pmu_conf->num_dbrs); + } + /* + * set new ownership + */ + SET_PMU_OWNER(task, ctx); + + DPRINT(("context loaded on PMU for [%d]\n", task->pid)); + } else { + /* + * when not current, task MUST be stopped, so this is safe + */ + regs = task_pt_regs(task); + + /* force a full reload */ + ctx->ctx_last_activation = PFM_INVALID_ACTIVATION; + SET_LAST_CPU(ctx, -1); + + /* initial saved psr (stopped) */ + ctx->ctx_saved_psr_up = 0UL; + ia64_psr(regs)->up = ia64_psr(regs)->pp = 0; + } +#else + BUG_ON(!is_system); + + /* load pmds, pmcs */ + xenpfm_restore_pmds(ctx); + xenpfm_restore_pmcs(ctx); + + ctx->ctx_reload_pmcs[0] = 0UL; + ctx->ctx_reload_pmds[0] = 0UL; + + BUG_ON(ctx->ctx_fl_using_dbreg); + + SET_PMU_OWNER(NULL, ctx); +#endif + + ret = 0; + +#ifndef XEN +error_unres: + if (ret) pfm_unreserve_session(ctx, ctx->ctx_fl_system, the_cpu); +#endif +error: +#ifndef XEN + /* + * we must undo the dbregs setting (for system-wide) + */ + if (ret && set_dbregs) { + LOCK_PFS(flags); + pfm_sessions.pfs_sys_use_dbregs--; + UNLOCK_PFS(flags); + } + /* + * release task, there is now a link with the context + */ + if (is_system == 0 && task != current) { + pfm_put_task(task); + + if (ret == 0) { + ret = pfm_check_task_exist(ctx); + if (ret) { + ctx->ctx_state = PFM_CTX_UNLOADED; + ctx->ctx_task = NULL; + } + } + } +#else + BUG_ON(set_dbregs); +#endif + return ret; +} + +/* + * in this function, we do not need to increase the use count + * for the task via get_task_struct(), because we hold the + * context lock. If the task were to disappear while having + * a context attached, it would go through pfm_exit_thread() + * which also grabs the context lock and would therefore be blocked + * until we are here. + */ +static void pfm_flush_pmds(struct task_struct *, pfm_context_t *ctx); + +static int +pfm_context_unload(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) +{ + struct task_struct *task = PFM_CTX_TASK(ctx); + struct pt_regs *tregs; + int prev_state, is_system; + int ret; + +#ifndef XEN + DPRINT(("ctx_state=%d task [%d]\n", ctx->ctx_state, task ? task->pid : -1)); +#else + task = NULL; + tregs = NULL; + BUG_ON(arg != NULL); + BUG_ON(count != 0); + BUG_ON(regs != NULL); +#endif + + prev_state = ctx->ctx_state; + is_system = ctx->ctx_fl_system; + + /* + * unload only when necessary + */ + if (prev_state == PFM_CTX_UNLOADED) { + DPRINT(("ctx_state=%d, nothing to do\n", prev_state)); + return 0; + } + + /* + * clear psr and dcr bits + */ +#ifndef XEN + ret = pfm_stop(ctx, NULL, 0, regs); + if (ret) return ret; +#else + /* caller does it by hand */ + ret = 0; +#endif + + ctx->ctx_state = PFM_CTX_UNLOADED; + + /* + * in system mode, we need to update the PMU directly + * and the user level state of the caller, which may not + * necessarily be the creator of the context. + */ + if (is_system) { + + /* + * Update cpuinfo + * + * local PMU is taken care of in pfm_stop() + */ + PFM_CPUINFO_CLEAR(PFM_CPUINFO_SYST_WIDE); + PFM_CPUINFO_CLEAR(PFM_CPUINFO_EXCL_IDLE); + + /* + * save PMDs in context + * release ownership + */ + pfm_flush_pmds(current, ctx); + + /* + * at this point we are done with the PMU + * so we can unreserve the resource. + */ + if (prev_state != PFM_CTX_ZOMBIE) + pfm_unreserve_session(ctx, 1 , ctx->ctx_cpu); + +#ifndef XEN + /* + * disconnect context from task + */ + task->thread.pfm_context = NULL; +#endif + /* + * disconnect task from context + */ + ctx->ctx_task = NULL; + + /* + * There is nothing more to cleanup here. + */ + return 0; + } + +#ifndef XEN + /* + * per-task mode + */ + tregs = task == current ? regs : task_pt_regs(task); + + if (task == current) { + /* + * cancel user level control + */ + ia64_psr(regs)->sp = 1; + + DPRINT(("setting psr.sp for [%d]\n", task->pid)); + } + /* + * save PMDs to context + * release ownership + */ + pfm_flush_pmds(task, ctx); + + /* + * at this point we are done with the PMU + * so we can unreserve the resource. + * + * when state was ZOMBIE, we have already unreserved. + */ + if (prev_state != PFM_CTX_ZOMBIE) + pfm_unreserve_session(ctx, 0 , ctx->ctx_cpu); + + /* + * reset activation counter and psr + */ + ctx->ctx_last_activation = PFM_INVALID_ACTIVATION; + SET_LAST_CPU(ctx, -1); + + /* + * PMU state will not be restored + */ + task->thread.flags &= ~IA64_THREAD_PM_VALID; + + /* + * break links between context and task + */ + task->thread.pfm_context = NULL; + ctx->ctx_task = NULL; + + PFM_SET_WORK_PENDING(task, 0); + + ctx->ctx_fl_trap_reason = PFM_TRAP_REASON_NONE; + ctx->ctx_fl_can_restart = 0; + ctx->ctx_fl_going_zombie = 0; + + DPRINT(("disconnected [%d] from context\n", task->pid)); + + return 0; +#else + BUG(); + return -EINVAL; +#endif +} + + +#ifndef XEN +/* + * called only from exit_thread(): task == current + * we come here only if current has a context attached (loaded or masked) + */ +void +pfm_exit_thread(struct task_struct *task) +{ + pfm_context_t *ctx; + unsigned long flags; + struct pt_regs *regs = task_pt_regs(task); + int ret, state; + int free_ok = 0; + + ctx = PFM_GET_CTX(task); + + PROTECT_CTX(ctx, flags); + + DPRINT(("state=%d task [%d]\n", ctx->ctx_state, task->pid)); + + state = ctx->ctx_state; + switch(state) { + case PFM_CTX_UNLOADED: + /* + * only comes to thios function if pfm_context is not NULL, i.e., cannot + * be in unloaded state + */ + printk(KERN_ERR "perfmon: pfm_exit_thread [%d] ctx unloaded\n", task->pid); + break; + case PFM_CTX_LOADED: + case PFM_CTX_MASKED: + ret = pfm_context_unload(ctx, NULL, 0, regs); + if (ret) { + printk(KERN_ERR "perfmon: pfm_exit_thread [%d] state=%d unload failed %d\n", task->pid, state, ret); + } + DPRINT(("ctx unloaded for current state was %d\n", state)); + + pfm_end_notify_user(ctx); + break; + case PFM_CTX_ZOMBIE: + ret = pfm_context_unload(ctx, NULL, 0, regs); + if (ret) { + printk(KERN_ERR "perfmon: pfm_exit_thread [%d] state=%d unload failed %d\n", task->pid, state, ret); + } + free_ok = 1; + break; + default: + printk(KERN_ERR "perfmon: pfm_exit_thread [%d] unexpected state=%d\n", task->pid, state); + break; + } + UNPROTECT_CTX(ctx, flags); + + { u64 psr = pfm_get_psr(); + BUG_ON(psr & (IA64_PSR_UP|IA64_PSR_PP)); + BUG_ON(GET_PMU_OWNER()); + BUG_ON(ia64_psr(regs)->up); + BUG_ON(ia64_psr(regs)->pp); + } + + /* + * All memory free operations (especially for vmalloc'ed memory) + * MUST be done with interrupts ENABLED. + */ + if (free_ok) pfm_context_free(ctx); +} + +/* + * functions MUST be listed in the increasing order of their index (see permfon.h) + */ +#define PFM_CMD(name, flags, arg_count, arg_type, getsz) { name, #name, flags, arg_count, sizeof(arg_type), getsz } +#define PFM_CMD_S(name, flags) { name, #name, flags, 0, 0, NULL } +#define PFM_CMD_PCLRWS (PFM_CMD_FD|PFM_CMD_ARG_RW|PFM_CMD_STOP) +#define PFM_CMD_PCLRW (PFM_CMD_FD|PFM_CMD_ARG_RW) +#define PFM_CMD_NONE { NULL, "no-cmd", 0, 0, 0, NULL} + +static pfm_cmd_desc_t pfm_cmd_tab[]={ +/* 0 */PFM_CMD_NONE, +/* 1 */PFM_CMD(pfm_write_pmcs, PFM_CMD_PCLRWS, PFM_CMD_ARG_MANY, pfarg_reg_t, NULL), +/* 2 */PFM_CMD(pfm_write_pmds, PFM_CMD_PCLRWS, PFM_CMD_ARG_MANY, pfarg_reg_t, NULL), +/* 3 */PFM_CMD(pfm_read_pmds, PFM_CMD_PCLRWS, PFM_CMD_ARG_MANY, pfarg_reg_t, NULL), +/* 4 */PFM_CMD_S(pfm_stop, PFM_CMD_PCLRWS), +/* 5 */PFM_CMD_S(pfm_start, PFM_CMD_PCLRWS), +/* 6 */PFM_CMD_NONE, +/* 7 */PFM_CMD_NONE, +/* 8 */PFM_CMD(pfm_context_create, PFM_CMD_ARG_RW, 1, pfarg_context_t, pfm_ctx_getsize), +/* 9 */PFM_CMD_NONE, +/* 10 */PFM_CMD_S(pfm_restart, PFM_CMD_PCLRW), +/* 11 */PFM_CMD_NONE, +/* 12 */PFM_CMD(pfm_get_features, PFM_CMD_ARG_RW, 1, pfarg_features_t, NULL), +/* 13 */PFM_CMD(pfm_debug, 0, 1, unsigned int, NULL), +/* 14 */PFM_CMD_NONE, +/* 15 */PFM_CMD(pfm_get_pmc_reset, PFM_CMD_ARG_RW, PFM_CMD_ARG_MANY, pfarg_reg_t, NULL), +/* 16 */PFM_CMD(pfm_context_load, PFM_CMD_PCLRWS, 1, pfarg_load_t, NULL), +/* 17 */PFM_CMD_S(pfm_context_unload, PFM_CMD_PCLRWS), +/* 18 */PFM_CMD_NONE, +/* 19 */PFM_CMD_NONE, +/* 20 */PFM_CMD_NONE, +/* 21 */PFM_CMD_NONE, +/* 22 */PFM_CMD_NONE, +/* 23 */PFM_CMD_NONE, +/* 24 */PFM_CMD_NONE, +/* 25 */PFM_CMD_NONE, +/* 26 */PFM_CMD_NONE, +/* 27 */PFM_CMD_NONE, +/* 28 */PFM_CMD_NONE, +/* 29 */PFM_CMD_NONE, +/* 30 */PFM_CMD_NONE, +/* 31 */PFM_CMD_NONE, +/* 32 */PFM_CMD(pfm_write_ibrs, PFM_CMD_PCLRWS, PFM_CMD_ARG_MANY, pfarg_dbreg_t, NULL), +/* 33 */PFM_CMD(pfm_write_dbrs, PFM_CMD_PCLRWS, PFM_CMD_ARG_MANY, pfarg_dbreg_t, NULL) +}; +#define PFM_CMD_COUNT (sizeof(pfm_cmd_tab)/sizeof(pfm_cmd_desc_t)) + +static int +pfm_check_task_state(pfm_context_t *ctx, int cmd, unsigned long flags) +{ + struct task_struct *task; + int state, old_state; + +recheck: + state = ctx->ctx_state; + task = ctx->ctx_task; + + if (task == NULL) { + DPRINT(("context %d no task, state=%d\n", ctx->ctx_fd, state)); + return 0; + } + + DPRINT(("context %d state=%d [%d] task_state=%ld must_stop=%d\n", + ctx->ctx_fd, + state, + task->pid, + task->state, PFM_CMD_STOPPED(cmd))); + + /* + * self-monitoring always ok. + * + * for system-wide the caller can either be the creator of the + * context (to one to which the context is attached to) OR + * a task running on the same CPU as the session. + */ + if (task == current || ctx->ctx_fl_system) return 0; + + /* + * we are monitoring another thread + */ + switch(state) { + case PFM_CTX_UNLOADED: + /* + * if context is UNLOADED we are safe to go + */ + return 0; + case PFM_CTX_ZOMBIE: + /* + * no command can operate on a zombie context + */ + DPRINT(("cmd %d state zombie cannot operate on context\n", cmd)); + return -EINVAL; + case PFM_CTX_MASKED: + /* + * PMU state has been saved to software even though + * the thread may still be running. + */ + if (cmd != PFM_UNLOAD_CONTEXT) return 0; + } + + /* + * context is LOADED or MASKED. Some commands may need to have + * the task stopped. + * + * We could lift this restriction for UP but it would mean that + * the user has no guarantee the task would not run between + * two successive calls to perfmonctl(). That's probably OK. + * If this user wants to ensure the task does not run, then + * the task must be stopped. + */ + if (PFM_CMD_STOPPED(cmd)) { + if ((task->state != TASK_STOPPED) && (task->state != TASK_TRACED)) { + DPRINT(("[%d] task not in stopped state\n", task->pid)); + return -EBUSY; + } + /* + * task is now stopped, wait for ctxsw out + * + * This is an interesting point in the code. + * We need to unprotect the context because + * the pfm_save_regs() routines needs to grab + * the same lock. There are danger in doing + * this because it leaves a window open for + * another task to get access to the context + * and possibly change its state. The one thing + * that is not possible is for the context to disappear + * because we are protected by the VFS layer, i.e., + * get_fd()/put_fd(). + */ + old_state = state; + + UNPROTECT_CTX(ctx, flags); + + wait_task_inactive(task); + + PROTECT_CTX(ctx, flags); + + /* + * we must recheck to verify if state has changed + */ + if (ctx->ctx_state != old_state) { + DPRINT(("old_state=%d new_state=%d\n", old_state, ctx->ctx_state)); + goto recheck; + } + } + return 0; +} + +/* + * system-call entry point (must return long) + */ +asmlinkage long +sys_perfmonctl (int fd, int cmd, void __user *arg, int count) +{ + struct file *file = NULL; + pfm_context_t *ctx = NULL; + unsigned long flags = 0UL; + void *args_k = NULL; + long ret; /* will expand int return types */ + size_t base_sz, sz, xtra_sz = 0; + int narg, completed_args = 0, call_made = 0, cmd_flags; + int (*func)(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs); + int (*getsize)(void *arg, size_t *sz); +#define PFM_MAX_ARGSIZE 4096 + + /* + * reject any call if perfmon was disabled at initialization + */ + if (unlikely(pmu_conf == NULL)) return -ENOSYS; + + if (unlikely(cmd < 0 || cmd >= PFM_CMD_COUNT)) { + DPRINT(("invalid cmd=%d\n", cmd)); + return -EINVAL; + } + + func = pfm_cmd_tab[cmd].cmd_func; + narg = pfm_cmd_tab[cmd].cmd_narg; + base_sz = pfm_cmd_tab[cmd].cmd_argsize; + getsize = pfm_cmd_tab[cmd].cmd_getsize; + cmd_flags = pfm_cmd_tab[cmd].cmd_flags; + + if (unlikely(func == NULL)) { + DPRINT(("invalid cmd=%d\n", cmd)); + return -EINVAL; + } + + DPRINT(("cmd=%s idx=%d narg=0x%x argsz=%lu count=%d\n", + PFM_CMD_NAME(cmd), + cmd, + narg, + base_sz, + count)); + + /* + * check if number of arguments matches what the command expects + */ + if (unlikely((narg == PFM_CMD_ARG_MANY && count <= 0) || (narg > 0 && narg != count))) + return -EINVAL; + +restart_args: + sz = xtra_sz + base_sz*count; + /* + * limit abuse to min page size + */ + if (unlikely(sz > PFM_MAX_ARGSIZE)) { + printk(KERN_ERR "perfmon: [%d] argument too big %lu\n", current->pid, sz); + return -E2BIG; + } + + /* + * allocate default-sized argument buffer + */ + if (likely(count && args_k == NULL)) { + args_k = kmalloc(PFM_MAX_ARGSIZE, GFP_KERNEL); + if (args_k == NULL) return -ENOMEM; + } + + ret = -EFAULT; + + /* + * copy arguments + * + * assume sz = 0 for command without parameters + */ + if (sz && copy_from_user(args_k, arg, sz)) { + DPRINT(("cannot copy_from_user %lu bytes @%p\n", sz, arg)); + goto error_args; + } + + /* + * check if command supports extra parameters + */ + if (completed_args == 0 && getsize) { + /* + * get extra parameters size (based on main argument) + */ + ret = (*getsize)(args_k, &xtra_sz); + if (ret) goto error_args; + + completed_args = 1; + + DPRINT(("restart_args sz=%lu xtra_sz=%lu\n", sz, xtra_sz)); + + /* retry if necessary */ + if (likely(xtra_sz)) goto restart_args; + } + + if (unlikely((cmd_flags & PFM_CMD_FD) == 0)) goto skip_fd; + + ret = -EBADF; + + file = fget(fd); + if (unlikely(file == NULL)) { + DPRINT(("invalid fd %d\n", fd)); + goto error_args; + } + if (unlikely(PFM_IS_FILE(file) == 0)) { + DPRINT(("fd %d not related to perfmon\n", fd)); + goto error_args; + } + + ctx = (pfm_context_t *)file->private_data; + if (unlikely(ctx == NULL)) { + DPRINT(("no context for fd %d\n", fd)); + goto error_args; + } + prefetch(&ctx->ctx_state); + + PROTECT_CTX(ctx, flags); + + /* + * check task is stopped + */ + ret = pfm_check_task_state(ctx, cmd, flags); + if (unlikely(ret)) goto abort_locked; + +skip_fd: + ret = (*func)(ctx, args_k, count, task_pt_regs(current)); + + call_made = 1; + +abort_locked: + if (likely(ctx)) { + DPRINT(("context unlocked\n")); + UNPROTECT_CTX(ctx, flags); + } + + /* copy argument back to user, if needed */ + if (call_made && PFM_CMD_RW_ARG(cmd) && copy_to_user(arg, args_k, base_sz*count)) ret = -EFAULT; + +error_args: + if (file) + fput(file); + + kfree(args_k); + + DPRINT(("cmd=%s ret=%ld\n", PFM_CMD_NAME(cmd), ret)); + + return ret; +} + +static void +pfm_resume_after_ovfl(pfm_context_t *ctx, unsigned long ovfl_regs, struct pt_regs *regs) +{ + pfm_buffer_fmt_t *fmt = ctx->ctx_buf_fmt; + pfm_ovfl_ctrl_t rst_ctrl; + int state; + int ret = 0; + + state = ctx->ctx_state; + /* + * Unlock sampling buffer and reset index atomically + * XXX: not really needed when blocking + */ + if (CTX_HAS_SMPL(ctx)) { + + rst_ctrl.bits.mask_monitoring = 0; + rst_ctrl.bits.reset_ovfl_pmds = 0; + + if (state == PFM_CTX_LOADED) + ret = pfm_buf_fmt_restart_active(fmt, current, &rst_ctrl, ctx->ctx_smpl_hdr, regs); + else + ret = pfm_buf_fmt_restart(fmt, current, &rst_ctrl, ctx->ctx_smpl_hdr, regs); + } else { + rst_ctrl.bits.mask_monitoring = 0; + rst_ctrl.bits.reset_ovfl_pmds = 1; + } + + if (ret == 0) { + if (rst_ctrl.bits.reset_ovfl_pmds) { + pfm_reset_regs(ctx, &ovfl_regs, PFM_PMD_LONG_RESET); + } + if (rst_ctrl.bits.mask_monitoring == 0) { + DPRINT(("resuming monitoring\n")); + if (ctx->ctx_state == PFM_CTX_MASKED) pfm_restore_monitoring(current); + } else { + DPRINT(("stopping monitoring\n")); + //pfm_stop_monitoring(current, regs); + } + ctx->ctx_state = PFM_CTX_LOADED; + } +} + +/* + * context MUST BE LOCKED when calling + * can only be called for current + */ +static void +pfm_context_force_terminate(pfm_context_t *ctx, struct pt_regs *regs) +{ + int ret; + + DPRINT(("entering for [%d]\n", current->pid)); + + ret = pfm_context_unload(ctx, NULL, 0, regs); + if (ret) { + printk(KERN_ERR "pfm_context_force_terminate: [%d] unloaded failed with %d\n", current->pid, ret); + } + + /* + * and wakeup controlling task, indicating we are now disconnected + */ + wake_up_interruptible(&ctx->ctx_zombieq); + + /* + * given that context is still locked, the controlling + * task will only get access when we return from + * pfm_handle_work(). + */ +} + +static int pfm_ovfl_notify_user(pfm_context_t *ctx, unsigned long ovfl_pmds); + /* + * pfm_handle_work() can be called with interrupts enabled + * (TIF_NEED_RESCHED) or disabled. The down_interruptible + * call may sleep, therefore we must re-enable interrupts + * to avoid deadlocks. It is safe to do so because this function + * is called ONLY when returning to user level (PUStk=1), in which case + * there is no risk of kernel stack overflow due to deep + * interrupt nesting. + */ +void +pfm_handle_work(void) +{ + pfm_context_t *ctx; + struct pt_regs *regs; + unsigned long flags, dummy_flags; + unsigned long ovfl_regs; + unsigned int reason; + int ret; + + ctx = PFM_GET_CTX(current); + if (ctx == NULL) { + printk(KERN_ERR "perfmon: [%d] has no PFM context\n", current->pid); + return; + } + + PROTECT_CTX(ctx, flags); + + PFM_SET_WORK_PENDING(current, 0); + + pfm_clear_task_notify(); + + regs = task_pt_regs(current); + + /* + * extract reason for being here and clear + */ + reason = ctx->ctx_fl_trap_reason; + ctx->ctx_fl_trap_reason = PFM_TRAP_REASON_NONE; + ovfl_regs = ctx->ctx_ovfl_regs[0]; + + DPRINT(("reason=%d state=%d\n", reason, ctx->ctx_state)); + + /* + * must be done before we check for simple-reset mode + */ + if (ctx->ctx_fl_going_zombie || ctx->ctx_state == PFM_CTX_ZOMBIE) goto do_zombie; + + + //if (CTX_OVFL_NOBLOCK(ctx)) goto skip_blocking; + if (reason == PFM_TRAP_REASON_RESET) goto skip_blocking; + + /* + * restore interrupt mask to what it was on entry. + * Could be enabled/diasbled. + */ + UNPROTECT_CTX(ctx, flags); + + /* + * force interrupt enable because of down_interruptible() + */ + local_irq_enable(); + + DPRINT(("before block sleeping\n")); + + /* + * may go through without blocking on SMP systems + * if restart has been received already by the time we call down() + */ + ret = wait_for_completion_interruptible(&ctx->ctx_restart_done); + + DPRINT(("after block sleeping ret=%d\n", ret)); + + /* + * lock context and mask interrupts again + * We save flags into a dummy because we may have + * altered interrupts mask compared to entry in this + * function. + */ + PROTECT_CTX(ctx, dummy_flags); + + /* + * we need to read the ovfl_regs only after wake-up + * because we may have had pfm_write_pmds() in between + * and that can changed PMD values and therefore + * ovfl_regs is reset for these new PMD values. + */ + ovfl_regs = ctx->ctx_ovfl_regs[0]; + + if (ctx->ctx_fl_going_zombie) { +do_zombie: + DPRINT(("context is zombie, bailing out\n")); + pfm_context_force_terminate(ctx, regs); + goto nothing_to_do; + } + /* + * in case of interruption of down() we don't restart anything + */ + if (ret < 0) goto nothing_to_do; + +skip_blocking: + pfm_resume_after_ovfl(ctx, ovfl_regs, regs); + ctx->ctx_ovfl_regs[0] = 0UL; + +nothing_to_do: + /* + * restore flags as they were upon entry + */ + UNPROTECT_CTX(ctx, flags); +} + +static int +pfm_notify_user(pfm_context_t *ctx, pfm_msg_t *msg) +{ + if (ctx->ctx_state == PFM_CTX_ZOMBIE) { + DPRINT(("ignoring overflow notification, owner is zombie\n")); + return 0; + } + + DPRINT(("waking up somebody\n")); + + if (msg) wake_up_interruptible(&ctx->ctx_msgq_wait); + + /* + * safe, we are not in intr handler, nor in ctxsw when + * we come here + */ + kill_fasync (&ctx->ctx_async_queue, SIGIO, POLL_IN); + + return 0; +} + +static int +pfm_ovfl_notify_user(pfm_context_t *ctx, unsigned long ovfl_pmds) +{ + pfm_msg_t *msg = NULL; + + if (ctx->ctx_fl_no_msg == 0) { + msg = pfm_get_new_msg(ctx); + if (msg == NULL) { + printk(KERN_ERR "perfmon: pfm_ovfl_notify_user no more notification msgs\n"); + return -1; + } + + msg->pfm_ovfl_msg.msg_type = PFM_MSG_OVFL; + msg->pfm_ovfl_msg.msg_ctx_fd = ctx->ctx_fd; + msg->pfm_ovfl_msg.msg_active_set = 0; + msg->pfm_ovfl_msg.msg_ovfl_pmds[0] = ovfl_pmds; + msg->pfm_ovfl_msg.msg_ovfl_pmds[1] = 0UL; + msg->pfm_ovfl_msg.msg_ovfl_pmds[2] = 0UL; + msg->pfm_ovfl_msg.msg_ovfl_pmds[3] = 0UL; + msg->pfm_ovfl_msg.msg_tstamp = 0UL; + } + + DPRINT(("ovfl msg: msg=%p no_msg=%d fd=%d ovfl_pmds=0x%lx\n", + msg, + ctx->ctx_fl_no_msg, + ctx->ctx_fd, + ovfl_pmds)); + + return pfm_notify_user(ctx, msg); +} + +static int +pfm_end_notify_user(pfm_context_t *ctx) +{ + pfm_msg_t *msg; + + msg = pfm_get_new_msg(ctx); + if (msg == NULL) { + printk(KERN_ERR "perfmon: pfm_end_notify_user no more notification msgs\n"); + return -1; + } + /* no leak */ + memset(msg, 0, sizeof(*msg)); + + msg->pfm_end_msg.msg_type = PFM_MSG_END; + msg->pfm_end_msg.msg_ctx_fd = ctx->ctx_fd; + msg->pfm_ovfl_msg.msg_tstamp = 0UL; + + DPRINT(("end msg: msg=%p no_msg=%d ctx_fd=%d\n", + msg, + ctx->ctx_fl_no_msg, + ctx->ctx_fd)); + + return pfm_notify_user(ctx, msg); +} +#else +#define pfm_ovfl_notify_user(ctx, ovfl_pmds) do {} while(0) +#endif + +/* + * main overflow processing routine. + * it can be called from the interrupt path or explicitely during the context switch code + */ +static void +pfm_overflow_handler(struct task_struct *task, pfm_context_t *ctx, u64 pmc0, struct pt_regs *regs) +{ + pfm_ovfl_arg_t *ovfl_arg; + unsigned long mask; + unsigned long old_val, ovfl_val, new_val; + unsigned long ovfl_notify = 0UL, ovfl_pmds = 0UL, smpl_pmds = 0UL, reset_pmds; + unsigned long tstamp; + pfm_ovfl_ctrl_t ovfl_ctrl; + unsigned int i, has_smpl; + int must_notify = 0; +#ifdef XEN + BUG_ON(task != NULL); +#endif + + if (unlikely(ctx->ctx_state == PFM_CTX_ZOMBIE)) goto stop_monitoring; + + /* + * sanity test. Should never happen + */ + if (unlikely((pmc0 & 0x1) == 0)) goto sanity_check; + + tstamp = ia64_get_itc(); + mask = pmc0 >> PMU_FIRST_COUNTER; + ovfl_val = pmu_conf->ovfl_val; + has_smpl = CTX_HAS_SMPL(ctx); + + DPRINT_ovfl(("pmc0=0x%lx pid=%d iip=0x%lx, %s " + "used_pmds=0x%lx\n", + pmc0, + task ? task->pid: -1, + (regs ? regs->cr_iip : 0), + CTX_OVFL_NOBLOCK(ctx) ? "nonblocking" : "blocking", + ctx->ctx_used_pmds[0])); + + + /* + * first we update the virtual counters + * assume there was a prior ia64_srlz_d() issued + */ + for (i = PMU_FIRST_COUNTER; mask ; i++, mask >>= 1) { + + /* skip pmd which did not overflow */ + if ((mask & 0x1) == 0) continue; + + /* + * Note that the pmd is not necessarily 0 at this point as qualified events + * may have happened before the PMU was frozen. The residual count is not + * taken into consideration here but will be with any read of the pmd via + * pfm_read_pmds(). + */ + old_val = new_val = ctx->ctx_pmds[i].val; + new_val += 1 + ovfl_val; + ctx->ctx_pmds[i].val = new_val; + + /* + * check for overflow condition + */ + if (likely(old_val > new_val)) { + ovfl_pmds |= 1UL << i; + if (PMC_OVFL_NOTIFY(ctx, i)) ovfl_notify |= 1UL << i; + } + + DPRINT_ovfl(("ctx_pmd[%d].val=0x%lx old_val=0x%lx pmd=0x%lx ovfl_pmds=0x%lx ovfl_notify=0x%lx\n", + i, + new_val, + old_val, + ia64_get_pmd(i) & ovfl_val, + ovfl_pmds, + ovfl_notify)); + } + + /* + * there was no 64-bit overflow, nothing else to do + */ + if (ovfl_pmds == 0UL) return; + + /* + * reset all control bits + */ + ovfl_ctrl.val = 0; + reset_pmds = 0UL; + + /* + * if a sampling format module exists, then we "cache" the overflow by + * calling the module's handler() routine. + */ + if (has_smpl) { + unsigned long start_cycles, end_cycles; + unsigned long pmd_mask; + int j, k, ret = 0; + int this_cpu = smp_processor_id(); + + pmd_mask = ovfl_pmds >> PMU_FIRST_COUNTER; + ovfl_arg = &ctx->ctx_ovfl_arg; + + prefetch(ctx->ctx_smpl_hdr); + + for(i=PMU_FIRST_COUNTER; pmd_mask && ret == 0; i++, pmd_mask >>=1) { + + mask = 1UL << i; + + if ((pmd_mask & 0x1) == 0) continue; + + ovfl_arg->ovfl_pmd = (unsigned char )i; + ovfl_arg->ovfl_notify = ovfl_notify & mask ? 1 : 0; + ovfl_arg->active_set = 0; + ovfl_arg->ovfl_ctrl.val = 0; /* module must fill in all fields */ + ovfl_arg->smpl_pmds[0] = smpl_pmds = ctx->ctx_pmds[i].smpl_pmds[0]; + + ovfl_arg->pmd_value = ctx->ctx_pmds[i].val; + ovfl_arg->pmd_last_reset = ctx->ctx_pmds[i].lval; + ovfl_arg->pmd_eventid = ctx->ctx_pmds[i].eventid; + + /* + * copy values of pmds of interest. Sampling format may copy them + * into sampling buffer. + */ + if (smpl_pmds) { + for(j=0, k=0; smpl_pmds; j++, smpl_pmds >>=1) { + if ((smpl_pmds & 0x1) == 0) continue; + ovfl_arg->smpl_pmds_values[k++] = PMD_IS_COUNTING(j) ? pfm_read_soft_counter(ctx, j) : ia64_get_pmd(j); + DPRINT_ovfl(("smpl_pmd[%d]=pmd%u=0x%lx\n", k-1, j, ovfl_arg->smpl_pmds_values[k-1])); + } + } + + pfm_stats[this_cpu].pfm_smpl_handler_calls++; + + start_cycles = ia64_get_itc(); + + /* + * call custom buffer format record (handler) routine + */ + ret = (*ctx->ctx_buf_fmt->fmt_handler)(task, ctx->ctx_smpl_hdr, ovfl_arg, regs, tstamp); + + end_cycles = ia64_get_itc(); + + /* + * For those controls, we take the union because they have + * an all or nothing behavior. + */ + ovfl_ctrl.bits.notify_user |= ovfl_arg->ovfl_ctrl.bits.notify_user; + ovfl_ctrl.bits.block_task |= ovfl_arg->ovfl_ctrl.bits.block_task; + ovfl_ctrl.bits.mask_monitoring |= ovfl_arg->ovfl_ctrl.bits.mask_monitoring; + /* + * build the bitmask of pmds to reset now + */ + if (ovfl_arg->ovfl_ctrl.bits.reset_ovfl_pmds) reset_pmds |= mask; + + pfm_stats[this_cpu].pfm_smpl_handler_cycles += end_cycles - start_cycles; + } + /* + * when the module cannot handle the rest of the overflows, we abort right here + */ + if (ret && pmd_mask) { + DPRINT(("handler aborts leftover ovfl_pmds=0x%lx\n", + pmd_mask<ctx_ovfl_regs[0] = ovfl_pmds; + + /* + * check for blocking context + */ + if (CTX_OVFL_NOBLOCK(ctx) == 0 && ovfl_ctrl.bits.block_task) { + + ctx->ctx_fl_trap_reason = PFM_TRAP_REASON_BLOCK; + + /* + * set the perfmon specific checking pending work for the task + */ + PFM_SET_WORK_PENDING(task, 1); + + /* + * when coming from ctxsw, current still points to the + * previous task, therefore we must work with task and not current. + */ + pfm_set_task_notify(task); + } + /* + * defer until state is changed (shorten spin window). the context is locked + * anyway, so the signal receiver would come spin for nothing. + */ + must_notify = 1; +#else + gdprintk(XENLOG_INFO, "%s check!\n", __func__); +#endif + } + + DPRINT_ovfl(("owner [%d] pending=%ld reason=%u ovfl_pmds=0x%lx ovfl_notify=0x%lx masked=%d\n", +#ifndef XEN + GET_PMU_OWNER() ? GET_PMU_OWNER()->pid : -1, + PFM_GET_WORK_PENDING(task), +#else + -1, 0UL, +#endif + ctx->ctx_fl_trap_reason, + ovfl_pmds, + ovfl_notify, + ovfl_ctrl.bits.mask_monitoring ? 1 : 0)); + /* + * in case monitoring must be stopped, we toggle the psr bits + */ + if (ovfl_ctrl.bits.mask_monitoring) { +#ifndef XEN + pfm_mask_monitoring(task); + ctx->ctx_state = PFM_CTX_MASKED; + ctx->ctx_fl_can_restart = 1; +#else + gdprintk(XENLOG_INFO, "%s check!\n", __func__); +#endif + } + + /* + * send notification now + */ + if (must_notify) pfm_ovfl_notify_user(ctx, ovfl_notify); + + return; + +sanity_check: + printk(KERN_ERR "perfmon: CPU%d overflow handler [%d] pmc0=0x%lx\n", + smp_processor_id(), + task ? task->pid : -1, + pmc0); + return; + +stop_monitoring: + /* + * in SMP, zombie context is never restored but reclaimed in pfm_load_regs(). + * Moreover, zombies are also reclaimed in pfm_save_regs(). Therefore we can + * come here as zombie only if the task is the current task. In which case, we + * can access the PMU hardware directly. + * + * Note that zombies do have PM_VALID set. So here we do the minimal. + * + * In case the context was zombified it could not be reclaimed at the time + * the monitoring program exited. At this point, the PMU reservation has been + * returned, the sampiing buffer has been freed. We must convert this call + * into a spurious interrupt. However, we must also avoid infinite overflows + * by stopping monitoring for this task. We can only come here for a per-task + * context. All we need to do is to stop monitoring using the psr bits which + * are always task private. By re-enabling secure montioring, we ensure that + * the monitored task will not be able to re-activate monitoring. + * The task will eventually be context switched out, at which point the context + * will be reclaimed (that includes releasing ownership of the PMU). + * + * So there might be a window of time where the number of per-task session is zero + * yet one PMU might have a owner and get at most one overflow interrupt for a zombie + * context. This is safe because if a per-task session comes in, it will push this one + * out and by the virtue on pfm_save_regs(), this one will disappear. If a system wide + * session is force on that CPU, given that we use task pinning, pfm_save_regs() will + * also push our zombie context out. + * + * Overall pretty hairy stuff.... + */ + DPRINT(("ctx is zombie for [%d], converted to spurious\n", task ? task->pid: -1)); + pfm_clear_psr_up(); + ia64_psr(regs)->up = 0; + ia64_psr(regs)->sp = 1; + return; +} + +static int +pfm_do_interrupt_handler(int irq, void *arg, struct pt_regs *regs) +{ + struct task_struct *task; + pfm_context_t *ctx; + unsigned long flags; + u64 pmc0; + int this_cpu = smp_processor_id(); + int retval = 0; + + pfm_stats[this_cpu].pfm_ovfl_intr_count++; + + /* + * srlz.d done before arriving here + */ + pmc0 = ia64_get_pmc(0); + +#ifndef XEN + task = GET_PMU_OWNER(); +#else + task = NULL; +#endif + ctx = GET_PMU_CTX(); + + /* + * if we have some pending bits set + * assumes : if any PMC0.bit[63-1] is set, then PMC0.fr = 1 + */ +#ifndef XEN + if (PMC0_HAS_OVFL(pmc0) && task) { +#else + if (PMC0_HAS_OVFL(pmc0)) { +#endif + /* + * we assume that pmc0.fr is always set here + */ + + /* sanity check */ + if (!ctx) goto report_spurious1; + +#ifndef XEN + if (ctx->ctx_fl_system == 0 && (task->thread.flags & IA64_THREAD_PM_VALID) == 0) + goto report_spurious2; +#endif + + PROTECT_CTX_NOPRINT(ctx, flags); + + pfm_overflow_handler(task, ctx, pmc0, regs); + + UNPROTECT_CTX_NOPRINT(ctx, flags); + + } else { + pfm_stats[this_cpu].pfm_spurious_ovfl_intr_count++; + retval = -1; + } + /* + * keep it unfrozen at all times + */ + pfm_unfreeze_pmu(); + + return retval; + +report_spurious1: +#ifndef XEN + printk(KERN_INFO "perfmon: spurious overflow interrupt on CPU%d: process %d has no PFM context\n", + this_cpu, task->pid); +#endif + pfm_unfreeze_pmu(); + return -1; +#ifndef XEN /* XEN path doesn't take this goto */ +report_spurious2: + printk(KERN_INFO "perfmon: spurious overflow interrupt on CPU%d: process %d, invalid flag\n", + this_cpu, + task->pid); + pfm_unfreeze_pmu(); + return -1; +#endif +} + +static irqreturn_t +pfm_interrupt_handler(int irq, void *arg, struct pt_regs *regs) +{ + unsigned long start_cycles, total_cycles; + unsigned long min, max; + int this_cpu; + int ret; + + this_cpu = get_cpu(); + if (likely(!pfm_alt_intr_handler)) { + min = pfm_stats[this_cpu].pfm_ovfl_intr_cycles_min; + max = pfm_stats[this_cpu].pfm_ovfl_intr_cycles_max; + + start_cycles = ia64_get_itc(); + + ret = pfm_do_interrupt_handler(irq, arg, regs); + + total_cycles = ia64_get_itc(); + + /* + * don't measure spurious interrupts + */ + if (likely(ret == 0)) { + total_cycles -= start_cycles; + + if (total_cycles < min) pfm_stats[this_cpu].pfm_ovfl_intr_cycles_min = total_cycles; + if (total_cycles > max) pfm_stats[this_cpu].pfm_ovfl_intr_cycles_max = total_cycles; + + pfm_stats[this_cpu].pfm_ovfl_intr_cycles += total_cycles; + } + } + else { + (*pfm_alt_intr_handler->handler)(irq, arg, regs); + } + + put_cpu_no_resched(); + return IRQ_HANDLED; +} + +#ifndef XEN +/* + * /proc/perfmon interface, for debug only + */ + +#define PFM_PROC_SHOW_HEADER ((void *)NR_CPUS+1) + +static void * +pfm_proc_start(struct seq_file *m, loff_t *pos) +{ + if (*pos == 0) { + return PFM_PROC_SHOW_HEADER; + } + + while (*pos <= NR_CPUS) { + if (cpu_online(*pos - 1)) { + return (void *)*pos; + } + ++*pos; + } + return NULL; +} + +static void * +pfm_proc_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return pfm_proc_start(m, pos); +} + +static void +pfm_proc_stop(struct seq_file *m, void *v) +{ +} + +static void +pfm_proc_show_header(struct seq_file *m) +{ + struct list_head * pos; + pfm_buffer_fmt_t * entry; + unsigned long flags; + + seq_printf(m, + "perfmon version : %u.%u\n" + "model : %s\n" + "fastctxsw : %s\n" + "expert mode : %s\n" + "ovfl_mask : 0x%lx\n" + "PMU flags : 0x%x\n", + PFM_VERSION_MAJ, PFM_VERSION_MIN, + pmu_conf->pmu_name, + pfm_sysctl.fastctxsw > 0 ? "Yes": "No", + pfm_sysctl.expert_mode > 0 ? "Yes": "No", + pmu_conf->ovfl_val, + pmu_conf->flags); + + LOCK_PFS(flags); + + seq_printf(m, + "proc_sessions : %u\n" + "sys_sessions : %u\n" + "sys_use_dbregs : %u\n" + "ptrace_use_dbregs : %u\n", + pfm_sessions.pfs_task_sessions, + pfm_sessions.pfs_sys_sessions, + pfm_sessions.pfs_sys_use_dbregs, + pfm_sessions.pfs_ptrace_use_dbregs); + + UNLOCK_PFS(flags); + + spin_lock(&pfm_buffer_fmt_lock); + + list_for_each(pos, &pfm_buffer_fmt_list) { + entry = list_entry(pos, pfm_buffer_fmt_t, fmt_list); + seq_printf(m, "format : %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x %s\n", + entry->fmt_uuid[0], + entry->fmt_uuid[1], + entry->fmt_uuid[2], + entry->fmt_uuid[3], + entry->fmt_uuid[4], + entry->fmt_uuid[5], + entry->fmt_uuid[6], + entry->fmt_uuid[7], + entry->fmt_uuid[8], + entry->fmt_uuid[9], + entry->fmt_uuid[10], + entry->fmt_uuid[11], + entry->fmt_uuid[12], + entry->fmt_uuid[13], + entry->fmt_uuid[14], + entry->fmt_uuid[15], + entry->fmt_name); + } + spin_unlock(&pfm_buffer_fmt_lock); + +} + +static int +pfm_proc_show(struct seq_file *m, void *v) +{ + unsigned long psr; + unsigned int i; + int cpu; + + if (v == PFM_PROC_SHOW_HEADER) { + pfm_proc_show_header(m); + return 0; + } + + /* show info for CPU (v - 1) */ + + cpu = (long)v - 1; + seq_printf(m, + "CPU%-2d overflow intrs : %lu\n" + "CPU%-2d overflow cycles : %lu\n" + "CPU%-2d overflow min : %lu\n" + "CPU%-2d overflow max : %lu\n" + "CPU%-2d smpl handler calls : %lu\n" + "CPU%-2d smpl handler cycles : %lu\n" + "CPU%-2d spurious intrs : %lu\n" + "CPU%-2d replay intrs : %lu\n" + "CPU%-2d syst_wide : %d\n" + "CPU%-2d dcr_pp : %d\n" + "CPU%-2d exclude idle : %d\n" + "CPU%-2d owner : %d\n" + "CPU%-2d context : %p\n" + "CPU%-2d activations : %lu\n", + cpu, pfm_stats[cpu].pfm_ovfl_intr_count, + cpu, pfm_stats[cpu].pfm_ovfl_intr_cycles, + cpu, pfm_stats[cpu].pfm_ovfl_intr_cycles_min, + cpu, pfm_stats[cpu].pfm_ovfl_intr_cycles_max, + cpu, pfm_stats[cpu].pfm_smpl_handler_calls, + cpu, pfm_stats[cpu].pfm_smpl_handler_cycles, + cpu, pfm_stats[cpu].pfm_spurious_ovfl_intr_count, + cpu, pfm_stats[cpu].pfm_replay_ovfl_intr_count, + cpu, pfm_get_cpu_data(pfm_syst_info, cpu) & PFM_CPUINFO_SYST_WIDE ? 1 : 0, + cpu, pfm_get_cpu_data(pfm_syst_info, cpu) & PFM_CPUINFO_DCR_PP ? 1 : 0, + cpu, pfm_get_cpu_data(pfm_syst_info, cpu) & PFM_CPUINFO_EXCL_IDLE ? 1 : 0, + cpu, pfm_get_cpu_data(pmu_owner, cpu) ? pfm_get_cpu_data(pmu_owner, cpu)->pid: -1, + cpu, pfm_get_cpu_data(pmu_ctx, cpu), + cpu, pfm_get_cpu_data(pmu_activation_number, cpu)); + + if (num_online_cpus() == 1 && pfm_sysctl.debug > 0) { + + psr = pfm_get_psr(); + + ia64_srlz_d(); + + seq_printf(m, + "CPU%-2d psr : 0x%lx\n" + "CPU%-2d pmc0 : 0x%lx\n", + cpu, psr, + cpu, ia64_get_pmc(0)); + + for (i=0; PMC_IS_LAST(i) == 0; i++) { + if (PMC_IS_COUNTING(i) == 0) continue; + seq_printf(m, + "CPU%-2d pmc%u : 0x%lx\n" + "CPU%-2d pmd%u : 0x%lx\n", + cpu, i, ia64_get_pmc(i), + cpu, i, ia64_get_pmd(i)); + } + } + return 0; +} + +struct seq_operations pfm_seq_ops = { + .start = pfm_proc_start, + .next = pfm_proc_next, + .stop = pfm_proc_stop, + .show = pfm_proc_show +}; + +static int +pfm_proc_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &pfm_seq_ops); +} +#endif + + +/* + * we come here as soon as local_cpu_data->pfm_syst_wide is set. this happens + * during pfm_enable() hence before pfm_start(). We cannot assume monitoring + * is active or inactive based on mode. We must rely on the value in + * local_cpu_data->pfm_syst_info + */ +void +pfm_syst_wide_update_task(struct task_struct *task, unsigned long info, int is_ctxswin) +{ + struct pt_regs *regs; + unsigned long dcr; + unsigned long dcr_pp; + + dcr_pp = info & PFM_CPUINFO_DCR_PP ? 1 : 0; + + /* + * pid 0 is guaranteed to be the idle task. There is one such task with pid 0 + * on every CPU, so we can rely on the pid to identify the idle task. + */ + if ((info & PFM_CPUINFO_EXCL_IDLE) == 0 || task->pid) { + regs = task_pt_regs(task); + ia64_psr(regs)->pp = is_ctxswin ? dcr_pp : 0; + return; + } + /* + * if monitoring has started + */ + if (dcr_pp) { + dcr = ia64_getreg(_IA64_REG_CR_DCR); + /* + * context switching in? + */ + if (is_ctxswin) { + /* mask monitoring for the idle task */ + ia64_setreg(_IA64_REG_CR_DCR, dcr & ~IA64_DCR_PP); + pfm_clear_psr_pp(); + ia64_srlz_i(); + return; + } + /* + * context switching out + * restore monitoring for next task + * + * Due to inlining this odd if-then-else construction generates + * better code. + */ + ia64_setreg(_IA64_REG_CR_DCR, dcr |IA64_DCR_PP); + pfm_set_psr_pp(); + ia64_srlz_i(); + } +} + +#ifndef XEN +#ifdef CONFIG_SMP + +static void +pfm_force_cleanup(pfm_context_t *ctx, struct pt_regs *regs) +{ + struct task_struct *task = ctx->ctx_task; + + ia64_psr(regs)->up = 0; + ia64_psr(regs)->sp = 1; + + if (GET_PMU_OWNER() == task) { + DPRINT(("cleared ownership for [%d]\n", ctx->ctx_task->pid)); + SET_PMU_OWNER(NULL, NULL); + } + + /* + * disconnect the task from the context and vice-versa + */ + PFM_SET_WORK_PENDING(task, 0); + + task->thread.pfm_context = NULL; + task->thread.flags &= ~IA64_THREAD_PM_VALID; + + DPRINT(("force cleanup for [%d]\n", task->pid)); +} + + +/* + * in 2.6, interrupts are masked when we come here and the runqueue lock is held + */ +void +pfm_save_regs(struct task_struct *task) +{ + pfm_context_t *ctx; + struct thread_struct *t; + unsigned long flags; + u64 psr; + + + ctx = PFM_GET_CTX(task); + if (ctx == NULL) return; + t = &task->thread; + + /* + * we always come here with interrupts ALREADY disabled by + * the scheduler. So we simply need to protect against concurrent + * access, not CPU concurrency. + */ + flags = pfm_protect_ctx_ctxsw(ctx); + + if (ctx->ctx_state == PFM_CTX_ZOMBIE) { + struct pt_regs *regs = task_pt_regs(task); + + pfm_clear_psr_up(); + + pfm_force_cleanup(ctx, regs); + + BUG_ON(ctx->ctx_smpl_hdr); + + pfm_unprotect_ctx_ctxsw(ctx, flags); + + pfm_context_free(ctx); + return; + } + + /* + * save current PSR: needed because we modify it + */ + ia64_srlz_d(); + psr = pfm_get_psr(); + + BUG_ON(psr & (IA64_PSR_I)); + + /* + * stop monitoring: + * This is the last instruction which may generate an overflow + * + * We do not need to set psr.sp because, it is irrelevant in kernel. + * It will be restored from ipsr when going back to user level + */ + pfm_clear_psr_up(); + + /* + * keep a copy of psr.up (for reload) + */ + ctx->ctx_saved_psr_up = psr & IA64_PSR_UP; + + /* + * release ownership of this PMU. + * PM interrupts are masked, so nothing + * can happen. + */ + SET_PMU_OWNER(NULL, NULL); + + /* + * we systematically save the PMD as we have no + * guarantee we will be schedule at that same + * CPU again. + */ + pfm_save_pmds(t->pmds, ctx->ctx_used_pmds[0]); + + /* + * save pmc0 ia64_srlz_d() done in pfm_save_pmds() + * we will need it on the restore path to check + * for pending overflow. + */ + t->pmcs[0] = ia64_get_pmc(0); + + /* + * unfreeze PMU if had pending overflows + */ + if (t->pmcs[0] & ~0x1UL) pfm_unfreeze_pmu(); + + /* + * finally, allow context access. + * interrupts will still be masked after this call. + */ + pfm_unprotect_ctx_ctxsw(ctx, flags); +} + +#else /* !CONFIG_SMP */ +void +pfm_save_regs(struct task_struct *task) +{ + pfm_context_t *ctx; + u64 psr; + + ctx = PFM_GET_CTX(task); + if (ctx == NULL) return; + + /* + * save current PSR: needed because we modify it + */ + psr = pfm_get_psr(); + + BUG_ON(psr & (IA64_PSR_I)); + + /* + * stop monitoring: + * This is the last instruction which may generate an overflow + * + * We do not need to set psr.sp because, it is irrelevant in kernel. + * It will be restored from ipsr when going back to user level + */ + pfm_clear_psr_up(); + + /* + * keep a copy of psr.up (for reload) + */ + ctx->ctx_saved_psr_up = psr & IA64_PSR_UP; +} + +static void +pfm_lazy_save_regs (struct task_struct *task) +{ + pfm_context_t *ctx; + struct thread_struct *t; + unsigned long flags; + + { u64 psr = pfm_get_psr(); + BUG_ON(psr & IA64_PSR_UP); + } + + ctx = PFM_GET_CTX(task); + t = &task->thread; + + /* + * we need to mask PMU overflow here to + * make sure that we maintain pmc0 until + * we save it. overflow interrupts are + * treated as spurious if there is no + * owner. + * + * XXX: I don't think this is necessary + */ + PROTECT_CTX(ctx,flags); + + /* + * release ownership of this PMU. + * must be done before we save the registers. + * + * after this call any PMU interrupt is treated + * as spurious. + */ + SET_PMU_OWNER(NULL, NULL); + + /* + * save all the pmds we use + */ + pfm_save_pmds(t->pmds, ctx->ctx_used_pmds[0]); + + /* + * save pmc0 ia64_srlz_d() done in pfm_save_pmds() + * it is needed to check for pended overflow + * on the restore path + */ + t->pmcs[0] = ia64_get_pmc(0); + + /* + * unfreeze PMU if had pending overflows + */ + if (t->pmcs[0] & ~0x1UL) pfm_unfreeze_pmu(); + + /* + * now get can unmask PMU interrupts, they will + * be treated as purely spurious and we will not + * lose any information + */ + UNPROTECT_CTX(ctx,flags); +} +#endif /* CONFIG_SMP */ + +#ifdef CONFIG_SMP +/* + * in 2.6, interrupts are masked when we come here and the runqueue lock is held + */ +void +pfm_load_regs (struct task_struct *task) +{ + pfm_context_t *ctx; + struct thread_struct *t; + unsigned long pmc_mask = 0UL, pmd_mask = 0UL; + unsigned long flags; + u64 psr, psr_up; + int need_irq_resend; + + ctx = PFM_GET_CTX(task); + if (unlikely(ctx == NULL)) return; + + BUG_ON(GET_PMU_OWNER()); + + t = &task->thread; + /* + * possible on unload + */ + if (unlikely((t->flags & IA64_THREAD_PM_VALID) == 0)) return; + + /* + * we always come here with interrupts ALREADY disabled by + * the scheduler. So we simply need to protect against concurrent + * access, not CPU concurrency. + */ + flags = pfm_protect_ctx_ctxsw(ctx); + psr = pfm_get_psr(); + + need_irq_resend = pmu_conf->flags & PFM_PMU_IRQ_RESEND; + + BUG_ON(psr & (IA64_PSR_UP|IA64_PSR_PP)); + BUG_ON(psr & IA64_PSR_I); + + if (unlikely(ctx->ctx_state == PFM_CTX_ZOMBIE)) { + struct pt_regs *regs = task_pt_regs(task); + + BUG_ON(ctx->ctx_smpl_hdr); + + pfm_force_cleanup(ctx, regs); + + pfm_unprotect_ctx_ctxsw(ctx, flags); + + /* + * this one (kmalloc'ed) is fine with interrupts disabled + */ + pfm_context_free(ctx); + + return; + } + + /* + * we restore ALL the debug registers to avoid picking up + * stale state. + */ + if (ctx->ctx_fl_using_dbreg) { + pfm_restore_ibrs(ctx->ctx_ibrs, pmu_conf->num_ibrs); + pfm_restore_dbrs(ctx->ctx_dbrs, pmu_conf->num_dbrs); + } + /* + * retrieve saved psr.up + */ + psr_up = ctx->ctx_saved_psr_up; + + /* + * if we were the last user of the PMU on that CPU, + * then nothing to do except restore psr + */ + if (GET_LAST_CPU(ctx) == smp_processor_id() && ctx->ctx_last_activation == GET_ACTIVATION()) { + + /* + * retrieve partial reload masks (due to user modifications) + */ + pmc_mask = ctx->ctx_reload_pmcs[0]; + pmd_mask = ctx->ctx_reload_pmds[0]; + + } else { + /* + * To avoid leaking information to the user level when psr.sp=0, + * we must reload ALL implemented pmds (even the ones we don't use). + * In the kernel we only allow PFM_READ_PMDS on registers which + * we initialized or requested (sampling) so there is no risk there. + */ + pmd_mask = pfm_sysctl.fastctxsw ? ctx->ctx_used_pmds[0] : ctx->ctx_all_pmds[0]; + + /* + * ALL accessible PMCs are systematically reloaded, unused registers + * get their default (from pfm_reset_pmu_state()) values to avoid picking + * up stale configuration. + * + * PMC0 is never in the mask. It is always restored separately. + */ + pmc_mask = ctx->ctx_all_pmcs[0]; + } + /* + * when context is MASKED, we will restore PMC with plm=0 + * and PMD with stale information, but that's ok, nothing + * will be captured. + * + * XXX: optimize here + */ + if (pmd_mask) pfm_restore_pmds(t->pmds, pmd_mask); + if (pmc_mask) pfm_restore_pmcs(t->pmcs, pmc_mask); + + /* + * check for pending overflow at the time the state + * was saved. + */ + if (unlikely(PMC0_HAS_OVFL(t->pmcs[0]))) { + /* + * reload pmc0 with the overflow information + * On McKinley PMU, this will trigger a PMU interrupt + */ + ia64_set_pmc(0, t->pmcs[0]); + ia64_srlz_d(); + t->pmcs[0] = 0UL; + + /* + * will replay the PMU interrupt + */ + if (need_irq_resend) hw_resend_irq(NULL, IA64_PERFMON_VECTOR); + + pfm_stats[smp_processor_id()].pfm_replay_ovfl_intr_count++; + } + + /* + * we just did a reload, so we reset the partial reload fields + */ + ctx->ctx_reload_pmcs[0] = 0UL; + ctx->ctx_reload_pmds[0] = 0UL; + + SET_LAST_CPU(ctx, smp_processor_id()); + + /* + * dump activation value for this PMU + */ + INC_ACTIVATION(); + /* + * record current activation for this context + */ + SET_ACTIVATION(ctx); + + /* + * establish new ownership. + */ + SET_PMU_OWNER(task, ctx); + + /* + * restore the psr.up bit. measurement + * is active again. + * no PMU interrupt can happen at this point + * because we still have interrupts disabled. + */ + if (likely(psr_up)) pfm_set_psr_up(); + + /* + * allow concurrent access to context + */ + pfm_unprotect_ctx_ctxsw(ctx, flags); +} +#else /* !CONFIG_SMP */ +/* + * reload PMU state for UP kernels + * in 2.5 we come here with interrupts disabled + */ +void +pfm_load_regs (struct task_struct *task) +{ + struct thread_struct *t; + pfm_context_t *ctx; + struct task_struct *owner; + unsigned long pmd_mask, pmc_mask; + u64 psr, psr_up; + int need_irq_resend; + + owner = GET_PMU_OWNER(); + ctx = PFM_GET_CTX(task); + t = &task->thread; + psr = pfm_get_psr(); + + BUG_ON(psr & (IA64_PSR_UP|IA64_PSR_PP)); + BUG_ON(psr & IA64_PSR_I); + + /* + * we restore ALL the debug registers to avoid picking up + * stale state. + * + * This must be done even when the task is still the owner + * as the registers may have been modified via ptrace() + * (not perfmon) by the previous task. + */ + if (ctx->ctx_fl_using_dbreg) { + pfm_restore_ibrs(ctx->ctx_ibrs, pmu_conf->num_ibrs); + pfm_restore_dbrs(ctx->ctx_dbrs, pmu_conf->num_dbrs); + } + + /* + * retrieved saved psr.up + */ + psr_up = ctx->ctx_saved_psr_up; + need_irq_resend = pmu_conf->flags & PFM_PMU_IRQ_RESEND; + + /* + * short path, our state is still there, just + * need to restore psr and we go + * + * we do not touch either PMC nor PMD. the psr is not touched + * by the overflow_handler. So we are safe w.r.t. to interrupt + * concurrency even without interrupt masking. + */ + if (likely(owner == task)) { + if (likely(psr_up)) pfm_set_psr_up(); + return; + } + + /* + * someone else is still using the PMU, first push it out and + * then we'll be able to install our stuff ! + * + * Upon return, there will be no owner for the current PMU + */ + if (owner) pfm_lazy_save_regs(owner); + + /* + * To avoid leaking information to the user level when psr.sp=0, + * we must reload ALL implemented pmds (even the ones we don't use). + * In the kernel we only allow PFM_READ_PMDS on registers which + * we initialized or requested (sampling) so there is no risk there. + */ + pmd_mask = pfm_sysctl.fastctxsw ? ctx->ctx_used_pmds[0] : ctx->ctx_all_pmds[0]; + + /* + * ALL accessible PMCs are systematically reloaded, unused registers + * get their default (from pfm_reset_pmu_state()) values to avoid picking + * up stale configuration. + * + * PMC0 is never in the mask. It is always restored separately + */ + pmc_mask = ctx->ctx_all_pmcs[0]; + + pfm_restore_pmds(t->pmds, pmd_mask); + pfm_restore_pmcs(t->pmcs, pmc_mask); + + /* + * check for pending overflow at the time the state + * was saved. + */ + if (unlikely(PMC0_HAS_OVFL(t->pmcs[0]))) { + /* + * reload pmc0 with the overflow information + * On McKinley PMU, this will trigger a PMU interrupt + */ + ia64_set_pmc(0, t->pmcs[0]); + ia64_srlz_d(); + + t->pmcs[0] = 0UL; + + /* + * will replay the PMU interrupt + */ + if (need_irq_resend) hw_resend_irq(NULL, IA64_PERFMON_VECTOR); + + pfm_stats[smp_processor_id()].pfm_replay_ovfl_intr_count++; + } + + /* + * establish new ownership. + */ + SET_PMU_OWNER(task, ctx); + + /* + * restore the psr.up bit. measurement + * is active again. + * no PMU interrupt can happen at this point + * because we still have interrupts disabled. + */ + if (likely(psr_up)) pfm_set_psr_up(); +} +#endif /* CONFIG_SMP */ +#endif /* XEN */ + +/* + * this function assumes monitoring is stopped + */ +static void +pfm_flush_pmds(struct task_struct *task, pfm_context_t *ctx) +{ +#ifndef XEN + u64 pmc0; + unsigned long mask2, val, pmd_val, ovfl_val; + int i, can_access_pmu = 0; + int is_self; + + /* + * is the caller the task being monitored (or which initiated the + * session for system wide measurements) + */ + is_self = ctx->ctx_task == task ? 1 : 0; + + /* + * can access PMU is task is the owner of the PMU state on the current CPU + * or if we are running on the CPU bound to the context in system-wide mode + * (that is not necessarily the task the context is attached to in this mode). + * In system-wide we always have can_access_pmu true because a task running on an + * invalid processor is flagged earlier in the call stack (see pfm_stop). + */ + can_access_pmu = (GET_PMU_OWNER() == task) || (ctx->ctx_fl_system && ctx->ctx_cpu == smp_processor_id()); + if (can_access_pmu) { + /* + * Mark the PMU as not owned + * This will cause the interrupt handler to do nothing in case an overflow + * interrupt was in-flight + * This also guarantees that pmc0 will contain the final state + * It virtually gives us full control on overflow processing from that point + * on. + */ + SET_PMU_OWNER(NULL, NULL); + DPRINT(("releasing ownership\n")); + + /* + * read current overflow status: + * + * we are guaranteed to read the final stable state + */ + ia64_srlz_d(); + pmc0 = ia64_get_pmc(0); /* slow */ + + /* + * reset freeze bit, overflow status information destroyed + */ + pfm_unfreeze_pmu(); + } else { + pmc0 = task->thread.pmcs[0]; + /* + * clear whatever overflow status bits there were + */ + task->thread.pmcs[0] = 0; + } + ovfl_val = pmu_conf->ovfl_val; + /* + * we save all the used pmds + * we take care of overflows for counting PMDs + * + * XXX: sampling situation is not taken into account here + */ + mask2 = ctx->ctx_used_pmds[0]; + + DPRINT(("is_self=%d ovfl_val=0x%lx mask2=0x%lx\n", is_self, ovfl_val, mask2)); + + for (i = 0; mask2; i++, mask2>>=1) { + + /* skip non used pmds */ + if ((mask2 & 0x1) == 0) continue; + + /* + * can access PMU always true in system wide mode + */ + val = pmd_val = can_access_pmu ? ia64_get_pmd(i) : task->thread.pmds[i]; + + if (PMD_IS_COUNTING(i)) { + DPRINT(("[%d] pmd[%d] ctx_pmd=0x%lx hw_pmd=0x%lx\n", + task->pid, + i, + ctx->ctx_pmds[i].val, + val & ovfl_val)); + + /* + * we rebuild the full 64 bit value of the counter + */ + val = ctx->ctx_pmds[i].val + (val & ovfl_val); + + /* + * now everything is in ctx_pmds[] and we need + * to clear the saved context from save_regs() such that + * pfm_read_pmds() gets the correct value + */ + pmd_val = 0UL; + + /* + * take care of overflow inline + */ + if (pmc0 & (1UL << i)) { + val += 1 + ovfl_val; + DPRINT(("[%d] pmd[%d] overflowed\n", task->pid, i)); + } + } + + DPRINT(("[%d] ctx_pmd[%d]=0x%lx pmd_val=0x%lx\n", task->pid, i, val, pmd_val)); + + if (is_self) task->thread.pmds[i] = pmd_val; + + ctx->ctx_pmds[i].val = val; + } +#else + /* XXX */ +#endif +} + +static struct irqaction perfmon_irqaction = { + .handler = pfm_interrupt_handler, +#ifndef XEN + .flags = SA_INTERRUPT, +#endif + .name = "perfmon" +}; + +#ifndef XEN +static void +pfm_alt_save_pmu_state(void *data) +{ + struct pt_regs *regs; + + regs = task_pt_regs(current); + + DPRINT(("called\n")); + + /* + * should not be necessary but + * let's take not risk + */ + pfm_clear_psr_up(); + pfm_clear_psr_pp(); + ia64_psr(regs)->pp = 0; + + /* + * This call is required + * May cause a spurious interrupt on some processors + */ + pfm_freeze_pmu(); + + ia64_srlz_d(); +} + +void +pfm_alt_restore_pmu_state(void *data) +{ + struct pt_regs *regs; + + regs = task_pt_regs(current); + + DPRINT(("called\n")); + + /* + * put PMU back in state expected + * by perfmon + */ + pfm_clear_psr_up(); + pfm_clear_psr_pp(); + ia64_psr(regs)->pp = 0; + + /* + * perfmon runs with PMU unfrozen at all times + */ + pfm_unfreeze_pmu(); + + ia64_srlz_d(); +} + +int +pfm_install_alt_pmu_interrupt(pfm_intr_handler_desc_t *hdl) +{ + int ret, i; + int reserve_cpu; + + /* some sanity checks */ + if (hdl == NULL || hdl->handler == NULL) return -EINVAL; + + /* do the easy test first */ + if (pfm_alt_intr_handler) return -EBUSY; + + /* one at a time in the install or remove, just fail the others */ + if (!spin_trylock(&pfm_alt_install_check)) { + return -EBUSY; + } + + /* reserve our session */ + for_each_online_cpu(reserve_cpu) { + ret = pfm_reserve_session(NULL, 1, reserve_cpu); + if (ret) goto cleanup_reserve; + } + + /* save the current system wide pmu states */ + ret = on_each_cpu(pfm_alt_save_pmu_state, NULL, 0, 1); + if (ret) { + DPRINT(("on_each_cpu() failed: %d\n", ret)); + goto cleanup_reserve; + } + + /* officially change to the alternate interrupt handler */ + pfm_alt_intr_handler = hdl; + + spin_unlock(&pfm_alt_install_check); + + return 0; + +cleanup_reserve: + for_each_online_cpu(i) { + /* don't unreserve more than we reserved */ + if (i >= reserve_cpu) break; + + pfm_unreserve_session(NULL, 1, i); + } + + spin_unlock(&pfm_alt_install_check); + + return ret; +} +EXPORT_SYMBOL_GPL(pfm_install_alt_pmu_interrupt); + +int +pfm_remove_alt_pmu_interrupt(pfm_intr_handler_desc_t *hdl) +{ + int i; + int ret; + + if (hdl == NULL) return -EINVAL; + + /* cannot remove someone else's handler! */ + if (pfm_alt_intr_handler != hdl) return -EINVAL; + + /* one at a time in the install or remove, just fail the others */ + if (!spin_trylock(&pfm_alt_install_check)) { + return -EBUSY; + } + + pfm_alt_intr_handler = NULL; + + ret = on_each_cpu(pfm_alt_restore_pmu_state, NULL, 0, 1); + if (ret) { + DPRINT(("on_each_cpu() failed: %d\n", ret)); + } + + for_each_online_cpu(i) { + pfm_unreserve_session(NULL, 1, i); + } + + spin_unlock(&pfm_alt_install_check); + + return 0; +} +EXPORT_SYMBOL_GPL(pfm_remove_alt_pmu_interrupt); +#endif + +/* + * perfmon initialization routine, called from the initcall() table + */ +#ifndef XEN +static int init_pfm_fs(void); +#else +#define init_pfm_fs() do {} while(0) +#endif + +static int __init +pfm_probe_pmu(void) +{ + pmu_config_t **p; + int family; + + family = local_cpu_data->family; + p = pmu_confs; + + while(*p) { + if ((*p)->probe) { + if ((*p)->probe() == 0) goto found; + } else if ((*p)->pmu_family == family || (*p)->pmu_family == 0xff) { + goto found; + } + p++; + } + return -1; +found: + pmu_conf = *p; + return 0; +} + +#ifndef XEN +static struct file_operations pfm_proc_fops = { + .open = pfm_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; +#endif + +int __init +pfm_init(void) +{ + unsigned int n, n_counters, i; + + printk("perfmon: version %u.%u IRQ %u\n", + PFM_VERSION_MAJ, + PFM_VERSION_MIN, + IA64_PERFMON_VECTOR); + + if (pfm_probe_pmu()) { + printk(KERN_INFO "perfmon: disabled, there is no support for processor family %d\n", + local_cpu_data->family); + return -ENODEV; + } + + /* + * compute the number of implemented PMD/PMC from the + * description tables + */ + n = 0; + for (i=0; PMC_IS_LAST(i) == 0; i++) { + if (PMC_IS_IMPL(i) == 0) continue; + pmu_conf->impl_pmcs[i>>6] |= 1UL << (i&63); + n++; + } + pmu_conf->num_pmcs = n; + + n = 0; n_counters = 0; + for (i=0; PMD_IS_LAST(i) == 0; i++) { + if (PMD_IS_IMPL(i) == 0) continue; + pmu_conf->impl_pmds[i>>6] |= 1UL << (i&63); + n++; + if (PMD_IS_COUNTING(i)) n_counters++; + } + pmu_conf->num_pmds = n; + pmu_conf->num_counters = n_counters; + + /* + * sanity checks on the number of debug registers + */ + if (pmu_conf->use_rr_dbregs) { + if (pmu_conf->num_ibrs > IA64_NUM_DBG_REGS) { + printk(KERN_INFO "perfmon: unsupported number of code debug registers (%u)\n", pmu_conf->num_ibrs); + pmu_conf = NULL; + return -1; + } + if (pmu_conf->num_dbrs > IA64_NUM_DBG_REGS) { + printk(KERN_INFO "perfmon: unsupported number of data debug registers (%u)\n", pmu_conf->num_ibrs); + pmu_conf = NULL; + return -1; + } + } + + printk("perfmon: %s PMU detected, %u PMCs, %u PMDs, %u counters (%lu bits)\n", + pmu_conf->pmu_name, + pmu_conf->num_pmcs, + pmu_conf->num_pmds, + pmu_conf->num_counters, + ffz(pmu_conf->ovfl_val)); + + /* sanity check */ + if (pmu_conf->num_pmds >= IA64_NUM_PMD_REGS || pmu_conf->num_pmcs >= IA64_NUM_PMC_REGS) { + printk(KERN_ERR "perfmon: not enough pmc/pmd, perfmon disabled\n"); + pmu_conf = NULL; + return -1; + } + +#ifndef XEN + /* + * create /proc/perfmon (mostly for debugging purposes) + */ + perfmon_dir = create_proc_entry("perfmon", S_IRUGO, NULL); + if (perfmon_dir == NULL) { + printk(KERN_ERR "perfmon: cannot create /proc entry, perfmon disabled\n"); + pmu_conf = NULL; + return -1; + } + /* + * install customized file operations for /proc/perfmon entry + */ + perfmon_dir->proc_fops = &pfm_proc_fops; + + /* + * create /proc/sys/kernel/perfmon (for debugging purposes) + */ + pfm_sysctl_header = register_sysctl_table(pfm_sysctl_root, 0); +#endif + + /* + * initialize all our spinlocks + */ + spin_lock_init(&pfm_sessions.pfs_lock); + spin_lock_init(&pfm_buffer_fmt_lock); + + init_pfm_fs(); + + for(i=0; i < NR_CPUS; i++) pfm_stats[i].pfm_ovfl_intr_cycles_min = ~0UL; + + return 0; +} + +__initcall(pfm_init); + +/* + * this function is called before pfm_init() + */ +void +pfm_init_percpu (void) +{ + /* + * make sure no measurement is active + * (may inherit programmed PMCs from EFI). + */ + pfm_clear_psr_pp(); + pfm_clear_psr_up(); + + /* + * we run with the PMU not frozen at all times + */ + pfm_unfreeze_pmu(); + + if (smp_processor_id() == 0) + register_percpu_irq(IA64_PERFMON_VECTOR, &perfmon_irqaction); + + ia64_setreg(_IA64_REG_CR_PMV, IA64_PERFMON_VECTOR); + ia64_srlz_d(); +} + +/* + * used for debug purposes only + */ +void +dump_pmu_state(const char *from) +{ + struct task_struct *task; + struct thread_struct *t; + struct pt_regs *regs; + pfm_context_t *ctx; + unsigned long psr, dcr, info, flags; + int i, this_cpu; + + local_irq_save(flags); + + this_cpu = smp_processor_id(); + regs = task_pt_regs(current); + info = PFM_CPUINFO_GET(); + dcr = ia64_getreg(_IA64_REG_CR_DCR); + + if (info == 0 && ia64_psr(regs)->pp == 0 && (dcr & IA64_DCR_PP) == 0) { + local_irq_restore(flags); + return; + } + +#ifndef XEN + printk("CPU%d from %s() current [%d] iip=0x%lx %s\n", + this_cpu, + from, + current->pid, + regs->cr_iip, + current->comm); +#endif + + task = GET_PMU_OWNER(); + ctx = GET_PMU_CTX(); + + printk("->CPU%d owner [%d] ctx=%p\n", this_cpu, task ? task->pid : -1, ctx); + + psr = pfm_get_psr(); + + printk("->CPU%d pmc0=0x%lx psr.pp=%d psr.up=%d dcr.pp=%d syst_info=0x%lx user_psr.up=%d user_psr.pp=%d\n", + this_cpu, + ia64_get_pmc(0), + psr & IA64_PSR_PP ? 1 : 0, + psr & IA64_PSR_UP ? 1 : 0, + dcr & IA64_DCR_PP ? 1 : 0, + info, + ia64_psr(regs)->up, + ia64_psr(regs)->pp); + + ia64_psr(regs)->up = 0; + ia64_psr(regs)->pp = 0; + + t = ¤t->thread; + + for (i=1; PMC_IS_LAST(i) == 0; i++) { + if (PMC_IS_IMPL(i) == 0) continue; + printk("->CPU%d pmc[%d]=0x%lx thread_pmc[%d]=0x%lx\n", this_cpu, i, ia64_get_pmc(i), i, t->pmcs[i]); + } + + for (i=1; PMD_IS_LAST(i) == 0; i++) { + if (PMD_IS_IMPL(i) == 0) continue; + printk("->CPU%d pmd[%d]=0x%lx thread_pmd[%d]=0x%lx\n", this_cpu, i, ia64_get_pmd(i), i, t->pmds[i]); + } + + if (ctx) { +#ifndef XEN + printk("->CPU%d ctx_state=%d vaddr=%p addr=%p fd=%d ctx_task=[%d] saved_psr_up=0x%lx\n", + this_cpu, + ctx->ctx_state, + ctx->ctx_smpl_vaddr, + ctx->ctx_smpl_hdr, + ctx->ctx_msgq_head, + ctx->ctx_msgq_tail, + ctx->ctx_saved_psr_up); +#else + printk("->CPU%d ctx_state=%d vaddr=%p addr=%p saved_psr_up=0x%lx\n", + this_cpu, + ctx->ctx_state, + ctx->ctx_smpl_vaddr, + ctx->ctx_smpl_hdr, + ctx->ctx_saved_psr_up); +#endif + } + local_irq_restore(flags); +} + +#ifndef XEN +/* + * called from process.c:copy_thread(). task is new child. + */ +void +pfm_inherit(struct task_struct *task, struct pt_regs *regs) +{ + struct thread_struct *thread; + + DPRINT(("perfmon: pfm_inherit clearing state for [%d]\n", task->pid)); + + thread = &task->thread; + + /* + * cut links inherited from parent (current) + */ + thread->pfm_context = NULL; + + PFM_SET_WORK_PENDING(task, 0); + + /* + * the psr bits are already set properly in copy_threads() + */ +} +#endif +#else /* !CONFIG_PERFMON */ +asmlinkage long +sys_perfmonctl (int fd, int cmd, void *arg, int count) +{ + return -ENOSYS; +} +#endif /* CONFIG_PERFMON */ + + +#ifdef XEN +static int xenpfm_context_unload(void); +static int xenpfm_start_stop_locked(int is_start); +DEFINE_PER_CPU(pfm_context_t*, xenpfm_context); + +/* + * note: some functions mask interrupt with this lock held + * so that this lock can't be locked from interrupt handler. + * lock order domlist_lock => xenpfm_context_lock + */ +DEFINE_SPINLOCK(xenpfm_context_lock); + +static int +xenpfm_get_features(XEN_GUEST_HANDLE(pfarg_features_t) req) +{ + pfarg_features_t res; + if (guest_handle_is_null(req)) + return -EFAULT; + + memset(&res, 0, sizeof(res)); + pfm_get_features(NULL, &res, 0, NULL); + if (copy_to_guest(req, &res, 1)) + return -EFAULT; + return 0; +} + +static int +xenpfm_pfarg_is_sane(pfarg_context_t* pfx) +{ + int error; + int ctx_flags; + + error = pfarg_is_sane(NULL, pfx); + if (error) + return error; + + ctx_flags = pfx->ctx_flags; + if (!(ctx_flags & PFM_FL_SYSTEM_WIDE) || + ctx_flags & PFM_FL_NOTIFY_BLOCK || + ctx_flags & PFM_FL_OVFL_NO_MSG) + return -EINVAL; + + /* probably more to add here */ + + return 0; +} + +static int +xenpfm_context_create(XEN_GUEST_HANDLE(pfarg_context_t) req) +{ + int error; + pfarg_context_t kreq; + + int cpu; + pfm_context_t* ctx[NR_CPUS] = {[0 ... (NR_CPUS - 1)] = NULL}; + + if (copy_from_guest(&kreq, req, 1)) { + error = -EINVAL; + goto out; + } + + error = xenpfm_pfarg_is_sane(&kreq); + if (error) + goto out; + + /* XXX fmt */ + for_each_cpu(cpu) { + ctx[cpu] = pfm_context_create(&kreq); + if (ctx[cpu] == NULL) { + error = -ENOMEM; + break; + } + } + if (error) + goto out; + + BUG_ON(in_irq()); + spin_lock(&xenpfm_context_lock); + for_each_cpu(cpu) { + if (per_cpu(xenpfm_context, cpu) != NULL) { + error = -EBUSY; + break; + } + } + for_each_cpu(cpu) { + per_cpu(xenpfm_context, cpu) = ctx[cpu]; + ctx[cpu] = NULL; + } + spin_unlock(&xenpfm_context_lock); + +out: + for_each_cpu(cpu) { + if (ctx[cpu] != NULL) + pfm_context_free(ctx[cpu]); + } + return error; +} + +static int +xenpfm_context_destroy(void) +{ + int cpu; + pfm_context_t* ctx; + unsigned long flags; + unsigned long need_unload; + int error = 0; + +again: + need_unload = 0; + BUG_ON(in_irq()); + spin_lock_irqsave(&xenpfm_context_lock, flags); + for_each_cpu(cpu) { + ctx = per_cpu(xenpfm_context, cpu); + if (ctx == NULL) { + error = -EINVAL; + break; + } + PROTECT_CTX_NOIRQ(ctx); + if (ctx->ctx_state != PFM_CTX_UNLOADED) + need_unload = 1; + } + if (error) { + for_each_cpu(cpu) { + ctx = per_cpu(xenpfm_context, cpu); + if (ctx == NULL) + break; + UNPROTECT_CTX_NOIRQ(per_cpu(xenpfm_context, cpu)); + } + goto out; + } + if (need_unload) { + for_each_cpu(cpu) + UNPROTECT_CTX_NOIRQ(per_cpu(xenpfm_context, cpu)); + spin_unlock_irqrestore(&xenpfm_context_lock, flags); + + error = xenpfm_context_unload(); + if (error) + return error; + goto again; + } + + for_each_cpu(cpu) { + pfm_context_t* ctx = per_cpu(xenpfm_context, cpu); + per_cpu(xenpfm_context, cpu) = NULL; + + /* pfm_close() unlocks spinlock and free the context. */ + error |= pfm_close(ctx); + } +out: + spin_unlock_irqrestore(&xenpfm_context_lock, flags); + return error; +} + +static int +xenpfm_write_pmcs(XEN_GUEST_HANDLE(pfarg_reg_t) req, unsigned long count) +{ + unsigned long i; + int error = 0; + unsigned long flags; + + for (i = 0; i < count; i++) { + pfarg_reg_t kreq; + int cpu; + if (copy_from_guest_offset(&kreq, req, i, 1)) { + error = -EFAULT; + goto out; + } + BUG_ON(in_irq()); + spin_lock_irqsave(&xenpfm_context_lock, flags); + for_each_online_cpu(cpu) { + pfm_context_t* ctx = per_cpu(xenpfm_context, cpu); + PROTECT_CTX_NOIRQ(ctx); + error |= pfm_write_pmcs(ctx, (void *)&kreq, 1, NULL); + UNPROTECT_CTX_NOIRQ(ctx); + } + spin_unlock_irqrestore(&xenpfm_context_lock, flags); + if (error) + break; + } + + /* XXX if is loaded, change all physical cpus pmcs. */ + /* Currently results in error */ +out: + return error; +} + +static int +xenpfm_write_pmds(XEN_GUEST_HANDLE(pfarg_reg_t) req, unsigned long count) +{ + unsigned long i; + int error = 0; + + for (i = 0; i < count; i++) { + pfarg_reg_t kreq; + int cpu; + unsigned long flags; + if (copy_from_guest_offset(&kreq, req, i, 1)) { + error = -EFAULT; + goto out; + } + BUG_ON(in_irq()); + spin_lock_irqsave(&xenpfm_context_lock, flags); + for_each_online_cpu(cpu) { + pfm_context_t* ctx = per_cpu(xenpfm_context, cpu); + PROTECT_CTX_NOIRQ(ctx); + error |= pfm_write_pmds(ctx, &kreq, 1, NULL); + UNPROTECT_CTX_NOIRQ(ctx); + } + spin_unlock_irqrestore(&xenpfm_context_lock, flags); + } + + /* XXX if is loaded, change all physical cpus pmds. */ + /* Currently results in error */ +out: + return error; +} + +struct xenpfm_context_load_arg { + pfarg_load_t* req; + int error[NR_CPUS]; +}; + +static void +xenpfm_context_load_cpu(void* info) +{ + unsigned long flags; + struct xenpfm_context_load_arg* arg = (struct xenpfm_context_load_arg*)info; + pfm_context_t* ctx = __get_cpu_var(xenpfm_context); + PROTECT_CTX(ctx, flags); + arg->error[smp_processor_id()] = pfm_context_load(ctx, arg->req, 0, NULL); + UNPROTECT_CTX(ctx, flags); +} + +static int +xenpfm_context_load(XEN_GUEST_HANDLE(pfarg_load_t) req) +{ + pfarg_load_t kreq; + int cpu; + struct xenpfm_context_load_arg arg; + int error = 0; + + if (copy_from_guest(&kreq, req, 1)) + return -EFAULT; + + arg.req = &kreq; + for_each_online_cpu(cpu) + arg.error[cpu] = 0; + + BUG_ON(in_irq()); + spin_lock(&xenpfm_context_lock); + smp_call_function(&xenpfm_context_load_cpu, &arg, 1, 1); + xenpfm_context_load_cpu(&arg); + spin_unlock(&xenpfm_context_lock); + for_each_online_cpu(cpu) { + if (arg.error[cpu]) { + gdprintk(XENLOG_INFO, "%s: error %d cpu %d\n", + __func__, error, cpu); + error = arg.error[cpu]; + } + } + return 0; +} + + +struct xenpfm_context_unload_arg { + int error[NR_CPUS]; +}; + +static void +xenpfm_context_unload_cpu(void* info) +{ + unsigned long flags; + struct xenpfm_context_unload_arg* arg = (struct xenpfm_context_unload_arg*)info; + pfm_context_t* ctx = __get_cpu_var(xenpfm_context); + PROTECT_CTX(ctx, flags); + arg->error[smp_processor_id()] = pfm_context_unload(ctx, NULL, 0, NULL); + UNPROTECT_CTX(ctx, flags); +} + +static int +xenpfm_context_unload(void) +{ + int cpu; + struct xenpfm_context_unload_arg arg; + int error = 0; + + for_each_online_cpu(cpu) + arg.error[cpu] = 0; + + BUG_ON(in_irq()); + read_lock(&domlist_lock); + spin_lock(&xenpfm_context_lock); + error = xenpfm_start_stop_locked(0); + read_unlock(&domlist_lock); + if (error) { + spin_unlock(&xenpfm_context_lock); + return error; + } + + smp_call_function(&xenpfm_context_unload_cpu, &arg, 1, 1); + xenpfm_context_unload_cpu(&arg); + spin_unlock(&xenpfm_context_lock); + for_each_online_cpu(cpu) { + if (arg.error[cpu]) { + gdprintk(XENLOG_INFO, "%s: error %d cpu %d\n", + __func__, error, cpu); + error = arg.error[cpu]; + } + } + return error; +} + +static int +__xenpfm_start(void) +{ + pfm_context_t* ctx = __get_cpu_var(xenpfm_context); + int state; + int error = 0; + + BUG_ON(local_irq_is_enabled()); + PROTECT_CTX_NOIRQ(ctx); + state = ctx->ctx_state; + if (state != PFM_CTX_LOADED) { + error = -EINVAL; + goto out; + } + + /* now update the local PMU and cpuinfo */ + PFM_CPUINFO_SET(PFM_CPUINFO_DCR_PP); + + /* start monitoring at kernel level */ + pfm_set_psr_pp(); + + /* start monitoring at kernel level */ + pfm_set_psr_up(); + + /* enable dcr pp */ + ia64_setreg(_IA64_REG_CR_DCR, ia64_getreg(_IA64_REG_CR_DCR) | IA64_DCR_PP); + ia64_srlz_i(); +out: + UNPROTECT_CTX_NOIRQ(ctx); + return error; +} + +static int +__xenpfm_stop(void) +{ + pfm_context_t* ctx = __get_cpu_var(xenpfm_context); + int state; + int error = 0; + + BUG_ON(local_irq_is_enabled()); + PROTECT_CTX_NOIRQ(ctx); + state = ctx->ctx_state; + if (state != PFM_CTX_LOADED) { + error = -EINVAL; + goto out; + } + + /* + * Update local PMU first + * + * disable dcr pp + */ + ia64_setreg(_IA64_REG_CR_DCR, ia64_getreg(_IA64_REG_CR_DCR) & ~IA64_DCR_PP); + ia64_srlz_i(); + + /* update local cpuinfo */ + PFM_CPUINFO_CLEAR(PFM_CPUINFO_DCR_PP); + + /* stop monitoring, does srlz.i */ + pfm_clear_psr_pp(); + + /* stop monitoring at kernel level */ + pfm_clear_psr_up(); +out: + UNPROTECT_CTX_NOIRQ(ctx); + return error; +} + +int +__xenpfm_start_stop(int is_start) +{ + if (is_start) + return __xenpfm_start(); + else + return __xenpfm_stop(); +} + +struct xenpfm_start_arg { + int is_start; + atomic_t started; + atomic_t finished; + int error[NR_CPUS]; +}; + +static void +xenpfm_start_stop_cpu(void* info) +{ + unsigned long flags; + struct xenpfm_start_arg* arg = (struct xenpfm_start_arg*)info; + + local_irq_save(flags); + atomic_inc(&arg->started); + while (!atomic_read(&arg->finished)) + cpu_relax(); + + arg->error[smp_processor_id()] = __xenpfm_start_stop(arg->is_start); + + atomic_inc(&arg->finished); + local_irq_restore(flags); +} + +static void +xenpfm_start_stop_vcpu(struct vcpu* v, int is_start) +{ + struct pt_regs *regs = vcpu_regs(v); + + if (is_start) { + /* set user level psr.pp for the caller */ + ia64_psr(regs)->pp = 1; + + /* activate monitoring at user level */ + ia64_psr(regs)->up = 1; + + /* don't allow user level control */ + ia64_psr(regs)->sp = 0; + } else { + /* + * stop monitoring in the caller + */ + ia64_psr(regs)->pp = 0; + + /* + * stop monitoring at the user level + */ + ia64_psr(regs)->up = 0; + +#if 0 + /* + * cancel user level control + */ + ia64_psr(regs)->sp = 1; +#endif + } +} + +static int +xenpfm_start_stop_locked(int is_start) +{ + struct xenpfm_start_arg arg; + int cpus = num_online_cpus(); + int cpu; + unsigned long flags; + struct domain* d; + struct vcpu* v; + int error = 0; + + arg.is_start = is_start; + atomic_set(&arg.started, 1); /* 1 for this cpu */ + atomic_set(&arg.finished, 0); + for_each_cpu(cpu) + arg.error[cpu] = 0; + + BUG_ON(!spin_is_locked(&xenpfm_context_lock)); + smp_call_function(&xenpfm_start_stop_cpu, &arg, 1, 0); + local_irq_save(flags); + + while (atomic_read(&arg.started) != cpus) + cpu_relax(); + + for_each_domain(d) { + for_each_vcpu(d, v) + xenpfm_start_stop_vcpu(v, is_start); + } + + arg.error[smp_processor_id()] = __xenpfm_start_stop(is_start); + atomic_inc(&arg.finished); + + while (atomic_read(&arg.finished) != cpus) + cpu_relax(); + local_irq_restore(flags); + + for_each_online_cpu(cpu) { + if (!arg.error[cpu]) { + gdprintk(XENLOG_INFO, "%s: cpu %d error %d\n", + __func__, cpu, arg.error[cpu]); + error = arg.error[cpu]; + } + } + return error; +} + +static int +xenpfm_start_stop(int is_start) +{ + int error; + + BUG_ON(in_irq()); + read_lock(&domlist_lock); + spin_lock(&xenpfm_context_lock); + error =xenpfm_start_stop_locked(is_start); + spin_unlock(&xenpfm_context_lock); + read_unlock(&domlist_lock); + + return error; +} + +#define NONPRIV_OP(cmd) (((cmd) == PFM_GET_FEATURES)) + +int +do_perfmon_op(unsigned long cmd, + XEN_GUEST_HANDLE(void) arg1, unsigned long count) +{ + unsigned long error = 0; + + if (!NONPRIV_OP(cmd) && current->domain != xenoprof_primary_profiler) { + gdprintk(XENLOG_INFO, "xen perfmon: " + "dom %d denied privileged operation %ld\n", + current->domain->domain_id, cmd); + return -EPERM; + } + switch (cmd) { + case PFM_GET_FEATURES: + error = xenpfm_get_features(guest_handle_cast(arg1, pfarg_features_t)); + break; + + case PFM_CREATE_CONTEXT: + error = xenpfm_context_create(guest_handle_cast(arg1, pfarg_context_t)); + break; + case PFM_DESTROY_CONTEXT: + error = xenpfm_context_destroy(); + break; + + case PFM_WRITE_PMCS: + error = xenpfm_write_pmcs(guest_handle_cast(arg1, pfarg_reg_t), count); + break; + case PFM_WRITE_PMDS: + error = xenpfm_write_pmds(guest_handle_cast(arg1, pfarg_reg_t), count); + break; + case PFM_READ_PMDS: + error = -ENOSYS; + break; + case PFM_GET_PMC_RESET_VAL: + error = -ENOSYS; + break; + + case PFM_LOAD_CONTEXT: + error = xenpfm_context_load(guest_handle_cast(arg1, pfarg_load_t)); + break; + case PFM_UNLOAD_CONTEXT: + error = xenpfm_context_unload(); + break; + + case PFM_START: + error = xenpfm_start_stop(1); + break; + case PFM_STOP: + error = xenpfm_start_stop(0); + break; + case PFM_RESTART: + error = -ENOSYS; + break; + + case PFM_DEBUG: + error = -ENOSYS; + break; + + case PFM_ENABLE: + case PFM_DISABLE: + case PFM_PROTECT_CONTEXT: + case PFM_UNPROTECT_CONTEXT: + default: + error = -EINVAL; + break; + } + return error; +} +#endif diff --git a/xen/arch/ia64/linux-xen/perfmon_default_smpl.c b/xen/arch/ia64/linux-xen/perfmon_default_smpl.c new file mode 100644 index 0000000000..f10a5a6f0a --- /dev/null +++ b/xen/arch/ia64/linux-xen/perfmon_default_smpl.c @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2002-2003 Hewlett-Packard Co + * Stephane Eranian + * + * This file implements the default sampling buffer format + * for the Linux/ia64 perfmon-2 subsystem. + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifndef XEN +MODULE_AUTHOR("Stephane Eranian "); +MODULE_DESCRIPTION("perfmon default sampling format"); +MODULE_LICENSE("GPL"); +#endif + +#ifdef XEN +#define pid vcpu_id +#endif + +#define DEFAULT_DEBUG 1 + +#ifdef DEFAULT_DEBUG +#define DPRINT(a) \ + do { \ + if (unlikely(pfm_sysctl.debug >0)) { printk("%s.%d: CPU%d ", __FUNCTION__, __LINE__, smp_processor_id()); printk a; } \ + } while (0) + +#define DPRINT_ovfl(a) \ + do { \ + if (unlikely(pfm_sysctl.debug > 0 && pfm_sysctl.debug_ovfl >0)) { printk("%s.%d: CPU%d ", __FUNCTION__, __LINE__, smp_processor_id()); printk a; } \ + } while (0) + +#else +#define DPRINT(a) +#define DPRINT_ovfl(a) +#endif + +static int +default_validate(struct task_struct *task, unsigned int flags, int cpu, void *data) +{ + pfm_default_smpl_arg_t *arg = (pfm_default_smpl_arg_t*)data; + int ret = 0; + + if (data == NULL) { + DPRINT(("[%d] no argument passed\n", task->pid)); + return -EINVAL; + } + + DPRINT(("[%d] validate flags=0x%x CPU%d\n", task->pid, flags, cpu)); + + /* + * must hold at least the buffer header + one minimally sized entry + */ + if (arg->buf_size < PFM_DEFAULT_SMPL_MIN_BUF_SIZE) return -EINVAL; + + DPRINT(("buf_size=%lu\n", arg->buf_size)); + + return ret; +} + +static int +default_get_size(struct task_struct *task, unsigned int flags, int cpu, void *data, unsigned long *size) +{ + pfm_default_smpl_arg_t *arg = (pfm_default_smpl_arg_t *)data; + + /* + * size has been validated in default_validate + */ + *size = arg->buf_size; + + return 0; +} + +static int +default_init(struct task_struct *task, void *buf, unsigned int flags, int cpu, void *data) +{ + pfm_default_smpl_hdr_t *hdr; + pfm_default_smpl_arg_t *arg = (pfm_default_smpl_arg_t *)data; + + hdr = (pfm_default_smpl_hdr_t *)buf; + + hdr->hdr_version = PFM_DEFAULT_SMPL_VERSION; + hdr->hdr_buf_size = arg->buf_size; + hdr->hdr_cur_offs = sizeof(*hdr); + hdr->hdr_overflows = 0UL; + hdr->hdr_count = 0UL; + + DPRINT(("[%d] buffer=%p buf_size=%lu hdr_size=%lu hdr_version=%u cur_offs=%lu\n", + task->pid, + buf, + hdr->hdr_buf_size, + sizeof(*hdr), + hdr->hdr_version, + hdr->hdr_cur_offs)); + + return 0; +} + +static int +default_handler(struct task_struct *task, void *buf, pfm_ovfl_arg_t *arg, struct pt_regs *regs, unsigned long stamp) +{ + pfm_default_smpl_hdr_t *hdr; + pfm_default_smpl_entry_t *ent; + void *cur, *last; + unsigned long *e, entry_size; + unsigned int npmds, i; + unsigned char ovfl_pmd; + unsigned char ovfl_notify; + + if (unlikely(buf == NULL || arg == NULL|| regs == NULL || task == NULL)) { + DPRINT(("[%d] invalid arguments buf=%p arg=%p\n", task->pid, buf, arg)); + return -EINVAL; + } + + hdr = (pfm_default_smpl_hdr_t *)buf; + cur = buf+hdr->hdr_cur_offs; + last = buf+hdr->hdr_buf_size; + ovfl_pmd = arg->ovfl_pmd; + ovfl_notify = arg->ovfl_notify; + + /* + * precheck for sanity + */ + if ((last - cur) < PFM_DEFAULT_MAX_ENTRY_SIZE) goto full; + + npmds = hweight64(arg->smpl_pmds[0]); + + ent = (pfm_default_smpl_entry_t *)cur; + + prefetch(arg->smpl_pmds_values); + + entry_size = sizeof(*ent) + (npmds << 3); + + /* position for first pmd */ + e = (unsigned long *)(ent+1); + + hdr->hdr_count++; + + DPRINT_ovfl(("[%d] count=%lu cur=%p last=%p free_bytes=%lu ovfl_pmd=%d ovfl_notify=%d npmds=%u\n", + task->pid, + hdr->hdr_count, + cur, last, + last-cur, + ovfl_pmd, + ovfl_notify, npmds)); + + /* + * current = task running at the time of the overflow. + * + * per-task mode: + * - this is ususally the task being monitored. + * Under certain conditions, it might be a different task + * + * system-wide: + * - this is not necessarily the task controlling the session + */ +#ifndef XEN + ent->pid = current->pid; +#endif + ent->ovfl_pmd = ovfl_pmd; + ent->last_reset_val = arg->pmd_last_reset; //pmd[0].reg_last_reset_val; + + /* + * where did the fault happen (includes slot number) + */ + ent->ip = regs->cr_iip | ((regs->cr_ipsr >> 41) & 0x3); + + ent->tstamp = stamp; + ent->cpu = smp_processor_id(); + ent->set = arg->active_set; +#ifndef XEN + ent->tgid = current->tgid; +#endif + + /* + * selectively store PMDs in increasing index number + */ + if (npmds) { + unsigned long *val = arg->smpl_pmds_values; + for(i=0; i < npmds; i++) { + *e++ = *val++; + } + } + + /* + * update position for next entry + */ + hdr->hdr_cur_offs += entry_size; + cur += entry_size; + + /* + * post check to avoid losing the last sample + */ + if ((last - cur) < PFM_DEFAULT_MAX_ENTRY_SIZE) goto full; + + /* + * keep same ovfl_pmds, ovfl_notify + */ + arg->ovfl_ctrl.bits.notify_user = 0; + arg->ovfl_ctrl.bits.block_task = 0; + arg->ovfl_ctrl.bits.mask_monitoring = 0; + arg->ovfl_ctrl.bits.reset_ovfl_pmds = 1; /* reset before returning from interrupt handler */ + + return 0; +full: + DPRINT_ovfl(("sampling buffer full free=%lu, count=%lu, ovfl_notify=%d\n", last-cur, hdr->hdr_count, ovfl_notify)); + + /* + * increment number of buffer overflow. + * important to detect duplicate set of samples. + */ + hdr->hdr_overflows++; + + /* + * if no notification requested, then we saturate the buffer + */ + if (ovfl_notify == 0) { + arg->ovfl_ctrl.bits.notify_user = 0; + arg->ovfl_ctrl.bits.block_task = 0; + arg->ovfl_ctrl.bits.mask_monitoring = 1; + arg->ovfl_ctrl.bits.reset_ovfl_pmds = 0; + } else { + arg->ovfl_ctrl.bits.notify_user = 1; + arg->ovfl_ctrl.bits.block_task = 1; /* ignored for non-blocking context */ + arg->ovfl_ctrl.bits.mask_monitoring = 1; + arg->ovfl_ctrl.bits.reset_ovfl_pmds = 0; /* no reset now */ + } + return -1; /* we are full, sorry */ +} + +static int +default_restart(struct task_struct *task, pfm_ovfl_ctrl_t *ctrl, void *buf, struct pt_regs *regs) +{ + pfm_default_smpl_hdr_t *hdr; + + hdr = (pfm_default_smpl_hdr_t *)buf; + + hdr->hdr_count = 0UL; + hdr->hdr_cur_offs = sizeof(*hdr); + + ctrl->bits.mask_monitoring = 0; + ctrl->bits.reset_ovfl_pmds = 1; /* uses long-reset values */ + + return 0; +} + +static int +default_exit(struct task_struct *task, void *buf, struct pt_regs *regs) +{ + DPRINT(("[%d] exit(%p)\n", task->pid, buf)); + return 0; +} + +static pfm_buffer_fmt_t default_fmt={ + .fmt_name = "default_format", + .fmt_uuid = PFM_DEFAULT_SMPL_UUID, + .fmt_arg_size = sizeof(pfm_default_smpl_arg_t), + .fmt_validate = default_validate, + .fmt_getsize = default_get_size, + .fmt_init = default_init, + .fmt_handler = default_handler, + .fmt_restart = default_restart, + .fmt_restart_active = default_restart, + .fmt_exit = default_exit, +}; + +#ifndef XEN +static int __init +pfm_default_smpl_init_module(void) +{ + int ret; + + ret = pfm_register_buffer_fmt(&default_fmt); + if (ret == 0) { + printk("perfmon_default_smpl: %s v%u.%u registered\n", + default_fmt.fmt_name, + PFM_DEFAULT_SMPL_VERSION_MAJ, + PFM_DEFAULT_SMPL_VERSION_MIN); + } else { + printk("perfmon_default_smpl: %s cannot register ret=%d\n", + default_fmt.fmt_name, + ret); + } + + return ret; +} +#endif + +static void __exit +pfm_default_smpl_cleanup_module(void) +{ + int ret; + ret = pfm_unregister_buffer_fmt(default_fmt.fmt_uuid); + + printk("perfmon_default_smpl: unregister %s=%d\n", default_fmt.fmt_name, ret); +} + +#ifndef XEN +module_init(pfm_default_smpl_init_module); +module_exit(pfm_default_smpl_cleanup_module); +#endif + diff --git a/xen/arch/ia64/linux-xen/perfmon_generic.h b/xen/arch/ia64/linux-xen/perfmon_generic.h new file mode 100644 index 0000000000..6748947804 --- /dev/null +++ b/xen/arch/ia64/linux-xen/perfmon_generic.h @@ -0,0 +1,45 @@ +/* + * This file contains the generic PMU register description tables + * and pmc checker used by perfmon.c. + * + * Copyright (C) 2002-2003 Hewlett Packard Co + * Stephane Eranian + */ + +static pfm_reg_desc_t pfm_gen_pmc_desc[PMU_MAX_PMCS]={ +/* pmc0 */ { PFM_REG_CONTROL , 0, 0x1UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc1 */ { PFM_REG_CONTROL , 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc2 */ { PFM_REG_CONTROL , 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc3 */ { PFM_REG_CONTROL , 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc4 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {RDEP(4),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc5 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {RDEP(5),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc6 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {RDEP(6),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc7 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {RDEP(7),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, + { PFM_REG_END , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}}, /* end marker */ +}; + +static pfm_reg_desc_t pfm_gen_pmd_desc[PMU_MAX_PMDS]={ +/* pmd0 */ { PFM_REG_NOTIMPL , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}}, +/* pmd1 */ { PFM_REG_NOTIMPL , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}}, +/* pmd2 */ { PFM_REG_NOTIMPL , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}}, +/* pmd3 */ { PFM_REG_NOTIMPL , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}}, +/* pmd4 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(4),0UL, 0UL, 0UL}}, +/* pmd5 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(5),0UL, 0UL, 0UL}}, +/* pmd6 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(6),0UL, 0UL, 0UL}}, +/* pmd7 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(7),0UL, 0UL, 0UL}}, + { PFM_REG_END , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}}, /* end marker */ +}; + +/* + * impl_pmcs, impl_pmds are computed at runtime to minimize errors! + */ +static pmu_config_t pmu_conf_gen={ + .pmu_name = "Generic", + .pmu_family = 0xff, /* any */ + .ovfl_val = (1UL << 32) - 1, + .num_ibrs = 0, /* does not use */ + .num_dbrs = 0, /* does not use */ + .pmd_desc = pfm_gen_pmd_desc, + .pmc_desc = pfm_gen_pmc_desc +}; + diff --git a/xen/arch/ia64/linux-xen/perfmon_itanium.h b/xen/arch/ia64/linux-xen/perfmon_itanium.h new file mode 100644 index 0000000000..d1d508a0fb --- /dev/null +++ b/xen/arch/ia64/linux-xen/perfmon_itanium.h @@ -0,0 +1,115 @@ +/* + * This file contains the Itanium PMU register description tables + * and pmc checker used by perfmon.c. + * + * Copyright (C) 2002-2003 Hewlett Packard Co + * Stephane Eranian + */ +static int pfm_ita_pmc_check(struct task_struct *task, pfm_context_t *ctx, unsigned int cnum, unsigned long *val, struct pt_regs *regs); + +static pfm_reg_desc_t pfm_ita_pmc_desc[PMU_MAX_PMCS]={ +/* pmc0 */ { PFM_REG_CONTROL , 0, 0x1UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc1 */ { PFM_REG_CONTROL , 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc2 */ { PFM_REG_CONTROL , 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc3 */ { PFM_REG_CONTROL , 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc4 */ { PFM_REG_COUNTING, 6, 0x0UL, -1UL, NULL, NULL, {RDEP(4),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc5 */ { PFM_REG_COUNTING, 6, 0x0UL, -1UL, NULL, NULL, {RDEP(5),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc6 */ { PFM_REG_COUNTING, 6, 0x0UL, -1UL, NULL, NULL, {RDEP(6),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc7 */ { PFM_REG_COUNTING, 6, 0x0UL, -1UL, NULL, NULL, {RDEP(7),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc8 */ { PFM_REG_CONFIG , 0, 0xf00000003ffffff8UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc9 */ { PFM_REG_CONFIG , 0, 0xf00000003ffffff8UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc10 */ { PFM_REG_MONITOR , 6, 0x0UL, -1UL, NULL, NULL, {RDEP(0)|RDEP(1),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc11 */ { PFM_REG_MONITOR , 6, 0x0000000010000000UL, -1UL, NULL, pfm_ita_pmc_check, {RDEP(2)|RDEP(3)|RDEP(17),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc12 */ { PFM_REG_MONITOR , 6, 0x0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc13 */ { PFM_REG_CONFIG , 0, 0x0003ffff00000001UL, -1UL, NULL, pfm_ita_pmc_check, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, + { PFM_REG_END , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}}, /* end marker */ +}; + +static pfm_reg_desc_t pfm_ita_pmd_desc[PMU_MAX_PMDS]={ +/* pmd0 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(1),0UL, 0UL, 0UL}, {RDEP(10),0UL, 0UL, 0UL}}, +/* pmd1 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(0),0UL, 0UL, 0UL}, {RDEP(10),0UL, 0UL, 0UL}}, +/* pmd2 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(3)|RDEP(17),0UL, 0UL, 0UL}, {RDEP(11),0UL, 0UL, 0UL}}, +/* pmd3 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(2)|RDEP(17),0UL, 0UL, 0UL}, {RDEP(11),0UL, 0UL, 0UL}}, +/* pmd4 */ { PFM_REG_COUNTING, 0, 0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(4),0UL, 0UL, 0UL}}, +/* pmd5 */ { PFM_REG_COUNTING, 0, 0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(5),0UL, 0UL, 0UL}}, +/* pmd6 */ { PFM_REG_COUNTING, 0, 0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(6),0UL, 0UL, 0UL}}, +/* pmd7 */ { PFM_REG_COUNTING, 0, 0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(7),0UL, 0UL, 0UL}}, +/* pmd8 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd9 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd10 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd11 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd12 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd13 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd14 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd15 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd16 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd17 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(2)|RDEP(3),0UL, 0UL, 0UL}, {RDEP(11),0UL, 0UL, 0UL}}, + { PFM_REG_END , 0, 0UL, -1UL, NULL, NULL, {0,}, {0,}}, /* end marker */ +}; + +static int +pfm_ita_pmc_check(struct task_struct *task, pfm_context_t *ctx, unsigned int cnum, unsigned long *val, struct pt_regs *regs) +{ + int ret; + int is_loaded; + + /* sanitfy check */ + if (ctx == NULL) return -EINVAL; + + is_loaded = ctx->ctx_state == PFM_CTX_LOADED || ctx->ctx_state == PFM_CTX_MASKED; + + /* + * we must clear the (instruction) debug registers if pmc13.ta bit is cleared + * before they are written (fl_using_dbreg==0) to avoid picking up stale information. + */ + if (cnum == 13 && is_loaded && ((*val & 0x1) == 0UL) && ctx->ctx_fl_using_dbreg == 0) { + + DPRINT(("pmc[%d]=0x%lx has active pmc13.ta cleared, clearing ibr\n", cnum, *val)); + + /* don't mix debug with perfmon */ + if (task && (task->thread.flags & IA64_THREAD_DBG_VALID) != 0) return -EINVAL; + + /* + * a count of 0 will mark the debug registers as in use and also + * ensure that they are properly cleared. + */ + ret = pfm_write_ibr_dbr(1, ctx, NULL, 0, regs); + if (ret) return ret; + } + + /* + * we must clear the (data) debug registers if pmc11.pt bit is cleared + * before they are written (fl_using_dbreg==0) to avoid picking up stale information. + */ + if (cnum == 11 && is_loaded && ((*val >> 28)& 0x1) == 0 && ctx->ctx_fl_using_dbreg == 0) { + + DPRINT(("pmc[%d]=0x%lx has active pmc11.pt cleared, clearing dbr\n", cnum, *val)); + + /* don't mix debug with perfmon */ + if (task && (task->thread.flags & IA64_THREAD_DBG_VALID) != 0) return -EINVAL; + + /* + * a count of 0 will mark the debug registers as in use and also + * ensure that they are properly cleared. + */ + ret = pfm_write_ibr_dbr(0, ctx, NULL, 0, regs); + if (ret) return ret; + } + return 0; +} + +/* + * impl_pmcs, impl_pmds are computed at runtime to minimize errors! + */ +static pmu_config_t pmu_conf_ita={ + .pmu_name = "Itanium", + .pmu_family = 0x7, + .ovfl_val = (1UL << 32) - 1, + .pmd_desc = pfm_ita_pmd_desc, + .pmc_desc = pfm_ita_pmc_desc, + .num_ibrs = 8, + .num_dbrs = 8, + .use_rr_dbregs = 1, /* debug register are use for range retrictions */ +}; + + diff --git a/xen/arch/ia64/linux-xen/perfmon_mckinley.h b/xen/arch/ia64/linux-xen/perfmon_mckinley.h new file mode 100644 index 0000000000..9becccda28 --- /dev/null +++ b/xen/arch/ia64/linux-xen/perfmon_mckinley.h @@ -0,0 +1,187 @@ +/* + * This file contains the McKinley PMU register description tables + * and pmc checker used by perfmon.c. + * + * Copyright (C) 2002-2003 Hewlett Packard Co + * Stephane Eranian + */ +static int pfm_mck_pmc_check(struct task_struct *task, pfm_context_t *ctx, unsigned int cnum, unsigned long *val, struct pt_regs *regs); + +static pfm_reg_desc_t pfm_mck_pmc_desc[PMU_MAX_PMCS]={ +/* pmc0 */ { PFM_REG_CONTROL , 0, 0x1UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc1 */ { PFM_REG_CONTROL , 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc2 */ { PFM_REG_CONTROL , 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc3 */ { PFM_REG_CONTROL , 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc4 */ { PFM_REG_COUNTING, 6, 0x0000000000800000UL, 0xfffff7fUL, NULL, pfm_mck_pmc_check, {RDEP(4),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc5 */ { PFM_REG_COUNTING, 6, 0x0UL, 0xfffff7fUL, NULL, pfm_mck_pmc_check, {RDEP(5),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc6 */ { PFM_REG_COUNTING, 6, 0x0UL, 0xfffff7fUL, NULL, pfm_mck_pmc_check, {RDEP(6),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc7 */ { PFM_REG_COUNTING, 6, 0x0UL, 0xfffff7fUL, NULL, pfm_mck_pmc_check, {RDEP(7),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc8 */ { PFM_REG_CONFIG , 0, 0xffffffff3fffffffUL, 0xffffffff3ffffffbUL, NULL, pfm_mck_pmc_check, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc9 */ { PFM_REG_CONFIG , 0, 0xffffffff3ffffffcUL, 0xffffffff3ffffffbUL, NULL, pfm_mck_pmc_check, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc10 */ { PFM_REG_MONITOR , 4, 0x0UL, 0xffffUL, NULL, pfm_mck_pmc_check, {RDEP(0)|RDEP(1),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc11 */ { PFM_REG_MONITOR , 6, 0x0UL, 0x30f01cf, NULL, pfm_mck_pmc_check, {RDEP(2)|RDEP(3)|RDEP(17),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc12 */ { PFM_REG_MONITOR , 6, 0x0UL, 0xffffUL, NULL, pfm_mck_pmc_check, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc13 */ { PFM_REG_CONFIG , 0, 0x00002078fefefefeUL, 0x1e00018181818UL, NULL, pfm_mck_pmc_check, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc14 */ { PFM_REG_CONFIG , 0, 0x0db60db60db60db6UL, 0x2492UL, NULL, pfm_mck_pmc_check, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc15 */ { PFM_REG_CONFIG , 0, 0x00000000fffffff0UL, 0xfUL, NULL, pfm_mck_pmc_check, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, + { PFM_REG_END , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}}, /* end marker */ +}; + +static pfm_reg_desc_t pfm_mck_pmd_desc[PMU_MAX_PMDS]={ +/* pmd0 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(1),0UL, 0UL, 0UL}, {RDEP(10),0UL, 0UL, 0UL}}, +/* pmd1 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(0),0UL, 0UL, 0UL}, {RDEP(10),0UL, 0UL, 0UL}}, +/* pmd2 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(3)|RDEP(17),0UL, 0UL, 0UL}, {RDEP(11),0UL, 0UL, 0UL}}, +/* pmd3 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(2)|RDEP(17),0UL, 0UL, 0UL}, {RDEP(11),0UL, 0UL, 0UL}}, +/* pmd4 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(4),0UL, 0UL, 0UL}}, +/* pmd5 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(5),0UL, 0UL, 0UL}}, +/* pmd6 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(6),0UL, 0UL, 0UL}}, +/* pmd7 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(7),0UL, 0UL, 0UL}}, +/* pmd8 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd9 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd10 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd11 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd12 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd13 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd14 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd15 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd16 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd17 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(2)|RDEP(3),0UL, 0UL, 0UL}, {RDEP(11),0UL, 0UL, 0UL}}, + { PFM_REG_END , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}}, /* end marker */ +}; + +/* + * PMC reserved fields must have their power-up values preserved + */ +static int +pfm_mck_reserved(unsigned int cnum, unsigned long *val, struct pt_regs *regs) +{ + unsigned long tmp1, tmp2, ival = *val; + + /* remove reserved areas from user value */ + tmp1 = ival & PMC_RSVD_MASK(cnum); + + /* get reserved fields values */ + tmp2 = PMC_DFL_VAL(cnum) & ~PMC_RSVD_MASK(cnum); + + *val = tmp1 | tmp2; + + DPRINT(("pmc[%d]=0x%lx, mask=0x%lx, reset=0x%lx, val=0x%lx\n", + cnum, ival, PMC_RSVD_MASK(cnum), PMC_DFL_VAL(cnum), *val)); + return 0; +} + +/* + * task can be NULL if the context is unloaded + */ +static int +pfm_mck_pmc_check(struct task_struct *task, pfm_context_t *ctx, unsigned int cnum, unsigned long *val, struct pt_regs *regs) +{ + int ret = 0, check_case1 = 0; + unsigned long val8 = 0, val14 = 0, val13 = 0; + int is_loaded; + + /* first preserve the reserved fields */ + pfm_mck_reserved(cnum, val, regs); + + /* sanitfy check */ + if (ctx == NULL) return -EINVAL; + + is_loaded = ctx->ctx_state == PFM_CTX_LOADED || ctx->ctx_state == PFM_CTX_MASKED; + + /* + * we must clear the debug registers if pmc13 has a value which enable + * memory pipeline event constraints. In this case we need to clear the + * the debug registers if they have not yet been accessed. This is required + * to avoid picking stale state. + * PMC13 is "active" if: + * one of the pmc13.cfg_dbrpXX field is different from 0x3 + * AND + * at the corresponding pmc13.ena_dbrpXX is set. + */ + DPRINT(("cnum=%u val=0x%lx, using_dbreg=%d loaded=%d\n", cnum, *val, ctx->ctx_fl_using_dbreg, is_loaded)); + + if (cnum == 13 && is_loaded + && (*val & 0x1e00000000000UL) && (*val & 0x18181818UL) != 0x18181818UL && ctx->ctx_fl_using_dbreg == 0) { + + DPRINT(("pmc[%d]=0x%lx has active pmc13 settings, clearing dbr\n", cnum, *val)); + + /* don't mix debug with perfmon */ + if (task && (task->thread.flags & IA64_THREAD_DBG_VALID) != 0) return -EINVAL; + + /* + * a count of 0 will mark the debug registers as in use and also + * ensure that they are properly cleared. + */ + ret = pfm_write_ibr_dbr(PFM_DATA_RR, ctx, NULL, 0, regs); + if (ret) return ret; + } + /* + * we must clear the (instruction) debug registers if any pmc14.ibrpX bit is enabled + * before they are (fl_using_dbreg==0) to avoid picking up stale information. + */ + if (cnum == 14 && is_loaded && ((*val & 0x2222UL) != 0x2222UL) && ctx->ctx_fl_using_dbreg == 0) { + + DPRINT(("pmc[%d]=0x%lx has active pmc14 settings, clearing ibr\n", cnum, *val)); + + /* don't mix debug with perfmon */ + if (task && (task->thread.flags & IA64_THREAD_DBG_VALID) != 0) return -EINVAL; + + /* + * a count of 0 will mark the debug registers as in use and also + * ensure that they are properly cleared. + */ + ret = pfm_write_ibr_dbr(PFM_CODE_RR, ctx, NULL, 0, regs); + if (ret) return ret; + + } + + switch(cnum) { + case 4: *val |= 1UL << 23; /* force power enable bit */ + break; + case 8: val8 = *val; + val13 = ctx->ctx_pmcs[13]; + val14 = ctx->ctx_pmcs[14]; + check_case1 = 1; + break; + case 13: val8 = ctx->ctx_pmcs[8]; + val13 = *val; + val14 = ctx->ctx_pmcs[14]; + check_case1 = 1; + break; + case 14: val8 = ctx->ctx_pmcs[8]; + val13 = ctx->ctx_pmcs[13]; + val14 = *val; + check_case1 = 1; + break; + } + /* check illegal configuration which can produce inconsistencies in tagging + * i-side events in L1D and L2 caches + */ + if (check_case1) { + ret = ((val13 >> 45) & 0xf) == 0 + && ((val8 & 0x1) == 0) + && ((((val14>>1) & 0x3) == 0x2 || ((val14>>1) & 0x3) == 0x0) + ||(((val14>>4) & 0x3) == 0x2 || ((val14>>4) & 0x3) == 0x0)); + + if (ret) DPRINT((KERN_DEBUG "perfmon: failure check_case1\n")); + } + + return ret ? -EINVAL : 0; +} + +/* + * impl_pmcs, impl_pmds are computed at runtime to minimize errors! + */ +static pmu_config_t pmu_conf_mck={ + .pmu_name = "Itanium 2", + .pmu_family = 0x1f, + .flags = PFM_PMU_IRQ_RESEND, + .ovfl_val = (1UL << 47) - 1, + .pmd_desc = pfm_mck_pmd_desc, + .pmc_desc = pfm_mck_pmc_desc, + .num_ibrs = 8, + .num_dbrs = 8, + .use_rr_dbregs = 1 /* debug register are use for range retrictions */ +}; + + diff --git a/xen/arch/ia64/linux-xen/perfmon_montecito.h b/xen/arch/ia64/linux-xen/perfmon_montecito.h new file mode 100644 index 0000000000..cd06ac6a68 --- /dev/null +++ b/xen/arch/ia64/linux-xen/perfmon_montecito.h @@ -0,0 +1,269 @@ +/* + * This file contains the Montecito PMU register description tables + * and pmc checker used by perfmon.c. + * + * Copyright (c) 2005-2006 Hewlett-Packard Development Company, L.P. + * Contributed by Stephane Eranian + */ +static int pfm_mont_pmc_check(struct task_struct *task, pfm_context_t *ctx, unsigned int cnum, unsigned long *val, struct pt_regs *regs); + +#define RDEP_MONT_ETB (RDEP(38)|RDEP(39)|RDEP(48)|RDEP(49)|RDEP(50)|RDEP(51)|RDEP(52)|RDEP(53)|RDEP(54)|\ + RDEP(55)|RDEP(56)|RDEP(57)|RDEP(58)|RDEP(59)|RDEP(60)|RDEP(61)|RDEP(62)|RDEP(63)) +#define RDEP_MONT_DEAR (RDEP(32)|RDEP(33)|RDEP(36)) +#define RDEP_MONT_IEAR (RDEP(34)|RDEP(35)) + +static pfm_reg_desc_t pfm_mont_pmc_desc[PMU_MAX_PMCS]={ +/* pmc0 */ { PFM_REG_CONTROL , 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {0,0, 0, 0}}, +/* pmc1 */ { PFM_REG_CONTROL , 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {0,0, 0, 0}}, +/* pmc2 */ { PFM_REG_CONTROL , 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {0,0, 0, 0}}, +/* pmc3 */ { PFM_REG_CONTROL , 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {0,0, 0, 0}}, +/* pmc4 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(4),0, 0, 0}, {0,0, 0, 0}}, +/* pmc5 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(5),0, 0, 0}, {0,0, 0, 0}}, +/* pmc6 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(6),0, 0, 0}, {0,0, 0, 0}}, +/* pmc7 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(7),0, 0, 0}, {0,0, 0, 0}}, +/* pmc8 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(8),0, 0, 0}, {0,0, 0, 0}}, +/* pmc9 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(9),0, 0, 0}, {0,0, 0, 0}}, +/* pmc10 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(10),0, 0, 0}, {0,0, 0, 0}}, +/* pmc11 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(11),0, 0, 0}, {0,0, 0, 0}}, +/* pmc12 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(12),0, 0, 0}, {0,0, 0, 0}}, +/* pmc13 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(13),0, 0, 0}, {0,0, 0, 0}}, +/* pmc14 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(14),0, 0, 0}, {0,0, 0, 0}}, +/* pmc15 */ { PFM_REG_COUNTING, 6, 0x2000000, 0x7c7fff7f, NULL, pfm_mont_pmc_check, {RDEP(15),0, 0, 0}, {0,0, 0, 0}}, +/* pmc16 */ { PFM_REG_NOTIMPL, }, +/* pmc17 */ { PFM_REG_NOTIMPL, }, +/* pmc18 */ { PFM_REG_NOTIMPL, }, +/* pmc19 */ { PFM_REG_NOTIMPL, }, +/* pmc20 */ { PFM_REG_NOTIMPL, }, +/* pmc21 */ { PFM_REG_NOTIMPL, }, +/* pmc22 */ { PFM_REG_NOTIMPL, }, +/* pmc23 */ { PFM_REG_NOTIMPL, }, +/* pmc24 */ { PFM_REG_NOTIMPL, }, +/* pmc25 */ { PFM_REG_NOTIMPL, }, +/* pmc26 */ { PFM_REG_NOTIMPL, }, +/* pmc27 */ { PFM_REG_NOTIMPL, }, +/* pmc28 */ { PFM_REG_NOTIMPL, }, +/* pmc29 */ { PFM_REG_NOTIMPL, }, +/* pmc30 */ { PFM_REG_NOTIMPL, }, +/* pmc31 */ { PFM_REG_NOTIMPL, }, +/* pmc32 */ { PFM_REG_CONFIG, 0, 0x30f01ffffffffff, 0x30f01ffffffffff, NULL, pfm_mont_pmc_check, {0,0, 0, 0}, {0,0, 0, 0}}, +/* pmc33 */ { PFM_REG_CONFIG, 0, 0x0, 0x1ffffffffff, NULL, pfm_mont_pmc_check, {0,0, 0, 0}, {0,0, 0, 0}}, +/* pmc34 */ { PFM_REG_CONFIG, 0, 0xf01ffffffffff, 0xf01ffffffffff, NULL, pfm_mont_pmc_check, {0,0, 0, 0}, {0,0, 0, 0}}, +/* pmc35 */ { PFM_REG_CONFIG, 0, 0x0, 0x1ffffffffff, NULL, pfm_mont_pmc_check, {0,0, 0, 0}, {0,0, 0, 0}}, +/* pmc36 */ { PFM_REG_CONFIG, 0, 0xfffffff0, 0xf, NULL, pfm_mont_pmc_check, {0,0, 0, 0}, {0,0, 0, 0}}, +/* pmc37 */ { PFM_REG_MONITOR, 4, 0x0, 0x3fff, NULL, pfm_mont_pmc_check, {RDEP_MONT_IEAR, 0, 0, 0}, {0, 0, 0, 0}}, +/* pmc38 */ { PFM_REG_CONFIG, 0, 0xdb6, 0x2492, NULL, pfm_mont_pmc_check, {0,0, 0, 0}, {0,0, 0, 0}}, +/* pmc39 */ { PFM_REG_MONITOR, 6, 0x0, 0xffcf, NULL, pfm_mont_pmc_check, {RDEP_MONT_ETB,0, 0, 0}, {0,0, 0, 0}}, +/* pmc40 */ { PFM_REG_MONITOR, 6, 0x2000000, 0xf01cf, NULL, pfm_mont_pmc_check, {RDEP_MONT_DEAR,0, 0, 0}, {0,0, 0, 0}}, +/* pmc41 */ { PFM_REG_CONFIG, 0, 0x00002078fefefefe, 0x1e00018181818, NULL, pfm_mont_pmc_check, {0,0, 0, 0}, {0,0, 0, 0}}, +/* pmc42 */ { PFM_REG_MONITOR, 6, 0x0, 0x7ff4f, NULL, pfm_mont_pmc_check, {RDEP_MONT_ETB,0, 0, 0}, {0,0, 0, 0}}, + { PFM_REG_END , 0, 0x0, -1, NULL, NULL, {0,}, {0,}}, /* end marker */ +}; + +static pfm_reg_desc_t pfm_mont_pmd_desc[PMU_MAX_PMDS]={ +/* pmd0 */ { PFM_REG_NOTIMPL, }, +/* pmd1 */ { PFM_REG_NOTIMPL, }, +/* pmd2 */ { PFM_REG_NOTIMPL, }, +/* pmd3 */ { PFM_REG_NOTIMPL, }, +/* pmd4 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(4),0, 0, 0}}, +/* pmd5 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(5),0, 0, 0}}, +/* pmd6 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(6),0, 0, 0}}, +/* pmd7 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(7),0, 0, 0}}, +/* pmd8 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(8),0, 0, 0}}, +/* pmd9 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(9),0, 0, 0}}, +/* pmd10 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(10),0, 0, 0}}, +/* pmd11 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(11),0, 0, 0}}, +/* pmd12 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(12),0, 0, 0}}, +/* pmd13 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(13),0, 0, 0}}, +/* pmd14 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(14),0, 0, 0}}, +/* pmd15 */ { PFM_REG_COUNTING, 0, 0x0, -1, NULL, NULL, {0,0, 0, 0}, {RDEP(15),0, 0, 0}}, +/* pmd16 */ { PFM_REG_NOTIMPL, }, +/* pmd17 */ { PFM_REG_NOTIMPL, }, +/* pmd18 */ { PFM_REG_NOTIMPL, }, +/* pmd19 */ { PFM_REG_NOTIMPL, }, +/* pmd20 */ { PFM_REG_NOTIMPL, }, +/* pmd21 */ { PFM_REG_NOTIMPL, }, +/* pmd22 */ { PFM_REG_NOTIMPL, }, +/* pmd23 */ { PFM_REG_NOTIMPL, }, +/* pmd24 */ { PFM_REG_NOTIMPL, }, +/* pmd25 */ { PFM_REG_NOTIMPL, }, +/* pmd26 */ { PFM_REG_NOTIMPL, }, +/* pmd27 */ { PFM_REG_NOTIMPL, }, +/* pmd28 */ { PFM_REG_NOTIMPL, }, +/* pmd29 */ { PFM_REG_NOTIMPL, }, +/* pmd30 */ { PFM_REG_NOTIMPL, }, +/* pmd31 */ { PFM_REG_NOTIMPL, }, +/* pmd32 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP(33)|RDEP(36),0, 0, 0}, {RDEP(40),0, 0, 0}}, +/* pmd33 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP(32)|RDEP(36),0, 0, 0}, {RDEP(40),0, 0, 0}}, +/* pmd34 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP(35),0, 0, 0}, {RDEP(37),0, 0, 0}}, +/* pmd35 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP(34),0, 0, 0}, {RDEP(37),0, 0, 0}}, +/* pmd36 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP(32)|RDEP(33),0, 0, 0}, {RDEP(40),0, 0, 0}}, +/* pmd37 */ { PFM_REG_NOTIMPL, }, +/* pmd38 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd39 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd40 */ { PFM_REG_NOTIMPL, }, +/* pmd41 */ { PFM_REG_NOTIMPL, }, +/* pmd42 */ { PFM_REG_NOTIMPL, }, +/* pmd43 */ { PFM_REG_NOTIMPL, }, +/* pmd44 */ { PFM_REG_NOTIMPL, }, +/* pmd45 */ { PFM_REG_NOTIMPL, }, +/* pmd46 */ { PFM_REG_NOTIMPL, }, +/* pmd47 */ { PFM_REG_NOTIMPL, }, +/* pmd48 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd49 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd50 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd51 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd52 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd53 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd54 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd55 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd56 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd57 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd58 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd59 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd60 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd61 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd62 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, +/* pmd63 */ { PFM_REG_BUFFER, 0, 0x0, -1, NULL, NULL, {RDEP_MONT_ETB,0, 0, 0}, {RDEP(39),0, 0, 0}}, + { PFM_REG_END , 0, 0x0, -1, NULL, NULL, {0,}, {0,}}, /* end marker */ +}; + +/* + * PMC reserved fields must have their power-up values preserved + */ +static int +pfm_mont_reserved(unsigned int cnum, unsigned long *val, struct pt_regs *regs) +{ + unsigned long tmp1, tmp2, ival = *val; + + /* remove reserved areas from user value */ + tmp1 = ival & PMC_RSVD_MASK(cnum); + + /* get reserved fields values */ + tmp2 = PMC_DFL_VAL(cnum) & ~PMC_RSVD_MASK(cnum); + + *val = tmp1 | tmp2; + + DPRINT(("pmc[%d]=0x%lx, mask=0x%lx, reset=0x%lx, val=0x%lx\n", + cnum, ival, PMC_RSVD_MASK(cnum), PMC_DFL_VAL(cnum), *val)); + return 0; +} + +/* + * task can be NULL if the context is unloaded + */ +static int +pfm_mont_pmc_check(struct task_struct *task, pfm_context_t *ctx, unsigned int cnum, unsigned long *val, struct pt_regs *regs) +{ + int ret = 0; + unsigned long val32 = 0, val38 = 0, val41 = 0; + unsigned long tmpval; + int check_case1 = 0; + int is_loaded; + + /* first preserve the reserved fields */ + pfm_mont_reserved(cnum, val, regs); + + tmpval = *val; + + /* sanity check */ + if (ctx == NULL) return -EINVAL; + + is_loaded = ctx->ctx_state == PFM_CTX_LOADED || ctx->ctx_state == PFM_CTX_MASKED; + + /* + * we must clear the debug registers if pmc41 has a value which enable + * memory pipeline event constraints. In this case we need to clear the + * the debug registers if they have not yet been accessed. This is required + * to avoid picking stale state. + * PMC41 is "active" if: + * one of the pmc41.cfg_dtagXX field is different from 0x3 + * AND + * at the corresponding pmc41.en_dbrpXX is set. + * AND + * ctx_fl_using_dbreg == 0 (i.e., dbr not yet used) + */ + DPRINT(("cnum=%u val=0x%lx, using_dbreg=%d loaded=%d\n", cnum, tmpval, ctx->ctx_fl_using_dbreg, is_loaded)); + + if (cnum == 41 && is_loaded + && (tmpval & 0x1e00000000000) && (tmpval & 0x18181818UL) != 0x18181818UL && ctx->ctx_fl_using_dbreg == 0) { + + DPRINT(("pmc[%d]=0x%lx has active pmc41 settings, clearing dbr\n", cnum, tmpval)); + + /* don't mix debug with perfmon */ + if (task && (task->thread.flags & IA64_THREAD_DBG_VALID) != 0) return -EINVAL; + + /* + * a count of 0 will mark the debug registers if: + * AND + */ + ret = pfm_write_ibr_dbr(PFM_DATA_RR, ctx, NULL, 0, regs); + if (ret) return ret; + } + /* + * we must clear the (instruction) debug registers if: + * pmc38.ig_ibrpX is 0 (enabled) + * AND + * ctx_fl_using_dbreg == 0 (i.e., dbr not yet used) + */ + if (cnum == 38 && is_loaded && ((tmpval & 0x492UL) != 0x492UL) && ctx->ctx_fl_using_dbreg == 0) { + + DPRINT(("pmc38=0x%lx has active pmc38 settings, clearing ibr\n", tmpval)); + + /* don't mix debug with perfmon */ + if (task && (task->thread.flags & IA64_THREAD_DBG_VALID) != 0) return -EINVAL; + + /* + * a count of 0 will mark the debug registers as in use and also + * ensure that they are properly cleared. + */ + ret = pfm_write_ibr_dbr(PFM_CODE_RR, ctx, NULL, 0, regs); + if (ret) return ret; + + } + switch(cnum) { + case 32: val32 = *val; + val38 = ctx->ctx_pmcs[38]; + val41 = ctx->ctx_pmcs[41]; + check_case1 = 1; + break; + case 38: val38 = *val; + val32 = ctx->ctx_pmcs[32]; + val41 = ctx->ctx_pmcs[41]; + check_case1 = 1; + break; + case 41: val41 = *val; + val32 = ctx->ctx_pmcs[32]; + val38 = ctx->ctx_pmcs[38]; + check_case1 = 1; + break; + } + /* check illegal configuration which can produce inconsistencies in tagging + * i-side events in L1D and L2 caches + */ + if (check_case1) { + ret = (((val41 >> 45) & 0xf) == 0 && ((val32>>57) & 0x1) == 0) + && ((((val38>>1) & 0x3) == 0x2 || ((val38>>1) & 0x3) == 0) + || (((val38>>4) & 0x3) == 0x2 || ((val38>>4) & 0x3) == 0)); + if (ret) { + DPRINT(("invalid config pmc38=0x%lx pmc41=0x%lx pmc32=0x%lx\n", val38, val41, val32)); + return -EINVAL; + } + } + *val = tmpval; + return 0; +} + +/* + * impl_pmcs, impl_pmds are computed at runtime to minimize errors! + */ +static pmu_config_t pmu_conf_mont={ + .pmu_name = "Montecito", + .pmu_family = 0x20, + .flags = PFM_PMU_IRQ_RESEND, + .ovfl_val = (1UL << 47) - 1, + .pmd_desc = pfm_mont_pmd_desc, + .pmc_desc = pfm_mont_pmc_desc, + .num_ibrs = 8, + .num_dbrs = 8, + .use_rr_dbregs = 1 /* debug register are use for range retrictions */ +}; diff --git a/xen/arch/ia64/linux-xen/smpboot.c b/xen/arch/ia64/linux-xen/smpboot.c index 4450ce9260..6a48aaf1de 100644 --- a/xen/arch/ia64/linux-xen/smpboot.c +++ b/xen/arch/ia64/linux-xen/smpboot.c @@ -365,9 +365,7 @@ smp_callin (void) smp_setup_percpu_timer(); -#ifndef XEN ia64_mca_cmc_vector_setup(); /* Setup vector on AP */ -#endif #ifdef CONFIG_PERFMON pfm_init_percpu(); diff --git a/xen/arch/ia64/linux/Makefile b/xen/arch/ia64/linux/Makefile index 926fc501df..6acc02583d 100644 --- a/xen/arch/ia64/linux/Makefile +++ b/xen/arch/ia64/linux/Makefile @@ -22,6 +22,7 @@ obj-y += __divdi3.o obj-y += __udivdi3.o obj-y += __moddi3.o obj-y += __umoddi3.o +obj-y += carta_random.o ## variants of divide/modulo ## see files in xen/arch/ia64/linux/lib (linux/arch/ia64/lib) diff --git a/xen/arch/ia64/linux/README.origin b/xen/arch/ia64/linux/README.origin index 66ef299d7d..87da72ac8f 100644 --- a/xen/arch/ia64/linux/README.origin +++ b/xen/arch/ia64/linux/README.origin @@ -24,3 +24,6 @@ idiv64.S -> linux/arch/ia64/lib/idiv64.S memcpy_mck.S -> linux/arch/ia64/lib/memcpy_mck.S memset.S -> linux/arch/ia64/lib/memset.S strlen.S -> linux/arch/ia64/lib/strlen.S + +# The files below are from Linux-2.6.16.33 +carta_random.S -> linux/arch/ia64/lib/carta_random.S diff --git a/xen/arch/ia64/linux/carta_random.S b/xen/arch/ia64/linux/carta_random.S new file mode 100644 index 0000000000..d0674c3603 --- /dev/null +++ b/xen/arch/ia64/linux/carta_random.S @@ -0,0 +1,54 @@ +/* + * Fast, simple, yet decent quality random number generator based on + * a paper by David G. Carta ("Two Fast Implementations of the + * `Minimal Standard' Random Number Generator," Communications of the + * ACM, January, 1990). + * + * Copyright (C) 2002 Hewlett-Packard Co + * David Mosberger-Tang + */ + +#include + +#define a r2 +#define m r3 +#define lo r8 +#define hi r9 +#define t0 r16 +#define t1 r17 +#define seed r32 + +GLOBAL_ENTRY(carta_random32) + movl a = (16807 << 16) | 16807 + ;; + pmpyshr2.u t0 = a, seed, 0 + pmpyshr2.u t1 = a, seed, 16 + ;; + unpack2.l t0 = t1, t0 + dep m = -1, r0, 0, 31 + ;; + zxt4 lo = t0 + shr.u hi = t0, 32 + ;; + dep t0 = 0, hi, 15, 49 // t0 = (hi & 0x7fff) + ;; + shl t0 = t0, 16 // t0 = (hi & 0x7fff) << 16 + shr t1 = hi, 15 // t1 = (hi >> 15) + ;; + add lo = lo, t0 + ;; + cmp.gtu p6, p0 = lo, m + ;; +(p6) and lo = lo, m + ;; +(p6) add lo = 1, lo + ;; + add lo = lo, t1 + ;; + cmp.gtu p6, p0 = lo, m + ;; +(p6) and lo = lo, m + ;; +(p6) add lo = 1, lo + br.ret.sptk.many rp +END(carta_random32) diff --git a/xen/arch/ia64/vmx/Makefile b/xen/arch/ia64/vmx/Makefile index 5be2b4321c..9b84f6b6b0 100644 --- a/xen/arch/ia64/vmx/Makefile +++ b/xen/arch/ia64/vmx/Makefile @@ -1,4 +1,4 @@ -obj-y += hvm_vioapic.o +obj-y += viosapic.o #obj-y += mm.o obj-y += mmio.o obj-y += pal_emul.o diff --git a/xen/arch/ia64/vmx/mmio.c b/xen/arch/ia64/vmx/mmio.c index 1d6347ef9e..737220df8f 100644 --- a/xen/arch/ia64/vmx/mmio.c +++ b/xen/arch/ia64/vmx/mmio.c @@ -23,7 +23,6 @@ #include #include -#include #include #include #include @@ -36,21 +35,8 @@ #include #include #include -/* -struct mmio_list *lookup_mmio(u64 gpa, struct mmio_list *mio_base) -{ - int i; - for (i=0; mio_base[i].iot != NOT_IO; i++ ) { - if ( gpa >= mio_base[i].start && gpa <= mio_base[i].end ) - return &mio_base[i]; - } - return NULL; -} -*/ - -#define PIB_LOW_HALF(ofst) !(ofst&(1<<20)) -#define PIB_OFST_INTA 0x1E0000 -#define PIB_OFST_XTP 0x1E0008 +#include +#include #define HVM_BUFFERED_IO_RANGE_NR 1 @@ -116,87 +102,6 @@ int hvm_buffered_io_intercept(ioreq_t *p) return 1; } -static void write_ipi (VCPU *vcpu, uint64_t addr, uint64_t value); - -static void pib_write(VCPU *vcpu, void *src, uint64_t pib_off, size_t s, int ma) -{ - switch (pib_off) { - case PIB_OFST_INTA: - panic_domain(NULL,"Undefined write on PIB INTA\n"); - break; - case PIB_OFST_XTP: - if ( s == 1 && ma == 4 /* UC */) { - vmx_vcpu_get_plat(vcpu)->xtp = *(uint8_t *)src; - } - else { - panic_domain(NULL,"Undefined write on PIB XTP\n"); - } - break; - default: - if ( PIB_LOW_HALF(pib_off) ) { // lower half - if ( s != 8 || ma != 0x4 /* UC */ ) { - panic_domain - (NULL,"Undefined IPI-LHF write with s %ld, ma %d!\n", s, ma); - } - else { - write_ipi(vcpu, pib_off, *(uint64_t *)src); - // TODO for SM-VP - } - } - else { // upper half - printk("IPI-UHF write %lx\n",pib_off); - panic_domain(NULL,"Not support yet for SM-VP\n"); - } - break; - } -} - -static void pib_read(VCPU *vcpu, uint64_t pib_off, void *dest, size_t s, int ma) -{ - switch (pib_off) { - case PIB_OFST_INTA: - // todo --- emit on processor system bus. - if ( s == 1 && ma == 4) { // 1 byte load - // TODO: INTA read from IOSAPIC - } - else { - panic_domain(NULL,"Undefined read on PIB INTA\n"); - } - break; - case PIB_OFST_XTP: - if ( s == 1 && ma == 4) { - *((uint8_t*)dest) = vmx_vcpu_get_plat(vcpu)->xtp; - } - else { - panic_domain(NULL,"Undefined read on PIB XTP\n"); - } - break; - default: - if ( PIB_LOW_HALF(pib_off) ) { // lower half - if ( s != 8 || ma != 4 ) { - panic_domain(NULL,"Undefined IPI-LHF read!\n"); - } - else { -#ifdef IPI_DEBUG - printk("IPI-LHF read %lx\n",pib_off); -#endif - *(uint64_t *)dest = 0; // TODO for SM-VP - } - } - else { // upper half - if ( s != 1 || ma != 4 ) { - panic_domain(NULL,"Undefined PIB-UHF read!\n"); - } - else { -#ifdef IPI_DEBUG - printk("IPI-UHF read %lx\n",pib_off); -#endif - *(uint8_t *)dest = 0; // TODO for SM-VP - } - } - break; - } -} static void low_mmio_access(VCPU *vcpu, u64 pa, u64 *val, size_t s, int dir) { @@ -271,31 +176,32 @@ static void legacy_io_access(VCPU *vcpu, u64 pa, u64 *val, size_t s, int dir) return; } -extern struct vmx_mmio_handler vioapic_mmio_handler; static void mmio_access(VCPU *vcpu, u64 src_pa, u64 *dest, size_t s, int ma, int dir) { struct virtual_platform_def *v_plat; //mmio_type_t iot; unsigned long iot; - struct vmx_mmio_handler *vioapic_handler = &vioapic_mmio_handler; iot=__gpfn_is_io(vcpu->domain, src_pa>>PAGE_SHIFT); v_plat = vmx_vcpu_get_plat(vcpu); perfc_incra(vmx_mmio_access, iot >> 56); switch (iot) { - case GPFN_PIB: - if(!dir) - pib_write(vcpu, dest, src_pa - v_plat->pib_base, s, ma); + case GPFN_PIB: + if (ma != 4) + panic_domain(NULL, "Access PIB not with UC attribute\n"); + + if (!dir) + vlsapic_write(vcpu, src_pa, s, *dest); else - pib_read(vcpu, src_pa - v_plat->pib_base, dest, s, ma); + *dest = vlsapic_read(vcpu, src_pa, s); break; case GPFN_GFW: break; case GPFN_IOSAPIC: if (!dir) - vioapic_handler->write_handler(vcpu, src_pa, s, *dest); + viosapic_write(vcpu, src_pa, s, *dest); else - *dest = vioapic_handler->read_handler(vcpu, src_pa, s); + *dest = viosapic_read(vcpu, src_pa, s); break; case GPFN_FRAME_BUFFER: case GPFN_LOW_MMIO: @@ -311,195 +217,6 @@ static void mmio_access(VCPU *vcpu, u64 src_pa, u64 *dest, size_t s, int ma, int return; } -/* - * Read or write data in guest virtual address mode. - */ -/* -void -memwrite_v(VCPU *vcpu, thash_data_t *vtlb, u64 *src, u64 *dest, size_t s) -{ - uint64_t pa; - - if (!vtlb->nomap) - panic("Normal memory write shouldn't go to this point!"); - pa = PPN_2_PA(vtlb->ppn); - pa += POFFSET((u64)dest, vtlb->ps); - mmio_write (vcpu, src, pa, s, vtlb->ma); -} - - -void -memwrite_p(VCPU *vcpu, u64 *src, u64 *dest, size_t s) -{ - uint64_t pa = (uint64_t)dest; - int ma; - - if ( pa & (1UL <<63) ) { - // UC - ma = 4; - pa <<=1; - pa >>=1; - } - else { - // WBL - ma = 0; // using WB for WBL - } - mmio_write (vcpu, src, pa, s, ma); -} - -void -memread_v(VCPU *vcpu, thash_data_t *vtlb, u64 *src, u64 *dest, size_t s) -{ - uint64_t pa; - - if (!vtlb->nomap) - panic_domain(NULL,"Normal memory write shouldn't go to this point!"); - pa = PPN_2_PA(vtlb->ppn); - pa += POFFSET((u64)src, vtlb->ps); - - mmio_read(vcpu, pa, dest, s, vtlb->ma); -} - -void -memread_p(VCPU *vcpu, u64 *src, u64 *dest, size_t s) -{ - uint64_t pa = (uint64_t)src; - int ma; - - if ( pa & (1UL <<63) ) { - // UC - ma = 4; - pa <<=1; - pa >>=1; - } - else { - // WBL - ma = 0; // using WB for WBL - } - mmio_read(vcpu, pa, dest, s, ma); -} -*/ - -/* - * To inject INIT to guest, we must set the PAL_INIT entry - * and set psr to switch to physical mode - */ -#define PAL_INIT_ENTRY 0x80000000ffffffa0 -#define PSR_SET_BITS (IA64_PSR_DT | IA64_PSR_IT | IA64_PSR_RT | \ - IA64_PSR_IC | IA64_PSR_RI) - -static void vmx_inject_guest_pal_init(VCPU *vcpu) -{ - REGS *regs = vcpu_regs(vcpu); - uint64_t psr = vmx_vcpu_get_psr(vcpu); - - regs->cr_iip = PAL_INIT_ENTRY; - - psr = psr & (~PSR_SET_BITS); - vmx_vcpu_set_psr(vcpu,psr); -} - -/* - * Deliver IPI message. (Only U-VP is supported now) - * offset: address offset to IPI space. - * value: deliver value. - */ -static void deliver_ipi (VCPU *vcpu, uint64_t dm, uint64_t vector) -{ -#ifdef IPI_DEBUG - printk ("deliver_ipi %lx %lx\n",dm,vector); -#endif - switch ( dm ) { - case 0: // INT - vmx_vcpu_pend_interrupt (vcpu, vector); - break; - case 2: // PMI - // TODO -- inject guest PMI - panic_domain (NULL, "Inject guest PMI!\n"); - break; - case 4: // NMI - vmx_vcpu_pend_interrupt (vcpu, 2); - break; - case 5: // INIT - vmx_inject_guest_pal_init(vcpu); - break; - case 7: // ExtINT - vmx_vcpu_pend_interrupt (vcpu, 0); - break; - case 1: - case 3: - case 6: - default: - panic_domain (NULL, "Deliver reserved IPI!\n"); - break; - } -} - -/* - * TODO: Use hash table for the lookup. - */ -static inline VCPU *lid_2_vcpu (struct domain *d, u64 id, u64 eid) -{ - int i; - VCPU *vcpu; - LID lid; - for (i=0; ivcpu[i]; - if (!vcpu) - continue; - lid.val = VCPU_LID(vcpu); - if ( lid.id == id && lid.eid == eid ) - return vcpu; - } - return NULL; -} - -/* - * execute write IPI op. - */ -static void write_ipi (VCPU *vcpu, uint64_t addr, uint64_t value) -{ - VCPU *targ; - struct domain *d=vcpu->domain; - targ = lid_2_vcpu(vcpu->domain, - ((ipi_a_t)addr).id, ((ipi_a_t)addr).eid); - if ( targ == NULL ) panic_domain (NULL,"Unknown IPI cpu\n"); - - if (!test_bit(_VCPUF_initialised, &targ->vcpu_flags)) { - struct pt_regs *targ_regs = vcpu_regs (targ); - struct vcpu_guest_context c; - - memset (&c, 0, sizeof (c)); - - if (arch_set_info_guest (targ, &c) != 0) { - printk ("arch_boot_vcpu: failure\n"); - return; - } - /* First or next rendez-vous: set registers. */ - vcpu_init_regs (targ); - targ_regs->cr_iip = d->arch.sal_data->boot_rdv_ip; - targ_regs->r1 = d->arch.sal_data->boot_rdv_r1; - - if (test_and_clear_bit(_VCPUF_down,&targ->vcpu_flags)) { - vcpu_wake(targ); - printk ("arch_boot_vcpu: vcpu %d awaken %016lx!\n", - targ->vcpu_id, targ_regs->cr_iip); - } - else - printk ("arch_boot_vcpu: huu, already awaken!"); - } - else { - int running = test_bit(_VCPUF_running,&targ->vcpu_flags); - deliver_ipi (targ, ((ipi_d_t)value).dm, - ((ipi_d_t)value).vector); - vcpu_unblock(targ); - if (running) - smp_send_event_check_cpu(targ->processor); - } - return; -} - - /* dir 1: read 0:write inst_type 0:integer 1:floating point diff --git a/xen/arch/ia64/vmx/optvfault.S b/xen/arch/ia64/vmx/optvfault.S index 4a268f9a5d..cadf461752 100644 --- a/xen/arch/ia64/vmx/optvfault.S +++ b/xen/arch/ia64/vmx/optvfault.S @@ -209,7 +209,10 @@ GLOBAL_ENTRY(vmx_asm_rsm) dep.z r28=4,61,3 ;; mov rr[r0]=r26 + ;; mov rr[r28]=r27 + ;; + srlz.d br.many vmx_resume_to_guest END(vmx_asm_rsm) @@ -260,9 +263,10 @@ GLOBAL_ENTRY(vmx_asm_ssm) dep.z r28=4,61,3 ;; mov rr[r0]=r26 + ;; mov rr[r28]=r27 ;; - srlz.i + srlz.d ;; vmx_asm_ssm_1: tbit.nz p6,p0=r29,IA64_PSR_I_BIT @@ -341,9 +345,10 @@ vmx_asm_mov_to_psr_back: dep.z r28=4,61,3 ;; mov rr[r0]=r26 + ;; mov rr[r28]=r27 ;; - srlz.i + srlz.d ;; vmx_asm_mov_to_psr_1: mov r20=cr.ipsr diff --git a/xen/arch/ia64/vmx/viosapic.c b/xen/arch/ia64/vmx/viosapic.c new file mode 100644 index 0000000000..f52a1f6590 --- /dev/null +++ b/xen/arch/ia64/vmx/viosapic.c @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2001 MandrakeSoft S.A. + * + * MandrakeSoft S.A. + * 43, rue d'Aboukir + * 75002 Paris - France + * http://www.linux-mandrake.com/ + * http://www.mandrakesoft.com/ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Yunhong Jiang + * Ported to xen by using virtual IRQ line. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void viosapic_deliver(struct viosapic *viosapic, int irq) +{ + uint16_t dest = viosapic->redirtbl[irq].dest_id; + uint8_t delivery_mode = viosapic->redirtbl[irq].delivery_mode; + uint8_t vector = viosapic->redirtbl[irq].vector; + struct vcpu *v; + + switch ( delivery_mode ) + { + case SAPIC_FIXED: + { + v = vlsapic_lid_to_vcpu(viosapic_domain(viosapic), dest); + vlsapic_set_irq(v, vector); + vcpu_kick(v); + break; + } + case SAPIC_LOWEST_PRIORITY: + { + v = vlsapic_lid_to_vcpu(viosapic_domain(viosapic), dest); + if (viosapic->lowest_vcpu) + v = viosapic->lowest_vcpu; + vlsapic_set_irq(v, vector); + vcpu_kick(v); + break; + } + case SAPIC_PMI: + case SAPIC_NMI: + case SAPIC_INIT: + case SAPIC_EXTINT: + default: + gdprintk(XENLOG_WARNING, "Unsupported delivery mode %d\n", + delivery_mode); + break; + } +} + + +static int iosapic_get_highest_irq(struct viosapic *viosapic) +{ + uint64_t irqs = viosapic->irr & ~viosapic->isr ; + + if (irqs) + return ia64_fls(irqs); + + return -1; +} + + +/* XXX If level interrupt, use vector->irq table for performance */ +static int get_redir_num(struct viosapic *viosapic, int vector) +{ + int i; + + for ( i = 0; i < VIOSAPIC_NUM_PINS; i++ ) + if ( viosapic->redirtbl[i].vector == vector ) + return i; + + return -1; +} + + +static void service_iosapic(struct viosapic *viosapic) +{ + int irq; + + while ( (irq = iosapic_get_highest_irq(viosapic)) != -1 ) + { + viosapic_deliver(viosapic, irq); + + if ( viosapic->redirtbl[irq].trig_mode == SAPIC_LEVEL ) + viosapic->isr |= (1UL << irq); + + viosapic->irr &= ~(1UL << irq); + } +} + + +static void viosapic_update_EOI(struct viosapic *viosapic, int vector) +{ + int redir_num; + + if ( (redir_num = get_redir_num(viosapic, vector)) == -1 ) + { + gdprintk(XENLOG_WARNING, "Can't find redir item for %d EOI\n", vector); + return; + } + + if ( !test_and_clear_bit(redir_num, &viosapic->isr) ) + { + gdprintk(XENLOG_WARNING, "redir %d not set for %d EOI\n", + redir_num, vector); + return; + } + service_iosapic(viosapic); +} + + +static unsigned long viosapic_read_indirect(struct viosapic *viosapic, + unsigned long addr, + unsigned long length) +{ + unsigned long result = 0; + + switch ( viosapic->ioregsel ) + { + case VIOSAPIC_VERSION: + result = ((((VIOSAPIC_NUM_PINS - 1) & 0xff) << 16) + | (VIOSAPIC_VERSION_ID & 0xff)); + break; + + default: + { + uint32_t redir_index = (viosapic->ioregsel - 0x10) >> 1; + uint64_t redir_content; + + if ( redir_index >= VIOSAPIC_NUM_PINS ) + { + gdprintk(XENLOG_WARNING, "viosapic_read_indirect:undefined " + "ioregsel %x\n", viosapic->ioregsel); + break; + } + + redir_content = viosapic->redirtbl[redir_index].bits; + result = (viosapic->ioregsel & 0x1) ? + (redir_content >> 32) & 0xffffffff : + redir_content & 0xffffffff; + break; + } + } + + return result; +} + + +unsigned long viosapic_read(struct vcpu *v, + unsigned long addr, + unsigned long length) +{ + struct viosapic *viosapic = vcpu_viosapic(v); + uint32_t result; + + addr &= 0xff; + + switch ( addr ) + { + case VIOSAPIC_REG_SELECT: + result = viosapic->ioregsel; + break; + + case VIOSAPIC_WINDOW: + result = viosapic_read_indirect(viosapic, addr, length); + break; + + default: + result = 0; + break; + } + + return result; +} + + +static void viosapic_write_indirect(struct viosapic *viosapic, + unsigned long addr, + unsigned long length, + unsigned long val) +{ + switch ( viosapic->ioregsel ) + { + case VIOSAPIC_VERSION: + /* Writes are ignored. */ + break; + + default: + { + uint32_t redir_index = (viosapic->ioregsel - 0x10) >> 1; + uint64_t redir_content; + + if ( redir_index >= VIOSAPIC_NUM_PINS ) + { + gdprintk(XENLOG_WARNING, "viosapic_write_indirect " + "error register %x\n", viosapic->ioregsel); + break; + } + + redir_content = viosapic->redirtbl[redir_index].bits; + + if ( viosapic->ioregsel & 0x1 ) + { + redir_content = (((uint64_t)val & 0xffffffff) << 32) | + (redir_content & 0xffffffff); + } + else + { + redir_content = ((redir_content >> 32) << 32) | + (val & 0xffffffff); + } + viosapic->redirtbl[redir_index].bits = redir_content; + break; + } + } /* switch */ +} + + +void viosapic_write(struct vcpu *v, + unsigned long addr, + unsigned long length, + unsigned long val) +{ + struct viosapic *viosapic = vcpu_viosapic(v); + + addr &= 0xff; + + switch ( addr ) + { + case VIOSAPIC_REG_SELECT: + viosapic->ioregsel = val; + break; + + case VIOSAPIC_WINDOW: + viosapic_write_indirect(viosapic, addr, length, val); + break; + + case VIOSAPIC_EOI: + viosapic_update_EOI(viosapic, val); + break; + + default: + break; + } +} + + +static void viosapic_reset(struct viosapic *viosapic) +{ + int i; + + memset(viosapic, 0, sizeof(*viosapic)); + + for ( i = 0; i < VIOSAPIC_NUM_PINS; i++ ) + { + viosapic->redirtbl[i].mask = 0x1; + } + spin_lock_init(&viosapic->lock); +} + +void viosapic_set_irq(struct domain *d, int irq, int level) +{ + struct viosapic *viosapic = domain_viosapic(d); + uint64_t bit; + + spin_lock(&viosapic->lock); + if ( (irq < 0) || (irq >= VIOSAPIC_NUM_PINS) ) + goto out; + + if ( viosapic->redirtbl[irq].mask ) + goto out; + + bit = 1UL << irq; + if ( viosapic->redirtbl[irq].trig_mode == SAPIC_LEVEL ) + { + if ( level ) + viosapic->irr |= bit; + else + viosapic->irr &= ~bit; + } + else + { + if ( level ) + /* XXX No irr clear for edge interrupt */ + viosapic->irr |= bit; + } + + service_iosapic(viosapic); +out: + spin_unlock(&viosapic->lock); +} + +#define hvm_pci_intx_gsi(dev, intx) \ + (((((dev) << 2) + ((dev) >> 3) + (intx)) & 31) + 16) + + +void viosapic_set_pci_irq(struct domain *d, int device, int intx, int level) +{ + int irq; + irq = hvm_pci_intx_gsi(device, intx); + + viosapic_set_irq(d, irq, level); +} + +void viosapic_init(struct domain *d) +{ + struct viosapic *viosapic = domain_viosapic(d); + + viosapic_reset(viosapic); + + viosapic->lowest_vcpu = NULL; + + viosapic->base_address = VIOSAPIC_DEFAULT_BASE_ADDRESS; +} diff --git a/xen/arch/ia64/vmx/vlsapic.c b/xen/arch/ia64/vmx/vlsapic.c index 3a6d85a36a..7a495ee547 100644 --- a/xen/arch/ia64/vmx/vlsapic.c +++ b/xen/arch/ia64/vmx/vlsapic.c @@ -25,11 +25,9 @@ #include #include #include -#include #include #include #include -#include #include #include #include @@ -38,8 +36,16 @@ #include #include #include -#include +#include +#include #include +#include + +#ifdef IPI_DEBUG +#define IPI_DPRINTK(x...) printk(x) +#else +#define IPI_DPRINTK(x...) +#endif //u64 fire_itc; //u64 fire_itc2; @@ -103,8 +109,10 @@ static int vmx_vcpu_unpend_interrupt(VCPU *vcpu, uint8_t vector) ret = test_and_clear_bit(vector, &VCPU(vcpu, irr[0])); local_irq_restore(spsr); - if (ret) + if (ret) { vcpu->arch.irq_new_pending = 1; + wmb(); + } return ret; } @@ -114,16 +122,13 @@ static int vmx_vcpu_unpend_interrupt(VCPU *vcpu, uint8_t vector) */ static uint64_t now_itc(vtime_t *vtm) { - uint64_t guest_itc=vtm->vtm_offset+ia64_get_itc(); - - if ( vtm->vtm_local_drift ) { -// guest_itc -= vtm->vtm_local_drift; - } - if (guest_itc >= vtm->last_itc) - return guest_itc; - else - /* guest ITC backwarded due after LP switch */ - return vtm->last_itc; + uint64_t guest_itc = vtm->vtm_offset + ia64_get_itc(); + + if (guest_itc >= vtm->last_itc) + return guest_itc; + else + /* guest ITC went backward due to LP switch */ + return vtm->last_itc; } /* @@ -173,7 +178,7 @@ void vtm_init(VCPU *vcpu) { vtime_t *vtm; uint64_t itc_freq; - + vtm = &VMX(vcpu, vtm); itc_freq = local_cpu_data->itc_freq; @@ -261,99 +266,6 @@ void vtm_set_itv(VCPU *vcpu, uint64_t val) } -/* - * Update interrupt or hook the vtm timer for fire - * At this point vtm_timer should be removed if itv is masked. - */ -/* Interrupt must be disabled at this point */ -/* -void vtm_interruption_update(VCPU *vcpu, vtime_t* vtm) -{ - uint64_t cur_itc,vitm,vitv; - uint64_t expires; - long diff_now, diff_last; - uint64_t spsr; - - vitv = VCPU(vcpu, itv); - if ( ITV_IRQ_MASK(vitv) ) { - return; - } - - vitm =VCPU(vcpu, itm); - local_irq_save(spsr); - cur_itc =now_itc(vtm); - diff_last = vtm->last_itc - vitm; - diff_now = cur_itc - vitm; - - if ( diff_last >= 0 ) { - // interrupt already fired. - stop_timer(&vtm->vtm_timer); - } - else if ( diff_now >= 0 ) { - // ITV is fired. - vmx_vcpu_pend_interrupt(vcpu, vitv&0xff); - } -*/ - /* Both last_itc & cur_itc < itm, wait for fire condition */ -/* else { - expires = NOW() + cycle_to_ns(0-diff_now) + TIMER_SLOP; - set_timer(&vtm->vtm_timer, expires); - } - local_irq_restore(spsr); -} - */ - -/* - * Action for vtm when the domain is scheduled out. - * Remove the timer for vtm. - */ -/* -void vtm_domain_out(VCPU *vcpu) -{ - if(!is_idle_domain(vcpu->domain)) - stop_timer(&vcpu->arch.arch_vmx.vtm.vtm_timer); -} - */ -/* - * Action for vtm when the domain is scheduled in. - * Fire vtm IRQ or add the timer for vtm. - */ -/* -void vtm_domain_in(VCPU *vcpu) -{ - vtime_t *vtm; - - if(!is_idle_domain(vcpu->domain)) { - vtm=&(vcpu->arch.arch_vmx.vtm); - vtm_interruption_update(vcpu, vtm); - } -} - */ - -#ifdef V_IOSAPIC_READY -int vlapic_match_logical_addr(struct vlapic *vlapic, uint16_t dest) -{ - return (VLAPIC_ID(vlapic) == dest); -} - -struct vlapic* apic_round_robin(struct domain *d, - uint8_t vector, - uint32_t bitmap) -{ - uint8_t bit = 0; - - if (!bitmap) { - printk(" no bit on bitmap\n"); - return NULL; - } - - while (!(bitmap & (1 << bit))) - bit++; - - return vcpu_vlapic(d->vcpu[bit]); -} -#endif - void vlsapic_reset(VCPU *vcpu) { int i; @@ -371,13 +283,11 @@ void vlsapic_reset(VCPU *vcpu) VCPU(vcpu, lrr0) = 0x10000; // default reset value? VCPU(vcpu, lrr1) = 0x10000; // default reset value? update_vhpi(vcpu, NULL_VECTOR); + VLSAPIC_XTP(vcpu) = 0x80; // disabled for ( i=0; i<4; i++) { VLSAPIC_INSVC(vcpu,i) = 0; } -#ifdef V_IOSAPIC_READY - vcpu->arch.arch_vmx.vlapic.vcpu = vcpu; -#endif dprintk(XENLOG_INFO, "VLSAPIC inservice base=%p\n", &VLSAPIC_INSVC(vcpu,0) ); } @@ -391,7 +301,7 @@ static __inline__ int highest_bits(uint64_t *dat) { uint64_t bits, bitnum; int i; - + /* loop for all 256 bits */ for ( i=3; i >= 0 ; i -- ) { bits = dat[i]; @@ -435,12 +345,6 @@ static int is_higher_class(int pending, int mic) { return ( (pending >> 4) > mic ); } -#if 0 -static int is_invalid_irq(int vec) -{ - return (vec == 1 || ((vec <= 14 && vec >= 3))); -} -#endif //shadow it due to no use currently #define IRQ_NO_MASKED 0 #define IRQ_MASKED_BY_VTPR 1 @@ -451,7 +355,7 @@ static int _xirq_masked(VCPU *vcpu, int h_pending, int h_inservice) { tpr_t vtpr; - + vtpr.val = VCPU(vcpu, tpr); if ( h_inservice == NMI_VECTOR ) { @@ -491,7 +395,7 @@ _xirq_masked(VCPU *vcpu, int h_pending, int h_inservice) static int irq_masked(VCPU *vcpu, int h_pending, int h_inservice) { int mask; - + mask = _xirq_masked(vcpu, h_pending, h_inservice); return mask; } @@ -514,8 +418,10 @@ int vmx_vcpu_pend_interrupt(VCPU *vcpu, uint8_t vector) ret = test_and_set_bit(vector, &VCPU(vcpu, irr[0])); local_irq_restore(spsr); - if (!ret) + if (!ret) { vcpu->arch.irq_new_pending = 1; + wmb(); + } return ret; } @@ -537,6 +443,7 @@ void vmx_vcpu_pend_batch_interrupt(VCPU *vcpu, u64 *pend_irr) } local_irq_restore(spsr); vcpu->arch.irq_new_pending = 1; + wmb(); } /* @@ -598,6 +505,7 @@ void guest_write_eoi(VCPU *vcpu) VLSAPIC_INSVC(vcpu,vec>>6) &= ~(1UL <<(vec&63)); VCPU(vcpu, eoi)=0; // overwrite the data vcpu->arch.irq_new_pending=1; + wmb(); } int is_unmasked_irq(VCPU *vcpu) @@ -667,24 +575,221 @@ void vmx_vexirq(VCPU *vcpu) generate_exirq (vcpu); } - -void vmx_vioapic_set_irq(struct domain *d, int irq, int level) +struct vcpu * vlsapic_lid_to_vcpu(struct domain *d, uint16_t dest) { - unsigned long flags; - - spin_lock_irqsave(&d->arch.arch_vmx.virq_assist_lock, flags); - vioapic_set_irq(d, irq, level); - spin_unlock_irqrestore(&d->arch.arch_vmx.virq_assist_lock, flags); + struct vcpu * v; + for_each_vcpu ( d, v ) { + if ( (v->arch.privregs->lid >> 16) == dest ) + return v; + } + return NULL; } -int vmx_vlapic_set_irq(VCPU *v, uint8_t vec, uint8_t trig) -{ - int ret; - int running = test_bit(_VCPUF_running, &v->vcpu_flags); - ret = vmx_vcpu_pend_interrupt(v, vec); - vcpu_unblock(v); - if (running) - smp_send_event_check_cpu(v->processor); - return ret; +/* + * To inject INIT to guest, we must set the PAL_INIT entry + * and set psr to switch to physical mode + */ +#define PAL_INIT_ENTRY 0x80000000ffffffa0 +#define PSR_SET_BITS (IA64_PSR_DT | IA64_PSR_IT | IA64_PSR_RT | \ + IA64_PSR_IC | IA64_PSR_RI) + +static void vmx_inject_guest_pal_init(VCPU *vcpu) +{ + REGS *regs = vcpu_regs(vcpu); + uint64_t psr = vmx_vcpu_get_psr(vcpu); + + regs->cr_iip = PAL_INIT_ENTRY; + + psr = psr & ~PSR_SET_BITS; + vmx_vcpu_set_psr(vcpu, psr); } + + +/* + * Deliver IPI message. (Only U-VP is supported now) + * offset: address offset to IPI space. + * value: deliver value. + */ +static void vlsapic_deliver_ipi(VCPU *vcpu, uint64_t dm, uint64_t vector) +{ + IPI_DPRINTK("deliver_ipi %lx %lx\n", dm, vector); + + switch (dm) { + case SAPIC_FIXED: // INT + vmx_vcpu_pend_interrupt(vcpu, vector); + break; + case SAPIC_PMI: + // TODO -- inject guest PMI + panic_domain(NULL, "Inject guest PMI!\n"); + break; + case SAPIC_NMI: + vmx_vcpu_pend_interrupt(vcpu, 2); + break; + case SAPIC_INIT: + vmx_inject_guest_pal_init(vcpu); + break; + case SAPIC_EXTINT: // ExtINT + vmx_vcpu_pend_interrupt(vcpu, 0); + break; + default: + panic_domain(NULL, "Deliver reserved IPI!\n"); + break; + } +} + +/* + * TODO: Use hash table for the lookup. + */ +static inline VCPU *lid_to_vcpu(struct domain *d, uint8_t id, uint8_t eid) +{ + VCPU *v; + LID lid; + + for_each_vcpu(d, v) { + lid.val = VCPU_LID(v); + if (lid.id == id && lid.eid == eid) + return v; + } + return NULL; +} + + +/* + * execute write IPI op. + */ +static void vlsapic_write_ipi(VCPU *vcpu, uint64_t addr, uint64_t value) +{ + VCPU *targ; + struct domain *d = vcpu->domain; + + targ = lid_to_vcpu(vcpu->domain, ((ipi_a_t)addr).id, ((ipi_a_t)addr).eid); + if (targ == NULL) + panic_domain(NULL, "Unknown IPI cpu\n"); + + if (!test_bit(_VCPUF_initialised, &targ->vcpu_flags)) { + struct pt_regs *targ_regs = vcpu_regs(targ); + struct vcpu_guest_context c; + + memset (&c, 0, sizeof(c)); + + if (arch_set_info_guest(targ, &c) != 0) { + printk("arch_boot_vcpu: failure\n"); + return; + } + /* First or next rendez-vous: set registers. */ + vcpu_init_regs(targ); + targ_regs->cr_iip = d->arch.sal_data->boot_rdv_ip; + targ_regs->r1 = d->arch.sal_data->boot_rdv_r1; + + if (test_and_clear_bit(_VCPUF_down,&targ->vcpu_flags)) { + vcpu_wake(targ); + printk("arch_boot_vcpu: vcpu %d awaken %016lx!\n", + targ->vcpu_id, targ_regs->cr_iip); + } else { + printk("arch_boot_vcpu: huh, already awake!"); + } + } else { + int running = test_bit(_VCPUF_running, &targ->vcpu_flags); + vlsapic_deliver_ipi(targ, ((ipi_d_t)value).dm, + ((ipi_d_t)value).vector); + vcpu_unblock(targ); + if (running) + smp_send_event_check_cpu(targ->processor); + } + return; +} + + +unsigned long vlsapic_read(struct vcpu *v, + unsigned long addr, + unsigned long length) +{ + uint64_t result = 0; + + addr &= (PIB_SIZE - 1); + + switch (addr) { + case PIB_OFST_INTA: + if (length == 1) // 1 byte load + ; // There is no i8259, there is no INTA access + else + panic_domain(NULL,"Undefined read on PIB INTA\n"); + + break; + case PIB_OFST_XTP: + if (length == 1) { + result = VLSAPIC_XTP(v); + // printk("read xtp %lx\n", result); + } else { + panic_domain(NULL, "Undefined read on PIB XTP\n"); + } + break; + default: + if (PIB_LOW_HALF(addr)) { // lower half + if (length != 8 ) + panic_domain(NULL, "Undefined IPI-LHF read!\n"); + else + IPI_DPRINTK("IPI-LHF read %lx\n", pib_off); + } else { // upper half + IPI_DPRINTK("IPI-UHF read %lx\n", addr); + } + break; + } + return result; +} + +static void vlsapic_write_xtp(struct vcpu *v, uint8_t val) +{ + struct viosapic * viosapic; + struct vcpu *lvcpu, *vcpu; + viosapic = vcpu_viosapic(v); + lvcpu = viosapic->lowest_vcpu; + VLSAPIC_XTP(v) = val; + + for_each_vcpu(v->domain, vcpu) { + if (VLSAPIC_XTP(lvcpu) > VLSAPIC_XTP(vcpu)) + lvcpu = vcpu; + } + + if (VLSAPIC_XTP(lvcpu) & 0x80) // Disabled + lvcpu = NULL; + + viosapic->lowest_vcpu = lvcpu; +} + +void vlsapic_write(struct vcpu *v, + unsigned long addr, + unsigned long length, + unsigned long val) +{ + addr &= (PIB_SIZE - 1); + + switch (addr) { + case PIB_OFST_INTA: + panic_domain(NULL, "Undefined write on PIB INTA\n"); + break; + case PIB_OFST_XTP: + if (length == 1) { + // printk("write xtp %lx\n", val); + vlsapic_write_xtp(v, val); + } else { + panic_domain(NULL, "Undefined write on PIB XTP\n"); + } + break; + default: + if (PIB_LOW_HALF(addr)) { // lower half + if (length != 8) + panic_domain(NULL, "Undefined IPI-LHF write with size %ld!\n", + length); + else + vlsapic_write_ipi(v, addr, val); + } + else { // upper half + // printk("IPI-UHF write %lx\n",addr); + panic_domain(NULL, "No support for SM-VP yet\n"); + } + break; + } +} + diff --git a/xen/arch/ia64/vmx/vmx_hypercall.c b/xen/arch/ia64/vmx/vmx_hypercall.c index dcb571e042..08726390f4 100644 --- a/xen/arch/ia64/vmx/vmx_hypercall.c +++ b/xen/arch/ia64/vmx/vmx_hypercall.c @@ -35,6 +35,73 @@ #include #include #include +#include + +static int hvmop_set_isa_irq_level( + XEN_GUEST_HANDLE(xen_hvm_set_isa_irq_level_t) uop) +{ + struct xen_hvm_set_isa_irq_level op; + struct domain *d; + int rc; + + if ( copy_from_guest(&op, uop, 1) ) + return -EFAULT; + + if ( !IS_PRIV(current->domain) ) + return -EPERM; + + if ( op.isa_irq > 15 ) + return -EINVAL; + + d = find_domain_by_id(op.domid); + if ( d == NULL ) + return -ESRCH; + + rc = -EINVAL; + if ( !is_hvm_domain(d) ) + goto out; + + rc = 0; + viosapic_set_irq(d, op.isa_irq, op.level); + + out: + put_domain(d); + return rc; +} + +static int hvmop_set_pci_intx_level( + XEN_GUEST_HANDLE(xen_hvm_set_pci_intx_level_t) uop) +{ + struct xen_hvm_set_pci_intx_level op; + struct domain *d; + int rc; + + if ( copy_from_guest(&op, uop, 1) ) + return -EFAULT; + + if ( !IS_PRIV(current->domain) ) + return -EPERM; + + if ( (op.domain > 0) || (op.bus > 0) || (op.device > 31) || (op.intx > 3) ) + return -EINVAL; + + d = find_domain_by_id(op.domid); + if ( d == NULL ) + return -ESRCH; + + rc = -EINVAL; + if ( !is_hvm_domain(d) ) + goto out; + + rc = 0; + viosapic_set_pci_irq(d, op.device, op.intx, op.level); + + out: + put_domain(d); + return rc; +} + + long do_hvm_op(unsigned long op, XEN_GUEST_HANDLE(void) arg) @@ -79,30 +146,19 @@ do_hvm_op(unsigned long op, XEN_GUEST_HANDLE(void) arg) break; } - case HVMOP_set_irq_level: - { - struct xen_hvm_set_irq_level op; - struct domain *d; - - if (copy_from_guest(&op, arg, 1)) - return -EFAULT; - - if (!IS_PRIV(current->domain)) - return -EPERM; - - d = find_domain_by_id(op.domid); - if (d == NULL) - return -ESRCH; - - rc = -EINVAL; - if (is_hvm_domain(d)) { - vmx_vioapic_set_irq(d, op.irq, op.level); - rc = 0; - } - - put_domain(d); + case HVMOP_set_pci_intx_level: + rc = hvmop_set_pci_intx_level( + guest_handle_cast(arg, xen_hvm_set_pci_intx_level_t)); + break; + + case HVMOP_set_isa_irq_level: + rc = hvmop_set_isa_irq_level( + guest_handle_cast(arg, xen_hvm_set_isa_irq_level_t)); + break; + + case HVMOP_set_pci_link_route: + rc = 0; break; - } default: gdprintk(XENLOG_INFO, "Bad HVM op %ld.\n", op); diff --git a/xen/arch/ia64/vmx/vmx_init.c b/xen/arch/ia64/vmx/vmx_init.c index 03b7af7b9c..c4cb0150ea 100644 --- a/xen/arch/ia64/vmx/vmx_init.c +++ b/xen/arch/ia64/vmx/vmx_init.c @@ -48,13 +48,12 @@ #include #include #include -#include +#include #include #include /* Global flag to identify whether Intel vmx feature is on */ u32 vmx_enabled = 0; -unsigned int opt_vmx_debug_level = 0; static u32 vm_order; static u64 buffer_size; static u64 vp_env_info; @@ -359,17 +358,11 @@ static const io_range_t io_ranges[] = { {PIB_START, PIB_SIZE, GPFN_PIB}, }; -/* Reseve 1 page for shared I/O ,1 page for xenstore and 1 page for buffer I/O. */ -#define VMX_SYS_PAGES (3 + (GFW_SIZE >> PAGE_SHIFT)) -/* If we support maxmem for domVTi, we should change from tot_page to max_pages. - * #define VMX_CONFIG_PAGES(d) ((d)->max_pages - VMX_SYS_PAGES) - */ -#define VMX_CONFIG_PAGES(d) ((d)->tot_pages - VMX_SYS_PAGES) - -static void vmx_build_physmap_table(struct domain *d) +// The P2M table is built in libxc/ia64/xc_ia64_hvm_build.c @ setup_guest() +// so only mark IO memory space here +static void vmx_build_io_physmap_table(struct domain *d) { - unsigned long i, j, start, tmp, end, mfn; - struct list_head *list_ent = d->page_list.next; + unsigned long i, j; /* Mark I/O ranges */ for (i = 0; i < (sizeof(io_ranges) / sizeof(io_range_t)); i++) { @@ -379,63 +372,13 @@ static void vmx_build_physmap_table(struct domain *d) ASSIGN_writable); } - /* Map normal memory below 3G */ - end = VMX_CONFIG_PAGES(d) << PAGE_SHIFT; - tmp = end < MMIO_START ? end : MMIO_START; - for (i = 0; (i < tmp) && (list_ent != &d->page_list); i += PAGE_SIZE) { - mfn = page_to_mfn(list_entry(list_ent, struct page_info, list)); - list_ent = mfn_to_page(mfn)->list.next; - if (VGA_IO_START <= i && i < VGA_IO_START + VGA_IO_SIZE) - continue; - assign_domain_page(d, i, mfn << PAGE_SHIFT); - } - ASSERT(list_ent != &d->page_list); - - /* Map normal memory beyond 4G */ - if (unlikely(end > MMIO_START)) { - start = 4 * MEM_G; - end = start + (end - 3 * MEM_G); - for (i = start; - (i < end) && (list_ent != &d->page_list); i += PAGE_SIZE) { - mfn = page_to_mfn(list_entry(list_ent, - struct page_info, list)); - assign_domain_page(d, i, mfn << PAGE_SHIFT); - list_ent = mfn_to_page(mfn)->list.next; - } - ASSERT(list_ent != &d->page_list); - } - - /* Map guest firmware */ - for (i = GFW_START; (i < GFW_START + GFW_SIZE) && - (list_ent != &d->page_list); i += PAGE_SIZE) { - mfn = page_to_mfn(list_entry(list_ent, struct page_info, list)); - assign_domain_page(d, i, mfn << PAGE_SHIFT); - list_ent = mfn_to_page(mfn)->list.next; - } - ASSERT(list_ent != &d->page_list); - - /* Map for shared I/O page and xenstore */ - mfn = page_to_mfn(list_entry(list_ent, struct page_info, list)); - assign_domain_page(d, IO_PAGE_START, mfn << PAGE_SHIFT); - list_ent = mfn_to_page(mfn)->list.next; - ASSERT(list_ent != &d->page_list); - - mfn = page_to_mfn(list_entry(list_ent, struct page_info, list)); - assign_domain_page(d, STORE_PAGE_START, mfn << PAGE_SHIFT); - list_ent = mfn_to_page(mfn)->list.next; - ASSERT(list_ent != &d->page_list); - - mfn = page_to_mfn(list_entry(list_ent, struct page_info, list)); - assign_domain_page(d, BUFFER_IO_PAGE_START, mfn << PAGE_SHIFT); - list_ent = mfn_to_page(mfn)->list.next; - ASSERT(list_ent == &d->page_list); } void vmx_setup_platform(struct domain *d) { ASSERT(d != dom0); /* only for non-privileged vti domain */ - vmx_build_physmap_table(d); + vmx_build_io_physmap_table(d); d->arch.vmx_platform.shared_page_va = (unsigned long)__va(__gpa_to_mpa(d, IO_PAGE_START)); @@ -452,11 +395,8 @@ void vmx_setup_platform(struct domain *d) memset(&d->shared_info->evtchn_mask[0], 0xff, sizeof(d->shared_info->evtchn_mask)); - /* initiate spinlock for pass virq */ - spin_lock_init(&d->arch.arch_vmx.virq_assist_lock); - /* Initialize iosapic model within hypervisor */ - vioapic_init(d); + viosapic_init(d); } void vmx_do_launch(struct vcpu *v) diff --git a/xen/arch/ia64/vmx/vmx_process.c b/xen/arch/ia64/vmx/vmx_process.c index 3526175cbd..94011beb71 100644 --- a/xen/arch/ia64/vmx/vmx_process.c +++ b/xen/arch/ia64/vmx/vmx_process.c @@ -206,34 +206,19 @@ void leave_hypervisor_tail(struct pt_regs *regs) do_softirq(); local_irq_disable(); -// if (user_regs != regs) -// printk("WARNING: checking pending interrupt in nested interrupt!!!\n"); - - /* FIXME: Check event pending indicator, and set - * pending bit if necessary to inject back to guest. - * Should be careful about window between this check - * and above assist, since IOPACKET_PORT shouldn't be - * injected into vmx domain. - * - * Now hardcode the vector as 0x10 temporarily - */ -// if (event_pending(v)&&(!(VLSAPIC_INSVC(v,0)&(1UL<<0x10)))) { -// VCPU(v, irr[0]) |= 1UL << 0x10; -// v->arch.irq_new_pending = 1; -// } - if (v->vcpu_id == 0) { int callback_irq = d->arch.hvm_domain.params[HVM_PARAM_CALLBACK_IRQ]; if (callback_irq != 0 && local_events_need_delivery()) { - /*inject para-device call back irq*/ - v->vcpu_info->evtchn_upcall_mask = 1; - vmx_vcpu_pend_interrupt(v, callback_irq); + /* change level for para-device callback irq */ + /* use level irq to send discrete event */ + viosapic_set_irq(d, callback_irq, 1); + viosapic_set_irq(d, callback_irq, 0); } } - if ( v->arch.irq_new_pending ) { - v->arch.irq_new_pending = 0; + rmb(); + if (xchg(&v->arch.irq_new_pending, 0)) { v->arch.irq_new_condition = 0; vmx_check_pending_irq(v); return; diff --git a/xen/arch/ia64/vmx/vmx_support.c b/xen/arch/ia64/vmx/vmx_support.c index ea7e24afe8..667d983857 100644 --- a/xen/arch/ia64/vmx/vmx_support.c +++ b/xen/arch/ia64/vmx/vmx_support.c @@ -76,11 +76,6 @@ void vmx_send_assist_req(struct vcpu *v) p->state = STATE_IOREQ_READY; notify_via_xen_event_channel(v->arch.arch_vmx.xen_port); - /* - * Waiting for MMIO completion - * like the wait_on_xen_event_channel() macro like... - * but, we can't call do_softirq() at this point.. - */ for (;;) { if (p->state != STATE_IOREQ_READY && p->state != STATE_IOREQ_INPROCESS) @@ -96,6 +91,7 @@ void vmx_send_assist_req(struct vcpu *v) } raise_softirq(SCHEDULE_SOFTIRQ); + do_softirq(); mb(); } diff --git a/xen/arch/ia64/xen/Makefile b/xen/arch/ia64/xen/Makefile index 87f22f0966..2ddd6bfd60 100644 --- a/xen/arch/ia64/xen/Makefile +++ b/xen/arch/ia64/xen/Makefile @@ -1,3 +1,7 @@ +subdir-y += oprofile + +obj-y += machine_kexec.o +obj-y += crash.o obj-y += acpi.o obj-y += dom0_ops.o obj-y += domain.o diff --git a/xen/arch/ia64/xen/crash.c b/xen/arch/ia64/xen/crash.c new file mode 100644 index 0000000000..f1729786ea --- /dev/null +++ b/xen/arch/ia64/xen/crash.c @@ -0,0 +1,19 @@ +#include /* for printk() used in stub */ +#include +#include + +void machine_crash_shutdown(void) +{ + printk("STUB: " __FILE__ ": %s: not implemented\n", __FUNCTION__); +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ + diff --git a/xen/arch/ia64/xen/dom0_ops.c b/xen/arch/ia64/xen/dom0_ops.c index 8d53e43ff3..e21c96f827 100644 --- a/xen/arch/ia64/xen/dom0_ops.c +++ b/xen/arch/ia64/xen/dom0_ops.c @@ -55,7 +55,7 @@ long arch_do_domctl(xen_domctl_t *op, XEN_GUEST_HANDLE(xen_domctl_t) u_domctl) pte = (pte_t *)lookup_noalloc_domain_pte(d, (start_page + i) << PAGE_SHIFT); if (pte && pte_present(*pte)) - mfn = pte_pfn(*pte); + mfn = start_page + i; else mfn = INVALID_MFN; @@ -109,6 +109,7 @@ long arch_do_domctl(xen_domctl_t *op, XEN_GUEST_HANDLE(xen_domctl_t) u_domctl) BUG_ON(v->arch.privregs == NULL); free_domheap_pages(virt_to_page(v->arch.privregs), get_order_from_shift(XMAPPEDREGS_SHIFT)); + v->arch.privregs = NULL; relinquish_vcpu_resources(v); } } @@ -125,6 +126,19 @@ long arch_do_domctl(xen_domctl_t *op, XEN_GUEST_HANDLE(xen_domctl_t) u_domctl) for_each_vcpu (d, v) v->arch.breakimm = d->arch.breakimm; } + { + /* + * XXX IA64_SHARED_INFO_PADDR + * assign these pages into guest psudo physical address + * space for dom0 to map this page by gmfn. + * this is necessary for domain build, save, restore and + * dump-core. + */ + unsigned long i; + for (i = 0; i < XSI_SIZE; i += PAGE_SIZE) + assign_domain_page(d, IA64_SHARED_INFO_PADDR + i, + virt_to_maddr(d->shared_info + i)); + } } } @@ -340,9 +354,19 @@ do_dom0vp_op(unsigned long cmd, ret = dom0vp_add_physmap(d, arg0, arg1, (unsigned int)arg2, (domid_t)arg3); break; + case IA64_DOM0VP_add_physmap_with_gmfn: + ret = dom0vp_add_physmap_with_gmfn(d, arg0, arg1, (unsigned int)arg2, + (domid_t)arg3); + break; case IA64_DOM0VP_expose_p2m: ret = dom0vp_expose_p2m(d, arg0, arg1, arg2, arg3); break; + case IA64_DOM0VP_perfmon: { + XEN_GUEST_HANDLE(void) hnd; + set_xen_guest_handle(hnd, (void*)arg1); + ret = do_perfmon_op(arg0, hnd, arg2); + break; + } default: ret = -1; printk("unknown dom0_vp_op 0x%lx\n", cmd); diff --git a/xen/arch/ia64/xen/dom_fw.c b/xen/arch/ia64/xen/dom_fw.c index 91b618ee88..cfb1cb13ce 100644 --- a/xen/arch/ia64/xen/dom_fw.c +++ b/xen/arch/ia64/xen/dom_fw.c @@ -240,6 +240,23 @@ acpi_update_lsapic (acpi_table_entry_header *header, const unsigned long end) return 0; } +static int __init +acpi_patch_plat_int_src ( + acpi_table_entry_header *header, const unsigned long end) +{ + struct acpi_table_plat_int_src *plintsrc; + + plintsrc = (struct acpi_table_plat_int_src *)header; + if (!plintsrc) + return -EINVAL; + + if (plintsrc->type == ACPI_INTERRUPT_CPEI) { + printk("ACPI_INTERRUPT_CPEI disabled for Domain0\n"); + plintsrc->type = -1; + } + return 0; +} + static u8 generate_acpi_checksum(void *tbl, unsigned long len) { @@ -271,7 +288,11 @@ static void touch_acpi_table(void) { lsapic_nbr = 0; if (acpi_table_parse_madt(ACPI_MADT_LSAPIC, acpi_update_lsapic, 0) < 0) - printk("Error parsing MADT - no LAPIC entires\n"); + printk("Error parsing MADT - no LAPIC entries\n"); + if (acpi_table_parse_madt(ACPI_MADT_PLAT_INT_SRC, + acpi_patch_plat_int_src, 0) < 0) + printk("Error parsing MADT - no PLAT_INT_SRC entries\n"); + acpi_table_parse(ACPI_APIC, acpi_update_madt_checksum); return; diff --git a/xen/arch/ia64/xen/domain.c b/xen/arch/ia64/xen/domain.c index ec0d415a05..8406c12708 100644 --- a/xen/arch/ia64/xen/domain.c +++ b/xen/arch/ia64/xen/domain.c @@ -48,6 +48,7 @@ #include #include #include +#include unsigned long dom0_size = 512*1024*1024; @@ -164,8 +165,11 @@ void context_switch(struct vcpu *prev, struct vcpu *next) local_irq_save(spsr); - __ia64_save_fpu(prev->arch._thread.fph); - __ia64_load_fpu(next->arch._thread.fph); + if (!is_idle_domain(prev->domain)) + __ia64_save_fpu(prev->arch._thread.fph); + if (!is_idle_domain(next->domain)) + __ia64_load_fpu(next->arch._thread.fph); + if (VMX_DOMAIN(prev)) { vmx_save_state(prev); if (!VMX_DOMAIN(next)) { @@ -228,11 +232,35 @@ void continue_running(struct vcpu *same) /* nothing to do */ } +#ifdef CONFIG_PERFMON +static int pal_halt = 1; +static int can_do_pal_halt = 1; + +static int __init nohalt_setup(char * str) +{ + pal_halt = can_do_pal_halt = 0; + return 1; +} +__setup("nohalt", nohalt_setup); + +void +update_pal_halt_status(int status) +{ + can_do_pal_halt = pal_halt && status; +} +#else +#define can_do_pal_halt (1) +#endif + static void default_idle(void) { local_irq_disable(); - if ( !softirq_pending(smp_processor_id()) ) - safe_halt(); + if ( !softirq_pending(smp_processor_id()) ) { + if (can_do_pal_halt) + safe_halt(); + else + cpu_relax(); + } local_irq_enable(); } @@ -280,6 +308,16 @@ void relinquish_vcpu_resources(struct vcpu *v) if (HAS_PERVCPU_VHPT(v->domain)) pervcpu_vhpt_free(v); if (v->arch.privregs != NULL) { + // this might be called by arch_do_domctl() with XEN_DOMCTL_arch_setup() + // for domVTi. + if (!(atomic_read(&v->domain->refcnt) & DOMAIN_DESTROYED)) { + unsigned long i; + for (i = 0; i < XMAPPEDREGS_SIZE; i += PAGE_SIZE) + guest_physmap_remove_page(v->domain, + IA64_XMAPPEDREGS_PADDR(v->vcpu_id) + i, + virt_to_maddr(v->arch.privregs + i)); + } + free_xenheap_pages(v->arch.privregs, get_order_from_shift(XMAPPEDREGS_SHIFT)); v->arch.privregs = NULL; @@ -339,6 +377,15 @@ int vcpu_initialise(struct vcpu *v) for (i = 0; i < (1 << order); i++) share_xen_page_with_guest(virt_to_page(v->arch.privregs) + i, d, XENSHARE_writable); + /* + * XXX IA64_XMAPPEDREGS_PADDR + * assign these pages into guest pseudo physical address + * space for dom0 to map this page by gmfn. + * this is necessary for domain save, restore and dump-core. + */ + for (i = 0; i < XMAPPEDREGS_SIZE; i += PAGE_SIZE) + assign_domain_page(d, IA64_XMAPPEDREGS_PADDR(v->vcpu_id) + i, + virt_to_maddr(v->arch.privregs + i)); tlbflush_update_time(&v->arch.tlbflush_timestamp, tlbflush_current_time()); @@ -462,11 +509,10 @@ fail_nomem1: void arch_domain_destroy(struct domain *d) { - BUG_ON(d->arch.mm.pgd != NULL); + mm_final_teardown(d); + if (d->shared_info != NULL) free_xenheap_pages(d->shared_info, get_order_from_shift(XSI_SHIFT)); - if (d->arch.shadow_bitmap != NULL) - xfree(d->arch.shadow_bitmap); tlb_track_destroy(d); @@ -482,7 +528,8 @@ void arch_getdomaininfo_ctxt(struct vcpu *v, struct vcpu_guest_context *c) struct vcpu_extra_regs *er = &c->extra_regs; c->user_regs = *vcpu_regs (v); - c->privregs_pfn = virt_to_maddr(v->arch.privregs) >> PAGE_SHIFT; + c->privregs_pfn = get_gpfn_from_mfn(virt_to_maddr(v->arch.privregs) >> + PAGE_SHIFT); /* Fill extra regs. */ for (i = 0; i < 8; i++) { @@ -613,19 +660,22 @@ static void relinquish_memory(struct domain *d, struct list_head *list) void domain_relinquish_resources(struct domain *d) { - /* Relinquish every page of memory. */ - - // relase page traversing d->arch.mm. - relinquish_mm(d); - + /* Relinquish guest resources for VT-i domain. */ if (d->vcpu[0] && VMX_DOMAIN(d->vcpu[0])) vmx_relinquish_guest_resources(d); + /* Tear down shadow mode stuff. */ + mm_teardown(d); + + /* Relinquish every page of memory. */ relinquish_memory(d, &d->xenpage_list); relinquish_memory(d, &d->page_list); if (d->arch.is_vti && d->arch.sal_data) xfree(d->arch.sal_data); + + /* Free page used by xen oprofile buffer */ + free_xenoprof_pages(d); } unsigned long diff --git a/xen/arch/ia64/xen/faults.c b/xen/arch/ia64/xen/faults.c index 05aea16fc2..741800fa70 100644 --- a/xen/arch/ia64/xen/faults.c +++ b/xen/arch/ia64/xen/faults.c @@ -524,7 +524,9 @@ ia64_handle_break(unsigned long ifa, struct pt_regs *regs, unsigned long isr, struct vcpu *v = current; IA64FAULT vector; - if (iim == 0x80001 || iim == 0x80002) { //FIXME: don't hardcode constant + /* FIXME: don't hardcode constant */ + if ((iim == 0x80001 || iim == 0x80002) + && ia64_get_cpl(regs->cr_ipsr) == 2) { do_ssc(vcpu_get_gr(current, 36), regs); } #ifdef CRASH_DEBUG diff --git a/xen/arch/ia64/xen/fw_emul.c b/xen/arch/ia64/xen/fw_emul.c index 28d827e975..2a9f8fcb53 100644 --- a/xen/arch/ia64/xen/fw_emul.c +++ b/xen/arch/ia64/xen/fw_emul.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "hpsim_ssc.h" @@ -31,11 +32,99 @@ #include #include #include +#include static DEFINE_SPINLOCK(efi_time_services_lock); extern unsigned long running_on_sim; +struct sal_mc_params { + u64 param_type; + u64 i_or_m; + u64 i_or_m_val; + u64 timeout; + u64 rz_always; +} sal_mc_params[SAL_MC_PARAM_CPE_INT + 1]; + +struct sal_vectors { + u64 vector_type; + u64 handler_addr1; + u64 gp1; + u64 handler_len1; + u64 handler_addr2; + u64 gp2; + u64 handler_len2; +} sal_vectors[SAL_VECTOR_OS_BOOT_RENDEZ + 1]; + +struct smp_call_args_t { + u64 type; + u64 ret; + u64 target; + struct domain *domain; + int corrected; + int status; + void *data; +}; + +extern sal_log_record_header_t *sal_record; +DEFINE_SPINLOCK(sal_record_lock); + +extern spinlock_t sal_queue_lock; + +#define IA64_SAL_NO_INFORMATION_AVAILABLE -5 + +#if defined(IA64_SAL_DEBUG_INFO) +static const char * const rec_name[] = { "MCA", "INIT", "CMC", "CPE" }; + +# define IA64_SAL_DEBUG(fmt...) printk("sal_emulator: " fmt) +#else +# define IA64_SAL_DEBUG(fmt...) +#endif + +void get_state_info_on(void *data) { + struct smp_call_args_t *arg = data; + int flags; + + spin_lock_irqsave(&sal_record_lock, flags); + memset(sal_record, 0, ia64_sal_get_state_info_size(arg->type)); + arg->ret = ia64_sal_get_state_info(arg->type, (u64 *)sal_record); + IA64_SAL_DEBUG("SAL_GET_STATE_INFO(%s) on CPU#%d returns %ld.\n", + rec_name[arg->type], smp_processor_id(), arg->ret); + if (arg->corrected) { + sal_record->severity = sal_log_severity_corrected; + IA64_SAL_DEBUG("%s: IA64_SAL_CLEAR_STATE_INFO(SAL_INFO_TYPE_MCA)" + " force\n", __FUNCTION__); + } + if (arg->ret > 0) { + /* + * Save current->domain and set to local(caller) domain for + * xencomm_paddr_to_maddr() which calculates maddr from + * paddr using mpa value of current->domain. + */ + struct domain *save; + save = current->domain; + current->domain = arg->domain; + if (xencomm_copy_to_guest((void*)arg->target, + sal_record, arg->ret, 0)) { + printk("SAL_GET_STATE_INFO can't copy to user!!!!\n"); + arg->status = IA64_SAL_NO_INFORMATION_AVAILABLE; + arg->ret = 0; + } + /* Restore current->domain to saved value. */ + current->domain = save; + } + spin_unlock_irqrestore(&sal_record_lock, flags); +} + +void clear_state_info_on(void *data) { + struct smp_call_args_t *arg = data; + + arg->ret = ia64_sal_clear_state_info(arg->type); + IA64_SAL_DEBUG("SAL_CLEAR_STATE_INFO(%s) on CPU#%d returns %ld.\n", + rec_name[arg->type], smp_processor_id(), arg->ret); + +} + struct sal_ret_values sal_emulator (long index, unsigned long in1, unsigned long in2, unsigned long in3, unsigned long in4, unsigned long in5, @@ -106,27 +195,160 @@ sal_emulator (long index, unsigned long in1, unsigned long in2, } } else - printk("*** CALLED SAL_SET_VECTORS %lu. IGNORED...\n", - in1); + { + if (in1 > sizeof(sal_vectors)/sizeof(sal_vectors[0])-1) + BUG(); + sal_vectors[in1].vector_type = in1; + sal_vectors[in1].handler_addr1 = in2; + sal_vectors[in1].gp1 = in3; + sal_vectors[in1].handler_len1 = in4; + sal_vectors[in1].handler_addr2 = in5; + sal_vectors[in1].gp2 = in6; + sal_vectors[in1].handler_len2 = in7; + } break; case SAL_GET_STATE_INFO: - /* No more info. */ - status = -5; - r9 = 0; + if (current->domain == dom0) { + sal_queue_entry_t *e; + unsigned long flags; + struct smp_call_args_t arg; + + spin_lock_irqsave(&sal_queue_lock, flags); + if (!sal_queue || list_empty(&sal_queue[in1])) { + sal_log_record_header_t header; + XEN_GUEST_HANDLE(void) handle = + *(XEN_GUEST_HANDLE(void)*)&in3; + + IA64_SAL_DEBUG("SAL_GET_STATE_INFO(%s) " + "no sal_queue entry found.\n", + rec_name[in1]); + memset(&header, 0, sizeof(header)); + + if (copy_to_guest(handle, &header, 1)) { + printk("sal_emulator: " + "SAL_GET_STATE_INFO can't copy " + "empty header to user: 0x%lx\n", + in3); + } + status = IA64_SAL_NO_INFORMATION_AVAILABLE; + r9 = 0; + spin_unlock_irqrestore(&sal_queue_lock, flags); + break; + } + e = list_entry(sal_queue[in1].next, + sal_queue_entry_t, list); + spin_unlock_irqrestore(&sal_queue_lock, flags); + + IA64_SAL_DEBUG("SAL_GET_STATE_INFO(%s <= %s) " + "on CPU#%d.\n", + rec_name[e->sal_info_type], + rec_name[in1], e->cpuid); + + arg.type = e->sal_info_type; + arg.target = in3; + arg.corrected = !!((in1 != e->sal_info_type) && + (e->sal_info_type == SAL_INFO_TYPE_MCA)); + arg.domain = current->domain; + arg.status = 0; + + if (e->cpuid == smp_processor_id()) { + IA64_SAL_DEBUG("SAL_GET_STATE_INFO: local\n"); + get_state_info_on(&arg); + } else { + int ret; + IA64_SAL_DEBUG("SAL_GET_STATE_INFO: remote\n"); + ret = smp_call_function_single(e->cpuid, + get_state_info_on, + &arg, 0, 1); + if (ret < 0) { + printk("SAL_GET_STATE_INFO " + "smp_call_function_single error:" + " %d\n", ret); + arg.ret = 0; + arg.status = + IA64_SAL_NO_INFORMATION_AVAILABLE; + } + } + r9 = arg.ret; + status = arg.status; + if (r9 == 0) { + spin_lock_irqsave(&sal_queue_lock, flags); + list_del(&e->list); + spin_unlock_irqrestore(&sal_queue_lock, flags); + xfree(e); + } + } else { + status = IA64_SAL_NO_INFORMATION_AVAILABLE; + r9 = 0; + } break; case SAL_GET_STATE_INFO_SIZE: - /* Return a dummy size. */ - status = 0; - r9 = 128; + r9 = ia64_sal_get_state_info_size(in1); break; case SAL_CLEAR_STATE_INFO: - /* Noop. */ + if (current->domain == dom0) { + sal_queue_entry_t *e; + unsigned long flags; + struct smp_call_args_t arg; + + spin_lock_irqsave(&sal_queue_lock, flags); + if (list_empty(&sal_queue[in1])) { + IA64_SAL_DEBUG("SAL_CLEAR_STATE_INFO(%s) " + "no sal_queue entry found.\n", + rec_name[in1]); + status = IA64_SAL_NO_INFORMATION_AVAILABLE; + r9 = 0; + spin_unlock_irqrestore(&sal_queue_lock, flags); + break; + } + e = list_entry(sal_queue[in1].next, + sal_queue_entry_t, list); + + list_del(&e->list); + spin_unlock_irqrestore(&sal_queue_lock, flags); + + IA64_SAL_DEBUG("SAL_CLEAR_STATE_INFO(%s <= %s) " + "on CPU#%d.\n", + rec_name[e->sal_info_type], + rec_name[in1], e->cpuid); + + + arg.type = e->sal_info_type; + arg.status = 0; + if (e->cpuid == smp_processor_id()) { + IA64_SAL_DEBUG("SAL_CLEAR_STATE_INFO: local\n"); + clear_state_info_on(&arg); + } else { + int ret; + IA64_SAL_DEBUG("SAL_CLEAR_STATE_INFO: remote\n"); + ret = smp_call_function_single(e->cpuid, + clear_state_info_on, &arg, 0, 1); + if (ret < 0) { + printk("sal_emulator: " + "SAL_CLEAR_STATE_INFO " + "smp_call_function_single error:" + " %d\n", ret); + arg.ret = 0; + arg.status = + IA64_SAL_NO_INFORMATION_AVAILABLE; + } + } + r9 = arg.ret; + status = arg.status; + xfree(e); + } break; case SAL_MC_RENDEZ: printk("*** CALLED SAL_MC_RENDEZ. IGNORED...\n"); break; case SAL_MC_SET_PARAMS: - printk("*** CALLED SAL_MC_SET_PARAMS. IGNORED...\n"); + if (in1 > sizeof(sal_mc_params)/sizeof(sal_mc_params[0])) + BUG(); + sal_mc_params[in1].param_type = in1; + sal_mc_params[in1].i_or_m = in2; + sal_mc_params[in1].i_or_m_val = in3; + sal_mc_params[in1].timeout = in4; + sal_mc_params[in1].rz_always = in5; break; case SAL_CACHE_FLUSH: if (1) { @@ -390,8 +612,10 @@ xen_pal_emulator(unsigned long index, u64 in1, u64 in2, u64 in3) case PAL_HALT_LIGHT: if (VMX_DOMAIN(current)) { /* Called by VTI. */ - if (!is_unmasked_irq(current)) + if (!is_unmasked_irq(current)) { do_sched_op_compat(SCHEDOP_block, 0); + do_softirq(); + } status = PAL_STATUS_SUCCESS; } break; @@ -977,9 +1201,9 @@ if (!running_on_sim) { printk("SSC_OPEN, not implemented on hardware. (ignoring vcpu_set_gr(current,8,-1L,0); break; default: - printk("ia64_handle_break: bad ssc code %lx, iip=0x%lx, b0=0x%lx... spinning\n", - ssc, regs->cr_iip, regs->b0); - while(1); + panic_domain(regs, + "%s: bad ssc code %lx, iip=0x%lx, b0=0x%lx\n", + __func__, ssc, regs->cr_iip, regs->b0); break; } vcpu_increment_iip(current); diff --git a/xen/arch/ia64/xen/hypercall.c b/xen/arch/ia64/xen/hypercall.c index effa38ef99..69be18efcd 100644 --- a/xen/arch/ia64/xen/hypercall.c +++ b/xen/arch/ia64/xen/hypercall.c @@ -68,7 +68,7 @@ const hypercall_t ia64_hypercall_table[NR_hypercalls] = (hypercall_t)do_ni_hypercall, /* do_nmi_op */ (hypercall_t)do_sched_op, (hypercall_t)do_callback_op, /* */ /* 30 */ - (hypercall_t)do_ni_hypercall, /* */ + (hypercall_t)do_xenoprof_op, /* */ (hypercall_t)do_event_channel_op, (hypercall_t)do_physdev_op, (hypercall_t)do_hvm_op, /* */ @@ -248,6 +248,8 @@ ia64_hypercall(struct pt_regs *regs) set_timer(&v->arch.hlt_timer, vcpu_get_next_timer_ns(v)); do_sched_op_compat(SCHEDOP_block, 0); + /* do_block only pends a softirq */ + do_softirq(); stop_timer(&v->arch.hlt_timer); } regs->r8 = 0; @@ -452,8 +454,36 @@ static long do_physdev_op(int cmd, XEN_GUEST_HANDLE(void) arg) break; } - default: + case PHYSDEVOP_free_irq_vector: { + struct physdev_irq irq_op; + int vector; + + ret = -EFAULT; + if ( copy_from_guest(&irq_op, arg, 1) != 0 ) + break; + + ret = -EPERM; + if ( !IS_PRIV(current->domain) ) + break; + ret = -EINVAL; + vector = irq_op.vector; + if (vector < IA64_FIRST_DEVICE_VECTOR || + vector > IA64_LAST_DEVICE_VECTOR) + break; + + /* XXX This should be called, but causes a NAT consumption via the + * reboot notifier_call_chain in dom0 if a device is hidden for + * a driver domain using pciback.hide= (specifically, hiding function + * 1 of a 2 port e1000 card). + * free_irq_vector(vector); + */ + ret = 0; + break; + } + + default: + ret = -ENOSYS; break; } @@ -479,7 +509,7 @@ static long register_guest_callback(struct callback_register *reg) break; default: - ret = -EINVAL; + ret = -ENOSYS; break; } @@ -488,7 +518,7 @@ static long register_guest_callback(struct callback_register *reg) static long unregister_guest_callback(struct callback_unregister *unreg) { - return -EINVAL ; + return -EINVAL; } /* First time to add callback to xen/ia64, so let's just stick to @@ -525,7 +555,7 @@ static long do_callback_op(int cmd, XEN_GUEST_HANDLE(void) arg) break; default: - ret = -EINVAL; + ret = -ENOSYS; break; } diff --git a/xen/arch/ia64/xen/machine_kexec.c b/xen/arch/ia64/xen/machine_kexec.c new file mode 100644 index 0000000000..df7ad0c9b6 --- /dev/null +++ b/xen/arch/ia64/xen/machine_kexec.c @@ -0,0 +1,34 @@ +#include /* for printk() used in stubs */ +#include +#include + +int machine_kexec_load(int type, int slot, xen_kexec_image_t *image) +{ + printk("STUB: " __FILE__ ": %s: not implemented\n", __FUNCTION__); + return -1; +} + +void machine_kexec_unload(int type, int slot, xen_kexec_image_t *image) +{ + printk("STUB: " __FILE__ ": %s: not implemented\n", __FUNCTION__); +} + +void machine_kexec(xen_kexec_image_t *image) +{ + printk("STUB: " __FILE__ ": %s: not implemented\n", __FUNCTION__); +} + +void machine_reboot_kexec(xen_kexec_image_t *image) +{ + printk("STUB: " __FILE__ ": %s: not implemented\n", __FUNCTION__); +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/arch/ia64/xen/mm.c b/xen/arch/ia64/xen/mm.c index e3134ec7e1..0a5acc0636 100644 --- a/xen/arch/ia64/xen/mm.c +++ b/xen/arch/ia64/xen/mm.c @@ -249,17 +249,21 @@ try_to_clear_PGC_allocate(struct domain* d, struct page_info* page) } static void -relinquish_pte(struct domain* d, pte_t* pte) +mm_teardown_pte(struct domain* d, volatile pte_t* pte, unsigned long offset) { - unsigned long mfn = pte_pfn(*pte); + pte_t old_pte; + unsigned long mfn; struct page_info* page; + old_pte = ptep_get_and_clear(&d->arch.mm, offset, pte);// acquire semantics + // vmx domain use bit[58:56] to distinguish io region from memory. // see vmx_build_physmap_table() in vmx_init.c - if (!pte_mem(*pte)) + if (!pte_mem(old_pte)) return; // domain might map IO space or acpi table pages. check it. + mfn = pte_pfn(old_pte); if (!mfn_valid(mfn)) return; page = mfn_to_page(mfn); @@ -272,81 +276,137 @@ relinquish_pte(struct domain* d, pte_t* pte) return; } - if (page_get_owner(page) == d) { + if (pte_pgc_allocated(old_pte)) { + BUG_ON(page_get_owner(page) != d); BUG_ON(get_gpfn_from_mfn(mfn) == INVALID_M2P_ENTRY); set_gpfn_from_mfn(mfn, INVALID_M2P_ENTRY); + try_to_clear_PGC_allocate(d, page); } - - try_to_clear_PGC_allocate(d, page); put_page(page); } static void -relinquish_pmd(struct domain* d, pmd_t* pmd, unsigned long offset) +mm_teardown_pmd(struct domain* d, volatile pmd_t* pmd, unsigned long offset) { unsigned long i; - pte_t* pte = pte_offset_map(pmd, offset); + volatile pte_t* pte = pte_offset_map(pmd, offset); for (i = 0; i < PTRS_PER_PTE; i++, pte++) { - if (!pte_present(*pte)) + if (!pte_present(*pte)) // acquire semantics continue; - - relinquish_pte(d, pte); + mm_teardown_pte(d, pte, offset + (i << PAGE_SHIFT)); } - pte_free_kernel(pte_offset_map(pmd, offset)); } static void -relinquish_pud(struct domain* d, pud_t *pud, unsigned long offset) +mm_teardown_pud(struct domain* d, volatile pud_t *pud, unsigned long offset) { unsigned long i; - pmd_t *pmd = pmd_offset(pud, offset); + volatile pmd_t *pmd = pmd_offset(pud, offset); for (i = 0; i < PTRS_PER_PMD; i++, pmd++) { - if (!pmd_present(*pmd)) + if (!pmd_present(*pmd)) // acquire semantics continue; - - relinquish_pmd(d, pmd, offset + (i << PMD_SHIFT)); + mm_teardown_pmd(d, pmd, offset + (i << PMD_SHIFT)); } - pmd_free(pmd_offset(pud, offset)); } static void -relinquish_pgd(struct domain* d, pgd_t *pgd, unsigned long offset) +mm_teardown_pgd(struct domain* d, volatile pgd_t *pgd, unsigned long offset) { unsigned long i; - pud_t *pud = pud_offset(pgd, offset); + volatile pud_t *pud = pud_offset(pgd, offset); for (i = 0; i < PTRS_PER_PUD; i++, pud++) { - if (!pud_present(*pud)) + if (!pud_present(*pud)) // acquire semantics continue; - - relinquish_pud(d, pud, offset + (i << PUD_SHIFT)); + mm_teardown_pud(d, pud, offset + (i << PUD_SHIFT)); } - pud_free(pud_offset(pgd, offset)); } void -relinquish_mm(struct domain* d) +mm_teardown(struct domain* d) { struct mm_struct* mm = &d->arch.mm; unsigned long i; - pgd_t* pgd; + volatile pgd_t* pgd; if (mm->pgd == NULL) return; + pgd = pgd_offset(mm, 0); + for (i = 0; i < PTRS_PER_PGD; i++, pgd++) { + if (!pgd_present(*pgd)) // acquire semantics + continue; + mm_teardown_pgd(d, pgd, i << PGDIR_SHIFT); + } +} + +static void +mm_p2m_teardown_pmd(struct domain* d, volatile pmd_t* pmd, + unsigned long offset) +{ + pte_free_kernel(pte_offset_map(pmd, offset)); +} + +static void +mm_p2m_teardown_pud(struct domain* d, volatile pud_t *pud, + unsigned long offset) +{ + unsigned long i; + volatile pmd_t *pmd = pmd_offset(pud, offset); + + for (i = 0; i < PTRS_PER_PMD; i++, pmd++) { + if (!pmd_present(*pmd)) + continue; + mm_p2m_teardown_pmd(d, pmd, offset + (i << PMD_SHIFT)); + } + pmd_free(pmd_offset(pud, offset)); +} + +static void +mm_p2m_teardown_pgd(struct domain* d, volatile pgd_t *pgd, + unsigned long offset) +{ + unsigned long i; + volatile pud_t *pud = pud_offset(pgd, offset); + + for (i = 0; i < PTRS_PER_PUD; i++, pud++) { + if (!pud_present(*pud)) + continue; + mm_p2m_teardown_pud(d, pud, offset + (i << PUD_SHIFT)); + } + pud_free(pud_offset(pgd, offset)); +} + +static void +mm_p2m_teardown(struct domain* d) +{ + struct mm_struct* mm = &d->arch.mm; + unsigned long i; + volatile pgd_t* pgd; + + BUG_ON(mm->pgd == NULL); pgd = pgd_offset(mm, 0); for (i = 0; i < PTRS_PER_PGD; i++, pgd++) { if (!pgd_present(*pgd)) continue; - - relinquish_pgd(d, pgd, i << PGDIR_SHIFT); + mm_p2m_teardown_pgd(d, pgd, i << PGDIR_SHIFT); } pgd_free(mm->pgd); mm->pgd = NULL; } +void +mm_final_teardown(struct domain* d) +{ + if (d->arch.shadow_bitmap != NULL) { + xfree(d->arch.shadow_bitmap); + d->arch.shadow_bitmap = NULL; + } + mm_p2m_teardown(d); +} + // stolen from share_xen_page_with_guest() in xen/arch/x86/mm.c void share_xen_page_with_guest(struct page_info *page, @@ -400,13 +460,6 @@ gmfn_to_mfn_foreign(struct domain *d, unsigned long gpfn) { unsigned long pte; - // This function may be called from __gnttab_copy() - // during domain destruction with VNIF copy receiver. - // ** FIXME: This is not SMP-safe yet about p2m table. ** - if (unlikely(d->arch.mm.pgd == NULL)) { - BUG(); - return INVALID_MFN; - } pte = lookup_domain_mpa(d,gpfn << PAGE_SHIFT, NULL); if (!pte) { panic("gmfn_to_mfn_foreign: bad gpfn. spinning...\n"); @@ -514,38 +567,6 @@ unsigned long translate_domain_mpaddr(unsigned long mpaddr, return ((pteval & _PAGE_PPN_MASK) | (mpaddr & ~PAGE_MASK)); } -//XXX !xxx_present() should be used instread of !xxx_none()? -// __assign_new_domain_page(), assign_new_domain_page() and -// assign_new_domain0_page() are used only when domain creation. -// their accesses aren't racy so that returned pte_t doesn't need -// volatile qualifier -static pte_t* -__lookup_alloc_domain_pte(struct domain* d, unsigned long mpaddr) -{ - struct mm_struct *mm = &d->arch.mm; - pgd_t *pgd; - pud_t *pud; - pmd_t *pmd; - - BUG_ON(mm->pgd == NULL); - pgd = pgd_offset(mm, mpaddr); - if (pgd_none(*pgd)) { - pgd_populate(mm, pgd, pud_alloc_one(mm,mpaddr)); - } - - pud = pud_offset(pgd, mpaddr); - if (pud_none(*pud)) { - pud_populate(mm, pud, pmd_alloc_one(mm,mpaddr)); - } - - pmd = pmd_offset(pud, mpaddr); - if (pmd_none(*pmd)) { - pmd_populate_kernel(mm, pmd, pte_alloc_one_kernel(mm, mpaddr)); - } - - return pte_offset_map(pmd, mpaddr); -} - //XXX !xxx_present() should be used instread of !xxx_none()? // pud, pmd, pte page is zero cleared when they are allocated. // Their area must be visible before population so that @@ -554,15 +575,15 @@ static volatile pte_t* lookup_alloc_domain_pte(struct domain* d, unsigned long mpaddr) { struct mm_struct *mm = &d->arch.mm; - pgd_t *pgd; - pud_t *pud; - pmd_t *pmd; + volatile pgd_t *pgd; + volatile pud_t *pud; + volatile pmd_t *pmd; BUG_ON(mm->pgd == NULL); pgd = pgd_offset(mm, mpaddr); again_pgd: - if (unlikely(pgd_none(*pgd))) { + if (unlikely(pgd_none(*pgd))) { // acquire semantics pud_t *old_pud = NULL; pud = pud_alloc_one(mm, mpaddr); if (unlikely(!pgd_cmpxchg_rel(mm, pgd, old_pud, pud))) { @@ -573,7 +594,7 @@ lookup_alloc_domain_pte(struct domain* d, unsigned long mpaddr) pud = pud_offset(pgd, mpaddr); again_pud: - if (unlikely(pud_none(*pud))) { + if (unlikely(pud_none(*pud))) { // acquire semantics pmd_t* old_pmd = NULL; pmd = pmd_alloc_one(mm, mpaddr); if (unlikely(!pud_cmpxchg_rel(mm, pud, old_pmd, pmd))) { @@ -584,7 +605,7 @@ lookup_alloc_domain_pte(struct domain* d, unsigned long mpaddr) pmd = pmd_offset(pud, mpaddr); again_pmd: - if (unlikely(pmd_none(*pmd))) { + if (unlikely(pmd_none(*pmd))) { // acquire semantics pte_t* old_pte = NULL; pte_t* pte = pte_alloc_one_kernel(mm, mpaddr); if (unlikely(!pmd_cmpxchg_kernel_rel(mm, pmd, old_pte, pte))) { @@ -593,7 +614,7 @@ lookup_alloc_domain_pte(struct domain* d, unsigned long mpaddr) } } - return (volatile pte_t*)pte_offset_map(pmd, mpaddr); + return pte_offset_map(pmd, mpaddr); } //XXX xxx_none() should be used instread of !xxx_present()? @@ -601,48 +622,48 @@ volatile pte_t* lookup_noalloc_domain_pte(struct domain* d, unsigned long mpaddr) { struct mm_struct *mm = &d->arch.mm; - pgd_t *pgd; - pud_t *pud; - pmd_t *pmd; + volatile pgd_t *pgd; + volatile pud_t *pud; + volatile pmd_t *pmd; BUG_ON(mm->pgd == NULL); pgd = pgd_offset(mm, mpaddr); - if (unlikely(!pgd_present(*pgd))) + if (unlikely(!pgd_present(*pgd))) // acquire semantics return NULL; pud = pud_offset(pgd, mpaddr); - if (unlikely(!pud_present(*pud))) + if (unlikely(!pud_present(*pud))) // acquire semantics return NULL; pmd = pmd_offset(pud, mpaddr); - if (unlikely(!pmd_present(*pmd))) + if (unlikely(!pmd_present(*pmd))) // acquire semantics return NULL; - return (volatile pte_t*)pte_offset_map(pmd, mpaddr); + return pte_offset_map(pmd, mpaddr); } static volatile pte_t* lookup_noalloc_domain_pte_none(struct domain* d, unsigned long mpaddr) { struct mm_struct *mm = &d->arch.mm; - pgd_t *pgd; - pud_t *pud; - pmd_t *pmd; + volatile pgd_t *pgd; + volatile pud_t *pud; + volatile pmd_t *pmd; BUG_ON(mm->pgd == NULL); pgd = pgd_offset(mm, mpaddr); - if (unlikely(pgd_none(*pgd))) + if (unlikely(pgd_none(*pgd))) // acquire semantics return NULL; pud = pud_offset(pgd, mpaddr); - if (unlikely(pud_none(*pud))) + if (unlikely(pud_none(*pud))) // acquire semantics return NULL; pmd = pmd_offset(pud, mpaddr); - if (unlikely(pmd_none(*pmd))) + if (unlikely(pmd_none(*pmd))) // acquire semantics return NULL; - return (volatile pte_t*)pte_offset_map(pmd, mpaddr); + return pte_offset_map(pmd, mpaddr); } unsigned long @@ -733,7 +754,8 @@ xencomm_paddr_to_maddr(unsigned long paddr) /* Allocate a new page for domain and map it to the specified metaphysical address. */ static struct page_info * -__assign_new_domain_page(struct domain *d, unsigned long mpaddr, pte_t* pte) +__assign_new_domain_page(struct domain *d, unsigned long mpaddr, + volatile pte_t* pte) { struct page_info *p; unsigned long maddr; @@ -775,7 +797,7 @@ __assign_new_domain_page(struct domain *d, unsigned long mpaddr, pte_t* pte) struct page_info * assign_new_domain_page(struct domain *d, unsigned long mpaddr) { - pte_t *pte = __lookup_alloc_domain_pte(d, mpaddr); + volatile pte_t *pte = lookup_alloc_domain_pte(d, mpaddr); if (!pte_none(*pte)) return NULL; @@ -786,10 +808,10 @@ assign_new_domain_page(struct domain *d, unsigned long mpaddr) void assign_new_domain0_page(struct domain *d, unsigned long mpaddr) { - pte_t *pte; + volatile pte_t *pte; BUG_ON(d != dom0); - pte = __lookup_alloc_domain_pte(d, mpaddr); + pte = lookup_alloc_domain_pte(d, mpaddr); if (pte_none(*pte)) { struct page_info *p = __assign_new_domain_page(d, mpaddr, pte); if (p == NULL) { @@ -1239,12 +1261,14 @@ dom0vp_zap_physmap(struct domain *d, unsigned long gpfn, return 0; } -unsigned long -dom0vp_add_physmap(struct domain* d, unsigned long gpfn, unsigned long mfn, - unsigned long flags, domid_t domid) +static unsigned long +__dom0vp_add_physmap(struct domain* d, unsigned long gpfn, + unsigned long mfn_or_gmfn, + unsigned long flags, domid_t domid, int is_gmfn) { - int error = 0; + int error = -EINVAL; struct domain* rd; + unsigned long mfn; /* Not allowed by a domain. */ if (flags & (ASSIGN_nocache | ASSIGN_pgc_allocated)) @@ -1261,22 +1285,28 @@ dom0vp_add_physmap(struct domain* d, unsigned long gpfn, unsigned long mfn, break; default: gdprintk(XENLOG_INFO, "d 0x%p domid %d " - "pgfn 0x%lx mfn 0x%lx flags 0x%lx domid %d\n", - d, d->domain_id, gpfn, mfn, flags, domid); + "pgfn 0x%lx mfn_or_gmfn 0x%lx flags 0x%lx domid %d\n", + d, d->domain_id, gpfn, mfn_or_gmfn, flags, domid); return -ESRCH; } BUG_ON(rd == NULL); get_knownalive_domain(rd); } - if (unlikely(rd == d || !mfn_valid(mfn))) { - error = -EINVAL; + if (unlikely(rd == d)) goto out1; - } - if (unlikely(get_page(mfn_to_page(mfn), rd) == 0)) { - error = -EINVAL; + /* + * DOMID_XEN and DOMID_IO don't have their own p2m table. + * It can be considered that their p2m conversion is p==m. + */ + if (likely(is_gmfn && domid != DOMID_XEN && domid != DOMID_IO)) + mfn = gmfn_to_mfn(rd, mfn_or_gmfn); + else + mfn = mfn_or_gmfn; + if (unlikely(!mfn_valid(mfn) || get_page(mfn_to_page(mfn), rd) == 0)) goto out1; - } + + error = 0; BUG_ON(page_get_owner(mfn_to_page(mfn)) == d && get_gpfn_from_mfn(mfn) != INVALID_M2P_ENTRY); assign_domain_page_replace(d, gpfn << PAGE_SHIFT, mfn, flags); @@ -1287,6 +1317,21 @@ out1: return error; } +unsigned long +dom0vp_add_physmap(struct domain* d, unsigned long gpfn, unsigned long mfn, + unsigned long flags, domid_t domid) +{ + return __dom0vp_add_physmap(d, gpfn, mfn, flags, domid, 0); +} + +unsigned long +dom0vp_add_physmap_with_gmfn(struct domain* d, unsigned long gpfn, + unsigned long gmfn, unsigned long flags, + domid_t domid) +{ + return __dom0vp_add_physmap(d, gpfn, gmfn, flags, domid, 1); +} + #ifdef CONFIG_XEN_IA64_EXPOSE_P2M static struct page_info* p2m_pte_zero_page = NULL; @@ -1308,7 +1353,7 @@ expose_p2m_page(struct domain* d, unsigned long mpaddr, struct page_info* page) // pte page is allocated form xen heap.(see pte_alloc_one_kernel().) // so that the page has NULL page owner and it's reference count // is useless. - // see also relinquish_pte()'s page_get_owner() == NULL check. + // see also mm_teardown_pte()'s page_get_owner() == NULL check. BUG_ON(page_get_owner(page) != NULL); return __assign_domain_page(d, mpaddr, page_to_maddr(page), @@ -1766,7 +1811,7 @@ domain_page_mapped(struct domain* d, unsigned long mpaddr) void domain_cache_flush (struct domain *d, int sync_only) { struct mm_struct *mm = &d->arch.mm; - pgd_t *pgd = mm->pgd; + volatile pgd_t *pgd = mm->pgd; unsigned long maddr; int i,j,k, l; int nbr_page = 0; @@ -1779,22 +1824,22 @@ void domain_cache_flush (struct domain *d, int sync_only) flush_func = &flush_dcache_range; for (i = 0; i < PTRS_PER_PGD; pgd++, i++) { - pud_t *pud; - if (!pgd_present(*pgd)) + volatile pud_t *pud; + if (!pgd_present(*pgd)) // acquire semantics continue; pud = pud_offset(pgd, 0); for (j = 0; j < PTRS_PER_PUD; pud++, j++) { - pmd_t *pmd; - if (!pud_present(*pud)) + volatile pmd_t *pmd; + if (!pud_present(*pud)) // acquire semantics continue; pmd = pmd_offset(pud, 0); for (k = 0; k < PTRS_PER_PMD; pmd++, k++) { - pte_t *pte; - if (!pmd_present(*pmd)) + volatile pte_t *pte; + if (!pmd_present(*pmd)) // acquire semantics continue; pte = pte_offset_map(pmd, 0); for (l = 0; l < PTRS_PER_PTE; pte++, l++) { - if (!pte_present(*pte)) + if (!pte_present(*pte)) // acquire semantics continue; /* Convert PTE to maddr. */ maddr = __va_ul (pte_val(*pte) diff --git a/xen/arch/ia64/xen/mm_init.c b/xen/arch/ia64/xen/mm_init.c index f9a44a43de..cecb879551 100644 --- a/xen/arch/ia64/xen/mm_init.c +++ b/xen/arch/ia64/xen/mm_init.c @@ -10,6 +10,11 @@ #include #include +#include +#include +#include + +struct ia64_mca_tlb_info ia64_mca_tlb_list[NR_CPUS]; extern void ia64_tlb_init (void); @@ -93,11 +98,13 @@ ia64_mmu_init (void *my_cpu_data) cpu = smp_processor_id(); -#ifndef XEN /* mca handler uses cr.lid as key to pick the right entry */ ia64_mca_tlb_list[cpu].cr_lid = ia64_getreg(_IA64_REG_CR_LID); /* insert this percpu data information into our list for MCA recovery purposes */ +#ifdef XEN + ia64_mca_tlb_list[cpu].percpu_paddr = __pa(my_cpu_data); +#else ia64_mca_tlb_list[cpu].percpu_paddr = pte_val(mk_pte_phys(__pa(my_cpu_data), PAGE_KERNEL)); /* Also save per-cpu tlb flush recipe for use in physical mode mca handler */ ia64_mca_tlb_list[cpu].ptce_base = local_cpu_data->ptce_base; diff --git a/xen/arch/ia64/xen/oprofile/Makefile b/xen/arch/ia64/xen/oprofile/Makefile new file mode 100644 index 0000000000..826b99b756 --- /dev/null +++ b/xen/arch/ia64/xen/oprofile/Makefile @@ -0,0 +1 @@ +obj-y += perfmon.o xenoprof.o diff --git a/xen/arch/ia64/xen/oprofile/perfmon.c b/xen/arch/ia64/xen/oprofile/perfmon.c new file mode 100644 index 0000000000..3a26a04dd9 --- /dev/null +++ b/xen/arch/ia64/xen/oprofile/perfmon.c @@ -0,0 +1,200 @@ +/****************************************************************************** + * perfmon.c for xenoprof + * This is based linux/arch/ia64/oprofile/perfmon.c, but heavily rewritten. + * + * Copyright (c) 2006 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +/** + * @file perfmon.c + * + * @remark Copyright 2003 OProfile authors + * @remark Read the file COPYING + * + * @author John Levon + */ + +#include +#include +#include +#include +#include +#include + +// XXX move them to an appropriate header file +extern void xenoprof_log_event(struct vcpu *vcpu, + unsigned long eip, int mode, int event); +extern int is_active(struct domain *d); + +static int allow_virq; +static int allow_ints; + +static int +xenoprof_handler(struct task_struct *task, void *buf, pfm_ovfl_arg_t *arg, + struct pt_regs *regs, unsigned long stamp) +{ + unsigned long ip = regs->cr_iip; + int event = arg->pmd_eventid; + + arg->ovfl_ctrl.bits.reset_ovfl_pmds = 1; + if (!allow_virq || !allow_ints) + return 0; + + xenoprof_log_event(current, ip, xenoprofile_get_mode(task, regs), event); + + // send VIRQ_XENOPROF + if (is_active(current->domain) && !ring_0(regs)) + send_guest_vcpu_virq(current, VIRQ_XENOPROF); + + return 0; +} + +// same as linux OPROFILE_FMT_UUID +#define XENOPROF_FMT_UUID { \ + 0x77, 0x7a, 0x6e, 0x61, 0x20, 0x65, 0x73, 0x69, 0x74, 0x6e, 0x72, 0x20, 0x61, 0x65, 0x0a, 0x6c } + +static pfm_buffer_fmt_t xenoprof_fmt = { + .fmt_name = "xenoprof_format", + .fmt_uuid = XENOPROF_FMT_UUID, + .fmt_handler = xenoprof_handler, +}; + +static char * get_cpu_type(void) +{ + __u8 family = local_cpu_data->family; + + switch (family) { + case 0x07: + return "ia64/itanium"; + case 0x1f: + return "ia64/itanium2"; + default: + return "ia64/ia64"; + } +} + +static int using_xenoprof; + +int __init +xenprof_perfmon_init(void) +{ + int ret = pfm_register_buffer_fmt(&xenoprof_fmt); + if (ret) + return -ENODEV; + using_xenoprof = 1; + printk("xenoprof: using perfmon.\n"); + return 0; +} +__initcall(xenprof_perfmon_init); + +#ifdef notyet +void xenoprof_perfmon_exit(void) +{ + if (!using_xenoprof) + return; + + pfm_unregister_buffer_fmt(xenoprof_fmt.fmt_uuid); +} +__exitcall(xenoprof_perfmon_exit); +#endif + +/////////////////////////////////////////////////////////////////////////// +// glue methods for xenoprof and perfmon. +int +xenoprof_arch_init(int *num_events, int *is_primary, char *cpu_type) +{ + *num_events = 0; + strncpy(cpu_type, get_cpu_type(), XENOPROF_CPU_TYPE_SIZE - 1); + cpu_type[XENOPROF_CPU_TYPE_SIZE - 1] = '\0'; + + *is_primary = 0; + if (xenoprof_primary_profiler == NULL) { + /* For now, only dom0 can be the primary profiler */ + if (current->domain->domain_id == 0) { + *is_primary = 1; + } + } else if (xenoprof_primary_profiler == current->domain) + *is_primary = 1; + return 0; +} + +int +xenoprof_arch_reserve_counters(void) +{ + // perfmon takes care + return 0; +} + +int +xenoprof_arch_counter(XEN_GUEST_HANDLE(void) arg) +{ + return -ENOSYS; +} + +int +xenoprof_arch_setup_events(void) +{ + // perfmon takes care + return 0; +} + +//XXX SMP: sync by IPI? +int +xenoprof_arch_enable_virq(void) +{ + allow_virq = 1; + return 0; +} + +//XXX SMP: sync by IPI? +int +xenoprof_arch_start(void) +{ + allow_ints = 1; + return 0; +} + +//XXX SMP: sync by IPI? +void +xenoprof_arch_stop(void) +{ + allow_ints = 0; +} + +//XXX SMP: sync by IPI? +void +xenoprof_arch_disable_virq(void) +{ + allow_virq = 0; +} + +void +xenoprof_arch_release_counters(void) +{ + // perfmon takes care +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/arch/ia64/xen/oprofile/xenoprof.c b/xen/arch/ia64/xen/oprofile/xenoprof.c new file mode 100644 index 0000000000..a1dd5128d4 --- /dev/null +++ b/xen/arch/ia64/xen/oprofile/xenoprof.c @@ -0,0 +1,56 @@ +/****************************************************************************** + * xenoprof.c + * + * Copyright (c) 2006 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include + +int +xenoprofile_get_mode(struct vcpu *v, struct cpu_user_regs * const regs) +{ + int mode = 0; + + // mode + // 0: user, 1: kernel, 2: xen + // Xen/IA64 uses ring2 for kernel, and doesn't use ring1. + if (ring_2(regs)) + mode = 1; + else if (ring_0(regs)) + mode = 2; + else if (ring_1(regs)) { + gdprintk(XENLOG_ERR, "%s:%d ring1 is used!\n", __func__, __LINE__); + mode = 1;// fall back to kernel mode. + } + + return mode; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/arch/ia64/xen/vcpu.c b/xen/arch/ia64/xen/vcpu.c index bc5784fff0..119fb54b20 100644 --- a/xen/arch/ia64/xen/vcpu.c +++ b/xen/arch/ia64/xen/vcpu.c @@ -46,8 +46,6 @@ typedef union { // this def for vcpu_regs won't work if kernel stack is present //#define vcpu_regs(vcpu) ((struct pt_regs *) vcpu->arch.regs -#define TRUE 1 -#define FALSE 0 #define IA64_PTA_SZ_BIT 2 #define IA64_PTA_VF_BIT 8 #define IA64_PTA_BASE_BIT 15 diff --git a/xen/arch/ia64/xen/xenmem.c b/xen/arch/ia64/xen/xenmem.c index c837f0fb4b..8329ecfd6b 100644 --- a/xen/arch/ia64/xen/xenmem.c +++ b/xen/arch/ia64/xen/xenmem.c @@ -181,6 +181,23 @@ void init_virtual_frametable(void) printk("virtual machine to physical table: %p size: %lukB\n" "max_page: 0x%lx\n", mpt_table, ((table_size << PAGE_SHIFT) >> 10), max_page); + + /* + * XXX work around for translate_domain_pte(). + * It returns mfn=0 when the machine page isn't present. This + * behavior is a work around for memory mapped I/O where no device + * is assigned. Xen might access page_info of mfn=0, so it must + * be guaranteed that it exists. Otherwise xen panics with tlb miss + * fault in xen's virtual address area. + * + * Once translate_domain_pte() is fixed correctly, this will + * be removed. + */ + if (!mfn_valid(0)) { + printk("allocating frame table/mpt table at mfn 0.\n"); + create_frametable_page_table(0, PAGE_SIZE, NULL); + create_mpttable_page_table(0, PAGE_SIZE, NULL); + } } int diff --git a/xen/arch/powerpc/Makefile b/xen/arch/powerpc/Makefile index 65ee7c2aec..191b274089 100644 --- a/xen/arch/powerpc/Makefile +++ b/xen/arch/powerpc/Makefile @@ -42,6 +42,8 @@ obj-y += smp-tbsync.o obj-y += sysctl.o obj-y += time.o obj-y += usercopy.o +obj-y += machine_kexec.o +obj-y += crash.o obj-$(debug) += 0opt.o obj-$(crash_debug) += gdbstub.o diff --git a/xen/arch/powerpc/crash.c b/xen/arch/powerpc/crash.c new file mode 100644 index 0000000000..f1729786ea --- /dev/null +++ b/xen/arch/powerpc/crash.c @@ -0,0 +1,19 @@ +#include /* for printk() used in stub */ +#include +#include + +void machine_crash_shutdown(void) +{ + printk("STUB: " __FILE__ ": %s: not implemented\n", __FUNCTION__); +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ + diff --git a/xen/arch/powerpc/machine_kexec.c b/xen/arch/powerpc/machine_kexec.c new file mode 100644 index 0000000000..df7ad0c9b6 --- /dev/null +++ b/xen/arch/powerpc/machine_kexec.c @@ -0,0 +1,34 @@ +#include /* for printk() used in stubs */ +#include +#include + +int machine_kexec_load(int type, int slot, xen_kexec_image_t *image) +{ + printk("STUB: " __FILE__ ": %s: not implemented\n", __FUNCTION__); + return -1; +} + +void machine_kexec_unload(int type, int slot, xen_kexec_image_t *image) +{ + printk("STUB: " __FILE__ ": %s: not implemented\n", __FUNCTION__); +} + +void machine_kexec(xen_kexec_image_t *image) +{ + printk("STUB: " __FILE__ ": %s: not implemented\n", __FUNCTION__); +} + +void machine_reboot_kexec(xen_kexec_image_t *image) +{ + printk("STUB: " __FILE__ ": %s: not implemented\n", __FUNCTION__); +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile index 89cc508d02..c8cb3af177 100644 --- a/xen/arch/x86/Makefile +++ b/xen/arch/x86/Makefile @@ -43,6 +43,8 @@ obj-y += trampoline.o obj-y += traps.o obj-y += usercopy.o obj-y += x86_emulate.o +obj-y += machine_kexec.o +obj-y += crash.o obj-$(crash_debug) += gdbstub.o diff --git a/xen/arch/x86/boot/x86_32.S b/xen/arch/x86/boot/x86_32.S index e0f2e09643..c3466dee5f 100644 --- a/xen/arch/x86/boot/x86_32.S +++ b/xen/arch/x86/boot/x86_32.S @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -17,12 +18,14 @@ ENTRY(_stext) .align 4 /*** MULTIBOOT HEADER ****/ +#define MULTIBOOT_HEADER_FLAGS (MULTIBOOT_HEADER_MODS_ALIGNED | \ + MULTIBOOT_HEADER_WANT_MEMORY) /* Magic number indicating a Multiboot header. */ - .long 0x1BADB002 + .long MULTIBOOT_HEADER_MAGIC /* Flags to bootloader (see Multiboot spec). */ - .long 0x00000003 + .long MULTIBOOT_HEADER_FLAGS /* Checksum: must be the negated sum of the first two fields. */ - .long -0x1BADB005 + .long -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS) not_multiboot_msg: .asciz "ERR: Not a Multiboot bootloader!" diff --git a/xen/arch/x86/boot/x86_64.S b/xen/arch/x86/boot/x86_64.S index 5a8a111b59..ce0e42987c 100644 --- a/xen/arch/x86/boot/x86_64.S +++ b/xen/arch/x86/boot/x86_64.S @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -19,12 +20,14 @@ ENTRY(_stext) .org 0x004 /*** MULTIBOOT HEADER ****/ +#define MULTIBOOT_HEADER_FLAGS (MULTIBOOT_HEADER_MODS_ALIGNED | \ + MULTIBOOT_HEADER_WANT_MEMORY) /* Magic number indicating a Multiboot header. */ - .long 0x1BADB002 + .long MULTIBOOT_HEADER_MAGIC /* Flags to bootloader (see Multiboot spec). */ - .long 0x00000003 + .long MULTIBOOT_HEADER_FLAGS /* Checksum: must be the negated sum of the first two fields. */ - .long -0x1BADB005 + .long -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS) .Lbad_cpu_msg: .asciz "ERR: Not a 64-bit CPU!" .Lbad_ldr_msg: .asciz "ERR: Not a Multiboot bootloader!" diff --git a/xen/arch/x86/crash.c b/xen/arch/x86/crash.c new file mode 100644 index 0000000000..a5b0aeedf1 --- /dev/null +++ b/xen/arch/x86/crash.c @@ -0,0 +1,120 @@ +/****************************************************************************** + * crash.c + * + * Based heavily on arch/i386/kernel/crash.c from Linux 2.6.16 + * + * Xen port written by: + * - Simon 'Horms' Horman + * - Magnus Damm + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static atomic_t waiting_for_crash_ipi; + +static int crash_nmi_callback(struct cpu_user_regs *regs, int cpu) +{ + /* Don't do anything if this handler is invoked on crashing cpu. + * Otherwise, system will completely hang. Crashing cpu can get + * an NMI if system was initially booted with nmi_watchdog parameter. + */ + if ( cpu == crashing_cpu ) + return 1; + local_irq_disable(); + + machine_crash_save_cpu(); + disable_local_APIC(); + atomic_dec(&waiting_for_crash_ipi); + hvm_disable(); + + for ( ; ; ) + __asm__ __volatile__ ( "hlt" ); + + return 1; +} + +/* + * By using the NMI code instead of a vector we just sneak thru the + * word generator coming out with just what we want. AND it does + * not matter if clustered_apic_mode is set or not. + */ +static void smp_send_nmi_allbutself(void) +{ + cpumask_t allbutself = cpu_online_map; + + cpu_clear(smp_processor_id(), allbutself); + send_IPI_mask(allbutself, APIC_DM_NMI); +} + +static void nmi_shootdown_cpus(void) +{ + unsigned long msecs; + + atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1); + /* Would it be better to replace the trap vector here? */ + set_nmi_callback(crash_nmi_callback); + /* Ensure the new callback function is set before sending out the NMI. */ + wmb(); + + smp_send_nmi_allbutself(); + + msecs = 1000; /* Wait at most a second for the other cpus to stop */ + while ( (atomic_read(&waiting_for_crash_ipi) > 0) && msecs ) + { + mdelay(1); + msecs--; + } + + /* Leave the nmi callback set */ + disable_local_APIC(); +} + +static void crash_save_xen_notes(void) +{ + crash_xen_info_t *info; + + info = machine_crash_save_info(); + + info->dom0_pfn_to_mfn_frame_list_list = \ + dom0->shared_info->arch.pfn_to_mfn_frame_list_list; +} + +void machine_crash_shutdown(void) +{ + local_irq_disable(); + + nmi_shootdown_cpus(); + + disable_IO_APIC(); + + hvm_disable(); + + crash_save_xen_notes(); +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c index 75d5284aeb..9171b394ae 100644 --- a/xen/arch/x86/domain.c +++ b/xen/arch/x86/domain.c @@ -134,6 +134,8 @@ int vcpu_initialise(struct vcpu *v) v->arch.flags = TF_kernel_mode; + pae_l3_cache_init(&v->arch.pae_l3_cache); + if ( is_hvm_domain(d) ) { if ( (rc = hvm_vcpu_initialise(v)) != 0 ) @@ -155,8 +157,6 @@ int vcpu_initialise(struct vcpu *v) v->arch.perdomain_ptes = d->arch.mm_perdomain_pt + (v->vcpu_id << GDT_LDT_VCPU_SHIFT); - pae_l3_cache_init(&v->arch.pae_l3_cache); - return 0; } diff --git a/xen/arch/x86/hvm/platform.c b/xen/arch/x86/hvm/platform.c index 46eab76175..a822fc093f 100644 --- a/xen/arch/x86/hvm/platform.c +++ b/xen/arch/x86/hvm/platform.c @@ -920,7 +920,7 @@ void handle_mmio(unsigned long gpa) df = regs->eflags & X86_EFLAGS_DF ? 1 : 0; mode = hvm_guest_x86_mode(v); - inst_addr = hvm_get_segment_base(v, seg_cs) + regs->eip; + inst_addr = hvm_get_segment_base(v, x86_seg_cs) + regs->eip; inst_len = hvm_instruction_length(inst_addr, mode); if ( inst_len <= 0 ) { @@ -964,10 +964,10 @@ void handle_mmio(unsigned long gpa) addr = regs->edi; if ( ad_size == WORD ) addr &= 0xFFFF; - addr += hvm_get_segment_base(v, seg_es); + addr += hvm_get_segment_base(v, x86_seg_es); if ( addr == gpa ) { - enum segment seg; + enum x86_segment seg; dir = IOREQ_WRITE; addr = regs->esi; @@ -975,13 +975,13 @@ void handle_mmio(unsigned long gpa) addr &= 0xFFFF; switch ( seg_sel ) { - case 0x26: seg = seg_es; break; - case 0x2e: seg = seg_cs; break; - case 0x36: seg = seg_ss; break; + case 0x26: seg = x86_seg_es; break; + case 0x2e: seg = x86_seg_cs; break; + case 0x36: seg = x86_seg_ss; break; case 0: - case 0x3e: seg = seg_ds; break; - case 0x64: seg = seg_fs; break; - case 0x65: seg = seg_gs; break; + case 0x3e: seg = x86_seg_ds; break; + case 0x64: seg = x86_seg_fs; break; + case 0x65: seg = x86_seg_gs; break; default: domain_crash_synchronous(); } addr += hvm_get_segment_base(v, seg); diff --git a/xen/arch/x86/hvm/svm/emulate.c b/xen/arch/x86/hvm/svm/emulate.c index 504bafe7db..7966bb9da2 100644 --- a/xen/arch/x86/hvm/svm/emulate.c +++ b/xen/arch/x86/hvm/svm/emulate.c @@ -128,17 +128,6 @@ static inline unsigned long DECODE_GPR_VALUE(struct vmcb_struct *vmcb, return (unsigned long) -1; \ } -#if 0 -/* - * hv_is_canonical - checks if the given address is canonical - */ -static inline u64 hv_is_canonical(u64 addr) -{ - u64 bits = addr & (u64)0xffff800000000000; - return (u64)((bits == (u64)0xffff800000000000) || (bits == (u64)0x0)); -} -#endif - #define modrm operand [0] #define sib operand [1] @@ -209,7 +198,7 @@ unsigned long get_effective_addr_modrm64(struct vmcb_struct *vmcb, #if __x86_64__ /* 64-bit mode */ - if (vmcb->cs.attributes.fields.l && (vmcb->efer & EFER_LMA)) + if (vmcb->cs.attr.fields.l && (vmcb->efer & EFER_LMA)) return vmcb->rip + inst_len + *size + disp; #endif return disp; @@ -334,7 +323,7 @@ unsigned long svm_rip2pointer(struct vmcb_struct *vmcb) * no matter what kind of addressing is used. */ unsigned long p = vmcb->cs.base + vmcb->rip; - if (!(vmcb->cs.attributes.fields.l && vmcb->efer & EFER_LMA)) + if (!(vmcb->cs.attr.fields.l && vmcb->efer & EFER_LMA)) return (u32)p; /* mask to 32 bits */ /* NB. Should mask to 16 bits if in real mode or 16-bit protected mode. */ return p; diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c index a9cc9e9325..5d19ae536c 100644 --- a/xen/arch/x86/hvm/svm/svm.c +++ b/xen/arch/x86/hvm/svm/svm.c @@ -269,13 +269,11 @@ static int svm_long_mode_enabled(struct vcpu *v) return test_bit(SVM_CPU_STATE_LMA_ENABLED, &v->arch.hvm_svm.cpu_state); } -#define IS_CANO_ADDRESS(add) 1 - static inline int long_mode_do_msr_read(struct cpu_user_regs *regs) { u64 msr_content = 0; - struct vcpu *vc = current; - struct vmcb_struct *vmcb = vc->arch.hvm_svm.vmcb; + struct vcpu *v = current; + struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; switch ((u32)regs->ecx) { @@ -284,17 +282,25 @@ static inline int long_mode_do_msr_read(struct cpu_user_regs *regs) msr_content &= ~EFER_SVME; break; +#ifdef __x86_64__ case MSR_FS_BASE: msr_content = vmcb->fs.base; - break; + goto check_long_mode; case MSR_GS_BASE: msr_content = vmcb->gs.base; - break; + goto check_long_mode; case MSR_SHADOW_GS_BASE: msr_content = vmcb->kerngsbase; + check_long_mode: + if ( !svm_long_mode_enabled(v) ) + { + svm_inject_exception(v, TRAP_gp_fault, 1, 0); + return 0; + } break; +#endif case MSR_STAR: msr_content = vmcb->star; @@ -326,25 +332,25 @@ static inline int long_mode_do_msr_read(struct cpu_user_regs *regs) static inline int long_mode_do_msr_write(struct cpu_user_regs *regs) { u64 msr_content = (u32)regs->eax | ((u64)regs->edx << 32); + u32 ecx = regs->ecx; struct vcpu *v = current; struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; HVM_DBG_LOG(DBG_LEVEL_1, "msr %x msr_content %"PRIx64"\n", - (u32)regs->ecx, msr_content); + ecx, msr_content); - switch ( (u32)regs->ecx ) + switch ( ecx ) { case MSR_EFER: -#ifdef __x86_64__ /* offending reserved bit will cause #GP */ if ( msr_content & ~(EFER_LME | EFER_LMA | EFER_NX | EFER_SCE) ) { - printk("Trying to set reserved bit in EFER: %"PRIx64"\n", - msr_content); - svm_inject_exception(v, TRAP_gp_fault, 1, 0); - return 0; + gdprintk(XENLOG_WARNING, "Trying to set reserved bit in " + "EFER: %"PRIx64"\n", msr_content); + goto gp_fault; } +#ifdef __x86_64__ /* LME: 0 -> 1 */ if ( msr_content & EFER_LME && !test_bit(SVM_CPU_STATE_LME_ENABLED, &v->arch.hvm_svm.cpu_state)) @@ -353,10 +359,9 @@ static inline int long_mode_do_msr_write(struct cpu_user_regs *regs) !test_bit(SVM_CPU_STATE_PAE_ENABLED, &v->arch.hvm_svm.cpu_state) ) { - printk("Trying to set LME bit when " - "in paging mode or PAE bit is not set\n"); - svm_inject_exception(v, TRAP_gp_fault, 1, 0); - return 0; + gdprintk(XENLOG_WARNING, "Trying to set LME bit when " + "in paging mode or PAE bit is not set\n"); + goto gp_fault; } set_bit(SVM_CPU_STATE_LME_ENABLED, &v->arch.hvm_svm.cpu_state); } @@ -371,37 +376,38 @@ static inline int long_mode_do_msr_write(struct cpu_user_regs *regs) vmcb->efer = msr_content | EFER_SVME; break; +#ifdef __x86_64__ case MSR_FS_BASE: case MSR_GS_BASE: - if ( !svm_long_mode_enabled(v) ) - goto exit_and_crash; - - if (!IS_CANO_ADDRESS(msr_content)) - { - HVM_DBG_LOG(DBG_LEVEL_1, "Not cano address of msr write\n"); - svm_inject_exception(v, TRAP_gp_fault, 1, 0); - } - - if (regs->ecx == MSR_FS_BASE) - vmcb->fs.base = msr_content; - else - vmcb->gs.base = msr_content; - break; - case MSR_SHADOW_GS_BASE: - vmcb->kerngsbase = msr_content; + if ( !svm_long_mode_enabled(v) ) + goto gp_fault; + + if ( !is_canonical_address(msr_content) ) + goto uncanonical_address; + + if ( ecx == MSR_FS_BASE ) + vmcb->fs.base = msr_content; + else if ( ecx == MSR_GS_BASE ) + vmcb->gs.base = msr_content; + else + vmcb->kerngsbase = msr_content; break; +#endif case MSR_STAR: vmcb->star = msr_content; break; case MSR_LSTAR: - vmcb->lstar = msr_content; - break; - case MSR_CSTAR: - vmcb->cstar = msr_content; + if ( !is_canonical_address(msr_content) ) + goto uncanonical_address; + + if ( ecx == MSR_LSTAR ) + vmcb->lstar = msr_content; + else + vmcb->cstar = msr_content; break; case MSR_SYSCALL_MASK: @@ -414,10 +420,11 @@ static inline int long_mode_do_msr_write(struct cpu_user_regs *regs) return 1; - exit_and_crash: - gdprintk(XENLOG_ERR, "Fatal error writing MSR %lx\n", (long)regs->ecx); - domain_crash(v->domain); - return 1; /* handled */ + uncanonical_address: + HVM_DBG_LOG(DBG_LEVEL_1, "Not cano address of msr write %x\n", ecx); + gp_fault: + svm_inject_exception(v, TRAP_gp_fault, 1, 0); + return 0; } @@ -476,13 +483,13 @@ static int svm_guest_x86_mode(struct vcpu *v) struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; if ( vmcb->efer & EFER_LMA ) - return (vmcb->cs.attributes.fields.l ? + return (vmcb->cs.attr.fields.l ? X86EMUL_MODE_PROT64 : X86EMUL_MODE_PROT32); if ( svm_realmode(v) ) return X86EMUL_MODE_REAL; - return (vmcb->cs.attributes.fields.db ? + return (vmcb->cs.attr.fields.db ? X86EMUL_MODE_PROT32 : X86EMUL_MODE_PROT16); } @@ -509,31 +516,51 @@ unsigned long svm_get_ctrl_reg(struct vcpu *v, unsigned int num) return 0; /* dummy */ } -static unsigned long svm_get_segment_base(struct vcpu *v, enum segment seg) +static unsigned long svm_get_segment_base(struct vcpu *v, enum x86_segment seg) { struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; int long_mode = 0; #ifdef __x86_64__ - long_mode = vmcb->cs.attributes.fields.l && (vmcb->efer & EFER_LMA); + long_mode = vmcb->cs.attr.fields.l && (vmcb->efer & EFER_LMA); #endif switch ( seg ) { - case seg_cs: return long_mode ? 0 : vmcb->cs.base; - case seg_ds: return long_mode ? 0 : vmcb->ds.base; - case seg_es: return long_mode ? 0 : vmcb->es.base; - case seg_fs: return vmcb->fs.base; - case seg_gs: return vmcb->gs.base; - case seg_ss: return long_mode ? 0 : vmcb->ss.base; - case seg_tr: return vmcb->tr.base; - case seg_gdtr: return vmcb->gdtr.base; - case seg_idtr: return vmcb->idtr.base; - case seg_ldtr: return vmcb->ldtr.base; + case x86_seg_cs: return long_mode ? 0 : vmcb->cs.base; + case x86_seg_ds: return long_mode ? 0 : vmcb->ds.base; + case x86_seg_es: return long_mode ? 0 : vmcb->es.base; + case x86_seg_fs: return vmcb->fs.base; + case x86_seg_gs: return vmcb->gs.base; + case x86_seg_ss: return long_mode ? 0 : vmcb->ss.base; + case x86_seg_tr: return vmcb->tr.base; + case x86_seg_gdtr: return vmcb->gdtr.base; + case x86_seg_idtr: return vmcb->idtr.base; + case x86_seg_ldtr: return vmcb->ldtr.base; } BUG(); return 0; } +static void svm_get_segment_register(struct vcpu *v, enum x86_segment seg, + struct segment_register *reg) +{ + struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; + switch ( seg ) + { + case x86_seg_cs: memcpy(reg, &vmcb->cs, sizeof(*reg)); break; + case x86_seg_ds: memcpy(reg, &vmcb->ds, sizeof(*reg)); break; + case x86_seg_es: memcpy(reg, &vmcb->es, sizeof(*reg)); break; + case x86_seg_fs: memcpy(reg, &vmcb->fs, sizeof(*reg)); break; + case x86_seg_gs: memcpy(reg, &vmcb->gs, sizeof(*reg)); break; + case x86_seg_ss: memcpy(reg, &vmcb->ss, sizeof(*reg)); break; + case x86_seg_tr: memcpy(reg, &vmcb->tr, sizeof(*reg)); break; + case x86_seg_gdtr: memcpy(reg, &vmcb->gdtr, sizeof(*reg)); break; + case x86_seg_idtr: memcpy(reg, &vmcb->idtr, sizeof(*reg)); break; + case x86_seg_ldtr: memcpy(reg, &vmcb->ldtr, sizeof(*reg)); break; + default: BUG(); + } +} + /* Make sure that xen intercepts any FP accesses from current */ static void svm_stts(struct vcpu *v) { @@ -785,6 +812,15 @@ static void svm_vcpu_destroy(struct vcpu *v) svm_destroy_vmcb(v); } +static void svm_hvm_inject_exception( + unsigned int trapnr, int errcode, unsigned long cr2) +{ + struct vcpu *v = current; + svm_inject_exception(v, trapnr, (errcode != -1), errcode); + if ( trapnr == TRAP_page_fault ) + v->arch.hvm_svm.vmcb->cr2 = v->arch.hvm_svm.cpu_cr2 = cr2; +} + int start_svm(void) { u32 eax, ecx, edx; @@ -844,12 +880,15 @@ int start_svm(void) hvm_funcs.guest_x86_mode = svm_guest_x86_mode; hvm_funcs.get_guest_ctrl_reg = svm_get_ctrl_reg; hvm_funcs.get_segment_base = svm_get_segment_base; + hvm_funcs.get_segment_register = svm_get_segment_register; hvm_funcs.update_host_cr3 = svm_update_host_cr3; hvm_funcs.stts = svm_stts; hvm_funcs.set_tsc_offset = svm_set_tsc_offset; + hvm_funcs.inject_exception = svm_hvm_inject_exception; + hvm_funcs.init_ap_context = svm_init_ap_context; hvm_funcs.init_hypercall_page = svm_init_hypercall_page; @@ -1154,7 +1193,7 @@ static void svm_dr_access(struct vcpu *v, struct cpu_user_regs *regs) static void svm_get_prefix_info( struct vmcb_struct *vmcb, - unsigned int dir, segment_selector_t **seg, unsigned int *asize) + unsigned int dir, svm_segment_register_t **seg, unsigned int *asize) { unsigned char inst[MAX_INST_LEN]; int i; @@ -1235,18 +1274,18 @@ static inline int svm_get_io_address( unsigned long reg; unsigned int asize, isize; int long_mode = 0; - segment_selector_t *seg = NULL; + svm_segment_register_t *seg = NULL; struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; #ifdef __x86_64__ /* If we're in long mode, we shouldn't check the segment presence & limit */ - long_mode = vmcb->cs.attributes.fields.l && vmcb->efer & EFER_LMA; + long_mode = vmcb->cs.attr.fields.l && vmcb->efer & EFER_LMA; #endif - /* d field of cs.attributes is 1 for 32-bit, 0 for 16 or 64 bit. - * l field combined with EFER_LMA -> longmode says whether it's 16 or 64 bit. + /* d field of cs.attr is 1 for 32-bit, 0 for 16 or 64 bit. + * l field combined with EFER_LMA says whether it's 16 or 64 bit. */ - asize = (long_mode)?64:((vmcb->cs.attributes.fields.db)?32:16); + asize = (long_mode)?64:((vmcb->cs.attr.fields.db)?32:16); /* The ins/outs instructions are single byte, so if we have got more @@ -1266,7 +1305,7 @@ static inline int svm_get_io_address( reg = regs->esi; if (!seg) /* If no prefix, used DS. */ seg = &vmcb->ds; - if (!long_mode && (seg->attributes.fields.type & 0xa) == 0x8) { + if (!long_mode && (seg->attr.fields.type & 0xa) == 0x8) { svm_inject_exception(v, TRAP_gp_fault, 1, 0); return 0; } @@ -1275,14 +1314,14 @@ static inline int svm_get_io_address( { reg = regs->edi; seg = &vmcb->es; /* Note: This is ALWAYS ES. */ - if (!long_mode && (seg->attributes.fields.type & 0xa) != 0x2) { + if (!long_mode && (seg->attr.fields.type & 0xa) != 0x2) { svm_inject_exception(v, TRAP_gp_fault, 1, 0); return 0; } } /* If the segment isn't present, give GP fault! */ - if (!long_mode && !seg->attributes.fields.p) + if (!long_mode && !seg->attr.fields.p) { svm_inject_exception(v, TRAP_gp_fault, 1, 0); return 0; @@ -1305,7 +1344,7 @@ static inline int svm_get_io_address( { ASSERT(*addr == (u32)*addr); if ((u32)(*addr + size - 1) < (u32)*addr || - (seg->attributes.fields.type & 0xc) != 0x4 ? + (seg->attr.fields.type & 0xc) != 0x4 ? *addr + size - 1 > seg->limit : *addr <= seg->limit) { @@ -1318,9 +1357,9 @@ static inline int svm_get_io_address( occur. Note that the checking is not necessary for page granular segments as transfers crossing page boundaries will be broken up anyway. */ - if (!seg->attributes.fields.g && *count > 1) + if (!seg->attr.fields.g && *count > 1) { - if ((seg->attributes.fields.type & 0xc) != 0x4) + if ((seg->attr.fields.type & 0xc) != 0x4) { /* expand-up */ if (!(regs->eflags & EF_DF)) @@ -1355,8 +1394,35 @@ static inline int svm_get_io_address( *addr += seg->base; } - else if (seg == &vmcb->fs || seg == &vmcb->gs) - *addr += seg->base; +#ifdef __x86_64__ + else + { + if (seg == &vmcb->fs || seg == &vmcb->gs) + *addr += seg->base; + + if (!is_canonical_address(*addr) || + !is_canonical_address(*addr + size - 1)) + { + svm_inject_exception(v, TRAP_gp_fault, 1, 0); + return 0; + } + if (*count > (1UL << 48) / size) + *count = (1UL << 48) / size; + if (!(regs->eflags & EF_DF)) + { + if (*addr + *count * size - 1 < *addr || + !is_canonical_address(*addr + *count * size - 1)) + *count = (*addr & ~((1UL << 48) - 1)) / size; + } + else + { + if ((*count - 1) * size > *addr || + !is_canonical_address(*addr + (*count - 1) * size)) + *count = (*addr & ~((1UL << 48) - 1)) / size + 1; + } + ASSERT(*count); + } +#endif return 1; } @@ -2154,52 +2220,52 @@ static int svm_do_vmmcall_reset_to_realmode(struct vcpu *v, /* setup the segment registers and all their hidden states */ vmcb->cs.sel = 0xF000; - vmcb->cs.attributes.bytes = 0x089b; + vmcb->cs.attr.bytes = 0x089b; vmcb->cs.limit = 0xffff; vmcb->cs.base = 0x000F0000; vmcb->ss.sel = 0x00; - vmcb->ss.attributes.bytes = 0x0893; + vmcb->ss.attr.bytes = 0x0893; vmcb->ss.limit = 0xffff; vmcb->ss.base = 0x00; vmcb->ds.sel = 0x00; - vmcb->ds.attributes.bytes = 0x0893; + vmcb->ds.attr.bytes = 0x0893; vmcb->ds.limit = 0xffff; vmcb->ds.base = 0x00; vmcb->es.sel = 0x00; - vmcb->es.attributes.bytes = 0x0893; + vmcb->es.attr.bytes = 0x0893; vmcb->es.limit = 0xffff; vmcb->es.base = 0x00; vmcb->fs.sel = 0x00; - vmcb->fs.attributes.bytes = 0x0893; + vmcb->fs.attr.bytes = 0x0893; vmcb->fs.limit = 0xffff; vmcb->fs.base = 0x00; vmcb->gs.sel = 0x00; - vmcb->gs.attributes.bytes = 0x0893; + vmcb->gs.attr.bytes = 0x0893; vmcb->gs.limit = 0xffff; vmcb->gs.base = 0x00; vmcb->ldtr.sel = 0x00; - vmcb->ldtr.attributes.bytes = 0x0000; + vmcb->ldtr.attr.bytes = 0x0000; vmcb->ldtr.limit = 0x0; vmcb->ldtr.base = 0x00; vmcb->gdtr.sel = 0x00; - vmcb->gdtr.attributes.bytes = 0x0000; + vmcb->gdtr.attr.bytes = 0x0000; vmcb->gdtr.limit = 0x0; vmcb->gdtr.base = 0x00; vmcb->tr.sel = 0; - vmcb->tr.attributes.bytes = 0; + vmcb->tr.attr.bytes = 0; vmcb->tr.limit = 0x0; vmcb->tr.base = 0; vmcb->idtr.sel = 0x00; - vmcb->idtr.attributes.bytes = 0x0000; + vmcb->idtr.attr.bytes = 0x0000; vmcb->idtr.limit = 0x3ff; vmcb->idtr.base = 0x00; diff --git a/xen/arch/x86/hvm/svm/vmcb.c b/xen/arch/x86/hvm/svm/vmcb.c index f00372440e..b32db1b62c 100644 --- a/xen/arch/x86/hvm/svm/vmcb.c +++ b/xen/arch/x86/hvm/svm/vmcb.c @@ -90,7 +90,7 @@ static int construct_vmcb(struct vcpu *v) { struct arch_svm_struct *arch_svm = &v->arch.hvm_svm; struct vmcb_struct *vmcb = arch_svm->vmcb; - segment_attributes_t attrib; + svm_segment_attributes_t attrib; /* Always flush the TLB on VMRUN. */ vmcb->tlb_control = 1; @@ -166,13 +166,13 @@ static int construct_vmcb(struct vcpu *v) attrib.fields.p = 1; /* segment present */ attrib.fields.db = 1; /* 32-bit */ attrib.fields.g = 1; /* 4K pages in limit */ - vmcb->es.attributes = attrib; - vmcb->ss.attributes = attrib; - vmcb->ds.attributes = attrib; - vmcb->fs.attributes = attrib; - vmcb->gs.attributes = attrib; + vmcb->es.attr = attrib; + vmcb->ss.attr = attrib; + vmcb->ds.attr = attrib; + vmcb->fs.attr = attrib; + vmcb->gs.attr = attrib; attrib.fields.type = 0xb; /* type=0xb -> executable/readable, accessed */ - vmcb->cs.attributes = attrib; + vmcb->cs.attr = attrib; /* Guest IDT. */ vmcb->idtr.base = 0; @@ -186,11 +186,11 @@ static int construct_vmcb(struct vcpu *v) vmcb->ldtr.sel = 0; vmcb->ldtr.base = 0; vmcb->ldtr.limit = 0; - vmcb->ldtr.attributes.bytes = 0; + vmcb->ldtr.attr.bytes = 0; /* Guest TSS. */ attrib.fields.type = 0xb; /* 32-bit TSS (busy) */ - vmcb->tr.attributes = attrib; + vmcb->tr.attr = attrib; vmcb->tr.base = 0; vmcb->tr.limit = 0xff; @@ -278,10 +278,10 @@ void svm_do_launch(struct vcpu *v) v->arch.schedule_tail = arch_svm_do_resume; } -static void svm_dump_sel(char *name, segment_selector_t *s) +static void svm_dump_sel(char *name, svm_segment_register_t *s) { printk("%s: sel=0x%04x, attr=0x%04x, limit=0x%08x, base=0x%016llx\n", - name, s->sel, s->attributes.bytes, s->limit, + name, s->sel, s->attr.bytes, s->limit, (unsigned long long)s->base); } diff --git a/xen/arch/x86/hvm/vlapic.c b/xen/arch/x86/hvm/vlapic.c index 1c6bef005e..a0304deb37 100644 --- a/xen/arch/x86/hvm/vlapic.c +++ b/xen/arch/x86/hvm/vlapic.c @@ -119,19 +119,16 @@ static int vlapic_find_highest_vector(u32 *bitmap) static int vlapic_test_and_set_irr(int vector, struct vlapic *vlapic) { - vlapic->flush_tpr_threshold = 1; return vlapic_test_and_set_vector(vector, vlapic->regs + APIC_IRR); } static void vlapic_set_irr(int vector, struct vlapic *vlapic) { - vlapic->flush_tpr_threshold = 1; vlapic_set_vector(vector, vlapic->regs + APIC_IRR); } static void vlapic_clear_irr(int vector, struct vlapic *vlapic) { - vlapic->flush_tpr_threshold = 1; vlapic_clear_vector(vector, vlapic->regs + APIC_IRR); } @@ -634,7 +631,6 @@ static void vlapic_write(struct vcpu *v, unsigned long address, { case APIC_TASKPRI: vlapic_set_reg(vlapic, APIC_TASKPRI, val & 0xff); - vlapic->flush_tpr_threshold = 1; break; case APIC_EOI: @@ -667,10 +663,7 @@ static void vlapic_write(struct vcpu *v, unsigned long address, } } else - { vlapic->disabled &= ~VLAPIC_SW_DISABLED; - vlapic->flush_tpr_threshold = 1; - } break; case APIC_ESR: @@ -730,7 +723,7 @@ static void vlapic_write(struct vcpu *v, unsigned long address, break; default: - gdprintk(XENLOG_WARNING, + gdprintk(XENLOG_DEBUG, "Local APIC Write to read-only register 0x%x\n", offset); break; } @@ -925,8 +918,6 @@ static int vlapic_reset(struct vlapic *vlapic) vlapic_set_reg(vlapic, APIC_SPIV, 0xff); vlapic->disabled |= VLAPIC_SW_DISABLED; - vlapic->flush_tpr_threshold = 1; - return 1; } diff --git a/xen/arch/x86/hvm/vmx/Makefile b/xen/arch/x86/hvm/vmx/Makefile index 3eede43031..ba31793c2f 100644 --- a/xen/arch/x86/hvm/vmx/Makefile +++ b/xen/arch/x86/hvm/vmx/Makefile @@ -1,6 +1,6 @@ subdir-$(x86_32) += x86_32 subdir-$(x86_64) += x86_64 -obj-y += io.o +obj-y += intr.o obj-y += vmcs.o obj-y += vmx.o diff --git a/xen/arch/x86/hvm/vmx/io.c b/xen/arch/x86/hvm/vmx/intr.c similarity index 95% rename from xen/arch/x86/hvm/vmx/io.c rename to xen/arch/x86/hvm/vmx/intr.c index 2f3cf623b1..662a6697a0 100644 --- a/xen/arch/x86/hvm/vmx/io.c +++ b/xen/arch/x86/hvm/vmx/intr.c @@ -71,10 +71,6 @@ static void update_tpr_threshold(struct vlapic *vlapic) { int max_irr, tpr; - /* Clear the work-to-do flag /then/ do the work. */ - vlapic->flush_tpr_threshold = 0; - mb(); - if ( !vlapic_enabled(vlapic) || ((max_irr = vlapic_find_highest_irr(vlapic)) == -1) ) { @@ -95,7 +91,6 @@ asmlinkage void vmx_intr_assist(void) int highest_vector; unsigned long eflags; struct vcpu *v = current; - struct vlapic *vlapic = vcpu_vlapic(v); struct hvm_domain *plat=&v->domain->arch.hvm_domain; struct periodic_time *pt = &plat->pl_time.periodic_tm; unsigned int idtv_info_field; @@ -110,8 +105,7 @@ asmlinkage void vmx_intr_assist(void) hvm_set_callback_irq_level(); - if ( vlapic->flush_tpr_threshold ) - update_tpr_threshold(vlapic); + update_tpr_threshold(vcpu_vlapic(v)); has_ext_irq = cpu_has_pending_irq(v); diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c index 2925c06087..aa6e364f6b 100644 --- a/xen/arch/x86/hvm/vmx/vmx.c +++ b/xen/arch/x86/hvm/vmx/vmx.c @@ -95,13 +95,7 @@ static void vmx_save_host_msrs(void) rdmsrl(msr_index[i], host_msr_state->msrs[i]); } -#define CASE_READ_MSR(address) \ - case MSR_ ## address: \ - msr_content = guest_msr_state->msrs[VMX_INDEX_MSR_ ## address]; \ - break - -#define CASE_WRITE_MSR(address) \ - case MSR_ ## address: \ +#define WRITE_MSR(address) \ guest_msr_state->msrs[VMX_INDEX_MSR_ ## address] = msr_content; \ if ( !test_bit(VMX_INDEX_MSR_ ## address, &guest_msr_state->flags) )\ set_bit(VMX_INDEX_MSR_ ## address, &guest_msr_state->flags); \ @@ -109,7 +103,6 @@ static void vmx_save_host_msrs(void) set_bit(VMX_INDEX_MSR_ ## address, &host_msr_state->flags); \ break -#define IS_CANO_ADDRESS(add) 1 static inline int long_mode_do_msr_read(struct cpu_user_regs *regs) { u64 msr_content = 0; @@ -123,27 +116,38 @@ static inline int long_mode_do_msr_read(struct cpu_user_regs *regs) break; case MSR_FS_BASE: - if ( !(vmx_long_mode_enabled(v)) ) - goto exit_and_crash; - msr_content = __vmread(GUEST_FS_BASE); - break; + goto check_long_mode; case MSR_GS_BASE: - if ( !(vmx_long_mode_enabled(v)) ) - goto exit_and_crash; - msr_content = __vmread(GUEST_GS_BASE); - break; + goto check_long_mode; case MSR_SHADOW_GS_BASE: msr_content = guest_msr_state->shadow_gs; + check_long_mode: + if ( !(vmx_long_mode_enabled(v)) ) + { + vmx_inject_hw_exception(v, TRAP_gp_fault, 0); + return 0; + } break; - CASE_READ_MSR(STAR); - CASE_READ_MSR(LSTAR); - CASE_READ_MSR(CSTAR); - CASE_READ_MSR(SYSCALL_MASK); + case MSR_STAR: + msr_content = guest_msr_state->msrs[VMX_INDEX_MSR_STAR]; + break; + + case MSR_LSTAR: + msr_content = guest_msr_state->msrs[VMX_INDEX_MSR_LSTAR]; + break; + + case MSR_CSTAR: + msr_content = guest_msr_state->msrs[VMX_INDEX_MSR_CSTAR]; + break; + + case MSR_SYSCALL_MASK: + msr_content = guest_msr_state->msrs[VMX_INDEX_MSR_SYSCALL_MASK]; + break; default: return 0; @@ -155,32 +159,28 @@ static inline int long_mode_do_msr_read(struct cpu_user_regs *regs) regs->edx = (u32)(msr_content >> 32); return 1; - - exit_and_crash: - gdprintk(XENLOG_ERR, "Fatal error reading MSR %lx\n", (long)regs->ecx); - domain_crash(v->domain); - return 1; /* handled */ } static inline int long_mode_do_msr_write(struct cpu_user_regs *regs) { u64 msr_content = (u32)regs->eax | ((u64)regs->edx << 32); + u32 ecx = regs->ecx; struct vcpu *v = current; struct vmx_msr_state *guest_msr_state = &v->arch.hvm_vmx.msr_state; struct vmx_msr_state *host_msr_state = &this_cpu(host_msr_state); HVM_DBG_LOG(DBG_LEVEL_1, "msr 0x%x msr_content 0x%"PRIx64"\n", - (u32)regs->ecx, msr_content); + ecx, msr_content); - switch ( (u32)regs->ecx ) { + switch ( ecx ) + { case MSR_EFER: /* offending reserved bit will cause #GP */ if ( msr_content & ~(EFER_LME | EFER_LMA | EFER_NX | EFER_SCE) ) { - printk("Trying to set reserved bit in EFER: %"PRIx64"\n", - msr_content); - vmx_inject_hw_exception(v, TRAP_gp_fault, 0); - return 0; + gdprintk(XENLOG_WARNING, "Trying to set reserved bit in " + "EFER: %"PRIx64"\n", msr_content); + goto gp_fault; } if ( (msr_content & EFER_LME) @@ -188,9 +188,9 @@ static inline int long_mode_do_msr_write(struct cpu_user_regs *regs) { if ( unlikely(vmx_paging_enabled(v)) ) { - printk("Trying to set EFER.LME with paging enabled\n"); - vmx_inject_hw_exception(v, TRAP_gp_fault, 0); - return 0; + gdprintk(XENLOG_WARNING, + "Trying to set EFER.LME with paging enabled\n"); + goto gp_fault; } } else if ( !(msr_content & EFER_LME) @@ -198,9 +198,9 @@ static inline int long_mode_do_msr_write(struct cpu_user_regs *regs) { if ( unlikely(vmx_paging_enabled(v)) ) { - printk("Trying to clear EFER.LME with paging enabled\n"); - vmx_inject_hw_exception(v, TRAP_gp_fault, 0); - return 0; + gdprintk(XENLOG_WARNING, + "Trying to clear EFER.LME with paging enabled\n"); + goto gp_fault; } } @@ -209,35 +209,40 @@ static inline int long_mode_do_msr_write(struct cpu_user_regs *regs) case MSR_FS_BASE: case MSR_GS_BASE: + case MSR_SHADOW_GS_BASE: if ( !vmx_long_mode_enabled(v) ) - goto exit_and_crash; + goto gp_fault; - if ( !IS_CANO_ADDRESS(msr_content) ) + if ( !is_canonical_address(msr_content) ) + goto uncanonical_address; + + if ( ecx == MSR_FS_BASE ) + __vmwrite(GUEST_FS_BASE, msr_content); + else if ( ecx == MSR_GS_BASE ) + __vmwrite(GUEST_GS_BASE, msr_content); + else { - HVM_DBG_LOG(DBG_LEVEL_1, "Not cano address of msr write\n"); - vmx_inject_hw_exception(v, TRAP_gp_fault, 0); - return 0; + v->arch.hvm_vmx.msr_state.shadow_gs = msr_content; + wrmsrl(MSR_SHADOW_GS_BASE, msr_content); } - if ( regs->ecx == MSR_FS_BASE ) - __vmwrite(GUEST_FS_BASE, msr_content); - else - __vmwrite(GUEST_GS_BASE, msr_content); - break; - case MSR_SHADOW_GS_BASE: - if ( !(vmx_long_mode_enabled(v)) ) - goto exit_and_crash; + case MSR_STAR: + WRITE_MSR(STAR); - v->arch.hvm_vmx.msr_state.shadow_gs = msr_content; - wrmsrl(MSR_SHADOW_GS_BASE, msr_content); - break; + case MSR_LSTAR: + if ( !is_canonical_address(msr_content) ) + goto uncanonical_address; + WRITE_MSR(LSTAR); - CASE_WRITE_MSR(STAR); - CASE_WRITE_MSR(LSTAR); - CASE_WRITE_MSR(CSTAR); - CASE_WRITE_MSR(SYSCALL_MASK); + case MSR_CSTAR: + if ( !is_canonical_address(msr_content) ) + goto uncanonical_address; + WRITE_MSR(CSTAR); + + case MSR_SYSCALL_MASK: + WRITE_MSR(SYSCALL_MASK); default: return 0; @@ -245,10 +250,11 @@ static inline int long_mode_do_msr_write(struct cpu_user_regs *regs) return 1; - exit_and_crash: - gdprintk(XENLOG_ERR, "Fatal error writing MSR %lx\n", (long)regs->ecx); - domain_crash(v->domain); - return 1; /* handled */ + uncanonical_address: + HVM_DBG_LOG(DBG_LEVEL_1, "Not cano address of msr write %x\n", ecx); + gp_fault: + vmx_inject_hw_exception(v, TRAP_gp_fault, 0); + return 0; } /* @@ -501,7 +507,7 @@ static unsigned long vmx_get_ctrl_reg(struct vcpu *v, unsigned int num) return 0; /* dummy */ } -static unsigned long vmx_get_segment_base(struct vcpu *v, enum segment seg) +static unsigned long vmx_get_segment_base(struct vcpu *v, enum x86_segment seg) { unsigned long base = 0; int long_mode = 0; @@ -516,22 +522,94 @@ static unsigned long vmx_get_segment_base(struct vcpu *v, enum segment seg) switch ( seg ) { - case seg_cs: if ( !long_mode ) base = __vmread(GUEST_CS_BASE); break; - case seg_ds: if ( !long_mode ) base = __vmread(GUEST_DS_BASE); break; - case seg_es: if ( !long_mode ) base = __vmread(GUEST_ES_BASE); break; - case seg_fs: base = __vmread(GUEST_FS_BASE); break; - case seg_gs: base = __vmread(GUEST_GS_BASE); break; - case seg_ss: if ( !long_mode ) base = __vmread(GUEST_SS_BASE); break; - case seg_tr: base = __vmread(GUEST_TR_BASE); break; - case seg_gdtr: base = __vmread(GUEST_GDTR_BASE); break; - case seg_idtr: base = __vmread(GUEST_IDTR_BASE); break; - case seg_ldtr: base = __vmread(GUEST_LDTR_BASE); break; + case x86_seg_cs: if ( !long_mode ) base = __vmread(GUEST_CS_BASE); break; + case x86_seg_ds: if ( !long_mode ) base = __vmread(GUEST_DS_BASE); break; + case x86_seg_es: if ( !long_mode ) base = __vmread(GUEST_ES_BASE); break; + case x86_seg_fs: base = __vmread(GUEST_FS_BASE); break; + case x86_seg_gs: base = __vmread(GUEST_GS_BASE); break; + case x86_seg_ss: if ( !long_mode ) base = __vmread(GUEST_SS_BASE); break; + case x86_seg_tr: base = __vmread(GUEST_TR_BASE); break; + case x86_seg_gdtr: base = __vmread(GUEST_GDTR_BASE); break; + case x86_seg_idtr: base = __vmread(GUEST_IDTR_BASE); break; + case x86_seg_ldtr: base = __vmread(GUEST_LDTR_BASE); break; default: BUG(); break; } return base; } +static void vmx_get_segment_register(struct vcpu *v, enum x86_segment seg, + struct segment_register *reg) +{ + u16 attr = 0; + + ASSERT(v == current); + + switch ( seg ) + { + case x86_seg_cs: + reg->sel = __vmread(GUEST_CS_SELECTOR); + reg->limit = __vmread(GUEST_CS_LIMIT); + reg->base = __vmread(GUEST_CS_BASE); + attr = __vmread(GUEST_CS_AR_BYTES); + break; + case x86_seg_ds: + reg->sel = __vmread(GUEST_DS_SELECTOR); + reg->limit = __vmread(GUEST_DS_LIMIT); + reg->base = __vmread(GUEST_DS_BASE); + attr = __vmread(GUEST_DS_AR_BYTES); + break; + case x86_seg_es: + reg->sel = __vmread(GUEST_ES_SELECTOR); + reg->limit = __vmread(GUEST_ES_LIMIT); + reg->base = __vmread(GUEST_ES_BASE); + attr = __vmread(GUEST_ES_AR_BYTES); + break; + case x86_seg_fs: + reg->sel = __vmread(GUEST_FS_SELECTOR); + reg->limit = __vmread(GUEST_FS_LIMIT); + reg->base = __vmread(GUEST_FS_BASE); + attr = __vmread(GUEST_FS_AR_BYTES); + break; + case x86_seg_gs: + reg->sel = __vmread(GUEST_GS_SELECTOR); + reg->limit = __vmread(GUEST_GS_LIMIT); + reg->base = __vmread(GUEST_GS_BASE); + attr = __vmread(GUEST_GS_AR_BYTES); + break; + case x86_seg_ss: + reg->sel = __vmread(GUEST_SS_SELECTOR); + reg->limit = __vmread(GUEST_SS_LIMIT); + reg->base = __vmread(GUEST_SS_BASE); + attr = __vmread(GUEST_SS_AR_BYTES); + break; + case x86_seg_tr: + reg->sel = __vmread(GUEST_TR_SELECTOR); + reg->limit = __vmread(GUEST_TR_LIMIT); + reg->base = __vmread(GUEST_TR_BASE); + attr = __vmread(GUEST_TR_AR_BYTES); + break; + case x86_seg_gdtr: + reg->limit = __vmread(GUEST_GDTR_LIMIT); + reg->base = __vmread(GUEST_GDTR_BASE); + break; + case x86_seg_idtr: + reg->limit = __vmread(GUEST_IDTR_LIMIT); + reg->base = __vmread(GUEST_IDTR_BASE); + break; + case x86_seg_ldtr: + reg->sel = __vmread(GUEST_LDTR_SELECTOR); + reg->limit = __vmread(GUEST_LDTR_LIMIT); + reg->base = __vmread(GUEST_LDTR_BASE); + attr = __vmread(GUEST_LDTR_AR_BYTES); + break; + default: + BUG(); + } + + reg->attr.bytes = (attr & 0xff) | ((attr >> 4) & 0xf00); +} + /* Make sure that xen intercepts any FP accesses from current */ static void vmx_stts(struct vcpu *v) { @@ -630,6 +708,22 @@ static int vmx_pae_enabled(struct vcpu *v) return (vmx_paging_enabled(v) && (cr4 & X86_CR4_PAE)); } +/* Works only for vcpu == current */ +static void vmx_update_host_cr3(struct vcpu *v) +{ + ASSERT(v == current); + __vmwrite(HOST_CR3, v->arch.cr3); +} + +static void vmx_inject_exception( + unsigned int trapnr, int errcode, unsigned long cr2) +{ + struct vcpu *v = current; + vmx_inject_hw_exception(v, trapnr, errcode); + if ( trapnr == TRAP_page_fault ) + v->arch.hvm_vmx.cpu_cr2 = cr2; +} + /* Setup HVM interfaces */ static void vmx_setup_hvm_funcs(void) { @@ -650,12 +744,15 @@ static void vmx_setup_hvm_funcs(void) hvm_funcs.guest_x86_mode = vmx_guest_x86_mode; hvm_funcs.get_guest_ctrl_reg = vmx_get_ctrl_reg; hvm_funcs.get_segment_base = vmx_get_segment_base; + hvm_funcs.get_segment_register = vmx_get_segment_register; hvm_funcs.update_host_cr3 = vmx_update_host_cr3; hvm_funcs.stts = vmx_stts; hvm_funcs.set_tsc_offset = vmx_set_tsc_offset; + hvm_funcs.inject_exception = vmx_inject_exception; + hvm_funcs.init_ap_context = vmx_init_ap_context; hvm_funcs.init_hypercall_page = vmx_init_hypercall_page; @@ -878,15 +975,19 @@ static void vmx_do_cpuid(struct cpu_user_regs *regs) { eax = ebx = ecx = edx = 0x0; } -#ifdef __i386__ else if ( input == CPUID_LEAF_0x80000001 ) { +#if CONFIG_PAGING_LEVELS >= 3 + if ( !v->domain->arch.hvm_domain.params[HVM_PARAM_PAE_ENABLED] ) +#endif + clear_bit(X86_FEATURE_NX & 31, &edx); +#ifdef __i386__ clear_bit(X86_FEATURE_LAHF_LM & 31, &ecx); clear_bit(X86_FEATURE_LM & 31, &edx); clear_bit(X86_FEATURE_SYSCALL & 31, &edx); - } #endif + } } regs->eax = (unsigned long) eax; @@ -958,14 +1059,14 @@ static void vmx_do_invlpg(unsigned long va) static int vmx_check_descriptor(int long_mode, unsigned long eip, int inst_len, - enum segment seg, unsigned long *base, + enum x86_segment seg, unsigned long *base, u32 *limit, u32 *ar_bytes) { enum vmcs_field ar_field, base_field, limit_field; *base = 0; *limit = 0; - if ( seg != seg_es ) + if ( seg != x86_seg_es ) { unsigned char inst[MAX_INST_LEN]; int i; @@ -995,22 +1096,22 @@ static int vmx_check_descriptor(int long_mode, unsigned long eip, int inst_len, #endif continue; case 0x2e: /* CS */ - seg = seg_cs; + seg = x86_seg_cs; continue; case 0x36: /* SS */ - seg = seg_ss; + seg = x86_seg_ss; continue; case 0x26: /* ES */ - seg = seg_es; + seg = x86_seg_es; continue; case 0x64: /* FS */ - seg = seg_fs; + seg = x86_seg_fs; continue; case 0x65: /* GS */ - seg = seg_gs; + seg = x86_seg_gs; continue; case 0x3e: /* DS */ - seg = seg_ds; + seg = x86_seg_ds; continue; } } @@ -1018,32 +1119,32 @@ static int vmx_check_descriptor(int long_mode, unsigned long eip, int inst_len, switch ( seg ) { - case seg_cs: + case x86_seg_cs: ar_field = GUEST_CS_AR_BYTES; base_field = GUEST_CS_BASE; limit_field = GUEST_CS_LIMIT; break; - case seg_ds: + case x86_seg_ds: ar_field = GUEST_DS_AR_BYTES; base_field = GUEST_DS_BASE; limit_field = GUEST_DS_LIMIT; break; - case seg_es: + case x86_seg_es: ar_field = GUEST_ES_AR_BYTES; base_field = GUEST_ES_BASE; limit_field = GUEST_ES_LIMIT; break; - case seg_fs: + case x86_seg_fs: ar_field = GUEST_FS_AR_BYTES; base_field = GUEST_FS_BASE; limit_field = GUEST_FS_LIMIT; break; - case seg_gs: + case x86_seg_gs: ar_field = GUEST_FS_AR_BYTES; base_field = GUEST_FS_BASE; limit_field = GUEST_FS_LIMIT; break; - case seg_ss: + case x86_seg_ss: ar_field = GUEST_GS_AR_BYTES; base_field = GUEST_GS_BASE; limit_field = GUEST_GS_LIMIT; @@ -1053,7 +1154,7 @@ static int vmx_check_descriptor(int long_mode, unsigned long eip, int inst_len, return 0; } - if ( !long_mode || seg == seg_fs || seg == seg_gs ) + if ( !long_mode || seg == x86_seg_fs || seg == x86_seg_gs ) { *base = __vmread(base_field); *limit = __vmread(limit_field); @@ -1123,7 +1224,7 @@ static void vmx_io_instruction(unsigned long exit_qualification, * selector is null. */ if ( !vmx_check_descriptor(long_mode, regs->eip, inst_len, - dir == IOREQ_WRITE ? seg_ds : seg_es, + dir==IOREQ_WRITE ? x86_seg_ds : x86_seg_es, &base, &limit, &ar_bytes) ) { if ( !long_mode ) { vmx_inject_hw_exception(current, TRAP_gp_fault, 0); @@ -1192,6 +1293,32 @@ static void vmx_io_instruction(unsigned long exit_qualification, ASSERT(count); } } +#ifdef __x86_64__ + else + { + if ( !is_canonical_address(addr) || + !is_canonical_address(addr + size - 1) ) + { + vmx_inject_hw_exception(current, TRAP_gp_fault, 0); + return; + } + if ( count > (1UL << 48) / size ) + count = (1UL << 48) / size; + if ( !(regs->eflags & EF_DF) ) + { + if ( addr + count * size - 1 < addr || + !is_canonical_address(addr + count * size - 1) ) + count = (addr & ~((1UL << 48) - 1)) / size; + } + else + { + if ( (count - 1) * size > addr || + !is_canonical_address(addr + (count - 1) * size) ) + count = (addr & ~((1UL << 48) - 1)) / size + 1; + } + ASSERT(count); + } +#endif /* * Handle string pio instructions that cross pages or that @@ -2409,7 +2536,6 @@ asmlinkage void vmx_vmexit_handler(struct cpu_user_regs *regs) break; case EXIT_REASON_TPR_BELOW_THRESHOLD: - vcpu_vlapic(v)->flush_tpr_threshold = 1; break; default: diff --git a/xen/arch/x86/machine_kexec.c b/xen/arch/x86/machine_kexec.c new file mode 100644 index 0000000000..81c392a7d3 --- /dev/null +++ b/xen/arch/x86/machine_kexec.c @@ -0,0 +1,105 @@ +/****************************************************************************** + * machine_kexec.c + * + * Xen port written by: + * - Simon 'Horms' Horman + * - Magnus Damm + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int machine_kexec_load(int type, int slot, xen_kexec_image_t *image) +{ + unsigned long prev_ma = 0; + int fix_base = FIX_KEXEC_BASE_0 + (slot * (KEXEC_XEN_NO_PAGES >> 1)); + int k; + + /* setup fixmap to point to our pages and record the virtual address + * in every odd index in page_list[]. + */ + + for ( k = 0; k < KEXEC_XEN_NO_PAGES; k++ ) + { + if ( (k & 1) == 0 ) + { + /* Even pages: machine address. */ + prev_ma = image->page_list[k]; + } + else + { + /* Odd pages: va for previous ma. */ + set_fixmap(fix_base + (k >> 1), prev_ma); + image->page_list[k] = fix_to_virt(fix_base + (k >> 1)); + } + } + + return 0; +} + +void machine_kexec_unload(int type, int slot, xen_kexec_image_t *image) +{ +} + +static void __machine_reboot_kexec(void *data) +{ + xen_kexec_image_t *image = (xen_kexec_image_t *)data; + + watchdog_disable(); + console_start_sync(); + + smp_send_stop(); + +#ifdef CONFIG_X86_IO_APIC + disable_IO_APIC(); +#endif + hvm_disable(); + + machine_kexec(image); +} + +void machine_reboot_kexec(xen_kexec_image_t *image) +{ + int reboot_cpu_id; + cpumask_t reboot_cpu; + + reboot_cpu_id = 0; + + if ( !cpu_isset(reboot_cpu_id, cpu_online_map) ) + reboot_cpu_id = smp_processor_id(); + + if ( reboot_cpu_id != smp_processor_id() ) + { + cpus_clear(reboot_cpu); + cpu_set(reboot_cpu_id, reboot_cpu); + on_selected_cpus(reboot_cpu, __machine_reboot_kexec, image, 1, 0); + for (;;) + ; /* nothing */ + } + else + { + __machine_reboot_kexec(image); + } + BUG(); +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c index 6980960a71..034f992643 100644 --- a/xen/arch/x86/mm.c +++ b/xen/arch/x86/mm.c @@ -335,7 +335,7 @@ void make_cr3(struct vcpu *v, unsigned long mfn) cache->high_mfn = mfn; /* Map the guest L3 table and copy to the chosen low-memory cache. */ - *(fix_pae_highmem_pl1e - cpu) = l1e_from_pfn(mfn, __PAGE_HYPERVISOR); + l1e_write(fix_pae_highmem_pl1e-cpu, l1e_from_pfn(mfn, __PAGE_HYPERVISOR)); /* First check the previous high mapping can't be in the TLB. * (i.e. have we loaded CR3 since we last did this?) */ if ( unlikely(this_cpu(make_cr3_timestamp) == this_cpu(tlbflush_time)) ) @@ -343,7 +343,7 @@ void make_cr3(struct vcpu *v, unsigned long mfn) highmem_l3tab = (l3_pgentry_t *)fix_to_virt(FIX_PAE_HIGHMEM_0 + cpu); lowmem_l3tab = cache->table[cache->inuse_idx]; memcpy(lowmem_l3tab, highmem_l3tab, sizeof(cache->table[0])); - *(fix_pae_highmem_pl1e - cpu) = l1e_empty(); + l1e_write(fix_pae_highmem_pl1e-cpu, l1e_empty()); this_cpu(make_cr3_timestamp) = this_cpu(tlbflush_time); v->arch.cr3 = __pa(lowmem_l3tab); @@ -380,7 +380,7 @@ void invalidate_shadow_ldt(struct vcpu *v) { pfn = l1e_get_pfn(v->arch.perdomain_ptes[i]); if ( pfn == 0 ) continue; - v->arch.perdomain_ptes[i] = l1e_empty(); + l1e_write(&v->arch.perdomain_ptes[i], l1e_empty()); page = mfn_to_page(pfn); ASSERT_PAGE_IS_TYPE(page, PGT_ldt_page); ASSERT_PAGE_IS_DOMAIN(page, v->domain); @@ -435,21 +435,12 @@ int map_ldt_shadow_page(unsigned int off) return 0; okay = get_page_and_type(mfn_to_page(mfn), d, PGT_ldt_page); - - if ( !okay && unlikely(shadow_mode_refcounts(d)) ) - { - shadow_lock(d); - shadow_remove_write_access(d->vcpu[0], _mfn(mfn), 0, 0); - okay = get_page_and_type(mfn_to_page(mfn), d, PGT_ldt_page); - shadow_unlock(d); - } - if ( unlikely(!okay) ) return 0; nl1e = l1e_from_pfn(mfn, l1e_get_flags(l1e) | _PAGE_RW); - v->arch.perdomain_ptes[off + 16] = nl1e; + l1e_write(&v->arch.perdomain_ptes[off + 16], nl1e); v->arch.shadow_ldt_mapcnt++; return 1; @@ -509,8 +500,6 @@ get_linear_pagetable( struct page_info *page; unsigned long pfn; - ASSERT( !shadow_mode_refcounts(d) ); - if ( (root_get_flags(re) & _PAGE_RW) ) { MEM_LOG("Attempt to create linear p.t. with write perms"); @@ -821,8 +810,6 @@ static int alloc_l1_table(struct page_info *page) l1_pgentry_t *pl1e; int i; - ASSERT(!shadow_mode_refcounts(d)); - pl1e = map_domain_page(pfn); for ( i = 0; i < L1_PAGETABLE_ENTRIES; i++ ) @@ -851,7 +838,7 @@ static int alloc_l1_table(struct page_info *page) static int create_pae_xen_mappings(l3_pgentry_t *pl3e) { struct page_info *page; - l2_pgentry_t *pl2e; + l2_pgentry_t *pl2e, l2e; l3_pgentry_t l3e3; int i; @@ -873,8 +860,6 @@ static int create_pae_xen_mappings(l3_pgentry_t *pl3e) * 2. Cannot appear in another page table's L3: * a. alloc_l3_table() calls this function and this check will fail * b. mod_l3_entry() disallows updates to slot 3 in an existing table - * - * XXX -- this needs revisiting for shadow_mode_refcount()==true... */ page = l3e_get_page(l3e3); BUG_ON(page->u.inuse.type_info & PGT_pinned); @@ -892,15 +877,19 @@ static int create_pae_xen_mappings(l3_pgentry_t *pl3e) &idle_pg_table_l2[L2_PAGETABLE_FIRST_XEN_SLOT], L2_PAGETABLE_XEN_SLOTS * sizeof(l2_pgentry_t)); for ( i = 0; i < PDPT_L2_ENTRIES; i++ ) - pl2e[l2_table_offset(PERDOMAIN_VIRT_START) + i] = - l2e_from_page( - virt_to_page(page_get_owner(page)->arch.mm_perdomain_pt) + i, - __PAGE_HYPERVISOR); + { + l2e = l2e_from_page( + virt_to_page(page_get_owner(page)->arch.mm_perdomain_pt) + i, + __PAGE_HYPERVISOR); + l2e_write(&pl2e[l2_table_offset(PERDOMAIN_VIRT_START) + i], l2e); + } for ( i = 0; i < (LINEARPT_MBYTES >> (L2_PAGETABLE_SHIFT - 20)); i++ ) - pl2e[l2_table_offset(LINEAR_PT_VIRT_START) + i] = - (l3e_get_flags(pl3e[i]) & _PAGE_PRESENT) ? - l2e_from_pfn(l3e_get_pfn(pl3e[i]), __PAGE_HYPERVISOR) : - l2e_empty(); + { + l2e = l2e_empty(); + if ( l3e_get_flags(pl3e[i]) & _PAGE_PRESENT ) + l2e = l2e_from_pfn(l3e_get_pfn(pl3e[i]), __PAGE_HYPERVISOR); + l2e_write(&pl2e[l2_table_offset(LINEAR_PT_VIRT_START) + i], l2e); + } unmap_domain_page(pl2e); return 1; @@ -955,8 +944,6 @@ static int alloc_l2_table(struct page_info *page, unsigned long type) l2_pgentry_t *pl2e; int i; - ASSERT(!shadow_mode_refcounts(d)); - pl2e = map_domain_page(pfn); for ( i = 0; i < L2_PAGETABLE_ENTRIES; i++ ) @@ -1004,8 +991,6 @@ static int alloc_l3_table(struct page_info *page) l3_pgentry_t *pl3e; int i; - ASSERT(!shadow_mode_refcounts(d)); - #ifdef CONFIG_X86_PAE /* * PAE pgdirs above 4GB are unacceptable if the guest does not understand @@ -1071,8 +1056,6 @@ static int alloc_l4_table(struct page_info *page) l4_pgentry_t *pl4e = page_to_virt(page); int i; - ASSERT(!shadow_mode_refcounts(d)); - for ( i = 0; i < L4_PAGETABLE_ENTRIES; i++ ) { if ( is_guest_l4_slot(i) && @@ -2760,7 +2743,7 @@ void destroy_gdt(struct vcpu *v) { if ( (pfn = l1e_get_pfn(v->arch.perdomain_ptes[i])) != 0 ) put_page_and_type(mfn_to_page(pfn)); - v->arch.perdomain_ptes[i] = l1e_empty(); + l1e_write(&v->arch.perdomain_ptes[i], l1e_empty()); v->arch.guest_context.gdt_frames[i] = 0; } } @@ -2794,8 +2777,8 @@ long set_gdt(struct vcpu *v, for ( i = 0; i < nr_pages; i++ ) { v->arch.guest_context.gdt_frames[i] = frames[i]; - v->arch.perdomain_ptes[i] = - l1e_from_pfn(frames[i], __PAGE_HYPERVISOR); + l1e_write(&v->arch.perdomain_ptes[i], + l1e_from_pfn(frames[i], __PAGE_HYPERVISOR)); } return 0; @@ -2974,9 +2957,54 @@ long arch_memory_op(int op, XEN_GUEST_HANDLE(void) arg) break; } + case XENMEM_set_memory_map: + { + struct xen_foreign_memory_map fmap; + struct domain *d; + int rc; + + if ( copy_from_guest(&fmap, arg, 1) ) + return -EFAULT; + + if ( fmap.map.nr_entries > ARRAY_SIZE(d->arch.e820) ) + return -EINVAL; + + if ( fmap.domid == DOMID_SELF ) + { + d = current->domain; + get_knownalive_domain(d); + } + else if ( !IS_PRIV(current->domain) ) + return -EPERM; + else if ( (d = find_domain_by_id(fmap.domid)) == NULL ) + return -ESRCH; + + rc = copy_from_guest(&d->arch.e820[0], fmap.map.buffer, + fmap.map.nr_entries) ? -EFAULT : 0; + d->arch.nr_e820 = fmap.map.nr_entries; + + put_domain(d); + return rc; + } + case XENMEM_memory_map: { - return -ENOSYS; + struct xen_memory_map map; + struct domain *d = current->domain; + + /* Backwards compatibility. */ + if ( d->arch.nr_e820 == 0 ) + return -ENOSYS; + + if ( copy_from_guest(&map, arg, 1) ) + return -EFAULT; + + map.nr_entries = min(map.nr_entries, d->arch.nr_e820); + if ( copy_to_guest(map.buffer, &d->arch.e820[0], map.nr_entries) || + copy_to_guest(arg, &map, 1) ) + return -EFAULT; + + return 0; } case XENMEM_machine_memory_map: @@ -3033,12 +3061,39 @@ long arch_memory_op(int op, XEN_GUEST_HANDLE(void) arg) * Writable Pagetables */ +struct ptwr_emulate_ctxt { + struct x86_emulate_ctxt ctxt; + unsigned long cr2; + l1_pgentry_t pte; +}; + +static int ptwr_emulated_read( + enum x86_segment seg, + unsigned long offset, + unsigned long *val, + unsigned int bytes, + struct x86_emulate_ctxt *ctxt) +{ + unsigned int rc; + unsigned long addr = offset; + + *val = 0; + if ( (rc = copy_from_user((void *)val, (void *)addr, bytes)) != 0 ) + { + propagate_page_fault(addr + bytes - rc, 0); /* read fault */ + return X86EMUL_PROPAGATE_FAULT; + } + + return X86EMUL_CONTINUE; +} + static int ptwr_emulated_update( unsigned long addr, paddr_t old, paddr_t val, unsigned int bytes, - unsigned int do_cmpxchg) + unsigned int do_cmpxchg, + struct ptwr_emulate_ctxt *ptwr_ctxt) { unsigned long gmfn, mfn; struct page_info *page; @@ -3046,11 +3101,11 @@ static int ptwr_emulated_update( struct vcpu *v = current; struct domain *d = v->domain; - /* Aligned access only, thank you. */ - if ( !access_ok(addr, bytes) || ((addr & (bytes-1)) != 0) ) + /* Only allow naturally-aligned stores within the original %cr2 page. */ + if ( unlikely(((addr^ptwr_ctxt->cr2) & PAGE_MASK) || (addr & (bytes-1))) ) { - MEM_LOG("ptwr_emulate: Unaligned or bad size ptwr access (%d, %lx)", - bytes, addr); + MEM_LOG("Bad ptwr access (cr2=%lx, addr=%lx, bytes=%u)", + ptwr_ctxt->cr2, addr, bytes); return X86EMUL_UNHANDLEABLE; } @@ -3079,17 +3134,9 @@ static int ptwr_emulated_update( old |= full; } - /* Read the PTE that maps the page being updated. */ - guest_get_eff_l1e(v, addr, &pte); - if ( unlikely(!(l1e_get_flags(pte) & _PAGE_PRESENT)) ) - { - MEM_LOG("%s: Cannot get L1 PTE for guest address %lx", - __func__, addr); - return X86EMUL_UNHANDLEABLE; - } - - gmfn = l1e_get_pfn(pte); - mfn = gmfn_to_mfn(d, gmfn); + pte = ptwr_ctxt->pte; + gmfn = l1e_get_pfn(pte); + mfn = gmfn_to_mfn(d, gmfn); page = mfn_to_page(mfn); /* We are looking only for read-only mappings of p.t. pages. */ @@ -3164,26 +3211,33 @@ static int ptwr_emulated_update( } static int ptwr_emulated_write( - unsigned long addr, + enum x86_segment seg, + unsigned long offset, unsigned long val, unsigned int bytes, struct x86_emulate_ctxt *ctxt) { - return ptwr_emulated_update(addr, 0, val, bytes, 0); + return ptwr_emulated_update( + offset, 0, val, bytes, 0, + container_of(ctxt, struct ptwr_emulate_ctxt, ctxt)); } static int ptwr_emulated_cmpxchg( - unsigned long addr, + enum x86_segment seg, + unsigned long offset, unsigned long old, unsigned long new, unsigned int bytes, struct x86_emulate_ctxt *ctxt) { - return ptwr_emulated_update(addr, old, new, bytes, 1); + return ptwr_emulated_update( + offset, old, new, bytes, 1, + container_of(ctxt, struct ptwr_emulate_ctxt, ctxt)); } static int ptwr_emulated_cmpxchg8b( - unsigned long addr, + enum x86_segment seg, + unsigned long offset, unsigned long old, unsigned long old_hi, unsigned long new, @@ -3192,18 +3246,17 @@ static int ptwr_emulated_cmpxchg8b( { if ( CONFIG_PAGING_LEVELS == 2 ) return X86EMUL_UNHANDLEABLE; - else - return ptwr_emulated_update( - addr, ((u64)old_hi << 32) | old, ((u64)new_hi << 32) | new, 8, 1); + return ptwr_emulated_update( + offset, ((u64)old_hi << 32) | old, ((u64)new_hi << 32) | new, 8, 1, + container_of(ctxt, struct ptwr_emulate_ctxt, ctxt)); } static struct x86_emulate_ops ptwr_emulate_ops = { - .read_std = x86_emulate_read_std, - .write_std = x86_emulate_write_std, - .read_emulated = x86_emulate_read_std, - .write_emulated = ptwr_emulated_write, - .cmpxchg_emulated = ptwr_emulated_cmpxchg, - .cmpxchg8b_emulated = ptwr_emulated_cmpxchg8b + .read = ptwr_emulated_read, + .insn_fetch = ptwr_emulated_read, + .write = ptwr_emulated_write, + .cmpxchg = ptwr_emulated_cmpxchg, + .cmpxchg8b = ptwr_emulated_cmpxchg8b }; /* Write page fault handler: check if guest is trying to modify a PTE. */ @@ -3214,7 +3267,7 @@ int ptwr_do_page_fault(struct vcpu *v, unsigned long addr, unsigned long pfn; struct page_info *page; l1_pgentry_t pte; - struct x86_emulate_ctxt emul_ctxt; + struct ptwr_emulate_ctxt ptwr_ctxt; LOCK_BIGLOCK(d); @@ -3235,10 +3288,11 @@ int ptwr_do_page_fault(struct vcpu *v, unsigned long addr, (page_get_owner(page) != d) ) goto bail; - emul_ctxt.regs = guest_cpu_user_regs(); - emul_ctxt.cr2 = addr; - emul_ctxt.mode = X86EMUL_MODE_HOST; - if ( x86_emulate_memop(&emul_ctxt, &ptwr_emulate_ops) ) + ptwr_ctxt.ctxt.regs = guest_cpu_user_regs(); + ptwr_ctxt.ctxt.mode = X86EMUL_MODE_HOST; + ptwr_ctxt.cr2 = addr; + ptwr_ctxt.pte = pte; + if ( x86_emulate_memop(&ptwr_ctxt.ctxt, &ptwr_emulate_ops) ) goto bail; UNLOCK_BIGLOCK(d); @@ -3272,8 +3326,8 @@ int map_pages_to_xen( !map_small_pages ) { /* Super-page mapping. */ - ol2e = *pl2e; - *pl2e = l2e_from_pfn(mfn, flags|_PAGE_PSE); + ol2e = *pl2e; + l2e_write(pl2e, l2e_from_pfn(mfn, flags|_PAGE_PSE)); if ( (l2e_get_flags(ol2e) & _PAGE_PRESENT) ) { @@ -3293,22 +3347,24 @@ int map_pages_to_xen( { pl1e = page_to_virt(alloc_xen_pagetable()); clear_page(pl1e); - *pl2e = l2e_from_page(virt_to_page(pl1e), __PAGE_HYPERVISOR); + l2e_write(pl2e, l2e_from_page(virt_to_page(pl1e), + __PAGE_HYPERVISOR)); } else if ( l2e_get_flags(*pl2e) & _PAGE_PSE ) { pl1e = page_to_virt(alloc_xen_pagetable()); for ( i = 0; i < L1_PAGETABLE_ENTRIES; i++ ) - pl1e[i] = l1e_from_pfn( - l2e_get_pfn(*pl2e) + i, - l2e_get_flags(*pl2e) & ~_PAGE_PSE); - *pl2e = l2e_from_page(virt_to_page(pl1e), __PAGE_HYPERVISOR); + l1e_write(&pl1e[i], + l1e_from_pfn(l2e_get_pfn(*pl2e) + i, + l2e_get_flags(*pl2e) & ~_PAGE_PSE)); + l2e_write(pl2e, l2e_from_page(virt_to_page(pl1e), + __PAGE_HYPERVISOR)); local_flush_tlb_pge(); } pl1e = l2e_to_l1e(*pl2e) + l1_table_offset(virt); ol1e = *pl1e; - *pl1e = l1e_from_pfn(mfn, flags); + l1e_write(pl1e, l1e_from_pfn(mfn, flags)); if ( (l1e_get_flags(ol1e) & _PAGE_PRESENT) ) local_flush_tlb_one(virt); diff --git a/xen/arch/x86/mm/shadow/common.c b/xen/arch/x86/mm/shadow/common.c index cad0ea4cc9..8ade67b2d4 100644 --- a/xen/arch/x86/mm/shadow/common.c +++ b/xen/arch/x86/mm/shadow/common.c @@ -69,121 +69,358 @@ int _shadow_mode_refcounts(struct domain *d) /* x86 emulator support for the shadow code */ -static int -sh_x86_emulate_read_std(unsigned long addr, - unsigned long *val, - unsigned int bytes, - struct x86_emulate_ctxt *ctxt) +struct segment_register *hvm_get_seg_reg( + enum x86_segment seg, struct sh_emulate_ctxt *sh_ctxt) { + struct segment_register *seg_reg = &sh_ctxt->seg_reg[seg]; + if ( !__test_and_set_bit(seg, &sh_ctxt->valid_seg_regs) ) + hvm_get_segment_register(current, seg, seg_reg); + return seg_reg; +} + +enum hvm_access_type { + hvm_access_insn_fetch, hvm_access_read, hvm_access_write +}; + +static int hvm_translate_linear_addr( + enum x86_segment seg, + unsigned long offset, + unsigned int bytes, + enum hvm_access_type access_type, + struct sh_emulate_ctxt *sh_ctxt, + unsigned long *paddr) +{ + struct segment_register *reg = hvm_get_seg_reg(seg, sh_ctxt); + unsigned long limit, addr = offset; + uint32_t last_byte; + + if ( sh_ctxt->ctxt.mode != X86EMUL_MODE_PROT64 ) + { + /* + * COMPATIBILITY MODE: Apply segment checks and add base. + */ + + switch ( access_type ) + { + case hvm_access_read: + if ( (reg->attr.fields.type & 0xa) == 0x8 ) + goto gpf; /* execute-only code segment */ + break; + case hvm_access_write: + if ( (reg->attr.fields.type & 0xa) != 0x2 ) + goto gpf; /* not a writable data segment */ + break; + default: + break; + } + + /* Calculate the segment limit, including granularity flag. */ + limit = reg->limit; + if ( reg->attr.fields.g ) + limit = (limit << 12) | 0xfff; + + last_byte = offset + bytes - 1; + + /* Is this a grows-down data segment? Special limit check if so. */ + if ( (reg->attr.fields.type & 0xc) == 0x4 ) + { + /* Is upper limit 0xFFFF or 0xFFFFFFFF? */ + if ( !reg->attr.fields.db ) + last_byte = (uint16_t)last_byte; + + /* Check first byte and last byte against respective bounds. */ + if ( (offset <= limit) || (last_byte < offset) ) + goto gpf; + } + else if ( (last_byte > limit) || (last_byte < offset) ) + goto gpf; /* last byte is beyond limit or wraps 0xFFFFFFFF */ + + /* + * Hardware truncates to 32 bits in compatibility mode. + * It does not truncate to 16 bits in 16-bit address-size mode. + */ + addr = (uint32_t)(addr + reg->base); + } + else + { + /* + * LONG MODE: FS and GS add segment base. Addresses must be canonical. + */ + + if ( (seg == x86_seg_fs) || (seg == x86_seg_gs) ) + addr += reg->base; + + if ( !is_canonical_address(addr) ) + goto gpf; + } + + *paddr = addr; + return 0; + + gpf: + /* Inject #GP(0). */ + hvm_inject_exception(TRAP_gp_fault, 0, 0); + return X86EMUL_PROPAGATE_FAULT; +} + +static int +hvm_read(enum x86_segment seg, + unsigned long offset, + unsigned long *val, + unsigned int bytes, + enum hvm_access_type access_type, + struct sh_emulate_ctxt *sh_ctxt) +{ + unsigned long addr; + int rc, errcode; + + rc = hvm_translate_linear_addr( + seg, offset, bytes, access_type, sh_ctxt, &addr); + if ( rc ) + return rc; + *val = 0; // XXX -- this is WRONG. // It entirely ignores the permissions in the page tables. // In this case, that is only a user vs supervisor access check. // - if ( hvm_copy_from_guest_virt(val, addr, bytes) == 0 ) - { -#if 0 - struct vcpu *v = current; - SHADOW_PRINTK("d=%u v=%u a=%#lx v=%#lx bytes=%u\n", - v->domain->domain_id, v->vcpu_id, - addr, *val, bytes); -#endif + if ( (rc = hvm_copy_from_guest_virt(val, addr, bytes)) == 0 ) return X86EMUL_CONTINUE; - } /* If we got here, there was nothing mapped here, or a bad GFN * was mapped here. This should never happen: we're here because * of a write fault at the end of the instruction we're emulating. */ SHADOW_PRINTK("read failed to va %#lx\n", addr); + errcode = ring_3(sh_ctxt->ctxt.regs) ? PFEC_user_mode : 0; + if ( access_type == hvm_access_insn_fetch ) + errcode |= PFEC_insn_fetch; + hvm_inject_exception(TRAP_page_fault, errcode, addr + bytes - rc); return X86EMUL_PROPAGATE_FAULT; } static int -sh_x86_emulate_write_std(unsigned long addr, - unsigned long val, - unsigned int bytes, - struct x86_emulate_ctxt *ctxt) +hvm_emulate_read(enum x86_segment seg, + unsigned long offset, + unsigned long *val, + unsigned int bytes, + struct x86_emulate_ctxt *ctxt) { -#if 0 - struct vcpu *v = current; - SHADOW_PRINTK("d=%u v=%u a=%#lx v=%#lx bytes=%u\n", - v->domain->domain_id, v->vcpu_id, addr, val, bytes); -#endif - - // XXX -- this is WRONG. - // It entirely ignores the permissions in the page tables. - // In this case, that includes user vs supervisor, and - // write access. - // - if ( hvm_copy_to_guest_virt(addr, &val, bytes) == 0 ) - return X86EMUL_CONTINUE; - - /* If we got here, there was nothing mapped here, or a bad GFN - * was mapped here. This should never happen: we're here because - * of a write fault at the end of the instruction we're emulating, - * which should be handled by sh_x86_emulate_write_emulated. */ - SHADOW_PRINTK("write failed to va %#lx\n", addr); - return X86EMUL_PROPAGATE_FAULT; + return hvm_read(seg, offset, val, bytes, hvm_access_read, + container_of(ctxt, struct sh_emulate_ctxt, ctxt)); } static int -sh_x86_emulate_write_emulated(unsigned long addr, - unsigned long val, - unsigned int bytes, - struct x86_emulate_ctxt *ctxt) +hvm_emulate_insn_fetch(enum x86_segment seg, + unsigned long offset, + unsigned long *val, + unsigned int bytes, + struct x86_emulate_ctxt *ctxt) { + struct sh_emulate_ctxt *sh_ctxt = + container_of(ctxt, struct sh_emulate_ctxt, ctxt); + unsigned int insn_off = offset - ctxt->regs->eip; + + /* Fall back if requested bytes are not in the prefetch cache. */ + if ( unlikely((insn_off + bytes) > sh_ctxt->insn_buf_bytes) ) + return hvm_read(seg, offset, val, bytes, + hvm_access_insn_fetch, sh_ctxt); + + /* Hit the cache. Simple memcpy. */ + *val = 0; + memcpy(val, &sh_ctxt->insn_buf[insn_off], bytes); + return X86EMUL_CONTINUE; +} + +static int +hvm_emulate_write(enum x86_segment seg, + unsigned long offset, + unsigned long val, + unsigned int bytes, + struct x86_emulate_ctxt *ctxt) +{ + struct sh_emulate_ctxt *sh_ctxt = + container_of(ctxt, struct sh_emulate_ctxt, ctxt); struct vcpu *v = current; -#if 0 - SHADOW_PRINTK("d=%u v=%u a=%#lx v=%#lx bytes=%u\n", - v->domain->domain_id, v->vcpu_id, addr, val, bytes); -#endif - return v->arch.shadow.mode->x86_emulate_write(v, addr, &val, bytes, ctxt); + unsigned long addr; + int rc; + + rc = hvm_translate_linear_addr( + seg, offset, bytes, hvm_access_write, sh_ctxt, &addr); + if ( rc ) + return rc; + + return v->arch.shadow.mode->x86_emulate_write( + v, addr, &val, bytes, sh_ctxt); } static int -sh_x86_emulate_cmpxchg_emulated(unsigned long addr, - unsigned long old, - unsigned long new, - unsigned int bytes, - struct x86_emulate_ctxt *ctxt) +hvm_emulate_cmpxchg(enum x86_segment seg, + unsigned long offset, + unsigned long old, + unsigned long new, + unsigned int bytes, + struct x86_emulate_ctxt *ctxt) { + struct sh_emulate_ctxt *sh_ctxt = + container_of(ctxt, struct sh_emulate_ctxt, ctxt); struct vcpu *v = current; -#if 0 - SHADOW_PRINTK("d=%u v=%u a=%#lx o?=%#lx n:=%#lx bytes=%u\n", - v->domain->domain_id, v->vcpu_id, addr, old, new, bytes); -#endif - return v->arch.shadow.mode->x86_emulate_cmpxchg(v, addr, old, new, - bytes, ctxt); + unsigned long addr; + int rc; + + rc = hvm_translate_linear_addr( + seg, offset, bytes, hvm_access_write, sh_ctxt, &addr); + if ( rc ) + return rc; + + return v->arch.shadow.mode->x86_emulate_cmpxchg( + v, addr, old, new, bytes, sh_ctxt); } static int -sh_x86_emulate_cmpxchg8b_emulated(unsigned long addr, - unsigned long old_lo, - unsigned long old_hi, - unsigned long new_lo, - unsigned long new_hi, - struct x86_emulate_ctxt *ctxt) +hvm_emulate_cmpxchg8b(enum x86_segment seg, + unsigned long offset, + unsigned long old_lo, + unsigned long old_hi, + unsigned long new_lo, + unsigned long new_hi, + struct x86_emulate_ctxt *ctxt) { + struct sh_emulate_ctxt *sh_ctxt = + container_of(ctxt, struct sh_emulate_ctxt, ctxt); struct vcpu *v = current; -#if 0 - SHADOW_PRINTK("d=%u v=%u a=%#lx o?=%#lx:%lx n:=%#lx:%lx\n", - v->domain->domain_id, v->vcpu_id, addr, old_hi, old_lo, - new_hi, new_lo, ctxt); -#endif - return v->arch.shadow.mode->x86_emulate_cmpxchg8b(v, addr, old_lo, old_hi, - new_lo, new_hi, ctxt); + unsigned long addr; + int rc; + + rc = hvm_translate_linear_addr( + seg, offset, 8, hvm_access_write, sh_ctxt, &addr); + if ( rc ) + return rc; + + return v->arch.shadow.mode->x86_emulate_cmpxchg8b( + v, addr, old_lo, old_hi, new_lo, new_hi, sh_ctxt); } - -struct x86_emulate_ops shadow_emulator_ops = { - .read_std = sh_x86_emulate_read_std, - .write_std = sh_x86_emulate_write_std, - .read_emulated = sh_x86_emulate_read_std, - .write_emulated = sh_x86_emulate_write_emulated, - .cmpxchg_emulated = sh_x86_emulate_cmpxchg_emulated, - .cmpxchg8b_emulated = sh_x86_emulate_cmpxchg8b_emulated, +static struct x86_emulate_ops hvm_shadow_emulator_ops = { + .read = hvm_emulate_read, + .insn_fetch = hvm_emulate_insn_fetch, + .write = hvm_emulate_write, + .cmpxchg = hvm_emulate_cmpxchg, + .cmpxchg8b = hvm_emulate_cmpxchg8b, }; +static int +pv_emulate_read(enum x86_segment seg, + unsigned long offset, + unsigned long *val, + unsigned int bytes, + struct x86_emulate_ctxt *ctxt) +{ + unsigned int rc; + + *val = 0; + if ( (rc = copy_from_user((void *)val, (void *)offset, bytes)) != 0 ) + { + propagate_page_fault(offset + bytes - rc, 0); /* read fault */ + return X86EMUL_PROPAGATE_FAULT; + } + + return X86EMUL_CONTINUE; +} + +static int +pv_emulate_write(enum x86_segment seg, + unsigned long offset, + unsigned long val, + unsigned int bytes, + struct x86_emulate_ctxt *ctxt) +{ + struct sh_emulate_ctxt *sh_ctxt = + container_of(ctxt, struct sh_emulate_ctxt, ctxt); + struct vcpu *v = current; + return v->arch.shadow.mode->x86_emulate_write( + v, offset, &val, bytes, sh_ctxt); +} + +static int +pv_emulate_cmpxchg(enum x86_segment seg, + unsigned long offset, + unsigned long old, + unsigned long new, + unsigned int bytes, + struct x86_emulate_ctxt *ctxt) +{ + struct sh_emulate_ctxt *sh_ctxt = + container_of(ctxt, struct sh_emulate_ctxt, ctxt); + struct vcpu *v = current; + return v->arch.shadow.mode->x86_emulate_cmpxchg( + v, offset, old, new, bytes, sh_ctxt); +} + +static int +pv_emulate_cmpxchg8b(enum x86_segment seg, + unsigned long offset, + unsigned long old_lo, + unsigned long old_hi, + unsigned long new_lo, + unsigned long new_hi, + struct x86_emulate_ctxt *ctxt) +{ + struct sh_emulate_ctxt *sh_ctxt = + container_of(ctxt, struct sh_emulate_ctxt, ctxt); + struct vcpu *v = current; + return v->arch.shadow.mode->x86_emulate_cmpxchg8b( + v, offset, old_lo, old_hi, new_lo, new_hi, sh_ctxt); +} + +static struct x86_emulate_ops pv_shadow_emulator_ops = { + .read = pv_emulate_read, + .insn_fetch = pv_emulate_read, + .write = pv_emulate_write, + .cmpxchg = pv_emulate_cmpxchg, + .cmpxchg8b = pv_emulate_cmpxchg8b, +}; + +struct x86_emulate_ops *shadow_init_emulation( + struct sh_emulate_ctxt *sh_ctxt, struct cpu_user_regs *regs) +{ + struct segment_register *creg; + struct vcpu *v = current; + unsigned long addr; + + sh_ctxt->ctxt.regs = regs; + + if ( !is_hvm_vcpu(v) ) + { + sh_ctxt->ctxt.mode = X86EMUL_MODE_HOST; + return &pv_shadow_emulator_ops; + } + + /* Segment cache initialisation. Primed with CS. */ + sh_ctxt->valid_seg_regs = 0; + creg = hvm_get_seg_reg(x86_seg_cs, sh_ctxt); + + /* Work out the emulation mode. */ + if ( hvm_long_mode_enabled(v) ) + sh_ctxt->ctxt.mode = creg->attr.fields.l ? + X86EMUL_MODE_PROT64 : X86EMUL_MODE_PROT32; + else if ( regs->eflags & X86_EFLAGS_VM ) + sh_ctxt->ctxt.mode = X86EMUL_MODE_REAL; + else + sh_ctxt->ctxt.mode = creg->attr.fields.db ? + X86EMUL_MODE_PROT32 : X86EMUL_MODE_PROT16; + + /* Attempt to prefetch whole instruction. */ + sh_ctxt->insn_buf_bytes = + (!hvm_translate_linear_addr( + x86_seg_cs, regs->eip, sizeof(sh_ctxt->insn_buf), + hvm_access_insn_fetch, sh_ctxt, &addr) && + !hvm_copy_from_guest_virt( + sh_ctxt->insn_buf, addr, sizeof(sh_ctxt->insn_buf))) + ? sizeof(sh_ctxt->insn_buf) : 0; + + return &hvm_shadow_emulator_ops; +} + /**************************************************************************/ /* Code for "promoting" a guest page to the point where the shadow code is * willing to let it be treated as a guest page table. This generally @@ -938,12 +1175,13 @@ shadow_set_p2m_entry(struct domain *d, unsigned long gfn, mfn_t mfn) void *table = sh_map_domain_page(table_mfn); unsigned long gfn_remainder = gfn; l1_pgentry_t *p2m_entry; + int rv=0; #if CONFIG_PAGING_LEVELS >= 4 if ( !p2m_next_level(d, &table_mfn, &table, &gfn_remainder, gfn, L4_PAGETABLE_SHIFT - PAGE_SHIFT, L4_PAGETABLE_ENTRIES, PGT_l3_page_table) ) - return 0; + goto out; #endif #if CONFIG_PAGING_LEVELS >= 3 // When using PAE Xen, we only allow 33 bits of pseudo-physical @@ -957,12 +1195,12 @@ shadow_set_p2m_entry(struct domain *d, unsigned long gfn, mfn_t mfn) ? 8 : L3_PAGETABLE_ENTRIES), PGT_l2_page_table) ) - return 0; + goto out; #endif if ( !p2m_next_level(d, &table_mfn, &table, &gfn_remainder, gfn, L2_PAGETABLE_SHIFT - PAGE_SHIFT, L2_PAGETABLE_ENTRIES, PGT_l1_page_table) ) - return 0; + goto out; p2m_entry = p2m_find_entry(table, &gfn_remainder, gfn, 0, L1_PAGETABLE_ENTRIES); @@ -981,9 +1219,12 @@ shadow_set_p2m_entry(struct domain *d, unsigned long gfn, mfn_t mfn) (void)__shadow_validate_guest_entry( d->vcpu[0], table_mfn, p2m_entry, sizeof(*p2m_entry)); + /* Success */ + rv = 1; + + out: sh_unmap_domain_page(table); - - return 1; + return rv; } // Allocate a new p2m table for a domain. @@ -1063,7 +1304,7 @@ sh_gfn_to_mfn_foreign(struct domain *d, unsigned long gpfn) /* Read another domain's p2m entries */ { mfn_t mfn; - unsigned long addr = gpfn << PAGE_SHIFT; + paddr_t addr = ((paddr_t)gpfn) << PAGE_SHIFT; l2_pgentry_t *l2e; l1_pgentry_t *l1e; @@ -1091,7 +1332,16 @@ sh_gfn_to_mfn_foreign(struct domain *d, unsigned long gpfn) #if CONFIG_PAGING_LEVELS >= 3 { l3_pgentry_t *l3e = sh_map_domain_page(mfn); - l3e += l3_table_offset(addr); +#if CONFIG_PAGING_LEVELS == 3 + /* On PAE hosts the p2m has eight l3 entries, not four (see + * shadow_set_p2m_entry()) so we can't use l3_table_offset. + * Instead, just count the number of l3es from zero. It's safe + * to do this because we already checked that the gfn is within + * the bounds of the p2m. */ + l3e += (addr >> L3_PAGETABLE_SHIFT); +#else + l3e += l3_table_offset(addr); +#endif if ( (l3e_get_flags(*l3e) & _PAGE_PRESENT) == 0 ) { sh_unmap_domain_page(l3e); @@ -2241,11 +2491,10 @@ void sh_update_paging_modes(struct vcpu *v) } } - if ( pagetable_get_pfn(v->arch.monitor_table) == 0 ) + if ( pagetable_is_null(v->arch.monitor_table) ) { mfn_t mmfn = shadow_make_monitor_table(v); v->arch.monitor_table = pagetable_from_mfn(mmfn); - v->arch.monitor_vtable = sh_map_domain_page(mmfn); } if ( v->arch.shadow.mode != old_mode ) @@ -2275,12 +2524,10 @@ void sh_update_paging_modes(struct vcpu *v) return; } - sh_unmap_domain_page(v->arch.monitor_vtable); old_mfn = pagetable_get_mfn(v->arch.monitor_table); v->arch.monitor_table = pagetable_null(); new_mfn = v->arch.shadow.mode->make_monitor_table(v); v->arch.monitor_table = pagetable_from_mfn(new_mfn); - v->arch.monitor_vtable = sh_map_domain_page(new_mfn); SHADOW_PRINTK("new monitor table %"SH_PRI_mfn "\n", mfn_x(new_mfn)); @@ -3080,7 +3327,8 @@ void shadow_audit_p2m(struct domain *d) //SHADOW_PRINTK("p2m audit starts\n"); - test_linear = ( (d == current->domain) && current->arch.monitor_vtable ); + test_linear = ( (d == current->domain) + && !pagetable_is_null(current->arch.monitor_table) ); if ( test_linear ) local_flush_tlb(); diff --git a/xen/arch/x86/mm/shadow/multi.c b/xen/arch/x86/mm/shadow/multi.c index c582b82b51..d2e7b302ba 100644 --- a/xen/arch/x86/mm/shadow/multi.c +++ b/xen/arch/x86/mm/shadow/multi.c @@ -581,7 +581,7 @@ guest_index(void *ptr) return (u32)((unsigned long)ptr & ~PAGE_MASK) / sizeof(guest_l1e_t); } -static inline u32 +static u32 shadow_l1_index(mfn_t *smfn, u32 guest_index) { #if (GUEST_PAGING_LEVELS == 2) && (SHADOW_PAGING_LEVELS != 2) @@ -593,7 +593,7 @@ shadow_l1_index(mfn_t *smfn, u32 guest_index) #endif } -static inline u32 +static u32 shadow_l2_index(mfn_t *smfn, u32 guest_index) { #if (GUEST_PAGING_LEVELS == 2) && (SHADOW_PAGING_LEVELS != 2) @@ -613,13 +613,13 @@ shadow_l2_index(mfn_t *smfn, u32 guest_index) #if GUEST_PAGING_LEVELS >= 4 -static inline u32 +static u32 shadow_l3_index(mfn_t *smfn, u32 guest_index) { return guest_index; } -static inline u32 +static u32 shadow_l4_index(mfn_t *smfn, u32 guest_index) { return guest_index; @@ -851,7 +851,9 @@ static inline void safe_write_entry(void *dst, void *src) * then writing the high word before the low word. */ BUILD_BUG_ON(sizeof (shadow_l1e_t) != 2 * sizeof (unsigned long)); d[0] = 0; + wmb(); d[1] = s[1]; + wmb(); d[0] = s[0]; #else /* In 32-bit and 64-bit, sizeof(pte) == sizeof(ulong) == 1 word, @@ -2582,7 +2584,8 @@ static int sh_page_fault(struct vcpu *v, mfn_t gmfn, sl1mfn=_mfn(0); shadow_l1e_t sl1e, *ptr_sl1e; paddr_t gpa; - struct x86_emulate_ctxt emul_ctxt; + struct sh_emulate_ctxt emul_ctxt; + struct x86_emulate_ops *emul_ops; int r, mmio; fetch_type_t ft = 0; @@ -2760,6 +2763,7 @@ static int sh_page_fault(struct vcpu *v, * shadow_set_l*e(), which will have crashed the guest. * Get out of the fault handler immediately. */ ASSERT(test_bit(_DOMF_dying, &d->domain_flags)); + unmap_walk(v, &gw); shadow_unlock(d); return 0; } @@ -2808,26 +2812,22 @@ static int sh_page_fault(struct vcpu *v, return EXCRET_fault_fixed; emulate: - /* Take the register set we were called with */ + if ( !shadow_mode_refcounts(d) || !guest_mode(regs) ) + goto not_a_shadow_fault; + if ( is_hvm_domain(d) ) hvm_store_cpu_guest_regs(v, regs, NULL); - emul_ctxt.regs = regs; - emul_ctxt.cr2 = va; - emul_ctxt.mode = (is_hvm_domain(d) ? - hvm_guest_x86_mode(v) : X86EMUL_MODE_HOST); - SHADOW_PRINTK("emulate: eip=%#lx\n", regs->eip); - v->arch.shadow.propagate_fault = 0; + emul_ops = shadow_init_emulation(&emul_ctxt, regs); /* * We do not emulate user writes. Instead we use them as a hint that the * page is no longer a page table. This behaviour differs from native, but * it seems very unlikely that any OS grants user access to page tables. - * We also disallow guest PTE updates from within Xen. */ - if ( (regs->error_code & PFEC_user_mode) || !guest_mode(regs) || - x86_emulate_memop(&emul_ctxt, &shadow_emulator_ops) ) + if ( (regs->error_code & PFEC_user_mode) || + x86_emulate_memop(&emul_ctxt.ctxt, emul_ops) ) { SHADOW_PRINTK("emulator failure, unshadowing mfn %#lx\n", mfn_x(gmfn)); @@ -2836,19 +2836,11 @@ static int sh_page_fault(struct vcpu *v, * to support more operations in the emulator. More likely, * though, this is a hint that this page should not be shadowed. */ shadow_remove_all_shadows(v, gmfn); - /* This means that actual missing operations will cause the - * guest to loop on the same page fault. */ - goto done; } - /* Emulation triggered another page fault? */ - if ( v->arch.shadow.propagate_fault ) - goto not_a_shadow_fault; - /* Emulator has changed the user registers: write back */ if ( is_hvm_domain(d) ) hvm_load_cpu_guest_regs(v, regs); - goto done; mmio: @@ -3787,11 +3779,11 @@ int sh_remove_l3_shadow(struct vcpu *v, mfn_t sl4mfn, mfn_t sl3mfn) * or NULL for error. */ static inline void * emulate_map_dest(struct vcpu *v, unsigned long vaddr, - struct x86_emulate_ctxt *ctxt, + struct sh_emulate_ctxt *sh_ctxt, mfn_t *mfnp) { walk_t gw; - u32 flags; + u32 flags, errcode; gfn_t gfn; mfn_t mfn; @@ -3802,13 +3794,17 @@ static inline void * emulate_map_dest(struct vcpu *v, sh_audit_gw(v, &gw); unmap_walk(v, &gw); - if ( !(flags & _PAGE_PRESENT) - || !(flags & _PAGE_RW) - || (!(flags & _PAGE_USER) && ring_3(ctxt->regs)) ) + if ( !(flags & _PAGE_PRESENT) ) { - /* This write would have faulted even on bare metal */ - v->arch.shadow.propagate_fault = 1; - return NULL; + errcode = 0; + goto page_fault; + } + + if ( !(flags & _PAGE_RW) || + (!(flags & _PAGE_USER) && ring_3(sh_ctxt->ctxt.regs)) ) + { + errcode = PFEC_page_present; + goto page_fault; } /* Attempted a write to a bad gfn? This should never happen: @@ -3818,11 +3814,19 @@ static inline void * emulate_map_dest(struct vcpu *v, ASSERT(sh_mfn_is_a_page_table(mfn)); *mfnp = mfn; return sh_map_domain_page(mfn) + (vaddr & ~PAGE_MASK); + + page_fault: + errcode |= PFEC_write_access; + if ( is_hvm_vcpu(v) ) + hvm_inject_exception(TRAP_page_fault, errcode, vaddr); + else + propagate_page_fault(vaddr, errcode); + return NULL; } int sh_x86_emulate_write(struct vcpu *v, unsigned long vaddr, void *src, - u32 bytes, struct x86_emulate_ctxt *ctxt) + u32 bytes, struct sh_emulate_ctxt *sh_ctxt) { mfn_t mfn; void *addr; @@ -3833,7 +3837,7 @@ sh_x86_emulate_write(struct vcpu *v, unsigned long vaddr, void *src, ASSERT(shadow_lock_is_acquired(v->domain)); ASSERT(((vaddr & ~PAGE_MASK) + bytes) <= PAGE_SIZE); - if ( (addr = emulate_map_dest(v, vaddr, ctxt, &mfn)) == NULL ) + if ( (addr = emulate_map_dest(v, vaddr, sh_ctxt, &mfn)) == NULL ) return X86EMUL_PROPAGATE_FAULT; memcpy(addr, src, bytes); @@ -3851,7 +3855,7 @@ sh_x86_emulate_write(struct vcpu *v, unsigned long vaddr, void *src, int sh_x86_emulate_cmpxchg(struct vcpu *v, unsigned long vaddr, unsigned long old, unsigned long new, - unsigned int bytes, struct x86_emulate_ctxt *ctxt) + unsigned int bytes, struct sh_emulate_ctxt *sh_ctxt) { mfn_t mfn; void *addr; @@ -3864,7 +3868,7 @@ sh_x86_emulate_cmpxchg(struct vcpu *v, unsigned long vaddr, if ( vaddr & (bytes-1) ) return X86EMUL_UNHANDLEABLE; - if ( (addr = emulate_map_dest(v, vaddr, ctxt, &mfn)) == NULL ) + if ( (addr = emulate_map_dest(v, vaddr, sh_ctxt, &mfn)) == NULL ) return X86EMUL_PROPAGATE_FAULT; switch ( bytes ) @@ -3900,7 +3904,7 @@ int sh_x86_emulate_cmpxchg8b(struct vcpu *v, unsigned long vaddr, unsigned long old_lo, unsigned long old_hi, unsigned long new_lo, unsigned long new_hi, - struct x86_emulate_ctxt *ctxt) + struct sh_emulate_ctxt *sh_ctxt) { mfn_t mfn; void *addr; @@ -3912,7 +3916,7 @@ sh_x86_emulate_cmpxchg8b(struct vcpu *v, unsigned long vaddr, if ( vaddr & 7 ) return X86EMUL_UNHANDLEABLE; - if ( (addr = emulate_map_dest(v, vaddr, ctxt, &mfn)) == NULL ) + if ( (addr = emulate_map_dest(v, vaddr, sh_ctxt, &mfn)) == NULL ) return X86EMUL_PROPAGATE_FAULT; old = (((u64) old_hi) << 32) | (u64) old_lo; diff --git a/xen/arch/x86/mm/shadow/private.h b/xen/arch/x86/mm/shadow/private.h index 61493b552d..6fab2789f9 100644 --- a/xen/arch/x86/mm/shadow/private.h +++ b/xen/arch/x86/mm/shadow/private.h @@ -257,9 +257,6 @@ static inline int sh_type_is_pinnable(struct vcpu *v, unsigned int t) * Various function declarations */ -/* x86 emulator support */ -extern struct x86_emulate_ops shadow_emulator_ops; - /* Hash table functions */ mfn_t shadow_hash_lookup(struct vcpu *v, unsigned long n, unsigned int t); void shadow_hash_insert(struct vcpu *v, @@ -506,6 +503,25 @@ static inline void sh_unpin(struct vcpu *v, mfn_t smfn) } } + +/**************************************************************************/ +/* PTE-write emulation. */ + +struct sh_emulate_ctxt { + struct x86_emulate_ctxt ctxt; + + /* [HVM] Cache of up to 15 bytes of instruction. */ + uint8_t insn_buf[15]; + uint8_t insn_buf_bytes; + + /* [HVM] Cache of segment registers already gathered for this emulation. */ + unsigned int valid_seg_regs; + struct segment_register seg_reg[6]; +}; + +struct x86_emulate_ops *shadow_init_emulation( + struct sh_emulate_ctxt *sh_ctxt, struct cpu_user_regs *regs); + #endif /* _XEN_SHADOW_PRIVATE_H */ /* diff --git a/xen/arch/x86/oprofile/op_model_athlon.c b/xen/arch/x86/oprofile/op_model_athlon.c index 1d8f9668f4..ec99be48b0 100644 --- a/xen/arch/x86/oprofile/op_model_athlon.c +++ b/xen/arch/x86/oprofile/op_model_athlon.c @@ -113,14 +113,15 @@ static int athlon_check_ctrs(unsigned int const cpu, unsigned long eip = regs->eip; int mode = 0; struct vcpu *v = current; - struct cpu_user_regs tmp_regs; + struct cpu_user_regs *guest_regs = guest_cpu_user_regs(); if (!guest_mode(regs) && (regs->eip == (unsigned long)svm_stgi_label)) { /* SVM guest was running when NMI occurred */ - hvm_store_cpu_guest_regs(v, &tmp_regs, NULL); - eip = tmp_regs.eip; - mode = xenoprofile_get_mode(v, &tmp_regs); + ASSERT(is_hvm_vcpu(v)); + hvm_store_cpu_guest_regs(v, guest_regs, NULL); + eip = guest_regs->eip; + mode = xenoprofile_get_mode(v, guest_regs); } else { eip = regs->eip; mode = xenoprofile_get_mode(v, regs); diff --git a/xen/arch/x86/setup.c b/xen/arch/x86/setup.c index e144b6ca47..8d9f6f9e08 100644 --- a/xen/arch/x86/setup.c +++ b/xen/arch/x86/setup.c @@ -27,6 +27,7 @@ #include #include #include +#include extern void dmi_scan_machine(void); extern void generic_apic_probe(void); @@ -273,6 +274,20 @@ static void srat_detect_node(int cpu) printk(KERN_INFO "CPU %d APIC %d -> Node %d\n", cpu, apicid, node); } +void __init move_memory(unsigned long dst, + unsigned long src_start, unsigned long src_end) +{ +#if defined(CONFIG_X86_32) + memmove((void *)dst, /* use low mapping */ + (void *)src_start, /* use low mapping */ + src_end - src_start); +#elif defined(CONFIG_X86_64) + memmove(__va(dst), + __va(src_start), + src_end - src_start); +#endif +} + void __init __start_xen(multiboot_info_t *mbi) { char __cmdline[] = "", *cmdline = __cmdline; @@ -290,6 +305,9 @@ void __init __start_xen(multiboot_info_t *mbi) .stop_bits = 1 }; + extern void early_page_fault(void); + set_intr_gate(TRAP_page_fault, &early_page_fault); + /* Parse the command-line options. */ if ( (mbi->flags & MBI_CMDLINE) && (mbi->cmdline != 0) ) cmdline = __va(mbi->cmdline); @@ -415,15 +433,8 @@ void __init __start_xen(multiboot_info_t *mbi) initial_images_start = xenheap_phys_end; initial_images_end = initial_images_start + modules_length; -#if defined(CONFIG_X86_32) - memmove((void *)initial_images_start, /* use low mapping */ - (void *)mod[0].mod_start, /* use low mapping */ - mod[mbi->mods_count-1].mod_end - mod[0].mod_start); -#elif defined(CONFIG_X86_64) - memmove(__va(initial_images_start), - __va(mod[0].mod_start), - mod[mbi->mods_count-1].mod_end - mod[0].mod_start); -#endif + move_memory(initial_images_start, + mod[0].mod_start, mod[mbi->mods_count-1].mod_end); /* Initialise boot-time allocator with all RAM situated after modules. */ xenheap_phys_start = init_boot_allocator(__pa(&_end)); @@ -471,10 +482,54 @@ void __init __start_xen(multiboot_info_t *mbi) #endif } + if ( kexec_crash_area.size > 0 ) + { + unsigned long kdump_start, kdump_size, k; + + /* Mark images pages as free for now. */ + + init_boot_pages(initial_images_start, initial_images_end); + + kdump_start = kexec_crash_area.start; + kdump_size = kexec_crash_area.size; + + printk("Kdump: %luMB (%lukB) at 0x%lx\n", + kdump_size >> 20, + kdump_size >> 10, + kdump_start); + + if ( (kdump_start & ~PAGE_MASK) || (kdump_size & ~PAGE_MASK) ) + panic("Kdump parameters not page aligned\n"); + + kdump_start >>= PAGE_SHIFT; + kdump_size >>= PAGE_SHIFT; + + /* Allocate pages for Kdump memory area. */ + + k = alloc_boot_pages_at(kdump_size, kdump_start); + if ( k != kdump_start ) + panic("Unable to reserve Kdump memory\n"); + + /* Allocate pages for relocated initial images. */ + + k = ((initial_images_end - initial_images_start) & ~PAGE_MASK) ? 1 : 0; + k += (initial_images_end - initial_images_start) >> PAGE_SHIFT; + + k = alloc_boot_pages(k, 1); + if ( k == 0 ) + panic("Unable to allocate initial images memory\n"); + + move_memory(k << PAGE_SHIFT, initial_images_start, initial_images_end); + + initial_images_end -= initial_images_start; + initial_images_start = k << PAGE_SHIFT; + initial_images_end += initial_images_start; + } + memguard_init(); percpu_guard_areas(); - printk("System RAM: %luMB (%lukB)\n", + printk("System RAM: %luMB (%lukB)\n", nr_pages >> (20 - PAGE_SHIFT), nr_pages << (PAGE_SHIFT - 10)); total_pages = nr_pages; diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c index 87f3f79491..11960e56fb 100644 --- a/xen/arch/x86/traps.c +++ b/xen/arch/x86/traps.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -934,6 +935,37 @@ asmlinkage int do_page_fault(struct cpu_user_regs *regs) return 0; } +/* + * Early handler to deal with spurious page faults. For example, consider a + * routine that uses a mapping immediately after installing it (making it + * present). The CPU may speculatively execute the memory access before + * executing the PTE write. The instruction will then be marked to cause a + * page fault when it is retired, despite the fact that the PTE is present and + * correct at that point in time. + */ +asmlinkage int do_early_page_fault(struct cpu_user_regs *regs) +{ + static int stuck; + static unsigned long prev_eip, prev_cr2; + unsigned long cr2 = read_cr2(); + + BUG_ON(smp_processor_id() != 0); + + if ( (regs->eip != prev_eip) || (cr2 != prev_cr2) ) + { + prev_eip = regs->eip; + prev_cr2 = cr2; + stuck = 0; + return EXCRET_not_a_fault; + } + + if ( stuck++ == 1000 ) + panic("Early fatal page fault at %04x:%p (cr2=%p, ec=%04x)\n", + regs->cs, _p(regs->eip), _p(cr2), regs->error_code); + + return EXCRET_not_a_fault; +} + long do_fpu_taskswitch(int set) { struct vcpu *v = current; @@ -1633,6 +1665,7 @@ static void unknown_nmi_error(unsigned char reason) printk("Uhhuh. NMI received for unknown reason %02x.\n", reason); printk("Dazed and confused, but trying to continue\n"); printk("Do you have a strange power saving mode enabled?\n"); + machine_crash_kexec(); } } diff --git a/xen/arch/x86/x86_32/domain_page.c b/xen/arch/x86/x86_32/domain_page.c index 2b760ce704..9080de1a87 100644 --- a/xen/arch/x86/x86_32/domain_page.c +++ b/xen/arch/x86/x86_32/domain_page.c @@ -107,7 +107,7 @@ void *map_domain_page(unsigned long mfn) spin_unlock(&cache->lock); - cache->l1tab[idx] = l1e_from_pfn(mfn, __PAGE_HYPERVISOR); + l1e_write(&cache->l1tab[idx], l1e_from_pfn(mfn, __PAGE_HYPERVISOR)); out: va = MAPCACHE_VIRT_START + (idx << PAGE_SHIFT); @@ -147,7 +147,7 @@ void unmap_domain_page(void *va) { /* /First/, zap the PTE. */ ASSERT(l1e_get_pfn(cache->l1tab[hashent->idx]) == hashent->mfn); - cache->l1tab[hashent->idx] = l1e_empty(); + l1e_write(&cache->l1tab[hashent->idx], l1e_empty()); /* /Second/, mark as garbage. */ set_bit(hashent->idx, cache->garbage); } @@ -159,7 +159,7 @@ void unmap_domain_page(void *va) else { /* /First/, zap the PTE. */ - cache->l1tab[idx] = l1e_empty(); + l1e_write(&cache->l1tab[idx], l1e_empty()); /* /Second/, mark as garbage. */ set_bit(idx, cache->garbage); } @@ -229,7 +229,7 @@ void *map_domain_page_global(unsigned long mfn) pl2e = virt_to_xen_l2e(va); pl1e = l2e_to_l1e(*pl2e) + l1_table_offset(va); - *pl1e = l1e_from_pfn(mfn, __PAGE_HYPERVISOR); + l1e_write(pl1e, l1e_from_pfn(mfn, __PAGE_HYPERVISOR)); return (void *)va; } @@ -246,35 +246,9 @@ void unmap_domain_page_global(void *va) /* /First/, we zap the PTE. */ pl2e = virt_to_xen_l2e(__va); pl1e = l2e_to_l1e(*pl2e) + l1_table_offset(__va); - *pl1e = l1e_empty(); + l1e_write(pl1e, l1e_empty()); /* /Second/, we add to the garbage map. */ idx = (__va - IOREMAP_VIRT_START) >> PAGE_SHIFT; set_bit(idx, garbage); } - -paddr_t maddr_from_mapped_domain_page(void *va) -{ - unsigned long __va = (unsigned long)va; - l2_pgentry_t *pl2e; - l1_pgentry_t *pl1e; - unsigned int idx; - struct mapcache *cache; - unsigned long mfn; - - if ( (__va >= MAPCACHE_VIRT_START) && (__va < MAPCACHE_VIRT_END) ) - { - cache = &mapcache_current_vcpu()->domain->arch.mapcache; - idx = ((unsigned long)va - MAPCACHE_VIRT_START) >> PAGE_SHIFT; - mfn = l1e_get_pfn(cache->l1tab[idx]); - } - else - { - ASSERT(__va >= IOREMAP_VIRT_START); - pl2e = virt_to_xen_l2e(__va); - pl1e = l2e_to_l1e(*pl2e) + l1_table_offset(__va); - mfn = l1e_get_pfn(*pl1e); - } - - return ((paddr_t)mfn << PAGE_SHIFT) | ((unsigned long)va & ~PAGE_MASK); -} diff --git a/xen/arch/x86/x86_32/entry.S b/xen/arch/x86/x86_32/entry.S index 0e26f46497..454e963fe3 100644 --- a/xen/arch/x86/x86_32/entry.S +++ b/xen/arch/x86/x86_32/entry.S @@ -536,6 +536,14 @@ ENTRY(spurious_interrupt_bug) pushl $TRAP_spurious_int<<16 jmp handle_exception +ENTRY(early_page_fault) + SAVE_ALL_NOSEGREGS(a) + movl %esp,%edx + pushl %edx + call do_early_page_fault + addl $4,%esp + jmp restore_all_xen + ENTRY(nmi) #ifdef CONFIG_X86_SUPERVISOR_MODE_KERNEL # NMI entry protocol is incompatible with guest kernel in ring 0. @@ -659,6 +667,7 @@ ENTRY(hypercall_table) .long do_hvm_op .long do_sysctl /* 35 */ .long do_domctl + .long do_kexec_op .rept NR_hypercalls-((.-hypercall_table)/4) .long do_ni_hypercall .endr @@ -701,6 +710,7 @@ ENTRY(hypercall_args_table) .byte 2 /* do_hvm_op */ .byte 1 /* do_sysctl */ /* 35 */ .byte 1 /* do_domctl */ + .byte 2 /* do_kexec_op */ .rept NR_hypercalls-(.-hypercall_args_table) .byte 0 /* do_ni_hypercall */ .endr diff --git a/xen/arch/x86/x86_32/mm.c b/xen/arch/x86/x86_32/mm.c index 116228a53d..4978b82dad 100644 --- a/xen/arch/x86/x86_32/mm.c +++ b/xen/arch/x86/x86_32/mm.c @@ -99,11 +99,12 @@ void __init paging_init(void) { if ( (pg = alloc_domheap_pages(NULL, PAGETABLE_ORDER, 0)) == NULL ) panic("Not enough memory to bootstrap Xen.\n"); - idle_pg_table_l2[l2_linear_offset(RDWR_MPT_VIRT_START) + i] = - l2e_from_page(pg, PAGE_HYPERVISOR | _PAGE_PSE); + l2e_write(&idle_pg_table_l2[l2_linear_offset(RDWR_MPT_VIRT_START) + i], + l2e_from_page(pg, PAGE_HYPERVISOR | _PAGE_PSE)); /* NB. Cannot be GLOBAL as shadow_mode_translate reuses this area. */ - idle_pg_table_l2[l2_linear_offset(RO_MPT_VIRT_START) + i] = - l2e_from_page(pg, (__PAGE_HYPERVISOR | _PAGE_PSE) & ~_PAGE_RW); + l2e_write(&idle_pg_table_l2[l2_linear_offset(RO_MPT_VIRT_START) + i], + l2e_from_page( + pg, (__PAGE_HYPERVISOR | _PAGE_PSE) & ~_PAGE_RW)); } /* Fill with an obvious debug pattern. */ @@ -115,8 +116,8 @@ void __init paging_init(void) { ioremap_pt = alloc_xenheap_page(); clear_page(ioremap_pt); - idle_pg_table_l2[l2_linear_offset(IOREMAP_VIRT_START) + i] = - l2e_from_page(virt_to_page(ioremap_pt), __PAGE_HYPERVISOR); + l2e_write(&idle_pg_table_l2[l2_linear_offset(IOREMAP_VIRT_START) + i], + l2e_from_page(virt_to_page(ioremap_pt), __PAGE_HYPERVISOR)); } } @@ -125,10 +126,10 @@ void __init setup_idle_pagetable(void) int i; for ( i = 0; i < PDPT_L2_ENTRIES; i++ ) - idle_pg_table_l2[l2_linear_offset(PERDOMAIN_VIRT_START) + i] = - l2e_from_page(virt_to_page(idle_vcpu[0]->domain-> - arch.mm_perdomain_pt) + i, - __PAGE_HYPERVISOR); + l2e_write(&idle_pg_table_l2[l2_linear_offset(PERDOMAIN_VIRT_START)+i], + l2e_from_page(virt_to_page(idle_vcpu[0]->domain-> + arch.mm_perdomain_pt) + i, + __PAGE_HYPERVISOR)); } void __init zap_low_mappings(l2_pgentry_t *base) @@ -136,14 +137,16 @@ void __init zap_low_mappings(l2_pgentry_t *base) int i; u32 addr; - for (i = 0; ; i++) { - addr = (i << L2_PAGETABLE_SHIFT); - if (addr >= HYPERVISOR_VIRT_START) + for ( i = 0; ; i++ ) + { + addr = i << L2_PAGETABLE_SHIFT; + if ( addr >= HYPERVISOR_VIRT_START ) break; - if (l2e_get_paddr(base[i]) != addr) + if ( l2e_get_paddr(base[i]) != addr ) continue; - base[i] = l2e_empty(); + l2e_write(&base[i], l2e_empty()); } + flush_tlb_all_pge(); } diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S index 3f2b8297c7..b28b644c33 100644 --- a/xen/arch/x86/x86_64/entry.S +++ b/xen/arch/x86/x86_64/entry.S @@ -478,6 +478,12 @@ ENTRY(double_fault) call do_double_fault ud2 +ENTRY(early_page_fault) + SAVE_ALL + movq %rsp,%rdi + call do_early_page_fault + jmp restore_all_xen + ENTRY(nmi) pushq $0 SAVE_ALL @@ -559,6 +565,7 @@ ENTRY(hypercall_table) .quad do_hvm_op .quad do_sysctl /* 35 */ .quad do_domctl + .quad do_kexec_op .rept NR_hypercalls-((.-hypercall_table)/8) .quad do_ni_hypercall .endr @@ -601,6 +608,7 @@ ENTRY(hypercall_args_table) .byte 2 /* do_hvm_op */ .byte 1 /* do_sysctl */ /* 35 */ .byte 1 /* do_domctl */ + .byte 2 /* do_kexec */ .rept NR_hypercalls-(.-hypercall_args_table) .byte 0 /* do_ni_hypercall */ .endr diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c index c2783e897d..e14d266e12 100644 --- a/xen/arch/x86/x86_64/mm.c +++ b/xen/arch/x86/x86_64/mm.c @@ -59,7 +59,7 @@ l2_pgentry_t *virt_to_xen_l2e(unsigned long v) { pl3e = page_to_virt(alloc_xen_pagetable()); clear_page(pl3e); - *pl4e = l4e_from_paddr(__pa(pl3e), __PAGE_HYPERVISOR); + l4e_write(pl4e, l4e_from_paddr(__pa(pl3e), __PAGE_HYPERVISOR)); } pl3e = l4e_to_l3e(*pl4e) + l3_table_offset(v); @@ -67,7 +67,7 @@ l2_pgentry_t *virt_to_xen_l2e(unsigned long v) { pl2e = page_to_virt(alloc_xen_pagetable()); clear_page(pl2e); - *pl3e = l3e_from_paddr(__pa(pl2e), __PAGE_HYPERVISOR); + l3e_write(pl3e, l3e_from_paddr(__pa(pl2e), __PAGE_HYPERVISOR)); } pl2e = l3e_to_l2e(*pl3e) + l2_table_offset(v); @@ -85,8 +85,8 @@ void __init paging_init(void) if ( (l2_pg = alloc_domheap_page(NULL)) == NULL ) goto nomem; l3_ro_mpt = clear_page(page_to_virt(l2_pg)); - idle_pg_table[l4_table_offset(RO_MPT_VIRT_START)] = - l4e_from_page(l2_pg, __PAGE_HYPERVISOR | _PAGE_USER); + l4e_write(&idle_pg_table[l4_table_offset(RO_MPT_VIRT_START)], + l4e_from_page(l2_pg, __PAGE_HYPERVISOR | _PAGE_USER)); /* * Allocate and map the machine-to-phys table. @@ -111,18 +111,19 @@ void __init paging_init(void) goto nomem; va = RO_MPT_VIRT_START + (i << L2_PAGETABLE_SHIFT); l2_ro_mpt = clear_page(page_to_virt(l2_pg)); - l3_ro_mpt[l3_table_offset(va)] = - l3e_from_page(l2_pg, __PAGE_HYPERVISOR | _PAGE_USER); + l3e_write(&l3_ro_mpt[l3_table_offset(va)], + l3e_from_page(l2_pg, __PAGE_HYPERVISOR | _PAGE_USER)); l2_ro_mpt += l2_table_offset(va); } /* NB. Cannot be GLOBAL as shadow_mode_translate reuses this area. */ - *l2_ro_mpt++ = l2e_from_page( - l1_pg, /*_PAGE_GLOBAL|*/_PAGE_PSE|_PAGE_USER|_PAGE_PRESENT); + l2e_write(l2_ro_mpt, l2e_from_page( + l1_pg, /*_PAGE_GLOBAL|*/_PAGE_PSE|_PAGE_USER|_PAGE_PRESENT)); + l2_ro_mpt++; } /* Set up linear page table mapping. */ - idle_pg_table[l4_table_offset(LINEAR_PT_VIRT_START)] = - l4e_from_paddr(__pa(idle_pg_table), __PAGE_HYPERVISOR); + l4e_write(&idle_pg_table[l4_table_offset(LINEAR_PT_VIRT_START)], + l4e_from_paddr(__pa(idle_pg_table), __PAGE_HYPERVISOR)); return; nomem: @@ -132,15 +133,15 @@ void __init paging_init(void) void __init setup_idle_pagetable(void) { /* Install per-domain mappings for idle domain. */ - idle_pg_table[l4_table_offset(PERDOMAIN_VIRT_START)] = - l4e_from_page( - virt_to_page(idle_vcpu[0]->domain->arch.mm_perdomain_l3), - __PAGE_HYPERVISOR); + l4e_write(&idle_pg_table[l4_table_offset(PERDOMAIN_VIRT_START)], + l4e_from_page( + virt_to_page(idle_vcpu[0]->domain->arch.mm_perdomain_l3), + __PAGE_HYPERVISOR)); } void __init zap_low_mappings(void) { - idle_pg_table[0] = l4e_empty(); + l4e_write(&idle_pg_table[0], l4e_empty()); flush_tlb_all_pge(); } diff --git a/xen/arch/x86/x86_emulate.c b/xen/arch/x86/x86_emulate.c index 2ce2dfb440..4f882556c4 100644 --- a/xen/arch/x86/x86_emulate.c +++ b/xen/arch/x86/x86_emulate.c @@ -7,25 +7,18 @@ */ #ifndef __XEN__ -#include +#include #include #include -#define dprintf(_f, _a...) printf( _f , ## _a ) #else #include #include #include -#include #include -#define dprintf(_f, _a...) gdprintk(XENLOG_WARNING, _f , ## _a ) +#undef cmpxchg #endif #include -#ifndef PFEC_write_access -#define PFEC_write_access (1U<<1) -#define PFEC_insn_fetch (1U<<4) -#endif - /* * Opcode effective-address decode tables. * Note that we only emulate instructions that have at least one memory @@ -38,6 +31,7 @@ /* Operand sizes: 8-bit operands or specified/overridden size. */ #define ByteOp (1<<0) /* 8-bit operands. */ /* Destination operand type. */ +#define DstBitBase (0<<1) /* Memory operand, bit string. */ #define ImplicitOps (1<<1) /* Implicit in opcode. No generic decode. */ #define DstReg (2<<1) /* Register operand. */ #define DstMem (3<<1) /* Memory operand. */ @@ -111,8 +105,8 @@ static uint8_t opcode_table[256] = { /* 0x90 - 0x9F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xA0 - 0xA7 */ - ByteOp|DstReg|SrcMem|Mov, DstReg|SrcMem|Mov, - ByteOp|DstMem|SrcReg|Mov, DstMem|SrcReg|Mov, + ByteOp|ImplicitOps|Mov, ImplicitOps|Mov, + ByteOp|ImplicitOps|Mov, ImplicitOps|Mov, ByteOp|ImplicitOps|Mov, ImplicitOps|Mov, 0, 0, /* 0xA8 - 0xAF */ 0, 0, ByteOp|ImplicitOps|Mov, ImplicitOps|Mov, @@ -170,17 +164,21 @@ static uint8_t twobyte_table[256] = { /* 0x90 - 0x9F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xA0 - 0xA7 */ - 0, 0, 0, DstMem|SrcReg|ModRM, 0, 0, 0, 0, + 0, 0, 0, DstBitBase|SrcReg|ModRM, 0, 0, 0, 0, /* 0xA8 - 0xAF */ - 0, 0, 0, DstMem|SrcReg|ModRM, 0, 0, 0, 0, + 0, 0, 0, DstBitBase|SrcReg|ModRM, 0, 0, 0, 0, /* 0xB0 - 0xB7 */ - ByteOp|DstMem|SrcReg|ModRM, DstMem|SrcReg|ModRM, 0, DstMem|SrcReg|ModRM, + ByteOp|DstMem|SrcReg|ModRM, DstMem|SrcReg|ModRM, + 0, DstBitBase|SrcReg|ModRM, 0, 0, ByteOp|DstReg|SrcMem|ModRM|Mov, DstReg|SrcMem16|ModRM|Mov, /* 0xB8 - 0xBF */ - 0, 0, DstMem|SrcImmByte|ModRM, DstMem|SrcReg|ModRM, + 0, 0, DstBitBase|SrcImmByte|ModRM, DstBitBase|SrcReg|ModRM, 0, 0, ByteOp|DstReg|SrcMem|ModRM|Mov, DstReg|SrcMem16|ModRM|Mov, - /* 0xC0 - 0xCF */ - 0, 0, 0, 0, 0, 0, 0, ImplicitOps|ModRM, 0, 0, 0, 0, 0, 0, 0, 0, + /* 0xC0 - 0xC7 */ + ByteOp|DstMem|SrcReg|ModRM, DstMem|SrcReg|ModRM, 0, 0, + 0, 0, 0, ImplicitOps|ModRM, + /* 0xC8 - 0xCF */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0xD0 - 0xDF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xE0 - 0xEF */ @@ -193,7 +191,12 @@ static uint8_t twobyte_table[256] = { struct operand { enum { OP_REG, OP_MEM, OP_IMM } type; unsigned int bytes; - unsigned long val, orig_val, *ptr; + unsigned long val, orig_val; + /* OP_REG: Pointer to register field. */ + unsigned long *reg; + /* OP_MEM: Segment and offset. */ + enum x86_segment mem_seg; + unsigned long mem_off; }; /* EFLAGS bit definitions. */ @@ -366,24 +369,23 @@ do{ __asm__ __volatile__ ( \ #endif /* __i386__ */ /* Fetch next part of the instruction being emulated. */ -#define _insn_fetch(_size) \ -({ unsigned long _x, _ptr = _regs.eip; \ - if ( mode == X86EMUL_MODE_REAL ) _ptr += _regs.cs << 4; \ - rc = ops->read_std(_ptr, &_x, (_size), ctxt); \ +#define insn_fetch_bytes(_size) \ +({ unsigned long _x; \ + rc = ops->insn_fetch(x86_seg_cs, _regs.eip, &_x, (_size), ctxt); \ if ( rc != 0 ) \ goto done; \ _regs.eip += (_size); \ _x; \ }) -#define insn_fetch(_type) ((_type)_insn_fetch(sizeof(_type))) +#define insn_fetch_type(_type) ((_type)insn_fetch_bytes(sizeof(_type))) -/* Access/update address held in a register, based on addressing mode. */ -#define register_address(sel, reg) \ -({ unsigned long __reg = (reg); \ - (((mode == X86EMUL_MODE_REAL) ? ((unsigned long)(sel) << 4) : 0) + \ - ((ad_bytes == sizeof(unsigned long)) ? __reg : \ - (__reg & ((1UL << (ad_bytes << 3)) - 1)))); \ +#define truncate_ea(ea) \ +({ unsigned long __ea = (ea); \ + ((ad_bytes == sizeof(unsigned long)) ? __ea : \ + (__ea & ((1UL << (ad_bytes << 3)) - 1))); \ }) + +/* Update address held in a register, based on addressing mode. */ #define register_address_increment(reg, inc) \ do { \ int _inc = (inc); /* signed type ensures sign extension to long */ \ @@ -394,17 +396,6 @@ do { \ (((reg) + _inc) & ((1UL << (ad_bytes << 3)) - 1)); \ } while (0) -/* - * We cannot handle a page fault on a data access that straddles two pages - * and faults on the second page. This is because CR2 is not equal to the - * memory operand's effective address in this case. Rather than fix up the - * effective address it is okay for us to fail the emulation. - */ -#define page_boundary_test() do { \ - if ( ((cr2 & (PAGE_SIZE-1)) == 0) && ((ea & 7) != 0) ) \ - goto bad_ea; \ -} while ( 0 ) - void * decode_register( uint8_t modrm_reg, struct cpu_user_regs *regs, int highbyte_regs) @@ -445,31 +436,6 @@ decode_register( return p; } -static void -dump_instr( - struct x86_emulate_ctxt *ctxt, - struct x86_emulate_ops *ops) -{ -#ifdef __XEN__ - int i; - unsigned long x, pc; - - pc = ctxt->regs->eip; - if ( ctxt->mode == X86EMUL_MODE_REAL ) - pc += ctxt->regs->cs << 4; - - dprintf("Instr:"); - for ( i = 0; i < 16; i++, pc++ ) - { - if ( ops->read_std(pc, &x, 1, ctxt) != 0 ) - printk(" ??"); - else - printk(" %02x", (uint8_t)x); - } - printk("\n"); -#endif -} - int x86_emulate_memop( struct x86_emulate_ctxt *ctxt, @@ -480,19 +446,13 @@ x86_emulate_memop( uint8_t b, d, sib, sib_index, sib_base, twobyte = 0, rex_prefix = 0; uint8_t modrm, modrm_mod = 0, modrm_reg = 0, modrm_rm = 0; - uint16_t *seg = &_regs.ds; /* override segment */ unsigned int op_bytes, ad_bytes, lock_prefix = 0, rep_prefix = 0, i; int rc = 0; struct operand src, dst; - unsigned long ea = 0, cr2 = ctxt->cr2; int mode = ctxt->mode; - /* - * We do not emulate faults on instruction fetch. We assume that the - * guest never executes out of a special memory area. - */ - if ( _regs.error_code & PFEC_insn_fetch ) - return -1; + enum x86_segment ea_seg = x86_seg_ds; + unsigned long ea_off = 0; switch ( mode ) { @@ -516,7 +476,7 @@ x86_emulate_memop( /* Legacy prefixes. */ for ( i = 0; i < 8; i++ ) { - switch ( b = insn_fetch(uint8_t) ) + switch ( b = insn_fetch_type(uint8_t) ) { case 0x66: /* operand-size override */ op_bytes ^= 6; /* switch between 2/4 bytes */ @@ -528,22 +488,22 @@ x86_emulate_memop( ad_bytes ^= 6; /* switch between 2/4 bytes */ break; case 0x2e: /* CS override */ - seg = &_regs.cs; + ea_seg = x86_seg_cs; break; case 0x3e: /* DS override */ - seg = &_regs.ds; + ea_seg = x86_seg_ds; break; case 0x26: /* ES override */ - seg = &_regs.es; + ea_seg = x86_seg_es; break; case 0x64: /* FS override */ - seg = &_regs.fs; + ea_seg = x86_seg_fs; break; case 0x65: /* GS override */ - seg = &_regs.gs; + ea_seg = x86_seg_gs; break; case 0x36: /* SS override */ - seg = &_regs.ss; + ea_seg = x86_seg_ss; break; case 0xf0: /* LOCK */ lock_prefix = 1; @@ -565,7 +525,7 @@ x86_emulate_memop( rex_prefix = b; if ( b & 8 ) /* REX.W */ op_bytes = 8; - b = insn_fetch(uint8_t); + b = insn_fetch_type(uint8_t); } /* Opcode byte(s). */ @@ -576,7 +536,7 @@ x86_emulate_memop( if ( b == 0x0f ) { twobyte = 1; - b = insn_fetch(uint8_t); + b = insn_fetch_type(uint8_t); d = twobyte_table[b]; } @@ -588,36 +548,40 @@ x86_emulate_memop( /* ModRM and SIB bytes. */ if ( d & ModRM ) { - modrm = insn_fetch(uint8_t); + modrm = insn_fetch_type(uint8_t); modrm_mod = (modrm & 0xc0) >> 6; modrm_reg = ((rex_prefix & 4) << 1) | ((modrm & 0x38) >> 3); modrm_rm = modrm & 0x07; if ( modrm_mod == 3 ) - { - dprintf("Cannot parse ModRM.mod == 3.\n"); goto cannot_emulate; - } if ( ad_bytes == 2 ) { /* 16-bit ModR/M decode. */ switch ( modrm_rm ) { - case 0: ea = _regs.ebx + _regs.esi; break; - case 1: ea = _regs.ebx + _regs.edi; break; - case 2: ea = _regs.ebp + _regs.esi; break; - case 3: ea = _regs.ebp + _regs.edi; break; - case 4: ea = _regs.esi; break; - case 5: ea = _regs.edi; break; - case 6: ea = _regs.ebp; break; - case 7: ea = _regs.ebx; break; + case 0: ea_off = _regs.ebx + _regs.esi; break; + case 1: ea_off = _regs.ebx + _regs.edi; break; + case 2: ea_off = _regs.ebp + _regs.esi; break; + case 3: ea_off = _regs.ebp + _regs.edi; break; + case 4: ea_off = _regs.esi; break; + case 5: ea_off = _regs.edi; break; + case 6: ea_off = _regs.ebp; break; + case 7: ea_off = _regs.ebx; break; } switch ( modrm_mod ) { - case 0: if ( modrm_rm == 6 ) ea = insn_fetch(uint16_t); break; - case 1: ea += insn_fetch(uint8_t); break; - case 2: ea += insn_fetch(uint16_t); break; + case 0: + if ( modrm_rm == 6 ) + ea_off = insn_fetch_type(int16_t); + break; + case 1: + ea_off += insn_fetch_type(int8_t); + break; + case 2: + ea_off += insn_fetch_type(int16_t); + break; } } else @@ -625,87 +589,58 @@ x86_emulate_memop( /* 32/64-bit ModR/M decode. */ if ( modrm_rm == 4 ) { - sib = insn_fetch(uint8_t); - sib_index = ((sib >> 3) & 7) | ((modrm << 2) & 8); - sib_base = (sib & 7) | ((modrm << 3) & 8); + sib = insn_fetch_type(uint8_t); + sib_index = ((sib >> 3) & 7) | ((rex_prefix << 2) & 8); + sib_base = (sib & 7) | ((rex_prefix << 3) & 8); if ( sib_index != 4 ) - ea = *(long *)decode_register(sib_index, &_regs, 0); - ea <<= (sib >> 6) & 3; + ea_off = *(long *)decode_register(sib_index, &_regs, 0); + ea_off <<= (sib >> 6) & 3; if ( (modrm_mod == 0) && ((sib_base & 7) == 5) ) - ea += insn_fetch(uint32_t); + ea_off += insn_fetch_type(int32_t); else - ea += *(long *)decode_register(sib_base, &_regs, 0); + ea_off += *(long *)decode_register(sib_base, &_regs, 0); } else { modrm_rm |= (rex_prefix & 1) << 3; - ea = *(long *)decode_register(modrm_rm, &_regs, 0); + ea_off = *(long *)decode_register(modrm_rm, &_regs, 0); } switch ( modrm_mod ) { case 0: if ( (modrm_rm & 7) != 5 ) break; - ea = insn_fetch(uint32_t); + ea_off = insn_fetch_type(int32_t); if ( mode != X86EMUL_MODE_PROT64 ) break; /* Relative to RIP of next instruction. Argh! */ - ea += _regs.eip; + ea_off += _regs.eip; if ( (d & SrcMask) == SrcImm ) - ea += (d & ByteOp) ? 1 : op_bytes; + ea_off += (d & ByteOp) ? 1 : + ((op_bytes == 8) ? 4 : op_bytes); else if ( (d & SrcMask) == SrcImmByte ) - ea += 1; + ea_off += 1; else if ( ((b == 0xf6) || (b == 0xf7)) && ((modrm_reg & 7) <= 1) ) /* Special case in Grp3: test has immediate operand. */ - ea += (d & ByteOp) ? 1 + ea_off += (d & ByteOp) ? 1 : ((op_bytes == 8) ? 4 : op_bytes); break; - case 1: ea += insn_fetch(uint8_t); break; - case 2: ea += insn_fetch(uint32_t); break; + case 1: + ea_off += insn_fetch_type(int8_t); + break; + case 2: + ea_off += insn_fetch_type(int32_t); + break; } } - ea = register_address(*seg, ea); - page_boundary_test(); + ea_off = truncate_ea(ea_off); } - /* Decode and fetch the destination operand: register or memory. */ - switch ( d & DstMask ) - { - case ImplicitOps: - /* Special instructions do their own operand decoding. */ + /* Special instructions do their own operand decoding. */ + if ( (d & DstMask) == ImplicitOps ) goto special_insn; - case DstReg: - dst.type = OP_REG; - if ( d & ByteOp ) - { - dst.ptr = decode_register(modrm_reg, &_regs, (rex_prefix == 0)); - dst.val = *(uint8_t *)dst.ptr; - dst.bytes = 1; - } - else - { - dst.ptr = decode_register(modrm_reg, &_regs, 0); - switch ( (dst.bytes = op_bytes) ) - { - case 2: dst.val = *(uint16_t *)dst.ptr; break; - case 4: dst.val = *(uint32_t *)dst.ptr; break; - case 8: dst.val = *(uint64_t *)dst.ptr; break; - } - } - break; - case DstMem: - dst.type = OP_MEM; - dst.ptr = (unsigned long *)cr2; - dst.bytes = (d & ByteOp) ? 1 : op_bytes; - if ( !(d & Mov) && /* optimisation - avoid slow emulated read */ - ((rc = ops->read_emulated((unsigned long)dst.ptr, - &dst.val, dst.bytes, ctxt)) != 0) ) - goto done; - break; - } - dst.orig_val = dst.val; /* Decode and fetch the source operand: register, memory or immediate. */ switch ( d & SrcMask ) @@ -716,18 +651,18 @@ x86_emulate_memop( src.type = OP_REG; if ( d & ByteOp ) { - src.ptr = decode_register(modrm_reg, &_regs, (rex_prefix == 0)); - src.val = src.orig_val = *(uint8_t *)src.ptr; + src.reg = decode_register(modrm_reg, &_regs, (rex_prefix == 0)); + src.val = src.orig_val = *(uint8_t *)src.reg; src.bytes = 1; } else { - src.ptr = decode_register(modrm_reg, &_regs, 0); + src.reg = decode_register(modrm_reg, &_regs, 0); switch ( (src.bytes = op_bytes) ) { - case 2: src.val = src.orig_val = *(uint16_t *)src.ptr; break; - case 4: src.val = src.orig_val = *(uint32_t *)src.ptr; break; - case 8: src.val = src.orig_val = *(uint64_t *)src.ptr; break; + case 2: src.val = src.orig_val = *(uint16_t *)src.reg; break; + case 4: src.val = src.orig_val = *(uint32_t *)src.reg; break; + case 8: src.val = src.orig_val = *(uint64_t *)src.reg; break; } } break; @@ -741,33 +676,102 @@ x86_emulate_memop( src.bytes = (d & ByteOp) ? 1 : op_bytes; srcmem_common: src.type = OP_MEM; - src.ptr = (unsigned long *)cr2; - if ( (rc = ops->read_emulated((unsigned long)src.ptr, - &src.val, src.bytes, ctxt)) != 0 ) + src.mem_seg = ea_seg; + src.mem_off = ea_off; + if ( (rc = ops->read(src.mem_seg, src.mem_off, + &src.val, src.bytes, ctxt)) != 0 ) goto done; src.orig_val = src.val; break; case SrcImm: src.type = OP_IMM; - src.ptr = (unsigned long *)_regs.eip; src.bytes = (d & ByteOp) ? 1 : op_bytes; if ( src.bytes == 8 ) src.bytes = 4; /* NB. Immediates are sign-extended as necessary. */ switch ( src.bytes ) { - case 1: src.val = insn_fetch(int8_t); break; - case 2: src.val = insn_fetch(int16_t); break; - case 4: src.val = insn_fetch(int32_t); break; + case 1: src.val = insn_fetch_type(int8_t); break; + case 2: src.val = insn_fetch_type(int16_t); break; + case 4: src.val = insn_fetch_type(int32_t); break; } break; case SrcImmByte: src.type = OP_IMM; - src.ptr = (unsigned long *)_regs.eip; src.bytes = 1; - src.val = insn_fetch(int8_t); + src.val = insn_fetch_type(int8_t); break; } + /* Decode and fetch the destination operand: register or memory. */ + switch ( d & DstMask ) + { + case DstReg: + dst.type = OP_REG; + if ( d & ByteOp ) + { + dst.reg = decode_register(modrm_reg, &_regs, (rex_prefix == 0)); + dst.val = *(uint8_t *)dst.reg; + dst.bytes = 1; + } + else + { + dst.reg = decode_register(modrm_reg, &_regs, 0); + switch ( (dst.bytes = op_bytes) ) + { + case 2: dst.val = *(uint16_t *)dst.reg; break; + case 4: dst.val = *(uint32_t *)dst.reg; break; + case 8: dst.val = *(uint64_t *)dst.reg; break; + } + } + break; + case DstBitBase: + dst.mem_off = ea_off; + if ( (d & SrcMask) == SrcImmByte ) + { + src.val &= (op_bytes << 3) - 1; + } + else + { + /* + * EA += BitOffset DIV op_bytes*8 + * BitOffset = BitOffset MOD op_byte*8 + * DIV truncates towards negative infinity. + * MOD always produces a positive result. + */ + if ( op_bytes == 2 ) + src.val = (int16_t)src.val; + else if ( op_bytes == 4 ) + src.val = (int32_t)src.val; + if ( (long)src.val < 0 ) + { + unsigned long byte_offset; + byte_offset = op_bytes + (((-src.val-1) >> 3) & ~(op_bytes-1)); + dst.mem_off -= byte_offset; + src.val = (byte_offset << 3) + src.val; + } + else + { + dst.mem_off += (src.val >> 3) & ~(op_bytes - 1); + src.val &= (op_bytes << 3) - 1; + } + } + /* Becomes a normal DstMem operation from here on. */ + d = (d & ~DstMask) | DstMem; + goto dstmem_common; + case DstMem: + dst.mem_off = ea_off; + dstmem_common: + dst.mem_seg = ea_seg; + dst.type = OP_MEM; + dst.bytes = (d & ByteOp) ? 1 : op_bytes; + if ( !(d & Mov) && /* optimisation - avoid slow emulated read */ + ((rc = ops->read(dst.mem_seg, dst.mem_off, + &dst.val, dst.bytes, ctxt)) != 0) ) + goto done; + break; + } + dst.orig_val = dst.val; + if ( twobyte ) goto twobyte_insn; @@ -822,45 +826,25 @@ x86_emulate_memop( /* Write back the register source. */ switch ( dst.bytes ) { - case 1: *(uint8_t *)src.ptr = (uint8_t)dst.val; break; - case 2: *(uint16_t *)src.ptr = (uint16_t)dst.val; break; - case 4: *src.ptr = (uint32_t)dst.val; break; /* 64b reg: zero-extend */ - case 8: *src.ptr = dst.val; break; + case 1: *(uint8_t *)src.reg = (uint8_t)dst.val; break; + case 2: *(uint16_t *)src.reg = (uint16_t)dst.val; break; + case 4: *src.reg = (uint32_t)dst.val; break; /* 64b reg: zero-extend */ + case 8: *src.reg = dst.val; break; } /* Write back the memory destination with implicit LOCK prefix. */ dst.val = src.val; lock_prefix = 1; break; - case 0xa0 ... 0xa1: /* mov */ - dst.ptr = (unsigned long *)&_regs.eax; - dst.val = src.val; - /* Source EA is not encoded via ModRM. */ - ea = register_address(*seg, _insn_fetch(ad_bytes)); - page_boundary_test(); - break; - case 0xa2 ... 0xa3: /* mov */ - dst.val = (unsigned long)_regs.eax; - /* Destination EA is not encoded via ModRM. */ - ea = register_address(*seg, _insn_fetch(ad_bytes)); - page_boundary_test(); - break; case 0x88 ... 0x8b: /* mov */ case 0xc6 ... 0xc7: /* mov (sole member of Grp11) */ dst.val = src.val; break; case 0x8f: /* pop (sole member of Grp1a) */ - /* - * If the faulting access was a read it means that the fault occurred - * when accessing the implicit stack operand. We assume the guest never - * uses special memory areas as stack space. - */ - if ( !(_regs.error_code & PFEC_write_access) ) - goto cannot_emulate; /* fault on stack access: bail */ /* 64-bit mode: POP always pops a 64-bit operand. */ if ( mode == X86EMUL_MODE_PROT64 ) dst.bytes = 8; - if ( (rc = ops->read_std(register_address(_regs.ss, _regs.esp), - &dst.val, dst.bytes, ctxt)) != 0 ) + if ( (rc = ops->read(x86_seg_ss, truncate_ea(_regs.esp), + &dst.val, dst.bytes, ctxt)) != 0 ) goto done; register_address_increment(_regs.esp, dst.bytes); break; @@ -903,14 +887,13 @@ x86_emulate_memop( case 0 ... 1: /* test */ /* Special case in Grp3: test has an immediate source operand. */ src.type = OP_IMM; - src.ptr = (unsigned long *)_regs.eip; src.bytes = (d & ByteOp) ? 1 : op_bytes; if ( src.bytes == 8 ) src.bytes = 4; switch ( src.bytes ) { - case 1: src.val = insn_fetch(int8_t); break; - case 2: src.val = insn_fetch(int16_t); break; - case 4: src.val = insn_fetch(int32_t); break; + case 1: src.val = insn_fetch_type(int8_t); break; + case 2: src.val = insn_fetch_type(int16_t); break; + case 4: src.val = insn_fetch_type(int32_t); break; } goto test; case 2: /* not */ @@ -933,24 +916,17 @@ x86_emulate_memop( emulate_1op("dec", dst, _regs.eflags); break; case 6: /* push */ - /* - * If the faulting access was a write it means that the fault - * occurred when accessing the implicit stack operand. We assume - * the guest never uses special memory areas as stack space. - */ - if ( _regs.error_code & PFEC_write_access ) - goto cannot_emulate; /* fault on stack access: bail */ /* 64-bit mode: PUSH always pushes a 64-bit operand. */ if ( mode == X86EMUL_MODE_PROT64 ) { dst.bytes = 8; - if ( (rc = ops->read_std((unsigned long)dst.ptr, - &dst.val, 8, ctxt)) != 0 ) + if ( (rc = ops->read(dst.mem_seg, dst.mem_off, + &dst.val, 8, ctxt)) != 0 ) goto done; } register_address_increment(_regs.esp, -dst.bytes); - if ( (rc = ops->write_std(register_address(_regs.ss, _regs.esp), - dst.val, dst.bytes, ctxt)) != 0 ) + if ( (rc = ops->write(x86_seg_ss, truncate_ea(_regs.esp), + dst.val, dst.bytes, ctxt)) != 0 ) goto done; dst.val = dst.orig_val; /* skanky: disable writeback */ break; @@ -969,20 +945,20 @@ x86_emulate_memop( /* The 4-byte case *is* correct: in 64-bit mode we zero-extend. */ switch ( dst.bytes ) { - case 1: *(uint8_t *)dst.ptr = (uint8_t)dst.val; break; - case 2: *(uint16_t *)dst.ptr = (uint16_t)dst.val; break; - case 4: *dst.ptr = (uint32_t)dst.val; break; /* 64b: zero-ext */ - case 8: *dst.ptr = dst.val; break; + case 1: *(uint8_t *)dst.reg = (uint8_t)dst.val; break; + case 2: *(uint16_t *)dst.reg = (uint16_t)dst.val; break; + case 4: *dst.reg = (uint32_t)dst.val; break; /* 64b: zero-ext */ + case 8: *dst.reg = dst.val; break; } break; case OP_MEM: if ( lock_prefix ) - rc = ops->cmpxchg_emulated( - (unsigned long)dst.ptr, dst.orig_val, + rc = ops->cmpxchg( + dst.mem_seg, dst.mem_off, dst.orig_val, dst.val, dst.bytes, ctxt); else - rc = ops->write_emulated( - (unsigned long)dst.ptr, dst.val, dst.bytes, ctxt); + rc = ops->write( + dst.mem_seg, dst.mem_off, dst.val, dst.bytes, ctxt); if ( rc != 0 ) goto done; default: @@ -1011,50 +987,51 @@ x86_emulate_memop( } switch ( b ) { + case 0xa0 ... 0xa1: /* mov moffs,{%al,%ax,%eax,%rax} */ + /* Source EA is not encoded via ModRM. */ + dst.type = OP_REG; + dst.reg = (unsigned long *)&_regs.eax; + dst.bytes = (d & ByteOp) ? 1 : op_bytes; + if ( (rc = ops->read(ea_seg, insn_fetch_bytes(ad_bytes), + &dst.val, dst.bytes, ctxt)) != 0 ) + goto done; + break; + case 0xa2 ... 0xa3: /* mov {%al,%ax,%eax,%rax},moffs */ + /* Destination EA is not encoded via ModRM. */ + dst.type = OP_MEM; + dst.mem_seg = ea_seg; + dst.mem_off = insn_fetch_bytes(ad_bytes); + dst.bytes = (d & ByteOp) ? 1 : op_bytes; + dst.val = (unsigned long)_regs.eax; + break; case 0xa4 ... 0xa5: /* movs */ dst.type = OP_MEM; dst.bytes = (d & ByteOp) ? 1 : op_bytes; - if ( _regs.error_code & PFEC_write_access ) - { - /* Write fault: destination is special memory. */ - dst.ptr = (unsigned long *)cr2; - if ( (rc = ops->read_std(register_address(*seg, _regs.esi), - &dst.val, dst.bytes, ctxt)) != 0 ) - goto done; - ea = register_address(_regs.es, _regs.edi); - } - else - { - /* Read fault: source is special memory. */ - dst.ptr = (unsigned long *)register_address(_regs.es, _regs.edi); - if ( (rc = ops->read_emulated(cr2, &dst.val, - dst.bytes, ctxt)) != 0 ) - goto done; - ea = register_address(*seg, _regs.esi); - } - page_boundary_test(); + dst.mem_seg = x86_seg_es; + dst.mem_off = truncate_ea(_regs.edi); + if ( (rc = ops->read(ea_seg, truncate_ea(_regs.esi), + &dst.val, dst.bytes, ctxt)) != 0 ) + goto done; register_address_increment( _regs.esi, (_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes); register_address_increment( _regs.edi, (_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes); break; case 0xaa ... 0xab: /* stos */ - ea = register_address(_regs.es, _regs.edi); - page_boundary_test(); dst.type = OP_MEM; dst.bytes = (d & ByteOp) ? 1 : op_bytes; - dst.ptr = (unsigned long *)cr2; + dst.mem_seg = x86_seg_es; + dst.mem_off = truncate_ea(_regs.edi); dst.val = _regs.eax; register_address_increment( _regs.edi, (_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes); break; case 0xac ... 0xad: /* lods */ - ea = register_address(*seg, _regs.esi); - page_boundary_test(); dst.type = OP_REG; dst.bytes = (d & ByteOp) ? 1 : op_bytes; - dst.ptr = (unsigned long *)&_regs.eax; - if ( (rc = ops->read_emulated(cr2, &dst.val, dst.bytes, ctxt)) != 0 ) + dst.reg = (unsigned long *)&_regs.eax; + if ( (rc = ops->read(ea_seg, truncate_ea(_regs.esi), + &dst.val, dst.bytes, ctxt)) != 0 ) goto done; register_address_increment( _regs.esi, (_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes); @@ -1116,19 +1093,16 @@ x86_emulate_memop( { /* Failure: write the value we saw to EAX. */ dst.type = OP_REG; - dst.ptr = (unsigned long *)&_regs.eax; + dst.reg = (unsigned long *)&_regs.eax; } break; case 0xa3: bt: /* bt */ - src.val &= (dst.bytes << 3) - 1; /* only subword offset */ emulate_2op_SrcV_nobyte("bt", src, dst, _regs.eflags); break; case 0xb3: btr: /* btr */ - src.val &= (dst.bytes << 3) - 1; /* only subword offset */ emulate_2op_SrcV_nobyte("btr", src, dst, _regs.eflags); break; case 0xab: bts: /* bts */ - src.val &= (dst.bytes << 3) - 1; /* only subword offset */ emulate_2op_SrcV_nobyte("bts", src, dst, _regs.eflags); break; case 0xb6 ... 0xb7: /* movzx */ @@ -1136,7 +1110,6 @@ x86_emulate_memop( dst.val = (d & ByteOp) ? (uint8_t)src.val : (uint16_t)src.val; break; case 0xbb: btc: /* btc */ - src.val &= (dst.bytes << 3) - 1; /* only subword offset */ emulate_2op_SrcV_nobyte("btc", src, dst, _regs.eflags); break; case 0xba: /* Grp8 */ @@ -1152,6 +1125,16 @@ x86_emulate_memop( dst.bytes = op_bytes; dst.val = (d & ByteOp) ? (int8_t)src.val : (int16_t)src.val; break; + case 0xc0 ... 0xc1: /* xadd */ + /* Write back the register source. */ + switch ( dst.bytes ) + { + case 1: *(uint8_t *)src.reg = (uint8_t)dst.val; break; + case 2: *(uint16_t *)src.reg = (uint16_t)dst.val; break; + case 4: *src.reg = (uint32_t)dst.val; break; /* 64b reg: zero-extend */ + case 8: *src.reg = dst.val; break; + } + goto add; } goto writeback; @@ -1167,8 +1150,8 @@ x86_emulate_memop( #if defined(__i386__) { unsigned long old_lo, old_hi; - if ( ((rc = ops->read_emulated(cr2+0, &old_lo, 4, ctxt)) != 0) || - ((rc = ops->read_emulated(cr2+4, &old_hi, 4, ctxt)) != 0) ) + if ( ((rc = ops->read(ea_seg, ea_off+0, &old_lo, 4, ctxt)) != 0) || + ((rc = ops->read(ea_seg, ea_off+4, &old_hi, 4, ctxt)) != 0) ) goto done; if ( (old_lo != _regs.eax) || (old_hi != _regs.edx) ) { @@ -1176,15 +1159,15 @@ x86_emulate_memop( _regs.edx = old_hi; _regs.eflags &= ~EFLG_ZF; } - else if ( ops->cmpxchg8b_emulated == NULL ) + else if ( ops->cmpxchg8b == NULL ) { rc = X86EMUL_UNHANDLEABLE; goto done; } else { - if ( (rc = ops->cmpxchg8b_emulated(cr2, old_lo, old_hi, _regs.ebx, - _regs.ecx, ctxt)) != 0 ) + if ( (rc = ops->cmpxchg8b(ea_seg, ea_off, old_lo, old_hi, + _regs.ebx, _regs.ecx, ctxt)) != 0 ) goto done; _regs.eflags |= EFLG_ZF; } @@ -1193,7 +1176,7 @@ x86_emulate_memop( #elif defined(__x86_64__) { unsigned long old, new; - if ( (rc = ops->read_emulated(cr2, &old, 8, ctxt)) != 0 ) + if ( (rc = ops->read(ea_seg, ea_off, &old, 8, ctxt)) != 0 ) goto done; if ( ((uint32_t)(old>>0) != (uint32_t)_regs.eax) || ((uint32_t)(old>>32) != (uint32_t)_regs.edx) ) @@ -1205,7 +1188,7 @@ x86_emulate_memop( else { new = (_regs.ecx<<32)|(uint32_t)_regs.ebx; - if ( (rc = ops->cmpxchg_emulated(cr2, old, new, 8, ctxt)) != 0 ) + if ( (rc = ops->cmpxchg(ea_seg, ea_off, old, new, 8, ctxt)) != 0 ) goto done; _regs.eflags |= EFLG_ZF; } @@ -1216,58 +1199,15 @@ x86_emulate_memop( goto writeback; cannot_emulate: - dprintf("Cannot emulate %02x\n", b); - dump_instr(ctxt, ops); - return -1; - - bad_ea: - dprintf("Access faulted on page boundary (cr2=%lx,ea=%lx).\n", cr2, ea); - dump_instr(ctxt, ops); - show_execution_state(ctxt->regs); - return -1; -} - #ifdef __XEN__ - -#include -#include - -int -x86_emulate_read_std( - unsigned long addr, - unsigned long *val, - unsigned int bytes, - struct x86_emulate_ctxt *ctxt) -{ - unsigned int rc; - - *val = 0; - - if ( (rc = copy_from_user((void *)val, (void *)addr, bytes)) != 0 ) + gdprintk(XENLOG_DEBUG, "Instr:"); + for ( ea_off = ctxt->regs->eip; ea_off < _regs.eip; ea_off++ ) { - propagate_page_fault(addr + bytes - rc, 0); /* read fault */ - return X86EMUL_PROPAGATE_FAULT; + unsigned long x; + ops->insn_fetch(x86_seg_cs, ea_off, &x, 1, ctxt); + printk(" %02x", (uint8_t)x); } - - return X86EMUL_CONTINUE; -} - -int -x86_emulate_write_std( - unsigned long addr, - unsigned long val, - unsigned int bytes, - struct x86_emulate_ctxt *ctxt) -{ - unsigned int rc; - - if ( (rc = copy_to_user((void *)addr, (void *)&val, bytes)) != 0 ) - { - propagate_page_fault(addr + bytes - rc, PFEC_write_access); - return X86EMUL_PROPAGATE_FAULT; - } - - return X86EMUL_CONTINUE; -} - + printk("\n"); #endif + return -1; +} diff --git a/xen/common/Makefile b/xen/common/Makefile index 3cb9eb7855..6f3a9a097e 100644 --- a/xen/common/Makefile +++ b/xen/common/Makefile @@ -7,6 +7,7 @@ obj-y += event_channel.o obj-y += grant_table.o obj-y += kernel.o obj-y += keyhandler.o +obj-y += kexec.o obj-y += lib.o obj-y += memory.o obj-y += multicall.o diff --git a/xen/common/domain.c b/xen/common/domain.c index 238ee037a7..51a2e78d31 100644 --- a/xen/common/domain.c +++ b/xen/common/domain.c @@ -283,8 +283,8 @@ void domain_shutdown(struct domain *d, u8 reason) if ( d->domain_id == 0 ) dom0_shutdown(reason); - d->shutdown_code = reason; - set_bit(_DOMF_shutdown, &d->domain_flags); + if ( !test_and_set_bit(_DOMF_shutdown, &d->domain_flags) ) + d->shutdown_code = reason; for_each_vcpu ( d, v ) vcpu_sleep_nosync(v); diff --git a/xen/common/domctl.c b/xen/common/domctl.c index bf6b8473aa..b05ecd1099 100644 --- a/xen/common/domctl.c +++ b/xen/common/domctl.c @@ -126,7 +126,7 @@ void getdomaininfo(struct domain *d, struct xen_domctl_getdomaininfo *info) info->tot_pages = d->tot_pages; info->max_pages = d->max_pages; - info->shared_info_frame = __pa(d->shared_info) >> PAGE_SHIFT; + info->shared_info_frame = mfn_to_gmfn(d, __pa(d->shared_info)>>PAGE_SHIFT); memcpy(info->handle, d->handle, sizeof(xen_domain_handle_t)); } diff --git a/xen/common/kexec.c b/xen/common/kexec.c new file mode 100644 index 0000000000..885afae80c --- /dev/null +++ b/xen/common/kexec.c @@ -0,0 +1,343 @@ +/****************************************************************************** + * kexec.c - Achitecture independent kexec code for Xen + * + * Xen port written by: + * - Simon 'Horms' Horman + * - Magnus Damm + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DEFINE_PER_CPU (crash_note_t, crash_notes); +cpumask_t crash_saved_cpus; +int crashing_cpu; + +xen_kexec_image_t kexec_image[KEXEC_IMAGE_NR]; + +#define KEXEC_FLAG_DEFAULT_POS (KEXEC_IMAGE_NR + 0) +#define KEXEC_FLAG_CRASH_POS (KEXEC_IMAGE_NR + 1) +#define KEXEC_FLAG_IN_PROGRESS (KEXEC_IMAGE_NR + 2) + +unsigned long kexec_flags = 0; /* the lowest bits are for KEXEC_IMAGE... */ + +spinlock_t kexec_lock = SPIN_LOCK_UNLOCKED; + +xen_kexec_reserve_t kexec_crash_area; + +static void __init parse_crashkernel(char *str) +{ + unsigned long start, size; + + size = parse_size_and_unit(str, &str); + if ( *str == '@' ) + start = parse_size_and_unit(str+1, NULL); + else + start = 0; + + if ( start && size ) + { + kexec_crash_area.start = start; + kexec_crash_area.size = size; + } +} +custom_param("crashkernel", parse_crashkernel); + +static void one_cpu_only(void) +{ + /* Only allow the first cpu to continue - force other cpus to spin */ + if ( test_and_set_bit(KEXEC_FLAG_IN_PROGRESS, &kexec_flags) ) + { + while (1); + } +} + +/* Save the registers in the per-cpu crash note buffer */ + +void machine_crash_save_cpu(void) +{ + int cpu = smp_processor_id(); + crash_note_t *cntp; + + if ( !cpu_test_and_set(cpu, crash_saved_cpus) ) + { + cntp = &per_cpu(crash_notes, cpu); + elf_core_save_regs(&cntp->core.desc.desc.pr_reg, + &cntp->xen_regs.desc.desc); + + /* setup crash "CORE" note */ + setup_crash_note(cntp, core, CORE_STR, CORE_STR_LEN, NT_PRSTATUS); + + /* setup crash note "Xen", XEN_ELFNOTE_CRASH_REGS */ + setup_crash_note(cntp, xen_regs, XEN_STR, XEN_STR_LEN, + XEN_ELFNOTE_CRASH_REGS); + } +} + +/* Setup the single Xen specific info crash note */ + +crash_xen_info_t *machine_crash_save_info(void) +{ + int cpu = smp_processor_id(); + crash_note_t *cntp; + crash_xen_info_t *info; + + BUG_ON(!cpu_test_and_set(cpu, crash_saved_cpus)); + + cntp = &per_cpu(crash_notes, cpu); + + /* setup crash note "Xen", XEN_ELFNOTE_CRASH_INFO */ + setup_crash_note(cntp, xen_info, XEN_STR, XEN_STR_LEN, + XEN_ELFNOTE_CRASH_INFO); + + info = &cntp->xen_info.desc.desc; + + info->xen_major_version = xen_major_version(); + info->xen_minor_version = xen_minor_version(); + info->xen_extra_version = __pa(xen_extra_version()); + info->xen_changeset = __pa(xen_changeset()); + info->xen_compiler = __pa(xen_compiler()); + info->xen_compile_date = __pa(xen_compile_date()); + info->xen_compile_time = __pa(xen_compile_time()); + info->tainted = tainted; + + return info; +} + +void machine_crash_kexec(void) +{ + int pos; + xen_kexec_image_t *image; + + one_cpu_only(); + + machine_crash_save_cpu(); + crashing_cpu = smp_processor_id(); + + machine_crash_shutdown(); + + pos = (test_bit(KEXEC_FLAG_CRASH_POS, &kexec_flags) != 0); + + if ( test_bit(KEXEC_IMAGE_CRASH_BASE + pos, &kexec_flags) ) + { + image = &kexec_image[KEXEC_IMAGE_CRASH_BASE + pos]; + machine_kexec(image); /* Does not return */ + } +} + +static void do_crashdump_trigger(unsigned char key) +{ + printk("triggering crashdump\n"); + machine_crash_kexec(); +} + +static __init int register_crashdump_trigger(void) +{ + register_keyhandler('c', do_crashdump_trigger, "trigger a crashdump"); + return 0; +} +__initcall(register_crashdump_trigger); + +static int kexec_get_reserve(xen_kexec_range_t *range) +{ + range->start = kexec_crash_area.start; + range->size = kexec_crash_area.size; + return 0; +} + +extern unsigned long _text; + +static int kexec_get_xen(xen_kexec_range_t *range) +{ + range->start = virt_to_maddr(&_text); + range->size = (unsigned long)&_end - (unsigned long)&_text; + return 0; +} + +static int kexec_get_cpu(xen_kexec_range_t *range) +{ + if ( range->nr < 0 || range->nr >= num_present_cpus() ) + return -EINVAL; + + range->start = __pa((unsigned long)&per_cpu(crash_notes, range->nr)); + range->size = sizeof(crash_note_t); + return 0; +} + +static int kexec_get_range(XEN_GUEST_HANDLE(void) uarg) +{ + xen_kexec_range_t range; + int ret = -EINVAL; + + if ( unlikely(copy_from_guest(&range, uarg, 1)) ) + return -EFAULT; + + switch ( range.range ) + { + case KEXEC_RANGE_MA_CRASH: + ret = kexec_get_reserve(&range); + break; + case KEXEC_RANGE_MA_XEN: + ret = kexec_get_xen(&range); + break; + case KEXEC_RANGE_MA_CPU: + ret = kexec_get_cpu(&range); + break; + } + + if ( ret == 0 && unlikely(copy_to_guest(uarg, &range, 1)) ) + return -EFAULT; + + return ret; +} + +static int kexec_load_get_bits(int type, int *base, int *bit) +{ + switch ( type ) + { + case KEXEC_TYPE_DEFAULT: + *base = KEXEC_IMAGE_DEFAULT_BASE; + *bit = KEXEC_FLAG_DEFAULT_POS; + break; + case KEXEC_TYPE_CRASH: + *base = KEXEC_IMAGE_CRASH_BASE; + *bit = KEXEC_FLAG_CRASH_POS; + break; + default: + return -1; + } + return 0; +} + +static int kexec_load_unload(unsigned long op, XEN_GUEST_HANDLE(void) uarg) +{ + xen_kexec_load_t load; + xen_kexec_image_t *image; + int base, bit, pos; + int ret = 0; + + if ( unlikely(copy_from_guest(&load, uarg, 1)) ) + return -EFAULT; + + if ( kexec_load_get_bits(load.type, &base, &bit) ) + return -EINVAL; + + pos = (test_bit(bit, &kexec_flags) != 0); + + /* Load the user data into an unused image */ + if ( op == KEXEC_CMD_kexec_load ) + { + image = &kexec_image[base + !pos]; + + BUG_ON(test_bit((base + !pos), &kexec_flags)); /* must be free */ + + memcpy(image, &load.image, sizeof(*image)); + + if ( !(ret = machine_kexec_load(load.type, base + !pos, image)) ) + { + /* Set image present bit */ + set_bit((base + !pos), &kexec_flags); + + /* Make new image the active one */ + change_bit(bit, &kexec_flags); + } + } + + /* Unload the old image if present and load successful */ + if ( ret == 0 && !test_bit(KEXEC_FLAG_IN_PROGRESS, &kexec_flags) ) + { + if ( test_and_clear_bit((base + pos), &kexec_flags) ) + { + image = &kexec_image[base + pos]; + machine_kexec_unload(load.type, base + pos, image); + } + } + + return ret; +} + +static int kexec_exec(XEN_GUEST_HANDLE(void) uarg) +{ + xen_kexec_exec_t exec; + xen_kexec_image_t *image; + int base, bit, pos; + + if ( unlikely(copy_from_guest(&exec, uarg, 1)) ) + return -EFAULT; + + if ( kexec_load_get_bits(exec.type, &base, &bit) ) + return -EINVAL; + + pos = (test_bit(bit, &kexec_flags) != 0); + + /* Only allow kexec/kdump into loaded images */ + if ( !test_bit(base + pos, &kexec_flags) ) + return -ENOENT; + + switch (exec.type) + { + case KEXEC_TYPE_DEFAULT: + image = &kexec_image[base + pos]; + one_cpu_only(); + machine_reboot_kexec(image); /* Does not return */ + break; + case KEXEC_TYPE_CRASH: + machine_crash_kexec(); /* Does not return */ + break; + } + + return -EINVAL; /* never reached */ +} + +long do_kexec_op(unsigned long op, XEN_GUEST_HANDLE(void) uarg) +{ + unsigned long flags; + int ret = -EINVAL; + + if ( !IS_PRIV(current->domain) ) + return -EPERM; + + switch ( op ) + { + case KEXEC_CMD_kexec_get_range: + ret = kexec_get_range(uarg); + break; + case KEXEC_CMD_kexec_load: + case KEXEC_CMD_kexec_unload: + spin_lock_irqsave(&kexec_lock, flags); + if (!test_bit(KEXEC_FLAG_IN_PROGRESS, &kexec_flags)) + { + ret = kexec_load_unload(op, uarg); + } + spin_unlock_irqrestore(&kexec_lock, flags); + break; + case KEXEC_CMD_kexec: + ret = kexec_exec(uarg); + break; + } + + return ret; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/common/memory.c b/xen/common/memory.c index 33ead8e259..f17ae4b913 100644 --- a/xen/common/memory.c +++ b/xen/common/memory.c @@ -328,7 +328,7 @@ static long memory_exchange(XEN_GUEST_HANDLE(xen_memory_exchange_t) arg) (exch.out.address_bits < (get_order_from_pages(max_page) + PAGE_SHIFT)) ) { - if ( exch.out.address_bits < 31 ) + if ( exch.out.address_bits < dma_bitsize ) { rc = -ENOMEM; goto fail_early; @@ -541,7 +541,7 @@ long do_memory_op(unsigned long cmd, XEN_GUEST_HANDLE(void) arg) (reservation.address_bits < (get_order_from_pages(max_page) + PAGE_SHIFT)) ) { - if ( reservation.address_bits < 31 ) + if ( reservation.address_bits < dma_bitsize ) return start_extent; args.memflags = MEMF_dma; } diff --git a/xen/common/page_alloc.c b/xen/common/page_alloc.c index 93fc193e17..1697226e90 100644 --- a/xen/common/page_alloc.c +++ b/xen/common/page_alloc.c @@ -45,19 +45,40 @@ static char opt_badpage[100] = ""; string_param("badpage", opt_badpage); +/* + * Bit width of the DMA heap. + */ +unsigned int dma_bitsize = CONFIG_DMA_BITSIZE; +unsigned long max_dma_mfn = (1UL << (CONFIG_DMA_BITSIZE - PAGE_SHIFT)) - 1; +static void parse_dma_bits(char *s) +{ + unsigned int v = simple_strtol(s, NULL, 0); + if ( v >= (sizeof(long)*8 + PAGE_SHIFT) ) + { + dma_bitsize = sizeof(long)*8 + PAGE_SHIFT; + max_dma_mfn = ~0UL; + } + else + { + dma_bitsize = v; + max_dma_mfn = (1UL << (dma_bitsize - PAGE_SHIFT)) - 1; + } +} +custom_param("dma_bits", parse_dma_bits); + /* * Amount of memory to reserve in a low-memory (<4GB) pool for specific * allocation requests. Ordinary requests will not fall back to the * lowmem emergency pool. */ -static unsigned long lowmem_emergency_pool_pages; -static void parse_lowmem_emergency_pool(char *s) +static unsigned long dma_emergency_pool_pages; +static void parse_dma_emergency_pool(char *s) { unsigned long long bytes; bytes = parse_size_and_unit(s, NULL); - lowmem_emergency_pool_pages = bytes >> PAGE_SHIFT; + dma_emergency_pool_pages = bytes >> PAGE_SHIFT; } -custom_param("lowmem_emergency_pool", parse_lowmem_emergency_pool); +custom_param("dma_emergency_pool", parse_dma_emergency_pool); #define round_pgdown(_p) ((_p)&PAGE_MASK) #define round_pgup(_p) (((_p)+(PAGE_SIZE-1))&PAGE_MASK) @@ -216,26 +237,37 @@ void init_boot_pages(paddr_t ps, paddr_t pe) } } -unsigned long alloc_boot_pages(unsigned long nr_pfns, unsigned long pfn_align) +unsigned long alloc_boot_pages_at(unsigned long nr_pfns, unsigned long pfn_at) { - unsigned long pg, i; + unsigned long i; - for ( pg = 0; (pg + nr_pfns) < max_page; pg += pfn_align ) + for ( i = 0; i < nr_pfns; i++ ) + if ( allocated_in_map(pfn_at + i) ) + break; + + if ( i == nr_pfns ) { - for ( i = 0; i < nr_pfns; i++ ) - if ( allocated_in_map(pg + i) ) - break; - - if ( i == nr_pfns ) - { - map_alloc(pg, nr_pfns); - return pg; - } + map_alloc(pfn_at, nr_pfns); + return pfn_at; } return 0; } +unsigned long alloc_boot_pages(unsigned long nr_pfns, unsigned long pfn_align) +{ + unsigned long pg, i = 0; + + for ( pg = 0; (pg + nr_pfns) < max_page; pg += pfn_align ) + { + i = alloc_boot_pages_at(nr_pfns, pg); + if (i != 0) + break; + } + + return i; +} + /************************* @@ -248,7 +280,7 @@ unsigned long alloc_boot_pages(unsigned long nr_pfns, unsigned long pfn_align) #define NR_ZONES 3 #define pfn_dom_zone_type(_pfn) \ - (((_pfn) <= MAX_DMADOM_PFN) ? MEMZONE_DMADOM : MEMZONE_DOM) + (((_pfn) <= max_dma_mfn) ? MEMZONE_DMADOM : MEMZONE_DOM) static struct list_head heap[NR_ZONES][MAX_NUMNODES][MAX_ORDER+1]; @@ -278,6 +310,8 @@ void end_boot_allocator(void) if ( curr_free ) init_heap_pages(pfn_dom_zone_type(i), mfn_to_page(i), 1); } + + printk("Domain heap initialised: DMA width %u bits\n", dma_bitsize); } /* @@ -575,13 +609,13 @@ void init_domheap_pages(paddr_t ps, paddr_t pe) s_tot = round_pgup(ps) >> PAGE_SHIFT; e_tot = round_pgdown(pe) >> PAGE_SHIFT; - s_dma = min(s_tot, MAX_DMADOM_PFN + 1); - e_dma = min(e_tot, MAX_DMADOM_PFN + 1); + s_dma = min(s_tot, max_dma_mfn + 1); + e_dma = min(e_tot, max_dma_mfn + 1); if ( s_dma < e_dma ) init_heap_pages(MEMZONE_DMADOM, mfn_to_page(s_dma), e_dma - s_dma); - s_nrm = max(s_tot, MAX_DMADOM_PFN + 1); - e_nrm = max(e_tot, MAX_DMADOM_PFN + 1); + s_nrm = max(s_tot, max_dma_mfn + 1); + e_nrm = max(e_tot, max_dma_mfn + 1); if ( s_nrm < e_nrm ) init_heap_pages(MEMZONE_DOM, mfn_to_page(s_nrm), e_nrm - s_nrm); } @@ -655,7 +689,7 @@ struct page_info *__alloc_domheap_pages( if ( unlikely(pg == NULL) && ((order > MAX_ORDER) || (avail_heap_pages(MEMZONE_DMADOM,-1) < - (lowmem_emergency_pool_pages + (1UL << order)))) ) + (dma_emergency_pool_pages + (1UL << order)))) ) return NULL; } @@ -799,8 +833,8 @@ unsigned long avail_domheap_pages(void) avail_nrm = avail_heap_pages(MEMZONE_DOM,-1); avail_dma = avail_heap_pages(MEMZONE_DMADOM,-1); - if ( avail_dma > lowmem_emergency_pool_pages ) - avail_dma -= lowmem_emergency_pool_pages; + if ( avail_dma > dma_emergency_pool_pages ) + avail_dma -= dma_emergency_pool_pages; else avail_dma = 0; diff --git a/xen/drivers/char/console.c b/xen/drivers/char/console.c index 6a97fc2965..1bca60cc80 100644 --- a/xen/drivers/char/console.c +++ b/xen/drivers/char/console.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -865,6 +866,8 @@ void panic(const char *fmt, ...) debugger_trap_immediate(); + machine_crash_kexec(); + if ( opt_noreboot ) { machine_halt(); diff --git a/xen/include/asm-ia64/config.h b/xen/include/asm-ia64/config.h index 05910e4310..e1306d744b 100644 --- a/xen/include/asm-ia64/config.h +++ b/xen/include/asm-ia64/config.h @@ -41,7 +41,7 @@ #define CONFIG_IOSAPIC #define supervisor_mode_kernel (0) -#define MAX_DMADOM_PFN (0x7FFFFFFFUL >> PAGE_SHIFT) /* 31 addressable bits */ +#define CONFIG_DMA_BITSIZE 30 /* If PERFC is used, include privop maps. */ #ifdef PERF_COUNTERS @@ -125,6 +125,7 @@ extern char _end[]; /* standard ELF symbol */ // from include/asm-ia64/smp.h #define get_cpu() smp_processor_id() #define put_cpu() do {} while(0) +#define put_cpu_no_resched() do{} while (0) // needed for common/dom0_ops.c until hyperthreading is supported #ifdef CONFIG_SMP @@ -166,6 +167,8 @@ extern int smp_num_siblings; #define ____cacheline_aligned_in_smp ____cacheline_aligned #endif +#define CONFIG_PERFMON + #ifndef __ASSEMBLY__ #include "asm/types.h" // for u64 #include "linux/linkage.h" // for asmlinkage which is used by @@ -280,9 +283,4 @@ struct screen_info { }; /* Define CONFIG_PRIVIFY to support privified OS (deprecated). */ #undef CONFIG_PRIVIFY -/* Necessary for hvm_vioapic.c */ -#define vcpu_vlapic(vcpu) (&(vcpu)->arch.arch_vmx.vlapic) -#define vlapic_vcpu(vpic) (container_of((vpic), struct vcpu, \ - arch.arch_vmx.vlapic)) - #endif /* _IA64_CONFIG_H_ */ diff --git a/xen/include/asm-ia64/debugger.h b/xen/include/asm-ia64/debugger.h index 104cea9bfc..3e6a51d2e4 100644 --- a/xen/include/asm-ia64/debugger.h +++ b/xen/include/asm-ia64/debugger.h @@ -23,11 +23,13 @@ #define __ASM_DEBUGGER_H__ // this number is an arbitary number which is not used for any other purpose +// __builtin_trap() 0x0 // ski 0x80001, 0x80002 // kdb 0x80100, 0x80101 // kprobe 0x80200, jprobe 0x80300 // kgdb 0x6665 // gdb 0x99998 (#define IA64_BREAKPOINT 0x00003333300LL) +// ltrace 0x80001 (NOTE: this conflicts ski) // cdb should handle 0 and CDB_BREAK_NUM. #define CDB_BREAK_NUM 0x80800 diff --git a/xen/include/asm-ia64/domain.h b/xen/include/asm-ia64/domain.h index cfe7c5e9a3..b327bcb27b 100644 --- a/xen/include/asm-ia64/domain.h +++ b/xen/include/asm-ia64/domain.h @@ -42,7 +42,7 @@ extern void panic_domain(struct pt_regs *, const char *, ...) __attribute__ ((noreturn, format (printf, 2, 3))); struct mm_struct { - pgd_t * pgd; + volatile pgd_t * pgd; // atomic_t mm_users; /* How many users with user space? */ }; @@ -124,8 +124,6 @@ struct arch_domain { struct last_vcpu last_vcpu[NR_CPUS]; - struct arch_vmx_domain arch_vmx; /* Virtual Machine Extensions */ - #ifdef CONFIG_XEN_IA64_TLB_TRACK struct tlb_track* tlb_track; #endif @@ -211,6 +209,10 @@ struct arch_vcpu { #define IO_PORTS_PADDR 0x00000ffffc000000UL #define IO_PORTS_SIZE 0x0000000004000000UL +int +do_perfmon_op(unsigned long cmd, + XEN_GUEST_HANDLE(void) arg1, unsigned long arg2); + #endif /* __ASM_DOMAIN_H__ */ /* diff --git a/xen/include/asm-ia64/elf.h b/xen/include/asm-ia64/elf.h new file mode 100644 index 0000000000..0bdbd9c735 --- /dev/null +++ b/xen/include/asm-ia64/elf.h @@ -0,0 +1,30 @@ +#ifndef __IA64_ELF_H__ +#define __IA64_ELF_H__ + +#include /* for printk() used in stub */ + +typedef struct { + unsigned long dummy; +} ELF_Gregset; + +typedef struct { + unsigned long dummy; +} crash_xen_core_t; + +static inline void elf_core_save_regs(ELF_Gregset *core_regs, + crash_xen_core_t *xen_core_regs) +{ + printk("STUB: " __FILE__ ": %s: not implemented\n", __FUNCTION__); +} + +#endif /* __IA64_ELF_H__ */ + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/include/asm-ia64/event.h b/xen/include/asm-ia64/event.h index ef4c33f2be..c3d67cfa2c 100644 --- a/xen/include/asm-ia64/event.h +++ b/xen/include/asm-ia64/event.h @@ -70,6 +70,8 @@ static inline int arch_virq_is_global(int virq) switch ( virq ) { case VIRQ_ITC: + case VIRQ_MCA_CMC: + case VIRQ_MCA_CPE: rc = 0; break; default: diff --git a/xen/include/asm-ia64/grant_table.h b/xen/include/asm-ia64/grant_table.h index 1d0bded64f..6b62d6abd3 100644 --- a/xen/include/asm-ia64/grant_table.h +++ b/xen/include/asm-ia64/grant_table.h @@ -23,8 +23,21 @@ void guest_physmap_add_page(struct domain *d, unsigned long gpfn, unsigned long } while (0) +/* XXX + * somewhere appropriate + * those constand shouldn't be pre-defined and + * those area should be reserved by EFI MD. + */ +/* Guest phsyical address of shared_info */ +#define IA64_SHARED_INFO_PADDR (1UL << 40) +/* Guest phsyical address of mapped_regs */ +#define IA64_XMAPPEDREGS_BASE_PADDR (IA64_SHARED_INFO_PADDR + XSI_SIZE) +#define IA64_XMAPPEDREGS_PADDR(vcpu_id) \ + (IA64_XMAPPEDREGS_BASE_PADDR + \ + (vcpu_id) * max_t(unsigned long, PAGE_SIZE, XMAPPEDREGS_SIZE)) + /* Guest physical address of the grant table. */ -#define IA64_GRANT_TABLE_PADDR (1UL << 40) +#define IA64_GRANT_TABLE_PADDR IA64_XMAPPEDREGS_PADDR(NR_CPUS) #define gnttab_shared_maddr(d, t, i) \ virt_to_maddr((char*)(t)->shared + ((i) << PAGE_SHIFT)) diff --git a/xen/include/asm-ia64/kexec.h b/xen/include/asm-ia64/kexec.h new file mode 100644 index 0000000000..45756e8f64 --- /dev/null +++ b/xen/include/asm-ia64/kexec.h @@ -0,0 +1,25 @@ +#ifndef __IA64_KEXEC_H__ +#define __IA64_KEXEC_H__ + +#include /* for printk() used in stub */ +#include +#include +#include + +static inline void machine_kexec(xen_kexec_image_t *image) +{ + printk("STUB: " __FILE__ ": %s: not implemented\n", __FUNCTION__); +} + +#endif /* __IA64_KEXEC_H__ */ + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ + diff --git a/xen/include/asm-ia64/linux-null/asm/signal.h b/xen/include/asm-ia64/linux-null/asm/signal.h new file mode 100644 index 0000000000..29f4f4cd69 --- /dev/null +++ b/xen/include/asm-ia64/linux-null/asm/signal.h @@ -0,0 +1 @@ +/* This file is intentionally left empty. */ diff --git a/xen/include/asm-ia64/linux-null/linux/capability.h b/xen/include/asm-ia64/linux-null/linux/capability.h new file mode 100644 index 0000000000..29f4f4cd69 --- /dev/null +++ b/xen/include/asm-ia64/linux-null/linux/capability.h @@ -0,0 +1 @@ +/* This file is intentionally left empty. */ diff --git a/xen/include/asm-ia64/linux-null/linux/completion.h b/xen/include/asm-ia64/linux-null/linux/completion.h new file mode 100644 index 0000000000..29f4f4cd69 --- /dev/null +++ b/xen/include/asm-ia64/linux-null/linux/completion.h @@ -0,0 +1 @@ +/* This file is intentionally left empty. */ diff --git a/xen/include/asm-ia64/linux-null/linux/mount.h b/xen/include/asm-ia64/linux-null/linux/mount.h new file mode 100644 index 0000000000..29f4f4cd69 --- /dev/null +++ b/xen/include/asm-ia64/linux-null/linux/mount.h @@ -0,0 +1 @@ +/* This file is intentionally left empty. */ diff --git a/xen/include/asm-ia64/linux-null/linux/pagemap.h b/xen/include/asm-ia64/linux-null/linux/pagemap.h new file mode 100644 index 0000000000..29f4f4cd69 --- /dev/null +++ b/xen/include/asm-ia64/linux-null/linux/pagemap.h @@ -0,0 +1 @@ +/* This file is intentionally left empty. */ diff --git a/xen/include/asm-ia64/linux-null/linux/poll.h b/xen/include/asm-ia64/linux-null/linux/poll.h new file mode 100644 index 0000000000..29f4f4cd69 --- /dev/null +++ b/xen/include/asm-ia64/linux-null/linux/poll.h @@ -0,0 +1 @@ +/* This file is intentionally left empty. */ diff --git a/xen/include/asm-ia64/linux-null/linux/rcupdate.h b/xen/include/asm-ia64/linux-null/linux/rcupdate.h new file mode 100644 index 0000000000..29f4f4cd69 --- /dev/null +++ b/xen/include/asm-ia64/linux-null/linux/rcupdate.h @@ -0,0 +1 @@ +/* This file is intentionally left empty. */ diff --git a/xen/include/asm-ia64/linux-null/linux/sysctl.h b/xen/include/asm-ia64/linux-null/linux/sysctl.h new file mode 100644 index 0000000000..29f4f4cd69 --- /dev/null +++ b/xen/include/asm-ia64/linux-null/linux/sysctl.h @@ -0,0 +1 @@ +/* This file is intentionally left empty. */ diff --git a/xen/include/asm-ia64/linux-null/linux/vfs.h b/xen/include/asm-ia64/linux-null/linux/vfs.h new file mode 100644 index 0000000000..29f4f4cd69 --- /dev/null +++ b/xen/include/asm-ia64/linux-null/linux/vfs.h @@ -0,0 +1 @@ +/* This file is intentionally left empty. */ diff --git a/xen/include/asm-ia64/linux-null/linux/vmalloc.h b/xen/include/asm-ia64/linux-null/linux/vmalloc.h new file mode 100644 index 0000000000..29f4f4cd69 --- /dev/null +++ b/xen/include/asm-ia64/linux-null/linux/vmalloc.h @@ -0,0 +1 @@ +/* This file is intentionally left empty. */ diff --git a/xen/include/asm-ia64/linux-xen/asm-generic/README.origin b/xen/include/asm-ia64/linux-xen/asm-generic/README.origin new file mode 100644 index 0000000000..6cd78a86fb --- /dev/null +++ b/xen/include/asm-ia64/linux-xen/asm-generic/README.origin @@ -0,0 +1,8 @@ +# Source files in this directory are near-identical copies of linux-2.6.13 +# files: + +# NOTE: ALL changes to these files should be clearly marked +# (e.g. with #ifdef XEN or XEN in a comment) so that they can be +# easily updated to future versions of the corresponding Linux files. + +pgtable-nopud.h -> linux/include/asm-generic/pgtable-nopud.h diff --git a/xen/include/asm-ia64/linux/asm-generic/pgtable-nopud.h b/xen/include/asm-ia64/linux-xen/asm-generic/pgtable-nopud.h similarity index 92% rename from xen/include/asm-ia64/linux/asm-generic/pgtable-nopud.h rename to xen/include/asm-ia64/linux-xen/asm-generic/pgtable-nopud.h index 82e29f0ce4..7e375bbdd0 100644 --- a/xen/include/asm-ia64/linux/asm-generic/pgtable-nopud.h +++ b/xen/include/asm-ia64/linux-xen/asm-generic/pgtable-nopud.h @@ -35,10 +35,18 @@ static inline void pgd_clear(pgd_t *pgd) { } */ #define set_pgd(pgdptr, pgdval) set_pud((pud_t *)(pgdptr), (pud_t) { pgdval }) +#ifndef XEN static inline pud_t * pud_offset(pgd_t * pgd, unsigned long address) { return (pud_t *)pgd; } +#else +static inline volatile pud_t * +pud_offset(volatile pgd_t * pgd, unsigned long address) +{ + return (volatile pud_t *)pgd; +} +#endif #define pud_val(x) (pgd_val((x).pgd)) #define __pud(x) ((pud_t) { __pgd(x) } ) diff --git a/xen/include/asm-ia64/linux-xen/asm/README.origin b/xen/include/asm-ia64/linux-xen/asm/README.origin index 3f2a03f704..f8d8375770 100644 --- a/xen/include/asm-ia64/linux-xen/asm/README.origin +++ b/xen/include/asm-ia64/linux-xen/asm/README.origin @@ -30,3 +30,7 @@ types.h -> linux/include/asm-ia64/types.h # The files below are from Linux-2.6.16 iosapic.h -> linux/include/asm-ia64/iosapic.h + +# The files below are from Linux-2.6.16.33 +perfmon.h -> linux/include/asm-ia64/perfmon.h +perfmon_default_smpl.h -> linux/include/asm-ia64/perfmon_default_smpl.h diff --git a/xen/include/asm-ia64/linux-xen/asm/mca_asm.h b/xen/include/asm-ia64/linux-xen/asm/mca_asm.h index 3cf3c38919..01c278afc2 100644 --- a/xen/include/asm-ia64/linux-xen/asm/mca_asm.h +++ b/xen/include/asm-ia64/linux-xen/asm/mca_asm.h @@ -59,8 +59,8 @@ #ifdef XEN #define GET_THIS_PADDR(reg, var) \ - movl reg = THIS_CPU(var) \ - tpa reg = reg + mov reg = IA64_KR(PER_CPU_DATA);; \ + addl reg = THIS_CPU(var) - PERCPU_ADDR, reg #else #define GET_THIS_PADDR(reg, var) \ mov reg = IA64_KR(PER_CPU_DATA);; \ diff --git a/xen/include/asm-ia64/linux-xen/asm/perfmon.h b/xen/include/asm-ia64/linux-xen/asm/perfmon.h new file mode 100644 index 0000000000..23dec44bc3 --- /dev/null +++ b/xen/include/asm-ia64/linux-xen/asm/perfmon.h @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2001-2003 Hewlett-Packard Co + * Stephane Eranian + */ + +#ifndef _ASM_IA64_PERFMON_H +#define _ASM_IA64_PERFMON_H + +#ifdef XEN +#include +#ifndef pt_regs +#define pt_regs cpu_user_regs +#endif +struct cpu_user_regs; +#endif + +/* + * perfmon comamnds supported on all CPU models + */ +#define PFM_WRITE_PMCS 0x01 +#define PFM_WRITE_PMDS 0x02 +#define PFM_READ_PMDS 0x03 +#define PFM_STOP 0x04 +#define PFM_START 0x05 +#define PFM_ENABLE 0x06 /* obsolete */ +#define PFM_DISABLE 0x07 /* obsolete */ +#define PFM_CREATE_CONTEXT 0x08 +#define PFM_DESTROY_CONTEXT 0x09 /* obsolete use close() */ +#define PFM_RESTART 0x0a +#define PFM_PROTECT_CONTEXT 0x0b /* obsolete */ +#define PFM_GET_FEATURES 0x0c +#define PFM_DEBUG 0x0d +#define PFM_UNPROTECT_CONTEXT 0x0e /* obsolete */ +#define PFM_GET_PMC_RESET_VAL 0x0f +#define PFM_LOAD_CONTEXT 0x10 +#define PFM_UNLOAD_CONTEXT 0x11 + +/* + * PMU model specific commands (may not be supported on all PMU models) + */ +#define PFM_WRITE_IBRS 0x20 +#define PFM_WRITE_DBRS 0x21 + +/* + * context flags + */ +#define PFM_FL_NOTIFY_BLOCK 0x01 /* block task on user level notifications */ +#define PFM_FL_SYSTEM_WIDE 0x02 /* create a system wide context */ +#define PFM_FL_OVFL_NO_MSG 0x80 /* do not post overflow/end messages for notification */ + +/* + * event set flags + */ +#define PFM_SETFL_EXCL_IDLE 0x01 /* exclude idle task (syswide only) XXX: DO NOT USE YET */ + +/* + * PMC flags + */ +#define PFM_REGFL_OVFL_NOTIFY 0x1 /* send notification on overflow */ +#define PFM_REGFL_RANDOM 0x2 /* randomize sampling interval */ + +/* + * PMD/PMC/IBR/DBR return flags (ignored on input) + * + * Those flags are used on output and must be checked in case EAGAIN is returned + * by any of the calls using a pfarg_reg_t or pfarg_dbreg_t structure. + */ +#define PFM_REG_RETFL_NOTAVAIL (1UL<<31) /* set if register is implemented but not available */ +#define PFM_REG_RETFL_EINVAL (1UL<<30) /* set if register entry is invalid */ +#define PFM_REG_RETFL_MASK (PFM_REG_RETFL_NOTAVAIL|PFM_REG_RETFL_EINVAL) + +#define PFM_REG_HAS_ERROR(flag) (((flag) & PFM_REG_RETFL_MASK) != 0) + +typedef unsigned char pfm_uuid_t[16]; /* custom sampling buffer identifier type */ + +/* + * Request structure used to define a context + */ +typedef struct { + pfm_uuid_t ctx_smpl_buf_id; /* which buffer format to use (if needed) */ + unsigned long ctx_flags; /* noblock/block */ + unsigned short ctx_nextra_sets; /* number of extra event sets (you always get 1) */ + unsigned short ctx_reserved1; /* for future use */ + int ctx_fd; /* return arg: unique identification for context */ + void *ctx_smpl_vaddr; /* return arg: virtual address of sampling buffer, is used */ + unsigned long ctx_reserved2[11];/* for future use */ +} pfarg_context_t; + +/* + * Request structure used to write/read a PMC or PMD + */ +typedef struct { + unsigned int reg_num; /* which register */ + unsigned short reg_set; /* event set for this register */ + unsigned short reg_reserved1; /* for future use */ + + unsigned long reg_value; /* initial pmc/pmd value */ + unsigned long reg_flags; /* input: pmc/pmd flags, return: reg error */ + + unsigned long reg_long_reset; /* reset after buffer overflow notification */ + unsigned long reg_short_reset; /* reset after counter overflow */ + + unsigned long reg_reset_pmds[4]; /* which other counters to reset on overflow */ + unsigned long reg_random_seed; /* seed value when randomization is used */ + unsigned long reg_random_mask; /* bitmask used to limit random value */ + unsigned long reg_last_reset_val;/* return: PMD last reset value */ + + unsigned long reg_smpl_pmds[4]; /* which pmds are accessed when PMC overflows */ + unsigned long reg_smpl_eventid; /* opaque sampling event identifier */ + + unsigned long reg_reserved2[3]; /* for future use */ +} pfarg_reg_t; + +typedef struct { + unsigned int dbreg_num; /* which debug register */ + unsigned short dbreg_set; /* event set for this register */ + unsigned short dbreg_reserved1; /* for future use */ + unsigned long dbreg_value; /* value for debug register */ + unsigned long dbreg_flags; /* return: dbreg error */ + unsigned long dbreg_reserved2[1]; /* for future use */ +} pfarg_dbreg_t; + +typedef struct { + unsigned int ft_version; /* perfmon: major [16-31], minor [0-15] */ + unsigned int ft_reserved; /* reserved for future use */ + unsigned long reserved[4]; /* for future use */ +} pfarg_features_t; + +typedef struct { + pid_t load_pid; /* process to load the context into */ + unsigned short load_set; /* first event set to load */ + unsigned short load_reserved1; /* for future use */ + unsigned long load_reserved2[3]; /* for future use */ +} pfarg_load_t; + +typedef struct { + int msg_type; /* generic message header */ + int msg_ctx_fd; /* generic message header */ + unsigned long msg_ovfl_pmds[4]; /* which PMDs overflowed */ + unsigned short msg_active_set; /* active set at the time of overflow */ + unsigned short msg_reserved1; /* for future use */ + unsigned int msg_reserved2; /* for future use */ + unsigned long msg_tstamp; /* for perf tuning/debug */ +} pfm_ovfl_msg_t; + +typedef struct { + int msg_type; /* generic message header */ + int msg_ctx_fd; /* generic message header */ + unsigned long msg_tstamp; /* for perf tuning */ +} pfm_end_msg_t; + +typedef struct { + int msg_type; /* type of the message */ + int msg_ctx_fd; /* unique identifier for the context */ + unsigned long msg_tstamp; /* for perf tuning */ +} pfm_gen_msg_t; + +#define PFM_MSG_OVFL 1 /* an overflow happened */ +#define PFM_MSG_END 2 /* task to which context was attached ended */ + +typedef union { + pfm_ovfl_msg_t pfm_ovfl_msg; + pfm_end_msg_t pfm_end_msg; + pfm_gen_msg_t pfm_gen_msg; +} pfm_msg_t; + +/* + * Define the version numbers for both perfmon as a whole and the sampling buffer format. + */ +#define PFM_VERSION_MAJ 2U +#define PFM_VERSION_MIN 0U +#define PFM_VERSION (((PFM_VERSION_MAJ&0xffff)<<16)|(PFM_VERSION_MIN & 0xffff)) +#define PFM_VERSION_MAJOR(x) (((x)>>16) & 0xffff) +#define PFM_VERSION_MINOR(x) ((x) & 0xffff) + + +/* + * miscellaneous architected definitions + */ +#define PMU_FIRST_COUNTER 4 /* first counting monitor (PMC/PMD) */ +#define PMU_MAX_PMCS 256 /* maximum architected number of PMC registers */ +#define PMU_MAX_PMDS 256 /* maximum architected number of PMD registers */ + +#ifdef __KERNEL__ + +extern long perfmonctl(int fd, int cmd, void *arg, int narg); + +typedef struct { + void (*handler)(int irq, void *arg, struct pt_regs *regs); +} pfm_intr_handler_desc_t; + +extern void pfm_save_regs (struct task_struct *); +extern void pfm_load_regs (struct task_struct *); + +extern void pfm_exit_thread(struct task_struct *); +extern int pfm_use_debug_registers(struct task_struct *); +extern int pfm_release_debug_registers(struct task_struct *); +extern void pfm_syst_wide_update_task(struct task_struct *, unsigned long info, int is_ctxswin); +extern void pfm_inherit(struct task_struct *task, struct pt_regs *regs); +extern void pfm_init_percpu(void); +extern void pfm_handle_work(void); +extern int pfm_install_alt_pmu_interrupt(pfm_intr_handler_desc_t *h); +extern int pfm_remove_alt_pmu_interrupt(pfm_intr_handler_desc_t *h); + + + +/* + * Reset PMD register flags + */ +#define PFM_PMD_SHORT_RESET 0 +#define PFM_PMD_LONG_RESET 1 + +typedef union { + unsigned int val; + struct { + unsigned int notify_user:1; /* notify user program of overflow */ + unsigned int reset_ovfl_pmds:1; /* reset overflowed PMDs */ + unsigned int block_task:1; /* block monitored task on kernel exit */ + unsigned int mask_monitoring:1; /* mask monitors via PMCx.plm */ + unsigned int reserved:28; /* for future use */ + } bits; +} pfm_ovfl_ctrl_t; + +typedef struct { + unsigned char ovfl_pmd; /* index of overflowed PMD */ + unsigned char ovfl_notify; /* =1 if monitor requested overflow notification */ + unsigned short active_set; /* event set active at the time of the overflow */ + pfm_ovfl_ctrl_t ovfl_ctrl; /* return: perfmon controls to set by handler */ + + unsigned long pmd_last_reset; /* last reset value of of the PMD */ + unsigned long smpl_pmds[4]; /* bitmask of other PMD of interest on overflow */ + unsigned long smpl_pmds_values[PMU_MAX_PMDS]; /* values for the other PMDs of interest */ + unsigned long pmd_value; /* current 64-bit value of the PMD */ + unsigned long pmd_eventid; /* eventid associated with PMD */ +} pfm_ovfl_arg_t; + + +typedef struct { + char *fmt_name; + pfm_uuid_t fmt_uuid; + size_t fmt_arg_size; + unsigned long fmt_flags; + + int (*fmt_validate)(struct task_struct *task, unsigned int flags, int cpu, void *arg); + int (*fmt_getsize)(struct task_struct *task, unsigned int flags, int cpu, void *arg, unsigned long *size); + int (*fmt_init)(struct task_struct *task, void *buf, unsigned int flags, int cpu, void *arg); + int (*fmt_handler)(struct task_struct *task, void *buf, pfm_ovfl_arg_t *arg, struct pt_regs *regs, unsigned long stamp); + int (*fmt_restart)(struct task_struct *task, pfm_ovfl_ctrl_t *ctrl, void *buf, struct pt_regs *regs); + int (*fmt_restart_active)(struct task_struct *task, pfm_ovfl_ctrl_t *ctrl, void *buf, struct pt_regs *regs); + int (*fmt_exit)(struct task_struct *task, void *buf, struct pt_regs *regs); + + struct list_head fmt_list; +} pfm_buffer_fmt_t; + +extern int pfm_register_buffer_fmt(pfm_buffer_fmt_t *fmt); +extern int pfm_unregister_buffer_fmt(pfm_uuid_t uuid); + +/* + * perfmon interface exported to modules + */ +extern int pfm_mod_read_pmds(struct task_struct *, void *req, unsigned int nreq, struct pt_regs *regs); +extern int pfm_mod_write_pmcs(struct task_struct *, void *req, unsigned int nreq, struct pt_regs *regs); +extern int pfm_mod_write_ibrs(struct task_struct *task, void *req, unsigned int nreq, struct pt_regs *regs); +extern int pfm_mod_write_dbrs(struct task_struct *task, void *req, unsigned int nreq, struct pt_regs *regs); + +/* + * describe the content of the local_cpu_date->pfm_syst_info field + */ +#define PFM_CPUINFO_SYST_WIDE 0x1 /* if set a system wide session exists */ +#define PFM_CPUINFO_DCR_PP 0x2 /* if set the system wide session has started */ +#define PFM_CPUINFO_EXCL_IDLE 0x4 /* the system wide session excludes the idle task */ + +/* + * sysctl control structure. visible to sampling formats + */ +typedef struct { + int debug; /* turn on/off debugging via syslog */ + int debug_ovfl; /* turn on/off debug printk in overflow handler */ + int fastctxsw; /* turn on/off fast (unsecure) ctxsw */ + int expert_mode; /* turn on/off value checking */ +} pfm_sysctl_t; +extern pfm_sysctl_t pfm_sysctl; + + +#endif /* __KERNEL__ */ + +#endif /* _ASM_IA64_PERFMON_H */ diff --git a/xen/include/asm-ia64/linux-xen/asm/perfmon_default_smpl.h b/xen/include/asm-ia64/linux-xen/asm/perfmon_default_smpl.h new file mode 100644 index 0000000000..48822c0811 --- /dev/null +++ b/xen/include/asm-ia64/linux-xen/asm/perfmon_default_smpl.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2002-2003 Hewlett-Packard Co + * Stephane Eranian + * + * This file implements the default sampling buffer format + * for Linux/ia64 perfmon subsystem. + */ +#ifndef __PERFMON_DEFAULT_SMPL_H__ +#define __PERFMON_DEFAULT_SMPL_H__ 1 + +#define PFM_DEFAULT_SMPL_UUID { \ + 0x4d, 0x72, 0xbe, 0xc0, 0x06, 0x64, 0x41, 0x43, 0x82, 0xb4, 0xd3, 0xfd, 0x27, 0x24, 0x3c, 0x97} + +/* + * format specific parameters (passed at context creation) + */ +typedef struct { + unsigned long buf_size; /* size of the buffer in bytes */ + unsigned int flags; /* buffer specific flags */ + unsigned int res1; /* for future use */ + unsigned long reserved[2]; /* for future use */ +} pfm_default_smpl_arg_t; + +/* + * combined context+format specific structure. Can be passed + * to PFM_CONTEXT_CREATE + */ +typedef struct { + pfarg_context_t ctx_arg; + pfm_default_smpl_arg_t buf_arg; +} pfm_default_smpl_ctx_arg_t; + +/* + * This header is at the beginning of the sampling buffer returned to the user. + * It is directly followed by the first record. + */ +typedef struct { + unsigned long hdr_count; /* how many valid entries */ + unsigned long hdr_cur_offs; /* current offset from top of buffer */ + unsigned long hdr_reserved2; /* reserved for future use */ + + unsigned long hdr_overflows; /* how many times the buffer overflowed */ + unsigned long hdr_buf_size; /* how many bytes in the buffer */ + + unsigned int hdr_version; /* contains perfmon version (smpl format diffs) */ + unsigned int hdr_reserved1; /* for future use */ + unsigned long hdr_reserved[10]; /* for future use */ +} pfm_default_smpl_hdr_t; + +/* + * Entry header in the sampling buffer. The header is directly followed + * with the values of the PMD registers of interest saved in increasing + * index order: PMD4, PMD5, and so on. How many PMDs are present depends + * on how the session was programmed. + * + * In the case where multiple counters overflow at the same time, multiple + * entries are written consecutively. + * + * last_reset_value member indicates the initial value of the overflowed PMD. + */ +typedef struct { + int pid; /* thread id (for NPTL, this is gettid()) */ + unsigned char reserved1[3]; /* reserved for future use */ + unsigned char ovfl_pmd; /* index of overflowed PMD */ + + unsigned long last_reset_val; /* initial value of overflowed PMD */ + unsigned long ip; /* where did the overflow interrupt happened */ + unsigned long tstamp; /* ar.itc when entering perfmon intr. handler */ + + unsigned short cpu; /* cpu on which the overfow occured */ + unsigned short set; /* event set active when overflow ocurred */ + int tgid; /* thread group id (for NPTL, this is getpid()) */ +} pfm_default_smpl_entry_t; + +#define PFM_DEFAULT_MAX_PMDS 64 /* how many pmds supported by data structures (sizeof(unsigned long) */ +#define PFM_DEFAULT_MAX_ENTRY_SIZE (sizeof(pfm_default_smpl_entry_t)+(sizeof(unsigned long)*PFM_DEFAULT_MAX_PMDS)) +#define PFM_DEFAULT_SMPL_MIN_BUF_SIZE (sizeof(pfm_default_smpl_hdr_t)+PFM_DEFAULT_MAX_ENTRY_SIZE) + +#define PFM_DEFAULT_SMPL_VERSION_MAJ 2U +#define PFM_DEFAULT_SMPL_VERSION_MIN 0U +#define PFM_DEFAULT_SMPL_VERSION (((PFM_DEFAULT_SMPL_VERSION_MAJ&0xffff)<<16)|(PFM_DEFAULT_SMPL_VERSION_MIN & 0xffff)) + +#endif /* __PERFMON_DEFAULT_SMPL_H__ */ diff --git a/xen/include/asm-ia64/linux-xen/asm/pgalloc.h b/xen/include/asm-ia64/linux-xen/asm/pgalloc.h index 6e847d9ae2..9adb3b7c7e 100644 --- a/xen/include/asm-ia64/linux-xen/asm/pgalloc.h +++ b/xen/include/asm-ia64/linux-xen/asm/pgalloc.h @@ -92,10 +92,17 @@ static inline pgd_t *pgd_alloc(struct mm_struct *mm) return pgtable_quicklist_alloc(); } +#ifndef XEN static inline void pgd_free(pgd_t * pgd) { pgtable_quicklist_free(pgd); } +#else +static inline void pgd_free(volatile pgd_t * pgd) +{ + pgtable_quicklist_free((void*)pgd); +} +#endif static inline void pud_populate(struct mm_struct *mm, pud_t * pud_entry, pmd_t * pmd) @@ -105,8 +112,8 @@ pud_populate(struct mm_struct *mm, pud_t * pud_entry, pmd_t * pmd) #ifdef XEN static inline int -pud_cmpxchg_rel(struct mm_struct *mm, pud_t * pud_entry, - pmd_t * old_pmd, pmd_t * new_pmd) +pud_cmpxchg_rel(struct mm_struct *mm, volatile pud_t * pud_entry, + pmd_t * old_pmd, volatile pmd_t * new_pmd) { #ifdef CONFIG_SMP unsigned long r; @@ -127,10 +134,17 @@ static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr) return pgtable_quicklist_alloc(); } +#ifndef XEN static inline void pmd_free(pmd_t * pmd) { pgtable_quicklist_free(pmd); } +#else +static inline void pmd_free(volatile pmd_t * pmd) +{ + pgtable_quicklist_free((void*)pmd); +} +#endif #define __pmd_free_tlb(tlb, pmd) pmd_free(pmd) @@ -150,7 +164,7 @@ pmd_populate_kernel(struct mm_struct *mm, pmd_t * pmd_entry, pte_t * pte) #ifdef XEN static inline int -pmd_cmpxchg_kernel_rel(struct mm_struct *mm, pmd_t * pmd_entry, +pmd_cmpxchg_kernel_rel(struct mm_struct *mm, volatile pmd_t * pmd_entry, pte_t * old_pte, pte_t * new_pte) { #ifdef CONFIG_SMP @@ -186,12 +200,17 @@ static inline void pte_free(struct page *pte) { pgtable_quicklist_free(page_address(pte)); } -#endif static inline void pte_free_kernel(pte_t * pte) { pgtable_quicklist_free(pte); } +#else +static inline void pte_free_kernel(volatile pte_t * pte) +{ + pgtable_quicklist_free((void*)pte); +} +#endif #ifndef XEN #define __pte_free_tlb(tlb, pte) pte_free(pte) diff --git a/xen/include/asm-ia64/linux-xen/asm/pgtable.h b/xen/include/asm-ia64/linux-xen/asm/pgtable.h index a859a8b437..306151d152 100644 --- a/xen/include/asm-ia64/linux-xen/asm/pgtable.h +++ b/xen/include/asm-ia64/linux-xen/asm/pgtable.h @@ -380,7 +380,11 @@ pgd_index (unsigned long address) /* The offset in the 1-level directory is given by the 3 region bits (61..63) and the level-1 bits. */ +#ifndef XEN static inline pgd_t* +#else +static inline volatile pgd_t* +#endif pgd_offset (struct mm_struct *mm, unsigned long address) { return mm->pgd + pgd_index(address); @@ -397,15 +401,24 @@ pgd_offset (struct mm_struct *mm, unsigned long address) #define pgd_offset_gate(mm, addr) pgd_offset_k(addr) /* Find an entry in the second-level page table.. */ +#ifndef XEN #define pmd_offset(dir,addr) \ ((pmd_t *) pud_page(*(dir)) + (((addr) >> PMD_SHIFT) & (PTRS_PER_PMD - 1))) +#else +#define pmd_offset(dir,addr) \ + ((volatile pmd_t *) pud_page(*(dir)) + (((addr) >> PMD_SHIFT) & (PTRS_PER_PMD - 1))) +#endif /* * Find an entry in the third-level page table. This looks more complicated than it * should be because some platforms place page tables in high memory. */ #define pte_index(addr) (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)) +#ifndef XEN #define pte_offset_kernel(dir,addr) ((pte_t *) pmd_page_kernel(*(dir)) + pte_index(addr)) +#else +#define pte_offset_kernel(dir,addr) ((volatile pte_t *) pmd_page_kernel(*(dir)) + pte_index(addr)) +#endif #define pte_offset_map(dir,addr) pte_offset_kernel(dir, addr) #define pte_offset_map_nested(dir,addr) pte_offset_map(dir, addr) #define pte_unmap(pte) do { } while (0) diff --git a/xen/include/asm-ia64/linux-xen/asm/processor.h b/xen/include/asm-ia64/linux-xen/asm/processor.h index 976c0960d6..fb753e0d16 100644 --- a/xen/include/asm-ia64/linux-xen/asm/processor.h +++ b/xen/include/asm-ia64/linux-xen/asm/processor.h @@ -28,8 +28,18 @@ * Limits for PMC and PMD are set to less than maximum architected values * but should be sufficient for a while */ +#ifdef XEN +/* + * These are increased in linux-2.6.16. Montecito requires 35PMDs. + * This ifdef will become unnecessary when this header file is + * upgraded to 2.6.16 or newer. + */ +#define IA64_NUM_PMC_REGS 64 +#define IA64_NUM_PMD_REGS 64 +#else #define IA64_NUM_PMC_REGS 32 #define IA64_NUM_PMD_REGS 32 +#endif #define DEFAULT_MAP_BASE __IA64_UL_CONST(0x2000000000000000) #define DEFAULT_TASK_SIZE __IA64_UL_CONST(0xa000000000000000) diff --git a/xen/include/asm-ia64/linux-xen/linux/README.origin b/xen/include/asm-ia64/linux-xen/linux/README.origin index b84b7b65f1..6cc513be16 100644 --- a/xen/include/asm-ia64/linux-xen/linux/README.origin +++ b/xen/include/asm-ia64/linux-xen/linux/README.origin @@ -9,3 +9,6 @@ efi.h -> linux/include/linux/efi.h gfp.h -> linux/include/linux/gfp.h hardirq.h -> linux/include/linux/hardirq.h interrupt.h -> linux/include/linux/interrupt.h + +# The files below are from Linux-2.6.16.33 +oprofile.h -> linux/include/linux/oprofile.h diff --git a/xen/include/asm-ia64/linux-xen/linux/interrupt.h b/xen/include/asm-ia64/linux-xen/linux/interrupt.h index a58afbec08..d6bbeb7772 100644 --- a/xen/include/asm-ia64/linux-xen/linux/interrupt.h +++ b/xen/include/asm-ia64/linux-xen/linux/interrupt.h @@ -29,13 +29,15 @@ */ #ifdef XEN typedef void irqreturn_t; +#define IRQ_NONE +#define IRQ_HANDLED +#define IRQ_RETVAL(x) #else typedef int irqreturn_t; -#endif - #define IRQ_NONE (0) #define IRQ_HANDLED (1) #define IRQ_RETVAL(x) ((x) != 0) +#endif #ifndef XEN struct irqaction { diff --git a/xen/include/asm-ia64/linux-xen/linux/oprofile.h b/xen/include/asm-ia64/linux-xen/linux/oprofile.h new file mode 100644 index 0000000000..559c4c38a9 --- /dev/null +++ b/xen/include/asm-ia64/linux-xen/linux/oprofile.h @@ -0,0 +1,119 @@ +/** + * @file oprofile.h + * + * API for machine-specific interrupts to interface + * to oprofile. + * + * @remark Copyright 2002 OProfile authors + * @remark Read the file COPYING + * + * @author John Levon + */ + +#ifndef OPROFILE_H +#define OPROFILE_H + +#include +#include +#include + +struct super_block; +struct dentry; +struct file_operations; +struct pt_regs; + +/* Operations structure to be filled in */ +struct oprofile_operations { + /* create any necessary configuration files in the oprofile fs. + * Optional. */ + int (*create_files)(struct super_block * sb, struct dentry * root); + /* Do any necessary interrupt setup. Optional. */ + int (*setup)(void); + /* Do any necessary interrupt shutdown. Optional. */ + void (*shutdown)(void); + /* Start delivering interrupts. */ + int (*start)(void); + /* Stop delivering interrupts. */ + void (*stop)(void); + /* Initiate a stack backtrace. Optional. */ + void (*backtrace)(struct pt_regs * const regs, unsigned int depth); + /* CPU identification string. */ + char * cpu_type; +}; + +/** + * One-time initialisation. *ops must be set to a filled-in + * operations structure. This is called even in timer interrupt + * mode so an arch can set a backtrace callback. + * + * If an error occurs, the fields should be left untouched. + */ +int oprofile_arch_init(struct oprofile_operations * ops); + +/** + * One-time exit/cleanup for the arch. + */ +void oprofile_arch_exit(void); + +/** + * Add a sample. This may be called from any context. Pass + * smp_processor_id() as cpu. + */ +void oprofile_add_sample(struct pt_regs * const regs, unsigned long event); + +/* Use this instead when the PC value is not from the regs. Doesn't + * backtrace. */ +void oprofile_add_pc(unsigned long pc, int is_kernel, unsigned long event); + +/* add a backtrace entry, to be called from the ->backtrace callback */ +void oprofile_add_trace(unsigned long eip); + + +/** + * Create a file of the given name as a child of the given root, with + * the specified file operations. + */ +int oprofilefs_create_file(struct super_block * sb, struct dentry * root, + char const * name, struct file_operations * fops); + +int oprofilefs_create_file_perm(struct super_block * sb, struct dentry * root, + char const * name, struct file_operations * fops, int perm); + +/** Create a file for read/write access to an unsigned long. */ +int oprofilefs_create_ulong(struct super_block * sb, struct dentry * root, + char const * name, ulong * val); + +/** Create a file for read-only access to an unsigned long. */ +int oprofilefs_create_ro_ulong(struct super_block * sb, struct dentry * root, + char const * name, ulong * val); + +/** Create a file for read-only access to an atomic_t. */ +int oprofilefs_create_ro_atomic(struct super_block * sb, struct dentry * root, + char const * name, atomic_t * val); + +/** create a directory */ +struct dentry * oprofilefs_mkdir(struct super_block * sb, struct dentry * root, + char const * name); + +/** + * Write the given asciz string to the given user buffer @buf, updating *offset + * appropriately. Returns bytes written or -EFAULT. + */ +ssize_t oprofilefs_str_to_user(char const * str, char __user * buf, size_t count, loff_t * offset); + +/** + * Convert an unsigned long value into ASCII and copy it to the user buffer @buf, + * updating *offset appropriately. Returns bytes written or -EFAULT. + */ +ssize_t oprofilefs_ulong_to_user(unsigned long val, char __user * buf, size_t count, loff_t * offset); + +/** + * Read an ASCII string for a number from a userspace buffer and fill *val on success. + * Returns 0 on success, < 0 on error. + */ +int oprofilefs_ulong_from_user(unsigned long * val, char const __user * buf, size_t count); + +/** lock for read/write safety */ +extern spinlock_t oprofilefs_lock; + +#endif /* OPROFILE_H */ diff --git a/xen/include/asm-ia64/linux/asm-generic/README.origin b/xen/include/asm-ia64/linux/asm-generic/README.origin index e1bc74c396..436f7c553f 100644 --- a/xen/include/asm-ia64/linux/asm-generic/README.origin +++ b/xen/include/asm-ia64/linux/asm-generic/README.origin @@ -9,7 +9,6 @@ ide_iops.h -> linux/include/asm-generic/ide_iops.h iomap.h -> linux/include/asm-generic/iomap.h pci.h -> linux/include/asm-generic/pci.h pgtable.h -> linux/include/asm-generic/pgtable.h -pgtable-nopud.h -> linux/include/asm-generic/pgtable-nopud.h sections.h -> linux/include/asm-generic/sections.h topology.h -> linux/include/asm-generic/topology.h unaligned.h -> linux/include/asm-generic/unaligned.h diff --git a/xen/include/asm-ia64/mm.h b/xen/include/asm-ia64/mm.h index 4a7fa50492..a232bd599c 100644 --- a/xen/include/asm-ia64/mm.h +++ b/xen/include/asm-ia64/mm.h @@ -422,7 +422,8 @@ extern unsigned long totalram_pages; extern int nr_swap_pages; extern void alloc_dom_xen_and_dom_io(void); -extern void relinquish_mm(struct domain* d); +extern void mm_teardown(struct domain* d); +extern void mm_final_teardown(struct domain* d); extern struct page_info * assign_new_domain_page(struct domain *d, unsigned long mpaddr); extern void assign_new_domain0_page(struct domain *d, unsigned long mpaddr); extern int __assign_domain_page(struct domain *d, unsigned long mpaddr, unsigned long physaddr, unsigned long flags); @@ -440,6 +441,7 @@ extern unsigned long ____lookup_domain_mpa(struct domain *d, unsigned long mpadd extern unsigned long do_dom0vp_op(unsigned long cmd, unsigned long arg0, unsigned long arg1, unsigned long arg2, unsigned long arg3); extern unsigned long dom0vp_zap_physmap(struct domain *d, unsigned long gpfn, unsigned int extent_order); extern unsigned long dom0vp_add_physmap(struct domain* d, unsigned long gpfn, unsigned long mfn, unsigned long flags, domid_t domid); +extern unsigned long dom0vp_add_physmap_with_gmfn(struct domain* d, unsigned long gpfn, unsigned long gmfn, unsigned long flags, domid_t domid); #ifdef CONFIG_XEN_IA64_EXPOSE_P2M extern void expose_p2m_init(void); extern unsigned long dom0vp_expose_p2m(struct domain* d, unsigned long conv_start_gpfn, unsigned long assign_start_gpfn, unsigned long expose_size, unsigned long granule_pfn); diff --git a/xen/include/asm-ia64/regs.h b/xen/include/asm-ia64/regs.h index e429039f1f..9cbd09ec72 100644 --- a/xen/include/asm-ia64/regs.h +++ b/xen/include/asm-ia64/regs.h @@ -1,2 +1 @@ #include -#define xen_regs pt_regs diff --git a/xen/include/asm-ia64/softirq.h b/xen/include/asm-ia64/softirq.h new file mode 100644 index 0000000000..9e5a03c482 --- /dev/null +++ b/xen/include/asm-ia64/softirq.h @@ -0,0 +1,9 @@ +#ifndef __ASM_SOFTIRQ_H__ +#define __ASM_SOFTIRQ_H__ + +#define CMC_DISABLE_SOFTIRQ (NR_COMMON_SOFTIRQS + 0) +#define CMC_ENABLE_SOFTIRQ (NR_COMMON_SOFTIRQS + 1) + +#define NR_ARCH_SOFTIRQS 2 + +#endif /* __ASM_SOFTIRQ_H__ */ diff --git a/xen/include/asm-ia64/vcpu.h b/xen/include/asm-ia64/vcpu.h index a95d3f1c29..b61915e3b2 100644 --- a/xen/include/asm-ia64/vcpu.h +++ b/xen/include/asm-ia64/vcpu.h @@ -10,7 +10,7 @@ #include #include #include -typedef int BOOLEAN; +#include struct vcpu; typedef struct vcpu VCPU; typedef cpu_user_regs_t REGS; diff --git a/xen/include/asm-ia64/viosapic.h b/xen/include/asm-ia64/viosapic.h new file mode 100644 index 0000000000..fdc91d56cf --- /dev/null +++ b/xen/include/asm-ia64/viosapic.h @@ -0,0 +1,95 @@ +/* + * + * Copyright (C) 2001 MandrakeSoft S.A. + * + * MandrakeSoft S.A. + * 43, rue d'Aboukir + * 75002 Paris - France + * http://www.linux-mandrake.com/ + * http://www.mandrakesoft.com/ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __ASM_IA64_VMX_VIOSAPIC_H__ +#define __ASM_IA64_VMX_VIOSAPIC_H__ + +#include +#include +#include + +/* Direct registers. */ +#define VIOSAPIC_REG_SELECT 0x00 +#define VIOSAPIC_WINDOW 0x10 +#define VIOSAPIC_EOI 0x40 + +#define VIOSAPIC_VERSION 0x1 + +#define VIOSAPIC_DEST_SHIFT 16 + + +#define VIOSAPIC_VERSION_ID 0x21 /* IOSAPIC version */ + +#define VIOSAPIC_NUM_PINS 48 + +#define VIOSAPIC_DEFAULT_BASE_ADDRESS 0xfec00000 +#define VIOSAPIC_MEM_LENGTH 0x100 + +#define domain_viosapic(d) (&(d)->arch.hvm_domain.viosapic) +#define viosapic_domain(v) (container_of((v), struct domain, \ + arch.hvm_domain.viosapic)) +#define vcpu_viosapic(v) (&(v)->domain->arch.hvm_domain.viosapic) + +union viosapic_rte +{ + uint64_t bits; + struct { + uint8_t vector; + + uint8_t delivery_mode : 3; + uint8_t reserve1 : 1; + uint8_t delivery_status: 1; + uint8_t polarity : 1; + uint8_t reserve2 : 1; + uint8_t trig_mode : 1; + + uint8_t mask : 1; + uint8_t reserve3 : 7; + + uint8_t reserved[3]; + uint16_t dest_id; + }; +}; + +struct viosapic { + uint64_t irr; + uint64_t isr; /* This is used for level trigger */ + uint32_t ioregsel; + spinlock_t lock; + struct vcpu * lowest_vcpu; + uint64_t base_address; + union viosapic_rte redirtbl[VIOSAPIC_NUM_PINS]; +}; + +void viosapic_init(struct domain *d); +void viosapic_set_irq(struct domain *d, int irq, int level); +void viosapic_set_pci_irq(struct domain *d, int device, int intx, int level); +void viosapic_write(struct vcpu *v, unsigned long addr, + unsigned long length, unsigned long val); + +unsigned long viosapic_read(struct vcpu *v, unsigned long addr, + unsigned long length); + +#endif /* __ASM_IA64_VMX_VIOSAPIC_H__ */ diff --git a/xen/include/asm-ia64/vlsapic.h b/xen/include/asm-ia64/vlsapic.h index 98bc88f9df..cd1eeff5ed 100644 --- a/xen/include/asm-ia64/vlsapic.h +++ b/xen/include/asm-ia64/vlsapic.h @@ -23,6 +23,46 @@ #ifndef _LSAPIC_H #define _LSAPIC_H #include +#include +/* + *Delivery mode + */ +#define SAPIC_DELIV_SHIFT 8 +#define SAPIC_FIXED 0x0 +#define SAPIC_LOWEST_PRIORITY 0x1 +#define SAPIC_PMI 0x2 +#define SAPIC_NMI 0x4 +#define SAPIC_INIT 0x5 +#define SAPIC_EXTINT 0x7 + +/* + *Interrupt polarity + */ +#define SAPIC_POLARITY_SHIFT 13 +#define SAPIC_POL_HIGH 0 +#define SAPIC_POL_LOW 1 + +/* + *Trigger mode + */ +#define SAPIC_TRIGGER_SHIFT 15 +#define SAPIC_EDGE 0 +#define SAPIC_LEVEL 1 + +/* + * LSAPIC OFFSET + */ +#define PIB_LOW_HALF(ofst) !(ofst & (1 << 20)) +#define PIB_OFST_INTA 0x1E0000 +#define PIB_OFST_XTP 0x1E0008 + +/* + *Mask bit + */ +#define SAPIC_MASK_SHIFT 16 +#define SAPIC_MASK (1 << SAPIC_MASK_SHIFT) + +#define VLSAPIC_XTP(_v) VMX(_v, xtp) extern void vtm_init(struct vcpu *vcpu); extern void vtm_set_itc(struct vcpu *vcpu, uint64_t new_itc); @@ -30,5 +70,9 @@ extern void vtm_set_itm(struct vcpu *vcpu, uint64_t val); extern void vtm_set_itv(struct vcpu *vcpu, uint64_t val); extern void vmx_vexirq(struct vcpu *vcpu); extern void vhpi_detection(struct vcpu *vcpu); - +extern int vmx_vcpu_pend_interrupt(VCPU * vcpu, uint8_t vector); +extern struct vcpu * vlsapic_lid_to_vcpu(struct domain *d, uint16_t dest); +extern uint64_t vlsapic_read(struct vcpu *v, uint64_t addr, uint64_t s); +extern void vlsapic_write(struct vcpu *v, uint64_t addr, uint64_t s, uint64_t val); +#define vlsapic_set_irq vmx_vcpu_pend_interrupt #endif diff --git a/xen/include/asm-ia64/vmx.h b/xen/include/asm-ia64/vmx.h index 344ef44b95..4e0971278a 100644 --- a/xen/include/asm-ia64/vmx.h +++ b/xen/include/asm-ia64/vmx.h @@ -55,7 +55,6 @@ extern void vmx_relinquish_guest_resources(struct domain *d); extern void vmx_relinquish_vcpu_resources(struct vcpu *v); extern void vmx_die_if_kernel(char *str, struct pt_regs *regs, long err); extern void vmx_send_assist_req(struct vcpu *v); -extern void vmx_vioapic_set_irq(struct domain *d, int irq, int level); static inline vcpu_iodata_t *get_vio(struct domain *d, unsigned long cpu) { diff --git a/xen/include/asm-ia64/vmx_platform.h b/xen/include/asm-ia64/vmx_platform.h index 878961ef04..5c0bd4c595 100644 --- a/xen/include/asm-ia64/vmx_platform.h +++ b/xen/include/asm-ia64/vmx_platform.h @@ -21,18 +21,17 @@ #include #include -#include +#include struct mmio_list; typedef struct virtual_platform_def { unsigned long buffered_io_va; spinlock_t buffered_io_lock; unsigned long shared_page_va; unsigned long pib_base; - unsigned char xtp; unsigned long params[HVM_NR_PARAMS]; struct mmio_list *mmio; /* One IOSAPIC now... */ - struct vioapic vioapic; + struct viosapic viosapic; } vir_plat_t; static inline int __fls(uint32_t word) @@ -43,47 +42,4 @@ static inline int __fls(uint32_t word) __asm__ __volatile__ ("getf.exp %0=%1" : "=r"(exp) : "f"(d)); return word ? (exp - 0xffff) : -1; } - -/* This is a connect structure between vIOSAPIC model and vLSAPIC model. - * vlapic is required by vIOSAPIC model to manipulate pending bits, and - * we just map them into vpd here - */ -typedef struct vlapic { - struct vcpu *vcpu; /* Link to current vcpu */ -} vlapic_t; - -extern uint64_t dummy_tmr[]; -#define VLAPIC_ID(l) (uint16_t)(((l)->vcpu->arch.privregs->lid) >> 16) -#define VLAPIC_IRR(l) ((l)->vcpu->arch.privregs->irr[0]) -struct vlapic *apic_round_robin(struct domain *d, uint8_t vector, uint32_t bitmap); -extern int vmx_vlapic_set_irq(struct vcpu *v, uint8_t vec, uint8_t trig); -static inline int vlapic_set_irq(struct vlapic *t, uint8_t vec, uint8_t trig) -{ - return vmx_vlapic_set_irq(t->vcpu, vec, trig); -} - -enum ioapic_irq_destination_types { - dest_Fixed = 0, - dest_LowestPrio = 1, - dest_SMI = 2, - dest__reserved_1 = 3, - dest_NMI = 4, - dest_INIT = 5, - dest__reserved_2 = 6, - dest_ExtINT = 7 -}; - -#define vlapic_enabled(l) 1 - -#define VLAPIC_DELIV_MODE_FIXED 0x0 -#define VLAPIC_DELIV_MODE_REDIR 0x1 -#define VLAPIC_DELIV_MODE_LPRI VLAPIC_DELIV_MODE_REDIR -#define VLAPIC_DELIV_MODE_PMI 0x2 -#define VLAPIC_DELIV_MODE_SMI 0x2 /* For IA32 */ -#define VLAPIC_DELIV_MODE_RESERVED 0x3 -#define VLAPIC_DELIV_MODE_NMI 0x4 -#define VLAPIC_DELIV_MODE_INIT 0x5 -#define VLAPIC_DELIV_MODE_STARTUP 0x6 /* For IA32 */ -#define VLAPIC_DELIV_MODE_EXT 0x7 - #endif diff --git a/xen/include/asm-ia64/vmx_vpd.h b/xen/include/asm-ia64/vmx_vpd.h index 17496d25f2..8e4eb4f55c 100644 --- a/xen/include/asm-ia64/vmx_vpd.h +++ b/xen/include/asm-ia64/vmx_vpd.h @@ -74,14 +74,9 @@ struct ivt_debug{ #define IVT_DEBUG_MAX 128 #endif -struct arch_vmx_domain { - spinlock_t virq_assist_lock; /* spinlock for pass virq */ -}; - struct arch_vmx_struct { // vpd_t *vpd; vtime_t vtm; - struct vlapic vlapic; unsigned long vrr[8]; /* if the corresponding bit is 1, then this page size is used in this region */ @@ -101,9 +96,9 @@ struct arch_vmx_struct { // unsigned long rfi_iip; // unsigned long rfi_ipsr; // unsigned long rfi_ifs; -// unsigned long in_service[4]; // vLsapic inservice IRQ bits unsigned long flags; unsigned long xen_port; + unsigned char xtp; #ifdef VTI_DEBUG unsigned long ivt_current; struct ivt_debug ivt_debug[IVT_DEBUG_MAX]; @@ -118,12 +113,6 @@ struct arch_vmx_struct { #define ARCH_VMX_IO_WAIT 3 /* Waiting for I/O completion */ #define ARCH_VMX_DOMAIN 5 /* Need it to indicate VTi domain */ - -#define VMX_DEBUG 1 -#if VMX_DEBUG - -extern unsigned int opt_vmx_debug_level; -#endif #endif //__ASSEMBLY__ // VPD field offset diff --git a/xen/include/asm-ia64/xenmca.h b/xen/include/asm-ia64/xenmca.h new file mode 100644 index 0000000000..653253e8fd --- /dev/null +++ b/xen/include/asm-ia64/xenmca.h @@ -0,0 +1,34 @@ +/* + * File: xenmca.h + * Purpose: Machine check handling specific defines for Xen + * + * Copyright (C) 2006 FUJITSU LTD. (kaz@jp.fujitsu.com) + */ + +#ifndef _ASM_IA64_XENMCA_H +#define _ASM_IA64_XENMCA_H + +#ifndef __ASSEMBLER__ +#include +#include + +typedef struct sal_queue_entry_t { + int cpuid; + int sal_info_type; + unsigned int vector; + unsigned int virq; + unsigned int length; + struct list_head list; +} sal_queue_entry_t; + +extern struct list_head *sal_queue; + +struct ia64_mca_tlb_info { + u64 cr_lid; + u64 percpu_paddr; +}; + +extern struct ia64_mca_tlb_info ia64_mca_tlb_list[]; +#endif /* __ASSEMBLER__ */ + +#endif /* _ASM_IA64_XENMCA_H */ diff --git a/xen/include/asm-ia64/xenoprof.h b/xen/include/asm-ia64/xenoprof.h new file mode 100644 index 0000000000..b8b1e602d9 --- /dev/null +++ b/xen/include/asm-ia64/xenoprof.h @@ -0,0 +1,64 @@ +/****************************************************************************** + * asm-ia64/xenoprof.h + * xenoprof ia64 arch specific header file + * + * Copyright (c) 2006 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __ASM_XENOPROF_H__ +#define __ASM_XENOPROF_H__ + +int xenoprof_arch_init(int *num_events, int *is_primary, char *cpu_type); +int xenoprof_arch_reserve_counters(void); +int xenoprof_arch_counter(XEN_GUEST_HANDLE(void) arg); +int xenoprof_arch_setup_events(void); +int xenoprof_arch_enable_virq(void); +int xenoprof_arch_start(void); +void xenoprof_arch_stop(void); +void xenoprof_arch_disable_virq(void); +void xenoprof_arch_release_counters(void); + +struct vcpu; +struct cpu_user_regs; +int xenoprofile_get_mode(struct vcpu *v, struct cpu_user_regs * const regs); + +#define xenoprof_shared_gmfn(d, gmaddr, maddr) \ + assign_domain_page((d), (gmaddr), (maddr)); + +static inline int +ring(const struct pt_regs* regs) +{ + return ((struct ia64_psr*)(&(regs)->cr_ipsr))->cpl; +} +#define ring_0(r) (ring(r) == 0) +#define ring_1(r) (ring(r) == 1) +#define ring_2(r) (ring(r) == 2) +#define ring_3(r) (ring(r) == 3) + +#endif /* __ASM_XENOPROF_H__ */ + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/include/asm-powerpc/config.h b/xen/include/asm-powerpc/config.h index 82d1138b6f..f578ebe781 100644 --- a/xen/include/asm-powerpc/config.h +++ b/xen/include/asm-powerpc/config.h @@ -72,7 +72,7 @@ extern char __bss_start[]; #define supervisor_mode_kernel (0) -#define MAX_DMADOM_PFN (~0UL) +#define CONFIG_DMA_BITSIZE 64 #include diff --git a/xen/include/asm-powerpc/elf.h b/xen/include/asm-powerpc/elf.h new file mode 100644 index 0000000000..3877c0f5fa --- /dev/null +++ b/xen/include/asm-powerpc/elf.h @@ -0,0 +1,30 @@ +#ifndef _ASM_ELF_H__ +#define _ASM_ELF_H__ + +#include /* for printk() used in stub */ + +typedef struct { + unsigned long dummy; +} ELF_Gregset; + +typedef struct { + unsigned long dummy; +} crash_xen_core_t; + +static inline void elf_core_save_regs(ELF_Gregset *core_regs, + crash_xen_core_t *xen_core_regs) +{ + printk("STUB: " __FILE__ ": %s: not implemented\n", __FUNCTION__); +} + +#endif /* _ASM_ELF_H__ */ + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/include/asm-powerpc/kexec.h b/xen/include/asm-powerpc/kexec.h new file mode 100644 index 0000000000..f228972047 --- /dev/null +++ b/xen/include/asm-powerpc/kexec.h @@ -0,0 +1,25 @@ +#ifndef _ASM_KEXEC_H__ +#define _ASM_KEXEC_H__ + +#include /* for printk() used in stub */ +#include +#include +#include + +static inline void machine_kexec(xen_kexec_image_t *image) +{ + printk("STUB: " __FILE__ ": %s: not implemented\n", __FUNCTION__); +} + +#endif /* _ASM_KEXEC_H__ */ + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ + diff --git a/xen/include/asm-powerpc/softirq.h b/xen/include/asm-powerpc/softirq.h new file mode 100644 index 0000000000..3444d300c0 --- /dev/null +++ b/xen/include/asm-powerpc/softirq.h @@ -0,0 +1,6 @@ +#ifndef __ASM_SOFTIRQ_H__ +#define __ASM_SOFTIRQ_H__ + +#define NR_ARCH_SOFTIRQS 0 + +#endif /* __ASM_SOFTIRQ_H__ */ diff --git a/xen/include/asm-x86/config.h b/xen/include/asm-x86/config.h index cca215f4b2..1ccb34edd2 100644 --- a/xen/include/asm-x86/config.h +++ b/xen/include/asm-x86/config.h @@ -82,7 +82,7 @@ /* Debug stack is restricted to 8kB by guard pages. */ #define DEBUG_STACK_SIZE 8192 -#define MAX_DMADOM_PFN 0x7FFFFUL /* 31 addressable bits */ +#define CONFIG_DMA_BITSIZE 30 #ifndef __ASSEMBLY__ extern unsigned long _end; /* standard ELF symbol */ diff --git a/xen/include/asm-x86/domain.h b/xen/include/asm-x86/domain.h index 03620a33ac..efda6dd263 100644 --- a/xen/include/asm-x86/domain.h +++ b/xen/include/asm-x86/domain.h @@ -5,6 +5,7 @@ #include #include #include +#include struct trap_bounce { unsigned long error_code; @@ -100,11 +101,7 @@ struct arch_domain /* I/O-port admin-specified access capabilities. */ struct rangeset *ioport_caps; - /* HVM stuff */ - struct hvm_domain hvm_domain; - - /* Shadow-translated guest: Pseudophys base address of reserved area. */ - unsigned long first_reserved_pfn; + struct hvm_domain hvm_domain; struct shadow_domain shadow; @@ -113,6 +110,9 @@ struct arch_domain /* Highest guest frame that's ever been mapped in the p2m */ unsigned long max_mapped_pfn; + /* Pseudophysical e820 map (XENMEM_memory_map). */ + struct e820entry e820[3]; + unsigned int nr_e820; } __cacheline_aligned; #ifdef CONFIG_X86_PAE @@ -147,8 +147,6 @@ struct shadow_vcpu { unsigned long last_writeable_pte_smfn; /* HVM guest: paging enabled (CR0.PG)? */ unsigned int translate_enabled:1; - /* Emulated fault needs to be propagated to guest? */ - unsigned int propagate_fault:1; }; struct arch_vcpu @@ -198,7 +196,6 @@ struct arch_vcpu unsigned long cr3; /* (MA) value to install in HW CR3 */ void *guest_vtable; /* virtual addr of pagetable */ - root_pgentry_t *monitor_vtable; /* virtual addr of monitor_table */ /* Current LDT details. */ unsigned long shadow_ldt_mapcnt; diff --git a/xen/include/asm-x86/elf.h b/xen/include/asm-x86/elf.h new file mode 100644 index 0000000000..9747d46e61 --- /dev/null +++ b/xen/include/asm-x86/elf.h @@ -0,0 +1,24 @@ +#ifndef __X86_ELF_H__ +#define __X86_ELF_H__ + +typedef struct { + unsigned long cr0, cr2, cr3, cr4; +} crash_xen_core_t; + +#ifdef __x86_64__ +#include +#else +#include +#endif + +#endif /* __X86_ELF_H__ */ + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/include/asm-x86/fixmap.h b/xen/include/asm-x86/fixmap.h index dbf1b70c55..8444e21262 100644 --- a/xen/include/asm-x86/fixmap.h +++ b/xen/include/asm-x86/fixmap.h @@ -16,6 +16,7 @@ #include #include #include +#include /* * Here we define all the compile-time 'special' virtual @@ -36,6 +37,9 @@ enum fixed_addresses { FIX_ACPI_END = FIX_ACPI_BEGIN + FIX_ACPI_PAGES - 1, FIX_HPET_BASE, FIX_CYCLONE_TIMER, + FIX_KEXEC_BASE_0, + FIX_KEXEC_BASE_END = FIX_KEXEC_BASE_0 \ + + ((KEXEC_XEN_NO_PAGES >> 1) * KEXEC_IMAGE_NR) - 1, __end_of_fixed_addresses }; diff --git a/xen/include/asm-x86/guest_access.h b/xen/include/asm-x86/guest_access.h index 01e1fa3583..ce5c290787 100644 --- a/xen/include/asm-x86/guest_access.h +++ b/xen/include/asm-x86/guest_access.h @@ -34,7 +34,7 @@ #define copy_to_guest_offset(hnd, off, ptr, nr) ({ \ const typeof(ptr) _x = (hnd).p; \ const typeof(ptr) _y = (ptr); \ - shadow_mode_translate(current->domain) ? \ + is_hvm_vcpu(current) ? \ copy_to_user_hvm(_x+(off), _y, sizeof(*_x)*(nr)) : \ copy_to_user(_x+(off), _y, sizeof(*_x)*(nr)); \ }) @@ -46,7 +46,7 @@ #define copy_from_guest_offset(ptr, hnd, off, nr) ({ \ const typeof(ptr) _x = (hnd).p; \ const typeof(ptr) _y = (ptr); \ - shadow_mode_translate(current->domain) ? \ + is_hvm_vcpu(current) ? \ copy_from_user_hvm(_y, _x+(off), sizeof(*_x)*(nr)) :\ copy_from_user(_y, _x+(off), sizeof(*_x)*(nr)); \ }) @@ -55,7 +55,7 @@ #define copy_field_to_guest(hnd, ptr, field) ({ \ const typeof(&(ptr)->field) _x = &(hnd).p->field; \ const typeof(&(ptr)->field) _y = &(ptr)->field; \ - shadow_mode_translate(current->domain) ? \ + is_hvm_vcpu(current) ? \ copy_to_user_hvm(_x, _y, sizeof(*_x)) : \ copy_to_user(_x, _y, sizeof(*_x)); \ }) @@ -64,7 +64,7 @@ #define copy_field_from_guest(ptr, hnd, field) ({ \ const typeof(&(ptr)->field) _x = &(hnd).p->field; \ const typeof(&(ptr)->field) _y = &(ptr)->field; \ - shadow_mode_translate(current->domain) ? \ + is_hvm_vcpu(current) ? \ copy_from_user_hvm(_y, _x, sizeof(*_x)) : \ copy_from_user(_y, _x, sizeof(*_x)); \ }) @@ -80,7 +80,7 @@ #define __copy_to_guest_offset(hnd, off, ptr, nr) ({ \ const typeof(ptr) _x = (hnd).p; \ const typeof(ptr) _y = (ptr); \ - shadow_mode_translate(current->domain) ? \ + is_hvm_vcpu(current) ? \ copy_to_user_hvm(_x+(off), _y, sizeof(*_x)*(nr)) : \ __copy_to_user(_x+(off), _y, sizeof(*_x)*(nr)); \ }) @@ -88,7 +88,7 @@ #define __copy_from_guest_offset(ptr, hnd, off, nr) ({ \ const typeof(ptr) _x = (hnd).p; \ const typeof(ptr) _y = (ptr); \ - shadow_mode_translate(current->domain) ? \ + is_hvm_vcpu(current) ? \ copy_from_user_hvm(_y, _x+(off),sizeof(*_x)*(nr)) : \ __copy_from_user(_y, _x+(off), sizeof(*_x)*(nr)); \ }) @@ -96,7 +96,7 @@ #define __copy_field_to_guest(hnd, ptr, field) ({ \ const typeof(&(ptr)->field) _x = &(hnd).p->field; \ const typeof(&(ptr)->field) _y = &(ptr)->field; \ - shadow_mode_translate(current->domain) ? \ + is_hvm_vcpu(current) ? \ copy_to_user_hvm(_x, _y, sizeof(*_x)) : \ __copy_to_user(_x, _y, sizeof(*_x)); \ }) @@ -104,7 +104,7 @@ #define __copy_field_from_guest(ptr, hnd, field) ({ \ const typeof(&(ptr)->field) _x = &(hnd).p->field; \ const typeof(&(ptr)->field) _y = &(ptr)->field; \ - shadow_mode_translate(current->domain) ? \ + is_hvm_vcpu(current) ? \ copy_from_user_hvm(_x, _y, sizeof(*_x)) : \ __copy_from_user(_y, _x, sizeof(*_x)); \ }) diff --git a/xen/include/asm-x86/hvm/hvm.h b/xen/include/asm-x86/hvm/hvm.h index 83ebf48805..13ee040fdb 100644 --- a/xen/include/asm-x86/hvm/hvm.h +++ b/xen/include/asm-x86/hvm/hvm.h @@ -17,28 +17,47 @@ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place - Suite 330, Boston, MA 02111-1307 USA. */ + #ifndef __ASM_X86_HVM_HVM_H__ #define __ASM_X86_HVM_HVM_H__ -enum segment { - seg_cs, - seg_ss, - seg_ds, - seg_es, - seg_fs, - seg_gs, - seg_tr, - seg_ldtr, - seg_gdtr, - seg_idtr -}; +#include + +/* + * Attribute for segment selector. This is a copy of bit 40:47 & 52:55 of the + * segment descriptor. It happens to match the format of an AMD SVM VMCB. + */ +typedef union segment_attributes { + u16 bytes; + struct + { + u16 type:4; /* 0; Bit 40-43 */ + u16 s: 1; /* 4; Bit 44 */ + u16 dpl: 2; /* 5; Bit 45-46 */ + u16 p: 1; /* 7; Bit 47 */ + u16 avl: 1; /* 8; Bit 52 */ + u16 l: 1; /* 9; Bit 53 */ + u16 db: 1; /* 10; Bit 54 */ + u16 g: 1; /* 11; Bit 55 */ + } fields; +} __attribute__ ((packed)) segment_attributes_t; + +/* + * Full state of a segment register (visible and hidden portions). + * Again, this happens to match the format of an AMD SVM VMCB. + */ +typedef struct segment_register { + u16 sel; + segment_attributes_t attr; + u32 limit; + u64 base; +} __attribute__ ((packed)) segment_register_t; /* * The hardware virtual machine (HVM) interface abstracts away from the * x86/x86_64 CPU virtualization assist specifics. Currently this interface * supports Intel's VT-x and AMD's SVM extensions. */ - struct hvm_function_table { /* * Disable HVM functionality @@ -74,7 +93,9 @@ struct hvm_function_table { int (*pae_enabled)(struct vcpu *v); int (*guest_x86_mode)(struct vcpu *v); unsigned long (*get_guest_ctrl_reg)(struct vcpu *v, unsigned int num); - unsigned long (*get_segment_base)(struct vcpu *v, enum segment seg); + unsigned long (*get_segment_base)(struct vcpu *v, enum x86_segment seg); + void (*get_segment_register)(struct vcpu *v, enum x86_segment seg, + struct segment_register *reg); /* * Re-set the value of CR3 that Xen runs on when handling VM exits @@ -89,6 +110,9 @@ struct hvm_function_table { void (*stts)(struct vcpu *v); void (*set_tsc_offset)(struct vcpu *v, u64 offset); + void (*inject_exception)(unsigned int trapnr, int errcode, + unsigned long cr2); + void (*init_ap_context)(struct vcpu_guest_context *ctxt, int vcpuid, int trampoline_vector); @@ -134,11 +158,15 @@ hvm_paging_enabled(struct vcpu *v) return hvm_funcs.paging_enabled(v); } +#ifdef __x86_64__ static inline int hvm_long_mode_enabled(struct vcpu *v) { return hvm_funcs.long_mode_enabled(v); } +#else +#define hvm_long_mode_enabled(v) (v,0) +#endif static inline int hvm_pae_enabled(struct vcpu *v) @@ -172,11 +200,18 @@ hvm_get_guest_ctrl_reg(struct vcpu *v, unsigned int num) } static inline unsigned long -hvm_get_segment_base(struct vcpu *v, enum segment seg) +hvm_get_segment_base(struct vcpu *v, enum x86_segment seg) { return hvm_funcs.get_segment_base(v, seg); } +static inline void +hvm_get_segment_register(struct vcpu *v, enum x86_segment seg, + struct segment_register *reg) +{ + hvm_funcs.get_segment_register(v, seg, reg); +} + void hvm_stts(struct vcpu *v); void hvm_set_guest_time(struct vcpu *v, u64 gtime); void hvm_freeze_time(struct vcpu *v); @@ -190,6 +225,12 @@ hvm_init_ap_context(struct vcpu_guest_context *ctxt, return hvm_funcs.init_ap_context(ctxt, vcpuid, trampoline_vector); } +static inline void +hvm_inject_exception(unsigned int trapnr, int errcode, unsigned long cr2) +{ + hvm_funcs.inject_exception(trapnr, errcode, cr2); +} + int hvm_bringup_ap(int vcpuid, int trampoline_vector); #endif /* __ASM_X86_HVM_HVM_H__ */ diff --git a/xen/include/asm-x86/hvm/svm/vmcb.h b/xen/include/asm-x86/hvm/svm/vmcb.h index 2d6a4ed0f3..81d41ea688 100644 --- a/xen/include/asm-x86/hvm/svm/vmcb.h +++ b/xen/include/asm-x86/hvm/svm/vmcb.h @@ -311,33 +311,9 @@ enum { SVM_CPU_STATE_ASSIST_ENABLED, }; -/* - * Attribute for segment selector. This is a copy of bit 40:47 & 52:55 of the - * segment descriptor. - */ -typedef union -{ - u16 bytes; - struct - { - u16 type:4; /* 0; Bit 40-43 */ - u16 s: 1; /* 4; Bit 44 */ - u16 dpl: 2; /* 5; Bit 45-46 */ - u16 p: 1; /* 7; Bit 47 */ - u16 avl: 1; /* 8; Bit 52 */ - u16 l: 1; /* 9; Bit 53 */ - u16 db: 1; /* 10; Bit 54 */ - u16 g: 1; /* 11; Bit 55 */ - } fields; -} __attribute__ ((packed)) segment_attributes_t; - -typedef struct -{ - u16 sel; - segment_attributes_t attributes; - u32 limit; - u64 base; -} __attribute__ ((packed)) segment_selector_t; +/* Definitions of segment state are borrowed by the generic HVM code. */ +typedef segment_attributes_t svm_segment_attributes_t; +typedef segment_register_t svm_segment_register_t; typedef union { @@ -426,16 +402,16 @@ struct vmcb_struct { u64 h_cr3; /* offset 0xB0 */ u64 res09[105]; /* offset 0xB8 pad to save area */ - segment_selector_t es; /* offset 1024 */ - segment_selector_t cs; - segment_selector_t ss; - segment_selector_t ds; - segment_selector_t fs; - segment_selector_t gs; - segment_selector_t gdtr; - segment_selector_t ldtr; - segment_selector_t idtr; - segment_selector_t tr; + svm_segment_register_t es; /* offset 1024 */ + svm_segment_register_t cs; + svm_segment_register_t ss; + svm_segment_register_t ds; + svm_segment_register_t fs; + svm_segment_register_t gs; + svm_segment_register_t gdtr; + svm_segment_register_t ldtr; + svm_segment_register_t idtr; + svm_segment_register_t tr; u64 res10[5]; u8 res11[3]; u8 cpl; diff --git a/xen/include/asm-x86/hvm/vlapic.h b/xen/include/asm-x86/hvm/vlapic.h index 003d93b389..2053fc56ec 100644 --- a/xen/include/asm-x86/hvm/vlapic.h +++ b/xen/include/asm-x86/hvm/vlapic.h @@ -54,7 +54,6 @@ struct vlapic { uint32_t timer_divisor; struct timer vlapic_timer; int timer_pending_count; - int flush_tpr_threshold; s_time_t timer_last_update; struct page_info *regs_page; void *regs; diff --git a/xen/include/asm-x86/hvm/vmx/vmx.h b/xen/include/asm-x86/hvm/vmx/vmx.h index e0ec14518b..701edf0e63 100644 --- a/xen/include/asm-x86/hvm/vmx/vmx.h +++ b/xen/include/asm-x86/hvm/vmx/vmx.h @@ -272,13 +272,6 @@ static inline int vmx_lme_is_set(struct vcpu *v) return efer & EFER_LME; } -/* Works only for vcpu == current */ -static inline void vmx_update_host_cr3(struct vcpu *v) -{ - ASSERT(v == current); - __vmwrite(HOST_CR3, v->arch.cr3); -} - static inline int vmx_pgbit_test(struct vcpu *v) { unsigned long cr0 = v->arch.hvm_vmx.cpu_shadow_cr0; diff --git a/xen/include/asm-x86/hypercall.h b/xen/include/asm-x86/hypercall.h index 69d7ba793d..860fb2ce77 100644 --- a/xen/include/asm-x86/hypercall.h +++ b/xen/include/asm-x86/hypercall.h @@ -6,6 +6,7 @@ #define __ASM_X86_HYPERCALL_H__ #include +#include extern long do_event_channel_op_compat( @@ -87,6 +88,10 @@ extern long arch_do_vcpu_op( int cmd, struct vcpu *v, XEN_GUEST_HANDLE(void) arg); +extern int +do_kexec( + unsigned long op, unsigned arg1, XEN_GUEST_HANDLE(void) uarg); + #ifdef __x86_64__ extern long diff --git a/xen/include/asm-x86/kexec.h b/xen/include/asm-x86/kexec.h new file mode 100644 index 0000000000..d137337da0 --- /dev/null +++ b/xen/include/asm-x86/kexec.h @@ -0,0 +1,20 @@ +#ifndef __X86_KEXEC_H__ +#define __X86_KEXEC_H__ + +#ifdef __x86_64__ +#include +#else +#include +#endif + +#endif /* __X86_KEXEC_H__ */ + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/include/asm-x86/page.h b/xen/include/asm-x86/page.h index b9c921a5cd..d384d34d65 100644 --- a/xen/include/asm-x86/page.h +++ b/xen/include/asm-x86/page.h @@ -33,9 +33,18 @@ /* Write a pte atomically to memory. */ #define l1e_write_atomic(l1ep, l1e) pte_write_atomic(l1ep, l1e_get_intpte(l1e)) -#define l2e_write_atomic(l2ep, l2e) pte_write_atomic(l2ep, l1e_get_intpte(l2e)) -#define l3e_write_atomic(l3ep, l3e) pte_write_atomic(l3ep, l1e_get_intpte(l3e)) -#define l4e_write_atomic(l4ep, l4e) pte_write_atomic(l4ep, l1e_get_intpte(l4e)) +#define l2e_write_atomic(l2ep, l2e) pte_write_atomic(l2ep, l2e_get_intpte(l2e)) +#define l3e_write_atomic(l3ep, l3e) pte_write_atomic(l3ep, l3e_get_intpte(l3e)) +#define l4e_write_atomic(l4ep, l4e) pte_write_atomic(l4ep, l4e_get_intpte(l4e)) + +/* + * Write a pte safely but non-atomically to memory. + * The PTE may become temporarily not-present during the update. + */ +#define l1e_write(l1ep, l1e) pte_write(l1ep, l1e_get_intpte(l1e)) +#define l2e_write(l2ep, l2e) pte_write(l2ep, l2e_get_intpte(l2e)) +#define l3e_write(l3ep, l3e) pte_write(l3ep, l3e_get_intpte(l3e)) +#define l4e_write(l4ep, l4e) pte_write(l4ep, l4e_get_intpte(l4e)) /* Get direct integer representation of a pte's contents (intpte_t). */ #define l1e_get_intpte(x) ((x).l1) diff --git a/xen/include/asm-x86/shadow.h b/xen/include/asm-x86/shadow.h index 7bf159b21d..78998edea9 100644 --- a/xen/include/asm-x86/shadow.h +++ b/xen/include/asm-x86/shadow.h @@ -246,7 +246,7 @@ shadow_vcpu_mode_translate(struct vcpu *v) /**************************************************************************/ /* Mode-specific entry points into the shadow code */ -struct x86_emulate_ctxt; +struct sh_emulate_ctxt; struct shadow_paging_mode { int (*page_fault )(struct vcpu *v, unsigned long va, struct cpu_user_regs *regs); @@ -267,18 +267,18 @@ struct shadow_paging_mode { void (*detach_old_tables )(struct vcpu *v); int (*x86_emulate_write )(struct vcpu *v, unsigned long va, void *src, u32 bytes, - struct x86_emulate_ctxt *ctxt); + struct sh_emulate_ctxt *sh_ctxt); int (*x86_emulate_cmpxchg )(struct vcpu *v, unsigned long va, unsigned long old, unsigned long new, unsigned int bytes, - struct x86_emulate_ctxt *ctxt); + struct sh_emulate_ctxt *sh_ctxt); int (*x86_emulate_cmpxchg8b )(struct vcpu *v, unsigned long va, unsigned long old_lo, unsigned long old_hi, unsigned long new_lo, unsigned long new_hi, - struct x86_emulate_ctxt *ctxt); + struct sh_emulate_ctxt *sh_ctxt); mfn_t (*make_monitor_table )(struct vcpu *v); void (*destroy_monitor_table )(struct vcpu *v, mfn_t mmfn); void * (*guest_map_l1e )(struct vcpu *v, unsigned long va, diff --git a/xen/include/asm-x86/softirq.h b/xen/include/asm-x86/softirq.h new file mode 100644 index 0000000000..3444d300c0 --- /dev/null +++ b/xen/include/asm-x86/softirq.h @@ -0,0 +1,6 @@ +#ifndef __ASM_SOFTIRQ_H__ +#define __ASM_SOFTIRQ_H__ + +#define NR_ARCH_SOFTIRQS 0 + +#endif /* __ASM_SOFTIRQ_H__ */ diff --git a/xen/include/asm-x86/x86_32/elf.h b/xen/include/asm-x86/x86_32/elf.h new file mode 100644 index 0000000000..1aa4e07148 --- /dev/null +++ b/xen/include/asm-x86/x86_32/elf.h @@ -0,0 +1,72 @@ +#ifndef __X86_32_ELF_H__ +#define __X86_32_ELF_H__ + +#include + +typedef struct { + unsigned long ebx; + unsigned long ecx; + unsigned long edx; + unsigned long esi; + unsigned long edi; + unsigned long ebp; + unsigned long eax; + unsigned long ds; + unsigned long es; + unsigned long fs; + unsigned long gs; + unsigned long orig_eax; + unsigned long eip; + unsigned long cs; + unsigned long eflags; + unsigned long esp; + unsigned long ss; +} ELF_Gregset; + +static inline void elf_core_save_regs(ELF_Gregset *core_regs, + crash_xen_core_t *xen_core_regs) +{ + unsigned long tmp; + + asm volatile("movl %%ebx,%0" : "=m"(core_regs->ebx)); + asm volatile("movl %%ecx,%0" : "=m"(core_regs->ecx)); + asm volatile("movl %%edx,%0" : "=m"(core_regs->edx)); + asm volatile("movl %%esi,%0" : "=m"(core_regs->esi)); + asm volatile("movl %%edi,%0" : "=m"(core_regs->edi)); + asm volatile("movl %%ebp,%0" : "=m"(core_regs->ebp)); + asm volatile("movl %%eax,%0" : "=m"(core_regs->eax)); + asm volatile("movw %%ds, %%ax;" :"=a"(core_regs->ds)); + asm volatile("movw %%es, %%ax;" :"=a"(core_regs->es)); + asm volatile("movw %%fs, %%ax;" :"=a"(core_regs->fs)); + asm volatile("movw %%gs, %%ax;" :"=a"(core_regs->gs)); + /* orig_eax not filled in for now */ + core_regs->eip = (unsigned long)current_text_addr(); + asm volatile("movw %%cs, %%ax;" :"=a"(core_regs->cs)); + asm volatile("pushfl; popl %0" :"=m"(core_regs->eflags)); + asm volatile("movl %%esp,%0" : "=m"(core_regs->esp)); + asm volatile("movw %%ss, %%ax;" :"=a"(core_regs->ss)); + + asm volatile("mov %%cr0, %0" : "=r" (tmp) : ); + xen_core_regs->cr0 = tmp; + + asm volatile("mov %%cr2, %0" : "=r" (tmp) : ); + xen_core_regs->cr2 = tmp; + + asm volatile("mov %%cr3, %0" : "=r" (tmp) : ); + xen_core_regs->cr3 = tmp; + + asm volatile("mov %%cr4, %0" : "=r" (tmp) : ); + xen_core_regs->cr4 = tmp; +} + +#endif /* __X86_32_ELF_H__ */ + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/include/asm-x86/x86_32/kexec.h b/xen/include/asm-x86/x86_32/kexec.h new file mode 100644 index 0000000000..aa6db02088 --- /dev/null +++ b/xen/include/asm-x86/x86_32/kexec.h @@ -0,0 +1,40 @@ +/****************************************************************************** + * kexec.h + * + * Based heavily on machine_kexec.c and kexec.h from Linux 2.6.19-rc1 + * + */ + +#ifndef __X86_KEXEC_X86_32_H__ +#define __X86_KEXEC_X86_32_H__ + +#include +#include +#include + +typedef asmlinkage void (*relocate_new_kernel_t)( + unsigned long indirection_page, + unsigned long page_list, + unsigned long start_address, + unsigned int has_pae); + +static inline void machine_kexec(xen_kexec_image_t *image) +{ + relocate_new_kernel_t rnk; + + rnk = (relocate_new_kernel_t) image->page_list[1]; + (*rnk)(image->indirection_page, (unsigned long)image->page_list, + image->start_address, (unsigned long)cpu_has_pae); +} + +#endif /* __X86_KEXEC_X86_32_H__ */ + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/include/asm-x86/x86_32/page-2level.h b/xen/include/asm-x86/x86_32/page-2level.h index a022b12087..86e8dbe30f 100644 --- a/xen/include/asm-x86/x86_32/page-2level.h +++ b/xen/include/asm-x86/x86_32/page-2level.h @@ -29,7 +29,8 @@ typedef l2_pgentry_t root_pgentry_t; #endif /* !__ASSEMBLY__ */ #define pte_read_atomic(ptep) (*(intpte_t *)(ptep)) -#define pte_write_atomic(ptep, pte) (*(intpte_t *)(ptep)) = (pte)) +#define pte_write_atomic(ptep, pte) ((*(intpte_t *)(ptep)) = (pte)) +#define pte_write(ptep, pte) ((*(intpte_t *)(ptep)) = (pte)) /* root table */ #define root_get_pfn l2e_get_pfn diff --git a/xen/include/asm-x86/x86_32/page-3level.h b/xen/include/asm-x86/x86_32/page-3level.h index 08864255ad..41bc5b0cff 100644 --- a/xen/include/asm-x86/x86_32/page-3level.h +++ b/xen/include/asm-x86/x86_32/page-3level.h @@ -48,6 +48,13 @@ typedef l3_pgentry_t root_pgentry_t; while ( (__npte = cmpxchg((intpte_t *)(ptep), __pte, (pte))) != __pte ) \ __pte = __npte; \ } while ( 0 ) +#define pte_write(ptep, pte) do { \ + *((u32 *)(ptep)+0) = 0; \ + wmb(); \ + *((u32 *)(ptep)+1) = (pte) >> 32; \ + wmb(); \ + *((u32 *)(ptep)+0) = (pte) >> 0; \ +} while ( 0 ) /* root table */ #define root_get_pfn l3e_get_pfn diff --git a/xen/include/asm-x86/x86_32/page.h b/xen/include/asm-x86/x86_32/page.h index 228445a62d..74e36a1a40 100644 --- a/xen/include/asm-x86/x86_32/page.h +++ b/xen/include/asm-x86/x86_32/page.h @@ -7,6 +7,8 @@ #define VADDR_BITS 32 #define VADDR_MASK (~0UL) +#define is_canonical_address(x) 1 + #include #ifdef CONFIG_X86_PAE # include diff --git a/xen/include/asm-x86/x86_64/elf.h b/xen/include/asm-x86/x86_64/elf.h new file mode 100644 index 0000000000..39c90b76f6 --- /dev/null +++ b/xen/include/asm-x86/x86_64/elf.h @@ -0,0 +1,92 @@ +#ifndef __X86_64_ELF_H__ +#define __X86_64_ELF_H__ + +#include + +typedef struct { + unsigned long r15; + unsigned long r14; + unsigned long r13; + unsigned long r12; + unsigned long rbp; + unsigned long rbx; + unsigned long r11; + unsigned long r10; + unsigned long r9; + unsigned long r8; + unsigned long rax; + unsigned long rcx; + unsigned long rdx; + unsigned long rsi; + unsigned long rdi; + unsigned long orig_rax; + unsigned long rip; + unsigned long cs; + unsigned long eflags; + unsigned long rsp; + unsigned long ss; + unsigned long thread_fs; + unsigned long thread_gs; + unsigned long ds; + unsigned long es; + unsigned long fs; + unsigned long gs; +} ELF_Gregset; + +static inline void elf_core_save_regs(ELF_Gregset *core_regs, + crash_xen_core_t *xen_core_regs) +{ + unsigned long tmp; + + asm volatile("movq %%r15,%0" : "=m"(core_regs->r15)); + asm volatile("movq %%r14,%0" : "=m"(core_regs->r14)); + asm volatile("movq %%r13,%0" : "=m"(core_regs->r13)); + asm volatile("movq %%r12,%0" : "=m"(core_regs->r12)); + asm volatile("movq %%rbp,%0" : "=m"(core_regs->rbp)); + asm volatile("movq %%rbx,%0" : "=m"(core_regs->rbx)); + asm volatile("movq %%r11,%0" : "=m"(core_regs->r11)); + asm volatile("movq %%r10,%0" : "=m"(core_regs->r10)); + asm volatile("movq %%r9,%0" : "=m"(core_regs->r9)); + asm volatile("movq %%r8,%0" : "=m"(core_regs->r8)); + asm volatile("movq %%rax,%0" : "=m"(core_regs->rax)); + asm volatile("movq %%rcx,%0" : "=m"(core_regs->rcx)); + asm volatile("movq %%rdx,%0" : "=m"(core_regs->rdx)); + asm volatile("movq %%rsi,%0" : "=m"(core_regs->rsi)); + asm volatile("movq %%rdi,%0" : "=m"(core_regs->rdi)); + /* orig_rax not filled in for now */ + core_regs->rip = (unsigned long)current_text_addr(); + asm volatile("movl %%cs, %%eax;" :"=a"(core_regs->cs)); + asm volatile("pushfq; popq %0" :"=m"(core_regs->eflags)); + asm volatile("movq %%rsp,%0" : "=m"(core_regs->rsp)); + asm volatile("movl %%ss, %%eax;" :"=a"(core_regs->ss)); + /* thread_fs not filled in for now */ + /* thread_gs not filled in for now */ + asm volatile("movl %%ds, %%eax;" :"=a"(core_regs->ds)); + asm volatile("movl %%es, %%eax;" :"=a"(core_regs->es)); + asm volatile("movl %%fs, %%eax;" :"=a"(core_regs->fs)); + asm volatile("movl %%gs, %%eax;" :"=a"(core_regs->gs)); + + asm volatile("mov %%cr0, %0" : "=r" (tmp) : ); + xen_core_regs->cr0 = tmp; + + asm volatile("mov %%cr2, %0" : "=r" (tmp) : ); + xen_core_regs->cr2 = tmp; + + asm volatile("mov %%cr3, %0" : "=r" (tmp) : ); + xen_core_regs->cr3 = tmp; + + asm volatile("mov %%cr4, %0" : "=r" (tmp) : ); + xen_core_regs->cr4 = tmp; +} + +#endif /* __X86_64_ELF_H__ */ + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/include/asm-x86/x86_64/kexec.h b/xen/include/asm-x86/x86_64/kexec.h new file mode 100644 index 0000000000..b9779aa9d7 --- /dev/null +++ b/xen/include/asm-x86/x86_64/kexec.h @@ -0,0 +1,39 @@ +/****************************************************************************** + * kexec.h + * + * Based heavily on machine_kexec.c and kexec.h from Linux 2.6.19-rc1 + * + */ + +#ifndef __X86_64_KEXEC_H__ +#define __X86_64_KEXEC_H__ + +#include +#include +#include + +typedef void (*relocate_new_kernel_t)( + unsigned long indirection_page, + unsigned long page_list, + unsigned long start_address); + +static inline void machine_kexec(xen_kexec_image_t *image) +{ + relocate_new_kernel_t rnk; + + rnk = (relocate_new_kernel_t) image->page_list[1]; + (*rnk)(image->indirection_page, (unsigned long)image->page_list, + image->start_address); +} + +#endif /* __X86_64_KEXEC_H__ */ + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/include/asm-x86/x86_64/page.h b/xen/include/asm-x86/x86_64/page.h index 4e0f3675ce..fca1173c1e 100644 --- a/xen/include/asm-x86/x86_64/page.h +++ b/xen/include/asm-x86/x86_64/page.h @@ -24,6 +24,8 @@ #define PADDR_MASK ((1UL << PADDR_BITS)-1) #define VADDR_MASK ((1UL << VADDR_BITS)-1) +#define is_canonical_address(x) (((long)(x) >> 47) == ((long)(x) >> 63)) + #ifndef __ASSEMBLY__ #include @@ -42,7 +44,8 @@ typedef l4_pgentry_t root_pgentry_t; #endif /* !__ASSEMBLY__ */ #define pte_read_atomic(ptep) (*(intpte_t *)(ptep)) -#define pte_write_atomic(ptep, pte) (*(intpte_t *)(ptep)) = (pte)) +#define pte_write_atomic(ptep, pte) ((*(intpte_t *)(ptep)) = (pte)) +#define pte_write(ptep, pte) ((*(intpte_t *)(ptep)) = (pte)) /* Given a virtual address, get an entry offset into a linear page table. */ #define l1_linear_offset(_a) (((_a) & VADDR_MASK) >> L1_PAGETABLE_SHIFT) diff --git a/xen/include/asm-x86/x86_emulate.h b/xen/include/asm-x86/x86_emulate.h index 3bd912ce68..89a42da7b0 100644 --- a/xen/include/asm-x86/x86_emulate.h +++ b/xen/include/asm-x86/x86_emulate.h @@ -12,34 +12,36 @@ struct x86_emulate_ctxt; /* - * x86_emulate_ops: - * + * Comprehensive enumeration of x86 segment registers. Note that the system + * registers (TR, LDTR, GDTR, IDTR) are never referenced by the emulator. + */ +enum x86_segment { + /* General purpose. */ + x86_seg_cs, + x86_seg_ss, + x86_seg_ds, + x86_seg_es, + x86_seg_fs, + x86_seg_gs, + /* System. */ + x86_seg_tr, + x86_seg_ldtr, + x86_seg_gdtr, + x86_seg_idtr +}; + +/* * These operations represent the instruction emulator's interface to memory. - * There are two categories of operation: those that act on ordinary memory - * regions (*_std), and those that act on memory regions known to require - * special treatment or emulation (*_emulated). - * - * The emulator assumes that an instruction accesses only one 'emulated memory' - * location, that this location is the given linear faulting address (cr2), and - * that this is one of the instruction's data operands. Instruction fetches and - * stack operations are assumed never to access emulated memory. The emulator - * automatically deduces which operand of a string-move operation is accessing - * emulated memory, and assumes that the other operand accesses normal memory. * * NOTES: - * 1. The emulator isn't very smart about emulated vs. standard memory. - * 'Emulated memory' access addresses should be checked for sanity. - * 'Normal memory' accesses may fault, and the caller must arrange to - * detect and handle reentrancy into the emulator via recursive faults. - * Accesses may be unaligned and may cross page boundaries. - * 2. If the access fails (cannot emulate, or a standard access faults) then + * 1. If the access fails (cannot emulate, or a standard access faults) then * it is up to the memop to propagate the fault to the guest VM via * some out-of-band mechanism, unknown to the emulator. The memop signals * failure by returning X86EMUL_PROPAGATE_FAULT to the emulator, which will * then immediately bail. - * 3. Valid access sizes are 1, 2, 4 and 8 bytes. On x86/32 systems only + * 2. Valid access sizes are 1, 2, 4 and 8 bytes. On x86/32 systems only * cmpxchg8b_emulated need support 8-byte accesses. - * 4. The emulator cannot handle 64-bit mode emulation on an x86/32 system. + * 3. The emulator cannot handle 64-bit mode emulation on an x86/32 system. */ /* Access completed successfully: continue emulation as normal. */ #define X86EMUL_CONTINUE 0 @@ -52,74 +54,63 @@ struct x86_emulate_ctxt; struct x86_emulate_ops { /* - * read_std: Read bytes of standard (non-emulated/special) memory. - * Used for instruction fetch, stack operations, and others. - * @addr: [IN ] Linear address from which to read. - * @val: [OUT] Value read from memory, zero-extended to 'u_long'. + * All functions: + * @seg: [IN ] Segment being dereferenced (specified as x86_seg_??). + * @offset:[IN ] Offset within segment. + * @ctxt: [IN ] Emulation context info as passed to the emulator. + */ + + /* + * read: Emulate a memory read. + * @val: [OUT] Value read from memory, zero-extended to 'ulong'. * @bytes: [IN ] Number of bytes to read from memory. */ - int (*read_std)( - unsigned long addr, + int (*read)( + enum x86_segment seg, + unsigned long offset, unsigned long *val, unsigned int bytes, struct x86_emulate_ctxt *ctxt); /* - * write_std: Write bytes of standard (non-emulated/special) memory. - * Used for stack operations, and others. - * @addr: [IN ] Linear address to which to write. - * @val: [IN ] Value to write to memory (low-order bytes used as req'd). - * @bytes: [IN ] Number of bytes to write to memory. + * insn_fetch: Emulate fetch from instruction byte stream. + * Parameters are same as for 'read'. @seg is always x86_seg_cs. */ - int (*write_std)( - unsigned long addr, - unsigned long val, - unsigned int bytes, - struct x86_emulate_ctxt *ctxt); - - /* - * read_emulated: Read bytes from emulated/special memory area. - * @addr: [IN ] Linear address from which to read. - * @val: [OUT] Value read from memory, zero-extended to 'u_long'. - * @bytes: [IN ] Number of bytes to read from memory. - */ - int (*read_emulated)( - unsigned long addr, + int (*insn_fetch)( + enum x86_segment seg, + unsigned long offset, unsigned long *val, unsigned int bytes, struct x86_emulate_ctxt *ctxt); /* - * write_emulated: Read bytes from emulated/special memory area. - * @addr: [IN ] Linear address to which to write. + * write: Emulate a memory write. * @val: [IN ] Value to write to memory (low-order bytes used as req'd). * @bytes: [IN ] Number of bytes to write to memory. */ - int (*write_emulated)( - unsigned long addr, + int (*write)( + enum x86_segment seg, + unsigned long offset, unsigned long val, unsigned int bytes, struct x86_emulate_ctxt *ctxt); /* - * cmpxchg_emulated: Emulate an atomic (LOCKed) CMPXCHG operation on an - * emulated/special memory area. - * @addr: [IN ] Linear address to access. + * cmpxchg: Emulate an atomic (LOCKed) CMPXCHG operation. * @old: [IN ] Value expected to be current at @addr. * @new: [IN ] Value to write to @addr. * @bytes: [IN ] Number of bytes to access using CMPXCHG. */ - int (*cmpxchg_emulated)( - unsigned long addr, + int (*cmpxchg)( + enum x86_segment seg, + unsigned long offset, unsigned long old, unsigned long new, unsigned int bytes, struct x86_emulate_ctxt *ctxt); /* - * cmpxchg8b_emulated: Emulate an atomic (LOCKed) CMPXCHG8B operation on an - * emulated/special memory area. - * @addr: [IN ] Linear address to access. + * cmpxchg8b: Emulate an atomic (LOCKed) CMPXCHG8B operation. * @old: [IN ] Value expected to be current at @addr. * @new: [IN ] Value to write to @addr. * NOTES: @@ -128,8 +119,9 @@ struct x86_emulate_ops * 2. Not defining this function (i.e., specifying NULL) is equivalent * to defining a function that always returns X86EMUL_UNHANDLEABLE. */ - int (*cmpxchg8b_emulated)( - unsigned long addr, + int (*cmpxchg8b)( + enum x86_segment seg, + unsigned long offset, unsigned long old_lo, unsigned long old_hi, unsigned long new_lo, @@ -137,20 +129,6 @@ struct x86_emulate_ops struct x86_emulate_ctxt *ctxt); }; -/* Standard reader/writer functions that callers may wish to use. */ -extern int -x86_emulate_read_std( - unsigned long addr, - unsigned long *val, - unsigned int bytes, - struct x86_emulate_ctxt *ctxt); -extern int -x86_emulate_write_std( - unsigned long addr, - unsigned long val, - unsigned int bytes, - struct x86_emulate_ctxt *ctxt); - struct cpu_user_regs; struct x86_emulate_ctxt @@ -158,9 +136,6 @@ struct x86_emulate_ctxt /* Register state before/after emulation. */ struct cpu_user_regs *regs; - /* Linear faulting address (if emulating a page-faulting instruction). */ - unsigned long cr2; - /* Emulated execution mode, represented by an X86EMUL_MODE value. */ int mode; }; diff --git a/xen/include/public/arch-ia64.h b/xen/include/public/arch-ia64.h index 01f6362d82..d478fb0afa 100644 --- a/xen/include/public/arch-ia64.h +++ b/xen/include/public/arch-ia64.h @@ -59,11 +59,20 @@ DEFINE_XEN_GUEST_HANDLE(xen_pfn_t); /* Arch specific VIRQs definition */ #define VIRQ_ITC VIRQ_ARCH_0 /* V. Virtual itc timer */ +#define VIRQ_MCA_CMC VIRQ_ARCH_1 /* MCA cmc interrupt */ +#define VIRQ_MCA_CPE VIRQ_ARCH_2 /* MCA cpe interrupt */ /* Maximum number of virtual CPUs in multi-processor guests. */ /* WARNING: before changing this, check that shared_info fits on a page */ #define MAX_VIRT_CPUS 64 +/* + * HVM_PARAM_PAE_ENABLED is meaningless on ia64, so we overload this + * entry to store the number of vCPUs. XXX Need arch-specific extentions + * for xc_get/set_hvm_param(). + */ +#define HVM_PARAM_VCPUS HVM_PARAM_PAE_ENABLED + #ifndef __ASSEMBLY__ typedef unsigned long xen_ulong_t; @@ -374,6 +383,12 @@ DEFINE_XEN_GUEST_HANDLE(vcpu_guest_context_t); /* expose the p2m table into domain */ #define IA64_DOM0VP_expose_p2m 7 +/* xen perfmon */ +#define IA64_DOM0VP_perfmon 8 + +/* gmfn version of IA64_DOM0VP_add_physmap */ +#define IA64_DOM0VP_add_physmap_with_gmfn 9 + // flags for page assignement to pseudo physical address space #define _ASSIGN_readonly 0 #define ASSIGN_readonly (1UL << _ASSIGN_readonly) @@ -460,6 +475,25 @@ struct xen_ia64_boot_param { (((unsigned long)(addr) & XENCOMM_INLINE_MASK) == XENCOMM_INLINE_FLAG) #define XENCOMM_INLINE_ADDR(addr) \ ((unsigned long)(addr) & ~XENCOMM_INLINE_MASK) + +/* xen perfmon */ +#ifdef XEN +#ifndef __ASSEMBLY__ +#ifndef _ASM_IA64_PERFMON_H + +#include // asm/perfmon.h requires struct list_head +#include +// for PFM_xxx and pfarg_features_t, pfarg_context_t, pfarg_reg_t, pfarg_load_t + +#endif /* _ASM_IA64_PERFMON_H */ + +DEFINE_XEN_GUEST_HANDLE(pfarg_features_t); +DEFINE_XEN_GUEST_HANDLE(pfarg_context_t); +DEFINE_XEN_GUEST_HANDLE(pfarg_reg_t); +DEFINE_XEN_GUEST_HANDLE(pfarg_load_t); +#endif /* __ASSEMBLY__ */ +#endif /* XEN */ + #endif /* __HYPERVISOR_IF_IA64_H__ */ /* diff --git a/xen/include/public/domctl.h b/xen/include/public/domctl.h index 37618ad827..da0b5b2868 100644 --- a/xen/include/public/domctl.h +++ b/xen/include/public/domctl.h @@ -93,7 +93,7 @@ struct xen_domctl_getdomaininfo { uint32_t flags; /* XEN_DOMINF_* */ uint64_t tot_pages; uint64_t max_pages; - uint64_t shared_info_frame; /* MFN of shared_info struct */ + uint64_t shared_info_frame; /* GMFN of shared_info struct */ uint64_t cpu_time; uint32_t nr_online_vcpus; /* Number of VCPUs currently online. */ uint32_t max_vcpu_id; /* Maximum VCPUID in use by this domain. */ diff --git a/xen/include/public/elfnote.h b/xen/include/public/elfnote.h index ccf959d6e1..4924767d30 100644 --- a/xen/include/public/elfnote.h +++ b/xen/include/public/elfnote.h @@ -147,6 +147,25 @@ */ #define XEN_ELFNOTE_HV_START_LOW 12 +/* + * System information exported through crash notes. + * + * The kexec / kdump code will create one XEN_ELFNOTE_CRASH_INFO + * note in case of a system crash. This note will contain various + * information about the system, see xen/include/xen/elfcore.h. + */ +#define XEN_ELFNOTE_CRASH_INFO 0x1000001 + +/* + * System registers exported through crash notes. + * + * The kexec / kdump code will create one XEN_ELFNOTE_CRASH_REGS + * note per cpu in case of a system crash. This note is architecture + * specific and will contain registers not saved in the "CORE" note. + * See xen/include/xen/elfcore.h for more information. + */ +#define XEN_ELFNOTE_CRASH_REGS 0x1000002 + #endif /* __XEN_PUBLIC_ELFNOTE_H__ */ /* diff --git a/xen/include/public/io/fbif.h b/xen/include/public/io/fbif.h new file mode 100644 index 0000000000..7b8861d51e --- /dev/null +++ b/xen/include/public/io/fbif.h @@ -0,0 +1,116 @@ +/* + * fbif.h -- Xen virtual frame buffer device + * + * Copyright (C) 2005 Anthony Liguori + * Copyright (C) 2006 Red Hat, Inc., Markus Armbruster + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#ifndef __XEN_PUBLIC_IO_FBIF_H__ +#define __XEN_PUBLIC_IO_FBIF_H__ + +#include + +/* Out events (frontend -> backend) */ + +/* + * Out events may be sent only when requested by backend, and receipt + * of an unknown out event is an error. + */ + +/* Event type 1 currently not used */ +/* + * Framebuffer update notification event + * Capable frontend sets feature-update in xenstore. + * Backend requests it by setting request-update in xenstore. + */ +#define XENFB_TYPE_UPDATE 2 + +struct xenfb_update +{ + __u8 type; /* XENFB_TYPE_UPDATE */ + __s32 x; /* source x */ + __s32 y; /* source y */ + __s32 width; /* rect width */ + __s32 height; /* rect height */ +}; + +#define XENFB_OUT_EVENT_SIZE 40 + +union xenfb_out_event +{ + __u8 type; + struct xenfb_update update; + char pad[XENFB_OUT_EVENT_SIZE]; +}; + +/* In events (backend -> frontend) */ + +/* + * Frontends should ignore unknown in events. + * No in events currently defined. + */ + +#define XENFB_IN_EVENT_SIZE 40 + +union xenfb_in_event +{ + __u8 type; + char pad[XENFB_IN_EVENT_SIZE]; +}; + +/* shared page */ + +#define XENFB_IN_RING_SIZE 1024 +#define XENFB_IN_RING_LEN (XENFB_IN_RING_SIZE / XENFB_IN_EVENT_SIZE) +#define XENFB_IN_RING_OFFS 1024 +#define XENFB_IN_RING(page) \ + ((union xenfb_in_event *)((char *)(page) + XENFB_IN_RING_OFFS)) +#define XENFB_IN_RING_REF(page, idx) \ + (XENFB_IN_RING((page))[(idx) % XENFB_IN_RING_LEN]) + +#define XENFB_OUT_RING_SIZE 2048 +#define XENFB_OUT_RING_LEN (XENFB_OUT_RING_SIZE / XENFB_OUT_EVENT_SIZE) +#define XENFB_OUT_RING_OFFS (XENFB_IN_RING_OFFS + XENFB_IN_RING_SIZE) +#define XENFB_OUT_RING(page) \ + ((union xenfb_out_event *)((char *)(page) + XENFB_OUT_RING_OFFS)) +#define XENFB_OUT_RING_REF(page, idx) \ + (XENFB_OUT_RING((page))[(idx) % XENFB_OUT_RING_LEN]) + +struct xenfb_page +{ + __u32 in_cons, in_prod; + __u32 out_cons, out_prod; + + __s32 width; /* the width of the framebuffer (in pixels) */ + __s32 height; /* the height of the framebuffer (in pixels) */ + __u32 line_length; /* the length of a row of pixels (in bytes) */ + __u32 mem_length; /* the length of the framebuffer (in bytes) */ + __u8 depth; /* the depth of a pixel (in bits) */ + + /* + * Framebuffer page directory + * + * Each directory page holds PAGE_SIZE / sizeof(*pd) + * framebuffer pages, and can thus map up to PAGE_SIZE * + * PAGE_SIZE / sizeof(*pd) bytes. With PAGE_SIZE == 4096 and + * sizeof(unsigned long) == 4, that's 4 Megs. Two directory + * pages should be enough for a while. + */ + unsigned long pd[2]; +}; + +/* + * Wart: xenkbd needs to know resolution. Put it here until a better + * solution is found, but don't leak it to the backend. + */ +#ifdef __KERNEL__ +#define XENFB_WIDTH 800 +#define XENFB_HEIGHT 600 +#define XENFB_DEPTH 32 +#endif + +#endif diff --git a/xen/include/public/io/kbdif.h b/xen/include/public/io/kbdif.h new file mode 100644 index 0000000000..be8c91545a --- /dev/null +++ b/xen/include/public/io/kbdif.h @@ -0,0 +1,108 @@ +/* + * kbdif.h -- Xen virtual keyboard/mouse + * + * Copyright (C) 2005 Anthony Liguori + * Copyright (C) 2006 Red Hat, Inc., Markus Armbruster + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#ifndef __XEN_PUBLIC_IO_KBDIF_H__ +#define __XEN_PUBLIC_IO_KBDIF_H__ + +#include + +/* In events (backend -> frontend) */ + +/* + * Frontends should ignore unknown in events. + */ + +/* Pointer movement event */ +#define XENKBD_TYPE_MOTION 1 +/* Event type 2 currently not used */ +/* Key event (includes pointer buttons) */ +#define XENKBD_TYPE_KEY 3 +/* + * Pointer position event + * Capable backend sets feature-abs-pointer in xenstore. + * Frontend requests ot instead of XENKBD_TYPE_MOTION by setting + * request-abs-update in xenstore. + */ +#define XENKBD_TYPE_POS 4 + +struct xenkbd_motion +{ + __u8 type; /* XENKBD_TYPE_MOTION */ + __s32 rel_x; /* relative X motion */ + __s32 rel_y; /* relative Y motion */ +}; + +struct xenkbd_key +{ + __u8 type; /* XENKBD_TYPE_KEY */ + __u8 pressed; /* 1 if pressed; 0 otherwise */ + __u32 keycode; /* KEY_* from linux/input.h */ +}; + +struct xenkbd_position +{ + __u8 type; /* XENKBD_TYPE_POS */ + __s32 abs_x; /* absolute X position (in FB pixels) */ + __s32 abs_y; /* absolute Y position (in FB pixels) */ +}; + +#define XENKBD_IN_EVENT_SIZE 40 + +union xenkbd_in_event +{ + __u8 type; + struct xenkbd_motion motion; + struct xenkbd_key key; + struct xenkbd_position pos; + char pad[XENKBD_IN_EVENT_SIZE]; +}; + +/* Out events (frontend -> backend) */ + +/* + * Out events may be sent only when requested by backend, and receipt + * of an unknown out event is an error. + * No out events currently defined. + */ + +#define XENKBD_OUT_EVENT_SIZE 40 + +union xenkbd_out_event +{ + __u8 type; + char pad[XENKBD_OUT_EVENT_SIZE]; +}; + +/* shared page */ + +#define XENKBD_IN_RING_SIZE 2048 +#define XENKBD_IN_RING_LEN (XENKBD_IN_RING_SIZE / XENKBD_IN_EVENT_SIZE) +#define XENKBD_IN_RING_OFFS 1024 +#define XENKBD_IN_RING(page) \ + ((union xenkbd_in_event *)((char *)(page) + XENKBD_IN_RING_OFFS)) +#define XENKBD_IN_RING_REF(page, idx) \ + (XENKBD_IN_RING((page))[(idx) % XENKBD_IN_RING_LEN]) + +#define XENKBD_OUT_RING_SIZE 1024 +#define XENKBD_OUT_RING_LEN (XENKBD_OUT_RING_SIZE / XENKBD_OUT_EVENT_SIZE) +#define XENKBD_OUT_RING_OFFS (XENKBD_IN_RING_OFFS + XENKBD_IN_RING_SIZE) +#define XENKBD_OUT_RING(page) \ + ((union xenkbd_out_event *)((char *)(page) + XENKBD_OUT_RING_OFFS)) +#define XENKBD_OUT_RING_REF(page, idx) \ + (XENKBD_OUT_RING((page))[(idx) % XENKBD_OUT_RING_LEN]) + +struct xenkbd_page +{ + __u32 in_cons, in_prod; + __u32 out_cons, out_prod; +}; + +#endif diff --git a/xen/include/public/kexec.h b/xen/include/public/kexec.h new file mode 100644 index 0000000000..ca6e85b701 --- /dev/null +++ b/xen/include/public/kexec.h @@ -0,0 +1,137 @@ +/****************************************************************************** + * kexec.h - Public portion + * + * Xen port written by: + * - Simon 'Horms' Horman + * - Magnus Damm + */ + +#ifndef _XEN_PUBLIC_KEXEC_H +#define _XEN_PUBLIC_KEXEC_H + + +/* This file describes the Kexec / Kdump hypercall interface for Xen. + * + * Kexec under vanilla Linux allows a user to reboot the physical machine + * into a new user-specified kernel. The Xen port extends this idea + * to allow rebooting of the machine from dom0. When kexec for dom0 + * is used to reboot, both the hypervisor and the domains get replaced + * with some other kernel. It is possible to kexec between vanilla + * Linux and Xen and back again. Xen to Xen works well too. + * + * The hypercall interface for kexec can be divided into three main + * types of hypercall operations: + * + * 1) Range information: + * This is used by the dom0 kernel to ask the hypervisor about various + * address information. This information is needed to allow kexec-tools + * to fill in the ELF headers for /proc/vmcore properly. + * + * 2) Load and unload of images: + * There are no big surprises here, the kexec binary from kexec-tools + * runs in userspace in dom0. The tool loads/unloads data into the + * dom0 kernel such as new kernel, initramfs and hypervisor. When + * loaded the dom0 kernel performs a load hypercall operation, and + * before releasing all page references the dom0 kernel calls unload. + * + * 3) Kexec operation: + * This is used to start a previously loaded kernel. + */ + +#include "xen.h" + +#if defined(__i386__) || defined(__x86_64__) +#define KEXEC_XEN_NO_PAGES 17 +#endif + +/* + * Prototype for this hypercall is: + * int kexec_op(int cmd, void *args) + * @cmd == KEXEC_CMD_... + * KEXEC operation to perform + * @args == Operation-specific extra arguments (NULL if none). + */ + +/* + * Kexec supports two types of operation: + * - kexec into a regular kernel, very similar to a standard reboot + * - KEXEC_TYPE_DEFAULT is used to specify this type + * - kexec into a special "crash kernel", aka kexec-on-panic + * - KEXEC_TYPE_CRASH is used to specify this type + * - parts of our system may be broken at kexec-on-panic time + * - the code should be kept as simple and self-contained as possible + */ + +#define KEXEC_TYPE_DEFAULT 0 +#define KEXEC_TYPE_CRASH 1 + + +/* The kexec implementation for Xen allows the user to load two + * types of kernels, KEXEC_TYPE_DEFAULT and KEXEC_TYPE_CRASH. + * All data needed for a kexec reboot is kept in one xen_kexec_image_t + * per "instance". The data mainly consists of machine address lists to pages + * together with destination addresses. The data in xen_kexec_image_t + * is passed to the "code page" which is one page of code that performs + * the final relocations before jumping to the new kernel. + */ + +typedef struct xen_kexec_image { +#if defined(__i386__) || defined(__x86_64__) + unsigned long page_list[KEXEC_XEN_NO_PAGES]; +#endif + unsigned long indirection_page; + unsigned long start_address; +} xen_kexec_image_t; + +/* + * Perform kexec having previously loaded a kexec or kdump kernel + * as appropriate. + * type == KEXEC_TYPE_DEFAULT or KEXEC_TYPE_CRASH [in] + */ +#define KEXEC_CMD_kexec 0 +typedef struct xen_kexec_exec { + int type; +} xen_kexec_exec_t; + +/* + * Load/Unload kernel image for kexec or kdump. + * type == KEXEC_TYPE_DEFAULT or KEXEC_TYPE_CRASH [in] + * image == relocation information for kexec (ignored for unload) [in] + */ +#define KEXEC_CMD_kexec_load 1 +#define KEXEC_CMD_kexec_unload 2 +typedef struct xen_kexec_load { + int type; + xen_kexec_image_t image; +} xen_kexec_load_t; + +#define KEXEC_RANGE_MA_CRASH 0 /* machine address and size of crash area */ +#define KEXEC_RANGE_MA_XEN 1 /* machine address and size of Xen itself */ +#define KEXEC_RANGE_MA_CPU 2 /* machine address and size of a CPU note */ + +/* + * Find the address and size of certain memory areas + * range == KEXEC_RANGE_... [in] + * nr == physical CPU number (starting from 0) if KEXEC_RANGE_MA_CPU [in] + * size == number of bytes reserved in window [out] + * start == address of the first byte in the window [out] + */ +#define KEXEC_CMD_kexec_get_range 3 +typedef struct xen_kexec_range { + int range; + int nr; + unsigned long size; + unsigned long start; +} xen_kexec_range_t; + +#endif /* _XEN_PUBLIC_KEXEC_H */ + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/include/public/memory.h b/xen/include/public/memory.h index e28ed12620..acbc89b7e7 100644 --- a/xen/include/public/memory.h +++ b/xen/include/public/memory.h @@ -222,7 +222,8 @@ DEFINE_XEN_GUEST_HANDLE(xen_translate_gpfn_list_t); /* * Returns the pseudo-physical memory map as it was when the domain - * was started. + * was started (specified by XENMEM_set_memory_map). + * arg == addr of xen_memory_map_t. */ #define XENMEM_memory_map 9 struct xen_memory_map { @@ -245,9 +246,23 @@ DEFINE_XEN_GUEST_HANDLE(xen_memory_map_t); /* * Returns the real physical memory map. Passes the same structure as * XENMEM_memory_map. + * arg == addr of xen_memory_map_t. */ #define XENMEM_machine_memory_map 10 +/* + * Set the pseudo-physical memory map of a domain, as returned by + * XENMEM_memory_map. + * arg == addr of xen_foreign_memory_map_t. + */ +#define XENMEM_set_memory_map 13 +struct xen_foreign_memory_map { + domid_t domid; + struct xen_memory_map map; +}; +typedef struct xen_foreign_memory_map xen_foreign_memory_map_t; +DEFINE_XEN_GUEST_HANDLE(xen_foreign_memory_map_t); + #endif /* __XEN_PUBLIC_MEMORY_H__ */ /* diff --git a/xen/include/public/physdev.h b/xen/include/public/physdev.h index 5692cee897..6f78a09fe1 100644 --- a/xen/include/public/physdev.h +++ b/xen/include/public/physdev.h @@ -152,6 +152,7 @@ DEFINE_XEN_GUEST_HANDLE(physdev_op_t); #define PHYSDEVOP_APIC_READ PHYSDEVOP_apic_read #define PHYSDEVOP_APIC_WRITE PHYSDEVOP_apic_write #define PHYSDEVOP_ASSIGN_VECTOR PHYSDEVOP_alloc_irq_vector +#define PHYSDEVOP_FREE_VECTOR PHYSDEVOP_free_irq_vector #define PHYSDEVOP_IRQ_NEEDS_UNMASK_NOTIFY XENIRQSTAT_needs_eoi #define PHYSDEVOP_IRQ_SHARED XENIRQSTAT_shared diff --git a/xen/include/public/xenoprof.h b/xen/include/public/xenoprof.h index a624b73249..5bd4c0dc38 100644 --- a/xen/include/public/xenoprof.h +++ b/xen/include/public/xenoprof.h @@ -29,6 +29,8 @@ #ifndef __XEN_PUBLIC_XENOPROF_H__ #define __XEN_PUBLIC_XENOPROF_H__ +#include "xen.h" + /* * Commands to HYPERVISOR_xenoprof_op(). */ diff --git a/xen/include/xen/domain_page.h b/xen/include/xen/domain_page.h index 3f83b2a6db..7067f33ff7 100644 --- a/xen/include/xen/domain_page.h +++ b/xen/include/xen/domain_page.h @@ -34,13 +34,6 @@ void unmap_domain_page(void *va); void *map_domain_page_global(unsigned long mfn); void unmap_domain_page_global(void *va); -/* - * Convert a VA (within a page previously mapped in the context of the - * currently-executing VCPU via a call to map_domain_page(), or via a - * previous call to map_domain_page_global()) to the mapped machine address. - */ -paddr_t maddr_from_mapped_domain_page(void *va); - #define DMCACHE_ENTRY_VALID 1U #define DMCACHE_ENTRY_HELD 2U @@ -109,8 +102,6 @@ domain_mmap_cache_destroy(struct domain_mmap_cache *cache) #define map_domain_page_global(mfn) maddr_to_virt((mfn)< +#include +#include +#include + +#define NT_PRSTATUS 1 + +typedef struct +{ + int signo; /* signal number */ + int code; /* extra code */ + int errno; /* errno */ +} ELF_Signifo; + +/* These seem to be the same length on all architectures on Linux */ +typedef int ELF_Pid; +typedef struct { + long tv_sec; + long tv_usec; +} ELF_Timeval; + +/* + * Definitions to generate Intel SVR4-like core files. + * These mostly have the same names as the SVR4 types with "elf_" + * tacked on the front to prevent clashes with linux definitions, + * and the typedef forms have been avoided. This is mostly like + * the SVR4 structure, but more Linuxy, with things that Linux does + * not support and which gdb doesn't really use excluded. + */ +typedef struct +{ + ELF_Signifo pr_info; /* Info associated with signal */ + short pr_cursig; /* Current signal */ + unsigned long pr_sigpend; /* Set of pending signals */ + unsigned long pr_sighold; /* Set of held signals */ + ELF_Pid pr_pid; + ELF_Pid pr_ppid; + ELF_Pid pr_pgrp; + ELF_Pid pr_sid; + ELF_Timeval pr_utime; /* User time */ + ELF_Timeval pr_stime; /* System time */ + ELF_Timeval pr_cutime; /* Cumulative user time */ + ELF_Timeval pr_cstime; /* Cumulative system time */ + ELF_Gregset pr_reg; /* GP registers - from asm header file */ + int pr_fpvalid; /* True if math co-processor being used. */ +} ELF_Prstatus; + +/* + * The following data structures provide 64-bit ELF notes. In theory it should + * be possible to support both 64-bit and 32-bit ELF files, but to keep it + * simple we only do 64-bit. + * + * Please note that the current code aligns the 64-bit notes in the same + * way as Linux does. We are not following the 64-bit ELF spec, no one does. + * + * We are avoiding two problems by restricting us to 64-bit notes only: + * - Alignment of notes change with the word size. Ick. + * - We would need to tell kexec-tools which format we are using in the + * hypervisor to make sure the right ELF format is generated. + * That requires infrastructure. Let's not. + */ + +#define NOTE_ALIGN(x, n) ((x + ((1 << n) - 1)) / (1 << n)) +#define PAD32(x) u32 pad_data[NOTE_ALIGN(x, 2)] + +#define TYPEDEF_NOTE(type, strlen, desctype) \ + typedef struct { \ + union { \ + struct { \ + Elf_Note note; \ + unsigned char name[strlen]; \ + } note; \ + PAD32(sizeof(Elf_Note) + strlen); \ + } note; \ + union { \ + desctype desc; \ + PAD32(sizeof(desctype)); \ + } desc; \ + } __attribute__ ((packed)) type + +#define CORE_STR "CORE" +#define CORE_STR_LEN 5 /* including terminating zero */ + +TYPEDEF_NOTE(crash_note_core_t, CORE_STR_LEN, ELF_Prstatus); + +#define XEN_STR "Xen" +#define XEN_STR_LEN 4 /* including terminating zero */ + +TYPEDEF_NOTE(crash_note_xen_core_t, XEN_STR_LEN, crash_xen_core_t); + +typedef struct { + unsigned long xen_major_version; + unsigned long xen_minor_version; + unsigned long xen_extra_version; + unsigned long xen_changeset; + unsigned long xen_compiler; + unsigned long xen_compile_date; + unsigned long xen_compile_time; + unsigned long tainted; +#ifdef CONFIG_X86 + unsigned long dom0_pfn_to_mfn_frame_list_list; +#endif +} crash_xen_info_t; + +TYPEDEF_NOTE(crash_note_xen_info_t, XEN_STR_LEN, crash_xen_info_t); + +typedef struct { + crash_note_core_t core; + crash_note_xen_core_t xen_regs; + crash_note_xen_info_t xen_info; +} __attribute__ ((packed)) crash_note_t; + +#define setup_crash_note(np, member, str, str_len, id) \ + np->member.note.note.note.namesz = str_len; \ + np->member.note.note.note.descsz = sizeof(np->member.desc.desc); \ + np->member.note.note.note.type = id; \ + memcpy(np->member.note.note.name, str, str_len) + +#endif /* __ELFCOREC_H__ */ + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/include/xen/hypercall.h b/xen/include/xen/hypercall.h index 16cde7b738..b8fc94d2c6 100644 --- a/xen/include/xen/hypercall.h +++ b/xen/include/xen/hypercall.h @@ -102,4 +102,10 @@ do_hvm_op( unsigned long op, XEN_GUEST_HANDLE(void) arg); +extern long +do_kexec_op( + unsigned long op, + int arg1, + XEN_GUEST_HANDLE(void) arg); + #endif /* __XEN_HYPERCALL_H__ */ diff --git a/xen/include/xen/kexec.h b/xen/include/xen/kexec.h new file mode 100644 index 0000000000..c22bfc8f5e --- /dev/null +++ b/xen/include/xen/kexec.h @@ -0,0 +1,45 @@ +#ifndef __XEN_KEXEC_H__ +#define __XEN_KEXEC_H__ + +#include +#include +#include + +extern int crashing_cpu; + +typedef struct xen_kexec_reserve { + unsigned long size; + unsigned long start; +} xen_kexec_reserve_t; + +extern xen_kexec_reserve_t kexec_crash_area; + +/* We have space for 4 images to support atomic update + * of images. This is important for CRASH images since + * a panic can happen at any time... + */ + +#define KEXEC_IMAGE_DEFAULT_BASE 0 +#define KEXEC_IMAGE_CRASH_BASE 2 +#define KEXEC_IMAGE_NR 4 + +int machine_kexec_load(int type, int slot, xen_kexec_image_t *image); +void machine_kexec_unload(int type, int slot, xen_kexec_image_t *image); +void machine_kexec_reserved(xen_kexec_reserve_t *reservation); +void machine_reboot_kexec(xen_kexec_image_t *image); +void machine_crash_kexec(void); +void machine_crash_save_cpu(void); +crash_xen_info_t *machine_crash_save_info(void); +void machine_crash_shutdown(void); + +#endif /* __XEN_KEXEC_H__ */ + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/xen/include/xen/mm.h b/xen/include/xen/mm.h index 4d05f6917f..2326b70442 100644 --- a/xen/include/xen/mm.h +++ b/xen/include/xen/mm.h @@ -40,6 +40,7 @@ struct page_info; paddr_t init_boot_allocator(paddr_t bitmap_start); void init_boot_pages(paddr_t ps, paddr_t pe); unsigned long alloc_boot_pages(unsigned long nr_pfns, unsigned long pfn_align); +unsigned long alloc_boot_pages_at(unsigned long nr_pfns, unsigned long pfn_at); void end_boot_allocator(void); /* Generic allocator. These functions are *not* interrupt-safe. */ @@ -89,6 +90,10 @@ int assign_pages( #define MAX_ORDER 20 /* 2^20 contiguous pages */ #endif +/* DMA heap parameters. */ +extern unsigned int dma_bitsize; +extern unsigned long max_dma_mfn; + /* Automatic page scrubbing for dead domains. */ extern struct list_head page_scrub_list; #define page_scrub_schedule_work() \ diff --git a/xen/include/xen/multiboot.h b/xen/include/xen/multiboot.h index 9473404775..86b59db1fe 100644 --- a/xen/include/xen/multiboot.h +++ b/xen/include/xen/multiboot.h @@ -18,16 +18,30 @@ #ifndef __MULTIBOOT_H__ #define __MULTIBOOT_H__ + +/* + * Multiboot header structure. + */ +#define MULTIBOOT_HEADER_MAGIC 0x1BADB002 +#define MULTIBOOT_HEADER_MODS_ALIGNED 0x00000001 +#define MULTIBOOT_HEADER_WANT_MEMORY 0x00000002 +#define MULTIBOOT_HEADER_HAS_VBE 0x00000004 +#define MULTIBOOT_HEADER_HAS_ADDR 0x00010000 + /* The magic number passed by a Multiboot-compliant boot loader. */ -#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002 +#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002 #define MBI_MEMLIMITS (1<<0) #define MBI_DRIVES (1<<1) #define MBI_CMDLINE (1<<2) #define MBI_MODULES (1<<3) +#define MBI_AOUT_SYMS (1<<4) +#define MBI_ELF_SYMS (1<<5) #define MBI_MEMMAP (1<<6) #define MBI_LOADERNAME (1<<9) +#ifndef __ASSEMBLY__ + /* The symbol table for a.out. */ typedef struct { u32 tabsize; @@ -47,16 +61,28 @@ typedef struct { /* The Multiboot information. */ typedef struct { u32 flags; + + /* Valid if flags sets MBI_MEMLIMITS */ u32 mem_lower; u32 mem_upper; + + /* Valid if flags sets MBI_DRIVES */ u32 boot_device; + + /* Valid if flags sets MBI_CMDLINE */ u32 cmdline; + + /* Valid if flags sets MBI_MODULES */ u32 mods_count; u32 mods_addr; + + /* Valid if flags sets ... */ union { - aout_symbol_table_t aout_sym; - elf_section_header_table_t elf_sec; + aout_symbol_table_t aout_sym; /* ... MBI_AOUT_SYMS */ + elf_section_header_table_t elf_sec; /* ... MBI_ELF_SYMS */ } u; + + /* Valid if flags sets MBI_MEMMAP */ u32 mmap_length; u32 mmap_addr; } multiboot_info_t; @@ -80,4 +106,7 @@ typedef struct { u32 type; } memory_map_t; + +#endif /* __ASSEMBLY__ */ + #endif /* __MULTIBOOT_H__ */ diff --git a/xen/include/xen/softirq.h b/xen/include/xen/softirq.h index d4e8edd7ad..a51aad6280 100644 --- a/xen/include/xen/softirq.h +++ b/xen/include/xen/softirq.h @@ -9,7 +9,12 @@ #define NMI_SOFTIRQ 4 #define PAGE_SCRUB_SOFTIRQ 5 #define TRACE_SOFTIRQ 6 -#define NR_SOFTIRQS 7 + +#define NR_COMMON_SOFTIRQS 7 + +#include + +#define NR_SOFTIRQS (NR_COMMON_SOFTIRQS + NR_ARCH_SOFTIRQS) #ifndef __ASSEMBLY__