spapr_rtas.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  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 = spapr_vio_find_by_reg(spapr->vio_bus,
  44. SPAPR_VTY_BASE_ADDRESS);
  45. if (!sdev) {
  46. rtas_st(rets, 0, -1);
  47. } else {
  48. vty_putchars(sdev, &c, sizeof(c));
  49. rtas_st(rets, 0, 0);
  50. }
  51. }
  52. static void rtas_get_time_of_day(sPAPREnvironment *spapr,
  53. uint32_t token, uint32_t nargs,
  54. target_ulong args,
  55. uint32_t nret, target_ulong rets)
  56. {
  57. struct tm tm;
  58. if (nret != 8) {
  59. rtas_st(rets, 0, -3);
  60. return;
  61. }
  62. qemu_get_timedate(&tm, 0);
  63. rtas_st(rets, 0, 0); /* Success */
  64. rtas_st(rets, 1, tm.tm_year + 1900);
  65. rtas_st(rets, 2, tm.tm_mon + 1);
  66. rtas_st(rets, 3, tm.tm_mday);
  67. rtas_st(rets, 4, tm.tm_hour);
  68. rtas_st(rets, 5, tm.tm_min);
  69. rtas_st(rets, 6, tm.tm_sec);
  70. rtas_st(rets, 7, 0); /* we don't do nanoseconds */
  71. }
  72. static void rtas_power_off(sPAPREnvironment *spapr,
  73. uint32_t token, uint32_t nargs, target_ulong args,
  74. uint32_t nret, target_ulong rets)
  75. {
  76. if (nargs != 2 || nret != 1) {
  77. rtas_st(rets, 0, -3);
  78. return;
  79. }
  80. qemu_system_shutdown_request();
  81. rtas_st(rets, 0, 0);
  82. }
  83. static void rtas_query_cpu_stopped_state(sPAPREnvironment *spapr,
  84. uint32_t token, uint32_t nargs,
  85. target_ulong args,
  86. uint32_t nret, target_ulong rets)
  87. {
  88. target_ulong id;
  89. CPUState *env;
  90. if (nargs != 1 || nret != 2) {
  91. rtas_st(rets, 0, -3);
  92. return;
  93. }
  94. id = rtas_ld(args, 0);
  95. for (env = first_cpu; env; env = env->next_cpu) {
  96. if (env->cpu_index != id) {
  97. continue;
  98. }
  99. if (env->halted) {
  100. rtas_st(rets, 1, 0);
  101. } else {
  102. rtas_st(rets, 1, 2);
  103. }
  104. rtas_st(rets, 0, 0);
  105. return;
  106. }
  107. /* Didn't find a matching cpu */
  108. rtas_st(rets, 0, -3);
  109. }
  110. static void rtas_start_cpu(sPAPREnvironment *spapr,
  111. uint32_t token, uint32_t nargs,
  112. target_ulong args,
  113. uint32_t nret, target_ulong rets)
  114. {
  115. target_ulong id, start, r3;
  116. CPUState *env;
  117. if (nargs != 3 || nret != 1) {
  118. rtas_st(rets, 0, -3);
  119. return;
  120. }
  121. id = rtas_ld(args, 0);
  122. start = rtas_ld(args, 1);
  123. r3 = rtas_ld(args, 2);
  124. for (env = first_cpu; env; env = env->next_cpu) {
  125. if (env->cpu_index != id) {
  126. continue;
  127. }
  128. if (!env->halted) {
  129. rtas_st(rets, 0, -1);
  130. return;
  131. }
  132. env->msr = (1ULL << MSR_SF) | (1ULL << MSR_ME);
  133. env->nip = start;
  134. env->gpr[3] = r3;
  135. env->halted = 0;
  136. qemu_cpu_kick(env);
  137. rtas_st(rets, 0, 0);
  138. return;
  139. }
  140. /* Didn't find a matching cpu */
  141. rtas_st(rets, 0, -3);
  142. }
  143. static struct rtas_call {
  144. const char *name;
  145. spapr_rtas_fn fn;
  146. } rtas_table[TOKEN_MAX];
  147. struct rtas_call *rtas_next = rtas_table;
  148. target_ulong spapr_rtas_call(sPAPREnvironment *spapr,
  149. uint32_t token, uint32_t nargs, target_ulong args,
  150. uint32_t nret, target_ulong rets)
  151. {
  152. if ((token >= TOKEN_BASE)
  153. && ((token - TOKEN_BASE) < TOKEN_MAX)) {
  154. struct rtas_call *call = rtas_table + (token - TOKEN_BASE);
  155. if (call->fn) {
  156. call->fn(spapr, token, nargs, args, nret, rets);
  157. return H_SUCCESS;
  158. }
  159. }
  160. /* HACK: Some Linux early debug code uses RTAS display-character,
  161. * but assumes the token value is 0xa (which it is on some real
  162. * machines) without looking it up in the device tree. This
  163. * special case makes this work */
  164. if (token == 0xa) {
  165. rtas_display_character(spapr, 0xa, nargs, args, nret, rets);
  166. return H_SUCCESS;
  167. }
  168. hcall_dprintf("Unknown RTAS token 0x%x\n", token);
  169. rtas_st(rets, 0, -3);
  170. return H_PARAMETER;
  171. }
  172. void spapr_rtas_register(const char *name, spapr_rtas_fn fn)
  173. {
  174. assert(rtas_next < (rtas_table + TOKEN_MAX));
  175. rtas_next->name = name;
  176. rtas_next->fn = fn;
  177. rtas_next++;
  178. }
  179. int spapr_rtas_device_tree_setup(void *fdt, target_phys_addr_t rtas_addr,
  180. target_phys_addr_t rtas_size)
  181. {
  182. int ret;
  183. int i;
  184. ret = fdt_add_mem_rsv(fdt, rtas_addr, rtas_size);
  185. if (ret < 0) {
  186. fprintf(stderr, "Couldn't add RTAS reserve entry: %s\n",
  187. fdt_strerror(ret));
  188. return ret;
  189. }
  190. ret = qemu_devtree_setprop_cell(fdt, "/rtas", "linux,rtas-base",
  191. rtas_addr);
  192. if (ret < 0) {
  193. fprintf(stderr, "Couldn't add linux,rtas-base property: %s\n",
  194. fdt_strerror(ret));
  195. return ret;
  196. }
  197. ret = qemu_devtree_setprop_cell(fdt, "/rtas", "linux,rtas-entry",
  198. rtas_addr);
  199. if (ret < 0) {
  200. fprintf(stderr, "Couldn't add linux,rtas-entry property: %s\n",
  201. fdt_strerror(ret));
  202. return ret;
  203. }
  204. ret = qemu_devtree_setprop_cell(fdt, "/rtas", "rtas-size",
  205. rtas_size);
  206. if (ret < 0) {
  207. fprintf(stderr, "Couldn't add rtas-size property: %s\n",
  208. fdt_strerror(ret));
  209. return ret;
  210. }
  211. for (i = 0; i < TOKEN_MAX; i++) {
  212. struct rtas_call *call = &rtas_table[i];
  213. if (!call->fn) {
  214. continue;
  215. }
  216. ret = qemu_devtree_setprop_cell(fdt, "/rtas", call->name,
  217. i + TOKEN_BASE);
  218. if (ret < 0) {
  219. fprintf(stderr, "Couldn't add rtas token for %s: %s\n",
  220. call->name, fdt_strerror(ret));
  221. return ret;
  222. }
  223. }
  224. return 0;
  225. }
  226. static void register_core_rtas(void)
  227. {
  228. spapr_rtas_register("display-character", rtas_display_character);
  229. spapr_rtas_register("get-time-of-day", rtas_get_time_of_day);
  230. spapr_rtas_register("power-off", rtas_power_off);
  231. spapr_rtas_register("query-cpu-stopped-state",
  232. rtas_query_cpu_stopped_state);
  233. spapr_rtas_register("start-cpu", rtas_start_cpu);
  234. }
  235. device_init(register_core_rtas);