Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / arch / amd64 / segment.c
1 /* Segmentation of the AMD64 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 extern char _start[], _end[];
44
45 void relocate(struct sys_info *info)
46 {
47     int i;
48     unsigned long prog_addr;
49     unsigned long prog_size;
50     unsigned long addr, new_base;
51     unsigned long long segsize;
52     unsigned long new_offset;
53     unsigned d0, d1, d2;
54     struct gdtarg gdtarg;
55 #define ALIGNMENT 16
56
57     prog_addr = virt_to_phys(&_start);
58     prog_size = virt_to_phys(&_end) - virt_to_phys(&_start);
59     debug("Current location: %#lx-%#lx\n", prog_addr, prog_addr+prog_size-1);
60
61     new_base = 0;
62     for (i = 0; i < info->n_memranges; i++) {
63         if (info->memrange[i].base >= 1ULL<<32)
64             continue;
65         segsize = info->memrange[i].size;
66         if (info->memrange[i].base + segsize > 1ULL<<32)
67             segsize = (1ULL<<32) - info->memrange[i].base;
68         if (segsize < prog_size+ALIGNMENT)
69             continue;
70         addr = info->memrange[i].base + segsize - prog_size;
71         addr &= ~(ALIGNMENT-1);
72         if (addr >= prog_addr && addr < prog_addr + prog_size)
73             continue;
74         if (prog_addr >= addr && prog_addr < addr + prog_size)
75             continue;
76         if (addr > new_base)
77             new_base = addr;
78     }
79     if (new_base == 0) {
80         printf("Can't find address to relocate\n");
81         return;
82     }
83
84     debug("Relocating to %#lx-%#lx... ",
85             new_base, new_base + prog_size - 1);
86
87     /* New virtual address offset */
88     new_offset = new_base - (unsigned long) &_start;
89
90     /* Tweak the GDT */
91     gdt[RELOC_CODE].base_0 = (unsigned short) new_offset;
92     gdt[RELOC_CODE].base_16 = (unsigned char) (new_offset>>16);
93     gdt[RELOC_CODE].base_24 = (unsigned char) (new_offset>>24);
94     gdt[RELOC_DATA].base_0 = (unsigned short) new_offset;
95     gdt[RELOC_DATA].base_16 = (unsigned char) (new_offset>>16);
96     gdt[RELOC_DATA].base_24 = (unsigned char) (new_offset>>24);
97
98     /* Load new GDT and reload segments */
99     gdtarg.base = new_offset + (unsigned long) gdt;
100     gdtarg.limit = GDT_LIMIT;
101     __asm__ __volatile__ (
102             "rep; movsb\n\t" /* copy everything */
103             "lgdt %3\n\t"
104             "ljmp %4, $1f\n1:\t"
105             "movw %5, %%ds\n\t"
106             "movw %5, %%es\n\t"
107             "movw %5, %%fs\n\t"
108             "movw %5, %%gs\n\t"
109             "movw %5, %%ss\n"
110             : "=&S" (d0), "=&D" (d1), "=&c" (d2)
111             : "m" (gdtarg), "n" (RELOC_CS), "q" ((unsigned short) RELOC_DS),
112             "0" (&_start), "1" (new_base), "2" (prog_size));
113
114     virt_offset = new_offset;
115     debug("ok\n");
116 }
117
118 #if 0
119 /* Copy GDT to new location and reload it */
120 void move_gdt(unsigned long newgdt)
121 {
122     struct gdtarg gdtarg;
123
124     debug("Moving GDT to %#lx...", newgdt);
125     memcpy(phys_to_virt(newgdt), gdt, sizeof gdt);
126     gdtarg.base = newgdt;
127     gdtarg.limit = GDT_LIMIT;
128     debug("reloading GDT...");
129     __asm__ __volatile__ ("lgdt %0\n\t" : : "m" (gdtarg));
130     debug("reloading CS for fun...");
131     __asm__ __volatile__ ("ljmp %0, $1f\n1:" : : "n" (RELOC_CS));
132     debug("ok\n");
133 }
134 #endif