diff --git a/src/kernel/main.c b/src/kernel/main.c
index 2c4a24f..6993233 100644
--- a/src/kernel/main.c
+++ b/src/kernel/main.c
@@ -24,27 +24,27 @@ OF THIS SOFTWARE.
 #include <stdbool.h>
 
 enum tag_type {
-  BOOT_COMMAND     =  1,
-  LOADER_NAME      =  2,
-  BOOT_MODULES     =  3,
-  MEMORY_INFO      =  4,
-  BOOT_DEVICE      =  5,
-  MEMORY_MAP       =  6,
-  VBE_INFO         =  7,
-  FBUF_INFO        =  8,
-  ELF_SYMBOLS      =  9,
-  APM_TABLE        = 10,
-  EFI_I386_TABLE   = 11,
-  EFI_AMD64_TABLE  = 12,
-  SMBIOS_TABLE     = 13,
-  RSDP_ACPI1       = 14,
-  RSDP_ACPI2       = 15,
-  NETWORK_INFO     = 16,
-  EFI_MEMORY_MAP   = 17,
-  EFI_SERVICES     = 18,
-  EFI_I386_HANDLE  = 19,
-  EFI_AMD64_HANDLE = 20,
-  IMAGE_BASE_ADDR  = 21
+  BOOT_COMMAND     = 0x00000001,
+  LOADER_NAME      = 0x00000002,
+  BOOT_MODULES     = 0x00000003,
+  MEMORY_INFO      = 0x00000004,
+  BOOT_DEVICE      = 0x00000005,
+  MEMORY_MAP       = 0x00000006,
+  VBE_INFO         = 0x00000007,
+  FBUF_INFO        = 0x00000008,
+  ELF_SYMBOLS      = 0x00000009,
+  APM_TABLE        = 0x0000000a,
+  EFI_I386_TABLE   = 0x0000000b,
+  EFI_AMD64_TABLE  = 0x0000000c,
+  SMBIOS_TABLE     = 0x0000000d,
+  RSDP_ACPI1       = 0x0000000e,
+  RSDP_ACPI2       = 0x0000000f,
+  NETWORK_INFO     = 0x00000010,
+  EFI_MEMORY_MAP   = 0x00000011,
+  EFI_SERVICES     = 0x00000012,
+  EFI_I386_HANDLE  = 0x00000013,
+  EFI_AMD64_HANDLE = 0x00000014,
+  IMAGE_BASE_ADDR  = 0x00000015
 };
 
 struct tag_start {
@@ -72,6 +72,47 @@ struct mmap_tag_entry {
   uint32_t type;
 } __attribute__ ((__packed__));
 
+struct vbe_tag {
+  uint16_t mode;
+  uint16_t v2_segment;
+  uint16_t v2_offset;
+  uint16_t v2_length;
+  uint8_t control_info[512];
+  uint8_t mode_info[256];
+} __attribute__ ((__packed__));
+
+enum color_types {
+  PALETTE = 0x00,
+  RGB     = 0x01,
+  TEXT    = 0x02
+};
+
+struct fbuf_tag {
+  uint32_t address_low;
+  uint32_t address_high;
+  uint32_t pitch;
+  uint32_t width;
+  uint32_t height;
+  uint8_t bpp;
+  uint8_t color_type;
+  uint8_t padding;
+} __attribute__ ((__packed__));
+
+struct fbuf_palette_entry {
+  uint8_t red;
+  uint8_t green;
+  uint8_t blue;
+} __attribute__ ((__packed__));
+
+struct fbuf_rgb_info {
+  uint8_t red_pos;
+  uint8_t red_mask;
+  uint8_t green_pos;
+  uint8_t green_mask;
+  uint8_t blue_pos;
+  uint8_t blue_mask;
+} __attribute__ ((__packed__));
+
 struct boot_device_tag boot_device;
 bool have_boot_device = false;
 bool have_mmap = false;
@@ -88,14 +129,26 @@ enum error_codes {
 struct tag_start *tag_pointer;
 
 uint32_t main(void) {
-  put_sz("Multiboot info:\n");
+  put_sz("Multiboot info:");
   while (tag_pointer->type) {
-    put_sz("  Tag type 0x");
+    put_sz("\n  Tag type 0x");
     put_32_hex(tag_pointer->type);
     put_sz(" with size 0x");
     put_32_hex(tag_pointer->size);
     switch (tag_pointer->type) {
 
+    case LOADER_NAME:
+      put_sz(": bootloader name\n    ");
+      put_sz((uint8_t *)(tag_pointer + 1));
+      break;
+
+    case MEMORY_INFO:
+      put_sz(": memory size\n    Lower memory: 0x");
+      put_32_hex((tag_pointer + 1)->type);
+      put_sz("\n    Upper memory: 0x");
+      put_32_hex((tag_pointer + 1)->size);
+      break;
+
     case BOOT_DEVICE:
       boot_device = *(struct boot_device_tag *)(tag_pointer + 1);
       have_boot_device = true;
@@ -107,17 +160,15 @@ uint32_t main(void) {
         if (boot_device.subpartition != 0xffffffff) {
           put_sz("\n      Subpartition number: ");
           put_32_hex(boot_device.subpartition);
-          put_char('\n');
         }
         else
-          put_sz("\n    No subpartition\n");
+          put_sz("\n    No subpartition");
       }
       else
-        put_sz("\n    No partition\n");
+        put_sz("\n    No partition");
       break;
 
-    case MEMORY_MAP:
-    {
+    case MEMORY_MAP: {
       uint32_t size = *(uint32_t *)(tag_pointer + 1);
       struct mmap_tag_entry *tag = (struct mmap_tag_entry *)(tag_pointer + 2);
       struct mmap_tag_entry *end = (struct mmap_tag_entry *)((uint32_t)tag_pointer + tag_pointer->size);
@@ -177,7 +228,6 @@ uint32_t main(void) {
 
       put_sz("    Total usable memory: 0x");
       put_32_hex(usable);
-      put_char('\n');
 
       bool dirty;
       do {
@@ -202,8 +252,70 @@ uint32_t main(void) {
       have_mmap = true;
       break;
     }
+
+    case VBE_INFO: {
+      struct vbe_tag *vbe_info = (struct vbe_tag *)(tag_pointer + 1);
+      put_sz(": VBE information");
+      if (vbe_info->v2_length) {
+        put_sz("\n    v2: 0x");
+        put_16_hex(vbe_info->v2_segment);
+        put_sz(":0x");
+        put_16_hex(vbe_info->v2_offset);
+        put_sz(" + 0x");
+        put_16_hex(vbe_info->v2_length);
+      }
+      put_sz("\n    v3 mode: 0x");
+      put_16_hex(vbe_info->mode);
+      break;
+    }
+
+    case FBUF_INFO: {
+      struct fbuf_tag *fbuf_info = (struct fbuf_tag *)(tag_pointer + 1);
+      put_sz(": framebuffer information\n    Address: 0x");
+      put_32_hex(fbuf_info->address_high);
+      put_char(':');
+      put_32_hex(fbuf_info->address_low);
+      put_sz("\n    Pitch: ");
+      put_32_dec(fbuf_info->pitch);
+      put_sz("B\n    Size: ");
+      put_32_dec(fbuf_info->width);
+      put_sz(" x ");
+      put_32_dec(fbuf_info->height);
+      put_sz(" x ");
+      put_8_dec(fbuf_info->bpp);
+      switch (fbuf_info->color_type) {
+      case PALETTE:
+        put_sz("b\n    Palette:");
+        uint32_t l = *(uint32_t *)(fbuf_info + 1);
+        struct fbuf_palette_entry *palette = (struct fbuf_palette_entry *)((uint32_t *)(fbuf_info + 1) + 1);
+        for (uint32_t i = 0; i < l; ++i) {
+          put_sz("\n      #");
+          put_8_hex(palette[i].red);
+          put_8_hex(palette[i].green);
+          put_8_hex(palette[i].blue);
+        }
+        break;
+      case RGB:
+        put_sz("b\n    RGB:");
+        //TODO
+        break;
+      case TEXT:
+        put_sz("b\n    Text mode");
+        break;
+      default:
+        put_sz("b\n    Unknown mode 0x");
+        put_8_hex(fbuf_info->color_type);
+      }
+      break;
+    }
+
+    case IMAGE_BASE_ADDR:
+      put_sz(": image address\n    0x");
+      put_32_hex(*(uint32_t *)(tag_pointer + 1));
+      break;
+
     default:
-      put_sz(": ignoring\n");
+      put_sz(": ignoring");
 
     }
     tag_pointer = (struct tag_start *)(((uint32_t)tag_pointer + tag_pointer->size - 1 & 0xfffffff8) + 8);
@@ -214,13 +326,17 @@ uint32_t main(void) {
   if (!have_mmap)
     return NO_MMAP;
 
-  put_sz("\nReady!\n  Memory map size: 0x");
+  put_sz("\n\nReady!\n  Memory map size: 0x");
   put_32_hex(sizeof(struct mmap_entry) * MMAP_SIZE);
   put_sz("\n  Process table size: 0x");
   put_32_hex(sizeof(struct proc_info) * 65536);
   put_sz("\n  File table size: 0x");
   put_32_hex(sizeof(struct file_info) * 65536);
-  put_char('\n');
+  put_sz("\n\n");
+
+  put_sz("No file support yet. Halting.");
+  while (1)
+    asm ("hlt");
 
   uint8_t rc_buffer[4096];
 
diff --git a/src/kernel/vga.c b/src/kernel/vga.c
index 5fd8a46..cf95bc2 100644
--- a/src/kernel/vga.c
+++ b/src/kernel/vga.c
@@ -21,10 +21,11 @@ OF THIS SOFTWARE.
 #include "serial.h"
 #include <stdbool.h>
 
-uint16_t cursor_pos = 0;
-uint8_t color = 0x70;
+#define VGA_BUFFER ((uint8_t *)0x000b8000)
 #define cols 80
 #define rows 25
+uint16_t cursor_pos = 0;
+uint8_t color = 0x70;
 
 void clear(void) {
   uint32_t fill = 0x00200020 | (color << 24) | (color << 8);
@@ -82,6 +83,46 @@ void put_32_hex(uint32_t n) {
   }
 }
 
+void put_16_hex(uint16_t n) {
+  for (uint8_t i = 0; i < 4; ++i) {
+    put_char("0123456789abcdef"[n >> 12]);
+    n <<= 4;
+  }
+}
+
+void put_8_hex(uint8_t n) {
+  put_char("0123456789abcdef"[n >> 4]);
+  put_char("0123456789abcdef"[n & 0x0f]);
+}
+
+void put_32_dec(uint32_t n) {
+  if (n) {
+    bool sig = false;
+    for (uint32_t m = 1000000000; m; m /= 10) {
+      if (((n / m) % 10) || sig) {
+        sig = true;
+        put_char((uint8_t)'0' + (n / m) % 10);
+      }
+    }
+  }
+  else
+    put_char('0');
+}
+
+void put_8_dec(uint8_t n) {
+  if (n) {
+    bool sig = false;
+    for (uint8_t m = 100; m; m /= 10) {
+      if (((n / m) % 10) || sig) {
+        sig = true;
+        put_char((uint8_t)'0' + (n / m) % 10);
+      }
+    }
+  }
+  else
+    put_char('0');
+}
+
 void move_cursor(uint8_t col, uint8_t row) {
   cursor_pos = (col + row * cols) * 2;
 }
diff --git a/src/kernel/vga.h b/src/kernel/vga.h
index 2504128..cb2c2e3 100644
--- a/src/kernel/vga.h
+++ b/src/kernel/vga.h
@@ -22,11 +22,14 @@ OF THIS SOFTWARE.
 #ifndef VGA_H
 #define VGA_H
 
-#define VGA_BUFFER ((uint8_t *)0x000b8000)
 void clear(void);
 void put_char(uint8_t ch);
 void put_sz(uint8_t *s);
 void put_32_hex(uint32_t n);
+void put_16_hex(uint16_t n);
+void put_8_hex(uint8_t n);
+void put_32_dec(uint32_t n);
+void put_8_dec(uint8_t n);
 void move_cursor(uint8_t col, uint8_t row);
 void set_color(uint8_t c);