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