1 /* Segmentation of the i386 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},
44 void relocate(struct sys_info
*info
)
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
;
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);
61 for (i
= 0; i
< info
->n_memranges
; i
++) {
62 if (info
->memrange
[i
].base
>= 1ULL<<32)
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
)
69 addr
= info
->memrange
[i
].base
+ segsize
- prog_size
;
70 addr
&= ~(ALIGNMENT
-1);
71 if (addr
>= prog_addr
&& addr
< prog_addr
+ prog_size
)
73 if (prog_addr
>= addr
&& prog_addr
< addr
+ prog_size
)
79 printf("Can't find address to relocate\n");
83 debug("Relocating to %#lx-%#lx... ",
84 new_base
, new_base
+ prog_size
- 1);
86 /* New virtual address offset */
87 new_offset
= new_base
- (unsigned long) &_start
;
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);
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 */
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
));
113 virt_offset
= new_offset
;
118 /* Copy GDT to new location and reload it */
119 void move_gdt(unsigned long newgdt
)
121 struct gdtarg gdtarg
;
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
));