xref: /openbmc/qemu/hw/s390x/s390-virtio-ccw.c (revision 77a8257e)
1 /*
2  * virtio ccw machine
3  *
4  * Copyright 2012 IBM Corp.
5  * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
6  *
7  * This work is licensed under the terms of the GNU GPL, version 2 or (at
8  * your option) any later version. See the COPYING file in the top-level
9  * directory.
10  */
11 
12 #include "hw/boards.h"
13 #include "exec/address-spaces.h"
14 #include "s390-virtio.h"
15 #include "hw/s390x/sclp.h"
16 #include "hw/s390x/s390_flic.h"
17 #include "ioinst.h"
18 #include "css.h"
19 #include "virtio-ccw.h"
20 #include "qemu/config-file.h"
21 #include "s390-pci-bus.h"
22 
23 #define TYPE_S390_CCW_MACHINE               "s390-ccw-machine"
24 
25 #define S390_CCW_MACHINE(obj) \
26     OBJECT_CHECK(S390CcwMachineState, (obj), TYPE_S390_CCW_MACHINE)
27 
28 typedef struct S390CcwMachineState {
29     /*< private >*/
30     MachineState parent_obj;
31 
32     /*< public >*/
33     bool aes_key_wrap;
34     bool dea_key_wrap;
35 } S390CcwMachineState;
36 
37 void io_subsystem_reset(void)
38 {
39     DeviceState *css, *sclp, *flic;
40 
41     css = DEVICE(object_resolve_path_type("", "virtual-css-bridge", NULL));
42     if (css) {
43         qdev_reset_all(css);
44     }
45     sclp = DEVICE(object_resolve_path_type("",
46                   "s390-sclp-event-facility", NULL));
47     if (sclp) {
48         qdev_reset_all(sclp);
49     }
50     flic = DEVICE(object_resolve_path_type("", "s390-flic", NULL));
51     if (flic) {
52         qdev_reset_all(flic);
53     }
54 }
55 
56 static int virtio_ccw_hcall_notify(const uint64_t *args)
57 {
58     uint64_t subch_id = args[0];
59     uint64_t queue = args[1];
60     SubchDev *sch;
61     int cssid, ssid, schid, m;
62 
63     if (ioinst_disassemble_sch_ident(subch_id, &m, &cssid, &ssid, &schid)) {
64         return -EINVAL;
65     }
66     sch = css_find_subch(m, cssid, ssid, schid);
67     if (!sch || !css_subch_visible(sch)) {
68         return -EINVAL;
69     }
70     if (queue >= VIRTIO_PCI_QUEUE_MAX) {
71         return -EINVAL;
72     }
73     virtio_queue_notify(virtio_ccw_get_vdev(sch), queue);
74     return 0;
75 
76 }
77 
78 static int virtio_ccw_hcall_early_printk(const uint64_t *args)
79 {
80     uint64_t mem = args[0];
81 
82     if (mem < ram_size) {
83         /* Early printk */
84         return 0;
85     }
86     return -EINVAL;
87 }
88 
89 static void virtio_ccw_register_hcalls(void)
90 {
91     s390_register_virtio_hypercall(KVM_S390_VIRTIO_CCW_NOTIFY,
92                                    virtio_ccw_hcall_notify);
93     /* Tolerate early printk. */
94     s390_register_virtio_hypercall(KVM_S390_VIRTIO_NOTIFY,
95                                    virtio_ccw_hcall_early_printk);
96 }
97 
98 static void ccw_init(MachineState *machine)
99 {
100     ram_addr_t my_ram_size = machine->ram_size;
101     MemoryRegion *sysmem = get_system_memory();
102     MemoryRegion *ram = g_new(MemoryRegion, 1);
103     sclpMemoryHotplugDev *mhd = init_sclp_memory_hotplug_dev();
104     uint8_t *storage_keys;
105     int ret;
106     VirtualCssBus *css_bus;
107     DeviceState *dev;
108     QemuOpts *opts = qemu_opts_find(qemu_find_opts("memory"), NULL);
109     ram_addr_t pad_size = 0;
110     ram_addr_t maxmem = qemu_opt_get_size(opts, "maxmem", my_ram_size);
111     ram_addr_t standby_mem_size = maxmem - my_ram_size;
112     uint64_t kvm_limit;
113 
114     /* The storage increment size is a multiple of 1M and is a power of 2.
115      * The number of storage increments must be MAX_STORAGE_INCREMENTS or fewer.
116      * The variable 'mhd->increment_size' is an exponent of 2 that can be
117      * used to calculate the size (in bytes) of an increment. */
118     mhd->increment_size = 20;
119     while ((my_ram_size >> mhd->increment_size) > MAX_STORAGE_INCREMENTS) {
120         mhd->increment_size++;
121     }
122     while ((standby_mem_size >> mhd->increment_size) > MAX_STORAGE_INCREMENTS) {
123         mhd->increment_size++;
124     }
125 
126     /* The core and standby memory areas need to be aligned with
127      * the increment size.  In effect, this can cause the
128      * user-specified memory size to be rounded down to align
129      * with the nearest increment boundary. */
130     standby_mem_size = standby_mem_size >> mhd->increment_size
131                                         << mhd->increment_size;
132     my_ram_size = my_ram_size >> mhd->increment_size
133                               << mhd->increment_size;
134 
135     /* let's propagate the changed ram size into the global variable. */
136     ram_size = my_ram_size;
137     machine->maxram_size = my_ram_size + standby_mem_size;
138 
139     ret = s390_set_memory_limit(machine->maxram_size, &kvm_limit);
140     if (ret == -E2BIG) {
141         hw_error("qemu: host supports a maximum of %" PRIu64 " GB",
142                  kvm_limit >> 30);
143     } else if (ret) {
144         hw_error("qemu: setting the guest size failed");
145     }
146 
147     /* get a BUS */
148     css_bus = virtual_css_bus_init();
149     s390_sclp_init();
150     s390_init_ipl_dev(machine->kernel_filename, machine->kernel_cmdline,
151                       machine->initrd_filename, "s390-ccw.img", true);
152     s390_flic_init();
153 
154     dev = qdev_create(NULL, TYPE_S390_PCI_HOST_BRIDGE);
155     object_property_add_child(qdev_get_machine(), TYPE_S390_PCI_HOST_BRIDGE,
156                               OBJECT(dev), NULL);
157     qdev_init_nofail(dev);
158 
159     /* register hypercalls */
160     virtio_ccw_register_hcalls();
161 
162     /* allocate RAM for core */
163     memory_region_init_ram(ram, NULL, "s390.ram", my_ram_size, &error_abort);
164     vmstate_register_ram_global(ram);
165     memory_region_add_subregion(sysmem, 0, ram);
166 
167     /* If the size of ram is not on a MEM_SECTION_SIZE boundary,
168        calculate the pad size necessary to force this boundary. */
169     if (standby_mem_size) {
170         if (my_ram_size % MEM_SECTION_SIZE) {
171             pad_size = MEM_SECTION_SIZE - my_ram_size % MEM_SECTION_SIZE;
172         }
173         my_ram_size += standby_mem_size + pad_size;
174         mhd->pad_size = pad_size;
175         mhd->standby_mem_size = standby_mem_size;
176     }
177 
178     /* allocate storage keys */
179     storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE);
180 
181     /* init CPUs */
182     s390_init_cpus(machine->cpu_model, storage_keys);
183 
184     if (kvm_enabled()) {
185         kvm_s390_enable_css_support(s390_cpu_addr2state(0));
186     }
187     /*
188      * Create virtual css and set it as default so that non mcss-e
189      * enabled guests only see virtio devices.
190      */
191     ret = css_create_css_image(VIRTUAL_CSSID, true);
192     assert(ret == 0);
193 
194     /* Create VirtIO network adapters */
195     s390_create_virtio_net(BUS(css_bus), "virtio-net-ccw");
196 
197     /* Register savevm handler for guest TOD clock */
198     register_savevm(NULL, "todclock", 0, 1,
199                     gtod_save, gtod_load, kvm_state);
200 }
201 
202 static void ccw_machine_class_init(ObjectClass *oc, void *data)
203 {
204     MachineClass *mc = MACHINE_CLASS(oc);
205     NMIClass *nc = NMI_CLASS(oc);
206 
207     mc->name = "s390-ccw-virtio";
208     mc->alias = "s390-ccw";
209     mc->desc = "VirtIO-ccw based S390 machine";
210     mc->init = ccw_init;
211     mc->block_default_type = IF_VIRTIO;
212     mc->no_cdrom = 1;
213     mc->no_floppy = 1;
214     mc->no_serial = 1;
215     mc->no_parallel = 1;
216     mc->no_sdcard = 1;
217     mc->use_sclp = 1;
218     mc->max_cpus = 255;
219     nc->nmi_monitor_handler = s390_nmi;
220 }
221 
222 static inline bool machine_get_aes_key_wrap(Object *obj, Error **errp)
223 {
224     S390CcwMachineState *ms = S390_CCW_MACHINE(obj);
225 
226     return ms->aes_key_wrap;
227 }
228 
229 static inline void machine_set_aes_key_wrap(Object *obj, bool value,
230                                             Error **errp)
231 {
232     S390CcwMachineState *ms = S390_CCW_MACHINE(obj);
233 
234     ms->aes_key_wrap = value;
235 }
236 
237 static inline bool machine_get_dea_key_wrap(Object *obj, Error **errp)
238 {
239     S390CcwMachineState *ms = S390_CCW_MACHINE(obj);
240 
241     return ms->dea_key_wrap;
242 }
243 
244 static inline void machine_set_dea_key_wrap(Object *obj, bool value,
245                                             Error **errp)
246 {
247     S390CcwMachineState *ms = S390_CCW_MACHINE(obj);
248 
249     ms->dea_key_wrap = value;
250 }
251 
252 static inline void s390_machine_initfn(Object *obj)
253 {
254     object_property_add_bool(obj, "aes-key-wrap",
255                              machine_get_aes_key_wrap,
256                              machine_set_aes_key_wrap, NULL);
257     object_property_set_description(obj, "aes-key-wrap",
258             "enable/disable AES key wrapping using the CPACF wrapping key",
259             NULL);
260     object_property_set_bool(obj, true, "aes-key-wrap", NULL);
261 
262     object_property_add_bool(obj, "dea-key-wrap",
263                              machine_get_dea_key_wrap,
264                              machine_set_dea_key_wrap, NULL);
265     object_property_set_description(obj, "dea-key-wrap",
266             "enable/disable DEA key wrapping using the CPACF wrapping key",
267             NULL);
268     object_property_set_bool(obj, true, "dea-key-wrap", NULL);
269 }
270 
271 static const TypeInfo ccw_machine_info = {
272     .name          = TYPE_S390_CCW_MACHINE,
273     .parent        = TYPE_MACHINE,
274     .instance_size = sizeof(S390CcwMachineState),
275     .instance_init = s390_machine_initfn,
276     .class_init    = ccw_machine_class_init,
277     .interfaces = (InterfaceInfo[]) {
278         { TYPE_NMI },
279         { }
280     },
281 };
282 
283 static void ccw_machine_register_types(void)
284 {
285     type_register_static(&ccw_machine_info);
286 }
287 
288 type_init(ccw_machine_register_types)
289