xref: /minix3/minix/kernel/arch/i386/pg_utils.c (revision 433d6423c39e34ec4b79c950597bb2d236f886be)
1*433d6423SLionel Sambuc 
2*433d6423SLionel Sambuc #include <minix/cpufeature.h>
3*433d6423SLionel Sambuc 
4*433d6423SLionel Sambuc #include <assert.h>
5*433d6423SLionel Sambuc #include "kernel/kernel.h"
6*433d6423SLionel Sambuc #include "arch_proto.h"
7*433d6423SLionel Sambuc 
8*433d6423SLionel Sambuc #include <string.h>
9*433d6423SLionel Sambuc 
10*433d6423SLionel Sambuc /* These are set/computed in kernel.lds. */
11*433d6423SLionel Sambuc extern char _kern_vir_base, _kern_phys_base, _kern_size;
12*433d6423SLionel Sambuc 
13*433d6423SLionel Sambuc /* Retrieve the absolute values to something we can use. */
14*433d6423SLionel Sambuc static phys_bytes kern_vir_start = (phys_bytes) &_kern_vir_base;
15*433d6423SLionel Sambuc static phys_bytes kern_phys_start = (phys_bytes) &_kern_phys_base;
16*433d6423SLionel Sambuc static phys_bytes kern_kernlen = (phys_bytes) &_kern_size;
17*433d6423SLionel Sambuc 
18*433d6423SLionel Sambuc /* page directory we can use to map things */
19*433d6423SLionel Sambuc static u32_t pagedir[1024]  __aligned(4096);
20*433d6423SLionel Sambuc 
21*433d6423SLionel Sambuc void print_memmap(kinfo_t *cbi)
22*433d6423SLionel Sambuc {
23*433d6423SLionel Sambuc         int m;
24*433d6423SLionel Sambuc         assert(cbi->mmap_size < MAXMEMMAP);
25*433d6423SLionel Sambuc         for(m = 0; m < cbi->mmap_size; m++) {
26*433d6423SLionel Sambuc 		phys_bytes addr = cbi->memmap[m].mm_base_addr, endit = cbi->memmap[m].mm_base_addr + cbi->memmap[m].mm_length;
27*433d6423SLionel Sambuc                 printf("%08lx-%08lx ",addr, endit);
28*433d6423SLionel Sambuc         }
29*433d6423SLionel Sambuc         printf("\nsize %08lx\n", cbi->mmap_size);
30*433d6423SLionel Sambuc }
31*433d6423SLionel Sambuc 
32*433d6423SLionel Sambuc void cut_memmap(kinfo_t *cbi, phys_bytes start, phys_bytes end)
33*433d6423SLionel Sambuc {
34*433d6423SLionel Sambuc         int m;
35*433d6423SLionel Sambuc         phys_bytes o;
36*433d6423SLionel Sambuc 
37*433d6423SLionel Sambuc         if((o=start % I386_PAGE_SIZE))
38*433d6423SLionel Sambuc                 start -= o;
39*433d6423SLionel Sambuc         if((o=end % I386_PAGE_SIZE))
40*433d6423SLionel Sambuc                 end += I386_PAGE_SIZE - o;
41*433d6423SLionel Sambuc 
42*433d6423SLionel Sambuc 	assert(kernel_may_alloc);
43*433d6423SLionel Sambuc 
44*433d6423SLionel Sambuc         for(m = 0; m < cbi->mmap_size; m++) {
45*433d6423SLionel Sambuc                 phys_bytes substart = start, subend = end;
46*433d6423SLionel Sambuc                 phys_bytes memaddr = cbi->memmap[m].mm_base_addr,
47*433d6423SLionel Sambuc                         memend = cbi->memmap[m].mm_base_addr + cbi->memmap[m].mm_length;
48*433d6423SLionel Sambuc 
49*433d6423SLionel Sambuc                 /* adjust cut range to be a subset of the free memory */
50*433d6423SLionel Sambuc                 if(substart < memaddr) substart = memaddr;
51*433d6423SLionel Sambuc                 if(subend > memend) subend = memend;
52*433d6423SLionel Sambuc                 if(substart >= subend) continue;
53*433d6423SLionel Sambuc 
54*433d6423SLionel Sambuc                 /* if there is any overlap, forget this one and add
55*433d6423SLionel Sambuc                  * 1-2 subranges back
56*433d6423SLionel Sambuc                  */
57*433d6423SLionel Sambuc                 cbi->memmap[m].mm_base_addr = cbi->memmap[m].mm_length = 0;
58*433d6423SLionel Sambuc                 if(substart > memaddr)
59*433d6423SLionel Sambuc                         add_memmap(cbi, memaddr, substart-memaddr);
60*433d6423SLionel Sambuc                 if(subend < memend)
61*433d6423SLionel Sambuc                         add_memmap(cbi, subend, memend-subend);
62*433d6423SLionel Sambuc         }
63*433d6423SLionel Sambuc }
64*433d6423SLionel Sambuc 
65*433d6423SLionel Sambuc phys_bytes alloc_lowest(kinfo_t *cbi, phys_bytes len)
66*433d6423SLionel Sambuc {
67*433d6423SLionel Sambuc 	/* Allocate the lowest physical page we have. */
68*433d6423SLionel Sambuc 	int m;
69*433d6423SLionel Sambuc #define EMPTY 0xffffffff
70*433d6423SLionel Sambuc 	phys_bytes lowest = EMPTY;
71*433d6423SLionel Sambuc 	assert(len > 0);
72*433d6423SLionel Sambuc 	len = roundup(len, I386_PAGE_SIZE);
73*433d6423SLionel Sambuc 
74*433d6423SLionel Sambuc 	assert(kernel_may_alloc);
75*433d6423SLionel Sambuc 
76*433d6423SLionel Sambuc 	for(m = 0; m < cbi->mmap_size; m++) {
77*433d6423SLionel Sambuc 		if(cbi->memmap[m].mm_length < len) continue;
78*433d6423SLionel Sambuc 		if(cbi->memmap[m].mm_base_addr < lowest) lowest = cbi->memmap[m].mm_base_addr;
79*433d6423SLionel Sambuc 	}
80*433d6423SLionel Sambuc 	assert(lowest != EMPTY);
81*433d6423SLionel Sambuc 	cut_memmap(cbi, lowest, len);
82*433d6423SLionel Sambuc 	cbi->kernel_allocated_bytes_dynamic += len;
83*433d6423SLionel Sambuc 	return lowest;
84*433d6423SLionel Sambuc }
85*433d6423SLionel Sambuc 
86*433d6423SLionel Sambuc void add_memmap(kinfo_t *cbi, u64_t addr, u64_t len)
87*433d6423SLionel Sambuc {
88*433d6423SLionel Sambuc         int m;
89*433d6423SLionel Sambuc #define LIMIT 0xFFFFF000
90*433d6423SLionel Sambuc         /* Truncate available memory at 4GB as the rest of minix
91*433d6423SLionel Sambuc          * currently can't deal with any bigger.
92*433d6423SLionel Sambuc          */
93*433d6423SLionel Sambuc         if(addr > LIMIT) return;
94*433d6423SLionel Sambuc         if(addr + len > LIMIT) {
95*433d6423SLionel Sambuc                 len -= (addr + len - LIMIT);
96*433d6423SLionel Sambuc         }
97*433d6423SLionel Sambuc         assert(cbi->mmap_size < MAXMEMMAP);
98*433d6423SLionel Sambuc         if(len == 0) return;
99*433d6423SLionel Sambuc 	addr = roundup(addr, I386_PAGE_SIZE);
100*433d6423SLionel Sambuc 	len = rounddown(len, I386_PAGE_SIZE);
101*433d6423SLionel Sambuc 
102*433d6423SLionel Sambuc 	assert(kernel_may_alloc);
103*433d6423SLionel Sambuc 
104*433d6423SLionel Sambuc         for(m = 0; m < MAXMEMMAP; m++) {
105*433d6423SLionel Sambuc 		phys_bytes highmark;
106*433d6423SLionel Sambuc                 if(cbi->memmap[m].mm_length) continue;
107*433d6423SLionel Sambuc                 cbi->memmap[m].mm_base_addr = addr;
108*433d6423SLionel Sambuc                 cbi->memmap[m].mm_length = len;
109*433d6423SLionel Sambuc                 cbi->memmap[m].mm_type = MULTIBOOT_MEMORY_AVAILABLE;
110*433d6423SLionel Sambuc                 if(m >= cbi->mmap_size)
111*433d6423SLionel Sambuc                         cbi->mmap_size = m+1;
112*433d6423SLionel Sambuc 		highmark = addr + len;
113*433d6423SLionel Sambuc 		if(highmark > cbi->mem_high_phys) {
114*433d6423SLionel Sambuc 			cbi->mem_high_phys = highmark;
115*433d6423SLionel Sambuc 		}
116*433d6423SLionel Sambuc 
117*433d6423SLionel Sambuc                 return;
118*433d6423SLionel Sambuc         }
119*433d6423SLionel Sambuc 
120*433d6423SLionel Sambuc         panic("no available memmap slot");
121*433d6423SLionel Sambuc }
122*433d6423SLionel Sambuc 
123*433d6423SLionel Sambuc u32_t *alloc_pagetable(phys_bytes *ph)
124*433d6423SLionel Sambuc {
125*433d6423SLionel Sambuc 	u32_t *ret;
126*433d6423SLionel Sambuc #define PG_PAGETABLES 6
127*433d6423SLionel Sambuc 	static u32_t pagetables[PG_PAGETABLES][1024]  __aligned(4096);
128*433d6423SLionel Sambuc 	static int pt_inuse = 0;
129*433d6423SLionel Sambuc 	if(pt_inuse >= PG_PAGETABLES) panic("no more pagetables");
130*433d6423SLionel Sambuc 	assert(sizeof(pagetables[pt_inuse]) == I386_PAGE_SIZE);
131*433d6423SLionel Sambuc 	ret = pagetables[pt_inuse++];
132*433d6423SLionel Sambuc 	*ph = vir2phys(ret);
133*433d6423SLionel Sambuc 	return ret;
134*433d6423SLionel Sambuc }
135*433d6423SLionel Sambuc 
136*433d6423SLionel Sambuc #define PAGE_KB (I386_PAGE_SIZE / 1024)
137*433d6423SLionel Sambuc 
138*433d6423SLionel Sambuc phys_bytes pg_alloc_page(kinfo_t *cbi)
139*433d6423SLionel Sambuc {
140*433d6423SLionel Sambuc 	int m;
141*433d6423SLionel Sambuc 	multiboot_memory_map_t *mmap;
142*433d6423SLionel Sambuc 
143*433d6423SLionel Sambuc 	assert(kernel_may_alloc);
144*433d6423SLionel Sambuc 
145*433d6423SLionel Sambuc 	for(m = cbi->mmap_size-1; m >= 0; m--) {
146*433d6423SLionel Sambuc 		mmap = &cbi->memmap[m];
147*433d6423SLionel Sambuc 		if(!mmap->mm_length) continue;
148*433d6423SLionel Sambuc 		assert(mmap->mm_length > 0);
149*433d6423SLionel Sambuc 		assert(!(mmap->mm_length % I386_PAGE_SIZE));
150*433d6423SLionel Sambuc 		assert(!(mmap->mm_base_addr % I386_PAGE_SIZE));
151*433d6423SLionel Sambuc 
152*433d6423SLionel Sambuc 		mmap->mm_length -= I386_PAGE_SIZE;
153*433d6423SLionel Sambuc 
154*433d6423SLionel Sambuc                 cbi->kernel_allocated_bytes_dynamic += I386_PAGE_SIZE;
155*433d6423SLionel Sambuc 
156*433d6423SLionel Sambuc 		return mmap->mm_base_addr + mmap->mm_length;
157*433d6423SLionel Sambuc 	}
158*433d6423SLionel Sambuc 
159*433d6423SLionel Sambuc 	panic("can't find free memory");
160*433d6423SLionel Sambuc }
161*433d6423SLionel Sambuc 
162*433d6423SLionel Sambuc void pg_identity(kinfo_t *cbi)
163*433d6423SLionel Sambuc {
164*433d6423SLionel Sambuc 	int i;
165*433d6423SLionel Sambuc 	phys_bytes phys;
166*433d6423SLionel Sambuc 
167*433d6423SLionel Sambuc 	/* We map memory that does not correspond to physical memory
168*433d6423SLionel Sambuc 	 * as non-cacheable. Make sure we know what it is.
169*433d6423SLionel Sambuc 	 */
170*433d6423SLionel Sambuc 	assert(cbi->mem_high_phys);
171*433d6423SLionel Sambuc 
172*433d6423SLionel Sambuc         /* Set up an identity mapping page directory */
173*433d6423SLionel Sambuc         for(i = 0; i < I386_VM_DIR_ENTRIES; i++) {
174*433d6423SLionel Sambuc 		u32_t flags = I386_VM_PRESENT | I386_VM_BIGPAGE
175*433d6423SLionel Sambuc 			| I386_VM_USER
176*433d6423SLionel Sambuc 			| I386_VM_WRITE;
177*433d6423SLionel Sambuc                 phys = i * I386_BIG_PAGE_SIZE;
178*433d6423SLionel Sambuc 		if((cbi->mem_high_phys & I386_VM_ADDR_MASK_4MB)
179*433d6423SLionel Sambuc 			<= (phys & I386_VM_ADDR_MASK_4MB)) {
180*433d6423SLionel Sambuc 			flags |= I386_VM_PWT | I386_VM_PCD;
181*433d6423SLionel Sambuc 		}
182*433d6423SLionel Sambuc                 pagedir[i] =  phys | flags;
183*433d6423SLionel Sambuc         }
184*433d6423SLionel Sambuc }
185*433d6423SLionel Sambuc 
186*433d6423SLionel Sambuc int pg_mapkernel(void)
187*433d6423SLionel Sambuc {
188*433d6423SLionel Sambuc 	int pde;
189*433d6423SLionel Sambuc 	u32_t mapped = 0, kern_phys = kern_phys_start;
190*433d6423SLionel Sambuc 
191*433d6423SLionel Sambuc         assert(!(kern_vir_start % I386_BIG_PAGE_SIZE));
192*433d6423SLionel Sambuc         assert(!(kern_phys % I386_BIG_PAGE_SIZE));
193*433d6423SLionel Sambuc         pde = kern_vir_start / I386_BIG_PAGE_SIZE; /* start pde */
194*433d6423SLionel Sambuc 	while(mapped < kern_kernlen) {
195*433d6423SLionel Sambuc 	        pagedir[pde] = kern_phys | I386_VM_PRESENT |
196*433d6423SLionel Sambuc 			I386_VM_BIGPAGE | I386_VM_WRITE;
197*433d6423SLionel Sambuc 		mapped += I386_BIG_PAGE_SIZE;
198*433d6423SLionel Sambuc 		kern_phys += I386_BIG_PAGE_SIZE;
199*433d6423SLionel Sambuc 		pde++;
200*433d6423SLionel Sambuc 	}
201*433d6423SLionel Sambuc 	return pde;	/* free pde */
202*433d6423SLionel Sambuc }
203*433d6423SLionel Sambuc 
204*433d6423SLionel Sambuc void vm_enable_paging(void)
205*433d6423SLionel Sambuc {
206*433d6423SLionel Sambuc         u32_t cr0, cr4;
207*433d6423SLionel Sambuc         int pgeok;
208*433d6423SLionel Sambuc 
209*433d6423SLionel Sambuc         pgeok = _cpufeature(_CPUF_I386_PGE);
210*433d6423SLionel Sambuc 
211*433d6423SLionel Sambuc         cr0= read_cr0();
212*433d6423SLionel Sambuc         cr4= read_cr4();
213*433d6423SLionel Sambuc 
214*433d6423SLionel Sambuc 	/* The boot loader should have put us in protected mode. */
215*433d6423SLionel Sambuc 	assert(cr0 & I386_CR0_PE);
216*433d6423SLionel Sambuc 
217*433d6423SLionel Sambuc         /* First clear PG and PGE flag, as PGE must be enabled after PG. */
218*433d6423SLionel Sambuc         write_cr0(cr0 & ~I386_CR0_PG);
219*433d6423SLionel Sambuc         write_cr4(cr4 & ~(I386_CR4_PGE | I386_CR4_PSE));
220*433d6423SLionel Sambuc 
221*433d6423SLionel Sambuc         cr0= read_cr0();
222*433d6423SLionel Sambuc         cr4= read_cr4();
223*433d6423SLionel Sambuc 
224*433d6423SLionel Sambuc         /* Our page table contains 4MB entries. */
225*433d6423SLionel Sambuc         cr4 |= I386_CR4_PSE;
226*433d6423SLionel Sambuc 
227*433d6423SLionel Sambuc         write_cr4(cr4);
228*433d6423SLionel Sambuc 
229*433d6423SLionel Sambuc         /* First enable paging, then enable global page flag. */
230*433d6423SLionel Sambuc         cr0 |= I386_CR0_PG;
231*433d6423SLionel Sambuc         write_cr0(cr0);
232*433d6423SLionel Sambuc         cr0 |= I386_CR0_WP;
233*433d6423SLionel Sambuc         write_cr0(cr0);
234*433d6423SLionel Sambuc 
235*433d6423SLionel Sambuc         /* May we enable these features? */
236*433d6423SLionel Sambuc         if(pgeok)
237*433d6423SLionel Sambuc                 cr4 |= I386_CR4_PGE;
238*433d6423SLionel Sambuc 
239*433d6423SLionel Sambuc         write_cr4(cr4);
240*433d6423SLionel Sambuc }
241*433d6423SLionel Sambuc 
242*433d6423SLionel Sambuc phys_bytes pg_load()
243*433d6423SLionel Sambuc {
244*433d6423SLionel Sambuc 	phys_bytes phpagedir = vir2phys(pagedir);
245*433d6423SLionel Sambuc         write_cr3(phpagedir);
246*433d6423SLionel Sambuc 	return phpagedir;
247*433d6423SLionel Sambuc }
248*433d6423SLionel Sambuc 
249*433d6423SLionel Sambuc void pg_clear(void)
250*433d6423SLionel Sambuc {
251*433d6423SLionel Sambuc 	memset(pagedir, 0, sizeof(pagedir));
252*433d6423SLionel Sambuc }
253*433d6423SLionel Sambuc 
254*433d6423SLionel Sambuc phys_bytes pg_rounddown(phys_bytes b)
255*433d6423SLionel Sambuc {
256*433d6423SLionel Sambuc 	phys_bytes o;
257*433d6423SLionel Sambuc 	if(!(o = b % I386_PAGE_SIZE))
258*433d6423SLionel Sambuc 		return b;
259*433d6423SLionel Sambuc 	return b  - o;
260*433d6423SLionel Sambuc }
261*433d6423SLionel Sambuc 
262*433d6423SLionel Sambuc void pg_map(phys_bytes phys, vir_bytes vaddr, vir_bytes vaddr_end,
263*433d6423SLionel Sambuc 	kinfo_t *cbi)
264*433d6423SLionel Sambuc {
265*433d6423SLionel Sambuc 	static int mapped_pde = -1;
266*433d6423SLionel Sambuc 	static u32_t *pt = NULL;
267*433d6423SLionel Sambuc 	int pde, pte;
268*433d6423SLionel Sambuc 
269*433d6423SLionel Sambuc 	assert(kernel_may_alloc);
270*433d6423SLionel Sambuc 
271*433d6423SLionel Sambuc 	if(phys == PG_ALLOCATEME) {
272*433d6423SLionel Sambuc 		assert(!(vaddr % I386_PAGE_SIZE));
273*433d6423SLionel Sambuc 	} else  {
274*433d6423SLionel Sambuc 		assert((vaddr % I386_PAGE_SIZE) == (phys % I386_PAGE_SIZE));
275*433d6423SLionel Sambuc 		vaddr = pg_rounddown(vaddr);
276*433d6423SLionel Sambuc 		phys = pg_rounddown(phys);
277*433d6423SLionel Sambuc 	}
278*433d6423SLionel Sambuc 	assert(vaddr < kern_vir_start);
279*433d6423SLionel Sambuc 
280*433d6423SLionel Sambuc 	while(vaddr < vaddr_end) {
281*433d6423SLionel Sambuc 		phys_bytes source = phys;
282*433d6423SLionel Sambuc 		assert(!(vaddr % I386_PAGE_SIZE));
283*433d6423SLionel Sambuc 		if(phys == PG_ALLOCATEME) {
284*433d6423SLionel Sambuc 			source = pg_alloc_page(cbi);
285*433d6423SLionel Sambuc 		} else {
286*433d6423SLionel Sambuc 			assert(!(phys % I386_PAGE_SIZE));
287*433d6423SLionel Sambuc 		}
288*433d6423SLionel Sambuc 		assert(!(source % I386_PAGE_SIZE));
289*433d6423SLionel Sambuc 		pde = I386_VM_PDE(vaddr);
290*433d6423SLionel Sambuc 		pte = I386_VM_PTE(vaddr);
291*433d6423SLionel Sambuc 		if(mapped_pde < pde) {
292*433d6423SLionel Sambuc 			phys_bytes ph;
293*433d6423SLionel Sambuc 			pt = alloc_pagetable(&ph);
294*433d6423SLionel Sambuc 			pagedir[pde] = (ph & I386_VM_ADDR_MASK)
295*433d6423SLionel Sambuc 		                | I386_VM_PRESENT | I386_VM_USER | I386_VM_WRITE;
296*433d6423SLionel Sambuc 			mapped_pde = pde;
297*433d6423SLionel Sambuc 		}
298*433d6423SLionel Sambuc 		assert(pt);
299*433d6423SLionel Sambuc 		pt[pte] = (source & I386_VM_ADDR_MASK) |
300*433d6423SLionel Sambuc 			I386_VM_PRESENT | I386_VM_USER | I386_VM_WRITE;
301*433d6423SLionel Sambuc 		vaddr += I386_PAGE_SIZE;
302*433d6423SLionel Sambuc 		if(phys != PG_ALLOCATEME)
303*433d6423SLionel Sambuc 			phys += I386_PAGE_SIZE;
304*433d6423SLionel Sambuc 	}
305*433d6423SLionel Sambuc }
306*433d6423SLionel Sambuc 
307*433d6423SLionel Sambuc void pg_info(reg_t *pagedir_ph, u32_t **pagedir_v)
308*433d6423SLionel Sambuc {
309*433d6423SLionel Sambuc 	*pagedir_ph = vir2phys(pagedir);
310*433d6423SLionel Sambuc 	*pagedir_v = pagedir;
311*433d6423SLionel Sambuc }
312*433d6423SLionel Sambuc 
313