Add partial support for sending request and receiving reply.
diff --git a/main.c b/main.c
index 973bd97..a0c8641 100644
--- a/main.c
+++ b/main.c
@@ -2,6 +2,7 @@
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/kthread.h>
+#include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/sched/task.h>
 #include <linux/slab.h>
@@ -24,12 +25,18 @@
 
 static struct hf_vm *hf_vms;
 static long hf_vm_count;
-
+static struct page *hf_send_page = NULL;
+static struct page *hf_recv_page = NULL;
 
 /* TODO: Define constants below according to spec. Include shared header. */
-#define HF_VCPU_RUN       0xff00
-#define HF_VM_GET_COUNT   0xff01
-#define HF_VCPU_GET_COUNT 0xff02
+#define HF_VCPU_RUN         0xff00
+#define HF_VM_GET_COUNT     0xff01
+#define HF_VCPU_GET_COUNT   0xff02
+#define HF_VM_CONFIGURE     0xff03
+#define HF_RPC_REQUEST      0xff04
+#define HF_RPC_READ_REQUEST 0xff05
+#define HF_RPC_ACK          0xff06
+#define HF_RPC_REPLY        0xff07
 
 /**
  * Wakes up the thread associated with the vcpu that owns the given timer. This
@@ -102,6 +109,19 @@
 					wake_up_process(vm->vcpu[target].task);
 			}
 			break;
+
+		case 0x03: /* Response available. */
+			{
+				size_t i, count = ret >> 8;
+				const char *buf = page_address(hf_recv_page);
+				pr_info("Received response (%zu bytes): ",
+					count);
+				for (i = 0; i < count; i++)
+					printk(KERN_CONT "%c", buf[i]);
+				printk(KERN_CONT "\n");
+				hf_hvc(HF_RPC_ACK, 0, 0, 0);
+			}
+			break;
 		}
 	}
 
@@ -148,7 +168,6 @@
 	struct task_struct *task;
 
 	/* TODO: Parse input to determine which vcpu to interrupt. */
-	pr_info("Interrupting the first vcpu of the first vm\n");
 	/* TODO: Check bounds. */
 
 	vcpu = hf_vms[0].vcpu + 0;
@@ -168,9 +187,46 @@
 	return count;
 }
 
+static ssize_t hf_send_store(struct kobject *kobj, struct kobj_attribute *attr,
+			     const char *buf, size_t count)
+{
+	long ret;
+	struct hf_vm *vm;
+
+	/* TODO: Use constant. */
+	if (count > 4096)
+		count = 4096;
+
+	/* Copy data to send buffer. */
+	memcpy(page_address(hf_send_page), buf, count);
+	ret = hf_hvc(HF_RPC_REQUEST, 0, count, 0);
+	if (ret < 0)
+		return -EAGAIN;
+
+	vm = hf_vms + 0;
+	if (ret > vm->vcpu_count)
+		return -EINVAL;
+
+	if (ret == 0) {
+		/*
+		 * TODO: We need to interrupt some CPU because none is actually
+		 * waiting for data.
+		 */
+	} else {
+		/* Wake up the vcpu that is going to process the data. */
+		/* TODO: There's a race where thread may get wake up before it
+		 * goes to sleep. Fix this. */
+		wake_up_process(vm->vcpu[ret - 1].task);
+	}
+
+	return count;
+}
+
 static struct kobject *hf_sysfs_obj = NULL;
 static struct kobj_attribute interrupt_attr =
 	__ATTR(interrupt, 0200, NULL, hf_interrupt_store);
+static struct kobj_attribute send_attr =
+	__ATTR(send, 0200, NULL, hf_send_store);
 
 /**
  * Initializes the hafnium driver by creating a thread for each vCPU of each
@@ -181,6 +237,36 @@
 	long ret;
 	long i, j;
 
+	/* Allocate a page for send and receive buffers. */
+	hf_send_page = alloc_page(GFP_KERNEL);
+	if (!hf_send_page) {
+		pr_err("Unable to allocate send buffer\n");
+		return -ENOMEM;
+	}
+
+	hf_recv_page = alloc_page(GFP_KERNEL);
+	if (!hf_recv_page) {
+		__free_page(hf_send_page);
+		pr_err("Unable to allocate receive buffer\n");
+		return -ENOMEM;
+	}
+
+	/*
+	 * Configure both addresses. Once configured, we cannot free these pages
+	 * because the hypervisor will use them, even if the module is
+	 * unloaded.
+	 */
+	ret = hf_hvc(HF_VM_CONFIGURE, (size_t)page_to_phys(hf_send_page),
+		     (size_t)page_to_phys(hf_recv_page), 0);
+	if (ret) {
+		__free_page(hf_send_page);
+		__free_page(hf_recv_page);
+		/* TODO: We may want to grab this information from hypervisor
+		 * and go from there. */
+		pr_err("Unable to configure VM\n");
+		return -EIO;
+	}
+
 	/* Get the number of VMs and allocate storage for them. */
 	ret = hf_hvc(HF_VM_GET_COUNT, 0, 0, 0);
 	if (ret < 0) {
@@ -257,6 +343,10 @@
 		ret = sysfs_create_file(hf_sysfs_obj, &interrupt_attr.attr);
 		if (ret)
 			pr_err("Unable to create 'interrupt' sysfs file");
+
+		ret = sysfs_create_file(hf_sysfs_obj, &send_attr.attr);
+		if (ret)
+			pr_err("Unable to create 'send' sysfs file");
 	}
 
 	return 0;