xref: /openbmc/qemu/hw/s390x/tod.c (revision 6f1e91f7)
1  /*
2   * TOD (Time Of Day) clock
3   *
4   * Copyright 2018 Red Hat, Inc.
5   * Author(s): David Hildenbrand <david@redhat.com>
6   *
7   * This work is licensed under the terms of the GNU GPL, version 2 or later.
8   * See the COPYING file in the top-level directory.
9   */
10  
11  #include "qemu/osdep.h"
12  #include "hw/s390x/tod.h"
13  #include "qapi/error.h"
14  #include "qemu/error-report.h"
15  #include "qemu/module.h"
16  #include "sysemu/kvm.h"
17  #include "sysemu/tcg.h"
18  #include "sysemu/qtest.h"
19  #include "migration/qemu-file-types.h"
20  #include "migration/register.h"
21  
22  void s390_init_tod(void)
23  {
24      Object *obj;
25  
26      if (kvm_enabled()) {
27          obj = object_new(TYPE_KVM_S390_TOD);
28      } else if (tcg_enabled()) {
29          obj = object_new(TYPE_QEMU_S390_TOD);
30      } else if (qtest_enabled()) {
31          return;
32      } else {
33          error_report("current accelerator not handled in s390_init_tod!");
34          abort();
35      }
36      object_property_add_child(qdev_get_machine(), TYPE_S390_TOD, obj);
37      object_unref(obj);
38  
39      qdev_realize(DEVICE(obj), NULL, &error_fatal);
40  }
41  
42  S390TODState *s390_get_todstate(void)
43  {
44      static S390TODState *ts;
45  
46      if (!ts) {
47          ts = S390_TOD(object_resolve_path_type("", TYPE_S390_TOD, NULL));
48      }
49  
50      return ts;
51  }
52  
53  #define S390_TOD_CLOCK_VALUE_MISSING    0x00
54  #define S390_TOD_CLOCK_VALUE_PRESENT    0x01
55  
56  static void s390_tod_save(QEMUFile *f, void *opaque)
57  {
58      S390TODState *td = opaque;
59      S390TODClass *tdc = S390_TOD_GET_CLASS(td);
60      Error *err = NULL;
61      S390TOD tod;
62  
63      tdc->get(td, &tod, &err);
64      if (err) {
65          warn_report_err(err);
66          error_printf("Guest clock will not be migrated "
67                       "which could cause the guest to hang.");
68          qemu_put_byte(f, S390_TOD_CLOCK_VALUE_MISSING);
69          return;
70      }
71  
72      qemu_put_byte(f, S390_TOD_CLOCK_VALUE_PRESENT);
73      qemu_put_byte(f, tod.high);
74      qemu_put_be64(f, tod.low);
75  }
76  
77  static int s390_tod_load(QEMUFile *f, void *opaque, int version_id)
78  {
79      S390TODState *td = opaque;
80      S390TODClass *tdc = S390_TOD_GET_CLASS(td);
81      Error *err = NULL;
82      S390TOD tod;
83  
84      if (qemu_get_byte(f) == S390_TOD_CLOCK_VALUE_MISSING) {
85          warn_report("Guest clock was not migrated. This could "
86                      "cause the guest to hang.");
87          return 0;
88      }
89  
90      tod.high = qemu_get_byte(f);
91      tod.low = qemu_get_be64(f);
92  
93      tdc->set(td, &tod, &err);
94      if (err) {
95          error_report_err(err);
96          return -1;
97      }
98      return 0;
99  }
100  
101  static SaveVMHandlers savevm_tod = {
102      .save_state = s390_tod_save,
103      .load_state = s390_tod_load,
104  };
105  
106  static void s390_tod_realize(DeviceState *dev, Error **errp)
107  {
108      S390TODState *td = S390_TOD(dev);
109  
110      /* Legacy migration interface */
111      register_savevm_live("todclock", 0, 1, &savevm_tod, td);
112  }
113  
114  static void s390_tod_class_init(ObjectClass *oc, void *data)
115  {
116      DeviceClass *dc = DEVICE_CLASS(oc);
117  
118      dc->desc = "TOD (Time Of Day) Clock";
119      dc->realize = s390_tod_realize;
120      set_bit(DEVICE_CATEGORY_MISC, dc->categories);
121  
122      /* We only have one TOD clock in the system attached to the machine */
123      dc->user_creatable = false;
124  }
125  
126  static const TypeInfo s390_tod_info = {
127      .name = TYPE_S390_TOD,
128      .parent = TYPE_DEVICE,
129      .instance_size = sizeof(S390TODState),
130      .class_init = s390_tod_class_init,
131      .class_size = sizeof(S390TODClass),
132      .abstract = true,
133  };
134  
135  static void register_types(void)
136  {
137      type_register_static(&s390_tod_info);
138  }
139  type_init(register_types);
140