2.6.4-rc1 remove x86 boot page tables
PATCH 1/1 2.6.4-rc1 remove x86 boot page tables
In the discussion on removing the hard coded limits on the boot page tables it occurred to me that on x86 they are totally unnecessary. A segment with an appropriately adjusted offset will work just as well, will use less memory, will use less code, and allow access to all memory below 4G in the machine. So I sat down and coded it up.
I believe I have accounted for everything. What I was looking for were pieces of code that used the identity mapping present at early boot on x86 and pieces of code that directly manipulated the page tables early in boot.
I have tested this patch on a i386 with 6MB of RAM and an dual operton with 5GB of RAM which tests most of the code paths. And both worked without a hitch.
I think I have accounted for the sub architectures but I don't have the hardware to test them. For voyager and VISWS I actually changed some code so I would appreciate a confirmation I didn't break anything.
I have rewritten and compiled tested the boot_ioremap code but I don't have a configuration to test it. This effects the EFI code and the
numa srat code. It might be worth replacing boot_ioremap with __va()
to reduce the amount of error checking necessary.
I have also carefully tested that I did not break the ignore_int code that is used to catch early exceptions before our normal exception handlers are setup.
Thanks to HPA who got the ball started :)
Eric
arch/i386/boot/tools/build.c +2 -4
@@ -150,10 +150,8 @@ sz = sb.st_size;fprintf (stderr,"System is %d kB\n" , sz/1024 ); sys_size = (sz +15 ) /16 ; -/* 0x40000*16 = 4.0 MB, reasonable estimate for the current maximum */ -if (sys_size > (is_big_kernel ?0x40000 :DEF_SYSSIZE )) -die ("System is too big. Try using %smodules." , - is_big_kernel ?"" :"bzImage or " ); +if (!is_big_kernel && sys_size >DEF_SYSSIZE ) +die ("System is too big. Try using bzImage or modules." );while (sz >0 ) {int l, n;
arch/i386/kernel/head.S +150 -177
459 lines not on this page · raw message
arch/i386/kernel/trampoline.S +4 -9
@@ -14,10 +14,6 @@ * In fact we don't actually need a stack so we don't * set one up. * - * We jump into the boot/compressed/head.S code. So you'd - * better be running a compressed kernel image or you - * won't get very far. - * * On entry to trampoline_data, the processor is in real mode * with 16-bit addressing and 16-bit data. CS has some value * and IP is zero. Thus, data addresses need to be absolute
@@ -56,9 +52,8 @@ lmsw %ax # into protected mode jmp flush_instr flush_instr: - ljmpl $__BOOT_CS, $0x00100000 + ljmpl $__KERNEL_CS, $0x00100000 # jump to startup_32 in arch/i386/kernel/head.S - boot_idt: .word 0 # idt limit = 0 .word 0, 0 # idt base = 0L
@@ -67,9 +62,9 @@ # NOTE: here we actually use CPU#0's GDT - but that is OK, we reload # the proper GDT shortly after booting up the secondary CPUs. # -ENTRY(boot_gdt) - .word __BOOT_DS + 7 # gdt limit - .long boot_gdt_table-__PAGE_OFFSET # gdt base = gdt (first SMP CPU) +boot_gdt: + .word GDT_ENTRIES*8-1 # gdt limit + .long cpu_gdt_table-__PAGE_OFFSET # gdt base = gdt (first SMP CPU) .globl trampoline_end trampoline_end:
arch/i386/mach-voyager/voyager_basic.c +10 -12
@@ -32,6 +32,8 @@#include <asm/tlbflush.h> #include <asm/arch_hooks.h> +extern void *boot_ioremap (unsigned long ,unsigned long ); +/* * Power off function, if any */
@@ -131,10 +133,9 @@__u8 cmos[4 ];ClickMap_t *map;unsigned long map_addr; -unsigned long old;if (region >=CLICK_ENTRIES ) { -printk ("Voyager: Illegal ClickMap region %d\n" , region); +printk (KERN_WARNING "Voyager: Illegal ClickMap region %d\n" , region);return 0 ; }
@@ -143,12 +144,13 @@ map_addr = *(unsigned long *)cmos; -/* steal page 0 for this */ - old = pg0[0 ]; - pg0[0 ] = ((map_addr &PAGE_MASK ) | _PAGE_RW | _PAGE_PRESENT); -local_flush_tlb (); -/* now clear everything out but page 0 */ - map = (ClickMap_t *)(map_addr & (~PAGE_MASK )); +/* map in the clickmap */ + map =boot_ioremap (map_addr,sizeof (*map)); +if (!map) { +printk (KERN_WARNING "Voyager: cannot map ClickMap\n" ); +return 0 ; + } +/* zero length is the end of the clickmap */ if (map->Entry[region].Length !=0 ) {
@@ -156,10 +158,6 @@ *start = map->Entry[region].Address; retval =1 ; } - -/* replace the mapping */ - pg0[0 ] = old; -local_flush_tlb ();return retval; }
arch/i386/mm/boot_ioremap.c +11 -63
100 lines not on this page · raw message
arch/i386/mm/init.c +19 -21
@@ -288,6 +288,11 @@#ifdef CONFIG_X86_PAE int i; +if (!cpu_has_pae) +panic ("cannot execute a PAE-enabled kernel on a PAE-less CPU!" ); + +set_in_cr4 (X86_CR4_PAE ); +/* Init entries of the first-level page table to the zero page */ for (i =0 ; i <PTRS_PER_PGD ; i++)set_pgd (pgd_base + i,__pgd (__pa (empty_zero_page) | _PAGE_PRESENT));
@@ -316,16 +321,23 @@permanent_kmaps_init (pgd_base); -#ifdef CONFIG_X86_PAE /* -* Add low memory identity-mappings - SMP needs it when -* starting up on an AP from real-mode. In the non-PAE -* case we already have these mappings through head.S. -* All user-space mappings are explicitly cleared after +* Identity map the code between _text and _stext. This +* identity mapping needs to persist through SMP startup. +* +* The only real user is paging_start() which needs to +* live on an identity mapped page to transition from physical +* to virtual address mode. paging_start() must be called +* on each processor so this mapping needs to persist through * SMP startup. +* +* After SMP startup all user-space mappings are explicitly +* cleared which removes the identity mapping. +* +* Identity mapping the first pgd entry is overkill but it +* is an extremely easy way to accomplish the needed mapping. */ pgd_base[0 ] = pgd_base[USER_PTRS_PER_PGD ]; -#endif }void zap_low_mappings (void )
@@ -382,17 +394,7 @@ {pagetable_init (); -load_cr3 (swapper_pg_dir); - -#ifdef CONFIG_X86_PAE -/* -* We will bail out later - printk doesn't work right now so -* the user would just see a hanging kernel. -*/ -if (cpu_has_pae) -set_in_cr4 (X86_CR4_PAE ); -#endif -__flush_tlb_all (); +paging_start ();kmap_init ();zone_sizes_init ();
@@ -508,10 +510,6 @@ (unsigned long ) (totalhigh_pages << (PAGE_SHIFT -10 )) ); -#ifdef CONFIG_X86_PAE -if (!cpu_has_pae) -panic ("cannot execute a PAE-enabled kernel on a PAE-less CPU!" ); -#endif if (boot_cpu_data.wp_works_ok <0 )test_wp_bit ();
include/asm-i386/pgtable.h +1 -0
@@ -42,6 +42,7 @@void pgd_dtor (void *,kmem_cache_t *,unsigned long );void pgtable_cache_init (void );void paging_init (void ); +void paging_start (void );#endif /* !__ASSEMBLY__ */
include/asm-i386/segment.h +5 -0
@@ -88,6 +88,11 @@#define GDT_ENTRY_BOOT_DS (GDT_ENTRY_BOOT_CS + 1)#define __BOOT_DS (GDT_ENTRY_BOOT_DS * 8) +/* Additional GDT entries for ..... */ +#define GDT_ENTRY_VIRT_CS 4 +#define __VIRT_CS (GDT_ENTRY_VIRT_CS * 8) +#define GDT_ENTRY_VIRT_DS (GDT_ENTRY_VIRT_CS + 1) +#define __VIRT_DS (GDT_ENTRY_VIRT_DS * 8)/* * The interrupt descriptor table has room for 256 idt's, * the global descriptor table is dependent on the number
Quoted 4 lines · Eric W. Biederman
I have rewritten and compiled tested the
boot_ioremapcode but I don't have a configuration to test it. This effects the EFI code and the
numa srat code. It might be worth replacingboot_ioremapwith__va()
to reduce the amount of error checking necessary.
I can probably have someone test it, but you're right, we don't really need boot_ioremap() if we're going to map in all 4G at boot time. I'd just prefer that you remove it completely in your patch.
I can test it on some SRAT hardware if you'd like.
-- dave
Quoted 11 lines · Dave Hansen
Quoted 4 lines · Eric W. Biederman
I have rewritten and compiled tested the
boot_ioremapcode but I don't have a configuration to test it. This effects the EFI code and the
numa srat code. It might be worth replacingboot_ioremapwith__va()
to reduce the amount of error checking necessary.I can probably have someone test it, but you're right, we don't really need
boot_ioremap()if we're going to map in all 4G at boot time. I'd just prefer that you remove it completely in your patch.I can test it on some SRAT hardware if you'd like.
I'd like.
What I worry about with transforming boot_ioremap to __va are two things.
1) It is clear I haven't broken code that uses boot_ioremap if I don't touch them. At least not that way.
2) There is a weird case at 4G where an exception is generated if you cross that boundary. I forget if it is logical or physical address. Keeping the boot_ioremap interface I can at least try and avoid that.
If I can get a clear picture of what I am avoiding and be certain that it won't happen I will remove all objections. But better safe than sorry at least for a start.
Eric
Followup to: <[email protected]> By author: [email protected] (Eric W. Biederman) In newsgroup: linux.dev.kernel
Quoted 6 lines
I think I have accounted for the sub architectures but I don't have the hardware to test them. For voyager and VISWS I actually changed some code so I would appreciate a confirmation I didn't break anything.
For VISWS I think you actually need to turn paging off explicitly. Also, you probably need to check that you didn't break 4G/4G, especially on SMP.
I would also like to remove the magic %bx, which I did in the version of my patch sent to akpm and which is now in -mm (basically the SMP trampoline jumps to a different entrypoint instead.) In that patch, %ebx is still used as a flag, but it's completely internal to head.S.
See ftp://ftp.kernel.org/pub/linux/kernel/people/hpa/earlymem-7.diff
Quoted 1 line
Thanks to HPA who got the ball started :)
:)
I definitely agree that simply using no paging until we have page tables is by far the cleanest approach. I felt that is was too high risk for 2.6, basically because I'm a chicken, but more realistically because I couldn't really see the effect on all subarchitectures, and I didn't feel 100% confident that there wasn't anything that relied on memory being dual mapped; however, I'm more than happy to be proven wrong :)
Oh yes, with this change you should probably just move swapper_pg_dir (and empty_zero_page?) into .bss like anything else that should be zero after boot.
-hpa
PATCH 1/1 Clean up empty_zero_page abuse
Quoted 3 lines · H. Peter Anvin
Oh yes, with this change you should probably just move
swapper_pg_dir(andempty_zero_page?) into .bss like anything else that should be zero after boot.
This patch (against EWB's boot segment patch) fixes the abuse of empty_zero_page for storing boot parameters. This usage is an artifact from before we had discardable init sections.
- Move some constants to setup.h
- Copy the command line directly to saved_command_line
- Add boot_params as temporary storage of the parameters block
- Move empty_zero_page to .bss
--
Brian Gerst
arch/i386/boot/setup.S +3 -3
@@ -605,7 +605,7 @@ # int 13h ah=48h "Get Device Parameters" # # A buffer of size EDDMAXNR*(EDDEXTSIZE+EDDPARMSIZE) is reserved for our use -# in the empty_zero_page at EDDBUF. The first four bytes of which are +# in boot_params at EDDBUF. The first four bytes of which are # used to store the device number, interface support map and version # results from fn41. The following 74 bytes are used to store # the results from fn48. Starting from device 80h, fn41, then fn48
@@ -617,9 +617,9 @@ # the structure, and the fn41 results are stored at offsets # from there. This removes the need to increment the pointer for # every store, and leaves it ready for the fn48 call. -# A second one-byte buffer, EDDNR, in the empty_zero_page stores +# A second one-byte buffer, EDDNR, in boot_params stores # the number of BIOS devices which exist, up to EDDMAXNR. -# In setup.c, copy_edd() stores both empty_zero_page buffers away +# In setup.c, copy_edd() stores both boot_params buffers away # for later use, as they would get overwritten otherwise. # This code is sensitive to the size of the structs in edd.h edd_start:
arch/i386/kernel/head.S +7 -24
@@ -17,13 +17,7 @@ #include <asm/desc.h> #include <asm/cache.h> #include <asm/thread_info.h> - - -#define OLD_CL_MAGIC_ADDR 0x90020 -#define OLD_CL_MAGIC 0xA33F -#define OLD_CL_BASE_ADDR 0x90000 -#define OLD_CL_OFFSET 0x90022 -#define NEW_CL_POINTER 0x228 /* Relative to real mode data */ +#include <asm/setup.h> /* * References to members of the new_cpu_data structure.
@@ -93,23 +87,16 @@ */ call setup_idt /* - * Copy bootup parameters out of the way. First 2kB of - * _empty_zero_page is for boot parameters, second 2kB - * is for the command line. - * + * Copy bootup parameters out of the way. * Note: %esi still has the pointer to the real-mode data. * No need to cld as DF is already clear from above... */ addl $__PAGE_OFFSET, %esi /* Make %esi virtual */ - movl $empty_zero_page,%edi - movl $512,%ecx + movl $boot_params,%edi + movl $(PARAM_SIZE/4),%ecx rep movsl - xorl %eax,%eax - movl $512,%ecx - rep - stosl - movl empty_zero_page+NEW_CL_POINTER,%esi + movl boot_params+NEW_CL_POINTER,%esi andl %esi,%esi jnz 2f # New command line protocol cmpw $(OLD_CL_MAGIC),OLD_CL_MAGIC_ADDR+__PAGE_OFFSET
@@ -118,8 +105,8 @@ addl $(OLD_CL_BASE_ADDR),%esi 2: addl $__PAGE_OFFSET, %esi /* Make %esi virtual */ - movl $empty_zero_page+2048,%edi - movl $512,%ecx + movl $saved_command_line,%edi + movl $(COMMAND_LINE_SIZE/4),%ecx rep movsl 1:
@@ -399,10 +386,6 @@ ENTRY(swapper_pg_dir) .fill 1024,4,0 -.balign 4096 -ENTRY(empty_zero_page) - .fill 4096,1,0 - /* * Real beginning of normal "text" segment */
arch/i386/kernel/setup.c +4 -3
@@ -126,6 +126,8 @@static char command_line[COMMAND_LINE_SIZE ];char saved_command_line[COMMAND_LINE_SIZE ]; +unsigned char __initdata boot_params[PARAM_SIZE ] = {0 , }; +static struct resource code_resource = {"Kernel code" ,0x100000 ,0 };static struct resource data_resource = {"Kernel data" ,0 ,0 };
@@ -452,7 +454,7 @@#endif /** * copy_edd() - Copy the BIOS EDD information -* from empty_zero_page into a safe place. +* from boot_param into a safe place. * */ static inline void copy_edd (void )
@@ -481,12 +483,11 @@static void __init parse_cmdline_early (char ** cmdline_p) { -char c =' ' , *to = command_line, *from =COMMAND_LINE ; +char c =' ' , *to = command_line, *from = saved_command_line;int len =0 ;int userdef =0 ;/* Save unparsed command line copy for /proc/cmdline */ -memcpy (saved_command_line,COMMAND_LINE ,COMMAND_LINE_SIZE ); saved_command_line[COMMAND_LINE_SIZE -1 ] ='\0' ;for (;;) {
arch/i386/kernel/vmlinux.lds.S +6 -1
@@ -104,7 +104,12 @@ /* freed after init ends here */ __bss_start = .; /* BSS */ - .bss : { *(.bss) } + .bss : { + . = ALIGN(4096); + empty_zero_page = .; + . = . + 4096; + *(.bss) + } __bss_stop = .; _end = . ;
arch/i386/mm/init.c +0 -3
@@ -476,9 +476,6 @@ high_memory = (void *)__va (max_low_pfn *PAGE_SIZE );#endif -/* clear the zero-page */ -memset (empty_zero_page,0 ,PAGE_SIZE ); -/* this will put all low memory onto the freelists */ totalram_pages +=__free_all_bootmem ();
include/asm-i386/setup.h +15 -3
@@ -16,10 +16,22 @@#define MAXMEM_PFN PFN_DOWN(MAXMEM)#define MAX_NONPAE_PFN (1 << 20) +#define PARAM_SIZE 2048 +#define COMMAND_LINE_SIZE 256 + +#define OLD_CL_MAGIC_ADDR 0x90020 +#define OLD_CL_MAGIC 0xA33F +#define OLD_CL_BASE_ADDR 0x90000 +#define OLD_CL_OFFSET 0x90022 +#define NEW_CL_POINTER 0x228/* Relative to real mode data */ + +#ifndef __ASSEMBLY__/* * This is set up by the setup-routine at boot-time */ -#define PARAM ((unsigned char *)empty_zero_page) +extern unsigned char boot_params[PARAM_SIZE ]; + +#define PARAM (boot_params)#define SCREEN_INFO (*(struct screen_info *) (PARAM+0))#define EXT_MEM_K (*(unsigned short *) (PARAM+2))#define ALT_MEM_K (*(unsigned long *) (PARAM+0x1e0))
@@ -47,7 +59,7 @@#define DISK80_SIGNATURE (*(unsigned int*) (PARAM+DISK80_SIG_BUFFER))#define EDD_NR (*(unsigned char *) (PARAM+EDDNR))#define EDD_BUF ((struct edd_info *) (PARAM+EDDBUF)) -#define COMMAND_LINE ((char *) (PARAM+2048)) -#define COMMAND_LINE_SIZE 256 + +#endif /* __ASSEMBLY__ */ #endif /* _i386_SETUP_H */
Quoted 11 lines · [email protected] (H. Peter Anvin) writes:
Followup to: <[email protected]> By author: [email protected] (Eric W. Biederman) In newsgroup: linux.dev.kernelQuoted 6 lines
I think I have accounted for the sub architectures but I don't have the hardware to test them. For voyager and VISWS I actually changed some code so I would appreciate a confirmation I didn't break anything.
For VISWS I think you actually need to turn paging off explicitly.
Hmm. I will look at that.
Quoted 2 lines
Also, you probably need to check that you didn't break 4G/4G, especially on SMP.
The patch will need a few tweaks but it should be fairly straight forward.
Quoted 4 lines
I would also like to remove the magic %bx, which I did in the version of my patch sent to akpm and which is now in -mm (basically the SMP trampoline jumps to a different entrypoint instead.) In that patch, %ebx is still used as a flag, but it's completely internal to
head.S.
Ok I have not seen that one. In this case there was enough common code that it seemed reasonable to reuse it. I managed to reduce the code to two tests. With just a bit of care I suspect I could remove the tests completely.
Quoted 6 lines
See ftp://ftp.kernel.org/pub/linux/kernel/people/hpa/earlymem-7.diff
Quoted 1 line
Thanks to HPA who got the ball started :)
:)
Quoted 7 lines
I definitely agree that simply using no paging until we have page tables is by far the cleanest approach. I felt that is was too high risk for 2.6, basically because I'm a chicken, but more realistically because I couldn't really see the effect on all subarchitectures, and I didn't feel 100% confident that there wasn't anything that relied on memory being dual mapped; however, I'm more than happy to be proven wrong :)
I will try. So far except for early_printk I have not found anything. And that was easy to fix.
Quoted 3 lines
Oh yes, with this change you should probably just move
swapper_pg_dir(andempty_zero_page?) into .bss like anything else that should be zero after boot.
Sounds like a good idea.
Eric
Followup to: <[email protected]> By author: [email protected] (Eric W. Biederman) In newsgroup: linux.dev.kernel
arch/i386/kernel/head.S line 424
Quoted 6 lines
arch/i386/kernel/head.S +2
-2- .quad 0x0000000000000000 /* 0x20 unused */ - .quad 0x0000000000000000 /* 0x28 unused */ + .quad 0x40cf9a000000ffff /* 0x20 kernel 4G code at 0-__PAGE_OFFSET */ + .quad 0x40cf92000000ffff /* 0x28 kernel 4G data at 0-__PAGE_OFFSET */
This needs to actually be derived from __PAGE_OFFSET (it may or may not be 0xc0000000.)
I'd suggest:
/* Create a descriptor from (base,limit,flags) */
/* Limit is in 20-bit form! */
#define DESC(b,l,f) .long (((l) & 0x0ffff)) |\
(((b) & 0x0000ffff) << 16), \
(((b) & 0x00ff0000) >> 16) | \
(((l) & 0xf0000)) | \
(((f) & 0xf0ff) << 8) | \
(((b) & 0xff000000))
DESC(-__PAGE_OFFSET,0xfffff,0xc09a) /* 0x20 kernel 4G code at -__PAGE_OFFSET */
DESC(-__PAGE_OFFSET,0xfffff,0xc092) /* 0x28 kernel 4G code at -__PAGE_OFFSET */[It broken up into .longs like that, because gas doesn't seem to handle 64-bit arithmetric.]
Writing all the descriptors in that form may be a good thing :)
-hpa