xref: /minix3/minix/kernel/arch/i386/pg_utils.c (revision bf609e10121541e123a8be251042bb3d29429c59)
1433d6423SLionel Sambuc 
2433d6423SLionel Sambuc #include <minix/cpufeature.h>
3433d6423SLionel Sambuc 
4433d6423SLionel Sambuc #include <assert.h>
5433d6423SLionel Sambuc #include "kernel/kernel.h"
6433d6423SLionel Sambuc #include "arch_proto.h"
7433d6423SLionel Sambuc 
8433d6423SLionel Sambuc #include <string.h>
9433d6423SLionel Sambuc 
10433d6423SLionel Sambuc /* These are set/computed in kernel.lds. */
11433d6423SLionel Sambuc extern char _kern_vir_base, _kern_phys_base, _kern_size;
12433d6423SLionel Sambuc 
13433d6423SLionel Sambuc /* Retrieve the absolute values to something we can use. */
14433d6423SLionel Sambuc static phys_bytes kern_vir_start = (phys_bytes) &_kern_vir_base;
15433d6423SLionel Sambuc static phys_bytes kern_phys_start = (phys_bytes) &_kern_phys_base;
16433d6423SLionel Sambuc static phys_bytes kern_kernlen = (phys_bytes) &_kern_size;
17433d6423SLionel Sambuc 
18433d6423SLionel Sambuc /* page directory we can use to map things */
19433d6423SLionel Sambuc static u32_t pagedir[1024]  __aligned(4096);
20433d6423SLionel Sambuc 
print_memmap(kinfo_t * cbi)21433d6423SLionel Sambuc void print_memmap(kinfo_t *cbi)
22433d6423SLionel Sambuc {
23433d6423SLionel Sambuc         int m;
24433d6423SLionel Sambuc         assert(cbi->mmap_size < MAXMEMMAP);
25433d6423SLionel Sambuc         for(m = 0; m < cbi->mmap_size; m++) {
26433d6423SLionel Sambuc 		phys_bytes addr = cbi->memmap[m].mm_base_addr, endit = cbi->memmap[m].mm_base_addr + cbi->memmap[m].mm_length;
27433d6423SLionel Sambuc                 printf("%08lx-%08lx ",addr, endit);
28433d6423SLionel Sambuc         }
29433d6423SLionel Sambuc         printf("\nsize %08lx\n", cbi->mmap_size);
30433d6423SLionel Sambuc }
31433d6423SLionel Sambuc 
cut_memmap(kinfo_t * cbi,phys_bytes start,phys_bytes end)32433d6423SLionel Sambuc void cut_memmap(kinfo_t *cbi, phys_bytes start, phys_bytes end)
33433d6423SLionel Sambuc {
34433d6423SLionel Sambuc         int m;
35433d6423SLionel Sambuc         phys_bytes o;
36433d6423SLionel Sambuc 
37433d6423SLionel Sambuc         if((o=start % I386_PAGE_SIZE))
38433d6423SLionel Sambuc                 start -= o;
39433d6423SLionel Sambuc         if((o=end % I386_PAGE_SIZE))
40433d6423SLionel Sambuc                 end += I386_PAGE_SIZE - o;
41433d6423SLionel Sambuc 
42433d6423SLionel Sambuc 	assert(kernel_may_alloc);
43433d6423SLionel Sambuc 
44433d6423SLionel Sambuc         for(m = 0; m < cbi->mmap_size; m++) {
45433d6423SLionel Sambuc                 phys_bytes substart = start, subend = end;
46433d6423SLionel Sambuc                 phys_bytes memaddr = cbi->memmap[m].mm_base_addr,
47433d6423SLionel Sambuc                         memend = cbi->memmap[m].mm_base_addr + cbi->memmap[m].mm_length;
48433d6423SLionel Sambuc 
49433d6423SLionel Sambuc                 /* adjust cut range to be a subset of the free memory */
50433d6423SLionel Sambuc                 if(substart < memaddr) substart = memaddr;
51433d6423SLionel Sambuc                 if(subend > memend) subend = memend;
52433d6423SLionel Sambuc                 if(substart >= subend) continue;
53433d6423SLionel Sambuc 
54433d6423SLionel Sambuc                 /* if there is any overlap, forget this one and add
55433d6423SLionel Sambuc                  * 1-2 subranges back
56433d6423SLionel Sambuc                  */
57433d6423SLionel Sambuc                 cbi->memmap[m].mm_base_addr = cbi->memmap[m].mm_length = 0;
58433d6423SLionel Sambuc                 if(substart > memaddr)
59433d6423SLionel Sambuc                         add_memmap(cbi, memaddr, substart-memaddr);
60433d6423SLionel Sambuc                 if(subend < memend)
61433d6423SLionel Sambuc                         add_memmap(cbi, subend, memend-subend);
62433d6423SLionel Sambuc         }
63433d6423SLionel Sambuc }
64433d6423SLionel Sambuc 
alloc_lowest(kinfo_t * cbi,phys_bytes len)65433d6423SLionel Sambuc phys_bytes alloc_lowest(kinfo_t *cbi, phys_bytes len)
66433d6423SLionel Sambuc {
67433d6423SLionel Sambuc 	/* Allocate the lowest physical page we have. */
68433d6423SLionel Sambuc 	int m;
69433d6423SLionel Sambuc #define EMPTY 0xffffffff
70433d6423SLionel Sambuc 	phys_bytes lowest = EMPTY;
71433d6423SLionel Sambuc 	assert(len > 0);
72433d6423SLionel Sambuc 	len = roundup(len, I386_PAGE_SIZE);
73433d6423SLionel Sambuc 
74433d6423SLionel Sambuc 	assert(kernel_may_alloc);
75433d6423SLionel Sambuc 
76433d6423SLionel Sambuc 	for(m = 0; m < cbi->mmap_size; m++) {
77433d6423SLionel Sambuc 		if(cbi->memmap[m].mm_length < len) continue;
78433d6423SLionel Sambuc 		if(cbi->memmap[m].mm_base_addr < lowest) lowest = cbi->memmap[m].mm_base_addr;
79433d6423SLionel Sambuc 	}
80433d6423SLionel Sambuc 	assert(lowest != EMPTY);
81433d6423SLionel Sambuc 	cut_memmap(cbi, lowest, len);
82433d6423SLionel Sambuc 	cbi->kernel_allocated_bytes_dynamic += len;
83433d6423SLionel Sambuc 	return lowest;
84433d6423SLionel Sambuc }
85433d6423SLionel Sambuc 
add_memmap(kinfo_t * cbi,u64_t addr,u64_t len)86433d6423SLionel Sambuc void add_memmap(kinfo_t *cbi, u64_t addr, u64_t len)
87433d6423SLionel Sambuc {
88433d6423SLionel Sambuc         int m;
89433d6423SLionel Sambuc #define LIMIT 0xFFFFF000
90433d6423SLionel Sambuc         /* Truncate available memory at 4GB as the rest of minix
91433d6423SLionel Sambuc          * currently can't deal with any bigger.
92433d6423SLionel Sambuc          */
93433d6423SLionel Sambuc         if(addr > LIMIT) return;
94433d6423SLionel Sambuc         if(addr + len > LIMIT) {
95433d6423SLionel Sambuc                 len -= (addr + len - LIMIT);
96433d6423SLionel Sambuc         }
97433d6423SLionel Sambuc         assert(cbi->mmap_size < MAXMEMMAP);
98433d6423SLionel Sambuc         if(len == 0) return;
99433d6423SLionel Sambuc 	addr = roundup(addr, I386_PAGE_SIZE);
100433d6423SLionel Sambuc 	len = rounddown(len, I386_PAGE_SIZE);
101433d6423SLionel Sambuc 
102433d6423SLionel Sambuc 	assert(kernel_may_alloc);
103433d6423SLionel Sambuc 
104433d6423SLionel Sambuc         for(m = 0; m < MAXMEMMAP; m++) {
105433d6423SLionel Sambuc 		phys_bytes highmark;
106433d6423SLionel Sambuc                 if(cbi->memmap[m].mm_length) continue;
107433d6423SLionel Sambuc                 cbi->memmap[m].mm_base_addr = addr;
108433d6423SLionel Sambuc                 cbi->memmap[m].mm_length = len;
109433d6423SLionel Sambuc                 cbi->memmap[m].mm_type = MULTIBOOT_MEMORY_AVAILABLE;
110433d6423SLionel Sambuc                 if(m >= cbi->mmap_size)
111433d6423SLionel Sambuc                         cbi->mmap_size = m+1;
112433d6423SLionel Sambuc 		highmark = addr + len;
113433d6423SLionel Sambuc 		if(highmark > cbi->mem_high_phys) {
114433d6423SLionel Sambuc 			cbi->mem_high_phys = highmark;
115433d6423SLionel Sambuc 		}
116433d6423SLionel Sambuc 
117433d6423SLionel Sambuc                 return;
118433d6423SLionel Sambuc         }
119433d6423SLionel Sambuc 
120433d6423SLionel Sambuc         panic("no available memmap slot");
121433d6423SLionel Sambuc }
122433d6423SLionel Sambuc 
alloc_pagetable(phys_bytes * ph)123433d6423SLionel Sambuc u32_t *alloc_pagetable(phys_bytes *ph)
124433d6423SLionel Sambuc {
125433d6423SLionel Sambuc 	u32_t *ret;
126433d6423SLionel Sambuc #define PG_PAGETABLES 6
127433d6423SLionel Sambuc 	static u32_t pagetables[PG_PAGETABLES][1024]  __aligned(4096);
128433d6423SLionel Sambuc 	static int pt_inuse = 0;
129433d6423SLionel Sambuc 	if(pt_inuse >= PG_PAGETABLES) panic("no more pagetables");
130433d6423SLionel Sambuc 	assert(sizeof(pagetables[pt_inuse]) == I386_PAGE_SIZE);
131433d6423SLionel Sambuc 	ret = pagetables[pt_inuse++];
132433d6423SLionel Sambuc 	*ph = vir2phys(ret);
133433d6423SLionel Sambuc 	return ret;
134433d6423SLionel Sambuc }
135433d6423SLionel Sambuc 
136433d6423SLionel Sambuc #define PAGE_KB (I386_PAGE_SIZE / 1024)
137433d6423SLionel Sambuc 
pg_alloc_page(kinfo_t * cbi)138433d6423SLionel Sambuc phys_bytes pg_alloc_page(kinfo_t *cbi)
139433d6423SLionel Sambuc {
140433d6423SLionel Sambuc 	int m;
141433d6423SLionel Sambuc 	multiboot_memory_map_t *mmap;
142433d6423SLionel Sambuc 
143433d6423SLionel Sambuc 	assert(kernel_may_alloc);
144433d6423SLionel Sambuc 
145433d6423SLionel Sambuc 	for(m = cbi->mmap_size-1; m >= 0; m--) {
146433d6423SLionel Sambuc 		mmap = &cbi->memmap[m];
147433d6423SLionel Sambuc 		if(!mmap->mm_length) continue;
148433d6423SLionel Sambuc 		assert(mmap->mm_length > 0);
149433d6423SLionel Sambuc 		assert(!(mmap->mm_length % I386_PAGE_SIZE));
150433d6423SLionel Sambuc 		assert(!(mmap->mm_base_addr % I386_PAGE_SIZE));
151433d6423SLionel Sambuc 
152433d6423SLionel Sambuc 		mmap->mm_length -= I386_PAGE_SIZE;
153433d6423SLionel Sambuc 
154433d6423SLionel Sambuc                 cbi->kernel_allocated_bytes_dynamic += I386_PAGE_SIZE;
155433d6423SLionel Sambuc 
156433d6423SLionel Sambuc 		return mmap->mm_base_addr + mmap->mm_length;
157433d6423SLionel Sambuc 	}
158433d6423SLionel Sambuc 
159433d6423SLionel Sambuc 	panic("can't find free memory");
160433d6423SLionel Sambuc }
161433d6423SLionel Sambuc 
pg_identity(kinfo_t * cbi)162433d6423SLionel Sambuc void pg_identity(kinfo_t *cbi)
163433d6423SLionel Sambuc {
1640a6a1f1dSLionel Sambuc 	uint32_t i;
165433d6423SLionel Sambuc 	phys_bytes phys;
166433d6423SLionel Sambuc 
167433d6423SLionel Sambuc 	/* We map memory that does not correspond to physical memory
168433d6423SLionel Sambuc 	 * as non-cacheable. Make sure we know what it is.
169433d6423SLionel Sambuc 	 */
170433d6423SLionel Sambuc 	assert(cbi->mem_high_phys);
171433d6423SLionel Sambuc 
172433d6423SLionel Sambuc         /* Set up an identity mapping page directory */
173433d6423SLionel Sambuc         for(i = 0; i < I386_VM_DIR_ENTRIES; i++) {
174433d6423SLionel Sambuc 		u32_t flags = I386_VM_PRESENT | I386_VM_BIGPAGE
175433d6423SLionel Sambuc 			| I386_VM_USER
176433d6423SLionel Sambuc 			| I386_VM_WRITE;
177433d6423SLionel Sambuc                 phys = i * I386_BIG_PAGE_SIZE;
178433d6423SLionel Sambuc 		if((cbi->mem_high_phys & I386_VM_ADDR_MASK_4MB)
179433d6423SLionel Sambuc 			<= (phys & I386_VM_ADDR_MASK_4MB)) {
180433d6423SLionel Sambuc 			flags |= I386_VM_PWT | I386_VM_PCD;
181433d6423SLionel Sambuc 		}
182433d6423SLionel Sambuc                 pagedir[i] =  phys | flags;
183433d6423SLionel Sambuc         }
184433d6423SLionel Sambuc }
185433d6423SLionel Sambuc 
pg_mapkernel(void)186433d6423SLionel Sambuc int pg_mapkernel(void)
187433d6423SLionel Sambuc {
188433d6423SLionel Sambuc 	int pde;
189433d6423SLionel Sambuc 	u32_t mapped = 0, kern_phys = kern_phys_start;
190433d6423SLionel Sambuc 
191433d6423SLionel Sambuc         assert(!(kern_vir_start % I386_BIG_PAGE_SIZE));
192433d6423SLionel Sambuc         assert(!(kern_phys % I386_BIG_PAGE_SIZE));
193433d6423SLionel Sambuc         pde = kern_vir_start / I386_BIG_PAGE_SIZE; /* start pde */
194433d6423SLionel Sambuc 	while(mapped < kern_kernlen) {
195433d6423SLionel Sambuc 	        pagedir[pde] = kern_phys | I386_VM_PRESENT |
196433d6423SLionel Sambuc 			I386_VM_BIGPAGE | I386_VM_WRITE;
197433d6423SLionel Sambuc 		mapped += I386_BIG_PAGE_SIZE;
198433d6423SLionel Sambuc 		kern_phys += I386_BIG_PAGE_SIZE;
199433d6423SLionel Sambuc 		pde++;
200433d6423SLionel Sambuc 	}
201433d6423SLionel Sambuc 	return pde;	/* free pde */
202433d6423SLionel Sambuc }
203433d6423SLionel Sambuc 
vm_enable_paging(void)204433d6423SLionel Sambuc void vm_enable_paging(void)
205433d6423SLionel Sambuc {
206433d6423SLionel Sambuc         u32_t cr0, cr4;
207433d6423SLionel Sambuc         int pgeok;
208433d6423SLionel Sambuc 
209433d6423SLionel Sambuc         pgeok = _cpufeature(_CPUF_I386_PGE);
210433d6423SLionel Sambuc 
211*bf609e10Srlfnb #ifdef PAE
212*bf609e10Srlfnb 	if(_cpufeature(_CPUF_I386_PAE) == 0)
213*bf609e10Srlfnb 		panic("kernel built with PAE support, CPU seems to lack PAE support?\n");
214*bf609e10Srlfnb #endif
215*bf609e10Srlfnb 
216433d6423SLionel Sambuc         cr0= read_cr0();
217433d6423SLionel Sambuc         cr4= read_cr4();
218433d6423SLionel Sambuc 
219433d6423SLionel Sambuc 	/* The boot loader should have put us in protected mode. */
220433d6423SLionel Sambuc 	assert(cr0 & I386_CR0_PE);
221433d6423SLionel Sambuc 
222433d6423SLionel Sambuc         /* First clear PG and PGE flag, as PGE must be enabled after PG. */
223433d6423SLionel Sambuc         write_cr0(cr0 & ~I386_CR0_PG);
224433d6423SLionel Sambuc         write_cr4(cr4 & ~(I386_CR4_PGE | I386_CR4_PSE));
225433d6423SLionel Sambuc 
226433d6423SLionel Sambuc         cr0= read_cr0();
227433d6423SLionel Sambuc         cr4= read_cr4();
228433d6423SLionel Sambuc 
229433d6423SLionel Sambuc         /* Our page table contains 4MB entries. */
230433d6423SLionel Sambuc         cr4 |= I386_CR4_PSE;
231433d6423SLionel Sambuc 
232433d6423SLionel Sambuc         write_cr4(cr4);
233433d6423SLionel Sambuc 
234433d6423SLionel Sambuc         /* First enable paging, then enable global page flag. */
235433d6423SLionel Sambuc         cr0 |= I386_CR0_PG;
236433d6423SLionel Sambuc         write_cr0(cr0);
237433d6423SLionel Sambuc         cr0 |= I386_CR0_WP;
238433d6423SLionel Sambuc         write_cr0(cr0);
239433d6423SLionel Sambuc 
240433d6423SLionel Sambuc         /* May we enable these features? */
241433d6423SLionel Sambuc         if(pgeok)
242433d6423SLionel Sambuc                 cr4 |= I386_CR4_PGE;
243433d6423SLionel Sambuc 
244433d6423SLionel Sambuc         write_cr4(cr4);
245433d6423SLionel Sambuc }
246433d6423SLionel Sambuc 
pg_load(void)2476077d1adSDr. Florian Grätz phys_bytes pg_load(void)
248433d6423SLionel Sambuc {
249433d6423SLionel Sambuc 	phys_bytes phpagedir = vir2phys(pagedir);
250433d6423SLionel Sambuc         write_cr3(phpagedir);
251433d6423SLionel Sambuc 	return phpagedir;
252433d6423SLionel Sambuc }
253433d6423SLionel Sambuc 
pg_clear(void)254433d6423SLionel Sambuc void pg_clear(void)
255433d6423SLionel Sambuc {
256433d6423SLionel Sambuc 	memset(pagedir, 0, sizeof(pagedir));
257433d6423SLionel Sambuc }
258433d6423SLionel Sambuc 
pg_rounddown(phys_bytes b)259433d6423SLionel Sambuc phys_bytes pg_rounddown(phys_bytes b)
260433d6423SLionel Sambuc {
261433d6423SLionel Sambuc 	phys_bytes o;
262433d6423SLionel Sambuc 	if(!(o = b % I386_PAGE_SIZE))
263433d6423SLionel Sambuc 		return b;
264433d6423SLionel Sambuc 	return b  - o;
265433d6423SLionel Sambuc }
266433d6423SLionel Sambuc 
pg_map(phys_bytes phys,vir_bytes vaddr,vir_bytes vaddr_end,kinfo_t * cbi)267433d6423SLionel Sambuc void pg_map(phys_bytes phys, vir_bytes vaddr, vir_bytes vaddr_end,
268433d6423SLionel Sambuc 	kinfo_t *cbi)
269433d6423SLionel Sambuc {
270433d6423SLionel Sambuc 	static int mapped_pde = -1;
271433d6423SLionel Sambuc 	static u32_t *pt = NULL;
272433d6423SLionel Sambuc 	int pde, pte;
273433d6423SLionel Sambuc 
274433d6423SLionel Sambuc 	assert(kernel_may_alloc);
275433d6423SLionel Sambuc 
276433d6423SLionel Sambuc 	if(phys == PG_ALLOCATEME) {
277433d6423SLionel Sambuc 		assert(!(vaddr % I386_PAGE_SIZE));
278433d6423SLionel Sambuc 	} else  {
279433d6423SLionel Sambuc 		assert((vaddr % I386_PAGE_SIZE) == (phys % I386_PAGE_SIZE));
280433d6423SLionel Sambuc 		vaddr = pg_rounddown(vaddr);
281433d6423SLionel Sambuc 		phys = pg_rounddown(phys);
282433d6423SLionel Sambuc 	}
283433d6423SLionel Sambuc 	assert(vaddr < kern_vir_start);
284433d6423SLionel Sambuc 
285433d6423SLionel Sambuc 	while(vaddr < vaddr_end) {
286433d6423SLionel Sambuc 		phys_bytes source = phys;
287433d6423SLionel Sambuc 		assert(!(vaddr % I386_PAGE_SIZE));
288433d6423SLionel Sambuc 		if(phys == PG_ALLOCATEME) {
289433d6423SLionel Sambuc 			source = pg_alloc_page(cbi);
290433d6423SLionel Sambuc 		} else {
291433d6423SLionel Sambuc 			assert(!(phys % I386_PAGE_SIZE));
292433d6423SLionel Sambuc 		}
293433d6423SLionel Sambuc 		assert(!(source % I386_PAGE_SIZE));
294433d6423SLionel Sambuc 		pde = I386_VM_PDE(vaddr);
295433d6423SLionel Sambuc 		pte = I386_VM_PTE(vaddr);
296433d6423SLionel Sambuc 		if(mapped_pde < pde) {
297433d6423SLionel Sambuc 			phys_bytes ph;
298433d6423SLionel Sambuc 			pt = alloc_pagetable(&ph);
299433d6423SLionel Sambuc 			pagedir[pde] = (ph & I386_VM_ADDR_MASK)
300433d6423SLionel Sambuc 		                | I386_VM_PRESENT | I386_VM_USER | I386_VM_WRITE;
301433d6423SLionel Sambuc 			mapped_pde = pde;
302433d6423SLionel Sambuc 		}
303433d6423SLionel Sambuc 		assert(pt);
304433d6423SLionel Sambuc 		pt[pte] = (source & I386_VM_ADDR_MASK) |
305433d6423SLionel Sambuc 			I386_VM_PRESENT | I386_VM_USER | I386_VM_WRITE;
306433d6423SLionel Sambuc 		vaddr += I386_PAGE_SIZE;
307433d6423SLionel Sambuc 		if(phys != PG_ALLOCATEME)
308433d6423SLionel Sambuc 			phys += I386_PAGE_SIZE;
309433d6423SLionel Sambuc 	}
310433d6423SLionel Sambuc }
311433d6423SLionel Sambuc 
pg_info(reg_t * pagedir_ph,u32_t ** pagedir_v)312433d6423SLionel Sambuc void pg_info(reg_t *pagedir_ph, u32_t **pagedir_v)
313433d6423SLionel Sambuc {
314433d6423SLionel Sambuc 	*pagedir_ph = vir2phys(pagedir);
315433d6423SLionel Sambuc 	*pagedir_v = pagedir;
316433d6423SLionel Sambuc }
317433d6423SLionel Sambuc 
318