1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
|
(* virt-v2v
* Copyright (C) 2009-2016 Red Hat Inc.
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*)
open Printf
open Common_gettext.Gettext
open Common_utils
open Regedit
open Types
open Utils
let virtio_win =
try Sys.getenv "VIRTIO_WIN"
with Not_found ->
try Sys.getenv "VIRTIO_WIN_DIR" (* old name for VIRTIO_WIN *)
with Not_found ->
Guestfs_config.datadir // "virtio-win"
let scsi_class_guid = "{4D36E97B-E325-11CE-BFC1-08002BE10318}"
let viostor_pciid = "VEN_1AF4&DEV_1001&SUBSYS_00021AF4&REV_00"
let vioscsi_pciid = "VEN_1AF4&DEV_1004&SUBSYS_00081AF4&REV_00"
let rec install_drivers g inspect systemroot root current_cs rcaps =
(* Copy the virtio drivers to the guest. *)
let driverdir = sprintf "%s/Drivers/VirtIO" systemroot in
g#mkdir_p driverdir;
if not (copy_drivers g inspect driverdir) then (
match rcaps with
| { rcaps_block_bus = Some Virtio_blk | Some Virtio_SCSI }
| { rcaps_net_bus = Some Virtio_net }
| { rcaps_video = Some QXL } ->
error (f_"there are no virtio drivers available for this version of Windows (%d.%d %s %s). virt-v2v looks for drivers in %s")
inspect.i_major_version inspect.i_minor_version inspect.i_arch
inspect.i_product_variant virtio_win
| { rcaps_block_bus = (Some IDE | None);
rcaps_net_bus = ((Some E1000 | Some RTL8139 | None) as net_type);
rcaps_video = (Some Cirrus | None) } ->
warning (f_"there are no virtio drivers available for this version of Windows (%d.%d %s %s). virt-v2v looks for drivers in %s\n\nThe guest will be configured to use slower emulated devices.")
inspect.i_major_version inspect.i_minor_version inspect.i_arch
inspect.i_product_variant virtio_win;
let net_type =
match net_type with
| Some model -> model
| None -> RTL8139 in
(IDE, net_type, Cirrus)
)
else (
(* Can we install the block driver? *)
let block : guestcaps_block_type =
let filenames = ["virtio_blk"; "vrtioblk"; "viostor"] in
let viostor_driver = try (
Some (
List.find (
fun driver_file ->
let source = driverdir // driver_file ^ ".sys" in
g#exists source
) filenames
)
) with Not_found -> None in
let has_vioscsi = g#exists (driverdir // "vioscsi.inf") in
match rcaps.rcaps_block_bus, viostor_driver, has_vioscsi with
| Some Virtio_blk, None, _ ->
error (f_"there is no virtio block device driver for this version of Windows (%d.%d %s). virt-v2v looks for this driver in %s\n\nThe guest will be configured to use a slower emulated device.")
inspect.i_major_version inspect.i_minor_version
inspect.i_arch virtio_win
| Some Virtio_SCSI, _, false ->
error (f_"there is no vioscsi (virtio SCSI) driver for this version of Windows (%d.%d %s). virt-v2v looks for this driver in %s\n\nThe guest will be configured to use a slower emulated device.")
inspect.i_major_version inspect.i_minor_version
inspect.i_arch virtio_win
| None, None, _ ->
warning (f_"there is no virtio block device driver for this version of Windows (%d.%d %s). virt-v2v looks for this driver in %s\n\nThe guest will be configured to use a slower emulated device.")
inspect.i_major_version inspect.i_minor_version
inspect.i_arch virtio_win;
IDE
| (Some Virtio_blk | None), Some driver_name, _ ->
(* Block driver needs tweaks to allow booting; the rest is set up by PnP
* manager *)
let source = driverdir // (driver_name ^ ".sys") in
let target = sprintf "%s/system32/drivers/%s.sys" systemroot driver_name in
let target = g#case_sensitive_path target in
g#cp source target;
add_guestor_to_registry g root current_cs driver_name
viostor_pciid;
Virtio_blk
| Some Virtio_SCSI, _, true ->
(* Block driver needs tweaks to allow booting; the rest is set up by PnP
* manager *)
let source = driverdir // "vioscsi.sys" in
let target = sprintf "%s/system32/drivers/vioscsi.sys" systemroot in
let target = g#case_sensitive_path target in
g#cp source target;
add_guestor_to_registry g root current_cs "vioscsi"
vioscsi_pciid;
Virtio_SCSI
| Some IDE, _, _ ->
IDE in
(* Can we install the virtio-net driver? *)
let net : guestcaps_net_type =
let filenames = ["virtio_net.inf"; "netkvm.inf"] in
let has_netkvm =
List.exists (
fun driver_file -> g#exists (driverdir // driver_file)
) filenames in
match rcaps.rcaps_net_bus, has_netkvm with
| Some Virtio_net, false ->
error (f_"there is no virtio network driver for this version of Windows (%d.%d %s). virt-v2v looks for this driver in %s")
inspect.i_major_version inspect.i_minor_version
inspect.i_arch virtio_win
| None, false ->
warning (f_"there is no virtio network driver for this version of Windows (%d.%d %s). virt-v2v looks for this driver in %s\n\nThe guest will be configured to use a slower emulated device.")
inspect.i_major_version inspect.i_minor_version
inspect.i_arch virtio_win;
RTL8139
| (Some Virtio_net | None), true ->
Virtio_net
| Some net_type, _ ->
net_type in
(* Can we install the QXL driver? *)
let video : guestcaps_video_type =
let has_qxl = g#exists (driverdir // "qxl.inf") in
match rcaps.rcaps_video, has_qxl with
| Some QXL, false ->
error (f_"there is no QXL driver for this version of Windows (%d.%d %s). virt-v2v looks for this driver in %s")
inspect.i_major_version inspect.i_minor_version
inspect.i_arch virtio_win
| None, false ->
warning (f_"there is no QXL driver for this version of Windows (%d.%d %s). virt-v2v looks for this driver in %s\n\nThe guest will be configured to use a basic VGA display driver.")
inspect.i_major_version inspect.i_minor_version
inspect.i_arch virtio_win;
Cirrus
| (Some QXL | None), true ->
QXL
| Some Cirrus, _ ->
Cirrus in
(block, net, video)
)
and add_guestor_to_registry g root current_cs drv_name drv_pciid =
let ddb_node = g#hivex_node_get_child root "DriverDatabase" in
let regedits =
if ddb_node = 0L then
cdb_regedits current_cs drv_name drv_pciid
else
ddb_regedits current_cs drv_name drv_pciid in
let drv_sys_path = sprintf "system32\\drivers\\%s.sys" drv_name in
let common_regedits = [
[ current_cs; "Services"; drv_name ],
[ "Type", REG_DWORD 0x1_l;
"Start", REG_DWORD 0x0_l;
"Group", REG_SZ "SCSI miniport";
"ErrorControl", REG_DWORD 0x1_l;
"ImagePath", REG_EXPAND_SZ drv_sys_path ];
] in
reg_import g root (regedits @ common_regedits)
and cdb_regedits current_cs drv_name drv_pciid =
(* See http://rwmj.wordpress.com/2010/04/30/tip-install-a-device-driver-in-a-windows-vm/
* NB: All these edits are in the HKLM\SYSTEM hive. No other
* hive may be modified here.
*)
[
[ current_cs; "Control"; "CriticalDeviceDatabase";
"PCI#" ^ drv_pciid ],
[ "Service", REG_SZ drv_name;
"ClassGUID", REG_SZ scsi_class_guid ];
]
and ddb_regedits current_cs drv_name drv_pciid =
(* Windows >= 8 doesn't use the CriticalDeviceDatabase. Instead
* one must add keys into the DriverDatabase.
*)
let drv_inf = "guestor.inf" in
let drv_inf_label = drv_inf ^ "_tmp" in
let drv_config = "guestor_conf" in
[
[ "DriverDatabase"; "DriverInfFiles"; drv_inf ],
[ "", REG_MULTI_SZ [ drv_inf_label ];
"Active", REG_SZ drv_inf_label;
"Configurations", REG_MULTI_SZ [ drv_config ] ];
[ "DriverDatabase"; "DeviceIds"; "PCI"; drv_pciid ],
[ drv_inf, REG_BINARY "\x01\xff\x00\x00" ];
[ "DriverDatabase"; "DriverPackages"; drv_inf_label;
"Configurations"; drv_config ],
[ "ConfigFlags", REG_DWORD 0_l;
"Service", REG_SZ drv_name ];
[ "DriverDatabase"; "DriverPackages"; drv_inf_label;
"Descriptors"; "PCI"; drv_pciid ],
[ "Configuration", REG_SZ drv_config ];
]
(* Copy the matching drivers to the driverdir; return true if any have
* been copied.
*)
and copy_drivers g inspect driverdir =
let ret = ref false in
if is_directory virtio_win then (
let cmd = sprintf "cd %s && find -L -type f" (quote virtio_win) in
let paths = external_command cmd in
List.iter (
fun path ->
if virtio_iso_path_matches_guest_os path inspect then (
let source = virtio_win // path in
let target = driverdir //
String.lowercase_ascii (Filename.basename path) in
debug "copying virtio driver bits: 'host:%s' -> '%s'"
source target;
g#write target (read_whole_file source);
ret := true
)
) paths
)
else if is_regular_file virtio_win then (
try
let g2 = open_guestfs ~identifier:"virtio_win" () in
g2#add_drive_opts virtio_win ~readonly:true;
g2#launch ();
let vio_root = "/" in
g2#mount_ro "/dev/sda" vio_root;
let paths = g2#find vio_root in
Array.iter (
fun path ->
let source = vio_root // path in
if g2#is_file source ~followsymlinks:false &&
virtio_iso_path_matches_guest_os path inspect then (
let target = driverdir //
String.lowercase_ascii (Filename.basename path) in
debug "copying virtio driver bits: '%s:%s' -> '%s'"
virtio_win path target;
g#write target (g2#read_file source);
ret := true
)
) paths;
g2#close()
with Guestfs.Error msg ->
error (f_"%s: cannot open virtio-win ISO file: %s") virtio_win msg
);
!ret
(* Given a path of a file relative to the root of the directory tree
* with virtio-win drivers, figure out if it's suitable for the
* specific Windows flavor of the current guest.
*)
and virtio_iso_path_matches_guest_os path inspect =
let { i_major_version = os_major; i_minor_version = os_minor;
i_arch = arch; i_product_variant = os_variant } = inspect in
try
(* Lowercased path, since the ISO may contain upper or lowercase path
* elements.
*)
let lc_path = String.lowercase_ascii path in
(* Using the full path, work out what version of Windows
* this driver is for. Paths can be things like:
* "NetKVM/2k12R2/amd64/netkvm.sys" or
* "./drivers/amd64/Win2012R2/netkvm.sys".
* Note we check lowercase paths.
*)
let pathelem elem = String.find lc_path ("/" ^ elem ^ "/") >= 0 in
let p_arch =
if pathelem "x86" || pathelem "i386" then "i386"
else if pathelem "amd64" then "x86_64"
else raise Not_found in
let is_client os_variant = os_variant = "Client"
and not_client os_variant = os_variant <> "Client"
and any_variant os_variant = true in
let p_os_major, p_os_minor, match_os_variant =
if pathelem "xp" || pathelem "winxp" then
(5, 1, any_variant)
else if pathelem "2k3" || pathelem "win2003" then
(5, 2, any_variant)
else if pathelem "vista" then
(6, 0, is_client)
else if pathelem "2k8" || pathelem "win2008" then
(6, 0, not_client)
else if pathelem "w7" || pathelem "win7" then
(6, 1, is_client)
else if pathelem "2k8r2" || pathelem "win2008r2" then
(6, 1, not_client)
else if pathelem "w8" || pathelem "win8" then
(6, 2, is_client)
else if pathelem "2k12" || pathelem "win2012" then
(6, 2, not_client)
else if pathelem "w8.1" || pathelem "win8.1" then
(6, 3, is_client)
else if pathelem "2k12r2" || pathelem "win2012r2" then
(6, 3, not_client)
else if pathelem "w10" || pathelem "win10" then
(10, 0, is_client)
else if pathelem "2k16" || pathelem "win2016" then
(10, 0, not_client)
else
raise Not_found in
arch = p_arch && os_major = p_os_major && os_minor = p_os_minor &&
match_os_variant os_variant
with Not_found -> false
(* The following function is only exported for unit tests. *)
module UNIT_TESTS = struct
let virtio_iso_path_matches_guest_os = virtio_iso_path_matches_guest_os
end
|