Rev 3596 | Rev 4377 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
| Rev 3596 | Rev 3624 | ||
|---|---|---|---|
| Line 110... | Line 110... | ||
| 110 | 110 | ||
| 111 | /** Kernel address space. */ |
111 | /** Kernel address space. */ |
| 112 | as_t *AS_KERNEL = NULL; |
112 | as_t *AS_KERNEL = NULL; |
| 113 | 113 | ||
| 114 | static int area_flags_to_page_flags(int); |
114 | static int area_flags_to_page_flags(int); |
| 115 | static as_area_t *find_area_and_lock(as_t *, uintptr_t); |
- | |
| 116 | static bool check_area_conflicts(as_t *, uintptr_t, size_t, as_area_t *); |
115 | static bool check_area_conflicts(as_t *, uintptr_t, size_t, as_area_t *); |
| 117 | static void sh_info_remove_reference(share_info_t *); |
116 | static void sh_info_remove_reference(share_info_t *); |
| 118 | 117 | ||
| 119 | static int as_constructor(void *obj, int flags) |
118 | static int as_constructor(void *obj, int flags) |
| 120 | { |
119 | { |
| Line 1105... | Line 1104... | ||
| 1105 | spinlock_unlock(&asidlock); |
1104 | spinlock_unlock(&asidlock); |
| 1106 | 1105 | ||
| 1107 | AS = new_as; |
1106 | AS = new_as; |
| 1108 | } |
1107 | } |
| 1109 | 1108 | ||
| 1110 | #ifdef CONFIG_UDEBUG |
- | |
| 1111 | - | ||
| 1112 | /** Write directly into a page, bypassing area flags. |
- | |
| 1113 | * |
- | |
| 1114 | * This allows a debugger to write into a page that is mapped read-only |
- | |
| 1115 | * (such as the text segment). Naturally, this can only be done if the |
- | |
| 1116 | * correspoinding area is private (not shared) and anonymous. |
- | |
| 1117 | * |
- | |
| 1118 | * If this is not the case, this function calls as_area_make_writeable() |
- | |
| 1119 | * first. |
- | |
| 1120 | */ |
- | |
| 1121 | static int debug_write_inside_page(uintptr_t va, void *data, size_t n) |
- | |
| 1122 | { |
- | |
| 1123 | uintptr_t page; |
- | |
| 1124 | pte_t *pte; |
- | |
| 1125 | as_area_t *area; |
- | |
| 1126 | uintptr_t frame; |
- | |
| 1127 | ipl_t ipl; |
- | |
| 1128 | int rc; |
- | |
| 1129 | - | ||
| 1130 | page = ALIGN_DOWN(va, PAGE_SIZE); |
- | |
| 1131 | ASSERT(ALIGN_DOWN(va + n - 1, PAGE_SIZE) == page); |
- | |
| 1132 | - | ||
| 1133 | restart: |
- | |
| 1134 | mutex_lock(&AS->lock); |
- | |
| 1135 | ipl = interrupts_disable(); |
- | |
| 1136 | area = find_area_and_lock(AS, page); |
- | |
| 1137 | if (area->backend != &anon_backend || area->sh_info != NULL) { |
- | |
| 1138 | mutex_unlock(&area->lock); |
- | |
| 1139 | mutex_unlock(&AS->lock); |
- | |
| 1140 | interrupts_restore(ipl); |
- | |
| 1141 | - | ||
| 1142 | rc = as_area_make_writeable(area->base); |
- | |
| 1143 | if (rc != 0) return rc; |
- | |
| 1144 | - | ||
| 1145 | goto restart; |
- | |
| 1146 | } |
- | |
| 1147 | - | ||
| 1148 | pte = page_mapping_find(AS, page); |
- | |
| 1149 | if (! (pte && PTE_VALID(pte) && PTE_PRESENT(pte)) ) { |
- | |
| 1150 | mutex_unlock(&area->lock); |
- | |
| 1151 | mutex_unlock(&AS->lock); |
- | |
| 1152 | interrupts_restore(ipl); |
- | |
| 1153 | - | ||
| 1154 | rc = as_page_fault(page, PF_ACCESS_WRITE, NULL); |
- | |
| 1155 | if (rc == AS_PF_FAULT) return EINVAL; |
- | |
| 1156 | - | ||
| 1157 | goto restart; |
- | |
| 1158 | } |
- | |
| 1159 | - | ||
| 1160 | frame = PTE_GET_FRAME(pte); |
- | |
| 1161 | memcpy((void *)(PA2KA(frame) + (va - page)), data, n); |
- | |
| 1162 | - | ||
| 1163 | mutex_unlock(&area->lock); |
- | |
| 1164 | mutex_unlock(&AS->lock); |
- | |
| 1165 | interrupts_restore(ipl); |
- | |
| 1166 | - | ||
| 1167 | return EOK; |
- | |
| 1168 | } |
- | |
| 1169 | - | ||
| 1170 | /** Write data bypassing area flags. |
- | |
| 1171 | * |
- | |
| 1172 | * See debug_write_inside_page(). |
- | |
| 1173 | */ |
- | |
| 1174 | int as_debug_write(uintptr_t va, void *data, size_t n) |
- | |
| 1175 | { |
- | |
| 1176 | size_t now; |
- | |
| 1177 | int rc; |
- | |
| 1178 | - | ||
| 1179 | while (n > 0) { |
- | |
| 1180 | /* Number of bytes until the end of page */ |
- | |
| 1181 | now = ALIGN_DOWN(va, PAGE_SIZE) + PAGE_SIZE - va; |
- | |
| 1182 | if (now > n) now = n; |
- | |
| 1183 | - | ||
| 1184 | rc = debug_write_inside_page(va, data, now); |
- | |
| 1185 | if (rc != EOK) return rc; |
- | |
| 1186 | - | ||
| 1187 | va += now; |
- | |
| 1188 | data += now; |
- | |
| 1189 | n -= now; |
- | |
| 1190 | } |
- | |
| 1191 | - | ||
| 1192 | return EOK; |
- | |
| 1193 | } |
- | |
| 1194 | - | ||
| 1195 | /** Make sure area is private and anonymous. |
- | |
| 1196 | * |
- | |
| 1197 | * Not atomic atm. |
- | |
| 1198 | * @param address Virtual address in AS. |
- | |
| 1199 | */ |
- | |
| 1200 | int as_area_make_writeable(uintptr_t address) |
- | |
| 1201 | { |
- | |
| 1202 | ipl_t ipl; |
- | |
| 1203 | as_area_t *area; |
- | |
| 1204 | uintptr_t base, page; |
- | |
| 1205 | uintptr_t old_frame, frame; |
- | |
| 1206 | size_t size; |
- | |
| 1207 | int flags; |
- | |
| 1208 | int page_flags; |
- | |
| 1209 | pte_t *pte; |
- | |
| 1210 | int rc; |
- | |
| 1211 | uintptr_t *pagemap; |
- | |
| 1212 | - | ||
| 1213 | ipl = interrupts_disable(); |
- | |
| 1214 | mutex_lock(&AS->lock); |
- | |
| 1215 | area = find_area_and_lock(AS, address); |
- | |
| 1216 | if (!area) { |
- | |
| 1217 | /* |
- | |
| 1218 | * Could not find the address space area. |
- | |
| 1219 | */ |
- | |
| 1220 | mutex_unlock(&AS->lock); |
- | |
| 1221 | interrupts_restore(ipl); |
- | |
| 1222 | return ENOENT; |
- | |
| 1223 | } |
- | |
| 1224 | - | ||
| 1225 | if (area->backend == &anon_backend && !area->sh_info) { |
- | |
| 1226 | /* Nothing to do */ |
- | |
| 1227 | mutex_unlock(&area->lock); |
- | |
| 1228 | mutex_unlock(&AS->lock); |
- | |
| 1229 | interrupts_restore(ipl); |
- | |
| 1230 | return EOK; |
- | |
| 1231 | } |
- | |
| 1232 | - | ||
| 1233 | base = area->base; |
- | |
| 1234 | size = area->pages * PAGE_SIZE; |
- | |
| 1235 | flags = area->flags; |
- | |
| 1236 | page_flags = as_area_get_flags(area); |
- | |
| 1237 | - | ||
| 1238 | pagemap = malloc(area->pages * sizeof(uintptr_t), 0); |
- | |
| 1239 | page_table_lock(AS, false); |
- | |
| 1240 | - | ||
| 1241 | for (page = base; page < base + size; page += PAGE_SIZE) { |
- | |
| 1242 | pte = page_mapping_find(AS, page); |
- | |
| 1243 | if (!pte || !PTE_PRESENT(pte) || !PTE_READABLE(pte)) { |
- | |
| 1244 | /* Fetch the missing page */ |
- | |
| 1245 | if (!area->backend || !area->backend->page_fault) { |
- | |
| 1246 | page_table_unlock(AS, false); |
- | |
| 1247 | mutex_unlock(&area->lock); |
- | |
| 1248 | mutex_unlock(&AS->lock); |
- | |
| 1249 | interrupts_restore(ipl); |
- | |
| 1250 | return EINVAL; |
- | |
| 1251 | } |
- | |
| 1252 | if (area->backend->page_fault(area, page, PF_ACCESS_READ) != AS_PF_OK) { |
- | |
| 1253 | page_table_unlock(AS, false); |
- | |
| 1254 | mutex_unlock(&area->lock); |
- | |
| 1255 | mutex_unlock(&AS->lock); |
- | |
| 1256 | interrupts_restore(ipl); |
- | |
| 1257 | return EINVAL; |
- | |
| 1258 | } |
- | |
| 1259 | } |
- | |
| 1260 | ASSERT(PTE_VALID(pte)); |
- | |
| 1261 | - | ||
| 1262 | old_frame = PTE_GET_FRAME(pte); |
- | |
| 1263 | - | ||
| 1264 | frame = (uintptr_t)frame_alloc(ONE_FRAME, 0); |
- | |
| 1265 | memcpy((void *) PA2KA(frame), (void *)PA2KA(old_frame), |
- | |
| 1266 | FRAME_SIZE); |
- | |
| 1267 | - | ||
| 1268 | pagemap[(page - base) / PAGE_SIZE] = frame; |
- | |
| 1269 | } |
- | |
| 1270 | - | ||
| 1271 | page_table_unlock(AS, false); |
- | |
| 1272 | mutex_unlock(&area->lock); |
- | |
| 1273 | mutex_unlock(&AS->lock); |
- | |
| 1274 | interrupts_restore(ipl); |
- | |
| 1275 | - | ||
| 1276 | rc = as_area_destroy(AS, address); |
- | |
| 1277 | if (rc < 0) { |
- | |
| 1278 | free(pagemap); |
- | |
| 1279 | return rc; |
- | |
| 1280 | } |
- | |
| 1281 | - | ||
| 1282 | area = as_area_create(AS, flags, size, base, AS_AREA_ATTR_PARTIAL, |
- | |
| 1283 | &anon_backend, NULL); |
- | |
| 1284 | if (area == NULL) { |
- | |
| 1285 | free(pagemap); |
- | |
| 1286 | return rc; |
- | |
| 1287 | } |
- | |
| 1288 | - | ||
| 1289 | mutex_lock(&AS->lock); |
- | |
| 1290 | mutex_lock(&area->lock); |
- | |
| 1291 | page_table_lock(AS, false); |
- | |
| 1292 | for (page = base; page < base + size; page += PAGE_SIZE) { |
- | |
| 1293 | frame = pagemap[(page - base) / PAGE_SIZE]; |
- | |
| 1294 | - | ||
| 1295 | page_mapping_insert(AS, page, frame, page_flags); |
- | |
| 1296 | if (!used_space_insert(area, page, 1)) |
- | |
| 1297 | panic("Could not insert used space.\n"); |
- | |
| 1298 | } |
- | |
| 1299 | - | ||
| 1300 | page_table_unlock(AS, false); |
- | |
| 1301 | - | ||
| 1302 | area->attributes &= ~AS_AREA_ATTR_PARTIAL; |
- | |
| 1303 | - | ||
| 1304 | mutex_unlock(&area->lock); |
- | |
| 1305 | mutex_unlock(&AS->lock); |
- | |
| 1306 | - | ||
| 1307 | free(pagemap); |
- | |
| 1308 | - | ||
| 1309 | return EOK; |
- | |
| 1310 | } |
- | |
| 1311 | - | ||
| 1312 | #endif /* defined(CONFIG_UDEBUG) */ |
- | |
| 1313 | - | ||
| 1314 | /** Convert address space area flags to page flags. |
1109 | /** Convert address space area flags to page flags. |
| 1315 | * |
1110 | * |
| 1316 | * @param aflags Flags of some address space area. |
1111 | * @param aflags Flags of some address space area. |
| 1317 | * |
1112 | * |
| 1318 | * @return Flags to be passed to page_mapping_insert(). |
1113 | * @return Flags to be passed to page_mapping_insert(). |