|
@@ -38,13 +38,6 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
38
|
38
|
#include <ipxe/hyperv.h>
|
39
|
39
|
#include <ipxe/vmbus.h>
|
40
|
40
|
|
41
|
|
-/** Chosen VMBus protocol version
|
42
|
|
- *
|
43
|
|
- * This is a policy decision. We use the oldest common version in
|
44
|
|
- * order to avoid including any version-specific code.
|
45
|
|
- */
|
46
|
|
-#define VMBUS_VERSION VMBUS_VERSION_WS2008
|
47
|
|
-
|
48
|
41
|
/** VMBus initial GPADL ID
|
49
|
42
|
*
|
50
|
43
|
* This is an opaque value with no meaning. The Linux kernel uses
|
|
@@ -122,9 +115,11 @@ static int vmbus_wait_for_message ( struct hv_hypervisor *hv ) {
|
122
|
115
|
* Initiate contact
|
123
|
116
|
*
|
124
|
117
|
* @v hv Hyper-V hypervisor
|
|
118
|
+ * @v raw VMBus protocol (raw) version
|
125
|
119
|
* @ret rc Return status code
|
126
|
120
|
*/
|
127
|
|
-static int vmbus_initiate_contact ( struct hv_hypervisor *hv ) {
|
|
121
|
+static int vmbus_initiate_contact ( struct hv_hypervisor *hv,
|
|
122
|
+ unsigned int raw ) {
|
128
|
123
|
struct vmbus *vmbus = hv->vmbus;
|
129
|
124
|
const struct vmbus_version_response *version = &vmbus->message->version;
|
130
|
125
|
struct vmbus_initiate_contact initiate;
|
|
@@ -133,7 +128,7 @@ static int vmbus_initiate_contact ( struct hv_hypervisor *hv ) {
|
133
|
128
|
/* Construct message */
|
134
|
129
|
memset ( &initiate, 0, sizeof ( initiate ) );
|
135
|
130
|
initiate.header.type = cpu_to_le32 ( VMBUS_INITIATE_CONTACT );
|
136
|
|
- initiate.version.raw = cpu_to_le32 ( VMBUS_VERSION );
|
|
131
|
+ initiate.version.raw = cpu_to_le32 ( raw );
|
137
|
132
|
initiate.intr = virt_to_phys ( vmbus->intr );
|
138
|
133
|
initiate.monitor_in = virt_to_phys ( vmbus->monitor_in );
|
139
|
134
|
initiate.monitor_out = virt_to_phys ( vmbus->monitor_out );
|
|
@@ -158,7 +153,7 @@ static int vmbus_initiate_contact ( struct hv_hypervisor *hv ) {
|
158
|
153
|
vmbus );
|
159
|
154
|
return -ENOTSUP;
|
160
|
155
|
}
|
161
|
|
- if ( version->version.raw != cpu_to_le32 ( VMBUS_VERSION ) ) {
|
|
156
|
+ if ( version->version.raw != cpu_to_le32 ( raw ) ) {
|
162
|
157
|
DBGC ( vmbus, "VMBUS %p unexpected version %d.%d\n",
|
163
|
158
|
vmbus, le16_to_cpu ( version->version.major ),
|
164
|
159
|
le16_to_cpu ( version->version.minor ) );
|
|
@@ -178,8 +173,69 @@ static int vmbus_initiate_contact ( struct hv_hypervisor *hv ) {
|
178
|
173
|
* @ret rc Return status code
|
179
|
174
|
*/
|
180
|
175
|
static int vmbus_unload ( struct hv_hypervisor *hv ) {
|
|
176
|
+ struct vmbus *vmbus = hv->vmbus;
|
|
177
|
+ const struct vmbus_message_header *header = &vmbus->message->header;
|
|
178
|
+ int rc;
|
|
179
|
+
|
|
180
|
+ /* Post message */
|
|
181
|
+ if ( ( rc = vmbus_post_empty_message ( hv, VMBUS_UNLOAD ) ) != 0 )
|
|
182
|
+ return rc;
|
|
183
|
+
|
|
184
|
+ /* Wait for response */
|
|
185
|
+ if ( ( rc = vmbus_wait_for_message ( hv ) ) != 0 )
|
|
186
|
+ return rc;
|
|
187
|
+
|
|
188
|
+ /* Check response */
|
|
189
|
+ if ( header->type != cpu_to_le32 ( VMBUS_UNLOAD_RESPONSE ) ) {
|
|
190
|
+ DBGC ( vmbus, "VMBUS %p unexpected unload response type %d\n",
|
|
191
|
+ vmbus, le32_to_cpu ( header->type ) );
|
|
192
|
+ return -EPROTO;
|
|
193
|
+ }
|
|
194
|
+
|
|
195
|
+ return 0;
|
|
196
|
+}
|
|
197
|
+
|
|
198
|
+/**
|
|
199
|
+ * Negotiate protocol version
|
|
200
|
+ *
|
|
201
|
+ * @v hv Hyper-V hypervisor
|
|
202
|
+ * @ret rc Return status code
|
|
203
|
+ */
|
|
204
|
+static int vmbus_negotiate_version ( struct hv_hypervisor *hv ) {
|
|
205
|
+ int rc;
|
181
|
206
|
|
182
|
|
- return vmbus_post_empty_message ( hv, VMBUS_UNLOAD );
|
|
207
|
+ /* We require the ability to disconnect from and reconnect to
|
|
208
|
+ * VMBus; if we don't have this then there is no (viable) way
|
|
209
|
+ * for a loaded operating system to continue to use any VMBus
|
|
210
|
+ * devices. (There is also a small but non-zero risk that the
|
|
211
|
+ * host will continue to write to our interrupt and monitor
|
|
212
|
+ * pages, since the VMBUS_UNLOAD message in earlier versions
|
|
213
|
+ * is essentially a no-op.)
|
|
214
|
+ *
|
|
215
|
+ * This requires us to ensure that the host supports protocol
|
|
216
|
+ * version 3.0 (VMBUS_VERSION_WIN8_1). However, we can't
|
|
217
|
+ * actually _use_ protocol version 3.0, since doing so causes
|
|
218
|
+ * an iSCSI-booted Windows Server 2012 R2 VM to crash due to a
|
|
219
|
+ * NULL pointer dereference in vmbus.sys.
|
|
220
|
+ *
|
|
221
|
+ * To work around this problem, we first ensure that we can
|
|
222
|
+ * connect using protocol v3.0, then disconnect and reconnect
|
|
223
|
+ * using the oldest known protocol.
|
|
224
|
+ */
|
|
225
|
+
|
|
226
|
+ /* Initiate contact to check for required protocol support */
|
|
227
|
+ if ( ( rc = vmbus_initiate_contact ( hv, VMBUS_VERSION_WIN8_1 ) ) != 0 )
|
|
228
|
+ return rc;
|
|
229
|
+
|
|
230
|
+ /* Terminate contact */
|
|
231
|
+ if ( ( rc = vmbus_unload ( hv ) ) != 0 )
|
|
232
|
+ return rc;
|
|
233
|
+
|
|
234
|
+ /* Reinitiate contact using the oldest known protocol version */
|
|
235
|
+ if ( ( rc = vmbus_initiate_contact ( hv, VMBUS_VERSION_WS2008 ) ) != 0 )
|
|
236
|
+ return rc;
|
|
237
|
+
|
|
238
|
+ return 0;
|
183
|
239
|
}
|
184
|
240
|
|
185
|
241
|
/**
|
|
@@ -1232,9 +1288,9 @@ int vmbus_probe ( struct hv_hypervisor *hv, struct device *parent ) {
|
1232
|
1288
|
/* Enable message interrupt */
|
1233
|
1289
|
hv_enable_sint ( hv, VMBUS_MESSAGE_SINT );
|
1234
|
1290
|
|
1235
|
|
- /* Initiate contact */
|
1236
|
|
- if ( ( rc = vmbus_initiate_contact ( hv ) ) != 0 )
|
1237
|
|
- goto err_initiate_contact;
|
|
1291
|
+ /* Negotiate protocol version */
|
|
1292
|
+ if ( ( rc = vmbus_negotiate_version ( hv ) ) != 0 )
|
|
1293
|
+ goto err_negotiate_version;
|
1238
|
1294
|
|
1239
|
1295
|
/* Enumerate channels */
|
1240
|
1296
|
if ( ( rc = vmbus_probe_channels ( hv, parent ) ) != 0 )
|
|
@@ -1245,7 +1301,7 @@ int vmbus_probe ( struct hv_hypervisor *hv, struct device *parent ) {
|
1245
|
1301
|
vmbus_remove_channels ( hv, parent );
|
1246
|
1302
|
err_probe_channels:
|
1247
|
1303
|
vmbus_unload ( hv );
|
1248
|
|
- err_initiate_contact:
|
|
1304
|
+ err_negotiate_version:
|
1249
|
1305
|
hv_disable_sint ( hv, VMBUS_MESSAGE_SINT );
|
1250
|
1306
|
hv_free_pages ( hv, vmbus->intr, vmbus->monitor_in, vmbus->monitor_out,
|
1251
|
1307
|
NULL );
|