Andrew Scull | 1ba470e | 2018-10-31 15:14:31 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2018 Google LLC |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * https://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #include "hf/arch/mm.h" |
| 18 | |
| 19 | #include "hf/mm.h" |
| 20 | |
| 21 | /* |
| 22 | * Our fake architecture has page tables base on those of aarch64: |
| 23 | * |
| 24 | * - The highest level table is always 2, lowest level is 0. |
| 25 | * - Blocks are allowed at all levels. |
| 26 | * |
| 27 | * There are four kinds of entry: |
| 28 | * |
| 29 | * 1. Absent: 0 |
| 30 | * 2. Page, at level 0: <page-aligned address> | <attrs> | 0x3 |
| 31 | * 3. Block, at level 2 or 1: <block-aligned address> | <attrs> | 0x1 |
| 32 | * 4. Subtable, at level 2 or 1: <subtable address> | 0x3 |
| 33 | * |
| 34 | * <attrs> are always 0 for now. |
| 35 | */ |
| 36 | |
| 37 | pte_t arch_mm_absent_pte(int level) |
| 38 | { |
| 39 | (void)level; |
| 40 | return 0; |
| 41 | } |
| 42 | |
| 43 | pte_t arch_mm_table_pte(int level, paddr_t pa) |
| 44 | { |
| 45 | /* This is the same for all levels. */ |
| 46 | (void)level; |
| 47 | return pa_addr(pa) | 0x3; |
| 48 | } |
| 49 | |
| 50 | pte_t arch_mm_block_pte(int level, paddr_t pa, uint64_t attrs) |
| 51 | { |
| 52 | /* Single pages are encoded differently to larger blocks. */ |
Andrew Scull | c66a04d | 2018-12-07 13:41:56 +0000 | [diff] [blame^] | 53 | pte_t pte = pa_addr(pa) | attrs; |
Andrew Scull | 1ba470e | 2018-10-31 15:14:31 +0000 | [diff] [blame] | 54 | if (level == 0) { |
| 55 | pte |= 0x2; |
| 56 | } |
| 57 | return pte; |
| 58 | } |
| 59 | |
| 60 | bool arch_mm_is_block_allowed(int level) |
| 61 | { |
| 62 | /* All levels can have blocks. */ |
| 63 | (void)level; |
| 64 | return true; |
| 65 | } |
| 66 | |
| 67 | bool arch_mm_pte_is_present(pte_t pte, int level) |
| 68 | { |
Andrew Scull | c66a04d | 2018-12-07 13:41:56 +0000 | [diff] [blame^] | 69 | /* TODO: model attributes. */ |
| 70 | return arch_mm_pte_is_valid(pte, level); |
| 71 | } |
| 72 | |
| 73 | bool arch_mm_pte_is_valid(pte_t pte, int level) |
| 74 | { |
Andrew Scull | 1ba470e | 2018-10-31 15:14:31 +0000 | [diff] [blame] | 75 | (void)level; |
| 76 | return (pte & 0x1) != 0; |
| 77 | } |
| 78 | |
| 79 | bool arch_mm_pte_is_table(pte_t pte, int level) |
| 80 | { |
| 81 | /* Level 0 only contains pages so cannot be a table. */ |
| 82 | return level != 0 && (pte & 0x3) == 0x3; |
| 83 | } |
| 84 | |
| 85 | bool arch_mm_pte_is_block(pte_t pte, int level) |
| 86 | { |
| 87 | /* Single pages are encoded differently to larger blocks. */ |
Andrew Scull | c66a04d | 2018-12-07 13:41:56 +0000 | [diff] [blame^] | 88 | return (level == 0 ? (pte & 0x2) != 0 |
| 89 | : arch_mm_pte_is_present(pte, level) && |
| 90 | !arch_mm_pte_is_table(pte, level)); |
Andrew Scull | 1ba470e | 2018-10-31 15:14:31 +0000 | [diff] [blame] | 91 | } |
| 92 | |
| 93 | static uint64_t hf_arch_fake_mm_clear_pte_attrs(pte_t pte) |
| 94 | { |
| 95 | return pte & ~0x3; |
| 96 | } |
| 97 | |
| 98 | paddr_t arch_mm_clear_pa(paddr_t pa) |
| 99 | { |
| 100 | /* This is assumed to round down to the page boundary. */ |
| 101 | return pa_init(hf_arch_fake_mm_clear_pte_attrs(pa_addr(pa)) & |
| 102 | ~((1 << PAGE_BITS) - 1)); |
| 103 | } |
| 104 | |
| 105 | paddr_t arch_mm_block_from_pte(pte_t pte) |
| 106 | { |
| 107 | return pa_init(hf_arch_fake_mm_clear_pte_attrs(pte)); |
| 108 | } |
| 109 | |
| 110 | paddr_t arch_mm_table_from_pte(pte_t pte) |
| 111 | { |
| 112 | return pa_init(hf_arch_fake_mm_clear_pte_attrs(pte)); |
| 113 | } |
| 114 | |
| 115 | uint64_t arch_mm_pte_attrs(pte_t pte) |
| 116 | { |
Andrew Scull | c66a04d | 2018-12-07 13:41:56 +0000 | [diff] [blame^] | 117 | /* Attributes are not modelled fully. */ |
| 118 | return pte & 0x1; |
Andrew Scull | 1ba470e | 2018-10-31 15:14:31 +0000 | [diff] [blame] | 119 | } |
| 120 | |
| 121 | uint64_t arch_mm_combine_table_entry_attrs(uint64_t table_attrs, |
| 122 | uint64_t block_attrs) |
| 123 | { |
| 124 | return table_attrs | block_attrs; |
| 125 | } |
| 126 | |
| 127 | void arch_mm_invalidate_stage1_range(vaddr_t va_begin, vaddr_t va_end) |
| 128 | { |
| 129 | /* There's no modelling of the stage-1 TLB. */ |
| 130 | } |
| 131 | |
| 132 | void arch_mm_invalidate_stage2_range(ipaddr_t va_begin, ipaddr_t va_end) |
| 133 | { |
| 134 | /* There's no modelling of the stage-2 TLB. */ |
| 135 | } |
| 136 | |
| 137 | uint8_t arch_mm_max_level(int mode) |
| 138 | { |
| 139 | /* All modes have 3 levels in the page table. */ |
| 140 | (void)mode; |
| 141 | return 2; |
| 142 | } |
| 143 | |
| 144 | uint8_t arch_mm_root_table_count(int mode) |
| 145 | { |
| 146 | /* Stage 1 has no concatenated tables but stage 2 has 4 of them. */ |
| 147 | return (mode & MM_MODE_STAGE1) ? 1 : 4; |
| 148 | } |
| 149 | |
| 150 | uint64_t arch_mm_mode_to_attrs(int mode) |
| 151 | { |
Andrew Scull | c66a04d | 2018-12-07 13:41:56 +0000 | [diff] [blame^] | 152 | /* Attributes are not modelled fully. */ |
| 153 | return mode & MM_MODE_INVALID ? 0 : 0x1; |
Andrew Scull | 1ba470e | 2018-10-31 15:14:31 +0000 | [diff] [blame] | 154 | } |
| 155 | |
| 156 | bool arch_mm_init(paddr_t table, bool first) |
| 157 | { |
| 158 | /* No initialization required. */ |
| 159 | (void)table; |
| 160 | (void)first; |
| 161 | return true; |
| 162 | } |