| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * 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 <trusty/trusty_dev.h> |
| #include <trusty/util.h> |
| |
| /* 48-bit physical address bits 47:12 */ |
| |
| #define NS_PTE_PHYSADDR_SHIFT 12 |
| #define NS_PTE_PHYSADDR(pte) ((pte) & 0xFFFFFFFFF000ULL) |
| |
| /* Access permissions bits 7:6 |
| * EL0 EL1 |
| * 00 None RW |
| * 01 RW RW |
| * 10 None RO |
| * 11 RO RO |
| */ |
| #define NS_PTE_AP_SHIFT 6 |
| #define NS_PTE_AP_MASK (0x3 << NS_PTE_AP_SHIFT) |
| |
| /* Memory type and cache attributes bits 55:48 */ |
| #define NS_PTE_MAIR_SHIFT 48 |
| #define NS_PTE_MAIR_MASK (0x00FFULL << NS_PTE_MAIR_SHIFT) |
| |
| #define NS_PTE_MAIR_INNER_SHIFT 48 |
| #define NS_PTE_MAIR_INNER_MASK (0x000FULL << NS_PTE_MAIR_INNER_SHIFT) |
| |
| #define NS_PTE_MAIR_OUTER_SHIFT 52 |
| #define NS_PTE_MAIR_OUTER_MASK (0x000FULL << NS_PTE_MAIR_OUTER_SHIFT) |
| |
| /* Normal memory */ |
| #define NS_MAIR_NORMAL_CACHED_WB_RWA 0xFF /* inner and outer write back read/write allocate */ |
| #define NS_MAIR_NORMAL_CACHED_WT_RA 0xAA /* inner and outer write through read allocate */ |
| #define NS_MAIR_NORMAL_CACHED_WB_RA 0xEE /* inner and outer write back, read allocate */ |
| #define NS_MAIR_NORMAL_UNCACHED 0x44 /* uncached */ |
| |
| /* Device memory */ |
| #define NS_MAIR_DEVICE_STRONGLY_ORDERED 0x00 /* nGnRnE (strongly ordered) */ |
| #define NS_MAIR_DEVICE 0x04 /* nGnRE (device) */ |
| #define NS_MAIR_DEVICE_GRE 0x0C /* GRE */ |
| |
| /* shareable attributes bits 9:8 */ |
| #define NS_PTE_SHAREABLE_SHIFT 8 |
| |
| #define NS_NON_SHAREABLE 0x0 |
| #define NS_OUTER_SHAREABLE 0x2 |
| #define NS_INNER_SHAREABLE 0x3 |
| |
| typedef uintptr_t addr_t; |
| typedef uintptr_t vaddr_t; |
| typedef uintptr_t paddr_t; |
| |
| #if NS_ARCH_ARM64 |
| |
| #define PAR_F (0x1 << 0) |
| |
| /* |
| * ARM64 |
| */ |
| |
| /* Note: this will crash if called from user space */ |
| static void arm64_write_ATS1ExW(uint64_t vaddr) |
| { |
| uint64_t _current_el; |
| |
| __asm__ volatile("mrs %0, CurrentEL" : "=r" (_current_el)); |
| |
| _current_el = (_current_el >> 2) & 0x3; |
| switch (_current_el) { |
| case 0x1: |
| __asm__ volatile("at S1E1W, %0" :: "r" (vaddr)); |
| break; |
| case 0x2: |
| __asm__ volatile("at S1E2W, %0" :: "r" (vaddr)); |
| break; |
| case 0x3: |
| default: |
| trusty_fatal("Unsupported execution state: EL%u\n", _current_el ); |
| break; |
| } |
| |
| __asm__ volatile("isb" ::: "memory"); |
| } |
| |
| static uint64_t arm64_read_par64(void) |
| { |
| uint64_t _val; |
| __asm__ volatile("mrs %0, par_el1" : "=r" (_val)); |
| return _val; |
| } |
| |
| |
| static uint64_t va2par(vaddr_t va) |
| { |
| uint64_t par; |
| unsigned long irq_state; |
| |
| trusty_local_irq_disable(&irq_state); |
| arm64_write_ATS1ExW(va); |
| par = arm64_read_par64(); |
| trusty_local_irq_restore(&irq_state); |
| |
| return par; |
| } |
| |
| static uint64_t par2attr(uint64_t par) |
| { |
| uint64_t attr; |
| |
| /* set phys address */ |
| attr = NS_PTE_PHYSADDR(par); |
| |
| /* cache attributes */ |
| attr |= ((par >> 56) & 0xFF) << NS_PTE_MAIR_SHIFT; |
| |
| /* shareable attributes */ |
| attr |= ((par >> 7) & 0x03) << NS_PTE_SHAREABLE_SHIFT; |
| |
| /* the memory is writable and accessible so leave AP field 0 */ |
| attr |= 0x0 << NS_PTE_AP_SHIFT; |
| |
| return attr; |
| } |
| |
| #else |
| |
| #define PAR_F (0x1 << 0) |
| #define PAR_SS (0x1 << 1) |
| #define PAR_SH (0x1 << 7) |
| #define PAR_NOS (0x1 << 10) |
| #define PAR_LPAE (0x1 << 11) |
| |
| /* |
| * ARM32 |
| */ |
| |
| /* Note: this will crash if called from user space */ |
| static void arm_write_ATS1xW(uint64_t vaddr) |
| { |
| uint32_t _cpsr; |
| |
| __asm__ volatile("mrs %0, cpsr" : "=r"(_cpsr)); |
| |
| if ((_cpsr & 0xF) == 0xa) |
| __asm__ volatile("mcr p15, 4, %0, c7, c8, 1" : : "r"(vaddr)); |
| else |
| __asm__ volatile("mcr p15, 0, %0, c7, c8, 1" : : "r"(vaddr)); |
| } |
| |
| static uint64_t arm_read_par64(void) |
| { |
| uint32_t lower, higher; |
| |
| __asm__ volatile( |
| "mrc p15, 0, %0, c7, c4, 0 \n" |
| "tst %0, #(1 << 11) @ LPAE / long desc format\n" |
| "moveq %1, #0 \n" |
| "mrrcne p15, 0, %0, %1, c7 \n" |
| :"=r"(lower), "=r"(higher) : : |
| ); |
| |
| return ((uint64_t)higher << 32) | lower; |
| } |
| |
| |
| static uint8_t ish_to_mair[8] = { |
| 0x04, /* 0b000 Non cacheble */ |
| 0x00, /* 0b001 Strongly ordered */ |
| 0xF0, /* 0b010 reserved */ |
| 0x04, /* 0b011 device */ |
| 0xF0, /* 0b100 reserved */ |
| 0x0F, /* 0b101 write back - write allocate */ |
| 0x0A, /* 0b110 write through */ |
| 0x0E, /* 0b111 write back - no write allocate */ |
| }; |
| |
| static uint8_t osh_to_mair[4] = { |
| 0x00, /* 0b00 Non-cacheable */ |
| 0x0F, /* 0b01 Write-back, Write-allocate */ |
| 0x0A, /* 0b10 Write-through, no Write-allocate */ |
| 0x0E, /* 0b11 Write-back, no Write-allocate */ |
| }; |
| |
| static uint64_t par2attr(uint64_t par) |
| { |
| uint64_t attr; |
| |
| if (par & PAR_LPAE) { |
| /* set phys address */ |
| attr = NS_PTE_PHYSADDR(par); |
| |
| /* cache attributes */ |
| attr |= ((par >> 56) & 0xFF) << NS_PTE_MAIR_SHIFT; |
| |
| /* shareable attributes */ |
| attr |= ((par >> 7) & 0x03) << NS_PTE_SHAREABLE_SHIFT; |
| |
| } else { |
| |
| /* set phys address */ |
| trusty_assert((par & PAR_SS) == 0); /* super section not supported */ |
| attr = NS_PTE_PHYSADDR(par); |
| |
| /* cache attributes */ |
| uint64_t inner = ((uint64_t)ish_to_mair[(par >> 4) & 0x7]) << NS_PTE_MAIR_INNER_SHIFT; |
| uint64_t outer = ((uint64_t)osh_to_mair[(par >> 2) & 0x3]) << NS_PTE_MAIR_OUTER_SHIFT; |
| uint64_t cache_attributes = (outer << 4) | inner; |
| |
| /* Trusty does not support any kind of device memory, so we will force |
| * cache attributes to be NORMAL UNCACHED on the Trusty side. |
| */ |
| if (cache_attributes == NS_MAIR_DEVICE_STRONGLY_ORDERED) { |
| attr |= ((uint64_t)NS_MAIR_NORMAL_UNCACHED << NS_PTE_MAIR_SHIFT); |
| } else { |
| attr |= inner; |
| attr |= outer; |
| } |
| |
| /* shareable attributes */ |
| if (par & PAR_SH) { |
| /* how to handle NOS bit ? */ |
| attr |= ((uint64_t)NS_INNER_SHAREABLE) << NS_PTE_SHAREABLE_SHIFT; |
| } else { |
| attr |= ((uint64_t)NS_NON_SHAREABLE) << NS_PTE_SHAREABLE_SHIFT; |
| } |
| } |
| |
| /* the memory is writable and accessible so leave AP field 0 */ |
| attr |= 0x0 << NS_PTE_AP_SHIFT; |
| |
| return attr; |
| } |
| |
| static uint64_t va2par(vaddr_t va) |
| { |
| uint64_t par; |
| unsigned long irq_state; |
| |
| trusty_local_irq_disable(&irq_state); |
| arm_write_ATS1xW(va); |
| par = arm_read_par64(); |
| trusty_local_irq_restore(&irq_state); |
| |
| return par; |
| } |
| |
| #endif /* ARM64 */ |
| |
| |
| int trusty_encode_page_info(struct ns_mem_page_info *inf, void *va) |
| { |
| uint64_t par = va2par((vaddr_t)va); |
| |
| if (par & PAR_F) { |
| return -1; |
| } |
| |
| inf->attr = par2attr(par); |
| |
| return 0; |
| } |
| |