message.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. // Copyright 2016 VMware, Inc. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package message
  15. import (
  16. "bytes"
  17. "encoding/binary"
  18. "errors"
  19. "unsafe"
  20. "github.com/vmware/vmw-guestinfo/bdoor"
  21. )
  22. const (
  23. messageTypeOpen = iota
  24. messageTypeSendSize
  25. messageTypeSendPayload
  26. messageTypeReceiveSize
  27. messageTypeReceivePayload
  28. messageTypeReceiveStatus
  29. messageTypeClose
  30. messageStatusFail = uint16(0x0000)
  31. messageStatusSuccess = uint16(0x0001)
  32. messageStatusDoRecieve = uint16(0x0002)
  33. messageStatusCheckPoint = uint16(0x0010)
  34. messageStatusHighBW = uint16(0x0080)
  35. )
  36. var (
  37. // ErrChannelOpen represents a failure to open a channel
  38. ErrChannelOpen = errors.New("could not open channel")
  39. // ErrChannelClose represents a failure to close a channel
  40. ErrChannelClose = errors.New("could not close channel")
  41. // ErrRpciSend represents a failure to send a message
  42. ErrRpciSend = errors.New("unable to send RPCI command")
  43. // ErrRpciReceive represents a failure to receive a message
  44. ErrRpciReceive = errors.New("unable to receive RPCI command result")
  45. )
  46. type Channel struct {
  47. id uint16
  48. forceLowBW bool
  49. buf []byte
  50. cookie bdoor.UInt64
  51. }
  52. // NewChannel opens a new Channel
  53. func NewChannel(proto uint32) (*Channel, error) {
  54. flags := bdoor.CommandFlagCookie
  55. retry:
  56. bp := &bdoor.BackdoorProto{}
  57. bp.BX.Low.SetWord(proto | flags)
  58. bp.CX.Low.High = messageTypeOpen
  59. bp.CX.Low.Low = bdoor.CommandMessage
  60. out := bp.InOut()
  61. if (out.CX.Low.High & messageStatusSuccess) == 0 {
  62. if flags != 0 {
  63. flags = 0
  64. goto retry
  65. }
  66. Errorf("Message: Unable to open communication channel")
  67. return nil, ErrChannelOpen
  68. }
  69. ch := &Channel{}
  70. ch.id = out.DX.Low.High
  71. ch.cookie.High.SetWord(out.SI.Low.Word())
  72. ch.cookie.Low.SetWord(out.DI.Low.Word())
  73. Debugf("Opened channel %d", ch.id)
  74. return ch, nil
  75. }
  76. func (c *Channel) Close() error {
  77. bp := &bdoor.BackdoorProto{}
  78. bp.CX.Low.High = messageTypeClose
  79. bp.CX.Low.Low = bdoor.CommandMessage
  80. bp.DX.Low.High = c.id
  81. bp.SI.Low.SetWord(c.cookie.High.Word())
  82. bp.DI.Low.SetWord(c.cookie.Low.Word())
  83. out := bp.InOut()
  84. if (out.CX.Low.High & messageStatusSuccess) == 0 {
  85. Errorf("Message: Unable to close communication channel %d", c.id)
  86. return ErrChannelClose
  87. }
  88. Debugf("Closed channel %d", c.id)
  89. return nil
  90. }
  91. func (c *Channel) Send(buf []byte) error {
  92. retry:
  93. bp := &bdoor.BackdoorProto{}
  94. bp.CX.Low.High = messageTypeSendSize
  95. bp.CX.Low.Low = bdoor.CommandMessage
  96. bp.DX.Low.High = c.id
  97. bp.SI.Low.SetWord(c.cookie.High.Word())
  98. bp.DI.Low.SetWord(c.cookie.Low.Word())
  99. bp.BX.Low.SetWord(uint32(len(buf)))
  100. // send the size
  101. out := bp.InOut()
  102. if (out.CX.Low.High & messageStatusSuccess) == 0 {
  103. Errorf("Message: Unable to send a message over the communication channel %d", c.id)
  104. return ErrRpciSend
  105. }
  106. // size of buf 0 is fine, just return
  107. if len(buf) == 0 {
  108. return nil
  109. }
  110. if !c.forceLowBW && (out.CX.Low.High&messageStatusHighBW) == messageStatusHighBW {
  111. hbbp := &bdoor.BackdoorProto{}
  112. hbbp.BX.Low.Low = bdoor.CommandHighBWMessage
  113. hbbp.BX.Low.High = messageStatusSuccess
  114. hbbp.DX.Low.High = c.id
  115. hbbp.BP.Low.SetWord(c.cookie.High.Word())
  116. hbbp.DI.Low.SetWord(c.cookie.Low.Word())
  117. hbbp.CX.Low.SetWord(uint32(len(buf)))
  118. hbbp.SI.SetQuad(uint64(uintptr(unsafe.Pointer(&buf[0]))))
  119. out := hbbp.HighBandwidthOut()
  120. if (out.BX.Low.High & messageStatusSuccess) == 0 {
  121. if (out.BX.Low.High & messageStatusCheckPoint) != 0 {
  122. Debugf("A checkpoint occurred. Retrying the operation")
  123. goto retry
  124. }
  125. Errorf("Message: Unable to send a message over the communication channel %d", c.id)
  126. return ErrRpciSend
  127. }
  128. } else {
  129. bp.CX.Low.High = messageTypeSendPayload
  130. bbuf := bytes.NewBuffer(buf)
  131. for {
  132. // read 4 bytes at a time
  133. words := bbuf.Next(4)
  134. if len(words) == 0 {
  135. break
  136. }
  137. Debugf("sending %q over %d", string(words), c.id)
  138. switch len(words) {
  139. case 3:
  140. bp.BX.Low.SetWord(binary.LittleEndian.Uint32([]byte{0x0, words[2], words[1], words[0]}))
  141. case 2:
  142. bp.BX.Low.SetWord(uint32(binary.LittleEndian.Uint16(words)))
  143. case 1:
  144. bp.BX.Low.SetWord(uint32(words[0]))
  145. default:
  146. bp.BX.Low.SetWord(binary.LittleEndian.Uint32(words))
  147. }
  148. out = bp.InOut()
  149. if (out.CX.Low.High & messageStatusSuccess) == 0 {
  150. Errorf("Message: Unable to send a message over the communication channel %d", c.id)
  151. return ErrRpciSend
  152. }
  153. }
  154. }
  155. return nil
  156. }
  157. func (c *Channel) Receive() ([]byte, error) {
  158. retry:
  159. var err error
  160. bp := &bdoor.BackdoorProto{}
  161. bp.CX.Low.High = messageTypeReceiveSize
  162. bp.CX.Low.Low = bdoor.CommandMessage
  163. bp.DX.Low.High = c.id
  164. bp.SI.Low.SetWord(c.cookie.High.Word())
  165. bp.DI.Low.SetWord(c.cookie.Low.Word())
  166. out := bp.InOut()
  167. if (out.CX.Low.High & messageStatusSuccess) == 0 {
  168. Errorf("Message: Unable to poll for messages over the communication channel %d", c.id)
  169. return nil, ErrRpciReceive
  170. }
  171. if (out.CX.Low.High & messageStatusDoRecieve) == 0 {
  172. Debugf("No message to retrieve")
  173. return nil, nil
  174. }
  175. // Receive the size.
  176. if out.DX.Low.High != messageTypeSendSize {
  177. Errorf("Message: Protocol error. Expected a MESSAGE_TYPE_SENDSIZE request from vmware")
  178. return nil, ErrRpciReceive
  179. }
  180. size := out.BX.Quad()
  181. var buf []byte
  182. if size != 0 {
  183. if !c.forceLowBW && (out.CX.Low.High&messageStatusHighBW == messageStatusHighBW) {
  184. buf = make([]byte, size)
  185. hbbp := &bdoor.BackdoorProto{}
  186. hbbp.BX.Low.Low = bdoor.CommandHighBWMessage
  187. hbbp.BX.Low.High = messageStatusSuccess
  188. hbbp.DX.Low.High = c.id
  189. hbbp.SI.Low.SetWord(c.cookie.High.Word())
  190. hbbp.BP.Low.SetWord(c.cookie.Low.Word())
  191. hbbp.CX.Low.SetWord(uint32(len(buf)))
  192. hbbp.DI.SetQuad(uint64(uintptr(unsafe.Pointer(&buf[0]))))
  193. out := hbbp.HighBandwidthIn()
  194. if (out.BX.Low.High & messageStatusSuccess) == 0 {
  195. Errorf("Message: Unable to send a message over the communication channel %d", c.id)
  196. c.reply(messageTypeReceivePayload, messageStatusFail)
  197. return nil, ErrRpciReceive
  198. }
  199. } else {
  200. b := bytes.NewBuffer(make([]byte, 0, size))
  201. for {
  202. if size == 0 {
  203. break
  204. }
  205. bp.CX.Low.High = messageTypeReceivePayload
  206. bp.BX.Low.Low = messageStatusSuccess
  207. out = bp.InOut()
  208. if (out.CX.Low.High & messageStatusSuccess) == 0 {
  209. if (out.CX.Low.High & messageStatusCheckPoint) != 0 {
  210. Debugf("A checkpoint occurred. Retrying the operation")
  211. goto retry
  212. }
  213. Errorf("Message: Unable to receive a message over the communication channel %d", c.id)
  214. c.reply(messageTypeReceivePayload, messageStatusFail)
  215. return nil, ErrRpciReceive
  216. }
  217. if out.DX.Low.High != messageTypeSendPayload {
  218. Errorf("Message: Protocol error. Expected a MESSAGE_TYPE_SENDPAYLOAD from vmware")
  219. c.reply(messageTypeReceivePayload, messageStatusFail)
  220. return nil, ErrRpciReceive
  221. }
  222. Debugf("Received %#v", out.BX.Low.Word())
  223. switch size {
  224. case 1:
  225. err = binary.Write(b, binary.LittleEndian, uint8(out.BX.Low.Low))
  226. size = size - 1
  227. case 2:
  228. err = binary.Write(b, binary.LittleEndian, uint16(out.BX.Low.Low))
  229. size = size - 2
  230. case 3:
  231. err = binary.Write(b, binary.LittleEndian, uint16(out.BX.Low.Low))
  232. if err != nil {
  233. c.reply(messageTypeReceivePayload, messageStatusFail)
  234. return nil, ErrRpciReceive
  235. }
  236. err = binary.Write(b, binary.LittleEndian, uint8(out.BX.Low.High))
  237. size = size - 3
  238. default:
  239. err = binary.Write(b, binary.LittleEndian, out.BX.Low.Word())
  240. size = size - 4
  241. }
  242. if err != nil {
  243. Errorf(err.Error())
  244. c.reply(messageTypeReceivePayload, messageStatusFail)
  245. return nil, ErrRpciReceive
  246. }
  247. }
  248. buf = b.Bytes()
  249. }
  250. }
  251. c.reply(messageTypeReceiveStatus, messageStatusSuccess)
  252. return buf, nil
  253. }
  254. func (c *Channel) reply(messageType, messageStatus uint16) {
  255. bp := &bdoor.BackdoorProto{}
  256. bp.BX.Low.Low = messageStatus
  257. bp.CX.Low.High = messageType
  258. bp.CX.Low.Low = bdoor.CommandMessage
  259. bp.DX.Low.High = c.id
  260. bp.SI.Low.SetWord(c.cookie.High.Word())
  261. bp.DI.Low.SetWord(c.cookie.Low.Word())
  262. out := bp.InOut()
  263. /* OUT: Status */
  264. if (out.CX.Low.High & messageStatusSuccess) == 0 {
  265. if messageStatus == messageStatusSuccess {
  266. Errorf("reply Message: Unable to send a message over the communication channel %d", c.id)
  267. } else {
  268. Errorf("reply Message: Unable to signal an error of reception over the communication channel %d", c.id)
  269. }
  270. }
  271. }