Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:23:58

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /**
0004  * @file
0005  *
0006  * @ingroup RTEMSBSPsPowerPCQorIQMMU
0007  *
0008  * @brief MMU implementation.
0009  */
0010 
0011 /*
0012  * Copyright (C) 2011, 2018 embedded brains GmbH & Co. KG
0013  *
0014  * Redistribution and use in source and binary forms, with or without
0015  * modification, are permitted provided that the following conditions
0016  * are met:
0017  * 1. Redistributions of source code must retain the above copyright
0018  *    notice, this list of conditions and the following disclaimer.
0019  * 2. Redistributions in binary form must reproduce the above copyright
0020  *    notice, this list of conditions and the following disclaimer in the
0021  *    documentation and/or other materials provided with the distribution.
0022  *
0023  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0024  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0025  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0026  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0027  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0028  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0029  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0030  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0031  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0032  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0033  * POSSIBILITY OF SUCH DAMAGE.
0034  */
0035 
0036 #include <bsp/mmu.h>
0037 #include <libcpu/powerpc-utility.h>
0038 
0039 #define TEXT __attribute__((section(".bsp_start_text")))
0040 
0041 static uintptr_t TEXT power_of_two(uintptr_t val)
0042 {
0043     uintptr_t test_power = QORIQ_MMU_MIN_POWER;
0044     uintptr_t power = test_power;
0045     uintptr_t alignment = 1U << test_power;
0046 
0047     while (test_power <= QORIQ_MMU_MAX_POWER && (val & (alignment - 1)) == 0) {
0048         power = test_power;
0049         alignment <<= QORIQ_MMU_POWER_STEP;
0050         test_power += QORIQ_MMU_POWER_STEP;
0051     }
0052 
0053     return power;
0054 }
0055 
0056 static uintptr_t TEXT max_power_of_two(uintptr_t val)
0057 {
0058     uintptr_t test_power = QORIQ_MMU_MIN_POWER;
0059     uintptr_t power = test_power;
0060     uintptr_t max = 1U << test_power;
0061 
0062     do {
0063         power = test_power;
0064         max <<= QORIQ_MMU_POWER_STEP;
0065         test_power += QORIQ_MMU_POWER_STEP;
0066     } while (test_power <= QORIQ_MMU_MAX_POWER && max <= val);
0067 
0068     return power;
0069 }
0070 
0071 void TEXT qoriq_mmu_context_init(qoriq_mmu_context *self)
0072 {
0073     int *cur = (int *) self;
0074     const int *end = cur + sizeof(*self) / sizeof(*cur);
0075 
0076     while (cur != end) {
0077         *cur = 0;
0078         ++cur;
0079     }
0080 }
0081 
0082 static void TEXT sort(qoriq_mmu_context *self)
0083 {
0084     qoriq_mmu_entry *entries = self->entries;
0085     int n = self->count;
0086     int i = 0;
0087 
0088     for (i = 1; i < n; ++i) {
0089         qoriq_mmu_entry key = entries [i];
0090         int j = 0;
0091 
0092         for (j = i - 1; j >= 0 && entries [j].begin > key.begin; --j) {
0093             entries [j + 1] = entries [j];
0094         }
0095 
0096         entries [j + 1] = key;
0097     }
0098 }
0099 
0100 static bool TEXT mas_compatible(const qoriq_mmu_entry *a, const qoriq_mmu_entry *b)
0101 {
0102     uint32_t m = FSL_EIS_MAS2_M;
0103 
0104     return (a->mas2 & ~m) == (b->mas2 & ~m);
0105 }
0106 
0107 static bool TEXT can_merge(const qoriq_mmu_entry *prev, const qoriq_mmu_entry *cur)
0108 {
0109     return mas_compatible(prev, cur)
0110         && (prev->begin == cur->begin || prev->last >= cur->begin - 1);
0111 }
0112 
0113 static void TEXT merge(qoriq_mmu_context *self)
0114 {
0115     qoriq_mmu_entry *entries = self->entries;
0116     int n = self->count;
0117     int i = 0;
0118 
0119     for (i = 1; i < n; ++i) {
0120         qoriq_mmu_entry *prev = &entries [i - 1];
0121         qoriq_mmu_entry *cur = &entries [i];
0122 
0123         if (can_merge(prev, cur)) {
0124             int j = 0;
0125 
0126             prev->mas1 |= cur->mas1;
0127             prev->mas2 |= cur->mas2;
0128             prev->mas3 |= cur->mas3;
0129 
0130             if (cur->last > prev->last) {
0131                 prev->last = cur->last;
0132             }
0133 
0134             for (j = i + 1; j < n; ++j) {
0135                 entries [j - 1] = entries [j];
0136             }
0137 
0138             --i;
0139             --n;
0140         }
0141     }
0142 
0143     self->count = n;
0144 }
0145 
0146 static void TEXT compact(qoriq_mmu_context *self)
0147 {
0148     sort(self);
0149     merge(self);
0150 }
0151 
0152 static bool TEXT can_expand_down(
0153     const qoriq_mmu_context *self,
0154     const qoriq_mmu_entry *cur,
0155     int i,
0156     uintptr_t new_begin
0157 )
0158 {
0159     int j;
0160 
0161     for (j = 0; j < i; ++j) {
0162         const qoriq_mmu_entry *before = &self->entries[j];
0163 
0164         if (
0165             before->begin <= new_begin
0166                 && new_begin <= before->last
0167                 && !mas_compatible(before, cur)
0168         ) {
0169             return false;
0170         }
0171     }
0172 
0173     return true;
0174 }
0175 
0176 static bool TEXT can_expand_up(
0177     const qoriq_mmu_context *self,
0178     const qoriq_mmu_entry *cur,
0179     int i,
0180     int n,
0181     uintptr_t new_last
0182 )
0183 {
0184     int j;
0185 
0186     for (j = i + 1; j < n; ++j) {
0187         const qoriq_mmu_entry *after = &self->entries[j];
0188 
0189         if (
0190             after->begin <= new_last
0191                 && new_last <= after->last
0192                 && !mas_compatible(after, cur)
0193         ) {
0194             return false;
0195         }
0196     }
0197 
0198     return true;
0199 }
0200 
0201 static void TEXT align(qoriq_mmu_context *self, uintptr_t alignment)
0202 {
0203     int n = self->count;
0204     int i;
0205 
0206     for (i = 0; i < n; ++i) {
0207         qoriq_mmu_entry *cur = &self->entries[i];
0208         uintptr_t new_begin = cur->begin & ~(alignment - 1);
0209         uintptr_t new_last = alignment + (cur->last & ~(alignment - 1)) - 1;
0210 
0211         if (
0212             can_expand_down(self, cur, i, new_begin)
0213                 && can_expand_up(self, cur, i, n, new_last)
0214         ) {
0215             cur->begin = new_begin;
0216             cur->last = new_last;
0217         }
0218     }
0219 }
0220 
0221 static bool TEXT is_full(qoriq_mmu_context *self)
0222 {
0223     return self->count >= QORIQ_TLB1_ENTRY_COUNT;
0224 }
0225 
0226 static void TEXT append(qoriq_mmu_context *self, const qoriq_mmu_entry *new_entry)
0227 {
0228     self->entries [self->count] = *new_entry;
0229     ++self->count;
0230 }
0231 
0232 bool TEXT qoriq_mmu_add(
0233     qoriq_mmu_context *self,
0234     uintptr_t begin,
0235     uintptr_t last,
0236     uint32_t mas1,
0237     uint32_t mas2,
0238     uint32_t mas3,
0239     uint32_t mas7
0240 )
0241 {
0242     bool ok = true;
0243 
0244     if (is_full(self)) {
0245         compact(self);
0246     }
0247 
0248     if (!is_full(self)) {
0249         if (begin < last) {
0250             qoriq_mmu_entry new_entry = {
0251                 .begin = begin,
0252                 .last = last,
0253                 .mas1 = mas1,
0254                 .mas2 = mas2,
0255                 .mas3 = mas3,
0256                 .mas7 = mas7
0257             };
0258             append(self, &new_entry);
0259         } else {
0260             ok = false;
0261         }
0262     } else {
0263         ok = false;
0264     }
0265 
0266     return ok;
0267 }
0268 
0269 static uintptr_t TEXT min(uintptr_t a, uintptr_t b)
0270 {
0271     return a < b ? a : b;
0272 }
0273 
0274 static bool TEXT split(qoriq_mmu_context *self, qoriq_mmu_entry *cur)
0275 {
0276     bool again = false;
0277     uintptr_t begin = cur->begin;
0278     uintptr_t end = cur->last + 1;
0279     uintptr_t size = end - begin;
0280     uintptr_t begin_power = power_of_two(begin);
0281     uintptr_t size_power = max_power_of_two(size);
0282     uintptr_t power = min(begin_power, size_power);
0283     uintptr_t split_size = power < 32 ? (1U << power) : 0;
0284     uintptr_t split_pos = begin + split_size;
0285 
0286     if (split_pos != end && !is_full(self)) {
0287         qoriq_mmu_entry new_entry = *cur;
0288         cur->begin = split_pos;
0289         new_entry.last = split_pos - 1;
0290         append(self, &new_entry);
0291         again = true;
0292     }
0293 
0294     return again;
0295 }
0296 
0297 static void TEXT split_all(qoriq_mmu_context *self)
0298 {
0299     qoriq_mmu_entry *entries = self->entries;
0300     int n = self->count;
0301     int i = 0;
0302 
0303     for (i = 0; i < n; ++i) {
0304         qoriq_mmu_entry *cur = &entries [i];
0305 
0306         while (split(self, cur)) {
0307             /* Repeat */
0308         }
0309     }
0310 }
0311 
0312 static TEXT void partition(qoriq_mmu_context *self)
0313 {
0314     compact(self);
0315     split_all(self);
0316     sort(self);
0317 }
0318 
0319 void TEXT qoriq_mmu_partition(qoriq_mmu_context *self, int max_count)
0320 {
0321     uintptr_t alignment = 4096;
0322 
0323     sort(self);
0324 
0325     do {
0326         align(self, alignment);
0327         partition(self);
0328         alignment *= 4;
0329     } while (self->count > max_count);
0330 }
0331 
0332 void TEXT qoriq_mmu_write_to_tlb1(qoriq_mmu_context *self, int first_tlb)
0333 {
0334     qoriq_mmu_entry *entries = self->entries;
0335     int n = self->count;
0336     int i = 0;
0337 
0338     for (i = 0; i < n; ++i) {
0339         qoriq_mmu_entry *cur = &entries [i];
0340         uintptr_t ea = cur->begin;
0341         uintptr_t size = cur->last - ea + 1;
0342         uintptr_t tsize = (power_of_two(size) - 10) / 2;
0343         int tlb = first_tlb + i;
0344 
0345         qoriq_tlb1_write(
0346             tlb,
0347             cur->mas1,
0348             cur->mas2,
0349             cur->mas3,
0350             cur->mas7,
0351             ea,
0352             (int) tsize
0353         );
0354     }
0355 }
0356 
0357 void qoriq_mmu_change_perm(uint32_t test, uint32_t set, uint32_t clear)
0358 {
0359     int i = 0;
0360 
0361     for (i = 0; i < QORIQ_TLB1_ENTRY_COUNT; ++i) {
0362         uint32_t mas0 = FSL_EIS_MAS0_TLBSEL | FSL_EIS_MAS0_ESEL(i);
0363         uint32_t mas1 = 0;
0364 
0365         PPC_SET_SPECIAL_PURPOSE_REGISTER(FSL_EIS_MAS0, mas0);
0366         ppc_synchronize_instructions();
0367         ppc_tlbre();
0368         ppc_synchronize_instructions();
0369 
0370         PPC_SPECIAL_PURPOSE_REGISTER(FSL_EIS_MAS1, mas1);
0371         if ((mas1 & FSL_EIS_MAS1_V) != 0) {
0372             uint32_t mask = 0x3ff;
0373             uint32_t mas3;
0374             PPC_SPECIAL_PURPOSE_REGISTER(FSL_EIS_MAS3, mas3);
0375 
0376             if ((mas3 & mask) == test) {
0377                 mas3 &= ~(clear & mask);
0378                 mas3 |= set & mask;
0379                 PPC_SET_SPECIAL_PURPOSE_REGISTER(FSL_EIS_MAS3, mas3);
0380                 ppc_msync();
0381                 ppc_synchronize_instructions();
0382                 ppc_tlbwe();
0383                 ppc_synchronize_instructions();
0384             }
0385         }
0386     }
0387 }
0388 
0389 int qoriq_mmu_find_free_tlb1_entry(void)
0390 {
0391     int i = 0;
0392 
0393     for (i = 0; i < QORIQ_TLB1_ENTRY_COUNT; ++i) {
0394         uint32_t mas0 = FSL_EIS_MAS0_TLBSEL | FSL_EIS_MAS0_ESEL(i);
0395         uint32_t mas1;
0396 
0397         PPC_SET_SPECIAL_PURPOSE_REGISTER(FSL_EIS_MAS0, mas0);
0398         ppc_synchronize_instructions();
0399         ppc_tlbre();
0400         ppc_synchronize_instructions();
0401 
0402         PPC_SPECIAL_PURPOSE_REGISTER(FSL_EIS_MAS1, mas1);
0403         if ((mas1 & FSL_EIS_MAS1_V) == 0) {
0404             return i;
0405         }
0406     }
0407 
0408     return -1;
0409 }
0410 
0411 void qoriq_mmu_adjust_and_write_to_tlb1(
0412     int tlb,
0413     uintptr_t begin,
0414     uintptr_t last,
0415     uint32_t mas1,
0416     uint32_t mas2,
0417     uint32_t mas3,
0418     uint32_t mas7
0419 )
0420 {
0421     qoriq_mmu_context context;
0422 
0423     qoriq_mmu_context_init(&context);
0424     qoriq_mmu_add(
0425         &context,
0426         begin,
0427         last,
0428         mas1,
0429         mas2,
0430         mas3,
0431         mas7
0432     );
0433     qoriq_mmu_partition(&context, 1);
0434     qoriq_mmu_write_to_tlb1(&context, tlb);
0435 }