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