1 /* This file is part of the program psim. 2 3 Copyright (C) 1994-1997, Andrew Cagney <cagney@highland.com.au> 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 3 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, see <http://www.gnu.org/licenses/>. 17 18 */ 19 20 21 #ifndef _HW_MEMORY_C_ 22 #define _HW_MEMORY_C_ 23 24 #ifndef STATIC_INLINE_HW_MEMORY 25 #define STATIC_INLINE_HW_MEMORY STATIC_INLINE 26 #endif 27 28 #include <stdlib.h> 29 30 #include "device_table.h" 31 32 /* DEVICE 33 34 35 memory - description of system memory 36 37 38 DESCRIPTION 39 40 41 This device describes the size and location of the banks of 42 physical memory within the simulation. 43 44 In addition, this device supports the "claim" and "release" methods 45 that can be used by OpenBoot client programs to manage the 46 allocation of physical memory. 47 48 49 PROPERTIES 50 51 52 reg = { <address> <size> } (required) 53 54 Each pair specify one bank of memory. 55 56 available = { <address> <size> } (automatic) 57 58 Each pair specifies a block of memory that is currently unallocated. 59 60 61 BUGS 62 63 64 OpenFirmware doesn't make it clear if, when releasing memory the 65 same address + size pair as was used during the claim should be 66 specified. 67 68 It is assumed that #size-cells and #address-cells for the parent 69 node of this device are both one i.e. an address or size can be 70 specified using a single memory cell (word). 71 72 Significant work will be required before the <<memory>> device can 73 support 64bit addresses (#address-cells equal two). 74 75 */ 76 77 typedef struct _memory_reg_spec { 78 unsigned_cell base; 79 unsigned_cell size; 80 } memory_reg_spec; 81 82 typedef struct _hw_memory_chunk hw_memory_chunk; 83 struct _hw_memory_chunk { 84 unsigned_word address; 85 unsigned_word size; 86 int available; 87 hw_memory_chunk *next; 88 }; 89 90 typedef struct _hw_memory_device { 91 hw_memory_chunk *heap; 92 } hw_memory_device; 93 94 95 static void * 96 hw_memory_create(const char *name, 97 const device_unit *unit_address, 98 const char *args) 99 { 100 hw_memory_device *hw_memory = ZALLOC(hw_memory_device); 101 return hw_memory; 102 } 103 104 105 static void 106 hw_memory_set_available(device *me, 107 hw_memory_device *hw_memory) 108 { 109 hw_memory_chunk *chunk = NULL; 110 memory_reg_spec *available = NULL; 111 int nr_available = 0; 112 int curr = 0; 113 int sizeof_available = 0; 114 /* determine the nr of available chunks */ 115 chunk = hw_memory->heap; 116 nr_available = 0; 117 while (chunk != NULL) { 118 if (chunk->available) 119 nr_available += 1; 120 ASSERT(chunk->next == NULL 121 || chunk->address < chunk->next->address); 122 ASSERT(chunk->next == NULL 123 || chunk->address + chunk->size == chunk->next->address); 124 chunk = chunk->next; 125 } 126 /* now create the available struct */ 127 ASSERT(nr_available > 0); 128 sizeof_available = sizeof(memory_reg_spec) * nr_available; 129 available = zalloc(sizeof_available); 130 chunk = hw_memory->heap; 131 curr = 0; 132 while (chunk != NULL) { 133 if (chunk->available) { 134 available[curr].base = H2BE_cell(chunk->address); 135 available[curr].size = H2BE_cell(chunk->size); 136 curr += 1; 137 } 138 chunk = chunk->next; 139 } 140 /* update */ 141 device_set_array_property(me, "available", available, sizeof_available); 142 free(available); 143 } 144 145 146 static void 147 hw_memory_init_address(device *me) 148 { 149 hw_memory_device *hw_memory = (hw_memory_device*)device_data(me); 150 151 /* free up any previous structures */ 152 { 153 hw_memory_chunk *curr_chunk = hw_memory->heap; 154 hw_memory->heap = NULL; 155 while (curr_chunk != NULL) { 156 hw_memory_chunk *dead_chunk = curr_chunk; 157 curr_chunk = dead_chunk->next; 158 dead_chunk->next = NULL; 159 free(dead_chunk); 160 } 161 } 162 163 /* attach memory regions according to the "reg" property */ 164 { 165 int reg_nr; 166 reg_property_spec reg; 167 for (reg_nr = 0; 168 device_find_reg_array_property(me, "reg", reg_nr, ®); 169 reg_nr++) { 170 int i; 171 /* check that the entry meets restrictions */ 172 for (i = 0; i < reg.address.nr_cells - 1; i++) 173 if (reg.address.cells[i] != 0) 174 device_error(me, "Only single celled addresses supported"); 175 for (i = 0; i < reg.size.nr_cells - 1; i++) 176 if (reg.size.cells[i] != 0) 177 device_error(me, "Only single celled sizes supported"); 178 /* attach the range */ 179 device_attach_address(device_parent(me), 180 attach_raw_memory, 181 0 /*address space*/, 182 reg.address.cells[reg.address.nr_cells - 1], 183 reg.size.cells[reg.size.nr_cells - 1], 184 access_read_write_exec, 185 me); 186 } 187 } 188 189 /* create the initial `available memory' data structure */ 190 if (device_find_property(me, "available") != NULL) { 191 hw_memory_chunk **curr_chunk = &hw_memory->heap; 192 int cell_nr; 193 unsigned_cell dummy; 194 int nr_cells = device_find_integer_array_property(me, "available", 0, &dummy); 195 if ((nr_cells % 2) != 0) 196 device_error(me, "property \"available\" invalid - contains an odd number of cells"); 197 for (cell_nr = 0; 198 cell_nr < nr_cells; 199 cell_nr += 2) { 200 hw_memory_chunk *new_chunk = ZALLOC(hw_memory_chunk); 201 device_find_integer_array_property(me, "available", cell_nr, 202 &new_chunk->address); 203 device_find_integer_array_property(me, "available", cell_nr + 1, 204 &new_chunk->size); 205 new_chunk->available = 1; 206 *curr_chunk = new_chunk; 207 curr_chunk = &new_chunk->next; 208 } 209 } 210 else { 211 hw_memory_chunk **curr_chunk = &hw_memory->heap; 212 int reg_nr; 213 reg_property_spec reg; 214 for (reg_nr = 0; 215 device_find_reg_array_property(me, "reg", reg_nr, ®); 216 reg_nr++) { 217 hw_memory_chunk *new_chunk; 218 new_chunk = ZALLOC(hw_memory_chunk); 219 new_chunk->address = reg.address.cells[reg.address.nr_cells - 1]; 220 new_chunk->size = reg.size.cells[reg.size.nr_cells - 1]; 221 new_chunk->available = 1; 222 *curr_chunk = new_chunk; 223 curr_chunk = &new_chunk->next; 224 } 225 } 226 227 /* initialize the alloc property for this device */ 228 hw_memory_set_available(me, hw_memory); 229 } 230 231 static void 232 hw_memory_instance_delete(device_instance *instance) 233 { 234 return; 235 } 236 237 static int 238 hw_memory_instance_claim(device_instance *instance, 239 int n_stack_args, 240 unsigned_cell stack_args[/*n_stack_args*/], 241 int n_stack_returns, 242 unsigned_cell stack_returns[/*n_stack_returns*/]) 243 { 244 hw_memory_device *hw_memory = device_instance_data(instance); 245 device *me = device_instance_device(instance); 246 int stackp = 0; 247 unsigned_word alignment; 248 unsigned_cell size; 249 unsigned_cell address; 250 hw_memory_chunk *chunk = NULL; 251 252 /* get the alignment from the stack */ 253 if (n_stack_args < stackp + 1) 254 device_error(me, "claim - incorrect number of arguments (alignment missing)"); 255 alignment = stack_args[stackp]; 256 stackp++; 257 258 /* get the size from the stack */ 259 { 260 int i; 261 int nr_cells = device_nr_size_cells(device_parent(me)); 262 if (n_stack_args < stackp + nr_cells) 263 device_error(me, "claim - incorrect number of arguments (size missing)"); 264 for (i = 0; i < nr_cells - 1; i++) { 265 if (stack_args[stackp] != 0) 266 device_error(me, "claim - multi-cell sizes not supported"); 267 stackp++; 268 } 269 size = stack_args[stackp]; 270 stackp++; 271 } 272 273 /* get the address from the stack */ 274 { 275 int nr_cells = device_nr_address_cells(device_parent(me)); 276 if (alignment != 0) { 277 if (n_stack_args != stackp) { 278 if (n_stack_args == stackp + nr_cells) 279 DTRACE(memory, ("claim - extra address argument ignored\n")); 280 else 281 device_error(me, "claim - incorrect number of arguments (optional addr)"); 282 } 283 address = 0; 284 } 285 else { 286 int i; 287 if (n_stack_args != stackp + nr_cells) 288 device_error(me, "claim - incorrect number of arguments (addr missing)"); 289 for (i = 0; i < nr_cells - 1; i++) { 290 if (stack_args[stackp] != 0) 291 device_error(me, "claim - multi-cell addresses not supported"); 292 stackp++; 293 } 294 address = stack_args[stackp]; 295 } 296 } 297 298 /* check that there is space for the result */ 299 if (n_stack_returns != 0 300 && n_stack_returns != device_nr_address_cells(device_parent(me))) 301 device_error(me, "claim - invalid number of return arguments"); 302 303 /* find a chunk candidate, either according to address or alignment */ 304 if (alignment == 0) { 305 chunk = hw_memory->heap; 306 while (chunk != NULL) { 307 if ((address + size) <= (chunk->address + chunk->size)) 308 break; 309 chunk = chunk->next; 310 } 311 if (chunk == NULL || address < chunk->address || !chunk->available) 312 device_error(me, "failed to allocate %ld bytes at 0x%lx", 313 (unsigned long)size, (unsigned long)address); 314 DTRACE(memory, ("claim - address=0x%lx size=0x%lx\n", 315 (unsigned long)address, 316 (unsigned long)size)); 317 } 318 else { 319 /* adjust the alignment so that it is a power of two */ 320 unsigned_word align_mask = 1; 321 while (align_mask < alignment && align_mask != 0) 322 align_mask <<= 1; 323 if (align_mask == 0) 324 device_error(me, "alignment 0x%lx is to large", (unsigned long)alignment); 325 align_mask -= 1; 326 /* now find an aligned chunk that fits */ 327 chunk = hw_memory->heap; 328 while (chunk != NULL) { 329 address = ((chunk->address + align_mask) & ~align_mask); 330 if ((chunk->available) 331 && (chunk->address + chunk->size >= address + size)) 332 break; 333 chunk = chunk->next; 334 } 335 if (chunk == NULL) 336 device_error(me, "failed to allocate %ld bytes with alignment %ld", 337 (unsigned long)size, (unsigned long)alignment); 338 DTRACE(memory, ("claim - size=0x%lx alignment=%ld (0x%lx), address=0x%lx\n", 339 (unsigned long)size, 340 (unsigned long)alignment, 341 (unsigned long)alignment, 342 (unsigned long)address)); 343 } 344 345 /* break off a bit before this chunk if needed */ 346 ASSERT(address >= chunk->address); 347 if (address > chunk->address) { 348 hw_memory_chunk *next_chunk = ZALLOC(hw_memory_chunk); 349 /* insert a new chunk */ 350 next_chunk->next = chunk->next; 351 chunk->next = next_chunk; 352 /* adjust the address/size */ 353 next_chunk->address = address; 354 next_chunk->size = chunk->address + chunk->size - next_chunk->address; 355 next_chunk->available = 1; 356 chunk->size = next_chunk->address - chunk->address; 357 /* make this new chunk the one to allocate */ 358 chunk = next_chunk; 359 } 360 ASSERT(address == chunk->address); 361 362 /* break off a bit after this chunk if needed */ 363 ASSERT(address + size <= chunk->address + chunk->size); 364 if (address + size < chunk->address + chunk->size) { 365 hw_memory_chunk *next_chunk = ZALLOC(hw_memory_chunk); 366 /* insert it in to the list */ 367 next_chunk->next = chunk->next; 368 chunk->next = next_chunk; 369 /* adjust the address/size */ 370 next_chunk->address = address + size; 371 next_chunk->size = chunk->address + chunk->size - next_chunk->address; 372 next_chunk->available = 1; 373 chunk->size = next_chunk->address - chunk->address; 374 } 375 ASSERT(address + size == chunk->address + chunk->size); 376 377 /* now allocate/return it */ 378 chunk->available = 0; 379 hw_memory_set_available(device_instance_device(instance), hw_memory); 380 if (n_stack_returns > 0) { 381 int i; 382 for (i = 0; i < n_stack_returns - 1; i++) 383 stack_returns[i] = 0; 384 stack_returns[n_stack_returns - 1] = address; 385 } 386 387 return 0; 388 } 389 390 391 static int 392 hw_memory_instance_release(device_instance *instance, 393 int n_stack_args, 394 unsigned_cell stack_args[/*n_stack_args*/], 395 int n_stack_returns, 396 unsigned_cell stack_returns[/*n_stack_returns*/]) 397 { 398 hw_memory_device *hw_memory = device_instance_data(instance); 399 device *me = device_instance_device(instance); 400 unsigned_word length; 401 unsigned_word address; 402 int stackp = 0; 403 hw_memory_chunk *chunk; 404 405 /* get the length from the stack */ 406 { 407 int i; 408 int nr_cells = device_nr_size_cells(device_parent(me)); 409 if (n_stack_args < stackp + nr_cells) 410 device_error(me, "release - incorrect number of arguments (length missing)"); 411 for (i = 0; i < nr_cells - 1; i++) { 412 if (stack_args[stackp] != 0) 413 device_error(me, "release - multi-cell length not supported"); 414 stackp++; 415 } 416 length = stack_args[stackp]; 417 stackp++; 418 } 419 420 /* get the address from the stack */ 421 { 422 int i; 423 int nr_cells = device_nr_address_cells(device_parent(me)); 424 if (n_stack_args != stackp + nr_cells) 425 device_error(me, "release - incorrect number of arguments (addr missing)"); 426 for (i = 0; i < nr_cells - 1; i++) { 427 if (stack_args[stackp] != 0) 428 device_error(me, "release - multi-cell addresses not supported"); 429 stackp++; 430 } 431 address = stack_args[stackp]; 432 } 433 434 /* returns ok */ 435 if (n_stack_returns != 0) 436 device_error(me, "release - nonzero number of results"); 437 438 /* try to free the corresponding memory chunk */ 439 chunk = hw_memory->heap; 440 while (chunk != NULL) { 441 if (chunk->address == address 442 && chunk->size == length) { 443 /* an exact match */ 444 if (chunk->available) 445 device_error(me, "memory chunk 0x%lx (size 0x%lx) already available", 446 (unsigned long)address, 447 (unsigned long)length); 448 else { 449 /* free this chunk */ 450 DTRACE(memory, ("release - address=0x%lx, length=0x%lx\n", 451 (unsigned long) address, 452 (unsigned long) length)); 453 chunk->available = 1; 454 break; 455 } 456 } 457 else if (chunk->address >= address 458 && chunk->address + chunk->size <= address + length) { 459 /* a sub region */ 460 if (!chunk->available) { 461 DTRACE(memory, ("release - address=0x%lx, size=0x%lx within region 0x%lx length 0x%lx\n", 462 (unsigned long) chunk->address, 463 (unsigned long) chunk->size, 464 (unsigned long) address, 465 (unsigned long) length)); 466 chunk->available = 1; 467 } 468 } 469 chunk = chunk->next; 470 } 471 if (chunk == NULL) { 472 printf_filtered("warning: released chunks within region 0x%lx..0x%lx\n", 473 (unsigned long)address, 474 (unsigned long)(address + length - 1)); 475 } 476 477 /* check for the chance to merge two adjacent available memory chunks */ 478 chunk = hw_memory->heap; 479 while (chunk != NULL) { 480 if (chunk->available 481 && chunk->next != NULL && chunk->next->available) { 482 /* adjacent */ 483 hw_memory_chunk *delete = chunk->next; 484 ASSERT(chunk->address + chunk->size == delete->address); 485 chunk->size += delete->size; 486 chunk->next = delete->next; 487 free(delete); 488 } 489 else { 490 chunk = chunk->next; 491 } 492 } 493 494 /* update the corresponding property */ 495 hw_memory_set_available(device_instance_device(instance), hw_memory); 496 497 return 0; 498 } 499 500 501 static device_instance_methods hw_memory_instance_methods[] = { 502 { "claim", hw_memory_instance_claim }, 503 { "release", hw_memory_instance_release }, 504 { NULL, }, 505 }; 506 507 static device_instance_callbacks const hw_memory_instance_callbacks = { 508 hw_memory_instance_delete, 509 NULL /*read*/, NULL /*write*/, NULL /*seek*/, 510 hw_memory_instance_methods 511 }; 512 513 static device_instance * 514 hw_memory_create_instance(device *me, 515 const char *path, 516 const char *args) 517 { 518 return device_create_instance_from(me, NULL, 519 device_data(me), /* nothing better */ 520 path, args, 521 &hw_memory_instance_callbacks); 522 } 523 524 static device_callbacks const hw_memory_callbacks = { 525 { hw_memory_init_address, }, 526 { NULL, }, /* address */ 527 { NULL, }, /* IO */ 528 { NULL, }, /* DMA */ 529 { NULL, }, /* interrupt */ 530 { NULL, }, /* unit */ 531 hw_memory_create_instance, 532 }; 533 534 const device_descriptor hw_memory_device_descriptor[] = { 535 { "memory", hw_memory_create, &hw_memory_callbacks }, 536 { NULL }, 537 }; 538 539 #endif /* _HW_MEMORY_C_ */ 540