1 /*
2  * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <string.h>
8 #include "pico/bootrom.h"
9 #include "pico/bootrom/sf_table.h"
10 
11 // NOTE THIS FUNCTION TABLE IS NOT PUBLIC OR NECESSARILY COMPLETE...
12 // IT IS ***NOT*** SAFE TO CALL THESE FUNCTION POINTERS FROM ARBITRARY CODE
13 uint32_t sd_table[SF_TABLE_V2_SIZE / 2];
14 
15 #if !(PICO_DOUBLE_SUPPORT_ROM_V1 && PICO_RP2040_B0_SUPPORTED)
missing_double_func_shim(void)16 static __attribute__((noreturn)) void missing_double_func_shim(void) {
17     panic("missing double function");
18 }
19 #endif
20 extern void double_table_shim_on_use_helper(void);
21 
22 void __attribute__((weak)) *sf_clz_func;
23 
__aeabi_double_init(void)24 void __aeabi_double_init(void) {
25     int rom_version = rp2040_rom_version();
26 #if PICO_DOUBLE_SUPPORT_ROM_V1 && PICO_RP2040_B0_SUPPORTED
27     if (rom_version == 1) {
28 
29         // this is a little tricky.. we only want to pull in a shim if the corresponding function
30         // is called. to that end we include a SVC instruction with the table offset as the call number
31         // followed by the shim function pointer inside the actual wrapper function. that way if the wrapper
32         // function is garbage collected, so is the shim function.
33         //
34         // double_table_shim_on_use_helper expects this SVC instruction in the calling code soon after the address
35         // pointed to by IP and patches the double_table entry with the real shim the first time the function is called.
36         for(uint i=0; i<SF_TABLE_V2_SIZE/4; i++) {
37             sd_table[i] = (uintptr_t)double_table_shim_on_use_helper;
38         }
39     }
40 #else
41     if (rom_version == 1) {
42         // opting for soft failure for now - you'll get a panic at runtime if you call any of the missing methods
43         for(uint i=0;i<SF_TABLE_V2_SIZE/4;i++) {
44             sd_table[i] = (uintptr_t)missing_double_func_shim;
45         }
46     }
47 #endif
48     if (rom_version >= 2) {
49         void *rom_table_double = rom_data_lookup(rom_table_code('S', 'D'));
50         void *rom_table_float = rom_data_lookup(rom_table_code('S', 'F'));
51         assert(*((uint8_t *)(((void *)rom_table_float)-2)) * 4 >= SF_TABLE_V2_SIZE);
52         (void)rom_table_float;
53         memcpy(&sd_table, rom_table_double, SF_TABLE_V2_SIZE);
54         if (rom_version == 2) {
55 #ifndef NDEBUG
56             if (*(uint16_t *)0x3854 != 0xb500 || // this is dsincos(_internal)
57 
58                 *(uint16_t *)0x38d8 != 0x4649 || // this is dsin_finish
59                 *(uint16_t *)0x389c != 0x4659  // this is dcos_finish
60                     ) {
61                 panic(NULL);
62             }
63 #endif
64         }
65     }
66     if (rom_version < 3) {
67         // we use the unused entry for SINCOS
68         sd_table[SF_TABLE_V3_FSINCOS / 4] = (uintptr_t) double_table_shim_on_use_helper;
69     }
70 
71     sf_clz_func = rom_func_lookup(ROM_FUNC_CLZ32);
72 }
73