Repository navigation
Expand file tree
/
Copy pathcm.h
More file actions
1193 lines (1093 loc) · 63 KB
/
Copy pathcm.h
File metadata and controls
1193 lines (1093 loc) · 63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Copyright Cartesi and individual authors (see AUTHORS)
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option) any
// later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along
// with this program (see COPYING). If not, see <https://www.gnu.org/licenses/>.
//
#ifndef CARTESI_MACHINE_H // NOLINTBEGIN
#define CARTESI_MACHINE_H
#include "cm-version.h"
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
// -----------------------------------------------------------------------------
// API definitions
// -----------------------------------------------------------------------------
// Compiler visibility definition
#ifndef CM_API
#define CM_API __attribute__((visibility("default")))
#endif
static const uint64_t CM_MCYCLE_MAX = -1ULL;
#define CM_DTB_BOOTARGS_CONSOLE_PART "quiet earlycon=sbi console=hvc0 "
#define CM_DTB_BOOTARGS_UIO_PART "uio_pdrv_genirq.of_id=generic-uio "
#define CM_DTB_BOOTARGS_ROOT_PART "root=/dev/pmem0 rw "
#define CM_DTB_BOOTARGS_INIT_PART "init=/usr/sbin/cartesi-init"
#define CM_DTB_BOOTARGS_INIT \
(CM_DTB_BOOTARGS_CONSOLE_PART CM_DTB_BOOTARGS_UIO_PART CM_DTB_BOOTARGS_ROOT_PART CM_DTB_BOOTARGS_INIT_PART)
// -----------------------------------------------------------------------------
// API enums and structures
// -----------------------------------------------------------------------------
/// \brief Constants.
typedef enum cm_constant {
CM_HASH_SIZE = 32,
CM_HASH_TREE_LOG2_WORD_SIZE = 5,
CM_HASH_TREE_LOG2_PAGE_SIZE = 12,
CM_HASH_TREE_LOG2_ROOT_SIZE = 64,
CM_ROLLUP_LOG2_MAX_MCYCLES_PER_ADVANCE_STATE = 48,
CM_ROLLUP_LOG2_MAX_UARCH_CYCLES_PER_MCYCLE = 20,
CM_ROLLUP_LOG2_MAX_OUTPUT_COUNT = 63,
CM_ROLLUP_LOG2_MAX_ADVANCE_STATES_PER_EPOCH = 24,
CM_FLASH_DRIVE_MAX = 8, ///< Maximum number of flash drives
CM_NVRAM_MAX = 8, ///< Maximum number of NVRAMs
CM_MEMORY_RANGE_LABEL_MAX = 31, ///< Maximum length of a memory range user label (DT alias constraint)
CM_RTC_FREQ_DIV = 8192, ///< mtime increments once per this many mcycle increments
} cm_constant;
static const uint64_t CM_UARCH_CYCLE_MAX = (1ULL << CM_ROLLUP_LOG2_MAX_UARCH_CYCLES_PER_MCYCLE) - 1;
/// \brief Physical memory addresses (only the most useful are exposed in the API).
typedef enum cm_pmas_constant {
CM_AR_CMIO_RX_BUFFER_START = 0x60000000,
CM_AR_CMIO_RX_BUFFER_LOG2_SIZE = 21,
CM_AR_CMIO_TX_BUFFER_START = 0x60800000,
CM_AR_CMIO_TX_BUFFER_LOG2_SIZE = 21,
CM_AR_SHADOW_REVERT_ROOT_HASH_START = 0xfe0,
CM_AR_RAM_START = 0x80000000,
CM_AR_SHADOW_STATE_START = 0x0,
CM_AR_SHADOW_STATE_LENGTH = 0x8000,
CM_AR_SHADOW_TLB_START = 0x1000,
CM_AR_SHADOW_TLB_LENGTH = 0x6000,
CM_AR_PMAS_START = 0x10000,
CM_AR_PMAS_LENGTH = 0x1000,
CM_AR_CLINT_START = 0x2000000,
CM_AR_CLINT_LENGTH = 0xC0000,
CM_AR_HTIF_START = 0x40008000,
CM_AR_HTIF_LENGTH = 0x1000,
CM_AR_FIRST_VIRTIO_START = 0x40010000,
CM_AR_LAST_VIRTIO_END = 0x40020000,
CM_AR_PLIC_START = 0x40100000,
CM_AR_PLIC_LENGTH = 0x00400000,
CM_AR_CMIO_RX_BUFFER_LENGTH = 0x200000,
CM_AR_CMIO_TX_BUFFER_LENGTH = 0x200000,
CM_AR_DTB_START = 0x7ff00000,
CM_AR_DTB_LENGTH = 0x100000,
CM_UARCH_STATE_START_ADDRESS = 0x400000,
CM_UARCH_STATE_LOG2_SIZE = 22,
CM_UARCH_STATE_LENGTH = 0x400000,
} cm_pmas_constant;
/// \brief Driver IDs for PMA entries.
typedef enum cm_pma_did {
CM_PMA_EMPTY_DID = 0, ///< Driver ID for empty range
CM_PMA_MEMORY_DID = 1, ///< Driver ID for memory
CM_PMA_SHADOW_STATE_DID = 2, ///< Driver ID for shadow state device
CM_PMA_FLASH_DRIVE_DID = 3, ///< Driver ID for flash drive device
CM_PMA_CLINT_DID = 4, ///< Driver ID for CLINT device
CM_PMA_HTIF_DID = 5, ///< Driver ID for HTIF device
CM_PMA_PLIC_DID = 6, ///< Driver ID for PLIC device
CM_PMA_CMIO_RX_BUFFER_DID = 7, ///< Driver ID for CMIO RX buffer
CM_PMA_CMIO_TX_BUFFER_DID = 8, ///< Driver ID for CMIO TX buffer
CM_PMA_SHADOW_UARCH_STATE_DID = 9, ///< Driver ID for uarch shadow state device
CM_PMA_VIRTIO_DID = 10, ///< Driver ID for VirtIO devices
CM_PMA_NVRAM_DID = 11, ///< Driver ID for NVRAM
} cm_pma_did;
/// \brief Error codes returned from the C API.
typedef enum cm_error {
CM_ERROR_OK = 0,
CM_ERROR_INVALID_ARGUMENT = -1,
CM_ERROR_DOMAIN_ERROR = -2,
CM_ERROR_LENGTH_ERROR = -3,
CM_ERROR_OUT_OF_RANGE = -4,
CM_ERROR_LOGIC_ERROR = -5,
CM_ERROR_RUNTIME_ERROR = -6,
CM_ERROR_RANGE_ERROR = -7,
CM_ERROR_OVERFLOW_ERROR = -8,
CM_ERROR_UNDERFLOW_ERROR = -9,
CM_ERROR_BAD_TYPEID = -10,
CM_ERROR_BAD_CAST = -11,
CM_ERROR_BAD_ANY_CAST = -12,
CM_ERROR_BAD_OPTIONAL_ACCESS = -13,
CM_ERROR_BAD_WEAK_PTR = -14,
CM_ERROR_BAD_FUNCTION_CALL = -15,
CM_ERROR_BAD_ALLOC = -16,
CM_ERROR_BAD_ARRAY_NEW_LENGTH = -17,
CM_ERROR_BAD_EXCEPTION = -18,
CM_ERROR_BAD_VARIANT_ACCESS = -19,
CM_ERROR_EXCEPTION = -20,
CM_ERROR_UNKNOWN = -21,
} cm_error;
/// \brief Reasons for the machine to break from call to cm_run.
typedef enum cm_break_reason {
CM_BREAK_REASON_FAILED,
CM_BREAK_REASON_HALTED,
CM_BREAK_REASON_YIELDED_MANUALLY,
CM_BREAK_REASON_YIELDED_AUTOMATICALLY,
CM_BREAK_REASON_YIELDED_SOFTLY,
CM_BREAK_REASON_REACHED_TARGET_MCYCLE,
CM_BREAK_REASON_CONSOLE_OUTPUT,
CM_BREAK_REASON_CONSOLE_INPUT,
CM_BREAK_REASON_MCYCLE_OVERFLOW,
} cm_break_reason;
/// \brief Reasons for the machine to break from call to cm_run_uarch.
typedef enum cm_uarch_break_reason {
CM_UARCH_BREAK_REASON_REACHED_TARGET_UARCH_CYCLE,
CM_UARCH_BREAK_REASON_UARCH_HALTED,
CM_UARCH_BREAK_REASON_UARCH_CYCLE_OVERFLOW,
CM_UARCH_BREAK_REASON_FAILED,
} cm_uarch_break_reason;
/// \brief Access log types.
typedef enum cm_access_log_type {
CM_ACCESS_LOG_TYPE_ANNOTATIONS = 1, ///< Includes annotations
CM_ACCESS_LOG_TYPE_LARGE_DATA = 2, ///< Includes data larger than 8 bytes
} cm_access_log_type;
/// \brief Sharing modes.
typedef enum cm_sharing_mode {
CM_SHARING_NONE = 0, ///< No sharing, all machine changes will be in-memory
CM_SHARING_CONFIG = 1, ///< Share backing stores marked as shared in the machine configuration
CM_SHARING_ALL = 2, ///< Share all backing stores, all machine changes will be on-disk
} cm_sharing_mode;
/// \brief HTIF tohost/fromhost register field shifts.
typedef enum cm_htif_shift {
CM_HTIF_DEV_SHIFT = 56,
CM_HTIF_CMD_SHIFT = 48,
CM_HTIF_REASON_SHIFT = 32,
CM_HTIF_DATA_SHIFT = 0,
} cm_htif_shift;
/// \brief HTIF tohost/fromhost register field masks.
static const uint64_t CM_HTIF_DEV_MASK = 0xFF00000000000000ULL;
static const uint64_t CM_HTIF_CMD_MASK = 0x00FF000000000000ULL;
static const uint64_t CM_HTIF_REASON_MASK = 0x0000FFFF00000000ULL;
static const uint64_t CM_HTIF_DATA_MASK = 0x00000000FFFFFFFFULL;
/// \brief HTIF device identifiers (DEV field of tohost/fromhost).
typedef enum cm_htif_device {
CM_HTIF_DEV_HALT = 0, ///< Halts the machine
CM_HTIF_DEV_CONSOLE = 1, ///< Console input and output
CM_HTIF_DEV_YIELD = 2, ///< Yield control back to the host
} cm_htif_device;
/// \brief HTIF device commands (CMD field of tohost/fromhost).
typedef enum cm_htif_command {
CM_HTIF_HALT_CMD_HALT = 0,
CM_HTIF_CONSOLE_CMD_GETCHAR = 0,
CM_HTIF_CONSOLE_CMD_PUTCHAR = 1,
CM_HTIF_YIELD_CMD_AUTOMATIC = 0,
CM_HTIF_YIELD_CMD_MANUAL = 1,
} cm_htif_command;
/// \brief HTIF yield reasons (REASON field of tohost/fromhost).
typedef enum cm_htif_yield_reason {
CM_HTIF_YIELD_AUTOMATIC_REASON_PROGRESS = 1,
CM_HTIF_YIELD_AUTOMATIC_REASON_TX_OUTPUT = 2,
CM_HTIF_YIELD_AUTOMATIC_REASON_TX_REPORT = 4,
CM_HTIF_YIELD_MANUAL_REASON_RX_ACCEPTED = 1,
CM_HTIF_YIELD_MANUAL_REASON_RX_REJECTED = 2,
CM_HTIF_YIELD_MANUAL_REASON_TX_EXCEPTION = 4,
CM_HTIF_YIELD_REASON_ADVANCE_STATE = 0,
CM_HTIF_YIELD_REASON_INSPECT_STATE = 1,
} cm_htif_yield_reason;
/// \brief Machine x, f, and control and status registers.
typedef enum cm_reg {
// Machine x registers
CM_REG_X0,
CM_REG_X1,
CM_REG_X2,
CM_REG_X3,
CM_REG_X4,
CM_REG_X5,
CM_REG_X6,
CM_REG_X7,
CM_REG_X8,
CM_REG_X9,
CM_REG_X10,
CM_REG_X11,
CM_REG_X12,
CM_REG_X13,
CM_REG_X14,
CM_REG_X15,
CM_REG_X16,
CM_REG_X17,
CM_REG_X18,
CM_REG_X19,
CM_REG_X20,
CM_REG_X21,
CM_REG_X22,
CM_REG_X23,
CM_REG_X24,
CM_REG_X25,
CM_REG_X26,
CM_REG_X27,
CM_REG_X28,
CM_REG_X29,
CM_REG_X30,
CM_REG_X31,
// Machine f registers
CM_REG_F0,
CM_REG_F1,
CM_REG_F2,
CM_REG_F3,
CM_REG_F4,
CM_REG_F5,
CM_REG_F6,
CM_REG_F7,
CM_REG_F8,
CM_REG_F9,
CM_REG_F10,
CM_REG_F11,
CM_REG_F12,
CM_REG_F13,
CM_REG_F14,
CM_REG_F15,
CM_REG_F16,
CM_REG_F17,
CM_REG_F18,
CM_REG_F19,
CM_REG_F20,
CM_REG_F21,
CM_REG_F22,
CM_REG_F23,
CM_REG_F24,
CM_REG_F25,
CM_REG_F26,
CM_REG_F27,
CM_REG_F28,
CM_REG_F29,
CM_REG_F30,
CM_REG_F31,
// Machine CSRs
CM_REG_PC,
CM_REG_FCSR,
CM_REG_MVENDORID,
CM_REG_MARCHID,
CM_REG_MIMPID,
CM_REG_MCYCLE,
CM_REG_ICYCLEINSTRET,
CM_REG_MSTATUS,
CM_REG_MTVEC,
CM_REG_MSCRATCH,
CM_REG_MEPC,
CM_REG_MCAUSE,
CM_REG_MTVAL,
CM_REG_MISA,
CM_REG_MIE,
CM_REG_MIP,
CM_REG_MEDELEG,
CM_REG_MIDELEG,
CM_REG_MCOUNTEREN,
CM_REG_MENVCFG,
CM_REG_STVEC,
CM_REG_SSCRATCH,
CM_REG_SEPC,
CM_REG_SCAUSE,
CM_REG_STVAL,
CM_REG_SATP,
CM_REG_SCOUNTEREN,
CM_REG_SENVCFG,
CM_REG_ILRSC,
CM_REG_IPRV,
CM_REG_IFLAGS_X,
CM_REG_IFLAGS_Y,
CM_REG_IFLAGS_H,
CM_REG_IUNREP,
CM_REG_IMCYCLEMAX,
// Device registers
CM_REG_CLINT_MTIMECMP,
CM_REG_PLIC_GIRQPEND,
CM_REG_PLIC_GIRQSRVD,
CM_REG_HTIF_TOHOST,
CM_REG_HTIF_FROMHOST,
CM_REG_HTIF_IHALT,
CM_REG_HTIF_ICONSOLE,
CM_REG_HTIF_IYIELD,
// Microarchitecture registers
CM_REG_UARCH_X0,
CM_REG_UARCH_X1,
CM_REG_UARCH_X2,
CM_REG_UARCH_X3,
CM_REG_UARCH_X4,
CM_REG_UARCH_X5,
CM_REG_UARCH_X6,
CM_REG_UARCH_X7,
CM_REG_UARCH_X8,
CM_REG_UARCH_X9,
CM_REG_UARCH_X10,
CM_REG_UARCH_X11,
CM_REG_UARCH_X12,
CM_REG_UARCH_X13,
CM_REG_UARCH_X14,
CM_REG_UARCH_X15,
CM_REG_UARCH_X16,
CM_REG_UARCH_X17,
CM_REG_UARCH_X18,
CM_REG_UARCH_X19,
CM_REG_UARCH_X20,
CM_REG_UARCH_X21,
CM_REG_UARCH_X22,
CM_REG_UARCH_X23,
CM_REG_UARCH_X24,
CM_REG_UARCH_X25,
CM_REG_UARCH_X26,
CM_REG_UARCH_X27,
CM_REG_UARCH_X28,
CM_REG_UARCH_X29,
CM_REG_UARCH_X30,
CM_REG_UARCH_X31,
CM_REG_UARCH_PC,
CM_REG_UARCH_CYCLE,
CM_REG_UARCH_HALT,
// Views of registers
CM_REG_HTIF_TOHOST_DEV,
CM_REG_HTIF_TOHOST_CMD,
CM_REG_HTIF_TOHOST_REASON,
CM_REG_HTIF_TOHOST_DATA,
CM_REG_HTIF_FROMHOST_DEV,
CM_REG_HTIF_FROMHOST_CMD,
CM_REG_HTIF_FROMHOST_REASON,
CM_REG_HTIF_FROMHOST_DATA,
// Enumeration helpers
CM_REG_UNKNOWN_,
CM_REG_FIRST_,
CM_REG_LAST_,
CM_REG_UARCH_FIRST_,
CM_REG_UARCH_LAST_,
} cm_reg;
/// \brief Hash function types.
typedef enum cm_hash_function {
CM_HASH_KECCAK256 = 0, ///< Keccak-256 (recommended for fraud proofs using microarchitecture)
CM_HASH_SHA256 = 1, ///< SHA-256 (recommended for fraud proofs using zkVMs)
} cm_hash_function;
/// \brief Storage for machine hash.
typedef uint8_t cm_hash[CM_HASH_SIZE];
/// \brief Machine object handle.
/// \details It's used only as an opaque handle to pass machine objects through the C API.
typedef struct cm_machine cm_machine;
// -----------------------------------------------------------------------------
// API functions
// -----------------------------------------------------------------------------
/// \brief Returns the machine emulator semantic version at runtime.
/// \returns Version as a 64-bit integer encoded as `(major * 1000000) + (minor * 1000) + patch`.
/// For example, version 0.18.1 is encoded as 18001.
CM_API uint64_t cm_get_version();
/// \brief Returns the error message set by the very last C API call.
/// \returns A C string, guaranteed to remain valid only until the next CM_API function call.
/// \details The string returned by this function must not be changed nor deallocated, and remains valid until
/// the next CM_API function that can return a cm_error code is called.
/// Must be called from the same thread as the function that produced the error.
/// In case the last CM_API function call on that thread was successful, returns an empty string.
/// \warning Do not use the empty string as an indication that the previous call was successful.
/// This would be error-prone since calls from different threads may see different strings.
/// Instead, use the return code of the previous call itself.
CM_API const char *cm_get_last_error_message();
/// \brief Obtains a JSON object with the default machine config as a string.
/// \param m Pointer to a machine object. Can be NULL (for local machines).
/// \param config Receives the default configuration as a JSON object in a string, guaranteed to remain valid only until
/// the next CM_API function is called from the same thread. Set to NULL on failure.
/// \returns 0 for success, non zero code for error.
/// \details The returned config is not sufficient to run a machine.
/// Additional configurations, such as RAM length, RAM image, flash drives, and entrypoint are still needed.
/// It's unnecessary to base configs on the default config,
/// because unset fields are automatically filled with defaults.
CM_API cm_error cm_get_default_config(const cm_machine *m, const char **config);
/// \brief Gets the address of any x, f, or control state register.
/// \param m Pointer to a machine object. Can be NULL (for local machines).
/// \param reg The register.
/// \param val Receives address of the register.
/// \returns 0 for success, non zero code for error.
CM_API cm_error cm_get_reg_address(const cm_machine *m, cm_reg reg, uint64_t *val);
/// \brief Gets the Keccak-256 hash of the pristine uarch state (shadow and RAM).
/// \param m Pointer to a machine object. Can be NULL (for local machines).
/// \param hash Valid pointer to cm_hash structure that receives the hash.
/// \returns 0 for success, non zero code for error.
CM_API cm_error cm_get_uarch_pristine_hash(const cm_machine *m, cm_hash *hash);
/// \brief Gets a description of what is at a given target physical address
/// \param m Pointer to a machine object. Can be NULL (for local machines).
/// \param paddr Target physical address of interest
/// \param name Receives the name in a string, guaranteed to remain valid only until
/// the next CM_API function is called from the same thread. Set to NULL on failure.
/// \returns 0 for success, non zero code for error.
/// \note The function will return the reg name if the address is that of a register.
/// It can also resolve TLB registers, uarch registers, and PMA registers, and other
/// static ranges in the address space, such as "uarch.ram", "htif", "plic",
/// "virtio", "cmio.tx_buffer" etc. It will but not identify "ram", any flash drives
/// or nvram , since these are not statically defined. It will report them as "memory".
CM_API cm_error cm_get_address_name(const cm_machine *m, uint64_t paddr, const char **name);
// -----------------------------------------------------------------------------
// Machine API functions
// -----------------------------------------------------------------------------
/// \brief Creates a new local machine object.
/// \param new_m Receives the pointer to the new machine object. Set to NULL on failure.
/// \returns 0 for success, non zero code for error.
/// \details A newly created object is empty (does not hold a machine instance).
/// Use cm_create() or cm_load() to instantiate a machine into the object.
/// Use cm_create_new() or cm_load_new() as single-call shortcuts.
/// Use cm_delete() to delete the object.
CM_API cm_error cm_new(cm_machine **new_m);
/// \brief Clones an empty machine object from an existing one.
/// \param m Pointer to the existing machine object to clone from.
/// \param new_m Receives the pointer to the new machine object. Set to NULL on failure.
/// \returns 0 for success, non zero code for error.
/// \details The new machine object will be of the same type as \p m.
/// Local if \p m is local, remote on the same host if \p m is remote.
/// Regardless, a newly created object is empty (does not hold a machine instance).
/// Use cm_create() or cm_load() to instantiate a machine into the object.
/// Use cm_delete() to delete the object.
/// \details A remote clone owns a newly forked server and inherits the communication timeout from \p m.
/// Deleting the clone implicitly shuts its server down, regardless of the cleanup policy of \p m.
/// Use cm_jsonrpc_set_timeout() and cm_jsonrpc_set_cleanup_call() to change these settings.
CM_API cm_error cm_clone_empty(const cm_machine *m, cm_machine **new_m);
/// \brief Checks if an object is empty (does not hold a machine instance).
/// \param m Pointer to the existing machine object.
/// \param yes Receives true if empty, false otherwise.
/// \returns 0 for success, non zero code for error.
CM_API cm_error cm_is_empty(const cm_machine *m, bool *yes);
/// \brief Checks if an object is a remote machine controlled via the JSONRPC API.
/// \param m Pointer to the existing machine object.
/// \param yes Receives true if remote, false otherwise.
/// \returns 0 for success, non zero code for error.
CM_API cm_error cm_is_jsonrpc_machine(const cm_machine *m, bool *yes);
/// \brief Deletes a machine object.
/// \param m Pointer to the existing machine object (can be NULL).
/// \details The pointer to the machine object must not be used after this call.
/// \details If the machine object is not empty (has a machine instance),
/// silently calls cm_destroy() to destroy the instance.
CM_API void cm_delete(cm_machine *m);
/// \brief Creates a new machine instance from configuration.
/// \param m Pointer to an empty machine object (does not hold a machine instance).
/// \param config Machine configuration as a JSON object in a string (at least RAM length must be set).
/// A useful config needs at least RAM length and image, a rootfs flash drive image, and a DTB entrypoint to be set.
/// For example:
/// ```json
/// {
/// "ram": {
/// "length": 134217728,
/// "backing_store": {
/// "data_filename": "linux.bin"
/// }
/// },
/// "flash_drive": [{
/// "backing_store": {
/// "data_filename": "rootfs.ext2"
/// }
/// }],
/// "dtb": {
/// "entrypoint": "echo Hello world!"
/// }
/// }
/// ```
/// \param runtime_config Machine runtime configuration as a JSON object in a string (can be NULL).
/// \param dir Directory to create an on-disk machine. If NULL or empty, the machine will be created in-memory.
/// \returns 0 for success, non zero code for error.
/// \details Use cm_destroy() to destroy the machine instance and remove it from the object.
CM_API cm_error cm_create(cm_machine *m, const char *config, const char *runtime_config, const char *dir);
/// \brief Combines cm_new() and cm_create() for convenience.
/// \param config Machine configuration as a JSON object in a string (at least RAM length must be set).
/// \param runtime_config Machine runtime configuration as a JSON object in a string (can be NULL).
/// \param new_m Receives the pointer to the new machine object with a machine instance. Set to NULL on failure.
/// \param dir Directory to create an on-disk machine. If NULL or empty, the machine will be created in-memory.
/// \returns 0 for success, non zero code for error.
/// \details Use cm_destroy() to destroy the machine instance and remove it from the object.
/// \details Use cm_delete() to delete the object.
/// \details See cm_new() and cm_create() for more details.
CM_API cm_error cm_create_new(const char *config, const char *runtime_config, const char *dir, cm_machine **new_m);
/// \brief Loads a new machine instance from a previously stored directory.
/// \param m Pointer to an empty machine object (does not hold a machine instance).
/// \param dir Directory where previous machine is stored.
/// \param sharing Backing stores sharing mode.
/// CM_SHARING_NONE: Machine state will be fully in-memory.
/// CM_SHARING_CONFIG: Machine state will be on-disk for shared backing stores, but in-memory for everything else.
/// CM_SHARING_ALL: Machine state will be fully on-disk.
/// \param new_m Receives the pointer to the new machine object with a machine instance. Set to NULL on failure.
/// \param runtime_config Machine runtime configuration as a JSON object in a string (can be NULL).
/// \returns 0 for success, non zero code for error.
/// \details Use cm_destroy() to destroy the machine instance and remove it from the object.
CM_API cm_error cm_load(cm_machine *m, const char *dir, const char *runtime_config, cm_sharing_mode sharing);
/// \brief Combines cm_new() and cm_load() for convenience.
/// \param dir Directory where previous machine is stored.
/// \param runtime_config Machine runtime configuration as a JSON object in a string (can be NULL).
/// \param sharing Backing stores sharing mode.
/// \returns 0 for success, non zero code for error.
/// \details Use cm_destroy() to destroy the machine instance and remove it from the object.
/// \details Use cm_delete() to delete the object.
/// \details See cm_load() and cm_create() for more details.
CM_API cm_error cm_load_new(const char *dir, const char *runtime_config, cm_sharing_mode sharing, cm_machine **new_m);
/// \brief Stores a machine instance to a directory, serializing its entire state.
/// \param m Pointer to a non-empty machine object (holds a machine instance).
/// \param dir Directory where the machine will be stored.
/// \param sharing Specifies how machine changes are reflected in the new store.
/// CM_SHARING_NONE: Copies backing stores from the initial machine state (rarely useful).
/// CM_SHARING_CONFIG: Store current machine state for shared backing stores; others are copied from the initial state.
/// CM_SHARING_ALL: Store current machine state for all backing stores.
/// \returns 0 for success, non zero code for error.
/// \details The function refuses to store into an existing directory (it will not overwrite an existing machine).
CM_API cm_error cm_store(const cm_machine *m, const char *dir, cm_sharing_mode sharing);
/// \brief Clones a machine stored from source directory to destination directory.
/// \param m Pointer to a machine object. Can be NULL (for local machines).
/// \param from_dir Path to the source directory containing a valid machine to be cloned.
/// \param to_dir Path to the destination directory where the cloned machine will be stored.
/// \returns 0 for success, non zero code for error.
/// \details Read-only files are copied using hard links on filesystems that support them.
/// Writable files are copied using reflinks on copy-on-write filesystems.
/// Sparsity of files is preserved.
/// The source directory machine must not have a running instance.
CM_API cm_error cm_clone_stored(const cm_machine *m, const char *from_dir, const char *to_dir);
/// \brief Renames a previously stored machine and makes the rename durable.
/// \param m Pointer to a machine object. Can be NULL (for local machines).
/// \param from_dir Path to the source directory containing a valid stored machine.
/// \param to_dir Path to the destination directory, which must not exist.
/// \returns 0 for success, non zero code for error.
/// \details The stored machine must not be in use.
/// The operation fails if source and destination are on different filesystems.
/// On success, the rename and the affected parent directory entries are durable on disk.
CM_API cm_error cm_rename_stored(const cm_machine *m, const char *from_dir, const char *to_dir);
/// \brief Removes all files and the directory of a previously stored machine.
/// \param m Pointer to a machine object. Can be NULL (for local machines).
/// \param dir Path to the directory containing the stored machine to be removed.
/// \returns 0 for success, non zero code for error.
/// \details The stored machine must not be in use.
/// On success, the entire directory and all machine files are deleted, and the
/// removed parent directory entry is durable on disk.
/// On failure, some files or the directory may remain.
CM_API cm_error cm_remove_stored(const cm_machine *m, const char *dir);
/// \brief Flushes all files of a previously stored machine to permanent storage.
/// \param m Pointer to a machine object. Can be NULL (for local machines).
/// \param dir Path to the directory containing the stored machine to be synced.
/// \returns 0 for success, non zero code for error.
/// \details The expected usage is to sync after the machine using the stored files has been closed.
/// On success, all previous changes to the stored machine files are durable on disk.
/// \warning Syncing while a machine is still using the stored files commits to disk only
/// the changes that were meant to persist on disk (changes to shared backing stores), and
/// only on systems with a unified page cache (Linux, macOS). Elsewhere, only data already
/// written back to the files is made durable.
CM_API cm_error cm_sync_stored(const cm_machine *m, const char *dir);
/// \brief Destroy a machine instance and remove it from the object.
/// \param m Pointer to a non-empty machine object (holds a machine instance).
/// \returns 0 for success, non zero code for error.
/// \details cm_delete() may fail silently when implicitly calling cm_destroy().
/// To make sure the machine was successfully destroyed, call cm_destroy() explicitly.
/// \details This function does not delete the machine object.
/// You must still call cm_delete() afterwards.
CM_API cm_error cm_destroy(cm_machine *m);
/// \brief Changes the machine runtime configuration.
/// \param m Pointer to a non-empty machine object (holds a machine instance).
/// \param runtime_config Machine runtime configuration as a JSON object in a string (can be NULL).
/// \returns 0 for success, non zero code for error.
CM_API cm_error cm_set_runtime_config(cm_machine *m, const char *runtime_config);
/// \brief Gets the machine runtime configuration.
/// \param m Pointer to a non-empty machine object (holds a machine instance).
/// \param runtime_config Receives the runtime configuration as a JSON object in a string,
/// guaranteed to remain valid only until the next CM_API function is called from the same thread.
/// Set to NULL on failure.
/// \returns 0 for success, non zero code for error.
CM_API cm_error cm_get_runtime_config(const cm_machine *m, const char **runtime_config);
/// \brief Replaces a memory range.
/// \param m Pointer to a non-empty machine object (holds a machine instance).
/// \param range_config Memory range configuration as a JSON object in a string.
/// Must have the same start, length, and read-only settings as an existing memory range.
/// For example:
/// ```json
/// {
/// "start": 0x80000000000000,
/// "length": 0x100000,
/// "read_only": false,
/// "backing_store": {
/// "data_filename": "linux.bin"
/// "dht_filename": "linux.dht"
/// "dpt_filename": "linux.dpt"
/// "shared": false
/// }
/// }
/// ```
/// \returns 0 for success, non zero code for error.
/// \details The machine must have been initialized with an existing memory range that
/// has the same start and length specified in the new range.
CM_API cm_error cm_replace_memory_range(cm_machine *m, const char *range_config);
/// \brief Returns a JSON object with the machine config used to initialize the machine.
/// \param m Pointer to a non-empty machine object (holds a machine instance).
/// \param config Receives the initial configuration as a JSON object in a string,
/// guaranteed to remain valid only until the next CM_API function is called from the same thread.
/// Set to NULL on failure.
/// \returns 0 for success, non zero code for error.
CM_API cm_error cm_get_initial_config(const cm_machine *m, const char **config);
/// \brief Returns a list with all address ranges in the machine.
/// \param m Pointer to a non-empty machine object (holds a machine instance).
/// \param ranges Receives the address ranges as a JSON object in a string,
/// guaranteed to remain valid only until the next CM_API function is called from the same thread.
/// \returns 0 for success, non zero code for error.
CM_API cm_error cm_get_address_ranges(const cm_machine *m, const char **ranges);
/// \brief Obtains the root hash of the hash tree.
/// \param m Pointer to a non-empty machine object (holds a machine instance).
/// \param hash Valid pointer to cm_hash structure that receives the hash.
/// \returns 0 for success, non zero code for error.
CM_API cm_error cm_get_root_hash(const cm_machine *m, cm_hash *hash);
/// \brief Obtains the revert root hash from the shadow state.
/// \param m Pointer to a non-empty machine object (holds a machine instance).
/// \param hash Valid pointer to cm_hash structure that receives the hash.
/// \returns 0 for success, non zero code for error.
CM_API cm_error cm_read_revert_root_hash(const cm_machine *m, cm_hash *hash);
/// \brief Writes the revert root hash to the shadow state.
/// \param m Pointer to a non-empty machine object (holds a machine instance).
/// \param hash Valid pointer to cm_hash structure with hash to store.
/// \returns 0 for success, non zero code for error.
CM_API cm_error cm_write_revert_root_hash(cm_machine *m, const cm_hash *hash);
/// \brief Obtains the hash of a node in the hash tree.
/// \param m Pointer to a non-empty machine object (holds a machine instance).
/// \param address Address of node.
/// \param log2_size Log<sub>2</sub> of node size.
/// \param hash Valid pointer to cm_hash structure that receives the hash.
/// \returns 0 for success, non zero code for error.
CM_API cm_error cm_get_node_hash(const cm_machine *m, uint64_t address, int32_t log2_size, cm_hash *hash);
/// \brief Obtains the proof for a node in the machine state hash tree.
/// \param m Pointer to a non-empty machine object (holds a machine instance).
/// \param address Address of target node. Must be aligned to a 2^log2_target_size boundary.
/// \param log2_target_size The log base 2 of the size subtended by the target node.
/// Must be between CM_HASH_TREE_LOG2_WORD_SIZE (for a word) and \p log2_root_size, inclusive.
/// \param log2_root_size The log base 2 of the size subtended by the subtree root.
/// Must be between CM_HASH_TREE_LOG2_WORD_SIZE (for a word) and CM_HASH_TREE_LOG2_ROOT_SIZE
/// (for the entire address space), inclusive.
/// \param proof Receives the proof as a JSON object in a string,
/// guaranteed to remain valid only until the next CM_API function is called from the same thread.
/// \returns 0 for success, non zero code for error.
/// \note The target_address in the returned proof is the absolute machine address, even when
/// log2_root_size is smaller than CM_HASH_TREE_LOG2_ROOT_SIZE. To obtain the offset relative to
/// the subtree root, compute target_address % (1 << log2_root_size).
CM_API cm_error cm_get_proof(const cm_machine *m, uint64_t address, int32_t log2_target_size, int log2_root_size,
const char **proof);
// ------------------------------------
// Reading and writing
// ------------------------------------
/// \brief Reads the value of a word in the machine state, by its physical address.
/// \param m Pointer to a non-empty machine object (holds a machine instance).
/// \param address Word address (aligned to 64-bit boundary).
/// \param val Receives word value (little-endian).
/// \returns 0 for success, non zero code for error.
/// \warning The implementation is slow when the word falls in a memory range mapped to a device.
CM_API cm_error cm_read_word(const cm_machine *m, uint64_t address, uint64_t *val);
/// \brief Writes the value of a word in the machine state, by its physical address.
/// \param m Pointer to a non-empty machine object (holds a machine instance).
/// \param address Word address (aligned to 64-bit boundary).
/// \param val Word value to write (little-endian).
/// \returns 0 for success, non zero code for error.
/// \warning The implementation is slow when the word falls in a memory range mapped to a device.
CM_API cm_error cm_write_word(cm_machine *m, uint64_t address, uint64_t val);
/// \brief Reads the value of a register.
/// \param m Pointer to a non-empty machine object (holds a machine instance).
/// \param reg Register to read.
/// \param val Receives the value (little-endian).
/// \returns 0 for success, non zero code for error.
CM_API cm_error cm_read_reg(const cm_machine *m, cm_reg reg, uint64_t *val);
/// \brief Writes the value of a register.
/// \param m Pointer to a non-empty machine object (holds a machine instance).
/// \param reg Register to write.
/// \param val Value to write (little-endian).
/// \returns 0 for success, non zero code for error.
CM_API cm_error cm_write_reg(cm_machine *m, cm_reg reg, uint64_t val);
/// \brief Reads a chunk of data, by its target physical address and length.
/// \param m Pointer to a non-empty machine object (holds a machine instance).
/// \param paddr Target physical address to start reading from.
/// \param data Buffer that receives data to read. Must be at least \p length bytes long.
/// \param length Number of bytes to read from \p paddr to \p data.
/// \returns 0 for success, non zero code for error.
/// \details The data can be anywhere in the entire address space.
CM_API cm_error cm_read_memory(const cm_machine *m, uint64_t paddr, uint8_t *data, uint64_t length);
/// \brief Writes a chunk of data to machine memory, by its target physical address and length.
/// \param m Pointer to a non-empty machine object (holds a machine instance).
/// \param paddr Target physical address to start writing to.
/// \param data Buffer that contains data to write. Must be at least \p length bytes long.
/// \param length Number of bytes to write starting from \p data to \p paddr.
/// \returns 0 for success, non zero code for error.
/// \details Unlike read_memory(), the entire chunk of data, from \p paddr to \p paddr + \p length,
/// must reside entirely in the same memory range. Moreover, it cannot be mapped to a device.
CM_API cm_error cm_write_memory(cm_machine *m, uint64_t paddr, const uint8_t *data, uint64_t length);
/// \brief Reads a chunk of data from a machine memory range, by its virtual address.
/// \param m Pointer to a non-empty machine object (holds a machine instance).
/// \param address Virtual address to start reading.
/// \param data Receives chunk of memory.
/// \param length Size of chunk in bytes.
/// \returns 0 for success, non zero code for error.
/// \details The translation is based on the current mapping, as defined in CM_REG_SATP.
CM_API cm_error cm_read_virtual_memory(cm_machine *m, uint64_t address, uint8_t *data, uint64_t length);
/// \brief Writes a chunk of data to a machine memory range, by its virtual address.
/// \param m Pointer to a non-empty machine object (holds a machine instance).
/// \param address Virtual address to start writing.
/// \param data Source for chunk of data.
/// \param length Size of chunk in bytes.
/// \returns 0 for success, non zero code for error.
/// \details The translation is based on the current mapping, as defined in CM_REG_SATP.
CM_API cm_error cm_write_virtual_memory(cm_machine *m, uint64_t address, const uint8_t *data, uint64_t length);
/// \brief Translates a virtual memory address to its corresponding physical memory address.
/// \param m Pointer to a non-empty machine object (holds a machine instance).
/// \param vaddr Virtual address to translate.
/// \param paddr Receives the physical memory address.
/// \returns 0 for success, non zero code for error.
/// \details The translation is based on the current mapping, as defined in CM_REG_SATP.
CM_API cm_error cm_translate_virtual_address(cm_machine *m, uint64_t vaddr, uint64_t *paddr);
/// \brief Reads and consumes data from the console output buffer.
/// \param m Pointer to a non-empty machine object (holds a machine instance).
/// \param data Buffer to receive console output data. Must be at least \p max_length bytes long.
/// \param max_length Maximum number of bytes to read.
/// \param read_len Receives the number of bytes actually read.
/// If \p max_length is zero or \p data is NULL, \p read_len receives the available size that could be read.
/// \returns 0 for success, non zero code for error.
/// \details Requires the runtime console output destination to be set to buffer.
/// Reads up to \p max_length bytes, or all available data if less is available.
/// Read data is consumed and removed from the buffer.
CM_API cm_error cm_read_console_output(cm_machine *m, uint8_t *data, uint64_t max_length, uint64_t *read_len);
/// \brief Writes data to the console input buffer.
/// \param m Pointer to a non-empty machine object (holds a machine instance).
/// \param data Buffer containing data to write. Must be at least \p length bytes long.
/// \param length Number of bytes to write.
/// \param written_len Receives the number of bytes actually written.
/// If \p length is zero or \p data is NULL, \p written_len receives the available space size that could be written.
/// \returns 0 for success, non zero code for error.
/// \details Requires the runtime console input destination to be set to buffer.
/// Writes up to \p length bytes, or less if the buffer lacks space.
/// If the buffer is full, \p written_len is set to zero.
CM_API cm_error cm_write_console_input(cm_machine *m, const uint8_t *data, uint64_t length, uint64_t *written_len);
// ------------------------------------
// Running
// ------------------------------------
/// \brief Runs the machine until CM_REG_MCYCLE reaches mcycle_end, the machine yields, or halts.
/// \param m Pointer to a non-empty machine object (holds a machine instance).
/// \param mcycle_end End cycle value.
/// \param break_reason Receives reason for returning (can be NULL). Set to CM_BREAK_REASON_FAILED on failure.
/// \returns 0 for success, non zero code for error.
/// \details You may want to receive cmio requests depending on the run break reason. The break reason precedence is
/// halt, manual yield, cycle overflow, then reaching the target mcycle.
CM_API cm_error cm_run(cm_machine *m, uint64_t mcycle_end, cm_break_reason *break_reason);
/// \brief Collects state root hashes after every 2^\p log2_mcycle_period machine cycles
/// until mcycle reaches \p mcycle_end or the machine reaches a fixed point (manual yields, halts, mcycle overflows).
/// \param m Pointer to a non-empty machine object (holds a machine instance).
/// \param mcycle_end End machine cycle value.
/// \param log2_mcycle_period Log base 2 of the number of machine cycles between sampling points.
/// \param mcycle_phase Number of machine cycles elapsed in the current sampling period.
/// \param log2_bundle_mcycle_count Log base 2 of the number of state root hashes covered by each bundle root hash.
/// \param previous_partial_bundle Optional partial bundle returned by a previous call.
/// If not NULL, it must be a JSON object as a string containing the partial bundle.
/// \param result Receives a JSON object as a string, guaranteed to remain valid only until
/// the next CM_API function is called from the same thread.
/// Each entry in "hashes" is a base64-encoded state root hash when \p log2_bundle_mcycle_count is zero, or a bundle
/// root hash when it is greater than zero.
/// The field "mcycle_phase" is the number of machine cycles elapsed in the current sampling period.
/// The field "break_reason" is a string with the reason why the function returned.
/// (Set to "failed" on failure.)
/// The optional field "partial_bundle" contains the partial bundle needed to continue collecting bundled root hashes.
/// For example:
/// ```json
/// {
/// "hashes": ["2av44Q3TaHKREgULA/iMoNe+btxCr+ja+hHGtXxaGjM=", ..., "FMAHn7mc2f+I30UjiywbAtOkw99iOa3NWS6fWql3UNU="],
/// "mcycle_phase": 1278,
/// "break_reason": "yielded_manually",
/// "partial_bundle": { ... },
/// "console_io_error": "..."
/// }
/// ```
/// \returns 0 for success, non zero code for error.
/// \detail Periodic sampling points are separated by 2^\p log2_mcycle_period machine cycles. Given
/// \p mcycle_phase, the next sampling point is reached after
/// (2^\p log2_mcycle_period - \p mcycle_phase) machine cycles. If execution reaches a fixed point before the next
/// periodic sampling point, the state root hash at that fixed point is collected as the final state root hash.
///
/// When \p log2_bundle_mcycle_count is greater than zero, each entry in "hashes" is the bundle root hash of
/// 2^log2_bundle_mcycle_count consecutive state root hashes. State root hashes from \p previous_partial_bundle, when
/// provided, precede those collected by this call in the first returned bundle.
///
/// If execution reaches a fixed point, its current state root hash is the final state root hash collected. Without
/// bundling, that state root hash is the final entry in "hashes" and can be used for padding. With bundling, it is
/// added to the current partial bundle, whose remaining positions are filled with copies of that state root hash.
/// The resulting bundle root hash is appended to "hashes", followed by one final bundle root hash covering only
/// copies of the fixed-point state root hash. This final fixed-point-only bundle root hash plays the same padding role
/// as the final fixed-point state root hash does without bundling. The preceding bundle root hash may also cover state
/// root hashes collected before the fixed point.
///
/// When the fixed point is a manual yield whose reason is rx-rejected, the recorded revert root hash is used instead
/// of the current state root hash. Consequently, the final entry and the padding entry described above are derived
/// from the recorded revert root hash.
///
/// If \p mcycle_end equals the current mcycle and the machine is not at a fixed point, no transition is requested
/// and "hashes" is empty. At a fixed point, the fixed-point entries described above are always returned, and the
/// machine remains unchanged.
///
/// "break_reason" is the same reason that cm_run() would return. The values "yielded_manually", "halted", and
/// "mcycle_overflow" identify fixed points.
///
/// The "partial_bundle" field is omitted when no partial bundle remains, including whenever execution reaches a fixed
/// point. Otherwise it contains the partial bundle and must be passed to a subsequent call to continue bundling the
/// sequence of state root hashes correctly.
///
/// If a console I/O error occurs during execution, it does NOT cause the operation to stop or fail.
/// Instead, the machine continues running until completion. Any console I/O error message is returned
/// in the "console_io_error" field of the result JSON object. This allows you to detect and handle
/// console I/O issues without interrupting the main execution flow.
CM_API cm_error cm_collect_mcycle_root_hashes(cm_machine *m, uint64_t mcycle_end, uint64_t log2_mcycle_period,
uint64_t mcycle_phase, int32_t log2_bundle_mcycle_count, const char *previous_partial_bundle, const char **result);
/// \brief Collects the state root hashes covered by one bundle of periodic samples taken from the current mcycle.
/// \param m Pointer to a non-empty machine object (holds a machine instance).
/// \param bundle_offset Zero-based offset of the bundle among the consecutive bundles starting at the current mcycle.
/// \param log2_mcycle_period Log base 2 of the number of machine cycles between sampling points.
/// \param log2_bundle_mcycle_count Log base 2 of the number of state root hashes covered by each bundle.
/// \param result Receives a JSON array of 2^\p log2_bundle_mcycle_count base64-encoded state root hashes as a string,
/// guaranteed to remain valid only until the next CM_API function is called from the same thread.
/// \returns 0 for success, non zero code for error.
/// \detail Each bundle spans 2^(\p log2_mcycle_period + \p log2_bundle_mcycle_count) machine cycles. Execution first
/// advances to the start of the bundle, then collects a state root hash after every 2^\p log2_mcycle_period machine
/// cycles. If execution reaches a fixed point before the bundle is complete, the remaining positions are filled with
/// copies of the fixed-point state root hash, following the same rules as cm_collect_mcycle_root_hashes(). A machine
/// already at a fixed point returns copies of its state root hash and remains unchanged. Execution continues through
/// automatic yields and ignores console I/O errors.
CM_API cm_error cm_collect_mcycle_bundle(cm_machine *m, uint64_t bundle_offset, uint64_t log2_mcycle_period,
int32_t log2_bundle_mcycle_count, const char **result);
/// \brief Runs the machine microarchitecture until CM_REG_UARCH_CYCLE reaches uarch_cycle_end or it halts.
/// \param m Pointer to a non-empty machine object (holds a machine instance).
/// \param uarch_cycle_end End micro cycle value.
/// \param uarch_break_reason Receives reason for returning (can be NULL).
/// \returns 0 for success, non zero code for error.
CM_API cm_error cm_run_uarch(cm_machine *m, uint64_t uarch_cycle_end, cm_uarch_break_reason *uarch_break_reason);
/// \brief Collects state root hashes after every uarch cycle until mcycle reaches \p mcycle_end,
/// or the machine reaches a fixed point (manual yields, halts, or mcycle overflows), implicitly
/// resetting the uarch between mcycles.
/// \param m Pointer to a non-empty machine object (holds a machine instance).
/// \param mcycle_end Value of mcycle to end execution, uarch cycle by uarch cycle.
/// \param log2_bundle_uarch_cycle_count Log base 2 of the number of state root hashes covered by each bundle root
/// hash.
/// \param revert_uarch_tail Optional JSON array of base64-encoded state root hashes after each uarch cycle of the
/// machine corresponding to the revert root hash. Its final two entries are the state root hash after the uarch
/// halted and the state root hash after reset, which equals the revert root hash (can be NULL).
/// It is obtained by calling this function with no bundling on the machine corresponding to revert root hash,
/// while it waits for a response.
/// It is required when the machine starts at a rejected manual yield, or when the call may execute a non-fixed-point
/// mcycle. Otherwise the call cannot consume it and ignores it.
/// \param result Receives an JSON object as a string, guaranteed to remain valid only until
/// the next CM_API function is called from the same thread.
/// Each entry in "hashes" is a base64-encoded state root hash when \p log2_bundle_uarch_cycle_count is zero, or a
/// bundle root hash when it is greater than zero.
/// The field "mcycle_hash_offsets" contains half-open offsets delimiting the entries for each mcycle in "hashes".
/// Its first entry is zero and each subsequent entry is the offset immediately after an implicit uarch reset.
/// The field "break_reason" is a string with the reason why the function returned.
/// (Set to "failed" on failure.)
/// If \p mcycle_end equals the current mcycle and the machine is not at a fixed point, no transition is requested
/// and "hashes" is empty. At a fixed point, one complete, reset-delimited mcycle is always represented in "hashes".
/// "break_reason" is the same reason that cm_run() would return.
/// For example:
/// ```json
/// {
/// "hashes": ["2av44Q3TaHKREgULA/iMoNe+btxCr+ja+hHGtXxaGjM=", ..., "FMAHn7mc2f+I30UjiywbAtOkw99iOa3NWS6fWql3UNU="],
/// "mcycle_hash_offsets": [0, 713, ..., 2293769],
/// "break_reason": "yielded_manually"
/// }
/// ```
/// \returns 0 for success, non zero code for error.
/// \detail The first state root hash covered by "hashes" is the state root hash after the first uarch cycle. The
/// final state root hash covered by each mcycle range is the state root hash immediately after the uarch reset.
///
/// When \p log2_bundle_uarch_cycle_count is greater than zero, each entry in "hashes" is the bundle root hash of
/// 2^log2_bundle_uarch_cycle_count consecutive state root hashes.
/// For each adjacent pair (begin, end) in "mcycle_hash_offsets", entries hashes[begin] through hashes[end - 1]
/// represent one mcycle:
/// - Without bundling, hashes[end - 2] is the state root hash after the uarch halted. With bundling, it is the
/// bundle root hash of copies of that state root hash. It can be used to pad the mcycle up to its final entry.
/// - Without bundling, hashes[end - 1] is the state root hash immediately after the uarch reset. With bundling, it
/// is the root hash of the final bundle, which may cover state root hashes collected during execution, followed
/// by zero or more copies of the state root hash after the uarch halted, and ends with the state root hash
/// immediately after the uarch reset.
///
/// If "break_reason" is "yielded_manually", "halted", or "mcycle_overflow", execution stopped at a fixed point. In
/// these cases, the function will attempt to execute one additional mcycle at this fixed point, and collect the
/// resulting state root hashes as well. As a result, the range ending at the final mcycle hash offset corresponds to
/// this fixed point.
///
/// The rejected manual yield is the exception to the paragraph above. When execution stops on a manual yield
/// whose reason is rx-rejected, the state root hash after the final uarch reset is replaced by the recorded
/// revert root hash, and instead of executing one additional mcycle, the function collects one extra period,
/// that of the reverted machine, as given by \p revert_uarch_tail.
CM_API cm_error cm_collect_uarch_cycle_root_hashes(cm_machine *m, uint64_t mcycle_end,
int32_t log2_bundle_uarch_cycle_count, const char *revert_uarch_tail, const char **result);
/// \brief Collects the state root hashes covered by one bundle of uarch cycles of the current mcycle.
/// \param m Pointer to a non-empty machine object (holds a machine instance).
/// \param bundle_offset Zero-based offset of the bundle among the 2^(CM_ROLLUP_LOG2_MAX_UARCH_CYCLES_PER_MCYCLE -
/// \p log2_bundle_uarch_cycle_count) bundles of the mcycle.
/// \param log2_bundle_uarch_cycle_count Log base 2 of the number of state root hashes covered by the bundle.
/// \param revert_uarch_tail Same as in cm_collect_uarch_cycle_root_hashes() (can be NULL).
/// \param result Receives a JSON array of 2^\p log2_bundle_uarch_cycle_count base64-encoded state root hashes as a
/// string, guaranteed to remain valid only until the next CM_API function is called from the same thread.
/// \returns 0 for success, non zero code for error.
/// \detail The mcycle expands to 2^CM_ROLLUP_LOG2_MAX_UARCH_CYCLES_PER_MCYCLE state root hashes, one after each uarch
/// cycle. Once the uarch halts, its state root hash repeats up to the final position, which holds the state root hash
/// after the uarch reset, following the same rules as cm_collect_uarch_cycle_root_hashes() without bundling. The