123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335 |
- // Copyright 2016-2017 VMware, Inc. All Rights Reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package message
- import (
- "bytes"
- "encoding/binary"
- "errors"
- "unsafe"
- "github.com/vmware/vmw-guestinfo/bdoor"
- )
- const (
- messageTypeOpen = iota
- messageTypeSendSize
- messageTypeSendPayload
- messageTypeReceiveSize
- messageTypeReceivePayload
- messageTypeReceiveStatus
- messageTypeClose
- messageStatusFail = uint16(0x0000)
- messageStatusSuccess = uint16(0x0001)
- messageStatusDoRecieve = uint16(0x0002)
- messageStatusCheckPoint = uint16(0x0010)
- messageStatusHighBW = uint16(0x0080)
- )
- var (
- // ErrChannelOpen represents a failure to open a channel
- ErrChannelOpen = errors.New("could not open channel")
- // ErrChannelClose represents a failure to close a channel
- ErrChannelClose = errors.New("could not close channel")
- // ErrRpciSend represents a failure to send a message
- ErrRpciSend = errors.New("unable to send RPCI command")
- // ErrRpciReceive represents a failure to receive a message
- ErrRpciReceive = errors.New("unable to receive RPCI command result")
- )
- type Channel struct {
- id uint16
- forceLowBW bool
- buf []byte
- cookie bdoor.UInt64
- }
- // NewChannel opens a new Channel
- func NewChannel(proto uint32) (*Channel, error) {
- flags := bdoor.CommandFlagCookie
- retry:
- bp := &bdoor.BackdoorProto{}
- bp.BX.AsUInt32().SetWord(proto | flags)
- bp.CX.AsUInt32().High = messageTypeOpen
- bp.CX.AsUInt32().Low = bdoor.CommandMessage
- out := bp.InOut()
- if (out.CX.AsUInt32().High & messageStatusSuccess) == 0 {
- if flags != 0 {
- flags = 0
- goto retry
- }
- Errorf("Message: Unable to open communication channel")
- return nil, ErrChannelOpen
- }
- ch := &Channel{}
- ch.id = out.DX.AsUInt32().High
- ch.cookie.High.SetWord(out.SI.AsUInt32().Word())
- ch.cookie.Low.SetWord(out.DI.AsUInt32().Word())
- Debugf("Opened channel %d", ch.id)
- return ch, nil
- }
- func (c *Channel) Close() error {
- bp := &bdoor.BackdoorProto{}
- bp.CX.AsUInt32().High = messageTypeClose
- bp.CX.AsUInt32().Low = bdoor.CommandMessage
- bp.DX.AsUInt32().High = c.id
- bp.SI.AsUInt32().SetWord(c.cookie.High.Word())
- bp.DI.AsUInt32().SetWord(c.cookie.Low.Word())
- out := bp.InOut()
- if (out.CX.AsUInt32().High & messageStatusSuccess) == 0 {
- Errorf("Message: Unable to close communication channel %d", c.id)
- return ErrChannelClose
- }
- Debugf("Closed channel %d", c.id)
- return nil
- }
- func (c *Channel) Send(buf []byte) error {
- retry:
- bp := &bdoor.BackdoorProto{}
- bp.CX.AsUInt32().High = messageTypeSendSize
- bp.CX.AsUInt32().Low = bdoor.CommandMessage
- bp.DX.AsUInt32().High = c.id
- bp.SI.AsUInt32().SetWord(c.cookie.High.Word())
- bp.DI.AsUInt32().SetWord(c.cookie.Low.Word())
- bp.BX.AsUInt32().SetWord(uint32(len(buf)))
- // send the size
- out := bp.InOut()
- if (out.CX.AsUInt32().High & messageStatusSuccess) == 0 {
- Errorf("Message: Unable to send a message over the communication channel %d", c.id)
- return ErrRpciSend
- }
- // size of buf 0 is fine, just return
- if len(buf) == 0 {
- return nil
- }
- if !c.forceLowBW && (out.CX.AsUInt32().High&messageStatusHighBW) == messageStatusHighBW {
- hbbp := &bdoor.BackdoorProto{}
- hbbp.BX.AsUInt32().Low = bdoor.CommandHighBWMessage
- hbbp.BX.AsUInt32().High = messageStatusSuccess
- hbbp.DX.AsUInt32().High = c.id
- hbbp.BP.AsUInt32().SetWord(c.cookie.High.Word())
- hbbp.DI.AsUInt32().SetWord(c.cookie.Low.Word())
- hbbp.CX.AsUInt32().SetWord(uint32(len(buf)))
- hbbp.SI.SetPointer(unsafe.Pointer(&buf[0]))
- out := hbbp.HighBandwidthOut()
- if (out.BX.AsUInt32().High & messageStatusSuccess) == 0 {
- if (out.BX.AsUInt32().High & messageStatusCheckPoint) != 0 {
- Debugf("A checkpoint occurred. Retrying the operation")
- goto retry
- }
- Errorf("Message: Unable to send a message over the communication channel %d", c.id)
- return ErrRpciSend
- }
- } else {
- bp.CX.AsUInt32().High = messageTypeSendPayload
- bbuf := bytes.NewBuffer(buf)
- for {
- // read 4 bytes at a time
- words := bbuf.Next(4)
- if len(words) == 0 {
- break
- }
- Debugf("sending %q over %d", string(words), c.id)
- switch len(words) {
- case 3:
- bp.BX.AsUInt32().SetWord(binary.LittleEndian.Uint32([]byte{0x0, words[2], words[1], words[0]}))
- case 2:
- bp.BX.AsUInt32().SetWord(uint32(binary.LittleEndian.Uint16(words)))
- case 1:
- bp.BX.AsUInt32().SetWord(uint32(words[0]))
- default:
- bp.BX.AsUInt32().SetWord(binary.LittleEndian.Uint32(words))
- }
- out = bp.InOut()
- if (out.CX.AsUInt32().High & messageStatusSuccess) == 0 {
- Errorf("Message: Unable to send a message over the communication channel %d", c.id)
- return ErrRpciSend
- }
- }
- }
- return nil
- }
- func (c *Channel) Receive() ([]byte, error) {
- retry:
- var err error
- bp := &bdoor.BackdoorProto{}
- bp.CX.AsUInt32().High = messageTypeReceiveSize
- bp.CX.AsUInt32().Low = bdoor.CommandMessage
- bp.DX.AsUInt32().High = c.id
- bp.SI.AsUInt32().SetWord(c.cookie.High.Word())
- bp.DI.AsUInt32().SetWord(c.cookie.Low.Word())
- out := bp.InOut()
- if (out.CX.AsUInt32().High & messageStatusSuccess) == 0 {
- Errorf("Message: Unable to poll for messages over the communication channel %d", c.id)
- return nil, ErrRpciReceive
- }
- if (out.CX.AsUInt32().High & messageStatusDoRecieve) == 0 {
- Debugf("No message to retrieve")
- return nil, nil
- }
- // Receive the size.
- if out.DX.AsUInt32().High != messageTypeSendSize {
- Errorf("Message: Protocol error. Expected a MESSAGE_TYPE_SENDSIZE request from vmware")
- return nil, ErrRpciReceive
- }
- size := out.BX.Value()
- var buf []byte
- if size != 0 {
- if !c.forceLowBW && (out.CX.AsUInt32().High&messageStatusHighBW == messageStatusHighBW) {
- buf = make([]byte, size)
- hbbp := &bdoor.BackdoorProto{}
- hbbp.BX.AsUInt32().Low = bdoor.CommandHighBWMessage
- hbbp.BX.AsUInt32().High = messageStatusSuccess
- hbbp.DX.AsUInt32().High = c.id
- hbbp.SI.AsUInt32().SetWord(c.cookie.High.Word())
- hbbp.BP.AsUInt32().SetWord(c.cookie.Low.Word())
- hbbp.CX.AsUInt32().SetWord(uint32(len(buf)))
- hbbp.DI.SetPointer(unsafe.Pointer(&buf[0]))
- out := hbbp.HighBandwidthIn()
- if (out.BX.AsUInt32().High & messageStatusSuccess) == 0 {
- Errorf("Message: Unable to send a message over the communication channel %d", c.id)
- c.reply(messageTypeReceivePayload, messageStatusFail)
- return nil, ErrRpciReceive
- }
- } else {
- b := bytes.NewBuffer(make([]byte, 0, size))
- for {
- if size == 0 {
- break
- }
- bp.CX.AsUInt32().High = messageTypeReceivePayload
- bp.BX.AsUInt32().Low = messageStatusSuccess
- out = bp.InOut()
- if (out.CX.AsUInt32().High & messageStatusSuccess) == 0 {
- if (out.CX.AsUInt32().High & messageStatusCheckPoint) != 0 {
- Debugf("A checkpoint occurred. Retrying the operation")
- goto retry
- }
- Errorf("Message: Unable to receive a message over the communication channel %d", c.id)
- c.reply(messageTypeReceivePayload, messageStatusFail)
- return nil, ErrRpciReceive
- }
- if out.DX.AsUInt32().High != messageTypeSendPayload {
- Errorf("Message: Protocol error. Expected a MESSAGE_TYPE_SENDPAYLOAD from vmware")
- c.reply(messageTypeReceivePayload, messageStatusFail)
- return nil, ErrRpciReceive
- }
- Debugf("Received %#v", out.BX.AsUInt32().Word())
- switch size {
- case 1:
- err = binary.Write(b, binary.LittleEndian, uint8(out.BX.AsUInt32().Low))
- size = size - 1
- case 2:
- err = binary.Write(b, binary.LittleEndian, uint16(out.BX.AsUInt32().Low))
- size = size - 2
- case 3:
- err = binary.Write(b, binary.LittleEndian, uint16(out.BX.AsUInt32().Low))
- if err != nil {
- c.reply(messageTypeReceivePayload, messageStatusFail)
- return nil, ErrRpciReceive
- }
- err = binary.Write(b, binary.LittleEndian, uint8(out.BX.AsUInt32().High))
- size = size - 3
- default:
- err = binary.Write(b, binary.LittleEndian, out.BX.AsUInt32().Word())
- size = size - 4
- }
- if err != nil {
- Errorf(err.Error())
- c.reply(messageTypeReceivePayload, messageStatusFail)
- return nil, ErrRpciReceive
- }
- }
- buf = b.Bytes()
- }
- }
- c.reply(messageTypeReceiveStatus, messageStatusSuccess)
- return buf, nil
- }
- func (c *Channel) reply(messageType, messageStatus uint16) {
- bp := &bdoor.BackdoorProto{}
- bp.BX.AsUInt32().Low = messageStatus
- bp.CX.AsUInt32().High = messageType
- bp.CX.AsUInt32().Low = bdoor.CommandMessage
- bp.DX.AsUInt32().High = c.id
- bp.SI.AsUInt32().SetWord(c.cookie.High.Word())
- bp.DI.AsUInt32().SetWord(c.cookie.Low.Word())
- out := bp.InOut()
- /* OUT: Status */
- if (out.CX.AsUInt32().High & messageStatusSuccess) == 0 {
- if messageStatus == messageStatusSuccess {
- Errorf("reply Message: Unable to send a message over the communication channel %d", c.id)
- } else {
- Errorf("reply Message: Unable to signal an error of reception over the communication channel %d", c.id)
- }
- }
- }
|