2012-05-08 21:22:26 +03:00
# include <linux/io.h>
# include <linux/memblock.h>
# include <asm/cacheflush.h>
# include <asm/pgtable.h>
# include <asm/realmode.h>
2012-05-08 21:22:41 +03:00
struct real_mode_header * real_mode_header ;
2012-05-08 21:22:46 +03:00
u32 * trampoline_cr4_features ;
2012-05-08 21:22:26 +03:00
void __init setup_real_mode ( void )
{
phys_addr_t mem ;
u16 real_mode_seg ;
u32 * rel ;
u32 count ;
u32 * ptr ;
u16 * seg ;
int i ;
2012-05-08 21:22:41 +03:00
unsigned char * base ;
2012-05-08 21:22:43 +03:00
struct trampoline_header * trampoline_header ;
2012-05-08 21:22:41 +03:00
size_t size = PAGE_ALIGN ( real_mode_blob_end - real_mode_blob ) ;
2012-05-08 21:22:43 +03:00
# ifdef CONFIG_X86_64
u64 * trampoline_pgd ;
2012-05-16 13:22:41 -07:00
u32 efer_low , efer_high ;
2012-05-08 21:22:43 +03:00
# endif
2012-05-08 21:22:26 +03:00
/* Has to be in very low memory so we can execute real-mode AP code. */
mem = memblock_find_in_range ( 0 , 1 < < 20 , size , PAGE_SIZE ) ;
if ( ! mem )
panic ( " Cannot allocate trampoline \n " ) ;
2012-05-08 21:22:41 +03:00
base = __va ( mem ) ;
2012-05-08 21:22:26 +03:00
memblock_reserve ( mem , size ) ;
2012-05-08 21:22:41 +03:00
real_mode_header = ( struct real_mode_header * ) base ;
2012-05-08 21:22:26 +03:00
printk ( KERN_DEBUG " Base memory trampoline at [%p] %llx size %zu \n " ,
2012-05-08 21:22:41 +03:00
base , ( unsigned long long ) mem , size ) ;
2012-05-08 21:22:26 +03:00
2012-05-08 21:22:41 +03:00
memcpy ( base , real_mode_blob , size ) ;
2012-05-08 21:22:26 +03:00
2012-05-08 21:22:41 +03:00
real_mode_seg = __pa ( base ) > > 4 ;
2012-05-08 21:22:26 +03:00
rel = ( u32 * ) real_mode_relocs ;
/* 16-bit segment relocations. */
count = rel [ 0 ] ;
rel = & rel [ 1 ] ;
for ( i = 0 ; i < count ; i + + ) {
2012-05-08 21:22:41 +03:00
seg = ( u16 * ) ( base + rel [ i ] ) ;
2012-05-08 21:22:26 +03:00
* seg = real_mode_seg ;
}
/* 32-bit linear relocations. */
count = rel [ i ] ;
rel = & rel [ i + 1 ] ;
for ( i = 0 ; i < count ; i + + ) {
2012-05-08 21:22:41 +03:00
ptr = ( u32 * ) ( base + rel [ i ] ) ;
* ptr + = __pa ( base ) ;
2012-05-08 21:22:26 +03:00
}
2012-05-08 21:22:43 +03:00
/* Must be perfomed *after* relocation. */
trampoline_header = ( struct trampoline_header * )
__va ( real_mode_header - > trampoline_header ) ;
2012-05-08 21:22:28 +03:00
# ifdef CONFIG_X86_32
2012-05-08 21:22:43 +03:00
trampoline_header - > start = __pa ( startup_32_smp ) ;
trampoline_header - > gdt_limit = __BOOT_DS + 7 ;
trampoline_header - > gdt_base = __pa ( boot_gdt ) ;
2012-05-08 21:22:28 +03:00
# else
2012-05-16 13:22:41 -07:00
/*
* Some AMD processors will # GP ( 0 ) if EFER . LMA is set in WRMSR
* so we need to mask it out .
*/
rdmsr ( MSR_EFER , efer_low , efer_high ) ;
trampoline_header - > efer_low = efer_low & ~ EFER_LMA ;
trampoline_header - > efer_high = efer_high ;
2012-05-08 21:22:46 +03:00
2012-05-08 21:22:43 +03:00
trampoline_header - > start = ( u64 ) secondary_startup_64 ;
2012-05-08 21:22:46 +03:00
trampoline_cr4_features = & trampoline_header - > cr4 ;
* trampoline_cr4_features = read_cr4 ( ) ;
2012-05-08 21:22:43 +03:00
trampoline_pgd = ( u64 * ) __va ( real_mode_header - > trampoline_pgd ) ;
trampoline_pgd [ 0 ] = __pa ( level3_ident_pgt ) + _KERNPG_TABLE ;
trampoline_pgd [ 511 ] = __pa ( level3_kernel_pgt ) + _KERNPG_TABLE ;
2012-05-08 21:22:28 +03:00
# endif
2012-05-08 21:22:26 +03:00
}
/*
* set_real_mode_permissions ( ) gets called very early , to guarantee the
* availability of low memory . This is before the proper kernel page
* tables are set up , so we cannot set page permissions in that
* function . Thus , we use an arch_initcall instead .
*/
static int __init set_real_mode_permissions ( void )
{
2012-05-08 21:22:41 +03:00
unsigned char * base = ( unsigned char * ) real_mode_header ;
size_t size = PAGE_ALIGN ( real_mode_blob_end - real_mode_blob ) ;
2012-05-08 21:22:26 +03:00
2012-05-08 21:22:30 +03:00
size_t ro_size =
2012-05-08 21:22:41 +03:00
PAGE_ALIGN ( real_mode_header - > ro_end ) -
__pa ( base ) ;
2012-05-08 21:22:30 +03:00
size_t text_size =
2012-05-08 21:22:41 +03:00
PAGE_ALIGN ( real_mode_header - > ro_end ) -
real_mode_header - > text_start ;
2012-05-08 21:22:30 +03:00
unsigned long text_start =
2012-05-08 21:22:41 +03:00
( unsigned long ) __va ( real_mode_header - > text_start ) ;
2012-05-08 21:22:30 +03:00
2012-05-08 21:22:41 +03:00
set_memory_nx ( ( unsigned long ) base , size > > PAGE_SHIFT ) ;
set_memory_ro ( ( unsigned long ) base , ro_size > > PAGE_SHIFT ) ;
2012-05-08 21:22:30 +03:00
set_memory_x ( ( unsigned long ) text_start , text_size > > PAGE_SHIFT ) ;
2012-05-08 21:22:26 +03:00
return 0 ;
}
arch_initcall ( set_real_mode_permissions ) ;