1 /*- 2 * Copyright (c) 2014 The FreeBSD Foundation 3 * 4 * This software was developed by Semihalf under 5 * the sponsorship of the FreeBSD Foundation. 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/param.h> 29 30 #include <machine/armreg.h> 31 #include <machine/atomic.h> 32 33 #include <stand.h> 34 35 #include "bootstrap.h" 36 #include "cache.h" 37 38 static long cache_flags; 39 #define CACHE_FLAG_DIC_OFF (1<<0) 40 #define CACHE_FLAG_IDC_OFF (1<<1) 41 42 static bool 43 get_cache_dic(uint64_t ctr) 44 { 45 if ((cache_flags & CACHE_FLAG_DIC_OFF) != 0) { 46 return (false); 47 } 48 49 return (CTR_DIC_VAL(ctr) != 0); 50 } 51 52 static bool 53 get_cache_idc(uint64_t ctr) 54 { 55 if ((cache_flags & CACHE_FLAG_IDC_OFF) != 0) { 56 return (false); 57 } 58 59 return (CTR_IDC_VAL(ctr) != 0); 60 } 61 62 static unsigned int 63 get_dcache_line_size(uint64_t ctr) 64 { 65 unsigned int dcl_size; 66 67 /* 68 * Relevant field [19:16] is LOG2 69 * of the number of words in DCache line 70 */ 71 dcl_size = CTR_DLINE_SIZE(ctr); 72 73 /* Size of word shifted by cache line size */ 74 return (sizeof(int) << dcl_size); 75 } 76 77 void 78 cpu_flush_dcache(const void *ptr, size_t len) 79 { 80 uint64_t cl_size, ctr; 81 vm_offset_t addr, end; 82 83 /* Accessible from all security levels */ 84 ctr = READ_SPECIALREG(ctr_el0); 85 86 if (get_cache_idc(ctr)) { 87 dsb(ishst); 88 } else { 89 cl_size = get_dcache_line_size(ctr); 90 91 /* Calculate end address to clean */ 92 end = (vm_offset_t)ptr + (vm_offset_t)len; 93 /* Align start address to cache line */ 94 addr = (vm_offset_t)ptr; 95 addr = rounddown2(addr, cl_size); 96 97 for (; addr < end; addr += cl_size) 98 __asm __volatile("dc civac, %0" : : "r" (addr) : 99 "memory"); 100 /* Full system DSB */ 101 dsb(ish); 102 } 103 } 104 105 void 106 cpu_inval_icache(void) 107 { 108 uint64_t ctr; 109 110 /* Accessible from all security levels */ 111 ctr = READ_SPECIALREG(ctr_el0); 112 113 if (get_cache_dic(ctr)) { 114 isb(); 115 } else { 116 __asm __volatile( 117 "ic ialluis \n" 118 "dsb ish \n" 119 "isb \n" 120 : : : "memory"); 121 } 122 } 123 124 static int 125 command_cache_flags(int argc, char *argv[]) 126 { 127 char *cp; 128 long new_flags; 129 130 if (argc == 3) { 131 if (strcmp(argv[1], "set") == 0) { 132 new_flags = strtol(argv[2], &cp, 0); 133 if (cp[0] != '\0') { 134 printf("Invalid flags\n"); 135 } else { 136 printf("Setting cache flags to %#lx\n", 137 new_flags); 138 cache_flags = new_flags; 139 return (CMD_OK); 140 } 141 } 142 } 143 144 printf("usage: cache_flags set <value>\n"); 145 return (CMD_ERROR); 146 } 147 COMMAND_SET(cache_flags, "cache_flags", "Set cache flags", command_cache_flags); 148