/* * Copyright (c) 2004-2015 Cadence Design Systems Inc. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #if XCHAL_HAVE_MPU #include #include #include #include /* * General notes: * Wherever an address is represented as an unsigned, it has only the 27 most significant bits. This is how * the addresses are represented in the MPU. It has the benefit that we don't need to worry about overflow. * * The asserts in the code are ignored unless an assert handler is set (as it is during testing). * * If an assert handler is set, then the MPU map is checked for correctness after every update. * * On some configs (actually all configs right now), the MPU entries must be aligned to the background map. * The constant: XCHAL_MPU_ALIGN_REQ indicates if alignment is required: * * The rules for a valid map are: * * 1) The entries' vStartAddress fields must always be in non-descending order. * 2) The entries' memoryType and accessRights must contain valid values * * If XCHAL_MPU_ALIGN_REQ == 1 then the following additional rules are enforced: * 3) If entry0's Virtual Address Start field is nonzero, then that field must equal one of the * Background Map's Virtual Address Start field values if software ever intends to assert entry0's MPUENB bit. * 4) If entryN's MPUENB bit will ever be negated while at the same time entryN+1's MPUENB bit is asserted, * then entryN+1's Virtual Address Start field must equal one of the Background Map's Virtual Address Start field values. * * The internal function are first, and the external 'xthal_' functions are at the end. * */ extern void (*_xthal_assert_handler)(); extern void xthal_write_map_raw(const xthal_MPU_entry* fg, unsigned int n); extern void xthal_read_map_raw(const xthal_MPU_entry* fg); extern xthal_MPU_entry _xthal_get_entry(const xthal_MPU_entry* fg, const xthal_MPU_entry* bg, unsigned int addr, int* infgmap); #define MPU_ADDRESS_MASK (0xffffffff << XCHAL_MPU_ALIGN_BITS) #define MPU_ALIGNMENT_MASK (0xffffffff - MPU_ADDRESS_MASK) #define MPU_VSTART_CORRECTNESS_MASK ((0x1 << (XCHAL_MPU_ALIGN_BITS)) - 1) // Set this to 1 for more extensive internal checking / 0 for production #define MPU_DEVELOPMENT_MODE 0 #if XCHAL_MPU_ALIGN_REQ #define XCHAL_MPU_WORST_CASE_ENTRIES_FOR_REGION 3 #else #define XCHAL_MPU_WORST_CASE_ENTRIES_FOR_REGION 2 #endif /* * At some point it is faster to commit/invalidate the entire cache rather than going on line at a time. * If a region is bigger than 'CACHE_REGION_THRESHOLD' we operate on the entire cache. */ #if XCHAL_DCACHE_LINESIZE #define CACHE_REGION_THRESHOLD (32 * XCHAL_DCACHE_LINESIZE / XCHAL_MPU_ALIGN) #else #define CACHE_REGION_THRESHOLD 0 #endif /* * Normally these functions are no-ops, but the MPU test harness sets an assert handler to detect any inconsistencies in MPU * entries or any other unexpected internal condition. */ #if MPU_DEVELOPMENT_MODE static void my_assert(int arg) { if (_xthal_assert_handler && !arg) _xthal_assert_handler(); } static void assert_map_valid() { if (_xthal_assert_handler) { xthal_MPU_entry fg[XCHAL_MPU_ENTRIES]; xthal_read_map(fg); if (xthal_check_map(fg, XCHAL_MPU_ENTRIES)) _xthal_assert_handler(); } } static void assert_attributes_equivalent(unsigned addr, const xthal_MPU_entry* initial, const xthal_MPU_entry* fg, const xthal_MPU_entry* bg) { xthal_MPU_entry e1 = _xthal_get_entry(initial, bg, addr, 0); xthal_MPU_entry e2 = _xthal_get_entry(fg, bg, addr, 0); my_assert((XTHAL_MPU_ENTRY_GET_ACCESS(e1) == XTHAL_MPU_ENTRY_GET_ACCESS(e2)) && (XTHAL_MPU_ENTRY_GET_MEMORY_TYPE(e1) == XTHAL_MPU_ENTRY_GET_MEMORY_TYPE(e2))); } static void assert_maps_equivalent(const xthal_MPU_entry* initial, const xthal_MPU_entry* fg, const xthal_MPU_entry* bg) { /* this function checks that for every address the MPU entries 'initial' result in the same attributes as the entries in 'fg'. * We only need to check at the addresses that appear in 'initial', 'fg', or 'bg'. */ int i; for (i = 0; i < XCHAL_MPU_ENTRIES; i++) { assert_attributes_equivalent(XTHAL_MPU_ENTRY_GET_VSTARTADDR(initial[i]), initial, fg, bg); assert_attributes_equivalent(XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[i]), initial, fg, bg); } for (i = 0; i < XCHAL_MPU_BACKGROUND_ENTRIES; i++) assert_attributes_equivalent(XTHAL_MPU_ENTRY_GET_VSTARTADDR(bg[i]), initial, fg, bg); } #else #define my_assert(x) #define assert_map_valid(x) #endif #if 0 // These functions aren't used, but am leaving the definitions in place // for possible future use. static inline unsigned read_mpucfg() { unsigned long tmp; __asm__ __volatile__("rsr.mpucfg %0\n\t" : "=a" (tmp)); return tmp; } static inline unsigned read_mpuenb() { unsigned long tmp; __asm__ __volatile__("rsr.mpuenb %0\n\t" : "=a" (tmp)); return tmp; } /* This function writes the enable for the MPU entries */ static inline void write_mpuenb(unsigned v) { __asm__ __volatile__("wsr.mpuenb %0\n\t" : : "a" (v)); } #endif static inline void isync() { __asm__ __volatile__("isync\n\t"); } /* This function writes the cache disable register which * disables the cache by 512MB registers to save power*/ static inline void write_cacheadrdis(unsigned v) { __asm__ __volatile__("wsr.cacheadrdis %0\n\t" : : "a" (v)); } inline static int is_cacheable(unsigned int mt); #if 0 static inline void read_map_entry(unsigned en_num, xthal_MPU_entry* en) { unsigned as; unsigned at0; unsigned at1; as = en_num; __asm__ __volatile__("RPTLB0 %0, %1\n\t" : "+a" (at0) : "a" (as)); __asm__ __volatile__("RPTLB1 %0, %1\n\t" : "+a" (at1) : "a" (as)); en->as = at0; en->at = at1; } #endif inline static int is_cacheable(unsigned int mt) { return (0x180 & mt) || ((mt & 0x18) == 0x10) || ((mt & 0x30) == 0x30); } inline static int is_writeback(unsigned int mt) { return (((0x180 & mt) && (mt & 0x11)) || ((((mt & 0x18) == 0x10) || ((mt & 0x30) == 0x30)) & 0x1)); } inline static int is_device(unsigned int mt) { return ((mt & 0x1f0) == 0); } inline static int is_kernel_readable(int accessRights) { switch (accessRights) { case XTHAL_AR_R: case XTHAL_AR_Rr: case XTHAL_AR_RX: case XTHAL_AR_RXrx: case XTHAL_AR_RW: case XTHAL_AR_RWX: case XTHAL_AR_RWr: case XTHAL_AR_RWrw: case XTHAL_AR_RWrwx: case XTHAL_AR_RWXrx: case XTHAL_AR_RWXrwx: return 1; case XTHAL_AR_NONE: case XTHAL_AR_Ww: return 0; default: return XTHAL_BAD_ACCESS_RIGHTS; } } inline static int is_kernel_writeable(int accessRights) { switch (accessRights) { case XTHAL_AR_RW: case XTHAL_AR_RWX: case XTHAL_AR_RWr: case XTHAL_AR_RWrw: case XTHAL_AR_RWrwx: case XTHAL_AR_RWXrx: case XTHAL_AR_RWXrwx: case XTHAL_AR_Ww: return 1; case XTHAL_AR_NONE: case XTHAL_AR_R: case XTHAL_AR_Rr: case XTHAL_AR_RX: case XTHAL_AR_RXrx: return 0; default: return XTHAL_BAD_ACCESS_RIGHTS; } } inline static int is_kernel_executable(int accessRights) { switch (accessRights) { case XTHAL_AR_RX: case XTHAL_AR_RXrx: case XTHAL_AR_RWX: case XTHAL_AR_RWXrx: case XTHAL_AR_RWXrwx: return 1; case XTHAL_AR_NONE: case XTHAL_AR_Ww: case XTHAL_AR_R: case XTHAL_AR_Rr: case XTHAL_AR_RW: case XTHAL_AR_RWr: case XTHAL_AR_RWrw: case XTHAL_AR_RWrwx: return 0; default: return XTHAL_BAD_ACCESS_RIGHTS; } } inline static int is_user_readable(int accessRights) { switch (accessRights) { case XTHAL_AR_Rr: case XTHAL_AR_RXrx: case XTHAL_AR_RWr: case XTHAL_AR_RWrw: case XTHAL_AR_RWrwx: case XTHAL_AR_RWXrx: case XTHAL_AR_RWXrwx: return 1; case XTHAL_AR_R: case XTHAL_AR_RX: case XTHAL_AR_RW: case XTHAL_AR_RWX: case XTHAL_AR_NONE: case XTHAL_AR_Ww: return 0; default: return XTHAL_BAD_ACCESS_RIGHTS; } } inline static int is_user_writeable(int accessRights) { switch (accessRights) { case XTHAL_AR_Ww: case XTHAL_AR_RWrw: case XTHAL_AR_RWrwx: case XTHAL_AR_RWXrwx: return 1; case XTHAL_AR_NONE: case XTHAL_AR_R: case XTHAL_AR_Rr: case XTHAL_AR_RX: case XTHAL_AR_RXrx: case XTHAL_AR_RW: case XTHAL_AR_RWX: case XTHAL_AR_RWr: case XTHAL_AR_RWXrx: return 0; default: return XTHAL_BAD_ACCESS_RIGHTS; } } inline static int is_user_executable(int accessRights) { switch (accessRights) { case XTHAL_AR_RXrx: case XTHAL_AR_RWrwx: case XTHAL_AR_RWXrx: case XTHAL_AR_RWXrwx: return 1; case XTHAL_AR_RW: case XTHAL_AR_RWX: case XTHAL_AR_RWr: case XTHAL_AR_RWrw: case XTHAL_AR_R: case XTHAL_AR_Rr: case XTHAL_AR_RX: case XTHAL_AR_NONE: case XTHAL_AR_Ww: return 0; default: return XTHAL_BAD_ACCESS_RIGHTS; } } /* This function returns the map entry that is used for the address 'addr' (27msb). * */ #if defined(__SPLIT__mpu_basic) xthal_MPU_entry _xthal_get_entry(const xthal_MPU_entry* fg, const xthal_MPU_entry* bg, unsigned int addr, int* infgmap) { int i; for (i = XCHAL_MPU_ENTRIES - 1; i >= 0; i--) { if (XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[i]) <= addr) { if (XTHAL_MPU_ENTRY_GET_VALID(fg[i])) { if (infgmap) *infgmap = 1; return fg[i]; } else break; } } for (i = XCHAL_MPU_BACKGROUND_ENTRIES - 1; i >= 0; i--) { if (XTHAL_MPU_ENTRY_GET_VSTARTADDR(bg[i]) <= addr) { if (infgmap) *infgmap = 0; return bg[i]; } } return bg[0]; // never reached ... just to get rid of compilation warning } /* returns true if the supplied address (27msb) is in the background map. */ int _xthal_in_bgmap(unsigned int address, const xthal_MPU_entry* bg) { int i; for (i = 0; i < XCHAL_MPU_BACKGROUND_ENTRIES; i++) if (XTHAL_MPU_ENTRY_GET_VSTARTADDR(bg[i]) == address) return 1; return 0; } #endif #if defined(__SPLIT__mpu_attributes) /* This function updates the map entry as well as internal duplicate of the map * state in fg. The assumption is that reading map entries could be somewhat * expensive in some situations so we are keeping a copy of the map in memory when * doing extensive map manipulations. */ static void write_map_entry(xthal_MPU_entry* fg, unsigned en_num, xthal_MPU_entry en) { en.at = (en.at & 0xffffffe0) | (en_num & 0x1f); xthal_mpu_set_entry(en); assert_map_valid(); fg[en_num] = en; } static void move_map_down(xthal_MPU_entry* fg, int dup, int idx) { /* moves the map entry list down one (leaving duplicate entries at idx and idx+1. This function assumes that the last * entry is invalid ... call MUST check this */ unsigned int i; for (i = dup; i > idx; i--) { write_map_entry(fg, i, fg[i - 1]); } } static void move_map_up(xthal_MPU_entry* fg, int dup, int idx) { /* moves the map entry list up one (leaving duplicate entries at idx and idx-1, removing the entry at dup */ int i; for (i = dup; i < idx - 1; i++) { write_map_entry(fg, i, fg[i + 1]); } } static int bubble_free_to_ip(xthal_MPU_entry* fg, int ip, int required) { /* This function shuffles the entries in the MPU to get at least 'required' free entries at * the insertion point 'ip'. This function returns the new insertion point (after all the shuffling). */ int i; int rv = ip; if (required < 1) return ip; my_assert(required <= XCHAL_MPU_ENTRIES); /* first we search for duplicate or unused entries at an index less than 'ip'. We start looking at ip-1 * (rather than 0) to minimize the number of shuffles required. */ for (i = ip - 2; i >= 0 && required;) { if (XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[i]) == XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[i + 1])) { move_map_up(fg, i, ip); rv--; required--; } i--; } // if there are any invalid entries at top of the map, we can remove them to make space while (required) { if (!XTHAL_MPU_ENTRY_GET_VALID(fg[0])) { move_map_up(fg, 0, ip); rv--; required--; } else break; } /* If there are not enough unneeded entries at indexes less than ip, then we search at indexes > ip. * We start the search at ip+1 and move down, again to minimize the number of shuffles required. */ for (i = ip + 1; i < XCHAL_MPU_ENTRIES && required;) { if (XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[i]) == XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[i - 1])) { move_map_down(fg, i, ip); required--; } else i++; } my_assert(required == 0); return rv; } /* This function removes 'inaccessible' entries from the MPU map (those that are hidden by previous entries * in the map). It leaves any entries that match background entries in place. */ static void remove_inaccessible_entries(xthal_MPU_entry* fg, const xthal_MPU_entry* bg) { int i; for (i = 1; i < XCHAL_MPU_ENTRIES; i++) { if (((XTHAL_MPU_ENTRY_GET_VALID(fg[i]) == XTHAL_MPU_ENTRY_GET_VALID(fg[i - 1])) && (XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[i]) > XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[i - 1])) && (XTHAL_MPU_ENTRY_GET_MEMORY_TYPE(fg[i]) == XTHAL_MPU_ENTRY_GET_MEMORY_TYPE(fg[i - 1])) && (XTHAL_MPU_ENTRY_GET_ACCESS(fg[i]) == XTHAL_MPU_ENTRY_GET_ACCESS(fg[i - 1])) && /* we can only remove the background map entry if either background alignment is not required, or * if the previous entry is enabled. */ (!_xthal_in_bgmap(XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[i]), bg))) || ((!XTHAL_MPU_ENTRY_GET_VALID(fg[i]) && (!XTHAL_MPU_ENTRY_GET_VALID(fg[i - 1])) && (!_xthal_in_bgmap(XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[i]), bg))))) { write_map_entry(fg, i, fg[i - 1]); } } } /* This function takes bitwise or'd combination of access rights and memory type, and extracts * the access rights. It returns the access rights, or -1. */ static int encode_access_rights(int cattr) { cattr = cattr & 0xF; if ((cattr) > 0 && (cattr < 4)) return -1; else return cattr; } /* * returns the largest value rv, such that for every index < rv, * entrys[index].vStartAddress < first. * * Assumes an ordered entry array (even disabled entries must be ordered). * value returned is in the range [0, XCHAL_MPU_ENTRIES]. * */ static int find_entry(xthal_MPU_entry* fg, unsigned first) { int i; for (i = XCHAL_MPU_ENTRIES - 1; i >= 0; i--) { if (XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[i]) <= first) return i + 1; } return 0; // if it is less than all existing entries return 0 } /* * This function returns 1 if there is an exact match for first and first+size * so that no manipulations are necessary before safing and updating the attributes * for [first, first+size). The the first and end entries * must be valid, as well as all the entries in between. Otherwise the memory * type might change across the region and we wouldn't be able to safe the caches. * * An alternative would be to require alignment regions in this case, but that seems * more wasteful. */ static int needed_entries_exist(xthal_MPU_entry* fg, unsigned first, unsigned last) { int i; for (i = 0; i < XCHAL_MPU_ENTRIES; i++) { if (XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[i]) == first) { int j; /* special case ... is last at the end of the address space * ... if so there is no end entry needed. */ if (last == 0xFFFFFFFF) { int k; for (k = i; k < XCHAL_MPU_ENTRIES; k++) if (!XTHAL_MPU_ENTRY_GET_VALID(fg[k])) return 0; return 1; } /* otherwise search for the end entry */ for (j = i; j < XCHAL_MPU_ENTRIES; j++) if (last == XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[j])) { int k; for (k = i; k <= j; k++) if (!XTHAL_MPU_ENTRY_GET_VALID(fg[k])) return 0; return 1; } return 0; } } return 0; } /* This function computes the number of MPU entries that are available for use in creating a new * region. */ static int number_available(xthal_MPU_entry* fg) { int i; int rv = 0; int valid_seen = 0; for (i = 0; i < XCHAL_MPU_ENTRIES; i++) { if (!valid_seen) { if (XTHAL_MPU_ENTRY_GET_VALID(fg[i])) valid_seen = 1; else { rv++; continue; } } else { if (i && (XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[i]) == XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[i - 1]))) rv++; } } return rv; } /* * This function returns index of the background map entry that maps the address 'first' if there are no * enabled/applicable foreground map entries. */ static int get_bg_map_index(const xthal_MPU_entry* bg, unsigned first) { int i; for (i = XCHAL_MPU_BACKGROUND_ENTRIES - 1; i >= 0; i--) if (first > XTHAL_MPU_ENTRY_GET_VSTARTADDR(bg[i])) return i; return 0; } inline static unsigned int covert_to_writethru_memtype(unsigned int wb_memtype) { unsigned int prefix = wb_memtype & 0x1f0; if (prefix == 0x10) return wb_memtype & 0xfffffffe; else return wb_memtype & 0xffffffee; } /* * This function takes the region pointed to by ip, and makes it safe from the aspect of cache coherency, before * changing the memory type and possibly corrupting the cache. If wb is 0, then that indicates * that we should ignore uncommitted entries. If the inv argument is 0 that indicates that we shouldn't invalidate * the cache before switching to bypass. */ static void safe_region(xthal_MPU_entry* fg, int ip, unsigned end_of_segment, int memoryType, int wb, int inv, unsigned int* post_inv_all) { unsigned length = end_of_segment - XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[ip]); // initially keep length 27msb to avoid possibility of overflow if (!length) return; // if the region is empty, there is no need to safe it int cmemType = XTHAL_MPU_ENTRY_GET_MEMORY_TYPE(fg[ip]); if (memoryType == cmemType) return; // not changing memory types ... we don't need to do anything int mt_is_wb = is_writeback(memoryType); int mt_is_ch = is_cacheable(memoryType); // nothing needs to be done in these cases if (mt_is_wb || (!wb && (!inv || mt_is_ch))) return; int need_flush = wb && (is_writeback(cmemType) && !is_writeback(memoryType)); int need_invalidate = inv && (is_cacheable(cmemType) && !is_cacheable(memoryType)); void* addr = (void*) XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[ip]); int write_by_region = length < CACHE_REGION_THRESHOLD; if (need_flush) { XTHAL_MPU_ENTRY_SET_MEMORY_TYPE(fg[ip], covert_to_writethru_memtype(XTHAL_MPU_ENTRY_GET_MEMORY_TYPE(fg[ip]))); // If the AR == NONE, the writing back the cache may generate exception. Temporarily open up the protections ... // ... if (XTHAL_MPU_ENTRY_GET_ACCESS(fg[ip]) == XTHAL_AR_NONE) XTHAL_MPU_ENTRY_SET_ACCESS(fg[ip], XTHAL_AR_RWXrwx); // bit 0 determines if it wb/wt write_map_entry(fg, ip, fg[ip]); if (!write_by_region) { /* unfortunately there is no straight forward way to avoid the possibility of doing * multiple xthal_dcache_all_writeback() calls during a region update. The reason for this * is that: * * 1) The writeback must be done before the memory type is changed to non-cacheable before * an invalidate (see below) * * 2) it isn't possible to reorganize the loop so that all the writebacks are done before * any of the invalidates because if part of the region of interest is (initially) mapped * by the background map, then a single foreground entry is reused to 'safe' across * each background map entry that is overlapped. */ xthal_dcache_all_writeback(); } else if (length) xthal_dcache_region_writeback(addr, length); } if (need_invalidate) { XTHAL_MPU_ENTRY_SET_MEMORY_TYPE(fg[ip], XTHAL_ENCODE_MEMORY_TYPE(XCHAL_CA_BYPASS)); write_map_entry(fg, ip, fg[ip]); /* only need to call all_invalidate once ... check * if it has already been done. */ if (!*post_inv_all) { if (!write_by_region) { *post_inv_all = 1; } else if (length) { xthal_icache_region_invalidate(addr, length); xthal_dcache_region_writeback_inv(addr, length); } } } } static unsigned max(unsigned a, unsigned b, unsigned c) { if (a > b && a > c) return a; else if (b > c) return b; else return c; } /* This function returns the next address to commit which will be the greatest of the following: * 1) The start of the region we are creating * 2) The vStartAddress of the previous entry * 3) The background map entry that precedes the current address (last address committed). */ static unsigned next_address_to_commit(xthal_MPU_entry* fg, const xthal_MPU_entry* bg, unsigned first, int current_index) { unsigned current = XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[current_index]); return max(first, XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[current_index - 1]), XTHAL_MPU_ENTRY_GET_VSTARTADDR(bg[get_bg_map_index(bg, current)])); } /* * This function does a series of calls to safe_region() to ensure that no data will be corrupted when changing the memory type * of an MPU entry. These calls are made for every entry address in the range[first,end), as well as at any background region boundary * in the range[first,end). In general it is necessary to safe at the background region boundaries, because the memory type could * change at that address. * * This function is written to reuse already needed entries for the background map 'safes' which complicates things somewhat. * * After the calls to safe region are complete, then the entry attributes are updated for every entry in the range [first,end). */ static void safe_and_commit_overlaped_regions(xthal_MPU_entry* fg, const xthal_MPU_entry*bg, unsigned first, unsigned last, int memoryType, int accessRights, int wb, int inv) { int i; unsigned int next; unsigned end_of_segment = last; unsigned post_inv_all = 0; unsigned int cachedisadr; write_cacheadrdis(0); for (i = XCHAL_MPU_ENTRIES - 1; i >= 0; i--) if (XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[i]) < last) { // first we want to commit the first entry safe_region(fg, i, end_of_segment, memoryType, wb, inv, &post_inv_all); end_of_segment = XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[i]); do { next = next_address_to_commit(fg, bg, first, i); if (next == XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[i - 1])) i--; XTHAL_MPU_ENTRY_SET_VSTARTADDR(fg[i], next); safe_region(fg, i, last, memoryType, wb, inv, &post_inv_all); end_of_segment = XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[i]); } while (next > first); if (post_inv_all) { xthal_icache_all_invalidate(); xthal_dcache_all_writeback_inv(); } for (; i < XCHAL_MPU_ENTRIES && XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[i]) < last; i++) { XTHAL_MPU_ENTRY_SET_MEMORY_TYPE(fg[i], memoryType); XTHAL_MPU_ENTRY_SET_ACCESS(fg[i], accessRights); XTHAL_MPU_ENTRY_SET_VALID(fg[i], 1); write_map_entry(fg, i, fg[i]); } break; } cachedisadr = xthal_calc_cacheadrdis(fg, XCHAL_MPU_ENTRIES); write_cacheadrdis(cachedisadr); } static void handle_invalid_pred(xthal_MPU_entry* fg, const xthal_MPU_entry* bg, unsigned first, int ip) { /* Handle the case where there is an invalid entry immediately preceding the entry we * are creating. If the entries addresses correspond to the same bg map, then we * make the previous entry valid with same attributes as the background map entry. * * The case where an invalid entry exists immediately preceding whose address corresponds to a different * background map entry is handled by create_aligning_entries_if_required(), so nothing is done here. */ /* todo ... optimization opportunity, the following block loops through the background map up to 4 times, * */ if (!ip || XTHAL_MPU_ENTRY_GET_VALID(fg[ip - 1])) return; { int i; unsigned fgipm1_addr = XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[ip - 1]); int first_in_bg_map = 0; int first_bg_map_index = -1; int fgipm1_bg_map_index = -1; #if MPU_DEVELOPMENT_MODE unsigned fgip_addr = XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[ip]); int fgip_bg_map_index = -1; #endif for (i = XCHAL_MPU_BACKGROUND_ENTRIES - 1; i >= 0; i--) { unsigned addr = XTHAL_MPU_ENTRY_GET_VSTARTADDR(bg[i]); if (addr == first) first_in_bg_map = 1; if (addr < fgipm1_addr && fgipm1_bg_map_index == -1) fgipm1_bg_map_index = i; #if MPU_DEVELOPMENT_MODE if (addr < fgip_addr && fgip_bg_map_index == -1) fgip_bg_map_index = i; #endif if (addr < first && first_bg_map_index == -1) first_bg_map_index = i; } if (!first_in_bg_map && (first_bg_map_index == fgipm1_bg_map_index)) { // There should be a subsequent entry that falls in the address range of same // background map entry ... if not, we have a problem because the following // will corrupt the memory map #if MPU_DEVELOPMENT_MODE { my_assert(fgip_bg_map_index == fgipm1_bg_map_index); } #endif xthal_MPU_entry temp = _xthal_get_entry(fg, bg, XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[ip - 1]), 0); XTHAL_MPU_ENTRY_SET_VSTARTADDR(temp, XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[ip - 1])); write_map_entry(fg, ip - 1, temp); } } } /* This function inserts a entry (unless it already exists) with vStartAddress of first. The new entry has * the same accessRights and memoryType as the address first had before the call. * * If 'invalid' is specified, then insert an invalid region if no foreground entry exists for the address 'first'. */ static int insert_entry_if_needed_with_existing_attr(xthal_MPU_entry* fg, const xthal_MPU_entry* bg, unsigned first, int invalid) { int i; int ip; int infg; int found = 0; for (i = XCHAL_MPU_ENTRIES - 1; i >= 0; i--) if (XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[i]) == first) { if (XTHAL_MPU_ENTRY_GET_VALID(fg[i]) || invalid) return XTHAL_SUCCESS; else { found = 1; ip = i; break; } } if (!found) { if (!number_available(fg)) return XTHAL_OUT_OF_ENTRIES; ip = find_entry(fg, first); ip = bubble_free_to_ip(fg, ip, 1); } if (!invalid) handle_invalid_pred(fg, bg, first, ip); xthal_MPU_entry n; memset(&n, 0, sizeof(n)); n = _xthal_get_entry(fg, bg, first, &infg); if (invalid && !infg) // If the entry mapping is currently in the foreground we can't make // the entry invalid without corrupting the attributes of the following entry. XTHAL_MPU_ENTRY_SET_VALID(n, 0); XTHAL_MPU_ENTRY_SET_VSTARTADDR(n,first); write_map_entry(fg, ip, n); return XTHAL_SUCCESS; } static unsigned int smallest_entry_greater_than_equal(xthal_MPU_entry* fg, unsigned x) { int i; for (i = 0; i < XCHAL_MPU_ENTRIES; i++) if (XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[i]) >= x) return XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[i]); return 0; } /* This function creates background map aligning entries if required.*/ static unsigned int create_aligning_entries_if_required(xthal_MPU_entry* fg, const xthal_MPU_entry* bg, unsigned x) { #if XCHAL_MPU_ALIGN_REQ int i; int rv; unsigned next_entry_address = 0; unsigned next_entry_valid = 0; int preceding_bg_entry_index_x = get_bg_map_index(bg, x); unsigned preceding_bg_entry_x_addr = XTHAL_MPU_ENTRY_GET_VSTARTADDR(bg[preceding_bg_entry_index_x]); for (i = XCHAL_MPU_ENTRIES - 1; i >= 0; i--) { if (XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[i]) < x) { if (XTHAL_MPU_ENTRY_GET_VALID(fg[i])) return XTHAL_SUCCESS; // If there is a valid entry immediately before the proposed new entry // ... then no aligning entries are required break; } else { next_entry_address = XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[i]); next_entry_valid = XTHAL_MPU_ENTRY_GET_VALID(fg[i]); } } /* * before creating the aligning entry, we may need to create an entry or entries a higher * addresses to limit the scope of the aligning entry. */ if ((!next_entry_address) || (!next_entry_valid) || (_xthal_in_bgmap(next_entry_address, bg))) { /* in this case, we can just create an invalid entry at the start of the new region because * a valid entry could have an alignment problem. An invalid entry is safe because we know that * the next entry is either invalid, or is on a bg map entry */ if ((rv = insert_entry_if_needed_with_existing_attr(fg, bg, x, 1)) != XTHAL_SUCCESS) { return rv; } } else { unsigned next_bg_entry_index; for (next_bg_entry_index = 0; next_bg_entry_index < XCHAL_MPU_BACKGROUND_ENTRIES; next_bg_entry_index++) if (XTHAL_MPU_ENTRY_GET_VSTARTADDR(bg[next_bg_entry_index]) > x) break; if (next_entry_address == XTHAL_MPU_ENTRY_GET_VSTARTADDR(bg[next_bg_entry_index])) // In this case there is no intervening bg entry // between the new entry x, and the next existing entry so, we don't need any limiting entry // (the existing next_entry serves as the limiting entry) { /* intentionally empty */ } else { // In this case we need to create a valid region at the background entry that immediately precedes // next_entry_address, and then create an invalid entry at the background entry immediately after // x if ((rv = insert_entry_if_needed_with_existing_attr(fg, bg, XTHAL_MPU_ENTRY_GET_VSTARTADDR(_xthal_get_entry(fg, bg, x, 0)), 0)) != XTHAL_SUCCESS) { return rv; } if ((rv = insert_entry_if_needed_with_existing_attr(fg, bg, XTHAL_MPU_ENTRY_GET_VSTARTADDR(_xthal_get_entry(fg, bg, XTHAL_MPU_ENTRY_GET_VSTARTADDR(bg[next_bg_entry_index]), 0)), 1)) != XTHAL_SUCCESS) { return rv; } } } /* now we are finally ready to create the aligning entry.*/ if (!(x == preceding_bg_entry_x_addr)) if ((rv = insert_entry_if_needed_with_existing_attr(fg, bg, preceding_bg_entry_x_addr, 0)) != XTHAL_SUCCESS) { return rv; } return XTHAL_SUCCESS; #else return XTHAL_SUCCESS; #endif } static unsigned start_initial_region(xthal_MPU_entry* fg, const xthal_MPU_entry* bg, unsigned first, unsigned end) { int i; unsigned addr; for (i = XCHAL_MPU_BACKGROUND_ENTRIES - 1; i >= 0; i--) { addr = XTHAL_MPU_ENTRY_GET_VSTARTADDR(bg[i]); if (addr <= first) break; if (addr < end) return addr; } return first; } static int safe_add_region(unsigned first, unsigned last, unsigned accessRights, unsigned memoryType, unsigned writeback, unsigned invalidate) { /* This function sets the memoryType and accessRights on a region of memory. If necessary additional MPU entries * are created so that the attributes of any memory outside the specified region are not changed. * * This function has 2 stages: * 1) The map is updated one entry at a time to create (if necessary) new entries to mark the beginning and end of the * region as well as addition alignment entries if needed. During this stage the map is always correct, and the memoryType * and accessRights for every address remain the same. * 2) The entries inside the update region are then safed for cache consistency (if necessary) and then written with * the new accessRights, and memoryType. * * If the function fails (by running out of available map entries) during stage 1 then everything is still consistent and * it is safe to return an error code. * * If XCHAL_MPU_ALIGN_REQ is provided then extra entries are create if needed * to satisfy these alignment conditions: * * 1) If entry0's Virtual Address Start field is nonzero, then that field must equal one of the Background Map's * Virtual Address Start field values if software ever intends to assert entry0's MPUENB bit. * 2) If entryN's MPUENB bit will ever be negated while at the same time entryN+1's MPUENB bit is * asserted, then entryN+1's Virtual Address Start field must equal one of the Background Map's Virtual Address Start field values. * * Between 0 and 2 available entries will be used by this function. In addition, if XCHAL_MPU_ALIGN_REQ == 1 up to ??? * additional entries will be needed to meet background map alignment requirements. * * This function keeps a copy of the current map in 'fg'. This is kept in sync with contents of the MPU at all times. * */ int rv; xthal_MPU_entry fg[XCHAL_MPU_ENTRIES]; #if MPU_DEVELOPMENT_MODE xthal_MPU_entry on_entry[XCHAL_MPU_ENTRIES]; xthal_read_map(on_entry); #endif xthal_read_map(fg); assert_map_valid(); /* First we check and see if consecutive entries at first, and first + size already exist. * in this important special case we don't need to do anything but safe and update the entries [first, first+size). * */ if (!needed_entries_exist(fg, first, last)) { unsigned x; unsigned pbg; /* * If we are tight on entries, the first step is to remove any redundant entries in the MPU * to make room to ensure that there is room for the new entries we need. * * We need to call it here ... once we have started transforming the map it is too late * (the process involves creating inaccessible entries that could potentially get removed). */ if (number_available(fg) < XCHAL_MPU_WORST_CASE_ENTRIES_FOR_REGION) remove_inaccessible_entries(fg, Xthal_mpu_bgmap); #if MPU_DEVELOPMENT_MODE assert_maps_equivalent(on_entry, fg, Xthal_mpu_bgmap); #endif // First we create foreground entries that 'duplicate' background entries to aide in // maintaining proper alignment. if ((rv = create_aligning_entries_if_required(fg, Xthal_mpu_bgmap, first)) != XTHAL_SUCCESS) return rv; // First we write the terminating entry for our region // 5 cases: // 1) end is at the end of the address space, then we don't need to do anything ... takes 0 entries // 2) There is an existing entry at end ... another nop ... 0 entries // 3) end > than any existing entry ... in this case we just create a new invalid entry at end to mark // end of the region. No problem with alignment ... this takes 1 entry // 4) otherwise if there is a background map boundary between end and x ,the smallest existing entry that is // greater than end, then we first create an equivalent foreground map entry for the background map entry that immediately // precedes x, and then we write an invalid entry for end. Takes 2 entries // 5) otherwise x is in the same background map entry as end, in this case we write a new foreground entry with the existing // attributes at end if (last == 0xFFFFFFFF) { /* the end is the end of the address space ... do nothing */ } else { x = smallest_entry_greater_than_equal(fg, last); if (last == x) { /* another nop */ } else if (last > x) { /* there is no entry that has a start after the new region ends ... we handle this by creating an invalid entry at the end point */ if ((rv = insert_entry_if_needed_with_existing_attr(fg, Xthal_mpu_bgmap, last, 1)) != XTHAL_SUCCESS) { #if MPU_DEVELOPMENT_MODE assert_maps_equivalent(on_entry, fg, Xthal_mpu_bgmap); #endif return rv; } #if MPU_DEVELOPMENT_MODE assert_maps_equivalent(on_entry, fg, Xthal_mpu_bgmap); #endif } else { pbg = XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[get_bg_map_index(Xthal_mpu_bgmap, x)]); /* so there is an existing entry we must deal with. We next need to find * if there is an existing background entry in between the end of * the new region and beginning of the next. */ if ((pbg != x) && (pbg > last)) { /* okay ... there is an intervening background map entry. We need * to handle this by inserting an aligning entry (if the architecture requires it) * and then placing writing an invalid entry at end. */ if (XCHAL_MPU_ALIGN_REQ) { if ((rv = insert_entry_if_needed_with_existing_attr(fg, Xthal_mpu_bgmap, pbg, 0)) != XTHAL_SUCCESS) { #if MPU_DEVELOPMENT_MODE assert_maps_equivalent(on_entry, fg, Xthal_mpu_bgmap); #endif return rv; } #if MPU_DEVELOPMENT_MODE assert_maps_equivalent(on_entry, fg, Xthal_mpu_bgmap); #endif } if ((rv = insert_entry_if_needed_with_existing_attr(fg, Xthal_mpu_bgmap, last, 1)) != XTHAL_SUCCESS) { #if MPU_DEVELOPMENT_MODE assert_maps_equivalent(on_entry, fg, Xthal_mpu_bgmap); #endif return rv; } #if MPU_DEVELOPMENT_MODE assert_maps_equivalent(on_entry, fg, Xthal_mpu_bgmap); #endif } else /* ok so there are no background map entry in between end and x, in this case * we just need to create a new entry at end writing the existing attributes. */ if ((rv = insert_entry_if_needed_with_existing_attr(fg, Xthal_mpu_bgmap, last, 1)) != XTHAL_SUCCESS) { #if MPU_DEVELOPMENT_MODE assert_maps_equivalent(on_entry, fg, Xthal_mpu_bgmap); #endif return rv; } #if MPU_DEVELOPMENT_MODE assert_maps_equivalent(on_entry, fg, Xthal_mpu_bgmap); #endif } } /* last, but not least we need to insert a entry at the starting address for our new region */ if ((rv = insert_entry_if_needed_with_existing_attr(fg, Xthal_mpu_bgmap, start_initial_region(fg, Xthal_mpu_bgmap, first, last), 0)) != XTHAL_SUCCESS) { #if MPU_DEVELOPMENT_MODE assert_maps_equivalent(on_entry, fg, Xthal_mpu_bgmap); #endif return rv; } #if MPU_DEVELOPMENT_MODE assert_maps_equivalent(on_entry, fg, Xthal_mpu_bgmap); #endif } // up to this point, the attributes of every byte in the address space should be the same as when this function // was called. safe_and_commit_overlaped_regions(fg, Xthal_mpu_bgmap, first, last, memoryType, accessRights, writeback, invalidate); assert_map_valid(); return XTHAL_SUCCESS; } // checks if x (full 32bit) is mpu_aligned for MPU static unsigned int mpu_aligned(unsigned x) { return !(x & MPU_ALIGNMENT_MASK); } static unsigned int mpu_align(unsigned int x, unsigned int roundUp) { if (roundUp) return (x + MPU_ALIGNMENT_MASK) & MPU_ADDRESS_MASK; else return (x & MPU_ADDRESS_MASK); } #endif #if defined(__SPLIT__mpu_check) static int bad_accessRights(unsigned ar) { if (ar == 0 || (ar >= 4 && ar <= 15)) return 0; else return 1; } /* this function checks if the supplied map 'fg' is a valid MPU map using 3 criteria: * 1) if an entry is valid, then that entries accessRights must be defined (0 or 4-15). * 2) The map entries' 'vStartAddress's must be in increasing order. * 3) If the architecture requires background map alignment then: * a) If entry0's 'vStartAddress' field is nonzero, then that field must equal * one of the Background Map's 'vStartAddress' field values if the entry 0's valid bit is set. * b) If entryN's 'valid' bit is 0 and entry[N+1]'s 'valid' bit is 1, then * entry[N+1]'s 'vStartAddress' field must equal one of the Background Map's 'vStartAddress' field values. * * This function returns XTHAL_SUCCESS if the map satisfies the condition, otherwise it returns * XTHAL_BAD_ACCESS_RIGHTS, XTHAL_OUT_OF_ORDER_MAP, or XTHAL_MAP_NOT_ALIGNED. * */ static int check_map(const xthal_MPU_entry* fg, unsigned int n, const xthal_MPU_entry* bg) { int i; unsigned current = 0; if (!n) return XTHAL_SUCCESS; if (n > XCHAL_MPU_ENTRIES) return XTHAL_OUT_OF_ENTRIES; for (i = 0; i < n; i++) { if (XTHAL_MPU_ENTRY_GET_VALID(fg[i]) && bad_accessRights(XTHAL_MPU_ENTRY_GET_ACCESS(fg[i]))) return XTHAL_BAD_ACCESS_RIGHTS; if ((XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[i]) < current)) return XTHAL_OUT_OF_ORDER_MAP; if (XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[i]) & MPU_VSTART_CORRECTNESS_MASK) return XTHAL_MAP_NOT_ALIGNED; current = XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[i]); } if (XCHAL_MPU_ALIGN_REQ && XTHAL_MPU_ENTRY_GET_VALID(fg[0]) && XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[0]) && !_xthal_in_bgmap(XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[0]), bg)) return XTHAL_MAP_NOT_ALIGNED; for (i = 0; i < n- 1; i++) if (XCHAL_MPU_ALIGN_REQ && !XTHAL_MPU_ENTRY_GET_VALID(fg[i]) && XTHAL_MPU_ENTRY_GET_VALID(fg[i + 1]) && !_xthal_in_bgmap(XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[i + 1]), bg)) return XTHAL_MAP_NOT_ALIGNED; return XTHAL_SUCCESS; } /* * this function checks that the bit-wise or-ed XTHAL_MEM_... bits in x correspond to a valid * MPU memoryType. If x is valid, then 0 is returned, otherwise XTHAL_BAD_MEMORY_TYPE is * returned. */ static int check_memory_type(unsigned x) { unsigned system_cache_type = _XTHAL_MEM_CACHE_MASK(x); unsigned processor_cache_type = (((x) & _XTHAL_LOCAL_CACHE_BITS) >> 4); if ((system_cache_type > XTHAL_MEM_NON_CACHEABLE) || (processor_cache_type > XTHAL_MEM_NON_CACHEABLE)) return XTHAL_BAD_MEMORY_TYPE; int processor_cache_type_set = 1; if (!processor_cache_type) { processor_cache_type = system_cache_type << 4; processor_cache_type_set = 0; } unsigned device = _XTHAL_MEM_IS_DEVICE(x); unsigned system_noncacheable = _XTHAL_IS_SYSTEM_NONCACHEABLE(x); if (device | system_noncacheable) { if ((system_cache_type || processor_cache_type_set) && device) return XTHAL_BAD_MEMORY_TYPE; if (processor_cache_type_set) return XTHAL_BAD_MEMORY_TYPE; // if memory is device or non cacheable, then processor cache type should not be set if (system_noncacheable && (x & XTHAL_MEM_INTERRUPTIBLE)) return XTHAL_BAD_MEMORY_TYPE; { unsigned z = x & XTHAL_MEM_SYSTEM_SHAREABLE; if ((z == XTHAL_MEM_INNER_SHAREABLE) || (z == XTHAL_MEM_OUTER_SHAREABLE)) return XTHAL_BAD_MEMORY_TYPE; } } else { if ((x & XTHAL_MEM_SYSTEM_SHAREABLE) == XTHAL_MEM_SYSTEM_SHAREABLE) return XTHAL_BAD_MEMORY_TYPE; if ((x & (XTHAL_MEM_BUFFERABLE | XTHAL_MEM_INTERRUPTIBLE))) return XTHAL_BAD_MEMORY_TYPE; } return 0; } #endif #endif // is MPU #if defined(__SPLIT__mpu_basic) /* * These functions accept encoded access rights, and return 1 if the supplied memory type has the property specified by the function name. */ extern int xthal_is_kernel_readable(int accessRights) { #if XCHAL_HAVE_MPU return is_kernel_readable(accessRights); #else return XTHAL_UNSUPPORTED; #endif } extern int xthal_is_kernel_writeable(int accessRights) { #if XCHAL_HAVE_MPU return is_kernel_writeable(accessRights); #else return XTHAL_UNSUPPORTED; #endif } extern int xthal_is_kernel_executable(int accessRights) { #if XCHAL_HAVE_MPU return is_kernel_executable(accessRights); #else return XTHAL_UNSUPPORTED; #endif } extern int xthal_is_user_readable(int accessRights) { #if XCHAL_HAVE_MPU return is_user_readable(accessRights); #else return XTHAL_UNSUPPORTED; #endif } extern int xthal_is_user_writeable(int accessRights) { #if XCHAL_HAVE_MPU return is_user_writeable(accessRights); #else return XTHAL_UNSUPPORTED; #endif } extern int xthal_is_user_executable(int accessRights) { #if XCHAL_HAVE_MPU return is_user_executable(accessRights); #else return XTHAL_UNSUPPORTED; #endif } /* * These functions accept either an encoded or unencoded memory type, and * return 1 if the supplied memory type has property specified by the * function name. */ int xthal_is_cacheable(unsigned int mt) { #if XCHAL_HAVE_MPU return is_cacheable(mt); #else return XTHAL_UNSUPPORTED; #endif } int xthal_is_writeback(unsigned int mt) { #if XCHAL_HAVE_MPU return is_writeback(mt); #else return XTHAL_UNSUPPORTED; #endif } int xthal_is_device(unsigned int mt) { #if XCHAL_HAVE_MPU return is_device(mt); #else return XTHAL_UNSUPPORTED; #endif } #endif /* * This function converts a bit-wise combination of the XTHAL_MEM_.. constants * to the corresponding MPU memory type (9-bits). * * If none of the XTHAL_MEM_.. bits are present in the argument, then * bits 4-12 (9-bits) are returned ... this supports using an already encoded * memoryType (perhaps obtained from an xthal_MPU_entry structure) as input * to xthal_set_region_attribute(). * * This function first checks that the supplied constants are a valid and * supported combination. If not, it returns XTHAL_BAD_MEMORY_TYPE. */ #if defined(__SPLIT__mpu_check) int xthal_encode_memory_type(unsigned int x) { #if XCHAL_HAVE_MPU const unsigned int MemoryTypeMask = 0x1ff0; const unsigned int MemoryFlagMask = 0xffffe000; /* * Encodes the memory type bits supplied in an | format (XCHAL_CA_PROCESSOR_CACHE_WRITEALLOC | XCHAL_CA_PROCESSOR_CACHE_WRITEBACK) */ unsigned memoryFlags = x & MemoryFlagMask; if (!memoryFlags) return (x & MemoryTypeMask) >> XTHAL_AR_WIDTH; else { int chk = check_memory_type(memoryFlags); if (chk < 0) return chk; else return XTHAL_ENCODE_MEMORY_TYPE(memoryFlags); } #else return XTHAL_UNSUPPORTED; #endif } #endif #if defined(__SPLIT__mpu_rmap) /* * Copies the current MPU entry list into 'entries' which * must point to available memory of at least * sizeof(xthal_MPU_entry) * XCHAL_MPU_ENTRIES. * * This function returns XTHAL_SUCCESS. * XTHAL_INVALID, or * XTHAL_UNSUPPORTED. */ int xthal_read_map(xthal_MPU_entry* fg_map) { #if XCHAL_HAVE_MPU unsigned i; if (!fg_map) return XTHAL_INVALID; xthal_read_map_raw(fg_map); return XTHAL_SUCCESS; #else return XTHAL_UNSUPPORTED; #endif } #if XCHAL_HAVE_MPU #undef XCHAL_MPU_BGMAP #define XCHAL_MPU_BGMAP(s,vstart,vend,rights,mtype,x...) XTHAL_MPU_ENTRY(vstart,1,rights,mtype), const xthal_MPU_entry Xthal_mpu_bgmap[] = { XCHAL_MPU_BACKGROUND_MAP(0) }; #endif /* * Copies the MPU background map into 'entries' which must point * to available memory of at least * sizeof(xthal_MPU_entry) * XCHAL_MPU_BACKGROUND_ENTRIES. * * This function returns XTHAL_SUCCESS. * XTHAL_INVALID, or * XTHAL_UNSUPPORTED. */ int xthal_read_background_map(xthal_MPU_entry* bg_map) { #if XCHAL_HAVE_MPU if (!bg_map) return XTHAL_INVALID; memcpy(bg_map, Xthal_mpu_bgmap, sizeof(Xthal_mpu_bgmap)); return XTHAL_SUCCESS; #else return XTHAL_UNSUPPORTED; #endif } #endif /* * Writes the map pointed to by 'entries' to the MPU. Before updating * the map, it commits any uncommitted * cache writes, and invalidates the cache if necessary. * * This function does not check for the correctness of the map. Generally * xthal_check_map() should be called first to check the map. * * If n == 0 then the existing map is cleared, and no new map is written * (useful for returning to reset state) * * If (n > 0 && n < XCHAL_MPU_ENTRIES) then a new map is written with * (XCHAL_MPU_ENTRIES-n) padding entries added to ensure a properly ordered * map. The resulting foreground map will be equivalent to the map vector * fg, but the position of the padding entries should not be relied upon. * * If n == XCHAL_MPU_ENTRIES then the complete map as specified by fg is * written. * * xthal_write_map() disables the MPU foreground map during the MPU * update and relies on the background map. * * As a result any interrupt that does not meet the following conditions * must be disabled before calling xthal_write_map(): * 1) All code and data needed for the interrupt must be * mapped by the background map with sufficient access rights. * 2) The interrupt code must not access the MPU. * */ #if defined(__SPLIT__mpu_wmap) void xthal_write_map(const xthal_MPU_entry* fg, unsigned int n) { #if XCHAL_HAVE_MPU unsigned int cacheadrdis = xthal_calc_cacheadrdis(fg, n); xthal_dcache_all_writeback_inv(); xthal_icache_all_invalidate(); xthal_write_map_raw(fg, n); write_cacheadrdis(cacheadrdis); isync(); // ditto #endif } #endif #if defined(__SPLIT__mpu_check) /* * Checks if entry vector 'fg' of length 'n' is a valid MPU access map. * Returns: * XTHAL_SUCCESS if valid, * XTHAL_OUT_OF_ENTRIES * XTHAL_MAP_NOT_ALIGNED, * XTHAL_BAD_ACCESS_RIGHTS, * XTHAL_OUT_OF_ORDER_MAP, or * XTHAL_UNSUPPORTED if config doesn't have an MPU. */ int xthal_check_map(const xthal_MPU_entry* fg, unsigned int n) { #if XCHAL_HAVE_MPU return check_map(fg, XCHAL_MPU_ENTRIES, Xthal_mpu_bgmap); #else return XTHAL_UNSUPPORTED; #endif } #endif #if defined(__SPLIT__mpu_basic) /* * Returns the MPU entry that maps 'vaddr'. If 'infgmap' is non-NULL then it is * set to 1 if 'vaddr' is mapped by the foreground map, or 0 if 'vaddr' * is mapped by the background map. */ extern xthal_MPU_entry xthal_get_entry_for_address(void* paddr, int* infgmap) { #if XCHAL_HAVE_MPU xthal_MPU_entry e; unsigned int p; __asm__ __volatile__("PPTLB %0, %1\n\t" : "=a" (p) : "a" (paddr)); if ((p & 0x80000000)) { if (infgmap) *infgmap = 1; e.at = (p & 0x1fffff); __asm__ __volatile__("RPTLB0 %0, %1\n\t" : "=a" (e.as) : "a" (p & 0x1f)); return e; } else { int i; if (infgmap) *infgmap = 0; for (i = XCHAL_MPU_BACKGROUND_ENTRIES - 1; i > 0; i--) { if (XTHAL_MPU_ENTRY_GET_VSTARTADDR(Xthal_mpu_bgmap[i]) <= (unsigned) paddr) { return Xthal_mpu_bgmap[i]; } } // in background map return Xthal_mpu_bgmap[0]; } #else xthal_MPU_entry e; return e; #endif } #endif /* * This function is intended as an MPU specific version of * xthal_set_region_attributes(). xthal_set_region_attributes() calls * this function for MPU configurations. * * This function sets the attributes for the region [vaddr, vaddr+size) * in the MPU. * * Depending on the state of the MPU this function will require from * 0 to 3 unused MPU entries. * * This function typically will move, add, and subtract entries from * the MPU map during execution, so that the resulting map may * be quite different than when the function was called. * * This function does make the following guarantees: * 1) The MPU access map remains in a valid state at all times * during its execution. * 2) At all points during (and after) completion the memoryType * and accessRights remain the same for all addresses * that are not in the range [vaddr, vaddr+size). * 3) If XTHAL_SUCCESS is returned, then the range * [vaddr, vaddr+size) will have the accessRights and memoryType * specified. * * The accessRights parameter should be either a 4-bit value corresponding * to an MPU access mode (as defined by the XTHAL_AR_.. constants), or * XTHAL_MPU_USE_EXISTING_ACCESS_RIGHTS. * * The memoryType parameter should be either a bit-wise or-ing of XTHAL_MEM_.. * constants that represent a valid MPU memoryType, a 9-bit MPU memoryType * value, or XTHAL_MPU_USE_EXISTING_MEMORY_TYPE. * * In addition to the error codes that xthal_set_region_attribute() * returns, this function can also return: XTHAL_BAD_ACCESS_RIGHTS * (if the access rights bits map to an unsupported combination), or * XTHAL_OUT_OF_ENTRIES (if there are not enough unused MPU entries). * * If this function is called with an invalid MPU map, then this function * will return one of the codes that is returned by xthal_check_map(). * * The flag, XTHAL_CAFLAG_EXPAND, is not supported. * */ #if defined(__SPLIT__mpu_attributes) int xthal_mpu_set_region_attribute(void* vaddr, unsigned size, int accessRights, int memoryType, unsigned flags) { #if XCHAL_HAVE_MPU unsigned int first; unsigned int last; int rv; if (flags & XTHAL_CAFLAG_EXPAND) return XTHAL_UNSUPPORTED; if (size == 0) return XTHAL_ZERO_SIZED_REGION; first = (unsigned) vaddr; last = first + size; if (last != 0xFFFFFFFF) last--; if (first >= last) return XTHAL_INVALID_ADDRESS_RANGE; // Wraps around if (accessRights & XTHAL_MPU_USE_EXISTING_ACCESS_RIGHTS) { accessRights = XTHAL_MPU_ENTRY_GET_ACCESS(xthal_get_entry_for_address(vaddr, 0)); } else { accessRights = encode_access_rights(accessRights); if (accessRights < 0) return XTHAL_BAD_ACCESS_RIGHTS; } if (memoryType & XTHAL_MPU_USE_EXISTING_MEMORY_TYPE) { memoryType = XTHAL_MPU_ENTRY_GET_MEMORY_TYPE(xthal_get_entry_for_address(vaddr, 0)); } else { if (memoryType & 0xffffe000) // Tests if any of the XTHAL MEM flags are present memoryType = xthal_encode_memory_type(memoryType); else if (memoryType & 0xfffffe00) // Tests if any of bits from 9 to 13 are set indicating // that the memoryType was improperly shifted // we flag this as an error return XTHAL_BAD_MEMORY_TYPE; if (memoryType < 0) return XTHAL_BAD_MEMORY_TYPE; } if (flags & XTHAL_CAFLAG_EXACT) if (!mpu_aligned(first) || !mpu_aligned(last + 1)) return XTHAL_INEXACT; first = mpu_align(first, (flags & XTHAL_CAFLAG_NO_PARTIAL)); if (last != 0xffffffff) { last = mpu_align(last + 1, !(flags & XTHAL_CAFLAG_NO_PARTIAL)); if (first >= last) return ((flags & XTHAL_CAFLAG_NO_PARTIAL) ? XTHAL_ZERO_SIZED_REGION : 0); } rv = safe_add_region(first, last, accessRights, memoryType, !(flags & XTHAL_CAFLAG_NO_AUTO_WB), !(flags & XTHAL_CAFLAG_NO_AUTO_INV)); isync(); return rv; #else return XTHAL_UNSUPPORTED; #endif } #endif #if defined(__SPLIT__mpu_cachedis) inline static unsigned int max2(unsigned int a, unsigned int b) { if (a>b) return a; else return b; } inline static unsigned int mask_cachedis(unsigned int current, int first_region, int last_region) { unsigned int x; x = ((1 << (last_region - first_region + 1)) - 1) << first_region; current &= ~x; return current; } /* * xthal_calc_cacheadrdis() computes the value that should be written * to the CACHEADRDIS register. The return value has bits 0-7 set according as: * bit n: is zero if any part of the region [512MB * n, 512MB* (n-1)) is cacheable. * is one if NO part of the region [512MB * n, 512MB* (n-1)) is cacheable. * * This function looks at both the loops through both the foreground and background maps * to find cacheable area. Once one cacheable area is found in a 512MB region, then we * skip to the next 512MB region. */ unsigned int xthal_calc_cacheadrdis(const xthal_MPU_entry* fg, unsigned int num_entries) { #if XCHAL_HAVE_MPU unsigned int cachedis = 0xff; int fg_index = num_entries - 1; int bg_index = XCHAL_MPU_BACKGROUND_ENTRIES - 1; int working_region = 7; int ending_region; unsigned int vaddr = 0xffffffff; while (bg_index >= 0 || fg_index >= 0) { if ((fg_index >= 0 && XTHAL_MPU_ENTRY_GET_VALID(fg[fg_index]))) { vaddr = XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[fg_index]); ending_region = vaddr >> 29; if (ending_region <= working_region) { unsigned int mt = XTHAL_MPU_ENTRY_GET_MEMORY_TYPE(fg[fg_index]); if (is_cacheable(mt)) { cachedis = mask_cachedis(cachedis, ending_region, working_region); /* Optimize since we have found one cacheable entry in the region ... no need to look for more */ if (ending_region == 0) return cachedis; else working_region = ending_region - 1; } else if (vaddr & 0x1fffffff) // If vaddr is on a 512MB region we want to move to the next region working_region = ending_region; else working_region = ending_region - 1; } } else if ((bg_index >= 0) && ((fg_index <= 0) || XTHAL_MPU_ENTRY_GET_VALID(fg[fg_index-1]))&& vaddr) { unsigned int caddr; unsigned int low_addr = ( (fg_index >= 0) ? (XTHAL_MPU_ENTRY_GET_VSTARTADDR(fg[fg_index])) : 0); /* First skip any background entries that start after the address of interest */ while ((caddr = XTHAL_MPU_ENTRY_GET_VSTARTADDR(Xthal_mpu_bgmap[bg_index])) >= vaddr) bg_index--; do { caddr = max2(XTHAL_MPU_ENTRY_GET_VSTARTADDR(Xthal_mpu_bgmap[bg_index]), low_addr); ending_region = caddr >> 29; if (ending_region <= working_region) { unsigned int mt = XTHAL_MPU_ENTRY_GET_MEMORY_TYPE( Xthal_mpu_bgmap[bg_index]); if (is_cacheable(mt)) { cachedis = mask_cachedis(cachedis, ending_region, working_region); /* Optimize since we have found one cacheable entry in the region ... * no need to look for more */ if (ending_region == 0) return cachedis; // we are done else working_region = ending_region - 1; } else if (caddr & 0x1fffffff) working_region = ending_region; else working_region = ending_region - 1; } bg_index--; }while (caddr > low_addr); vaddr = caddr; } fg_index--; if (!vaddr) break; } return cachedis; #else return 0; #endif } #endif #if defined(__SPLIT__mpu_basic) void (*_xthal_assert_handler)(); /* Undocumented internal testing function */ extern void _xthal_set_assert_handler(void (*handler)()) { #if XCHAL_HAVE_MPU _xthal_assert_handler = handler; #endif } #endif