1
+/*
2
+ * Tenstorrent Atlantis RISC-V System on Chip
3
+ *
4
+ * SPDX-License-Identifier: GPL-2.0-or-later
5
+ *
6
+ * Copyright 2025 Tenstorrent, Joel Stanley <joel@jms.id.au>
7
+ */
8
+
9
+#include "qemu/osdep.h"
10
+#include "qemu/cutils.h"
11
+#include "qemu/error-report.h"
12
+#include "qemu/guest-random.h"
13
+#include "qemu/units.h"
14
+
15
+#include "hw/core/boards.h"
16
+#include "hw/core/loader.h"
17
+#include "hw/core/sysbus.h"
18
+
19
+#include "target/riscv/cpu.h"
20
+#include "target/riscv/pmu.h"
21
+
22
+#include "hw/riscv/boot.h"
23
+#include "hw/riscv/fdt-common.h"
24
+#include "hw/riscv/machines-qom.h"
25
+#include "hw/riscv/riscv_hart.h"
26
+
27
+#include "hw/char/serial-mm.h"
28
+#include "hw/intc/riscv_aclint.h"
29
+#include "hw/misc/unimp.h"
30
+
31
+#include "system/system.h"
32
+#include "system/device_tree.h"
33
+
34
+#include "hw/riscv/tt_atlantis.h"
35
+
36
+#include "aia.h"
37
+
38
+#define TT_IRQCHIP_NUM_MSIS 255
39
+#define TT_IRQCHIP_NUM_SOURCES 128
40
+#define TT_IRQCHIP_NUM_PRIO_BITS 3
41
+#define TT_IRQCHIP_GUESTS 63 /* aia_guests, gives guest_index_bits=6 */
42
+#define TT_IRQCHIP_MIMSIC_STRIDE 0x40000
43
+
44
+#define TT_ACLINT_MTIME_SIZE 0x8050
45
+#define TT_ACLINT_MTIME 0x0
46
+#define TT_ACLINT_MTIMECMP 0x8000
47
+#define TT_ACLINT_TIMEBASE_FREQ 1000000000
48
+
49
+static const MemMapEntry tt_atlantis_memmap[] = {
50
+ /* Keep sorted with :'<,'>!sort -g -k 4 */
51
+ [TT_ATL_DDR_LO] = { 0x00000000, 0x80000000 },
52
+ [TT_ATL_BOOTROM] = { 0x80000000, 0x2000 },
53
+ [TT_ATL_MIMSIC] = { 0xa0000000, 0x200000 },
54
+ [TT_ATL_ACLINT] = { 0xa2180000, 0x10000 },
55
+ [TT_ATL_SIMSIC] = { 0xa4000000, 0x200000 },
56
+ [TT_ATL_MAPLIC] = { 0xcc000000, 0x4000000 },
57
+ [TT_ATL_UART1] = { 0xd4110000, 0x10000 },
58
+ [TT_ATL_SAPLIC] = { 0xe8000000, 0x4000000 },
59
+ [TT_ATL_DDR_HI] = { 0x100000000, 0x1000000000 },
60
+};
61
+
62
+static uint32_t fdt_phandle = 1;
63
+static uint32_t next_phandle(void)
64
+{
65
+ return fdt_phandle++;
66
+}
67
+
68
+static void create_fdt_memory(TTAtlantisState *s)
69
+{
70
+ void *fdt = MACHINE(s)->fdt;
71
+ hwaddr size_lo = MACHINE(s)->ram_size;
72
+ hwaddr size_hi = 0;
73
+
74
+ if (size_lo > s->memmap[TT_ATL_DDR_LO].size) {
75
+ size_lo = s->memmap[TT_ATL_DDR_LO].size;
76
+ size_hi = MACHINE(s)->ram_size - size_lo;
77
+ }
78
+
79
+ create_fdt_socket_memory(fdt, s->memmap[TT_ATL_DDR_LO].base, size_lo,
80
+ 0, false);
81
+ if (size_hi) {
82
+ /*
83
+ * The first part of the HI address is aliased at the LO address
84
+ * so do not include that as usable memory. Is there any way
85
+ * (or good reason) to describe that aliasing 2GB with DT?
86
+ */
87
+ create_fdt_socket_memory(fdt, s->memmap[TT_ATL_DDR_HI].base + size_lo,
88
+ size_hi, 0, false);
89
+ }
90
+}
91
+
92
+static void create_fdt_aclint(TTAtlantisState *s, uint32_t *intc_phandles)
93
+{
94
+ void *fdt = MACHINE(s)->fdt;
95
+ g_autofree char *name = NULL;
96
+ g_autofree uint32_t *aclint_mtimer_cells = NULL;
97
+ uint32_t aclint_cells_size;
98
+ hwaddr addr;
99
+
100
+ aclint_mtimer_cells = g_new0(uint32_t, s->soc.num_harts * 2);
101
+
102
+ for (int cpu = 0; cpu < s->soc.num_harts; cpu++) {
103
+ aclint_mtimer_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]);
104
+ aclint_mtimer_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_M_TIMER);
105
+ }
106
+ aclint_cells_size = s->soc.num_harts * sizeof(uint32_t) * 2;
107
+
108
+ addr = s->memmap[TT_ATL_ACLINT].base;
109
+
110
+ name = g_strdup_printf("/soc/mtimer@%"HWADDR_PRIX, addr);
111
+ qemu_fdt_add_subnode(fdt, name);
112
+ qemu_fdt_setprop_string(fdt, name, "compatible", "riscv,aclint-mtimer");
113
+ qemu_fdt_setprop_sized_cells(fdt, name, "reg",
114
+ 2, addr + TT_ACLINT_MTIME,
115
+ 2, 0x1000,
116
+ 2, addr + TT_ACLINT_MTIMECMP,
117
+ 2, 0x1000);
118
+ qemu_fdt_setprop(fdt, name, "interrupts-extended",
119
+ aclint_mtimer_cells, aclint_cells_size);
120
+}
121
+
122
+static void create_fdt_one_imsic(void *fdt, const MemMapEntry *mem, int cpus,
123
+ uint32_t *intc_phandles, uint32_t msi_phandle,
124
+ int irq_line, uint32_t imsic_guest_bits)
125
+{
126
+ g_autofree char *name = NULL;
127
+ g_autofree uint32_t *imsic_cells = g_new0(uint32_t, cpus * 2);
128
+
129
+ for (int cpu = 0; cpu < cpus; cpu++) {
130
+ imsic_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]);
131
+ imsic_cells[cpu * 2 + 1] = cpu_to_be32(irq_line);
132
+ }
133
+
134
+ name = g_strdup_printf("/soc/interrupt-controller@%"HWADDR_PRIX, mem->base);
135
+ qemu_fdt_add_subnode(fdt, name);
136
+ qemu_fdt_setprop_string(fdt, name, "compatible", "riscv,imsics");
137
+
138
+ qemu_fdt_setprop_cell(fdt, name, "#interrupt-cells", 0);
139
+ qemu_fdt_setprop(fdt, name, "interrupt-controller", NULL, 0);
140
+ qemu_fdt_setprop(fdt, name, "msi-controller", NULL, 0);
141
+ qemu_fdt_setprop(fdt, name, "interrupts-extended",
142
+ imsic_cells, sizeof(uint32_t) * cpus * 2);
143
+ qemu_fdt_setprop_sized_cells(fdt, name, "reg", 2, mem->base, 2, mem->size);
144
+ qemu_fdt_setprop_cell(fdt, name, "riscv,num-ids", TT_IRQCHIP_NUM_MSIS);
145
+
146
+ if (imsic_guest_bits) {
147
+ qemu_fdt_setprop_cell(fdt, name, "riscv,guest-index-bits",
148
+ imsic_guest_bits);
149
+ }
150
+ qemu_fdt_setprop_cell(fdt, name, "phandle", msi_phandle);
151
+}
152
+
153
+static void create_fdt_one_aplic(void *fdt,
154
+ const MemMapEntry *mem,
155
+ uint32_t msi_phandle,
156
+ uint32_t *intc_phandles,
157
+ uint32_t aplic_phandle,
158
+ uint32_t aplic_child_phandle,
159
+ int irq_line, int num_harts)
160
+{
161
+ g_autofree char *name =
162
+ g_strdup_printf("/soc/interrupt-controller@%"HWADDR_PRIX, mem->base);
163
+ g_autofree uint32_t *aplic_cells = g_new0(uint32_t, num_harts * 2);
164
+
165
+ for (int cpu = 0; cpu < num_harts; cpu++) {
166
+ aplic_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]);
167
+ aplic_cells[cpu * 2 + 1] = cpu_to_be32(irq_line);
168
+ }
169
+
170
+ qemu_fdt_add_subnode(fdt, name);
171
+ qemu_fdt_setprop_string(fdt, name, "compatible", "riscv,aplic");
172
+ qemu_fdt_setprop_cell(fdt, name, "#address-cells", 0);
173
+ qemu_fdt_setprop_cell(fdt, name, "#interrupt-cells", 2);
174
+ qemu_fdt_setprop(fdt, name, "interrupt-controller", NULL, 0);
175
+
176
+ qemu_fdt_setprop(fdt, name, "interrupts-extended",
177
+ aplic_cells, num_harts * sizeof(uint32_t) * 2);
178
+ qemu_fdt_setprop_cell(fdt, name, "msi-parent", msi_phandle);
179
+
180
+ qemu_fdt_setprop_sized_cells(fdt, name, "reg", 2, mem->base, 2, mem->size);
181
+ qemu_fdt_setprop_cell(fdt, name, "riscv,num-sources",
182
+ TT_IRQCHIP_NUM_SOURCES);
183
+
184
+ if (aplic_child_phandle) {
185
+ qemu_fdt_setprop_cell(fdt, name, "riscv,children",
186
+ aplic_child_phandle);
187
+ qemu_fdt_setprop_cells(fdt, name, "riscv,delegation",
188
+ aplic_child_phandle, 1, TT_IRQCHIP_NUM_SOURCES);
189
+ }
190
+
191
+ qemu_fdt_setprop_cell(fdt, name, "phandle", aplic_phandle);
192
+}
193
+
194
+static void create_fdt_pmu(TTAtlantisState *s)
195
+{
196
+ char pmu_name[] = "/pmu";
197
+ void *fdt = MACHINE(s)->fdt;
198
+ RISCVCPU *hart = &s->soc.harts[0];
199
+
200
+ qemu_fdt_add_subnode(fdt, pmu_name);
201
+ qemu_fdt_setprop_string(fdt, pmu_name, "compatible", "riscv,pmu");
202
+ riscv_pmu_generate_fdt_node(fdt, hart->pmu_avail_ctrs, pmu_name);
203
+}
204
+
205
+static void create_fdt_cpu(TTAtlantisState *s, const MemMapEntry *memmap,
206
+ uint32_t aplic_s_phandle,
207
+ uint32_t imsic_s_phandle)
208
+{
209
+ MachineState *ms = MACHINE(s);
210
+ void *fdt = MACHINE(s)->fdt;
211
+ g_autofree uint32_t *intc_phandles = g_new0(uint32_t, ms->smp.cpus);
212
+
213
+ fdt_create_cpu_socket_subnode(fdt, TT_ACLINT_TIMEBASE_FREQ);
214
+
215
+ create_fdt_socket_cpus(fdt, s->soc.harts, 0, s->soc.num_harts,
216
+ s->soc.hartid_base, &fdt_phandle, intc_phandles,
217
+ false, false);
218
+
219
+ create_fdt_memory(s);
220
+
221
+ create_fdt_aclint(s, intc_phandles);
222
+
223
+ uint32_t imsic_guest_bits = imsic_num_bits(TT_IRQCHIP_GUESTS + 1);
224
+
225
+ /* M-level IMSIC node */
226
+ uint32_t msi_m_phandle = next_phandle();
227
+ create_fdt_one_imsic(fdt, &s->memmap[TT_ATL_MIMSIC], ms->smp.cpus,
228
+ intc_phandles, msi_m_phandle,
229
+ IRQ_M_EXT, imsic_guest_bits);
230
+
231
+ /* S-level IMSIC node */
232
+ create_fdt_one_imsic(fdt, &s->memmap[TT_ATL_SIMSIC], ms->smp.cpus,
233
+ intc_phandles, imsic_s_phandle,
234
+ IRQ_S_EXT, imsic_guest_bits);
235
+
236
+ uint32_t aplic_m_phandle = next_phandle();
237
+
238
+ /* M-level APLIC node */
239
+ create_fdt_one_aplic(fdt, &s->memmap[TT_ATL_MAPLIC],
240
+ msi_m_phandle, intc_phandles,
241
+ aplic_m_phandle, aplic_s_phandle,
242
+ IRQ_M_EXT, s->soc.num_harts);
243
+
244
+ /* S-level APLIC node */
245
+ create_fdt_one_aplic(fdt, &s->memmap[TT_ATL_SAPLIC],
246
+ imsic_s_phandle, intc_phandles,
247
+ aplic_s_phandle, 0,
248
+ IRQ_S_EXT, s->soc.num_harts);
249
+}
250
+
251
+static void create_fdt_uart(void *fdt, const MemMapEntry *mem, int irq,
252
+ int irqchip_phandle)
253
+{
254
+ g_autofree char *name = g_strdup_printf("/soc/serial@%"HWADDR_PRIX,
255
+ mem->base);
256
+
257
+ qemu_fdt_add_subnode(fdt, name);
258
+ qemu_fdt_setprop_string(fdt, name, "compatible", "ns16550a");
259
+ qemu_fdt_setprop_sized_cells(fdt, name, "reg", 2, mem->base, 2, mem->size);
260
+ qemu_fdt_setprop_cell(fdt, name, "reg-shift", 2);
261
+ qemu_fdt_setprop_cell(fdt, name, "reg-io-width", 4);
262
+ qemu_fdt_setprop_cell(fdt, name, "clock-frequency", 3686400);
263
+ qemu_fdt_setprop_cell(fdt, name, "interrupt-parent", irqchip_phandle);
264
+ qemu_fdt_setprop_cells(fdt, name, "interrupts", irq, 0x4);
265
+
266
+ qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", name);
267
+ qemu_fdt_setprop_string(fdt, "/aliases", "serial0", name);
268
+}
269
+
270
+static void create_fdt_rng(void *fdt)
271
+{
272
+ uint8_t rng_seed[32];
273
+
274
+ qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed));
275
+ qemu_fdt_setprop(fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed));
276
+}
277
+
278
+static void finalize_fdt(TTAtlantisState *s)
279
+{
280
+ uint32_t aplic_s_phandle = next_phandle();
281
+ uint32_t imsic_s_phandle = next_phandle();
282
+ void *fdt = MACHINE(s)->fdt;
283
+
284
+ create_fdt_cpu(s, s->memmap, aplic_s_phandle, imsic_s_phandle);
285
+
286
+ /*
287
+ * We want to do this, but the Linux aplic driver was broken before v6.16
288
+ *
289
+ * qemu_fdt_setprop_cell(MACHINE(s)->fdt, "/soc", "interrupt-parent",
290
+ * aplic_s_phandle);
291
+ */
292
+
293
+ create_fdt_uart(fdt, &s->memmap[TT_ATL_UART1], TT_ATL_UART1_IRQ,
294
+ aplic_s_phandle);
295
+}
296
+
297
+static void create_fdt(TTAtlantisState *s)
298
+{
299
+ MachineState *ms = MACHINE(s);
300
+
301
+ ms->fdt = create_board_device_tree("Tenstorrent Atlantis RISC-V Machine",
302
+ "tenstorrent,atlantis", &s->fdt_size);
303
+
304
+ qemu_fdt_add_subnode(ms->fdt, "/chosen");
305
+
306
+ create_fdt_rng(ms->fdt);
307
+
308
+ qemu_fdt_add_subnode(ms->fdt, "/aliases");
309
+
310
+ create_fdt_pmu(s);
311
+}
312
+
313
+static void load_fdt(TTAtlantisState *s)
314
+{
315
+ MachineState *ms = MACHINE(s);
316
+ char **node_path;
317
+ Error *err = NULL;
318
+
319
+ ms->fdt = load_device_tree(ms->dtb, &s->fdt_size);
320
+ if (!ms->fdt) {
321
+ error_report("load_device_tree() failed");
322
+ exit(1);
323
+ }
324
+
325
+ qemu_fdt_add_path(ms->fdt, "/chosen");
326
+
327
+ /* Clear memory nodes and update with the specified RAM size */
328
+ node_path = qemu_fdt_node_unit_path(ms->fdt, "memory", &err);
329
+ if (err) {
330
+ warn_report_err(err);
331
+ } else {
332
+ for (int i = 0; node_path[i]; i++) {
333
+ warn_report("Replacing device tree %s with the requested RAM size",
334
+ node_path[i]);
335
+ qemu_fdt_nop_node(ms->fdt, node_path[i]);
336
+ }
337
+ g_strfreev(node_path);
338
+ }
339
+
340
+ create_fdt_memory(s);
341
+}
342
+
343
+static void tt_atlantis_machine_done(Notifier *notifier, void *data)
344
+{
345
+ TTAtlantisState *s = container_of(notifier, TTAtlantisState, machine_done);
346
+ MachineState *machine = MACHINE(s);
347
+ hwaddr start_addr = s->memmap[TT_ATL_DDR_LO].base;
348
+ hwaddr mem_size;
349
+ target_ulong firmware_end_addr, kernel_start_addr;
350
+ const char *firmware_name = riscv_default_firmware_name(&s->soc);
351
+ uint64_t fdt_load_addr;
352
+ uint64_t kernel_entry;
353
+ RISCVBootInfo boot_info;
354
+
355
+ /*
356
+ * A user provided dtb must include everything, including
357
+ * dynamic sysbus devices. Our FDT needs to be finalized.
358
+ */
359
+ if (machine->dtb == NULL) {
360
+ finalize_fdt(s);
361
+ }
362
+
363
+ mem_size = machine->ram_size;
364
+ if (mem_size > s->memmap[TT_ATL_DDR_LO].size) {
365
+ mem_size = s->memmap[TT_ATL_DDR_LO].size;
366
+ }
367
+ riscv_boot_info_init_discontig_mem(&boot_info, &s->soc,
368
+ s->memmap[TT_ATL_DDR_LO].base,
369
+ mem_size);
370
+
371
+ firmware_end_addr = riscv_find_and_load_firmware(machine, &boot_info,
372
+ firmware_name,
373
+ &start_addr, NULL);
374
+
375
+ kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info,
376
+ firmware_end_addr);
377
+ if (machine->kernel_filename) {
378
+ riscv_load_kernel(machine, &boot_info, kernel_start_addr,
379
+ true, NULL);
380
+ }
381
+ kernel_entry = boot_info.image_low_addr;
382
+
383
+ fdt_load_addr = riscv_compute_fdt_addr(s->memmap[TT_ATL_DDR_LO].base,
384
+ s->memmap[TT_ATL_DDR_LO].size,
385
+ machine, &boot_info);
386
+ riscv_load_fdt(fdt_load_addr, machine->fdt);
387
+
388
+ /* load the reset vector */
389
+ riscv_setup_rom_reset_vec(machine, &s->soc, start_addr,
390
+ s->memmap[TT_ATL_BOOTROM].base,
391
+ s->memmap[TT_ATL_BOOTROM].size,
392
+ kernel_entry,
393
+ fdt_load_addr);
394
+}
395
+
396
+static void tt_atlantis_machine_init(MachineState *machine)
397
+{
398
+ TTAtlantisState *s = TT_ATLANTIS_MACHINE(machine);
399
+
400
+ MemoryRegion *system_memory = get_system_memory();
401
+ MemoryRegion *ram_hi = g_new(MemoryRegion, 1);
402
+ MemoryRegion *ram_lo = g_new(MemoryRegion, 1);
403
+ MemoryRegion *bootrom = g_new(MemoryRegion, 1);
404
+ ram_addr_t lo_ram_size;
405
+ int hart_count = machine->smp.cpus;
406
+
407
+ s->memmap = tt_atlantis_memmap;
408
+
409
+ object_initialize_child(OBJECT(machine), "soc", &s->soc,
410
+ TYPE_RISCV_HART_ARRAY);
411
+ object_property_set_str(OBJECT(&s->soc), "cpu-type", machine->cpu_type,
412
+ &error_abort);
413
+ object_property_set_int(OBJECT(&s->soc), "hartid-base", 0,
414
+ &error_abort);
415
+ object_property_set_int(OBJECT(&s->soc), "num-harts", hart_count,
416
+ &error_abort);
417
+ object_property_set_int(OBJECT(&s->soc), "resetvec",
418
+ s->memmap[TT_ATL_BOOTROM].base,
419
+ &error_abort);
420
+ sysbus_realize(SYS_BUS_DEVICE(&s->soc), &error_fatal);
421
+
422
+ s->irqchip = riscv_create_aia(true, TT_IRQCHIP_GUESTS,
423
+ TT_IRQCHIP_MIMSIC_STRIDE,
424
+ TT_IRQCHIP_NUM_SOURCES,
425
+ &s->memmap[TT_ATL_MAPLIC],
426
+ &s->memmap[TT_ATL_SAPLIC],
427
+ &s->memmap[TT_ATL_MIMSIC],
428
+ &s->memmap[TT_ATL_SIMSIC],
429
+ 0, 0, hart_count,
430
+ TT_IRQCHIP_NUM_MSIS,
431
+ TT_IRQCHIP_NUM_PRIO_BITS);
432
+
433
+ riscv_aclint_mtimer_create(s->memmap[TT_ATL_ACLINT].base,
434
+ TT_ACLINT_MTIME_SIZE,
435
+ 0, hart_count,
436
+ TT_ACLINT_MTIMECMP,
437
+ TT_ACLINT_MTIME,
438
+ TT_ACLINT_TIMEBASE_FREQ, true);
439
+
440
+ /*
441
+ * DDR
442
+ *
443
+ * The high address is where RAM lives. It is always present and may be
444
+ * up to 64GB. The low address is an alias of the first 2GB of that RAM.
445
+ */
446
+ if (machine->ram_size > s->memmap[TT_ATL_DDR_HI].size) {
447
+ char *sz = size_to_str(s->memmap[TT_ATL_DDR_HI].size);
448
+ error_report("RAM size is too large, maximum is %s", sz);
449
+ g_free(sz);
450
+ exit(EXIT_FAILURE);
451
+ }
452
+
453
+ memory_region_init_alias(ram_hi, OBJECT(machine), "ram.high", machine->ram,
454
+ 0, machine->ram_size);
455
+ memory_region_add_subregion(system_memory,
456
+ s->memmap[TT_ATL_DDR_HI].base, ram_hi);
457
+
458
+ lo_ram_size = MIN(machine->ram_size, s->memmap[TT_ATL_DDR_LO].size);
459
+ memory_region_init_alias(ram_lo, OBJECT(machine), "ram.low", machine->ram,
460
+ 0, lo_ram_size);
461
+ memory_region_add_subregion(system_memory,
462
+ s->memmap[TT_ATL_DDR_LO].base, ram_lo);
463
+
464
+ /* Boot ROM */
465
+ memory_region_init_rom(bootrom, NULL, "tt-atlantis.bootrom",
466
+ s->memmap[TT_ATL_BOOTROM].size, &error_fatal);
467
+ memory_region_add_subregion(system_memory, s->memmap[TT_ATL_BOOTROM].base,
468
+ bootrom);
469
+
470
+ /* UART1, the soc console (UART0 is for the boot microcontroller) */
471
+ serial_mm_init(system_memory, s->memmap[TT_ATL_UART1].base, 2,
472
+ qdev_get_gpio_in(s->irqchip, TT_ATL_UART1_IRQ),
473
+ 115200, serial_hd(0), DEVICE_LITTLE_ENDIAN);
474
+ /*
475
+ * Atlantis contains a DesignWare uart while the QEMU machine
476
+ * uses the serial_mm model with the base ns16550 register set.
477
+ * Linux's dw driver writes outside of serial_mm's 0x20 sized
478
+ * mapping and faults.
479
+ *
480
+ * Create an unimplemented device region so writes don't fault
481
+ * and reads return zero, which keeps Linux happy.
482
+ */
483
+ create_unimplemented_device("tt-atlantis.uart0",
484
+ s->memmap[TT_ATL_UART1].base,
485
+ s->memmap[TT_ATL_UART1].size);
486
+
487
+ /* Load or create device tree */
488
+ if (machine->dtb) {
489
+ load_fdt(s);
490
+ } else {
491
+ create_fdt(s);
492
+ }
493
+
494
+ s->machine_done.notify = tt_atlantis_machine_done;
495
+ qemu_add_machine_init_done_notifier(&s->machine_done);
496
+}
497
+
498
+static void tt_atlantis_machine_class_init(ObjectClass *oc, const void *data)
499
+{
500
+ MachineClass *mc = MACHINE_CLASS(oc);
501
+
502
+ mc->desc = "Tenstorrent Atlantis RISC-V SoC (Experimental)";
503
+ mc->init = tt_atlantis_machine_init;
504
+ mc->max_cpus = 8;
505
+ mc->default_cpus = 8;
506
+ mc->default_ram_size = 4 * GiB;
507
+ mc->default_cpu_type = TYPE_RISCV_CPU_TT_ASCALON;
508
+ mc->block_default_type = IF_VIRTIO;
509
+ mc->no_cdrom = 1;
510
+ mc->default_ram_id = "tt_atlantis.ram";
511
+}
512
+
513
+static const TypeInfo tt_atlantis_types[] = {
514
+ {
515
+ .name = MACHINE_TYPE_NAME("tt-atlantis"),
516
+ .parent = TYPE_MACHINE,
517
+ .class_init = tt_atlantis_machine_class_init,
518
+ .instance_size = sizeof(TTAtlantisState),
519
+ .interfaces = riscv64_machine_interfaces,
520
+ },
521
+};
522
+
523
+DEFINE_TYPES(tt_atlantis_types)