sys-manager.c 16 KB

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