-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbuilder-hex0-riscv64-stage2.hex0
More file actions
4119 lines (3577 loc) · 116 KB
/
Copy pathbuilder-hex0-riscv64-stage2.hex0
File metadata and controls
4119 lines (3577 loc) · 116 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
# SPDX-FileCopyrightText: 2026 Alexandre Gomes Gaigalas <alganet@gmail.com>
# SPDX-License-Identifier: Apache-2.0
#
# Builder-hex0 RISC-V 64-bit Stage 2
#
# A minimal kernel for bootstrapping compilers from source on RISC-V.
# Inspired by builder-hex0 (https://github.com/ironmeld/builder-hex0).
#
# Boot (virt): qemu-system-riscv64 -machine virt -m 2G -nographic \
# -kernel builder-hex0-riscv64-stage1-virt.bin \
# -drive file=disk.img,format=raw,if=none,id=hd0 \
# -device virtio-blk-device,drive=hd0 --no-reboot
# Boot (sifive_u): qemu-system-riscv64 -machine sifive_u -m 2G -nographic \
# -kernel builder-hex0-riscv64-stage1-sifive_u.bin \
# -drive file=disk.img,format=raw,if=sd --no-reboot
#
# Compiled by stage 1 from hex0 source on disk to 0x80210000.
# On entry: a0 = next disk sector (filesystem start), a1 = DTB pointer
#
# Memory layout (physical addresses):
# 0x80100000 Sv39 root page table (4KB)
# 0x80210000 Kernel code (base, s2 points here)
# 0x80300000 Kernel global data (variables)
# 0x80400000 Process descriptors (16 x 4096)
# 0x80500000 VirtIO structures (desc/avail/used/buffers)
# 0x80600000 User process area (VA 0x00600000 via Sv39 gigapage)
# 0x90000000 User stack top (VA 0x10000000, grows down)
# 0xA0000000 Scratch buffer (64KB)
# 0xA0010000 stdin device buffer (64KB)
# 0xA0020000 File names (8MB, 8192 entries x 1024 bytes)
# 0xA0820000 File descriptors (512KB, 32768 x 16 bytes)
# 0xA8000000 Kernel stack top (grows down) / sscratch
# 0xB0000000 Saved process memory (bump allocator)
# 0xD4000000 File data (bump allocator, next_file_address)
#
# Sv39 paging (gigapages, root table only):
# Entry 0: VA 0x00000000-0x3FFFFFFF -> PA 0x80000000 (U|R|W|X|V)
# Entry 2: VA 0x80000000-0xBFFFFFFF -> PA 0x80000000 (R|W|X|V)
# Entry 3: VA 0xC0000000-0xFFFFFFFF -> PA 0xC0000000 (R|W|X|V)
# Kernel disables paging (satp=0) on trap entry, enables before sret
#
# Global data at 0x80300000:
# +0x000: storage_mmio_base (8 bytes) [VirtIO MMIO or SPI controller base]
# +0x008: virtio_struct_base (8 bytes) [VirtIO only]
# +0x010: next_filenum (8 bytes)
# +0x018: next_file_address (8 bytes)
# +0x020: next_process_num (8 bytes)
# +0x028: next_save_process_address (8 bytes)
# +0x030: stdin_sector (8 bytes)
# +0x038: stdin_offset (8 bytes)
# +0x040: current_brk (8 bytes) [PA, per-process simplified]
# +0x048: enable_flush (8 bytes)
# +0x050: have_userspace (8 bytes)
# +0x058: cmd_args (3x8: argv[0], argv[1], NULL)
# +0x070: cmd_env (1x8: NULL)
# +0x078: satp_value (8 bytes)
# +0x080: dtb_pointer (8 bytes)
# +0x088: disk_read_fn (8 bytes) [function pointer, set by storage driver]
# +0x090: disk_write_fn (8 bytes) [function pointer, set by storage driver]
# +0x098: spi_base (8 bytes) [SPI+SD only, used by spi_transfer]
# +0x0A0: create_new_process (8 bytes) [execve: 1 = forked/first (new
# process level), 0 = fork-less exec (overlay the caller)]
#
# Process descriptor (4096 bytes each, at 0x80400000):
# +0x000: process_address (8)
# +0x008: brk (8)
# +0x010: saved_stack_pointer (8) [return-to-shell]
# +0x018: saved_stack_pointer_fork (8)
# +0x020: forked (8)
# +0x028: saved_brk (8)
# +0x030: child_exit_code (8)
# +0x038: saved_process_mem_addr (8)
# +0x040: saved_process_mem_len (8)
# +0x048: saved_stack_addr (8)
# +0x050: saved_stack_len (8)
# +0x100: current_dir (256 bytes, null-terminated, trailing /)
# +0x200: open_files (448 entries x 8 bytes)
# each: global_file_index(4), current_offset(4)
#
# Global file descriptor table (16 bytes each, at 0xA0820000):
# +0x00: file_address (8)
# +0x08: file_length (8)
#
# File names table (1024 bytes each, at 0xA0020000):
# Null-terminated absolute paths
#
# VirtIO structures at 0x80500000:
# +0x0000: Descriptor table (8 entries x 16 bytes = 128)
# +0x0080: Available ring (flags + idx + 8 entries = 22 bytes)
# +0x1000: Used ring (flags + idx + 8 entries = 68 bytes)
# +0x2000: Request header (16 bytes)
# +0x2010: Status byte
# +0x3000: Sector buffer (512 bytes)
#
# Register conventions:
# s2 = RAM base (0x80210000), set during boot
# s3 = VirtIO MMIO base, available during boot
# s4 = VirtIO struct base (0x80500000), available during boot
# s5 = global data base (0x80300000), available during boot
# sp = kernel stack pointer (in sscratch during U-mode)
#
# Trap handler at offset 4 from binary start
#:_start
6f 20 00 42 # jal zero, main
#============================================================
# Trap handler (at offset 4 = address 0x80210004)
# Entered on ecall from U-mode (scause = 8)
#============================================================
#:trap_handler
# Swap sp and sscratch: sp = kernel sp, sscratch = user sp
73 11 01 14 # csrrw sp, sscratch, sp
# Disable paging (kernel operates on physical addresses)
73 10 00 18 # csrrw zero, satp, zero
73 00 00 12 # sfence.vma
# Quick sanity: if sp < 0x80000000, sscratch was wrong
b7 02 00 40 # lui t0, 0x40000
93 92 12 00 # slli t0, t0, 1
63 76 51 02 # bgeu sp, t0, trap_sp_ok
# Bad sp! sp = old sscratch (bad kernel sp?), sscratch = old user sp
# Save bad sp value before overwriting
13 03 01 00 # mv t1, sp
# Set sp to a safe kernel stack area: 0xA7F00000
b7 02 f8 53 # lui t0, 0x53F80
13 91 12 00 # slli sp, t0, 1
13 01 01 fc # addi sp, sp, -64
23 30 11 00 # sd ra, 0(sp)
23 34 81 00 # sd s0, 8(sp)
23 38 91 00 # sd s1, 16(sp)
13 04 03 00 # mv s0, t1
f3 24 00 14 # csrrs s1, sscratch, zero
6f 00 50 7d # j reboot
#:trap_sp_ok
# Allocate trap frame (256 bytes = 32 slots of 8 bytes)
13 01 01 f0 # addi sp, sp, -256
# Save t0 first (we use it as scratch for CSR reads)
23 30 51 02 # sd t0, 32(sp)
# Save user sp (from sscratch) into frame
f3 22 00 14 # csrrs t0, sscratch, zero
23 34 51 00 # sd t0, 8(sp)
# Save sepc into frame
f3 22 10 14 # csrrs t0, sepc, zero
23 3c 51 0e # sd t0, 248(sp)
# Save remaining registers
23 30 11 00 # sd ra, 0(sp)
23 38 31 00 # sd gp, 16(sp)
23 3c 41 00 # sd tp, 24(sp)
# t0 already saved at 32
23 34 61 02 # sd t1, 40(sp)
23 38 71 02 # sd t2, 48(sp)
23 3c 81 02 # sd s0, 56(sp)
23 30 91 04 # sd s1, 64(sp)
23 34 a1 04 # sd a0, 72(sp)
23 38 b1 04 # sd a1, 80(sp)
23 3c c1 04 # sd a2, 88(sp)
23 30 d1 06 # sd a3, 96(sp)
23 34 e1 06 # sd a4, 104(sp)
23 38 f1 06 # sd a5, 112(sp)
23 3c 01 07 # sd a6, 120(sp)
23 30 11 09 # sd a7, 128(sp)
23 34 21 09 # sd s2, 136(sp)
23 38 31 09 # sd s3, 144(sp)
23 3c 41 09 # sd s4, 152(sp)
23 30 51 0b # sd s5, 160(sp)
23 34 61 0b # sd s6, 168(sp)
23 38 71 0b # sd s7, 176(sp)
23 3c 81 0b # sd s8, 184(sp)
23 30 91 0d # sd s9, 192(sp)
23 34 a1 0d # sd s10, 200(sp)
23 38 b1 0d # sd s11, 208(sp)
23 3c c1 0d # sd t3, 216(sp)
23 30 d1 0f # sd t4, 224(sp)
23 34 e1 0f # sd t5, 232(sp)
23 38 f1 0f # sd t6, 240(sp)
# Check scause == 8 (ecall from U-mode)
f3 22 20 14 # csrrs t0, scause, zero
13 03 80 00 # li t1, 8
63 88 62 00 # beq t0, t1, dispatch
# Not ecall from U-mode: terminate process with exit code 139
93 02 b0 08 # li t0, 139
23 34 51 04 # sd t0, 72(sp)
6f 00 10 4f # j syscall_exit
# Dispatch syscall based on a7
#:dispatch
83 38 01 08 # ld a7, 128(sp)
# All dispatch uses trampolines (J-type, ±1MB range)
13 03 00 04 # li t1, 64
63 8c 68 08 # beq a7, t1, tramp_write
13 03 d0 05 # li t1, 93
63 8a 68 08 # beq a7, t1, tramp_exit
13 03 60 0d # li t1, 214
63 88 68 08 # beq a7, t1, tramp_brk
13 03 f0 03 # li t1, 63
63 86 68 08 # beq a7, t1, tramp_read
13 03 80 03 # li t1, 56
63 84 68 08 # beq a7, t1, tramp_openat
13 03 90 03 # li t1, 57
63 82 68 08 # beq a7, t1, tramp_close
13 03 e0 03 # li t1, 62
63 80 68 08 # beq a7, t1, tramp_lseek
13 03 10 03 # li t1, 49
63 8e 68 06 # beq a7, t1, tramp_chdir
13 03 10 01 # li t1, 17
63 8c 68 06 # beq a7, t1, tramp_getcwd
13 03 00 03 # li t1, 48
63 8a 68 06 # beq a7, t1, tramp_faccessat
13 03 20 02 # li t1, 34
63 88 68 06 # beq a7, t1, tramp_mkdirat
13 03 c0 0d # li t1, 220
63 86 68 06 # beq a7, t1, tramp_clone
13 03 d0 0d # li t1, 221
63 84 68 06 # beq a7, t1, tramp_execve
13 03 f0 05 # li t1, 95
63 82 68 06 # beq a7, t1, tramp_waitid
13 03 40 10 # li t1, 260
63 80 68 06 # beq a7, t1, tramp_wait4
13 03 00 06 # li t1, 96
63 8e 68 04 # beq a7, t1, tramp_stub
13 03 d0 01 # li t1, 29
63 8a 68 04 # beq a7, t1, tramp_stub
13 03 50 03 # li t1, 53
63 86 68 04 # beq a7, t1, tramp_stub
# Unknown syscall
93 02 f0 ff # li t0, -1
23 34 51 04 # sd t0, 72(sp)
6f 00 40 04 # j trap_exit
# Trampolines (J-type, ±1MB range)
#:tramp_write
6f 00 c0 0e # j syscall_write
#:tramp_exit
6f 00 90 44 # j syscall_exit
#:tramp_brk
6f 00 00 1b # j syscall_brk
#:tramp_read
6f 00 00 22 # j syscall_read
#:tramp_openat
6f 00 80 33 # j syscall_openat
#:tramp_close
6f 00 40 46 # j syscall_close
#:tramp_lseek
6f 00 00 48 # j syscall_lseek
#:tramp_chdir
6f 00 c0 4e # j syscall_chdir
#:tramp_getcwd
6f 00 00 58 # j syscall_getcwd
#:tramp_faccessat
6f 00 c0 60 # j syscall_faccessat
#:tramp_mkdirat
6f 00 c0 63 # j syscall_mkdirat
#:tramp_clone
6f 00 c0 71 # j syscall_clone
#:tramp_execve
6f 00 90 04 # j syscall_execve
#:tramp_waitid
6f 00 10 4d # j syscall_waitid
#:tramp_wait4
6f 00 d0 4f # j syscall_wait4
#:tramp_stub
6f 00 40 70 # j syscall_stub_zero
#============================================================
# trap_exit: Restore all registers and sret
#============================================================
#:trap_exit
# Restore sepc (incremented by 4 to skip ecall)
83 32 81 0f # ld t0, 248(sp)
93 82 42 00 # addi t0, t0, 4
73 90 12 14 # csrrw zero, sepc, t0
# Restore user sp to sscratch
83 32 81 00 # ld t0, 8(sp)
73 90 02 14 # csrrw zero, sscratch, t0
# Enable paging before returning to user mode
# (kernel code+stack are identity-mapped via gigapage 2, so this is safe)
b7 02 18 40 # lui t0, 0x40180
93 92 12 00 # slli t0, t0, 1
83 b2 82 07 # ld t0, 0x78(t0)
73 90 02 18 # csrrw zero, satp, t0
73 00 00 12 # sfence.vma
# Restore all registers
83 30 01 00 # ld ra, 0(sp)
83 31 01 01 # ld gp, 16(sp)
03 32 81 01 # ld tp, 24(sp)
83 32 01 02 # ld t0, 32(sp)
03 33 81 02 # ld t1, 40(sp)
83 33 01 03 # ld t2, 48(sp)
03 34 81 03 # ld s0, 56(sp)
83 34 01 04 # ld s1, 64(sp)
03 35 81 04 # ld a0, 72(sp)
83 35 01 05 # ld a1, 80(sp)
03 36 81 05 # ld a2, 88(sp)
83 36 01 06 # ld a3, 96(sp)
03 37 81 06 # ld a4, 104(sp)
83 37 01 07 # ld a5, 112(sp)
03 38 81 07 # ld a6, 120(sp)
83 38 01 08 # ld a7, 128(sp)
03 39 81 08 # ld s2, 136(sp)
83 39 01 09 # ld s3, 144(sp)
03 3a 81 09 # ld s4, 152(sp)
83 3a 01 0a # ld s5, 160(sp)
03 3b 81 0a # ld s6, 168(sp)
83 3b 01 0b # ld s7, 176(sp)
03 3c 81 0b # ld s8, 184(sp)
83 3c 01 0c # ld s9, 192(sp)
03 3d 81 0c # ld s10, 200(sp)
83 3d 01 0d # ld s11, 208(sp)
03 3e 81 0d # ld t3, 216(sp)
83 3e 01 0e # ld t4, 224(sp)
03 3f 81 0e # ld t5, 232(sp)
83 3f 01 0f # ld t6, 240(sp)
# Deallocate frame
13 01 01 10 # addi sp, sp, 256
# Swap back: sp = user sp, sscratch = kernel sp
73 11 01 14 # csrrw sp, sscratch, sp
# Return to user mode
73 00 20 10 # sret
#============================================================
# syscall_write: write(fd, buf, count) -> count
# a0(72)=fd, a1(80)=buf, a2(88)=count
#============================================================
#:syscall_write
# Load args from trap frame
83 39 81 04 # ld s3, 72(sp)
03 3a 01 05 # ld s4, 80(sp)
83 3a 81 05 # ld s5, 88(sp)
13 8b 0a 00 # mv s6, s5
# Translate buf VA -> PA
b7 02 00 40 # lui t0, 0x40000
93 92 12 00 # slli t0, t0, 1
33 0a 5a 00 # add s4, s4, t0
# If fd <= 2, write to console
93 02 30 00 # li t0, 3
63 c4 59 00 # blt s3, t0, write_console
6f 00 40 02 # j write_file
#:write_console
#:write_loop
63 8c 0a 00 # beqz s5, write_done
03 45 0a 00 # lbu a0, 0(s4)
ef 00 90 44 # call sbi_putchar
13 0a 1a 00 # addi s4, s4, 1
93 8a fa ff # addi s5, s5, -1
6f f0 df fe # j write_loop
#:write_done
23 34 61 05 # sd s6, 72(sp)
6f f0 1f f1 # j trap_exit
#:write_file
# s3=fd, s4=buf, s5=count, s6=original count
# Get file info from process fd table
13 85 09 00 # mv a0, s3
ef 10 40 17 # call fd_to_file_index
93 0b 05 00 # mv s7, a0
# Get file_address and file_length from global descriptor
b7 02 41 50 # lui t0, 0x50410
93 92 12 00 # slli t0, t0, 1
13 93 4b 00 # slli t1, s7, 4
33 8c 62 00 # add s8, t0, t1
83 3c 0c 00 # ld s9, 0(s8)
03 3d 8c 00 # ld s10, 8(s8)
# Get current_offset from process fd
ef 00 50 6e # call get_process_base
13 05 05 20 # addi a0, a0, 0x200
93 92 39 00 # slli t0, s3, 3
b3 0d 55 00 # add s11, a0, t0
83 e9 4d 00 # lwu s3, 4(s11)
#:write_file_loop
63 80 0a 04 # beqz s5, write_file_done
# Copy byte from user buf to file
83 42 0a 00 # lbu t0, 0(s4)
33 83 3c 01 # add t1, s9, s3
23 00 53 00 # sb t0, 0(t1)
13 0a 1a 00 # addi s4, s4, 1
93 89 19 00 # addi s3, s3, 1
93 8a fa ff # addi s5, s5, -1
# If offset > file_length, extend file
e3 52 3d ff # bge s10, s3, write_file_loop
# Extend: file_length = offset
13 8d 09 00 # mv s10, s3
23 34 ac 01 # sd s10, 8(s8)
# Also advance next_file_address
b7 02 18 40 # lui t0, 0x40180
93 92 12 00 # slli t0, t0, 1
03 b3 82 01 # ld t1, 0x18(t0)
13 03 13 00 # addi t1, t1, 1
23 bc 62 00 # sd t1, 0x18(t0)
6f f0 5f fc # j write_file_loop
#:write_file_done
# Update current_offset in process fd
23 a2 3d 01 # sw s3, 4(s11)
# Return original count
23 34 61 05 # sd s6, 72(sp)
6f f0 df e8 # j trap_exit
#============================================================
# syscall_brk: brk(addr) -> addr
# a0(72)=addr. If addr==0, return current brk.
# brk stored in global data at offset 0x40
#============================================================
#:syscall_brk
83 32 81 04 # ld t0, 72(sp)
# Global data at 0x80300000
37 03 18 40 # lui t1, 0x40180
13 13 13 00 # slli t1, t1, 1
83 33 03 04 # ld t2, 0x40(t1)
63 82 02 04 # beqz t0, brk_return_current
# Convert VA to PA: PA = VA + 0x80000000
37 0e 00 40 # lui t3, 0x40000
13 1e 1e 00 # slli t3, t3, 1
b3 82 c2 01 # add t0, t0, t3
# Cap at user-space limit (PA 0x8FF00000 = VA 0x0FF00000), leaves 1MB
# for user stack growing down from VA 0x10000000. Without this cap,
# large brk requests (e.g. mes's default 64-bit M2 arena ~480MB) walk
# the zero-loop past 0x90000000 into kernel structures.
#
# Return -1 on an over-cap request (NOT the current brk): M2libc's
# _malloc_brk only detects failure via `if(-1 == brk(...)) return 0;`
# -- no "brk didn't grow" check -- so returning the current brk (the bare
# Linux syscall convention) reads as success, malloc advances past the cap
# and returns a block that was never mapped, and the caller faults on first
# write. -1 makes the OOM detectable, so malloc returns NULL and the caller
# fails cleanly instead of corrupting.
b7 0e f8 47 # lui t4, 0x47F80
93 9e 1e 00 # slli t4, t4, 1
63 e0 5e 04 # bgtu t0, t4, brk_return_fail
# Zero memory from old brk to new brk if extending
63 dc 53 00 # bge t2, t0, brk_store
93 8e 03 00 # mv t4, t2
#:brk_zero_loop
63 d8 5e 00 # bge t4, t0, brk_store
23 80 0e 00 # sb zero, 0(t4)
93 8e 1e 00 # addi t4, t4, 1
6f f0 5f ff # j brk_zero_loop
#:brk_store
23 30 53 04 # sd t0, 0x40(t1)
# Return VA to user
b3 82 c2 41 # sub t0, t0, t3
23 34 51 04 # sd t0, 72(sp)
6f f0 9f e3 # j trap_exit
#:brk_return_current
# Return current brk as VA = PA - 0x80000000
37 0e 00 40 # lui t3, 0x40000
13 1e 1e 00 # slli t3, t3, 1
b3 83 c3 41 # sub t2, t2, t3
23 34 71 04 # sd t2, 72(sp)
6f f0 5f e2 # j trap_exit
#:brk_return_fail
# Over-cap request: return -1 so M2libc's _malloc_brk detects the failure.
93 02 f0 ff # li t0, -1
23 34 51 04 # sd t0, 72(sp)
6f f0 9f e1 # j trap_exit
#============================================================
# syscall_read: read(fd, buf, count) -> bytes_read
# a0(72)=fd, a1(80)=buf, a2(88)=count
# fd 0 = stdin (from disk via VirtIO)
#============================================================
#:syscall_read
83 39 81 04 # ld s3, 72(sp)
03 3a 01 05 # ld s4, 80(sp)
83 3a 81 05 # ld s5, 88(sp)
13 8b 0a 00 # mv s6, s5
# Translate buf VA -> PA
b7 02 00 40 # lui t0, 0x40000
93 92 12 00 # slli t0, t0, 1
33 0a 5a 00 # add s4, s4, t0
63 98 09 08 # bnez s3, read_file
# Read from stdin (disk)
#:read_stdin_loop
63 82 0a 08 # beqz s5, read_stdin_done
# Load global data base
b7 02 18 40 # lui t0, 0x40180
93 92 12 00 # slli t0, t0, 1
03 b3 02 03 # ld t1, 0x30(t0)
83 b3 82 03 # ld t2, 0x38(t0)
# Check if we need to read a new sector
# stdin buffer at 0x80700000
37 85 00 50 # lui a0, 0x50008
13 15 15 00 # slli a0, a0, 1
# If offset >= 512, read next sector
13 0e 00 20 # li t3, 512
63 c8 c3 03 # blt t2, t3, read_stdin_have_data
# Read next sector
13 05 03 00 # mv a0, t1
b7 85 00 50 # lui a1, 0x50008
93 95 15 00 # slli a1, a1, 1
ef 10 40 3c # call disk_read_sector
# Advance sector, reset offset
b7 02 18 40 # lui t0, 0x40180
93 92 12 00 # slli t0, t0, 1
03 b3 02 03 # ld t1, 0x30(t0)
13 03 13 00 # addi t1, t1, 1
23 b8 62 02 # sd t1, 0x30(t0)
23 bc 02 02 # sd zero, 0x38(t0)
93 03 00 00 # li t2, 0
#:read_stdin_have_data
# Read one byte from stdin buffer
37 85 00 50 # lui a0, 0x50008
13 15 15 00 # slli a0, a0, 1
33 05 75 00 # add a0, a0, t2
03 4e 05 00 # lbu t3, 0(a0)
# Store to user buffer
23 00 ca 01 # sb t3, 0(s4)
13 0a 1a 00 # addi s4, s4, 1
93 8a fa ff # addi s5, s5, -1
# Update stdin_offset
b7 02 18 40 # lui t0, 0x40180
93 92 12 00 # slli t0, t0, 1
83 b3 82 03 # ld t2, 0x38(t0)
93 83 13 00 # addi t2, t2, 1
23 bc 72 02 # sd t2, 0x38(t0)
6f f0 1f f8 # j read_stdin_loop
#:read_stdin_done
23 34 61 05 # sd s6, 72(sp)
6f f0 df d6 # j trap_exit
#:read_file
# s3=fd, s4=buf, s5=count
# Get file info from process fd table
13 85 09 00 # mv a0, s3
ef 00 10 7d # call fd_to_file_index
93 0b 05 00 # mv s7, a0
# Get file_address and file_length from global descriptor
b7 02 41 50 # lui t0, 0x50410
93 92 12 00 # slli t0, t0, 1
13 93 4b 00 # slli t1, s7, 4
b3 82 62 00 # add t0, t0, t1
03 bc 02 00 # ld s8, 0(t0)
83 bc 82 00 # ld s9, 8(t0)
# Get current_offset from process fd
ef 00 10 54 # call get_process_base
13 05 05 20 # addi a0, a0, 0x200
93 92 39 00 # slli t0, s3, 3
33 0d 55 00 # add s10, a0, t0
83 6d 4d 00 # lwu s11, 4(s10)
# Read loop
13 0b 00 00 # li s6, 0
#:read_file_loop
63 84 0a 02 # beqz s5, read_file_done
# Check if offset >= length (EOF)
63 d2 9d 03 # bge s11, s9, read_file_done
# Copy one byte
b3 02 bc 01 # add t0, s8, s11
03 c3 02 00 # lbu t1, 0(t0)
23 00 6a 00 # sb t1, 0(s4)
13 0a 1a 00 # addi s4, s4, 1
93 8d 1d 00 # addi s11, s11, 1
93 8a fa ff # addi s5, s5, -1
13 0b 1b 00 # addi s6, s6, 1
6f f0 df fd # j read_file_loop
#:read_file_done
# Update current_offset
23 22 bd 01 # sw s11, 4(s10)
# Return bytes read
23 34 61 05 # sd s6, 72(sp)
6f f0 df cf # j trap_exit
#============================================================
# syscall_openat: openat(dirfd, pathname, flags, mode)
# a0(72)=dirfd(ignored), a1(80)=pathname, a2(88)=flags
# Returns: fd number or -1
#============================================================
#:syscall_openat
83 39 01 05 # ld s3, 80(sp)
03 3a 81 05 # ld s4, 88(sp)
# Translate pathname VA -> PA
b7 02 00 40 # lui t0, 0x40000
93 92 12 00 # slli t0, t0, 1
b3 89 59 00 # add s3, s3, t0
# Resolve to absolute path
13 85 09 00 # mv a0, s3
ef 00 10 50 # call absolute_path
93 09 05 00 # mv s3, a0
# Check O_CREAT (bit 6 = 0x40)
93 72 0a 04 # andi t0, s4, 0x40
63 9c 02 00 # bnez t0, openat_create
# Open existing file
13 85 09 00 # mv a0, s3
ef 00 10 6c # call find_file
63 4a 05 0e # bltz a0, openat_fail
93 0a 05 00 # mv s5, a0
6f 00 00 0b # j openat_alloc_fd
#:openat_create
# Check parent directory exists
# Find last '/' in path
93 82 09 00 # mv t0, s3
13 83 09 00 # mv t1, s3
#:openat_find_slash
83 c3 02 00 # lbu t2, 0(t0)
63 8c 03 00 # beqz t2, openat_check_parent
13 0e f0 02 # li t3, 0x2F
63 94 c3 01 # bne t2, t3, openat_next_char
13 83 02 00 # mv t1, t0
#:openat_next_char
93 82 12 00 # addi t0, t0, 1
6f f0 9f fe # j openat_find_slash
#:openat_check_parent
# If slash is first char, parent is root (always exists)
63 00 33 03 # beq t1, s3, openat_do_create
# Temporarily null-terminate at last slash
93 0b 03 00 # mv s7, t1
03 cb 0b 00 # lbu s6, 0(s7)
23 80 0b 00 # sb zero, 0(s7)
13 85 09 00 # mv a0, s3
ef 00 90 67 # call find_file
23 80 6b 01 # sb s6, 0(s7)
63 44 05 0a # bltz a0, openat_fail
#:openat_do_create
# Allocate new file entry
b7 02 18 40 # lui t0, 0x40180
93 92 12 00 # slli t0, t0, 1
83 ba 02 01 # ld s5, 0x10(t0)
# Copy filename to file names table
# dest = 0xA0020000 + s5 * 1024
37 03 01 50 # lui t1, 0x50010
13 13 13 00 # slli t1, t1, 1
93 93 aa 00 # slli t2, s5, 10
33 03 73 00 # add t1, t1, t2
93 83 09 00 # mv t2, s3
#:openat_copy_name
03 ce 03 00 # lbu t3, 0(t2)
23 00 c3 01 # sb t3, 0(t1)
63 08 0e 00 # beqz t3, openat_name_done
13 03 13 00 # addi t1, t1, 1
93 83 13 00 # addi t2, t2, 1
6f f0 df fe # j openat_copy_name
#:openat_name_done
# Set up global file descriptor
# fd_entry = 0xA0820000 + s5 * 16
b7 02 41 50 # lui t0, 0x50410
93 92 12 00 # slli t0, t0, 1
13 93 4a 00 # slli t1, s5, 4
b3 82 62 00 # add t0, t0, t1
# file_address = next_file_address
37 03 18 40 # lui t1, 0x40180
13 13 13 00 # slli t1, t1, 1
83 33 83 01 # ld t2, 0x18(t1)
23 b0 72 00 # sd t2, 0(t0)
23 b4 02 00 # sd zero, 8(t0)
# Increment next_filenum
83 33 03 01 # ld t2, 0x10(t1)
93 83 13 00 # addi t2, t2, 1
23 38 73 00 # sd t2, 0x10(t1)
#:openat_alloc_fd
# Allocate a process fd slot (start from fd 4)
ef 00 d0 40 # call get_process_base
13 05 05 20 # addi a0, a0, 0x200
93 02 40 00 # li t0, 4
13 03 00 1c # li t1, 448
#:openat_find_fd
63 d6 62 02 # bge t0, t1, openat_fail
93 93 32 00 # slli t2, t0, 3
b3 03 75 00 # add t2, a0, t2
03 ae 03 00 # lw t3, 0(t2)
63 06 0e 00 # beqz t3, openat_got_fd
93 82 12 00 # addi t0, t0, 1
6f f0 9f fe # j openat_find_fd
#:openat_got_fd
# Store file index and reset offset
23 a0 53 01 # sw s5, 0(t2)
23 a2 03 00 # sw zero, 4(t2)
# Return fd
23 34 51 04 # sd t0, 72(sp)
6f f0 9f bd # j trap_exit
#:openat_fail
93 02 f0 ff # li t0, -1
23 34 51 04 # sd t0, 72(sp)
6f f0 df bc # j trap_exit
#============================================================
# syscall_close: close(fd) -> 0
# a0(72)=fd
#============================================================
#:syscall_close
83 39 81 04 # ld s3, 72(sp)
ef 00 10 3c # call get_process_base
13 05 05 20 # addi a0, a0, 0x200
93 92 39 00 # slli t0, s3, 3
33 05 55 00 # add a0, a0, t0
23 20 05 00 # sw zero, 0(a0)
23 34 01 04 # sd zero, 72(sp)
6f f0 df ba # j trap_exit
#============================================================
# syscall_lseek: lseek(fd, offset, whence) -> new_offset
# a0(72)=fd, a1(80)=offset, a2(88)=whence
# whence: 0=SEEK_SET, 1=SEEK_CUR, 2=SEEK_END
#============================================================
#:syscall_lseek
83 39 81 04 # ld s3, 72(sp)
03 3a 01 05 # ld s4, 80(sp)
83 3a 81 05 # ld s5, 88(sp)
ef 00 90 39 # call get_process_base
13 05 05 20 # addi a0, a0, 0x200
93 92 39 00 # slli t0, s3, 3
33 0b 55 00 # add s6, a0, t0
# SEEK_SET (whence == 0)
63 98 0a 00 # bnez s5, lseek_not_set
23 22 4b 01 # sw s4, 4(s6)
23 34 41 05 # sd s4, 72(sp)
6f f0 1f b8 # j trap_exit
#:lseek_not_set
93 02 10 00 # li t0, 1
63 9c 5a 00 # bne s5, t0, lseek_end
# SEEK_CUR (whence == 1)
83 62 4b 00 # lwu t0, 4(s6)
b3 82 42 01 # add t0, t0, s4
23 22 5b 00 # sw t0, 4(s6)
23 34 51 04 # sd t0, 72(sp)
6f f0 5f b6 # j trap_exit
#:lseek_end
# SEEK_END (whence == 2)
83 22 0b 00 # lw t0, 0(s6)
37 03 41 50 # lui t1, 0x50410
13 13 13 00 # slli t1, t1, 1
93 93 42 00 # slli t2, t0, 4
33 03 73 00 # add t1, t1, t2
83 32 83 00 # ld t0, 8(t1)
b3 82 42 01 # add t0, t0, s4
23 22 5b 00 # sw t0, 4(s6)
23 34 51 04 # sd t0, 72(sp)
6f f0 df b3 # j trap_exit
#============================================================
# syscall_chdir: chdir(path) -> 0 or -1
# a0(72)=path
#============================================================
#:syscall_chdir
83 39 81 04 # ld s3, 72(sp)
# Translate VA -> PA
b7 02 00 40 # lui t0, 0x40000
93 92 12 00 # slli t0, t0, 1
b3 89 59 00 # add s3, s3, t0
# Resolve to absolute path
13 85 09 00 # mv a0, s3
ef 00 50 34 # call absolute_path
93 09 05 00 # mv s3, a0
# Find the file
13 85 09 00 # mv a0, s3
ef 00 d0 50 # call find_file
63 44 05 06 # bltz a0, chdir_fail
# Check it's a directory (file_length == 0)
b7 02 41 50 # lui t0, 0x50410
93 92 12 00 # slli t0, t0, 1
13 13 45 00 # slli t1, a0, 4
b3 82 62 00 # add t0, t0, t1
03 b3 82 00 # ld t1, 8(t0)
63 18 03 04 # bnez t1, chdir_fail
# Copy path to process current_dir
ef 00 50 2f # call get_process_base
13 0a 05 10 # addi s4, a0, 0x100
93 82 09 00 # mv t0, s3
13 03 0a 00 # mv t1, s4
13 0e 00 00 # li t3, 0
#:chdir_copy
83 c3 02 00 # lbu t2, 0(t0)
23 00 73 00 # sb t2, 0(t1)
63 8a 03 00 # beqz t2, chdir_check_slash
13 8e 03 00 # mv t3, t2
93 82 12 00 # addi t0, t0, 1
13 03 13 00 # addi t1, t1, 1
6f f0 9f fe # j chdir_copy
#:chdir_check_slash
# Append trailing '/' if not present
93 03 f0 02 # li t2, 0x2F
63 08 7e 00 # beq t3, t2, chdir_ok
23 00 73 00 # sb t2, 0(t1)
13 03 13 00 # addi t1, t1, 1
23 00 03 00 # sb zero, 0(t1)
#:chdir_ok
23 34 01 04 # sd zero, 72(sp)
6f f0 1f ab # j trap_exit
#:chdir_fail
93 02 f0 ff # li t0, -1
23 34 51 04 # sd t0, 72(sp)
6f f0 5f aa # j trap_exit
#============================================================
# syscall_getcwd: getcwd(buf, size) -> buf or -1
# a0(72)=buf, a1(80)=size
#============================================================
#:syscall_getcwd
83 39 81 04 # ld s3, 72(sp)
03 3a 01 05 # ld s4, 80(sp)
# Translate buf VA -> PA
b7 02 00 40 # lui t0, 0x40000
93 92 12 00 # slli t0, t0, 1
b3 89 59 00 # add s3, s3, t0
ef 00 90 28 # call get_process_base
13 05 05 10 # addi a0, a0, 0x100
93 02 05 00 # mv t0, a0
13 83 09 00 # mv t1, s3
# Check for root "/"
83 c3 02 00 # lbu t2, 0(t0)
13 0e f0 02 # li t3, 0x2F
63 9c c3 01 # bne t2, t3, getcwd_copy
83 c3 12 00 # lbu t2, 1(t0)
63 98 03 00 # bnez t2, getcwd_copy
# Root: copy "/" + null
23 00 c3 01 # sb t3, 0(t1)
a3 00 03 00 # sb zero, 1(t1)
6f 00 c0 03 # j getcwd_return
#:getcwd_copy
83 c3 02 00 # lbu t2, 0(t0)
63 8a 03 00 # beqz t2, getcwd_strip
23 00 73 00 # sb t2, 0(t1)
93 82 12 00 # addi t0, t0, 1
13 03 13 00 # addi t1, t1, 1
6f f0 df fe # j getcwd_copy
#:getcwd_strip
# Strip trailing '/'
13 03 f3 ff # addi t1, t1, -1
83 43 03 00 # lbu t2, 0(t1)
13 0e f0 02 # li t3, 0x2F
63 96 c3 01 # bne t2, t3, getcwd_no_strip
23 00 03 00 # sb zero, 0(t1)
6f 00 c0 00 # j getcwd_return
#:getcwd_no_strip
13 03 13 00 # addi t1, t1, 1
23 00 03 00 # sb zero, 0(t1)
#:getcwd_return
# Return original VA (s3 is PA, convert back to VA)
b7 02 00 40 # lui t0, 0x40000
93 92 12 00 # slli t0, t0, 1
b3 82 59 40 # sub t0, s3, t0
23 34 51 04 # sd t0, 72(sp)
6f f0 5f a1 # j trap_exit
#============================================================
# syscall_faccessat: faccessat(dirfd, pathname, mode, flags)
# a0(72)=dirfd(ignored), a1(80)=pathname
# Returns: 0 if file exists, -1 if not
#============================================================
#:syscall_faccessat
83 39 01 05 # ld s3, 80(sp)
# Translate VA -> PA
b7 02 00 40 # lui t0, 0x40000
93 92 12 00 # slli t0, t0, 1
b3 89 59 00 # add s3, s3, t0
13 85 09 00 # mv a0, s3
ef 00 d0 21 # call absolute_path
ef 00 d0 3e # call find_file
63 46 05 00 # bltz a0, faccessat_fail
23 34 01 04 # sd zero, 72(sp)
6f f0 df 9e # j trap_exit
#:faccessat_fail
93 02 f0 ff # li t0, -1
23 34 51 04 # sd t0, 72(sp)
6f f0 1f 9e # j trap_exit
#============================================================
# syscall_mkdirat: mkdirat(dirfd, pathname, mode)
# a0(72)=dirfd(ignored), a1(80)=pathname
# Creates a zero-length file entry (= directory)
# Returns: 0 on success, -1 on failure
#============================================================
#:syscall_mkdirat
83 39 01 05 # ld s3, 80(sp)
# Translate VA -> PA
b7 02 00 40 # lui t0, 0x40000
93 92 12 00 # slli t0, t0, 1
b3 89 59 00 # add s3, s3, t0
# Resolve to absolute path
13 85 09 00 # mv a0, s3
ef 00 90 1e # call absolute_path
93 09 05 00 # mv s3, a0
# Check parent directory exists (find last '/')
93 82 09 00 # mv t0, s3
13 83 09 00 # mv t1, s3
#:mkdirat_find_slash
83 c3 02 00 # lbu t2, 0(t0)
63 8c 03 00 # beqz t2, mkdirat_check_parent
13 0e f0 02 # li t3, 0x2F
63 94 c3 01 # bne t2, t3, mkdirat_next_char
13 83 02 00 # mv t1, t0
#:mkdirat_next_char
93 82 12 00 # addi t0, t0, 1
6f f0 9f fe # j mkdirat_find_slash
#:mkdirat_check_parent
63 00 33 03 # beq t1, s3, mkdirat_do_create
93 0b 03 00 # mv s7, t1
03 cb 0b 00 # lbu s6, 0(s7)
23 80 0b 00 # sb zero, 0(s7)
13 85 09 00 # mv a0, s3
ef 00 d0 37 # call find_file
23 80 6b 01 # sb s6, 0(s7)
63 4a 05 06 # bltz a0, mkdirat_fail
#:mkdirat_do_create
# Allocate new file entry
b7 02 18 40 # lui t0, 0x40180
93 92 12 00 # slli t0, t0, 1
83 ba 02 01 # ld s5, 0x10(t0)
# Copy name to file names table
37 03 01 50 # lui t1, 0x50010
13 13 13 00 # slli t1, t1, 1
93 93 aa 00 # slli t2, s5, 10
33 03 73 00 # add t1, t1, t2
93 83 09 00 # mv t2, s3
#:mkdirat_copy_name
03 ce 03 00 # lbu t3, 0(t2)
23 00 c3 01 # sb t3, 0(t1)
63 08 0e 00 # beqz t3, mkdirat_name_done
13 03 13 00 # addi t1, t1, 1