:: limine / common / sys / cpu_riscv.c 12.5 KB raw

1
2
#if defined(__riscv)
3
4
#include <lib/acpi.h>
5
#include <lib/misc.h>
6
#include <lib/print.h>
7
#include <lib/config.h>
8
#include <sys/cpu.h>
9
#include <mm/pmm.h>
10
#include <stddef.h>
11
#include <stdint.h>
12
#include <libfdt.h>
13
14
// ACPI RISC-V Hart Capabilities Table
15
struct rhct {
16
    struct sdt header;
17
    uint32_t flags;
18
    uint64_t time_base_frequency;
19
    uint32_t nodes_len;
20
    uint32_t nodes_offset;
21
    uint8_t nodes[];
22
} __attribute__((packed));
23
24
#define RHCT_ISA_STRING 0
25
#define RHCT_CMO        1
26
#define RHCT_MMU        2
27
#define RHCT_HART_INFO  65535
28
29
struct rhct_header {
30
    uint16_t type;      // node type
31
    uint16_t size;      // node size (bytes)
32
    uint16_t revision;  // node revision
33
} __attribute__((packed));
34
35
// One `struct rhct_hart_info` structure exists per hart in the system.
36
// The `offsets` array points to other entries in the RHCT associated with the
37
// hart.
38
struct rhct_hart_info {
39
    struct rhct_header header;
40
    uint16_t offsets_len;
41
    uint32_t acpi_processor_uid;
42
    uint32_t offsets[];
43
} __attribute__((packed));
44
45
struct rhct_isa_string {
46
    struct rhct_header header;
47
    uint16_t isa_string_len;
48
    const char isa_string[];
49
} __attribute__((packed));
50
51
#define RISCV_MMU_TYPE_SV39 0
52
#define RISCV_MMU_TYPE_SV48 1
53
#define RISCV_MMU_TYPE_SV57 2
54
55
struct rhct_mmu {
56
    struct rhct_header header;
57
    uint8_t reserved0;
58
    uint8_t mmu_type;
59
} __attribute__((packed));
60
61
void *riscv_fdt = NULL;
62
63
size_t bsp_hartid;
64
struct riscv_hart *hart_list = NULL;
65
struct riscv_hart *bsp_hart;
66
static const char *current_config = NULL;
67
68
static uint64_t cached_time_base_freq = 0;
69
70
uint64_t riscv_time_base_frequency(void) {
71
    return cached_time_base_freq;
72
}
73
74
static struct riscv_hart *riscv_get_hart(size_t hartid) {
75
    for (struct riscv_hart *hart = hart_list; hart != NULL; hart = hart->next) {
76
        if (hart->hartid == hartid) {
77
            return hart;
78
        }
79
    }
80
    panic(false, "no `struct riscv_hart` for hartid %U", (uint64_t)hartid);
81
}
82
83
static inline struct rhct_hart_info *rhct_get_hart_info(struct rhct *rhct, uint32_t acpi_uid) {
84
    uint32_t offset = rhct->nodes_offset;
85
    for (uint32_t i = 0; i < rhct->nodes_len; i++) {
86
        if (offset + sizeof(struct rhct_header) > rhct->header.length) {
87
            return NULL;
88
        }
89
        struct rhct_hart_info *node = (void *)((uintptr_t)rhct + offset);
90
        if (node->header.type == RHCT_HART_INFO && node->acpi_processor_uid == acpi_uid) {
91
            return node;
92
        }
93
        if (node->header.size == 0) {
94
            return NULL;
95
        }
96
        offset += node->header.size;
97
    }
98
    return NULL;
99
}
100
101
static void init_riscv_acpi(void) {
102
    struct madt *madt = acpi_get_table("APIC", 0);
103
    struct rhct *rhct = acpi_get_table("RHCT", 0);
104
    if (madt == NULL || rhct == NULL) {
105
        panic(false, "riscv: requires `APIC` and `RHCT` ACPI tables");
106
    }
107
108
    cached_time_base_freq = rhct->time_base_frequency;
109
110
    for (uint8_t *madt_ptr = (uint8_t *)madt->madt_entries_begin;
111
         (uintptr_t)madt_ptr + 1 < (uintptr_t)madt + madt->header.length; madt_ptr += *(madt_ptr + 1)) {
112
        if (*(madt_ptr + 1) == 0) {
113
            break;
114
        }
115
        if (*madt_ptr != 0x18) {
116
            continue;
117
        }
118
        struct madt_riscv_intc *intc = (struct madt_riscv_intc *)madt_ptr;
119
120
        // Ignore harts we can't do anything with.
121
        if (!(intc->flags & MADT_RISCV_INTC_ENABLED ||
122
                intc->flags & MADT_RISCV_INTC_ONLINE_CAPABLE)) {
123
            continue;
124
        }
125
126
        uint32_t acpi_uid = intc->acpi_processor_uid;
127
        size_t hartid = intc->hartid;
128
129
        struct rhct_hart_info *hart_info = rhct_get_hart_info(rhct, acpi_uid);
130
        if (hart_info == NULL) {
131
            panic(false, "riscv: missing rhct node for hartid %U", (uint64_t)hartid);
132
        }
133
134
        // Ensure the offsets[] array fits within the hart_info node as
135
        // declared by the containing header.size.
136
        uint64_t offsets_bytes = (uint64_t)hart_info->offsets_len * sizeof(uint32_t);
137
        if (offsetof(struct rhct_hart_info, offsets) + offsets_bytes > hart_info->header.size) {
138
            panic(false, "riscv: RHCT hart_info offsets_len exceeds node size");
139
        }
140
141
        const char *isa_string = NULL;
142
        uint8_t mmu_type = 0;
143
        uint8_t flags = 0;
144
145
        for (uint32_t i = 0; i < hart_info->offsets_len; i++) {
146
            uint32_t node_offset = hart_info->offsets[i];
147
            if (node_offset + sizeof(struct rhct_header) > rhct->header.length) {
148
                continue;
149
            }
150
            const struct rhct_header *node = (void *)((uintptr_t)rhct + node_offset);
151
            if (node->size < sizeof(struct rhct_header) ||
152
                node_offset + node->size > rhct->header.length) {
153
                continue;
154
            }
155
            switch (node->type) {
156
                case RHCT_ISA_STRING: {
157
                    if (node->size < sizeof(struct rhct_isa_string))
158
                        break;
159
                    struct rhct_isa_string *isa_node = (struct rhct_isa_string *)node;
160
                    // Validate string is within node bounds and null-terminated
161
                    uint16_t max_str_len = node->size - sizeof(struct rhct_isa_string);
162
                    if (isa_node->isa_string_len > max_str_len)
163
                        break;
164
                    if (isa_node->isa_string_len == 0 ||
165
                        isa_node->isa_string[isa_node->isa_string_len - 1] != '\0')
166
                        break;
167
                    isa_string = isa_node->isa_string;
168
                    break;
169
                }
170
                case RHCT_MMU:
171
                    if (node->size < sizeof(struct rhct_mmu))
172
                        break;
173
                    mmu_type = ((struct rhct_mmu *)node)->mmu_type;
174
                    flags |= RISCV_HART_HAS_MMU;
175
                    break;
176
            }
177
        }
178
179
        if (isa_string == NULL) {
180
            print("riscv: missing isa string for hartid %U, skipping.\n", (uint64_t)hartid);
181
            continue;
182
        }
183
184
        if (strncmp("rv64", isa_string, 4) && strncmp("rv32", isa_string, 4)) {
185
            print("riscv: skipping hartid %U with invalid isa string: %s\n", (uint64_t)hartid, isa_string);
186
            continue;
187
        }
188
189
        struct riscv_hart *hart = ext_mem_alloc(sizeof(struct riscv_hart));
190
        if (hart == NULL) {
191
            panic(false, "out of memory");
192
        }
193
194
        hart->hartid = hartid;
195
        hart->acpi_uid = acpi_uid;
196
        hart->isa_string = isa_string;
197
        hart->mmu_type = mmu_type;
198
        hart->flags = flags;
199
200
        hart->next = hart_list;
201
        hart_list = hart;
202
203
        if (hart->hartid == bsp_hartid) {
204
            bsp_hart = hart;
205
        }
206
    }
207
}
208
209
static void init_riscv_fdt(const void *fdt) {
210
    if (fdt_check_header(fdt)) {
211
        panic(false, "riscv: invalid device tree");
212
    }
213
214
    int cpus = fdt_path_offset(fdt, "/cpus");
215
    if (cpus < 0) {
216
        panic(false, "riscv: missing `/cpus` node");
217
    }
218
219
    int len;
220
    const void *tbf = fdt_getprop(fdt, cpus, "timebase-frequency", &len);
221
    if (tbf != NULL) {
222
        if (len == 8) {
223
            cached_time_base_freq = fdt64_ld(tbf);
224
        } else if (len == 4) {
225
            cached_time_base_freq = fdt32_ld(tbf);
226
        }
227
    }
228
229
    int node;
230
    fdt_for_each_subnode(node, fdt, cpus) {
231
        const void *prop;
232
233
        if (!(prop = fdt_getprop(fdt, node, "device_type", NULL)) || strcmp(prop, "cpu")) {
234
            continue;
235
        }
236
237
        if (!(prop = fdt_getprop(fdt, node, "reg", NULL))) {
238
            continue;
239
        }
240
        size_t hartid = fdt32_ld(prop);
241
242
        uint8_t flags = 0;
243
        uint8_t mmu_type = 0;
244
        if ((prop = fdt_getprop(fdt, node, "mmu-type", NULL))) {
245
            if (!strcmp(prop, "riscv,sv39")) {
246
                mmu_type = RISCV_MMU_TYPE_SV39;
247
                flags |= RISCV_HART_HAS_MMU;
248
            } else if (!strcmp(prop, "riscv,sv48")) {
249
                mmu_type = RISCV_MMU_TYPE_SV48;
250
                flags |= RISCV_HART_HAS_MMU;
251
            } else if (!strcmp(prop, "riscv,sv57")) {
252
                mmu_type = RISCV_MMU_TYPE_SV57;
253
                flags |= RISCV_HART_HAS_MMU;
254
            }
255
        }
256
257
        const char *isa_string = fdt_getprop(fdt, node, "riscv,isa", NULL);
258
        if (isa_string == NULL) {
259
            print("riscv: missing isa string for hartid %U, skipping.\n", (uint64_t)hartid);
260
            continue;
261
        }
262
263
        if (strncmp("rv64", isa_string, 4) && strncmp("rv32", isa_string, 4)) {
264
            print("riscv: skipping hartid %U with invalid isa string: %s\n", (uint64_t)hartid, isa_string);
265
            continue;
266
        }
267
268
        struct riscv_hart *hart = ext_mem_alloc(sizeof(struct riscv_hart));
269
        if (hart == NULL) {
270
            panic(false, "out of memory");
271
        }
272
273
        hart->hartid = hartid;
274
        hart->acpi_uid = 0;
275
        hart->isa_string = isa_string;
276
        hart->mmu_type = mmu_type;
277
        hart->flags = flags;
278
279
        hart->next = hart_list;
280
        hart_list = hart;
281
282
        if (hart->hartid == bsp_hartid) {
283
            bsp_hart = hart;
284
        }
285
    }
286
}
287
288
void init_riscv(const char *config) {
289
    if (current_config == config && hart_list != NULL) {
290
        return;
291
    }
292
293
    while (hart_list != NULL) {
294
        void *cur_hart = hart_list;
295
        hart_list = hart_list->next;
296
        pmm_free(cur_hart, sizeof(struct riscv_hart));
297
    }
298
    bsp_hart = NULL;
299
300
    if (riscv_fdt != NULL) {
301
        pmm_free(riscv_fdt, fdt_totalsize(riscv_fdt));
302
        riscv_fdt = NULL;
303
    }
304
305
    bool prioritise_dtb = false;
306
    if (config != NULL) {
307
        prioritise_dtb = config_get_value(config, 0, "dtb_path");
308
    }
309
    if (!prioritise_dtb) {
310
        prioritise_dtb = config_get_value(NULL, 0, "global_dtb");
311
    }
312
313
    if (!prioritise_dtb && acpi_get_rsdp()) {
314
        init_riscv_acpi();
315
    } else {
316
        riscv_fdt = get_device_tree_blob(config, 0, false);
317
        if (riscv_fdt != NULL) {
318
            init_riscv_fdt(riscv_fdt);
319
        } else {
320
            panic(false, "riscv: requires DTB or ACPI");
321
        }
322
    }
323
324
    if (cached_time_base_freq != 0) {
325
        tsc_freq = cached_time_base_freq;
326
    }
327
328
    if (bsp_hart == NULL) {
329
        panic(false, "riscv: missing `struct riscv_hart` for BSP");
330
    }
331
332
    if (strncasecmp(bsp_hart->isa_string, "rv64i", 5)) {
333
        panic(false, "unsupported cpu: %s", bsp_hart->isa_string);
334
    }
335
336
    for (struct riscv_hart *hart = hart_list; hart != NULL; hart = hart->next) {
337
        if (hart != bsp_hart && strcmp(bsp_hart->isa_string, hart->isa_string)) {
338
            hart->flags |= RISCV_HART_COPROC;
339
        }
340
    }
341
342
    current_config = config;
343
}
344
345
struct isa_extension {
346
    const char *name;
347
    size_t name_len;
348
    uint32_t ver_maj;
349
    uint32_t ver_min;
350
};
351
352
// Parse the next sequence of digit characters into an integer.
353
static bool parse_number(const char **s, size_t *_n) {
354
    size_t n = 0;
355
    bool parsed = false;
356
    while (isdigit(**s)) {
357
        n *= 10;
358
        n += *(*s)++ - '0';
359
        parsed = true;
360
    }
361
    *_n = n;
362
    return parsed;
363
}
364
365
// Parse the next extension from an ISA string.
366
static bool parse_extension(const char **s, struct isa_extension *ext) {
367
    if (**s == '\0') {
368
        return false;
369
    }
370
371
    const char *name = *s;
372
    size_t name_len = 1;
373
    if (**s == 's' || **s == 'S' || **s == 'x' || **s == 'X' || **s == 'z' || **s == 'Z') {
374
        while (isalpha((*s)[name_len])) {
375
            name_len++;
376
        }
377
    }
378
    *s += name_len;
379
380
    size_t maj = 0, min = 0;
381
    if (parse_number(s, &maj)) {
382
        if (**s == 'p') {
383
            *s += 1;
384
            parse_number(s, &min);
385
        }
386
    }
387
388
    while (**s == '_') {
389
        *s += 1;
390
    }
391
392
    if (ext) {
393
        ext->name = name;
394
        ext->name_len = name_len;
395
        ext->ver_maj = maj;
396
        ext->ver_min = min;
397
    }
398
    return true;
399
}
400
401
static bool extension_matches(const struct isa_extension *ext, const char *name) {
402
    for (size_t i = 0; i < ext->name_len; i++) {
403
        const char c1 = tolower(ext->name[i]);
404
        const char c2 = tolower(*name++);
405
        if (c2 == '\0' || c1 != c2) {
406
            return false;
407
        }
408
    }
409
    // Make sure `name` is not longer.
410
    return *name == '\0';
411
}
412
413
bool riscv_check_isa_extension_for(size_t hartid, const char *name, size_t *maj, size_t *min) {
414
    // Skip the `rv{32,64}` prefix so it's not parsed as extensions.
415
    const char *isa_string = riscv_get_hart(hartid)->isa_string + 4;
416
417
    struct isa_extension ext;
418
    while (parse_extension(&isa_string, &ext)) {
419
        if (!extension_matches(&ext, name)) {
420
            continue;
421
        }
422
        if (maj) {
423
            *maj = ext.ver_maj;
424
        }
425
        if (min) {
426
            *min = ext.ver_min;
427
        }
428
        return true;
429
    }
430
431
    return false;
432
}
433
434
#endif
tab: 248 wrap: offon