ipmi_bmc_extern.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. /*
  2. * IPMI BMC external connection
  3. *
  4. * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to deal
  8. * in the Software without restriction, including without limitation the rights
  9. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in
  14. * all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. * THE SOFTWARE.
  23. */
  24. /*
  25. * This is designed to connect with OpenIPMI's lanserv serial interface
  26. * using the "VM" connection type. See that for details.
  27. */
  28. #include "qemu/osdep.h"
  29. #include "qapi/error.h"
  30. #include "qemu/timer.h"
  31. #include "chardev/char-fe.h"
  32. #include "sysemu/sysemu.h"
  33. #include "hw/ipmi/ipmi.h"
  34. #define VM_MSG_CHAR 0xA0 /* Marks end of message */
  35. #define VM_CMD_CHAR 0xA1 /* Marks end of a command */
  36. #define VM_ESCAPE_CHAR 0xAA /* Set bit 4 from the next byte to 0 */
  37. #define VM_PROTOCOL_VERSION 1
  38. #define VM_CMD_VERSION 0xff /* A version number byte follows */
  39. #define VM_CMD_NOATTN 0x00
  40. #define VM_CMD_ATTN 0x01
  41. #define VM_CMD_ATTN_IRQ 0x02
  42. #define VM_CMD_POWEROFF 0x03
  43. #define VM_CMD_RESET 0x04
  44. #define VM_CMD_ENABLE_IRQ 0x05 /* Enable/disable the messaging irq */
  45. #define VM_CMD_DISABLE_IRQ 0x06
  46. #define VM_CMD_SEND_NMI 0x07
  47. #define VM_CMD_CAPABILITIES 0x08
  48. #define VM_CAPABILITIES_POWER 0x01
  49. #define VM_CAPABILITIES_RESET 0x02
  50. #define VM_CAPABILITIES_IRQ 0x04
  51. #define VM_CAPABILITIES_NMI 0x08
  52. #define VM_CAPABILITIES_ATTN 0x10
  53. #define VM_CAPABILITIES_GRACEFUL_SHUTDOWN 0x20
  54. #define VM_CMD_GRACEFUL_SHUTDOWN 0x09
  55. #define TYPE_IPMI_BMC_EXTERN "ipmi-bmc-extern"
  56. #define IPMI_BMC_EXTERN(obj) OBJECT_CHECK(IPMIBmcExtern, (obj), \
  57. TYPE_IPMI_BMC_EXTERN)
  58. typedef struct IPMIBmcExtern {
  59. IPMIBmc parent;
  60. CharBackend chr;
  61. bool connected;
  62. unsigned char inbuf[MAX_IPMI_MSG_SIZE + 2];
  63. unsigned int inpos;
  64. bool in_escape;
  65. bool in_too_many;
  66. bool waiting_rsp;
  67. bool sending_cmd;
  68. unsigned char outbuf[(MAX_IPMI_MSG_SIZE + 2) * 2 + 1];
  69. unsigned int outpos;
  70. unsigned int outlen;
  71. struct QEMUTimer *extern_timer;
  72. /* A reset event is pending to be sent upstream. */
  73. bool send_reset;
  74. } IPMIBmcExtern;
  75. static int can_receive(void *opaque);
  76. static void receive(void *opaque, const uint8_t *buf, int size);
  77. static void chr_event(void *opaque, int event);
  78. static unsigned char
  79. ipmb_checksum(const unsigned char *data, int size, unsigned char start)
  80. {
  81. unsigned char csum = start;
  82. for (; size > 0; size--, data++) {
  83. csum += *data;
  84. }
  85. return csum;
  86. }
  87. static void continue_send(IPMIBmcExtern *ibe)
  88. {
  89. int ret;
  90. if (ibe->outlen == 0) {
  91. goto check_reset;
  92. }
  93. send:
  94. ret = qemu_chr_fe_write(&ibe->chr, ibe->outbuf + ibe->outpos,
  95. ibe->outlen - ibe->outpos);
  96. if (ret > 0) {
  97. ibe->outpos += ret;
  98. }
  99. if (ibe->outpos < ibe->outlen) {
  100. /* Not fully transmitted, try again in a 10ms */
  101. timer_mod_ns(ibe->extern_timer,
  102. qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 10000000);
  103. } else {
  104. /* Sent */
  105. ibe->outlen = 0;
  106. ibe->outpos = 0;
  107. if (!ibe->sending_cmd) {
  108. ibe->waiting_rsp = true;
  109. } else {
  110. ibe->sending_cmd = false;
  111. }
  112. check_reset:
  113. if (ibe->connected && ibe->send_reset) {
  114. /* Send the reset */
  115. ibe->outbuf[0] = VM_CMD_RESET;
  116. ibe->outbuf[1] = VM_CMD_CHAR;
  117. ibe->outlen = 2;
  118. ibe->outpos = 0;
  119. ibe->send_reset = false;
  120. ibe->sending_cmd = true;
  121. goto send;
  122. }
  123. if (ibe->waiting_rsp) {
  124. /* Make sure we get a response within 4 seconds. */
  125. timer_mod_ns(ibe->extern_timer,
  126. qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 4000000000ULL);
  127. }
  128. }
  129. return;
  130. }
  131. static void extern_timeout(void *opaque)
  132. {
  133. IPMIBmcExtern *ibe = opaque;
  134. IPMIInterface *s = ibe->parent.intf;
  135. if (ibe->connected) {
  136. if (ibe->waiting_rsp && (ibe->outlen == 0)) {
  137. IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
  138. /* The message response timed out, return an error. */
  139. ibe->waiting_rsp = false;
  140. ibe->inbuf[1] = ibe->outbuf[1] | 0x04;
  141. ibe->inbuf[2] = ibe->outbuf[2];
  142. ibe->inbuf[3] = IPMI_CC_TIMEOUT;
  143. k->handle_rsp(s, ibe->outbuf[0], ibe->inbuf + 1, 3);
  144. } else {
  145. continue_send(ibe);
  146. }
  147. }
  148. }
  149. static void addchar(IPMIBmcExtern *ibe, unsigned char ch)
  150. {
  151. switch (ch) {
  152. case VM_MSG_CHAR:
  153. case VM_CMD_CHAR:
  154. case VM_ESCAPE_CHAR:
  155. ibe->outbuf[ibe->outlen] = VM_ESCAPE_CHAR;
  156. ibe->outlen++;
  157. ch |= 0x10;
  158. /* No break */
  159. default:
  160. ibe->outbuf[ibe->outlen] = ch;
  161. ibe->outlen++;
  162. }
  163. }
  164. static void ipmi_bmc_extern_handle_command(IPMIBmc *b,
  165. uint8_t *cmd, unsigned int cmd_len,
  166. unsigned int max_cmd_len,
  167. uint8_t msg_id)
  168. {
  169. IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(b);
  170. IPMIInterface *s = ibe->parent.intf;
  171. uint8_t err = 0, csum;
  172. unsigned int i;
  173. if (ibe->outlen) {
  174. /* We already have a command queued. Shouldn't ever happen. */
  175. fprintf(stderr, "IPMI KCS: Got command when not finished with the"
  176. " previous command\n");
  177. abort();
  178. }
  179. /* If it's too short or it was truncated, return an error. */
  180. if (cmd_len < 2) {
  181. err = IPMI_CC_REQUEST_DATA_LENGTH_INVALID;
  182. } else if ((cmd_len > max_cmd_len) || (cmd_len > MAX_IPMI_MSG_SIZE)) {
  183. err = IPMI_CC_REQUEST_DATA_TRUNCATED;
  184. } else if (!ibe->connected) {
  185. err = IPMI_CC_BMC_INIT_IN_PROGRESS;
  186. }
  187. if (err) {
  188. IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
  189. unsigned char rsp[3];
  190. rsp[0] = cmd[0] | 0x04;
  191. rsp[1] = cmd[1];
  192. rsp[2] = err;
  193. ibe->waiting_rsp = false;
  194. k->handle_rsp(s, msg_id, rsp, 3);
  195. goto out;
  196. }
  197. addchar(ibe, msg_id);
  198. for (i = 0; i < cmd_len; i++) {
  199. addchar(ibe, cmd[i]);
  200. }
  201. csum = ipmb_checksum(&msg_id, 1, 0);
  202. addchar(ibe, -ipmb_checksum(cmd, cmd_len, csum));
  203. ibe->outbuf[ibe->outlen] = VM_MSG_CHAR;
  204. ibe->outlen++;
  205. /* Start the transmit */
  206. continue_send(ibe);
  207. out:
  208. return;
  209. }
  210. static void handle_hw_op(IPMIBmcExtern *ibe, unsigned char hw_op)
  211. {
  212. IPMIInterface *s = ibe->parent.intf;
  213. IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
  214. switch (hw_op) {
  215. case VM_CMD_VERSION:
  216. /* We only support one version at this time. */
  217. break;
  218. case VM_CMD_NOATTN:
  219. k->set_atn(s, 0, 0);
  220. break;
  221. case VM_CMD_ATTN:
  222. k->set_atn(s, 1, 0);
  223. break;
  224. case VM_CMD_ATTN_IRQ:
  225. k->set_atn(s, 1, 1);
  226. break;
  227. case VM_CMD_POWEROFF:
  228. k->do_hw_op(s, IPMI_POWEROFF_CHASSIS, 0);
  229. break;
  230. case VM_CMD_RESET:
  231. k->do_hw_op(s, IPMI_RESET_CHASSIS, 0);
  232. break;
  233. case VM_CMD_ENABLE_IRQ:
  234. k->set_irq_enable(s, 1);
  235. break;
  236. case VM_CMD_DISABLE_IRQ:
  237. k->set_irq_enable(s, 0);
  238. break;
  239. case VM_CMD_SEND_NMI:
  240. k->do_hw_op(s, IPMI_SEND_NMI, 0);
  241. break;
  242. case VM_CMD_GRACEFUL_SHUTDOWN:
  243. k->do_hw_op(s, IPMI_SHUTDOWN_VIA_ACPI_OVERTEMP, 0);
  244. break;
  245. }
  246. }
  247. static void handle_msg(IPMIBmcExtern *ibe)
  248. {
  249. IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(ibe->parent.intf);
  250. if (ibe->in_escape) {
  251. ipmi_debug("msg escape not ended\n");
  252. return;
  253. }
  254. if (ibe->inpos < 5) {
  255. ipmi_debug("msg too short\n");
  256. return;
  257. }
  258. if (ibe->in_too_many) {
  259. ibe->inbuf[3] = IPMI_CC_REQUEST_DATA_TRUNCATED;
  260. ibe->inpos = 4;
  261. } else if (ipmb_checksum(ibe->inbuf, ibe->inpos, 0) != 0) {
  262. ipmi_debug("msg checksum failure\n");
  263. return;
  264. } else {
  265. ibe->inpos--; /* Remove checkum */
  266. }
  267. timer_del(ibe->extern_timer);
  268. ibe->waiting_rsp = false;
  269. k->handle_rsp(ibe->parent.intf, ibe->inbuf[0], ibe->inbuf + 1, ibe->inpos - 1);
  270. }
  271. static int can_receive(void *opaque)
  272. {
  273. return 1;
  274. }
  275. static void receive(void *opaque, const uint8_t *buf, int size)
  276. {
  277. IPMIBmcExtern *ibe = opaque;
  278. int i;
  279. unsigned char hw_op;
  280. for (i = 0; i < size; i++) {
  281. unsigned char ch = buf[i];
  282. switch (ch) {
  283. case VM_MSG_CHAR:
  284. handle_msg(ibe);
  285. ibe->in_too_many = false;
  286. ibe->inpos = 0;
  287. break;
  288. case VM_CMD_CHAR:
  289. if (ibe->in_too_many) {
  290. ipmi_debug("cmd in too many\n");
  291. ibe->in_too_many = false;
  292. ibe->inpos = 0;
  293. break;
  294. }
  295. if (ibe->in_escape) {
  296. ipmi_debug("cmd in escape\n");
  297. ibe->in_too_many = false;
  298. ibe->inpos = 0;
  299. ibe->in_escape = false;
  300. break;
  301. }
  302. ibe->in_too_many = false;
  303. if (ibe->inpos < 1) {
  304. break;
  305. }
  306. hw_op = ibe->inbuf[0];
  307. ibe->inpos = 0;
  308. goto out_hw_op;
  309. break;
  310. case VM_ESCAPE_CHAR:
  311. ibe->in_escape = true;
  312. break;
  313. default:
  314. if (ibe->in_escape) {
  315. ch &= ~0x10;
  316. ibe->in_escape = false;
  317. }
  318. if (ibe->in_too_many) {
  319. break;
  320. }
  321. if (ibe->inpos >= sizeof(ibe->inbuf)) {
  322. ibe->in_too_many = true;
  323. break;
  324. }
  325. ibe->inbuf[ibe->inpos] = ch;
  326. ibe->inpos++;
  327. break;
  328. }
  329. }
  330. return;
  331. out_hw_op:
  332. handle_hw_op(ibe, hw_op);
  333. }
  334. static void chr_event(void *opaque, int event)
  335. {
  336. IPMIBmcExtern *ibe = opaque;
  337. IPMIInterface *s = ibe->parent.intf;
  338. IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s);
  339. unsigned char v;
  340. switch (event) {
  341. case CHR_EVENT_OPENED:
  342. ibe->connected = true;
  343. ibe->outpos = 0;
  344. ibe->outlen = 0;
  345. addchar(ibe, VM_CMD_VERSION);
  346. addchar(ibe, VM_PROTOCOL_VERSION);
  347. ibe->outbuf[ibe->outlen] = VM_CMD_CHAR;
  348. ibe->outlen++;
  349. addchar(ibe, VM_CMD_CAPABILITIES);
  350. v = VM_CAPABILITIES_IRQ | VM_CAPABILITIES_ATTN;
  351. if (k->do_hw_op(ibe->parent.intf, IPMI_POWEROFF_CHASSIS, 1) == 0) {
  352. v |= VM_CAPABILITIES_POWER;
  353. }
  354. if (k->do_hw_op(ibe->parent.intf, IPMI_SHUTDOWN_VIA_ACPI_OVERTEMP, 1)
  355. == 0) {
  356. v |= VM_CAPABILITIES_GRACEFUL_SHUTDOWN;
  357. }
  358. if (k->do_hw_op(ibe->parent.intf, IPMI_RESET_CHASSIS, 1) == 0) {
  359. v |= VM_CAPABILITIES_RESET;
  360. }
  361. if (k->do_hw_op(ibe->parent.intf, IPMI_SEND_NMI, 1) == 0) {
  362. v |= VM_CAPABILITIES_NMI;
  363. }
  364. addchar(ibe, v);
  365. ibe->outbuf[ibe->outlen] = VM_CMD_CHAR;
  366. ibe->outlen++;
  367. ibe->sending_cmd = false;
  368. continue_send(ibe);
  369. break;
  370. case CHR_EVENT_CLOSED:
  371. if (!ibe->connected) {
  372. return;
  373. }
  374. ibe->connected = false;
  375. if (ibe->waiting_rsp) {
  376. ibe->waiting_rsp = false;
  377. ibe->inbuf[1] = ibe->outbuf[1] | 0x04;
  378. ibe->inbuf[2] = ibe->outbuf[2];
  379. ibe->inbuf[3] = IPMI_CC_BMC_INIT_IN_PROGRESS;
  380. k->handle_rsp(s, ibe->outbuf[0], ibe->inbuf + 1, 3);
  381. }
  382. break;
  383. }
  384. }
  385. static void ipmi_bmc_extern_handle_reset(IPMIBmc *b)
  386. {
  387. IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(b);
  388. ibe->send_reset = true;
  389. continue_send(ibe);
  390. }
  391. static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp)
  392. {
  393. IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(dev);
  394. if (!qemu_chr_fe_get_driver(&ibe->chr)) {
  395. error_setg(errp, "IPMI external bmc requires chardev attribute");
  396. return;
  397. }
  398. qemu_chr_fe_set_handlers(&ibe->chr, can_receive, receive,
  399. chr_event, ibe, NULL, true);
  400. }
  401. static int ipmi_bmc_extern_post_migrate(void *opaque, int version_id)
  402. {
  403. IPMIBmcExtern *ibe = opaque;
  404. /*
  405. * We don't directly restore waiting_rsp, Instead, we return an
  406. * error on the interface if a response was being waited for.
  407. */
  408. if (ibe->waiting_rsp) {
  409. IPMIInterface *ii = ibe->parent.intf;
  410. IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii);
  411. ibe->waiting_rsp = false;
  412. ibe->inbuf[1] = ibe->outbuf[1] | 0x04;
  413. ibe->inbuf[2] = ibe->outbuf[2];
  414. ibe->inbuf[3] = IPMI_CC_BMC_INIT_IN_PROGRESS;
  415. iic->handle_rsp(ii, ibe->outbuf[0], ibe->inbuf + 1, 3);
  416. }
  417. return 0;
  418. }
  419. static const VMStateDescription vmstate_ipmi_bmc_extern = {
  420. .name = TYPE_IPMI_BMC_EXTERN,
  421. .version_id = 1,
  422. .minimum_version_id = 1,
  423. .post_load = ipmi_bmc_extern_post_migrate,
  424. .fields = (VMStateField[]) {
  425. VMSTATE_BOOL(send_reset, IPMIBmcExtern),
  426. VMSTATE_BOOL(waiting_rsp, IPMIBmcExtern),
  427. VMSTATE_END_OF_LIST()
  428. }
  429. };
  430. static void ipmi_bmc_extern_init(Object *obj)
  431. {
  432. IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(obj);
  433. ibe->extern_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, extern_timeout, ibe);
  434. vmstate_register(NULL, 0, &vmstate_ipmi_bmc_extern, ibe);
  435. }
  436. static void ipmi_bmc_extern_finalize(Object *obj)
  437. {
  438. IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(obj);
  439. timer_del(ibe->extern_timer);
  440. timer_free(ibe->extern_timer);
  441. }
  442. static Property ipmi_bmc_extern_properties[] = {
  443. DEFINE_PROP_CHR("chardev", IPMIBmcExtern, chr),
  444. DEFINE_PROP_END_OF_LIST(),
  445. };
  446. static void ipmi_bmc_extern_class_init(ObjectClass *oc, void *data)
  447. {
  448. DeviceClass *dc = DEVICE_CLASS(oc);
  449. IPMIBmcClass *bk = IPMI_BMC_CLASS(oc);
  450. bk->handle_command = ipmi_bmc_extern_handle_command;
  451. bk->handle_reset = ipmi_bmc_extern_handle_reset;
  452. dc->hotpluggable = false;
  453. dc->realize = ipmi_bmc_extern_realize;
  454. dc->props = ipmi_bmc_extern_properties;
  455. }
  456. static const TypeInfo ipmi_bmc_extern_type = {
  457. .name = TYPE_IPMI_BMC_EXTERN,
  458. .parent = TYPE_IPMI_BMC,
  459. .instance_size = sizeof(IPMIBmcExtern),
  460. .instance_init = ipmi_bmc_extern_init,
  461. .instance_finalize = ipmi_bmc_extern_finalize,
  462. .class_init = ipmi_bmc_extern_class_init,
  463. };
  464. static void ipmi_bmc_extern_register_types(void)
  465. {
  466. type_register_static(&ipmi_bmc_extern_type);
  467. }
  468. type_init(ipmi_bmc_extern_register_types)