diff options
-rw-r--r-- | arch/x86/kernel/e820.c | 140 | ||||
-rw-r--r-- | arch/x86/kernel/head.c | 2 | ||||
-rw-r--r-- | include/asm-x86/e820.h | 1 |
3 files changed, 133 insertions, 10 deletions
diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c index 8a8afbdeb34e..600c9de237a0 100644 --- a/arch/x86/kernel/e820.c +++ b/arch/x86/kernel/e820.c | |||
@@ -604,6 +604,7 @@ void __init e820_mark_nosave_regions(unsigned long limit_pfn) | |||
604 | struct early_res { | 604 | struct early_res { |
605 | u64 start, end; | 605 | u64 start, end; |
606 | char name[16]; | 606 | char name[16]; |
607 | char overlap_ok; | ||
607 | }; | 608 | }; |
608 | static struct early_res early_res[MAX_EARLY_RES] __initdata = { | 609 | static struct early_res early_res[MAX_EARLY_RES] __initdata = { |
609 | { 0, PAGE_SIZE, "BIOS data page" }, /* BIOS data page */ | 610 | { 0, PAGE_SIZE, "BIOS data page" }, /* BIOS data page */ |
@@ -640,7 +641,93 @@ static int __init find_overlapped_early(u64 start, u64 end) | |||
640 | return i; | 641 | return i; |
641 | } | 642 | } |
642 | 643 | ||
643 | void __init reserve_early(u64 start, u64 end, char *name) | 644 | /* |
645 | * Drop the i-th range from the early reservation map, | ||
646 | * by copying any higher ranges down one over it, and | ||
647 | * clearing what had been the last slot. | ||
648 | */ | ||
649 | static void __init drop_range(int i) | ||
650 | { | ||
651 | int j; | ||
652 | |||
653 | for (j = i + 1; j < MAX_EARLY_RES && early_res[j].end; j++) | ||
654 | ; | ||
655 | |||
656 | memmove(&early_res[i], &early_res[i + 1], | ||
657 | (j - 1 - i) * sizeof(struct early_res)); | ||
658 | |||
659 | early_res[j - 1].end = 0; | ||
660 | } | ||
661 | |||
662 | /* | ||
663 | * Split any existing ranges that: | ||
664 | * 1) are marked 'overlap_ok', and | ||
665 | * 2) overlap with the stated range [start, end) | ||
666 | * into whatever portion (if any) of the existing range is entirely | ||
667 | * below or entirely above the stated range. Drop the portion | ||
668 | * of the existing range that overlaps with the stated range, | ||
669 | * which will allow the caller of this routine to then add that | ||
670 | * stated range without conflicting with any existing range. | ||
671 | */ | ||
672 | static void __init drop_overlaps_that_are_ok(u64 start, u64 end) | ||
673 | { | ||
674 | int i; | ||
675 | struct early_res *r; | ||
676 | u64 lower_start, lower_end; | ||
677 | u64 upper_start, upper_end; | ||
678 | char name[16]; | ||
679 | |||
680 | for (i = 0; i < MAX_EARLY_RES && early_res[i].end; i++) { | ||
681 | r = &early_res[i]; | ||
682 | |||
683 | /* Continue past non-overlapping ranges */ | ||
684 | if (end <= r->start || start >= r->end) | ||
685 | continue; | ||
686 | |||
687 | /* | ||
688 | * Leave non-ok overlaps as is; let caller | ||
689 | * panic "Overlapping early reservations" | ||
690 | * when it hits this overlap. | ||
691 | */ | ||
692 | if (!r->overlap_ok) | ||
693 | return; | ||
694 | |||
695 | /* | ||
696 | * We have an ok overlap. We will drop it from the early | ||
697 | * reservation map, and add back in any non-overlapping | ||
698 | * portions (lower or upper) as separate, overlap_ok, | ||
699 | * non-overlapping ranges. | ||
700 | */ | ||
701 | |||
702 | /* 1. Note any non-overlapping (lower or upper) ranges. */ | ||
703 | strncpy(name, r->name, sizeof(name) - 1); | ||
704 | |||
705 | lower_start = lower_end = 0; | ||
706 | upper_start = upper_end = 0; | ||
707 | if (r->start < start) { | ||
708 | lower_start = r->start; | ||
709 | lower_end = start; | ||
710 | } | ||
711 | if (r->end > end) { | ||
712 | upper_start = end; | ||
713 | upper_end = r->end; | ||
714 | } | ||
715 | |||
716 | /* 2. Drop the original ok overlapping range */ | ||
717 | drop_range(i); | ||
718 | |||
719 | i--; /* resume for-loop on copied down entry */ | ||
720 | |||
721 | /* 3. Add back in any non-overlapping ranges. */ | ||
722 | if (lower_end) | ||
723 | reserve_early_overlap_ok(lower_start, lower_end, name); | ||
724 | if (upper_end) | ||
725 | reserve_early_overlap_ok(upper_start, upper_end, name); | ||
726 | } | ||
727 | } | ||
728 | |||
729 | static void __init __reserve_early(u64 start, u64 end, char *name, | ||
730 | int overlap_ok) | ||
644 | { | 731 | { |
645 | int i; | 732 | int i; |
646 | struct early_res *r; | 733 | struct early_res *r; |
@@ -656,14 +743,55 @@ void __init reserve_early(u64 start, u64 end, char *name) | |||
656 | r->end - 1, r->name); | 743 | r->end - 1, r->name); |
657 | r->start = start; | 744 | r->start = start; |
658 | r->end = end; | 745 | r->end = end; |
746 | r->overlap_ok = overlap_ok; | ||
659 | if (name) | 747 | if (name) |
660 | strncpy(r->name, name, sizeof(r->name) - 1); | 748 | strncpy(r->name, name, sizeof(r->name) - 1); |
661 | } | 749 | } |
662 | 750 | ||
751 | /* | ||
752 | * A few early reservtations come here. | ||
753 | * | ||
754 | * The 'overlap_ok' in the name of this routine does -not- mean it | ||
755 | * is ok for these reservations to overlap an earlier reservation. | ||
756 | * Rather it means that it is ok for subsequent reservations to | ||
757 | * overlap this one. | ||
758 | * | ||
759 | * Use this entry point to reserve early ranges when you are doing | ||
760 | * so out of "Paranoia", reserving perhaps more memory than you need, | ||
761 | * just in case, and don't mind a subsequent overlapping reservation | ||
762 | * that is known to be needed. | ||
763 | * | ||
764 | * The drop_overlaps_that_are_ok() call here isn't really needed. | ||
765 | * It would be needed if we had two colliding 'overlap_ok' | ||
766 | * reservations, so that the second such would not panic on the | ||
767 | * overlap with the first. We don't have any such as of this | ||
768 | * writing, but might as well tolerate such if it happens in | ||
769 | * the future. | ||
770 | */ | ||
771 | void __init reserve_early_overlap_ok(u64 start, u64 end, char *name) | ||
772 | { | ||
773 | drop_overlaps_that_are_ok(start, end); | ||
774 | __reserve_early(start, end, name, 1); | ||
775 | } | ||
776 | |||
777 | /* | ||
778 | * Most early reservations come here. | ||
779 | * | ||
780 | * We first have drop_overlaps_that_are_ok() drop any pre-existing | ||
781 | * 'overlap_ok' ranges, so that we can then reserve this memory | ||
782 | * range without risk of panic'ing on an overlapping overlap_ok | ||
783 | * early reservation. | ||
784 | */ | ||
785 | void __init reserve_early(u64 start, u64 end, char *name) | ||
786 | { | ||
787 | drop_overlaps_that_are_ok(start, end); | ||
788 | __reserve_early(start, end, name, 0); | ||
789 | } | ||
790 | |||
663 | void __init free_early(u64 start, u64 end) | 791 | void __init free_early(u64 start, u64 end) |
664 | { | 792 | { |
665 | struct early_res *r; | 793 | struct early_res *r; |
666 | int i, j; | 794 | int i; |
667 | 795 | ||
668 | i = find_overlapped_early(start, end); | 796 | i = find_overlapped_early(start, end); |
669 | r = &early_res[i]; | 797 | r = &early_res[i]; |
@@ -671,13 +799,7 @@ void __init free_early(u64 start, u64 end) | |||
671 | panic("free_early on not reserved area: %llx-%llx!", | 799 | panic("free_early on not reserved area: %llx-%llx!", |
672 | start, end - 1); | 800 | start, end - 1); |
673 | 801 | ||
674 | for (j = i + 1; j < MAX_EARLY_RES && early_res[j].end; j++) | 802 | drop_range(i); |
675 | ; | ||
676 | |||
677 | memmove(&early_res[i], &early_res[i + 1], | ||
678 | (j - 1 - i) * sizeof(struct early_res)); | ||
679 | |||
680 | early_res[j - 1].end = 0; | ||
681 | } | 803 | } |
682 | 804 | ||
683 | void __init early_res_to_bootmem(u64 start, u64 end) | 805 | void __init early_res_to_bootmem(u64 start, u64 end) |
diff --git a/arch/x86/kernel/head.c b/arch/x86/kernel/head.c index a727c0b9819c..a6816be01cd6 100644 --- a/arch/x86/kernel/head.c +++ b/arch/x86/kernel/head.c | |||
@@ -51,7 +51,7 @@ void __init reserve_ebda_region(void) | |||
51 | lowmem = 0x9f000; | 51 | lowmem = 0x9f000; |
52 | 52 | ||
53 | /* reserve all memory between lowmem and the 1MB mark */ | 53 | /* reserve all memory between lowmem and the 1MB mark */ |
54 | reserve_early(lowmem, 0x100000, "BIOS reserved"); | 54 | reserve_early_overlap_ok(lowmem, 0x100000, "BIOS reserved"); |
55 | } | 55 | } |
56 | 56 | ||
57 | void __init reserve_setup_data(void) | 57 | void __init reserve_setup_data(void) |
diff --git a/include/asm-x86/e820.h b/include/asm-x86/e820.h index 7c32df07bae4..13fa5a076aa2 100644 --- a/include/asm-x86/e820.h +++ b/include/asm-x86/e820.h | |||
@@ -88,6 +88,7 @@ extern unsigned long end_user_pfn; | |||
88 | extern u64 find_e820_area(u64 start, u64 end, u64 size, u64 align); | 88 | extern u64 find_e820_area(u64 start, u64 end, u64 size, u64 align); |
89 | extern u64 find_e820_area_size(u64 start, u64 *sizep, u64 align); | 89 | extern u64 find_e820_area_size(u64 start, u64 *sizep, u64 align); |
90 | extern void reserve_early(u64 start, u64 end, char *name); | 90 | extern void reserve_early(u64 start, u64 end, char *name); |
91 | extern void reserve_early_overlap_ok(u64 start, u64 end, char *name); | ||
91 | extern void free_early(u64 start, u64 end); | 92 | extern void free_early(u64 start, u64 end); |
92 | extern void early_res_to_bootmem(u64 start, u64 end); | 93 | extern void early_res_to_bootmem(u64 start, u64 end); |
93 | extern u64 early_reserve_e820(u64 startt, u64 sizet, u64 align); | 94 | extern u64 early_reserve_e820(u64 startt, u64 sizet, u64 align); |