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  }