github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/vm/qemu/qmp.go (about) 1 // Copyright 2020 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 package qemu 5 6 import ( 7 "encoding/json" 8 "errors" 9 "fmt" 10 "net" 11 "strings" 12 13 "github.com/google/syzkaller/pkg/log" 14 ) 15 16 type qmpVersion struct { 17 Package string 18 QEMU struct { 19 Major int 20 Micro int 21 Minor int 22 } 23 } 24 25 type qmpBanner struct { 26 QMP struct { 27 Version qmpVersion 28 } 29 } 30 31 type qmpCommand struct { 32 Execute string `json:"execute"` 33 Arguments interface{} `json:"arguments,omitempty"` 34 } 35 36 type hmpCommand struct { 37 Command string `json:"command-line"` 38 CPU int `json:"cpu-index"` 39 } 40 41 type qmpResponse struct { 42 Error struct { 43 Class string 44 Desc string 45 } 46 Return interface{} 47 48 Event string 49 Data map[string]interface{} 50 Timestamp struct { 51 Seconds int64 52 Microseconds int64 53 } 54 } 55 56 func (inst *instance) qmpConnCheck() error { 57 if inst.mon != nil { 58 return nil 59 } 60 61 addr := fmt.Sprintf("127.0.0.1:%v", inst.monport) 62 conn, err := net.Dial("tcp", addr) 63 if err != nil { 64 return err 65 } 66 67 monDec := json.NewDecoder(conn) 68 monEnc := json.NewEncoder(conn) 69 70 var banner qmpBanner 71 if err := monDec.Decode(&banner); err != nil { 72 return err 73 } 74 75 inst.monEnc = monEnc 76 inst.monDec = monDec 77 if _, err := inst.doQmp(&qmpCommand{Execute: "qmp_capabilities"}); err != nil { 78 inst.monEnc = nil 79 inst.monDec = nil 80 return err 81 } 82 inst.mon = conn 83 84 return nil 85 } 86 87 func (inst *instance) qmpRecv() (*qmpResponse, error) { 88 for { 89 qmp := new(qmpResponse) 90 err := inst.monDec.Decode(qmp) 91 if err != nil || qmp.Event == "" { 92 return qmp, err 93 } 94 log.Logf(3, "event: %v", qmp) 95 } 96 } 97 98 func (inst *instance) doQmp(cmd *qmpCommand) (*qmpResponse, error) { 99 if err := inst.monEnc.Encode(cmd); err != nil { 100 return nil, err 101 } 102 return inst.qmpRecv() 103 } 104 105 func (inst *instance) qmp(cmd *qmpCommand) (interface{}, error) { 106 if err := inst.qmpConnCheck(); err != nil { 107 return nil, err 108 } 109 resp, err := inst.doQmp(cmd) 110 if err != nil { 111 return nil, err 112 } 113 if resp.Error.Desc != "" { 114 return nil, fmt.Errorf("%v", resp.Error) 115 } 116 if resp.Return == nil { 117 return nil, fmt.Errorf(`no "return" nor "error" in [%v]`, resp) 118 } 119 if output, _ := resp.Return.(string); strings.HasPrefix(output, "Error:") || 120 strings.HasPrefix(output, "unknown command:") { 121 return nil, errors.New(output) 122 } 123 return resp.Return, nil 124 } 125 126 func (inst *instance) hmp(cmd string, cpu int) (string, error) { 127 if inst.debug { 128 log.Logf(0, "qemu: running hmp command: %v", cmd) 129 } 130 req := &qmpCommand{ 131 Execute: "human-monitor-command", 132 Arguments: &hmpCommand{ 133 Command: cmd, 134 CPU: cpu, 135 }, 136 } 137 resp, err := inst.qmp(req) 138 if inst.debug { 139 log.Logf(0, "qemu: reply: %v\n%v", err, resp) 140 } 141 if err != nil { 142 return "", fmt.Errorf("qemu hmp command '%s': %w", cmd, err) 143 } 144 return resp.(string), nil 145 }