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