summaryrefslogtreecommitdiffstats
path: root/qemu/roms/openbios/arch/x86/segment.c
diff options
context:
space:
mode:
authorYang Zhang <yang.z.zhang@intel.com>2015-08-28 09:58:54 +0800
committerYang Zhang <yang.z.zhang@intel.com>2015-09-01 12:44:00 +0800
commite44e3482bdb4d0ebde2d8b41830ac2cdb07948fb (patch)
tree66b09f592c55df2878107a468a91d21506104d3f /qemu/roms/openbios/arch/x86/segment.c
parent9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 (diff)
Add qemu 2.4.0
Change-Id: Ic99cbad4b61f8b127b7dc74d04576c0bcbaaf4f5 Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Diffstat (limited to 'qemu/roms/openbios/arch/x86/segment.c')
-rw-r--r--qemu/roms/openbios/arch/x86/segment.c133
1 files changed, 133 insertions, 0 deletions
diff --git a/qemu/roms/openbios/arch/x86/segment.c b/qemu/roms/openbios/arch/x86/segment.c
new file mode 100644
index 000000000..5146cd302
--- /dev/null
+++ b/qemu/roms/openbios/arch/x86/segment.c
@@ -0,0 +1,133 @@
+/* Segmentation of the i386 architecture.
+ *
+ * 2003-07 by SONE Takeshi
+ */
+
+#include "config.h"
+#include "kernel/kernel.h"
+#include "libopenbios/sys_info.h"
+#include "relocate.h"
+#include "segment.h"
+
+#define printf printk
+#ifdef CONFIG_DEBUG_BOOT
+#define debug printk
+#else
+#define debug(x...)
+#endif
+
+/* i386 lgdt argument */
+struct gdtarg {
+ unsigned short limit;
+ unsigned int base;
+} __attribute__((packed));
+
+/* How far the virtual address (used in C) is different from physical
+ * address. Since we start in flat mode, the initial value is zero. */
+unsigned long virt_offset = 0;
+
+/* GDT, the global descriptor table */
+struct segment_desc gdt[NUM_SEG] = {
+ /* 0x00: null segment */
+ {0, 0, 0, 0, 0, 0},
+ /* 0x08: flat code segment */
+ {0xffff, 0, 0, 0x9f, 0xcf, 0},
+ /* 0x10: flat data segment */
+ {0xffff, 0, 0, 0x93, 0xcf, 0},
+ /* 0x18: code segment for relocated execution */
+ {0xffff, 0, 0, 0x9f, 0xcf, 0},
+ /* 0x20: data segment for relocated execution */
+ {0xffff, 0, 0, 0x93, 0xcf, 0},
+};
+
+
+void relocate(struct sys_info *info)
+{
+ int i;
+ unsigned long prog_addr;
+ unsigned long prog_size;
+ unsigned long addr, new_base;
+ unsigned long long segsize;
+ unsigned long new_offset;
+ unsigned d0, d1, d2;
+ struct gdtarg gdtarg;
+#define ALIGNMENT 16
+
+ prog_addr = virt_to_phys(&_start);
+ prog_size = virt_to_phys(&_end) - virt_to_phys(&_start);
+ debug("Current location: %#lx-%#lx\n", prog_addr, prog_addr+prog_size-1);
+
+ new_base = 0;
+ for (i = 0; i < info->n_memranges; i++) {
+ if (info->memrange[i].base >= 1ULL<<32)
+ continue;
+ segsize = info->memrange[i].size;
+ if (info->memrange[i].base + segsize > 1ULL<<32)
+ segsize = (1ULL<<32) - info->memrange[i].base;
+ if (segsize < prog_size+ALIGNMENT)
+ continue;
+ addr = info->memrange[i].base + segsize - prog_size;
+ addr &= ~(ALIGNMENT-1);
+ if (addr >= prog_addr && addr < prog_addr + prog_size)
+ continue;
+ if (prog_addr >= addr && prog_addr < addr + prog_size)
+ continue;
+ if (addr > new_base)
+ new_base = addr;
+ }
+ if (new_base == 0) {
+ printf("Can't find address to relocate\n");
+ return;
+ }
+
+ debug("Relocating to %#lx-%#lx... ",
+ new_base, new_base + prog_size - 1);
+
+ /* New virtual address offset */
+ new_offset = new_base - (unsigned long) &_start;
+
+ /* Tweak the GDT */
+ gdt[RELOC_CODE].base_0 = (unsigned short) new_offset;
+ gdt[RELOC_CODE].base_16 = (unsigned char) (new_offset>>16);
+ gdt[RELOC_CODE].base_24 = (unsigned char) (new_offset>>24);
+ gdt[RELOC_DATA].base_0 = (unsigned short) new_offset;
+ gdt[RELOC_DATA].base_16 = (unsigned char) (new_offset>>16);
+ gdt[RELOC_DATA].base_24 = (unsigned char) (new_offset>>24);
+
+ /* Load new GDT and reload segments */
+ gdtarg.base = new_offset + (unsigned long) gdt;
+ gdtarg.limit = GDT_LIMIT;
+ __asm__ __volatile__ (
+ "rep; movsb\n\t" /* copy everything */
+ "lgdt %3\n\t"
+ "ljmp %4, $1f\n1:\t"
+ "movw %5, %%ds\n\t"
+ "movw %5, %%es\n\t"
+ "movw %5, %%fs\n\t"
+ "movw %5, %%gs\n\t"
+ "movw %5, %%ss\n"
+ : "=&S" (d0), "=&D" (d1), "=&c" (d2)
+ : "m" (gdtarg), "n" (RELOC_CS), "q" ((unsigned short) RELOC_DS),
+ "0" (&_start), "1" (new_base), "2" (prog_size));
+
+ virt_offset = new_offset;
+ debug("ok\n");
+}
+
+#if 0
+/* Copy GDT to new location and reload it */
+void move_gdt(unsigned long newgdt)
+{
+ struct gdtarg gdtarg;
+
+ debug("Moving GDT to %#lx...", newgdt);
+ memcpy(phys_to_virt(newgdt), gdt, sizeof gdt);
+ gdtarg.base = newgdt;
+ gdtarg.limit = GDT_LIMIT;
+ debug("reloading GDT...");
+ __asm__ __volatile__ ("lgdt %0\n\t" : : "m" (gdtarg));
+ debug("reloading CS for fun...");
+ __asm__ __volatile__ ("ljmp %0, $1f\n1:" : : "n" (RELOC_CS));
+ debug("ok\n");
+}
+#endif