summaryrefslogtreecommitdiffstats
path: root/purgatory/arch/ia64
diff options
context:
space:
mode:
Diffstat (limited to 'purgatory/arch/ia64')
-rw-r--r--purgatory/arch/ia64/Makefile17
-rw-r--r--purgatory/arch/ia64/console-ia64.c47
-rw-r--r--purgatory/arch/ia64/entry.S73
-rw-r--r--purgatory/arch/ia64/io.h108
-rw-r--r--purgatory/arch/ia64/purgatory-ia64.c307
-rw-r--r--purgatory/arch/ia64/purgatory-ia64.h5
-rw-r--r--purgatory/arch/ia64/vga.c143
7 files changed, 700 insertions, 0 deletions
diff --git a/purgatory/arch/ia64/Makefile b/purgatory/arch/ia64/Makefile
new file mode 100644
index 0000000..4a2564c
--- /dev/null
+++ b/purgatory/arch/ia64/Makefile
@@ -0,0 +1,17 @@
+#
+# Purgatory ia64
+#
+ia64_PURGATORY_SRCS += purgatory/arch/ia64/entry.S
+ia64_PURGATORY_SRCS += purgatory/arch/ia64/purgatory-ia64.c
+ia64_PURGATORY_SRCS += purgatory/arch/ia64/console-ia64.c
+ia64_PURGATORY_SRCS += purgatory/arch/ia64/vga.c
+
+ia64_PURGATORY_EXTRA_CFLAGS = -ffixed-r28
+
+# sha256.c needs to be compiled without optimization, else
+# purgatory fails to execute on ia64.
+ia64_PURGATORY_SHA256_CFLAGS = -O0
+
+dist += purgatory/arch/ia64/Makefile $(ia64_PURGATORY_SRCS) \
+ purgatory/arch/ia64/io.h purgatory/arch/ia64/purgatory-ia64.h
+
diff --git a/purgatory/arch/ia64/console-ia64.c b/purgatory/arch/ia64/console-ia64.c
new file mode 100644
index 0000000..99d97ca
--- /dev/null
+++ b/purgatory/arch/ia64/console-ia64.c
@@ -0,0 +1,47 @@
+#include <purgatory.h>
+#include "io.h"
+
+#define VGABASE UNCACHED(0xb8000)
+
+/* code based on i386 console code
+ * TODO add serial support
+ */
+#define MAX_YPOS 25
+#define MAX_XPOS 80
+
+unsigned long current_ypos = 1, current_xpos = 0;
+
+static void putchar_vga(int ch)
+{
+ int i, k, j;
+
+ if (current_ypos >= MAX_YPOS) {
+ /* scroll 1 line up */
+ for (k = 1, j = 0; k < MAX_YPOS; k++, j++) {
+ for (i = 0; i < MAX_XPOS; i++) {
+ writew(readw(VGABASE + 2*(MAX_XPOS*k + i)),
+ VGABASE + 2*(MAX_XPOS*j + i));
+ }
+ }
+ for (i = 0; i < MAX_XPOS; i++)
+ writew(0x720, VGABASE + 2*(MAX_XPOS*j + i));
+ current_ypos = MAX_YPOS-1;
+ }
+ if (ch == '\n') {
+ current_xpos = 0;
+ current_ypos++;
+ } else if (ch != '\r') {
+ writew(((0x7 << 8) | (unsigned short) ch),
+ VGABASE + 2*(MAX_XPOS*current_ypos +
+ current_xpos++));
+ if (current_xpos >= MAX_XPOS) {
+ current_xpos = 0;
+ current_ypos++;
+ }
+ }
+}
+
+void putchar(int ch)
+{
+ putchar_vga(ch);
+}
diff --git a/purgatory/arch/ia64/entry.S b/purgatory/arch/ia64/entry.S
new file mode 100644
index 0000000..f05434f
--- /dev/null
+++ b/purgatory/arch/ia64/entry.S
@@ -0,0 +1,73 @@
+/*
+ * purgatory: setup code
+ *
+ * Copyright (C) 2005-2006 Zou Nan hai (nanhai.zou@intel.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#define DECLARE_DATA8(name) \
+.global name; \
+.size name, 8; \
+name: data8 0x0
+
+.global __dummy_efi_function
+.align 32
+.proc __dummy_efi_function
+__dummy_efi_function:
+ mov r8=r0;;
+ br.ret.sptk.many rp;;
+.global __dummy_efi_function_end
+__dummy_efi_function_end:
+.endp __dummy_efi_function
+
+.global purgatory_start
+.align 32
+.proc purgatory_start
+purgatory_start:
+ movl r2=__gp_value;;
+ ld8 gp=[r2];;
+ br.call.sptk.many b0=purgatory
+ ;;
+ alloc r2 = ar.pfs, 0, 0, 2, 0
+ ;;
+ mov out0=r28
+ movl out1=__vmcode_base;
+ br.call.sptk.many b0=ia64_env_setup
+ movl r10=__kernel_entry;;
+ ld8 r14=[r10];;
+ movl r10=__boot_param_base;;
+ ld8 r28=[r10];;
+ mov b6=r14;;
+ mov ar.lc=r0
+ mov ar.ec=r0
+ cover;;
+ invala;;
+ br.call.sptk.many b0=b6
+.endp purgatory_start
+
+DECLARE_DATA8(__kernel_entry)
+DECLARE_DATA8(__vmcode_base)
+DECLARE_DATA8(__vmcode_size)
+DECLARE_DATA8(__ramdisk_base)
+DECLARE_DATA8(__ramdisk_size)
+DECLARE_DATA8(__command_line)
+DECLARE_DATA8(__command_line_len)
+DECLARE_DATA8(__efi_memmap_base)
+DECLARE_DATA8(__efi_memmap_size)
+DECLARE_DATA8(__boot_param_base)
+DECLARE_DATA8(__loaded_segments)
+DECLARE_DATA8(__loaded_segments_num)
+
+DECLARE_DATA8(__gp_value)
+DECLARE_DATA8(__noio)
diff --git a/purgatory/arch/ia64/io.h b/purgatory/arch/ia64/io.h
new file mode 100644
index 0000000..0f78580
--- /dev/null
+++ b/purgatory/arch/ia64/io.h
@@ -0,0 +1,108 @@
+#ifndef IO_H
+#define IO_H
+#define UNCACHED(x) (void *)((x)|(1UL<<63))
+#define MF() asm volatile ("mf.a" ::: "memory")
+#define IO_SPACE_ENCODING(p) ((((p) >> 2) << 12) | (p & 0xfff))
+extern long __noio;
+static inline void *io_addr (unsigned long port)
+{
+ unsigned long offset;
+ unsigned long io_base;
+ asm volatile ("mov %0=ar.k0":"=r"(io_base));
+ offset = IO_SPACE_ENCODING(port);
+ return UNCACHED(io_base | offset);
+}
+
+static inline unsigned int inb (unsigned long port)
+{
+ volatile unsigned char *addr = io_addr(port);
+ unsigned char ret = 0;
+ if (!__noio) {
+ ret = *addr;
+ MF();
+ }
+ return ret;
+}
+
+static inline unsigned int inw (unsigned long port)
+{
+ volatile unsigned short *addr = io_addr(port);
+ unsigned short ret = 0;
+
+ if (!__noio) {
+ ret = *addr;
+ MF();
+ }
+ return ret;
+}
+
+static inline unsigned int inl (unsigned long port)
+{
+ volatile unsigned int *addr = io_addr(port);
+ unsigned int ret ;
+ if (!__noio) {
+ ret = *addr;
+ MF();
+ }
+ return ret;
+}
+
+static inline void outb (unsigned char val, unsigned long port)
+{
+ volatile unsigned char *addr = io_addr(port);
+
+ if (!__noio) {
+ *addr = val;
+ MF();
+ }
+}
+
+static inline void outw (unsigned short val, unsigned long port)
+{
+ volatile unsigned short *addr = io_addr(port);
+
+ if (!__noio) {
+ *addr = val;
+ MF();
+ }
+}
+
+static inline void outl (unsigned int val, unsigned long port)
+{
+ volatile unsigned int *addr = io_addr(port);
+
+ if (!__noio) {
+ *addr = val;
+ MF();
+ }
+}
+
+static inline unsigned char readb(const volatile void *addr)
+{
+ return __noio ? 0 :*(volatile unsigned char *) addr;
+}
+static inline unsigned short readw(const volatile void *addr)
+{
+ return __noio ? 0 :*(volatile unsigned short *) addr;
+}
+static inline unsigned int readl(const volatile void *addr)
+{
+ return __noio ? 0 :*(volatile unsigned int *) addr;
+}
+
+static inline void writeb(unsigned char b, volatile void *addr)
+{
+ if (!__noio)
+ *(volatile unsigned char *) addr = b;
+}
+static inline void writew(unsigned short b, volatile void *addr)
+{
+ if (!__noio)
+ *(volatile unsigned short *) addr = b;
+}
+static inline void writel(unsigned int b, volatile void *addr)
+{
+ if (!__noio)
+ *(volatile unsigned int *) addr = b;
+}
+#endif
diff --git a/purgatory/arch/ia64/purgatory-ia64.c b/purgatory/arch/ia64/purgatory-ia64.c
new file mode 100644
index 0000000..e138e9c
--- /dev/null
+++ b/purgatory/arch/ia64/purgatory-ia64.c
@@ -0,0 +1,307 @@
+/*
+ * purgatory: setup code
+ *
+ * Copyright (C) 2005-2006 Zou Nan hai (nanhai.zou@intel.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License).
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <purgatory.h>
+#include <stdint.h>
+#include <string.h>
+#include "purgatory-ia64.h"
+
+#define PAGE_OFFSET 0xe000000000000000UL
+
+#define EFI_PAGE_SHIFT 12
+#define EFI_PAGE_SIZE (1UL<<EFI_PAGE_SHIFT)
+#define EFI_PAGE_ALIGN(x) ((x + EFI_PAGE_SIZE - 1)&~(EFI_PAGE_SIZE-1))
+/* Memory types: */
+#define EFI_RESERVED_TYPE 0
+#define EFI_LOADER_CODE 1
+#define EFI_LOADER_DATA 2
+#define EFI_BOOT_SERVICES_CODE 3
+#define EFI_BOOT_SERVICES_DATA 4
+#define EFI_RUNTIME_SERVICES_CODE 5
+#define EFI_RUNTIME_SERVICES_DATA 6
+#define EFI_CONVENTIONAL_MEMORY 7
+#define EFI_UNUSABLE_MEMORY 8
+#define EFI_ACPI_RECLAIM_MEMORY 9
+#define EFI_ACPI_MEMORY_NVS 10
+#define EFI_MEMORY_MAPPED_IO 11
+#define EFI_MEMORY_MAPPED_IO_PORT_SPACE 12
+#define EFI_PAL_CODE 13
+#define EFI_MAX_MEMORY_TYPE 14
+
+typedef struct {
+ uint64_t signature;
+ uint32_t revision;
+ uint32_t headersize;
+ uint32_t crc32;
+ uint32_t reserved;
+} efi_table_hdr_t;
+
+typedef struct {
+ efi_table_hdr_t hdr;
+ unsigned long get_time;
+ unsigned long set_time;
+ unsigned long get_wakeup_time;
+ unsigned long set_wakeup_time;
+ unsigned long set_virtual_address_map;
+ unsigned long convert_pointer;
+ unsigned long get_variable;
+ unsigned long get_next_variable;
+ unsigned long set_variable;
+ unsigned long get_next_high_mono_count;
+ unsigned long reset_system;
+} efi_runtime_services_t;
+
+typedef struct {
+ efi_table_hdr_t hdr;
+ unsigned long fw_vendor; /* physical addr of
+ CHAR16 vendor string */
+ uint32_t fw_revision;
+ unsigned long con_in_handle;
+ unsigned long con_in;
+ unsigned long con_out_handle;
+ unsigned long con_out;
+ unsigned long stderr_handle;
+ unsigned long stderr;
+ unsigned long runtime;
+ unsigned long boottime;
+ unsigned long nr_tables;
+ unsigned long tables;
+} efi_system_table_t;
+
+struct ia64_boot_param {
+ uint64_t command_line; /* physical address of
+ command linearguments */
+ uint64_t efi_systab; /* physical address of
+ EFI system table */
+ uint64_t efi_memmap; /* physical address of
+ EFI memory map */
+ uint64_t efi_memmap_size; /* size of EFI memory map */
+ uint64_t efi_memdesc_size; /* size of an EFI memory map
+ descriptor */
+ uint32_t efi_memdesc_version; /* memory descriptor version */
+ struct {
+ uint16_t num_cols; /* number of columns on console
+ output device */
+ uint16_t num_rows; /* number of rows on console
+ output device */
+ uint16_t orig_x; /* cursor's x position */
+ uint16_t orig_y; /* cursor's y position */
+ } console_info;
+ uint64_t fpswa; /* physical address of
+ the fpswa interface */
+ uint64_t initrd_start;
+ uint64_t initrd_size;
+
+ uint64_t vmcode_start;
+ uint64_t vmcode_size;
+};
+
+typedef struct {
+ uint32_t type;
+ uint32_t pad;
+ uint64_t phys_addr;
+ uint64_t virt_addr;
+ uint64_t num_pages;
+ uint64_t attribute;
+} efi_memory_desc_t;
+
+struct loaded_segment {
+ unsigned long start;
+ unsigned long end;
+};
+
+struct kexec_boot_params {
+ uint64_t vmcode_base;
+ uint64_t vmcode_size;
+ uint64_t ramdisk_base;
+ uint64_t ramdisk_size;
+ uint64_t command_line;
+ uint64_t command_line_len;
+ uint64_t efi_memmap_base;
+ uint64_t efi_memmap_size;
+ uint64_t boot_param_base;
+ struct loaded_segment *loaded_segments;
+ unsigned long loaded_segments_num;
+};
+
+void
+setup_arch(void)
+{
+ reset_vga();
+}
+
+inline unsigned long PA(unsigned long addr)
+{
+ return addr & 0x0fffffffffffffffLL;
+}
+
+void
+patch_efi_memmap(struct kexec_boot_params *params,
+ struct ia64_boot_param *boot_param)
+{
+ void *dest = (void *)params->efi_memmap_base;
+ void *src = (void *)boot_param->efi_memmap;
+ uint64_t orig_type;
+ efi_memory_desc_t *src_md, *dst_md;
+ void *src_end = src + boot_param->efi_memmap_size;
+ unsigned long i;
+ for (; src < src_end; src += boot_param->efi_memdesc_size,
+ dest += boot_param->efi_memdesc_size) {
+ unsigned long mstart, mend;
+ src_md = src;
+ dst_md = dest;
+ if (src_md->num_pages == 0)
+ continue;
+ mstart = src_md->phys_addr;
+ mend = src_md->phys_addr +
+ (src_md->num_pages << EFI_PAGE_SHIFT);
+ *dst_md = *src_md;
+ if (src_md->type == EFI_LOADER_DATA)
+ dst_md->type = EFI_CONVENTIONAL_MEMORY;
+ /* segments are already sorted and aligned to 4K */
+ orig_type = dst_md->type;
+ for (i = 0; i < params->loaded_segments_num; i++) {
+ struct loaded_segment *seg;
+ unsigned long start_pages, mid_pages, end_pages;
+
+ seg = &params->loaded_segments[i];
+ if (seg->start < mstart || seg->start >= mend)
+ continue;
+
+ while (seg->end > mend && src < src_end) {
+ src += boot_param->efi_memdesc_size;
+ src_md = src;
+ /* TODO check contig and attribute here */
+ mend = src_md->phys_addr +
+ (src_md->num_pages << EFI_PAGE_SHIFT);
+ }
+ if (seg->end < mend && src < src_end) {
+ void *src_next;
+ efi_memory_desc_t *src_next_md;
+ src_next = src + boot_param->efi_memdesc_size;
+ src_next_md = src_next;
+ if (src_next_md->type ==
+ EFI_CONVENTIONAL_MEMORY) {
+ /* TODO check contig and attribute */
+ src += boot_param->efi_memdesc_size;
+ src_md = src;
+ mend = src_md->phys_addr +
+ (src_md->num_pages <<
+ EFI_PAGE_SHIFT);
+ }
+
+ }
+ start_pages = (seg->start - mstart) >> EFI_PAGE_SHIFT;
+ mid_pages = (seg->end - seg->start) >> EFI_PAGE_SHIFT;
+ end_pages = (mend - seg->end) >> EFI_PAGE_SHIFT;
+ if (start_pages) {
+ dst_md->num_pages = start_pages;
+ dest += boot_param->efi_memdesc_size;
+ dst_md = dest;
+ *dst_md = *src_md;
+ }
+ dst_md->phys_addr = seg->start;
+ dst_md->num_pages = mid_pages;
+ dst_md->type = EFI_LOADER_DATA;
+ if (!end_pages)
+ break;
+ dest += boot_param->efi_memdesc_size;
+ dst_md = dest;
+ *dst_md = *src_md;
+ dst_md->phys_addr = seg->end;
+ dst_md->num_pages = end_pages;
+ dst_md->type = orig_type;
+ mstart = seg->end;
+ }
+ }
+
+ boot_param->efi_memmap_size = dest - (void *)params->efi_memmap_base;
+}
+
+void
+flush_icache_range(char *start, unsigned long len)
+{
+ unsigned long i, addr;
+ addr = (unsigned long)start & ~31UL;
+ len += (unsigned long)start - addr;
+ for (i = 0;i < len; i += 32)
+ asm volatile("fc.i %0"::"r"(start + i):"memory");
+ asm volatile (";;sync.i;;":::"memory");
+ asm volatile ("srlz.i":::"memory");
+}
+
+extern char __dummy_efi_function[], __dummy_efi_function_end[];
+
+
+void
+ia64_env_setup(struct ia64_boot_param *boot_param,
+ struct kexec_boot_params *params)
+{
+ unsigned long len;
+ efi_system_table_t *systab;
+ efi_runtime_services_t *runtime;
+ unsigned long *set_virtual_address_map;
+ char *command_line = (char *)params->command_line;
+ uint64_t command_line_len = params->command_line_len;
+ struct ia64_boot_param *new_boot_param =
+ (struct ia64_boot_param *) params->boot_param_base;
+ memcpy(new_boot_param, boot_param, 4096);
+
+ /*
+ * patch efi_runtime->set_virtual_address_map to a dummy function
+ *
+ * The EFI specification mandates that set_virtual_address_map only
+ * takes effect the first time that it is called, and that
+ * subsequent calls will return error. By replacing it with a
+ * dummy function the new OS can think it is calling it again
+ * without either the OS or any buggy EFI implementations getting
+ * upset.
+ *
+ * Note: as the EFI specification says that set_virtual_address_map
+ * will only take affect the first time it is called, the mapping
+ * can't be updated, and thus mapping of the old and new OS really
+ * needs to be the same.
+ */
+ len = __dummy_efi_function_end - __dummy_efi_function;
+ memcpy(command_line + command_line_len,
+ __dummy_efi_function, len);
+ systab = (efi_system_table_t *)new_boot_param->efi_systab;
+ runtime = (efi_runtime_services_t *)PA(systab->runtime);
+ set_virtual_address_map =
+ (unsigned long *)PA(runtime->set_virtual_address_map);
+ *(set_virtual_address_map) =
+ (unsigned long)(command_line + command_line_len);
+ flush_icache_range(command_line + command_line_len, len);
+
+ patch_efi_memmap(params, new_boot_param);
+
+ new_boot_param->efi_memmap = params->efi_memmap_base;
+ new_boot_param->command_line = params->command_line;
+ new_boot_param->console_info.orig_x = 0;
+ new_boot_param->console_info.orig_y = 0;
+ new_boot_param->initrd_start = params->ramdisk_base;
+ new_boot_param->initrd_size = params->ramdisk_size;
+ new_boot_param->vmcode_start = params->vmcode_base;
+ new_boot_param->vmcode_size = params->vmcode_size;
+}
+
+/* This function can be used to execute after the SHA256 verification. */
+void post_verification_setup_arch(void)
+{
+ /* Nothing for now */
+}
diff --git a/purgatory/arch/ia64/purgatory-ia64.h b/purgatory/arch/ia64/purgatory-ia64.h
new file mode 100644
index 0000000..8fd3af1
--- /dev/null
+++ b/purgatory/arch/ia64/purgatory-ia64.h
@@ -0,0 +1,5 @@
+#ifndef PURGATORY_IA64_H
+#define PURGATORY_IA64_H
+
+void reset_vga(void);
+#endif /* PURGATORY_IA64_H */
diff --git a/purgatory/arch/ia64/vga.c b/purgatory/arch/ia64/vga.c
new file mode 100644
index 0000000..dceadb7
--- /dev/null
+++ b/purgatory/arch/ia64/vga.c
@@ -0,0 +1,143 @@
+#include "io.h"
+void reset_vga(void)
+{
+ /* Hello */
+ inb(0x3da);
+ outb(0, 0x3c0);
+
+ /* Sequencer registers */
+ outw(0x0300, 0x3c4);
+ outw(0x0001, 0x3c4);
+ outw(0x0302, 0x3c4);
+ outw(0x0003, 0x3c4);
+ outw(0x0204, 0x3c4);
+
+ /* Ensure CRTC regs 0-7 are unlocked by clearing bit 7 of CRTC[17] */
+ outw(0x0e11, 0x3d4);
+ /* CRTC registers */
+ outw(0x5f00, 0x3d4);
+ outw(0x4f01, 0x3d4);
+ outw(0x5002, 0x3d4);
+ outw(0x8203, 0x3d4);
+ outw(0x5504, 0x3d4);
+ outw(0x8105, 0x3d4);
+ outw(0xbf06, 0x3d4);
+ outw(0x1f07, 0x3d4);
+ outw(0x0008, 0x3d4);
+ outw(0x4f09, 0x3d4);
+ outw(0x200a, 0x3d4);
+ outw(0x0e0b, 0x3d4);
+ outw(0x000c, 0x3d4);
+ outw(0x000d, 0x3d4);
+ outw(0x010e, 0x3d4);
+ outw(0xe00f, 0x3d4);
+ outw(0x9c10, 0x3d4);
+ outw(0x8e11, 0x3d4);
+ outw(0x8f12, 0x3d4);
+ outw(0x2813, 0x3d4);
+ outw(0x1f14, 0x3d4);
+ outw(0x9615, 0x3d4);
+ outw(0xb916, 0x3d4);
+ outw(0xa317, 0x3d4);
+ outw(0xff18, 0x3d4);
+
+ /* Graphic registers */
+ outw(0x0000, 0x3ce);
+ outw(0x0001, 0x3ce);
+ outw(0x0002, 0x3ce);
+ outw(0x0003, 0x3ce);
+ outw(0x0004, 0x3ce);
+ outw(0x1005, 0x3ce);
+ outw(0x0e06, 0x3ce);
+ outw(0x0007, 0x3ce);
+ outw(0xff08, 0x3ce);
+
+ /* Attribute registers */
+ inb(0x3da);
+ outb(0x00, 0x3c0);
+ outb(0x00, 0x3c0);
+
+ inb(0x3da);
+ outb(0x01, 0x3c0);
+ outb(0x01, 0x3c0);
+
+ inb(0x3da);
+ outb(0x02, 0x3c0);
+ outb(0x02, 0x3c0);
+
+ inb(0x3da);
+ outb(0x03, 0x3c0);
+ outb(0x03, 0x3c0);
+
+ inb(0x3da);
+ outb(0x04, 0x3c0);
+ outb(0x04, 0x3c0);
+
+ inb(0x3da);
+ outb(0x05, 0x3c0);
+ outb(0x05, 0x3c0);
+
+ inb(0x3da);
+ outb(0x06, 0x3c0);
+ outb(0x14, 0x3c0);
+
+ inb(0x3da);
+ outb(0x07, 0x3c0);
+ outb(0x07, 0x3c0);
+
+ inb(0x3da);
+ outb(0x08, 0x3c0);
+ outb(0x38, 0x3c0);
+
+ inb(0x3da);
+ outb(0x09, 0x3c0);
+ outb(0x39, 0x3c0);
+
+ inb(0x3da);
+ outb(0x0a, 0x3c0);
+ outb(0x3a, 0x3c0);
+
+ inb(0x3da);
+ outb(0x0b, 0x3c0);
+ outb(0x3b, 0x3c0);
+
+ inb(0x3da);
+ outb(0x0c, 0x3c0);
+ outb(0x3c, 0x3c0);
+
+ inb(0x3da);
+ outb(0x0d, 0x3c0);
+ outb(0x3d, 0x3c0);
+
+ inb(0x3da);
+ outb(0x0e, 0x3c0);
+ outb(0x3e, 0x3c0);
+
+ inb(0x3da);
+ outb(0x0f, 0x3c0);
+ outb(0x3f, 0x3c0);
+
+ inb(0x3da);
+ outb(0x10, 0x3c0);
+ outb(0x0c, 0x3c0);
+
+ inb(0x3da);
+ outb(0x11, 0x3c0);
+ outb(0x00, 0x3c0);
+
+ inb(0x3da);
+ outb(0x12, 0x3c0);
+ outb(0x0f, 0x3c0);
+
+ inb(0x3da);
+ outb(0x13, 0x3c0);
+ outb(0x08, 0x3c0);
+
+ inb(0x3da);
+ outb(0x14, 0x3c0);
+ outb(0x00, 0x3c0);
+
+ /* Goodbye */
+ inb(0x3da);
+ outb(0x20, 0x3c0);
+}