2
0

spapr_rtas.c 8.7 KB

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