Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / arch / x86 / segment.c
1 /* Segmentation of the i386 architecture.
2  *
3  * 2003-07 by SONE Takeshi
4  */
5
6 #include "config.h"
7 #include "kernel/kernel.h"
8 #include "libopenbios/sys_info.h"
9 #include "relocate.h"
10 #include "segment.h"
11
12 #define printf printk
13 #ifdef CONFIG_DEBUG_BOOT
14 #define debug printk
15 #else
16 #define debug(x...)
17 #endif
18
19 /* i386 lgdt argument */
20 struct gdtarg {
21     unsigned short limit;
22     unsigned int base;
23 } __attribute__((packed));
24
25 /* How far the virtual address (used in C) is different from physical
26  * address. Since we start in flat mode, the initial value is zero. */
27 unsigned long virt_offset = 0;
28
29 /* GDT, the global descriptor table */
30 struct segment_desc gdt[NUM_SEG] = {
31     /* 0x00: null segment */
32     {0, 0, 0, 0, 0, 0},
33     /* 0x08: flat code segment */
34     {0xffff, 0, 0, 0x9f, 0xcf, 0},
35     /* 0x10: flat data segment */
36     {0xffff, 0, 0, 0x93, 0xcf, 0},
37     /* 0x18: code segment for relocated execution */
38     {0xffff, 0, 0, 0x9f, 0xcf, 0},
39     /* 0x20: data segment for relocated execution */
40     {0xffff, 0, 0, 0x93, 0xcf, 0},
41 };
42
43
44 void relocate(struct sys_info *info)
45 {
46     int i;
47     unsigned long prog_addr;
48     unsigned long prog_size;
49     unsigned long addr, new_base;
50     unsigned long long segsize;
51     unsigned long new_offset;
52     unsigned d0, d1, d2;
53     struct gdtarg gdtarg;
54 #define ALIGNMENT 16
55
56     prog_addr = virt_to_phys(&_start);
57     prog_size = virt_to_phys(&_end) - virt_to_phys(&_start);
58     debug("Current location: %#lx-%#lx\n", prog_addr, prog_addr+prog_size-1);
59
60     new_base = 0;
61     for (i = 0; i < info->n_memranges; i++) {
62         if (info->memrange[i].base >= 1ULL<<32)
63             continue;
64         segsize = info->memrange[i].size;
65         if (info->memrange[i].base + segsize > 1ULL<<32)
66             segsize = (1ULL<<32) - info->memrange[i].base;
67         if (segsize < prog_size+ALIGNMENT)
68             continue;
69         addr = info->memrange[i].base + segsize - prog_size;
70         addr &= ~(ALIGNMENT-1);
71         if (addr >= prog_addr && addr < prog_addr + prog_size)
72             continue;
73         if (prog_addr >= addr && prog_addr < addr + prog_size)
74             continue;
75         if (addr > new_base)
76             new_base = addr;
77     }
78     if (new_base == 0) {
79         printf("Can't find address to relocate\n");
80         return;
81     }
82
83     debug("Relocating to %#lx-%#lx... ",
84             new_base, new_base + prog_size - 1);
85
86     /* New virtual address offset */
87     new_offset = new_base - (unsigned long) &_start;
88
89     /* Tweak the GDT */
90     gdt[RELOC_CODE].base_0 = (unsigned short) new_offset;
91     gdt[RELOC_CODE].base_16 = (unsigned char) (new_offset>>16);
92     gdt[RELOC_CODE].base_24 = (unsigned char) (new_offset>>24);
93     gdt[RELOC_DATA].base_0 = (unsigned short) new_offset;
94     gdt[RELOC_DATA].base_16 = (unsigned char) (new_offset>>16);
95     gdt[RELOC_DATA].base_24 = (unsigned char) (new_offset>>24);
96
97     /* Load new GDT and reload segments */
98     gdtarg.base = new_offset + (unsigned long) gdt;
99     gdtarg.limit = GDT_LIMIT;
100     __asm__ __volatile__ (
101             "rep; movsb\n\t" /* copy everything */
102             "lgdt %3\n\t"
103             "ljmp %4, $1f\n1:\t"
104             "movw %5, %%ds\n\t"
105             "movw %5, %%es\n\t"
106             "movw %5, %%fs\n\t"
107             "movw %5, %%gs\n\t"
108             "movw %5, %%ss\n"
109             : "=&S" (d0), "=&D" (d1), "=&c" (d2)
110             : "m" (gdtarg), "n" (RELOC_CS), "q" ((unsigned short) RELOC_DS),
111             "0" (&_start), "1" (new_base), "2" (prog_size));
112
113     virt_offset = new_offset;
114     debug("ok\n");
115 }
116
117 #if 0
118 /* Copy GDT to new location and reload it */
119 void move_gdt(unsigned long newgdt)
120 {
121     struct gdtarg gdtarg;
122
123     debug("Moving GDT to %#lx...", newgdt);
124     memcpy(phys_to_virt(newgdt), gdt, sizeof gdt);
125     gdtarg.base = newgdt;
126     gdtarg.limit = GDT_LIMIT;
127     debug("reloading GDT...");
128     __asm__ __volatile__ ("lgdt %0\n\t" : : "m" (gdtarg));
129     debug("reloading CS for fun...");
130     __asm__ __volatile__ ("ljmp %0, $1f\n1:" : : "n" (RELOC_CS));
131     debug("ok\n");
132 }
133 #endif