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 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * XXX This stuff should be in usr/src/common, to be shared by boot 28 * code, kernel DR, and busra stuff. 29 * 30 * NOTE: We are only using the next-> link. The prev-> link is 31 * not used in the implementation. 32 */ 33 #include <sys/types.h> 34 #include <sys/memlist.h> 35 #include <sys/kmem.h> 36 #include <sys/cmn_err.h> 37 #include <sys/pci_impl.h> 38 #include <sys/debug.h> 39 40 extern int pci_boot_debug; 41 #define dprintf if (pci_boot_debug) printf 42 43 void 44 memlist_dump(struct memlist *listp) 45 { 46 dprintf("memlist 0x%p content", (void *)listp); 47 while (listp) { 48 dprintf("(0x%x%x, 0x%x%x)", 49 (int)(listp->ml_address >> 32), (int)listp->ml_address, 50 (int)(listp->ml_size >> 32), (int)listp->ml_size); 51 listp = listp->ml_next; 52 } 53 } 54 55 struct memlist * 56 memlist_alloc() 57 { 58 return ((struct memlist *)kmem_zalloc(sizeof (struct memlist), 59 KM_SLEEP)); 60 } 61 62 void 63 memlist_free(struct memlist *buf) 64 { 65 kmem_free(buf, sizeof (struct memlist)); 66 } 67 68 void 69 memlist_free_all(struct memlist **list) 70 { 71 struct memlist *next, *buf; 72 73 next = *list; 74 while (next) { 75 buf = next; 76 next = buf->ml_next; 77 kmem_free(buf, sizeof (struct memlist)); 78 } 79 *list = 0; 80 } 81 82 /* insert in the order of addresses */ 83 void 84 memlist_insert(struct memlist **listp, uint64_t addr, uint64_t size) 85 { 86 int merge_left, merge_right; 87 struct memlist *entry; 88 struct memlist *prev = 0, *next; 89 90 /* find the location in list */ 91 next = *listp; 92 while (next && next->ml_address <= addr) { 93 /* 94 * Drop if this entry already exists, in whole 95 * or in part 96 */ 97 if (next->ml_address <= addr && 98 next->ml_address + next->ml_size >= addr + size) { 99 /* next already contains this entire element; drop */ 100 return; 101 } 102 103 /* Is this a "grow block size" request? */ 104 if (next->ml_address == addr) { 105 break; 106 } 107 prev = next; 108 next = prev->ml_next; 109 } 110 111 merge_left = (prev && addr == prev->ml_address + prev->ml_size); 112 merge_right = (next && addr + size == next->ml_address); 113 if (merge_left && merge_right) { 114 prev->ml_size += size + next->ml_size; 115 prev->ml_next = next->ml_next; 116 memlist_free(next); 117 return; 118 } 119 120 if (merge_left) { 121 prev->ml_size += size; 122 return; 123 } 124 125 if (merge_right) { 126 next->ml_address = addr; 127 next->ml_size += size; 128 return; 129 } 130 131 entry = memlist_alloc(); 132 entry->ml_address = addr; 133 entry->ml_size = size; 134 if (prev == 0) { 135 entry->ml_next = *listp; 136 *listp = entry; 137 } else { 138 entry->ml_next = next; 139 prev->ml_next = entry; 140 } 141 } 142 143 /* 144 * Delete memlist entries, assuming list sorted by address 145 */ 146 147 #define MIN(a, b) ((a) < (b) ? (a) : (b)) 148 #define MAX(a, b) ((a) > (b) ? (a) : (b)) 149 #define IN_RANGE(a, b, e) ((a) >= (b) && (a) <= (e)) 150 151 int 152 memlist_remove(struct memlist **listp, uint64_t addr, uint64_t size) 153 { 154 struct memlist *prev = 0; 155 struct memlist *chunk; 156 uint64_t rem_begin, rem_end; 157 uint64_t chunk_begin, chunk_end; 158 int begin_in_chunk, end_in_chunk; 159 160 161 /* ignore removal of zero-length item */ 162 if (size == 0) 163 return (0); 164 165 /* also inherently ignore a zero-length list */ 166 rem_begin = addr; 167 rem_end = addr + size - 1; 168 chunk = *listp; 169 while (chunk) { 170 chunk_begin = chunk->ml_address; 171 chunk_end = chunk->ml_address + chunk->ml_size - 1; 172 begin_in_chunk = IN_RANGE(rem_begin, chunk_begin, chunk_end); 173 end_in_chunk = IN_RANGE(rem_end, chunk_begin, chunk_end); 174 175 if (rem_begin <= chunk_begin && rem_end >= chunk_end) { 176 struct memlist *delete_chunk; 177 178 /* spans entire chunk - delete chunk */ 179 delete_chunk = chunk; 180 if (prev == 0) 181 chunk = *listp = chunk->ml_next; 182 else 183 chunk = prev->ml_next = chunk->ml_next; 184 185 memlist_free(delete_chunk); 186 /* skip to start of while-loop */ 187 continue; 188 } else if (begin_in_chunk && end_in_chunk && 189 chunk_begin != rem_begin && chunk_end != rem_end) { 190 struct memlist *new; 191 /* split chunk */ 192 new = memlist_alloc(); 193 new->ml_address = rem_end + 1; 194 new->ml_size = chunk_end - new->ml_address + 1; 195 chunk->ml_size = rem_begin - chunk_begin; 196 new->ml_next = chunk->ml_next; 197 chunk->ml_next = new; 198 /* done - break out of while-loop */ 199 break; 200 } else if (begin_in_chunk || end_in_chunk) { 201 /* trim chunk */ 202 chunk->ml_size -= MIN(chunk_end, rem_end) - 203 MAX(chunk_begin, rem_begin) + 1; 204 if (rem_begin <= chunk_begin) { 205 chunk->ml_address = rem_end + 1; 206 break; 207 } 208 /* fall-through to next chunk */ 209 } 210 prev = chunk; 211 chunk = chunk->ml_next; 212 } 213 214 return (0); 215 } 216 217 /* 218 * find and claim a memory chunk of given size, first fit 219 */ 220 uint64_t 221 memlist_find(struct memlist **listp, uint64_t size, int align) 222 { 223 uint64_t delta, total_size; 224 uint64_t paddr; 225 struct memlist *prev = 0, *next; 226 227 /* find the chunk with sufficient size */ 228 next = *listp; 229 while (next) { 230 delta = next->ml_address & ((align != 0) ? (align - 1) : 0); 231 if (delta != 0) 232 total_size = size + align - delta; 233 else 234 total_size = size; /* the addr is already aligned */ 235 if (next->ml_size >= total_size) 236 break; 237 prev = next; 238 next = prev->ml_next; 239 } 240 241 if (next == 0) 242 return (0); /* Not found */ 243 244 paddr = next->ml_address; 245 if (delta) 246 paddr += align - delta; 247 (void) memlist_remove(listp, paddr, size); 248 249 return (paddr); 250 } 251 252 /* 253 * find and claim a memory chunk of given size, starting 254 * at a specified address 255 */ 256 uint64_t 257 memlist_find_with_startaddr(struct memlist **listp, uint64_t address, 258 uint64_t size, int align) 259 { 260 uint64_t delta, total_size; 261 uint64_t paddr; 262 struct memlist *next; 263 264 /* find the chunk starting at 'address' */ 265 next = *listp; 266 while (next && (next->ml_address != address)) { 267 next = next->ml_next; 268 } 269 if (next == 0) 270 return (0); /* Not found */ 271 272 delta = next->ml_address & ((align != 0) ? (align - 1) : 0); 273 if (delta != 0) 274 total_size = size + align - delta; 275 else 276 total_size = size; /* the addr is already aligned */ 277 if (next->ml_size < total_size) 278 return (0); /* unsufficient size */ 279 280 paddr = next->ml_address; 281 if (delta) 282 paddr += align - delta; 283 (void) memlist_remove(listp, paddr, size); 284 285 return (paddr); 286 } 287 288 /* 289 * Subsume memlist src into memlist dest 290 */ 291 void 292 memlist_subsume(struct memlist **src, struct memlist **dest) 293 { 294 struct memlist *head, *prev; 295 296 head = *src; 297 while (head) { 298 memlist_insert(dest, head->ml_address, head->ml_size); 299 prev = head; 300 head = head->ml_next; 301 memlist_free(prev); 302 } 303 *src = 0; 304 } 305 306 /* 307 * Merge memlist src into memlist dest; don't destroy src 308 */ 309 void 310 memlist_merge(struct memlist **src, struct memlist **dest) 311 { 312 struct memlist *p; 313 314 p = *src; 315 while (p) { 316 memlist_insert(dest, p->ml_address, p->ml_size); 317 p = p->ml_next; 318 } 319 } 320 321 /* 322 * Make a copy of memlist 323 */ 324 struct memlist * 325 memlist_dup(struct memlist *listp) 326 { 327 struct memlist *head = 0, *prev = 0; 328 329 while (listp) { 330 struct memlist *entry = memlist_alloc(); 331 entry->ml_address = listp->ml_address; 332 entry->ml_size = listp->ml_size; 333 entry->ml_next = 0; 334 if (prev) 335 prev->ml_next = entry; 336 else 337 head = entry; 338 prev = entry; 339 listp = listp->ml_next; 340 } 341 342 return (head); 343 } 344 345 int 346 memlist_count(struct memlist *listp) 347 { 348 int count = 0; 349 while (listp) { 350 count++; 351 listp = listp->ml_next; 352 } 353 354 return (count); 355 } 356