mem_detect.c 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. /*
  2. * Copyright IBM Corp. 2008, 2009
  3. *
  4. * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
  5. */
  6. #include <linux/kernel.h>
  7. #include <linux/module.h>
  8. #include <asm/ipl.h>
  9. #include <asm/sclp.h>
  10. #include <asm/setup.h>
  11. #define ADDR2G (1ULL << 31)
  12. static void find_memory_chunks(struct mem_chunk chunk[], unsigned long maxsize)
  13. {
  14. unsigned long long memsize, rnmax, rzm;
  15. unsigned long addr = 0, size;
  16. int i = 0, type;
  17. rzm = sclp_get_rzm();
  18. rnmax = sclp_get_rnmax();
  19. memsize = rzm * rnmax;
  20. if (!rzm)
  21. rzm = 1ULL << 17;
  22. if (sizeof(long) == 4) {
  23. rzm = min(ADDR2G, rzm);
  24. memsize = memsize ? min(ADDR2G, memsize) : ADDR2G;
  25. }
  26. if (maxsize)
  27. memsize = memsize ? min((unsigned long)memsize, maxsize) : maxsize;
  28. do {
  29. size = 0;
  30. type = tprot(addr);
  31. do {
  32. size += rzm;
  33. if (memsize && addr + size >= memsize)
  34. break;
  35. } while (type == tprot(addr + size));
  36. if (type == CHUNK_READ_WRITE || type == CHUNK_READ_ONLY) {
  37. if (memsize && (addr + size > memsize))
  38. size = memsize - addr;
  39. chunk[i].addr = addr;
  40. chunk[i].size = size;
  41. chunk[i].type = type;
  42. i++;
  43. }
  44. addr += size;
  45. } while (addr < memsize && i < MEMORY_CHUNKS);
  46. }
  47. /**
  48. * detect_memory_layout - fill mem_chunk array with memory layout data
  49. * @chunk: mem_chunk array to be filled
  50. * @maxsize: maximum address where memory detection should stop
  51. *
  52. * Fills the passed in memory chunk array with the memory layout of the
  53. * machine. The array must have a size of at least MEMORY_CHUNKS and will
  54. * be fully initialized afterwards.
  55. * If the maxsize paramater has a value > 0 memory detection will stop at
  56. * that address. It is guaranteed that all chunks have an ending address
  57. * that is smaller than maxsize.
  58. * If maxsize is 0 all memory will be detected.
  59. */
  60. void detect_memory_layout(struct mem_chunk chunk[], unsigned long maxsize)
  61. {
  62. unsigned long flags, flags_dat, cr0;
  63. memset(chunk, 0, MEMORY_CHUNKS * sizeof(struct mem_chunk));
  64. /*
  65. * Disable IRQs, DAT and low address protection so tprot does the
  66. * right thing and we don't get scheduled away with low address
  67. * protection disabled.
  68. */
  69. local_irq_save(flags);
  70. flags_dat = __arch_local_irq_stnsm(0xfb);
  71. /*
  72. * In case DAT was enabled, make sure chunk doesn't reside in vmalloc
  73. * space. We have disabled DAT and any access to vmalloc area will
  74. * cause an exception.
  75. * If DAT was disabled we are called from early ipl code.
  76. */
  77. if (test_bit(5, &flags_dat)) {
  78. if (WARN_ON_ONCE(is_vmalloc_or_module_addr(chunk)))
  79. goto out;
  80. }
  81. __ctl_store(cr0, 0, 0);
  82. __ctl_clear_bit(0, 28);
  83. find_memory_chunks(chunk, maxsize);
  84. __ctl_load(cr0, 0, 0);
  85. out:
  86. __arch_local_irq_ssm(flags_dat);
  87. local_irq_restore(flags);
  88. }
  89. EXPORT_SYMBOL(detect_memory_layout);
  90. /*
  91. * Create memory hole with given address and size.
  92. */
  93. void create_mem_hole(struct mem_chunk mem_chunk[], unsigned long addr,
  94. unsigned long size)
  95. {
  96. int i;
  97. for (i = 0; i < MEMORY_CHUNKS; i++) {
  98. struct mem_chunk *chunk = &mem_chunk[i];
  99. if (chunk->size == 0)
  100. continue;
  101. if (addr > chunk->addr + chunk->size)
  102. continue;
  103. if (addr + size <= chunk->addr)
  104. continue;
  105. /* Split */
  106. if ((addr > chunk->addr) &&
  107. (addr + size < chunk->addr + chunk->size)) {
  108. struct mem_chunk *new = chunk + 1;
  109. memmove(new, chunk, (MEMORY_CHUNKS-i-1) * sizeof(*new));
  110. new->addr = addr + size;
  111. new->size = chunk->addr + chunk->size - new->addr;
  112. chunk->size = addr - chunk->addr;
  113. continue;
  114. } else if ((addr <= chunk->addr) &&
  115. (addr + size >= chunk->addr + chunk->size)) {
  116. memset(chunk, 0 , sizeof(*chunk));
  117. } else if (addr + size < chunk->addr + chunk->size) {
  118. chunk->size = chunk->addr + chunk->size - addr - size;
  119. chunk->addr = addr + size;
  120. } else if (addr > chunk->addr) {
  121. chunk->size = addr - chunk->addr;
  122. }
  123. }
  124. }