diff options
-rw-r--r-- | arch/x86/boot/compressed/eboot.c | 442 | ||||
-rw-r--r-- | arch/x86/boot/compressed/eboot.h | 1 | ||||
-rw-r--r-- | drivers/firmware/efi/efi-stub-helper.c | 463 |
3 files changed, 464 insertions, 442 deletions
diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index b7388a425f09..ab0eefcc5123 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c | |||
@@ -19,214 +19,10 @@ | |||
19 | 19 | ||
20 | static efi_system_table_t *sys_table; | 20 | static efi_system_table_t *sys_table; |
21 | 21 | ||
22 | static void efi_char16_printk(efi_char16_t *str) | ||
23 | { | ||
24 | struct efi_simple_text_output_protocol *out; | ||
25 | |||
26 | out = (struct efi_simple_text_output_protocol *)sys_table->con_out; | ||
27 | efi_call_phys2(out->output_string, out, str); | ||
28 | } | ||
29 | |||
30 | static void efi_printk(char *str) | ||
31 | { | ||
32 | char *s8; | ||
33 | |||
34 | for (s8 = str; *s8; s8++) { | ||
35 | efi_char16_t ch[2] = { 0 }; | ||
36 | |||
37 | ch[0] = *s8; | ||
38 | if (*s8 == '\n') { | ||
39 | efi_char16_t nl[2] = { '\r', 0 }; | ||
40 | efi_char16_printk(nl); | ||
41 | } | ||
42 | |||
43 | efi_char16_printk(ch); | ||
44 | } | ||
45 | } | ||
46 | |||
47 | static efi_status_t __get_map(efi_memory_desc_t **map, unsigned long *map_size, | ||
48 | unsigned long *desc_size) | ||
49 | { | ||
50 | efi_memory_desc_t *m = NULL; | ||
51 | efi_status_t status; | ||
52 | unsigned long key; | ||
53 | u32 desc_version; | ||
54 | |||
55 | *map_size = sizeof(*m) * 32; | ||
56 | again: | ||
57 | /* | ||
58 | * Add an additional efi_memory_desc_t because we're doing an | ||
59 | * allocation which may be in a new descriptor region. | ||
60 | */ | ||
61 | *map_size += sizeof(*m); | ||
62 | status = efi_call_phys3(sys_table->boottime->allocate_pool, | ||
63 | EFI_LOADER_DATA, *map_size, (void **)&m); | ||
64 | if (status != EFI_SUCCESS) | ||
65 | goto fail; | ||
66 | |||
67 | status = efi_call_phys5(sys_table->boottime->get_memory_map, map_size, | ||
68 | m, &key, desc_size, &desc_version); | ||
69 | if (status == EFI_BUFFER_TOO_SMALL) { | ||
70 | efi_call_phys1(sys_table->boottime->free_pool, m); | ||
71 | goto again; | ||
72 | } | ||
73 | |||
74 | if (status != EFI_SUCCESS) | ||
75 | efi_call_phys1(sys_table->boottime->free_pool, m); | ||
76 | |||
77 | fail: | ||
78 | *map = m; | ||
79 | return status; | ||
80 | } | ||
81 | |||
82 | /* | ||
83 | * Allocate at the highest possible address that is not above 'max'. | ||
84 | */ | ||
85 | static efi_status_t high_alloc(unsigned long size, unsigned long align, | ||
86 | unsigned long *addr, unsigned long max) | ||
87 | { | ||
88 | unsigned long map_size, desc_size; | ||
89 | efi_memory_desc_t *map; | ||
90 | efi_status_t status; | ||
91 | unsigned long nr_pages; | ||
92 | u64 max_addr = 0; | ||
93 | int i; | ||
94 | |||
95 | status = __get_map(&map, &map_size, &desc_size); | ||
96 | if (status != EFI_SUCCESS) | ||
97 | goto fail; | ||
98 | |||
99 | nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; | ||
100 | again: | ||
101 | for (i = 0; i < map_size / desc_size; i++) { | ||
102 | efi_memory_desc_t *desc; | ||
103 | unsigned long m = (unsigned long)map; | ||
104 | u64 start, end; | ||
105 | |||
106 | desc = (efi_memory_desc_t *)(m + (i * desc_size)); | ||
107 | if (desc->type != EFI_CONVENTIONAL_MEMORY) | ||
108 | continue; | ||
109 | |||
110 | if (desc->num_pages < nr_pages) | ||
111 | continue; | ||
112 | 22 | ||
113 | start = desc->phys_addr; | 23 | #include "../../../../drivers/firmware/efi/efi-stub-helper.c" |
114 | end = start + desc->num_pages * (1UL << EFI_PAGE_SHIFT); | ||
115 | 24 | ||
116 | if ((start + size) > end || (start + size) > max) | ||
117 | continue; | ||
118 | |||
119 | if (end - size > max) | ||
120 | end = max; | ||
121 | |||
122 | if (round_down(end - size, align) < start) | ||
123 | continue; | ||
124 | |||
125 | start = round_down(end - size, align); | ||
126 | |||
127 | /* | ||
128 | * Don't allocate at 0x0. It will confuse code that | ||
129 | * checks pointers against NULL. | ||
130 | */ | ||
131 | if (start == 0x0) | ||
132 | continue; | ||
133 | |||
134 | if (start > max_addr) | ||
135 | max_addr = start; | ||
136 | } | ||
137 | |||
138 | if (!max_addr) | ||
139 | status = EFI_NOT_FOUND; | ||
140 | else { | ||
141 | status = efi_call_phys4(sys_table->boottime->allocate_pages, | ||
142 | EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, | ||
143 | nr_pages, &max_addr); | ||
144 | if (status != EFI_SUCCESS) { | ||
145 | max = max_addr; | ||
146 | max_addr = 0; | ||
147 | goto again; | ||
148 | } | ||
149 | |||
150 | *addr = max_addr; | ||
151 | } | ||
152 | |||
153 | free_pool: | ||
154 | efi_call_phys1(sys_table->boottime->free_pool, map); | ||
155 | |||
156 | fail: | ||
157 | return status; | ||
158 | } | ||
159 | |||
160 | /* | ||
161 | * Allocate at the lowest possible address. | ||
162 | */ | ||
163 | static efi_status_t low_alloc(unsigned long size, unsigned long align, | ||
164 | unsigned long *addr) | ||
165 | { | ||
166 | unsigned long map_size, desc_size; | ||
167 | efi_memory_desc_t *map; | ||
168 | efi_status_t status; | ||
169 | unsigned long nr_pages; | ||
170 | int i; | ||
171 | |||
172 | status = __get_map(&map, &map_size, &desc_size); | ||
173 | if (status != EFI_SUCCESS) | ||
174 | goto fail; | ||
175 | |||
176 | nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; | ||
177 | for (i = 0; i < map_size / desc_size; i++) { | ||
178 | efi_memory_desc_t *desc; | ||
179 | unsigned long m = (unsigned long)map; | ||
180 | u64 start, end; | ||
181 | |||
182 | desc = (efi_memory_desc_t *)(m + (i * desc_size)); | ||
183 | |||
184 | if (desc->type != EFI_CONVENTIONAL_MEMORY) | ||
185 | continue; | ||
186 | |||
187 | if (desc->num_pages < nr_pages) | ||
188 | continue; | ||
189 | |||
190 | start = desc->phys_addr; | ||
191 | end = start + desc->num_pages * (1UL << EFI_PAGE_SHIFT); | ||
192 | |||
193 | /* | ||
194 | * Don't allocate at 0x0. It will confuse code that | ||
195 | * checks pointers against NULL. Skip the first 8 | ||
196 | * bytes so we start at a nice even number. | ||
197 | */ | ||
198 | if (start == 0x0) | ||
199 | start += 8; | ||
200 | |||
201 | start = round_up(start, align); | ||
202 | if ((start + size) > end) | ||
203 | continue; | ||
204 | |||
205 | status = efi_call_phys4(sys_table->boottime->allocate_pages, | ||
206 | EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, | ||
207 | nr_pages, &start); | ||
208 | if (status == EFI_SUCCESS) { | ||
209 | *addr = start; | ||
210 | break; | ||
211 | } | ||
212 | } | ||
213 | 25 | ||
214 | if (i == map_size / desc_size) | ||
215 | status = EFI_NOT_FOUND; | ||
216 | |||
217 | free_pool: | ||
218 | efi_call_phys1(sys_table->boottime->free_pool, map); | ||
219 | fail: | ||
220 | return status; | ||
221 | } | ||
222 | |||
223 | static void low_free(unsigned long size, unsigned long addr) | ||
224 | { | ||
225 | unsigned long nr_pages; | ||
226 | |||
227 | nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; | ||
228 | efi_call_phys2(sys_table->boottime->free_pages, addr, nr_pages); | ||
229 | } | ||
230 | 26 | ||
231 | static void find_bits(unsigned long mask, u8 *pos, u8 *size) | 27 | static void find_bits(unsigned long mask, u8 *pos, u8 *size) |
232 | { | 28 | { |
@@ -624,242 +420,6 @@ void setup_graphics(struct boot_params *boot_params) | |||
624 | } | 420 | } |
625 | } | 421 | } |
626 | 422 | ||
627 | struct initrd { | ||
628 | efi_file_handle_t *handle; | ||
629 | u64 size; | ||
630 | }; | ||
631 | |||
632 | /* | ||
633 | * Check the cmdline for a LILO-style initrd= arguments. | ||
634 | * | ||
635 | * We only support loading an initrd from the same filesystem as the | ||
636 | * kernel image. | ||
637 | */ | ||
638 | static efi_status_t handle_ramdisks(efi_loaded_image_t *image, | ||
639 | struct setup_header *hdr) | ||
640 | { | ||
641 | struct initrd *initrds; | ||
642 | unsigned long initrd_addr; | ||
643 | efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID; | ||
644 | u64 initrd_total; | ||
645 | efi_file_io_interface_t *io; | ||
646 | efi_file_handle_t *fh; | ||
647 | efi_status_t status; | ||
648 | int nr_initrds; | ||
649 | char *str; | ||
650 | int i, j, k; | ||
651 | |||
652 | initrd_addr = 0; | ||
653 | initrd_total = 0; | ||
654 | |||
655 | str = (char *)(unsigned long)hdr->cmd_line_ptr; | ||
656 | |||
657 | j = 0; /* See close_handles */ | ||
658 | |||
659 | if (!str || !*str) | ||
660 | return EFI_SUCCESS; | ||
661 | |||
662 | for (nr_initrds = 0; *str; nr_initrds++) { | ||
663 | str = strstr(str, "initrd="); | ||
664 | if (!str) | ||
665 | break; | ||
666 | |||
667 | str += 7; | ||
668 | |||
669 | /* Skip any leading slashes */ | ||
670 | while (*str == '/' || *str == '\\') | ||
671 | str++; | ||
672 | |||
673 | while (*str && *str != ' ' && *str != '\n') | ||
674 | str++; | ||
675 | } | ||
676 | |||
677 | if (!nr_initrds) | ||
678 | return EFI_SUCCESS; | ||
679 | |||
680 | status = efi_call_phys3(sys_table->boottime->allocate_pool, | ||
681 | EFI_LOADER_DATA, | ||
682 | nr_initrds * sizeof(*initrds), | ||
683 | &initrds); | ||
684 | if (status != EFI_SUCCESS) { | ||
685 | efi_printk("Failed to alloc mem for initrds\n"); | ||
686 | goto fail; | ||
687 | } | ||
688 | |||
689 | str = (char *)(unsigned long)hdr->cmd_line_ptr; | ||
690 | for (i = 0; i < nr_initrds; i++) { | ||
691 | struct initrd *initrd; | ||
692 | efi_file_handle_t *h; | ||
693 | efi_file_info_t *info; | ||
694 | efi_char16_t filename_16[256]; | ||
695 | unsigned long info_sz; | ||
696 | efi_guid_t info_guid = EFI_FILE_INFO_ID; | ||
697 | efi_char16_t *p; | ||
698 | u64 file_sz; | ||
699 | |||
700 | str = strstr(str, "initrd="); | ||
701 | if (!str) | ||
702 | break; | ||
703 | |||
704 | str += 7; | ||
705 | |||
706 | initrd = &initrds[i]; | ||
707 | p = filename_16; | ||
708 | |||
709 | /* Skip any leading slashes */ | ||
710 | while (*str == '/' || *str == '\\') | ||
711 | str++; | ||
712 | |||
713 | while (*str && *str != ' ' && *str != '\n') { | ||
714 | if ((u8 *)p >= (u8 *)filename_16 + sizeof(filename_16)) | ||
715 | break; | ||
716 | |||
717 | if (*str == '/') { | ||
718 | *p++ = '\\'; | ||
719 | *str++; | ||
720 | } else { | ||
721 | *p++ = *str++; | ||
722 | } | ||
723 | } | ||
724 | |||
725 | *p = '\0'; | ||
726 | |||
727 | /* Only open the volume once. */ | ||
728 | if (!i) { | ||
729 | efi_boot_services_t *boottime; | ||
730 | |||
731 | boottime = sys_table->boottime; | ||
732 | |||
733 | status = efi_call_phys3(boottime->handle_protocol, | ||
734 | image->device_handle, &fs_proto, &io); | ||
735 | if (status != EFI_SUCCESS) { | ||
736 | efi_printk("Failed to handle fs_proto\n"); | ||
737 | goto free_initrds; | ||
738 | } | ||
739 | |||
740 | status = efi_call_phys2(io->open_volume, io, &fh); | ||
741 | if (status != EFI_SUCCESS) { | ||
742 | efi_printk("Failed to open volume\n"); | ||
743 | goto free_initrds; | ||
744 | } | ||
745 | } | ||
746 | |||
747 | status = efi_call_phys5(fh->open, fh, &h, filename_16, | ||
748 | EFI_FILE_MODE_READ, (u64)0); | ||
749 | if (status != EFI_SUCCESS) { | ||
750 | efi_printk("Failed to open initrd file: "); | ||
751 | efi_char16_printk(filename_16); | ||
752 | efi_printk("\n"); | ||
753 | goto close_handles; | ||
754 | } | ||
755 | |||
756 | initrd->handle = h; | ||
757 | |||
758 | info_sz = 0; | ||
759 | status = efi_call_phys4(h->get_info, h, &info_guid, | ||
760 | &info_sz, NULL); | ||
761 | if (status != EFI_BUFFER_TOO_SMALL) { | ||
762 | efi_printk("Failed to get initrd info size\n"); | ||
763 | goto close_handles; | ||
764 | } | ||
765 | |||
766 | grow: | ||
767 | status = efi_call_phys3(sys_table->boottime->allocate_pool, | ||
768 | EFI_LOADER_DATA, info_sz, &info); | ||
769 | if (status != EFI_SUCCESS) { | ||
770 | efi_printk("Failed to alloc mem for initrd info\n"); | ||
771 | goto close_handles; | ||
772 | } | ||
773 | |||
774 | status = efi_call_phys4(h->get_info, h, &info_guid, | ||
775 | &info_sz, info); | ||
776 | if (status == EFI_BUFFER_TOO_SMALL) { | ||
777 | efi_call_phys1(sys_table->boottime->free_pool, info); | ||
778 | goto grow; | ||
779 | } | ||
780 | |||
781 | file_sz = info->file_size; | ||
782 | efi_call_phys1(sys_table->boottime->free_pool, info); | ||
783 | |||
784 | if (status != EFI_SUCCESS) { | ||
785 | efi_printk("Failed to get initrd info\n"); | ||
786 | goto close_handles; | ||
787 | } | ||
788 | |||
789 | initrd->size = file_sz; | ||
790 | initrd_total += file_sz; | ||
791 | } | ||
792 | |||
793 | if (initrd_total) { | ||
794 | unsigned long addr; | ||
795 | |||
796 | /* | ||
797 | * Multiple initrd's need to be at consecutive | ||
798 | * addresses in memory, so allocate enough memory for | ||
799 | * all the initrd's. | ||
800 | */ | ||
801 | status = high_alloc(initrd_total, 0x1000, | ||
802 | &initrd_addr, hdr->initrd_addr_max); | ||
803 | if (status != EFI_SUCCESS) { | ||
804 | efi_printk("Failed to alloc highmem for initrds\n"); | ||
805 | goto close_handles; | ||
806 | } | ||
807 | |||
808 | /* We've run out of free low memory. */ | ||
809 | if (initrd_addr > hdr->initrd_addr_max) { | ||
810 | efi_printk("We've run out of free low memory\n"); | ||
811 | status = EFI_INVALID_PARAMETER; | ||
812 | goto free_initrd_total; | ||
813 | } | ||
814 | |||
815 | addr = initrd_addr; | ||
816 | for (j = 0; j < nr_initrds; j++) { | ||
817 | u64 size; | ||
818 | |||
819 | size = initrds[j].size; | ||
820 | while (size) { | ||
821 | u64 chunksize; | ||
822 | if (size > EFI_READ_CHUNK_SIZE) | ||
823 | chunksize = EFI_READ_CHUNK_SIZE; | ||
824 | else | ||
825 | chunksize = size; | ||
826 | status = efi_call_phys3(fh->read, | ||
827 | initrds[j].handle, | ||
828 | &chunksize, addr); | ||
829 | if (status != EFI_SUCCESS) { | ||
830 | efi_printk("Failed to read initrd\n"); | ||
831 | goto free_initrd_total; | ||
832 | } | ||
833 | addr += chunksize; | ||
834 | size -= chunksize; | ||
835 | } | ||
836 | |||
837 | efi_call_phys1(fh->close, initrds[j].handle); | ||
838 | } | ||
839 | |||
840 | } | ||
841 | |||
842 | efi_call_phys1(sys_table->boottime->free_pool, initrds); | ||
843 | |||
844 | hdr->ramdisk_image = initrd_addr; | ||
845 | hdr->ramdisk_size = initrd_total; | ||
846 | |||
847 | return status; | ||
848 | |||
849 | free_initrd_total: | ||
850 | low_free(initrd_total, initrd_addr); | ||
851 | |||
852 | close_handles: | ||
853 | for (k = j; k < i; k++) | ||
854 | efi_call_phys1(fh->close, initrds[k].handle); | ||
855 | free_initrds: | ||
856 | efi_call_phys1(sys_table->boottime->free_pool, initrds); | ||
857 | fail: | ||
858 | hdr->ramdisk_image = 0; | ||
859 | hdr->ramdisk_size = 0; | ||
860 | |||
861 | return status; | ||
862 | } | ||
863 | 423 | ||
864 | /* | 424 | /* |
865 | * Because the x86 boot code expects to be passed a boot_params we | 425 | * Because the x86 boot code expects to be passed a boot_params we |
diff --git a/arch/x86/boot/compressed/eboot.h b/arch/x86/boot/compressed/eboot.h index 02265107cce3..81b6b652b46a 100644 --- a/arch/x86/boot/compressed/eboot.h +++ b/arch/x86/boot/compressed/eboot.h | |||
@@ -10,7 +10,6 @@ | |||
10 | #define SEG_GRANULARITY_4KB (1 << 0) | 10 | #define SEG_GRANULARITY_4KB (1 << 0) |
11 | 11 | ||
12 | #define DESC_TYPE_CODE_DATA (1 << 0) | 12 | #define DESC_TYPE_CODE_DATA (1 << 0) |
13 | #define EFI_READ_CHUNK_SIZE (1024 * 1024) | ||
14 | 13 | ||
15 | #define EFI_CONSOLE_OUT_DEVICE_GUID \ | 14 | #define EFI_CONSOLE_OUT_DEVICE_GUID \ |
16 | EFI_GUID(0xd3b36f2c, 0xd551, 0x11d4, 0x9a, 0x46, 0x0, 0x90, 0x27, \ | 15 | EFI_GUID(0xd3b36f2c, 0xd551, 0x11d4, 0x9a, 0x46, 0x0, 0x90, 0x27, \ |
diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c new file mode 100644 index 000000000000..8a83387fef92 --- /dev/null +++ b/drivers/firmware/efi/efi-stub-helper.c | |||
@@ -0,0 +1,463 @@ | |||
1 | /* | ||
2 | * Helper functions used by the EFI stub on multiple | ||
3 | * architectures. This should be #included by the EFI stub | ||
4 | * implementation files. | ||
5 | * | ||
6 | * Copyright 2011 Intel Corporation; author Matt Fleming | ||
7 | * | ||
8 | * This file is part of the Linux kernel, and is made available | ||
9 | * under the terms of the GNU General Public License version 2. | ||
10 | * | ||
11 | */ | ||
12 | #define EFI_READ_CHUNK_SIZE (1024 * 1024) | ||
13 | |||
14 | struct initrd { | ||
15 | efi_file_handle_t *handle; | ||
16 | u64 size; | ||
17 | }; | ||
18 | |||
19 | |||
20 | |||
21 | |||
22 | static void efi_char16_printk(efi_char16_t *str) | ||
23 | { | ||
24 | struct efi_simple_text_output_protocol *out; | ||
25 | |||
26 | out = (struct efi_simple_text_output_protocol *)sys_table->con_out; | ||
27 | efi_call_phys2(out->output_string, out, str); | ||
28 | } | ||
29 | |||
30 | static void efi_printk(char *str) | ||
31 | { | ||
32 | char *s8; | ||
33 | |||
34 | for (s8 = str; *s8; s8++) { | ||
35 | efi_char16_t ch[2] = { 0 }; | ||
36 | |||
37 | ch[0] = *s8; | ||
38 | if (*s8 == '\n') { | ||
39 | efi_char16_t nl[2] = { '\r', 0 }; | ||
40 | efi_char16_printk(nl); | ||
41 | } | ||
42 | |||
43 | efi_char16_printk(ch); | ||
44 | } | ||
45 | } | ||
46 | |||
47 | |||
48 | static efi_status_t __get_map(efi_memory_desc_t **map, unsigned long *map_size, | ||
49 | unsigned long *desc_size) | ||
50 | { | ||
51 | efi_memory_desc_t *m = NULL; | ||
52 | efi_status_t status; | ||
53 | unsigned long key; | ||
54 | u32 desc_version; | ||
55 | |||
56 | *map_size = sizeof(*m) * 32; | ||
57 | again: | ||
58 | /* | ||
59 | * Add an additional efi_memory_desc_t because we're doing an | ||
60 | * allocation which may be in a new descriptor region. | ||
61 | */ | ||
62 | *map_size += sizeof(*m); | ||
63 | status = efi_call_phys3(sys_table->boottime->allocate_pool, | ||
64 | EFI_LOADER_DATA, *map_size, (void **)&m); | ||
65 | if (status != EFI_SUCCESS) | ||
66 | goto fail; | ||
67 | |||
68 | status = efi_call_phys5(sys_table->boottime->get_memory_map, map_size, | ||
69 | m, &key, desc_size, &desc_version); | ||
70 | if (status == EFI_BUFFER_TOO_SMALL) { | ||
71 | efi_call_phys1(sys_table->boottime->free_pool, m); | ||
72 | goto again; | ||
73 | } | ||
74 | |||
75 | if (status != EFI_SUCCESS) | ||
76 | efi_call_phys1(sys_table->boottime->free_pool, m); | ||
77 | |||
78 | fail: | ||
79 | *map = m; | ||
80 | return status; | ||
81 | } | ||
82 | |||
83 | /* | ||
84 | * Allocate at the highest possible address that is not above 'max'. | ||
85 | */ | ||
86 | static efi_status_t high_alloc(unsigned long size, unsigned long align, | ||
87 | unsigned long *addr, unsigned long max) | ||
88 | { | ||
89 | unsigned long map_size, desc_size; | ||
90 | efi_memory_desc_t *map; | ||
91 | efi_status_t status; | ||
92 | unsigned long nr_pages; | ||
93 | u64 max_addr = 0; | ||
94 | int i; | ||
95 | |||
96 | status = __get_map(&map, &map_size, &desc_size); | ||
97 | if (status != EFI_SUCCESS) | ||
98 | goto fail; | ||
99 | |||
100 | nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; | ||
101 | again: | ||
102 | for (i = 0; i < map_size / desc_size; i++) { | ||
103 | efi_memory_desc_t *desc; | ||
104 | unsigned long m = (unsigned long)map; | ||
105 | u64 start, end; | ||
106 | |||
107 | desc = (efi_memory_desc_t *)(m + (i * desc_size)); | ||
108 | if (desc->type != EFI_CONVENTIONAL_MEMORY) | ||
109 | continue; | ||
110 | |||
111 | if (desc->num_pages < nr_pages) | ||
112 | continue; | ||
113 | |||
114 | start = desc->phys_addr; | ||
115 | end = start + desc->num_pages * (1UL << EFI_PAGE_SHIFT); | ||
116 | |||
117 | if ((start + size) > end || (start + size) > max) | ||
118 | continue; | ||
119 | |||
120 | if (end - size > max) | ||
121 | end = max; | ||
122 | |||
123 | if (round_down(end - size, align) < start) | ||
124 | continue; | ||
125 | |||
126 | start = round_down(end - size, align); | ||
127 | |||
128 | /* | ||
129 | * Don't allocate at 0x0. It will confuse code that | ||
130 | * checks pointers against NULL. | ||
131 | */ | ||
132 | if (start == 0x0) | ||
133 | continue; | ||
134 | |||
135 | if (start > max_addr) | ||
136 | max_addr = start; | ||
137 | } | ||
138 | |||
139 | if (!max_addr) | ||
140 | status = EFI_NOT_FOUND; | ||
141 | else { | ||
142 | status = efi_call_phys4(sys_table->boottime->allocate_pages, | ||
143 | EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, | ||
144 | nr_pages, &max_addr); | ||
145 | if (status != EFI_SUCCESS) { | ||
146 | max = max_addr; | ||
147 | max_addr = 0; | ||
148 | goto again; | ||
149 | } | ||
150 | |||
151 | *addr = max_addr; | ||
152 | } | ||
153 | |||
154 | free_pool: | ||
155 | efi_call_phys1(sys_table->boottime->free_pool, map); | ||
156 | |||
157 | fail: | ||
158 | return status; | ||
159 | } | ||
160 | |||
161 | /* | ||
162 | * Allocate at the lowest possible address. | ||
163 | */ | ||
164 | static efi_status_t low_alloc(unsigned long size, unsigned long align, | ||
165 | unsigned long *addr) | ||
166 | { | ||
167 | unsigned long map_size, desc_size; | ||
168 | efi_memory_desc_t *map; | ||
169 | efi_status_t status; | ||
170 | unsigned long nr_pages; | ||
171 | int i; | ||
172 | |||
173 | status = __get_map(&map, &map_size, &desc_size); | ||
174 | if (status != EFI_SUCCESS) | ||
175 | goto fail; | ||
176 | |||
177 | nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; | ||
178 | for (i = 0; i < map_size / desc_size; i++) { | ||
179 | efi_memory_desc_t *desc; | ||
180 | unsigned long m = (unsigned long)map; | ||
181 | u64 start, end; | ||
182 | |||
183 | desc = (efi_memory_desc_t *)(m + (i * desc_size)); | ||
184 | |||
185 | if (desc->type != EFI_CONVENTIONAL_MEMORY) | ||
186 | continue; | ||
187 | |||
188 | if (desc->num_pages < nr_pages) | ||
189 | continue; | ||
190 | |||
191 | start = desc->phys_addr; | ||
192 | end = start + desc->num_pages * (1UL << EFI_PAGE_SHIFT); | ||
193 | |||
194 | /* | ||
195 | * Don't allocate at 0x0. It will confuse code that | ||
196 | * checks pointers against NULL. Skip the first 8 | ||
197 | * bytes so we start at a nice even number. | ||
198 | */ | ||
199 | if (start == 0x0) | ||
200 | start += 8; | ||
201 | |||
202 | start = round_up(start, align); | ||
203 | if ((start + size) > end) | ||
204 | continue; | ||
205 | |||
206 | status = efi_call_phys4(sys_table->boottime->allocate_pages, | ||
207 | EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, | ||
208 | nr_pages, &start); | ||
209 | if (status == EFI_SUCCESS) { | ||
210 | *addr = start; | ||
211 | break; | ||
212 | } | ||
213 | } | ||
214 | |||
215 | if (i == map_size / desc_size) | ||
216 | status = EFI_NOT_FOUND; | ||
217 | |||
218 | free_pool: | ||
219 | efi_call_phys1(sys_table->boottime->free_pool, map); | ||
220 | fail: | ||
221 | return status; | ||
222 | } | ||
223 | |||
224 | static void low_free(unsigned long size, unsigned long addr) | ||
225 | { | ||
226 | unsigned long nr_pages; | ||
227 | |||
228 | nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; | ||
229 | efi_call_phys2(sys_table->boottime->free_pages, addr, nr_pages); | ||
230 | } | ||
231 | |||
232 | |||
233 | /* | ||
234 | * Check the cmdline for a LILO-style initrd= arguments. | ||
235 | * | ||
236 | * We only support loading an initrd from the same filesystem as the | ||
237 | * kernel image. | ||
238 | */ | ||
239 | static efi_status_t handle_ramdisks(efi_loaded_image_t *image, | ||
240 | struct setup_header *hdr) | ||
241 | { | ||
242 | struct initrd *initrds; | ||
243 | unsigned long initrd_addr; | ||
244 | efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID; | ||
245 | u64 initrd_total; | ||
246 | efi_file_io_interface_t *io; | ||
247 | efi_file_handle_t *fh; | ||
248 | efi_status_t status; | ||
249 | int nr_initrds; | ||
250 | char *str; | ||
251 | int i, j, k; | ||
252 | |||
253 | initrd_addr = 0; | ||
254 | initrd_total = 0; | ||
255 | |||
256 | str = (char *)(unsigned long)hdr->cmd_line_ptr; | ||
257 | |||
258 | j = 0; /* See close_handles */ | ||
259 | |||
260 | if (!str || !*str) | ||
261 | return EFI_SUCCESS; | ||
262 | |||
263 | for (nr_initrds = 0; *str; nr_initrds++) { | ||
264 | str = strstr(str, "initrd="); | ||
265 | if (!str) | ||
266 | break; | ||
267 | |||
268 | str += 7; | ||
269 | |||
270 | /* Skip any leading slashes */ | ||
271 | while (*str == '/' || *str == '\\') | ||
272 | str++; | ||
273 | |||
274 | while (*str && *str != ' ' && *str != '\n') | ||
275 | str++; | ||
276 | } | ||
277 | |||
278 | if (!nr_initrds) | ||
279 | return EFI_SUCCESS; | ||
280 | |||
281 | status = efi_call_phys3(sys_table->boottime->allocate_pool, | ||
282 | EFI_LOADER_DATA, | ||
283 | nr_initrds * sizeof(*initrds), | ||
284 | &initrds); | ||
285 | if (status != EFI_SUCCESS) { | ||
286 | efi_printk("Failed to alloc mem for initrds\n"); | ||
287 | goto fail; | ||
288 | } | ||
289 | |||
290 | str = (char *)(unsigned long)hdr->cmd_line_ptr; | ||
291 | for (i = 0; i < nr_initrds; i++) { | ||
292 | struct initrd *initrd; | ||
293 | efi_file_handle_t *h; | ||
294 | efi_file_info_t *info; | ||
295 | efi_char16_t filename_16[256]; | ||
296 | unsigned long info_sz; | ||
297 | efi_guid_t info_guid = EFI_FILE_INFO_ID; | ||
298 | efi_char16_t *p; | ||
299 | u64 file_sz; | ||
300 | |||
301 | str = strstr(str, "initrd="); | ||
302 | if (!str) | ||
303 | break; | ||
304 | |||
305 | str += 7; | ||
306 | |||
307 | initrd = &initrds[i]; | ||
308 | p = filename_16; | ||
309 | |||
310 | /* Skip any leading slashes */ | ||
311 | while (*str == '/' || *str == '\\') | ||
312 | str++; | ||
313 | |||
314 | while (*str && *str != ' ' && *str != '\n') { | ||
315 | if ((u8 *)p >= (u8 *)filename_16 + sizeof(filename_16)) | ||
316 | break; | ||
317 | |||
318 | if (*str == '/') { | ||
319 | *p++ = '\\'; | ||
320 | *str++; | ||
321 | } else { | ||
322 | *p++ = *str++; | ||
323 | } | ||
324 | } | ||
325 | |||
326 | *p = '\0'; | ||
327 | |||
328 | /* Only open the volume once. */ | ||
329 | if (!i) { | ||
330 | efi_boot_services_t *boottime; | ||
331 | |||
332 | boottime = sys_table->boottime; | ||
333 | |||
334 | status = efi_call_phys3(boottime->handle_protocol, | ||
335 | image->device_handle, &fs_proto, &io); | ||
336 | if (status != EFI_SUCCESS) { | ||
337 | efi_printk("Failed to handle fs_proto\n"); | ||
338 | goto free_initrds; | ||
339 | } | ||
340 | |||
341 | status = efi_call_phys2(io->open_volume, io, &fh); | ||
342 | if (status != EFI_SUCCESS) { | ||
343 | efi_printk("Failed to open volume\n"); | ||
344 | goto free_initrds; | ||
345 | } | ||
346 | } | ||
347 | |||
348 | status = efi_call_phys5(fh->open, fh, &h, filename_16, | ||
349 | EFI_FILE_MODE_READ, (u64)0); | ||
350 | if (status != EFI_SUCCESS) { | ||
351 | efi_printk("Failed to open initrd file: "); | ||
352 | efi_char16_printk(filename_16); | ||
353 | efi_printk("\n"); | ||
354 | goto close_handles; | ||
355 | } | ||
356 | |||
357 | initrd->handle = h; | ||
358 | |||
359 | info_sz = 0; | ||
360 | status = efi_call_phys4(h->get_info, h, &info_guid, | ||
361 | &info_sz, NULL); | ||
362 | if (status != EFI_BUFFER_TOO_SMALL) { | ||
363 | efi_printk("Failed to get initrd info size\n"); | ||
364 | goto close_handles; | ||
365 | } | ||
366 | |||
367 | grow: | ||
368 | status = efi_call_phys3(sys_table->boottime->allocate_pool, | ||
369 | EFI_LOADER_DATA, info_sz, &info); | ||
370 | if (status != EFI_SUCCESS) { | ||
371 | efi_printk("Failed to alloc mem for initrd info\n"); | ||
372 | goto close_handles; | ||
373 | } | ||
374 | |||
375 | status = efi_call_phys4(h->get_info, h, &info_guid, | ||
376 | &info_sz, info); | ||
377 | if (status == EFI_BUFFER_TOO_SMALL) { | ||
378 | efi_call_phys1(sys_table->boottime->free_pool, info); | ||
379 | goto grow; | ||
380 | } | ||
381 | |||
382 | file_sz = info->file_size; | ||
383 | efi_call_phys1(sys_table->boottime->free_pool, info); | ||
384 | |||
385 | if (status != EFI_SUCCESS) { | ||
386 | efi_printk("Failed to get initrd info\n"); | ||
387 | goto close_handles; | ||
388 | } | ||
389 | |||
390 | initrd->size = file_sz; | ||
391 | initrd_total += file_sz; | ||
392 | } | ||
393 | |||
394 | if (initrd_total) { | ||
395 | unsigned long addr; | ||
396 | |||
397 | /* | ||
398 | * Multiple initrd's need to be at consecutive | ||
399 | * addresses in memory, so allocate enough memory for | ||
400 | * all the initrd's. | ||
401 | */ | ||
402 | status = high_alloc(initrd_total, 0x1000, | ||
403 | &initrd_addr, hdr->initrd_addr_max); | ||
404 | if (status != EFI_SUCCESS) { | ||
405 | efi_printk("Failed to alloc highmem for initrds\n"); | ||
406 | goto close_handles; | ||
407 | } | ||
408 | |||
409 | /* We've run out of free low memory. */ | ||
410 | if (initrd_addr > hdr->initrd_addr_max) { | ||
411 | efi_printk("We've run out of free low memory\n"); | ||
412 | status = EFI_INVALID_PARAMETER; | ||
413 | goto free_initrd_total; | ||
414 | } | ||
415 | |||
416 | addr = initrd_addr; | ||
417 | for (j = 0; j < nr_initrds; j++) { | ||
418 | u64 size; | ||
419 | |||
420 | size = initrds[j].size; | ||
421 | while (size) { | ||
422 | u64 chunksize; | ||
423 | if (size > EFI_READ_CHUNK_SIZE) | ||
424 | chunksize = EFI_READ_CHUNK_SIZE; | ||
425 | else | ||
426 | chunksize = size; | ||
427 | status = efi_call_phys3(fh->read, | ||
428 | initrds[j].handle, | ||
429 | &chunksize, addr); | ||
430 | if (status != EFI_SUCCESS) { | ||
431 | efi_printk("Failed to read initrd\n"); | ||
432 | goto free_initrd_total; | ||
433 | } | ||
434 | addr += chunksize; | ||
435 | size -= chunksize; | ||
436 | } | ||
437 | |||
438 | efi_call_phys1(fh->close, initrds[j].handle); | ||
439 | } | ||
440 | |||
441 | } | ||
442 | |||
443 | efi_call_phys1(sys_table->boottime->free_pool, initrds); | ||
444 | |||
445 | hdr->ramdisk_image = initrd_addr; | ||
446 | hdr->ramdisk_size = initrd_total; | ||
447 | |||
448 | return status; | ||
449 | |||
450 | free_initrd_total: | ||
451 | low_free(initrd_total, initrd_addr); | ||
452 | |||
453 | close_handles: | ||
454 | for (k = j; k < i; k++) | ||
455 | efi_call_phys1(fh->close, initrds[k].handle); | ||
456 | free_initrds: | ||
457 | efi_call_phys1(sys_table->boottime->free_pool, initrds); | ||
458 | fail: | ||
459 | hdr->ramdisk_image = 0; | ||
460 | hdr->ramdisk_size = 0; | ||
461 | |||
462 | return status; | ||
463 | } | ||