• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

bobuhiro11 / gokvm / 21215126589

21 Jan 2026 03:21PM UTC coverage: 68.011% (-0.1%) from 68.145%
21215126589

Pull #183

github

bobuhiro11
fix

Signed-off-by: Nobuhiro MIKI <nob@bobuhiro11.net>
Pull Request #183: Fix flaky tests by retrying ioctl on EINTR.

19 of 26 new or added lines in 2 files covered. (73.08%)

4 existing lines in 1 file now uncovered.

2260 of 3323 relevant lines covered (68.01%)

2.14 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.45
/machine/machine.go
1
package machine
2

3
import (
4
        "bytes"
5
        "debug/elf"
6
        "encoding/binary"
7
        "encoding/hex"
8
        "errors"
9
        "fmt"
10
        "io"
11
        "log"
12
        "os"
13
        "reflect"
14
        "runtime"
15
        "syscall"
16
        "unsafe"
17

18
        "github.com/bobuhiro11/gokvm/bootparam"
19
        "github.com/bobuhiro11/gokvm/ebda"
20
        "github.com/bobuhiro11/gokvm/iodev"
21
        "github.com/bobuhiro11/gokvm/kvm"
22
        "github.com/bobuhiro11/gokvm/pci"
23
        "github.com/bobuhiro11/gokvm/pvh"
24
        "github.com/bobuhiro11/gokvm/serial"
25
        "github.com/bobuhiro11/gokvm/tap"
26
        "github.com/bobuhiro11/gokvm/virtio"
27
        "golang.org/x/arch/x86/x86asm"
28
)
29

30
const (
31
        bootParamAddr = 0x10000
32
        cmdlineAddr   = 0x20000
33

34
        initrdAddr  = 0xf000000
35
        highMemBase = 0x100000
36

37
        serialIRQ    = 4
38
        virtioNetIRQ = 9
39
        virtioBlkIRQ = 10
40

41
        pageTableBase = 0x30_000
42

43
        MinMemSize = 1 << 25
44
)
45

46
const (
47
        // These *could* be in kvm, but we'll see.
48

49
        // golangci-lint is completely wrong about these names.
50
        // Control Register Paging Enable for example:
51
        // golang style requires all letters in an acronym to be caps.
52
        // CR0 bits.
53
        CR0xPE = 1
54
        CR0xMP = (1 << 1)
55
        CR0xEM = (1 << 2)
56
        CR0xTS = (1 << 3)
57
        CR0xET = (1 << 4)
58
        CR0xNE = (1 << 5)
59
        CR0xWP = (1 << 16)
60
        CR0xAM = (1 << 18)
61
        CR0xNW = (1 << 29)
62
        CR0xCD = (1 << 30)
63
        CR0xPG = (1 << 31)
64

65
        // CR4 bits.
66
        CR4xVME        = 1
67
        CR4xPVI        = (1 << 1)
68
        CR4xTSD        = (1 << 2)
69
        CR4xDE         = (1 << 3)
70
        CR4xPSE        = (1 << 4)
71
        CR4xPAE        = (1 << 5)
72
        CR4xMCE        = (1 << 6)
73
        CR4xPGE        = (1 << 7)
74
        CR4xPCE        = (1 << 8)
75
        CR4xOSFXSR     = (1 << 8)
76
        CR4xOSXMMEXCPT = (1 << 10)
77
        CR4xUMIP       = (1 << 11)
78
        CR4xVMXE       = (1 << 13)
79
        CR4xSMXE       = (1 << 14)
80
        CR4xFSGSBASE   = (1 << 16)
81
        CR4xPCIDE      = (1 << 17)
82
        CR4xOSXSAVE    = (1 << 18)
83
        CR4xSMEP       = (1 << 20)
84
        CR4xSMAP       = (1 << 21)
85

86
        EFERxSCE = 1
87
        EFERxLME = (1 << 8)
88
        EFERxLMA = (1 << 10)
89
        EFERxNXE = (1 << 11)
90

91
        // 64-bit page * entry bits.
92
        PDE64xPRESENT  = 1
93
        PDE64xRW       = (1 << 1)
94
        PDE64xUSER     = (1 << 2)
95
        PDE64xACCESSED = (1 << 5)
96
        PDE64xDIRTY    = (1 << 6)
97
        PDE64xPS       = (1 << 7)
98
        PDE64xG        = (1 << 8)
99
)
100

101
const (
102
        // Poison is an instruction that should force a vmexit.
103
        // it fills memory to make catching guest errors easier.
104
        // vmcall, nop is this pattern
105
        // Poison = []byte{0x0f, 0x0b, } //0x01, 0xC1, 0x90}
106
        // Disassembly:
107
        // 0:  b8 be ba fe ca          mov    eax,0xcafebabe
108
        // 5:  90                      nop
109
        // 6:  0f 0b                   ud2
110
        Poison = "\xB8\xBE\xBA\xFE\xCA\x90\x0F\x0B"
111
)
112

113
var ErrZeroSizeKernel = errors.New("kernel is 0 bytes")
114

115
// ErrWriteToCF9 indicates a write to cf9, the standard x86 reset port.
116
var ErrWriteToCF9 = fmt.Errorf("power cycle via 0xcf9")
117

118
// ErrBadVA indicates a bad virtual address was used.
119
var ErrBadVA = fmt.Errorf("bad virtual address")
120

121
// ErrBadCPU indicates a cpu number is invalid.
122
var ErrBadCPU = fmt.Errorf("bad cpu number")
123

124
// ErrUnsupported indicates something we do not yet do.
125
var ErrUnsupported = fmt.Errorf("unsupported")
126

127
// ErrMemTooSmall indicates the requested memory size is too small.
128
var ErrMemTooSmall = fmt.Errorf("mem request must be at least 1<<20")
129

130
var ErrNotELF64File = fmt.Errorf("file is not ELF64")
131

132
var errPTNoteHasNoFSize = fmt.Errorf("elf programm PT_NOTE has file size equel zero")
133

134
type Machine struct {
135
        kvmFd, vmFd    uintptr
136
        vcpuFds        []uintptr
137
        mem            []byte
138
        runs           []*kvm.RunData
139
        pci            *pci.PCI
140
        serial         *serial.Serial
141
        devices        []iodev.Device
142
        ioportHandlers [0x10000][2]func(port uint64, bytes []byte) error
143
}
144

145
// New creates a new KVM. This includes opening the kvm device, creating VM, creating
146
// vCPUs, and attaching memory, disk (if needed), and tap (if needed).
147
func New(kvmPath string, nCpus int, memSize int) (*Machine, error) {
3✔
148
        if memSize < MinMemSize {
6✔
149
                return nil, fmt.Errorf("memory size %d:%w", memSize, ErrMemTooSmall)
3✔
150
        }
3✔
151

152
        m := &Machine{}
3✔
153

3✔
154
        m.pci = pci.New(pci.NewBridge())
3✔
155

3✔
156
        var err error
3✔
157

3✔
158
        m.kvmFd, m.vmFd, m.vcpuFds, m.runs, err = initVMandVCPU(kvmPath, nCpus)
3✔
159
        if err != nil {
3✔
160
                return nil, err
×
161
        }
×
162

163
        // initCPUIDs here manually
164
        for cpuNr := range m.runs {
6✔
165
                if err := m.initCPUID(cpuNr); err != nil {
3✔
166
                        return nil, err
×
167
                }
×
168
        }
169

170
        // Another coding anti-pattern reguired by golangci-lint.
171
        // Would not pass review in Google.
172
        if m.mem, err = syscall.Mmap(-1, 0, memSize,
3✔
173
                syscall.PROT_READ|syscall.PROT_WRITE,
3✔
174
                syscall.MAP_SHARED|syscall.MAP_ANONYMOUS); err != nil {
3✔
175
                return m, err
×
176
        }
×
177

178
        err = kvm.SetUserMemoryRegion(m.vmFd, &kvm.UserspaceMemoryRegion{
3✔
179
                Slot: 0, Flags: 0, GuestPhysAddr: 0, MemorySize: uint64(memSize),
3✔
180
                UserspaceAddr: uint64(uintptr(unsafe.Pointer(&m.mem[0]))),
3✔
181
        })
3✔
182
        if err != nil {
3✔
183
                return m, err
×
184
        }
×
185

186
        // Poison memory.
187
        // 0 is valid instruction and if you start running in the middle of all those
188
        // 0's it is impossible to diagnore.
189
        for i := highMemBase; i < len(m.mem); i += len(Poison) {
6✔
190
                copy(m.mem[i:], Poison)
3✔
191
        }
3✔
192

193
        return m, nil
3✔
194
}
195

196
func (m *Machine) AddTapIf(tapIfName string) error {
3✔
197
        t, err := tap.New(tapIfName)
3✔
198
        if err != nil {
3✔
199
                return err
×
200
        }
×
201

202
        v := virtio.NewNet(virtioNetIRQ, m, t, m.mem)
3✔
203
        go v.TxThreadEntry()
3✔
204
        go v.RxThreadEntry()
3✔
205
        // 00:01.0 for Virtio net
3✔
206
        m.pci.Devices = append(m.pci.Devices, v)
3✔
207

3✔
208
        return nil
3✔
209
}
210

211
func (m *Machine) AddDisk(diskPath string) error {
3✔
212
        v, err := virtio.NewBlk(diskPath, virtioBlkIRQ, m, m.mem)
3✔
213
        if err != nil {
3✔
214
                return err
×
215
        }
×
216

217
        go v.IOThreadEntry()
3✔
218
        // 00:02.0 for Virtio blk
3✔
219
        m.pci.Devices = append(m.pci.Devices, v)
3✔
220

3✔
221
        return nil
3✔
222
}
223

224
// Translate translates a virtual address for all active CPUs
225
// and returns a []*Translate or error.
226
func (m *Machine) Translate(vaddr uint64) ([]*kvm.Translation, error) {
×
227
        t := make([]*kvm.Translation, 0, len(m.vcpuFds))
×
228

×
229
        for cpu := range m.vcpuFds {
×
230
                tr := &kvm.Translation{
×
231
                        LinearAddress: vaddr,
×
232
                }
×
233
                if err := kvm.Translate(m.vcpuFds[cpu], tr); err != nil {
×
234
                        return t, err
×
235
                }
×
236

237
                t = append(t, tr)
×
238
        }
239

240
        return t, nil
×
241
}
242

243
// SetupRegs sets up the general purpose registers,
244
// including a RIP and BP.
245
func (m *Machine) SetupRegs(rip, bp uint64, amd64 bool) error {
3✔
246
        for _, cpu := range m.vcpuFds {
6✔
247
                if err := m.initRegs(cpu, rip, bp); err != nil {
3✔
248
                        return err
×
249
                }
×
250

251
                if err := m.initSregs(cpu, amd64); err != nil {
3✔
252
                        return err
×
253
                }
×
254
        }
255

256
        return nil
3✔
257
}
258

259
// RunData returns the kvm.RunData for the VM.
260
func (m *Machine) RunData() []*kvm.RunData {
3✔
261
        return m.runs
3✔
262
}
3✔
263

264
func (m *Machine) LoadPVH(kern, initrd *os.File, cmdline string) error {
3✔
265
        // Set EDBA-Pointer
3✔
266
        edbaval := uint32(bootparam.EBDAStart >> 4)
3✔
267
        edbabytes := make([]byte, 4)
3✔
268

3✔
269
        // Convert EBDA-Address to bytes
3✔
270
        binary.LittleEndian.PutUint32(edbabytes, edbaval)
3✔
271

3✔
272
        // Copy EBDA-Address to memory at EBDAPointer-Address (0x40e)
3✔
273
        copy(m.mem[pvh.EBDAPointer:], edbabytes)
3✔
274

3✔
275
        // Create EBDA/mptables - Required for booting into Linux with PVH.
3✔
276
        e, err := ebda.New(len(m.vcpuFds))
3✔
277
        if err != nil {
3✔
278
                return err
×
279
        }
×
280

281
        // Convert EBDA/mptables to bytes
282
        eb, err := e.Bytes()
3✔
283
        if err != nil {
3✔
284
                return err
×
285
        }
×
286

287
        // Write EBDA/mptables to memory at EBDAStart (0x0009_FC00)
288
        copy(m.mem[bootparam.EBDAStart:], eb)
3✔
289

3✔
290
        // Create Global Descriptor Table
3✔
291
        gdt := pvh.CreateGDT()
3✔
292

3✔
293
        // Write GDT to memory
3✔
294
        copy(m.mem[pvh.BootGDTStart:], gdt.Bytes())
3✔
295

3✔
296
        // Write IDT to memory
3✔
297
        copy(m.mem[pvh.BootIDTStart:], []byte{0x0})
3✔
298

3✔
299
        // Load firmware as ELF
3✔
300
        fwElf, err := elf.NewFile(kern)
3✔
301
        if err != nil {
3✔
302
                // Abort if no ELF-File
×
303
                return err
×
304
        }
×
305

306
        ripAddr := fwElf.Entry
3✔
307

3✔
308
        for _, entry := range fwElf.Progs {
6✔
309
                if entry.Type == elf.PT_LOAD {
6✔
310
                        _, err := entry.ReadAt(m.mem[entry.Paddr:], 0)
3✔
311
                        if err != nil && !errors.Is(err, io.EOF) {
3✔
312
                                return err
×
313
                        }
×
314
                } else if entry.Type == elf.PT_NOTE {
6✔
315
                        if entry.Filesz == 0 {
3✔
316
                                return errPTNoteHasNoFSize
×
317
                        }
×
318

319
                        addr, _ := pvh.ParsePVHEntry(kern, entry)
3✔
320

3✔
321
                        if fwElf.Entry != uint64(addr) {
6✔
322
                                ripAddr = uint64(addr)
3✔
323
                        }
3✔
324
                }
325

326
                continue
3✔
327
        }
328

329
        for _, cpu := range m.vcpuFds {
6✔
330
                if err := pvh.InitRegs(cpu, ripAddr); err != nil {
3✔
331
                        return err
×
332
                }
×
333

334
                if err := pvh.InitSRegs(cpu, gdt); err != nil {
3✔
335
                        return err
×
336
                }
×
337
        }
338

339
        pvhstartinfo := pvh.NewStartInfo(bootparam.EBDAStart, cmdlineAddr)
3✔
340

3✔
341
        if initrd != nil {
6✔
342
                initrdSize, err := initrd.ReadAt(m.mem[initrdAddr:], 0)
3✔
343
                if err != nil && initrdSize == 0 && !errors.Is(err, io.EOF) {
3✔
344
                        return fmt.Errorf("initrd: (%v, %w)", initrdSize, err)
×
345
                }
×
346

347
                // Load kernel command-line parameters
348
                copy(m.mem[cmdlineAddr:], cmdline)
3✔
349
                m.mem[cmdlineAddr+len(cmdline)] = 0 // for null terminated string
3✔
350

3✔
351
                ramdiskmod := pvh.NewModListEntry(initrdAddr, uint64(initrdSize), 0)
3✔
352

3✔
353
                pvhstartinfo.NrModules += 1
3✔
354
                pvhstartinfo.ModlistPAddr = pvh.PVHModlistStart
3✔
355

3✔
356
                ramdiskmodbytes, err := ramdiskmod.Bytes()
3✔
357
                if err != nil {
3✔
358
                        return err
×
359
                }
×
360

361
                copy(m.mem[pvh.PVHModlistStart:], ramdiskmodbytes)
3✔
362

3✔
363
                m.AddDevice(&iodev.Noop{Port: 0x80, Psize: 0x30}) // DMA Page Registers (Commonly 74L612 Chip)
3✔
364
        } else {
3✔
365
                m.AddDevice(&iodev.PostCode{}) // Port 0x80
3✔
366
        }
3✔
367

368
        memmapentries := make([]*pvh.HVMMemMapTableEntry, 0)
3✔
369

3✔
370
        entry0 := pvh.NewMemMapTableEntry(0,
3✔
371
                bootparam.EBDAStart,
3✔
372
                bootparam.E820Ram)
3✔
373

3✔
374
        memmapentries = append(memmapentries, entry0)
3✔
375

3✔
376
        entry := pvh.NewMemMapTableEntry(
3✔
377
                pvh.HighRAMStart,
3✔
378
                uint64(len(m.mem)-pvh.HighRAMStart),
3✔
379
                bootparam.E820Ram)
3✔
380

3✔
381
        memmapentries = append(memmapentries, entry)
3✔
382

3✔
383
        pvhstartinfo.MemMapEntries = uint32(len(memmapentries))
3✔
384

3✔
385
        memOffset := pvh.PVHMemMapStart
3✔
386

3✔
387
        // Copy the MEMMapEntries to memory one at a time.
3✔
388
        for _, entry := range memmapentries {
6✔
389
                b, err := entry.Bytes()
3✔
390
                if err != nil {
3✔
391
                        return err
×
392
                }
×
393

394
                copy(m.mem[memOffset:], b)
3✔
395

3✔
396
                memOffset += len(b)
3✔
397
        }
398

399
        // Copy the PVHInfoStart struct to memory.
400
        pvhstartinfob, err := pvhstartinfo.Bytes()
3✔
401
        if err != nil {
3✔
402
                return err
×
403
        }
×
404

405
        copy(m.mem[pvh.PVHInfoStart:], pvhstartinfob)
3✔
406

3✔
407
        if m.serial, err = serial.New(m); err != nil {
3✔
408
                return err
×
409
        }
×
410

411
        m.AddDevice(&iodev.FWDebug{}) // Port 0x402
3✔
412
        m.AddDevice(iodev.NewCMOS(0xC000000, 0x0))
3✔
413
        m.AddDevice(iodev.NewACPIPMTimer())
3✔
414
        m.initIOPortHandlers()
3✔
415

3✔
416
        return nil
3✔
417
}
418

419
// LoadLinux loads a bzImage or ELF file, an optional initrd, and
420
// optional params.
421
func (m *Machine) LoadLinux(kernel, initrd io.ReaderAt, params string) error {
3✔
422
        var (
3✔
423
                DefaultKernelAddr = uint64(highMemBase)
3✔
424
                err               error
3✔
425
        )
3✔
426

3✔
427
        e, err := ebda.New(len(m.vcpuFds))
3✔
428
        if err != nil {
3✔
429
                return err
×
430
        }
×
431

432
        bytes, err := e.Bytes()
3✔
433
        if err != nil {
3✔
434
                return err
×
435
        }
×
436

437
        copy(m.mem[bootparam.EBDAStart:], bytes)
3✔
438

3✔
439
        // Load initrd
3✔
440
        var initrdSize int
3✔
441
        if initrd != nil {
6✔
442
                initrdSize, err = initrd.ReadAt(m.mem[initrdAddr:], 0)
3✔
443
                if err != nil && initrdSize == 0 && !errors.Is(err, io.EOF) {
3✔
444
                        return fmt.Errorf("initrd: (%v, %w)", initrdSize, err)
×
445
                }
×
446
        }
447

448
        // Load kernel command-line parameters
449
        copy(m.mem[cmdlineAddr:], params)
3✔
450
        m.mem[cmdlineAddr+len(params)] = 0 // for null terminated string
3✔
451

3✔
452
        // try to read as ELF. If it fails, no problem,
3✔
453
        // next effort is to read as a bzimage.
3✔
454
        var isElfFile bool
3✔
455

3✔
456
        k, err := elf.NewFile(kernel)
3✔
457
        if err == nil {
6✔
458
                isElfFile = true
3✔
459
        }
3✔
460

461
        bootParam := &bootparam.BootParam{}
3✔
462

3✔
463
        // might be a bzimage
3✔
464
        if !isElfFile {
6✔
465
                // Load Boot Param
3✔
466
                bootParam, err = bootparam.New(kernel)
3✔
467
                if err != nil {
3✔
468
                        return err
×
469
                }
×
470
        }
471

472
        // refs https://github.com/kvmtool/kvmtool/blob/0e1882a49f81cb15d328ef83a78849c0ea26eecc/x86/bios.c#L66-L86
473
        bootParam.AddE820Entry(
3✔
474
                bootparam.RealModeIvtBegin,
3✔
475
                bootparam.EBDAStart-bootparam.RealModeIvtBegin,
3✔
476
                bootparam.E820Ram,
3✔
477
        )
3✔
478
        bootParam.AddE820Entry(
3✔
479
                bootparam.EBDAStart,
3✔
480
                bootparam.VGARAMBegin-bootparam.EBDAStart,
3✔
481
                bootparam.E820Reserved,
3✔
482
        )
3✔
483
        bootParam.AddE820Entry(
3✔
484
                bootparam.MBBIOSBegin,
3✔
485
                bootparam.MBBIOSEnd-bootparam.MBBIOSBegin,
3✔
486
                bootparam.E820Reserved,
3✔
487
        )
3✔
488
        bootParam.AddE820Entry(
3✔
489
                highMemBase,
3✔
490
                uint64(len(m.mem)-highMemBase),
3✔
491
                bootparam.E820Ram,
3✔
492
        )
3✔
493

3✔
494
        bootParam.Hdr.VidMode = 0xFFFF                                                                  // Proto ALL
3✔
495
        bootParam.Hdr.TypeOfLoader = 0xFF                                                               // Proto 2.00+
3✔
496
        bootParam.Hdr.RamdiskImage = initrdAddr                                                         // Proto 2.00+
3✔
497
        bootParam.Hdr.RamdiskSize = uint32(initrdSize)                                                  // Proto 2.00+
3✔
498
        bootParam.Hdr.LoadFlags |= bootparam.CanUseHeap | bootparam.LoadedHigh | bootparam.KeepSegments // Proto 2.00+
3✔
499
        bootParam.Hdr.HeapEndPtr = 0xFE00                                                               // Proto 2.01+
3✔
500
        bootParam.Hdr.ExtLoaderVer = 0                                                                  // Proto 2.02+
3✔
501
        bootParam.Hdr.CmdlinePtr = cmdlineAddr                                                          // Proto 2.06+
3✔
502
        bootParam.Hdr.CmdlineSize = uint32(len(params) + 1)                                             // Proto 2.06+
3✔
503

3✔
504
        bytes, err = bootParam.Bytes()
3✔
505
        if err != nil {
3✔
506
                return err
×
507
        }
×
508

509
        copy(m.mem[bootParamAddr:], bytes)
3✔
510

3✔
511
        var (
3✔
512
                amd64    bool
3✔
513
                kernSize int
3✔
514
        )
3✔
515

3✔
516
        switch isElfFile {
3✔
517
        case false:
3✔
518
                // Load kernel
3✔
519
                // copy to g.mem with offset setupsz
3✔
520
                //
3✔
521
                // The 32-bit (non-real-mode) kernel starts at offset (setup_sects+1)*512 in
3✔
522
                // the kernel file (again, if setup_sects == 0 the real value is 4.) It should
3✔
523
                // be loaded at address 0x10000 for Image/zImage kernels and highMemBase for bzImage kernels.
3✔
524
                //
3✔
525
                // refs: https://www.kernel.org/doc/html/latest/x86/boot.html#loading-the-rest-of-the-kernel
3✔
526
                setupsz := int(bootParam.Hdr.SetupSects+1) * 512
3✔
527

3✔
528
                kernSize, err = kernel.ReadAt(m.mem[DefaultKernelAddr:], int64(setupsz))
3✔
529

3✔
530
                if err != nil && !errors.Is(err, io.EOF) {
3✔
531
                        return fmt.Errorf("kernel: (%v, %w)", kernSize, err)
×
532
                }
×
533
        case true:
3✔
534
                if k.Class == elf.ELFCLASS64 {
6✔
535
                        amd64 = true
3✔
536
                }
3✔
537

538
                DefaultKernelAddr = k.Entry
3✔
539

3✔
540
                for i, p := range k.Progs {
6✔
541
                        if p.Type != elf.PT_LOAD {
6✔
542
                                continue
3✔
543
                        }
544

545
                        log.Printf("Load elf segment @%#x from file %#x %#x bytes", p.Paddr, p.Off, p.Filesz)
3✔
546

3✔
547
                        n, err := p.ReadAt(m.mem[p.Paddr:], 0)
3✔
548
                        if !errors.Is(err, io.EOF) || uint64(n) != p.Filesz {
3✔
549
                                return fmt.Errorf("reading ELF prog %d@%#x: %d/%d bytes, err %w", i, p.Paddr, n, p.Filesz, err)
×
550
                        }
×
551

552
                        kernSize += n
3✔
553
                }
554
        }
555

556
        if kernSize == 0 {
3✔
557
                return ErrZeroSizeKernel
×
558
        }
×
559

560
        if err := m.SetupRegs(DefaultKernelAddr, bootParamAddr, amd64); err != nil {
3✔
561
                return err
×
562
        }
×
563

564
        if m.serial, err = serial.New(m); err != nil {
3✔
565
                return err
×
566
        }
×
567

568
        m.AddDevice(iodev.NewCMOS(0xC000_0000, 0x0))
3✔
569
        m.AddDevice(&iodev.Noop{Port: 0x80, Psize: 0xA0})
3✔
570
        m.initIOPortHandlers()
3✔
571

3✔
572
        return nil
3✔
573
}
574

575
// GetInputChan returns a chan <- byte for serial.
576
func (m *Machine) GetInputChan() chan<- byte {
3✔
577
        return m.serial.GetInputChan()
3✔
578
}
3✔
579

580
// GetRegs gets regs for vCPU.
581
func (m *Machine) GetRegs(cpu int) (*kvm.Regs, error) {
3✔
582
        fd, err := m.CPUToFD(cpu)
3✔
583
        if err != nil {
6✔
584
                return nil, err
3✔
585
        }
3✔
586

587
        return kvm.GetRegs(fd)
3✔
588
}
589

590
// GetSRegs gets sregs for vCPU.
591
func (m *Machine) GetSRegs(cpu int) (*kvm.Sregs, error) {
3✔
592
        fd, err := m.CPUToFD(cpu)
3✔
593
        if err != nil {
6✔
594
                return nil, err
3✔
595
        }
3✔
596

597
        return kvm.GetSregs(fd)
×
598
}
599

600
// SetRegs sets regs for vCPU.
601
func (m *Machine) SetRegs(cpu int, r *kvm.Regs) error {
3✔
602
        fd, err := m.CPUToFD(cpu)
3✔
603
        if err != nil {
6✔
604
                return err
3✔
605
        }
3✔
606

607
        return kvm.SetRegs(fd, r)
3✔
608
}
609

610
// SetSRegs sets sregs for vCPU.
611
func (m *Machine) SetSRegs(cpu int, s *kvm.Sregs) error {
3✔
612
        fd, err := m.CPUToFD(cpu)
3✔
613
        if err != nil {
6✔
614
                return err
3✔
615
        }
3✔
616

617
        return kvm.SetSregs(fd, s)
×
618
}
619

620
func (m *Machine) initRegs(vcpufd uintptr, rip, bp uint64) error {
3✔
621
        regs, err := kvm.GetRegs(vcpufd)
3✔
622
        if err != nil {
3✔
623
                return err
×
624
        }
×
625

626
        // Clear all FLAGS bits, except bit 1 which is always set.
627
        regs.RFLAGS = 2
3✔
628
        regs.RIP = rip
3✔
629
        // Create stack which will grow down.
3✔
630
        regs.RSI = bp
3✔
631

3✔
632
        if err := kvm.SetRegs(vcpufd, regs); err != nil {
3✔
633
                return err
×
634
        }
×
635

636
        return nil
3✔
637
}
638

639
func (m *Machine) initSregs(vcpufd uintptr, amd64 bool) error {
3✔
640
        sregs, err := kvm.GetSregs(vcpufd)
3✔
641
        if err != nil {
3✔
642
                return err
×
643
        }
×
644

645
        if !amd64 {
6✔
646
                // set all segment flat
3✔
647
                sregs.CS.Base, sregs.CS.Limit, sregs.CS.G = 0, 0xFFFFFFFF, 1
3✔
648
                sregs.DS.Base, sregs.DS.Limit, sregs.DS.G = 0, 0xFFFFFFFF, 1
3✔
649
                sregs.FS.Base, sregs.FS.Limit, sregs.FS.G = 0, 0xFFFFFFFF, 1
3✔
650
                sregs.GS.Base, sregs.GS.Limit, sregs.GS.G = 0, 0xFFFFFFFF, 1
3✔
651
                sregs.ES.Base, sregs.ES.Limit, sregs.ES.G = 0, 0xFFFFFFFF, 1
3✔
652
                sregs.SS.Base, sregs.SS.Limit, sregs.SS.G = 0, 0xFFFFFFFF, 1
3✔
653

3✔
654
                sregs.CS.DB, sregs.SS.DB = 1, 1
3✔
655
                sregs.CR0 |= 1 // protected mode
3✔
656

3✔
657
                if err := kvm.SetSregs(vcpufd, sregs); err != nil {
3✔
658
                        return err
×
659
                }
×
660

661
                return nil
3✔
662
        }
663

664
        high64k := m.mem[pageTableBase : pageTableBase+0x6000]
3✔
665

3✔
666
        // zero out the page tables.
3✔
667
        // but we might in fact want to poison them?
3✔
668
        // do we really want 1G, for example?
3✔
669
        for i := range high64k {
6✔
670
                high64k[i] = 0
3✔
671
        }
3✔
672

673
        // Set up page tables for long mode.
674
        // take the first six pages of an area it should not touch -- PageTableBase
675
        // present, read/write, page table at 0xffff0000
676
        // ptes[0] = PageTableBase + 0x1000 | 0x3
677
        // 3 in lowest 2 bits means present and read/write
678
        // 0x60 means accessed/dirty
679
        // 0x80 means the page size bit -- 0x80 | 0x60 = 0xe0
680
        // 0x10 here is making it point at the next page.
681
        // another go anti-pattern from golangci-lint.
682
        // golangci-lint claims this file has not been go-fumpt-ed
683
        // but it has.
684
        copy(high64k, []byte{
3✔
685
                0x03,
3✔
686
                0x10 | uint8((pageTableBase>>8)&0xff),
3✔
687
                uint8((pageTableBase >> 16) & 0xff),
3✔
688
                uint8((pageTableBase >> 24) & 0xff), 0, 0, 0, 0,
3✔
689
        })
3✔
690
        // need four pointers to 2M page tables -- PHYSICAL addresses:
3✔
691
        // 0x2000, 0x3000, 0x4000, 0x5000
3✔
692
        // experiment: set PS bit
3✔
693
        // Don't.
3✔
694
        for i := uint64(0); i < 4; i++ {
6✔
695
                ptb := pageTableBase + (i+2)*0x1000
3✔
696
                // Another coding anti-pattern
3✔
697
                copy(high64k[int(i*8)+0x1000:],
3✔
698
                        []byte{
3✔
699
                                /*0x80 |*/ 0x63,
3✔
700
                                uint8((ptb >> 8) & 0xff),
3✔
701
                                uint8((ptb >> 16) & 0xff),
3✔
702
                                uint8((ptb >> 24) & 0xff), 0, 0, 0, 0,
3✔
703
                        })
3✔
704
        }
3✔
705
        // Now the 2M pages.
706
        for i := uint64(0); i < 0x1_0000_0000; i += 0x2_00_000 {
6✔
707
                ptb := i | 0xe3
3✔
708
                ix := int((i/0x2_00_000)*8 + 0x2000)
3✔
709
                // another coding anti-pattern from golangci-lint.
3✔
710
                copy(high64k[ix:], []byte{
3✔
711
                        uint8(ptb),
3✔
712
                        uint8((ptb >> 8) & 0xff),
3✔
713
                        uint8((ptb >> 16) & 0xff),
3✔
714
                        uint8((ptb >> 24) & 0xff), 0, 0, 0, 0,
3✔
715
                })
3✔
716
        }
3✔
717

718
        // set to true to debug.
719
        if false {
3✔
720
                log.Printf("Page tables: %s", hex.Dump(m.mem[pageTableBase:pageTableBase+0x3000]))
×
721
        }
×
722

723
        sregs.CR3 = uint64(pageTableBase)
3✔
724
        sregs.CR4 = CR4xPAE
3✔
725
        sregs.CR0 = CR0xPE | CR0xMP | CR0xET | CR0xNE | CR0xWP | CR0xAM | CR0xPG
3✔
726
        sregs.EFER = EFERxLME | EFERxLMA
3✔
727

3✔
728
        seg := kvm.Segment{
3✔
729
                Base:     0,
3✔
730
                Limit:    0xffffffff,
3✔
731
                Selector: 1 << 3,
3✔
732
                Typ:      11, /* Code: execute, read, accessed */
3✔
733
                Present:  1,
3✔
734
                DPL:      0,
3✔
735
                DB:       0,
3✔
736
                S:        1, /* Code/data */
3✔
737
                L:        1,
3✔
738
                G:        1, /* 4KB granularity */
3✔
739
                AVL:      0,
3✔
740
        }
3✔
741

3✔
742
        sregs.CS = seg
3✔
743

3✔
744
        seg.Typ = 3 /* Data: read/write, accessed */
3✔
745
        seg.Selector = 2 << 3
3✔
746
        sregs.DS, sregs.ES, sregs.FS, sregs.GS, sregs.SS = seg, seg, seg, seg, seg
3✔
747

3✔
748
        if err := kvm.SetSregs(vcpufd, sregs); err != nil {
3✔
749
                return err
×
750
        }
×
751

752
        return nil
3✔
753
}
754

755
func (m *Machine) initCPUID(cpu int) error {
3✔
756
        cpuid := kvm.CPUID{
3✔
757
                Nent:    100,
3✔
758
                Entries: make([]kvm.CPUIDEntry2, 100),
3✔
759
        }
3✔
760

3✔
761
        if err := kvm.GetSupportedCPUID(m.kvmFd, &cpuid); err != nil {
3✔
762
                return err
×
763
        }
×
764

765
        // https://www.kernel.org/doc/html/latest/virt/kvm/cpuid.html
766
        for i := 0; i < int(cpuid.Nent); i++ {
6✔
767
                switch cpuid.Entries[i].Function {
3✔
768
                case kvm.CPUIDFuncPerMon:
3✔
769
                        cpuid.Entries[i].Eax = 0 // disable
3✔
770

771
                case kvm.CPUIDSignature:
3✔
772
                        cpuid.Entries[i].Eax = kvm.CPUIDFeatures
3✔
773
                        cpuid.Entries[i].Ebx = 0x4b4d564b // KVMK
3✔
774
                        cpuid.Entries[i].Ecx = 0x564b4d56 // VMKV
3✔
775
                        cpuid.Entries[i].Edx = 0x4d       // M
3✔
776

777
                case 7:
3✔
778
                        // Unset X86_FEATURE_FSRM (Fast Short Rep Mov)
3✔
779
                        cpuid.Entries[i].Edx &= ^(uint32(1) << 4)
3✔
780

781
                default:
3✔
782
                        continue
3✔
783
                }
784
        }
785

786
        if err := kvm.SetCPUID2(m.vcpuFds[cpu], &cpuid); err != nil {
3✔
787
                return err
×
788
        }
×
789

790
        return nil
3✔
791
}
792

793
// SingleStep enables single stepping the guest.
794
func (m *Machine) SingleStep(onoff bool) error {
3✔
795
        for cpu := range m.vcpuFds {
6✔
796
                if err := kvm.SingleStep(m.vcpuFds[cpu], onoff); err != nil {
3✔
797
                        return fmt.Errorf("single step %d:%w", cpu, err)
×
798
                }
×
799
        }
800

801
        return nil
3✔
802
}
803

804
// RunInfiniteLoop runs the guest cpu until there is an error.
805
// If the error is ErrExitDebug, this function can be called again.
806
func (m *Machine) RunInfiniteLoop(cpu int) error {
3✔
807
        // https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt
3✔
808
        // - vcpu ioctls: These query and set attributes that control the operation
3✔
809
        //   of a single virtual cpu.
3✔
810
        //
3✔
811
        //   vcpu ioctls should be issued from the same thread that was used to create
3✔
812
        //   the vcpu, except for asynchronous vcpu ioctl that are marked as such in
3✔
813
        //   the documentation.  Otherwise, the first ioctl after switching threads
3✔
814
        //   could see a performance impact.
3✔
815
        //
3✔
816
        // - device ioctls: These query and set attributes that control the operation
3✔
817
        //   of a single device.
3✔
818
        //
3✔
819
        //   device ioctls must be issued from the same process (address space) that
3✔
820
        //   was used to create the VM.
3✔
821
        runtime.LockOSThread()
3✔
822
        defer runtime.UnlockOSThread()
3✔
823

3✔
824
        for {
6✔
825
                isContinue, err := m.RunOnce(cpu)
3✔
826
                if isContinue {
6✔
827
                        if err != nil {
3✔
828
                                fmt.Printf("%v\r\n", err)
×
829
                        }
×
830

831
                        continue
3✔
832
                }
833

834
                if err != nil {
×
835
                        return err
×
836
                }
×
837
        }
838
}
839

840
// RunOnce runs the guest vCPU until it exits.
841
func (m *Machine) RunOnce(cpu int) (bool, error) {
3✔
842
        fd, err := m.CPUToFD(cpu)
3✔
843
        if err != nil {
3✔
844
                return false, err
×
845
        }
×
846

847
        _ = kvm.Run(fd)
3✔
848
        exit := kvm.ExitType(m.runs[cpu].ExitReason)
3✔
849

3✔
850
        switch exit {
3✔
851
        case kvm.EXITHLT:
×
852
                return false, err
×
853
        case kvm.EXITIO:
3✔
854
                direction, size, port, count, offset := m.runs[cpu].IO()
3✔
855
                f := m.ioportHandlers[port][direction]
3✔
856

3✔
857
                bytes := (*(*[100]byte)(unsafe.Pointer(uintptr(unsafe.Pointer(m.runs[cpu])) + uintptr(offset))))[0:size]
3✔
858
                for i := 0; i < int(count); i++ {
6✔
859
                        if err := f(port, bytes); err != nil {
3✔
860
                                return false, err
×
861
                        }
×
862
                }
863

864
                return true, err
3✔
865
        case kvm.EXITUNKNOWN:
×
866
                return true, err
×
UNCOV
867
        case kvm.EXITINTR:
×
UNCOV
868
                // When a signal is sent to the thread hosting the VM it will result in EINTR
×
UNCOV
869
                // refs https://gist.github.com/mcastelino/df7e65ade874f6890f618dc51778d83a
×
UNCOV
870
                return true, nil
×
871
        case kvm.EXITDEBUG:
3✔
872
                return false, kvm.ErrDebug
3✔
873

874
        case kvm.EXITDCR,
875
                kvm.EXITEXCEPTION,
876
                kvm.EXITFAILENTRY,
877
                kvm.EXITHYPERCALL,
878
                kvm.EXITINTERNALERROR,
879
                kvm.EXITIRQWINDOWOPEN,
880
                kvm.EXITMMIO,
881
                kvm.EXITNMI,
882
                kvm.EXITS390RESET,
883
                kvm.EXITS390SIEIC,
884
                kvm.EXITSETTPR,
885
                kvm.EXITSHUTDOWN,
886
                kvm.EXITTPRACCESS:
3✔
887
                if err != nil {
3✔
888
                        return false, err
×
889
                }
×
890

891
                return false, fmt.Errorf("%w: %s", kvm.ErrUnexpectedExitReason, exit.String())
3✔
892
        default:
×
893
                if err != nil {
×
894
                        return false, err
×
895
                }
×
896

897
                r, _ := m.GetRegs(cpu)
×
898
                s, _ := m.GetSRegs(cpu)
×
899
                // another coding anti-pattern from golangci-lint.
×
900
                return false, fmt.Errorf("%w: %v: regs:\n%s",
×
901
                        kvm.ErrUnexpectedExitReason,
×
902
                        kvm.ExitType(m.runs[cpu].ExitReason).String(), show("", &s, &r))
×
903
        }
904
}
905

906
func (m *Machine) registerIOPortHandler(
907
        start, end uint64,
908
        inHandler, outHandler func(port uint64, bytes []byte) error,
909
) {
3✔
910
        for i := start; i < end; i++ {
6✔
911
                m.ioportHandlers[i][kvm.EXITIOIN] = inHandler
3✔
912
                m.ioportHandlers[i][kvm.EXITIOOUT] = outHandler
3✔
913
        }
3✔
914
}
915

916
func (m *Machine) initIOPortHandlers() {
3✔
917
        funcNone := func(port uint64, bytes []byte) error {
6✔
918
                return nil
3✔
919
        }
3✔
920

921
        funcError := func(port uint64, bytes []byte) error {
3✔
922
                return fmt.Errorf("%w: unexpected io port 0x%x", kvm.ErrUnexpectedExitReason, port)
×
923
        }
×
924

925
        // 0xCF9 port can get three values for three types of reset:
926
        //
927
        // Writing 4 to 0xCF9:(INIT) Will INIT the CPU. Meaning it will jump
928
        // to the initial location of booting but it will keep many CPU
929
        // elements untouched. Most internal tables, chaches etc will remain
930
        // unchanged by the Init call (but may change during it).
931
        //
932
        // Writing 6 to 0xCF9:(RESET) Will RESET the CPU with all
933
        // internal tables caches etc cleared to initial state.
934
        //
935
        // Writing 0xE to 0xCF9:(RESTART) Will power cycle the mother board
936
        // with everything that comes with it.
937
        // For now, we will exit without regard to the value. Should we wish
938
        // to have more sophisticated cf9 handling, we will need to modify
939
        // gokvm a bit more.
940
        funcOutbCF9 := func(port uint64, bytes []byte) error {
3✔
941
                if len(bytes) == 1 && bytes[0] == 0xe {
×
942
                        return fmt.Errorf("write 0xe to cf9: %w", ErrWriteToCF9)
×
943
                }
×
944

945
                return fmt.Errorf("write %#x to cf9: %w", bytes, ErrWriteToCF9)
×
946
        }
947

948
        // In ubuntu 20.04 on wsl2, the output to IO port 0x64 continued
949
        // infinitely. To deal with this issue, refer to kvmtool and
950
        // configure the input to the Status Register of the PS2 controller.
951
        //
952
        // refs:
953
        // https://github.com/kvmtool/kvmtool/blob/0e1882a49f81cb15d328ef83a78849c0ea26eecc/hw/i8042.c#L312
954
        // https://git.kernel.org/pub/scm/linux/kernel/git/will/kvmtool.git/tree/hw/i8042.c#n312
955
        // https://wiki.osdev.org/%228042%22_PS/2_Controller
956
        funcInbPS2 := func(port uint64, bytes []byte) error {
6✔
957
                bytes[0] = 0x20
3✔
958

3✔
959
                return nil
3✔
960
        }
3✔
961

962
        m.registerIOPortHandler(0, 0x10000, funcError, funcError)    // default handler
3✔
963
        m.registerIOPortHandler(0xcf9, 0xcfa, funcNone, funcOutbCF9) // CF9
3✔
964
        m.registerIOPortHandler(0x3c0, 0x3db, funcNone, funcNone)    // VGA
3✔
965
        m.registerIOPortHandler(0x3b4, 0x3b6, funcNone, funcNone)    // VGA
3✔
966
        m.registerIOPortHandler(0x2f8, 0x300, funcNone, funcNone)    // Serial port 2
3✔
967
        m.registerIOPortHandler(0x3e8, 0x3f0, funcNone, funcNone)    // Serial port 3
3✔
968
        m.registerIOPortHandler(0x2e8, 0x2f0, funcNone, funcNone)    // Serial port 4
3✔
969
        m.registerIOPortHandler(0xcfe, 0xcff, funcNone, funcNone)    // unknown
3✔
970
        m.registerIOPortHandler(0xcfa, 0xcfc, funcNone, funcNone)    // unknown
3✔
971
        m.registerIOPortHandler(0xc000, 0xd000, funcNone, funcNone)  // PCI Configuration Space Access Mechanism #2
3✔
972
        m.registerIOPortHandler(0x60, 0x70, funcInbPS2, funcNone)    // PS/2 Keyboard (Always 8042 Chip)
3✔
973
        m.registerIOPortHandler(0xed, 0xee, funcNone, funcNone)      // 0xed is the new standard delay port.
3✔
974

3✔
975
        // Serial port 1
3✔
976
        m.registerIOPortHandler(serial.COM1Addr, serial.COM1Addr+8, m.serial.In, m.serial.Out)
3✔
977

3✔
978
        // PCI configuration
3✔
979
        //
3✔
980
        // 0xcf8 for address register for PCI Config Space
3✔
981
        // 0xcfc + 0xcff for data for PCI Config Space
3✔
982
        // see https://github.com/torvalds/linux/blob/master/arch/x86/pci/direct.c for more detail.
3✔
983
        m.registerIOPortHandler(0xcf8, 0xcf9, m.pci.PciConfAddrIn, m.pci.PciConfAddrOut)
3✔
984
        m.registerIOPortHandler(0xcfc, 0xd00, m.pci.PciConfDataIn, m.pci.PciConfDataOut)
3✔
985

3✔
986
        // IO Devices - non PCI
3✔
987
        for _, dev := range m.devices {
6✔
988
                m.registerIOPortHandler(dev.IOPort(), dev.IOPort()+dev.Size(), dev.Read, dev.Write)
3✔
989
        }
3✔
990

991
        // PCI devices
992
        for _, dev := range m.pci.Devices {
6✔
993
                m.registerIOPortHandler(dev.IOPort(), dev.IOPort()+dev.Size(), dev.Read, dev.Write)
3✔
994
        }
3✔
995
}
996

997
// InjectSerialIRQ injects a serial interrupt.
998
func (m *Machine) InjectSerialIRQ() error {
3✔
999
        if err := kvm.IRQLineStatus(m.vmFd, serialIRQ, 0); err != nil {
3✔
1000
                return err
×
1001
        }
×
1002

1003
        if err := kvm.IRQLineStatus(m.vmFd, serialIRQ, 1); err != nil {
3✔
1004
                return err
×
1005
        }
×
1006

1007
        return nil
3✔
1008
}
1009

1010
// InjectViortNetIRQ injects a virtio net interrupt.
1011
func (m *Machine) InjectVirtioNetIRQ() error {
3✔
1012
        if err := kvm.IRQLineStatus(m.vmFd, virtioNetIRQ, 0); err != nil {
3✔
1013
                return err
×
1014
        }
×
1015

1016
        if err := kvm.IRQLineStatus(m.vmFd, virtioNetIRQ, 1); err != nil {
3✔
1017
                return err
×
1018
        }
×
1019

1020
        return nil
3✔
1021
}
1022

1023
// InjectViortNetIRQ injects a virtio block interrupt.
1024
func (m *Machine) InjectVirtioBlkIRQ() error {
3✔
1025
        if err := kvm.IRQLineStatus(m.vmFd, virtioBlkIRQ, 0); err != nil {
3✔
1026
                return err
×
1027
        }
×
1028

1029
        if err := kvm.IRQLineStatus(m.vmFd, virtioBlkIRQ, 1); err != nil {
3✔
1030
                return err
×
1031
        }
×
1032

1033
        return nil
3✔
1034
}
1035

1036
// ReadAt implements io.ReadAt for the kvm guest pvh.
1037
func (m *Machine) ReadAt(b []byte, off int64) (int, error) {
3✔
1038
        mem := bytes.NewReader(m.mem)
3✔
1039

3✔
1040
        return mem.ReadAt(b, off)
3✔
1041
}
3✔
1042

1043
// WriteAt implements io.WriteAt for the kvm guest pvh.
1044
func (m *Machine) WriteAt(b []byte, off int64) (int, error) {
3✔
1045
        if off > int64(len(m.mem)) {
6✔
1046
                return 0, syscall.EFBIG
3✔
1047
        }
3✔
1048

1049
        n := copy(m.mem[off:], b)
3✔
1050

3✔
1051
        return n, nil
3✔
1052
}
1053

1054
func showone(indent string, in interface{}) string {
3✔
1055
        var ret string
3✔
1056

3✔
1057
        s := reflect.ValueOf(in).Elem()
3✔
1058
        typeOfT := s.Type()
3✔
1059

3✔
1060
        for i := 0; i < s.NumField(); i++ {
6✔
1061
                f := s.Field(i)
3✔
1062
                if f.Kind() == reflect.String {
3✔
1063
                        ret += fmt.Sprintf(indent+"%s %s = %s\n", typeOfT.Field(i).Name, f.Type(), f.Interface())
×
1064
                } else {
3✔
1065
                        ret += fmt.Sprintf(indent+"%s %s = %#x\n", typeOfT.Field(i).Name, f.Type(), f.Interface())
3✔
1066
                }
3✔
1067
        }
1068

1069
        return ret
3✔
1070
}
1071

1072
func show(indent string, l ...interface{}) string {
3✔
1073
        var ret string
3✔
1074
        for _, i := range l {
6✔
1075
                ret += showone(indent, i)
3✔
1076
        }
3✔
1077

1078
        return ret
3✔
1079
}
1080

1081
// CPUToFD translates a CPU number to an fd.
1082
func (m *Machine) CPUToFD(cpu int) (uintptr, error) {
3✔
1083
        if cpu > len(m.vcpuFds) {
6✔
1084
                return 0, fmt.Errorf("cpu %d out of range 0-%d:%w", cpu, len(m.vcpuFds), ErrBadCPU)
3✔
1085
        }
3✔
1086

1087
        return m.vcpuFds[cpu], nil
3✔
1088
}
1089

1090
// VtoP returns the physical address for a vCPU virtual address.
1091
func (m *Machine) VtoP(cpu int, vaddr uint64) (int64, error) {
3✔
1092
        fd, err := m.CPUToFD(cpu)
3✔
1093
        if err != nil {
6✔
1094
                return 0, err
3✔
1095
        }
3✔
1096

1097
        t := &kvm.Translation{
3✔
1098
                LinearAddress: vaddr,
3✔
1099
        }
3✔
1100
        if err := kvm.Translate(fd, t); err != nil {
3✔
1101
                return -1, err
×
1102
        }
×
1103

1104
        // There can exist a valid translation for memory that does not exist.
1105
        // For now, we call that an error.
1106
        if t.Valid == 0 || t.PhysicalAddress > uint64(len(m.mem)) {
6✔
1107
                return -1, fmt.Errorf("%#x:valid not set:%w", vaddr, ErrBadVA)
3✔
1108
        }
3✔
1109

1110
        return int64(t.PhysicalAddress), nil
3✔
1111
}
1112

1113
// GetReg gets a pointer to a register in kvm.Regs, given
1114
// a register number from reg. This used to be a comprehensive
1115
// case, but golangci-lint disliked the cyclomatic complexity
1116
// So we only show the few registers we support.
1117
func GetReg(r *kvm.Regs, reg x86asm.Reg) (*uint64, error) {
3✔
1118
        if reg == x86asm.RAX {
6✔
1119
                return &r.RAX, nil
3✔
1120
        }
3✔
1121

1122
        if reg == x86asm.RCX {
6✔
1123
                return &r.RCX, nil
3✔
1124
        }
3✔
1125

1126
        if reg == x86asm.RDX {
6✔
1127
                return &r.RDX, nil
3✔
1128
        }
3✔
1129

1130
        if reg == x86asm.RBX {
6✔
1131
                return &r.RBX, nil
3✔
1132
        }
3✔
1133

1134
        if reg == x86asm.RSP {
6✔
1135
                return &r.RSP, nil
3✔
1136
        }
3✔
1137

1138
        if reg == x86asm.RBP {
6✔
1139
                return &r.RBP, nil
3✔
1140
        }
3✔
1141

1142
        if reg == x86asm.RSI {
6✔
1143
                return &r.RSI, nil
3✔
1144
        }
3✔
1145

1146
        if reg == x86asm.RDI {
6✔
1147
                return &r.RDI, nil
3✔
1148
        }
3✔
1149

1150
        if reg == x86asm.R8 {
6✔
1151
                return &r.R8, nil
3✔
1152
        }
3✔
1153

1154
        if reg == x86asm.R9 {
6✔
1155
                return &r.R9, nil
3✔
1156
        }
3✔
1157

1158
        if reg == x86asm.R10 {
6✔
1159
                return &r.R10, nil
3✔
1160
        }
3✔
1161

1162
        if reg == x86asm.R11 {
6✔
1163
                return &r.R11, nil
3✔
1164
        }
3✔
1165

1166
        if reg == x86asm.R12 {
6✔
1167
                return &r.R12, nil
3✔
1168
        }
3✔
1169

1170
        if reg == x86asm.R13 {
6✔
1171
                return &r.R13, nil
3✔
1172
        }
3✔
1173

1174
        if reg == x86asm.R14 {
6✔
1175
                return &r.R14, nil
3✔
1176
        }
3✔
1177

1178
        if reg == x86asm.R15 {
6✔
1179
                return &r.R15, nil
3✔
1180
        }
3✔
1181

1182
        if reg == x86asm.RIP {
6✔
1183
                return &r.RIP, nil
3✔
1184
        }
3✔
1185

1186
        return nil, fmt.Errorf("register %v%w", reg, ErrUnsupported)
3✔
1187
}
1188

1189
// InitKVM takes care of the general kvm setup without dependencies to runtime target.
1190
func initVMandVCPU(
1191
        kvmPath string,
1192
        nCpus int,
1193
) (uintptr, uintptr, []uintptr, []*kvm.RunData, error) {
3✔
1194
        var err error
3✔
1195

3✔
1196
        devKVM, err := os.OpenFile(kvmPath, os.O_RDWR, 0o644)
3✔
1197
        if err != nil {
3✔
1198
                return 0, 0, nil, nil, err
×
1199
        }
×
1200

1201
        kvmFd := devKVM.Fd()
3✔
1202
        vmFd := uintptr(0)
3✔
1203
        vcpuFds := make([]uintptr, nCpus)
3✔
1204
        runs := make([]*kvm.RunData, nCpus)
3✔
1205

3✔
1206
        if vmFd, err = kvm.CreateVM(kvmFd); err != nil {
3✔
1207
                return 0, 0, nil, nil, fmt.Errorf("CreateVM: %w", err)
×
1208
        }
×
1209

1210
        if err := kvm.SetTSSAddr(vmFd, pvh.KVMTSSStart); err != nil {
3✔
1211
                return 0, 0, nil, nil, err
×
1212
        }
×
1213

1214
        if err := kvm.SetIdentityMapAddr(vmFd, pvh.KVMIdentityMapStart); err != nil {
3✔
1215
                return 0, 0, nil, nil, err
×
1216
        }
×
1217

1218
        if err := kvm.CreateIRQChip(vmFd); err != nil {
3✔
1219
                return 0, 0, nil, nil, err
×
1220
        }
×
1221

1222
        if err := kvm.CreatePIT2(vmFd); err != nil {
3✔
1223
                return 0, 0, nil, nil, err
×
1224
        }
×
1225

1226
        mmapSize, err := kvm.GetVCPUMMmapSize(kvmFd)
3✔
1227
        if err != nil {
3✔
1228
                return 0, 0, nil, nil, err
×
1229
        }
×
1230

1231
        for cpu := 0; cpu < nCpus; cpu++ {
6✔
1232
                // Create vCPU
3✔
1233
                vcpuFds[cpu], err = kvm.CreateVCPU(vmFd, cpu)
3✔
1234
                if err != nil {
3✔
1235
                        return 0, 0, nil, nil, err
×
1236
                }
×
1237

1238
                // init kvm_run structure
1239
                r, err := syscall.Mmap(int(vcpuFds[cpu]), 0, int(mmapSize),
3✔
1240
                        syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
3✔
1241
                if err != nil {
3✔
1242
                        return 0, 0, nil, nil, err
×
1243
                }
×
1244

1245
                runs[cpu] = (*kvm.RunData)(unsafe.Pointer(&r[0]))
3✔
1246
        }
1247

1248
        return kvmFd, vmFd, vcpuFds, runs, nil
3✔
1249
}
1250

1251
func (m *Machine) VCPU(stdout io.Writer, cpu, traceCount int) error {
×
1252
        trace := traceCount > 0
×
1253

×
1254
        var err error
×
1255
        // Consider ANOTHER option, maxInsCount, which would
×
1256
        // exit this loop after a certain number of instructions
×
1257
        // were run.
×
1258
        for tc := 0; ; tc++ {
×
1259
                err = m.RunInfiniteLoop(cpu)
×
1260
                if err == nil {
×
1261
                        continue
×
1262
                }
1263

1264
                if !errors.Is(err, kvm.ErrDebug) {
×
1265
                        return fmt.Errorf("CPU %d: %w", cpu, err)
×
1266
                }
×
1267

1268
                if err := m.SingleStep(trace); err != nil {
×
1269
                        fmt.Fprintf(stdout, "Setting trace to %v:%v", trace, err)
×
1270
                }
×
1271

1272
                if tc%traceCount != 0 {
×
1273
                        continue
×
1274
                }
1275

1276
                _, r, s, err := m.Inst(cpu)
×
1277
                if err != nil {
×
1278
                        fmt.Fprintf(stdout, "disassembling after debug exit:%v", err)
×
1279
                } else {
×
1280
                        fmt.Fprintf(stdout, "%#x:%s\r\n", r.RIP, s)
×
1281
                }
×
1282
        }
1283
}
1284

1285
func (m *Machine) GetSerial() *serial.Serial {
×
1286
        return m.serial
×
1287
}
×
1288

1289
func (m *Machine) AddDevice(dev iodev.Device) {
3✔
1290
        m.devices = append(m.devices, dev)
3✔
1291
}
3✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc