diff --git a/shared/hypercall_params.h b/shared/hypercall_params.h
index b7ec132ac6572dc5b3c2514b13687770d44753b0..c804f8039287ad3f6b5e339bd4ae12efe911765b 100644
--- a/shared/hypercall_params.h
+++ b/shared/hypercall_params.h
@@ -3,8 +3,25 @@
 
 #include <stdint.h>
 
+// --- DEFINE ---
+
+#define HYPERCALL_SHARED_ADDR 0xC0000000
+#define HYPERCALL_BUFF_SIZE 4096
+
+// --- SHARED STRUCT ---
+
 typedef struct {
-    uint32_t us;  // delay in micro-seconds
+
+    uint64_t msg;    // address of the message
+} __attribute__((packed)) hyper_virtual_console_params_t;
+
+typedef struct {
+
+    uint32_t us;    // delay in micro-seconds
 } __attribute__((packed)) hyper_timer_sleep_params_t;
 
+// --- FUNCTION ---
+
+// ...
+
 #endif
diff --git a/vmm/vmm_main.c b/vmm/vmm_main.c
index e62c8006165947524dea77bdb66227d41bfc119e..ea766d95819629d4900934b96d88e2d83f9109c5 100644
--- a/vmm/vmm_main.c
+++ b/vmm/vmm_main.c
@@ -12,6 +12,9 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
+#include "states.h"
+#include "shared/hypercall_params.h"
+
 // -- DEFINE --
 
 #define KVM_API_VERSION   12
@@ -24,10 +27,11 @@ int main(int argc, char* argv[])
     int kvmfd, vmfd, vcpufd;
     struct kvm_sregs sregs;
     struct kvm_run *run;
-    size_t mmap_size;
-    uint8_t *mem;
+    int32_t mmap_size;
+    uint8_t *mem, *shared_buf;
 
     // -- 0. Handle Args --
+    if (argc != 2) err(1, "Number of args invalide");
     char *file_path = argv[1];
 
     // -- 1. Create a KVM device --
@@ -43,12 +47,12 @@ int main(int argc, char* argv[])
     vmfd = ioctl(kvmfd, KVM_CREATE_VM, (unsigned long)0);
     if (vmfd == -1) err(1, "KVM_CREATE_VM");
 
-    // -- 3. Allocate RAM for the VM --
+    // -- 3.1 Allocate RAM for the VM --
     mem = mmap(NULL, RAM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); // memory to hold the code
     if (!mem) err(1, "allocating guest memory");
 
-    // -- 4. Map allocated RAM into the VM’s address space --
-    struct kvm_userspace_memory_region region = {
+    // -- 3.2 Map allocated RAM into the VM’s address space --
+    struct kvm_userspace_memory_region region_mem = {
         .slot = 0,
         .guest_phys_addr = 0,
         .memory_size = RAM_SIZE,
@@ -56,9 +60,25 @@ int main(int argc, char* argv[])
         .flags = 0,
     };
 
-    int memreg_err = ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &region);
+    int memreg_err = ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &region_mem);
     if (memreg_err == -1) err(1, "KVM_SET_USER_MEMORY_REGION");
 
+    // -- 4.1 Allocate shared buffer for the VM and guest --
+    shared_buf = mmap(NULL, HYPERCALL_BUFF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); // memory to hold the code
+    if (!shared_buf) err(1, "allocating shared buffer memory");
+
+    // -- 4.2 Map allocated shared buffer into the VM’s address space --
+    struct kvm_userspace_memory_region region_shared_buf = {
+        .slot = 1,
+        .guest_phys_addr = HYPERCALL_SHARED_ADDR,
+        .memory_size = HYPERCALL_BUFF_SIZE,
+        .userspace_addr = (uint64_t)shared_buf,
+        .flags = 0,
+    };
+
+    int sharedbufreg_err = ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &region_shared_buf);
+    if (sharedbufreg_err == -1) err(1, "KVM_SET_USER_MEMORY_REGION");
+
     // -- 5. Load guest OS into VM’s RAM --
     // Open the binary file
     int binfd = open(file_path, O_RDONLY);
@@ -89,7 +109,7 @@ int main(int argc, char* argv[])
     // map the shared kvm_run structure and following data
     mmap_size = ioctl(kvmfd, KVM_GET_VCPU_MMAP_SIZE, NULL);
     if (mmap_size == -1) err(1, "KVM_GET_VCPU_MMAP_SIZE");
-    if (mmap_size < sizeof(*run)) errx(1, "KVM_GET_VCPU_MMAP_SIZE unexpectedly small");
+    if (mmap_size < (int32_t)sizeof(*run)) errx(1, "KVM_GET_VCPU_MMAP_SIZE unexpectedly small");
 
     // get memory-mapped file
     run = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, vcpufd, 0);
@@ -127,36 +147,41 @@ int main(int argc, char* argv[])
         // handle VM exits (blocking call)
         switch (run->exit_reason) {
 
-            case KVM_EXIT_HLT:
+            case KVM_EXIT_HLT:    // "halt" CPU instruction
+
                 puts("KVM_EXIT_HLT");
-                done = true;
                 break;
 
-            case KVM_EXIT_IO:
-                if (run->io.direction == KVM_EXIT_IO_OUT && run->io.size == 1
-                        && run->io.port == 0x3f8 && run->io.count == 1)
-                    putchar(*(((char *)run) + run->io.data_offset));
-                else
-                    errx(1, "unhandled KVM_EXIT_IO");
-                break;
+            case KVM_EXIT_IO:     // Encountered a PMIO VMexit
 
-            case KVM_EXIT_MMIO:
-                // !TODO!
-                done = true;
-                break;
+                if (run->io.direction == KVM_EXIT_IO_OUT) {
+
+                    uint8_t *addr = (uint8_t *)run + run->io.data_offset;
+                    uint8_t code = *(uint8_t *)addr;
+
+                    switch (code) {
+
+                        case 1: // console
+
+                            hyper_virtual_console_params_t *p = (hyper_virtual_console_params_t *)shared_buf;
+                            printf("%s\n", p->msg + mem);
+                            break;
+                    }
+                }
 
-            case KVM_EXIT_FAIL_ENTRY:
-                errx(1, "KVM_EXIT_FAIL_ENTRY: hardware_entry_failure_reason = 0x%llx", 
-                     (unsigned long long)run->fail_entry.hardware_entry_failure_reason);
                 done = true;
                 break;
 
-            case KVM_EXIT_INTERNAL_ERROR:
-                errx(1, "KVM_EXIT_INTERNAL_ERROR: suberror = 0x%x", run->internal.suberror);
-                done = true;
+            case KVM_EXIT_MMIO:   // Encountered a MMIO VMexit
+
+                // !TODO!
                 break;
 
-            default:
+            // handle errors
+            case KVM_EXIT_SHUTDOWN :
+            case KVM_EXIT_FAIL_ENTRY :
+            case KVM_EXIT_INTERNAL_ERROR :
+            default :
                 errx(1, "exit_reason = 0x%x", run->exit_reason);
                 done = true;
                 break;