zircon/kernel/arch/x86/page_tables/page_tables.cc
/**
 * @brief Creates mappings for the range specified by start_cursor
 *
 * Level must be top_level() when invoked.
 *
 * @param table The top-level paging structure's virtual address.
 * @param start_cursor A cursor describing the range of address space to
 * act on within table
 * @param new_cursor A returned cursor describing how much work was not
 * completed.  Must be non-null.
 *
 * @return ZX_OK if successful
 * @return ZX_ERR_ALREADY_EXISTS if the range overlaps an existing mapping
 * @return ZX_ERR_NO_MEMORY if intermediate page tables could not be allocated
 */
@brief Creates mappings for the range specified by start_cursor
Level must be top_level() when invoked.

@param table The top-level paging structure's virtual address.
@param start_cursor A cursor describing the range of address space to
act on within table
@param new_cursor A returned cursor describing how much work was not
completed.  Must be non-null.

@return ZX_OK if successful
@return ZX_ERR_ALREADY_EXISTS if the range overlaps an existing mapping
@return ZX_ERR_NO_MEMORY if intermediate page tables could not be allocated
@brief Creates mappings for the range specified by start_cursor Level must be top_level () when invoked . @param table The top - level paging structure ' s virtual address . @param start_cursor A cursor describing the range of address space to act on within table @param new_cursor A returned cursor describing how much work was not completed . Must be non - null . @return ZX_OK if successful @return ZX_ERR_ALREADY_EXISTS if the range overlaps an existing mapping @return ZX_ERR_NO_MEMORY if intermediate page tables could not be allocated
zx_status_t X86PageTableBase::AddMapping(volatile pt_entry_t* table, uint mmu_flags,
                                         PageTableLevel level, const MappingCursor& start_cursor,
                                         MappingCursor* new_cursor, ConsistencyManager* cm) {
  DEBUG_ASSERT(table);
  DEBUG_ASSERT(check_vaddr(start_cursor.vaddr));
  DEBUG_ASSERT(check_paddr(start_cursor.paddr));
  zx_status_t ret = ZX_OK;
  *new_cursor = start_cursor;
  if (level == PT_L) {
    return AddMappingL0(table, mmu_flags, start_cursor, new_cursor, cm);
  }
  auto abort = fbl::MakeAutoCall([&]() {
    AssertHeld(lock_);
    if (level == top_level()) {
      MappingCursor cursor = start_cursor;
      MappingCursor result;
      cursor.size -= new_cursor->size;
      if (cursor.size > 0) {
        RemoveMapping(table, level, cursor, &result, cm);
        DEBUG_ASSERT(result.size == 0);
      }
    }
  });
  IntermediatePtFlags interm_flags = intermediate_flags();
  PtFlags term_flags = terminal_flags(level, mmu_flags);
  size_t ps = page_size(level);
  bool level_supports_large_pages = supports_page_size(level);
  uint index = vaddr_to_index(level, new_cursor->vaddr);
  for (; index != NO_OF_PT_ENTRIES && new_cursor->size != 0; ++index) {
    volatile pt_entry_t* e = table + index;
    pt_entry_t pt_val = *e;
    if (IS_PAGE_PRESENT(pt_val) && IS_LARGE_PAGE(pt_val)) {
      return ZX_ERR_ALREADY_EXISTS;
    }
    bool level_valigned = page_aligned(level, new_cursor->vaddr);
    bool level_paligned = page_aligned(level, new_cursor->paddr);
    if (level_supports_large_pages && !IS_PAGE_PRESENT(pt_val) && level_valigned &&
        level_paligned && new_cursor->size >= ps) {
      UpdateEntry(cm, level, new_cursor->vaddr, table + index, new_cursor->paddr,
                  term_flags | X86_MMU_PG_PS, false );
      new_cursor->paddr += ps;
      new_cursor->vaddr += ps;
      new_cursor->size -= ps;
      DEBUG_ASSERT(new_cursor->size <= start_cursor.size);
    } else {
      if (!IS_PAGE_PRESENT(pt_val)) {
        volatile pt_entry_t* m = AllocatePageTable();
        if (m == nullptr) {
          size_t partial_update = ktl::min(ps, new_cursor->size);
          new_cursor->paddr += partial_update;
          new_cursor->vaddr += partial_update;
          new_cursor->size -= partial_update;
          return ZX_ERR_NO_MEMORY;
        }
        LTRACEF_LEVEL(2, "new table %p at level %d\n", m, level);
        UpdateEntry(cm, level, new_cursor->vaddr, e, X86_VIRT_TO_PHYS(m), interm_flags,
                    false );
        pt_val = *e;
        pages_++;
      }
      MappingCursor cursor;
      ret = AddMapping(get_next_table_from_entry(pt_val), mmu_flags, lower_level(level),
                       *new_cursor, &cursor, cm);
      *new_cursor = cursor;
      DEBUG_ASSERT(new_cursor->size <= start_cursor.size);
      if (ret != ZX_OK) {
        return ret;
      }
    }
  }
  abort.cancel();
  return ZX_OK;
}
zx_status_t X86PageTableBase :: AddMapping ( volatile pt_entry_t * table , uint mmu_flags , PageTableLevel level , const MappingCursor & start_cursor , MappingCursor * new_cursor , ConsistencyManager * cm ) { DEBUG_ASSERT ( table ) ; DEBUG_ASSERT ( check_vaddr ( start_cursor . vaddr ) ) ; DEBUG_ASSERT ( check_paddr ( start_cursor . paddr ) ) ; zx_status_t ret = ZX_OK ; * new_cursor = start_cursor ; if ( level == PT_L ) { return AddMappingL0 ( table , mmu_flags , start_cursor , new_cursor , cm ) ; } auto abort = fbl :: MakeAutoCall ( [ & ] ( ) { AssertHeld ( lock_ ) ; if ( level == top_level ( ) ) { MappingCursor cursor = start_cursor ; MappingCursor result ; cursor . size -= new_cursor -> size ; if ( cursor . size > 0 ) { RemoveMapping ( table , level , cursor , & result , cm ) ; DEBUG_ASSERT ( result . size == 0 ) ; } } } ) ; IntermediatePtFlags interm_flags = intermediate_flags ( ) ; PtFlags term_flags = terminal_flags ( level , mmu_flags ) ; size_t ps = page_size ( level ) ; bool level_supports_large_pages = supports_page_size ( level ) ; uint index = vaddr_to_index ( level , new_cursor -> vaddr ) ; for ( ; index != NO_OF_PT_ENTRIES && new_cursor -> size != 0 ; ++ index ) { volatile pt_entry_t * e = table + index ; pt_entry_t pt_val = * e ; if ( IS_PAGE_PRESENT ( pt_val ) && IS_LARGE_PAGE ( pt_val ) ) { return ZX_ERR_ALREADY_EXISTS ; } bool level_valigned = page_aligned ( level , new_cursor -> vaddr ) ; bool level_paligned = page_aligned ( level , new_cursor -> paddr ) ; if ( level_supports_large_pages && ! IS_PAGE_PRESENT ( pt_val ) && level_valigned && level_paligned && new_cursor -> size >= ps ) { UpdateEntry ( cm , level , new_cursor -> vaddr , table + index , new_cursor -> paddr , term_flags | X86_MMU_PG_PS , false ) ; new_cursor -> paddr += ps ; new_cursor -> vaddr += ps ; new_cursor -> size -= ps ; DEBUG_ASSERT ( new_cursor -> size <= start_cursor . size ) ; } else { if ( ! IS_PAGE_PRESENT ( pt_val ) ) { volatile pt_entry_t * m = AllocatePageTable ( ) ; if ( m == nullptr ) { size_t partial_update = ktl :: min ( ps , new_cursor -> size ) ; new_cursor -> paddr += partial_update ; new_cursor -> vaddr += partial_update ; new_cursor -> size -= partial_update ; return ZX_ERR_NO_MEMORY ; } LTRACEF_LEVEL ( 2 , " \n " , m , level ) ; UpdateEntry ( cm , level , new_cursor -> vaddr , e , X86_VIRT_TO_PHYS ( m ) , interm_flags , false ) ; pt_val = * e ; pages_ ++ ; } MappingCursor cursor ; ret = AddMapping ( get_next_table_from_entry ( pt_val ) , mmu_flags , lower_level ( level ) , * new_cursor , & cursor , cm ) ; * new_cursor = cursor ; DEBUG_ASSERT ( new_cursor -> size <= start_cursor . size ) ; if ( ret != ZX_OK ) { return ret ; } } } abort . cancel ( ) ; return ZX_OK ; }
@brief Creates mappings for the range specified by start_cursor
 Level must be top_level() when invoked.
@brief Creates mappings for the range specified by start_cursor Level must be top_level () when invoked .
// new_cursor->size should be how much is left to be mapped still // See if there's a large page in our way // Check if this is a candidate for a new large page /* was_terminal */ // See if we need to create a new table // The mapping wasn't fully updated, but there is work here // that might need to be undone. /* was_terminal */