]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/RISC-V_RV32_SiFive_HiFive1_GCC/freedom-metal/src/pmp.c
Base project to replace existing Freedom Studio project using latest Freedom Studio...
[freertos] / FreeRTOS / Demo / RISC-V_RV32_SiFive_HiFive1_GCC / freedom-metal / src / pmp.c
1 /* Copyright 2018 SiFive, Inc */
2 /* SPDX-License-Identifier: Apache-2.0 */
3
4 #include <metal/machine.h>
5 #include <metal/pmp.h>
6 #include <metal/cpu.h>
7
8 #define CONFIG_TO_INT(_config) (*((size_t *) &(_config)))
9 #define INT_TO_CONFIG(_int) (*((struct metal_pmp_config *) &(_int)))
10
11 struct metal_pmp *metal_pmp_get_device(void)
12 {
13 #ifdef __METAL_DT_PMP_HANDLE
14     return __METAL_DT_PMP_HANDLE;
15 #else
16     return NULL;
17 #endif
18 }
19
20 /* This function calculates the minimum granularity from the address
21  * that pmpaddr takes on after writing all ones to pmpaddr when pmpcfg = 0.
22  *
23  * Detect the address granularity based on the position of the
24  * least-significant 1 set in the address.
25  *
26  * For example, if the value read from pmpaddr is 0x3ffffc00, the
27  * least-significant set bit is in bit 10 (counting from 0), resulting
28  * in a detected granularity of 2^(10 + 2) = 4096.
29  */
30 static uintptr_t _get_detected_granularity(uintptr_t address) {
31     if(address == 0) {
32         return (uintptr_t) -1;
33     }
34
35     /* Get the index of the least significant set bit */
36     int index = 0;
37     while(((address >> index) & 0x1) == 0) {
38         index += 1;
39     }
40
41     /* The granularity is equal to 2^(index + 2) bytes */
42     return (1 << (index + 2));
43 }
44
45 /* This function calculates the granularity requested by the user's provided
46  * value for pmpaddr.
47  *
48  * Calculate the requested granularity based on the position of the
49  * least-significant unset bit.
50  *
51  * For example, if the requested address is 0x20009ff, the least-significant
52  * unset bit is at index 9 (counting from 0), resulting in a requested
53  * granularity of 2^(9 + 3) = 4096.
54  */
55 static uintptr_t _get_pmpaddr_granularity(uintptr_t address) {
56     /* Get the index of the least significant unset bit */
57     int index = 0;
58     while(((address >> index) & 0x1) == 1) {
59         index += 1;
60     }
61
62     /* The granularity is equal to 2^(index + 3) bytes */
63     return (1 << (index + 3));
64 }
65
66 /* Get the number of pmp regions for the current hart */
67 static int _pmp_regions() {
68     struct metal_cpu *current_cpu = metal_cpu_get(metal_cpu_get_current_hartid());
69
70     return __metal_driver_cpu_num_pmp_regions(current_cpu);
71 }
72
73
74 void metal_pmp_init(struct metal_pmp *pmp) {
75     if(!pmp) {
76         return;
77     }
78
79     struct metal_pmp_config init_config = {
80         .L = METAL_PMP_UNLOCKED,
81         .A = METAL_PMP_OFF,
82         .X = 0,
83         .W = 0,
84         .R = 0,
85     };
86
87     for(unsigned int i = 0; i < _pmp_regions(); i++) {
88         metal_pmp_set_region(pmp, i, init_config, 0);
89     }
90
91     /* Detect the region granularity by writing all 1s to pmpaddr0 while
92      * pmpcfg0 = 0. */
93     if(metal_pmp_set_address(pmp, 0, -1) != 0) {
94         /* Failed to detect granularity */
95         return;
96     }
97
98     /* Calculate the granularity based on the value that pmpaddr0 takes on */
99     pmp->_granularity[metal_cpu_get_current_hartid()] = _get_detected_granularity(metal_pmp_get_address(pmp, 0));
100
101     /* Clear pmpaddr0 */
102     metal_pmp_set_address(pmp, 0, 0);
103 }
104
105 int metal_pmp_set_region(struct metal_pmp *pmp,
106                        unsigned int region,
107                        struct metal_pmp_config config,
108                        size_t address)
109 {
110     struct metal_pmp_config old_config;
111     size_t old_address;
112     size_t cfgmask;
113     size_t pmpcfg;
114     int rc = 0;
115
116     if(!pmp) {
117         /* Device handle cannot be NULL */
118         return 1;
119     }
120
121     if(region > _pmp_regions()) {
122         /* Region outside of supported range */
123         return 2;
124     }
125
126     if(config.A == METAL_PMP_NA4 && pmp->_granularity[metal_cpu_get_current_hartid()] > 4) {
127         /* The requested granularity is too small */
128         return 3;
129     }
130
131     if(config.A == METAL_PMP_NAPOT &&
132        pmp->_granularity[metal_cpu_get_current_hartid()] > _get_pmpaddr_granularity(address))
133     {
134         /* The requested granularity is too small */
135         return 3;
136     }
137
138     rc = metal_pmp_get_region(pmp, region, &old_config, &old_address);
139     if(rc) {
140         /* Error reading region */
141         return rc;
142     }
143
144     if(old_config.L == METAL_PMP_LOCKED) {
145         /* Cannot modify locked region */
146         return 4;
147     }
148
149     /* Update the address first, because if the region is being locked we won't
150      * be able to modify it after we set the config */
151     if(old_address != address) {
152         switch(region) {
153         case 0:
154             asm("csrw pmpaddr0, %[addr]"
155                     :: [addr] "r" (address) :);
156             break;
157         case 1:
158             asm("csrw pmpaddr1, %[addr]"
159                     :: [addr] "r" (address) :);
160             break;
161         case 2:
162             asm("csrw pmpaddr2, %[addr]"
163                     :: [addr] "r" (address) :);
164             break;
165         case 3:
166             asm("csrw pmpaddr3, %[addr]"
167                     :: [addr] "r" (address) :);
168             break;
169         case 4:
170             asm("csrw pmpaddr4, %[addr]"
171                     :: [addr] "r" (address) :);
172             break;
173         case 5:
174             asm("csrw pmpaddr5, %[addr]"
175                     :: [addr] "r" (address) :);
176             break;
177         case 6:
178             asm("csrw pmpaddr6, %[addr]"
179                     :: [addr] "r" (address) :);
180             break;
181         case 7:
182             asm("csrw pmpaddr7, %[addr]"
183                     :: [addr] "r" (address) :);
184             break;
185         case 8:
186             asm("csrw pmpaddr8, %[addr]"
187                     :: [addr] "r" (address) :);
188             break;
189         case 9:
190             asm("csrw pmpaddr9, %[addr]"
191                     :: [addr] "r" (address) :);
192             break;
193         case 10:
194             asm("csrw pmpaddr10, %[addr]"
195                     :: [addr] "r" (address) :);
196             break;
197         case 11:
198             asm("csrw pmpaddr11, %[addr]"
199                     :: [addr] "r" (address) :);
200             break;
201         case 12:
202             asm("csrw pmpaddr12, %[addr]"
203                     :: [addr] "r" (address) :);
204             break;
205         case 13:
206             asm("csrw pmpaddr13, %[addr]"
207                     :: [addr] "r" (address) :);
208             break;
209         case 14:
210             asm("csrw pmpaddr14, %[addr]"
211                     :: [addr] "r" (address) :);
212             break;
213         case 15:
214             asm("csrw pmpaddr15, %[addr]"
215                     :: [addr] "r" (address) :);
216             break;
217         }
218     }
219
220 #if __riscv_xlen==32
221     if(CONFIG_TO_INT(old_config) != CONFIG_TO_INT(config)) {
222         /* Mask to clear old pmpcfg */
223         cfgmask = (0xFF << (8 * (region % 4)) );
224         pmpcfg = (CONFIG_TO_INT(config) << (8 * (region % 4)) );
225         
226         switch(region / 4) {
227         case 0:
228             asm("csrc pmpcfg0, %[mask]"
229                     :: [mask] "r" (cfgmask) :);
230
231             asm("csrs pmpcfg0, %[cfg]"
232                     :: [cfg] "r" (pmpcfg) :);
233             break;
234         case 1:
235             asm("csrc pmpcfg1, %[mask]"
236                     :: [mask] "r" (cfgmask) :);
237
238             asm("csrs pmpcfg1, %[cfg]"
239                     :: [cfg] "r" (pmpcfg) :);
240             break;
241         case 2:
242             asm("csrc pmpcfg2, %[mask]"
243                     :: [mask] "r" (cfgmask) :);
244
245             asm("csrs pmpcfg2, %[cfg]"
246                     :: [cfg] "r" (pmpcfg) :);
247             break;
248         case 3:
249             asm("csrc pmpcfg3, %[mask]"
250                     :: [mask] "r" (cfgmask) :);
251
252             asm("csrs pmpcfg3, %[cfg]"
253                     :: [cfg] "r" (pmpcfg) :);
254             break;
255         }
256     }
257 #elif __riscv_xlen==64
258     if(CONFIG_TO_INT(old_config) != CONFIG_TO_INT(config)) {
259         /* Mask to clear old pmpcfg */
260         cfgmask = (0xFF << (8 * (region % 8)) );
261         pmpcfg = (CONFIG_TO_INT(config) << (8 * (region % 8)) );
262         
263         switch(region / 8) {
264         case 0:
265             asm("csrc pmpcfg0, %[mask]"
266                     :: [mask] "r" (cfgmask) :);
267
268             asm("csrs pmpcfg0, %[cfg]"
269                     :: [cfg] "r" (pmpcfg) :);
270             break;
271         case 1:
272             asm("csrc pmpcfg2, %[mask]"
273                     :: [mask] "r" (cfgmask) :);
274
275             asm("csrs pmpcfg2, %[cfg]"
276                     :: [cfg] "r" (pmpcfg) :);
277             break;
278         }
279     }
280 #else
281 #error XLEN is not set to supported value for PMP driver
282 #endif
283
284     return 0;
285 }
286
287 int metal_pmp_get_region(struct metal_pmp *pmp,
288                        unsigned int region,
289                        struct metal_pmp_config *config,
290                        size_t *address)
291 {
292     size_t pmpcfg = 0;
293
294     if(!pmp || !config || !address) {
295         /* NULL pointers are invalid arguments */
296         return 1;
297     }
298
299     if(region > _pmp_regions()) {
300         /* Region outside of supported range */
301         return 2;
302     }
303
304 #if __riscv_xlen==32
305     switch(region / 4) {
306     case 0:
307         asm("csrr %[cfg], pmpcfg0"
308                 : [cfg] "=r" (pmpcfg) ::);
309         break;
310     case 1:
311         asm("csrr %[cfg], pmpcfg1"
312                 : [cfg] "=r" (pmpcfg) ::);
313         break;
314     case 2:
315         asm("csrr %[cfg], pmpcfg2"
316                 : [cfg] "=r" (pmpcfg) ::);
317         break;
318     case 3:
319         asm("csrr %[cfg], pmpcfg3"
320                 : [cfg] "=r" (pmpcfg) ::);
321         break;
322     }
323
324     pmpcfg = (0xFF & (pmpcfg >> (8 * (region % 4)) ) );
325
326 #elif __riscv_xlen==64
327     switch(region / 8) {
328     case 0:
329         asm("csrr %[cfg], pmpcfg0"
330                 : [cfg] "=r" (pmpcfg) ::);
331         break;
332     case 1:
333         asm("csrr %[cfg], pmpcfg2"
334                 : [cfg] "=r" (pmpcfg) ::);
335         break;
336     }
337
338     pmpcfg = (0xFF & (pmpcfg >> (8 * (region % 8)) ) );
339
340 #else
341 #error XLEN is not set to supported value for PMP driver
342 #endif
343
344     *config = INT_TO_CONFIG(pmpcfg);
345
346     switch(region) {
347     case 0:
348         asm("csrr %[addr], pmpaddr0"
349                 : [addr] "=r" (*address) ::);
350         break;
351     case 1:
352         asm("csrr %[addr], pmpaddr1"
353                 : [addr] "=r" (*address) ::);
354         break;
355     case 2:
356         asm("csrr %[addr], pmpaddr2"
357                 : [addr] "=r" (*address) ::);
358         break;
359     case 3:
360         asm("csrr %[addr], pmpaddr3"
361                 : [addr] "=r" (*address) ::);
362         break;
363     case 4:
364         asm("csrr %[addr], pmpaddr4"
365                 : [addr] "=r" (*address) ::);
366         break;
367     case 5:
368         asm("csrr %[addr], pmpaddr5"
369                 : [addr] "=r" (*address) ::);
370         break;
371     case 6:
372         asm("csrr %[addr], pmpaddr6"
373                 : [addr] "=r" (*address) ::);
374         break;
375     case 7:
376         asm("csrr %[addr], pmpaddr7"
377                 : [addr] "=r" (*address) ::);
378         break;
379     case 8:
380         asm("csrr %[addr], pmpaddr8"
381                 : [addr] "=r" (*address) ::);
382         break;
383     case 9:
384         asm("csrr %[addr], pmpaddr9"
385                 : [addr] "=r" (*address) ::);
386         break;
387     case 10:
388         asm("csrr %[addr], pmpaddr10"
389                 : [addr] "=r" (*address) ::);
390         break;
391     case 11:
392         asm("csrr %[addr], pmpaddr11"
393                 : [addr] "=r" (*address) ::);
394         break;
395     case 12:
396         asm("csrr %[addr], pmpaddr12"
397                 : [addr] "=r" (*address) ::);
398         break;
399     case 13:
400         asm("csrr %[addr], pmpaddr13"
401                 : [addr] "=r" (*address) ::);
402         break;
403     case 14:
404         asm("csrr %[addr], pmpaddr14"
405                 : [addr] "=r" (*address) ::);
406         break;
407     case 15:
408         asm("csrr %[addr], pmpaddr15"
409                 : [addr] "=r" (*address) ::);
410         break;
411     }
412
413     return 0;
414 }
415
416 int metal_pmp_lock(struct metal_pmp *pmp, unsigned int region)
417 {
418     struct metal_pmp_config config;
419     size_t address;
420     int rc = 0;
421
422     rc = metal_pmp_get_region(pmp, region, &config, &address);
423     if(rc) {
424         return rc;
425     }
426
427     if(config.L == METAL_PMP_LOCKED) {
428         return 0;
429     }
430
431     config.L = METAL_PMP_LOCKED;
432
433     rc = metal_pmp_set_region(pmp, region, config, address);
434
435     return rc;
436 }
437
438
439 int metal_pmp_set_address(struct metal_pmp *pmp, unsigned int region, size_t address)
440 {
441     struct metal_pmp_config config;
442     size_t old_address;
443     int rc = 0;
444
445     rc = metal_pmp_get_region(pmp, region, &config, &old_address);
446     if(rc) {
447         return rc;
448     }
449
450     rc = metal_pmp_set_region(pmp, region, config, address);
451
452     return rc;
453 }
454
455 size_t metal_pmp_get_address(struct metal_pmp *pmp, unsigned int region)
456 {
457     struct metal_pmp_config config;
458     size_t address = 0;
459
460     metal_pmp_get_region(pmp, region, &config, &address);
461
462     return address;
463 }
464
465
466 int metal_pmp_set_address_mode(struct metal_pmp *pmp, unsigned int region, enum metal_pmp_address_mode mode)
467 {
468     struct metal_pmp_config config;
469     size_t address;
470     int rc = 0;
471
472     rc = metal_pmp_get_region(pmp, region, &config, &address);
473     if(rc) {
474         return rc;
475     }
476
477     config.A = mode;
478
479     rc = metal_pmp_set_region(pmp, region, config, address);
480
481     return rc;
482 }
483
484 enum metal_pmp_address_mode metal_pmp_get_address_mode(struct metal_pmp *pmp, unsigned int region)
485 {
486     struct metal_pmp_config config;
487     size_t address = 0;
488
489     metal_pmp_get_region(pmp, region, &config, &address);
490
491     return config.A;
492 }
493
494
495 int metal_pmp_set_executable(struct metal_pmp *pmp, unsigned int region, int X)
496 {
497     struct metal_pmp_config config;
498     size_t address;
499     int rc = 0;
500
501     rc = metal_pmp_get_region(pmp, region, &config, &address);
502     if(rc) {
503         return rc;
504     }
505
506     config.X = X;
507
508     rc = metal_pmp_set_region(pmp, region, config, address);
509
510     return rc;
511 }
512
513 int metal_pmp_get_executable(struct metal_pmp *pmp, unsigned int region)
514 {
515     struct metal_pmp_config config;
516     size_t address = 0;
517
518     metal_pmp_get_region(pmp, region, &config, &address);
519
520     return config.X;
521 }
522
523
524 int metal_pmp_set_writeable(struct metal_pmp *pmp, unsigned int region, int W)
525 {
526     struct metal_pmp_config config;
527     size_t address;
528     int rc = 0;
529
530     rc = metal_pmp_get_region(pmp, region, &config, &address);
531     if(rc) {
532         return rc;
533     }
534
535     config.W = W;
536
537     rc = metal_pmp_set_region(pmp, region, config, address);
538
539     return rc;
540 }
541
542 int metal_pmp_get_writeable(struct metal_pmp *pmp, unsigned int region)
543 {
544     struct metal_pmp_config config;
545     size_t address = 0;
546
547     metal_pmp_get_region(pmp, region, &config, &address);
548
549     return config.W;
550 }
551
552
553 int metal_pmp_set_readable(struct metal_pmp *pmp, unsigned int region, int R)
554 {
555     struct metal_pmp_config config;
556     size_t address;
557     int rc = 0;
558
559     rc = metal_pmp_get_region(pmp, region, &config, &address);
560     if(rc) {
561         return rc;
562     }
563
564     config.R = R;
565
566     rc = metal_pmp_set_region(pmp, region, config, address);
567
568     return rc;
569 }
570
571 int metal_pmp_get_readable(struct metal_pmp *pmp, unsigned int region)
572 {
573     struct metal_pmp_config config;
574     size_t address = 0;
575
576     metal_pmp_get_region(pmp, region, &config, &address);
577
578     return config.R;
579 }
580