github.com/kata-containers/runtime@v0.0.0-20210505125100-04f29832a923/virtcontainers/acrn_arch_base.go (about) 1 // Copyright (c) 2019 Intel Corporation 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 // 5 6 package virtcontainers 7 8 import ( 9 "bytes" 10 "context" 11 "fmt" 12 "os" 13 "os/exec" 14 "strings" 15 16 "github.com/kata-containers/runtime/virtcontainers/device/config" 17 "github.com/kata-containers/runtime/virtcontainers/types" 18 "github.com/sirupsen/logrus" 19 ) 20 21 type acrnArch interface { 22 23 // acrnPath returns the path to the acrn binary 24 acrnPath() (string, error) 25 26 // acrnctlPath returns the path to the acrnctl binary 27 acrnctlPath() (string, error) 28 29 // kernelParameters returns the kernel parameters 30 // if debug is true then kernel debug parameters are included 31 kernelParameters(debug bool) []Param 32 33 //capabilities returns the capabilities supported by acrn 34 capabilities() types.Capabilities 35 36 // memoryTopology returns the memory topology using the given amount of memoryMb and hostMemoryMb 37 memoryTopology(memMb uint64) Memory 38 39 // appendConsole appends a console to devices 40 appendConsole(devices []Device, path string) []Device 41 42 // appendImage appends an image to devices 43 appendImage(devices []Device, path string) ([]Device, error) 44 45 // appendBridges appends bridges to devices 46 appendBridges(devices []Device) []Device 47 48 // appendLPC appends LPC to devices 49 // UART device emulated by the acrn-dm is connected to the system by the LPC bus 50 appendLPC(devices []Device) []Device 51 52 // appendSocket appends a socket to devices 53 appendSocket(devices []Device, socket types.Socket) []Device 54 55 // appendNetwork appends a endpoint device to devices 56 appendNetwork(devices []Device, endpoint Endpoint) []Device 57 58 // appendBlockDevice appends a block drive to devices 59 appendBlockDevice(devices []Device, drive config.BlockDrive) []Device 60 61 // handleImagePath handles the Hypervisor Config image path 62 handleImagePath(config HypervisorConfig) 63 } 64 65 type acrnArchBase struct { 66 path string 67 ctlpath string 68 kernelParamsNonDebug []Param 69 kernelParamsDebug []Param 70 kernelParams []Param 71 } 72 73 const acrnPath = "/usr/bin/acrn-dm" 74 const acrnctlPath = "/usr/bin/acrnctl" 75 76 // acrn GVT-g slot is harded code to 2 as there is 77 // no simple way to pass arguments of PCI slots from 78 // device model (acrn-dm) to ACRNGT module. 79 const acrnGVTgReservedSlot = 2 80 81 const acrnLPCDev = "lpc" 82 const acrnHostBridge = "hostbridge" 83 84 var baselogger *logrus.Entry 85 86 // AcrnBlkDevPoolSz defines the number of dummy virtio-blk 87 // device that will be created for hot-plugging container 88 // rootfs. Since acrn doesn't support hot-plug, dummy virtio-blk 89 // devices are added and later replaced with container-rootfs. 90 var AcrnBlkDevPoolSz = 8 91 92 // AcrnBlkdDevSlot array provides translation between 93 // the vitio-blk device index and slot it is currently 94 // attached. 95 // Allocating extra 1 to accommodate for VM rootfs 96 // which is at driveIndex 0 97 var AcrnBlkdDevSlot = make([]int, AcrnBlkDevPoolSz+1) 98 99 // acrnKernelParamsNonDebug is a list of the default kernel 100 // parameters that will be used in standard (non-debug) mode. 101 var acrnKernelParamsNonDebug = []Param{ 102 {"quiet", ""}, 103 } 104 105 // acrnKernelParamsSystemdNonDebug is a list of the default systemd related 106 // kernel parameters that will be used in standard (non-debug) mode. 107 var acrnKernelParamsSystemdNonDebug = []Param{ 108 {"systemd.show_status", "false"}, 109 } 110 111 // acrnKernelParamsDebug is a list of the default kernel 112 // parameters that will be used in debug mode (as much boot output as 113 // possible). 114 var acrnKernelParamsDebug = []Param{ 115 {"debug", ""}, 116 } 117 118 // acrnKernelParamsSystemdDebug is a list of the default systemd related kernel 119 // parameters that will be used in debug mode (as much boot output as 120 // possible). 121 var acrnKernelParamsSystemdDebug = []Param{ 122 {"systemd.show_status", "true"}, 123 {"systemd.log_level", "debug"}, 124 {"systemd.log_target", "kmsg"}, 125 {"printk.devkmsg", "on"}, 126 } 127 128 var acrnKernelRootParams = []Param{ 129 {"root", "/dev/vda1 rw rootwait"}, 130 } 131 132 var acrnKernelParams = []Param{ 133 {"tsc", "reliable"}, 134 {"no_timer_check", ""}, 135 {"nohpet", ""}, 136 {"console", "tty0"}, 137 {"console", "ttyS0"}, 138 {"console", "hvc0"}, 139 {"log_buf_len", "16M"}, 140 {"consoleblank", "0"}, 141 {"iommu", "off"}, 142 {"i915.avail_planes_per_pipe", "0x070F00"}, 143 {"i915.enable_hangcheck", "0"}, 144 {"i915.nuclear_pageflip", "1"}, 145 {"i915.enable_guc_loading", "0"}, 146 {"i915.enable_guc_submission", "0"}, 147 {"i915.enable_guc", "0"}, 148 } 149 150 // Device is the acrn device interface. 151 type Device interface { 152 Valid() bool 153 AcrnParams(slot int, config *Config) []string 154 } 155 156 // ConsoleDeviceBackend is the character device backend for acrn 157 type ConsoleDeviceBackend string 158 159 const ( 160 161 // Socket creates a 2 way stream socket (TCP or Unix). 162 Socket ConsoleDeviceBackend = "socket" 163 164 // Stdio sends traffic from the guest to acrn's standard output. 165 Stdio ConsoleDeviceBackend = "console" 166 167 // File backend only supports console output to a file (no input). 168 File ConsoleDeviceBackend = "file" 169 170 // TTY is an alias for Serial. 171 TTY ConsoleDeviceBackend = "tty" 172 173 // PTY creates a new pseudo-terminal on the host and connect to it. 174 PTY ConsoleDeviceBackend = "pty" 175 ) 176 177 // BEPortType marks the port as console port or virtio-serial port 178 type BEPortType int 179 180 const ( 181 // SerialBE marks the port as serial port 182 SerialBE BEPortType = iota 183 184 //ConsoleBE marks the port as console port (append @) 185 ConsoleBE 186 ) 187 188 // ConsoleDevice represents a acrn console device. 189 type ConsoleDevice struct { 190 // Name of the socket 191 Name string 192 193 //Backend device used for virtio-console 194 Backend ConsoleDeviceBackend 195 196 // PortType marks the port as serial or console port (@) 197 PortType BEPortType 198 199 //Path to virtio-console backend (can be omitted for pty, tty, stdio) 200 Path string 201 } 202 203 // NetDeviceType is a acrn networking device type. 204 type NetDeviceType string 205 206 const ( 207 // TAP is a TAP networking device type. 208 TAP NetDeviceType = "tap" 209 210 // MACVTAP is a macvtap networking device type. 211 MACVTAP NetDeviceType = "macvtap" 212 ) 213 214 // NetDevice represents a guest networking device 215 type NetDevice struct { 216 // Type is the netdev type (e.g. tap). 217 Type NetDeviceType 218 219 // IfName is the interface name 220 IFName string 221 222 //MACAddress is the networking device interface MAC address 223 MACAddress string 224 } 225 226 // BlockDevice represents a acrn block device. 227 type BlockDevice struct { 228 229 // mem path to block device 230 FilePath string 231 232 //BlkIndex - Blk index corresponding to slot 233 Index int 234 } 235 236 // BridgeDevice represents a acrn bridge device like pci-bridge, pxb, etc. 237 type BridgeDevice struct { 238 239 // Function is PCI function. Func can be from 0 to 7 240 Function int 241 242 // Emul is a string describing the type of PCI device e.g. virtio-net 243 Emul string 244 245 // Config is an optional string, depending on the device, that can be 246 // used for configuration 247 Config string 248 } 249 250 // LPCDevice represents a acrn LPC device 251 type LPCDevice struct { 252 253 // Function is PCI function. Func can be from 0 to 7 254 Function int 255 256 // Emul is a string describing the type of PCI device e.g. virtio-net 257 Emul string 258 } 259 260 // Memory is the guest memory configuration structure. 261 type Memory struct { 262 // Size is the amount of memory made available to the guest. 263 // It should be suffixed with M or G for sizes in megabytes or 264 // gigabytes respectively. 265 Size string 266 } 267 268 // Kernel is the guest kernel configuration structure. 269 type Kernel struct { 270 // Path is the guest kernel path on the host filesystem. 271 Path string 272 273 // InitrdPath is the guest initrd path on the host filesystem. 274 ImagePath string 275 276 // Params is the kernel parameters string. 277 Params string 278 } 279 280 // Config is the acrn configuration structure. 281 // It allows for passing custom settings and parameters to the acrn-dm API. 282 type Config struct { 283 284 // Path is the acrn binary path. 285 Path string 286 287 // Path is the acrn binary path. 288 CtlPath string 289 290 // Name is the acrn guest name 291 Name string 292 293 // UUID is the acrn process UUID. 294 UUID string 295 296 // Devices is a list of devices for acrn to create and drive. 297 Devices []Device 298 299 // Kernel is the guest kernel configuration. 300 Kernel Kernel 301 302 // Memory is the guest memory configuration. 303 Memory Memory 304 305 acrnParams []string 306 307 // ACPI virtualization support 308 ACPIVirt bool 309 } 310 311 // MaxAcrnVCPUs returns the maximum number of vCPUs supported 312 func MaxAcrnVCPUs() uint32 { 313 return uint32(8) 314 } 315 316 func newAcrnArch(config HypervisorConfig) acrnArch { 317 a := &acrnArchBase{ 318 path: acrnPath, 319 ctlpath: acrnctlPath, 320 kernelParamsNonDebug: acrnKernelParamsNonDebug, 321 kernelParamsDebug: acrnKernelParamsDebug, 322 kernelParams: acrnKernelParams, 323 } 324 325 a.handleImagePath(config) 326 return a 327 } 328 329 func (a *acrnArchBase) acrnPath() (string, error) { 330 p := a.path 331 return p, nil 332 } 333 334 func (a *acrnArchBase) acrnctlPath() (string, error) { 335 ctlpath := a.ctlpath 336 return ctlpath, nil 337 } 338 339 func (a *acrnArchBase) kernelParameters(debug bool) []Param { 340 params := a.kernelParams 341 342 if debug { 343 params = append(params, a.kernelParamsDebug...) 344 } else { 345 params = append(params, a.kernelParamsNonDebug...) 346 } 347 348 return params 349 } 350 351 func (a *acrnArchBase) memoryTopology(memoryMb uint64) Memory { 352 mem := fmt.Sprintf("%dM", memoryMb) 353 memory := Memory{ 354 Size: mem, 355 } 356 357 return memory 358 } 359 360 func (a *acrnArchBase) capabilities() types.Capabilities { 361 var caps types.Capabilities 362 363 caps.SetBlockDeviceSupport() 364 caps.SetBlockDeviceHotplugSupport() 365 366 return caps 367 } 368 369 // Valid returns true if the CharDevice structure is valid and complete. 370 func (cdev ConsoleDevice) Valid() bool { 371 if cdev.Backend != "tty" && cdev.Backend != "pty" && 372 cdev.Backend != "console" && cdev.Backend != "socket" && 373 cdev.Backend != "file" { 374 return false 375 } else if cdev.PortType != ConsoleBE && cdev.PortType != SerialBE { 376 return false 377 } else if cdev.Path == "" { 378 return false 379 } else { 380 return true 381 } 382 } 383 384 // AcrnParams returns the acrn parameters built out of this console device. 385 func (cdev ConsoleDevice) AcrnParams(slot int, config *Config) []string { 386 var acrnParams []string 387 var deviceParams []string 388 389 acrnParams = append(acrnParams, "-s") 390 deviceParams = append(deviceParams, fmt.Sprintf("%d,virtio-console,", slot)) 391 392 if cdev.PortType == ConsoleBE { 393 deviceParams = append(deviceParams, "@") 394 } 395 396 switch cdev.Backend { 397 case "pty": 398 deviceParams = append(deviceParams, "pty:pty_port") 399 case "tty": 400 deviceParams = append(deviceParams, fmt.Sprintf("tty:tty_port=%s", cdev.Path)) 401 case "socket": 402 deviceParams = append(deviceParams, fmt.Sprintf("socket:%s=%s", cdev.Name, cdev.Path)) 403 case "file": 404 deviceParams = append(deviceParams, fmt.Sprintf("file:file_port=%s", cdev.Path)) 405 case "stdio": 406 deviceParams = append(deviceParams, "stdio:stdio_port") 407 default: 408 // do nothing. Error should be already caught 409 } 410 411 acrnParams = append(acrnParams, strings.Join(deviceParams, "")) 412 return acrnParams 413 } 414 415 // AcrnNetdevParam converts to the acrn type to string 416 func (netdev NetDevice) AcrnNetdevParam() []string { 417 var deviceParams []string 418 419 switch netdev.Type { 420 case TAP: 421 deviceParams = append(deviceParams, netdev.IFName) 422 deviceParams = append(deviceParams, fmt.Sprintf(",mac=%s", netdev.MACAddress)) 423 case MACVTAP: 424 deviceParams = append(deviceParams, netdev.IFName) 425 deviceParams = append(deviceParams, fmt.Sprintf(",mac=%s", netdev.MACAddress)) 426 default: 427 deviceParams = append(deviceParams, netdev.IFName) 428 429 } 430 431 return deviceParams 432 } 433 434 // Valid returns true if the NetDevice structure is valid and complete. 435 func (netdev NetDevice) Valid() bool { 436 if netdev.IFName == "" { 437 return false 438 } else if netdev.MACAddress == "" { 439 return false 440 } else if netdev.Type != TAP && netdev.Type != MACVTAP { 441 return false 442 } else { 443 return true 444 } 445 } 446 447 // AcrnParams returns the acrn parameters built out of this network device. 448 func (netdev NetDevice) AcrnParams(slot int, config *Config) []string { 449 var acrnParams []string 450 451 acrnParams = append(acrnParams, "-s") 452 acrnParams = append(acrnParams, fmt.Sprintf("%d,virtio-net,%s", slot, strings.Join(netdev.AcrnNetdevParam(), ""))) 453 454 return acrnParams 455 } 456 457 // Valid returns true if the BlockDevice structure is valid and complete. 458 func (blkdev BlockDevice) Valid() bool { 459 return blkdev.FilePath != "" 460 } 461 462 // AcrnParams returns the acrn parameters built out of this block device. 463 func (blkdev BlockDevice) AcrnParams(slot int, config *Config) []string { 464 var acrnParams []string 465 466 device := "virtio-blk" 467 acrnParams = append(acrnParams, "-s") 468 acrnParams = append(acrnParams, fmt.Sprintf("%d,%s,%s", 469 slot, device, blkdev.FilePath)) 470 471 // Update the global array (BlkIndex<->slot) 472 // Used to identify slots for the hot-plugged virtio-blk devices 473 if blkdev.Index <= AcrnBlkDevPoolSz { 474 AcrnBlkdDevSlot[blkdev.Index] = slot 475 } else { 476 baselogger.WithFields(logrus.Fields{ 477 "device": device, 478 "index": blkdev.Index, 479 }).Info("Invalid index device") 480 } 481 482 return acrnParams 483 } 484 485 // Valid returns true if the BridgeDevice structure is valid and complete. 486 func (bridgeDev BridgeDevice) Valid() bool { 487 if bridgeDev.Function != 0 || bridgeDev.Emul != acrnHostBridge { 488 return false 489 } 490 return true 491 } 492 493 // AcrnParams returns the acrn parameters built out of this bridge device. 494 func (bridgeDev BridgeDevice) AcrnParams(slot int, config *Config) []string { 495 var acrnParams []string 496 497 acrnParams = append(acrnParams, "-s") 498 acrnParams = append(acrnParams, fmt.Sprintf("%d:%d,%s", slot, 499 bridgeDev.Function, bridgeDev.Emul)) 500 501 return acrnParams 502 } 503 504 // Valid returns true if the BridgeDevice structure is valid and complete. 505 func (lpcDev LPCDevice) Valid() bool { 506 return lpcDev.Emul == acrnLPCDev 507 } 508 509 // AcrnParams returns the acrn parameters built out of this bridge device. 510 func (lpcDev LPCDevice) AcrnParams(slot int, config *Config) []string { 511 var acrnParams []string 512 var deviceParams []string 513 514 acrnParams = append(acrnParams, "-s") 515 acrnParams = append(acrnParams, fmt.Sprintf("%d:%d,%s", slot, 516 lpcDev.Function, lpcDev.Emul)) 517 518 //define UART port 519 deviceParams = append(deviceParams, "-l") 520 deviceParams = append(deviceParams, "com1,stdio") 521 acrnParams = append(acrnParams, strings.Join(deviceParams, "")) 522 523 return acrnParams 524 } 525 526 func (config *Config) appendName() { 527 if config.Name != "" { 528 config.acrnParams = append(config.acrnParams, config.Name) 529 } 530 } 531 532 func (config *Config) appendDevices() { 533 slot := 0 534 for _, d := range config.Devices { 535 if !d.Valid() { 536 continue 537 } 538 539 if slot == acrnGVTgReservedSlot { 540 slot++ /*Slot 2 is assigned for GVT-g in acrn, so skip 2 */ 541 baselogger.Info("Slot 2 is assigned for GVT-g in acrn, so skipping this slot") 542 543 } 544 config.acrnParams = append(config.acrnParams, d.AcrnParams(slot, config)...) 545 slot++ 546 } 547 } 548 549 func (config *Config) appendUUID() { 550 if config.UUID != "" { 551 config.acrnParams = append(config.acrnParams, "-U") 552 config.acrnParams = append(config.acrnParams, config.UUID) 553 } 554 } 555 556 func (config *Config) appendACPI() { 557 if config.ACPIVirt { 558 config.acrnParams = append(config.acrnParams, "-A") 559 } 560 } 561 562 func (config *Config) appendMemory() { 563 if config.Memory.Size != "" { 564 config.acrnParams = append(config.acrnParams, "-m") 565 config.acrnParams = append(config.acrnParams, config.Memory.Size) 566 } 567 } 568 569 func (config *Config) appendKernel() { 570 if config.Kernel.Path == "" { 571 return 572 } 573 config.acrnParams = append(config.acrnParams, "-k") 574 config.acrnParams = append(config.acrnParams, config.Kernel.Path) 575 576 if config.Kernel.Params == "" { 577 return 578 } 579 config.acrnParams = append(config.acrnParams, "-B") 580 config.acrnParams = append(config.acrnParams, config.Kernel.Params) 581 } 582 583 // LaunchAcrn can be used to launch a new acrn instance. 584 // 585 // The Config parameter contains a set of acrn parameters and settings. 586 // 587 // This function writes its log output via logger parameter. 588 func LaunchAcrn(config Config, logger *logrus.Entry) (int, string, error) { 589 baselogger = logger 590 config.appendUUID() 591 config.appendACPI() 592 config.appendMemory() 593 config.appendDevices() 594 config.appendKernel() 595 config.appendName() 596 597 return LaunchCustomAcrn(context.Background(), config.Path, config.acrnParams, logger) 598 } 599 600 // LaunchCustomAcrn can be used to launch a new acrn instance. 601 // 602 // The path parameter is used to pass the acrn executable path. 603 // 604 // params is a slice of options to pass to acrn-dm 605 // 606 // This function writes its log output via logger parameter. 607 func LaunchCustomAcrn(ctx context.Context, path string, params []string, 608 logger *logrus.Entry) (int, string, error) { 609 610 errStr := "" 611 612 if path == "" { 613 path = "acrn-dm" 614 } 615 616 /* #nosec */ 617 cmd := exec.CommandContext(ctx, path, params...) 618 619 var stderr bytes.Buffer 620 cmd.Stderr = &stderr 621 logger.WithFields(logrus.Fields{ 622 "Path": path, 623 "Params": params, 624 }).Info("launching acrn with:") 625 626 err := cmd.Start() 627 if err != nil { 628 logger.Errorf("Unable to launch %s: %v", path, err) 629 errStr = stderr.String() 630 logger.Errorf("%s", errStr) 631 } 632 return cmd.Process.Pid, errStr, err 633 } 634 635 func (a *acrnArchBase) appendImage(devices []Device, path string) ([]Device, error) { 636 if _, err := os.Stat(path); os.IsNotExist(err) { 637 return nil, err 638 } 639 640 ImgBlkdevice := BlockDevice{ 641 FilePath: path, 642 Index: 0, 643 } 644 645 devices = append(devices, ImgBlkdevice) 646 647 return devices, nil 648 } 649 650 // appendBridges appends to devices the given bridges 651 func (a *acrnArchBase) appendBridges(devices []Device) []Device { 652 devices = append(devices, 653 BridgeDevice{ 654 Function: 0, 655 Emul: acrnHostBridge, 656 Config: "", 657 }, 658 ) 659 660 return devices 661 } 662 663 // appendBridges appends to devices the given bridges 664 func (a *acrnArchBase) appendLPC(devices []Device) []Device { 665 devices = append(devices, 666 LPCDevice{ 667 Function: 0, 668 Emul: acrnLPCDev, 669 }, 670 ) 671 672 return devices 673 } 674 675 func (a *acrnArchBase) appendConsole(devices []Device, path string) []Device { 676 console := ConsoleDevice{ 677 Name: "console0", 678 Backend: Socket, 679 PortType: ConsoleBE, 680 Path: path, 681 } 682 683 devices = append(devices, console) 684 return devices 685 } 686 687 func (a *acrnArchBase) appendSocket(devices []Device, socket types.Socket) []Device { 688 serailsocket := ConsoleDevice{ 689 Name: socket.Name, 690 Backend: Socket, 691 PortType: SerialBE, 692 Path: socket.HostPath, 693 } 694 695 devices = append(devices, serailsocket) 696 return devices 697 } 698 699 func networkModelToAcrnType(model NetInterworkingModel) NetDeviceType { 700 switch model { 701 case NetXConnectMacVtapModel: 702 return MACVTAP 703 default: 704 //TAP should work for most other cases 705 return TAP 706 } 707 } 708 709 func (a *acrnArchBase) appendNetwork(devices []Device, endpoint Endpoint) []Device { 710 switch ep := endpoint.(type) { 711 case *VethEndpoint: 712 netPair := ep.NetworkPair() 713 devices = append(devices, 714 NetDevice{ 715 Type: networkModelToAcrnType(netPair.NetInterworkingModel), 716 IFName: netPair.TAPIface.Name, 717 MACAddress: netPair.TAPIface.HardAddr, 718 }, 719 ) 720 case *MacvtapEndpoint: 721 devices = append(devices, 722 NetDevice{ 723 Type: MACVTAP, 724 IFName: ep.Name(), 725 MACAddress: ep.HardwareAddr(), 726 }, 727 ) 728 default: 729 // Return devices as is for unsupported endpoint. 730 baselogger.WithField("Endpoint", endpoint).Error("Unsupported N/W Endpoint") 731 } 732 733 return devices 734 } 735 736 func (a *acrnArchBase) appendBlockDevice(devices []Device, drive config.BlockDrive) []Device { 737 if drive.File == "" { 738 return devices 739 } 740 741 devices = append(devices, 742 BlockDevice{ 743 FilePath: drive.File, 744 Index: drive.Index, 745 }, 746 ) 747 748 return devices 749 } 750 751 func (a *acrnArchBase) handleImagePath(config HypervisorConfig) { 752 if config.ImagePath != "" { 753 a.kernelParams = append(a.kernelParams, acrnKernelRootParams...) 754 a.kernelParamsNonDebug = append(a.kernelParamsNonDebug, acrnKernelParamsSystemdNonDebug...) 755 a.kernelParamsDebug = append(a.kernelParamsDebug, acrnKernelParamsSystemdDebug...) 756 } 757 }