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