1 /*
2  * Copyright (c) 2024 Travis Geiselbrecht
3  *
4  * Use of this source code is governed by a MIT-style
5  * license that can be found in the LICENSE file or at
6  * https://opensource.org/licenses/MIT
7  */
8 #include "arch/riscv/feature.h"
9 
10 #include <lk/trace.h>
11 #include <lk/debug.h>
12 #include <string.h>
13 #include <stdlib.h>
14 #include <ctype.h>
15 
16 #define LOCAL_TRACE 0
17 
18 // Make feature bitmap
19 uint32_t riscv_feature_bitmap[ROUNDUP(RISCV_FEAT_COUNT, 32) / 32];
20 
set_feature(enum riscv_feature feature)21 static void set_feature(enum riscv_feature feature) {
22     riscv_feature_bitmap[feature / 32] |= (1U << feature % 32);
23 }
24 
25 // match a one word feature from a known list
match_feature(const char * str,size_t start,size_t end)26 static void match_feature(const char *str, size_t start, size_t end) {
27     const struct {
28         const char *str;
29         enum riscv_feature feat;
30     } oneword[] = {
31         { "zba", RISCV_FEAT_ZBA },
32         { "zbb", RISCV_FEAT_ZBB },
33         { "zbc", RISCV_FEAT_ZBC },
34         { "zbs", RISCV_FEAT_ZBS },
35         { "zicbom", RISCV_FEAT_ZICBOM },
36         { "zicbop", RISCV_FEAT_ZICBOP },
37         { "zicboz", RISCV_FEAT_ZICBOZ },
38         { "sstc", RISCV_FEAT_SSTC },
39         { "svadu", RISCV_FEAT_SVADU },
40         { "zicsr", RISCV_FEAT_ZICSR },
41         { "zifencei", RISCV_FEAT_ZIFENCEI },
42     };
43 
44     if (LOCAL_TRACE) {
45         char feat[128];
46         strlcpy(feat, &str[start], end - start + 1);
47         printf("feature '%s'\n", feat);
48     }
49 
50     for (size_t i = 0; i < countof(oneword); i++) {
51         if (strlen(oneword[i].str) != end - start)
52             continue;
53 
54         if (strncasecmp(oneword[i].str, &str[start], end - start) == 0) {
55             dprintf(INFO, "riscv: found feature '%s'\n", oneword[i].str);
56             set_feature(oneword[i].feat);
57         }
58     }
59 }
60 
riscv_set_isa_string(const char * str)61 void riscv_set_isa_string(const char *str) {
62     LTRACEF("%s\n", str);
63 
64     const size_t slen = strlen(str);
65 
66     // Handle simple features first.
67     // Feature string must start with either 'rv64' or 'rv32' followed by
68     // one or more one character features until _ or null.
69     if (slen < 4 || str[0] != 'r' || str[1] != 'v') {
70         return;
71     }
72 
73     // We're going to continue, so wipe out the existing default feature list
74     memset(riscv_feature_bitmap, 0, 4 * countof(riscv_feature_bitmap));
75 
76     size_t pos = 4;
77     while (str[pos] != 0 && str[pos] != '_') {
78         bool found = true;
79         switch (tolower(str[pos])) {
80             // TODO: make sure this is the complete list
81             case 'i': set_feature(RISCV_FEAT_I); break;
82             case 'm': set_feature(RISCV_FEAT_M); break;
83             case 'a': set_feature(RISCV_FEAT_A); break;
84             case 'q': set_feature(RISCV_FEAT_Q);
85                 // fallthrough
86             case 'd':
87 feat_d:
88                 set_feature(RISCV_FEAT_D);
89                 // fallthrough
90             case 'f': set_feature(RISCV_FEAT_F); break;
91             case 'c': set_feature(RISCV_FEAT_C); break;
92             case 'b': set_feature(RISCV_FEAT_B); break;
93             case 'p': set_feature(RISCV_FEAT_P); break;
94             case 'h': set_feature(RISCV_FEAT_H); break;
95             case 'v':
96                 set_feature(RISCV_FEAT_V);
97                 goto feat_d;
98             case 'g':
99                 // 'g' is a special case that implies IMAFDZisr_Zifenci
100                 set_feature(RISCV_FEAT_I);
101                 set_feature(RISCV_FEAT_M);
102                 set_feature(RISCV_FEAT_A);
103                 set_feature(RISCV_FEAT_ZICSR);
104                 set_feature(RISCV_FEAT_ZIFENCEI);
105                 goto feat_d;
106             default:
107                 found = false;
108         }
109         if (found) {
110             dprintf(INFO, "riscv: found feature '%c'\n", tolower(str[pos]));
111         }
112 
113         pos++;
114     }
115 
116     // walk the one-word features
117     bool in_word = false;
118     size_t start;
119     for (; pos <= slen; pos++) {
120         if (!in_word) {
121             if (str[pos] == '_') {
122                 continue;
123             } else if (str[pos] == 0) {
124                 break;
125             }
126             start = pos;
127             in_word = true;
128         } else {
129             // we're in a word
130             if (str[pos] == '_' || str[pos] == 0) {
131                 // end of word
132                 in_word = false;
133 
134                 // process the feature word, between str[start...pos]
135                 match_feature(str, start, pos);
136             }
137         }
138     }
139 }
140 
riscv_feature_early_init(void)141 void riscv_feature_early_init(void) {
142     // set the default features based on the compiler switches
143 #if __riscv_i
144     set_feature(RISCV_FEAT_I);
145 #endif
146 #if __riscv_m
147     set_feature(RISCV_FEAT_M);
148 #endif
149 #if __riscv_a
150     set_feature(RISCV_FEAT_A);
151 #endif
152 #if __riscv_f
153     set_feature(RISCV_FEAT_F);
154 #endif
155 #if __riscv_d
156     set_feature(RISCV_FEAT_D);
157 #endif
158 #if __riscv_q
159     set_feature(RISCV_FEAT_Q);
160 #endif
161 #if __riscv_c
162     set_feature(RISCV_FEAT_C);
163 #endif
164 #if __riscv_zba
165     set_feature(RISCV_FEAT_ZBA);
166 #endif
167 #if __riscv_zbb
168     set_feature(RISCV_FEAT_ZBB);
169 #endif
170 #if __riscv_zba
171     set_feature(RISCV_FEAT_ZBA);
172 #endif
173 #if __riscv_zbc
174     set_feature(RISCV_FEAT_ZBC);
175 #endif
176 #if __riscv_zbs
177     set_feature(RISCV_FEAT_ZBS);
178 #endif
179 }
180 
riscv_feature_to_string(enum riscv_feature feature)181 const char *riscv_feature_to_string(enum riscv_feature feature) {
182     switch (feature) {
183         case RISCV_FEAT_I: return "i";
184         case RISCV_FEAT_M: return "m";
185         case RISCV_FEAT_A: return "a";
186         case RISCV_FEAT_F: return "f";
187         case RISCV_FEAT_D: return "d";
188         case RISCV_FEAT_Q: return "q";
189         case RISCV_FEAT_C: return "c";
190         case RISCV_FEAT_B: return "b";
191         case RISCV_FEAT_P: return "p";
192         case RISCV_FEAT_V: return "v";
193         case RISCV_FEAT_H: return "h";
194         case RISCV_FEAT_ZBA: return "zba";
195         case RISCV_FEAT_ZBB: return "zbb";
196         case RISCV_FEAT_ZBC: return "zbc";
197         case RISCV_FEAT_ZBS: return "zbs";
198         case RISCV_FEAT_ZICBOM: return "zicbom";
199         case RISCV_FEAT_ZICBOP: return "zicbop";
200         case RISCV_FEAT_ZICBOZ: return "zicboz";
201         case RISCV_FEAT_ZICSR: return "zicsr";
202         case RISCV_FEAT_ZIFENCEI: return "zifencei";
203         case RISCV_FEAT_SSTC: return "sstc";
204         case RISCV_FEAT_SVADU: return "svadu";
205 
206         // keep this in so the compiler warns if something is missing
207         case RISCV_FEAT_COUNT: return "";
208     }
209     return "";
210 }
211 
riscv_feature_init(void)212 void riscv_feature_init(void) {
213     dprintf(INFO, "RISCV: detected features");
214     for (size_t i = 0; i < countof(riscv_feature_bitmap); i++) {
215         for (size_t j = 0; j < sizeof(riscv_feature_bitmap[i]) * 8; j++) {
216             if (riscv_feature_bitmap[i] & (1U << j)) {
217                 dprintf(INFO, " %s", riscv_feature_to_string(i * 32 + j));
218             }
219         }
220     }
221     dprintf(INFO, "\n");
222 }