1 /* Segmentation of the AMD64 architecture.
3 * 2003-07 by SONE Takeshi
7 #include "kernel/kernel.h"
8 #include "libopenbios/sys_info.h"
13 #ifdef CONFIG_DEBUG_BOOT
19 /* i386 lgdt argument */
23 } __attribute__((packed));
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;
29 /* GDT, the global descriptor table */
30 struct segment_desc gdt[NUM_SEG] = {
31 /* 0x00: null segment */
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},
43 extern char _start[], _end[];
45 void relocate(struct sys_info *info)
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;
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);
62 for (i = 0; i < info->n_memranges; i++) {
63 if (info->memrange[i].base >= 1ULL<<32)
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)
70 addr = info->memrange[i].base + segsize - prog_size;
71 addr &= ~(ALIGNMENT-1);
72 if (addr >= prog_addr && addr < prog_addr + prog_size)
74 if (prog_addr >= addr && prog_addr < addr + prog_size)
80 printf("Can't find address to relocate\n");
84 debug("Relocating to %#lx-%#lx... ",
85 new_base, new_base + prog_size - 1);
87 /* New virtual address offset */
88 new_offset = new_base - (unsigned long) &_start;
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);
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 */
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));
114 virt_offset = new_offset;
119 /* Copy GDT to new location and reload it */
120 void move_gdt(unsigned long newgdt)
122 struct gdtarg gdtarg;
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));