mm_page.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  1. /*
  2. * Copyright (c) 2006-2022, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2019-11-01 Jesven The first version
  9. * 2022-12-13 WangXiaoyao Hot-pluggable, extensible
  10. * page management algorithm
  11. */
  12. #include <rtthread.h>
  13. #include <stddef.h>
  14. #include <stdint.h>
  15. #include <string.h>
  16. #include "mm_fault.h"
  17. #include "mm_private.h"
  18. #include "mm_aspace.h"
  19. #include "mm_flag.h"
  20. #include "mm_page.h"
  21. #include <mmu.h>
  22. #define DBG_TAG "mm.page"
  23. #define DBG_LVL DBG_WARNING
  24. #include <rtdbg.h>
  25. #ifdef RT_USING_SMART
  26. #include "lwp_arch_comm.h"
  27. #define CT_ASSERT(name, x) \
  28. struct assert_##name \
  29. { \
  30. char ary[2 * (x)-1]; \
  31. }
  32. #ifdef ARCH_CPU_64BIT
  33. CT_ASSERT(order_huge_pg, RT_PAGE_MAX_ORDER > ARCH_PAGE_SHIFT - 2);
  34. #else
  35. CT_ASSERT(size_width, sizeof(rt_size_t) == sizeof(rt_size_t));
  36. #endif /* ARCH_CPU_64BIT */
  37. #endif /* RT_USING_SMART */
  38. static rt_size_t init_mpr_align_start;
  39. static rt_size_t init_mpr_align_end;
  40. static void *init_mpr_cont_start;
  41. static struct rt_varea mpr_varea;
  42. static struct rt_page *page_list[RT_PAGE_MAX_ORDER];
  43. #define page_start ((rt_page_t)rt_mpr_start)
  44. static rt_size_t page_nr;
  45. static rt_size_t early_offset;
  46. static const char *get_name(rt_varea_t varea)
  47. {
  48. return "master-page-record";
  49. }
  50. static void hint_free(rt_mm_va_hint_t hint)
  51. {
  52. hint->flags = MMF_MAP_FIXED;
  53. hint->limit_start = rt_kernel_space.start;
  54. hint->limit_range_size = rt_kernel_space.size;
  55. hint->prefer = rt_mpr_start;
  56. }
  57. static void on_page_fault(struct rt_varea *varea, struct rt_mm_fault_msg *msg)
  58. {
  59. void *init_start = (void *)init_mpr_align_start;
  60. void *init_end = (void *)init_mpr_align_end;
  61. if (msg->vaddr < init_end && msg->vaddr >= init_start)
  62. {
  63. rt_size_t offset = msg->vaddr - init_start;
  64. msg->response.status = MM_FAULT_STATUS_OK;
  65. msg->response.vaddr = init_mpr_cont_start + offset;
  66. msg->response.size = ARCH_PAGE_SIZE;
  67. }
  68. else
  69. {
  70. void *raw_page = rt_pages_alloc(0);
  71. msg->response.status = MM_FAULT_STATUS_OK;
  72. msg->response.vaddr = raw_page;
  73. msg->response.size = ARCH_PAGE_SIZE;
  74. }
  75. }
  76. static struct rt_mem_obj mm_page_mapper = {
  77. .get_name = get_name,
  78. .on_page_fault = on_page_fault,
  79. .hint_free = hint_free,
  80. };
  81. static inline void *page_to_addr(rt_page_t page)
  82. {
  83. return (void *)((page - page_start) << ARCH_PAGE_SHIFT) - PV_OFFSET;
  84. }
  85. static inline rt_page_t addr_to_page(rt_page_t pg_start, void *addr)
  86. {
  87. addr += PV_OFFSET;
  88. return &pg_start[((uintptr_t)addr >> ARCH_PAGE_SHIFT)];
  89. }
  90. #define FLOOR(val, align) (((rt_size_t)(val) + (align)-1) & ~((align)-1))
  91. const rt_size_t shadow_mask =
  92. ((1ul << (RT_PAGE_MAX_ORDER + ARCH_PAGE_SHIFT - 1)) - 1);
  93. const rt_size_t rt_mpr_size = FLOOR(
  94. ((1ul << (ARCH_VADDR_WIDTH - ARCH_PAGE_SHIFT))) * sizeof(struct rt_page),
  95. ARCH_PAGE_SIZE);
  96. void *rt_mpr_start;
  97. rt_weak int rt_hw_clz(unsigned long n)
  98. {
  99. return __builtin_clzl(n);
  100. }
  101. rt_weak int rt_hw_ctz(unsigned long n)
  102. {
  103. return __builtin_ctzl(n);
  104. }
  105. rt_size_t rt_page_bits(rt_size_t size)
  106. {
  107. int bit = sizeof(rt_size_t) * 8 - rt_hw_clz(size) - 1;
  108. if ((size ^ (1UL << bit)) != 0)
  109. {
  110. bit++;
  111. }
  112. bit -= ARCH_PAGE_SHIFT;
  113. if (bit < 0)
  114. {
  115. bit = 0;
  116. }
  117. return bit;
  118. }
  119. struct rt_page *rt_page_addr2page(void *addr)
  120. {
  121. return addr_to_page(page_start, addr);
  122. }
  123. void *rt_page_page2addr(struct rt_page *p)
  124. {
  125. return page_to_addr(p);
  126. }
  127. static inline struct rt_page *buddy_get(struct rt_page *p,
  128. rt_uint32_t size_bits)
  129. {
  130. rt_size_t addr;
  131. addr = (rt_size_t)rt_page_page2addr(p);
  132. addr ^= (1UL << (size_bits + ARCH_PAGE_SHIFT));
  133. return rt_page_addr2page((void *)addr);
  134. }
  135. static void page_remove(struct rt_page *p, rt_uint32_t size_bits)
  136. {
  137. if (p->pre)
  138. {
  139. p->pre->next = p->next;
  140. }
  141. else
  142. {
  143. page_list[size_bits] = p->next;
  144. }
  145. if (p->next)
  146. {
  147. p->next->pre = p->pre;
  148. }
  149. p->size_bits = ARCH_ADDRESS_WIDTH_BITS;
  150. }
  151. static void page_insert(struct rt_page *p, rt_uint32_t size_bits)
  152. {
  153. p->next = page_list[size_bits];
  154. if (p->next)
  155. {
  156. p->next->pre = p;
  157. }
  158. p->pre = 0;
  159. page_list[size_bits] = p;
  160. p->size_bits = size_bits;
  161. }
  162. static void _pages_ref_inc(struct rt_page *p, rt_uint32_t size_bits)
  163. {
  164. struct rt_page *page_head;
  165. int idx;
  166. /* find page group head */
  167. idx = p - page_start;
  168. idx = idx & ~((1UL << size_bits) - 1);
  169. page_head = page_start + idx;
  170. page_head = (void *)page_head + early_offset;
  171. page_head->ref_cnt++;
  172. }
  173. static int _pages_ref_get(struct rt_page *p, rt_uint32_t size_bits)
  174. {
  175. struct rt_page *page_head;
  176. int idx;
  177. /* find page group head */
  178. idx = p - page_start;
  179. idx = idx & ~((1UL << size_bits) - 1);
  180. page_head = page_start + idx;
  181. return page_head->ref_cnt;
  182. }
  183. static int _pages_free(struct rt_page *p, rt_uint32_t size_bits)
  184. {
  185. rt_uint32_t level = size_bits;
  186. struct rt_page *buddy;
  187. RT_ASSERT(p >= page_start);
  188. RT_ASSERT((void *)p < rt_mpr_start + rt_mpr_size);
  189. RT_ASSERT(rt_kmem_v2p(p));
  190. RT_ASSERT(p->ref_cnt > 0);
  191. RT_ASSERT(p->size_bits == ARCH_ADDRESS_WIDTH_BITS);
  192. RT_ASSERT(size_bits < RT_PAGE_MAX_ORDER);
  193. p->ref_cnt--;
  194. if (p->ref_cnt != 0)
  195. {
  196. return 0;
  197. }
  198. while (level < RT_PAGE_MAX_ORDER - 1)
  199. {
  200. buddy = buddy_get(p, level);
  201. if (buddy && buddy->size_bits == level)
  202. {
  203. page_remove(buddy, level);
  204. p = (p < buddy) ? p : buddy;
  205. level++;
  206. }
  207. else
  208. {
  209. break;
  210. }
  211. }
  212. page_insert(p, level);
  213. return 1;
  214. }
  215. static struct rt_page *_pages_alloc(rt_uint32_t size_bits)
  216. {
  217. struct rt_page *p;
  218. if (page_list[size_bits])
  219. {
  220. p = page_list[size_bits];
  221. page_remove(p, size_bits);
  222. }
  223. else
  224. {
  225. rt_uint32_t level;
  226. for (level = size_bits + 1; level < RT_PAGE_MAX_ORDER; level++)
  227. {
  228. if (page_list[level])
  229. {
  230. break;
  231. }
  232. }
  233. if (level == RT_PAGE_MAX_ORDER)
  234. {
  235. return 0;
  236. }
  237. p = page_list[level];
  238. page_remove(p, level);
  239. while (level > size_bits)
  240. {
  241. page_insert(p, level - 1);
  242. p = buddy_get(p, level - 1);
  243. level--;
  244. }
  245. }
  246. p->size_bits = ARCH_ADDRESS_WIDTH_BITS;
  247. p->ref_cnt = 1;
  248. return p;
  249. }
  250. static void _early_page_remove(rt_page_t page, rt_uint32_t size_bits)
  251. {
  252. rt_page_t page_cont = (void *)page + early_offset;
  253. if (page_cont->pre)
  254. {
  255. rt_page_t pre_cont = (void *)page_cont->pre + early_offset;
  256. pre_cont->next = page_cont->next;
  257. }
  258. else
  259. {
  260. page_list[size_bits] = page_cont->next;
  261. }
  262. if (page_cont->next)
  263. {
  264. rt_page_t next_cont = (void *)page_cont->next + early_offset;
  265. next_cont->pre = page_cont->pre;
  266. }
  267. page_cont->size_bits = ARCH_ADDRESS_WIDTH_BITS;
  268. }
  269. static void _early_page_insert(rt_page_t page, int size_bits)
  270. {
  271. RT_ASSERT((void *)page >= rt_mpr_start &&
  272. (void *)page - rt_mpr_start < +rt_mpr_size);
  273. rt_page_t page_cont = (void *)page + early_offset;
  274. page_cont->next = page_list[size_bits];
  275. if (page_cont->next)
  276. {
  277. rt_page_t next_cont = (void *)page_cont->next + early_offset;
  278. next_cont->pre = page;
  279. }
  280. page_cont->pre = 0;
  281. page_list[size_bits] = page;
  282. page_cont->size_bits = size_bits;
  283. }
  284. static struct rt_page *_early_pages_alloc(rt_uint32_t size_bits)
  285. {
  286. struct rt_page *p;
  287. if (page_list[size_bits])
  288. {
  289. p = page_list[size_bits];
  290. _early_page_remove(p, size_bits);
  291. }
  292. else
  293. {
  294. rt_uint32_t level;
  295. for (level = size_bits + 1; level < RT_PAGE_MAX_ORDER; level++)
  296. {
  297. if (page_list[level])
  298. {
  299. break;
  300. }
  301. }
  302. if (level == RT_PAGE_MAX_ORDER)
  303. {
  304. return 0;
  305. }
  306. p = page_list[level];
  307. _early_page_remove(p, level);
  308. while (level > size_bits)
  309. {
  310. _early_page_insert(p, level - 1);
  311. p = buddy_get(p, level - 1);
  312. level--;
  313. }
  314. }
  315. rt_page_t page_cont = (void *)p + early_offset;
  316. page_cont->size_bits = ARCH_ADDRESS_WIDTH_BITS;
  317. page_cont->ref_cnt = 1;
  318. return p;
  319. }
  320. int rt_page_ref_get(void *addr, rt_uint32_t size_bits)
  321. {
  322. struct rt_page *p;
  323. rt_base_t level;
  324. int ref;
  325. p = rt_page_addr2page(addr);
  326. level = rt_hw_interrupt_disable();
  327. ref = _pages_ref_get(p, size_bits);
  328. rt_hw_interrupt_enable(level);
  329. return ref;
  330. }
  331. void rt_page_ref_inc(void *addr, rt_uint32_t size_bits)
  332. {
  333. struct rt_page *p;
  334. rt_base_t level;
  335. p = rt_page_addr2page(addr);
  336. level = rt_hw_interrupt_disable();
  337. _pages_ref_inc(p, size_bits);
  338. rt_hw_interrupt_enable(level);
  339. }
  340. static rt_page_t (*pages_alloc_handler)(rt_uint32_t size_bits);
  341. void *rt_pages_alloc(rt_uint32_t size_bits)
  342. {
  343. void *alloc_buf = RT_NULL;
  344. struct rt_page *p;
  345. rt_base_t level;
  346. level = rt_hw_interrupt_disable();
  347. p = pages_alloc_handler(size_bits);
  348. rt_hw_interrupt_enable(level);
  349. if (p)
  350. {
  351. alloc_buf = page_to_addr(p);
  352. }
  353. return alloc_buf;
  354. }
  355. int rt_pages_free(void *addr, rt_uint32_t size_bits)
  356. {
  357. struct rt_page *p;
  358. int real_free = 0;
  359. p = rt_page_addr2page(addr);
  360. if (p)
  361. {
  362. rt_base_t level;
  363. level = rt_hw_interrupt_disable();
  364. real_free = _pages_free(p, size_bits);
  365. rt_hw_interrupt_enable(level);
  366. }
  367. return real_free;
  368. }
  369. void rt_page_list(void) __attribute__((alias("list_page")));
  370. void list_page(void)
  371. {
  372. int i;
  373. rt_size_t total = 0;
  374. rt_base_t level;
  375. level = rt_hw_interrupt_disable();
  376. for (i = 0; i < RT_PAGE_MAX_ORDER; i++)
  377. {
  378. struct rt_page *p = page_list[i];
  379. rt_kprintf("level %d ", i);
  380. while (p)
  381. {
  382. total += (1UL << i);
  383. rt_kprintf("[0x%08p]", rt_page_page2addr(p));
  384. p = p->next;
  385. }
  386. rt_kprintf("\n");
  387. }
  388. rt_hw_interrupt_enable(level);
  389. rt_kprintf("free pages is 0x%08lx (%ld KB)\n", total, total * ARCH_PAGE_SIZE / 1024);
  390. rt_kprintf("-------------------------------\n");
  391. }
  392. MSH_CMD_EXPORT(list_page, show page info);
  393. void rt_page_get_info(rt_size_t *total_nr, rt_size_t *free_nr)
  394. {
  395. int i;
  396. rt_size_t total_free = 0;
  397. rt_base_t level;
  398. level = rt_hw_interrupt_disable();
  399. for (i = 0; i < RT_PAGE_MAX_ORDER; i++)
  400. {
  401. struct rt_page *p = page_list[i];
  402. while (p)
  403. {
  404. total_free += (1UL << i);
  405. p = p->next;
  406. }
  407. }
  408. rt_hw_interrupt_enable(level);
  409. *total_nr = page_nr;
  410. *free_nr = total_free;
  411. }
  412. void rt_page_init(rt_region_t reg)
  413. {
  414. int i;
  415. rt_region_t shadow;
  416. /* inclusive start, exclusive end */
  417. reg.start += ARCH_PAGE_MASK;
  418. reg.start &= ~ARCH_PAGE_MASK;
  419. reg.end &= ~ARCH_PAGE_MASK;
  420. if (reg.end <= reg.start)
  421. {
  422. LOG_E("region end(%p) must greater than start(%p)", reg.start, reg.end);
  423. RT_ASSERT(0);
  424. }
  425. page_nr = ((reg.end - reg.start) >> ARCH_PAGE_SHIFT);
  426. shadow.start = reg.start & ~shadow_mask;
  427. shadow.end = FLOOR(reg.end, shadow_mask + 1);
  428. LOG_D("[Init page] start: 0x%lx, end: 0x%lx, total: 0x%lx", reg.start,
  429. reg.end, page_nr);
  430. int err;
  431. /* init free list */
  432. for (i = 0; i < RT_PAGE_MAX_ORDER; i++)
  433. {
  434. page_list[i] = 0;
  435. }
  436. /* map MPR area */
  437. err = rt_aspace_map_static(&rt_kernel_space, &mpr_varea, &rt_mpr_start,
  438. rt_mpr_size, MMU_MAP_K_RWCB, MMF_MAP_FIXED,
  439. &mm_page_mapper, 0);
  440. if (err != RT_EOK)
  441. {
  442. LOG_E("MPR map failed with size %lx at %p", rt_mpr_size, rt_mpr_start);
  443. RT_ASSERT(0);
  444. }
  445. /* calculate footprint */
  446. init_mpr_align_start =
  447. (rt_size_t)addr_to_page(page_start, (void *)shadow.start) &
  448. ~ARCH_PAGE_MASK;
  449. init_mpr_align_end =
  450. FLOOR(addr_to_page(page_start, (void *)shadow.end), ARCH_PAGE_SIZE);
  451. rt_size_t init_mpr_size = init_mpr_align_end - init_mpr_align_start;
  452. rt_size_t init_mpr_npage = init_mpr_size >> ARCH_PAGE_SHIFT;
  453. init_mpr_cont_start = (void *)reg.start;
  454. void *init_mpr_cont_end = init_mpr_cont_start + init_mpr_size;
  455. early_offset = init_mpr_cont_start - (void *)init_mpr_align_start;
  456. rt_page_t mpr_cont = rt_mpr_start + early_offset;
  457. /* mark init mpr pages as illegal */
  458. rt_page_t head_cont = addr_to_page(mpr_cont, (void *)reg.start);
  459. rt_page_t tail_cont = addr_to_page(mpr_cont, (void *)reg.end);
  460. for (rt_page_t iter = head_cont; iter < tail_cont; iter++)
  461. {
  462. iter->size_bits = ARCH_ADDRESS_WIDTH_BITS;
  463. }
  464. /* mark shadow pages as illegal */
  465. rt_page_t shad_head_cont = addr_to_page(mpr_cont, (void *)shadow.start);
  466. for (rt_page_t iter = shad_head_cont; iter < head_cont; iter++)
  467. {
  468. iter->size_bits = ARCH_ADDRESS_WIDTH_BITS;
  469. }
  470. rt_page_t shad_tail_cont = addr_to_page(mpr_cont, (void *)shadow.end);
  471. for (rt_page_t iter = tail_cont; iter < shad_tail_cont; iter++)
  472. {
  473. iter->size_bits = ARCH_ADDRESS_WIDTH_BITS;
  474. }
  475. /* insert reserved pages to list */
  476. reg.start = (rt_size_t)init_mpr_cont_end;
  477. const int max_order = RT_PAGE_MAX_ORDER + ARCH_PAGE_SHIFT - 1;
  478. while (reg.start != reg.end)
  479. {
  480. struct rt_page *p;
  481. int align_bits;
  482. int size_bits;
  483. size_bits =
  484. ARCH_ADDRESS_WIDTH_BITS - 1 - rt_hw_clz(reg.end - reg.start);
  485. align_bits = rt_hw_ctz(reg.start);
  486. if (align_bits < size_bits)
  487. {
  488. size_bits = align_bits;
  489. }
  490. if (size_bits > max_order)
  491. {
  492. size_bits = max_order;
  493. }
  494. p = addr_to_page(mpr_cont, (void *)reg.start);
  495. p->size_bits = ARCH_ADDRESS_WIDTH_BITS;
  496. p->ref_cnt = 0;
  497. /* insert to list */
  498. _early_page_insert((void *)p - early_offset,
  499. size_bits - ARCH_PAGE_SHIFT);
  500. reg.start += (1UL << size_bits);
  501. }
  502. pages_alloc_handler = _early_pages_alloc;
  503. /* doing the page table bushiness */
  504. if (rt_aspace_load_page(&rt_kernel_space, (void *)init_mpr_align_start, init_mpr_npage))
  505. {
  506. LOG_E("%s: failed to load pages", __func__);
  507. RT_ASSERT(0);
  508. }
  509. if (rt_hw_mmu_tbl_get() == rt_kernel_space.page_table)
  510. rt_page_cleanup();
  511. }
  512. static int _load_mpr_area(void *head, void *tail)
  513. {
  514. int err = 0;
  515. void *iter = (void *)((uintptr_t)head & ~ARCH_PAGE_MASK);
  516. tail = (void *)FLOOR(tail, ARCH_PAGE_SIZE);
  517. while (iter != tail)
  518. {
  519. void *paddr = rt_kmem_v2p(iter);
  520. if (paddr == ARCH_MAP_FAILED)
  521. {
  522. err = rt_aspace_load_page(&rt_kernel_space, iter, 1);
  523. if (err != RT_EOK)
  524. {
  525. LOG_E("%s: failed to load page", __func__);
  526. break;
  527. }
  528. }
  529. iter += ARCH_PAGE_SIZE;
  530. }
  531. return err;
  532. }
  533. int rt_page_install(rt_region_t region)
  534. {
  535. int err = -RT_EINVAL;
  536. if (region.end != region.start && !(region.start & ARCH_PAGE_MASK) &&
  537. !(region.end & ARCH_PAGE_MASK) &&
  538. !((region.end - region.start) & shadow_mask))
  539. {
  540. void *head = addr_to_page(page_start, (void *)region.start);
  541. void *tail = addr_to_page(page_start, (void *)region.end);
  542. page_nr += ((region.end - region.start) >> ARCH_PAGE_SHIFT);
  543. err = _load_mpr_area(head, tail);
  544. if (err == RT_EOK)
  545. {
  546. while (region.start != region.end)
  547. {
  548. struct rt_page *p;
  549. int size_bits;
  550. size_bits = RT_PAGE_MAX_ORDER - 1;
  551. p = addr_to_page(page_start, (void *)region.start);
  552. p->size_bits = ARCH_ADDRESS_WIDTH_BITS;
  553. p->ref_cnt = 1;
  554. _pages_free(p, size_bits);
  555. region.start += (1UL << (size_bits + ARCH_PAGE_SHIFT));
  556. }
  557. }
  558. }
  559. return err;
  560. }
  561. void rt_page_cleanup(void)
  562. {
  563. early_offset = 0;
  564. pages_alloc_handler = _pages_alloc;
  565. }