1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/types.h> 30 #include <sys/saio.h> 31 #include <sys/sysmacros.h> 32 #include <sys/promif.h> 33 #include <sys/bootconf.h> 34 #include <sys/salib.h> 35 36 #define NIL 0 37 38 #ifdef DEBUG 39 static int resalloc_debug = 1; 40 #else /* DEBUG */ 41 static int resalloc_debug = 0; 42 #endif /* DEBUG */ 43 #define dprintf if (resalloc_debug) printf 44 45 extern struct memlist *vfreelistp, *pfreelistp; 46 extern void reset_alloc(void); 47 extern void alloc_segment(caddr_t); 48 49 caddr_t memlistpage; 50 caddr_t le_page; 51 caddr_t ie_page; 52 caddr_t scratchmemp; 53 extern int pagesize; 54 55 #define N_FREELIST 20 /* keep the largest 20 free regions */ 56 static size_t free_size[N_FREELIST]; 57 static caddr_t free_addr[N_FREELIST]; 58 59 /* 60 * OBP sets up a 1:1 mapping of virtual to physical in the range 8KB-10MB. The 61 * standalone is free to use any or all of this during its lifetime. 62 * Unfortunately, some platforms (Serengeti and LW8) can't use the full range. 63 * See 4799331 for more details. Limited platforms can use up to 64 * MAPPEDMEM_MINTOP; everyone else can use up to MAPPEDMEM_FULLTOP. 65 * resalloc_init makes the determination as to how much the machine being booted 66 * can use. 67 * 68 * But wait! There's more! resalloc handles three types of allocations: Two 69 * flavors of RES_BOOTSCRATCH (RES_BOOTSCRATCH and RES_BOOTSCRATCH_NOFAIL), and 70 * one of RES_CHILDVIRT. RES_CHILDVIRT is handled by prom_alloc, and is boring. 71 * We handle RES_BOOTSCRATCH allocations ourselves using the portion of the 1:1 72 * range not consumed by boot. The unconsumed range is subdivided into two 73 * portions - the general area from top_resvmem to top_bootmem and the reserved 74 * area from above memlistpage to top_resvmem. Both RES_BOOTSCRATCH flavors are 75 * satisfied by the general area until said area is exhausted, at which point 76 * RES_BOOTSCRATCH allocations return failure. RES_BOOTSCRATCH_NOFAIL 77 * allocations can't fail, so we'll try to satisfy them from the reserved area 78 * if the general area is full. If we still can't satisfy the nofail 79 * allocation, we'll call prom_panic. 80 * 81 * This whole boot memory allocation thing needs some serious rethinking. 82 * 83 * Memory layout: 84 * 85 * |-------| top_bootmem 86 * | | } MAPPEDMEM_FULLTOP (only on non-serengeti, lw8) 87 * | | } MAPPEDMEM_MINTOP 88 * |-------| top_resvmem/scratchmemp 89 * | | } MAPPEDMEM_RESERVE 90 * |-------| scratchresvp 91 * | | } one page 92 * |-------| memlistpage (at roundup(_end, pagesize)) 93 * |-------| _end 94 * | boot | 95 * : : 96 * 97 */ 98 99 #define MAPPEDMEM_RESERVE (512*1024) /* reserved for NOFAIL allocs */ 100 101 #define MAPPEDMEM_MINTOP (caddr_t)(6*1024*1024) 102 #define MAPPEDMEM_FULLTOP (caddr_t)(10*1024*1024) 103 104 static caddr_t top_bootmem = MAPPEDMEM_MINTOP; 105 static caddr_t top_resvmem, scratchresvp; 106 107 static int 108 impl_name(char *buf, size_t bufsz) 109 { 110 pnode_t n = prom_rootnode(); 111 size_t len = prom_getproplen(n, "name"); 112 113 if (len == 0 || len >= bufsz) 114 return (-1); 115 116 (void) prom_getprop(n, "name", buf); 117 buf[len] = '\0'; 118 119 return (0); 120 } 121 122 static caddr_t 123 vpage_from_freelist(size_t bytes) 124 { 125 caddr_t v; 126 int i; 127 128 /* find first region which fits */ 129 for (i = 0; i < N_FREELIST && free_size[i] < bytes; i++) 130 continue; 131 132 if (i == N_FREELIST) { 133 dprintf("boot: failed to allocate %lu bytes from scratch " 134 "memory\n", bytes); 135 return (NULL); 136 } 137 138 v = free_addr[i]; 139 free_addr[i] += bytes; 140 free_size[i] -= bytes; 141 dprintf("reuse freed temp scratch: bytes = %lu at %p\n", bytes, 142 (void *)v); 143 return (v); 144 } 145 146 /* 147 * This routine will find the next PAGESIZE chunk in the 148 * low MAPPEDMEM_MINTOP. It is analogous to valloc(). It is only for boot 149 * scratch memory, because child scratch memory goes up in 150 * the the high memory. We just need to verify that the 151 * pages are on the list. The calling routine will actually 152 * remove them. 153 */ 154 static caddr_t 155 get_low_vpage(size_t numpages, enum RESOURCES type) 156 { 157 size_t bytes; 158 caddr_t v; 159 160 if (!numpages) 161 return (0); 162 163 /* We know the page is mapped because the 1st MAPPEDMEM_MINTOP is 1:1 */ 164 bytes = numpages * pagesize; 165 if (scratchmemp + bytes <= top_bootmem) { 166 v = scratchmemp; 167 scratchmemp += bytes; 168 return (v); 169 } 170 171 /* 172 * If we run out of scratch memory, look in the freelist 173 */ 174 if ((v = vpage_from_freelist(bytes)) != NULL) 175 return (v); 176 177 /* 178 * Try really hard for allocations that can't fail. Look in the area 179 * that we've reserved for them. 180 */ 181 if (type == RES_BOOTSCRATCH_NOFAIL) { 182 if (scratchresvp + bytes <= top_resvmem) { 183 v = scratchresvp; 184 scratchresvp += bytes; 185 dprintf("using %lu bytes of reserved mem (%lu left)\n", 186 bytes, top_resvmem - scratchresvp); 187 return (v); 188 } else { 189 printf("boot: failed to allocate %lu bytes from " 190 "reserved scratch memory\n", bytes); 191 prom_panic("boot: scratch memory overflow.\n"); 192 } 193 } 194 195 return (NULL); 196 } 197 198 void 199 resalloc_init(void) 200 { 201 char iarch[128]; 202 203 if (impl_name(iarch, sizeof (iarch)) < 0) { 204 dprintf("boot: resalloc_init: failed to read iarch\n"); 205 return; 206 } 207 208 dprintf("boot: resalloc_init: got iarch %s\n", iarch); 209 210 /* 211 * Some versions of SG/LW8 firmware can actually handle the entire 10MB, 212 * but we don't have the ability to check for the firmware version here. 213 */ 214 if (strcmp(iarch, "SUNW,Sun-Fire") == 0 || 215 strcmp(iarch, "SUNW,Netra-T12") == 0) 216 return; 217 218 top_bootmem = MAPPEDMEM_FULLTOP; 219 220 dprintf("boot: resalloc_init: boosted top_bootmem to %p\n", 221 (void *)top_bootmem); 222 } 223 224 caddr_t 225 resalloc(enum RESOURCES type, size_t bytes, caddr_t virthint, int align) 226 { 227 caddr_t vaddr; 228 long pmap = 0; 229 230 if (memlistpage == (caddr_t)0) 231 reset_alloc(); 232 233 if (bytes == 0) 234 return ((caddr_t)0); 235 236 /* extend request to fill a page */ 237 bytes = roundup(bytes, pagesize); 238 239 dprintf("resalloc: bytes = %lu\n", bytes); 240 241 switch (type) { 242 243 /* 244 * even V2 PROMs never bother to indicate whether the 245 * first MAPPEDMEM_MINTOP is taken or not. So we do it all here. 246 * Smart PROM or no smart PROM. 247 */ 248 case RES_BOOTSCRATCH: 249 case RES_BOOTSCRATCH_NOFAIL: 250 vaddr = get_low_vpage((bytes/pagesize), type); 251 252 if (resalloc_debug) { 253 dprintf("vaddr = %p, paddr = %lx\n", (void *)vaddr, 254 ptob(pmap)); 255 print_memlist(vfreelistp); 256 print_memlist(pfreelistp); 257 } 258 return (vaddr); 259 /*NOTREACHED*/ 260 261 case RES_CHILDVIRT: 262 vaddr = (caddr_t)prom_alloc(virthint, bytes, align); 263 264 if (vaddr == (caddr_t)virthint) 265 return (vaddr); 266 printf("Alloc of 0x%lx bytes at 0x%p refused.\n", 267 bytes, (void *)virthint); 268 return ((caddr_t)0); 269 /*NOTREACHED*/ 270 271 default: 272 printf("Bad resurce type\n"); 273 return ((caddr_t)0); 274 } 275 } 276 277 #ifdef lint 278 static char _end[1]; /* defined by the linker! */ 279 #endif /* lint */ 280 281 void 282 reset_alloc(void) 283 { 284 extern char _end[]; 285 286 /* Cannot be called multiple times */ 287 if (memlistpage != (caddr_t)0) 288 return; 289 290 /* 291 * Due to kernel history and ease of programming, we 292 * want to keep everything private to /boot BELOW MAPPEDMEM_MINTOP. 293 * In this way, the kernel can just snarf it all when 294 * when it is ready, and not worry about snarfing lists. 295 */ 296 memlistpage = (caddr_t)roundup((uintptr_t)_end, pagesize); 297 298 /* 299 * This next is for scratch memory only 300 * We only need 1 page in memlistpage for now 301 */ 302 scratchresvp = (caddr_t)(memlistpage + pagesize); 303 scratchmemp = top_resvmem = scratchresvp + MAPPEDMEM_RESERVE; 304 le_page = (caddr_t)(scratchmemp + pagesize); 305 ie_page = (caddr_t)(le_page + pagesize); 306 307 bzero(memlistpage, pagesize); 308 bzero(scratchmemp, pagesize); 309 dprintf("memlistpage = %p\n", (void *)memlistpage); 310 dprintf("le_page = %p\n", (void *)le_page); 311 } 312 313 void 314 resfree(enum RESOURCES type, caddr_t virtaddr, size_t size) 315 { 316 int i; 317 318 /* make sure this is boot scratch memory */ 319 switch (type) { 320 case RES_BOOTSCRATCH: 321 if (virtaddr + size > top_bootmem) 322 return; 323 break; 324 default: 325 return; 326 } 327 328 /* 329 * Add this to the end of the free list 330 * NOTE: This relies on the fact that KRTLD calls BOP_FREE 331 * from largest to smallest chunks. 332 */ 333 for (i = 0; i < N_FREELIST && free_size[i]; i++) 334 ; 335 if (i == N_FREELIST) 336 return; 337 free_size[i] = size; 338 free_addr[i] = virtaddr; 339 } 340