blob: 739a95ecaaf7479217caa952e5388e9849ece013 [file] [log] [blame]
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +01001#include <linux/hrtimer.h>
2#include <linux/init.h>
3#include <linux/kernel.h>
4#include <linux/kthread.h>
Wedson Almeida Filhof9e11922018-08-12 15:54:31 +01005#include <linux/mm.h>
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +01006#include <linux/module.h>
7#include <linux/sched/task.h>
8#include <linux/slab.h>
9
Andrew Scull55704232018-08-10 17:19:54 +010010#include <hf/call.h>
11
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +010012struct hf_vcpu {
13 spinlock_t lock;
Andrew Scullb722f952018-09-27 15:39:10 +010014 struct hf_vm *vm;
Andrew Scull55704232018-08-10 17:19:54 +010015 uint32_t vcpu_index;
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +010016 struct task_struct *task;
17 struct hrtimer timer;
18 bool pending_irq;
19};
20
21struct hf_vm {
Andrew Scullb722f952018-09-27 15:39:10 +010022 uint32_t id;
Andrew Scullbb7ae412018-09-28 21:07:15 +010023 uint32_t vcpu_count;
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +010024 struct hf_vcpu *vcpu;
25};
26
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +010027static struct hf_vm *hf_vms;
Andrew Scullbb7ae412018-09-28 21:07:15 +010028static uint32_t hf_vm_count;
Wedson Almeida Filhof9e11922018-08-12 15:54:31 +010029static struct page *hf_send_page = NULL;
30static struct page *hf_recv_page = NULL;
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +010031
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +010032/**
33 * Wakes up the thread associated with the vcpu that owns the given timer. This
34 * is called when the timer the thread is waiting on expires.
35 */
36static enum hrtimer_restart hf_vcpu_timer_expired(struct hrtimer *timer)
37{
38 struct hf_vcpu *vcpu = container_of(timer, struct hf_vcpu, timer);
39 wake_up_process(vcpu->task);
40 return HRTIMER_NORESTART;
41}
42
43/**
44 * This is the main loop of each vcpu.
45 */
46static int hf_vcpu_thread(void *data)
47{
48 struct hf_vcpu *vcpu = data;
Andrew Sculldc8cab52018-10-10 18:29:39 +010049 struct hf_vcpu_run_return ret;
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +010050
51 hrtimer_init(&vcpu->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
52 vcpu->timer.function = &hf_vcpu_timer_expired;
53
54 while (!kthread_should_stop()) {
55 unsigned long flags;
56 size_t irqs;
57
58 set_current_state(TASK_RUNNING);
59
60 /* Determine if we must interrupt the vcpu. */
61 spin_lock_irqsave(&vcpu->lock, flags);
62 irqs = vcpu->pending_irq ? 1 : 0;
63 vcpu->pending_irq = false;
64 spin_unlock_irqrestore(&vcpu->lock, flags);
65
Andrew Scullbb7ae412018-09-28 21:07:15 +010066 /* Call into Hafnium to run vcpu. */
Andrew Scullb722f952018-09-27 15:39:10 +010067 ret = hf_vcpu_run(vcpu->vm->id, vcpu->vcpu_index);
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +010068
Andrew Sculldc8cab52018-10-10 18:29:39 +010069 switch (ret.code) {
Andrew Scullb3a61b52018-09-17 14:30:34 +010070 /* Yield (forcibly or voluntarily). */
71 case HF_VCPU_RUN_YIELD:
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +010072 break;
73
Andrew Scullb3a61b52018-09-17 14:30:34 +010074 /* WFI. */
75 case HF_VCPU_RUN_WAIT_FOR_INTERRUPT:
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +010076 set_current_state(TASK_INTERRUPTIBLE);
77 if (kthread_should_stop())
78 break;
79 schedule();
80 break;
81
Andrew Scullb3a61b52018-09-17 14:30:34 +010082 /* Wake up another vcpu. */
83 case HF_VCPU_RUN_WAKE_UP:
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +010084 {
Andrew Scull0973a2e2018-10-05 11:11:24 +010085 struct hf_vm *vm;
Andrew Sculldc8cab52018-10-10 18:29:39 +010086 if (ret.wake_up.vm_id > hf_vm_count)
Andrew Scull0973a2e2018-10-05 11:11:24 +010087 break;
Andrew Sculldc8cab52018-10-10 18:29:39 +010088 vm = &hf_vms[ret.wake_up.vm_id - 1];
89 if (ret.wake_up.vcpu < vm->vcpu_count) {
90 wake_up_process(vm->vcpu[ret.wake_up.vcpu].task);
91 } else if (ret.wake_up.vcpu == HF_INVALID_VCPU) {
Andrew Scull0973a2e2018-10-05 11:11:24 +010092 /* TODO: pick one to interrupt. */
93 pr_warning("No vcpu to wake.");
94 }
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +010095 }
96 break;
Wedson Almeida Filhof9e11922018-08-12 15:54:31 +010097
Andrew Scullb3a61b52018-09-17 14:30:34 +010098 /* Response available. */
Andrew Scull0973a2e2018-10-05 11:11:24 +010099 case HF_VCPU_RUN_MESSAGE:
Wedson Almeida Filhof9e11922018-08-12 15:54:31 +0100100 {
Andrew Sculldc8cab52018-10-10 18:29:39 +0100101 uint32_t i;
Wedson Almeida Filhof9e11922018-08-12 15:54:31 +0100102 const char *buf = page_address(hf_recv_page);
Andrew Scull0973a2e2018-10-05 11:11:24 +0100103 pr_info("Received response from vm %u (%u bytes): ",
Andrew Sculldc8cab52018-10-10 18:29:39 +0100104 vcpu->vm->id, ret.message.size);
105 for (i = 0; i < ret.message.size; i++)
Wedson Almeida Filhof9e11922018-08-12 15:54:31 +0100106 printk(KERN_CONT "%c", buf[i]);
107 printk(KERN_CONT "\n");
Andrew Scull0973a2e2018-10-05 11:11:24 +0100108 hf_mailbox_clear();
Wedson Almeida Filhof9e11922018-08-12 15:54:31 +0100109 }
110 break;
Andrew Sculldc8cab52018-10-10 18:29:39 +0100111
112 case HF_VCPU_RUN_SLEEP:
113 set_current_state(TASK_INTERRUPTIBLE);
114 if (kthread_should_stop())
115 break;
116 hrtimer_start(&vcpu->timer, ret.sleep.ns, HRTIMER_MODE_REL);
117 schedule();
118 hrtimer_cancel(&vcpu->timer);
119 break;
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +0100120 }
121 }
122
123 set_current_state(TASK_RUNNING);
124
125 return 0;
126}
127
128/**
Andrew Scullbb7ae412018-09-28 21:07:15 +0100129 * Frees all resources, including threads, associated with the Hafnium driver.
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +0100130 */
Andrew Scullbb7ae412018-09-28 21:07:15 +0100131static void hf_free_resources(uint32_t vm_count)
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +0100132{
Andrew Scullbb7ae412018-09-28 21:07:15 +0100133 uint32_t i, j;
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +0100134
135 /*
136 * First stop all worker threads. We need to do this before freeing
137 * resources because workers may reference each other, so it is only
138 * safe to free resources after they have all stopped.
139 */
140 for (i = 0; i < vm_count; i++) {
Andrew Scullb3a61b52018-09-17 14:30:34 +0100141 struct hf_vm *vm = &hf_vms[i];
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +0100142 for (j = 0; j < vm->vcpu_count; j++)
143 kthread_stop(vm->vcpu[j].task);
144 }
145
146 /* Free resources. */
147 for (i = 0; i < vm_count; i++) {
Andrew Scullb3a61b52018-09-17 14:30:34 +0100148 struct hf_vm *vm = &hf_vms[i];
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +0100149 for (j = 0; j < vm->vcpu_count; j++)
150 put_task_struct(vm->vcpu[j].task);
151 kfree(vm->vcpu);
152 }
153
154 kfree(hf_vms);
155}
156
157static ssize_t hf_interrupt_store(struct kobject *kobj,
158 struct kobj_attribute *attr, const char *buf,
159 size_t count)
160{
161 struct hf_vcpu *vcpu;
162 unsigned long flags;
163 struct task_struct *task;
164
165 /* TODO: Parse input to determine which vcpu to interrupt. */
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +0100166 /* TODO: Check bounds. */
167
Andrew Scullb3a61b52018-09-17 14:30:34 +0100168 vcpu = &hf_vms[0].vcpu[0];
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +0100169
170 spin_lock_irqsave(&vcpu->lock, flags);
171 vcpu->pending_irq = true;
172 /* TODO: Do we need to increment the task's ref count here? */
173 task = vcpu->task;
174 spin_unlock_irqrestore(&vcpu->lock, flags);
175
176 /* Wake up the task. If it's already running, kick it out. */
177 /* TODO: There's a race here: the kick may happen right before we go
178 * to the hypervisor. */
179 if (wake_up_process(task) == 0)
180 kick_process(task);
181
182 return count;
183}
184
Wedson Almeida Filhof9e11922018-08-12 15:54:31 +0100185static ssize_t hf_send_store(struct kobject *kobj, struct kobj_attribute *attr,
186 const char *buf, size_t count)
187{
Andrew Scullbb7ae412018-09-28 21:07:15 +0100188 int64_t ret;
Wedson Almeida Filhof9e11922018-08-12 15:54:31 +0100189 struct hf_vm *vm;
190
Andrew Scull0973a2e2018-10-05 11:11:24 +0100191 count = min_t(size_t, count, HF_MAILBOX_SIZE);
Wedson Almeida Filhof9e11922018-08-12 15:54:31 +0100192
193 /* Copy data to send buffer. */
194 memcpy(page_address(hf_send_page), buf, count);
Andrew Scullb722f952018-09-27 15:39:10 +0100195
196 vm = &hf_vms[0];
Andrew Scull0973a2e2018-10-05 11:11:24 +0100197 ret = hf_mailbox_send(vm->id, count);
Wedson Almeida Filhof9e11922018-08-12 15:54:31 +0100198 if (ret < 0)
199 return -EAGAIN;
200
Andrew Scull0973a2e2018-10-05 11:11:24 +0100201 if (ret == HF_INVALID_VCPU) {
202 /*
203 * TODO: We need to interrupt some vcpu because none are waiting
204 * for data.
205 */
206 pr_warning("No vcpu to receive message.");
207 return -ENOSYS;
208 }
209
210 if (ret >= vm->vcpu_count)
Wedson Almeida Filhof9e11922018-08-12 15:54:31 +0100211 return -EINVAL;
212
Andrew Scull0973a2e2018-10-05 11:11:24 +0100213 /* Wake up the vcpu that is going to process the data. */
214 /* TODO: There's a race where thread may get wake up before it
215 * goes to sleep. Fix this. */
216 wake_up_process(vm->vcpu[ret].task);
Wedson Almeida Filhof9e11922018-08-12 15:54:31 +0100217
218 return count;
219}
220
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +0100221static struct kobject *hf_sysfs_obj = NULL;
222static struct kobj_attribute interrupt_attr =
223 __ATTR(interrupt, 0200, NULL, hf_interrupt_store);
Wedson Almeida Filhof9e11922018-08-12 15:54:31 +0100224static struct kobj_attribute send_attr =
225 __ATTR(send, 0200, NULL, hf_send_store);
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +0100226
227/**
Andrew Scullbb7ae412018-09-28 21:07:15 +0100228 * Initializes the Hafnium driver's sysfs interface.
229 */
230static void __init hf_init_sysfs(void)
231{
232 int ret;
233
234 /* Create the sysfs interface to interrupt vcpus. */
235 hf_sysfs_obj = kobject_create_and_add("hafnium", kernel_kobj);
236 if (!hf_sysfs_obj) {
237 pr_err("Unable to create sysfs object");
238 } else {
239 ret = sysfs_create_file(hf_sysfs_obj, &interrupt_attr.attr);
240 if (ret)
241 pr_err("Unable to create 'interrupt' sysfs file");
242
243 ret = sysfs_create_file(hf_sysfs_obj, &send_attr.attr);
244 if (ret)
245 pr_err("Unable to create 'send' sysfs file");
246 }
247}
248
249/**
250 * Initializes the Hafnium driver by creating a thread for each vCPU of each
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +0100251 * virtual machine.
252 */
253static int __init hf_init(void)
254{
Andrew Scullbb7ae412018-09-28 21:07:15 +0100255 int64_t ret;
256 uint32_t i, j;
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +0100257
Wedson Almeida Filhof9e11922018-08-12 15:54:31 +0100258 /* Allocate a page for send and receive buffers. */
259 hf_send_page = alloc_page(GFP_KERNEL);
260 if (!hf_send_page) {
261 pr_err("Unable to allocate send buffer\n");
262 return -ENOMEM;
263 }
264
265 hf_recv_page = alloc_page(GFP_KERNEL);
266 if (!hf_recv_page) {
267 __free_page(hf_send_page);
268 pr_err("Unable to allocate receive buffer\n");
269 return -ENOMEM;
270 }
271
272 /*
273 * Configure both addresses. Once configured, we cannot free these pages
274 * because the hypervisor will use them, even if the module is
275 * unloaded.
276 */
Andrew Scull55704232018-08-10 17:19:54 +0100277 ret = hf_vm_configure(page_to_phys(hf_send_page),
278 page_to_phys(hf_recv_page));
Wedson Almeida Filhof9e11922018-08-12 15:54:31 +0100279 if (ret) {
280 __free_page(hf_send_page);
281 __free_page(hf_recv_page);
282 /* TODO: We may want to grab this information from hypervisor
283 * and go from there. */
284 pr_err("Unable to configure VM\n");
285 return -EIO;
286 }
287
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +0100288 /* Get the number of VMs and allocate storage for them. */
Andrew Scull55704232018-08-10 17:19:54 +0100289 ret = hf_vm_get_count();
Andrew Scullb722f952018-09-27 15:39:10 +0100290 if (ret < 1) {
Andrew Scullbb7ae412018-09-28 21:07:15 +0100291 pr_err("Unable to retrieve number of VMs: %lld\n", ret);
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +0100292 return ret;
293 }
294
Andrew Scullb722f952018-09-27 15:39:10 +0100295 /* Only track the secondary VMs. */
296 hf_vm_count = ret - 1;
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +0100297 hf_vms = kmalloc(sizeof(struct hf_vm) * hf_vm_count, GFP_KERNEL);
298 if (!hf_vms)
299 return -ENOMEM;
300
301 /* Initialize each VM. */
302 for (i = 0; i < hf_vm_count; i++) {
Andrew Scullb3a61b52018-09-17 14:30:34 +0100303 struct hf_vm *vm = &hf_vms[i];
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +0100304
Andrew Scullb722f952018-09-27 15:39:10 +0100305 /* Adjust the ID as only the secondaries are tracked. */
306 vm->id = i + 1;
307
308 ret = hf_vcpu_get_count(vm->id);
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +0100309 if (ret < 0) {
Andrew Scullbb7ae412018-09-28 21:07:15 +0100310 pr_err("HF_VCPU_GET_COUNT failed for vm=%u: %lld",
311 vm->id, ret);
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +0100312 hf_free_resources(i);
313 return ret;
314 }
315
316 vm->vcpu_count = ret;
317 vm->vcpu = kmalloc(sizeof(struct hf_vcpu) * vm->vcpu_count,
318 GFP_KERNEL);
319 if (!vm->vcpu) {
Andrew Scullbb7ae412018-09-28 21:07:15 +0100320 pr_err("No memory for %u vcpus for vm %u",
Andrew Scullb722f952018-09-27 15:39:10 +0100321 vm->vcpu_count, vm->id);
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +0100322 hf_free_resources(i);
323 return -ENOMEM;
324 }
325
326 /* Create a kernel thread for each vcpu. */
327 for (j = 0; j < vm->vcpu_count; j++) {
Andrew Scullb3a61b52018-09-17 14:30:34 +0100328 struct hf_vcpu *vcpu = &vm->vcpu[j];
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +0100329 vcpu->task = kthread_create(hf_vcpu_thread, vcpu,
Andrew Scullbb7ae412018-09-28 21:07:15 +0100330 "vcpu_thread_%u_%u",
Andrew Scullb722f952018-09-27 15:39:10 +0100331 vm->id, j);
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +0100332 if (IS_ERR(vcpu->task)) {
Andrew Scullbb7ae412018-09-28 21:07:15 +0100333 pr_err("Error creating task (vm=%u,vcpu=%u)"
Andrew Scullb722f952018-09-27 15:39:10 +0100334 ": %ld\n", vm->id, j, PTR_ERR(vcpu->task));
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +0100335 vm->vcpu_count = j;
336 hf_free_resources(i + 1);
337 return PTR_ERR(vcpu->task);
338 }
339
340 get_task_struct(vcpu->task);
341 spin_lock_init(&vcpu->lock);
Andrew Scullb722f952018-09-27 15:39:10 +0100342 vcpu->vm = vm;
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +0100343 vcpu->vcpu_index = j;
344 vcpu->pending_irq = false;
345 }
346 }
347
348 /* Start running threads now that all is initialized. */
349 for (i = 0; i < hf_vm_count; i++) {
Andrew Scullb3a61b52018-09-17 14:30:34 +0100350 struct hf_vm *vm = &hf_vms[i];
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +0100351 for (j = 0; j < vm->vcpu_count; j++)
352 wake_up_process(vm->vcpu[j].task);
353 }
354
355 /* Dump vm/vcpu count info. */
Andrew Scullbb7ae412018-09-28 21:07:15 +0100356 pr_info("Hafnium successfully loaded with %u VMs:\n", hf_vm_count);
Andrew Scullb722f952018-09-27 15:39:10 +0100357 for (i = 0; i < hf_vm_count; i++) {
358 struct hf_vm *vm = &hf_vms[i];
Andrew Scullbb7ae412018-09-28 21:07:15 +0100359 pr_info("\tVM %u: %u vCPUS\n", vm->id, vm->vcpu_count);
Andrew Scullb722f952018-09-27 15:39:10 +0100360 }
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +0100361
Andrew Scullbb7ae412018-09-28 21:07:15 +0100362 hf_init_sysfs();
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +0100363
364 return 0;
365}
366
367/**
Andrew Scullbb7ae412018-09-28 21:07:15 +0100368 * Frees up all resources used by the Hafnium driver in preparation for
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +0100369 * unloading it.
370 */
371static void __exit hf_exit(void)
372{
373 if (hf_sysfs_obj)
374 kobject_put(hf_sysfs_obj);
375
Andrew Scullbb7ae412018-09-28 21:07:15 +0100376 pr_info("Preparing to unload Hafnium\n");
Wedson Almeida Filho2f62b422018-06-19 06:44:32 +0100377 hf_free_resources(hf_vm_count);
378 pr_info("Hafnium ready to unload\n");
379}
380
381MODULE_LICENSE("GPL");
382
383module_init(hf_init);
384module_exit(hf_exit);