sys-manager.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  1. /*
  2. * PS3 System Manager.
  3. *
  4. * Copyright (C) 2007 Sony Computer Entertainment Inc.
  5. * Copyright 2007 Sony Corp.
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; version 2 of the License.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  19. */
  20. #include <linux/kernel.h>
  21. #include <linux/module.h>
  22. #include <linux/workqueue.h>
  23. #include <linux/reboot.h>
  24. #include <asm/ps3.h>
  25. #include "vuart.h"
  26. MODULE_AUTHOR("Sony Corporation");
  27. MODULE_LICENSE("GPL v2");
  28. MODULE_DESCRIPTION("PS3 System Manager");
  29. /**
  30. * ps3_sys_manager - PS3 system manager driver.
  31. *
  32. * The system manager provides an asyncronous system event notification
  33. * mechanism for reporting events like thermal alert and button presses to
  34. * guests. It also provides support to control system shutdown and startup.
  35. *
  36. * The actual system manager is implemented as an application running in the
  37. * system policy module in lpar_1. Guests communicate with the system manager
  38. * through port 2 of the vuart using a simple packet message protocol.
  39. * Messages are comprised of a fixed field header followed by a message
  40. * specific payload.
  41. */
  42. /**
  43. * struct ps3_sys_manager_header - System manager message header.
  44. * @version: Header version, currently 1.
  45. * @size: Header size in bytes, curently 16.
  46. * @payload_size: Message payload size in bytes.
  47. * @service_id: Message type, one of enum ps3_sys_manager_service_id.
  48. */
  49. struct ps3_sys_manager_header {
  50. /* version 1 */
  51. u8 version;
  52. u8 size;
  53. u16 reserved_1;
  54. u32 payload_size;
  55. u16 service_id;
  56. u16 reserved_2[3];
  57. };
  58. /**
  59. * @PS3_SM_RX_MSG_LEN - System manager received message length.
  60. *
  61. * Currently all messages received from the system manager are the same length
  62. * (16 bytes header + 16 bytes payload = 32 bytes). This knowlege is used to
  63. * simplify the logic.
  64. */
  65. enum {
  66. PS3_SM_RX_MSG_LEN = 32,
  67. };
  68. /**
  69. * enum ps3_sys_manager_service_id - Message header service_id.
  70. * @PS3_SM_SERVICE_ID_REQUEST: guest --> sys_manager.
  71. * @PS3_SM_SERVICE_ID_COMMAND: guest <-- sys_manager.
  72. * @PS3_SM_SERVICE_ID_RESPONSE: guest --> sys_manager.
  73. * @PS3_SM_SERVICE_ID_SET_ATTR: guest --> sys_manager.
  74. * @PS3_SM_SERVICE_ID_EXTERN_EVENT: guest <-- sys_manager.
  75. * @PS3_SM_SERVICE_ID_SET_NEXT_OP: guest --> sys_manager.
  76. */
  77. enum ps3_sys_manager_service_id {
  78. /* version 1 */
  79. PS3_SM_SERVICE_ID_REQUEST = 1,
  80. PS3_SM_SERVICE_ID_RESPONSE = 2,
  81. PS3_SM_SERVICE_ID_COMMAND = 3,
  82. PS3_SM_SERVICE_ID_EXTERN_EVENT = 4,
  83. PS3_SM_SERVICE_ID_SET_NEXT_OP = 5,
  84. PS3_SM_SERVICE_ID_SET_ATTR = 8,
  85. };
  86. /**
  87. * enum ps3_sys_manager_attr - Notification attribute (bit position mask).
  88. * @PS3_SM_ATTR_POWER: Power button.
  89. * @PS3_SM_ATTR_RESET: Reset button, not available on retail console.
  90. * @PS3_SM_ATTR_THERMAL: Sytem thermal alert.
  91. * @PS3_SM_ATTR_CONTROLLER: Remote controller event.
  92. * @PS3_SM_ATTR_ALL: Logical OR of all.
  93. *
  94. * The guest tells the system manager which events it is interested in receiving
  95. * notice of by sending the system manager a logical OR of notification
  96. * attributes via the ps3_sys_manager_send_attr() routine.
  97. */
  98. enum ps3_sys_manager_attr {
  99. /* version 1 */
  100. PS3_SM_ATTR_POWER = 1,
  101. PS3_SM_ATTR_RESET = 2,
  102. PS3_SM_ATTR_THERMAL = 4,
  103. PS3_SM_ATTR_CONTROLLER = 8, /* bogus? */
  104. PS3_SM_ATTR_ALL = 0x0f,
  105. };
  106. /**
  107. * enum ps3_sys_manager_event - External event type, reported by system manager.
  108. * @PS3_SM_EVENT_POWER_PRESSED: payload.value not used.
  109. * @PS3_SM_EVENT_POWER_RELEASED: payload.value = time pressed in millisec.
  110. * @PS3_SM_EVENT_RESET_PRESSED: payload.value not used.
  111. * @PS3_SM_EVENT_RESET_RELEASED: payload.value = time pressed in millisec.
  112. * @PS3_SM_EVENT_THERMAL_ALERT: payload.value = thermal zone id.
  113. * @PS3_SM_EVENT_THERMAL_CLEARED: payload.value = thermal zone id.
  114. */
  115. enum ps3_sys_manager_event {
  116. /* version 1 */
  117. PS3_SM_EVENT_POWER_PRESSED = 3,
  118. PS3_SM_EVENT_POWER_RELEASED = 4,
  119. PS3_SM_EVENT_RESET_PRESSED = 5,
  120. PS3_SM_EVENT_RESET_RELEASED = 6,
  121. PS3_SM_EVENT_THERMAL_ALERT = 7,
  122. PS3_SM_EVENT_THERMAL_CLEARED = 8,
  123. /* no info on controller events */
  124. };
  125. /**
  126. * enum ps3_sys_manager_next_op - Operation to perform after lpar is destroyed.
  127. */
  128. enum ps3_sys_manager_next_op {
  129. /* version 3 */
  130. PS3_SM_NEXT_OP_SYS_SHUTDOWN = 1,
  131. PS3_SM_NEXT_OP_SYS_REBOOT = 2,
  132. PS3_SM_NEXT_OP_LPAR_REBOOT = 0x82,
  133. };
  134. /**
  135. * enum ps3_sys_manager_wake_source - Next-op wakeup source (bit position mask).
  136. * @PS3_SM_WAKE_DEFAULT: Disk insert, power button, eject button, IR
  137. * controller, and bluetooth controller.
  138. * @PS3_SM_WAKE_RTC:
  139. * @PS3_SM_WAKE_RTC_ERROR:
  140. * @PS3_SM_WAKE_P_O_R: Power on reset.
  141. *
  142. * Additional wakeup sources when specifying PS3_SM_NEXT_OP_SYS_SHUTDOWN.
  143. * System will always wake from the PS3_SM_WAKE_DEFAULT sources.
  144. */
  145. enum ps3_sys_manager_wake_source {
  146. /* version 3 */
  147. PS3_SM_WAKE_DEFAULT = 0,
  148. PS3_SM_WAKE_RTC = 0x00000040,
  149. PS3_SM_WAKE_RTC_ERROR = 0x00000080,
  150. PS3_SM_WAKE_P_O_R = 0x10000000,
  151. };
  152. /**
  153. * enum ps3_sys_manager_cmd - Command from system manager to guest.
  154. *
  155. * The guest completes the actions needed, then acks or naks the command via
  156. * ps3_sys_manager_send_response(). In the case of @PS3_SM_CMD_SHUTDOWN,
  157. * the guest must be fully prepared for a system poweroff prior to acking the
  158. * command.
  159. */
  160. enum ps3_sys_manager_cmd {
  161. /* version 1 */
  162. PS3_SM_CMD_SHUTDOWN = 1, /* shutdown guest OS */
  163. };
  164. /**
  165. * ps3_sys_manager_write - Helper to write a two part message to the vuart.
  166. *
  167. */
  168. static int ps3_sys_manager_write(struct ps3_vuart_port_device *dev,
  169. const struct ps3_sys_manager_header *header, const void *payload)
  170. {
  171. int result;
  172. BUG_ON(header->version != 1);
  173. BUG_ON(header->size != 16);
  174. BUG_ON(header->payload_size != 8 && header->payload_size != 16);
  175. BUG_ON(header->service_id > 8);
  176. result = ps3_vuart_write(dev, header,
  177. sizeof(struct ps3_sys_manager_header));
  178. if (!result)
  179. result = ps3_vuart_write(dev, payload, header->payload_size);
  180. return result;
  181. }
  182. /**
  183. * ps3_sys_manager_send_attr - Send a 'set attribute' to the system manager.
  184. *
  185. */
  186. static int ps3_sys_manager_send_attr(struct ps3_vuart_port_device *dev,
  187. enum ps3_sys_manager_attr attr)
  188. {
  189. static const struct ps3_sys_manager_header header = {
  190. .version = 1,
  191. .size = 16,
  192. .payload_size = 16,
  193. .service_id = PS3_SM_SERVICE_ID_SET_ATTR,
  194. };
  195. struct {
  196. u8 version;
  197. u8 reserved_1[3];
  198. u32 attribute;
  199. } payload;
  200. BUILD_BUG_ON(sizeof(payload) != 8);
  201. dev_dbg(&dev->core, "%s:%d: %xh\n", __func__, __LINE__, attr);
  202. memset(&payload, 0, sizeof(payload));
  203. payload.version = 1;
  204. payload.attribute = attr;
  205. return ps3_sys_manager_write(dev, &header, &payload);
  206. }
  207. /**
  208. * ps3_sys_manager_send_next_op - Send a 'set next op' to the system manager.
  209. *
  210. * Tell the system manager what to do after this lpar is destroyed.
  211. */
  212. static int ps3_sys_manager_send_next_op(struct ps3_vuart_port_device *dev,
  213. enum ps3_sys_manager_next_op op,
  214. enum ps3_sys_manager_wake_source wake_source)
  215. {
  216. static const struct ps3_sys_manager_header header = {
  217. .version = 1,
  218. .size = 16,
  219. .payload_size = 16,
  220. .service_id = PS3_SM_SERVICE_ID_SET_NEXT_OP,
  221. };
  222. struct {
  223. u8 version;
  224. u8 type;
  225. u8 gos_id;
  226. u8 reserved_1;
  227. u32 wake_source;
  228. u8 reserved_2[8];
  229. } payload;
  230. BUILD_BUG_ON(sizeof(payload) != 16);
  231. dev_dbg(&dev->core, "%s:%d: (%xh)\n", __func__, __LINE__, op);
  232. memset(&payload, 0, sizeof(payload));
  233. payload.version = 3;
  234. payload.type = op;
  235. payload.gos_id = 3; /* other os */
  236. payload.wake_source = wake_source;
  237. return ps3_sys_manager_write(dev, &header, &payload);
  238. }
  239. /**
  240. * ps3_sys_manager_send_request_shutdown - Send 'request' to the system manager.
  241. *
  242. * The guest sends this message to request an operation or action of the system
  243. * manager. The reply is a command message from the system manager. In the
  244. * command handler the guest performs the requested operation. The result of
  245. * the command is then communicated back to the system manager with a response
  246. * message.
  247. *
  248. * Currently, the only supported request it the 'shutdown self' request.
  249. */
  250. static int ps3_sys_manager_send_request_shutdown(struct ps3_vuart_port_device *dev)
  251. {
  252. static const struct ps3_sys_manager_header header = {
  253. .version = 1,
  254. .size = 16,
  255. .payload_size = 16,
  256. .service_id = PS3_SM_SERVICE_ID_REQUEST,
  257. };
  258. struct {
  259. u8 version;
  260. u8 type;
  261. u8 gos_id;
  262. u8 reserved_1[13];
  263. } static const payload = {
  264. .version = 1,
  265. .type = 1, /* shutdown */
  266. .gos_id = 0, /* self */
  267. };
  268. BUILD_BUG_ON(sizeof(payload) != 16);
  269. dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
  270. return ps3_sys_manager_write(dev, &header, &payload);
  271. }
  272. /**
  273. * ps3_sys_manager_send_response - Send a 'response' to the system manager.
  274. * @status: zero = success, others fail.
  275. *
  276. * The guest sends this message to the system manager to acnowledge success or
  277. * failure of a command sent by the system manager.
  278. */
  279. static int ps3_sys_manager_send_response(struct ps3_vuart_port_device *dev,
  280. u64 status)
  281. {
  282. static const struct ps3_sys_manager_header header = {
  283. .version = 1,
  284. .size = 16,
  285. .payload_size = 16,
  286. .service_id = PS3_SM_SERVICE_ID_RESPONSE,
  287. };
  288. struct {
  289. u8 version;
  290. u8 reserved_1[3];
  291. u8 status;
  292. u8 reserved_2[11];
  293. } payload;
  294. BUILD_BUG_ON(sizeof(payload) != 16);
  295. dev_dbg(&dev->core, "%s:%d: (%s)\n", __func__, __LINE__,
  296. (status ? "nak" : "ack"));
  297. memset(&payload, 0, sizeof(payload));
  298. payload.version = 1;
  299. payload.status = status;
  300. return ps3_sys_manager_write(dev, &header, &payload);
  301. }
  302. /**
  303. * ps3_sys_manager_handle_event - Second stage event msg handler.
  304. *
  305. */
  306. static int ps3_sys_manager_handle_event(struct ps3_vuart_port_device *dev)
  307. {
  308. int result;
  309. struct {
  310. u8 version;
  311. u8 type;
  312. u8 reserved_1[2];
  313. u32 value;
  314. u8 reserved_2[8];
  315. } event;
  316. BUILD_BUG_ON(sizeof(event) != 16);
  317. result = ps3_vuart_read(dev, &event, sizeof(event));
  318. BUG_ON(result);
  319. if (event.version != 1) {
  320. dev_dbg(&dev->core, "%s:%d: unsupported event version (%u)\n",
  321. __func__, __LINE__, event.version);
  322. return -EIO;
  323. }
  324. switch (event.type) {
  325. case PS3_SM_EVENT_POWER_PRESSED:
  326. dev_dbg(&dev->core, "%s:%d: POWER_PRESSED\n",
  327. __func__, __LINE__);
  328. break;
  329. case PS3_SM_EVENT_POWER_RELEASED:
  330. dev_dbg(&dev->core, "%s:%d: POWER_RELEASED (%u ms)\n",
  331. __func__, __LINE__, event.value);
  332. kill_cad_pid(SIGINT, 1);
  333. break;
  334. case PS3_SM_EVENT_THERMAL_ALERT:
  335. dev_dbg(&dev->core, "%s:%d: THERMAL_ALERT (zone %u)\n",
  336. __func__, __LINE__, event.value);
  337. printk(KERN_INFO "PS3 Thermal Alert Zone %u\n", event.value);
  338. break;
  339. case PS3_SM_EVENT_THERMAL_CLEARED:
  340. dev_dbg(&dev->core, "%s:%d: THERMAL_CLEARED (zone %u)\n",
  341. __func__, __LINE__, event.value);
  342. break;
  343. default:
  344. dev_dbg(&dev->core, "%s:%d: unknown event (%u)\n",
  345. __func__, __LINE__, event.type);
  346. return -EIO;
  347. }
  348. return 0;
  349. }
  350. /**
  351. * ps3_sys_manager_handle_cmd - Second stage command msg handler.
  352. *
  353. * The system manager sends this in reply to a 'request' message from the guest.
  354. */
  355. static int ps3_sys_manager_handle_cmd(struct ps3_vuart_port_device *dev)
  356. {
  357. int result;
  358. struct {
  359. u8 version;
  360. u8 type;
  361. u8 reserved_1[14];
  362. } cmd;
  363. BUILD_BUG_ON(sizeof(cmd) != 16);
  364. dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
  365. result = ps3_vuart_read(dev, &cmd, sizeof(cmd));
  366. if(result)
  367. return result;
  368. if (cmd.version != 1) {
  369. dev_dbg(&dev->core, "%s:%d: unsupported cmd version (%u)\n",
  370. __func__, __LINE__, cmd.version);
  371. return -EIO;
  372. }
  373. if (cmd.type != PS3_SM_CMD_SHUTDOWN) {
  374. dev_dbg(&dev->core, "%s:%d: unknown cmd (%u)\n",
  375. __func__, __LINE__, cmd.type);
  376. return -EIO;
  377. }
  378. ps3_sys_manager_send_response(dev, 0);
  379. return 0;
  380. }
  381. /**
  382. * ps3_sys_manager_handle_msg - First stage msg handler.
  383. *
  384. */
  385. static int ps3_sys_manager_handle_msg(struct ps3_vuart_port_device *dev)
  386. {
  387. int result;
  388. struct ps3_sys_manager_header header;
  389. result = ps3_vuart_read(dev, &header,
  390. sizeof(struct ps3_sys_manager_header));
  391. if(result)
  392. return result;
  393. if (header.version != 1) {
  394. dev_dbg(&dev->core, "%s:%d: unsupported header version (%u)\n",
  395. __func__, __LINE__, header.version);
  396. goto fail_header;
  397. }
  398. BUILD_BUG_ON(sizeof(header) != 16);
  399. BUG_ON(header.size != 16);
  400. BUG_ON(header.payload_size != 16);
  401. switch (header.service_id) {
  402. case PS3_SM_SERVICE_ID_EXTERN_EVENT:
  403. dev_dbg(&dev->core, "%s:%d: EVENT\n", __func__, __LINE__);
  404. return ps3_sys_manager_handle_event(dev);
  405. case PS3_SM_SERVICE_ID_COMMAND:
  406. dev_dbg(&dev->core, "%s:%d: COMMAND\n", __func__, __LINE__);
  407. return ps3_sys_manager_handle_cmd(dev);
  408. default:
  409. dev_dbg(&dev->core, "%s:%d: unknown service_id (%u)\n",
  410. __func__, __LINE__, header.service_id);
  411. break;
  412. }
  413. goto fail_id;
  414. fail_header:
  415. ps3_vuart_clear_rx_bytes(dev, 0);
  416. return -EIO;
  417. fail_id:
  418. ps3_vuart_clear_rx_bytes(dev, header.payload_size);
  419. return -EIO;
  420. }
  421. /**
  422. * ps3_sys_manager_work - Asyncronous read handler.
  423. *
  424. * Signaled when a complete message arrives at the vuart port.
  425. */
  426. static void ps3_sys_manager_work(struct work_struct *work)
  427. {
  428. struct ps3_vuart_port_device *dev = ps3_vuart_work_to_port_device(work);
  429. ps3_sys_manager_handle_msg(dev);
  430. ps3_vuart_read_async(dev, ps3_sys_manager_work, PS3_SM_RX_MSG_LEN);
  431. }
  432. struct {
  433. struct ps3_vuart_port_device *dev;
  434. } static drv_priv;
  435. /**
  436. * ps3_sys_manager_restart - The final platform machine_restart routine.
  437. *
  438. * This routine never returns. The routine disables asyncronous vuart reads
  439. * then spins calling ps3_sys_manager_handle_msg() to receive and acknowledge
  440. * the shutdown command sent from the system manager. Soon after the
  441. * acknowledgement is sent the lpar is destroyed by the HV. This routine
  442. * should only be called from ps3_restart().
  443. */
  444. void ps3_sys_manager_restart(void)
  445. {
  446. struct ps3_vuart_port_device *dev = drv_priv.dev;
  447. BUG_ON(!drv_priv.dev);
  448. dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
  449. ps3_vuart_cancel_async(dev);
  450. ps3_sys_manager_send_attr(dev, 0);
  451. ps3_sys_manager_send_next_op(dev, PS3_SM_NEXT_OP_LPAR_REBOOT,
  452. PS3_SM_WAKE_DEFAULT);
  453. ps3_sys_manager_send_request_shutdown(dev);
  454. printk(KERN_EMERG "System Halted, OK to turn off power\n");
  455. while(1)
  456. ps3_sys_manager_handle_msg(dev);
  457. }
  458. /**
  459. * ps3_sys_manager_power_off - The final platform machine_power_off routine.
  460. *
  461. * This routine never returns. The routine disables asyncronous vuart reads
  462. * then spins calling ps3_sys_manager_handle_msg() to receive and acknowledge
  463. * the shutdown command sent from the system manager. Soon after the
  464. * acknowledgement is sent the lpar is destroyed by the HV. This routine
  465. * should only be called from ps3_power_off().
  466. */
  467. void ps3_sys_manager_power_off(void)
  468. {
  469. struct ps3_vuart_port_device *dev = drv_priv.dev;
  470. BUG_ON(!drv_priv.dev);
  471. dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
  472. ps3_vuart_cancel_async(dev);
  473. ps3_sys_manager_send_next_op(dev, PS3_SM_NEXT_OP_SYS_SHUTDOWN,
  474. PS3_SM_WAKE_DEFAULT);
  475. ps3_sys_manager_send_request_shutdown(dev);
  476. printk(KERN_EMERG "System Halted, OK to turn off power\n");
  477. while(1)
  478. ps3_sys_manager_handle_msg(dev);
  479. }
  480. static int ps3_sys_manager_probe(struct ps3_vuart_port_device *dev)
  481. {
  482. int result;
  483. dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
  484. BUG_ON(drv_priv.dev);
  485. drv_priv.dev = dev;
  486. result = ps3_sys_manager_send_attr(dev, PS3_SM_ATTR_ALL);
  487. BUG_ON(result);
  488. result = ps3_vuart_read_async(dev, ps3_sys_manager_work,
  489. PS3_SM_RX_MSG_LEN);
  490. BUG_ON(result);
  491. return result;
  492. }
  493. static struct ps3_vuart_port_driver ps3_sys_manager = {
  494. .match_id = PS3_MATCH_ID_SYSTEM_MANAGER,
  495. .core = {
  496. .name = "ps3_sys_manager",
  497. },
  498. .probe = ps3_sys_manager_probe,
  499. };
  500. static int __init ps3_sys_manager_init(void)
  501. {
  502. return ps3_vuart_port_driver_register(&ps3_sys_manager);
  503. }
  504. module_init(ps3_sys_manager_init);