]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - wimboot/wimboot-2.7.3/src/paging.c
1.1.07 release
[Ventoy.git] / wimboot / wimboot-2.7.3 / src / paging.c
1 /*
2 * Copyright (C) 2021 Michael Brown <mbrown@fensystems.co.uk>.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 */
19
20 /**
21 * @file
22 *
23 * Paging
24 *
25 */
26
27 #include <stdint.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <assert.h>
31 #include "wimboot.h"
32 #include "memmap.h"
33 #include "paging.h"
34
35 /** Virtual address used as a 2MB window during relocation */
36 #define COPY_WINDOW 0x200000
37
38 /** Paging is available */
39 int paging;
40
41 /** Page directory pointer table */
42 static uint64_t pdpt[4] __attribute__ (( aligned ( PAGE_SIZE ) ));
43
44 /** Page directories */
45 static uint64_t pd[2048] __attribute__ (( aligned ( PAGE_SIZE ) ));
46
47 /**
48 * Check that paging can be supported
49 *
50 * @ret supported Paging can be supported on this CPU
51 */
52 static int paging_supported ( void ) {
53 uint32_t eax;
54 uint32_t ebx;
55 uint32_t ecx;
56 uint32_t edx;
57
58 /* Get CPU features */
59 __asm__ ( "cpuid"
60 : "=a" ( eax ), "=b" ( ebx ), "=c" ( ecx ), "=d" ( edx )
61 : "0" ( CPUID_FEATURES ) );
62
63 return ( edx & CPUID_FEATURE_EDX_PAE );
64 }
65
66 /**
67 * Map 2MB page directory entry containing address
68 *
69 * @v vaddr Virtual address
70 * @v paddr Physical address
71 */
72 static void map_page ( uint32_t vaddr, uint64_t paddr ) {
73 char *byte = ( ( char * ) ( intptr_t ) vaddr );
74 unsigned int index;
75
76 /* Sanity checks */
77 assert ( ( vaddr & ( PAGE_SIZE_2MB - 1 ) ) == 0 );
78 assert ( ( paddr & ( PAGE_SIZE_2MB - 1 ) ) == 0 );
79
80 /* Populate page directory entry */
81 index = ( vaddr / PAGE_SIZE_2MB );
82 pd[index] = ( paddr | PG_P | PG_RW | PG_US | PG_PS );
83
84 /* Invalidate TLB */
85 __asm__ __volatile__ ( "invlpg %0" : : "m" ( *byte ) );
86 }
87
88 /**
89 * Initialise paging
90 *
91 */
92 void init_paging ( void ) {
93 uint32_t addr;
94 unsigned int i;
95
96 /* Do nothing if paging is disabled */
97 if ( cmdline_linear ) {
98 DBG ( "Paging disabled\n" );
99 return;
100 }
101
102 /* Check for PAE */
103 if ( ! paging_supported() ) {
104 DBG ( "Paging not possible on this CPU\n" );
105 return;
106 }
107
108 /* Initialise page directory entries */
109 addr = 0;
110 do {
111 map_page ( addr, addr );
112 addr += PAGE_SIZE_2MB;
113 } while ( addr );
114
115 /* Initialise page directory pointer table */
116 for ( i = 0 ; i < ( sizeof ( pdpt ) / sizeof ( pdpt[0] ) ) ; i++ ) {
117 addr = ( ( intptr_t ) &pd[ i * PAGE_SIZE / sizeof ( pd[0] ) ] );
118 pdpt[i] = ( addr | PG_P );
119 }
120
121 /* Mark paging as available */
122 paging = 1;
123 }
124
125 /**
126 * Enable paging
127 *
128 * @v state Saved paging state to fill in
129 */
130 void enable_paging ( struct paging_state *state ) {
131 unsigned long cr0;
132 unsigned long cr3;
133 unsigned long cr4;
134
135 /* Do nothing if paging is unavailable */
136 if ( ! paging )
137 return;
138
139 /* Save paging state */
140 __asm__ __volatile__ ( "mov %%cr0, %0\n\t"
141 "mov %%cr3, %1\n\t"
142 "mov %%cr4, %2\n\t"
143 : "=r" ( cr0 ), "=r" ( cr3 ), "=r" ( cr4 ) );
144 state->cr0 = cr0;
145 state->cr3 = cr3;
146 state->cr4 = cr4;
147
148 /* Disable any existing paging */
149 __asm__ __volatile__ ( "mov %0, %%cr0" : : "r" ( cr0 & ~CR0_PG ) );
150
151 /* Enable PAE */
152 __asm__ __volatile__ ( "mov %0, %%cr4" : : "r" ( cr4 | CR4_PAE ) );
153
154 /* Load page directory pointer table */
155 __asm__ __volatile__ ( "mov %0, %%cr3" : : "r" ( pdpt ) );
156
157 /* Enable paging */
158 __asm__ __volatile__ ( "mov %0, %%cr0" : : "r" ( cr0 | CR0_PG ) );
159 }
160
161 /**
162 * Disable paging
163 *
164 * @v state Previously saved paging state
165 */
166 void disable_paging ( struct paging_state *state ) {
167 unsigned long cr0 = state->cr0;
168 unsigned long cr3 = state->cr3;
169 unsigned long cr4 = state->cr4;
170
171 /* Do nothing if paging is unavailable */
172 if ( ! paging )
173 return;
174
175 /* Disable paging */
176 __asm__ __volatile__ ( "mov %0, %%cr0" : : "r" ( cr0 & ~CR0_PG ) );
177
178 /* Restore saved paging state */
179 __asm__ __volatile__ ( "mov %2, %%cr4\n\t"
180 "mov %1, %%cr3\n\t"
181 "mov %0, %%cr0\n\t"
182 : : "r" ( cr0 ), "r" ( cr3 ), "r" ( cr4 ) );
183 }
184
185 /**
186 * Relocate data out of 32-bit address space, if possible
187 *
188 * @v data Start of data
189 * @v len Length of data
190 * @ret start Physical start address
191 */
192 uint64_t relocate_memory_high ( void *data, size_t len ) {
193 intptr_t end = ( ( ( intptr_t ) data ) + len );
194 struct e820_entry *e820 = NULL;
195 uint64_t start;
196 uint64_t dest;
197 size_t offset;
198 size_t frag_len;
199
200 /* Do nothing if paging is unavailable */
201 if ( ! paging )
202 return ( ( intptr_t ) data );
203
204 /* Read system memory map */
205 while ( ( e820 = memmap_next ( e820 ) ) != NULL ) {
206
207 /* Find highest compatible placement within this region */
208 start = ( e820->start + e820->len );
209 if ( start < ADDR_4GB )
210 continue;
211 start = ( ( ( start - end ) & ~( PAGE_SIZE_2MB - 1 ) ) + end );
212 start -= len;
213 if ( start < e820->start )
214 continue;
215 if ( start < ADDR_4GB )
216 continue;
217
218 /* Relocate to this region */
219 dest = start;
220 while ( len ) {
221
222 /* Calculate length within this 2MB page */
223 offset = ( ( ( intptr_t ) data ) &
224 ( PAGE_SIZE_2MB - 1 ) );
225 frag_len = ( PAGE_SIZE_2MB - offset );
226 if ( frag_len > len )
227 frag_len = len;
228
229 /* Map copy window to destination */
230 map_page ( COPY_WINDOW,
231 ( dest & ~( PAGE_SIZE_2MB - 1 ) ) );
232
233 /* Copy data through copy window */
234 memcpy ( ( ( ( void * ) COPY_WINDOW ) + offset ),
235 data, frag_len );
236
237 /* Map original page to destination */
238 map_page ( ( ( ( intptr_t ) data ) - offset ),
239 ( dest & ~( PAGE_SIZE_2MB - 1 ) ) );
240
241 /* Move to next 2MB page */
242 data += frag_len;
243 dest += frag_len;
244 len -= frag_len;
245 }
246
247 /* Remap copy window */
248 map_page ( COPY_WINDOW, COPY_WINDOW );
249
250 return start;
251 }
252
253 /* Leave at original location */
254 return ( ( intptr_t ) data );
255 }