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

bobuhiro11 / gokvm / 22289063954

20 Feb 2026 05:54AM UTC coverage: 69.731% (+1.6%) from 68.145%
22289063954

push

github

web-flow
Merge pull request #188 from bobuhiro11/fix_flaky_test

Fix flaky CI tests

217 of 265 new or added lines in 6 files covered. (81.89%)

4 existing lines in 1 file now uncovered.

2435 of 3492 relevant lines covered (69.73%)

0.74 hits per line

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

78.6
/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
        "sync/atomic"
16
        "syscall"
17
        "unsafe"
18

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

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

35
        initrdAddr  = 0xf000000
36
        highMemBase = 0x100000
37

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

42
        pageTableBase = 0x30_000
43

44
        MinMemSize = 1 << 25
45
)
46

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

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

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

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

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

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

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

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

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

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

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

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

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

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

135
// ErrMachineStopped is returned by RunOnce when Machine.Close
136
// has been called.
137
var ErrMachineStopped = errors.New("machine stopped")
138

139
type Machine struct {
140
        kvmFd, vmFd    uintptr
141
        vcpuFds        []uintptr
142
        mem            []byte
143
        runs           []*kvm.RunData
144
        pci            *pci.PCI
145
        serial         *serial.Serial
146
        devices        []iodev.Device
147
        ioportHandlers [0x10000][2]func(port uint64, bytes []byte) error
148
        stopped        uint32
149
}
150

151
// Close stops vCPU goroutines and releases PCI device
152
// resources (tap FDs, disk FDs, signal registrations).
153
func (m *Machine) Close() error {
1✔
154
        atomic.StoreUint32(&m.stopped, 1)
1✔
155

1✔
156
        for _, r := range m.runs {
2✔
157
                r.ImmediateExit = 1
1✔
158
        }
1✔
159

160
        for _, d := range m.pci.Devices {
2✔
161
                if c, ok := d.(io.Closer); ok {
2✔
162
                        c.Close()
1✔
163
                }
1✔
164
        }
165

166
        return nil
1✔
167
}
168

169
// New creates a new KVM. This includes opening the kvm device, creating VM, creating
170
// vCPUs, and attaching memory, disk (if needed), and tap (if needed).
171
func New(kvmPath string, nCpus int, memSize int) (*Machine, error) {
1✔
172
        if memSize < MinMemSize {
2✔
173
                return nil, fmt.Errorf("memory size %d:%w", memSize, ErrMemTooSmall)
1✔
174
        }
1✔
175

176
        m := &Machine{}
1✔
177

1✔
178
        m.pci = pci.New(pci.NewBridge())
1✔
179

1✔
180
        var err error
1✔
181

1✔
182
        m.kvmFd, m.vmFd, m.vcpuFds, m.runs, err = initVMandVCPU(kvmPath, nCpus)
1✔
183
        if err != nil {
1✔
184
                return nil, err
×
185
        }
×
186

187
        // initCPUIDs here manually
188
        for cpuNr := range m.runs {
2✔
189
                if err := m.initCPUID(cpuNr); err != nil {
1✔
190
                        return nil, err
×
191
                }
×
192
        }
193

194
        // Another coding anti-pattern reguired by golangci-lint.
195
        // Would not pass review in Google.
196
        if m.mem, err = syscall.Mmap(-1, 0, memSize,
1✔
197
                syscall.PROT_READ|syscall.PROT_WRITE,
1✔
198
                syscall.MAP_SHARED|syscall.MAP_ANONYMOUS); err != nil {
1✔
199
                return m, err
×
200
        }
×
201

202
        err = kvm.SetUserMemoryRegion(m.vmFd, &kvm.UserspaceMemoryRegion{
1✔
203
                Slot: 0, Flags: 0, GuestPhysAddr: 0, MemorySize: uint64(memSize),
1✔
204
                UserspaceAddr: uint64(uintptr(unsafe.Pointer(&m.mem[0]))),
1✔
205
        })
1✔
206
        if err != nil {
1✔
207
                return m, err
×
208
        }
×
209

210
        // Poison memory.
211
        // 0 is valid instruction and if you start running in the middle of all those
212
        // 0's it is impossible to diagnore.
213
        for i := highMemBase; i < len(m.mem); i += len(Poison) {
2✔
214
                copy(m.mem[i:], Poison)
1✔
215
        }
1✔
216

217
        return m, nil
1✔
218
}
219

220
func (m *Machine) AddTapIf(tapIfName string) error {
1✔
221
        t, err := tap.New(tapIfName)
1✔
222
        if err != nil {
1✔
223
                return err
×
224
        }
×
225

226
        v := virtio.NewNet(virtioNetIRQ, m, t, m.mem)
1✔
227
        go v.TxThreadEntry()
1✔
228
        go v.RxThreadEntry()
1✔
229
        // 00:01.0 for Virtio net
1✔
230
        m.pci.Devices = append(m.pci.Devices, v)
1✔
231

1✔
232
        return nil
1✔
233
}
234

235
func (m *Machine) AddDisk(diskPath string) error {
1✔
236
        v, err := virtio.NewBlk(diskPath, virtioBlkIRQ, m, m.mem)
1✔
237
        if err != nil {
1✔
238
                return err
×
239
        }
×
240

241
        go v.IOThreadEntry()
1✔
242
        // 00:02.0 for Virtio blk
1✔
243
        m.pci.Devices = append(m.pci.Devices, v)
1✔
244

1✔
245
        return nil
1✔
246
}
247

248
// Translate translates a virtual address for all active CPUs
249
// and returns a []*Translate or error.
250
func (m *Machine) Translate(vaddr uint64) ([]*kvm.Translation, error) {
×
251
        t := make([]*kvm.Translation, 0, len(m.vcpuFds))
×
252

×
253
        for cpu := range m.vcpuFds {
×
254
                tr := &kvm.Translation{
×
255
                        LinearAddress: vaddr,
×
256
                }
×
257
                if err := kvm.Translate(m.vcpuFds[cpu], tr); err != nil {
×
258
                        return t, err
×
259
                }
×
260

261
                t = append(t, tr)
×
262
        }
263

264
        return t, nil
×
265
}
266

267
// SetupRegs sets up the general purpose registers,
268
// including a RIP and BP.
269
func (m *Machine) SetupRegs(rip, bp uint64, amd64 bool) error {
1✔
270
        for _, cpu := range m.vcpuFds {
2✔
271
                if err := m.initRegs(cpu, rip, bp); err != nil {
1✔
272
                        return err
×
273
                }
×
274

275
                if err := m.initSregs(cpu, amd64); err != nil {
1✔
276
                        return err
×
277
                }
×
278
        }
279

280
        return nil
1✔
281
}
282

283
// RunData returns the kvm.RunData for the VM.
284
func (m *Machine) RunData() []*kvm.RunData {
1✔
285
        return m.runs
1✔
286
}
1✔
287

288
func (m *Machine) LoadPVH(kern, initrd *os.File, cmdline string) error {
1✔
289
        // Set EDBA-Pointer
1✔
290
        edbaval := uint32(bootparam.EBDAStart >> 4)
1✔
291
        edbabytes := make([]byte, 4)
1✔
292

1✔
293
        // Convert EBDA-Address to bytes
1✔
294
        binary.LittleEndian.PutUint32(edbabytes, edbaval)
1✔
295

1✔
296
        // Copy EBDA-Address to memory at EBDAPointer-Address (0x40e)
1✔
297
        copy(m.mem[pvh.EBDAPointer:], edbabytes)
1✔
298

1✔
299
        // Create EBDA/mptables - Required for booting into Linux with PVH.
1✔
300
        e, err := ebda.New(len(m.vcpuFds))
1✔
301
        if err != nil {
1✔
302
                return err
×
303
        }
×
304

305
        // Convert EBDA/mptables to bytes
306
        eb, err := e.Bytes()
1✔
307
        if err != nil {
1✔
308
                return err
×
309
        }
×
310

311
        // Write EBDA/mptables to memory at EBDAStart (0x0009_FC00)
312
        copy(m.mem[bootparam.EBDAStart:], eb)
1✔
313

1✔
314
        // Create Global Descriptor Table
1✔
315
        gdt := pvh.CreateGDT()
1✔
316

1✔
317
        // Write GDT to memory
1✔
318
        copy(m.mem[pvh.BootGDTStart:], gdt.Bytes())
1✔
319

1✔
320
        // Write IDT to memory
1✔
321
        copy(m.mem[pvh.BootIDTStart:], []byte{0x0})
1✔
322

1✔
323
        // Load firmware as ELF
1✔
324
        fwElf, err := elf.NewFile(kern)
1✔
325
        if err != nil {
1✔
326
                // Abort if no ELF-File
×
327
                return err
×
328
        }
×
329

330
        ripAddr := fwElf.Entry
1✔
331

1✔
332
        for _, entry := range fwElf.Progs {
2✔
333
                if entry.Type == elf.PT_LOAD {
2✔
334
                        _, err := entry.ReadAt(m.mem[entry.Paddr:], 0)
1✔
335
                        if err != nil && !errors.Is(err, io.EOF) {
1✔
336
                                return err
×
337
                        }
×
338
                } else if entry.Type == elf.PT_NOTE {
2✔
339
                        if entry.Filesz == 0 {
1✔
340
                                return errPTNoteHasNoFSize
×
341
                        }
×
342

343
                        addr, _ := pvh.ParsePVHEntry(kern, entry)
1✔
344

1✔
345
                        if fwElf.Entry != uint64(addr) {
2✔
346
                                ripAddr = uint64(addr)
1✔
347
                        }
1✔
348
                }
349

350
                continue
1✔
351
        }
352

353
        for _, cpu := range m.vcpuFds {
2✔
354
                if err := pvh.InitRegs(cpu, ripAddr); err != nil {
1✔
355
                        return err
×
356
                }
×
357

358
                if err := pvh.InitSRegs(cpu, gdt); err != nil {
1✔
359
                        return err
×
360
                }
×
361
        }
362

363
        pvhstartinfo := pvh.NewStartInfo(bootparam.EBDAStart, cmdlineAddr)
1✔
364

1✔
365
        if initrd != nil {
2✔
366
                initrdSize, err := initrd.ReadAt(m.mem[initrdAddr:], 0)
1✔
367
                if err != nil && initrdSize == 0 && !errors.Is(err, io.EOF) {
1✔
368
                        return fmt.Errorf("initrd: (%v, %w)", initrdSize, err)
×
369
                }
×
370

371
                // Load kernel command-line parameters
372
                copy(m.mem[cmdlineAddr:], cmdline)
1✔
373
                m.mem[cmdlineAddr+len(cmdline)] = 0 // for null terminated string
1✔
374

1✔
375
                ramdiskmod := pvh.NewModListEntry(initrdAddr, uint64(initrdSize), 0)
1✔
376

1✔
377
                pvhstartinfo.NrModules += 1
1✔
378
                pvhstartinfo.ModlistPAddr = pvh.PVHModlistStart
1✔
379

1✔
380
                ramdiskmodbytes, err := ramdiskmod.Bytes()
1✔
381
                if err != nil {
1✔
382
                        return err
×
383
                }
×
384

385
                copy(m.mem[pvh.PVHModlistStart:], ramdiskmodbytes)
1✔
386

1✔
387
                m.AddDevice(&iodev.Noop{Port: 0x80, Psize: 0x30}) // DMA Page Registers (Commonly 74L612 Chip)
1✔
388
        } else {
1✔
389
                m.AddDevice(&iodev.PostCode{}) // Port 0x80
1✔
390
        }
1✔
391

392
        memmapentries := make([]*pvh.HVMMemMapTableEntry, 0)
1✔
393

1✔
394
        entry0 := pvh.NewMemMapTableEntry(0,
1✔
395
                bootparam.EBDAStart,
1✔
396
                bootparam.E820Ram)
1✔
397

1✔
398
        memmapentries = append(memmapentries, entry0)
1✔
399

1✔
400
        entry := pvh.NewMemMapTableEntry(
1✔
401
                pvh.HighRAMStart,
1✔
402
                uint64(len(m.mem)-pvh.HighRAMStart),
1✔
403
                bootparam.E820Ram)
1✔
404

1✔
405
        memmapentries = append(memmapentries, entry)
1✔
406

1✔
407
        pvhstartinfo.MemMapEntries = uint32(len(memmapentries))
1✔
408

1✔
409
        memOffset := pvh.PVHMemMapStart
1✔
410

1✔
411
        // Copy the MEMMapEntries to memory one at a time.
1✔
412
        for _, entry := range memmapentries {
2✔
413
                b, err := entry.Bytes()
1✔
414
                if err != nil {
1✔
415
                        return err
×
416
                }
×
417

418
                copy(m.mem[memOffset:], b)
1✔
419

1✔
420
                memOffset += len(b)
1✔
421
        }
422

423
        // Copy the PVHInfoStart struct to memory.
424
        pvhstartinfob, err := pvhstartinfo.Bytes()
1✔
425
        if err != nil {
1✔
426
                return err
×
427
        }
×
428

429
        copy(m.mem[pvh.PVHInfoStart:], pvhstartinfob)
1✔
430

1✔
431
        if m.serial, err = serial.New(m); err != nil {
1✔
432
                return err
×
433
        }
×
434

435
        m.AddDevice(&iodev.FWDebug{}) // Port 0x402
1✔
436
        m.AddDevice(iodev.NewCMOS(0xC000000, 0x0))
1✔
437
        m.AddDevice(iodev.NewACPIPMTimer())
1✔
438
        m.initIOPortHandlers()
1✔
439

1✔
440
        return nil
1✔
441
}
442

443
// LoadLinux loads a bzImage or ELF file, an optional initrd, and
444
// optional params.
445
func (m *Machine) LoadLinux(kernel, initrd io.ReaderAt, params string) error {
1✔
446
        var (
1✔
447
                DefaultKernelAddr = uint64(highMemBase)
1✔
448
                err               error
1✔
449
        )
1✔
450

1✔
451
        e, err := ebda.New(len(m.vcpuFds))
1✔
452
        if err != nil {
1✔
453
                return err
×
454
        }
×
455

456
        bytes, err := e.Bytes()
1✔
457
        if err != nil {
1✔
458
                return err
×
459
        }
×
460

461
        copy(m.mem[bootparam.EBDAStart:], bytes)
1✔
462

1✔
463
        // Load initrd
1✔
464
        var initrdSize int
1✔
465
        if initrd != nil {
2✔
466
                initrdSize, err = initrd.ReadAt(m.mem[initrdAddr:], 0)
1✔
467
                if err != nil && initrdSize == 0 && !errors.Is(err, io.EOF) {
1✔
468
                        return fmt.Errorf("initrd: (%v, %w)", initrdSize, err)
×
469
                }
×
470
        }
471

472
        // Load kernel command-line parameters
473
        copy(m.mem[cmdlineAddr:], params)
1✔
474
        m.mem[cmdlineAddr+len(params)] = 0 // for null terminated string
1✔
475

1✔
476
        // try to read as ELF. If it fails, no problem,
1✔
477
        // next effort is to read as a bzimage.
1✔
478
        var isElfFile bool
1✔
479

1✔
480
        k, err := elf.NewFile(kernel)
1✔
481
        if err == nil {
2✔
482
                isElfFile = true
1✔
483
        }
1✔
484

485
        bootParam := &bootparam.BootParam{}
1✔
486

1✔
487
        // might be a bzimage
1✔
488
        if !isElfFile {
2✔
489
                // Load Boot Param
1✔
490
                bootParam, err = bootparam.New(kernel)
1✔
491
                if err != nil {
1✔
492
                        return err
×
493
                }
×
494
        }
495

496
        // refs https://github.com/kvmtool/kvmtool/blob/0e1882a49f81cb15d328ef83a78849c0ea26eecc/x86/bios.c#L66-L86
497
        bootParam.AddE820Entry(
1✔
498
                bootparam.RealModeIvtBegin,
1✔
499
                bootparam.EBDAStart-bootparam.RealModeIvtBegin,
1✔
500
                bootparam.E820Ram,
1✔
501
        )
1✔
502
        bootParam.AddE820Entry(
1✔
503
                bootparam.EBDAStart,
1✔
504
                bootparam.VGARAMBegin-bootparam.EBDAStart,
1✔
505
                bootparam.E820Reserved,
1✔
506
        )
1✔
507
        bootParam.AddE820Entry(
1✔
508
                bootparam.MBBIOSBegin,
1✔
509
                bootparam.MBBIOSEnd-bootparam.MBBIOSBegin,
1✔
510
                bootparam.E820Reserved,
1✔
511
        )
1✔
512
        bootParam.AddE820Entry(
1✔
513
                highMemBase,
1✔
514
                uint64(len(m.mem)-highMemBase),
1✔
515
                bootparam.E820Ram,
1✔
516
        )
1✔
517

1✔
518
        bootParam.Hdr.VidMode = 0xFFFF                                                                  // Proto ALL
1✔
519
        bootParam.Hdr.TypeOfLoader = 0xFF                                                               // Proto 2.00+
1✔
520
        bootParam.Hdr.RamdiskImage = initrdAddr                                                         // Proto 2.00+
1✔
521
        bootParam.Hdr.RamdiskSize = uint32(initrdSize)                                                  // Proto 2.00+
1✔
522
        bootParam.Hdr.LoadFlags |= bootparam.CanUseHeap | bootparam.LoadedHigh | bootparam.KeepSegments // Proto 2.00+
1✔
523
        bootParam.Hdr.HeapEndPtr = 0xFE00                                                               // Proto 2.01+
1✔
524
        bootParam.Hdr.ExtLoaderVer = 0                                                                  // Proto 2.02+
1✔
525
        bootParam.Hdr.CmdlinePtr = cmdlineAddr                                                          // Proto 2.06+
1✔
526
        bootParam.Hdr.CmdlineSize = uint32(len(params) + 1)                                             // Proto 2.06+
1✔
527

1✔
528
        bytes, err = bootParam.Bytes()
1✔
529
        if err != nil {
1✔
530
                return err
×
531
        }
×
532

533
        copy(m.mem[bootParamAddr:], bytes)
1✔
534

1✔
535
        var (
1✔
536
                amd64    bool
1✔
537
                kernSize int
1✔
538
        )
1✔
539

1✔
540
        switch isElfFile {
1✔
541
        case false:
1✔
542
                // Load kernel
1✔
543
                // copy to g.mem with offset setupsz
1✔
544
                //
1✔
545
                // The 32-bit (non-real-mode) kernel starts at offset (setup_sects+1)*512 in
1✔
546
                // the kernel file (again, if setup_sects == 0 the real value is 4.) It should
1✔
547
                // be loaded at address 0x10000 for Image/zImage kernels and highMemBase for bzImage kernels.
1✔
548
                //
1✔
549
                // refs: https://www.kernel.org/doc/html/latest/x86/boot.html#loading-the-rest-of-the-kernel
1✔
550
                setupsz := int(bootParam.Hdr.SetupSects+1) * 512
1✔
551

1✔
552
                kernSize, err = kernel.ReadAt(m.mem[DefaultKernelAddr:], int64(setupsz))
1✔
553

1✔
554
                if err != nil && !errors.Is(err, io.EOF) {
1✔
555
                        return fmt.Errorf("kernel: (%v, %w)", kernSize, err)
×
556
                }
×
557
        case true:
1✔
558
                if k.Class == elf.ELFCLASS64 {
2✔
559
                        amd64 = true
1✔
560
                }
1✔
561

562
                DefaultKernelAddr = k.Entry
1✔
563

1✔
564
                for i, p := range k.Progs {
2✔
565
                        if p.Type != elf.PT_LOAD {
2✔
566
                                continue
1✔
567
                        }
568

569
                        log.Printf("Load elf segment @%#x from file %#x %#x bytes", p.Paddr, p.Off, p.Filesz)
1✔
570

1✔
571
                        n, err := p.ReadAt(m.mem[p.Paddr:], 0)
1✔
572
                        if !errors.Is(err, io.EOF) || uint64(n) != p.Filesz {
1✔
573
                                return fmt.Errorf("reading ELF prog %d@%#x: %d/%d bytes, err %w", i, p.Paddr, n, p.Filesz, err)
×
574
                        }
×
575

576
                        kernSize += n
1✔
577
                }
578
        }
579

580
        if kernSize == 0 {
1✔
581
                return ErrZeroSizeKernel
×
582
        }
×
583

584
        if err := m.SetupRegs(DefaultKernelAddr, bootParamAddr, amd64); err != nil {
1✔
585
                return err
×
586
        }
×
587

588
        if m.serial, err = serial.New(m); err != nil {
1✔
589
                return err
×
590
        }
×
591

592
        m.AddDevice(iodev.NewCMOS(0xC000_0000, 0x0))
1✔
593
        m.AddDevice(&iodev.Noop{Port: 0x80, Psize: 0xA0})
1✔
594
        m.initIOPortHandlers()
1✔
595

1✔
596
        return nil
1✔
597
}
598

599
// GetInputChan returns a chan <- byte for serial.
600
func (m *Machine) GetInputChan() chan<- byte {
1✔
601
        return m.serial.GetInputChan()
1✔
602
}
1✔
603

604
// GetRegs gets regs for vCPU.
605
func (m *Machine) GetRegs(cpu int) (*kvm.Regs, error) {
1✔
606
        fd, err := m.CPUToFD(cpu)
1✔
607
        if err != nil {
2✔
608
                return nil, err
1✔
609
        }
1✔
610

611
        return kvm.GetRegs(fd)
1✔
612
}
613

614
// GetSRegs gets sregs for vCPU.
615
func (m *Machine) GetSRegs(cpu int) (*kvm.Sregs, error) {
1✔
616
        fd, err := m.CPUToFD(cpu)
1✔
617
        if err != nil {
2✔
618
                return nil, err
1✔
619
        }
1✔
620

621
        return kvm.GetSregs(fd)
×
622
}
623

624
// SetRegs sets regs for vCPU.
625
func (m *Machine) SetRegs(cpu int, r *kvm.Regs) error {
1✔
626
        fd, err := m.CPUToFD(cpu)
1✔
627
        if err != nil {
2✔
628
                return err
1✔
629
        }
1✔
630

631
        return kvm.SetRegs(fd, r)
1✔
632
}
633

634
// SetSRegs sets sregs for vCPU.
635
func (m *Machine) SetSRegs(cpu int, s *kvm.Sregs) error {
1✔
636
        fd, err := m.CPUToFD(cpu)
1✔
637
        if err != nil {
2✔
638
                return err
1✔
639
        }
1✔
640

641
        return kvm.SetSregs(fd, s)
×
642
}
643

644
func (m *Machine) initRegs(vcpufd uintptr, rip, bp uint64) error {
1✔
645
        regs, err := kvm.GetRegs(vcpufd)
1✔
646
        if err != nil {
1✔
647
                return err
×
648
        }
×
649

650
        // Clear all FLAGS bits, except bit 1 which is always set.
651
        regs.RFLAGS = 2
1✔
652
        regs.RIP = rip
1✔
653
        // Create stack which will grow down.
1✔
654
        regs.RSI = bp
1✔
655

1✔
656
        if err := kvm.SetRegs(vcpufd, regs); err != nil {
1✔
657
                return err
×
658
        }
×
659

660
        return nil
1✔
661
}
662

663
func (m *Machine) initSregs(vcpufd uintptr, amd64 bool) error {
1✔
664
        sregs, err := kvm.GetSregs(vcpufd)
1✔
665
        if err != nil {
1✔
666
                return err
×
667
        }
×
668

669
        if !amd64 {
2✔
670
                // set all segment flat
1✔
671
                sregs.CS.Base, sregs.CS.Limit, sregs.CS.G = 0, 0xFFFFFFFF, 1
1✔
672
                sregs.DS.Base, sregs.DS.Limit, sregs.DS.G = 0, 0xFFFFFFFF, 1
1✔
673
                sregs.FS.Base, sregs.FS.Limit, sregs.FS.G = 0, 0xFFFFFFFF, 1
1✔
674
                sregs.GS.Base, sregs.GS.Limit, sregs.GS.G = 0, 0xFFFFFFFF, 1
1✔
675
                sregs.ES.Base, sregs.ES.Limit, sregs.ES.G = 0, 0xFFFFFFFF, 1
1✔
676
                sregs.SS.Base, sregs.SS.Limit, sregs.SS.G = 0, 0xFFFFFFFF, 1
1✔
677

1✔
678
                sregs.CS.DB, sregs.SS.DB = 1, 1
1✔
679
                sregs.CR0 |= 1 // protected mode
1✔
680

1✔
681
                if err := kvm.SetSregs(vcpufd, sregs); err != nil {
1✔
682
                        return err
×
683
                }
×
684

685
                return nil
1✔
686
        }
687

688
        high64k := m.mem[pageTableBase : pageTableBase+0x6000]
1✔
689

1✔
690
        // zero out the page tables.
1✔
691
        // but we might in fact want to poison them?
1✔
692
        // do we really want 1G, for example?
1✔
693
        for i := range high64k {
2✔
694
                high64k[i] = 0
1✔
695
        }
1✔
696

697
        // Set up page tables for long mode.
698
        // take the first six pages of an area it should not touch -- PageTableBase
699
        // present, read/write, page table at 0xffff0000
700
        // ptes[0] = PageTableBase + 0x1000 | 0x3
701
        // 3 in lowest 2 bits means present and read/write
702
        // 0x60 means accessed/dirty
703
        // 0x80 means the page size bit -- 0x80 | 0x60 = 0xe0
704
        // 0x10 here is making it point at the next page.
705
        // another go anti-pattern from golangci-lint.
706
        // golangci-lint claims this file has not been go-fumpt-ed
707
        // but it has.
708
        copy(high64k, []byte{
1✔
709
                0x03,
1✔
710
                0x10 | uint8((pageTableBase>>8)&0xff),
1✔
711
                uint8((pageTableBase >> 16) & 0xff),
1✔
712
                uint8((pageTableBase >> 24) & 0xff), 0, 0, 0, 0,
1✔
713
        })
1✔
714
        // need four pointers to 2M page tables -- PHYSICAL addresses:
1✔
715
        // 0x2000, 0x3000, 0x4000, 0x5000
1✔
716
        // experiment: set PS bit
1✔
717
        // Don't.
1✔
718
        for i := uint64(0); i < 4; i++ {
2✔
719
                ptb := pageTableBase + (i+2)*0x1000
1✔
720
                // Another coding anti-pattern
1✔
721
                copy(high64k[int(i*8)+0x1000:],
1✔
722
                        []byte{
1✔
723
                                /*0x80 |*/ 0x63,
1✔
724
                                uint8((ptb >> 8) & 0xff),
1✔
725
                                uint8((ptb >> 16) & 0xff),
1✔
726
                                uint8((ptb >> 24) & 0xff), 0, 0, 0, 0,
1✔
727
                        })
1✔
728
        }
1✔
729
        // Now the 2M pages.
730
        for i := uint64(0); i < 0x1_0000_0000; i += 0x2_00_000 {
2✔
731
                ptb := i | 0xe3
1✔
732
                ix := int((i/0x2_00_000)*8 + 0x2000)
1✔
733
                // another coding anti-pattern from golangci-lint.
1✔
734
                copy(high64k[ix:], []byte{
1✔
735
                        uint8(ptb),
1✔
736
                        uint8((ptb >> 8) & 0xff),
1✔
737
                        uint8((ptb >> 16) & 0xff),
1✔
738
                        uint8((ptb >> 24) & 0xff), 0, 0, 0, 0,
1✔
739
                })
1✔
740
        }
1✔
741

742
        // set to true to debug.
743
        if false {
1✔
744
                log.Printf("Page tables: %s", hex.Dump(m.mem[pageTableBase:pageTableBase+0x3000]))
×
745
        }
×
746

747
        sregs.CR3 = uint64(pageTableBase)
1✔
748
        sregs.CR4 = CR4xPAE
1✔
749
        sregs.CR0 = CR0xPE | CR0xMP | CR0xET | CR0xNE | CR0xWP | CR0xAM | CR0xPG
1✔
750
        sregs.EFER = EFERxLME | EFERxLMA
1✔
751

1✔
752
        seg := kvm.Segment{
1✔
753
                Base:     0,
1✔
754
                Limit:    0xffffffff,
1✔
755
                Selector: 1 << 3,
1✔
756
                Typ:      11, /* Code: execute, read, accessed */
1✔
757
                Present:  1,
1✔
758
                DPL:      0,
1✔
759
                DB:       0,
1✔
760
                S:        1, /* Code/data */
1✔
761
                L:        1,
1✔
762
                G:        1, /* 4KB granularity */
1✔
763
                AVL:      0,
1✔
764
        }
1✔
765

1✔
766
        sregs.CS = seg
1✔
767

1✔
768
        seg.Typ = 3 /* Data: read/write, accessed */
1✔
769
        seg.Selector = 2 << 3
1✔
770
        sregs.DS, sregs.ES, sregs.FS, sregs.GS, sregs.SS = seg, seg, seg, seg, seg
1✔
771

1✔
772
        if err := kvm.SetSregs(vcpufd, sregs); err != nil {
1✔
773
                return err
×
774
        }
×
775

776
        return nil
1✔
777
}
778

779
func (m *Machine) initCPUID(cpu int) error {
1✔
780
        cpuid := kvm.CPUID{
1✔
781
                Nent:    100,
1✔
782
                Entries: make([]kvm.CPUIDEntry2, 100),
1✔
783
        }
1✔
784

1✔
785
        if err := kvm.GetSupportedCPUID(m.kvmFd, &cpuid); err != nil {
1✔
786
                return err
×
787
        }
×
788

789
        // https://www.kernel.org/doc/html/latest/virt/kvm/cpuid.html
790
        for i := 0; i < int(cpuid.Nent); i++ {
2✔
791
                switch cpuid.Entries[i].Function {
1✔
792
                case kvm.CPUIDFuncPerMon:
1✔
793
                        cpuid.Entries[i].Eax = 0 // disable
1✔
794

795
                case kvm.CPUIDSignature:
1✔
796
                        cpuid.Entries[i].Eax = kvm.CPUIDFeatures
1✔
797
                        cpuid.Entries[i].Ebx = 0x4b4d564b // KVMK
1✔
798
                        cpuid.Entries[i].Ecx = 0x564b4d56 // VMKV
1✔
799
                        cpuid.Entries[i].Edx = 0x4d       // M
1✔
800

801
                case 7:
1✔
802
                        // Unset X86_FEATURE_FSRM (Fast Short Rep Mov)
1✔
803
                        cpuid.Entries[i].Edx &= ^(uint32(1) << 4)
1✔
804

805
                default:
1✔
806
                        continue
1✔
807
                }
808
        }
809

810
        if err := kvm.SetCPUID2(m.vcpuFds[cpu], &cpuid); err != nil {
1✔
811
                return err
×
812
        }
×
813

814
        return nil
1✔
815
}
816

817
// SingleStep enables single stepping the guest.
818
func (m *Machine) SingleStep(onoff bool) error {
1✔
819
        for cpu := range m.vcpuFds {
2✔
820
                if err := kvm.SingleStep(m.vcpuFds[cpu], onoff); err != nil {
1✔
821
                        return fmt.Errorf("single step %d:%w", cpu, err)
×
822
                }
×
823
        }
824

825
        return nil
1✔
826
}
827

828
// RunInfiniteLoop runs the guest cpu until there is an error.
829
// If the error is ErrExitDebug, this function can be called again.
830
func (m *Machine) RunInfiniteLoop(cpu int) error {
1✔
831
        // https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt
1✔
832
        // - vcpu ioctls: These query and set attributes that control the operation
1✔
833
        //   of a single virtual cpu.
1✔
834
        //
1✔
835
        //   vcpu ioctls should be issued from the same thread that was used to create
1✔
836
        //   the vcpu, except for asynchronous vcpu ioctl that are marked as such in
1✔
837
        //   the documentation.  Otherwise, the first ioctl after switching threads
1✔
838
        //   could see a performance impact.
1✔
839
        //
1✔
840
        // - device ioctls: These query and set attributes that control the operation
1✔
841
        //   of a single device.
1✔
842
        //
1✔
843
        //   device ioctls must be issued from the same process (address space) that
1✔
844
        //   was used to create the VM.
1✔
845
        runtime.LockOSThread()
1✔
846
        defer runtime.UnlockOSThread()
1✔
847

1✔
848
        for {
2✔
849
                isContinue, err := m.RunOnce(cpu)
1✔
850
                if isContinue {
2✔
851
                        if err != nil {
1✔
852
                                fmt.Printf("%v\r\n", err)
×
853
                        }
×
854

855
                        continue
1✔
856
                }
857

858
                if err != nil {
2✔
859
                        return err
1✔
860
                }
1✔
861
        }
862
}
863

864
// RunOnce runs the guest vCPU until it exits.
865
func (m *Machine) RunOnce(cpu int) (bool, error) {
1✔
866
        fd, err := m.CPUToFD(cpu)
1✔
867
        if err != nil {
1✔
868
                return false, err
×
869
        }
×
870

871
        _ = kvm.Run(fd)
1✔
872

1✔
873
        if atomic.LoadUint32(&m.stopped) != 0 {
2✔
874
                log.Printf("RunOnce: stopped flag set, exiting")
1✔
875

1✔
876
                return false, ErrMachineStopped
1✔
877
        }
1✔
878

879
        exit := kvm.ExitType(m.runs[cpu].ExitReason)
1✔
880

1✔
881
        switch exit {
1✔
882
        case kvm.EXITHLT:
×
883
                return false, err
×
884
        case kvm.EXITIO:
1✔
885
                direction, size, port, count, offset := m.runs[cpu].IO()
1✔
886
                f := m.ioportHandlers[port][direction]
1✔
887

1✔
888
                bytes := (*(*[100]byte)(unsafe.Pointer(uintptr(unsafe.Pointer(m.runs[cpu])) + uintptr(offset))))[0:size]
1✔
889
                for i := 0; i < int(count); i++ {
2✔
890
                        if err := f(port, bytes); err != nil {
1✔
891
                                return false, err
×
892
                        }
×
893
                }
894

895
                return true, err
1✔
896
        case kvm.EXITUNKNOWN:
×
897
                return true, err
×
UNCOV
898
        case kvm.EXITINTR:
×
UNCOV
899
                // When a signal is sent to the thread hosting the VM it will result in EINTR
×
UNCOV
900
                // refs https://gist.github.com/mcastelino/df7e65ade874f6890f618dc51778d83a
×
UNCOV
901
                return true, nil
×
902
        case kvm.EXITDEBUG:
1✔
903
                return false, kvm.ErrDebug
1✔
904

905
        case kvm.EXITDCR,
906
                kvm.EXITEXCEPTION,
907
                kvm.EXITFAILENTRY,
908
                kvm.EXITHYPERCALL,
909
                kvm.EXITINTERNALERROR,
910
                kvm.EXITIRQWINDOWOPEN,
911
                kvm.EXITMMIO,
912
                kvm.EXITNMI,
913
                kvm.EXITS390RESET,
914
                kvm.EXITS390SIEIC,
915
                kvm.EXITSETTPR,
916
                kvm.EXITSHUTDOWN,
917
                kvm.EXITTPRACCESS:
1✔
918
                if err != nil {
1✔
919
                        return false, err
×
920
                }
×
921

922
                return false, fmt.Errorf("%w: %s", kvm.ErrUnexpectedExitReason, exit.String())
1✔
923
        default:
×
924
                if err != nil {
×
925
                        return false, err
×
926
                }
×
927

928
                r, _ := m.GetRegs(cpu)
×
929
                s, _ := m.GetSRegs(cpu)
×
930
                // another coding anti-pattern from golangci-lint.
×
931
                return false, fmt.Errorf("%w: %v: regs:\n%s",
×
932
                        kvm.ErrUnexpectedExitReason,
×
933
                        kvm.ExitType(m.runs[cpu].ExitReason).String(), show("", &s, &r))
×
934
        }
935
}
936

937
func (m *Machine) registerIOPortHandler(
938
        start, end uint64,
939
        inHandler, outHandler func(port uint64, bytes []byte) error,
940
) {
1✔
941
        for i := start; i < end; i++ {
2✔
942
                m.ioportHandlers[i][kvm.EXITIOIN] = inHandler
1✔
943
                m.ioportHandlers[i][kvm.EXITIOOUT] = outHandler
1✔
944
        }
1✔
945
}
946

947
func (m *Machine) initIOPortHandlers() {
1✔
948
        funcNone := func(port uint64, bytes []byte) error {
2✔
949
                return nil
1✔
950
        }
1✔
951

952
        funcError := func(port uint64, bytes []byte) error {
1✔
953
                return fmt.Errorf("%w: unexpected io port 0x%x", kvm.ErrUnexpectedExitReason, port)
×
954
        }
×
955

956
        // 0xCF9 port can get three values for three types of reset:
957
        //
958
        // Writing 4 to 0xCF9:(INIT) Will INIT the CPU. Meaning it will jump
959
        // to the initial location of booting but it will keep many CPU
960
        // elements untouched. Most internal tables, chaches etc will remain
961
        // unchanged by the Init call (but may change during it).
962
        //
963
        // Writing 6 to 0xCF9:(RESET) Will RESET the CPU with all
964
        // internal tables caches etc cleared to initial state.
965
        //
966
        // Writing 0xE to 0xCF9:(RESTART) Will power cycle the mother board
967
        // with everything that comes with it.
968
        // For now, we will exit without regard to the value. Should we wish
969
        // to have more sophisticated cf9 handling, we will need to modify
970
        // gokvm a bit more.
971
        funcOutbCF9 := func(port uint64, bytes []byte) error {
1✔
972
                if len(bytes) == 1 && bytes[0] == 0xe {
×
973
                        return fmt.Errorf("write 0xe to cf9: %w", ErrWriteToCF9)
×
974
                }
×
975

976
                return fmt.Errorf("write %#x to cf9: %w", bytes, ErrWriteToCF9)
×
977
        }
978

979
        // In ubuntu 20.04 on wsl2, the output to IO port 0x64 continued
980
        // infinitely. To deal with this issue, refer to kvmtool and
981
        // configure the input to the Status Register of the PS2 controller.
982
        //
983
        // refs:
984
        // https://github.com/kvmtool/kvmtool/blob/0e1882a49f81cb15d328ef83a78849c0ea26eecc/hw/i8042.c#L312
985
        // https://git.kernel.org/pub/scm/linux/kernel/git/will/kvmtool.git/tree/hw/i8042.c#n312
986
        // https://wiki.osdev.org/%228042%22_PS/2_Controller
987
        funcInbPS2 := func(port uint64, bytes []byte) error {
2✔
988
                bytes[0] = 0x20
1✔
989

1✔
990
                return nil
1✔
991
        }
1✔
992

993
        m.registerIOPortHandler(0, 0x10000, funcError, funcError)    // default handler
1✔
994
        m.registerIOPortHandler(0xcf9, 0xcfa, funcNone, funcOutbCF9) // CF9
1✔
995
        m.registerIOPortHandler(0x3c0, 0x3db, funcNone, funcNone)    // VGA
1✔
996
        m.registerIOPortHandler(0x3b4, 0x3b6, funcNone, funcNone)    // VGA
1✔
997
        m.registerIOPortHandler(0x2f8, 0x300, funcNone, funcNone)    // Serial port 2
1✔
998
        m.registerIOPortHandler(0x3e8, 0x3f0, funcNone, funcNone)    // Serial port 3
1✔
999
        m.registerIOPortHandler(0x2e8, 0x2f0, funcNone, funcNone)    // Serial port 4
1✔
1000
        m.registerIOPortHandler(0xcfe, 0xcff, funcNone, funcNone)    // unknown
1✔
1001
        m.registerIOPortHandler(0xcfa, 0xcfc, funcNone, funcNone)    // unknown
1✔
1002
        m.registerIOPortHandler(0xc000, 0xd000, funcNone, funcNone)  // PCI Configuration Space Access Mechanism #2
1✔
1003
        m.registerIOPortHandler(0x60, 0x70, funcInbPS2, funcNone)    // PS/2 Keyboard (Always 8042 Chip)
1✔
1004
        m.registerIOPortHandler(0xed, 0xee, funcNone, funcNone)      // 0xed is the new standard delay port.
1✔
1005

1✔
1006
        // Serial port 1
1✔
1007
        m.registerIOPortHandler(serial.COM1Addr, serial.COM1Addr+8, m.serial.In, m.serial.Out)
1✔
1008

1✔
1009
        // PCI configuration
1✔
1010
        //
1✔
1011
        // 0xcf8 for address register for PCI Config Space
1✔
1012
        // 0xcfc + 0xcff for data for PCI Config Space
1✔
1013
        // see https://github.com/torvalds/linux/blob/master/arch/x86/pci/direct.c for more detail.
1✔
1014
        m.registerIOPortHandler(0xcf8, 0xcf9, m.pci.PciConfAddrIn, m.pci.PciConfAddrOut)
1✔
1015
        m.registerIOPortHandler(0xcfc, 0xd00, m.pci.PciConfDataIn, m.pci.PciConfDataOut)
1✔
1016

1✔
1017
        // IO Devices - non PCI
1✔
1018
        for _, dev := range m.devices {
2✔
1019
                m.registerIOPortHandler(dev.IOPort(), dev.IOPort()+dev.Size(), dev.Read, dev.Write)
1✔
1020
        }
1✔
1021

1022
        // PCI devices
1023
        for _, dev := range m.pci.Devices {
2✔
1024
                m.registerIOPortHandler(dev.IOPort(), dev.IOPort()+dev.Size(), dev.Read, dev.Write)
1✔
1025
        }
1✔
1026
}
1027

1028
// InjectSerialIRQ injects a serial interrupt.
1029
func (m *Machine) InjectSerialIRQ() error {
1✔
1030
        if err := kvm.IRQLineStatus(m.vmFd, serialIRQ, 0); err != nil {
1✔
1031
                return err
×
1032
        }
×
1033

1034
        if err := kvm.IRQLineStatus(m.vmFd, serialIRQ, 1); err != nil {
1✔
1035
                return err
×
1036
        }
×
1037

1038
        return nil
1✔
1039
}
1040

1041
// InjectViortNetIRQ injects a virtio net interrupt.
1042
func (m *Machine) InjectVirtioNetIRQ() error {
1✔
1043
        if err := kvm.IRQLineStatus(m.vmFd, virtioNetIRQ, 0); err != nil {
1✔
1044
                return err
×
1045
        }
×
1046

1047
        if err := kvm.IRQLineStatus(m.vmFd, virtioNetIRQ, 1); err != nil {
1✔
1048
                return err
×
1049
        }
×
1050

1051
        return nil
1✔
1052
}
1053

1054
// InjectViortNetIRQ injects a virtio block interrupt.
1055
func (m *Machine) InjectVirtioBlkIRQ() error {
1✔
1056
        if err := kvm.IRQLineStatus(m.vmFd, virtioBlkIRQ, 0); err != nil {
1✔
1057
                return err
×
1058
        }
×
1059

1060
        if err := kvm.IRQLineStatus(m.vmFd, virtioBlkIRQ, 1); err != nil {
1✔
1061
                return err
×
1062
        }
×
1063

1064
        return nil
1✔
1065
}
1066

1067
// ReadAt implements io.ReadAt for the kvm guest pvh.
1068
func (m *Machine) ReadAt(b []byte, off int64) (int, error) {
1✔
1069
        mem := bytes.NewReader(m.mem)
1✔
1070

1✔
1071
        return mem.ReadAt(b, off)
1✔
1072
}
1✔
1073

1074
// WriteAt implements io.WriteAt for the kvm guest pvh.
1075
func (m *Machine) WriteAt(b []byte, off int64) (int, error) {
1✔
1076
        if off > int64(len(m.mem)) {
2✔
1077
                return 0, syscall.EFBIG
1✔
1078
        }
1✔
1079

1080
        n := copy(m.mem[off:], b)
1✔
1081

1✔
1082
        return n, nil
1✔
1083
}
1084

1085
func showone(indent string, in interface{}) string {
1✔
1086
        var ret string
1✔
1087

1✔
1088
        s := reflect.ValueOf(in).Elem()
1✔
1089
        typeOfT := s.Type()
1✔
1090

1✔
1091
        for i := 0; i < s.NumField(); i++ {
2✔
1092
                f := s.Field(i)
1✔
1093
                if f.Kind() == reflect.String {
1✔
1094
                        ret += fmt.Sprintf(indent+"%s %s = %s\n", typeOfT.Field(i).Name, f.Type(), f.Interface())
×
1095
                } else {
1✔
1096
                        ret += fmt.Sprintf(indent+"%s %s = %#x\n", typeOfT.Field(i).Name, f.Type(), f.Interface())
1✔
1097
                }
1✔
1098
        }
1099

1100
        return ret
1✔
1101
}
1102

1103
func show(indent string, l ...interface{}) string {
1✔
1104
        var ret string
1✔
1105
        for _, i := range l {
2✔
1106
                ret += showone(indent, i)
1✔
1107
        }
1✔
1108

1109
        return ret
1✔
1110
}
1111

1112
// CPUToFD translates a CPU number to an fd.
1113
func (m *Machine) CPUToFD(cpu int) (uintptr, error) {
1✔
1114
        if cpu > len(m.vcpuFds) {
2✔
1115
                return 0, fmt.Errorf("cpu %d out of range 0-%d:%w", cpu, len(m.vcpuFds), ErrBadCPU)
1✔
1116
        }
1✔
1117

1118
        return m.vcpuFds[cpu], nil
1✔
1119
}
1120

1121
// VtoP returns the physical address for a vCPU virtual address.
1122
func (m *Machine) VtoP(cpu int, vaddr uint64) (int64, error) {
1✔
1123
        fd, err := m.CPUToFD(cpu)
1✔
1124
        if err != nil {
2✔
1125
                return 0, err
1✔
1126
        }
1✔
1127

1128
        t := &kvm.Translation{
1✔
1129
                LinearAddress: vaddr,
1✔
1130
        }
1✔
1131
        if err := kvm.Translate(fd, t); err != nil {
1✔
1132
                return -1, err
×
1133
        }
×
1134

1135
        // There can exist a valid translation for memory that does not exist.
1136
        // For now, we call that an error.
1137
        if t.Valid == 0 || t.PhysicalAddress > uint64(len(m.mem)) {
2✔
1138
                return -1, fmt.Errorf("%#x:valid not set:%w", vaddr, ErrBadVA)
1✔
1139
        }
1✔
1140

1141
        return int64(t.PhysicalAddress), nil
1✔
1142
}
1143

1144
// GetReg gets a pointer to a register in kvm.Regs, given
1145
// a register number from reg. This used to be a comprehensive
1146
// case, but golangci-lint disliked the cyclomatic complexity
1147
// So we only show the few registers we support.
1148
func GetReg(r *kvm.Regs, reg x86asm.Reg) (*uint64, error) {
1✔
1149
        if reg == x86asm.RAX {
2✔
1150
                return &r.RAX, nil
1✔
1151
        }
1✔
1152

1153
        if reg == x86asm.RCX {
2✔
1154
                return &r.RCX, nil
1✔
1155
        }
1✔
1156

1157
        if reg == x86asm.RDX {
2✔
1158
                return &r.RDX, nil
1✔
1159
        }
1✔
1160

1161
        if reg == x86asm.RBX {
2✔
1162
                return &r.RBX, nil
1✔
1163
        }
1✔
1164

1165
        if reg == x86asm.RSP {
2✔
1166
                return &r.RSP, nil
1✔
1167
        }
1✔
1168

1169
        if reg == x86asm.RBP {
2✔
1170
                return &r.RBP, nil
1✔
1171
        }
1✔
1172

1173
        if reg == x86asm.RSI {
2✔
1174
                return &r.RSI, nil
1✔
1175
        }
1✔
1176

1177
        if reg == x86asm.RDI {
2✔
1178
                return &r.RDI, nil
1✔
1179
        }
1✔
1180

1181
        if reg == x86asm.R8 {
2✔
1182
                return &r.R8, nil
1✔
1183
        }
1✔
1184

1185
        if reg == x86asm.R9 {
2✔
1186
                return &r.R9, nil
1✔
1187
        }
1✔
1188

1189
        if reg == x86asm.R10 {
2✔
1190
                return &r.R10, nil
1✔
1191
        }
1✔
1192

1193
        if reg == x86asm.R11 {
2✔
1194
                return &r.R11, nil
1✔
1195
        }
1✔
1196

1197
        if reg == x86asm.R12 {
2✔
1198
                return &r.R12, nil
1✔
1199
        }
1✔
1200

1201
        if reg == x86asm.R13 {
2✔
1202
                return &r.R13, nil
1✔
1203
        }
1✔
1204

1205
        if reg == x86asm.R14 {
2✔
1206
                return &r.R14, nil
1✔
1207
        }
1✔
1208

1209
        if reg == x86asm.R15 {
2✔
1210
                return &r.R15, nil
1✔
1211
        }
1✔
1212

1213
        if reg == x86asm.RIP {
2✔
1214
                return &r.RIP, nil
1✔
1215
        }
1✔
1216

1217
        return nil, fmt.Errorf("register %v%w", reg, ErrUnsupported)
1✔
1218
}
1219

1220
// InitKVM takes care of the general kvm setup without dependencies to runtime target.
1221
func initVMandVCPU(
1222
        kvmPath string,
1223
        nCpus int,
1224
) (uintptr, uintptr, []uintptr, []*kvm.RunData, error) {
1✔
1225
        var err error
1✔
1226

1✔
1227
        devKVM, err := os.OpenFile(kvmPath, os.O_RDWR, 0o644)
1✔
1228
        if err != nil {
1✔
1229
                return 0, 0, nil, nil, err
×
1230
        }
×
1231

1232
        kvmFd := devKVM.Fd()
1✔
1233
        vmFd := uintptr(0)
1✔
1234
        vcpuFds := make([]uintptr, nCpus)
1✔
1235
        runs := make([]*kvm.RunData, nCpus)
1✔
1236

1✔
1237
        if vmFd, err = kvm.CreateVM(kvmFd); err != nil {
1✔
1238
                return 0, 0, nil, nil, fmt.Errorf("CreateVM: %w", err)
×
1239
        }
×
1240

1241
        if err := kvm.SetTSSAddr(vmFd, pvh.KVMTSSStart); err != nil {
1✔
1242
                return 0, 0, nil, nil, err
×
1243
        }
×
1244

1245
        if err := kvm.SetIdentityMapAddr(vmFd, pvh.KVMIdentityMapStart); err != nil {
1✔
1246
                return 0, 0, nil, nil, err
×
1247
        }
×
1248

1249
        if err := kvm.CreateIRQChip(vmFd); err != nil {
1✔
1250
                return 0, 0, nil, nil, err
×
1251
        }
×
1252

1253
        if err := kvm.CreatePIT2(vmFd); err != nil {
1✔
1254
                return 0, 0, nil, nil, err
×
1255
        }
×
1256

1257
        mmapSize, err := kvm.GetVCPUMMmapSize(kvmFd)
1✔
1258
        if err != nil {
1✔
1259
                return 0, 0, nil, nil, err
×
1260
        }
×
1261

1262
        for cpu := 0; cpu < nCpus; cpu++ {
2✔
1263
                // Create vCPU
1✔
1264
                vcpuFds[cpu], err = kvm.CreateVCPU(vmFd, cpu)
1✔
1265
                if err != nil {
1✔
1266
                        return 0, 0, nil, nil, err
×
1267
                }
×
1268

1269
                // init kvm_run structure
1270
                r, err := syscall.Mmap(int(vcpuFds[cpu]), 0, int(mmapSize),
1✔
1271
                        syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
1✔
1272
                if err != nil {
1✔
1273
                        return 0, 0, nil, nil, err
×
1274
                }
×
1275

1276
                runs[cpu] = (*kvm.RunData)(unsafe.Pointer(&r[0]))
1✔
1277
        }
1278

1279
        return kvmFd, vmFd, vcpuFds, runs, nil
1✔
1280
}
1281

1282
func (m *Machine) VCPU(stdout io.Writer, cpu, traceCount int) error {
×
1283
        trace := traceCount > 0
×
1284

×
1285
        var err error
×
1286
        // Consider ANOTHER option, maxInsCount, which would
×
1287
        // exit this loop after a certain number of instructions
×
1288
        // were run.
×
1289
        for tc := 0; ; tc++ {
×
1290
                err = m.RunInfiniteLoop(cpu)
×
1291
                if err == nil {
×
1292
                        continue
×
1293
                }
1294

1295
                if !errors.Is(err, kvm.ErrDebug) {
×
1296
                        return fmt.Errorf("CPU %d: %w", cpu, err)
×
1297
                }
×
1298

1299
                if err := m.SingleStep(trace); err != nil {
×
1300
                        fmt.Fprintf(stdout, "Setting trace to %v:%v", trace, err)
×
1301
                }
×
1302

1303
                if tc%traceCount != 0 {
×
1304
                        continue
×
1305
                }
1306

1307
                _, r, s, err := m.Inst(cpu)
×
1308
                if err != nil {
×
1309
                        fmt.Fprintf(stdout, "disassembling after debug exit:%v", err)
×
1310
                } else {
×
1311
                        fmt.Fprintf(stdout, "%#x:%s\r\n", r.RIP, s)
×
1312
                }
×
1313
        }
1314
}
1315

1316
func (m *Machine) GetSerial() *serial.Serial {
1✔
1317
        return m.serial
1✔
1318
}
1✔
1319

1320
func (m *Machine) AddDevice(dev iodev.Device) {
1✔
1321
        m.devices = append(m.devices, dev)
1✔
1322
}
1✔
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