spapr_rtas.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. /*
  2. * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator
  3. *
  4. * Hypercall based emulated RTAS
  5. *
  6. * Copyright (c) 2010-2011 David Gibson, IBM Corporation.
  7. *
  8. * Permission is hereby granted, free of charge, to any person obtaining a copy
  9. * of this software and associated documentation files (the "Software"), to deal
  10. * in the Software without restriction, including without limitation the rights
  11. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12. * copies of the Software, and to permit persons to whom the Software is
  13. * furnished to do so, subject to the following conditions:
  14. *
  15. * The above copyright notice and this permission notice shall be included in
  16. * all copies or substantial portions of the Software.
  17. *
  18. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  21. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  24. * THE SOFTWARE.
  25. *
  26. */
  27. #include "cpu.h"
  28. #include "sysemu.h"
  29. #include "qemu-char.h"
  30. #include "hw/qdev.h"
  31. #include "device_tree.h"
  32. #include "hw/spapr.h"
  33. #include "hw/spapr_vio.h"
  34. #include <libfdt.h>
  35. #define TOKEN_BASE 0x2000
  36. #define TOKEN_MAX 0x100
  37. static void rtas_display_character(sPAPREnvironment *spapr,
  38. uint32_t token, uint32_t nargs,
  39. target_ulong args,
  40. uint32_t nret, target_ulong rets)
  41. {
  42. uint8_t c = rtas_ld(args, 0);
  43. VIOsPAPRDevice *sdev = vty_lookup(spapr, 0);
  44. if (!sdev) {
  45. rtas_st(rets, 0, -1);
  46. } else {
  47. vty_putchars(sdev, &c, sizeof(c));
  48. rtas_st(rets, 0, 0);
  49. }
  50. }
  51. static void rtas_get_time_of_day(sPAPREnvironment *spapr,
  52. uint32_t token, uint32_t nargs,
  53. target_ulong args,
  54. uint32_t nret, target_ulong rets)
  55. {
  56. struct tm tm;
  57. if (nret != 8) {
  58. rtas_st(rets, 0, -3);
  59. return;
  60. }
  61. qemu_get_timedate(&tm, spapr->rtc_offset);
  62. rtas_st(rets, 0, 0); /* Success */
  63. rtas_st(rets, 1, tm.tm_year + 1900);
  64. rtas_st(rets, 2, tm.tm_mon + 1);
  65. rtas_st(rets, 3, tm.tm_mday);
  66. rtas_st(rets, 4, tm.tm_hour);
  67. rtas_st(rets, 5, tm.tm_min);
  68. rtas_st(rets, 6, tm.tm_sec);
  69. rtas_st(rets, 7, 0); /* we don't do nanoseconds */
  70. }
  71. static void rtas_set_time_of_day(sPAPREnvironment *spapr,
  72. uint32_t token, uint32_t nargs,
  73. target_ulong args,
  74. uint32_t nret, target_ulong rets)
  75. {
  76. struct tm tm;
  77. tm.tm_year = rtas_ld(args, 0) - 1900;
  78. tm.tm_mon = rtas_ld(args, 1) - 1;
  79. tm.tm_mday = rtas_ld(args, 2);
  80. tm.tm_hour = rtas_ld(args, 3);
  81. tm.tm_min = rtas_ld(args, 4);
  82. tm.tm_sec = rtas_ld(args, 5);
  83. /* Just generate a monitor event for the change */
  84. rtc_change_mon_event(&tm);
  85. spapr->rtc_offset = qemu_timedate_diff(&tm);
  86. rtas_st(rets, 0, 0); /* Success */
  87. }
  88. static void rtas_power_off(sPAPREnvironment *spapr,
  89. uint32_t token, uint32_t nargs, target_ulong args,
  90. uint32_t nret, target_ulong rets)
  91. {
  92. if (nargs != 2 || nret != 1) {
  93. rtas_st(rets, 0, -3);
  94. return;
  95. }
  96. qemu_system_shutdown_request();
  97. rtas_st(rets, 0, 0);
  98. }
  99. static void rtas_system_reboot(sPAPREnvironment *spapr,
  100. uint32_t token, uint32_t nargs,
  101. target_ulong args,
  102. uint32_t nret, target_ulong rets)
  103. {
  104. if (nargs != 0 || nret != 1) {
  105. rtas_st(rets, 0, -3);
  106. return;
  107. }
  108. qemu_system_reset_request();
  109. rtas_st(rets, 0, 0);
  110. }
  111. static void rtas_query_cpu_stopped_state(sPAPREnvironment *spapr,
  112. uint32_t token, uint32_t nargs,
  113. target_ulong args,
  114. uint32_t nret, target_ulong rets)
  115. {
  116. target_ulong id;
  117. CPUPPCState *env;
  118. if (nargs != 1 || nret != 2) {
  119. rtas_st(rets, 0, -3);
  120. return;
  121. }
  122. id = rtas_ld(args, 0);
  123. for (env = first_cpu; env; env = env->next_cpu) {
  124. if (env->cpu_index != id) {
  125. continue;
  126. }
  127. if (env->halted) {
  128. rtas_st(rets, 1, 0);
  129. } else {
  130. rtas_st(rets, 1, 2);
  131. }
  132. rtas_st(rets, 0, 0);
  133. return;
  134. }
  135. /* Didn't find a matching cpu */
  136. rtas_st(rets, 0, -3);
  137. }
  138. static void rtas_start_cpu(sPAPREnvironment *spapr,
  139. uint32_t token, uint32_t nargs,
  140. target_ulong args,
  141. uint32_t nret, target_ulong rets)
  142. {
  143. target_ulong id, start, r3;
  144. CPUPPCState *env;
  145. if (nargs != 3 || nret != 1) {
  146. rtas_st(rets, 0, -3);
  147. return;
  148. }
  149. id = rtas_ld(args, 0);
  150. start = rtas_ld(args, 1);
  151. r3 = rtas_ld(args, 2);
  152. for (env = first_cpu; env; env = env->next_cpu) {
  153. if (env->cpu_index != id) {
  154. continue;
  155. }
  156. if (!env->halted) {
  157. rtas_st(rets, 0, -1);
  158. return;
  159. }
  160. env->msr = (1ULL << MSR_SF) | (1ULL << MSR_ME);
  161. env->nip = start;
  162. env->gpr[3] = r3;
  163. env->halted = 0;
  164. qemu_cpu_kick(env);
  165. rtas_st(rets, 0, 0);
  166. return;
  167. }
  168. /* Didn't find a matching cpu */
  169. rtas_st(rets, 0, -3);
  170. }
  171. static struct rtas_call {
  172. const char *name;
  173. spapr_rtas_fn fn;
  174. } rtas_table[TOKEN_MAX];
  175. struct rtas_call *rtas_next = rtas_table;
  176. target_ulong spapr_rtas_call(sPAPREnvironment *spapr,
  177. uint32_t token, uint32_t nargs, target_ulong args,
  178. uint32_t nret, target_ulong rets)
  179. {
  180. if ((token >= TOKEN_BASE)
  181. && ((token - TOKEN_BASE) < TOKEN_MAX)) {
  182. struct rtas_call *call = rtas_table + (token - TOKEN_BASE);
  183. if (call->fn) {
  184. call->fn(spapr, token, nargs, args, nret, rets);
  185. return H_SUCCESS;
  186. }
  187. }
  188. /* HACK: Some Linux early debug code uses RTAS display-character,
  189. * but assumes the token value is 0xa (which it is on some real
  190. * machines) without looking it up in the device tree. This
  191. * special case makes this work */
  192. if (token == 0xa) {
  193. rtas_display_character(spapr, 0xa, nargs, args, nret, rets);
  194. return H_SUCCESS;
  195. }
  196. hcall_dprintf("Unknown RTAS token 0x%x\n", token);
  197. rtas_st(rets, 0, -3);
  198. return H_PARAMETER;
  199. }
  200. void spapr_rtas_register(const char *name, spapr_rtas_fn fn)
  201. {
  202. assert(rtas_next < (rtas_table + TOKEN_MAX));
  203. rtas_next->name = name;
  204. rtas_next->fn = fn;
  205. rtas_next++;
  206. }
  207. int spapr_rtas_device_tree_setup(void *fdt, target_phys_addr_t rtas_addr,
  208. target_phys_addr_t rtas_size)
  209. {
  210. int ret;
  211. int i;
  212. ret = fdt_add_mem_rsv(fdt, rtas_addr, rtas_size);
  213. if (ret < 0) {
  214. fprintf(stderr, "Couldn't add RTAS reserve entry: %s\n",
  215. fdt_strerror(ret));
  216. return ret;
  217. }
  218. ret = qemu_devtree_setprop_cell(fdt, "/rtas", "linux,rtas-base",
  219. rtas_addr);
  220. if (ret < 0) {
  221. fprintf(stderr, "Couldn't add linux,rtas-base property: %s\n",
  222. fdt_strerror(ret));
  223. return ret;
  224. }
  225. ret = qemu_devtree_setprop_cell(fdt, "/rtas", "linux,rtas-entry",
  226. rtas_addr);
  227. if (ret < 0) {
  228. fprintf(stderr, "Couldn't add linux,rtas-entry property: %s\n",
  229. fdt_strerror(ret));
  230. return ret;
  231. }
  232. ret = qemu_devtree_setprop_cell(fdt, "/rtas", "rtas-size",
  233. rtas_size);
  234. if (ret < 0) {
  235. fprintf(stderr, "Couldn't add rtas-size property: %s\n",
  236. fdt_strerror(ret));
  237. return ret;
  238. }
  239. for (i = 0; i < TOKEN_MAX; i++) {
  240. struct rtas_call *call = &rtas_table[i];
  241. if (!call->fn) {
  242. continue;
  243. }
  244. ret = qemu_devtree_setprop_cell(fdt, "/rtas", call->name,
  245. i + TOKEN_BASE);
  246. if (ret < 0) {
  247. fprintf(stderr, "Couldn't add rtas token for %s: %s\n",
  248. call->name, fdt_strerror(ret));
  249. return ret;
  250. }
  251. }
  252. return 0;
  253. }
  254. static void core_rtas_register_types(void)
  255. {
  256. spapr_rtas_register("display-character", rtas_display_character);
  257. spapr_rtas_register("get-time-of-day", rtas_get_time_of_day);
  258. spapr_rtas_register("set-time-of-day", rtas_set_time_of_day);
  259. spapr_rtas_register("power-off", rtas_power_off);
  260. spapr_rtas_register("system-reboot", rtas_system_reboot);
  261. spapr_rtas_register("query-cpu-stopped-state",
  262. rtas_query_cpu_stopped_state);
  263. spapr_rtas_register("start-cpu", rtas_start_cpu);
  264. }
  265. type_init(core_rtas_register_types)