1 /*
2  * Alternate p2m HVM
3  * Copyright (c) 2014, Intel Corporation.
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms and conditions of the GNU General Public License,
7  * version 2, as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
12  * more details.
13  *
14  * You should have received a copy of the GNU General Public License along with
15  * this program; If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <asm/hvm/support.h>
19 #include <asm/hvm/hvm.h>
20 #include <asm/p2m.h>
21 #include <asm/altp2m.h>
22 
23 void
altp2m_vcpu_initialise(struct vcpu * v)24 altp2m_vcpu_initialise(struct vcpu *v)
25 {
26     if ( v != current )
27         vcpu_pause(v);
28 
29     vcpu_altp2m(v).p2midx = 0;
30     atomic_inc(&p2m_get_altp2m(v)->active_vcpus);
31 
32     altp2m_vcpu_update_p2m(v);
33 
34     if ( v != current )
35         vcpu_unpause(v);
36 }
37 
38 void
altp2m_vcpu_destroy(struct vcpu * v)39 altp2m_vcpu_destroy(struct vcpu *v)
40 {
41     struct p2m_domain *p2m;
42 
43     if ( v != current )
44         vcpu_pause(v);
45 
46     if ( (p2m = p2m_get_altp2m(v)) )
47         atomic_dec(&p2m->active_vcpus);
48 
49     altp2m_vcpu_disable_ve(v);
50 
51     vcpu_altp2m(v).p2midx = INVALID_ALTP2M;
52     altp2m_vcpu_update_p2m(v);
53 
54     if ( v != current )
55         vcpu_unpause(v);
56 }
57 
altp2m_vcpu_enable_ve(struct vcpu * v,gfn_t gfn)58 int altp2m_vcpu_enable_ve(struct vcpu *v, gfn_t gfn)
59 {
60     struct domain *d = v->domain;
61     struct altp2mvcpu *a = &vcpu_altp2m(v);
62     p2m_type_t p2mt;
63     struct page_info *pg;
64     int rc;
65 
66     /* Early exit path if #VE is already configured. */
67     if ( a->veinfo_pg )
68         return -EEXIST;
69 
70     rc = check_get_page_from_gfn(d, gfn, false, &p2mt, &pg);
71     if ( rc )
72         return rc;
73 
74     /*
75      * Looking for a plain piece of guest writeable RAM with isn't a magic
76      * frame such as a grant/ioreq/shared_info/etc mapping.  We (ab)use the
77      * pageable() predicate for this, due to it having the same properties
78      * that we want.
79      */
80     if ( !p2m_is_pageable(p2mt) || is_special_page(pg) )
81     {
82         rc = -EINVAL;
83         goto err;
84     }
85 
86     /*
87      * Update veinfo_pg, making sure to be safe with concurrent hypercalls.
88      * The first caller to make veinfo_pg become non-NULL will program its MFN
89      * into the VMCS, so must not be clobbered.  Callers which lose the race
90      * back off with -EEXIST.
91      */
92     if ( cmpxchg(&a->veinfo_pg, NULL, pg) != NULL )
93     {
94         rc = -EEXIST;
95         goto err;
96     }
97 
98     altp2m_vcpu_update_vmfunc_ve(v);
99 
100     return 0;
101 
102  err:
103     put_page(pg);
104 
105     return rc;
106 }
107 
altp2m_vcpu_disable_ve(struct vcpu * v)108 void altp2m_vcpu_disable_ve(struct vcpu *v)
109 {
110     struct altp2mvcpu *a = &vcpu_altp2m(v);
111     struct page_info *pg;
112 
113     /*
114      * Update veinfo_pg, making sure to be safe with concurrent hypercalls.
115      * The winner of this race is responsible to update the VMCS to no longer
116      * point at the page, then drop the associated ref.
117      */
118     if ( (pg = xchg(&a->veinfo_pg, NULL)) )
119     {
120         altp2m_vcpu_update_vmfunc_ve(v);
121 
122         put_page(pg);
123     }
124 }
125 
126 /*
127  * Local variables:
128  * mode: C
129  * c-file-style: "BSD"
130  * c-basic-offset: 4
131  * tab-width: 4
132  * indent-tabs-mode: nil
133  * End:
134  */
135