Skip to content

Commit 9e10d53

Browse files
committed
Adds a driver for riscv64 harts.
This restores the ability to boot, since the hart will now be properly detected. Signed-off-by: Amy Ringo <me@remexre.com>
1 parent 9ae7afd commit 9e10d53

5 files changed

Lines changed: 477 additions & 0 deletions

File tree

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/*
2+
* SPDX-FileCopyrightText: ukoOS Contributors
3+
*
4+
* SPDX-License-Identifier: GPL-3.0-or-later
5+
*/
6+
7+
#include <arch/riscv64/devices/hart.h>
8+
#include <mm/alloc.h>
9+
10+
// TODO: This should become a hashtable, but this isn't _so_ horrible right now,
11+
// since there won't be very many hart groups or harts.
12+
13+
struct riscv_hart_group_list {
14+
struct riscv_hart_group_list *next;
15+
struct riscv_hart_group_key key;
16+
struct riscv_hart_group group;
17+
};
18+
19+
static struct riscv_hart_group_list *hart_groups = nullptr;
20+
21+
static bool riscv_hart_group_key_eq(struct riscv_hart_group_key key1,
22+
struct riscv_hart_group_key key2) {
23+
return key1.cboz_block_size == key2.cboz_block_size &&
24+
key1.flen == key2.flen && key1.vlen == key2.vlen;
25+
}
26+
27+
static void riscv_hart_group_make(struct riscv_hart_group *group,
28+
struct riscv_hart_group_key key) {
29+
usize len = 0;
30+
31+
// X
32+
group->x_off = len;
33+
group->x_len = 32 * sizeof(u64); // We don't support rv32, rv64e, or rv64y
34+
len += group->x_len;
35+
36+
// F
37+
if (key.flen != FLEN_0) {
38+
usize sizeof_f;
39+
switch (key.flen) {
40+
case FLEN_0:
41+
sizeof_f = 0;
42+
break;
43+
case FLEN_32:
44+
sizeof_f = 4;
45+
break;
46+
case FLEN_64:
47+
sizeof_f = 8;
48+
break;
49+
case FLEN_128:
50+
sizeof_f = 16;
51+
break;
52+
}
53+
len = (len + sizeof_f - 1) & ~(sizeof_f - 1);
54+
group->f_off = len;
55+
group->f_len = 32 * sizeof_f;
56+
len += group->f_len;
57+
} else {
58+
group->f_off = 0;
59+
group->f_len = 0;
60+
}
61+
62+
// V
63+
if (key.vlen != VLEN_0) {
64+
usize sizeof_v;
65+
switch (key.vlen) {
66+
case VLEN_UNKNOWN:
67+
default:
68+
panic("hart group key was not fully determined");
69+
case VLEN_0:
70+
sizeof_v = 0;
71+
break;
72+
case VLEN_128:
73+
sizeof_v = 16;
74+
break;
75+
case VLEN_256:
76+
sizeof_v = 32;
77+
break;
78+
case VLEN_512:
79+
sizeof_v = 64;
80+
break;
81+
case VLEN_1024:
82+
sizeof_v = 128;
83+
break;
84+
case VLEN_2048:
85+
sizeof_v = 256;
86+
break;
87+
case VLEN_4096:
88+
sizeof_v = 512;
89+
break;
90+
case VLEN_8192:
91+
sizeof_v = 1024;
92+
break;
93+
case VLEN_16384:
94+
sizeof_v = 2048;
95+
break;
96+
case VLEN_32768:
97+
sizeof_v = 4096;
98+
break;
99+
case VLEN_65536:
100+
sizeof_v = 8192;
101+
break;
102+
}
103+
len = (len + 7) & ~(usize)7;
104+
group->v_off = len;
105+
group->v_len = 32 * sizeof_v;
106+
len += group->v_len;
107+
} else {
108+
group->v_off = 0;
109+
group->v_len = 0;
110+
}
111+
112+
group->hart_group.sizeof_register_save_area = len;
113+
}
114+
115+
struct riscv_hart_group *riscv_hart_group_get(struct riscv_hart_group_key key) {
116+
struct riscv_hart_group_list *link;
117+
118+
for (link = hart_groups; link; link = link->next)
119+
if (riscv_hart_group_key_eq(key, link->key))
120+
return &link->group;
121+
122+
link = alloc(sizeof(struct riscv_hart_group_list));
123+
if (!link)
124+
return nullptr;
125+
link->next = hart_groups;
126+
link->key = key;
127+
riscv_hart_group_make(&link->group, key);
128+
hart_groups = link;
129+
return &link->group;
130+
}

src/kernel/arch/riscv64/include.mak

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ kernel-cflags += \
99
-mcmodel=medany
1010
kernel-objs-asm += arch/riscv64/start
1111
kernel-objs-c += arch/riscv64/backtrace
12+
kernel-objs-c += arch/riscv64/hart_group
1213
kernel-objs-c += arch/riscv64/hart_locals
1314
kernel-objs-c += arch/riscv64/paging
1415
kernel-objs-c += arch/riscv64/panic

src/kernel/drivers/include.mak

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
# SPDX-License-Identifier: GPL-3.0-or-later
44

55
kernel-objs-c += drivers/ns16550a
6+
kernel-objs-c += drivers/riscv_cpu

src/kernel/drivers/riscv_cpu.c

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
/*
2+
* SPDX-FileCopyrightText: ukoOS Contributors
3+
*
4+
* SPDX-License-Identifier: GPL-3.0-or-later
5+
*/
6+
7+
#include <arch/riscv64/devices/hart.h>
8+
#include <devices/hart.h>
9+
#include <devicetree.h>
10+
#include <endian.h>
11+
#include <mm/paging.h>
12+
#include <physical.h>
13+
14+
struct riscv_cpu {
15+
struct device device;
16+
struct hart hart;
17+
18+
atomic struct riscv_hart_group_key hart_group_key;
19+
};
20+
21+
static bool extensions_list_has_extension(const u8 *value, usize value_len,
22+
const char *extension_name) {
23+
usize extension_name_len = strlen(extension_name);
24+
while (value_len) {
25+
usize ext_len = strnlen((const char *)value, value_len);
26+
if (ext_len == extension_name_len &&
27+
memcmp(extension_name, value, ext_len) == 0) {
28+
return true;
29+
}
30+
value += ext_len + 1;
31+
value_len -= ext_len + 1;
32+
}
33+
return false;
34+
}
35+
36+
static u32 get_cboz_block_size_from_extensions(
37+
struct devicetree_node *node, struct devicetree_prop *prop_isa_extensions) {
38+
u32 cboz_block_size = 0;
39+
if (extensions_list_has_extension(prop_isa_extensions->value,
40+
prop_isa_extensions->value_len, "zicboz")) {
41+
u32 block_size;
42+
struct devicetree_prop *prop_cboz_block_size =
43+
devicetree_prop(node, "riscv,cboz-block-size");
44+
if (prop_cboz_block_size) {
45+
if (prop_cboz_block_size->value_len == sizeof(block_size)) {
46+
memcpy(&block_size, prop_cboz_block_size->value, sizeof(block_size));
47+
block_size = big_to_native(block_size);
48+
49+
cboz_block_size = block_size;
50+
} else {
51+
print("Devicetree device {cstr} did not have the riscv,cboz-block-size "
52+
"property",
53+
node->name);
54+
}
55+
} else {
56+
print("Devicetree device {cstr} had the wrong size for the "
57+
"riscv,cboz-block-size property",
58+
node->name);
59+
}
60+
}
61+
return cboz_block_size;
62+
}
63+
64+
static enum riscv_flen
65+
get_flen_from_extensions(struct devicetree_prop *prop_isa_extensions) {
66+
if (extensions_list_has_extension(prop_isa_extensions->value,
67+
prop_isa_extensions->value_len, "q")) {
68+
return FLEN_128;
69+
} else if (extensions_list_has_extension(prop_isa_extensions->value,
70+
prop_isa_extensions->value_len,
71+
"d")) {
72+
return FLEN_64;
73+
} else if (extensions_list_has_extension(prop_isa_extensions->value,
74+
prop_isa_extensions->value_len,
75+
"f")) {
76+
return FLEN_32;
77+
} else {
78+
return FLEN_0;
79+
}
80+
}
81+
82+
static enum riscv_vlen
83+
get_vlen_from_extensions(struct devicetree_node *node,
84+
struct devicetree_prop *prop_isa_extensions) {
85+
enum riscv_vlen vlen;
86+
87+
if (extensions_list_has_extension(prop_isa_extensions->value,
88+
prop_isa_extensions->value_len, "v")) {
89+
vlen = VLEN_UNKNOWN;
90+
91+
u32 vlenb;
92+
struct devicetree_prop *prop_thead_vlenb =
93+
devicetree_prop(node, "thead,vlenb");
94+
if (prop_thead_vlenb) {
95+
if (prop_thead_vlenb->value_len == sizeof(vlenb)) {
96+
memcpy(&vlenb, prop_thead_vlenb->value, sizeof(vlenb));
97+
vlenb = big_to_native(vlenb);
98+
99+
switch (vlenb) {
100+
case 128:
101+
vlen = VLEN_128;
102+
break;
103+
case 256:
104+
vlen = VLEN_256;
105+
break;
106+
case 512:
107+
vlen = VLEN_512;
108+
break;
109+
case 1024:
110+
vlen = VLEN_1024;
111+
break;
112+
case 2048:
113+
vlen = VLEN_2048;
114+
break;
115+
case 4096:
116+
vlen = VLEN_4096;
117+
break;
118+
case 8192:
119+
vlen = VLEN_8192;
120+
break;
121+
case 16384:
122+
vlen = VLEN_16384;
123+
break;
124+
case 32768:
125+
vlen = VLEN_32768;
126+
break;
127+
case 65536:
128+
vlen = VLEN_65536;
129+
break;
130+
default:
131+
print("Devicetree device {cstr} had a spec-disallowed value for the "
132+
"thead,vlenb property ({u32})",
133+
node->name, vlenb);
134+
}
135+
} else {
136+
print("Devicetree device {cstr} had the wrong size for the thead,vlenb "
137+
"property",
138+
node->name);
139+
}
140+
} else {
141+
print("Devicetree device {cstr} did not have the thead,vlenb property",
142+
node->name);
143+
vlen = VLEN_1024;
144+
}
145+
} else {
146+
vlen = VLEN_0;
147+
}
148+
149+
return vlen;
150+
}
151+
152+
static struct device *riscv_cpu_enumerate_dt(struct devicetree_node *node) {
153+
struct riscv_cpu *device = nullptr;
154+
struct devicetree_prop *prop_reg, *prop_isa_base, *prop_isa_extensions;
155+
u32 id;
156+
157+
// Get the hart number.
158+
prop_reg = devicetree_prop(node, "reg");
159+
if (!prop_reg) {
160+
print("Devicetree device {cstr} was missing the reg property", node->name);
161+
goto fail;
162+
}
163+
if (prop_reg->value_len != sizeof(id)) {
164+
print("Devicetree device {cstr} had the wrong size for the reg property",
165+
node->name);
166+
goto fail;
167+
}
168+
memcpy(&id, prop_reg->value, sizeof(id));
169+
id = big_to_native(id);
170+
171+
// Check for the ISA the hart supports.
172+
prop_isa_base = devicetree_prop(node, "riscv,isa-base");
173+
prop_isa_extensions = devicetree_prop(node, "riscv,isa-extensions");
174+
if (!prop_isa_base || !prop_isa_extensions) {
175+
if (devicetree_prop(node, "riscv,isa")) {
176+
print("Devicetree device {cstr} has the riscv,isa property but not "
177+
"riscv,isa-base and riscv,isa-extensions; update the Devicetree",
178+
node->name);
179+
}
180+
goto fail;
181+
}
182+
183+
if (prop_isa_base->value_len != 6 ||
184+
memcmp(prop_isa_base->value, "rv64i", 6) != 0) {
185+
print("Devicetree device {cstr} was not rv64i", node->name);
186+
goto fail;
187+
}
188+
189+
// Allocate the device.
190+
device = alloc(sizeof(struct riscv_cpu));
191+
if (!device)
192+
goto fail;
193+
*device = (struct riscv_cpu){
194+
.device = DEVICE_INIT(device->device, "riscv_cpu@{u32}", id),
195+
.hart =
196+
{
197+
.list = LIST_INIT(device->hart.list),
198+
.device = &device->device,
199+
.id = id,
200+
.hart_group = nullptr,
201+
},
202+
.hart_group_key =
203+
{
204+
.cboz_block_size = get_cboz_block_size_from_extensions(
205+
node, prop_isa_extensions),
206+
.flen = get_flen_from_extensions(prop_isa_extensions),
207+
.vlen = get_vlen_from_extensions(node, prop_isa_extensions),
208+
},
209+
};
210+
if (!device->device.name)
211+
goto fail;
212+
213+
// Try to assign a hart group now, if we can.
214+
if (riscv_hart_group_key_is_fully_determined(device->hart_group_key)) {
215+
struct riscv_hart_group *hart_group =
216+
riscv_hart_group_get(device->hart_group_key);
217+
if (!hart_group)
218+
goto fail;
219+
device->hart.hart_group = &hart_group->hart_group;
220+
}
221+
222+
// Add the device to the list of harts and return.
223+
list_push(&harts, &device->hart.list);
224+
return &device->device;
225+
226+
fail:
227+
if (device) {
228+
free(device->device.name);
229+
}
230+
free(device);
231+
return nullptr;
232+
}
233+
234+
DEFINE_INIT(INIT_REGISTER_DRIVERS) {
235+
devicetree_register("riscv", riscv_cpu_enumerate_dt);
236+
}

0 commit comments

Comments
 (0)