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